1. 程式人生 > >(譯)An introduction to Kubernetes

(譯)An introduction to Kubernetes

原文:https://www.jeremyjordan.me/kubernetes/(部落格園團隊推薦的)

 

這篇部落格文章將對Kubernetes進行介紹,以便您瞭解該工具背後的動機,含義以及使用方式。在後續文章中,我將討論如何使用更具體的(資料科學)示例來利用Kubernetes增強資料科學工作負載。但是,這有助於您首先了解基本原理-這是本文的重點。

先決條件:我將假設您熟悉Docker等容器技術。如果您沒有構建和執行容器映像的經驗,建議您先熟悉之後,在繼續閱讀本文

總覽

 這是我們將在本文中討論的內容。

Kubernetes有什麼意義?

Kubernetes通常被描述為容器編排平臺。為了理解確切的含義,它有助於重新審視容器的用途,缺少的內容以及Kubernetes如何填補這一空白。

注意:您還將看到Kubernetes其簡稱numeronym,K8S。這意味著同一件事,只是更容易鍵入。

為什麼我們喜歡容器?容器提供了一種輕量級的機制來隔離應用程式的環境。對於給定的應用程式,我們可以指定要安裝的系統配置和庫,而不必擔心與可能在同一臺物理計算機上執行的其他應用程式產生衝突。我們將每個應用程式封裝為容器映像(container image)可以在任何機器上可靠地執行*(只要它能夠執行容器映像),從而為我們提供了可移植性,以實現從開發到部署的平穩過渡。此外,由於每個應用程式都是獨立的,無需擔心環境衝突,因此將多個工作負載放置在同一臺物理計算機上並實現更高的資源(記憶體和CPU)利用率更加容易-最終降低了成本。

缺少的東西?但是,如果您的容器死了怎麼辦?甚至更糟的是,如果執行您的容器的計算機發生故障,會發生什麼?容器沒有提供容錯(fault tolerance)解決方案。或者,如果您有多個需要通訊的容器,該如何在容器之間實現聯網?當您旋轉單個容器時,此變化如何?容器網路(networking )很容易變成一團糟。最後,假設您的生產環境由多臺機器組成-您如何決定使用哪臺機器來執行容器?

Kubernetes作為容器編排平臺。我們可以使用容器編排平臺解決上述許多問題。

樂團的負責人擁有音樂表演的願景,並與音樂家溝通,以協調他們個人的樂器演奏,以實現總體願景。作為系統的架構師,您的工作只是簡單地

創作音樂(指定要執行的容器),然後將控制權移交給樂團總監(容器編排平臺)以實現該願景。

容器編排平臺管理單個容器的整個生命週期,根據需要擴充套件和關閉資源。如果某個容器意外關閉,編排平臺將通過在其位置啟動另一個容器來作出反應。

最重要的是,編排平臺為應用程式之間的通訊提供了一種機制,即使底層的單個容器被建立和銷燬也是如此。

最後,在給定(1)一組要執行的容器工作負載和(2)叢集上的一組計算機的情況下,容器協調器將檢查每個容器並確定最佳的計算機來排程該工作負載。要了解為什麼這很有價值,請觀看Kelsey Hightower(17:47-20:55)使用俄羅斯方塊示例遊戲來說明自動化部署和容器編排之間的區別。

設計原則。

現在我們大致瞭解了容器編排的動機,讓我們花一些時間來討論Kubernetes背後的動機設計原則。它有助於理解這些原理,以便您可以按預期使用該工具。

陳述式

也許Kubernetes中最重要的設計原則是,我們僅定義系統的期望狀態,並讓Kubernetes自動化工作以確保系統的實際狀態反映這些期望。這使您免於在大多數事物損壞時進行修復的責任;你只需說明你的系統是什麼應該看起來像一個理想的狀態。Kubernetes將檢測到系統的實際狀態何時不符合這些期望,它將代表您進行干預以解決問題。這使我們的系統能夠自我修復並對問題做出反應,而無需人工干預。

系統的“狀態”由一組物件定義。每個Kubernetes物件具有(1)一個規範在其中提供所期望的狀態和(2)的狀態反映了物件的當前狀態。Kubernetes維護所有物件規範的列表,並不斷輪詢每個物件,以確保其狀態與規範相等。如果物件無響應,Kubernetes將啟動一個新版本來替換它。如果物件的狀態偏離了規範,Kubernetes將發出必要的命令以將該物件驅動回到其所需狀態。

