🐳 Docker 从入门到实战:一次搞定容器化 + Nginx 反向代理

从"我的电脑能跑,你的电脑跑不了"到"一次构建,到处运行"的华丽转身


📖 前言:一个真实的故事

想象这样一个场景:

你入职一家新公司,接手一个 5 年前用 Vue 2 开发的老项目。项目文档上写着:"请使用 Node 16 + npm 8 运行"

你自信地打开终端,敲下 node -v,屏幕上赫然显示:v22.0.0

然后你开始了漫长的折腾:卸载 Node 22、安装 Node 16、npm install 报错、缺少 Python 环境、node-gyp 编译失败...

折腾半天,项目还是跑不起来。

这个时候,你一定会想:有没有一种技术,能让"我的电脑能跑,你的电脑也能跑"?

答案就是:Docker。


一、Docker 是什么?

1.1 核心思想

Docker = 应用 + 运行环境 的标准化打包工具

简单说,Docker 把你的应用程序 和它需要的所有依赖(Node.js、Redis、MySQL、Nginx...)一起打包成一个独立的"集装箱"。

生活类比

想象你是做海运的:

方式 说明 结果
传统方式 货物直接扔船上 到地方发现包装坏了、温度不对 → 跑不起来 ❌
Docker 方式 货物装进标准化集装箱 温度、湿度都在箱内控制 → 到哪都能完好无损 ✅

1.2 Docker 解决了什么问题?

问题 传统方式 Docker 方式
环境不一致 开发用 Node 16,生产用 Node 22 → 跑不起来 统一在容器中用 Node 16 ✅
依赖冲突 项目 A 需要 Python 2,项目 B 需要 Python 3 每个容器隔离,互不影响 ✅
部署复杂 写部署文档:装 Node、配 Nginx、设环境变量... 一条命令:docker run
团队协作 "我电脑上明明是好的啊!" 每个人跑同一个镜像 ✅

二、Docker 核心概念

2.1 三大核心

scss 复制代码
┌─────────────────────────────────────────────────────────────┐
│                                                           │
│   🏪 Docker Hub (镜像仓库)                                 │
│      ↓ docker pull                                        │
│   💿 Image (镜像) → 只读模板,包含应用+环境                │
│      ↓ docker run                                         │
│   📦 Container (容器) → 可运行的实例                       │
│                                                           │
└─────────────────────────────────────────────────────────────┘

① Image(镜像)= 光盘/模具

属性 说明
是什么 应用程序 + 运行环境的"只读模板"
类比 游戏光盘、蛋糕模具、建筑设计图纸
特点 不可修改,可复用
获取 从 Docker Hub 下载
bash 复制代码
docker pull nginx    # 下载 nginx 镜像
docker pull node:16  # 下载 Node 16 镜像
docker images        # 查看所有镜像

② Container(容器)= 运行中的实例

属性 说明
是什么 从镜像启动的"可运行进程"
类比 放进光驱播放的光盘、烤出来的蛋糕、盖好的房子
特点 可启动、停止、删除,彼此隔离
关系 一个镜像 → 多个容器
bash 复制代码
docker run -d nginx         # 从镜像启动容器
docker ps                   # 查看运行中的容器
docker stop <容器ID>        # 停止容器
docker rm <容器ID>          # 删除容器

③ Docker Hub = 镜像超市

存放各种官方和社区制作的镜像,就像 GitHub 存代码,Docker Hub 存镜像。

bash 复制代码
# 常用镜像
docker pull nginx      # Web 服务器
docker pull node       # Node.js 环境
docker pull mysql      # MySQL 数据库
docker pull redis      # Redis 缓存

2.2 镜像的层级结构(为什么 Docker 这么快)

yaml 复制代码
传统虚拟机 (笨重)
├── 完整的操作系统 (几 GB)
├── 运行环境
└── 应用代码
启动时间:几分钟 ❌

Docker 镜像 (轻量)
├── Layer 1: 基础系统 (Ubuntu/Alpine)    ← 共享宿主机内核
├── Layer 2: 安装 Node.js                ← 可复用
├── Layer 3: 复制 package.json           ← 可缓存
├── Layer 4: npm install                 ← 可缓存
└── Layer 5: 应用代码                    ← 新增
启动时间:几秒钟 ✅

三、Docker 实战:用 Nginx 做反向代理

3.1 场景说明

我们有一个 Node.js 应用,运行在 1314 端口。但我们希望用户通过 80 端口访问,并且不需要知道后端端口号。

解决方案:用 Nginx 做反向代理。

markdown 复制代码
用户访问 localhost:80
        ↓
   Nginx (反向代理)
        ↓
   转发到 localhost:1314
        ↓
   Node.js 应用

3.2 Nginx 是什么?

Nginx = 高性能 Web 服务器 + 反向代理服务器

