Categories
程式開發

Kubernetes投入生產的3年,我們得到的一些經驗教訓


我們從2017年開始基於1.9.4版本構建第一個Kubernetes 集群。 我們有兩個集群,一個集群在裸金屬的RHEL 虛擬機上運行,另一個集群在AWS EC2上運行。

現在,我們的Kubernetes 基礎設施平台由分佈在多個數據中心的400多台虛擬機組成。 該平台託管了高可用的關鍵任務軟件應用程序和系統,以管理具有近四百萬個活動設備的大型實時網絡。

Kubernetes 最終使我們變得更輕鬆,但是這個過程很艱難,是一種思維上的轉變。 不僅讓我們的技能和工具有了徹底的轉變,還讓我們的設計和思維也得到了徹底的轉變。 我們不得不採用多種新技術,並進行大量投資以擴展和提高我們的團隊和基礎架構的技能。

回顧Kubernetes 在生產環境中運行的這三年,我們記下了一些很重要的經驗教訓。

  1. Java應用程序的奇怪案例

在微服務和容器化方面,工程師傾向於避免使用Java,這主要是由於Java 臭名昭著的內存管理。 但是,現在情況發生了改變,過去幾年來Java 的容器兼容性得到了改善。 畢竟,大量的系統(例如Apache KafkaElasticsearch)在Java上運行。

回顧2017-18年度,我們有一些應用程序在Java 8上運行。 這些應用程序通常很難理解像Docker 這樣的容器環境,並因堆內存問題和異常的垃圾回收趨勢而崩潰。 我們了解到,這是由於JVM 無法使用的Linuxcgroupnamespace造成的,而它們是容器化技術的核心。

但是,從那時起,Oracle 一直在不斷提高Java 在容器領域的兼容性。 甚至Java 8的後續補丁都引入了實驗性的JVM 標誌來解決這些問題,XX:+UnlockExperimentalVMOptionsXX:+UseCGroupMemoryLimitForHeap

但是,儘管做了所有的這些改進,不可否認的是,Java 在內存佔用方面仍然聲譽不佳,與Python 或Go 等同行相比啟動速度慢。 這主要是由JVM 的內存管理和類加載器引起的。

現在,如果我們必須選擇Java,請確保版本為11或更高。 並且Kubernetes 的內存限制要在JVM 最大堆內存(-Xmx)的基礎上增加1GB,以留有餘量。 也就是說,如果JVM 使用8GB的堆內存,則我們對該應用程序的Kubernetes 資源限制為9GB。

  1. Kubernetes 生命週期管理:升級

Kubernetes 生命週期管理(例如昇級或增強)非常繁瑣,尤其是如果已經在裸金屬或虛擬機上構建了自己的集群。 對於升級,我們已經意識到,最簡單的方法是使用最新版本構建新集群,並將工作負載從舊版本過渡到新版本。 節點原地升級所做的努力和計劃是不值得的。

Kubernetes 具有多個活動組件,需要升級保持一致。 從Docker 到Calico 或Flannel 之類的CNI 插件,你需要仔細地將它們組合在一起才能正常工作。 雖然像Kubespray、Kubeone、Kops和Kubeaws 這樣的項目使它變得更容易,但它們都有缺點。

我們在RHEL 虛擬機上使用Kubespray 構建了自己的集群。 Kubespray 非常棒,它具有用於構建、添加和刪除新節點、升級版本的playbook,以及我們在生產環境中操作Kubernetes 所需的幾乎所有內容。 但是,用於升級的playbook附帶了免責聲明,以避免我們跳過子版本。 因此,必須經過所有中間版本才能到達目標版本。

關鍵是,如果你打算使用Kubernetes 或已經在使用Kubernetes,請考慮生命週期活動以及解決這一問題的方案。 構建和運行集群相對容易一些,但是生命週期維護是一個全新的體驗,具有多個活動組件。

  1. 構建和部署

在準備重新設計整個構建和部署流水線之前, 我們的構建過程和部署必須經歷Kubernetes 世界的完整轉型。 不僅在Jenkins 流水線中進行了大量的重構,而且還使用了諸如Helm 之類的新工具,策劃了新的git 流和構建、標籤化docker 鏡像,以及版本化helm 的部署chart。

你需要一種策略來維護代碼,以及Kubernetes 部署文件、Docker 文件、Docker 鏡像、Helm chart,並設計一種方法將它們組合在一起。

經過幾次迭代,我們決定採用以下設計。

  • 應用程序代碼及其helm chart 放在各自的git 存儲庫中。 這使我們可以分別對它們進行版本控制(語義版本控制)。
  • 然後,我們將chart 版本與應用程序版本關聯起來,並使用它來跟踪發布。 例如,app-1.2.0使用charts-1.1.0進行部署。 如果只更改Helm 的values 文件,則只更改chart 的補丁版本(例如,從1.1.01.1.1)。 所有這些版本均由每個存儲庫中的RELEASE.txt中的發行說明規定。
  • 對於我們未構建或修改代碼的系統應用程序,例如Apache Kafka 或Redis ,工作方式有所不同。 也就是說,我們沒有兩個git 存儲庫,因為Docker 標籤只是Helm chart 版本控制的一部分。 如果我們更改了docker 標籤以進行升級,則會升級chart 標籤的主要版本。
  1. 存活和就緒探針(雙刃劍)