分散式

對於一定的操作規模,有必要將您的應用程式設計為分散式系統。Kubernetes旨在為此類分散式系統提供基礎設施層,產生乾淨的抽象以在一組機器(統稱為叢集)之上構建應用程式。更具體地說,Kubernetes提供了一個用於與該叢集互動的統一介面,因此您不必擔心與每臺機器進行單獨通訊。

解耦

容器開發通常建議單一關注。結果,開發容器化應用程式非常適合微服務架構設計模式,該模式建議“將軟體應用程式設計為可獨立部署的服務套件”。

Kubernetes中提供的抽象自然支援分離服務的思想,該服務可以獨立縮放和更新。這些服務在邏輯上是分開的,並通過定義良好的API進行通訊。這種邏輯上的分離使團隊可以更快地將更改部署到生產中,因為每個服務都可以在獨立的釋出週期內執行(前提是他們遵守現有的API合約)。

不變的基礎設施

為了從容器和容器編排中獲得最大收益,您應該部署不可變的基礎結構。這是不是應該登入到計算機上的容器以進行更改(例如,更新庫),而是應該構建新的容器映像,部署新版本並終止舊版本。在專案的生命週期(開發->測試->生產)中跨環境過渡時,您應該使用相同的容器映像,並且只能修改容器映像外部的配置(例如,通過安裝配置檔案)。

這一點非常重要,因為容器被設計為短暫的,隨時可以被另一個容器例項替換。如果您的原始容器處於突變狀態(例如,手動配置),但是由於執行狀況檢查失敗而被關閉,則在其位置旋轉的新容器不會反映這些手動更改,並可能破壞您的應用程式。

當您維護不可變的基礎結構時,將應用程式回滾到以前的狀態(例如,如果發生錯誤)也變得更加容易-您可以簡單地更新配置以使用較舊的容器映像。

Kubernetes中的基本物件。

之前,我提到過,我們通過Kubernetes 物件的集合描述了系統的期望狀態。到目前為止,我們對Kubernetes的討論還相對抽象和高層次。在本節中,我們將通過覆蓋Kubernetes中可用的基本物件,深入探討有關如何在Kubernetes上部署應用程式的更多細節。

可以使用YAML或JSON檔案定義Kubernetes物件。這些定義物件的檔案通常稱為清單(manifests)。將這些清單保留在版本控制的儲存庫中是一個好習慣,該儲存庫可作為有關叢集上正在執行哪些物件的唯一事實來源。

Pod

pod物件是Kubernetes的基本構建塊,由一個或多個(緊密相關的)的容器,一個共享的網路層,和共享檔案系統的卷。與容器類似,Pods被設計為短暫的-不會期望特定的單個POD會長期存在。

通常,您不會在清單中顯式建立Pod物件,因為使用更高階的元件來為您管理Pod物件通常更簡單。

部署方式

 

一個部署物件包括由模板和副本數量(模板的多少副本,我們要執行)定義的pods的集合。您可以為副本數設定特定的值,也可以使用單獨的Kubernetes資源(例如,水平Pod自動縮放器)根據系統指標(例如CPU利用率)來控制副本數。

注意:Deployment物件的控制器實際上在內部建立了另一個物件ReplicaSet。但是,這是作為使用者從您那裡抽象出來的。

雖然您不能依賴任何一個Pod來無限期地執行,但是您可以依靠叢集將始終嘗試使n個Pod可用的事實(其中n由您指定的副本數定義)。 如果我們有一個部署的副本數為10的Deployment,並且其中3個Pod因機器故障而崩潰,那麼將安排另外3個Pod在群集中的另一臺計算機上執行。因此,Deployment最適合無狀態應用程式,在這些應用程式中Pod可以隨時更換而不會損壞。

以下YAML檔案提供了有關如何定義Deployment物件的帶註釋的示例。在此示例中,我們要執行一個容器的10個例項,該例項通過REST介面提供ML模型。

注意:為了讓Kubernetes知道此工作負載可能有多計算密集型,我們還應該在Pod模板規範中提供資源限制。

部署還允許我們指定當我們有新版本的容器映像時我們希望如何推出更新;這篇部落格文章很好地概述了您的不同選擇。如果我們想覆蓋預設值,我們將strategy在object下包含一個附加欄位spec。Kubernetes將確保正常關閉執行舊容器映像的Pod並啟動執行新容器映像的新Pod。

