Docker Compose 编排 Flask + MySQL + Redis:3 服务一键部署与健康检查配置
Docker Compose 编排 Flask + MySQL + Redis:3 服务一键部署与健康检查配置
现代Web应用开发中,微服务架构已成为主流趋势。将应用拆分为多个独立服务虽然提高了灵活性和可维护性,但也带来了部署复杂度。本文将手把手教你如何使用Docker Compose编排一个完整的Python Web应用栈,包含Flask应用、MySQL数据库和Redis缓存服务,并实现生产级健康检查机制。
1. 项目架构设计与准备工作
我们先来看一个典型的Python Web应用技术栈组成:
- Web框架 :Flask/Django等提供HTTP服务
- 数据库 :MySQL/PostgreSQL等关系型数据库
- 缓存 :Redis/Memcached等内存数据库
- 应用服务器 :Gunicorn/uWSGI等WSGI服务器
传统部署方式需要分别在服务器上安装配置这些组件,而使用Docker Compose可以:
- 通过容器化隔离各服务依赖环境
- 使用声明式配置定义服务间关系
- 实现一键启动/停止整个应用栈
项目目录结构 建议如下:
flask-demo/
├── app/ # Flask应用代码
│ ├── __init__.py
│ ├── routes.py
│ └── models.py
├── requirements.txt # Python依赖
├── Dockerfile # Flask应用镜像构建
├── docker-compose.yml # 多服务编排
├── .env # 环境变量配置
└── init.sql # 数据库初始化脚本
2. Flask应用容器化
首先我们需要将Flask应用打包为Docker镜像。采用多阶段构建可以显著减小最终镜像体积:
# 构建阶段
FROM python:3.11-slim as builder
WORKDIR /app
COPY requirements.txt .
# 安装构建依赖
RUN apt-get update && \
apt-get install -y --no-install-recommends \
gcc \
python3-dev \
&& rm -rf /var/lib/apt/lists/*
# 创建虚拟环境并安装依赖
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
RUN pip install --no-cache-dir -r requirements.txt
# 运行阶段
FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /opt/venv /opt/venv
COPY . .
ENV PATH="/opt/venv/bin:$PATH"
ENV FLASK_APP=app.py
ENV FLASK_ENV=production
# 创建非root用户运行
RUN useradd -m appuser && chown -R appuser:appuser /app
USER appuser
EXPOSE 5000
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "app:app"]
关键优化点:
- 使用slim基础镜像减少体积
- 多阶段构建分离构建环境和运行环境
- 创建专用用户增强安全性
- 使用Gunicorn替代Flask开发服务器
对应的
requirements.txt
应包含:
flask==2.3.0
gunicorn==21.2.0
mysql-connector-python==8.1.0
redis==4.6.0
3. Docker Compose服务编排
下面是完整的
docker-compose.yml
文件,编排三个服务并配置它们之间的依赖关系:
version: '3.8'
services:
app:
build: .
container_name: flask-app
ports:
- "5000:5000"
environment:
- DB_HOST=db
- DB_USER=${DB_USER}
- DB_PASSWORD=${DB_PASSWORD}
- REDIS_HOST=redis
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
networks:
- app-network
db:
image: mysql:8.0
container_name: mysql-db
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
MYSQL_DATABASE: ${DB_NAME}
volumes:
- mysql-data:/var/lib/mysql
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
ports:
- "3306:3306"
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-uroot", "-p${DB_PASSWORD}"]
interval: 10s
timeout: 5s
retries: 3
networks:
- app-network
redis:
image: redis:7-alpine
container_name: redis-cache
ports:
- "6379:6379"
volumes:
- redis-data:/data
command: redis-server --appendonly yes
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 3
networks:
- app-network
networks:
app-network:
driver: bridge
volumes:
mysql-data:
redis-data:
配置亮点:
- 服务发现 :通过服务名(db/redis)直接访问,Docker内置DNS解析
- 健康检查 :各服务定义健康检查策略,确保依赖服务就绪
- 数据持久化 :使用命名卷保存数据库和Redis数据
- 环境变量 :敏感信息通过.env文件配置
对应的
.env
文件示例:
DB_USER=appuser
DB_PASSWORD=securepassword
DB_NAME=flaskdb
4. 健康检查实现与优化
健康检查是生产环境部署的关键组件。我们在三个层面实现健康监控:
4.1 MySQL健康检查
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-uroot", "-p${DB_PASSWORD}"]
interval: 10s
timeout: 5s
retries: 3
4.2 Redis健康检查
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 3
4.3 Flask应用健康检查端点
在Flask应用中添加
/health
端点:
@app.route('/health')
def health():
try:
# 检查数据库连接
conn = mysql.connector.connect(
host=os.getenv('DB_HOST'),
user=os.getenv('DB_USER'),
password=os.getenv('DB_PASSWORD'),
database=os.getenv('DB_NAME')
)
conn.close()
# 检查Redis连接
redis_conn.ping()
return jsonify({'status': 'healthy'}), 200
except Exception as e:
return jsonify({'status': 'unhealthy', 'error': str(e)}), 500
然后在Compose中配置:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
5. 数据库初始化与数据持久化
为了保证数据库服务重启后数据不丢失,我们采用两种持久化方案:
5.1 MySQL数据卷
volumes:
- mysql-data:/var/lib/mysql
5.2 Redis AOF持久化
command: redis-server --appendonly yes
volumes:
- redis-data:/data
数据库初始化脚本
init.sql
示例:
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL,
email VARCHAR(100) NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO users (username, email) VALUES
('admin', 'admin@example.com'),
('user1', 'user1@example.com');
6. 网络配置与服务通信
Docker Compose默认会为应用创建专用网络,服务间通过服务名互相访问:
networks:
app-network:
driver: bridge
网络拓扑特点:
- 隔离性:与应用外部网络隔离
- 服务发现:容器间通过服务名解析IP
- 安全性:仅暴露必要端口到主机
7. 生产环境部署建议
将开发配置升级为生产配置需要考虑以下方面:
-
日志收集 :配置JSON格式日志并收集到ELK
import json import logging logging.basicConfig( level=logging.INFO, format='{"time": "%(asctime)s", "level": "%(levelname)s", "message": "%(message)s"}' ) -
性能调优 :
- MySQL:调整InnoDB缓冲池大小
- Redis:配置最大内存限制
- Gunicorn:根据CPU核心数设置worker数量
-
监控集成 :
- Prometheus指标端点
- 健康检查扩展为就绪检查/存活检查
-
安全加固 :
- 使用非root用户运行容器
- 限制容器资源使用
- 定期更新基础镜像
完整的生产级
docker-compose.prod.yml
示例:
version: '3.8'
services:
app:
build: .
image: my-registry/flask-app:${TAG:-latest}
deploy:
resources:
limits:
cpus: '1'
memory: 512M
ports:
- "5000:5000"
environment:
- DB_HOST=db
- DB_USER=${DB_USER}
- DB_PASSWORD=${DB_PASSWORD}
- REDIS_HOST=redis
- LOG_LEVEL=INFO
configs:
- source: gunicorn_conf
target: /app/gunicorn.conf.py
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
networks:
- app-network
db:
image: mysql:8.0
deploy:
resources:
limits:
cpus: '0.5'
memory: 1G
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
MYSQL_DATABASE: ${DB_NAME}
MYSQL_INNODB_BUFFER_POOL_SIZE: 256M
volumes:
- mysql-data:/var/lib/mysql
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-uroot", "-p${DB_PASSWORD}"]
interval: 10s
timeout: 5s
retries: 3
networks:
- app-network
redis:
image: redis:7-alpine
command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru
volumes:
- redis-data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 3
networks:
- app-network
networks:
app-network:
driver: bridge
volumes:
mysql-data:
redis-data:
configs:
gunicorn_conf:
file: ./gunicorn.conf.py
对应的Gunicorn配置文件
gunicorn.conf.py
:
import multiprocessing
bind = "0.0.0.0:5000"
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = "gevent"
max_requests = 1000
timeout = 120
keepalive = 5
accesslog = "-"
errorlog = "-"
loglevel = "info"
8. 实际部署操作流程
8.1 开发环境部署
# 构建并启动服务
docker-compose up -d --build
# 查看服务状态
docker-compose ps
# 查看应用日志
docker-compose logs -f app
# 停止服务
docker-compose down
8.2 生产环境部署
# 构建生产镜像
docker-compose -f docker-compose.prod.yml build
# 推送镜像到仓库
docker push my-registry/flask-app:latest
# 服务器拉取并启动
docker-compose -f docker-compose.prod.yml up -d
# 滚动更新
docker-compose -f docker-compose.prod.yml pull app
docker-compose -f docker-compose.prod.yml up -d --no-deps app
8.3 常用维护命令
# 进入容器执行命令
docker-compose exec app flask shell
# 备份数据库
docker-compose exec db mysqldump -u root -p${DB_PASSWORD} ${DB_NAME} > backup.sql
# 查看服务资源使用
docker stats
# 更新服务配置
docker-compose up -d --force-recreate
9. 故障排查与调试技巧
遇到部署问题时,可以按照以下步骤排查:
-
检查容器状态 :
docker-compose ps docker inspect <container_id> -
查看服务日志 :
docker-compose logs -f app docker-compose logs -f db -
手动测试连接 :
# 测试MySQL连接 docker-compose exec db mysql -u root -p${DB_PASSWORD} # 测试Redis连接 docker-compose exec redis redis-cli ping -
进入容器调试 :
docker-compose exec app bash curl http://localhost:5000/health -
网络连通性测试 :
docker-compose exec app ping db docker-compose exec app nc -zv db 3306
常见问题解决方案:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 应用启动失败 | 依赖服务未就绪 |
增加
depends_on
健康检查
|
| 数据库连接超时 | 网络配置错误 | 检查服务名称和网络配置 |
| 性能瓶颈 | 资源不足 | 调整CPU/内存限制 |
| 数据丢失 | 卷未正确挂载 | 验证卷挂载路径和权限 |
10. 扩展与进阶配置
10.1 添加Nginx反向代理
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- app
networks:
- app-network
示例
nginx.conf
:
events {
worker_connections 1024;
}
http {
upstream flask_app {
server app:5000;
}
server {
listen 80;
location / {
proxy_pass http://flask_app;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /static/ {
alias /app/static/;
}
}
}
10.2 集成Celery异步任务
services:
celery:
build: .
command: celery -A app.celery worker --loglevel=info
environment:
- BROKER_URL=redis://redis:6379/0
- RESULT_BACKEND=redis://redis:6379/1
depends_on:
- redis
networks:
- app-network
10.3 多环境配置管理
通过多个Compose文件实现环境差异化:
# 开发环境
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d
# 生产环境
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
docker-compose.dev.yml
示例:
version: '3.8'
services:
app:
environment:
- FLASK_ENV=development
- DEBUG=True
volumes:
- ./app:/app
11. 性能优化实践
11.1 镜像构建优化
-
使用
.dockerignore文件排除不需要的文件 - 多阶段构建减少最终镜像大小
- 合并RUN命令减少镜像层数
11.2 数据库优化
services:
db:
environment:
- MYSQL_INNODB_BUFFER_POOL_SIZE=256M
- MYSQL_INNODB_LOG_FILE_SIZE=64M
command:
--max_connections=200
--innodb_flush_log_at_trx_commit=2
11.3 Redis优化
services:
redis:
command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru
12. 安全最佳实践
-
最小权限原则 :
- 使用非root用户运行容器
-
限制容器能力
--cap-drop ALL
-
秘密管理 :
services: app: secrets: - db_password secrets: db_password: file: ./secrets/db_password.txt -
网络隔离 :
networks: app-network: internal: true -
镜像扫描 :
docker scan my-flask-app
13. 监控与日志收集
13.1 Prometheus监控
services:
prometheus:
image: prom/prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
networks:
- app-network
13.2 Grafana仪表盘
services:
grafana:
image: grafana/grafana
ports:
- "3000:3000"
volumes:
- grafana-data:/var/lib/grafana
networks:
- app-network
13.3 集中式日志
services:
loki:
image: grafana/loki
ports:
- "3100:3100"
networks:
- app-network
promtail:
image: grafana/promtail
volumes:
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- ./promtail-config.yml:/etc/promtail/config.yml
networks:
- app-network
14. CI/CD集成示例
.gitlab-ci.yml
示例:
stages:
- test
- build
- deploy
variables:
DOCKER_HOST: tcp://docker:2375
DOCKER_DRIVER: overlay2
services:
- docker:dind
test:
stage: test
image: python:3.11
script:
- pip install -r requirements.txt
- pytest
build:
stage: build
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker-compose -f docker-compose.prod.yml build
- docker push $CI_REGISTRY_IMAGE:latest
deploy:
stage: deploy
environment: production
script:
- ssh deploy@server "cd /app && docker-compose pull && docker-compose up -d"
only:
- main
15. 实际项目经验分享
在真实项目中部署这类技术栈时,有几个关键点值得注意:
- 数据库版本控制 :使用Flyway或Alembic管理数据库迁移,而不是直接执行SQL脚本
- 配置管理 :区分不同环境的配置,使用12-factor应用原则管理配置
- 启动顺序 :确保数据库完全初始化后再启动应用,可以添加初始化检查脚本
- 资源限制 :为每个服务设置合理的CPU和内存限制,避免单个服务耗尽资源
- 日志轮转 :配置日志轮转策略,避免日志占满磁盘空间
一个实用的初始化检查脚本示例:
import time
import mysql.connector
import redis
import os
import sys
def check_mysql(max_attempts=30, wait_seconds=1):
for _ in range(max_attempts):
try:
conn = mysql.connector.connect(
host=os.getenv('DB_HOST'),
user=os.getenv('DB_USER'),
password=os.getenv('DB_PASSWORD'),
database=os.getenv('DB_NAME')
)
conn.close()
return True
except Exception:
time.sleep(wait_seconds)
return False
def check_redis(max_attempts=30, wait_seconds=1):
for _ in range(max_attempts):
try:
r = redis.Redis(
host=os.getenv('REDIS_HOST'),
port=6379,
db=0
)
return r.ping()
except Exception:
time.sleep(wait_seconds)
return False
if __name__ == "__main__":
if not check_mysql():
print("MySQL check failed", file=sys.stderr)
sys.exit(1)
if not check_redis():
print("Redis check failed", file=sys.stderr)
sys.exit(1)
print("All dependencies are ready")
sys.exit(0)
在Dockerfile中作为健康检查使用:
HEALTHCHECK --interval=5s --timeout=3s --start-period=30s --retries=3 \
CMD python healthcheck.py || exit 1
更多推荐
所有评论(0)