Kubernetes 的存活探針和就緒探針是自動解決系統問題的出色功能。 它們可以在發生故障時重啟容器,並將流量從不正常的實例進行轉移。 但是,在某些故障情況下,這些探針可能會變成一把雙刃劍,並會影響應用程序的啟動和恢復,尤其是有狀態的應用程序,例如消息平台或數據庫。

我們的Kafka 系統就是這個受害者。 我們運行了一個3 Broker 3 Zookeeper有狀態副本集,該狀態集的ReplicationFactor為3,minInSyncReplica為2。 當系統意外故障或崩潰導致Kafka 啟動時,問題發生了。 這導致它在啟動期間運行其他腳本來修復損壞的索引,根據嚴重性,此過程可能需要10到30分鐘。 由於增加了時間,存活探針將不斷失敗,從而向Kafka 發出終止信號以重新啟動。 這阻止了Kafka 修復索引並完全啟動。

唯一的解決方案是在存活探針設置中配置initialDelaySeconds,以在容器啟動後延遲探針評估。 但是,問題在於很難對此加以評估。 有些恢復甚至需要一個小時,因此我們需要提供足夠的空間來解決這一問題。 但是,initialDelaySeconds越大,彈性的速度就越慢,因為在啟動失敗期間Kubernetes 需要更長的時間來重啟容器。

因此,折中的方案是評估initialDelaySeconds字段的值,以在Kubernetes 中的彈性與應用程序在所有故障情況(磁盤故障、網絡故障、系統崩潰等)下成功啟動所花費的時間之間取得更好的平衡。

更新:如果你使用最新版本,Kubernetes 引入了第三種探針類型,稱為“啟動探針”,以解決此問題。 從1.16版開始提供alpha 版本,從1.18版開始提供beta 版本。

啟動探針會禁用就緒和存活檢查,直到容器啟動為止,以確保應用程序的啟動不會中斷。

  1. 公開外部IP

我們了解到,使用靜態外部IP 公開服務會對內核的連接跟踪機製造成巨大代價。 除非進行完整的計劃,否則它很輕易就破壞了擴展性。

我們的集群運行在Calico for CNI上,在Kubernetes 內部採用BGP作為路由協議,並與邊緣路由器對等。 對於Kubeproxy,我們使用IP Tables模式。 我們在Kubernetes 中託管著大量的服務,通過外部IP 公開,每天處理數百萬個連接。 由於來自軟件定義網絡的所有SNAT 和偽裝,Kubernetes 需要一種機制來跟踪所有這些邏輯流。 為此,它使用內核的Conntrack and netfilter工具來管理靜態IP 的這些外部連接,然後將其轉換為內部服務IP,然後轉換為pod IP。 所有這些都是通過conntrack表和IP 表完成的。

但是conntrack表有其局限性。 一旦達到限制,你的Kubernetes 集群(如下所示的OS 內核)將不再接受任何新連接。 在RHEL 上,可以通過這種方式進行檢查。

$  sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_maxnet.netfilter.nf_conntrack_count = 167012
net.netfilter.nf_conntrack_max = 262144

解決此問題的一些方法是使用邊緣路由器對等多個節點,以使連接到靜態IP的傳入連接遍及整個集群。 因此,如果你的集群中有大量的計算機,累積起來,你可以擁有一個巨大的conntrack表來處理大量的傳入連接。

回到2017年我們剛開始的時候,這一切就讓我們望而卻步,但最近,Calico 在2019年對此進行了詳細研究,標題為“為什麼conntrack不再是你的朋友”。

你是否一定需要Kubernetes嗎?

三年過去了,我們每天仍然在繼續發現和學習新知識。 它是一個複雜的平台,具有自己的一系列挑戰,尤其是在構建和維護環境方面的開銷。 它將改變你的設計、思維、架構,並需要提高技能和擴大團隊規模以適應轉型。

但是,如果你在雲上並且能夠將Kubernetes 作為一種“服務”使用,它可以減輕平台維護帶來的大部分開銷,例如“如何擴展內部網絡CIDR?”或“如何升級我的Kubernetes 版本?”

今天,我們意識到,你需要問自己的第一個問題是“你是否一定需要Kubernetes?”。 這可以幫助你評估所遇到的問題以及Kubernetes 解決該問題的重要性。

Kubernetes 轉型並不便宜,為此支付的價格必須確實證明“你的”用例的必要性及其如何利用該平台。 如果可以,那麼Kubernetes 可以極大地提高你的生產力。

記住,為了技術而技術是沒有意義的。

原文鏈接:
Kubernetes投入生產的3年-這就是我們學到的東西