換個姿勢學習Kubernetes運營,如何5個月在生產(chǎn)環(huán)境構建K8S?

在分布式系統(tǒng)上管理服務是運維團隊面臨的最困難的問題之一。在生產(chǎn)中突破新軟件并學習如何可靠地運營是非常重要的。本文是一則實例,講述為什么學習運營Kubernetes很重要,以及為什么很難。本文是關于Kubernetes bug導致的一小時中斷故障的事后剖析。

為什么選擇在Kubernetes之上構建?如何將Kubernetes集成到現(xiàn)有基礎設施中?本文作者給出的方法是建立 (和改進) 對Kubernetes集群的可靠性的信任,以及構建在Kubernetes之上的抽象。

我們最近在Kubernetes之上構建了一個分布式的cron作業(yè)調(diào)度系統(tǒng),這是一個令人興奮的容器編排的新平臺。Kubernetes現(xiàn)在非常流行,并且有許多令人興奮的承諾:最令人興奮的是,程序員不需要知道或關心他們的應用程序運行的是什么機器。

什么是Kubernetes?

Kubernetes是一個分布式系統(tǒng),用于調(diào)度程序在集群中運行。你可以告訴Kubernetes運行一個程序的5個副本,它將在工作節(jié)點上動態(tài)調(diào)度它們。容器自動調(diào)度以增加利用率,節(jié)省資金,強大的deployment primitives允許逐步推出新的代碼,安全上下文和網(wǎng)絡策略允許企業(yè)以安全的方式運行多租戶的工作負載。

Kubernetes有很多不同類型的調(diào)度能力。它可以調(diào)度長時間運行的HTTP服務、在集群中每臺機器上運行的daemonsets、每小時運行的cron作業(yè)等等。

為什么是Kubernetes?

每個基礎設施項目都是從業(yè)務需求開始的,我們的目標是提高現(xiàn)有分布式cron作業(yè)系統(tǒng)的可靠性和安全性。我們的要求是:

  • 建立和運營一支小團隊(只有2人在項目中全職工作)。

  • 在20臺機器上可靠地安排大約500個不同的cron作業(yè)。

我們決定在Kubernetes之上建立的幾個原因:

  • 希望構建一個現(xiàn)有的開源項目。

  • kubernetes包含一個分布式cron作業(yè)調(diào)度器,不必自己編寫。

  • kubernetes是一個非?;钴S的項目,經(jīng)常接受捐贈。

  • kubernetes是用Go寫的,很容易學。幾乎所有Kubernetes的bug都是由團隊中沒有經(jīng)驗的程序員做的。

如果我們能夠成功地運營Kubernetes,可以在未來的Kubernetes上構建,例如,目前正在開發(fā)基于kubernet的系統(tǒng)來訓練機器學習模型。

我們以前使用Chronos作為cron作業(yè)調(diào)度系統(tǒng),但它不再是滿足可靠性要求,而且大部分都沒有維護(在過去9個月中1次提交, 最后一次合并請求的時間是2016年3月))Chronos未維護的,我們認為不值得繼續(xù)投資改善現(xiàn)有的集群。

如果你正考慮Kubernetes,請記住:不要僅僅因為其他公司在使用Kubernetes而使用它。建立一個可靠的集群需要花費大量的時間,使用它的業(yè)務案例并不是很突出。把你的時間用在聰明的方法上。

可靠性是什么意思?

說到運營服務,“可靠”這個詞本身并沒有什么意義。要討論可靠性,首先需要建立一個SLO(服務級別目標)。

我們有三個主要目標:

  • 99.99%的cron作業(yè)應該在預定運行時間的20分鐘內(nèi)開始運行。20分鐘是一個很寬的窗口,但是我們采訪了內(nèi)部客戶,沒有人要求更高的精確度。

  • Jobs應該運行99.99%的時間(不被終止)。

  • 向Kubernetes的遷移不會導致任何面向客戶的事件。

這意味著:

  • Kubernetes API的短暫停機時間是可以接受的(如果停機10分鐘,只要在5分鐘內(nèi)恢復即可)。

  • 調(diào)度錯誤(cron作業(yè)運行完全丟失并且根本無法運行)是不可接受的。我們非常重視安排錯誤報告。

  • 要謹慎對待pod evictions 和安全終止實例,以免作業(yè)過于頻繁地終止。

  • 需要一個好的遷移計劃。

