三、應用編排與管理:核心原理
1、資源元資訊

Kubernetes的資源物件組成:主要包括了Spec、Status兩部分,其中Spec部分用來描述期望的狀態,Status部分用來描述觀測到的狀態
Kubernetes的元資料部分,該部分主要包括了用來識別資源的標簽:Labels;用來描述資源的注解:Annotations;用來描述多個資源之間相互關系的OwnerReference
1)、labels

Labels是一種具有標識型的Key:Value元資料,標簽主要用來篩選資源和組合資源,可以使用類似于SQL查詢select,來根據Label查詢相關的資源
2)、Selector
最常見的Selector就是相等型Selector

假設系統中有四個Pod,每個Pod都有標識系統層級和環境的標簽,通過Tie:front這個標簽,可以匹配左邊欄的Pod,相等型Selector還可以包括多個相等條件,多個相等條件之間是邏輯與的關系

通過Tie=front,Env=dev的Selector,可以篩選出所有Tie=front,而且Env=dev的Pod,也就是上圖中左上角的Pod,另外一種Selector是集合型Selector,在例子中,Selector篩選所有環境是test或者gray的Pod

除了in的集合操作外,還有notin集合操作,比如tie notin(front,back),將會篩選所有tie不是front且不是back的Pod,另外,也可以根據是否存在某lable的篩選,如:Selector release,篩選所有帶release標簽的Pod,集合型和相等型的Selector,也可以用,來連接,同樣的標識邏輯與的關系
3)、Annotations
Annotations一般是系統或者工具用來存盤資源的非標示性資訊,可以用來擴展資源的spec/status的描述

4)、Ownereference

