Docker-compose完全指南

引言

Docker Compose是Docker生态系统中的一个重要工具,它极大地简化了多容器应用的定义和运行。通过使用简单的YAML配置文件,开发者可以轻松地定义、启动和管理由多个相互关联的容器组成的应用程序。本文将全面介绍Docker Compose的基本概念、配置选项、使用方法以及最佳实践,帮助您更高效地管理容器化应用。

目录

什么是Docker Compose

Docker Compose是一个用于定义和运行多容器Docker应用程序的工具。它使用YAML文件来配置应用程序的服务、网络和卷,使开发人员能够在一个地方定义和管理多个容器。通过简单的命令,您可以创建和启动配置中定义的所有服务,极大地简化了容器化应用的开发和部署流程。

安装Docker Compose

安装Docker Compose

在大多数Linux系统上,可以通过以下命令安装Docker Compose:

sudo apt install docker-compose

对于其他操作系统或更新版本的安装,可以参考Docker官方文档

检查安装

安装完成后,可以通过以下命令检查Docker Compose的版本:

docker-compose --version

基本概念

版本与名称

版本(Version)

在docker-compose.yml文件中,可以指定使用的Compose文件格式版本。虽然在最新版本中已不再强制要求,但为了兼容性考虑,仍可以指定:

version: '3'

名称(Name)

可以为项目指定一个名称,这在管理多个项目时非常有用:

name: myapp

服务(Service)

服务是Docker Compose中定义的一个容器,它可以是一个应用程序、数据库、缓存等。每个服务都有自己的配置,包括镜像、端口、环境变量、依赖关系等。

镜像(Image)

指定服务使用的Docker镜像:

image: nginx:latest

端口(Ports)

将容器端口映射到主机端口:

ports:
  - "80:80"

环境变量(Environment Variables)

设置容器的环境变量:

environment:
  - "DB_HOST=db"
  - "DB_PORT=3306"

也可以使用对象语法:

environment:
  DB_HOST: db
  DB_PORT: 3306

或者使用环境变量文件:

env_file:
  - ./common.env
  - ./app.env

依赖关系(Depends On)

指定服务之间的依赖关系,确保服务按正确的顺序启动:

depends_on:
  - db

命令(Command)

覆盖容器的默认命令:

command: "python app.py"

构建(Build)

从Dockerfile构建镜像:

build: ./dir

指定Dockerfile和构建参数:

build:
  context: ./dir
  dockerfile: Dockerfile-alternate
  args:
    buildno: 1
    gitcommithash: cdc3b19

重启策略(Restart)

配置容器的重启策略:

restart: "no" # 不自动重启
restart: always # 总是重启
restart: on-failure # 仅在失败时重启
restart: unless-stopped # 除非手动停止,否则总是重启

健康检查(Healthcheck)

配置容器的健康检查:

healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost"]
  interval: 1m30s
  timeout: 10s
  retries: 3
  start_period: 40s

禁用健康检查:

healthcheck:
  disable: true

日志配置(Logging)

配置容器的日志驱动:

logging:
  driver: json-file
  options:
    max-size: "200k"
    max-file: "10"

用户(User)

指定运行容器的用户:

user: postgres

或者使用UID:GID格式:

user: "1000:1000"

网络(Network)

网络是用于连接服务之间的通信通道。Docker Compose会自动创建一个默认网络,但也可以定义自定义网络。

自定义网络

networks:
  mynetwork:
    driver: bridge

服务连接网络

services:
  web:
    image: nginx:latest
    networks:
      - mynetwork
  db:
    image: mysql:latest
    networks:
      - mynetwork

networks:
  mynetwork:
    driver: bridge

服务指定网络配置

services:
  web:
    image: nginx:latest
    networks:
      mynetwork:
        ipv4_address: 172.16.238.10
    ports:
      - "80:80"
  db:
    image: mysql:latest
    networks:
      mynetwork:
        ipv4_address: 172.16.238.11
    environment:
      - "MYSQL_ROOT_PASSWORD=secret"

networks:
  mynetwork:
    driver: bridge
    ipam:
      config:
        - subnet: 172.16.238.0/24

卷(Volume)

卷是用于持久化存储数据的容器。Docker Compose会自动创建一个默认卷,但也可以定义自定义卷。

自定义卷

volumes:
  myvolume:
    driver: local