生活类比

  • Nginx = 公司前台小姐
  • 你访问 80 端口 → 跟前台说"我要找技术部"
  • 前台查通讯录(配置文件)→ 带你去 13 楼 14 号房间(1314 端口)
  • 你只需要记住公司地址(localhost),不需要知道具体房间号

3.3 什么是反向代理?

正向代理 vs 反向代理

对比维度 正向代理 反向代理
谁在用 客户端(你)主动使用 服务器(Nginx)使用
目的 访问受限资源(科学上网) 隐藏后端、负载均衡、安全
用户知道吗 ✅ 知道,要手动配置 ❌ 不知道,无感知
隐藏谁 隐藏客户端 隐藏服务端
例子 VPN、公司网络代理 Nginx、阿里云 SLB

核心区别一句话

正向代理 :客户端主动找帮手 反向代理:服务器主动找帮手

反向代理的好处

  1. 隐藏真实端口 :用户只知道 80,不知道 1314
  2. 灵活迁移 :后端从 1314 改成 1315,用户无感知
  3. 负载均衡:可以把请求分发到多台服务器
  4. 统一 SSL:Nginx 统一处理 HTTPS,后端不用管

3.4 网络基础:端口是什么?

ini 复制代码
localhost:1314
├── localhost = 本机地址 (127.0.0.1)
└── 1314 = 端口号 (应用程序的"门牌号")

默认端口(必须记住)

端口 协议/服务 说明
80 HTTP 浏览器访问时默认端口
443 HTTPS 加密访问默认端口
3306 MySQL 数据库默认端口
6379 Redis 缓存默认端口
3000 Node.js 常用开发端口
1314 自定义 你的后端端口

重要 :浏览器访问 http://localhost 等同于 http://localhost:80

3.5 准备 Nginx 配置文件

创建 nginx.conf

nginx 复制代码
events {
    worker_connections 1024;  # 最大并发连接数
}

