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
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 知识体系图
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 我积累下来的核心经验了。缩进写对了,配置复用做对了,团队协作自然就顺了。你下次写配置时,可以试试这些方法。说实话,一旦习惯了这个节奏,你会觉得其他格式写起来总觉得哪里不太顺手。