本文同步刊登於個人技術部落格,有興趣關注更多 Kubernetes、DevOps 相關資源的讀者,請務必追蹤從零開始的軟體工程師之旅,喜歡的話幫我按讚分享、歡迎留言、或是許願想要看的文章。
如果有技術問題也可透過粉絲專頁 討論,技術方面諮詢免錢、需要動手做另計 XD。
Borg, Omega and Kubernetes
這是原文完整版本。好讀版請見 Borg Omega and Kubernetes 前世今生摘要
完整英文原文請見 Borg, Omega, and Kubernetes - Lessons learned from three containermanagement systems over a decade
前車之鑑 (Things to avoid)
Google 在開發這些系統時,學到一些最好別做的事情,我們提供這些資訊,所以其他人可以避免重複的錯誤,而把犯錯的成本放在新的錯誤上。
不要使用容器管理系統管理 Port number
Borg 上所有容器都使用宿主機器的 IP,
- Borg 在分配時便指派給每個容器各自的 port number。每個容器移動到新機器時會取得一個新的 port number,有時在本機重啟也會拿到新的。
- 這點表示既存的網路服務 (例如 DNS) Google 都必須自行改動並使用自維護的版本;
- 服務的客戶端並不能主動獲得服務的 port number 資訊,需要被動告知;
- port number 也不能放在 URLs 中,還需要額外的轉址機制;
- 所有基於 IP 運作的工具都需要改成使用 IP:Port。
有鑑於此,Kubernetes 上每個 Pod 分配一個 IP,讓
- 應用的身分符合網路身分 (IP address),
- 讓現成的應用更容易在 Kubernetes 上執行,應用可以直接使用靜態的常規 port number (例如 HTTP 流量使用 80 port),
- 現有的工具也可以直接使用,例如網段切分 (network segmentation)、帶寬節流 (bandwidth throttling) 與控制。
- 所有的公有雲都提供每個 Pod 一個 IP 的網路基底,
- 在實體機器 (bare metal),也可以使用軟體定義網路 (Software defined network) 的 overlay 網路,或是設定 L3 路由來配置機器上的 Pod IPs。
不要幫容器編號,使用 label 控制容器
容器建構變得方便後,使用者會產生大量容器,很快就需要一個群組管理的方法。
Borg 提供 job 給一組相同的 tasks (task 作為容器的名稱),一個 job 是一組向量集合 (compact vector) 可以指向一個或多個 tasks,將 task 從 0 開始遞增編號,
- 這個做法很直接也很方便,
- 但等稍後需要重啟容器時,Google 就後悔這個固定的設計,例如其中一個 task 死了需要重啟刀另外一台機器,原本的 task 向量的相同位置,現在需要做兩倍工作:找到新的副本,然後指向舊的副本以免需要除錯。
- 當向量集合中的 task 離開後,這個向量上就有很多空洞,這讓 Borg 上層的管理系統分配,跨級群的 job 時變得很困難。
- 這也造成 Borg 的 job 升級語法中出現許多危險且不可預期的交互 (當滾動升級時依照 index 順序重啟 task),而應用仰賴 task index (例如應用使用 index 執行資料級的 sharding 或 paritioning):如果應用使用 task index 來做 sharding,Borg 重啟時會移除鄰近的 tasks,導致資料不可用。
- Borg 也沒有好方法來增加來自應用的 metadata 到 task 上,例如角色 (e.g. 前端網頁)、或是滾動升級的狀態 (e.g. canary),工程師只好將 metadata 編碼,壓在 jobs 的名稱上,再使用正規表達式 (regular expression) 解碼。
Kubernetes 直接使用 label 來辨識容器群,label 是鍵值資料對 (key-value pair),包含可以辨識應用的資料,
- 一個 Pod 可能有 role=frontend 與 state=production 表示 Pod 是屬於 production 環境的前端網頁。
- label 可以動態增加,並使用自動化工具更改,個團隊可以自行維護一組各自的 label。
- 使用 label-selector 來取得一組物件 (e.g. stage==production && role==frontend)。
- 各組物件可以重複,一個物件也可以屬於多個物件組,
- 所以 label 也比靜態的物件清單更有彈性。
- 由於物件組都是動態查詢時產生的,任何時候都可以增加新的物件組。
- label-selector 是 Kubernetes 的群組機制,並藉此分界管理維運,又可以同時處理多個實體。
雖然有些使用情形,事先明確知道一組內有哪些 task 很有用 (例如靜態腳色指派、工作 partitioning 或 sharding),合理的 label 也能產生一樣的效果,只是應用 (或是其他外部管理系統) 就需要負責提供 labeling。label 與 label-selector 為雙邊提供最適的做法。
注意 Pod 的所有權
Borg 上 task 依賴 job,不會獨自存在,產生 job 時產生 tasks,這些 tasks 永遠連結特定的 job,刪除 job 同時刪除 tasks。這點很方便,但有一個缺陷,這個單一的群組機制,需要應對所有使用需求。例如 一個 job 需要儲存參數,提供給服務或是提供給應用,但不會一起提供,使用者就需要開發取代方案來協助處理 (e.g. DaemonSet 將 Pod 複製到所有節點上)
Kubernetes 中 Pod 的生命週期管理元件,例如 replication controller,使用 label selector 決定那些 Pod 要負責管理,
- 所以會有一個以上的 controller 認為他們都對某個 Pod 有管轄權,這樣的衝突需要透過明確設定避免的。
- 由於 label 的彈性也有許多優點,例如將 controller 與 Pod 解藕,表示再也不會有以往的孤兒 Pod (orphan) 或是認領的 Pod (adopt)。
- 考慮附載均衡服務,透過 label selector 選擇流量的端點,如果一個 Pod 行為有異,這個 Pod 只要移除對應的 label,Kubernetes service load balancer 就可以避免流量,輕易的被隔離開來,
- 但 Pod 本身還會存在,提供原地除錯。
- 同時,管理 Pod 的 replication controller 會增加一個 Pod 來取代行為有異的 Pod。
不要暴露 raw state
Borg、Omega、Kubernetes 的關鍵不同點是 API 的架構設計。
- Borgmaster 是一個集中單一的 (monolithic) 元件,可見所有的 API 行為,包含級群管理邏輯,例如 job 與 task 的狀態機制,並且使用基於 Paxos 算法的分散式儲存庫。
- Omega 除了儲存庫以外,沒有集中式的元件,儲存庫只儲存被動的狀態資訊,提供樂觀的平行控制 (optimistic concurrenty control):
- 所有邏輯與語法都推送到儲存庫的客戶端,
- 所以所有客戶端都可以讀取 Omega 的所有邏輯。
- 實務中 Omega 的元件都使用相同的客戶端程式庫,負責打包/解包資料結構、資料重送、並確保語法的一致性。
Kubernetes 選擇中間,
- 類似 Omega 元件化架構的架構,確保全域的常數、政策控管、資料轉型,來提供彈性與擴容性。
- Kubernetes 確保所有的儲存庫存取都透過 API server,API server 隱藏儲存庫的實作、負責物件的驗證、除錯、版本控制。
- 如同 Omega,Kubernetes 提供多種不同的客戶端 (特別給是開源的環境),
- 但是集中化的 API server,仍能確保相同的語法、恆量、與政策控管。
明日小結:Borg Omega and Kubernete,其他困難的開放問題