當前位置:
首頁 > 最新 > 手把手教你度過運維 Kubernetes 瓶頸期

手把手教你度過運維 Kubernetes 瓶頸期

| 為 | 容 | 器 | 技 | 術 | 而 | 生 |

翻譯:Ashley,夏天

校對:岑鵬浩

最近,基於 Kubernetes 我們創建了一個分散式的 cron job 調度系統,這是一個令人興奮的容器編排新平台。目前,Kubernetes 非常受歡迎,並且給人們許下了一系列承諾,其中最讓人興奮的一個就是,以後工程師們再也不需要關心或了解他們的應用程序正運行在哪個機器上了

分散式系統非常難,而管理分散式系統上的服務又是運維團隊面臨的最大困難之一。因此,如何在生產環境中部署新軟體,以及學習如何可靠地運維對我們來說非常重要。想學好如何運維 Kubernetes,多看例子非常重要(為什麼就那麼難!),我們這正好有一個由 Kubernetes 中的一個 bug, 引起 1 小時停機的小故事。接下來,本文將解釋我們為什麼會選擇部署在 Kubernetes 之上;如何將 Kubernetes 集成到現有的基礎設施中;如何建立並提高對 Kubernetes 集群可靠性的信心,以及我們是怎樣在 Kubernetes 之上建立抽象層的 。

Kubernetes 是什麼?

Kubernetes 是一個在集群中調度應用程序的分散式系統。你可以讓 Kubernetes 運行一個程序的五個副本,它就會動態地在你的工作節點上調度這些副本。

自動調度的容器可以有效提高利用率,並節省資金,強大的部署基元(即 Workloads API,例如 Pod,Deployment 等) 幫你逐步升級新的代碼,安全上下文和網路策略可以讓你安全的運行多租戶任務。

Kubernetes 內置多種不同的調度功能。它可以調度長時間運行的 HTTP 服務,集群中每台機器上運行的守護進程(DaemonSet),每小時定期運行的 cron jobs 等等。Kubernetes 有趣之處還有很多。如果你想了解更多,Kelsey Hightower 曾經做過很多優秀的演講。比如,Kubernetes for Sysadmins和Healthz: Stop Reverse Engineering Applications and Start Monitoring from the Inside ,對入門者來說,這是兩個非常好的演講。另外,Slack 上也有一個非常棒的相關社區。

為什麼用 Kubernetes?

每個基礎設施項目都是從業務需求開始的,我們的目標是,提高現有分散式 cron job 系統的可靠性和安全性。

我們的需求

我們需要建立和運作一個相對較小的團隊(只有 2 人是全職工作)

我們需要在大約 20 台機器上調度 500 多個不同的 cron job

決定部署在 Kubernetes 之上的原因

我們希望建立在現有的開源項目之上

Kubernetes 包含一個分散式 cron job 調度器,所以我們不必自己寫一個

Kubernetes 是一個非常活躍的項目,並且社區在持續對他貢獻

Kubernetes 是用 Go 語言寫的,很容易學習。幾乎所有的 Kubernetes bug 修復都是由我們團隊中具有初級經驗的 Go 程序員完成的

如果我們能夠成功的運行 Kubernetes,將來就可以在 Kubernetes 的基礎上進行構建(例如,我們正在研究一個基於 Kubernetes 的用來訓練機器學習模型的系統)

我們之前一直在使用 Chronos 作為 cron job 的調度系統,但是現在它已經不再滿足我們的可靠性要求,而且大部分是無人維護的(在過去的 9 個月中只有 1 次代碼提交,而且最後一次代碼合併的時間是 2016 年 3 月) 由於 Chronos 沒人維護,因此我們覺得它不值得繼續貢獻以改善現有的集群。

如果你正在考慮使用 Kubernetes,請記住一點:千萬不要僅僅因為其他公司正在使用 Kubernetes 而使用Kubernetes。建立一個可靠的集群需要花費大量的時間,過於商業的案例使用 Kubernetes 產出效果並不明顯。友情提示,還是按需分配有限的時間比較明智。

究竟什麼是可靠性?

說到運維一個服務,「可靠性」這個詞語本身就沒有意義。可是要談論可靠性,首先你需要建立一個 SLO(服務水平目標)。

我們的三個主要目標

