Kubernetes應用管理器OpenKruise
OpenKruise
OpenKruise 是 Kubernetes 的一個標準擴展,它可以配合原生 Kubernetes 使用,并為管理應用容器、sidecar、鏡像分發等方面提供更加強大和高效的能力。
核心功能
原地升級
原地升級是一種可以避免刪除、新建 Pod 的升級鏡像能力。它比原生 Deployment/StatefulSet 的重建 Pod 升級更快、更高效,并且避免對 Pod 對其他不需要更新的容器造成干擾。
Sidecar 管理
支持在一個單獨的 CR 中定義 sidecar 容器,OpenKruise 能夠幫你把這些 Sidecar 容器注入到所有符合條件的 Pod 中。這個過程和 Istio 的注入很相似,但是你可以管理任意你關心的 Sidecar。
跨多可用區部署
定義一個跨多個可用區的全局 workload,容器,OpenKruise 會幫你在每個可用區創建一個對應的下屬 workload。你可以統一管理他們的副本數、版本、甚至針對不同可用區采用不同的發布策略。
CRD 列表
- CloneSet
- 提供更加高效、確定可控的應用管理和部署能力,支持優雅原地升級、指定刪除、發布順序可配置、并行/灰度發布等豐富的策略,可以滿足更多樣化的應用場景。
- Advanced StatefulSet
- 基于原生 StatefulSet 之上的增強版本,默認行為與原生完全一致,在此之外提供了原地升級、并行發布(最大不可用)、發布暫停等功能。
- SidecarSet
- 對 sidecar 容器做統一管理,在滿足 selector 條件的 Pod 中注入指定的 sidecar 容器。
- UnitedDeployment
- 通過多個 subset workload 將應用部署到多個可用區。
- BroadcastJob
- 配置一個 job,在集群中所有滿足條件的 Node 上都跑一個 Pod 任務。
- Advanced DaemonSet
- 基于原生 DaemonSet 之上的增強版本,默認行為與原生一致,在此之外提供了灰度分批、按 Node label 選擇、暫停、熱升級等發布策略。
- AdvancedCronJob
- 一個擴展的 CronJob 控制器,目前 template 模板支持配置使用 Job 或 BroadcastJob。
以上在官方文檔都有介紹,本文主要著重實戰,先講CloneSet,其他控制器后面會陸續更新。。。
部署Kruise到Kubernetes集群
這里使用helm來安裝Kruise
1、現在kruise Chart
- wget https://github.com/openkruise/kruise/releases/download/v0.7.0/kruise-chart.tgz
- tar -zxf kruise-chart.tgz
- cd kruise
- [root@ kruise]# ls -l
- total 16
- -rw-r--r-- 1 root root 311 Dec 20 15:09 Chart.yaml
- -rw-r--r-- 1 root root 4052 Dec 20 15:09 README.md
- drwxr-xr-x 2 root root 4096 Dec 23 10:18 templates
- -rw-r--r-- 1 root root 659 Dec 20 15:09 values.yaml
2、修改values.yaml,默認不用修改也行
3、執行部署
- [root@qd01-stop-k8s-master001 kruise]# kubectl create ns kruise
- namespace/kruise created
- [root@qd01-stop-k8s-master001 kruise]# helm install kruise -n kruise -f values.yaml .
- W1223 10:22:13.562088 1589994 warnings.go:67] apiextensions.k8s.io/v1beta1 CustomResourceDefinition is deprecated in v1.16+, unavailable in v1.22+; use apiextensions.k8s.io/v1 CustomResourceDefinition
- 。。。。。。。
- NAME: kruise
- LAST DEPLOYED: Wed Dec 23 10:22:12 2020
- NAMESPACE: kruise
- STATUS: deployed
- REVISION: 1
- TEST SUITE: None
- 這里會看到一堆的deprecated信息,因為新版的kubernetes對CRD的版本會淘汰,可以根據自己的集群版本修改CRD的API版本即可
4、檢查kruise部署狀態
- [root@qd01-stop-k8s-master001 kruise]# helm ls -n kruise
- NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
- kruise kruise 1 2020-12-23 10:22:12.963651877 +0800 CST deployed kruise-0.7.0
- 可以看到,集群中有的kruise crd類型
- [root@qd01-stop-k8s-master001 kruise]# kubectl get crd|grep kruise
- advancedcronjobs.apps.kruise.io 2020-12-23T02:22:13Z
- broadcastjobs.apps.kruise.io 2020-12-23T02:22:13Z
- clonesets.apps.kruise.io 2020-12-23T02:22:13Z
- daemonsets.apps.kruise.io 2020-12-23T02:22:13Z
- sidecarsets.apps.kruise.io 2020-12-23T02:22:13Z
- statefulsets.apps.kruise.io 2020-12-23T02:22:13Z
- uniteddeployments.apps.kruise.io 2020-12-23T02:22:13Z
下面我們開始來使用這些管理器
CloneSet
CloneSet 控制器提供了高效管理無狀態應用的能力,它可以對標原生的 Deployment,但 CloneSet 提供了很多增強功能。
1、我們先創建一個簡單的CloneSet,yaml如下
- apiVersion: apps.kruise.io/v1alpha1
- kind: CloneSet
- metadata:
- labels:
- app: nginx-alpine
- name: nginx-alpine
- spec:
- replicas: 5
- selector:
- matchLabels:
- app: nginx-alpine
- template:
- metadata:
- labels:
- app: nginx-alpine
- spec:
- containers:
- - name: nginx
- image: nginx:alpine
2、部署
- [root@qd01-stop-k8s-master001 demo]# kubectl apply -f CloneSet.yaml
- cloneset.apps.kruise.io/nginx-alpine created
- [root@qd01-stop-k8s-master001 demo]# kubectl get po |grep nginx
- nginx-alpine-29g7n 1/1 Running 0 45s
- nginx-alpine-bvgqm 1/1 Running 0 45s
- nginx-alpine-q9tlw 1/1 Running 0 45s
- nginx-alpine-s2t46 1/1 Running 0 44s
- nginx-alpine-sslvf 1/1 Running 0 44s
- 從輸出結果看,和原生的Deployment沒有啥區別
- #注意,這里如果get deployment是看不到nginx-alpine這個應用的,需要get cloneset才能看到
- [root@qd01-stop-k8s-master001 demo]# kubectl get deployment
- [root@qd01-stop-k8s-master001 demo]# kubectl get cloneset
- NAME DESIRED UPDATED UPDATED_READY READY TOTAL AGE
- nginx-alpine 5 5 5 5 5 2m16s
CloneSet 允許用戶配置 PVC 模板 volumeClaimTemplates,用來給每個 Pod 生成獨享的 PVC,這是 Deployment 所不支持的。 如果用戶沒有指定這個模板,CloneSet 會創建不帶 PVC 的 Pod。
3、現在來創建一個帶有 PVC 模板的例子
- apiVersion: apps.kruise.io/v1alpha1
- kind: CloneSet
- metadata:
- labels:
- app: nginx-2
- name: nginx-2
- spec:
- replicas: 5
- selector:
- matchLabels:
- app: nginx-2
- template:
- metadata:
- labels:
- app: nginx-2
- spec:
- containers:
- - name: nginx
- image: nginx:alpine
- volumeMounts:
- - name: data-vol
- mountPath: /usr/share/nginx/html
- volumeClaimTemplates:
- - metadata:
- name: rbd
- spec:
- accessModes: [ "ReadWriteOnce" ]
- storageClassName: rbd
- resources:
- requests:
- storage: 2Gi
部署
- [root@qd01-stop-k8s-master001 demo]# kubectl apply -f CloneSet.yaml
- cloneset.apps.kruise.io/nginx-2 created
- [root@qd01-stop-k8s-master001 demo]# kubectl get pv|grep data-vol
- pvc-0fde19f3-ea4b-47e0-81be-a8e43812e47b 2Gi RWO Delete Bound default/data-vol-nginx-2-t55h8 rbd 83s
- pvc-72accf10-57a6-4418-a1bc-c64633b84434 2Gi RWO Delete Bound default/data-vol-nginx-2-t49mk rbd 82s
- pvc-8fc8b9a5-afe8-446a-9190-08fcee0ec9f6 2Gi RWO Delete Bound default/data-vol-nginx-2-jw2zp rbd 84s
- pvc-c9fba396-e357-43e8-9510-616f698da765 2Gi RWO Delete Bound default/data-vol-nginx-2-b5fdd rbd 84s
- pvc-e5302eab-a9f2-4a71-a5a3-4cd43205e8a0 2Gi RWO Delete Bound default/data-vol-nginx-2-l54dz rbd 84s
- [root@qd01-stop-k8s-master001 demo]# kubectl get po|grep nginx
- nginx-2-b5fdd 1/1 Running 0 97s
- nginx-2-jw2zp 1/1 Running 0 97s
- nginx-2-l54dz 1/1 Running 0 97s
- nginx-2-t49mk 1/1 Running 0 96s
- nginx-2-t55h8 1/1 Running 0 96s
從部署結果可以看到,每個pod都創建了一個PVC,這個是原生的Deployment不能實現的。
注意:
- 每個被自動創建的 PVC 會有一個 ownerReference 指向 CloneSet,因此 CloneSet 被刪除時,它創建的所有 Pod 和 PVC 都會被刪除。
- 每個被 CloneSet 創建的 Pod 和 PVC,都會帶一個 apps.kruise.io/cloneset-instance-id: xxx 的 label。關聯的 Pod 和 PVC 會有相同的 instance-id,且它們的名字后綴都是這個 instance-id。
- 如果一個 Pod 被 CloneSet controller 縮容刪除時,這個 Pod 關聯的 PVC 都會被一起刪掉。
- 如果一個 Pod 被外部直接調用刪除或驅逐時,這個 Pod 關聯的 PVC 還都存在;并且 CloneSet controller 發現數量不足重新擴容時,新擴出來的 Pod 會復用原 Pod 的 instance-id 并關聯原來的 PVC。
- 當 Pod 被重建升級時,關聯的 PVC 會跟隨 Pod 一起被刪除、新建。
- 當 Pod 被原地升級時,關聯的 PVC 會持續使用。
4、指定 Pod 縮容
當一個 CloneSet 被縮容時,有時候用戶需要指定一些 Pod 來刪除。這對于 StatefulSet 或者 Deployment 來說是無法實現的,因為 StatefulSet 要根據序號來刪除 Pod,而 Deployment/ReplicaSet 目前只能根據控制器里定義的排序來刪除。
CloneSet 允許用戶在縮小 replicas 數量的同時,指定想要刪除的 Pod 名字。
現在我們來修改上面例子的部署文件,指定刪除nginx-2-t55h8這個Pod
- apiVersion: apps.kruise.io/v1alpha1
- kind: CloneSet
- metadata:
- labels:
- app: nginx-2
- name: nginx-2
- spec:
- replicas: 4
- scaleStrategy:
- podsToDelete:
- - nginx-2-t55h8
然后更新yaml文件
- [root@qd01-stop-k8s-master001 demo]# kubectl apply -f CloneSet.yaml
- cloneset.apps.kruise.io/nginx-2 configured
- [root@qd01-stop-k8s-master001 demo]# kubectl get po|grep nginx
- nginx-2-b5fdd 1/1 Running 0 11m
- nginx-2-jw2zp 1/1 Running 0 11m
- nginx-2-l54dz 1/1 Running 0 11m
- nginx-2-t49mk 1/1 Running 0 11m
現在看輸入結果,已經沒有nginx-2-t55h8這個Pod了
這個功能很實用,比如某臺機器故障了,或者負載太高,你想刪除指定的pod。
5、升級功能
- CloneSet 提供了和 Advanced StatefulSet 相同的 3 個升級方式,默認為 ReCreate:
- ReCreate: 控制器會刪除舊 Pod 和它的 PVC,然后用新版本重新創建出來。
- InPlaceIfPossible: 控制器會優先嘗試原地升級 Pod,如果不行再采用重建升級。目前,只有修改 spec.template.metadata.* 和 spec.template.spec.containers[x].image 這些字段才可以走原地升級。
- InPlaceOnly: 控制器只允許采用原地升級。因此,用戶只能修改上一條中的限制字段,如果嘗試修改其他字段會被 Kruise 拒絕。
現在我們來嘗試原地升級Pod功能,把nginx鏡像由nginx:alpine 升級為 nginx:latest
首先修改yaml文件,這里只粘貼出文件的修改的部分
- apiVersion: apps.kruise.io/v1alpha1
- kind: CloneSet
- ...
- spec:
- replicas: 4
- updateStrategy:
- type: InPlaceIfPossible
- inPlaceUpdateStrategy:
- gracePeriodSeconds: 10
- ......
- spec:
- containers:
- - name: nginx
- image: nginx
執行升級
- [root@qd01-stop-k8s-master001 demo]# kubectl apply -f CloneSet.yaml
- cloneset.apps.kruise.io/nginx-2 configured
- 使用 kubectl describe查看升級過程
- Events:
- Type Reason Age From Message
- ---- ------ ---- ---- -------
- Warning FailedScheduling 59m default-scheduler 0/22 nodes are available: 22 pod has unbound immediate PersistentVolumeClaims.
- Warning FailedScheduling 59m default-scheduler 0/22 nodes are available: 22 pod has unbound immediate PersistentVolumeClaims.
- Warning FailedScheduling 59m default-scheduler 0/22 nodes are available: 22 pod has unbound immediate PersistentVolumeClaims.
- Normal Scheduled 59m default-scheduler Successfully assigned default/nginx-2-l54dz to qd01-stop-k8s-node007.ps.easou.com
- Normal SuccessfulAttachVolume 59m attachdetach-controller AttachVolume.Attach succeeded for volume "pvc-e5302eab-a9f2-4a71-a5a3-4cd43205e8a0"
- Normal Pulling 58m kubelet Pulling image "nginx:alpine"
- Normal Pulled 58m kubelet Successfully pulled image "nginx:alpine" in 6.230045975s
- Normal Killing 55s kubelet Container nginx definition changed, will be restarted
- Normal Pulling 55s kubelet Pulling image "nginx"
- Normal Pulled 26s kubelet Successfully pulled image "nginx" in 29.136659264s
- Normal Created 23s (x2 over 58m) kubelet Created container nginx
- Normal Started 23s (x2 over 58m) kubelet Started container nginx
從輸出可以看到,Container nginx definition changed, will be restarted,Pod并沒有刪除在重建,而是在原來的基礎上直接更新了鏡像文件,并重啟了服務。
原地升級減少了刪除重建環節,節省了升級時間和資源調度頻率。。。
6、Partition 分批灰度
Partition 的語義是 保留舊版本 Pod 的數量或百分比,默認為 0。這里的 partition 不表示任何 order 序號。
- 在發布過程中設置了 partition:
- 如果是數字,控制器會將 (replicas - partition) 數量的 Pod 更新到最新版本。
- 如果是百分比,控制器會將 (replicas * (100% - partition)) 數量的 Pod 更新到最新版本。
現在我將上面的例子的 image 更新為 nginx:1.19.6-alpine 并且設置 partition=3
- kind: CloneSet
- metadata:
- labels:
- app: nginx-2
- name: nginx-2
- spec:
- replicas: 5
- updateStrategy:
- type: InPlaceIfPossible
- inPlaceUpdateStrategy:
- gracePeriodSeconds: 10
- partition: 3
- selector:
- matchLabels:
- app: nginx-2
- template:
- metadata:
- labels:
- app: nginx-2
- spec:
- containers:
- - name: nginx
- image: nginx:1.19.6-alpine
查看結果
- Status:
- Available Replicas: 5
- Collision Count: 0
- Label Selector: app=nginx-2
- Observed Generation: 6
- Ready Replicas: 5
- Replicas: 5
- Update Revision: nginx-2-7b44cb9c8
- Updated Ready Replicas: 2
- Updated Replicas: 2
- Events:
- Type Reason Age From Message
- ---- ------ ---- ---- -------
- Normal SuccessfulUpdatePodInPlace 45m cloneset-controller successfully update pod nginx-2-l54dz in-place(revision nginx-2-5879fd9f7)
- Normal SuccessfulUpdatePodInPlace 44m cloneset-controller successfully update pod nginx-2-t49mk in-place(revision nginx-2-5879fd9f7)
- Normal SuccessfulUpdatePodInPlace 43m cloneset-controller successfully update pod nginx-2-b5fdd in-place(revision nginx-2-5879fd9f7)
- Normal SuccessfulUpdatePodInPlace 43m cloneset-controller successfully update pod nginx-2-jw2zp in-place(revision nginx-2-5879fd9f7)
- Normal SuccessfulCreate 22m cloneset-controller succeed to create pod nginx-2-zpp8z
- Normal SuccessfulUpdatePodInPlace 5m22s cloneset-controller successfully update pod nginx-2-zpp8z in-place(revision nginx-2-7b44cb9c8)
- Normal SuccessfulUpdatePodInPlace 4m55s cloneset-controller successfully update pod nginx-2-jw2zp in-place(revision nginx-2-7b44cb9c8)
- [root@qd01-stop-k8s-master001 demo]# kubectl get pod -L controller-revision-hash
- NAME READY STATUS RESTARTS AGE CONTROLLER-REVISION-HASH
- nginx-2-b5fdd 1/1 Running 1 99m nginx-2-5879fd9f7
- nginx-2-jw2zp 1/1 Running 2 99m nginx-2-7b44cb9c8
- nginx-2-l54dz 1/1 Running 1 99m nginx-2-5879fd9f7
- nginx-2-t49mk 1/1 Running 1 99m nginx-2-5879fd9f7
- nginx-2-zpp8z 1/1 Running 1 19m nginx-2-7b44cb9c8
從輸出信息我們可以看到,Update Revision已經更新為nginx-2-7b44cb9c8,而Pod中只有兩個Pod升級了。
由于我們設置了 partition=3,控制器只升級了 2 個 Pod。
Partition 分批灰度功能完善了原生的Pod升級方式,使得升級能夠進行更靈活,能夠進行灰度上線。超贊。。。
7、最后再演示下發布暫停
用戶可以通過設置 paused 為 true 暫停發布,不過控制器還是會做 replicas 數量管理:
- 首先,我們將示例中image改為nginx:1.18.0 并設置副本數為10,修改后更新yaml,運行結果如下:
- [root@qd01-stop-k8s-master001 demo]# kubectl get po -o=jsonpath='{range .items[*]}{"\n"}{.metadata.name}{":\t"}{range .spec.containers[*]}{.image}{", "}{end}{end}' |sort
- nginx-2-7lzx9: nginx:1.18.0,
- nginx-2-b5fdd: nginx:1.18.0,
- nginx-2-jw2zp: nginx:1.18.0,
- nginx-2-l54dz: nginx:1.18.0,
- nginx-2-nknrt: nginx:1.18.0,
- nginx-2-rgmsc: nginx:1.18.0,
- nginx-2-rpr5z: nginx:1.18.0,
- nginx-2-t49mk: nginx:1.18.0,
- nginx-2-v2bpx: nginx:1.18.0,
- nginx-2-zpp8z: nginx:1.18.0,
- 現在我們修改yaml文件,將image修改為nginx:alpine 執行更新,運行如下
- [root@qd01-stop-k8s-master001 demo]# kubectl get po -o=jsonpath='{range .items[*]}{"\n"}{.metadata.name}{":\t"}{range .spec.containers[*]}{.image}{", "}{end}{end}' |sort
- nginx-2-7lzx9: nginx:1.18.0,
- nginx-2-b5fdd: nginx:1.18.0,
- nginx-2-jw2zp: nginx:1.18.0,
- nginx-2-l54dz: nginx:1.18.0,
- nginx-2-nknrt: nginx:alpine,
- nginx-2-rgmsc: nginx:alpine,
- nginx-2-rpr5z: nginx:alpine,
- nginx-2-t49mk: nginx:1.18.0,
- nginx-2-v2bpx: nginx:alpine,
- nginx-2-zpp8z: nginx:1.18.0,
- 現在看到,有4個pod的image已經更新為nginx:alpine 然后我們再次修改yaml文件,添加paused: true
- spec:
- replicas: 10
- updateStrategy:
- paused: true
- type: InPlaceIfPossible
- inPlaceUpdateStrategy:
- gracePeriodSeconds: 10
- 再次執行apply,更新yaml,再次查看更新進度,發現pod并沒有繼續更新了,已經暫停升級image了
- [root@qd01-stop-k8s-master001 demo]# kubectl get po -o=jsonpath='{range .items[*]}{"\n"}{.metadata.name}{":\t"}{range .spec.containers[*]}{.image}{", "}{end}{end}' |sort
- nginx-2-7lzx9: nginx:1.18.0,
- nginx-2-b5fdd: nginx:1.18.0,
- nginx-2-jw2zp: nginx:1.18.0,
- nginx-2-l54dz: nginx:1.18.0,
- nginx-2-nknrt: nginx:alpine,
- nginx-2-rgmsc: nginx:alpine,
- nginx-2-rpr5z: nginx:alpine,
- nginx-2-t49mk: nginx:1.18.0,
- nginx-2-v2bpx: nginx:alpine,
- nginx-2-zpp8z: nginx:1.18.0,
- 最后把paused: true取消,再次apply yaml文件,升級會繼續。。。
- [root@qd01-stop-k8s-master001 demo]# kubectl get po -o=jsonpath='{range .items[*]}{"\n"}{.metadata.name}{":\t"}{range .spec.containers[*]}{.image}{", "}{end}{end}' |sort
- nginx-2-7lzx9: nginx:alpine,
- nginx-2-b5fdd: nginx:alpine,
- nginx-2-jw2zp: nginx:alpine,
- nginx-2-l54dz: nginx:alpine,
- nginx-2-nknrt: nginx:alpine,
- nginx-2-rgmsc: nginx:alpine,
- nginx-2-rpr5z: nginx:alpine,
- nginx-2-t49mk: nginx:alpine,
- nginx-2-v2bpx: nginx:alpine,
- nginx-2-zpp8z: nginx:alpine,
以上就是整個發布暫停的演示,這個功能好處就是;我們在升級的過程中可以隨時中斷升級。
除此之外,CloneSet還有很多特性,例如:MaxUnavailable 最大不可用數量、MaxSurge 最大彈性數量、升級順序、打散策略、生命周期鉤子等,鑒于文章篇幅,這些特性不再演示了,有需要的可以查看官方文檔。