Docker Compose 从入门到实战:多容器编排完全指南


一、什么是 Docker Compose

Docker Compose 是 Docker 官方提供的多容器编排工具,用于定义和运行多个 Docker 容器的应用。通过一个 docker-compose.yml 文件,你可以配置应用程序所需的所有服务(容器),然后使用一条命令即可启动所有服务。

Docker Compose 的核心价值:将多个容器的配置、依赖关系、网络、数据卷等集中声明在一个 YAML 文件中,实现多容器应用的一键部署和环境一致性。

1.1 适用场景

场景说明
开发环境快速搭建完整的应用依赖(数据库、缓存、消息队列等)
本地测试模拟生产环境进行集成测试
CI/CD 流水线自动化构建和测试环境
单机生产部署小型项目或微服务部署(生产环境一般用 K8s)

二、安装 Docker Compose

安装看这篇:二进制安装 Docker 和 Docker-compose


三、docker-compose.yml 核心语法详解

3.1 基础结构

version: '3.8'    # Compose 文件版本

services:         # 服务定义(每个服务对应一个容器)
  web:            # 服务名称
    image: nginx  # 使用的镜像
    ports:        # 端口映射
      - "8080:80"
    volumes:      # 数据卷挂载
      - ./html:/usr/share/nginx/html

networks:         # 网络定义(可选)
  default:
    driver: bridge

volumes:          # 卷定义(可选)
  data:

四、核心配置项详解

4.1 image——指定镜像

services:
  web:
    image: nginx:alpine              # 从仓库拉取

  app:
    build: ./app                     # 从 Dockerfile 构建
    # 或指定构建参数
    build:
      context: ./app
      dockerfile: Dockerfile
      args:
        VERSION: 1.0

  db:
    image: mysql:5.7

4.2 ports——端口映射

services:
  web:
    ports:
      - "8080:80"                    # 宿主机:容器
      - "127.0.0.1:8081:81"          # 绑定到指定 IP
      - "8082:80"                    # 多个端口映射

  # 暴露内部端口但不映射到宿主机(仅服务间通信)
  app:
    expose:
      - "8080"
      - "9090"

4.3 volumes——数据卷挂载

services:
  web:
    volumes:
      # 绑定挂载(宿主机目录:容器目录)
      - ./app:/app
      # 命名卷
      - data:/var/lib/mysql
      # 只读挂载
      - ./config:/etc/app/config:ro
      # 相对路径挂载
      - ./logs:/var/log/app

  db:
    volumes:
      - db_data:/var/lib/mysql

# 定义命名卷
volumes:
  data:
  db_data:

4.4 environment 与 env_file——环境变量

services:
  web:
    # 直接定义
    environment:
      - NODE_ENV=production
      - DB_HOST=mysql
      - APP_PORT=8080

  db:
    # 从文件加载(.env 文件)
    env_file:
      - .env
      - ./config/db.env

    environment:
      - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}  # 引用外部变量

.env 文件示例:

MYSQL_ROOT_PASSWORD=123456
MYSQL_DATABASE=myapp
DB_USER=admin

4.5 depends_on——依赖与启动顺序

services:
  web:
    image: nginx
    depends_on:
      - app
      - db

  app:
    build: ./app
    depends_on:
      - db

  db:
    image: mysql:5.7

注意depends_on 仅控制容器启动顺序,不等待服务完全就绪。如需健康检查后才启动,需配合 condition

services:
  web:
    depends_on:
      db:
        condition: service_healthy   # 等待 db 健康后才启动

  db:
    image: mysql:5.7
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5

4.6 restart——重启策略

services:
  web:
    restart: always        # 总是重启
    # restart: unless-stopped   # 除非手动停止
    # restart: on-failure:3     # 失败时重启,最多 3 次
    # restart: no               # 不自动重启(默认)

4.7 networks——网络配置

services:
  web:
    networks:
      - frontend
      - backend

  app:
    networks:
      - backend

  db:
    networks:
      - backend

# 定义网络
networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true          # 内部网络(不对外暴露)

4.8 command 与 entrypoint——启动命令

services:
  web:
    image: python:3.11
    command: python app.py
    # 或数组格式
    command: ["python", "app.py"]

  app:
    build: .
    entrypoint: ["java", "-jar"]
    command: ["app.jar"]
    # 组合使用:实际执行 java -jar app.jar

4.9 container_name——指定容器名

services:
  web:
    container_name: my-web-app    # 固定容器名
    image: nginx

注意:生产环境尽量不要固定容器名,避免冲突。

4.10 logging——日志配置

services:
  web:
    logging:
      driver: "json-file"          # 日志驱动
      options:
        max-size: "10m"            # 单个日志文件最大 10M
        max-file: "3"              # 最多保留 3 个文件

