类型推断:YAML 最大的静默炸弹
YAML 会自动推断标量类型,很多「看起来是字符串」的值会被静默转换:no/na/n 变布尔 false(著名的挪威问题),on/off/yes 变 true/false,0733 变八进制,1.10 变数字 1.1。
后果是配置「看着对、跑起来错」:版本号丢失精度、国家代码变布尔。除非明确要数字/布尔/null,标量一律加引号——这是 YAML 最省心的一条纪律。
country: no # 实际是 false,不是挪威!
country: "no" # 这才是字符串
version: 1.10 # 变成数字 1.1
version: "1.10" # 字符串,安全什么时候必须加引号
三类值必须引号:① 可能被推断为数字/布尔/null 的(no、on、1.10,以及会被解析成日期对象的 2026-01-01);② 以特殊字符开头的(* & ! % @);③ 值里含「冒号+空格」或 # 的。
日期坑单独强调:date: 2026-01-01 在多数解析器里是日期对象而非字符串,日志排查时会以完全不同的形态出现。写 date: "2026-01-01" 才是字符串。
date: 2026-01-01 # 日期对象,不是字符串
date: "2026-01-01" # 字符串
value: a: b # 语法错误(冒号+空格)
value: "a: b" # 正确缩进与结构:错一层,数据落到别的节点
YAML 只认空格不认 Tab(Tab 直接语法错误);同级键必须对齐;列表项 - 后的内容属于子层级。结构错一层,数据就静默落到别的节点下——多数解析器根本不报错。
两个高频细节:冒号后必须有空格(key:value 是错的);注释 # 前要有空格,否则被当作值的一部分。编辑器装 YAML 语言服务(如 VSCode 的 Red Hat YAML)能实时标出这类错误。
server:
host: 0.0.0.0 # 缩进 2 空格,属于 server
ports:
- 80 # 列表项再缩进一级
- 443
# key:value 错 → key: value 对多行字符串:| 与 > 的区别
竖线 | 保留换行(字面块),折角 > 把换行折叠成空格(折叠块),加 - 表示去掉末尾换行(|-、>-)。写脚本、证书、SQL 这类内容时用 |;写长段落说明时用 >。
块的缩进基准取第一行:块内所有行必须比父键多缩进且一致——这就是「明明对齐了还报错」的原因:和上一层比错了对象。
script: |
#!/usr/bin/env bash
set -euo pipefail
echo "ok"
desc: >
这是一段很长的说明文字,
换行会被折叠成空格。
sql: |-
SELECT *
FROM users;锚点、别名与合并:复用公共片段
锚点 &name 打标记,别名 *name 引用它,<<: 把一个映射合并进当前键——docker-compose 复用公共配置就靠这套机制。
两个限制要知道:数组(列表)不能用 << 合并,只能整体引用;别名是「引用」不是「拷贝」,改一处全变。顺带一个高频暗雷:端口映射 80:80 不加引号在 YAML 1.1 里会被解析成 60 进制数字,compose 里务必写成字符串 "80:80"。
common: &common
image: nginx:1.27
restart: unless-stopped
web:
<<: *common
ports:
- "80:80"排错工具链与收尾清单
上线前用工具验证:yamllint 查风格与结构;用目标工具本身解析一遍(docker compose config、kubectl --dry-run);Python 侧一条 yaml.safe_load 即可快速确认解析结果是否符合预期。
排查原则:报错行号往往指向「问题显现处」,真正的错常在上方几行的缩进或引号。清单:标量加引号、缩进只用空格、冒号后空格、多行块选对 |/>、端口映射写引号、提交前过一遍解析器。
yamllint config.yaml
docker compose config # 解析并规范化输出
kubectl apply --dry-run=client -f manifest.yaml
python -c "import yaml,sys; yaml.safe_load(open(sys.argv[1]))" config.yaml