YAML 是什么?语法规则和常见踩坑一次讲清
YAML 是 Kubernetes、Docker Compose、GitHub Actions 这些工具的配置文件格式——如果你在做云原生或 DevOps,YAML 几乎天天写。但它有个让人又爱又恨的特点:语法看起来简单,踩坑却一个接一个。
YAML 是什么
YAML 全称"YAML Ain't Markup Language"(递归缩写,故意这么玩的),是一种面向人类的数据序列化格式。和 JSON、XML 一样,它用来表示结构化数据,但设计目标很明确:让人能直接读和写。
一句话区分:JSON 是给机器看的,YAML 是给人看的。
YAML 的三种数据结构
所有 YAML 内容都由这三种结构组合而成,理解它们就能看懂任何 YAML 文件。
映射(键值对)
yamlname: nginx port: 8080
冒号后面必须跟一个空格,这是 YAML 最基本的规则。漏掉这个空格会直接报解析错误。
序列(列表)
yamlfeatures: - authentication - logging - monitoring
列表项用 - 开头,注意连字符后面也有一个空格。
标量(单个值)
字符串、数字、布尔值、null 都是标量。YAML 会自动推断类型:
yamlversion: 1.2 # 浮点数 debug: true # 布尔值 host: localhost # 字符串 timeout: null # null
自动推断有时候会坑人。比如 off 会被解析为 false,yes 会被解析为 true,版本号 1.10 看起来像浮点数。需要原样保留字符串时,加上引号:
yamlversion: "1.10" # 强制字符串,不会被转成 1.1 switch: "off" # 强制字符串,不会被转成 false
这个坑在生产环境排查过的人都知道——一个引号之差,配置就跑偏了。
YAML vs JSON vs XML
实际项目中这三种格式经常需要选择,核心区别一目了然:
| 特性 | YAML | JSON | XML |
|---|---|---|---|
| 注释 | 支持 # | 不支持 | 支持 |
| 多行字符串 | 支持 ` | 和>` | 不支持 |
| 可读性 | 高 | 中 | 低 |
| 解析速度 | 慢 | 快 | 中 |
| 数据类型 | 丰富(含日期、时间戳) | 基本类型 | 全是字符串 |
| 超集关系 | JSON 的超集 | — | — |
YAML 是 JSON 的超集,意味着任何合法的 JSON 写法直接放进 YAML 文件也能解析。所以在 YAML 里嵌入 JSON 片段是完全合法的。
选择建议:配置文件用 YAML,API 数据交换用 JSON,需要严格验证结构用 XML。
缩进:YAML 的命门
YAML 用缩进表示层级关系,这条规则没有商量的余地:
- 只能用空格,不能用 Tab。混用空格和 Tab 是 YAML 解析报错的第一大原因
- 同层元素必须对齐。缩进空格数不限制(2 个或 4 个都行),但同一层必须一致
- 推荐 2 个空格缩进,Kubernetes 和 Docker Compose 的官方示例都用 2 空格
yaml# 正确:同层对齐 server: host: localhost port: 8080 features: - auth - logging # 错误:缩进不对齐,解析器直接报错 server: host: localhost port: 8080 # 多了一个空格
大多数编辑器可以设置"将 Tab 转换为空格",强烈建议开启。VS Code 底部状态栏点击"Tab Size"就能改。
多行字符串:配置文件的救星
YAML 处理多行文本的方式比 JSON 优雅得多,有两种模式:
| 保留换行(literal block):每一行换行原样保留,适合脚本、证书等
yamlstartup_script: | #!/bin/bash echo "Starting service..." sleep 3 systemctl start app
> 折叠换行(folded block):连续换行合并成一个空格,适合长段落文本
yamldescription: > This is a long description that will be folded into a single line when parsed.
在 Docker Compose 里写启动命令、在 Kubernetes 里挂载配置文件,这两种写法用得最多。
锚点和引用:YAML 的复用机制
当配置文件里有重复内容时,锚点(&)和引用(*)能减少冗余:
yamldefaults: &defaults timeout: 30 retries: 3 log_level: info production: <<: *defaults log_level: warning retries: 5 staging: <<: *defaults timeout: 10
&defaults 定义锚点,*defaults 引用它,<<: 表示合并(merge)。这在多环境配置中非常实用——基础配置写一次,各环境只覆盖差异项。
多文档分隔
一个 YAML 文件可以包含多个文档,用 --- 分隔。Kubernetes 的资源清单经常这么用:
yaml--- apiVersion: v1 kind: Service metadata: name: nginx-svc --- apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deploy
一个文件管理多个资源,kubectl apply 一次搞定。
真实项目配置示例
一个完整的 Docker Compose 配置,把前面提到的语法串起来:
yamlversion: "3.8" x-logging: &default-logging # 锚点定义 driver: json-file options: max-size: "10m" max-file: "3" services: web: image: nginx:alpine ports: - "80:80" - "443:443" volumes: - ./nginx.conf:/etc/nginx/nginx.conf:ro logging: *default-logging # 锚点引用 healthcheck: test: ["CMD", "curl", "-f", "http://localhost"] interval: 30s timeout: 10s retries: 3 db: image: postgres:15 environment: POSTGRES_DB: myapp POSTGRES_PASSWORD: "${DB_PASSWORD}" # 引用环境变量 volumes: - db-data:/var/lib/postgresql/data logging: *default-logging volumes: db-data:
这个配置用到了映射、序列、锚点引用、多行字符串、环境变量插值——基本上 YAML 的核心特性全覆盖了。
常见踩坑清单
| 坑 | 现象 | 解决方案 |
|---|---|---|
| Tab 混用空格 | 解析报错"found character that cannot start any token" | 编辑器开启"Tab 转空格" |
| 冒号后没空格 | key:value 被当成字符串 | 写成 key: value |
| 自动类型转换 | off 变 false,1.10 变 1.1 | 加引号强制字符串 |
| 缩进不一致 | 解析报错或数据嵌套错误 | 保持同级缩进对齐 |
| 特殊字符未转义 | :, {, [, , 等字符导致解析异常 | 用引号包裹含特殊字符的值 |
| 文件编码问题 | 含中文时解析乱码 | 确保文件为 UTF-8 编码 |
这些坑在实际项目中反复出现,养成习惯比事后排查高效得多。