1. 程式人生 > >換個姿勢學習Kubernetes運營,如何5個月在生產環境構建K8S?_Kubernetes中文社群

換個姿勢學習Kubernetes運營,如何5個月在生產環境構建K8S?_Kubernetes中文社群

在分散式系統上管理服務是運維團隊面臨的最困難的問題之一。在生產中突破新軟體並學習如何可靠地運營是非常重要的。本文是一則例項,講述為什麼學習運營Kubernetes很重要,以及為什麼很難。本文是關於Kubernetes bug導致的一小時中斷故障的事後剖析。為什麼選擇在Kubernetes之上構建?如何將Kubernetes整合到現有基礎設施中?本文作者給出的方法是建立 (和改進) 對Kubernetes叢集的可靠性的信任,以及構建在Kubernetes之上的抽象。

我們最近在Kubernetes之上構建了一個分散式的cron作業排程系統,這是一個令人興奮的容器編排的新平臺。Kubernetes現在非常流行,並且有許多令人興奮的承諾:最令人興奮的是,程式設計師不需要知道或關心他們的應用程式執行的是什麼機器。

什麼是Kubernetes?

Kubernetes是一個分散式系統,用於排程程式在叢集中執行。你可以告訴Kubernetes執行一個程式的5個副本,它將在工作節點上動態排程它們。容器自動排程以增加利用率,節省資金,強大的deployment primitives允許逐步推出新的程式碼,安全上下文和網路策略允許企業以安全的方式執行多租戶的工作負載。

Kubernetes有很多不同型別的排程能力。它可以排程長時間執行的HTTP服務、在叢集中每臺機器上執行的daemonsets、每小時執行的cron作業等等。

為什麼是Kubernetes?

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

  • 建立和運營一支小團隊(只有2人在專案中全職工作)。
  • 在20臺機器上可靠地安排大約500個不同的cron作業。

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

  • 希望構建一個現有的開源專案。
  • kubernetes包含一個分散式cron作業排程器,不必自己編寫。
  • kubernetes是一個非常活躍的專案,經常接受捐贈。
  • kubernetes是用Go寫的,很容易學。幾乎所有Kubernetes的bug都是由團隊中沒有經驗的程式設計師做的。

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

我們以前使用Chronos作為cron作業排程系統,但它不再是滿足可靠性要求,而且大部分都沒有維護(在過去9個月中1次提交, 最後一次合併請求的時間是2016年3月))Chronos未維護的,我們認為不值得繼續投資改善現有的叢集。

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

可靠性是什麼意思?

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

我們有三個主要目標:

  • 99.99%的cron作業應該在預定執行時間的20分鐘內開始執行。20分鐘是一個很寬的視窗,但是我們採訪了內部客戶,沒有人要求更高的精確度。
  • Jobs應該執行99.99%的時間(不被終止)。
  • 向Kubernetes的遷移不會導致任何面向客戶的事件。

這意味著:

  • Kubernetes API的短暫停機時間是可以接受的(如果停機10分鐘,只要在5分鐘內恢復即可)。
  • 排程錯誤(cron作業執行完全丟失並且根本無法執行)是不可接受的。我們非常重視安排錯誤報告。
  • 要謹慎對待pod evictions 和安全終止例項,以免作業過於頻繁地終止。
  • 需要一個好的遷移計劃。
建立一個Kubernetes叢集

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

我們希望將Kubernetes整合到現有的基礎架構中。與現有系統無縫整合,以便進行日誌記錄,證書管理,加密,網路安全,監控,AWS例項管理,部署,資料庫代理,內部DNS伺服器,配置管理以及更多。整合所有這些系統有時需要一點創造力,但總體上比試圖讓kubeadm / kops成為我們想要的更容易。

在信任並瞭解如何操作這些現有系統後,我們希望繼續在新的Kubernetes群集中使用。例如,安全證書管理是一個非常棘手的問題,已經有辦法頒發和管理證書。通過適當的整合,我們避免了為Kubernetes建立新的CA。

準確瞭解設定的引數是如何影響Kubernetes設定的。例如,在配置用於身份驗證的證書/CAs時,使用了超過12個引數。瞭解這些引數有助於在遇到身份驗證問題時更容易除錯設定。

對Kubernetes建立信心

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

戰略0:與其他公司交談

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

