Docker Compose实战踩坑记那些年我遇到的配置陷阱
我的写法,亲测靠谱
用了几年Docker Compose,我发现很多人写yaml文件还是按照传统的docker run那种思路,一个容器一个文件,最后项目一复杂就乱得不行。我现在都是把相关的服务打包在一个compose文件里,这样管理起来方便多了。
先说说我觉得最靠谱的写法:
version: '3.8'
services:
web:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://user:pass@db:5432/myapp
depends_on:
- db
- redis
volumes:
- .:/app
- /app/node_modules
restart: unless-stopped
db:
image: postgres:13
environment:
POSTGRES_DB: myapp
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
volumes:
- postgres_data:/var/lib/postgresql/data
restart: unless-stopped
redis:
image: redis:alpine
restart: unless-stopped
volumes:
postgres_data:
这种写法的好处是所有环境变量都在docker-compose.yml里统一管理,不用再去搞什么.env文件。我一般这样处理数据库连接,用service name作为host,这样容器间通信很稳定。
这些错误写法,千万别学
最常见的错误就是直接暴露数据库端口,像这样:
# 错误示范
db:
image: postgres:13
ports:
- "5432:5432" # 千万别这么干
environment:
POSTGRES_PASSWORD: secret
这样做的问题是任何人都能连上你的数据库,安全风险太大。正确的做法是只在内部通信,外部通过web服务来访问数据。
另一个坑是volume挂载写错了,比如:
# 错误示范
web:
build: .
volumes:
- ./src:/app/src # 这样可能导致权限问题
- node_modules:/app/node_modules
这种写法在不同系统环境下容易出问题,Linux和Windows的权限机制不一样。我一般用排除node_modules的方式:
# 正确示范
web:
build: .
volumes:
- .:/app
- /app/node_modules # 这样会创建匿名卷,避免覆盖
实际项目中的坑
有个项目我折腾了好久才搞定启动顺序的问题。PostgreSQL启动需要时间,但是Node.js应用很快就启动完了,结果连接不上数据库。后来发现了depends_on其实只是等待容器启动,不一定等到服务就绪。
现在我会这样处理:
version: '3.8'
services:
web:
build: .
ports:
- "3000:3000"
depends_on:
db:
condition: service_healthy
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/myapp
db:
image: postgres:13
environment:
POSTGRES_DB: myapp
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d myapp"]
interval: 10s
timeout: 5s
retries: 5
volumes:
postgres_data:
这里的healthcheck确保PostgreSQL真的可以接受连接了,web服务才会启动。这个配置我用了很久,基本没出过问题。
还有个常见问题是环境变量的处理。之前我在.env文件里放了很多敏感信息,结果不小心提交到了git,被同事发现后一顿批评。现在我都是把敏感配置写在yml文件里,非敏感的才用env文件。
生产环境的部署注意点
生产环境和开发环境的配置差别很大。我一般会准备两套配置文件,docker-compose.yml给开发用,docker-compose.prod.yml给生产用。
生产环境的关键配置:
version: '3.8'
services:
web:
build: .
ports:
- "127.0.0.1:3000:3000" # 只绑定localhost
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://user:pass@db:5432/myapp
restart: always
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
db:
image: postgres:13
environment:
POSTGRES_DB: myapp
POSTGRES_USER: user
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
restart: always
secrets:
- db_password
secrets:
db_password:
file: ./secrets/db_password.txt
volumes:
postgres_data:
注意几个要点:端口只绑定本地回环地址、开启日志轮转防止磁盘爆满、密码用secrets管理而不是环境变量。这些都是生产环境的安全要求。
还有一点容易被忽视:容器资源限制。之前有个项目跑着跑着就把服务器内存吃光了,后来加上了限制:
web:
build: .
deploy:
resources:
limits:
memory: 512M
cpus: '0.5'
reservations:
memory: 256M
cpus: '0.25'
调试和监控的小技巧
日常开发的时候经常需要看日志,我一般这样起服务:
docker-compose up --build
build参数确保代码更新能生效。如果想后台运行:
docker-compose up -d
docker-compose logs -f web # 查看某个服务的日志
有时候需要进入容器调试:
docker-compose exec web sh
这种方式比docker attach好用,而且可以直接执行命令。我还习惯给docker-compose命令加个别名,在.bashrc里:
alias dco='docker-compose'
这样输入命令更快。
网络配置很重要
默认情况下所有service都在同一个bridge network里,大部分情况够用了。但是有时候需要自定义网络配置,比如服务隔离或者跨机器部署。
自定义网络的写法:
version: '3.8'
services:
web:
build: .
networks:
- frontend
- backend
ports:
- "3000:3000"
db:
image: postgres:13
networks:
- backend
environment:
POSTGRES_PASSWORD: pass
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true # 内网网络,不能访问外网
internal设置为true的网络不能访问外网,这样数据库就更加安全了。不过要注意,如果backend需要拉取远程镜像就不行了。
版本选择有讲究
Docker Compose有很多版本,建议用3.8以上,新特性支持得比较好。低版本可能会遇到一些奇怪的问题,比如healthcheck不生效。
另外要注意docker-compose和docker engine的兼容性,版本太老的话有些功能用不了。我一般保持docker版本比较新,这样新特性都能用上。
以上是我个人对Docker Compose的一些实践经验总结,主要是一些容易踩坑的地方和解决方案。这个工具用得越久就越觉得设计得不错,不过确实需要一些摸索才能用得顺手。有更优的实现方式欢迎评论区交流。

暂无评论