服务指定卷

services:
  web:
    image: nginx:latest
    volumes:
      - myvolume:/var/www/html
  db:
    image: mysql:latest
    volumes:
      - dbdata:/var/lib/mysql

volumes:
  myvolume:
    driver: local
  dbdata:
    driver: local

挂载主机目录

services:
  web:
    image: nginx:latest
    volumes:
      - ./html:/var/www/html
      - ./nginx/conf.d:/etc/nginx/conf.d

项目(Project)

项目是一个包含多个服务的Docker Compose应用程序。每个项目都有自己的配置文件(docker-compose.yml),用于定义服务、网络和卷。

常用命令

启动项目

docker-compose up

使用-d参数可以在后台运行:

docker-compose up -d

停止项目

docker-compose down

添加--volumes参数可以同时删除卷:

docker-compose down --volumes

查看项目服务

docker-compose ps

查看项目日志

docker-compose logs

使用-f参数可以持续查看日志:

docker-compose logs -f

重启项目服务

docker-compose restart

停止项目服务

docker-compose stop

启动项目服务

docker-compose start

查看项目网络

docker-compose network ls

查看项目卷

docker-compose volume ls

执行容器命令

docker-compose exec web bash

构建或重建服务

docker-compose build

拉取服务镜像

docker-compose pull

推送服务镜像

docker-compose push

查看配置

docker-compose config

验证配置

docker-compose config --quiet

查看容器运行的进程

docker-compose top

暂停服务

docker-compose pause

恢复服务

docker-compose unpause

强制停止服务

docker-compose kill

实际应用场景

Web应用 + 数据库

version: '3'

services:
  web:
    image: nginx:latest
    ports:
      - "80:80"
    volumes:
      - ./html:/usr/share/nginx/html
    depends_on:
      - app

  app:
    build: ./app
    environment:
      - DB_HOST=db
      - DB_USER=root
      - DB_PASSWORD=example
      - DB_NAME=myapp
    depends_on:
      - db

  db:
    image: mysql:5.7
    environment:
      - MYSQL_ROOT_PASSWORD=example
      - MYSQL_DATABASE=myapp
    volumes:
      - db_data:/var/lib/mysql

volumes:
  db_data:

微服务架构

version: '3'

services:
  gateway:
    image: nginx:latest
    ports:
      - "80:80"
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d
    depends_on:
      - service1
      - service2

  service1:
    build: ./service1
    environment:
      - DB_HOST=db
    depends_on:
      - db
      - redis

  service2:
    build: ./service2
    environment:
      - DB_HOST=db
    depends_on:
      - db
      - redis

  db:
    image: postgres:13
    environment:
      - POSTGRES_PASSWORD=example
      - POSTGRES_DB=microservices
    volumes:
      - db_data:/var/lib/postgresql/data

  redis:
    image: redis:6
    volumes:
      - redis_data:/data

volumes:
  db_data:
  redis_data:

前端 + 后端 + 数据库的完整应用

version: '3'

services:
  # 前端服务
  frontend:
    build: ./frontend
    ports:
      - "80:80"
    depends_on:
      - backend
    environment:
      - API_URL=http://backend:3000

  # 后端API服务
  backend:
    build: ./backend
    ports:
      - "3000:3000"
    depends_on:
      - db
      - redis
    environment:
      - DB_HOST=db
      - DB_USER=postgres
      - DB_PASSWORD=example
      - DB_NAME=app
      - REDIS_HOST=redis
    volumes:
      - ./backend:/app
      - /app/node_modules

  # 数据库服务
  db:
    image: postgres:13
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=example
      - POSTGRES_DB=app
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"

  # Redis缓存服务
  redis:
    image: redis:6
    volumes:
      - redis_data:/data
    ports:
      - "6379:6379"

volumes:
  postgres_data:
  redis_data:

CI/CD流水线

version: '3'