在談到Kubernetes這樣龐大而複雜的系統時,重要的是認真思考自己的用例,做自己的實驗,建立對自己環境的信心,並做出決定。 例如,你不該讀這篇部落格文章並得出結論:“Stripe正在成功使用Kubernetes,所以它也適用於我們!”

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

  • 優先考慮企業etcd叢集的可靠性(etcd是儲存所有Kubernetes叢集狀態的地方)。
  • 某些Kubernetes功能比其他功能更穩定,因此請小心Alpha功能。一些公司只有在穩定後才能使用穩定特性(例如,如果某個功能在1.8版本中保持穩定,則在使用它之前會等待1.9或1.10)。
  • 考慮使用託管的Kubernetes系統,如GKE / AKS / EKS。從頭開始建立高可用性Kubernetes系統是一項巨大的工作。 AWS在此專案中沒有託管的Kubernetes服務,所以這不適合我們。
  • 注意由覆蓋網路/軟體定義網路引入的額外網路延遲。

策略1:閱讀程式碼。

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

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

  • cron作業控制器是一個無狀態的服務(與其他Kubernetes元件一樣,除了etcd)。
  • 每10秒鐘,這個控制器呼叫syncAll函式:go wait.Until(jm.syncAll,10 * time.Second,stopCh)
  • syncAll函式從Kubernetes API中獲取所有cron作業,遍歷該列表,確定下一步應該執行哪些作業,然後啟動這些作業。

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

策略2:做負載測試

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

在一個3節點叢集中運行了測試,建立了1000個cron作業,每個任務每分鐘執行一次。這些工作中的每一個都簡單地執行bash -c ‘echo hello world’。我們選擇簡單的作業,是因為希望測試叢集的排程和編排能力,而不是叢集的總計算能力。

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

策略3:優先構建和測試高可用性etcd叢集。

在設定Kubernetes時,最重要的事情之一就是執行etcd。Etcd是Kubernetes叢集的核心,它是儲存叢集中所有資料的地方。除了etcd之外,其他一切都是無狀態的。如果etcd沒有執行,不能對Kubernetes叢集進行任何更改(儘管現有的服務將繼續執行!)

這張圖顯示了etcd是Kubernetes叢集的核心——API伺服器是etcd前面的無狀態REST/認證端點,然後其他元件通過API伺服器與etcd對話。 在執行時,有兩個要點需要牢記:

  • 設定複製,這樣叢集不會死如果你失去了一個節點。我們現在有三個etcd副本。
  • 確保足夠的I / O頻寬。我們的etcd版本有一個問題,一個具有高fsync延遲的節點可能觸發連續的leader elections,導致叢集無法使用。通過確保所有節點的I/O頻寬都比etcd的寫入數量多,從而彌補了這一點。

設定複製不是一個設定-忘記操作。我們仔細地測試後發現可能會丟失一個etcd節點,並且叢集優雅地恢復了。

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

  • 設定複製
  • 監控etcd服務是可用的
  • 寫一些簡單的工具,以便輕鬆建立新的etcd節點,並加入到叢集當中
  • 編寫一些簡單的工具,以便我們可以輕鬆建立新的etcd節點並將它們加入到群集中
  • 補丁etcd的高整合,這樣我們可以在生產環境中執行超過1個 etcd叢集
  • 測試從一個etcd備份中恢復
  • 測試可以在不停機情況下重建整個叢集

很高興很早就做了這個測試。某個週五的早晨,在我們的生產叢集中,一個etcd節點停止了對ping的響應。我們得到了警報,終止了節點,帶來了一個新的節點,加入到叢集中,同時Kubernetes繼續執行。

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

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

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

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

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

  • 根據他們的重要程度大致排序
  • 將一些重複的工作遷移到Kubernetes。如果發現新的情況,快速回滾,修復問題,然後重試。

策略5:調查 Kubernetes bug 並修復它們

我們在專案開始時制定了一個規則:如果Kubernetes做了一些意外的事情,必須調查,找出原因,並提出補救措施。

調查每個問題都很耗時,但很重要。如果只是簡單地將Kubernetes的”古怪行為”看作是複雜的分散式系統的功能,我們擔心,因為它們會被呼叫導致產生bug叢集。

在使用了這種方法之後,我們發現並且修復了Kubernetes的幾個bug。

以下是測試中發現的一些問題:

  • 名稱超過52個字元的Cronjob無法安排作業。
  • Pods有時會永遠停留在掛起狀態。
  • 排程程式會每3個小時崩潰一次。
  • Flannel的hostgw後端沒有替換過時的路由表項

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

Kubernetes有bug,像所有的軟體一樣。特別是,我們非常頻繁地使用排程器(cron作業總是在建立新的pods),而排程器使用快取有時會導致bug、回退和崩潰。快取是困難的!但是程式碼庫是可接近的,我們已經能夠處理遇到的bug。

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

如果執行的是大型Kubernetes叢集,請仔細閱讀節點控制器文件,仔細地考慮設定,並進行廣泛測試。每次通過建立網路分割槽測試對這些設定的配置更改(例如,pod-驅逐超時),就會發生令人驚訝的事情。最好在測試中發現這些意外,而不是在生產中發現。

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

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

