警告:有用的警告即将到来
作为 Kubernetes 的维护者,我们一直在寻找在保持兼容性的同时提高可用性的方法。当我们开发功能、分类错误和回答支持问题时,我们会积累对 Kubernetes 用户有用的信息。过去,分享这些信息仅限于发布说明、公告电子邮件、文档和博客文章等带外方法。除非有人知道要寻找这些信息并设法找到它,否则他们不会从中受益。
在 Kubernetes v1.19 中,我们添加了一项功能,允许 Kubernetes API 服务器向 API 客户端发送警告。警告使用标准的 Warning
响应头发送,因此它不会以任何方式更改状态码或响应主体。这允许服务器轻松地发送任何 API 客户端都可读取的警告,同时保持与以前的客户端版本的兼容性。
警告由 kubectl
v1.19+ 在 stderr
输出中显示,并由 k8s.io/client-go
客户端库 v0.19.0+ 在日志输出中显示。k8s.io/client-go
的行为可以按进程或按客户端覆盖。
弃用警告
我们使用这项新功能的第一种方法是发送有关使用已弃用 API 的警告。
Kubernetes 是一个大型、快速发展的项目。即使对于全职从事该项目的人来说,要跟上每个版本中的更改也可能是一项艰巨的任务。一种重要的更改是 API 弃用。随着 Kubernetes 中的 API 升级到 GA 版本,预发布 API 版本将被弃用并最终删除。
即使有延长的弃用期,并且弃用包含在发布说明中,它们仍然可能难以跟踪。在弃用期间,预发布 API 保持功能正常,允许几个版本过渡到稳定的 API 版本。但是,我们发现用户通常甚至在升级到停止提供它的版本之前,都没有意识到他们依赖于已弃用的 API 版本。
从 v1.19 开始,每当向已弃用的 REST API 发出请求时,都会返回一个警告以及 API 响应。此警告包括有关 API 将不再可用的版本的详细信息以及替换的 API 版本。
由于警告源自服务器并在客户端级别被拦截,因此它适用于所有 kubectl 命令,包括高级命令(如 kubectl apply
)和低级命令(如 kubectl get --raw
)。
这有助于受弃用影响的人员了解他们正在发出的请求已弃用,他们有多长时间来解决问题,以及他们应该使用哪个 API。当用户正在应用他们没有创建的清单时,这尤其有用,因此他们有时间与作者联系以请求更新的版本。
我们还意识到,使用已弃用 API 的人通常不是负责升级集群的同一个人,因此我们添加了两个面向管理员的工具,以帮助跟踪已弃用 API 的使用情况并确定何时升级是安全的。
指标
从 Kubernetes v1.19 开始,当向已弃用的 REST API 端点发出请求时,kube-apiserver 进程中的 apiserver_requested_deprecated_apis
计量指标将设置为 1
。此指标具有 API group
、version
、resource
和 subresource
的标签,以及一个 removed_release
标签,指示 API 将不再提供的 Kubernetes 版本。
这是一个使用 kubectl
、prom2json 和 jq 的示例查询,用于确定已从当前 API 服务器实例请求了哪些已弃用的 API
kubectl get --raw /metrics | prom2json | jq '
.[] | select(.name=="apiserver_requested_deprecated_apis").metrics[].labels
'
输出
{
"group": "extensions",
"removed_release": "1.22",
"resource": "ingresses",
"subresource": "",
"version": "v1beta1"
}
{
"group": "rbac.authorization.k8s.io",
"removed_release": "1.22",
"resource": "clusterroles",
"subresource": "",
"version": "v1beta1"
}
这表明已在此服务器上请求了已弃用的 extensions/v1beta1
Ingress 和 rbac.authorization.k8s.io/v1beta1
ClusterRole API,并且将在 v1.22 中删除。
我们可以将该信息与 apiserver_request_total
指标连接起来,以获取有关对这些 API 发出的请求的更多详细信息
kubectl get --raw /metrics | prom2json | jq '
# set $deprecated to a list of deprecated APIs
[
.[] |
select(.name=="apiserver_requested_deprecated_apis").metrics[].labels |
{group,version,resource}
] as $deprecated
|
# select apiserver_request_total metrics which are deprecated
.[] | select(.name=="apiserver_request_total").metrics[] |
select(.labels | {group,version,resource} as $key | $deprecated | index($key))
'
输出
{
"labels": {
"code": "0",
"component": "apiserver",
"contentType": "application/vnd.kubernetes.protobuf;stream=watch",
"dry_run": "",
"group": "extensions",
"resource": "ingresses",
"scope": "cluster",
"subresource": "",
"verb": "WATCH",
"version": "v1beta1"
},
"value": "21"
}
{
"labels": {
"code": "200",
"component": "apiserver",
"contentType": "application/vnd.kubernetes.protobuf",
"dry_run": "",
"group": "extensions",
"resource": "ingresses",
"scope": "cluster",
"subresource": "",
"verb": "LIST",
"version": "v1beta1"
},
"value": "1"
}
{
"labels": {
"code": "200",
"component": "apiserver",
"contentType": "application/json",
"dry_run": "",
"group": "rbac.authorization.k8s.io",
"resource": "clusterroles",
"scope": "cluster",
"subresource": "",
"verb": "LIST",
"version": "v1beta1"
},
"value": "1"
}
输出显示,仅对这些 API 发出读取请求,并且最多请求已用于监视已弃用的 Ingress API。
您还可以通过以下 Prometheus 查询找到该信息,该查询返回有关对已弃用 API 发出的请求的信息,这些 API 将在 v1.22 中删除
apiserver_requested_deprecated_apis{removed_release="1.22"} * on(group,version,resource,subresource)
group_right() apiserver_request_total
审计注解
指标是检查是否正在使用已弃用 API 以及以什么速率使用的快速方法,但它们不包含足够的信息来识别特定客户端或 API 对象。从 Kubernetes v1.19 开始,对已弃用 API 的请求的审计事件包括 "k8s.io/deprecated":"true"
的审计注解。管理员可以使用这些审计事件来识别需要更新的特定客户端或对象。
自定义资源定义
除了 API 服务器警告已弃用 API 使用情况的功能外,从 v1.19 开始,CustomResourceDefinition 可以指示其定义的资源的特定版本已弃用。当对自定义资源的已弃用版本发出 API 请求时,将返回一条警告消息,该消息与内置 API 的行为匹配。
CustomResourceDefinition 的作者还可以根据需要自定义每个版本的警告。这允许他们根据需要提供指向迁移指南或其他信息的指针。
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
name: crontabs.example.com
spec:
versions:
- name: v1alpha1
# This indicates the v1alpha1 version of the custom resource is deprecated.
# API requests to this version receive a warning in the server response.
deprecated: true
# This overrides the default warning returned to clients making v1alpha1 API requests.
deprecationWarning: "example.com/v1alpha1 CronTab is deprecated; use example.com/v1 CronTab (see http://example.com/v1alpha1-v1)"
...
- name: v1beta1
# This indicates the v1beta1 version of the custom resource is deprecated.
# API requests to this version receive a warning in the server response.
# A default warning message is returned for this version.
deprecated: true
...
- name: v1
...
准入 Webhook
准入 webhook是将自定义策略或验证与 Kubernetes 集成的主要方式。从 v1.19 开始,准入 webhook 可以返回警告消息,这些消息会传递给请求的 API 客户端。警告可以与允许或拒绝的准入响应一起返回。
例如,为了允许一个请求但警告一个已知效果不佳的配置,准入 webhook 可以发送此响应
{
"apiVersion": "admission.k8s.io/v1",
"kind": "AdmissionReview",
"response": {
"uid": "<value from request.uid>",
"allowed": true,
"warnings": [
".spec.memory: requests >1GB do not work on Fridays"
]
}
}
如果您正在实现返回警告消息的 webhook,以下是一些提示
- 不要在消息中包含“Warning:”前缀(该前缀由客户端在输出时添加)
- 使用警告消息来描述发出 API 请求的客户端应纠正或注意的问题
- 简洁;如果可能,将警告限制为 120 个字符
准入 webhook 可以使用此新功能的方式有很多,我很期待看到人们想出什么。以下是一些可以帮助您入门的想法
- webhook 实现添加“抱怨”模式,其中它们返回警告而不是拒绝,以允许在开始强制执行策略之前尝试策略以验证其是否按预期工作
- “lint”或“vet”样式 webhook,检查对象并在未遵循最佳实践时显示警告
自定义客户端处理
使用 k8s.io/client-go
库发出 API 请求的应用程序可以自定义如何处理从服务器返回的警告。默认情况下,警告会在收到时记录到 stderr,但此行为可以按进程或按客户端自定义。
此示例显示如何使您的应用程序的行为类似于 kubectl
,覆盖整个进程的消息处理以删除重复的警告并在支持的情况下使用彩色输出突出显示消息
import (
"os"
"k8s.io/client-go/rest"
"k8s.io/kubectl/pkg/util/term"
...
)
func main() {
rest.SetDefaultWarningHandler(
rest.NewWarningWriter(os.Stderr, rest.WarningWriterOptions{
// only print a given warning the first time we receive it
Deduplicate: true,
// highlight the output with color when the output supports it
Color: term.AllowsColorOutput(os.Stderr),
},
),
)
...
下一个示例显示如何构建忽略警告的客户端。这对于在所有资源类型的元数据上运行(使用发现 API 在运行时动态找到)并且不会从有关特定资源被弃用的警告中受益的客户端非常有用。对于需要使用特定 API 的客户端,不建议禁止显示弃用警告。
import (
"k8s.io/client-go/rest"
"k8s.io/client-go/kubernetes"
)
func getClientWithoutWarnings(config *rest.Config) (kubernetes.Interface, error) {
// copy to avoid mutating the passed-in config
config = rest.CopyConfig(config)
// set the warning handler for this client to ignore warnings
config.WarningHandler = rest.NoWarnings{}
// construct and return the client
return kubernetes.NewForConfig(config)
}
Kubectl 严格模式
如果您想确保您尽快注意到弃用并快速开始处理它们,则 kubectl
在 v1.19 中添加了一个 --warnings-as-errors
选项。当使用此选项调用时,kubectl
会将从服务器收到的任何警告都视为错误,并以非零退出代码退出
这可以在 CI 作业中使用,以将清单应用于当前服务器,并且需要以零退出代码通过才能使 CI 作业成功。
未来的可能性
既然我们有了一种在上下文中向用户传达有用信息的方法,我们已经开始考虑其他方法,我们可以使用这种方法来改善人们使用 Kubernetes 的体验。我们接下来关注的几个领域是警告我们出于兼容性原因无法直接拒绝的已知有问题的值,以及警告使用已弃用的字段或字段值(如使用 beta os/arch 节点标签的选择器,在 v1.14 中已弃用)。我很高兴看到这方面的进展,继续使 Kubernetes 更易于使用。