Ownereference一般就是指集合類的資源,比如說Pod集合,就有replicaset、statefulset
集合類資源的控制器會創建對應的歸屬資源,比如:replicaset控制器在操作中會創建Pod,被創建Pod的Ownereference就指向了創建Pod的replicaset,Ownereference使得用戶可以方便地查找一個創建資源的物件,另外,還可以用來實作級聯洗掉的效果
2、kubectl查看和修改Kubernetes元資料
pod1.yaml:
apiVersion: v1
kind: Pod
metadata:
name: nginx1
namespace: default
labels:
env: dev
tie: front
spec:
containers:
- name : nginx
image: nginx:1.8
ports:
- containerPort: 80
pod2.yaml:
apiVersion: v1
kind: Pod
metadata:
name: nginx2
namespace: default
labels:
env: dev
tie: front
spec:
containers:
- name : nginx
image: nginx:1.8
ports:
- containerPort: 80
創建兩個pod
hanxiantaodeMBP:yamls hanxiantao$ kubectl get pods
No resources found in default namespace.
hanxiantaodeMBP:yamls hanxiantao$ kubectl apply -f pod1.yaml
pod/nginx1 created
hanxiantaodeMBP:yamls hanxiantao$ kubectl apply -f pod2.yaml
pod/nginx2 created
查看所有pod的標簽
hanxiantaodeMBP:yamls hanxiantao$ kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
nginx1 1/1 Running 0 62s env=dev,tie=front
nginx2 1/1 Running 0 58s env=dev,tie=front
查看nginx1這個pod的詳細資訊
hanxiantaodeMBP:yamls hanxiantao$ kubectl get pods nginx1 -o yaml | less
修改pod的label,改為env=test
hanxiantaodeMBP:yamls hanxiantao$ kubectl label pods nginx1 env=test
error: 'env' already has a value (dev), and --overwrite is false
hanxiantaodeMBP:yamls hanxiantao$ kubectl label pods nginx1 env=test --overwrite
pod/nginx1 labeled
hanxiantaodeMBP:yamls hanxiantao$ kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
nginx1 1/1 Running 0 5m39s env=test,tie=front
nginx2 1/1 Running 0 5m35s env=dev,tie=front
去掉pod的tie這個標簽
hanxiantaodeMBP:yamls hanxiantao$ kubectl label pods nginx1 tie-
pod/nginx1 labeled
hanxiantaodeMBP:yamls hanxiantao$ kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
nginx1 1/1 Running 0 6m58s env=test
nginx2 1/1 Running 0 6m54s env=dev,tie=front
通過label來篩選pod
hanxiantaodeMBP:yamls hanxiantao$ kubectl get pods --show-labels -l env=test
NAME READY STATUS RESTARTS AGE LABELS
nginx1 1/1 Running 0 7m33s env=test
hanxiantaodeMBP:yamls hanxiantao$ kubectl get pods --show-labels -l env=test,env=dev
No resources found in default namespace.
hanxiantaodeMBP:yamls hanxiantao$ kubectl get pods --show-labels -l env=dev,tie=front
NAME READY STATUS RESTARTS AGE LABELS
nginx2 1/1 Running 0 8m4s env=dev,tie=front
hanxiantaodeMBP:yamls hanxiantao$ kubectl get pods --show-labels -l 'env in (test,dev)'
NAME READY STATUS RESTARTS AGE LABELS
nginx1 1/1 Running 0 8m36s env=test
nginx2 1/1 Running 0 8m32s env=dev,tie=front
添加annotate
hanxiantaodeMBP:yamls hanxiantao$ kubectl annotate pods nginx1 my-annotate='my comment, ok'
pod/nginx1 annotated
hanxiantaodeMBP:yamls hanxiantao$ kubectl get pods -o yaml | less
apiVersion: v1
kind: Pod
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{},"labels":{"env":"dev","tie":"front"},"name":"nginx1","namespace":"default"},"spec":{"containers":[{"image":"nginx:1.8","name":"nginx","ports":[{"containerPort":80}]}]}}
my-annotate: my comment, ok
creationTimestamp: "2020-12-24T00:18:18Z"
labels:
env: test
rs.yaml:
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: nginx-replicasets
namespace: default
labels:
env: prod
spec:
replicas: 2
selector:
matchLabels:
env: prod
template:
metadata:
labels:
env: prod
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
通過創建replicaset物件來創建pod,pod中會包含ownerReference資訊
hanxiantaodeMBP:yamls hanxiantao$ kubectl apply -f rs.yaml
replicaset.apps/nginx-replicasets created
hanxiantaodeMBP:yamls hanxiantao$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-replicasets-ld4n6 1/1 Running 0 2m3s
nginx-replicasets-xvr6k 1/1 Running 0 2m3s
nginx1 1/1 Running 0 27m
nginx2 1/1 Running 0 27m
hanxiantaodeMBP:yamls hanxiantao$ kubectl get pods nginx-replicasets-ld4n6 -o yaml | less
name: nginx-replicasets-ld4n6
namespace: default
ownerReferences:
- apiVersion: apps/v1
blockOwnerDeletion: true
controller: true
kind: ReplicaSet
name: nginx-replicasets
uid: 5beab4c4-3aae-4c6c-a3f2-3822a2e3ba3a
resourceVersion: "567337"
selfLink: /api/v1/namespaces/default/pods/nginx-replicasets-ld4n6
uid: 9dbcafce-72ec-4088-a0ab-3eccaba95c2d
spec:
containers:
- image: nginx:1.7.9
imagePullPolicy: IfNotPresent
name: nginx
ports:
- containerPort: 80
protocol: TCP
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: default-token-6c86p
readOnly: true
3、控制器模式
1)、控制回圈

控制型模式最核心的就是控制回圈的概念,在控制回圈中包括了控制器,被控制的系統,以及能夠觀測系統的傳感器,三個邏輯組件
外界通過修改資源spec來控制資源,控制器比較資源spec和status,從而計算一個diff,diff最后會用來決定執行對系統進行什么樣的控制操作,控制操作會使得系統產生新的輸出,并被傳感器以資源status形式上報,控制器的各個組件將都會是獨立自主地運行,不斷使系統向spec表示終態趨近
2)、Sensor

