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 原地升级。

posted @ 2022-03-31 12:33  Sureing  阅读(1205)  评论(0编辑  收藏  举报