Skip to content

Docker Compose

本章将详细介绍 Docker Compose,学习如何使用 YAML 文件定义和管理多容器应用,实现容器编排和服务管理。

Docker Compose 简介

什么是 Docker Compose?

Docker Compose 是一个用于定义和运行多容器 Docker 应用程序的工具。通过一个 YAML 文件来配置应用程序的服务,然后使用一个命令就可以创建并启动所有服务。

核心概念

  • 服务 (Service):应用程序的一个组件,如 web 服务器、数据库等
  • 项目 (Project):由一组关联的服务组成的完整应用
  • 网络 (Network):服务间通信的网络环境
  • 数据卷 (Volume):数据持久化和共享的存储

Docker Compose 的优势

  1. 简化部署:一个命令启动整个应用栈
  2. 环境隔离:每个项目有独立的网络和命名空间
  3. 服务发现:容器间可以通过服务名互相访问
  4. 配置管理:统一管理所有服务的配置
  5. 扩展性:轻松扩展服务实例数量

安装 Docker Compose

Linux 安装

bash
# 方法 1:使用 curl 下载
sudo curl -L "https://github.com/docker/compose/releases/download/v2.12.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

# 方法 2:使用 pip 安装
pip3 install docker-compose

# 方法 3:使用包管理器(Ubuntu)
sudo apt-get update
sudo apt-get install docker-compose-plugin

# 验证安装
docker-compose --version

Windows 和 macOS

Docker Desktop 已经包含了 Docker Compose,无需单独安装。

docker-compose.yml 文件结构

基本结构

yaml
version: '3.8'  # Compose 文件版本

services:       # 定义服务
  web:
    # 服务配置
  db:
    # 服务配置

volumes:        # 定义数据卷(可选)
  # 数据卷配置

networks:       # 定义网络(可选)
  # 网络配置

版本对应关系

Compose 版本Docker Engine 版本
3.819.03.0+
3.718.06.0+
3.618.02.0+
3.517.12.0+

服务配置详解

基本服务配置

yaml
version: '3.8'

services:
  web:
    # 使用现有镜像
    image: nginx:latest
    
    # 或者构建镜像
    build: .
    # build:
    #   context: .
    #   dockerfile: Dockerfile
    
    # 容器名称
    container_name: my-web-server
    
    # 端口映射
    ports:
      - "8080:80"
      - "8443:443"
    
    # 环境变量
    environment:
      - NODE_ENV=production
      - DEBUG=false
    
    # 数据卷挂载
    volumes:
      - ./html:/usr/share/nginx/html
      - logs:/var/log/nginx
    
    # 网络
    networks:
      - frontend
    
    # 依赖关系
    depends_on:
      - db
    
    # 重启策略
    restart: unless-stopped

构建配置

yaml
services:
  web:
    build:
      context: .                    # 构建上下文
      dockerfile: Dockerfile.prod   # 指定 Dockerfile
      args:                        # 构建参数
        - VERSION=1.0
        - BUILD_DATE=2023-01-01
      target: production           # 多阶段构建目标
      cache_from:                  # 缓存来源
        - myapp:cache
    image: myapp:latest           # 构建后的镜像名

环境变量配置

yaml
services:
  web:
    # 方法 1:直接定义
    environment:
      - NODE_ENV=production
      - PORT=3000
      - DATABASE_URL=postgresql://user:pass@db:5432/mydb
    
    # 方法 2:使用对象格式
    environment:
      NODE_ENV: production
      PORT: 3000
      DATABASE_URL: postgresql://user:pass@db:5432/mydb
    
    # 方法 3:从文件读取
    env_file:
      - .env
      - .env.local
    
    # 方法 4:从主机环境继承
    environment:
      - NODE_ENV
      - DATABASE_URL=${DATABASE_URL}

数据卷配置

yaml
services:
  web:
    volumes:
      # 绑定挂载
      - ./src:/app/src
      - ./config.json:/app/config.json:ro  # 只读
      
      # 命名数据卷
      - app-data:/app/data
      - logs:/var/log
      
      # 匿名数据卷
      - /app/node_modules
      
      # tmpfs 挂载
      - type: tmpfs
        target: /tmp
        tmpfs:
          size: 100M

# 定义命名数据卷
volumes:
  app-data:
    driver: local
  logs:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: /host/logs

网络配置

yaml
services:
  web:
    networks:
      - frontend
      - backend
  
  db:
    networks:
      - backend

# 定义网络
networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true  # 内部网络,不能访问外部

依赖关系和启动顺序

yaml
services:
  web:
    depends_on:
      - db
      - redis
    # 或者使用条件依赖(需要 healthcheck)
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
  
  db:
    image: postgres:13
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s
  
  redis:
    image: redis:alpine

实际应用示例

示例 1:Web 应用 + 数据库

yaml
version: '3.8'