建立一個Kubernetes集群

我們建立第一個Kubernetes集群的基本方法是從零開始構建集群,而不是使用kubeadm或kops之類的工具。使用Puppet(常用的配置管理工具)調(diào)配了配置。從頭開始構建很好,原因有兩個:能夠深入地集成Kubernetes在架構中,并且深入理解其內(nèi)部。

我們希望將Kubernetes整合到現(xiàn)有的基礎架構中。與現(xiàn)有系統(tǒng)無縫集成,以便進行日志記錄,證書管理,加密,網(wǎng)絡安全,監(jiān)控,AWS實例管理,部署,數(shù)據(jù)庫代理,內(nèi)部DNS服務器,配置管理以及更多。整合所有這些系統(tǒng)有時需要一點創(chuàng)造力,但總體上比試圖讓kubeadm / kops成為我們想要的更容易。

在信任并了解如何操作這些現(xiàn)有系統(tǒng)后,我們希望繼續(xù)在新的Kubernetes群集中使用。例如,安全證書管理是一個非常棘手的問題,已經(jīng)有辦法頒發(fā)和管理證書。通過適當?shù)恼?,我們避免了為Kubernetes創(chuàng)建新的CA。

準確了解設置的參數(shù)是如何影響Kubernetes設置的。例如,在配置用于身份驗證的證書/CAs時,使用了超過12個參數(shù)。了解這些參數(shù)有助于在遇到身份驗證問題時更容易調(diào)試設置。

對Kubernetes建立信心

在Kubernetes之初,團隊中沒有人使用過Kubernetes。如何從“沒有人用過Kubernetes”到“我們有信心在生產(chǎn)中運行Kubernetes”?

112.jpg

戰(zhàn)略0:與其他公司交談

我們向其他公司詢問了Kubernetes的經(jīng)歷。 他們都以不同的方式或在不同的環(huán)境中使用Kubernetes(運行HTTP服務,裸機,Google Kubernetes引擎等)。

在談到Kubernetes這樣龐大而復雜的系統(tǒng)時,重要的是認真思考自己的用例,做自己的實驗,建立對自己環(huán)境的信心,并做出決定。 例如,你不該讀這篇博客文章并得出結(jié)論:“Stripe正在成功使用Kubernetes,所以它也適用于我們!”

以下是我們在與幾家運營Kubernetes集群的公司溝通后后學到的:

  • 優(yōu)先考慮企業(yè)etcd集群的可靠性(etcd是存儲所有Kubernetes集群狀態(tài)的地方)。

  • 某些Kubernetes功能比其他功能更穩(wěn)定,因此請小心Alpha功能。一些公司只有在穩(wěn)定后才能使用穩(wěn)定特性(例如,如果某個功能在1.8版本中保持穩(wěn)定,則在使用它之前會等待1.9或1.10)。

  • 考慮使用托管的Kubernetes系統(tǒng),如GKE / AKS / EKS。從頭開始建立高可用性Kubernetes系統(tǒng)是一項巨大的工作。 AWS在此項目中沒有托管的Kubernetes服務,所以這不適合我們。

  • 注意由覆蓋網(wǎng)絡/軟件定義網(wǎng)絡引入的額外網(wǎng)絡延遲。

策略1:閱讀代碼。

我們計劃很大程度上依賴于Kubernetes的一個組件,即cronjob控制器。這個組件當時處于alpha階段,這讓我們有點擔心。我們在一個測試集群中嘗試了它,但是如何判斷它在生產(chǎn)中是否適合我們呢?

值得慶幸的是,所有cronjob控制器的核心功能只有400行Go。通過源代碼快速讀取顯示:

  • cron作業(yè)控制器是一個無狀態(tài)的服務(與其他Kubernetes組件一樣,除了etcd)。

  • 每10秒鐘,這個控制器調(diào)用syncAll函數(shù):go wait.Until(jm.syncAll,10 * time.Second,stopCh)

  • syncAll函數(shù)從Kubernetes API中獲取所有cron作業(yè),遍歷該列表,確定下一步應該運行哪些作業(yè),然后啟動這些作業(yè)。

核心邏輯似乎相對容易理解。更重要的是,如果在這個控制器中有一個bug,它可能是我們可以修復的東西。

策略2:做負載測試

