Docker Compose用于定义和运行多容器Docker应用。在Web项目中,前端、后端API、数据库、缓存等服务需要协同工作,手动逐个启动容器效率低且容易出错。通过docker-compose.yml文件声明式定义服务拓扑,一条命令完成所有服务的构建、启动和依赖编排。本文以典型的LNMP+Redis架构为例,讲解Compose文件编写、网络隔离、数据持久化和部署自动化。
docker-compose.yml文件结构与基础配置
一个包含Nginx、PHP-FPM、MySQL、Redis的Web应用编排文件示例:
version: "3.8"
services:
nginx:
image: nginx:1.25-alpine
container_name: web-nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
- ./app/public:/var/www/html/public:ro
- nginx-logs:/var/log/nginx
depends_on:
- php-fpm
networks:
- frontend
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://localhost/health"]
interval: 30s
timeout: 5s
retries: 3
php-fpm:
build:
context: ./php
dockerfile: Dockerfile
container_name: web-php
volumes:
- ./app:/var/www/html
- ./php/php.ini:/usr/local/etc/php/php.ini:ro
networks:
- frontend
- backend
restart: unless-stopped
environment:
- DB_HOST=mysql
- DB_NAME=appdb
- REDIS_HOST=redis
mysql:
image: mysql:8.0
container_name: web-mysql
volumes:
- mysql-data:/var/lib/mysql
- ./mysql/init:/docker-entrypoint-initdb.d:ro
- ./mysql/conf.d:/etc/mysql/conf.d:ro
networks:
- backend
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: appdb
MYSQL_USER: appuser
MYSQL_PASSWORD: ${MYSQL_APP_PASSWORD}
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
redis:
image: redis:7-alpine
container_name: web-redis
volumes:
- redis-data:/data
- ./redis/redis.conf:/etc/redis/redis.conf:ro
networks:
- backend
restart: unless-stopped
command: redis-server /etc/redis/redis.conf --requirepass ${REDIS_PASSWORD}
volumes:
mysql-data:
redis-data:
nginx-logs:
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true
网络隔离策略与容器间通信
上面的配置定义了两个网络:frontend和backend。Nginx同时接入两个网络,作为反向代理连接外部请求与内部服务。PHP-FPM跨接两个网络,接收Nginx转发的FastCGI请求,同时访问后端数据库和缓存。MySQL和Redis只接入backend网络。
backend网络设置internal: true,该网络不与宿主机网络连通,容器在该网络中无法访问外部互联网,外部也无法直接访问该网络中的容器。这意味着MySQL和Redis完全不可能被外部直接访问,只能通过同一网络内的PHP-FPM容器连接。
# 验证网络隔离效果
# 从宿主机直接连接MySQL——应失败
mysql -h 127.0.0.1 -P 3306 -u root -p
# ERROR: Connection refused(端口未映射到宿主机)
# 从PHP容器内部连接MySQL——应成功
docker exec web-php mysql -h mysql -u appuser -p
# 查看容器的网络连接
docker inspect web-php --format='{{json .NetworkSettings.Networks}}' | python -m json.tool
容器间通过服务名互访是Docker Compose的内置功能。DNS解析由Docker内嵌的DNS服务器自动处理,mysql、redis这些服务名直接作为主机名使用,无需配置hosts文件。服务扩容后新容器自动注册到DNS,其他容器能立即发现新实例。
数据持久化与Volume管理
容器本身是无状态且临时的,容器删除后内部数据丢失。需要持久化的数据通过Volume挂载到宿主机。上面的配置中使用了三种Volume策略:
# 命名Volume:由Docker管理,适合数据库等需要高性能IO的场景
volumes:
- mysql-data:/var/lib/mysql
# 绑定挂载:直接映射宿主机目录,适合配置文件和代码热更新
volumes:
- ./app:/var/www/html
- ./nginx/conf.d:/etc/nginx/conf.d:ro
# tmpfs挂载:内存中的临时文件系统,适合敏感数据和临时文件
volumes:
- tmpfs:/tmp
命名Volume存储在Docker管理的目录中(默认/var/lib/docker/volumes/),性能优于绑定挂载,因为Docker可以使用overlay2等优化的存储驱动。MySQL的数据目录使用命名Volume,避免宿主机文件系统权限问题。代码目录使用绑定挂载,开发环境下修改代码立即生效,无需重建镜像。
Volume的备份与迁移:
# 备份MySQL数据Volume
docker run --rm \
-v mysql-data:/data:ro \
-v $(pwd)/backup:/backup \
alpine tar czf /backup/mysql-$(date +%Y%m%d).tar.gz -C /data .
# 恢复MySQL数据Volume
docker run --rm \
-v mysql-data:/data \
-v $(pwd)/backup:/backup:ro \
alpine sh -c "rm -rf /data/* && tar xzf /backup/mysql-20260805.tar.gz -C /data"
# 清理未使用的Volume(谨慎操作)
docker volume prune
Health Check与服务依赖编排
depends_on只保证容器启动顺序,不等待依赖服务就绪。MySQL容器启动后需要数秒完成初始化,如果PHP-FPM在MySQL就绪前连接数据库会报错。通过Health Check解决此问题:
mysql:
healthcheck:
test: ["CMD-SHELL", "mysqladmin ping -h localhost -u root -p${MYSQL_ROOT_PASSWORD} --silent"]
interval: 10s
timeout: 5s
retries: 10
start_period: 30s
php-fpm:
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_started
condition: service_healthy表示PHP-FPM等待MySQL的healthcheck通过后才启动。start_period: 30s给MySQL 30秒的初始化窗口,在此期间healthcheck失败不计入重试次数。retries设为10次、每次间隔10秒,最长等待100秒,覆盖大多数数据库初始化场景。
多环境配置与CI/CD集成
开发、测试、生产环境使用不同的Compose配置文件。通过docker-compose.override.yml机制实现环境差异覆盖:
# 开发环境override示例
version: "3.8"
services:
php-fpm:
build:
target: dev
volumes:
- ./app:/var/www/html
environment:
- PHP_DEBUG=1
- XDEBUG_MODE=develop,debug
# 生产环境prod示例
version: "3.8"
services:
php-fpm:
build:
target: prod
deploy:
replicas: 3
resources:
limits:
cpus: '2'
memory: 1G
environment:
- PHP_DEBUG=0
# 启动命令
# 开发环境(自动加载override)
docker compose up -d
# 生产环境
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
CI/CD流水线中的自动化部署脚本:
#!/bin/bash
set -e
IMAGE_TAG=$(git rev-parse --short HEAD)
git pull origin main
docker compose -f docker-compose.yml -f docker-compose.prod.yml build \
--build-arg BUILD_TAG=$IMAGE_TAG
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d \
--no-deps --build php-fpm
docker image prune -f --filter "until=168h"
sleep 10
curl -f http://localhost/health || {
echo "Health check failed, rolling back"
docker compose -f docker-compose.yml -f docker-compose.prod.yml \
up -d --no-deps php-fpm:previous
exit 1
}
echo "Deployment successful: $IMAGE_TAG"
滚动更新通过--no-deps只重建PHP-FPM容器而不影响数据库和缓存服务。部署后的健康检查失败时回滚到上一个镜像版本。通过Git commit hash作为镜像标签,每次部署都可追溯到具体的代码版本。
日志收集与容器监控
多容器环境中日志分散在各容器内部,集中收集是运维的基础。使用Docker的json-file日志驱动配合日志轮转:
# docker-compose.yml中为每个服务配置日志驱动
services:
php-fpm:
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
nginx:
logging:
driver: json-file
options:
max-size: "10m"
max-file: "5"
max-size限制单个日志文件大小,max-file限制保留的日志文件数量。超出后自动轮转删除最旧文件。避免容器日志无限增长导致磁盘满。
# 查看所有服务的实时日志
docker compose logs -f
# 只看特定服务最近100行
docker compose logs --tail 100 php-fpm
# 查看特定时间段的日志
docker compose logs --since "2026-08-05T10:00:00" --until "2026-08-05T12:00:00"
生产环境建议将日志驱动改为syslog或fluentd,将日志发送到集中式日志系统。配合Prometheus + Grafana监控容器CPU、内存、网络指标,构建完整的可观测性体系。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/dockercompose-duo-fu-wu-bian-pai-yu-zi-dong-hua-bu-shu-pei/