Docker Compose实战指南

简介: 本文基于 Docker Compose V2(官方推荐),涵盖核心概念、YAML 配置详解、常用命令、四大实战案例及生产级最佳实践,助你从新手成长为能独立部署复杂多容器应用的专家。

本文基于 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 V1 Compose 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 调用。

关键底层细节:

  1. 项目(Project):Compose 将一个应用视为一个项目,默认使用当前目录的名称作为项目名称。你可以通过 --project-name 标志或 COMPOSE_PROJECT_NAME 环境变量覆盖。
  2. 服务(Service):项目由多个服务组成,每个服务对应一个或多个相同配置的容器。
  3. 网络隔离:Compose 会为每个项目创建一个独立的桥接网络,同一项目中的所有服务默认都连接到这个网络,并且可以通过服务名称互相访问。
  4. 数据卷管理:Compose 会自动管理命名数据卷,确保数据在容器重启或重建时不会丢失。

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

适用场景:

  • 开发环境:快速搭建一致的开发环境
  • 测试环境:自动化测试的环境准备
  • 小型到中型生产环境:单节点部署
  • CI/CD 流水线:构建和测试应用
  • 演示和原型:快速展示应用功能

局限性:

  • 仅支持单节点部署,不支持多节点集群
  • 没有内置的服务发现和负载均衡(需要配合反向代理)
  • 没有滚动更新和蓝绿部署功能(需要手动实现)
  • 不适合超大规模应用部署

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

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

2.1 文件格式与基本结构

Compose 文件使用 YAML 格式编写,默认文件名是 compose.yamldocker-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 服务会在 dbredis 容器启动后立即启动,但此时 PostgreSQL 可能还在初始化过程中,无法接受数据库连接,导致 app 服务启动失败。

解决方案:

  1. 应用层重试:在应用程序中实现数据库连接重试逻辑
  2. 健康检查:使用 healthcheck 配合 depends_on 的条件语法
  3. 等待脚本:使用 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 顶级元素详解

configssecrets 用于管理配置文件和敏感信息,避免将它们硬编码在镜像或 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

