(译)An introduction to Kubernetes
原文:https://www.jeremyjordan.me/kubernetes/(博客園團(tuán)隊(duì)推薦的)
這篇博客文章將對Kubernetes進(jìn)行介紹,以便您了解該工具背后的動機(jī),含義以及使用方式。在后續(xù)文章中,我將討論如何使用更具體的(數(shù)據(jù)科學(xué))示例來利用Kubernetes增強(qiáng)數(shù)據(jù)科學(xué)工作負(fù)載。但是,這有助于您首先了解基本原理-這是本文的重點(diǎn)。
先決條件:我將假設(shè)您熟悉Docker等容器技術(shù)。如果您沒有構(gòu)建和運(yùn)行容器映像的經(jīng)驗(yàn),建議您先熟悉之后,在繼續(xù)閱讀本文
總覽
這是我們將在本文中討論的內(nèi)容。
Kubernetes有什么意義?
Kubernetes通常被描述為容器編排平臺。為了理解確切的含義,它有助于重新審視容器的用途,缺少的內(nèi)容以及Kubernetes如何填補(bǔ)這一空白。
注意:您還將看到Kubernetes其簡稱numeronym,K8S。這意味著同一件事,只是更容易鍵入。
為什么我們喜歡容器?容器提供了一種輕量級的機(jī)制來隔離應(yīng)用程序的環(huán)境。對于給定的應(yīng)用程序,我們可以指定要安裝的系統(tǒng)配置和庫,而不必?fù)?dān)心與可能在同一臺物理計(jì)算機(jī)上運(yùn)行的其他應(yīng)用程序產(chǎn)生沖突。我們將每個應(yīng)用程序封裝為容器映像(container image)可以在任何機(jī)器上可靠地執(zhí)行*(只要它能夠運(yùn)行容器映像),從而為我們提供了可移植性,以實(shí)現(xiàn)從開發(fā)到部署的平穩(wěn)過渡。此外,由于每個應(yīng)用程序都是獨(dú)立的,無需擔(dān)心環(huán)境沖突,因此將多個工作負(fù)載放置在同一臺物理計(jì)算機(jī)上并實(shí)現(xiàn)更高的資源(內(nèi)存和CPU)利用率更加容易-最終降低了成本。
缺少的東西?但是,如果您的容器死了怎么辦?甚至更糟的是,如果運(yùn)行您的容器的計(jì)算機(jī)發(fā)生故障,會發(fā)生什么?容器沒有提供容錯(fault tolerance)解決方案。或者,如果您有多個需要通信的容器,該如何在容器之間實(shí)現(xiàn)聯(lián)網(wǎng)?當(dāng)您旋轉(zhuǎn)單個容器時,此變化如何?容器網(wǎng)絡(luò)(networking?)很容易變成一團(tuán)糟。最后,假設(shè)您的生產(chǎn)環(huán)境由多臺機(jī)器組成-您如何決定使用哪臺機(jī)器來運(yùn)行容器?
Kubernetes作為容器編排平臺。我們可以使用容器編排平臺解決上述許多問題。
樂團(tuán)的負(fù)責(zé)人擁有音樂表演的愿景,并與音樂家溝通,以協(xié)調(diào)他們個人的樂器演奏,以實(shí)現(xiàn)總體愿景。作為系統(tǒng)的架構(gòu)師,您的工作只是簡單地創(chuàng)作音樂(指定要運(yùn)行的容器),然后將控制權(quán)移交給樂團(tuán)總監(jiān)(容器編排平臺)以實(shí)現(xiàn)該愿景。
容器編排平臺管理單個容器的整個生命周期,根據(jù)需要擴(kuò)展和關(guān)閉資源。如果某個容器意外關(guān)閉,編排平臺將通過在其位置啟動另一個容器來作出反應(yīng)。
最重要的是,編排平臺為應(yīng)用程序之間的通信提供了一種機(jī)制,即使底層的單個容器被創(chuàng)建和銷毀也是如此。
最后,在給定(1)一組要運(yùn)行的容器工作負(fù)載和(2)集群上的一組計(jì)算機(jī)的情況下,容器協(xié)調(diào)器將檢查每個容器并確定最佳的計(jì)算機(jī)來調(diào)度該工作負(fù)載。要了解為什么這很有價值,請觀看Kelsey Hightower(17:47-20:55)使用俄羅斯方塊示例游戲來說明自動化部署和容器編排之間的區(qū)別。
設(shè)計(jì)原則。
現(xiàn)在我們大致了解了容器編排的動機(jī),讓我們花一些時間來討論Kubernetes背后的動機(jī)設(shè)計(jì)原則。它有助于理解這些原理,以便您可以按預(yù)期使用該工具。
陳述式
也許Kubernetes中最重要的設(shè)計(jì)原則是,我們僅定義系統(tǒng)的期望狀態(tài),并讓Kubernetes自動化工作以確保系統(tǒng)的實(shí)際狀態(tài)反映這些期望。這使您免于在大多數(shù)事物損壞時進(jìn)行修復(fù)的責(zé)任;你只需說明你的系統(tǒng)是什么應(yīng)該看起來像一個理想的狀態(tài)。Kubernetes將檢測到系統(tǒng)的實(shí)際狀態(tài)何時不符合這些期望,它將代表您進(jìn)行干預(yù)以解決問題。這使我們的系統(tǒng)能夠自我修復(fù)并對問題做出反應(yīng),而無需人工干預(yù)。
系統(tǒng)的“狀態(tài)”由一組對象定義。每個Kubernetes對象具有(1)一個規(guī)范在其中提供所期望的狀態(tài)和(2)的狀態(tài)反映了對象的當(dāng)前狀態(tài)。Kubernetes維護(hù)所有對象規(guī)范的列表,并不斷輪詢每個對象,以確保其狀態(tài)與規(guī)范相等。如果對象無響應(yīng),Kubernetes將啟動一個新版本來替換它。如果對象的狀態(tài)偏離了規(guī)范,Kubernetes將發(fā)出必要的命令以將該對象驅(qū)動回到其所需狀態(tài)。
分布式
對于一定的操作規(guī)模,有必要將您的應(yīng)用程序設(shè)計(jì)為分布式系統(tǒng)。Kubernetes旨在為此類分布式系統(tǒng)提供基礎(chǔ)設(shè)施層,產(chǎn)生干凈的抽象以在一組機(jī)器(統(tǒng)稱為集群)之上構(gòu)建應(yīng)用程序。更具體地說,Kubernetes提供了一個用于與該集群交互的統(tǒng)一界面,因此您不必?fù)?dān)心與每臺機(jī)器進(jìn)行單獨(dú)通信。
解耦
容器開發(fā)通常建議單一關(guān)注。結(jié)果,開發(fā)容器化應(yīng)用程序非常適合微服務(wù)架構(gòu)設(shè)計(jì)模式,該模式建議“將軟件應(yīng)用程序設(shè)計(jì)為可獨(dú)立部署的服務(wù)套件”。
Kubernetes中提供的抽象自然支持分離服務(wù)的思想,該服務(wù)可以獨(dú)立縮放和更新。這些服務(wù)在邏輯上是分開的,并通過定義良好的API進(jìn)行通信。這種邏輯上的分離使團(tuán)隊(duì)可以更快地將更改部署到生產(chǎn)中,因?yàn)槊總€服務(wù)都可以在獨(dú)立的發(fā)布周期內(nèi)運(yùn)行(前提是他們遵守現(xiàn)有的API合約)。
不變的基礎(chǔ)設(shè)施
為了從容器和容器編排中獲得最大收益,您應(yīng)該部署不可變的基礎(chǔ)結(jié)構(gòu)。這是不是應(yīng)該登錄到計(jì)算機(jī)上的容器以進(jìn)行更改(例如,更新庫),而是應(yīng)該構(gòu)建新的容器映像,部署新版本并終止舊版本。在項(xiàng)目的生命周期(開發(fā)->測試->生產(chǎn))中跨環(huán)境過渡時,您應(yīng)該使用相同的容器映像,并且只能修改容器映像外部的配置(例如,通過安裝配置文件)。
這一點(diǎn)非常重要,因?yàn)槿萜鞅辉O(shè)計(jì)為短暫的,隨時可以被另一個容器實(shí)例替換。如果您的原始容器處于突變狀態(tài)(例如,手動配置),但是由于運(yùn)行狀況檢查失敗而被關(guān)閉,則在其位置旋轉(zhuǎn)的新容器不會反映這些手動更改,并可能破壞您的應(yīng)用程序。
當(dāng)您維護(hù)不可變的基礎(chǔ)結(jié)構(gòu)時,將應(yīng)用程序回滾到以前的狀態(tài)(例如,如果發(fā)生錯誤)也變得更加容易-您可以簡單地更新配置以使用較舊的容器映像。
Kubernetes中的基本對象。
之前,我提到過,我們通過Kubernetes?對象的集合描述了系統(tǒng)的期望狀態(tài)。到目前為止,我們對Kubernetes的討論還相對抽象和高層次。在本節(jié)中,我們將通過覆蓋Kubernetes中可用的基本對象,深入探討有關(guān)如何在Kubernetes上部署應(yīng)用程序的更多細(xì)節(jié)。
可以使用YAML或JSON文件定義Kubernetes對象。這些定義對象的文件通常稱為清單(manifests)。將這些清單保留在版本控制的存儲庫中是一個好習(xí)慣,該存儲庫可作為有關(guān)集群上正在運(yùn)行哪些對象的唯一事實(shí)來源。
Pod
pod對象是Kubernetes的基本構(gòu)建塊,由一個或多個(緊密相關(guān)的)的容器,一個共享的網(wǎng)絡(luò)層,和共享文件系統(tǒng)的卷。與容器類似,Pods被設(shè)計(jì)為短暫的-不會期望特定的單個POD會長期存在。
通常,您不會在清單中顯式創(chuàng)建Pod對象,因?yàn)槭褂酶呒壍慕M件來為您管理Pod對象通常更簡單。
部署方式
一個部署對象包括由模板和副本數(shù)量(模板的多少副本,我們要運(yùn)行)定義的pods的集合。您可以為副本數(shù)設(shè)置特定的值,也可以使用單獨(dú)的Kubernetes資源(例如,水平Pod自動縮放器)根據(jù)系統(tǒng)指標(biāo)(例如CPU利用率)來控制副本數(shù)。
注意:Deployment對象的控制器實(shí)際上在內(nèi)部創(chuàng)建了另一個對象ReplicaSet。但是,這是作為用戶從您那里抽象出來的。
雖然您不能依賴任何一個Pod來無限期地運(yùn)行,但是您可以依靠集群將始終嘗試使n個Pod可用的事實(shí)(其中n由您指定的副本數(shù)定義)。?如果我們有一個部署的副本數(shù)為10的Deployment,并且其中3個Pod因機(jī)器故障而崩潰,那么將安排另外3個Pod在群集中的另一臺計(jì)算機(jī)上運(yùn)行。因此,Deployment最適合無狀態(tài)應(yīng)用程序,在這些應(yīng)用程序中Pod可以隨時更換而不會損壞。
以下YAML文件提供了有關(guān)如何定義Deployment對象的帶注釋的示例。在此示例中,我們要運(yùn)行一個容器的10個實(shí)例,該實(shí)例通過REST接口提供ML模型。
注意:為了讓Kubernetes知道此工作負(fù)載可能有多計(jì)算密集型,我們還應(yīng)該在Pod模板規(guī)范中提供資源限制。
部署還允許我們指定當(dāng)我們有新版本的容器映像時我們希望如何推出更新;這篇博客文章很好地概述了您的不同選擇。如果我們想覆蓋默認(rèn)值,我們將strategy在object下包含一個附加字段spec。Kubernetes將確保正常關(guān)閉運(yùn)行舊容器映像的Pod并啟動運(yùn)行新容器映像的新Pod。
服務(wù)
Kubernetes中的每個Pod都分配有一個唯一的IP地址,我們可以用來與之通信。但是,由于Pod是短暫的,因此很難將流量發(fā)送到所需的容器。例如,讓我們考慮上面的“部署”,其中有10個Pod運(yùn)行一個容器,通過REST為機(jī)器學(xué)習(xí)模型提供服務(wù)。如果作為部署的一部分運(yùn)行的Pod集合可以隨時更改,我們?nèi)绾闻c服務(wù)器可靠地通信?這是服務(wù)對象輸入圖片的地方。Kubernetes服務(wù)為您提供了一個穩(wěn)定的端點(diǎn),即使由于更新,擴(kuò)展和故障導(dǎo)致確切的基礎(chǔ)Pod發(fā)生變化,它也可以用于將流量引導(dǎo)到所需的Pod。服務(wù)根據(jù)標(biāo)簽知道應(yīng)將流量發(fā)送到哪個Pod?(鍵值對),我們在Pod元數(shù)據(jù)中定義。
注意:這篇博客文章很好地解釋了如何實(shí)際路由流量。
在此示例中,我們的服務(wù)使用標(biāo)簽將流量發(fā)送到所有健康的Pod?app="ml-model"。
以下YAML文件提供了一個示例,說明了我們?nèi)绾螄@早期的Deployment示例包裝Service。
Ingress
盡管“服務(wù)”使我們可以在穩(wěn)定的終結(jié)點(diǎn)后面公開應(yīng)用程序,但該終結(jié)點(diǎn)僅可用于內(nèi)部群集通信。如果我們想將應(yīng)用程序暴露給集群外部的流量,則需要定義一個Ingress對象。
這種方法的好處在于,您可以選擇公開哪些服務(wù)。例如,假設(shè)除了我們的機(jī)器學(xué)習(xí)模型服務(wù)外,我們還有一個UI,該UI利用了模型的預(yù)測作為大型應(yīng)用程序的一部分。我們可能選擇僅使UI可用于公共流量,從而阻止用戶直接查詢服務(wù)模型服務(wù)。
以下YAML文件為上述示例定義了一個Ingress對象,使UI可以公開訪問。
Job
到目前為止,我已經(jīng)描述過的Kubernetes對象可以組成可靠的,長期運(yùn)行的服務(wù)。相反,當(dāng)您要執(zhí)行離散任務(wù)時,Job對象很有用。例如,假設(shè)我們想根據(jù)前一天收集的信息每天重新訓(xùn)練模型。每天,我們都希望啟動一個容器來執(zhí)行預(yù)定義的工作負(fù)載(例如train.py腳本),然后在培訓(xùn)結(jié)束時關(guān)閉它。喬布斯為我們提供了做到這一點(diǎn)的能力!如果由于某種原因我們的容器在完成腳本之前崩潰了,Kubernetes將通過在其位置啟動一個新Pod來完成工作來做出反應(yīng)。對于Job對象,對象的“所需狀態(tài)”是作業(yè)的完成。
以下YAML定義了一個用于訓(xùn)練機(jī)器學(xué)習(xí)模型的示例Job(假設(shè)在中定義了訓(xùn)練代碼train.py)。
注意:此作業(yè)規(guī)范將僅執(zhí)行一次訓(xùn)練。如果我們想每天執(zhí)行此作業(yè),則可以定義一個CronJob對象。
...還有很多。
上面討論的對象當(dāng)然不是Kubernetes中可用資源類型的詳盡列表。在部署應(yīng)用程序時,您可能會發(fā)現(xiàn)有用的其他一些對象包括:
Volume:用于管理安裝在Pod上的目錄
Secret:用于存儲敏感憑證
NameSpace:用于分隔群集上的資源
ConfigMap:用于指定要作為文件掛載的應(yīng)用程序配置值
HorizontalPodAutoscaler:用于基于現(xiàn)有Pod的當(dāng)前資源利用率擴(kuò)展部署
StatefulSet:與Deployment類似,但適用于需要運(yùn)行有狀態(tài)應(yīng)用程序的情況
怎么樣?Kubernetes control plane(控制平面)。
至此,您可能想知道Kubernetes如何能夠采用我們所有的對象規(guī)范并在集群上實(shí)際執(zhí)行這些工作負(fù)載。在本節(jié)中,我們將討論組成Kubernetes 控制平面的組件,這些組件控制如何在集群上執(zhí)行,監(jiān)視和維護(hù)工作負(fù)載。
在深入研究之前,重要的是區(qū)分集群上的兩類計(jì)算機(jī):
一個master node主節(jié)點(diǎn)包含了大部分,這使得我們的控制平面,我們將在下面討論的組件。在大多數(shù)中等大小的集群中,您只有一個主節(jié)點(diǎn),盡管可以有多個主節(jié)點(diǎn)來實(shí)現(xiàn)高可用性。如果您使用云提供商的托管Kubernetes服務(wù),則它們通常會抽象化主節(jié)點(diǎn),而您不必進(jìn)行管理或?yàn)榇烁顿M(fèi)。
一個worker node工作節(jié)點(diǎn)是實(shí)際運(yùn)行我們的應(yīng)用程序工作負(fù)載的機(jī)器。可以針對集群上的不同類型的工作負(fù)載量身定制多種不同的計(jì)算機(jī)類型。例如,您可能具有一些GPU優(yōu)化的節(jié)點(diǎn)以進(jìn)行更快的模型訓(xùn)練,然后使用CPU優(yōu)化的節(jié)點(diǎn)進(jìn)行服務(wù)。定義對象規(guī)格時,可以指定有關(guān)將工作負(fù)載分配給哪種機(jī)器的首選項(xiàng)。
現(xiàn)在,讓我們深入了解主節(jié)點(diǎn)上的主要組件。與Kubernetes通信以提供新的或更新的對象規(guī)范時,您正在與API服務(wù)器進(jìn)行通信。
更具體地說,API服務(wù)器驗(yàn)證更新對象的請求,并充當(dāng)有關(guān)集群當(dāng)前狀態(tài)的問題的統(tǒng)一接口。但是,集群的狀態(tài)存儲在etcd(分布式鍵值存儲)中。我們將使用etcd來保存有關(guān)以下信息:集群配置,對象規(guī)范,對象狀態(tài),集群上的節(jié)點(diǎn)以及分配對象在哪些節(jié)點(diǎn)上運(yùn)行。
注意:etcd是我們控制平面中唯一的有狀態(tài)組件,所有其他組件都是無狀態(tài)的。
說到應(yīng)該在哪里運(yùn)行對象,調(diào)度程序scheduler?負(fù)責(zé)確定這一點(diǎn)!調(diào)度程序?qū)⒃儐朅PI服務(wù)器(然后將與etcd通信)尚未分配給計(jì)算機(jī)的對象。然后,調(diào)度程序?qū)⒋_定這些對象應(yīng)分配給哪些機(jī)器,并將回復(fù)API服務(wù)器以反映此分配(該分配將傳播到etcd)。
我們將在本文中討論的主節(jié)點(diǎn)上的最后一個組件是controller-manager,它通過API服務(wù)器監(jiān)視集群的狀態(tài),以查看集群的當(dāng)前狀態(tài)是否符合我們的期望狀態(tài)。如果實(shí)際狀態(tài)與我們的期望狀態(tài)不同,則控制器管理器將通過API服務(wù)器進(jìn)行更改,以嘗試將集群驅(qū)動到期望狀態(tài)。控制器管理器由一組控制器controllers定義,每個負(fù)責(zé)管理集群上特定資源類型的對象。在非常高的級別上,控制器將監(jiān)視存儲在etcd中的特定資源類型(例如,部署),并為應(yīng)運(yùn)行的Pod創(chuàng)建規(guī)范以實(shí)現(xiàn)對象的所需狀態(tài)。然后,控制者有責(zé)任確保這些吊艙在運(yùn)行時保持健康,并在需要時關(guān)閉。
總結(jié)到目前為止我們所涵蓋的內(nèi)容...
接下來,讓我們討論在工作程序節(jié)點(diǎn)上運(yùn)行的控制平面組件。我們的工作程序節(jié)點(diǎn)上可用的大多數(shù)資源都花在了運(yùn)行我們的實(shí)際應(yīng)用程序上,但是我們的節(jié)點(diǎn)確實(shí)需要知道他們應(yīng)該運(yùn)行哪些Pod,以及如何與其他計(jì)算機(jī)上的Pod通信。我們將討論的控制平面的兩個最后組成部分恰好涵蓋了這兩個方面。
該kubelet作為一個節(jié)點(diǎn)的“代理人”,其與API服務(wù)器進(jìn)行通信,以查看哪些容器工作量被分配到節(jié)點(diǎn)。然后,它負(fù)責(zé)旋轉(zhuǎn)Pod以運(yùn)行這些分配的工作負(fù)載。當(dāng)節(jié)點(diǎn)首次加入集群時,kubelet負(fù)責(zé)向API服務(wù)器宣布節(jié)點(diǎn)的存在,以便調(diào)度程序可以為其分配容器。
最后,kube-proxy使容器能夠跨集群上的各個節(jié)點(diǎn)相互通信。該組件處理所有網(wǎng)絡(luò)問題,例如如何將流量轉(zhuǎn)發(fā)到適當(dāng)?shù)腜od。
希望到這一點(diǎn),您應(yīng)該能夠開始了解Kubernetes集群中事物的運(yùn)行方式。所有組件都通過API服務(wù)器進(jìn)行交互,我們將群集的狀態(tài)存儲在etcd中。有多種組件(通過API服務(wù)器)寫入etcd,以對集群進(jìn)行更改,并且集群上的節(jié)點(diǎn)(通過API服務(wù)器)偵聽etcd,以查看其應(yīng)運(yùn)行的Pod。
整個系統(tǒng)的設(shè)計(jì)使故障對整個群集的影響最小。例如,如果我們的主節(jié)點(diǎn)發(fā)生故障,那么我們的應(yīng)用程序都不會立即受到影響;在新的主節(jié)點(diǎn)上線之前,我們將無法對集群進(jìn)行任何進(jìn)一步的更改。
什么時候不應(yīng)該使用Kubernetes?
與每項(xiàng)新技術(shù)一樣,你會花費(fèi)一些時間,您了解它是如何工作的,以及它如何應(yīng)用于您正在構(gòu)建的應(yīng)用程序時。問“我真的需要Kubernetes嗎?”是一個合理的問題。因此,我將嘗試提供一些答案可能為否的示例情況。
您可以在單臺計(jì)算機(jī)上運(yùn)行工作負(fù)載。(Kubernetes可以看作是構(gòu)建分布式系統(tǒng)的平臺,但是如果不需要,則不應(yīng)構(gòu)建分布式系統(tǒng)!)
您的計(jì)算需求不多。(在本例中,用于編排框架的計(jì)算相對較高!)
您不需要高可用性,并且可以容忍停機(jī)時間。
您不會想到對已部署的服務(wù)進(jìn)行大量更改。
您已經(jīng)擁有一個滿意的有效工具棧。
您擁有一個單體架構(gòu),不打算將其分成微服務(wù)。(這可以回到原本打算使用的工具的狀態(tài)。)
您閱讀了這篇文章,并認(rèn)為“這很復(fù)雜”而不是“這很有用”。
- 參考:
朱莉婭·埃文斯(Julia Evans)-Kubernetes很酷的原因(https://jvns.ca/blog/2017/10/05/reasons-kubernetes-is-cool/)
朱莉婭·埃文斯(Julia Evans)-我對Kubernetes的一些了解(Julia的雜志對我的視覺解釋Kubernetes控制平面有很大的啟發(fā))(https://jvns.ca/blog/2017/06/04/learning-about-kubernetes/)
總結(jié)
以上是生活随笔為你收集整理的(译)An introduction to Kubernetes的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用ASP.NET Core 3.x 构
- 下一篇: WeihanLi.Npoi 支持 Sha