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