著者:ワイントースト(王思雨)
クラウドネイティブアプリケーション自動化管理スイート、CNCFサンドボックスプロジェクト-OpenKruise、最近リリースされたv1.1バージョン。
OpenKruise [1] は、Kubernetes向けの拡張機能スイートであり、クラウドネイティブアプリケーションのデプロイ、アップグレード、運用と保守、安定性保護、およびその他の分野に焦点を当てています。すべての機能はCRDなどの標準的な方法で拡張され、バージョン1.16以降の任意のKubernetesクラスターに適用できます。Kruiseのワンクリック展開は、1つのhelmコマンドで実行でき、それ以上の構成は必要ありません。******
バージョン解決
v1.1バージョンでは、OpenKruiseは多くの既存の機能を拡張および拡張し、大規模クラスターでの実行パフォーマンスを最適化しました。以下は、v1.1のいくつかの機能の簡単な紹介です。
OpenKruise v1.1がKubernetesコード依存関係バージョンをv1.22にアップグレードしたことは注目に値します。つまり、ユーザーはCloneSetなどのワークロードのポッドテンプレートテンプレートでv1.22までの新しいフィールドを使用できます。ユーザーがインストールするOpenKruiseと互換性のあるKubernetesクラスターバージョンは>=v1.16のままです。
インプレースアップグレードは、コンテナ注文の優先順位をサポートします
昨年末にリリースされたv1.0バージョンでは、OpenKruiseはコンテナ起動シーケンス制御機能[2]を導入しました。これは、ポッド内の複数のコンテナの異なる重量関係の定義をサポートし、重量に応じて異なるコンテナの起動を制御します。ポッドが作成されたとき。注文。
v1.0では、この機能は各ポッドの作成フェーズでのみ機能します。作成が完了した後、ポッド内の複数のコンテナーが所定の場所でアップグレードされると、これらのコンテナーは同時にアップグレードされます。
最近、コミュニティはLinkedInなどの企業といくつかのやり取りを行い、ユーザーシナリオからより多くの情報を得ています。一部のシナリオでは、ポッド内の複数のコンテナーが関連付けられます。たとえば、ビジネスコンテナーがアップグレードされると、ポッド内の他のコンテナーもこの新しいバージョンに関連付けられるように構成をアップグレードする必要があります。または、複数のコンテナーが並列アップグレードを回避して、 logコレクションクラスのサイドカーコンテナは、ビジネスコンテナのログを失うことはありません。
したがって、v1.1では、OpenKruiseはコンテナーの優先順位に従ってインプレースアップグレードをサポートします。実際の使用では、ユーザーは追加のパラメーターを構成する必要はありません。ポッドがコンテナーの起動優先度で作成されている限り、ポッドの作成段階だけでなく、優先度の高いコンテナーが優先度の低いコンテナーよりも先に開始されることが保証されます。 ;単一のインプレースアップグレードでは、複数のコンテナが同時にアップグレードされる場合、優先度の高いコンテナが最初にアップグレードされ、次に、アップグレードと起動が完了した後に優先度の低いコンテナがアップグレードされます。
ここでのインプレースアップグレードには、イメージイメージのアップグレードの変更と、メタデータからのenvの環境変数のアップグレードの変更が含まれます。詳細については、インプレースアップグレードの概要[3]を参照してください。概要:
-
コンテナの起動順序がないポッドの場合、マルチコンテナのインプレースアップグレード中の順序は保証されません。
-
コンテナの起動シーケンスがあるポッドの場合:
-
今回アップグレードする複数のコンテナの起動順序が異なる場合、インプレースアップグレードの順序は起動順序に従って制御されます。
-
ローカルでインプレースでアップグレードされる複数のコンテナーの起動順序が同じである場合、インプレースアップグレード中の順序は保証されません。
たとえば、起動順序が異なる2つのコンテナを含む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アノテーションとメインコンテナのイメージを変更すると、サイドカーとメインコンテナの両方を更新する必要があります。Kruiseはポッドをアップグレードして、サイドカーコンテナを再構築して有効にします。アノテーションからの新しい環境。
次に、apps.kruise.io/inplace-update-stateアノテーションとアップグレードされたポッドでのその値を確認できます。
{
"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 了。
StatefulSetAutoDeletePVC 功能
从 Kubernetes v1.23 开始,原生的 StatefulSet 加入了 StatefulSetAutoDeletePVC 功能,即根据给定策略来选择保留或自动删除 StatefulSet 创建的 PVC 对象,参考文档 [4] 。
因此,v1.1 版本的 Advanced StatefulSet 从上游同步了这个功能,允许用户通过 .spec.persistentVolumeClaimRetentionPolicy 字段来指定这个自动清理策略。这需要你在安装或升级 Kruise 的时候,启用 StatefulSetAutoDeletePVC feature-gate 功能。
apiVersion: apps.kruise.io/v1beta1
kind: StatefulSet
spec:
...
persistentVolumeClaimRetentionPolicy: # optional
whenDeleted: Retain | Delete
whenScaled: Retain | Delete
复制代码
其中,两个策略字段包括:
- whenDeleted:当 Advanced StatefulSet 被删除时,对 PVC 的保留/删除策略。
- whenScaled:当 Advanced StatefulSet 发生缩容时,对缩容 Pod 关联 PVC 的保留/删除策略。
每个策略都可以配置以下两种值:
- Retain(默认值):它的行为与过去 StatefulSet 一样,在 Pod 删除时对它关联的 PVC 做保留。
- Delete:当 Pod 删除时,自动删除它所关联的 PVC 对象。
除此之外,还有几个注意点:
- StatefulSetAutoDeletePVC 功能只会清理由 volumeClaimTemplate 中定义和创建的 PVC,而不会清理用户自己创建或关联到 StatefulSet Pod 中的 PVC。
- 上述清理只发生在 Advanced StatefulSet 被删除或主动缩容的情况下。例如 node 故障导致的 Pod 驱逐重建等,仍然会复用已有的 PVC。
Advanced DaemonSet 重构并支持生命周期钩子
早先版本的 Advanced DaemonSet 实现与上游控制器差异较大,例如对于 not-ready 和 unschedulable 的节点需要额外配置字段来选择是否处理,这对于我们的用户来说都增加了使用成本和负担。
在 v1.1 版本中,我们对 Advanced DaemonSet 做了一次小重构,将它与上游控制器重新做了对齐。因此,Advanced DaemonSet 的所有默认行为会与原生 DaemonSet 基本一致,用户可以像使用 Advanced StatefulSet 一样,通过修改 apiVersion 就能很方便地将一个原生 DaemonSet 修改为 Advanced DaemonSet 来使用。
另外,我们还为 Advanced DaemonSet 增加了生命周期钩子,首先支持 preDelete hook,来允许用户在 daemon Pod 被删除前执行一些自定义的逻辑。
apiVersion: apps.kruise.io/v1alpha1
kind: DaemonSet
spec:
...
# define with label
lifecycle:
preDelete:
labelsHandler:
example.io/block-deleting: "true"
复制代码
当 DaemonSet 删除一个 Pod 时(包括缩容和重建升级):
- 如果没有定义 lifecycle hook 或者 Pod 不符合 preDelete 条件,则直接删除。
- 否则,会先将 Pod 更新为 PreparingDelete 状态,并等待用户自定义的 controller 将 Pod 中关联的 label/finalizer 去除,再执行 Pod 删除。
Disable DeepCopy 性能优化
默认情况下,我们在使用 controller-runtime 来编写 Operator/Controller 时, 使用其中 sigs.k8s.io/controller-runtime/pkg/client Client 客户端来 get/list 查询对象(typed),都是从内存 Informer 中获取并返回,这是大部分人都知道的。
但很多人不知道的是,在这些 get/list 操作背后,controller-runtime 会将从 Informer 中查到的所有对象做一次 deep copy 深拷贝后再返回。
这个设计的初衷,是避免开发者错误地将 Informer 中的对象直接篡改。在深拷贝之后,无论开发者对 get/list 返回的对象做了任何修改,都不会影响到 Informer 中的对象,后者只会从 kube-apiserver 的 ListWatch 请求中同步。
但是在一些很大规模的集群中,OpenKruise 中各个控制器同时在运行,同时每个控制器还存在多个 worker 执行 Reconcile,可能会带来大量的 deep copy 操作。例如集群中有大量应用的 CloneSet,而其中一些 CloneSet 下管理的 Pod 数量非常多,则每个 worker 在 Reconcile 的时候都会 list 查询一个 CloneSet 下的所有 Pod 对象,再加上多个 worker 并行操作, 可能造成 kruise-manager 瞬时的 CPU 和 Memory 压力陡增,甚至在内存配额不足的情况下有发生 OOM 的风险。
在上游的 controller-runtime 中,我在去年已经提交合并了 DisableDeepCopy 功能 [5] ,包含在 controller-runtime v0.10 及以上的版本。它允许开发者指定某些特定的资源类型,在做 get/list 查询时不执行深拷贝,而是直接返回 Informer 中的对象指针。
例如下述代码,在 main.go 中初始化 Manager 时,为 cache 加入参数即可配置 Pod 等资源类型不做深拷贝。
mgr, err := ctrl.NewManager(cfg, ctrl.Options{
...
NewCache: cache.BuilderWithOptions(cache.Options{
UnsafeDisableDeepCopyByObject: map[client.Object]bool{
&v1.Pod{}: true,
},
}),
})
复制代码
但在 Kruise v1.1 版本中,我们没有选择直接使用这个功能,而是将 Delegating Client [6] 重新做了封装, 从而使得开发者可以在任意做 list 查询的地方通过 DisableDeepCopy ListOption 来指定单次的 list 操作不做深拷贝。
if err := r.List(context.TODO(), &podList, client.InNamespace("default"), utilclient.DisableDeepCopy); err != nil {
return nil, nil, err
}
复制代码
这样做的好处是使用上更加灵活,避免为整个资源类型关闭深拷贝后,众多社区贡献者在参与开发的过程中如果没有注意到则可能会错误修改 Informer 中的对象。
其他改动
你可以通过 Github release [7] 页面,来查看更多的改动以及它们的作者与提交记录。
社区参与
非常欢迎你通过 Github/Slack/钉钉/微信 等方式加入我们来参与 OpenKruise 开源社区。你是否已经有一些希望与我们社区交流的内容呢?可以在我们的社区双周会 [8] 上分享你的声音,或通过以下渠道参与讨论:
- 加入社区 Slack channel [9] (English)
- 加入社区钉钉群:搜索群号 23330762 (Chinese)
- 加入社区微信群(新):添加用户 openkruise 并让机器人拉你入群 (Chinese)
相关链接* *
[1]OpenKruise
[2]容器启动顺序控制
https://openkruise.io/zh/docs/user-manuals/containerlaunchpriority/
[3]原地升级介绍
https://openkruise.io/zh/docs/core-concepts/inplace-update
[4]参考文档
[5]DisableDeepCopy 功能
https://github.com/kubernetes-sigs/controller-runtime/pull/1274
[6]Delegating Client
https://github.com/openkruise/kruise/blob/master/pkg/util/client/delegating_client.go
[7]Github release
https://github.com/openkruise/kruise/releases
[8]社区双周会
https://shimo.im/docs/gXqmeQOYBehZ4vqo
[9]Slack channel
https://kubernetes.slack.com/?redir=%2Farchives%2Fopenkruise
点击此处,查看 OpenKruise 项目官方主页与文档!