Docker Compose 详尽笔记


目录

  1. Compose 基本概念与版本说明
  2. 文件结构与常用字段详解
  3. 网络(Networks)全面解析
  4. 宿主机与容器之间的网络交互(host、端口映射、macvlan 等)
  5. 数据持久化:Volumes 与 bind mount
  6. 环境与配置:环境变量、.env、env_file、configs、secrets
  7. 服务依赖、健康检查、重启策略与资源限制
  8. Compose 多环境/覆盖/扩展(override、profiles、多个文件)
  9. 常用命令与调试技巧
  10. 典型示例(含完整 docker-compose.yml
  11. 常见问题与排查思路
  12. 最佳实践简要清单

1. Compose 基本概念与版本说明

  • docker compose(或历史上的 docker-compose)是用于定义和运行多容器 Docker 应用的工具。Compose 文件使用 YAML 来描述服务、网络、卷、配置等。
  • Compose 规格(Compose specification):现在推荐使用 Compose 规范(无需显式 version: 字段),但很多项目仍使用 version: '3.x'version: '2.4'v2v3 系列有一些差异(例如 deploy 在 v3 里用于 swarm 模式)。
  • 实践建议:如果不使用 Swarm 的 deploy 特性,优先用现代 Compose 语法并依赖 docker compose(Docker CLI 插件),不要过分依赖旧工具。

2. 文件结构与常用字段详解

基础骨架:

services:
  web:
    image: nginx:stable
    ports:
      - "8080:80"
    volumes:
      - web_data:/var/www/html
    environment:
      - ENV=prod

volumes:
  web_data:

networks:
  frontend:
    driver: bridge

常用字段(服务级)

  • image:使用已有镜像名(带 tag 可指定版本)。
  • build:构建镜像相关(可以是路径或包含 contextdockerfileargs 的对象)。

    build:
      context: ./app
      dockerfile: Dockerfile
      args:
        NODE_ENV: production
    
  • ports:宿主机端口:容器端口映射,支持 "8080:80" 或短语法 - 8080:80
  • expose:仅声明容器内部监听端口(不映射到宿主机),供同一网络中的其他容器访问。
  • environment / env_file:设置环境变量或载入环境变量文件。
  • volumes:绑定卷或绑定目录(bind mount)。
  • networks:指定服务加入的网络及网络别名。
  • depends_on:声明服务启动顺序(只影响启动顺序,不会等待健康检查,详见第7节)。
  • healthcheck:定义容器健康检查(用于 depends_on 的 condition 或手动判断)。
  • restart:重启策略(no, on-failure, always, unless-stopped)。
  • command / entrypoint:覆盖镜像默认的命令或入口点。
  • logging:指定日志驱动及选项。
  • deploy:与 Swarm 或集群相关(在普通单机 Compose 中通常被忽略)。

顶层字段

  • volumes::声明命名卷并可指定驱动及 driver_opts。
  • networks::声明自定义网络、驱动(bridge, overlay, macvlan 等)与配置。
  • configs:secrets::用于将机密或配置注入容器(尤其在 Swarm 或 Docker Compose v3+ 更常见)。

3. 网络(Networks)全面解析

网络模型概念

  • Docker 网络提供容器之间及容器与外界的通信路径。Compose 默认会为项目创建一个桥接网络(bridge),并将所有服务连接到该网络,服务可以通过服务名互相访问。
  • 每个 compose 项目默认网络名为 项目名_default(可以在 docker compose ps 中看到)。

常见 driver 类型

  • bridge(默认):容器获得私有子网 IP,使用 NAT 与宿主通信。适合大多数场景。
  • host:容器使用宿主机网络命名空间(无隔离)。Linux 下可用;性能最好,但会有端口冲突与安全隔离问题。
  • overlay:用于 swarm 集群跨主机通信(非单机);通常搭配 Swarm 使用。
  • macvlan:把容器直接放到物理網段,容器获得与宿主相同网段的独立 MAC/IP,可被局域网其他设备直连。用于需要 LAN 可见性或与外部网络设备直接通信的场景,但配置复杂且对宿主机和网络有要求。

自定义 bridge 网络优点

  • 同一自定义网络内,容器可以通过服务名 DNS 解析访问彼此(例如 http://db:5432)。
  • 自定义网络允许指定子网、网关、IP 范围、驱动选项与别名。

networks 字段示例

networks:
  frontend:
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 172.28.0.0/16
          gateway: 172.28.0.1
  backend:
    driver: bridge

在服务中引用:

services:
  web:
    networks:
      - frontend
  db:
    networks:
      - backend
      - frontend
    aliases:
      - mydb

网络别名与固定 IP

  • aliases:在网络内为容器设置一个或多个别名。
  • 也可以通过 ipv4_address 在自定义网段里给容器静态 IP(要确保 IP 在 ipam 配置范围内)。
services:
  app:
    networks:
      customnet:
        ipv4_address: 172.28.0.10

networks:
  customnet:
    ipam:
      config:
        - subnet: 172.28.0.0/16

4. 宿主机与容器之间的网络交互

端口映射(ports

  • ports: 将宿主机端口映射到容器端口(HOST:CONTAINER)。如果只写容器端口(- "80"),Docker 会随机分配宿主端口。
  • 典型用例:- "8080:80" 将宿主机 8080 映射到容器 80。
  • 注意:若使用 network_mode: hostports 设置会被忽略(容器直接使用宿主端口)。

host 网络模式(network_mode: host

  • 在 Linux 系统下,设置 network_mode: host 会让容器共享宿主机网络命名空间:容器内服务直接绑定到宿主机的 IP 和端口。
  • 优点:降低网络开销、获得宿主机网络上的原生性能与可见性。
  • 缺点:失去网络命名空间隔离 => 容器与宿主、其他容器容易冲突(端口冲突);不可在 macOS/Windows 上完全等效使用。

示例:

services:
  prometheus:
    image: prom/prometheus
    network_mode: host

macvlan(让容器像物理机一样在 LAN 中出现)

  • macvlan 将容器暴露到物理网络,获取物理网络可识别的 IP/MAC。常用于需要被 LAN 中其他设备直接访问的场景(如某些网络服务或需要硬件隔离的场景)。
  • 配置复杂,需要宿主机网络接口支持并正确配置。宿主与容器默认情况下无法直接通信(有办法通过子接口桥接实现)。

示例(networks):

networks:
  my_macvlan:
    driver: macvlan
    driver_opts:
      parent: eth0
    ipam:
      config:
        - subnet: 192.168.1.0/24
          gateway: 192.168.1.1

宿主机访问容器

  • 当容器使用桥接网络并通过 ports 暴露端口时,宿主机可通过 localhost:HOSTPORT 或宿主机 IP:HOSTPORT 访问。
  • 如果使用自定义 bridge 网络但未映射端口,宿主机默认不能直接通过 localhost 访问容器服务(宿主机可以直接访问容器的内部 IP,但这不推荐作为稳定方案)。
  • macOS/Windows 的 Docker Desktop 在实现上会进行额外的网络转发,细节与 Linux 不同:在这些平台上 network_mode: host 的表现有限制。

5. 数据持久化:Volumes 与 bind mount

  • 命名卷(named volumes):由 Docker 管理,易于在不同容器间共享与迁移(例如 db_data:/var/lib/postgresql/data)。
  • bind mount(绑定挂载):把宿主目录直接挂到容器(例如 - ./app:/usr/src/app),常用于开发时代码热加载。
  • volumes: 顶层可以声明命名卷并配置 driverdriver_opts

示例:

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

volumes:
  db_data:
    driver: local

注意:权限问题(UID/GID)是常见误区。宿主与容器的文件权限映射可能导致读写受限,常用解决办法包括在 Dockerfile 中指定 chown,或在宿主上调整目录权限(谨慎)。

6. 环境与配置:环境变量、.env、env_file、configs、secrets

  • .env 文件:放在 Compose 文件同目录,Compose 会自动读取其中的变量并可在 docker-compose.yml 中以 ${VAR} 用法引用。常用来存放非敏感配置(不要放密码)。
  • env_file:在服务中指定一个或多个 env 文件,文件内容会注入到容器环境中(适合在不同服务间共享变量)。
  • configs / secrets:用于更安全地管理配置与敏感信息(例如证书、私钥、数据库密码),在 Swarm 中更完整,但本地 Compose 也支持通过 secrets: 注入(但注意权限与存储安全)。

示例:

services:
  web:
    env_file:
      - ./web.env
    environment:
      - DEBUG=${DEBUG}

7. 服务依赖、健康检查、重启策略与资源限制

  • depends_on:控制 docker compose up 时的启动顺序,但默认不等待依赖服务“就绪”。
    • 示例: depends_on: [db, redis]
    • 若需要真正等待健康检查通过,可使用 depends_on 的 condition(仅在某些 Compose 版本支持)或在启动脚本里轮询依赖服务健康。
  • healthcheck:定义容器如何自检(testintervaltimeoutretriesstart_period)。

    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:80/ || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3
    
  • restart 策略no/always/unless-stopped/on-failure
  • 资源限制(更早在 v2 支持 mem_limit 等,现代做法推荐使用 deploy.resources 在 Swarm 中或直接通过 Docker CLI 在容器运行时限制)。Compose 在单机上对 deploy 的支持有限,注意区别。

8. Compose 多环境/覆盖/扩展

  • 支持通过多个 Compose 文件叠加覆盖配置:例如 docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
  • docker-compose.override.yml:默认会被 docker compose up 自动合并,适合放开发时的扩展(如绑定源码目录、启用调试)。
  • profiles:用于选择性启用服务(例如 profiles: ["debug"]),使用 docker compose --profile debug up 来启用。
  • YAML anchors/aliases:用 &* 来复用配置片段,便于避免重复(例如统一 environment 或 labels)。

9. 常用命令与调试技巧

  • docker compose up -d:后台启动并创建所有服务。
  • docker compose down:停止并移除容器、网络、默认卷(除非声明为 external)。
  • docker compose stop / start:停止或启动现存容器(不删除)。
  • docker compose ps:列出 compose 项目内容器。
  • docker compose logs -f service:跟随日志。
  • docker compose exec service sh(或 bash):进入正在运行的容器进行调试。
  • docker compose build --no-cache:重新构建镜像(不使用缓存)。
  • docker compose config:查看合并后的最终配置(验证变量替换、合并效果)。
  • docker compose pull:拉取镜像以避免启动时再下载。

调试技巧:

  • 使用 docker compose config 检查语法与变量替换。
  • 若网络连通异常,使用 docker compose exec svc ping other_svc 来测试 DNS 解析与连通性。
  • 使用 docker inspect 查看容器网络配置、IP、挂载点等详细信息。

10. 典型示例(多个场景,带注释)

示例 A:简单 Web + Postgres(开发用)

# docker-compose.yml
services:
  web:
    build: ./web
    ports:
      - "8080:80"
    volumes:
      - ./web:/usr/src/app
    env_file:
      - .env
    depends_on:
      - db
    networks:
      - appnet

  db:
    image: postgres:15
    environment:
      POSTGRES_PASSWORD: example
    volumes:
      - db_data:/var/lib/postgresql/data
    networks:
      - appnet

volumes:
  db_data:

networks:
  appnet:
    driver: bridge

要点:开发时使用 bind mount 使代码变动即时生效;使用 depends_on 保证 db 启动顺序,但在应用里应有重试逻辑直至 DB 可用。


示例 B:生产就绪(Nginx 反向代理 + app + Redis,使用命名卷和自定义网络)

services:
  proxy:
    image: nginx:stable
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d:ro
      - certs:/etc/ssl/certs
    depends_on:
      - app
    networks:
      - frontend

  app:
    image: myorg/app:1.2.3
    deploy:
      replicas: 2
    environment:
      - DATABASE_URL=postgres://user:pass@db:5432/appdb
    networks:
      - frontend
      - backend

  redis:
    image: redis:7
    volumes:
      - redis_data:/data
    networks:
      - backend

  db:
    image: postgres:15
    volumes:
      - pg_data:/var/lib/postgresql/data
    networks:
      - backend

volumes:
  pg_data:
  redis_data:
  certs:

networks:
  frontend:
  backend:

要点:将外网(frontend)与内部(backend)分离;反向代理仅连 frontend。生产环境应使用外部托管的证书管理、监控与日志收集。


示例 C:使用 macvlan(使容器出现在 LAN)

services:
  iot_gateway:
    image: my-iot:latest
    networks:
      lan:
        ipv4_address: 192.168.1.50

networks:
  lan:
    driver: macvlan
    driver_opts:
      parent: eth0
    ipam:
      config:
        - subnet: 192.168.1.0/24
          gateway: 192.168.1.1

要点:macvlan 需谨慎使用,注意网络策略与宿主机与容器直接通信的问题。

11. 常见问题与排查思路

  • 服务找不到依赖(Connection refused):检查 depends_on 是否只是控制启动顺序;使用 healthcheck 与重试逻辑或在应用中重试 DB 连接。
  • 端口冲突:宿主机上已有服务占用某端口;使用 ss -tlnpdocker compose ps 检查。
  • 权限问题(卷挂载):确保宿主目录权限/UID/GID 与容器内用户匹配;必要时在 Dockerfile 里调整文件权限。
  • DNS 解析失败(服务名解析不到):确认服务在同一自定义网络,使用 docker compose exec svc ping其它服务名 测试。
  • 镜像未更新docker compose pulldocker compose build --no-cache,并重启服务。

12. 最佳实践简要清单

  • 尽量把敏感信息放到 secrets 或外部密钥管理系统,而不是 .env
  • 开发环境使用 bind mount,生产使用命名卷或构建好的镜像。
  • 将服务按网络职责分离(frontend/backend)并使用最小暴露的端口。
  • 使用 docker compose config 验证合并后的配置。
  • 在服务里实现重试/退避策略以应对依赖不可用问题。
  • 尽量保持 docker-compose.yml 简洁,把复杂的配置放到单独的覆盖文件(docker-compose.prod.yml)。