services:
  # Web 应用服务
  web:
    build: .
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgresql://postgres:password@db:5432/myapp
    depends_on:
      - db
    volumes:
      - ./uploads:/app/uploads
    networks:
      - app-network
    restart: unless-stopped

  # PostgreSQL 数据库
  db:
    image: postgres:13
    environment:
      - POSTGRES_DB=myapp
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=password
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
    networks:
      - app-network
    restart: unless-stopped

  # Redis 缓存
  redis:
    image: redis:alpine
    command: redis-server --appendonly yes
    volumes:
      - redis_data:/data
    networks:
      - app-network
    restart: unless-stopped

volumes:
  postgres_data:
  redis_data:

networks:
  app-network:
    driver: bridge

示例 2:微服务架构

yaml
version: '3.8'

services:
  # Nginx 反向代理
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./ssl:/etc/nginx/ssl
    depends_on:
      - api
      - web
    networks:
      - frontend
    restart: unless-stopped

  # 前端应用
  web:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    networks:
      - frontend
    restart: unless-stopped

  # API 服务
  api:
    build:
      context: ./backend
      dockerfile: Dockerfile
    environment:
      - DATABASE_URL=postgresql://postgres:password@db:5432/api
      - REDIS_URL=redis://redis:6379
    depends_on:
      - db
      - redis
    networks:
      - frontend
      - backend
    restart: unless-stopped

  # 数据库
  db:
    image: postgres:13
    environment:
      - POSTGRES_DB=api
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=password
    volumes:
      - db_data:/var/lib/postgresql/data
    networks:
      - backend
    restart: unless-stopped

  # 缓存
  redis:
    image: redis:alpine
    volumes:
      - redis_data:/data
    networks:
      - backend
    restart: unless-stopped

volumes:
  db_data:
  redis_data:

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true

示例 3:开发环境

yaml
version: '3.8'

services:
  # 开发服务器
  dev:
    build:
      context: .
      dockerfile: Dockerfile.dev
    ports:
      - "3000:3000"
      - "9229:9229"  # Node.js 调试端口
    environment:
      - NODE_ENV=development
      - DATABASE_URL=postgresql://postgres:password@db:5432/devdb
    volumes:
      - .:/app
      - /app/node_modules  # 防止覆盖 node_modules
    depends_on:
      - db
      - redis
    networks:
      - dev-network
    command: npm run dev

  # 数据库
  db:
    image: postgres:13
    environment:
      - POSTGRES_DB=devdb
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=password
    ports:
      - "5432:5432"  # 暴露端口用于外部连接
    volumes:
      - dev_db_data:/var/lib/postgresql/data
    networks:
      - dev-network

  # Redis
  redis:
    image: redis:alpine
    ports:
      - "6379:6379"
    networks:
      - dev-network

  # 邮件服务(开发用)
  mailhog:
    image: mailhog/mailhog
    ports:
      - "1025:1025"  # SMTP
      - "8025:8025"  # Web UI
    networks:
      - dev-network

volumes:
  dev_db_data:

networks:
  dev-network:
    driver: bridge

Docker Compose 命令

基本命令

bash
# 启动服务
docker-compose up

# 后台启动
docker-compose up -d

# 启动指定服务
docker-compose up web db

# 构建镜像
docker-compose build

# 构建并启动
docker-compose up --build

# 停止服务
docker-compose stop

# 停止并删除容器
docker-compose down

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

# 查看服务状态
docker-compose ps

# 查看日志
docker-compose logs

# 实时查看日志
docker-compose logs -f

# 查看指定服务日志
docker-compose logs web

# 进入容器
docker-compose exec web bash

# 运行一次性命令
docker-compose run web npm install

# 扩展服务实例
docker-compose up --scale web=3

# 重启服务
docker-compose restart

# 暂停服务
docker-compose pause

# 恢复服务
docker-compose unpause

高级命令

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

# 显示配置(解析变量后)
docker-compose config --services

# 拉取镜像
docker-compose pull

# 推送镜像
docker-compose push

# 查看端口映射
docker-compose port web 80

# 查看服务进程
docker-compose top

# 发送信号
docker-compose kill -s SIGINT web

# 删除停止的容器
docker-compose rm

# 强制删除
docker-compose rm -f

环境管理

多环境配置

bash
# 开发环境
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up

# 生产环境
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up

# 测试环境
docker-compose -f docker-compose.yml -f docker-compose.test.yml up

配置文件示例

docker-compose.yml(基础配置):

yaml
version: '3.8'

services:
  web:
    build: .
    volumes:
      - ./app:/app
    networks:
      - app-network

  db:
    image: postgres:13
    networks:
      - app-network

networks:
  app-network:

docker-compose.dev.yml(开发环境覆盖):

yaml
version: '3.8'

services:
  web:
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=development
    command: npm run dev

  db:
    ports:
      - "5432:5432"
    environment:
      - POSTGRES_DB=devdb
      - POSTGRES_USER=dev
      - POSTGRES_PASSWORD=devpass

docker-compose.prod.yml(生产环境覆盖):

yaml
version: '3.8'

services:
  web:
    ports:
      - "80:3000"
    environment:
      - NODE_ENV=production
    restart: unless-stopped

  db:
    environment:
      - POSTGRES_DB=proddb
      - POSTGRES_USER=prod
      - POSTGRES_PASSWORD=${DB_PASSWORD}
    volumes:
      - prod_db_data:/var/lib/postgresql/data
    restart: unless-stopped

