Docker Compose 多容器部署:依赖管理实战
部署一个 Python + Redis + Nginx 的服务,容器启动顺序搞错了。Redis 还没 ready,Python 服务就报 Connection refused。排查了半天,发现是 depends_on 只保证容器启动顺序,不保证服务就绪。
问题现象
# docker-compose.yml
services:
web:
build: .
depends_on:
- redis
ports:
- "8000:8000"
redis:
image: redis:7-alpine
ports:
- "6379:6379"
docker-compose up -d
Python 服务启动后报错:
redis.exceptions.ConnectionError: Error 111 connecting to localhost:6379. Connection refused.
坑在于:depends_on 只保证 Redis 容器先启动,不保证 Redis 服务已经就绪。Redis 容器启动了,但 Redis 服务可能还在初始化。
解决方案
方案一:healthcheck + condition(推荐)
services:
web:
build: .
depends_on:
redis:
condition: service_healthy
ports:
- "8000:8000"
redis:
image: redis:7-alpine
ports:
- "6379:6379"
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
healthcheck 定义健康检查命令,condition: service_healthy 等待 Redis 健康后再启动 web。
方案二:启动脚本等待
# app.py
import redis
import time
def wait_for_redis(host, port, max_retries=30):
"""等待 Redis 就绪"""
for i in range(max_retries):
try:
r = redis.Redis(host=host, port=port)
r.ping()
print("Redis 已就绪")
return r
except redis.ConnectionError:
print(f"等待 Redis... ({i+1}/{max_retries})")
time.sleep(1)
raise Exception("Redis 连接超时")
r = wait_for_redis("redis", 6379)
坑在于:这个方案需要在应用代码里加等待逻辑,不够优雅。
方案三:wait-for-it 脚本
services:
web:
build: .
depends_on:
- redis
command: >
sh -c "wait-for-it -t 30 redis:6379 -- python app.py"
ports:
- "8000:8000"
# Dockerfile
FROM python:3.11-slim
# 安装 wait-for-it
RUN apt-get update && apt-get install -y wait-for-it && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
这个方案需要在镜像里安装 wait-for-it,增加镜像体积。
完整配置示例
Python + Redis + Nginx
services:
web:
build: .
depends_on:
redis:
condition: service_healthy
environment:
- REDIS_HOST=redis
- REDIS_PORT=6379
ports:
- "8000:8000"
redis:
image: redis:7-alpine
ports:
- "6379:6379"
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
volumes:
- redis_data:/data
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
depends_on:
web:
condition: service_started
volumes:
redis_data:
健康检查配置详解
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 10s # 检查间隔
timeout: 5s # 超时时间
retries: 3 # 重试次数
start_period: 30s # 启动等待时间
start_period 给容器启动的时间,这段时间内的失败不算在 retries 里。
网络配置
services:
web:
build: .
networks:
- app-network
redis:
image: redis:7-alpine
networks:
- app-network
networks:
app-network:
driver: bridge
同一个网络里的容器可以用服务名互相访问,比如 redis:6379。
常用命令
# 启动所有服务
docker-compose up -d
# 查看服务状态
docker-compose ps
# 查看日志
docker-compose logs -f web
# 重建并启动
docker-compose up -d --build
# 停止并清理
docker-compose down
# 停止并清理数据卷
docker-compose down -v
踩坑总结
depends_on只保证容器启动顺序,不保证服务就绪- 用
healthcheck+condition: service_healthy等待服务健康 start_period给容器启动的时间,避免误判- 同一个网络里的容器可以用服务名互相访问
- 生产环境用
docker-compose up -d --build重建镜像
从"容器启动了但服务没就绪"到"服务健康后再启动依赖",只需要配置 healthcheck + condition。坑在于:很多人只用 depends_on,不知道还有 condition 这个选项。