4. YAML:比JSON更懂人的配置语言

聊到配置文件,JSON 和 YAML 你多半都见过。但我个人习惯,只要项目稍微复杂点,我会优先选 YAML。

为什么?因为可读性。**YAML 的设计哲学就是让人能一眼看懂**。它用缩进来表示层级,用短横线来表示列表,看起来就像一份简单的技术文档。

我曾经接手过一个遗留的 Node.js 项目,对方把所有配置都塞进了一个 500 行的 JSON 文件里。括号嵌套了七八层,改个字段得小心翼翼数逗号。后来我花了半天把它全部重构为 YAML,团队里的新人看了都说清爽多了。

4.1 YAML 的核心语法

YAML 主要就三样东西:键值对、数组、字典。学会了这三样,基本就能读懂 90% 的配置文件。

4.1.1 基本键值对

这是最基础的。冒号后面要加空格,这是 YAML 的硬性要求。

app_name: MyService
version: "2.1.0"
debug: false
max_connections: 100

你看,没有花括号,没有引号(非必要时),干净利落。

语法要素 说明 避坑要点
键值对 key: value 冒号后必须有一个空格
字符串 可直接写,特殊字符可加引号 纯数字加引号可强制为字符串
布尔值 true/false/yes/no 大小写不敏感,但建议小写

4.1.2 数组与列表

用短横线开头表示列表项。这在定义主机列表、日志路径时特别好用。

hosts:
  - 192.168.1.10
  - 192.168.1.11
  - 192.168.1.12

logging:
  levels:
    - INFO
    - WARN
    - ERROR
我的小技巧:如果你需要表示一个键对应多个值,优先用数组。这比用逗号分隔的字符串更清晰,也更容易被 CI/CD 工具解析。

4.1.3 嵌套字典

YAML 的层级靠缩进表示。两个空格是行业标准,我个人强烈建议不要用 Tab。

server:
  host: 0.0.0.0
  port: 8080
  ssl:
    enabled: true
    cert_path: /etc/ssl/cert.pem
    key_path: /etc/ssl/key.pem

这段配置,如果用 JSON 来表达,需要三层花括号。用 YAML,三层缩进,一目了然。

4.2 YAML 与 JSON 的对比

特性 YAML JSON
可读性 高,接近自然语言 中等,括号多时干扰视线
注释支持 原生支持(# 注释) 不支持(必须用 hack 或去掉
数据类型 支持日期、多行字符串等 基础类型为主
引用/锚点 支持 & 和 * 引用 不支持
解析性能 稍慢(需要处理缩进) 快,原生解析
一句话总结:
DevOps 世界里,YAML 是首选。原因很简单——它能写注释、能复用代码块、结构清晰到连非技术人员都能看懂。JSON 更适合机器对机器的数据传输。

4.3 多环境管理实战

我负责的一个微服务项目,部署环境有 dev、staging、prod 三套。如果每个环境都维护一份完整的 YAML,维护量太大了。

我的做法是:**写一个公共配置,再用 override 的方式覆盖差异点**。

# config/common.yaml
app:
  name: my-service
  version: 2.1.0
  log_level: INFO

database:
  host: localhost
  port: 5432
  pool_size: 10

# config/prod.yaml
database:
  host: prod-db.internal
  pool_size: 50

# config/dev.yaml
app:
  log_level: DEBUG
database:
  host: dev-db.internal
  pool_size: 5

启动时用脚本 merge 这两个文件。具体做法可以用 yq 命令行工具。

# 合并配置
yq eval-all 'select(fileIndex == 0) * select(fileIndex == 1)' config/common.yaml config/prod.yaml

这个命令会以 common 为基础,用 prod 覆盖同名字段。结果就是 prod 的日志级别仍为 INFO,但数据库地址和连接池被替换了。

我曾经踩过的一个坑:
合并 YAML 时,**数组不会被覆盖,而是追加**。比如 common 里定义了日志级别列表,prod 只想覆盖其中一个,结果变成了两个同时生效。后来我在数组前加了 !!merge 强制替换。

4.4 YAML 最佳实践与避坑指南

4.4.1 锚点与引用(减少重复)

你可能遇到多个服务共享相同配置的情况。用锚点能少写很多重复代码。

defaults: &defaults
  adapter: postgresql
  host: localhost
  port: 5432

development:
  <<: *defaults
  database: myapp_dev

test:
  <<: *defaults
  database: myapp_test

production:
  <<: *defaults
  host: prod-db.internal
  database: myapp_prod

这里定义了 &defaults 锚点,后续用 *defaults 引用。用 <<: 把锚点内容合并进来,然后只覆盖差异字段。这就是我处理多环境的终极武器。

4.4.2 缩进与空格陷阱

常见错误 说明 正确做法
使用 Tab YAML 解析器可能报错或行为诡异 只用空格,编辑器设 expand tab
缩进不一致 部分 2 空格,部分 4 空格 全局统一 2 空格
冒号后无空格 key:value 会被认为是字符串 必须 key: value
纯数字未加引号 以 0 开头的数字可能被当八进制解析 字符串用引号包起,如 "0123"

4.4.3 命名规范

我所在团队定了几条硬性规则:

  • 键名使用小写字母加下划线(snake_case)
  • 环境变量使用大写加下划线(UPPER_SNAKE_CASE)
  • 不要在同级出现 key 和 Key(大小写敏感只在理论正确,实践中容易搞混)
  • 布尔值明确写 true/false,不要用 yes/no

4.5 知识体系图

YAML 配置管理知识体系 YAML 基础语法 高级特性 多环境管理 最佳实践 键值对 数组/列表 锚点引用 合并语法 公共+override yq 合并工具 缩进规范 命名规则 嵌套字典 核心思路 用基础语法入门,用高级特性减少重复,用多环境模板规范团队协作

4.6 工具链推荐

说几个我常用的 YAML 工具:

  • yq:命令行 YAML 处理器,支持 merge、diff、查询。我用来做配置合并。
  • yamllint:语法检查器,可以在 CI 里跑。防止缩进错误提前被合并进主分支。
  • Prettier:格式化 YAML 时,会统一缩进、对齐冒号。
  • VS Code YAML 插件:支持 schema 校验和自动补全。

在 CI 中集成 yamllint 的命令示例:

yamllint config/*.yaml
yq eval '...' config/common.yaml | head -5

如果 lint 没通过,直接让流水线失败。这个规则在我团队已经成了硬要求。

总结一句:
YAML 让你把“配置”这件事,从“机器能读”升级到“人也爱读”。
配合锚点、合并和多环境管理,你就能省下大量手改配置文件的时间。

嗯,这些就是关于 YAML 我积累下来的核心经验了。缩进写对了,配置复用做对了,团队协作自然就顺了。你下次写配置时,可以试试这些方法。说实话,一旦习惯了这个节奏,你会觉得其他格式写起来总觉得哪里不太顺手。