Docker Compose 进阶
大约 10 分钟约 2968 字
Docker Compose 进阶
简介
Docker Compose 是定义和运行多容器 Docker 应用的工具。进阶使用包括多环境配置、网络管理、健康检查、依赖控制、资源限制和 CI/CD 集成。掌握这些高级特性是编排复杂应用的基础。
特点
多环境配置
docker-compose.yml 基础
# docker-compose.yml — 基础配置
version: '3.8'
services:
api:
build:
context: .
dockerfile: Dockerfile
ports:
- "5000:8080"
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ConnectionStrings__Default=Server=db;Database=MyApp;User=sa;Password=${DB_PASSWORD}
- Redis__Host=redis
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
deploy:
resources:
limits:
cpus: '2'
memory: 1G
reservations:
cpus: '0.5'
memory: 256M
networks:
- backend
db:
image: mcr.microsoft.com/mssql/server:2022-latest
environment:
- ACCEPT_EULA=Y
- SA_PASSWORD=${DB_PASSWORD}
volumes:
- sqldata:/var/opt/mssql
healthcheck:
test: ["CMD-SHELL", "/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P ${DB_PASSWORD} -Q 'SELECT 1'"]
interval: 15s
timeout: 5s
retries: 5
networks:
- backend
redis:
image: redis:7-alpine
command: redis-server --requirepass ${REDIS_PASSWORD}
volumes:
- redisdata:/data
networks:
- backend
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
depends_on:
api:
condition: service_healthy
networks:
- backend
- frontend
volumes:
sqldata:
redisdata:
networks:
backend:
driver: bridge
frontend:
driver: bridge覆盖配置
# docker-compose.override.yml — 开发环境(自动加载)
version: '3.8'
services:
api:
build:
target: development
volumes:
- .:/src # 挂载源码,支持热重载
- ~/.nuget:/root/.nuget
environment:
- ASPNETCORE_ENVIRONMENT=Development
ports:
- "5000:8080"
- "5001:8081" # HTTPS
db:
ports:
- "1433:1433" # 开发环境暴露端口# docker-compose.prod.yml — 生产环境
version: '3.8'
services:
api:
build:
target: production
deploy:
replicas: 3
resources:
limits:
cpus: '2'
memory: 1G
environment:
- ASPNETCORE_ENVIRONMENT=Production
restart: always
db:
deploy:
resources:
limits:
cpus: '4'
memory: 4G
restart: always
# 启动生产环境:
# docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d常用命令
服务管理
# 启动
docker compose up -d # 后台启动
docker compose up -d --build # 重新构建并启动
docker compose up -d api db redis # 只启动指定服务
# 多环境启动
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
# 扩展
docker compose up -d --scale api=3 # 启动3个api实例
# 停止和清理
docker compose down # 停止并删除容器
docker compose down -v # 同时删除数据卷
docker compose down --rmi all # 同时删除镜像
# 查看状态
docker compose ps # 容器状态
docker compose logs -f api # 实时日志
docker compose logs --tail=100 api # 最近100行
docker compose top # 容器进程
# 服务操作
docker compose restart api # 重启服务
docker compose stop api # 停止服务
docker compose start api # 启动服务
docker compose exec api bash # 进入容器
docker compose run --rm api bash # 一次性运行.NET 项目 Dockerfile
多阶段构建
# Dockerfile
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
# 先复制 csproj,利用缓存
COPY ["MyApp.csproj", "."]
RUN dotnet restore
# 复制源码并构建
COPY . .
RUN dotnet publish -c Release -o /app/publish
# 运行阶段
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
WORKDIR /app
# 安装必要工具
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
COPY --from=build /app/publish .
# 非 root 用户
RUN adduser --disabled-password --gecos "" appuser
USER appuser
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
ENTRYPOINT ["dotnet", "MyApp.dll"]完整 .NET + SQL Server 示例
# 完整 .NET 微服务开发环境
version: '3.8'
services:
# API 网关
gateway:
build: ./src/Gateway
ports:
- "8080:8080"
depends_on:
- order-service
- product-service
networks:
- microservices
# 订单服务
order-service:
build:
context: ./src/OrderService
dockerfile: Dockerfile
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ConnectionStrings__Default=Server=order-db;Database=Orders;User=sa;Password=Pass@word
- RabbitMQ__Host=rabbitmq
- Redis__Host=redis
depends_on:
order-db:
condition: service_healthy
rabbitmq:
condition: service_healthy
networks:
- microservices
order-db:
image: mcr.microsoft.com/mssql/server:2022-latest
environment:
- ACCEPT_EULA=Y
- SA_PASSWORD=Pass@word123
volumes:
- order-db-data:/var/opt/mssql
healthcheck:
test: ["CMD-SHELL", "/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P Pass@word123 -Q 'SELECT 1'"]
interval: 10s
retries: 10
networks:
- microservices
# 产品服务
product-service:
build:
context: ./src/ProductService
dockerfile: Dockerfile
environment:
- ASPNETCORE_ENVIRONMENT=Development
- MongoDB__Host=mongo
depends_on:
mongo:
condition: service_healthy
networks:
- microservices
mongo:
image: mongo:7
volumes:
- mongo-data:/data/db
healthcheck:
test: echo 'db.runCommand("ping").ok' | mongosh --quiet
interval: 10s
retries: 5
networks:
- microservices
# 消息队列
rabbitmq:
image: rabbitmq:3-management-alpine
ports:
- "15672:15672" # 管理界面
environment:
- RABBITMQ_DEFAULT_USER=admin
- RABBITMQ_DEFAULT_PASS=admin123
volumes:
- rabbitmq-data:/var/lib/rabbitmq
healthcheck:
test: rabbitmq-diagnostics -q ping
interval: 10s
retries: 5
networks:
- microservices
# 缓存
redis:
image: redis:7-alpine
command: redis-server --requirepass redis123
volumes:
- redis-data:/data
networks:
- microservices
# 监控
seq:
image: datalust/seq:latest
ports:
- "5341:80"
environment:
- ACCEPT_EULA=Y
networks:
- microservices
volumes:
order-db-data:
mongo-data:
rabbitmq-data:
redis-data:
networks:
microservices:
driver: bridge网络进阶配置
多网络隔离
# 网络隔离示例:前端、后端、数据层分离
version: '3.8'
services:
# 前端应用
web:
build: ./src/Web
ports:
- "80:80"
networks:
- frontend
depends_on:
- api
# API 服务
api:
build: ./src/Api
networks:
- frontend
- backend
depends_on:
- redis
# 缓存
redis:
image: redis:7-alpine
networks:
- backend
- data
# 数据库
postgres:
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- pgdata:/var/lib/postgresql/data
networks:
- data
networks:
frontend:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/16
backend:
driver: bridge
ipam:
config:
- subnet: 172.21.0.0/16
data:
driver: bridge
internal: true # 不暴露到宿主机网络隔离原则:
- 前端网络:暴露到宿主机,只接入 Web 和 API
- 后端网络:不对外暴露,API 和中间件通信
- 数据网络:internal=true,数据库只能被后端服务访问
- 最小权限原则:服务只加入必要的网络服务间 DNS 与通信
# Docker Compose 内置 DNS 解析
services:
api:
# 服务名即主机名,api 可通过 "db:1433" 访问数据库
environment:
- ConnectionStrings__Default=Server=db;Database=MyApp;User=sa;Password=${DB_PASSWORD}
# 可通过链接别名访问
links:
- db:database
db:
image: mcr.microsoft.com/mssql/server:2022-latest
# 设置固定主机名
hostname: mssql-primary
networks:
backend:
aliases:
- database # 网络内的别名
- mssql # 另一个别名日志与监控集成
日志配置
# 日志驱动和大小限制
services:
api:
build: .
logging:
driver: "json-file"
options:
max-size: "10m" # 单个日志文件最大 10MB
max-file: "3" # 最多保留 3 个日志文件
tag: "{{.Name}}/{{.ID}}" # 日志标签格式
environment:
- Serilog__WriteTo__0__Name=Console
- Serilog__WriteTo__1__Name=Seq
- Serilog__WriteTo__1__Args__ServerUrl=http://seq:5341
seq:
image: datalust/seq:latest
ports:
- "5341:80"
environment:
- ACCEPT_EULA=Y
volumes:
- seq-data:/dataPrometheus + Grafana 监控栈
# 监控栈
services:
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus-data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.retention.time=15d'
networks:
- monitoring
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD}
- GF_USERS_ALLOW_SIGN_UP=false
volumes:
- grafana-data:/var/lib/grafana
- ./monitoring/grafana/dashboards:/etc/grafana/provisioning/dashboards:ro
depends_on:
- prometheus
networks:
- monitoring
node-exporter:
image: prom/node-exporter:latest
ports:
- "9100:9100"
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
- '--path.rootfs=/rootfs'
networks:
- monitoring
volumes:
prometheus-data:
grafana-data:CI/CD 集成
GitHub Actions 示例
# .github/workflows/deploy.yml
name: Deploy with Docker Compose
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Login to Registry
uses: docker/login-action@v3
with:
registry: ${{ secrets.REGISTRY }}
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_PASS }}
- name: Build and Push
run: |
docker compose build
docker compose push
- name: Deploy to Server
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.DEPLOY_HOST }}
username: deploy
key: ${{ secrets.DEPLOY_KEY }}
script: |
cd /opt/app
docker compose -f docker-compose.yml -f docker-compose.prod.yml pull
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --remove-orphans
docker compose logs --tail=50健康检查与就绪探针
services:
api:
build: .
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health/ready"]
interval: 10s
timeout: 5s
retries: 3
start_period: 30s
# 使用 wget 替代 curl(Alpine 镜像无 curl)
worker:
build: .
healthcheck:
test: ["CMD-SHELL", "wget -q --spider http://localhost:8080/health || exit 1"]
interval: 15s
timeout: 5s
retries: 5
start_period: 20s健康检查最佳实践:
- start_period 设置为应用启动所需时间 + 缓冲
- interval 不宜过短(避免健康检查请求压垮服务)
- 推荐区分 liveness(是否存活)和 readiness(是否就绪)
- 使用 /health/live 检查存活,/health/ready 检查就绪(含依赖检测)
- 数据库连接失败时应标记为 not ready 而非 not live环境变量与安全
.env 文件管理
# .env — 基础环境变量(不入库)
DB_PASSWORD=StrongPass123!
REDIS_PASSWORD=RedisPass456!
GRAFANA_PASSWORD=AdminPass789!
REGISTRY=myregistry.azurecr.io
IMAGE_TAG=v1.2.3
# .env.example — 示例文件(入库)
DB_PASSWORD=change-me
REDIS_PASSWORD=change-me
GRAFANA_PASSWORD=change-me# 使用 env_file 加载多个环境文件
services:
api:
env_file:
- .env # 基础变量
- .env.local # 本地覆盖(不入库)
environment:
# environment 优先级高于 env_file
- ASPNETCORE_ENVIRONMENT=ProductionDocker Secrets(敏感数据管理)
# 使用 Docker Secrets 管理敏感数据(Swarm 模式)
version: '3.8'
services:
api:
image: myapp:latest
secrets:
- db_password
- api_key
environment:
- DB_PASSWORD_FILE=/run/secrets/db_password
- API_KEY_FILE=/run/secrets/api_key
secrets:
db_password:
file: ./secrets/db_password.txt
api_key:
file: ./secrets/api_key.txt
# 非 Swarm 环境可使用 bind mount 模拟:
# volumes:
# - ./secrets/db_password.txt:/run/secrets/db_password:ro镜像优化策略
# 优化前:可能 800MB+
FROM mcr.microsoft.com/dotnet/sdk:8.0
WORKDIR /app
COPY . .
RUN dotnet publish -c Release -o /out
ENTRYPOINT ["dotnet", "/out/MyApp.dll"]
# 优化后:约 100-200MB
# 1. 多阶段构建
# 2. 使用 aspnet runtime(不含 SDK)
# 3. 使用 alpine 基础镜像
# 4. 合并 RUN 层减少镜像大小
FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build
WORKDIR /src
COPY ["MyApp.csproj", "."]
RUN dotnet restore
COPY . .
RUN dotnet publish -c Release -o /app/publish \
--no-restore \
/p:PublishTrimmed=true \
/p:PublishSingleFile=false
FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine AS runtime
WORKDIR /app
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
COPY --from=build /app/publish .
USER appuser
EXPOSE 8080
HEALTHCHECK CMD wget -q --spider http://localhost:8080/health || exit 1
ENTRYPOINT ["dotnet", "MyApp.dll"]镜像优化要点:
- 多阶段构建:构建阶段用 SDK 镜像,运行阶段用 Runtime 镜像
- Alpine 变体:比 Debian 镜像小 100MB+
- PublishTrimmed:裁剪未使用的程序集(注意反射场景)
- 合并 RUN 指令:减少镜像层数
- .dockerignore:排除 bin/obj/tests/docs 等不需要的文件
- 固定基础镜像版本号,不用 latest
- 使用 docker scan 或 Trivy 做安全扫描# .dockerignore 示例
bin/
obj/
*.md
.git/
.gitignore
docker-compose*.yml
Dockerfile*
.vs/
.vscode/
tests/
*.sln优点
缺点
总结
Docker Compose 是开发环境的标准编排工具。掌握多环境配置、健康检查、依赖控制和资源限制,就能构建完整的开发/测试环境。核心原则:基础配置 + 覆盖文件分离环境,健康检查保证依赖就绪,volumes 持久化数据。
关键知识点
- DevOps 主题的核心是让交付更快、更稳、更可审计。
- 自动化不是把命令脚本化,而是把失败、回滚、权限和观测一起设计进去。
- 生产链路必须明确制品、环境、凭据、配置和责任边界。
- 部署主题通常要同时看镜像、容器、卷、网络和宿主机资源。
项目落地视角
- 把流水线拆成构建、测试、制品、部署、验证和回滚几个阶段。
- 为关键步骤补齐日志、指标、通知和人工兜底点。
- 定期演练扩容、回滚、故障注入和灾备切换。
- 固定镜像标签,记录端口、挂载目录、环境变量和自启动策略。
常见误区
- 只关注部署成功,不关注失败恢复和审计追踪。
- 把环境差异藏在临时脚本或人工操作里。
- 上线频率高了以后,没有标准化制品和配置管理。
- 使用 latest 导致结果不可复现。
进阶路线
- 继续补齐 GitOps、可观测性、平台工程和成本治理。
- 把主题和应用架构、安全、权限、备份恢复联动起来理解。
- 形成团队级平台能力,而不是每个项目重复造轮子。
- 继续补齐 Compose 编排、镜像瘦身、安全扫描和镜像仓库治理。
适用场景
- 当你准备把《Docker Compose 进阶》真正落到项目里时,最适合先在一个独立模块或最小样例里验证关键路径。
- 适合构建自动化交付、基础设施治理、监控告警和生产发布体系。
- 当团队规模扩大、发布频率提升或环境变多时,这类主题会显著影响交付效率。
落地建议
- 所有自动化流程尽量做到幂等、可审计、可回滚。
- 把制品、变量、凭据和执行权限分层管理。
- 定期演练扩容、回滚、密钥轮换和灾备恢复。
排错清单
- 先定位失败发生在代码、构建、制品、环境还是权限层。
- 检查流水线变量、凭据、镜像标签和目标环境配置是否一致。
- 如果问题偶发,重点看并发发布、资源争抢和外部依赖抖动。
复盘问题
- 如果把《Docker Compose 进阶》放进你的当前项目,最先要验证的输入、输出和失败路径分别是什么?
- 《Docker Compose 进阶》最容易在什么规模、什么边界条件下暴露问题?你会用什么指标或日志去确认?
- 相比默认实现或替代方案,采用《Docker Compose 进阶》最大的收益和代价分别是什么?
