天天看點

使用Kubernetes兩年的經驗教訓

大約兩年前,我們決定放棄基于ansible的安裝配置方式,以便在ec2上部署應用程式的方式,并轉向使用kubernetes實作應用程式的容器化和編排。我們已經将大部分基礎設施遷移到了kubernetes。這是一項艱巨的任務以及挑戰——從混合部署方式直到大部分遷移完成,再到教育訓練整個團隊學習全新的操作範式等等。

使用Kubernetes兩年的經驗教訓

在這篇文章中,我們想回顧一下我們的經驗,并與你分享我們在這段旅程中所學到的東西,以幫助你做出更好的決策,增加成功的機會。

無服務和容器化是很好的概念。如果你正在建立一個新的業務并從頭開始建構所有東西,那麼一定要使用容器部署應用程式,如果你有信心(或者可能沒有,請繼續閱讀)和技術能力來配置和操作kubernetes,以及在kubernetes上部署應用程式,那麼就請使用kubernetes來部署應用程式。

即使你使用托管的kubernetes服務,如eks、gke或aks,在kubernetes上正确部署和操作應用程式也有它的學習曲線。你的開發團隊應該準備好迎接挑戰。隻有當你的團隊遵循devops理念,團隊才能從中獲益。如果你讓專門的運維團隊為開發團隊編寫應用程式清單,那麼從devops的角度來看,我們個人認為你将從kubernetes中獲益較少。當然,選擇kubernetes還有很多其他的好處,例如成本、更快的實驗、更快的自動伸縮性、彈性等等。

如果你已經在雲虛拟機或其他paas平台上部署應用程式,請認真考慮你為什麼還要遷移到kubernetes?你相信kubernetes是解決問題的唯一方法嗎?你必須清楚自己的動機,因為将現有基礎設施遷移到kubernetes是一項艱巨的任務。

我們在這方面犯了一些錯誤。我們遷移到kubernetes的主要原因是建構一個持續內建平台。在這些年中,我們的應用程式累積了大量的技術債務,我們希望kubernetes可以幫助我們快速地重新建構我們的微服務。大部分的新功能開發都需要修改多處的代碼才能完成,這嚴重拖慢了我們新特性的開發速度。我們覺得有必要為每個開發人員和每個變更提供一個內建環境,以幫助加快開發和測試周期,而不需要協調,在同一套環境上進行開發。

下圖是我們的持續內建管道之一,它提供了一個新的內建環境和所有的微服務,并運作自動化測試。

使用Kubernetes兩年的經驗教訓

我們現在做得很好。我們今天在kubernetes上,可以在8分鐘内部署一個具有21個微服務的內建環境。任何開發人員都可以使用我們自己開發的工具來實作這一點。針對每個pull request我們亦可建立一個內建環境。整個測試周期(部署環境、配置環境以及運作測試)不到12分鐘。你可能覺得12分鐘很長,但它确實幫助我們有效驗證了每一次釋出變更。

下圖為持續內建管道的執行報告。

使用Kubernetes兩年的經驗教訓

太好了!我們怎麼做到的?我們花了将近1年半的時間來完成這一切。值得嗎?

我們花了将近1年半的時間,通過建構額外的工具、自動化和重構每個應用程式來穩定這個複雜的持續內建。為了實作開發環境和生産環境的一緻性,我們必須将所有微服務部署到生産環境中,否則,基礎設施和部署設定之間的偏差将使開發人員難以了解和操作應用程式。

我們對這個話題有着複雜的感情。回顧過去,我們認為我們讓解決持續內建的問題變得更糟,因為将所有微服務推到生産環境中以實作開發環境和生産環境的一緻性,這使持續內建變得更複雜和困難,也大大降低了持續內建的速度。在kubernetes之前,我們使用ansible,hashicorp consul和vault進行基礎設施部署以及配置。它慢嗎?是的,當然慢。但我們認為我們可以引入consul服務發現,并對ansible部署進行優化,以便在較短的時間内接近我們的目标。

我們應該遷移到kubernetes嗎?是的,當然。使用kubernetes有幾個好處——服務發現、更好的成本管理、彈性、治理、雲基礎設施等等。我們今天也收獲了所有這些好處。但這并不是我們剛開始時的主要目标,我們自己強加給我們的壓力和付出的痛苦也許是不必要的。

對我們來說,一個重大的教訓是,我們本可以采取一種不同的、阻力較小的方式來采用kubernetes。但我們被kubernetes收購了,kubernetes是我們唯一的選擇,甚至不需要去評估其他解決方案。

