這裡咬人的行為是指定的,而不是偶然的: YAML 1.2 規範 定義如何將非引用標量解析為類型。
我曾經發布過一個部署,將服務固定到版本 1.1 當設定檔直白地說 1.10. 不是錯字。尋找並替換並不糟糕。我讀過四次的 YAML 檔案是這樣說的:
image_tag: 1.10
解析器將數字交給了我的部署腳本 1.1. 因為 1.10 isn't 是 YAML 的版本字串 - it's a 浮動字面量,浮點數 don't 保持尾隨零。十變成一分一。該文件是 100% 有效的 YAML。行話者很高興。 CI 是綠色的。錯誤的容器壞了。
That'這是沒有人告訴你的關於 YAML 驗證的事情: &引用;有效&引用;與"不同;正確。&引用; 僅回答是/否的語法檢查器正在回答簡單的問題。困難的問題 - 實際中斷的問題 - 是 我的 YAML 變成了什麼? 因為 YAML 不是 config 格式,所以 it'是一個採用 config 格式和#39; 的類型推斷引擎;它的衣服,它會對您的資料做出您從未要求它做出的決定。
的 yaml 驗證器 在 toolz。dev 上回答這兩個問題。它告訴您文件是否解析,然後向您顯示解析結果為 JSON - 您的工具將收到的實際資料結構。後半部分本來可以拯救我。 "image_tag": 1.1 在輸出窗格中不可能被誤讀。
TL;DR: 將您的 YAML 貼到 yaml 驗證器 並閱讀 JSON 輸出,不只是綠色複選標記。 That's 類型強制顯示的地方:
1.10→1.1,0123→123,未引用的值默默地變成數字、布林值或空值。它完全在瀏覽器中的 js-yaml (YAML 1.2) 上運行,因此 Kubernetes 的秘密和資料庫憑證永遠不會離開您的電腦。引用任何必須保留字串的內容。如果您需要將結果與 JSON 配置進行比較,則 json 格式化程式 和 JSON 差異 從那裡接。
YAML 驗證器實際上檢查什麼?
兩件不同的事情,它'值得將它們分開,因為它們的失敗方式不同。
語法驗證 問:這個文字可以解析嗎?空格所屬選項卡、冒號後缺少的空格、正文為 't 縮排的區塊標量、未閉合的引號。這些是 大聲 失敗。你的解析器拋出,你的管道變紅,你在兩分鐘內修復它。煩人,不危險。
語義檢查 問:它是什麼解析的 進入?這就是安靜故障所在。該文件有效。管道是綠色的。這個值根本不是你以為你寫的東西。直到生產表現奇怪,然後沒有人發現,然後沒有人's 查看設定文件,因為設定檔是 "精細。&引用;
大多數線上 YAML 跳棋只執行第一個。 toolz。dev 驗證器執行第一個操作,然後遞給您第二個操作 - 解析的文件,呈現為 JSON,就在您的輸入旁邊。養成閱讀該窗格的習慣。 It'是"之間的區別;文件格式良好"和"文件就是我的意思。"
哪些 YAML 錯誤實際上會破壞建置?
這裡'這是真正出現的東西,根據我一生中每一次花費我多少錢來排名。低於我的每一種行為都會被驗證 js-yaml 4,這是 toolz。dev 驗證器運行的解析器,並且實現了 亞姆爾1.2 規格。
1. 選項卡。始終選項卡。
YAML 禁止使用選項卡字元進行縮排。不是&引用;勸阻和引用; - 禁止。規格明確,錯誤訊息直接令人耳目一新:
tab characters must not be used in indentation
這種情況不斷發生的原因是選項卡是不可見的。您的編輯器向您顯示一個對齊良好的文件;解析器看到一個控製字元。從來源修復它:將編輯器設定為插入空格,然後開啟 "渲染空白並引用;對於 YAML 檔案。每個層級有兩個空格,這是每個主要 YAML 生態系統所確定的約定。
2。 重複的按鍵
database:
host: localhost
port: 5432
host: production-db.example.com
host 出現兩次。會發生什麼事?它完全取決於您的解析器,這是一個關於配置格式的可怕句子。
js-yaml 投擲: duplicated mapping key.好的。那'是你想要的行為,它'是 toolz。dev 驗證器將向您展示的內容。但 PyYAML--這就是 Ansible 和許多 Python 工具所坐的--默默地接受了 最後的 價值並繼續前進。沒有警告。您的資料庫主機現在是最後一個副本所說的任何內容,在一個長文件中,您合併得很糟糕,它可能距離您'的位置有三百行;正在尋找。
即使您的生產工具接受配置,這也是透過嚴格驗證器運行配置的最佳論點。驗證器 's 更嚴格 比你的運行時是一個發現錯誤的驗證器。
3。 類型強制-那是讓我著迷的
YAML 從未引用的標量推斷類型。它非常有信心,但你的意圖常常是錯誤的:
| 你寫了 | 你的意思是 | YAML 1.2 為您提供 |
|---|---|---|
version: 1.10 |
字串"1.10" | 漂浮物 1.1 |
pin: 0123 |
字串"0123" | 整數 123 |
port: "8080" |
數字8080 | 字串 "8080" |
enabled: true |
布爾 | 布爾 true - 正確的 |
value: |
也許是空字串? | null |
value: ~ |
一個波形符 | null |
那個 0123 行是 I'd 人身上的刺青。郵遞區號、PIN 碼、帳號、零填充 ID - 您出於某種原因寫下的每個前導零都會被吃掉。引用它們。
從未讓我失望過的規則: 如果該值是識別碼、版本、程式碼或任何您'從不進行算術運算的內容,請將其放入引號中。 連接埠和副本計數可以保持裸露。一切只是 看起來 數字應該是 "quoted".
4. 依賴版本的布林值(又稱挪威問題)
這確實臭名昭著,細節比迷因更重要。
在 亞姆爾 1.1,布林類型接受 yes, no, on, off, y, n,以及它們的資本化 true 和 false. 所以 country: NO - 挪威's ISO 國家代碼 - 解析為布林值 假的. 在 亞姆爾1.2,這只是清理了這個 true 和 false 是布林值; NO 只是字串 "NO".
這意味著相同的文件 在不同的工具中意味著不同的東西:
country: NO
feature_flag: on
- js-yaml 4(YAML 1.2,以及此驗證器使用的內容):
{"country": "NO", "feature_flag": "on"}- 字串。 - 吡喃甲醛(YAML 1.1):
{"country": False, "feature_flag": True}- 布爾。
相同的位元組。不同的數據。如果您的 CI 在 Node 服務消耗的配置上運行 Python 連結器,則有兩個解析器對您的檔案有不同意見,並且它們都沒有錯。
這也是 GitHub Actions' 的起源;最奇怪的怪癖: on: 每個工作流程開始時的關鍵是 a 布爾 對於 YAML 1.1 解析器,在 Python 中 lint 工作流程檔案的腳本會找到一個名為 的金鑰 True 代替 on. 引用("on":) 是合法的並且修復了它。
防守動作與之前相同: 引用它. country: "NO" 手段 "NO" 在曾經存在的每個解析器中。
5。 塊標量縮排
description: |
This is not indented
的 | (字面意思)和 > (折疊)區塊標量需要其內容相對於鍵縮排。未縮排的內容會立即結束區塊,解析器開始將您的散文讀取為 YAML 鍵,這會產生似乎與實際錯誤無關的錯誤訊息。
當您'在這裡時,值得了解咀嚼指標: | 保留一條尾隨換行符, |- 剝去它, |+ 保留所有這些。如果您'重新嵌入私鑰或腳本以及下游抱怨拖尾換行符的內容,這就是您的旋鈕。
6。 未引用的特殊字元
未引用值內的冒號空間結束該值並啟動一個新鍵。這會咬合錯誤訊息和 URL:
message: Error: file not found # parse error
regex: [a-z]+ # parsed as a LIST, not a string
time: "22:22" # quote it — in YAML 1.1 this was base-60!
[, {, #, &, *, !, |, >, %, @ 標量開始時都有意義。先引用,然後再提問。
如何在 Toolz。dev 上驗證 YAML?
- 打開 yaml 驗證器. 沒有帳戶,沒有上傳。
- 貼上您的文件。 Helm 值檔案、docker-compose、工作流程 - 無論如何'行為不端。
- 點擊驗證。 錯誤會隨著確切的情況而回來 行和列 來自解析器,加上解析器和#39;自己的原因字串((
bad indentation of a mapping entry,duplicated mapping key,等等)。 - 讀取 JSON 輸出窗格。 這是人們跳過的步驟,它'是重要的。掃描它以查找您關心的價值觀。是
image_tag字串還是數字?該連接埠被引用嗎?空值是否變為null? - 修復,重新驗證。 錯誤可能會相互掩蓋 - 解析器會在第一個解析器處停止並#39;t 恢復,因此修復一個解析器有時會顯示另外兩個。那'這很正常,並不是事情變得更糟的跡象。
一個已知的限制,明確指出
驗證器目前解析 a 單一 YAML 文件。 如果您貼上多文檔檔案 - 多個 Kubernetes 清單以分隔 --- 在一個文件中,這是一種極其常見的模式 - 它將報告:
expected a single document in the stream, but found more
That'解析器是正確的,而不是檔案被破壞。今天的解決方法是單獨驗證每個文件:將所有內容貼到上面 ---,檢查一下,然後貼上下一個區塊。多文檔支援出現在我的清單中,正是因為 Kubernetes 用戶立即點擊了該列表,並且 I'寧願告訴你差距,也不願讓你在事件中發現它。
YAML 與 JSON:我什麼時候該使用哪一個?
YAML 1.2 是 JSON 的嚴格超集 - 每個有效的 JSON 文件都是有效的 YAML,這就是為什麼驗證器可以向您提供 JSON 輸出。但這些格式具有相反的性質。
| 亞姆爾 | 傑森 | |
|---|---|---|
| 結構定義 | 縮排(空白有效) | 大括號和大括號(明確) |
| 評論 | 是的(#) |
不 |
| 類型推論 | 攻擊性 - 推斷數字、布林值、空值、日期 | 無 - 引號表示字串,始終 |
| 多文檔 | 是的(--- 分隔符號) |
不 |
| 重複使用 | 錨 (&)、別名(*)、合併鍵(<<) |
沒有 |
| 故障模式 | 無聲的誤解 | 大聲解析錯誤 |
| 最好在 | 人類編寫和編輯的文件 | 數據機交換 |
這種交易是真實的,而且是雙向的。 YAML'可讀性和評論正是基礎設施配置存在的原因 - 沒有人願意在沒有評論的情況下維護 JSON 中的 400 行 Kubernetes 清單。 JSON'完全缺乏聰明才智正是 API 使用它的原因: "1.10" 是 "1.10" 而且沒有什麼好討論的。
我的規則: YAML 用於人們編輯的文件,JSON 用於資料機器傳遞。 當 YAML 檔案由程式產生而不是由人鍵入時,'s 氣味 - 機器產生的配置不會得到 YAML's 的好處及其所有風險。
如果你'在兩者之間移動, JSON 到 YAML 轉換器 處理變換,並且 json 格式化程式 會收拾另一邊。
什麼是錨和別名?我應該使用它們嗎?
YAML 讓您定義一個區塊一次並重複使用它。錨與 &,參考 *,合併到地圖中 <<:
defaults: &defaults
adapter: postgres
host: localhost
port: 5432
development:
<<: *defaults
database: myapp_dev
test:
<<: *defaults
database: myapp_test
兩者 development 和 test 適配器、主機和連接埠合併出來。它'確實有用,js-yaml 處理它 - 我驗證了合併解析正確。
不過有兩個警告。
首先, 合併鍵是 YAML 1.1 擴充,不是 YAML 1.2 核心的一部分。支持是廣泛的,但不是普遍的,而且--吸引人的支持-- GitHub Actions 不支援它們。 工作流程檔案中的錨不會執行您想要的內容。在依靠此之前檢查您的消費者。
其次,錨點使下一個人更難閱讀文件,在配置中,下一個人通常是凌晨 2 點的您,我將它們用於真正重複的區塊,而不是為了聰明。
而我們'則處於 YAML 的危險面:該格式支援某些解析器用來建構任意物件的自訂標籤。 Python's yaml.load() 眾所周知,這種方式是可以利用的,這就是原因 yaml.safe_load() 存在以及為什麼您應該始終在來自團隊外部的任何 YAML 上使用它。 js-yaml's load() 在 v4 中預設是安全的(它贏得了't 構造任意類型),這裡需要擔心的事情少了一件。
我該如何先停止編寫損壞的 YAML?
預防勝過驗證,其中大部分是編輯器配置:
- 兩個空格,從不製表。 按文件類型設定它,以便您可以'忘記。
- 開啟空白渲染 對於
.yml/.yaml. 如果您可以看到選項卡,則您獲勝並#39;t 提交選項卡。 - 安裝 YAML 語言伺服器。 針對 Kubernetes、GitHub Actions 和 docker-compose 模式的即時模式驗證會捕獲一整類錯誤,語法驗證可以't:使用拼字錯誤的金鑰進行有效的 YAML。
- 有疑問時預設報價。 不必要的報價的成本為零。缺少一個的成本就是部署。
- 在推動之前驗證1、CI 失敗後不行。貼上到瀏覽器標籤需要八秒鐘;失敗的管道需要八分鐘。
- 對於 Kubernetes,分層檢查。 語法驗證捕獲結構;
kubectl apply --dry-run=client捕捉模式。他們發現了不同的錯誤,而你想要兩者。
這個習慣實際上改變了我的事情:當配置驅動的部署做了一些莫名其妙的事情時, 在查看其他內容之前先查看解析的輸出。 不是文件。解析後的輸出。該文件是一個關於您的意思的故事。解析後的輸出就是實際發生的情況。
那'同樣的本能支配著我的一切 API調試工作流程 - 閱讀數據,而不是程式碼 - 它既適用於配置,也適用於回應。如果您想更廣泛地了解該工具箱中的其他內容,那麼 編碼工具指南 覆蓋它。
常見問題
為什麼我的 YAML 會驗證但仍然會破壞我的部署?
因為語法有效性和語意正確性是不同的。 YAML 從未引用的值推斷類型,因此 1.10 成為浮標 1.1, 0123 成為整數 123,空值變為 null - 全部在一個完全有效的文件中。讀取解析的 JSON 輸出,而不僅僅是通過/失敗結果,並引用必須保留字串的任何值。
為什麼 YAML 將我的版本號變成不同的號碼?
1.10 是 YAML 的浮點文字,浮點不保留尾隨零,因此它解析為 1.1。 必須引用任何版本、建置編號或零填充識別碼: version: "1.10". 這是最昂貴的 YAML 錯誤之一,因為檔案看起來正確且解析成功。
YAML 中的挪威問題是什麼?
在 YAML 1.1 中,這些值 no, NO, off, 和 yes 是布林值,所以挪威's 國家代碼 NO 解析為 false。 YAML 1.2 修復了這個 - 僅 true 和 false 是布林值 - 但許多工具(特別是 Ansible 使用的 PyYAML)仍然實作 1.1。因此,同一個文件在不同的工具中可能意味著不同的東西。引用值(country: "NO")讓它到處都是一條繩子。
我可以使用選項卡在 YAML 中縮排嗎?
否。YAML 規範禁止縮排中的選項卡字符,解析器會拒絕它們,並出現諸如 " 縮排中不得使用選項卡字符。" 配置編輯器以插入 YAML 檔案的空格 - 每個層級有兩個空格是標準約定。
Toolz。dev YAML 驗證器是否支援多文件檔案?
目前沒有。它驗證一個文檔,因此包含多個 Kubernetes 的文件顯示為分隔 --- 退貨和報價;期望流中有一個文檔。"分別驗證每個文檔作為解決方法。計劃提供多文檔支援。
YAML 中允許重複金鑰嗎?
該規範規定映射鍵必須是唯一的,但解析器在實踐中不同意。 js-yaml - 此驗證器使用 - 拋出 "重複映射鍵"錯誤。 PyYAML 靜默地保留最後一個值,這意味著重複項可以在沒有任何警告的情況下靜默地覆蓋您的配置。在運行時靜默接受之前,透過嚴格的驗證器運行您的配置會捕獲它。
在線上驗證 Kubernetes 的秘密和憑證安全嗎?
使用 toolz。dev 驗證器,是的 - 解析完全透過 JavaScript 在您的瀏覽器中進行,並且不會將任何內容傳輸到任何伺服器。您可以在驗證時開啟瀏覽器's Network 標籤並觀察是否提出請求,從而自行確認這一點。在將基礎架構配置貼上到任何線上工具之前,請將其套用相同的檢查。
。yml 和 。yaml 有什麼不同?
沒有任何功能 - 每個 YAML 解析器都認可這兩個擴充。官方建議是 .yaml; .yml 從三字元擴展時代倖存下來,並且仍然非常常見(docker Compose 和 GitHub Actions 都預設為它)。選擇一個並在專案中保持一致。
如何將 YAML 轉換為 JSON?
將 YAML 貼到驗證器中並讀取輸出窗格 - 它將解析的文件呈現為 JSON,即轉換。由於 YAML 1.2 是 JSON 的超集,因此每個有效的 YAML 文件都有一個 JSON 等效項,但首先應用類型推斷,因此不引用 1.10 到達as 1.1 和 0123 作為 123. 如果您需要將這些值保留為字串,請先引用它們。
如何根據模式驗證 YAML?
此驗證器檢查語法並顯示解析的結果,但它不會針對模式進行驗證 - 這是一個單獨的檢查,確認您的金鑰和值類型與 Kubernetes 或 GitHub Actions 等工具所期望的相符。對於模式驗證,請在編輯器中使用 YAML 語言伺服器,或模式感知 CLI,例如 kubeconform 對於庫伯內特斯或 kubectl apply --dry-run=client. 語法和模式驗證會捕獲不同的錯誤,因此同時執行這兩個錯誤。



