Docker Compose 三容器部署:React+Express+NGINX 生产级协同实践
1. 项目概述:为什么你真正需要的不是“学 Docker Compose”,而是掌握多容器协同的工程直觉
“Working with Multiple Containers Using Docker Compose”——这个标题看起来像一句教科书里的章节名,但在我过去三年带过的27个真实交付项目里,它几乎就是每个中型前端团队从“能跑通”走向“可交付、可运维、可交接”的分水岭。我见过太多人卡在 docker-compose.yml 文件第一行 version: "3.9" 后面就停住:不是不会写 services: ,而是根本没想清楚—— 为什么非得用 Compose?为什么不能直接 docker run 十次?为什么 NGINX 要和 Express 分开?React 构建产物到底该挂载进哪个容器? 这些问题不厘清,你写的 docker-compose.yml 就只是 YAML 语法练习,不是工程实践。
核心关键词已经给出线索: Docker Compose、docker-compose.yml、NGINX、Express、React 。这五者组合,不是随意堆砌,而是一个典型的现代 Web 应用三层部署范式:React 是纯静态前端(build 后是 HTML/JS/CSS),Express 是 Node.js 后端 API 服务,NGINX 则是它们共同的“门卫+快递员”——既负责把用户请求精准路由到前端或后端,又承担 HTTPS 终结、静态资源缓存、负载均衡等生产级职责。Docker Compose 的价值,正在于把这三者从“逻辑上耦合、物理上分散”的混乱状态,变成一个可声明、可版本化、可一键启停的 原子化部署单元 。它解决的从来不是“能不能跑”,而是“能不能让新来的实习生在 5 分钟内复现和调试整个环境”。
适合谁来读?如果你正面临这些场景中的任意一个,这篇就是为你写的:
- 你刚用
create-react-app搭好前端,用express-generator初始化了后端,本地npm start和nodemon能跑,但一到部署就懵——是把 React build 目录拷进 Express 静态服务?还是单独起个 Nginx?怎么配跨域? - 你在 CI/CD 流水线里写
docker build命令时,发现每次改一行前端代码就要重构建整个 Express 镜像,CI 时间从 2 分钟涨到 8 分钟; - 你用宝塔面板或手动配置 Nginx 反向代理,结果某天 Express 接口返回 502,查日志发现是容器网络不通,但
docker ps显示所有容器都在运行; - 你面试被问“React 和 Express 如何通信”,答了 CORS,面试官追问“那生产环境如何避免浏览器跨域限制”,你卡壳了——其实答案就藏在 NGINX 的反向代理配置里。
这不是 Docker 入门课,也不讲 docker run -it ubuntu 这类基础命令。我们要做的,是把你脑子里模糊的“前后端分离”概念,落地成一份能放进 Git 仓库、能被 Jenkins 自动拉起、能在 Ubuntu 服务器上稳定跑三个月不出问题的 docker-compose.yml 。接下来每一部分,都来自我踩过的坑、客户现场的报错截图、以及线上故障复盘会议的笔记。我们不讲理论,只讲“为什么这么写,不这么写会怎样”。
2. 整体架构设计与方案选型:为什么必须拆成三个容器,而不是两个或一个?
2.1 三层解耦的本质:职责分离不是教条,而是故障隔离的刚需
很多人第一反应是:“React 是静态文件,Express 是动态服务,那我直接让 Express app.use(express.static('build')) 不就完事了?何必多此一举搞 NGINX?” 这个想法在开发阶段完全成立,甚至能省下 20 行代码。但一旦进入测试或生产环境,问题立刻浮出水面:
- 缓存策略冲突 :React 的
index.html需要强缓存(Cache-Control: public, max-age=31536000),但index.html里引用的 JS 文件名带 hash(如main.a1b2c3.js),每次构建都会变。Express 的静态服务默认对所有.html和.js用同一套缓存头,你没法对index.html设no-cache,对*.js设max-age=31536000——而 NGINX 可以用location ~* \.js$精确匹配并单独配置。 - HTTPS 终结位置 :生产环境必须用 HTTPS。如果让 Express 自己处理 TLS,它就得加载证书、管理私钥、处理 OCSP Stapling——Node.js 并非为高并发 TLS 优化,且证书轮换时需重启服务。NGINX 是专为 HTTPS 终结设计的,性能高出 3~5 倍,且支持 SNI、HTTP/2、TLS 1.3 等特性,证书更新只需
nginx -s reload,毫秒级生效,零中断。 - 故障域隔离 :某天 Express 因数据库连接池耗尽而卡死,
curl http://localhost:3000/api/users超时。如果前端也跑在 Express 上,curl http://localhost:3000/同样超时,整个网站不可用。而用 NGINX + Express 分离,NGINX 仍能正常返回 React 的index.html和静态资源,用户看到的是“功能受限但页面可访问”的降级体验,而非一片空白。
所以, 三个容器不是为了炫技,而是把“前端展示层”、“业务逻辑层”、“网络接入层”这三个天然不同的故障域,映射到三个独立的进程边界 。Docker Compose 的 services 字段,本质上是在定义这个边界。
2.2 为什么不用 docker run 手动启动?Compose 的不可替代性在哪?
你可以用 10 条 docker run 命令启动三个容器,但很快会遇到这些硬伤:
| 问题类型 | docker run 方案 |
Docker Compose 方案 | 实际影响 |
|---|---|---|---|
| 网络配置 | 需手动 docker network create mynet ,再给每个 run 加 --network mynet --name express-app |
docker-compose.yml 中 networks: 下统一定义,服务名自动成为 DNS 名 |
手动命名易错: express-api vs express_app vs express_service ,拼错一个, fetch('/api') 就 502 |
| 依赖顺序 | docker run 无依赖感知,Express 容器可能在 PostgreSQL 容器启动前就去连 DB,报 ECONNREFUSED |
depends_on: 声明依赖,配合 healthcheck 可确保 PG 健康后再启 Express |
我曾见一个团队在 CI 中加 sleep 30 硬等,结果 PG 启动慢时仍失败,日志里全是 waiting for db... |
| 卷挂载路径 | docker run -v $(pwd)/react-build:/usr/share/nginx/html ,路径写死,不同环境(Mac/Windows/Linux)路径分隔符不同 |
volumes: 中用相对路径 ./react-build:/usr/share/nginx/html ,Compose 自动适配宿主机路径 |
Windows 用户用 WSL2 时, $(pwd) 可能指向 /mnt/c/Users/xxx ,而 Docker Desktop 默认只共享 C:\Users ,路径错则静态文件 404 |
| 环境变量管理 | docker run -e NODE_ENV=production -e DB_HOST=pg ,密钥明文写在命令行,历史记录可查 |
environment: 字段 + .env 文件, .env 可 gitignore,敏感变量如 DB_PASSWORD 不进 Git |
客户曾因误提交 .env 到 GitHub,导致数据库密码泄露,紧急回滚 |
更关键的是 可复现性 。 docker-compose.yml 是一个声明式蓝图:它不告诉你“先做 A 再做 B”,而是说“系统最终状态应包含 NGINX、Express、React,它们之间通过 mynet 网络通信,React 静态文件来自 ./react-build ”。无论你在 Ubuntu 服务器、Mac M1 笔记本、还是 Windows 11 的 WSL2 里执行 docker compose up ,只要 Docker Engine 版本一致,得到的就是完全相同的运行时拓扑。这种确定性,是手工 docker run 永远无法提供的。
2.3 为什么选 NGINX 而非 Caddy 或 Traefik?一个被低估的运维现实
热词列表里有 nginx 、 nginx配置 、 nginx反向代理 ,但没提 Caddy 或 Traefik。这不是偶然。我在 12 个生产环境做过对比测试,NGINX 在以下场景仍有不可替代优势:
- 日志格式定制 :金融客户要求所有请求日志必须含
X-Request-ID(用于全链路追踪),NGINX 的log_format支持$http_x_request_id变量,一行配置即可;Caddy 的 JSON 日志虽灵活,但自定义字段需写 Go 插件,学习成本陡增。 - 细粒度限流 :电商大促时需对
/api/order接口按 IP 限流 100req/s,NGINX 的limit_req_zone $binary_remote_addr zone=api:10m rate=100r/s是成熟方案;Traefik 的限流基于中间件,配置分散在docker-compose.yml和traefik.yml两处,排查时需来回切换。 - Windows 兼容性 :热词中有
windows docker compose、windows通过docker compose安装jellyfin。NGINX 官方提供 Windows 二进制包,Docker Hub 的nginx:alpine镜像在 Windows Docker Desktop 上启动最快(实测平均 1.2s);Caddy 的 Windows 镜像启动慢 3 倍,且某些 TLS 配置在 Windows 上行为异常。
当然,Caddy 的自动 HTTPS(无需手动申请证书)很诱人,但企业级应用往往需对接内部 CA 或使用通配符证书,此时 NGINX 的 ssl_certificate 和 ssl_certificate_key 指令反而更可控。所以, 选 NGINX 不是因为它最先进,而是因为它最“稳”——文档最全、案例最多、排障工具最成熟( nginx -t 验证配置, nginx -s reload 热更新),这对运维同学极其友好 。
3. 核心细节解析与实操要点: docker-compose.yml 的每一行都是经验之谈
3.1 version 字段:别盲目追新,3.8 是当前最稳妥的选择
热词里出现 docker-compose.yml ,但没提具体版本。很多教程一上来就写 version: "3.9" 或 "3.10" ,这是危险的。Docker Compose 的版本号对应 Docker Engine 的 API 版本,而不同 Linux 发行版预装的 Docker 版本差异巨大:
- Ubuntu 22.04 默认 Docker 20.10.12,仅支持 Compose v3.8;
- CentOS 7 的 EPEL 仓库里 Docker 19.03.15,最高支持 v3.7;
- Windows Docker Desktop 4.20+ 才支持 v3.9。
我在线上踩过一次坑:在 Ubuntu 20.04 服务器上用 version: "3.9" , docker compose up 报错 Unsupported config option for services service: 'deploy' 。查文档才发现 deploy 在 v3.8 才正式 GA,v3.7 仅实验性支持。 解决方案不是升级 Docker(可能破坏现有服务),而是降级 Compose 文件版本 。因此,我的标准模板始终用:
version: "3.8" # 兼容性优先,覆盖 95% 的生产环境
提示:
version字段只影响docker-compose.yml的语法解析,不影响容器内运行的软件版本。不必为“最新版”牺牲稳定性。
3.2 services 定义:NGINX、Express、React 的角色分工与配置深意
3.2.1 NGINX 服务:不只是反向代理,更是前端资源的“智能分发中心”
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./react-build:/usr/share/nginx/html:ro
depends_on:
- express
networks:
- app-network
ports:段暴露 80/443,这是用户访问的入口。注意: 不要写- "8080:80"!热词里有nginx部署前端项目、nginx配置,新手常误以为要映射到宿主机 8080 端口,结果用户必须输http://localhost:8080才能访问,违背 Web 习惯。标准 HTTP 端口就是 80,HTTPS 是 443。volumes:中./nginx/conf.d:/etc/nginx/conf.d:ro是关键。conf.d目录下放自定义配置,如default.conf,内容如下:
server {
listen 80;
server_name localhost;
# React 前端:所有非 /api/ 路径都返回 index.html(支持 React Router)
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
}
# Express 后端:/api/ 路径反向代理到 express 容器
location /api/ {
proxy_pass http://express:3000/; # 注意末尾的 /,否则路径会错位
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
这里 proxy_pass http://express:3000/ 的末尾 / 是魔鬼细节:若写成 http://express:3000 ,当用户访问 /api/users 时,NGINX 会转发到 http://express:3000/api/users ;而加了 / ,则转发到 http://express:3000/users (去掉 /api/ 前缀)。这样 Express 的路由就能写 app.get('/users') ,无需关心前面的 /api 。这是前后端解耦的核心技巧。
depends_on:声明依赖,但 仅保证启动顺序,不保证服务就绪 。所以 NGINX 启动时,Express 可能还在连数据库。解决方案是 NGINX 配置中加proxy_next_upstream error timeout http_502;,当 Express 返回 502 时自动重试,配合健康检查更可靠。
3.2.2 Express 服务:轻量化镜像与环境变量的实战取舍
express:
build:
context: ./express-app
dockerfile: Dockerfile
environment:
- NODE_ENV=production
- DB_HOST=postgres
- DB_PORT=5432
ports:
- "3000:3000" # 仅用于本地调试,生产环境不应暴露
depends_on:
- postgres
networks:
- app-network
build:段用Dockerfile而非image:,因为 Express 应用代码是业务核心,必须版本化构建。Dockerfile内容精简为:
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production # 比 npm install 快 40%,且不装 devDependencies
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
npm ci 是关键:它根据 package-lock.json 精确安装,确保各环境依赖版本一致; --only=production 跳过 devDependencies (如 nodemon 、 eslint ),镜像体积从 1.2GB 降至 280MB,启动快 3 倍。
-
environment:中DB_HOST=postgres是重点。热词有ubuntu安装docker compose、centos7 docker compose安装,新手常写DB_HOST=localhost,这是致命错误!localhost在容器内指自身,而非宿主机。Docker Compose 会为每个服务名创建 DNS 记录,postgres服务名会自动解析为 Postgres 容器的 IP。 永远用服务名,不用 localhost 。 -
ports:段注释# 仅用于本地调试是经验之谈。生产环境 Express 不应暴露端口,所有流量必须经 NGINX 进入。否则用户可绕过 NGINX 直连http://your-domain.com:3000/api/users,失去 HTTPS、缓存、限流等所有保护。
3.2.3 React 服务:构建产物挂载,而非运行时构建
热词里有 react 、 react学习 、 react面试题 ,但 docker-compose.yml 中 绝不该有 react 服务 !React 是静态站点,没有“服务”概念。正确做法是:
- 本地或 CI 中执行
npm run build,生成react-build目录; - 在
docker-compose.yml中,将./react-build挂载到 NGINX 的 HTML 目录。
为什么不用 create-react-app 的 npm start ?因为:
npm start启动的是 Webpack Dev Server,含热更新、Source Map,体积大、内存占用高,不适合生产;- 构建产物
build/是纯静态文件,NGINX 可高效服务,QPS 达 15000+;而 Node.js 服务静态文件 QPS 仅 2000 左右。
所以, docker-compose.yml 中没有 react: 服务,只有 NGINX 的 volumes: 挂载。这是对 React 本质的尊重——它不是服务,是资源。
3.3 networks 与 volumes :让容器“说同一种语言”的底层机制
3.3.1 自定义网络: app-network 是容器间通信的“高速公路”
networks:
app-network:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/16
driver: bridge是 Docker 默认网络驱动,安全隔离;subnet: 172.20.0.0/16是关键。默认bridge网络用172.17.0.0/16,但若宿主机已有此网段(如公司内网),会导致容器无法访问外网。自定义子网可规避冲突。- 服务间通信靠 DNS:
nginx容器内ping express能通,curl http://express:3000/health能返回 200。这是 Docker 内置的 DNS 服务实现的,无需额外配置。
3.3.2 卷挂载: volumes 的三种模式与选型逻辑
热词里有 设置volumes ,但没说明场景。 volumes: 在 docker-compose.yml 中有三种常见用法:
| 类型 | 语法示例 | 适用场景 | 注意事项 |
|---|---|---|---|
| 绑定挂载(Bind Mount) | ./react-build:/usr/share/nginx/html:ro |
挂载宿主机目录到容器,如 React 构建产物、NGINX 配置 | :ro 只读,防容器内误删;Windows 路径需用 /c/Users/xxx 格式 |
| 命名卷(Named Volume) | db-data:/var/lib/postgresql/data |
持久化数据库数据,如 Postgres 的 data 目录 |
数据由 Docker 管理,路径抽象,跨平台一致 |
| 匿名卷(Anonymous Volume) | /app/node_modules |
临时存储,如 Node.js 的 node_modules |
容器删除后数据丢失,适合缓存 |
对于我们的栈, ./react-build 必须用绑定挂载,因为构建产物是外部生成的;Postgres 的数据目录必须用命名卷 db-data ,确保容器重启后数据不丢。 绝不能用绑定挂载 ./postgres-data:/var/lib/postgresql/data !因为 Windows/macOS 的文件系统权限与 Linux 不同,Postgres 启动会报 Permission denied 错误。
4. 实操过程与核心环节实现:从零开始搭建可运行的三容器系统
4.1 环境准备:Ubuntu/Windows/macOS 的差异化处理
热词中有 ubuntu安装docker compose 、 windows docker compose 、 centos7 docker compose安装 ,说明环境差异是首要障碍。以下是各平台实操要点:
4.1.1 Ubuntu 22.04(推荐生产环境)
# 1. 卸载旧版 Docker(如有)
sudo apt remove docker docker-engine docker.io containerd runc
# 2. 安装 Docker Engine(官方源)
sudo apt update
sudo apt install ca-certificates curl gnupg lsb-release
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin
# 3. 验证
sudo docker run hello-world
sudo docker compose version # 注意:新版是 docker compose(无横杠),非 docker-compose
注意:Ubuntu 22.04 默认启用
systemd-resolved,可能与 Docker DNS 冲突。若容器内ping google.com失败,执行:echo "dns=1.1.1.1" | sudo tee -a /etc/systemd/resolved.conf sudo systemctl restart systemd-resolved
4.1.2 Windows 11(WSL2 + Docker Desktop)
热词 windows通过docker compose安装jellyfin 暗示 Windows 用户多。 强烈建议用 WSL2 + Docker Desktop 组合,而非旧版 Docker Toolbox :
- WSL2 内核与 Linux 几乎一致,
docker compose up行为 100% 一致; - Docker Desktop 自动集成 WSL2,无需手动配置;
./react-build路径在 WSL2 中为/home/username/project/react-build,Docker Desktop 会自动映射。
安装后,在 WSL2 终端中执行:
# 确保 WSL2 发行版设为默认
wsl -l -v
wsl --set-default Ubuntu-22.04
# 启动 Docker Desktop,然后在 WSL2 中验证
docker compose version
4.1.3 macOS(M1/M2 芯片)
热词未提 macOS,但实际占比高。Apple Silicon 芯片需注意镜像兼容性:
nginx:alpine、node:18-alpine均有arm64镜像,无需额外操作;- 若遇到
qemu: unshare: Operation not permitted错误,是 Docker Desktop 权限问题,在设置中开启Use the new Virtualization framework。
4.2 项目结构搭建:5 分钟初始化一个可运行骨架
按热词 react 、 express 、 nginx ,创建标准目录结构:
my-web-app/
├── docker-compose.yml # 主编排文件
├── .env # 环境变量(gitignore)
├── nginx/
│ └── conf.d/
│ └── default.conf # NGINX 配置
├── express-app/ # Express 后端
│ ├── package.json
│ ├── Dockerfile
│ └── server.js
└── react-app/ # React 前端(仅用于本地开发,构建产物放根目录)
└── ... # create-react-app 生成的文件
4.2.1 编写 docker-compose.yml (完整版)
version: "3.8"
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./react-build:/usr/share/nginx/html:ro
depends_on:
- express
networks:
- app-network
express:
build:
context: ./express-app
dockerfile: Dockerfile
environment:
- NODE_ENV=production
- DB_HOST=postgres
- DB_PORT=5432
- DB_NAME=myapp
- DB_USER=appuser
- DB_PASSWORD=secret
depends_on:
- postgres
networks:
- app-network
postgres:
image: postgres:14-alpine
environment:
- POSTGRES_DB=myapp
- POSTGRES_USER=appuser
- POSTGRES_PASSWORD=secret
volumes:
- db-data:/var/lib/postgresql/data
networks:
- app-network
networks:
app-network:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/16
volumes:
db-data:
4.2.2 创建 Express 后端(最小可行)
express-app/server.js :
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;
// 解析 JSON body
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// 健康检查端点
app.get('/health', (req, res) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() });
});
// 示例 API
app.get('/api/hello', (req, res) => {
res.json({ message: 'Hello from Express!' });
});
app.listen(PORT, '0.0.0.0', () => {
console.log(`Express server running on port ${PORT}`);
});
express-app/Dockerfile :
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
# 使用 npm ci 确保依赖精确一致
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
4.2.3 构建 React 前端并生成 react-build
热词有 react学习 、 react面试题 ,但构建步骤极简:
# 进入 react-app 目录
cd react-app
# 若未初始化,用 CRA 创建(跳过此步若已有项目)
# npx create-react-app .
# 构建生产版本
npm run build
# 构建产物在 build/ 目录,复制到根目录
cp -r build ../react-build
此时 ./react-build 目录存在,结构为:
react-build/
├── index.html
├── static/
│ ├── css/
│ └── js/
4.2.4 启动与验证:四步确认系统健康
# 1. 在 my-web-app/ 目录执行
docker compose up -d
# 2. 查看容器状态
docker compose ps
# 应显示 nginx, express, postgres 三者均为 "running"
# 3. 检查 NGINX 是否能访问 React
curl http://localhost
# 返回 react-build/index.html 内容
# 4. 检查 Express API 是否可达(通过 NGINX 代理)
curl http://localhost/api/hello
# 返回 {"message":"Hello from Express!"}
# 注意:不是 curl http://localhost:3000/api/hello,那是绕过 NGINX!
# 5. 检查健康检查
curl http://localhost/health
# 返回 404(NGINX 未配置 /health 路径),证明 NGINX 正在工作且未代理此路径
4.3 生产级增强:HTTPS、日志、监控的落地配置
4.3.1 NGINX 配置 HTTPS(以 Let's Encrypt 为例)
热词有 nginx安装配置 、 深入浅出nginx实战 ,HTTPS 是必选项。在 nginx/conf.d/default.conf 中添加:
server {
listen 443 ssl http2;
server_name your-domain.com;
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
# SSL 优化
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://express:3000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# HTTP 重定向到 HTTPS
server {
listen 80;
server_name your-domain.com;
return 301 https://$server_name$request_uri;
}
证书文件 fullchain.pem 和 privkey.pem 需提前放入 ./nginx/ssl/ 目录,并在 volumes: 中挂载:
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
- ./react-build:/usr/share/nginx/html:ro
4.3.2 日志集中化:用 logging 配置对接 ELK 或 Loki
热词有 ipv6 双栈 服务器 nginx 日志 ,说明日志是运维重点。在 docker-compose.yml 中为 NGINX 添加:
nginx:
# ... 其他配置
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
这会将日志写入 Docker 的 JSON 日志驱动,然后用 docker compose logs nginx 查看。若需对接 ELK,改用 driver: "fluentd" ,并配置 Fluentd 容器。
4.3.3 健康检查:让 Compose 知道服务是否真“活”
热词有 docker compose 部署 gerrit 、 docker compose 部署xinfenence ,这些复杂服务需健康检查。为 Express 添加:
express:
# ... 其他配置
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
这样 docker compose ps 会显示 healthy 或 unhealthy , depends_on: 结合 healthcheck 可确保 Express 真正就绪后才启动 NGINX。
5. 常见问题与排查技巧实录:那些让你凌晨三点爬起来的报错
5.1 典型问题速查表
| 现象 | 可能原因 | 排查命令 | 解决方案 |
|---|---|---|---|
curl http://localhost 返回 404 |
NGINX 配置未加载,或 react-build 目录为空 |
docker compose exec nginx ls -la /usr/share/nginx/html |
确认 ./react-build 存在且非空;检查 default.conf 是否在 conf.d/ 下 |
curl http://localhost/api/hello 返回 502 Bad Gateway |
Express 未启动,或 proxy_pass 地址错误 |
docker compose logs express ; docker compose exec nginx ping express |
查 Express 日志是否有启动错误; ping express 应通,不通则检查 networks: 配置 |
docker compose up 报 ERROR: for express Cannot create container for service express: invalid mount config for type "bind": bind source path does not exist |
./express-app 目录不存在,或路径大小写错误(Windows/macOS 不区分,Linux 区分) |
ls -la 看目录是否存在 |
确保 express-app/ 目录在 docker-compose.yml 同级 |
docker compose ps 显示 Up 2 seconds (unhealthy) |
Express 的 /health 端点返回非 200,或健康检查命令写错 |
docker compose exec express curl -v http://localhost:3000/health |
检查 Express 是否监听 0.0.0.0:3000 (非 127.0.0.1 ),且 /health 路由存在 |
Windows 上 docker compose up 报 The system cannot find the path specified |
./react-build 路径在 WSL2 中为 /home/user/project/react-build ,但 Docker Desktop 未共享该路径 |
在 Docker Desktop Settings → Resources → WSL Integration 中启用对应发行版 | 启用 WSL2 集成,并确保路径在 WSL |
更多推荐



所有评论(0)