原文:https://www.jeremyjordan.me/kubernetes/(博客園團隊推薦的)node
這篇博客文章將對Kubernetes進行介紹,以便您瞭解該工具背後的動機,含義以及使用方式。在後續文章中,我將討論如何使用更具體的(數據科學)示例來利用Kubernetes加強數據科學工做負載。可是,這有助於您首先了解基本原理-這是本文的重點。設計模式
先決條件:我將假設您熟悉Docker等容器技術。若是您沒有構建和運行容器映像的經驗,建議您先熟悉以後,在繼續閱讀本文服務器
這是咱們將在本文中討論的內容。網絡
Kubernetes一般被描述爲容器編排平臺。爲了理解確切的含義,它有助於從新審視容器的用途,缺乏的內容以及Kubernetes如何填補這一空白。架構
注意:您還將看到Kubernetes其簡稱numeronym,K8S。這意味着同一件事,只是更容易鍵入。app
爲何咱們喜歡容器?容器提供了一種輕量級的機制來隔離應用程序的環境。對於給定的應用程序,咱們能夠指定要安裝的系統配置和庫,而沒必要擔憂與可能在同一臺物理計算機上運行的其餘應用程序產生衝突。咱們將每一個應用程序封裝爲容器映像(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上部署應用程序的更多細節。
可使用YAML或JSON文件定義Kubernetes對象。這些定義對象的文件一般稱爲清單(manifests)。將這些清單保留在版本控制的存儲庫中是一個好習慣,該存儲庫可做爲有關集羣上正在運行哪些對象的惟一事實來源。
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能夠公開訪問。
到目前爲止,我已經描述過的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能夠看做是構建分佈式系統的平臺,可是若是不須要,則不該構建分佈式系統!)
您的計算需求很少。(在本例中,用於編排框架的計算相對較高!)
您不須要高可用性,而且能夠容忍停機時間。
您不會想到對已部署的服務進行大量更改。
您已經擁有一個滿意的有效工具棧。
您擁有一個單體架構,不打算將其分紅微服務。(這能夠回到本來打算使用的工具的狀態。)
您閱讀了這篇文章,並認爲「這很複雜」而不是「這頗有用」。
參考:
朱莉婭·埃文斯(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/)