在這篇文章中,我們将看到kubernetes上的遷移和操作與部署在雲上或裸機上是不一樣的。你的工程師團隊會有一個學校曲線。讓你的團隊經曆一下也許是值得的。但問題是你現在需要這麼做嗎?你一定要想清楚。

很多人會把kubernetes當作paas解決方案——它不是paas解決方案。它是建構paas解決方案的平台。例如openshift就是一個基于kubernetes的paas的平台。

開箱即用的kubernetes對幾乎任何人來說都是遠遠不夠的。這是一個學習和探索的好地方。但是,你很可能需要更多的基礎設施元件,并将它們作為應用程式的一部分,有機的結合在一起,使其對你的開發人員更有意義。通常這種帶有附加基礎設施元件和政策的kubernetes平台,我們稱其為“internal kubernetes platform[1]”。有幾種方法可以擴充kubernetes平台[2]。

在度量名額監控、日志、服務發現、分布式鍊路追蹤、配置和秘鑰管理、ci/cd、本地開發經驗、自動伸縮等方面,我們都需要考慮和作出決策。以上這些隻是我們能想到的。當然還有更多的決策要做,更多的基礎設施需要建立。一個重要的方面是你的開發人員将如何使用kubernetes資源和清單,在稍後的文章中我們将對此進行詳細介紹。

以下是我們的作出的一些決策和理由。

我們最終決定使用prometheus。在度量名額監控方面,prometheus幾乎是一個事實标準。cncf和kubernetes非常喜歡它。它在grafana生态系統中非常有效。我們也很喜歡grafana!我們唯一的問題是以前用的是influxdb。我們決定離開influxdb,完全投入prometheus。

日志一直是我們的大問題。我們一直在努力使用elk建立一個穩定的日志記錄平台。我們發現在elk中充滿了對我們團隊沒有實際使用的功能。這些功能是有代價的。另外,我們認為在日志中使用elasticsearch存在固有的挑戰,這使得它成為一個昂貴的日志解決方案。我們最後決定使用loki。它很簡單,具有滿足我們團隊需要的必要功能。最重要的是,它的查詢語言與promql非常相似,具有良好的使用者體驗。而且,它與grafana配合得很好。更可以将整個度量名額監控和日志記錄平台集中在一個使用者界面中。

下圖是grafana儀表闆的一個示例,它可以同時顯示度量名額和相應的日志。

使用Kubernetes兩年的經驗教訓

在kubernetes中,你會發現大多數文章使用configmap和secret對象。我們的經驗是它們可以滿足你的基本需求。在現有服務中使用configmap是要付出一定代價的。configmap可以以某種方式挂載到pod中,使用環境變量是最常見的方式。如果你有大量的遺留微服務,從配置管理工具(如puppet、chef或ansible)中讀取配置檔案,則必須重構現有的代碼,以便從環境變量中讀取。我們沒有找到足夠的理由去做這件事。另外,配置檔案的更改意味着您必須重新開機服務。

使用Kubernetes兩年的經驗教訓

為了避免這一切,我們決定使用consul、vault和consul template進行配置管理和秘鑰管理。我們将consul template作為init容器運作,并在pod中添加一個sidecar容器,以便它可以監視consul template中的配置更改,重新整理vault中過期的秘鑰資訊,并優雅地重新加載應用程式程序。

在遷移到kubernetes之前,我們使用jenkins做ci/cd。遷移到kubernetes之後,我們決定依然使用jenkins。到目前為止,我們的經驗是,jenkins并不是雲場景下的最佳解決方案。我們用python、bash、docker和腳本化/聲明性的jenkins管道做了大量的工作,使ci/cd可以工作。編寫和維護這些工具和管道開始變得很昂貴。我們現在正在探索tekton和argo workflows工作流作為我們新的ci/cd平台。你也有其他更多的選擇,如jenkins x,screwdriver,keptn等。

在開發中,有很多種方法使用kubernetes。我們主要集中在兩個選項上——telepresence.io和skaffold。skaffold能夠監視你的本地更改,并不斷地将它們部署到kubernetes叢集中。另一方面,telepresence允許你在本地運作服務,同時使用kubernetes叢集設定一個透明的網絡代理,以便你的本地服務可以像部署在叢集中一樣與kubernetes中的其他服務通信。這是個人意見和喜好的問題。很難決定哪一種工具更好。我們現在主要是在嘗試telepresence,但我們沒有放棄skaffold 成為更好工具的可能性。隻有時間會告訴我們到底使用,或者兩者同時使用。當然還有其他的解決方案,比如draft。