volumes:
  prod_db_data:

环境变量文件

.env 文件:

bash
# 数据库配置
DB_PASSWORD=secretpassword
DB_NAME=myapp
DB_USER=postgres

# 应用配置
APP_PORT=3000
APP_ENV=production

# 外部服务
REDIS_URL=redis://redis:6379
API_KEY=your-api-key

在 docker-compose.yml 中使用:

yaml
version: '3.8'

services:
  web:
    ports:
      - "${APP_PORT}:3000"
    environment:
      - NODE_ENV=${APP_ENV}
      - API_KEY=${API_KEY}

  db:
    environment:
      - POSTGRES_DB=${DB_NAME}
      - POSTGRES_USER=${DB_USER}
      - POSTGRES_PASSWORD=${DB_PASSWORD}

网络和服务发现

默认网络

Docker Compose 自动创建一个默认网络,所有服务都连接到这个网络:

yaml
version: '3.8'

services:
  web:
    image: nginx
    # 可以通过 'db' 访问数据库服务
  
  db:
    image: postgres
    # 可以通过 'web' 访问 Web 服务

自定义网络

yaml
version: '3.8'

services:
  web:
    image: nginx
    networks:
      - frontend
      - backend

  api:
    image: myapi
    networks:
      - backend

  db:
    image: postgres
    networks:
      - backend

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true  # 内部网络

外部网络

yaml
version: '3.8'

services:
  web:
    image: nginx
    networks:
      - existing-network

networks:
  existing-network:
    external: true

数据持久化

命名数据卷

yaml
version: '3.8'

services:
  db:
    image: postgres
    volumes:
      - db_data:/var/lib/postgresql/data

volumes:
  db_data:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: /host/path/to/data

外部数据卷

yaml
version: '3.8'

services:
  db:
    image: postgres
    volumes:
      - existing_volume:/var/lib/postgresql/data

volumes:
  existing_volume:
    external: true

健康检查和依赖管理

健康检查

yaml
version: '3.8'

services:
  web:
    image: nginx
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s

  db:
    image: postgres
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 30s
      timeout: 5s
      retries: 5

等待依赖服务

使用 wait-for-it 脚本:

yaml
version: '3.8'

services:
  web:
    build: .
    depends_on:
      - db
    command: ["./wait-for-it.sh", "db:5432", "--", "npm", "start"]

  db:
    image: postgres

扩展和负载均衡

服务扩展

bash
# 扩展 web 服务到 3 个实例
docker-compose up --scale web=3

# 扩展多个服务
docker-compose up --scale web=3 --scale worker=2

负载均衡配置

yaml
version: '3.8'

services:
  nginx:
    image: nginx
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
    depends_on:
      - web

  web:
    build: .
    # 不暴露端口,只通过 nginx 访问

# nginx.conf 配置负载均衡
upstream backend {
    server web_web_1:3000;
    server web_web_2:3000;
    server web_web_3:3000;
}

server {
    listen 80;
    location / {
        proxy_pass http://backend;
    }
}

监控和日志

日志配置

yaml
version: '3.8'

services:
  web:
    image: nginx
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

  app:
    build: .
    logging:
      driver: "syslog"
      options:
        syslog-address: "tcp://192.168.1.42:123"

监控服务

yaml
version: '3.8'

services:
  # 应用服务
  web:
    build: .
    ports:
      - "3000:3000"

  # Prometheus 监控
  prometheus:
    image: prom/prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml

  # Grafana 可视化
  grafana:
    image: grafana/grafana
    ports:
      - "3001:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    volumes:
      - grafana_data:/var/lib/grafana

volumes:
  grafana_data:

故障排查

常见问题

  1. 服务无法启动
bash
# 查看详细日志
docker-compose logs service_name

# 检查配置
docker-compose config

# 查看服务状态
docker-compose ps
  1. 网络连接问题
bash
# 检查网络
docker network ls
docker network inspect project_default

# 测试连接
docker-compose exec web ping db
  1. 数据卷问题
bash
# 查看数据卷
docker volume ls
docker volume inspect project_volume_name

# 检查挂载
docker-compose exec service_name df -h
  1. 端口冲突
bash
# 查看端口使用
netstat -tulpn | grep :8080

# 修改端口映射
ports:
  - "8081:80"  # 使用不同的主机端口

本章小结

本章全面介绍了 Docker Compose 的使用:

关键要点:

  • 服务编排:使用 YAML 文件定义多容器应用
  • 网络管理:服务发现和网络隔离
  • 数据管理:数据卷和持久化存储
  • 环境管理:多环境配置和变量管理
  • 扩展性:服务扩展和负载均衡
  • 监控调试:日志管理和故障排查

最佳实践:

  • 使用版本控制管理 Compose 文件
  • 合理设计服务间的依赖关系
  • 使用健康检查确保服务可用性
  • 分离配置和代码
  • 定期备份重要数据

在下一章中,我们将学习 Docker 的安全最佳实践,包括镜像安全、容器安全和网络安全。

延伸阅读

本站内容仅供学习和研究使用。