99.9% 的 cron jobs 應該在 20 分鐘內被調度並開始運行。20 分鐘之內其實是非常不精確的時間,但我們採訪了我們的內部客戶,他們對時間都沒有更精確要求

99.99% 的 jobs 應該在一定的時間內完成(沒有被主動終止)

向 Kubernetes 做的遷移應該是對客戶無感的

需要我們做到

Kubernetes API 中的短暫停機時間是可以接受的(如果停機十分鐘,只要五分鐘內恢復,就可以)

調度的 bug(cron job 完全沒有被調度,並且根本無法運行)是不可接受的。我們非常重視調度錯誤的報告

對於 Pod 驅逐我們要小心,並安全地終止實例,以免 job 過於頻繁地被終止

我們需要一個好的遷移計劃

構建一個 K8S 的集群

我們構建第一個 Kubernetes 集群的基本方法是,從零開始,而不是使用像 Kubeadm 或 Kops(使用 Kubernetes The Hard Way 作為參考)之類的工具。我們配置了常用的配置管理工具 Puppet。從零開始部署其實非常好,原因有二:其一,我們能夠將 Kubernetes 深入整合到我們的架構中;其二我們可以深入了解其內部。

從頭開始讓我們將 Kubernetes 整合到我們現有的基礎設施中。我們希望 Kubernetes 能與現有的日誌,證書管理,秘鑰,網路安全,監控,AWS 實例管理,部署,資料庫代理,內部 DNS 伺服器,配置管理等系統進行無縫集成。整合所有這些系統有時需要一點創造力,但總的來說,比使用 Kubeadm / Kops 去做更容易。

我們已經知道如何運維所有這些現有的系統,所以我們希望繼續在新的 Kubernetes 集群中使用它們。例如,安全證書管理是一個非常困難的問題,我們已經有一種方法來頒發和管理證書。這能夠避免為 Kubernetes 創建一個新的證書。

在這過程中,我們不得不了解,我們設置的參數究竟是如何影響 Kubernetes 設置的。例如,在配置用於認證的證書時使用了十幾個參數。了解所有這些參數可以讓我們在遇到與身份驗證有關的問題時,更容易調試設置。

對 K8S 建立信心的 7 個策略

在我們的 Kubernetes 工作開始之前,團隊中並沒有人曾使用過 Kubernetes(除了在某些情況下看過幾眼)。面對這樣的情況,你要如何從 「我們從來沒使用過 Kubernetes」 轉變到「 我們有信心在生產環境使用 Kubernetes」? 接下來,我來分享以下 7 個策略!

策略 0

與其他公司的人交談

我們向其他公司的一些人詢問了他們使用 Kubernetes 的經歷。這些人的共同點是,都曾以不同的方式或者在不同的環境下使用過 Kubernetes(運行過 HTTP 服務,在裸機或 GKE 上使用過,等等)。

特別是當談到像 Kubernetes 這樣龐大而複雜的系統時,對我們來說,重要的是要認真思考自己的應用實例,做自己的實驗,建立對自己運行環境的信心,最後做出自己的決定。舉例來說,你不應該讀完這個博客文章,就得出結論:「好吧,Stripe 既然已經成功使用 Kubernetes,所以它也適用於我們!」

以下是我們在與幾家運營 Kubernetes 集群的公司進行對話之後所學到的知識:

優先考慮你的 etcd 集群的可靠性(etcd 是你存儲所有 Kubernetes 集群狀態的地方)

Kubernetes 的有些功能比另一些功能更穩定,所以要小心 Alpha 版本的功能。一些公司只有在穩定版本發布多個版本之後才會使用那些穩定的功能(例如,如果某個功能在 1.8 版本中是穩定,他們則會在 1.9 或 1.10 版本開始使用)

考慮使用像 GKE / AKS / EKS 這樣的託管 Kubernetes 系統。我們都知道自己建立一個高可用性的 Kubernetes 系統是一項巨大的工程。AWS 在這個項目中沒有託管的 Kubernetes 服務,所以它並不是我們的選擇

請注意因為 overlay 網路/軟體定義網路引入的額外網路延遲

雖然與其他公司的談話並沒有給我們一個明確的答案,或者告訴我們 Kubernetes 是否會為我所用,但這樣的談話確實給我們提供了需要注意的點以及新的啟迪。

策略 1

閱讀代碼

