使用自定義控制器解決Kubernetes配置問題

作者: Joel Speed
Joel是Pusher的雲基礎架構工程師,致力於構建其內部Kubernetes平臺,有四年多的DevOps工作經驗。 最近一直專注於擴充套件Kubernetes的能力,構建鑑權和自定義資源控制器以改善平臺上的開發人員體驗的專案。Joel熱衷於自動擴充套件,部署流水線,身份驗證和授權。 他還為Pusher工程團隊建立並維護了一個ChatOps機器人Marvin。
【編者的話】我們都知道,Kubernetes中的應用配置儲存在ConfigMaps和Secrets中,但是它們都沒有版本化,也沒有控制迴圈。然而如果做了多次變更,由於沒有版本控制,不知道到底發生了什麼,也無法回滾,這將是一件多麼可怕的事情。本文作者就此問題提出了一個解決方案——Wave,相信也可以解決你遇到的同樣的困境。
兩年前, ofollow,noindex" target="_blank">Pusher 開始構建一個基於Kubernetes的內部平臺。當我們從單一產品轉變為多產品的公司時,我們希望幫助產品團隊花更少的時間處理共享問題(例如基礎設施),能夠更加專注於實現產品的業務邏輯。
在這段時間裡,我們的平臺團隊已經解決了許多Kubernetes沒有解決的問題。直到最近,我們還沒有解決配置問題。遺憾的是,與大多數資源不同,普通的Kubernetes叢集對配置(儲存在ConfigMaps和Secrets中)的更改不會觸發對叢集中正在執行的應用程式狀態的更改。
我們平臺上執行的許多應用程式,包括自研和第三方的,都不能動態地重新載入其配置。當配置更新時,雖然可以更新掛載到容器中的檔案(如果它們來自ConfigMap),但應用程式不會監視更改,只會在重新啟動程序時載入新配置,或者更通常的情況是,新的Pod已建立並且現有Pod已終止。
由於Kubernetes通常會為你協調所需的狀態,因此典型的使用者可能會認為更新ConfigMap或Secret會使應用程式載入新的配置。然而情況並非如此,使用者很容易更新ConfigMap或Secret,而不替換掛載配置的Pods,從而導致應用程式正在使用的配置與更新後的所需配置不同。
我們的配置困境
通常在Kubernetes中,當你更新所需狀態時,會有一些控制迴圈監視全域性的狀態,並調整狀態以使其與你所需的更改保持一致。 在部署中更新Pod的模板時,部署控制器將使用新的Pod替換所有現有的Pod,以匹配更新的規範。
Kubernetes中的配置儲存在ConfigMaps和Secrets中,它們通過檔案或環境變數掛載到Pod中,以允許容器程序讀取資料。但是,這種方法存在一個問題。 ConfigMaps和Secrets都沒有版本化,也沒有控制迴圈。更新ConfigMap將更新Pod中掛載的檔案,而更新Secrets不會觸發群集內的任何更改。
Pusher平臺上的產品依賴於一個稱為Bridge的關鍵元件。Bridge是我們入口層的一部分,將流量路由到後端服務,並在此過程中處理大量Pusher協議邏輯。遺憾的是,Bridge不能動態地重新載入其配置。
該元件對於執行在平臺上的所有產品(Chatkit、Beams、TextSync)的執行都是必不可少的,當它宕機時,其他一切都會隨之而去。
在過去的兩年裡,我們在平臺上沒有發生太多的事故(誠然,我們只有GA幾個月),但是當我們遇到停機時,事後分析幾乎總是得出相同的結論。在Bridge中執行的配置不是它所掛載的ConfigMap中的配置。
由於對Kubernetes叢集進行更改,大多數事件都是由一個基礎架構團隊觸發的。 我們以不可變的方式執行所有機器,因此,當我們想要進行更改時,我們會替換叢集中的每個節點,反過來又替換每個Pod。新的Pods將從該點存在的配置開始執行。
我們發現,儘管我們已經採用了相應的程式,但在更新配置時,我們元件的Pod並不總是被替換,在某些情況下,只有在應用了破壞的配置後兩週才更換Pod。 如果你曾經遇到過同樣的情況,你就會明白,如果不知道發生了什麼更改,並且由於沒有版本控制,無法回滾,這是一件多麼可怕的事情。
我們是如何解決問題的
我們目前正在使用我們的GitOps專案 Faros 自動化我們的部署流水線。我們的平臺服務團隊擁有許多服務,無法動態重新載入配置並引發對專案的擔憂。為了能夠在git merge上同步配置更新,他們需要保證實際部署配置更新,並且在配置中斷的情況下,會有人通知git還原他們剛剛部署的更改。
這就是我們的新專案 Wave ,一個使用 Kubebuilder 構建的自定義控制器的用武之地。Wave的作用是確保在更新ConfigMap或Secrets時,任何部署安裝該ConfigMap或Secret都會替換它的所有Pod。Wave有效地推動了Kubernetes內建 Deployment 控制器並使其執行滾動更新,刪除執行舊配置的Pod並使用更新的配置建立新Pod。
Wave的工作原理
與其他Kubernetes控制器一樣,Wave訂閱Kubernetes API for Deployment物件中的事件。 這意味著,只要對部署執行操作(建立/讀取/更新/刪除),Wave就可以處理部署並檢視是否需要更改。
Wave所做的第一件事就是檢查部署中是否存在 Annotation (wave.pusher.com/update-on-config-change:true)。 如果Annotation不存在,Wave將忽略該部署。 這使得Wave成為一個選擇加入控制器並從Wave中受益,使用者必須手動分配他們的部署由Wave管理。 因此,可以安全地部署到現有的Kubernetes叢集,而無需擔心它會突然開始干擾不需要其更新觸發功能的已部署工作負載。
其次,Wave解析部署並查詢對掛載到部署建立的Pod中的ConfigMaps和Secrets的引用。 然後它獲取每個掛載的ConfigMaps和Secrets,並使用可重現的演算法,建立當前配置的雜湊值。 雜湊表示所有已掛載的ConfigMaps和Secrets的配置,只有在新增新的掛載,刪除或修改掛載中的資料中的欄位時才會更改。
然後將此雜湊作為Annotation放置在部署中的 Pod模板 上。 這是我們利用內建Kubernetes Deployment控制器的地方。通過修改Pod模板的元資料,Deployment控制器將其計為更新並開始處理Deployments更新策略。
總之,每當更新配置時,Wave計算的雜湊將更改,Wave將更新部署的Pod模板上的雜湊,然後部署控制器將讀取更新策略並通過建立新的ReplicaSet開始新配置的部署。
由於Wave還訂閱了ConfigMaps和Secrets的事件,因此它可以使用 Owner References 來跟蹤哪些部署正在掛載哪些ConfigMaps和Secrets,並且每當其中一個更新發生時,就會協調父部署更新其雜湊值。但是,我們不使用 Owner References
的垃圾收集部分。一旦部署標記為刪除,Wave將刪除所有所有者引用,否則部署的ConfigMaps和Secrets也將被刪除。
下一步的計劃
Wave工作尚未完成!目前它僅支援部署,將來我們計劃為Wave新增一項功能,以觸發Daemonsets和Statefulsets的更新。
我們已將Wave部署到我們的系統中,並在我們的團隊內和整個組織中實現了大量部署。 自從開始使用它以來,我們一直在更快地捕獲破壞的配置(沒有更多的生產事件)並且能夠減少大量的手動部署過程(部署配置,慢慢重啟pod)。
該專案已經解決了我們最長期存在的問題之一。我們現在部署時更有信心,並且知道所需配置的更新實際上將在應用後立即執行。
原文連結: Solving Kubernetes Configuration Woes with a Custom Controller
譯者:Mr.lzc,軟體工程師、DevOpsDays深圳核心組織者,目前供職於華為,從事雲端儲存工作,以Cloud Native方式構建雲檔案系統服務,專注於K8s、容器及微服務領域。