services:
  # Jenkins CI/CD服务
  jenkins:
    image: jenkins/jenkins:lts
    ports:
      - "8080:8080"
      - "50000:50000"
    volumes:
      - jenkins_home:/var/jenkins_home
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - JAVA_OPTS=-Djenkins.install.runSetupWizard=false

  # SonarQube代码质量分析
  sonarqube:
    image: sonarqube:latest
    ports:
      - "9000:9000"
    environment:
      - SONARQUBE_JDBC_URL=jdbc:postgresql://sonardb:5432/sonar
      - SONARQUBE_JDBC_USERNAME=sonar
      - SONARQUBE_JDBC_PASSWORD=sonar
    volumes:
      - sonarqube_data:/opt/sonarqube/data
      - sonarqube_extensions:/opt/sonarqube/extensions
      - sonarqube_logs:/opt/sonarqube/logs
    depends_on:
      - sonardb

  # SonarQube数据库
  sonardb:
    image: postgres:12
    environment:
      - POSTGRES_USER=sonar
      - POSTGRES_PASSWORD=sonar
      - POSTGRES_DB=sonar
    volumes:
      - sonardb_data:/var/lib/postgresql/data

  # Nexus制品库
  nexus:
    image: sonatype/nexus3
    ports:
      - "8081:8081"
    volumes:
      - nexus_data:/nexus-data

volumes:
  jenkins_home:
  sonarqube_data:
  sonarqube_extensions:
  sonarqube_logs:
  sonardb_data:
  nexus_data:

开发环境配置

version: '3'

services:
  # 开发环境的应用服务
  app:
    build:
      context: .
      dockerfile: Dockerfile.dev
    volumes:
      - .:/app
      - /app/node_modules
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=development
      - CHOKIDAR_USEPOLLING=true  # 在Docker中启用热重载
    command: npm run dev

  # 测试服务
  test:
    build:
      context: .
      dockerfile: Dockerfile.dev
    volumes:
      - .:/app
      - /app/node_modules
    command: npm run test
    environment:
      - NODE_ENV=test

  # 数据库服务
  db:
    image: postgres:13
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
      - POSTGRES_DB=dev
    ports:
      - "5432:5432"
    volumes:
      - postgres_dev_data:/var/lib/postgresql/data
      - ./init-scripts:/docker-entrypoint-initdb.d  # 初始化脚本

  # 数据库管理工具
  adminer:
    image: adminer
    ports:
      - "8080:8080"
    depends_on:
      - db

volumes:
  postgres_dev_data:

监控系统

version: '3'

services:
  # Prometheus监控服务
  prometheus:
    image: prom/prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus_data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--web.console.libraries=/usr/share/prometheus/console_libraries'
      - '--web.console.templates=/usr/share/prometheus/consoles'

  # Grafana可视化服务
  grafana:
    image: grafana/grafana
    ports:
      - "3000:3000"
    volumes:
      - grafana_data:/var/lib/grafana
    depends_on:
      - prometheus

  # Node Exporter服务器指标收集
  node_exporter:
    image: prom/node-exporter
    ports:
      - "9100:9100"
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    command:
      - '--path.procfs=/host/proc'
      - '--path.sysfs=/host/sys'
      - '--collector.filesystem.ignored-mount-points=^/(sys|proc|dev|host|etc)($$|/)'

  # cAdvisor容器指标收集
  cadvisor:
    image: gcr.io/cadvisor/cadvisor
    ports:
      - "8080:8080"
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:ro
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro
      - /dev/disk/:/dev/disk:ro

volumes:
  prometheus_data:
  grafana_data:

常见问题与解决方案

容器间通信问题

问题:服务无法通过服务名访问其他服务。

解决方案

  • 确保服务在同一网络中
  • 使用服务名作为主机名进行访问
  • 检查网络配置是否正确

卷挂载权限问题

问题:容器无法写入挂载的卷。

解决方案

  • 检查宿主机目录的权限
  • 在容器中使用适当的用户运行应用
  • 使用命名卷而不是绑定挂载
services:
  app:
    image: myapp
    user: "1000:1000"  # 使用特定UID:GID运行
    volumes:
      - myvolume:/app/data

volumes:
  myvolume:

服务启动顺序问题

问题:依赖的服务尚未准备好,导致应用启动失败。

解决方案

  • 使用depends_on指定依赖关系
  • 实现健康检查
  • 在应用中实现重试逻辑
services:
  app:
    image: myapp
    depends_on:
      db:
        condition: service_healthy

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

使用环境变量

使用.env文件和环境变量来配置服务,避免在配置文件中硬编码敏感信息:

services:
  db:
    image: mysql:5.7
    environment:
      - MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD}
      - MYSQL_DATABASE=${DB_NAME}

分离开发和生产环境