我們還沒有實作分布式鍊路追蹤。不過,我們已經在計劃中。與日志一樣,我們希望也能将分布式鍊路追蹤內建在grafana中,以便為我們的開發團隊提供更佳的可觀察性體驗。

kubernetes的一個重要方面是考慮開發人員将如何與叢集互動并部署他們的工作負載。我們想讓事情變得簡單,易于擴充。我們正在嘗試kustomize,skaffold和一些自行開發的crds,作為開發人員部署和管理應用程式的方式。盡管如此,任何團隊都可以自由地使用他們想使用的任何工具來與叢集互動,隻要他們是開源的,并且建構在開放标準之上。

我們的業務主要在新加坡地區營運。在我們開始kubernetes的旅程時,eks還沒有在新加坡地區提供服務。是以我們不得不在ec2上使用kops建立自己的kubernetes叢集。

建立一個開箱即用的叢集也許沒有那麼困難。我們能夠在一周内啟動我們的第一個叢集。大多數問題發生在開始部署工作負載時。如何将叢集調整到最佳狀态,自動擴縮容該如何設定,網絡該怎麼配置?你必須自己進行研究和配置。預設值在大多數情況下都不适用于生産(或者至少對我們不起作用)。

經過兩年的生産經驗,我們認為操作kubernetes是很複雜的。kubernetes有很多元件組成。你更關心的是你的核心業務,而不是如果操作kubernetes,盡可能地将這些事情交給雲服務提供商,比如eks、gke、aks等。你不能從操作kubernetes中獲得任何價值。

你還得考慮更新

kubernetes非常複雜,即使你使用的是托管服務,更新也不會一帆風順。

即使使用了托管kubernetes服務,也應盡早将災難恢複和更新的過程自動化,能夠在災難面臨前快速做出反應。

如果你願意,你可以嘗試gitops理念。如果不能做到這一點,那麼将手動步驟減少到最低限度也是一個很好的開始。我們使用eksctl、terraform和我們的叢集配置清單(包括平台服務的清單)組合來建立我們的kubernetes平台 ——“grofers kubernetes platform”。為了使部署和配置過程更簡單和可重複,我們建構了一個自動化管道來設定新的叢集并将更改部署到現有叢集中。

在開始遷移之後,我們發現叢集中由于配置不正确而出現了許多性能和功能問題。其效果之一是在資源請求和限制中添加了大量的緩沖區,以消除資源限制作為性能降低的可能性。

最初的現象是由于節點記憶體不足而導緻pod被迫逐出。原因是與pod的資源請求相比,資源限制過高。随着流量的激增,記憶體消耗的增加可能會導緻節點上的記憶體飽和,進而導緻pod被迫逐出。

經過兩年的生産經驗,我們認為保持資源請求足夠高,但不要太高,以防止在低流量時間浪費資源,并将資源限制相對接近資源請求,以便為尖峰流量提供一些喘息空間,而不會因節點記憶體壓力而導緻pod被迫逐出。資源限制與資源請求的具體數值取決于你的流量模式。

但以上經驗并不适用于非生産環境,如開發環境。這些環境下的流量不會出現任何峰值。理論上,如果将cpu請求設定為零,并為容器設定足夠高的cpu限制,則可以運作無限的容器。如果你的容器開始大量使用cpu,它們将被限制。對于記憶體請求和限制也可以這樣做。然而,達到記憶體限制的行為與cpu不同。如果使用的記憶體超過了設定的記憶體限制,那麼容器就會被殺死并重新啟動。如果記憶體限制異常高(假設高于節點的容量),則可以繼續使用記憶體,最終當節點的可用記憶體不足時,排程程式将開始逐出pod。

在非生産環境中,我們通過保持資源請求極低而限制極高,來盡可能安全地過度送出資源。在這種情況下,限制因素是記憶體,也就是說,無論記憶體請求有多低,記憶體限制有多高,pod逐出,都是一個節點上所有容器使用的記憶體總和的函數。

kubernetes旨在為開發者打造一個雲平台,使他們更加獨立,并推動devops文化。向開發人員開放平台,減少雲工程師團隊(或系統管理者)的幹預,使開發團隊更獨立,應該是重要目标之一。

有時這種獨立性可能帶來嚴重的風險。例如,在eks中使用loadbalancer類型的服務,預設情況下提供面向公共網絡的elb。添加某個注釋,確定建立的是内部elb,而不是面向公共網絡的。我們很早就犯了這些錯誤。

我們使用open policy agent來降低這些安全風險,以及與成本、安全和技術債務相關的風險。

使用open policy agent來建構正确的自動化變更管理過程,并為我們的開發人員建構正确的安全網。使用open policy agent,我們可以限制服務對象的建立,除非出現正确的注釋,這樣開發人員就不會意外地建立面向公共網絡的elb。