http {
    server {
        listen 80;  # Nginx 监听 80 端口

        location / {  # 所有请求
            proxy_pass http://host.docker.internal:1314;  # 转发到宿主机 1314
            proxy_set_header Host $host;  # 保持原始 Host 头
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }
}

配置文件解析

配置项 含义
events { worker_connections 1024; } Nginx 的"体能配置",能同时接待 1024 个客人
http { ... } HTTP 服务配置
server { ... } 虚拟主机配置(相当于一个网站)
listen 80; 在 80 号门口等着
location / { ... } 所有访问根路径 '/' 的请求
proxy_pass http://host.docker.internal:1314; "把客人领到 1314 房间"
proxy_set_header Host $host; 转发时告诉后端:"是哪个域名访问的"

关于 host.docker.internal

  • Docker 容器内访问宿主机的专用域名
  • Windows/Mac 默认支持
  • Linux 需要加 --add-host host.docker.internal:host-gateway

四、Docker 命令实战

4.1 完整操作流程

步骤 1:测试配置文件语法

bash 复制代码
docker run --rm \
  -v C:/path/to/nginx.conf:/etc/nginx/nginx.conf \
  nginx nginx -t

输出

bash 复制代码
nginx: configuration file /etc/nginx/nginx.conf test is successful ✅

命令解析

  • --rm:容器退出后自动删除(测试用)
  • -v:挂载本机文件到容器
  • nginx nginx -t:第一个 nginx 是镜像名,第二个是执行 nginx 测试命令

步骤 2:启动 Nginx 容器

bash 复制代码
docker run -d \
  --name my-nginx \
  --add-host host.docker.internal:host-gateway \
  -p 80:80 \
  -v C:/path/to/nginx.conf:/etc/nginx/nginx.conf \
  nginx

参数详解

参数 含义
-d 后台运行 (detach)
--name my-nginx 给容器起名
--add-host host.docker.internal:host-gateway 容器访问宿主机
-p 80:80 端口映射:本机 80 → 容器 80
-v ... 挂载配置文件
nginx 使用 nginx 镜像

步骤 3:验证容器运行

bash 复制代码
# 查看运行中的容器
docker ps

输出示例

bash 复制代码
CONTAINER ID   IMAGE     STATUS         PORTS                         NAMES
a1b2c3d4e5f6   nginx     Up 10 seconds  0.0.0.0:80->80/tcp           my-nginx
bash 复制代码
# 查看日志
docker logs my-nginx

# 进入容器调试
docker exec -it my-nginx bash

步骤 4:测试访问

bash 复制代码
# 方式1:浏览器访问
http://localhost

# 方式2:命令行测试
curl http://localhost

# 方式3:查看 Nginx 访问日志
docker logs -f my-nginx

4.2 常用 Docker 命令速查

镜像操作

bash 复制代码
docker pull nginx        # 下载镜像
docker images            # 查看所有镜像
docker rmi <镜像ID>      # 删除镜像
docker tag <镜像ID> <新标签>  # 打标签

容器操作

bash 复制代码
docker run -d nginx      # 启动容器
docker ps                # 查看运行中的容器
docker ps -a             # 查看所有容器(包括停止的)
docker stop <容器ID>     # 停止容器
docker start <容器ID>    # 启动已停止的容器
docker restart <容器ID>  # 重启容器
docker rm <容器ID>       # 删除容器
docker rm -f <容器ID>    # 强制删除

调试命令

bash 复制代码
docker logs <容器ID>       # 查看日志
docker logs -f <容器ID>    # 实时查看日志
docker exec -it <容器ID> bash  # 进入容器
docker exec <容器ID> nginx -s reload  # 重载配置
docker inspect <容器ID>    # 查看容器详细信息

五、端口映射详解

5.1 端口映射的语法

bash 复制代码
-p 宿主机端口:容器端口

5.2 不同端口映射的区别

bash 复制代码
# 本机 80 → 容器 80
docker run -p 80:80 nginx
# 访问:http://localhost

# 本机 8080 → 容器 80
docker run -p 8080:80 nginx
# 访问:http://localhost:8080

# 本机 3000 → 容器 3000
docker run -p 3000:3000 node
# 访问:http://localhost:3000

# 多个端口映射
docker run -p 80:80 -p 443:443 nginx

5.3 为什么开发用 8080,生产用 80?

环境 端口 原因
开发 8080、3000、1314 不需要管理员权限,不冲突
生产 80、443 用户访问方便,专业

六、文件挂载详解

6.1 挂载的作用

bash 复制代码
-v 本机文件路径:容器文件路径

好处

  • 修改本机配置文件 → 容器立即生效
  • 不用重新构建镜像
  • 方便调试

6.2 挂载的注意点

bash 复制代码
# ✅ 正确:使用绝对路径
-v C:/Users/xxx/nginx.conf:/etc/nginx/nginx.conf

# ✅ 正确:使用相对路径(已进入目录)
-v ./nginx.conf:/etc/nginx/nginx.conf

# ❌ 错误:只有文件名,Docker 会当成目录
-v nginx.conf:/etc/nginx/nginx.conf
# 报错:not a directory

6.3 Windows 路径格式

bash 复制代码
# PowerShell 推荐
-v C:/Users/xxx/nginx.conf:/etc/nginx/nginx.conf

# CMD 使用
-v C:\Users\xxx\nginx.conf:/etc/nginx/nginx.conf

# Git Bash/WSL
-v /c/Users/xxx/nginx.conf:/etc/nginx/nginx.conf

七、完整访问链路图

csharp 复制代码
用户浏览器访问 http://localhost
         ↓
请求到达本机 80 端口
         ↓
Docker 端口映射 (-p 80:80)
         ↓
Nginx 容器接收请求(监听 80)
         ↓
Nginx 读取配置文件 (nginx.conf)
         ↓
location / 匹配 → proxy_pass
         ↓
转发到 http://host.docker.internal:1314
         ↓
--add-host 解析到宿主机 IP
         ↓
宿主机 Node.js 应用(1314 端口)
         ↓
处理请求,返回响应
         ↓
原路返回给用户浏览器 ✅

八、实战:完整的 Node.js + Nginx 项目

8.1 Node.js 后端 (app.js)

javascript 复制代码
const http = require('http');

const server = http.createServer((req, res) => {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({
        message: 'Hello from Docker!',
        path: req.url,
        timestamp: new Date().toISOString()
    }));
});

const PORT = 1314;
server.listen(PORT, '0.0.0.0', () => {
    console.log(`Server running on port ${PORT}`);
});

8.2 Nginx 配置 (nginx.conf)

nginx 复制代码
events {
    worker_connections 1024;
}

