类型推断: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