IT策士 10余年一线大厂经验专注 IT 思维、架构、职场进阶。我会在各个平台持续发布最新文章助你少走弯路。在第 11 篇中我们第一次接触了docker-compose.yml体验了从“手动敲 8 条命令”到“一条docker compose up -d启动整个应用栈”的效率跃升。但当时我们只是“照葫芦画瓢”地写了一份配置文件——对每一行配置的含义、最佳实践和灵活用法还没有深入展开。今天我们就来彻底拆解Compose 文件把 services、networks、volumes 这三大核心模块吃透。掌握了这些当你后面进入 Kubernetes 看到 Service、Ingress、ConfigMap、PersistentVolumeClaim 时会发现它们的设计思路一脉相承——都是从“手动管理资源”到“声明式管理资源”的进化。一、Compose 文件结构总览一个完整的 Compose 文件由三个顶级元素构成services:# 定义应用服务... networks:# 定义网络... volumes:# 定义数据卷...此外还有configs和secrets用于 Swarm 模式本篇暂不涉及。我们重点解析前三个。1.1 项目名称与资源命名Compose 管理的所有资源容器、网络、卷都会自动加上项目名称作为前缀避免不同项目之间命名冲突。默认的项目名称是docker-compose.yml所在目录的名称。例如目录名为flask-redis-counter那么网络app-net实际创建为flask-redis-counter_app-net卷redis-data实际创建为flask-redis-counter_redis-data容器redis实际名称为redis因为显式指定了container_name如果未指定则为项目名-服务名-序号你可以通过-p参数或COMPOSE_PROJECT_NAME环境变量自定义项目名称dockercompose-pmyproject up-d二、services 详解服务的核心配置services是 Compose 文件中最核心的模块每个服务可以配置的选项非常多。我将按功能分类逐一拆解并用我们贯穿的 Flask Redis 应用来演示。2.1 镜像与构建指定服务使用的镜像有两种方式直接使用已有镜像services: redis: image: redis:alpine# 从仓库拉取从 Dockerfile 构建services: flask-app: build:.# 使用当前目录的 Dockerfile# 或者更详细的写法build: context: ./app# 构建上下文路径dockerfile: Dockerfile.dev# 指定 Dockerfile 文件名args:# 构建参数-ENVproduction如果同时指定了build和imageCompose 会用image的值作为构建后的镜像名称相当于自动执行docker build -t image .services: flask-app: build:.image: flask-redis-counter:latest2.2 容器名称与重启策略services: flask-app: container_name: flask-app# 固定容器名不指定则自动生成restart: unless-stopped# 重启策略重启策略与docker run --restart一致可选值no、always、on-failure、unless-stopped。注意如果计划使用--scale扩容不要指定固定的container_name因为每个副本必须有不同的名称。扩容场景下应让 Compose 自动生成名称。2.3 端口映射services: flask-app: ports: -5000:5000# 宿主机端口:容器端口-5001:5000# 可以映射多个端口-127.0.0.1:5002:5000# 只绑定到宿主机特定 IP等价于docker run -p 5000:5000。2.4 环境变量services: flask-app: environment: -FLASK_ENVproduction -REDIS_HOSTredis -DB_PASSWORDsecret123# ⚠️ 不要在 YAML 中硬编码敏感信息# 或从文件加载env_file: - .env直接写在 YAML 中的环境变量会被提交到 Git 仓库敏感信息密码、API 密钥不应硬编码。更好的方式是使用.env文件或 Docker SecretsSwarm 模式这将在第 13 篇和第 33 篇中专门展开。2.5 数据卷挂载services: flask-app: volumes:# 命名卷挂载- flask-logs:/app/logs# Bind Mount宿主机绝对路径- /home/user/config:/app/config:ro# 相对路径 Bind Mount相对于 Compose 文件所在目录- ./data:/app/data# 匿名卷- /app/tmp第一行对应docker run -v flask-logs:/app/logs命名卷生产环境推荐第二行对应docker run -v /home/user/config:/app/config:roBind Mount开发环境常用第三行是相对路径 Bind Mount./data相对于 docker-compose.yml 所在目录第四行只指定容器内路径Docker 自动创建匿名卷挂载目录的注意事项当使用 Bind Mount 时宿主机目录会完全覆盖容器内对应路径。如果宿主机目录为空容器内原来镜像中的文件会被“隐藏”。这常导致“挂载后服务启动失败”的困惑——比如挂载了一个空的./config到容器的/etc/nginx/conf.dNginx 就找不到任何配置文件了。2.6 网络配置services: flask-app: networks: - app-net - monitor-net# 可以连接多个网络# 也可以为每个网络指定别名networks: app-net: aliases: - web - api网络别名aliases让其他容器可以通过多个名称访问这个服务等价于docker run --network-alias。2.7 启动命令与入口点services: redis: command: redis-server--appendonlyyes--maxmemory256mb# 或者用数组格式推荐command:[redis-server,--appendonly,yes]command会覆盖镜像默认的CMD但不覆盖ENTRYPOINT。2.8 健康检查services: redis: healthcheck: test:[CMD,redis-cli,ping]interval: 10s timeout: 3s retries:3start_period: 5s等价于 Dockerfile 中的HEALTHCHECK指令但 Compose 中定义可以覆盖镜像内置的检查规则。2.9 服务依赖与启动顺序services: flask-app: depends_on: redis: condition: service_healthy# 等待 Redis 健康检查通过db: condition: service_started# 仅等待 db 容器启动不等服务就绪三种条件service_started仅等待容器启动service_healthy等待健康检查通过推荐service_completed_successfully等待一次性任务完成如数据库迁移退出码为 0depends_on只控制启动顺序不保证依赖服务完全就绪。这就是为什么condition: service_healthy如此重要——它把“启动顺序”和“服务就绪”绑定在了一起。2.10 资源限制Compose v2services: flask-app: deploy: resources: limits: cpus:0.50# 最多使用 50% 单核 CPUmemory: 256M# 内存上限reservations: cpus:0.25# 至少保留 25% 单核 CPUmemory: 128M# 至少保留内存注意deploy.resources在docker compose up中会被忽略仅在 Swarm 模式docker stack deploy下生效。单机 Compose 场景下如需限制资源请使用docker run的--cpus和--memory参数或者在 Compose 文件中使用已弃用但依然有效的 V2 格式的mem_limit和cpus不推荐兼容性差。在实际生产环境中资源限制应交给 Kubernetes 的 ResourceQuota 和 LimitRange 管理。2.11 完整示例Flask Redis 的 services 配置综合以上知识我们把 Flask Redis 应用的 services 部分打磨得更完善services: redis: image: redis:alpine restart: unless-stopped command: redis-server--appendonlyyes--maxmemory256mb --maxmemory-policy allkeys-lru volumes: - redis-data:/data networks: - app-net healthcheck: test:[CMD,redis-cli,ping]interval: 10s timeout: 3s retries:3start_period: 5s flask-app: image: flask-redis-counter:2.0 restart: unless-stopped ports: -5000:5000environment: -FLASK_ENVproduction volumes: - flask-logs:/app/logs networks: - app-net depends_on: redis: condition: service_healthy healthcheck: test:[CMD,curl,-f,http://localhost:5000/health]interval: 30s timeout: 3s retries:3start_period: 5s三、networks 详解自定义网络配置Compose 默认会为每个项目创建一个默认网络项目名_default所有未显式声明networks的服务都会连接到这个默认网络。但我们强烈建议显式声明自定义网络以获得更好的控制和隔离性。networks: app-net: driver: bridge# 默认驱动单机桥接网络frontend: driver: bridge backend: driver: bridge3.1 自定义子网和网关networks: app-net: driver: bridge ipam: config: - subnet:172.25.0.0/16 gateway:172.25.0.1这在你需要固定 IP 段、与外部系统对接时非常有用。3.2 外部网络有时候你希望容器连接到一个已经存在的 Docker 网络由其他项目或手动创建而非由 Compose 自动创建networks: existing-net: external:true# 表示该网络已在外部创建然后在服务中引用services: flask-app: networks: - existing-netexternal: true告诉 Compose“不要创建新网络去连接那个已经存在的网络”。如果网络不存在docker compose up会报错。3.3 网络别名在同一网络中可以通过aliases为服务定义额外的 DNS 名称services: redis: networks: app-net: aliases: - cache - redis-cache现在其他容器可以通过cache或redis-cache来解析到这个 Redis 容器实现了简单的“服务发现别名”。在 K8s 中这对应着 Service 的name和spec.clusterIP的 DNS A 记录。四、volumes 详解持久化存储配置volumes: redis-data:# 命名卷Docker 自动管理存储路径flask-logs:4.1 卷驱动默认使用local驱动存储在宿主机本地磁盘。也可以使用第三方驱动如 NFS、AWS EBSvolumes: nfs-data: driver:localdriver_opts: type: nfs o:addr192.168.1.100,nolock,soft,rw device::/export/data4.2 外部卷和外部网络类似可以使用已有的卷volumes: existing-data: external:true这对于数据迁移或跨项目共享数据卷很有用。五、完整的 docker-compose.yml 最终版综合所有知识我们为 Flask Redis 计数器应用编写一份生产就绪的 Compose 文件# # Flask Redis 计数器应用 —— Docker Compose 生产配置# 系列贯穿案例 v3.0# services: redis: image: redis:alpine restart: unless-stopped command: redis-server--appendonlyyes--maxmemory256mb --maxmemory-policy allkeys-lru volumes: - redis-data:/data networks: app-net: aliases: - cache healthcheck: test:[CMD,redis-cli,ping]interval: 10s timeout: 3s retries:3start_period: 5s flask-app: image: flask-redis-counter:2.0 restart: unless-stopped ports: -5000:5000environment: -FLASK_ENVproduction -REDIS_HOSTcache# 使用网络别名volumes: - flask-logs:/app/logs networks: - app-net depends_on: redis: condition: service_healthy healthcheck: test:[CMD,curl,-f,http://localhost:5000/health]interval: 30s timeout: 3s retries:3start_period: 5s volumes: redis-data: flask-logs: networks: app-net: driver: bridge ipam: config: - subnet:172.25.0.0/165.1 启动与验证# 启动dockercompose up-d# 查看资源dockercomposepsdockernetworkls|grepapp-netdockervolumels|grepflask-redis-counter# 测试别名dockercomposeexecflask-appnslookupcache六、命令速查表七、本篇总结services每个服务涵盖镜像、端口、环境变量、卷挂载、网络、健康检查、依赖等全套配置任何一个docker run参数在 Compose 中都有对应的声明式写法。networks自定义网络提供了 DNS 解析和网络隔离aliases实现服务别名external接入已存在的网络。volumes命名卷是生产持久化的首选外部卷和第三方驱动扩展了存储能力。演进视角你现在看到的services、networks、volumes在 Kubernetes 中会对应到Deployment、Service、PersistentVolumeClaim。Compose 是单机上的声明式编排而 K8s 是集群级的声明式编排——核心理念完全一致只是规模和抽象层次不同。下一篇——第 13 篇Compose 环境变量与配置管理我们将深入探讨如何优雅地管理多环境配置告别硬编码让同一份 Compose 文件在开发、测试、生产环境中灵活切换。想了解更多还可以去各个平台搜索「IT策士」一起升级 IT 思维
Docker Compose 文件详解:服务、网络与卷
IT策士 10余年一线大厂经验专注 IT 思维、架构、职场进阶。我会在各个平台持续发布最新文章助你少走弯路。在第 11 篇中我们第一次接触了docker-compose.yml体验了从“手动敲 8 条命令”到“一条docker compose up -d启动整个应用栈”的效率跃升。但当时我们只是“照葫芦画瓢”地写了一份配置文件——对每一行配置的含义、最佳实践和灵活用法还没有深入展开。今天我们就来彻底拆解Compose 文件把 services、networks、volumes 这三大核心模块吃透。掌握了这些当你后面进入 Kubernetes 看到 Service、Ingress、ConfigMap、PersistentVolumeClaim 时会发现它们的设计思路一脉相承——都是从“手动管理资源”到“声明式管理资源”的进化。一、Compose 文件结构总览一个完整的 Compose 文件由三个顶级元素构成services:# 定义应用服务... networks:# 定义网络... volumes:# 定义数据卷...此外还有configs和secrets用于 Swarm 模式本篇暂不涉及。我们重点解析前三个。1.1 项目名称与资源命名Compose 管理的所有资源容器、网络、卷都会自动加上项目名称作为前缀避免不同项目之间命名冲突。默认的项目名称是docker-compose.yml所在目录的名称。例如目录名为flask-redis-counter那么网络app-net实际创建为flask-redis-counter_app-net卷redis-data实际创建为flask-redis-counter_redis-data容器redis实际名称为redis因为显式指定了container_name如果未指定则为项目名-服务名-序号你可以通过-p参数或COMPOSE_PROJECT_NAME环境变量自定义项目名称dockercompose-pmyproject up-d二、services 详解服务的核心配置services是 Compose 文件中最核心的模块每个服务可以配置的选项非常多。我将按功能分类逐一拆解并用我们贯穿的 Flask Redis 应用来演示。2.1 镜像与构建指定服务使用的镜像有两种方式直接使用已有镜像services: redis: image: redis:alpine# 从仓库拉取从 Dockerfile 构建services: flask-app: build:.# 使用当前目录的 Dockerfile# 或者更详细的写法build: context: ./app# 构建上下文路径dockerfile: Dockerfile.dev# 指定 Dockerfile 文件名args:# 构建参数-ENVproduction如果同时指定了build和imageCompose 会用image的值作为构建后的镜像名称相当于自动执行docker build -t image .services: flask-app: build:.image: flask-redis-counter:latest2.2 容器名称与重启策略services: flask-app: container_name: flask-app# 固定容器名不指定则自动生成restart: unless-stopped# 重启策略重启策略与docker run --restart一致可选值no、always、on-failure、unless-stopped。注意如果计划使用--scale扩容不要指定固定的container_name因为每个副本必须有不同的名称。扩容场景下应让 Compose 自动生成名称。2.3 端口映射services: flask-app: ports: -5000:5000# 宿主机端口:容器端口-5001:5000# 可以映射多个端口-127.0.0.1:5002:5000# 只绑定到宿主机特定 IP等价于docker run -p 5000:5000。2.4 环境变量services: flask-app: environment: -FLASK_ENVproduction -REDIS_HOSTredis -DB_PASSWORDsecret123# ⚠️ 不要在 YAML 中硬编码敏感信息# 或从文件加载env_file: - .env直接写在 YAML 中的环境变量会被提交到 Git 仓库敏感信息密码、API 密钥不应硬编码。更好的方式是使用.env文件或 Docker SecretsSwarm 模式这将在第 13 篇和第 33 篇中专门展开。2.5 数据卷挂载services: flask-app: volumes:# 命名卷挂载- flask-logs:/app/logs# Bind Mount宿主机绝对路径- /home/user/config:/app/config:ro# 相对路径 Bind Mount相对于 Compose 文件所在目录- ./data:/app/data# 匿名卷- /app/tmp第一行对应docker run -v flask-logs:/app/logs命名卷生产环境推荐第二行对应docker run -v /home/user/config:/app/config:roBind Mount开发环境常用第三行是相对路径 Bind Mount./data相对于 docker-compose.yml 所在目录第四行只指定容器内路径Docker 自动创建匿名卷挂载目录的注意事项当使用 Bind Mount 时宿主机目录会完全覆盖容器内对应路径。如果宿主机目录为空容器内原来镜像中的文件会被“隐藏”。这常导致“挂载后服务启动失败”的困惑——比如挂载了一个空的./config到容器的/etc/nginx/conf.dNginx 就找不到任何配置文件了。2.6 网络配置services: flask-app: networks: - app-net - monitor-net# 可以连接多个网络# 也可以为每个网络指定别名networks: app-net: aliases: - web - api网络别名aliases让其他容器可以通过多个名称访问这个服务等价于docker run --network-alias。2.7 启动命令与入口点services: redis: command: redis-server--appendonlyyes--maxmemory256mb# 或者用数组格式推荐command:[redis-server,--appendonly,yes]command会覆盖镜像默认的CMD但不覆盖ENTRYPOINT。2.8 健康检查services: redis: healthcheck: test:[CMD,redis-cli,ping]interval: 10s timeout: 3s retries:3start_period: 5s等价于 Dockerfile 中的HEALTHCHECK指令但 Compose 中定义可以覆盖镜像内置的检查规则。2.9 服务依赖与启动顺序services: flask-app: depends_on: redis: condition: service_healthy# 等待 Redis 健康检查通过db: condition: service_started# 仅等待 db 容器启动不等服务就绪三种条件service_started仅等待容器启动service_healthy等待健康检查通过推荐service_completed_successfully等待一次性任务完成如数据库迁移退出码为 0depends_on只控制启动顺序不保证依赖服务完全就绪。这就是为什么condition: service_healthy如此重要——它把“启动顺序”和“服务就绪”绑定在了一起。2.10 资源限制Compose v2services: flask-app: deploy: resources: limits: cpus:0.50# 最多使用 50% 单核 CPUmemory: 256M# 内存上限reservations: cpus:0.25# 至少保留 25% 单核 CPUmemory: 128M# 至少保留内存注意deploy.resources在docker compose up中会被忽略仅在 Swarm 模式docker stack deploy下生效。单机 Compose 场景下如需限制资源请使用docker run的--cpus和--memory参数或者在 Compose 文件中使用已弃用但依然有效的 V2 格式的mem_limit和cpus不推荐兼容性差。在实际生产环境中资源限制应交给 Kubernetes 的 ResourceQuota 和 LimitRange 管理。2.11 完整示例Flask Redis 的 services 配置综合以上知识我们把 Flask Redis 应用的 services 部分打磨得更完善services: redis: image: redis:alpine restart: unless-stopped command: redis-server--appendonlyyes--maxmemory256mb --maxmemory-policy allkeys-lru volumes: - redis-data:/data networks: - app-net healthcheck: test:[CMD,redis-cli,ping]interval: 10s timeout: 3s retries:3start_period: 5s flask-app: image: flask-redis-counter:2.0 restart: unless-stopped ports: -5000:5000environment: -FLASK_ENVproduction volumes: - flask-logs:/app/logs networks: - app-net depends_on: redis: condition: service_healthy healthcheck: test:[CMD,curl,-f,http://localhost:5000/health]interval: 30s timeout: 3s retries:3start_period: 5s三、networks 详解自定义网络配置Compose 默认会为每个项目创建一个默认网络项目名_default所有未显式声明networks的服务都会连接到这个默认网络。但我们强烈建议显式声明自定义网络以获得更好的控制和隔离性。networks: app-net: driver: bridge# 默认驱动单机桥接网络frontend: driver: bridge backend: driver: bridge3.1 自定义子网和网关networks: app-net: driver: bridge ipam: config: - subnet:172.25.0.0/16 gateway:172.25.0.1这在你需要固定 IP 段、与外部系统对接时非常有用。3.2 外部网络有时候你希望容器连接到一个已经存在的 Docker 网络由其他项目或手动创建而非由 Compose 自动创建networks: existing-net: external:true# 表示该网络已在外部创建然后在服务中引用services: flask-app: networks: - existing-netexternal: true告诉 Compose“不要创建新网络去连接那个已经存在的网络”。如果网络不存在docker compose up会报错。3.3 网络别名在同一网络中可以通过aliases为服务定义额外的 DNS 名称services: redis: networks: app-net: aliases: - cache - redis-cache现在其他容器可以通过cache或redis-cache来解析到这个 Redis 容器实现了简单的“服务发现别名”。在 K8s 中这对应着 Service 的name和spec.clusterIP的 DNS A 记录。四、volumes 详解持久化存储配置volumes: redis-data:# 命名卷Docker 自动管理存储路径flask-logs:4.1 卷驱动默认使用local驱动存储在宿主机本地磁盘。也可以使用第三方驱动如 NFS、AWS EBSvolumes: nfs-data: driver:localdriver_opts: type: nfs o:addr192.168.1.100,nolock,soft,rw device::/export/data4.2 外部卷和外部网络类似可以使用已有的卷volumes: existing-data: external:true这对于数据迁移或跨项目共享数据卷很有用。五、完整的 docker-compose.yml 最终版综合所有知识我们为 Flask Redis 计数器应用编写一份生产就绪的 Compose 文件# # Flask Redis 计数器应用 —— Docker Compose 生产配置# 系列贯穿案例 v3.0# services: redis: image: redis:alpine restart: unless-stopped command: redis-server--appendonlyyes--maxmemory256mb --maxmemory-policy allkeys-lru volumes: - redis-data:/data networks: app-net: aliases: - cache healthcheck: test:[CMD,redis-cli,ping]interval: 10s timeout: 3s retries:3start_period: 5s flask-app: image: flask-redis-counter:2.0 restart: unless-stopped ports: -5000:5000environment: -FLASK_ENVproduction -REDIS_HOSTcache# 使用网络别名volumes: - flask-logs:/app/logs networks: - app-net depends_on: redis: condition: service_healthy healthcheck: test:[CMD,curl,-f,http://localhost:5000/health]interval: 30s timeout: 3s retries:3start_period: 5s volumes: redis-data: flask-logs: networks: app-net: driver: bridge ipam: config: - subnet:172.25.0.0/165.1 启动与验证# 启动dockercompose up-d# 查看资源dockercomposepsdockernetworkls|grepapp-netdockervolumels|grepflask-redis-counter# 测试别名dockercomposeexecflask-appnslookupcache六、命令速查表七、本篇总结services每个服务涵盖镜像、端口、环境变量、卷挂载、网络、健康检查、依赖等全套配置任何一个docker run参数在 Compose 中都有对应的声明式写法。networks自定义网络提供了 DNS 解析和网络隔离aliases实现服务别名external接入已存在的网络。volumes命名卷是生产持久化的首选外部卷和第三方驱动扩展了存储能力。演进视角你现在看到的services、networks、volumes在 Kubernetes 中会对应到Deployment、Service、PersistentVolumeClaim。Compose 是单机上的声明式编排而 K8s 是集群级的声明式编排——核心理念完全一致只是规模和抽象层次不同。下一篇——第 13 篇Compose 环境变量与配置管理我们将深入探讨如何优雅地管理多环境配置告别硬编码让同一份 Compose 文件在开发、测试、生产环境中灵活切换。想了解更多还可以去各个平台搜索「IT策士」一起升级 IT 思维