最佳实践:

  1. 提供一个 .env.example 文件作为模板,包含所有必需的环境变量
  2. .env 添加到 .gitignore 中,不要提交到版本控制
  3. 使用不同的 .env 文件对应不同的环境(.env.dev, .env.prod
  4. 使用 ${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_nameports 映射到固定主机端口,则不能进行扩容。

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 安全最佳实践

  1. 不要以 root 用户运行容器

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

  1. 使用只读文件系统

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

  1. 删除不必要的 capabilities

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

  1. 不要暴露不必要的端口
  2. 使用具体的镜像标签,不要使用 latest
  3. 定期更新基础镜像
  4. 扫描镜像漏洞

5.5 性能优化最佳实践

  1. 使用多阶段构建减小镜像大小
  2. 合理配置资源限制
  3. 使用命名数据卷而不是绑定挂载
  4. 避免在容器中运行多个进程
  5. 使用缓存加速构建
  6. 使用 .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

解决方案:

  1. 查找占用端口的进程:

sudo lsof -i :80

  1. 停止占用端口的进程
  2. 或者修改 Compose 文件中的端口映射

7.2 服务启动顺序问题

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

解决方案:

  1. 使用健康检查配合 depends_on 的条件语法(推荐)
  2. 在应用程序中实现重试逻辑
  3. 使用等待脚本

7.3 数据丢失问题

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

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

解决方案:

  1. 始终使用命名数据卷存储持久化数据
  2. 不要轻易使用 docker compose down -v,它会删除所有数据卷
  3. 定期备份数据

7.4 镜像拉取失败问题

解决方案:

  1. 检查网络连接
  2. 配置镜像加速器
  3. 登录到私有镜像仓库
  4. 检查镜像名称和标签是否正确

7.5 权限问题

错误信息:

Permission denied: '/app/data'

解决方案:

  1. 确保容器内的用户对挂载的目录有读写权限
  2. 使用 user 指令指定与主机目录权限匹配的 UID 和 GID
  3. 修改主机目录的权限

八、总结

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

目录
相关文章
|
18天前
|
人工智能 数据可视化 安全
王炸组合!阿里云 OpenClaw X 飞书 CLI,开启 Agent 基建狂潮!(附带免费使用6个月服务器)
本文详解如何用阿里云Lighthouse一键部署OpenClaw,结合飞书CLI等工具,让AI真正“动手”——自动群发、生成科研日报、整理知识库。核心理念:未来软件应为AI而生,CLI即AI的“手脚”,实现高效、安全、可控的智能自动化。
34830 46
王炸组合!阿里云 OpenClaw X 飞书 CLI,开启 Agent 基建狂潮!(附带免费使用6个月服务器)
|
12天前
|
人工智能 自然语言处理 安全
Claude Code 全攻略:命令大全 + 实战工作流(建议收藏)
本文介绍了Claude Code终端AI助手的使用指南,主要内容包括:1)常用命令如版本查看、项目启动和更新;2)三种工作模式切换及界面说明;3)核心功能指令速查表,包含初始化、压缩对话、清除历史等操作;4)详细解析了/init、/help、/clear、/compact、/memory等关键命令的使用场景和语法。文章通过丰富的界面截图和场景示例,帮助开发者快速掌握如何通过命令行和交互界面高效使用Claude Code进行项目开发,特别强调了CLAUDE.md文件作为项目知识库的核心作用。
11585 36
Claude Code 全攻略:命令大全 + 实战工作流(建议收藏)
|
7天前
|
人工智能 JavaScript Ubuntu
低成本搭建AIP自动化写作系统:Hermes保姆级使用教程,长文和逐步实操贴图
我带着怀疑的态度,深度使用了几天,聚焦微信公众号AIP自动化写作场景,写出来的几篇文章,几乎没有什么修改,至少合乎我本人的意愿,而且排版风格,也越来越完善,同样是起码过得了我自己这一关。 这个其实OpenClaw早可以实现了,但是目前我觉得最大的区别是,Hermes会自主总结提炼,并更新你的写作技能。 相信就冲这一点,就值得一试。 这篇帖子主要就Hermes部署使用,作一个非常详细的介绍,几乎一步一贴图。 关于Hermes,无论你赞成哪种声音,我希望都是你自己动手行动过,发自内心的选择!
2424 24
|
29天前
|
人工智能 JSON 机器人
让龙虾成为你的“公众号分身” | 阿里云服务器玩Openclaw
本文带你零成本玩转OpenClaw:学生认证白嫖6个月阿里云服务器,手把手配置飞书机器人、接入免费/高性价比AI模型(NVIDIA/通义),并打造微信公众号“全自动分身”——实时抓热榜、AI选题拆解、一键发布草稿,5分钟完成热点→文章全流程!
45740 157
让龙虾成为你的“公众号分身” | 阿里云服务器玩Openclaw
|
5天前
|
人工智能 弹性计算 安全
Hermes Agent是什么?怎么部署?超详细实操教程
Hermes Agent 是 Nous Research 于2026年2月开源的自进化AI智能体,支持跨会话持久记忆、自动提炼可复用技能、多平台接入与200+模型切换,真正实现“越用越懂你”。MIT协议,部署灵活,隐私可控。
1654 3
|
12天前
|
机器学习/深度学习 存储 人工智能
还在手写Skill?hermes-agent 让 Agent 自己进化能力
Hermes-agent 是 GitHub 23k+ Star 的开源项目,突破传统 Agent 依赖人工编写Aegnt Skill 的瓶颈,首创“自我进化”机制:通过失败→反思→自动生成技能→持续优化的闭环,让 Agent 在实践中自主构建、更新技能库,持续自我改进。
1802 6

热门文章

最新文章

下一篇
开通oss服务