http {
    server {
        listen 80;

        location / {
            proxy_pass http://host.docker.internal:1314;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }
}

8.3 启动脚本

bash 复制代码
#!/bin/bash

# 1. 启动 Node.js 后端
node app.js &

# 2. 启动 Nginx 容器
docker run -d \
  --name my-nginx \
  --add-host host.docker.internal:host-gateway \
  -p 80:80 \
  -v $(pwd)/nginx.conf:/etc/nginx/nginx.conf \
  nginx

echo "✅ 服务已启动!访问 http://localhost"

8.4 验证

bash 复制代码
# 测试 API
curl http://localhost

输出

json 复制代码
{"message":"Hello from Docker!","path":"/","timestamp":"2026-08-18T..."}

九、常见问题排查

9.1 容器启动失败 (Exited 1)

bash 复制代码
# 查看错误日志
docker logs <容器ID>

# 常见原因:
# 1. 配置文件语法错误 → 用 nginx -t 测试
# 2. 端口被占用 → 换端口或 kill 占用进程
# 3. 挂载路径错误 → 检查文件是否存在

9.2 502 Bad Gateway

问题:Nginx 正常运行,但无法连接后端

原因

  1. 后端服务未启动
  2. 后端端口不正确
  3. 后端监听 localhost 而非 0.0.0.0
  4. 未加 --add-host 参数

解决步骤

bash 复制代码
# 1. 检查后端是否运行
netstat -ano | findstr :1314

# 2. 检查后端是否监听 0.0.0.0
# 错误:server.listen(1314, 'localhost')
# 正确:server.listen(1314, '0.0.0.0')

# 3. 测试直接访问后端
curl http://host.docker.internal:1314

# 4. 查看 Nginx 错误日志
docker logs my-nginx

9.3 端口被占用

bash 复制代码
# Windows:查看端口占用
netstat -ano | findstr :80

# 杀掉进程
taskkill /PID <进程号> /F

# 或换一个端口
docker run -p 8080:80 nginx

9.4 配置文件挂载失败

bash 复制代码
# 错误:not a directory
# 原因:Docker 把文件名当成了目录

# 解决:使用绝对路径
-v C:/Users/xxx/nginx.conf:/etc/nginx/nginx.conf

# 或进入目录使用相对路径
cd /path/to/config
-v ./nginx.conf:/etc/nginx/nginx.conf

十、最佳实践总结

10.1 开发 vs 生产

环境 端口 配置方式
开发 8080/3000 直接访问,快速调试
生产 80/443 Nginx 反向代理

10.2 配置文件的"三要素"

markdown 复制代码
1. listen     → 在哪里等(端口)
2. location   → 等什么请求(路径)
3. proxy_pass → 去哪儿(转发目标)

10.3 Docker 使用口诀

复制代码
镜像像图纸,容器像产品
镜像像光盘,容器像播放
镜像像类,容器像对象

端口映射要记牢,宿主容器要分好
配置文件用挂载,修改不用重启了
日志查看很重要,问题在哪早知道

10.4 最佳实践清单

  • 开发时用 8080 端口(避免权限问题)
  • 生产时用 80 端口(用户访问方便)
  • 配置文件用挂载(方便修改)
  • --add-host(容器访问宿主机)
  • 先测试配置再启动(nginx -t
  • docker logs 查看错误
  • --name 给容器命名
  • 一个容器只运行一个进程

十一、进阶学习方向

学完本文,你已经掌握了 Docker 的核心概念和 Nginx 反向代理的实战用法。接下来可以学习:

  1. Docker Compose:同时管理多个容器(nginx + node + mysql)
  2. Dockerfile:自己构建镜像
  3. Docker 网络:容器间通信
  4. Docker 卷:数据持久化
  5. Kubernetes:大规模容器编排

📝 写在最后

还记得开头的那个场景吗?

"我的电脑能跑,你的电脑怎么跑不了?"

现在你有了答案:Docker

它就像一个魔法盒子,把你的应用和它需要的所有东西打包在一起。从此:

  • ✅ 不再有环境不一致的问题
  • ✅ 不再有依赖冲突的痛苦
  • ✅ 不再有"我电脑上明明是好的"的尴尬

你今天的收获

  1. 理解了 Docker 的核心概念(镜像、容器、仓库)
  2. 掌握了 Docker 的常用命令
  3. 学会了 Nginx 反向代理配置
  4. 完成了从入门到实战的完整流程

下一步:去给你的项目加上 Docker,体验"一次构建,到处运行"的快乐吧!🚀


💡 小贴士:所有命令和配置都在文章中,建议边看边敲,动手实践才能真正掌握。

如果你觉得这篇文章有帮助,欢迎点赞、收藏、分享! 🙌

相关推荐
程序员清风1 小时前
专业再升级!程序员专属显示器明基RD280UG上手实测!
java·后端·面试
six_2 小时前
C# 上位机(持续更新中)
前端·面试·c#
码匠许师傅3 小时前
【C++ 面试真题】聊聊 C++ 的序列容器
java·c++·面试
东华万里4 小时前
第40篇C++核心基础与工程实践:从底层逻辑到避坑指南
开发语言·c++·面试·大学生专区
wangjialelele7 小时前
动态规划DP经典题型总结(Java、C++):路径、子数组、子序列、背包问题详解
java·c++·算法·面试·动态规划·代理模式
一只叫煤球的猫8 小时前
Spring AI 2.0 源码解析(一):一次 ChatClient 调用到底经历了什么?
后端·面试·ai编程
子林super8 小时前
使用dockerfile打包ng镜像演示
面试
王中阳Go8 小时前
苏神百万年薪简历翻车:一个包装争议,给后端同学的 5 个工程视角警示
面试
明月_清风9 小时前
开发者写PPT自救指南:4类对接场景,把技术讲清楚
前端·后端·面试