五、配置文件完整参考(含全部常用字段)

version: '3.8'

services:
  # ========== Web 服务 ==========
  web:
    image: nginx:alpine
    container_name: nginx-web
    restart: always
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./html:/usr/share/nginx/html:ro
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      app:
        condition: service_healthy
    networks:
      - frontend
      - backend
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

  # ========== 应用服务 ==========
  app:
    build:
      context: ./app
      dockerfile: Dockerfile
      args:
        BUILD_ENV: production
    container_name: spring-app
    restart: always
    environment:
      - SPRING_PROFILES_ACTIVE=prod
      - DB_HOST=db
      - DB_PORT=3306
      - REDIS_HOST=redis
    env_file:
      - ./config/app.env
    volumes:
      - ./logs:/app/logs
      - app_data:/app/data
    ports:
      - "8080:8080"
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
      interval: 30s
      timeout: 3s
      retries: 3
      start_period: 30s
    networks:
      - backend
      - internal
    logging:
      driver: "json-file"
      options:
        max-size: "50m"
        max-file: "5"

  # ========== 数据库服务 ==========
  db:
    image: mysql:5.7
    container_name: mysql-db
    restart: always
    environment:
      - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
      - MYSQL_DATABASE=myapp
      - MYSQL_USER=appuser
      - MYSQL_PASSWORD=${MYSQL_USER_PASSWORD}
    volumes:
      - db_data:/var/lib/mysql
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
    ports:
      - "3306:3306"
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-uroot", "-p${MYSQL_ROOT_PASSWORD}"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - backend
      - internal

  # ========== Redis 缓存 ==========
  redis:
    image: redis:7.0-alpine
    container_name: redis-cache
    restart: always
    command: redis-server --requirepass ${REDIS_PASSWORD}
    volumes:
      - redis_data:/data
    ports:
      - "6379:6379"
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 3s
      retries: 5
    networks:
      - backend

# ========== 网络定义 ==========
networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: false
  internal:
    driver: bridge
    internal: true          # 内部网络,无法访问外部

# ========== 卷定义 ==========
volumes:
  db_data:
    driver: local
  redis_data:
  app_data:

六、常用命令详解

6.1 基础命令

命令说明示例
up启动所有服务docker-compose up -d
down停止并删除所有容器、网络docker-compose down
ps查看服务状态docker-compose ps
logs查看日志docker-compose logs -f web
stop停止服务(不删除容器)docker-compose stop
start启动已停止的服务docker-compose start
restart重启服务docker-compose restart web
exec进入容器执行命令docker-compose exec web bash
build构建或重新构建服务镜像docker-compose build --no-cache
pull拉取服务镜像docker-compose pull
push推送服务镜像到仓库docker-compose push
top查看容器内进程docker-compose top
config验证配置文件格式docker-compose config

6.2 常用组合示例

# 后台启动所有服务
docker-compose up -d

# 只启动特定服务
docker-compose up -d web db

# 查看所有容器状态
docker-compose ps -a

# 实时查看所有服务日志
docker-compose logs -f

# 查看特定服务日志(最近 100 行)
docker-compose logs --tail=100 web

# 进入 Web 容器终端
docker-compose exec web /bin/sh

# 执行数据库备份
docker-compose exec db mysqldump -uroot -p123456 myapp > backup.sql

# 停止并删除所有容器、网络、卷
docker-compose down -v

# 重新构建并启动
docker-compose up -d --build

# 验证配置文件
docker-compose config --quiet

七、实战案例

7.1 案例一:LAMP 环境(WordPress + MySQL)

version: '3.8'

services:
  # WordPress 服务
  wordpress:
    image: wordpress:latest
    container_name: wordpress
    restart: always
    ports:
      - "8080:80"
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: wordpress_pass
      WORDPRESS_DB_NAME: wordpress
    volumes:
      - wordpress_data:/var/www/html
      - ./wp-content:/var/www/html/wp-content
    depends_on:
      db:
        condition: service_healthy
    networks:
      - webnet

  # MySQL 数据库
  db:
    image: mysql:5.7
    container_name: mysql-wp
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: root_pass
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: wordpress_pass
    volumes:
      - db_data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - webnet

  # phpMyAdmin(可选)
  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    container_name: phpmyadmin
    restart: always
    ports:
      - "8081:80"
    environment:
      PMA_HOST: db
      PMA_PORT: 3306
      UPLOAD_LIMIT: 100M
    depends_on:
      - db
    networks:
      - webnet

volumes:
  wordpress_data:
  db_data:

networks:
  webnet:

启动访问:

  • WordPress: http://localhost:8080
  • phpMyAdmin: http://localhost:8081

7.2 案例二:微服务架构(Spring Boot + MySQL + Redis + Nginx)

version: '3.8'