控制回圈中邏輯的傳感器主要由Reflector、Informer、Indexer三個組件構成
Reflector通過List和Watch K8s server來獲取資源的資料,List用來在Controller重啟以及Watch中斷的情況下,進行系統資源的全量更新;而Watch則在多次List之間進行增量的資源更新;Reflector在獲取新的資源資料后,會在Delta佇列中塞入一個包括資源物件資訊本身以及資源物件事件型別的Delta記錄,Delta佇列中可以保證同一個物件在佇列中僅有一條記錄,從而避免Reflector重新List和Watch的時候產生重復的記錄
Informer組件不斷地從Delta佇列中彈出delta記錄,然后把資源物件交給indexer,讓indexer把資源記錄在一個快取中,快取在默認設定下是用資源的命名空間來做索引的,并且可以被Controller Manager或多個Controller所共享,之后,再把這個事件交給事件的回呼函式
控制回圈中的控制器組件主要由事件處理函式以及worker組成,事件處理函式之間會相互關注資源的新增、更新、洗掉的事件,并根據控制器的邏輯去決定是否需要處理,對需要處理的事件,會把事件關聯資源的命名空間以及名字塞入一個作業佇列中,并且由后續的worker池中的一個Worker來處理,作業佇列會對存盤的物件進行去重,從而避免多個Woker處理同一個資源的情況
Worker在處理資源物件時,一般需要用資源的名字來重新獲得最新的資源資料,用來創建或者更新資源物件,或者呼叫其他的外部服務,Worker如果處理失敗的時候,一般情況下會把資源的名字重新加入到作業佇列中,從而方便之后進行重試
3)、控制回圈例子——擴容

ReplicaSet是一個用來描述無狀態應用的擴縮容行為的資源,ReplicaSet controler通過監聽ReplicaSet資源來維持應用希望的狀態數量,ReplicaSet中通過selector來匹配所關聯的Pod,在這里考慮ReplicaSet rsA的,replicas從2被改到3的場景

首先,Reflector會watch到ReplicaSet和Pod兩種資源的變化,發現ReplicaSet發生變化后,在delta佇列中塞入了物件是rsA,而且型別是更新的記錄
Informer一方面把新的ReplicaSet更新到快取中,并與Namespace nsA作為索引,另外一方面,呼叫Update的回呼函式,ReplicaSet控制器發現ReplicaSet發生變化后會把字串的nsA/rsA字串塞入到作業佇列中,作業佇列后的一個Worker從作業佇列中取到了nsA/rsA這個字串的key,并且從快取中取到了最新的ReplicaSet資料
Worker通過比較ReplicaSet中spec和status里的數值,發現需要對這個ReplicaSet進行擴容,因此ReplicaSet的Worker創建了一個Pod,這個pod中的Ownereference取向了ReplicaSet rsA

然后Reflector Watch到的Pod新增事件,在delta佇列中額外加入了Add型別的deta記錄,一方面把新的Pod記錄通過Indexer存盤到了快取中,另一方面呼叫了ReplicaSet控制器的Add回呼函式,Add回呼函式通過檢查pod ownerReferences找到了對應的ReplicaSet,并把包括ReplicaSet命名空間和字串塞入到了作業佇列中
ReplicaSet的Woker在得到新的作業項之后,從快取中取到了新的ReplicaSet記錄,并得到了其所有創建的Pod,因為ReplicaSet的狀態不是最新的,也就是所有創建Pod的數量不是最新的,因此在此時ReplicaSet更新status使得spec和status達成一致
4、控制器模式總結

