今天我們來聊一聊關(guān)于K8s對象的那些事。
是的,K8s 也面向?qū)ο?/h1>
K8s 這個體系也是面向?qū)ο蟮?,聽起來有沒有那么一點點那么顫抖,天天上班、回家都得面向"對象”了,到頭來學(xué)學(xué)云原生基建,結(jié)果還得面向?qū)ο?。不過你反過來想一想,既然都有多年的面向?qū)ο蠼?jīng)驗了,學(xué)學(xué)面向?qū)ο蟮腒8s不還得手拿把攥啊。
在K8s中,對象是系統(tǒng)中持久化的實體,K8s 使用對象來表示集群中各種資源,以及跟蹤他們在集群中的狀態(tài)。
K8s 中有很多種對象,大到集群的節(jié)點(Node)、小到配置項 (ConfigMap) 都是對象,這些我們一股腦全都學(xué)會也不現(xiàn)實,也沒必要,只要我們掌握要領(lǐng)就什么對象都能面向了。
那么有哪些我們作為開發(fā)人員會接觸到的K8s對象呢,放一張圖:

K8s常用對象間的關(guān)系
上面這張圖就是K8s各個對象之間的關(guān)系,上一節(jié)我們也提到過,常用的K8s對象有這些:
- Pod
- Deployment
- StatuefulSet
- Service
- DaemonSet
- Ingress
你現(xiàn)在看這個圖可能有點懵,不過等看完下面的解釋和例子后,再回過來看,對這張圖里這些對象他們之間的關(guān)系就能有比較好的理解了。
K8s的對象這么多,如果你剛開始學(xué)上來就硬記,倒也不是不行哈,畢竟我們都是經(jīng)過九年義務(wù)教育和大學(xué)四年考試的臨時抱佛腳王者選手,硬記肯定沒問題。不過你要硬記就沒我什么事兒了,直接叉掉文章走吧哈哈,這里我總結(jié)了一個方法,讓大家秒懂K8s對象。咱們接著往下看
K8s對象的三要素
我們之前提過Pod是K8s 里的最小調(diào)度單元,控制器則是管理 Pod 用的,他們在 K8s 系統(tǒng)中都是通過相應(yīng)的對象來表示的。要創(chuàng)建 / 更改對象時, 我們只需要把他們的聲明文件(YAML) 提交給 K8s 的 API Server,集群就會自動幫我們創(chuàng)建、更新對象。
比如下面這個 Pod 的YAML定義文件:

Pod對象的定義
在這個文件中,我們最能直觀地看到的就是它是一試運行Nginx容器的Pod。提交給集群的聲明YAML 文件中,會寫清楚,對象的類型、元信息、以及預(yù)期在集群里預(yù)期的狀態(tài)。這就構(gòu)成了K8s 對象定義的三要素。
其實應(yīng)該算四要素,K8s對象的第四要素是 Status,不過這是給K8s集群用的,K8s通過Status來獲取資源對象的實時狀態(tài),我們在定義K8s對象時使用的是前三個要素,它們分別是:
- Kind : 對象種類
- metadata:對象的元信息, 名字、標(biāo)簽、uid、所在的命名空間等等
- spec:技術(shù)規(guī)范或者叫技術(shù)說明,描述對象的技術(shù)細(xì)節(jié)、各個資源要設(shè)置的細(xì)節(jié)不一樣,以及提交者者期望對象達(dá)到的理想狀態(tài)。PS:所有預(yù)期狀態(tài)的定義都是聲明式的(Declarative)的而不是命令式(Imperative),在分布式系統(tǒng)中的好處是穩(wěn)定,不怕丟操作或執(zhí)行多次。比如設(shè)定期望 3 試運行 Nginx 的Pod,執(zhí)行多次也還是一個結(jié)果,而給副本數(shù)加1的操作就不是聲明式的,執(zhí)行多次結(jié)果就錯了
這三個元素在上面 Pod 對象的定義里我們都能看到,這下對對象的理解是不是清晰了一點。
我們再來看一下控制器 Deployment 的定義,Deployment 是創(chuàng)建、滾動更新 Pod 的控制器,文章后面會細(xì)講,在它的對象定義里我們同樣能看到剛才提到的三要素。

