1. Docker Compose 初探:从单容器到多服务编排
第一次接触Docker时,我像大多数开发者一样从单个容器开始练习。但当我尝试部署一个包含MySQL、Redis和Node.js应用的完整项目时,频繁的手动操作和容器间依赖管理让我意识到:需要更高效的工具。这就是docker-compose.yml出现的背景——它用声明式语法将多容器应用的部署流程代码化。
在微服务架构普及的当下,一个典型的后端系统往往由多个服务组成。以我最近部署的电商项目为例,需要同时启动:
- 用户服务(Spring Boot)
- 商品服务(Python Flask)
- MySQL数据库
- Redis缓存
- Nginx网关
手动用docker run逐个启动这些容器不仅繁琐,还要处理网络连接、环境变量传递、数据卷挂载等复杂问题。而docker-compose.yml文件就像乐高说明书,用YAML格式清晰定义:
- 每个服务的构建配置
- 容器间的依赖关系
- 共享网络和存储方案
- 运行时参数
提示:YAML对缩进敏感,建议使用2个空格(非Tab)进行缩进,这是实践中最容易出错的细节之一
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 解剖docker-compose.yml:核心字段详解
2.1 版本声明与基础结构
每个docker-compose.yml文件开头都需要声明版本,这决定了可用功能的范围。目前主流版本有:
yaml复制version: '3.8' # 支持的最新稳定版
services:
web:
image: nginx:alpine
db:
image: postgres:13
版本差异直接影响功能可用性。比如:
- 3.x版本支持服务部署配置(deploy)
- 2.x版本更侧重单机开发环境
- 1.x已基本淘汰
我在生产环境常用3.8版本,因为它同时支持:
- 资源限制(cpus/mem_limit)
- 服务健康检查
- 密文管理(secrets)
2.2 服务定义的艺术
services部分是核心,每个键代表一个容器服务。以部署WordPress为例:
yaml复制services:
wordpress:
image: wordpress:php8.0
ports:
- "8080:80"
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wpuser
depends_on:
- db
db:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: example
关键配置项解析:
- ports:将容器端口映射到主机,格式为"主机端口:容器端口"
- environment:设置环境变量,敏感信息应使用secrets
- depends_on:控制启动顺序,但不保证服务就绪(需配合健康检查)
避坑指南:避免在production环境使用depends_on做服务依赖判断,应该实现真正的健康检查机制
2.3 网络与存储配置
默认情况下,compose会创建专属网络,所有服务通过服务名互访。自定义网络配置示例:
yaml复制networks:
app_net:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/24
volumes:
db_data:
driver: local
实际项目中我常用这些技巧:
- 为不同业务模块划分独立网络(如frontend/backend)
- 生产环境使用overlay驱动支持多主机通信
- 命名volume比主机挂载更易维护
3. 进阶配置实战技巧
3.1 多环境适配方案
通过extends和env_file实现开发/生产配置分离:
yaml复制# docker-compose.base.yml
services:
app:
image: ${APP_IMAGE}
env_file:
- .env.${ENV_MODE}
# docker-compose.prod.yml
version: '3.8'
services:
app:
extends:
file: docker-compose.base.yml
service: app
deploy:
replicas: 3
启动时指定环境变量:
bash复制ENV_MODE=prod APP_IMAGE=myapp:1.0 docker-compose -f docker-compose.prod.yml up
3.2 资源限制与健康检查
保证服务稳定性的关键配置:
yaml复制services:
api:
image: myapi:latest
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
3.3 构建与调试技巧
开发阶段推荐使用build替代image,配合Dockerfile热更新:
yaml复制services:
web:
build:
context: .
dockerfile: Dockerfile.dev
args:
NODE_ENV: development
volumes:
- .:/code
- /code/node_modules
调试时常用命令:
bash复制# 查看服务日志
docker-compose logs -f web
# 进入容器shell
docker-compose exec web sh
# 重建单个服务
docker-compose up -d --no-deps --build web
4. 生产环境最佳实践
4.1 安全加固方案
- 非root用户运行容器:
yaml复制services:
redis:
image: redis:6
user: "1000:1000"
- 只读文件系统:
yaml复制services:
api:
read_only: true
tmpfs:
- /tmp
- 密钥管理:
yaml复制secrets:
db_password:
file: ./secrets/db_password.txt
services:
db:
secrets:
- db_password
4.2 性能优化策略
- 使用alpine基础镜像减小体积
- 配置合理的资源限制
- 日志轮转配置:
yaml复制services:
nginx:
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
4.3 监控与日志方案
集成Prometheus监控示例:
yaml复制services:
node_exporter:
image: prom/node-exporter
ports:
- "9100:9100"
prometheus:
image: prom/prometheus
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
5. 常见问题排雷指南
5.1 容器启动顺序问题
症状:服务A报错"无法连接服务B"
解决方案:
- 添加健康检查
- 使用wait-for-it.sh脚本:
yaml复制command: ["./wait-for-it.sh", "db:3306", "--", "npm", "start"]
5.2 端口冲突处理
当出现"端口已占用"错误时:
- 查看占用进程:
bash复制lsof -i :8080
- 修改compose文件端口映射:
yaml复制ports:
- "8081:80"
5.3 变量替换失效
确保.env文件存在且变量命名正确:
code复制# .env
DB_HOST=mysql
DB_PORT=3306
引用方式:
yaml复制environment:
DATABASE_URL: "jdbc:mysql://${DB_HOST}:${DB_PORT}/app"
5.4 Windows路径问题
处理volume挂载时的路径差异:
yaml复制volumes:
# Linux/Mac
- ./data:/app/data
# Windows
- .\data:/app/data
6. 现代替代方案比较
虽然docker-compose仍是本地开发的首选,但在生产环境可以考虑:
-
Kubernetes + Helm
- 更适合大规模集群
- 提供更完善的滚动更新、自愈能力
-
Docker Swarm
- 内置在Docker Engine中
- 学习曲线较平缓
-
Nomad
- 轻量级调度器
- 支持多种任务类型
对于大多数中小项目,我仍然推荐使用docker-compose部署,它的优势在于:
- 配置即文档
- 一键环境复现
- 与CI/CD管道无缝集成
最后分享一个真实案例:我曾用docker-compose部署包含15个微服务的系统,通过合理配置将原本需要2小时的部署时间缩短到15分钟。关键在于:
- 精心设计的网络拓扑
- 共享基础服务(如日志收集器)
- 分层的compose文件结构
