Docker + Nginx + Node.js:从端口映射到反向代理的完整请求链路

很多初学者能照着执行 docker run,却分不清 -p 80:80、listen 80 和 proxy_pass ...:3000 各自负责什么。本文围绕 Windows Docker Desktop 下的一个最小 Nginx + Node.js 示例,拆清镜像、容器、文件挂载、端口映射与反向代理的关系,并给出排错清单。文中代码根据学习材料整理,运行未验证。

一、先看最终请求链路

浏览器访问 localhost:80
        ↓
Windows 宿主机 80 端口
        ↓  Docker 端口映射 -p 80:80
Nginx 容器 80 端口
        ↓  proxy_pass
host.docker.internal:3000
        ↓
Windows 上的 Node.js 服务

理解这条链路后,命令与配置就不再是孤立知识点。

二、镜像与容器是什么关系

Docker 可以把应用及运行所需内容组织成镜像,再基于镜像启动容器:

镜像 Image --docker run--> 容器 Container
  • 镜像是只读模板,类似带运行环境的应用模板。
  • 容器是镜像运行后的实例。
  • Docker Hub 是默认的公共镜像仓库之一。

下载 Nginx 镜像:

docker pull nginx

这里应使用 docker pull,不是 Git 的 git pull。下载镜像并不会自动提供服务,还需要 docker run 创建并启动容器。

三、拆解 docker run 命令

Windows PowerShell 中可以使用:

docker run --name my-nginx-demo -p 80:80 -v "C:\path\nginx.conf:/etc/nginx/nginx.conf" -d nginx
参数作用初学者常见误解
--name my-nginx-demo指定容器名称同名容器不能重复创建
-p 80:80宿主机 80 映射到容器 80左边是宿主机,右边是容器
-v把本机配置挂载进容器Windows 路径建议整体加引号
-d后台运行返回容器 ID 不代表服务持续正常
nginx使用 Nginx 镜像镜像与容器不是同一个概念

端口映射只解决:浏览器如何把请求送到容器里的 Nginx。它还没有决定 Nginx 要把请求交给哪个后端。

四、Nginx 配置如何转发到 Node.js

最小配置如下:

events {}
http {
    server {
        listen 80;
        location / {
            proxy_pass http://host.docker.internal:3000;
            proxy_set_header Host $host;
        }
    }
}

逐项理解:

  1. listen 80:Nginx 在容器内部监听 80 端口。
  2. location /:匹配从根路径开始的请求。
  3. proxy_pass:把请求转发给指定后端。
  4. host.docker.internal:Docker Desktop 容器访问 Windows 宿主机时使用的特殊域名。
  5. proxy_set_header Host $host:把原请求的 Host 请求头继续传给后端。

因此,listen 80 是 Nginx 的入口,:3000 是后端 Node.js 的入口,两者不是同一个端口。

五、端口映射不等于反向代理

这是最容易混淆的地方。

端口映射

Windows:80 → Nginx 容器:80

由 Docker 完成,解决宿主机与容器之间的网络入口问题。

反向代理

Nginx:80 → Node.js:3000

由 Nginx 根据 proxy_pass 完成,解决统一入口向真实后端转发的问题。

浏览器只访问 Nginx,不需要知道 Node.js 的地址和端口,所以 Nginx 在这里是反向代理。

六、Node.js 示例中的模块错误

Node.js 入口使用 .mjs 后缀,意味着文件按 ESM 处理,应使用:

import http from 'node:http';

不能再同时写:

const http = require('http');

否则既会遇到 ESM 中没有 require 的问题,也会重复声明 http。即使 package.json 中设置了 "type": "commonjs",.mjs 仍按 ESM 解释。

后端计划监听 3000 端口:

server.listen(3000, 'localhost', () => {
  console.log('server is running on port 3000');
});

容器能否通过宿主机网络访问仅监听 localhost 的服务需要实际验证。开发场景中通常可考虑监听 0.0.0.0,但仍应结合安全边界配置。

七、错误—原因—处理表

现象常见原因排查方式
docker run 提示名称冲突已存在同名容器docker ps -a,启动、删除或改名
docker ps 看不到容器容器已经退出使用 docker ps -a
Nginx 容器立即退出配置语法或指令层级错误docker logs my-nginx-demo
proxy_pass directive is not allowed hereproxy_pass 不在允许的上下文放到 location 等合法配置块中
浏览器返回 502Node.js 未启动、端口错误或网络不可达检查 3000 端口和监听地址
require is not defined.mjs 使用了 CommonJS 语法改用 import

八、最终自检清单

  • Docker Desktop 已启动。
  • 已使用 docker pull nginx 获取镜像。
  • 本机 80 端口未被其他程序占用。
  • -p 左边是宿主机端口,右边是容器端口。
  • Windows 挂载路径已加引号。
  • proxy_pass 位于 location 中。
  • Node.js 确实监听 3000 端口。
  • docker ps 中 Nginx 状态为 Up。
  • 失败时已查看 docker logs。

九、面试回答模板

Docker 镜像是应用及运行环境的只读模板,容器是镜像的运行实例。docker run -p 80:80 将宿主机 80 端口映射到 Nginx 容器的 80 端口;Nginx 再通过 proxy_pass 把请求转发到 Node.js 的 3000 端口。端口映射解决宿主机到容器的入口问题,反向代理解决统一入口到真实后端的请求转发问题。

十、结语

学习这组技术时,不要只背命令。始终沿着“浏览器 → 宿主机端口 → 容器端口 → Nginx → Node.js”检查,请求在哪一段断开,问题通常就在哪一层。下一步可以补充 Dockerfile、Docker Compose、容器网络和数据卷,让多个服务从手工命令走向可复现编排。

标签: Docker、Nginx、Node.js、反向代理、容器化

更多推荐