YAML•
2025-06-19
•7 min
•alltools.one Team
YAMLAnchorsAliasesDRYConfiguration
YAML 锚点与别名:DRY 配置模式
在 YAML 文件的多个部分重复相同的配置违反了 DRY(Don't Repeat Yourself)原则,并导致维护困难。YAML 锚点和别名通过允许你定义一次值并多次引用来解决这个问题。
基本语法
锚点(&):标记一个值以供复用
别名(*):引用一个已锚定的值
# 定义锚点
defaults: &default_settings
timeout: 30
retries: 3
log_level: info
# 使用别名引用它
development:
<<: *default_settings
log_level: debug
production:
<<: *default_settings
retries: 5
<< 合并键将引用的锚点中的所有键值对合并进来。合并之后定义的属性会覆盖锚定的值。
解析后的结果:
development:
timeout: 30
retries: 3
log_level: debug # 已覆盖
production:
timeout: 30
retries: 5 # 已覆盖
log_level: info
锚点类型
标量锚点
复用单个值:
max_connections: &max_conn 100
database:
pool_size: *max_conn
cache:
pool_size: *max_conn
序列锚点
复用整个列表:
common_ports: &ports
- 80
- 443
web_server:
ports: *ports
load_balancer:
ports: *ports
映射锚点
复用整个对象(最常见的模式):
logging: &log_config
driver: json-file
options:
max-size: "10m"
max-file: "3"
services:
api:
logging: *log_config
worker:
logging: *log_config
合并键(<<)
合并键有特定的行为:
- 它从引用的映射中复制所有键值对
- 显式设置的键优先于合并的键
- 多个合并按顺序处理(冲突时先到先得)
# 多个合并
base: &base
a: 1
b: 2
extra: &extra
b: 3
c: 4
combined:
<<: [*base, *extra]
# 结果:a=1, b=2(base 优先),c=4
实际示例
Docker Compose
x-common: &common
restart: unless-stopped
networks:
- app-network
logging:
driver: json-file
options:
max-size: "10m"
services:
api:
<<: *common
image: myapp/api:latest
ports:
- "8080:8080"
environment:
DATABASE_URL: postgres://db:5432/myapp
worker:
<<: *common
image: myapp/worker:latest
environment:
QUEUE_URL: redis://cache:6379
scheduler:
<<: *common
image: myapp/scheduler:latest
Docker Compose 使用 x- 前缀约定来定义包含锚点的扩展字段。更多模式请参见我们的 YAML 用于 Docker Compose 指南。
GitHub Actions
jobs:
test:
runs-on: ubuntu-latest
env: &common_env
NODE_VERSION: '20'
CI: true
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- run: npm test
build:
runs-on: ubuntu-latest
needs: test
env:
<<: *common_env
BUILD_TARGET: production
steps:
- uses: actions/checkout@v4
- run: npm run build
Kubernetes
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: app
resources: &resources
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
- name: sidecar
resources: *resources # 相同的资源限制
局限性与陷阱
1. 无跨文件引用
锚点只在单个 YAML 文件内有效。你不能引用另一个文件中定义的锚点。对于跨文件复用,请使用模板工具如 Helm、Jsonnet 或 Kustomize。
2. 无法部分覆盖序列
你不能合并并部分覆盖列表 — 你只能替换整个列表:
base_ports: &ports
- 80
- 443
service:
ports: *ports # 你得到的恰好是 [80, 443]
# 无法通过合并向此列表添加端口 8080
变通方案:单独定义扩展的列表。
3. 顺序很重要
锚点必须在引用之前定义:
# 错误 - 别名在锚点之前
service:
settings: *defaults
defaults: &defaults
timeout: 30
# 正确 - 锚点在别名之前
defaults: &defaults
timeout: 30
service:
settings: *defaults
4. 并非所有工具都支持合并键
<< 合并键不是 YAML 核心规范的一部分 — 它是一个特定类型的扩展。大多数流行的 YAML 解析器支持它,但某些严格的解析器可能不支持。
使用我们的 YAML 验证器 验证你的 YAML 锚点用法。
常见问题
将 YAML 转换为 JSON 时锚点会保留吗?
不会。当 YAML 被解析时,锚点和别名会被解析为它们的值。生成的 JSON 将内联展开这些值。这意味着转换为 JSON 再转回来会丢失 DRY 结构。锚点只存在于 YAML 源格式中。
我可以在复杂的嵌套覆盖中使用锚点吗?
合并键(<<)只执行浅合并 — 它从锚定的映射中复制顶层键。嵌套对象会被完全替换,而不是深度合并。对于深度合并行为,你需要特定工具的解决方案(Helm 的 merge 函数、自定义 YAML 处理器)。
相关资源
- YAML 验证器 — 验证带有锚点和别名的 YAML
- YAML 语法教程 — YAML 基础知识
- YAML 用于 Docker Compose — Docker 特定模式