Docker Compose 详尽笔记
目录
- Compose 基本概念与版本说明
- 文件结构与常用字段详解
- 网络(Networks)全面解析
- 宿主机与容器之间的网络交互(host、端口映射、macvlan 等)
- 数据持久化:Volumes 与 bind mount
- 环境与配置:环境变量、
.env、env_file、configs、secrets - 服务依赖、健康检查、重启策略与资源限制
- Compose 多环境/覆盖/扩展(override、profiles、多个文件)
- 常用命令与调试技巧
- 典型示例(含完整
docker-compose.yml) - 常见问题与排查思路
- 最佳实践简要清单
1. Compose 基本概念与版本说明
docker compose(或历史上的docker-compose)是用于定义和运行多容器 Docker 应用的工具。Compose 文件使用 YAML 来描述服务、网络、卷、配置等。- Compose 规格(Compose specification):现在推荐使用 Compose 规范(无需显式
version:字段),但很多项目仍使用version: '3.x'或version: '2.4'。v2和v3系列有一些差异(例如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:构建镜像相关(可以是路径或包含context、dockerfile、args的对象)。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: host,ports设置会被忽略(容器直接使用宿主端口)。
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:顶层可以声明命名卷并配置driver与driver_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:定义容器如何自检(test、interval、timeout、retries、start_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 -tlnp或docker compose ps检查。 - 权限问题(卷挂载):确保宿主目录权限/UID/GID 与容器内用户匹配;必要时在 Dockerfile 里调整文件权限。
- DNS 解析失败(服务名解析不到):确认服务在同一自定义网络,使用
docker compose exec svc ping其它服务名测试。 - 镜像未更新:
docker compose pull或docker compose build --no-cache,并重启服务。
12. 最佳实践简要清单
- 尽量把敏感信息放到 secrets 或外部密钥管理系统,而不是
.env。 - 开发环境使用 bind mount,生产使用命名卷或构建好的镜像。
- 将服务按网络职责分离(frontend/backend)并使用最小暴露的端口。
- 使用
docker compose config验证合并后的配置。 - 在服务里实现重试/退避策略以应对依赖不可用问题。
- 尽量保持
docker-compose.yml简洁,把复杂的配置放到单独的覆盖文件(docker-compose.prod.yml)。