當我們計劃大規模使用 Kubernetes 的一個組件 cron job 控制器時。這個組件當時還是 Alpha 版本,這讓我們有點擔心。雖然我們在一個測試集群中試了一下,但是我們怎麼知道它是否會在我們的生產環境中工作呢?

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

cron job 控制器是一個無狀態的服務(與其他 Kubernetes 組件一樣,除了etcd)

每十秒鐘,這個控制器就調用一次 syncAll 函數:

go wait.Until(jm.syncAll, 10*time.Second, stopCh)

該syncAll函數從 Kubernetes API 中獲取所有 cron job,遍歷該列表,確定下一個應該運行的 jobs,然後啟動這些 jobs

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

策略 2

做負載測試(壓力測試)

在我們開始認真構建集群之前,我們做了一些負載測試。我們並不擔心 Kubernetes 集群究竟能夠處理多少個節點(我們打算部署大約 20 個節點),但是我們確實希望讓某些 Kubernetes 能夠處理和我們想要的一樣多的 cron jobs(每分鐘大約 50 個)。

我們在一個 3 節點的集群中進行測試,創建了 1000 個 cron jobs,每個 jobs 每分鐘運行一次。所有這些工作只是跑了bash -c "echo helloworld".。我們選擇簡單的 job 是因為我們想測試集群的調度和編排能力,而不是集群的總計算能力。

我們的測試集群每分鐘無法處理 1000 個 cron jobs。我們觀察到每個節點每秒最多只能啟動一個 Pod,群集每分鐘能正常運行 200 個 cron jobs。由於我們只想每分鐘運行大約 50 個 cron jobs ,所以我們認為這些限制並不是一個阻礙因素(如果需要的話,我們可以在以後找出這些限制)。所有測試結果已經 OK,準備全力以赴!

策略 3

優先構建和測試高可用性 etcd 集群

在設置 Kubernetes 時最重要的一件事就是運行 etcd。etcd 是 Kubernetes 集群的核心,它是存儲集群中所有數據的地方。etcd 以外的東西都是無狀態的。如果 etcd 未運行,你就不能對 Kubernetes 集群進行任何更改(儘管現有服務將繼續運行!)。

下圖顯示了 etcd 是如何擔當 Kubernetes 集群的核心的。 API server 是 etcd 前面的無狀態 REST 服務/鑒權終端,然後每個其他組件通過 API server 與 etcd 通信。

運行時,有兩個重要的點要牢記在心:

設置副本,以便在丟失節點時不會造成群集宕機。我們現在有三個 etcd 副本

確保你有足夠的 I/O 帶寬可用。我們的 etcd 版本有一個問題,一個高 fsync 延遲的節點可能會引發持續的 leader elections,從而導致集群不可用。通過確保所有節點的 I/O 帶寬高於 etcd 執行的寫入次數可以修復此問題

設置副本不是「一勞永逸」運維。我們仔細測試過,實際上我們可能會丟失一個 etcd 節點,並且集群可以正常恢復。

以下是我們設置 etcd 集群所做的一些工作:

設置副本

監控 etcd 服務是否可用(一旦 etcd 停機,我們就要馬上知道)

寫一些簡單的工具,以便我們可以輕鬆地啟動新的 etcd 節點,並將它們加入到群集中

升級 etcd 的 Consul 集成,以便我們可以在我們的生產環境中運行多於 1 個 etcd 集群

嘗試從 etcd 的備份數據中恢復

測試是否可以在沒有停機的情況下重建整個集群

我們很高興很早就做了這個測試。在我們的生產集群過程中的一個星期五上午,其中一個 etcd 節點突然停止了對 ping 的響應。我們收到警報後,終止了 node,重建了一個新的 node,並把它加入到集群中,Kubernetes 繼續運行,沒有發生任何事故。這簡直太棒了!

策略 4

逐步將 jobs 遷移到 Kubernetes

我們的主要目標之一是將我們的 jobs 遷移到 Kubernetes 而不造成任何中斷。成功遷移的秘密不是避免犯錯(這是不可能的),而是通過精心設計,降低錯誤對遷移的影響。

很幸運,我們已經讓很多種 jobs 遷移到我們的新集群上,所以,我們可以遷移一些對整體影響較低的 jobs,就算有 1-2 個失敗也在容錯範圍內。

