新手也能看懂,Kubernetes其實(shí)很簡(jiǎn)單
Kubernetes 是這兩年最熱門、最被人熟知的技術(shù)了,它為軟件工程師提供了強(qiáng)大的容器編排能力,模糊了開發(fā)和運(yùn)維之間的邊界,讓我們開發(fā)、管理和維護(hù)一個(gè)大型的分布式系統(tǒng)和項(xiàng)目變得更加容易。
本文會(huì)先簡(jiǎn)單介紹 Kuberentes 的背景、依賴的技術(shù),它的架構(gòu)以及設(shè)計(jì)理念,***會(huì)提及一些關(guān)鍵概念和實(shí)現(xiàn)原理。
Kuberentes 的背景
作為一個(gè)目前在生產(chǎn)環(huán)境已經(jīng)廣泛使用的開源項(xiàng)目,Kubernetes 被定義成一個(gè)用于自動(dòng)化部署、擴(kuò)容和管理容器應(yīng)用的開源系統(tǒng);它將一個(gè)分布式軟件的一組容器打包成一個(gè)個(gè)更容易管理和發(fā)現(xiàn)的邏輯單元。
Kubernetes 是希臘語(yǔ)『舵手』的意思,它最開始由 Google 的幾位軟件工程師創(chuàng)立,深受公司內(nèi)部 Borg 和 Omega 項(xiàng)目的影響,很多設(shè)計(jì)都是從 Borg 中借鑒的,同時(shí)也對(duì) Borg 的缺陷進(jìn)行了改進(jìn)。
Kubernetes 目前是 Cloud Native Computing Foundation (CNCF) 的項(xiàng)目,并且是很多公司管理分布式系統(tǒng)的解決方案。
在 Kubernetes 統(tǒng)治了容器編排這一領(lǐng)域之前,其實(shí)也有很多容器編排方案,例如 compose 和 Swarm,但是在運(yùn)維大規(guī)模、復(fù)雜的集群時(shí),這些方案基本已經(jīng)都被 Kubernetes 替代了。
Kubernetes 將已經(jīng)打包好的應(yīng)用鏡像進(jìn)行編排,所以如果沒有容器技術(shù)的發(fā)展和微服務(wù)架構(gòu)中復(fù)雜的應(yīng)用關(guān)系,其實(shí)也很難找到合適的應(yīng)用場(chǎng)景去使用。
所以在這里我們會(huì)簡(jiǎn)單介紹 Kubernetes 的兩大『依賴』——容器技術(shù)和微服務(wù)架構(gòu)。
容器技術(shù)
Docker 已經(jīng)是容器技術(shù)的事實(shí)標(biāo)準(zhǔn)了,作者在前面的文章中 Docker 核心技術(shù)與實(shí)現(xiàn)原理 曾經(jīng)介紹過(guò) Docker 的實(shí)現(xiàn)主要依賴于 Linux 的 namespace、cgroups 和 UnionFS。
它讓開發(fā)者將自己的應(yīng)用以及依賴打包到一個(gè)可移植的容器中,讓應(yīng)用程序的運(yùn)行可以實(shí)現(xiàn)與環(huán)境無(wú)關(guān)。
我們能夠通過(guò) Docker 實(shí)現(xiàn)進(jìn)程、網(wǎng)絡(luò)以及掛載點(diǎn)和文件系統(tǒng)隔離的環(huán)境,并且能夠?qū)λ拗鳈C(jī)的資源進(jìn)行分配。
這能夠讓我們?cè)谕粋€(gè)機(jī)器上運(yùn)行多個(gè)不同的 Docker 容器,任意一個(gè) Docker 的進(jìn)程都不需要關(guān)心宿主機(jī)的依賴,都各自在鏡像構(gòu)建時(shí)完成依賴的安裝和編譯等工作。
這也是為什么 Docker 是 Kubernetes 項(xiàng)目的一個(gè)重要依賴。
微服務(wù)架構(gòu)
如果今天的軟件并不是特別復(fù)雜并且需要承載的峰值流量不是特別多,那么后端項(xiàng)目的部署其實(shí)也只需要在虛擬機(jī)上安裝一些簡(jiǎn)單的依賴,將需要部署的項(xiàng)目編譯后運(yùn)行就可以了。
但是隨著軟件變得越來(lái)越復(fù)雜,一個(gè)完整的后端服務(wù)不再是單體服務(wù),而是由多個(gè)職責(zé)和功能不同的服務(wù)組成。
服務(wù)之間復(fù)雜的拓?fù)潢P(guān)系以及單機(jī)已經(jīng)無(wú)法滿足性能需求使得軟件的部署和運(yùn)維工作變得非常復(fù)雜,這也就使得部署和運(yùn)維大型集群變成了非常迫切的需求。
小結(jié):Kubernetes 的出現(xiàn)不僅主宰了容器編排的市場(chǎng),更改變了過(guò)去的運(yùn)維方式,不僅將開發(fā)與運(yùn)維之間邊界變得更加模糊,而且讓 DevOps 這一角色變得更加清晰。
每一個(gè)軟件工程師都可以通過(guò) Kubernetes 來(lái)定義服務(wù)之間的拓?fù)潢P(guān)系、線上的節(jié)點(diǎn)個(gè)數(shù)、資源使用量并且能夠快速實(shí)現(xiàn)水平擴(kuò)容、藍(lán)綠部署等在過(guò)去復(fù)雜的運(yùn)維操作。
Kuberentes 設(shè)計(jì)理念及架構(gòu)
設(shè)計(jì)理念
我們先介紹 Kubernetes 的一些設(shè)計(jì)理念,這些關(guān)鍵字能夠幫助了解 Kubernetes 在設(shè)計(jì)時(shí)所做的一些選擇:
這里將按照順序分別介紹聲明式、顯式接口、無(wú)侵入性和可移植性這幾個(gè)設(shè)計(jì)的選擇能夠?yàn)槲覀儙?lái)什么。
聲明式
聲明式(Declarative)的編程方式一直都會(huì)被工程師們拿來(lái)與命令式(Imperative)進(jìn)行對(duì)比,這兩者是完全不同的編程方法。
我們最常接觸的其實(shí)是命令式編程,它要求我們描述為了達(dá)到某一個(gè)效果或者目標(biāo)所需要完成的指令,常見的編程語(yǔ)言 Go、Ruby、C++ 都為開發(fā)者提供了命令式的編程方法。
在 Kubernetes 中,我們可以直接使用 YAML 文件定義服務(wù)的拓?fù)浣Y(jié)構(gòu)和狀態(tài):
- apiVersion: v1
- kind: Pod
- metadata:
- name: rss-site
- labels:
- app: web
- spec:
- containers:
- - name: front-end
- image: nginx
- ports:
- - containerPort: 80
- - name: rss-reader
- image: nickchase/rss-php-nginx:v1
- ports:
- - containerPort: 88
這種聲明式的方式能夠大量地減少使用者的工作量,極大地增加開發(fā)的效率。
這是因?yàn)槁暶魇侥軌蚝?jiǎn)化需要的代碼,減少開發(fā)人員的工作,如果我們使用命令式的方式進(jìn)行開發(fā),雖然在配置上比較靈活,但是帶來(lái)了更多的工作。
- SELECT * FROM posts WHERE user_id = 1 AND title LIKE 'hello%';
SQL 其實(shí)就是一種常見的聲明式『編程語(yǔ)言』,它能夠讓開發(fā)者自己去指定想要的數(shù)據(jù)是什么。
Kubernetes 中的 YAML 文件也有著相同的原理,我們可以告訴 Kubernetes 想要的最終狀態(tài)是什么,而它會(huì)幫助我們從現(xiàn)有的狀態(tài)進(jìn)行遷移。
如果 Kubernetes 采用命令式編程的方式提供接口,那么工程師可能就需要通過(guò)代碼告訴 Kubernetes 要達(dá)到某個(gè)狀態(tài)需要通過(guò)哪些操作,相比于更關(guān)注狀態(tài)和結(jié)果聲明式的編程方式,命令式的編程方式更強(qiáng)調(diào)過(guò)程。
總而言之,Kubernetes 中聲明式的 API 其實(shí)指定的是集群期望的運(yùn)行狀態(tài)。
所以在出現(xiàn)任何不一致問(wèn)題時(shí),它本身都可以通過(guò)指定的 YAML 文件對(duì)線上集群進(jìn)行狀態(tài)的遷移。
就像一個(gè)水平觸發(fā)的系統(tǒng),哪怕系統(tǒng)錯(cuò)過(guò)了相應(yīng)的事件,最終也會(huì)根據(jù)當(dāng)前的狀態(tài)自動(dòng)做出合適的操作。
顯式接口
第二個(gè) Kubernetes 的設(shè)計(jì)規(guī)范其實(shí)就是:不存在內(nèi)部的私有接口,所有的接口都是顯示定義的,組件之間通信使用的接口對(duì)于使用者來(lái)說(shuō)都是顯式的,我們都可以直接調(diào)用。
當(dāng) Kubernetes 的接口不能滿足工程師的復(fù)雜需求時(shí),我們需要利用已有的接口實(shí)現(xiàn)更復(fù)雜的特性,在這時(shí) Kubernetes 的這一設(shè)計(jì)就不會(huì)成為自定義需求的障礙。
無(wú)侵入性
為了盡可能滿足用戶(工程師)的需求,減少工程師的工作量與任務(wù)并增強(qiáng)靈活性,Kubernetes 為工程師提供了無(wú)侵入式的接入方式。
每一個(gè)應(yīng)用或者服務(wù)一旦被打包成了鏡像就可以直接在 Kubernetes 中無(wú)縫使用,不需要修改應(yīng)用程序中的任何代碼。
Docker 和 Kubernetes 就像包裹在應(yīng)用程序上的兩層,它們兩個(gè)為應(yīng)用程序提供了容器化以及編排的能力。
在應(yīng)用程序內(nèi)部卻不需要任何的修改就能夠在 Docker 和 Kubernetes 集群中運(yùn)行,這是 Kubernetes 在設(shè)計(jì)時(shí)選擇無(wú)侵入帶來(lái)***的好處,同時(shí)無(wú)侵入的接入方式也是目前幾乎所有應(yīng)用程序或者服務(wù)都必須考慮的一點(diǎn)。
可移植性
在微服務(wù)架構(gòu)中,我們往往都會(huì)讓所有處理業(yè)務(wù)的服務(wù)變成無(wú)狀態(tài)的服務(wù)。
以前在內(nèi)存中存儲(chǔ)的數(shù)據(jù)、Session 等緩存,現(xiàn)在都會(huì)放到 Redis、ETCD 等數(shù)據(jù)庫(kù)中存儲(chǔ),微服務(wù)架構(gòu)要求我們對(duì)業(yè)務(wù)進(jìn)行拆分并劃清服務(wù)之間的邊界,所以有狀態(tài)的服務(wù)往往會(huì)對(duì)架構(gòu)的水平遷移帶來(lái)障礙。
然而有狀態(tài)的服務(wù)其實(shí)是無(wú)可避免的,我們將每一個(gè)基礎(chǔ)服務(wù)或者業(yè)務(wù)服務(wù)都變成了一個(gè)個(gè)只負(fù)責(zé)計(jì)算的進(jìn)程。
但是仍然需要有其他的進(jìn)程負(fù)責(zé)存儲(chǔ)易失的緩存和持久的數(shù)據(jù),Kubernetes 對(duì)這種有狀態(tài)的服務(wù)也提供了比較好的支持。
Kubernetes 引入了 Persistent Volume 和 Persistent Volume Claim 的概念用來(lái)屏蔽底層存儲(chǔ)的差異性,目前的 Kubernetes 支持下列類型的 Persistent Volume:
這些不同的 Persistent Volume 會(huì)被開發(fā)者聲明的 Persistent Volume Claim 分配到不同的服務(wù)中。
對(duì)于上層來(lái)講所有的服務(wù)都不需要接觸 Persistent Volume,只需要直接使用 Persistent Volume Claim 得到的卷就可以了。
架構(gòu)
Kubernetes 遵循非常傳統(tǒng)的客戶端服務(wù)端架構(gòu),客戶端通過(guò) RESTful 接口或者直接使用 kubectl 與 Kubernetes 集群進(jìn)行通信。
這兩者在實(shí)際上并沒有太多的區(qū)別,后者也只是對(duì) Kubernetes 提供的 RESTful API 進(jìn)行封裝并提供出來(lái)。
每一個(gè) Kubernetes 集群都由一組 Master 節(jié)點(diǎn)和一系列的 Worker 節(jié)點(diǎn)組成,其中 Master 節(jié)點(diǎn)主要負(fù)責(zé)存儲(chǔ)集群的狀態(tài)并為 Kubernetes 對(duì)象分配和調(diào)度資源。
Master
作為管理集群狀態(tài)的 Master 節(jié)點(diǎn),它主要負(fù)責(zé)接收客戶端的請(qǐng)求,安排容器的執(zhí)行并且運(yùn)行控制循環(huán),將集群的狀態(tài)向目標(biāo)狀態(tài)進(jìn)行遷移,Master 節(jié)點(diǎn)內(nèi)部由三個(gè)組件構(gòu)成:
其中 API Server 負(fù)責(zé)處理來(lái)自用戶的請(qǐng)求,其主要作用就是對(duì)外提供 RESTful 的接口,包括用于查看集群狀態(tài)的讀請(qǐng)求以及改變集群狀態(tài)的寫請(qǐng)求,也是唯一一個(gè)與 etcd 集群通信的組件。
而 Controller 管理器運(yùn)行了一系列的控制器進(jìn)程,這些進(jìn)程會(huì)按照用戶的期望狀態(tài)在后臺(tái)不斷地調(diào)節(jié)整個(gè)集群中的對(duì)象,當(dāng)服務(wù)的狀態(tài)發(fā)生了改變,控制器就會(huì)發(fā)現(xiàn)這個(gè)改變并且開始向目標(biāo)狀態(tài)遷移。
***的 Scheduler 調(diào)度器其實(shí)為 Kubernetes 中運(yùn)行的 Pod 選擇部署的 Worker 節(jié)點(diǎn),它會(huì)根據(jù)用戶的需要選擇最能滿足請(qǐng)求的節(jié)點(diǎn)來(lái)運(yùn)行 Pod,它會(huì)在每次需要調(diào)度 Pod 時(shí)執(zhí)行。
Worker
其他的 Worker 節(jié)點(diǎn)實(shí)現(xiàn)就相對(duì)比較簡(jiǎn)單了,它主要由 kubelet 和 kube-proxy 兩部分組成:
kubelet 是一個(gè)節(jié)點(diǎn)上的主要服務(wù),它周期性地從 API Server 接受新的或者修改的 Pod 規(guī)范并且保證節(jié)點(diǎn)上的 Pod 和其中容器的正常運(yùn)行,還會(huì)保證節(jié)點(diǎn)會(huì)向目標(biāo)狀態(tài)遷移,該節(jié)點(diǎn)仍然會(huì)向 Master 節(jié)點(diǎn)發(fā)送宿主機(jī)的健康狀況。
另一個(gè)運(yùn)行在各個(gè)節(jié)點(diǎn)上的代理服務(wù) kube-proxy 負(fù)責(zé)宿主機(jī)的子網(wǎng)管理,同時(shí)也能將服務(wù)暴露給外部,其原理就是在多個(gè)隔離的網(wǎng)絡(luò)中把請(qǐng)求轉(zhuǎn)發(fā)給正確的 Pod 或者容器。
Kubernetes 實(shí)現(xiàn)原理
到現(xiàn)在,我們已經(jīng)對(duì) Kubernetes 有了一些簡(jiǎn)單的認(rèn)識(shí)和了解,也大概清楚了 Kubernetes 的架構(gòu),下面我們將介紹 Kubernetes 中的一些重要概念和實(shí)現(xiàn)原理。
對(duì)象
Kubernetes 對(duì)象是系統(tǒng)中的持久實(shí)體,它使用這些對(duì)象來(lái)表示集群中的狀態(tài),這些對(duì)象能夠描述:
這些對(duì)象描述了哪些應(yīng)用應(yīng)該運(yùn)行在集群中,它們請(qǐng)求的資源下限和上限以及重啟、升級(jí)和容錯(cuò)的策略。
每一個(gè)創(chuàng)建的對(duì)象都是我們對(duì)集群狀態(tài)的改變,這些對(duì)象描述的其實(shí)就是集群的期望狀態(tài),Kubernetes 會(huì)根據(jù)我們指定的期望狀態(tài)不斷檢查對(duì)當(dāng)前的集群狀態(tài)進(jìn)行遷移。
- type Deployment struct {
- metav1.TypeMeta `json:",inline"`
- metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
- Spec DeploymentSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`
- Status DeploymentStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
- }
每一個(gè)對(duì)象都包含兩個(gè)嵌套對(duì)象來(lái)描述規(guī)格(Spec)和狀態(tài)(Status),對(duì)象的規(guī)格其實(shí)就是我們期望的目標(biāo)狀態(tài)。
而狀態(tài)描述了對(duì)象的當(dāng)前狀態(tài),這部分一般由 Kubernetes 系統(tǒng)本身提供和管理,是我們觀察集群本身的一個(gè)接口。
Pod
Pod 是 Kubernetes 中最基本的概念,它也是 Kubernetes 對(duì)象模型中我們可以創(chuàng)建或者部署的最小并且最簡(jiǎn)單的單元。
它將應(yīng)用的容器、存儲(chǔ)資源以及獨(dú)立的網(wǎng)絡(luò) IP 地址等資源打包到了一起,表示一個(gè)最小的部署單元。
但是每一個(gè) Pod 中的運(yùn)行的容器可能不止一個(gè),這是因?yàn)?Pod 最開始設(shè)計(jì)時(shí)就能夠在多個(gè)進(jìn)程之間進(jìn)行協(xié)調(diào),構(gòu)建一個(gè)高內(nèi)聚的服務(wù)單元,這些容器能夠共享存儲(chǔ)和網(wǎng)絡(luò),非常方便地進(jìn)行通信。
控制器
***要介紹的就是 Kubernetes 中的控制器,它們其實(shí)是用于創(chuàng)建和管理 Pod 的實(shí)例,能夠在集群的曾名提供復(fù)制、發(fā)布以及健康檢查的功能,這些控制器都運(yùn)行在 Kubernetes 集群的主節(jié)點(diǎn)上。
在 Kuberentes 的 kubernetes/pkg/controller/ 目錄中包含了官方提供的一些常見控制器,我們可以通過(guò)下面這個(gè)函數(shù)看到所有需要運(yùn)行的控制器:
- func NewControllerInitializers(loopMode ControllerLoopMode) map[string]InitFunc {
- controllers := map[string]InitFunc{}
- controllers["endpoint"] = startEndpointController
- controllers["replicationcontroller"] = startReplicationController
- controllers["podgc"] = startPodGCController
- controllers["resourcequota"] = startResourceQuotaController
- controllers["namespace"] = startNamespaceController
- controllers["serviceaccount"] = startServiceAccountController
- controllers["garbagecollector"] = startGarbageCollectorController
- controllers["daemonset"] = startDaemonSetController
- controllers["job"] = startJobController
- controllers["deployment"] = startDeploymentController
- controllers["replicaset"] = startReplicaSetController
- controllers["horizontalpodautoscaling"] = startHPAController
- controllers["disruption"] = startDisruptionController
- controllers["statefulset"] = startStatefulSetController
- controllers["cronjob"] = startCronJobController
- // ...
- return controllers
- }
這些控制器會(huì)隨著控制器管理器的啟動(dòng)而運(yùn)行,它們會(huì)監(jiān)聽集群狀態(tài)的變更來(lái)調(diào)整集群中的 Kuberentes 對(duì)象的狀態(tài),在后面的文章中我們會(huì)展開介紹一些常見控制器的實(shí)現(xiàn)原理。
總結(jié)
通過(guò)上文我們已經(jīng)了解了它出現(xiàn)的背景、依賴的關(guān)鍵技術(shù),同時(shí)我們也介紹了 Kubernetes 的架構(gòu)設(shè)計(jì),主節(jié)點(diǎn)負(fù)責(zé)處理客戶端的請(qǐng)求、節(jié)點(diǎn)的調(diào)度,***我們提到了幾個(gè) Kuberentes 中非常重要的概念:對(duì)象、Pod 和控制器。