Kubernetes所采用的控制器模式,是由宣告式API驅動的,確切來說,是基于對Kubernetes資源物件的修改來驅動的
Kubernetes資源之后,是關注該資源的控制器,這些控制器將異步的控制系統向設定的終態驅近
這些控制器是自主運行的,使得系統的自動化和無人值守成為可能
因為Kubernete的控制器和資源都是可以自定義的,因此可以方便的擴展控制器模式,特別是對于有狀態應用,我們往往通過自定義資源和控制器的方式,來自動化運維操作,這個也就是operator的場景
四、應用編排與管理:Deployment
1、需求來源

如果直接管理集群中所有的Pod,應用A、B、C的Pod,其實是散亂地分布在集群中
現在有以下的問題:
- 首先,如何保證集群內可用Pod的數量?也就是說我們應用A四個Pod如果出現了一些宿主機故障,或者一些網路問題,如何能保證它可用的數量?
- 如何為所有Pod更新鏡像版本?我們是否要某一個Pod去重建新版本的Pod?
- 然后在更新程序中,如何保證服務的可用性?
- 以及更新程序中,如果發現了問題,如何快速回滾到上一個版本?

通過Deployment將應用A、B、C分別規劃到不同的Deployment中,每個Deployment其實是管理的一組相同的應用Pod,這組Pod我們認為它是相同的一個副本,那么Deployment能幫我們做什么事情呢?
1)首先,Deployment定義了一種Pod期望數量,比如說應用A,我們期望Pod數量是四個,那么這樣的話,controller就會持續維持Pod數量為期望的數量,當我們與Pod出現了網路問題或者宿主機問題的話,controller能幫我們恢復,也就是新擴出來對應的Pod,來保證可用的Pod數量與期望數量一致
2)配置Pod發布方式,也就是說controller會按照用戶給定的策略來更新Pod,而且更新程序中,也可以設定不可用Pod數量在多少范圍內
3)如果更新程序中發生問題的話,即所謂一鍵回滾,也就是說你通過一條命令或者一行修改能夠將Deployment下面所有Pod更新為某一個舊版本
2、用例解讀
1)、Deployment語法

apiVersion:apps/v1,也就是說Deployment當前所屬的組是apps,版本是v1
Deployment作為一個K8s資源,它有自己的metadata元資訊,這里定義的Deployment.name是nginx-deployment
Deployment.spec中首先要有一個核心的欄位,即replicas,這里定義期望的Pod數量為三個;selector其實是Pod選擇器,那么所有擴容出來的Pod,它的Labels必須匹配selector層上的image.labels,也就是app:nginx
2)、查看Deployment狀態

可以通過kubectl get deployment,看到Deployment總體的一個狀態
- DESIRED:期望的Pod數量是3個
- CURRENT:當前實際Pod數量是3個
- UP-TO-DATE:其實是到達最新的期望版本的Pod數量
- AVAILABLE:這個其實是運行程序中可用的Pod數量,這里AVAILABLE并不簡單是可用的,也就是Ready狀態的,它其實包含了一些可用超過一定時間長度的Pod
- AGE:deployment創建的時長,如上圖Deployment就是已經創建了80分鐘
3)、查看Pod

Pod名字格式最前面一段:nginx-deployment,其實是Pod所屬 Deployment.name;中間一段:template-hash,這里三個Pod是一樣的,因為這三個Pod其實都是同一個template中創建出來的;最后一段,是一個random的字串
通過get.pod可以看到,Pod的ownerReferences即Pod所屬的controller資源,并不是Deployment,而是一個ReplicaSet,這個ReplicaSet的name,其實是nginx-deployment加上pod.template-hash,所有的Pod都是ReplicaSet創建出來的,而ReplicaSet它對應的某一個具體的Deployment.template版本
4)、更新鏡像

