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 文件由三个顶级元素构成:

bash 复制代码
services:    # 定义应用服务
  ...
networks:    # 定义网络
  ...
volumes:     # 定义数据卷
  ...

此外还有 configssecrets(用于 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 环境变量自定义项目名称:

bash 复制代码
docker compose -p myproject up -d

二、services 详解:服务的核心配置

services 是 Compose 文件中最核心的模块,每个服务可以配置的选项非常多。我将按功能分类逐一拆解,并用我们贯穿的 Flask + Redis 应用来演示。

2.1 镜像与构建

指定服务使用的镜像有两种方式:

直接使用已有镜像:

bash 复制代码
services:
  redis:
    image: redis:alpine     # 从仓库拉取

从 Dockerfile 构建:

bash 复制代码
services:
  flask-app:
    build: .                 # 使用当前目录的 Dockerfile
    # 或者更详细的写法
    build:
      context: ./app         # 构建上下文路径
      dockerfile: Dockerfile.dev  # 指定 Dockerfile 文件名
      args:                  # 构建参数
        - ENV=production

如果同时指定了 buildimage,Compose 会用 image 的值作为构建后的镜像名称(相当于自动执行 docker build -t <image> .):

bash 复制代码
services:
  flask-app:
    build: .
    image: flask-redis-counter:latest

2.2 容器名称与重启策略

bash 复制代码
services:
  flask-app:
    container_name: flask-app     # 固定容器名(不指定则自动生成)
    restart: unless-stopped       # 重启策略

重启策略与 docker run --restart 一致,可选值:noalwayson-failureunless-stopped

注意 :如果计划使用 --scale 扩容,不要 指定固定的 container_name,因为每个副本必须有不同的名称。扩容场景下应让 Compose 自动生成名称。

2.3 端口映射

bash 复制代码
services:
  flask-app:
    ports:
      - "5000:5000"                # 宿主机端口:容器端口
      - "5001:5000"                # 可以映射多个端口
      - "127.0.0.1:5002:5000"      # 只绑定到宿主机特定 IP

等价于 docker run -p 5000:5000

2.4 环境变量

bash 复制代码
services:
  flask-app:
    environment:
      - FLASK_ENV=production
      - REDIS_HOST=redis
      - DB_PASSWORD=secret123      # ⚠️ 不要在 YAML 中硬编码敏感信息
    # 或从文件加载
    env_file:
      - .env

直接写在 YAML 中的环境变量会被提交到 Git 仓库,敏感信息(密码、API 密钥)不应硬编码。更好的方式是使用 .env 文件或 Docker Secrets(Swarm 模式),这将在第 13 篇和第 33 篇中专门展开。

2.5 数据卷挂载

bash 复制代码
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:ro(Bind Mount,开发环境常用)

  • 第三行是相对路径 Bind Mount,./data 相对于 docker-compose.yml 所在目录

  • 第四行只指定容器内路径,Docker 自动创建匿名卷

挂载目录的注意事项 :当使用 Bind Mount 时,宿主机目录会完全覆盖容器内对应路径。如果宿主机目录为空,容器内原来镜像中的文件会被"隐藏"。这常导致"挂载后服务启动失败"的困惑------比如挂载了一个空的 ./config 到容器的 /etc/nginx/conf.d,Nginx 就找不到任何配置文件了。

2.6 网络配置

bash 复制代码
services:
  flask-app:
    networks:
      - app-net
      - monitor-net          # 可以连接多个网络
    # 也可以为每个网络指定别名
    networks:
      app-net:
        aliases:
          - web
          - api

网络别名(aliases)让其他容器可以通过多个名称访问这个服务,等价于 docker run --network-alias

2.7 启动命令与入口点

bash 复制代码
services:
  redis:
    command: redis-server --appendonly yes --maxmemory 256mb
    # 或者用数组格式(推荐)
    command: ["redis-server", "--appendonly", "yes"]

command 会覆盖镜像默认的 CMD,但不覆盖 ENTRYPOINT

2.8 健康检查

bash 复制代码
services:
  redis:
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 3s
      retries: 3
      start_period: 5s

等价于 Dockerfile 中的 HEALTHCHECK 指令,但 Compose 中定义可以覆盖镜像内置的检查规则。

2.9 服务依赖与启动顺序

bash 复制代码
services:
  flask-app:
    depends_on:
      redis:
        condition: service_healthy   # 等待 Redis 健康检查通过
      db:
        condition: service_started   # 仅等待 db 容器启动(不等服务就绪)

三种条件:

  • service_started:仅等待容器启动

  • service_healthy:等待健康检查通过(推荐)

  • service_completed_successfully:等待一次性任务完成(如数据库迁移),退出码为 0

depends_on只控制启动顺序,不保证依赖服务完全就绪 。这就是为什么 condition: service_healthy 如此重要------它把"启动顺序"和"服务就绪"绑定在了一起。

2.10 资源限制(Compose v2+)

bash 复制代码
services:
  flask-app:
    deploy:
      resources:
        limits:
          cpus: '0.50'       # 最多使用 50% 单核 CPU
          memory: 256M        # 内存上限
        reservations:
          cpus: '0.25'        # 至少保留 25% 单核 CPU
          memory: 128M        # 至少保留内存

注意deploy.resourcesdocker compose up 中会被忽略 ,仅在 Swarm 模式(docker stack deploy)下生效。单机 Compose 场景下如需限制资源,请使用 docker run--cpus--memory 参数,或者在 Compose 文件中使用已弃用但依然有效的 V2 格式的 mem_limitcpus(不推荐,兼容性差)。在实际生产环境中,资源限制应交给 Kubernetes 的 ResourceQuota 和 LimitRange 管理。

2.11 完整示例:Flask + Redis 的 services 配置

综合以上知识,我们把 Flask + Redis 应用的 services 部分打磨得更完善:

bash 复制代码
services:
  redis:
    image: redis:alpine
    restart: unless-stopped
    command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru
    volumes:
      - redis-data:/data
    networks:
      - app-net
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 3s
      retries: 3
      start_period: 5s

  flask-app:
    image: flask-redis-counter:2.0
    restart: unless-stopped
    ports:
      - "5000:5000"
    environment:
      - FLASK_ENV=production
    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: 3
      start_period: 5s

三、networks 详解:自定义网络配置

Compose 默认会为每个项目创建一个默认网络(项目名_default),所有未显式声明 networks 的服务都会连接到这个默认网络。但我们强烈建议显式声明自定义网络,以获得更好的控制和隔离性。

bash 复制代码
networks:
  app-net:
    driver: bridge            # 默认驱动,单机桥接网络
  frontend:
    driver: bridge
  backend:
    driver: bridge

3.1 自定义子网和网关

bash 复制代码
networks:
  app-net:
    driver: bridge
    ipam:
      config:
        - subnet: 172.25.0.0/16
          gateway: 172.25.0.1

这在你需要固定 IP 段、与外部系统对接时非常有用。

3.2 外部网络

有时候你希望容器连接到一个已经存在的 Docker 网络(由其他项目或手动创建),而非由 Compose 自动创建:

bash 复制代码
networks:
  existing-net:
    external: true      # 表示该网络已在外部创建

然后在服务中引用:

bash 复制代码
services:
  flask-app:
    networks:
      - existing-net

external: true 告诉 Compose:"不要创建新网络,去连接那个已经存在的网络"。如果网络不存在,docker compose up 会报错。

3.3 网络别名

在同一网络中,可以通过 aliases 为服务定义额外的 DNS 名称:

bash 复制代码
services:
  redis:
    networks:
      app-net:
        aliases:
          - cache
          - redis-cache

现在,其他容器可以通过 cacheredis-cache 来解析到这个 Redis 容器,实现了简单的"服务发现别名"。在 K8s 中,这对应着 Service 的 namespec.clusterIP 的 DNS A 记录。

四、volumes 详解:持久化存储配置

bash 复制代码
volumes:
  redis-data:       # 命名卷,Docker 自动管理存储路径
  flask-logs:

4.1 卷驱动

默认使用 local 驱动(存储在宿主机本地磁盘)。也可以使用第三方驱动,如 NFS、AWS EBS:

bash 复制代码
volumes:
  nfs-data:
    driver: local
    driver_opts:
      type: nfs
      o: addr=192.168.1.100,nolock,soft,rw
      device: ":/export/data"

4.2 外部卷

和外部网络类似,可以使用已有的卷:

bash 复制代码
volumes:
  existing-data:
    external: true

这对于数据迁移或跨项目共享数据卷很有用。

五、完整的 docker-compose.yml 最终版

综合所有知识,我们为 Flask + Redis 计数器应用编写一份生产就绪的 Compose 文件:

bash 复制代码
# ============================================================
# Flask + Redis 计数器应用 ------ Docker Compose 生产配置
# 系列贯穿案例 v3.0
# ============================================================

services:
  redis:
    image: redis:alpine
    restart: unless-stopped
    command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru
    volumes:
      - redis-data:/data
    networks:
      app-net:
        aliases:
          - cache
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 3s
      retries: 3
      start_period: 5s

  flask-app:
    image: flask-redis-counter:2.0
    restart: unless-stopped
    ports:
      - "5000:5000"
    environment:
      - FLASK_ENV=production
      - REDIS_HOST=cache          # 使用网络别名
    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: 3
      start_period: 5s

volumes:
  redis-data:
  flask-logs:

networks:
  app-net:
    driver: bridge
    ipam:
      config:
        - subnet: 172.25.0.0/16

5.1 启动与验证

bash 复制代码
# 启动
docker compose up -d

# 查看资源
docker compose ps
docker network ls | grep app-net
docker volume ls | grep flask-redis-counter

# 测试别名
docker compose exec flask-app nslookup cache

六、命令速查表

七、本篇总结

  • services :每个服务涵盖镜像、端口、环境变量、卷挂载、网络、健康检查、依赖等全套配置,任何一个 docker run 参数在 Compose 中都有对应的声明式写法。

  • networks :自定义网络提供了 DNS 解析和网络隔离,aliases 实现服务别名,external 接入已存在的网络。

  • volumes:命名卷是生产持久化的首选,外部卷和第三方驱动扩展了存储能力。

  • 演进视角 :你现在看到的 servicesnetworksvolumes,在 Kubernetes 中会对应到 DeploymentServicePersistentVolumeClaim。Compose 是单机上的声明式编排,而 K8s 是集群级的声明式编排------核心理念完全一致,只是规模和抽象层次不同。

下一篇------第 13 篇:Compose 环境变量与配置管理,我们将深入探讨如何优雅地管理多环境配置,告别硬编码,让同一份 Compose 文件在开发、测试、生产环境中灵活切换。


想了解更多还可以去各个平台搜索「IT策士」,一起升级 IT 思维 !

相关推荐
楼田莉子11 小时前
C++20现代特性:概念与约束
开发语言·c++·后端·学习·c++20
Dante丶12 小时前
Codex Desktop 不断 Reconnecting 的代理环境变量处理
前端·后端·代码规范
XovH12 小时前
Docker Compose 入门:一条命令启动多服务
后端
Yeats_Liao12 小时前
6:部署Servlet-Java Web
java·后端·架构
XovH12 小时前
Docker 网络入门:桥接、自定义与主机网络
后端
用户21816970493012 小时前
golang socket(三) TCP协议 实现聊天功能 TCPConn
后端
Kir1to12 小时前
线程的三种创建方式与生命周期及线程池
后端
Rust研习社12 小时前
MSRV 是什么?一文说清楚
后端·rust·编程语言
XovH12 小时前
Docker 网络进阶:容器间通信与 DNS 解析
后端