在開始認真構建集群之前,我們做了一些負載測試。我們并不擔心Kubernetes集群能夠處理多少節(jié)點(計劃部署大約20個節(jié)點),但是確實想讓某些Kubernetes能夠處理我們希望運行的那么多的cron作業(yè)(大約每分鐘50個)。


113.jpg

在一個3節(jié)點集群中運行了測試,創(chuàng)建了1000個cron作業(yè),每個任務每分鐘運行一次。這些工作中的每一個都簡單地運行bash -c 'echo hello world'。我們選擇簡單的作業(yè),是因為希望測試集群的調(diào)度和編排能力,而不是集群的總計算能力。

測試集群無法處理每分鐘1000個cron作業(yè)。每個節(jié)點每秒最多只能啟動一個pod,而集群能夠每分鐘運行200個cron作業(yè)。由于我們只希望每分鐘運行大約50個cron作業(yè),所以我們認為這些限制不是阻礙因素。

策略3:優(yōu)先構建和測試高可用性etcd集群。

在設置Kubernetes時,最重要的事情之一就是運行etcd。Etcd是Kubernetes集群的核心,它是存儲集群中所有數(shù)據(jù)的地方。除了etcd之外,其他一切都是無狀態(tài)的。如果etcd沒有運行,不能對Kubernetes集群進行任何更改(盡管現(xiàn)有的服務將繼續(xù)運行!)

這張圖顯示了etcd是Kubernetes集群的核心——API服務器是etcd前面的無狀態(tài)REST/認證端點,然后其他組件通過API服務器與etcd對話。 在運行時,有兩個要點需要牢記:

  • 設置復制,這樣集群不會死如果你失去了一個節(jié)點。我們現(xiàn)在有三個etcd副本。

  • 確保足夠的I / O帶寬。我們的etcd版本有一個問題,一個具有高fsync延遲的節(jié)點可能觸發(fā)連續(xù)的leader elections,導致集群無法使用。通過確保所有節(jié)點的I/O帶寬都比etcd的寫入數(shù)量多,從而彌補了這一點。

image

設置復制不是一個設置-忘記操作。我們仔細地測試后發(fā)現(xiàn)可能會丟失一個etcd節(jié)點,并且集群優(yōu)雅地恢復了。

以下是為建立etcd集群所做的一些工作:

  • 設置復制

  • 監(jiān)控etcd服務是可用的

  • 寫一些簡單的工具,以便輕松創(chuàng)建新的etcd節(jié)點,并加入到集群當中

  • 編寫一些簡單的工具,以便我們可以輕松創(chuàng)建新的etcd節(jié)點并將它們加入到群集中

  • 補丁etcd的高集成,這樣我們可以在生產(chǎn)環(huán)境中運行超過1個 etcd集群

  • 測試從一個etcd備份中恢復

  • 測試可以在不停機情況下重建整個集群

很高興很早就做了這個測試。某個周五的早晨,在我們的生產(chǎn)集群中,一個etcd節(jié)點停止了對ping的響應。我們得到了警報,終止了節(jié)點,帶來了一個新的節(jié)點,加入到集群中,同時Kubernetes繼續(xù)運行。

策略4:逐步將工作遷移到Kubernetes

我們的目標之一是將工作遷移到Kubernetes而不造成任何中斷。成功進行生產(chǎn)遷移的秘訣不是避免犯錯(這是不可能的),而是設計你的遷移以減少錯誤的影響。

我們很幸運有多種職位可以遷移到新集群,所以可以遷移一些低影響的工作崗位,接受一兩次失敗。

在開始遷移之前,構建了易于使用的工具,如果有必要,可以在不到五分鐘的時間內(nèi)在舊系統(tǒng)和新系統(tǒng)之間來回移動作業(yè)。這種簡單的工具減少了錯誤的影響 - 如果遷移一個沒有計劃的依賴的工作,沒有什么大不了的!可以將其移回原處,解決問題,然后再試。

以下是我們采用的整體遷移策略:

  • 根據(jù)他們的重要程度大致排序

  • 將一些重復的工作遷移到Kubernetes。如果發(fā)現(xiàn)新的情況,快速回滾,修復問題,然后重試。

策略5:調(diào)查 Kubernetes bug 并修復它們

我們在項目開始時制定了一個規(guī)則:如果Kubernetes做了一些意外的事情,必須調(diào)查,找出原因,并提出補救措施。