首先kubectl后面有一個set image固定寫法,這里指的是設定鏡像;其次是一個deployment.v1.apps,這里也是一個固定寫法,寫的是我們要操作的資源型別,deployment是資源名、v1是資源版本、apps是資源組,這里也可以簡寫為deployment或者deployment.apps,比如說寫為deployment的時候,默認將使用apps組v1版本
第三部分是要更新的deployment的name,也就是我們的nginx-deployment;再往后的nginx其實指的是template,也就是Pod中的container.name;這里可以注意到:一個Pod中,其實可能存在多個container,而我們指定想要更新的鏡像的container.name,就是nginx
最后,指定我們這個容器期望更新的鏡像版本,這里指的是nginx: 1.9.1,如上圖所示:當執行完這條命令之后,可以看到deployment中的template.spec已經更新為nginx: 1.9.1
5)、快速回滾

通過kubectl執行的話,其實是kubectl rollout undo這個命令,可以回滾到Deployment上一版本;通過rollout undo加上to-revision來指定可以回滾到某一個具體的版本
6)、DeploymeStatus

deploymentStatus中描述的三個其實是它的conversion狀態,也就是Processing、Complete以及Failed
以Processing為例:Processing指的是Deployment正在處于擴容和發布中,比如說Processing狀態的deployment,它所有的replicas及Pod副本全部達到最新版本,而且是available,這樣的話,就可以進入complete狀態,而complete狀態如果發生了一些擴縮容的話,也會進入processing這個處理作業狀態
如果在處理程序中遇到一些問題:比如說拉鏡像失敗了,或者說readiness probe檢查失敗了,就會進入failed狀態;如果在運行程序中即complete狀態,中間運行時發生了一些pod readiness probe檢查失敗,這個時候deployment也會進入failed狀態,進入failed狀態之后,除非所有點replicas均變成available,而且是updated最新版本,deployment才會重新進入complete狀態
3、操作演示
hanxiantaodeMBP:yamls hanxiantao$ kubectl create -f deployment-case.yaml
deployment.apps/nginx-deployment created
hanxiantaodeMBP:yamls hanxiantao$ kubectl get deployment nginx-deployment
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 3/3 3 3 15s
hanxiantaodeMBP:yamls hanxiantao$ kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-deployment-5d59d67564-528s8 1/1 Running 0 30s
nginx-deployment-5d59d67564-6znl8 1/1 Running 0 30s
nginx-deployment-5d59d67564-q47cp 1/1 Running 0 30s
hanxiantaodeMBP:yamls hanxiantao$ kubectl get replicaset
NAME DESIRED CURRENT READY AGE
nginx-deployment-5d59d67564 3 3 3 99s
升級nginx為nginx:1.9.1
hanxiantaodeMBP:yamls hanxiantao$ kubectl set image deployment nginx-deployment nginx=nginx:1.9.1
deployment.apps/nginx-deployment image updated
hanxiantaodeMBP:yamls hanxiantao$ kubectl edit deployment nginx-deployment
template:
metadata:
creationTimestamp: null
labels:
app: nginx
spec:
containers:
- image: nginx:1.9.1
imagePullPolicy: IfNotPresent
name: nginx
ports:
- containerPort: 80
protocol: TCP
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
hanxiantaodeMBP:yamls hanxiantao$ kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-deployment-69c44dfb78-6v2gv 1/1 Running 0 2m2s
nginx-deployment-69c44dfb78-gzbnf 1/1 Running 0 83s
nginx-deployment-69c44dfb78-j7wwl 1/1 Running 0 85s
hanxiantaodeMBP:yamls hanxiantao$ kubectl get replicaset
NAME DESIRED CURRENT READY AGE
nginx-deployment-5d59d67564 0 0 0 8m33s
nginx-deployment-69c44dfb78 3 3 3 5m47s
回滾到上一版本
hanxiantaodeMBP:yamls hanxiantao$ kubectl rollout undo deployment nginx-deployment
deployment.apps/nginx-deployment rolled back
hanxiantaodeMBP:yamls hanxiantao$ kubectl get replicaset
NAME DESIRED CURRENT READY AGE
nginx-deployment-5d59d67564 3 3 3 12m
nginx-deployment-69c44dfb78 0 0 0 9m15s
4、架構設計
1)、管理模式