使用多个Compose文件来分离开发和生产环境的配置:

  • docker-compose.yml:基本配置
  • docker-compose.override.yml:开发环境特定配置
  • docker-compose.prod.yml:生产环境特定配置
# 开发环境
docker-compose up

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

使用命名卷

使用命名卷而不是绑定挂载来持久化数据,这样可以更好地管理数据:

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

volumes:
  postgres_data:

限制资源使用

在生产环境中,限制容器的资源使用:

services:
  app:
    image: myapp
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
        reservations:
          cpus: '0.25'
          memory: 256M

使用多阶段构建

使用Docker的多阶段构建来创建更小、更安全的镜像:

services:
  app:
    build:
      context: ./app
      dockerfile: Dockerfile.multistage

对应的Dockerfile.multistage示例:

# 构建阶段
FROM node:14 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

# 运行阶段
FROM node:14-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package*.json ./
RUN npm install --production
CMD ["node", "dist/index.js"]

使用健康检查

为服务配置健康检查,确保服务正常运行:

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

使用配置文件

将配置文件挂载到容器中,而不是将配置硬编码到镜像中:

services:
  app:
    image: myapp
    volumes:
      - ./config:/app/config

使用Docker Compose覆盖文件

使用多个Docker Compose文件来覆盖配置,适应不同环境:

# 基本配置 + 开发环境配置
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up

# 基本配置 + 生产环境配置
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d

安全最佳实践

不使用root用户

在容器中避免使用root用户运行应用:

services:
  app:
    image: myapp
    user: "1000:1000"

限制容器权限

限制容器的权限,只授予必要的权限:

services:
  app:
    image: myapp
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE

使用只读文件系统

将容器的文件系统设置为只读,只有特定目录可写:

services:
  app:
    image: myapp
    read_only: true
    tmpfs:
      - /tmp
      - /var/run
    volumes:
      - data:/app/data

高级网络配置

使用外部网络

使用已存在的外部网络:

services:
  app:
    image: myapp
    networks:
      - external_network

networks:
  external_network:
    external: true
    name: existing_network

配置网络别名

为服务配置网络别名:

services:
  app:
    image: myapp
    networks:
      frontend:
        aliases:
          - webapp
          - app.local

扩展性考虑

使用服务扩展

使用docker-compose up --scale命令扩展服务:

docker-compose up --scale web=3 --scale worker=2

对应的配置:

services:
  web:
    image: nginx:latest
    ports:
      - "8080-8082:80"  # 动态端口映射
  
  worker:
    image: worker:latest

使用Docker Swarm

对于更大规模的部署,考虑使用Docker Swarm:

version: '3.8'

services:
  web:
    image: nginx:latest
    deploy:
      replicas: 3
      update_config:
        parallelism: 1
        delay: 10s
      restart_policy:
        condition: on-failure

监控和日志管理

集中式日志管理

配置日志驱动将日志发送到集中式日志系统:

services:
  app:
    image: myapp
    logging:
      driver: "fluentd"
      options:
        fluentd-address: "localhost:24224"
        tag: "docker.{{.Name}}"

使用Prometheus监控

配置Prometheus监控指标:

services:
  app:
    image: myapp
    labels:
      - "prometheus.enable=true"
      - "prometheus.port=8080"
      - "prometheus.path=/metrics"

生产环境部署检查清单

在将Docker Compose应用部署到生产环境之前,请检查以下事项:

  1. 移除开发环境特定的配置
  2. 设置适当的资源限制
  3. 配置健康检查
  4. 实现适当的日志管理
  5. 配置备份策略
  6. 设置监控
  7. 实现安全最佳实践
  8. 使用稳定的镜像标签,避免使用latest
  9. 测试高可用性和故障恢复
  10. 文档化部署和操作流程

总结

Docker Compose是一个强大的工具,它简化了多容器应用的定义和管理。通过使用YAML配置文件,开发人员可以轻松地定义服务、网络和卷,使应用程序的部署和管理更加简单和高效。本文介绍了Docker Compose的基本概念、配置选项、使用方法以及最佳实践,希望能帮助您更好地使用Docker Compose来管理容器化应用。

无论是开发环境还是生产环境,Docker Compose都是容器编排的理想选择,特别是对于中小型应用。对于更大规模的部署,可以考虑使用Kubernetes等更强大的容器编排平台。