services:
  # Nginx 网关
  nginx:
    image: nginx:alpine
    container_name: api-gateway
    restart: always
    ports:
      - "80:80"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/conf.d:/etc/nginx/conf.d:ro
    depends_on:
      - user-service
      - order-service
      - product-service
    networks:
      - frontend
      - backend

  # 用户服务
  user-service:
    build: ./user-service
    container_name: user-service
    restart: always
    environment:
      - SPRING_PROFILES_ACTIVE=docker
      - DB_HOST=mysql
      - REDIS_HOST=redis
    ports:
      - "8081:8080"
    depends_on:
      mysql:
        condition: service_healthy
      redis:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
      interval: 30s
      timeout: 3s
      retries: 3
      start_period: 30s
    networks:
      - backend

  # 订单服务
  order-service:
    build: ./order-service
    container_name: order-service
    restart: always
    environment:
      - SPRING_PROFILES_ACTIVE=docker
      - DB_HOST=mysql
      - REDIS_HOST=redis
    ports:
      - "8082:8080"
    depends_on:
      mysql:
        condition: service_healthy
      redis:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
      interval: 30s
      timeout: 3s
      retries: 3
      start_period: 30s
    networks:
      - backend

  # 商品服务
  product-service:
    build: ./product-service
    container_name: product-service
    restart: always
    environment:
      - SPRING_PROFILES_ACTIVE=docker
      - DB_HOST=mysql
      - REDIS_HOST=redis
    ports:
      - "8083:8080"
    depends_on:
      mysql:
        condition: service_healthy
      redis:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
      interval: 30s
      timeout: 3s
      retries: 3
      start_period: 30s
    networks:
      - backend

  # MySQL
  mysql:
    image: mysql:5.7
    container_name: mysql-micro
    restart: always
    environment:
      - MYSQL_ROOT_PASSWORD=root123
    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", "-proot123"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - backend

  # Redis
  redis:
    image: redis:7.0-alpine
    container_name: redis-micro
    restart: always
    volumes:
      - redis_data:/data
    ports:
      - "6379:6379"
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 3s
      retries: 5
    networks:
      - backend

volumes:
  mysql_data:
  redis_data:

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge

八、常用环境变量文件 .env

# .env 文件(与 docker-compose.yml 同目录)

# 数据库配置
MYSQL_ROOT_PASSWORD=root123456
MYSQL_DATABASE=myapp
MYSQL_USER=appuser
MYSQL_PASSWORD=apppass123

# 应用配置
APP_ENV=production
APP_PORT=8080
JWT_SECRET=your-secret-key-here

# Redis 配置
REDIS_PASSWORD=redispass

# 对外域名
DOMAIN=example.com

docker-compose.yml 中引用:

services:
  db:
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}

九、最佳实践总结

9.1 配置文件规范

序号最佳实践说明
1使用固定版本号,避免 latest保证环境一致性
2使用 .env 管理敏感信息不将密码硬编码在 YAML 中
3添加 healthcheck 健康检查确保服务就绪后再启动依赖
4合理规划网络隔离前后端分离网络,提高安全性
5使用 restart: always保证容器意外退出后自动恢复
6限制日志文件大小避免磁盘爆满
7使用命名卷持久化数据容器重启后数据不丢失
8配置文件添加注释便于团队协作和维护
9使用 depends_on + condition确保依赖服务健康后才启动

9.2 常用 .gitignore 内容

# 环境变量(含敏感信息)
.env
*.env

# 日志目录
logs/
*.log

# 数据卷目录(避免提交大文件)
data/
volumes/

# IDE 配置
.idea/
.vscode/
*.iml

9.3 生产环境注意事项

注意事项说明
禁用 latest 标签指定具体版本号,保证可追溯性
使用国内镜像加速docker-compose.yml 中配置 registry-mirrors
设置资源限制使用 deploy.resources.limits 限制 CPU 和内存
配置日志轮转避免日志占满磁盘
使用外部卷便于数据备份和迁移
定期备份数据数据库卷需要定期备份

资源限制示例:

services:
  web:
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
        reservations:
          cpus: '0.25'
          memory: 256M

十、常用命令速查表

分类命令说明
启动docker-compose up -d后台启动所有服务
停止docker-compose down停止并删除容器、网络
docker-compose down -v同时删除数据卷
查看docker-compose ps查看服务状态
docker-compose logs -f实时查看所有日志
操作docker-compose exec web bash进入容器
docker-compose restart web重启特定服务
构建docker-compose build构建或重建镜像
docker-compose build --no-cache不使用缓存构建
调试docker-compose config验证配置文件
docker-compose top查看容器内进程

如果这篇文章对你有帮助,欢迎收藏、分享,也欢迎在评论区交流讨论!

更多推荐