openkruise详解
快速开始
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 原地升级。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)