Deployment只負責管理不同版本的ReplicaSet,由ReplicaSet來管理具體的Pod副本數,每個ReplicaSet對應Deployment template的一個版本
如上圖所示:Deployment創建ReplicaSet,而ReplicaSet創建Pod,他們的OwnerRef其實都對應了其控制器的資源
2)、Deployment控制器

所有的控制器都是通過Informer中的Event 做一些Handler和Watch,這個地方Deployment控制器,其實是關注Deployment和ReplicaSet中的event,收到事件后會加入到佇列中,而Deployment controller從佇列中取出來之后,它的邏輯會判斷Check Paused,這個Paused其實是Deployment是否需要新的發布,如果Paused設定為true的話,就表示這個Deployment只會做一個數量上的維持,不會做新的發布
如果Check paused為Yes也就是true的話,那么只會做Sync replicas,也就是說把replicas sync同步到對應的ReplicaSet 中,最后再Update Deployment status,那么controller這一次的ReplicaSet就結束了
如果paused為false的話,它就會做Rollout,也就是通過Create或者是Rolling的方式來做更新,更新的方式其實也是通過Create/Update/Delete這種ReplicaSet來做實作的
3)、ReplicaSet控制器

當Deployment分配ReplicaSet之后,ReplicaSet控制器本身也是從Informer中watch一些事件,這些事件包含了ReplicaSet和Pod的事件,從佇列中取出之后,ReplicaSet controller的邏輯很簡單,就只管理副本數,也就是說如果controller發現replicas比Pod數量大的話,就會擴容,而如果發現實際數量超過期望數量的話,就會洗掉Pod
上面Deployment控制器的圖中可以看到,Deployment控制器其實做了更復雜的事情,包含了版本管理,而它把每一個版本下的數量維持作業交給ReplicaSet來做
4)、擴容模擬

有一個Deployment,它的副本數是2,對應的ReplicaSet有Pod1和Pod2,這時如果我們修改Deployment replicas, controller就會把replicas同步到當前版本的ReplicaSet中,這個ReplicaSet發現當前有2個Pod,不滿足當前期望3個,就會創建一個新的Pod3
5)、發布模擬

Deployment當前初始的template,比如說template1這個版本,template1這個ReplicaSet對應的版本下有三個Pod:Pod1,Pod2,Pod3
這時修改template中一個容器的image, Deployment controller就會新建一個對應template2的ReplicaSet,創建出來之后ReplicaSet會逐漸修改兩個ReplicaSet的數量,比如它會逐漸增加ReplicaSet2中replicas的期望數量,而逐漸減少ReplicaSet1中的Pod數量
那么最終達到的效果是:新版本的Pod為Pod4、Pod5和Pod6,舊版本的Pod已經被洗掉了,這里就完成了一次發布
6)、回滾模擬

回滾模擬,根據上面的發布模擬可以知道Pod4、Pod5、Pod6已經發布完成,這時發現當前的業務版本是有問題的,如果做回滾的話,不管是通過rollout命令還是通過回滾修改template,它其實都是把template回滾為舊版本的template1
這個時候Deployment會重新修改ReplicaSet1中Pod的期望數量,把期望數量修改為3個,且會逐漸減少新版本也就是ReplicaSet2中的replica數量,最終的效果就是把Pod從舊版本重新創建出來
發布模擬的圖中可以看到,其實初始版本中Pod1、Pod2、Pod3是舊版本,而回滾之后其實是Pod7、Pod8、Pod9,就是說它的回滾并不是把之前的Pod重新找出來,而是說重新創建出符合舊版本template的Pod
7)、spec欄位決議

8)、升級策略欄位決議

課程地址:https://edu.aliyun.com/roadmap/cloudnative?spm=5176.11399608.aliyun-edu-index-014.4.dc2c4679O3eIId#suit
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/241488.html
標籤:其他
