Docker Compose 实战:构建高可用 Web 服务集群 原创
温馨提示:
本文最后更新于 2026-06-24,已超过 78 天没有更新。
若文章内的图片失效(无法正常加载),请留言反馈或直接 联系我。
Docker Compose 实战:构建高可用 Web 服务集群
在现代 Web 服务架构中,Docker Compose 已经成为容器编排的事实标准之一。它让开发者能够用声明式的方式定义多容器应用,实现一键部署。本文将深入讲解如何使用 Docker Compose 构建一个高可用的 Web 服务集群,涵盖负载均衡、数据库主从、缓存层和监控告警。
一、架构设计
我们的目标是构建一个生产级别的 Web 服务集群,包含以下组件:
- Nginx — 反向代理与负载均衡
- PHP-FPM — 应用服务器(多实例)
- MySQL — 主从复制数据库
- Redis — 缓存层
- Prometheus + Grafana — 监控告警
二、项目结构
web-cluster/
├── docker-compose.yml
├── .env
├── nginx/
│ ├── Dockerfile
│ └── conf.d/
│ ├── default.conf
│ └── upstream.conf
├── php/
│ ├── Dockerfile
│ └── php.ini
├── mysql/
│ ├── master/
│ │ ├── Dockerfile
│ │ └── my.cnf
│ └── slave/
│ ├── Dockerfile
│ └── my.cnf
├── redis/
│ └── redis.conf
├── monitoring/
│ ├── prometheus.yml
│ └── grafana/
│ └── datasources.yml
└── app/
└── index.php
三、核心配置详解
3.1 docker-compose.yml
version: '3.8'
services:
# Nginx 负载均衡
nginx:
build: ./nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d
- ./app:/var/www/html
depends_on:
- php1
- php2
networks:
- webnet
restart: always
healthcheck:
test: ["CMD", "nginx", "-t"]
interval: 30s
timeout: 5s
retries: 3
# PHP-FPM 实例 1
php1:
build: ./php
volumes:
- ./app:/var/www/html
environment:
- DB_HOST=mysql_master
- DB_DATABASE=appdb
- DB_USERNAME=appuser
- DB_PASSWORD=${DB_PASSWORD}
- REDIS_HOST=redis
depends_on:
- mysql_master
- redis
networks:
- webnet
restart: always
# PHP-FPM 实例 2
php2:
build: ./php
volumes:
- ./app:/var/www/html
environment:
- DB_HOST=mysql_master
- DB_DATABASE=appdb
- DB_USERNAME=appuser
- DB_PASSWORD=${DB_PASSWORD}
- REDIS_HOST=redis
depends_on:
- mysql_master
- redis
networks:
- webnet
restart: always
# MySQL 主库
mysql_master:
build: ./mysql/master
volumes:
- mysql_master_data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: appdb
MYSQL_USER: appuser
MYSQL_PASSWORD: ${DB_PASSWORD}
ports:
- "3306:3306"
networks:
- webnet
restart: always
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
# MySQL 从库
mysql_slave:
build: ./mysql/slave
volumes:
- mysql_slave_data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
depends_on:
- mysql_master
networks:
- webnet
restart: always
# Redis 缓存
redis:
image: redis:7-alpine
volumes:
- ./redis/redis.conf:/usr/local/etc/redis/redis.conf
- redis_data:/data
command: redis-server /usr/local/etc/redis/redis.conf
ports:
- "6379:6379"
networks:
- webnet
restart: always
# Prometheus 监控
prometheus:
image: prom/prometheus:latest
volumes:
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
ports:
- "9090:9090"
networks:
- webnet
restart: always
# Grafana 可视化
grafana:
image: grafana/grafana:latest
volumes:
- ./monitoring/grafana:/etc/grafana/provisioning
- grafana_data:/var/lib/grafana
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD}
depends_on:
- prometheus
networks:
- webnet
restart: always
volumes:
mysql_master_data:
mysql_slave_data:
redis_data:
prometheus_data:
grafana_data:
networks:
webnet:
driver: bridge
3.2 Nginx 负载均衡配置
# nginx/conf.d/upstream.conf
upstream php_backend {
least_conn;
server php1:9000 weight=3 max_fails=3 fail_timeout=30s;
server php2:9000 weight=2 max_fails=3 fail_timeout=30s;
keepalive 32;
}
# nginx/conf.d/default.conf
server {
listen 80;
server_name example.com;
root /var/www/html;
index index.php;
# 静态文件缓存
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff2)$ {
expires 30d;
add_header Cache-Control "public, immutable";
access_log off;
}
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass php_backend;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_keep_conn on;
fastcgi_read_timeout 300;
}
# 健康检查端点
location /health {
access_log off;
return 200 "OK\n";
add_header Content-Type text/plain;
}
}
3.3 MySQL 主从配置
# mysql/master/my.cnf
[mysqld]
server-id = 1
log_bin = /var/log/mysql/mysql-bin.log
binlog_do_db = appdb
expire_logs_days = 7
max_binlog_size = 100M
innodb_flush_log_at_trx_commit = 1
sync_binlog = 1
# mysql/slave/my.cnf
[mysqld]
server-id = 2
relay_log = /var/log/mysql/mysql-relay-bin.log
log_bin = /var/log/mysql/mysql-bin.log
binlog_do_db = appdb
read_only = 1
skip_slave_start = 0
四、部署与运维
4.1 一键部署
# 1. 克隆项目并配置环境变量
git clone https://github.com/your-repo/web-cluster.git
cd web-cluster
cp .env.example .env
# 编辑 .env 文件设置密码
# 2. 启动所有服务
docker compose up -d
# 3. 验证服务状态
docker compose ps
docker compose logs -f
# 4. 初始化 MySQL 主从复制
docker compose exec mysql_master mysql -u root -p
# 在主库创建复制用户
CREATE USER 'replicator'@'%' IDENTIFIED BY 'password';
GRANT REPLICATION SLAVE ON *.* TO 'replicator'@'%';
FLUSH PRIVILEGES;
SHOW MASTER STATUS;
# 在从库配置复制
docker compose exec mysql_slave mysql -u root -p
CHANGE MASTER TO
MASTER_HOST='mysql_master',
MASTER_USER='replicator',
MASTER_PASSWORD='password',
MASTER_LOG_FILE='mysql-bin.000001',
MASTER_LOG_POS=0;
START SLAVE;
SHOW SLAVE STATUS\G
4.2 扩容与缩容
# 增加 PHP-FPM 实例
docker compose up -d --scale php=4
# 减少实例
docker compose up -d --scale php=2
# 滚动更新
docker compose build php
docker compose up -d --no-deps --scale php=0 php
docker compose up -d --no-deps --scale php=4 php
五、监控与告警
5.1 Prometheus 配置
# monitoring/prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
alerting:
alertmanagers:
- static_configs:
- targets: []
rule_files:
- "alerts.yml"
scrape_configs:
- job_name: 'nginx'
static_configs:
- targets: ['nginx:9113']
- job_name: 'php-fpm'
static_configs:
- targets: ['php1:9253', 'php2:9253']
- job_name: 'mysql'
static_configs:
- targets: ['mysql_master:9104', 'mysql_slave:9104']
- job_name: 'redis'
static_configs:
- targets: ['redis:9121']
- job_name: 'node'
static_configs:
- targets: ['node_exporter:9100']
5.2 Grafana 面板
Grafana 预配置了以下仪表盘:
- Web 服务概览 — 请求量、响应时间、错误率
- 数据库性能 — 查询延迟、连接数、主从延迟
- 缓存命中率 — Redis 内存使用、命中率
- 系统资源 — CPU、内存、磁盘、网络
六、性能调优
6.1 Nginx 优化
# 调整 worker 进程数
worker_processes auto;
# 开启 gzip 压缩
gzip on;
gzip_types text/plain text/css application/json application/javascript;
gzip_min_length 1000;
gzip_comp_level 6;
# 连接池优化
keepalive_timeout 65;
client_max_body_size 100M;
6.2 PHP-FPM 优化
; php/php.ini
memory_limit = 256M
max_execution_time = 300
upload_max_filesize = 50M
post_max_size = 50M
opcache.enable = 1
opcache.memory_consumption = 128
opcache.max_accelerated_files = 10000
; PHP-FPM pool 配置
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35
七、安全加固
- 网络隔离:使用自定义网络,仅暴露必要端口
- 密钥管理:敏感信息通过 .env 文件管理,不硬编码
- 资源限制:为每个容器设置 CPU 和内存限制
- 日志轮转:配置 Docker 日志驱动,避免磁盘爆满
- 定期更新:使用
docker compose pull更新基础镜像
# 在 docker-compose.yml 中添加资源限制
services:
php1:
deploy:
resources:
limits:
cpus: '2'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M
总结
通过 Docker Compose 构建高可用 Web 服务集群,不仅简化了部署流程,还提供了弹性伸缩和故障恢复能力。本文的配置可以直接用于生产环境,但需要根据实际业务需求进行调整。关键要点:
- 使用
healthcheck确保服务健康 - 配置
restart: always实现自动恢复 - 使用
.env管理敏感配置 - 部署完整的监控告警体系
- 定期备份数据库和持久化数据