Kubernetes KYAML YAML Helm GitOps

KYAML 從 1.34 熬到 Kubernetes 1.37 stable:用花括號跟強制引號把 Norway bug 跟 Helm 縮排地雷一次拆掉

Kubernetes 1.37「Garhwal」把 KYAML 推到 GA,kubectl get -o kyaml 從此是穩定介面。這篇拆解 KYAML 為什麼要選 flow style、KUBECTL_KYAML 環境變數在 alpha/beta/GA 三個階段的行為差異,以及既有 Helm chart 到底該不該跟著改寫。

Kubernetes 1.37「Garhwal」在 2026 年 8 月 26 日釋出,release note 裡最不起眼但影響面最大的改動,是 KYAML 正式進入 stable。這件事對只在意「pod 有沒有跑起來」的使用者沒差,對維護幾百份 manifest、每天在 Helm template 跟 GitOps repo 之間打滾的 platform team 來說,是十年來 YAML 第一次真正被收乾淨的機會。SIG CLI 從 KEP-5295 開始推,1.34 進 alpha、1.35 到 beta、1.37 GA,中間補上 conformance test,kubectl get -o kyaml 從此是穩定介面。

KYAML 本質上是 YAML 的嚴格子集,不是新語言、不需要新 parser。任何一份 KYAML 文件都是合法的 YAML,可以被現有工具鏈直接吃下去。它做的事情只有一件:把 YAML 那些會咬人的模糊語意全部關掉,用花括號、方括號、強制雙引號跑出一種看起來有點像 JSON、但保留註解跟尾逗號、且對縮排完全不敏感的格式。

Norway bug 十年沒補、Helm indentation 是另一個時間炸彈

YAML 1.1 規範裡把 yYyesYesYESnNnoNoNOonoff 全部視為布林。這件事在 config 檔裡的後果就是那個著名的 Norway bug:

1
2
3
4
countries:
- NO
- SE
- FI

parser 讀進來 NO 變成 false,挪威從清單裡消失。這不是理論問題,Ansible、Helm、Kubernetes manifest 都出過類似災情。同樣的問題也發生在版本號跟 MAC address:version: 1.10 有時被讀成浮點數 1.1address: 0x0a:0x1b 被讀成什麼要看 parser 心情。標準做法是全部字串加引號,但這件事沒人強制執行,code review 也很少抓。

比 Norway bug 更痛的是 Helm chart 的縮排問題。Helm template 是用文字取代的方式產生 YAML,{{ toYaml .Values.env | nindent 8 }} 這種寫法本質上是在賭渲染出來的空白會落在正確的縮排層級。改一次上層結構、多包一層 if,下游 template 的縮排常常要跟著調兩三處。渲染完的 YAML 語法上合法、marshal 也不會炸,但物件層級整個歪掉,跑起來才會發現 sidecar container 被塞成 pod 的 label。

這兩個問題本質相同:YAML 規範太寬鬆,把型別推斷跟結構判定都交給實作決定,寫的人跟讀的人的心智模型很難對齊。JSON 是另一個極端——沒有註解、不能有尾逗號、每個 key 都要引號、寫組態很痛苦。KYAML 選擇在中間畫線。

為什麼是花括號、強制引號、還要 — 開頭

KYAML 的語法規則可以濃縮成幾條:所有 struct 跟 map 用花括號 {}、所有 list 用方括號 []、所有字串值強制雙引號、每個元素後面接 trailing comma、key 除非本身是型別關鍵字(noyeson 這類)否則不加引號、兩格縮排維持慣例。純數字、布林、null 照原樣寫,不加引號。

一份 Service 用 KYAML 表達出來長這樣:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
---
{
apiVersion: "v1",
kind: "Service",
metadata: {
name: "hostnames",
labels: {
app: "hostnames",
},
},
spec: {
selector: {
app: "hostnames",
},
ports: [{
port: 80,
targetPort: 9376,
}],
},
}

