前后端一把拉起:Docker Compose 编排告别手动起服务
一、背景:手动起服务,环境一换就抓瞎
本文把"多容器小项目"用 compose 编排讲透:服务怎么定义、容器间怎么互通、数据怎么持久化、常见坑。它和 27 篇是递进——27 讲"怎么把单个服务容器化",这篇讲"多个服务怎么一起管"。
二、一个典型的多服务 compose
假设项目有前端、后端、再加一个数据库(演示用):
services:
db:
image: mysql:8
environment:
MYSQL_ROOT_PASSWORD: "devpass"
MYSQL_DATABASE: "app"
volumes:
- db-data:/var/lib/mysql # 数据持久化
ports: ["3306:3306"]
backend:
build: ./backend
environment:
DB_HOST: db # 用服务名当主机名
depends_on: [db]
ports: ["3000:3000"]
frontend:
build: ./frontend
depends_on: [backend]
ports: ["8080:80"]
# 前端 Nginx 反代 /api -> http://backend:3000
volumes:
db-data:
三个 services:db / backend / frontend。depends_on 表达启动顺序(先 db、再 backend、再 frontend)。容器间通信用服务名当主机名——backend 连数据库填 DB_HOST: db、compose 内置 DNS 把 db 解析到 db 容器,不用写 IP。volumes 把 MySQL 数据目录挂到命名卷 db-data,容器重建数据不丢。前端 Nginx 反代 backend:3000(服务名),和 27 篇一致。
三、为什么用服务名就能互通
手动 docker run 多个容器时,容器 IP 是随机的、重启还变,互相连极麻烦。Compose 给同一文件里的服务建了一个用户自定义网络,服务名就是这个网络里的主机名。所以 backend 容器里 mysql -h db 能连上 db 容器,frontend 的 Nginx proxy_pass http://backend:3000 也能解析——全靠 compose 的网络和 DNS,你不用管 IP、不用 --link。这是 compose 相比裸 docker run 最大的省心事之一。
四、数据持久化:卷是关键
容器本身是"用完可弃"的,里面写的任何东西容器一删就没。数据库、用户上传这类要留的数据,必须用**卷(volume)或绑定挂载(bind mount)**挂出来:
volumes: db-data:/var/lib/mysql:命名卷,由 Docker 管理,适合数据库数据;./uploads:/app/uploads:绑定挂载,把宿主机目录挂进容器,适合开发时实时看文件、或用户上传要落到宿主机。
演示里 db 用命名卷,重启 compose down 后数据还在(down 默认不清卷;down -v 才连卷一起删,慎用)。不挂卷的容器(如 frontend 静态文件,构建进镜像的)重启就重置,那是预期。关键数据务必挂卷,否则"容器一重建、库空了"。
五、常用命令与开发流程
docker compose up -d --build # 构建并后台启动全部
docker compose ps # 看各服务状态
docker compose logs backend # 看某个服务日志
docker compose down # 停止并删容器(默认留卷)
docker compose restart backend # 只重启后端
up -d --build 一次起全部,-d 后台、--build 重新构建镜像(改了代码要加)。logs <服务> 单独看某服务日志,排查哪挂了。down 停服,down -v 才删卷(清数据)。开发时改代码 → up --build 或 restart,流程固定、不用记一堆 run 参数。具体命令以 Docker 官方文档为准。
六、几个容易踩的坑
第一,depends_on 只管启动顺序、不管就绪。backend depends_on: [db] 只保证"db 容器先启动",不保证"MySQL 已经能接受连接"。如果 backend 起来立刻连 db,可能连不上(MySQL 还在初始化)。生产要解决"等待就绪"(如 backend 加重连逻辑、或用 depends_on 的 condition: service_healthy 健康检查,以文档为准),别假设 depends_on 等于"依赖已可用"。
第二,端口冲突。宿主机 3306/3000/8080 若本机已占用(如本机已跑着 MySQL),compose 映射会失败。开发环境常用不同宿主机端口(如 3307:3306),或确保本机没占。端口映射 宿主机:容器,冲突的是宿主机侧。
第三,.env 与密码。compose 里 MYSQL_ROOT_PASSWORD: "devpass" 明文写在文件里、还提交了仓库,是隐患。用 .env 文件 + ${DB_PASS} 引用、.env 不提交,或接密钥管理。开发用弱密码可接受,生产务必走环境变量/凭据,别硬编码。
第四,构建上下文与 .dockerignore。每个 build: ./backend 把该目录作构建上下文,务必在子目录放 .dockerignore 排除 node_modules/.git,否则构建慢、还可能把本机依赖带进镜像导致跨平台跑不起来(见 27 篇)。
第五,前端反代地址。前端容器里反代填的是服务名 backend:3000(容器内网络),不是 localhost——localhost 在 frontend 容器里指的是 frontend 自己,连不到 backend。这个"容器内 localhost ≠ 宿主机 localhost"的坑,新手必踩一次。
七、小结
Docker Compose 把"多容器小项目"从一堆 docker run 参数变成一份 docker-compose.yml:服务定义、用服务名互通(compose 内置网络 DNS)、depends_on 定顺序、卷做数据持久化,一条 up 全起。重点是——容器间用服务名当主机名(不是 localhost/IP)、关键数据挂卷(命名卷/绑定挂载)、depends_on 只管启动顺序不管就绪(就绪要靠重连或健康检查)。端口冲突、明文密码、反代写 localhost 是三个高频坑。Docker Compose 语法与命令以官方文档为准。
- 点赞
- 收藏
- 关注作者
评论(0)