一、最小可用的 compose 文件
services:
web:
build: .
ports:
- "8080:3000"
environment:
- NODE_ENV=production
depends_on:
- db
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: example
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
新版本用 compose.yaml(不再需要 version: 字段),旧教程里的 version: "3" 已可省略。
二、build 还是 image
build: .:用当前目录的 Dockerfile 构建,适合自己开发的服务。image: postgres:16:直接用现成镜像,适合中间件。- 两者可以同时写:有
image名字时构建结果会打上这个 tag。
api:
build:
context: .
dockerfile: Dockerfile.prod
args:
- NODE_ENV=production
image: myapi:1.0
三、端口与数据卷
ports:
- "127.0.0.1:8080:3000" # 只监听本机,更安全
- "8080:3000" # 监听所有网卡
volumes:
- ./conf:/etc/app/conf:ro # 只读挂载配置
- uploads:/app/uploads # 命名卷存数据
ports 是「宿主:容器」,只写一个端口时 Docker 随机分配宿主端口。数据库这类不该对外暴露的服务,不要写 ports,同一网络内用服务名访问即可。
四、depends_on 不等于「等它就绪」
这是最大的误解:
depends_on:
- db
它只保证启动顺序(先起 db 容器),不保证 db 已经能接受连接。应用启动太快就会连不上数据库然后退出。
正确做法是加 healthcheck 并用条件等待(新版 compose 支持):
db:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 10
api:
depends_on:
db:
condition: service_healthy
或者更稳妥:应用自身做「连接重试」,不依赖编排顺序。生产环境更推荐后者,因为容器随时可能重启。
五、环境变量管理
env_file:
- .env
environment:
- TZ=Asia/Shanghai
优先级:environment 高于 env_file,env_file 高于宿主机同名变量。.env 里不要提交真实密码,加进 .gitignore,生产用 CI 注入或密钥管理。
六、常用命令
docker compose up -d # 后台启动(首次会构建)
docker compose up -d --build # 改了代码强制重建
docker compose ps # 状态
docker compose logs -f api # 跟日志
docker compose exec api sh # 进容器
docker compose restart api
docker compose down # 停止并删除容器/网络(保留卷)
docker compose down -v # 连卷一起删(数据会没)
docker compose config # 校验并展开最终配置
docker compose config 在排查「变量没生效」时特别有用,它会把最终解析结果打出来。
七、几个坑
- 服务名就是主机名:应用里连数据库要写
db:5432,不是localhost。 - 代码改动没生效:镜像是构建时固化的,改代码要
--build,或用 volume 挂载源码做开发。 - 卷不删除:
down默认保留命名卷,下次up数据还在;想清空要-v。 - 宿主机端口冲突:
port is already allocated,改宿主端口或先停掉占用的进程。 - 文件权限:容器内用户 UID 与宿主机不一致时,挂载目录可能出现写不进去的情况,需指定
user:或调整目录属主。
小结
build 管自建服务、image 管中间件;depends_on 只管顺序,要等就绪得配 healthcheck;服务名即主机名;down -v 会删数据,执行前确认。