服務

Kubernetes中的每個Pod都分配有一個唯一的IP地址,我們可以用來與之通訊。但是,由於Pod是短暫的,因此很難將流量傳送到所需的容器。例如,讓我們考慮上面的“部署”,其中有10個Pod執行一個容器,通過REST為機器學習模型提供服務。如果作為部署的一部分執行的Pod集合可以隨時更改,我們如何與伺服器可靠地通訊?這是服務物件輸入圖片的地方。Kubernetes服務為您提供了一個穩定的端點,即使由於更新,擴充套件和故障導致確切的基礎Pod發生變化,它也可以用於將流量引導到所需的Pod。服務根據標籤知道應將流量傳送到哪個Pod (鍵值對),我們在Pod元資料中定義。

注意:這篇部落格文章很好地解釋瞭如何實際路由流量。

在此示例中,我們的服務使用標籤將流量傳送到所有健康的Pod app="ml-model"

 

以下YAML檔案提供了一個示例,說明了我們如何圍繞早期的Deployment示例包裝Service。

Ingress

儘管“服務”使我們可以在穩定的終結點後面公開應用程式,但該終結點僅可用於內部群集通訊。如果我們想將應用程式暴露給叢集外部的流量,則需要定義一個Ingress物件。

這種方法的好處在於,您可以選擇公開哪些服務。例如,假設除了我們的機器學習模型服務外,我們還有一個UI,該UI利用了模型的預測作為大型應用程式的一部分。我們可能選擇僅使UI可用於公共流量,從而阻止使用者直接查詢服務模型服務。

以下YAML檔案為上述示例定義了一個Ingress物件,使UI可以公開訪問。

Job

到目前為止,我已經描述過的Kubernetes物件可以組成可靠的,長期執行的服務。相反,當您要執行離散任務時,Job物件很有用。例如,假設我們想根據前一天收集的資訊每天重新訓練模型。每天,我們都希望啟動一個容器來執行預定義的工作負載(例如train.py指令碼),然後在培訓結束時關閉它。喬布斯為我們提供了做到這一點的能力!如果由於某種原因我們的容器在完成指令碼之前崩潰了,Kubernetes將通過在其位置啟動一個新Pod來完成工作來做出反應。對於Job物件,物件的“所需狀態”是作業的完成。

以下YAML定義了一個用於訓練機器學習模型的示例Job(假設在中定義了訓練程式碼train.py)。

注意:此作業規範將僅執行一次訓練。如果我們想每天執行此作業,則可以定義一個CronJob物件。

...還有很多。

上面討論的物件當然不是Kubernetes中可用資源型別的詳盡列表。在部署應用程式時,您可能會發現有用的其他一些物件包括:

  • Volume:用於管理安裝在Pod上的目錄

  • Secret:用於儲存敏感憑證

  • NameSpace:用於分隔群集上的資源

  • ConfigMap:用於指定要作為檔案掛載的應用程式配置值

  • HorizontalPodAutoscaler:用於基於現有Pod的當前資源利用率擴充套件部署

  • StatefulSet:與Deployment類似,但適用於需要執行有狀態應用程式的情況

怎麼樣?Kubernetes control plane(控制平面)。

至此,您可能想知道Kubernetes如何能夠採用我們所有的物件規範並在叢集上實際執行這些工作負載。在本節中,我們將討論組成Kubernetes 控制平面的元件,這些元件控制如何在叢集上執行,監視和維護工作負載。

 

在深入研究之前,重要的是區分叢集上的兩類計算機:

  • 一個master node主節點包含了大部分,這使得我們的控制平面,我們將在下面討論的元件。在大多數中等大小的叢集中,您只有一個主節點,儘管可以有多個主節點來實現高可用性。如果您使用雲提供商的託管Kubernetes服務,則它們通常會抽象化主節點,而您不必進行管理或為此付費。

  • 一個worker node工作節點是實際執行我們的應用程式工作負載的機器。可以針對叢集上的不同型別的工作負載量身定製多種不同的計算機型別。例如,您可能具有一些GPU優化的節點以進行更快的模型訓練,然後使用CPU優化的節點進行服務。定義物件規格時,可以指定有關將工作負載分配給哪種機器的首選項。

現在,讓我們深入瞭解主節點上的主要元件。與Kubernetes通訊以提供新的或更新的物件規範時,您正在與API伺服器進行通訊。