調(diào)查每個問題都很耗時,但很重要。如果只是簡單地將Kubernetes的”古怪行為”看作是復雜的分布式系統(tǒng)的功能,我們擔心,因為它們會被調(diào)用導致產(chǎn)生bug集群。

在使用了這種方法之后,我們發(fā)現(xiàn)并且修復了Kubernetes的幾個bug。

以下是測試中發(fā)現(xiàn)的一些問題:

  • 名稱超過52個字符的Cronjob無法安排作業(yè)。

  • Pods有時會永遠停留在掛起狀態(tài)。

  • 調(diào)度程序會每3個小時崩潰一次。

  • Flannel的hostgw后端沒有替換過時的路由表項

修復這些bug讓我們對Kubernetes項目的使用感覺好得多——不僅它運行得比較好,而且也接受補丁并有一個良好的PR審查過程。

Kubernetes有bug,像所有的軟件一樣。特別是,我們非常頻繁地使用調(diào)度器(cron作業(yè)總是在創(chuàng)建新的pods),而調(diào)度器使用緩存有時會導致bug、回退和崩潰。緩存是困難的!但是代碼庫是可接近的,我們已經(jīng)能夠處理遇到的bug。

值得一提的是,Kubernetes的pod驅(qū)逐邏輯。Kubernetes有一個稱為節(jié)點控制器的組件,它負責將pod驅(qū)逐出去,如果節(jié)點沒有響應,則將它們移到另一個節(jié)點。allnodes會暫時無響應(例如,由于網(wǎng)絡或配置問題),在這種情況下,Kubernetes可以終止集群中的所有pod。

如果運行的是大型Kubernetes集群,請仔細閱讀節(jié)點控制器文檔,仔細地考慮設置,并進行廣泛測試。每次通過創(chuàng)建網(wǎng)絡分區(qū)測試對這些設置的配置更改(例如,pod-驅(qū)逐超時),就會發(fā)生令人驚訝的事情。最好在測試中發(fā)現(xiàn)這些意外,而不是在生產(chǎn)中發(fā)現(xiàn)。

策略6:有意引起Kubernetes集群問題

之前討論過在Stripe中進行游戲日練習。這個想法是要想出你最終會在生產(chǎn)中發(fā)生的情況,然后在生產(chǎn)中故意造成這些情況,從而確保能夠處理它們。

在集群上進行了幾次練習之后,經(jīng)常發(fā)現(xiàn)諸如監(jiān)視或配置錯誤等問題。很高興在早期發(fā)現(xiàn)這些問題,而不是六個月后突然發(fā)現(xiàn)。

以下是運行的一些比賽日練習:

  • 終止Kubernetes API服務器

  • 終止所有Kubernetes API服務器并將其恢復(這非常有效)

  • 終止etcd節(jié)點

  • 從API服務器中關閉Kubernetes集群中的工作節(jié)點(以便它們無法通信)。這導致節(jié)點上的所有pods被遷移到其他節(jié)點。

很高興看到Kubernetes如何應對我們投入的大量干擾。 Kubernetes的設計是為了適應錯誤 - 它有存儲所有狀態(tài)的etcd集群,一個只是該數(shù)據(jù)庫的REST接口的API服務器,以及一個協(xié)調(diào)所有集群管理的無狀態(tài)控制器集合。

如果任何Kubernetes核心組件(API服務器,控制器管理器或調(diào)度程序)被中斷或重新啟動,一旦它們出現(xiàn),它們將從etcd讀取相關狀態(tài)并繼續(xù)無縫運行。這是我們希望的事情之一,而且在實踐中實際運作良好。

以下是測試中發(fā)現(xiàn)的一些問題:

  • “沒有得到paged,來修復監(jiān)控?!?/p>

  • “當銷毀API服務器實例并將其恢復后,需要人工干預。最好解決這個問題?!?/p>

  • “有時執(zhí)行etcd故障轉(zhuǎn)移時,API服務器會啟動超時請求,直至重新啟動。”

在運行這些測試后,針對發(fā)現(xiàn)的問題開發(fā)了補救措施:改進了監(jiān)控,發(fā)現(xiàn)了固定配置問題,并提交了Kubernetes bug。

使cron作業(yè)易于使用

簡單地探討一下我們是如何使基于kubernetes的系統(tǒng)易于使用的。

