这是本节的多页打印视图。 点击此处打印.

返回本页常规视图.

参考

这是 Kubernetes 文档的参考部分。

API 参考

官方支持的客户端库

如果你需要通过编程语言调用 Kubernetes API,你可以使用客户端库。 以下是官方支持的客户端库:

CLI

  • kubectl —— 主要的 CLI 工具,用于运行命令和管理 Kubernetes 集群。
  • kubeadm - 此 CLI 工具可轻松配置安全的 Kubernetes 集群。

组件

  • kubelet —— 在每个节点上运行的主代理。kubelet 接收一组 PodSpec 并确保其所描述的容器健康地运行。

  • kube-apiserver —— REST API,用于验证和配置 API 对象(如 Pod、服务或副本控制器等)的数据。

  • kube-controller-manager —— 一个守护进程,其中包含 Kubernetes 所附带的核心控制回路。

  • kube-proxy —— 可进行简单的 TCP/UDP 流转发或针对一组后端执行轮流 TCP/UDP 转发。

  • kube-scheduler —— 一个调度程序,用于管理可用性、性能和容量。

配置 API

本节包含用于配置 kubernetes 组件或工具的 "未发布" API 的文档。 尽管这些 API 对于用户或操作者使用或管理集群来说是必不可少的, 它们大都没有以 RESTful 的方式在 API 服务器上公开。

kubeadm 的配置 API

设计文档

Kubernetes 功能的设计文档归档,不妨考虑从 Kubernetes 架构Kubernetes 设计概述开始阅读。

1 - 词汇表

2 - API 概述

本文提供了 Kubernetes API 的参考信息。

REST API 是 Kubernetes 的基本结构。 所有操作和组件之间的通信及外部用户命令都是调用 API 服务器处理的 REST API。 因此,Kubernetes 平台视一切皆为 API 对象, 且它们在 API 中有相应的定义。

Kubernetes API 参考 列出了 Kubernetes v1.25 版本的 API。

如需了解一般背景信息,请查阅 Kubernetes APIKubernetes API 控制访问描述了客户端如何向 Kubernetes API 服务器进行身份认证以及他们的请求如何被鉴权。

API 版本控制

JSON 和 Protobuf 序列化模式遵循相同的模式更改原则。 以下描述涵盖了这两种格式。

API 版本控制和软件版本控制是间接相关的。 API 和发布版本控制提案描述了 API 版本控制和软件版本控制间的关系。

不同的 API 版本代表着不同的稳定性和支持级别。 你可以在 API 变更文档 中查看到更多的不同级别的判定标准。

下面是每个级别的摘要:

  • Alpha:
    • 版本名称包含 alpha(例如:v1alpha1)。
    • 内置的 Alpha API 版本默认被禁用且必须在 kube-apiserver 配置中显式启用才能使用。
    • 软件可能会有 Bug。启用某个特性可能会暴露出 Bug。
    • 对某个 Alpha API 特性的支持可能会随时被删除,恕不另行通知。
    • API 可能在以后的软件版本中以不兼容的方式更改,恕不另行通知。
    • 由于缺陷风险增加和缺乏长期支持,建议该软件仅用于短期测试集群。
  • Beta:

    • 版本名称包含 beta(例如:v2beta3)。
    • 内置的 Beta API 版本默认被禁用且必须在 kube-apiserver 配置中显式启用才能使用 (例外情况是 Kubernetes 1.22 之前引入的 Beta 版本的 API,这些 API 默认被启用)。
    • 内置 Beta API 版本从引入到弃用的最长生命周期为 9 个月或 3 个次要版本(以较长者为准), 从弃用到移除的最长生命周期为 9 个月或 3 个次要版本(以较长者为准)。
    • 软件被很好的测试过。启用某个特性被认为是安全的。
    • 尽管一些特性会发生细节上的变化,但它们将会被长期支持。
    • 在随后的 Beta 版或 Stable 版中,对象的模式和(或)语义可能以不兼容的方式改变。 当这种情况发生时,将提供迁移说明。 适配后续的 Beta 或 Stable API 版本可能需要编辑或重新创建 API 对象,这可能并不简单。 对于依赖此功能的应用程序,可能需要停机迁移。
    • 该版本的软件不建议生产使用。 后续发布版本可能会有不兼容的变动。 一旦 Beta API 版本被弃用且不再提供服务, 则使用 Beta API 版本的用户需要转为使用后续的 Beta 或 Stable API 版本。
  • Stable:
    • 版本名称如 vX,其中 X 为整数。
    • 特性的 Stable 版本会出现在后续很多版本的发布软件中。 Stable API 版本仍然适用于 Kubernetes 主要版本范围内的所有后续发布, 并且 Kubernetes 的主要版本当前没有移除 Stable API 的修订计划。

API 组

API 组能够简化对 Kubernetes API 的扩展。API 组信息出现在 REST 路径中,也出现在序列化对象的 apiVersion 字段中。

以下是 Kubernetes 中的几个组:

  • 核心(core)(也被称为 legacy)组的 REST 路径为 /api/v1。 核心组并不作为 apiVersion 字段的一部分,例如, apiVersion: v1
  • 指定的组位于 REST 路径 /apis/$GROUP_NAME/$VERSION, 并且使用 apiVersion: $GROUP_NAME/$VERSION (例如,apiVersion: batch/v1)。 你可以在 Kubernetes API 参考文档 中查看全部的 API 组。

启用或禁用 API 组

资源和 API 组是在默认情况下被启用的。 你可以通过在 API 服务器上设置 --runtime-config 参数来启用或禁用它们。 --runtime-config 参数接受逗号分隔的 <key>[=<value>] 对, 来描述 API 服务器的运行时配置。如果省略了 =<value> 部分,那么视其指定为 =true。 例如:

  • 禁用 batch/v1,对应参数设置 --runtime-config=batch/v1=false
  • 启用 batch/v2alpha1,对应参数设置 --runtime-config=batch/v2alpha1
  • 要启用特定版本的 API,如 storage.k8s.io/v1beta1/csistoragecapacities,可以设置 --runtime-config=storage.k8s.io/v1beta1/csistoragecapacities

持久化

Kubernetes 通过 API 资源来将序列化的状态写到 etcd 中存储。

接下来

2.1 - Kubernetes API 概念

Kubernetes API 是通过 HTTP 提供的基于资源 (RESTful) 的编程接口。 它支持通过标准 HTTP 动词(POST、PUT、PATCH、DELETE、GET)检索、创建、更新和删除主要资源。

对于某些资源,API 包括额外的子资源,允许细粒度授权(例如:将 Pod 的详细信息与检索日志分开), 为了方便或者提高效率,可以以不同的表示形式接受和服务这些资源。

Kubernetes 支持通过 watch 实现高效的资源变更通知。 Kubernetes 还提供了一致的列表操作,以便 API 客户端可以有效地缓存、跟踪和同步资源的状态。

你可以在线查看 API 参考, 或继续阅读以了解 API 的一般信息。

Kubernetes API 术语

Kubernetes 通常使用常见的 RESTful 术语来描述 API 概念:

  • 资源类型(Resource Type) 是 URL 中使用的名称(podsnamespacesservices
  • 所有资源类型都有一个具体的表示(它们的对象模式),称为 类别(Kind)
  • 资源实例的列表称为 集合(Collection)
  • 资源类型的单个实例称为 资源(Resource),通常也表示一个 对象(Object)
  • 对于某些资源类型,API 包含一个或多个 子资源(sub-resources),这些子资源表示为资源下的 URI 路径

大多数 Kubernetes API 资源类型都是对象: 它们代表集群上某个概念的具体实例,例如 Pod 或命名空间。 少数 API 资源类型是 “虚拟的”,它们通常代表的是操作而非对象本身, 例如权限检查(使用带有 JSON 编码的 SubjectAccessReview 主体的 POST 到 subjectaccessreviews 资源), 或 Pod 的子资源 eviction(用于触发 API-发起的驱逐)。

对象名字

你可以通过 API 创建的所有对象都有一个唯一的名字, 以允许幂等创建和检索, 但如果虚拟资源类型不可检索或不依赖幂等性,则它们可能没有唯一名称。 在命名空间内, 同一时刻只能有一个给定类别的对象具有给定名称。 但是,如果你删除该对象,你可以创建一个具有相同名称的新对象。 有些对象没有命名空间(例如:节点),因此它们的名称在整个集群中必须是唯一的。

API 动词

几乎所有对象资源类型都支持标准 HTTP 动词 - GET、POST、PUT、PATCH 和 DELETE。 Kubernetes 也使用自己的动词,这些动词通常写成小写,以区别于 HTTP 动词。

Kubernetes 使用术语 list 来描述返回资源集合, 以区别于通常称为 get 的单个资源检索。 如果你发送带有 ?watch 查询参数的 HTTP GET 请求, Kubernetes 将其称为 watch 而不是 get(有关详细信息,请参阅快速检测更改)。

对于 PUT 请求,Kubernetes 在内部根据现有对象的状态将它们分类为 createupdateupdate 不同于 patchpatch 的 HTTP 动词是 PATCH。

资源 URI

所有资源类型要么是集群作用域的(/apis/GROUP/VERSION/*), 要么是名字空间作用域的(/apis/GROUP/VERSION/namespaces/NAMESPACE/*)。 名字空间作用域的资源类型会在其名字空间被删除时也被删除, 并且对该资源类型的访问是由定义在名字空间域中的授权检查来控制的。

你还可以访问资源集合(例如:列出所有 Node)。以下路径用于检索集合和资源:

  • 集群作用域的资源:
    • GET /apis/GROUP/VERSION/RESOURCETYPE - 返回指定资源类型的资源的集合
    • GET /apis/GROUP/VERSION/RESOURCETYPE/NAME - 返回指定资源类型下名称为 NAME 的资源
  • 名字空间作用域的资源:
    • GET /apis/GROUP/VERSION/RESOURCETYPE - 返回所有名字空间中指定资源类型的全部实例的集合
    • GET /apis/GROUP/VERSION/namespaces/NAMESPACE/RESOURCETYPE - 返回名字空间 NAMESPACE 内给定资源类型的全部实例的集合
    • GET /apis/GROUP/VERSION/namespaces/NAMESPACE/RESOURCETYPE/NAME - 返回名字空间 NAMESPACE 中给定资源类型的名称为 NAME 的实例

由于名字空间本身是一个集群作用域的资源类型,你可以通过 GET /api/v1/namespaces/ 检视所有名字空间的列表(“集合”),使用 GET /api/v1/namespaces/NAME 查看特定名字空间的详细信息。

  • 集群作用域的子资源:GET /apis/GROUP/VERSION/RESOURCETYPE/NAME/SUBRESOURCE
  • 名字空间作用域的子资源:GET /apis/GROUP/VERSION/namespaces/NAMESPACE/RESOURCETYPE/NAME/SUBRESOURCE

取决于对象是什么,每个子资源所支持的动词有所不同 - 参见 API 文档以了解更多信息。 跨多个资源来访问其子资源是不可能的 - 如果需要这一能力,则通常意味着需要一种新的虚拟资源类型了。

高效检测变更

Kubernetes API 允许客户端对对象或集合发出初始请求,然后跟踪自该初始请求以来的更改:watch。 客户端可以发送 list 或者 get 请求,然后发出后续 watch 请求。

为了使这种更改跟踪成为可能,每个 Kubernetes 对象都有一个 resourceVersion 字段, 表示存储在底层持久层中的该资源的版本。在检索资源集合(命名空间或集群范围)时, 来自 API 服务器的响应包含一个 resourceVersion 值。 客户端可以使用该 resourceVersion 来启动对 API 服务器的 watch

当你发送 watch 请求时,API 服务器会响应更改流。 这些更改逐项列出了在你指定为 watch 请求参数的 resourceVersion 之后发生的操作(例如 createdeleteupdate)的结果。 整个 watch 机制允许客户端获取当前状态,然后订阅后续更改,而不会丢失任何事件。

如果客户端 watch 连接断开,则该客户端可以从最后返回的 resourceVersion 开始新的 watch 请求; 客户端还可以执行新的 get/list 请求并重新开始。有关更多详细信息,请参阅资源版本语义

例如:

  1. 列举给定名字空间中的所有 Pod:

    GET /api/v1/namespaces/test/pods
    ---
    200 OK
    Content-Type: application/json
    
    {
      "kind": "PodList",
      "apiVersion": "v1",
      "metadata": {"resourceVersion":"10245"},
      "items": [...]
    }
    
  1. 从资源版本 10245 开始,接收影响 test 命名空间中 Pod 的所有 API 操作 (例如 createdeleteapplyupdate)的通知。 每个更改通知都是一个 JSON 文档。 HTTP 响应正文(用作 application/json)由一系列 JSON 文档组成。

    GET /api/v1/namespaces/test/pods?watch=1&resourceVersion=10245
    ---
    200 OK
    Transfer-Encoding: chunked
    Content-Type: application/json
    
    {
      "type": "ADDED",
      "object": {"kind": "Pod", "apiVersion": "v1", "metadata": {"resourceVersion": "10596", ...}, ...}
    }
    {
      "type": "MODIFIED",
      "object": {"kind": "Pod", "apiVersion": "v1", "metadata": {"resourceVersion": "11020", ...}, ...}
    }
    ...
    

给定的 Kubernetes 服务器只会保留一定的时间内发生的历史变更列表。 使用 etcd3 的集群默认保存过去 5 分钟内发生的变更。 当所请求的 watch 操作因为资源的历史版本不存在而失败, 客户端必须能够处理因此而返回的状态代码 410 Gone,清空其本地的缓存, 重新执行 get 或者 list 操作, 并基于新返回的 resourceVersion 来开始新的 watch 操作。

对于订阅集合,Kubernetes 客户端库通常会为 list -然后- watch 的逻辑提供某种形式的标准工具。 (在 Go 客户端库中,这称为 反射器(Reflector),位于 k8s.io/client-go/tools/cache 包中。)

监视书签

为了减轻短历史窗口的影响,Kubernetes API 提供了一个名为 BOOKMARK 的监视事件。 这是一种特殊的事件,用于标记客户端请求的给定 resourceVersion 的所有更改都已发送。 代表 BOOKMARK 事件的文档属于请求所请求的类型,但仅包含一个 .metadata.resourceVersion 字段。例如:

GET /api/v1/namespaces/test/pods?watch=1&resourceVersion=10245&allowWatchBookmarks=true
---
200 OK
Transfer-Encoding: chunked
Content-Type: application/json

{
  "type": "ADDED",
  "object": {"kind": "Pod", "apiVersion": "v1", "metadata": {"resourceVersion": "10596", ...}, ...}
}
...
{
  "type": "BOOKMARK",
  "object": {"kind": "Pod", "apiVersion": "v1", "metadata": {"resourceVersion": "12746"} }
}

作为客户端,你可以在 watch 请求中设置 allowWatchBookmarks=true 查询参数来请求 BOOKMARK 事件, 但你不应假设书签会在任何特定时间间隔返回,即使要求时,客户端也不能假设 API 服务器会发送任何 BOOKMARK 事件。

分块检视大体量结果

特性状态: Kubernetes v1.9 [beta]

在较大规模集群中,检索某些资源类型的集合可能会导致非常大的响应,从而影响服务器和客户端。 例如,一个集群可能有数万个 Pod,每个 Pod 大约相当于 2 KiB 的编码 JSON。 跨所有命名空间检索所有 Pod 可能会导致非常大的响应 (10-20MB) 并消耗大量服务器资源。

如果你没有明确禁用 APIListChunking 特性门控, Kubernetes API 服务器支持将单个大型集合请求分解为许多较小块的能力,同时保持总请求的一致性。

你可以请求 API 服务器通过使用页(Kubernetes 将其称为“块(Chunk)”)的方式来处理 list, 完成单个集合的响应。 要以块的形式检索单个集合,针对集合的请求支持两个查询参数 limitcontinue, 并且从集合元 metadata 字段中的所有 list 操作返回响应字段 continue。 客户端应该指定他们希望在每个带有 limit 的块中接收的条目数上限,如果集合中有更多资源, 服务器将在结果中返回 limit 资源并包含一个 continue 值。

作为 API 客户端,你可以在下一次请求时将 continue 值传递给 API 服务器, 以指示服务器返回下一页()结果。继续下去直到服务器返回一个空的 continue 值, 你可以检索整个集合。

watch 操作类似,continue 令牌也会在很短的时间(默认为 5 分钟)内过期, 并在无法返回更多结果时返回 410 Gone 代码。 这时,客户端需要从头开始执行上述检视操作或者忽略 limit 参数。

例如,如果集群上有 1253 个 Pod,客户端希望每次收到包含至多 500 个 Pod 的数据块,它应按下面的步骤来请求数据块:

  1. 列举集群中所有 Pod,每次接收至多 500 个 Pod:

    GET /api/v1/pods?limit=500
    ---
    200 OK
    Content-Type: application/json
    
    {
      "kind": "PodList",
      "apiVersion": "v1",
      "metadata": {
        "resourceVersion":"10245",
        "continue": "ENCODED_CONTINUE_TOKEN",
        "remainingItemCount": 753,
        ...
      },
      "items": [...] // returns pods 1-500
    }
    
  1. 继续前面的调用,返回下一组 500 个 Pod:

    GET /api/v1/pods?limit=500&continue=ENCODED_CONTINUE_TOKEN
    ---
    200 OK
    Content-Type: application/json
    
    {
      "kind": "PodList",
      "apiVersion": "v1",
      "metadata": {
        "resourceVersion":"10245",
        "continue": "ENCODED_CONTINUE_TOKEN_2",
        "remainingItemCount": 253,
        ...
      },
      "items": [...] // returns pods 501-1000
    }
    
  1. 继续前面的调用,返回最后 253 个 Pod:

    GET /api/v1/pods?limit=500&continue=ENCODED_CONTINUE_TOKEN_2
    ---
    200 OK
    Content-Type: application/json
    
    {
      "kind": "PodList",
      "apiVersion": "v1",
      "metadata": {
        "resourceVersion":"10245",
        "continue": "", // continue token is empty because we have reached the end of the list
        ...
      },
      "items": [...] // returns pods 1001-1253
    }
    

请注意,集合的 resourceVersion 在每个请求中保持不变, 这表明服务器正在向你显示 Pod 的一致快照。 在版本 10245 之后创建、更新或删除的 Pod 将不会显示, 除非你在没有继续令牌的情况下发出单独的 list 请求。 这使你可以将大请求分成更小的块,然后对整个集合执行 watch 操作,而不会丢失任何更新。

remainingItemCount 是集合中未包含在此响应中的后续项目的数量。 如果 list 请求包含标签或字段选择器, 则剩余项目的数量是未知的,并且 API 服务器在其响应中不包含 remainingItemCount 字段。 如果 list 是完整的(因为它没有分块,或者因为这是最后一个块),没有更多的剩余项目, API 服务器在其响应中不包含 remainingItemCount 字段。 remainingItemCount 的用途是估计集合的大小。

集合

在 Kubernetes 术语中,你从 list 中获得的响应是一个“集合(Collections)”。 然而,Kubernetes 为不同类型资源的集合定义了具体类型。 集合的类别名是针对资源类别的,并附加了 List

当你查询特定类型的 API 时,该查询返回的所有项目都属于该类型。 例如,当你 list Service 对象时,集合响应的 kind 设置为 ServiceList; 该集合中的每个项目都代表一个 Service。例如:

GET /api/v1/services
{
  "kind": "ServiceList",
  "apiVersion": "v1",
  "metadata": {
    "resourceVersion": "2947301"
  },
  "items": [
    {
      "metadata": {
        "name": "kubernetes",
        "namespace": "default",
...
      "metadata": {
        "name": "kube-dns",
        "namespace": "kube-system",
...

Kubernetes API 中定义了数十种集合类型(如 PodListServiceListNodeList)。 你可以从 Kubernetes API 文档中获取有关每种集合类型的更多信息。

一些工具,例如 kubectl,对于 Kubernetes 集合的表现机制与 Kubernetes API 本身略有不同。 因为 kubectl 的输出可能包含来自 API 级别的多个 list 操作的响应, 所以 kubectl 使用 kind: List 表示项目列表。例如:

kubectl get services -A -o yaml
apiVersion: v1
kind: List
metadata:
  resourceVersion: ""
  selfLink: ""
items:
- apiVersion: v1
  kind: Service
  metadata:
    creationTimestamp: "2021-06-03T14:54:12Z"
    labels:
      component: apiserver
      provider: kubernetes
    name: kubernetes
    namespace: default
...
- apiVersion: v1
  kind: Service
  metadata:
    annotations:
      prometheus.io/port: "9153"
      prometheus.io/scrape: "true"
    creationTimestamp: "2021-06-03T14:54:14Z"
    labels:
      k8s-app: kube-dns
      kubernetes.io/cluster-service: "true"
      kubernetes.io/name: CoreDNS
    name: kube-dns
    namespace: kube-system

以表格形式接收资源

当你执行 kubectl get 时,默认的输出格式是特定资源类型的一个或多个实例的简单表格形式。 过去,客户端需要重复 kubectl 中所实现的表格输出和描述输出逻辑,以执行简单的对象列表操作。 该方法的一些限制包括处理某些对象时的不可忽视逻辑。 此外,API 聚合或第三方资源提供的类型在编译时是未知的。 这意味着必须为客户端无法识别的类型提供通用实现。

为了避免上述各种潜在的局限性,客户端可以请求服务器端返回对象的表格(Table) 表现形式,从而将打印输出的特定细节委托给服务器。 Kubernetes API 实现标准的 HTTP 内容类型(Content Type)协商:为 GET 调用传入一个值为 application/json;as=Table;g=meta.k8s.io;v=v1Accept 头部即可请求服务器以 Table 的内容类型返回对象。

例如,以 Table 格式列举集群中所有 Pod:

GET /api/v1/pods
Accept: application/json;as=Table;g=meta.k8s.io;v=v1
---
200 OK
Content-Type: application/json

{
    "kind": "Table",
    "apiVersion": "meta.k8s.io/v1",
    ...
    "columnDefinitions": [
        ...
    ]
}

对于在控制平面上不存在定制的 Table 定义的 API 资源类型而言,服务器会返回一个默认的 Table 响应,其中包含资源的 namecreationTimestamp 字段。

GET /apis/crd.example.com/v1alpha1/namespaces/default/resources
---
200 OK
Content-Type: application/json
...

{
    "kind": "Table",
    "apiVersion": "meta.k8s.io/v1",
    ...
    "columnDefinitions": [
        {
            "name": "Name",
            "type": "string",
            ...
        },
        {
            "name": "Created At",
            "type": "date",
            ...
        }
    ]
}

并非所有 API 资源类型都支持 Table 响应; 例如,CustomResourceDefinitions 可能没有定义字段到表的映射, 扩展核心 Kubernetes API 的 APIService 可能根本不提供 Table 响应。 如果你正在实现使用 Table 信息并且必须针对所有资源类型(包括扩展)工作的客户端, 你应该在 Accept 请求头中指定多种内容类型的请求。例如:

Accept: application/json;as=Table;g=meta.k8s.io;v=v1, application/json

资源的其他表示形式

默认情况下,Kubernetes 返回序列化为 JSON 的对象,内容类型为 application/json。 这是 API 的默认序列化格式。 但是,客户端可能会使用更有效的 Protobuf 表示 请求这些对象, 以获得更好的大规模性能。Kubernetes API 实现标准的 HTTP 内容类型协商: 带有 Accept 请求头部的 GET 调用会请求服务器尝试以你的首选媒体类型返回响应, 而将 Protobuf 中的对象发送到服务器以进行 PUTPOST 调用意味着你必须适当地设置 Content-Type 请求头。

如果支持请求的格式,服务器将返回带有 Content-Type 标头的响应, 如果不支持你请求的媒体类型,则返回 406 Not Acceptable 错误。 所有内置资源类型都支持 application/json 媒体类型。

有关每个 API 支持的内容类型列表,请参阅 Kubernetes API 参考

例如:

  1. 以 Protobuf 格式列举集群上的所有 Pod:

    GET /api/v1/pods
    Accept: application/vnd.kubernetes.protobuf
    ---
    200 OK
    Content-Type: application/vnd.kubernetes.protobuf
    
    ... binary encoded PodList object
    
  1. 通过向服务器发送 Protobuf 编码的数据创建 Pod,但请求以 JSON 形式接收响应:

    POST /api/v1/namespaces/test/pods
    Content-Type: application/vnd.kubernetes.protobuf
    Accept: application/json
    ... binary encoded Pod object
    ---
    200 OK
    Content-Type: application/json
    
    {
      "kind": "Pod",
      "apiVersion": "v1",
      ...
    }
    

并非所有 API 资源类型都支持 Protobuf;具体来说, Protobuf 不适用于定义为 CustomResourceDefinitions 或通过聚合层提供服务的资源。 作为客户端,如果你可能需要使用扩展类型,则应在请求 Accept 请求头中指定多种内容类型以支持回退到 JSON。 例如:

Accept: application/vnd.kubernetes.protobuf, application/json

Kubernetes Protobuf 编码

Kubernetes 使用封套形式来对 Protobuf 响应进行编码。 封套外层由 4 个字节的特殊数字开头,便于从磁盘文件或 etcd 中辩识 Protobuf 格式的(而不是 JSON)数据。 接下来存放的是 Protobuf 编码的封套消息,其中描述下层对象的编码和类型,最后 才是对象本身。

封套格式如下:

四个字节的特殊数字前缀:
  字节 0-3: "k8s\x00" [0x6b, 0x38, 0x73, 0x00]

使用下面 IDL 来编码的 Protobuf 消息:
  message Unknown {
    // typeMeta 应该包含 "kind" 和 "apiVersion" 的字符串值,就像
    // 对应的 JSON 对象中所设置的那样
    optional TypeMeta typeMeta = 1;

    // raw 中将保存用 protobuf 序列化的完整对象。
    // 参阅客户端库中为指定 kind 所作的 protobuf 定义
    optional bytes raw = 2;

    // contentEncoding 用于 raw 数据的编码格式。未设置此值意味着没有特殊编码。
    optional string contentEncoding = 3;

    // contentType 包含 raw 数据所采用的序列化方法。
    // 未设置此值意味着 application/vnd.kubernetes.protobuf,且通常被忽略
    optional string contentType = 4;
  }

  message TypeMeta {
    // apiVersion 是 type 对应的组名/版本
    optional string apiVersion = 1;
    // kind 是对象模式定义的名称。此对象应该存在一个 protobuf 定义。
    optional string kind = 2;
  }

资源删除

当你 delete 资源时,操作将分两个阶段进行。

  1. 终结(finalization)
  2. 移除
{
  "kind": "ConfigMap",
  "apiVersion": "v1",
  "metadata": {
    "finalizers": {"url.io/neat-finalization", "other-url.io/my-finalizer"},
    "deletionTimestamp": nil,
  }
}

当客户端第一次发送 delete 请求删除资源时,.metadata.deletionTimestamp 设置为当前时间。 一旦设置了 .metadata.deletionTimestamp, 作用于终结器的外部控制器可以在任何时间以任何顺序开始执行它们的清理工作。

终结器之间 不存在 强制的执行顺序,因为这会带来卡住 .metadata.finalizers 的重大风险。

.metadata.finalizers 字段是共享的:任何有权限的参与者都可以重新排序。 如果终结器列表是按顺序处理的,那么这可能会导致这样一种情况: 在列表中负责第一个终结器的组件正在等待列表中稍后负责终结器的组件产生的某些信号 (字段值、外部系统或其他),从而导致死锁。

如果没有强制排序,终结者可以在它们之间自由排序,并且不易受到列表中排序变化的影响。

当最后一个终结器也被移除时,资源才真正从 etcd 中移除。

单个资源 API

Kubernetes API 动词 getcreateapplyupdatepatchdeleteproxy 仅支持单一资源。 这些具有单一资源支持的动词不支持在有序或无序列表或事务中一起提交多个资源。

当客户端(包括 kubectl)对一组资源进行操作时,客户端会发出一系列单资源 API 请求, 然后在需要时聚合响应。

相比之下,Kubernetes API 动词 listwatch 允许获取多个资源, 而 deletecollection 允许删除多个资源。

字段校验

Kubernetes 总是校验字段的类型。例如,如果 API 中的某个字段被定义为数值, 你就不能将该字段设置为文本类型的值。如果某个字段被定义为字符串数组,你只能提供数组。 有些字段可以忽略,有些字段必须填写。忽略 API 请求中的必填字段会报错。

如果请求中带有集群控制面无法识别的额外字段,API 服务器的行为会更加复杂。

默认情况下,如果接收到的输入信息中含有 API 服务器无法识别的字段,API 服务器会丢弃该字段 (例如: PUT 请求中的 JSON 主体)。

API 服务器会在两种情况下丢弃 HTTP 请求中提供的字段。

这些情况是:

  1. 相关资源的 OpenAPI 模式定义中没有该字段,因此无法识别该字段(有种例外情形是, CRD 通过 x-kubernetes-preserve-unknown-fields 显式选择不删除未知字段)。
  1. 字段在对象中重复出现。

设置字段校验层级

特性状态: Kubernetes v1.25 [beta]

如果启用了 ServerSideFieldValidation 特性门控 (在 1.23 和 1.24 中默认处于禁用状态,从 1.25 开始默认启用), 你可以用服务端的字段校验来抓取这些未能识别的字段。

使用可以提交数据的 HTTP 动词(POSTPUTPATCH)时,可以在字段校验中设置 API 服务器丢弃字段时的通知设置。通知层级可能包括 IgnoreWarnStrict

字段校验需要通过 fieldValidation 查询参数进行设置。此参数接受三种值:

Ignore
使 API 服务器像没有遇到错误字段一样成功处理请求,丢弃所有的未知字段和重复字段,并且不发送丢弃字段的通知。

Warn :(默认值)使 API 服务器成功处理请求,并向客户端发送告警信息。告警信息通过 Warning: 响应头发送, 并为每个未知字段或重复字段添加一条告警信息。有关告警和相关的 Kubernetes API 的信息, 可参阅博文告警:增加实用告警功能

Strict
API 服务器检测到任何未知字段或重复字段时,拒绝处理请求并返回 400 Bad Request 错误。 来自 API 服务器的响应消息列出了 API 检测到的所有未知字段或重复字段。

向服务器提交请求的工具(例如 kubectl)可能会设置自己的默认值,与 API 服务器默认使用的 Warn 校验层级不同。

kubectl 工具使用 --validate 标志设置字段校验层级。 之前 --validate 被作为布尔值开启或关闭客户段的校验功能。 从 Kubernetes 1.25 开始,kubectl 向启用字段校验的服务器发送请求时使用服务端字段校验。 只有无法连接启用了字段校验的 API 服务器时,才会恢复使用客户端的字段校验。

kubectl 接受 ignorewarnstrict 值,同时也接受 true(等效于 strict) 和 false(等效于 ignore)。kubectl 的字段校验默认配置为 --validate=true, 即服务端的 strict 级字段校验。

试运行

特性状态: Kubernetes v1.18 [stable]

当你使用可以修改资源的 HTTP 动词(POSTPUTPATCHDELETE)时, 你可以在 试运行(dry run) 模式下提交你的请求。 试运行模式有助于通过典型的请求阶段(准入链、验证、合并冲突)评估请求,直到将对象持久化到存储中。 请求的响应正文尽可能接近非试运行响应。Kubernetes 保证试运行请求不会被持久化存储或产生任何其他副作用。

发起试运行请求

通过设置 dryRun 查询参数触发试运行。此参数是一个字符串,用作枚举,唯一可接受的值是:

[未设置值]
允许副作用。你可以使用 ?dryRun?dryRun&pretty=true 之类的查询字符串请求此操作。 响应是最终会被持久化的对象,或者如果请求不能被满足则会出现一个错误。
All
每个阶段都正常运行,除了防止副作用的最终存储阶段。

当你设置 ?dryRun=All 时,将运行任何相关的准入控制器, 验证准入控制器检查经过变更的请求,针对 PATCH 请求执行合并、设置字段默认值等操作,并进行模式验证。 更改不会持久化到底层存储,但本应持久化的最终对象仍会与正常状态代码一起返回给用户。

如果请求的非试运行版本会触发具有副作用的准入控制器,则该请求将失败,而不是冒不希望的副作用的风险。 所有内置准入控制插件都支持试运行。 此外,准入 Webhook 还可以设置配置对象sideEffects 字段为 None,借此声明它们没有副作用。

这是一个使用 ?dryRun=All 的试运行请求的示例:

POST /api/v1/namespaces/test/pods?dryRun=All
Content-Type: application/json
Accept: application/json

响应会与非试运行模式请求的响应看起来相同,只是某些生成字段的值可能会不同。

生成值

对象的某些值通常是在对象被写入数据库之前生成的。很重要的一点是不要依赖试运行请求为这些字段所设置的值, 因为试运行模式下所得到的这些值与真实请求所获得的值很可能不同。这类字段有:

  • name:如果设置了 generateName 字段,则 name 会获得一个唯一的随机名称
  • creationTimestamp / deletionTimestamp:记录对象的创建/删除时间
  • UID唯一标识对象, 取值随机生成(非确定性)
  • resourceVersion:跟踪对象的持久化(存储)版本
  • 变更性准入控制器所设置的字段
  • 对于 Service 资源:kube-apiserverService 对象分配的端口和 IP 地址

试运行的授权

试运行和非试运行请求的鉴权是完全相同的。因此,要发起一个试运行请求, 你必须被授权执行非试运行请求。

例如,要在 Deployment 对象上试运行 patch 操作,你必须具有对 Deployment 执行 patch 操作的访问权限, 如下面的 RBAC 规则所示:

rules:
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["patch"]

参阅鉴权概述以了解鉴权细节。

服务器端应用

Kubernetes 的服务器端应用功能允许控制平面跟踪新创建对象的托管字段。 服务端应用为管理字段冲突提供了清晰的模式,提供了服务器端 ApplyUpdate 操作, 并替换了 kubectl apply 的客户端功能。

服务端应用的 API 动词是 apply。有关详细信息, 请参阅服务器端应用

资源版本

资源版本是标识服务器内部对象版本的字符串。 客户端可以使用资源版本来确定对象何时更改, 或者在获取、列出和监视资源时表达数据一致性要求。 资源版本必须被客户端视为不透明的,并且未经修改地传回服务器。

你不能假设资源版本是数字的或可排序的。 API 客户端只能比较两个资源版本的相等性(这意味着你不能比较资源版本的大于或小于关系)。

metadata 中的 resourceVersion

客户端在资源中查找资源版本,这些资源包括来自用于 watch 的响应流资源,或者使用 list 枚举的资源。

v1.meta/ObjectMeta - 资源的 metadata.resourceVersion 值标明该实例上次被更改时的资源版本。

v1.meta/ListMeta - 资源集合即 list 操作的响应)的 metadata.resourceVersion 所标明的是 list 响应被构造时的资源版本。

查询字符串中的 resourceVersion 参数

getlistwatch 操作支持 resourceVersion 参数。 从 v1.19 版本开始,Kubernetes API 服务器支持 list 请求的 resourceVersionMatch 参数。

API 服务器根据你请求的操作和 resourceVersion 的值对 resourceVersion 参数进行不同的解释。 如果你设置 resourceVersionMatch 那么这也会影响匹配发生的方式。

getlist 语义

对于 getlist 而言,resourceVersion 的语义为:

get:

resourceVersion 未设置resourceVersion="0"resourceVersion="<非零值>"
最新版本任何版本不老于给定版本

list:

从 v1.19 版本开始,Kubernetes API 服务器支持 list 请求的 resourceVersionMatch 参数。 如果同时设置 resourceVersionresourceVersionMatch, 则 resourceVersionMatch 参数确定 API 服务器如何解释 resourceVersion

list 请求上设置 resourceVersion 时,你应该始终设置 resourceVersionMatch 参数。 但是,请准备好处理响应的 API 服务器不知道 resourceVersionMatch 并忽略它的情况。

除非你对一致性有着非常强烈的需求,使用 resourceVersionMatch=NotOlderThan 同时为 resourceVersion 设定一个已知值是优选的交互方式,因为与不设置 resourceVersionresourceVersionMatch 相比,这种配置可以取得更好的集群性能和可扩缩性。 后者需要提供带票选能力的读操作。

设置 resourceVersionMatch 参数而不设置 resourceVersion 参数是不合法的。

下表解释了具有各种 resourceVersionresourceVersionMatch 组合的 list 请求的行为:

list 操作的 resourceVersionMatch 与分页参数
resourceVersionMatch 参数分页参数resourceVersion 未设置resourceVersion="0"resourceVersion="<非零值>"
未设置limit 未设置最新版本任意版本不老于指定版本
未设置limit=<n>, continue 未设置最新版本任意版本精确匹配
未设置limit=<n>, continue=<token>从 token 开始、精确匹配非法请求,视为从 token 开始、精确匹配非法请求,返回 HTTP 400 Bad Request
resourceVersionMatch=Exact [1]limit 未设置非法请求非法请求精确匹配
resourceVersionMatch=Exact [1]limit=<n>, continue 未设置非法请求非法请求精确匹配
resourceVersionMatch=NotOlderThan [1]limit 未设置非法请求任意版本不老于指定版本
resourceVersionMatch=NotOlderThan [1]limit=<n>, continue 未设置非法请求任意版本不老于指定版本

getlist 的语义是:

任意版本
返回任何资源版本的数据。最新可用资源版本优先,但不需要强一致性; 可以提供任何资源版本的数据。由于分区或过时的缓存, 请求可能返回客户端先前观察到的更旧资源版本的数据,特别是在高可用性配置中。 不能容忍这种情况的客户不应该使用这种语义。
最新版本
返回最新资源版本的数据。 返回的数据必须一致(详细说明:通过仲裁读取从 etcd 提供)。
不老于指定版本
返回数据至少与提供的 resourceVersion 一样新。 最新的可用数据是首选,但可以提供不早于提供的 resourceVersion 的任何数据。 对于对遵守 resourceVersionMatch 参数的服务器的 list 请求, 这保证了集合的 .metadata.resourceVersion 不早于请求的 resourceVersion, 但不保证该集合中任何项目的 .metadata.resourceVersion
精确匹配
以提供的确切资源版本返回数据。如果提供的 resourceVersion 不可用, 则服务器以 HTTP 410 “Gone”响应。对于对支持 resourceVersionMatch 参数的服务器的 list 请求, 这可以保证集合的 .metadata.resourceVersion 与你在查询字符串中请求的 resourceVersion 相同。 该保证不适用于该集合中任何项目的 .metadata.resourceVersion
从 token 开始、精确匹配
返回初始分页 list 调用的资源版本的数据。 返回的 Continue 令牌 负责跟踪最初提供的资源版本,最初提供的资源版本用于在初始分页 list 之后的所有分页 list 中。

当使用 resourceVersionMatch=NotOlderThan 并设置了限制时, 客户端必须处理 HTTP 410 “Gone” 响应。 例如,客户端可能会使用更新的 resourceVersion 重试或回退到 resourceVersion=""

当使用 resourceVersionMatch=Exact 并且未设置限制时, 客户端必须验证集合的 .metadata.resourceVersion 是否与请求的 resourceVersion 匹配, 并处理不匹配的情况。例如,客户端可能会退回到设置了限制的请求。

watch 语义

对于 watch 操作而言,资源版本的语义如下:

watch:

watch 操作的 resourceVersion 设置
resourceVersion 未设置resourceVersion="0"resourceVersion="<非零值>"
读取状态并从最新版本开始读取状态并从任意版本开始从指定版本开始

watch 操作语义的含义如下:

读取状态并从任意版本开始
在任何资源版本开始 watch;首选可用的最新资源版本,但不是必需的。允许任何起始资源版本。 由于分区或过时的缓存,watch 可能从客户端之前观察到的更旧的资源版本开始, 特别是在高可用性配置中。不能容忍这种明显倒带的客户不应该用这种语义启动 watch。 为了建立初始状态,watch 从起始资源版本中存在的所有资源实例的合成 “添加” 事件开始。 以下所有监视事件都针对在 watch 开始的资源版本之后发生的所有更改。
读取状态并从最新版本开始
从最近的资源版本开始 watch, 它必须是一致的(详细说明:通过仲裁读取从 etcd 提供服务)。 为了建立初始状态,watch 从起始资源版本中存在的所有资源实例的合成 “添加” 事件开始。 以下所有监视事件都针对在 watch 开始的资源版本之后发生的所有更改。
从指定版本开始
以确切的资源版本开始 watch。监视事件适用于提供的资源版本之后的所有更改。 与 “Get State and Start at Most Recent” 和 “Get State and Start at Any” 不同, watch 不会以所提供资源版本的合成 “添加” 事件启动。 由于客户端提供了资源版本,因此假定客户端已经具有起始资源版本的初始状态。

"410 Gone" 响应

服务器不需要提供所有老的资源版本,在客户端请求的是早于服务器端所保留版本的 resourceVersion 时,可以返回 HTTP 410 (Gone) 状态码。 客户端必须能够容忍 410 (Gone) 响应。 参阅高效检测变更以了解如何在监测资源时处理 410 (Gone) 响应。

如果所请求的 resourceVersion 超出了可应用的 limit, 那么取决于请求是否是通过高速缓存来满足的,API 服务器可能会返回一个 410 Gone HTTP 响应。

不可用的资源版本

服务器不需要提供无法识别的资源版本。 如果你请求了 listget API 服务器无法识别的资源版本,则 API 服务器可能会:

  • 短暂等待资源版本可用,如果提供的资源版本在合理的时间内仍不可用, 则应超时并返回 504 (Gateway Timeout)
  • 使用 Retry-After 响应标头进行响应,指示客户端在重试请求之前应等待多少秒。

如果你请求 API 服务器无法识别的资源版本, kube-apiserver 还会使用 “Too large resource version” 消息额外标识其错误响应。

如果你对无法识别的资源版本发出 watch 请求, API 服务器可能会无限期地等待(直到请求超时)资源版本变为可用。

2.2 - 服务器端应用(Server-Side Apply)

特性状态: Kubernetes v1.22 [stable]

简介

服务器端应用协助用户、控制器通过声明式配置的方式管理他们的资源。 客户端可以发送完整描述的目标(A fully specified intent), 声明式地创建和修改对象

一个完整描述的目标并不是一个完整的对象,仅包括能体现用户意图的字段和值。 该目标(intent)可以用来创建一个新对象, 也可以通过服务器来实现与现有对象的合并

系统支持多个应用者(appliers)在同一个对象上开展协作。

字段管理(field management)”机制追踪对象字段的变化。 当一个字段值改变时,其所有权从当前管理器(manager)转移到施加变更的管理器。 当尝试将新配置应用到一个对象时,如果字段有不同的值,且由其他管理器管理, 将会引发冲突。 冲突引发警告信号:此操作可能抹掉其他协作者的修改。 冲突可以被刻意忽略,这种情况下,值将会被改写,所有权也会发生转移。

当你从配置文件中删除一个字段,然后应用这个配置文件, 将触发服务器端应用检查此字段是否还被其他字段管理器拥有。 如果没有,那就从活动对象中删除该字段;如果有,那就重置为默认值。 该规则同样适用于 list 或 map 项目。

服务器端应用既是原有 kubectl apply 的替代品, 也是控制器发布自身变化的一个简化机制。

如果你启用了服务器端应用,控制平面就会跟踪被所有新创建对象管理的字段。

字段管理

相对于通过 kubectl 管理的注解 last-applied, 服务器端应用使用了一种更具声明式特点的方法: 它持续的跟踪用户的字段管理,而不仅仅是最后一次的执行状态。 这就意味着,作为服务器端应用的一个副作用, 关于用哪一个字段管理器负责管理对象中的哪个字段的这类信息,都要对外界开放了。

用户管理字段这件事,在服务器端应用的场景中,意味着用户依赖并期望字段的值不要改变。 最后一次对字段值做出断言的用户将被记录到当前字段管理器。 这可以通过发送 POSTPUT、 或非应用(non-apply)方式的 PATCH 等命令来修改字段值的方式实现, 或通过把字段放在配置文件中,然后发送到服务器端应用的服务端点的方式实现。 当使用服务器端应用,尝试着去改变一个被其他人管理的字段, 会导致请求被拒绝(在没有设置强制执行时,参见冲突)。

如果两个或以上的应用者均把同一个字段设置为相同值,他们将共享此字段的所有权。 后续任何改变共享字段值的尝试,不管由那个应用者发起,都会导致冲突。 共享字段的所有者可以放弃字段的所有权,这只需从配置文件中删除该字段即可。

字段管理的信息存储在 managedFields 字段中,该字段是对象的 metadata 中的一部分。

服务器端应用创建对象的简单示例如下:

apiVersion: v1
kind: ConfigMap
metadata:
  name: test-cm
  namespace: default
  labels:
    test-label: test
  managedFields:
  - manager: kubectl
    operation: Apply
    apiVersion: v1
    time: "2010-10-10T0:00:00Z"
    fieldsType: FieldsV1
    fieldsV1:
      f:metadata:
        f:labels:
          f:test-label: {}
      f:data:
        f:key: {}
data:
  key: some value

上述对象在 metadata.managedFields 中包含了唯一的管理器。 管理器由管理实体自身的基本信息组成,比如操作类型、API 版本、以及它管理的字段。

不过,执行 Update 操作修改 metadata.managedFields 也是可实现的。 强烈不鼓励这么做,但当发生如下情况时, 比如 managedFields 进入不一致的状态(显然不应该发生这种情况), 这么做也是一个合理的尝试。

managedFields 的格式在 API 文档中描述。

冲突

冲突是一种特定的错误状态, 发生在执行 Apply 改变一个字段,而恰巧该字段被其他用户声明过主权时。 这可以防止一个应用者不小心覆盖掉其他用户设置的值。 冲突发生时,应用者有三种办法来解决它:

  • 覆盖前值,成为唯一的管理器: 如果打算覆盖该值(或应用者是一个自动化部件,比如控制器), 应用者应该设置查询参数 force 为 true(在 kubectl 中,可以通过在 apply 命令中使用 --force-conflicts 标志来完成),然后再发送一次请求。 这将强制操作成功,改变字段的值,从所有其他管理器的 managedFields 条目中删除指定字段。

  • 不覆盖前值,放弃管理权: 如果应用者不再关注该字段的值, 可以从配置文件中删掉它,再重新发送请求。 这就保持了原值不变,并从 managedFields 的应用者条目中删除该字段。

  • 不覆盖前值,成为共享的管理器: 如果应用者仍然关注字段值,并不想覆盖它, 他们可以在配置文件中把字段的值改为和服务器对象一样,再重新发送请求。 这样在不改变字段值的前提下, 就实现了字段管理被应用者和所有声明了管理权的其他的字段管理器共享。

管理器

管理器识别出正在修改对象的工作流程(在冲突时尤其有用), 管理器可以通过修改请求的参数 fieldManager 指定。 虽然 kubectl 默认发往 kubectl 服务端点,但它则请求到应用的服务端点(apply endpoint)。 对于其他的更新,它默认的是从用户代理计算得来。

应用和更新

此特性涉及两类操作,分别是 Apply (内容类型为 application/apply-patch+yamlPATCH 请求) 和 Update (所有修改对象的其他操作)。 这两类操作都会更新字段 managedFields,但行为表现有一点不同。

例如,在冲突发生的时候,只有 apply 操作失败,而 update 则不会。 此外,apply 操作必须通过提供一个 fieldManager 查询参数来标识自身, 而此查询参数对于 update 操作则是可选的。 最后,当使用 apply 命令时,你不能在应用中的对象中持有 managedFields

一个包含多个管理器的对象,示例如下:

apiVersion: v1
kind: ConfigMap
metadata:
  name: test-cm
  namespace: default
  labels:
    test-label: test
  managedFields:
  - manager: kubectl
    operation: Apply
    apiVersion: v1
    fields:
      f:metadata:
        f:labels:
          f:test-label: {}
  - manager: kube-controller-manager
    operation: Update
    apiVersion: v1
    time: '2019-03-30T16:00:00.000Z'
    fields:
      f:data:
        f:key: {}
data:
  key: new value

在这个例子中, 第二个操作被管理器 kube-controller-managerUpdate 的方式运行。 此 update 更改 data 字段的值, 并使得字段管理器被改为 kube-controller-manager

如果把 update 操作改为 Apply,那就会因为所有权冲突的原因,导致操作失败。

合并策略

由服务器端应用实现的合并策略,提供了一个总体更稳定的对象生命周期。 服务器端应用试图依据负责管理它们的主体来合并字段,而不是根据值来否决。 这么做是为了多个主体可以更新同一个对象,且不会引起意外的相互干扰。

当用户发送一个“完整描述的目标”对象到服务器端应用的服务端点, 服务器会将它和活动对象做一次合并,如果两者中有重复定义的值,那就以配置文件的为准。 如果配置文件中的项目集合不是此用户上一次操作项目的超集, 所有缺少的、没有其他应用者管理的项目会被删除。 关于合并时用来做决策的对象规格的更多信息,参见 sigs.k8s.io/structured-merge-diff.

Kubernetes 1.16 和 1.17 中添加了一些标记, 允许 API 开发人员描述由 list、map、和 structs 支持的合并策略。 这些标记可应用到相应类型的对象,在 Go 文件或在 CRD 的 OpenAPI 的模式中定义:

Golang 标记OpenAPI extension可接受的值描述引入版本
//+listTypex-kubernetes-list-typeatomic/set/map适用于 list。set 适用于仅包含标量元素的列表。这些元素必须是不重复的。map 仅适用于包含嵌套类型的列表。列表中的键(参见 listMapKey)不可以重复。atomic 适用于任何类型的列表。如果配置为 atomic,则合并时整个列表会被替换掉。任何时候,只有一个管理器负责管理指定列表。如果配置为 setmap,不同的管理器也可以分开管理条目。1.16
//+listMapKeyx-kubernetes-list-map-keys字段名称的列表,例如,["port", "protocol"]仅当 +listType=map 时适用。取值为字段名称的列表,这些字段值的组合能够唯一标识列表中的条目。尽管可以存在多个键,listMapKey 是单数的,这是因为键名需要在 Go 类型中各自独立指定。键字段必须是标量。1.16
//+mapTypex-kubernetes-map-typeatomic/granular适用于 map。 atomic 指 map 只能被单个的管理器整个的替换。 granular 指 map 支持多个管理器各自更新自己的字段。1.17
//+structTypex-kubernetes-map-typeatomic/granular适用于 structs;否则就像 //+mapType 有相同的用法和 openapi 注释.1.17

若未指定 listType,API 服务器将 patchMergeStrategy=merge 标记解释为 listType=map 并且视对应的 patchMergeKey 标记为 listMapKey 取值。

atomic 列表类型是递归的。

这些标记都是用源代码注释的方式给出的,不必作为字段标签(tag)再重复。

拓扑变化时的兼容性

在极少的情况下,CRD 或者内置类型的作者可能希望更改其资源中的某个字段的 拓扑配置,同时又不提升版本号。 通过升级集群或者更新 CRD 来更改类型的拓扑信息与更新现有对象的结果不同。 变更的类型有两种:一种是将字段从 map/set/granular 更改为 atomic, 另一种是做逆向改变。

listTypemapTypestructTypemap/set/granular 改为 atomic 时,现有对象的整个列表、映射或结构的属主都会变为这些类型的 元素之一的属主。这意味着,对这些对象的进一步变更会引发冲突。

当一个列表、映射或结构从 atomic 改为 map/set/granular 之一 时,API 服务器无法推导这些字段的新的属主。因此,当对象的这些字段 再次被更新时不会引发冲突。出于这一原因,不建议将某类型从 atomic 改为 map/set/granular

以下面的自定义资源为例:

apiVersion: example.com/v1
kind: Foo
metadata:
  name: foo-sample
  managedFields:
  - manager: manager-one
    operation: Apply
    apiVersion: example.com/v1
    fields:
      f:spec:
        f:data: {}
spec:
  data:
    key1: val1
    key2: val2

spec.dataatomic 改为 granular 之前,manager-onespec.data 字段及其所包含字段(key1key2)的属主。 当对应的 CRD 被更改,使得 spec.data 变为 granular 拓扑时, manager-one 继续拥有顶层字段 spec.data(这意味着其他管理者想删除名为 data 的映射而不引起冲突是不可能的),但不再拥有 key1key2。因此,其他管理者可以在不引起冲突的情况下更改 或删除这些字段。

自定义资源

默认情况下,服务器端应用把自定义资源看做非结构化数据。 所有的键值(keys)就像 struct 的字段一样被处理, 所有的 list 被认为是原子性的。

如果自定义资源定义(Custom Resource Definition,CRD)定义了一个 模式, 它包含类似以前“合并策略”章节中定义过的注解, 这些注解将在合并此类型的对象时使用。

在控制器中使用服务器端应用

控制器的开发人员可以把服务器端应用作为简化控制器的更新逻辑的方式。 读-改-写 和/或 patch 的主要区别如下所示:

  • 应用的对象必须包含控制器关注的所有字段。
  • 对于在控制器没有执行过应用操作之前就已经存在的字段,不能删除。 (控制器在这种用例环境下,依然可以发送一个 PATCH/UPDATE)
  • 对象不必事先读取,resourceVersion 不必指定。

强烈推荐:设置控制器在冲突时强制执行,这是因为冲突发生时,它们没有其他解决方案或措施。

转移所有权

除了通过冲突解决方案提供的并发控制, 服务器端应用提供了一些协作方式来将字段所有权从用户转移到控制器。

最好通过例子来说明这一点。 让我们来看看,在使用 Horizo​​ntalPodAutoscaler 资源和与之配套的控制器, 且开启了 Deployment 的自动水平扩展功能之后, 怎么安全的将 replicas 字段的所有权从用户转移到控制器。

假设用户定义了 Deployment,且 replicas 字段已经设置为期望的值:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2

并且,用户使用服务器端应用,像这样创建 Deployment:

kubectl apply -f https://k8s.io/examples/application/ssa/nginx-deployment.yaml --server-side

然后,为 Deployment 启用 HPA,例如:

kubectl autoscale deployment nginx-deployment --cpu-percent=50 --min=1 --max=10

现在,用户希望从他们的配置中删除 replicas,所以他们总是和 HPA 控制器冲突。 然而,这里存在一个竟态: 在 HPA 需要调整 replicas 之前会有一个时间窗口, 如果在 HPA 写入字段成为所有者之前,用户删除了replicas, 那 API 服务器就会把 replicas 的值设为 1, 也就是默认值。 这不是用户希望发生的事情,即使是暂时的。

这里有两个解决方案:

  • (基本操作)把 replicas 留在配置文件中;当 HPA 最终写入那个字段, 系统基于此事件告诉用户:冲突发生了。在这个时间点,可以安全的删除配置文件。
  • (高级操作)然而,如果用户不想等待,比如他们想为合作伙伴保持集群清晰, 那他们就可以执行以下步骤,安全的从配置文件中删除 replicas

首先,用户新定义一个只包含 replicas 字段的配置文件:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 3

用户使用名为 handover-to-hpa 的字段管理器,应用此配置文件。

kubectl apply -f https://k8s.io/examples/application/ssa/nginx-deployment-replicas-only.yaml \
  --server-side --field-manager=handover-to-hpa \
  --validate=false

如果应用操作和 HPA 控制器产生冲突,那什么都不做。 冲突表明控制器在更早的流程中已经对字段声明过所有权。

在此时间点,用户可以从配置文件中删除 replicas

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2

注意,只要 HPA 控制器为 replicas 设置了一个新值, 该临时字段管理器将不再拥有任何字段,会被自动删除。 这里不需要执行清理工作。

在用户之间转移所有权

通过在配置文件中把一个字段设置为相同的值,用户可以在他们之间转移字段的所有权, 从而共享了字段的所有权。 当用户共享了字段的所有权,任何一个用户可以从他的配置文件中删除该字段, 并应用该变更,从而放弃所有权,并实现了所有权向其他用户的转移。

与客户端应用的对比

由服务器端应用实现的冲突检测和解决方案的一个结果就是, 应用者总是可以在本地状态中得到最新的字段值。 如果得不到最新值,下次执行应用操作时就会发生冲突。 解决冲突三个选项的任意一个都会保证:此应用过的配置文件是服务器上对象字段的最新子集。

这和客户端应用(Client Side Apply) 不同,如果有其他用户覆盖了此值, 过期的值被留在了应用者本地的配置文件中。 除非用户更新了特定字段,此字段才会准确, 应用者没有途径去了解下一次应用操作是否会覆盖其他用户的修改。

另一个区别是使用客户端应用的应用者不能改变他们正在使用的 API 版本,但服务器端应用支持这个场景。

从客户端应用升级到服务器端应用

客户端应用方式时,用户使用 kubectl apply 管理资源, 可以通过使用下面标记切换为使用服务器端应用。

kubectl apply --server-side [--dry-run=server]

默认情况下,对象的字段管理从客户端应用方式迁移到 kubectl 触发的服务器端应用时,不会发生冲突。

此操作以 kubectl 作为字段管理器来应用到服务器端应用。 作为例外,可以指定一个不同的、非默认字段管理器停止的这种行为,如下面的例子所示。 对于 kubectl 触发的服务器端应用,默认的字段管理器是 kubectl

kubectl apply --server-side --field-manager=my-manager [--dry-run=server]

从服务器端应用降级到客户端应用

如果你用 kubectl apply --server-side 管理一个资源, 可以直接用 kubectl apply 命令将其降级为客户端应用。

降级之所以可行,这是因为 kubectl server-side apply 会保存最新的 last-applied-configuration 注解。

此操作以 kubectl 作为字段管理器应用到服务器端应用。 作为例外,可以指定一个不同的、非默认字段管理器停止这种行为,如下面的例子所示。 对于 kubectl 触发的服务器端应用,默认的字段管理器是 kubectl

kubectl apply --server-side --field-manager=my-manager [--dry-run=server]

API 端点

启用了服务器端应用特性之后, PATCH 服务端点接受额外的内容类型 application/apply-patch+yaml。 服务器端应用的用户就可以把 YAMl 格式的部分定义对象(partially specified objects)发送到此端点。 当一个配置文件被应用时,它应该包含所有体现你意图的字段。

RBAC 和权限

因为服务器端应用是一种 PATCH 操作,所以一个角色编辑资源需要 PATCH 权限, 但要用服务器端应用创建资源还需要 CREATE 动作权限。

清除 ManagedFields

可以从对象中剥离所有 managedField, 实现方法是通过使用 MergePatchStrategicMergePatchJSONPatchUpdate、以及所有的非应用方式的操作来覆盖它。 这可以通过用空条目覆盖 managedFields 字段的方式实现。以下是两个示例:

PATCH /api/v1/namespaces/default/configmaps/example-cm
Content-Type: application/merge-patch+json
Accept: application/json
Data: {"metadata":{"managedFields": [{}]}}
PATCH /api/v1/namespaces/default/configmaps/example-cm
Content-Type: application/json-patch+json
Accept: application/json
Data: [{"op": "replace", "path": "/metadata/managedFields", "value": [{}]}]

这一操作将用只包含一个空条目的列表覆写 managedFields, 来实现从对象中整个的去除 managedFields。 注意,只把 managedFields 设置为空列表并不会重置字段。 这么做是有目的的,所以 managedFields 将永远不会被与该字段无关的客户删除。

在重置操作结合 managedFields 以外其他字段更改的场景中, 将导致 managedFields 首先被重置,其他改变被押后处理。 其结果是,应用者取得了同一个请求中所有字段的所有权。

2.3 - 客户端库

本页面包含基于各种编程语言使用 Kubernetes API 的客户端库概述。

在使用 Kubernetes REST API 编写应用程序时, 你并不需要自己实现 API 调用和 “请求/响应” 类型。 你可以根据自己的编程语言需要选择使用合适的客户端库。

客户端库通常为你处理诸如身份验证之类的常见任务。 如果 API 客户端在 Kubernetes 集群中运行,大多数客户端库可以发现并使用 Kubernetes 服务帐户进行身份验证, 或者能够理解 kubeconfig 文件 格式来读取凭据和 API 服务器地址。

官方支持的 Kubernetes 客户端库

以下客户端库由 Kubernetes SIG API Machinery 正式维护。

语言客户端库样例程序
Cgithub.com/kubernetes-client/c浏览
dotnetgithub.com/kubernetes-client/csharp浏览
Gogithub.com/kubernetes/client-go/浏览
Haskellgithub.com/kubernetes-client/haskell浏览
Javagithub.com/kubernetes-client/java浏览
JavaScriptgithub.com/kubernetes-client/javascript浏览
Perlgithub.com/kubernetes-client/perl/浏览
Pythongithub.com/kubernetes-client/python/浏览
Rubygithub.com/kubernetes-client/ruby/浏览

社区维护的客户端库

以下 Kubernetes API 客户端库是由社区,而非 Kubernetes 团队支持、维护的。

语言客户端库
Clojuregithub.com/yanatan16/clj-kubernetes-api
DotNetgithub.com/tonnyeremin/kubernetes_gen
DotNet (RestSharp)github.com/masroorhasan/Kubernetes.DotNet
Elixirgithub.com/obmarg/kazan
Elixirgithub.com/coryodaniel/k8s
Gogithub.com/ericchiang/k8s
Java (OSGi)bitbucket.org/amdatulabs/amdatu-kubernetes
Java (Fabric8, OSGi)github.com/fabric8io/kubernetes-client
Javagithub.com/manusa/yakc
Lispgithub.com/brendandburns/cl-k8s
Lispgithub.com/xh4/cube
Node.js (TypeScript)github.com/Goyoo/node-k8s-client
Node.jsgithub.com/ajpauwels/easy-k8s
Node.jsgithub.com/godaddy/kubernetes-client
Node.jsgithub.com/tenxcloud/node-kubernetes-client
Perlmetacpan.org/pod/Net::Kubernetes
PHPgithub.com/allansun/kubernetes-php-client
PHPgithub.com/maclof/kubernetes-client
PHPgithub.com/travisghansen/kubernetes-client-php
PHPgithub.com/renoki-co/php-k8s
Pythongithub.com/fiaas/k8s
Pythongithub.com/gtsystem/lightkube
Pythongithub.com/mnubo/kubernetes-py
Pythongithub.com/tomplus/kubernetes_asyncio
Pythongithub.com/Frankkkkk/pykorm
Rubygithub.com/abonas/kubeclient
Rubygithub.com/k8s-ruby/k8s-ruby
Rubygithub.com/kontena/k8s-client
Rustgithub.com/clux/kube-rs
Rustgithub.com/ynqa/kubernetes-rust
Scalagithub.com/hagay3/skuber
Scalagithub.com/hnaderi/scala-k8s
Scalagithub.com/joan38/kubernetes-client
Swiftgithub.com/swiftkube/client

2.4 - Kubernetes 弃用策略

本文档详细解释系统中各个层面的弃用策略(Deprecation Policy)。

Kubernetes 是一个组件众多、贡献者人数众多的大系统。 就像很多类似的软件,所提供的功能特性集合会随着时间推移而自然发生变化, 而且有时候某个功能特性可能需要被去除。被去除的可能是一个 API、 一个参数标志或者甚至某整个功能特性。为了避免影响到现有用户, Kubernetes 对于其中渐次移除的各个方面规定了一种弃用策略并遵从此策略。

弃用 API 的一部分

由于 Kubernetes 是一个 API 驱动的系统,API 会随着时间推移而演化, 以反映人们对问题空间的认识的变化。Kubernetes API 实际上是一个 API 集合, 其中每个成员称作“API 组(API Group)”,并且每个 API 组都是独立管理版本的。 API 版本会有三类, 每类有不同的废弃策略:

示例分类
v1正式发布(Generally available,GA,稳定版本)
v1beta1Beta (预发布)
v1alpha1Alpha (试验性)

给定的 Kubernetes 发布版本中可以支持任意数量的 API 组,且每组可以包含任意个数的版本。

下面的规则负责指导 API 元素的弃用,具体元素包括:

  • REST 资源(也即 API 对象)
  • REST 资源的字段
  • REST 资源的注解,包含“beta”类注解但不包含“alpha”类注解
  • 枚举值或者常数值
  • 组件配置结构

以下是跨正式发布版本时要实施的规则,不适用于对 master 或发布分支上不同提交之间的变化。

规则 #1:只能在增加 API 组版本号时删除 API 元素。

一旦在某个特定 API 组版本中添加了 API 元素,则该元素不可从该版本中删除, 且其行为也不能大幅度地变化,无论属于哪一类(GA、Alpha 或 Beta)。

规则 #2:在给定的发布版本中,API 对象必须能够在不同的 API 版本之间来回转换且不造成信息丢失,除非整个 REST 资源在某些版本中完全不存在。

例如,一个对象可被用 v1 来写入之后用 v2 来读出并转换为 v1,所得到的 v1 必须与原来的 v1 对象完全相同。v2 中的表现形式可能与 v1 不同,但系统知道如何在两个版本之间执行双向转换。 此外,v2 中添加的所有新字段都必须能够转换为 v1 再转换回来。这意味着 v1 必须添加一个新的等效字段或者将其表现为一个注解。

规则 #3:给定类别的 API 版本不可被弃用以支持稳定性更差的 API 版本。

  • 一个正式发布的(GA)API 版本可替换 Beta 或 Alpha API 版本。
  • Beta API 版本可以替换早期的 Beta 和 Alpha API 版本,但 不可以 替换正式的 API 版本。
  • Alpha API 版本可以替换早期的 Alpha API 版本,但 不可以 替换 Beta 或正式的 API 版本。

规则 #4a:API 生命周期由 API 稳定性级别决定

  • GA API 版本可以被标记为已弃用,但不得在 Kubernetes 的主要版本中删除
  • Beta API 版本在引入后不超过 9 个月或 3 个次要版本(以较长者为准)将被弃用, 并且在弃用后 9 个月或 3 个次要版本(以较长者为准)不再提供服务
  • Alpha API 版本可能会在任何版本中被删除,不另行通知

这确保了 Beta API 支持涵盖了最多 2 个版本的支持版本偏差, 并且这些 API 不会在不稳定的 Beta 版本上停滞不前,积累的生产使用数据将在对 Beta API 的支持结束时中断。

规则 #4b:标记为“preferred(优选的)” API 版本和给定 API 组的 “storage version(存储版本)”在既支持老版本也支持新版本的 Kubernetes 发布版本出来以前不可以提升其版本号。

用户必须能够升级到 Kubernetes 新的发行版本,之后再回滚到前一个发行版本, 且整个过程中无需针对新的 API 版本做任何转换,也不允许出现功能损坏的情况, 除非用户显式地使用了仅在较新版本中才存在的功能特性。 就对象的存储表示而言,这一点尤其是不言自明的。

以上所有规则最好通过例子来说明。假定现有 Kubernetes 发行版本为 X,其中引入了新的 API 组。 大约每隔 4 个月会有一个新的 Kubernetes 版本被发布(每年 3 个版本)。 下面的表格描述了在一系列后续的发布版本中哪些 API 版本是受支持的。

发布版本API 版本优选/存储版本注释
Xv1alpha1v1alpha1
X+1v1alpha2v1alpha2
  • v1alpha1 被去除,发布说明中会包含 "action required(采取行动)" 说明
X+2v1beta1v1beta1
  • v1alpha2 被去除,发布说明中包含对应的 "action required(采取行动)" 说明
X+3v1beta2、v1beta1(已弃用)v1beta1
  • v1beta1 被弃用,发布说明中包含对应的 "action required(采取行动)" 说明
X+4v1beta2、v1beta1(已弃用)v1beta2
X+5v1、v1beta1(已弃用)、v1beta2(已弃用)v1beta2
  • v1beta2 被弃用,发布说明中包含对应的 "action required(采取行动)" 说明
X+6v1、v1beta2(已弃用)v1
  • v1beta1 被去除,发布说明中包含对应的 "action required(采取行动)" 说明
X+7v1、v1beta2(已弃用)v1
X+8v2alpha1、v1v1
  • v1beta2 被去除,发布说明中包含对应的 "action required(采取行动)" 说明
X+9v2alpha2、v1v1
  • v2alpha1 被删除,发布说明中包含对应的 "action required(采取行动)" 说明
X+10v2beta1、v1v1
  • v2alpha2 被删除,发布说明中包含对应的 "action required(采取行动)" 说明
X+11v2beta2、v2beta1(已弃用)、v1v1
  • v2beta1 被弃用,发布说明中包含对应的 "action required(采取行动)" 说明
X+12v2、v2beta2(已弃用)、v2beta1(已弃用)、v1(已弃用)v1
  • v2beta2 已被弃用,发布说明中包含对应的 "action required(采取行动)" 说明
  • v1 已被弃用,取而代之的是 v2,但不会被删除
X+13v2、v2beta1(已弃用)、v2beta2(已弃用)、v1(已弃用)v2
X+14v2、v2beta2(已弃用)、v1(已弃用)v2
  • v2beta1 被删除,发布说明中包含对应的 "action required(采取行动)" 说明
X+15v2、v1(已弃用)v2
  • v2beta2 被删除,发布说明中包含对应的 "action required(采取行动)" 说明

REST 资源(也即 API 对象)

考虑一个假想的名为 Widget 的 REST 资源,在上述时间线中位于 API v1,而现在打算将其弃用。 我们会在文档和公告中与 X+1 版本的发布同步记述此弃用决定。 Wdiget 资源仍会在 API 版本 v1(已弃用)中存在,但不会出现在 v2alpha1 中。 Widget 资源会 X+8 发布版本之前(含 X+8)一直存在并可用。 只有在发布版本 X+9 中,API v1 寿终正寝时,Widget 才彻底消失,相应的资源行为也被移除。

从 Kubernetes v1.19 开始,当 API 请求被发送到一个已弃用的 REST API 末端时:

  1. API 响应中会包含一个 Warning 头部字段(如 RFC7234 5.5 节所定义);

  2. 该请求会导致对应的审计事件中会增加一个注解 "k8s.io/deprecated":"true"

  3. kube-apiserver 进程的 apiserver_requested_deprecated_apis 度量值会被设置为 1。 该度量值还附带 groupversionresourcesubresource 标签 (可供添加到度量值 apiserver_request_total 上), 和一个 removed_release 标签,标明该 API 将消失的 Kubernetes 发布版本。 下面的 Prometheus 查询会返回对 v1.22 中将移除的、已弃用的 API 的请求的信息:

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

REST 资源的字段

就像整个 REST 资源一样,在 API v1 中曾经存在的各个字段在 API v1 被移除之前必须一直存在且起作用。 与整个资源上的规定不同,v2 API 可以选择为字段提供不同的表示方式, 只要对应的资源对象可在不同版本之间来回转换即可。 例如,v1 版本中一个名为 "magnitude" 的已弃用字段可能在 API v2 中被命名为 "deprecatedMagnitude"。 当 v1 最终被移除时,废弃的字段也可以从 v2 中移除。

枚举值或常数值

就像前文讲述的 REST 资源及其中的单个字段一样,API v1 中所支持的常数值必须在 API v1 被移除之前一直存在且起作用。

组件配置结构

组件的配置也是有版本的,并且按 REST 资源的方式来管理。

将来的工作

随着时间推移,Kubernetes 会引入粒度更细的 API 版本。 到那时,这里的规则会根据需要进行调整。

弃用一个标志或 CLI 命令

Kubernetes 系统中包含若干不同的、相互协作的程序。 有时,Kubernetes 可能会删除这些程序的某些标志或 CLI 命令(统称“命令行元素”)。 这些程序可以天然地划分到两个大组中:面向用户的和面向管理员的程序。 二者之间的弃用策略略有不同。 除非某个标志显示地通过前缀或文档来标明其为“alpha”或“beta”, 该标志要被视作正式发布的(GA)。

命令行元素相当于系统的 API 的一部分,不过因为它们并没有采用 REST API 一样的方式来管理版本,其弃用规则规定如下:

规则 #5a:面向用户的命令行元素(例如,kubectl)必须在其宣布被弃用其在以下时长内仍能使用:

  • GA:12 个月或者 2 个发布版本(取其较长者)
  • Beta:3 个月或者 1 个发布版本(取其较长者)
  • Alpha:0 发布版本

规则 #5b:面向管理员的命令行元素(例如,kubelet)必须在其被宣布弃用之后以下时长内保持可用:

  • GA:6 个月或 1 个发行版本(取其较长者)
  • Beta: 3 个月或 1 个发行版本(取其较长者)
  • Alpha: 0 个发布版本

规则 #6:被弃用的 CLI 元素在被用到时必须能够产生警告,而警告的产生是可以被禁止的。

弃用某功能特性或行为

在一些较偶然的情形下,某 Kubernetes 发行版本需要弃用系统的某项功能特性或者行为, 而对应的功能特性或行为并不受 API 或 CLI 控制。在这种情况下,其弃用规则如下:

规则 #7:被弃用的行为必须在被宣布弃用之后至少 1 年时间内必须保持能用。

这并不意味着对系统的所有更改都受此策略约束。 此规则仅适用于重大的、用户可见的行为;这些行为会影响到在 Kubernetes 中运行的应用的正确性,或者影响到 Kubernetes 集群的管理。 此规则也适用于那些被整个移除的功能特性或行为。

上述规则的一个例外是 特性门控(Feature Gate)。特性门控是一些键值偶对, 允许用户启用或禁用一些试验性的功能特性。

特性门控意在覆盖功能特性的整个开发周期,它们无意成为长期的 API。 因此,它们会在某功能特性正式发布或被抛弃之后被弃用和删除。

随着一个功能特性经过不同的成熟阶段,相关的特性门控也会演化。 与功能特性生命周期对应的特性门控状态为:

  • Alpha:特性门控默认被禁用,只能由用户显式启用。
  • Beta:特性门控默认被弃用,可被用户显式禁用。
  • GA: 特性门控被弃用(参见弃用),并且不再起作用。
  • GA,弃用窗口期结束:特性门控被删除,不再接受调用。

弃用

功能特性在正式发布之前的生命周期内任何时间点都可被移除。 当未正式发布的功能特性被移除时,它们对应的特性门控也被弃用。

当尝试禁用一个不再起作用的特性门控时,该调用会失败,这样可以避免毫无迹象地执行一些不受支持的场景。

在某些场合,移除一个即将正式发布的功能特性需要很长时间。 特性门控可以保持其功能,直到对应的功能特性被彻底去除,直到那时特性门控自身才可被弃用。

由于移除一个已经正式发布的功能特性对应的特性门控也需要一定时间,对特性门控的调用可能一直被允许, 前提是特性门控对功能本身无影响且特性门控不会引发任何错误。

意在允许用户禁用的功能特性应该包含一个在相关联的特性门控中禁用该功能的机制。

特性门控的版本管理与之前讨论的组件版本管理不同,因此其对应的弃用策略如下:

规则 #8:特性门控所对应的功能特性经历下面所列的成熟性阶段转换时,特性门控必须被弃用。 特性门控弃用时必须在以下时长内保持其功能可用:

  • Beta 特性转为 GA:6 个月或者 2 个发布版本(取其较长者)
  • Beta 特性转为丢弃:3 个月或者 1 个发布版本(取其较长者)
  • Alpha 特性转为丢弃:0 个发布版本

规则 #9:已弃用的特色门控再被使用时必须给出警告回应。当特性门控被弃用时, 必须在发布说明和对应的 CLI 帮助信息中通过文档宣布。 警告信息和文档都要标明是否某特性门控不再起作用。

弃用度量值

Kubernetes 控制平面的每个组件都公开度量值(通常是 /metrics 端点),它们通常由集群管理员使用。 并不是所有的度量值都是同样重要的:一些度量值通常用作 SLIs 或被使用来确定 SLOs,这些往往比较重要。 其他度量值在本质上带有实验性,或者主要用于 Kubernetes 开发过程。

因此,度量值分为两个稳定性类别(ALPHASTABLE); 此分类会影响在 Kubernetes 发布版本中移除某度量值。 所对应的分类取决于对该度量值重要性的预期。 弃用和移除度量值的规则如下:

规则 #9a: 对于相应的稳定性类别,度量值起作用的周期必须不小于:

  • STABLE: 4 个发布版本或者 12 个月 (取其较长者)
  • ALPHA: 0 个发布版本

规则 #9b: 在度量值被宣布启用之后,它起作用的周期必须不小于:

  • STABLE: 3 个发布版本或者 9 个月 (取其较长者)
  • ALPHA: 0 个发布版本

已弃用的度量值将在其描述文本前加上一个已弃用通知字符串 '(Deprecated from x.y)', 并将在度量值被记录期间发出警告日志。就像稳定的、未被弃用的度量指标一样, 被弃用的度量值将自动注册到 metrics 端点,因此被弃用的度量值也是可见的。

在随后的版本中(当度量值 deprecatedVersion 等于 当前 Kubernetes 版本 - 3), 被弃用的度量值将变成 隐藏(Hidden) metric 度量值。 与被弃用的度量值不同,隐藏的度量值将不再被自动注册到 metrics 端点(因此被隐藏)。 但是,它们可以通过可执行文件的命令行标志显式启用 (--show-hidden-metrics-for-version=)。 如果集群管理员不能对早期的弃用警告作出反应,这一设计就为他们提供了抓紧迁移弃用度量值的途径。 隐藏的度量值应该在再过一个发行版本后被删除。

例外

没有策略可以覆盖所有情况。此策略文档是一个随时被更新的文档,会随着时间推移演化。 在实践中,会有一些情况无法很好地匹配到这里的弃用策略, 或者这里的策略变成了很严重的羁绊。这类情形要与 SIG 和项目领导讨论, 寻求对应场景的最佳解决方案。请一直铭记,Kubernetes 承诺要成为一个稳定的系统, 至少会尽力做到不会影响到其用户。此弃用策略的任何例外情况都会在所有相关的发布说明中公布。

2.5 - 已弃用 API 的迁移指南

随着 Kubernetes API 的演化,APIs 会周期性地被重组或升级。 当 APIs 演化时,老的 API 会被弃用并被最终删除。 本页面包含你在将已弃用 API 版本迁移到新的更稳定的 API 版本时需要了解的知识。

各发行版本中移除的 API

v1.27

v1.27 发行版本中将去除以下已弃用的 API 版本:

CSIStorageCapacity

storage.k8s.io/v1beta1 API版本的 CSIStorageCapacity 将不再在 v1.27 提供。

  • 自 v1.24 版本起,迁移清单和 API 客户端使用 storage.k8s.io/v1 API 版本
  • 所有现有的持久化对象都可以通过新的 API 访问
  • 没有需要额外注意的变更

v1.26

v1.26 发行版本中将去除以下已弃用的 API 版本:

流控制资源

flowcontrol.apiserver.k8s.io/v1beta1 API 版本的 FlowSchema 和 PriorityLevelConfiguration 将不会在 v1.26 中提供。

  • 迁移清单和 API 客户端使用 flowcontrol.apiserver.k8s.io/v1beta2 API 版本, 此 API 从 v1.23 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • 没有需要额外注意的变更

HorizontalPodAutoscaler

autoscaling/v2beta2 API 版本的 HorizontalPodAutoscaler 将不会在 v1.26 版本中提供。

  • 迁移清单和 API 客户端使用 autoscaling/v2 API 版本, 此 API 从 v1.23 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;

v1.25

v1.25 发行版本将停止提供以下已废弃 API 版本:

CronJob

batch/v1beta1 API 版本的 CronJob 将不会在 v1.25 版本中继续提供。

  • 迁移清单和 API 客户端使用 batch/v1 API 版本,此 API 从 v1.21 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • 没有需要额外注意的变更

EndpointSlice

discovery.k8s.io/v1beta1 API 版本的 EndpointSlice 将不会在 v1.25 版本中继续提供。

  • 迁移清单和 API 客户端使用 discovery.k8s.io/v1 API 版本,此 API 从 v1.21 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • discovery.k8s.io/v1 中值得注意的变更有:
    • 使用每个 Endpoint 的 nodeName 字段而不是已被弃用的 topology["kubernetes.io/hostname"] 字段;
    • 使用每个 Endpoint 的 zone 字段而不是已被弃用的 topology["kubernetes.io/zone"] 字段;
    • topology 字段被替换为 deprecatedTopology,并且在 v1 版本中不可写入。

Event

events.k8s.io/v1beta1 API 版本的 Event 将不会在 v1.25 版本中继续提供。

  • 迁移清单和 API 客户端使用 events.k8s.io/v1 API 版本,此 API 从 v1.19 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • events.k8s.io/v1 中值得注意的变更有:
    • type 字段只能设置为 NormalWarning 之一;
    • involvedObject 字段被更名为 regarding
    • actionreasonreportingControllerreportingInstance 字段 在创建新的 events.k8s.io/v1 版本 Event 时都是必需的字段;
    • 使用 eventTime 而不是已被弃用的 firstTimestamp 字段 (该字段已被更名为 deprecatedFirstTimestamp,且不允许出现在新的 events.k8s.io/v1 Event 对象中);
    • 使用 series.lastObservedTime 而不是已被弃用的 lastTimestamp 字段 (该字段已被更名为 deprecatedLastTimestamp,且不允许出现在新的 events.k8s.io/v1 Event 对象中);
    • 使用 series.count 而不是已被弃用的 count 字段 (该字段已被更名为 deprecatedCount,且不允许出现在新的 events.k8s.io/v1 Event 对象中);
    • 使用 reportingController 而不是已被弃用的 source.component 字段 (该字段已被更名为 deprecatedSource.component,且不允许出现在新的 events.k8s.io/v1 Event 对象中);
    • 使用 reportingInstance 而不是已被弃用的 source.host 字段 (该字段已被更名为 deprecatedSource.host,且不允许出现在新的 events.k8s.io/v1 Event 对象中)。

HorizontalPodAutoscaler

autoscaling/v2beta1 API 版本的 HorizontalPodAutoscaler 将不会在 v1.25 版本中继续提供。

  • 迁移清单和 API 客户端使用 autoscaling/v2 API 版本,此 API 从 v1.23 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;

PodDisruptionBudget

policy/v1beta1 API 版本的 PodDisruptionBudget 将不会在 v1.25 版本中继续提供。

  • 迁移清单和 API 客户端使用 policy/v1 API 版本,此 API 从 v1.21 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • policy/v1 中需要额外注意的变更有:
    • policy/v1 版本的 PodDisruptionBudget 中将 spec.selector 设置为空({})时会选择名字空间中的所有 Pods(在 policy/v1beta1 版本中,空的 spec.selector 不会选择任何 Pods)。如果 spec.selector 未设置,则在两个 API 版本下都不会选择任何 Pods。

PodSecurityPolicy

policy/v1beta1 API 版本中的 PodSecurityPolicy 将不会在 v1.25 中提供, 并且 PodSecurityPolicy 准入控制器也会被删除。

迁移到 Pod 安全准入第三方准入 webhook。 有关迁移指南,请参阅从 PodSecurityPolicy 迁移到内置 PodSecurity 准入控制器。 有关弃用的更多信息,请参阅 PodSecurityPolicy 弃用:过去、现在和未来

RuntimeClass

node.k8s.io/v1beta1 API 版本中的 RuntimeClass 将不会在 v1.25 中提供。

  • 迁移清单和 API 客户端使用 node.k8s.io/v1 API 版本,此 API 从 v1.20 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • 没有需要额外注意的变更

v1.22

v1.22 发行版本停止提供以下已废弃 API 版本:

Webhook 资源

admissionregistration.k8s.io/v1beta1 API 版本的 MutatingWebhookConfiguration 和 ValidatingWebhookConfiguration 不在 v1.22 版本中继续提供。

  • 迁移清单和 API 客户端使用 admissionregistration.k8s.io/v1 API 版本, 此 API 从 v1.16 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • 值得注意的变更:
    • webhooks[*].failurePolicy 在 v1 版本中默认值从 Ignore 改为 Fail
    • webhooks[*].matchPolicy 在 v1 版本中默认值从 Exact 改为 Equivalent
    • webhooks[*].timeoutSeconds 在 v1 版本中默认值从 30s 改为 10s
    • webhooks[*].sideEffects 的默认值被删除,并且该字段变为必须指定; 在 v1 版本中可选的值只能是 NoneNoneOnDryRun 之一
    • webhooks[*].admissionReviewVersions 的默认值被删除,在 v1 版本中此字段变为必须指定(AdmissionReview 的被支持版本包括 v1v1beta1
    • webhooks[*].name 必须在通过 admissionregistration.k8s.io/v1 创建的对象列表中唯一

CustomResourceDefinition

apiextensions.k8s.io/v1beta1 API 版本的 CustomResourceDefinition 不在 v1.22 版本中继续提供。

  • 迁移清单和 API 客户端使用 apiextensions/v1 API 版本,此 API 从 v1.16 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • 值得注意的变更:
    • spec.scope 的默认值不再是 Namespaced,该字段必须显式指定
    • spec.version 在 v1 版本中被删除;应改用 spec.versions
    • spec.validation 在 v1 版本中被删除;应改用 spec.versions[*].schema
    • spec.subresources 在 v1 版本中被删除;应改用 spec.versions[*].subresources
    • spec.additionalPrinterColumns 在 v1 版本中被删除;应改用 spec.versions[*].additionalPrinterColumns
    • spec.conversion.webhookClientConfig 在 v1 版本中被移动到 spec.conversion.webhook.clientConfig
    * `spec.conversion.conversionReviewVersions` 在 v1 版本中被移动到
    `spec.conversion.webhook.conversionReviewVersions`
    
    • spec.versions[*].schema.openAPIV3Schema 在创建 v1 版本的 CustomResourceDefinition 对象时变成必需字段,并且其取值必须是一个 结构化的 Schema
    • spec.preserveUnknownFields: true 在创建 v1 版本的 CustomResourceDefinition 对象时不允许指定;该配置必须在 Schema 定义中使用 x-kubernetes-preserve-unknown-fields: true 来设置
    • 在 v1 版本中,additionalPrinterColumns 的条目中的 JSONPath 字段被更名为 jsonPath(补丁 #66531

APIService

apiregistration/v1beta1 API 版本的 APIService 不在 v1.22 版本中继续提供。

  • 迁移清单和 API 客户端使用 apiregistration.k8s.io/v1 API 版本,此 API 从 v1.10 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • 没有需要额外注意的变更

TokenReview

authentication.k8s.io/v1beta1 API 版本的 TokenReview 不在 v1.22 版本中继续提供。

  • 迁移清单和 API 客户端使用 authentication.k8s.io/v1 API 版本,此 API 从 v1.6 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • 没有需要额外注意的变更

SubjectAccessReview resources

authorization.k8s.io/v1beta1 API 版本的 LocalSubjectAccessReview、 SelfSubjectAccessReview、SubjectAccessReview、SelfSubjectRulesReview 不在 v1.22 版本中继续提供。

  • 迁移清单和 API 客户端使用 authorization.k8s.io/v1 API 版本,此 API 从 v1.6 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • 需要额外注意的变更:
    • spec.group 在 v1 版本中被更名为 spec.groups (补丁 #32709

CertificateSigningRequest

certificates.k8s.io/v1beta1 API 版本的 CertificateSigningRequest 不在 v1.22 版本中继续提供。

  • 迁移清单和 API 客户端使用 certificates.k8s.io/v1 API 版本,此 API 从 v1.19 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • certificates.k8s.io/v1 中需要额外注意的变更:
    • 对于请求证书的 API 客户端而言:
      • spec.signerName 现在变成必需字段(参阅 已知的 Kubernetes 签署者), 并且通过 certificates.k8s.io/v1 API 不可以创建签署者为 kubernetes.io/legacy-unknown 的请求
      • spec.usages 现在变成必需字段,其中不可以包含重复的字符串值, 并且只能包含已知的用法字符串
    • 对于要批准或者签署证书的 API 客户端而言:
      • status.conditions 中不可以包含重复的类型
      • status.conditions[*].status 字段现在变为必需字段
      • status.certificate 必须是 PEM 编码的,而且其中只能包含 CERTIFICATE 数据块

Lease

coordination.k8s.io/v1beta1 API 版本的 Lease 不在 v1.22 版本中继续提供。

  • 迁移清单和 API 客户端使用 coordination.k8s.io/v1 API 版本,此 API 从 v1.14 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • 没有需要额外注意的变更

Ingress

extensions/v1beta1networking.k8s.io/v1beta1 API 版本的 Ingress 不在 v1.22 版本中继续提供。

  • 迁移清单和 API 客户端使用 networking.k8s.io/v1 API 版本,此 API 从 v1.19 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • 值得注意的变更:
    • spec.backend 字段被更名为 spec.defaultBackend
    • 后端的 serviceName 字段被更名为 service.name
    • 数值表示的后端 servicePort 字段被更名为 service.port.number
    • 字符串表示的后端 servicePort 字段被更名为 service.port.name
    • 对所有要指定的路径,pathType 都成为必需字段。 可选项为 PrefixExactImplementationSpecific。 要匹配 v1beta1 版本中未定义路径类型时的行为,可使用 ImplementationSpecific

IngressClass

networking.k8s.io/v1beta1 API 版本的 IngressClass 不在 v1.22 版本中继续提供。

  • 迁移清单和 API 客户端使用 networking.k8s.io/v1 API 版本,此 API 从 v1.19 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • 没有需要额外注意的变更

RBAC 资源

rbac.authorization.k8s.io/v1beta1 API 版本的 ClusterRole、ClusterRoleBinding、 Role 和 RoleBinding 不在 v1.22 版本中继续提供。

  • 迁移清单和 API 客户端使用 rbac.authorization.k8s.io/v1 API 版本,此 API 从 v1.8 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • 没有需要额外注意的变更

PriorityClass

scheduling.k8s.io/v1beta1 API 版本的 PriorityClass 不在 v1.22 版本中继续提供。

  • 迁移清单和 API 客户端使用 scheduling.k8s.io/v1 API 版本,此 API 从 v1.14 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • 没有需要额外注意的变更

存储资源

storage.k8s.io/v1beta1 API 版本的 CSIDriver、CSINode、StorageClass 和 VolumeAttachment 不在 v1.22 版本中继续提供。

  • 迁移清单和 API 客户端使用 storage.k8s.io/v1 API 版本
    • CSIDriver 从 v1.19 版本开始在 storage.k8s.io/v1 中提供;
    • CSINode 从 v1.17 版本开始在 storage.k8s.io/v1 中提供;
    • StorageClass 从 v1.6 版本开始在 storage.k8s.io/v1 中提供;
    • VolumeAttachment 从 v1.13 版本开始在 storage.k8s.io/v1 中提供;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • 没有需要额外注意的变更

v1.16

v1.16 发行版本停止提供以下已废弃 API 版本:

NetworkPolicy

extensions/v1beta1 API 版本的 NetworkPolicy 不在 v1.16 版本中继续提供。

  • 迁移清单和 API 客户端使用 networking.k8s.io/v1 API 版本,此 API 从 v1.8 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;

DaemonSet

extensions/v1beta1apps/v1beta2 API 版本的 DaemonSet 在 v1.16 版本中不再继续提供。

  • 迁移清单和 API 客户端使用 apps/v1 API 版本,此 API 从 v1.9 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • 值得注意的变更:
    • spec.templateGeneration 字段被删除
    • spec.selector 现在变成必需字段,并且在对象创建之后不可变更; 可以将现有模板的标签作为选择算符以实现无缝迁移。
    • spec.updateStrategy.type 的默认值变为 RollingUpdateextensions/v1beta1 API 版本中的默认值是 OnDelete)。

Deployment

extensions/v1beta1apps/v1beta1apps/v1beta2 API 版本的 Deployment 在 v1.16 版本中不再继续提供。

  • 迁移清单和 API 客户端使用 apps/v1 API 版本,此 API 从 v1.9 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • 值得注意的变更:
    • spec.rollbackTo 字段被删除
    • spec.selector 字段现在变为必需字段,并且在 Deployment 创建之后不可变更; 可以使用现有的模板的标签作为选择算符以实现无缝迁移。
    • spec.progressDeadlineSeconds 的默认值变为 600 秒 (extensions/v1beta1 中的默认值是没有期限)
    • spec.revisionHistoryLimit 的默认值变为 10apps/v1beta1 API 版本中此字段默认值为 2,在extensions/v1beta1 API 版本中的默认行为是保留所有历史记录)。
    • maxSurgemaxUnavailable 的默认值变为 25% (在 extensions/v1beta1 API 版本中,这些字段的默认值是 1)。

StatefulSet

apps/v1beta1apps/v1beta2 API 版本的 StatefulSet 在 v1.16 版本中不再继续提供。

  • 迁移清单和 API 客户端使用 apps/v1 API 版本,此 API 从 v1.9 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • 值得注意的变更:
    • spec.selector 字段现在变为必需字段,并且在 StatefulSet 创建之后不可变更; 可以使用现有的模板的标签作为选择算符以实现无缝迁移。
    • spec.updateStrategy.type 的默认值变为 RollingUpdateapps/v1beta1 API 版本中的默认值是 OnDelete)。

ReplicaSet

extensions/v1beta1apps/v1beta1apps/v1beta2 API 版本的 ReplicaSet 在 v1.16 版本中不再继续提供。

  • 迁移清单和 API 客户端使用 apps/v1 API 版本,此 API 从 v1.9 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • 值得注意的变更:
    • spec.selector 现在变成必需字段,并且在对象创建之后不可变更; 可以将现有模板的标签作为选择算符以实现无缝迁移。

PodSecurityPolicy

extensions/v1beta1 API 版本的 PodSecurityPolicy 在 v1.16 版本中不再继续提供。

  • 迁移清单和 API 客户端使用 policy/v1beta1 API 版本,此 API 从 v1.10 版本开始可用;
  • 注意 policy/v1beta1 API 版本的 PodSecurityPolicy 会在 v1.25 版本中移除。

需要做什么

在禁用已启用 API 的情况下执行测试

你可以通过在启动 API 服务器时禁用特定的 API 版本来模拟即将发生的 API 移除,从而完成测试。在 API 服务器启动参数中添加如下标志:

--runtime-config=<group>/<version>=false

例如:

--runtime-config=admissionregistration.k8s.io/v1beta1=false,apiextensions.k8s.io/v1beta1,...

定位何处使用了已弃用的 API

使用 client warnings, metrics, and audit information available in 1.19+ 来定位在何处使用了已弃用的 API。

迁移到未被弃用的 API

  • 更新自定义的集成组件和控制器,调用未被弃用的 API
  • 更改 YAML 文件引用未被弃用的 API

你可以用 kubectl-convert 命令(在 v1.20 之前是 kubectl convert) 来自动转换现有对象:

kubectl-convert -f <file> --output-version <group>/<version>.

例如,要将较老的 Deployment 版本转换为 apps/v1 版本,你可以运行

kubectl-convert -f ./my-deployment.yaml --output-version apps/v1

需要注意的是这种操作使用的默认值可能并不理想。 要进一步了解某个特定资源,可查阅 Kubernetes API 参考

2.6 - Kubernetes API 健康端点

Kubernetes API 服务器 提供 API 端点以指示 API 服务器的当前状态。 本文描述了这些 API 端点,并说明如何使用。

API 健康端点

Kubernetes API 服务器提供 3 个 API 端点(healthzlivezreadyz)来表明 API 服务器的当前状态。 healthz 端点已被弃用(自 Kubernetes v1.16 起),你应该使用更为明确的 livezreadyz 端点。 livez 端点可与 --livez-grace-period 标志一起使用,来指定启动持续时间。 为了正常关机,你可以使用 /readyz 端点并指定 --shutdown-delay-duration 标志。 检查 API 服务器的 healthz/livez/readyz 端点的机器应依赖于 HTTP 状态代码。 状态码 200 表示 API 服务器是 healthylive 还是 ready,具体取决于所调用的端点。 以下更详细的选项供操作人员使用,用来调试其集群或了解 API 服务器的状态。

以下示例将显示如何与运行状况 API 端点进行交互。

对于所有端点,都可以使用 verbose 参数来打印检查项以及检查状态。 这对于操作人员调试 API 服务器的当前状态很有用,这些不打算给机器使用:

curl -k https://localhost:6443/livez?verbose

或从具有身份验证的远程主机:

kubectl get --raw='/readyz?verbose'

输出将如下所示:

[+]ping ok
[+]log ok
[+]etcd ok
[+]poststarthook/start-kube-apiserver-admission-initializer ok
[+]poststarthook/generic-apiserver-start-informers ok
[+]poststarthook/start-apiextensions-informers ok
[+]poststarthook/start-apiextensions-controllers ok
[+]poststarthook/crd-informer-synced ok
[+]poststarthook/bootstrap-controller ok
[+]poststarthook/rbac/bootstrap-roles ok
[+]poststarthook/scheduling/bootstrap-system-priority-classes ok
[+]poststarthook/start-cluster-authentication-info-controller ok
[+]poststarthook/start-kube-aggregator-informers ok
[+]poststarthook/apiservice-registration-controller ok
[+]poststarthook/apiservice-status-available-controller ok
[+]poststarthook/kube-apiserver-autoregistration ok
[+]autoregister-completion ok
[+]poststarthook/apiservice-openapi-controller ok
healthz check passed

Kubernetes API 服务器也支持排除特定的检查项。 查询参数也可以像以下示例一样进行组合:

curl -k 'https://localhost:6443/readyz?verbose&exclude=etcd'

输出显示排除了 etcd 检查:

[+]ping ok
[+]log ok
[+]etcd excluded: ok
[+]poststarthook/start-kube-apiserver-admission-initializer ok
[+]poststarthook/generic-apiserver-start-informers ok
[+]poststarthook/start-apiextensions-informers ok
[+]poststarthook/start-apiextensions-controllers ok
[+]poststarthook/crd-informer-synced ok
[+]poststarthook/bootstrap-controller ok
[+]poststarthook/rbac/bootstrap-roles ok
[+]poststarthook/scheduling/bootstrap-system-priority-classes ok
[+]poststarthook/start-cluster-authentication-info-controller ok
[+]poststarthook/start-kube-aggregator-informers ok
[+]poststarthook/apiservice-registration-controller ok
[+]poststarthook/apiservice-status-available-controller ok
[+]poststarthook/kube-apiserver-autoregistration ok
[+]autoregister-completion ok
[+]poststarthook/apiservice-openapi-controller ok
[+]shutdown ok
healthz check passed

独立健康检查

特性状态: Kubernetes v1.25 [alpha]

每个单独的健康检查都会公开一个 HTTP 端点,并且可以单独检查。 单个运行状况检查的模式为 /livez/<healthcheck-name>,其中 livezreadyz 表明你要检查的是 API 服务器是否存活或就绪。 <healthcheck-name> 的路径可以通过上面的 verbose 参数发现 ,并采用 [+]ok 之间的路径。 这些单独的健康检查不应由机器使用,但对于操作人员调试系统而言,是有帮助的:

curl -k https://localhost:6443/livez/etcd

3.1 - 用户认证

本页提供身份认证有关的概述。

Kubernetes 中的用户

所有 Kubernetes 集群都有两类用户:由 Kubernetes 管理的服务账号和普通用户。

Kubernetes 假定普通用户是由一个与集群无关的服务通过以下方式之一进行管理的:

  • 负责分发私钥的管理员
  • 类似 Keystone 或者 Google Accounts 这类用户数据库
  • 包含用户名和密码列表的文件

有鉴于此,Kubernetes 并不包含用来代表普通用户账号的对象。 普通用户的信息无法通过 API 调用添加到集群中。

尽管无法通过 API 调用来添加普通用户, Kubernetes 仍然认为能够提供由集群的证书机构签名的合法证书的用户是通过身份认证的用户。 基于这样的配置,Kubernetes 使用证书中的 'subject' 的通用名称(Common Name)字段 (例如,"/CN=bob")来确定用户名。 接下来,基于角色访问控制(RBAC)子系统会确定用户是否有权针对某资源执行特定的操作。 进一步的细节可参阅证书请求 下普通用户主题。

与此不同,服务账号是 Kubernetes API 所管理的用户。它们被绑定到特定的名字空间, 或者由 API 服务器自动创建,或者通过 API 调用创建。服务账号与一组以 Secret 保存的凭据相关,这些凭据会被挂载到 Pod 中,从而允许集群内的进程访问 Kubernetes API。

API 请求则或者与某普通用户相关联,或者与某服务账号相关联, 亦或者被视作匿名请求。这意味着集群内外的每个进程在向 API 服务器发起请求时都必须通过身份认证,否则会被视作匿名用户。这里的进程可以是在某工作站上输入 kubectl 命令的操作人员,也可以是节点上的 kubelet 组件,还可以是控制面的成员。

身份认证策略

Kubernetes 通过身份认证插件利用客户端证书、持有者令牌(Bearer Token)或身份认证代理(Proxy) 来认证 API 请求的身份。HTTP 请求发给 API 服务器时,插件会将以下属性关联到请求本身:

  • 用户名:用来辩识最终用户的字符串。常见的值可以是 kube-adminjane@example.com
  • 用户 ID:用来辩识最终用户的字符串,旨在比用户名有更好的一致性和唯一性。
  • 用户组:取值为一组字符串,其中各个字符串用来标明用户是某个命名的用户逻辑集合的成员。 常见的值可能是 system:masters 或者 devops-team 等。
  • 附加字段:一组额外的键-值映射,键是字符串,值是一组字符串; 用来保存一些鉴权组件可能觉得有用的额外信息。

所有(属性)值对于身份认证系统而言都是不透明的, 只有被鉴权组件解释过之后才有意义。

你可以同时启用多种身份认证方法,并且你通常会至少使用两种方法:

  • 针对服务账号使用服务账号令牌
  • 至少另外一种方法对用户的身份进行认证

当集群中启用了多个身份认证模块时,第一个成功地对请求完成身份认证的模块会直接做出评估决定。 API 服务器并不保证身份认证模块的运行顺序。

对于所有通过身份认证的用户,system:authenticated 组都会被添加到其组列表中。

与其它身份认证协议(LDAP、SAML、Kerberos、X509 的替代模式等等) 都可以通过使用一个身份认证代理身份认证 Webhoook 来实现。

X509 客户证书

通过给 API 服务器传递 --client-ca-file=SOMEFILE 选项,就可以启动客户端证书身份认证。 所引用的文件必须包含一个或者多个证书机构,用来验证向 API 服务器提供的客户端证书。 如果提供了客户端证书并且证书被验证通过,则 subject 中的公共名称(Common Name) 就被作为请求的用户名。 自 Kubernetes 1.4 开始,客户端证书还可以通过证书的 organization 字段标明用户的组成员信息。 要包含用户的多个组成员信息,可以在证书中包含多个 organization 字段。

例如,使用 openssl 命令行工具生成一个证书签名请求:

openssl req -new -key jbeda.pem -out jbeda-csr.pem -subj "/CN=jbeda/O=app1/O=app2"

此命令将使用用户名 jbeda 生成一个证书签名请求(CSR),且该用户属于 "app1" 和 "app2" 两个用户组。

参阅管理证书了解如何生成客户端证书。

静态令牌文件

当 API 服务器的命令行设置了 --token-auth-file=SOMEFILE 选项时,会从文件中读取持有者令牌。 目前,令牌会长期有效,并且在不重启 API 服务器的情况下无法更改令牌列表。

令牌文件是一个 CSV 文件,包含至少 3 个列:令牌、用户名和用户的 UID。 其余列被视为可选的组名。

在请求中放入持有者令牌

当使用持有者令牌来对某 HTTP 客户端执行身份认证时,API 服务器希望看到一个名为 Authorization 的 HTTP 头,其值格式为 Bearer <token>。 持有者令牌必须是一个可以放入 HTTP 头部值字段的字符序列,至多可使用 HTTP 的编码和引用机制。 例如:如果持有者令牌为 31ada4fd-adec-460c-809a-9e56ceb75269,则其出现在 HTTP 头部时如下所示:

Authorization: Bearer 31ada4fd-adec-460c-809a-9e56ceb75269

启动引导令牌

特性状态: Kubernetes v1.18 [stable]

为了支持平滑地启动引导新的集群,Kubernetes 包含了一种动态管理的持有者令牌类型, 称作 启动引导令牌(Bootstrap Token)。 这些令牌以 Secret 的形式保存在 kube-system 名字空间中,可以被动态管理和创建。 控制器管理器包含的 TokenCleaner 控制器能够在启动引导令牌过期时将其删除。

这些令牌的格式为 [a-z0-9]{6}.[a-z0-9]{16}。第一个部分是令牌的 ID; 第二个部分是令牌的 Secret。你可以用如下所示的方式来在 HTTP 头部设置令牌:

Authorization: Bearer 781292.db7bc3a58fc5f07e

你必须在 API 服务器上设置 --enable-bootstrap-token-auth 标志来启用基于启动引导令牌的身份认证组件。 你必须通过控制器管理器的 --controllers 标志来启用 TokenCleaner 控制器; 这可以通过类似 --controllers=*,tokencleaner 这种设置来做到。 如果你使用 kubeadm 来启动引导新的集群,该工具会帮你完成这些设置。

身份认证组件的认证结果为 system:bootstrap:<令牌 ID>,该用户属于 system:bootstrappers 用户组。 这里的用户名和组设置都是有意设计成这样,其目的是阻止用户在启动引导集群之后继续使用这些令牌。 这里的用户名和组名可以用来(并且已经被 kubeadm 用来)构造合适的鉴权策略, 以完成启动引导新集群的工作。

请参阅启动引导令牌, 以了解关于启动引导令牌身份认证组件与控制器的更深入的信息,以及如何使用 kubeadm 来管理这些令牌。

服务账号令牌

服务账号(Service Account)是一种自动被启用的用户认证机制,使用经过签名的持有者令牌来验证请求。 该插件可接受两个可选参数:

  • --service-account-key-file 文件包含 PEM 编码的 x509 RSA 或 ECDSA 私钥或公钥, 用于验证 ServiceAccount 令牌。这样指定的文件可以包含多个密钥, 并且可以使用不同的文件多次指定此参数。若未指定,则使用 --tls-private-key-file 参数。
  • --service-account-lookup 如果启用,则从 API 删除的令牌会被回收。

服务账号通常由 API 服务器自动创建并通过 ServiceAccount 准入控制器关联到集群中运行的 Pod 上。 持有者令牌会挂载到 Pod 中可预知的位置,允许集群内进程与 API 服务器通信。 服务账号也可以使用 Pod 规约的 serviceAccountName 字段显式地关联到 Pod 上。

apiVersion: apps/v1 # 此 apiVersion 从 Kubernetes 1.9 开始可用
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: default
spec:
  replicas: 3
  template:
    metadata:
    # ...
    spec:
      serviceAccountName: bob-the-bot
      containers:
      - name: nginx
        image: nginx:1.14.2

在集群外部使用服务账号持有者令牌也是完全合法的,且可用来为长时间运行的、需要与 Kubernetes API 服务器通信的任务创建标识。要手动创建服务账号,可以使用 kubectl create serviceaccount <名称> 命令。 此命令会在当前的名字空间中生成一个服务账号。

kubectl create serviceaccount jenkins
serviceaccount/jenkins created

创建相关联的令牌:

kubectl create token jenkins
eyJhbGciOiJSUzI1NiIsImtp...

所创建的令牌是一个已签名的 JWT 令牌。

已签名的 JWT 可以用作持有者令牌,并将被认证为所给的服务账号。 关于如何在请求中包含令牌,请参阅前文。 通常,这些令牌数据会被挂载到 Pod 中以便集群内访问 API 服务器时使用, 不过也可以在集群外部使用。

服务账号被身份认证后,所确定的用户名为 system:serviceaccount:<名字空间>:<服务账号>, 并被分配到用户组 system:serviceaccountssystem:serviceaccounts:<名字空间>

OpenID Connect(OIDC)令牌

OpenID Connect 是一种 OAuth2 认证方式, 被某些 OAuth2 提供者支持,例如 Azure 活动目录、Salesforce 和 Google。 协议对 OAuth2 的主要扩充体现在有一个附加字段会和访问令牌一起返回, 这一字段称作 ID Token(ID 令牌)。 ID 令牌是一种由服务器签名的 JWT 令牌,其中包含一些可预知的字段, 例如用户的邮箱地址,

要识别用户,身份认证组件使用 OAuth2 令牌响应中的 id_token(而非 access_token)作为持有者令牌。 关于如何在请求中设置令牌,可参见前文

sequenceDiagram participant user as 用户 participant idp as 身份提供者 participant kube as Kubectl participant api as API 服务器 user ->> idp: 1. 登录到 IdP activate idp idp -->> user: 2. 提供 access_token,
id_token, 和 refresh_token deactivate idp activate user user ->> kube: 3. 调用 Kubectl 并
设置 --token 为 id_token
或者将令牌添加到 .kube/config deactivate user activate kube kube ->> api: 4. Authorization: Bearer... deactivate kube activate api api ->> api: 5. JWT 签名合法么? api ->> api: 6. JWT 是否已过期?(iat+exp) api ->> api: 7. 用户被授权了么? api -->> kube: 8. 已授权:执行
操作并返回结果 deactivate api activate kube kube --x user: 9. 返回结果 deactivate kube
  1. 登录到你的身份服务(Identity Provider)
  2. 你的身份服务将为你提供 access_tokenid_tokenrefresh_token
  3. 在使用 kubectl 时,将 id_token 设置为 --token 标志值,或者将其直接添加到 kubeconfig
  4. kubectl 将你的 id_token 放到一个称作 Authorization 的头部,发送给 API 服务器
  5. API 服务器将负责通过检查配置中引用的证书来确认 JWT 的签名是合法的
  6. 检查确认 id_token 尚未过期
  7. 确认用户有权限执行操作
  8. 鉴权成功之后,API 服务器向 kubectl 返回响应
  9. kubectl 向用户提供反馈信息

由于用来验证你是谁的所有数据都在 id_token 中,Kubernetes 不需要再去联系身份服务。 在一个所有请求都是无状态请求的模型中,这一工作方式可以使得身份认证的解决方案更容易处理大规模请求。 不过,此访问也有一些挑战:

  1. Kubernetes 没有提供用来触发身份认证过程的 "Web 界面"。 因为不存在用来收集用户凭据的浏览器或用户接口,你必须自己先行完成对身份服务的认证过程。
  2. id_token 令牌不可收回。因其属性类似于证书,其生命期一般很短(只有几分钟), 所以,每隔几分钟就要获得一个新的令牌这件事可能很让人头疼。
  3. 如果需要向 Kubernetes 控制面板执行身份认证,你必须使用 kubectl proxy 命令或者一个能够注入 id_token 的反向代理。

配置 API 服务器

要启用此插件,须在 API 服务器上配置以下标志:

参数描述示例必需?
--oidc-issuer-url允许 API 服务器发现公开的签名密钥的服务的 URL。只接受模式为 https:// 的 URL。此值通常设置为服务的发现 URL,不含路径。例如:"https://accounts.google.com" 或 "https://login.salesforce.com"。此 URL 应指向 .well-known/openid-configuration 下一层的路径。如果发现 URL 是 https://accounts.google.com/.well-known/openid-configuration,则此值应为 https://accounts.google.com
--oidc-client-id所有令牌都应发放给此客户 ID。kubernetes
--oidc-username-claim用作用户名的 JWT 申领(JWT Claim)。默认情况下使用 sub 值,即最终用户的一个唯一的标识符。管理员也可以选择其他申领,例如 email 或者 name,取决于所用的身份服务。不过,除了 email 之外的申领都会被添加令牌发放者的 URL 作为前缀,以免与其他插件产生命名冲突。sub
--oidc-username-prefix要添加到用户名申领之前的前缀,用来避免与现有用户名发生冲突(例如:system: 用户)。例如,此标志值为 oidc: 时将创建形如 oidc:jane.doe 的用户名。如果此标志未设置,且 --oidc-username-claim 标志值不是 email,则默认前缀为 <令牌发放者的 URL>#,其中 <令牌发放者 URL > 的值取自 --oidc-issuer-url 标志的设定。此标志值为 - 时,意味着禁止添加用户名前缀。oidc:
--oidc-groups-claim用作用户组名的 JWT 申领。如果所指定的申领确实存在,则其值必须是一个字符串数组。groups
--oidc-groups-prefix添加到组申领的前缀,用来避免与现有用户组名(如:system: 组)发生冲突。例如,此标志值为 oidc: 时,所得到的用户组名形如 oidc:engineeringoidc:infraoidc:
--oidc-required-claim取值为一个 key=value 偶对,意为 ID 令牌中必须存在的申领。如果设置了此标志,则 ID 令牌会被检查以确定是否包含取值匹配的申领。此标志可多次重复,以指定多个申领。claim=value
--oidc-ca-file指向一个 CA 证书的路径,该 CA 负责对你的身份服务的 Web 证书提供签名。默认值为宿主系统的根 CA。/etc/kubernetes/ssl/kc-ca.pem
--oidc-signing-algs采纳的签名算法。默认为 "RS256"。RS512

很重要的一点是,API 服务器并非一个 OAuth2 客户端,相反,它只能被配置为信任某一个令牌发放者。 这使得使用公共服务(如 Google)的用户可以不信任发放给第三方的凭据。 如果管理员希望使用多个 OAuth 客户端,他们应该研究一下那些支持 azp (Authorized Party,被授权方)申领的服务。 azp 是一种允许某客户端代替另一客户端发放令牌的机制。

Kubernetes 并未提供 OpenID Connect 的身份服务。 你可以使用现有的公共的 OpenID Connect 身份服务 (例如 Google 或者其他服务)。 或者,你也可以选择自己运行一个身份服务,例如 CoreOS dexKeycloak、 CloudFoundry UAA 或者 Tremolo Security 的 OpenUnison

要在 Kubernetes 环境中使用某身份服务,该服务必须:

  1. 支持 OpenID connect 发现; 但事实上并非所有服务都具备此能力
  2. 运行 TLS 协议且所使用的加密组件都未过时
  3. 拥有由 CA 签名的证书(即使 CA 不是商业 CA 或者是自签名的 CA 也可以)

关于上述第三条需求,即要求具备 CA 签名的证书,有一些额外的注意事项。 如果你部署了自己的身份服务,而不是使用云厂商(如 Google 或 Microsoft)所提供的服务, 你必须对身份服务的 Web 服务器证书进行签名,签名所用证书的 CA 标志要设置为 TRUE,即使用的是自签名证书。这是因为 GoLang 的 TLS 客户端实现对证书验证标准方面有非常严格的要求。如果你手头没有现成的 CA 证书,可以使用 CoreOS 团队所开发的这个脚本 来创建一个简单的 CA 和被签了名的证书与密钥对。 或者你也可以使用这个类似的脚本, 生成一个合法期更长、密钥尺寸更大的 SHA256 证书。

特定系统的安装指令:

使用 kubectl

选项一:OIDC 身份认证组件

第一种方案是使用 kubectl 的 oidc 身份认证组件,该组件将 id_token 设置为所有请求的持有者令牌, 并且在令牌过期时自动刷新。在你登录到你的身份服务之后, 可以使用 kubectl 来添加你的 id_tokenrefresh_tokenclient_idclient_secret,以配置该插件。

如果服务在其刷新令牌响应中不包含 id_token,则此插件无法支持该服务。 这时你应该考虑下面的选项二。

kubectl config set-credentials USER_NAME \
   --auth-provider=oidc \
   --auth-provider-arg=idp-issuer-url=( issuer url ) \
   --auth-provider-arg=client-id=( your client id ) \
   --auth-provider-arg=client-secret=( your client secret ) \
   --auth-provider-arg=refresh-token=( your refresh token ) \
   --auth-provider-arg=idp-certificate-authority=( path to your ca certificate ) \
   --auth-provider-arg=id-token=( your id_token )

作为示例,在完成对你的身份服务的身份认证之后,运行下面的命令:

kubectl config set-credentials mmosley  \
        --auth-provider=oidc  \
        --auth-provider-arg=idp-issuer-url=https://oidcidp.tremolo.lan:8443/auth/idp/OidcIdP  \
        --auth-provider-arg=client-id=kubernetes  \
        --auth-provider-arg=client-secret=1db158f6-177d-4d9c-8a8b-d36869918ec5  \
        --auth-provider-arg=refresh-token=q1bKLFOyUiosTfawzA93TzZIDzH2TNa2SMm0zEiPKTUwME6BkEo6Sql5yUWVBSWpKUGphaWpxSVAfekBOZbBhaEW+VlFUeVRGcluyVF5JT4+haZmPsluFoFu5XkpXk5BXqHega4GAXlF+ma+vmYpFcHe5eZR+slBFpZKtQA= \
        --auth-provider-arg=idp-certificate-authority=/root/ca.pem \
        --auth-provider-arg=id-token=eyJraWQiOiJDTj1vaWRjaWRwLnRyZW1vbG8ubGFuLCBPVT1EZW1vLCBPPVRybWVvbG8gU2VjdXJpdHksIEw9QXJsaW5ndG9uLCBTVD1WaXJnaW5pYSwgQz1VUy1DTj1rdWJlLWNhLTEyMDIxNDc5MjEwMzYwNzMyMTUyIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL29pZGNpZHAudHJlbW9sby5sYW46ODQ0My9hdXRoL2lkcC9PaWRjSWRQIiwiYXVkIjoia3ViZXJuZXRlcyIsImV4cCI6MTQ4MzU0OTUxMSwianRpIjoiMm96US15TXdFcHV4WDlHZUhQdy1hZyIsImlhdCI6MTQ4MzU0OTQ1MSwibmJmIjoxNDgzNTQ5MzMxLCJzdWIiOiI0YWViMzdiYS1iNjQ1LTQ4ZmQtYWIzMC0xYTAxZWU0MWUyMTgifQ.w6p4J_6qQ1HzTG9nrEOrubxIMb9K5hzcMPxc9IxPx2K4xO9l-oFiUw93daH3m5pluP6K7eOE6txBuRVfEcpJSwlelsOsW8gb8VJcnzMS9EnZpeA0tW_p-mnkFc3VcfyXuhe5R3G7aa5d8uHv70yJ9Y3-UhjiN9EhpMdfPAoEB9fYKKkJRzF7utTTIPGrSaSU6d2pcpfYKaxIwePzEkT4DfcQthoZdy9ucNvvLoi1DIC-UocFD8HLs8LYKEqSxQvOcvnThbObJ9af71EwmuE21fO5KzMW20KtAeget1gnldOosPtz1G5EwvaQ401-RPQzPGMVBld0_zMCAwZttJ4knw

此操作会生成以下配置:

users:
- name: mmosley
  user:
    auth-provider:
      config:
        client-id: kubernetes
        client-secret: 1db158f6-177d-4d9c-8a8b-d36869918ec5
        id-token: eyJraWQiOiJDTj1vaWRjaWRwLnRyZW1vbG8ubGFuLCBPVT1EZW1vLCBPPVRybWVvbG8gU2VjdXJpdHksIEw9QXJsaW5ndG9uLCBTVD1WaXJnaW5pYSwgQz1VUy1DTj1rdWJlLWNhLTEyMDIxNDc5MjEwMzYwNzMyMTUyIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL29pZGNpZHAudHJlbW9sby5sYW46ODQ0My9hdXRoL2lkcC9PaWRjSWRQIiwiYXVkIjoia3ViZXJuZXRlcyIsImV4cCI6MTQ4MzU0OTUxMSwianRpIjoiMm96US15TXdFcHV4WDlHZUhQdy1hZyIsImlhdCI6MTQ4MzU0OTQ1MSwibmJmIjoxNDgzNTQ5MzMxLCJzdWIiOiI0YWViMzdiYS1iNjQ1LTQ4ZmQtYWIzMC0xYTAxZWU0MWUyMTgifQ.w6p4J_6qQ1HzTG9nrEOrubxIMb9K5hzcMPxc9IxPx2K4xO9l-oFiUw93daH3m5pluP6K7eOE6txBuRVfEcpJSwlelsOsW8gb8VJcnzMS9EnZpeA0tW_p-mnkFc3VcfyXuhe5R3G7aa5d8uHv70yJ9Y3-UhjiN9EhpMdfPAoEB9fYKKkJRzF7utTTIPGrSaSU6d2pcpfYKaxIwePzEkT4DfcQthoZdy9ucNvvLoi1DIC-UocFD8HLs8LYKEqSxQvOcvnThbObJ9af71EwmuE21fO5KzMW20KtAeget1gnldOosPtz1G5EwvaQ401-RPQzPGMVBld0_zMCAwZttJ4knw
        idp-certificate-authority: /root/ca.pem
        idp-issuer-url: https://oidcidp.tremolo.lan:8443/auth/idp/OidcIdP
        refresh-token: q1bKLFOyUiosTfawzA93TzZIDzH2TNa2SMm0zEiPKTUwME6BkEo6Sql5yUWVBSWpKUGphaWpxSVAfekBOZbBhaEW+VlFUeVRGcluyVF5JT4+haZmPsluFoFu5XkpXk5BXq
      name: oidc

当你的 id_token 过期时,kubectl 会尝试使用你的 refresh_token 来刷新你的 id_token,并且在 .kube/config 文件的 client_secret 中存放 refresh_tokenid_token 的新值。

选项二:使用 --token 选项

kubectl 命令允许你使用 --token 选项传递一个令牌。 你可以将 id_token 的内容复制粘贴过来,作为此标志的取值:

kubectl --token=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL21sYi50cmVtb2xvLmxhbjo4MDQzL2F1dGgvaWRwL29pZGMiLCJhdWQiOiJrdWJlcm5ldGVzIiwiZXhwIjoxNDc0NTk2NjY5LCJqdGkiOiI2RDUzNXoxUEpFNjJOR3QxaWVyYm9RIiwiaWF0IjoxNDc0NTk2MzY5LCJuYmYiOjE0NzQ1OTYyNDksInN1YiI6Im13aW5kdSIsInVzZXJfcm9sZSI6WyJ1c2VycyIsIm5ldy1uYW1lc3BhY2Utdmlld2VyIl0sImVtYWlsIjoibXdpbmR1QG5vbW9yZWplZGkuY29tIn0.f2As579n9VNoaKzoF-dOQGmXkFKf1FMyNV0-va_B63jn-_n9LGSCca_6IVMP8pO-Zb4KvRqGyTP0r3HkHxYy5c81AnIh8ijarruczl-TK_yF5akjSTHFZD-0gRzlevBDiH8Q79NAr-ky0P4iIXS8lY9Vnjch5MF74Zx0c3alKJHJUnnpjIACByfF2SCaYzbWFMUNat-K1PaUk5-ujMBG7yYnr95xD-63n8CO8teGUAAEMx6zRjzfhnhbzX-ajwZLGwGUBT4WqjMs70-6a7_8gZmLZb2az1cZynkFRj2BaCkVT3A2RrjeEwZEtGXlMqKJ1_I2ulrOVsYx01_yD35-rw get nodes

Webhook 令牌身份认证

Webhook 身份认证是一种用来验证持有者令牌的回调机制。

  • --authentication-token-webhook-config-file 指向一个配置文件, 其中描述如何访问远程的 Webhook 服务。
  • --authentication-token-webhook-cache-ttl 用来设定身份认证决定的缓存时间。 默认时长为 2 分钟。

配置文件使用 kubeconfig 文件的格式。文件中,clusters 指代远程服务,users 指代远程 API 服务 Webhook。下面是一个例子:

# Kubernetes API 版本
apiVersion: v1
# API 对象类别
kind: Config
# clusters 指代远程服务
clusters:
  - name: name-of-remote-authn-service
    cluster:
      certificate-authority: /path/to/ca.pem         # 用来验证远程服务的 CA
      server: https://authn.example.com/authenticate # 要查询的远程服务 URL。生产环境中建议使用 'https'。

# users 指代 API 服务的 Webhook 配置
users:
  - name: name-of-api-server
    user:
      client-certificate: /path/to/cert.pem # Webhook 插件要使用的证书
      client-key: /path/to/key.pem          # 与证书匹配的密钥

# kubeconfig 文件需要一个上下文(Context),此上下文用于本 API 服务器
current-context: webhook
contexts:
- context:
    cluster: name-of-remote-authn-service
    user: name-of-api-server
  name: webhook

当客户端尝试在 API 服务器上使用持有者令牌完成身份认证 (如所述)时, 身份认证 Webhook 会用 POST 请求发送一个 JSON 序列化的对象到远程服务。 该对象是 TokenReview 对象,其中包含持有者令牌。 Kubernetes 不会强制请求提供此 HTTP 头部。

要注意的是,Webhook API 对象和其他 Kubernetes API 对象一样, 也要受到同一版本兼容规则约束。 实现者应检查请求的 apiVersion 字段以确保正确的反序列化, 并且 必须 以与请求相同版本的 TokenReview 对象进行响应。

{
  "apiVersion": "authentication.k8s.io/v1",
  "kind": "TokenReview",
  "spec": {
   # 发送到 API 服务器的不透明持有者令牌
    "token": "014fbff9a07c...",
   
    # 提供令牌的服务器的受众标识符的可选列表。
    # 受众感知令牌验证器(例如,OIDC 令牌验证器)
    # 应验证令牌是否针对此列表中的至少一个受众,
    # 并返回此列表与响应状态中令牌的有效受众的交集。
    # 这确保了令牌对于向其提供给的服务器进行身份验证是有效的。
    # 如果未提供受众,则应验证令牌以向 Kubernetes API 服务器进行身份验证。
    "audiences": ["https://myserver.example.com", "https://myserver.internal.example.com"]
  }
}

{
  "apiVersion": "authentication.k8s.io/v1beta1",
  "kind": "TokenReview",
  "spec": {
    # 发送到 API 服务器的不透明匿名令牌
    "token": "014fbff9a07c...",
   
    # 提供令牌的服务器的受众标识符的可选列表。
    # 受众感知令牌验证器(例如,OIDC 令牌验证器)
    # 应验证令牌是否针对此列表中的至少一个受众,
    # 并返回此列表与响应状态中令牌的有效受众的交集。
    # 这确保了令牌对于向其提供给的服务器进行身份验证是有效的。
    # 如果未提供受众,则应验证令牌以向 Kubernetes API 服务器进行身份验证。
    "audiences": ["https://myserver.example.com", "https://myserver.internal.example.com"]
  }
}

远程服务预计会填写请求的 status 字段以指示登录成功。 响应正文的 spec 字段被忽略并且可以省略。 远程服务必须使用它收到的相同 TokenReview API 版本返回响应。 持有者令牌的成功验证将返回:

{
  "apiVersion": "authentication.k8s.io/v1",
  "kind": "TokenReview",
  "status": {
    "authenticated": true,
    "user": {
      # 必要
      "username": "janedoe@example.com",
      # 可选
      "uid": "42",
      # 可选的组成员身份
      "groups": ["developers", "qa"],
      # 认证者提供的可选附加信息。
      # 此字段不可包含机密数据,因为这类数据可能被记录在日志或 API 对象中,
      # 并且可能传递给 admission webhook。
      "extra": {
        "extrafield1": [
          "extravalue1",
          "extravalue2"
        ]
      }
    },
    # 验证器可以返回的、可选的用户感知令牌列表,
    # 包含令牌对其有效的、包含于 `spec.audiences` 列表中的受众。
    # 如果省略,则认为该令牌可用于对 Kubernetes API 服务器进行身份验证。
    "audiences": ["https://myserver.example.com"]
  }
}

{
  "apiVersion": "authentication.k8s.io/v1beta1",
  "kind": "TokenReview",
  "status": {
    "authenticated": true,
    "user": {
      # 必要
      "username": "janedoe@example.com",
      # 可选
      "uid": "42",
      # 可选的组成员身份
      "groups": ["developers", "qa"],
      # 认证者提供的可选附加信息。
      # 此字段不可包含机密数据,因为这类数据可能被记录在日志或 API 对象中,
      # 并且可能传递给 admission webhook。
      "extra": {
        "extrafield1": [
          "extravalue1",
          "extravalue2"
        ]
      }
    },
    # 验证器可以返回的、可选的用户感知令牌列表,
    # 包含令牌对其有效的、包含于 `spec.audiences` 列表中的受众。
    # 如果省略,则认为该令牌可用于对 Kubernetes API 服务器进行身份验证。
    "audiences": ["https://myserver.example.com"]
  }
}

而不成功的请求会返回:

{
  "apiVersion": "authentication.k8s.io/v1",
  "kind": "TokenReview",
  "status": {
    "authenticated": false,
    # 可选地包括有关身份验证失败原因的详细信息。
    # 如果没有提供错误信息,API 将返回一个通用的 Unauthorized 消息。
    # 当 authenticated=true 时,error 字段被忽略。
    "error": "Credentials are expired"
  }
}

{
  "apiVersion": "authentication.k8s.io/v1beta1",
  "kind": "TokenReview",
  "status": {
    "authenticated": false,
    # 可选地包括有关身份验证失败原因的详细信息。
    # 如果没有提供错误信息,API 将返回一个通用的 Unauthorized 消息。
    # 当 authenticated=true 时,error 字段被忽略。
    "error": "Credentials are expired"
  }
}

身份认证代理

API 服务器可以配置成从请求的头部字段值(如 X-Remote-User)中辩识用户。 这一设计是用来与某身份认证代理一起使用 API 服务器,代理负责设置请求的头部字段值。

  • --requestheader-username-headers 必需字段,大小写不敏感。 用来设置要获得用户身份所要检查的头部字段名称列表(有序)。 第一个包含数值的字段会被用来提取用户名。
  • --requestheader-group-headers 可选字段,在 Kubernetes 1.6 版本以后支持,大小写不敏感。 建议设置为 "X-Remote-Group"。用来指定一组头部字段名称列表,以供检查用户所属的组名称。 所找到的全部头部字段的取值都会被用作用户组名。
  • --requestheader-extra-headers-prefix 可选字段,在 Kubernetes 1.6 版本以后支持,大小写不敏感。 建议设置为 "X-Remote-Extra-"。用来设置一个头部字段的前缀字符串, API 服务器会基于所给前缀来查找与用户有关的一些额外信息。这些额外信息通常用于所配置的鉴权插件。 API 服务器会将与所给前缀匹配的头部字段过滤出来,去掉其前缀部分,将剩余部分转换为小写字符串, 并在必要时执行百分号解码后, 构造新的附加信息字段键名。原来的头部字段值直接作为附加信息字段的值。

例如,使用下面的配置:

--requestheader-username-headers=X-Remote-User
--requestheader-group-headers=X-Remote-Group
--requestheader-extra-headers-prefix=X-Remote-Extra-

针对所收到的如下请求:

GET / HTTP/1.1
X-Remote-User: fido
X-Remote-Group: dogs
X-Remote-Group: dachshunds
X-Remote-Extra-Acme.com%2Fproject: some-project
X-Remote-Extra-Scopes: openid
X-Remote-Extra-Scopes: profile

会生成下面的用户信息:

name: fido
groups:
- dogs
- dachshunds
extra:
  acme.com/project:
  - some-project
  scopes:
  - openid
  - profile

为了防范头部信息侦听,在请求中的头部字段被检视之前, 身份认证代理需要向 API 服务器提供一份合法的客户端证书,供后者使用所给的 CA 来执行验证。 警告:不要 在不同的上下文中复用 CA 证书,除非你清楚这样做的风险是什么以及应如何保护 CA 用法的机制。

  • --requestheader-client-ca-file 必需字段,给出 PEM 编码的证书包。 在检查请求的头部字段以提取用户名信息之前,必须提供一个合法的客户端证书, 且该证书要能够被所给文件中的机构所验证。
  • --requestheader-allowed-names 可选字段,用来给出一组公共名称(CN)。 如果此标志被设置,则在检视请求中的头部以提取用户信息之前, 必须提供包含此列表中所给的 CN 名的、合法的客户端证书。

匿名请求

启用匿名请求支持之后,如果请求没有被已配置的其他身份认证方法拒绝, 则被视作匿名请求(Anonymous Requests)。这类请求获得用户名 system:anonymous 和对应的用户组 system:unauthenticated

例如,在一个配置了令牌身份认证且启用了匿名访问的服务器上,如果请求提供了非法的持有者令牌, 则会返回 401 Unauthorized 错误。如果请求没有提供持有者令牌,则被视为匿名请求。

在 1.5.1-1.5.x 版本中,匿名访问默认情况下是被禁用的,可以通过为 API 服务器设定 --anonymous-auth=true 来启用。

在 1.6 及之后版本中,如果所使用的鉴权模式不是 AlwaysAllow,则匿名访问默认是被启用的。 从 1.6 版本开始,ABAC 和 RBAC 鉴权模块要求对 system:anonymous 用户或者 system:unauthenticated 用户组执行显式的权限判定,所以之前的为用户 * 或用户组 * 赋予访问权限的策略规则都不再包含匿名用户。

用户伪装

一个用户可以通过伪装(Impersonation)头部字段来以另一个用户的身份执行操作。 使用这一能力,你可以手动重载请求被身份认证所识别出来的用户信息。 例如,管理员可以使用这一功能特性来临时伪装成另一个用户,查看请求是否被拒绝, 从而调试鉴权策略中的问题,

带伪装的请求首先会被身份认证识别为发出请求的用户, 之后会切换到使用被伪装的用户的用户信息。

  • 用户发起 API 调用时 同时 提供自身的凭据和伪装头部字段信息
  • API 服务器对用户执行身份认证
  • API 服务器确认通过认证的用户具有伪装特权
  • 请求用户的信息被替换成伪装字段的值
  • 评估请求,鉴权组件针对所伪装的用户信息执行操作

以下 HTTP 头部字段可用来执行伪装请求:

  • Impersonate-User:要伪装成的用户名
  • Impersonate-Group:要伪装成的用户组名。可以多次指定以设置多个用户组。 可选字段;要求 "Impersonate-User" 必须被设置。
  • Impersonate-Extra-<附加名称>:一个动态的头部字段,用来设置与用户相关的附加字段。 此字段可选;要求 "Impersonate-User" 被设置。为了能够以一致的形式保留, <附加名称>部分必须是小写字符, 如果有任何字符不是合法的 HTTP 头部标签字符, 则必须是 utf8 字符,且转换为百分号编码
  • Impersonate-Uid:一个唯一标识符,用来表示所伪装的用户。此头部可选。 如果设置,则要求 "Impersonate-User" 也存在。Kubernetes 对此字符串没有格式要求。

伪装带有用户组的用户时,所使用的伪装头部字段示例:

Impersonate-User: jane.doe@example.com
Impersonate-Group: developers
Impersonate-Group: admins

伪装带有 UID 和附加字段的用户时,所使用的伪装头部字段示例:

Impersonate-User: jane.doe@example.com
Impersonate-Extra-dn: cn=jane,ou=engineers,dc=example,dc=com
Impersonate-Extra-acme.com%2Fproject: some-project
Impersonate-Extra-scopes: view
Impersonate-Extra-scopes: development
Impersonate-Uid: 06f6ce97-e2c5-4ab8-7ba5-7654dd08d52b

在使用 kubectl 时,可以使用 --as 标志来配置 Impersonate-User 头部字段值, 使用 --as-group 标志配置 Impersonate-Group 头部字段值。

kubectl drain mynode
Error from server (Forbidden): User "clark" cannot get nodes at the cluster scope. (get nodes mynode)

设置 --as--as-group 标志:

kubectl drain mynode --as=superman --as-group=system:masters
node/mynode cordoned
node/mynode drained

若要伪装成某个用户、某个组、用户标识符(UID))或者设置附加字段, 执行伪装操作的用户必须具有对所伪装的类别(“user”、“group”、“uid” 等)执行 “impersonate” 动词操作的能力。 对于启用了 RBAC 鉴权插件的集群,下面的 ClusterRole 封装了设置用户和组伪装字段所需的规则:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: impersonator
rules:
- apiGroups: [""]
  resources: ["users", "groups", "serviceaccounts"]
  verbs: ["impersonate"]

为了执行伪装,附加字段和所伪装的 UID 都位于 "authorization.k8s.io" apiGroup 中。 附加字段会被作为 userextras 资源的子资源来执行权限评估。 如果要允许用户为附加字段 “scopes” 和 UID 设置伪装头部,该用户需要被授予以下角色:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: scopes-and-uid-impersonator
rules:
# 可以设置 "Impersonate-Extra-scopes" 和 "Impersonate-Uid" 头部
- apiGroups: ["authentication.k8s.io"]
  resources: ["userextras/scopes", "uids"]
  verbs: ["impersonate"]

你也可以通过约束资源可能对应的 resourceNames 限制伪装头部的取值:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: limited-impersonator
rules:
  # 可以伪装成用户 "jane.doe@example.com"
  - apiGroups: [""]
    resources: ["users"]
    verbs: ["impersonate"]
    resourceNames: ["jane.doe@example.com"]
  
  # 可以伪装成用户组 "developers" 和 "admins"
  - apiGroups: [""]
    resources: ["groups"]
    verbs: ["impersonate"]
    resourceNames: ["developers","admins"]
  
  # 可以将附加字段 "scopes" 伪装成 "view" 和 "development"
  - apiGroups: ["authentication.k8s.io"]
    resources: ["userextras/scopes"]
    verbs: ["impersonate"]
    resourceNames: ["view", "development"]
  
  # 可以伪装 UID "06f6ce97-e2c5-4ab8-7ba5-7654dd08d52b"
  - apiGroups: ["authentication.k8s.io"]
    resources: ["uids"]
    verbs: ["impersonate"]
    resourceNames: ["06f6ce97-e2c5-4ab8-7ba5-7654dd08d52b"]

client-go 凭据插件

特性状态: Kubernetes v1.22 [stable]

k8s.io/client-go 及使用它的工具(如 kubectlkubelet) 可以执行某个外部命令来获得用户的凭据信息。

这一特性的目的是便于客户端与 k8s.io/client-go 并不支持的身份认证协议 (LDAP、Kerberos、OAuth2、SAML 等)继承。 插件实现特定于协议的逻辑,之后返回不透明的凭据以供使用。 几乎所有的凭据插件使用场景中都需要在服务器端存在一个支持 Webhook 令牌身份认证组件的模块, 负责解析客户端插件所生成的凭据格式。

示例应用场景

在一个假想的应用场景中,某组织运行这一个外部的服务,能够将特定用户的已签名的令牌转换成 LDAP 凭据。此服务还能够对 Webhook 令牌身份认证组件的请求做出响应以验证所提供的令牌。 用户需要在自己的工作站上安装一个凭据插件。

要对 API 服务器认证身份时:

  • 用户发出 kubectl 命令。
  • 凭据插件提示用户输入 LDAP 凭据,并与外部服务交互,获得令牌。
  • 凭据插件将令牌返回该 client-go,后者将其用作持有者令牌提交给 API 服务器。
  • API 服务器使用 Webhook 令牌身份认证组件向外部服务发出 TokenReview 请求。
  • 外部服务检查令牌上的签名,返回用户的用户名和用户组信息。

配置

凭据插件通过 kubectl 配置文件 来作为 user 字段的一部分设置。

apiVersion: v1
kind: Config
users:
- name: my-user
  user:
    exec:
      # 要执行的命令。必需。
      command: "example-client-go-exec-plugin"

      # 解析 ExecCredentials 资源时使用的 API 版本。必需。
      # 插件返回的 API 版本必需与这里列出的版本匹配。
      #
      # 要与支持多个版本的工具(如 client.authentication.k8s.io/v1beta1)集成,
      # 可以设置一个环境变量或者向工具传递一个参数标明 exec 插件所期望的版本,
      # 或者从 KUBERNETES_EXEC_INFO 环境变量的 ExecCredential 对象中读取版本信息。
      apiVersion: "client.authentication.k8s.io/v1"

      # 执行此插件时要设置的环境变量。可选字段。
      env:
      - name: "FOO"
        value: "bar"

      # 执行插件时要传递的参数。可选字段。
      args:
      - "arg1"
      - "arg2"

      # 当可执行文件不存在时显示给用户的文本。可选的。
      installHint: |
        需要 example-client-go-exec-plugin 来在当前集群上执行身份认证。可以通过以下命令安装:

        MacOS: brew install example-client-go-exec-plugin

        Ubuntu: apt-get install example-client-go-exec-plugin

        Fedora: dnf install example-client-go-exec-plugin

        ...        

      # 是否使用 KUBERNETES_EXEC_INFO 环境变量的一部分向这个 exec 插件
      # 提供集群信息(可能包含非常大的 CA 数据)
      provideClusterInfo: true

      # Exec 插件与标准输入 I/O 数据流之间的协议。如果协议无法满足,
      # 则插件无法运行并会返回错误信息。合法的值包括 "Never" (Exec 插件从不使用标准输入),
      # "IfAvailable" (Exec 插件希望在可以的情况下使用标准输入),
      # 或者 "Always" (Exec 插件需要使用标准输入才能工作)。必需字段。
      interactiveMode: Never
clusters:
- name: my-cluster
  cluster:
    server: "https://172.17.4.100:6443"
    certificate-authority: "/etc/kubernetes/ca.pem"
    extensions:
    - name: client.authentication.k8s.io/exec # 为每个集群 exec 配置保留的扩展名
      extension:
        arbitrary: config
        this: 在设置 provideClusterInfo 时可通过环境变量 KUBERNETES_EXEC_INFO 指定
        you: ["can", "put", "anything", "here"]
contexts:
- name: my-cluster
  context:
    cluster: my-cluster
    user: my-user
current-context: my-cluster

apiVersion: v1
kind: Config
users:
- name: my-user
  user:
    exec:
      # 要执行的命令。必需。
      command: "example-client-go-exec-plugin"

      # 解析 ExecCredentials 资源时使用的 API 版本。必需。
      # 插件返回的 API 版本必需与这里列出的版本匹配。
      #
      # 要与支持多个版本的工具(如 client.authentication.k8s.io/v1)集成,
      # 可以设置一个环境变量或者向工具传递一个参数标明 exec 插件所期望的版本,
      # 或者从 KUBERNETES_EXEC_INFO 环境变量的 ExecCredential 对象中读取版本信息。
      apiVersion: "client.authentication.k8s.io/v1beta1"

      # 执行此插件时要设置的环境变量。可选字段。
      env:
      - name: "FOO"
        value: "bar"

      # 执行插件时要传递的参数。可选字段。
      args:
      - "arg1"
      - "arg2"

      # 当可执行文件不存在时显示给用户的文本。可选的。
      installHint: |
        需要 example-client-go-exec-plugin 来在当前集群上执行身份认证。可以通过以下命令安装:

        MacOS: brew install example-client-go-exec-plugin

        Ubuntu: apt-get install example-client-go-exec-plugin

        Fedora: dnf install example-client-go-exec-plugin

        ...        

      # 是否使用 KUBERNETES_EXEC_INFO 环境变量的一部分向这个 exec 插件
      # 提供集群信息(可能包含非常大的 CA 数据)
      provideClusterInfo: true

      # Exec 插件与标准输入 I/O 数据流之间的协议。如果协议无法满足,
      # 则插件无法运行并会返回错误信息。合法的值包括 "Never" (Exec 插件从不使用标准输入),
      # "IfAvailable" (Exec 插件希望在可以的情况下使用标准输入),
      # 或者 "Always" (Exec 插件需要使用标准输入才能工作)。可选字段。
      # 默认值为 "IfAvailable"。
      interactiveMode: Never
clusters:
- name: my-cluster
  cluster:
    server: "https://172.17.4.100:6443"
    certificate-authority: "/etc/kubernetes/ca.pem"
    extensions:
    - name: client.authentication.k8s.io/exec # 为每个集群 exec 配置保留的扩展名
      extension:
        arbitrary: config
        this: 在设置 provideClusterInfo 时可通过环境变量 KUBERNETES_EXEC_INFO 指定
        you: ["can", "put", "anything", "here"]
contexts:
- name: my-cluster
  context:
    cluster: my-cluster
    user: my-user
current-context: my-cluster

解析相对命令路径时,kubectl 将其视为与配置文件比较而言的相对路径。 如果 KUBECONFIG 被设置为 /home/jane/kubeconfig,而 exec 命令为 ./bin/example-client-go-exec-plugin,则要执行的可执行文件为 /home/jane/bin/example-client-go-exec-plugin

- name: my-user
  user:
    exec:
      # 对 kubeconfig 目录而言的相对路径
      command: "./bin/example-client-go-exec-plugin"
      apiVersion: "client.authentication.k8s.io/v1"
      interactiveMode: Never

输出和输出格式

所执行的命令会在 stdout 打印 ExecCredential 对象。 k8s.io/client-go 使用 status 中返回的凭据信息向 Kubernetes API 服务器执行身份认证。 所执行的命令会通过环境变量 KUBERNETES_EXEC_INFO 收到一个 ExecCredential 对象作为其输入。 此输入中包含类似于所返回的 ExecCredential 对象的预期 API 版本, 以及是否插件可以使用 stdin 与用户交互这类信息。

在交互式会话(即,某终端)中运行时,stdin 是直接暴露给插件使用的。 插件应该使用来自 KUBERNETES_EXEC_INFO 环境变量的 ExecCredential 输入对象中的 spec.interactive 字段来确定是否提供了 stdin。 插件的 stdin 需求(即,为了能够让插件成功运行,是否 stdin 是可选的、 必须提供的或者从不会被使用的)是通过 kubeconfig 中的 user.exec.interactiveMode 来声明的(参见下面的表格了解合法值)。 字段 user.exec.interactiveModeclient.authentication.k8s.io/v1beta1 中是可选的,在 client.authentication.k8s.io/v1 中是必需的。

interactiveMode 取值
interactiveMode 取值含义
Never此 exec 插件从不需要使用标准输入,因此如论是否有标准输入提供给用户输入,该 exec 插件都能运行。
IfAvailable此 exec 插件希望在标准输入可用的情况下使用标准输入,但在标准输入不存在时也可运行。因此,无论是否存在给用户提供输入的标准输入,此 exec 插件都会运行。如果存在供用户输入的标准输入,则该标准输入会被提供给 exec 插件。
Always此 exec 插件需要标准输入才能正常运行,因此只有存在供用户输入的标准输入时,此 exec 插件才会运行。如果不存在供用户输入的标准输入,则 exec 插件无法运行,并且 exec 插件的执行者会因此返回错误信息。

与使用持有者令牌凭据,插件在 ExecCredential 的状态中返回一个令牌:

{
  "apiVersion": "client.authentication.k8s.io/v1",
  "kind": "ExecCredential",
  "status": {
    "token": "my-bearer-token"
  }
}

{
  "apiVersion": "client.authentication.k8s.io/v1beta1",
  "kind": "ExecCredential",
  "status": {
    "token": "my-bearer-token"
  }
}

另一种方案是,返回 PEM 编码的客户端证书和密钥,以便执行 TLS 客户端身份认证。 如果插件在后续调用中返回了不同的证书或密钥,k8s.io/client-go 会终止其与服务器的连接,从而强制执行新的 TLS 握手过程。

如果指定了这种方式,则 clientKeyDataclientCertificateData 字段都必需存在。

clientCertificateData 字段可能包含一些要发送给服务器的中间证书(Intermediate Certificates)。

{
  "apiVersion": "client.authentication.k8s.io/v1",
  "kind": "ExecCredential",
  "status": {
    "clientCertificateData": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----",
    "clientKeyData": "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----"
  }
}

{
  "apiVersion": "client.authentication.k8s.io/v1beta1",
  "kind": "ExecCredential",
  "status": {
    "clientCertificateData": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----",
    "clientKeyData": "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----"
  }
}

作为一种可选方案,响应中还可以包含以 RFC 3339 时间戳格式给出的证书到期时间。 证书到期时间的有无会有如下影响:

  • 如果响应中包含了到期时间,持有者令牌和 TLS 凭据会被缓存,直到期限到来、 或者服务器返回 401 HTTP 状态码,或者进程退出。
  • 如果未指定到期时间,则持有者令牌和 TLS 凭据会被缓存,直到服务器返回 401 HTTP 状态码或者进程退出。

{
  "apiVersion": "client.authentication.k8s.io/v1",
  "kind": "ExecCredential",
  "status": {
    "token": "my-bearer-token",
    "expirationTimestamp": "2018-03-05T17:30:20-08:00"
  }
}

{
  "apiVersion": "client.authentication.k8s.io/v1beta1",
  "kind": "ExecCredential",
  "status": {
    "token": "my-bearer-token",
    "expirationTimestamp": "2018-03-05T17:30:20-08:00"
  }
}

为了让 exec 插件能够获得特定与集群的信息,可以在 kubeconfig 中的 user.exec 设置 provideClusterInfo。 这一特定于集群的信息就会通过 KUBERNETES_EXEC_INFO 环境变量传递给插件。 此环境变量中的信息可以用来执行特定于集群的凭据获取逻辑。 下面的 ExecCredential 清单描述的是一个示例集群信息。

{
  "apiVersion": "client.authentication.k8s.io/v1",
  "kind": "ExecCredential",
  "spec": {
    "cluster": {
      "server": "https://172.17.4.100:6443",
      "certificate-authority-data": "LS0t...",
      "config": {
        "arbitrary": "config",
        "this": "可以在设置 provideClusterInfo 时通过 KUBERNETES_EXEC_INFO 环境变量提供",
        "you": ["can", "put", "anything", "here"]
      }
    },
    "interactive": true
  }
}

{
  "apiVersion": "client.authentication.k8s.io/v1beta1",
  "kind": "ExecCredential",
  "spec": {
    "cluster": {
      "server": "https://172.17.4.100:6443",
      "certificate-authority-data": "LS0t...",
      "config": {
        "arbitrary": "config",
        "this": "可以在设置 provideClusterInfo 时通过 KUBERNETES_EXEC_INFO 环境变量提供",
        "you": ["can", "put", "anything", "here"]
      }
    },
    "interactive": true
  }
}

接下来

3.2 - 使用启动引导令牌(Bootstrap Tokens)认证

特性状态: Kubernetes v1.18 [stable]

启动引导令牌是一种简单的持有者令牌(Bearer Token),这种令牌是在新建集群 或者在现有集群中添加新节点时使用的。 它被设计成能够支持 kubeadm, 但是也可以被用在其他的案例中以便用户在不使用 kubeadm 的情况下启动集群。 它也被设计成可以通过 RBAC 策略,结合 Kubelet TLS 启动引导 系统进行工作。

启动引导令牌被定义成一个特定类型的 Secret(bootstrap.kubernetes.io/token), 并存在于 kube-system 名字空间中。 这些 Secret 会被 API 服务器上的启动引导认证组件(Bootstrap Authenticator)读取。 控制器管理器中的控制器 TokenCleaner 能够删除过期的令牌。 这些令牌也被用来在节点发现的过程中会使用的一个特殊的 ConfigMap 对象。 BootstrapSigner 控制器也会使用这一 ConfigMap。

令牌格式

启动引导令牌使用 abcdef.0123456789abcdef 的形式。 更加规范地说,它们必须符合正则表达式 [a-z0-9]{6}\.[a-z0-9]{16}

令牌的第一部分是 “Token ID”,它是一种公开信息,用于引用令牌并确保不会 泄露认证所使用的秘密信息。 第二部分是“令牌秘密(Token Secret)”,它应该被共享给受信的第三方。

启用启动引导令牌

启用启动引导令牌身份认证

启动引导令牌认证组件可以通过 API 服务器上的如下标志启用:

--enable-bootstrap-token-auth

启动引导令牌被启用后,可以作为持有者令牌的凭据,用于 API 服务器请求的身份认证。

Authorization: Bearer 07401b.f395accd246ae52d

令牌认证为用户名 system:bootstrap:<token id> 并且是组 system:bootstrappers 的成员。额外的组信息可以通过令牌的 Secret 来设置。

过期的令牌可以通过启用控制器管理器中的 tokencleaner 控制器来删除。

--controllers=*,tokencleaner

启动引导令牌的 Secret 格式

每个合法的令牌背后对应着 kube-system 名字空间中的某个 Secret 对象。 你可以从 这里 找到完整设计文档。

这是 Secret 看起来的样子。

apiVersion: v1
kind: Secret
metadata:
  # name 必须是 "bootstrap-token-<token id>" 格式的
  name: bootstrap-token-07401b
  namespace: kube-system

# type 必须是 'bootstrap.kubernetes.io/token'
type: bootstrap.kubernetes.io/token
stringData:
  # 供人阅读的描述,可选。
  description: "The default bootstrap token generated by 'kubeadm init'."

  # 令牌 ID 和秘密信息,必需。
  token-id: 07401b
  token-secret: f395accd246ae52d

  # 可选的过期时间字段
  expiration: 2017-03-10T03:22:11Z

  # 允许的用法
  usage-bootstrap-authentication: "true"
  usage-bootstrap-signing: "true"

  # 令牌要认证为的额外组,必须以 "system:bootstrappers:" 开头
  auth-extra-groups: system:bootstrappers:worker,system:bootstrappers:ingress

Secret 的类型必须是 bootstrap.kubernetes.io/token,而且名字必须是 bootstrap-token-<token id>。 令牌必须存在于 kube-system 名字空间中。

usage-bootstrap-* 成员表明这个 Secret 的用途。启用时,值必须设置为 true

  • usage-bootstrap-authentication 表示令牌可以作为持有者令牌用于 API 服务器的身份认证。
  • usage-bootstrap-signing 表示令牌可被用于 cluster-info ConfigMap 的签名, 就像下面描述的那样。

expiration 字段控制令牌的失效期。过期的令牌在用于身份认证时会被拒绝,在用于 ConfigMap 签名时会被忽略。 过期时间值是遵循 RFC3339 进行编码的 UTC 时间。 启用 TokenCleaner 控制器会自动删除过期的令牌。

使用 kubeadm 管理令牌

你可以使用 kubeadm 工具管理运行中集群上的令牌。 参见 kubeadm token 文档 以了解详细信息。

ConfigMap 签名

除了身份认证,令牌还可以用于签名 ConfigMap。 这一用法发生在集群启动过程的早期,在客户端信任 API 服务器之前。 被签名的 ConfigMap 可以被共享令牌完成身份认证。

通过在控制器管理器上启用 bootstrapsigner 控制器可以启用 ConfigMap 签名特性。

--controllers=*,bootstrapsigner

被签名的 ConfigMap 是 kube-public 名字空间中的 cluster-info。 典型的工作流中,客户端在未经认证和忽略 TLS 报错的状态下读取这个 ConfigMap。 通过检查 ConfigMap 中嵌入的签名校验 ConfigMap 的载荷。

ConfigMap 会是这个样子的:

apiVersion: v1
kind: ConfigMap
metadata:
  name: cluster-info
  namespace: kube-public
data:
  jws-kubeconfig-07401b: eyJhbGciOiJIUzI1NiIsImtpZCI6IjA3NDAxYiJ9..tYEfbo6zDNo40MQE07aZcQX2m3EB2rO3NuXtxVMYm9U
  kubeconfig: |
    apiVersion: v1
    clusters:
    - cluster:
        certificate-authority-data: <非常长的证书数据>
        server: https://10.138.0.2:6443
      name: ""
    contexts: []
    current-context: ""
    kind: Config
    preferences: {}
    users: []    

ConfigMap 的 kubeconfig 成员是一个填好了集群信息的配置文件。 这里主要交换的信息是 certificate-authority-data。在将来可能会有扩展。

签名是一个使用 “detached” 模式生成的 JWS 签名。 为了检验签名,用户应该按照 JWS 规则(base64 编码且丢掉结尾的 =)对 kubeconfig 的载荷进行编码。完成编码的载荷会被插入到两个句点中间,形成完整的 JWS。你可以使用完整的令牌(比如 07401b.f395accd246ae52d)作为共享密钥, 通过 HS256 方式 (HMAC-SHA256) 对 JWS 进行校验。 用户必须确保使用了 HS256。

参考 kubeadm 实现细节 了解更多信息。

3.3 - 证书签名请求

特性状态: Kubernetes v1.19 [stable]

证书 API 支持 X.509 的自动化配置, 它为 Kubernetes API 的客户端提供一个编程接口, 用于从证书颁发机构(CA)请求并获取 X.509 证书

CertificateSigningRequest(CSR)资源用来向指定的签名者申请证书签名, 在最终签名之前,申请可能被批准,也可能被拒绝。

请求签名流程

CertificateSigningRequest 资源类型允许客户使用它申请发放 X.509 证书。 CertificateSigningRequest 对象 在 spec.request 中包含一个 PEM 编码的 PKCS#10 签名请求。 CertificateSigningRequest 使用 spec.signerName 字段标示 签名者(请求的接收方)。 注意,spec.signerNamecertificates.k8s.io/v1 之后的 API 版本是必填项。 在 Kubernetes v1.22 和以后的版本,客户可以可选地设置 spec.expirationSeconds 字段来为颁发的证书设定一个特定的有效期。该字段的最小有效值是 600,也就是 10 分钟。

创建完成的 CertificateSigningRequest,要先通过批准,然后才能签名。 根据所选的签名者,CertificateSigningRequest 可能会被 控制器自动批准。 否则,就必须人工批准, 人工批准可以使用 REST API(或 go 客户端),也可以执行 kubectl certificate approve 命令。 同样,CertificateSigningRequest 也可能被驳回, 这就相当于通知了指定的签名者,这个证书不能签名。

对于已批准的证书,下一步是签名。 对应的签名控制器首先验证签名条件是否满足,然后才创建证书。 签名控制器然后更新 CertificateSigningRequest, 将新证书保存到现有 CertificateSigningRequest 对象的 status.certificate 字段中。 此时,字段 status.certificate 要么为空,要么包含一个用 PEM 编码的 X.509 证书。 直到签名完成前,CertificateSigningRequest 的字段 status.certificate 都为空。

一旦 status.certificate 字段完成填充,请求既算完成, 客户端现在可以从 CertificateSigningRequest 资源中获取已签名的证书的 PEM 数据。 当然如果不满足签名条件,签名者可以拒签。

为了减少集群中遗留的过时的 CertificateSigningRequest 资源的数量, 一个垃圾收集控制器将会周期性地运行。 此垃圾收集器会清除在一段时间内没有改变过状态的 CertificateSigningRequests:

  • 已批准的请求:1小时后自动删除
  • 已拒绝的请求:1小时后自动删除
  • 已失败的请求:1小时后自动删除
  • 挂起的请求:24小时后自动删除
  • 所有请求:在颁发的证书过期后自动删除

签名者

也可以指定自定义 signerName。 所有签名者都应该提供自己工作方式的信息, 以便客户端可以预期到他们的 CSR 将发生什么。 此类信息包括:

  1. 信任分发:信任(CA 证书包)是如何分发的。
  2. 许可的主体:当一个受限制的主体(subject)发送请求时,相应的限制和应对手段。
  3. 许可的 x509 扩展:包括 IP subjectAltNames、DNS subjectAltNames、 Email subjectAltNames、URI subjectAltNames 等,请求一个受限制的扩展项时的应对手段。
  4. 许可的密钥用途/扩展的密钥用途:当用途和签名者在 CSR 中指定的用途不同时, 相应的限制和应对手段。
  5. 过期时间/证书有效期:过期时间由签名者确定、由管理员配置、还是由 CSR spec.expirationSeconds 字段指定等, 以及签名者决定的过期时间与 CSR spec.expirationSeconds 字段不同时的应对手段。
  6. 允许/不允许 CA 位:当 CSR 包含一个签名者并不允许的 CA 证书的请求时,相应的应对手段。

一般来说,当 CSR 被批准通过,且证书被签名后,status.certificate 字段 将包含一个 PEM 编码的 X.509 证书。 有些签名者在 status.certificate 字段中存储多个证书。 在这种情况下,签名者的说明文档应当指明附加证书的含义。 例如,这是要在 TLS 握手时提供的证书和中继证书。

PKCS#10 签名请求格式并没有一种标准的方法去设置证书的过期时间或者生命期。 因此,证书的过期时间或者生命期必须通过 CSR 对象的 spec.expirationSeconds 字段来设置。 当 spec.expirationSeconds 没有被指定时,内置的签名者默认使用 ClusterSigningDuration 配置选项 (kube-controller-manager 的命令行选项 --cluster-signing-duration),该选项的默认值设为 1 年。 当 spec.expirationSeconds 被指定时,spec.expirationSecondsClusterSigningDuration 中的最小值会被使用。

Kubernetes 签名者

Kubernetes提供了内置的签名者,每个签名者都有一个众所周知的 signerName:

  1. kubernetes.io/kube-apiserver-client:签名的证书将被 API 服务器视为客户证书。 kube-controller-manager 不会自动批准它。
    1. 信任分发:签名的证书将被 API 服务器视为客户端证书。CA 证书包不通过任何其他方式分发。
    2. 许可的主体:没有主体限制,但审核人和签名者可以选择不批准或不签署。 某些主体,比如集群管理员级别的用户或组因部署和安装方式不同而不同, 所以批准和签署之前需要进行额外仔细审查。 用来限制 system:masters 的 CertificateSubjectRestriction 准入插件默认处于启用状态, 但它通常不是集群中唯一的集群管理员主体。
    3. 许可的 x509 扩展:允许 subjectAltName 和 key usage 扩展,弃用其他扩展。
    4. 许可的密钥用途:必须包含 ["client auth"],但不能包含 ["digital signature", "key encipherment", "client auth"] 之外的键。
    5. 过期时间/证书有效期:对于 kube-controller-manager 实现的签名者, 设置为 --cluster-signing-duration 选项和 CSR 对象的 spec.expirationSeconds 字段(如有设置该字段)中的最小值。
    6. 允许/不允许 CA 位:不允许。
  1. kubernetes.io/kube-apiserver-client-kubelet: 签名的证书将被 kube-apiserver 视为客户证书。 kube-controller-manager 可以自动批准它。

    1. 信任分发:签名的证书将被 API 服务器视为客户端证书。CA 证书包不通过任何其他方式分发。
    2. 许可的主体:组织名必须是 ["system:nodes"],用户名以 "system:node:" 开头
    3. 许可的 x509 扩展:允许 key usage 扩展,禁用 subjectAltName 扩展,并删除其他扩展。
    4. 许可的密钥用途:必须是 ["key encipherment", "digital signature", "client auth"]
    5. 过期时间/证书有效期:对于 kube-controller-manager 实现的签名者, 设置为 --cluster-signing-duration 选项和 CSR 对象的 spec.expirationSeconds 字段(如有设置该字段)中的最小值。
    6. 允许/不允许 CA 位:不允许。
  1. kubernetes.io/kubelet-serving: 签名服务证书,该服务证书被 API 服务器视为有效的 kubelet 服务证书, 但没有其他保证。kube-controller-manager 不会自动批准它。
    1. 信任分发:签名的证书必须被 kube-apiserver 认可,可有效的中止 kubelet 连接。CA 证书包不通过任何其他方式分发。
    2. 许可的主体:组织名必须是 ["system:nodes"],用户名以 "system:node:" 开头
    3. 许可的 x509 扩展:允许 key usage、DNSName/IPAddress subjectAltName 等扩展, 禁止 EmailAddress、URI subjectAltName 等扩展,并丢弃其他扩展。 至少有一个 DNS 或 IP 的 SubjectAltName 存在。
    4. 许可的密钥用途:必须是 ["key encipherment", "digital signature", "server auth"]
    5. 过期时间/证书有效期:对于 kube-controller-manager 实现的签名者, 设置为 --cluster-signing-duration 选项和 CSR 对象的 spec.expirationSeconds 字段(如有设置该字段)中的最小值。
    6. 允许/不允许 CA 位:不允许。
  1. kubernetes.io/legacy-unknown: 不保证信任。Kubernetes 的一些第三方发行版可能会使用它签署的客户端证书。 稳定版的 CertificateSigningRequest API(certificates.k8s.io/v1 以及之后的版本)不允许将 signerName 设置为 kubernetes.io/legacy-unknownkube-controller-manager 不会自动批准这类请求。
    1. 信任分发:没有。这个签名者在 Kubernetes 集群中没有标准的信任或分发。
    2. 许可的主体:全部。
    3. 许可的 x509 扩展:允许 subjectAltName 和 key usage 等扩展,并弃用其他扩展。
    4. 许可的密钥用途:全部。
    5. 过期时间/证书有效期:对于 kube-controller-manager 实现的签名者, 设置为 --cluster-signing-duration 选项和 CSR 对象的 spec.expirationSeconds 字段(如有设置该字段)中的最小值。
    6. 允许/不允许 CA 位 - 不允许。

对于这些签名者,信任的分发发生在带外(out of band)。上述信任之外的任何信任都是完全巧合的。 例如,一些发行版可能会将 kubernetes.io/legacy-unknown 作为 kube-apiserver 的客户端证书, 但这个做法并不标准。 这些用途都没有以任何方式涉及到 ServiceAccount 中的 Secrets .data[ca.crt]。 此 CA 证书包只保证使用默认的服务(kubernetes.default.svc)来验证到 API 服务器的连接。

鉴权

授权创建 CertificateSigningRequest 和检索 CertificateSigningRequest:

  • verbs(动词): creategetlistwatch, group(组):certificates.k8s.io, resources:certificatesigningrequests

例如:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: csr-creator
rules:
- apiGroups:
  - certificates.k8s.io
  resources:
  - certificatesigningrequests
  verbs:
  - create
  - get
  - list
  - watch

授权批准 CertificateSigningRequest:

  • verbs(动词): getlistwatch, group(组):certificates.k8s.io, resources(资源):certificatesigningrequests
  • verbs(动词): update, group(组):certificates.k8s.io, resources(资源):certificatesigningrequests/approval
  • verbs(动词):approve, group(组):certificates.k8s.io, resources(资源):signers, resourceName:<signerNameDomain>/<signerNamePath><signerNameDomain>/*

例如:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: csr-approver
rules:
- apiGroups:
  - certificates.k8s.io
  resources:
  - certificatesigningrequests
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - certificates.k8s.io
  resources:
  - certificatesigningrequests/approval
  verbs:
  - update
- apiGroups:
  - certificates.k8s.io
  resources:
  - signers
  resourceNames:
  - example.com/my-signer-name # example.com/* 可用于为 “example.com” 域中的所有签名者授权
  verbs:
  - approve

授权签名 CertificateSigningRequest:

  • verbs(动词):getlistwatch, group(组):certificates.k8s.io, resources(资源):certificatesigningrequests
  • verbs(动词):update, group(组):certificates.k8s.io, resources(资源):certificatesigningrequests/status
  • verbs(动词):sign, group(组):certificates.k8s.io, resources(资源):signers, resourceName:<signerNameDomain>/<signerNamePath><signerNameDomain>/*
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: csr-signer
rules:
- apiGroups:
  - certificates.k8s.io
  resources:
  - certificatesigningrequests
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - certificates.k8s.io
  resources:
  - certificatesigningrequests/status
  verbs:
  - update
- apiGroups:
  - certificates.k8s.io
  resources:
  - signers
  resourceNames:
  - example.com/my-signer-name # example.com/* 可用于为 “example.com” 域中的所有签名者授权
  verbs:
  - sign

普通用户

为了让普通用户能够通过认证并调用 API,需要执行几个步骤。 首先,该用户必须拥有 Kubernetes 集群签发的证书, 然后将该证书提供给 Kubernetes API。

创建私钥

下面的脚本展示了如何生成 PKI 私钥和 CSR。 设置 CSR 的 CN 和 O 属性很重要。CN 是用户名,O 是该用户归属的组。 你可以参考 RBAC 了解标准组的信息。

openssl genrsa -out myuser.key 2048
openssl req -new -key myuser.key -out myuser.csr

创建 CertificateSigningRequest

创建一个 CertificateSigningRequest,并通过 kubectl 将其提交到 Kubernetes 集群。 下面是生成 CertificateSigningRequest 的脚本。

cat <<EOF | kubectl apply -f -
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
  name: myuser
spec:
  request: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQ1ZqQ0NBVDRDQVFBd0VURVBNQTBHQTFVRUF3d0dZVzVuWld4aE1JSUJJakFOQmdrcWhraUc5dzBCQVFFRgpBQU9DQVE4QU1JSUJDZ0tDQVFFQTByczhJTHRHdTYxakx2dHhWTTJSVlRWMDNHWlJTWWw0dWluVWo4RElaWjBOCnR2MUZtRVFSd3VoaUZsOFEzcWl0Qm0wMUFSMkNJVXBGd2ZzSjZ4MXF3ckJzVkhZbGlBNVhwRVpZM3ExcGswSDQKM3Z3aGJlK1o2MVNrVHF5SVBYUUwrTWM5T1Nsbm0xb0R2N0NtSkZNMUlMRVI3QTVGZnZKOEdFRjJ6dHBoaUlFMwpub1dtdHNZb3JuT2wzc2lHQ2ZGZzR4Zmd4eW8ybmlneFNVekl1bXNnVm9PM2ttT0x1RVF6cXpkakJ3TFJXbWlECklmMXBMWnoyalVnald4UkhCM1gyWnVVV1d1T09PZnpXM01LaE8ybHEvZi9DdS8wYk83c0x0MCt3U2ZMSU91TFcKcW90blZtRmxMMytqTy82WDNDKzBERHk5aUtwbXJjVDBnWGZLemE1dHJRSURBUUFCb0FBd0RRWUpLb1pJaHZjTgpBUUVMQlFBRGdnRUJBR05WdmVIOGR4ZzNvK21VeVRkbmFjVmQ1N24zSkExdnZEU1JWREkyQTZ1eXN3ZFp1L1BVCkkwZXpZWFV0RVNnSk1IRmQycVVNMjNuNVJsSXJ3R0xuUXFISUh5VStWWHhsdnZsRnpNOVpEWllSTmU3QlJvYXgKQVlEdUI5STZXT3FYbkFvczFqRmxNUG5NbFpqdU5kSGxpT1BjTU1oNndLaTZzZFhpVStHYTJ2RUVLY01jSVUyRgpvU2djUWdMYTk0aEpacGk3ZnNMdm1OQUxoT045UHdNMGM1dVJVejV4T0dGMUtCbWRSeEgvbUNOS2JKYjFRQm1HCkkwYitEUEdaTktXTU0xMzhIQXdoV0tkNjVoVHdYOWl4V3ZHMkh4TG1WQzg0L1BHT0tWQW9FNkpsYWFHdTlQVmkKdjlOSjVaZlZrcXdCd0hKbzZXdk9xVlA3SVFjZmg3d0drWm89Ci0tLS0tRU5EIENFUlRJRklDQVRFIFJFUVVFU1QtLS0tLQo=
  signerName: kubernetes.io/kube-apiserver-client
  expirationSeconds: 86400  # one day
  usages:
  - client auth
EOF

需要注意的几点:

  • usage 字段必须是 'client auth'
  • expirationSeconds 可以设置为更长(例如 864000 是十天)或者更短(例如 3600 是一个小时)
  • request 字段是 CSR 文件内容的 base64 编码值。 要得到该值,可以执行命令 cat myuser.csr | base64 | tr -d "\n"

批准证书签名请求

使用 kubectl 创建 CSR 并批准。

获取 CSR 列表:

kubectl get csr

批准 CSR:

kubectl certificate approve myuser

取得证书

从 CSR 取得证书:

kubectl get csr/myuser -o yaml

证书的内容使用 base64 编码,存放在字段 status.certificate

从 CertificateSigningRequest 导出颁发的证书。

kubectl get csr myuser -o jsonpath='{.status.certificate}'| base64 -d > myuser.crt

创建角色和角色绑定

创建了证书之后,为了让这个用户能访问 Kubernetes 集群资源,现在就要创建 Role 和 RoleBinding 了。

下面是为这个新用户创建 Role 的示例命令:

kubectl create role developer --verb=create --verb=get --verb=list --verb=update --verb=delete --resource=pods

下面是为这个新用户创建 RoleBinding 的示例命令:

kubectl create rolebinding developer-binding-myuser --role=developer --user=myuser

添加到 kubeconfig

最后一步是将这个用户添加到 kubeconfig 文件。 我们假设私钥和证书文件存放在 “/home/vagrant/work/” 目录中。

首先,我们需要添加新的凭据:

kubectl config set-credentials myuser --client-key=myuser.key --client-certificate=myuser.crt --embed-certs=true

然后,你需要添加上下文:

kubectl config set-context myuser --cluster=kubernetes --user=myuser

来测试一下,把上下文切换为 myuser

kubectl config use-context myuser

批准和驳回

控制平面的自动化批准

kube-controller-manager 内建了一个证书批准者,其 signerName 为 kubernetes.io/kube-apiserver-client-kubelet, 该批准者将 CSR 上用于节点凭据的各种权限委托给权威认证机构。 kube-controller-manager 将 SubjectAccessReview 资源发送(POST)到 API 服务器, 以便检验批准证书的授权。

使用 kubectl 批准或驳回

Kubernetes 管理员(拥有足够的权限)可以手工批准(或驳回)CertificateSigningRequests, 此操作使用 kubectl certificate approvekubectl certificate deny 命令实现。

使用 kubectl 批准一个 CSR:

kubectl certificate approve <certificate-signing-request-name>

同样地,驳回一个 CSR:

kubectl certificate deny <certificate-signing-request-name>

使用 Kubernetes API 批准或驳回

REST API 的用户可以通过向待批准的 CSR 的 approval 子资源提交更新请求来批准 CSR。 例如,你可以编写一个 operator 来监视特定类型的 CSR,然后发送一个更新来批准它。

当你发出批准或驳回的指令时,根据你期望的状态来选择设置 ApprovedDenied

批准(Approved) 的 CSR:

apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
...
status:
  conditions:
  - lastUpdateTime: "2020-02-08T11:37:35Z"
    lastTransitionTime: "2020-02-08T11:37:35Z"
    message: Approved by my custom approver controller
    reason: ApprovedByMyPolicy # You can set this to any string
    type: Approved

驳回(Denied)的 CSR:

apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
...
status:
  conditions:
  - lastUpdateTime: "2020-02-08T11:37:35Z"
    lastTransitionTime: "2020-02-08T11:37:35Z"
    message: Denied by my custom approver controller
    reason: DeniedByMyPolicy # You can set this to any string
    type: Denied

status.conditions.reason 字段通常设置为一个首字母大写的对机器友好的原因码; 这是一个命名约定,但你也可以随你的个人喜好设置。 如果你想添加一个供人类使用的注释,那就用 status.conditions.message 字段。

签名

控制平面签名者

Kubernetes 控制平面实现了每一个 Kubernetes 签名者, 每个签名者的实现都是 kube-controller-manager 的一部分。

基于 API 的签名者

REST API 的用户可以通过向待签名的 CSR 的 status 子资源提交更新请求来对 CSR 进行签名。

作为这个请求的一部分,status.certificate 字段应设置为已签名的证书。 此字段可包含一个或多个 PEM 编码的证书。

所有的 PEM 块必须具备 "CERTIFICATE" 标签,且不包含文件头,且编码的数据必须是 RFC5280 第 4 节 中描述的 BER 编码的 ASN.1 证书结构。

-----BEGIN CERTIFICATE-----
MIIDgjCCAmqgAwIBAgIUC1N1EJ4Qnsd322BhDPRwmg3b/oAwDQYJKoZIhvcNAQEL
BQAwXDELMAkGA1UEBhMCeHgxCjAIBgNVBAgMAXgxCjAIBgNVBAcMAXgxCjAIBgNV
BAoMAXgxCjAIBgNVBAsMAXgxCzAJBgNVBAMMAmNhMRAwDgYJKoZIhvcNAQkBFgF4
MB4XDTIwMDcwNjIyMDcwMFoXDTI1MDcwNTIyMDcwMFowNzEVMBMGA1UEChMMc3lz
dGVtOm5vZGVzMR4wHAYDVQQDExVzeXN0ZW06bm9kZToxMjcuMC4wLjEwggEiMA0G
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDne5X2eQ1JcLZkKvhzCR4Hxl9+ZmU3
+e1zfOywLdoQxrPi+o4hVsUH3q0y52BMa7u1yehHDRSaq9u62cmi5ekgXhXHzGmm
kmW5n0itRECv3SFsSm2DSghRKf0mm6iTYHWDHzUXKdm9lPPWoSOxoR5oqOsm3JEh
Q7Et13wrvTJqBMJo1GTwQuF+HYOku0NF/DLqbZIcpI08yQKyrBgYz2uO51/oNp8a
sTCsV4OUfyHhx2BBLUo4g4SptHFySTBwlpRWBnSjZPOhmN74JcpTLB4J5f4iEeA7
2QytZfADckG4wVkhH3C2EJUmRtFIBVirwDn39GXkSGlnvnMgF3uLZ6zNAgMBAAGj
YTBfMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDAjAMBgNVHRMB
Af8EAjAAMB0GA1UdDgQWBBTREl2hW54lkQBDeVCcd2f2VSlB1DALBgNVHREEBDAC
ggAwDQYJKoZIhvcNAQELBQADggEBABpZjuIKTq8pCaX8dMEGPWtAykgLsTcD2jYr
L0/TCrqmuaaliUa42jQTt2OVsVP/L8ofFunj/KjpQU0bvKJPLMRKtmxbhXuQCQi1
qCRkp8o93mHvEz3mTUN+D1cfQ2fpsBENLnpS0F4G/JyY2Vrh19/X8+mImMEK5eOy
o0BMby7byUj98WmcUvNCiXbC6F45QTmkwEhMqWns0JZQY+/XeDhEcg+lJvz9Eyo2
aGgPsye1o3DpyXnyfJWAWMhOz7cikS5X2adesbgI86PhEHBXPIJ1v13ZdfCExmdd
M1fLPhLyR54fGaY+7/X8P9AZzPefAkwizeXwe9ii6/a08vWoiE4=
-----END CERTIFICATE-----

非 PEM 内容可能会出现在证书 PEM 块前后的位置,且未经验证, 以允许使用 RFC7468 第 5.2 节中描述的解释性文本。

当使用 JSON 或 YAML 格式时,此字段是 base-64 编码。 包含上述示例证书的 CertificateSigningRequest 如下所示:

apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
...
status:
  certificate: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JS..."

接下来

3.4 - 准入控制器参考

此页面提供准入控制器(Admission Controllers)的概述。

什么是准入控制插件?

准入控制器 是一段代码,它会在请求通过认证和鉴权之后、对象被持久化之前拦截到达 API 服务器的请求。

准入控制器可以执行验证(Validating) 和/或变更(Mutating) 操作。 变更(mutating)控制器可以根据被其接受的请求更改相关对象;验证(validating)控制器则不行。

准入控制器限制创建、删除、修改对象的请求。 准入控制器也可以阻止自定义动作,例如通过 API 服务器代理连接到 Pod 的请求。 准入控制器不会 (也不能)阻止读取(getwatchlist)对象的请求。

Kubernetes 1.25 中的准入控制器由下面的列表组成, 并编译进 kube-apiserver 可执行文件,并且只能由集群管理员配置。 在该列表中,有两个特殊的控制器:MutatingAdmissionWebhook 和 ValidatingAdmissionWebhook。 它们根据 API 中的配置, 分别执行变更和验证准入控制 webhook

准入控制阶段

准入控制过程分为两个阶段。第一阶段,运行变更准入控制器。第二阶段,运行验证准入控制器。 再次提醒,某些控制器既是变更准入控制器又是验证准入控制器。

如果两个阶段之一的任何一个控制器拒绝了某请求,则整个请求将立即被拒绝,并向最终用户返回错误。

最后,除了对对象进行变更外,准入控制器还可能有其它副作用:将相关资源作为请求处理的一部分进行变更。 增加配额用量就是一个典型的示例,说明了这样做的必要性。 此类用法都需要相应的回收或回调过程,因为任一准入控制器都无法确定某个请求能否通过所有其它准入控制器。

为什么需要准入控制器?

Kubernetes 的若干重要功能都要求启用一个准入控制器,以便正确地支持该特性。 因此,没有正确配置准入控制器的 Kubernetes API 服务器是不完整的,它无法支持你所期望的所有特性。

如何启用一个准入控制器?

Kubernetes API 服务器的 enable-admission-plugins 标志接受一个(以逗号分隔的)准入控制插件列表, 这些插件会在集群修改对象之前被调用。

例如,下面的命令启用 NamespaceLifecycleLimitRanger 准入控制插件:

kube-apiserver --enable-admission-plugins=NamespaceLifecycle,LimitRanger ...

怎么关闭准入控制器?

Kubernetes API 服务器的 disable-admission-plugins 标志,会将传入的(以逗号分隔的) 准入控制插件列表禁用,即使是默认启用的插件也会被禁用。

kube-apiserver --disable-admission-plugins=PodNodeSelector,AlwaysDeny ...

哪些插件是默认启用的?

要查看哪些插件是被启用的:

kube-apiserver -h | grep enable-admission-plugins

在 Kubernetes 1.25 中,默认启用的插件有:

CertificateApproval, CertificateSigning, CertificateSubjectRestriction, DefaultIngressClass, DefaultStorageClass, DefaultTolerationSeconds, LimitRanger, MutatingAdmissionWebhook, NamespaceLifecycle, PersistentVolumeClaimResize, PodSecurity, Priority, ResourceQuota, RuntimeClass, ServiceAccount, StorageObjectInUseProtection, TaintNodesByCondition, ValidatingAdmissionWebhook

每个准入控制器的作用是什么?

AlwaysAdmit

特性状态: Kubernetes v1.13 [deprecated]

该准入控制器允许所有的 Pod 进入集群。此插件已被弃用,因其行为与没有准入控制器一样。

AlwaysDeny

特性状态: Kubernetes v1.13 [deprecated]

拒绝所有的请求。由于它没有实际意义,已被弃用

AlwaysPullImages

该准入控制器会修改每个新创建的 Pod,将其镜像拉取策略设置为 Always。 这在多租户集群中是有用的,这样用户就可以放心,他们的私有镜像只能被那些有凭证的人使用。 如果没有这个准入控制器,一旦镜像被拉取到节点上,任何用户的 Pod 都可以通过已了解到的镜像的名称 (假设 Pod 被调度到正确的节点上)来使用它,而不需要对镜像进行任何鉴权检查。 启用这个准入控制器之后,启动容器之前必须拉取镜像,这意味着需要有效的凭证。

CertificateApproval

此准入控制器获取审批 CertificateSigningRequest 资源的请求并执行额外的鉴权检查, 以确保针对设置了 spec.signerName 的 CertificateSigningRequest 资源而言, 审批请求的用户有权限对证书请求执行 审批 操作。

有关对 CertificateSigningRequest 资源执行不同操作所需权限的详细信息, 请参阅证书签名请求

CertificateSigning

此准入控制器监视对 CertificateSigningRequest 资源的 status.certificate 字段的更新请求, 并执行额外的鉴权检查,以确保针对设置了 spec.signerName 的 CertificateSigningRequest 资源而言, 签发证书的用户有权限对证书请求执行 签发 操作。

有关对 CertificateSigningRequest 资源执行不同操作所需权限的详细信息, 请参阅证书签名请求

CertificateSubjectRestriction

此准入控制器监视 spec.signerName 被设置为 kubernetes.io/kube-apiserver-client 的 CertificateSigningRequest 资源创建请求,并拒绝所有将 “group”(或 “organization attribute”) 设置为 system:masters 的请求。

DefaultIngressClass

该准入控制器监测没有请求任何特定 Ingress 类的 Ingress 对象创建请求,并自动向其添加默认 Ingress 类。 这样,没有任何特殊 Ingress 类需求的用户根本不需要关心它们,他们将被设置为默认 Ingress 类。

当未配置默认 Ingress 类时,此准入控制器不执行任何操作。如果有多个 Ingress 类被标记为默认 Ingress 类, 此控制器将拒绝所有创建 Ingress 的操作,并返回错误信息。 要修复此错误,管理员必须重新检查其 IngressClass 对象,并仅将其中一个标记为默认 (通过注解 "ingressclass.kubernetes.io/is-default-class")。 此准入控制器会忽略所有 Ingress 更新操作,仅处理创建操作。

关于 Ingress 类以及如何将 Ingress 类标记为默认的更多信息,请参见 Ingress 页面。

DefaultStorageClass

此准入控制器监测没有请求任何特定存储类的 PersistentVolumeClaim 对象的创建请求, 并自动向其添加默认存储类。 这样,没有任何特殊存储类需求的用户根本不需要关心它们,它们将被设置为使用默认存储类。

当未配置默认存储类时,此准入控制器不执行任何操作。如果将多个存储类标记为默认存储类, 此控制器将拒绝所有创建 PersistentVolumeClaim 的请求,并返回错误信息。 要修复此错误,管理员必须重新检查其 StorageClass 对象,并仅将其中一个标记为默认。 此准入控制器会忽略所有 PersistentVolumeClaim 更新操作,仅处理创建操作。

关于持久卷申领和存储类,以及如何将存储类标记为默认,请参见持久卷页面。

DefaultTolerationSeconds

此准入控制器基于 k8s-apiserver 的输入参数 default-not-ready-toleration-secondsdefault-unreachable-toleration-seconds 为 Pod 设置默认的容忍度,以容忍 notready:NoExecuteunreachable:NoExecute 污点 (如果 Pod 尚未容忍 node.kubernetes.io/not-ready:NoExecutenode.kubernetes.io/unreachable:NoExecute 污点的话)。 default-not-ready-toleration-secondsdefault-unreachable-toleration-seconds 的默认值是 5 分钟。

DenyServiceExternalIPs

此准入控制器拒绝新的 Service 中使用字段 externalIPs。 此功能非常强大(允许网络流量拦截),并且无法很好地受策略控制。 启用后,集群用户将无法创建使用 externalIPs 的新 Service,也无法在现有 Service 对象上为 externalIPs 添加新值。 externalIPs 的现有使用不受影响,用户可以在现有 Service 对象上从 externalIPs 中删除值。

大多数用户根本不需要此特性,集群管理员应考虑将其禁用。 确实需要使用此特性的集群应考虑使用一些自定义策略来管理 externalIPs 的使用。 此准入控制器默认被禁用。

EventRateLimit

特性状态: Kubernetes v1.13 [alpha]

此准入控制器缓解了请求存储新事件时淹没 API 服务器的问题。集群管理员可以通过以下方式指定事件速率限制:

  • 启用 EventRateLimit 准入控制器;
  • 在通过 API 服务器的命令行标志 --admission-control-config-file 设置的文件中, 引用 EventRateLimit 配置文件:
apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
  - name: EventRateLimit
    path: eventconfig.yaml
...

可以在配置中指定的限制有四种类型:

  • Server:API 服务器收到的所有(创建或修改)Event 请求共享一个桶。
  • Namespace:每个名字空间都对应一个专用的桶。
  • User:为每个用户分配一个桶。
  • SourceAndObject:根据事件的来源和涉及对象的各种组合分配桶。

下面是一个针对此配置的 eventconfig.yaml 示例:

apiVersion: eventratelimit.admission.k8s.io/v1alpha1
kind: Configuration
limits:
  - type: Namespace
    qps: 50
    burst: 100
    cacheSize: 2000
  - type: User
    qps: 10
    burst: 50

详情请参见 EventRateLimit 配置 API 文档(v1alpha1)

此准入控制器默认被禁用。

ExtendedResourceToleration

此插件有助于创建带有扩展资源的专用节点。 如果运维人员想要创建带有扩展资源(如 GPU、FPGA 等)的专用节点,他们应该以扩展资源名称作为键名, 为节点设置污点。 如果启用了此准入控制器,会将此类污点的容忍度自动添加到请求扩展资源的 Pod 中, 用户不必再手动添加这些容忍度。

此准入控制器默认被禁用。

ImagePolicyWebhook

ImagePolicyWebhook 准入控制器允许使用后端 Webhook 做出准入决策。

此准入控制器默认被禁用。

配置文件格式

ImagePolicyWebhook 使用配置文件来为后端行为设置选项。该文件可以是 JSON 或 YAML, 并具有以下格式:

imagePolicy:
  kubeConfigFile: /path/to/kubeconfig/for/backend
  # 以秒计的时长,控制批准请求的缓存时间
  allowTTL: 50
  # 以秒计的时长,控制拒绝请求的缓存时间
  denyTTL: 50
  # 以毫秒计的时长,控制重试间隔
  retryBackoff: 500
  # 确定 Webhook 后端失效时的行为
  defaultAllow: true

在通过命令行标志 --admission-control-config-file 为 API 服务器提供的文件中, 引用 ImagePolicyWebhook 配置文件:

apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
  - name: ImagePolicyWebhook
    path: imagepolicyconfig.yaml
...

或者,你也可以直接将配置嵌入到该文件中:

apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
  - name: ImagePolicyWebhook
    configuration:
      imagePolicy:
        kubeConfigFile: <kubeconfig 文件路径>
        allowTTL: 50
        denyTTL: 50
        retryBackoff: 500
        defaultAllow: true

ImagePolicyWebhook 的配置文件必须引用 kubeconfig 格式的文件;该文件用来设置与后端的连接。要求后端使用 TLS 进行通信。

kubeconfig 文件的 clusters 字段需要指向远端服务,users 字段需要包含已返回的授权者。

# clusters 指的是远程服务。
clusters:
  - name: name-of-remote-imagepolicy-service
    cluster:
      certificate-authority: /path/to/ca.pem    # CA 用于验证远程服务
      server: https://images.example.com/policy # 要查询的远程服务的 URL,必须是 'https'。

# users 指的是 API 服务器的 Webhook 配置。
users:
  - name: name-of-api-server
    user:
      client-certificate: /path/to/cert.pem # Webhook 准入控制器使用的证书
      client-key: /path/to/key.pem          # 证书匹配的密钥

关于 HTTP 配置的更多信息,请参阅 kubeconfig 文档。

请求载荷

当面对一个准入决策时,API 服务器发送一个描述操作的 JSON 序列化的 imagepolicy.k8s.io/v1alpha1 ImageReview 对象。 该对象包含描述被准入容器的字段,以及与 *.image-policy.k8s.io/* 匹配的所有 Pod 注解。

请求载荷示例:

{
  "apiVersion":"imagepolicy.k8s.io/v1alpha1",
  "kind":"ImageReview",
  "spec":{
    "containers":[
      {
        "image":"myrepo/myimage:v1"
      },
      {
        "image":"myrepo/myimage@sha256:beb6bd6a68f114c1dc2ea4b28db81bdf91de202a9014972bec5e4d9171d90ed"
      }
    ],
    "annotations":{
      "mycluster.image-policy.k8s.io/ticket-1234": "break-glass"
    },
    "namespace":"mynamespace"
  }
}

远程服务将填充请求的 status 字段,并返回允许或不允许访问的响应。 响应体的 spec 字段会被忽略,并且可以被省略。一个允许访问应答会返回:

{
  "apiVersion": "imagepolicy.k8s.io/v1alpha1",
  "kind": "ImageReview",
  "status": {
    "allowed": true
  }
}

若不允许访问,服务将返回:

{
  "apiVersion": "imagepolicy.k8s.io/v1alpha1",
  "kind": "ImageReview",
  "status": {
    "allowed": false,
    "reason": "image currently blacklisted"
  }
}

更多的文档,请参阅 imagepolicy.v1alpha1 API

使用注解进行扩展

一个 Pod 中匹配 *.image-policy.k8s.io/* 的注解都会被发送给 Webhook。 这样做使得了解后端镜像策略的用户可以向它发送额外的信息, 并让不同的后端实现接收不同的信息。

你可以在这里输入的信息有:

  • 在紧急情况下,请求破例覆盖某个策略。
  • 从一个记录了破例的请求的工单(Ticket)系统得到的一个工单号码。
  • 向策略服务器提供提示信息,用于提供镜像的 imageID,以方便它进行查找。

在任何情况下,注解都是由用户提供的,并不会被 Kubernetes 以任何方式进行验证。

LimitPodHardAntiAffinityTopology

此准入控制器拒绝定义了 AntiAffinity 拓扑键的任何 Pod (requiredDuringSchedulingRequiredDuringExecution 中的 kubernetes.io/hostname 除外)。

此准入控制器默认被禁用。

LimitRanger

此准入控制器会监测传入的请求,并确保请求不会违反 NamespaceLimitRange 对象所设置的任何约束。 如果你在 Kubernetes 部署中使用了 LimitRange 对象,则必须使用此准入控制器来执行这些约束。 LimitRanger 还可以用于将默认资源请求应用到没有设定资源约束的 Pod; 当前,默认的 LimitRanger 对 default 名字空间中的所有 Pod 都设置 0.1 CPU 的需求。

请查看 limitRange API 文档LimitRange 例子以了解更多细节。

MutatingAdmissionWebhook

此准入控制器调用任何与请求匹配的变更(Mutating) Webhook。匹配的 Webhook 将被顺序调用。 每一个 Webhook 都可以自由修改对象。

MutatingAdmissionWebhook,顾名思义,仅在变更阶段运行。

如果由此准入控制器调用的 Webhook 有副作用(如:减少配额), 则它 必须 具有协调系统,因为不能保证后续的 Webhook 和验证准入控制器都会允许完成请求。

如果你禁用了 MutatingAdmissionWebhook,那么还必须使用 --runtime-config 标志禁止 admissionregistration.k8s.io/v1 组/版本中的 MutatingWebhookConfiguration, 二者都是默认启用的。

谨慎编写和安装变更 webhook

  • 当用户尝试创建的对象与返回的对象不同时,用户可能会感到困惑。
  • 当他们读回的对象与尝试创建的对象不同,内建的控制回路可能会出问题。
    • 与覆盖原始请求中设置的字段相比,使用原始请求未设置的字段会引起问题的可能性较小。 应尽量避免覆盖原始请求中的字段设置。
  • 内建资源和第三方资源的控制回路未来可能会出现破坏性的变更,使现在运行良好的 Webhook 无法再正常运行。即使完成了 Webhook API 安装,也不代表该 Webhook 会被提供无限期的支持。

NamespaceAutoProvision

此准入控制器会检查针对名字空间域资源的所有传入请求,并检查所引用的名字空间是否确实存在。 如果找不到所引用的名字空间,控制器将创建一个名字空间。 此准入控制器对于不想要求名字空间必须先创建后使用的集群部署很有用。

NamespaceExists

此准入控制器检查针对名字空间作用域的资源(除 Namespace 自身)的所有请求。 如果请求引用的名字空间不存在,则拒绝该请求。

NamespaceLifecycle

该准入控制器禁止在一个正在被终止的 Namespace 中创建新对象,并确保针对不存在的 Namespace 的请求被拒绝。 该准入控制器还会禁止删除三个系统保留的名字空间,即 defaultkube-systemkube-public

Namespace 的删除操作会触发一系列删除该名字空间中所有对象(Pod、Service 等)的操作。 为了确保这个过程的完整性,我们强烈建议启用这个准入控制器。

NodeRestriction

该准入控制器限制了某 kubelet 可以修改的 NodePod 对象。 为了受到这个准入控制器的限制,kubelet 必须使用在 system:nodes 组中的凭证, 并使用 system:node:<nodeName> 形式的用户名。 这样,kubelet 只可修改自己的 Node API 对象,只能修改绑定到自身节点的 Pod 对象。

不允许 kubelet 更新或删除 Node API 对象的污点。

NodeRestriction 准入插件可防止 kubelet 删除其 Node API 对象, 并对前缀为 kubernetes.io/k8s.io/ 的标签的修改对 kubelet 作如下限制:

  • 禁止 kubelet 添加、删除或更新前缀为 node-restriction.kubernetes.io/ 的标签。 这类前缀的标签时保留给管理员的,用以为 Node 对象设置标签以隔离工作负载,而不允许 kubelet 修改带有该前缀的标签。
  • 允许 kubelet 添加、删除、更新以下标签:
    • kubernetes.io/hostname
    • kubernetes.io/arch
    • kubernetes.io/os
    • beta.kubernetes.io/instance-type
    • node.kubernetes.io/instance-type
    • failure-domain.beta.kubernetes.io/region (已弃用)
    • failure-domain.beta.kubernetes.io/zone (已弃用)
    • topology.kubernetes.io/region
    • topology.kubernetes.io/zone
    • kubelet.kubernetes.io/ 为前缀的标签
    • node.kubernetes.io/ 为前缀的标签

kubernetes.iok8s.io 为前缀的所有其他标签都限制 kubelet 使用,并且将来可能会被 NodeRestriction 准入插件允许或禁止。

将来的版本可能会增加其他限制,以确保 kubelet 具有正常运行所需的最小权限集。

OwnerReferencesPermissionEnforcement

此准入控制器保护对对象的 metadata.ownerReferences 的访问,以便只有对该对象具有 delete 权限的用户才能对其进行更改。 该准入控制器还保护对 metadata.ownerReferences[x].blockOwnerDeletion 对象的访问, 以便只有对所引用的 属主(owner)finalizers 子资源具有 update 权限的用户才能对其进行更改。

PersistentVolumeClaimResize

特性状态: Kubernetes v1.24 [stable]

此准入控制器检查传入的 PersistentVolumeClaim 调整大小请求,对其执行额外的验证检查操作。

建议启用 PersistentVolumeClaimResize 准入控制器。除非 PVC 的 StorageClass 明确地将 allowVolumeExpansion 设置为 true 来显式启用调整大小。 否则,默认情况下该准入控制器会阻止所有对 PVC 大小的调整。

例如:由以下 StorageClass 创建的所有 PersistentVolumeClaim 都支持卷容量扩充:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: gluster-vol-default
provisioner: kubernetes.io/glusterfs
parameters:
  resturl: "http://192.168.10.100:8080"
  restuser: ""
  secretNamespace: ""
  secretName: ""
allowVolumeExpansion: true

关于持久化卷申领的更多信息,请参见 PersistentVolumeClaim

PersistentVolumeLabel

特性状态: Kubernetes v1.13 [deprecated]

此准入控制器会自动将由云提供商(如 Azure 或 GCP)定义的区(region)或区域(zone) 标签附加到 PersistentVolume 上。这有助于确保 Pod 和 PersistentVolume 位于相同的区或区域。 如果准入控制器不支持为 PersistentVolumes 自动添加标签,那你可能需要手动添加标签, 以防止 Pod 挂载其他区域的卷。 PersistentVolumeLabel 已被弃用, 为持久卷添加标签的操作已由云管理控制器接管。

此准入控制器默认被禁用。

PodNodeSelector

特性状态: Kubernetes v1.5 [alpha]

此准入控制器通过读取名字空间注解和全局配置,来为名字空间中可以使用的节点选择器设置默认值并实施限制。

此准入控制器默认被禁用。

配置文件格式

PodNodeSelector 使用配置文件来设置后端行为的选项。 请注意,配置文件格式将在将来某个版本中改为版本化文件。 该文件可以是 JSON 或 YAML,格式如下:

podNodeSelectorPluginConfig:
 clusterDefaultNodeSelector: name-of-node-selector
 namespace1: name-of-node-selector
 namespace2: name-of-node-selector

通过 API 服务器命令行标志 --admission-control-config-file 为 API 服务器提供的文件中, 需要引用 PodNodeSelector 配置文件:

apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: PodNodeSelector
  path: podnodeselector.yaml
...

配置注解格式

PodNodeSelector 使用键为 scheduler.alpha.kubernetes.io/node-selector 的注解为名字空间设置节点选择算符。

apiVersion: v1
kind: Namespace
metadata:
  annotations:
    scheduler.alpha.kubernetes.io/node-selector: name-of-node-selector
  name: namespace3

内部行为

此准入控制器行为如下:

  1. 如果 Namespace 的注解带有键 scheduler.alpha.kubernetes.io/node-selector, 则将其值用作节点选择算符。
  2. 如果名字空间缺少此类注解,则使用 PodNodeSelector 插件配置文件中定义的 clusterDefaultNodeSelector 作为节点选择算符。
  3. 评估 Pod 节点选择算符和名字空间节点选择算符是否存在冲突。存在冲突将拒绝 Pod。
  4. 评估 Pod 节点选择算符和特定于名字空间的被允许的选择算符所定义的插件配置文件是否存在冲突。 存在冲突将导致拒绝 Pod。

PodSecurity

特性状态: Kubernetes v1.25 [stable]

这是下节所讨论的已被废弃的 PodSecurityPolicy 准入控制器的替代品。 此准入控制器负责在创建和修改 Pod 时,根据请求的安全上下文和 Pod 安全标准 来确定是否可以执行请求。

更多信息请参阅 Pod 安全性准入控制器

PodSecurityPolicy

特性状态: Kubernetes v1.21 [deprecated]

此准入控制器负责在创建和修改 Pod 时根据请求的安全上下文和可用的 Pod 安全策略确定是否可以执行请求。

查看 Pod 安全策略文档进一步了解其间细节。

PodTolerationRestriction

特性状态: Kubernetes v1.7 [alpha]

准入控制器 PodTolerationRestriction 检查 Pod 的容忍度与其名字空间的容忍度之间是否存在冲突。 如果存在冲突,则拒绝 Pod 请求。 控制器接下来会将名字空间的容忍度合并到 Pod 的容忍度中, 根据名字空间的容忍度白名单检查所得到的容忍度结果。 如果检查成功,则将接受 Pod 请求,否则拒绝该请求。

如果 Pod 的名字空间没有任何关联的默认容忍度或容忍度白名单, 则使用集群级别的默认容忍度或容忍度白名单(如果有的话)。

名字空间的容忍度通过注解键 scheduler.alpha.kubernetes.io/defaultTolerations 来设置。可接受的容忍度可以通过 scheduler.alpha.kubernetes.io/tolerationsWhitelist 注解键来添加。

名字空间注解的示例:

apiVersion: v1
kind: Namespace
metadata:
  name: apps-that-need-nodes-exclusively
  annotations:
    scheduler.alpha.kubernetes.io/defaultTolerations: '[{"operator": "Exists", "effect": "NoSchedule", "key": "dedicated-node"}]'
    scheduler.alpha.kubernetes.io/tolerationsWhitelist: '[{"operator": "Exists", "effect": "NoSchedule", "key": "dedicated-node"}]'

此准入控制器默认被禁用。

优先级

优先级准入控制器使用 priorityClassName 字段并用整型值填充优先级。 如果找不到优先级,则拒绝 Pod。

ResourceQuota

此准入控制器会监测传入的请求,并确保它不违反任何一个 Namespace 中的 ResourceQuota 对象中列举的约束。如果你在 Kubernetes 部署中使用了 ResourceQuota, 则必须使用这个准入控制器来强制执行配额限制。

请参阅 resourceQuota API 参考Resource Quota 例子了解更多细节。

RuntimeClass

如果你所定义的 RuntimeClass 包含 Pod 开销, 这个准入控制器会检查新的 Pod。 被启用后,此准入控制器会拒绝所有已经设置了 overhead 字段的 Pod 创建请求。 对于配置了 RuntimeClass 并在其 .spec 中选定 RuntimeClass 的 Pod, 此准入控制器会根据相应 RuntimeClass 中定义的值为 Pod 设置 .spec.overhead

详情请参见 Pod 开销

SecurityContextDeny

此准入控制器将拒绝任何试图设置特定提升 SecurityContext 中某些字段的 Pod,正如任务为 Pod 或 Container 配置安全上下文 中所展示的那样。如果集群没有使用 Pod 安全性准入PodSecurityPolicy, 也没有任何外部强制机制,那么你可以使用此准入控制器来限制安全上下文所能获取的值集。

有关限制 Pod 权限的更多内容,请参阅 Pod 安全标准

ServiceAccount

此准入控制器实现了 ServiceAccount 的自动化。强烈推荐为 Kubernetes 项目启用此准入控制器。 如果你打算使用 Kubernetes 的 ServiceAccount 对象,你应启用这个准入控制器。

StorageObjectInUseProtection

StorageObjectInUseProtection 插件将 kubernetes.io/pvc-protectionkubernetes.io/pv-protection finalizers 添加到新创建的持久卷申领(PVC) 或持久卷(PV)中。如果用户尝试删除 PVC/PV,除非 PVC/PV 的保护控制器移除终结器(finalizers), 否则 PVC/PV 不会被删除。有关更多详细信息, 请参考保护使用中的存储对象

TaintNodesByCondition

该准入控制器为新创建的节点添加 NotReadyNoSchedule 污点。 这些污点能够避免一些竞态条件的发生,而这类竞态条件可能导致 Pod 在更新节点污点以准确反映其所报告状况之前,就被调度到新节点上。

ValidatingAdmissionWebhook

此准入控制器调用与请求匹配的所有验证性 Webhook。 匹配的 Webhook 将被并行调用。如果其中任何一个拒绝请求,则整个请求将失败。 该准入控制器仅在验证(Validating)阶段运行;与 MutatingAdmissionWebhook 准入控制器所调用的 Webhook 相反,它调用的 Webhook 不可以变更对象。

如果以此方式调用的 Webhook 有其它副作用(如:减少配额),则它 必须 具有协调机制。 这是因为无法保证后续的 Webhook 或其他验证性准入控制器都允许请求完成。

如果你禁用了 ValidatingAdmissionWebhook,还必须通过 --runtime-config 标志来禁用 admissionregistration.k8s.io/v1 组/版本中的 ValidatingWebhookConfiguration 对象。

有。推荐使用的准入控制器默认情况下都处于启用状态 (请查看这里)。 因此,你无需显式指定它们。 你可以使用 --enable-admission-plugins 标志( 顺序不重要 )来启用默认设置以外的其他准入控制器。

3.5 - 动态准入控制

除了内置的 admission 插件, 准入插件可以作为扩展独立开发,并以运行时所配置的 Webhook 的形式运行。 此页面描述了如何构建、配置、使用和监视准入 Webhook。

什么是准入 Webhook?

准入 Webhook 是一种用于接收准入请求并对其进行处理的 HTTP 回调机制。 可以定义两种类型的准入 webhook,即 验证性质的准入 Webhook修改性质的准入 Webhook。 修改性质的准入 Webhook 会先被调用。它们可以更改发送到 API 服务器的对象以执行自定义的设置默认值操作。

在完成了所有对象修改并且 API 服务器也验证了所传入的对象之后, 验证性质的 Webhook 会被调用,并通过拒绝请求的方式来强制实施自定义的策略。

尝试准入 Webhook

准入 Webhook 本质上是集群控制平面的一部分。你应该非常谨慎地编写和部署它们。 如果你打算编写或者部署生产级准入 webhook,请阅读用户指南以获取相关说明。 在下文中,我们将介绍如何快速试验准入 Webhook。

先决条件

  • 确保启用 MutatingAdmissionWebhook 和 ValidatingAdmissionWebhook 控制器。 这里 是一组推荐的 admission 控制器,通常可以启用。

  • 确保启用了 admissionregistration.k8s.io/v1 API。

编写一个准入 Webhook 服务器

请参阅 Kubernetes e2e 测试中的 admission webhook 服务器 的实现。webhook 处理由 API 服务器发送的 AdmissionReview 请求,并且将其决定 作为 AdmissionReview 对象以相同版本发送回去。

有关发送到 webhook 的数据的详细信息,请参阅 webhook 请求

要获取来自 webhook 的预期数据,请参阅 webhook 响应

示例准入 Webhook 服务器置 ClientAuth 字段为 , 默认为 NoClientCert 。这意味着 webhook 服务器不会验证客户端的身份,认为其是 apiservers。 如果你需要双向 TLS 或其他方式来验证客户端,请参阅 如何对 apiservers 进行身份认证

部署准入 Webhook 服务

e2e 测试中的 webhook 服务器通过 deployment API 部署在 Kubernetes 集群中。该测试还将创建一个 service 作为 webhook 服务器的前端。参见 相关代码

你也可以在集群外部署 webhook。这样做需要相应地更新你的 webhook 配置。

即时配置准入 Webhook

你可以通过 ValidatingWebhookConfiguration 或者 MutatingWebhookConfiguration 动态配置哪些资源要被哪些准入 Webhook 处理。

以下是一个 ValidatingWebhookConfiguration 示例,mutating webhook 配置与此类似。有关每个配置字段的详细信息,请参阅 webhook 配置 部分。

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  name: "pod-policy.example.com"
webhooks:
- name: "pod-policy.example.com"
  rules:
  - apiGroups:   [""]
    apiVersions: ["v1"]
    operations:  ["CREATE"]
    resources:   ["pods"]
    scope:       "Namespaced"
  clientConfig:
    service:
      namespace: "example-namespace"
      name: "example-service"
    caBundle: <CA_BUNDLE>
  admissionReviewVersions: ["v1"]
  sideEffects: None
  timeoutSeconds: 5

scope 字段指定是仅集群范围的资源(Cluster)还是名字空间范围的资源资源(Namespaced)将与此规则匹配。 * 表示没有范围限制。

当一个 API 服务器收到与 rules 相匹配的请求时, 该 API 服务器将按照 clientConfig 中指定的方式向 webhook 发送一个 admissionReview 请求。

创建 Webhook 配置后,系统将花费几秒钟使新配置生效。

对 API 服务器进行身份认证

如果你的 Webhook 需要身份验证,则可以将 API 服务器配置为使用基本身份验证、持有者令牌或证书来向 Webhook 提供身份证明。完成此配置需要三个步骤。

  • 启动 API 服务器时,通过 --admission-control-config-file 参数指定准入控制配置文件的位置。

  • 在准入控制配置文件中,指定 MutatingAdmissionWebhook 控制器和 ValidatingAdmissionWebhook 控制器应该读取凭据的位置。 凭证存储在 kubeConfig 文件中(是​​的,与 kubectl 使用的模式相同),因此字段名称为 kubeConfigFile。 以下是一个准入控制配置文件示例:

apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: ValidatingAdmissionWebhook
  configuration:
    apiVersion: apiserver.config.k8s.io/v1
    kind: WebhookAdmissionConfiguration
    kubeConfigFile: "<path-to-kubeconfig-file>"
- name: MutatingAdmissionWebhook
  configuration:
    apiVersion: apiserver.config.k8s.io/v1
    kind: WebhookAdmissionConfiguration
    kubeConfigFile: "<path-to-kubeconfig-file>"

# 1.17 中被淘汰,推荐使用 apiserver.config.k8s.io/v1
apiVersion: apiserver.k8s.io/v1alpha1
kind: AdmissionConfiguration
plugins:
- name: ValidatingAdmissionWebhook
  configuration:
    # 1.17 中被淘汰,推荐使用 apiserver.config.k8s.io/v1,kind = WebhookAdmissionConfiguration
    apiVersion: apiserver.config.k8s.io/v1alpha1
    kind: WebhookAdmission
    kubeConfigFile: "<path-to-kubeconfig-file>"
- name: MutatingAdmissionWebhook
  configuration:
    # 1.17 中被淘汰,推荐使用 apiserver.config.k8s.io/v1,kind = WebhookAdmissionConfiguration
    apiVersion: apiserver.config.k8s.io/v1alpha1
    kind: WebhookAdmission
    kubeConfigFile: "<path-to-kubeconfig-file>"

有关 AdmissionConfiguration 的更多信息,请参见 AdmissionConfiguration (v1) reference。 有关每个配置字段的详细信息,请参见 webhook 配置部分。

  • 在 kubeConfig 文件中,提供证书凭据:

    apiVersion: v1
    kind: Config
    users:
    # 名称应设置为服务的 DNS 名称或配置了 Webhook 的 URL 的主机名(包括端口)。
    # 如果将非 443 端口用于服务,则在配置 1.16+ API 服务器时,该端口必须包含在名称中。
    #
    # 对于配置在默认端口(443)上与服务对话的 Webhook,请指定服务的 DNS 名称:
    # - name: webhook1.ns1.svc
    #   user: ...
    #
    # 对于配置在非默认端口(例如 8443)上与服务对话的 Webhook,请在 1.16+ 中指定服务的 DNS 名称和端口:
    # - name: webhook1.ns1.svc:8443
    #   user: ...
    # 并可以选择仅使用服务的 DNS 名称来创建第二节,以与 1.15 API 服务器版本兼容:
    # - name: webhook1.ns1.svc
    #   user: ...
    #
    # 对于配置为使用 URL 的 webhook,请匹配在 webhook 的 URL 中指定的主机(和端口)。
    # 带有 `url: https://www.example.com` 的 webhook:
    # - name: www.example.com
    #   user: ...
    #
    # 带有 `url: https://www.example.com:443` 的 webhook:
    # - name: www.example.com:443
    #   user: ...
    #
    # 带有 `url: https://www.example.com:8443` 的 webhook:
    # - name: www.example.com:8443
    #   user: ...
    #
    - name: 'webhook1.ns1.svc'
      user:
        client-certificate-data: "<pem encoded certificate>"
        client-key-data: "<pem encoded key>"
    # `name` 支持使用 * 通配符匹配前缀段。
    - name: '*.webhook-company.org'
      user:
        password: "<password>"
        username: "<name>"
    # '*' 是默认匹配项。
    - name: '*'
      user:
        token: "<token>"
    

当然,你需要设置 Webhook 服务器来处理这些身份验证请求。

Webhook 请求与响应

请求

Webhook 发送 POST 请求时,请设置 Content-Type: application/json 并对 admission.k8s.io API 组中的 AdmissionReview 对象进行序列化,将所得到的 JSON 作为请求的主体。

Webhook 可以在配置中的 admissionReviewVersions 字段指定可接受的 AdmissionReview 对象版本:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
webhooks:
- name: my-webhook.example.com
  admissionReviewVersions: ["v1", "v1beta1"]

创建 webhook 配置时,admissionReviewVersions 是必填字段。 Webhook 必须支持至少一个当前和以前的 API 服务器都可以解析的 AdmissionReview 版本。

API 服务器将发送的是 admissionReviewVersions 列表中所支持的第一个 AdmissionReview 版本。如果 API 服务器不支持列表中的任何版本,则不允许创建配置。

如果 API 服务器遇到以前创建的 Webhook 配置,并且不支持该 API 服务器知道如何发送的任何 AdmissionReview 版本,则调用 Webhook 的尝试将失败,并依据失败策略进行处理。

此示例显示了 AdmissionReview 对象中包含的数据,该数据用于请求更新 apps/v1 Deploymentscale 子资源:

apiVersion: admission.k8s.io/v1
kind: AdmissionReview
request:
  # 唯一标识此准入回调的随机 uid
  uid: 705ab4f5-6393-11e8-b7cc-42010a800002

  # 传入完全正确的 group/version/kind 对象
  kind:
    group: autoscaling
    version: v1
    kind: Scale

  # 修改 resource 的完全正确的的 group/version/kind
  resource:
    group: apps
    version: v1
    resource: deployments

  # subResource(如果请求是针对 subResource 的)
  subResource: scale

  # 在对 API 服务器的原始请求中,传入对象的标准 group/version/kind
  # 仅当 webhook 指定 `matchPolicy: Equivalent` 且将对 API 服务器的原始请求
  # 转换为 webhook 注册的版本时,这才与 `kind` 不同。
  requestKind:
    group: autoscaling
    version: v1
    kind: Scale

  # 在对 API 服务器的原始请求中正在修改的资源的标准 group/version/kind
  # 仅当 webhook 指定了 `matchPolicy:Equivalent` 并且将对 API 服务器的原始请求转换为
  # webhook 注册的版本时,这才与 `resource` 不同。
  requestResource:
    group: apps
    version: v1
    resource: deployments

  # subResource(如果请求是针对 subResource 的)
  # 仅当 webhook 指定了 `matchPolicy:Equivalent` 并且将对
  # API 服务器的原始请求转换为该 webhook 注册的版本时,这才与 `subResource` 不同。
  requestSubResource: scale

  # 被修改资源的名称
  name: my-deployment

  # 如果资源是属于名字空间(或者是名字空间对象),则这是被修改的资源的名字空间
  namespace: my-namespace

  # 操作可以是 CREATE、UPDATE、DELETE 或 CONNECT
  operation: UPDATE

  userInfo:
    # 向 API 服务器发出请求的经过身份验证的用户的用户名
    username: admin

    # 向 API 服务器发出请求的经过身份验证的用户的 UID
    uid: 014fbff9a07c

    # 向 API 服务器发出请求的经过身份验证的用户的组成员身份
    groups:
      - system:authenticated
      - my-admin-group
    # 向 API 服务器发出请求的用户相关的任意附加信息
    # 该字段由 API 服务器身份验证层填充,并且如果 webhook 执行了任何
    # SubjectAccessReview 检查,则应将其包括在内。
    extra:
      some-key:
        - some-value1
        - some-value2

  # object 是被接纳的新对象。
  # 对于 DELETE 操作,它为 null。
  object:
    apiVersion: autoscaling/v1
    kind: Scale

  # oldObject 是现有对象。
  # 对于 CREATE 和 CONNECT 操作,它为 null。
  oldObject:
    apiVersion: autoscaling/v1
    kind: Scale

  # options 包含要接受的操作的选项,例如 meta.k8s.io/v CreateOptions、UpdateOptions 或 DeleteOptions。
  # 对于 CONNECT 操作,它为 null。
  options:
    apiVersion: meta.k8s.io/v1
    kind: UpdateOptions

  # dryRun 表示 API 请求正在以 `dryrun` 模式运行,并且将不会保留。
  # 带有副作用的 Webhook 应该避免在 dryRun 为 true 时激活这些副作用。
  # 有关更多详细信息,请参见 http://k8s.io/docs/reference/using-api/api-concepts/#make-a-dry-run-request
  dryRun: False

响应

Webhook 使用 HTTP 200 状态码、Content-Type: application/json 和一个包含 AdmissionReview 对象的 JSON 序列化格式来发送响应。该 AdmissionReview 对象与发送的版本相同,且其中包含的 response 字段已被有效填充。

response 至少必须包含以下字段:

  • uid,从发送到 Webhook 的 request.uid 中复制而来
  • allowed,设置为 truefalse

Webhook 允许请求的最简单响应示例:

{
  "apiVersion": "admission.k8s.io/v1",
  "kind": "AdmissionReview",
  "response": {
    "uid": "<value from request.uid>",
    "allowed": true
  }
}

Webhook 禁止请求的最简单响应示例:

{
  "apiVersion": "admission.k8s.io/v1",
  "kind": "AdmissionReview",
  "response": {
    "uid": "<value from request.uid>",
    "allowed": false
  }
}

当拒绝请求时,Webhook 可以使用 status 字段自定义 http 响应码和返回给用户的消息。 有关状态类型的详细信息,请参见 API 文档。 禁止请求的响应示例,它定制了向用户显示的 HTTP 状态码和消息:

{
  "apiVersion": "admission.k8s.io/v1",
  "kind": "AdmissionReview",
  "response": {
    "uid": "<value from request.uid>",
    "allowed": false,
    "status": {
      "code": 403,
      "message": "You cannot do this because it is Tuesday and your name starts with A"
    }
  }
}

当允许请求时,mutating准入 Webhook 也可以选择修改传入的对象。 这是通过在响应中使用 patchpatchType 字段来完成的。 当前唯一支持的 patchTypeJSONPatch。 有关更多详细信息,请参见 JSON patch。 对于 patchType: JSONPatchpatch 字段包含一个以 base64 编码的 JSON patch 操作数组。

例如,设置 spec.replicas 的单个补丁操作将是 [{"op": "add", "path": "/spec/replicas", "value": 3}]

如果以 Base64 形式编码,结果将是 W3sib3AiOiAiYWRkIiwgInBhdGgiOiAiL3NwZWMvcmVwbGljYXMiLCAidmFsdWUiOiAzfV0=

因此,添加该标签的 Webhook 响应为:

{
  "apiVersion": "admission.k8s.io/v1",
  "kind": "AdmissionReview",
  "response": {
    "uid": "<value from request.uid>",
    "allowed": true,
    "patchType": "JSONPatch",
    "patch": "W3sib3AiOiAiYWRkIiwgInBhdGgiOiAiL3NwZWMvcmVwbGljYXMiLCAidmFsdWUiOiAzfV0="
  }
}

准入 Webhook 可以选择性地返回在 HTTP Warning 头中返回给请求客户端的警告消息,警告代码为 299。 警告可以与允许或拒绝的准入响应一起发送。

如果你正在实现返回一条警告的 webhook,则:

  • 不要在消息中包括 "Warning:" 前缀
  • 使用警告消息描述该客户端进行 API 请求时会遇到或应意识到的问题
  • 如果可能,将警告限制为 120 个字符
{
  "apiVersion": "admission.k8s.io/v1",
  "kind": "AdmissionReview",
  "response": {
    "uid": "<value from request.uid>",
    "allowed": true,
    "warnings": [
      "duplicate envvar entries specified with name MY_ENV",
      "memory request less than 4MB specified for container mycontainer, which will not start successfully"
    ]
  }
}

Webhook 配置

要注册准入 Webhook,请创建 MutatingWebhookConfigurationValidatingWebhookConfiguration API 对象。 MutatingWebhookConfigurationValidatingWebhookConfiguration 对象的名称必须是有效的 DNS 子域名

每种配置可以包含一个或多个 Webhook。如果在单个配置中指定了多个 Webhook,则应为每个 Webhook 赋予一个唯一的名称。 这是必需的,以使生成的审计日志和指标更易于与激活的配置相匹配。

每个 Webhook 定义以下内容。

匹配请求-规则

每个 webhook 必须指定用于确定是否应将对 apiserver 的请求发送到 webhook 的规则列表。 每个规则都指定一个或多个 operations、apiGroups、apiVersions 和 resources 以及资源的 scope:

  • operations 列出一个或多个要匹配的操作。 可以是 CREATEUPDATEDELETECONNECT* 以匹配所有内容。
  • apiGroups 列出了一个或多个要匹配的 API 组。"" 是核心 API 组。"*" 匹配所有 API 组。
  • apiVersions 列出了一个或多个要匹配的 API 版本。"*" 匹配所有 API 版本。
  • resources 列出了一个或多个要匹配的资源。
    • "*" 匹配所有资源,但不包括子资源。
    • "*/*" 匹配所有资源,包括子资源。
    • "pods/*" 匹配 pod 的所有子资源。
    • "*/status" 匹配所有 status 子资源。
  • scope 指定要匹配的范围。有效值为 "Cluster""Namespaced""*"。 子资源匹配其父资源的范围。默认值为 "*"
    • "Cluster" 表示只有集群作用域的资源才能匹配此规则(API 对象 Namespace 是集群作用域的)。
    • "Namespaced" 意味着仅具有名字空间的资源才符合此规则。
    • "*" 表示没有作用域限制。

如果传入请求与任何 Webhook rules 的指定 operationsgroupsversionsresourcesscope 匹配,则该请求将发送到 Webhook。

以下是可用于指定应拦截哪些资源的规则的其他示例。

匹配针对 apps/v1apps/v1beta1 组中 deploymentsreplicasets 资源的 CREATEUPDATE 请求:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
...
webhooks:
- name: my-webhook.example.com
  rules:
  - operations: ["CREATE", "UPDATE"]
    apiGroups: ["apps"]
    apiVersions: ["v1", "v1beta1"]
    resources: ["deployments", "replicasets"]
    scope: "Namespaced"
  ...

匹配所有 API 组和版本中的所有资源(但不包括子资源)的创建请求:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
webhooks:
  - name: my-webhook.example.com
    rules:
      - operations: ["CREATE"]
        apiGroups: ["*"]
        apiVersions: ["*"]
        resources: ["*"]
        scope: "*"

匹配所有 API 组和版本中所有 status 子资源的更新请求:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
webhooks:
  - name: my-webhook.example.com
    rules:
      - operations: ["UPDATE"]
        apiGroups: ["*"]
        apiVersions: ["*"]
        resources: ["*/status"]
        scope: "*"

匹配请求:objectSelector

通过指定 objectSelector,Webhook 能够根据可能发送的对象的标签来限制哪些请求被拦截。 如果指定,则将对 objectSelector 和可能发送到 Webhook 的 object 和 oldObject 进行评估。如果两个对象之一与选择器匹配,则认为该请求已匹配。

空对象(对于创建操作而言为 oldObject,对于删除操作而言为 newObject), 或不能带标签的对象(例如 DeploymentRollbackPodProxyOptions 对象) 被认为不匹配。

仅当选择使用 webhook 时才使用对象选择器,因为最终用户可以通过设置标签来 跳过准入 Webhook。

这个例子展示了一个 mutating webhook,它将匹配带有标签 foo:bar 的任何资源的 CREATE 的操作:

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
webhooks:
- name: my-webhook.example.com
  objectSelector:
    matchLabels:
      foo: bar
  rules:
  - operations: ["CREATE"]
    apiGroups: ["*"]
    apiVersions: ["*"]
    resources: ["*"]
    scope: "*"

有关标签选择器的更多示例,请参见标签

匹配请求:namespaceSelector

通过指定 namespaceSelector, Webhook 可以根据具有名字空间的资源所处的名字空间的标签来选择拦截哪些资源的操作。

namespaceSelector 根据名字空间的标签是否匹配选择器,决定是否针对具名字空间的资源 (或 Namespace 对象)的请求运行 webhook。 如果对象是除 Namespace 以外的集群范围的资源,则 namespaceSelector 标签无效。

本例给出的修改性质的 Webhook 将匹配到对名字空间中具名字空间的资源的 CREATE 请求, 前提是这些资源不含值为 "0" 或 "1" 的 "runlevel" 标签:

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
webhooks:
  - name: my-webhook.example.com
    namespaceSelector:
      matchExpressions:
        - key: runlevel
          operator: NotIn
          values: ["0","1"]
    rules:
      - operations: ["CREATE"]
        apiGroups: ["*"]
        apiVersions: ["*"]
        resources: ["*"]
        scope: "Namespaced"

此示例显示了一个验证性质的 Webhook,它将匹配到对某名字空间中的任何具名字空间的资源的 CREATE 请求,前提是该名字空间具有值为 "prod" 或 "staging" 的 "environment" 标签:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
webhooks:
  - name: my-webhook.example.com
    namespaceSelector:
      matchExpressions:
        - key: environment
          operator: In
          values: ["prod","staging"]
    rules:
      - operations: ["CREATE"]
        apiGroups: ["*"]
        apiVersions: ["*"]
        resources: ["*"]
        scope: "Namespaced"

有关标签选择器的更多示例,请参见 标签

匹配请求:matchPolicy

API 服务器可以通过多个 API 组或版本来提供对象。

例如,如果一个 webhook 仅为某些 API 组/版本指定了规则(例如 apiGroups:["apps"], apiVersions:["v1","v1beta1"]),而修改资源的请求是通过另一个 API 组/版本(例如 extensions/v1beta1)发出的,该请求将不会被发送到 Webhook。

matchPolicy 允许 webhook 定义如何使用其 rules 匹配传入的请求。 允许的值为 ExactEquivalent

  • Exact 表示仅当请求与指定规则完全匹配时才应拦截该请求。
  • Equivalent 表示如果某个请求意在修改 rules 中列出的资源, 即使该请求是通过其他 API 组或版本发起,也应拦截该请求。

在上面给出的示例中,仅为 apps/v1 注册的 webhook 可以使用 matchPolicy

  • matchPolicy: Exact 表示不会将 extensions/v1beta1 请求发送到 Webhook
  • matchPolicy:Equivalent 表示将 extensions/v1beta1 请求发送到 webhook (将对象转换为 webhook 指定的版本:apps/v1

建议指定 Equivalent,确保升级后启用 API 服务器中资源的新版本时, Webhook 继续拦截他们期望的资源。

当 API 服务器停止提供某资源时,该资源不再被视为等同于该资源的其他仍在提供服务的版本。 例如,extensions/v1beta1 中的 Deployment 已被废弃,计划在 v1.16 中移除。

移除后,带有 apiGroups:["extensions"], apiVersions:["v1beta1"], resources: ["deployments"] 规则的 Webhook 将不再拦截通过 apps/v1 API 来创建的 Deployment。 因此,Webhook 应该优先注册稳定版本的资源。

此示例显示了一个验证性质的 Webhook,该 Webhook 拦截对 Deployment 的修改(无论 API 组或版本是什么), 始终会发送一个 apps/v1 版本的 Deployment 对象:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
webhooks:
- name: my-webhook.example.com
  matchPolicy: Equivalent
  rules:
  - operations: ["CREATE","UPDATE","DELETE"]
    apiGroups: ["apps"]
    apiVersions: ["v1"]
    resources: ["deployments"]
    scope: "Namespaced"

准入 Webhook 所用的 matchPolicy 默认为 Equivalent

调用 Webhook

API 服务器确定请求应发送到 webhook 后,它需要知道如何调用 webhook。 此信息在 webhook 配置的 clientConfig 节中指定。

Webhook 可以通过 URL 或服务引用来调用,并且可以选择包含自定义 CA 包,以用于验证 TLS 连接。

URL

url 以标准 URL 形式给出 webhook 的位置(scheme://host:port/path)。

host 不应引用集群中运行的服务;通过指定 service 字段来使用服务引用。 主机可以通过某些 API 服务器中的外部 DNS 进行解析。 (例如,kube-apiserver 无法解析集群内 DNS,因为这将违反分层规则)。host 也可以是 IP 地址。

请注意,将 localhost127.0.0.1 用作 host 是有风险的, 除非你非常小心地在所有运行 apiserver 的、可能需要对此 webhook 进行调用的主机上运行。这样的安装方式可能不具有可移植性,即很难在新集群中启用。

scheme 必须为 "https";URL 必须以 "https://" 开头。

使用用户或基本身份验证(例如:"user:password@")是不允许的。 使用片段("#...")和查询参数("?...")也是不允许的。

这是配置为调用 URL 的修改性质的 Webhook 的示例 (并且期望使用系统信任根证书来验证 TLS 证书,因此不指定 caBundle):

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
webhooks:
- name: my-webhook.example.com
  clientConfig:
    url: "https://my-webhook.example.com:9443/my-webhook-path"

服务引用

clientConfig 内部的 Service 是对该 Webhook 服务的引用。 如果 Webhook 在集群中运行,则应使用 service 而不是 url。 服务的 namespacename 是必需的。 port 是可选的,默认值为 443。path 是可选的,默认为 "/"。

这是一个 mutating Webhook 的示例,该 mutating Webhook 配置为在子路径 "/my-path" 端口 "1234" 上调用服务,并使用自定义 CA 包针对 ServerName my-service-name.my-service-namespace.svc 验证 TLS 连接:

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
webhooks:
- name: my-webhook.example.com
  clientConfig:
    caBundle: <CA_BUNDLE>
    service:
      namespace: my-service-namespace
      name: my-service-name
      path: /my-path
      port: 1234

副作用

Webhook 通常仅对发送给他们的 AdmissionReview 内容进行操作。 但是,某些 Webhook 在处理 admission 请求时会进行带外更改。

进行带外更改的(产生“副作用”的) Webhook 必须具有协调机制(如控制器), 该机制定期确定事物的实际状态,并调整由准入 Webhook 修改的带外数据以反映现实情况。 这是因为对准入 Webhook 的调用不能保证所准入的对象将原样保留,或根本不保留。 以后,webhook 可以修改对象的内容,在写入存储时可能会发生冲突,或者 服务器可以在持久保存对象之前关闭电源。

此外,处理 dryRun: true admission 请求时,具有副作用的 Webhook 必须避免产生副作用。 一个 Webhook 必须明确指出在使用 dryRun 运行时不会有副作用, 否则 dry-run 请求将不会发送到该 Webhook,而 API 请求将会失败。

Webhook 使用 webhook 配置中的 sideEffects 字段显示它们是否有副作用:

  • None:调用 webhook 没有副作用。
  • NoneOnDryRun:调用 webhook 可能会有副作用,但是如果将带有 dryRun: true 属性的请求发送到 webhook,则 webhook 将抑制副作用(该 webhook 可识别 dryRun)。

这是一个 validating webhook 的示例,表明它对 dryRun: true 请求没有副作用:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
webhooks:
  - name: my-webhook.example.com
    sideEffects: NoneOnDryRun

超时

由于 Webhook 会增加 API 请求的延迟,因此应尽快完成自身的操作。 timeoutSeconds 用来配置在将调用视为失败之前,允许 API 服务器等待 Webhook 响应的时间长度。

如果超时在 Webhook 响应之前被触发,则基于失败策略,将忽略 Webhook 调用或拒绝 API 调用。

超时值必须设置在 1 到 30 秒之间。

这是一个自定义超时设置为 2 秒的 validating Webhook 的示例:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
webhooks:
  - name: my-webhook.example.com
    timeoutSeconds: 2

准入 Webhook 所用的超时时间默认为 10 秒。

再调用策略

修改性质的准入插件(包括 Webhook)的任何一种排序方式都不会适用于所有情况。 (参见 https://issue.k8s.io/64333 示例)。 修改性质的 Webhook 可以向对象中添加新的子结构(例如向 pod 中添加 container), 已经运行的其他修改插件可能会对这些新结构有影响 (就像在所有容器上设置 imagePullPolicy 一样)。

要允许修改性质的准入插件感应到其他插件所做的更改, 如果修改性质的 Webhook 修改了一个对象,则会重新运行内置的修改性质的准入插件, 并且修改性质的 Webhook 可以指定 reinvocationPolicy 来控制是否也重新调用它们。

可以将 reinvocationPolicy 设置为 NeverIfNeeded。 默认为 Never

  • Never: 在一次准入测试中,不得多次调用 Webhook。
  • IfNeeded: 如果在最初的 Webhook 调用之后被其他对象的插件修改了被接纳的对象, 则可以作为准入测试的一部分再次调用该 webhook。

要注意的重要因素有:

  • 不能保证附加调用的次数恰好是一。
  • 如果其他调用导致对该对象的进一步修改,则不能保证再次调用 Webhook。
  • 使用此选项的 Webhook 可能会重新排序,以最大程度地减少额外调用的次数。
  • 要在确保所有修改都完成后验证对象,请改用验证性质的 Webhook (推荐用于有副作用的 Webhook)。

这是一个修改性质的 Webhook 的示例,该 Webhook 在以后的准入插件修改对象时被重新调用:

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
webhooks:
- name: my-webhook.example.com
  reinvocationPolicy: IfNeeded

修改性质的 Webhook 必须具有幂等性,并且能够成功处理 已被接纳并可能被修改的对象的修改性质的 Webhook。 对于所有修改性质的准入 Webhook 都是如此,因为它们可以在对象中进行的 任何更改可能已经存在于用户提供的对象中,但是对于选择重新调用的 webhook 来说是必不可少的。

失败策略

failurePolicy 定义了如何处理准入 webhook 中无法识别的错误和超时错误。允许的值为 IgnoreFail

  • Ignore 表示调用 webhook 的错误将被忽略并且允许 API 请求继续。
  • Fail 表示调用 webhook 的错误导致准入失败并且 API 请求被拒绝。

这是一个修改性质的 webhook,配置为在调用准入 Webhook 遇到错误时拒绝 API 请求:

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
webhooks:
- name: my-webhook.example.com
  failurePolicy: Fail

准入 Webhook 所用的默认 failurePolicyFail

监控 Admission Webhook

API 服务器提供了监视准入 Webhook 行为的方法。这些监视机制可帮助集群管理员回答以下问题:

  1. 哪个修改性质的 webhook 改变了 API 请求中的对象?
  2. 修改性质的 Webhook 对对象做了哪些更改?
  3. 哪些 webhook 经常拒绝 API 请求?是什么原因拒绝?

Mutating Webhook 审计注解

有时,了解 API 请求中的哪个修改性质的 Webhook 使对象改变以及该 Webhook 应用了哪些更改很有用。

Kubernetes API 服务器针对每个修改性质的 Webhook 调用执行审计操作。 每个调用都会生成一个审计注解,记述请求对象是否发生改变, 可选地还可以根据 Webhook 的准入响应生成一个注解,记述所应用的修补。 针对给定请求的给定执行阶段,注解被添加到审计事件中, 然后根据特定策略进行预处理并写入后端。

事件的审计级别决定了要记录哪些注解:

  • Metadata 或更高审计级别上,将使用 JSON 负载记录带有键名 mutation.webhook.admission.k8s.io/round_{round idx}_index_{order idx} 的注解, 该注解表示针对给定请求调用了 Webhook,以及该 Webhook 是否更改了对象。

    例如,对于正在被重新调用的某 Webhook,所记录的注解如下。 Webhook 在 mutating Webhook 链中排在第三个位置,并且在调用期间未改变请求对象。

    # 审计事件相关记录
    {
        "kind": "Event",
        "apiVersion": "audit.k8s.io/v1",
        "annotations": {
            "mutation.webhook.admission.k8s.io/round_1_index_2": "{\"configuration\":\"my-mutating-webhook-configuration.example.com\",\"webhook\":\"my-webhook.example.com\",\"mutated\": false}"
            # 其他注解
            ...
        }
        # 其他字段
        ...
    }
    
    # 反序列化的注解值
    {
        "configuration": "my-mutating-webhook-configuration.example.com",
        "webhook": "my-webhook.example.com",
        "mutated": false
    }
    

    对于在第一轮中调用的 Webhook,所记录的注解如下。 Webhook 在 mutating Webhook 链中排在第一位,并在调用期间改变了请求对象。

    # 审计事件相关记录
    {
        "kind": "Event",
        "apiVersion": "audit.k8s.io/v1",
        "annotations": {
            "mutation.webhook.admission.k8s.io/round_0_index_0": "{\"configuration\":\"my-mutating-webhook-configuration.example.com\",\"webhook\":\"my-webhook-always-mutate.example.com\",\"mutated\": true}"
            # 其他注解
            ...
        }
        # 其他字段
        ...
    }
    
    # 反序列化的注解值
    {
        "configuration": "my-mutating-webhook-configuration.example.com",
        "webhook": "my-webhook-always-mutate.example.com",
        "mutated": true
    }
    
  • Request 或更高审计级别上,将使用 JSON 负载记录带有键名为 patch.webhook.admission.k8s.io/round_{round idx}_index_{order idx} 的注解, 该注解表明针对给定请求调用了 Webhook 以及应用于请求对象之上的修改。

    例如,以下是针对正在被重新调用的某 Webhook 所记录的注解。 Webhook 在修改性质的 Webhook 链中排在第四,并在其响应中包含一个 JSON 补丁, 该补丁已被应用于请求对象。

    # 审计事件相关记录
    {
        "kind": "Event",
        "apiVersion": "audit.k8s.io/v1",
        "annotations": {
            "patch.webhook.admission.k8s.io/round_1_index_3": "{\"configuration\":\"my-other-mutating-webhook-configuration.example.com\",\"webhook\":\"my-webhook-always-mutate.example.com\",\"patch\":[{\"op\":\"add\",\"path\":\"/data/mutation-stage\",\"value\":\"yes\"}],\"patchType\":\"JSONPatch\"}"
            # 其他注解
            ...
        }
        # 其他字段
        ...
    }
    
    # 反序列化的注解值
    {
        "configuration": "my-other-mutating-webhook-configuration.example.com",
        "webhook": "my-webhook-always-mutate.example.com",
        "patchType": "JSONPatch",
        "patch": [
            {
                "op": "add",
                "path": "/data/mutation-stage",
                "value": "yes"
            }
        ]
    }
    

准入 Webhook 度量值

API 服务器从 /metrics 端点公开 Prometheus 指标,这些指标可用于监控和诊断 API 服务器状态。 以下指标记录了与准入 Webhook 相关的状态。

apiserver 准入 Webhook 拒绝次数

有时,了解哪些准入 Webhook 经常拒绝 API 请求以及拒绝的原因是很有用的。

在 v1.16+ 中,kube-apiserver 提供了 Prometheus 计数器度量值,记录 准入 Webhook 的拒绝次数。 度量值的标签给出了 Webhook 拒绝该请求的原因:

  • name:拒绝请求 Webhook 的名称。
  • operation:请求的操作类型可以是 CREATEUPDATEDELETECONNECT 其中之一。
  • type:Admission webhook 类型,可以是 admitvalidating 其中之一。
  • error_type:标识在 webhook 调用期间是否发生了错误并且导致了拒绝。其值可以是以下之一:
    • calling_webhook_error:发生了来自准入 Webhook 的无法识别的错误或超时错误, 并且 webhook 的 失败策略 设置为 Fail
    • no_error:未发生错误。Webhook 在准入响应中以 allowed: false 值拒绝了请求。 度量标签 rejection_code 记录了在准入响应中设置的 .status.code
    • apiserver_internal_error:apiserver 发生内部错误。
  • rejection_code:当 Webhook 拒绝请求时,在准入响应中设置的 HTTP 状态码。

拒绝计数指标示例:

# HELP apiserver_admission_webhook_rejection_count [ALPHA] Admission webhook rejection count, identified by name and broken out for each admission type (validating or admit) and operation. Additional labels specify an error type (calling_webhook_error or apiserver_internal_error if an error occurred; no_error otherwise) and optionally a non-zero rejection code if the webhook rejects the request with an HTTP status code (honored by the apiserver when the code is greater or equal to 400). Codes greater than 600 are truncated to 600, to keep the metrics cardinality bounded.
# TYPE apiserver_admission_webhook_rejection_count counter
apiserver_admission_webhook_rejection_count{error_type="calling_webhook_error",name="always-timeout-webhook.example.com",operation="CREATE",rejection_code="0",type="validating"} 1
apiserver_admission_webhook_rejection_count{error_type="calling_webhook_error",name="invalid-admission-response-webhook.example.com",operation="CREATE",rejection_code="0",type="validating"} 1
apiserver_admission_webhook_rejection_count{error_type="no_error",name="deny-unwanted-configmap-data.example.com",operation="CREATE",rejection_code="400",type="validating"} 13

最佳实践和警告

幂等性

幂等的修改性质的准入 Webhook 能够成功处理已经被它接纳甚或修改的对象。 即使多次执行该准入测试,也不会产生与初次执行结果相异的结果。

幂等 mutating admission Webhook 的示例:

  1. 对于 CREATE Pod 请求,将 Pod 的字段 .spec.securityContext.runAsNonRoot 设置为 true,以实施安全最佳实践。
  2. 对于 CREATE Pod 请求,如果未设置容器的字段 .spec.containers[].resources.limits,设置默认资源限制值。
  3. 对于 CREATE pod 请求,如果 Pod 中不存在名为 foo-sidecar 的边车容器, 向 Pod 注入一个 foo-sidecar 容器。

在上述情况下,可以安全地重新调用 Webhook,或接受已经设置了字段的对象。

非幂等 mutating admission Webhook 的示例:

  1. 对于 CREATE pod 请求,注入名称为 foo-sidecar 并带有当前时间戳的 边车容器(例如 foo-sidecar-19700101-000000)。
  2. 对于 CREATE/UPDATE pod 请求,如果容器已设置标签 "env" 则拒绝, 否则将 "env": "prod" 标签添加到容器。
  3. 对于 CREATE pod 请求,盲目地添加一个名为 foo-sidecar 的边车容器, 而未查看 Pod 中是否已经有 foo-sidecar 容器。

在上述第一种情况下,重新调用该 Webhook 可能导致同一个 Sidecar 容器 多次注入到 Pod 中,而且每次使用不同的容器名称。 类似地,如果 Sidecar 已存在于用户提供的 Pod 中,则 Webhook 可能注入重复的容器。

在上述第二种情况下,重新调用 Webhook 将导致 Webhook 自身输出失败。

在上述第三种情况下,重新调用 Webhook 将导致 Pod 规范中的容器重复, 从而使请求无效并被 API 服务器拒绝。

拦截对象的所有版本

建议通过将 .webhooks[].matchPolicy 设置为 Equivalent, 以确保准入 Webhooks 始终拦截对象的所有版本。 建议准入 Webhooks 应该更偏向注册资源的稳定版本。 如果无法拦截对象的所有版本,可能会导致准入策略未再某些版本的请求上执行。 有关示例,请参见匹配请求:matchPolicy

可用性

建议准入 webhook 尽快完成执行(时长通常是毫秒级),因为它们会增加 API 请求的延迟。 建议对 Webhook 使用较小的超时值。有关更多详细信息,请参见超时

建议 Admission Webhook 应该采用某种形式的负载均衡机制,以提供高可用性和高性能。 如果集群中正在运行 Webhook,则可以在服务后面运行多个 Webhook 后端,以利用该服务支持的负载均衡。

确保看到对象的最终状态

如果某准入 Webhook 需要保证自己能够看到对象的最终状态以实施策略, 则应该使用一个验证性质的 webhook, 因为可以通过 mutating Webhook 看到对象后对其进行修改。

例如,一个修改性质的准入Webhook 被配置为在每个 CREATE Pod 请求中 注入一个名称为 "foo-sidecar" 的 sidecar 容器。

如果必须存在边车容器,则还应配置一个验证性质的准入 Webhook 以拦截 CREATE Pod 请求,并验证要创建的对象中是否存在具有预期配置的名称为 "foo-sidecar" 的容器。

避免自托管的 Webhooks 中出现死锁

如果集群内的 Webhook 配置能够拦截启动其自己的 Pod 所需的资源, 则该 Webhook 可能导致其自身部署时发生死锁。

例如,某修改性质的准入 Webhook 配置为仅当 Pod 中设置了某个标签 (例如 "env": "prod")时,才接受 CREATE Pod 请求。 Webhook 服务器在未设置 "env" 标签的 Deployment 中运行。当运行 Webhook 服务器的 容器的节点运行不正常时,Webhook 部署尝试将容器重新调度到另一个节点。 但是,由于未设置 "env" 标签,因此请求将被现有的 Webhook 服务器拒绝,并且调度迁移不会发生。

建议使用 namespaceSelector 排除 Webhook 所在的名字空间。

副作用

建议准入 Webhook 应尽可能避免副作用,这意味着该准入 webhook 仅对发送给他们的 AdmissionReview 的内容起作用,并且不要进行额外更改。 如果 Webhook 没有任何副作用,则 .webhooks[].sideEffects 字段应设置为 None

如果在准入执行期间存在副作用,则应在处理 dryRuntrueAdmissionReview 对象时避免产生副作用,并且其 .webhooks[].sideEffects 字段应设置为 NoneOnDryRun。更多详细信息,请参见副作用

避免对 kube-system 名字空间进行操作

kube-system 名字空间包含由 Kubernetes 系统创建的对象, 例如用于控制平面组件的服务账号,诸如 kube-dns 之类的 Pod 等。 意外更改或拒绝 kube-system 名字空间中的请求可能会导致控制平面组件停止运行或者导致未知行为发生。 如果你的准入 Webhook 不想修改 Kubernetes 控制平面的行为,请使用 namespaceSelector 避免拦截 kube-system 名字空间。

3.6 - 管理服务账号

ServiceAccount 为 Pod 中运行的进程提供了一个身份。

Pod 内的进程可以使用其关联服务账号的身份,向集群的 API 服务器进行身份认证。

有关服务账号的介绍, 请参阅配置服务账号

本任务指南阐述有关 ServiceAccount 的几个概念。 本指南还讲解如何获取或撤销代表 ServiceAccount 的令牌。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你的 Kubernetes 集群必须带有 kubectl 命令行工具。 建议在至少有两个节点的集群上运行本教程,且这些节点不作为控制平面主机。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面任意一个 Kubernetes 工具构建:

为了能够准确地跟随这些步骤,确保你有一个名为 examplens 的名字空间。 如果你没有,运行以下命令创建一个名字空间:

kubectl create namespace examplens

用户账号与服务账号

Kubernetes 区分用户账号和服务账号的概念,主要基于以下原因:

  • 用户账号是针对人而言的。而服务账号是针对运行在 Pod 中的应用进程而言的, 在 Kubernetes 中这些进程运行在容器中,而容器是 Pod 的一部分。
  • 用户账号是全局性的。其名称在某集群中的所有名字空间中必须是唯一的。 无论你查看哪个名字空间,代表用户的特定用户名都代表着同一个用户。 在 Kubernetes 中,服务账号是名字空间作用域的。 两个不同的名字空间可以包含具有相同名称的 ServiceAccount。
  • 通常情况下,集群的用户账号可能会从企业数据库进行同步,创建新用户需要特殊权限,并且涉及到复杂的业务流程。 服务账号创建有意做得更轻量,允许集群用户为了具体的任务按需创建服务账号。 将 ServiceAccount 的创建与新用户注册的步骤分离开来,使工作负载更易于遵从权限最小化原则。
  • 对人员和服务账号审计所考虑的因素可能不同;这种分离更容易区分不同之处。
  • 针对复杂系统的配置包可能包含系统组件相关的各种服务账号的定义。 因为服务账号的创建约束不多并且有名字空间域的名称,所以这种配置通常是轻量的。

绑定的服务账号令牌卷机制

特性状态: Kubernetes v1.22 [stable]

默认情况下,Kubernetes 控制平面(特别是 ServiceAccount 准入控制器) 添加一个投射卷到 Pod, 此卷包括了访问 Kubernetes API 的令牌。

以下示例演示如何查找已启动的 Pod:

...
  - name: kube-api-access-<随机后缀>
    projected:
      sources:
        - serviceAccountToken:
            path: token # 必须与应用所预期的路径匹配
        - configMap:
            items:
              - key: ca.crt
                path: ca.crt
            name: kube-root-ca.crt
        - downwardAPI:
            items:
              - fieldRef:
                  apiVersion: v1
                  fieldPath: metadata.namespace
                path: namespace

该清单片段定义了由三个数据源组成的投射卷。在当前场景中,每个数据源也代表该卷内的一条独立路径。这三个数据源是:

  1. serviceAccountToken 数据源,包含 kubelet 从 kube-apiserver 获取的令牌。 kubelet 使用 TokenRequest API 获取有时间限制的令牌。为 TokenRequest 服务的这个令牌会在 Pod 被删除或定义的生命周期(默认为 1 小时)结束之后过期。该令牌绑定到特定的 Pod, 并将其 audience(受众)设置为与 kube-apiserver 的 audience 相匹配。 这种机制取代了之前基于 Secret 添加卷的机制,之前 Secret 代表了针对 Pod 的 ServiceAccount 但不会过期。
  2. configMap 数据源。ConfigMap 包含一组证书颁发机构数据。 Pod 可以使用这些证书来确保自己连接到集群的 kube-apiserver(而不是连接到中间件或意外配置错误的对等点上)。
  3. downwardAPI 数据源,用于查找包含 Pod 的名字空间的名称, 并使该名称信息可用于在 Pod 内运行的应用程序代码。

Pod 内挂载这个特定卷的所有容器都可以访问上述信息。

手动管理 ServiceAccount 的 Secret

v1.22 之前的 Kubernetes 版本会自动创建凭据访问 Kubernetes API。 这种更老的机制基于先创建令牌 Secret,然后将其挂载到正运行的 Pod 中。

在包括 Kubernetes v1.25 在内最近的几个版本中,使用 TokenRequest API 直接获得 API 凭据,并使用投射卷挂载到 Pod 中。 使用这种方法获得的令牌具有绑定的生命周期,当挂载的 Pod 被删除时这些令牌将自动失效。

你仍然可以手动创建 Secret 来保存服务账号令牌;例如在你需要一个永不过期的令牌的时候。

一旦你手动创建一个 Secret 并将其关联到 ServiceAccount,Kubernetes 控制平面就会自动将令牌填充到该 Secret 中。

控制平面细节

令牌控制器

服务账号令牌控制器作为 kube-controller-manager 的一部分运行,以异步的形式工作。 其职责包括:

  • 监测 ServiceAccount 的删除并删除所有相应的服务账号令牌 Secret。
  • 监测服务账号令牌 Secret 的添加,保证相应的 ServiceAccount 存在,如有需要, 向 Secret 中添加令牌。
  • 监测服务账号令牌 Secret 的删除,如有需要,从相应的 ServiceAccount 中移除引用。

你必须通过 --service-account-private-key-file 标志为 kube-controller-manager 的令牌控制器传入一个服务账号私钥文件。该私钥用于为所生成的服务账号令牌签名。 同样地,你需要通过 --service-account-key-file 标志将对应的公钥通知给 kube-apiserver。公钥用于在身份认证过程中校验令牌。

ServiceAccount 准入控制器

对 Pod 的改动通过一个被称为准入控制器的插件来实现。 它是 API 服务器的一部分。当 Pod 被创建时,该准入控制器会同步地修改 Pod。 如果该插件处于激活状态(在大多数发行版中都是默认激活的),当 Pod 被创建时它会进行以下操作:

  1. 如果该 Pod 没有设置 .spec.serviceAccountName, 准入控制器为新来的 Pod 将 ServiceAccount 的名称设为 default
  2. 准入控制器保证新来的 Pod 所引用的 ServiceAccount 确实存在。 如果没有 ServiceAccount 具有匹配的名称,则准入控制器拒绝新来的 Pod。 这个检查甚至适用于 default ServiceAccount。
  1. 如果服务账号的 automountServiceAccountToken 字段或 Pod 的 automountServiceAccountToken 字段都未显式设置为 false
    • 准入控制器变更新来的 Pod,添加一个包含 API 访问令牌的额外
    • 准入控制器将 volumeMount 添加到 Pod 中的每个容器, 忽略已为 /var/run/secrets/kubernetes.io/serviceaccount 路径定义的卷挂载的所有容器。 对于 Linux 容器,此卷挂载在 /var/run/secrets/kubernetes.io/serviceaccount; 在 Windows 节点上,此卷挂载在等价的路径上。
  2. 如果新来 Pod 的规约已包含任何 imagePullSecrets,则准入控制器添加 imagePullSecrets, 并从 ServiceAccount 进行复制。

TokenRequest API

特性状态: Kubernetes v1.22 [stable]

你使用 ServiceAccount 的 TokenRequest 子资源为该 ServiceAccount 获取有时间限制的令牌。 你不需要调用它来获取在容器中使用的 API 令牌,因为 kubelet 使用 投射卷 对此进行了设置。

如果你想要从 kubectl 使用 TokenRequest API, 请参阅为 ServiceAccount 手动创建 API 令牌

Kubernetes 控制平面(特别是 ServiceAccount 准入控制器)向 Pod 添加了一个投射卷, kubelet 确保该卷包含允许容器作为正确 ServiceAccount 进行身份认证的令牌。

(这种机制取代了之前基于 Secret 添加卷的机制,之前 Secret 代表了 Pod 所用的 ServiceAccount 但不会过期。)

以下示例演示如何查找已启动的 Pod:

...
  - name: kube-api-access-<random-suffix>
    projected:
      defaultMode: 420 # 这个十进制数等同于八进制 0644
      sources:
        - serviceAccountToken:
            expirationSeconds: 3607
            path: token
        - configMap:
            items:
              - key: ca.crt
                path: ca.crt
            name: kube-root-ca.crt
        - downwardAPI:
            items:
              - fieldRef:
                  apiVersion: v1
                  fieldPath: metadata.namespace
                path: namespace

该清单片段定义了由三个数据源信息组成的投射卷。

  1. serviceAccountToken 数据源,包含 kubelet 从 kube-apiserver 获取的令牌。 kubelet 使用 TokenRequest API 获取有时间限制的令牌。为 TokenRequest 服务的这个令牌会在 Pod 被删除或定义的生命周期(默认为 1 小时)结束之后过期。该令牌绑定到特定的 Pod, 并将其 audience(受众)设置为与 kube-apiserver 的 audience 相匹配。
  2. configMap 数据源。ConfigMap 包含一组证书颁发机构数据。 Pod 可以使用这些证书来确保自己连接到集群的 kube-apiserver(而不是连接到中间件或意外配置错误的对等点上)。
  3. downwardAPI 数据源。这个 downwardAPI 卷获得包含 Pod 的名字空间的名称, 并使该名称信息可用于在 Pod 内运行的应用程序代码。

挂载此卷的 Pod 内的所有容器均可以访问上述信息。

创建额外的 API 令牌

要为 ServiceAccount 创建一个不过期、持久化的 API 令牌, 请创建一个类型为 kubernetes.io/service-account-token 的 Secret,附带引用 ServiceAccount 的注解。 控制平面随后生成一个长久的令牌,并使用生成的令牌数据更新该 Secret。

以下是此类 Secret 的示例清单:

apiVersion: v1
kind: Secret
type: kubernetes.io/service-account-token
metadata:
  name: mysecretname
  annotations:
    - kubernetes.io/service-account.name: myserviceaccount

若要基于此示例创建 Secret,运行以下命令:

kubectl -n examplens create -f https://k8s.io/examples/secret/serviceaccount/mysecretname.yaml

若要查看该 Secret 的详细信息,运行以下命令:

kubectl -n examplens describe secret mysecretname

输出类似于:

Name:           mysecretname
Namespace:      examplens
Labels:         <none>
Annotations:    kubernetes.io/service-account.name=myserviceaccount
                kubernetes.io/service-account.uid=8a85c4c4-8483-11e9-bc42-526af7764f64

Type:   kubernetes.io/service-account-token

Data
====
ca.crt:         1362 bytes
namespace:      9 bytes
token:          ...

如果你在 examplens 名字空间中启动新的 Pod,可以使用你刚刚创建的 myserviceaccount service-account-token Secret。

删除/废止 ServiceAccount 令牌

如果你知道 Secret 的名称且该 Secret 包含要移除的令牌:

kubectl delete secret name-of-secret

否则,先找到 ServiceAccount 所用的 Secret。

# 此处假设你已有一个名为 'examplens' 的名字空间
kubectl -n examplens get serviceaccount/example-automated-thing -o yaml

输出类似于:

apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
            {"apiVersion":"v1","kind":"ServiceAccount","metadata":{"annotations":{},"name":"example-automated-thing","namespace":"examplens"}}
  creationTimestamp: "2019-07-21T07:07:07Z"
  name: example-automated-thing
  namespace: examplens
  resourceVersion: "777"
  selfLink: /api/v1/namespaces/examplens/serviceaccounts/example-automated-thing
  uid: f23fd170-66f2-4697-b049-e1e266b7f835
secrets:
- name: example-automated-thing-token-zyxwv

随后删除你现在知道名称的 Secret:

kubectl -n examplens delete secret/example-automated-thing-token-zyxwv

控制平面发现 ServiceAccount 缺少其 Secret,并创建一个替代项:

kubectl -n examplens get serviceaccount/example-automated-thing -o yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
            {"apiVersion":"v1","kind":"ServiceAccount","metadata":{"annotations":{},"name":"example-automated-thing","namespace":"examplens"}}
  creationTimestamp: "2019-07-21T07:07:07Z"
  name: example-automated-thing
  namespace: examplens
  resourceVersion: "1026"
  selfLink: /api/v1/namespaces/examplens/serviceaccounts/example-automated-thing
  uid: f23fd170-66f2-4697-b049-e1e266b7f835
secrets:
- name: example-automated-thing-token-4rdrh

清理

如果创建了一个 examplens 名字空间进行试验,你可以移除它:

kubectl delete namespace examplens

控制平面细节

ServiceAccount 控制器

ServiceAccount 控制器管理名字空间内的 ServiceAccount,并确保每个活跃的名字空间中都存在名为 “default” 的 ServiceAccount。

令牌控制器

服务账号令牌控制器作为 kube-controller-manager 的一部分运行,以异步的形式工作。 其职责包括:

  • 监测 ServiceAccount 的创建并创建相应的服务账号令牌 Secret 以允许 API 访问。
  • 监测 ServiceAccount 的删除并删除所有相应的服务账号令牌 Secret。
  • 监测服务账号令牌 Secret 的添加,保证相应的 ServiceAccount 存在,如有需要, 向 Secret 中添加令牌。
  • 监测 Secret 的删除,如有需要,从相应的 ServiceAccount 中移除引用。

你必须通过 --service-account-private-key-file 标志为 kube-controller-manager 的令牌控制器传入一个服务账号私钥文件。该私钥用于为所生成的服务账号令牌签名。 同样地,你需要通过 --service-account-key-file 标志将对应的公钥通知给 kube-apiserver。公钥用于在身份认证过程中校验令牌。

接下来

3.7 - 鉴权概述

了解有关 Kubernetes 鉴权的更多信息,包括使用支持的鉴权模块创建策略的详细信息。

在 Kubernetes 中,你必须在鉴权(授予访问权限)之前进行身份验证(登录),有关身份验证的信息, 请参阅访问控制概述.

Kubernetes 期望请求中存在 REST API 常见的属性。 这意味着 Kubernetes 鉴权适用于现有的组织范围或云提供商范围的访问控制系统, 除了 Kubernetes API 之外,它还可以处理其他 API。

确定是允许还是拒绝请求

Kubernetes 使用 API 服务器对 API 请求进行鉴权。 它根据所有策略评估所有请求属性来决定允许或拒绝请求。 一个 API 请求的所有部分都必须被某些策略允许才能继续。 这意味着默认情况下拒绝权限。

(尽管 Kubernetes 使用 API 服务器,但是依赖于特定对象种类的特定字段的访问控制 和策略由准入控制器处理。)

当系统配置了多个鉴权模块时,Kubernetes 将按顺序使用每个模块。 如果任何鉴权模块批准或拒绝请求,则立即返回该决定,并且不会与其他鉴权模块协商。 如果所有模块对请求没有意见,则拒绝该请求。 被拒绝响应返回 HTTP 状态代码 403。

审查你的请求属性

Kubernetes 仅审查以下 API 请求属性:

  • 用户 —— 身份验证期间提供的 user 字符串。
  • —— 经过身份验证的用户所属的组名列表。
  • 额外信息 —— 由身份验证层提供的任意字符串键到字符串值的映射。
  • API —— 指示请求是否针对 API 资源。
  • 请求路径 —— 各种非资源端点的路径,如 /api/healthz
  • API 请求动词 —— API 动词 getlistcreateupdatepatchwatchproxyredirectdeletedeletecollection 用于资源请求。 要确定资源 API 端点的请求动词,请参阅确定请求动词
  • HTTP 请求动词 —— HTTP 动词 getpostputdelete 用于非资源请求。
  • 资源 —— 正在访问的资源的 ID 或名称(仅限资源请求)- 对于使用 getupdatepatchdelete 动词的资源请求,你必须提供资源名称。
  • 子资源 —— 正在访问的子资源(仅限资源请求)。
  • 名字空间 —— 正在访问的对象的名称空间(仅适用于名字空间资源请求)。
  • API 组 —— 正在访问的 API 组 (仅限资源请求)。空字符串表示核心 API 组

确定请求动词

非资源请求

对于 /api/v1/.../apis/<group>/<version>/... 之外的端点的请求被视为 “非资源请求(Non-Resource Requests)”, 并使用该请求的 HTTP 方法的小写形式作为其请求动词。

例如,对 /api/healthz 这类端点的 GET 请求将使用 get 作为其动词。

资源请求

要确定对资源 API 端点的请求动词,需要查看所使用的 HTTP 动词以及该请求是针对单个资源还是一组资源:

HTTP 动词请求动词
POSTcreate
GET, HEADget (针对单个资源)、list(针对集合)
PUTupdate
PATCHpatch
DELETEdelete(针对单个资源)、deletecollection(针对集合)

Kubernetes 有时使用专门的动词以对额外的权限进行鉴权。例如:

  • RBAC
    • rbac.authorization.k8s.io API 组中 rolesclusterroles 资源的 bindescalate 动词
  • 身份认证
    • 对核心 API 组中 usersgroupsserviceaccounts 以及 authentication.k8s.io API 组中的 userextras 所使用的 impersonate 动词。

鉴权模块

  • Node —— 一个专用鉴权模式,根据调度到 kubelet 上运行的 Pod 为 kubelet 授予权限。 要了解有关使用节点鉴权模式的更多信息,请参阅节点鉴权
  • ABAC —— 基于属性的访问控制(ABAC)定义了一种访问控制范型,通过使用将属性组合在一起的策略, 将访问权限授予用户。策略可以使用任何类型的属性(用户属性、资源属性、对象,环境属性等)。 要了解有关使用 ABAC 模式的更多信息,请参阅 ABAC 模式
  • RBAC —— 基于角色的访问控制(RBAC) 是一种基于企业内个人用户的角色来管理对计算机或网络资源的访问的方法。 在此上下文中,权限是单个用户执行特定任务的能力, 例如查看、创建或修改文件。要了解有关使用 RBAC 模式的更多信息,请参阅 RBAC 模式
    • 被启用之后,RBAC(基于角色的访问控制)使用 rbac.authorization.k8s.io API 组来驱动鉴权决策,从而允许管理员通过 Kubernetes API 动态配置权限策略。
    • 要启用 RBAC,请使用 --authorization-mode = RBAC 启动 API 服务器。
  • Webhook —— WebHook 是一个 HTTP 回调:发生某些事情时调用的 HTTP POST; 通过 HTTP POST 进行简单的事件通知。 实现 WebHook 的 Web 应用程序会在发生某些事情时将消息发布到 URL。 要了解有关使用 Webhook 模式的更多信息,请参阅 Webhook 模式

检查 API 访问

kubectl 提供 auth can-i 子命令,用于快速查询 API 鉴权。 该命令使用 SelfSubjectAccessReview API 来确定当前用户是否可以执行给定操作, 无论使用何种鉴权模式该命令都可以工作。

kubectl auth can-i create deployments --namespace dev

输出类似于:

yes
kubectl auth can-i create deployments --namespace prod

输出类似于:

no

管理员可以将此与用户扮演(User Impersonation) 结合使用,以确定其他用户可以执行的操作。

kubectl auth can-i list secrets --namespace dev --as dave

输出类似于:

no

类似地,检查名字空间 dev 里的 dev-sa 服务账户是否可以列举名字空间 target 里的 Pod:

kubectl auth can-i list pods \
	--namespace target \
	--as system:serviceaccount:dev:dev-sa

输出类似于:

yes

SelfSubjectAccessReviewauthorization.k8s.io API 组的一部分,它将 API 服务器鉴权公开给外部服务。该组中的其他资源包括:

  • SubjectAccessReview - 对任意用户的访问进行评估,而不仅仅是当前用户。 当鉴权决策被委派给 API 服务器时很有用。例如,kubelet 和扩展 API 服务器使用它来确定用户对自己的 API 的访问权限。
  • LocalSubjectAccessReview - 与 SubjectAccessReview 类似,但仅限于特定的名字空间。
  • SelfSubjectRulesReview - 返回用户可在名字空间内执行的操作集的审阅。 用户可以快速汇总自己的访问权限,或者用于 UI 中的隐藏/显示动作。

可以通过创建普通的 Kubernetes 资源来查询这些 API,其中返回对象的响应 "status" 字段是查询的结果。

kubectl create -f - -o yaml << EOF
apiVersion: authorization.k8s.io/v1
kind: SelfSubjectAccessReview
spec:
  resourceAttributes:
    group: apps
    name: deployments
    verb: create
    namespace: dev
EOF

生成的 SelfSubjectAccessReview 为:

apiVersion: authorization.k8s.io/v1
kind: SelfSubjectAccessReview
metadata:
  creationTimestamp: null
spec:
  resourceAttributes:
    group: apps
    name: deployments
    namespace: dev
    verb: create
status:
  allowed: true
  denied: false

为你的鉴权模块设置参数

你必须在策略中包含一个参数标志,以指明你的策略包含哪个鉴权模块:

可以使用的参数有:

  • --authorization-mode=ABAC 基于属性的访问控制(ABAC)模式允许你使用本地文件配置策略。
  • --authorization-mode=RBAC 基于角色的访问控制(RBAC)模式允许你使用 Kubernetes API 创建和存储策略。
  • --authorization-mode=Webhook WebHook 是一种 HTTP 回调模式,允许你使用远程 REST 端点管理鉴权。
  • --authorization-mode=Node 节点鉴权是一种特殊用途的鉴权模式,专门对 kubelet 发出的 API 请求执行鉴权。
  • --authorization-mode=AlwaysDeny 该标志阻止所有请求。仅将此标志用于测试。
  • --authorization-mode=AlwaysAllow 此标志允许所有请求。仅在你不需要 API 请求的鉴权时才使用此标志。

你可以选择多个鉴权模块。模块按顺序检查,以便较靠前的模块具有更高的优先级来允许或拒绝请求。

通过创建或编辑工作负载提升权限

能够在名字空间中创建或者编辑 Pod 的用户, 无论是直接操作还是通过控制器 (例如,一个 Operator)来操作,都可以提升他们在该名字空间内的权限。

特权提升途径

  • 挂载该名字空间内的任意 Secret
    • 可以用来访问其他工作负载专用的 Secret
    • 可以用来获取权限更高的服务账号的令牌
  • 使用该名字空间内的任意服务账号
    • 可以用另一个工作负载的身份来访问 Kubernetes API(伪装)
    • 可以执行该服务账号的任意特权操作
  • 挂载该名字空间里其他工作负载专用的 ConfigMap
    • 可以用来获取其他工作负载专用的信息,例如数据库主机名。
  • 挂载该名字空间里其他工作负载的卷
    • 可以用来获取其他工作负载专用的信息,并且更改它。

接下来

3.8 - 使用 RBAC 鉴权

基于角色(Role)的访问控制(RBAC)是一种基于组织中用户的角色来调节控制对计算机或网络资源的访问的方法。

RBAC 鉴权机制使用 rbac.authorization.k8s.io API 组来驱动鉴权决定, 允许你通过 Kubernetes API 动态配置策略。

要启用 RBAC,在启动 API 服务器时将 --authorization-mode 参数设置为一个逗号分隔的列表并确保其中包含 RBAC

kube-apiserver --authorization-mode=Example,RBAC --<其他选项> --<其他选项>

API 对象

RBAC API 声明了四种 Kubernetes 对象:RoleClusterRoleRoleBindingClusterRoleBinding。你可以像使用其他 Kubernetes 对象一样,通过类似 kubectl 这类工具描述对象, 或修补对象。

Role 和 ClusterRole

RBAC 的 RoleClusterRole 中包含一组代表相关权限的规则。 这些权限是纯粹累加的(不存在拒绝某操作的规则)。

Role 总是用来在某个名字空间内设置访问权限; 在你创建 Role 时,你必须指定该 Role 所属的名字空间。

与之相对,ClusterRole 则是一个集群作用域的资源。这两种资源的名字不同(Role 和 ClusterRole) 是因为 Kubernetes 对象要么是名字空间作用域的,要么是集群作用域的,不可两者兼具。

ClusterRole 有若干用法。你可以用它来:

  1. 定义对某名字空间域对象的访问权限,并将在个别名字空间内被授予访问权限;
  2. 为名字空间作用域的对象设置访问权限,并被授予跨所有名字空间的访问权限;
  3. 为集群作用域的资源定义访问权限。

如果你希望在名字空间内定义角色,应该使用 Role; 如果你希望定义集群范围的角色,应该使用 ClusterRole。

Role 示例

下面是一个位于 "default" 名字空间的 Role 的示例,可用来授予对 Pod 的读访问权限:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: pod-reader
rules:
- apiGroups: [""] # "" 标明 core API 组
  resources: ["pods"]
  verbs: ["get", "watch", "list"]

ClusterRole 示例

ClusterRole 同样可以用于授予 Role 能够授予的权限。 因为 ClusterRole 属于集群范围,所以它也可以为以下资源授予访问权限:

  • 集群范围资源(比如节点(Node)

  • 非资源端点(比如 /healthz

  • 跨名字空间访问的名字空间作用域的资源(如 Pod)

    比如,你可以使用 ClusterRole 来允许某特定用户执行 kubectl get pods --all-namespaces

下面是一个 ClusterRole 的示例,可用来为任一特定名字空间中的 Secret 授予读访问权限, 或者跨名字空间的访问权限(取决于该角色是如何绑定的):

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  # "namespace" 被忽略,因为 ClusterRoles 不受名字空间限制
  name: secret-reader
rules:
- apiGroups: [""]
  # 在 HTTP 层面,用来访问 Secret 资源的名称为 "secrets"
  resources: ["secrets"]
  verbs: ["get", "watch", "list"]

Role 或 ClusterRole 对象的名称必须是合法的路径分段名称

RoleBinding 和 ClusterRoleBinding

角色绑定(Role Binding)是将角色中定义的权限赋予一个或者一组用户。 它包含若干 主体(用户、组或服务账户)的列表和对这些主体所获得的角色的引用。 RoleBinding 在指定的名字空间中执行授权,而 ClusterRoleBinding 在集群范围执行授权。

一个 RoleBinding 可以引用同一的名字空间中的任何 Role。 或者,一个 RoleBinding 可以引用某 ClusterRole 并将该 ClusterRole 绑定到 RoleBinding 所在的名字空间。 如果你希望将某 ClusterRole 绑定到集群中所有名字空间,你要使用 ClusterRoleBinding。

RoleBinding 或 ClusterRoleBinding 对象的名称必须是合法的 路径分段名称

RoleBinding 示例

下面的例子中的 RoleBinding 将 "pod-reader" Role 授予在 "default" 名字空间中的用户 "jane"。 这样,用户 "jane" 就具有了读取 "default" 名字空间中所有 Pod 的权限。

apiVersion: rbac.authorization.k8s.io/v1
# 此角色绑定允许 "jane" 读取 "default" 名字空间中的 Pod
# 你需要在该命名空间中有一个名为 “pod-reader” 的 Role
kind: RoleBinding
metadata:
  name: read-pods
  namespace: default
subjects:
# 你可以指定不止一个“subject(主体)”
- kind: User
  name: jane # "name" 是区分大小写的
  apiGroup: rbac.authorization.k8s.io
roleRef:
  # "roleRef" 指定与某 Role 或 ClusterRole 的绑定关系
  kind: Role        # 此字段必须是 Role 或 ClusterRole
  name: pod-reader  # 此字段必须与你要绑定的 Role 或 ClusterRole 的名称匹配
  apiGroup: rbac.authorization.k8s.io

RoleBinding 也可以引用 ClusterRole,以将对应 ClusterRole 中定义的访问权限授予 RoleBinding 所在名字空间的资源。这种引用使得你可以跨整个集群定义一组通用的角色, 之后在多个名字空间中复用。

例如,尽管下面的 RoleBinding 引用的是一个 ClusterRole,"dave"(这里的主体, 区分大小写)只能访问 "development" 名字空间中的 Secrets 对象,因为 RoleBinding 所在的名字空间(由其 metadata 决定)是 "development"。

apiVersion: rbac.authorization.k8s.io/v1
# 此角色绑定使得用户 "dave" 能够读取 "development" 名字空间中的 Secrets
# 你需要一个名为 "secret-reader" 的 ClusterRole
kind: RoleBinding
metadata:
  name: read-secrets
  # RoleBinding 的名字空间决定了访问权限的授予范围。
  # 这里隐含授权仅在 "development" 名字空间内的访问权限。
  namespace: development
subjects:
- kind: User
  name: dave # 'name' 是区分大小写的
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io

ClusterRoleBinding 示例

要跨整个集群完成访问权限的授予,你可以使用一个 ClusterRoleBinding。 下面的 ClusterRoleBinding 允许 "manager" 组内的所有用户访问任何名字空间中的 Secret。

apiVersion: rbac.authorization.k8s.io/v1
# 此集群角色绑定允许 “manager” 组中的任何人访问任何名字空间中的 Secret 资源
kind: ClusterRoleBinding
metadata:
  name: read-secrets-global
subjects:
- kind: Group
  name: manager      # 'name' 是区分大小写的
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io

创建了绑定之后,你不能再修改绑定对象所引用的 Role 或 ClusterRole。 试图改变绑定对象的 roleRef 将导致合法性检查错误。 如果你想要改变现有绑定对象中 roleRef 字段的内容,必须删除重新创建绑定对象。

这种限制有两个主要原因:

  1. roleRef 设置为不可以改变,这使得可以为用户授予对现有绑定对象的 update 权限, 这样可以让他们管理主体列表,同时不能更改被授予这些主体的角色。
  1. 针对不同角色的绑定是完全不一样的绑定。要求通过删除/重建绑定来更改 roleRef, 这样可以确保要赋予绑定的所有主体会被授予新的角色(而不是在允许或者不小心修改了 roleRef 的情况下导致所有现有主体未经验证即被授予新角色对应的权限)。

命令 kubectl auth reconcile 可以创建或者更新包含 RBAC 对象的清单文件, 并且在必要的情况下删除和重新创建绑定对象,以改变所引用的角色。 更多相关信息请参照命令用法和示例

对资源的引用

在 Kubernetes API 中,大多数资源都是使用对象名称的字符串表示来呈现与访问的。 例如,对于 Pod 应使用 "pods"。 RBAC 使用对应 API 端点的 URL 中呈现的名字来引用资源。 有一些 Kubernetes API 涉及 子资源(subresource),例如 Pod 的日志。 对 Pod 日志的请求看起来像这样:

GET /api/v1/namespaces/{namespace}/pods/{name}/log

在这里,pods 对应名字空间作用域的 Pod 资源,而 logpods 的子资源。 在 RBAC 角色表达子资源时,使用斜线(/)来分隔资源和子资源。 要允许某主体读取 pods 同时访问这些 Pod 的 log 子资源,你可以这样写:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: pod-and-pod-logs-reader
rules:
- apiGroups: [""]
  resources: ["pods", "pods/log"]
  verbs: ["get", "list"]

对于某些请求,也可以通过 resourceNames 列表按名称引用资源。 在指定时,可以将请求限定为资源的单个实例。 下面的例子中限制可以 getupdate 一个名为 my-configmapConfigMap

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: configmap-updater
rules:
- apiGroups: [""]
  # 在 HTTP 层面,用来访问 ConfigMap 资源的名称为 "configmaps"
  resources: ["configmaps"]
  resourceNames: ["my-configmap"]
  verbs: ["update", "get"]

使用通配符 * 可以批量引用所有的 resourcesverbs 对象,无需逐一引用。 对于 nonResourceURLs,可以将通配符 * 作为后缀实现全局通配, 对于 apiGroupsresourceNames,空集表示没有任何限制。 下面的示例允许对所有当前和未来资源执行所有动作(注意,这类似于内置的 cluster-admin)。

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: example.com-superuser  # 此角色仅作示范,请勿使用
rules:
- apiGroups: ["example.com"]
  resources: ["*"]
  verbs: ["*"]

聚合的 ClusterRole

你可以将若干 ClusterRole 聚合(Aggregate) 起来,形成一个复合的 ClusterRole。 作为集群控制面的一部分,控制器会监视带有 aggregationRule 的 ClusterRole 对象集合。aggregationRule 为控制器定义一个标签选择算符供后者匹配应该组合到当前 ClusterRole 的 roles 字段中的 ClusterRole 对象。

下面是一个聚合 ClusterRole 的示例:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: monitoring
aggregationRule:
  clusterRoleSelectors:
  - matchLabels:
      rbac.example.com/aggregate-to-monitoring: "true"
rules: [] # 控制面自动填充这里的规则

如果你创建一个与某个已存在的聚合 ClusterRole 的标签选择算符匹配的 ClusterRole, 这一变化会触发新的规则被添加到聚合 ClusterRole 的操作。 下面的例子中,通过创建一个标签同样为 rbac.example.com/aggregate-to-monitoring: true 的 ClusterRole,新的规则可被添加到 "monitoring" ClusterRole 中。

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: monitoring-endpoints
  labels:
    rbac.example.com/aggregate-to-monitoring: "true"
# 当你创建 "monitoring-endpoints" ClusterRole 时,
# 下面的规则会被添加到 "monitoring" ClusterRole 中
rules:
- apiGroups: [""]
  resources: ["services", "endpointslices", "pods"]
  verbs: ["get", "list", "watch"]

默认的面向用户的角色使用 ClusterRole 聚合。 这使得作为集群管理员的你可以为扩展默认规则,包括为定制资源设置规则, 比如通过 CustomResourceDefinitions 或聚合 API 服务器提供的定制资源。

例如,下面的 ClusterRoles 让默认角色 "admin" 和 "edit" 拥有管理自定义资源 "CronTabs" 的权限, "view" 角色对 CronTab 资源拥有读操作权限。 你可以假定 CronTab 对象在 API 服务器所看到的 URL 中被命名为 "crontabs"

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: aggregate-cron-tabs-edit
  labels:
    # 添加以下权限到默认角色 "admin" 和 "edit" 中
    rbac.authorization.k8s.io/aggregate-to-admin: "true"
    rbac.authorization.k8s.io/aggregate-to-edit: "true"
rules:
- apiGroups: ["stable.example.com"]
  resources: ["crontabs"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: aggregate-cron-tabs-view
  labels:
    # 添加以下权限到 "view" 默认角色中
    rbac.authorization.k8s.io/aggregate-to-view: "true"
rules:
- apiGroups: ["stable.example.com"]
  resources: ["crontabs"]
  verbs: ["get", "list", "watch"]

Role 示例

以下示例均为从 Role 或 ClusterRole 对象中截取出来,我们仅展示其 rules 部分。

允许读取在核心 API 组下的 "pods"

rules:
- apiGroups: [""]
  # 在 HTTP 层面,用来访问 Pod 资源的名称为 "pods"
  resources: ["pods"]
  verbs: ["get", "list", "watch"]

允许在 "apps" API 组中读/写 Deployment(在 HTTP 层面,对应 URL 中资源部分为 "deployments"):

rules:
- apiGroups: ["apps"]
  #
  # 在 HTTP 层面,用来访问 Deployment 资源的名称为 "deployments"
  resources: ["deployments"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

允许读取核心 API 组中的 Pod 和读/写 "batch" API 组中的 Job 资源:

rules:
- apiGroups: [""]
  # 在 HTTP 层面,用来访问 Pod 资源的名称为 "pods"
  resources: ["pods"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["batch"]
  # 在 HTTP 层面,用来访问 Job 资源的名称为 "jobs"
  resources: ["jobs"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

允许读取名称为 "my-config" 的 ConfigMap(需要通过 RoleBinding 绑定以限制为某名字空间中特定的 ConfigMap):

rules:
- apiGroups: [""]
  # 在 HTTP 层面,用来访问 ConfigMap 资源的名称为 "configmaps"
  resources: ["configmaps"]
  resourceNames: ["my-config"]
  verbs: ["get"]

允许读取在核心组中的 "nodes" 资源(因为 Node 是集群作用域的,所以需要 ClusterRole 绑定到 ClusterRoleBinding 才生效):

rules:
- apiGroups: [""]
  # 在 HTTP 层面,用来访问 Node 资源的名称为 "nodes"
  resources: ["nodes"]
  verbs: ["get", "list", "watch"]

允许针对非资源端点 /healthz 和其子路径上发起 GET 和 POST 请求 (必须在 ClusterRole 绑定 ClusterRoleBinding 才生效):

rules:
- nonResourceURLs: ["/healthz", "/healthz/*"] # nonResourceURL 中的 '*' 是一个全局通配符
  verbs: ["get", "post"]

对主体的引用

RoleBinding 或者 ClusterRoleBinding 可绑定角色到某 主体(Subject) 上。 主体可以是组,用户或者服务账户

Kubernetes 用字符串来表示用户名。 用户名可以是普通的用户名,像 "alice";或者是邮件风格的名称,如 "bob@example.com", 或者是以字符串形式表达的数字 ID。你作为 Kubernetes 管理员负责配置身份认证模块, 以便后者能够生成你所期望的格式的用户名。

在 Kubernetes 中,身份认证(Authenticator)模块提供用户组信息。 与用户名一样,用户组名也用字符串来表示,而且对该字符串没有格式要求, 只是不能使用保留的前缀 system:

服务账户(ServiceAccount) 的用户名前缀为 system:serviceaccount:,属于前缀为 system:serviceaccounts: 的用户组。

RoleBinding 示例

下面示例是 RoleBinding 中的片段,仅展示其 subjects 的部分。

对于名称为 alice@example.com 的用户:

subjects:
- kind: User
  name: "alice@example.com"
  apiGroup: rbac.authorization.k8s.io

对于名称为 frontend-admins 的用户组:

subjects:
- kind: Group
  name: "frontend-admins"
  apiGroup: rbac.authorization.k8s.io

对于 kube-system 名字空间中的默认服务账户:

subjects:
- kind: ServiceAccount
  name: default
  namespace: kube-system

对于 "qa" 名称空间中的所有服务账户:

subjects:
- kind: Group
  name: system:serviceaccounts:qa
  apiGroup: rbac.authorization.k8s.io

对于在任何名字空间中的服务账户:

subjects:
- kind: Group
  name: system:serviceaccounts
  apiGroup: rbac.authorization.k8s.io

对于所有已经过身份认证的用户:

subjects:
- kind: Group
  name: system:authenticated
  apiGroup: rbac.authorization.k8s.io

对于所有未通过身份认证的用户:

subjects:
- kind: Group
  name: system:unauthenticated
  apiGroup: rbac.authorization.k8s.io

对于所有用户:

subjects:
- kind: Group
  name: system:authenticated
  apiGroup: rbac.authorization.k8s.io
- kind: Group
  name: system:unauthenticated
  apiGroup: rbac.authorization.k8s.io

默认 Roles 和 Role Bindings

API 服务器创建一组默认的 ClusterRole 和 ClusterRoleBinding 对象。 这其中许多是以 system: 为前缀的,用以标识对应资源是直接由集群控制面管理的。 所有的默认 ClusterRole 和 ClusterRoleBinding 都有 kubernetes.io/bootstrapping=rbac-defaults 标签。

自动协商

在每次启动时,API 服务器都会更新默认 ClusterRole 以添加缺失的各种权限, 并更新默认的 ClusterRoleBinding 以增加缺失的各类主体。 这种自动协商机制允许集群去修复一些不小心发生的修改, 并且有助于保证角色和角色绑定在新的发行版本中有权限或主体变更时仍然保持最新。

如果要禁止此功能,请将默认 ClusterRole 以及 ClusterRoleBinding 的 rbac.authorization.kubernetes.io/autoupdate 注解设置成 false。 注意,缺少默认权限和角色绑定主体可能会导致集群无法正常工作。

如果基于 RBAC 的鉴权机制被启用,则自动协商功能默认是被启用的。

API 发现角色

无论是经过身份验证的还是未经过身份验证的用户, 默认的角色绑定都授权他们读取被认为是可安全地公开访问的 API(包括 CustomResourceDefinitions)。 如果要禁用匿名的未经过身份验证的用户访问,请在 API 服务器配置中中添加 --anonymous-auth=false 的配置选项。

通过运行命令 kubectl 可以查看这些角色的配置信息:

kubectl get clusterroles system:discovery -o yaml
Kubernetes RBAC API 发现角色
默认 ClusterRole默认 ClusterRoleBinding描述
system:basic-usersystem:authenticated允许用户以只读的方式去访问他们自己的基本信息。在 v1.14 版本之前,这个角色在默认情况下也绑定在 system:unauthenticated 上。
system:discoverysystem:authenticated允许以只读方式访问 API 发现端点,这些端点用来发现和协商 API 级别。 在 v1.14 版本之前,这个角色在默认情况下绑定在 system:unauthenticated 上。
system:public-info-viewersystem:authenticatedsystem:unauthenticated允许对集群的非敏感信息进行只读访问,此角色是在 v1.14 版本中引入的。

面向用户的角色

一些默认的 ClusterRole 不是以前缀 system: 开头的。这些是面向用户的角色。 它们包括超级用户(Super-User)角色(cluster-admin)、 使用 ClusterRoleBinding 在集群范围内完成授权的角色(cluster-status)、 以及使用 RoleBinding 在特定名字空间中授予的角色(admineditview)。

面向用户的 ClusterRole 使用 ClusterRole 聚合以允许管理员在这些 ClusterRole 上添加用于定制资源的规则。如果想要添加规则到 adminedit 或者 view, 可以创建带有以下一个或多个标签的 ClusterRole:

metadata:
  labels:
    rbac.authorization.k8s.io/aggregate-to-admin: "true"
    rbac.authorization.k8s.io/aggregate-to-edit: "true"
    rbac.authorization.k8s.io/aggregate-to-view: "true"
默认 ClusterRole默认 ClusterRoleBinding描述
cluster-adminsystem:masters允许超级用户在平台上的任何资源上执行所有操作。 当在 ClusterRoleBinding 中使用时,可以授权对集群中以及所有名字空间中的全部资源进行完全控制。 当在 RoleBinding 中使用时,可以授权控制角色绑定所在名字空间中的所有资源,包括名字空间本身。
admin允许管理员访问权限,旨在使用 RoleBinding 在名字空间内执行授权。

如果在 RoleBinding 中使用,则可授予对名字空间中的大多数资源的读/写权限, 包括创建角色和角色绑定的能力。 此角色不允许对资源配额或者名字空间本身进行写操作。 此角色也不允许对 Kubernetes v1.22+ 创建的 EndpointSlices(或 Endpoints)进行写操作。 更多信息参阅 “EndpointSlices 和 Endpoints 写权限”小节

edit允许对名字空间的大多数对象进行读/写操作。

此角色不允许查看或者修改角色或者角色绑定。 不过,此角色可以访问 Secret,以名字空间中任何 ServiceAccount 的身份运行 Pod, 所以可以用来了解名字空间内所有服务账户的 API 访问级别。 此角色也不允许对 Kubernetes v1.22+ 创建的 EndpointSlices(或 Endpoints)进行写操作。 更多信息参阅 “EndpointSlices 和 Endpoints 写操作”小节

view允许对名字空间的大多数对象有只读权限。 它不允许查看角色或角色绑定。

此角色不允许查看 Secrets,因为读取 Secret 的内容意味着可以访问名字空间中 ServiceAccount 的凭据信息,进而允许利用名字空间中任何 ServiceAccount 的身份访问 API(这是一种特权提升)。

核心组件角色

默认 ClusterRole默认 ClusterRoleBinding描述
system:kube-schedulersystem:kube-scheduler 用户允许访问 scheduler 组件所需要的资源。
system:volume-schedulersystem:kube-scheduler 用户允许访问 kube-scheduler 组件所需要的卷资源。
system:kube-controller-managersystem:kube-controller-manager 用户允许访问控制器管理器组件所需要的资源。 各个控制回路所需要的权限在控制器角色详述。
system:node允许访问 kubelet 所需要的资源,包括对所有 Secret 的读操作和对所有 Pod 状态对象的写操作。

你应该使用 Node 鉴权组件NodeRestriction 准入插件而不是 system:node 角色。同时基于 kubelet 上调度执行的 Pod 来授权 kubelet 对 API 的访问。

system:node 角色的意义仅是为了与从 v1.8 之前版本升级而来的集群兼容。

system:node-proxiersystem:kube-proxy 用户允许访问 kube-proxy 组件所需要的资源。

其他组件角色

默认 ClusterRole默认 ClusterRoleBinding描述
system:auth-delegator允许将身份认证和鉴权检查操作外包出去。 这种角色通常用在插件式 API 服务器上,以实现统一的身份认证和鉴权。
system:heapsterHeapster 组件(已弃用)定义的角色。
system:kube-aggregatorkube-aggregator 组件定义的角色。
system:kube-dnskube-system 名字空间中的 kube-dns 服务账户kube-dns 组件定义的角色。
system:kubelet-api-admin允许 kubelet API 的完全访问权限。
system:node-bootstrapper允许访问执行 kubelet TLS 启动引导 所需要的资源。
system:node-problem-detectornode-problem-detector 组件定义的角色。
system:persistent-volume-provisioner允许访问大部分动态卷驱动所需要的资源。
system:monitoringsystem:monitoring允许对控制平面监控端点的读取访问(例如:kube-apiserver 存活和就绪端点(/healthz/livez/readyz), 各个健康检查端点(/healthz/*/livez/*/readyz/*)和 /metrics)。 请注意,各个运行状况检查端点和度量标准端点可能会公开敏感信息。

内置控制器的角色

Kubernetes 控制器管理器运行内建于 Kubernetes 控制面的控制器。 当使用 --use-service-account-credentials 参数启动时,kube-controller-manager 使用单独的服务账户来启动每个控制器。 每个内置控制器都有相应的、前缀为 system:controller: 的角色。 如果控制管理器启动时未设置 --use-service-account-credentials, 它使用自己的身份凭据来运行所有的控制器,该身份必须被授予所有相关的角色。 这些角色包括:

  • system:controller:attachdetach-controller
  • system:controller:certificate-controller
  • system:controller:clusterrole-aggregation-controller
  • system:controller:cronjob-controller
  • system:controller:daemon-set-controller
  • system:controller:deployment-controller
  • system:controller:disruption-controller
  • system:controller:endpoint-controller
  • system:controller:expand-controller
  • system:controller:generic-garbage-collector
  • system:controller:horizontal-pod-autoscaler
  • system:controller:job-controller
  • system:controller:namespace-controller
  • system:controller:node-controller
  • system:controller:persistent-volume-binder
  • system:controller:pod-garbage-collector
  • system:controller:pv-protection-controller
  • system:controller:pvc-protection-controller
  • system:controller:replicaset-controller
  • system:controller:replication-controller
  • system:controller:resourcequota-controller
  • system:controller:root-ca-cert-publisher
  • system:controller:route-controller
  • system:controller:service-account-controller
  • system:controller:service-controller
  • system:controller:statefulset-controller
  • system:controller:ttl-controller

初始化与预防权限提升

RBAC API 会阻止用户通过编辑角色或者角色绑定来提升权限。 由于这一点是在 API 级别实现的,所以在 RBAC 鉴权组件未启用的状态下依然可以正常工作。

对角色创建或更新的限制

只有在符合下列条件之一的情况下,你才能创建/更新角色:

  1. 你已经拥有角色中包含的所有权限,且其作用域与正被修改的对象作用域相同。 (对 ClusterRole 而言意味着集群范围,对 Role 而言意味着相同名字空间或者集群范围)。
  2. 你被显式授权在 rbac.authorization.k8s.io API 组中的 rolesclusterroles 资源使用 escalate 动词。

例如,如果 user-1 没有列举集群范围所有 Secret 的权限,他将不能创建包含该权限的 ClusterRole。 若要允许用户创建/更新角色:

  1. 根据需要赋予他们一个角色,允许他们根据需要创建/更新 Role 或者 ClusterRole 对象。
  2. 授予他们在所创建/更新角色中包含特殊权限的权限:
    • 隐式地为他们授权(如果它们试图创建或者更改 Role 或 ClusterRole 的权限, 但自身没有被授予相应权限,API 请求将被禁止)。
    • 通过允许他们在 Role 或 ClusterRole 资源上执行 escalate 动作显式完成授权。 这里的 rolesclusterroles 资源包含在 rbac.authorization.k8s.io API 组中。

对角色绑定创建或更新的限制

只有你已经具有了所引用的角色中包含的全部权限时,或者 你被授权在所引用的角色上执行 bind 动词时,你才可以创建或更新角色绑定。这里的权限与角色绑定的作用域相同。 例如,如果用户 user-1 没有列举集群范围所有 Secret 的能力,则他不可以创建 ClusterRoleBinding 引用授予该许可权限的角色。 如要允许用户创建或更新角色绑定:

  1. 赋予他们一个角色,使得他们能够根据需要创建或更新 RoleBinding 或 ClusterRoleBinding 对象。
  2. 授予他们绑定某特定角色所需要的许可权限:
    • 隐式授权下,可以将角色中包含的许可权限授予他们;
    • 显式授权下,可以授权他们在特定 Role (或 ClusterRole)上执行 bind 动词的权限。

例如,下面的 ClusterRole 和 RoleBinding 将允许用户 user-1 把名字空间 user-1-namespace 中的 admineditview 角色赋予其他用户:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: role-grantor
rules:
- apiGroups: ["rbac.authorization.k8s.io"]
  resources: ["rolebindings"]
  verbs: ["create"]
- apiGroups: ["rbac.authorization.k8s.io"]
  resources: ["clusterroles"]
  verbs: ["bind"]
  # 忽略 resourceNames 意味着允许绑定任何 ClusterRole
  resourceNames: ["admin","edit","view"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: role-grantor-binding
  namespace: user-1-namespace
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: role-grantor
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: User
  name: user-1

当启动引导第一个角色和角色绑定时,需要为初始用户授予他们尚未拥有的权限。 对初始角色和角色绑定进行初始化时需要:

  • 使用用户组为 system:masters 的凭据,该用户组由默认绑定关联到 cluster-admin 这个超级用户角色。

一些命令行工具

kubectl create role

创建 Role 对象,定义在某一名字空间中的权限。例如:

  • 创建名称为 “pod-reader” 的 Role 对象,允许用户对 Pods 执行 getwatchlist 操作:

    kubectl create role pod-reader --verb=get --verb=list --verb=watch --resource=pods
    
  • 创建名称为 “pod-reader” 的 Role 对象并指定 resourceNames

    kubectl create role pod-reader --verb=get --resource=pods --resource-name=readablepod --resource-name=anotherpod
    
  • 创建名为 “foo” 的 Role 对象并指定 apiGroups

    kubectl create role foo --verb=get,list,watch --resource=replicasets.apps
    
  • 创建名为 “foo” 的 Role 对象并指定子资源权限:

    kubectl create role foo --verb=get,list,watch --resource=pods,pods/status
    
  • 创建名为 “my-component-lease-holder” 的 Role 对象,使其具有对特定名称的资源执行 get/update 的权限:

    kubectl create role my-component-lease-holder --verb=get,list,watch,update --resource=lease --resource-name=my-component
    

kubectl create clusterrole

创建 ClusterRole 对象。例如:

  • 创建名称为 “pod-reader” 的 ClusterRole 对象,允许用户对 Pods 对象执行 getwatchlist 操作:

    kubectl create clusterrole pod-reader --verb=get,list,watch --resource=pods
    
  • 创建名为 “pod-reader” 的 ClusterRole 对象并指定 resourceNames

    kubectl create clusterrole pod-reader --verb=get --resource=pods --resource-name=readablepod --resource-name=anotherpod
    
  • 创建名为 “foo” 的 ClusterRole 对象并指定 apiGroups

    kubectl create clusterrole foo --verb=get,list,watch --resource=replicasets.apps
    
  • 创建名为 “foo” 的 ClusterRole 对象并指定子资源:

    kubectl create clusterrole foo --verb=get,list,watch --resource=pods,pods/status
    
  • 创建名为 “foo” 的 ClusterRole 对象并指定 nonResourceURL

    kubectl create clusterrole "foo" --verb=get --non-resource-url=/logs/*
    
  • 创建名为 “monitoring” 的 ClusterRole 对象并指定 aggregationRule

    kubectl create clusterrole monitoring --aggregation-rule="rbac.example.com/aggregate-to-monitoring=true"
    

kubectl create rolebinding

在特定的名字空间中对 RoleClusterRole 授权。例如:

  • 在名字空间 “acme” 中,将名为 admin 的 ClusterRole 中的权限授予名称 “bob” 的用户:

    kubectl create rolebinding bob-admin-binding --clusterrole=admin --user=bob --namespace=acme
    
  • 在名字空间 “acme” 中,将名为 view 的 ClusterRole 中的权限授予名字空间 “acme” 中名为 myapp 的服务账户:

    kubectl create rolebinding myapp-view-binding --clusterrole=view --serviceaccount=acme:myapp --namespace=acme
    
  • 在名字空间 “acme” 中,将名为 view 的 ClusterRole 对象中的权限授予名字空间 “myappnamespace” 中名称为 myapp 的服务账户:

    kubectl create rolebinding myappnamespace-myapp-view-binding --clusterrole=view --serviceaccount=myappnamespace:myapp --namespace=acme
    

kubectl create clusterrolebinding

在整个集群(所有名字空间)中用 ClusterRole 授权。例如:

  • 在整个集群范围,将名为 cluster-admin 的 ClusterRole 中定义的权限授予名为 “root” 用户:

    kubectl create clusterrolebinding root-cluster-admin-binding --clusterrole=cluster-admin --user=root
    
  • 在整个集群范围内,将名为 system:node-proxier 的 ClusterRole 的权限授予名为 “system:kube-proxy” 的用户:

    kubectl create clusterrolebinding kube-proxy-binding --clusterrole=system:node-proxier --user=system:kube-proxy
    
  • 在整个集群范围内,将名为 view 的 ClusterRole 中定义的权限授予 “acme” 名字空间中名为 “myapp” 的服务账户:

    kubectl create clusterrolebinding myapp-view-binding --clusterrole=view --serviceaccount=acme:myapp
    

kubectl auth reconcile

使用清单文件来创建或者更新 rbac.authorization.k8s.io/v1 API 对象。

尚不存在的对象会被创建,如果对应的名字空间也不存在,必要的话也会被创建。 已经存在的角色会被更新,使之包含输入对象中所给的权限。如果指定了 --remove-extra-permissions,可以删除额外的权限。

已经存在的绑定也会被更新,使之包含输入对象中所给的主体。如果指定了 --remove-extra-permissions,则可以删除多余的主体。

例如:

  • 测试应用 RBAC 对象的清单文件,显示将要进行的更改:

    kubectl auth reconcile -f my-rbac-rules.yaml --dry-run=client
    
  • 应用 RBAC 对象的清单文件,保留角色(roles)中的额外权限和绑定(bindings)中的其他主体:

    kubectl auth reconcile -f my-rbac-rules.yaml
    
  • 应用 RBAC 对象的清单文件,删除角色(roles)中的额外权限和绑定中的其他主体:

    kubectl auth reconcile -f my-rbac-rules.yaml --remove-extra-subjects --remove-extra-permissions
    

服务账户权限

默认的 RBAC 策略为控制面组件、节点和控制器授予权限。 但是不会对 kube-system 名字空间之外的服务账户授予权限。 (除了授予所有已认证用户的发现权限)

这使得你可以根据需要向特定 ServiceAccount 授予特定权限。 细粒度的角色绑定可带来更好的安全性,但需要更多精力管理。 粗粒度的授权可能导致 ServiceAccount 被授予不必要的 API 访问权限(甚至导致潜在的权限提升), 但更易于管理。

按从最安全到最不安全的顺序,存在以下方法:

  1. 为特定应用的服务账户授予角色(最佳实践)

    这要求应用在其 Pod 规约中指定 serviceAccountName, 并额外创建服务账户(包括通过 API、应用程序清单、kubectl create serviceaccount 等)。

    例如,在名字空间 “my-namespace” 中授予服务账户 “my-sa” 只读权限:

    kubectl create rolebinding my-sa-view \
      --clusterrole=view \
      --serviceaccount=my-namespace:my-sa \
      --namespace=my-namespace
    
  1. 将角色授予某名字空间中的 “default” 服务账户

    如果某应用没有指定 serviceAccountName,那么它将使用 “default” 服务账户。

    例如,在名字空间 "my-namespace" 中授予服务账户 "default" 只读权限:

    kubectl create rolebinding default-view \
      --clusterrole=view \
      --serviceaccount=my-namespace:default \
      --namespace=my-namespace
    

    许多插件组件kube-system 名字空间以 “default” 服务账户运行。 要允许这些插件组件以超级用户权限运行,需要将集群的 cluster-admin 权限授予 kube-system 名字空间中的 “default” 服务账户。

    kubectl create clusterrolebinding add-on-cluster-admin \
      --clusterrole=cluster-admin \
      --serviceaccount=kube-system:default
    
  1. 将角色授予名字空间中所有服务账户

    如果你想要名字空间中所有应用都具有某角色,无论它们使用的什么服务账户, 可以将角色授予该名字空间的服务账户组。

    例如,在名字空间 “my-namespace” 中的只读权限授予该名字空间中的所有服务账户:

    kubectl create rolebinding serviceaccounts-view \
      --clusterrole=view \
      --group=system:serviceaccounts:my-namespace \
      --namespace=my-namespace
    
  1. 在集群范围内为所有服务账户授予一个受限角色(不鼓励)

    如果你不想管理每一个名字空间的权限,你可以向所有的服务账户授予集群范围的角色。

    例如,为集群范围的所有服务账户授予跨所有名字空间的只读权限:

    kubectl create clusterrolebinding serviceaccounts-view \
      --clusterrole=view \
      --group=system:serviceaccounts
    
  1. 授予超级用户访问权限给集群范围内的所有服务帐户(强烈不鼓励)

    如果你不在乎如何区分权限,你可以将超级用户访问权限授予所有服务账户。

    kubectl create clusterrolebinding serviceaccounts-cluster-admin \
      --clusterrole=cluster-admin \
      --group=system:serviceaccounts
    

EndpointSlices 和 Endpoints 写权限

在 Kubernetes v1.22 之前版本创建的集群里, “edit” 和 “admin” 聚合角色包含对 EndpointSlices(和 Endpoints)的写权限。 作为 CVE-2021-25740 的缓解措施, 此访问权限不包含在 Kubernetes 1.22 以及更高版本集群的聚合角色里。

升级到 Kubernetes v1.22 版本的现有集群不会包括此变化。 CVE 公告包含了在现有集群里限制此访问权限的指引。

如果你希望在新集群的聚合角色里保留此访问权限,你可以创建下面的 ClusterRole:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  annotations:
    kubernetes.io/description: |-
      将端点写入权限添加到 edit 和 admin 角色。此特性因 CVE-2021-25740 在 1.22
      中默认被移除。请参阅 https://issue.k8s.io/103675
      这一设置将允许写者要求 LoadBalancer 或 Ingress 的实现向外暴露后端 IP 地址,
      所暴露的 IP 地址无法通过其他方式访问,
      并且可以规避对这些后端访问进行预防/隔离的网络策略或安全控制机制。
      EndpointSlice 从未包含在 edit 和 admin 角色中,
      因此 EndpointSlice API 没有什么可恢复的。      
  labels:
    rbac.authorization.k8s.io/aggregate-to-edit: "true"
  name: custom:aggregate-to-edit:endpoints # 你可以随意愿更改这个 name
rules:
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["create", "delete", "deletecollection", "patch", "update"]

从 ABAC 升级

原来运行较老版本 Kubernetes 的集群通常会使用限制宽松的 ABAC 策略, 包括授予所有服务帐户全权访问 API 的能力。

默认的 RBAC 策略为控制面组件、节点和控制器等授予有限的权限,但不会为 kube-system 名字空间外的服务账户授权(除了授予所有认证用户的发现权限之外)。

这样做虽然安全得多,但可能会干扰期望自动获得 API 权限的现有工作负载。 这里有两种方法来完成这种转换:

并行鉴权

同时运行 RBAC 和 ABAC 鉴权模式, 并指定包含现有的 ABAC 策略的策略文件:

--authorization-mode=...,RBAC,ABAC --authorization-policy-file=mypolicy.json

关于命令行中的第一个选项:如果早期的鉴权组件,例如 Node,拒绝了某个请求,则 RBAC 鉴权组件尝试对该 API 请求鉴权。如果 RBAC 也拒绝了该 API 请求,则运行 ABAC 鉴权组件。这意味着被 RBAC 或 ABAC 策略所允许的任何请求都是被允许的请求。

如果 kube-apiserver 启动时,RBAC 组件的日志级别为 5 或更高(--vmodule=rbac*=5--v=5), 你可以在 API 服务器的日志中看到 RBAC 拒绝的细节(前缀 RBAC) 你可以使用这些信息来确定需要将哪些角色授予哪些用户、组或服务帐户。

一旦你将角色授予服务账户且工作负载运行时, 服务器日志中没有出现 RBAC 拒绝消息,就可以删除 ABAC 鉴权器。

宽松的 RBAC 权限

你可以使用 RBAC 角色绑定复制宽松的 ABAC 策略。

在你完成到 RBAC 的迁移后,应该调整集群的访问控制,确保相关的策略满足你的信息安全需求。

3.9 - 使用 ABAC 鉴权

基于属性的访问控制(Attribute-based access control - ABAC)定义了访问控制范例, 其中通过使用将属性组合在一起的策略来向用户授予访问权限。

策略文件格式

基于 ABAC 模式,可以这样指定策略文件 --authorization-policy-file=SOME_FILENAME

此文件格式是 JSON Lines,不应存在外层的列表或映射,每行应只有一个映射。

每一行都是一个策略对象,策略对象是具有以下属性的映射:

  • 版本控制属性:
    • apiVersion,字符串类型:有效值为 abac.authorization.kubernetes.io/v1beta1,允许对策略格式进行版本控制和转换。
    • kind,字符串类型:有效值为 Policy,允许对策略格式进行版本控制和转换。
  • spec 配置为具有以下映射的属性:
    • 主体匹配属性:
      • user,字符串类型;来自 --token-auth-file 的用户字符串,如果你指定 user,它必须与验证用户的用户名匹配。
      • group,字符串类型;如果指定 group,它必须与经过身份验证的用户的一个组匹配,system:authenticated 匹配所有经过身份验证的请求。 system:unauthenticated 匹配所有未经过身份验证的请求。
  • 资源匹配属性:
    • apiGroup,字符串类型;一个 API 组。
      • 例如:appsnetworking.k8s.io
      • 通配符:*匹配所有 API 组。
    • namespace,字符串类型;一个命名空间。
      • 例如:kube-system
      • 通配符:*匹配所有资源请求。
    • resource,字符串类型;资源类型。
      • 例如:podsdeployments
      • 通配符:*匹配所有资源请求。
  • 非资源匹配属性:
    • nonResourcePath,字符串类型;非资源请求路径。
      • 例如:/version/apis
      • 通配符:
        • * 匹配所有非资源请求。
        • /foo/* 匹配 /foo/ 的所有子路径。
  • readonly,键入布尔值,如果为 true,则表示该策略仅适用于 get、list 和 watch 操作。

鉴权算法

请求具有与策略对象的属性对应的属性。

当接收到请求时,确定属性。未知属性设置为其类型的零值(例如:空字符串,0,false)。

设置为 "*" 的属性将匹配相应属性的任何值。

检查属性的元组,以匹配策略文件中的每个策略。如果至少有一行匹配请求属性,则请求被鉴权(但仍可能无法通过稍后的合法性检查)。

要允许任何经过身份验证的用户执行某些操作,请将策略组属性设置为 "system:authenticated"

要允许任何未经身份验证的用户执行某些操作,请将策略组属性设置为 "system:unauthenticated"

要允许用户执行任何操作,请使用设置为 "*" 的 apiGroup,namespace,resource 和 nonResourcePath 属性编写策略。

kubectl

kubectl 使用 apiserver 的 /api/apis 端点来发现服务资源类型, 并使用位于 /openapi/v2 的模式信息来验证通过创建/更新操作发送到 API 的对象。

当使用 ABAC 鉴权时,这些特殊资源必须显式地通过策略中的 nonResourcePath 属性暴露出来(参见下面的 示例):

  • /api/api/*/apis/apis/* 用于 API 版本协商。
  • /version 通过 kubectl version 检索服务器版本。
  • /swaggerapi/* 用于创建 / 更新操作。

要检查涉及到特定 kubectl 操作的 HTTP 调用,你可以调整详细程度: kubectl --v=8 version

例子

  1. Alice 可以对所有资源做任何事情:

    {"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "alice", "namespace": "*", "resource": "*", "apiGroup": "*"}}
    
  2. kubelet 可以读取任何 pod:

    {"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "kubelet", "namespace": "*", "resource": "pods", "readonly": true}}
    
  3. kubelet 可以读写事件:

    {"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "kubelet", "namespace": "*", "resource": "events"}}
    
  1. Bob 可以在命名空间 projectCaribou 中读取 pod:

    {"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "bob", "namespace": "projectCaribou", "resource": "pods", "readonly": true}}
    
  2. 任何人都可以对所有非资源路径进行只读请求:

    {"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"group": "system:authenticated", "readonly": true, "nonResourcePath": "*"}}
    {"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"group": "system:unauthenticated", "readonly": true, "nonResourcePath": "*"}}
    

完整文件示例

服务帐户的快速说明

服务帐户自动生成用户。用户名是根据命名约定生成的:

system:serviceaccount:<namespace>:<serviceaccountname>

创建新的命名空间也会导致创建一个新的服务帐户:

system:serviceaccount:<namespace>:default

例如,如果要将 API 的 kube-system 完整权限中的默认服务帐户授予,则可以将此行添加到策略文件中:

{"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec":{"user":"system:serviceaccount:kube-system:default","namespace":"*","resource":"*","apiGroup":"*"}}

需要重新启动 apiserver 以获取新的策略行。

3.10 - 使用 Node 鉴权

节点鉴权是一种特殊用途的鉴权模式,专门对 kubelet 发出的 API 请求进行授权。

概述

节点鉴权器允许 kubelet 执行 API 操作。包括:

读取操作:

  • services
  • endpoints
  • nodes
  • pods
  • 与绑定到 kubelet 节点的 Pod 相关的 Secret、ConfigMap、PersistentVolumeClaim 和持久卷

写入操作:

  • 节点和节点状态(启用 NodeRestriction 准入插件以限制 kubelet 只能修改自己的节点)
  • Pod 和 Pod 状态 (启用 NodeRestriction 准入插件以限制 kubelet 只能修改绑定到自身的 Pod)
  • 事件

身份认证与鉴权相关的操作:

  • 对于基于 TLS 的启动引导过程时使用的 certificationsigningrequests API 的读/写权限
  • 为委派的身份验证/鉴权检查创建 TokenReview 和 SubjectAccessReview 的能力

在将来的版本中,节点鉴权器可能会添加或删除权限,以确保 kubelet 具有正确操作所需的最小权限集。

为了获得节点鉴权器的授权,kubelet 必须使用一个凭证以表示它在 system:nodes 组中,用户名为 system:node:<nodeName>。上述的组名和用户名格式要与 kubelet TLS 启动引导 过程中为每个 kubelet 创建的标识相匹配。

<nodeName> 的值必须与 kubelet 注册的节点名称精确匹配。默认情况下,节点名称是由 hostname 提供的主机名,或者通过 kubelet --hostname-override 选项 覆盖。 但是,当使用 --cloud-provider kubelet 选项时,具体的主机名可能由云提供商确定, 忽略本地的 hostname--hostname-override 选项。有关 kubelet 如何确定主机名的详细信息,请参阅 kubelet 选项参考

要启用节点鉴权器,请使用 --authorization-mode=Node 启动 API 服务器。

要限制 kubelet 可以写入的 API 对象,请使用 --enable-admission-plugins=...,NodeRestriction,... 启动 API 服务器,从而启用 NodeRestriction 准入插件。

迁移考虑因素

system:nodes 组之外的 kubelet

system:nodes 组之外的 kubelet 不会被 Node 鉴权模式授权,并且需要继续通过当前授权它们的机制来授权。 节点准入插件不会限制来自这些 kubelet 的请求。

具有无差别用户名的 kubelet

在一些部署中,kubelet 具有 system:nodes 组的凭证, 但是无法给出它们所关联的节点的标识,因为它们没有 system:node:... 格式的用户名。 这些 kubelet 不会被 Node 鉴权模式授权,并且需要继续通过当前授权它们的任何机制来授权。

因为默认的节点标识符实现不会把它当作节点身份标识,NodeRestriction 准入插件会忽略来自这些 kubelet 的请求。

相对于以前使用 RBAC 的版本的更新

升级的 1.7 之前的使用 RBAC 的集群将继续按原样运行,因为 system:nodes 组绑定已经存在。

如果集群管理员希望开始使用 Node 鉴权器和 NodeRestriction 准入插件来限制节点对 API 的访问,这一需求可以通过下列操作来完成且不会影响已部署的应用:

  1. 启用 Node 鉴权模式 (--authorization-mode=Node,RBAC) 和 NodeRestriction 准入插件
  2. 确保所有 kubelet 的凭据符合组/用户名要求
  3. 审核 API 服务器日志以确保 Node 鉴权器不会拒绝来自 kubelet 的请求(日志中没有持续的 NODE DENY 消息)
  4. 删除 system:node 集群角色绑定

RBAC 节点权限

在 1.6 版本中,当使用 RBAC 鉴权模式 时,system:nodes 集群角色会被自动绑定到 system:node 组。

在 1.7 版本中,不再推荐将 system:nodes 组自动绑定到 system:node 角色,因为节点鉴权器通过对 Secret 和 ConfigMap 访问的额外限制完成了相同的任务。 如果同时启用了 NodeRBAC 鉴权模式,1.7 版本则不会创建 system:nodes 组到 system:node 角色的自动绑定。

在 1.8 版本中,绑定将根本不会被创建。

使用 RBAC 时,将继续创建 system:node 集群角色,以便与将其他用户或组绑定到该角色的部署方法兼容。

3.11 - 从 PodSecurityPolicy 映射到 Pod 安全性标准

下面的表格列举了 PodSecurityPolicy 对象上的配置参数,这些字段是否会变更或检查 Pod 配置,以及这些配置值如何映射到 Pod 安全性标准(Pod Security Standards) 之上。

对于每个可应用的参数,表格中给出了 BaselineRestricted 配置下可接受的取值。 对这两种配置而言不可接受的取值均归入 Privileged 配置下。“无意见”意味着对所有 Pod 安全性标准而言所有取值都可接受。

如果想要了解如何一步步完成迁移,可参阅从 PodSecurityPolicy 迁移到内置的 PodSecurity 准入控制器

PodSecurityPolicy 规约

下面表格中所列举的字段是 PodSecurityPolicySpec 的一部分,是通过 .spec 字段路径来设置的。

从 PodSecurityPolicySpec 字段映射到 Pod Security 标准
PodSecurityPolicySpec类型Pod 安全性标准中对应设置
privileged检查性质Baseline & Restricted: false / 未定义 / nil
defaultAddCapabilities更改性质 & 检查性质需求满足下面的 allallowedCapabilities
allowedCapabilities检查性质

Baseline:下面各项的子集

  • AUDIT_WRITE
  • CHOWN
  • DAC_OVERRIDE
  • FOWNER
  • FSETID
  • KILL
  • MKNOD
  • NET_BIND_SERVICE
  • SETFCAP
  • SETGID
  • SETPCAP
  • SETUID
  • SYS_CHROOT

Restricted:空 / 未定义 / nil 或包含 NET_BIND_SERVICE 的列表

requiredDropCapabilities更改性质 & 检查性质

Baseline:无意见

Restricted:必须包含 ALL

volumes检查性质

Baseline除下列取值之外的任何值

  • hostPath
  • *

Restricted:下列取值的子集

  • configMap
  • csi
  • downwardAPI
  • emptyDir
  • ephemeral
  • persistentVolumeClaim
  • projected
  • secret
hostNetwork检查性质Baseline & Restrictedfalse / 未定义 / nil
hostPorts检查性质Baseline & Restricted:未定义 / nil / 空
hostPID检查性质Baseline & Restrictedfalse / 未定义 / nil
hostIPC检查性质Baseline & Restrictedfalse / 未定义 / nil
seLinux更改性质 & 检查性质

Baseline & RestrictedseLinux.ruleMustRunAs,且 options 如下:

  • user 未设置("" / 未定义 / nil)
  • role 未设置("" / 未定义 / nil)
  • type 未设置或者取值为 container_tcontainer_init_tcontainer_kvm_t 之一
  • level 是任何取值
runAsUser变更性质 & 检查性质

Baseline:任何取值

RestrictedruleMustRunAsNonRoot

runAsGroup变更性质(MustRunAs)& 检查性质无意见
supplementalGroups变更性质 & 检查性质无意见
fsGroup变更性质 & 验证性质无意见
readOnlyRootFilesystem变更性质 & 检查性质无意见
defaultAllowPrivilegeEscalation变更性质无意见(非变更性质)
allowPrivilegeEscalation变更性质 & 检查性质

只有设置为 false 时才执行变更动作

Baseline:无意见

Restrictedfalse

allowedHostPaths检查性质无意见(volumes 优先)
allowedFlexVolumes检查性质无意见(volumes 优先)
allowedCSIDrivers检查性质无意见(volumes 优先)
allowedUnsafeSysctls检查性质Baseline & Restricted:未定义 / nil / 空
forbiddenSysctls检查性质无意见
allowedProcMountTypes
(alpha feature)
检查性质Baseline & Restricted["Default"] 或者未定义 / nil / 空
runtimeClass
 .defaultRuntimeClassName
变更性质无意见
runtimeClass
 .allowedRuntimeClassNames
检查性质无意见

PodSecurityPolicy 注解

下面表格中所列举的注解可以通过 .metadata.annotations 设置到 PodSecurityPolicy 对象之上。

将 PodSecurityPolicy 注解映射到 Pod 安全性标准
PSP 注解类型Pod 安全性标准中对应设置
seccomp.security.alpha.kubernetes.io
/defaultProfileName
变更性质无意见
seccomp.security.alpha.kubernetes.io
/allowedProfileNames
检查性质

Baseline"runtime/default," (其中尾部的逗号允许取消设置)

Restricted"runtime/default" (没有尾部逗号)

localhost/* 取值对于 Baseline 和 Restricted 都是可接受的

apparmor.security.beta.kubernetes.io
/defaultProfileName
变更性质无意见
apparmor.security.beta.kubernetes.io
/allowedProfileNames
检查性质

Baseline"runtime/default," (其中尾部的逗号允许取消设置)

Restricted"runtime/default" (没有尾部逗号)

localhost/* 取值对于 Baseline 和 Restricted 都是可接受的

3.12 - Webhook 模式

WebHook 是一种 HTTP 回调:某些条件下触发的 HTTP POST 请求;通过 HTTP POST 发送的简单事件通知。一个基于 web 应用实现的 WebHook 会在特定事件发生时把消息发送给特定的 URL。

具体来说,当在判断用户权限时,Webhook 模式会使 Kubernetes 查询外部的 REST 服务。

配置文件格式

Webhook 模式需要一个 HTTP 配置文件,通过 --authorization-webhook-config-file=SOME_FILENAME 的参数声明。

配置文件的格式使用 kubeconfig。 在该文件中,“users” 代表着 API 服务器的 webhook,而 “cluster” 代表着远程服务。

使用 HTTPS 客户端认证的配置例子:

# Kubernetes API 版本
apiVersion: v1
# API 对象种类
kind: Config
# clusters 代表远程服务。
clusters:
  - name: name-of-remote-authz-service
    cluster:
      # 对远程服务进行身份认证的 CA。
      certificate-authority: /path/to/ca.pem
      # 远程服务的查询 URL。必须使用 'https'。
      server: https://authz.example.com/authorize

# users 代表 API 服务器的 webhook 配置
users:
  - name: name-of-api-server
    user:
      client-certificate: /path/to/cert.pem # webhook plugin 使用 cert
      client-key: /path/to/key.pem          # cert 所对应的 key

# kubeconfig 文件必须有 context。需要提供一个给 API 服务器。
current-context: webhook
contexts:
- context:
    cluster: name-of-remote-authz-service
    user: name-of-api-server
  name: webhook

请求载荷

在做认证决策时,API 服务器会 POST 一个 JSON 序列化的 authorization.k8s.io/v1beta1 SubjectAccessReview 对象来描述这个动作。这个对象包含了描述用户请求的字段,同时也包含了需要被访问资源或请求特征的具体信息。

需要注意的是 webhook API 对象与其他 Kubernetes API 对象一样都同样都遵从版本兼容规则。 实施人员应该了解 beta 对象的更宽松的兼容性承诺,同时确认请求的 "apiVersion" 字段能被正确地反序列化。 此外,API 服务器还必须启用 authorization.k8s.io/v1beta1 API 扩展组 (--runtime-config=authorization.k8s.io/v1beta1=true)。

一个请求内容的例子:

{
  "apiVersion": "authorization.k8s.io/v1beta1",
  "kind": "SubjectAccessReview",
  "spec": {
    "resourceAttributes": {
      "namespace": "kittensandponies",
      "verb": "get",
      "group": "unicorn.example.org",
      "resource": "pods"
    },
    "user": "jane",
    "group": [
      "group1",
      "group2"
    ]
  }
}

期待远程服务填充请求的 status 字段并响应允许或禁止访问。响应主体的 spec 字段被忽略,可以省略。允许的响应将返回:

{
  "apiVersion": "authorization.k8s.io/v1beta1",
  "kind": "SubjectAccessReview",
  "status": {
    "allowed": true
  }
}

为了禁止访问,有两种方法。

在大多数情况下,第一种方法是首选方法,它指示授权 webhook 不允许或对请求 “无意见”。 但是,如果配置了其他授权者,则可以给他们机会允许请求。如果没有其他授权者,或者没有一个授权者,则该请求被禁止。webhook 将返回:

{
  "apiVersion": "authorization.k8s.io/v1beta1",
  "kind": "SubjectAccessReview",
  "status": {
    "allowed": false,
    "reason": "user does not have read access to the namespace"
  }
}

第二种方法立即拒绝其他配置的授权者进行短路评估。仅应由对集群的完整授权者配置有详细了解的 webhook 使用。webhook 将返回:

{
  "apiVersion": "authorization.k8s.io/v1beta1",
  "kind": "SubjectAccessReview",
  "status": {
    "allowed": false,
    "denied": true,
    "reason": "user does not have read access to the namespace"
  }
}

对于非资源的路径访问是这么发送的:

{
  "apiVersion": "authorization.k8s.io/v1beta1",
  "kind": "SubjectAccessReview",
  "spec": {
    "nonResourceAttributes": {
      "path": "/debug",
      "verb": "get"
    },
    "user": "jane",
    "group": [
      "group1",
      "group2"
    ]
  }
}

非资源类的路径包括:/api/apis/metrics/logs/debug/healthz/livez/openapi/v2/readyz、和 /version。 客户端需要访问 /api/api/*/apis/apis/*/version 以便 能发现服务器上有什么资源和版本。对于其他非资源类的路径访问在没有 REST API 访问限制的情况下拒绝。

更多信息可以参考 authorization.v1beta1 API 对象和 webhook.go

3.13 - Kubelet 认证/鉴权

概述

kubelet 的 HTTPS 端点公开了 API, 这些 API 可以访问敏感度不同的数据, 并允许你在节点上和容器内以不同级别的权限执行操作。

本文档介绍了如何对 kubelet 的 HTTPS 端点的访问进行认证和鉴权。

Kubelet 身份认证

默认情况下,未被已配置的其他身份认证方法拒绝的对 kubelet 的 HTTPS 端点的请求会被视为匿名请求, 并被赋予 system:anonymous 用户名和 system:unauthenticated 组。

要禁用匿名访问并向未经身份认证的请求发送 401 Unauthorized 响应,请执行以下操作:

  • --anonymous-auth=false 标志启动 kubelet

要对 kubelet 的 HTTPS 端点启用 X509 客户端证书认证:

  • --client-ca-file 标志启动 kubelet,提供一个 CA 证书包以供验证客户端证书
  • --kubelet-client-certificate--kubelet-client-key 标志启动 API 服务器
  • 有关更多详细信息,请参见 API 服务器身份验证文档

要启用 API 持有者令牌(包括服务帐户令牌)以对 kubelet 的 HTTPS 端点进行身份验证,请执行以下操作:

  • 确保在 API 服务器中启用了 authentication.k8s.io/v1beta1 API 组
  • --authentication-token-webhook--kubeconfig 标志启动 kubelet
  • kubelet 调用已配置的 API 服务器上的 TokenReview API,以根据持有者令牌确定用户信息

Kubelet 鉴权

任何成功通过身份验证的请求(包括匿名请求)之后都会被鉴权。 默认的鉴权模式为 AlwaysAllow,它允许所有请求。

细分对 kubelet API 的访问权限可能有多种原因:

  • 启用了匿名身份验证,但是应限制匿名用户调用 kubelet API 的能力
  • 启用了持有者令牌认证,但应限制任意 API 用户(如服务帐户)调用 kubelet API 的能力
  • 启用了客户端证书身份验证,但仅应允许已配置的 CA 签名的某些客户端证书使用 kubelet API

要细分对 kubelet API 的访问权限,请将鉴权委派给 API 服务器:

  • 确保在 API 服务器中启用了 authorization.k8s.io/v1beta1 API 组
  • --authorization-mode=Webhook--kubeconfig 标志启动 kubelet
  • kubelet 调用已配置的 API 服务器上的 SubjectAccessReview API, 以确定每个请求是否得到鉴权

kubelet 使用与 API 服务器相同的 请求属性 方法对 API 请求执行鉴权。

请求的动词根据传入请求的 HTTP 动词确定:

HTTP 动词请求动词
POSTcreate
GET, HEADget
PUTupdate
PATCHpatch
DELETEdelete

资源和子资源是根据传入请求的路径确定的:

Kubelet API资源子资源
/stats/*nodesstats
/metrics/*nodesmetrics
/logs/*nodeslog
/spec/*nodesspec
其它所有nodesproxy

名字空间和 API 组属性始终是空字符串, 资源名称始终是 kubelet 的 Node API 对象的名称。

在此模式下运行时,请确保传递给 API 服务器的由 --kubelet-client-certificate--kubelet-client-key 标志标识的用户具有以下属性的鉴权:

  • verb=*, resource=nodes, subresource=proxy
  • verb=*, resource=nodes, subresource=stats
  • verb=*, resource=nodes, subresource=log
  • verb=*, resource=nodes, subresource=spec
  • verb=*, resource=nodes, subresource=metrics

3.14 - TLS 启动引导

在一个 Kubernetes 集群中,工作节点上的组件(kubelet 和 kube-proxy)需要与 Kubernetes 控制平面组件通信,尤其是 kube-apiserver。 为了确保通信本身是私密的、不被干扰,并且确保集群的每个组件都在与另一个可信的组件通信, 我们强烈建议使用节点上的客户端 TLS 证书。

启动引导这些组件的正常过程,尤其是需要证书来与 kube-apiserver 安全通信的工作节点, 可能会是一个具有挑战性的过程,因为这一过程通常不受 Kubernetes 控制,需要不少额外工作。 这也使得初始化或者扩缩一个集群的操作变得具有挑战性。

为了简化这一过程,从 1.4 版本开始,Kubernetes 引入了一个证书请求和签名 API。 该提案可在这里看到。

本文档描述节点初始化的过程,如何为 kubelet 配置 TLS 客户端证书启动引导, 以及其背后的工作原理。

初始化过程

当工作节点启动时,kubelet 执行以下操作:

  1. 寻找自己的 kubeconfig 文件
  2. 检索 API 服务器的 URL 和凭据,通常是来自 kubeconfig 文件中的 TLS 密钥和已签名证书
  3. 尝试使用这些凭据来与 API 服务器通信

假定 kube-apiserver 成功地认证了 kubelet 的凭据数据,它会将 kubelet 视为一个合法的节点并开始将 Pod 分派给该节点。

注意,签名的过程依赖于:

  • kubeconfig 中包含密钥和本地主机的证书
  • 证书被 kube-apiserver 所信任的一个证书机构(CA)所签名

负责部署和管理集群的人有以下责任:

  1. 创建 CA 密钥和证书
  2. 将 CA 证书发布到 kube-apiserver 运行所在的控制平面节点上
  3. 为每个 kubelet 创建密钥和证书;强烈建议为每个 kubelet 使用独一无二的、 CN 取值与众不同的密钥和证书
  4. 使用 CA 密钥对 kubelet 证书签名
  5. 将 kubelet 密钥和签名的证书发布到 kubelet 运行所在的特定节点上

本文中描述的 TLS 启动引导过程有意简化甚至完全自动化上述过程, 尤其是第三步之后的操作,因为这些步骤是初始化或者扩缩集群时最常见的操作。

启动引导初始化

在启动引导初始化过程中,会发生以下事情:

  1. kubelet 启动
  2. kubelet 看到自己没有对应的 kubeconfig 文件
  3. kubelet 搜索并发现 bootstrap-kubeconfig 文件
  4. kubelet 读取该启动引导文件,从中获得 API 服务器的 URL 和用途有限的一个“令牌(Token)”
  5. kubelet 建立与 API 服务器的连接,使用上述令牌执行身份认证
  6. kubelet 现在拥有受限制的凭据来创建和取回证书签名请求(CSR)
  7. kubelet 为自己创建一个 CSR,并将其 signerName 设置为 kubernetes.io/kube-apiserver-client-kubelet
  8. CSR 被以如下两种方式之一批复:
  • 如果配置了,kube-controller-manager 会自动批复该 CSR
  • 如果配置了,一个外部进程,或者是人,使用 Kubernetes API 或者使用 kubectl 来批复该 CSR
  1. kubelet 所需要的证书被创建
  1. 证书被发放给 kubelet
  2. kubelet 取回该证书
  3. kubelet 创建一个合适的 kubeconfig,其中包含密钥和已签名的证书
  4. kubelet 开始正常操作
  5. 可选地,如果配置了,kubelet 在证书接近于过期时自动请求更新证书
  6. 更新的证书被批复并发放;取决于配置,这一过程可能是自动的或者手动完成

本文的其余部分描述配置 TLS 启动引导的必要步骤及其局限性。

配置

要配置 TLS 启动引导及可选的自动批复,你必须配置以下组件的选项:

  • kube-apiserver
  • kube-controller-manager
  • kubelet
  • 集群内的资源:ClusterRoleBinding 以及可能需要的 ClusterRole

此外,你需要有 Kubernetes 证书机构(Certificate Authority,CA)。

证书机构

就像在没有 TLS 启动引导支持的情况下,你会需要证书机构(CA)密钥和证书。 这些数据会被用来对 kubelet 证书进行签名。 如前所述,将证书机构密钥和证书发布到控制平面节点是你的责任。

就本文而言,我们假定这些数据被发布到控制平面节点上的 /var/lib/kubernetes/ca.pem(证书)和 /var/lib/kubernetes/ca-key.pem(密钥)文件中。 我们将这两个文件称作“Kubernetes CA 证书和密钥”。 所有 Kubernetes 组件(kubelet、kube-apiserver、kube-controller-manager) 都使用这些凭据,并假定这里的密钥和证书都是 PEM 编码的。

kube-apiserver 配置

启用 TLS 启动引导对 kube-apiserver 有若干要求:

  • 能够识别对客户端证书进行签名的 CA
  • 能够对启动引导的 kubelet 执行身份认证,并将其置入 system:bootstrappers
  • 能够对启动引导的 kubelet 执行鉴权操作,允许其创建证书签名请求(CSR)

识别客户证书

对于所有客户端证书的认证操作而言,这是很常见的。 如果还没有设置,要为 kube-apiserver 命令添加 --client-ca-file=FILENAME 标志来启用客户端证书认证,在标志中引用一个包含用来签名的证书的证书机构包, 例如:--client-ca-file=/var/lib/kubernetes/ca.pem

初始启动引导认证

为了让启动引导的 kubelet 能够连接到 kube-apiserver 并请求证书, 它必须首先在服务器上认证自身身份。你可以使用任何一种能够对 kubelet 执行身份认证的身份认证组件

尽管所有身份认证策略都可以用来对 kubelet 的初始启动凭据来执行认证, 出于容易准备的因素,建议使用如下两个身份认证组件:

  1. 启动引导令牌(Bootstrap Token)
  2. 令牌认证文件

启动引导令牌是一种对 kubelet 进行身份认证的方法,相对简单且容易管理, 且不需要在启动 kube-apiserver 时设置额外的标志。

无论选择哪种方法,这里的需求是 kubelet 能够被身份认证为某个具有如下权限的用户:

  1. 创建和读取 CSR
  2. 在启用了自动批复时,能够在请求节点客户端证书时得到自动批复

使用启动引导令牌执行身份认证的 kubelet 会被认证为 system:bootstrappers 组中的用户。这是使用启动引导令牌的一种标准方法。

随着这个功能特性的逐渐成熟,你需要确保令牌绑定到某基于角色的访问控制(RBAC) 策略上,从而严格限制请求(使用启动引导令牌) 仅限于客户端申请提供证书。当 RBAC 被配置启用时,可以将令牌限制到某个组, 从而提高灵活性。例如,你可以在准备节点期间禁止某特定启动引导组的访问。

启动引导令牌

启动引导令牌的细节在这里 详述。启动引导令牌在 Kubernetes 集群中存储为 Secret 对象,被发放给各个 kubelet。 你可以在整个集群中使用同一个令牌,也可以为每个节点发放单独的令牌。

这一过程有两个方面:

  1. 基于令牌 ID、机密数据和范畴信息创建 Kubernetes Secret
  2. 将令牌发放给 kubelet

从 kubelet 的角度,所有令牌看起来都很像,没有特别的含义。 从 kube-apiserver 服务器的角度,启动引导令牌是很特殊的。 根据其 typenamespacename,kube-apiserver 能够将其认作特殊的令牌, 并授予携带该令牌的任何人特殊的启动引导权限,换言之,将其视为 system:bootstrappers 组的成员。这就满足了 TLS 启动引导的基本需求。

关于创建 Secret 的进一步细节可访问这里

如果你希望使用启动引导令牌,你必须在 kube-apiserver 上使用下面的标志启用之:

--enable-bootstrap-token-auth=true

令牌认证文件

kube-apiserver 能够将令牌视作身份认证依据。 这些令牌可以是任意数据,但必须表示为基于某安全的随机数生成器而得到的至少 128 位混沌数据。这里的随机数生成器可以是现代 Linux 系统上的 /dev/urandom。生成令牌的方式有很多种。例如:

head -c 16 /dev/urandom | od -An -t x | tr -d ' '

上面的命令会生成类似于 02b50b05283e98dd0fd71db496ef01e8 这样的令牌。

令牌文件看起来是下面的例子这样,其中前面三个值可以是任何值, 用引号括起来的组名称则只能用例子中给的值。

02b50b05283e98dd0fd71db496ef01e8,kubelet-bootstrap,10001,"system:bootstrappers"

向 kube-apiserver 添加 --token-auth-file=FILENAME 标志(或许这要对 systemd 的单元文件作修改)以启用令牌文件。有关进一步细节的文档, 可参见这里

授权 kubelet 创建 CSR

现在启动引导节点被 身份认证system:bootstrapping 组的成员, 它需要被 授权 创建证书签名请求(CSR)并在证书被签名之后将其取回。 幸运的是,Kubernetes 提供了一个 ClusterRole,其中精确地封装了这些许可, system:node-bootstrapper

为了实现这一点,你只需要创建 ClusterRoleBinding,将 system:bootstrappers 组绑定到集群角色 system:node-bootstrapper

# 允许启动引导节点创建 CSR
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: create-csrs-for-bootstrapping
subjects:
- kind: Group
  name: system:bootstrappers
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: system:node-bootstrapper
  apiGroup: rbac.authorization.k8s.io

kube-controller-manager 配置

API 服务器从 kubelet 收到证书请求并对这些请求执行身份认证, 但真正负责发放签名证书的是控制器管理器(controller-manager)。

控制器管理器通过一个证书发放的控制回路来执行此操作。该操作的执行方式是使用磁盘上的文件用 cfssl 本地签名组件来完成。 目前,所发放的所有证书都有一年的有效期,并设定了默认的一组密钥用法。

为了让控制器管理器对证书签名,它需要:

  • 能够访问你之前所创建并分发的 “Kubernetes CA 密钥和证书”
  • 启用 CSR 签名

访问密钥和证书

如前所述,你需要创建一个 Kubernetes CA 密钥和证书,并将其发布到控制平面节点。 这些数据会被控制器管理器来对 kubelet 证书进行签名。

由于这些被签名的证书反过来会被 kubelet 用来在 kube-apiserver 执行普通的 kubelet 身份认证,很重要的一点是为控制器管理器所提供的 CA 也被 kube-apiserver 信任用来执行身份认证。CA 密钥和证书是通过 kube-apiserver 的标志 --client-ca-file=FILENAME(例如,--client-ca-file=/var/lib/kubernetes/ca.pem), 来设定的,正如 kube-apiserver 配置节所述。

要将 Kubernetes CA 密钥和证书提供给 kube-controller-manager,可使用以下标志:

--cluster-signing-cert-file="/etc/path/to/kubernetes/ca/ca.crt" --cluster-signing-key-file="/etc/path/to/kubernetes/ca/ca.key"

例如:

--cluster-signing-cert-file="/var/lib/kubernetes/ca.pem" --cluster-signing-key-file="/var/lib/kubernetes/ca-key.pem"

所签名的证书的合法期限可以通过下面的标志来配置:

--cluster-signing-duration

批复

为了对 CSR 进行批复,你需要告诉控制器管理器批复这些 CSR 是可接受的。 这是通过将 RBAC 访问权限授予正确的组来实现的。

许可权限有两组:

  • nodeclient:如果节点在为节点创建新的证书,则该节点还没有证书。 该节点使用前文所列的令牌之一来执行身份认证,因此是组 system:bootstrappers 组的成员。
  • selfnodeclient:如果节点在对证书执行续期操作,则该节点已经拥有一个证书。 节点持续使用现有的证书将自己认证为 system:nodes 组的成员。

要允许 kubelet 请求并接收新的证书,可以创建一个 ClusterRoleBinding 将启动引导节点所处的组 system:bootstrappers 绑定到为其赋予访问权限的 ClusterRole system:certificates.k8s.io:certificatesigningrequests:nodeclient

# 批复 "system:bootstrappers" 组的所有 CSR
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: auto-approve-csrs-for-group
subjects:
- kind: Group
  name: system:bootstrappers
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: system:certificates.k8s.io:certificatesigningrequests:nodeclient
  apiGroup: rbac.authorization.k8s.io

要允许 kubelet 对其客户端证书执行续期操作,可以创建一个 ClusterRoleBinding 将正常工作的节点所处的组 system:nodes 绑定到为其授予访问许可的 ClusterRole system:certificates.k8s.io:certificatesigningrequests:selfnodeclient

# 批复 "system:nodes" 组的 CSR 续约请求
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: auto-approve-renewals-for-nodes
subjects:
- kind: Group
  name: system:nodes
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: system:certificates.k8s.io:certificatesigningrequests:selfnodeclient
  apiGroup: rbac.authorization.k8s.io

作为 kube-controller-manager 的一部分的 csrapproving 控制器是自动被启用的。 该控制器使用 SubjectAccessReview API 来确定给定用户是否被授权请求 CSR,之后基于鉴权结果执行批复操作。 为了避免与其它批复组件发生冲突,内置的批复组件不会显式地拒绝任何 CSRs。 该组件仅是忽略未被授权的请求。 控制器也会作为垃圾收集的一部分清除已过期的证书。

kubelet 配置

最后,当控制平面节点被正确配置并且所有必要的身份认证和鉴权机制都就绪时, 我们可以配置 kubelet。

kubelet 需要以下配置来执行启动引导:

  • 一个用来存储所生成的密钥和证书的路径(可选,可以使用默认配置)
  • 一个用来指向尚不存在的 kubeconfig 文件的路径;kubelet 会将启动引导配置文件放到这个位置
  • 一个指向启动引导 kubeconfig 文件的路径,用来提供 API 服务器的 URL 和启动引导凭据, 例如,启动引导令牌
  • 可选的:轮换证书的指令

启动引导 kubeconfig 文件应该放在一个 kubelet 可访问的路径下,例如 /var/lib/kubelet/bootstrap-kubeconfig

其格式与普通的 kubeconfig 文件完全相同。实例文件可能看起来像这样:

apiVersion: v1
kind: Config
clusters:
- cluster:
    certificate-authority: /var/lib/kubernetes/ca.pem
    server: https://my.server.example.com:6443
  name: bootstrap
contexts:
- context:
    cluster: bootstrap
    user: kubelet-bootstrap
  name: bootstrap
current-context: bootstrap
preferences: {}
users:
- name: kubelet-bootstrap
  user:
    token: 07401b.f395accd246ae52d

需要额外注意的一些因素有:

  • certificate-authority:指向 CA 文件的路径,用来对 kube-apiserver 所出示的服务器证书进行验证
  • server:用来访问 kube-apiserver 的 URL
  • token:要使用的令牌

令牌的格式并不重要,只要它与 kube-apiserver 的期望匹配即可。 在上面的例子中,我们使用的是启动引导令牌。 如前所述,任何合法的身份认证方法都可以使用,不限于令牌。

因为启动引导 kubeconfig 文件是一个标准的 kubeconfig 文件,你可以使用 kubectl 来生成该文件。要生成上面的示例文件:

kubectl config --kubeconfig=/var/lib/kubelet/bootstrap-kubeconfig set-cluster bootstrap --server='https://my.server.example.com:6443' --certificate-authority=/var/lib/kubernetes/ca.pem
kubectl config --kubeconfig=/var/lib/kubelet/bootstrap-kubeconfig set-credentials kubelet-bootstrap --token=07401b.f395accd246ae52d
kubectl config --kubeconfig=/var/lib/kubelet/bootstrap-kubeconfig set-context bootstrap --user=kubelet-bootstrap --cluster=bootstrap
kubectl config --kubeconfig=/var/lib/kubelet/bootstrap-kubeconfig use-context bootstrap

要指示 kubelet 使用启动引导 kubeconfig 文件,可以使用下面的 kubelet 标志:

--bootstrap-kubeconfig="/var/lib/kubelet/bootstrap-kubeconfig" --kubeconfig="/var/lib/kubelet/kubeconfig"

在启动 kubelet 时,如果 --kubeconfig 标志所指定的文件并不存在,会使用通过标志 --bootstrap-kubeconfig 所指定的启动引导 kubeconfig 配置来向 API 服务器请求客户端证书。 在证书请求被批复并被 kubelet 收回时,一个引用所生成的密钥和所获得证书的 kubeconfig 文件会被写入到通过 --kubeconfig 所指定的文件路径下。 证书和密钥文件会被放到 --cert-dir 所指定的目录中。

客户和服务证书

前文所述的内容都与 kubelet 客户端证书相关,尤其是 kubelet 用来向 kube-apiserver 认证自身身份的证书。

kubelet 也可以使用 服务(Serving) 证书。kubelet 自身向外提供一个 HTTPS 末端, 包含若干功能特性。要保证这些末端的安全性,kubelet 可以执行以下操作之一:

  • 使用通过 --tls-private-key-file--tls-cert-file 所设置的密钥和证书
  • 如果没有提供密钥和证书,则创建自签名的密钥和证书
  • 通过 CSR API 从集群服务器请求服务证书

TLS 启动引导所提供的客户端证书默认被签名为仅用于 client auth(客户端认证), 因此不能作为提供服务的证书,或者 server auth

不过,你可以启用服务器证书,至少可以部分地通过证书轮换来实现这点。

证书轮换

Kubernetes v1.8 和更高版本的 kubelet 实现了对客户端证书与/或服务证书进行轮换 这一 Beta 特性。这一特性通过 kubelet 对应的 RotateKubeletClientCertificateRotateKubeletServerCertificate 特性门控标志来控制,并且是默认启用的。

RotateKubeletClientCertificate 会导致 kubelet 在其现有凭据即将过期时通过创建新的 CSR 来轮换其客户端证书。要启用此功能特性,可将下面的标志传递给 kubelet:

--rotate-certificates

RotateKubeletServerCertificate 会让 kubelet 在启动引导其客户端凭据之后请求一个服务证书 对该服务证书执行轮换操作。要启用此功能特性,将下面的标志传递给 kubelet:

--rotate-server-certificates

其它身份认证组件

本文所描述的所有 TLS 启动引导内容都与 kubelet 相关。不过,其它组件也可能需要直接与 kube-apiserver 直接通信。容易想到的是 kube-proxy,同样隶属于 Kubernetes 的节点组件并且运行在所有节点之上,不过也可能包含一些其它负责监控或者联网的组件。

与 kubelet 类似,这些其它组件也需要一种向 kube-apiserver 认证身份的方法。 你可以用几种方法来生成这类凭据:

  • 较老的方式:和 kubelet 在 TLS 启动引导之前所做的一样,用类似的方式创建和分发证书。
  • DaemonSet:由于 kubelet 自身被加载到所有节点之上,并且有足够能力来启动基本服务, 你可以运行将 kube-proxy 和其它特定节点的服务作为 kube-system 名字空间中的 DaemonSet 来执行,而不是独立的进程。由于 DaemonSet 位于集群内部, 你可以为其指派一个合适的服务账户,使之具有适当的访问权限来完成其使命。 这也许是配置此类服务的最简单的方法。

kubectl 批复

CSRs 可以在控制器管理其内置的批复工作流之外被批复。

签名控制器并不会立即对所有证书请求执行签名操作。相反, 它会等待这些请求被某具有适当特权的用户标记为 “Approved(已批准)”状态。 这一流程有意允许由外部批复控制器来自动执行的批复, 或者由控制器管理器内置的批复控制器来自动批复。 不过,集群管理员也可以使用 kubectl 来手动批准证书请求。 管理员可以通过 kubectl get csr 来列举所有的 CSR,使用 kubectl descsribe csr <name> 来描述某个 CSR 的细节。 管理员可以使用 kubectl certificate approve <name 来批准某 CSR,或者 kubectl certificate deny <name> 来拒绝某 CSR。

4 - Kubernetes API

Kubernetes API 是通过 RESTful 接口提供 Kubernetes 功能服务并负责集群状态存储的应用程序。

Kubernetes 资源和"意向记录"都是作为 API 对象储存的,并可以通过调用 RESTful 风格的 API 进行修改。 API 允许以声明方式管理配置。 用户可以直接和 Kubernetes API 交互,也可以通过 kubectl 这样的工具进行交互。 核心的 Kubernetes API 是很灵活的,可以扩展以支持定制资源。

4.1 - 工作负载资源

4.1.1 - Pod

Pod 是可以在主机上运行的容器的集合。

apiVersion: v1

import "k8s.io/api/core/v1"

Pod

Pod 是可以在主机上运行的容器的集合。此资源由客户端创建并调度到主机上。


  • apiVersion: v1

  • kind: Pod

PodSpec

PodSpec 是对 Pod 的描述。


容器

  • containers ([]Container),必需

    补丁策略:基于 name 键合并

    属于 Pod 的容器列表。当前无法添加或删除容器。Pod 中必须至少有一个容器。无法更新。

  • initContainers ([]Container)

    补丁策略:基于 name 键合并

    属于 Pod 的 Init 容器列表。Init 容器在容器启动之前按顺序执行。 如果任何一个 Init 容器发生故障,则认为该 Pod 失败,并根据其 restartPolicy 处理。 Init 容器或普通容器的名称在所有容器中必须是唯一的。 Init 容器不可以有生命周期操作、就绪态探针、存活态探针或启动探针。 在调度过程中会考虑 Init 容器的资源需求,方法是查找每种资源类型的最高请求/限制, 然后使用该值的最大值或正常容器的资源请求的总和。 对资源限制以类似的方式应用于 Init 容器。当前无法添加或删除 Init 容器。无法更新。更多信息: https://kubernetes.io/zh-cn/docs/concepts/workloads/pods/init-containers/

  • ephemeralContainers ([]EphemeralContainer)

    补丁策略:基于 name 键合并

    在此 Pod 中运行的临时容器列表。临时容器可以在现有的 Pod 中运行,以执行用户发起的操作,例如调试。 此列表在创建 Pod 时不能指定,也不能通过更新 Pod 规约来修改。 要将临时容器添加到现有 Pod,请使用 Pod 的 ephemeralcontainers 子资源。

  • enableServiceLinks (boolean)

    enableServiceLinks 指示是否应将有关服务的信息注入到 Pod 的环境变量中,服务连接的语法与 Docker links 的语法相匹配。可选。默认为 true。

  • os (PodOS)

    指定 Pod 中容器的操作系统。如果设置了此属性,则某些 Pod 和容器字段会受到限制。

    如果 os 字段设置为 linux,则必须不能设置以下字段:

    • securityContext.windowsOptions

    如果 os 字段设置为 windows,则必须不能设置以下字段:

    • spec.hostPID
    • spec.hostIPC
    • spec.hostUsers
    • spec.securityContext.seLinuxOptions
    • spec.securityContext.seccompProfile
    • spec.securityContext.fsGroup
    • spec.securityContext.fsGroupChangePolicy
    • spec.securityContext.sysctls
    • spec.shareProcessNamespace
    • spec.securityContext.runAsUser
    • spec.securityContext.runAsGroup
    • spec.securityContext.supplementalGroups
    • spec.containers[*].securityContext.seLinuxOptions
    • spec.containers[*].securityContext.seccompProfile
    • spec.containers[*].securityContext.capabilities
    • spec.containers[*].securityContext.readOnlyRootFilesystem
    • spec.containers[*].securityContext.privileged
    • spec.containers[*].securityContext.allowPrivilegeEscalation
    • spec.containers[*].securityContext.procMount
    • spec.containers[*].securityContext.runAsUser
    • spec.containers[*].securityContext.runAsGroup

    PodOS 定义一个 Pod 的操作系统参数。

调度

  • nodeName (string)

    nodeName 是将此 Pod 调度到特定节点的请求。 如果字段值不为空,调度器只是直接将这个 Pod 调度到所指定节点上,假设节点符合资源要求。

  • affinity (Affinity)

    如果指定了,则作为 Pod 的调度约束。

    Affinity 是一组亲和性调度规则。

    • affinity.nodeAffinity (NodeAffinity)

      描述 Pod 的节点亲和性调度规则。

    • affinity.podAffinity (PodAffinity)

      描述 Pod 亲和性调度规则(例如,将此 Pod 与其他一些 Pod 放在同一节点、区域等)。

    • affinity.podAntiAffinity (PodAntiAffinity)

      描述 Pod 反亲和性调度规则(例如,避免将此 Pod 与其他一些 Pod 放在相同的节点、区域等)。

  • tolerations ([]Toleration)

    如果设置了此字段,则作为 Pod 的容忍度。

    这个 Toleration 所附加到的 Pod 能够容忍任何使用匹配运算符 <operator> 匹配三元组 <key,value,effect> 所得到的污点。

    • tolerations.key (string)

      key 是容忍度所适用的污点的键名。此字段为空意味着匹配所有的污点键。 如果 key 为空,则 operator 必须为 Exists;这种组合意味着匹配所有值和所有键。

    • tolerations.operator (string)

      operator 表示 key 与 value 之间的关系。有效的 operator 取值是 ExistsEqual。默认为 EqualExists 相当于 value 为某种通配符,因此 Pod 可以容忍特定类别的所有污点。

    • tolerations.value (string)

      value 是容忍度所匹配的污点值。如果 operator 为 Exists,则此 value 值应该为空, 否则 value 值应该是一个正常的字符串。

    • tolerations.effect (string)

      effect 指示要匹配的污点效果。空值意味著匹配所有污点效果。如果要设置此字段,允许的值为 NoSchedulePreferNoScheduleNoExecute 之一。

    • tolerations.tolerationSeconds (int64)

      tolerationSeconds 表示容忍度(effect 必须是 NoExecute,否则此字段被忽略)容忍污点的时间长度。 默认情况下,此字段未被设置,这意味着会一直能够容忍对应污点(不会发生驱逐操作)。 零值和负值会被系统当做 0 值处理(立即触发驱逐)。

  • schedulerName (string)

    如果设置了此字段,则 Pod 将由指定的调度器调度。如果未指定,则使用默认调度器来调度 Pod。

  • runtimeClassName (string)

    runtimeClassName 引用 node.k8s.io 组中的一个 RuntimeClass 对象,该 RuntimeClass 将被用来运行这个 Pod。 如果没有 RuntimeClass 资源与所设置的类匹配,则 Pod 将不会运行。 如果此字段未设置或为空,将使用 "旧版" RuntimeClass。 "旧版" RuntimeClass 可以视作一个隐式的运行时类,其定义为空,会使用默认运行时处理程序。 更多信息: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class

  • priorityClassName (string)

    如果设置了此字段,则用来标明 Pod 的优先级。 "system-node-critical""system-cluster-critical" 是两个特殊关键字, 分别用来表示两个最高优先级,前者优先级更高一些。 任何其他名称都必须通过创建具有该名称的 PriorityClass 对象来定义。 如果未指定此字段,则 Pod 优先级将为默认值。如果没有默认值,则为零。

  • priority (int32)

    优先级值。各种系统组件使用该字段来确定 Pod 的优先级。当启用 Priority 准入控制器时, 该控制器会阻止用户设置此字段。准入控制器基于 priorityClassName 设置来填充此字段。 字段值越高,优先级越高。

  • preemptionPolicy (string)

    preemptionPolicy 是用来抢占优先级较低的 Pod 的策略。取值为 "Never""PreemptLowerPriority" 之一。 如果未设置,则默认为 "PreemptLowerPriority"

  • topologySpreadConstraints ([]TopologySpreadConstraint)

    补丁策略:基于 topologyKey 键合并

    映射:topologyKey, whenUnsatisfiable 键组合的唯一值 將在合并期间保留

    TopologySpreadConstraints 描述一组 Pod 应该如何跨拓扑域来分布。调度器将以遵从此约束的方式来调度 Pod。 所有 topologySpreadConstraints 条目会通过逻辑与操作进行组合。

    TopologySpreadConstraint 指定如何在规定的拓扑下分布匹配的 Pod。

    • topologySpreadConstraints.maxSkew (int32),必需

      maxSkew 描述 Pod 可能分布不均衡的程度。当 whenUnsatisfiable=DoNotSchedule 时, 此字段值是目标拓扑中匹配的 Pod 数量与全局最小值之间的最大允许差值。 全局最小值是候选域中匹配 Pod 的最小数量,如果候选域的数量小于 minDomains,则为零。 例如,在一个包含三个可用区的集群中,maxSkew 设置为 1,具有相同 labelSelector 的 Pod 分布为 2/2/1: 在这种情况下,全局最小值为 1。

      | zone1 | zone2 | zone3 |
      | PP    | PP    |  P    |
      
      • 如果 maxSkew 为 1,传入的 Pod 只能调度到 "zone3",变成 2/2/2; 将其调度到 "zone1"("zone2")将使"zone1"("zone2")上的实际偏差(Actual Skew)为 3-1,进而违反 maxSkew 限制(1)。
      • 如果 maxSkew 为 2,则可以将传入的 Pod 调度到任何区域。

      whenUnsatisfiable=ScheduleAnyway 时,此字段被用来给满足此约束的拓扑域更高的优先级。

      此字段是一个必填字段。默认值为 1,不允许为 0。

    • topologySpreadConstraints.topologyKey (string),必需

      topologyKey 是节点标签的键名。如果节点的标签中包含此键名且键值亦相同,则被认为在相同的拓扑域中。 我们将每个 <键, 值> 视为一个 "桶(Bucket)",并尝试将数量均衡的 Pod 放入每个桶中。 我们定义域(Domain)为拓扑域的特定实例。 此外,我们定义一个候选域(Eligible Domain)为其节点与 nodeAffinityPolicy 和 nodeTaintsPolicy 的要求匹配的域。 例如,如果 topologyKey 是 "kubernetes.io/hostname",则每个 Node 都是该拓扑的域。 而如果 topologyKey 是 "topology.kubernetes.io/zone",则每个区域都是该拓扑的一个域。 这是一个必填字段。

    • topologySpreadConstraints.whenUnsatisfiable (string),必需

      whenUnsatisfiable 表示如果 Pod 不满足分布约束,如何处理它。

      • DoNotSchedule(默认):告诉调度器不要调度它。
      • ScheduleAnyway:告诉调度器将 Pod 调度到任何位置,但给予能够降低偏差的拓扑更高的优先级。

      当且仅当该 Pod 的每个可能的节点分配都会违反某些拓扑对应的 "maxSkew" 时, 才认为传入 Pod 的约束是 "不可满足的"。

      例如,在一个包含三个区域的集群中,maxSkew 设置为 1,具有相同 labelSelector 的 Pod 分布为 3/1/1:

      | zone1 | zone2 | zone3 |
      | P P P | P     | P     |
      

      如果 whenUnsatisfiable 设置为 DoNotSchedule,则传入的 Pod 只能调度到 "zone2"("zone3"), Pod 分布变成 3/2/1(3/1/2),因为 "zone2"("zone3")上的实际偏差(Actual Skew) 为 2-1, 满足 maxSkew 约束(1)。 换句话说,集群仍然可以不平衡,但调度器不会使其更加地不平衡。

      这是一个必填字段。

    • topologySpreadConstraints.labelSelector (LabelSelector)

      labelSelector 用于识别匹配的 Pod。对匹配此标签选择算符的 Pod 进行计数, 以确定其相应拓扑域中的 Pod 数量。

    • topologySpreadConstraints.matchLabelKeys ([]string)

    原子性:将在合并期间被替换

    matchLabelKeys 是一组 Pod 标签键,用于通过计算 Pod 分布方式来选择 Pod。 新 Pod 标签中不存在的键将被忽略。这些键用于从新来的 Pod 标签中查找值,这些键值标签与 labelSelector 进行逻辑与运算, 通过计算 Pod 的分布方式来选择现有 Pod 的组。新来的 Pod 标签中不存在的键将被忽略。 null 或空的列表意味着仅与 labelSelector 匹配。

    • topologySpreadConstraints.minDomains (int32)

      minDomains 表示符合条件的域的最小数量。当符合拓扑键的候选域个数小于 minDomains 时, Pod 拓扑分布特性会将 "全局最小值" 视为 0,然后进行偏差的计算。 当匹配拓扑键的候选域的数量等于或大于 minDomains 时,此字段的值对调度没有影响。 因此,当候选域的数量少于 minDomains 时,调度程序不会将超过 maxSkew 个 Pods 调度到这些域。 如果字段值为 nil,所表达的约束为 minDomains 等于 1。 字段的有效值为大于 0 的整数。当字段值不为 nil 时,whenUnsatisfiable 必须为 DoNotSchedule

      例如,在一个包含三个区域的集群中,maxSkew 设置为 2,minDomains 设置为 5,具有相同 labelSelector 的 Pod 分布为 2/2/2:

      | zone1 | zone2 | zone3 |
      | PP    | PP    | PP    |
      

      域的数量小于 5(minDomains 取值),因此"全局最小值"被视为 0。 在这种情况下,无法调度具有相同 labelSelector 的新 Pod,因为如果基于新 Pod 计算的偏差值将为 3(3-0)。将这个 Pod 调度到三个区域中的任何一个,都会违反 maxSkew 约束。

      此字段是一个 Beta 字段,需要启用 MinDomainsInPodTopologySpread 特性门控(默认被启用)。

    • topologySpreadConstraints.nodeAffinityPolicy (string)

      nodeAffinityPolicy 表示我们在计算 Pod 拓扑分布偏差时将如何处理 Pod 的 nodeAffinity/nodeSelector。 选项为:

      • Honor:只有与 nodeAffinity/nodeSelector 匹配的节点才会包括到计算中。
      • Ignore:nodeAffinity/nodeSelector 被忽略。所有节点均包括到计算中。

      如果此值为 nil,此行为等同于 Honor 策略。 这是通过 NodeInclusionPolicyInPodTopologySpread 特性标志启用的 Alpha 级别特性。

    • topologySpreadConstraints.nodeTaintsPolicy (string)

    nodeTaintsPolicy 表示我们在计算 Pod 拓扑分布偏差时将如何处理节点污点。选项为:

    • Honor:包括不带污点的节点以及新来 Pod 具有容忍度且带有污点的节点。
    • Ignore:节点污点被忽略。包括所有节点。

    如果此值为 nil,此行为等同于 Ignore 策略。 这是通过 NodeInclusionPolicyInPodTopologySpread 特性标志启用的 Alpha 级别特性。

  • overhead (map[string]Quantity)

    overhead 表示与用指定 RuntimeClass 运行 Pod 相关的资源开销。 该字段将由 RuntimeClass 准入控制器在准入时自动填充。 如果启用了 RuntimeClass 准入控制器,则不得在 Pod 创建请求中设置 overhead 字段。 RuntimeClass 准入控制器将拒绝已设置 overhead 字段的 Pod 创建请求。 如果在 Pod 规约中配置并选择了 RuntimeClass,overhead 字段将被设置为对应 RuntimeClass 中定义的值, 否则将保持不设置并视为零。更多信息: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md

生命周期

  • terminationGracePeriodSeconds (int64)

    可选字段,表示 Pod 需要体面终止的所需的时长(以秒为单位)。字段值可以在删除请求中减少。 字段值必须是非负整数。零值表示收到 kill 信号则立即停止(没有机会关闭)。 如果此值为 nil,则将使用默认宽限期。 宽限期是从 Pod 中运行的进程收到终止信号后,到进程被 kill 信号强制停止之前,Pod 可以继续存在的时间(以秒为单位)。 应该将此值设置为比你的进程的预期清理时间更长。默认为 30 秒。

  • activeDeadlineSeconds (int64)

    在系统将主动尝试将此 Pod 标记为已失败并杀死相关容器之前,Pod 可能在节点上活跃的时长; 市场计算基于 startTime 计算间(以秒为单位)。字段值必须是正整数。

  • readinessGate ([]PodReadinessGate)

    如果设置了此字段,则将评估所有就绪门控(Readiness Gate)以确定 Pod 就绪状况。 当所有容器都已就绪,并且就绪门控中指定的所有状况的 status 都为 "true" 时,Pod 被视为就绪。 更多信息: https://git.k8s.io/enhancements/keps/sig-network/580-pod-readiness-gates

    PodReadinessGate 包含对 Pod 状况的引用

    • readinessGates.conditionType (string),必需

      conditionType 是指 Pod 的状况列表中类型匹配的状况。

主机名和名称解析

  • hostname (string)

    指定 Pod 的主机名。如果此字段未指定,则 Pod 的主机名将设置为系统定义的值。

  • setHostnameAsFQDN (boolean)

    如果为 true,则 Pod 的主机名将配置为 Pod 的 FQDN,而不是叶名称(默认值)。 在 Linux 容器中,这意味着将内核的 hostname 字段(struct utsname 的 nodename 字段)设置为 FQDN。 在 Windows 容器中,这意味着将注册表项 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters 的 hostname 键设置为 FQDN。如果 Pod 没有 FQDN,则此字段不起作用。 默认为 false。

  • subdomain (string)

    如果设置了此字段,则完全限定的 Pod 主机名将是 <hostname>.<subdomain>.<Pod 名字空间>.svc.<集群域名>。 如果未设置此字段,则该 Pod 将没有域名。

  • hostAliases ([]HostAlias)

    补丁策略:基于 ip 键合并

    hostAliases 是一个可选的列表属性,包含要被注入到 Pod 的 hosts 文件中的主机和 IP 地址。 这仅对非 hostNetwork Pod 有效。

    HostAlias 结构保存 IP 和主机名之间的映射,这些映射将作为 Pod 的 hosts 文件中的条目注入。

    • hostAliases.hostnames ([]string)

      指定 IP 地址对应的主机名。

    • hostAliases.ip (string)

      主机文件条目的 IP 地址。

  • dnsConfig (PodDNSConfig)

    指定 Pod 的 DNS 参数。此处指定的参数将被合并到基于 dnsPolicy 生成的 DNS 配置中。

    PodDNSConfig 定义 Pod 的 DNS 参数,这些参数独立于基于 dnsPolicy 生成的参数。

    • dnsConfig.nameservers ([]string)

      DNS 名字服务器的 IP 地址列表。此列表将被追加到基于 dnsPolicy 生成的基本名字服务器列表。 重复的名字服务器将被删除。

    • dnsConfig.options ([]PodDNSConfigOption)

      DNS 解析器选项列表。此处的选项将与基于 dnsPolicy 所生成的基本选项合并。重复的条目将被删除。 options 中所给出的解析选项将覆盖基本 dnsPolicy 中出现的对应选项。

      PodDNSConfigOption 定义 Pod 的 DNS 解析器选项。

      • dnsConfig.options.name (string)

        必需字段。

      • dnsConfig.options.value (string)

        选项取值。

    • dnsConfig.searches ([]string)

      用于主机名查找的 DNS 搜索域列表。这一列表将被追加到基于 dnsPolicy 生成的基本搜索路径列表。 重复的搜索路径将被删除。

  • dnsPolicy (string)

    为 Pod 设置 DNS 策略。默认为 "ClusterFirst"。 有效值为 "ClusterFirstWithHostNet""ClusterFirst""Default""None"。 dnsConfig 字段中给出的 DNS 参数将与使用 dnsPolicy 字段所选择的策略合并。 要针对 hostNetwork 的 Pod 设置 DNS 选项,你必须将 DNS 策略显式设置为 "ClusterFirstWithHostNet"

主机名字空间

  • hostNetwork (boolean)

    为此 Pod 请求主机层面联网支持。使用主机的网络名字空间。 如果设置了此选项,则必须指定将使用的端口。默认为 false。

  • hostPID (boolean)

    使用主机的 PID 名字空间。可选:默认为 false。

  • hostIPC (boolean)

    使用主机的 IPC 名字空间。可选:默认为 false。

  • shareProcessNamespace (boolean)

    在 Pod 中的所有容器之间共享单个进程名字空间。设置了此字段之后,容器将能够查看来自同一 Pod 中其他容器的进程并发出信号, 并且每个容器中的第一个进程不会被分配 PID 1。hostPIDshareProcessNamespace 不能同时设置。 可选:默认为 false。

服务账号

安全上下文

  • securityContext (PodSecurityContext)

    SecurityContext 包含 Pod 级别的安全属性和常见的容器设置。 可选:默认为空。每个字段的默认值见类型描述。

    PodSecurityContext 包含 Pod 级别的安全属性和常用容器设置。 一些字段也存在于 container.securityContext 中。container.securityContext 中的字段值优先于 PodSecurityContext 的字段值。

    • securityContext.runAsUser (int64)

      运行容器进程入口点(Entrypoint)的 UID。如果未指定,则默认为镜像元数据中指定的用户。 也可以在 SecurityContext 中设置。 如果同时在 SecurityContext 和 PodSecurityContext 中设置,则在对应容器中所设置的 SecurityContext 值优先。 注意,spec.os.name 为 "windows" 时不能设置此字段。

    • securityContext.runAsNonRoot (boolean)

      指示容器必须以非 root 用户身份运行。如果为 true,kubelet 将在运行时验证镜像, 以确保它不会以 UID 0(root)身份运行。如果镜像中确实使用 root 账号启动,则容器无法被启动。 如果此字段未设置或为 false,则不会执行此类验证。也可以在 SecurityContext 中设置。 如果同时在 SecurityContext 和 PodSecurityContext 中设置,则在 SecurityContext 中指定的值优先。

    • securityContext.runAsGroup (int64)

      运行容器进程入口点(Entrypoint)的 GID。如果未设置,则使用运行时的默认值。 也可以在 SecurityContext 中设置。如果同时在 SecurityContext 和 PodSecurityContext 中设置, 则在对应容器中设置的 SecurityContext 值优先。 注意,spec.os.name 为 "windows" 时不能设置该字段。

    • securityContext.supplementalGroups ([]int64)

      在容器的主 GID 之外,应用于每个容器中运行的第一个进程的组列表。 如果未设置此字段,则不会向任何容器添加额外的组。 注意,spec.os.name 为 "windows" 时不能设置此字段。

    • securityContext.fsGroup (int64)

      应用到 Pod 中所有容器的特殊补充组。某些卷类型允许 kubelet 将该卷的所有权更改为由 Pod 拥有:

      1. 文件系统的属主 GID 将是 fsGroup 字段值
      2. setgid 位已设置(在卷中创建的新文件将归 fsGroup 所有)
      3. 权限位将与 rw-rw---- 进行按位或操作

      如果未设置此字段,kubelet 不会修改任何卷的所有权和权限。 注意,spec.os.name 为 "windows" 时不能设置此字段。

    • securityContext.fsGroupChangePolicy (string)

      fsGroupChangePolicy 定义了在卷被在 Pod 中暴露之前更改其属主和权限的行为。 此字段仅适用于支持基于 fsGroup 的属主权(和权限)的卷类型。它不会影响临时卷类型, 例如:secretconfigmapemptydir。 有效值为 "OnRootMismatch""Always"。如果未设置,则使用 "Always"。 注意,spec.os.name 为 "windows" 时不能设置此字段。

    • securityContext.seccompProfile (SeccompProfile)

      此 Pod 中的容器使用的 seccomp 选项。注意,spec.os.name 为 "windows" 时不能设置此字段。

      SeccompProfile 定义 Pod 或容器的 seccomp 配置文件设置。只能设置一个配置文件源。

      • securityContext.seccompProfile.type (string),必需

        type 标明将应用哪种 seccomp 配置文件。有效的选项有:

        • Localhost - 应使用在节点上的文件中定义的配置文件。
        • RuntimeDefault - 应使用容器运行时默认配置文件。
        • Unconfined - 不应应用任何配置文件。
      • securityContext.seccompProfile.localhostProfile (string)

        localhostProfile 指示应使用在节点上的文件中定义的配置文件。该配置文件必须在节点上预先配置才能工作。 必须是相对于 kubelet 配置的 seccomp 配置文件位置的下降路径。 仅当 type 为 "Localhost" 时才必须设置。

    • securityContext.seLinuxOptions (SELinuxOptions)

      应用于所有容器的 SELinux 上下文。如果未设置,容器运行时将为每个容器分配一个随机 SELinux 上下文。 也可以在 SecurityContext 中设置。 如果同时在 SecurityContext 和 PodSecurityContext 中设置,则在对应容器中设置的 SecurityContext 值优先。 注意,spec.os.name 为 "windows" 时不能设置该字段。

      SELinuxOptions 是要应用于容器的标签

      • securityContext.seLinuxOptions.level (string)

        level 是应用于容器的 SELinux 级别标签。

      • securityContext.seLinuxOptions.role (string)

        role 是应用于容器的 SELinux 角色标签。

      • securityContext.seLinuxOptions.type (string)

        type 是适用于容器的 SELinux 类型标签。

      • securityContext.seLinuxOptions.user (string)

        user 是应用于容器的 SELinux 用户标签。

    • securityContext.sysctls ([]Sysctl)

      sysctls 包含用于 Pod 的名字空间 sysctl 列表。具有不受(容器运行时)支持的 sysctl 的 Pod 可能无法启动。 注意,spec.os.name 为 "windows" 时不能设置此字段。

      Sysctl 定义要设置的内核参数

      • securityContext.sysctls.name (string),必需

        要设置的属性的名称。

      • securityContext.sysctls.value (string),必需

        要设置的属性值。

    • securityContext.windowsOptions (WindowsSecurityContextOptions)

      要应用到所有容器上的、特定于 Windows 的设置。 如果未设置此字段,将使用容器的 SecurityContext 中的选项。 如果同时在 SecurityContext 和 PodSecurityContext 中设置,则在 SecurityContext 中指定的值优先。 注意,spec.os.name 为 "linux" 时不能设置该字段。

      WindowsSecurityContextOptions 包含特定于 Windows 的选项和凭据。

      • securityContext.windowsOptions.gmsaCredentialSpec (string)

        gmsaCredentialSpec 是 GMSA 准入 Webhook 内嵌由 gmsaCredentialSpecName 字段所指定的 GMSA 凭证规约内容的地方。

      • securityContext.windowsOptions.gmsaCredentialSpecName (string)

        gmsaCredentialSpecName 是要使用的 GMSA 凭证规约的名称。

      • securityContext.windowsOptions.hostProcess (boolean)

        hostProcess 确定容器是否应作为"主机进程"容器运行。 此字段是 Alpha 级别的,只有启用 WindowsHostProcessContainers 特性门控的组件才会理解此字段。 在不启用该功能门控的前提下设置了此字段,将导致验证 Pod 时发生错误。 一个 Pod 的所有容器必须具有相同的有效 hostProcess 值(不允许混合设置了 hostProcess 的容器和未设置 hostProcess 容器)。 此外,如果 hostProcess 为 true,则 hostNetwork 也必须设置为 true。

      • securityContext.windowsOptions.runAsUserName (string)

        Windows 中用来运行容器进程入口点的用户名。如果未设置,则默认为镜像元数据中指定的用户。 也可以在 PodSecurityContext 中设置。 如果同时在 SecurityContext 和 PodSecurityContext 中设置,则在 SecurityContext 中指定的值优先。

Alpha 级别

  • hostUsers (boolean)

    使用主机的用户名字空间。可选:默认为 true。如果设置为 true 或不存在,则 Pod 将运行在主机的用户名字空间中, 当 Pod 需要仅对主机用户名字空间可用的一个特性时这会很有用,例如使用 CAP_SYS_MODULE 加载内核模块。 当设置为 false 时,会为该 Pod 创建一个新的用户名字空间。 设置为 false 对于缓解容器逃逸漏洞非常有用,可防止允许实际在主机上没有 root 特权的用户以 root 运行他们的容器。 此字段是 Alpha 级别的字段,只有启用 UserNamespacesSupport 特性的服务器才能使用此字段。

已弃用

  • serviceAccount (string)

    deprecatedServiceAccount 是 serviceAccountName 的弃用别名。此字段已被弃用:应改用 serviceAccountName。

容器

要在 Pod 中运行的单个应用容器。


  • name (string),必需

    指定为 DNS_LABEL 的容器的名称。Pod 中的每个容器都必须有一个唯一的名称 (DNS_LABEL)。无法更新。

镜像

Entrypoint

  • workingDir (string)

    容器的工作目录。如果未指定,将使用容器运行时的默认值,默认值可能在容器镜像中配置。无法更新。

端口

  • ports([]ContainerPort)

    补丁策略:基于 containerPort 键合并

    映射:键 containerPort, protocol 组合的唯一值将在合并期间保留

    要从容器暴露的端口列表。此处不指定端口不会阻止该端口被暴露。 任何侦听容器内默认 "0.0.0.0" 地址的端口都可以从网络访问。使用策略合并补丁来修改此数组可能会破坏数据。 更多细节请参阅 https://github.com/kubernetes/kubernetes/issues/108255。 无法更新。

    ContainerPort 表示单个容器中的网络端口。

    • ports.containerPort (int32),必需

      要在 Pod 的 IP 地址上公开的端口号。这必须是有效的端口号,0 < x < 65536。

    • ports.hostIP (string)

      绑定外部端口的主机 IP。

    • ports.hostPort (int32)

      要在主机上公开的端口号。如果指定,此字段必须是一个有效的端口号,0 < x < 65536。 如果设置了 hostNetwork,此字段值必须与 containerPort 匹配。大多数容器不需要设置此字段。

    • ports.name (string)

      如果设置此字段,这必须是 IANA_SVC_NAME 并且在 Pod 中唯一。 Pod 中的每个命名端口都必须具有唯一的名称。服务可以引用的端口的名称。

    • ports.protocol (string)

      端口协议。必须是 UDPTCPSCTP。默认为 TCP

环境变量

  • env([]EnvVar)

    补丁策略:基于 name 键合并

    要在容器中设置的环境变量列表。无法更新。

    EnvVar 表示容器中存在的环境变量。

    • env.name (string),必需

      环境变量的名称。必须是 C_IDENTIFIER。

    • env.value (string)

      变量引用 $(VAR_NAME) 使用容器中先前定义的环境变量和任何服务环境变量进行扩展。 如果无法解析变量,则输入字符串中的引用将保持不变。 $$ 会被简化为 $,这允许转义 $(VAR_NAME) 语法:即 "$$(VAR_NAME)" 将产生字符串字面值 "$(VAR_NAME)"。 无论变量是否存在,转义引用都不会被扩展。默认为 ""。

    • env.valueFrom (EnvVarSource)

      环境变量值的来源。如果 value 值不为空,则不能使用。

      EnvVarSource 表示 envVar 值的来源。

      • env.valueFrom.configMapKeyRef (ConfigMapKeySelector)

        选择某个 ConfigMap 的一个主键。

      • env.valueFrom.fieldRef (ObjectFieldSelector)

        选择 Pod 的一个字段:支持 metadata.namemetadata.namespacemetadata.labels['<KEY>']metadata.annotations['<KEY>']spec.nodeNamespec.serviceAccountNamestatus.hostIP status.podIPstatus.podIPs

      • env.valueFrom.resourceFieldRef (ResourceFieldSelector)

        选择容器的资源:目前仅支持资源限制和请求(limits.cpulimits.memorylimits.ephemeral-storagerequests.cpurequests.memoryrequests.ephemeral-storage)。

      • env.valueFrom.secretKeyRef (SecretKeySelector)

        在 Pod 的名字空间中选择 Secret 的主键。

        SecretKeySelector 选择一个 Secret 的主键。

  • envFrom ([]EnvFromSource)

    用来在容器中填充环境变量的数据源列表。在源中定义的键必须是 C_IDENTIFIER。 容器启动时,所有无效主键都将作为事件报告。 当一个键存在于多个源中时,与最后一个来源关联的值将优先。 由 env 定义的条目中,与此处键名重复者,以 env 中定义为准。无法更新。

    EnvFromSource 表示一组 ConfigMaps 的来源

    • envFrom.configMapRef (ConfigMapEnvSource)

      要从中选择主键的 ConfigMap。

      ConfigMapEnvSource 选择一个 ConfigMap 来填充环境变量。目标 ConfigMap 的 data 字段的内容将键值对表示为环境变量。

    • envFrom.prefix (string)

      附加到 ConfigMap 中每个键名之前的可选标识符。必须是 C_IDENTIFIER。

    • envFrom.secretRef (SecretEnvSource)

      要从中选择主键的 Secret。

      SecretEnvSource 选择一个 Secret 来填充环境变量。 目标 Secret 的 data 字段的内容将键值对表示为环境变量。

  • volumeMounts ([]VolumeMount)

    补丁策略:基于 mountPath 键合并

    要挂载到容器文件系统中的 Pod 卷。无法更新。

    VolumeMount 描述在容器中安装卷。

    • volumeMounts.mountPath (string),必需

      容器内卷的挂载路径。不得包含 ':'。

    • volumeMounts.name (string),必需

      此字段必须与卷的名称匹配。

    • volumeMounts.mountPropagation (string)

      mountPropagation 确定挂载如何从主机传播到容器,及如何反向传播。 如果未设置,则使用 MountPropagationNone。该字段在 1.10 中是 Beta 版。

    • volumeMounts.readOnly (boolean)

      如果为 true,则以只读方式挂载,否则(false 或未设置)以读写方式挂载。默认为 false。

    • volumeMounts.subPath (boolean)

      卷中的路径,容器中的卷应该这一路径安装。默认为 ""(卷的根)。

    • volumeMounts.subPathExpr (string)

      应安装容器卷的卷内的扩展路径。行为类似于 subPath,但环境变量引用 $(VAR_NAME) 使用容器的环境进行扩展。默认为 ""(卷的根)。subPathExprsubPath 是互斥的。

  • volumeDevices ([]VolumeDevice)

    补丁策略:基于 devicePath 键合并

    volumeDevices 是容器要使用的块设备列表。

    volumeDevice 描述了容器内原始块设备的映射。

    • volumeDevices.devicePath (string),必需

      devicePath 是设备将被映射到的容器内的路径。

    • volumeDevices.name (string),必需

      name 必须与 Pod 中的 persistentVolumeClaim 的名称匹配

资源

生命周期

  • lifecycle (Lifecycle)

    管理系统应对容器生命周期事件采取的行动。无法更新。

    Lifecycle 描述管理系统为响应容器生命周期事件应采取的行动。 对于 postStart 和 preStop 生命周期处理程序,容器的管理会阻塞,直到操作完成, 除非容器进程失败,在这种情况下处理程序被中止。

    • lifecycle.preStop (LifecycleHandler)

      preStop 在容器因 API 请求或管理事件(如存活态探针/启动探针失败、抢占、资源争用等)而终止之前立即调用。 如果容器崩溃或退出,则不会调用处理程序。Pod 的终止宽限期倒计时在 preStop 钩子执行之前开始。 无论处理程序的结果如何,容器最终都会在 Pod 的终止宽限期内终止(除非被终结器延迟)。 容器的其他管理会阻塞,直到钩子完成或达到终止宽限期。更多信息: https://kubernetes.io/zh-cn/docs/concepts/containers/container-lifecycle-hooks/#container-hooks

  • terminationMessagePath (string)

    可选字段。挂载到容器文件系统的一个路径,容器终止消息写入到该路径下的文件中。 写入的消息旨在成为简短的最终状态,例如断言失败消息。如果大于 4096 字节,将被节点截断。 所有容器的总消息长度将限制为 12 KB。默认为 /dev/termination-log。无法更新。

  • terminationMessagePolicy (string)

    指示应如何填充终止消息。字段值 File 将使用 terminateMessagePath 的内容来填充成功和失败的容器状态消息。 如果终止消息文件为空并且容器因错误退出,FallbackToLogsOnError 将使用容器日志输出的最后一块。 日志输出限制为 2048 字节或 80 行,以较小者为准。默认为 File。无法更新。

  • startupProbe (Probe)

    startupProbe 表示 Pod 已成功初始化。如果设置了此字段,则此探针成功完成之前不会执行其他探针。 如果这个探针失败,Pod 会重新启动,就像存活态探针失败一样。 这可用于在 Pod 生命周期开始时提供不同的探针参数,此时加载数据或预热缓存可能需要比稳态操作期间更长的时间。 这无法更新。更多信息: https://kubernetes.io/zh-cn/docs/concepts/workloads/pods/pod-lifecycle#container-probes

安全上下文

  • securityContext (SecurityContext)

    SecurityContext 定义了容器应该运行的安全选项。如果设置,SecurityContext 的字段将覆盖 PodSecurityContext 的等效字段。更多信息: https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/security-context/

    SecurityContext 保存将应用于容器的安全配置。某些字段在 SecurityContext 和 PodSecurityContext 中都存在。 当两者都设置时,SecurityContext 中的值优先。

    • securityContext.runAsUser (int64)

      运行容器进程入口点的 UID。如果未指定,则默认为镜像元数据中指定的用户。 也可以在 PodSecurityContext 中设置。 如果同时在 SecurityContext 和 PodSecurityContext 中设置,则在 SecurityContext 中指定的值优先。 注意,spec.os.name 为 "windows" 时不能设置该字段。

    • securityContext.runAsNonRoot (boolean)

      指示容器必须以非 root 用户身份运行。 如果为 true,kubelet 将在运行时验证镜像,以确保它不会以 UID 0(root)身份运行,如果是,则无法启动容器。 如果未设置或为 false,则不会执行此类验证。也可以在 PodSecurityContext 中设置。 如果同时在 SecurityContext 和 PodSecurityContext 中设置,则在 SecurityContext 中指定的值优先。

    • securityContext.runAsGroup (int64)

      运行容器进程入口点的 GID。如果未设置,则使用运行时默认值。也可以在 PodSecurityContext 中设置。 如果同时在 SecurityContext 和 PodSecurityContext 中设置,则在 SecurityContext 中指定的值优先。 注意,spec.os.name 为 "windows" 时不能设置该字段。

    • securityContext.readOnlyRootFilesystem (boolean)

      此容器是否具有只读根文件系统。默认为 false。注意,spec.os.name 为 "windows" 时不能设置该字段。

    • securityContext.procMount (string)

      procMount 表示用于容器的 proc 挂载类型。默认值为 DefaultProcMount, 它针对只读路径和掩码路径使用容器运行时的默认值。此字段需要启用 ProcMountType 特性门控。 注意,spec.os.name 为 "windows" 时不能设置此字段。

    • securityContext.privileged (boolean)

      以特权模式运行容器。特权容器中的进程本质上等同于主机上的 root。默认为 false。 注意,spec.os.name 为 "windows" 时不能设置此字段。

    • securityContext.allowPrivilegeEscalation (boolean)

      allowPrivilegeEscalation 控制进程是否可以获得比其父进程更多的权限。此布尔值直接控制是否在容器进程上设置 no_new_privs 标志。allowPrivilegeEscalation 在容器处于以下状态时始终为 true:

      1. 以特权身份运行
      2. 具有 CAP_SYS_ADMIN

      请注意,当 spec.os.name 为 "windows" 时,无法设置此字段。

    • securityContext.capabilities (Capabilities)

      运行容器时添加或放弃的权能(Capabilities)。默认为容器运行时所授予的权能集合。 注意,spec.os.name 为 "windows" 时不能设置此字段。

      在运行中的容器中添加和放弃 POSIX 权能。

      • securityContext.capabilities.add ([]string)

        新增权能。

      • securityContext.capabilities.drop ([]string)

        放弃权能。

    • securityContext.seccompProfile (SeccompProfile)

      此容器使用的 seccomp 选项。如果在 Pod 和容器级别都提供了 seccomp 选项,则容器级别的选项会覆盖 Pod 级别的选项设置。 注意,spec.os.name 为 "windows" 时不能设置此字段。

      SeccompProfile 定义 Pod 或容器的 seccomp 配置文件设置。只能设置一个配置文件源。

      • securityContext.seccompProfile.type (string),必需

        type 指示应用哪种 seccomp 配置文件。有效的选项有:

        • Localhost - 应使用在节点上的文件中定义的配置文件。
        • RuntimeDefault - 应使用容器运行时的默认配置文件。
        • Unconfined - 不应用任何配置文件。
      • securityContext.seccompProfile.localhostProfile (string)

        localhostProfile 指示应使用的在节点上的文件,文件中定义了配置文件。 该配置文件必须在节点上先行配置才能使用。 必须是相对于 kubelet 所配置的 seccomp 配置文件位置下的下级路径。 仅当 type 为 "Localhost" 时才必须设置。

    • securityContext.seLinuxOptions (SELinuxOptions)

      要应用到容器上的 SELinux 上下文。如果未设置此字段,容器运行时将为每个容器分配一个随机的 SELinux 上下文。 也可以在 PodSecurityContext 中设置。如果同时在 SecurityContext 和 PodSecurityContext 中设置, 则在 SecurityContext 中指定的值优先。注意,spec.os.name 为 "windows" 时不能设置此字段。

      SELinuxOptions 是要应用到容器上的标签。

      • securityContext.seLinuxOptions.level (string)

        level 是应用于容器的 SELinux 级别标签。

      • securityContext.seLinuxOptions.role (string)

        role 是应用于容器的 SELinux 角色标签。

      • securityContext.seLinuxOptions.type (string)

        type 是适用于容器的 SELinux 类型标签。

      • securityContext.seLinuxOptions.user (string)

        user 是应用于容器的 SELinux 用户标签。

    • securityContext.windowsOptions (WindowsSecurityContextOptions)

      要应用于所有容器上的特定于 Windows 的设置。如果未指定,将使用 PodSecurityContext 中的选项。 如果同时在 SecurityContext 和 PodSecurityContext 中设置,则在 SecurityContext 中指定的值优先。 注意,spec.os.name 为 "linux" 时不能设置此字段。

      WindowsSecurityContextOptions 包含特定于 Windows 的选项和凭据。

      • securityContext.windowsOptions.gmsaCredentialSpec (string)

        gmsaCredentialSpec 是 GMSA 准入 Webhook 内嵌由 gmsaCredentialSpecName 字段所指定的 GMSA 凭证规约的内容的地方。

      • securityContext.windowsOptions.hostProcess (boolean)

        hostProcess 确定容器是否应作为 "主机进程" 容器运行。 此字段是 Alpha 级别的,只有启用 WindowsHostProcessContainers 特性门控的组件才会处理。 设置此字段而不启用特性门控是,在验证 Pod 时将发生错误。 一个 Pod 的所有容器必须具有相同的有效 hostProcess 值(不允许混合设置了 hostProcess 容器和未设置 hostProcess 的容器)。 此外,如果 hostProcess 为 true,则 hostNetwork 也必须设置为 true。

      • securityContext.windowsOptions.runAsUserName (string)

        Windows 中运行容器进程入口点的用户名。如果未指定,则默认为镜像元数据中指定的用户。 也可以在 PodSecurityContext 中设置。 如果同时在 SecurityContext 和 PodSecurityContext 中设置,则在 SecurityContext 中指定的值优先。

调试

  • stdin (boolean)

    此容器是否应在容器运行时为 stdin 分配缓冲区。如果未设置,从容器中的 stdin 读取将始终导致 EOF。 默认为 false。

  • stdinOnce (boolean)

    容器运行时是否应在某个 attach 打开 stdin 通道后关闭它。当 stdin 为 true 时,stdin 流将在多个 attach 会话中保持打开状态。 如果 stdinOnce 设置为 true,则 stdin 在容器启动时打开,在第一个客户端连接到 stdin 之前为空, 然后保持打开并接受数据,直到客户端断开连接,此时 stdin 关闭并保持关闭直到容器重新启动。 如果此标志为 false,则从 stdin 读取的容器进程将永远不会收到 EOF。默认为 false。

EphemeralContainer

EphemeralContainer 是一个临时容器,你可以将其添加到现有 Pod 以用于用户发起的活动,例如调试。 临时容器没有资源或调度保证,它们在退出或 Pod 被移除或重新启动时不会重新启动。 如果临时容器导致 Pod 超出其资源分配,kubelet 可能会驱逐 Pod。

要添加临时容器,请使用现有 Pod 的 ephemeralcontainers 子资源。临时容器不能被删除或重新启动。


  • name (string),必需

    以 DNS_LABEL 形式设置的临时容器的名称。此名称在所有容器、Init 容器和临时容器中必须是唯一的。

  • targetContainerName (string)

    如果设置,则为 Pod 规约中此临时容器所针对的容器的名称。临时容器将在该容器的名字空间(IPC、PID 等)中运行。 如果未设置,则临时容器使用 Pod 规约中配置的名字空间。

    容器运行时必须实现对此功能的支持。如果运行时不支持名字空间定位,则设置此字段的结果是未定义的。

镜像

入口点

  • workingDir (string)

    容器的工作目录。如果未指定,将使用容器运行时的默认值,默认值可能在容器镜像中配置。无法更新。

环境变量

  • env([]EnvVar)

    补丁策略:基于 name 键合并

    要在容器中设置的环境变量列表。无法更新。

    EnvVar 表示容器中存在的环境变量。

    • env.name (string),必需

      环境变量的名称。必须是 C_IDENTIFIER。

    • env.value (string)

      变量引用 $(VAR_NAME) 使用容器中先前定义的环境变量和任何服务环境变量进行扩展。 如果无法解析变量,则输入字符串中的引用将保持不变。 $$ 被简化为 $,这允许转义 $(VAR_NAME) 语法:即 "$$(VAR_NAME)" 将产生字符串字面值 "$(VAR_NAME)"。 无论变量是否存在,转义引用都不会被扩展。默认为 ""。

    • env.valueFrom (EnvVarSource)

      环境变量值的来源。如果取值不为空,则不能使用。

      EnvVarSource 表示 envVar 值的源。

      • env.valueFrom.configMapKeyRef (ConfigMapKeySelector)

        选择 ConfigMap 的主键。

        选择 ConfigMap 的主键。

        • env.valueFrom.configMapKeyRef.optional (boolean)

          指定是否 ConfigMap 或其键必须已经被定义。

      • env.valueFrom.fieldRefObjectFieldSelector

        选择 Pod 的一个字段:支持 metadata.namemetadata.namespacemetadata.labels['<KEY>']metadata.annotations['<KEY>']spec.nodeNamespec.serviceAccountNamestatus.hostIPstatus.podIPstatus.podIPs

      • env.valueFrom.resourceFieldRefResourceFieldSelector

        选择容器的资源:当前仅支持资源限制和请求(limits.cpulimits.memorylimits.ephemeral-storagerequests.cpurequests.memoryrequests.ephemeral-storage)。

      • env.valueFrom.secretKeyRef (SecretKeySelector)

        在 Pod 的名字空间中选择某 Secret 的主键。

        SecretKeySelector 选择某 Secret 的主键。

  • envFrom ([]EnvFromSource)

    在容器中填充环境变量的来源列表。在来源中定义的键名必须是 C_IDENTIFIER。 容器启动时,所有无效键都将作为事件报告。当一个键存在于多个来源中时,与最后一个来源关联的值将优先。 如果有重复主键,env 中定义的值将优先。无法更新。

    EnvFromSource 表示一组 ConfigMap 来源

    • envFrom.configMapRef (ConfigMapEnvSource)

      要从中选择的 ConfigMap。

      ConfigMapEnvSource 选择一个 ConfigMap 来填充环境变量。目标 ConfigMap 的 data 字段的内容将键值对表示为环境变量。

    • envFrom.prefix (string)

      要在 ConfigMap 中的每个键前面附加的可选标识符。必须是C_IDENTIFIER。

    • envFrom.secretRef (SecretEnvSource)

      可供选择的 Secret。

      SecretEnvSource 选择一个 Secret 来填充环境变量。目标 Secret 的 data 字段的内容将键值对表示为环境变量。

  • volumeMounts ([]VolumeMount)

    补丁策略:基于 mountPath 键合并

    要挂载到容器文件系统中的 Pod 卷。临时容器不允许子路径挂载。无法更新。

    VolumeMount 描述在容器中卷的挂载。

    • volumeMounts.mountPath (string),必需

      容器内应安装卷的路径。不得包含 ':'。

    • volumeMounts.name (string),必需

      此字段必须与卷的名称匹配。

    • volumeMounts.mountPropagation (string)

      mountPropagation 确定装载如何从主机传播到容器,及反向传播选项。 如果未设置,则使用 None。此字段在 1.10 中为 Beta 字段。

    • volumeMounts.readOnly (boolean)

      如果为 true,则挂载卷为只读,否则为读写(false 或未指定)。默认值为 false。

    • volumeMounts.subPath (string)

      卷中的路径名,应该从该路径挂在容器的卷。默认为 "" (卷的根)。

    • volumeMounts.subPathExpr (string)

      应安装容器卷的卷内的扩展路径。行为类似于 subPath,但环境变量引用 $(VAR_NAME) 使用容器的环境进行扩展。默认为 ""(卷的根)。subPathExprSubPath 是互斥的。

  • volumeDevices ([]VolumeDevice)

    补丁策略:基于 devicePath 键合并

    volumeDevices 是容器要使用的块设备列表。

    volumeDevice 描述容器内原始块设备的映射。

    • volumeDevices.devicePath (string),必需

      devicePath 是设备将被映射到的容器内的路径。

    • volumeDevices.name (string),必需

      name 必须与 Pod 中的 persistentVolumeClaim 的名称匹配。

生命周期

  • terminationMessagePath (string)

    可选字段。挂载到容器文件系统的路径,用于写入容器终止消息的文件。 写入的消息旨在成为简短的最终状态,例如断言失败消息。如果超出 4096 字节,将被节点截断。 所有容器的总消息长度将限制为 12 KB。默认为 /dev/termination-log。无法更新。

  • terminationMessagePolicy (string)

    指示应如何填充终止消息。字段值为 File 表示将使用 terminateMessagePath 的内容来填充成功和失败的容器状态消息。 如果终止消息文件为空并且容器因错误退出,字段值 FallbackToLogsOnError 表示将使用容器日志输出的最后一块。日志输出限制为 2048 字节或 80 行,以较小者为准。 默认为 File。无法更新。

调试

  • stdin (boolean)

    是否应在容器运行时内为此容器 stdin 分配缓冲区。 如果未设置,从容器中的 stdin 读数据将始终导致 EOF。默认为 false。

  • stdinOnce (boolean)

    容器运行时是否应在某个 attach 操作打开 stdin 通道后关闭它。 当 stdin 为 true 时,stdin 流将在多个 attach 会话中保持打开状态。 如果 stdinOnce 设置为 true,则 stdin 在容器启动时打开,在第一个客户端连接到 stdin 之前为空, 然后保持打开并接受数据,直到客户端断开连接,此时 stdin 关闭并保持关闭直到容器重新启动。 如果此标志为 false,则从 stdin 读取的容器进程将永远不会收到 EOF。默认为 false。

  • tty (boolean)

    这个容器是否应该为自己分配一个 TTY,也需要 stdin 为 true。默认为 false。

安全上下文

  • securityContext (SecurityContext)

    可选字段。securityContext 定义了运行临时容器的安全选项。 如果设置了此字段,SecurityContext 的字段将覆盖 PodSecurityContext 的等效字段。

    SecurityContext 保存将应用于容器的安全配置。 一些字段在 SecurityContext 和 PodSecurityContext 中都存在。 当两者都设置时,SecurityContext 中的值优先。

    • securityContext.runAsUser (int64)

      运行容器进程入口点的 UID。如果未指定,则默认为镜像元数据中指定的用户。 也可以在 PodSecurityContext 中设置。如果同时在 SecurityContext 和 PodSecurityContext 中设置,则在 SecurityContext 中指定的值优先。 注意,spec.os.name 为 "windows" 时不能设置该字段。

    • securityContext.runAsNonRoot (boolean)

      指示容器必须以非 root 用户身份运行。如果为 true,Kubelet 将在运行时验证镜像, 以确保它不会以 UID 0(root)身份运行,如果是,则无法启动容器。 如果未设置或为 false,则不会执行此类验证。也可以在 PodSecurityContext 中设置。 如果同时在 SecurityContext 和 PodSecurityContext 中设置,则在 SecurityContext 中指定的值优先。

    • securityContext.runAsGroup (int64)

      运行容器进程入口点的 GID。如果未设置,则使用运行时默认值。也可以在 PodSecurityContext 中设置。 如果同时在 SecurityContext 和 PodSecurityContext 中设置,则在 SecurityContext 中指定的值优先。注意,spec.os.name 为 "windows" 时不能设置该字段。

    • securityContext.readOnlyRootFilesystem (boolean)

      此容器是否具有只读根文件系统。 默认为 false。注意,spec.os.name 为 "windows" 时不能设置该字段。

    • securityContext.procMount (string)

      procMount 表示用于容器的 proc 挂载类型。默认值为 DefaultProcMount, 它将容器运行时默认值用于只读路径和掩码路径。这需要启用 ProcMountType 特性门控。 注意,spec.os.name 为 "windows" 时不能设置该字段。

    • securityContext.privileged (boolean)

      以特权模式运行容器。特权容器中的进程本质上等同于主机上的 root。默认为 false。 注意,spec.os.name 为 "windows" 时不能设置该字段。

    • securityContext.allowPrivilegeEscalation (boolean)

      allowPrivilegeEscalation 控制进程是否可以获得比其父进程更多的权限。 此布尔值直接控制是否在容器进程上设置 no_new_privs 标志。allowPrivilegeEscalation 在容器处于以下状态时始终为 true:

      1. 以特权身份运行
      2. 具有 CAP_SYS_ADMIN 权能

      请注意,当 spec.os.name 为 "windows" 时,无法设置此字段。

    • securityContext.capabilities (Capabilities)

      运行容器时添加/放弃的权能。默认为容器运行时授予的默认权能集。 注意,spec.os.name 为 "windows" 时不能设置此字段。

      在运行中的容器中添加和放弃 POSIX 权能。

      • securityContext.capabilities.add ([]string)

        新增的权能。

      • securityContext.capabilities.drop ([]string)

        放弃的权能。

    • securityContext.seccompProfile (SeccompProfile)

      此容器使用的 seccomp 选项。如果在 Pod 和容器级别都提供了 seccomp 选项, 则容器选项会覆盖 Pod 选项。注意,spec.os.name 为 "windows" 时不能设置该字段。

      SeccompProfile 定义 Pod 或容器的 seccomp 配置文件设置。只能设置一个配置文件源。

      • securityContext.seccompProfile.type (string),必需

        type 指示将应用哪种 seccomp 配置文件。有效的选项是:

        • Localhost - 应使用在节点上的文件中定义的配置文件。
        • RuntimeDefault - 应使用容器运行时默认配置文件。
        • Unconfined - 不应应用任何配置文件。
      • securityContext.seccompProfile.localhostProfile (string)

        localhostProfile 指示应使用在节点上的文件中定义的配置文件。 该配置文件必须在节点上预先配置才能工作。 必须是相对于 kubelet 配置的 seccomp 配置文件位置下的子路径。 仅当 type 为 "Localhost" 时才必须设置。

    • securityContext.seLinuxOptions (SELinuxOptions)

      要应用于容器的 SELinux 上下文。如果未指定,容器运行时将为每个容器分配一个随机 SELinux 上下文。也可以在 PodSecurityContext 中设置。 如果同时在 SecurityContext 和 PodSecurityContext 中设置,则在 SecurityContext 中指定的值优先。注意,spec.os.name 为 "windows" 时不能设置此字段。

      SELinuxOptions 是要应用于容器的标签

      • securityContext.seLinuxOptions.level (string)

        level 是应用于容器的 SELinux 级别标签。

      • securityContext.seLinuxOptions.role (string)

        role 是应用于容器的 SELinux 角色标签。

      • securityContext.seLinuxOptions.type (string)

        type 是适用于容器的 SELinux 类型标签。

      • securityContext.seLinuxOptions.user (string)

        user 是应用于容器的 SELinux 用户标签。

    • securityContext.windowsOptions (WindowsSecurityContextOptions)

      要应用到所有容器上的特定于 Windows 的设置。如果未指定,将使用 PodSecurityContext 中的选项。 如果同时在 SecurityContext 和 PodSecurityContext 中设置,则在 SecurityContext 中指定的值优先。注意,spec.os.name 为 "linux" 时不能设置此字段。

      WindowsSecurityContextOptions 包含特定于 Windows 的选项和凭据。

      • securityContext.windowsOptions.gmsaCredentialSpec (string)

        gmsaCredentialSpec 是 GMSA 准入 Webhook 内嵌由 gmsaCredentialSpecName 字段所指定的 GMSA 凭证规约内容的地方。

      • securityContext.windowsOptions.gmsaCredentialSpecName (string)

        gmsaCredentialSpecName 是要使用的 GMSA 凭证规约的名称。

      • securityContext.windowsOptions.hostProcess (boolean)

        hostProcess 确定容器是否应作为 "主机进程" 容器运行。此字段是 Alpha 级别的,只有启用了 WindowsHostProcessContainers 特性门控的组件才会处理此字段。 设置此字段而未启用特性门控的话,在验证 Pod 时将引发错误。 一个 Pod 的所有容器必须具有相同的有效 hostProcess 值 (不允许混合设置了 hostProcess 的容器和未设置 hostProcess 的容器)。 此外,如果 hostProcess 为 true,则 hostNetwork 也必须设置为 true。

      • securityContext.windowsOptions.runAsUserName (string)

        Windows 中运行容器进程入口点的用户名。如果未指定,则默认为镜像元数据中指定的用户。 也可以在 PodSecurityContext 中设置。如果同时在 SecurityContext 和 PodSecurityContext 中设置,则在 SecurityContext 中指定的值优先。

不允许

  • ports([]ContainerPort)

    补丁策略:基于 containerPort 键合并

    映射:键 containerPort, protocol 组合的唯一值将在合并期间保留

    临时容器不允许使用端口。

    ContainerPort 表示单个容器中的网络端口。

    • ports.containerPort (int32),必需

      要在容器的 IP 地址上公开的端口号。这必须是有效的端口号 0 < x < 65536。

    • ports.hostIP (string)

      要将外部端口绑定到的主机 IP。

    • ports.hostPort (int32)

      要在主机上公开的端口号。如果设置了,则作为必须是一个有效的端口号,0 < x < 65536。 如果指定了 hostNetwork,此值必须与 containerPort 匹配。大多数容器不需要这个配置。

    • ports.name(string)

      如果指定了,则作为端口的名称。必须是 IANA_SVC_NAME 并且在 Pod 中是唯一的。 Pod 中的每个命名端口都必须具有唯一的名称。服务可以引用的端口的名称。

    • ports.protocol (string)

      端口协议。必须是 UDPTCPSCTP 之一。默认为 TCP

  • lifecycle (Lifecycle)

    临时容器不允许使用生命周期。

    生命周期描述了管理系统为响应容器生命周期事件应采取的行动。 对于 postStart 和 preStop 生命周期处理程序,容器的管理会阻塞,直到操作完成, 除非容器进程失败,在这种情况下处理程序被中止。

    • lifecycle.preStopLifecycleHandler

      preStop 在容器因 API 请求或管理事件(例如:存活态探针/启动探针失败、抢占、资源争用等) 而终止之前立即调用。如果容器崩溃或退出,则不会调用处理程序。 Pod 的终止宽限期倒计时在 preStop 钩子执行之前开始。 无论处理程序的结果如何,容器最终都会在 Pod 的终止宽限期内终止(除非被终结器延迟)。 容器的其他管理会阻塞,直到钩子完成或达到终止宽限期。更多信息: https://kubernetes.io/zh-cn/docs/concepts/containers/container-lifecycle-hooks/#container-hooks

  • livenessProbeProbe

    临时容器不允许使用探针。

  • readyProbeProbe

    临时容器不允许使用探针。

  • startupProbeProbe

    临时容器不允许使用探针。

LifecycleHandler

LifecycleHandler 定义了应在生命周期挂钩中执行的特定操作。 必须指定一个且只能指定一个字段,tcpSocket 除外。


  • exec (execAction)

    Exec 指定要执行的操作。

    ExecAction 描述了 "在容器中运行" 操作。

    • exec.command ([]string)

      command 是要在容器内执行的命令行,命令的工作目录是容器文件系统中的根目录('/')。 该命令只是被通过 exec 执行,而不会单独启动一个 Shell 来运行,因此传统的 Shell 指令('|' 等)将不起作用。要使用某 Shell,你需要显式调用该 Shell。 退出状态 0 被视为活动/健康,非零表示不健康。

  • httpGet (HTTPGetAction)

    HTTPGet 指定要执行的 HTTP 请求。

    HTTPGetAction 描述基于 HTTP Get 请求的操作。

    • httpGet.port (IntOrString),必需

      要在容器上访问的端口的名称或编号。数字必须在 1 到 65535 的范围内。名称必须是 IANA_SVC_NAME。

      IntOrString 是一种可以包含 int32 或字符串值的类型。在 JSON 或 YAML 封组和取消编组时, 它会生成或使用内部类型。例如,这允许你拥有一个可以接受名称或数字的 JSON 字段。

    • httpGet.host (string)

      要连接的主机名,默认为 Pod IP。你可能想在 httpHeaders 中设置 "Host"。

    • httpGet.httpHeaders ([]HTTPHeader)

      要在请求中设置的自定义标头。HTTP 允许重复的标头。

      HTTPHeader 描述了在 HTTP 探针中使用的自定义标头

      • httpGet.httpHeaders.name (string),必需

        HTTP 头部字段名称。

      • httpGet.httpHeaders.value (string),必需

        HTTP 头部字段取值。

    • httpGet.path (string)

      HTTP 服务器上的访问路径。

    • httpGet.scheme (string)

      用于连接到主机的方案。默认为 HTTP

  • tcpSocket (TCPSocketAction)

    已弃用。不再支持 tcpSocket 作为 LifecycleHandler,但为向后兼容保留之。 当指定 tcp 处理程序时,此字段不会被验证,而生命周期回调将在运行时失败。

    TCPSocketAction 描述基于打开套接字的动作。

    • tcpSocket.port (IntOrString),必需

      容器上要访问的端口的编号或名称。端口号必须在 1 到 65535 的范围内。 名称必须是 IANA_SVC_NAME。

      IntOrString 是一种可以保存 int32 或字符串值的类型。在 JSON 或 YAML 编组和解组中使用时, 会生成或使用内部类型。例如,这允许你拥有一个可以接受名称或数字的 JSON 字段。

    • tcpSocket.host (string)

      可选字段。要连接的主机名,默认为 Pod IP。

NodeAffinity

节点亲和性是一组节点亲和性调度规则。


  • preferredDuringSchedulingIgnoredDuringExecution ([]PreferredSchedulingTerm)

    调度程序会更倾向于将 Pod 调度到满足该字段指定的亲和性表达式的节点, 但它可能会选择违反一个或多个表达式的节点。最优选的节点是权重总和最大的节点, 即对于满足所有调度要求(资源请求、requiredDuringScheduling 亲和表达式等)的每个节点, 通过迭代该字段的元素来计算总和如果节点匹配相应的 matchExpressions,则将 "权重" 添加到总和中; 具有最高总和的节点是最优选的。

    空的首选调度条件匹配所有具有隐式权重 0 的对象(即它是一个 no-op 操作)。 null 值的首选调度条件不匹配任何对象(即也是一个 no-op 操作)。

    • preferredDuringSchedulingIgnoredDuringExecution.preference (NodeSelectorTerm),必需

      与相应权重相关联的节点选择条件。

      null 值或空值的节点选择条件不会匹配任何对象。这些条件的请求按逻辑与操作组合。 TopologySelectorTerm 类型实现了 NodeSelectorTerm 的一个子集。

      • preferredDuringSchedulingIgnoredDuringExecution.preference.matchExpressions ([]NodeSelectorRequirement

        按节点标签列出的节点选择条件列表。

      • preferredDuringSchedulingIgnoredDuringExecution.preference.matchFields ([]NodeSelectorRequirement

        按节点字段列出的节点选择要求列表。

    • preferredDuringSchedulingIgnoredDuringExecution.weight (int32),必需

      与匹配相应的 nodeSelectorTerm 相关的权重,范围为 1-100。

  • requiredDuringSchedulingIgnoredDuringExecution (NodeSelector)

    如果在调度时不满足该字段指定的亲和性要求,则不会将 Pod 调度到该节点上。 如果在 Pod 执行期间的某个时间点不再满足此字段指定的亲和性要求(例如:由于更新), 系统可能会或可能不会尝试最终将 Pod 从其节点中逐出。

    一个节点选择器代表一个或多个标签查询结果在一组节点上的联合;换言之, 它表示由节点选择器项表示的选择器的逻辑或组合。

    • requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms ([]NodeSelectorTerm),必需

      必需的字段。节点选择条件列表。这些条件按逻辑或操作组合。

      null 值或空值的节点选择器条件不匹配任何对象。这里的条件是按逻辑与操作组合的。 TopologySelectorTerm 类型实现了 NodeSelectorTerm 的一个子集。

      • requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms.matchExpressions ([]NodeSelectorRequirement

        按节点标签列出的节点选择器需求列表。

      • requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms.matchFields ([]NodeSelectorRequirement

        按节点字段列出的节点选择器要求列表。

PodAffinity

Pod 亲和性是一组 Pod 间亲和性调度规则。


  • preferredDuringSchedulingIgnoredDuringExecution ([]WeightedPodAffinityTerm)

    调度器会更倾向于将 Pod 调度到满足该字段指定的亲和性表达式的节点, 但它可能会选择违反一个或多个表达式的节点。最优选择是权重总和最大的节点, 即对于满足所有调度要求(资源请求、requiredDuringScheduling 亲和表达式等)的每个节点, 通过迭代该字段的元素来计算总和,如果节点具有与相应 podAffinityTerm 匹配的 Pod,则将“权重”添加到总和中; 具有最高总和的节点是最优选的。

    所有匹配的 WeightedPodAffinityTerm 字段的权重都是按节点累计的,以找到最优选的节点。

    • preferredDuringSchedulingIgnoredDuringExecution.podAffinityTerm (PodAffinityTerm),必需

      必需的字段。一个 Pod 亲和性条件,对应一个与相应的权重值。

      定义一组 Pod(即那些与给定名字空间相关的标签选择算符匹配的 Pod 集合), 当前 Pod 应该与所选 Pod 集合位于同一位置(亲和性)或位于不同位置(反亲和性), 其中“在同一位置”意味着运行在一个节点上,其键 topologyKey 的标签值与运行所选 Pod 集合中的某 Pod 的任何节点上的标签值匹配。

      • preferredDuringSchedulingIgnoredDuringExecution.podAffinityTerm.topologyKey (string),必需

        此 Pod 应与指定名字空间中与标签选择算符匹配的 Pod 集合位于同一位置(亲和性) 或位于不同位置(反亲和性),这里的“在同一位置”意味着运行在一个节点上,其键名为 topologyKey 的标签值与运行所选 Pod 集合中的某 Pod 的任何节点上的标签值匹配。 不允许使用空的 topologyKey

      • preferredDuringSchedulingIgnoredDuringExecution.podAffinityTerm.labelSelectorLabelSelector

        对一组资源的标签查询,在这里资源为 Pod。

      • preferredDuringSchedulingIgnoredDuringExecution.podAffinityTerm.namespaceSelectorLabelSelector

        对条件所适用的名字空间集合的标签查询。 此条件会被应用到此字段所选择的名字空间和 namespaces 字段中列出的名字空间的组合之上。 选择算符为 null 和 namespaces 列表为 null 值或空表示“此 Pod 的名字空间”。 空的选择算符 ({}) 可用来匹配所有名字空间。

      • preferredDuringSchedulingIgnoredDuringExecution.podAffinityTerm.namespaces ([]string)

        namespaces 指定此条件所适用的名字空间,是一个静态列表。 此条件会被应用到 namespaces 字段中列出的名字空间和由 namespaceSelector 选中的名字空间上。 namespaces 列表为 null 或空,以及 namespaceSelector 值为 null 均表示“此 Pod 的名字空间”。

    • preferredDuringSchedulingIgnoredDuringExecution.weight (int32),必需

      weight 是匹配相应 podAffinityTerm 条件的权重,范围为 1-100。

  • requiredDuringSchedulingIgnoredDuringExecution ([]PodAffinityTerm)

    如果在调度时不满足该字段指定的亲和性要求,则该 Pod 不会被调度到该节点上。 如果在 Pod 执行期间的某个时间点不再满足此字段指定的亲和性要求(例如:由于 Pod 标签更新), 系统可能会也可能不会尝试最终将 Pod 从其节点中逐出。 当此列表中有多个元素时,每个 podAffinityTerm 对应的节点列表是取其交集的,即必须满足所有条件。

    定义一组 Pod(即那些与给定名字空间相关的标签选择算符匹配的 Pod 集合),当前 Pod 应该与该 Pod 集合位于同一位置(亲和性)或不位于同一位置(反亲和性)。 这里的“位于同一位置”含义是运行在一个节点上。基于 topologyKey 字段所给的标签键名, 检查所选 Pod 集合中各个 Pod 所在的节点上的标签值,标签值相同则认作“位于同一位置”。

    • requiredDuringSchedulingIgnoredDuringExecution.topologyKey (string),必需

      此 Pod 应与指定名字空间中与标签选择算符匹配的 Pod 集合位于同一位置(亲和性) 或不位于同一位置(反亲和性), 这里的“位于同一位置”含义是运行在一个节点上。基于 topologyKey 字段所给的标签键名, 检查所选 Pod 集合中各个 Pod 所在的节点上的标签值,标签值相同则认作“位于同一位置”。 不允许使用空的 topologyKey

    • requiredDuringSchedulingIgnoredDuringExecution.labelSelectorLabelSelector

      对一组资源的标签查询,在这里资源为 Pod。

    • requiredDuringSchedulingIgnoredDuringExecution.namespaceSelectorLabelSelector

      对条件所适用的名字空间集合的标签查询。 当前条件将应用于此字段选择的名字空间和 namespaces 字段中列出的名字空间。 选择算符为 null 和 namespaces 列表为 null 或空值表示“此 Pod 的名字空间”。 空选择算符 ({}) 能够匹配所有名字空间。

    • requiredDuringSchedulingIgnoredDuringExecution.namespaces ([]string)

      namespaces 指定当前条件所适用的名字空间名称的静态列表。 当前条件适用于此字段中列出的名字空间和由 namespaceSelector 选中的名字空间。 namespaces 列表为 null 或空,以及 namespaceSelector 为 null 表示“此 Pod 的名字空间”。

PodAntiAffinity

Pod 反亲和性是一组 Pod 间反亲和性调度规则。


  • preferredDuringSchedulingIgnoredDuringExecution ([]WeightedPodAffinityTerm)

    调度器更倾向于将 Pod 调度到满足该字段指定的反亲和性表达式的节点, 但它可能会选择违反一个或多个表达式的节点。 最优选的节点是权重总和最大的节点,即对于满足所有调度要求(资源请求、requiredDuringScheduling 反亲和性表达式等)的每个节点,通过遍历元素来计算总和如果节点具有与相应 podAffinityTerm 匹配的 Pod,则此字段并在总和中添加"权重";具有最高加和的节点是最优选的。

    所有匹配的 WeightedPodAffinityTerm 字段的权重都是按节点添加的,以找到最优选的节点。

    • preferredDuringSchedulingIgnoredDuringExecution.podAffinityTerm (PodAffinityTerm),必需

      必需的字段。一个 Pod 亲和性条件,与相应的权重相关联。

      定义一组 Pod(即那些与给定名字空间相关的标签选择算符匹配的 Pod 集合), 当前 Pod 应该与所选 Pod 集合位于同一位置(亲和性)或不位于同一位置(反亲和性), 其中 "在同一位置" 意味着运行在一个节点上,其键 topologyKey 的标签值与运行所选 Pod 集合中的某 Pod 的任何节点上的标签值匹配。

      • preferredDuringSchedulingIgnoredDuringExecution.podAffinityTerm.topologyKey (string),必需

        此 Pod 应与指定名字空间中与标签选择算符匹配的 Pod 集合位于同一位置(亲和性) 或不位于同一位置(反亲和性),这里的 "在同一位置" 意味着运行在一个节点上,其键名为 topologyKey 的标签值与运行所选 Pod 集合中的某 Pod 的任何节点上的标签值匹配。 不允许使用空的 topologyKey

      • preferredDuringSchedulingIgnoredDuringExecution.podAffinityTerm.labelSelectorLabelSelector

        对一组资源的标签查询,在这里资源为 Pod。

      • preferredDuringSchedulingIgnoredDuringExecution.podAffinityTerm.namespaceSelectorLabelSelector

        对条件所适用的名字空间集合的标签查询。 此条件会被应用到此字段所选择的名字空间和 namespaces 字段中列出的名字空间的组合之上。 选择算符为 null 和 namespaces 列表为 null 值或空表示 "此 Pod 的名字空间"。 空的选择算符 ({}) 可用来匹配所有名字空间。

      • preferredDuringSchedulingIgnoredDuringExecution.podAffinityTerm.namespaces ([]string)

        namespaces 指定此条件所适用的名字空间,是一个静态列表。 此条件会被应用到 namespaces 字段中列出的名字空间和由 namespaceSelector 选中的名字空间上。 namespaces 列表为 null 或空,以及 namespaceSelector 值为 null 均表示 "此 Pod 的名字空间"。

    • preferredDuringSchedulingIgnoredDuringExecution.weight (int32),必需

      weight 是匹配相应 podAffinityTerm 条件的权重,范围为 1-100。

  • requiredDuringSchedulingIgnoredDuringExecution ([]PodAffinityTerm)

    如果在调度时不满足该字段指定的反亲和性要求,则该 Pod 不会被调度到该节点上。 如果在 Pod 执行期间的某个时间点不再满足此字段指定的反亲和性要求(例如:由于 Pod 标签更新), 系统可能会或可能不会尝试最终将 Pod 从其节点中逐出。 当有多个元素时,每个 podAffinityTerm 对应的节点列表是取其交集的,即必须满足所有条件。

    定义一组 Pod(即那些与给定名字空间相关的标签选择算符匹配的 Pod 集合),当前 Pod 应该与该 Pod 集合位于同一位置(亲和性)或不位于同一位置(反亲和性)。 这里的 "位于同一位置" 含义是运行在一个节点上。基于 topologyKey 字段所给的标签键名, 检查所选 Pod 集合中各个 Pod 所在的节点上的标签值,标签值相同则认作 "位于同一位置"。

    • requiredDuringSchedulingIgnoredDuringExecution.topologyKey (string),必需

      此 Pod 应与指定名字空间中与标签选择算符匹配的 Pod 集合位于同一位置(亲和性) 或不位于同一位置(反亲和性), 这里的 "位于同一位置" 含义是运行在一个节点上。基于 topologyKey 字段所给的标签键名, 检查所选 Pod 集合中各个 Pod 所在的节点上的标签值,标签值相同则认作 "位于同一位置"。 不允许使用空的 topologyKey

    • requiredDuringSchedulingIgnoredDuringExecution.labelSelectorLabelSelector

      对一组资源的标签查询,在这里资源为 Pod。

    • requiredDuringSchedulingIgnoredDuringExecution.namespaceSelectorLabelSelector

      对条件所适用的名字空间集合的标签查询。 当前条件将应用于此字段选择的名字空间和 namespaces 字段中列出的名字空间。 选择算符为 null 和 namespaces 列表为 null 或空值表示 “此 Pod 的名字空间”。 空选择算符 ({}) 能够匹配所有名字空间。

    • requiredDuringSchedulingIgnoredDuringExecution.namespaces ([]string)

      namespaces 指定当前条件所适用的名字空间名称的静态列表。 当前条件适用于此字段中列出的名字空间和由 namespaceSelector 选中的名字空间。 namespaces 列表为 null 或空,以及 namespaceSelector 为 null 表示 “此 Pod 的名字空间”。

探针

探针描述了要对容器执行的健康检查,以确定它是否处于活动状态或准备好接收流量。


  • exec (execAction)

    exec 指定要执行的操作。

    ExecAction 描述了 "在容器中运行" 操作。

    • exec.command ([]string)

      command 是要在容器内执行的命令行,命令的工作目录是容器文件系统中的根目录('/')。 该命令只是通过 exec 执行,而不会启动 Shell,因此传统的 Shell 指令('|' 等)将不起作用。 要使用某 Shell,你需要显式调用该 Shell。 退出状态 0 被视为存活/健康,非零表示不健康。

  • httpGet (HTTPGetAction)

    httpGet 指定要执行的 HTTP 请求。

    HTTPGetAction 描述基于 HTTP Get 请求的操作。

    • httpGet.port (IntOrString),必需

      容器上要访问的端口的名称或端口号。端口号必须在 1 到 65535 内。名称必须是 IANA_SVC_NAME。

      IntOrString 是一种可以保存 int32 或字符串值的类型。在 JSON 或 YAML 编组和解组时, 它会生成或使用内部类型。例如,这允许你拥有一个可以接受名称或数字的 JSON 字段。

    • httpGet.host (string)

      要连接的主机名,默认为 Pod IP。你可能想在 httpHeaders 中设置 "Host"。

    • httpGet.httpHeaders ([]HTTPHeader)

      要在请求中设置的自定义 HTTP 标头。HTTP 允许重复的标头。

      HTTPHeader 描述了在 HTTP 探针中使用的自定义标头。

      • httpGet.httpHeaders.name (string),必需

        HTTP 头部域名称。

      • httpGet.httpHeaders.value (string),必需

        HTTP 头部域值。

    • httpGet.path (string)

      HTTP 服务器上的访问路径。

    • httpGet.scheme (string)

      用于连接到主机的方案。默认为 HTTP。

  • tcpSocket (TCPSocketAction)

    tcpSocket 指定涉及 TCP 端口的操作。

    TCPSocketAction 描述基于打开套接字的动作。

    • tcpSocket.port (IntOrString),必需

      容器上要访问的端口的端口号或名称。端口号必须在 1 到 65535 内。名称必须是 IANA_SVC_NAME。

      IntOrString 是一种可以保存 int32 或字符串的类型。在 JSON 或 YAML 编组和解组时, 它会生成或使用内部类型。例如,这允许你拥有一个可以接受名称或数字的 JSON 字段。

    • tcpSocket.host (string)

      可选字段。要连接的主机名,默认为 Pod IP。

  • terminationGracePeriodSeconds (int64)

    Pod 需要在探针失败时体面终止所需的时间长度(以秒为单位),为可选字段。 宽限期是 Pod 中运行的进程收到终止信号后,到进程被终止信号强制停止之前的时间长度(以秒为单位)。 你应该将此值设置为比你的进程的预期清理时间更长。 如果此值为 nil,则将使用 Pod 的 terminateGracePeriodSeconds。 否则,此值将覆盖 Pod 规约中设置的值。字段值值必须是非负整数。 零值表示收到终止信号立即停止(没有机会关闭)。 这是一个 Beta 字段,需要启用 ProbeTerminationGracePeriod 特性门控。最小值为 1。 如果未设置,则使用 spec.terminationGracePeriodSeconds

  • periodSeconds (int32)

    探针的执行周期(以秒为单位)。默认为 10 秒。最小值为 1。

  • failureThreshold (int32)

    探针成功后的最小连续失败次数,超出此阈值则认为探针失败。默认为 3。最小值为 1。

  • successThreshold (int32)

    探针失败后最小连续成功次数,超过此阈值才会被视为探针成功。默认为 1。 存活性探针和启动探针必须为 1。最小值为 1。

  • grpc (GRPCAction)

    GRPC 指定涉及 GRPC 端口的操作。这是一个 Beta 字段,需要启用 GRPCContainerProbe 特性门控。

    • grpc.port (int32),必需

      gRPC 服务的端口号。数字必须在 1 到 65535 的范围内。

    • grpc.service (string)

      service 是要放置在 gRPC 运行状况检查请求中的服务的名称 (请参见 https://github.com/grpc/grpc/blob/master/doc/health-checking.md)。

      如果未指定,则默认行为由 gRPC 定义。

PodStatus

PodStatus 表示有关 Pod 状态的信息。状态内容可能会滞后于系统的实际状态, 尤其是在托管 Pod 的节点无法联系控制平面的情况下。


  • nominatedNodeName (string)

    仅当此 Pod 抢占节点上的其他 Pod 时才设置 nominatedNodeName, 但抢占操作的受害者会有体面终止期限,因此此 Pod 无法立即被调度。 此字段不保证 Pod 会在该节点上调度。 如果其他节点更早进入可用状态,调度器可能会决定将 Pod 放置在其他地方。 调度器也可能决定将此节点上的资源分配给优先级更高的、在抢占操作之后创建的 Pod。 因此,当 Pod 被调度时,该字段可能与 Pod 规约中的 nodeName 不同。

  • hostIP (string)

    Pod 被调度到的主机的 IP 地址。如果尚未被调度,则为字段为空。

  • startTime (Time)

    kubelet 确认 Pod 对象的日期和时间,格式遵从 RFC 3339。 此时间点处于 kubelet 为 Pod 拉取容器镜像之前。

    Time 是 time.Time 的包装器,支持正确编组为 YAML 和 JSON。 time 包所提供的许多工厂方法都有包装器。

  • phase (string)

    Pod 的 phase 是对 Pod 在其生命周期中所处位置的简单、高级摘要。 conditions 数组、reason 和 message 字段以及各个容器的 status 数组包含有关 Pod 状态的进一步详细信息。phase 的取值有五种可能性:

    • Pending:Pod 已被 Kubernetes 系统接受,但尚未创建容器镜像。 这包括 Pod 被调度之前的时间以及通过网络下载镜像所花费的时间。
    • Running:Pod 已经被绑定到某个节点,并且所有的容器都已经创建完毕。至少有一个容器仍在运行,或者正在启动或重新启动过程中。
    • Succeeded:Pod 中的所有容器都已成功终止,不会重新启动。
    • Failed:Pod 中的所有容器都已终止,并且至少有一个容器因故障而终止。 容器要么以非零状态退出,要么被系统终止。
    • Unknown:由于某种原因无法获取 Pod 的状态,通常是由于与 Pod 的主机通信时出错。

    更多信息: https://kubernetes.io/zh-cn/docs/concepts/workloads/pods/pod-lifecycle#pod-phase

  • message (string)

    一条人类可读的消息,标示有关 Pod 为何处于这种情况的详细信息。

  • reason (string)

    一条简短的驼峰式命名的消息,指示有关 Pod 为何处于此状态的详细信息。例如 'Evicted'。

  • podIP (string)

    分配给 Pod 的 IP 地址。至少在集群内可路由。如果尚未分配则为空。

  • podIPs ([]PodIP)

    补丁策略:基于 ip 键合并

    podIPs 保存分配给 Pod 的 IP 地址。如果指定了该字段,则第 0 个条目必须与 podIP 字段值匹配。 Pod 最多可以为 IPv4 和 IPv6 各分配 1 个值。如果尚未分配 IP,则此列表为空。

    podIPs 字段中每个条目的 IP 地址信息。每个条目都包含:

    ip 字段:给出分配给 Pod 的 IP 地址。该 IP 地址至少在集群内可路由。

    • podIP.ip (string)

      ip 是分配给 Pod 的 IP 地址(IPv4 或 IPv6)。

  • initContainerStatuses ([]ContainerStatus)

    该列表在清单中的每个 Init 容器中都有一个条目。最近成功的 Init 容器会将 ready 设置为 true, 最近启动的容器将设置 startTime。更多信息: https://kubernetes.io/zh-cn/docs/concepts/workloads/pods/pod-lifecycle#pod-and-container-status

    ContainerStatus 包含此容器当前状态的详细信息。

    • initContainerStatuses.name (string),必需

      此字段值必须是 DNS_LABEL。Pod 中的每个容器都必须具有唯一的名称。无法更新。

    • initContainerStatuses.image (string),必需

      容器中正在运行的镜像。更多信息: https://kubernetes.io/zh-cn/docs/concepts/containers/images

    • initContainerStatuses.imageID (string),必需

      容器镜像的镜像 ID。

    • initContainerStatuses.containerID (string)

      格式为 <type>://<container_id> 的容器 ID。

    • initContainerStatuses.state (ContainerState)

      有关容器当前状况的详细信息。

      ContainerState 中保存容器的可能状态。只能设置其成员之一。如果其中所有字段都未设置, 则默认为 ContainerStateWaiting。

      • initContainerStatuses.state.running (ContainerStateRunning)

        有关正在运行的容器的详细信息。

        ContainerStateRunning 是容器的运行状态。

        • initContainerStatuses.state.running.startedAt (Time)

          容器上次(重新)启动的时间。

          Time 是 time.Time 的包装器,支持正确编组为 YAML 和 JSON。 time 包所提供的许多工厂方法都有包装器。

      • initContainerStatuses.state.terminated (ContainerStateTerminated)

        有关已终止容器的详细信息。

        ContainerStateTerminated 是容器的终止状态。

        • initContainerStatuses.state.terminated.containerID (string)

          容器的 ID,格式为 "<类型>://<container_id>"

        • initContainerStatuses.state.terminated.exitCode (int32),必需

          容器上次终止时的退出状态

        • initContainerStatuses.state.terminated.startedAt (Time)

          容器上次执行时的开始时间。

          Time 是 time.Time 的包装器,支持正确编组为 YAML 和 JSON。 time 包所提供的许多工厂方法都有包装器。

        • initContainerStatuses.state.terminated.finishedAt (Time)

          容器上次终止的时间。

          Time 是 time.Time 的包装器,支持正确编组为 YAML 和 JSON。 time 包所提供的许多工厂方法都有包装器。

        • initContainerStatuses.state.terminated.message (string)

          有关容器上次终止的消息。

        • initContainerStatuses.state.terminated.reason (string)

          容器最后一次终止的(简要)原因。

        • initContainerStatuses.state.terminated.signal (int32)

          容器最后一次终止的信号。

      • initContainerStatuses.state.waiting (ContainerStateWaiting)

        有关等待状态容器的详细信息。

        容器状态等待是容器的等待状态。

        • initContainerStatuses.state.waiting.message (string)

          有关容器尚未运行的原因的消息。

        • initContainerStatuses.state.waiting.reason (string)

          容器尚未运行的(简要)原因。

    • initContainerStatuses.lastState (ContainerState)

      有关容器上次终止状况的详细信息。

      ContainerState 保存容器的可能状态。只能设置其成员之一。如果未设置任何成员, 则默认为 ContainerStateWaiting。

      • initContainerStatuses.lastState.running (ContainerStateRunning)

        有关正在运行的容器的详细信息

        ContainerStateRunning 是容器的运行状态。

        • initContainerStatuses.lastState.running.startedAt (Time)

          容器上次(重新)启动的时间

          Time 是 time.Time 的包装器,支持正确编组为 YAML 和 JSON。 time 包所提供的许多工厂方法都有包装器。

      • initContainerStatuses.lastState.terminated (ContainerStateTerminated)

        有关已终止容器的详细信息。

        ContainerStateTerminated 是容器的终止状态。

        • initContainerStatuses.lastState.terminated.containerID (string)

          容器的 ID,格式为 "<类型>://<container_id>"

        • initContainerStatuses.lastState.terminated.exitCode (int32),必需

          容器上次终止的退出状态码。

        • initContainerStatuses.lastState.terminated.startedAt (Time)

          容器上次执行的开始时间。

          Time 是 time.Time 的包装器,支持正确编组为 YAML 和 JSON。 time 包所提供的许多工厂方法都有包装器。

        • initContainerStatuses.lastState.terminated.finishedAt (Time)

          容器上次终止的时间。

          Time 是 time.Time 的包装器,支持正确编组为 YAML 和 JSON。 time 包所提供的许多工厂方法都有包装器。

        • initContainerStatuses.lastState.terminated.message (string)

          有关容器上次终止的消息。

        • initContainerStatuses.lastState.terminated.reason (string)

          容器最后一次终止的(简要)原因。

        • initContainerStatuses.lastState.terminated.signal (int32)

          容器最后一次终止的信号。

      • initContainerStatuses.lastState.waiting (ContainerStateWaiting)

        有关等待状态的容器的详细信息。

        ContainerStateWaiting 是容器的等待状态。

        • initContainerStatuses.lastState.waiting.message (string)

          关于容器尚未运行的原因的消息。

        • initContainerStatuses.lastState.waiting.reason (string)

          容器尚未运行的(简要)原因。

    • initContainerStatuses.ready (boolean),必需

      指定容器是否已通过其就绪态探测。

    • initContainerStatuses.restartCount (int32),必需

      容器重新启动的次数。

    • initContainerStatuses.started (boolean)

      指定容器是否已通过其启动探测。初始化为 false,在 startupProbe 成功之后变为 true。 在容器重新启动时,或者如果 kubelet 暂时失去状态时重置为 false。 在未定义启动探测器时始终为 true。

  • containerStatuses ([]ContainerStatus)

    该列表中针对清单中的每个容器都有一个条目。更多信息: https://kubernetes.io/zh-cn/docs/concepts/workloads/pods/pod-lifecycle#pod-and-container-status

    ContainerStatus 包含此容器当前状态的详细信息。

    • containerStatuses.name(string),必需

      此字段必须是一个 DNS_LABEL。Pod 中的每个容器都必须具有唯一的名称。无法更新。

    • containerStatuses.image (string),必需

      容器正在运行的镜像。更多信息: https://kubernetes.io/zh-cn/docs/concepts/containers/images

    • containerStatuses.imageID (string),必需

      容器镜像的镜像 ID。

    • containerStatuses.containerID (string)

      容器的 ID,格式为 "<类型>://<container_id>"

    • containerStatuses.state (ContainerState)

      有关容器当前状况的详细信息。

      ContainerStatuses 保存容器的可能状态。只能设置其中一个成员。如果所有成员都未设置, 则默认为 ContainerStateWaiting。

      • containerStatuses.state.running (ContainerStateRunning)

        有关正在运行的容器的详细信息。

        ContainerStateRunning 是容器的运行状态。

        • containerStatuses.state.running.startedAt (Time)

          容器上次(重新)启动的时间。

          Time 是 time.Time 的包装器,支持正确编组为 YAML 和 JSON。 time 包所提供的许多工厂方法都有包装器。

      • containerStatuses.state.terminated (ContainerStateTerminated)

        有关已终止容器的详细信息。

        ContainerStateTerminated 是容器的终止状态。

        • containerStatuses.state.terminated.containerID (string)

          容器的 ID,格式为 "<类型>://<container_id>"

        • containerStatuses.state.terminated.exitCode (int32),必需

          容器上次终止的退出状态码。

        • containerStatuses.state.terminated.startedAt (Time)

          容器上次执行的开始时间。

          Time 是 time.Time 的包装器,支持正确编组为 YAML 和 JSON。 time 包所提供的许多工厂方法都有包装器。

        • containerStatuses.state.terminated.message (string)

          有关容器上次终止的消息。

        • containerStatuses.state.terminated.reason (string)

          容器最后一次终止的(简要)原因

        • containerStatuses.state.terminated.signal (int32)

          容器最后一次终止的信号。

      • containerStatuses.state.waiting (ContainerStateWaiting)

        有关等待容器的详细信息。

        ContainerStateWaiting 是容器的等待状态。

        • containerStatuses.state.waiting.message (string)

          关于容器尚未运行的原因的消息。

        • containerStatuses.state.waiting.reason (string)

          容器尚未运行的(简要)原因。

    • containerStatuses.lastState (ContainerState)

      有关容器上次终止状况的详细信息。

      容器状态保存容器的可能状态。只能设置一个成员。如果所有成员都未设置, 则默认为 ContainerStateWaiting。

      • containerStatuses.lastState.running (ContainerStateRunning)

        有关正在运行的容器的详细信息。

        ContainerStateRunning 是容器的运行状态。

        • containerStatuses.lastState.running.startedAt (Time)

          容器上次(重新)启动的时间。

          Time 是 time.Time 的包装器,支持正确编组为 YAML 和 JSON。 time 包所提供的许多工厂方法都有包装器。

      • containerStatuses.lastState.terminated (ContainerStateTerminated)

        有关已终止容器的详细信息。

        ContainerStateTerminated 是容器的终止状态。

        • containerStatuses.lastState.terminated.containerID (string)

          格式为 <type>://<container_id> 的容器 ID。

        • containerStatuses.lastState.terminated.exitCode (int32),必需

          容器最后终止的退出状态码。

        • containerStatuses.lastState.terminated.startedAt (Time)

          容器上次执行时的开始时间。

          Time 是 time.Time 的包装器,支持正确编组为 YAML 和 JSON。 time 包所提供的许多工厂方法都有包装器。

        • containerStatuses.lastState.terminated.finishedAt (Time)

          容器上次终止的时间。

          Time 是 time.Time 的包装器,支持正确编组为 YAML 和 JSON。 time 包所提供的许多工厂方法都有包装器。

        • containerStatuses.lastState.terminated.message (string)

          关于容器上次终止的消息。

        • containerStatuses.lastState.terminated.reason (string)

          容器上次终止的(简要)原因

        • containerStatuses.lastState.terminated.signal (int32)

          容器上次终止的信号。

      • containerStatuses.lastState.waiting (ContainerStateWaiting)

        有关等待容器的详细信息。

        ContainerStateWaiting 是容器的等待状态。

        • containerStatuses.lastState.waiting.message (string)

          关于容器尚未运行的原因的消息。

        • containerStatuses.lastState.waiting.reason (string)

          容器尚未运行的(简要)原因。

    • containerStatuses.ready (boolean),必需

      指定容器是否已通过其就绪态探针。

    • containerStatuses.restartCount (int32),必需

      容器重启的次数。

    • containerStatuses.started (boolean)

      指定容器是否已通过其启动探针探测。初始化为 false,startupProbe 被认为成功后变为 true。 当容器重新启动或 kubelet 暂时丢失状态时重置为 false。 未定义启动探针时始终为 true。

  • ephemeralContainerStatuses ([]ContainerStatus)

    已在此 Pod 中运行的任何临时容器的状态。

    ContainerStatus 包含此容器当前状态的详细信息。

    • ephemeralContainerStatuses.name (string),必需

      字段值必须是 DNS_LABEL。Pod 中的每个容器都必须具有唯一的名称。无法更新。

    • ephemeralContainerStatuses.image (string),必需

      容器正在运行的镜像。更多信息: https://kubernetes.io/zh-cn/docs/concepts/containers/images

    • ephemeralContainerStatuses.imageID (string),必需

      容器镜像的镜像 ID。

    • ephemeralContainerStatuses.containerID (string)

      格式为 <type>://<container_id> 的容器 ID。

    • ephemeralContainerStatuses.state.running (ContainerStateRunning)

      有关正在运行的容器的详细信息

      ContainerStateRunning 是容器的运行状态。

      • ephemeralContainerStatuses.state.running.startedAt (Time)

        容器上次(重新)启动的时间。

        Time 是 time.Time 的包装器,支持正确编组为 YAML 和 JSON。 time 包所提供的许多工厂方法都有包装器。

    • ephemeralContainerStatuses.state.terminated (ContainerStateTerminated)

      有关已终止容器的详细信息。

      ContainerStateTerminated 是容器的终止状态。

      • ephemeralContainerStatuses.state.terminated.containerID (string)

        格式为 <type>://<container_id> 的容器 ID。

      • ephemeralContainerStatuses.state.terminated.exitCode (int32),必需

        容器上次终止的退出状态码。

      • ephemeralContainerStatuses.state.terminated.startedAt (Time)

        容器上次执行的开始时间。

        Time 是 time.Time 的包装器,支持正确编组为 YAML 和 JSON。 time 包所提供的许多工厂方法都有包装器。

      • ephemeralContainerStatuses.state.terminated.finishat (Time)

        容器上次终止的时间。

        Time 是 time.Time 的包装器,支持正确编组为 YAML 和 JSON。 time 包所提供的许多工厂方法都有包装器。

      • ephemeralContainerStatuses.state.terminated.message (string)

        关于容器上次终止的消息。

      • ephemeralContainerStatuses.state.terminated.reason (string)

        容器上次终止的(简要)原因

      • ephemeralContainerStatuses.state.terminated.signal (int32)

        容器上次终止的信号

    • ephemeralContainerStatuses.state.waiting (ContainerStateWaiting)

      有关等待容器的详细信息。

      ContainerStateWaiting 是容器的等待状态。

      • ephemeralContainerStatuses.state.waiting.message (string)

        关于容器尚未运行的原因的消息。

      • ephemeralContainerStatuses.state.waiting.reason (string)

        容器尚未运行的(简要)原因。

  • ephemeralContainerStatuses.lastState (ContainerState)

    有关容器的上次终止状况的详细信息。

    ContainerState 保存容器的可能状态。只能设置其中一个成员。如果所有成员都未设置, 则默认为 ContainerStateWaiting

    • ephemeralContainerStatuses.lastState.running (ContainerStateRunning)

      有关正在运行的容器的详细信息。

      ContainerStateRunning 是容器的运行状态。

      • ephemeralContainerStatuses.lastState.running.startedAt (Time)

        容器上次(重新)启动的时间。

        Time 是 time.Time 的包装器,支持正确编组为 YAML 和 JSON。 time 包所提供的许多工厂方法都有包装器。

    • ephemeralContainerStatuses.lastState.terminated (ContainerStateTerminated)

      有关已终止容器的详细信息。

      ContainerStateTerminated 是容器的终止状态。

      • ephemeralContainerStatuses.lastState.terminated.containerID (string)

        格式为 <type>://<container_id> 的容器 ID。

      • ephemeralContainerStatuses.lastState.terminated.exitCode (int32),必需

        容器上次终止时的退出状态码。

      • ephemeralContainerStatuses.lastState.terminated.startedAt (Time)

        容器上次执行的开始时间。

        Time 是 time.Time 的包装器,支持正确编组为 YAML 和 JSON。 time 包所提供的许多工厂方法都有包装器。

      • ephemeralContainerStatuses.lastState.terminated.finishedAt (Time)

        容器上次终止的时间。

        Time 是 time.Time 的包装器,支持正确编组为 YAML 和 JSON。 time 包所提供的许多工厂方法都有包装器。

      • ephemeralContainerStatuses.lastState.terminated.message (string)

        关于容器上次终止的消息。

      • ephemeralContainerStatuses.lastState.terminated.reason (string)

        容器上次终止的(简要)原因。

      • ephemeralContainerStatuses.lastState.terminated.signal (int32)

        容器上次终止的信号。

    • ephemeralContainerStatuses.lastState.waiting (ContainerStateWaiting)

      有关等待状态容器的详细信息。

      ContainerStateWaiting 是容器的等待状态。

      • ephemeralContainerStatuses.lastState.waiting.message (string)

        关于容器尚未运行的原因的消息。

      • ephemeralContainerStatuses.lastState.waiting.reason (string)

        容器尚未运行的(简要)原因。

  • ephemeralContainerStatuses.ready (boolean),必需

    指定容器是否已通过其就绪态探测。

  • ephemeralContainerStatuses.restartCount (int32),必需

    容器重新启动的次数。

  • ephemeralContainerStatuses.started (boolean)

    指定容器是否已通过其启动探测。初始化为 false,在 startProbe 成功之后变为 true。 在容器重新启动时或者 kubelet 暂时失去状态时重置为 false。 在未定义 startupProbe 时始终为 true。

PodList

PodList 是 Pod 的列表。


操作


get 读取指定的 Pod

HTTP 请求

GET /api/v1/namespaces/{namespace}/pods/{name}

参数

  • name (路径参数): string,必需

    Pod 的名称

  • namespace (路径参数): string,必需

    namespace

  • pretty (查询参数): string

    pretty

响应

200 (Pod): OK

401: Unauthorized

get 读取指定 Pod 的 ephemeralcontainers

HTTP 请求

GET /api/v1/namespaces/{namespace}/pods/{name}/ephemeralcontainers

参数

  • name (路径参数): string,必需

    Pod 的名称

  • namespace (路径参数): string,必需

    namespace

  • pretty (查询参数): string

    pretty

响应

200 (Pod): OK

401: Unauthorized

get 读取指定 Pod 的日志

HTTP 请求

GET /api/v1/namespaces/{namespace}/pods/{name}/log

参数

  • name (路径参数): string,必需

    Pod 的名称。

  • namespace (路径参数): string,必需

    namespace

  • container (查询参数): string

    为其流式传输日志的容器。如果 Pod 中有一个容器,则默认为仅容器。

  • follow (查询参数):boolean

    跟踪 Pod 的日志流。默认为 false。

  • insecureSkipTLSVerifyBackend (查询参数):boolean

    insecureSkipTLSVerifyBackend 表示 API 服务器不应确认它所连接的后端的服务证书的有效性。 这将使 API 服务器和后端之间的 HTTPS 连接不安全。 这意味着 API 服务器无法验证它接收到的日志数据是否来自真正的 kubelet。 如果 kubelet 配置为验证 API 服务器的 TLS 凭据,这并不意味着与真实 kubelet 的连接容易受到中间人攻击(例如,攻击者无法拦截来自真实 kubelet 的实际日志数据)。

  • limitBytes (查询参数): integer

    如果设置,则表示在终止日志输出之前从服务器读取的字节数。 设置此参数可能导致无法显示完整的最后一行日志记录,并且可能返回略多于或略小于指定限制。

  • pretty (查询参数): string

    pretty

  • previous (查询参数):boolean

    返回之前终止了的容器的日志。默认为 false。

  • sinceSeconds (查询参数): integer

    显示日志的当前时间之前的相对时间(以秒为单位)。如果此值早于 Pod 启动时间, 则仅返回自 Pod 启动以来的日志。如果此值是将来的值,则不会返回任何日志。 只能指定 sinceSecondssinceTime 之一。

  • tailLines (查询参数): integer

    如果设置,则从日志末尾开始显示的行数。如果未指定,则从容器创建或 sinceSecondssinceTime 时刻显示日志。

  • timestamps (查询参数):boolean

    如果为 true,则在每行日志输出的开头添加 RFC3339 或 RFC3339Nano 时间戳。默认为 false。

响应

200 (string): OK

401: Unauthorized

get 读取指定 Pod 的状态

HTTP 请求

GET /api/v1/namespaces/{namespace}/pods/{name}/status

参数

  • name (路径参数): string,必需

    Pod 的名称

  • namespace (路径参数): string,必需

    namespace

  • pretty (查询参数): string

    pretty

响应

200 (Pod): OK

401: Unauthorized

list 列出或观察 Pod 种类的对象

HTTP 请求

GET /api/v1/namespaces/{namespace}/pods

参数

响应

200 (PodList): OK

401: Unauthorized

list 列出或观察 Pod 种类的对象

HTTP 请求

GET /api/v1/pods

参数

响应

200 (PodList): OK

401: Unauthorized

create 创建一个 Pod

HTTP 请求

POST /api/v1/namespaces/{namespace}/pods

参数

  • namespace (路径参数): string,必需

    namespace

  • bodyPod,必需

  • dryRun (查询参数): string

    dryRun

响应

200 (Pod): OK

201 (Pod): Created

202 (Pod): Accepted

401: Unauthorized

update 替换指定的 Pod

HTTP 请求

PUT /api/v1/namespaces/{namespace}/pods/{name}

参数

  • name (路径参数): string,必需

    Pod 的名称。

  • namespace (路径参数): string,必需

    namespace

  • bodyPod,必需

  • fieldValidat