向 Client-Go 引入特性门控:增强灵活性和控制力

Kubernetes 组件使用称为“特性门控(Feature Gates)”的开关来管理添加新特性的风险, 特性门控机制使特性能够通过 Alpha、Beta 和 GA 阶段逐步升级。

Kubernetes 组件(例如 kube-controller-manager 和 kube-scheduler)使用 client-go 库与 API 交互, 整个 Kubernetes 生态系统使用相同的库来构建控制器、工具、webhook 等。 client-go 现在包含自己的特性门控机制,使开发人员和集群管理员能够更好地控制如何使用客户端特性。

要了解有关 Kubernetes 中特性门控的更多信息,请参阅特性门控

动机

在没有 client-go 特性门控的情况下,每个新特性都以自己的方式(如果有的话)将特性可用性与特性的启用分开。 某些特性可通过更新到较新版本的 client-go 来启用,其他特性则需要在每个使用它们的程序中进行主动配置, 其中一些可在运行时使用环境变量进行配置。使用 kube-apiserver 公开的特性门控功能时,有时需要客户端回退机制, 以保持与由于版本新旧或配置不同而不支持该特性服务器的兼容性。 如果在这些回退机制中发现问题,则缓解措施需要更新到 client-go 的固定版本或回滚。

这些方法都无法很好地支持为某些(但不是全部)使用 client-go 的程序默认启用特性。 默认设置的更改不会首先仅为单个组件启用新特性,而是会立即影响所有 Kubernetes 组件的默认设置,从而大大扩大影响半径。

client-go 中的特性门控

为了应对这些挑战,大量的 client-go 特性将使用新的特性门控机制来逐步引入。 这一机制将允许开发人员和用户以类似 Kubernetes 组件特性门控的管理方式启用或禁用特性。

作为一种开箱即用的能力,用户只需使用最新版本的 client-go。这种设计带来多种好处。

对于使用通过 client-go 构建的软件的用户:

  • 早期采用者可以针对各个进程分别启用默认关闭的 client-go 特性。
  • 无需构建新的二进制文件即可禁用行为不当的特性。
  • 所有已知的 client-go 特性门控的状态都会被记录到日志中,允许用户检查。

对于开发使用 client-go 构建的软件的人员:

  • 默认情况下,client-go 特性门控覆盖是从环境变量中读取的。 如果在 client-go 特性中发现错误,用户将能够禁用它,而无需等待新版本发布。
  • 开发人员可以替换程序中基于默认环境变量的覆盖值以更改默认值、从其他源读取覆盖值或完全禁用运行时覆盖值。 Kubernetes 组件使用这种可定制性将 client-go 特性门控与现有的 --feature-gates 命令行标志、特性启用指标和日志记录集成在一起。

覆盖 client-go 特性门控

注意:这描述了在运行时覆盖 client-go 特性门控的默认方法,它可以由特定程序的开发人员禁用或自定义。 在 Kubernetes 组件中,client-go 特性门控覆盖由 --feature-gates 标志控制。

可以通过设置以 KUBE_FEATURE 为前缀的环境变量来启用或禁用 client-go 的特性。 例如,要启用名为 MyFeature 的特性,请按如下方式设置环境变量:

KUBE_FEATURE_MyFeature=true

要禁用特性,可将环境变量设置为 false

KUBE_FEATURE_MyFeature=false

注意:在某些操作系统上,环境变量区分大小写。 因此,KUBE_FEATURE_MyFeatureKUBE_FEATURE_MYFEATURE 将被视为两个不同的变量。

自定义 client-go 特性门控

基于环境变量的默认特性门控覆盖机制足以满足 Kubernetes 生态系统中许多程序的需求,无需特殊集成。 需要不同行为的程序可以用自己的自定义特性门控提供程序替换它。 这允许程序执行诸如强制禁用已知运行不良的特性、直接从远程配置服务读取特性门控或通过命令行选项接受特性门控覆盖等操作。

Kubernetes 组件将 client-go 的默认特性门控提供程序替换为现有 Kubernetes 特性门控提供程序的转换层。 在所有实际应用场合中,client-go 特性门控与其他 Kubernetes 特性门控的处理方式相同: 它们连接到 --feature-gates 命令行标志,包含在特性启用指标中,并在启动时记录。

要替换默认的特性门控提供程序,请实现 Gates 接口并在包初始化时调用 ReplaceFeatureGates,如以下简单示例所示:

import (
 "k8s.io/client-go/features"
)

type AlwaysEnabledGates struct{}

func (AlwaysEnabledGates) Enabled(features.Feature) bool {
 return true
}

func init() {
 features.ReplaceFeatureGates(AlwaysEnabledGates{})
}

需要定义的 client-go 特性完整列表的实现可以通过实现 Registry 接口并调用 AddFeaturesToExistingFeatureGates 来获取它。 完整示例请参考 Kubernetes 内部使用

总结

随着 client-go v1.30 中特性门控的引入,推出新的 client-go 特性变得更加安全、简单。 用户和开发人员可以控制自己采用 client-go 特性的步伐。 通过为跨 Kubernetes API 边界两侧的特性提供一种通用的培育机制,Kubernetes 贡献者的工作得到了简化。

特别感谢 @sttts@deads2k 对此特性提供的帮助。