本文已發布超過一年。較舊的文章可能包含過時的內容。請檢查頁面中的資訊自發布以來是否已變得不正確。
伺服器端套用非常棒,而且您應該使用它
伺服器端套用 (SSA) 現在已經 GA 幾個版本了,我發現自己在許多對話中,建議不同情況下的人員/團隊使用它。因此,我想寫下其中的一些原因。
SSA 的明顯(和不太明顯的)好處
從各種事物切換到伺服器端套用後,您可以獲得的改進/優點列表!
- 與用戶端套用(即,普通的
kubectl apply
)相比- 當您不小心與另一個參與者爭奪欄位的值時,系統會給您衝突!
- 當與
--dry-run
結合使用時,絕對不會意外運行用戶端模擬運行而不是伺服器端模擬運行。
- 與手動滾動修補程式相比
- SSA 修補程式格式非常自然易於編寫,沒有奇怪的語法。它只是一個常規物件,但您可以(並且應該)省略您不關心的任何欄位。
- 舊的修補程式格式(「策略合併修補程式」)是臨時的,並且仍然存在一些錯誤;JSON-patch 和 JSON 合併修補程式無法處理 Kubernetes API 中常見的某些情況,即具有應根據「名稱」或其他識別欄位遞迴合併的項目的列表。
- 現在還有很棒的 go 語言程式庫支援,用於以程式方式建構套用呼叫!
- 您可以使用 SSA 通過將您不「擁有」的欄位設定為
null
來顯式刪除它們,這使其成為所有舊修補程式格式的功能完整替代品。
- 與調用 kubectl 相比
- 您可以從任何語言使用 apply API 呼叫,而無需調用 kubectl!
- 如上所述,Go 程式庫具有專用機制,使這一切變得容易。
- 與 GET-modify-PUT 相比
- (這個比較複雜,如果您從未編寫過控制器,可以跳過它!)
- 要正確使用 GET-modify-PUT,您必須在其他人以任何方式修改了您的 GET 和 PUT 之間的物件的情況下,處理並重試寫入失敗。當發生這種情況時,這是一種「樂觀並發失敗」。
- SSA 將此任務卸載到伺服器 - 您只需在發生衝突時重試,並且您可以獲得的所有衝突都是有意義的,例如當您實際上試圖從系統中的另一個參與者那裡奪走欄位時。
- 換句話說,如果 10 個參與者同時執行 GET-modify-PUT 循環,則 9 個將獲得樂觀並發失敗並且必須重試,然後 8 個,依此類推,在最壞情況下總共嘗試 50 次 GET-PUT(對於 N 個同時進行更改的參與者,這是 .5N^2 次 GET 和 PUT 呼叫)。如果參與者改為使用 SSA,並且更改實際上並未在特定欄位上發生衝突,那麼所有更改都可以按任何順序進行。此外,SSA 更改通常可以在完全沒有 GET 呼叫的情況下完成。對於 N 個參與者來說,只有 N 個 apply 請求,這是一個巨大的改進!
如何使用 SSA?
使用者
使用 kubectl apply --server-side
!很快我們 (SIG API Machinery) 希望將其設為預設值,並完全刪除「用戶端」套用!
控制器作者
這裡有兩個主要類別,但對於這兩者,當使用 SSA 時,您可能應該強制衝突。這是因為您的控制器可能不知道當系統中的某些其他實體對特定欄位有與您的控制器不同的期望時該怎麼辦。(請參閱 CI/CD 部分!)
使用 GET-modify-PUT 順序或 PATCH 的控制器
這種類型的控制器 GET 物件(可能來自 watch),修改它,然後 PUT 回去以寫入其更改。有時它會建構自訂 PATCH,但語義是相同的。大多數現有控制器(尤其是樹內控制器)都像這樣工作。
如果您的控制器是完美的,那就太好了!您無需更改它。但是,如果您確實想更改它,您可以利用新用戶端程式庫的提取工作流程 - 即,get 現有物件,提取您現有的期望,進行修改,然後重新 apply。對於許多計算最小 API 變更的控制器,這將是對現有實作的次要更新。
此工作流程避免了意外嘗試擁有物件中每個欄位的失敗模式,如果您只是 GET 物件、進行更改,然後 apply,就會發生這種情況。(請注意,伺服器會注意到您這樣做了並拒絕您的更改!)
重建控制器
這種類型的控制器在 SSA 之前並非真正可行。這裡的想法是(每當某些事情發生變化等時)從頭開始重建物件的欄位,使其成為控制器希望的樣子,然後 apply 更改到伺服器,讓它找出結果。我現在建議新的控制器從這種方式開始 - 說出您希望物件看起來像什麼,比說出您希望它如何更改要容易得多。
用戶端程式庫預設支援此操作方法。
唯一的缺點是,即使實際上物件已經符合控制器的期望,您最終也可能會向 API 伺服器發送不必要的 apply 請求。如果偶爾發生一次,這沒關係,但對於極高吞吐量的控制器,這可能會導致叢集的效能問題 - 尤其是 API 伺服器。空操作寫入不會寫入儲存體 (etcd) 或廣播給任何監視器,因此這並不是什麼大問題。如果您仍然擔心這一點,那麼今天您可以使用上一節中解釋的方法,或者您現在仍然可以這樣做,並等待額外的用戶端機制來抑制零變更套用。
為了避免這個缺點,為什麼不 GET 物件,並且僅在物件需要時才發送您的 apply?令人驚訝的是,它沒有太大幫助 - 對於 API 伺服器來說,空操作 apply 比額外的 GET 工作量多不了多少;而更改事物的 apply 比具有先前 GET 的相同 apply 更便宜。更糟糕的是,由於它是一個分散式系統,因此在您的 GET 和 apply 之間可能會發生某些變化,從而使您的計算失效。相反,您可以對從快取中檢索的物件使用此最佳化 - 然後它確實會減少系統的負載(以需要更改且快取有點延遲時的延遲為代價)。
CI/CD 系統
持續整合 (CI) 和/或持續部署 (CD) 系統是一種特殊的控制器,它正在執行類似於從原始碼控制(例如 Git 儲存庫)讀取資訊清單並自動將它們推送到叢集中的操作。也許 CI/CD 流程首先從範本產生資訊清單,然後運行一些測試,然後部署更改。通常,使用者是將更改推送到原始碼控制的實體,儘管情況不一定總是如此。
某些系統(例如此系統)持續與叢集協調,其他系統可能僅在更改推送到原始碼控制系統時才運行。以下注意事項對於兩者都很重要,但對於持續協調的類型更重要。
CI/CD 系統實際上是控制器,但為了 apply 的目的,它們更像是使用者,並且與其他控制器不同,它們需要注意衝突。原因
- 從抽象意義上講,CI/CD 系統可以更改任何內容,這意味著它們可能與外部的 任何 控制器發生衝突。控制器強制衝突的建議假設控制器僅更改有限數量的內容,並且您可以合理地確定它們不會與其他控制器就這些內容發生衝突;對於 CI/CD 控制器而言,情況顯然並非如此。
- 具體範例:想像一下 CI/CD 系統希望某些 Deployment 的
.spec.replicas
為 3,因為這是簽入原始碼的值;但是,還有一個 HorizontalPodAutoscaler (HPA) 以相同的部署為目標。HPA 計算目標規模並決定應該有 10 個副本。哪個應該獲勝?我剛才說過,大多數控制器(包括 HPA)都應該忽略衝突。HPA 不知道它是否已錯誤啟用,並且 HPA 無法方便地將錯誤通知使用者。 - CI/CD 系統發生衝突的另一個常見原因是,當它試圖覆蓋系統管理員/SRE/值班開發人員放置在那裡的熱修復(手動滾動修補程式)時。您幾乎肯定不想自動覆蓋它。
- 當然,有時 SRE 會進行意外更改,或者開發人員會進行未經授權的更改 - 您確實想要注意並覆蓋這些更改;但是,CI/CD 系統無法區分這最後兩種情況。
希望這能讓您相信 CI/CD 系統需要錯誤路徑 - 一種將這些衝突錯誤反向傳播給人類的方式;實際上,它們應該已經具備此功能,當然持續整合系統需要某種方式來報告測試失敗。但也許我還可以說一些關於人類如何處理錯誤的事情
拒絕熱修復:CI/CD 系統的(人類)管理員觀察到錯誤,並手動強制套用有問題的資訊清單。然後 CI/CD 系統將能夠成功套用資訊清單並成為共同擁有者。
可選:然後管理員套用空白資訊清單(僅物件類型/命名空間/名稱)以放棄他們成為管理員的任何欄位。如果省略此步驟,則管理員很有可能會最終擁有欄位並導致不必要的未來衝突。
注意:為什麼是管理員?我假設通常推送到 CI/CD 系統和/或其原始碼控制系統的開發人員可能沒有直接推送到叢集的權限。
接受熱修復:有問題的更改的作者看到衝突,並編輯他們的更改以接受生產環境中運行的值。
接受然後拒絕:與接受選項相同,但在套用該資訊清單之後,並且 CI/CD 佇列再次擁有所有內容(因此沒有衝突),重新套用原始資訊清單。
我也可以想像 CI/CD 系統允許您以某種方式將資訊清單標記為「強制衝突」 - 如果有這方面的需求,我們可以考慮製作一種更標準化的方法來做到這一點。嚴格的版本(讓您準確聲明您打算強制執行的衝突)將需要 API 伺服器的支援;在沒有這種支援的情況下,您可以製作第二個資訊清單,其中僅包含欄位的子集。
未來工作:我們可以想像一個特別先進的 CI/CD 系統,它可以解析
metadata.managedFields
資料,以查看他們正在與誰或什麼衝突,在哪些欄位上衝突,並決定是否忽略衝突。實際上,此資訊也顯示在任何衝突錯誤中,儘管可能不是以易於機器解析的格式顯示。我們 (SIG API Machinery) 主要沒想到人們會想要採用這種方法 - 因此我們很想知道是否實際上人們想要/需要這種方法暗示的功能,例如在 apply 時請求覆蓋某些衝突但不覆蓋其他衝突的能力。如果這聽起來像是您希望為自己的控制器採用的方法,請來與 SIG API Machinery 交流!
apply 愉快!