Kubernetes v1.36:陈旧性缓解与控制器的可观测性
Source: Kubernetes Blog
Kubernetes 控制器中的陈旧性
Kubernetes 控制器中的陈旧性是一个影响许多控制器的问题,可能以微妙的方式影响控制器的行为。
通常要等到为时已晚才被发现——当生产环境中的控制器已经采取了错误的操作时——因为控制器作者所做的一个基本假设被证明是错误的。
陈旧性导致的典型问题包括:
- 控制器采取 错误的操作。
- 控制器在应该采取行动时 未采取行动。
- 控制器 响应过慢。
我很高兴地宣布 Kubernetes v1.36 包含了帮助缓解控制器陈旧性并提供更好可观测性的全新特性。
什么是陈旧性?
陈旧性在控制器中来自于控制器缓存中对世界的过时视图。为了提供快速的用户体验,控制器通常维护一个本地缓存,存放集群状态。该缓存通过监听 Kubernetes API 服务器上控制器关心的对象的变化来填充。当控制器需要采取行动时,它首先检查缓存中的最新信息;如果缓存已过期,控制器会通过监听 API 服务器来更新缓存。此过程称为调和。
缓存何时会变得过时?
- 控制器重启 – 必须从头重建缓存,期间会有缓存陈旧的窗口。
- API‑server 宕机 – 缓存无法刷新,控制器只能基于陈旧数据运行。
- 其他边缘情况 – 如网络分区、Informer 延迟或大量事件突发。
在上述任何期间,控制器可能无法正确行动。
Source: …
v1.36 版本的改进
Kubernetes v1.36 在 client‑go 和 kube‑controller‑manager 中高度竞争的控制器实现上都带来了增强,利用了 client‑go 的新功能。
client‑go 改进
-
原子 FIFO 处理(特性开关
AtomicFIFO)——基于现有的 FIFO 队列实现。- 队列现在能够原子地处理批量收到的操作(例如 informer 用来填充缓存的对象初始列表)。
- 即使事件顺序错乱,也能保证队列保持一致状态。
-
缓存自省——在
Store接口上新增的方法:
// LastStoreSyncResourceVersion returns the latest resource version that the store has observed.
func (s *Store) LastStoreSyncResourceVersion() string
- 控制器可以使用该方法获取缓存最近观察到的资源版本,从而为
kube‑controller‑manager中的防陈旧特性提供依据。
kube‑controller‑manager 改进
v1.36 发行版默认让 四个 控制器使用新能力:
| 控制器 | 特性开关 |
|---|---|
| DaemonSet | StaleControllerConsistencyDaemonSet |
| StatefulSet | StaleControllerConsistencyStatefulSet |
| ReplicaSet | StaleControllerConsistencyReplicaSet |
| Job | StaleControllerConsistencyJob |
- 通过将相应的特性开关设为
false可以关闭该功能。 - 当特性开关开启时,控制器会在执行任何操作前先检查缓存的 最新资源版本。
- 如果缓存的版本 低于 控制器已经写入 API Server 的对象版本,则控制器 不执行操作——因为它的视图已经过时。
对 Informer 作者的使用建议
Informer 作者可以立即受益于这些改进。下面示例展示了 ReplicaSet informer 如何使用该新特性。
type ConsistencyStore interface {
// WroteAt records that the given object was written at the given resource version.
WroteAt(owningObj runtime.Object, uid types.UID,
groupResource schema.GroupResource, resourceVersion string)
// EnsureReady returns true if the cache is up‑to‑date for the given object.
// It is used prior to taking any reconciliation action.
EnsureReady(owningObj runtime.Object, uid types.UID,
groupResource schema.GroupResource) bool
}
ReplicaSet控制器会同时跟踪 ReplicaSet 本身的资源版本 以及 它管理的 Pod 的资源版本。- 对于特定的 ReplicaSet,控制器记录其 Pod 的最新写入资源版本以及对 ReplicaSet 本身的任何写入。
- 如果缓存的最新版本低于控制器已经写入的版本,控制器会因为视图陈旧而不进行操作。
Informer 作者可以利用 ConsistencyStore 来:
- 记录写入(
WroteAt)——在写入发生时记录。 - 检查新鲜度(
EnsureReady)——在处理事件前进行检查。
这种模式确保控制器仅在信息是最新的情况下才执行操作,从而降低因状态陈旧导致的 bug 风险。
一致性存储接口
type ConsistencyStore interface {
// WroteAt records the latest resource version that the controller has written
// to the API server for a given object.
// * `owningObj` – the object being reconciled.
// * `uid` – UID of the owning object.
// * `resourceVersion` – the resource version just written.
// * `groupResource` – the GroupResource of the object.
WroteAt(owningObj client.Object, uid types.UID,
resourceVersion string, groupResource schema.GroupResource)
// EnsureReady checks whether the cache is up‑to‑date for the given object.
// It is called before reconciliation to decide whether to proceed.
// Returns `true` if the cache is current, otherwise `false`.
EnsureReady(namespacedName types.NamespacedName) bool
// Clear removes the given object from the consistency store.
// It is used when an object is deleted.
Clear(namespacedName types.NamespacedName, uid types.UID)
}
函数说明
| 函数 | 目的 | 细节 |
|---|---|---|
| WroteAt | 记录控制器写入的最新资源版本。 | - 在控制器向 API Server 写入对象后调用。 - 跟踪 owningObj、其 uid、写入的 resourceVersion 以及对象的 GroupResource。- 只存储版本信息,不保留对象本身。 |
| EnsureReady | 在进行调和前验证缓存是否是最新的。 | - 在调和之前调用。 - 如果缓存反映了最新版本(由 WroteAt 记录),返回 true,否则返回 false。 |
| Clear | 当对象被删除时从存储中移除对应条目。 | - 防止存储无限增长。 - 使用对象的 UID 区分已删除的对象和同名的新创建对象。- EnsureReady 并不依赖此函数,只关心最新的版本信息。 |
有了这三个函数,Informer 编写者即可在其控制器中实现 陈旧性缓解。
可观测性
Kubernetes 在 v1.36 中为 kube‑controller‑manager 添加了相关的仪表化。
这些指标默认启用,并通过相同的功能门控进行控制。
指标
| 指标 | 描述 |
|---|---|
stale_sync_skips_total | 控制器因缓存陈旧而跳过同步的次数。对使用陈旧缓解特性的每个控制器在其子系统下进行暴露。 |
store_resource_version | 暴露每个共享 informer 的最新资源版本。 标签: group, version, resource。允许您将 informer 缓存版本与 API 服务器的版本进行比较,以检测陈旧性。 |
这些指标可通过 kube‑controller‑manager 指标端点获取。
接下来是什么?
- SIG API Machinery 将继续发展此功能,并在更多控制器中推广其采用。
- 欢迎提供反馈——请在下方评论或在 Kubernetes GitHub repository 上打开 issue。
- 正在与 controller‑runtime 合作,将这些语义暴露给所有使用它构建的控制器,使每个控制器都具备“read‑your‑own‑writes” 能力,而无需额外的实现工作。