Compose 允许您通过多个 Compose 文件来定义应用模型。当使用多个文件时,Compose 会遵循特定规则来合并这些文件。

  • 映射(Mapping)的合并:YAML 映射通过 添加缺失的条目 和 合并冲突的条目 来实现合并。
# 文件 1
services:
  foo:
    key1: value1
    key2: value2

# 文件 2
services:
  foo:
    key2: VALUE  # 此值将覆盖上一个文件的 value2
    key3: value3

合并结果为:

services:
  foo:
    key1: value1
    key2: VALUE  # 来自第二个文件
    key3: value3 # 来自第二个文件
  • 序列(Sequence)的合并:YAML 序列通过 将后续文件的值追加 到前一个文件的序列中来实现合并。

合并以下示例:

# 文件 1
services:
  foo:
    DNS:
      - 1.1.1.1

# 文件 2
services:
  foo:
    DNS: 
      - 8.8.8.8

合并结果为:

services:
  foo:
    DNS:
      - 1.1.1.1  # 来自第一个文件
      - 8.8.8.8  # 来自第二个文件,被追加
  • 特殊规则(例外情况)
    • Shell 命令属性:对于服务属性 command、entrypoint 和 healthcheck: test,合并时最新(后面)的 Compose 文件的值会完全覆盖前一个文件的值,而不是追加。合并以下示例:
# 文件 1
services:
  foo:
    command: ["echo", "foo"]

# 文件 2
services:
  foo:
    command: ["echo", "bar"]

合并结果为:

services:
  foo:
    command: ["echo", "bar"]  # 来自第二个文件,覆盖了第一个
  • 唯一资源属性:适用于 ports、volumes、secrets 和 configs 这些服务属性。虽然它们在 Compose 文件中以序列形式表示,但有特殊的唯一性约束:

属性

唯一性判断依据

volumes

target

(容器内的目标路径)

secrets

target

configs

target

ports

{ip, target, published, protocol}

 的组合

合并文件时,Compose 会追加不违反唯一性约束的新条目,并合并共享同一“唯一键”的条目。合并以下示例:

# 文件 1
services:
  foo:
    volumes:
      - foo:/work  # target 是 /work

# 文件 2
services:
  foo:
    volumes:
      - bar:/work  # target 也是 /work,唯一键冲突

合并结果为:

services:
  foo:
    volumes:
      - bar:/work  # 第二个文件的值覆盖了第一个,因为 target (/work) 相同
  • 重置值(!reset):除了前面描述的机制外,覆盖(Override)Compose 文件也可用于从您的应用模型中移除元素。为此,可以使用自定义的 YAML 标签 !reset 来覆盖被覆盖 Compose 文件中设置的值。虽然必须为该属性提供一个有效的值,但该值会被忽略,目标属性将被设置为该类型的默认值或 null。为了增强可读性,建议将属性值显式地设置为 null 或空数组 [](使用 !reset null 或 !reset []),这样可以清楚地表明结果属性将被清除。

基础文件 compose.yaml:

services:
  app:
    image: myapp
    ports:
      - "8080:80" 
    environment:
      FOO: BAR

覆盖文件 compose.override.yaml:

services:
  app:
    image: myapp
    ports: !reset []         # 移除所有端口映射
    environment:
      FOO: !reset null       # 移除 FOO 环境变量

合并结果为:

services:
  app:
    image: myapp
    # ports 和 environment 下的 FOO 已被移除
  • 替换值(!override):要求:Docker Compose 2.24.4 或更高版本。虽然 !reset 可用于移除声明,但 !override 允许您完全替换一个属性,绕过标准的合并规则。一个典型的用例是:用相同名称但完全不同的模型来替换某个资源定义。

基础文件 compose.yaml:

services:
  app:
    image: myapp
    ports:
      - "8080:80"

覆盖文件:

services:
  app:
    ports: !override        # 使用 !override 标签
      - "8443:443"          # 这将完全替换之前的端口列表

合并结果为:

services:
  app:
    image: myapp
    ports:
      - "8443:443"          # 只有这个端口,之前的 “8080:80” 被完全替换

如果这里没有使用 !override,根据前面的合并规则,结果将会是 "8080:80" 和 "8443:443" 两个端口并存。

更多推荐