圖片來自 Pexels
每個微服務(wù)通過 Docker 進行發(fā)布,隨著業(yè)務(wù)的發(fā)展,系統(tǒng)中遍布著各種各樣的容器。于是,容器的資源調(diào)度,部署運行,擴容縮容就是我們要面臨的問題。
基于 Kubernetes 作為容器集群的管理平臺被廣泛應(yīng)用,今天我們一起來看看 Kubernetes 的架構(gòu)中有那些常用的組件以及運行原理。
Kubernetes 架構(gòu)概述
Kubernetes 是用來管理容器集群的平臺。既然是管理集群,那么就存在被管理節(jié)點,針對每個 Kubernetes 集群都由一個 Master 負責(zé)管理和控制集群節(jié)點。
我們通過 Master 對每個節(jié)點 Node 發(fā)送命令。簡單來說,Master 就是管理者,Node 就是被管理者。
Node 可以是一臺機器或者一臺虛擬機。在 Node 上面可以運行多個 Pod,Pod 是 Kubernetes 管理的最小單位,同時每個 Pod 可以包含多個容器(Docker)。
通過下面的 Kubernetes 架構(gòu)簡圖可以看到 Master 和 Node 之間的關(guān)系:
Kubernetes 架構(gòu)簡圖
通常我們都是通過 kubectl 對 Kubernetes 下命令的,它通過 APIServer 去調(diào)用各個進程來完成對 Node 的部署和控制。
APIServer 的核心功能是對核心對象(例如:Pod,Service,RC)的增刪改查操作,同時也是集群內(nèi)模塊之間數(shù)據(jù)交換的樞紐。
它包括了常用的 API,訪問(權(quán)限)控制,注冊,信息存儲(etcd)等功能。在它的下面我們可以看到 Scheduler,它將待調(diào)度的 Pod 綁定到 Node 上,并將綁定信息寫入 etcd 中。
etcd 包含在 APIServer 中,用來存儲資源信息。接下來就是 Controller Manager 了,如果說 Kubernetes 是一個自動化運行的系統(tǒng),那么就需要有一套管理規(guī)則來控制這套系統(tǒng)。
Controller Manager 就是這個管理者,或者說是控制者。它包括 8 個 Controller,分別對應(yīng)著副本,節(jié)點,資源,命名空間,服務(wù)等等。
緊接著,Scheduler 會把 Pod 調(diào)度到 Node 上,調(diào)度完以后就由 kubelet 來管理 Node 了。
kubelet 用于處理 Master 下發(fā)到 Node 的任務(wù)(即 Scheduler 的調(diào)度任務(wù)),同時管理 Pod 及 Pod 中的容器。
在完成資源調(diào)度以后,kubelet 進程也會在 APIServer 上注冊 Node 信息,定期向 Master 匯報 Node 信息,并通過 cAdvisor 監(jiān)控容器和節(jié)點資源。
由于,微服務(wù)的部署都是分布式的,所以對應(yīng)的 Pod 以及容器的部署也是。為了能夠方便地找到這些 Pod 或者容器,引入了 Service(kube-proxy)進程,它來負責(zé)反向代理和負載均衡的實施。
上面就是 Kubernetes 架構(gòu)的簡易說明,涉及到了一些核心概念以及簡單的信息流動。
將一些功能收錄到了 APIServer 中,這個簡圖比官網(wǎng)的圖顯得簡單一些,主要是方便大家記憶。
后面我們會用一個簡單的例子,帶大家把 Kubernetes 的概念的由來做深入的了解。
從一個例子開始
假設(shè)使用 Kubernetes 部署 Tomcat 和 MySQL 服務(wù)到兩個 Node 上面。其中 Tomcat 服務(wù)生成兩個實例也就是生成兩個 Pod,用來對其做水平擴展。
MySQL 只部署一個實例,也就是一個 Pod??梢酝ㄟ^外網(wǎng)訪問 Tomcat,而 Tomcat 可以在內(nèi)網(wǎng)訪問 MySQL。
例子示意圖
這里我們假設(shè) Kubernetes 和 Docker 的安裝都已經(jīng)完成,并且鏡像文件都已經(jīng)準(zhǔn)備好了。重點看 Kubernetes 如何部署和管理容器。
kubectl 和 APIServer
既然我們要完成上面的例子,接下來就要部署兩個應(yīng)用。
首先,根據(jù)要部署的應(yīng)用建立 Replication Controller(RC)。RC 是用來聲明應(yīng)用副本的個數(shù),也就是 Pod 的個數(shù)。
按照上面的例子,Tomcat 的 RC 就是 2,MySQL 的 RC 就是 1。
由于 kubectl 作為用戶接口向 Kubernetes 下發(fā)指令,那么指令是通過“.yaml”的配置文件編寫的。
定義 mysql-rc.yaml 的配置文件來描述 MySQL 的 RC:
apiVersion: V1
kind: ReplicationController
metadata:
name: mysql#RC的名稱,全局唯一
spec:
replicas:1 #Pod 副本的期待數(shù)量
selector :
app: mysql
template: #Pod模版,用這個模版來創(chuàng)建Pod
metadata:
labels:
app:mysql#Pod副本的標(biāo)簽
spec:
containers:#容器定義部分
-name:mysql
Image:mysql#容器對應(yīng)的DockerImage
Ports:
-containerPort:3306#容器應(yīng)用監(jiān)聽的端口號
Env:#注入容器的環(huán)境變量
-name:MYSQL_ROOT_PASSWORD
Value:”123456”
從上面的配置文件可以看出,需要對這個 RC 定義一個名字,以及期望的副本數(shù),以及容器中的鏡像文件。然后通過 kubectl 作為客戶端的 cli 工具,執(zhí)行這個配置文件。
通過 kubectl 執(zhí)行 RC 配置文件
執(zhí)行了上面的命令以后,Kubernetes 會幫助我們部署副本 MySQL 的 Pod 到 Node。
此時先不著急看結(jié)果,回到最開始的架構(gòu)圖,可以看到 kubectl 會向 Master 中的 APIServer 發(fā)起命令,看看 APIServer 的結(jié)構(gòu)和信息的傳遞吧。
Kubernetes API Server 通過一個名為 kube-apiserver 的進程提供服務(wù),該進程運行在 Master 上。
可以通過 Master 的 8080 端口訪問 kube-apiserver 進程,它提供 REST 服務(wù)。
因此可以通過命令行工具 kubectl 來與 Kubernetes APIServer 交互,它們之間的接口是 RESTful API。
APIServer 的架構(gòu)從上到下分為四層:
API 層:主要以 REST 方式提供各種 API 接口,針對 Kubernetes 資源對象的 CRUD 和 Watch 等主要 API,還有健康檢查、UI、日志、性能指標(biāo)等運維監(jiān)控相關(guān)的 API。
訪問控制層:負責(zé)身份鑒權(quán),核準(zhǔn)用戶對資源的訪問權(quán)限,設(shè)置訪問邏輯(Admission Control)。
注冊表層:選擇要訪問的資源對象。PS:Kubernetes 把所有資源對象都保存在注冊表(Registry)中,例如:Pod,Service,Deployment 等等。
etcd 數(shù)據(jù)庫:保存創(chuàng)建副本的信息。用來持久化 Kubernetes 資源對象的 Key-Value 數(shù)據(jù)庫。
APIServer 分層架構(gòu)圖
當(dāng) kubectl 用 Create 命令建立 Pod 時,是通過 APIServer 中的 API 層調(diào)用對應(yīng)的 RESTAPI 方法。
之后會進入權(quán)限控制層,通過 Authentication 獲取調(diào)用者身份,Authorization 獲取權(quán)限信息。
AdmissionControl 中可配置權(quán)限認證插件,通過插件來檢查請求約束。例如:啟動容器之前需要下載鏡像,或者檢查具備某命名空間的資源。
還記得 mysql-rc.yaml 中配置需要生成的 Pod 的個數(shù)為 1。到了 Registry 層會從 CoreRegistry 資源中取出 1 個 Pod 作為要創(chuàng)建的 Kubernetes 資源對象。
然后將 Node,Pod 和 Container 信息保存在 etcd 中去。這里的 etcd 可以是一個集群,由于里面保存集群中各個 Node/Pod/Container 的信息,所以必要時需要備份,或者保證其可靠性。
Controller Manager,Scheduler 和 kubelet
前面通過 kubectl 根據(jù)配置文件,向 APIServer 發(fā)送命令,在 Node 上面建立 Pod 和 Container。
在 APIServer,經(jīng)過 API 調(diào)用,權(quán)限控制,調(diào)用資源和存儲資源的過程。實際上還沒有真正開始部署應(yīng)用。
這里需要 Controller Manager,Scheduler 和 kubelet 的協(xié)助才能完成整個部署過程。
在介紹他們協(xié)同工作之前,要介紹一下在 Kubernetes 中的監(jiān)聽接口。從上面的操作知道,所有部署的信息都會寫到 etcd 中保存。
實際上 etcd 在存儲部署信息的時候,會發(fā)送 Create 事件給 APIServer,而 APIServer 會通過監(jiān)聽(Watch)etcd 發(fā)過來的事件。其他組件也會監(jiān)聽(Watch)APIServer 發(fā)出來的事件。
Kubernetes 就是用這種 List-Watch 的機制保持數(shù)據(jù)同步的,如上圖:
這里有三個 List-Watch,分別是 kube-controller-manager(運行在Master),kube-scheduler(運行在 Master),kublete(運行在 Node)。他們在進程已啟動就會監(jiān)聽(Watch)APIServer 發(fā)出來的事件。
kubectl 通過命令行,在 APIServer 上建立一個 Pod 副本。
這個部署請求被記錄到 etcd 中,存儲起來。
當(dāng) etcd 接受創(chuàng)建 Pod 信息以后,會發(fā)送一個 Create 事件給 APIServer。
由于 Kubecontrollermanager 一直在監(jiān)聽 APIServer 中的事件。此時 APIServer 接受到了 Create 事件,又會發(fā)送給 Kubecontrollermanager。
Kubecontrollermanager 在接到 Create 事件以后,調(diào)用其中的 Replication Controller 來保證 Node 上面需要創(chuàng)建的副本數(shù)量。
上面的例子 MySQL 應(yīng)用是 1 個副本,Tomcat 應(yīng)用是兩個副本。一旦副本數(shù)量少于 RC 中定義的數(shù)量,Replication Controller 會自動創(chuàng)建副本。總之它是保證副本數(shù)量的 Controller。PS:擴容縮容的擔(dān)當(dāng)。
在 Controller Manager 創(chuàng)建 Pod 副本以后,APIServer 會在 etcd 中記錄這個 Pod 的詳細信息。例如在 Pod 的副本數(shù),Container 的內(nèi)容是什么。
同樣的 etcd 會將創(chuàng)建 Pod 的信息通過事件發(fā)送給 APIServer。
由于 Scheduler 在監(jiān)聽(Watch)APIServer,并且它在系統(tǒng)中起到了“承上啟下”的作用,“承上”是指它負責(zé)接收創(chuàng)建的 Pod 事件,為其安排 Node;“啟下”是指安置工作完成后,Node 上的 kubelet 服務(wù)進程接管后繼工作,負責(zé) Pod 生命周期中的“下半生”。
換句話說,Scheduler 的作用是將待調(diào)度的 Pod 按照調(diào)度算法和策略綁定到集群中 Node 上,并將綁定信息寫入 etcd 中。
Scheduler 調(diào)度完畢以后會更新 Pod 的信息,此時的信息更加豐富了。除了知道 Pod 的副本數(shù)量,副本內(nèi)容。還知道部署到哪個 Node 上面了。
同樣,將上面的 Pod 信息更新到 etcd 中,保存起來。
etcd 將更新成功的事件發(fā)送給 APIServer。
注意這里的 kubelet 是在 Node 上面運行的進程,它也通過 List-Watch 的方式監(jiān)聽(Watch)APIServer 發(fā)送的 Pod 更新的事件。實際上,在第 9 步的時候創(chuàng)建 Pod 的工作就已經(jīng)完成了。
為什么 kubelete 還要一直監(jiān)聽呢?原因很簡單,假設(shè)這個時候 kubectl 發(fā)命令,需要把原來的 MySQL 的 1 個 RC 副本擴充成 2 個。那么這個流程又會觸發(fā)一遍。
作為 Node 的管理者 kubelet 也會根據(jù)最新的 Pod 的部署情況調(diào)整 Node 端的資源。
又或者 MySQL 應(yīng)用的 RC 個數(shù)沒有發(fā)生變化,但是其中的鏡像文件升級了,kubelet 也會自動獲取最新的鏡像文件并且加載。
通過上面 List-Watch 的介紹大家發(fā)現(xiàn)了,除了之前引入的 kubectl 和 APIServer 以外又引入了 Controller Manager,Scheduler 和 kubelet。
這里給大家介紹一下他們的作用和原理:
聚焦 Scheduler,Controller Manager,kubelet
Controller Manager
Kubernetes 需要管理集群中的不同資源,所以針對不同的資源要建立不同的 Controller。
每個 Controller 通過監(jiān)聽機制獲取 APIServer 中的事件(消息),它們通過 API Server 提供的(List-Watch)接口監(jiān)控集群中的資源,并且調(diào)整資源的狀態(tài)。
可以把它想象成一個盡職的管理者,隨時管理和調(diào)整資源。比如 MySQL 所在的 Node 意外宕機了,Controller Manager 中的 Node Controller 會及時發(fā)現(xiàn)故障,并執(zhí)行修復(fù)流程。
在部署了成百上千微服務(wù)的系統(tǒng)中,這個功能極大地協(xié)助了運維人員。從此可以看出,Controller Manager 是 Kubernetes 資源的管理者,是運維自動化的核心。
它分為 8 個 Controller,上面我們介紹了 Replication Controller,這里我們把其他幾個都列出來,就不展開描述了。
Controller Manager 中不同的 Controller 負責(zé)對不同資源的監(jiān)控和管理
Scheduler 與 kubelet
Scheduler 的作用是,將待調(diào)度的 Pod 按照算法和策略綁定到 Node 上,同時將信息保存在 etcd 中。
如果把 Scheduler 比作調(diào)度室,那么這三件事就是它需要關(guān)注的,待調(diào)度的 Pod、可用的 Node,調(diào)度算法和策略。
簡單地說,就是通過調(diào)度算法/策略把 Pod 放到合適的 Node 中去。此時 Node 上的 kubelet 通過 APIServer 監(jiān)聽到 Scheduler 產(chǎn)生的 Pod 綁定事件,然后通過 Pod 的描述裝載鏡像文件,并且啟動容器。
也就是說 Scheduler 負責(zé)思考,Pod 放在哪個 Node,然后將決策告訴 kubelet,kubelet 完成 Pod 在 Node 的加載工作。
說白了,Scheduler 是 boss,kubelet 是干活的工人,他們都通過 APIServer 進行信息交換。
Scheduler 與 kubelet 協(xié)同工作圖
Service 和 kubelet
經(jīng)歷上面一系列的過程,終于將 Pod 和容器部署到 Node 上了。
MySQL 部署成功
作為部署在 Kubernetes 中,Pod 如何訪問其他的 Pod 呢?答案是通過 Kubernetes 的 Service 機制。
在 Kubernetes 中的 Service 定義了一個服務(wù)的訪問入口地址(IP+Port)。Pod 中的應(yīng)用通過這個地址訪問一個或者一組 Pod 副本。
Service 與后端 Pod 副本集群之間是通過 Label Selector 來實現(xiàn)連接的。Service 所訪問的這一組 Pod 都會有同樣的 Label,通過這樣的方法知道這些 Pod 屬于同一個組。
Pod 通過 Service 訪問其他 Pod
寫 MySQL 服務(wù)的配置文件(mysql-svc.yaml)如下:
apiVersion : v1
kind: Service #說明創(chuàng)建資源對象的類型是Service
metadata:
name: mysql#Service全局唯一名稱
spec:
prots:
-port: 3306#Service的服務(wù)端口號
selector:#Service對應(yīng)的Pod標(biāo)簽,用來給Pod分類
app: mysql
按照慣例運行 kubectl,創(chuàng)建 Service:
再用 getsvc 命令檢查 Service 信息:
這里的 Cluster-IP 169.169.253.143 是由 Kubernetes 自動分配的。當(dāng)一個 Pod 需要訪問其他的 Pod 的時候就需要通過 Service 的 Cluster-IP 和 Port。
也就是說 Cluster-IP 和 Port 是 Kubernetes 集群的內(nèi)部地址,是提供給集群內(nèi)的 Pod 之間訪問使用的,外部系統(tǒng)是無法通過這個 Cluster-IP 來訪問 Kubernetes 中的應(yīng)用的。
上面提到的 Service 只是一個概念,而真正將 Service 落實的是 kube-proxy。
只有理解了 kube-proxy 的原理和機制,我們才能真正理解 Service 背后的實現(xiàn)邏輯。
在 Kubernetes 集群的每個 Node 上都會運行一個 kube-proxy 服務(wù)進程,我們可以把這個進程看作 Service 的負載均衡器,其核心功能是將到 Service 的請求轉(zhuǎn)發(fā)到后端的多個 Pod 上。
此外,Service 的 Cluster-IP 與 NodePort 是 kube-proxy 服務(wù)通過 iptables 的 NAT 轉(zhuǎn)換實現(xiàn)的。kube-proxy 在運行過程中動態(tài)創(chuàng)建與 Service 相關(guān)的 iptables 規(guī)則。
由于 iptables 機制針對的是本地的 kube-proxy 端口,所以在每個 Node 上都要運行 kube-proxy 組件。
因此在 Kubernetes 集群內(nèi)部,可以在任意 Node 上發(fā)起對 Service 的訪問請求。
集群內(nèi)部通過 kube-proxy(Service)訪問其他 Pod
正如 MySQL 服務(wù),可以被 Kubernetes 內(nèi)部的 Tomcat 調(diào)用,那么 Tomcat 如何被 Kubernetes 外部調(diào)用?
先生成配置文件,myweb-rc.yaml 看看:
apiVersion: V1
kind: ReplicationController
metadata:
name: myweb#RC的名稱,全局唯一
spec:
replicas:2#Pod 副本的期待數(shù)量,這里的數(shù)量是2,需要建立兩個Tomcat的副本
selector :
app: myweb
template: #Pod模版,用這個模版來創(chuàng)建Pod
metadata:
labels:
app:myweb#Pod副本的標(biāo)簽
spec:
containers: #容器定義部分
-name:mysql
Image:kubeguide/tomcat-app:v1#容器對應(yīng)的DockerImage
Ports:
-containerPort:8080#容器應(yīng)用監(jiān)聽的端口號
在 kubectl 中使用 Create 建立 myweb 副本。
副本創(chuàng)建完畢以后,創(chuàng)建對應(yīng)的服務(wù)配置文件 myweb-svc.yaml。
apiVersion : v1
kind: Service #說明創(chuàng)建資源對象的類型是Service
metadata:
name: myweb#Service全局唯一名稱
spec:
prots:
-port: 8080#Service的服務(wù)端口號
nodePort: 30001#這個就是外網(wǎng)訪問Kubernetes內(nèi)部應(yīng)用的端口。
selector: #Service對應(yīng)的Pod標(biāo)簽,用來給Pod分類
app: myweb
同樣在 kubectl 中運行 Create 命令,建立 Service 資源。
從上面的配置文件可以看出,Tomcat 的 Service 中多了一個 nodePort 的配置,值為 30001。
也就是說外網(wǎng)通過 30001 這個端口加上 NodeIP 就可以訪問 Tomcat 了。
運行命令之后,得到一個提示,大致意思是“如果你要將服務(wù)暴露給外網(wǎng)使用,你需要設(shè)置防火墻規(guī)則讓 30001 端口能夠通行?!?/p>
由于 Cluster-IP 是一個虛擬的 IP,僅供 Kubernetes 內(nèi)部的 Pod 之間的通信。
Node 作為一個物理節(jié)點,因此需要使用 Node-IP 和 nodePort 的組合來從 Kubernetes 外面訪問內(nèi)部的應(yīng)用。
如果按照上面的配置,部署了兩個 Tomcat 應(yīng)用,當(dāng)外網(wǎng)訪問時選擇那個 Pod 呢?這里需要通過 Kubernetes 之外的負載均衡器來實現(xiàn)的。
Kubernetes 之外的負載均衡器
可以通過 Kubernetes 的 LoadBlancerService 組件來協(xié)助實現(xiàn)。通過云平臺申請創(chuàng)建負載均衡器,向外暴露服務(wù)。
目前 LoadBlancerService 組件支持的云平臺比較完善,比如國外的 GCE、DigitalOcean,國內(nèi)的阿里云,私有云 OpenStack 等等。
從用法上只要把 Service 的 type=NodePort 改為 type=LoadBalancer,Kubernetes 就會自動創(chuàng)建一個對應(yīng)的 Load Balancer 實例并返回它的 IP 地址供外部客戶端使用。
至此,MySQL(RC 1)和 Tomcat(RC 2)已經(jīng)在 Kubernetes 部署了。并在 Kubernetes 內(nèi)部 Pod 之間是可以互相訪問的,在外網(wǎng)也可以訪問到 Kubernetes 內(nèi)部的 Pod。
Pod 在 Kubernetes 內(nèi)互相訪問,外網(wǎng)訪問 Pod
另外,作為資源監(jiān)控 Kubernetes 在每個 Node 和容器上都運行了 cAdvisor。它是用來分析資源使用率和性能的工具,支持 Docker 容器。
kubelet 通過 cAdvisor 獲取其所在 Node 及容器(Docker)的數(shù)據(jù)。cAdvisor 自動采集 CPU、內(nèi)存、文件系統(tǒng)和網(wǎng)絡(luò)使用的統(tǒng)計信息。
kubelet 作為 Node 的管理者,把 cAdvisor 采集上來的數(shù)據(jù)通過 RESTAPI 的形式暴露給 Kubernetes 的其他資源,讓他們知道 Node/Pod 中的資源使用情況。
總結(jié)
由于微服務(wù)的迅猛發(fā)展,Kubernetes 作為微服務(wù)治理平臺被廣泛應(yīng)用。由于其發(fā)展時間長,包含服務(wù)功能多我們無法一一列出。
因此,從一個簡單的創(chuàng)建應(yīng)用副本的例子入手,介紹了各個重要組件的概念和基本原理。
Kubernetes 是用來管理容器集群的,Master 作為管理者,包括 APIServer,Scheduler,Controller Manager。
Node作為副本部署的載體,包含多個 Pod,每個 Pod 又包含多個容器(container)。用戶通過 kubectl 給 Master 中的 APIServer 下部署命令。
命令主體是以“.yaml”結(jié)尾的配置文件,包含副本的類型,副本個數(shù),名稱,端口,模版等信息。
APIServer 接受到請求以后,會分別進行以下操作:權(quán)限驗證(包括特殊控制),取出需要創(chuàng)建的資源,保存副本信息到etcd。
APIServer 和 Controller Manager,Scheduler 以及 kubelete 之間通過 List-Watch 方式通信(事件發(fā)送與監(jiān)聽)。
Controller Manager 通過 etcd 獲取需要創(chuàng)建資源的副本數(shù),交由 Scheduler 進行策略分析。
最后 kubelet 負責(zé)最終的 Pod 創(chuàng)建和容器加載。部署好容器以后,通過 Service 進行訪問,通過 cAdvisor 監(jiān)控資源。
作者:崔皓
簡介:十六年開發(fā)和架構(gòu)經(jīng)驗,曾擔(dān)任過惠普武漢交付中心技術(shù)專家,需求分析師,項目經(jīng)理,后在創(chuàng)業(yè)公司擔(dān)任技術(shù)/產(chǎn)品經(jīng)理。善于學(xué)習(xí),樂于分享。目前專注于技術(shù)架構(gòu)與研發(fā)管理。