Deployment對象的定義
下面我們來學(xué)習(xí)一下K8s中經(jīng)常會用到的控制器對象
常用的控制器對象
控制器對象的通用模式
K8s 中能經(jīng)常被我們用到的控制器對象有下面這些:
- Deployment
- StatuefulSet
- Service
- DaemonSet
- Ingress
K8s 本身提供的控制器遠(yuǎn)不止這些,他們都遵循 K8s 項目中的一個通用編排模式,即:控制循環(huán)(control loop),有的資料里解釋K8s是做容器編排的(當(dāng)然K8s遠(yuǎn)不止這個單一功能),這個編排就是由這些控制器來完成的。
比如,現(xiàn)在有一種待編排的對象 X,它有一個對應(yīng)的控制器。那么,可以用一段 Go 語言風(fēng)格的偽代碼,為你描述這個控制循環(huán):
// 這段內(nèi)容參考自極客時間《深入剖析Kubernetes》
for {
實際狀態(tài) := 獲取集群中對象X的實際狀態(tài)(Actual State)
期望狀態(tài) := 獲取集群中對象X的期望狀態(tài)(Desired State)
if 實際狀態(tài) == 期望狀態(tài){
什么都不做
} else {
執(zhí)行編排動作,將實際狀態(tài)調(diào)整為期望狀態(tài)
}
}
Deployment
Deployment 控制器用來管理無狀態(tài)應(yīng)用的,創(chuàng)建、水平擴容/縮容、滾動更新、健康檢查等。為啥叫無狀態(tài)應(yīng)用呢,就是它的創(chuàng)建和滾動更新是不保證順序的,這個特征就特別適合管控運行者 Web 服務(wù)的 Pod, 因為一個 Web 服務(wù)的重啟、更新并不依賴副本的順序。不像 MySQL 這樣的服務(wù),肯定是要先啟動主節(jié)點再啟動從節(jié)點才行。
那種情況我們應(yīng)該用 StatefulSet 控制器。
Deployment 是一個復(fù)合型的控制器,它包裝了一個叫做 ReplicaSet -- 副本集的控制器。ReplicaSet 管理正在運行的Pod數(shù)量,Deployment 在其之上實現(xiàn) Pod 滾動更新,對Pod的運行狀況進(jìn)行健康檢查以及回滾更新的能力。他們?nèi)咧g的關(guān)系可以用下面這張圖表示。

Deployment、ReplicaSet和Pod之間的關(guān)系
回滾更新是 Deployment 在內(nèi)部記錄了 ReplicaSet 每個版本的對象,要回滾就直接把生效的版本切回原來的ReplicaSet 對象
ReplicaSet 和 Pod 的定義其實是包含在 Deployment 對象的定義中的,我們再把上一個 Deployment 定義的例子拿過來看一下。

Deployment對象的定義
定義文件里的 replicas: 3 代表的就是我期望一個擁有三個副本 Pod 的副本集,而 template 這個 YAML 定義也叫做Pod模板,意思就是副本集的Pod,要按照這個樣板創(chuàng)建出來。
所以這里有一點需要注意,雖然我們知道了 Pod 是K8s 里最小的調(diào)度單元,ReplicaSet 控制器可以控制 Pod 的可用數(shù)量始終保持在想要數(shù)量。但是在 K8s 里我們卻不應(yīng)直接定義和操作他們倆。對這兩種對象的所有操作都應(yīng)該通過 Deployment 來執(zhí)行。這么做最主要的好處是能控制 Pod 的滾動更新。
Deployment 里邊有很多配置允許我們制定 Pod 的滾動更新,比如允許在更新期間多創(chuàng)建出來多少個副本,以及最多容忍多少個副本不可用等等。這個滾動更新我們就先不細(xì)究了,只要先記住一點,Deployment 對Pod 的滾動更新是先創(chuàng)建新Pod, 逐步的用新創(chuàng)建的 Pod 替換掉老版本的 Pod。
這里給大家來個動圖演示一下 Deployment 對 Pod 的滾動更新。






Pod的滾動更新過程
StatefulSet
StatefulSet,是在Deployment的基礎(chǔ)上擴展出來的控制器。使用Deployment時多數(shù)時候你不會在意Pod的調(diào)度方式。但當(dāng)你需要調(diào)度有拓?fù)錉顟B(tài)的應(yīng)用時,就需要關(guān)心Pod的部署順序、對應(yīng)的持久化存儲、 Pod 在集群內(nèi)擁有固定的網(wǎng)絡(luò)標(biāo)識(即使重啟或者重新調(diào)度后也不會變)這些文圖,這個時候,就需要 StatefulSet 控制器實現(xiàn)調(diào)度目標(biāo)。
Service
Service 是另一個我們必用到的控制器對象,因為在K8s 里 Pod 的 IP 是不固定的,所以 K8s 通過 Service 對象向應(yīng)用程序的客戶端提供一個靜態(tài)/穩(wěn)定的網(wǎng)絡(luò)地址,另外因為應(yīng)用程序往往是由多個Pod 副本構(gòu)成, Service還可以為它負(fù)責(zé)的 Pod 提供負(fù)載均衡的功能。
每個 Service 都具有一個ClusterIP和一個可以解析為該IP的DNS名,并且由這個 ClusterIP 向 Pod 提供負(fù)載均衡。
Service 控制器也是靠著 Pod 的標(biāo)簽在集群里篩選到自己要代理的 Pod,被選中的 Pod 叫做 Service 的端點(EndPoint)
看一下 Service 對象定義的模板:

Svc對象的定義模版
K8s 中提供了好幾種類似的Service,他們分別適用于不同場景,下面介紹兩個最常用的Service,更多類型的Service 和他們的實現(xiàn)原理可以參考我之前的文章:
- 學(xué)練結(jié)合,快速掌握Kubernetes Service
ClusterIP Service
這是默認(rèn)的Service類型,會將Service對象通過一個內(nèi)部IP暴露給集群內(nèi)部,這種類型的Service只能夠在集群內(nèi)部使用 <ClusterIP>:<port> 的形式訪問。

集群內(nèi)使用的ClusterIp Service
NodePort Service
會在每個 Node 的一個指定的固定端口上暴露Service,與此同時還會自動創(chuàng)建一個ClusterIP類型的Service,NodePort類型的Service會將集群外部的請求路由給ClusterIP類型的Service。
使用 <NodeIP>:<NodePort> 訪問NodePort類型的Service,NodePort的端口范圍為30000-32767。

向集群外暴露的NodePort類型的Service
NodePort 類型的Service 是向集群外暴露服務(wù)的最原始方式,也是最好讓人理解的。優(yōu)點是簡單,好理解,通過IP+端口的方式就能訪問,不過它的缺點也很明顯。
- 每向外暴露一個服務(wù)都要占用所有Node的一個端口,如果多了難以管理。
- NodePort的端口區(qū)間固定,只能使用30000–32767間的端口。
- 如果Node的IP發(fā)生改變,負(fù)載均衡代理需要跟著改后端端點IP才行。
正因為有這些缺點,K8s才提供了 Ingress,誒嘛好累,終于到本文要介紹的最后一個控制器對象啦。
Ingress
Ingress 在 K8s 集群里的角色是給 Service 充當(dāng)反向代理。它可以位于多個 Service 的前端,給這些 Service 充當(dāng)“智能路由”或者集群的入口點。

Ingress工作原理
使用 Ingress 對象前需要先安裝 Ingress-Controller, 像阿里云、亞馬遜 AWS 他們的 K8s 企業(yè)服務(wù)都會提供自己的Controller ,對于自己搭建的集群,通常使用nginx-ingress作為控制器,它使用 NGINX 服務(wù)器作為反向代理,訪問 Ingress 的流量按規(guī)則路由給集群內(nèi)部的Service。
下面看一下 Ingress 的配置

Ingress對象的定義
正常的生產(chǎn)環(huán)境,因為Ingress是公網(wǎng)的流量入口,所以壓力比較大肯定需要多機部署。一般會在集群里單獨出幾臺Node,只用來跑Ingress-Controller,可以使用deamonSet的讓節(jié)點創(chuàng)建時就安裝上Ingress-Controller,在這些Node上層再做一層負(fù)載均衡,把域名的DNS解析到負(fù)載均衡IP上。
DaemonSet
這個控制器不常用,主要保證每個 Node上 都有且只有一個 DaemonSet 指定的 Pod 在運行。當(dāng)然可以定義多個不同的 DaemonSet 來運行各種基礎(chǔ)軟件的 Pod。
比如新建節(jié)點的網(wǎng)絡(luò)配置、或者是每個節(jié)點上收集日志的組件往往是靠 DaemonSet 來保證的, 他會在集群創(chuàng)建時優(yōu)先于其他組件執(zhí)行, 為的是做好集群的基礎(chǔ)設(shè)施建設(shè)。
總結(jié)
這篇文章從K8s面向?qū)ο蟮乃悸烽_始,提供了我們掌握它的眾多對象的三要素,緊接著我們通過一個Nginx 服務(wù)的簡單例子把我們?nèi)粘D苡玫降腒8s對象捋了一遍。
不過文章里我盡量通過讓大家更容易理解的方式快速過了一下他們的概念,如果以后真上手開始構(gòu)建自己的服務(wù)了,需要把我在每部分里給出的參考文章好好看看,這樣才能構(gòu)建一個能在生產(chǎn)上穩(wěn)定運行的服務(wù)。
終于花了三篇文章,把K8s入門的理論知識給大家講清楚了,后面還有兩節(jié),咱們面向?qū)嵺`,把這里學(xué)到的理論知識再應(yīng)用一遍。我會分別寫一個Go服務(wù)和SpringBoot服務(wù),從零開始怎么從寫代碼到最后把他們部署到K8s上運行的詳細(xì)過程,后面多多期待吧。
本文的示例已經(jīng)上傳我自己維護(hù)的K8s學(xué)習(xí)倉庫,給公眾號「網(wǎng)管叨bi叨」發(fā)私信k8s,即可獲得地址,頁面上搜入門就能找到這個入門系列的所有示例。如果找不到的話也別著急,發(fā)信息給我,等摸魚的時候回你。