最初的目標是設計一個運行cron作業(yè)的系統(tǒng),團隊有信心運營和維護。一旦建立了對Kubernetes的信心,就需要工程師們輕松地配置和增加新的cron作業(yè)。我們開發(fā)了一個簡單的YAML配置格式,這樣用戶就不需要了解Kubernetes的內(nèi)部結(jié)構來使用這個系統(tǒng)。這是我們開發(fā)的格式:

name: job-name-here

kubernetes:

schedule: '15 */2 * * *'

command:

  • ruby

  • "/path/to/script.rb"

resources:

requests:

cpu: 0.1

memory: 128M

limits:

memory: 1024M

沒有做什么特別的事情——我們編寫了一個簡單的程序,將這種格式轉(zhuǎn)換為Kubernetes cron作業(yè)配置,將其應用于kubectl。

我們還編寫了一個測試套件,以確保作業(yè)名稱不會太長,并且所有名稱都是惟一的。我們目前不使用cgroups來強化對大多數(shù)作業(yè)的內(nèi)存限制,但計劃將來推出。

我們的簡單格式易于使用,而且由于自動生成了來自相同格式的Chronos和Kubernetes cron作業(yè)定義,所以在兩個系統(tǒng)之間遷移作業(yè)非常簡單。這是使我們的增量遷移工作良好的關鍵部分。將作業(yè)遷移到Kubernetes時,可以用一個簡單的三行配置更改,在不到十分鐘的時間內(nèi)將其移回。

監(jiān)控Kubernetes

監(jiān)測Kubernetes集群的內(nèi)部狀態(tài)非常令人愉悅。我們使用kube-state-metrics軟件包進行監(jiān)測,并使用一個名為veneurl - Prometheus的小型Go程序來獲取Prometheus的度量標準,將它們作為statsd指標發(fā)布到我們的監(jiān)控系統(tǒng)中。

例如,以下是過去一小時內(nèi)集群中未決Pod的數(shù)量圖表。Pending意味著等待分配一個工作節(jié)點來運行。可以看到上午11點的數(shù)字峰值,很多cron作業(yè)在每小時的第0分鐘運行。

111.jpg

還有一個監(jiān)視器,用于檢查有沒有任何Pod在Pending狀態(tài)中卡住 - 每個Pod在5分鐘內(nèi)開始在worker節(jié)點上運行,否則會收到警報。

Kubernetes未來計劃

設置Kubernetes,到順暢地運行生產(chǎn)代碼并將所有cron作業(yè)遷移到新集群,花了五個月的時間,三位工程師全職工作。我們投資學習Kubernetes的一個重要原因是希望能夠在Stripe中更廣泛地使用Kubernetes。

以下是適用于運行Kubernetes(或任何其他復雜分布式系統(tǒng))的原則:

  • 為企業(yè)的Kubernetes項目,以及所有基礎設施項目,定義清晰的商業(yè)原因。了解業(yè)務案例和用戶的需求使項目變得更加容易。

  • 積極削減范圍。避免使用許多Kubernetes的基本特性來簡化集群。這讓我們可以更快速地發(fā)送消息,例如,由于pod-to-pod聯(lián)網(wǎng)不是我們項目的必需條件,可以關閉節(jié)點之間的所有網(wǎng)絡連接,并將Kubernetes的網(wǎng)絡安全性推遲。

花大量時間學習如何正確地運營Kubernetes集群。仔細測試邊界情況。分布式系統(tǒng)非常復雜,有很多潛在的問題。以前面的例子為例:如果節(jié)點控制器由于配置與API服務器失去聯(lián)系,那么它可以殺死集群中的所有pods。學習Kubernetes在每次配置更改后的表現(xiàn)如何,需要時間和精心的關注。

通過專注于這些原則,我們已經(jīng)有信心在生產(chǎn)中使用Kubernetes。我們將繼續(xù)開發(fā)Kubernetes的使用,例如,我們正在關注AWS EKS的發(fā)布。我們正在完成另一個系統(tǒng)的工作,訓練機器學習模型,并且正在探索將一些HTTP服務遷移到Kubernetes。隨著我們繼續(xù)在生產(chǎn)中運行Kubernetes時,我們計劃對開源項目做出貢獻。

原文作者:Julia Evans

原文鏈接:

Learning to operate Kubernetes reliably

https://stripe.com/blog/operating-kubernetes

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容