欢迎光临
我们一直在努力

Docker Compose实战指南

本文基于 Docker Compose V2,所有内容均来自 Docker 官方文档和生产环境实践。

全文约 14800 字,建议收藏后阅读。读完本文,你将从 Docker Compose 新手成长为能够独立部署复杂多容器应用的专家。

一、Docker Compose 核心概念与底层原理

1.1 什么是 Docker Compose

Docker Compose 是 Docker 官方推出的多容器应用编排工具,它允许你使用一个 YAML 文件来定义和运行多个 Docker 容器组成的应用程序。

简单来说,如果你需要同时运行一个 Web 应用、一个数据库、一个缓存和一个消息队列,传统方式需要手动执行多个 docker run 命令,并且还要处理它们之间的网络连接、依赖关系等问题。而 Docker Compose 让你可以在一个文件中定义所有这些服务,然后用一条命令启动整个应用栈。

Docker Compose 的核心价值:

  • 一键部署:一条命令启动/停止整个应用

  • 环境一致性:开发、测试、生产环境使用相同的配置

  • 依赖管理:自动处理服务之间的启动顺序和依赖关系

  • 可重复性:相同的配置在任何地方都能得到相同的结果

  • 声明式配置:描述"想要什么",而不是"怎么做"

1.2 Docker Compose V1 vs V2:为什么必须升级到 V2

重要提醒:Compose V1(docker-compose 命令)已于 2023 年 7 月停止接收更新,并且不再包含在新的 Docker Desktop 版本中。所有新的开发和部署都应该使用 Compose V2(docker compose 命令)。

特性Compose V1Compose V2
命令格式 docker-compose(带连字符) docker compose(空格分隔)
实现语言 Python Go
集成方式 独立二进制文件 Docker CLI 插件
性能 较慢 显著提升(特别是构建速度)
容器命名 使用下划线分隔(project_service_1) 使用连字符分隔(project-service-1)
支持状态 已弃用 官方推荐,持续更新
BuildKit 支持 有限 原生支持,默认启用

升级方法:

  • Windows/macOS:升级到最新版 Docker Desktop 即可自动获得 Compose V2

  • Linux:通过 Docker 官方仓库安装或手动下载二进制文件到 $HOME/.docker/cli-plugins/ 目录

1.3 Docker Compose 工作原理

Docker Compose 本身不创建容器,它只是一个客户端工具,通过 Docker API 与 Docker Engine 通信,将 Compose 文件中的声明转换为一系列 Docker API 调用。