遷移之前,我們新建了易於使用的工具,可以讓我們在不到五分鐘的時間內,實現新舊系統之間來回切換。這個簡單的工具減少了錯誤的影響。如果我們轉移了一個計劃之外有依賴性的 jobs,也沒有什麼大不了的!我們可以將其移回原處,解決掉,再來一次。

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

粗略地按照重要程度給他們排序

反覆把部分 jobs 轉移到 Kubernetes。如果我們發現新的極端案例,快速回滾,修復問題,然後重試

策略 5

調查 Kubernetes 漏洞(並修復)

在項目開始的時候我們制定了一個規則:如果 Kubernetes 做了一些出人意料的事情,我們必須進行調查,找出原因並提出補救措施。

調查每個問題很耗時,但卻非常重要。如果我們根據分散式系統的複雜程度簡單地將 Kubernetes 中的片狀和奇怪的行為排除在外,我們估計不得不因為成堆的漏洞而隨時待命。採取這種方法後,我們發現了 Kubernetes 的幾個漏洞。

以下是我們在這些測試中發現的一些問題:

名字超過 52 個字元的 Cronjob 無法安排工作

Pod 有時會永久卡在待定狀態

調度程序每 3 小時崩潰一次

Flannel 的 hostgw 後端沒有替換過時的路由表條目

修復這些錯誤將提升 Kubernetes 的項目使用體驗。不僅能運轉良好,也接受補丁,並有良好的 PR 審查流程。

像所有軟體一樣,Kubernetes 肯定有漏洞。特別是,當我們使用調度程序非常頻繁時(因為我們的cron job 在不斷創建新的 Pod),調度程序使用緩存有時會導致漏洞,回歸和崩潰。緩存很難!但是,代碼庫容易訪問,我們已經能夠處理我們遇到的錯誤。

另一個值得一提的問題是 Kubernetes 的 Pod 驅逐邏輯。Kubernetes 有一個稱為節點控制器(node controller)的組件,如果節點沒有響應的話負責將 Pod 從該節點驅逐,並遷移到另一個節點。很有可能所有節點,均暫無響應(比如,網路或配置出現問題的時候),在這種情況下,Kubernetes 可以終止群集中的所有 Pod。這一般會發生在測試早期。

如果你運行的是大型 Kubernetes 群集,請仔細閱讀節點控制器(node controller)文檔,仔細考慮設置並進行廣泛的測試。每當我們通過創建網路分區測試,對這些設置的配置進行更改的時候,就會發生令人吃驚的事情(例如--pod-eviction-timeout)。在測試中發現這些情況,總比在凌晨 3 點發現要好的多。

策略 6

有意引起 Kubernetes 集群問題

之前我們已經討論過在 Stripe 上運行遊戲日的練習,而且我們還是經常這樣做。這個想法是要想出你最終會在生產中發生的情況(例如,丟失一個 Kubernetes API server),然後在生產中(在工作日期間有警告地)故意造成這些情況,以確保你能夠處理它們。

在我們的集群上進行了幾個試驗之後,他們經常發現監控或配置錯誤等問題。我們很高興能夠在六個月後以在可控範圍內儘早發現這些問題,而不是發生在意料之外。

以下是我們運行的一些遊戲日的練習:

終止一個 Kubernetes API server

終止所有的 Kubernetes API server,並把它們恢復(令我們驚訝的是,這運行得很好)

終止一個 etcd 節點

從 API server 中切斷我們的 Kubernetes 集群中的工作節點(致使它們不能通信)。使得這些節點上的所有 Pod 被移動到其他節點。

我們很高興看到 Kubernetes 如何應對我們設置的大量干擾。 Kubernetes 的設計是為了適應錯誤。它有一個存儲所有狀態的 etcd 集群,一個簡單的 REST 介面的 API server 和一個協調所有集群管理的無狀態控制器集合。

如果任何 Kubernetes 核心組件(API server,controller manager 或 scheduler)被中斷或重新啟動,一旦它們出現,就會從 etcd 中讀取相關的狀態並繼續無縫運行。這是我們所希望的事情之一,在實踐中也運作良好。

以下是我們在這些測試中發現的一些問題:

奇怪,該分頁的地方沒有分頁,讓我們來修理監控

當我們銷毀我們的 API server 實例並將其恢復時,他們需要人工干預。我們最好解決這個問題

