警告:提前有用的警告

作者 : (谷歌)

作为Kubernetes的维护者,我们一直在寻找在保持兼容性的同时提高可用性的方法。 在开发功能,分类错误和回答支持问题时,我们会积累有助于Kubernetes用户了解的信息。 过去,共享信息仅限于带外方法,例如发行说明,公告电子邮件,文档和博客文章。 除非有人知道寻找该信息并设法找到它,否则他们将不会从中受益。

在Kubernetes v1.19中,我们添加了一项功能,该功能允许Kubernetes API服务器执行以下操作: 向API客户端发送警告. 该警告使用 standard Warning response header, 因此它不会以任何方式更改状态代码或响应正文。 这使服务器可以发送任何API客户端都易于读取的警告,同时保持与先前客户端版本的兼容性。

Warnings are surfaced by kubectl v1.19+ in stderr output, 和 by the k8s.io/client-go client library v0.19.0+ in log output. The k8s.io/client-go behavior can be 覆盖每个进程或每个客户端.

弃用警告

我们使用这项新功能的第一种方法是发送警告,告知使用已弃用的API。

Kubernetes 是 大型,快速发展的项目. Keeping up with the 变化 即使对于专职从事该项目的人员而言,每个发行版中的代码也可能令人生畏。 API弃用是一种重要的更改类型。 随着Kubernetes中的API逐渐升级到GA版本,预发布的API版本已被弃用,并最终被删除。

即使有 延长折旧期限, and deprecations are 包含在发行说明中, 他们仍然很难追踪。在弃用期间,预发布API仍可正常运行, 允许多个版本过渡到稳定的API版本。但是,我们发现用户通常甚至没有意识到 它们取决于已弃用的API版本,直到它们升级到停止为其提供服务的版本为止。

从v1.19开始,每当对不赞成使用的REST API发出请求时,都会随API响应一起返回警告。 此警告包括有关该API不再可用的发行版本以及替换API版本的详细信息。

由于该警告起源于服务器,并且在客户端级别被拦截,因此该警告适用于所有kubectl命令, including high-level commands like kubectl apply, 和 low-level commands like kubectl get --raw:

kubectl应用清单文件,然后显示警告消息'networking.k8s.io/v1beta1在v1.19 +中不推荐使用Ingress,在v1.22 +中不可用;使用network.k8s.io/v1入口”。

这有助于受弃用影响的人们知道他们提出的请求已被弃用, 他们必须解决问题多长时间,以及应该使用什么API。 当用户应用未创建的清单时,这特别有用。 因此他们有时间与作者联系,索取更新版本。

我们也意识到那个人 使用 弃用的API通常不是负责升级群集的同一个人, 因此我们添加了两个面向管理员的工具,以帮助跟踪已弃用的API的使用并确定何时安全升级。

指标

从Kubernetes v1.19开始,当对不赞成使用的REST API端点发出请求时, an apiserver_requested_deprecated_apis gauge metric is set to 1 in the kube-apiserver process. This metric has labels for the API group, version, resource, 和 subresource, and a removed_version label that indicates the Kubernetes release in which the API will no longer be served.

This is an example query 使用 kubectl, prom2json , and q 确定已请求过时的API 从API服务器的当前实例开始:

kubectl get --raw /metrics |  prom2json  |  q  '
  .[] | 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"
}

This shows the deprecated extensions/v1beta1 Ingress 和 rbac.authorization.k8s.io/v1beta1 ClusterRole APIs 已在此服务器上被请求,并将在v1.22中删除。

We can join that information with the apiserver_request_total metrics to get more details about the requests being made to these APIs:

kubectl get --raw /metrics |  prom2json  |  q  '
  # 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的请求的信息,这些信息将在v1.22中删除:

apiserver_requested_deprecated_apis{removed_version="1.22"} * on(group,version,resource,subresource)
group_right() apiserver_request_total

审核注释

指标是检查是否使用过时的API以及使用速率的一种快速方法, 但它们没有包含足够的信息来标识特定的客户端或API对象。 从Kubernetes v1.19开始, 审计事件 for requests to deprecated APIs include an audit annotation of "k8s.io/deprecated":"true". 管理员可以使用这些审核事件来识别需要更新的特定客户端或对象。

自定义资源定义

从v1.19开始,API服务器还具有警告已弃用API的功能,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
    ...

入学网钩

入学网钩 是将自定义策略或验证与Kubernetes集成的主要方法。 从v1.19开始,准入网钩可以 返回警告消息 传递给请求的API客户端。可以使用允许或拒绝的入站响应返回警告。

例如,为了允许请求但警告已知的配置不起作用,准入网络挂钩可以发送以下响应:

{
  "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,请参考以下提示:

  • 消息中不要包含“警告:”前缀(由客户端在输出中添加)
  • 使用警告消息来描述发出API请求的客户端应更正或意识到的问题
  • 简洁的;尽可能将警告限制为120个字符

入学Webhooks可以通过多种方式使用此新功能,我期待看到人们的想法。 这里有一些想法可以帮助您入门:

  • webhook实施添加了“投诉”模式,该模式返回警告而不是拒绝, 允许尝试策略以在开始执行之前验证其是否按预期工作
  • “棉绒”或“兽医”式的网钩,在不遵循最佳实践的情况下检查对象并显示警告

自定义客户端处理

Applications that use the k8s.io/client-go library to make API requests can customize 从服务器返回的警告如何处理。默认情况下,警告记录到 标准错误,但可以自定义此行为 每过程 要么 每个客户 .

This example shows how to make your application behave like 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  和  return the client
  return kubernetes.NewForConfig(config)
}

Kubectl严格模式

如果您希望确保尽快注意到弃用情况,并迅速着手解决这些问题, kubectl added a --warnings-as-errors option in v1.19. When invoked with this option, kubectl 将从服务器收到的所有警告都视为错误,并以非零退出代码退出:

kubectl应用带有--warnings-as-errors标志的清单文件,显示警告消息并以非零退出代码退出。

可以在CI作业中使用它来将清单应用于当前服务器, 并且要求传递零退出代码以使CI作业成功。

未来的可能性

现在,我们有了一种在上下文中向用户传达有用信息的方法, 我们已经在考虑其他方法来使用它来改善人们对Kubernetes的体验。 我们接下来要注意的几个方面警告 已知问题值 我们出于兼容性原因不能完全拒绝,也不能警告您使用不赞成使用的字段或字段值 (例如使用beta os / arch节点标签的选择器, v1.14中已弃用 )。 我很高兴看到这一领域的进展,并继续使使用Kubernetes更容易。


乔丹·利吉特(Jordan Liggitt) 是Google的软件工程师,并帮助领导Kubernetes的身份验证,授权和API工作。