关键底层细节:

  • 项目(Project):Compose 将一个应用视为一个项目,默认使用当前目录的名称作为项目名称。你可以通过 –project-name 标志或 COMPOSE_PROJECT_NAME 环境变量覆盖。

  • 服务(Service):项目由多个服务组成,每个服务对应一个或多个相同配置的容器。

  • 网络隔离:Compose 会为每个项目创建一个独立的桥接网络,同一项目中的所有服务默认都连接到这个网络,并且可以通过服务名称互相访问。

  • 数据卷管理:Compose 会自动管理命名数据卷,确保数据在容器重启或重建时不会丢失。

  • 1.4 Docker Compose 的适用场景与局限性

    适用场景:

    • 开发环境:快速搭建一致的开发环境

    • 测试环境:自动化测试的环境准备

    • 小型到中型生产环境:单节点部署

    • CI/CD 流水线:构建和测试应用

    • 演示和原型:快速展示应用功能

    局限性:

    • 仅支持单节点部署,不支持多节点集群

    • 没有内置的服务发现和负载均衡(需要配合反向代理)

    • 没有滚动更新和蓝绿部署功能(需要手动实现)

    • 不适合超大规模应用部署

    注意:对于大规模生产环境,应该使用 Kubernetes 而不是 Docker Compose。但对于 90% 的中小型应用,Docker Compose 已经足够强大且简单易用。

    二、docker-compose.yml 文件详解(Compose Specification)

    2.1 文件格式与基本结构

    Compose 文件使用 YAML 格式编写,默认文件名是 compose.yaml 或 docker-compose.yml(两者都支持,推荐使用 compose.yaml)。

    重要更新:自 Docker Compose v1.27 起,version 字段已被弃用。现在所有新的 Compose 文件都应该省略 version 字段,直接使用最新的 Compose Specification。

    基本结构:

    # 顶级元素:services(必需)
    services:
      # 服务1定义
      service-name-1:
        # 服务配置
        image: nginx:alpine
        ports:
          – "80:80"
      
      # 服务2定义
      service-name-2:
        # 服务配置
        build: .
        depends_on:
          – service-name-1

    # 顶级元素:networks(可选)
    networks:
      # 自定义网络定义
      my-network:
        driver: bridge

    # 顶级元素:volumes(可选)
    volumes:
      # 自定义数据卷定义
      my-volume:

    # 顶级元素:configs(可选)
    configs:
      # 配置文件定义
      my-config:
        file: ./config.conf

    # 顶级元素:secrets(可选)
    secrets:
      # 敏感信息定义
      my-secret:
        file: ./secret.txt

    2.2 services 顶级元素详解

    services 是 Compose 文件中唯一必需的顶级元素,用于定义应用中的所有服务。每个服务对应一个或多个容器。

    2.2.1 基本配置

    image:指定服务使用的镜像。

    services:
      nginx:
        image: nginx:1.25-alpine  # 推荐使用具体版本,不要使用 latest

    container_name:指定容器名称。

    services:
      nginx:
        image: nginx:alpine
        container_name: my-nginx  # 自定义容器名称

    注意:如果指定了 container_name,则该服务不能进行扩容(scale),因为容器名称必须是唯一的。

    command:覆盖容器默认的 CMD 指令。

    services:
      app:
        image: node:20-alpine
        command: npm start  # 字符串形式
        
        # 或者数组形式(推荐,避免 shell 解析问题)
        command: ["npm", "start"]

    entrypoint:覆盖容器默认的 ENTRYPOINT 指令。

    services:
      app:
        image: node:20-alpine
        entrypoint: ["/app/entrypoint.sh"]

    working_dir:设置容器内的工作目录。

    services:
      app:
        image: node:20-alpine
        working_dir: /app

    user:指定运行容器内进程的用户。

    services:
      app:
        image: node:20-alpine
        user: "1000:1000"  # UID:GID

    2.2.2 构建配置

    build:指定从 Dockerfile 构建镜像。

    # 简单形式:指定上下文路径
    services:
      app:
        build: .  # 使用当前目录下的 Dockerfile 构建

    # 完整形式
    services:
      app:
        build:
          context: ./app  # 构建上下文路径
          dockerfile: Dockerfile.prod  # 指定 Dockerfile 名称
          args:  # 构建参数
            NODE_ENV: production
            VERSION: 1.0.0
          target: production  # 多阶段构建的目标阶段
          cache_from:  # 构建缓存源
            – myapp:latest
          network: host  # 构建时使用的网络

    重要提示: 构建上下文是 Docker 守护进程可以访问的文件和目录的集合。Docker 会将上下文中的所有文件发送给守护进程,因此应该尽量减小上下文大小,将不需要的文件添加到 .dockerignore 中。

    2.2.3 端口配置

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

    services:
      nginx:
        image: nginx:alpine
        ports:
          – "80:80"  # 主机端口:容器端口
          – "443:443"
          – "127.0.0.1:8080:80"  # 只绑定到 localhost
          – "8000-8010:8000-8010"  # 端口范围映射
          – "9000"  # 随机映射主机端口到容器 9000 端口

    expose:暴露端口给同一网络中的其他服务,但不映射到主机。

    services:
      db:
        image: postgres:16-alpine
        expose:
          – "5432"  # 同一网络中的服务可以通过 db:5432 访问

    2.2.4 环境变量配置

    environment:设置环境变量。

    services:
      db:
        image: postgres:16-alpine
        environment:
          # 数组形式
          – POSTGRES_DB=myapp
          – POSTGRES_USER=postgres
          – POSTGRES_PASSWORD=secret
          
          # 或者对象形式(推荐)
          POSTGRES_DB: myapp
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: secret

    env_file:从文件中加载环境变量。

    services:
      app:
        image: node:20-alpine
        env_file:
          – .env  # 默认加载
          – .env.production  # 额外的环境文件

    最佳实践:永远不要在 Compose 文件中硬编码敏感信息(如密码、API 密钥等)。应该使用 .env 文件,并将 .env 添加到 .gitignore 中。对于高度敏感的数据,应该使用 Docker Secrets。

    2.2.5 数据卷配置

    volumes:挂载数据卷或主机目录到容器。

    services:
      db:
        image: postgres:16-alpine
        volumes:
          # 命名数据卷(推荐,由 Docker 管理)
          – postgres_data:/var/lib/postgresql/data
          
          # 绑定挂载(主机目录)
          – ./config:/etc/postgresql/config:ro  # 只读挂载
          – ./logs:/var/log/postgresql:rw  # 读写挂载(默认)
          
          # tmpfs 挂载(临时文件系统,存储在内存中)
          – type: tmpfs
            target: /tmp
            tmpfs:
              size: 100M

    # 必须在顶级 volumes 中声明命名数据卷
    volumes:
      postgres_data:

    三种挂载类型对比:

    类型语法特点适用场景
    命名数据卷 volume_name:/container/path Docker 管理,性能好,数据持久化 数据库数据、应用状态
    绑定挂载 /host/path:/container/path 主机目录直接挂载,开发时方便 开发环境代码挂载、配置文件
    tmpfs type: tmpfs 内存中存储,速度快,数据不持久 临时文件、缓存
    2.2.6 网络配置

    networks:指定服务连接的网络。

    services:
      web:
        image: nginx:alpine
        networks:
          – frontend
          – backend
      
      app:
        image: node:20-alpine
        networks:
          – backend
      
      db:
        image: postgres:16-alpine
        networks:
          – backend

    networks:
      frontend:
      backend:

    重要:默认情况下,Compose 会创建一个名为 {project_name}_default 的网络,所有服务都会连接到这个网络。只有当你需要网络隔离时,才需要自定义网络。

    network_mode:指定网络模式。

    services:
      app:
        image: node:20-alpine
        network_mode: bridge  # 默认
        # network_mode: host  # 使用主机网络
        # network_mode: none  # 禁用网络
        # network_mode: service:web  # 共享另一个服务的网络命名空间

    2.2.7 依赖关系与启动顺序

    depends_on:指定服务之间的依赖关系,决定启动顺序。

    services:
      web:
        image: nginx:alpine
        depends_on:
          – app
          – db
      
      app:
        image: node:20-alpine
        depends_on:
          – db
          – redis
      
      db:
        image: postgres:16-alpine
      
      redis:
        image: redis:7-alpine

    非常重要的注意事项:

    depends_on 只保证容器的启动顺序,不保证服务的就绪顺序。也就是说,它只会等待容器启动,而不会等待容器内的应用程序完全启动并准备好接受请求。

    例如,上面的配置中,app 服务会在 db 和 redis 容器启动后立即启动,但此时 PostgreSQL 可能还在初始化过程中,无法接受数据库连接,导致 app 服务启动失败。

    解决方案:

  • 应用层重试:在应用程序中实现数据库连接重试逻辑

  • 健康检查:使用 healthcheck 配合 depends_on 的条件语法

  • 等待脚本:使用 wait-for-it.sh 等工具等待依赖服务就绪

  • 健康检查配合 depends_on(推荐):

    services:
      app:
        image: node:20-alpine
        depends_on:
          db:
            condition: service_healthy
          redis:
            condition: service_healthy
      
      db:
        image: postgres:16-alpine
        healthcheck:
          test: ["CMD-SHELL", "pg_isready -U postgres"]
          interval: 5s
          timeout: 5s
          retries: 5
      
      redis:
        image: redis:7-alpine
        healthcheck:
          test: ["CMD", "redis-cli", "ping"]
          interval: 5s
          timeout: 5s
          retries: 5

    2.2.8 重启策略

    restart:指定容器的重启策略。

    services:
      app:
        image: node:20-alpine
        restart: no  # 默认,不重启
        # restart: always  # 总是重启
        # restart: on-failure  # 仅在失败时重启
        # restart: unless-stopped  # 除非手动停止,否则总是重启

    **生产环境推荐使用 restart: unless-stopped**,这样容器在崩溃或 Docker 重启时会自动重启,但如果你手动停止了容器,它不会自动重启。

    2.2.9 健康检查

    healthcheck:配置容器的健康检查,用于判断容器内的应用是否正常运行。

    services:
      web:
        image: nginx:alpine
        healthcheck:
          test: ["CMD", "curl", "-f", "http://localhost/health"]
          interval: 30s  # 检查间隔
          timeout: 10s  # 超时时间
          retries: 3  # 重试次数
          start_period: 30s  # 启动后等待多久开始检查
          disable: false  # 是否禁用健康检查

    test 命令的三种形式:

    • ["CMD", "command", "arg1", "arg2"]:直接执行命令,不通过 shell

    • ["CMD-SHELL", "command arg1 arg2"]:通过 shell 执行命令

    • "command arg1 arg2":等同于 ["CMD-SHELL", "command arg1 arg2"]

    2.2.10 资源限制

    deploy.resources:配置容器的资源限制和预留。

    services:
      app:
        image: node:20-alpine
        deploy:
          resources:
            limits:  # 资源上限
              cpus: "1.0"  # 最多使用 1 个 CPU 核心
              memory: 512M  # 最多使用 512MB 内存
            reservations:  # 资源预留
              cpus: "0.25"  # 预留 0.25 个 CPU 核心
              memory: 128M  # 预留 128MB 内存

    生产环境必须配置资源限制,防止单个容器占用所有主机资源,导致其他服务无法运行。

    2.3 networks 顶级元素详解

    networks 用于定义自定义网络。默认情况下,Compose 会创建一个桥接网络,但你可以自定义网络的驱动、IP 地址范围等。

    networks:
      frontend:
        driver: bridge  # 默认驱动
        driver_opts:
          com.docker.network.bridge.enable_icc: "true"  # 启用容器间通信
      
      backend:
        driver: bridge
        ipam:  # IP 地址管理
          config:
            – subnet: 172.20.0.0/16
              gateway: 172.20.0.1
      
      external-network:
        external: true  # 使用已存在的外部网络
        name: my-existing-network  # 外部网络的实际名称

    2.4 volumes 顶级元素详解

    volumes 用于定义命名数据卷。

    volumes:
      postgres_data:
        driver: local  # 默认驱动
        driver_opts:
          type: none
          o: bind
          device: /data/postgres  # 绑定到主机指定目录
      
      redis_data:
        external: true  # 使用已存在的外部数据卷
        name: my-existing-redis-volume

    2.5 configs 与 secrets 顶级元素详解

    configs 和 secrets 用于管理配置文件和敏感信息,避免将它们硬编码在镜像或 Compose 文件中。

    configs 示例:

    services:
      nginx:
        image: nginx:alpine
        configs:
          – source: nginx-config
            target: /etc/nginx/nginx.conf
            mode: 0444

    configs:
      nginx-config:
        file: ./nginx.conf  # 从文件加载配置

    secrets 示例:

    services:
      db:
        image: postgres:16-alpine
        secrets:
          – postgres-password
        environment:
          POSTGRES_PASSWORD_FILE: /run/secrets/postgres-password

    secrets:
      postgres-password:
        file: ./postgres-password.txt  # 从文件加载密码

    注意:在单机 Docker 环境中,secrets 只是简单地将文件挂载到容器的 /run/secrets/ 目录,并没有加密。但它仍然比硬编码密码要好,因为密码不会出现在 Compose 文件或镜像中。

    2.6 变量替换与 .env 文件

    Docker Compose 支持在 Compose 文件中使用环境变量进行变量替换。

    基本语法:

    services:
      db:
        image: postgres:${POSTGRES_VERSION:-16}-alpine
        environment:
          POSTGRES_DB: ${POSTGRES_DB}
          POSTGRES_USER: ${POSTGRES_USER}
          POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}

    变量修饰符:

    • ${VAR:-default}:如果 VAR 未设置或为空,使用 default

    • ${VAR-default}:如果 VAR 未设置,使用 default

    • ${VAR:?error}:如果 VAR 未设置或为空,抛出错误并退出

    • ${VAR?error}:如果 VAR 未设置,抛出错误并退出

    .env 文件: Compose 会自动从当前目录下的 .env 文件加载环境变量。

    # .env 文件示例
    POSTGRES_VERSION=16
    POSTGRES_DB=myapp
    POSTGRES_USER=postgres
    POSTGRES_PASSWORD=supersecretpassword

    最佳实践:

  • 提供一个 .env.example 文件作为模板,包含所有必需的环境变量

  • 将 .env 添加到 .gitignore 中,不要提交到版本控制

  • 使用不同的 .env 文件对应不同的环境(.env.dev, .env.prod)

  • 使用 ${VAR:?error} 语法验证必需的环境变量,实现快速失败

  • 三、Docker Compose 常用命令详解

    3.1 基本生命周期命令

    docker compose up

    创建并启动所有服务。

    # 前台运行,显示所有日志
    docker compose up

    # 后台运行(守护进程模式)
    docker compose up -d

    # 启动前重新构建镜像
    docker compose up –build

    # 强制重新创建所有容器
    docker compose up –force-recreate

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

    docker compose down

    停止并删除所有服务容器、网络。

    # 停止并删除容器和网络
    docker compose down

    # 同时删除数据卷(谨慎使用!会丢失所有数据)
    docker compose down -v

    # 同时删除镜像
    docker compose down –rmi all

    # 删除孤立的容器(不在 Compose 文件中定义的容器)
    docker compose down –remove-orphans

    docker compose start/stop/restart

    启动、停止或重启已存在的服务。

    # 启动所有服务
    docker compose start

    # 启动指定服务
    docker compose start web

    # 停止所有服务
    docker compose stop

    # 停止指定服务
    docker compose stop db

    # 重启所有服务
    docker compose restart

    # 重启指定服务
    docker compose restart app

    3.2 查看与调试命令

    docker compose ps

    列出所有服务及其状态。

    # 列出运行中的服务
    docker compose ps

    # 列出所有服务(包括已停止的)
    docker compose ps -a

    docker compose logs

    查看服务日志。

    # 查看所有服务的日志
    docker compose logs

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

    # 实时跟踪日志
    docker compose logs -f

    # 显示最后 N 行日志
    docker compose logs –tail=100

    # 显示日志时间戳
    docker compose logs -t

    docker compose exec

    在运行中的容器内执行命令。

    # 在 app 容器中启动 bash
    docker compose exec app bash

    # 以 root 用户执行命令
    docker compose exec -u root app bash

    # 执行一次性命令
    docker compose exec db psql -U postgres myapp

    docker compose run

    运行一个一次性的服务容器。

    # 运行 app 服务并执行 npm test
    docker compose run app npm test

    # 运行时不启动依赖服务
    docker compose run –no-deps app npm test

    # 运行后自动删除容器
    docker compose run –rm app npm test

    docker compose exec vs docker compose run:

    • exec:在已经运行的容器中执行命令

    • run:创建一个新的容器并执行命令,执行完后容器会停止

    3.3 构建与镜像命令

    docker compose build

    构建服务镜像。

    # 构建所有服务的镜像
    docker compose build

    # 构建指定服务的镜像
    docker compose build app

    # 不使用缓存构建
    docker compose build –no-cache

    # 构建并拉取最新的基础镜像
    docker compose build –pull

    docker compose pull

    拉取服务使用的镜像。

    # 拉取所有服务的镜像
    docker compose pull

    # 拉取指定服务的镜像
    docker compose pull db

    docker compose push

    推送服务镜像到镜像仓库。

    # 推送所有服务的镜像
    docker compose push

    # 推送指定服务的镜像
    docker compose push app

    3.4 其他常用命令

    docker compose config

    验证并查看合并后的 Compose 配置。

    # 验证配置是否正确
    docker compose config

    # 查看合并后的完整配置
    docker compose config –no-interpolate

    docker compose top

    显示服务容器中运行的进程。

    # 显示所有服务的进程
    docker compose top

    # 显示指定服务的进程
    docker compose top app

    docker compose stats

    显示服务容器的资源使用情况。

    # 显示所有服务的资源使用情况
    docker compose stats

    # 显示指定服务的资源使用情况
    docker compose stats app db

    docker compose scale

    扩容服务。

    # 将 app 服务扩容到 3 个实例
    docker compose up –scale app=3 -d

    # 或者
    docker compose scale app=3

    注意:如果服务指定了 container_name 或 ports 映射到固定主机端口,则不能进行扩容。

    docker compose watch

    自动监控文件变化并重新构建和重启服务(Compose V2.22+)。

    # 启动文件监控
    docker compose watch

    这是开发环境的一个非常有用的功能,可以实现代码热重载。

    四、实战案例:从简单到复杂的应用部署

    4.1 案例一:最简单的 Nginx 静态网站

    文件结构:

    .
    ├── docker-compose.yml
    └── html
        └── index.html

    docker-compose.yml:

    services:
      nginx:
        image: nginx:1.25-alpine
        ports:
          – "80:80"
        volumes:
          – ./html:/usr/share/nginx/html:ro
        restart: unless-stopped

    html/index.html:

    <!DOCTYPE html>
    <html>
    <head>
        <title>Hello Docker Compose</title>
    </head>
    <body>
        <h1>Hello Docker Compose!</h1>
    </body>
    </html>

    启动命令:

    docker compose up -d

    访问 http://localhost 即可看到页面。

    4.2 案例二:Node.js + PostgreSQL + Redis 应用

    文件结构:

    .
    ├── .env
    ├── docker-compose.yml
    └── app
        ├── Dockerfile
        ├── package.json
        └── server.js

    .env:

    POSTGRES_DB=myapp
    POSTGRES_USER=postgres
    POSTGRES_PASSWORD=supersecretpassword
    REDIS_URL=redis://redis:6379
    DATABASE_URL=postgres://postgres:supersecretpassword@db:5432/myapp

    docker-compose.yml:

    services:
      app:
        build: ./app
        ports:
          – "3000:3000"
        environment:
          DATABASE_URL: ${DATABASE_URL}
          REDIS_URL: ${REDIS_URL}
        depends_on:
          db:
            condition: service_healthy
          redis:
            condition: service_healthy
        restart: unless-stopped
        deploy:
          resources:
            limits:
              cpus: "1.0"
              memory: 512M

      db:
        image: postgres:16-alpine
        environment:
          POSTGRES_DB: ${POSTGRES_DB}
          POSTGRES_USER: ${POSTGRES_USER}
          POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
        volumes:
          – postgres_data:/var/lib/postgresql/data
        healthcheck:
          test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
          interval: 5s
          timeout: 5s
          retries: 5
        restart: unless-stopped

      redis:
        image: redis:7-alpine
        volumes:
          – redis_data:/data
        healthcheck:
          test: ["CMD", "redis-cli", "ping"]
          interval: 5s
          timeout: 5s
          retries: 5
        restart: unless-stopped

    volumes:
      postgres_data:
      redis_data:

    app/Dockerfile:

    FROM node:20-alpine

    WORKDIR /app

    COPY package*.json ./
    RUN npm ci –only=production

    COPY . .

    EXPOSE 3000

    CMD ["node", "server.js"]

    启动命令:

    docker compose up -d

    4.3 案例三:带 Nginx 反向代理和 HTTPS 的完整应用

    文件结构:

    .
    ├── .env
    ├── docker-compose.yml
    ├── nginx
    │   ├── Dockerfile
    │   └── nginx.conf
    ├── certs
    │   ├── fullchain.pem
    │   └── privkey.pem
    └── app
        ├── Dockerfile
        └── …

    docker-compose.yml:

    services:
      nginx:
        build: ./nginx
        ports:
          – "80:80"
          – "443:443"
        volumes:
          – ./certs:/etc/nginx/certs:ro
        depends_on:
          – app
        restart: unless-stopped
        networks:
          – frontend
          – backend

      app:
        build: ./app
        environment:
          DATABASE_URL: ${DATABASE_URL}
          REDIS_URL: ${REDIS_URL}
        depends_on:
          db:
            condition: service_healthy
          redis:
            condition: service_healthy
        restart: unless-stopped
        networks:
          – backend
        deploy:
          resources:
            limits:
              cpus: "1.0"
              memory: 512M

      db:
        image: postgres:16-alpine
        environment:
          POSTGRES_DB: ${POSTGRES_DB}
          POSTGRES_USER: ${POSTGRES_USER}
          POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
        volumes:
          – postgres_data:/var/lib/postgresql/data
        healthcheck:
          test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
          interval: 5s
          timeout: 5s
          retries: 5
        restart: unless-stopped
        networks:
          – backend

      redis:
        image: redis:7-alpine
        volumes:
          – redis_data:/data
        healthcheck:
          test: ["CMD", "redis-cli", "ping"]
          interval: 5s
          timeout: 5s
          retries: 5
        restart: unless-stopped
        networks:
          – backend

    networks:
      frontend:
      backend:

    volumes:
      postgres_data:
      redis_data:

    nginx/nginx.conf:

    events {}

    http {
    server {
    listen 80;
    server_name example.com;
    return 301 https://$server_name$request_uri;
    }

    server {
    listen 443 ssl;
    server_name example.com;

    ssl_certificate /etc/nginx/certs/fullchain.pem;
    ssl_certificate_key /etc/nginx/certs/privkey.pem;

    location / {
    proxy_pass http://app:3000;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    }
    }
    }

    4.4 案例四:多环境配置(开发/生产)

    使用多个 Compose 文件实现不同环境的配置。

    基础配置:docker-compose.yml

    services:
      app:
        build: ./app
        environment:
          DATABASE_URL: ${DATABASE_URL}
          REDIS_URL: ${REDIS_URL}

      db:
        image: postgres:16-alpine
        environment:
          POSTGRES_DB: ${POSTGRES_DB}
          POSTGRES_USER: ${POSTGRES_USER}
          POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
        volumes:
          – postgres_data:/var/lib/postgresql/data

      redis:
        image: redis:7-alpine
        volumes:
          – redis_data:/data

    volumes:
      postgres_data:
      redis_data:

    开发环境覆盖:docker-compose.override.yml(默认自动加载)

    services:
      app:
        ports:
          – "3000:3000"
        volumes:
          – ./app:/app
          – /app/node_modules
        environment:
          NODE_ENV: development
        command: npm run dev

      db:
        ports:
          – "5432:5432"

      redis:
        ports:
          – "6379:6379"

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

    services:
      nginx:
        image: nginx:1.25-alpine
        ports:
          – "80:80"
          – "443:443"
        volumes:
          – ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
          – ./certs:/etc/nginx/certs:ro
        depends_on:
          – app
        restart: unless-stopped

      app:
        environment:
          NODE_ENV: production
        restart: unless-stopped
        deploy:
          resources:
            limits:
              cpus: "1.0"
              memory: 512M

      db:
        restart: unless-stopped
        healthcheck:
          test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
          interval: 5s
          timeout: 5s
          retries: 5

      redis:
        restart: unless-stopped
        healthcheck:
          test: ["CMD", "redis-cli", "ping"]
          interval: 5s
          timeout: 5s
          retries: 5

    启动开发环境:

    docker compose up -d

    启动生产环境:

    docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

    五、高级特性与最佳实践

    5.1 使用 Profiles 管理可选服务

    Profiles 允许你定义哪些服务在特定环境下应该启动。

    services:
      app:
        image: node:20-alpine
        # 总是启动

      db:
        image: postgres:16-alpine
        # 总是启动

      redis:
        image: redis:7-alpine
        profiles: ["with-redis"]  # 只有指定 with-redis profile 时才启动

      adminer:
        image: adminer:latest
        ports:
          – "8080:8080"
        profiles: ["debug"]  # 只有指定 debug profile 时才启动

    启动命令:

    # 只启动 app 和 db
    docker compose up -d

    # 启动 app、db 和 redis
    docker compose –profile with-redis up -d

    # 启动所有服务
    docker compose –profile "*" up -d

    5.2 使用 extends 共享配置

    extends 允许你从另一个 Compose 文件或同一文件中的另一个服务继承配置。

    # base.yml
    services:
      base-app:
        build: ./app
        environment:
          NODE_ENV: production
        restart: unless-stopped
        deploy:
          resources:
            limits:
              cpus: "1.0"
              memory: 512M

    # docker-compose.yml
    services:
      app1:
        extends:
          file: base.yml
          service: base-app
        environment:
          APP_NAME: app1

      app2:
        extends:
          file: base.yml
          service: base-app
        environment:
          APP_NAME: app2

    5.3 日志配置最佳实践

    配置日志驱动和日志轮转:

    services:
      app:
        image: node:20-alpine
        logging:
          driver: json-file  # 默认驱动
          options:
            max-size: "10m"  # 单个日志文件最大大小
            max-file: "3"    # 最多保留 3 个日志文件

    生产环境推荐配置:

    x-logging: &default-logging
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"

    services:
      app:
        image: node:20-alpine
        logging: *default-logging

      db:
        image: postgres:16-alpine
        logging: *default-logging

      redis:
        image: redis:7-alpine
        logging: *default-logging

    5.4 安全最佳实践

  • 不要以 root 用户运行容器

    services:
      app:
        image: node:20-alpine
        user: "1000:1000"  # 使用非 root 用户

  • 使用只读文件系统

    services:
      app:
        image: node:20-alpine
        read_only: true
        tmpfs:
          – /tmp
          – /app/tmp

  • 删除不必要的 capabilities

    services:
      app:
        image: node:20-alpine
        cap_drop:
          – ALL

  • 不要暴露不必要的端口

  • 使用具体的镜像标签,不要使用 latest

  • 定期更新基础镜像

  • 扫描镜像漏洞

  • 5.5 性能优化最佳实践

  • 使用多阶段构建减小镜像大小

  • 合理配置资源限制

  • 使用命名数据卷而不是绑定挂载

  • 避免在容器中运行多个进程

  • 使用缓存加速构建

  • 使用 .dockerignore 减小构建上下文

  • 六、生产环境部署与运维

    6.1 生产环境部署清单

    在将应用部署到生产环境之前,请确保你已经完成了以下检查:

    • [ ] 所有服务都配置了 restart: unless-stopped

    • [ ] 所有关键服务都配置了健康检查

    • [ ] 所有服务都配置了资源限制

    • [ ] 所有持久化数据都使用了命名数据卷

    • [ ] 没有硬编码任何敏感信息

    • [ ] 日志配置了轮转

    • [ ] 应用运行在非 root 用户下

    • [ ] 只暴露了必要的端口

    • [ ] 配置了 HTTPS

    • [ ] 有备份策略

    • [ ] 有监控和告警

    6.2 备份与恢复

    备份数据卷:

    # 创建备份容器,将数据卷内容打包
    docker run –rm \\
      -v myapp_postgres_data:/data \\
      -v $(pwd):/backup \\
      alpine tar czf /backup/postgres_backup_$(date +%Y%m%d).tar.gz -C /data .

    恢复数据卷:

    # 停止相关服务
    docker compose stop db

    # 创建恢复容器,将备份内容解压到数据卷
    docker run –rm \\
      -v myapp_postgres_data:/data \\
      -v $(pwd):/backup \\
      alpine tar xzf /backup/postgres_backup_20260420.tar.gz -C /data

    # 启动服务
    docker compose start db

    6.3 滚动更新

    Docker Compose 本身不支持真正的滚动更新,但你可以通过以下方式实现类似的效果:

    # 构建新镜像
    docker compose build app

    # 启动新的容器实例
    docker compose up -d –scale app=2 –no-recreate

    # 等待新容器健康检查通过
    sleep 30

    # 停止旧容器
    docker compose stop app-old

    # 删除旧容器
    docker compose rm -f app-old

    # 缩容回原来的数量
    docker compose up -d –scale app=1 –no-recreate

    对于更复杂的更新需求,建议使用 Kubernetes。

    6.4 监控与告警

    使用 docker stats 监控资源使用:

    docker compose stats

    使用 Prometheus + Grafana 进行全面监控:

    services:
      prometheus:
        image: prom/prometheus:latest
        ports:
          – "9090:9090"
        volumes:
          – ./prometheus.yml:/etc/prometheus/prometheus.yml
        restart: unless-stopped

      grafana:
        image: grafana/grafana:latest
        ports:
          – "3000:3000"
        volumes:
          – grafana_data:/var/lib/grafana
        restart: unless-stopped

    volumes:
      grafana_data:

    七、常见问题排查与解决方案

    7.1 端口冲突问题

    错误信息:

    Error response from daemon: driver failed programming external connectivity on endpoint …: Bind for 0.0.0.0:80 failed: port is already allocated

    解决方案:

  • 查找占用端口的进程:

    sudo lsof -i :80

  • 停止占用端口的进程

  • 或者修改 Compose 文件中的端口映射

  • 7.2 服务启动顺序问题

    问题描述: 应用服务在数据库服务准备好之前就启动了,导致连接失败。

    解决方案:

  • 使用健康检查配合 depends_on 的条件语法(推荐)

  • 在应用程序中实现重试逻辑

  • 使用等待脚本

  • 7.3 数据丢失问题

    问题描述: 执行 docker compose down 后,数据库数据丢失。

    原因: 没有使用命名数据卷,而是使用了匿名数据卷。

    解决方案:

  • 始终使用命名数据卷存储持久化数据

  • 不要轻易使用 docker compose down -v,它会删除所有数据卷

  • 定期备份数据

  • 7.4 镜像拉取失败问题

    解决方案:

  • 检查网络连接

  • 配置镜像加速器

  • 登录到私有镜像仓库

  • 检查镜像名称和标签是否正确

  • 7.5 权限问题

    错误信息:

    Permission denied: '/app/data'

    解决方案:

  • 确保容器内的用户对挂载的目录有读写权限

  • 使用 user 指令指定与主机目录权限匹配的 UID 和 GID

  • 修改主机目录的权限

  • 八、总结

    Docker Compose 是一个非常强大且简单易用的多容器应用编排工具。它通过声明式的配置文件,让你可以轻松地定义、运行和管理复杂的多容器应用。 希望本文能够帮助你全面掌握 Docker Compose,让你的开发和部署工作更加高效和轻松。如果你觉得本文对你有帮助,欢迎点赞、收藏和分享。

    赞(0)
    未经允许不得转载:171主机测评 » Docker Compose实战指南
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址