--- tags: - docker - devops - containerization - docker-compose create time: 2026-04-28 14:35 --- # Docker Compose 使用指南 > 用 **YAML 文件**定义多容器应用,一条 `docker compose up` 启动所有服务。从"一条命令跑起全栈"到多环境编排,本文带你掌握 Compose 从入门到进阶的所有关键能力。 ## 概述 Docker Compose 的核心价值:**把分散的 `docker run` 命令变成声明式的 YAML 配置**,让团队用同一份文件即可启动完整应用栈。 > [!tip] 快速上手(TL;DR) > > ```bash > # 1. 写好 docker-compose.yml,包含所有服务 > # 2. 一条命令启动全部 > docker compose up -d > # 3. 看日志、看状态 > docker compose logs -f > docker compose ps > # 4. 清理 > docker compose down > ``` > [!question] 什么时候需要 Compose? > 如果你的应用只跑一个进程(比如单个 Nginx),`docker run` 就够了。但只要涉及**两个及以上**的容器协作(应用 + 数据库、前端 + API + 缓存),Compose 就是标配。 ## 正文 ### 一、Compose 文件结构 Compose 文件由三个顶级字段构成,理解它们就能掌握 80% 的配置: ```mermaid flowchart LR A["services — 要跑的服务"] --> D["docker compose up"] B["networks — 服务间的网络"] --> D C["volumes — 数据持久化"] --> D style A fill:#e3f2fd style B fill:#f3e5f5 style C fill:#e8f5e9 ``` 基本结构示例: ```yaml # docker-compose.yml services: web: # 前端/后端应用 db: # 数据库 redis: # 缓存 nginx: # 反向代理 networks: # 网络定义 volumes: # 数据卷定义 ``` > [!note] `version` 字段已弃用 > Compose V2(2020年起)已不需要 `version` 字段。新项目直接用无 version 的新格式,兼容性更好。 --- ### 二、完整示例:Node.js + MySQL + Redis 全栈项目 一个生产级别的 Compose 文件应该包含**健康检查、网络隔离、数据持久化**: ```yaml services: # ========== 后端应用 ========== api: build: context: ./server dockerfile: Dockerfile args: NODE_ENV: production target: production container_name: my-api ports: - "3000:3000" environment: NODE_ENV: production DB_HOST: db DB_PORT: 3306 DB_USER: root DB_PASSWORD: root123 DB_NAME: mydb REDIS_HOST: redis REDIS_PORT: 6379 depends_on: db: condition: service_healthy # 等 db 健康检查通过再启动 redis: condition: service_started restart: unless-stopped networks: - app-network volumes: - api-logs:/app/logs # ========== 前端应用 ========== web: build: context: ./client dockerfile: Dockerfile container_name: my-web ports: - "80:3000" depends_on: - api restart: unless-stopped networks: - app-network # ========== 数据库 ========== db: image: mysql:8.0 container_name: my-db environment: MYSQL_ROOT_PASSWORD: root123 MYSQL_DATABASE: mydb MYSQL_USER: admin MYSQL_PASSWORD: admin123 ports: - "3306:3306" volumes: - db-data:/var/lib/mysql - ./db/init.sql:/docker-entrypoint-initdb.d/init.sql healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] interval: 10s timeout: 5s retries: 5 start_period: 30s restart: unless-stopped networks: - app-network # ========== 缓存 ========== redis: image: redis:7-alpine container_name: my-redis ports: - "6379:6379" volumes: - redis-data:/data command: redis-server --appendonly yes restart: unless-stopped networks: - app-network # ========== 反向代理 ========== nginx: image: nginx:alpine container_name: my-nginx ports: - "80:80" - "443:443" volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro depends_on: - web - api restart: unless-stopped networks: - app-network # ========== 网络 ========== networks: app-network: driver: bridge # ========== 数据卷 ========== volumes: db-data: redis-data: api-logs: ``` > [!warning] 服务启动顺序 > 理解这个示例的启动链:**db + redis 先启动 → api 启动(依赖它们就绪)→ web 启动(依赖 api 就绪)→ nginx 启动(依赖前端和后端)**。用 `depends_on` + `healthcheck` 确保这个顺序。 --- ### 三、常用配置详解 #### 3.1 `build` — 从 Dockerfile 构建 > [!info] build 与 image 的选择 > 开发环境通常用 `build`(代码改动自动重构建),生产环境可以用 `image`(提前打好镜像推送仓库)。 ```yaml services: # 方式一:当前目录(自动找 Dockerfile) web: build: . # 方式二:指定上下文和 Dockerfile web: build: context: ./frontend dockerfile: Dockerfile.prod # 方式三:加构建参数 + 多阶段指定目标 web: build: context: . dockerfile: Dockerfile args: NODE_ENV: production VERSION: 1.0.0 target: production # 方式四:直接用已有镜像(不构建) db: image: mysql:8.0 ``` #### 3.2 `depends_on` — 启动顺序 ```yaml services: api: depends_on: db: condition: service_healthy # 等 db 健康检查通过 redis: condition: service_started # 等 redis 启动即可 ``` > [!note] depends_on 的真相 > `depends_on` 只管**启动顺序**,不管**可用性**。不加 `healthcheck` 时,容器"已启动"不代表服务"已就绪"。数据库可能需要几秒初始化才能接受连接。 #### 3.3 `healthcheck` — 健康检查 > [!tip] 为什么数据库必须配健康检查? > 容器启动 ≠ 服务就绪。MySQL 启动后可能要跑初始化脚本,`start_period` 就是给这段缓冲时间。 ```yaml services: db: image: mysql:8.0 healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] interval: 10s # 每 10 秒检查一次 timeout: 5s # 超时时间 retries: 5 # 最多重试 5 次 start_period: 30s # 启动后等 30 秒才开始检查 ``` **常见健康检查命令速查:** | 服务 | 健康检查命令 | |------|-------------| | MySQL | `mysqladmin ping -h localhost` | | Redis | `redis-cli ping` | | Node.js | `wget -qO- http://localhost:3000/health` | | Nginx | `curl -f http://localhost/` | | PostgreSQL | `pg_isready -h localhost` | #### 3.4 `environment` — 环境变量 ```yaml # 方式一:列表格式(简洁) environment: - NODE_ENV=production - DB_HOST=db # 方式二:字典格式(推荐,更易读,支持类型) environment: NODE_ENV: production DB_HOST: db DB_PORT: 3306 # 方式三:从 .env 文件加载(最推荐) env_file: - .env.production # 混合使用 environment: NODE_ENV: production DB_HOST: db env_file: - .env ``` #### 3.5 `volumes` — 数据持久化 ```yaml volumes: # 命名卷(推荐,Docker 自动管理) - db-data:/var/lib/mysql # 绑定挂载(绑定宿主机路径,开发用) - ./data:/app/data # 只读挂载(配置文件) - ./nginx.conf:/etc/nginx/nginx.conf:ro # 匿名卷(不需要命名) - /tmp/data ``` > [!abstract] 命名卷 vs 绑定挂载 > - **命名卷**:Docker 管理存储位置,更安全,推荐生产环境用 > - **绑定挂载**:直接映射宿主机路径,开发方便但可能跨平台不兼容 #### 3.6 `restart` — 重启策略 ```yaml restart: always # 永远重启(包括手动停止后也重启) restart: on-failure # 仅失败时重启 restart: unless-stopped # 除非手动停止,否则重启(推荐) restart: no # 不自动重启(默认) ``` #### 3.7 `ports` vs `expose` ```yaml ports: - "8080:80" # 宿主端口:容器端口(外部可访问) - "127.0.0.1:8080:80" # 仅本地可访问(安全) - "3000" # 随机端口 expose: - "3000" # 仅容器间通信,外部不可访问 ``` > [!danger] 安全警告 > 数据库**不要暴露 `ports` 到宿主机**!用 `expose` 或同网络即可,避免被外部直接访问。 --- ### 四、多环境配置 > [!abstract] 多环境策略 > 核心思路:**一份基础配置 + 环境差异覆盖**,而不是为每个环境写完整文件。 #### 4.1 覆盖文件(推荐) ```yaml # docker-compose.yml(开发环境基础配置) services: api: build: . environment: NODE_ENV: development volumes: - ./server:/app # 开发热重载 ``` ```yaml # docker-compose.prod.yml(生产覆盖) services: api: environment: NODE_ENV: production volumes: [] # 生产时不挂载宿主机目录 ``` ```bash # 组合使用 docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d ``` #### 4.2 环境变量控制(简洁) ```yaml # docker-compose.yml services: api: build: . environment: NODE_ENV: ${NODE_ENV:-development} DB_HOST: ${DB_HOST:-db} volumes: - ${MOUNT_VOLUMES:-./server:/app} ``` ```bash # 开发环境(默认值) docker compose up -d # 生产环境 NODE_ENV=production MOUNT_VOLUMES="" docker compose up -d ``` --- ### 五、常用命令 | 操作 | 命令 | |------|------| | 启动全部(后台) | `docker compose up -d` | | 启动全部(前台看日志) | `docker compose up` | | 启动单个服务 | `docker compose up -d db redis` | | 停止全部 | `docker compose stop` | | 停止并删除容器 | `docker compose down` | | 删除容器 + 网络 | `docker compose down --rmi all` | | 代码改动后重建 | `docker compose up -d --build` | | 删除容器 + 数据卷 | `docker compose down -v` | | 查看服务状态 | `docker compose ps` | | 查看日志 | `docker compose logs -f` | | 进入容器 | `docker compose exec api bash` | | 单次执行命令 | `docker compose run --rm web npm test` | | 拉取新镜像 | `docker compose pull` | --- ### 六、常见问题 #### 6.1 容器间无法互相访问 ```yaml # 确保所有服务在同一网络下 services: api: networks: [app-network] db: networks: [app-network] networks: app-network: driver: bridge ``` > [!note] 默认网络 > 同一个 Compose 文件下的服务**默认在同一网络**,可以互相通过服务名访问。不同 Compose 文件的容器不能互相访问。 #### 6.2 数据库启动慢,应用连不上 ```yaml services: api: depends_on: db: condition: service_healthy # 健康检查通过才启动 ``` 或在应用代码中添加重试逻辑(指数退避)。 #### 6.3 端口冲突 ```bash # 查看端口占用 netstat -ano | findstr :3000 # Windows lsof -i :3000 # macOS/Linux ``` 修改 `docker-compose.yml` 中的端口映射: ```yaml ports: - "3001:3000" # 宿主机 3001 → 容器 3000 ``` #### 6.4 `.env` 文件 ```bash # 在项目根目录创建 .env 文件 DB_HOST=db DB_PASSWORD=root123 NODE_ENV=production COMPOSE_PROJECT_NAME=myapp ``` > [!info] 自动读取 > Docker Compose 会**自动读取**当前目录的 `.env` 文件,`$DB_HOST` 在 `docker-compose.yml` 中会被自动替换。 --- ### 七、常用模板速查 #### 7.1 纯前端静态站点(Nginx) ```yaml services: web: build: . ports: ["80:80"] restart: unless-stopped ``` #### 7.2 Node.js + PostgreSQL ```yaml services: app: build: . ports: ["3000:3000"] environment: DATABASE_URL: postgresql://user:pass@db:5432/mydb depends_on: db: condition: service_healthy db: image: postgres:16-alpine environment: POSTGRES_USER: user POSTGRES_PASSWORD: pass POSTGRES_DB: mydb volumes: - pg-data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U user"] interval: 10s volumes: pg-data: ``` #### 7.3 Next.js 全功能模板 ```yaml services: app: build: context: . dockerfile: Dockerfile target: production ports: ["3000:3000"] environment: NODE_ENV: production DATABASE_URL: $DATABASE_URL NEXTAUTH_SECRET: $NEXTAUTH_SECRET restart: unless-stopped networks: [app-network] db: image: postgres:16-alpine environment: POSTGRES_DB: myapp POSTGRES_USER: app POSTGRES_PASSWORD: $DB_PASSWORD volumes: [pg-data:/var/lib/postgresql/data] healthcheck: test: ["CMD-SHELL", "pg_isready -U app"] networks: [app-network] redis: image: redis:7-alpine volumes: [redis-data:/data] networks: [app-network] networks: app-network: volumes: pg-data: redis-data: ``` --- ### 八、Compose 文件格式演进 > [!tip] 新项目直接用新格式 > Compose V2 起默认使用 Compose Specification 格式,不再需要 `version` 字段。 ```yaml # 新版写法(推荐) services: app: image: node:20-alpine ports: - "3000:3000" environment: NODE_ENV: production networks: default: driver: bridge volumes: data: ``` | 对比项 | V3 及以下(旧) | V4+ / Compose Specification(新) | |--------|----------------|--------------------------------| | `version` 字段 | 必须写 | 不需要 | | 命令 | `docker-compose`(连字符) | `docker compose`(空格) | | 字段格式 | 点号分隔 | YAML 嵌套格式 | --- ### 九、高级配置 #### 9.1 `deploy` — 资源限制 ```yaml services: api: image: my-api:latest deploy: replicas: 3 # 副本数(Swarm 模式) restart_policy: condition: on-failure delay: 5s max_attempts: 3 window: 120s resources: limits: cpus: '0.5' # 最多 0.5 核 CPU memory: 512M # 最多 512MB 内存 reservations: cpus: '0.25' memory: 128M ``` > [!warning] 注意 > `deploy` 的 `replicas`、`update_config` 在 `docker compose up`(非 Swarm)下**不生效**。但 `resources.limits/reservations` 在普通模式下仍然有效。 #### 9.2 `profiles` — 按需启停服务 ```yaml services: web: image: nginx:alpine profiles: ["app"] db: image: postgres:16-alpine profiles: ["db", "app"] redis: image: redis:7-alpine profiles: ["cache", "app"] monitoring: image: grafana/grafana profiles: ["monitoring"] ``` ```bash # 只启动 app 相关服务(web + db + redis) docker compose --profile app up -d # 启动 app + 监控 docker compose --profile app --profile monitoring up -d # 启动所有服务(包括无 profile 的) docker compose up -d ``` #### 9.3 `configs` — 配置文件管理 ```yaml services: nginx: image: nginx:alpine configs: - source: nginx_conf target: /etc/nginx/conf.d/default.conf mode: 0444 configs: nginx_conf: file: ./configs/nginx.conf ``` #### 9.4 `secrets` — 敏感信息管理 ```yaml services: db: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_pass secrets: - db_root_pass secrets: db_root_pass: file: ./secrets/db_root_pass.txt db_config: string: '{"max_connections": 100}' ``` > [!danger] 安全建议 > 敏感信息不要写死在 `docker-compose.yml` 中,使用 `secrets` 或 `.env` 文件管理。 #### 9.5 其他实用配置 ```yaml services: app: image: node:20-alpine # 指定运行用户 user: "1000:1000" # 工作目录 working_dir: /app # 覆盖默认入口点和命令 entrypoint: ["node"] command: ["server.js"] # 容器别名 container_name: my-app # 标签(元数据) labels: - "com.example.description=API Server" # 时区 environment: - TZ=Asia/Shanghai # 挂载共享内存(深度学习场景) shm_size: '2gb' # 安全选项 security_opt: - no-new-privileges:true cap_drop: - ALL cap_add: - NET_BIND_SERVICE ``` --- ### 十、最佳实践 #### 10.1 安全性 ```yaml # ✅ 使用非 root 用户 user: "1000:1000" # ✅ 最小化权限 cap_drop: - ALL security_opt: - no-new-privileges:true # ❌ 避免 privileged # privileged: true ``` #### 10.2 可靠性 ```yaml # ✅ 外部依赖都加 healthcheck db: healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] start_period: 30s # ✅ 合理的重启策略 restart: unless-stopped # ✅ 依赖顺序用 condition depends_on: db: condition: service_healthy ``` #### 10.3 可维护性 ```yaml # ✅ 使用 .env 管理变量 # .env COMPOSE_PROJECT_NAME=myapp MYSQL_ROOT_PASSWORD=xxxxx NODE_ENV=production # ✅ 命名卷而不是绑定挂载(数据持久化) volumes: db-data: # Docker 管理,更安全 # ✅ 用 profiles 组织服务 # 开发只启动 app + db # docker compose --profile dev up -d ``` #### 10.4 性能优化 ```yaml # ✅ 生产环境使用 Alpine 镜像 image: node:20-alpine # 约 150MB # vs image: node:20 # 约 1GB # ✅ 限制资源使用 deploy: resources: limits: cpus: '0.5' memory: 512M # ✅ 数据库不要暴露到宿主机 # db: # ports: # - "3306:3306" # 删除这行,用 expose 代替 ``` --- ## 快速参考卡 | 字段 | 用途 | 示例 | |------|------|------| | `image` | 使用已有镜像 | `image: nginx:alpine` | | `build` | 从 Dockerfile 构建 | `build: ./app` | | `ports` | 端口映射(外部可访问) | `"80:80"` | | `expose` | 端口暴露(仅容器间) | `"3000"` | | `volumes` | 数据持久化 | `data:/var/lib/mysql` | | `environment` | 环境变量 | `NODE_ENV: production` | | `depends_on` | 启动依赖 | `db: condition: service_healthy` | | `healthcheck` | 健康检查 | `test: ["CMD", "curl", ...]` | | `restart` | 重启策略 | `unless-stopped` | | `networks` | 网络归属 | `app-network` | | `profiles` | 按需启停 | `["dev", "monitoring"]` | | `secrets` | 敏感信息管理 | `file: ./secrets/xxx` | | `deploy.resources` | 资源限制 | `cpus: '0.5', memory: 512M` | | `container_name` | 固定容器名 | `my-container` | --- ## 关联笔记 - [[Dockerfile编写指南]] — 编写高效的 Dockerfile - [[Docker常用命令]] — 日常 Docker 操作速查