有時,當我們執行 etcd 故障轉移時,API server 會啟動超時請求,直到我們重新啟動

運行這些測試之後,我們針對所發現的問題做了補救措施:改進了監控,修復了我們發現的配置問題,並提交了 Kubernetes 的漏洞。

使 corn jobs 易於使用

讓我們簡單地探討一下如何讓基於 Kubernetes 的系統更好用。

我們最初的目標是設計一個運行 cron jobs 的系統,我們的團隊有信心運行和維護。一旦我們建立了對 Kubernetes 的信心,我們需要讓同事們輕鬆配置和添加新的 cron job 。我們開發了一個簡單的 YAML 配置格式,方便我們的用戶即使不了解有關 Kubernetes 內部的任何信息,也能順利使用該系統。

這是我們開發的格式:

我們並沒有做任何特別的事情。僅僅是編寫了一個簡單的程序來採取這種格式,並將其轉換成我們使用 Kubectl 應用的 Kubernetes cron job 配置。

此外,我們還編寫了測試套件,以確保 job 名稱不會太長(Kubernetes cron job 名稱不能超過 52 個字元),並且所有名稱都是唯一的。我們目前不使用 cgroup 來強化我們大部分工作的內存限制,但是這是我們計劃在未來推出的。

我們的簡單格式很容易使用,而且由於自動生成了 Chronos 和 Kubernetes cron job 定義格式相同的格式,因此在兩個系統之間移動 job 非常簡單。這是使我們的增量遷移工作順利運轉的關鍵因素。無論何時將 jobs 轉移到 Kubernetes 產生了任何問題,我們都可以在不到十分鐘的時間內通過簡單的三行配置更改將其移回。

監控 Kubernetes

監控我們的 Kubernetes 集群的內部狀態是非常順利的。我們使用 kube-state-metrics 軟體包進行監控,並使用名為 veneur-prometheus 的小型 Go 程序來抓取 Prometheus 度量指標 kube-state-metrics,並將它們作為統計指標發布到我們的監控系統。

例如,以下是過去一小時內群集中待處理群的數量的圖表。

Pending 意味著他們正在等待分配一個工作節點來運行。你可以看到這個數字在上午 11 點抵達峰值,因為我們很多的 cron job 都是在每個小時的第 0 分鐘運行的。

我們還有一個監控器,用來檢查沒有掛在 Pending 狀態的 Pods,我們檢查每個 Pod 是否在 5 分鐘內就能開始在 worker 節點上運行,否則將會收到警報。

Kubernetes的未來

設置 Kubernetes 到合適的地方運行生產環境的代碼,將所有的 cron jobs 遷移到新的集群,花了三名全職工程師五個月的時間。我們投資學習 Kubernetes 的一個重要原因是我們希望能夠在 Stripe 中更廣泛地使用 Kubernetes。

以下是適用於運行 Kubernetes(或任何其他複雜分散式系統)的原則:

為你的 Kubernetes 項目(以及所有基礎設施項目)定義明確的商業原因!了解業務案例和我們用戶的需求使我們的項目變得更加容易

積極削減範圍。我們決定避免使用許多 Kubernetes 的基本特性來簡化我們的集群。這讓我們可以更快地運行。例如,由於 pod-to-pod 聯網不是我們項目的必要條件,我們可以阻斷節點之間的所有網路連接,並將 Kubernetes 的網路安全性放到未來的項目

花大量時間學習如何正確運維 Kubernetes 集群。仔細測試極端案例。分散式系統是非常複雜的,有很多潛在的問題容易出錯。以我們之前描述的例子為例:根據你的配置,節點控制器(node controller)在與 API server 失去聯繫時終止集群中的所有的 Pod。需要花時間和精力了解 Kubernetes 在每次配置更改後的表現如何

注意到這些原則後,我們可以放心地使用 Kubernetes。隨著時間的推移,我們將繼續發展和演進 Kubernetes 的使用,例如,我們正在關注 AWS 的 EKS 版本,完成另一個系統的 job 來訓練機器學習模型,同時也在研究將一些 HTTP 服務移動到 Kubernetes 的工作。隨著在生產中持續使用 Kubernetes,我們也將計劃持續貢獻開源項目。


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 K8sMeetup 的精彩文章:

KubeCon在Austin 盛大舉行、微軟開源 Virtual Kubelet 工具

TAG:K8sMeetup |