警告:前方有用的警告
作为 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 的版本才意识到他们依赖于废弃的 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 查询找到该信息,该查询返回有关对将在 v1.22 中删除的废弃 API 发出的请求的信息
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"`。管理员可以使用这些审计事件来识别需要更新的特定客户端或对象。
自定义资源定义(Custom Resource Definitions)
除了 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 更易于使用。