在叢集上進行了幾次練習之後,經常發現諸如監視或配置錯誤等問題。很高興在早期發現這些問題,而不是六個月後突然發現。

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

  • 終止Kubernetes API伺服器
  • 終止所有Kubernetes API伺服器並將其恢復(這非常有效)
  • 終止etcd節點
  • 從API伺服器中關閉Kubernetes叢集中的工作節點(以便它們無法通訊)。這導致節點上的所有pods被遷移到其他節點。

很高興看到Kubernetes如何應對我們投入的大量干擾。 Kubernetes的設計是為了適應錯誤 – 它有儲存所有狀態的etcd叢集,一個只是該資料庫的REST介面的API伺服器,以及一個協調所有叢集管理的無狀態控制器集合。

如果任何Kubernetes核心元件(API伺服器,控制器管理器或排程程式)被中斷或重新啟動,一旦它們出現,它們將從etcd讀取相關狀態並繼續無縫執行。這是我們希望的事情之一,而且在實踐中實際運作良好。

以下是測試中發現的一些問題:

  • “沒有得到paged,來修復監控。“
  • “當銷燬API伺服器例項並將其恢復後,需要人工干預。最好解決這個問題。“
  • “有時執行etcd故障轉移時,API伺服器會啟動超時請求,直至重新啟動。”

在執行這些測試後,針對發現的問題開發了補救措施:改進了監控,發現了固定配置問題,並提交了Kubernetes bug。

使cron作業易於使用

簡單地探討一下我們是如何使基於kubernetes的系統易於使用的。

最初的目標是設計一個執行cron作業的系統,團隊有信心運營和維護。一旦建立了對Kubernetes的信心,就需要工程師們輕鬆地配置和增加新的cron作業。我們開發了一個簡單的YAML配置格式,這樣使用者就不需要了解Kubernetes的內部結構來使用這個系統。這是我們開發的格式:

name: job-name-here

kubernetes:

schedule: ’15 */2 * * *’

command:

– ruby

– “/path/to/script.rb”

resources:

requests:

cpu: 0.1

memory: 128M

limits:

memory: 1024M

沒有做什麼特別的事情——我們編寫了一個簡單的程式,將這種格式轉換為Kubernetes cron作業配置,將其應用於kubectl。

我們還編寫了一個測試套件,以確保作業名稱不會太長,並且所有名稱都是惟一的。我們目前不使用cgroups來強化對大多數作業的記憶體限制,但計劃將來推出。

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

監控Kubernetes

監測Kubernetes叢集的內部狀態非常令人愉悅。我們使用kube-state-metrics軟體包進行監測,並使用一個名為veneurl – Prometheus的小型Go程式來獲取Prometheus的度量標準,將它們作為statsd指標釋出到我們的監控系統中。

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

還有一個監視器,用於檢查有沒有任何Pod在Pending狀態中卡住 – 每個Pod在5分鐘內開始在worker節點上執行,否則會收到警報。

Kubernetes未來計劃

設定Kubernetes,到順暢地執行生產程式碼並將所有cron作業遷移到新叢集,花了五個月的時間,三位工程師全職工作。我們投資學習Kubernetes的一個重要原因是希望能夠在Stripe中更廣泛地使用Kubernetes。

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

  • 為企業的Kubernetes專案,以及所有基礎設施專案,定義清晰的商業原因。瞭解業務案例和使用者的需求使專案變得更加容易。
  • 積極削減範圍。避免使用許多Kubernetes的基本特性來簡化叢集。這讓我們可以更快速地傳送訊息,例如,由於pod-to-pod聯網不是我們專案的必需條件,可以關閉節點之間的所有網路連線,並將Kubernetes的網路安全性推遲。

花大量時間學習如何正確地運營Kubernetes叢集。仔細測試邊界情況。分散式系統非常複雜,有很多潛在的問題。以前面的例子為例:如果節點控制器由於配置與API伺服器失去聯絡,那麼它可以殺死叢集中的所有pods。學習Kubernetes在每次配置更改後的表現如何,需要時間和精心的關注。

通過專注於這些原則,我們已經有信心在生產中使用Kubernetes。我們將繼續開發Kubernetes的使用,例如,我們正在關注AWS EKS的釋出。我們正在完成另一個系統的工作,訓練機器學習模型,並且正在探索將一些HTTP服務遷移到Kubernetes。隨著我們繼續在生產中執行Kubernetes時,我們計劃對開源專案做出貢獻。

原文作者:Julia Evans

原文連結:

Learning to operate Kubernetes reliably

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