我們在遷移後看到了巨大的成本效益。然而,并不是所有的好處都是立竿見影的。我們正在整理一份關于成本效益的更詳細的文章。

這是最明顯的一個。我們使用了更少的計算、記憶體和存儲資源,提供了和以前一樣的基礎設施能力。除了因為容器和流程變得更好了,我們更好地利用了我們的共享服務,比如可觀察性流程(度量名額監控和日志)。

然而,最初我們在遷移的時候浪費了大量的資源。由于我們無法以正确的方式優化自我管理的kubernetes叢集,這導緻了大量的性能問題,我們最終在pod中添加了大量的資源請求作為緩沖,更像是保險,以減少由于計算或記憶體不足而導緻的停機或性能問題的可能性。

由于大量的資源緩沖,基礎設施成本高是一個大問題。由于kubernetes的原因,我們并沒有意識到kubernetes帶來的産能好處。在遷移到eks之後,kubernetes的穩定性幫助我們變得更加自信,幫助我們采取必要的步驟來糾正資源請求,并大幅減少資源浪費。

在kubernetes中使用spot執行個體會變得容易得多。在使用spot執行個體時,spot執行個體随時可能被回收,對于一般應用程式而言,如何確定應用程式一直正常運作,這可能會變得更複雜。但對于kubernetes來說,對于中斷的容器,可以被快速的重新排程。

spot執行個體同時幫助我們節省了大量資金。今天,我們的測試叢集運作在spot執行個體上。

對我們來說,下一步優化是如何将生産叢集運作在spot執行個體上。在另一篇博文中有更多關于這個主題的文章。

我們使用ingress來整合測試環境中的elb,并大幅降低elb的支出成本。為了避免在代碼中,實作開發與測試/生産環境的差異,我們決定實作一個控制器,它将loadbalancer類型的服務與測試/生産環境中的一個ingres對象一起變更為nodeport類型的服務。

對于我們來說,遷移到nginx ingress相對簡單,由于我們的控制器方法,不需要太多更改。如果我們在生産中也使用ingress,可以節省更多的成本。這不是一個簡單的改變。在以正确的方式為生産環境配置ingress時,必須考慮幾個問題,并且需要從安全性和api管理的角度來考慮。這是我們打算在不久的将來開展工作的領域。

雖然我們節省了大量的基礎設施開支,但有一個基礎設施領域的成本會增加——跨az資料傳輸。

pod可以被配置設定到任何節點。即使你控制了pod在叢集中的配置設定方式,也沒有簡單的方法來控制服務如何發現彼此,即一個服務的pod與同一az中的另一個服務的pod進行通信,以減少跨az的資料傳輸。

在與其他公司的同行進行了大量的研究和交談之後,我們了解到,通過引入服務網格來控制從一個pod到另一個pod的流量是如何路由的,這是可以實作的。我們還沒有準備好僅僅為了節省跨az資料傳輸的成本而自己承擔操作服務網格的複雜性。

每個組織都有自己的工作流程和運維挑戰。我們也有我們的。

在兩年的kubernetes之旅中,我們了解到kubernetes很棒,當你使用它的功能,如控制器、operators和crd,來簡化日常操作,并為開發人員提供更內建的體驗時,它簡直太棒了。

我們已經開始投資建立一系列operators和crd。例如,loadbalancer服務類型到ingress的轉換是一個控制器操作。類似地,每當部署新服務時,我們使用控制器在dns伺服器中自動建立cname記錄。這是幾個例子。我們還有5個單獨的用例,我們依賴内部控制器來簡化日常操作并減少工作量。

我們還建造了一些crd。其中一種被廣泛用于在grafana上生成監控儀表闆,聲明性地指定應該使用什麼樣的監控儀表闆。這使得開發人員可以在應用程式代碼庫旁邊簽入監視儀表闆,并使用相同的工作流kubectl apply -f ... 部署所有内容。

我們看到了控制器和crd的巨大優勢。當我們與雲供應商aws密切合作以簡化叢集基礎設施操作時,我們将更多精力放在建構“the grofers kubernetes platform”上,該平台架構的宗旨是以最佳方式支援我們的開發團隊。

相關連結:

https://itnext.io/why-everyone-builds-internal-kubernetes-platforms-284c2cf76226

https://speakerdeck.com/gianarb/cloud-native-ambassador-day-extending-kubernetes

原文連結:https://lambda.grofers.com/learnings-from-two-years-of-kubernetes-in-production-b0ec21aa2814

繼續閱讀