開頭那個 --- 不是裝飾。KYAML 跟 JSON 都以 { 開頭,parser 無法從第一個字元判斷是哪個格式。document separator 補上去之後,YAML parser 立刻確定這是 YAML 文件、走 YAML 分支處理,不會誤觸嚴格 JSON 模式。

強制雙引號解掉 Norway bug——NO 變成 "NO" 就是字串,沒有型別歧義的空間。花括號解掉縮排敏感——{ a: 1, b: 2 }{a:1,b:2} 甚至跨多行的寫法,parser 判定結果完全一樣。這件事對 Helm 是決定性的:template 產出來的 KYAML 不管 nindent 給的空白數對不對,只要花括號配對正確,物件結構就穩。

trailing comma 這條看起來很小,實際上省掉大量 diff noise。加一行 list 元素、diff 出來只有新增那一行,不會因為前一行原本沒逗號、現在要補上而多動一行。code review 讀起來乾淨、merge conflict 也少。

kubectl 端的三段生命週期跟 KUBECTL_KYAML

kubectl get -o kyaml 是 KYAML 對外的主要接觸面,行為隨版本推進:

  • 1.34 alphaKUBECTL_KYAML="true" 才啟用。沒設環境變數的話 -o kyaml 會噴 unknown output format
  • 1.35 beta:預設啟用,KUBECTL_KYAML="false" 才會關掉。這個階段是回歸測試的窗口,發現問題可以先關回去
  • 1.37 GA:環境變數整個移除,-o kyaml 永遠可用

實作路徑值得注意。KYAML 不是直接從 YAML 轉 YAML,而是先 marshal 成 JSON、再 render 成 KYAML。這個設計把 JSON tag 的既有邏輯全部拿來用,避開了 Go 的 YAML 函式庫在 comment 保留、anchor 處理上一堆已知 bug。副作用是 anchor、alias、explicit tag 這些 YAML 高階特性在 KYAML 輸出裡會被 reify——展開成實際值,原本共用的錨點消失。手寫 YAML 依賴 anchor 減少重複的做法,一旦跑過 -o kyaml 就回不去了。

kubectl edit 目前預設仍走傳統 YAML。要 edit 出來就是 KYAML 得先設 KUBECTL_EDITOR_OUTPUT=kyaml(實作在 1.37 還在 beta 階段),實務上 GitOps 環境比較少用 edit,這件事影響有限。

遷移策略:從 output 看起、不必急著改寫 chart

正確的導入順序是先把 KYAML 當 output 用、不動 source of truth。日常操作把 kubectl get -o kyaml 加進 alias、debug 時看 KYAML 版本的物件,眼睛先適應花括號。這步幾乎零成本,因為 KYAML 是合法 YAML,kubectl apply -f 直接吃。

第二步是新寫的 manifest 直接用 KYAML。這個階段最省事,沒有轉檔顧慮、沒有 anchor 消失的問題,寫起來也比傳統 YAML 少踩雷。搭配 kubectl-neatkubectl get ... -o kyaml | sed 這類 pipeline 可以快速從 cluster 撈出乾淨版本當範本。

Helm chart 遷移是最麻煩的一段,短期內不建議整包重寫。Helm 3.15 之後的 template engine 對 KYAML 輸出有基本支援,但 built-in toYamlnindent 這些 helper 仍然只吐傳統 YAML。想在 chart 裡混用 KYAML 需要自己寫 toKyaml 這類 template function,或改用 kubectl kustomize 的 KYAML output。實務上比較穩的做法是:chart 內部繼續維持 YAML、CD pipeline 最後把渲染結果轉一次 KYAML 再送進 cluster,把「渲染錯誤」跟「格式錯誤」兩件事分開。

CI 側可以引入 hack/verify-yamlfmt.sh 這個 kubernetes 上游用來 lint KYAML 格式的工具。這支 script 支援 opt-in 模式,per-file 決定用傳統 YAML 或 KYAML 規則檢查,重構過渡期不會一次要求全部改完。

KYAML 撐不住的兩個場景

多行字串是 KYAML 目前最明顯的短板。傳統 YAML 的 block scalar(|>)保留原始換行的能力在 KYAML 裡沒有直接對應,必須寫成 "first line\nsecond line\nthird line" 這種帶 \n escape 的長字串。ConfigMap 裡塞 shell script、nginx.conf、Envoy config 這類多行內容,用 KYAML 表達會顯著變醜、可讀性下降。這種場景 KEP 的建議是保留傳統 YAML block 寫法。

第二個限制是 non-string map key。YAML 允許整數、布林當 key,KYAML 因為底層走 JSON marshal 的關係不支援。這個場景在 Kubernetes manifest 裡罕見(幾乎沒有),但如果 chart values 有這種寫法,轉 KYAML 會直接 fail、不是自動降級。

該不該現在就切過去

答案分兩層。日常 kubectl 操作直接把 -o kyaml 當預設用,收益立刻兌現、成本接近零。source of truth 這一層,如果是 GitOps 全新 repo、Helm 依賴不重、團隊願意投資新語法認知成本,KYAML 現在切過去是划算的;反過來已經有大量 Helm chart 跟 Kustomize overlay 的環境,等 Helm 官方把 toKyaml helper 收進 upstream 再遷移比較實際。

在臺灣 VPS 上跑 Kubernetes 順手驗一次 KYAML

要把 KYAML 的行為差異弄清楚,最直接的路徑是開一臺 VPS、跑 kind 或 k3s 拉一個 1.37 cluster,把 KUBECTL_KYAML 三個階段的行為都試一輪。NCSE Network 的 VPS 主機建置在臺灣是方電訊機房,Intel Gold CPU 加 NVMe SSD 對 kubelet 跟 etcd 這種 I/O 敏感的元件跑起來很穩,Debian 13、Ubuntu 26.04 等映像檔開箱即用、kernel 6.x 全支援 cgroup v2。想了解方案細節,可以到 ncse.tw 查看。

需要穩定的雲端主機?

NCSE Network 提供企業級 VPS,7 天免費試用,臺灣是方電訊機房,99% SLA 保證。

查看 VPS 方案 →