快速開始
openkruise簡介
OpenKruise 是一個基於 Kubernetes 的擴展套件,主要聚焦於雲原生應用的自動化,比如部署、發布、運維以及可用性防護。
OpenKruise 提供的絕大部分能力都是基於 CRD 擴展來定義,它們不存在於任何外部依賴,可以運行在任意純凈的 Kubernetes 集群中。
核心能力
- 增強版本的 Workloads
OpenKruise 包含了一系列增強版本的 Workloads(工作負載),比如 CloneSet、Advanced StatefulSet、Advanced DaemonSet、BroadcastJob 等。
它們不僅支持類似於 Kubernetes 原生 Workloads 的基礎功能,還提供了如原地升級、可配置的擴縮容/發布策略、並發操作等。
其中,原地升級是一種升級應用容器鏡像甚至環境變量的全新方式。它只會用新的鏡像重建 Pod 中的特定容器,整個 Pod 以及其中的其他容器都不會被影響。因此它帶來了更快的發布速度,以及避免了對其他 Scheduler、CNI、CSI 等組件的負面影響。 - 應用的旁路管理
OpenKruise 提供了多種通過旁路管理應用 sidecar 容器、多區域部署的方式,“旁路” 意味着你可以不需要修改應用的 Workloads 來實現它們。
比如,SidecarSet 能幫助你在所有匹配的 Pod 創建的時候都注入特定的 sidecar 容器,甚至可以原地升級已經注入的 sidecar 容器鏡像、並且對 Pod 中其他容器不造成影響。
而 WorkloadSpread 可以約束無狀態 Workload 擴容出來 Pod 的區域分布,賦予單一 workload 的多區域和彈性部署的能力。 - 高可用性防護
OpenKruise 在為應用的高可用性防護方面也做出了很多努力。
目前它可以保護你的 Kubernetes 資源不受級聯刪除機制的干擾,包括 CRD、Namespace、以及幾乎全部的 Workloads 類型資源。
相比於 Kubernetes 原生的 PDB 只提供針對 Pod Eviction 的防護,PodUnavailableBudget 能夠防護 Pod Deletion、Eviction、Update 等許多種 voluntary disruption 場景。 - 高級的應用運維能力
OpenKruise 也提供了很多高級的運維能力來幫助你更好地管理應用。
你可以通過 ImagePullJob 來在任意范圍的節點上預先拉取某些鏡像,或者指定某個 Pod 中的一個或多個容器被原地重啟。
關系對比
OpenKruise vs. Kubernetes
簡單來說,OpenKruise 對於 Kubernetes 是一個輔助擴展角色。
Kubernetes 自身已經提供了一些應用部署管理的功能,比如一些基礎工作負載。 但對於大規模應用與集群的場景,這些基礎功能是遠遠不夠的。
OpenKruise 可以被很容易地安裝到任意 Kubernetes 集群中,它彌補了 Kubernetes 在應用部署、升級、防護、運維 等領域的不足。
OpenKruise vs. Platform-as-a-Service (PaaS)
OpenKruise 不是一個 PaaS 平台,並且也不會提供任何 PaaS 層的能力。
它是一個 Kubernetes 的標准擴展套件,目前包括 kruise-manager 和 kruise-daemon 兩個組件。 PaaS 平台可以通過使用 OpenKruise 提供的這些擴展功能,來使得應用部署、管理流程更加強大與高效。
安裝
從 v1.0.0 (alpha/beta) 開始,OpenKruise 要求在 Kubernetes >= 1.16 以上版本的集群中安裝和使用。
通過 helm 安裝
建議采用 helm v3.5+ 來安裝 Kruise,helm 是一個簡單的命令行工具可以從 這里 獲取。
# Firstly add openkruise charts repository if you haven't do this.
$ helm repo add openkruise https://openkruise.github.io/charts/
# [Optional]
$ helm repo update
# Install the latest version.
$ helm install kruise openkruise/kruise --version 1.1.0
通過 helm 升級
# Firstly add openkruise charts repository if you haven't do this.
$ helm repo add openkruise https://openkruise.github.io/charts/
# [Optional]
$ helm repo update
# Upgrade the latest version.
$ helm upgrade kruise openkruise/kruise --version 1.1.0 [--force]
注意:
在升級之前,必須 先閱讀 Change Log ,確保你已經了解新版本的不兼容變化。
如果你要重置之前舊版本上用的參數或者配置一些新參數,建議在 helm upgrade 命令里加上 --reset-values。
如果你在將 Kruise 從 0.x 升級到 1.x 版本,你需要為 upgrade 命令添加 --force 參數,其他情況下這個參數是可選的。
可選的:手工下載 charts 包
如果你在生產環境無法連接到 https://openkruise.github.io/charts/,可以先在這里手工下載 chart 包,再用它安裝或更新到集群中。
$ helm install/upgrade kruise /PATH/TO/CHART
可選項
注意直接安裝 chart 會使用默認的 template values,你也可以根據你的集群情況指定一些特殊配置,比如修改 resources 限制或者配置 feature-gates。
可選: chart 安裝參數
下表展示了 chart 所有可配置的參數和它們的默認值:
Parameter | Description | Default |
---|---|---|
featureGates |
可配置的 feature gates 參數,空表示按默認開關處理 | `` |
installation.namespace |
kruise 安裝到的 namespace,一般不建議修改 | kruise-system |
installation.createNamespace |
是否需要創建上述 namespace,一般不建議修改,除非指定安裝到已有的 ns 中 | true |
manager.log.level |
kruise-manager 日志輸出級別 | 4 |
manager.replicas |
kruise-manager 的期望副本數 | 2 |
manager.image.repository |
kruise-manager/kruise-daemon 鏡像倉庫 | openkruise/kruise-manager |
manager.image.tag |
kruise-manager/kruise-daemon 鏡像版本 | 1.1.0 |
manager.resources.limits.cpu |
kruise-manager 的 limit CPU 資源 | 200m |
manager.resources.limits.memory |
kruise-manager 的 limit memory 資源 | 512Mi |
manager.resources.requests.cpu |
kruise-manager 的 request CPU 資源 | 100m |
manager.resources.requests.memory |
kruise-manager 的 request memory 資源 | 256Mi |
manager.metrics.port |
metrics 服務的監聽端口 | 8080 |
manager.webhook.port |
webhook 服務的監聽端口 | 9443 |
manager.nodeAffinity |
kruise-manager 部署的 node affinity 親和性 | {} |
manager.nodeSelector |
kruise-manager 部署的 node selector 親和性 | {} |
manager.tolerations |
kruise-manager 部署的 tolerations | [] |
daemon.log.level |
kruise-daemon 日志輸出級別 | 4 |
daemon.port |
kruise-daemon 的 metrics/healthz 服務監聽端口 | 10221 |
daemon.resources.limits.cpu |
kruise-daemon 的 limit CPU 資源 | 50m |
daemon.resources.limits.memory |
kruise-daemon 的 limit memory 資源 | 128Mi |
daemon.resources.requests.cpu |
kruise-daemon 的 request CPU 資源 | 0 |
daemon.resources.requests.memory |
kruise-daemon 的 request memory 資源 | 0 |
daemon.affinity |
kruise-daemon 部署的 affinity 親和性 (可以排除一些 node 不部署 daemon) | {} |
daemon.socketLocation |
Node 節點上 CRI socket 文件所在目錄 | /var/run |
daemon.socketFile |
指定 socketLocation 目錄下的 socket 文件名 (如果你使用的 CRI 類型不是 containerd/docker/pouch/cri-o) |
`` |
webhookConfiguration.failurePolicy.pods |
Pod webhook 的失敗策略 | Ignore |
webhookConfiguration.timeoutSeconds |
所有 Kruise webhook 的調用超時時間 | 30 |
crds.managed |
是否安裝 Kruise CRD (如何關閉則 chart 不會安裝任何 CRD) | true |
manager.resyncPeriod |
kruise-manager 中 informer 的 resync 周期,默認不做 resync | 0 |
manager.hostNetwork |
kruise-manager pod 是否采用 hostnetwork 網絡 | false |
imagePullSecrets |
kruise 鏡像用的 imagePullSecrets 列表 | false |
這些參數可以通過 --set key=value[,key=value] 參數在 helm install 或 helm upgrade 命令中生效。
可選: feature-gate
Feature-gate 控制了 Kruise 中一些有影響性的功能:
Name | Description | Default | Side effect (if closed) |
---|---|---|---|
PodWebhook |
啟用對於 Pod 創建 的 webhook (不建議關閉) | true |
SidecarSet/KruisePodReadinessGate 不可用 |
KruiseDaemon |
啟用 kruise-daemon DaemonSet (不建議關閉) |
true |
鏡像預熱/容器重啟 不可用 |
DaemonWatchingPod |
每個 kruise-daemon 會 watch 與自己同節點的 pod (不建議關閉) |
true |
同 imageID 的原地升級,以及支持 env from labels/annotation 原地升級 不可用 |
CloneSetShortHash |
啟用 CloneSet controller 只在 pod label 中設置短 hash 值 | false |
CloneSet 名字不能超過 54 個字符(默認行為) |
KruisePodReadinessGate |
啟用 Kruise webhook 將 'KruisePodReady' readiness-gate 在所有 Pod 創建時注入 | false |
只會注入到 Kruise workloads 創建的 Pod 中 |
PreDownloadImageForInPlaceUpdate |
啟用 CloneSet 自動為原地升級的過程創建 ImagePullJob 來預熱鏡像 | false |
原地升級無鏡像提前預熱 |
CloneSetPartitionRollback |
啟用如果 partition 被調大, CloneSet controller 會回滾 Pod 到 currentRevision 老版本 | false |
CloneSet 只會正向發布 Pod 到 updateRevision |
ResourcesDeletionProtection |
資源刪除防護 | false |
資源刪除無保護 |
TemplateNoDefaults |
是否取消對 workload 中 pod/pvc template 的默認值注入 | false |
Should not close this feature if it has open |
PodUnavailableBudgetDeleteGate |
啟用 PodUnavailableBudget 保護 pod 刪除、驅逐 | false |
不防護 pod 刪除、驅逐 |
PodUnavailableBudgetUpdateGate |
啟用 PodUnavailableBudget 保護 pod 原地升級 | false |
不防護 pod 原地升級 |
WorkloadSpread |
啟用 WorkloadSpread 管理應用多分區彈性與拓撲部署 | false |
不支持 WorkloadSpread |
InPlaceUpdateEnvFromMetadata |
啟用 Kruise 原地升級容器當它存在 env from 的 labels/annotations 發生了變化 | false |
容器中只有 image 能夠原地升級 |
StatefulSetAutoDeletePVC |
啟用 StatefulSet 自動刪除它所創建的 PVC | false |
StatefulSet 不會清理 PVC |
如果你要配置 feature-gate,只要在安裝或升級時配置參數即可,比如:
$ helm install kruise https://... --set featureGates="ResourcesDeletionProtection=true\,PreDownloadImageForInPlaceUpdate=true"
如果你希望打開所有 feature-gate 功能,配置參數 featureGates=AllAlpha=true。
可選: 中國本地鏡像
如果你在中國、並且很難從官方 DockerHub 上拉鏡像,那么你可以使用托管在阿里雲上的鏡像倉庫:
$ helm install kruise https://... --set manager.image.repository=openkruise-registry.cn-hangzhou.cr.aliyuncs.com/openkruise/kruise-manager
最佳實踐
k3s 安裝參數
通常來說 k3s 有着與默認 /var/run 不同的 runtime socket 路徑。所以你需要將 daemon.socketLocation 參數設置為你的 k3s 節點上真實的路徑(比如 /run/k3s 或 /var/run/k3s/)。
AWS EKS 安裝參數
當在 EKS 上使用自定義 CNI 插件(如 Weave 或 Calico)時,默認情況下 webhook 無法被連接到。這是因為在 EKS 上 control plane 不能被配置為運行到一個自定義的 CNI 上,所以 control plane 和 worker 節點的 CNI 是不同的。
可以通過給 webhook 配置 host network 網絡來解決這個問題,在 helm install/upgrade 的時候加入 --set manager.hostNetwork=true 參數即可。
卸載
注意:卸載會導致所有 Kruise 下的資源都會刪除掉,包括 webhook configurations, services, namespace, CRDs, CR instances 以及所有 Kruise workload 下的 Pod。 請務必謹慎操作!
卸載使用 helm chart 安裝的 Kruise:
$ helm uninstall kruise
release "kruise" uninstalled
核心概念
系統架構
OpenKruise 的整體架構如下:
所有 OpenKruise 的功能都是通過 Kubernetes API 來提供, 比如:
新的 CRD 定義,比如
$ kubectl get crd | grep kruise.io
advancedcronjobs.apps.kruise.io 2021-09-16T06:02:36Z
broadcastjobs.apps.kruise.io 2021-09-16T06:02:36Z
clonesets.apps.kruise.io 2021-09-16T06:02:36Z
containerrecreaterequests.apps.kruise.io 2021-09-16T06:02:36Z
daemonsets.apps.kruise.io 2021-09-16T06:02:36Z
imagepulljobs.apps.kruise.io 2021-09-16T06:02:36Z
nodeimages.apps.kruise.io 2021-09-16T06:02:36Z
podunavailablebudgets.policy.kruise.io 2021-09-16T06:02:36Z
resourcedistributions.apps.kruise.io 2021-09-16T06:02:36Z
sidecarsets.apps.kruise.io 2021-09-16T06:02:36Z
statefulsets.apps.kruise.io 2021-09-16T06:02:36Z
uniteddeployments.apps.kruise.io 2021-09-16T06:02:37Z
workloadspreads.apps.kruise.io 2021-09-16T06:02:37Z
# ...
資源對象中的特定標識(labels, annotations, envs 等),比如
apiVersion: v1
kind: Namespace
metadata:
labels:
# 保護這個 namespace 下的 Pod 不被整個 ns 級聯刪除
policy.kruise.io/delete-protection: Cascading
Manager
Kruise-manager 是一個運行 controller 和 webhook 中心組件,它通過 Deployment 部署在 kruise-system 命名空間中。
$ kubectl get deploy -n kruise-system
NAME READY UP-TO-DATE AVAILABLE AGE
kruise-controller-manager 2/2 2 2 4h6m
$ kubectl get pod -n kruise-system -l control-plane=controller-manager
NAME READY STATUS RESTARTS AGE
kruise-controller-manager-68dc6d87cc-k9vg8 1/1 Running 0 4h6m
kruise-controller-manager-68dc6d87cc-w7x82 1/1 Running 0 4h6m
邏輯上來說,如 cloneset-controller/sidecarset-controller 這些的 controller 都是獨立運行的。不過為了減少復雜度,它們都被打包在一個獨立的二進制文件、並運行在 kruise-controller-manager-xxx 這個 Pod 中。
除了 controller 之外,kruise-controller-manager-xxx 中還包含了針對 Kruise CRD 以及 Pod 資源的 admission webhook。Kruise-manager 會創建一些 webhook configurations 來配置哪些資源需要感知處理、以及提供一個 Service 來給 kube-apiserver 調用。
$ kubectl get svc -n kruise-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kruise-webhook-service ClusterIP 172.24.9.234 <none> 443/TCP 4h9m
上述的 kruise-webhook-service 非常重要,是提供給 kube-apiserver 調用的。
Daemon
這是從 Kruise v0.8.0 版本開始提供的一個新的 daemon 組件。
它通過 DaemonSet 部署到每個 Node 節點上,提供鏡像預熱、容器重啟等功能。
$ kubectl get pod -n kruise-system -l control-plane=daemon
NAME READY STATUS RESTARTS AGE
kruise-daemon-6hw6d 1/1 Running 0 4h7m
kruise-daemon-d7xr4 1/1 Running 0 4h7m
kruise-daemon-dqp8z 1/1 Running 0 4h7m
kruise-daemon-dv96r 1/1 Running 0 4h7m
kruise-daemon-q7594 1/1 Running 0 4h7m
kruise-daemon-vnsbw 1/1 Running 0 4h7m
原地升級
原地升級是 OpenKruise 提供的核心功能之一。
目前支持原地升級的 Workload:
- CloneSet
- Advanced StatefulSet
- Advanced DaemonSet
- SidecarSet
目前 CloneSet、Advanced StatefulSet、Advanced DaemonSet 是復用的同一個代碼包 ./pkg/util/inplaceupdate 並且有類似的原地升級行為。在本文中,我們會介紹它的用法和工作流程。
注意,SidecarSet 的原地升級流程和其他 workloads 不太一樣,比如它在升級 Pod 之前並不會把 Pod 設置為 not-ready 狀態。因此,下文中討論的內容並不完全適用於 SidecarSet。
什么是原地升級
當我們要升級一個存量 Pod 中的鏡像時,這是 重建升級 和 原地升級 的區別:
重建升級時我們要刪除舊 Pod、創建新 Pod:
Pod 名字和 uid 發生變化,因為它們是完全不同的兩個 Pod 對象(比如 Deployment 升級)
Pod 名字可能不變、但 uid 變化,因為它們是不同的 Pod 對象,只是復用了同一個名字(比如 StatefulSet 升級)
Pod 所在 Node 名字發生變化,因為新 Pod 很大可能性是不會調度到之前所在的 Node 節點的
Pod IP 發生變化,因為新 Pod 很大可能性是不會被分配到之前的 IP 地址的
但是對於原地升級,我們仍然復用同一個 Pod 對象,只是修改它里面的字段。因此:
可以避免如 調度、分配 IP、分配、掛載盤 等額外的操作和代價
更快的鏡像拉取,因為開源復用已有舊鏡像的大部分 layer 層,只需要拉取新鏡像變化的一些 layer
當一個容器在原地升級時,Pod 中的其他容器不會受到影響,仍然維持運行
理解 InPlaceIfPossible
這種 Kruise workload 的升級類型名為 InPlaceIfPossible,它意味着 Kruise 會盡量對 Pod 采取原地升級,如果不能則退化到重建升級。
以下的改動會被允許執行原地升級:
更新 workload 中的 spec.template.metadata.*,比如 labels/annotations,Kruise 只會將 metadata 中的改動更新到存量 Pod 上。
更新 workload 中的 spec.template.spec.containers[x].image,Kruise 會原地升級 Pod 中這些容器的鏡像,而不會重建整個 Pod。
從 Kruise v1.0 版本開始(包括 v1.0 alpha/beta),更新 spec.template.metadata.labels/annotations 並且 container 中有配置 env from 這些改動的 labels/anntations,Kruise 會原地升級這些容器來生效新的 env 值。
否則,其他字段的改動,比如 spec.template.spec.containers[x].env 或 spec.template.spec.containers[x].resources,都是會回退為重建升級。
例如對下述 CloneSet YAML:
修改 app-image:v1 鏡像,會觸發原地升級。
修改 annotations 中 app-config 的 value 內容,會觸發原地升級(參考下文使用要求)。
同時修改上述兩個字段,會在原地升級中同時更新鏡像和環境變量。
直接修改 env 中 APP_NAME 的 value 內容或者新增 env 等其他操作,會觸發 Pod 重建升級。
apiVersion: apps.kruise.io/v1alpha1
kind: CloneSet
metadata:
...
spec:
replicas: 1
template:
metadata:
annotations:
app-config: "... the real env value ..."
spec:
containers:
- name: app
image: app-image:v1
env:
- name: APP_CONFIG
valueFrom:
fieldRef:
fieldPath: metadata.annotations['app-config']
- name: APP_NAME
value: xxx
updateStrategy:
type: InPlaceIfPossible
工作流程總覽
可以在下圖中看到原地升級的整體工作流程(你可能需要右擊在新標簽頁中打開):
原地升級 - 多容器升級順序控制
FEATURE STATE: Kruise v1.1.0
當你同時原地升級多個具有不同啟動順序的容器時,Kruise 會按照相同的權重順序來逐個升級這些容器。
對於不存在容器啟動順序的 Pod,在多容器原地升級時沒有順序保證。
對於存在容器啟動順序的 Pod:
如果本次原地升級的多個容器具有不同的啟動順序,會按啟動順序來控制原地升級的先后順序。
如果本地原地升級的多個容器的啟動順序相同,則原地升級時沒有順序保證。
例如,一個包含兩個不同啟動順序容器的 CloneSet 如下:
apiVersion: apps.kruise.io/v1alpha1
kind: CloneSet
metadata:
...
spec:
replicas: 1
template:
metadata:
annotations:
app-config: "... config v1 ..."
spec:
containers:
- name: sidecar
env:
- name: KRUISE_CONTAINER_PRIORITY
value: "10"
- name: APP_CONFIG
valueFrom:
fieldRef:
fieldPath: metadata.annotations['app-config']
- name: main
image: main-image:v1
updateStrategy:
type: InPlaceIfPossible
當我們更新 CloneSet,將其中 app-config annotation 和 main 容器的鏡像修改后,意味着 sidecar 與 main 容器都需要被更新,Kruise 會先原地升級 Pod 來將其中 sidecar 容器重建來生效新的 env from annotation。
此時,我們可以在已升級的 Pod 中看到 apps.kruise.io/inplace-update-state annotation 和它的值:
{
"revision": "{CLONESET_NAME}-{HASH}", // 本次原地升級的目標 revision 名字
"updateTimestamp": "2022-03-22T09:06:55Z", // 整個原地升級的初次開始時間
"nextContainerImages": {"main": "main-image:v2"}, // 后續批次中還需要升級的容器鏡像
// "nextContainerRefMetadata": {...}, // 后續批次中還需要升級的容器 env from labels/annotations
"preCheckBeforeNext": {"containersRequiredReady": ["sidecar"]}, // pre-check 檢查項,符合要求后才能原地升級后續批次的容器
"containerBatchesRecord":[
{"timestamp":"2022-03-22T09:06:55Z","containers":["sidecar"]} // 已更新的首個批次容器(它僅僅表明容器的 spec 已經被更新,例如 pod.spec.containers 中的 image 或是 labels/annotations,但並不代表 node 上真實的容器已經升級完成了)
]
}
當 sidecar 容器升級成功之后,Kruise 會接着再升級 main 容器。最終你會在 Pod 中看到如下的 apps.kruise.io/inplace-update-state annotation:
{
"revision": "{CLONESET_NAME}-{HASH}",
"updateTimestamp": "2022-03-22T09:06:55Z",
"lastContainerStatuses":{"main":{"imageID":"THE IMAGE ID OF OLD MAIN CONTAINER"}},
"containerBatchesRecord":[
{"timestamp":"2022-03-22T09:06:55Z","containers":["sidecar"]},
{"timestamp":"2022-03-22T09:07:20Z","containers":["main"]}
]
}
通常來說,用戶只需要關注其中 containerBatchesRecord 來確保容器是被分為多批升級的。如果這個 Pod 在原地升級的過程中卡住了,你可以檢查 nextContainerImages/nextContainerRefMetadata 字段,以及 preCheckBeforeNext 中前一次升級的容器是否已經升級成功並 ready 了。
使用要求
如果要使用 env from metadata 原地升級能力,你需要在安裝或升級 Kruise chart 的時候打開 kruise-daemon(默認打開)和 InPlaceUpdateEnvFromMetadata 兩個 feature-gate。
注意,如果你有一些 virtual-kubelet 類型的 Node 節點,kruise-daemon 可能是無法在上面運行的,因此也無法使用 env from metadata 原地升級。