更具體地說,API伺服器驗證更新物件的請求,並充當有關叢集當前狀態的問題的統一介面。但是,叢集的狀態儲存在etcd(分散式鍵值儲存)中。我們將使用etcd來儲存有關以下資訊:叢集配置,物件規範,物件狀態,叢集上的節點以及分配物件在哪些節點上執行。

注意:etcd是我們控制平面中唯一的有狀態元件,所有其他元件都是無狀態的。

說到應該在哪裡執行物件,排程程式scheduler 負責確定這一點!排程程式將詢問API伺服器(然後將與etcd通訊)尚未分配給計算機的物件。然後,排程程式將確定這些物件應分配給哪些機器,並將回覆API伺服器以反映此分配(該分配將傳播到etcd)。

我們將在本文中討論的主節點上的最後一個元件是controller-manager,它通過API伺服器監視叢集的狀態,以檢視叢集的當前狀態是否符合我們的期望狀態。如果實際狀態與我們的期望狀態不同,則控制器管理器將通過API伺服器進行更改,以嘗試將叢集驅動到期望狀態。控制器管理器由一組控制器controllers定義,每個負責管理叢集上特定資源型別的物件。在非常高的級別上,控制器將監視儲存在etcd中的特定資源型別(例如,部署),併為應執行的Pod建立規範以實現物件的所需狀態。然後,控制者有責任確保這些吊艙在執行時保持健康,並在需要時關閉。

總結到目前為止我們所涵蓋的內容...

接下來,讓我們討論在工作程式節點上執行的控制平面元件。我們的工作程式節點上可用的大多數資源都花在了執行我們的實際應用程式上,但是我們的節點確實需要知道他們應該執行哪些Pod,以及如何與其他計算機上的Pod通訊。我們將討論的控制平面的兩個最後組成部分恰好涵蓋了這兩個方面。

該kubelet作為一個節點的“代理人”,其與API伺服器進行通訊,以檢視哪些容器工作量被分配到節點。然後,它負責旋轉Pod以執行這些分配的工作負載。當節點首次加入叢集時,kubelet負責向API伺服器宣佈節點的存在,以便排程程式可以為其分配容器。

最後,kube-proxy使容器能夠跨叢集上的各個節點相互通訊。該元件處理所有網路問題,例如如何將流量轉發到適當的Pod。

希望到這一點,您應該能夠開始瞭解Kubernetes叢集中事物的執行方式。所有元件都通過API伺服器進行互動,我們將群集的狀態儲存在etcd中。有多種元件(通過API伺服器)寫入etcd,以對叢集進行更改,並且叢集上的節點(通過API伺服器)偵聽etcd,以檢視其應執行的Pod。

整個系統的設計使故障對整個群集的影響最小。例如,如果我們的主節點發生故障,那麼我們的應用程式都不會立即受到影響;在新的主節點上線之前,我們將無法對叢集進行任何進一步的更改。

什麼時候不應該使用Kubernetes?

與每項新技術一樣,你會花費一些時間,您瞭解它是如何工作的,以及它如何應用於您正在構建的應用程式時。問“我真的需要Kubernetes嗎?”是一個合理的問題。因此,我將嘗試提供一些答案可能為否的示例情況。

  • 您可以在單臺計算機上執行工作負載。(Kubernetes可以看作是構建分散式系統的平臺,但是如果不需要,則不應構建分散式系統!

  • 您的計算需求不多。(在本例中,用於編排框架的計算相對較高!

  • 您不需要高可用性,並且可以容忍停機時間。

  • 您不會想到對已部署的服務進行大量更改。

  • 您已經擁有一個滿意的有效工具棧。

  • 您擁有一個單體架構,不打算將其分成微服務。(這可以回到原本打算使用的工具的狀態。

  • 您閱讀了這篇文章,並認為“這很複雜”而不是“這很有用”。


參考:

    • 朱莉婭·埃文斯(Julia Evans)-Kubernetes很酷的原因(https://jvns.ca/blog/2017/10/05/reasons-kubernetes-is-cool/)

    • 朱莉婭·埃文斯(Julia Evans)-我對Kubernetes的一些瞭解(Julia的雜誌對我的視覺解釋Kubernetes控制平面有很大的啟發)(https://jvns.ca/blog/2017/06/04/learning-about-kubernetes/)