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

返回本页常规视图.

文档

1 - Kubernetes 文档

Kubernetes 是一个开源的容器编排引擎,用来对容器化应用进行自动部署、扩缩和管理。此开源项目由云原生计算基金会(CNCF)托管。

1.1 - Kubernetes 文档支持的版本

本网站包含当前版本和之前四个版本的 Kubernetes 文档。

Kubernetes 版本的文档可用性与当前是否支持该版本是分开的。 阅读支持期限,了解官方支持 Kubernetes 的哪些版本,以及支持多长时间。

2 - 入门

本节列出了设置和运行 Kubernetes 的不同方法。 安装 Kubernetes 时,请根据以下条件选择安装类型:易于维护、安全性、可控制性、可用资源以及操作和管理 Kubernetes 集群所需的专业知识。

你可以下载 Kubernetes,在本地机器、云或你自己的数据中心上部署 Kubernetes 集群。

诸如 kube-apiserverkube-proxy 等某些 Kubernetes 组件可以在集群中以容器镜像部署。

建议尽可能将 Kubernetes 组件作为容器镜像运行,并且让 Kubernetes 管理这些组件。 但是运行容器的相关组件 —— 尤其是 kubelet,不在此列。

如果你不想自己管理 Kubernetes 集群,则可以选择托管服务,包括经过认证的平台。 在各种云和裸机环境中,还有其他标准化和定制的解决方案。

学习环境

如果正打算学习 Kubernetes,请使用 Kubernetes 社区支持 或生态系统中的工具在本地计算机上设置 Kubernetes 集群。 请参阅安装工具

生产环境

在评估生产环境的解决方案时, 请考虑要自己管理 Kubernetes 集群(或相关抽象)的哪些方面,将哪些托付给提供商。

对于你自己管理的集群,官方支持的用于部署 Kubernetes 的工具是 kubeadm

接下来

Kubernetes 的设计是让其控制平面在 Linux 上运行的。 在集群中,你可以在 Linux 或其他操作系统(包括 Windows)上运行应用程序。

2.1 - 学习环境

2.2 - 生产环境

生产质量的 Kubernetes 集群需要规划和准备。 如果你的 Kubernetes 集群是用来运行关键负载的,该集群必须被配置为弹性的(Resilient)。 本页面阐述你在安装生产就绪的集群或将现有集群升级为生产用途时可以遵循的步骤。 如果你已经熟悉生产环境安装,因此只关注一些链接,则可以跳到接下来节。

生产环境考量

通常,一个生产用 Kubernetes 集群环境与个人学习、开发或测试环境所使用的 Kubernetes 相比有更多的需求。 生产环境可能需要被很多用户安全地访问,需要提供一致的可用性,以及能够与需求变化相适配的资源。

在你决定在何处运行你的生产用 Kubernetes 环境(在本地或者在云端), 以及你希望承担或交由他人承担的管理工作量时, 需要考察以下因素如何影响你对 Kubernetes 集群的需求:

  • 可用性:一个单机的 Kubernetes 学习环境 具有单点失效特点。创建高可用的集群则意味着需要考虑:
    • 将控制面与工作节点分开
    • 在多个节点上提供控制面组件的副本
    • 为针对集群的 API 服务器 的流量提供负载均衡
    • 随着负载的合理需要,提供足够的可用的(或者能够迅速变为可用的)工作节点
  • 规模:如果你预期你的生产用 Kubernetes 环境要承受固定量的请求, 你可能可以针对所需要的容量来一次性完成安装。 不过,如果你预期服务请求会随着时间增长,或者因为类似季节或者特殊事件的原因而发生剧烈变化, 你就需要规划如何处理请求上升时对控制面和工作节点的压力,或者如何缩减集群规模以减少未使用资源的消耗。
  • 安全性与访问管理:在你自己的学习环境 Kubernetes 集群上,你拥有完全的管理员特权。 但是针对运行着重要工作负载的共享集群,用户账户不止一两个时, 就需要更细粒度的方案来确定谁或者哪些主体可以访问集群资源。 你可以使用基于角色的访问控制(RBAC) 和其他安全机制来确保用户和负载能够访问到所需要的资源, 同时确保工作负载及集群自身仍然是安全的。 你可以通过管理策略容器资源 来针对用户和工作负载所可访问的资源设置约束。

在自行构建 Kubernetes 生产环境之前, 请考虑将这一任务的部分或者全部交给云方案承包服务提供商或者其他 Kubernetes 合作伙伴。选项有:

  • 无服务:仅是在第三方设备上运行负载,完全不必管理集群本身。 你需要为 CPU 用量、内存和磁盘请求等付费。
  • 托管控制面:让供应商决定集群控制面的规模和可用性,并负责打补丁和升级等操作。
  • 托管工作节点:配置一个节点池来满足你的需要,由供应商来确保节点始终可用,并在需要的时候完成升级。
  • 集成:有一些供应商能够将 Kubernetes 与一些你可能需要的其他服务集成, 这类服务包括存储、容器镜像仓库、身份认证方法以及开发工具等。

无论你是自行构造一个生产用 Kubernetes 集群还是与合作伙伴一起协作, 请审阅下面章节以评估你的需求,因为这关系到你的集群的控制面工作节点用户访问以及负载资源

生产用集群安装

在生产质量的 Kubernetes 集群中,控制面用不同的方式来管理集群和可以分布到多个计算机上的服务。 每个工作节点则代表的是一个可配置来运行 Kubernetes Pod 的实体。

生产用控制面

最简单的 Kubernetes 集群中,整个控制面和工作节点服务都运行在同一台机器上。 你可以通过添加工作节点来提升环境运算能力,正如 Kubernetes 组件示意图所示。 如果只需要集群在很短的一段时间内可用,或者可以在某些事物出现严重问题时直接丢弃, 这种配置可能符合你的需要。

如果你需要一个更为持久的、高可用的集群,那么就需要考虑扩展控制面的方式。 根据设计,运行在一台机器上的单机控制面服务不是高可用的。 如果你认为保持集群的正常运行并需要确保它在出错时可以被修复是很重要的, 可以考虑以下步骤:

  • 选择部署工具:你可以使用类似 kubeadm、kops 和 kubespray 这类工具来部署控制面。 参阅使用部署工具安装 Kubernetes 以了解使用这类部署方法来完成生产就绪部署的技巧。 存在不同的容器运行时 可供你的部署采用。
  • 管理证书:控制面服务之间的安全通信是通过证书来完成的。 证书是在部署期间自动生成的,或者你也可以使用自己的证书机构来生成它们。 参阅 PKI 证书和需求了解细节。
  • 为 API 服务器配置负载均衡:配置负载均衡器来将外部的 API 请求散布给运行在不同节点上的 API 服务实例。 参阅创建外部负载均衡器了解细节。
  • 分离并备份 etcd 服务:etcd 服务可以运行于其他控制面服务所在的机器上, 也可以运行在不同的机器上以获得更好的安全性和可用性。 因为 etcd 存储着集群的配置数据,应该经常性地对 etcd 数据库进行备份, 以确保在需要的时候你可以修复该数据库。与配置和使用 etcd 相关的细节可参阅 etcd FAQ。 更多的细节可参阅为 Kubernetes 运维 etcd 集群使用 kubeadm 配置高可用的 etcd 集群
  • 创建多控制面系统:为了实现高可用性,控制面不应被限制在一台机器上。 如果控制面服务是使用某 init 服务(例如 systemd)来运行的,每个服务应该至少运行在三台机器上。 不过,将控制面作为服务运行在 Kubernetes Pod 中可以确保你所请求的个数的服务始终保持可用。 调度器应该是可容错的,但不是高可用的。 某些部署工具会安装 Raft 票选算法来对 Kubernetes 服务执行领导者选举。 如果主节点消失,另一个服务会被选中并接手相应服务。
  • 跨多个可用区:如果保持你的集群一直可用这点非常重要,可以考虑创建一个跨多个数据中心的集群; 在云环境中,这些数据中心被视为可用区。若干个可用区在一起可构成地理区域。 通过将集群分散到同一区域中的多个可用区内,即使某个可用区不可用,整个集群能够继续工作的机会也大大增加。 更多的细节可参阅跨多个可用区运行
  • 管理演进中的特性:如果你计划长时间保留你的集群,就需要执行一些维护其健康和安全的任务。 例如,如果你采用 kubeadm 安装的集群, 则有一些可以帮助你完成 证书管理升级 kubeadm 集群 的指令。 参见管理集群了解一个 Kubernetes 管理任务的较长列表。

如要了解运行控制面服务时可使用的选项,可参阅 kube-apiserverkube-controller-managerkube-scheduler 组件参考页面。 如要了解高可用控制面的例子,可参阅高可用拓扑结构选项使用 kubeadm 创建高可用集群 以及为 Kubernetes 运维 etcd 集群。 关于制定 etcd 备份计划,可参阅对 etcd 集群执行备份

生产用工作节点

生产质量的工作负载需要是弹性的;它们所依赖的其他组件(例如 CoreDNS)也需要是弹性的。 无论你是自行管理控制面还是让云供应商来管理,你都需要考虑如何管理工作节点 (有时也简称为节点)。

  • 配置节点:节点可以是物理机或者虚拟机。如果你希望自行创建和管理节点, 你可以安装一个受支持的操作系统,之后添加并运行合适的节点服务。考虑:
    • 在安装节点时要通过配置适当的内存、CPU 和磁盘读写速率、存储容量来满足你的负载的需求。
    • 是否通用的计算机系统即足够,还是你有负载需要使用 GPU 处理器、Windows 节点或者 VM 隔离。
  • 验证节点:参阅验证节点配置以了解如何确保节点满足加入到 Kubernetes 集群的需求。
  • 添加节点到集群中:如果你自行管理你的集群,你可以通过安装配置你的机器, 之后或者手动加入集群,或者让它们自动注册到集群的 API 服务器。 参阅节点节,了解如何配置 Kubernetes 以便以这些方式来添加节点。
  • 扩缩节点:制定一个扩充集群容量的规划,你的集群最终会需要这一能力。 参阅大规模集群考察事项 以确定你所需要的节点数; 这一规模是基于你要运行的 Pod 和容器个数来确定的。 如果你自行管理集群节点,这可能意味着要购买和安装你自己的物理设备。
  • 节点自动扩缩容:查阅集群自动扩缩容, 了解可以自动管理节点的工具及其提供的能力。
  • 安装节点健康检查:对于重要的工作负载,你会希望确保节点以及在节点上运行的 Pod 处于健康状态。 通过使用 Node Problem Detector, 你可以确保你的节点是健康的。

生产级用户环境

在生产环境中,情况可能不再是你或者一小组人在访问集群,而是几十上百人需要访问集群。 在学习环境或者平台原型环境中,你可能具有一个可以执行任何操作的管理账号。 在生产环境中,你会需要对不同名字空间具有不同访问权限级别的很多账号。

建立一个生产级别的集群意味着你需要决定如何有选择地允许其他用户访问集群。 具体而言,你需要选择验证尝试访问集群的人的身份标识(身份认证), 并确定他们是否被许可执行他们所请求的操作(鉴权):

  • 认证(Authentication):API 服务器可以使用客户端证书、持有者令牌、 身份认证代理或者 HTTP 基本认证机制来完成身份认证操作。 你可以选择你要使用的认证方法。通过使用插件, API 服务器可以充分利用你所在组织的现有身份认证方法, 例如 LDAP 或者 Kerberos。 关于认证 Kubernetes 用户身份的不同方法的描述, 可参阅身份认证
  • 鉴权(Authorization):当你准备为一般用户执行权限判定时, 你可能会需要在 RBAC 和 ABAC 鉴权机制之间做出选择。 参阅鉴权概述, 了解对用户账户(以及访问你的集群的服务账户)执行鉴权的不同模式。
    • 基于角色的访问控制RBAC): 让你通过为通过身份认证的用户授权特定的许可集合来控制集群访问。 访问许可可以针对某特定名字空间(Role)或者针对整个集群(ClusterRole)。 通过使用 RoleBinding 和 ClusterRoleBinding 对象,这些访问许可可以被关联到特定的用户身上。
    • 基于属性的访问控制ABAC): 让你能够基于集群中资源的属性来创建访问控制策略,基于对应的属性来决定允许还是拒绝访问。 策略文件的每一行都给出版本属性(apiVersion 和 kind)以及一个规约属性的映射, 用来匹配主体(用户或组)、资源属性、非资源属性(/version 或 /apis)和只读属性。 参阅示例以了解细节。

作为在你的生产用 Kubernetes 集群中安装身份认证和鉴权机制的负责人,要考虑的事情如下:

  • 设置鉴权模式:当 Kubernetes API 服务器(kube-apiserver)启动时, 所支持的鉴权模式必须使用 --authorization-mode 标志配置。 例如,kube-apiserver.yaml(位于 /etc/kubernetes/manifests 下)中对应的标志可以设置为 Node,RBAC。 这样就会针对已完成身份认证的请求执行 Node 和 RBAC 鉴权。
  • 创建用户证书和角色绑定(RBAC):如果你在使用 RBAC 鉴权,用户可以创建由集群 CA 签名的 CertificateSigningRequest(CSR)。接下来你就可以将 Role 和 ClusterRole 绑定到每个用户身上。 参阅证书签名请求了解细节。
  • 创建组合属性的策略(ABAC):如果你在使用 ABAC 鉴权, 你可以设置属性组合以构造策略对所选用户或用户组执行鉴权, 判定他们是否可访问特定的资源(例如 Pod)、名字空间或者 apiGroup。 进一步的详细信息可参阅示例
  • 考虑准入控制器:针对指向 API 服务器的请求的其他鉴权形式还包括 Webhook 令牌认证。 Webhook 和其他特殊的鉴权类型需要通过向 API 服务器添加准入控制器来启用。

为负载资源设置约束

生产环境负载的需求可能对 Kubernetes 的控制面内外造成压力。 在针对你的集群的负载执行配置时,要考虑以下条目:

  • 为 DNS 请求做准备:如果你希望工作负载能够完成大规模扩展,你的 DNS 服务也必须能够扩大规模。 参阅自动扩缩集群中 DNS 服务
  • 创建额外的服务账户:用户账户决定用户可以在集群上执行的操作,服务账号则定义的是在特定名字空间中 Pod 的访问权限。默认情况下,Pod 使用所在名字空间中的 default 服务账号。 参阅管理服务账号以了解如何创建新的服务账号。 例如,你可能需要:

接下来

2.2.1 - 容器运行时

你需要在集群内每个节点上安装一个 容器运行时 以使 Pod 可以运行在上面。本文概述了所涉及的内容并描述了与节点设置相关的任务。

Kubernetes 1.30 要求你使用符合容器运行时接口(CRI)的运行时。

有关详细信息,请参阅 CRI 版本支持。 本页简要介绍在 Kubernetes 中几个常见的容器运行时的用法。

安装和配置先决条件

默认情况下,Linux 内核不允许 IPv4 数据包在接口之间路由。 大多数 Kubernetes 集群网络实现都会更改此设置(如果需要),但有些人可能希望管理员为他们执行此操作。 (有些人可能还期望设置其他 sysctl 参数、加载内核模块等;请参阅你的特定网络实施的文档。)

启用 IPv4 数据包转发

手动启用 IPv4 数据包转发:

# 设置所需的 sysctl 参数,参数在重新启动后保持不变
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.ipv4.ip_forward = 1
EOF

# 应用 sysctl 参数而不重新启动
sudo sysctl --system

使用以下命令验证 net.ipv4.ip_forward 是否设置为 1:

sysctl net.ipv4.ip_forward

cgroup 驱动

在 Linux 上,控制组(CGroup)用于限制分配给进程的资源。

kubelet 和底层容器运行时都需要对接控制组来强制执行 为 Pod 和容器管理资源 并为诸如 CPU、内存这类资源设置请求和限制。若要对接控制组,kubelet 和容器运行时需要使用一个 cgroup 驱动。 关键的一点是 kubelet 和容器运行时需使用相同的 cgroup 驱动并且采用相同的配置。

可用的 cgroup 驱动有两个:

cgroupfs 驱动

cgroupfs 驱动是 kubelet 中默认的 cgroup 驱动。 当使用 cgroupfs 驱动时, kubelet 和容器运行时将直接对接 cgroup 文件系统来配置 cgroup。

systemd 是初始化系统时, 推荐使用 cgroupfs 驱动,因为 systemd 期望系统上只有一个 cgroup 管理器。 此外,如果你使用 cgroup v2, 则应用 systemd cgroup 驱动取代 cgroupfs

systemd cgroup 驱动

当某个 Linux 系统发行版使用 systemd 作为其初始化系统时,初始化进程会生成并使用一个 root 控制组(cgroup),并充当 cgroup 管理器。

systemd 与 cgroup 集成紧密,并将为每个 systemd 单元分配一个 cgroup。 因此,如果你 systemd 用作初始化系统,同时使用 cgroupfs 驱动,则系统中会存在两个不同的 cgroup 管理器。

同时存在两个 cgroup 管理器将造成系统中针对可用的资源和使用中的资源出现两个视图。某些情况下, 将 kubelet 和容器运行时配置为使用 cgroupfs、但为剩余的进程使用 systemd 的那些节点将在资源压力增大时变得不稳定。

当 systemd 是选定的初始化系统时,缓解这个不稳定问题的方法是针对 kubelet 和容器运行时将 systemd 用作 cgroup 驱动。

要将 systemd 设置为 cgroup 驱动,需编辑 KubeletConfigurationcgroupDriver 选项,并将其设置为 systemd。例如:

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
...
cgroupDriver: systemd

在 Kubernetes v1.28 中,启用 KubeletCgroupDriverFromCRI 特性门控结合支持 RuntimeConfig CRI RPC 的容器运行时,kubelet 会自动从运行时检测适当的 Cgroup 驱动程序,并忽略 kubelet 配置中的 cgroupDriver 设置。

如果你将 systemd 配置为 kubelet 的 cgroup 驱动,你也必须将 systemd 配置为容器运行时的 cgroup 驱动。参阅容器运行时文档,了解指示说明。例如:

将 kubeadm 管理的集群迁移到 systemd 驱动

如果你希望将现有的由 kubeadm 管理的集群迁移到 systemd cgroup 驱动, 请按照配置 cgroup 驱动操作。

CRI 版本支持

你的容器运行时必须至少支持 v1alpha2 版本的容器运行时接口。

Kubernetes 从 1.26 版本开始仅适用于 v1 版本的容器运行时(CRI)API。早期版本默认为 v1 版本, 但是如果容器运行时不支持 v1 版本的 API, 则 kubelet 会回退到使用(已弃用的)v1alpha2 版本的 API。

容器运行时

containerd

本节概述了使用 containerd 作为 CRI 运行时的必要步骤。

要在系统上安装 containerd,请按照开始使用 containerd 的说明进行操作。创建有效的 config.toml 配置文件后返回此步骤。

你可以在路径 /etc/containerd/config.toml 下找到此文件。

你可以在路径 C:\Program Files\containerd\config.toml 下找到此文件。

在 Linux 上,containerd 的默认 CRI 套接字是 /run/containerd/containerd.sock。 在 Windows 上,默认 CRI 端点是 npipe://./pipe/containerd-containerd

配置 systemd cgroup 驱动

结合 runc 使用 systemd cgroup 驱动,在 /etc/containerd/config.toml 中设置:

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
  ...
  [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
    SystemdCgroup = true

如果你使用 cgroup v2,则推荐 systemd cgroup 驱动。

如果你应用此更改,请确保重新启动 containerd:

sudo systemctl restart containerd

当使用 kubeadm 时,请手动配置 kubelet 的 cgroup 驱动

在 Kubernetes v1.28 中,你可以启用 Cgroup 驱动程序的自动检测的 Alpha 级别特性。 详情参阅 systemd cgroup 驱动

重载沙箱(pause)镜像

在你的 containerd 配置中, 你可以通过设置以下选项重载沙箱镜像:

[plugins."io.containerd.grpc.v1.cri"]
  sandbox_image = "registry.k8s.io/pause:3.2"

一旦你更新了这个配置文件,可能就同样需要重启 containerdsystemctl restart containerd

请注意,声明匹配的 pod-infra-container-image 是 kubelet 的最佳实践。 如果未配置,kubelet 可能会尝试对 pause 镜像进行垃圾回收。 containerd 固定 pause 镜像的工作正在进行中, 将不再需要在 kubelet 上进行此设置。

CRI-O

本节包含安装 CRI-O 作为容器运行时的必要步骤。

要安装 CRI-O,请按照 CRI-O 安装说明执行操作。

cgroup 驱动

CRI-O 默认使用 systemd cgroup 驱动,这对你来说可能工作得很好。 要切换到 cgroupfs cgroup 驱动,请编辑 /etc/crio/crio.conf 或在 /etc/crio/crio.conf.d/02-cgroup-manager.conf 中放置一个插入式配置,例如:

[crio.runtime]
conmon_cgroup = "pod"
cgroup_manager = "cgroupfs"

你还应该注意当使用 CRI-O 时,并且 CRI-O 的 cgroup 设置为 cgroupfs 时,必须将 conmon_cgroup 设置为值 pod。 通常需要保持 kubelet 的 cgroup 驱动配置(通常通过 kubeadm 完成)和 CRI-O 同步。

在 Kubernetes v1.28 中,你可以启用 Cgroup 驱动程序的自动检测的 Alpha 级别特性。 详情参阅 systemd cgroup 驱动

对于 CRI-O,CRI 套接字默认为 /var/run/crio/crio.sock

重载沙箱(pause)镜像

在你的 CRI-O 配置中, 你可以设置以下配置值:

[crio.image]
pause_image="registry.k8s.io/pause:3.6"

这一设置选项支持动态配置重加载来应用所做变更:systemctl reload crio。 也可以通过向 crio 进程发送 SIGHUP 信号来实现。

Docker Engine

  1. 在你的每个节点上,遵循安装 Docker Engine 指南为你的 Linux 发行版安装 Docker。
  1. 请按照文档中的安装部分指示来安装 cri-dockerd

对于 cri-dockerd,默认情况下,CRI 套接字是 /run/cri-dockerd.sock

Mirantis 容器运行时

Mirantis Container Runtime (MCR) 是一种商用容器运行时,以前称为 Docker 企业版。 你可以使用 MCR 中包含的开源 cri-dockerd 组件将 Mirantis Container Runtime 与 Kubernetes 一起使用。

要了解有关如何安装 Mirantis Container Runtime 的更多信息, 请访问 MCR 部署指南

检查名为 cri-docker.socket 的 systemd 单元以找出 CRI 套接字的路径。

重载沙箱(pause)镜像

cri-dockerd 适配器能够接受指定用作 Pod 的基础容器的容器镜像(“pause 镜像”)作为命令行参数。 要使用的命令行参数是 --pod-infra-container-image

接下来

除了容器运行时,你的集群还需要有效的网络插件

2.2.2 - 使用部署工具安装 Kubernetes

搭建你自己的 Kubernetes 生产集群有许多方法和工具。例如:

  • kops:自动化集群制备工具。 有关教程、最佳实践、配置选项和社区联系信息,请查阅 kOps 网站

2.2.2.1 - 使用 kubeadm 引导集群

2.2.2.1.1 - 安装 kubeadm

本页面显示如何安装 kubeadm 工具箱。 有关在执行此安装过程后如何使用 kubeadm 创建集群的信息, 请参见使用 kubeadm 创建集群

This installation guide is for Kubernetes v1.30. If you want to use a different Kubernetes version, please refer to the following pages instead:

准备开始

  • 一台兼容的 Linux 主机。Kubernetes 项目为基于 Debian 和 Red Hat 的 Linux 发行版以及一些不提供包管理器的发行版提供通用的指令。
  • 每台机器 2 GB 或更多的 RAM(如果少于这个数字将会影响你应用的运行内存)。
  • CPU 2 核心及以上。
  • 集群中的所有机器的网络彼此均能相互连接(公网和内网都可以)。
  • 节点之中不可以有重复的主机名、MAC 地址或 product_uuid。请参见这里了解更多详细信息。
  • 开启机器上的某些端口。请参见这里了解更多详细信息。
  • 交换分区的配置。kubelet 的默认行为是在节点上检测到交换内存时无法启动。 kubelet 自 v1.22 起已开始支持交换分区。自 v1.28 起,仅针对 cgroup v2 支持交换分区; kubelet 的 NodeSwap 特性门控处于 Beta 阶段,但默认被禁用。
    • 如果 kubelet 未被正确配置使用交换分区,则你必须禁用交换分区。 例如,sudo swapoff -a 将暂时禁用交换分区。要使此更改在重启后保持不变,请确保在如 /etc/fstabsystemd.swap 等配置文件中禁用交换分区,具体取决于你的系统如何配置。

确保每个节点上 MAC 地址和 product_uuid 的唯一性

  • 你可以使用命令 ip linkifconfig -a 来获取网络接口的 MAC 地址
  • 可以使用 sudo cat /sys/class/dmi/id/product_uuid 命令对 product_uuid 校验

一般来讲,硬件设备会拥有唯一的地址,但是有些虚拟机的地址可能会重复。 Kubernetes 使用这些值来唯一确定集群中的节点。 如果这些值在每个节点上不唯一,可能会导致安装失败

检查网络适配器

如果你有一个以上的网络适配器,同时你的 Kubernetes 组件通过默认路由不可达,我们建议你预先添加 IP 路由规则, 这样 Kubernetes 集群就可以通过对应的适配器完成连接。

检查所需端口

启用这些必要的端口后才能使 Kubernetes 的各组件相互通信。 可以使用 netcat 之类的工具来检查端口是否开放,例如:

nc 127.0.0.1 6443 -v

你使用的 Pod 网络插件 (详见后续章节) 也可能需要开启某些特定端口。 由于各个 Pod 网络插件的功能都有所不同,请参阅他们各自文档中对端口的要求。

安装容器运行时

为了在 Pod 中运行容器,Kubernetes 使用 容器运行时(Container Runtime)

默认情况下,Kubernetes 使用 容器运行时接口(Container Runtime Interface,CRI) 来与你所选择的容器运行时交互。

如果你不指定运行时,kubeadm 会自动尝试通过扫描已知的端点列表来检测已安装的容器运行时。

如果检测到有多个或者没有容器运行时,kubeadm 将抛出一个错误并要求你指定一个想要使用的运行时。

参阅容器运行时 以了解更多信息。

下面的表格包括被支持的操作系统的已知端点。

Linux 容器运行时
运行时Unix 域套接字
containerdunix:///var/run/containerd/containerd.sock
CRI-Ounix:///var/run/crio/crio.sock
Docker Engine(使用 cri-dockerd)unix:///var/run/cri-dockerd.sock

Windows 容器运行时
运行时Windows 命名管道路径
containerdnpipe:////./pipe/containerd-containerd
Docker Engine(使用 cri-dockerd)npipe:////./pipe/cri-dockerd

安装 kubeadm、kubelet 和 kubectl

你需要在每台机器上安装以下的软件包:

  • kubeadm:用来初始化集群的指令。

  • kubelet:在集群中的每个节点上用来启动 Pod 和容器等。

  • kubectl:用来与集群通信的命令行工具。

kubeadm 不能帮你安装或者管理 kubeletkubectl, 所以你需要确保它们与通过 kubeadm 安装的控制平面的版本相匹配。 如果不这样做,则存在发生版本偏差的风险,可能会导致一些预料之外的错误和问题。 然而,控制平面与 kubelet 之间可以存在一个次要版本的偏差,但 kubelet 的版本不可以超过 API 服务器的版本。 例如,1.7.0 版本的 kubelet 可以完全兼容 1.8.0 版本的 API 服务器,反之则不可以。

有关安装 kubectl 的信息,请参阅安装和设置 kubectl 文档。

关于版本偏差的更多信息,请参阅以下文档:

以下指令适用于 Kubernetes 1.30.

  1. 更新 apt 包索引并安装使用 Kubernetes apt 仓库所需要的包:

    sudo apt-get update
    # apt-transport-https 可能是一个虚拟包(dummy package);如果是的话,你可以跳过安装这个包
    sudo apt-get install -y apt-transport-https ca-certificates curl gpg
    
  1. 下载用于 Kubernetes 软件包仓库的公共签名密钥。所有仓库都使用相同的签名密钥,因此你可以忽略URL中的版本:

    # 如果 `/etc/apt/keyrings` 目录不存在,则应在 curl 命令之前创建它,请阅读下面的注释。
    # sudo mkdir -p -m 755 /etc/apt/keyrings
    curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
    
  1. 添加 Kubernetes apt 仓库。 请注意,此仓库仅包含适用于 Kubernetes 1.30 的软件包; 对于其他 Kubernetes 次要版本,则需要更改 URL 中的 Kubernetes 次要版本以匹配你所需的次要版本 (你还应该检查正在阅读的安装文档是否为你计划安装的 Kubernetes 版本的文档)。

    # 此操作会覆盖 /etc/apt/sources.list.d/kubernetes.list 中现存的所有配置。
    echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.30/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
    
  1. 更新 apt 包索引,安装 kubelet、kubeadm 和 kubectl,并锁定其版本:

    sudo apt-get update
    sudo apt-get install -y kubelet kubeadm kubectl
    sudo apt-mark hold kubelet kubeadm kubectl
    

  1. 将 SELinux 设置为 permissive 模式:

    以下指令适用于 Kubernetes 1.30。

    # 将 SELinux 设置为 permissive 模式(相当于将其禁用)
    sudo setenforce 0
    sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config
    
  1. 添加 Kubernetes 的 yum 仓库。在仓库定义中的 exclude 参数确保了与 Kubernetes 相关的软件包在运行 yum update 时不会升级,因为升级 Kubernetes 需要遵循特定的过程。请注意,此仓库仅包含适用于 Kubernetes 1.30 的软件包; 对于其他 Kubernetes 次要版本,则需要更改 URL 中的 Kubernetes 次要版本以匹配你所需的次要版本 (你还应该检查正在阅读的安装文档是否为你计划安装的 Kubernetes 版本的文档)。

    # 此操作会覆盖 /etc/yum.repos.d/kubernetes.repo 中现存的所有配置
    cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
    [kubernetes]
    name=Kubernetes
    baseurl=https://pkgs.k8s.io/core:/stable:/v1.30/rpm/
    enabled=1
    gpgcheck=1
    gpgkey=https://pkgs.k8s.io/core:/stable:/v1.30/rpm/repodata/repomd.xml.key
    exclude=kubelet kubeadm kubectl cri-tools kubernetes-cni
    EOF
    
  1. 安装 kubelet、kubeadm 和 kubectl,并启用 kubelet 以确保它在启动时自动启动:

    sudo yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes
    sudo systemctl enable --now kubelet
    

安装 CNI 插件(大多数 Pod 网络都需要):

CNI_PLUGINS_VERSION="v1.3.0"
ARCH="amd64"
DEST="/opt/cni/bin"
sudo mkdir -p "$DEST"
curl -L "https://github.com/containernetworking/plugins/releases/download/${CNI_PLUGINS_VERSION}/cni-plugins-linux-${ARCH}-${CNI_PLUGINS_VERSION}.tgz" | sudo tar -C "$DEST" -xz

定义要下载命令文件的目录:

DOWNLOAD_DIR="/usr/local/bin"
sudo mkdir -p "$DOWNLOAD_DIR"

安装 crictl(kubeadm/kubelet 容器运行时接口(CRI)所需):

CRICTL_VERSION="v1.28.0"
ARCH="amd64"
curl -L "https://github.com/kubernetes-sigs/cri-tools/releases/download/${CRICTL_VERSION}/crictl-${CRICTL_VERSION}-linux-${ARCH}.tar.gz" | sudo tar -C $DOWNLOAD_DIR -xz

安装 kubeadmkubeletkubectl 并添加 kubelet 系统服务:

RELEASE="$(curl -sSL https://dl.k8s.io/release/stable.txt)"
ARCH="amd64"
cd $DOWNLOAD_DIR
sudo curl -L --remote-name-all https://dl.k8s.io/release/${RELEASE}/bin/linux/${ARCH}/{kubeadm,kubelet}
sudo chmod +x {kubeadm,kubelet}

RELEASE_VERSION="v0.16.2"
curl -sSL "https://raw.githubusercontent.com/kubernetes/release/${RELEASE_VERSION}/cmd/krel/templates/latest/kubelet/kubelet.service" | sed "s:/usr/bin:${DOWNLOAD_DIR}:g" | sudo tee /usr/lib/systemd/system/kubelet.service
sudo mkdir -p /usr/lib/systemd/system/kubelet.service.d
curl -sSL "https://raw.githubusercontent.com/kubernetes/release/${RELEASE_VERSION}/cmd/krel/templates/latest/kubeadm/10-kubeadm.conf" | sed "s:/usr/bin:${DOWNLOAD_DIR}:g" | sudo tee /usr/lib/systemd/system/kubelet.service.d/10-kubeadm.conf

请参照安装工具页面的说明安装 kubelet。 激活并启动 kubelet

systemctl enable --now kubelet

kubelet 现在每隔几秒就会重启,因为它陷入了一个等待 kubeadm 指令的死循环。

配置 cgroup 驱动程序

容器运行时和 kubelet 都具有名字为 "cgroup driver" 的属性,该属性对于在 Linux 机器上管理 CGroups 而言非常重要。

故障排查

如果你在使用 kubeadm 时遇到困难, 请参阅我们的故障排查文档

接下来

2.2.2.1.2 - 对 kubeadm 进行故障排查

与任何程序一样,你可能会在安装或者运行 kubeadm 时遇到错误。 本文列举了一些常见的故障场景,并提供可帮助你理解和解决这些问题的步骤。

如果你的问题未在下面列出,请执行以下步骤:

  • 如果你认为问题是 kubeadm 的错误:

  • 如果你对 kubeadm 的工作方式有疑问,可以在 Slack 上的 #kubeadm 频道提问, 或者在 StackOverflow 上提问。 请加入相关标签,例如 #kubernetes#kubeadm,这样其他人可以帮助你。

由于缺少 RBAC,无法将 v1.18 Node 加入 v1.17 集群

自从 v1.18 后,如果集群中已存在同名 Node,kubeadm 将禁止 Node 加入集群。 这需要为 bootstrap-token 用户添加 RBAC 才能 GET Node 对象。

但这会导致一个问题,v1.18 的 kubeadm join 无法加入由 kubeadm v1.17 创建的集群。

要解决此问题,你有两种选择:

使用 kubeadm v1.18 在控制平面节点上执行 kubeadm init phase bootstrap-token。 请注意,这也会启用 bootstrap-token 的其余权限。

或者,也可以使用 kubectl apply -f ... 手动应用以下 RBAC:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: kubeadm:get-nodes
rules:
- apiGroups:
  - ""
  resources:
  - nodes
  verbs:
  - get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: kubeadm:get-nodes
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: kubeadm:get-nodes
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: system:bootstrappers:kubeadm:default-node-token

在安装过程中没有找到 ebtables 或者其他类似的可执行文件

如果在运行 kubeadm init 命令时,遇到以下的警告

[preflight] WARNING: ebtables not found in system path
[preflight] WARNING: ethtool not found in system path

那么或许在你的节点上缺失 ebtablesethtool 或者类似的可执行文件。 你可以使用以下命令安装它们:

  • 对于 Ubuntu/Debian 用户,运行 apt install ebtables ethtool 命令。
  • 对于 CentOS/Fedora 用户,运行 yum install ebtables ethtool 命令。

在安装过程中,kubeadm 一直等待控制平面就绪

如果你注意到 kubeadm init 在打印以下行后挂起:

[apiclient] Created API client, waiting for the control plane to become ready

这可能是由许多问题引起的。最常见的是:

  • 网络连接问题。在继续之前,请检查你的计算机是否具有全部联通的网络连接。
  • 容器运行时的 cgroup 驱动不同于 kubelet 使用的 cgroup 驱动。要了解如何正确配置 cgroup 驱动, 请参阅配置 cgroup 驱动
  • 控制平面上的 Docker 容器持续进入崩溃状态或(因其他原因)挂起。你可以运行 docker ps 命令来检查以及 docker logs 命令来检视每个容器的运行日志。 对于其他容器运行时,请参阅使用 crictl 对 Kubernetes 节点进行调试

当删除托管容器时 kubeadm 阻塞

如果容器运行时停止并且未删除 Kubernetes 所管理的容器,可能发生以下情况:

sudo kubeadm reset
[preflight] Running pre-flight checks
[reset] Stopping the kubelet service
[reset] Unmounting mounted directories in "/var/lib/kubelet"
[reset] Removing kubernetes-managed containers
(block)

一个可行的解决方案是重新启动 Docker 服务,然后重新运行 kubeadm reset: 你也可以使用 crictl 来调试容器运行时的状态。 参见使用 CRICTL 调试 Kubernetes 节点

Pod 处于 RunContainerErrorCrashLoopBackOff 或者 Error 状态

kubeadm init 命令运行后,系统中不应该有 Pod 处于这类状态。

  • kubeadm init 命令执行完后,如果有 Pod 处于这些状态之一,请在 kubeadm 仓库提起一个 issue。coredns (或者 kube-dns) 应该处于 Pending 状态, 直到你部署了网络插件为止。

  • 如果在部署完网络插件之后,有 Pod 处于 RunContainerErrorCrashLoopBackOffError 状态之一,并且 coredns (或者 kube-dns)仍处于 Pending 状态, 那很可能是你安装的网络插件由于某种原因无法工作。你或许需要授予它更多的 RBAC 特权或使用较新的版本。请在 Pod Network 提供商的问题跟踪器中提交问题, 然后在此处分类问题。

coredns 停滞在 Pending 状态

这一行为是预期之中的,因为系统就是这么设计的。kubeadm 的网络供应商是中立的, 因此管理员应该选择安装 Pod 的网络插件。 你必须完成 Pod 的网络配置,然后才能完全部署 CoreDNS。 在网络被配置好之前,DNS 组件会一直处于 Pending 状态。

HostPort 服务无法工作

HostPortHostIP 功能是否可用取决于你的 Pod 网络配置。请联系 Pod 网络插件的作者, 以确认 HostPortHostIP 功能是否可用。

已验证 Calico、Canal 和 Flannel CNI 驱动程序支持 HostPort。

有关更多信息,请参考 CNI portmap 文档.

如果你的网络提供商不支持 portmap CNI 插件,你或许需要使用 NodePort 服务的功能或者使用 HostNetwork=true

无法通过其服务 IP 访问 Pod

  • 许多网络附加组件尚未启用 hairpin 模式 该模式允许 Pod 通过其服务 IP 进行访问。这是与 CNI 有关的问题。 请与网络附加组件提供商联系,以获取他们所提供的 hairpin 模式的最新状态。

  • 如果你正在使用 VirtualBox (直接使用或者通过 Vagrant 使用),你需要 确保 hostname -i 返回一个可路由的 IP 地址。默认情况下,第一个接口连接不能路由的仅主机网络。 解决方法是修改 /etc/hosts,请参考示例 Vagrantfile

TLS 证书错误

以下错误说明证书可能不匹配。

# kubectl get pods
Unable to connect to the server: x509: certificate signed by unknown authority (possibly because of "crypto/rsa: verification error" while trying to verify candidate authority certificate "kubernetes")
  • 验证 $HOME/.kube/config 文件是否包含有效证书, 并在必要时重新生成证书。在 kubeconfig 文件中的证书是 base64 编码的。 该 base64 --decode 命令可以用来解码证书,openssl x509 -text -noout 命令可以用于查看证书信息。

  • 使用如下方法取消设置 KUBECONFIG 环境变量的值:

    unset KUBECONFIG
    

    或者将其设置为默认的 KUBECONFIG 位置:

    export KUBECONFIG=/etc/kubernetes/admin.conf
    
  • 另一个方法是覆盖 kubeconfig 的现有用户 "管理员":

    mv $HOME/.kube $HOME/.kube.bak
    mkdir $HOME/.kube
    sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
    sudo chown $(id -u):$(id -g) $HOME/.kube/config
    

Kubelet 客户端证书轮换失败

默认情况下,kubeadm 使用 /etc/kubernetes/kubelet.conf 中指定的 /var/lib/kubelet/pki/kubelet-client-current.pem 符号链接来配置 kubelet 自动轮换客户端证书。如果此轮换过程失败,你可能会在 kube-apiserver 日志中看到诸如 x509: certificate has expired or is not yet valid 之类的错误。要解决此问题,你必须执行以下步骤:

  1. 从故障节点备份和删除 /etc/kubernetes/kubelet.conf/var/lib/kubelet/pki/kubelet-client*
  2. 在集群中具有 /etc/kubernetes/pki/ca.key 的、正常工作的控制平面节点上 执行 kubeadm kubeconfig user --org system:nodes --client-name system:node:$NODE > kubelet.conf$NODE 必须设置为集群中现有故障节点的名称。 手动修改生成的 kubelet.conf 以调整集群名称和服务器端点, 或传递 kubeconfig user --config (请参阅为其他用户生成 kubeconfig 文件)。 如果你的集群没有 ca.key,你必须在外部对 kubelet.conf 中的嵌入式证书进行签名。
  1. 将得到的 kubelet.conf 文件复制到故障节点上,作为 /etc/kubernetes/kubelet.conf
  2. 在故障节点上重启 kubelet(systemctl restart kubelet),等待 /var/lib/kubelet/pki/kubelet-client-current.pem 重新创建。
  1. 手动编辑 kubelet.conf 指向轮换的 kubelet 客户端证书,方法是将 client-certificate-dataclient-key-data 替换为:

    client-certificate: /var/lib/kubelet/pki/kubelet-client-current.pem
    client-key: /var/lib/kubelet/pki/kubelet-client-current.pem
    
  1. 重新启动 kubelet。
  2. 确保节点状况变为 Ready

在 Vagrant 中使用 flannel 作为 Pod 网络时的默认 NIC

以下错误可能表明 Pod 网络中出现问题:

Error from server (NotFound): the server could not find the requested resource
  • 如果你正在 Vagrant 中使用 flannel 作为 Pod 网络,则必须指定 flannel 的默认接口名称。

    Vagrant 通常为所有 VM 分配两个接口。第一个为所有主机分配了 IP 地址 10.0.2.15,用于获得 NATed 的外部流量。

    这可能会导致 flannel 出现问题,它默认为主机上的第一个接口。这导致所有主机认为它们具有相同的公共 IP 地址。为防止这种情况,传递 --iface eth1 标志给 flannel 以便选择第二个接口。

容器使用的非公共 IP

在某些情况下 kubectl logskubectl run 命令或许会返回以下错误,即便除此之外集群一切功能正常:

Error from server: Get https://10.19.0.41:10250/containerLogs/default/mysql-ddc65b868-glc5m/mysql: dial tcp 10.19.0.41:10250: getsockopt: no route to host
  • 这或许是由于 Kubernetes 使用的 IP 无法与看似相同的子网上的其他 IP 进行通信的缘故, 可能是由机器提供商的政策所导致的。

  • DigitalOcean 既分配一个共有 IP 给 eth0,也分配一个私有 IP 在内部用作其浮动 IP 功能的锚点, 然而 kubelet 将选择后者作为节点的 InternalIP 而不是公共 IP。

    使用 ip addr show 命令代替 ifconfig 命令去检查这种情况,因为 ifconfig 命令 不会显示有问题的别名 IP 地址。或者指定的 DigitalOcean 的 API 端口允许从 droplet 中 查询 anchor IP:

    curl http://169.254.169.254/metadata/v1/interfaces/public/0/anchor_ipv4/address
    

    解决方法是通知 kubelet 使用哪个 --node-ip。当使用 DigitalOcean 时,可以是(分配给 eth0 的)公网 IP, 或者是(分配给 eth1 的)私网 IP。私网 IP 是可选的。 kubadm NodeRegistrationOptions 结构KubeletExtraArgs 部分被用来处理这种情况。

    然后重启 kubelet

    systemctl daemon-reload
    systemctl restart kubelet
    

coredns Pod 有 CrashLoopBackOff 或者 Error 状态

如果有些节点运行的是旧版本的 Docker,同时启用了 SELinux,你或许会遇到 coredns Pod 无法启动的情况。 要解决此问题,你可以尝试以下选项之一:

kubectl -n kube-system get deployment coredns -o yaml | \
  sed 's/allowPrivilegeEscalation: false/allowPrivilegeEscalation: true/g' | \
  kubectl apply -f -

CoreDNS 处于 CrashLoopBackOff 时的另一个原因是当 Kubernetes 中部署的 CoreDNS Pod 检测到环路时。 有许多解决方法 可以避免在每次 CoreDNS 监测到循环并退出时,Kubernetes 尝试重启 CoreDNS Pod 的情况。

etcd Pod 持续重启

如果你遇到以下错误:

rpc error: code = 2 desc = oci runtime error: exec failed: container_linux.go:247: starting container process caused "process_linux.go:110: decoding init error from pipe caused \"read parent: connection reset by peer\""

如果你使用 Docker 1.13.1.84 运行 CentOS 7 就会出现这种问题。 此版本的 Docker 会阻止 kubelet 在 etcd 容器中执行。

为解决此问题,请选择以下选项之一:

  • 回滚到早期版本的 Docker,例如 1.13.1-75

    yum downgrade docker-1.13.1-75.git8633870.el7.centos.x86_64 docker-client-1.13.1-75.git8633870.el7.centos.x86_64 docker-common-1.13.1-75.git8633870.el7.centos.x86_64
    
  • 安装较新的推荐版本之一,例如 18.06:

    sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
    yum install docker-ce-18.06.1.ce-3.el7.x86_64
    

无法将以逗号分隔的值列表传递给 --component-extra-args 标志内的参数

kubeadm init 标志例如 --component-extra-args 允许你将自定义参数传递给像 kube-apiserver 这样的控制平面组件。然而,由于解析 (mapStringString) 的基础类型值,此机制将受到限制。

如果你决定传递一个支持多个逗号分隔值(例如 --apiserver-extra-args "enable-admission-plugins=LimitRanger,NamespaceExists")参数, 将出现 flag: malformed pair, expect string=string 错误。 发生这种问题是因为参数列表 --apiserver-extra-args 预期的是 key=value 形式, 而这里的 NamespacesExists 被误认为是缺少取值的键名。

一种解决方法是尝试分离 key=value 对,像这样: --apiserver-extra-args "enable-admission-plugins=LimitRanger,enable-admission-plugins=NamespaceExists" 但这将导致键 enable-admission-plugins 仅有值 NamespaceExists

已知的解决方法是使用 kubeadm 配置文件

在节点被云控制管理器初始化之前,kube-proxy 就被调度了

在云环境场景中,可能出现在云控制管理器完成节点地址初始化之前,kube-proxy 就被调度到新节点了。 这会导致 kube-proxy 无法正确获取节点的 IP 地址,并对管理负载平衡器的代理功能产生连锁反应。

在 kube-proxy Pod 中可以看到以下错误:

server.go:610] Failed to retrieve node IP: host IP unknown; known addresses: []
proxier.go:340] invalid nodeIP, initializing kube-proxy with 127.0.0.1 as nodeIP

一种已知的解决方案是修补 kube-proxy DaemonSet,以允许在控制平面节点上调度它, 而不管它们的条件如何,将其与其他节点保持隔离,直到它们的初始保护条件消除:

kubectl -n kube-system patch ds kube-proxy -p='{
  "spec": {
    "template": {
      "spec": {
        "tolerations": [
          {
            "key": "CriticalAddonsOnly",
            "operator": "Exists"
          },
          {
            "effect": "NoSchedule",
            "key": "node-role.kubernetes.io/control-plane"
          }
        ]
      }
    }
  }
}'

此问题的跟踪在这里

节点上的 /usr 被以只读方式挂载

在类似 Fedora CoreOS 或者 Flatcar Container Linux 这类 Linux 发行版本中, 目录 /usr 是以只读文件系统的形式挂载的。 在支持 FlexVolume 时, 类似 kubelet 和 kube-controller-manager 这类 Kubernetes 组件使用默认路径 /usr/libexec/kubernetes/kubelet-plugins/volume/exec/, 而 FlexVolume 的目录必须是可写入的,该功能特性才能正常工作。

为了解决这个问题,你可以使用 kubeadm 的配置文件来配置 FlexVolume 的目录。

在(使用 kubeadm init 创建的)主控制节点上,使用 --config 参数传入如下文件:

apiVersion: kubeadm.k8s.io/v1beta3
kind: InitConfiguration
nodeRegistration:
  kubeletExtraArgs:
    volume-plugin-dir: "/opt/libexec/kubernetes/kubelet-plugins/volume/exec/"
---
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
controllerManager:
  extraArgs:
    flex-volume-plugin-dir: "/opt/libexec/kubernetes/kubelet-plugins/volume/exec/"

在加入到集群中的节点上,使用下面的文件:

apiVersion: kubeadm.k8s.io/v1beta3
kind: JoinConfiguration
nodeRegistration:
  kubeletExtraArgs:
    volume-plugin-dir: "/opt/libexec/kubernetes/kubelet-plugins/volume/exec/"

或者,你可以更改 /etc/fstab 使得 /usr 目录能够以可写入的方式挂载, 不过请注意这样做本质上是在更改 Linux 发行版的某种设计原则。

kubeadm upgrade plan 输出错误信息 context deadline exceeded

在使用 kubeadm 来升级某运行外部 etcd 的 Kubernetes 集群时可能显示这一错误信息。 这并不是一个非常严重的一个缺陷,之所以出现此错误信息,原因是老的 kubeadm 版本会对外部 etcd 集群执行版本检查。你可以继续执行 kubeadm upgrade apply ...

这一问题已经在 1.19 版本中得到修复。

kubeadm reset 会卸载 /var/lib/kubelet

如果已经挂载了 /var/lib/kubelet 目录,执行 kubeadm reset 操作的时候会将其卸载。

要解决这一问题,可以在执行了 kubeadm reset 操作之后重新挂载 /var/lib/kubelet 目录。

这是一个在 1.15 中引入的故障,已经在 1.20 版本中修复。

无法在 kubeadm 集群中安全地使用 metrics-server

在 kubeadm 集群中可以通过为 metrics-server 设置 --kubelet-insecure-tls 来以不安全的形式使用该服务。 建议不要在生产环境集群中这样使用。

如果你需要在 metrics-server 和 kubelet 之间使用 TLS,会有一个问题, kubeadm 为 kubelet 部署的是自签名的服务证书。这可能会导致 metrics-server 端报告下面的错误信息:

x509: certificate signed by unknown authority
x509: certificate is valid for IP-foo not IP-bar

参见为 kubelet 启用签名的服务证书 以进一步了解如何在 kubeadm 集群中配置 kubelet 使用正确签名了的服务证书。

另请参阅 How to run the metrics-server securely

因 etcd 哈希值无变化而升级失败

仅适用于通过 kubeadm 二进制文件 v1.28.3 或更高版本升级控制平面节点的情况, 其中此节点当前由 kubeadm v1.28.0、v1.28.1 或 v1.28.2 管理。

以下是你可能遇到的错误消息:

[upgrade/etcd] Failed to upgrade etcd: couldn't upgrade control plane. kubeadm has tried to recover everything into the earlier state. Errors faced: static Pod hash for component etcd on Node kinder-upgrade-control-plane-1 did not change after 5m0s: timed out waiting for the condition
[upgrade/etcd] Waiting for previous etcd to become available
I0907 10:10:09.109104    3704 etcd.go:588] [etcd] attempting to see if all cluster endpoints ([https://172.17.0.6:2379/ https://172.17.0.4:2379/ https://172.17.0.3:2379/]) are available 1/10
[upgrade/etcd] Etcd was rolled back and is now available
static Pod hash for component etcd on Node kinder-upgrade-control-plane-1 did not change after 5m0s: timed out waiting for the condition
couldn't upgrade control plane. kubeadm has tried to recover everything into the earlier state. Errors faced
k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade.rollbackOldManifests
	cmd/kubeadm/app/phases/upgrade/staticpods.go:525
k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade.upgradeComponent
	cmd/kubeadm/app/phases/upgrade/staticpods.go:254
k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade.performEtcdStaticPodUpgrade
	cmd/kubeadm/app/phases/upgrade/staticpods.go:338
...

本次失败的原因是受影响的版本在 PodSpec 中生成的 etcd 清单文件带有不需要的默认值。 这将导致与清单比较的差异,并且 kubeadm 预期 Pod 哈希值将发生变化,但 kubelet 永远不会更新哈希值。

如果你在集群中遇到此问题,有两种解决方法:

  • 可以运行以下命令跳过 etcd 的版本升级,即受影响版本和 v1.28.3(或更高版本)之间的版本升级:

    kubeadm upgrade {apply|node} [version] --etcd-upgrade=false
    

    但不推荐这种方法,因为后续的 v1.28 补丁版本可能引入新的 etcd 版本。

  • 在升级之前,对 etcd 静态 Pod 的清单进行修补,以删除有问题的默认属性:

    diff --git a/etc/kubernetes/manifests/etcd_defaults.yaml b/etc/kubernetes/manifests/etcd_origin.yaml
    index d807ccbe0aa..46b35f00e15 100644
    --- a/etc/kubernetes/manifests/etcd_defaults.yaml
    +++ b/etc/kubernetes/manifests/etcd_origin.yaml
    @@ -43,7 +43,6 @@ spec:
            scheme: HTTP
          initialDelaySeconds: 10
          periodSeconds: 10
    -      successThreshold: 1
          timeoutSeconds: 15
        name: etcd
        resources:
    @@ -59,26 +58,18 @@ spec:
            scheme: HTTP
          initialDelaySeconds: 10
          periodSeconds: 10
    -      successThreshold: 1
          timeoutSeconds: 15
    -    terminationMessagePath: /dev/termination-log
    -    terminationMessagePolicy: File
        volumeMounts:
        - mountPath: /var/lib/etcd
          name: etcd-data
        - mountPath: /etc/kubernetes/pki/etcd
          name: etcd-certs
    -  dnsPolicy: ClusterFirst
    -  enableServiceLinks: true
      hostNetwork: true
      priority: 2000001000
      priorityClassName: system-node-critical
    -  restartPolicy: Always
    -  schedulerName: default-scheduler
      securityContext:
        seccompProfile:
          type: RuntimeDefault
    -  terminationGracePeriodSeconds: 30
      volumes:
      - hostPath:
          path: /etc/kubernetes/pki/etcd
    

有关此错误的更多信息,请查阅此问题的跟踪页面

2.2.2.1.3 - 使用 kubeadm 创建集群

使用 kubeadm,你能创建一个符合最佳实践的最小化 Kubernetes 集群。 事实上,你可以使用 kubeadm 配置一个通过 Kubernetes 一致性测试的集群。 kubeadm 还支持其他集群生命周期功能, 例如启动引导令牌和集群升级。

kubeadm 工具很棒,如果你需要:

  • 一个尝试 Kubernetes 的简单方法。
  • 一个现有用户可以自动设置集群并测试其应用程序的途径。
  • 其他具有更大范围的生态系统和/或安装工具中的构建模块。

你可以在各种机器上安装和使用 kubeadm:笔记本电脑、一组云服务器、Raspberry Pi 等。 无论是部署到云还是本地,你都可以将 kubeadm 集成到 Ansible 或 Terraform 这类预配置系统中。

准备开始

要遵循本指南,你需要:

  • 一台或多台运行兼容 deb/rpm 的 Linux 操作系统的计算机;例如:Ubuntu 或 CentOS。
  • 每台机器 2 GB 以上的内存,内存不足时应用会受限制。
  • 用作控制平面节点的计算机上至少有 2 个 CPU。
  • 集群中所有计算机之间具有完全的网络连接。你可以使用公共网络或专用网络。

你还需要使用可以在新集群中部署特定 Kubernetes 版本对应的 kubeadm

Kubernetes 版本及版本偏差策略适用于 kubeadm 以及整个 Kubernetes。 查阅该策略以了解支持哪些版本的 Kubernetes 和 kubeadm。 该页面是为 Kubernetes v1.30 编写的。

kubeadm 工具的整体功能状态为一般可用性(GA)。一些子功能仍在积极开发中。 随着工具的发展,创建集群的实现可能会略有变化,但总体实现应相当稳定。

目标

  • 安装单个控制平面的 Kubernetes 集群
  • 在集群上安装 Pod 网络,以便你的 Pod 可以相互连通

操作指南

主机准备

安装组件

在所有主机上安装容器运行时和 kubeadm。 详细说明和其他前提条件,请参见安装 kubeadm

网络设置

kubeadm 与其他 Kubernetes 组件类似,会尝试在与主机默认网关关联的网络接口上找到可用的 IP 地址。 这个 IP 地址随后用于由某组件执行的公告和/或监听。

要在 Linux 主机上获得此 IP 地址,你可以使用以下命令:

ip route show # 查找以 "default via" 开头的行

Kubernetes 组件不接受自定义网络接口作为选项,因此必须将自定义 IP 地址作为标志传递给所有需要此自定义配置的组件实例。

要为使用 initjoin 创建的控制平面节点配置 API 服务器的公告地址, 你可以使用 --apiserver-advertise-address 标志。 最好在 kubeadm API中使用 InitConfiguration.localAPIEndpointJoinConfiguration.controlPlane.localAPIEndpoint 来设置此选项。

对于所有节点上的 kubelet,--node-ip 选项可以在 kubeadm 配置文件 (InitConfigurationJoinConfiguration)的 .nodeRegistration.kubeletExtraArgs 中设置。

有关双协议栈细节参见使用 kubeadm 支持双协议栈

你分配给控制平面组件的 IP 地址将成为其 X.509 证书的使用者备用名称字段的一部分。 更改这些 IP 地址将需要签署新的证书并重启受影响的组件, 以便反映证书文件中的变化。有关此主题的更多细节参见 手动续期证书

准备所需的容器镜像

这个步骤是可选的,只适用于你希望 kubeadm initkubeadm join 不去下载存放在 registry.k8s.io 上的默认容器镜像的情况。

当你在离线的节点上创建一个集群的时候,kubeadm 有一些命令可以帮助你预拉取所需的镜像。 阅读离线运行 kubeadm 获取更多的详情。

kubeadm 允许你给所需要的镜像指定一个自定义的镜像仓库。 阅读使用自定义镜像获取更多的详情。

初始化控制平面节点

控制平面节点是运行控制平面组件的机器, 包括 etcd(集群数据库) 和 API 服务器 (命令行工具 kubectl 与之通信)。

  1. (推荐)如果计划将单个控制平面 kubeadm 集群升级成高可用, 你应该指定 --control-plane-endpoint 为所有控制平面节点设置共享端点。 端点可以是负载均衡器的 DNS 名称或 IP 地址。
  2. 选择一个 Pod 网络插件,并验证是否需要为 kubeadm init 传递参数。 根据你选择的第三方网络插件,你可能需要设置 --pod-network-cidr 的值。 请参阅安装 Pod 网络附加组件
  1. (可选)kubeadm 试图通过使用已知的端点列表来检测容器运行时。 使用不同的容器运行时或在预配置的节点上安装了多个容器运行时,请为 kubeadm init 指定 --cri-socket 参数。 请参阅安装运行时

要初始化控制平面节点,请运行:

kubeadm init <args>

关于 apiserver-advertise-address 和 ControlPlaneEndpoint 的注意事项

--apiserver-advertise-address 可用于为控制平面节点的 API 服务器设置广播地址, --control-plane-endpoint 可用于为所有控制平面节点设置共享端点。

--control-plane-endpoint 允许 IP 地址和可以映射到 IP 地址的 DNS 名称。 请与你的网络管理员联系,以评估有关此类映射的可能解决方案。

这是一个示例映射:

192.168.0.102 cluster-endpoint

其中 192.168.0.102 是此节点的 IP 地址,cluster-endpoint 是映射到该 IP 的自定义 DNS 名称。 这将允许你将 --control-plane-endpoint=cluster-endpoint 传递给 kubeadm init, 并将相同的 DNS 名称传递给 kubeadm join。稍后你可以修改 cluster-endpoint 以指向高可用性方案中的负载均衡器的地址。

kubeadm 不支持将没有 --control-plane-endpoint 参数的单个控制平面集群转换为高可用性集群。

更多信息

有关 kubeadm init 参数的更多信息,请参见 kubeadm 参考指南

要使用配置文件配置 kubeadm init 命令, 请参见带配置文件使用 kubeadm init

要自定义控制平面组件,包括可选的对控制平面组件和 etcd 服务器的活动探针提供 IPv6 支持, 请参阅自定义参数

要重新配置一个已经创建的集群, 请参见重新配置一个 kubeadm 集群

要再次运行 kubeadm init,你必须首先卸载集群

如果将具有不同架构的节点加入集群, 请确保已部署的 DaemonSet 对这种体系结构具有容器镜像支持。

kubeadm init 首先运行一系列预检查以确保机器为运行 Kubernetes 准备就绪。 这些预检查会显示警告并在错误时退出。然后 kubeadm init 下载并安装集群控制平面组件。这可能会需要几分钟。完成之后你应该看到:

Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

You should now deploy a Pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  /docs/concepts/cluster-administration/addons/

You can now join any number of machines by running the following on each node
as root:

  kubeadm join <control-plane-host>:<control-plane-port> --token <token> --discovery-token-ca-cert-hash sha256:<hash>

要使非 root 用户可以运行 kubectl,请运行以下命令, 它们也是 kubeadm init 输出的一部分:

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

或者,如果你是 root 用户,则可以运行:

export KUBECONFIG=/etc/kubernetes/admin.conf

记录 kubeadm init 输出的 kubeadm join 命令。 你需要此命令将节点加入集群

令牌用于控制平面节点和加入节点之间的相互身份验证。 这里包含的令牌是密钥。确保它的安全, 因为拥有此令牌的任何人都可以将经过身份验证的节点添加到你的集群中。 可以使用 kubeadm token 命令列出,创建和删除这些令牌。 请参阅 kubeadm 参考指南

安装 Pod 网络附加组件

一些外部项目为 Kubernetes 提供使用 CNI 的 Pod 网络, 其中一些还支持网络策略

请参阅实现 Kubernetes 网络模型的附加组件列表。

请参阅安装插件页面, 了解 Kubernetes 支持的网络插件的非详尽列表。

你可以使用以下命令在控制平面节点或具有 kubeconfig 凭据的节点上安装 Pod 网络附加组件:

kubectl apply -f <add-on.yaml>

每个集群只能安装一个 Pod 网络。

安装 Pod 网络后,你可以通过在 kubectl get pods --all-namespaces 输出中检查 CoreDNS Pod 是否 Running 来确认其是否正常运行。 一旦 CoreDNS Pod 启用并运行,你就可以继续加入节点。

如果你的网络无法正常工作或 CoreDNS 不在 Running 状态,请查看 kubeadm故障排除指南

托管节点标签

默认情况下,kubeadm 启用 NodeRestriction 准入控制器来限制 kubelet 在节点注册时可以应用哪些标签。准入控制器文档描述 kubelet --node-labels 选项允许使用哪些标签。 其中 node-role.kubernetes.io/control-plane 标签就是这样一个受限制的标签, kubeadm 在节点创建后使用特权客户端手动应用此标签。 你可以使用一个有特权的 kubeconfig,比如由 kubeadm 管理的 /etc/kubernetes/admin.conf, 通过执行 kubectl label 来手动完成操作。

控制平面节点隔离

默认情况下,出于安全原因,你的集群不会在控制平面节点上调度 Pod。 如果你希望能够在单机 Kubernetes 集群等控制平面节点上调度 Pod,请运行:

kubectl taint nodes --all node-role.kubernetes.io/control-plane-

输出看起来像:

node "test-01" untainted
...

这将从任何拥有 node-role.kubernetes.io/control-plane:NoSchedule 污点的节点(包括控制平面节点)上移除该污点。 这意味着调度程序将能够在任何地方调度 Pod。

此外,你可以执行以下命令从控制平面节点中删除 node.kubernetes.io/exclude-from-external-load-balancers 标签,这会将其从后端服务器列表中排除:

kubectl label nodes --all node.kubernetes.io/exclude-from-external-load-balancers-

加入节点

节点是你的工作负载(容器和 Pod 等)运行的地方。要将新节点添加到集群,请对每台计算机执行以下操作:

  • SSH 到机器

  • 成为 root (例如 sudo su -

  • 必要时安装一个运行时

  • 运行 kubeadm init 输出的命令,例如:

    kubeadm join --token <token> <control-plane-host>:<control-plane-port> --discovery-token-ca-cert-hash sha256:<hash>
    

如果你没有令牌,可以通过在控制平面节点上运行以下命令来获取令牌:

kubeadm token list

输出类似于以下内容:

TOKEN                    TTL  EXPIRES              USAGES           DESCRIPTION            EXTRA GROUPS
8ewj1p.9r9hcjoqgajrj4gi  23h  2018-06-12T02:51:28Z authentication,  The default bootstrap  system:
                                                   signing          token generated by     bootstrappers:
                                                                    'kubeadm init'.        kubeadm:
                                                                                           default-node-token

默认情况下,令牌会在 24 小时后过期。如果要在当前令牌过期后将节点加入集群, 则可以通过在控制平面节点上运行以下命令来创建新令牌:

kubeadm token create

输出类似于以下内容:

5didvk.d09sbcov8ph2amjw

如果你没有 --discovery-token-ca-cert-hash 的值,则可以通过在控制平面节点上执行以下命令链来获取它:

openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | \
   openssl dgst -sha256 -hex | sed 's/^.* //'

输出类似于以下内容:

8cb2de97839780a412b93877f8507ad6c94f73add17d5d7058e91741c9d5ec78

输出应类似于:

[preflight] Running pre-flight checks

... (log output of join workflow) ...

Node join complete:
* Certificate signing request sent to control-plane and response
  received.
* Kubelet informed of new secure connection details.

Run 'kubectl get nodes' on control-plane to see this machine join.

几秒钟后,当你在控制平面节点上执行 kubectl get nodes,你会注意到该节点出现在输出中。

(可选)从控制平面节点以外的计算机控制集群

为了使 kubectl 在其他计算机(例如笔记本电脑)上与你的集群通信, 你需要将管理员 kubeconfig 文件从控制平面节点复制到工作站,如下所示:

scp root@<control-plane-host>:/etc/kubernetes/admin.conf .
kubectl --kubeconfig ./admin.conf get nodes

(可选)将 API 服务器代理到本地主机

如果你要从集群外部连接到 API 服务器,则可以使用 kubectl proxy

scp root@<control-plane-host>:/etc/kubernetes/admin.conf .
kubectl --kubeconfig ./admin.conf proxy

你现在可以在 http://localhost:8001/api/v1 从本地访问 API 服务器。

清理

如果你在集群中使用了一次性服务器进行测试,则可以关闭这些服务器,而无需进一步清理。 你可以使用 kubectl config delete-cluster 删除对集群的本地引用。

但是,如果要更干净地取消配置集群, 则应首先清空节点并确保该节点为空, 然后取消配置该节点。

移除节点

使用适当的凭据与控制平面节点通信,运行:

kubectl drain <节点名称> --delete-emptydir-data --force --ignore-daemonsets

在移除节点之前,请重置 kubeadm 安装的状态:

kubeadm reset

重置过程不会重置或清除 iptables 规则或 IPVS 表。如果你希望重置 iptables,则必须手动进行:

iptables -F && iptables -t nat -F && iptables -t mangle -F && iptables -X

如果要重置 IPVS 表,则必须运行以下命令:

ipvsadm -C

现在移除节点:

kubectl delete node <节点名称>

如果你想重新开始,只需运行 kubeadm initkubeadm join 并加上适当的参数。

清理控制平面

你可以在控制平面主机上使用 kubeadm reset 来触发尽力而为的清理。

有关此子命令及其选项的更多信息,请参见 kubeadm reset 参考文档。

版本偏差策略

虽然 kubeadm 允许所管理的组件有一定程度的版本偏差, 但是建议你将 kubeadm 的版本与控制平面组件、kube-proxy 和 kubelet 的版本相匹配。

kubeadm 中的 Kubernetes 版本偏差

kubeadm 可以与 Kubernetes 组件一起使用,这些组件的版本与 kubeadm 相同,或者比它大一个版本。 Kubernetes 版本可以通过使用 --kubeadm init--kubernetes-version 标志或使用 --config 时的 ClusterConfiguration.kubernetesVersion 字段指定给 kubeadm。 这个选项将控制 kube-apiserver、kube-controller-manager、kube-scheduler 和 kube-proxy 的版本。

例子:

  • kubeadm 的版本为 1.30。
  • kubernetesVersion 必须为 1.30 或者 1.29。

kubeadm 中 kubelet 的版本偏差

与 Kubernetes 版本类似,kubeadm 可以使用与 kubeadm 相同版本的 kubelet, 或者比 kubeadm 老三个版本的 kubelet。

例子:

  • kubeadm 的版本为 1.30。
  • 主机上的 kubelet 必须为 1.30、1.29、 1.28 或 1.27。

kubeadm 支持的 kubeadm 的版本偏差

kubeadm 命令在现有节点或由 kubeadm 管理的整个集群上的操作有一定限制。

如果新的节点加入到集群中,用于 kubeadm join 的 kubeadm 二进制文件必须与用 kubeadm init 创建集群或用 kubeadm upgrade 升级同一节点时所用的 kubeadm 版本一致。 类似的规则适用于除了 kubeadm upgrade 以外的其他 kubeadm 命令。

kubeadm join 的例子:

  • 使用 kubeadm init 创建集群时使用版本为 1.30 的 kubeadm。
  • 添加节点所用的 kubeadm 可执行文件为版本 。

对于正在升级的节点,所使用的的 kubeadm 必须与管理该节点的 kubeadm 具有相同的 MINOR 版本或比后者新一个 MINOR 版本。

kubeadm upgrade 的例子:

  • 用于创建或升级节点的 kubeadm 版本为 1.29。
  • 用于升级节点的 kubeadm 版本必须为 1.29 或 1.30。

要了解更多关于不同 Kubernetes 组件之间的版本偏差, 请参见版本偏差策略

局限性

集群弹性

此处创建的集群具有单个控制平面节点,运行单个 etcd 数据库。 这意味着如果控制平面节点发生故障,你的集群可能会丢失数据并且可能需要从头开始重新创建。

解决方法:

  • 定期备份 etcd。 kubeadm 配置的 etcd 数据目录位于控制平面节点上的 /var/lib/etcd 中。

平台兼容性

kubeadm deb/rpm 软件包和二进制文件是为 amd64、arm (32-bit)、arm64、ppc64le 和 s390x 构建的遵循多平台提案

从 v1.12 开始还支持用于控制平面和附加组件的多平台容器镜像。

只有一些网络提供商为所有平台提供解决方案。 请查阅上方的网络提供商清单或每个提供商的文档以确定提供商是否支持你选择的平台。

故障排除

如果你在使用 kubeadm 时遇到困难, 请查阅我们的故障排除文档

下一步

反馈

2.2.2.1.4 - 使用 kubeadm API 定制组件

本页面介绍了如何自定义 kubeadm 部署的组件。 你可以使用 ClusterConfiguration 结构中定义的参数,或者在每个节点上应用补丁来定制控制平面组件。 你可以使用 KubeletConfigurationKubeProxyConfiguration 结构分别定制 kubelet 和 kube-proxy 组件。

所有这些选项都可以通过 kubeadm 配置 API 实现。 有关配置中的每个字段的详细信息,你可以导航到我们的 API 参考页面

使用 ClusterConfiguration 中的标志自定义控制平面

kubeadm ClusterConfiguration 对象为用户提供了一种方法, 用以覆盖传递给控制平面组件(如 APIServer、ControllerManager、Scheduler 和 Etcd)的默认参数。 各组件配置使用如下字段定义:

  • apiServer
  • controllerManager
  • scheduler
  • etcd

这些结构包含一个通用的 extraArgs 字段,该字段由 key: value 组成。 要覆盖控制平面组件的参数:

  1. 将适当的字段 extraArgs 添加到配置中。
  2. 向字段 extraArgs 添加要覆盖的参数值。
  3. --config <YOUR CONFIG YAML> 运行 kubeadm init

APIServer 参数

有关详细信息,请参阅 kube-apiserver 参考文档

使用示例:

apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
kubernetesVersion: v1.16.0
apiServer:
  extraArgs:
    anonymous-auth: "false"
    enable-admission-plugins: AlwaysPullImages,DefaultStorageClass
    audit-log-path: /home/johndoe/audit.log

ControllerManager 参数

有关详细信息,请参阅 kube-controller-manager 参考文档

使用示例:

apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
kubernetesVersion: v1.16.0
controllerManager:
  extraArgs:
    cluster-signing-key-file: /home/johndoe/keys/ca.key
    deployment-controller-sync-period: "50"

Scheduler 参数

有关详细信息,请参阅 kube-scheduler 参考文档

使用示例:

apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
kubernetesVersion: v1.16.0
scheduler:
  extraArgs:
    config: /etc/kubernetes/scheduler-config.yaml
  extraVolumes:
    - name: schedulerconfig
      hostPath: /home/johndoe/schedconfig.yaml
      mountPath: /etc/kubernetes/scheduler-config.yaml
      readOnly: true
      pathType: "File"

Etcd 参数

有关详细信息,请参阅 etcd 服务文档.

使用示例:

apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
etcd:
  local:
    extraArgs:
      election-timeout: 1000

使用补丁定制

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

Kubeadm 允许将包含补丁文件的目录传递给各个节点上的 InitConfigurationJoinConfiguration。 这些补丁可被用作组件配置写入磁盘之前的最后一个自定义步骤。

可以使用 --config <你的 YAML 格式控制文件> 将配置文件传递给 kubeadm init

apiVersion: kubeadm.k8s.io/v1beta3
kind: InitConfiguration
patches:
  directory: /home/user/somedir

你可以使用 --config <你的 YAML 格式配置文件> 将配置文件传递给 kubeadm join

apiVersion: kubeadm.k8s.io/v1beta3
kind: JoinConfiguration
patches:
  directory: /home/user/somedir

补丁目录必须包含名为 target[suffix][+patchtype].extension 的文件。 例如,kube-apiserver0+merge.yaml 或只是 etcd.json

  • target 可以是 kube-apiserverkube-controller-managerkube-scheduleretcdkubeletconfiguration 之一。
  • patchtype 可以是 strategymergejson 之一,并且这些必须匹配 kubectl 支持 的补丁格式。 默认补丁类型是 strategic 的。
  • extension 必须是 jsonyaml
  • suffix 是一个可选字符串,可用于确定首先按字母数字应用哪些补丁。

自定义 kubelet

要自定义 kubelet,你可以在同一配置文件中的 ClusterConfigurationInitConfiguration 之外添加一个 KubeletConfiguration,用 --- 分隔。 然后可以将此文件传递给 kubeadm init,kubeadm 会将相同的 KubeletConfiguration 配置应用于集群中的所有节点。

要在基础 KubeletConfiguration 上应用特定节点的配置,你可以使用 kubeletconfiguration 补丁定制

或者你可以使用 kubelet 参数进行覆盖,方法是将它们传递到 InitConfigurationJoinConfiguration 支持的 nodeRegistration.kubeletExtraArgs 字段中。一些 kubelet 参数已被弃用, 因此在使用这些参数之前,请在 kubelet 参考文档 中检查它们的状态。

更多详情,请参阅使用 kubeadm 配置集群中的每个 kubelet

自定义 kube-proxy

要自定义 kube-proxy,你可以在 ClusterConfigurationInitConfiguration 之外添加一个由 --- 分隔的 KubeProxyConfiguration, 传递给 kubeadm init

可以导航到 API 参考页面查看更多详情,

2.2.2.1.5 - 高可用拓扑选项

本页面介绍了配置高可用(HA)Kubernetes 集群拓扑的两个选项。

你可以设置 HA 集群:

  • 使用堆叠(stacked)控制平面节点,其中 etcd 节点与控制平面节点共存
  • 使用外部 etcd 节点,其中 etcd 在与控制平面不同的节点上运行

在设置 HA 集群之前,你应该仔细考虑每种拓扑的优缺点。

堆叠(Stacked)etcd 拓扑

堆叠(Stacked)HA 集群是一种这样的拓扑, 其中 etcd 分布式数据存储集群堆叠在 kubeadm 管理的控制平面节点上,作为控制平面的一个组件运行。

每个控制平面节点运行 kube-apiserverkube-schedulerkube-controller-manager 实例。 kube-apiserver 使用负载均衡器暴露给工作节点。

每个控制平面节点创建一个本地 etcd 成员(member),这个 etcd 成员只与该节点的 kube-apiserver 通信。 这同样适用于本地 kube-controller-managerkube-scheduler 实例。

这种拓扑将控制平面和 etcd 成员耦合在同一节点上。相对使用外部 etcd 集群, 设置起来更简单,而且更易于副本管理。

然而,堆叠集群存在耦合失败的风险。如果一个节点发生故障,则 etcd 成员和控制平面实例都将丢失, 并且冗余会受到影响。你可以通过添加更多控制平面节点来降低此风险。

因此,你应该为 HA 集群运行至少三个堆叠的控制平面节点。

这是 kubeadm 中的默认拓扑。当使用 kubeadm initkubeadm join --control-plane 时, 在控制平面节点上会自动创建本地 etcd 成员。

堆叠的 etcd 拓扑

外部 etcd 拓扑

具有外部 etcd 的 HA 集群是一种这样的拓扑, 其中 etcd 分布式数据存储集群在独立于控制平面节点的其他节点上运行。

就像堆叠的 etcd 拓扑一样,外部 etcd 拓扑中的每个控制平面节点都会运行 kube-apiserverkube-schedulerkube-controller-manager 实例。 同样,kube-apiserver 使用负载均衡器暴露给工作节点。但是 etcd 成员在不同的主机上运行, 每个 etcd 主机与每个控制平面节点的 kube-apiserver 通信。

这种拓扑结构解耦了控制平面和 etcd 成员。因此它提供了一种 HA 设置, 其中失去控制平面实例或者 etcd 成员的影响较小,并且不会像堆叠的 HA 拓扑那样影响集群冗余。

但此拓扑需要两倍于堆叠 HA 拓扑的主机数量。 具有此拓扑的 HA 集群至少需要三个用于控制平面节点的主机和三个用于 etcd 节点的主机。

外部 etcd 拓扑

接下来

2.2.2.1.6 - 利用 kubeadm 创建高可用集群

本文讲述了使用 kubeadm 设置一个高可用的 Kubernetes 集群的两种不同方式:

  • 使用具有堆叠的控制平面节点。这种方法所需基础设施较少。etcd 成员和控制平面节点位于同一位置。
  • 使用外部 etcd 集群。这种方法所需基础设施较多。控制平面的节点和 etcd 成员是分开的。

在下一步之前,你应该仔细考虑哪种方法更好地满足你的应用程序和环境的需求。 高可用拓扑选项 讲述了每种方法的优缺点。

如果你在安装 HA 集群时遇到问题,请在 kubeadm 问题跟踪里向我们提供反馈。

你也可以阅读升级文档

准备开始

根据集群控制平面所选择的拓扑结构不同,准备工作也有所差异:

需要准备:

  • 配置满足 kubeadm 的最低要求 的三台机器作为控制面节点。控制平面节点为奇数有利于机器故障或者分区故障时重新选举。
  • 配置满足 kubeadm 的最低要求 的三台机器作为工作节点
  • 在集群中,确保所有计算机之间存在全网络连接(公网或私网)
  • 在所有机器上具有 sudo 权限
    • 可以使用其他工具;本教程以 sudo 举例
  • 从某台设备通过 SSH 访问系统中所有节点的能力
  • 所有机器上已经安装 kubeadmkubelet

拓扑详情请参考堆叠(Stacked)etcd 拓扑

需要准备:

  • 配置满足 kubeadm 的最低要求 的三台机器作为控制面节点。控制平面节点为奇数有利于机器故障或者分区故障时重新选举。
  • 配置满足 kubeadm 的最低要求 的三台机器作为工作节点
  • 在集群中,确保所有计算机之间存在全网络连接(公网或私网)
  • 在所有机器上具有 sudo 权限
    • 可以使用其他工具;本教程以 sudo 举例
  • 从某台设备通过 SSH 访问系统中所有节点的能力
  • 所有机器上已经安装 kubeadmkubelet

还需要准备:

  • 给 etcd 集群使用的另外至少三台机器。为了分布式一致性算法达到更好的投票效果,集群必须由奇数个节点组成。
    • 机器上已经安装 kubeadmkubelet
    • 机器上同样需要安装好容器运行时,并能正常运行。

拓扑详情请参考外部 etcd 拓扑

容器镜像

每台主机需要能够从 Kubernetes 容器镜像仓库(registry.k8s.io)读取和拉取镜像。 想要在无法拉取 Kubernetes 仓库镜像的机器上部署高可用集群也是可行的。通过其他的手段保证主机上已经有对应的容器镜像即可。

命令行

一旦集群创建成功,需要在 PC 上安装 kubectl 用于管理 Kubernetes。 为了方便故障排查,也可以在每个控制平面节点上安装 kubectl

这两种方法的第一步

为 kube-apiserver 创建负载均衡器

  1. 创建一个名为 kube-apiserver 的负载均衡器解析 DNS。

    • 在云环境中,应该将控制平面节点放置在 TCP 转发负载平衡后面。 该负载均衡器将流量分配给目标列表中所有运行状况良好的控制平面节点。 API 服务器的健康检查是在 kube-apiserver 的监听端口(默认值 :6443) 上进行的一个 TCP 检查。

    • 不建议在云环境中直接使用 IP 地址。

    • 负载均衡器必须能够在 API 服务器端口上与所有控制平面节点通信。 它还必须允许其监听端口的入站流量。

    • 确保负载均衡器的地址始终匹配 kubeadm 的 ControlPlaneEndpoint 地址。

    • 阅读软件负载平衡选项指南 以获取更多详细信息。

  1. 添加第一个控制平面节点到负载均衡器并测试连接:

    nc -v LOAD_BALANCER_IP PORT
    

    由于 API 服务器尚未运行,预期会出现一个连接拒绝错误。 然而超时意味着负载均衡器不能和控制平面节点通信。 如果发生超时,请重新配置负载均衡器与控制平面节点进行通信。

  2. 将其余控制平面节点添加到负载均衡器目标组。

使用堆控制平面和 etcd 节点

控制平面节点的第一步

  1. 初始化控制平面:

    sudo kubeadm init --control-plane-endpoint "LOAD_BALANCER_DNS:LOAD_BALANCER_PORT" --upload-certs
    
    • 你可以使用 --kubernetes-version 标志来设置要使用的 Kubernetes 版本。 建议将 kubeadm、kebelet、kubectl 和 Kubernetes 的版本匹配。
    • 这个 --control-plane-endpoint 标志应该被设置成负载均衡器的地址或 DNS 和端口。
    • 这个 --upload-certs 标志用来将在所有控制平面实例之间的共享证书上传到集群。 如果正好相反,你更喜欢手动地通过控制平面节点或者使用自动化工具复制证书, 请删除此标志并参考如下部分证书分配手册

    输出类似于:

    ...
    You can now join any number of control-plane node by running the following command on each as a root:
        kubeadm join 192.168.0.200:6443 --token 9vr73a.a8uxyaju799qwdjv --discovery-token-ca-cert-hash sha256:7c2e69131a36ae2a042a339b33381c6d0d43887e2de83720eff5359e26aec866 --control-plane --certificate-key f8902e114ef118304e561c3ecd4d0b543adc226b7a07f675f56564185ffe0c07
    
    Please note that the certificate-key gives access to cluster sensitive data, keep it secret!
    As a safeguard, uploaded-certs will be deleted in two hours; If necessary, you can use kubeadm init phase upload-certs to reload certs afterward.
    
    Then you can join any number of worker nodes by running the following on each as root:
        kubeadm join 192.168.0.200:6443 --token 9vr73a.a8uxyaju799qwdjv --discovery-token-ca-cert-hash sha256:7c2e69131a36ae2a042a339b33381c6d0d43887e2de83720eff5359e26aec866
    
    • 将此输出复制到文本文件。 稍后你将需要它来将控制平面节点和工作节点加入集群。

    • 当使用 --upload-certs 调用 kubeadm init 时,主控制平面的证书被加密并上传到 kubeadm-certs Secret 中。

    • 要重新上传证书并生成新的解密密钥,请在已加入集群节点的控制平面上使用以下命令:

      sudo kubeadm init phase upload-certs --upload-certs
      
    • 你还可以在 init 期间指定自定义的 --certificate-key,以后可以由 join 使用。 要生成这样的密钥,可以使用以下命令:
    kubeadm certs certificate-key
    

    证书密钥是一个十六进制编码的字符串,它是一个 32 字节大小的 AES 密钥。

  1. 应用你所选择的 CNI 插件: 请遵循以下指示 安装 CNI 驱动。如果适用,请确保配置与 kubeadm 配置文件中指定的 Pod CIDR 相对应。

  1. 输入以下内容,并查看控制平面组件的 Pod 启动:

    kubectl get pod -n kube-system -w
    

其余控制平面节点的步骤

对于每个其他控制平面节点,你应该:

  1. 执行先前由第一个节点上的 kubeadm init 输出提供给你的 join 命令。 它看起来应该像这样:

    sudo kubeadm join 192.168.0.200:6443 --token 9vr73a.a8uxyaju799qwdjv --discovery-token-ca-cert-hash sha256:7c2e69131a36ae2a042a339b33381c6d0d43887e2de83720eff5359e26aec866 --control-plane --certificate-key f8902e114ef118304e561c3ecd4d0b543adc226b7a07f675f56564185ffe0c07
    
    • 这个 --control-plane 标志通知 kubeadm join 创建一个新的控制平面。
    • --certificate-key ... 将导致从集群中的 kubeadm-certs Secret 下载控制平面证书并使用给定的密钥进行解密。

你可以并行地加入多个控制面节点。

外部 etcd 节点

使用外部 etcd 节点设置集群类似于用于堆叠 etcd 的过程, 不同之处在于你应该首先设置 etcd,并在 kubeadm 配置文件中传递 etcd 信息。

设置 etcd 集群

  1. 按照这里的指示去设置。

  2. 根据这里 的描述配置 SSH。

  3. 将以下文件从集群中的任一 etcd 节点复制到第一个控制平面节点:

    export CONTROL_PLANE="ubuntu@10.0.0.7"
    scp /etc/kubernetes/pki/etcd/ca.crt "${CONTROL_PLANE}":
    scp /etc/kubernetes/pki/apiserver-etcd-client.crt "${CONTROL_PLANE}":
    scp /etc/kubernetes/pki/apiserver-etcd-client.key "${CONTROL_PLANE}":
    
    • 用第一台控制平面节点的 user@host 替换 CONTROL_PLANE 的值。

设置第一个控制平面节点

  1. 用以下内容创建一个名为 kubeadm-config.yaml 的文件:

    ---
    apiVersion: kubeadm.k8s.io/v1beta3
    kind: ClusterConfiguration
    kubernetesVersion: stable
    controlPlaneEndpoint: "LOAD_BALANCER_DNS:LOAD_BALANCER_PORT" # change this (see below)
    etcd:
      external:
        endpoints:
          - https://ETCD_0_IP:2379 # 适当地更改 ETCD_0_IP
          - https://ETCD_1_IP:2379 # 适当地更改 ETCD_1_IP
          - https://ETCD_2_IP:2379 # 适当地更改 ETCD_2_IP
        caFile: /etc/kubernetes/pki/etcd/ca.crt
        certFile: /etc/kubernetes/pki/apiserver-etcd-client.crt
        keyFile: /etc/kubernetes/pki/apiserver-etcd-client.key
    
    • 在你的集群中,将配置模板中的以下变量替换为适当值:

    • LOAD_BALANCER_DNS

    • LOAD_BALANCER_PORT

    • ETCD_0_IP

    • ETCD_1_IP

    • ETCD_2_IP

以下的步骤与设置内置 etcd 的集群是相似的:

  1. 在节点上运行 sudo kubeadm init --config kubeadm-config.yaml --upload-certs 命令。

  2. 记下输出的 join 命令,这些命令将在以后使用。

  3. 应用你选择的 CNI 插件。

其他控制平面节点的步骤

步骤与设置内置 etcd 相同:

  • 确保第一个控制平面节点已完全初始化。
  • 使用保存到文本文件的 join 命令将每个控制平面节点连接在一起。 建议一次加入一个控制平面节点。
  • 不要忘记默认情况下,--certificate-key 中的解密秘钥会在两个小时后过期。

列举控制平面之后的常见任务

安装工作节点

你可以使用之前存储的 kubeadm init 命令的输出将工作节点加入集群中:

sudo kubeadm join 192.168.0.200:6443 --token 9vr73a.a8uxyaju799qwdjv --discovery-token-ca-cert-hash sha256:7c2e69131a36ae2a042a339b33381c6d0d43887e2de83720eff5359e26aec866

手动证书分发

如果你选择不将 kubeadm init--upload-certs 命令一起使用, 则意味着你将必须手动将证书从主控制平面节点复制到将要加入的控制平面节点上。

有许多方法可以实现这种操作。下面的例子使用了 sshscp

如果要在单独的一台计算机控制所有节点,则需要 SSH。

  1. 在你的主设备上启用 ssh-agent,要求该设备能访问系统中的所有其他节点:

    eval $(ssh-agent)
    
  1. 将 SSH 身份添加到会话中:

    ssh-add ~/.ssh/path_to_private_key
    
  1. 检查节点间的 SSH 以确保连接是正常运行的

    • SSH 到任何节点时,请确保添加 -A 标志。 此标志允许你通过 SSH 登录到节点后从该节点上访问你自己 PC 上的 SSH 代理。 如果你不完全信任该节点上的用户会话安全,可以考虑使用其他替代方法。
    ssh -A 10.0.0.7
    
    • 当在任何节点上使用 sudo 时,请确保保持环境变量设置,以便 SSH 转发能够正常工作:
    sudo -E -s
    
  1. 在所有节点上配置 SSH 之后,你应该在运行过 kubeadm init 命令的第一个控制平面节点上运行以下脚本。 该脚本会将证书从第一个控制平面节点复制到另一个控制平面节点:

    在以下示例中,用其他控制平面节点的 IP 地址替换 CONTROL_PLANE_IPS

    USER=ubuntu # 可定制
    CONTROL_PLANE_IPS="10.0.0.7 10.0.0.8"
    for host in ${CONTROL_PLANE_IPS}; do
        scp /etc/kubernetes/pki/ca.crt "${USER}"@$host:
        scp /etc/kubernetes/pki/ca.key "${USER}"@$host:
        scp /etc/kubernetes/pki/sa.key "${USER}"@$host:
        scp /etc/kubernetes/pki/sa.pub "${USER}"@$host:
        scp /etc/kubernetes/pki/front-proxy-ca.crt "${USER}"@$host:
        scp /etc/kubernetes/pki/front-proxy-ca.key "${USER}"@$host:
        scp /etc/kubernetes/pki/etcd/ca.crt "${USER}"@$host:etcd-ca.crt
        # 如果你正使用外部 etcd,忽略下一行
        scp /etc/kubernetes/pki/etcd/ca.key "${USER}"@$host:etcd-ca.key
    done
    
  1. 然后,在每个即将加入集群的控制平面节点上,你必须先运行以下脚本,然后再运行 kubeadm join。 该脚本会将先前复制的证书从主目录移动到 /etc/kubernetes/pki

    USER=ubuntu # 可定制
    mkdir -p /etc/kubernetes/pki/etcd
    mv /home/${USER}/ca.crt /etc/kubernetes/pki/
    mv /home/${USER}/ca.key /etc/kubernetes/pki/
    mv /home/${USER}/sa.pub /etc/kubernetes/pki/
    mv /home/${USER}/sa.key /etc/kubernetes/pki/
    mv /home/${USER}/front-proxy-ca.crt /etc/kubernetes/pki/
    mv /home/${USER}/front-proxy-ca.key /etc/kubernetes/pki/
    mv /home/${USER}/etcd-ca.crt /etc/kubernetes/pki/etcd/ca.crt
    # 如果你正使用外部 etcd,忽略下一行
    mv /home/${USER}/etcd-ca.key /etc/kubernetes/pki/etcd/ca.key
    

2.2.2.1.7 - 使用 kubeadm 创建一个高可用 etcd 集群

默认情况下,kubeadm 在每个控制平面节点上运行一个本地 etcd 实例。也可以使用外部的 etcd 集群,并在不同的主机上提供 etcd 实例。 这两种方法的区别在高可用拓扑的选项页面中阐述。

这个任务将指导你创建一个由三个成员组成的高可用外部 etcd 集群,该集群在创建过程中可被 kubeadm 使用。

准备开始

  • 三个可以通过 2379 和 2380 端口相互通信的主机。本文档使用这些作为默认端口。 不过,它们可以通过 kubeadm 的配置文件进行自定义。
  • 每个主机都应该能够访问 Kubernetes 容器镜像仓库 (registry.k8s.io), 或者使用 kubeadm config images list/pull 列出/拉取所需的 etcd 镜像。 本指南将把 etcd 实例设置为由 kubelet 管理的静态 Pod
  • 一些可以用来在主机间复制文件的基础设施。例如 sshscp 就可以满足此需求。

建立集群

一般来说,是在一个节点上生成所有证书并且只分发这些必要的文件到其它节点上。

  1. 将 kubelet 配置为 etcd 的服务管理器。

    由于 etcd 是首先创建的,因此你必须通过创建具有更高优先级的新文件来覆盖 kubeadm 提供的 kubelet 单元文件。

    cat << EOF > /etc/systemd/system/kubelet.service.d/kubelet.conf
    # 将下面的 "systemd" 替换为你的容器运行时所使用的 cgroup 驱动。
    # kubelet 的默认值为 "cgroupfs"。
    # 如果需要的话,将 "containerRuntimeEndpoint" 的值替换为一个不同的容器运行时。
    #
    apiVersion: kubelet.config.k8s.io/v1beta1
    kind: KubeletConfiguration
    authentication:
      anonymous:
        enabled: false
      webhook:
        enabled: false
    authorization:
      mode: AlwaysAllow
    cgroupDriver: systemd
    address: 127.0.0.1
    containerRuntimeEndpoint: unix:///var/run/containerd/containerd.sock
    staticPodPath: /etc/kubernetes/manifests
    EOF
    
    cat << EOF > /etc/systemd/system/kubelet.service.d/20-etcd-service-manager.conf
    [Service]
    ExecStart=
    ExecStart=/usr/bin/kubelet --config=/etc/systemd/system/kubelet.service.d/kubelet.conf
    Restart=always
    EOF
    
    systemctl daemon-reload
    systemctl restart kubelet
    

    检查 kubelet 的状态以确保其处于运行状态:

    systemctl status kubelet
    
  1. 为 kubeadm 创建配置文件。

    使用以下脚本为每个将要运行 etcd 成员的主机生成一个 kubeadm 配置文件。

    # 使用你的主机 IP 更新 HOST0、HOST1 和 HOST2 的 IP 地址
    export HOST0=10.0.0.6
    export HOST1=10.0.0.7
    export HOST2=10.0.0.8
    
    # 使用你的主机名更新 NAME0、NAME1 和 NAME2
    export NAME0="infra0"
    export NAME1="infra1"
    export NAME2="infra2"
    
    # 创建临时目录来存储将被分发到其它主机上的文件
    mkdir -p /tmp/${HOST0}/ /tmp/${HOST1}/ /tmp/${HOST2}/
    
    HOSTS=(${HOST0} ${HOST1} ${HOST2})
    NAMES=(${NAME0} ${NAME1} ${NAME2})
    
    for i in "${!HOSTS[@]}"; do
    HOST=${HOSTS[$i]}
    NAME=${NAMES[$i]}
    cat << EOF > /tmp/${HOST}/kubeadmcfg.yaml
    ---
    apiVersion: "kubeadm.k8s.io/v1beta3"
    kind: InitConfiguration
    nodeRegistration:
        name: ${NAME}
    localAPIEndpoint:
        advertiseAddress: ${HOST}
    ---
    apiVersion: "kubeadm.k8s.io/v1beta3"
    kind: ClusterConfiguration
    etcd:
        local:
            serverCertSANs:
            - "${HOST}"
            peerCertSANs:
            - "${HOST}"
            extraArgs:
                initial-cluster: ${NAMES[0]}=https://${HOSTS[0]}:2380,${NAMES[1]}=https://${HOSTS[1]}:2380,${NAMES[2]}=https://${HOSTS[2]}:2380
                initial-cluster-state: new
                name: ${NAME}
                listen-peer-urls: https://${HOST}:2380
                listen-client-urls: https://${HOST}:2379
                advertise-client-urls: https://${HOST}:2379
                initial-advertise-peer-urls: https://${HOST}:2380
    EOF
    done
    
  1. 生成证书颁发机构。

    如果你已经拥有 CA,那么唯一的操作是复制 CA 的 crtkey 文件到 etc/kubernetes/pki/etcd/ca.crt/etc/kubernetes/pki/etcd/ca.key。 复制完这些文件后继续下一步,“为每个成员创建证书”。

    如果你还没有 CA,则在 $HOST0(你为 kubeadm 生成配置文件的位置)上运行此命令。

    kubeadm init phase certs etcd-ca
    

    这一操作创建如下两个文件:

    • /etc/kubernetes/pki/etcd/ca.crt
    • /etc/kubernetes/pki/etcd/ca.key
  1. 为每个成员创建证书。

    kubeadm init phase certs etcd-server --config=/tmp/${HOST2}/kubeadmcfg.yaml
    kubeadm init phase certs etcd-peer --config=/tmp/${HOST2}/kubeadmcfg.yaml
    kubeadm init phase certs etcd-healthcheck-client --config=/tmp/${HOST2}/kubeadmcfg.yaml
    kubeadm init phase certs apiserver-etcd-client --config=/tmp/${HOST2}/kubeadmcfg.yaml
    cp -R /etc/kubernetes/pki /tmp/${HOST2}/
    # 清理不可重复使用的证书
    find /etc/kubernetes/pki -not -name ca.crt -not -name ca.key -type f -delete
    
    kubeadm init phase certs etcd-server --config=/tmp/${HOST1}/kubeadmcfg.yaml
    kubeadm init phase certs etcd-peer --config=/tmp/${HOST1}/kubeadmcfg.yaml
    kubeadm init phase certs etcd-healthcheck-client --config=/tmp/${HOST1}/kubeadmcfg.yaml
    kubeadm init phase certs apiserver-etcd-client --config=/tmp/${HOST1}/kubeadmcfg.yaml
    cp -R /etc/kubernetes/pki /tmp/${HOST1}/
    find /etc/kubernetes/pki -not -name ca.crt -not -name ca.key -type f -delete
    
    kubeadm init phase certs etcd-server --config=/tmp/${HOST0}/kubeadmcfg.yaml
    kubeadm init phase certs etcd-peer --config=/tmp/${HOST0}/kubeadmcfg.yaml
    kubeadm init phase certs etcd-healthcheck-client --config=/tmp/${HOST0}/kubeadmcfg.yaml
    kubeadm init phase certs apiserver-etcd-client --config=/tmp/${HOST0}/kubeadmcfg.yaml
    # 不需要移动 certs 因为它们是给 HOST0 使用的
    
    # 清理不应从此主机复制的证书
    find /tmp/${HOST2} -name ca.key -type f -delete
    find /tmp/${HOST1} -name ca.key -type f -delete
    
  1. 复制证书和 kubeadm 配置。

    证书已生成,现在必须将它们移动到对应的主机。

    USER=ubuntu
    HOST=${HOST1}
    scp -r /tmp/${HOST}/* ${USER}@${HOST}:
    ssh ${USER}@${HOST}
    USER@HOST $ sudo -Es
    root@HOST $ chown -R root:root pki
    root@HOST $ mv pki /etc/kubernetes/
    
  1. 确保已经所有预期的文件都存在

    $HOST0 所需文件的完整列表如下:

    /tmp/${HOST0}
    └── kubeadmcfg.yaml
    ---
    /etc/kubernetes/pki
    ├── apiserver-etcd-client.crt
    ├── apiserver-etcd-client.key
    └── etcd
        ├── ca.crt
        ├── ca.key
        ├── healthcheck-client.crt
        ├── healthcheck-client.key
        ├── peer.crt
        ├── peer.key
        ├── server.crt
        └── server.key
    

    $HOST1 上:

    $HOME
    └── kubeadmcfg.yaml
    ---
    /etc/kubernetes/pki
    ├── apiserver-etcd-client.crt
    ├── apiserver-etcd-client.key
    └── etcd
        ├── ca.crt
        ├── healthcheck-client.crt
        ├── healthcheck-client.key
        ├── peer.crt
        ├── peer.key
        ├── server.crt
        └── server.key
    

    $HOST2 上:

    $HOME
    └── kubeadmcfg.yaml
    ---
    /etc/kubernetes/pki
    ├── apiserver-etcd-client.crt
    ├── apiserver-etcd-client.key
    └── etcd
        ├── ca.crt
        ├── healthcheck-client.crt
        ├── healthcheck-client.key
        ├── peer.crt
        ├── peer.key
        ├── server.crt
        └── server.key
    
  1. 创建静态 Pod 清单。

    既然证书和配置已经就绪,是时候去创建清单了。 在每台主机上运行 kubeadm 命令来生成 etcd 使用的静态清单。

    root@HOST0 $ kubeadm init phase etcd local --config=/tmp/${HOST0}/kubeadmcfg.yaml
    root@HOST1 $ kubeadm init phase etcd local --config=$HOME/kubeadmcfg.yaml
    root@HOST2 $ kubeadm init phase etcd local --config=$HOME/kubeadmcfg.yaml
    
  1. 可选:检查集群运行状况。

    如果 etcdctl 不可用,你可以在容器镜像内运行此工具。 你可以使用 crictl run 这类工具直接在容器运行时执行此操作,而不是通过 Kubernetes。

    ETCDCTL_API=3 etcdctl \
    --cert /etc/kubernetes/pki/etcd/peer.crt \
    --key /etc/kubernetes/pki/etcd/peer.key \
    --cacert /etc/kubernetes/pki/etcd/ca.crt \
    --endpoints https://${HOST0}:2379 endpoint health
    ...
    https://[HOST0 IP]:2379 is healthy: successfully committed proposal: took = 16.283339ms
    https://[HOST1 IP]:2379 is healthy: successfully committed proposal: took = 19.44402ms
    https://[HOST2 IP]:2379 is healthy: successfully committed proposal: took = 35.926451ms
    
    • ${HOST0} 设置为要测试的主机的 IP 地址。

接下来

一旦拥有了一个正常工作的 3 成员的 etcd 集群, 你就可以基于使用 kubeadm 外部 etcd 的方法, 继续部署一个高可用的控制平面。

2.2.2.1.8 - 使用 kubeadm 配置集群中的每个 kubelet

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

kubeadm CLI 工具的生命周期与 kubelet 解耦;kubelet 是一个守护程序,在 Kubernetes 集群中的每个节点上运行。 当 Kubernetes 初始化或升级时,kubeadm CLI 工具由用户执行,而 kubelet 始终在后台运行。

由于kubelet是守护程序,因此需要通过某种初始化系统或服务管理器进行维护。 当使用 DEB 或 RPM 安装 kubelet 时,配置系统去管理 kubelet。 你可以改用其他服务管理器,但需要手动地配置。

集群中涉及的所有 kubelet 的一些配置细节都必须相同, 而其他配置方面则需要基于每个 kubelet 进行设置,以适应给定机器的不同特性(例如操作系统、存储和网络)。 你可以手动地管理 kubelet 的配置,但是 kubeadm 现在提供一种 KubeletConfiguration API 类型用于集中管理 kubelet 的配置

Kubelet 配置模式

以下各节讲述了通过使用 kubeadm 简化 kubelet 配置模式,而不是在每个节点上手动地管理 kubelet 配置。

将集群级配置传播到每个 kubelet 中

你可以通过 kubeadm initkubeadm join 命令为 kubelet 提供默认值。 有趣的示例包括使用其他容器运行时或通过服务器设置不同的默认子网。

如果你想使用子网 10.96.0.0/12 作为服务的默认网段,你可以给 kubeadm 传递 --service-cidr 参数:

kubeadm init --service-cidr 10.96.0.0/12

现在,可以从该子网分配服务的虚拟 IP。 你还需要通过 kubelet 使用 --cluster-dns 标志设置 DNS 地址。 在集群中的每个管理器和节点上的 kubelet 的设置需要相同。 kubelet 提供了一个版本化的结构化 API 对象,该对象可以配置 kubelet 中的大多数参数,并将此配置推送到集群中正在运行的每个 kubelet 上。 此对象被称为 KubeletConfigurationKubeletConfiguration 允许用户指定标志,例如用骆峰值代表集群的 DNS IP 地址,如下所示:

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
clusterDNS:
- 10.96.0.10

有关 KubeletConfiguration 的更多详细信息,请参阅本节

提供特定于某实例的配置细节

由于硬件、操作系统、网络或者其他主机特定参数的差异。某些主机需要特定的 kubelet 配置。 以下列表提供了一些示例。

  • 由 kubelet 配置标志 --resolv-conf 指定的 DNS 解析文件的路径在操作系统之间可能有所不同, 它取决于你是否使用 systemd-resolved。 如果此路径错误,则在其 kubelet 配置错误的节点上 DNS 解析也将失败。

  • 除非你使用云驱动,否则默认情况下 Node API 对象的 .metadata.name 会被设置为计算机的主机名。 如果你需要指定一个与机器的主机名不同的节点名称,你可以使用 --hostname-override 标志覆盖默认值。

  • 当前,kubelet 无法自动检测容器运行时使用的 cgroup 驱动程序, 但是值 --cgroup-driver 必须与容器运行时使用的 cgroup 驱动程序匹配,以确保 kubelet 的健康运行状况。

  • 要指定容器运行时,你必须用 --container-runtime-endpoint=<path> 标志来指定端点。

应用此类特定于实例的配置的推荐方法是使用 KubeletConfiguration 补丁

使用 kubeadm 配置 kubelet

如果自定义的 KubeletConfiguration API 对象使用像 kubeadm ... --config some-config-file.yaml 这样的配置文件进行传递,则可以配置 kubeadm 启动的 kubelet。

通过调用 kubeadm config print init-defaults --component-configs KubeletConfiguration, 你可以看到此结构中的所有默认值。

也可以在基础 KubeletConfiguration 上应用实例特定的补丁。 阅读自定义 kubelet 来获取有关各个字段的更多信息。

使用 kubeadm init 时的工作流程

当调用 kubeadm init 时,kubelet 的配置会被写入磁盘 /var/lib/kubelet/config.yaml, 并上传到集群 kube-system 命名空间的 kubelet-config ConfigMap。 kubelet 配置信息也被写入 /etc/kubernetes/kubelet.conf,其中包含集群内所有 kubelet 的基线配置。 此配置文件指向允许 kubelet 与 API 服务器通信的客户端证书。 这解决了将集群级配置传播到每个 kubelet 的需求。

针对为特定实例提供配置细节的第二种模式, kubeadm 的解决方法是将环境文件写入 /var/lib/kubelet/kubeadm-flags.env,其中包含了一个标志列表, 当 kubelet 启动时,该标志列表会传递给 kubelet 标志在文件中的显示方式如下:

KUBELET_KUBEADM_ARGS="--flag1=value1 --flag2=value2 ..."

除了启动 kubelet 时所使用的标志外,该文件还包含动态参数,例如 cgroup 驱动程序以及是否使用其他容器运行时套接字(--cri-socket)。

将这两个文件编组到磁盘后,如果使用 systemd,则 kubeadm 尝试运行以下两个命令:

systemctl daemon-reload && systemctl restart kubelet

如果重新加载和重新启动成功,则正常的 kubeadm init 工作流程将继续。

使用 kubeadm join 时的工作流程

当运行 kubeadm join 时,kubeadm 使用 Bootstrap Token 证书执行 TLS 引导,该引导会获取一份证书, 该证书需要下载 kubelet-config ConfigMap 并把它写入 /var/lib/kubelet/config.yaml 中。 动态环境文件的生成方式恰好与 kubeadm init 完全相同。

接下来,kubeadm 运行以下两个命令将新配置加载到 kubelet 中:

systemctl daemon-reload && systemctl restart kubelet

在 kubelet 加载新配置后,kubeadm 将写入 /etc/kubernetes/bootstrap-kubelet.conf KubeConfig 文件中, 该文件包含 CA 证书和引导程序令牌。 kubelet 使用这些证书执行 TLS 引导程序并获取唯一的凭据,该凭据被存储在 /etc/kubernetes/kubelet.conf 中。

/etc/kubernetes/kubelet.conf 文件被写入后,kubelet 就完成了 TLS 引导过程。 kubeadm 在完成 TLS 引导过程后将删除 /etc/kubernetes/bootstrap-kubelet.conf 文件。

kubelet 的 systemd drop-in 文件

kubeadm 中附带了有关系统如何运行 kubelet 的 systemd 配置文件。 请注意 kubeadm CLI 命令不会修改此文件。

通过 kubeadm 安装的配置文件被写入 /usr/lib/systemd/system/kubelet.service.d/10-kubeadm.conf 并由 systemd 使用。它对原来的 kubelet.service 作了增强。

如果你想进一步覆盖它,可以创建一个目录/etc/systemd/system/kubelet.service.d/ (而不是/usr/lib/systemd/kubelet.service.d/),并将你自己的自定义配置写入到该目录下的一个文件中。

例如,您可以添加一个新的本地文件/etc/systemd/system/kubelet.service.d/local-overrides.conf 以覆盖“kubeadm”配置的单元设置。

以下是你可能在“/usr/lib/systemd/kubelet.service.d/10 kubeadm.conf”中找到的内容:

[Service]
Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf"
Environment="KUBELET_CONFIG_ARGS=--config=/var/lib/kubelet/config.yaml"
# 这是 "kubeadm init" 和 "kubeadm join" 运行时生成的文件,
# 动态地填充 KUBELET_KUBEADM_ARGS 变量
EnvironmentFile=-/var/lib/kubelet/kubeadm-flags.env
# 这是一个文件,用户在不得已下可以将其用作替代 kubelet args。
# 用户最好使用 .NodeRegistration.KubeletExtraArgs 对象在配置文件中替代。
# KUBELET_EXTRA_ARGS 应该从此文件中获取。
EnvironmentFile=-/etc/default/kubelet
ExecStart=
ExecStart=/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_KUBEADM_ARGS $KUBELET_EXTRA_ARGS

此文件指定由 kubeadm 为 kubelet 管理的所有文件的默认位置。

  • 用于 TLS 引导程序的 KubeConfig 文件为 /etc/kubernetes/bootstrap-kubelet.conf, 但仅当 /etc/kubernetes/kubelet.conf 不存在时才能使用。
  • 具有唯一 kubelet 标识的 KubeConfig 文件为 /etc/kubernetes/kubelet.conf
  • 包含 kubelet 的组件配置的文件为 /var/lib/kubelet/config.yaml
  • 包含的动态环境的文件 KUBELET_KUBEADM_ARGS 是来源于 /var/lib/kubelet/kubeadm-flags.env
  • 包含用户指定标志替代的文件 KUBELET_EXTRA_ARGS 是来源于 /etc/default/kubelet(对于 DEB),或者 /etc/sysconfig/kubelet(对于 RPM)。 KUBELET_EXTRA_ARGS 在标志链中排在最后,并且在设置冲突时具有最高优先级。

Kubernetes 可执行文件和软件包内容

Kubernetes 版本对应的 DEB 和 RPM 软件包是:

软件包名称描述
kubeadm给 kubelet 安装 /usr/bin/kubeadm CLI 工具和 kubelet 的 systemd drop-in 文件
kubelet安装 /usr/bin/kubelet 可执行文件。
kubectl安装 /usr/bin/kubectl 可执行文件。
cri-toolscri-tools git 仓库中安装 /usr/bin/crictl 可执行文件。
kubernetes-cniplugins git 仓库中安装 /opt/cni/bin 可执行文件。

2.2.2.1.9 - 使用 kubeadm 支持双协议栈

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

你的集群包含双协议栈组网支持, 这意味着集群网络允许你在两种地址族间任选其一。在集群中,控制面可以为同一个 Pod 或者 Service 同时赋予 IPv4 和 IPv6 地址。

准备开始

你需要已经遵从安装 kubeadm 中所给的步骤安装了 kubeadm 工具。

针对你要作为节点使用的每台服务器, 确保其允许 IPv6 转发。在 Linux 节点上,你可以通过以 root 用户在每台服务器上运行 sysctl -w net.ipv6.conf.all.forwarding=1 来完成设置。

你需要一个可以使用的 IPv4 和 IPv6 地址范围。集群操作人员通常对于 IPv4 使用 私有地址范围。对于 IPv6,集群操作人员通常会基于分配给该操作人员的地址范围, 从 2000::/3 中选择一个全局的单播地址块。你不需要将集群的 IP 地址范围路由到公众互联网。

所分配的 IP 地址数量应该与你计划运行的 Pod 和 Service 的数量相适应。

创建双协议栈集群

要使用 kubeadm init 创建一个双协议栈集群,你可以传递与下面的例子类似的命令行参数:

# 这里的地址范围仅作示例使用
kubeadm init --pod-network-cidr=10.244.0.0/16,2001:db8:42:0::/56 --service-cidr=10.96.0.0/16,2001:db8:42:1::/112

为了更便于理解,参看下面的名为 kubeadm-config.yaml 的 kubeadm 配置文件, 该文件用于双协议栈控制面的主控制节点。

---
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
networking:
  podSubnet: 10.244.0.0/16,2001:db8:42:0::/56
  serviceSubnet: 10.96.0.0/16,2001:db8:42:1::/112
---
apiVersion: kubeadm.k8s.io/v1beta3
kind: InitConfiguration
localAPIEndpoint:
  advertiseAddress: "10.100.0.1"
  bindPort: 6443
nodeRegistration:
  kubeletExtraArgs:
    node-ip: 10.100.0.2,fd00:1:2:3::2

InitConfiguration 中的 advertiseAddress 给出 API 服务器将公告自身要监听的 IP 地址。advertiseAddress 的取值与 kubeadm init 的标志 --apiserver-advertise-address 的取值相同。

运行 kubeadm 来实例化双协议栈控制面节点:

kubeadm init --config=kubeadm-config.yaml

kube-controller-manager 标志 --node-cidr-mask-size-ipv4|--node-cidr-mask-size-ipv6 是使用默认值来设置的。参见配置 IPv4/IPv6 双协议栈

向双协议栈集群添加节点

在添加节点之前,请确保该节点具有 IPv6 可路由的网络接口并且启用了 IPv6 转发。

下面的名为 kubeadm-config.yaml 的 kubeadm 配置文件 示例用于向集群中添加工作节点。

apiVersion: kubeadm.k8s.io/v1beta3
kind: JoinConfiguration
discovery:
  bootstrapToken:
    apiServerEndpoint: 10.100.0.1:6443
    token: "clvldh.vjjwg16ucnhp94qr"
    caCertHashes:
    - "sha256:a4863cde706cfc580a439f842cc65d5ef112b7b2be31628513a9881cf0d9fe0e"
    # 请更改上面的认证信息,使之与你的集群中实际使用的令牌和 CA 证书匹配
nodeRegistration:
  kubeletExtraArgs:
    node-ip: 10.100.0.3,fd00:1:2:3::3

下面的名为 kubeadm-config.yaml 的 kubeadm 配置文件 示例用于向集群中添加另一个控制面节点。

apiVersion: kubeadm.k8s.io/v1beta3
kind: JoinConfiguration
controlPlane:
  localAPIEndpoint:
    advertiseAddress: "10.100.0.2"
    bindPort: 6443
discovery:
  bootstrapToken:
    apiServerEndpoint: 10.100.0.1:6443
    token: "clvldh.vjjwg16ucnhp94qr"
    caCertHashes:
    - "sha256:a4863cde706cfc580a439f842cc65d5ef112b7b2be31628513a9881cf0d9fe0e"
    # 请更改上面的认证信息,使之与你的集群中实际使用的令牌和 CA 证书匹配
nodeRegistration:
  kubeletExtraArgs:
    node-ip: 10.100.0.4,fd00:1:2:3::4

JoinConfiguration.controlPlane 中的 advertiseAddress 设定 API 服务器将公告自身要监听的 IP 地址。advertiseAddress 的取值与 kubeadm join 的标志 --apiserver-advertise-address 的取值相同。

kubeadm join --config=kubeadm-config.yaml

创建单协议栈集群

为了更便于理解,参看下面的名为 kubeadm-config.yaml 的 kubeadm 配置文件示例, 该文件用于单协议栈控制面节点。

apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
networking:
  podSubnet: 10.244.0.0/16
  serviceSubnet: 10.96.0.0/16

接下来

2.2.3 - Turnkey 云解决方案

本页列示 Kubernetes 认证解决方案供应商。 在每一个供应商分页,你可以学习如何安装和设置生产就绪的集群。

2.3 - 最佳实践

2.3.1 - 大规模集群的注意事项

集群是运行 Kubernetes 代理的、 由控制平面管理的一组 节点(物理机或虚拟机)。 Kubernetes v1.30 单个集群支持的最大节点数为 5,000。 更具体地说,Kubernetes 旨在适应满足以下所有标准的配置:

  • 每个节点的 Pod 数量不超过 110
  • 节点数不超过 5,000
  • Pod 总数不超过 150,000
  • 容器总数不超过 300,000

你可以通过添加或删除节点来扩展集群。集群扩缩的方式取决于集群的部署方式。

云供应商资源配额

为避免遇到云供应商配额问题,在创建具有大规模节点的集群时,请考虑以下事项:

  • 请求增加云资源的配额,例如:
    • 计算实例
    • CPU
    • 存储卷
    • 使用中的 IP 地址
    • 数据包过滤规则集
    • 负载均衡数量
    • 网络子网
    • 日志流
  • 由于某些云供应商限制了创建新实例的速度,因此通过分批启动新节点来控制集群扩展操作,并在各批之间有一个暂停。

控制面组件

对于大型集群,你需要一个具有足够计算能力和其他资源的控制平面。

通常,你将在每个故障区域运行一个或两个控制平面实例, 先垂直缩放这些实例,然后在到达下降点(垂直)后再水平缩放。

你应该在每个故障区域至少应运行一个实例,以提供容错能力。 Kubernetes 节点不会自动将流量引向相同故障区域中的控制平面端点。 但是,你的云供应商可能有自己的机制来执行此操作。

例如,使用托管的负载均衡器时,你可以配置负载均衡器发送源自故障区域 A 中的 kubelet 和 Pod 的流量, 并将该流量仅定向到也位于区域 A 中的控制平面主机。 如果单个控制平面主机或端点故障区域 A 脱机,则意味着区域 A 中的节点的所有控制平面流量现在都在区域之间发送。 在每个区域中运行多个控制平面主机能降低出现这种结果的可能性。

etcd 存储

为了提高大规模集群的性能,你可以将事件对象存储在单独的专用 etcd 实例中。

在创建集群时,你可以(使用自定义工具):

  • 启动并配置额外的 etcd 实例
  • 配置 API 服务器,将它用于存储事件

有关为大型集群配置和管理 etcd 的详细信息, 请参阅为 Kubernetes 运行 etcd 集群 和使用 kubeadm 创建一个高可用 etcd 集群

插件资源

Kubernetes 资源限制 有助于最大程度地减少内存泄漏的影响以及 Pod 和容器可能对其他组件的其他方式的影响。 这些资源限制适用于插件资源, 就像它们适用于应用程序工作负载一样。

例如,你可以对日志组件设置 CPU 和内存限制:

  ...
  containers:
  - name: fluentd-cloud-logging
    image: fluent/fluentd-kubernetes-daemonset:v1
    resources:
      limits:
        cpu: 100m
        memory: 200Mi

插件的默认限制通常基于从中小规模 Kubernetes 集群上运行每个插件的经验收集的数据。 插件在大规模集群上运行时,某些资源消耗常常比其默认限制更多。 如果在不调整这些值的情况下部署了大规模集群,则插件可能会不断被杀死,因为它们不断达到内存限制。 或者,插件可能会运行,但由于 CPU 时间片的限制而导致性能不佳。

为避免遇到集群插件资源问题,在创建大规模集群时,请考虑以下事项:

  • 部分垂直扩展插件 —— 总有一个插件副本服务于整个集群或服务于整个故障区域。 对于这些附加组件,请在扩大集群时加大资源请求和资源限制。
  • 许多水平扩展插件 —— 你可以通过运行更多的 Pod 来增加容量——但是在大规模集群下, 可能还需要稍微提高 CPU 或内存限制。 VerticalPodAutoscaler 可以在 recommender 模式下运行, 以提供有关请求和限制的建议数字。
  • 一些插件在每个节点上运行一个副本,并由 DaemonSet 控制: 例如,节点级日志聚合器。与水平扩展插件的情况类似, 你可能还需要稍微提高 CPU 或内存限制。

接下来

  • VerticalPodAutoscaler 是一种自定义资源,你可以将其部署到集群中,帮助你管理 Pod 的资源请求和资源限制。 了解有关 Vertical Pod Autoscaler 的更多信息,了解如何用它扩展集群组件(包括对集群至关重要的插件)的信息。

  • 阅读关于集群自动扩缩容的信息。

  • addon resizer 可帮助你在集群规模变化时自动调整插件的大小。

2.3.2 - 运行于多可用区环境

本页描述如何跨多个区(Zone)运行集群。

背景

Kubernetes 从设计上允许同一个 Kubernetes 集群跨多个失效区来运行, 通常这些区位于某个称作 区域(Region) 逻辑分组中。 主要的云提供商都将区域定义为一组失效区的集合(也称作 可用区(Availability Zones)), 能够提供一组一致的功能特性:每个区域内,各个可用区提供相同的 API 和服务。

典型的云体系结构都会尝试降低某个区中的失效影响到其他区中服务的概率。

控制面行为

所有的控制面组件 都支持以一组可相互替换的资源池的形式来运行,每个组件都有多个副本。

当你部署集群控制面时,应将控制面组件的副本跨多个失效区来部署。 如果可用性是一个很重要的指标,应该选择至少三个失效区, 并将每个控制面组件(API 服务器、调度器、etcd、控制器管理器)复制多个副本, 跨至少三个失效区来部署。如果你在运行云控制器管理器, 则也应该将该组件跨所选的三个失效区来部署。

节点行为

Kubernetes 自动为负载资源(如 DeploymentStatefulSet) 跨集群中不同节点来部署其 Pod。 这种分布逻辑有助于降低失效带来的影响。

节点启动时,每个节点上的 kubelet 会向 Kubernetes API 中代表该 kubelet 的 Node 对象添加标签。 这些标签可能包含区信息

如果你的集群跨了多个可用区或者地理区域,你可以使用节点标签,结合 Pod 拓扑分布约束 来控制如何在你的集群中多个失效域之间分布 Pod。这里的失效域可以是地理区域、可用区甚至是特定节点。 这些提示信息使得调度器能够更好地调度 Pod,以实现更好的可用性,降低因为某种失效给整个工作负载带来的风险。

例如,你可以设置一种约束,确保某个 StatefulSet 中的 3 个副本都运行在不同的可用区中, 只要其他条件允许。你可以通过声明的方式来定义这种约束, 而不需要显式指定每个工作负载使用哪些可用区。

跨多个区分布节点

Kubernetes 的核心逻辑并不会帮你创建节点,你需要自行完成此操作,或者使用类似 Cluster API 这类工具来替你管理节点。

使用类似 Cluster API 这类工具,你可以跨多个失效域来定义一组用做你的集群工作节点的机器, 以及当整个区的服务出现中断时如何自动治愈集群的策略。

为 Pod 手动指定区

你可以应用节点选择算符约束到你所创建的 Pod 上,或者为 Deployment、StatefulSet 或 Job 这类工作负载资源中的 Pod 模板设置此类约束。

跨区的存储访问

当创建持久卷时,Kubernetes 会自动向那些链接到特定区的 PersistentVolume 添加区标签。 调度器通过其 NoVolumeZoneConflict 断言确保申领给定 PersistentVolume 的 Pod 只会被调度到该卷所在的可用区。

请注意,添加区标签的方法可能取决于你的云提供商和存储制备器。 请参阅具体的环境文档,确保配置正确。

请注意,添加区标签的方法可能会根据你所使用的云提供商和存储制备器而有所不同。 为确保配置正确,请始终参阅你的环境的特定文档。

你可以为 PersistentVolumeClaim 指定 StorageClass 以设置该类中的存储可以使用的失效域(区)。 要了解如何配置能够感知失效域或区的 StorageClass, 请参阅可用的拓扑逻辑

网络

Kubernetes 自身不提供与可用区相关的联网配置。 你可以使用网络插件 来配置集群的联网,该网络解决方案可能拥有一些与可用区相关的元素。 例如,如果你的云提供商支持 type=LoadBalancer 的 Service, 则负载均衡器可能仅会将请求流量发送到运行在负责处理给定连接的负载均衡器组件所在的区。 请查阅云提供商的文档了解详细信息。

对于自定义的或本地集群部署,也可以考虑这些因素。 ServiceIngress 的行为, 包括处理不同失效区的方法,在很大程度上取决于你的集群是如何搭建的。

失效恢复

在搭建集群时,你可能需要考虑当某区域中的所有失效区都同时掉线时,是否以及如何恢复服务。 例如,你是否要求在某个区中至少有一个节点能够运行 Pod? 请确保任何对集群很关键的修复工作都不要指望集群中至少有一个健康节点。 例如:当所有节点都不健康时,你可能需要运行某个修复性的 Job, 该 Job 要设置特定的容忍度, 以便修复操作能够至少将一个节点恢复为可用状态。

Kubernetes 对这类问题没有现成的解决方案;不过这也是要考虑的因素之一。

接下来

要了解调度器如何在集群中放置 Pod 并遵从所配置的约束, 可参阅调度与驱逐

2.3.3 - 校验节点设置

节点一致性测试

节点一致性测试 是一个容器化的测试框架,提供了针对节点的系统验证和功能测试。 测试验证节点是否满足 Kubernetes 的最低要求;通过测试的节点有资格加入 Kubernetes 集群。

该测试主要检测节点是否满足 Kubernetes 的最低要求,通过检测的节点有资格加入 Kubernetes 集群。

节点的前提条件

要运行节点一致性测试,节点必须满足与标准 Kubernetes 节点相同的前提条件。节点至少应安装以下守护程序:

  • 与 CRI 兼容的容器运行时,例如 Docker、Containerd 和 CRI-O
  • Kubelet

运行节点一致性测试

要运行节点一致性测试,请执行以下步骤:

  1. 得出 kubelet 的 --kubeconfig 的值;例如:--kubeconfig=/var/lib/kubelet/config.yaml。 由于测试框架启动了本地控制平面来测试 kubelet,因此使用 http://localhost:8080 作为API 服务器的 URL。 一些其他的 kubelet 命令行参数可能会被用到:
    • --cloud-provider:如果使用 --cloud-provider=gce,需要移除这个参数来运行测试。
  1. 使用以下命令运行节点一致性测试:

    # $CONFIG_DIR 是你 Kubelet 的 pod manifest 路径。
    # $LOG_DIR 是测试的输出路径。
    sudo docker run -it --rm --privileged --net=host \
      -v /:/rootfs -v $CONFIG_DIR:$CONFIG_DIR -v $LOG_DIR:/var/result \
      registry.k8s.io/node-test:0.2
    

针对其他硬件体系结构运行节点一致性测试

Kubernetes 也为其他硬件体系结构的系统提供了节点一致性测试的 Docker 镜像:

架构镜像
amd64node-test-amd64
armnode-test-arm
arm64node-test-arm64

运行特定的测试

要运行特定测试,请使用你希望运行的测试的特定表达式覆盖环境变量 FOCUS

sudo docker run -it --rm --privileged --net=host \
  -v /:/rootfs:ro -v $CONFIG_DIR:$CONFIG_DIR -v $LOG_DIR:/var/result \
  -e FOCUS=MirrorPod \ # Only run MirrorPod test
  registry.k8s.io/node-test:0.2

要跳过特定的测试,请使用你希望跳过的测试的常规表达式覆盖环境变量 SKIP

sudo docker run -it --rm --privileged --net=host \
  -v /:/rootfs:ro -v $CONFIG_DIR:$CONFIG_DIR -v $LOG_DIR:/var/result \
  -e SKIP=MirrorPod \ # 运行除 MirrorPod 测试外的所有一致性测试内容
  registry.k8s.io/node-test:0.2

节点一致性测试是节点端到端测试的容器化版本。

默认情况下,它会运行所有一致性测试。

理论上,只要合理地配置容器和挂载所需的卷,就可以运行任何的节点端到端测试用例。但是这里强烈建议只运行一致性测试,因为运行非一致性测试需要很多复杂的配置。

注意事项

  • 测试会在节点上遗留一些 Docker 镜像,包括节点一致性测试本身的镜像和功能测试相关的镜像。
  • 测试会在节点上遗留一些死的容器。这些容器是在功能测试的过程中创建的。

2.3.4 - 强制实施 Pod 安全性标准

本页提供实施 Pod 安全标准(Pod Security Standards) 时的一些最佳实践。

使用内置的 Pod 安全性准入控制器

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

Pod 安全性准入控制器 尝试替换已被废弃的 PodSecurityPolicies。

配置所有集群名字空间

完全未经配置的名字空间应该被视为集群安全模型中的重大缺陷。 我们建议花一些时间来分析在每个名字空间中执行的负载的类型, 并通过引用 Pod 安全性标准来确定每个负载的合适级别。 未设置标签的名字空间应该视为尚未被评估。

针对所有名字空间中的所有负载都具有相同的安全性需求的场景, 我们提供了一个示例 用来展示如何批量应用 Pod 安全性标签。

拥抱最小特权原则

在一个理想环境中,每个名字空间中的每个 Pod 都会满足 restricted 策略的需求。 不过,这既不可能也不现实,某些负载会因为合理的原因而需要特权上的提升。

  • 允许 privileged 负载的名字空间需要建立并实施适当的访问控制机制。
  • 对于运行在特权宽松的名字空间中的负载,需要维护其独特安全性需求的文档。 如果可能的话,要考虑如何进一步约束这些需求。

采用多种模式的策略

Pod 安全性标准准入控制器的 auditwarn 模式(mode) 能够在不影响现有负载的前提下,让该控制器更方便地收集关于 Pod 的重要的安全信息。

针对所有名字空间启用这些模式是一种好的实践,将它们设置为你最终打算 enforce期望的 级别和版本。这一阶段中所生成的警告和审计注解信息可以帮助你到达这一状态。 如果你期望负载的作者能够作出变更以便适应期望的级别,可以启用 warn 模式。 如果你希望使用审计日志了监控和驱动变更,以便负载能够适应期望的级别,可以启用 audit 模式。

当你将 enforce 模式设置为期望的取值时,这些模式在不同的场合下仍然是有用的:

  • 通过将 warn 设置为 enforce 相同的级别,客户可以在尝试创建无法通过合法检查的 Pod (或者包含 Pod 模板的资源)时收到警告信息。这些信息会帮助于更新资源使其合规。
  • 在将 enforce 锁定到特定的非最新版本的名字空间中,将 auditwarn 模式设置为 enforce 一样的级别而非 latest 版本, 这样可以方便看到之前版本所允许但当前最佳实践中被禁止的设置。

第三方替代方案

Kubernetes 生态系统中也有一些其他强制实施安全设置的替代方案处于开发状态中:

采用 内置的 方案(例如 PodSecurity 准入控制器)还是第三方工具, 这一决策完全取决于你自己的情况。在评估任何解决方案时,对供应链的信任都是至关重要的。 最终,使用前述方案中的 任何 一种都好过放任自流。

2.3.5 - PKI 证书和要求

Kubernetes 需要 PKI 证书才能进行基于 TLS 的身份验证。如果你是使用 kubeadm 安装的 Kubernetes, 则会自动生成集群所需的证书。你还可以生成自己的证书。 例如,不将私钥存储在 API 服务器上,可以让私钥更加安全。此页面说明了集群必需的证书。

集群是如何使用证书的

Kubernetes 需要 PKI 才能执行以下操作:

  • Kubelet 的客户端证书,用于 API 服务器身份验证
  • Kubelet 服务端证书, 用于 API 服务器与 Kubelet 的会话
  • API 服务器端点的证书
  • 集群管理员的客户端证书,用于 API 服务器身份认证
  • API 服务器的客户端证书,用于和 Kubelet 的会话
  • API 服务器的客户端证书,用于和 etcd 的会话
  • 控制器管理器的客户端证书或 kubeconfig,用于和 API 服务器的会话
  • 调度器的客户端证书或 kubeconfig,用于和 API 服务器的会话
  • 前端代理的客户端及服务端证书

etcd 还实现了双向 TLS 来对客户端和对其他对等节点进行身份验证。

证书存储位置

假如你通过 kubeadm 安装 Kubernetes,大多数证书会被存储在 /etc/kubernetes/pki 中。 本文档中的所有路径都是相对于该目录的,但用户账号证书除外,kubeadm 将其放在 /etc/kubernetes 中。

手动配置证书

如果你不想通过 kubeadm 生成所需证书,你可以使用一个单根 CA 来创建这些证书,或者直接提供所有证书。 参见证书以进一步了解如何创建自己的证书授权机构。 更多关于管理证书的信息,请参阅使用 kubeadm 进行证书管理

单根 CA

你可以创建由管理员控制的单根 CA。这个根 CA 可以创建多个中间 CA, 并将所有进一步的创建委托给 Kubernetes 本身。

需要这些 CA:

路径默认 CN描述
ca.crt,keykubernetes-caKubernetes 通用 CA
etcd/ca.crt,keyetcd-ca与 etcd 相关的所有功能
front-proxy-ca.crt,keykubernetes-front-proxy-ca用于前端代理

上面的 CA 之外,还需要获取用于服务账号管理的密钥对,也就是 sa.keysa.pub

下面的例子说明了上表中所示的 CA 密钥和证书文件。

/etc/kubernetes/pki/ca.crt
/etc/kubernetes/pki/ca.key
/etc/kubernetes/pki/etcd/ca.crt
/etc/kubernetes/pki/etcd/ca.key
/etc/kubernetes/pki/front-proxy-ca.crt
/etc/kubernetes/pki/front-proxy-ca.key

所有的证书

如果你不想将 CA 的私钥拷贝至你的集群中,你也可以自己生成全部的证书。

需要这些证书:

默认 CN父级 CAO(位于 Subject 中)kind主机 (SAN)
kube-etcdetcd-caserver, client<hostname>, <Host_IP>, localhost, 127.0.0.1
kube-etcd-peeretcd-caserver, client<hostname>, <Host_IP>, localhost, 127.0.0.1
kube-etcd-healthcheck-clientetcd-caclient
kube-apiserver-etcd-clientetcd-caclient
kube-apiserverkubernetes-caserver<hostname>, <Host_IP>, <advertise_IP>, [1]
kube-apiserver-kubelet-clientkubernetes-casystem:mastersclient
front-proxy-clientkubernetes-front-proxy-caclient

[1]: 用来连接到集群的不同 IP 或 DNS 名称 (就像 kubeadm 为负载均衡所使用的固定 IP 或 DNS 名称:kuberneteskubernetes.defaultkubernetes.default.svckubernetes.default.svc.clusterkubernetes.default.svc.cluster.local)。

其中 kind 对应一种或多种类型的 x509 密钥用途,也可记录在 CertificateSigningRequest 类型的 .spec.usages 中:

kind密钥用途
server数字签名、密钥加密、服务端认证
client数字签名、密钥加密、客户端认证

证书路径

证书应放置在建议的路径中(以便 kubeadm 使用)。无论使用什么位置,都应使用给定的参数指定路径。

默认 CN建议的密钥路径建议的证书路径命令密钥参数证书参数
etcd-caetcd/ca.keyetcd/ca.crtkube-apiserver--etcd-cafile
kube-apiserver-etcd-clientapiserver-etcd-client.keyapiserver-etcd-client.crtkube-apiserver--etcd-keyfile--etcd-certfile
kubernetes-caca.keyca.crtkube-apiserver--client-ca-file
kubernetes-caca.keyca.crtkube-controller-manager--cluster-signing-key-file--client-ca-file, --root-ca-file, --cluster-signing-cert-file
kube-apiserverapiserver.keyapiserver.crtkube-apiserver--tls-private-key-file--tls-cert-file
kube-apiserver-kubelet-clientapiserver-kubelet-client.keyapiserver-kubelet-client.crtkube-apiserver--kubelet-client-key--kubelet-client-certificate
front-proxy-cafront-proxy-ca.keyfront-proxy-ca.crtkube-apiserver--requestheader-client-ca-file
front-proxy-cafront-proxy-ca.keyfront-proxy-ca.crtkube-controller-manager--requestheader-client-ca-file
front-proxy-clientfront-proxy-client.keyfront-proxy-client.crtkube-apiserver--proxy-client-key-file--proxy-client-cert-file
etcd-caetcd/ca.keyetcd/ca.crtetcd--trusted-ca-file, --peer-trusted-ca-file
kube-etcdetcd/server.keyetcd/server.crtetcd--key-file--cert-file
kube-etcd-peeretcd/peer.keyetcd/peer.crtetcd--peer-key-file--peer-cert-file
etcd-caetcd/ca.crtetcdctl--cacert
kube-etcd-healthcheck-clientetcd/healthcheck-client.keyetcd/healthcheck-client.crtetcdctl--key--cert

注意事项同样适用于服务账号密钥对:

私钥路径公钥路径命令参数
sa.keykube-controller-manager--service-account-private-key-file
sa.pubkube-apiserver--service-account-key-file

下面的例子展示了自行生成所有密钥和证书时所需要提供的文件路径。 这些路径基于前面的表格

/etc/kubernetes/pki/etcd/ca.key
/etc/kubernetes/pki/etcd/ca.crt
/etc/kubernetes/pki/apiserver-etcd-client.key
/etc/kubernetes/pki/apiserver-etcd-client.crt
/etc/kubernetes/pki/ca.key
/etc/kubernetes/pki/ca.crt
/etc/kubernetes/pki/apiserver.key
/etc/kubernetes/pki/apiserver.crt
/etc/kubernetes/pki/apiserver-kubelet-client.key
/etc/kubernetes/pki/apiserver-kubelet-client.crt
/etc/kubernetes/pki/front-proxy-ca.key
/etc/kubernetes/pki/front-proxy-ca.crt
/etc/kubernetes/pki/front-proxy-client.key
/etc/kubernetes/pki/front-proxy-client.crt
/etc/kubernetes/pki/etcd/server.key
/etc/kubernetes/pki/etcd/server.crt
/etc/kubernetes/pki/etcd/peer.key
/etc/kubernetes/pki/etcd/peer.crt
/etc/kubernetes/pki/etcd/healthcheck-client.key
/etc/kubernetes/pki/etcd/healthcheck-client.crt
/etc/kubernetes/pki/sa.key
/etc/kubernetes/pki/sa.pub

为用户账号配置证书

你必须手动配置以下管理员账号和服务账号:

文件名凭据名称默认 CNO (位于 Subject 中)
admin.confdefault-adminkubernetes-admin<admin-group>
super-admin.confdefault-super-adminkubernetes-super-adminsystem:masters
kubelet.confdefault-authsystem:node:<nodeName>(参阅注释)system:nodes
controller-manager.confdefault-controller-managersystem:kube-controller-manager
scheduler.confdefault-schedulersystem:kube-scheduler
  1. 对于每个配置,请都使用给定的 CN 和 O 生成 x509 证书/密钥偶对。

  2. 为每个配置运行下面的 kubectl 命令:

KUBECONFIG=<filename> kubectl config set-cluster default-cluster --server=https://<host ip>:6443 --certificate-authority <path-to-kubernetes-ca> --embed-certs
KUBECONFIG=<filename> kubectl config set-credentials <credential-name> --client-key <path-to-key>.pem --client-certificate <path-to-cert>.pem --embed-certs
KUBECONFIG=<filename> kubectl config set-context default-system --cluster default-cluster --user <credential-name>
KUBECONFIG=<filename> kubectl config use-context default-system

这些文件用途如下:

文件名命令说明
admin.confkubectl配置集群的管理员
super-admin.confkubectl为集群配置超级管理员用户
kubelet.confkubelet集群中的每个节点都需要一份
controller-manager.confkube-controller-manager必须添加到 manifests/kube-controller-manager.yaml 清单中
scheduler.confkube-scheduler必须添加到 manifests/kube-scheduler.yaml 清单中

下面是前表中所列文件的完整路径。

/etc/kubernetes/admin.conf
/etc/kubernetes/super-admin.conf
/etc/kubernetes/kubelet.conf
/etc/kubernetes/controller-manager.conf
/etc/kubernetes/scheduler.conf

3 - 概念

概念部分帮助你了解 Kubernetes 系统的各个部分以及 Kubernetes 用来表示集群的抽象概念, 并帮助你更深入地理解 Kubernetes 是如何工作的。

3.1 - 概述

Kubernetes 是一个可移植、可扩展的开源平台,用于管理容器化的工作负载和服务,方便进行声明式配置和自动化。Kubernetes 拥有一个庞大且快速增长的生态系统,其服务、支持和工具的使用范围广泛。

此页面是 Kubernetes 的概述。

Kubernetes 是一个可移植、可扩展的开源平台,用于管理容器化的工作负载和服务,可促进声明式配置和自动化。 Kubernetes 拥有一个庞大且快速增长的生态,其服务、支持和工具的使用范围相当广泛。

Kubernetes 这个名字源于希腊语,意为“舵手”或“飞行员”。K8s 这个缩写是因为 K 和 s 之间有 8 个字符的关系。 Google 在 2014 年开源了 Kubernetes 项目。 Kubernetes 建立在 Google 大规模运行生产工作负载十几年经验的基础上, 结合了社区中最优秀的想法和实践。

时光回溯

让我们回顾一下为何 Kubernetes 能够裨益四方。

部署演进

传统部署时代:

早期,各个组织是在物理服务器上运行应用程序。 由于无法限制在物理服务器中运行的应用程序资源使用,因此会导致资源分配问题。 例如,如果在同一台物理服务器上运行多个应用程序, 则可能会出现一个应用程序占用大部分资源的情况,而导致其他应用程序的性能下降。 一种解决方案是将每个应用程序都运行在不同的物理服务器上, 但是当某个应用程序资源利用率不高时,剩余资源无法被分配给其他应用程序, 而且维护许多物理服务器的成本很高。

虚拟化部署时代:

因此,虚拟化技术被引入了。虚拟化技术允许你在单个物理服务器的 CPU 上运行多台虚拟机(VM)。 虚拟化能使应用程序在不同 VM 之间被彼此隔离,且能提供一定程度的安全性, 因为一个应用程序的信息不能被另一应用程序随意访问。

虚拟化技术能够更好地利用物理服务器的资源,并且因为可轻松地添加或更新应用程序, 而因此可以具有更高的可扩缩性,以及降低硬件成本等等的好处。 通过虚拟化,你可以将一组物理资源呈现为可丢弃的虚拟机集群。

每个 VM 是一台完整的计算机,在虚拟化硬件之上运行所有组件,包括其自己的操作系统。

容器部署时代:

容器类似于 VM,但是更宽松的隔离特性,使容器之间可以共享操作系统(OS)。 因此,容器比起 VM 被认为是更轻量级的。且与 VM 类似,每个容器都具有自己的文件系统、CPU、内存、进程空间等。 由于它们与基础架构分离,因此可以跨云和 OS 发行版本进行移植。

容器因具有许多优势而变得流行起来,例如:

  • 敏捷应用程序的创建和部署:与使用 VM 镜像相比,提高了容器镜像创建的简便性和效率。
  • 持续开发、集成和部署:通过快速简单的回滚(由于镜像不可变性), 提供可靠且频繁的容器镜像构建和部署。
  • 关注开发与运维的分离:在构建、发布时创建应用程序容器镜像,而不是在部署时, 从而将应用程序与基础架构分离。
  • 可观察性:不仅可以显示 OS 级别的信息和指标,还可以显示应用程序的运行状况和其他指标信号。
  • 跨开发、测试和生产的环境一致性:在笔记本计算机上也可以和在云中运行一样的应用程序。
  • 跨云和操作系统发行版本的可移植性:可在 Ubuntu、RHEL、CoreOS、本地、 Google Kubernetes Engine 和其他任何地方运行。
  • 以应用程序为中心的管理:提高抽象级别,从在虚拟硬件上运行 OS 到使用逻辑资源在 OS 上运行应用程序。
  • 松散耦合、分布式、弹性、解放的微服务:应用程序被分解成较小的独立部分, 并且可以动态部署和管理 - 而不是在一台大型单机上整体运行。
  • 资源隔离:可预测的应用程序性能。
  • 资源利用:高效率和高密度。

为什么需要 Kubernetes,它能做什么?

容器是打包和运行应用程序的好方式。在生产环境中, 你需要管理运行着应用程序的容器,并确保服务不会下线。 例如,如果一个容器发生故障,则你需要启动另一个容器。 如果此行为交由给系统处理,是不是会更容易一些?

这就是 Kubernetes 要来做的事情! Kubernetes 为你提供了一个可弹性运行分布式系统的框架。 Kubernetes 会满足你的扩展要求、故障转移你的应用、提供部署模式等。 例如,Kubernetes 可以轻松管理系统的 Canary (金丝雀) 部署。

Kubernetes 为你提供:

  • 服务发现和负载均衡

    Kubernetes 可以使用 DNS 名称或自己的 IP 地址来暴露容器。 如果进入容器的流量很大, Kubernetes 可以负载均衡并分配网络流量,从而使部署稳定。

  • 存储编排

    Kubernetes 允许你自动挂载你选择的存储系统,例如本地存储、公共云提供商等。

  • 自动部署和回滚

    你可以使用 Kubernetes 描述已部署容器的所需状态, 它可以以受控的速率将实际状态更改为期望状态。 例如,你可以自动化 Kubernetes 来为你的部署创建新容器, 删除现有容器并将它们的所有资源用于新容器。

  • 自动完成装箱计算

    你为 Kubernetes 提供许多节点组成的集群,在这个集群上运行容器化的任务。 你告诉 Kubernetes 每个容器需要多少 CPU 和内存 (RAM)。 Kubernetes 可以将这些容器按实际情况调度到你的节点上,以最佳方式利用你的资源。

  • 自我修复

    Kubernetes 将重新启动失败的容器、替换容器、杀死不响应用户定义的运行状况检查的容器, 并且在准备好服务之前不将其通告给客户端。

  • 密钥与配置管理

    Kubernetes 允许你存储和管理敏感信息,例如密码、OAuth 令牌和 SSH 密钥。 你可以在不重建容器镜像的情况下部署和更新密钥和应用程序配置,也无需在堆栈配置中暴露密钥。

  • 批处理执行 除了服务外,Kubernetes 还可以管理你的批处理和 CI(持续集成)工作负载,如有需要,可以替换失败的容器。
  • 水平扩缩 使用简单的命令、用户界面或根据 CPU 使用率自动对你的应用进行扩缩。
  • IPv4/IPv6 双栈 为 Pod(容器组)和 Service(服务)分配 IPv4 和 IPv6 地址。
  • 为可扩展性设计 在不改变上游源代码的情况下为你的 Kubernetes 集群添加功能。

Kubernetes 不是什么

Kubernetes 不是传统的、包罗万象的 PaaS(平台即服务)系统。 由于 Kubernetes 是在容器级别运行,而非在硬件级别,它提供了 PaaS 产品共有的一些普遍适用的功能, 例如部署、扩展、负载均衡,允许用户集成他们的日志记录、监控和警报方案。 但是,Kubernetes 不是单体式(monolithic)系统,那些默认解决方案都是可选、可插拔的。 Kubernetes 为构建开发人员平台提供了基础,但是在重要的地方保留了用户选择权,能有更高的灵活性。

Kubernetes:

  • 不限制支持的应用程序类型。 Kubernetes 旨在支持极其多种多样的工作负载,包括无状态、有状态和数据处理工作负载。 如果应用程序可以在容器中运行,那么它应该可以在 Kubernetes 上很好地运行。
  • 不部署源代码,也不构建你的应用程序。 持续集成(CI)、交付和部署(CI/CD)工作流取决于组织的文化和偏好以及技术要求。
  • 不提供应用程序级别的服务作为内置服务,例如中间件(例如消息中间件)、 数据处理框架(例如 Spark)、数据库(例如 MySQL)、缓存、集群存储系统 (例如 Ceph)。这样的组件可以在 Kubernetes 上运行,并且/或者可以由运行在 Kubernetes 上的应用程序通过可移植机制(例如开放服务代理)来访问。
  • 不是日志记录、监视或警报的解决方案。 它集成了一些功能作为概念证明,并提供了收集和导出指标的机制。
  • 不提供也不要求配置用的语言、系统(例如 jsonnet),它提供了声明性 API, 该声明性 API 可以由任意形式的声明性规范所构成。
  • 不提供也不采用任何全面的机器配置、维护、管理或自我修复系统。
  • 此外,Kubernetes 不仅仅是一个编排系统,实际上它消除了编排的需要。 编排的技术定义是执行已定义的工作流程:首先执行 A,然后执行 B,再执行 C。 而 Kubernetes 包含了一组独立可组合的控制过程,可以持续地将当前状态驱动到所提供的预期状态。 你不需要在乎如何从 A 移动到 C,也不需要集中控制,这使得系统更易于使用且功能更强大、 系统更健壮,更为弹性和可扩展。

接下来

3.1.1 - Kubernetes 对象

Kubernetes 对象是 Kubernetes 系统中的持久性实体。 Kubernetes 使用这些实体表示你的集群状态。 了解 Kubernetes 对象模型以及如何使用这些对象。

本页说明了在 Kubernetes API 中是如何表示 Kubernetes 对象的, 以及如何使用 .yaml 格式的文件表示 Kubernetes 对象。

理解 Kubernetes 对象

在 Kubernetes 系统中,Kubernetes 对象是持久化的实体。 Kubernetes 使用这些实体去表示整个集群的状态。 具体而言,它们描述了如下信息:

  • 哪些容器化应用正在运行(以及在哪些节点上运行)
  • 可以被应用使用的资源
  • 关于应用运行时行为的策略,比如重启策略、升级策略以及容错策略

Kubernetes 对象是一种“意向表达(Record of Intent)”。一旦创建该对象, Kubernetes 系统将不断工作以确保该对象存在。通过创建对象,你本质上是在告知 Kubernetes 系统,你想要的集群工作负载状态看起来应是什么样子的, 这就是 Kubernetes 集群所谓的期望状态(Desired State)

操作 Kubernetes 对象 —— 无论是创建、修改或者删除 —— 需要使用 Kubernetes API。 比如,当使用 kubectl 命令行接口(CLI)时,CLI 会调用必要的 Kubernetes API; 也可以在程序中使用客户端库, 来直接调用 Kubernetes API。

对象规约(Spec)与状态(Status)

几乎每个 Kubernetes 对象包含两个嵌套的对象字段,它们负责管理对象的配置: 对象 spec(规约) 和对象 status(状态)。 对于具有 spec 的对象,你必须在创建对象时设置其内容,描述你希望对象所具有的特征: 期望状态(Desired State)

status 描述了对象的当前状态(Current State),它是由 Kubernetes 系统和组件设置并更新的。在任何时刻,Kubernetes 控制平面 都一直在积极地管理着对象的实际状态,以使之达成期望状态。

例如,Kubernetes 中的 Deployment 对象能够表示运行在集群中的应用。 当创建 Deployment 时,你可能会设置 Deployment 的 spec,指定该应用要有 3 个副本运行。 Kubernetes 系统读取 Deployment 的 spec, 并启动我们所期望的应用的 3 个实例 —— 更新状态以与规约相匹配。 如果这些实例中有的失败了(一种状态变更),Kubernetes 系统会通过执行修正操作来响应 specstatus 间的不一致 —— 意味着它会启动一个新的实例来替换。

关于对象 spec、status 和 metadata 的更多信息,可参阅 Kubernetes API 约定

描述 Kubernetes 对象

创建 Kubernetes 对象时,必须提供对象的 spec,用来描述该对象的期望状态, 以及关于对象的一些基本信息(例如名称)。 当使用 Kubernetes API 创建对象时(直接创建或经由 kubectl 创建), API 请求必须在请求主体中包含 JSON 格式的信息。 大多数情况下,你会通过 清单(Manifest) 文件为 kubectl 提供这些信息。 按照惯例,清单是 YAML 格式的(你也可以使用 JSON 格式)。 像 kubectl 这样的工具在通过 HTTP 进行 API 请求时, 会将清单中的信息转换为 JSON 或其他受支持的序列化格式。

这里有一个清单示例文件,展示了 Kubernetes Deployment 的必需字段和对象 spec

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2 # 告知 Deployment 运行 2 个与该模板匹配的 Pod
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

与上面使用清单文件来创建 Deployment 类似,另一种方式是使用 kubectl 命令行接口(CLI)的 kubectl apply 命令, 将 .yaml 文件作为参数。下面是一个示例:

kubectl apply -f https://k8s.io/examples/application/deployment.yaml

输出类似下面这样:

deployment.apps/nginx-deployment created

必需字段

在想要创建的 Kubernetes 对象所对应的清单(YAML 或 JSON 文件)中,需要配置的字段如下:

  • apiVersion - 创建该对象所使用的 Kubernetes API 的版本
  • kind - 想要创建的对象的类别
  • metadata - 帮助唯一标识对象的一些数据,包括一个 name 字符串、UID 和可选的 namespace
  • spec - 你所期望的该对象的状态

对每个 Kubernetes 对象而言,其 spec 之精确格式都是不同的,包含了特定于该对象的嵌套字段。 Kubernetes API 参考可以帮助你找到想要使用 Kubernetes 创建的所有对象的规约格式。

例如,参阅 Pod API 参考文档中 spec 字段。 对于每个 Pod,其 .spec 字段设置了 Pod 及其期望状态(例如 Pod 中每个容器的容器镜像名称)。 另一个对象规约的例子是 StatefulSet API 中的 spec 字段。 对于 StatefulSet 而言,其 .spec 字段设置了 StatefulSet 及其期望状态。 在 StatefulSet 的 .spec 内,有一个为 Pod 对象提供的模板。 该模板描述了 StatefulSet 控制器为了满足 StatefulSet 规约而要创建的 Pod。 不同类型的对象可以有不同的 .status 信息。API 参考页面给出了 .status 字段的详细结构, 以及针对不同类型 API 对象的具体内容。

服务器端字段验证

从 Kubernetes v1.25 开始,API 服务器提供了服务器端字段验证, 可以检测对象中未被识别或重复的字段。它在服务器端提供了 kubectl --validate 的所有功能。

kubectl 工具使用 --validate 标志来设置字段验证级别。它接受值 ignorewarnstrict,同时还接受值 true(等同于 strict)和 false(等同于 ignore)。kubectl 的默认验证设置为 --validate=true

Strict
严格的字段验证,验证失败时会报错
Warn
执行字段验证,但错误会以警告形式提供而不是拒绝请求
Ignore
不执行服务器端字段验证

kubectl 无法连接到支持字段验证的 API 服务器时,它将回退为使用客户端验证。 Kubernetes 1.27 及更高版本始终提供字段验证;较早的 Kubernetes 版本可能没有此功能。 如果你的集群版本低于 v1.27,可以查阅适用于你的 Kubernetes 版本的文档。

接下来

如果你刚开始学习 Kubernetes,可以进一步阅读以下信息:

Kubernetes 对象管理 介绍了如何使用 kubectl 来管理对象。 如果你还没有安装 kubectl,你可能需要安装 kubectl

从总体上了解 Kubernetes API,可以查阅:

若要更深入地了解 Kubernetes 对象,可以阅读本节的其他页面:

3.1.1.1 - Kubernetes 对象管理

kubectl 命令行工具支持多种不同的方式来创建和管理 Kubernetes 对象。 本文档概述了不同的方法。 阅读 Kubectl book 来了解 kubectl 管理对象的详细信息。

管理技巧

管理技术作用于建议的环境支持的写者学习难度
指令式命令活跃对象开发项目1+最低
指令式对象配置单个文件生产项目1中等
声明式对象配置文件目录生产项目1+最高

指令式命令

使用指令式命令时,用户可以在集群中的活动对象上进行操作。用户将操作传给 kubectl 命令作为参数或标志。

这是开始或者在集群中运行一次性任务的推荐方法。因为这个技术直接在活跃对象 上操作,所以它不提供以前配置的历史记录。

例子

通过创建 Deployment 对象来运行 nginx 容器的实例:

kubectl create deployment nginx --image nginx

权衡

与对象配置相比的优点:

  • 命令用单个动词表示。
  • 命令仅需一步即可对集群进行更改。

与对象配置相比的缺点:

  • 命令不与变更审查流程集成。
  • 命令不提供与更改关联的审核跟踪。
  • 除了实时内容外,命令不提供记录源。
  • 命令不提供用于创建新对象的模板。

指令式对象配置

在指令式对象配置中,kubectl 命令指定操作(创建,替换等),可选标志和 至少一个文件名。指定的文件必须包含 YAML 或 JSON 格式的对象的完整定义。

有关对象定义的详细信息,请查看 API 参考

例子

创建配置文件中定义的对象:

kubectl create -f nginx.yaml

删除两个配置文件中定义的对象:

kubectl delete -f nginx.yaml -f redis.yaml

通过覆盖活动配置来更新配置文件中定义的对象:

kubectl replace -f nginx.yaml

权衡

与指令式命令相比的优点:

  • 对象配置可以存储在源控制系统中,比如 Git。
  • 对象配置可以与流程集成,例如在推送和审计之前检查更新。
  • 对象配置提供了用于创建新对象的模板。

与指令式命令相比的缺点:

  • 对象配置需要对对象架构有基本的了解。
  • 对象配置需要额外的步骤来编写 YAML 文件。

与声明式对象配置相比的优点:

  • 指令式对象配置行为更加简单易懂。
  • 从 Kubernetes 1.5 版本开始,指令对象配置更加成熟。

与声明式对象配置相比的缺点:

  • 指令式对象配置更适合文件,而非目录。
  • 对活动对象的更新必须反映在配置文件中,否则会在下一次替换时丢失。

声明式对象配置

使用声明式对象配置时,用户对本地存储的对象配置文件进行操作,但是用户 未定义要对该文件执行的操作。 kubectl 会自动检测每个文件的创建、更新和删除操作。 这使得配置可以在目录上工作,根据目录中配置文件对不同的对象执行不同的操作。

例子

处理 configs 目录中的所有对象配置文件,创建并更新活跃对象。 可以首先使用 diff 子命令查看将要进行的更改,然后在进行应用:

kubectl diff -f configs/
kubectl apply -f configs/

递归处理目录:

kubectl diff -R -f configs/
kubectl apply -R -f configs/

权衡

与指令式对象配置相比的优点:

  • 对活动对象所做的更改即使未合并到配置文件中,也会被保留下来。
  • 声明性对象配置更好地支持对目录进行操作并自动检测每个文件的操作类型(创建,修补,删除)。

与指令式对象配置相比的缺点:

  • 声明式对象配置难于调试并且出现异常时结果难以理解。
  • 使用 diff 产生的部分更新会创建复杂的合并和补丁操作。

接下来

3.1.1.2 - 对象名称和 ID

集群中的每一个对象都有一个名称来标识在同类资源中的唯一性。

每个 Kubernetes 对象也有一个 UID 来标识在整个集群中的唯一性。

比如,在同一个名字空间 中只能有一个名为 myapp-1234 的 Pod,但是可以命名一个 Pod 和一个 Deployment 同为 myapp-1234

对于用户提供的非唯一性的属性,Kubernetes 提供了标签(Label)注解(Annotation)机制。

名称

客户端提供的字符串,引用资源 URL 中的对象,如/api/v1/pods/some name

某一时刻,只能有一个给定类型的对象具有给定的名称。但是,如果删除该对象,则可以创建同名的新对象。

名称在同一资源的所有 API 版本中必须是唯一的。 这些 API 资源通过各自的 API 组、资源类型、名字空间(对于划分名字空间的资源)和名称来区分。 换言之,API 版本在此上下文中是不相关的。

以下是比较常见的四种资源命名约束。

DNS 子域名

很多资源类型需要可以用作 DNS 子域名的名称。 DNS 子域名的定义可参见 RFC 1123。 这一要求意味着名称必须满足如下规则:

  • 不能超过 253 个字符
  • 只能包含小写字母、数字,以及 '-' 和 '.'
  • 必须以字母数字开头
  • 必须以字母数字结尾

RFC 1123 标签名

某些资源类型需要其名称遵循 RFC 1123 所定义的 DNS 标签标准。也就是命名必须满足如下规则:

  • 最多 63 个字符
  • 只能包含小写字母、数字,以及 '-'
  • 必须以字母数字开头
  • 必须以字母数字结尾

RFC 1035 标签名

某些资源类型需要其名称遵循 RFC 1035 所定义的 DNS 标签标准。也就是命名必须满足如下规则:

  • 最多 63 个字符
  • 只能包含小写字母、数字,以及 '-'
  • 必须以字母开头
  • 必须以字母数字结尾

路径分段名称

某些资源类型要求名称能被安全地用作路径中的片段。 换句话说,其名称不能是 ...,也不可以包含 /% 这些字符。

下面是一个名为 nginx-demo 的 Pod 的配置清单:

apiVersion: v1
kind: Pod
metadata:
  name: nginx-demo
spec:
  containers:
  - name: nginx
    image: nginx:1.14.2
    ports:
    - containerPort: 80

UID

Kubernetes 系统生成的字符串,唯一标识对象。

在 Kubernetes 集群的整个生命周期中创建的每个对象都有一个不同的 UID,它旨在区分类似实体的历史事件。

Kubernetes UID 是全局唯一标识符(也叫 UUID)。 UUID 是标准化的,见 ISO/IEC 9834-8 和 ITU-T X.667。

接下来

3.1.1.3 - 标签和选择算符

标签(Labels) 是附加到 Kubernetes 对象(比如 Pod)上的键值对。 标签旨在用于指定对用户有意义且相关的对象的标识属性,但不直接对核心系统有语义含义。 标签可以用于组织和选择对象的子集。标签可以在创建时附加到对象,随后可以随时添加和修改。 每个对象都可以定义一组键/值标签。每个键对于给定对象必须是唯一的。

"metadata": {
  "labels": {
    "key1" : "value1",
    "key2" : "value2"
  }
}

标签能够支持高效的查询和监听操作,对于用户界面和命令行是很理想的。 应使用注解记录非识别信息。

动机

标签使用户能够以松散耦合的方式将他们自己的组织结构映射到系统对象,而无需客户端存储这些映射。

服务部署和批处理流水线通常是多维实体(例如,多个分区或部署、多个发行序列、多个层,每层多个微服务)。 管理通常需要交叉操作,这打破了严格的层次表示的封装,特别是由基础设施而不是用户确定的严格的层次结构。

示例标签:

  • "release" : "stable", "release" : "canary"
  • "environment" : "dev", "environment" : "qa", "environment" : "production"
  • "tier" : "frontend", "tier" : "backend", "tier" : "cache"
  • "partition" : "customerA", "partition" : "customerB"
  • "track" : "daily", "track" : "weekly"

有一些常用标签的例子;你可以任意制定自己的约定。 请记住,标签的 Key 对于给定对象必须是唯一的。

语法和字符集

标签是键值对。有效的标签键有两个段:可选的前缀和名称,用斜杠(/)分隔。 名称段是必需的,必须小于等于 63 个字符,以字母数字字符([a-z0-9A-Z])开头和结尾, 带有破折号(-),下划线(_),点( .)和之间的字母数字。 前缀是可选的。如果指定,前缀必须是 DNS 子域:由点(.)分隔的一系列 DNS 标签,总共不超过 253 个字符, 后跟斜杠(/)。

如果省略前缀,则假定标签键对用户是私有的。 向最终用户对象添加标签的自动系统组件(例如 kube-schedulerkube-controller-managerkube-apiserverkubectl 或其他第三方自动化工具)必须指定前缀。

kubernetes.io/k8s.io/ 前缀是为 Kubernetes 核心组件保留的

有效标签值:

  • 必须为 63 个字符或更少(可以为空)
  • 除非标签值为空,必须以字母数字字符([a-z0-9A-Z])开头和结尾
  • 包含破折号(-)、下划线(_)、点(.)和字母或数字

例如,以下是一个清单 (manifest),适用于具有 environment: productionapp: nginx 这两个标签的 Pod:


apiVersion: v1
kind: Pod
metadata:
  name: label-demo
  labels:
    environment: production
    app: nginx
spec:
  containers:
  - name: nginx
    image: nginx:1.14.2
    ports:
    - containerPort: 80

标签选择算符

名称和 UID 不同, 标签不支持唯一性。通常,我们希望许多对象携带相同的标签。

通过标签选择算符,客户端/用户可以识别一组对象。标签选择算符是 Kubernetes 中的核心分组原语。

API 目前支持两种类型的选择算符:基于等值的基于集合的。 标签选择算符可以由逗号分隔的多个需求组成。 在多个需求的情况下,必须满足所有要求,因此逗号分隔符充当逻辑&&)运算符。

空标签选择算符或者未指定的选择算符的语义取决于上下文, 支持使用选择算符的 API 类别应该将算符的合法性和含义用文档记录下来。

基于等值的需求

基于等值基于不等值的需求允许按标签键和值进行过滤。 匹配对象必须满足所有指定的标签约束,尽管它们也可能具有其他标签。 可接受的运算符有 ===!= 三种。 前两个表示相等(并且是同义词),而后者表示不相等。例如:

environment = production
tier != frontend

前者选择所有资源,其键名等于 environment,值等于 production。 后者选择所有资源,其键名等于 tier,值不同于 frontend,所有资源都没有带有 tier 键的标签。 可以使用逗号运算符来过滤 production 环境中的非 frontend 层资源:environment=production,tier!=frontend

基于等值的标签要求的一种使用场景是 Pod 要指定节点选择标准。 例如,下面的示例 Pod 选择带有标签 "accelerator=nvidia-tesla-p100"。

apiVersion: v1
kind: Pod
metadata:
  name: cuda-test
spec:
  containers:
    - name: cuda-test
      image: "registry.k8s.io/cuda-vector-add:v0.1"
      resources:
        limits:
          nvidia.com/gpu: 1
  nodeSelector:
    accelerator: nvidia-tesla-p100

基于集合的需求

基于集合的标签需求允许你通过一组值来过滤键。 支持三种操作符:innotinexists(只可以用在键标识符上)。例如:

environment in (production, qa)
tier notin (frontend, backend)
partition
!partition
  • 第一个示例选择了所有键等于 environment 并且值等于 production 或者 qa 的资源。
  • 第二个示例选择了所有键等于 tier 并且值不等于 frontend 或者 backend 的资源,以及所有没有 tier 键标签的资源。
  • 第三个示例选择了所有包含了有 partition 标签的资源;没有校验它的值。
  • 第四个示例选择了所有没有 partition 标签的资源;没有校验它的值。

类似地,逗号分隔符充当运算符。因此,使用 partition 键(无论为何值)和 environment 不同于 qa 来过滤资源可以使用 partition, environment notin (qa) 来实现。

基于集合的标签选择算符是相等标签选择算符的一般形式,因为 environment=production 等同于 environment in (production)!=notin 也是类似的。

基于集合的要求可以与基于相等的要求混合使用。例如:partition in (customerA, customerB),environment!=qa

API

LIST 和 WATCH 过滤

LIST 和 WATCH 操作可以使用查询参数指定标签选择算符过滤一组对象。 两种需求都是允许的。(这里显示的是它们出现在 URL 查询字符串中)

  • 基于等值的需求:?labelSelector=environment%3Dproduction,tier%3Dfrontend
  • 基于集合的需求:?labelSelector=environment+in+%28production%2Cqa%29%2Ctier+in+%28frontend%29

两种标签选择算符都可以通过 REST 客户端用于 list 或者 watch 资源。 例如,使用 kubectl 定位 apiserver,可以使用基于等值的标签选择算符可以这么写:

kubectl get pods -l environment=production,tier=frontend

或者使用基于集合的需求:

kubectl get pods -l 'environment in (production),tier in (frontend)'

正如刚才提到的,基于集合的需求更具有表达力。例如,它们可以实现值的操作:

kubectl get pods -l 'environment in (production, qa)'

或者通过notin运算符限制不匹配:

kubectl get pods -l 'environment,environment notin (frontend)'

在 API 对象中设置引用

一些 Kubernetes 对象,例如 servicesreplicationcontrollers, 也使用了标签选择算符去指定了其他资源的集合,例如 pods

Service 和 ReplicationController

一个 Service 指向的一组 Pod 是由标签选择算符定义的。同样,一个 ReplicationController 应该管理的 Pod 的数量也是由标签选择算符定义的。

两个对象的标签选择算符都是在 json 或者 yaml 文件中使用映射定义的,并且只支持 基于等值需求的选择算符:

"selector": {
    "component" : "redis",
}

或者

selector:
  component: redis

这个选择算符(分别在 json 或者 yaml 格式中)等价于 component=rediscomponent in (redis)

支持基于集合需求的资源

比较新的资源,例如 JobDeploymentReplicaSetDaemonSet, 也支持基于集合的需求。

selector:
  matchLabels:
    component: redis
  matchExpressions:
    - { key: tier, operator: In, values: [cache] }
    - { key: environment, operator: NotIn, values: [dev] }

matchLabels 是由 {key,value} 对组成的映射。 matchLabels 映射中的单个 {key,value} 等同于 matchExpressions 的元素, 其 key 字段为 "key",operator 为 "In",而 values 数组仅包含 "value"。 matchExpressions 是 Pod 选择算符需求的列表。 有效的运算符包括 InNotInExistsDoesNotExist。 在 InNotIn 的情况下,设置的值必须是非空的。 来自 matchLabelsmatchExpressions 的所有要求都按逻辑与的关系组合到一起 -- 它们必须都满足才能匹配。

选择节点集

通过标签进行选择的一个用例是确定节点集,方便 Pod 调度。 有关更多信息,请参阅选择节点文档。

有效地使用标签

到目前为止我们使用的示例中的资源最多使用了一个标签。 在许多情况下,应使用多个标签来区分不同集合。

例如,不同的应用可能会为 app 标签设置不同的值。 但是,类似 guestbook 示例 这样的多层应用,还需要区分每一层。前端可能会带有以下标签:

labels:
  app: guestbook
  tier: frontend

Redis 的主从节点会有不同的 tier 标签,甚至还有一个额外的 role 标签:

labels:
  app: guestbook
  tier: backend
  role: master

以及

labels:
  app: guestbook
  tier: backend
  role: replica

标签使得我们能够按照所指定的任何维度对我们的资源进行切片和切块:

kubectl apply -f examples/guestbook/all-in-one/guestbook-all-in-one.yaml
kubectl get pods -Lapp -Ltier -Lrole
NAME                           READY  STATUS    RESTARTS   AGE   APP         TIER       ROLE
guestbook-fe-4nlpb             1/1    Running   0          1m    guestbook   frontend   <none>
guestbook-fe-ght6d             1/1    Running   0          1m    guestbook   frontend   <none>
guestbook-fe-jpy62             1/1    Running   0          1m    guestbook   frontend   <none>
guestbook-redis-master-5pg3b   1/1    Running   0          1m    guestbook   backend    master
guestbook-redis-replica-2q2yf  1/1    Running   0          1m    guestbook   backend    replica
guestbook-redis-replica-qgazl  1/1    Running   0          1m    guestbook   backend    replica
my-nginx-divi2                 1/1    Running   0          29m   nginx       <none>     <none>
my-nginx-o0ef1                 1/1    Running   0          29m   nginx       <none>     <none>
kubectl get pods -lapp=guestbook,role=replica
NAME                           READY  STATUS   RESTARTS  AGE
guestbook-redis-replica-2q2yf  1/1    Running  0         3m
guestbook-redis-replica-qgazl  1/1    Running  0         3m

更新标签

有时需要要在创建新资源之前对现有的 Pod 和其它资源重新打标签。 这可以用 kubectl label 完成。 例如,如果想要将所有 NGINX Pod 标记为前端层,运行:

kubectl label pods -l app=nginx tier=fe
pod/my-nginx-2035384211-j5fhi labeled
pod/my-nginx-2035384211-u2c7e labeled
pod/my-nginx-2035384211-u3t6x labeled

首先用标签 "app=nginx" 过滤所有的 Pod,然后用 "tier=fe" 标记它们。 想要查看你刚设置了标签的 Pod,请运行:

kubectl get pods -l app=nginx -L tier
NAME                        READY     STATUS    RESTARTS   AGE       TIER
my-nginx-2035384211-j5fhi   1/1       Running   0          23m       fe
my-nginx-2035384211-u2c7e   1/1       Running   0          23m       fe
my-nginx-2035384211-u3t6x   1/1       Running   0          23m       fe

此命令将输出所有 "app=nginx" 的 Pod,并有一个额外的描述 Pod 所在分层的标签列 (用参数 -L 或者 --label-columns 标明)。

想要了解更多信息,请参考标签kubectl label 命令文档。

接下来

3.1.1.4 - 名字空间

在 Kubernetes 中,名字空间(Namespace) 提供一种机制,将同一集群中的资源划分为相互隔离的组。 同一名字空间内的资源名称要唯一,但跨名字空间时没有这个要求。 名字空间作用域仅针对带有名字空间的对象, (例如 Deployment、Service 等),这种作用域对集群范围的对象 (例如 StorageClass、Node、PersistentVolume 等)不适用。

何时使用多个名字空间

名字空间适用于存在很多跨多个团队或项目的用户的场景。对于只有几到几十个用户的集群,根本不需要创建或考虑名字空间。当需要名字空间提供的功能时,请开始使用它们。

名字空间为名称提供了一个范围。资源的名称需要在名字空间内是唯一的,但不能跨名字空间。 名字空间不能相互嵌套,每个 Kubernetes 资源只能在一个名字空间中。

名字空间是在多个用户之间划分集群资源的一种方法(通过资源配额)。

不必使用多个名字空间来分隔仅仅轻微不同的资源,例如同一软件的不同版本: 应该使用标签来区分同一名字空间中的不同资源。

初始名字空间

Kubernetes 启动时会创建四个初始名字空间:

default
Kubernetes 包含这个名字空间,以便于你无需创建新的名字空间即可开始使用新集群。
kube-node-lease
该名字空间包含用于与各个节点关联的 Lease(租约)对象。 节点租约允许 kubelet 发送心跳, 由此控制面能够检测到节点故障。
kube-public
所有的客户端(包括未经身份验证的客户端)都可以读取该名字空间。 该名字空间主要预留为集群使用,以便某些资源需要在整个集群中可见可读。 该名字空间的公共属性只是一种约定而非要求。
kube-system
该名字空间用于 Kubernetes 系统创建的对象。

使用名字空间

名字空间的创建和删除在名字空间的管理指南文档描述。

查看名字空间

你可以使用以下命令列出集群中现存的名字空间:

kubectl get namespace
NAME              STATUS   AGE
default           Active   1d
kube-node-lease   Active   1d
kube-public       Active   1d
kube-system       Active   1d

为请求设置名字空间

要为当前请求设置名字空间,请使用 --namespace 参数。

例如:

kubectl run nginx --image=nginx --namespace=<名字空间名称>
kubectl get pods --namespace=<名字空间名称>

设置名字空间偏好

你可以永久保存名字空间,以用于对应上下文中所有后续 kubectl 命令。

kubectl config set-context --current --namespace=<名字空间名称>
# 验证
kubectl config view --minify | grep namespace:

名字空间和 DNS

当你创建一个服务时, Kubernetes 会创建一个相应的 DNS 条目

该条目的形式是 <服务名称>.<名字空间名称>.svc.cluster.local,这意味着如果容器只使用 <服务名称>,它将被解析到本地名字空间的服务。这对于跨多个名字空间(如开发、测试和生产) 使用相同的配置非常有用。如果你希望跨名字空间访问,则需要使用完全限定域名(FQDN)。

因此,所有的名字空间名称都必须是合法的 RFC 1123 DNS 标签

并非所有对象都在名字空间中

大多数 kubernetes 资源(例如 Pod、Service、副本控制器等)都位于某些名字空间中。 但是名字空间资源本身并不在名字空间中。而且底层资源, 例如节点持久化卷不属于任何名字空间。

查看哪些 Kubernetes 资源在名字空间中,哪些不在名字空间中:

# 位于名字空间中的资源
kubectl api-resources --namespaced=true

# 不在名字空间中的资源
kubectl api-resources --namespaced=false

自动打标签

特性状态: Kubernetes 1.22 [stable]

Kubernetes 控制面会为所有名字空间设置一个不可变更的标签 kubernetes.io/metadata.name。 标签的值是名字空间的名称。

接下来

3.1.1.5 - 注解

你可以使用 Kubernetes 注解为对象附加任意的非标识的元数据。 客户端程序(例如工具和库)能够获取这些元数据信息。

为对象附加元数据

你可以使用标签或注解将元数据附加到 Kubernetes 对象。 标签可以用来选择对象和查找满足某些条件的对象集合。 相反,注解不用于标识和选择对象。 注解中的元数据,可以很小,也可以很大,可以是结构化的,也可以是非结构化的,能够包含标签不允许的字符。 可以在同一对象的元数据中同时使用标签和注解。

注解和标签一样,是键/值对:

"metadata": {
  "annotations": {
    "key1" : "value1",
    "key2" : "value2"
  }
}

以下是一些例子,用来说明哪些信息可以使用注解来记录:

  • 由声明性配置所管理的字段。 将这些字段附加为注解,能够将它们与客户端或服务端设置的默认值、 自动生成的字段以及通过自动调整大小或自动伸缩系统设置的字段区分开来。
  • 构建、发布或镜像信息(如时间戳、发布 ID、Git 分支、PR 数量、镜像哈希、仓库地址)。
  • 指向日志记录、监控、分析或审计仓库的指针。
  • 可用于调试目的的客户端库或工具信息:例如,名称、版本和构建信息。

  • 用户或者工具/系统的来源信息,例如来自其他生态系统组件的相关对象的 URL。

  • 轻量级上线工具的元数据信息:例如,配置或检查点。

  • 负责人员的电话或呼机号码,或指定在何处可以找到该信息的目录条目,如团队网站。

  • 从用户到最终运行的指令,以修改行为或使用非标准功能。

你可以将这类信息存储在外部数据库或目录中而不使用注解, 但这样做就使得开发人员很难生成用于部署、管理、自检的客户端共享库和工具。

语法和字符集

注解(Annotations) 存储的形式是键/值对。有效的注解键分为两部分: 可选的前缀和名称,以斜杠(/)分隔。 名称段是必需项,并且必须在 63 个字符以内,以字母数字字符([a-z0-9A-Z])开头和结尾, 并允许使用破折号(-),下划线(_),点(.)和字母数字。 前缀是可选的。如果指定,则前缀必须是 DNS 子域:一系列由点(.)分隔的 DNS 标签, 总计不超过 253 个字符,后跟斜杠(/)。 如果省略前缀,则假定注解键对用户是私有的。 由系统组件添加的注解 (例如,kube-schedulerkube-controller-managerkube-apiserverkubectl 或其他第三方组件),必须为终端用户添加注解前缀。

kubernetes.io/k8s.io/ 前缀是为 Kubernetes 核心组件保留的。

例如,下面是一个 Pod 的清单,其注解中包含 imageregistry: https://hub.docker.com/

apiVersion: v1
kind: Pod
metadata:
  name: annotations-demo
  annotations:
    imageregistry: "https://hub.docker.com/"
spec:
  containers:
  - name: nginx
    image: nginx:1.14.2
    ports:
    - containerPort: 80

接下来

3.1.1.6 - 字段选择器

“字段选择器(Field selectors)”允许你根据一个或多个资源字段的值筛选 Kubernetes 对象。 下面是一些使用字段选择器查询的例子:

  • metadata.name=my-service
  • metadata.namespace!=default
  • status.phase=Pending

下面这个 kubectl 命令将筛选出 status.phase 字段值为 Running 的所有 Pod:

kubectl get pods --field-selector status.phase=Running

支持的字段

不同的 Kubernetes 资源类型支持不同的字段选择器。 所有资源类型都支持 metadata.namemetadata.namespace 字段。 使用不被支持的字段选择器会产生错误。例如:

kubectl get ingress --field-selector foo.bar=baz
Error from server (BadRequest): Unable to find "ingresses" that match label selector "", field selector "foo.bar=baz": "foo.bar" is not a known field selector: only "metadata.name", "metadata.namespace"

支持的操作符

你可在字段选择器中使用 ===!==== 的意义是相同的)操作符。 例如,下面这个 kubectl 命令将筛选所有不属于 default 命名空间的 Kubernetes 服务:

kubectl get services  --all-namespaces --field-selector metadata.namespace!=default

链式选择器

标签和其他选择器一样, 字段选择器可以通过使用逗号分隔的列表组成一个选择链。 下面这个 kubectl 命令将筛选 status.phase 字段不等于 Running 同时 spec.restartPolicy 字段等于 Always 的所有 Pod:

kubectl get pods --field-selector=status.phase!=Running,spec.restartPolicy=Always

多种资源类型

你能够跨多种资源类型来使用字段选择器。 下面这个 kubectl 命令将筛选出所有不在 default 命名空间中的 StatefulSet 和 Service:

kubectl get statefulsets,services --all-namespaces --field-selector metadata.namespace!=default

3.1.1.7 - Finalizers

Finalizer 是带有命名空间的键,告诉 Kubernetes 等到特定的条件被满足后, 再完全删除被标记为删除的资源。 Finalizer 提醒控制器清理被删除的对象拥有的资源。

当你告诉 Kubernetes 删除一个指定了 Finalizer 的对象时, Kubernetes API 通过填充 .metadata.deletionTimestamp 来标记要删除的对象, 并返回 202 状态码(HTTP "已接受") 使其进入只读状态。 此时控制平面或其他组件会采取 Finalizer 所定义的行动, 而目标对象仍然处于终止中(Terminating)的状态。 这些行动完成后,控制器会删除目标对象相关的 Finalizer。 当 metadata.finalizers 字段为空时,Kubernetes 认为删除已完成并删除对象。

你可以使用 Finalizer 控制资源的垃圾收集。 例如,你可以定义一个 Finalizer,在删除目标资源前清理相关资源或基础设施。

你可以使用 Finalizers 来控制对象垃圾回收, 方法是在删除目标资源之前提醒控制器执行特定的清理任务。

Finalizers 通常不指定要执行的代码。 相反,它们通常是特定资源上的键的列表,类似于注解。 Kubernetes 自动指定了一些 Finalizers,但你也可以指定你自己的。

Finalizers 如何工作

当你使用清单文件创建资源时,你可以在 metadata.finalizers 字段指定 Finalizers。 当你试图删除该资源时,处理删除请求的 API 服务器会注意到 finalizers 字段中的值, 并进行以下操作:

  • 修改对象,将你开始执行删除的时间添加到 metadata.deletionTimestamp 字段。
  • 禁止对象被删除,直到其 metadata.finalizers 字段内的所有项被删除。
  • 返回 202 状态码(HTTP "Accepted")。

管理 finalizer 的控制器注意到对象上发生的更新操作,对象的 metadata.deletionTimestamp 被设置,意味着已经请求删除该对象。然后,控制器会试图满足资源的 Finalizers 的条件。 每当一个 Finalizer 的条件被满足时,控制器就会从资源的 finalizers 字段中删除该键。 当 finalizers 字段为空时,deletionTimestamp 字段被设置的对象会被自动删除。 你也可以使用 Finalizers 来阻止删除未被管理的资源。

一个常见的 Finalizer 的例子是 kubernetes.io/pv-protection, 它用来防止意外删除 PersistentVolume 对象。 当一个 PersistentVolume 对象被 Pod 使用时, Kubernetes 会添加 pv-protection Finalizer。 如果你试图删除 PersistentVolume,它将进入 Terminating 状态, 但是控制器因为该 Finalizer 存在而无法删除该资源。 当 Pod 停止使用 PersistentVolume 时, Kubernetes 清除 pv-protection Finalizer,控制器就会删除该卷。

属主引用、标签和 Finalizers

标签类似, 属主引用 描述了 Kubernetes 中对象之间的关系,但它们作用不同。 当一个控制器 管理类似于 Pod 的对象时,它使用标签来跟踪相关对象组的变化。 例如,当 Job 创建一个或多个 Pod 时, Job 控制器会给这些 Pod 应用上标签,并跟踪集群中的具有相同标签的 Pod 的变化。

Job 控制器还为这些 Pod 添加了“属主引用”,指向创建 Pod 的 Job。 如果你在这些 Pod 运行的时候删除了 Job, Kubernetes 会使用属主引用(而不是标签)来确定集群中哪些 Pod 需要清理。

当 Kubernetes 识别到要删除的资源上的属主引用时,它也会处理 Finalizers。

在某些情况下,Finalizers 会阻止依赖对象的删除, 这可能导致目标属主对象被保留的时间比预期的长,而没有被完全删除。 在这些情况下,你应该检查目标属主和附属对象上的 Finalizers 和属主引用,来排查原因。

接下来

3.1.1.8 - 属主与附属

在 Kubernetes 中,一些对象是其他对象的“属主(Owner)”。 例如,ReplicaSet 是一组 Pod 的属主。 具有属主的对象是属主的“附属(Dependent)”。

属主关系不同于一些资源使用的标签和选择算符机制。 例如,有一个创建 EndpointSlice 对象的 Service, 该 Service 使用标签来让控制平面确定哪些 EndpointSlice 对象属于该 Service。除开标签,每个代表 Service 所管理的 EndpointSlice 都有一个属主引用。属主引用避免 Kubernetes 的不同部分干扰到不受它们控制的对象。

对象规约中的属主引用

附属对象有一个 metadata.ownerReferences 字段,用于引用其属主对象。一个有效的属主引用, 包含与附属对象同在一个命名空间下的对象名称和一个 UID。 Kubernetes 自动为一些对象的附属资源设置属主引用的值, 这些对象包含 ReplicaSet、DaemonSet、Deployment、Job、CronJob、ReplicationController 等。 你也可以通过改变这个字段的值,来手动配置这些关系。 然而,通常不需要这么做,你可以让 Kubernetes 自动管理附属关系。

附属对象还有一个 ownerReferences.blockOwnerDeletion 字段,该字段使用布尔值, 用于控制特定的附属对象是否可以阻止垃圾收集删除其属主对象。 如果控制器(例如 Deployment 控制器) 设置了 metadata.ownerReferences 字段的值,Kubernetes 会自动设置 blockOwnerDeletion 的值为 true。 你也可以手动设置 blockOwnerDeletion 字段的值,以控制哪些附属对象会阻止垃圾收集。

Kubernetes 准入控制器根据属主的删除权限控制用户访问,以便为附属资源更改此字段。 这种控制机制可防止未经授权的用户延迟属主对象的删除。

属主关系与 Finalizer

当你告诉 Kubernetes 删除一个资源,API 服务器允许管理控制器处理该资源的任何 Finalizer 规则Finalizer 防止意外删除你的集群所依赖的、用于正常运作的资源。 例如,如果你试图删除一个仍被 Pod 使用的 PersistentVolume,该资源不会被立即删除, 因为 PersistentVolumekubernetes.io/pv-protection Finalizer。 相反,数据卷将进入 Terminating 状态, 直到 Kubernetes 清除这个 Finalizer,而这种情况只会发生在 PersistentVolume 不再被挂载到 Pod 上时。

当你使用前台或孤立级联删除时, Kubernetes 也会向属主资源添加 Finalizer。 在前台删除中,会添加 foreground Finalizer,这样控制器必须在删除了拥有 ownerReferences.blockOwnerDeletion=true 的附属资源后,才能删除属主对象。 如果你指定了孤立删除策略,Kubernetes 会添加 orphan Finalizer, 这样控制器在删除属主对象后,会忽略附属资源。

接下来

3.1.1.9 - 推荐使用的标签

除了 kubectl 和 dashboard 之外,你还可以使用其他工具来可视化和管理 Kubernetes 对象。 一组通用的标签可以让多个工具之间相互操作,用所有工具都能理解的通用方式描述对象。

除了支持工具外,推荐的标签还以一种可以查询的方式描述了应用程序。

元数据围绕 应用(application) 的概念进行组织。Kubernetes 不是平台即服务(PaaS),没有或强制执行正式的应用程序概念。 相反,应用程序是非正式的,并使用元数据进行描述。应用程序包含的定义是松散的。

共享标签和注解都使用同一个前缀:app.kubernetes.io。没有前缀的标签是用户私有的。 共享前缀可以确保共享标签不会干扰用户自定义的标签。

标签

为了充分利用这些标签,应该在每个资源对象上都使用它们。

描述示例类型
app.kubernetes.io/name应用程序的名称mysql字符串
app.kubernetes.io/instance用于唯一确定应用实例的名称mysql-abcxzy字符串
app.kubernetes.io/version应用程序的当前版本(例如语义版本 1.0、修订版哈希等)5.7.21字符串
app.kubernetes.io/component架构中的组件database字符串
app.kubernetes.io/part-of此级别的更高级别应用程序的名称wordpress字符串
app.kubernetes.io/managed-by用于管理应用程序的工具helm字符串

为说明这些标签的实际使用情况,请看下面的 StatefulSet 对象:

# 这是一段节选
apiVersion: apps/v1
kind: StatefulSet
metadata:
  labels:
    app.kubernetes.io/name: mysql
    app.kubernetes.io/instance: mysql-abcxzy
    app.kubernetes.io/version: "5.7.21"
    app.kubernetes.io/component: database
    app.kubernetes.io/part-of: wordpress
    app.kubernetes.io/managed-by: helm

应用和应用实例

应用可以在 Kubernetes 集群中安装一次或多次。在某些情况下,可以安装在同一命名空间中。 例如,可以不止一次地为不同的站点安装不同的 WordPress。

应用的名称和实例的名称是分别记录的。例如,WordPress 应用的 app.kubernetes.io/namewordpress,而其实例名称 app.kubernetes.io/instancewordpress-abcxzy。 这使得应用和应用的实例均可被识别,应用的每个实例都必须具有唯一的名称。

示例

为了说明使用这些标签的不同方式,以下示例具有不同的复杂性。

一个简单的无状态服务

考虑使用 DeploymentService 对象部署的简单无状态服务的情况。 以下两个代码段表示如何以最简单的形式使用标签。

下面的 Deployment 用于监督运行应用本身的那些 Pod。

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app.kubernetes.io/name: myservice
    app.kubernetes.io/instance: myservice-abcxzy
...

下面的 Service 用于暴露应用。

apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/name: myservice
    app.kubernetes.io/instance: myservice-abcxzy
...

带有一个数据库的 Web 应用程序

考虑一个稍微复杂的应用:一个使用 Helm 安装的 Web 应用(WordPress), 其中使用了数据库(MySQL)。以下代码片段说明用于部署此应用程序的对象的开始。

以下 Deployment 的开头用于 WordPress:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app.kubernetes.io/name: wordpress
    app.kubernetes.io/instance: wordpress-abcxzy
    app.kubernetes.io/version: "4.9.4"
    app.kubernetes.io/managed-by: helm
    app.kubernetes.io/component: server
    app.kubernetes.io/part-of: wordpress
...

这个 Service 用于暴露 WordPress:

apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/name: wordpress
    app.kubernetes.io/instance: wordpress-abcxzy
    app.kubernetes.io/version: "4.9.4"
    app.kubernetes.io/managed-by: helm
    app.kubernetes.io/component: server
    app.kubernetes.io/part-of: wordpress
...

MySQL 作为一个 StatefulSet 暴露,包含它和它所属的较大应用程序的元数据:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  labels:
    app.kubernetes.io/name: mysql
    app.kubernetes.io/instance: mysql-abcxzy
    app.kubernetes.io/version: "5.7.21"
    app.kubernetes.io/managed-by: helm
    app.kubernetes.io/component: database
    app.kubernetes.io/part-of: wordpress
...

Service 用于将 MySQL 作为 WordPress 的一部分暴露:

apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/name: mysql
    app.kubernetes.io/instance: mysql-abcxzy
    app.kubernetes.io/version: "5.7.21"
    app.kubernetes.io/managed-by: helm
    app.kubernetes.io/component: database
    app.kubernetes.io/part-of: wordpress
...

使用 MySQL StatefulSetService,你会注意到有关 MySQL 和 WordPress 的信息,包括更广泛的应用程序。

3.1.2 - Kubernetes 组件

Kubernetes 集群由控制平面的组件和一组称为节点的机器组成。

当你部署完 Kubernetes,便拥有了一个完整的集群。

一组工作机器,称为节点, 会运行容器化应用程序。每个集群至少有一个工作节点。

工作节点会托管 Pod,而 Pod 就是作为应用负载的组件。 控制平面管理集群中的工作节点和 Pod。 在生产环境中,控制平面通常跨多台计算机运行, 一个集群通常运行多个节点,提供容错性和高可用性。

本文档概述了一个正常运行的 Kubernetes 集群所需的各种组件。

Kubernetes 的组件

Kubernetes 集群的组件

控制平面组件(Control Plane Components)

控制平面组件会为集群做出全局决策,比如资源的调度。 以及检测和响应集群事件,例如当不满足部署的 replicas 字段时,要启动新的 Pod)。

控制平面组件可以在集群中的任何节点上运行。 然而,为了简单起见,设置脚本通常会在同一个计算机上启动所有控制平面组件, 并且不会在此计算机上运行用户容器。 请参阅使用 kubeadm 构建高可用性集群 中关于跨多机器控制平面设置的示例。

kube-apiserver

API 服务器是 Kubernetes 控制平面的组件, 该组件负责公开了 Kubernetes API,负责处理接受请求的工作。 API 服务器是 Kubernetes 控制平面的前端。

Kubernetes API 服务器的主要实现是 kube-apiserverkube-apiserver 设计上考虑了水平扩缩,也就是说,它可通过部署多个实例来进行扩缩。 你可以运行 kube-apiserver 的多个实例,并在这些实例之间平衡流量。

etcd

一致且高可用的键值存储,用作 Kubernetes 所有集群数据的后台数据库。

如果你的 Kubernetes 集群使用 etcd 作为其后台数据库, 请确保你针对这些数据有一份 备份计划。

你可以在官方文档中找到有关 etcd 的深入知识。

kube-scheduler

kube-scheduler控制平面的组件, 负责监视新创建的、未指定运行节点(node)Pods, 并选择节点来让 Pod 在上面运行。

调度决策考虑的因素包括单个 Pod 及 Pods 集合的资源需求、软硬件及策略约束、 亲和性及反亲和性规范、数据位置、工作负载间的干扰及最后时限。

kube-controller-manager

kube-controller-manager控制平面的组件, 负责运行控制器进程。

从逻辑上讲, 每个控制器都是一个单独的进程, 但是为了降低复杂性,它们都被编译到同一个可执行文件,并在同一个进程中运行。

有许多不同类型的控制器。以下是一些例子:

  • 节点控制器(Node Controller):负责在节点出现故障时进行通知和响应
  • 任务控制器(Job Controller):监测代表一次性任务的 Job 对象,然后创建 Pod 来运行这些任务直至完成
  • 端点分片控制器(EndpointSlice controller):填充端点分片(EndpointSlice)对象(以提供 Service 和 Pod 之间的链接)。
  • 服务账号控制器(ServiceAccount controller):为新的命名空间创建默认的服务账号(ServiceAccount)。

以上并不是一个详尽的列表。

cloud-controller-manager

一个 Kubernetes 控制平面组件, 嵌入了特定于云平台的控制逻辑。 云控制器管理器(Cloud Controller Manager) 允许将你的集群连接到云提供商的 API 之上, 并将与该云平台交互的组件同与你的集群交互的组件分离开来。

cloud-controller-manager 仅运行特定于云平台的控制器。 因此如果你在自己的环境中运行 Kubernetes,或者在本地计算机中运行学习环境, 所部署的集群不需要有云控制器管理器。

kube-controller-manager 类似,cloud-controller-manager 将若干逻辑上独立的控制回路组合到同一个可执行文件中, 供你以同一进程的方式运行。 你可以对其执行水平扩容(运行不止一个副本)以提升性能或者增强容错能力。

下面的控制器都包含对云平台驱动的依赖:

  • 节点控制器(Node Controller):用于在节点终止响应后检查云提供商以确定节点是否已被删除
  • 路由控制器(Route Controller):用于在底层云基础架构中设置路由
  • 服务控制器(Service Controller):用于创建、更新和删除云提供商负载均衡器

Node 组件

节点组件会在每个节点上运行,负责维护运行的 Pod 并提供 Kubernetes 运行环境。

kubelet

kubelet 会在集群中每个节点(node)上运行。 它保证容器(containers)都运行在 Pod 中。

kubelet 接收一组通过各类机制提供给它的 PodSpec,确保这些 PodSpec 中描述的容器处于运行状态且健康。 kubelet 不会管理不是由 Kubernetes 创建的容器。

kube-proxy

kube-proxy 是集群中每个节点(node)上所运行的网络代理, 实现 Kubernetes 服务(Service) 概念的一部分。

kube-proxy 维护节点上的一些网络规则, 这些网络规则会允许从集群内部或外部的网络会话与 Pod 进行网络通信。

如果操作系统提供了可用的数据包过滤层,则 kube-proxy 会通过它来实现网络规则。 否则,kube-proxy 仅做流量转发。

容器运行时(Container Runtime)

这个基础组件使 Kubernetes 能够有效运行容器。 它负责管理 Kubernetes 环境中容器的执行和生命周期。

Kubernetes 支持许多容器运行环境,例如 containerdCRI-O 以及 Kubernetes CRI (容器运行环境接口) 的其他任何实现。

插件(Addons)

插件使用 Kubernetes 资源(DaemonSetDeployment 等)实现集群功能。 因为这些插件提供集群级别的功能,插件中命名空间域的资源属于 kube-system 命名空间。

下面描述众多插件中的几种。有关可用插件的完整列表,请参见 插件(Addons)

DNS

尽管其他插件都并非严格意义上的必需组件,但几乎所有 Kubernetes 集群都应该有集群 DNS, 因为很多示例都需要 DNS 服务。

集群 DNS 是一个 DNS 服务器,和环境中的其他 DNS 服务器一起工作,它为 Kubernetes 服务提供 DNS 记录。

Kubernetes 启动的容器自动将此 DNS 服务器包含在其 DNS 搜索列表中。

Web 界面(仪表盘)

Dashboard 是 Kubernetes 集群的通用的、基于 Web 的用户界面。 它使用户可以管理集群中运行的应用程序以及集群本身, 并进行故障排除。

容器资源监控

容器资源监控 将关于容器的一些常见的时间序列度量值保存到一个集中的数据库中, 并提供浏览这些数据的界面。

集群层面日志

集群层面日志机制负责将容器的日志数据保存到一个集中的日志存储中, 这种集中日志存储提供搜索和浏览接口。

网络插件

网络插件 是实现容器网络接口(CNI)规范的软件组件。它们负责为 Pod 分配 IP 地址,并使这些 Pod 能在集群内部相互通信。

接下来

进一步了解以下内容:

3.1.3 - Kubernetes API

Kubernetes API 使你可以查询和操纵 Kubernetes 中对象的状态。 Kubernetes 控制平面的核心是 API 服务器和它暴露的 HTTP API。 用户、集群的不同部分以及外部组件都通过 API 服务器相互通信。

Kubernetes 控制面的核心是 API 服务器。 API 服务器负责提供 HTTP API,以供用户、集群中的不同部分和集群外部组件相互通信。

Kubernetes API 使你可以在 Kubernetes 中查询和操纵 API 对象 (例如 Pod、Namespace、ConfigMap 和 Event)的状态。

大部分操作都可以通过 kubectl 命令行接口或类似 kubeadm 这类命令行工具来执行, 这些工具在背后也是调用 API。不过,你也可以使用 REST 调用来访问这些 API。 Kubernetes 为那些希望使用 Kubernetes API 编写应用的开发者提供一组客户端库

每个 Kubernetes 集群都会发布集群所使用的 API 规范。 Kubernetes 使用两种机制来发布这些 API 规范;这两种机制都有助于实现自动互操作。 例如,kubectl 工具获取并缓存 API 规范,以实现命令行补全和其他特性。所支持的两种机制如下:

  • 发现 API 提供有关 Kubernetes API 的信息:API 名称、资源、版本和支持的操作。 此 API 是特定于 Kubernetes 的一个术语,因为它是一个独立于 Kubernetes OpenAPI 的 API。 其目的是为可用的资源提供简要总结,不详细说明资源的具体模式。有关资源模式的参考,请参阅 OpenAPI 文档。
  • Kubernetes OpenAPI 文档为所有 Kubernetes API 端点提供(完整的) OpenAPI v2.0 和 v3.0 模式。OpenAPI v3 是访问 OpenAPI 的首选方法, 因为它提供了更全面和准确的 API 视图。其中包括所有可用的 API 路径,以及每个端点上每个操作所接收和生成的所有资源。 它还包括集群支持的所有可扩展组件。这些数据是完整的规范,比 Discovery API 提供的规范要大得多。

Discovery API

Kubernetes 通过 Discovery API 发布集群所支持的所有组版本和资源列表。对于每个资源,包括以下内容:

  • 名称
  • 集群作用域还是名字空间作用域
  • 端点 URL 和所支持的动词
  • 别名
  • 组、版本、类别

API 以聚合和非聚合形式提供。聚合的发现提供两个端点,而非聚合的发现为每个组版本提供单独的端点。

聚合的发现

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

Kubernetes 为聚合的发现提供了 Beta 支持,通过两个端点(/api/apis)发布集群所支持的所有资源。 请求这个端点会大大减少从集群获取发现数据时发送的请求数量。你可以通过带有 Accept 头(Accept: application/json;v=v2beta1;g=apidiscovery.k8s.io;as=APIGroupDiscoveryList) 的请求发送到不同端点,来指明聚合发现的资源。

如果没有使用 Accept 头指示资源类型,对于 /api/apis 端点的默认响应将是一个非聚合的发现文档。

内置资源的发现文档可以在 Kubernetes GitHub 代码仓库中找到。如果手头没有 Kubernetes 集群可供查询, 此 Github 文档可用作可用资源的基础集合的参考。端点还支持 ETag 和 protobuf 编码。

非聚合的发现

在不使用聚合发现的情况下,发现 API 以不同级别发布,同时根端点为下游文档发布发现信息。

集群支持的所有组版本列表发布在 /api/apis 端点。例如:

{
  "kind": "APIGroupList",
  "apiVersion": "v1",
  "groups": [
    {
      "name": "apiregistration.k8s.io",
      "versions": [
        {
          "groupVersion": "apiregistration.k8s.io/v1",
          "version": "v1"
        }
      ],
      "preferredVersion": {
        "groupVersion": "apiregistration.k8s.io/v1",
        "version": "v1"
      }
    },
    {
      "name": "apps",
      "versions": [
        {
          "groupVersion": "apps/v1",
          "version": "v1"
        }
      ],
      "preferredVersion": {
        "groupVersion": "apps/v1",
        "version": "v1"
      }
    },
    ...
}

用户需要发出额外的请求才能在 /apis/<group>/<version>(例如 /apis/rbac.authorization.k8s.io/v1alpha1) 获取每个组版本的发现文档。这些发现文档会公布在特定组版本下所提供的资源列表。 kubectl 使用这些端点来获取某集群所支持的资源列表。

OpenAPI 接口定义

有关 OpenAPI 规范的细节,参阅 OpenAPI 文档

Kubernetes 同时提供 OpenAPI v2.0 和 OpenAPI v3.0。OpenAPI v3 是访问 OpenAPI 的首选方法, 因为它提供了对 Kubernetes 资源更全面(无损)的表示。由于 OpenAPI v2 的限制, 所公布的 OpenAPI 中会丢弃掉一些字段,包括但不限于 defaultnullableoneOf

OpenAPI v2

Kubernetes API 服务器通过 /openapi/v2 端点提供聚合的 OpenAPI v2 规范。 你可以按照下表所给的请求头部,指定响应的格式:

OpenAPI v2 查询请求的合法头部值
头部可选值说明
Accept-Encodinggzip不指定此头部也是可以的
Acceptapplication/com.github.proto-openapi.spec.v2@v1.0+protobuf主要用于集群内部
application/json默认值
*提供application/json

OpenAPI v3

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

Kubernetes 支持将其 API 的描述以 OpenAPI v3 形式发布。

发现端点 /openapi/v3 被提供用来查看可用的所有组、版本列表。 此列表仅返回 JSON。这些组、版本以下面的格式提供:

{
    "paths": {
        ...,
        "api/v1": {
            "serverRelativeURL": "/openapi/v3/api/v1?hash=CC0E9BFD992D8C59AEC98A1E2336F899E8318D3CF4C68944C3DEC640AF5AB52D864AC50DAA8D145B3494F75FA3CFF939FCBDDA431DAD3CA79738B297795818CF"
        },
        "apis/admissionregistration.k8s.io/v1": {
            "serverRelativeURL": "/openapi/v3/apis/admissionregistration.k8s.io/v1?hash=E19CC93A116982CE5422FC42B590A8AFAD92CDE9AE4D59B5CAAD568F083AD07946E6CB5817531680BCE6E215C16973CD39003B0425F3477CFD854E89A9DB6597"
        },
        ....
    }
}

为了改进客户端缓存,相对的 URL 会指向不可变的 OpenAPI 描述。 为了此目的,API 服务器也会设置正确的 HTTP 缓存标头 (Expires 为未来 1 年,和 Cache-Controlimmutable)。 当一个过时的 URL 被使用时,API 服务器会返回一个指向最新 URL 的重定向。

Kubernetes API 服务器会在端点 /openapi/v3/apis/<group>/<version>?hash=<hash> 发布一个 Kubernetes 组版本的 OpenAPI v3 规范。

请参阅下表了解可接受的请求头部。

OpenAPI v3 查询的合法请求头部值
头部可选值说明
Accept-Encodinggzip不提供此头部也是可接受的
Acceptapplication/com.github.proto-openapi.spec.v3@v1.0+protobuf主要用于集群内部使用
application/json默认
* application/json 形式返回

k8s.io/client-go/openapi3 包中提供了获取 OpenAPI v3 的 Golang 实现。

Kubernetes 1.30 发布了 OpenAPI v2.0 和 v3.0; 近期没有支持 v3.1 的计划。

Protobuf 序列化

Kubernetes 为 API 实现了一种基于 Protobuf 的序列化格式,主要用于集群内部通信。 关于此格式的详细信息,可参考 Kubernetes Protobuf 序列化设计提案。 每种模式对应的接口描述语言(IDL)位于定义 API 对象的 Go 包中。

持久化

Kubernetes 通过将序列化状态的对象写入到 etcd 中完成存储操作。

API 发现

集群支持的所有组版本列表被发布在 /api/apis 端点。 每个组版本还会通过 /apis/<group>/<version> (例如 /apis/rbac.authorization.k8s.io/v1alpha1)广播支持的资源列表。 这些端点由 kubectl 用于获取集群支持的资源列表。

聚合发现

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

Kubernetes 对聚合发现提供 Beta 支持,通过两个端点(/api/apis) 发布集群支持的所有资源,而不是每个组版本都需要一个端点。 请求此端点显著减少了获取平均 Kubernetes 集群发现而发送的请求数量。 通过请求各自的端点并附带表明聚合发现资源 Accept: application/json;v=v2beta1;g=apidiscovery.k8s.io;as=APIGroupDiscoveryList 的 Accept 头部来进行访问。

该端点还支持 ETag 和 protobuf 编码。

API 组和版本控制

为了更容易消除字段或重组资源的呈现方式,Kubernetes 支持多个 API 版本,每个版本位于不同的 API 路径, 例如 /api/v1/apis/rbac.authorization.k8s.io/v1alpha1

版本控制是在 API 级别而不是在资源或字段级别完成的,以确保 API 呈现出清晰、一致的系统资源和行为视图, 并能够控制对生命结束和/或实验性 API 的访问。

为了更容易演进和扩展其 API,Kubernetes 实现了 API 组, 这些 API 组可以被启用或禁用

API 资源通过其 API 组、资源类型、名字空间(用于名字空间作用域的资源)和名称来区分。 API 服务器透明地处理 API 版本之间的转换:所有不同的版本实际上都是相同持久化数据的呈现。 API 服务器可以通过多个 API 版本提供相同的底层数据。

例如,假设针对相同的资源有两个 API 版本:v1v1beta1。 如果你最初使用其 API 的 v1beta1 版本创建了一个对象, 你稍后可以使用 v1beta1v1 API 版本来读取、更新或删除该对象, 直到 v1beta1 版本被废弃和移除为止。此后,你可以使用 v1 API 继续访问和修改该对象。

API 变更

任何成功的系统都要随着新的使用案例的出现和现有案例的变化来成长和变化。 为此,Kubernetes 已设计了 Kubernetes API 来持续变更和成长。 Kubernetes 项目的目标是 不要 给现有客户端带来兼容性问题,并在一定的时期内维持这种兼容性, 以便其他项目有机会作出适应性变更。

一般而言,新的 API 资源和新的资源字段可以被频繁地添加进来。 删除资源或者字段则要遵从 API 废弃策略

Kubernetes 对维护达到正式发布(GA)阶段的官方 API 的兼容性有着很强的承诺,通常这一 API 版本为 v1。 此外,Kubernetes 保持与 Kubernetes 官方 API 的 Beta API 版本持久化数据的兼容性, 并确保在该功能特性已进入稳定期时数据可以通过 GA API 版本进行转换和访问。

如果你采用一个 Beta API 版本,一旦该 API 进阶,你将需要转换到后续的 Beta 或稳定的 API 版本。 执行此操作的最佳时间是 Beta API 处于弃用期,因为此时可以通过两个 API 版本同时访问那些对象。 一旦 Beta API 结束其弃用期并且不再提供服务,则必须使用替换的 API 版本。

关于 API 版本分级的定义细节,请参阅 API 版本参考页面。

API 扩展

有两种途径来扩展 Kubernetes API:

  1. 你可以使用自定义资源来以声明式方式定义 API 服务器如何提供你所选择的资源 API。
  2. 你也可以选择实现自己的聚合层来扩展 Kubernetes API。

接下来

3.2 - Kubernetes 架构

Kubernetes 背后的架构概念。
Kubernetes 组件

Kubernetes 集群架构

3.2.1 - 节点

Kubernetes 通过将容器放入在节点(Node)上运行的 Pod 中来执行你的工作负载。 节点可以是一个虚拟机或者物理机器,取决于所在的集群配置。 每个节点包含运行 Pod 所需的服务; 这些节点由控制面负责管理。

通常集群中会有若干个节点;而在一个学习所用或者资源受限的环境中,你的集群中也可能只有一个节点。

节点上的组件包括 kubelet容器运行时以及 kube-proxy

管理

API 服务器添加节点的方式主要有两种:

  1. 节点上的 kubelet 向控制面执行自注册;
  2. 你(或者别的什么人)手动添加一个 Node 对象。

在你创建了 Node 对象或者节点上的 kubelet 执行了自注册操作之后,控制面会检查新的 Node 对象是否合法。 例如,如果你尝试使用下面的 JSON 对象来创建 Node 对象:

{
  "kind": "Node",
  "apiVersion": "v1",
  "metadata": {
    "name": "10.240.79.157",
    "labels": {
      "name": "my-first-k8s-node"
    }
  }
}

Kubernetes 会在内部创建一个 Node 对象作为节点的表示。Kubernetes 检查 kubelet 向 API 服务器注册节点时使用的 metadata.name 字段是否匹配。 如果节点是健康的(即所有必要的服务都在运行中),则该节点可以用来运行 Pod。 否则,直到该节点变为健康之前,所有的集群活动都会忽略该节点。

Node 对象的名称必须是合法的 DNS 子域名

节点名称唯一性

节点的名称用来标识 Node 对象。 没有两个 Node 可以同时使用相同的名称。 Kubernetes 还假定名字相同的资源是同一个对象。 就 Node 而言,隐式假定使用相同名称的实例会具有相同的状态(例如网络配置、根磁盘内容) 和类似节点标签这类属性。这可能在节点被更改但其名称未变时导致系统状态不一致。 如果某个 Node 需要被替换或者大量变更,需要从 API 服务器移除现有的 Node 对象, 之后再在更新之后重新将其加入。

节点自注册

当 kubelet 标志 --register-node 为 true(默认)时,它会尝试向 API 服务注册自己。 这是首选模式,被绝大多数发行版选用。

对于自注册模式,kubelet 使用下列参数启动:

  • --kubeconfig - 用于向 API 服务器执行身份认证所用的凭据的路径。
  • --cloud-provider - 与某云驱动 进行通信以读取与自身相关的元数据的方式。
  • --register-node - 自动向 API 服务器注册。
  • --register-with-taints - 使用所给的污点列表 (逗号分隔的 <key>=<value>:<effect>)注册节点。当 register-node 为 false 时无效。
  • --node-ip - 可选的以英文逗号隔开的节点 IP 地址列表。你只能为每个地址簇指定一个地址。 例如在单协议栈 IPv4 集群中,需要将此值设置为 kubelet 应使用的节点 IPv4 地址。 参阅配置 IPv4/IPv6 双协议栈了解运行双协议栈集群的详情。

    如果你未提供这个参数,kubelet 将使用节点默认的 IPv4 地址(如果有); 如果节点没有 IPv4 地址,则 kubelet 使用节点的默认 IPv6 地址。

  • --node-labels - 在集群中注册节点时要添加的标签。 (参见 NodeRestriction 准入控制插件所实施的标签限制)。
  • --node-status-update-frequency - 指定 kubelet 向 API 服务器发送其节点状态的频率。

Node 鉴权模式NodeRestriction 准入插件被启用后, 仅授权 kubelet 创建/修改自己的 Node 资源。

手动节点管理

你可以使用 kubectl 来创建和修改 Node 对象。

如果你希望手动创建节点对象时,请设置 kubelet 标志 --register-node=false

你可以修改 Node 对象(忽略 --register-node 设置)。 例如,你可以修改节点上的标签或并标记其为不可调度。

你可以结合使用 Node 上的标签和 Pod 上的选择算符来控制调度。 例如,你可以限制某 Pod 只能在符合要求的节点子集上运行。

如果标记节点为不可调度(unschedulable),将阻止新 Pod 调度到该 Node 之上, 但不会影响任何已经在其上的 Pod。 这是重启节点或者执行其他维护操作之前的一个有用的准备步骤。

要标记一个 Node 为不可调度,执行以下命令:

kubectl cordon $NODENAME

更多细节参考安全地腾空节点

节点状态

一个节点的状态包含以下信息:

你可以使用 kubectl 来查看节点状态和其他细节信息:

kubectl describe node <节点名称>

更多细节参见 Node Status

节点心跳

Kubernetes 节点发送的心跳帮助你的集群确定每个节点的可用性,并在检测到故障时采取行动。

对于节点,有两种形式的心跳:

节点控制器

节点控制器是 Kubernetes 控制面组件, 管理节点的方方面面。

节点控制器在节点的生命周期中扮演多个角色。 第一个是当节点注册时为它分配一个 CIDR 区段(如果启用了 CIDR 分配)。

第二个是保持节点控制器内的节点列表与云服务商所提供的可用机器列表同步。 如果在云环境下运行,只要某节点不健康,节点控制器就会询问云服务是否节点的虚拟机仍可用。 如果不可用,节点控制器会将该节点从它的节点列表删除。

第三个是监控节点的健康状况。节点控制器负责:

  • 在节点不可达的情况下,在 Node 的 .status 中更新 Ready 状况。 在这种情况下,节点控制器将 NodeReady 状况更新为 Unknown
  • 如果节点仍然无法访问:对于不可达节点上的所有 Pod 触发 API 发起的逐出操作。 默认情况下,节点控制器在将节点标记为 Unknown 后等待 5 分钟提交第一个驱逐请求。

默认情况下,节点控制器每 5 秒检查一次节点状态,可以使用 kube-controller-manager 组件上的 --node-monitor-period 参数来配置周期。

逐出速率限制

大部分情况下,节点控制器把逐出速率限制在每秒 --node-eviction-rate 个(默认为 0.1)。 这表示它每 10 秒钟内至多从一个节点驱逐 Pod。

当一个可用区域(Availability Zone)中的节点变为不健康时,节点的驱逐行为将发生改变。 节点控制器会同时检查可用区域中不健康(Ready 状况为 UnknownFalse) 的节点的百分比:

  • 如果不健康节点的比例超过 --unhealthy-zone-threshold(默认为 0.55), 驱逐速率将会降低。
  • 如果集群较小(意即小于等于 --large-cluster-size-threshold 个节点 - 默认为 50), 驱逐操作将会停止。
  • 否则驱逐速率将降为每秒 --secondary-node-eviction-rate 个(默认为 0.01)。

在逐个可用区域中实施这些策略的原因是, 当一个可用区域可能从控制面脱离时其它可用区域可能仍然保持连接。 如果你的集群没有跨越云服务商的多个可用区域,那(整个集群)就只有一个可用区域。

跨多个可用区域部署你的节点的一个关键原因是当某个可用区域整体出现故障时, 工作负载可以转移到健康的可用区域。 因此,如果一个可用区域中的所有节点都不健康时,节点控制器会以正常的速率 --node-eviction-rate 进行驱逐操作。 在所有的可用区域都不健康(也即集群中没有健康节点)的极端情况下, 节点控制器将假设控制面与节点间的连接出了某些问题,它将停止所有驱逐动作 (如果故障后部分节点重新连接,节点控制器会从剩下不健康或者不可达节点中驱逐 Pod)。

节点控制器还负责驱逐运行在拥有 NoExecute 污点的节点上的 Pod, 除非这些 Pod 能够容忍此污点。 节点控制器还负责根据节点故障(例如节点不可访问或没有就绪) 为其添加污点。 这意味着调度器不会将 Pod 调度到不健康的节点上。

资源容量跟踪

Node 对象会跟踪节点上资源的容量(例如可用内存和 CPU 数量)。 通过自注册机制生成的 Node 对象会在注册期间报告自身容量。 如果你手动添加了 Node, 你就需要在添加节点时手动设置节点容量。

Kubernetes 调度器 保证节点上有足够的资源供其上的所有 Pod 使用。 它会检查节点上所有容器的请求的总和不会超过节点的容量。 总的请求包括由 kubelet 启动的所有容器,但不包括由容器运行时直接启动的容器, 也不包括不受 kubelet 控制的其他进程。

节点拓扑

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

如果启用了 TopologyManager 特性门控kubelet 可以在作出资源分配决策时使用拓扑提示。 参考控制节点上拓扑管理策略了解详细信息。

节点体面关闭

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

kubelet 会尝试检测节点系统关闭事件并终止在节点上运行的所有 Pod。

在节点终止期间,kubelet 保证 Pod 遵从常规的 Pod 终止流程, 且不接受新的 Pod(即使这些 Pod 已经绑定到该节点)。

节点体面关闭特性依赖于 systemd,因为它要利用 systemd 抑制器锁机制, 在给定的期限内延迟节点关闭。

节点体面关闭特性受 GracefulNodeShutdown 特性门控控制, 在 1.21 版本中是默认启用的。

注意,默认情况下,下面描述的两个配置选项,shutdownGracePeriodshutdownGracePeriodCriticalPods 都是被设置为 0 的,因此不会激活节点体面关闭功能。 要激活此功能特性,这两个 kubelet 配置选项要适当配置,并设置为非零值。

一旦 systemd 检测到或通知节点关闭,kubelet 就会在节点上设置一个 NotReady 状况,并将 reason 设置为 "node is shutting down"。 kube-scheduler 会重视此状况,不将 Pod 调度到受影响的节点上; 其他第三方调度程序也应当遵循相同的逻辑。这意味着新的 Pod 不会被调度到该节点上, 因此不会有新 Pod 启动。

如果检测到节点关闭过程正在进行中,kubelet 也会PodAdmission 阶段拒绝 Pod,即使是该 Pod 带有 node.kubernetes.io/not-ready:NoSchedule容忍度

同时,当 kubelet 通过 API 在其 Node 上设置该状况时,kubelet 也开始终止在本地运行的所有 Pod。

在体面关闭节点过程中,kubelet 分两个阶段来终止 Pod:

  1. 终止在节点上运行的常规 Pod。
  2. 终止在节点上运行的关键 Pod

节点体面关闭的特性对应两个 KubeletConfiguration 选项:

  • shutdownGracePeriod
    • 指定节点应延迟关闭的总持续时间。此时间是 Pod 体面终止的时间总和,不区分常规 Pod 还是关键 Pod
  • shutdownGracePeriodCriticalPods
    • 在节点关闭期间指定用于终止关键 Pod 的持续时间。该值应小于 shutdownGracePeriod

例如,如果设置了 shutdownGracePeriod=30sshutdownGracePeriodCriticalPods=10s, 则 kubelet 将延迟 30 秒关闭节点。 在关闭期间,将保留前 20(30 - 10)秒用于体面终止常规 Pod, 而保留最后 10 秒用于终止关键 Pod

基于 Pod 优先级的节点体面关闭

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

为了在节点体面关闭期间提供更多的灵活性,尤其是处理关闭期间的 Pod 排序问题, 节点体面关闭机制能够关注 Pod 的 PriorityClass 设置,前提是你已经在集群中启用了此功能特性。 此功能特性允许集群管理员基于 Pod 的优先级类(Priority Class) 显式地定义节点体面关闭期间 Pod 的处理顺序。

前文所述的节点体面关闭特性能够分两个阶段关闭 Pod, 首先关闭的是非关键的 Pod,之后再处理关键 Pod。 如果需要显式地以更细粒度定义关闭期间 Pod 的处理顺序,需要一定的灵活度, 这时可以使用基于 Pod 优先级的体面关闭机制。

当节点体面关闭能够处理 Pod 优先级时,节点体面关闭的处理可以分为多个阶段, 每个阶段关闭特定优先级类的 Pod。kubelet 可以被配置为按确切的阶段处理 Pod, 且每个阶段可以独立设置关闭时间。

假设集群中存在以下自定义的 Pod 优先级类

Pod 优先级类名称Pod 优先级类数值
custom-class-a100000
custom-class-b10000
custom-class-c1000
regular/unset0

kubelet 配置中, shutdownGracePeriodByPodPriority 可能看起来是这样:

Pod 优先级类数值关闭期限
10000010 秒
10000180 秒
1000120 秒
060 秒

对应的 kubelet 配置 YAML 将会是:

shutdownGracePeriodByPodPriority:
  - priority: 100000
    shutdownGracePeriodSeconds: 10
  - priority: 10000
    shutdownGracePeriodSeconds: 180
  - priority: 1000
    shutdownGracePeriodSeconds: 120
  - priority: 0
    shutdownGracePeriodSeconds: 60

上面的表格表明,所有 priority 值大于等于 100000 的 Pod 会得到 10 秒钟期限停止, 所有 priority 值介于 10000 和 100000 之间的 Pod 会得到 180 秒钟期限停止, 所有 priority 值介于 1000 和 10000 之间的 Pod 会得到 120 秒钟期限停止, 所有其他 Pod 将获得 60 秒的时间停止。

用户不需要为所有的优先级类都设置数值。例如,你也可以使用下面这种配置:

Pod 优先级类数值关闭期限
100000300 秒
1000120 秒
060 秒

在上面这个场景中,优先级类为 custom-class-b 的 Pod 会与优先级类为 custom-class-c 的 Pod 在关闭时按相同期限处理。

如果在特定的范围内不存在 Pod,则 kubelet 不会等待对应优先级范围的 Pod。 kubelet 会直接跳到下一个优先级数值范围进行处理。

如果此功能特性被启用,但没有提供配置数据,则不会出现排序操作。

使用此功能特性需要启用 GracefulNodeShutdownBasedOnPodPriority 特性门控, 并将 kubelet 配置 中的 shutdownGracePeriodByPodPriority 设置为期望的配置, 其中包含 Pod 的优先级类数值以及对应的关闭期限。

kubelet 子系统中会生成 graceful_shutdown_start_time_secondsgraceful_shutdown_end_time_seconds 度量指标以便监视节点关闭行为。

处理节点非体面关闭

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

节点关闭的操作可能无法被 kubelet 的节点关闭管理器检测到, 是因为该命令不会触发 kubelet 所使用的抑制锁定机制,或者是因为用户错误的原因, 即 ShutdownGracePeriod 和 ShutdownGracePeriodCriticalPod 配置不正确。 请参考以上节点体面关闭部分了解更多详细信息。

当某节点关闭但 kubelet 的节点关闭管理器未检测到这一事件时, 在那个已关闭节点上、属于 StatefulSet 的 Pod 将停滞于终止状态,并且不能移动到新的运行节点上。 这是因为已关闭节点上的 kubelet 已不存在,亦无法删除 Pod, 因此 StatefulSet 无法创建同名的新 Pod。 如果 Pod 使用了卷,则 VolumeAttachments 不会从原来的已关闭节点上删除, 因此这些 Pod 所使用的卷也无法挂接到新的运行节点上。 所以,那些以 StatefulSet 形式运行的应用无法正常工作。 如果原来的已关闭节点被恢复,kubelet 将删除 Pod,新的 Pod 将被在不同的运行节点上创建。 如果原来的已关闭节点没有被恢复,那些在已关闭节点上的 Pod 将永远滞留在终止状态。

为了缓解上述情况,用户可以手动将具有 NoExecuteNoSchedule 效果的 node.kubernetes.io/out-of-service 污点添加到节点上,标记其无法提供服务。 如果在 kube-controller-manager 上启用了 NodeOutOfServiceVolumeDetach 特性门控, 并且节点被通过污点标记为无法提供服务,如果节点 Pod 上没有设置对应的容忍度, 那么这样的 Pod 将被强制删除,并且该在节点上被终止的 Pod 将立即进行卷分离操作。 这样就允许那些在无法提供服务节点上的 Pod 能在其他节点上快速恢复。

在非体面关闭期间,Pod 分两个阶段终止:

  1. 强制删除没有匹配的 out-of-service 容忍度的 Pod。
  2. 立即对此类 Pod 执行分离卷操作。

存储超时强制解除挂接

当任何 Pod 未能在 6 分钟内被成功删除时,如果节点当时不健康, Kubernetes 将强制解除挂接正在被卸载的卷。 在启用了强制解除挂接卷的节点上仍在运行的所有工作负载都将导致违反 CSI 规范, 该规范指出 ControllerUnpublishVolume "必须在调用卷上的所有 NodeUnstageVolumeNodeUnpublishVolume 执行且成功后被调用"。在这种情况下,相关节点上的卷可能会遇到数据损坏。

强制存储解除挂接行为是可选的;用户可以选择使用"非体面节点关闭"特性。

可以通过在 kube-controller-manager 中设置 disable-force-detach-on-timeout 配置字段来禁用超时时存储强制解除挂接。 禁用超时强制解除挂接特性意味着托管在不正常运行时间超过 6 分钟的节点上的卷将不会删除其关联的 VolumeAttachment

应用此设置后,仍关联到某卷的不健康 Pod 必须通过上述非体面节点关闭过程进行恢复。

交换内存(swap)管理

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

要在节点上启用交换内存,必须启用 kubelet 的 NodeSwap 特性门控(默认启用), 同时使用 --fail-swap-on 命令行参数或者将 failSwapOn 配置设置为 false。 为了允许 Pod 使用交换内存,在 kubelet 配置中不应将 swapBehavior 设置为 NoSwap(默认行为)。

用户还可以选择配置 memorySwap.swapBehavior 以指定节点使用交换内存的方式。例如:

memorySwap:
  swapBehavior: LimitedSwap
  • NoSwap(默认):Kubernetes 工作负载不会使用交换内存。
  • LimitedSwap:Kubernetes 工作负载对交换内存的使用受到限制。 只有具有 Burstable QoS 的 Pod 可以使用交换内存。

如果启用了特性门控但是未指定 memorySwap 的配置,默认情况下 kubelet 将使用与 NoSwap 设置相同的行为。

采用 LimitedSwap 时,不属于 Burstable QoS 分类的 Pod (即 BestEffort/Guaranteed QoS Pod) 被禁止使用交换内存。为了保持上述的安全性和节点健康性保证, 在 LimitedSwap 生效时,不允许这些 Pod 使用交换内存。

在详细介绍交换限制的计算之前,有必要定义以下术语:

  • nodeTotalMemory:节点上可用的物理内存总量。
  • totalPodsSwapAvailable:节点上可供 Pod 使用的交换内存总量 (一些交换内存可能被保留由系统使用)。
  • containerMemoryRequest:容器的内存请求。

交换内存限制被配置为 (containerMemoryRequest / nodeTotalMemory) * totalPodsSwapAvailable 的值。

需要注意的是,位于 Burstable QoS Pod 中的容器可以通过将内存请求设置为与内存限制相同来选择不使用交换内存。 以这种方式配置的容器将无法访问交换内存。

只有 Cgroup v2 支持交换内存,Cgroup v1 不支持。

如需了解更多信息、协助测试和提交反馈,请参阅关于 Kubernetes 1.28:NodeSwap 进阶至 Beta1 的博客文章、 KEP-2400 及其设计提案

接下来

进一步了解以下资料:

3.2.2 - 节点与控制面之间的通信

本文列举控制面节点(确切地说是 API 服务器)和 Kubernetes 集群之间的通信路径。 目的是为了让用户能够自定义他们的安装,以实现对网络配置的加固, 使得集群能够在不可信的网络上(或者在一个云服务商完全公开的 IP 上)运行。

节点到控制面

Kubernetes 采用的是中心辐射型(Hub-and-Spoke)API 模式。 所有从节点(或运行于其上的 Pod)发出的 API 调用都终止于 API 服务器。 其它控制面组件都没有被设计为可暴露远程服务。 API 服务器被配置为在一个安全的 HTTPS 端口(通常为 443)上监听远程连接请求, 并启用一种或多种形式的客户端身份认证机制。 一种或多种客户端鉴权机制应该被启用, 特别是在允许使用匿名请求服务账户令牌的时候。

应该使用集群的公共根证书开通节点, 这样它们就能够基于有效的客户端凭据安全地连接 API 服务器。 一种好的方法是以客户端证书的形式将客户端凭据提供给 kubelet。 请查看 kubelet TLS 启动引导 以了解如何自动提供 kubelet 客户端证书。

想要连接到 API 服务器的 Pod 可以使用服务账号安全地进行连接。 当 Pod 被实例化时,Kubernetes 自动把公共根证书和一个有效的持有者令牌注入到 Pod 里。 kubernetes 服务(位于 default 名字空间中)配置了一个虚拟 IP 地址, 用于(通过 kube-proxy)转发请求到 API 服务器的 HTTPS 末端。

控制面组件也通过安全端口与集群的 API 服务器通信。

这样,从集群节点和节点上运行的 Pod 到控制面的连接的缺省操作模式即是安全的, 能够在不可信的网络或公网上运行。

控制面到节点

从控制面(API 服务器)到节点有两种主要的通信路径。 第一种是从 API 服务器到集群中每个节点上运行的 kubelet 进程。 第二种是从 API 服务器通过它的代理功能连接到任何节点、Pod 或者服务。

API 服务器到 kubelet

从 API 服务器到 kubelet 的连接用于:

  • 获取 Pod 日志。
  • 挂接(通过 kubectl)到运行中的 Pod。
  • 提供 kubelet 的端口转发功能。

这些连接终止于 kubelet 的 HTTPS 末端。 默认情况下,API 服务器不检查 kubelet 的服务证书。这使得此类连接容易受到中间人攻击, 在非受信网络或公开网络上运行也是 不安全的

为了对这个连接进行认证,使用 --kubelet-certificate-authority 标志给 API 服务器提供一个根证书包,用于 kubelet 的服务证书。

如果无法实现这点,又要求避免在非受信网络或公共网络上进行连接,可在 API 服务器和 kubelet 之间使用 SSH 隧道

最后,应该启用 Kubelet 认证/鉴权 来保护 kubelet API。

API 服务器到节点、Pod 和服务

从 API 服务器到节点、Pod 或服务的连接默认为纯 HTTP 方式,因此既没有认证,也没有加密。 这些连接可通过给 API URL 中的节点、Pod 或服务名称添加前缀 https: 来运行在安全的 HTTPS 连接上。 不过这些连接既不会验证 HTTPS 末端提供的证书,也不会提供客户端证书。 因此,虽然连接是加密的,仍无法提供任何完整性保证。 这些连接 目前还不能安全地 在非受信网络或公共网络上运行。

SSH 隧道

Kubernetes 支持使用 SSH 隧道来保护从控制面到节点的通信路径。 在这种配置下,API 服务器建立一个到集群中各节点的 SSH 隧道(连接到在 22 端口监听的 SSH 服务器) 并通过这个隧道传输所有到 kubelet、节点、Pod 或服务的请求。 这一隧道保证通信不会被暴露到集群节点所运行的网络之外。

Konnectivity 服务

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

作为 SSH 隧道的替代方案,Konnectivity 服务提供 TCP 层的代理,以便支持从控制面到集群的通信。 Konnectivity 服务包含两个部分:Konnectivity 服务器和 Konnectivity 代理, 分别运行在控制面网络和节点网络中。 Konnectivity 代理建立并维持到 Konnectivity 服务器的网络连接。 启用 Konnectivity 服务之后,所有控制面到节点的通信都通过这些连接传输。

请浏览 Konnectivity 服务任务 在你的集群中配置 Konnectivity 服务。

接下来

3.2.3 - 控制器

在机器人技术和自动化领域,控制回路(Control Loop)是一个非终止回路,用于调节系统状态。

这是一个控制环的例子:房间里的温度自动调节器。

当你设置了温度,告诉了温度自动调节器你的期望状态(Desired State)。 房间的实际温度是当前状态(Current State)。 通过对设备的开关控制,温度自动调节器让其当前状态接近期望状态。

在 Kubernetes 中,控制器通过监控集群 的公共状态,并致力于将当前状态转变为期望的状态。

控制器模式

一个控制器至少追踪一种类型的 Kubernetes 资源。这些 对象 有一个代表期望状态的 spec 字段。 该资源的控制器负责确保其当前状态接近期望状态。

控制器可能会自行执行操作;在 Kubernetes 中更常见的是一个控制器会发送信息给 API 服务器,这会有副作用。 具体可参看后文的例子。

通过 API 服务器来控制

Job 控制器是一个 Kubernetes 内置控制器的例子。 内置控制器通过和集群 API 服务器交互来管理状态。

Job 是一种 Kubernetes 资源,它运行一个或者多个 Pod, 来执行一个任务然后停止。 (一旦被调度了,对 kubelet 来说 Pod 对象就会变成期望状态的一部分)。

在集群中,当 Job 控制器拿到新任务时,它会保证一组 Node 节点上的 kubelet 可以运行正确数量的 Pod 来完成工作。 Job 控制器不会自己运行任何的 Pod 或者容器。Job 控制器是通知 API 服务器来创建或者移除 Pod。 控制面中的其它组件 根据新的消息作出反应(调度并运行新 Pod)并且最终完成工作。

创建新 Job 后,所期望的状态就是完成这个 Job。Job 控制器会让 Job 的当前状态不断接近期望状态:创建为 Job 要完成工作所需要的 Pod,使 Job 的状态接近完成。

控制器也会更新配置对象。例如:一旦 Job 的工作完成了,Job 控制器会更新 Job 对象的状态为 Finished

(这有点像温度自动调节器关闭了一个灯,以此来告诉你房间的温度现在到你设定的值了)。

直接控制

相比 Job 控制器,有些控制器需要对集群外的一些东西进行修改。

例如,如果你使用一个控制回路来保证集群中有足够的 节点,那么控制器就需要当前集群外的 一些服务在需要时创建新节点。

和外部状态交互的控制器从 API 服务器获取到它想要的状态,然后直接和外部系统进行通信 并使当前状态更接近期望状态。

(实际上有一个控制器 可以水平地扩展集群中的节点。)

这里的重点是,控制器做出了一些变更以使得事物更接近你的期望状态, 之后将当前状态报告给集群的 API 服务器。 其他控制回路可以观测到所汇报的数据的这种变化并采取其各自的行动。

在温度计的例子中,如果房间很冷,那么某个控制器可能还会启动一个防冻加热器。 就 Kubernetes 集群而言,控制面间接地与 IP 地址管理工具、存储服务、云驱动 APIs 以及其他服务协作,通过扩展 Kubernetes 来实现这点。

期望状态与当前状态

Kubernetes 采用了系统的云原生视图,并且可以处理持续的变化。

在任务执行时,集群随时都可能被修改,并且控制回路会自动修复故障。 这意味着很可能集群永远不会达到稳定状态。

只要集群中的控制器在运行并且进行有效的修改,整体状态的稳定与否是无关紧要的。

设计

作为设计原则之一,Kubernetes 使用了很多控制器,每个控制器管理集群状态的一个特定方面。 最常见的一个特定的控制器使用一种类型的资源作为它的期望状态, 控制器管理控制另外一种类型的资源向它的期望状态演化。 例如,Job 的控制器跟踪 Job 对象(以发现新的任务)和 Pod 对象(以运行 Job,然后查看任务何时完成)。 在这种情况下,新任务会创建 Job,而 Job 控制器会创建 Pod。

使用简单的控制器而不是一组相互连接的单体控制回路是很有用的。 控制器会失败,所以 Kubernetes 的设计正是考虑到了这一点。

运行控制器的方式

Kubernetes 内置一组控制器,运行在 kube-controller-manager 内。 这些内置的控制器提供了重要的核心功能。

Deployment 控制器和 Job 控制器是 Kubernetes 内置控制器的典型例子。 Kubernetes 允许你运行一个稳定的控制平面,这样即使某些内置控制器失败了, 控制平面的其他部分会接替它们的工作。

你会遇到某些控制器运行在控制面之外,用以扩展 Kubernetes。 或者,如果你愿意,你也可以自己编写新控制器。 你可以以一组 Pod 来运行你的控制器,或者运行在 Kubernetes 之外。 最合适的方案取决于控制器所要执行的功能是什么。

接下来

3.2.4 - 租约(Lease)

分布式系统通常需要租约(Lease);租约提供了一种机制来锁定共享资源并协调集合成员之间的活动。 在 Kubernetes 中,租约概念表示为 coordination.k8s.io API 组中的 Lease 对象, 常用于类似节点心跳和组件级领导者选举等系统核心能力。

节点心跳

Kubernetes 使用 Lease API 将 kubelet 节点心跳传递到 Kubernetes API 服务器。 对于每个 Node,在 kube-node-lease 名字空间中都有一个具有匹配名称的 Lease 对象。 在此基础上,每个 kubelet 心跳都是对该 Lease 对象的 update 请求,更新该 Lease 的 spec.renewTime 字段。 Kubernetes 控制平面使用此字段的时间戳来确定此 Node 的可用性。

更多细节请参阅 Node Lease 对象

领导者选举

Kubernetes 也使用 Lease 确保在任何给定时间某个组件只有一个实例在运行。 这在高可用配置中由 kube-controller-managerkube-scheduler 等控制平面组件进行使用, 这些组件只应有一个实例激活运行,而其他实例待机。

API 服务器身份

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

从 Kubernetes v1.26 开始,每个 kube-apiserver 都使用 Lease API 将其身份发布到系统中的其他位置。 虽然它本身并不是特别有用,但为客户端提供了一种机制来发现有多少个 kube-apiserver 实例正在操作 Kubernetes 控制平面。kube-apiserver 租约的存在使得未来可以在各个 kube-apiserver 之间协调新的能力。

你可以检查 kube-system 名字空间中名为 kube-apiserver-<sha256-hash> 的 Lease 对象来查看每个 kube-apiserver 拥有的租约。你还可以使用标签选择算符 apiserver.kubernetes.io/identity=kube-apiserver

kubectl -n kube-system get lease -l apiserver.kubernetes.io/identity=kube-apiserver
NAME                                        HOLDER                                                                           AGE
apiserver-07a5ea9b9b072c4a5f3d1c3702        apiserver-07a5ea9b9b072c4a5f3d1c3702_0c8914f7-0f35-440e-8676-7844977d3a05        5m33s
apiserver-7be9e061c59d368b3ddaf1376e        apiserver-7be9e061c59d368b3ddaf1376e_84f2a85d-37c1-4b14-b6b9-603e62e4896f        4m23s
apiserver-1dfef752bcb36637d2763d1868        apiserver-1dfef752bcb36637d2763d1868_c5ffa286-8a9a-45d4-91e7-61118ed58d2e        4m43s

租约名称中使用的 SHA256 哈希基于 API 服务器所看到的操作系统主机名生成。 每个 kube-apiserver 都应该被配置为使用集群中唯一的主机名。 使用相同主机名的 kube-apiserver 新实例将使用新的持有者身份接管现有 Lease,而不是实例化新的 Lease 对象。 你可以通过检查 kubernetes.io/hostname 标签的值来查看 kube-apisever 所使用的主机名:

kubectl -n kube-system get lease apiserver-07a5ea9b9b072c4a5f3d1c3702 -o yaml
apiVersion: coordination.k8s.io/v1
kind: Lease
metadata:
  creationTimestamp: "2023-07-02T13:16:48Z"
  labels:
    apiserver.kubernetes.io/identity: kube-apiserver
    kubernetes.io/hostname: master-1
  name: apiserver-07a5ea9b9b072c4a5f3d1c3702
  namespace: kube-system
  resourceVersion: "334899"
  uid: 90870ab5-1ba9-4523-b215-e4d4e662acb1
spec:
  holderIdentity: apiserver-07a5ea9b9b072c4a5f3d1c3702_0c8914f7-0f35-440e-8676-7844977d3a05
  leaseDurationSeconds: 3600
  renewTime: "2023-07-04T21:58:48.065888Z"

kube-apiserver 中不再存续的已到期租约将在到期 1 小时后被新的 kube-apiserver 作为垃圾收集。

你可以通过禁用 APIServerIdentity 特性门控来禁用 API 服务器身份租约。

工作负载

你自己的工作负载可以定义自己使用的 Lease。例如, 你可以运行自定义的控制器, 让主要成员或领导者成员在其中执行其对等方未执行的操作。 你定义一个 Lease,以便控制器副本可以使用 Kubernetes API 进行协调以选择或选举一个领导者。 如果你使用 Lease,良好的做法是为明显关联到产品或组件的 Lease 定义一个名称。 例如,如果你有一个名为 Example Foo 的组件,可以使用名为 example-foo 的 Lease。

如果集群操作员或其他终端用户可以部署一个组件的多个实例, 则选择名称前缀并挑选一种机制(例如 Deployment 名称的哈希)以避免 Lease 的名称冲突。

你可以使用另一种方式来达到相同的效果:不同的软件产品不相互冲突。

3.2.5 - 云控制器管理器

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

使用云基础设施技术,你可以在公有云、私有云或者混合云环境中运行 Kubernetes。 Kubernetes 的信条是基于自动化的、API 驱动的基础设施,同时避免组件间紧密耦合。

组件 cloud-controller-manager 是指云控制器管理器, 一个 Kubernetes 控制平面组件, 嵌入了特定于云平台的控制逻辑。 云控制器管理器(Cloud Controller Manager) 允许将你的集群连接到云提供商的 API 之上, 并将与该云平台交互的组件同与你的集群交互的组件分离开来。

通过分离 Kubernetes 和底层云基础设置之间的互操作性逻辑, cloud-controller-manager 组件使云提供商能够以不同于 Kubernetes 主项目的步调发布新特征。

cloud-controller-manager 组件是基于一种插件机制来构造的, 这种机制使得不同的云厂商都能将其平台与 Kubernetes 集成。

设计

此图展示了 Kubernetes 集群的组件

云控制器管理器以一组多副本的进程集合的形式运行在控制面中,通常表现为 Pod 中的容器。每个 cloud-controller-manager 在同一进程中实现多个控制器

云控制器管理器的功能

云控制器管理器中的控制器包括:

节点控制器

节点控制器负责在云基础设施中创建了新服务器时为之更新节点(Node)对象。 节点控制器从云提供商获取当前租户中主机的信息。节点控制器执行以下功能:

  1. 使用从云平台 API 获取的对应服务器的唯一标识符更新 Node 对象;
  2. 利用特定云平台的信息为 Node 对象添加注解和标签,例如节点所在的区域 (Region)和所具有的资源(CPU、内存等等);
  3. 获取节点的网络地址和主机名;
  4. 检查节点的健康状况。如果节点无响应,控制器通过云平台 API 查看该节点是否已从云中禁用、删除或终止。如果节点已从云中删除, 则控制器从 Kubernetes 集群中删除 Node 对象。

某些云驱动实现中,这些任务被划分到一个节点控制器和一个节点生命周期控制器中。

路由控制器

Route 控制器负责适当地配置云平台中的路由,以便 Kubernetes 集群中不同节点上的容器之间可以相互通信。

取决于云驱动本身,路由控制器可能也会为 Pod 网络分配 IP 地址块。

服务控制器

服务(Service)与受控的负载均衡器、 IP 地址、网络包过滤、目标健康检查等云基础设施组件集成。 服务控制器与云驱动的 API 交互,以配置负载均衡器和其他基础设施组件。 你所创建的 Service 资源会需要这些组件服务。

鉴权

本节分别讲述云控制器管理器为了完成自身工作而产生的对各类 API 对象的访问需求。

节点控制器

节点控制器只操作 Node 对象。它需要读取和修改 Node 对象的完全访问权限。

v1/Node

  • get
  • list
  • create
  • update
  • patch
  • watch
  • delete

路由控制器

路由控制器会监听 Node 对象的创建事件,并据此配置路由设施。 它需要读取 Node 对象的 Get 权限。

v1/Node

  • get

服务控制器

服务控制器监测 Service 对象的 createupdatedelete 事件, 并配置对应服务的 Endpoints 对象 (对于 EndpointSlices,kube-controller-manager 按需对其进行管理)。

为了访问 Service 对象,它需要 listwatch 访问权限。 为了更新 Service 对象,它需要 patchupdate 访问权限。

为了能够配置 Service 对应的 Endpoints 资源, 它需要 createlistgetwatchupdate 等访问权限。

v1/Service

  • list
  • get
  • watch
  • patch
  • update

其他

在云控制器管理器的实现中,其核心部分需要创建 Event 对象的访问权限, 并创建 ServiceAccount 资源以保证操作安全性的权限。

v1/Event:

  • create
  • patch
  • update

v1/ServiceAccount:

  • create

用于云控制器管理器 RBAC 的 ClusterRole 如下例所示:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: cloud-controller-manager
rules:
- apiGroups:
  - ""
  resources:
  - events
  verbs:
  - create
  - patch
  - update
- apiGroups:
  - ""
  resources:
  - nodes
  verbs:
  - '*'
- apiGroups:
  - ""
  resources:
  - nodes/status
  verbs:
  - patch
- apiGroups:
  - ""
  resources:
  - services
  verbs:
  - list
  - patch
  - update
  - watch
- apiGroups:
  - ""
  resources:
  - serviceaccounts
  verbs:
  - create
- apiGroups:
  - ""
  resources:
  - persistentvolumes
  verbs:
  - get
  - list
  - update
  - watch
- apiGroups:
  - ""
  resources:
  - endpoints
  verbs:
  - create
  - get
  - list
  - watch
  - update

接下来

  • 云控制器管理器的管理 给出了运行和管理云控制器管理器的指南。

  • 要升级 HA 控制平面以使用云控制器管理器, 请参见将复制的控制平面迁移以使用云控制器管理器

  • 想要了解如何实现自己的云控制器管理器,或者对现有项目进行扩展么?

    • 云控制器管理器使用 Go 语言的接口(具体指在 kubernetes/cloud-provider 项目中 cloud.go 文件中所定义的 CloudProvider 接口),从而使得针对各种云平台的具体实现都可以接入。
    • 本文中列举的共享控制器(节点控制器、路由控制器和服务控制器等)的实现以及其他一些生成具有 CloudProvider 接口的框架的代码,都是 Kubernetes 的核心代码。 特定于云驱动的实现虽不是 Kubernetes 核心成分,仍要实现 CloudProvider 接口。

3.2.6 - 关于 cgroup v2

在 Linux 上,控制组约束分配给进程的资源。

kubelet 和底层容器运行时都需要对接 cgroup 来强制执行为 Pod 和容器管理资源, 这包括为容器化工作负载配置 CPU/内存请求和限制。

Linux 中有两个 cgroup 版本:cgroup v1 和 cgroup v2。cgroup v2 是新一代的 cgroup API。

什么是 cgroup v2?

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

cgroup v2 是 Linux cgroup API 的下一个版本。cgroup v2 提供了一个具有增强资源管理能力的统一控制系统。

cgroup v2 对 cgroup v1 进行了多项改进,例如:

  • API 中单个统一的层次结构设计
  • 更安全的子树委派给容器
  • 更新的功能特性, 例如压力阻塞信息(Pressure Stall Information,PSI)
  • 跨多个资源的增强资源分配管理和隔离
    • 统一核算不同类型的内存分配(网络内存、内核内存等)
    • 考虑非即时资源变化,例如页面缓存回写

一些 Kubernetes 特性专门使用 cgroup v2 来增强资源管理和隔离。 例如,MemoryQoS 特性改进了内存 QoS 并依赖于 cgroup v2 原语。

使用 cgroup v2

使用 cgroup v2 的推荐方法是使用一个默认启用 cgroup v2 的 Linux 发行版。

要检查你的发行版是否使用 cgroup v2,请参阅识别 Linux 节点上的 cgroup 版本

要求

cgroup v2 具有以下要求:

  • 操作系统发行版启用 cgroup v2
  • Linux 内核为 5.8 或更高版本
  • 容器运行时支持 cgroup v2。例如:
  • kubelet 和容器运行时被配置为使用 systemd cgroup 驱动

Linux 发行版 cgroup v2 支持

有关使用 cgroup v2 的 Linux 发行版的列表, 请参阅 cgroup v2 文档

  • Container-Optimized OS(从 M97 开始)
  • Ubuntu(从 21.10 开始,推荐 22.04+)
  • Debian GNU/Linux(从 Debian 11 Bullseye 开始)
  • Fedora(从 31 开始)
  • Arch Linux(从 2021 年 4 月开始)
  • RHEL 和类似 RHEL 的发行版(从 9 开始)

要检查你的发行版是否使用 cgroup v2, 请参阅你的发行版文档或遵循识别 Linux 节点上的 cgroup 版本中的指示说明。

你还可以通过修改内核 cmdline 引导参数在你的 Linux 发行版上手动启用 cgroup v2。 如果你的发行版使用 GRUB,则应在 /etc/default/grub 下的 GRUB_CMDLINE_LINUX 中添加 systemd.unified_cgroup_hierarchy=1, 然后执行 sudo update-grub。不过,推荐的方法仍是使用一个默认已启用 cgroup v2 的发行版。

迁移到 cgroup v2

要迁移到 cgroup v2,需确保满足要求,然后升级到一个默认启用 cgroup v2 的内核版本。

kubelet 能够自动检测操作系统是否运行在 cgroup v2 上并相应调整其操作,无需额外配置。

切换到 cgroup v2 时,用户体验应没有任何明显差异,除非用户直接在节点上或从容器内访问 cgroup 文件系统。

cgroup v2 使用一个与 cgroup v1 不同的 API,因此如果有任何应用直接访问 cgroup 文件系统, 则需要将这些应用更新为支持 cgroup v2 的版本。例如:

  • 一些第三方监控和安全代理可能依赖于 cgroup 文件系统。你要将这些代理更新到支持 cgroup v2 的版本。
  • 如果以独立的 DaemonSet 的形式运行 cAdvisor 以监控 Pod 和容器, 需将其更新到 v0.43.0 或更高版本。
  • 如果你部署 Java 应用程序,最好使用完全支持 cgroup v2 的版本:
  • 如果你正在使用 uber-go/automaxprocs 包, 确保你使用的版本是 v1.5.1 或者更高。

识别 Linux 节点上的 cgroup 版本

cgroup 版本取决于正在使用的 Linux 发行版和操作系统上配置的默认 cgroup 版本。 要检查你的发行版使用的是哪个 cgroup 版本,请在该节点上运行 stat -fc %T /sys/fs/cgroup/ 命令:

stat -fc %T /sys/fs/cgroup/

对于 cgroup v2,输出为 cgroup2fs

对于 cgroup v1,输出为 tmpfs

接下来

3.2.7 - 容器运行时接口(CRI)

CRI 是一个插件接口,它使 kubelet 能够使用各种容器运行时,无需重新编译集群组件。

你需要在集群中的每个节点上都有一个可以正常工作的容器运行时, 这样 kubelet 能启动 Pod 及其容器。

容器运行时接口(CRI)是 kubelet 和容器运行时之间通信的主要协议。

Kubernetes 容器运行时接口(Container Runtime Interface;CRI)定义了主要 gRPC 协议, 用于节点组件 kubelet容器运行时之间的通信。

API

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

当通过 gRPC 连接到容器运行时,kubelet 将充当客户端。运行时和镜像服务端点必须在容器运行时中可用, 可以使用 --image-service-endpoint 命令行标志在 kubelet 中单独配置。

对 Kubernetes v1.30,kubelet 偏向于使用 CRI v1 版本。 如果容器运行时不支持 CRI 的 v1 版本,那么 kubelet 会尝试协商较老的、仍被支持的所有版本。 v1.30 版本的 kubelet 也可协商 CRI v1alpha2 版本,但该版本被视为已弃用。 如果 kubelet 无法协商出可支持的 CRI 版本,则 kubelet 放弃并且不会注册为节点。

升级

升级 Kubernetes 时,kubelet 会尝试在组件重启时自动选择最新的 CRI 版本。 如果失败,则将如上所述进行回退。如果由于容器运行时已升级而需要 gRPC 重拨, 则容器运行时还必须支持最初选择的版本,否则重拨预计会失败。 这需要重新启动 kubelet。

接下来

3.2.8 - 垃圾收集

垃圾收集(Garbage Collection)是 Kubernetes 用于清理集群资源的各种机制的统称。 垃圾收集允许系统清理如下资源:

属主与依赖

Kubernetes 中很多对象通过属主引用 链接到彼此。属主引用(Owner Reference)可以告诉控制面哪些对象依赖于其他对象。 Kubernetes 使用属主引用来为控制面以及其他 API 客户端在删除某对象时提供一个清理关联资源的机会。 在大多数场合,Kubernetes 都是自动管理属主引用的。

属主关系与某些资源所使用的标签和选择算符不同。 例如,考虑一个创建 EndpointSlice 对象的 Service。 Service 使用标签来允许控制面确定哪些 EndpointSlice 对象被该 Service 使用。 除了标签,每个被 Service 托管的 EndpointSlice 对象还有一个属主引用属性。 属主引用可以帮助 Kubernetes 中的不同组件避免干预并非由它们控制的对象。

级联删除

Kubernetes 会检查并删除那些不再拥有属主引用的对象,例如在你删除了 ReplicaSet 之后留下来的 Pod。当你删除某个对象时,你可以控制 Kubernetes 是否去自动删除该对象的依赖对象, 这个过程称为级联删除(Cascading Deletion)。 级联删除有两种类型,分别如下:

  • 前台级联删除
  • 后台级联删除

你也可以使用 Kubernetes Finalizers 来控制垃圾收集机制如何以及何时删除包含属主引用的资源。

前台级联删除

在前台级联删除中,正在被你删除的属主对象首先进入 deletion in progress 状态。 在这种状态下,针对属主对象会发生以下事情:

  • Kubernetes API 服务器将某对象的 metadata.deletionTimestamp 字段设置为该对象被标记为要删除的时间点。
  • Kubernetes API 服务器也会将 metadata.finalizers 字段设置为 foregroundDeletion
  • 在删除过程完成之前,通过 Kubernetes API 仍然可以看到该对象。

当属主对象进入删除过程中状态后,控制器删除其依赖对象。控制器在删除完所有依赖对象之后, 删除属主对象。这时,通过 Kubernetes API 就无法再看到该对象。

在前台级联删除过程中,唯一可能阻止属主对象被删除的是那些带有 ownerReference.blockOwnerDeletion=true 字段的依赖对象。 参阅使用前台级联删除 以了解进一步的细节。

后台级联删除

在后台级联删除过程中,Kubernetes 服务器立即删除属主对象,控制器在后台清理所有依赖对象。 默认情况下,Kubernetes 使用后台级联删除方案,除非你手动设置了要使用前台删除, 或者选择遗弃依赖对象。

参阅使用后台级联删除 以了解进一步的细节。

被遗弃的依赖对象

当 Kubernetes 删除某个属主对象时,被留下来的依赖对象被称作被遗弃的(Orphaned)对象。 默认情况下,Kubernetes 会删除依赖对象。要了解如何重载这种默认行为,可参阅 删除属主对象和遗弃依赖对象

未使用容器和镜像的垃圾收集

kubelet 会每两分钟对未使用的镜像执行一次垃圾收集, 每分钟对未使用的容器执行一次垃圾收集。 你应该避免使用外部的垃圾收集工具,因为外部工具可能会破坏 kubelet 的行为,移除应该保留的容器。

要配置对未使用容器和镜像的垃圾收集选项,可以使用一个 配置文件,基于 KubeletConfiguration 资源类型来调整与垃圾收集相关的 kubelet 行为。

容器镜像生命周期

Kubernetes 通过其镜像管理器(Image Manager) 来管理所有镜像的生命周期, 该管理器是 kubelet 的一部分,工作时与 cadvisor 协同。 kubelet 在作出垃圾收集决定时会考虑如下磁盘用量约束:

  • HighThresholdPercent
  • LowThresholdPercent

磁盘用量超出所配置的 HighThresholdPercent 值时会触发垃圾收集, 垃圾收集器会基于镜像上次被使用的时间来按顺序删除它们,首先删除的是最近未使用的镜像。 kubelet 会持续删除镜像,直到磁盘用量到达 LowThresholdPercent 值为止。

未使用容器镜像的垃圾收集

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

这是一个 Beta 特性,不论磁盘使用情况如何,你都可以指定本地镜像未被使用的最长时间。 这是一个可以为每个节点配置的 kubelet 设置。

请为 kubelet 启用 ImageMaximumGCAge 特性门控, 并在 kubelet 配置文件中为 ImageMaximumGCAge 字段赋值来配置该设置。

该值应遵循 Kubernetes 持续时间(Duration) 格式;例如,你可以将配置字段设置为 3d12h, 代表 3 天 12 小时。

容器垃圾收集

kubelet 会基于如下变量对所有未使用的容器执行垃圾收集操作,这些变量都是你可以定义的:

  • MinAge:kubelet 可以垃圾回收某个容器时该容器的最小年龄。设置为 0 表示禁止使用此规则。
  • MaxPerPodContainer:每个 Pod 可以包含的已死亡的容器个数上限。设置为小于 0 的值表示禁止使用此规则。
  • MaxContainers:集群中可以存在的已死亡的容器个数上限。设置为小于 0 的值意味着禁止应用此规则。

除以上变量之外,kubelet 还会垃圾收集除无标识的以及已删除的容器,通常从最近未使用的容器开始。

当保持每个 Pod 的最大数量的容器(MaxPerPodContainer)会使得全局的已死亡容器个数超出上限 (MaxContainers)时,MaxPerPodContainerMaxContainers 之间可能会出现冲突。 在这种情况下,kubelet 会调整 MaxPerPodContainer 来解决这一冲突。 最坏的情形是将 MaxPerPodContainer 降格为 1,并驱逐最近未使用的容器。 此外,当隶属于某已被删除的 Pod 的容器的年龄超过 MinAge 时,它们也会被删除。

配置垃圾收集

你可以通过配置特定于管理资源的控制器来调整资源的垃圾收集行为。 下面的页面为你展示如何配置垃圾收集:

接下来

3.2.9 - 混合版本代理

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

Kubernetes 1.30 包含了一个 Alpha 特性,可以让 API 服务器代理指向其他对等 API 服务器的资源请求。当一个集群中运行着多个 API 服务器,且各服务器的 Kubernetes 版本不同时 (例如在上线 Kubernetes 新版本的时间跨度较长时),这一特性非常有用。

此特性通过将(升级过程中所发起的)资源请求引导到正确的 kube-apiserver 使得集群管理员能够配置高可用的、升级动作更安全的集群。 该代理机制可以防止用户在升级过程中看到意外的 404 Not Found 错误。

这个机制称为 Mixed Version Proxy(混合版本代理)

启用混合版本代理

当你启动 API 服务器时, 确保启用了 UnknownVersionInteroperabilityProxy 特性门控

kube-apiserver \
--feature-gates=UnknownVersionInteroperabilityProxy=true \
# 需要为此特性添加的命令行参数
--peer-ca-file=<指向 kube-apiserver CA 证书的路径>
--proxy-client-cert-file=<指向聚合器代理证书的路径>,
--proxy-client-key-file=<指向聚合器代理密钥的路径>,
--requestheader-client-ca-file=<指向聚合器 CA 证书的路径>,
# requestheader-allowed-names 可设置为空以允许所有 Common Name
--requestheader-allowed-names=<验证代理客户端证书的合法 Common Name>,

# 此特性的可选标志
--peer-advertise-ip=`应由对等方用于代理请求的 kube-apiserver IP`
--peer-advertise-port=`应由对等方用于代理请求的 kube-apiserver 端口`

# ... 和其他常规标志

API 服务器之间的代理传输和身份验证

  • 源 kube-apiserver 重用现有的 API 服务器客户端身份验证标志 --proxy-client-cert-file--proxy-client-key-file 来表明其身份,供对等(目标 kube-apiserver)验证。 目标 API 服务器根据你使用 --requestheader-client-ca-file 命令行参数指定的配置来验证对等连接。

  • 要对目标服务器所用的证书进行身份验证,必须通过指定 --peer-ca-file 命令行参数来为 API 服务器配置一个证书机构包。

对等 API 服务器连接的配置

要设置 kube-apiserver 的网络位置以供对等方来代理请求, 使用为 kube-apiserver 设置的 --peer-advertise-ip--peer-advertise-port 命令行参数, 或在 API 服务器配置文件中指定这些字段。如果未指定这些参数,对等方将使用 --advertise-address--bind-address 命令行参数的值。如果这些也未设置,则使用主机的默认接口。

混合版本代理

启用混合版本代理时, 聚合层会加载一个特殊的过滤器, 完成以下操作:

  • 当资源请求到达无法提供该 API 的 API 服务器时 (可能的原因是服务器早于该 API 的正式引入日期或该 API 在 API 服务器上被关闭), API 服务器会尝试将请求发送到能够提供所请求 API 的对等 API 服务器。 API 服务器通过发现本地服务器无法识别的 API 组/版本/资源来实现这一点, 并尝试将这些请求代理到能够处理这些请求的对等 API 服务器。
  • 如果对等 API 服务器无法响应,则 API 服务器将以 503("Service Unavailable")错误进行响应。

内部工作原理

当 API 服务器收到一个资源请求时,它首先检查哪些 API 服务器可以提供所请求的资源。 这个检查是使用内部的 StorageVersion API 进行的。

  • 如果资源被收到请求(例如 GET /api/v1/pods/some-pod)的 API 服务器所了解,则请求会在本地处理。
  • 如果没有找到适合所请求资源(例如 GET /my-api/v1/my-resource)的内部 StorageVersion 对象, 并且所配置的 APIService 设置了指向扩展 API 服务器的代理,那么代理操作将按照扩展 API 的常规流程进行。
  • 如果找到了对应所请求资源(例如 GET /batch/v1/jobs)的合法的内部 StorageVersion 对象, 并且正在处理请求的 API 服务器(处理中的 API 服务器)禁用了 batch API, 则正处理的 API 服务器使用已获取的 StorageVersion 对象中的信息, 获取提供相关 API 组/版本/资源(在此情况下为 api/v1/batch)的对等 API 服务器。 处理中的 API 服务器随后将请求代理到能够理解所请求资源且匹配的对等 kube-apiserver 之一。

    • 如果没有对等方了解所给的 API 组/版本/资源,则处理请求的 API 服务器将请求传递给自己的处理程序链, 最终应返回 404("Not Found")响应。

    • 如果处理请求的 API 服务器已经识别并选择了一个对等 API 服务器,但该对等方无法响应 (原因可能是网络连接问题或正接收的请求与向控制平面注册对等信息的控制器之间存在数据竞争等), 则处理请求的 API 服务器会以 503("Service Unavailable")错误进行响应。

3.3 - 容器

打包应用及其运行依赖环境的技术。

每个运行的容器都是可重复的; 包含依赖环境在内的标准,意味着无论你在哪里运行它都会得到相同的行为。

容器将应用程序从底层的主机设施中解耦。 这使得在不同的云或 OS 环境中部署更加容易。

Kubernetes 集群中的每个节点都会运行容器, 这些容器构成分配给该节点的 Pod。 单个 Pod 中的容器会在共同调度下,于同一位置运行在相同的节点上。

容器镜像

容器镜像是一个随时可以运行的软件包, 包含运行应用程序所需的一切:代码和它需要的所有运行时、应用程序和系统库,以及一些基本设置的默认值。

容器旨在设计成无状态且不可变的: 你不应更改已经运行的容器的代码。如果有一个容器化的应用程序需要修改, 正确的流程是:先构建包含更改的新镜像,再基于新构建的镜像重新运行容器。

容器运行时

这个基础组件使 Kubernetes 能够有效运行容器。 它负责管理 Kubernetes 环境中容器的执行和生命周期。

Kubernetes 支持许多容器运行环境,例如 containerdCRI-O 以及 Kubernetes CRI (容器运行环境接口) 的其他任何实现。

通常,你可以允许集群为一个 Pod 选择其默认的容器运行时。如果你需要在集群中使用多个容器运行时, 你可以为一个 Pod 指定 RuntimeClass, 以确保 Kubernetes 会使用特定的容器运行时来运行这些容器。

你还可以通过 RuntimeClass,使用相同的容器运行时,但使用不同设定的配置来运行不同的 Pod。

3.3.1 - 镜像

容器镜像(Image)所承载的是封装了应用程序及其所有软件依赖的二进制数据。 容器镜像是可执行的软件包,可以单独运行;该软件包对所处的运行时环境具有良定(Well Defined)的假定。

你通常会创建应用的容器镜像并将其推送到某仓库(Registry),然后在 Pod 中引用它。

本页概要介绍容器镜像的概念。

镜像名称

容器镜像通常会被赋予 pauseexample/mycontainer 或者 kube-apiserver 这类的名称。 镜像名称也可以包含所在仓库的主机名。例如:fictional.registry.example/imagename。 还可以包含仓库的端口号,例如:fictional.registry.example:10443/imagename

如果你不指定仓库的主机名,Kubernetes 认为你在使用 Docker 公共仓库。

在镜像名称之后,你可以添加一个标签(Tag)(与使用 dockerpodman 等命令时的方式相同)。 使用标签能让你辨识同一镜像序列中的不同版本。

镜像标签可以包含小写字母、大写字母、数字、下划线(_)、句点(.)和连字符(-)。 关于在镜像标签中何处可以使用分隔字符(_-.)还有一些额外的规则。 如果你不指定标签,Kubernetes 认为你想使用标签 latest

更新镜像

当你最初创建一个 DeploymentStatefulSet、Pod 或者其他包含 Pod 模板的对象时,如果没有显式设定的话, Pod 中所有容器的默认镜像拉取策略是 IfNotPresent。这一策略会使得 kubelet 在镜像已经存在的情况下直接略过拉取镜像的操作。

镜像拉取策略

容器的 imagePullPolicy 和镜像的标签会影响 kubelet 尝试拉取(下载)指定的镜像。

以下列表包含了 imagePullPolicy 可以设置的值,以及这些值的效果:

IfNotPresent
只有当镜像在本地不存在时才会拉取。
Always
每当 kubelet 启动一个容器时,kubelet 会查询容器的镜像仓库, 将名称解析为一个镜像摘要。 如果 kubelet 有一个容器镜像,并且对应的摘要已在本地缓存,kubelet 就会使用其缓存的镜像; 否则,kubelet 就会使用解析后的摘要拉取镜像,并使用该镜像来启动容器。
Never
Kubelet 不会尝试获取镜像。如果镜像已经以某种方式存在本地, kubelet 会尝试启动容器;否则,会启动失败。 更多细节见提前拉取镜像

只要能够可靠地访问镜像仓库,底层镜像提供者的缓存语义甚至可以使 imagePullPolicy: Always 高效。 你的容器运行时可以注意到节点上已经存在的镜像层,这样就不需要再次下载。

为了确保 Pod 总是使用相同版本的容器镜像,你可以指定镜像的摘要; 将 <image-name>:<tag> 替换为 <image-name>@<digest>,例如 image@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2

当使用镜像标签时,如果镜像仓库修改了代码所对应的镜像标签,可能会出现新旧代码混杂在 Pod 中运行的情况。 镜像摘要唯一标识了镜像的特定版本,因此 Kubernetes 每次启动具有指定镜像名称和摘要的容器时,都会运行相同的代码。 通过摘要指定镜像可固定你运行的代码,这样镜像仓库的变化就不会导致版本的混杂。

有一些第三方的准入控制器 在创建 Pod(和 Pod 模板)时产生变更,这样运行的工作负载就是根据镜像摘要,而不是标签来定义的。 无论镜像仓库上的标签发生什么变化,你都想确保你所有的工作负载都运行相同的代码,那么指定镜像摘要会很有用。

默认镜像拉取策略

当你(或控制器)向 API 服务器提交一个新的 Pod 时,你的集群会在满足特定条件时设置 imagePullPolicy 字段:

  • 如果你省略了 imagePullPolicy 字段,并且你为容器镜像指定了摘要, 那么 imagePullPolicy 会自动设置为 IfNotPresent
  • 如果你省略了 imagePullPolicy 字段,并且容器镜像的标签是 :latestimagePullPolicy 会自动设置为 Always
  • 如果你省略了 imagePullPolicy 字段,并且没有指定容器镜像的标签, imagePullPolicy 会自动设置为 Always
  • 如果你省略了 imagePullPolicy 字段,并且为容器镜像指定了非 :latest 的标签, imagePullPolicy 就会自动设置为 IfNotPresent

必要的镜像拉取

如果你想总是强制执行拉取,你可以使用下述的一中方式:

  • 设置容器的 imagePullPolicyAlways
  • 省略 imagePullPolicy,并使用 :latest 作为镜像标签; 当你提交 Pod 时,Kubernetes 会将策略设置为 Always
  • 省略 imagePullPolicy 和镜像的标签; 当你提交 Pod 时,Kubernetes 会将策略设置为 Always
  • 启用准入控制器 AlwaysPullImages

ImagePullBackOff

当 kubelet 使用容器运行时创建 Pod 时,容器可能因为 ImagePullBackOff 导致状态为 Waiting

ImagePullBackOff 状态意味着容器无法启动, 因为 Kubernetes 无法拉取容器镜像(原因包括无效的镜像名称,或从私有仓库拉取而没有 imagePullSecret)。 BackOff 部分表示 Kubernetes 将继续尝试拉取镜像,并增加回退延迟。

Kubernetes 会增加每次尝试之间的延迟,直到达到编译限制,即 300 秒(5 分钟)。

基于运行时类的镜像拉取

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

Kubernetes 包含了根据 Pod 的 RuntimeClass 来执行镜像拉取的 Alpha 支持。

如果你启用了 RuntimeClassInImageCriApi 特性门控, kubelet 会通过一个元组(镜像名称,运行时处理程序)而不仅仅是镜像名称或镜像摘要来引用容器镜像。 你的容器运行时 可能会根据选定的运行时处理程序调整其行为。 基于运行时类来拉取镜像对于基于 VM 的容器(如 Windows Hyper-V 容器)会有帮助。

串行和并行镜像拉取

默认情况下,kubelet 以串行方式拉取镜像。 也就是说,kubelet 一次只向镜像服务发送一个镜像拉取请求。 其他镜像拉取请求必须等待,直到正在处理的那个请求完成。

节点独立地做出镜像拉取的决策。即使你使用串行的镜像拉取,两个不同的节点也可以并行拉取相同的镜像。

如果你想启用并行镜像拉取,可以在 kubelet 配置 中将字段 serializeImagePulls 设置为 false。

serializeImagePulls 设置为 false 时,kubelet 会立即向镜像服务发送镜像拉取请求,多个镜像将同时被拉动。

启用并行镜像拉取时,请确保你的容器运行时的镜像服务可以处理并行镜像拉取。

kubelet 从不代表一个 Pod 并行地拉取多个镜像。

例如,如果你有一个 Pod,它有一个初始容器和一个应用容器,那么这两个容器的镜像拉取将不会并行。 但是,如果你有两个使用不同镜像的 Pod,当启用并行镜像拉取时,kubelet 会代表两个不同的 Pod 并行拉取镜像。

最大并行镜像拉取数量

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

serializeImagePulls 被设置为 false 时,kubelet 默认对同时拉取的最大镜像数量没有限制。 如果你想限制并行镜像拉取的数量,可以在 kubelet 配置中设置字段 maxParallelImagePulls。 当 maxParallelImagePulls 设置为 n 时,只能同时拉取 n 个镜像, 超过 n 的任何镜像都必须等到至少一个正在进行拉取的镜像拉取完成后,才能拉取。

当启用并行镜像拉取时,限制并行镜像拉取的数量可以防止镜像拉取消耗过多的网络带宽或磁盘 I/O。

你可以将 maxParallelImagePulls 设置为大于或等于 1 的正数。 如果将 maxParallelImagePulls 设置为大于等于 2,则必须将 serializeImagePulls 设置为 false。 kubelet 在无效的 maxParallelImagePulls 设置下会启动失败。

带镜像索引的多架构镜像

除了提供二进制的镜像之外, 容器仓库也可以提供容器镜像索引。 镜像索引可以指向镜像的多个镜像清单, 提供特定于体系结构版本的容器。 这背后的理念是让你可以为镜像命名(例如:pauseexample/mycontainerkube-apiserver) 的同时,允许不同的系统基于它们所使用的机器体系结构取回正确的二进制镜像。

Kubernetes 自身通常在命名容器镜像时添加后缀 -$(ARCH)。 为了向前兼容,请在生成较老的镜像时也提供后缀。 这里的理念是为某镜像(如 pause)生成针对所有平台都适用的清单时, 生成 pause-amd64 这类镜像,以便较老的配置文件或者将镜像后缀硬编码到其中的 YAML 文件也能兼容。

使用私有仓库

从私有仓库读取镜像时可能需要密钥。 凭据可以用以下方式提供:

  • 配置节点向私有仓库进行身份验证
    • 所有 Pod 均可读取任何已配置的私有仓库
    • 需要集群管理员配置节点
  • kubelet 凭据提供程序,动态获取私有仓库的凭据
    • kubelet 可以被配置为使用凭据提供程序 exec 插件来访问对应的私有镜像库
  • 预拉镜像
    • 所有 Pod 都可以使用节点上缓存的所有镜像
    • 需要所有节点的 root 访问权限才能进行设置
  • 在 Pod 中设置 ImagePullSecrets
    • 只有提供自己密钥的 Pod 才能访问私有仓库
  • 特定于厂商的扩展或者本地扩展
    • 如果你在使用定制的节点配置,你(或者云平台提供商)可以实现让节点向容器仓库认证的机制

下面将详细描述每一项。

配置 Node 对私有仓库认证

设置凭据的具体说明取决于你选择使用的容器运行时和仓库。 你应该参考解决方案的文档来获取最准确的信息。

有关配置私有容器镜像仓库的示例, 请参阅任务从私有镜像库中拉取镜像。 该示例使用 Docker Hub 中的私有镜像仓库。

用于认证镜像拉取的 kubelet 凭据提供程序

你可以配置 kubelet,以调用插件可执行文件的方式来动态获取容器镜像的仓库凭据。 这是为私有仓库获取凭据最稳健和最通用的方法,但也需要 kubelet 级别的配置才能启用。

有关更多细节请参见配置 kubelet 镜像凭据提供程序

config.json 说明

对于 config.json 的解释在原始 Docker 实现和 Kubernetes 的解释之间有所不同。 在 Docker 中,auths 键只能指定根 URL,而 Kubernetes 允许 glob URL 以及前缀匹配的路径。 唯一的限制是 glob 模式(*)必须为每个子域名包括点(.)。 匹配的子域名数量必须等于 glob 模式(*.)的数量,例如:

  • *.kubernetes.io 会匹配 kubernetes.io,但会匹配 abc.kubernetes.io
  • *.*.kubernetes.io 会匹配 abc.kubernetes.io,但会匹配 abc.def.kubernetes.io
  • prefix.*.io 将匹配 prefix.kubernetes.io
  • *-good.kubernetes.io 将匹配 prefix-good.kubernetes.io

这意味着,像这样的 config.json 是有效的:

{
    "auths": {
        "my-registry.io/images": { "auth": "…" },
        "*.my-registry.io/images": { "auth": "…" }
    }
}

现在镜像拉取操作会将每种有效模式的凭据都传递给 CRI 容器运行时。例如下面的容器镜像名称会匹配成功:

  • my-registry.io/images
  • my-registry.io/images/my-image
  • my-registry.io/images/another-image
  • sub.my-registry.io/images/my-image

但这些不会匹配成功:

  • a.sub.my-registry.io/images/my-image
  • a.b.sub.my-registry.io/images/my-image

kubelet 为每个找到的凭据的镜像按顺序拉取。这意味着对于不同的路径在 config.json 中也可能有多项:

{
    "auths": {
        "my-registry.io/images": {
            "auth": "…"
        },
        "my-registry.io/images/subpath": {
            "auth": "…"
        }
    }
}

如果一个容器指定了要拉取的镜像 my-registry.io/images/subpath/my-image, 并且其中一个失败,kubelet 将尝试从另一个身份验证源下载镜像。

提前拉取镜像

默认情况下,kubelet 会尝试从指定的仓库拉取每个镜像。 但是,如果容器属性 imagePullPolicy 设置为 IfNotPresent 或者 Never, 则会优先使用(对应 IfNotPresent)或者一定使用(对应 Never)本地镜像。

如果你希望使用提前拉取镜像的方法代替仓库认证,就必须保证集群中所有节点提前拉取的镜像是相同的。

这一方案可以用来提前载入指定的镜像以提高速度,或者作为向私有仓库执行身份认证的一种替代方案。

所有的 Pod 都可以使用节点上提前拉取的镜像。

在 Pod 上指定 ImagePullSecrets

Kubernetes 支持在 Pod 中设置容器镜像仓库的密钥。 imagePullSecrets 必须全部与 Pod 位于同一个名字空间中。 引用的 Secret 必须是 kubernetes.io/dockercfgkubernetes.io/dockerconfigjson 类型。

使用 Docker Config 创建 Secret

你需要知道用于向仓库进行身份验证的用户名、密码和客户端电子邮件地址,以及它的主机名。 运行以下命令,注意替换适当的大写值:

kubectl create secret docker-registry <name> \
  --docker-server=DOCKER_REGISTRY_SERVER \
  --docker-username=DOCKER_USER \
  --docker-password=DOCKER_PASSWORD \
  --docker-email=DOCKER_EMAIL

如果你已经有 Docker 凭据文件,则可以将凭据文件导入为 Kubernetes Secret, 而不是执行上面的命令。 基于已有的 Docker 凭据创建 Secret 解释了如何完成这一操作。

如果你在使用多个私有容器仓库,这种技术将特别有用。 原因是 kubectl create secret docker-registry 创建的是仅适用于某个私有仓库的 Secret。

在 Pod 中引用 ImagePullSecrets

现在,在创建 Pod 时,可以在 Pod 定义中增加 imagePullSecrets 部分来引用该 Secret。 imagePullSecrets 数组中的每一项只能引用同一名字空间中的 Secret。

例如:

cat <<EOF > pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: foo
  namespace: awesomeapps
spec:
  containers:
    - name: foo
      image: janedoe/awesomeapp:v1
  imagePullSecrets:
    - name: myregistrykey
EOF

cat <<EOF >> ./kustomization.yaml
resources:
- pod.yaml
EOF

你需要对使用私有仓库的每个 Pod 执行以上操作。不过, 设置该字段的过程也可以通过为服务账号资源设置 imagePullSecrets 来自动完成。有关详细指令, 可参见将 ImagePullSecrets 添加到服务账号

你也可以将此方法与节点级别的 .docker/config.json 配置结合使用。 来自不同来源的凭据会被合并。

使用案例

配置私有仓库有多种方案,以下是一些常用场景和建议的解决方案。

  1. 集群运行非专有镜像(例如,开源镜像)。镜像不需要隐藏。
    • 使用来自公共仓库的公共镜像
      • 无需配置
      • 某些云厂商会自动为公开镜像提供高速缓存,以便提升可用性并缩短拉取镜像所需时间
  1. 集群运行一些专有镜像,这些镜像需要对公司外部隐藏,对所有集群用户可见
    • 使用托管的私有仓库
      • 在需要访问私有仓库的节点上可能需要手动配置
    • 或者,在防火墙内运行一个组织内部的私有仓库,并开放读取权限
      • 不需要配置 Kubernetes
    • 使用控制镜像访问的托管容器镜像仓库服务
      • 与手动配置节点相比,这种方案能更好地处理集群自动扩缩容
    • 或者,在不方便更改节点配置的集群中,使用 imagePullSecrets
  1. 集群使用专有镜像,且有些镜像需要更严格的访问控制
    • 确保 AlwaysPullImages 准入控制器被启用。 否则,所有 Pod 都可以使用所有镜像。
    • 确保将敏感数据存储在 Secret 资源中,而不是将其打包在镜像里。
  1. 集群是多租户的并且每个租户需要自己的私有仓库
    • 确保 AlwaysPullImages 准入控制器。 否则,所有租户的所有的 Pod 都可以使用所有镜像。
    • 为私有仓库启用鉴权。
    • 为每个租户生成访问仓库的凭据,放置在 Secret 中,并将 Secret 发布到各租户的名字空间下。
    • 租户将 Secret 添加到每个名字空间中的 imagePullSecrets。

如果你需要访问多个仓库,可以为每个仓库创建一个 Secret。

旧版的内置 kubelet 凭据提供程序

在旧版本的 Kubernetes 中,kubelet 与云提供商凭据直接集成。 这使它能够动态获取镜像仓库的凭据。

kubelet 凭据提供程序集成存在三个内置实现: ACR(Azure 容器仓库)、ECR(Elastic 容器仓库)和 GCR(Google 容器仓库)。

有关该旧版机制的更多信息,请阅读你正在使用的 Kubernetes 版本的文档。 从 Kubernetes v1.26 到 v1.30 不再包含该旧版机制,因此你需要:

  • 在每个节点上配置一个 kubelet 镜像凭据提供程序
  • 使用 imagePullSecrets 和至少一个 Secret 指定镜像拉取凭据

接下来

3.3.2 - 容器环境

本页描述了在容器环境里容器可用的资源。

容器环境

Kubernetes 的容器环境给容器提供了几个重要的资源:

  • 文件系统,其中包含一个镜像 和一个或多个的
  • 容器自身的信息
  • 集群中其他对象的信息

容器信息

一个容器的 hostname 是该容器运行所在的 Pod 的名称。通过 hostname 命令或者调用 libc 中的 gethostname 函数可以获取该名称。

Pod 名称和命名空间可以通过 下行 API 转换为环境变量。

Pod 定义中的用户所定义的环境变量也可在容器中使用,就像在 container 镜像中静态指定的任何环境变量一样。

集群信息

创建容器时正在运行的所有服务都可用作该容器的环境变量。 这里的服务仅限于新容器的 Pod 所在的名字空间中的服务,以及 Kubernetes 控制面的服务。

对于名为 foo 的服务,当映射到名为 bar 的容器时,定义了以下变量:

FOO_SERVICE_HOST=<其上服务正运行的主机>
FOO_SERVICE_PORT=<其上服务正运行的端口>

服务具有专用的 IP 地址。如果启用了 DNS 插件, 可以在容器中通过 DNS 来访问服务。

接下来

3.3.3 - 容器运行时类(Runtime Class)

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

本页面描述了 RuntimeClass 资源和运行时的选择机制。

RuntimeClass 是一个用于选择容器运行时配置的特性,容器运行时配置用于运行 Pod 中的容器。

动机

你可以在不同的 Pod 设置不同的 RuntimeClass,以提供性能与安全性之间的平衡。 例如,如果你的部分工作负载需要高级别的信息安全保证,你可以决定在调度这些 Pod 时尽量使它们在使用硬件虚拟化的容器运行时中运行。 这样,你将从这些不同运行时所提供的额外隔离中获益,代价是一些额外的开销。

你还可以使用 RuntimeClass 运行具有相同容器运行时但具有不同设置的 Pod。

设置

  1. 在节点上配置 CRI 的实现(取决于所选用的运行时)
  2. 创建相应的 RuntimeClass 资源

1. 在节点上配置 CRI 实现

RuntimeClass 的配置依赖于运行时接口(CRI)的实现。 根据你使用的 CRI 实现,查阅相关的文档(下方)来了解如何配置。

所有这些配置都具有相应的 handler 名,并被 RuntimeClass 引用。 handler 必须是有效的 DNS 标签名

2. 创建相应的 RuntimeClass 资源

在上面步骤 1 中,每个配置都需要有一个用于标识配置的 handler。 针对每个 handler 需要创建一个 RuntimeClass 对象。

RuntimeClass 资源当前只有两个重要的字段:RuntimeClass 名 (metadata.name) 和 handler (handler)。 对象定义如下所示:

# RuntimeClass 定义于 node.k8s.io API 组
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  # 用来引用 RuntimeClass 的名字
  # RuntimeClass 是一个集群层面的资源
  name: myclass
# 对应的 CRI 配置的名称
handler: myconfiguration

RuntimeClass 对象的名称必须是有效的 DNS 子域名

使用说明

一旦完成集群中 RuntimeClasses 的配置, 你可以在 Pod spec 中指定 runtimeClassName 来使用它。例如:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  runtimeClassName: myclass
  # ...

这一设置会告诉 kubelet 使用所指的 RuntimeClass 来运行该 Pod。 如果所指的 RuntimeClass 不存在或者 CRI 无法运行相应的 handler, 那么 Pod 将会进入 Failed 终止阶段。 你可以查看相应的事件, 获取执行过程中的错误信息。

如果未指定 runtimeClassName,则将使用默认的 RuntimeHandler,相当于禁用 RuntimeClass 功能特性。

CRI 配置

关于如何安装 CRI 运行时,请查阅 CRI 安装

containerd

通过 containerd 的 /etc/containerd/config.toml 配置文件来配置运行时 handler。 handler 需要配置在 runtimes 块中:

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.${HANDLER_NAME}]

更详细信息,请查阅 containerd 的配置指南

CRI-O

通过 CRI-O 的 /etc/crio/crio.conf 配置文件来配置运行时 handler。 handler 需要配置在 crio.runtime 表之下:

[crio.runtime.runtimes.${HANDLER_NAME}]
  runtime_path = "${PATH_TO_BINARY}"

更详细信息,请查阅 CRI-O 配置文档

调度

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

通过为 RuntimeClass 指定 scheduling 字段, 你可以通过设置约束,确保运行该 RuntimeClass 的 Pod 被调度到支持该 RuntimeClass 的节点上。 如果未设置 scheduling,则假定所有节点均支持此 RuntimeClass。

为了确保 pod 会被调度到支持指定运行时的 node 上,每个 node 需要设置一个通用的 label 用于被 runtimeclass.scheduling.nodeSelector 挑选。在 admission 阶段,RuntimeClass 的 nodeSelector 将会与 Pod 的 nodeSelector 合并,取二者的交集。如果有冲突,Pod 将会被拒绝。

如果 node 需要阻止某些需要特定 RuntimeClass 的 Pod,可以在 tolerations 中指定。 与 nodeSelector 一样,tolerations 也在 admission 阶段与 Pod 的 tolerations 合并,取二者的并集。

更多有关 node selector 和 tolerations 的配置信息,请查阅 将 Pod 分派到节点

Pod 开销

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

你可以指定与运行 Pod 相关的开销资源。声明开销即允许集群(包括调度器)在决策 Pod 和资源时将其考虑在内。

Pod 开销通过 RuntimeClass 的 overhead 字段定义。 通过使用这个字段,你可以指定使用该 RuntimeClass 运行 Pod 时的开销并确保 Kubernetes 将这些开销计算在内。

接下来

3.3.4 - 容器生命周期回调

这个页面描述了 kubelet 管理的容器如何使用容器生命周期回调框架, 藉由其管理生命周期中的事件触发,运行指定代码。

概述

类似于许多具有生命周期回调组件的编程语言框架,例如 Angular、Kubernetes 为容器提供了生命周期回调。 回调使容器能够了解其管理生命周期中的事件,并在执行相应的生命周期回调时运行在处理程序中实现的代码。

容器回调

有两个回调暴露给容器:

PostStart

这个回调在容器被创建之后立即被执行。 但是,不能保证回调会在容器入口点(ENTRYPOINT)之前执行。 没有参数传递给处理程序。

PreStop

在容器因 API 请求或者管理事件(诸如存活态探针、启动探针失败、资源抢占、资源竞争等) 而被终止之前,此回调会被调用。 如果容器已经处于已终止或者已完成状态,则对 preStop 回调的调用将失败。 在用来停止容器的 TERM 信号被发出之前,回调必须执行结束。 Pod 的终止宽限周期在 PreStop 回调被执行之前即开始计数, 所以无论回调函数的执行结果如何,容器最终都会在 Pod 的终止宽限期内被终止。 没有参数会被传递给处理程序。

有关终止行为的更详细描述,请参见 终止 Pod

回调处理程序的实现

容器可以通过实现和注册该回调的处理程序来访问该回调。 针对容器,有三种类型的回调处理程序可供实现:

  • Exec - 在容器的 cgroups 和名字空间中执行特定的命令(例如 pre-stop.sh)。 命令所消耗的资源计入容器的资源消耗。
  • HTTP - 对容器上的特定端点执行 HTTP 请求。
  • Sleep - 将容器暂停一段指定的时间。 当特性门控 PodLifecycleSleepAction 被启用时, “Sleep” 操作才可用。

回调处理程序执行

当调用容器生命周期管理回调时,Kubernetes 管理系统根据回调动作执行其处理程序, httpGettcpSocketsleep 由 kubelet 进程执行,而 exec 在容器中执行。

回调处理程序调用在包含容器的 Pod 上下文中是同步的。 这意味着对于 PostStart 回调,容器入口点和回调异步触发。 但是,如果回调运行或挂起的时间太长,则容器无法达到 running 状态。

PreStop 回调并不会与停止容器的信号处理程序异步执行;回调必须在可以发送信号之前完成执行。 如果 PreStop 回调在执行期间停滞不前,Pod 的阶段会变成 Terminating并且一直处于该状态, 直到其 terminationGracePeriodSeconds 耗尽为止,这时 Pod 会被杀死。 这一宽限期是针对 PreStop 回调的执行时间及容器正常停止时间的总和而言的。 例如,如果 terminationGracePeriodSeconds 是 60,回调函数花了 55 秒钟完成执行, 而容器在收到信号之后花了 10 秒钟来正常结束,那么容器会在其能够正常结束之前即被杀死, 因为 terminationGracePeriodSeconds 的值小于后面两件事情所花费的总时间(55+10)。

如果 PostStartPreStop 回调失败,它会杀死容器。

用户应该使他们的回调处理程序尽可能的轻量级。 但也需要考虑长时间运行的命令也很有用的情况,比如在停止容器之前保存状态。

回调递送保证

回调的递送应该是至少一次,这意味着对于任何给定的事件, 例如 PostStartPreStop,回调可以被调用多次。 如何正确处理被多次调用的情况,是回调实现所要考虑的问题。

通常情况下,只会进行单次递送。 例如,如果 HTTP 回调接收器宕机,无法接收流量,则不会尝试重新发送。 然而,偶尔也会发生重复递送的可能。 例如,如果 kubelet 在发送回调的过程中重新启动,回调可能会在 kubelet 恢复后重新发送。

调试回调处理程序

回调处理程序的日志不会在 Pod 事件中公开。 如果处理程序由于某种原因失败,它将播放一个事件。 对于 PostStart,这是 FailedPostStartHook 事件,对于 PreStop,这是 FailedPreStopHook 事件。 要自己生成失败的 FailedPostStartHook 事件,请修改 lifecycle-events.yaml 文件将 postStart 命令更改为 “badcommand” 并应用它。 以下是通过运行 kubectl describe pod lifecycle-demo 后你看到的一些结果事件的示例输出:

Events:
  Type     Reason               Age              From               Message
  ----     ------               ----             ----               -------
  Normal   Scheduled            7s               default-scheduler  Successfully assigned default/lifecycle-demo to ip-XXX-XXX-XX-XX.us-east-2...
  Normal   Pulled               6s               kubelet            Successfully pulled image "nginx" in 229.604315ms
  Normal   Pulling              4s (x2 over 6s)  kubelet            Pulling image "nginx"
  Normal   Created              4s (x2 over 5s)  kubelet            Created container lifecycle-demo-container
  Normal   Started              4s (x2 over 5s)  kubelet            Started container lifecycle-demo-container
  Warning  FailedPostStartHook  4s (x2 over 5s)  kubelet            Exec lifecycle hook ([badcommand]) for Container "lifecycle-demo-container" in Pod "lifecycle-demo_default(30229739-9651-4e5a-9a32-a8f1688862db)" failed - error: command 'badcommand' exited with 126: , message: "OCI runtime exec failed: exec failed: container_linux.go:380: starting container process caused: exec: \"badcommand\": executable file not found in $PATH: unknown\r\n"
  Normal   Killing              4s (x2 over 5s)  kubelet            FailedPostStartHook
  Normal   Pulled               4s               kubelet            Successfully pulled image "nginx" in 215.66395ms
  Warning  BackOff              2s (x2 over 3s)  kubelet            Back-off restarting failed container

接下来

3.4 - 工作负载

理解 Kubernetes 中可部署的最小计算对象 Pod 以及辅助 Pod 运行的上层抽象。
工作负载是在 Kubernetes 上运行的应用程序。

在 Kubernetes 中,无论你的负载是由单个组件还是由多个一同工作的组件构成, 你都可以在一组 Pod 中运行它。 在 Kubernetes 中,Pod 代表的是集群上处于运行状态的一组 容器的集合。

Kubernetes Pod 遵循预定义的生命周期。 例如,当在你的集群中运行了某个 Pod,但是 Pod 所在的 节点 出现致命错误时, 所有该节点上的 Pod 的状态都会变成失败。Kubernetes 将这类失败视为最终状态: 即使该节点后来恢复正常运行,你也需要创建新的 Pod 以恢复应用。

不过,为了减轻用户的使用负担,通常不需要用户直接管理每个 Pod。 而是使用负载资源来替用户管理一组 Pod。 这些负载资源通过配置 控制器 来确保正确类型的、处于运行状态的 Pod 个数是正确的,与用户所指定的状态相一致。

Kubernetes 提供若干种内置的工作负载资源:

  • DeploymentReplicaSet (替换原来的资源 ReplicationController)。 Deployment 很适合用来管理你的集群上的无状态应用,Deployment 中的所有 Pod 都是相互等价的,并且在需要的时候被替换。
  • StatefulSet 让你能够运行一个或者多个以某种方式跟踪应用状态的 Pod。 例如,如果你的负载会将数据作持久存储,你可以运行一个 StatefulSet,将每个 Pod 与某个 PersistentVolume 对应起来。你在 StatefulSet 中各个 Pod 内运行的代码可以将数据复制到同一 StatefulSet 中的其它 Pod 中以提高整体的服务可靠性。
  • DaemonSet 定义提供节点本地支撑设施的 Pod。这些 Pod 可能对于你的集群的运维是 非常重要的,例如作为网络链接的辅助工具或者作为网络 插件 的一部分等等。每次你向集群中添加一个新节点时,如果该节点与某 DaemonSet 的规约匹配,则控制平面会为该 DaemonSet 调度一个 Pod 到该新节点上运行。
  • JobCronJob。 定义一些一直运行到结束并停止的任务。 你可以使用 Job 来定义只需要执行一次并且执行后即视为完成的任务。你可以使用 CronJob 来根据某个排期表来多次运行同一个 Job。

在庞大的 Kubernetes 生态系统中,你还可以找到一些提供额外操作的第三方工作负载相关的资源。 通过使用定制资源定义(CRD), 你可以添加第三方工作负载资源,以完成原本不是 Kubernetes 核心功能的工作。 例如,如果你希望运行一组 Pod,但要求所有 Pod 都可用时才执行操作 (比如针对某种高吞吐量的分布式任务),你可以基于定制资源实现一个能够满足这一需求的扩展, 并将其安装到集群中运行。

接下来

除了阅读了解每类资源外,你还可以了解与这些资源相关的任务:

要了解 Kubernetes 将代码与配置分离的实现机制,可参阅配置节。

关于 Kubernetes 如何为应用管理 Pod,还有两个支撑概念能够提供相关背景信息:

  • 垃圾收集机制负责在 对象的属主资源被删除时在集群中清理这些对象。
  • Time-to-Live 控制器会在 Job 结束之后的指定时间间隔之后删除它们。

一旦你的应用处于运行状态,你就可能想要以 Service 的形式使之可在互联网上访问;或者对于 Web 应用而言,使用 Ingress 资源将其暴露到互联网上。

3.4.1 - Pod

Pod 是可以在 Kubernetes 中创建和管理的、最小的可部署的计算单元。

Pod(就像在鲸鱼荚或者豌豆荚中)是一组(一个或多个) 容器; 这些容器共享存储、网络、以及怎样运行这些容器的声明。 Pod 中的内容总是并置(colocated)的并且一同调度,在共享的上下文中运行。 Pod 所建模的是特定于应用的 “逻辑主机”,其中包含一个或多个应用容器, 这些容器相对紧密地耦合在一起。 在非云环境中,在相同的物理机或虚拟机上运行的应用类似于在同一逻辑主机上运行的云应用。

除了应用容器,Pod 还可以包含在 Pod 启动期间运行的 Init 容器。 你也可以注入临时性容器来调试正在运行的 Pod。

什么是 Pod?

Pod 的共享上下文包括一组 Linux 名字空间、控制组(cgroup)和可能一些其他的隔离方面, 即用来隔离容器的技术。 在 Pod 的上下文中,每个独立的应用可能会进一步实施隔离。

Pod 类似于共享名字空间并共享文件系统卷的一组容器。

Kubernetes 集群中的 Pod 主要有两种用法:

  • 运行单个容器的 Pod。"每个 Pod 一个容器"模型是最常见的 Kubernetes 用例; 在这种情况下,可以将 Pod 看作单个容器的包装器,并且 Kubernetes 直接管理 Pod,而不是容器。

  • 运行多个协同工作的容器的 Pod。 Pod 可以封装由紧密耦合且需要共享资源的多个并置容器组成的应用。 这些位于同一位置的容器构成一个内聚单元。

    将多个并置、同管的容器组织到一个 Pod 中是一种相对高级的使用场景。 只有在一些场景中,容器之间紧密关联时你才应该使用这种模式。

    你不需要运行多个容器来扩展副本(为了弹性或容量); 如果你需要多个副本,请参阅工作负载管理

使用 Pod

下面是一个 Pod 示例,它由一个运行镜像 nginx:1.14.2 的容器组成。

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx:1.14.2
    ports:
    - containerPort: 80

要创建上面显示的 Pod,请运行以下命令:

kubectl apply -f https://k8s.io/examples/pods/simple-pod.yaml

Pod 通常不是直接创建的,而是使用工作负载资源创建的。 有关如何将 Pod 用于工作负载资源的更多信息,请参阅使用 Pod

用于管理 Pod 的工作负载资源

通常你不需要直接创建 Pod,甚至单实例 Pod。相反,你会使用诸如 DeploymentJob 这类工作负载资源来创建 Pod。 如果 Pod 需要跟踪状态,可以考虑 StatefulSet 资源。

每个 Pod 都旨在运行给定应用程序的单个实例。如果希望横向扩展应用程序 (例如,运行多个实例以提供更多的资源),则应该使用多个 Pod,每个实例使用一个 Pod。 在 Kubernetes 中,这通常被称为副本(Replication)。 通常使用一种工作负载资源及其控制器来创建和管理一组 Pod 副本。

参见 Pod 和控制器以了解 Kubernetes 如何使用工作负载资源及其控制器以实现应用的扩缩和自动修复。

Pod 天生地为其成员容器提供了两种共享资源:网络存储

使用 Pod

你很少在 Kubernetes 中直接创建一个个的 Pod,甚至是单实例(Singleton)的 Pod。 这是因为 Pod 被设计成了相对临时性的、用后即抛的一次性实体。 当 Pod 由你或者间接地由控制器 创建时,它被调度在集群中的节点上运行。 Pod 会保持在该节点上运行,直到 Pod 结束执行、Pod 对象被删除、Pod 因资源不足而被驱逐或者节点失效为止。

Pod 的名称必须是一个合法的 DNS 子域值, 但这可能对 Pod 的主机名产生意外的结果。为获得最佳兼容性,名称应遵循更严格的 DNS 标签规则。

Pod 操作系统

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

你应该将 .spec.os.name 字段设置为 windowslinux 以表示你希望 Pod 运行在哪个操作系统之上。 这两个是 Kubernetes 目前支持的操作系统。将来,这个列表可能会被扩充。

在 Kubernetes v1.30 中,为此字段设置的值对 Pod 的调度没有影响。 设置 .spec.os.name 有助于确定性地标识 Pod 的操作系统并用于验证。 如果你指定的 Pod 操作系统与运行 kubelet 所在节点的操作系统不同, 那么 kubelet 将会拒绝运行该 Pod。 Pod 安全标准也使用这个字段来避免强制执行与该操作系统无关的策略。

Pod 和控制器

你可以使用工作负载资源来创建和管理多个 Pod。 资源的控制器能够处理副本的管理、上线,并在 Pod 失效时提供自愈能力。 例如,如果一个节点失败,控制器注意到该节点上的 Pod 已经停止工作, 就可以创建替换性的 Pod。调度器会将替身 Pod 调度到一个健康的节点执行。

下面是一些管理一个或者多个 Pod 的工作负载资源的示例:

Pod 模板

工作负载资源的控制器通常使用 **Pod 模板(Pod Template)**来替你创建 Pod 并管理它们。

Pod 模板是包含在工作负载对象中的规范,用来创建 Pod。这类负载资源包括 DeploymentJobDaemonSet 等。

工作负载的控制器会使用负载对象中的 PodTemplate 来生成实际的 Pod。 PodTemplate 是你用来运行应用时指定的负载资源的目标状态的一部分。

下面的示例是一个简单的 Job 的清单,其中的 template 指示启动一个容器。 该 Pod 中的容器会打印一条消息之后暂停。

apiVersion: batch/v1
kind: Job
metadata:
  name: hello
spec:
  template:
    # 这里是 Pod 模板
    spec:
      containers:
      - name: hello
        image: busybox:1.28
        command: ['sh', '-c', 'echo "Hello, Kubernetes!" && sleep 3600']
      restartPolicy: OnFailure
    # 以上为 Pod 模板

修改 Pod 模板或者切换到新的 Pod 模板都不会对已经存在的 Pod 直接起作用。 如果改变工作负载资源的 Pod 模板,工作负载资源需要使用更新后的模板来创建 Pod, 并使用新创建的 Pod 替换旧的 Pod。

例如,StatefulSet 控制器针对每个 StatefulSet 对象确保运行中的 Pod 与当前的 Pod 模板匹配。如果编辑 StatefulSet 以更改其 Pod 模板, StatefulSet 将开始基于更新后的模板创建新的 Pod。

每个工作负载资源都实现了自己的规则,用来处理对 Pod 模板的更新。 如果你想了解更多关于 StatefulSet 的具体信息, 请阅读 StatefulSet 基础教程中的更新策略

在节点上,kubelet 并不直接监测或管理与 Pod 模板相关的细节或模板的更新,这些细节都被抽象出来。 这种抽象和关注点分离简化了整个系统的语义, 并且使得用户可以在不改变现有代码的前提下就能扩展集群的行为。

Pod 更新与替换

正如前面章节所述,当某工作负载的 Pod 模板被改变时, 控制器会基于更新的模板创建新的 Pod 对象而不是对现有 Pod 执行更新或者修补操作。

Kubernetes 并不禁止你直接管理 Pod。对运行中的 Pod 的某些字段执行就地更新操作还是可能的。不过,类似 patchreplace 这类更新操作有一些限制:

  • Pod 的绝大多数元数据都是不可变的。例如,你不可以改变其 namespacenameuid 或者 creationTimestamp 字段;generation 字段是比较特别的, 如果更新该字段,只能增加字段取值而不能减少。

  • 如果 metadata.deletionTimestamp 已经被设置,则不可以向 metadata.finalizers 列表中添加新的条目。

  • Pod 更新不可以改变除 spec.containers[*].imagespec.initContainers[*].imagespec.activeDeadlineSecondsspec.tolerations 之外的字段。 对于 spec.tolerations,你只被允许添加新的条目到其中。

  • 在更新 spec.activeDeadlineSeconds 字段时,以下两种更新操作是被允许的:

    1. 如果该字段尚未设置,可以将其设置为一个正数;
    2. 如果该字段已经设置为一个正数,可以将其设置为一个更小的、非负的整数。

资源共享和通信

Pod 使它的成员容器间能够进行数据共享和通信。

Pod 中的存储

一个 Pod 可以设置一组共享的存储。 Pod 中的所有容器都可以访问该共享卷,从而允许这些容器共享数据。 卷还允许 Pod 中的持久数据保留下来,即使其中的容器需要重新启动。 有关 Kubernetes 如何在 Pod 中实现共享存储并将其提供给 Pod 的更多信息, 请参考存储

Pod 联网

每个 Pod 都在每个地址族中获得一个唯一的 IP 地址。 Pod 中的每个容器共享网络名字空间,包括 IP 地址和网络端口。 Pod 内的容器可以使用 localhost 互相通信。 当 Pod 中的容器与 Pod 之外的实体通信时,它们必须协调如何使用共享的网络资源(例如端口)。

在同一个 Pod 内,所有容器共享一个 IP 地址和端口空间,并且可以通过 localhost 发现对方。 他们也能通过如 SystemV 信号量或 POSIX 共享内存这类标准的进程间通信方式互相通信。 不同 Pod 中的容器的 IP 地址互不相同,如果没有特殊配置,就无法通过 OS 级 IPC 进行通信。 如果某容器希望与运行于其他 Pod 中的容器通信,可以通过 IP 联网的方式实现。

Pod 中的容器所看到的系统主机名与为 Pod 配置的 name 属性值相同。 网络部分提供了更多有关此内容的信息。

容器的特权模式

Pod 中的所有容器都可以在特权模式下运行,以使用原本无法访问的操作系统管理权能。 此模式同时适用于 Windows 和 Linux。

Linux 特权容器

在 Linux 中,Pod 中的所有容器都可以使用容器规约中的 安全性上下文中的 privileged(Linux)参数启用特权模式。 这对于想要使用操作系统管理权能(Capabilities,如操纵网络堆栈和访问硬件设备)的容器很有用。

Windows 特权容器

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

在 Windows 中,你可以使用 Pod 规约中安全上下文的 windowsOptions.hostProcess 参数来创建 Windows HostProcess Pod。 这些 Pod 中的所有容器都必须以 Windows HostProcess 容器方式运行。 HostProcess Pod 可以直接运行在主机上,它也能像 Linux 特权容器一样,用于执行管理任务。

静态 Pod

**静态 Pod(Static Pod)**直接由特定节点上的 kubelet 守护进程管理, 不需要 API 服务器看到它们。 尽管大多数 Pod 都是通过控制面(例如,Deployment) 来管理的,对于静态 Pod 而言,kubelet 直接监控每个 Pod,并在其失效时重启之。

静态 Pod 通常绑定到某个节点上的 kubelet。 其主要用途是运行自托管的控制面。 在自托管场景中,使用 kubelet 来管理各个独立的控制面组件

kubelet 自动尝试为每个静态 Pod 在 Kubernetes API 服务器上创建一个镜像 Pod。 这意味着在节点上运行的 Pod 在 API 服务器上是可见的,但不可以通过 API 服务器来控制。 有关更多信息,请参阅创建静态 Pod 的指南。

Pod 管理多个容器

Pod 被设计成支持构造内聚的服务单元的多个协作进程(形式为容器)。 Pod 中的容器被自动并置到集群中的同一物理机或虚拟机上,并可以一起进行调度。 容器之间可以共享资源和依赖、彼此通信、协调何时以及何种方式终止自身。

Kubernetes 集群中的 Pod 主要有两种用法:

  • 运行单个容器的 Pod。"每个 Pod 一个容器" 模型是最常见的 Kubernetes 用例; 在这种情况下,可以将 Pod 看作单个容器的包装器。Kubernetes 直接管理 Pod,而不是容器。
  • 运行多个需要协同工作的容器的 Pod。 Pod 可以封装由多个紧密耦合且需要共享资源的并置容器组成的应用。 这些位于同一位置的容器可能形成单个内聚的服务单元 —— 一个容器将文件从共享卷提供给公众, 而另一个单独的边车容器则刷新或更新这些文件。 Pod 将这些容器和存储资源打包为一个可管理的实体。

例如,你可能有一个容器,为共享卷中的文件提供 Web 服务器支持,以及一个单独的 边车(Sidercar) 容器负责从远端更新这些文件,如下图所示:

Pod 创建示意图

有些 Pod 具有 Init 容器应用容器。 Init 容器默认会在启动应用容器之前运行并完成。

你还可以拥有为主应用 Pod 提供辅助服务的 边车容器(例如:服务网格)。

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

启用 SidecarContainers 特性门控(默认启用)允许你为 Init 容器指定 restartPolicy: Always。设置重启策略为 Always 会确保设置的 Init 容器被视为边车, 并在 Pod 的整个生命周期内保持运行。 更多细节参阅边车容器和重启策略

容器探针

Probe 是由 kubelet 对容器执行的定期诊断。要执行诊断,kubelet 可以执行三种动作:

  • ExecAction(借助容器运行时执行)
  • TCPSocketAction(由 kubelet 直接检测)
  • HTTPGetAction(由 kubelet 直接检测)

你可以参阅 Pod 的生命周期文档中的探针部分。

接下来

要了解为什么 Kubernetes 会在其他资源 (如 StatefulSetDeployment) 封装通用的 Pod API,相关的背景信息可以在前人的研究中找到。具体包括:

3.4.1.1 - Pod 的生命周期

本页面讲述 Pod 的生命周期。 Pod 遵循预定义的生命周期,起始于 Pending 阶段, 如果至少其中有一个主要容器正常启动,则进入 Running,之后取决于 Pod 中是否有容器以失败状态结束而进入 Succeeded 或者 Failed 阶段。

在 Pod 运行期间,kubelet 能够重启容器以处理一些失效场景。 在 Pod 内部,Kubernetes 跟踪不同容器的状态并确定使 Pod 重新变得健康所需要采取的动作。

在 Kubernetes API 中,Pod 包含规约部分和实际状态部分。 Pod 对象的状态包含了一组 Pod 状况(Conditions)。 如果应用需要的话,你也可以向其中注入自定义的就绪态信息

Pod 在其生命周期中只会被调度一次。 一旦 Pod 被调度(分派)到某个节点,Pod 会一直在该节点运行,直到 Pod 停止或者被终止

Pod 生命期

和一个个独立的应用容器一样,Pod 也被认为是相对临时性(而不是长期存在)的实体。 Pod 会被创建、赋予一个唯一的 ID(UID), 并被调度到节点,并在终止(根据重启策略)或删除之前一直运行在该节点。

如果一个节点死掉了,调度到该节点的 Pod 也被计划在给定超时期限结束后删除

Pod 自身不具有自愈能力。如果 Pod 被调度到某节点而该节点之后失效, Pod 会被删除;类似地,Pod 无法在因节点资源耗尽或者节点维护而被驱逐期间继续存活。 Kubernetes 使用一种高级抽象来管理这些相对而言可随时丢弃的 Pod 实例, 称作控制器

任何给定的 Pod (由 UID 定义)从不会被“重新调度(rescheduled)”到不同的节点; 相反,这一 Pod 可以被一个新的、几乎完全相同的 Pod 替换掉。 如果需要,新 Pod 的名字可以不变,但是其 UID 会不同。

如果某物声称其生命期与某 Pod 相同,例如存储, 这就意味着该对象在此 Pod (UID 亦相同)存在期间也一直存在。 如果 Pod 因为任何原因被删除,甚至某完全相同的替代 Pod 被创建时, 这个相关的对象(例如这里的卷)也会被删除并重建。

Pod 结构图例

一个包含多个容器的 Pod 中包含一个用来拉取文件的程序和一个 Web 服务器, 均使用持久卷作为容器间共享的存储。

Pod 阶段

Pod 的 status 字段是一个 PodStatus 对象,其中包含一个 phase 字段。

Pod 的阶段(Phase)是 Pod 在其生命周期中所处位置的简单宏观概述。 该阶段并不是对容器或 Pod 状态的综合汇总,也不是为了成为完整的状态机。

Pod 阶段的数量和含义是严格定义的。 除了本文档中列举的内容外,不应该再假定 Pod 有其他的 phase 值。

下面是 phase 可能的值:

取值描述
Pending(悬决)Pod 已被 Kubernetes 系统接受,但有一个或者多个容器尚未创建亦未运行。此阶段包括等待 Pod 被调度的时间和通过网络下载镜像的时间。
Running(运行中)Pod 已经绑定到了某个节点,Pod 中所有的容器都已被创建。至少有一个容器仍在运行,或者正处于启动或重启状态。
Succeeded(成功)Pod 中的所有容器都已成功终止,并且不会再重启。
Failed(失败)Pod 中的所有容器都已终止,并且至少有一个容器是因为失败终止。也就是说,容器以非 0 状态退出或者被系统终止。
Unknown(未知)因为某些原因无法取得 Pod 的状态。这种情况通常是因为与 Pod 所在主机通信失败。

从 Kubernetes 1.27 开始,除了静态 Pod 和没有 Finalizer 的强制终止 Pod 之外,kubelet 会将已删除的 Pod 转换到终止阶段 (FailedSucceeded 具体取决于 Pod 容器的退出状态),然后再从 API 服务器中删除。

如果某节点死掉或者与集群中其他节点失联,Kubernetes 会实施一种策略,将失去的节点上运行的所有 Pod 的 phase 设置为 Failed

容器状态

Kubernetes 会跟踪 Pod 中每个容器的状态,就像它跟踪 Pod 总体上的阶段一样。 你可以使用容器生命周期回调 来在容器生命周期中的特定时间点触发事件。

一旦调度器将 Pod 分派给某个节点,kubelet 就通过容器运行时开始为 Pod 创建容器。容器的状态有三种:Waiting(等待)、Running(运行中)和 Terminated(已终止)。

要检查 Pod 中容器的状态,你可以使用 kubectl describe pod <pod 名称>。 其输出中包含 Pod 中每个容器的状态。

每种状态都有特定的含义:

Waiting (等待)

如果容器并不处在 RunningTerminated 状态之一,它就处在 Waiting 状态。 处于 Waiting 状态的容器仍在运行它完成启动所需要的操作:例如, 从某个容器镜像仓库拉取容器镜像,或者向容器应用 Secret 数据等等。 当你使用 kubectl 来查询包含 Waiting 状态的容器的 Pod 时,你也会看到一个 Reason 字段,其中给出了容器处于等待状态的原因。

Running(运行中)

Running 状态表明容器正在执行状态并且没有问题发生。 如果配置了 postStart 回调,那么该回调已经执行且已完成。 如果你使用 kubectl 来查询包含 Running 状态的容器的 Pod 时, 你也会看到关于容器进入 Running 状态的信息。

Terminated(已终止)

处于 Terminated 状态的容器已经开始执行并且或者正常结束或者因为某些原因失败。 如果你使用 kubectl 来查询包含 Terminated 状态的容器的 Pod 时, 你会看到容器进入此状态的原因、退出代码以及容器执行期间的起止时间。

如果容器配置了 preStop 回调,则该回调会在容器进入 Terminated 状态之前执行。

容器重启策略

Pod 的 spec 中包含一个 restartPolicy 字段,其可能取值包括 Always、OnFailure 和 Never。默认值是 Always。

restartPolicy 应用于 Pod 中的应用容器和常规的 Init 容器Sidecar 容器忽略 Pod 级别的 restartPolicy 字段:在 Kubernetes 中,Sidecar 被定义为 initContainers 内的一个条目,其容器级别的 restartPolicy 被设置为 Always。 对于因错误而退出的 Init 容器,如果 Pod 级别 restartPolicyOnFailureAlways, 则 kubelet 会重新启动 Init 容器。

当 kubelet 根据配置的重启策略处理容器重启时,仅适用于同一 Pod 内替换容器并在同一节点上运行的重启。当 Pod 中的容器退出时,kubelet 会按指数回退方式计算重启的延迟(10s、20s、40s、...),其最长延迟为 5 分钟。 一旦某容器执行了 10 分钟并且没有出现问题,kubelet 对该容器的重启回退计时器执行重置操作。 Sidecar 容器和 Pod 生命周期中解释了 init containers 在指定 restartpolicy 字段时的行为。

Pod 状况

Pod 有一个 PodStatus 对象,其中包含一个 PodConditions 数组。Pod 可能通过也可能未通过其中的一些状况测试。 Kubelet 管理以下 PodCondition:

  • PodScheduled:Pod 已经被调度到某节点;
  • PodReadyToStartContainers:Pod 沙箱被成功创建并且配置了网络(Beta 特性,默认启用);
  • ContainersReady:Pod 中所有容器都已就绪;
  • Initialized:所有的 Init 容器都已成功完成;
  • Ready:Pod 可以为请求提供服务,并且应该被添加到对应服务的负载均衡池中。
字段名称描述
typePod 状况的名称
status表明该状况是否适用,可能的取值有 "True"、"False" 或 "Unknown"
lastProbeTime上次探测 Pod 状况时的时间戳
lastTransitionTimePod 上次从一种状态转换到另一种状态时的时间戳
reason机器可读的、驼峰编码(UpperCamelCase)的文字,表述上次状况变化的原因
message人类可读的消息,给出上次状态转换的详细信息

Pod 就绪态

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

你的应用可以向 PodStatus 中注入额外的反馈或者信号:Pod Readiness(Pod 就绪态)。 要使用这一特性,可以设置 Pod 规约中的 readinessGates 列表,为 kubelet 提供一组额外的状况供其评估 Pod 就绪态时使用。

就绪态门控基于 Pod 的 status.conditions 字段的当前值来做决定。 如果 Kubernetes 无法在 status.conditions 字段中找到某状况, 则该状况的状态值默认为 "False"。

这里是一个例子:

kind: Pod
...
spec:
  readinessGates:
    - conditionType: "www.example.com/feature-1"
status:
  conditions:
    - type: Ready                              # 内置的 Pod 状况
      status: "False"
      lastProbeTime: null
      lastTransitionTime: 2018-01-01T00:00:00Z
    - type: "www.example.com/feature-1"        # 额外的 Pod 状况
      status: "False"
      lastProbeTime: null
      lastTransitionTime: 2018-01-01T00:00:00Z
  containerStatuses:
    - containerID: docker://abcd...
      ready: true
...

你所添加的 Pod 状况名称必须满足 Kubernetes 标签键名格式

Pod 就绪态的状态

命令 kubectl patch 不支持修改对象的状态。 如果需要设置 Pod 的 status.conditions,应用或者 Operators 需要使用 PATCH 操作。你可以使用 Kubernetes 客户端库之一来编写代码, 针对 Pod 就绪态设置定制的 Pod 状况。

对于使用定制状况的 Pod 而言,只有当下面的陈述都适用时,该 Pod 才会被评估为就绪:

  • Pod 中所有容器都已就绪;
  • readinessGates 中的所有状况都为 True 值。

当 Pod 的容器都已就绪,但至少一个定制状况没有取值或者取值为 Falsekubelet 将 Pod 的状况设置为 ContainersReady

Pod 网络就绪

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

在 Pod 被调度到某节点后,它需要被 kubelet 接受并且挂载所需的存储卷。 一旦这些阶段完成,Kubelet 将与容器运行时(使用容器运行时接口(Container Runtime Interface;CRI)) 一起为 Pod 生成运行时沙箱并配置网络。如果启用了 PodReadyToStartContainersCondition 特性门控 (Kubernetes 1.30 版本中默认启用), PodReadyToStartContainers 状况会被添加到 Pod 的 status.conditions 字段中。

当 kubelet 检测到 Pod 不具备配置了网络的运行时沙箱时,PodReadyToStartContainers 状况将被设置为 False。以下场景中将会发生这种状况:

  • 在 Pod 生命周期的早期阶段,kubelet 还没有开始使用容器运行时为 Pod 设置沙箱时。
  • 在 Pod 生命周期的末期阶段,Pod 的沙箱由于以下原因被销毁时:
    • 节点重启时 Pod 没有被驱逐
    • 对于使用虚拟机进行隔离的容器运行时,Pod 沙箱虚拟机重启时,需要创建一个新的沙箱和全新的容器网络配置。

在运行时插件成功完成 Pod 的沙箱创建和网络配置后, kubelet 会将 PodReadyToStartContainers 状况设置为 True。 当 PodReadyToStartContainers 状况设置为 True 后, Kubelet 可以开始拉取容器镜像和创建容器。

对于带有 Init 容器的 Pod,kubelet 会在 Init 容器成功完成后将 Initialized 状况设置为 True (这发生在运行时成功创建沙箱和配置网络之后), 对于没有 Init 容器的 Pod,kubelet 会在创建沙箱和网络配置开始之前将 Initialized 状况设置为 True

Pod 调度就绪态

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

有关详细信息,请参阅 Pod 调度就绪态

容器探针

probe 是由 kubelet 对容器执行的定期诊断。 要执行诊断,kubelet 既可以在容器内执行代码,也可以发出一个网络请求。

检查机制

使用探针来检查容器有四种不同的方法。 每个探针都必须准确定义为这四种机制中的一种:

exec
在容器内执行指定命令。如果命令退出时返回码为 0 则认为诊断成功。
grpc
使用 gRPC 执行一个远程过程调用。 目标应该实现 gRPC 健康检查。 如果响应的状态是 "SERVING",则认为诊断成功。
httpGet
对容器的 IP 地址上指定端口和路径执行 HTTP GET 请求。如果响应的状态码大于等于 200 且小于 400,则诊断被认为是成功的。
tcpSocket
对容器的 IP 地址上的指定端口执行 TCP 检查。如果端口打开,则诊断被认为是成功的。 如果远程系统(容器)在打开连接后立即将其关闭,这算作是健康的。

探测结果

每次探测都将获得以下三种结果之一:

Success(成功)
容器通过了诊断。
Failure(失败)
容器未通过诊断。
Unknown(未知)
诊断失败,因此不会采取任何行动。

探测类型

针对运行中的容器,kubelet 可以选择是否执行以下三种探针,以及如何针对探测结果作出反应:

livenessProbe
指示容器是否正在运行。如果存活态探测失败,则 kubelet 会杀死容器, 并且容器将根据其重启策略决定未来。如果容器不提供存活探针, 则默认状态为 Success
readinessProbe
指示容器是否准备好为请求提供服务。如果就绪态探测失败, 端点控制器将从与 Pod 匹配的所有服务的端点列表中删除该 Pod 的 IP 地址。 初始延迟之前的就绪态的状态值默认为 Failure。 如果容器不提供就绪态探针,则默认状态为 Success
startupProbe
指示容器中的应用是否已经启动。如果提供了启动探针,则所有其他探针都会被 禁用,直到此探针成功为止。如果启动探测失败,kubelet 将杀死容器, 而容器依其重启策略进行重启。 如果容器没有提供启动探测,则默认状态为 Success

如欲了解如何设置存活态、就绪态和启动探针的进一步细节, 可以参阅配置存活态、就绪态和启动探针

何时该使用存活态探针?

如果容器中的进程能够在遇到问题或不健康的情况下自行崩溃,则不一定需要存活态探针; kubelet 将根据 Pod 的 restartPolicy 自动执行修复操作。

如果你希望容器在探测失败时被杀死并重新启动,那么请指定一个存活态探针, 并指定 restartPolicy 为 "Always" 或 "OnFailure"。

何时该使用就绪态探针?

如果要仅在探测成功时才开始向 Pod 发送请求流量,请指定就绪态探针。 在这种情况下,就绪态探针可能与存活态探针相同,但是规约中的就绪态探针的存在意味着 Pod 将在启动阶段不接收任何数据,并且只有在探针探测成功后才开始接收数据。

如果你希望容器能够自行进入维护状态,也可以指定一个就绪态探针, 检查某个特定于就绪态的因此不同于存活态探测的端点。

如果你的应用程序对后端服务有严格的依赖性,你可以同时实现存活态和就绪态探针。 当应用程序本身是健康的,存活态探针检测通过后,就绪态探针会额外检查每个所需的后端服务是否可用。 这可以帮助你避免将流量导向只能返回错误信息的 Pod。

如果你的容器需要在启动期间加载大型数据、配置文件或执行迁移, 你可以使用启动探针。 然而,如果你想区分已经失败的应用和仍在处理其启动数据的应用,你可能更倾向于使用就绪探针。

何时该使用启动探针?

对于所包含的容器需要较长时间才能启动就绪的 Pod 而言,启动探针是有用的。 你不再需要配置一个较长的存活态探测时间间隔,只需要设置另一个独立的配置选定, 对启动期间的容器执行探测,从而允许使用远远超出存活态时间间隔所允许的时长。

如果你的容器启动时间通常超出 initialDelaySeconds + failureThreshold × periodSeconds 总值,你应该设置一个启动探测,对存活态探针所使用的同一端点执行检查。 periodSeconds 的默认值是 10 秒。你应该将其 failureThreshold 设置得足够高, 以便容器有充足的时间完成启动,并且避免更改存活态探针所使用的默认值。 这一设置有助于减少死锁状况的发生。

Pod 的终止

由于 Pod 所代表的是在集群中节点上运行的进程,当不再需要这些进程时允许其体面地终止是很重要的。 一般不应武断地使用 KILL 信号终止它们,导致这些进程没有机会完成清理操作。

设计的目标是令你能够请求删除进程,并且知道进程何时被终止,同时也能够确保删除操作终将完成。 当你请求删除某个 Pod 时,集群会记录并跟踪 Pod 的体面终止周期, 而不是直接强制地杀死 Pod。在存在强制关闭设施的前提下, kubelet 会尝试体面地终止 Pod。

通常 Pod 体面终止的过程为:kubelet 先发送一个带有体面超时限期的 TERM(又名 SIGTERM) 信号到每个容器中的主进程,将请求发送到容器运行时来尝试停止 Pod 中的容器。 停止容器的这些请求由容器运行时以异步方式处理。 这些请求的处理顺序无法被保证。许多容器运行时遵循容器镜像内定义的 STOPSIGNAL 值, 如果不同,则发送容器镜像中配置的 STOPSIGNAL,而不是 TERM 信号。 一旦超出了体面终止限期,容器运行时会向所有剩余进程发送 KILL 信号,之后 Pod 就会被从 API 服务器上移除。 如果 kubelet 或者容器运行时的管理服务在等待进程终止期间被重启, 集群会从头开始重试,赋予 Pod 完整的体面终止限期。

下面是一个例子:

  1. 你使用 kubectl 工具手动删除某个特定的 Pod,而该 Pod 的体面终止限期是默认值(30 秒)。

  2. API 服务器中的 Pod 对象被更新,记录涵盖体面终止限期在内 Pod 的最终死期,超出所计算时间点则认为 Pod 已死(dead)。 如果你使用 kubectl describe 来查验你正在删除的 Pod,该 Pod 会显示为 "Terminating" (正在终止)。 在 Pod 运行所在的节点上:kubelet 一旦看到 Pod 被标记为正在终止(已经设置了体面终止限期),kubelet 即开始本地的 Pod 关闭过程。

    1. 如果 Pod 中的容器之一定义了 preStop 回调kubelet 开始在容器内运行该回调逻辑。如果超出体面终止限期时, preStop 回调逻辑仍在运行,kubelet 会请求给予该 Pod 的宽限期一次性增加 2 秒钟。

      如果 preStop 回调在体面期结束后仍在运行,kubelet 将请求短暂的、一次性的体面期延长 2 秒。

    1. kubelet 接下来触发容器运行时发送 TERM 信号给每个容器中的进程 1。

  1. kubelet 启动 Pod 的体面关闭逻辑的同时,控制平面会评估是否将关闭的 Pod 从对应的 EndpointSlice(和端点)对象中移除,过滤条件是 Pod 被对应的服务以某 选择算符选定。 ReplicaSet 和其他工作负载资源不再将关闭进程中的 Pod 视为合法的、能够提供服务的副本。

    关闭动作很慢的 Pod 不应继续处理常规服务请求,而应开始终止并完成对打开的连接的处理。 一些应用程序不仅需要完成对打开的连接的处理,还需要更进一步的体面终止逻辑 - 比如:排空和完成会话。

    任何正在终止的 Pod 所对应的端点都不会立即从 EndpointSlice 中被删除,EndpointSlice API(以及传统的 Endpoints API)会公开一个状态来指示其处于 终止状态。 正在终止的端点始终将其 ready 状态设置为 false(为了向后兼容 1.26 之前的版本), 因此负载均衡器不会将其用于常规流量。

    如果需要排空正被终止的 Pod 上的流量,可以将 serving 状况作为实际的就绪状态。你可以在教程 探索 Pod 及其端点的终止行为 中找到有关如何实现连接排空的更多详细信息。

  1. 超出终止宽限期限时,kubelet 会触发强制关闭过程。容器运行时会向 Pod 中所有容器内仍在运行的进程发送 SIGKILL 信号。 kubelet 也会清理隐藏的 pause 容器,如果容器运行时使用了这种容器的话。

  2. kubelet 将 Pod 转换到终止阶段(FailedSucceeded 具体取决于其容器的结束状态)。 这一步从 1.27 版本开始得到保证。

  3. kubelet 触发强制从 API 服务器上删除 Pod 对象的逻辑,并将体面终止限期设置为 0 (这意味着马上删除)。

  4. API 服务器删除 Pod 的 API 对象,从任何客户端都无法再看到该对象。

强制终止 Pod

默认情况下,所有的删除操作都会附有 30 秒钟的宽限期限。 kubectl delete 命令支持 --grace-period=<seconds> 选项,允许你重载默认值, 设定自己希望的期限值。

将宽限期限强制设置为 0 意味着立即从 API 服务器删除 Pod。 如果 Pod 仍然运行于某节点上,强制删除操作会触发 kubelet 立即执行清理操作。

执行强制删除操作时,API 服务器不再等待来自 kubelet 的、关于 Pod 已经在原来运行的节点上终止执行的确认消息。 API 服务器直接删除 Pod 对象,这样新的与之同名的 Pod 即可以被创建。 在节点侧,被设置为立即终止的 Pod 仍然会在被强行杀死之前获得一点点的宽限时间。

如果你需要强制删除 StatefulSet 的 Pod, 请参阅从 StatefulSet 中删除 Pod 的任务文档。

Pod 的垃圾收集

对于已失败的 Pod 而言,对应的 API 对象仍然会保留在集群的 API 服务器上, 直到用户或者控制器进程显式地将其删除。

Pod 的垃圾收集器(PodGC)是控制平面的控制器,它会在 Pod 个数超出所配置的阈值 (根据 kube-controller-managerterminated-pod-gc-threshold 设置)时删除已终止的 Pod(阶段值为 SucceededFailed)。 这一行为会避免随着时间演进不断创建和终止 Pod 而引起的资源泄露问题。

此外,PodGC 会清理满足以下任一条件的所有 Pod:

  1. 孤儿 Pod - 绑定到不再存在的节点,
  2. 计划外终止的 Pod
  3. 终止过程中的 Pod,当启用 NodeOutOfServiceVolumeDetach 特性门控时, 绑定到有 node.kubernetes.io/out-of-service 污点的未就绪节点。

若启用 PodDisruptionConditions 特性门控,在清理 Pod 的同时, 如果它们处于非终止状态阶段,PodGC 也会将它们标记为失败。 此外,PodGC 在清理孤儿 Pod 时会添加 Pod 干扰状况。参阅 Pod 干扰状况 了解更多详情。

接下来

3.4.1.2 - Init 容器

本页提供了 Init 容器的概览。Init 容器是一种特殊容器,在 Pod 内的应用容器启动之前运行。Init 容器可以包括一些应用镜像中不存在的实用工具和安装脚本。

你可以在 Pod 的规约中与用来描述应用容器的 containers 数组平行的位置指定 Init 容器。

在 Kubernetes 中,边车容器 是在主应用容器之前启动并持续运行的容器。本文介绍 Init 容器:在 Pod 初始化期间完成运行的容器。

理解 Init 容器

每个 Pod 中可以包含多个容器, 应用运行在这些容器里面,同时 Pod 也可以有一个或多个先于应用容器启动的 Init 容器。

Init 容器与普通的容器非常像,除了如下两点:

  • 它们总是运行到完成。
  • 每个都必须在下一个启动之前成功完成。

如果 Pod 的 Init 容器失败,kubelet 会不断地重启该 Init 容器直到该容器成功为止。 然而,如果 Pod 对应的 restartPolicy 值为 "Never",并且 Pod 的 Init 容器失败, 则 Kubernetes 会将整个 Pod 状态设置为失败。

为 Pod 设置 Init 容器需要在 Pod 规约中添加 initContainers 字段, 该字段以 Container 类型对象数组的形式组织,和应用的 containers 数组同级相邻。 参阅 API 参考的容器章节了解详情。

Init 容器的状态在 status.initContainerStatuses 字段中以容器状态数组的格式返回 (类似 status.containerStatuses 字段)。

与普通容器的不同之处

Init 容器支持应用容器的全部字段和特性,包括资源限制、 数据卷和安全设置。 然而,Init 容器对资源请求和限制的处理稍有不同, 在下面容器内的资源共享节有说明。

常规的 Init 容器(即不包括边车容器)不支持 lifecyclelivenessProbereadinessProbestartupProbe 字段。Init 容器必须在 Pod 准备就绪之前完成运行;而边车容器在 Pod 的生命周期内继续运行, 它支持一些探针。有关边车容器的细节请参阅边车容器

如果为一个 Pod 指定了多个 Init 容器,这些容器会按顺序逐个运行。 每个 Init 容器必须运行成功,下一个才能够运行。当所有的 Init 容器运行完成时, Kubernetes 才会为 Pod 初始化应用容器并像平常一样运行。

与边车容器的不同之处

Init 容器在主应用容器启动之前运行并完成其任务。 与边车容器不同, Init 容器不会持续与主容器一起运行。

Init 容器按顺序完成运行,等到所有 Init 容器成功完成之后,主容器才会启动。

Init 容器不支持 lifecyclelivenessProbereadinessProbestartupProbe, 而边车容器支持所有这些探针以控制其生命周期。

Init 容器与主应用容器共享资源(CPU、内存、网络),但不直接与主应用容器进行交互。 不过这些容器可以使用共享卷进行数据交换。

使用 Init 容器

因为 Init 容器具有与应用容器分离的单独镜像,其启动相关代码具有如下优势:

  • Init 容器可以包含一些安装过程中应用容器中不存在的实用工具或个性化代码。 例如,没有必要仅为了在安装过程中使用类似 sedawkpythondig 这样的工具而去 FROM 一个镜像来生成一个新的镜像。

  • 应用镜像的创建者和部署者可以各自独立工作,而没有必要联合构建一个单独的应用镜像。

  • 与同一 Pod 中的多个应用容器相比,Init 容器能以不同的文件系统视图运行。因此,Init 容器可以被赋予访问应用容器不能访问的 Secret 的权限。

  • 由于 Init 容器必须在应用容器启动之前运行完成,因此 Init 容器提供了一种机制来阻塞或延迟应用容器的启动,直到满足了一组先决条件。 一旦前置条件满足,Pod 内的所有的应用容器会并行启动。

  • Init 容器可以安全地运行实用程序或自定义代码,而在其他方式下运行这些实用程序或自定义代码可能会降低应用容器镜像的安全性。 通过将不必要的工具分开,你可以限制应用容器镜像的被攻击范围。

示例

下面是一些如何使用 Init 容器的想法:

  • 等待一个 Service 完成创建,通过类似如下 Shell 命令:

    for i in {1..100}; do sleep 1; if nslookup myservice; then exit 0; fi; done; exit 1
    
  • 注册这个 Pod 到远程服务器,通过在命令中调用 API,类似如下:

    curl -X POST http://$MANAGEMENT_SERVICE_HOST:$MANAGEMENT_SERVICE_PORT/register -d 'instance=$(<POD_NAME>)&ip=$(<POD_IP>)'
    
  • 在启动应用容器之前等一段时间,使用类似命令:

    sleep 60
    
  • 克隆 Git 仓库到中。

  • 将配置值放到配置文件中,运行模板工具为主应用容器动态地生成配置文件。 例如,在配置文件中存放 POD_IP 值,并使用 Jinja 生成主应用配置文件。

使用 Init 容器的情况

下面的例子定义了一个具有 2 个 Init 容器的简单 Pod。 第一个等待 myservice 启动, 第二个等待 mydb 启动。 一旦这两个 Init 容器都启动完成,Pod 将启动 spec 节中的应用容器。

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app.kubernetes.io/name: MyApp
spec:
  containers:
  - name: myapp-container
    image: busybox:1.28
    command: ['sh', '-c', 'echo The app is running! && sleep 3600']
  initContainers:
  - name: init-myservice
    image: busybox:1.28
    command: ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]
  - name: init-mydb
    image: busybox:1.28
    command: ['sh', '-c', "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done"]

你通过运行下面的命令启动 Pod:

kubectl apply -f myapp.yaml

输出类似于:

pod/myapp-pod created

使用下面的命令检查其状态:

kubectl get -f myapp.yaml

输出类似于:

NAME        READY     STATUS     RESTARTS   AGE
myapp-pod   0/1       Init:0/2   0          6m

或者查看更多详细信息:

kubectl describe -f myapp.yaml

输出类似于:

Name:          myapp-pod
Namespace:     default
[...]
Labels:        app.kubernetes.io/name=MyApp
Status:        Pending
[...]
Init Containers:
  init-myservice:
[...]
    State:         Running
[...]
  init-mydb:
[...]
    State:         Waiting
      Reason:      PodInitializing
    Ready:         False
[...]
Containers:
  myapp-container:
[...]
    State:         Waiting
      Reason:      PodInitializing
    Ready:         False
[...]
Events:
  FirstSeen    LastSeen    Count    From                      SubObjectPath                           Type          Reason        Message
  ---------    --------    -----    ----                      -------------                           --------      ------        -------
  16s          16s         1        {default-scheduler }                                              Normal        Scheduled     Successfully assigned myapp-pod to 172.17.4.201
  16s          16s         1        {kubelet 172.17.4.201}    spec.initContainers{init-myservice}     Normal        Pulling       pulling image "busybox"
  13s          13s         1        {kubelet 172.17.4.201}    spec.initContainers{init-myservice}     Normal        Pulled        Successfully pulled image "busybox"
  13s          13s         1        {kubelet 172.17.4.201}    spec.initContainers{init-myservice}     Normal        Created       Created container init-myservice
  13s          13s         1        {kubelet 172.17.4.201}    spec.initContainers{init-myservice}     Normal        Started       Started container init-myservice

如需查看 Pod 内 Init 容器的日志,请执行:

kubectl logs myapp-pod -c init-myservice # 查看第一个 Init 容器
kubectl logs myapp-pod -c init-mydb      # 查看第二个 Init 容器

在这一刻,Init 容器将会等待至发现名称为 mydbmyservice服务

如下为创建这些 Service 的配置文件:

---
apiVersion: v1
kind: Service
metadata:
  name: myservice
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9376
---
apiVersion: v1
kind: Service
metadata:
  name: mydb
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9377

创建 mydbmyservice 服务的命令:

kubectl apply -f services.yaml

输出类似于:

service/myservice created
service/mydb created

这样你将能看到这些 Init 容器执行完毕,随后 my-app 的 Pod 进入 Running 状态:

kubectl get -f myapp.yaml

输出类似于:

NAME        READY     STATUS    RESTARTS   AGE
myapp-pod   1/1       Running   0          9m

这个简单例子应该能为你创建自己的 Init 容器提供一些启发。 接下来节提供了更详细例子的链接。

具体行为

在 Pod 启动过程中,每个 Init 容器会在网络和数据卷初始化之后按顺序启动。 kubelet 运行依据 Init 容器在 Pod 规约中的出现顺序依次运行之。

每个 Init 容器成功退出后才会启动下一个 Init 容器。 如果某容器因为容器运行时的原因无法启动,或以错误状态退出,kubelet 会根据 Pod 的 restartPolicy 策略进行重试。 然而,如果 Pod 的 restartPolicy 设置为 "Always",Init 容器失败时会使用 restartPolicy 的 "OnFailure" 策略。

在所有的 Init 容器没有成功之前,Pod 将不会变成 Ready 状态。 Init 容器的端口将不会在 Service 中进行聚集。正在初始化中的 Pod 处于 Pending 状态, 但会将状况 Initializing 设置为 false。

如果 Pod 重启,所有 Init 容器必须重新执行。

对 Init 容器规约的修改仅限于容器的 image 字段。 更改 Init 容器的 image 字段,等同于重启该 Pod。

因为 Init 容器可能会被重启、重试或者重新执行,所以 Init 容器的代码应该是幂等的。 特别地,基于 emptyDirs 写文件的代码,应该对输出文件可能已经存在做好准备。

Init 容器具有应用容器的所有字段。然而 Kubernetes 禁止使用 readinessProbe, 因为 Init 容器不能定义不同于完成态(Completion)的就绪态(Readiness)。 Kubernetes 会在校验时强制执行此检查。

在 Pod 上使用 activeDeadlineSeconds 和在容器上使用 livenessProbe 可以避免 Init 容器一直重复失败。 activeDeadlineSeconds 时间包含了 Init 容器启动的时间。 但建议仅在团队将其应用程序部署为 Job 时才使用 activeDeadlineSeconds, 因为 activeDeadlineSeconds 在 Init 容器结束后仍有效果。 如果你设置了 activeDeadlineSeconds,已经在正常运行的 Pod 会被杀死。

在 Pod 中的每个应用容器和 Init 容器的名称必须唯一; 与任何其它容器共享同一个名称,会在校验时抛出错误。

容器内的资源共享

在给定的 Init、边车和应用容器执行顺序下,资源使用适用于如下规则:

  • 所有 Init 容器上定义的任何特定资源的 limit 或 request 的最大值,作为 Pod 有效初始 request/limit。 如果任何资源没有指定资源限制,这被视为最高限制。
  • Pod 对资源的 有效 limit/request 是如下两者中的较大者:
    • 所有应用容器对某个资源的 limit/request 之和
    • 对某个资源的有效初始 limit/request
  • 基于有效 limit/request 完成调度,这意味着 Init 容器能够为初始化过程预留资源, 这些资源在 Pod 生命周期过程中并没有被使用。
  • Pod 的 有效 QoS 层,与 Init 容器和应用容器的一样。

配额和限制适用于有效 Pod 的请求和限制值。

Pod 级别的控制组(Cgroup)是基于 Pod 的有效 request 和 limit,与调度器相同。

Pod 重启的原因

Pod 重启会导致 Init 容器重新执行,主要有如下几个原因:

  • Pod 的基础设施容器 (译者注:如 pause 容器) 被重启。这种情况不多见, 必须由具备 root 权限访问节点的人员来完成。

  • restartPolicy 设置为 Always,Pod 中所有容器会终止而强制重启。 由于垃圾回收机制的原因, Init 容器的完成记录将会丢失。

当 Init 容器的镜像发生改变或者 Init 容器的完成记录因为垃圾收集等原因被丢失时, Pod 不会被重启。这一行为适用于 Kubernetes v1.20 及更新版本。 如果你在使用较早版本的 Kubernetes,可查阅你所使用的版本对应的文档。

接下来

进一步了解以下内容:

3.4.1.3 - 边车容器

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

边车容器是与主应用容器在同一个 Pod 中运行的辅助容器。 这些容器通过提供额外的服务或功能(如日志记录、监控、安全性或数据同步)来增强或扩展主应用容器的功能, 而无需直接修改主应用代码。

启用边车容器

Kubernetes 1.29 默认启用,一个名为 SidecarContainers特性门控允许你为 Pod 的 initContainers 字段中列出的容器指定 restartPolicy。这些可重启的边车容器与同一 Pod 内的其他 Init 容器及主应用容器相互独立。 边车容器可以在不影响主应用容器和其他 Init 容器的情况下启动、停止或重启。

边车容器和 Pod 生命周期

如果创建 Init 容器时将 restartPolicy 设置为 Always, 则它将在整个 Pod 的生命周期内启动并持续运行。这对于运行与主应用容器分离的支持服务非常有帮助。

如果为此 Init 容器指定了 readinessProbe,其结果将用于确定 Pod 的 ready 状态。

由于这些容器被定义为 Init 容器,所以它们享有与其他 Init 容器相同的顺序和按序执行保证, 可以将它们与其他 Init 容器混合在一起,形成复杂的 Pod 初始化流程。

与常规 Init 容器相比,在 initContainers 中定义的边车容器在启动后继续运行。 当 Pod 的 .spec.initContainers 中有多个条目时,这一点非常重要。 在边车风格的 Init 容器运行后(kubelet 将该 Init 容器的 started 状态设置为 true), kubelet 启动 .spec.initContainers 这一有序列表中的下一个 Init 容器。 该状态要么因为容器中有一个正在运行的进程且没有定义启动探针而变为 true, 要么是其 startupProbe 成功而返回的结果。

以下是一个具有两个容器的 Deployment 示例,其中一个是边车:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  labels:
    app: myapp
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
        - name: myapp
          image: alpine:latest
          command: ['sh', '-c', 'echo "logging" > /opt/logs.txt']
          volumeMounts:
            - name: data
              mountPath: /opt
      initContainers:
        - name: logshipper
          image: alpine:latest
          restartPolicy: Always
          command: ['sh', '-c', 'tail /opt/logs.txt']
          volumeMounts:
            - name: data
              mountPath: /opt
  volumes:
    - name: data
      emptyDir: {}

此特性也适用于带有边车的 Job,因为边车容器在主容器完成后不会阻止 Job 的完成。

以下是一个具有两个容器的 Job 示例,其中一个是边车:

apiVersion: batch/v1
kind: Job
metadata:
  name: myjob
spec:
  template:
    spec:
      containers:
        - name: myjob
          image: alpine:latest
          command: ['sh', '-c', 'echo "logging" > /opt/logs.txt']
          volumeMounts:
            - name: data
              mountPath: /opt
      initContainers:
        - name: logshipper
          image: alpine:latest
          restartPolicy: Always
          command: ['sh', '-c', 'tail /opt/logs.txt']
          volumeMounts:
            - name: data
              mountPath: /opt
      restartPolicy: Never
      volumes:
        - name: data
          emptyDir: {}

与常规容器的区别

边车容器与同一 Pod 中的常规容器并行运行。不过边车容器不执行主应用逻辑,而是为主应用提供支持功能。

边车容器具有独立的生命周期。它们可以独立于常规容器启动、停止和重启。 这意味着你可以更新、扩展或维护边车容器,而不影响主应用。

边车容器与主容器共享相同的网络和存储命名空间。这种共存使它们能够紧密交互并共享资源。

与 Init 容器的区别

边车容器与主容器并行工作,扩展其功能并提供附加服务。

边车容器与主应用容器同时运行。它们在整个 Pod 的生命周期中都处于活动状态,并且可以独立于主容器启动和停止。 与 Init 容器不同, 边车容器支持探针来控制其生命周期。

这些边车容器可以直接与主应用容器进行交互,共享相同的网络命名空间、文件系统和环境变量。 所有这些容器紧密合作,提供额外的功能。

容器内的资源共享

假如执行顺序为 Init 容器、边车容器和应用容器,则关于资源用量适用以下规则:

  • 所有 Init 容器上定义的任何特定资源的 limit 或 request 的最大值,作为 Pod 有效初始 request/limit。 如果任何资源没有指定资源限制,则被视为最高限制。
  • Pod 对资源的 有效 limit/request 是如下两者中的较大者:
    • 所有应用容器对某个资源的 limit/request 之和
    • Init 容器中对某个资源的有效 limit/request
  • 系统基于有效的 limit/request 完成调度,这意味着 Init 容器能够为初始化过程预留资源, 而这些资源在 Pod 的生命周期中不再被使用。
  • Pod 的 有效 QoS 级别,对于 Init 容器和应用容器而言是相同的。

配额和限制适用于 Pod 的有效请求和限制值。

Pod 级别的 cgroup 是基于 Pod 的有效请求和限制值,与调度器相同。

接下来

3.4.1.4 - 干扰(Disruptions)

本指南针对的是希望构建高可用性应用的应用所有者,他们有必要了解可能发生在 Pod 上的干扰类型。

文档同样适用于想要执行自动化集群操作(例如升级和自动扩展集群)的集群管理员。

自愿干扰和非自愿干扰

Pod 不会消失,除非有人(用户或控制器)将其销毁,或者出现了不可避免的硬件或软件系统错误。

我们把这些不可避免的情况称为应用的非自愿干扰(Involuntary Disruptions)。例如:

  • 节点下层物理机的硬件故障
  • 集群管理员错误地删除虚拟机(实例)
  • 云提供商或虚拟机管理程序中的故障导致的虚拟机消失
  • 内核错误
  • 节点由于集群网络隔离从集群中消失
  • 由于节点资源不足导致 pod 被驱逐。

除了资源不足的情况,大多数用户应该都熟悉这些情况;它们不是特定于 Kubernetes 的。

我们称其他情况为自愿干扰(Voluntary Disruptions)。 包括由应用所有者发起的操作和由集群管理员发起的操作。 典型的应用所有者的操作包括:

  • 删除 Deployment 或其他管理 Pod 的控制器
  • 更新了 Deployment 的 Pod 模板导致 Pod 重启
  • 直接删除 Pod(例如,因为误操作)

集群管理员操作包括:

这些操作可能由集群管理员直接执行,也可能由集群管理员所使用的自动化工具执行,或者由集群托管提供商自动执行。

咨询集群管理员或联系云提供商,或者查询发布文档,以确定是否为集群启用了任何资源干扰源。 如果没有启用,可以不用创建 Pod Disruption Budgets(Pod 干扰预算)

处理干扰

以下是减轻非自愿干扰的一些方法:

  • 确保 Pod 在请求中给出所需资源
  • 如果需要更高的可用性,请复制应用。 (了解有关运行多副本的无状态有状态应用的信息。)
  • 为了在运行复制应用时获得更高的可用性,请跨机架(使用 反亲和性) 或跨区域(如果使用多区域集群)扩展应用。

自愿干扰的频率各不相同。在一个基本的 Kubernetes 集群中,没有自愿干扰(只有用户触发的干扰)。 然而,集群管理员或托管提供商可能运行一些可能导致自愿干扰的额外服务。例如,节点软 更新可能导致自愿干扰。另外,集群(节点)自动缩放的某些 实现可能导致碎片整理和紧缩节点的自愿干扰。集群 管理员或托管提供商应该已经记录了各级别的自愿干扰(如果有的话)。 有些配置选项,例如在 pod spec 中 使用 PriorityClasses 也会产生自愿(和非自愿)的干扰。

干扰预算

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

即使你会经常引入自愿性干扰,Kubernetes 提供的功能也能够支持你运行高度可用的应用。

作为一个应用的所有者,你可以为每个应用创建一个 PodDisruptionBudget(PDB)。 PDB 将限制在同一时间因自愿干扰导致的多副本应用中发生宕机的 Pod 数量。 例如,基于票选机制的应用希望确保运行中的副本数永远不会低于票选所需的数量。 Web 前端可能希望确保提供负载的副本数量永远不会低于总数的某个百分比。

集群管理员和托管提供商应该使用遵循 PodDisruptionBudgets 的接口 (通过调用Eviction API), 而不是直接删除 Pod 或 Deployment。

例如,kubectl drain 命令可以用来标记某个节点即将停止服务。 运行 kubectl drain 命令时,工具会尝试驱逐你所停服的节点上的所有 Pod。 kubectl 代表你所提交的驱逐请求可能会暂时被拒绝, 所以该工具会周期性地重试所有失败的请求, 直到目标节点上的所有的 Pod 都被终止,或者达到配置的超时时间。

PDB 指定应用可以容忍的副本数量(相当于应该有多少副本)。 例如,具有 .spec.replicas: 5 的 Deployment 在任何时间都应该有 5 个 Pod。 如果 PDB 允许其在某一时刻有 4 个副本,那么驱逐 API 将允许同一时刻仅有一个(而不是两个)Pod 自愿干扰。

使用标签选择器来指定构成应用的一组 Pod,这与应用的控制器(Deployment、StatefulSet 等) 选择 Pod 的逻辑一样。

Pod 的“预期”数量由管理这些 Pod 的工作负载资源的 .spec.replicas 参数计算出来的。 控制平面通过检查 Pod 的 .metadata.ownerReferences 来发现关联的工作负载资源。

PDB 无法防止非自愿干扰; 但它们确实计入预算。

由于应用的滚动升级而被删除或不可用的 Pod 确实会计入干扰预算, 但是工作负载资源(如 Deployment 和 StatefulSet) 在进行滚动升级时不受 PDB 的限制。 应用更新期间的故障处理方式是在对应的工作负载资源的 spec 中配置的。

建议在你的 PodDisruptionBudget 中将 不健康 Pod 驱逐策略 设置为 AlwaysAllow 以支持在节点腾空期间驱逐行为不当的应用程序。 默认行为是等待应用程序 Pod 变得 健康,然后才能继续执行腾空。

当使用驱逐 API 驱逐 Pod 时,Pod 会被体面地 终止,期间会 参考 PodSpec 中的 terminationGracePeriodSeconds 配置值。

PodDisruptionBudget 例子

假设集群有 3 个节点,node-1node-3。集群上运行了一些应用。 其中一个应用有 3 个副本,分别是 pod-apod-bpod-c。 另外,还有一个不带 PDB 的无关 pod pod-x 也同样显示出来。 最初,所有的 Pod 分布如下:

node-1node-2node-3
pod-a availablepod-b availablepod-c available
pod-x available

3 个 Pod 都是 deployment 的一部分,并且共同拥有同一个 PDB,要求 3 个 Pod 中至少有 2 个 Pod 始终处于可用状态。

例如,假设集群管理员想要重启系统,升级内核版本来修复内核中的缺陷。 集群管理员首先使用 kubectl drain 命令尝试腾空 node-1 节点。 命令尝试驱逐 pod-apod-x。操作立即就成功了。 两个 Pod 同时进入 terminating 状态。这时的集群处于下面的状态:

node-1 drainingnode-2node-3
pod-a terminatingpod-b availablepod-c available
pod-x terminating

Deployment 控制器观察到其中一个 Pod 正在终止,因此它创建了一个替代 Pod pod-d。 由于 node-1 被封锁(cordon),pod-d 落在另一个节点上。 同样其他控制器也创建了 pod-y 作为 pod-x 的替代品。

(注意:对于 StatefulSet 来说,pod-a(也称为 pod-0)需要在替换 Pod 创建之前完全终止, 替代它的也称为 pod-0,但是具有不同的 UID。除此之外,此示例也适用于 StatefulSet。)

当前集群的状态如下:

node-1 drainingnode-2node-3
pod-a terminatingpod-b availablepod-c available
pod-x terminatingpod-d startingpod-y

在某一时刻,Pod 被终止,集群如下所示:

node-1 drainednode-2node-3
pod-b availablepod-c available
pod-d startingpod-y

此时,如果一个急躁的集群管理员试图排空(drain)node-2node-3,drain 命令将被阻塞, 因为对于 Deployment 来说只有 2 个可用的 Pod,并且它的 PDB 至少需要 2 个。 经过一段时间,pod-d 变得可用。

集群状态如下所示:

node-1 drainednode-2node-3
pod-b availablepod-c available
pod-d availablepod-y

现在,集群管理员试图排空(drain)node-2。 drain 命令将尝试按照某种顺序驱逐两个 Pod,假设先是 pod-b,然后是 pod-d。 命令成功驱逐 pod-b,但是当它尝试驱逐 pod-d时将被拒绝,因为对于 Deployment 来说只剩一个可用的 Pod 了。

Deployment 创建 pod-b 的替代 Pod pod-e。 因为集群中没有足够的资源来调度 pod-e,drain 命令再次阻塞。集群最终将是下面这种状态:

node-1 drainednode-2node-3no node
pod-b terminatingpod-c availablepod-e pending
pod-d availablepod-y

此时,集群管理员需要增加一个节点到集群中以继续升级操作。

可以看到 Kubernetes 如何改变干扰发生的速率,根据:

  • 应用需要多少个副本
  • 优雅关闭应用实例需要多长时间
  • 启动应用新实例需要多长时间
  • 控制器的类型
  • 集群的资源能力

Pod 干扰状况

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

启用后,会给 Pod 添加一个 DisruptionTarget 状况, 用来表明该 Pod 因为发生干扰而被删除。 状况中的 reason 字段进一步给出 Pod 终止的原因,如下:

PreemptionByScheduler
Pod 将被调度器抢占, 目的是接受优先级更高的新 Pod。 要了解更多的相关信息,请参阅 Pod 优先级和抢占
DeletionByTaintManager
由于 Pod 不能容忍 NoExecute 污点,Pod 将被 Taint Manager(kube-controller-manager 中节点生命周期控制器的一部分)删除; 请参阅基于污点的驱逐。
EvictionByEvictionAPI
Pod 已被标记为通过 Kubernetes API 驱逐
DeletionByPodGC
绑定到一个不再存在的 Node 上的 Pod 将被 Pod 垃圾收集删除。
TerminationByKubelet
Pod 由于节点压力驱逐节点体面关闭而被 kubelet 终止。

PodDisruptionConditions 特性门控被启用时,在清理 Pod 的同时,如果这些 Pod 处于非终止阶段, 则 Pod 垃圾回收器 (PodGC) 也会将这些 Pod 标记为失效 (另见 Pod 垃圾回收)。

使用 Job(或 CronJob)时,你可能希望将这些 Pod 干扰状况作为 Job Pod 失效策略的一部分。

分离集群所有者和应用所有者角色

通常,将集群管理者和应用所有者视为彼此了解有限的独立角色是很有用的。这种责任分离在下面这些场景下是有意义的:

  • 当有许多应用团队共用一个 Kubernetes 集群,并且有自然的专业角色
  • 当第三方工具或服务用于集群自动化管理

Pod 干扰预算通过在角色之间提供接口来支持这种分离。

如果你的组织中没有这样的责任分离,则可能不需要使用 Pod 干扰预算。

如何在集群上执行干扰性操作

如果你是集群管理员,并且需要对集群中的所有节点执行干扰操作,例如节点或系统软件升级,则可以使用以下选项

  • 接受升级期间的停机时间。
  • 故障转移到另一个完整的副本集群。
    • 没有停机时间,但是对于重复的节点和人工协调成本可能是昂贵的。
  • 编写可容忍干扰的应用和使用 PDB。
    • 不停机。
    • 最小的资源重复。
    • 允许更多的集群管理自动化。
    • 编写可容忍干扰的应用是棘手的,但对于支持容忍自愿干扰所做的工作,和支持自动扩缩和容忍非 自愿干扰所做工作相比,有大量的重叠

接下来

3.4.1.5 - 临时容器

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

本页面概述了临时容器:一种特殊的容器,该容器在现有 Pod 中临时运行,以便完成用户发起的操作,例如故障排查。 你会使用临时容器来检查服务,而不是用它来构建应用程序。

了解临时容器

Pod 是 Kubernetes 应用程序的基本构建块。 由于 Pod 是一次性且可替换的,因此一旦 Pod 创建,就无法将容器加入到 Pod 中。 取而代之的是,通常使用 Deployment 以受控的方式来删除并替换 Pod。

有时有必要检查现有 Pod 的状态。例如,对于难以复现的故障进行排查。 在这些场景中,可以在现有 Pod 中运行临时容器来检查其状态并运行任意命令。

什么是临时容器?

临时容器与其他容器的不同之处在于,它们缺少对资源或执行的保证,并且永远不会自动重启, 因此不适用于构建应用程序。 临时容器使用与常规容器相同的 ContainerSpec 节来描述,但许多字段是不兼容和不允许的。

  • 临时容器没有端口配置,因此像 portslivenessProbereadinessProbe 这样的字段是不允许的。
  • Pod 资源分配是不可变的,因此 resources 配置是不允许的。
  • 有关允许字段的完整列表,请参见 EphemeralContainer 参考文档

临时容器是使用 API 中的一种特殊的 ephemeralcontainers 处理器进行创建的, 而不是直接添加到 pod.spec 段,因此无法使用 kubectl edit 来添加一个临时容器。

与常规容器一样,将临时容器添加到 Pod 后,将不能更改或删除临时容器。

临时容器的用途

当由于容器崩溃或容器镜像不包含调试工具而导致 kubectl exec 无用时, 临时容器对于交互式故障排查很有用。

尤其是,Distroless 镜像 允许用户部署最小的容器镜像,从而减少攻击面并减少故障和漏洞的暴露。 由于 distroless 镜像不包含 Shell 或任何的调试工具,因此很难单独使用 kubectl exec 命令进行故障排查。

使用临时容器时, 启用进程名字空间共享很有帮助, 可以查看其他容器中的进程。

接下来

3.4.1.6 - Pod QoS 类

本页介绍 Kubernetes 中的 服务质量(Quality of Service,QoS) 类, 阐述 Kubernetes 如何根据为 Pod 中的容器指定的资源约束为每个 Pod 设置 QoS 类。 Kubernetes 依赖这种分类来决定当 Node 上没有足够可用资源时要驱逐哪些 Pod。

QoS 类

Kubernetes 对你运行的 Pod 进行分类,并将每个 Pod 分配到特定的 QoS 类中。 Kubernetes 使用这种分类来影响不同 Pod 被处理的方式。Kubernetes 基于 Pod 中容器资源请求进行分类, 同时确定这些请求如何与资源限制相关。 这称为服务质量 (QoS) 类。 Kubernetes 基于每个 Pod 中容器的资源请求和限制为 Pod 设置 QoS 类。Kubernetes 使用 QoS 类来决定从遇到节点压力的 Node 中驱逐哪些 Pod。可选的 QoS 类有 GuaranteedBurstableBestEffort。 当一个 Node 耗尽资源时,Kubernetes 将首先驱逐在该 Node 上运行的 BestEffort Pod, 然后是 Burstable Pod,最后是 Guaranteed Pod。当这种驱逐是由于资源压力时, 只有超出资源请求的 Pod 才是被驱逐的候选对象。

Guaranteed

Guaranteed Pod 具有最严格的资源限制,并且最不可能面临驱逐。 在这些 Pod 超过其自身的限制或者没有可以从 Node 抢占的低优先级 Pod 之前, 这些 Pod 保证不会被杀死。这些 Pod 不可以获得超出其指定 limit 的资源。这些 Pod 也可以使用 static CPU 管理策略来使用独占的 CPU。

判据

Pod 被赋予 Guaranteed QoS 类的几个判据:

  • Pod 中的每个容器必须有内存 limit 和内存 request。
  • 对于 Pod 中的每个容器,内存 limit 必须等于内存 request。
  • Pod 中的每个容器必须有 CPU limit 和 CPU request。
  • 对于 Pod 中的每个容器,CPU limit 必须等于 CPU request。

Burstable

Burstable Pod 有一些基于 request 的资源下限保证,但不需要特定的 limit。 如果未指定 limit,则默认为其 limit 等于 Node 容量,这允许 Pod 在资源可用时灵活地增加其资源。 在由于 Node 资源压力导致 Pod 被驱逐的情况下,只有在所有 BestEffort Pod 被驱逐后 这些 Pod 才会被驱逐。因为 Burstable Pod 可以包括没有资源 limit 或资源 request 的容器, 所以 Burstable Pod 可以尝试使用任意数量的节点资源。

判据

Pod 被赋予 Burstable QoS 类的几个判据:

  • Pod 不满足针对 QoS 类 Guaranteed 的判据。
  • Pod 中至少一个容器有内存或 CPU 的 request 或 limit。

BestEffort

BestEffort QoS 类中的 Pod 可以使用未专门分配给其他 QoS 类中的 Pod 的节点资源。 例如若你有一个节点有 16 核 CPU 可供 kubelet 使用,并且你将 4 核 CPU 分配给一个 Guaranteed Pod, 那么 BestEffort QoS 类中的 Pod 可以尝试任意使用剩余的 12 核 CPU。

如果节点遇到资源压力,kubelet 将优先驱逐 BestEffort Pod。

判据

如果 Pod 不满足 GuaranteedBurstable 的判据,则它的 QoS 类为 BestEffort。 换言之,只有当 Pod 中的所有容器没有内存 limit 或内存 request,也没有 CPU limit 或 CPU request 时,Pod 才是 BestEffort。Pod 中的容器可以请求(除 CPU 或内存之外的) 其他资源并且仍然被归类为 BestEffort

使用 cgroup v2 的内存 QoS

特性状态: Kubernetes v1.30 []

内存 QoS 使用 cgroup v2 的内存控制器来保证 Kubernetes 中的内存资源。 Pod 中容器的内存请求和限制用于设置由内存控制器所提供的特定接口 memory.minmemory.high。 当 memory.min 被设置为内存请求时,内存资源被保留并且永远不会被内核回收; 这就是内存 QoS 确保 Kubernetes Pod 的内存可用性的方式。而如果容器中设置了内存限制, 这意味着系统需要限制容器内存的使用;内存 QoS 使用 memory.high 来限制接近其内存限制的工作负载, 确保系统不会因瞬时内存分配而不堪重负。

内存 QoS 依赖于 QoS 类来确定应用哪些设置;它们的机制不同,但都提供对服务质量的控制。

某些行为独立于 QoS 类

某些行为独立于 Kubernetes 分配的 QoS 类。例如:

  • 所有超过资源 limit 的容器都将被 kubelet 杀死并重启,而不会影响该 Pod 中的其他容器。
  • 如果一个容器超出了自身的资源 request,且该容器运行的节点面临资源压力,则该容器所在的 Pod 就会成为被驱逐的候选对象。 如果出现这种情况,Pod 中的所有容器都将被终止。Kubernetes 通常会在不同的节点上创建一个替代的 Pod。
  • Pod 的资源 request 等于其成员容器的资源 request 之和,Pod 的资源 limit 等于其成员容器的资源 limit 之和。
  • kube-scheduler 在选择要抢占的 Pod 时不考虑 QoS 类。当集群没有足够的资源来运行你所定义的所有 Pod 时,就会发生抢占。

接下来

3.4.1.7 - 用户命名空间

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

本页解释了在 Kubernetes Pod 中如何使用用户命名空间。 用户命名空间将容器内运行的用户与主机中的用户隔离开来。

在容器中以 root 身份运行的进程可以在主机中以不同的(非 root)用户身份运行; 换句话说,该进程在用户命名空间内的操作具有完全的权限, 但在命名空间外的操作是无特权的。

你可以使用这个功能来减少被破坏的容器对主机或同一节点中的其他 Pod 的破坏。 有几个安全漏洞被评为 重要, 当用户命名空间处于激活状态时,这些漏洞是无法被利用的。 预计用户命名空间也会减轻一些未来的漏洞。

准备开始

这是一个只对 Linux 有效的功能特性,且需要 Linux 支持在所用文件系统上挂载 idmap。 这意味着:

  • 在节点上,你用于 /var/lib/kubelet/pods/ 的文件系统,或你为此配置的自定义目录, 需要支持 idmap 挂载。
  • Pod 卷中使用的所有文件系统都必须支持 idmap 挂载。

在实践中,这意味着你最低需要 Linux 6.3,因为 tmpfs 在该版本中开始支持 idmap 挂载。 这通常是需要的,因为有几个 Kubernetes 功能特性使用 tmpfs (默认情况下挂载的服务账号令牌使用 tmpfs、Secret 使用 tmpfs 等等)。

Linux 6.3 中支持 idmap 挂载的一些比较流行的文件系统是:btrfs、ext4、xfs、fat、 tmpfs、overlayfs。

此外,需要在容器运行时提供支持, 才能在 Kubernetes Pod 中使用这一功能:

  • CRI-O:1.25(及更高)版本支持配置容器的用户命名空间。

containerd v1.7 与 Kubernetes v1.27 至 v1.30 版本中的用户命名空间不兼容。 Kubernetes v1.25 和 v1.26 使用了早期的实现,在用户命名空间方面与 containerd v1.7 兼容。 如果你使用的 Kubernetes 版本不是 1.30,请查看该版本 Kubernetes 的文档以获取更准确的信息。 如果有比 v1.7 更新的 containerd 版本可供使用,请检查 containerd 文档以获取兼容性信息。

你可以在 GitHub 上的 [issue][CRI-dockerd-issue] 中查看 cri-dockerd 中用户命名空间支持的状态。

介绍

用户命名空间是一个 Linux 功能,允许将容器中的用户映射到主机中的不同用户。 此外,在某用户命名空间中授予 Pod 的权能只在该命名空间中有效,在该命名空间之外无效。

一个 Pod 可以通过将 pod.spec.hostUsers 字段设置为 false 来选择使用用户命名空间。

kubelet 将挑选 Pod 所映射的主机 UID/GID, 并以此保证同一节点上没有两个 Pod 使用相同的方式进行映射。

pod.spec 中的 runAsUserrunAsGroupfsGroup 等字段总是指的是容器内的用户。 启用该功能时,有效的 UID/GID 在 0-65535 范围内。这以限制适用于文件和进程(runAsUserrunAsGroup 等)。

使用这个范围之外的 UID/GID 的文件将被视为属于溢出 ID, 通常是 65534(配置在 /proc/sys/kernel/overflowuid和/proc/sys/kernel/overflowgid)。 然而,即使以 65534 用户/组的身份运行,也不可能修改这些文件。

大多数需要以 root 身份运行但不访问其他主机命名空间或资源的应用程序, 在用户命名空间被启用时,应该可以继续正常运行,不需要做任何改变。

了解 Pod 的用户命名空间

一些容器运行时的默认配置(如 Docker Engine、containerd、CRI-O)使用 Linux 命名空间进行隔离。 其他技术也存在,也可以与这些运行时(例如,Kata Containers 使用虚拟机而不是 Linux 命名空间)结合使用。 本页适用于使用 Linux 命名空间进行隔离的容器运行时。

在创建 Pod 时,默认情况下会使用几个新的命名空间进行隔离: 一个网络命名空间来隔离容器网络,一个 PID 命名空间来隔离进程视图等等。 如果使用了一个用户命名空间,这将把容器中的用户与节点中的用户隔离开来。

这意味着容器可以以 root 身份运行,并将该身份映射到主机上的一个非 root 用户。 在容器内,进程会认为它是以 root 身份运行的(因此像 aptyum 等工具可以正常工作), 而实际上该进程在主机上没有权限。 你可以验证这一点,例如,如果你从主机上执行 ps aux 来检查容器进程是以哪个用户运行的。 ps 显示的用户与你在容器内执行 id 命令时看到的用户是不一样的。

这种抽象限制了可能发生的情况,例如,容器设法逃逸到主机上时的后果。 鉴于容器是作为主机上的一个非特权用户运行的,它能对主机做的事情是有限的。

此外,由于每个 Pod 上的用户将被映射到主机中不同的非重叠用户, 他们对其他 Pod 可以执行的操作也是有限的。

授予一个 Pod 的权能也被限制在 Pod 的用户命名空间内, 并且在这一命名空间之外大多无效,有些甚至完全无效。这里有两个例子:

  • CAP_SYS_MODULE 若被授予一个使用用户命名空间的 Pod 则没有任何效果,这个 Pod 不能加载内核模块。
  • CAP_SYS_ADMIN 只限于 Pod 所在的用户命名空间,在该命名空间之外无效。

在不使用用户命名空间的情况下,以 root 账号运行的容器,在容器逃逸时,在节点上有 root 权限。 而且如果某些权能被授予了某容器,这些权能在宿主机上也是有效的。 当我们使用用户命名空间时,这些都不再成立。

如果你想知道关于使用用户命名空间时的更多变化细节,请参见 man 7 user_namespaces

设置一个节点以支持用户命名空间

建议主机的文件和主机的进程使用 0-65535 范围内的 UID/GID。

kubelet 会把高于这个范围的 UID/GID 分配给 Pod。 因此,为了保证尽可能多的隔离,主机的文件和主机的进程所使用的 UID/GID 应该在 0-65535 范围内。

请注意,这个建议对减轻 CVE-2021-25741 等 CVE 的影响很重要; 在这些 CVE 中,Pod 有可能读取主机中的任意文件。 如果 Pod 和主机的 UID/GID 不重叠,Pod 能够做的事情就会受到限制: Pod 的 UID/GID 不会与主机的文件所有者/组相匹配。

与 Pod 安全准入检查的集成

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

对于启用了用户命名空间的 Linux Pod,Kubernetes 会以受控方式放宽 Pod 安全性标准的应用。 这种行为可以通过特性门控 UserNamespacesPodSecurityStandards 进行控制,可以让最终用户提前尝试此特性。 如果管理员启用此特性门控,必须确保群集中的所有节点都启用了用户命名空间。

如果你启用相关特性门控并创建了使用用户命名空间的 Pod,以下的字段不会被限制, 即使在执行了 BaselineRestricted Pod 安全性标准的上下文中。这种行为不会带来安全问题, 因为带有用户命名空间的 Pod 内的 root 实际上指的是容器内的用户,绝不会映射到主机上的特权用户。 以下是在这种情况下不进行检查的 Pod 字段列表:

  • spec.securityContext.runAsNonRoot
  • spec.containers[*].securityContext.runAsNonRoot
  • spec.initContainers[*].securityContext.runAsNonRoot
  • spec.ephemeralContainers[*].securityContext.runAsNonRoot
  • spec.securityContext.runAsUser
  • spec.containers[*].securityContext.runAsUser
  • spec.initContainers[*].securityContext.runAsUser

限制

当 Pod 使用用户命名空间时,不允许 Pod 使用其他主机命名空间。 特别是,如果你设置了 hostUsers: false,那么你就不可以设置如下属性:

  • hostNetwork: true
  • hostIPC: true
  • hostPID: true

接下来

3.4.1.8 - Downward API

有两种方法可以将 Pod 和容器字段暴露给运行中的容器:环境变量和由特殊卷类型承载的文件。 这两种暴露 Pod 和容器字段的方法统称为 Downward API。

对于容器来说,在不与 Kubernetes 过度耦合的情况下,拥有关于自身的信息有时是很有用的。 Downward API 允许容器在不使用 Kubernetes 客户端或 API 服务器的情况下获得自己或集群的信息。

例如,现有应用程序假设某特定的周知的环境变量是存在的,其中包含唯一标识符。 一种方法是对应用程序进行封装,但这很繁琐且容易出错,并且违背了低耦合的目标。 更好的选择是使用 Pod 名称作为标识符,并将 Pod 名称注入到周知的环境变量中。

在 Kubernetes 中,有两种方法可以将 Pod 和容器字段暴露给运行中的容器:

这两种暴露 Pod 和容器字段的方式统称为 Downward API

可用字段

只有部分 Kubernetes API 字段可以通过 Downward API 使用。本节列出了你可以使用的字段。

你可以使用 fieldRef 传递来自可用的 Pod 级字段的信息。在 API 层面,一个 Pod 的 spec 总是定义了至少一个 Container。 你可以使用 resourceFieldRef 传递来自可用的 Container 级字段的信息。

可通过 fieldRef 获得的信息

对于某些 Pod 级别的字段,你可以将它们作为环境变量或使用 downwardAPI 卷提供给容器。 通过这两种机制可用的字段有:

metadata.name
Pod 的名称
metadata.namespace
Pod 的命名空间
metadata.uid
Pod 的唯一 ID
metadata.annotations['<KEY>']
Pod 的注解 <KEY> 的值(例如:metadata.annotations['myannotation']
metadata.labels['<KEY>']
Pod 的标签 <KEY> 的值(例如:metadata.labels['mylabel']

以下信息可以通过环境变量获得,但不能作为 downwardAPIfieldRef 获得:

spec.serviceAccountName
Pod 的服务账号名称
spec.nodeName
Pod 运行时所处的节点名称
status.hostIP
Pod 所在节点的主 IP 地址
status.hostIPs
这组 IP 地址是 status.hostIP 的双协议栈版本,第一个 IP 始终与 status.hostIP 相同。 该字段在启用了 PodHostIPs 特性门控后可用。
status.podIP
Pod 的主 IP 地址(通常是其 IPv4 地址)
status.podIPs
这组 IP 地址是 status.podIP 的双协议栈版本, 第一个 IP 始终与 status.podIP 相同。

以下信息可以通过 downwardAPIfieldRef 获得,但不能作为环境变量获得:

metadata.labels
Pod 的所有标签,格式为 标签键名="转义后的标签值",每行一个标签
metadata.annotations
Pod 的全部注解,格式为 注解键名="转义后的注解值",每行一个注解

可通过 resourceFieldRef 获得的信息

resource: limits.cpu
容器的 CPU 限制值
resource: requests.cpu
容器的 CPU 请求值
resource: limits.memory
容器的内存限制值
resource: requests.memory
容器的内存请求值
resource: limits.hugepages-*
容器的巨页限制值
resource: requests.hugepages-*
容器的巨页请求值
resource: limits.ephemeral-storage
容器的临时存储的限制值
resource: requests.ephemeral-storage
容器的临时存储的请求值

资源限制的后备信息

如果没有为容器指定 CPU 和内存限制时尝试使用 Downward API 暴露该信息,那么 kubelet 默认会根据 节点可分配资源 计算并暴露 CPU 和内存的最大可分配值。

接下来

你可以阅读有关 downwardAPI的内容。

你可以尝试使用 Downward API 暴露容器或 Pod 级别的信息:

3.4.2 - 工作负载管理

Kubernetes 提供了几个内置的 API 来声明式管理工作负载及其组件。

最终,你的应用以容器的形式在 Pods 中运行; 但是,直接管理单个 Pod 的工作量将会非常繁琐。例如,如果一个 Pod 失败了,你可能希望运行一个新的 Pod 来替换它。Kubernetes 可以为你完成这些操作。

你可以使用 Kubernetes API 创建工作负载对象, 这些对象所表达的是比 Pod 更高级别的抽象概念,Kubernetes 控制平面根据你定义的工作负载对象规约自动管理 Pod 对象。

用于管理工作负载的内置 API 包括:

Deployment (也间接包括 ReplicaSet) 是在集群上运行应用的最常见方式。Deployment 适合在集群上管理无状态应用工作负载, 其中 Deployment 中的任何 Pod 都是可互换的,可以在需要时进行替换。 (Deployment 替代原来的 ReplicationController API)。

StatefulSet 允许你管理一个或多个运行相同应用代码、但具有不同身份标识的 Pod。 StatefulSet 与 Deployment 不同。Deployment 中的 Pod 预期是可互换的。 StatefulSet 最常见的用途是能够建立其 Pod 与其持久化存储之间的关联。 例如,你可以运行一个将每个 Pod 关联到 PersistentVolume 的 StatefulSet。如果该 StatefulSet 中的一个 Pod 失败了,Kubernetes 将创建一个新的 Pod, 并连接到相同的 PersistentVolume。

DaemonSet 定义了在特定节点上提供本地设施的 Pod, 例如允许该节点上的容器访问存储系统的驱动。当必须在合适的节点上运行某种驱动或其他节点级别的服务时, 你可以使用 DaemonSet。DaemonSet 中的每个 Pod 执行类似于经典 Unix / POSIX 服务器上的系统守护进程的角色。DaemonSet 可能对集群的操作至关重要, 例如作为插件让该节点访问集群网络, 也可能帮助你管理节点,或者提供增强正在运行的容器平台所需的、不太重要的设施。 你可以在集群的每个节点上运行 DaemonSets(及其 Pod),或者仅在某个子集上运行 (例如,只在安装了 GPU 的节点上安装 GPU 加速驱动)。

你可以使用 Job 和/或 CronJob 定义一次性任务和定时任务。 Job 表示一次性任务,而每个 CronJob 可以根据排期表重复执行。

本节中的其他主题:

3.4.2.1 - Deployments

Deployment 用于管理运行一个应用负载的一组 Pod,通常适用于不保持状态的负载。

一个 Deployment 为 PodReplicaSet 提供声明式的更新能力。

你负责描述 Deployment 中的目标状态,而 Deployment 控制器(Controller) 以受控速率更改实际状态, 使其变为期望状态。你可以定义 Deployment 以创建新的 ReplicaSet,或删除现有 Deployment, 并通过新的 Deployment 收养其资源。

用例

以下是 Deployments 的典型用例:

  • 创建 Deployment 以将 ReplicaSet 上线。ReplicaSet 在后台创建 Pod。 检查 ReplicaSet 的上线状态,查看其是否成功。
  • 通过更新 Deployment 的 PodTemplateSpec,声明 Pod 的新状态 。 新的 ReplicaSet 会被创建,Deployment 以受控速率将 Pod 从旧 ReplicaSet 迁移到新 ReplicaSet。 每个新的 ReplicaSet 都会更新 Deployment 的修订版本。

下面是一个 Deployment 示例。其中创建了一个 ReplicaSet,负责启动三个 nginx Pod:

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
        ports:
        - containerPort: 80

在该例中:

  • 创建名为 nginx-deployment(由 .metadata.name 字段标明)的 Deployment。 该名称将成为后续创建 ReplicaSet 和 Pod 的命名基础。 参阅编写 Deployment 规约获取更多详细信息。

  • 该 Deployment 创建一个 ReplicaSet,它创建三个(由 .spec.replicas 字段标明)Pod 副本。

  • .spec.selector 字段定义所创建的 ReplicaSet 如何查找要管理的 Pod。 在这里,你选择在 Pod 模板中定义的标签(app: nginx)。 不过,更复杂的选择规则是也可能的,只要 Pod 模板本身满足所给规则即可。

  • template 字段包含以下子字段:
    • Pod 被使用 .metadata.labels 字段打上 app: nginx 标签。
    • Pod 模板规约(即 .template.spec 字段)指示 Pod 运行一个 nginx 容器, 该容器运行版本为 1.14.2 的 nginx Docker Hub 镜像。
    • 创建一个容器并使用 .spec.template.spec.containers[0].name 字段将其命名为 nginx

开始之前,请确保的 Kubernetes 集群已启动并运行。 按照以下步骤创建上述 Deployment :

  1. 通过运行以下命令创建 Deployment :

    kubectl apply -f https://k8s.io/examples/controllers/nginx-deployment.yaml
    
  1. 运行 kubectl get deployments 检查 Deployment 是否已创建。 如果仍在创建 Deployment,则输出类似于:

    NAME               READY   UP-TO-DATE   AVAILABLE   AGE
    nginx-deployment   0/3     0            0           1s
    

    在检查集群中的 Deployment 时,所显示的字段有:

    • NAME 列出了名字空间中 Deployment 的名称。
    • READY 显示应用程序的可用的“副本”数。显示的模式是“就绪个数/期望个数”。
    • UP-TO-DATE 显示为了达到期望状态已经更新的副本数。
    • AVAILABLE 显示应用可供用户使用的副本数。
    • AGE 显示应用程序运行的时间。

    请注意期望副本数是根据 .spec.replicas 字段设置 3。

  1. 要查看 Deployment 上线状态,运行 kubectl rollout status deployment/nginx-deployment

    输出类似于:

    Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
    deployment "nginx-deployment" successfully rolled out
    
  1. 几秒钟后再次运行 kubectl get deployments。输出类似于:

    NAME               READY   UP-TO-DATE   AVAILABLE   AGE
    nginx-deployment   3/3     3            3           18s
    

    注意 Deployment 已创建全部三个副本,并且所有副本都是最新的(它们包含最新的 Pod 模板) 并且可用。

  1. 要查看 Deployment 创建的 ReplicaSet(rs),运行 kubectl get rs。 输出类似于:

    NAME                          DESIRED   CURRENT   READY   AGE
    nginx-deployment-75675f5897   3         3         3       18s
    

    ReplicaSet 输出中包含以下字段:

    • NAME 列出名字空间中 ReplicaSet 的名称;
    • DESIRED 显示应用的期望副本个数,即在创建 Deployment 时所定义的值。 此为期望状态;
    • CURRENT 显示当前运行状态中的副本个数;
    • READY 显示应用中有多少副本可以为用户提供服务;
    • AGE 显示应用已经运行的时间长度。

    注意 ReplicaSet 的名称格式始终为 [Deployment 名称]-[哈希]。 该名称将成为所创建的 Pod 的命名基础。 其中的哈希字符串与 ReplicaSet 上的 pod-template-hash 标签一致。

  1. 要查看每个 Pod 自动生成的标签,运行 kubectl get pods --show-labels。 输出类似于:

    NAME                                READY     STATUS    RESTARTS   AGE       LABELS
    nginx-deployment-75675f5897-7ci7o   1/1       Running   0          18s       app=nginx,pod-template-hash=75675f5897
    nginx-deployment-75675f5897-kzszj   1/1       Running   0          18s       app=nginx,pod-template-hash=75675f5897
    nginx-deployment-75675f5897-qqcnn   1/1       Running   0          18s       app=nginx,pod-template-hash=75675f5897
    

    所创建的 ReplicaSet 确保总是存在三个 nginx Pod。

Pod-template-hash 标签

Deployment 控制器将 pod-template-hash 标签添加到 Deployment 所创建或收留的每个 ReplicaSet 。

此标签可确保 Deployment 的子 ReplicaSet 不重叠。 标签是通过对 ReplicaSet 的 PodTemplate 进行哈希处理。 所生成的哈希值被添加到 ReplicaSet 选择算符、Pod 模板标签,并存在于在 ReplicaSet 可能拥有的任何现有 Pod 中。

更新 Deployment

按照以下步骤更新 Deployment:

  1. 先来更新 nginx Pod 以使用 nginx:1.16.1 镜像,而不是 nginx:1.14.2 镜像。

    kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.16.1
    

    或者使用下面的命令:

    kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1
    

    在这里,deployment/nginx-deployment 表明 Deployment 的名称,nginx 表明需要进行更新的容器, 而 nginx:1.16.1 则表示镜像的新版本以及它的标签。

    输出类似于:

    deployment.apps/nginx-deployment image updated
    

    或者,可以对 Deployment 执行 edit 操作并将 .spec.template.spec.containers[0].imagenginx:1.14.2 更改至 nginx:1.16.1

    kubectl edit deployment/nginx-deployment
    

    输出类似于:

    deployment.apps/nginx-deployment edited
    
  1. 要查看上线状态,运行:

    kubectl rollout status deployment/nginx-deployment
    

    输出类似于:

    Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
    

    或者

    deployment "nginx-deployment" successfully rolled out
    

获取关于已更新的 Deployment 的更多信息:

  • 在上线成功后,可以通过运行 kubectl get deployments 来查看 Deployment: 输出类似于:

    NAME               READY   UP-TO-DATE   AVAILABLE   AGE
    nginx-deployment   3/3     3            3           36s
    
  • 运行 kubectl get rs 以查看 Deployment 通过创建新的 ReplicaSet 并将其扩容到 3 个副本并将旧 ReplicaSet 缩容到 0 个副本完成了 Pod 的更新操作:

    kubectl get rs
    

    输出类似于:

    NAME                          DESIRED   CURRENT   READY   AGE
    nginx-deployment-1564180365   3         3         3       6s
    nginx-deployment-2035384211   0         0         0       36s
    
  • 现在运行 get pods 应仅显示新的 Pod:

    kubectl get pods
    

    输出类似于:

    NAME                                READY     STATUS    RESTARTS   AGE
    nginx-deployment-1564180365-khku8   1/1       Running   0          14s
    nginx-deployment-1564180365-nacti   1/1       Running   0          14s
    nginx-deployment-1564180365-z9gth   1/1       Running   0          14s
    

    下次要更新这些 Pod 时,只需再次更新 Deployment Pod 模板即可。

    Deployment 可确保在更新时仅关闭一定数量的 Pod。默认情况下,它确保至少所需 Pod 的 75% 处于运行状态(最大不可用比例为 25%)。

    Deployment 还确保仅所创建 Pod 数量只可能比期望 Pod 数高一点点。 默认情况下,它可确保启动的 Pod 个数比期望个数最多多出 125%(最大峰值 25%)。

    例如,如果仔细查看上述 Deployment ,将看到它首先创建了一个新的 Pod,然后删除旧的 Pod, 并创建了新的 Pod。它不会杀死旧 Pod,直到有足够数量的新 Pod 已经出现。 在足够数量的旧 Pod 被杀死前并没有创建新 Pod。它确保至少 3 个 Pod 可用, 同时最多总共 4 个 Pod 可用。 当 Deployment 设置为 4 个副本时,Pod 的个数会介于 3 和 5 之间。

  • 获取 Deployment 的更多信息

    kubectl describe deployments
    

    输出类似于:

    Name:                   nginx-deployment
    Namespace:              default
    CreationTimestamp:      Thu, 30 Nov 2017 10:56:25 +0000
    Labels:                 app=nginx
    Annotations:            deployment.kubernetes.io/revision=2
    Selector:               app=nginx
    Replicas:               3 desired | 3 updated | 3 total | 3 available | 0 unavailable
    StrategyType:           RollingUpdate
    MinReadySeconds:        0
    RollingUpdateStrategy:  25% max unavailable, 25% max surge
    Pod Template:
      Labels:  app=nginx
       Containers:
        nginx:
          Image:        nginx:1.16.1
          Port:         80/TCP
          Environment:  <none>
          Mounts:       <none>
        Volumes:        <none>
      Conditions:
        Type           Status  Reason
        ----           ------  ------
        Available      True    MinimumReplicasAvailable
        Progressing    True    NewReplicaSetAvailable
      OldReplicaSets:  <none>
      NewReplicaSet:   nginx-deployment-1564180365 (3/3 replicas created)
      Events:
        Type    Reason             Age   From                   Message
        ----    ------             ----  ----                   -------
        Normal  ScalingReplicaSet  2m    deployment-controller  Scaled up replica set nginx-deployment-2035384211 to 3
        Normal  ScalingReplicaSet  24s   deployment-controller  Scaled up replica set nginx-deployment-1564180365 to 1
        Normal  ScalingReplicaSet  22s   deployment-controller  Scaled down replica set nginx-deployment-2035384211 to 2
        Normal  ScalingReplicaSet  22s   deployment-controller  Scaled up replica set nginx-deployment-1564180365 to 2
        Normal  ScalingReplicaSet  19s   deployment-controller  Scaled down replica set nginx-deployment-2035384211 to 1
        Normal  ScalingReplicaSet  19s   deployment-controller  Scaled up replica set nginx-deployment-1564180365 to 3
        Normal  ScalingReplicaSet  14s   deployment-controller  Scaled down replica set nginx-deployment-2035384211 to 0
    

    可以看到,当第一次创建 Deployment 时,它创建了一个 ReplicaSet(nginx-deployment-2035384211) 并将其直接扩容至 3 个副本。更新 Deployment 时,它创建了一个新的 ReplicaSet (nginx-deployment-1564180365),并将其扩容为 1,等待其就绪;然后将旧 ReplicaSet 缩容到 2, 将新的 ReplicaSet 扩容到 2 以便至少有 3 个 Pod 可用且最多创建 4 个 Pod。 然后,它使用相同的滚动更新策略继续对新的 ReplicaSet 扩容并对旧的 ReplicaSet 缩容。 最后,你将有 3 个可用的副本在新的 ReplicaSet 中,旧 ReplicaSet 将缩容到 0。

翻转(多 Deployment 动态更新)

Deployment 控制器每次注意到新的 Deployment 时,都会创建一个 ReplicaSet 以启动所需的 Pod。 如果更新了 Deployment,则控制标签匹配 .spec.selector 但模板不匹配 .spec.template 的 Pod 的现有 ReplicaSet 被缩容。 最终,新的 ReplicaSet 缩放为 .spec.replicas 个副本, 所有旧 ReplicaSet 缩放为 0 个副本。

当 Deployment 正在上线时被更新,Deployment 会针对更新创建一个新的 ReplicaSet 并开始对其扩容,之前正在被扩容的 ReplicaSet 会被翻转,添加到旧 ReplicaSet 列表 并开始缩容。

例如,假定你在创建一个 Deployment 以生成 nginx:1.14.2 的 5 个副本,但接下来 更新 Deployment 以创建 5 个 nginx:1.16.1 的副本,而此时只有 3 个 nginx:1.14.2 副本已创建。在这种情况下,Deployment 会立即开始杀死 3 个 nginx:1.14.2 Pod, 并开始创建 nginx:1.16.1 Pod。它不会等待 nginx:1.14.2 的 5 个副本都创建完成后才开始执行变更动作。

更改标签选择算符

通常不鼓励更新标签选择算符。建议你提前规划选择算符。 在任何情况下,如果需要更新标签选择算符,请格外小心, 并确保自己了解这背后可能发生的所有事情。

  • 添加选择算符时要求使用新标签更新 Deployment 规约中的 Pod 模板标签,否则将返回验证错误。 此更改是非重叠的,也就是说新的选择算符不会选择使用旧选择算符所创建的 ReplicaSet 和 Pod, 这会导致创建新的 ReplicaSet 时所有旧 ReplicaSet 都会被孤立。
  • 选择算符的更新如果更改了某个算符的键名,这会导致与添加算符时相同的行为。
  • 删除选择算符的操作会删除从 Deployment 选择算符中删除现有算符。 此操作不需要更改 Pod 模板标签。现有 ReplicaSet 不会被孤立,也不会因此创建新的 ReplicaSet, 但请注意已删除的标签仍然存在于现有的 Pod 和 ReplicaSet 中。

回滚 Deployment

有时,你可能想要回滚 Deployment;例如,当 Deployment 不稳定时(例如进入反复崩溃状态)。 默认情况下,Deployment 的所有上线记录都保留在系统中,以便可以随时回滚 (你可以通过修改修订历史记录限制来更改这一约束)。

  • 假设你在更新 Deployment 时犯了一个拼写错误,将镜像名称命名设置为 nginx:1.161 而不是 nginx:1.16.1

    kubectl set image deployment/nginx-deployment nginx=nginx:1.161
    

    输出类似于:

    deployment.apps/nginx-deployment image updated
    
  • 此上线进程会出现停滞。你可以通过检查上线状态来验证:

    kubectl rollout status deployment/nginx-deployment
    

    输出类似于:

    Waiting for rollout to finish: 1 out of 3 new replicas have been updated...
    
  • 按 Ctrl-C 停止上述上线状态观测。有关上线停滞的详细信息,参考这里
  • 你可以看到旧的副本(算上来自 nginx-deployment-1564180365nginx-deployment-2035384211 的副本)有 3 个, 新的副本(来自 nginx-deployment-3066724191)有 1 个:

    kubectl get rs
    

    输出类似于:

    NAME                          DESIRED   CURRENT   READY   AGE
    nginx-deployment-1564180365   3         3         3       25s
    nginx-deployment-2035384211   0         0         0       36s
    nginx-deployment-3066724191   1         1         0       6s
    
  • 查看所创建的 Pod,你会注意到新 ReplicaSet 所创建的 1 个 Pod 卡顿在镜像拉取循环中。

    kubectl get pods
    

    输出类似于:

    NAME                                READY     STATUS             RESTARTS   AGE
    nginx-deployment-1564180365-70iae   1/1       Running            0          25s
    nginx-deployment-1564180365-jbqqo   1/1       Running            0          25s
    nginx-deployment-1564180365-hysrc   1/1       Running            0          25s
    nginx-deployment-3066724191-08mng   0/1       ImagePullBackOff   0          6s
    
  • 获取 Deployment 描述信息:

    kubectl describe deployment
    

    输出类似于:

    Name:           nginx-deployment
    Namespace:      default
    CreationTimestamp:  Tue, 15 Mar 2016 14:48:04 -0700
    Labels:         app=nginx
    Selector:       app=nginx
    Replicas:       3 desired | 1 updated | 4 total | 3 available | 1 unavailable
    StrategyType:       RollingUpdate
    MinReadySeconds:    0
    RollingUpdateStrategy:  25% max unavailable, 25% max surge
    Pod Template:
      Labels:  app=nginx
      Containers:
       nginx:
        Image:        nginx:1.161
        Port:         80/TCP
        Host Port:    0/TCP
        Environment:  <none>
        Mounts:       <none>
      Volumes:        <none>
    Conditions:
      Type           Status  Reason
      ----           ------  ------
      Available      True    MinimumReplicasAvailable
      Progressing    True    ReplicaSetUpdated
    OldReplicaSets:     nginx-deployment-1564180365 (3/3 replicas created)
    NewReplicaSet:      nginx-deployment-3066724191 (1/1 replicas created)
    Events:
      FirstSeen LastSeen    Count   From                    SubObjectPath   Type        Reason              Message
      --------- --------    -----   ----                    -------------   --------    ------              -------
      1m        1m          1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-2035384211 to 3
      22s       22s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-1564180365 to 1
      22s       22s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled down replica set nginx-deployment-2035384211 to 2
      22s       22s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-1564180365 to 2
      21s       21s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled down replica set nginx-deployment-2035384211 to 1
      21s       21s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-1564180365 to 3
      13s       13s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled down replica set nginx-deployment-2035384211 to 0
      13s       13s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-3066724191 to 1
    

    要解决此问题,需要回滚到以前稳定的 Deployment 版本。

检查 Deployment 上线历史

按照如下步骤检查回滚历史:

  1. 首先,检查 Deployment 修订历史:

    kubectl rollout history deployment/nginx-deployment
    

    输出类似于:

    deployments "nginx-deployment"
    REVISION    CHANGE-CAUSE
    1           kubectl apply --filename=https://k8s.io/examples/controllers/nginx-deployment.yaml
    2           kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1
    3           kubectl set image deployment/nginx-deployment nginx=nginx:1.161
    

    CHANGE-CAUSE 的内容是从 Deployment 的 kubernetes.io/change-cause 注解复制过来的。 复制动作发生在修订版本创建时。你可以通过以下方式设置 CHANGE-CAUSE 消息:

    • 使用 kubectl annotate deployment/nginx-deployment kubernetes.io/change-cause="image updated to 1.16.1" 为 Deployment 添加注解。
    • 手动编辑资源的清单。
  1. 要查看修订历史的详细信息,运行:

    kubectl rollout history deployment/nginx-deployment --revision=2
    

    输出类似于:

    deployments "nginx-deployment" revision 2
      Labels:       app=nginx
              pod-template-hash=1159050644
      Annotations:  kubernetes.io/change-cause=kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1
      Containers:
       nginx:
        Image:      nginx:1.16.1
        Port:       80/TCP
         QoS Tier:
            cpu:      BestEffort
            memory:   BestEffort
        Environment Variables:      <none>
      No volumes.
    

回滚到之前的修订版本

按照下面给出的步骤将 Deployment 从当前版本回滚到以前的版本(即版本 2)。

  1. 假定现在你已决定撤消当前上线并回滚到以前的修订版本:

    kubectl rollout undo deployment/nginx-deployment
    

    输出类似于:

    deployment.apps/nginx-deployment rolled back
    

    或者,你也可以通过使用 --to-revision 来回滚到特定修订版本:

    kubectl rollout undo deployment/nginx-deployment --to-revision=2
    

    输出类似于:

    deployment.apps/nginx-deployment rolled back
    

    与回滚相关的指令的更详细信息,请参考 kubectl rollout

    现在,Deployment 正在回滚到以前的稳定版本。正如你所看到的,Deployment 控制器生成了回滚到修订版本 2 的 DeploymentRollback 事件。

  1. 检查回滚是否成功以及 Deployment 是否正在运行,运行:

    kubectl get deployment nginx-deployment
    

    输出类似于:

    NAME               READY   UP-TO-DATE   AVAILABLE   AGE
    nginx-deployment   3/3     3            3           30m
    
  1. 获取 Deployment 描述信息:

    kubectl describe deployment nginx-deployment
    

    输出类似于:

    Name:                   nginx-deployment
    Namespace:              default
    CreationTimestamp:      Sun, 02 Sep 2018 18:17:55 -0500
    Labels:                 app=nginx
    Annotations:            deployment.kubernetes.io/revision=4
                            kubernetes.io/change-cause=kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1
    Selector:               app=nginx
    Replicas:               3 desired | 3 updated | 3 total | 3 available | 0 unavailable
    StrategyType:           RollingUpdate
    MinReadySeconds:        0
    RollingUpdateStrategy:  25% max unavailable, 25% max surge
    Pod Template:
      Labels:  app=nginx
      Containers:
       nginx:
        Image:        nginx:1.16.1
        Port:         80/TCP
        Host Port:    0/TCP
        Environment:  <none>
        Mounts:       <none>
      Volumes:        <none>
    Conditions:
      Type           Status  Reason
      ----           ------  ------
      Available      True    MinimumReplicasAvailable
      Progressing    True    NewReplicaSetAvailable
    OldReplicaSets:  <none>
    NewReplicaSet:   nginx-deployment-c4747d96c (3/3 replicas created)
    Events:
      Type    Reason              Age   From                   Message
      ----    ------              ----  ----                   -------
      Normal  ScalingReplicaSet   12m   deployment-controller  Scaled up replica set nginx-deployment-75675f5897 to 3
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled up replica set nginx-deployment-c4747d96c to 1
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled down replica set nginx-deployment-75675f5897 to 2
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled up replica set nginx-deployment-c4747d96c to 2
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled down replica set nginx-deployment-75675f5897 to 1
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled up replica set nginx-deployment-c4747d96c to 3
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled down replica set nginx-deployment-75675f5897 to 0
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled up replica set nginx-deployment-595696685f to 1
      Normal  DeploymentRollback  15s   deployment-controller  Rolled back deployment "nginx-deployment" to revision 2
      Normal  ScalingReplicaSet   15s   deployment-controller  Scaled down replica set nginx-deployment-595696685f to 0
    

缩放 Deployment

你可以使用如下指令缩放 Deployment:

kubectl scale deployment/nginx-deployment --replicas=10

输出类似于:

deployment.apps/nginx-deployment scaled

假设集群启用了Pod 的水平自动缩放, 你可以为 Deployment 设置自动缩放器,并基于现有 Pod 的 CPU 利用率选择要运行的 Pod 个数下限和上限。

kubectl autoscale deployment/nginx-deployment --min=10 --max=15 --cpu-percent=80

输出类似于:

deployment.apps/nginx-deployment scaled

比例缩放

RollingUpdate 的 Deployment 支持同时运行应用程序的多个版本。 当自动缩放器缩放处于上线进程(仍在进行中或暂停)中的 RollingUpdate Deployment 时, Deployment 控制器会平衡现有的活跃状态的 ReplicaSet(含 Pod 的 ReplicaSet)中的额外副本, 以降低风险。这称为 比例缩放(Proportional Scaling)

例如,你正在运行一个 10 个副本的 Deployment,其 maxSurge=3,maxUnavailable=2。

  • 确保 Deployment 的这 10 个副本都在运行。

    kubectl get deploy
    

    输出类似于:

    NAME                 DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
    nginx-deployment     10        10        10           10          50s
    
  • 更新 Deployment 使用新镜像,碰巧该镜像无法从集群内部解析。

    kubectl set image deployment/nginx-deployment nginx=nginx:sometag
    

    输出类似于:

    deployment.apps/nginx-deployment image updated
    
  • 镜像更新使用 ReplicaSet nginx-deployment-1989198191 启动新的上线过程, 但由于上面提到的 maxUnavailable 要求,该进程被阻塞了。检查上线状态:

    kubectl get rs
    

    输出类似于:

    NAME                          DESIRED   CURRENT   READY     AGE
    nginx-deployment-1989198191   5         5         0         9s
    nginx-deployment-618515232    8         8         8         1m
    
  • 然后,出现了新的 Deployment 扩缩请求。自动缩放器将 Deployment 副本增加到 15。 Deployment 控制器需要决定在何处添加 5 个新副本。如果未使用比例缩放,所有 5 个副本 都将添加到新的 ReplicaSet 中。使用比例缩放时,可以将额外的副本分布到所有 ReplicaSet。 较大比例的副本会被添加到拥有最多副本的 ReplicaSet,而较低比例的副本会进入到 副本较少的 ReplicaSet。所有剩下的副本都会添加到副本最多的 ReplicaSet。 具有零副本的 ReplicaSet 不会被扩容。

在上面的示例中,3 个副本被添加到旧 ReplicaSet 中,2 个副本被添加到新 ReplicaSet。 假定新的副本都很健康,上线过程最终应将所有副本迁移到新的 ReplicaSet 中。 要确认这一点,请运行:

kubectl get deploy

输出类似于:

NAME                 DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment     15        18        7            8           7m

上线状态确认了副本是如何被添加到每个 ReplicaSet 的。

kubectl get rs

输出类似于:

NAME                          DESIRED   CURRENT   READY     AGE
nginx-deployment-1989198191   7         7         0         7m
nginx-deployment-618515232    11        11        11        7m

暂停、恢复 Deployment 的上线过程

在你更新一个 Deployment 的时候,或者计划更新它的时候, 你可以在触发一个或多个更新之前暂停 Deployment 的上线过程。 当你准备应用这些变更时,你可以重新恢复 Deployment 上线过程。 这样做使得你能够在暂停和恢复执行之间应用多个修补程序,而不会触发不必要的上线操作。

  • 例如,对于一个刚刚创建的 Deployment:

    获取该 Deployment 信息:

    kubectl get deploy
    

    输出类似于:

    NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
    nginx     3         3         3            3           1m
    

    获取上线状态:

    kubectl get rs
    

    输出类似于:

    NAME               DESIRED   CURRENT   READY     AGE
    nginx-2142116321   3         3         3         1m
    
  • 使用如下指令暂停上线:

    kubectl rollout pause deployment/nginx-deployment
    

    输出类似于:

    deployment.apps/nginx-deployment paused
    
  • 接下来更新 Deployment 镜像:

    kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1
    

    输出类似于:

    deployment.apps/nginx-deployment image updated
    
  • 注意没有新的上线被触发:

    kubectl rollout history deployment/nginx-deployment
    

    输出类似于:

    deployments "nginx"
    REVISION  CHANGE-CAUSE
    1   <none>
    
  • 获取上线状态验证现有的 ReplicaSet 没有被更改:

    kubectl get rs
    

    输出类似于:

    NAME               DESIRED   CURRENT   READY     AGE
    nginx-2142116321   3         3         3         2m
    
  • 你可以根据需要执行很多更新操作,例如,可以要使用的资源:

    kubectl set resources deployment/nginx-deployment -c=nginx --limits=cpu=200m,memory=512Mi
    

    输出类似于:

    deployment.apps/nginx-deployment resource requirements updated
    

    暂停 Deployment 上线之前的初始状态将继续发挥作用,但新的更新在 Deployment 上线被暂停期间不会产生任何效果。

  • 最终,恢复 Deployment 上线并观察新的 ReplicaSet 的创建过程,其中包含了所应用的所有更新:

    kubectl rollout resume deployment/nginx-deployment
    

    输出类似于这样:

    deployment.apps/nginx-deployment resumed
    
  • 观察上线的状态,直到完成。

    kubectl get rs -w
    

    输出类似于:

    NAME               DESIRED   CURRENT   READY     AGE
    nginx-2142116321   2         2         2         2m
    nginx-3926361531   2         2         0         6s
    nginx-3926361531   2         2         1         18s
    nginx-2142116321   1         2         2         2m
    nginx-2142116321   1         2         2         2m
    nginx-3926361531   3         2         1         18s
    nginx-3926361531   3         2         1         18s
    nginx-2142116321   1         1         1         2m
    nginx-3926361531   3         3         1         18s
    nginx-3926361531   3         3         2         19s
    nginx-2142116321   0         1         1         2m
    nginx-2142116321   0         1         1         2m
    nginx-2142116321   0         0         0         2m
    nginx-3926361531   3         3         3         20s
    
  • 获取最近上线的状态:

    kubectl get rs
    

    输出类似于:

    NAME               DESIRED   CURRENT   READY     AGE
    nginx-2142116321   0         0         0         2m
    nginx-3926361531   3         3         3         28s
    

Deployment 状态

Deployment 的生命周期中会有许多状态。上线新的 ReplicaSet 期间可能处于 Progressing(进行中),可能是 Complete(已完成),也可能是 Failed(失败)以至于无法继续进行。

进行中的 Deployment

执行下面的任务期间,Kubernetes 标记 Deployment 为进行中(Progressing)_:

  • Deployment 创建新的 ReplicaSet
  • Deployment 正在为其最新的 ReplicaSet 扩容
  • Deployment 正在为其旧有的 ReplicaSet(s) 缩容
  • 新的 Pod 已经就绪或者可用(就绪至少持续了 MinReadySeconds 秒)。

当上线过程进入“Progressing”状态时,Deployment 控制器会向 Deployment 的 .status.conditions 中添加包含下面属性的状况条目:

  • type: Progressing
  • status: "True"
  • reason: NewReplicaSetCreated | reason: FoundNewReplicaSet | reason: ReplicaSetUpdated

你可以使用 kubectl rollout status 监视 Deployment 的进度。

完成的 Deployment

当 Deployment 具有以下特征时,Kubernetes 将其标记为完成(Complete);

  • 与 Deployment 关联的所有副本都已更新到指定的最新版本,这意味着之前请求的所有更新都已完成。
  • 与 Deployment 关联的所有副本都可用。
  • 未运行 Deployment 的旧副本。

当上线过程进入“Complete”状态时,Deployment 控制器会向 Deployment 的 .status.conditions 中添加包含下面属性的状况条目:

  • type: Progressing
  • status: "True"
  • reason: NewReplicaSetAvailable

这一 Progressing 状况的状态值会持续为 "True",直至新的上线动作被触发。 即使副本的可用状态发生变化(进而影响 Available 状况),Progressing 状况的值也不会变化。

你可以使用 kubectl rollout status 检查 Deployment 是否已完成。 如果上线成功完成,kubectl rollout status 返回退出代码 0。

kubectl rollout status deployment/nginx-deployment

输出类似于:

Waiting for rollout to finish: 2 of 3 updated replicas are available...
deployment "nginx-deployment" successfully rolled out

kubectl rollout 命令获得的返回状态为 0(成功):

echo $?
0

失败的 Deployment

你的 Deployment 可能会在尝试部署其最新的 ReplicaSet 受挫,一直处于未完成状态。 造成此情况一些可能因素如下:

  • 配额(Quota)不足
  • 就绪探测(Readiness Probe)失败
  • 镜像拉取错误
  • 权限不足
  • 限制范围(Limit Ranges)问题
  • 应用程序运行时的配置错误

检测此状况的一种方法是在 Deployment 规约中指定截止时间参数: (.spec.progressDeadlineSeconds)。 .spec.progressDeadlineSeconds 给出的是一个秒数值,Deployment 控制器在(通过 Deployment 状态) 标示 Deployment 进展停滞之前,需要等待所给的时长。

以下 kubectl 命令设置规约中的 progressDeadlineSeconds,从而告知控制器 在 10 分钟后报告 Deployment 的上线没有进展:

kubectl patch deployment/nginx-deployment -p '{"spec":{"progressDeadlineSeconds":600}}'

输出类似于:

deployment.apps/nginx-deployment patched

超过截止时间后,Deployment 控制器将添加具有以下属性的 Deployment 状况到 Deployment 的 .status.conditions 中:

  • type: Progressing
  • status: "False"
  • reason: ProgressDeadlineExceeded

这一状况也可能会比较早地失败,因而其状态值被设置为 "False", 其原因为 ReplicaSetCreateError。 一旦 Deployment 上线完成,就不再考虑其期限。

参考 Kubernetes API Conventions 获取更多状态状况相关的信息。

Deployment 可能会出现瞬时性的错误,可能因为设置的超时时间过短, 也可能因为其他可认为是临时性的问题。例如,假定所遇到的问题是配额不足。 如果描述 Deployment,你将会注意到以下部分:

kubectl describe deployment nginx-deployment

输出类似于:

<...>
Conditions:
  Type            Status  Reason
  ----            ------  ------
  Available       True    MinimumReplicasAvailable
  Progressing     True    ReplicaSetUpdated
  ReplicaFailure  True    FailedCreate
<...>

如果运行 kubectl get deployment nginx-deployment -o yaml,Deployment 状态输出 将类似于这样:

status:
  availableReplicas: 2
  conditions:
  - lastTransitionTime: 2016-10-04T12:25:39Z
    lastUpdateTime: 2016-10-04T12:25:39Z
    message: Replica set "nginx-deployment-4262182780" is progressing.
    reason: ReplicaSetUpdated
    status: "True"
    type: Progressing
  - lastTransitionTime: 2016-10-04T12:25:42Z
    lastUpdateTime: 2016-10-04T12:25:42Z
    message: Deployment has minimum availability.
    reason: MinimumReplicasAvailable
    status: "True"
    type: Available
  - lastTransitionTime: 2016-10-04T12:25:39Z
    lastUpdateTime: 2016-10-04T12:25:39Z
    message: 'Error creating: pods "nginx-deployment-4262182780-" is forbidden: exceeded quota:
      object-counts, requested: pods=1, used: pods=3, limited: pods=2'
    reason: FailedCreate
    status: "True"
    type: ReplicaFailure
  observedGeneration: 3
  replicas: 2
  unavailableReplicas: 2

最终,一旦超过 Deployment 进度限期,Kubernetes 将更新状态和进度状况的原因:

Conditions:
  Type            Status  Reason
  ----            ------  ------
  Available       True    MinimumReplicasAvailable
  Progressing     False   ProgressDeadlineExceeded
  ReplicaFailure  True    FailedCreate

可以通过缩容 Deployment 或者缩容其他运行状态的控制器,或者直接在命名空间中增加配额 来解决配额不足的问题。如果配额条件满足,Deployment 控制器完成了 Deployment 上线操作, Deployment 状态会更新为成功状况(Status=TrueReason=NewReplicaSetAvailable)。

Conditions:
  Type          Status  Reason
  ----          ------  ------
  Available     True    MinimumReplicasAvailable
  Progressing   True    NewReplicaSetAvailable

type: Available 加上 status: True 意味着 Deployment 具有最低可用性。 最低可用性由 Deployment 策略中的参数指定。 type: Progressing 加上 status: True 表示 Deployment 处于上线过程中,并且正在运行, 或者已成功完成进度,最小所需新副本处于可用。 请参阅对应状况的 Reason 了解相关细节。 在我们的案例中 reason: NewReplicaSetAvailable 表示 Deployment 已完成。

你可以使用 kubectl rollout status 检查 Deployment 是否未能取得进展。 如果 Deployment 已超过进度限期,kubectl rollout status 返回非零退出代码。

kubectl rollout status deployment/nginx-deployment

输出类似于:

Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
error: deployment "nginx" exceeded its progress deadline

kubectl rollout 命令的退出状态为 1(表明发生了错误):

echo $?
1

对失败 Deployment 的操作

可应用于已完成的 Deployment 的所有操作也适用于失败的 Deployment。 你可以对其执行扩缩容、回滚到以前的修订版本等操作,或者在需要对 Deployment 的 Pod 模板应用多项调整时,将 Deployment 暂停。

清理策略

你可以在 Deployment 中设置 .spec.revisionHistoryLimit 字段以指定保留此 Deployment 的多少个旧有 ReplicaSet。其余的 ReplicaSet 将在后台被垃圾回收。 默认情况下,此值为 10。

金丝雀部署

如果要使用 Deployment 向用户子集或服务器子集上线版本, 则可以遵循资源管理所描述的金丝雀模式, 创建多个 Deployment,每个版本一个。

编写 Deployment 规约

同其他 Kubernetes 配置一样, Deployment 需要 .apiVersion.kind.metadata 字段。 有关配置文件的其他信息,请参考部署 Deployment、 配置容器和使用 kubectl 管理资源等相关文档。

当控制面为 Deployment 创建新的 Pod 时,Deployment 的 .metadata.name 是命名这些 Pod 的部分基础。 Deployment 的名称必须是一个合法的 DNS 子域值, 但这会对 Pod 的主机名产生意外的结果。为获得最佳兼容性,名称应遵循更严格的 DNS 标签规则。

Deployment 还需要 .spec 部分

Pod 模板

.spec 中只有 .spec.template.spec.selector 是必需的字段。

.spec.template 是一个 Pod 模板。 它和 Pod 的语法规则完全相同。 只是这里它是嵌套的,因此不需要 apiVersionkind

除了 Pod 的必填字段外,Deployment 中的 Pod 模板必须指定适当的标签和适当的重新启动策略。 对于标签,请确保不要与其他控制器重叠。请参考选择算符

只有 .spec.template.spec.restartPolicy 等于 Always 才是被允许的,这也是在没有指定时的默认设置。

副本

.spec.replicas 是指定所需 Pod 的可选字段。它的默认值是1。

如果你对某个 Deployment 执行了手动扩缩操作(例如,通过 kubectl scale deployment deployment --replicas=X), 之后基于清单对 Deployment 执行了更新操作(例如通过运行 kubectl apply -f deployment.yaml),那么通过应用清单而完成的更新会覆盖之前手动扩缩所作的变更。

如果一个 HorizontalPodAutoscaler (或者其他执行水平扩缩操作的类似 API)在管理 Deployment 的扩缩, 则不要设置 .spec.replicas

恰恰相反,应该允许 Kubernetes 控制面来自动管理 .spec.replicas 字段。

选择算符

.spec.selector 是指定本 Deployment 的 Pod 标签选择算符的必需字段。

.spec.selector 必须匹配 .spec.template.metadata.labels,否则请求会被 API 拒绝。

在 API apps/v1版本中,.spec.selector.metadata.labels 如果没有设置的话, 不会被默认设置为 .spec.template.metadata.labels,所以需要明确进行设置。 同时在 apps/v1版本中,Deployment 创建后 .spec.selector 是不可变的。

当 Pod 的标签和选择算符匹配,但其模板和 .spec.template 不同时,或者此类 Pod 的总数超过 .spec.replicas 的设置时,Deployment 会终结之。 如果 Pod 总数未达到期望值,Deployment 会基于 .spec.template 创建新的 Pod。

如果有多个控制器的选择算符发生重叠,则控制器之间会因冲突而无法正常工作。

策略

.spec.strategy 策略指定用于用新 Pod 替换旧 Pod 的策略。 .spec.strategy.type 可以是 “Recreate” 或 “RollingUpdate”。“RollingUpdate” 是默认值。

重新创建 Deployment

如果 .spec.strategy.type==Recreate,在创建新 Pod 之前,所有现有的 Pod 会被杀死。

滚动更新 Deployment

Deployment 会在 .spec.strategy.type==RollingUpdate时,采取 滚动更新的方式更新 Pod。你可以指定 maxUnavailablemaxSurge 来控制滚动更新过程。

最大不可用

.spec.strategy.rollingUpdate.maxUnavailable 是一个可选字段, 用来指定更新过程中不可用的 Pod 的个数上限。该值可以是绝对数字(例如,5),也可以是所需 Pod 的百分比(例如,10%)。百分比值会转换成绝对数并去除小数部分。 如果 .spec.strategy.rollingUpdate.maxSurge 为 0,则此值不能为 0。 默认值为 25%。

例如,当此值设置为 30% 时,滚动更新开始时会立即将旧 ReplicaSet 缩容到期望 Pod 个数的70%。 新 Pod 准备就绪后,可以继续缩容旧有的 ReplicaSet,然后对新的 ReplicaSet 扩容, 确保在更新期间可用的 Pod 总数在任何时候都至少为所需的 Pod 个数的 70%。

最大峰值

.spec.strategy.rollingUpdate.maxSurge 是一个可选字段,用来指定可以创建的超出期望 Pod 个数的 Pod 数量。此值可以是绝对数(例如,5)或所需 Pod 的百分比(例如,10%)。 如果 MaxUnavailable 为 0,则此值不能为 0。百分比值会通过向上取整转换为绝对数。 此字段的默认值为 25%。

例如,当此值为 30% 时,启动滚动更新后,会立即对新的 ReplicaSet 扩容,同时保证新旧 Pod 的总数不超过所需 Pod 总数的 130%。一旦旧 Pod 被杀死,新的 ReplicaSet 可以进一步扩容, 同时确保更新期间的任何时候运行中的 Pod 总数最多为所需 Pod 总数的 130%。

以下是一些使用 maxUnavailablemaxSurge 的滚动更新 Deployment 的示例:

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
        ports:
        - containerPort: 80
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1

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
        ports:
        - containerPort: 80
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1

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
        ports:
        - containerPort: 80
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1

进度期限秒数

.spec.progressDeadlineSeconds 是一个可选字段,用于指定系统在报告 Deployment 进展失败之前等待 Deployment 取得进展的秒数。 这类报告会在资源状态中体现为 type: Progressingstatus: Falsereason: ProgressDeadlineExceeded。Deployment 控制器将在默认 600 毫秒内持续重试 Deployment。 将来,一旦实现了自动回滚,Deployment 控制器将在探测到这样的条件时立即回滚 Deployment。

如果指定,则此字段值需要大于 .spec.minReadySeconds 取值。

最短就绪时间

.spec.minReadySeconds 是一个可选字段,用于指定新创建的 Pod 在没有任意容器崩溃情况下的最小就绪时间, 只有超出这个时间 Pod 才被视为可用。默认值为 0(Pod 在准备就绪后立即将被视为可用)。 要了解何时 Pod 被视为就绪, 可参考容器探针

修订历史限制

Deployment 的修订历史记录存储在它所控制的 ReplicaSet 中。

.spec.revisionHistoryLimit 是一个可选字段,用来设定出于回滚目的所要保留的旧 ReplicaSet 数量。 这些旧 ReplicaSet 会消耗 etcd 中的资源,并占用 kubectl get rs 的输出。 每个 Deployment 修订版本的配置都存储在其 ReplicaSet 中;因此,一旦删除了旧的 ReplicaSet, 将失去回滚到 Deployment 的对应修订版本的能力。 默认情况下,系统保留 10 个旧 ReplicaSet,但其理想值取决于新 Deployment 的频率和稳定性。

更具体地说,将此字段设置为 0 意味着将清理所有具有 0 个副本的旧 ReplicaSet。 在这种情况下,无法撤消新的 Deployment 上线,因为它的修订历史被清除了。

paused(暂停的)

.spec.paused 是用于暂停和恢复 Deployment 的可选布尔字段。 暂停的 Deployment 和未暂停的 Deployment 的唯一区别是,Deployment 处于暂停状态时, PodTemplateSpec 的任何修改都不会触发新的上线。 Deployment 在创建时是默认不会处于暂停状态。

接下来

3.4.2.2 - ReplicaSet

ReplicaSet 的作用是维持在任何给定时间运行的一组稳定的副本 Pod。 通常,你会定义一个 Deployment,并用这个 Deployment 自动管理 ReplicaSet。

ReplicaSet 的目的是维护一组在任何时候都处于运行状态的 Pod 副本的稳定集合。 因此,它通常用来保证给定数量的、完全相同的 Pod 的可用性。

ReplicaSet 的工作原理

ReplicaSet 是通过一组字段来定义的,包括一个用来识别可获得的 Pod 的集合的选择算符、一个用来标明应该维护的副本个数的数值、一个用来指定应该创建新 Pod 以满足副本个数条件时要使用的 Pod 模板等等。 每个 ReplicaSet 都通过根据需要创建和删除 Pod 以使得副本个数达到期望值, 进而实现其存在价值。当 ReplicaSet 需要创建新的 Pod 时,会使用所提供的 Pod 模板。

ReplicaSet 通过 Pod 上的 metadata.ownerReferences 字段连接到附属 Pod,该字段给出当前对象的属主资源。 ReplicaSet 所获得的 Pod 都在其 ownerReferences 字段中包含了属主 ReplicaSet 的标识信息。正是通过这一连接,ReplicaSet 知道它所维护的 Pod 集合的状态, 并据此计划其操作行为。

ReplicaSet 使用其选择算符来辨识要获得的 Pod 集合。如果某个 Pod 没有 OwnerReference 或者其 OwnerReference 不是一个控制器, 且其匹配到某 ReplicaSet 的选择算符,则该 Pod 立即被此 ReplicaSet 获得。

何时使用 ReplicaSet

ReplicaSet 确保任何时间都有指定数量的 Pod 副本在运行。 然而,Deployment 是一个更高级的概念,它管理 ReplicaSet,并向 Pod 提供声明式的更新以及许多其他有用的功能。 因此,我们建议使用 Deployment 而不是直接使用 ReplicaSet, 除非你需要自定义更新业务流程或根本不需要更新。

这实际上意味着,你可能永远不需要操作 ReplicaSet 对象:而是使用 Deployment,并在 spec 部分定义你的应用。

示例

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: frontend
  labels:
    app: guestbook
    tier: frontend
spec:
  # 按你的实际情况修改副本数
  replicas: 3
  selector:
    matchLabels:
      tier: frontend
  template:
    metadata:
      labels:
        tier: frontend
    spec:
      containers:
      - name: php-redis
        image: gcr.io/google_samples/gb-frontend:v3

将此清单保存到 frontend.yaml 中,并将其提交到 Kubernetes 集群, 就能创建 yaml 文件所定义的 ReplicaSet 及其管理的 Pod。

kubectl apply -f https://kubernetes.io/examples/controllers/frontend.yaml

你可以看到当前被部署的 ReplicaSet:

kubectl get rs

并看到你所创建的前端:

NAME       DESIRED   CURRENT   READY   AGE
frontend   3         3         3       6s

你也可以查看 ReplicaSet 的状态:

kubectl describe rs/frontend

你会看到类似如下的输出:

Name:         frontend
Namespace:    default
Selector:     tier=frontend
Labels:       app=guestbook
              tier=frontend
Annotations:  kubectl.kubernetes.io/last-applied-configuration:
                {"apiVersion":"apps/v1","kind":"ReplicaSet","metadata":{"annotations":{},"labels":{"app":"guestbook","tier":"frontend"},"name":"frontend",...
Replicas:     3 current / 3 desired
Pods Status:  3 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
  Labels:  tier=frontend
  Containers:
   php-redis:
    Image:        gcr.io/google_samples/gb-frontend:v3
    Port:         <none>
    Host Port:    <none>
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Events:
  Type    Reason            Age   From                   Message
  ----    ------            ----  ----                   -------
  Normal  SuccessfulCreate  117s  replicaset-controller  Created pod: frontend-wtsmm
  Normal  SuccessfulCreate  116s  replicaset-controller  Created pod: frontend-b2zdv
  Normal  SuccessfulCreate  116s  replicaset-controller  Created pod: frontend-vcmts

最后可以查看启动了的 Pod 集合:

kubectl get pods

你会看到类似如下的 Pod 信息:

NAME             READY   STATUS    RESTARTS   AGE
frontend-b2zdv   1/1     Running   0          6m36s
frontend-vcmts   1/1     Running   0          6m36s
frontend-wtsmm   1/1     Running   0          6m36s

你也可以查看 Pod 的属主引用被设置为前端的 ReplicaSet。 要实现这点,可取回运行中的某个 Pod 的 YAML:

kubectl get pods frontend-b2zdv -o yaml

输出将类似这样,frontend ReplicaSet 的信息被设置在 metadata 的 ownerReferences 字段中:

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: "2020-02-12T07:06:16Z"
  generateName: frontend-
  labels:
    tier: frontend
  name: frontend-b2zdv
  namespace: default
  ownerReferences:
  - apiVersion: apps/v1
    blockOwnerDeletion: true
    controller: true
    kind: ReplicaSet
    name: frontend
    uid: f391f6db-bb9b-4c09-ae74-6a1f77f3d5cf
...

非模板 Pod 的获得

尽管你完全可以直接创建裸的 Pod,强烈建议你确保这些裸的 Pod 并不包含可能与你的某个 ReplicaSet 的选择算符相匹配的标签。原因在于 ReplicaSet 并不仅限于拥有在其模板中设置的 Pod,它还可以像前面小节中所描述的那样获得其他 Pod。

以前面的 frontend ReplicaSet 为例,并在以下清单中指定这些 Pod:

apiVersion: v1
kind: Pod
metadata:
  name: pod1
  labels:
    tier: frontend
spec:
  containers:
  - name: hello1
    image: gcr.io/google-samples/hello-app:2.0

---

apiVersion: v1
kind: Pod
metadata:
  name: pod2
  labels:
    tier: frontend
spec:
  containers:
  - name: hello2
    image: gcr.io/google-samples/hello-app:1.0

由于这些 Pod 没有控制器(Controller,或其他对象)作为其属主引用, 并且其标签与 frontend ReplicaSet 的选择算符匹配,它们会立即被该 ReplicaSet 获取。

假定你在 frontend ReplicaSet 已经被部署之后创建 Pod,并且你已经在 ReplicaSet 中设置了其初始的 Pod 副本数以满足其副本计数需要:

kubectl apply -f https://kubernetes.io/examples/pods/pod-rs.yaml

新的 Pod 会被该 ReplicaSet 获取,并立即被 ReplicaSet 终止, 因为它们的存在会使得 ReplicaSet 中 Pod 个数超出其期望值。

取回 Pod:

kubectl get pods

输出显示新的 Pod 或者已经被终止,或者处于终止过程中:

NAME             READY   STATUS        RESTARTS   AGE
frontend-b2zdv   1/1     Running       0          10m
frontend-vcmts   1/1     Running       0          10m
frontend-wtsmm   1/1     Running       0          10m
pod1             0/1     Terminating   0          1s
pod2             0/1     Terminating   0          1s

如果你先行创建 Pod:

kubectl apply -f https://kubernetes.io/examples/pods/pod-rs.yaml

之后再创建 ReplicaSet:

kubectl apply -f https://kubernetes.io/examples/controllers/frontend.yaml

你会看到 ReplicaSet 已经获得了该 Pod,并仅根据其规约创建新的 Pod, 直到新的 Pod 和原来的 Pod 的总数达到其预期个数。 这时取回 Pod 列表:

kubectl get pods

将会生成下面的输出:

NAME             READY   STATUS    RESTARTS   AGE
frontend-hmmj2   1/1     Running   0          9s
pod1             1/1     Running   0          36s
pod2             1/1     Running   0          36s

采用这种方式,一个 ReplicaSet 中可以包含异质的 Pod 集合。

编写 ReplicaSet 的清单

与所有其他 Kubernetes API 对象一样,ReplicaSet 也需要 apiVersionkind、和 metadata 字段。 对于 ReplicaSet 而言,其 kind 始终是 ReplicaSet。

当控制平面为 ReplicaSet 创建新的 Pod 时,ReplicaSet 的 .metadata.name 是命名这些 Pod 的部分基础。ReplicaSet 的名称必须是一个合法的 DNS 子域值, 但这可能对 Pod 的主机名产生意外的结果。为获得最佳兼容性,名称应遵循更严格的 DNS 标签规则。

ReplicaSet 也需要 .spec 部分。

Pod 模板

.spec.template 是一个 Pod 模板, 要求设置标签。在 frontend.yaml 示例中,我们指定了标签 tier: frontend。 注意不要将标签与其他控制器的选择算符重叠,否则那些控制器会尝试收养此 Pod。

对于模板的重启策略 字段,.spec.template.spec.restartPolicy,唯一允许的取值是 Always,这也是默认值.

Pod 选择算符

.spec.selector 字段是一个标签选择算符。 如前文中所讨论的,这些是用来标识要被获取的 Pod 的标签。在签名的 frontend.yaml 示例中,选择算符为:

matchLabels:
  tier: frontend

在 ReplicaSet 中,.spec.template.metadata.labels 的值必须与 spec.selector 值相匹配,否则该配置会被 API 拒绝。

Replicas

你可以通过设置 .spec.replicas 来指定要同时运行的 Pod 个数。 ReplicaSet 创建、删除 Pod 以与此值匹配。

如果你没有指定 .spec.replicas,那么默认值为 1。

使用 ReplicaSet

删除 ReplicaSet 和它的 Pod

要删除 ReplicaSet 和它的所有 Pod,使用 kubectl delete 命令。 默认情况下,垃圾收集器 自动删除所有依赖的 Pod。

当使用 REST API 或 client-go 库时,你必须在 -d 选项中将 propagationPolicy 设置为 BackgroundForeground。例如:

kubectl proxy --port=8080
curl -X DELETE  'localhost:8080/apis/apps/v1/namespaces/default/replicasets/frontend' \
  -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Foreground"}' \
  -H "Content-Type: application/json"

只删除 ReplicaSet

你可以只删除 ReplicaSet 而不影响它的各个 Pod,方法是使用 kubectl delete 命令并设置 --cascade=orphan 选项。

当使用 REST API 或 client-go 库时,你必须将 propagationPolicy 设置为 Orphan。 例如:

kubectl proxy --port=8080
curl -X DELETE  'localhost:8080/apis/apps/v1/namespaces/default/replicasets/frontend' \
  -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Orphan"}' \
  -H "Content-Type: application/json"

一旦删除了原来的 ReplicaSet,就可以创建一个新的来替换它。 由于新旧 ReplicaSet 的 .spec.selector 是相同的,新的 ReplicaSet 将接管老的 Pod。 但是,它不会努力使现有的 Pod 与新的、不同的 Pod 模板匹配。 若想要以可控的方式更新 Pod 的规约,可以使用 Deployment 资源,因为 ReplicaSet 并不直接支持滚动更新。

将 Pod 从 ReplicaSet 中隔离

可以通过改变标签来从 ReplicaSet 中移除 Pod。 这种技术可以用来从服务中去除 Pod,以便进行排错、数据恢复等。 以这种方式移除的 Pod 将被自动替换(假设副本的数量没有改变)。

扩缩 ReplicaSet

通过更新 .spec.replicas 字段,ReplicaSet 可以被轻松地进行扩缩。ReplicaSet 控制器能确保匹配标签选择器的数量的 Pod 是可用的和可操作的。

在降低集合规模时,ReplicaSet 控制器通过对可用的所有 Pod 进行排序来优先选择要被删除的那些 Pod。 其一般性算法如下:

  1. 首先选择剔除悬决(Pending,且不可调度)的各个 Pod
  2. 如果设置了 controller.kubernetes.io/pod-deletion-cost 注解,则注解值较小的优先被裁减掉
  3. 所处节点上副本个数较多的 Pod 优先于所处节点上副本较少者
  4. 如果 Pod 的创建时间不同,最近创建的 Pod 优先于早前创建的 Pod 被裁减。 (当 LogarithmicScaleDown 这一特性门控 被启用时,创建时间是按整数幂级来分组的)。

如果以上比较结果都相同,则随机选择。

Pod 删除开销

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

通过使用 controller.kubernetes.io/pod-deletion-cost 注解,用户可以对 ReplicaSet 缩容时要先删除哪些 Pod 设置偏好。

此注解要设置到 Pod 上,取值范围为 [-2147483648, 2147483647]。 所代表的是删除同一 ReplicaSet 中其他 Pod 相比较而言的开销。 删除开销较小的 Pod 比删除开销较高的 Pod 更容易被删除。

Pod 如果未设置此注解,则隐含的设置值为 0。负值也是可接受的。 如果注解值非法,API 服务器会拒绝对应的 Pod。

此功能特性处于 Beta 阶段,默认被启用。你可以通过为 kube-apiserver 和 kube-controller-manager 设置特性门控 PodDeletionCost 来禁用此功能。

使用场景示例

同一应用的不同 Pod 可能其利用率是不同的。在对应用执行缩容操作时, 可能希望移除利用率较低的 Pod。为了避免频繁更新 Pod,应用应该在执行缩容操作之前更新一次 controller.kubernetes.io/pod-deletion-cost 注解值 (将注解值设置为一个与其 Pod 利用率对应的值)。 如果应用自身控制器缩容操作时(例如 Spark 部署的驱动 Pod),这种机制是可以起作用的。

ReplicaSet 作为水平的 Pod 自动扩缩器目标

ReplicaSet 也可以作为水平的 Pod 扩缩器 (HPA) 的目标。也就是说,ReplicaSet 可以被 HPA 自动扩缩。 以下是 HPA 以我们在前一个示例中创建的副本集为目标的示例。

apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: frontend-scaler
spec:
  scaleTargetRef:
    kind: ReplicaSet
    name: frontend
  minReplicas: 3
  maxReplicas: 10
  targetCPUUtilizationPercentage: 50

将这个列表保存到 hpa-rs.yaml 并提交到 Kubernetes 集群,就能创建它所定义的 HPA,进而就能根据复制的 Pod 的 CPU 利用率对目标 ReplicaSet 进行自动扩缩。

kubectl apply -f https://k8s.io/examples/controllers/hpa-rs.yaml

或者,可以使用 kubectl autoscale 命令完成相同的操作(而且它更简单!)

kubectl autoscale rs frontend --max=10 --min=3 --cpu-percent=50

ReplicaSet 的替代方案

Deployment 是一个可以拥有 ReplicaSet 并使用声明式方式在服务器端完成对 Pod 滚动更新的对象。 尽管 ReplicaSet 可以独立使用,目前它们的主要用途是提供给 Deployment 作为编排 Pod 创建、删除和更新的一种机制。当使用 Deployment 时,你不必关心如何管理它所创建的 ReplicaSet,Deployment 拥有并管理其 ReplicaSet。 因此,建议你在需要 ReplicaSet 时使用 Deployment。

裸 Pod

与用户直接创建 Pod 的情况不同,ReplicaSet 会替换那些由于某些原因被删除或被终止的 Pod,例如在节点故障或破坏性的节点维护(如内核升级)的情况下。 因为这个原因,我们建议你使用 ReplicaSet,即使应用程序只需要一个 Pod。 想像一下,ReplicaSet 类似于进程监视器,只不过它在多个节点上监视多个 Pod, 而不是在单个节点上监视单个进程。 ReplicaSet 将本地容器重启的任务委托给了节点上的某个代理(例如,Kubelet)去完成。

Job

使用Job 代替 ReplicaSet, 可以用于那些期望自行终止的 Pod。

DaemonSet

对于管理那些提供主机级别功能(如主机监控和主机日志)的容器, 就要用 DaemonSet 而不用 ReplicaSet。 这些 Pod 的寿命与主机寿命有关:这些 Pod 需要先于主机上的其他 Pod 运行, 并且在机器准备重新启动/关闭时安全地终止。

ReplicationController

ReplicaSet 是 ReplicationController 的后继者。二者目的相同且行为类似,只是 ReplicationController 不支持 标签用户指南 中讨论的基于集合的选择算符需求。 因此,相比于 ReplicationController,应优先考虑 ReplicaSet。

接下来

3.4.2.3 - StatefulSet

StatefulSet 运行一组 Pod,并为每个 Pod 保留一个稳定的标识。 这可用于管理需要持久化存储或稳定、唯一网络标识的应用。

StatefulSet 是用来管理有状态应用的工作负载 API 对象。

StatefulSet 用来管理某 Pod 集合的部署和扩缩, 并为这些 Pod 提供持久存储和持久标识符。

Deployment 类似, StatefulSet 管理基于相同容器规约的一组 Pod。但和 Deployment 不同的是, StatefulSet 为它们的每个 Pod 维护了一个有粘性的 ID。这些 Pod 是基于相同的规约来创建的, 但是不能相互替换:无论怎么调度,每个 Pod 都有一个永久不变的 ID。

如果希望使用存储卷为工作负载提供持久存储,可以使用 StatefulSet 作为解决方案的一部分。 尽管 StatefulSet 中的单个 Pod 仍可能出现故障, 但持久的 Pod 标识符使得将现有卷与替换已失败 Pod 的新 Pod 相匹配变得更加容易。

使用 StatefulSet

StatefulSet 对于需要满足以下一个或多个需求的应用程序很有价值:

  • 稳定的、唯一的网络标识符。
  • 稳定的、持久的存储。
  • 有序的、优雅的部署和扩缩。
  • 有序的、自动的滚动更新。

在上面描述中,“稳定的”意味着 Pod 调度或重调度的整个过程是有持久性的。 如果应用程序不需要任何稳定的标识符或有序的部署、删除或扩缩, 则应该使用由一组无状态的副本控制器提供的工作负载来部署应用程序,比如 Deployment 或者 ReplicaSet 可能更适用于你的无状态应用部署需要。

限制

  • 给定 Pod 的存储必须由 PersistentVolume Provisioner 基于所请求的 storage class 来制备,或者由管理员预先制备。
  • 删除或者扩缩 StatefulSet 并不会删除它关联的存储卷。 这样做是为了保证数据安全,它通常比自动清除 StatefulSet 所有相关的资源更有价值。
  • StatefulSet 当前需要无头服务来负责 Pod 的网络标识。你需要负责创建此服务。
  • 当删除一个 StatefulSet 时,该 StatefulSet 不提供任何终止 Pod 的保证。 为了实现 StatefulSet 中的 Pod 可以有序且体面地终止,可以在删除之前将 StatefulSet 缩容到 0。
  • 在默认 Pod 管理策略(OrderedReady) 时使用滚动更新, 可能进入需要人工干预才能修复的损坏状态。

组件

下面的示例演示了 StatefulSet 的组件。

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx # 必须匹配 .spec.template.metadata.labels
  serviceName: "nginx"
  replicas: 3 # 默认值是 1
  minReadySeconds: 10 # 默认值是 0
  template:
    metadata:
      labels:
        app: nginx # 必须匹配 .spec.selector.matchLabels
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: nginx
        image: registry.k8s.io/nginx-slim:0.8
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "my-storage-class"
      resources:
        requests:
          storage: 1Gi

上述例子中:

  • 名为 nginx 的 Headless Service 用来控制网络域名。
  • 名为 web 的 StatefulSet 有一个 Spec,它表明将在独立的 3 个 Pod 副本中启动 nginx 容器。
  • volumeClaimTemplates 将通过 PersistentVolume 制备程序所准备的 PersistentVolumes 来提供稳定的存储。

StatefulSet 的命名需要遵循 DNS 标签规范。

Pod 选择算符

你必须设置 StatefulSet 的 .spec.selector 字段,使之匹配其在 .spec.template.metadata.labels 中设置的标签。 未指定匹配的 Pod 选择算符将在创建 StatefulSet 期间导致验证错误。

卷申领模板

你可以设置 .spec.volumeClaimTemplates, 它可以使用 PersistentVolume 制备程序所准备的 PersistentVolumes 来提供稳定的存储。

最短就绪秒数

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

.spec.minReadySeconds 是一个可选字段。 它指定新创建的 Pod 应该在没有任何容器崩溃的情况下运行并准备就绪,才能被认为是可用的。 这用于在使用滚动更新策略时检查滚动的进度。 该字段默认为 0(Pod 准备就绪后将被视为可用)。 要了解有关何时认为 Pod 准备就绪的更多信息, 请参阅容器探针

Pod 标识

StatefulSet Pod 具有唯一的标识,该标识包括顺序标识、稳定的网络标识和稳定的存储。 该标识和 Pod 是绑定的,与该 Pod 调度到哪个节点上无关。

序号索引

对于具有 N 个副本的 StatefulSet,该 StatefulSet 中的每个 Pod 将被分配一个整数序号, 该序号在此 StatefulSet 中是唯一的。默认情况下,这些 Pod 将被赋予从 0 到 N-1 的序号。 StatefulSet 的控制器也会添加一个包含此索引的 Pod 标签:apps.kubernetes.io/pod-index

起始序号

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

.spec.ordinals 是一个可选的字段,允许你配置分配给每个 Pod 的整数序号。 该字段默认为 nil 值。你必须启用 StatefulSetStartOrdinal 特性门控才能使用此字段。 一旦启用,你就可以配置以下选项:

  • .spec.ordinals.start:如果 .spec.ordinals.start 字段被设置,则 Pod 将被分配从 .spec.ordinals.start.spec.ordinals.start + .spec.replicas - 1 的序号。

稳定的网络 ID

StatefulSet 中的每个 Pod 根据 StatefulSet 的名称和 Pod 的序号派生出它的主机名。 组合主机名的格式为$(StatefulSet 名称)-$(序号)。 上例将会创建三个名称分别为 web-0、web-1、web-2 的 Pod。 StatefulSet 可以使用无头服务控制它的 Pod 的网络域。管理域的这个服务的格式为: $(服务名称).$(名字空间).svc.cluster.local,其中 cluster.local 是集群域。 一旦每个 Pod 创建成功,就会得到一个匹配的 DNS 子域,格式为: $(pod 名称).$(所属服务的 DNS 域名),其中所属服务由 StatefulSet 的 serviceName 域来设定。

取决于集群域内部 DNS 的配置,有可能无法查询一个刚刚启动的 Pod 的 DNS 命名。 当集群内其他客户端在 Pod 创建完成前发出 Pod 主机名查询时,就会发生这种情况。 负缓存 (在 DNS 中较为常见) 意味着之前失败的查询结果会被记录和重用至少若干秒钟, 即使 Pod 已经正常运行了也是如此。

如果需要在 Pod 被创建之后及时发现它们,可使用以下选项:

  • 直接查询 Kubernetes API(比如,利用 watch 机制)而不是依赖于 DNS 查询
  • 缩短 Kubernetes DNS 驱动的缓存时长(通常这意味着修改 CoreDNS 的 ConfigMap,目前缓存时长为 30 秒)

正如限制中所述, 你需要负责创建无头服务以便为 Pod 提供网络标识。

下面给出一些选择集群域、服务名、StatefulSet 名、及其怎样影响 StatefulSet 的 Pod 上的 DNS 名称的示例:

集群域名服务(名字空间/名字)StatefulSet(名字空间/名字)StatefulSet 域名Pod DNSPod 主机名
cluster.localdefault/nginxdefault/webnginx.default.svc.cluster.localweb-{0..N-1}.nginx.default.svc.cluster.localweb-{0..N-1}
cluster.localfoo/nginxfoo/webnginx.foo.svc.cluster.localweb-{0..N-1}.nginx.foo.svc.cluster.localweb-{0..N-1}
kube.localfoo/nginxfoo/webnginx.foo.svc.kube.localweb-{0..N-1}.nginx.foo.svc.kube.localweb-{0..N-1}

稳定的存储

对于 StatefulSet 中定义的每个 VolumeClaimTemplate,每个 Pod 接收到一个 PersistentVolumeClaim。 在上面的 nginx 示例中,每个 Pod 将会得到基于 StorageClass my-storage-class 制备的 1 GiB 的 PersistentVolume。如果没有指定 StorageClass,就会使用默认的 StorageClass。 当一个 Pod 被调度(重新调度)到节点上时,它的 volumeMounts 会挂载与其 PersistentVolumeClaims 相关联的 PersistentVolume。 请注意,当 Pod 或者 StatefulSet 被删除时,与 PersistentVolumeClaims 相关联的 PersistentVolume 并不会被删除。要删除它必须通过手动方式来完成。

Pod 名称标签

当 StatefulSet 控制器创建 Pod 时, 它会添加一个标签 statefulset.kubernetes.io/pod-name,该标签值设置为 Pod 名称。 这个标签允许你给 StatefulSet 中的特定 Pod 绑定一个 Service。

Pod 索引标签

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

当 StatefulSet 控制器创建一个 Pod 时, 新的 Pod 会被打上 apps.kubernetes.io/pod-index 标签。标签的取值为 Pod 的序号索引。 此标签使你能够将流量路由到特定索引值的 Pod、使用 Pod 索引标签来过滤日志或度量值等等。 注意要使用这一特性需要启用特性门控 PodIndexLabel,而该门控默认是被启用的。

部署和扩缩保证

  • 对于包含 N 个 副本的 StatefulSet,当部署 Pod 时,它们是依次创建的,顺序为 0..N-1
  • 当删除 Pod 时,它们是逆序终止的,顺序为 N-1..0
  • 在将扩缩操作应用到 Pod 之前,它前面的所有 Pod 必须是 Running 和 Ready 状态。
  • 在一个 Pod 终止之前,所有的继任者必须完全关闭。

StatefulSet 不应将 pod.Spec.TerminationGracePeriodSeconds 设置为 0。 这种做法是不安全的,要强烈阻止。 更多的解释请参考强制删除 StatefulSet Pod

在上面的 nginx 示例被创建后,会按照 web-0、web-1、web-2 的顺序部署三个 Pod。 在 web-0 进入 Running 和 Ready 状态前不会部署 web-1。在 web-1 进入 Running 和 Ready 状态前不会部署 web-2。 如果 web-1 已经处于 Running 和 Ready 状态,而 web-2 尚未部署,在此期间发生了 web-0 运行失败,那么 web-2 将不会被部署,要等到 web-0 部署完成并进入 Running 和 Ready 状态后,才会部署 web-2。

如果用户想将示例中的 StatefulSet 扩缩为 replicas=1,首先被终止的是 web-2。 在 web-2 没有被完全停止和删除前,web-1 不会被终止。 当 web-2 已被终止和删除、web-1 尚未被终止,如果在此期间发生 web-0 运行失败, 那么就不会终止 web-1,必须等到 web-0 进入 Running 和 Ready 状态后才会终止 web-1。

Pod 管理策略

StatefulSet 允许你放宽其排序保证, 同时通过它的 .spec.podManagementPolicy 域保持其唯一性和身份保证。

OrderedReady Pod 管理

OrderedReady Pod 管理是 StatefulSet 的默认设置。 它实现了上面描述的功能。

并行 Pod 管理

Parallel Pod 管理让 StatefulSet 控制器并行的启动或终止所有的 Pod, 启动或者终止其他 Pod 前,无需等待 Pod 进入 Running 和 Ready 或者完全停止状态。 这个选项只会影响扩缩操作的行为,更新则不会被影响。

更新策略

StatefulSet 的 .spec.updateStrategy 字段让你可以配置和禁用掉自动滚动更新 Pod 的容器、标签、资源请求或限制、以及注解。有两个允许的值:

OnDelete
当 StatefulSet 的 .spec.updateStrategy.type 设置为 OnDelete 时, 它的控制器将不会自动更新 StatefulSet 中的 Pod。 用户必须手动删除 Pod 以便让控制器创建新的 Pod,以此来对 StatefulSet 的 .spec.template 的变动作出反应。
RollingUpdate
RollingUpdate 更新策略对 StatefulSet 中的 Pod 执行自动的滚动更新。这是默认的更新策略。

滚动更新

当 StatefulSet 的 .spec.updateStrategy.type 被设置为 RollingUpdate 时, StatefulSet 控制器会删除和重建 StatefulSet 中的每个 Pod。 它将按照与 Pod 终止相同的顺序(从最大序号到最小序号)进行,每次更新一个 Pod。

Kubernetes 控制平面会等到被更新的 Pod 进入 Running 和 Ready 状态,然后再更新其前身。 如果你设置了 .spec.minReadySeconds(查看最短就绪秒数), 控制平面在 Pod 就绪后会额外等待一定的时间再执行下一步。

分区滚动更新

通过声明 .spec.updateStrategy.rollingUpdate.partition 的方式,RollingUpdate 更新策略可以实现分区。 如果声明了一个分区,当 StatefulSet 的 .spec.template 被更新时, 所有序号大于等于该分区序号的 Pod 都会被更新。 所有序号小于该分区序号的 Pod 都不会被更新,并且,即使它们被删除也会依据之前的版本进行重建。 如果 StatefulSet 的 .spec.updateStrategy.rollingUpdate.partition 大于它的 .spec.replicas,则对它的 .spec.template 的更新将不会传递到它的 Pod。 在大多数情况下,你不需要使用分区,但如果你希望进行阶段更新、执行金丝雀或执行分阶段上线,则这些分区会非常有用。

最大不可用 Pod

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

你可以通过指定 .spec.updateStrategy.rollingUpdate.maxUnavailable 字段来控制更新期间不可用的 Pod 的最大数量。 该值可以是绝对值(例如,“5”)或者是期望 Pod 个数的百分比(例如,10%)。 绝对值是根据百分比值四舍五入计算的。 该字段不能为 0。默认设置为 1。

该字段适用于 0replicas - 1 范围内的所有 Pod。 如果在 0replicas - 1 范围内存在不可用 Pod,这类 Pod 将被计入 maxUnavailable 值。

强制回滚

在默认 Pod 管理策略(OrderedReady) 下使用滚动更新, 可能进入需要人工干预才能修复的损坏状态。

如果更新后 Pod 模板配置进入无法运行或就绪的状态(例如, 由于错误的二进制文件或应用程序级配置错误),StatefulSet 将停止回滚并等待。

在这种状态下,仅将 Pod 模板还原为正确的配置是不够的。 由于已知问题,StatefulSet 将继续等待损坏状态的 Pod 准备就绪(永远不会发生),然后再尝试将其恢复为正常工作配置。

恢复模板后,还必须删除 StatefulSet 尝试使用错误的配置来运行的 Pod。这样, StatefulSet 才会开始使用被还原的模板来重新创建 Pod。

PersistentVolumeClaim 保留

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

在 StatefulSet 的生命周期中,可选字段 .spec.persistentVolumeClaimRetentionPolicy 控制是否删除以及如何删除 PVC。 使用该字段,你必须在 API 服务器和控制器管理器启用 StatefulSetAutoDeletePVC 特性门控。 启用后,你可以为每个 StatefulSet 配置两个策略:

whenDeleted
配置删除 StatefulSet 时应用的卷保留行为。
whenScaled
配置当 StatefulSet 的副本数减少时应用的卷保留行为;例如,缩小集合时。

对于你可以配置的每个策略,你可以将值设置为 DeleteRetain

Delete
对于受策略影响的每个 Pod,基于 StatefulSet 的 volumeClaimTemplate 字段创建的 PVC 都会被删除。 使用 whenDeleted 策略,所有来自 volumeClaimTemplate 的 PVC 在其 Pod 被删除后都会被删除。 使用 whenScaled 策略,只有与被缩减的 Pod 副本对应的 PVC 在其 Pod 被删除后才会被删除。
Retain(默认)
来自 volumeClaimTemplate 的 PVC 在 Pod 被删除时不受影响。这是此新功能之前的行为。

请记住,这些策略适用于由于 StatefulSet 被删除或被缩小而被删除的 Pod。 例如,如果与 StatefulSet 关联的 Pod 由于节点故障而失败, 并且控制平面创建了替换 Pod,则 StatefulSet 保留现有的 PVC。 现有卷不受影响,集群会将其附加到新 Pod 即将启动的节点上。

策略的默认值为 Retain,与此新功能之前的 StatefulSet 行为相匹配。

这是一个示例策略。

apiVersion: apps/v1
kind: StatefulSet
...
spec:
  persistentVolumeClaimRetentionPolicy:
    whenDeleted: Retain
    whenScaled: Delete
...

StatefulSet 控制器为其 PVC 添加了属主引用, 这些 PVC 在 Pod 终止后被垃圾回收器删除。 这使 Pod 能够在删除 PVC 之前(以及在删除后备 PV 和卷之前,取决于保留策略)干净地卸载所有卷。 当你设置 whenDeleted 删除策略,对 StatefulSet 实例的属主引用放置在与该 StatefulSet 关联的所有 PVC 上。

whenScaled 策略必须仅在 Pod 缩减时删除 PVC,而不是在 Pod 因其他原因被删除时删除。 执行协调操作时,StatefulSet 控制器将其所需的副本数与集群上实际存在的 Pod 进行比较。 对于 StatefulSet 中的所有 Pod 而言,如果其 ID 大于副本数,则将被废弃并标记为需要删除。 如果 whenScaled 策略是 Delete,则在删除 Pod 之前, 首先将已销毁的 Pod 设置为与 StatefulSet 模板对应的 PVC 的属主。 这会导致 PVC 仅在已废弃的 Pod 终止后被垃圾收集。

这意味着如果控制器崩溃并重新启动,在其属主引用更新到适合策略的 Pod 之前,不会删除任何 Pod。 如果在控制器关闭时强制删除了已废弃的 Pod,则属主引用可能已被设置,也可能未被设置,具体取决于控制器何时崩溃。 更新属主引用可能需要几个协调循环,因此一些已废弃的 Pod 可能已经被设置了属主引用,而其他可能没有。 出于这个原因,我们建议等待控制器恢复,控制器将在终止 Pod 之前验证属主引用。 如果这不可行,则操作员应验证 PVC 上的属主引用,以确保在强制删除 Pod 时删除预期的对象。

副本数

.spec.replicas 是一个可选字段,用于指定所需 Pod 的数量。它的默认值为 1。

如果你手动扩缩已部署的负载,例如通过 kubectl scale statefulset statefulset --replicas=X, 然后根据清单更新 StatefulSet(例如:通过运行 kubectl apply -f statefulset.yaml), 那么应用该清单的操作会覆盖你之前所做的手动扩缩。

如果 HorizontalPodAutoscaler (或任何类似的水平扩缩 API)正在管理 StatefulSet 的扩缩, 请不要设置 .spec.replicas。 相反,允许 Kubernetes 控制平面自动管理 .spec.replicas 字段。

接下来

3.4.2.4 - DaemonSet

DaemonSet 定义了提供节点本地设施的 Pod。这些设施可能对于集群的运行至关重要,例如网络辅助工具,或者作为 add-on 的一部分。

DaemonSet 确保全部(或者某些)节点上运行一个 Pod 的副本。 当有节点加入集群时, 也会为他们新增一个 Pod 。 当有节点从集群移除时,这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod。

DaemonSet 的一些典型用法:

  • 在每个节点上运行集群守护进程
  • 在每个节点上运行日志收集守护进程
  • 在每个节点上运行监控守护进程

一种简单的用法是为每种类型的守护进程在所有的节点上都启动一个 DaemonSet。 一个稍微复杂的用法是为同一种守护进程部署多个 DaemonSet;每个具有不同的标志, 并且对不同硬件类型具有不同的内存、CPU 要求。

编写 DaemonSet Spec

创建 DaemonSet

你可以在 YAML 文件中描述 DaemonSet。 例如,下面的 daemonset.yaml 文件描述了一个运行 fluentd-elasticsearch Docker 镜像的 DaemonSet:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd-elasticsearch
  namespace: kube-system
  labels:
    k8s-app: fluentd-logging
spec:
  selector:
    matchLabels:
      name: fluentd-elasticsearch
  template:
    metadata:
      labels:
        name: fluentd-elasticsearch
    spec:
      tolerations:
      # 这些容忍度设置是为了让该守护进程集在控制平面节点上运行
      # 如果你不希望自己的控制平面节点运行 Pod,可以删除它们
      - key: node-role.kubernetes.io/control-plane
        operator: Exists
        effect: NoSchedule
      - key: node-role.kubernetes.io/master
        operator: Exists
        effect: NoSchedule
      containers:
      - name: fluentd-elasticsearch
        image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2
        resources:
          limits:
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 200Mi
        volumeMounts:
        - name: varlog
          mountPath: /var/log
      # 可能需要设置较高的优先级类以确保 DaemonSet Pod 可以抢占正在运行的 Pod
      # priorityClassName: important
      terminationGracePeriodSeconds: 30
      volumes:
      - name: varlog
        hostPath:
          path: /var/log

基于 YAML 文件创建 DaemonSet:

kubectl apply -f https://k8s.io/examples/controllers/daemonset.yaml

必需字段

与所有其他 Kubernetes 配置一样,DaemonSet 也需要 apiVersionkindmetadata 字段。 有关使用这些配置文件的通用信息, 参见运行无状态应用使用 kubectl 管理对象

DaemonSet 对象的名称必须是一个合法的 DNS 子域名

DaemonSet 也需要 .spec 节区。

Pod 模板

.spec 中唯一必需的字段是 .spec.template

.spec.template 是一个 Pod 模板。 除了它是嵌套的,因而不具有 apiVersionkind 字段之外,它与 Pod 具有相同的 schema。

除了 Pod 必需字段外,在 DaemonSet 中的 Pod 模板必须指定合理的标签(查看 Pod 选择算符)。

在 DaemonSet 中的 Pod 模板必须具有一个值为 AlwaysRestartPolicy。 当该值未指定时,默认是 Always

Pod 选择算符

.spec.selector 字段表示 Pod 选择算符,它与 Job.spec.selector 的作用是相同的。

你必须指定与 .spec.template 的标签匹配的 Pod 选择算符。 此外,一旦创建了 DaemonSet,它的 .spec.selector 就不能修改。 修改 Pod 选择算符可能导致 Pod 意外悬浮,并且这对用户来说是费解的。

spec.selector 是一个对象,如下两个字段组成:

  • matchLabels - 与 ReplicationController.spec.selector 的作用相同。
  • matchExpressions - 允许构建更加复杂的选择器,可以通过指定 key、value 列表以及将 key 和 value 列表关联起来的 Operator。

当上述两个字段都指定时,结果会按逻辑与(AND)操作处理。

.spec.selector 必须与 .spec.template.metadata.labels 相匹配。 如果配置中这两个字段不匹配,则会被 API 拒绝。

在选定的节点上运行 Pod

如果指定了 .spec.template.spec.nodeSelector,DaemonSet 控制器将在能够与 Node 选择算符匹配的节点上创建 Pod。 类似这种情况,可以指定 .spec.template.spec.affinity,之后 DaemonSet 控制器将在能够与节点亲和性匹配的节点上创建 Pod。 如果根本就没有指定,则 DaemonSet Controller 将在所有节点上创建 Pod。

Daemon Pods 是如何被调度的

DaemonSet 可用于确保所有符合条件的节点都运行该 Pod 的一个副本。 DaemonSet 控制器为每个符合条件的节点创建一个 Pod,并添加 Pod 的 spec.affinity.nodeAffinity 字段以匹配目标主机。Pod 被创建之后,默认的调度程序通常通过设置 .spec.nodeName 字段来接管 Pod 并将 Pod 绑定到目标主机。如果新的 Pod 无法放在节点上,则默认的调度程序可能会根据新 Pod 的优先级抢占 (驱逐)某些现存的 Pod。

用户通过设置 DaemonSet 的 .spec.template.spec.schedulerName 字段,可以为 DaemonSet 的 Pod 指定不同的调度程序。

当评估符合条件的节点时,原本在 .spec.template.spec.affinity.nodeAffinity 字段上指定的节点亲和性将由 DaemonSet 控制器进行考量,但在创建的 Pod 上会被替换为与符合条件的节点名称匹配的节点亲和性。

ScheduleDaemonSetPods 允许你使用默认调度器而不是 DaemonSet 控制器来调度这些 DaemonSet, 方法是将 NodeAffinity 条件而不是 .spec.nodeName 条件添加到这些 DaemonSet Pod。 默认调度器接下来将 Pod 绑定到目标主机。 如果 DaemonSet Pod 的节点亲和性配置已存在,则被替换 (原始的节点亲和性配置在选择目标主机之前被考虑)。 DaemonSet 控制器仅在创建或修改 DaemonSet Pod 时执行这些操作, 并且不会更改 DaemonSet 的 spec.template

nodeAffinity:
  requiredDuringSchedulingIgnoredDuringExecution:
    nodeSelectorTerms:
    - matchFields:
      - key: metadata.name
        operator: In
        values:
        - target-host-name

污点和容忍度

DaemonSet 控制器会自动将一组容忍度添加到 DaemonSet Pod:

DaemonSet Pod 适用的容忍度
容忍度键名效果描述
node.kubernetes.io/not-readyNoExecuteDaemonSet Pod 可以被调度到不健康或还不准备接受 Pod 的节点上。在这些节点上运行的所有 DaemonSet Pod 将不会被驱逐。
node.kubernetes.io/unreachableNoExecuteDaemonSet Pod 可以被调度到从节点控制器不可达的节点上。在这些节点上运行的所有 DaemonSet Pod 将不会被驱逐。
node.kubernetes.io/disk-pressureNoScheduleDaemonSet Pod 可以被调度到具有磁盘压力问题的节点上。
node.kubernetes.io/memory-pressureNoScheduleDaemonSet Pod 可以被调度到具有内存压力问题的节点上。
node.kubernetes.io/pid-pressureNoScheduleDaemonSet Pod 可以被调度到具有进程压力问题的节点上。
node.kubernetes.io/unschedulableNoScheduleDaemonSet Pod 可以被调度到不可调度的节点上。
node.kubernetes.io/network-unavailableNoSchedule仅针对请求主机联网的 DaemonSet Pod 添加此容忍度,即 Pod 具有 spec.hostNetwork: true。这些 DaemonSet Pod 可以被调度到网络不可用的节点上。

你也可以在 DaemonSet 的 Pod 模板中定义自己的容忍度并将其添加到 DaemonSet Pod。

因为 DaemonSet 控制器自动设置 node.kubernetes.io/unschedulable:NoSchedule 容忍度, 所以 Kubernetes 可以在标记为不可调度的节点上运行 DaemonSet Pod。

如果你使用 DaemonSet 提供重要的节点级别功能, 例如集群联网, Kubernetes 在节点就绪之前将 DaemonSet Pod 放到节点上会很有帮助。 例如,如果没有这种特殊的容忍度,因为网络插件未在节点上运行,所以你可能会在未标记为就绪的节点上陷入死锁状态, 同时因为该节点还未就绪,所以网络插件不会在该节点上运行。

与 Daemon Pod 通信

与 DaemonSet 中的 Pod 进行通信的几种可能模式如下:

  • 推送(Push):配置 DaemonSet 中的 Pod,将更新发送到另一个服务,例如统计数据库。 这些服务没有客户端。

  • NodeIP 和已知端口:DaemonSet 中的 Pod 可以使用 hostPort,从而可以通过节点 IP 访问到 Pod。客户端能通过某种方法获取节点 IP 列表,并且基于此也可以获取到相应的端口。

  • DNS:创建具有相同 Pod 选择算符的无头服务, 通过使用 endpoints 资源或从 DNS 中检索到多个 A 记录来发现 DaemonSet。

  • Service:创建具有相同 Pod 选择算符的服务,并使用该服务随机访问到某个节点上的守护进程(没有办法访问到特定节点)。

更新 DaemonSet

如果节点的标签被修改,DaemonSet 将立刻向新匹配上的节点添加 Pod, 同时删除不匹配的节点上的 Pod。

你可以修改 DaemonSet 创建的 Pod。不过并非 Pod 的所有字段都可更新。 下次当某节点(即使具有相同的名称)被创建时,DaemonSet 控制器还会使用最初的模板。

你可以删除一个 DaemonSet。如果使用 kubectl 并指定 --cascade=orphan 选项, 则 Pod 将被保留在节点上。接下来如果创建使用相同选择算符的新 DaemonSet, 新的 DaemonSet 会收养已有的 Pod。 如果有 Pod 需要被替换,DaemonSet 会根据其 updateStrategy 来替换。

你可以对 DaemonSet 执行滚动更新操作。

DaemonSet 的替代方案

init 脚本

直接在节点上启动守护进程(例如使用 initupstartdsystemd)的做法当然是可行的。 不过,基于 DaemonSet 来运行这些进程有如下一些好处:

  • 像所运行的其他应用一样,DaemonSet 具备为守护进程提供监控和日志管理的能力。

  • 为守护进程和应用所使用的配置语言和工具(如 Pod 模板、kubectl)是相同的。

  • 在资源受限的容器中运行守护进程能够增加守护进程和应用容器的隔离性。 然而,这一点也可以通过在容器中运行守护进程但却不在 Pod 中运行之来实现。

裸 Pod

直接创建 Pod并指定其运行在特定的节点上也是可以的。 然而,DaemonSet 能够替换由于任何原因(例如节点失败、例行节点维护、内核升级) 而被删除或终止的 Pod。 由于这个原因,你应该使用 DaemonSet 而不是单独创建 Pod。

静态 Pod

通过在一个指定的、受 kubelet 监视的目录下编写文件来创建 Pod 也是可行的。 这类 Pod 被称为静态 Pod。 不像 DaemonSet,静态 Pod 不受 kubectl 和其它 Kubernetes API 客户端管理。 静态 Pod 不依赖于 API 服务器,这使得它们在启动引导新集群的情况下非常有用。 此外,静态 Pod 在将来可能会被废弃。

Deployment

DaemonSet 与 Deployment 非常类似, 它们都能创建 Pod,并且 Pod 中的进程都不希望被终止(例如,Web 服务器、存储服务器)。

建议为无状态的服务使用 Deployment,比如前端服务。 对这些服务而言,对副本的数量进行扩缩容、平滑升级,比精确控制 Pod 运行在某个主机上要重要得多。 当需要 Pod 副本总是运行在全部或特定主机上,并且当该 DaemonSet 提供了节点级别的功能(允许其他 Pod 在该特定节点上正确运行)时, 应该使用 DaemonSet。

例如,网络插件通常包含一个以 DaemonSet 运行的组件。 这个 DaemonSet 组件确保它所在的节点的集群网络正常工作。

接下来

3.4.2.5 - Job

Job 表示一次性任务,运行完成后就会停止。

Job 会创建一个或者多个 Pod,并将继续重试 Pod 的执行,直到指定数量的 Pod 成功终止。 随着 Pod 成功结束,Job 跟踪记录成功完成的 Pod 个数。 当数量达到指定的成功个数阈值时,任务(即 Job)结束。 删除 Job 的操作会清除所创建的全部 Pod。 挂起 Job 的操作会删除 Job 的所有活跃 Pod,直到 Job 被再次恢复执行。

一种简单的使用场景下,你会创建一个 Job 对象以便以一种可靠的方式运行某 Pod 直到完成。 当第一个 Pod 失败或者被删除(比如因为节点硬件失效或者重启)时,Job 对象会启动一个新的 Pod。

你也可以使用 Job 以并行的方式运行多个 Pod。

如果你想按某种排期表(Schedule)运行 Job(单个任务或多个并行任务),请参阅 CronJob

运行示例 Job

下面是一个 Job 配置示例。它负责计算 π 到小数点后 2000 位,并将结果打印出来。 此计算大约需要 10 秒钟完成。

apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  template:
    spec:
      containers:
      - name: pi
        image: perl:5.34.0
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never
  backoffLimit: 4

你可以使用下面的命令来运行此示例:

kubectl apply -f https://kubernetes.io/examples/controllers/job.yaml

输出类似于:

job.batch/pi created

使用 kubectl 来检查 Job 的状态:


Name:           pi
Namespace:      default
Selector:       batch.kubernetes.io/controller-uid=c9948307-e56d-4b5d-8302-ae2d7b7da67c
Labels:         batch.kubernetes.io/controller-uid=c9948307-e56d-4b5d-8302-ae2d7b7da67c
                batch.kubernetes.io/job-name=pi
                ...
Annotations:    batch.kubernetes.io/job-tracking: ""
Parallelism:    1
Completions:    1
Start Time:     Mon, 02 Dec 2019 15:20:11 +0200
Completed At:   Mon, 02 Dec 2019 15:21:16 +0200
Duration:       65s
Pods Statuses:  0 Running / 1 Succeeded / 0 Failed
Pod Template:
  Labels:  batch.kubernetes.io/controller-uid=c9948307-e56d-4b5d-8302-ae2d7b7da67c
           batch.kubernetes.io/job-name=pi
  Containers:
   pi:
    Image:      perl:5.34.0
    Port:       <none>
    Host Port:  <none>
    Command:
      perl
      -Mbignum=bpi
      -wle
      print bpi(2000)
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Events:
  Type    Reason            Age   From            Message
  ----    ------            ----  ----            -------
  Normal  SuccessfulCreate  21s   job-controller  Created pod: pi-xf9p4
  Normal  Completed         18s   job-controller  Job completed


apiVersion: batch/v1
kind: Job
metadata:
  annotations: batch.kubernetes.io/job-tracking: ""
             ...  
  creationTimestamp: "2022-11-10T17:53:53Z"
  generation: 1
  labels:
    batch.kubernetes.io/controller-uid: 863452e6-270d-420e-9b94-53a54146c223
    batch.kubernetes.io/job-name: pi
  name: pi
  namespace: default
  resourceVersion: "4751"
  uid: 204fb678-040b-497f-9266-35ffa8716d14
spec:
  backoffLimit: 4
  completionMode: NonIndexed
  completions: 1
  parallelism: 1
  selector:
    matchLabels:
      batch.kubernetes.io/controller-uid: 863452e6-270d-420e-9b94-53a54146c223
  suspend: false
  template:
    metadata:
      creationTimestamp: null
      labels:
        batch.kubernetes.io/controller-uid: 863452e6-270d-420e-9b94-53a54146c223
        batch.kubernetes.io/job-name: pi
    spec:
      containers:
      - command:
        - perl
        - -Mbignum=bpi
        - -wle
        - print bpi(2000)
        image: perl:5.34.0
        imagePullPolicy: IfNotPresent
        name: pi
        resources: {}
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
      dnsPolicy: ClusterFirst
      restartPolicy: Never
      schedulerName: default-scheduler
      securityContext: {}
      terminationGracePeriodSeconds: 30
status:
  active: 1
  ready: 0
  startTime: "2022-11-10T17:53:57Z"
  uncountedTerminatedPods: {}

要查看 Job 对应的已完成的 Pod,可以执行 kubectl get pods

要以机器可读的方式列举隶属于某 Job 的全部 Pod,你可以使用类似下面这条命令:

pods=$(kubectl get pods --selector=batch.kubernetes.io/job-name=pi --output=jsonpath='{.items[*].metadata.name}')
echo $pods

输出类似于:

pi-5rwd7

这里,选择算符与 Job 的选择算符相同。--output=jsonpath 选项给出了一个表达式, 用来从返回的列表中提取每个 Pod 的 name 字段。

查看其中一个 Pod 的标准输出:

kubectl logs $pods

另外一种查看 Job 日志的方法:

kubectl logs jobs/pi

输出类似于:

3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989380952572010654858632788659361533818279682303019520353018529689957736225994138912497217752834791315155748572424541506959508295331168617278558890750983817546374649393192550604009277016711390098488240128583616035637076601047101819429555961989467678374494482553797747268471040475346462080466842590694912933136770289891521047521620569660240580381501935112533824300355876402474964732639141992726042699227967823547816360093417216412199245863150302861829745557067498385054945885869269956909272107975093029553211653449872027559602364806654991198818347977535663698074265425278625518184175746728909777727938000816470600161452491921732172147723501414419735685481613611573525521334757418494684385233239073941433345477624168625189835694855620992192221842725502542568876717904946016534668049886272327917860857843838279679766814541009538837863609506800642251252051173929848960841284886269456042419652850222106611863067442786220391949450471237137869609563643719172874677646575739624138908658326459958133904780275901

编写 Job 规约

与 Kubernetes 中其他资源的配置类似,Job 也需要 apiVersionkindmetadata 字段。

当控制面为 Job 创建新的 Pod 时,Job 的 .metadata.name 是命名这些 Pod 的基础组成部分。 Job 的名字必须是合法的 DNS 子域名值, 但这可能对 Pod 主机名产生意料之外的结果。为了获得最佳兼容性,此名字应遵循更严格的 DNS 标签规则。 即使该名字被要求遵循 DNS 子域名规则,也不得超过 63 个字符。

Job 配置还需要一个 .spec

Job 标签

Job 标签将为 job-namecontroller-uid 加上 batch.kubernetes.io/ 前缀。

Pod 模板

Job 的 .spec 中只有 .spec.template 是必需的字段。

字段 .spec.template 的值是一个 Pod 模板。 其定义规范与 Pod 完全相同,只是其中不再需要 apiVersionkind 字段。

除了作为 Pod 所必需的字段之外,Job 中的 Pod 模板必须设置合适的标签 (参见 Pod 选择算符)和合适的重启策略。

Job 中 Pod 的 RestartPolicy 只能设置为 NeverOnFailure 之一。

Pod 选择算符

字段 .spec.selector 是可选的。在绝大多数场合,你都不需要为其赋值。 参阅设置自己的 Pod 选择算符

Job 的并行执行

适合以 Job 形式来运行的任务主要有三种:

  1. 非并行 Job:
    • 通常只启动一个 Pod,除非该 Pod 失败。
    • 当 Pod 成功终止时,立即视 Job 为完成状态。
  2. 具有确定完成计数的并行 Job:
    • .spec.completions 字段设置为非 0 的正数值。
    • Job 用来代表整个任务,当成功的 Pod 个数达到 .spec.completions 时,Job 被视为完成。
    • 当使用 .spec.completionMode="Indexed" 时,每个 Pod 都会获得一个不同的 索引值,介于 0 和 .spec.completions-1 之间。
  3. 工作队列的并行 Job:
    • 不设置 spec.completions,默认值为 .spec.parallelism
    • 多个 Pod 之间必须相互协调,或者借助外部服务确定每个 Pod 要处理哪个工作条目。 例如,任一 Pod 都可以从工作队列中取走最多 N 个工作条目。
    • 每个 Pod 都可以独立确定是否其它 Pod 都已完成,进而确定 Job 是否完成。
    • 当 Job 中任何 Pod 成功终止,不再创建新 Pod。
    • 一旦至少 1 个 Pod 成功完成,并且所有 Pod 都已终止,即可宣告 Job 成功完成。
    • 一旦任何 Pod 成功退出,任何其它 Pod 都不应再对此任务执行任何操作或生成任何输出。 所有 Pod 都应启动退出过程。

对于非并行的 Job,你可以不设置 spec.completionsspec.parallelism。 这两个属性都不设置时,均取默认值 1。

对于确定完成计数类型的 Job,你应该设置 .spec.completions 为所需要的完成个数。 你可以设置 .spec.parallelism,也可以不设置。其默认值为 1。

对于一个工作队列 Job,你不可以设置 .spec.completions,但要将.spec.parallelism 设置为一个非负整数。

关于如何利用不同类型的 Job 的更多信息,请参见 Job 模式一节。

控制并行性

并行性请求(.spec.parallelism)可以设置为任何非负整数。 如果未设置,则默认为 1。 如果设置为 0,则 Job 相当于启动之后便被暂停,直到此值被增加。

实际并行性(在任意时刻运行状态的 Pod 个数)可能比并行性请求略大或略小, 原因如下:

  • 对于确定完成计数 Job,实际上并行执行的 Pod 个数不会超出剩余的完成数。 如果 .spec.parallelism 值较高,会被忽略。
  • 对于工作队列 Job,有任何 Job 成功结束之后,不会有新的 Pod 启动。 不过,剩下的 Pod 允许执行完毕。
  • 如果 Job 控制器 没有来得及作出响应,或者
  • 如果 Job 控制器因为任何原因(例如,缺少 ResourceQuota 或者没有权限)无法创建 Pod。 Pod 个数可能比请求的数目小。
  • Job 控制器可能会因为之前同一 Job 中 Pod 失效次数过多而压制新 Pod 的创建。
  • 当 Pod 处于体面终止进程中,需要一定时间才能停止。

完成模式

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

带有确定完成计数的 Job,即 .spec.completions 不为 null 的 Job, 都可以在其 .spec.completionMode 中设置完成模式:

  • NonIndexed(默认值):当成功完成的 Pod 个数达到 .spec.completions 所 设值时认为 Job 已经完成。换言之,每个 Job 完成事件都是独立无关且同质的。 要注意的是,当 .spec.completions 取值为 null 时,Job 被隐式处理为 NonIndexed

  • Indexed:Job 的 Pod 会获得对应的完成索引,取值为 0 到 .spec.completions-1。 该索引可以通过四种方式获取:

    • Pod 注解 batch.kubernetes.io/job-completion-index
    • Pod 标签 batch.kubernetes.io/job-completion-index(适用于 v1.28 及更高版本)。 请注意,必须启用 PodIndexLabel 特性门控才能使用此标签,默认被启用。
    • 作为 Pod 主机名的一部分,遵循模式 $(job-name)-$(index)。 当你同时使用带索引的 Job(Indexed Job)与 服务(Service), Job 中的 Pod 可以通过 DNS 使用确切的主机名互相寻址。 有关如何配置的更多信息,请参阅带 Pod 间通信的 Job
    • 对于容器化的任务,在环境变量 JOB_COMPLETION_INDEX 中。

    当每个索引都对应一个成功完成的 Pod 时,Job 被认为是已完成的。 关于如何使用这种模式的更多信息,可参阅 用带索引的 Job 执行基于静态任务分配的并行处理

处理 Pod 和容器失效

Pod 中的容器可能因为多种不同原因失效,例如因为其中的进程退出时返回值非零, 或者容器因为超出内存约束而被杀死等等。 如果发生这类事件,并且 .spec.template.spec.restartPolicy = "OnFailure", Pod 则继续留在当前节点,但容器会被重新运行。 因此,你的程序需要能够处理在本地被重启的情况,或者要设置 .spec.template.spec.restartPolicy = "Never"。 关于 restartPolicy 的更多信息,可参阅 Pod 生命周期

整个 Pod 也可能会失败,且原因各不相同。 例如,当 Pod 启动时,节点失效(被升级、被重启、被删除等)或者其中的容器失败而 .spec.template.spec.restartPolicy = "Never"。 当 Pod 失败时,Job 控制器会启动一个新的 Pod。 这意味着,你的应用需要处理在一个新 Pod 中被重启的情况。 尤其是应用需要处理之前运行所产生的临时文件、锁、不完整的输出等问题。

默认情况下,每个 Pod 失效都被计入 .spec.backoffLimit 限制, 请参阅 Pod 回退失效策略。 但你可以通过设置 Job 的 Pod 失效策略自定义对 Pod 失效的处理方式。

此外,你可以通过设置 .spec.backoffLimitPerIndex 字段, 选择为 Indexed Job 的每个索引独立计算 Pod 失败次数 (细节参阅逐索引的回退限制)。

注意,即使你将 .spec.parallelism 设置为 1,且将 .spec.completions 设置为 1,并且 .spec.template.spec.restartPolicy 设置为 "Never",同一程序仍然有可能被启动两次。

如果你确实将 .spec.parallelism.spec.completions 都设置为比 1 大的值, 那就有可能同时出现多个 Pod 运行的情况。 为此,你的 Pod 也必须能够处理并发性问题。

特性门控 PodDisruptionConditionsJobPodFailurePolicy 都被启用且 .spec.podFailurePolicy 字段被设置时, Job 控制器不会将终止过程中的 Pod(已设置 .metadata.deletionTimestamp 字段的 Pod)视为失效 Pod, 直到该 Pod 完全终止(其 .status.phaseFailedSucceeded)。 但只要终止变得显而易见,Job 控制器就会创建一个替代的 Pod。一旦 Pod 终止,Job 控制器将把这个刚终止的 Pod 考虑在内,评估相关 Job 的 .backoffLimit.podFailurePolicy

如果不满足任一要求,即使 Pod 稍后以 phase: "Succeeded" 终止,Job 控制器也会将此即将终止的 Pod 计为立即失效。

Pod 回退失效策略

在有些情形下,你可能希望 Job 在经历若干次重试之后直接进入失败状态, 因为这很可能意味着遇到了配置错误。 为了实现这点,可以将 .spec.backoffLimit 设置为视 Job 为失败之前的重试次数。 失效回退的限制值默认为 6。 与 Job 相关的失效的 Pod 会被 Job 控制器重建,回退重试时间将会按指数增长 (从 10 秒、20 秒到 40 秒)最多至 6 分钟。

计算重试次数有以下两种方法:

  • 计算 .status.phase = "Failed" 的 Pod 数量。
  • 当 Pod 的 restartPolicy = "OnFailure" 时,针对 .status.phase 等于 PendingRunning 的 Pod,计算其中所有容器的重试次数。

如果两种方式其中一个的值达到 .spec.backoffLimit,则 Job 被判定为失败。

逐索引的回退限制

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

运行 Indexed Job 时,你可以选择对每个索引独立处理 Pod 失败的重试。 为此,可以设置 .spec.backoffLimitPerIndex 来指定每个索引的最大 Pod 失败次数。

当某个索引超过逐索引的回退限制后,Kubernetes 将视该索引为已失败,并将其添加到 .status.failedIndexes 字段中。 无论你是否设置了 backoffLimitPerIndex 字段,已成功执行的索引(具有成功执行的 Pod)将被记录在 .status.completedIndexes 字段中。

请注意,失败的索引不会中断其他索引的执行。一旦在指定了逐索引回退限制的 Job 中的所有索引完成, 如果其中至少有一个索引失败,Job 控制器会通过在状态中设置 Failed 状况将整个 Job 标记为失败。 即使其中一些(可能几乎全部)索引已被成功处理,该 Job 也会被标记为失败。

你还可以通过设置 .spec.maxFailedIndexes 字段来限制标记为失败的最大索引数。 当失败的索引数量超过 maxFailedIndexes 字段时,Job 控制器会对该 Job 的运行中的所有余下 Pod 触发终止操作。一旦所有 Pod 被终止,Job 控制器将通过设置 Job 状态中的 Failed 状况将整个 Job 标记为失败。

以下是定义 backoffLimitPerIndex 的 Job 示例清单:

apiVersion: batch/v1
kind: Job
metadata:
  name: job-backoff-limit-per-index-example
spec:
  completions: 10
  parallelism: 3
  completionMode: Indexed  # 此特性所必需的字段
  backoffLimitPerIndex: 1  # 每个索引最大失败次数
  maxFailedIndexes: 5      # 终止 Job 执行之前失败索引的最大个数
  template:
    spec:
      restartPolicy: Never # 此特性所必需的字段
      containers:
      - name: example
        image: python
        command:           # 作业失败,因为至少有一个索引失败(此处所有偶数索引均失败),
                           # 但由于未超过 maxFailedIndexes,所以所有索引都会被执行
        - python3
        - -c
        - |
          import os, sys
          print("Hello world")
          if int(os.environ.get("JOB_COMPLETION_INDEX")) % 2 == 0:
            sys.exit(1)          

在上面的示例中,Job 控制器允许每个索引重新启动一次。 当失败的索引总数超过 5 个时,整个 Job 将被终止。

Job 完成后,该 Job 的状态如下所示:

kubectl get -o yaml job job-backoff-limit-per-index-example
  status:
    completedIndexes: 1,3,5,7,9
    failedIndexes: 0,2,4,6,8
    succeeded: 5          # 每 5 个成功的索引有 1 个成功的 Pod
    failed: 10            # 每 5 个失败的索引有 2 个失败的 Pod(1 次重试)
    conditions:
    - message: Job has failed indexes
      reason: FailedIndexes
      status: "True"
      type: Failed

此外,你可能想要结合使用逐索引回退与 Pod 失败策略。 在使用逐索引回退时,有一个新的 FailIndex 操作可用,它让你避免就某个索引进行不必要的重试。

Pod 失效策略

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

Pod 失效策略使用 .spec.podFailurePolicy 字段来定义, 它能让你的集群根据容器的退出码和 Pod 状况来处理 Pod 失效事件。

在某些情况下,你可能希望更好地控制 Pod 失效的处理方式, 而不是仅限于 Pod 回退失效策略所提供的控制能力, 后者是基于 Job 的 .spec.backoffLimit 实现的。以下是一些使用场景:

  • 通过避免不必要的 Pod 重启来优化工作负载的运行成本, 你可以在某 Job 中一个 Pod 失效且其退出码表明存在软件错误时立即终止该 Job。
  • 为了保证即使有干扰也能完成 Job,你可以忽略由干扰导致的 Pod 失效 (例如抢占通过 API 发起的驱逐 或基于污点的驱逐), 这样这些失效就不会被计入 .spec.backoffLimit 的重试限制中。

你可以在 .spec.podFailurePolicy 字段中配置 Pod 失效策略,以满足上述使用场景。 该策略可以根据容器退出码和 Pod 状况来处理 Pod 失效。

下面是一个定义了 podFailurePolicy 的 Job 的清单:

apiVersion: batch/v1
kind: Job
metadata:
  name: job-pod-failure-policy-example
spec:
  completions: 12
  parallelism: 3
  template:
    spec:
      restartPolicy: Never
      containers:
      - name: main
        image: docker.io/library/bash:5
        command: ["bash"]        # 模拟一个触发 FailJob 动作的错误的示例命令
        args:
        - -c
        - echo "Hello world!" && sleep 5 && exit 42
  backoffLimit: 6
  podFailurePolicy:
    rules:
    - action: FailJob
      onExitCodes:
        containerName: main      # 可选
        operator: In             # In 和 NotIn 二选一
        values: [42]
    - action: Ignore             # Ignore、FailJob、Count 其中之一
      onPodConditions:
      - type: DisruptionTarget   # 表示 Pod 失效

在上面的示例中,Pod 失效策略的第一条规则规定如果 main 容器失败并且退出码为 42, Job 将被标记为失败。以下是 main 容器的具体规则:

  • 退出码 0 代表容器成功
  • 退出码 42 代表整个 Job 失败
  • 所有其他退出码都代表容器失败,同时也代表着整个 Pod 失效。 如果重启总次数低于 backoffLimit 定义的次数,则会重新启动 Pod, 如果等于 backoffLimit 所设置的次数,则代表整个 Job 失效。

Pod 失效策略的第二条规则, 指定对于状况为 DisruptionTarget 的失效 Pod 采取 Ignore 操作, 统计 .spec.backoffLimit 重试次数限制时不考虑 Pod 因干扰而发生的异常。

下面是此 API 的一些要求和语义:

  • 如果你想在 Job 中使用 .spec.podFailurePolicy 字段, 你必须将 Job 的 Pod 模板中的 .spec.restartPolicy 设置为 Never
  • spec.podFailurePolicy.rules 中设定的 Pod 失效策略规则将按序评估。 一旦某个规则与 Pod 失效策略匹配,其余规则将被忽略。 当没有规则匹配 Pod 失效策略时,将会采用默认的处理方式。
  • 你可能希望在 spec.podFailurePolicy.rules[*].onExitCodes.containerName 中通过指定的名称限制只能针对特定容器应用对应的规则。 如果不设置此属性,规则将适用于所有容器。 如果指定了容器名称,它应该匹配 Pod 模板中的一个普通容器或一个初始容器(Init Container)。
  • 你可以在 spec.podFailurePolicy.rules[*].action 指定当 Pod 失效策略发生匹配时要采取的操作。 可能的值为:
    • FailJob:表示 Pod 的任务应标记为 Failed,并且所有正在运行的 Pod 应被终止。
    • Ignore:表示 .spec.backoffLimit 的计数器不应该增加,应该创建一个替换的 Pod。
    • Count:表示 Pod 应该以默认方式处理。.spec.backoffLimit 的计数器应该增加。
    • FailIndex:表示使用此操作以及逐索引回退限制来避免就失败的 Pod 的索引进行不必要的重试。

Job 终止与清理

Job 完成时不会再创建新的 Pod,不过已有的 Pod 通常也不会被删除。 保留这些 Pod 使得你可以查看已完成的 Pod 的日志输出,以便检查错误、警告或者其它诊断性输出。 Job 完成时 Job 对象也一样被保留下来,这样你就可以查看它的状态。 在查看了 Job 状态之后删除老的 Job 的操作留给了用户自己。 你可以使用 kubectl 来删除 Job(例如,kubectl delete jobs/pi 或者 kubectl delete -f ./job.yaml)。 当使用 kubectl 来删除 Job 时,该 Job 所创建的 Pod 也会被删除。

默认情况下,Job 会持续运行,除非某个 Pod 失败(restartPolicy=Never) 或者某个容器出错退出(restartPolicy=OnFailure)。 这时,Job 基于前述的 spec.backoffLimit 来决定是否以及如何重试。 一旦重试次数到达 .spec.backoffLimit 所设的上限,Job 会被标记为失败, 其中运行的 Pod 都会被终止。

终止 Job 的另一种方式是设置一个活跃期限。 你可以为 Job 的 .spec.activeDeadlineSeconds 设置一个秒数值。 该值适用于 Job 的整个生命期,无论 Job 创建了多少个 Pod。 一旦 Job 运行时间达到 activeDeadlineSeconds 秒,其所有运行中的 Pod 都会被终止, 并且 Job 的状态更新为 type: Failedreason: DeadlineExceeded

注意 Job 的 .spec.activeDeadlineSeconds 优先级高于其 .spec.backoffLimit 设置。 因此,如果一个 Job 正在重试一个或多个失效的 Pod,该 Job 一旦到达 activeDeadlineSeconds 所设的时限即不再部署额外的 Pod, 即使其重试次数还未达到 backoffLimit 所设的限制。

例如:

apiVersion: batch/v1
kind: Job
metadata:
  name: pi-with-timeout
spec:
  backoffLimit: 5
  activeDeadlineSeconds: 100
  template:
    spec:
      containers:
      - name: pi
        image: perl:5.34.0
        command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never

注意 Job 规约和 Job 中的 Pod 模板规约 都有 activeDeadlineSeconds 字段。 请确保你在合适的层次设置正确的字段。

还要注意的是,restartPolicy 对应的是 Pod,而不是 Job 本身: 一旦 Job 状态变为 type: Failed,就不会再发生 Job 重启的动作。 换言之,由 .spec.activeDeadlineSeconds.spec.backoffLimit 所触发的 Job 终结机制都会导致 Job 永久性的失败,而这类状态都需要手工干预才能解决。

自动清理完成的 Job

完成的 Job 通常不需要留存在系统中。在系统中一直保留它们会给 API 服务器带来额外的压力。 如果 Job 由某种更高级别的控制器来管理,例如 CronJob, 则 Job 可以被 CronJob 基于特定的根据容量裁定的清理策略清理掉。

已完成 Job 的 TTL 机制

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

自动清理已完成 Job (状态为 CompleteFailed)的另一种方式是使用由 TTL 控制器所提供的 TTL 机制。 通过设置 Job 的 .spec.ttlSecondsAfterFinished 字段,可以让该控制器清理掉已结束的资源。

TTL 控制器清理 Job 时,会级联式地删除 Job 对象。 换言之,它会删除所有依赖的对象,包括 Pod 及 Job 本身。 注意,当 Job 被删除时,系统会考虑其生命周期保障,例如其 Finalizers。

例如:

apiVersion: batch/v1
kind: Job
metadata:
  name: pi-with-ttl
spec:
  ttlSecondsAfterFinished: 100
  template:
    spec:
      containers:
      - name: pi
        image: perl:5.34.0
        command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never

Job pi-with-ttl 在结束 100 秒之后,可以成为被自动删除的对象。

如果该字段设置为 0,Job 在结束之后立即成为可被自动删除的对象。 如果该字段没有设置,Job 不会在结束之后被 TTL 控制器自动清除。

Job 模式

Job 对象可以用来处理一组相互独立而又彼此关联的“工作条目”。 这类工作条目可能是要发送的电子邮件、要渲染的视频帧、要编解码的文件、NoSQL 数据库中要扫描的主键范围等等。

在一个复杂系统中,可能存在多个不同的工作条目集合。 这里我们仅考虑用户希望一起管理的工作条目集合之一:批处理作业

并行计算的模式有好多种,每种都有自己的强项和弱点。这里要权衡的因素有:

  • 每个工作条目对应一个 Job 或者所有工作条目对应同一 Job 对象。 为每个工作条目创建一个 Job 的做法会给用户带来一些额外的负担,系统需要管理大量的 Job 对象。 用一个 Job 对象来完成所有工作条目的做法更适合处理大量工作条目的场景。
  • 创建数目与工作条目相等的 Pod 或者令每个 Pod 可以处理多个工作条目。 当 Pod 个数与工作条目数目相等时,通常不需要在 Pod 中对现有代码和容器做较大改动; 让每个 Pod 能够处理多个工作条目的做法更适合于工作条目数量较大的场合。
  • 有几种技术都会用到工作队列。这意味着需要运行一个队列服务, 并修改现有程序或容器使之能够利用该工作队列。 与之比较,其他方案在修改现有容器化应用以适应需求方面可能更容易一些。
  • 当 Job 与某个无头 Service 之间存在关联时,你可以让 Job 中的 Pod 之间能够相互通信,从而协作完成计算。

下面是对这些权衡的汇总,第 2 到 4 列对应上面的权衡比较。 模式的名称对应了相关示例和更详细描述的链接。

模式单个 Job 对象Pod 数少于工作条目数?直接使用应用无需修改?
每工作条目一 Pod 的队列有时
Pod 数量可变的队列
静态任务分派的带索引的 Job
带 Pod 间通信的 Job有时有时
Job 模板扩展

当你使用 .spec.completions 来设置完成数时,Job 控制器所创建的每个 Pod 使用完全相同的 spec。 这意味着任务的所有 Pod 都有相同的命令行,都使用相同的镜像和数据卷, 甚至连环境变量都(几乎)相同。 这些模式是让每个 Pod 执行不同工作的几种不同形式。

下表显示的是每种模式下 .spec.parallelism.spec.completions 所需要的设置。 其中,W 表示的是工作条目的个数。

模式.spec.completions.spec.parallelism
每工作条目一 Pod 的队列W任意值
Pod 数量可变的队列1任意值
静态任务分派的带索引的 JobW
带 Pod 间通信的 JobWW
Job 模板扩展1应该为 1

高级用法

挂起 Job

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

Job 被创建时,Job 控制器会马上开始执行 Pod 创建操作以满足 Job 的需求, 并持续执行此操作直到 Job 完成为止。 不过你可能想要暂时挂起 Job 执行,或启动处于挂起状态的 Job, 并拥有一个自定义控制器以后再决定什么时候开始。

要挂起一个 Job,你可以更新 .spec.suspend 字段为 true, 之后,当你希望恢复其执行时,将其更新为 false。 创建一个 .spec.suspend 被设置为 true 的 Job 本质上会将其创建为被挂起状态。

当 Job 被从挂起状态恢复执行时,其 .status.startTime 字段会被重置为当前的时间。 这意味着 .spec.activeDeadlineSeconds 计时器会在 Job 挂起时被停止, 并在 Job 恢复执行时复位。

当你挂起一个 Job 时,所有正在运行且状态不是 Completed 的 Pod 将被终止。 Pod 的体面终止期限会被考虑,不过 Pod 自身也必须在此期限之内处理完信号。 处理逻辑可能包括保存进度以便将来恢复,或者取消已经做出的变更等等。 Pod 以这种形式终止时,不会被记入 Job 的 completions 计数。

处于被挂起状态的 Job 的定义示例可能是这样子:

kubectl get job myjob -o yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: myjob
spec:
  suspend: true
  parallelism: 1
  completions: 5
  template:
    spec:
      ...

你也可以使用命令行为 Job 打补丁来切换 Job 的挂起状态。

挂起一个活跃的 Job:

kubectl patch job/myjob --type=strategic --patch '{"spec":{"suspend":true}}'

恢复一个挂起的 Job:

kubectl patch job/myjob --type=strategic --patch '{"spec":{"suspend":false}}'

Job 的 status 可以用来确定 Job 是否被挂起,或者曾经被挂起。

kubectl get jobs/myjob -o yaml
apiVersion: batch/v1
kind: Job
# .metadata 和 .spec 已省略
status:
  conditions:
  - lastProbeTime: "2021-02-05T13:14:33Z"
    lastTransitionTime: "2021-02-05T13:14:33Z"
    status: "True"
    type: Suspended
  startTime: "2021-02-05T13:13:48Z"

Job 的 "Suspended" 类型的状况在状态值为 "True" 时意味着 Job 正被挂起; lastTransitionTime 字段可被用来确定 Job 被挂起的时长。 如果此状况字段的取值为 "False",则 Job 之前被挂起且现在在运行。 如果 "Suspended" 状况在 status 字段中不存在,则意味着 Job 从未被停止执行。

当 Job 被挂起和恢复执行时,也会生成事件:

kubectl describe jobs/myjob
Name:           myjob
...
Events:
  Type    Reason            Age   From            Message
  ----    ------            ----  ----            -------
  Normal  SuccessfulCreate  12m   job-controller  Created pod: myjob-hlrpl
  Normal  SuccessfulDelete  11m   job-controller  Deleted pod: myjob-hlrpl
  Normal  Suspended         11m   job-controller  Job suspended
  Normal  SuccessfulCreate  3s    job-controller  Created pod: myjob-jvb44
  Normal  Resumed           3s    job-controller  Job resumed

最后四个事件,特别是 "Suspended" 和 "Resumed" 事件,都是因为 .spec.suspend 字段值被改来改去造成的。在这两个事件之间,我们看到没有 Pod 被创建,不过当 Job 被恢复执行时,Pod 创建操作立即被重启执行。

可变调度指令

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

在大多数情况下,并行作业会希望 Pod 在一定约束条件下运行, 比如所有的 Pod 都在同一个区域,或者所有的 Pod 都在 GPU 型号 x 或 y 上,而不是两者的混合。

suspend 字段是实现这些语义的第一步。 suspend 允许自定义队列控制器,以决定工作何时开始;然而,一旦工作被取消暂停, 自定义队列控制器对 Job 中 Pod 的实际放置位置没有影响。

此特性允许在 Job 开始之前更新调度指令,从而为定制队列提供影响 Pod 放置的能力,同时将 Pod 与节点间的分配关系留给 kube-scheduler 决定。 这一特性仅适用于之前从未被暂停过的、已暂停的 Job。 控制器能够影响 Pod 放置,同时参考实际 pod-to-node 分配给 kube-scheduler。 这仅适用于从未暂停的 Job。

Job 的 Pod 模板中可以更新的字段是节点亲和性、节点选择器、容忍、标签、注解和 调度门控

指定你自己的 Pod 选择算符

通常,当你创建一个 Job 对象时,你不会设置 .spec.selector。 系统的默认值填充逻辑会在创建 Job 时添加此字段。 它会选择一个不会与任何其他 Job 重叠的选择算符设置。

不过,有些场合下,你可能需要重载这个自动设置的选择算符。 为了实现这点,你可以手动设置 Job 的 spec.selector 字段。

做这个操作时请务必小心。 如果你所设定的标签选择算符并不唯一针对 Job 对应的 Pod 集合, 甚或该算符还能匹配其他无关的 Pod,这些无关的 Job 的 Pod 可能会被删除。 或者当前 Job 会将另外一些 Pod 当作是完成自身工作的 Pod, 又或者两个 Job 之一或者二者同时都拒绝创建 Pod,无法运行至完成状态。 如果所设置的算符不具有唯一性,其他控制器(如 RC 副本控制器)及其所管理的 Pod 集合可能会变得行为不可预测。 Kubernetes 不会在你设置 .spec.selector 时尝试阻止你犯这类错误。

下面是一个示例场景,在这种场景下你可能会使用刚刚讲述的特性。

假定名为 old 的 Job 已经处于运行状态。 你希望已有的 Pod 继续运行,但你希望 Job 接下来要创建的其他 Pod 使用一个不同的 Pod 模板,甚至希望 Job 的名字也发生变化。 你无法更新现有的 Job,因为这些字段都是不可更新的。 因此,你会删除 old Job,但允许该 Job 的 Pod 集合继续运行。 这是通过 kubectl delete jobs/old --cascade=orphan 实现的。 在删除之前,我们先记下该 Job 所使用的选择算符。

kubectl get job old -o yaml

输出类似于:

kind: Job
metadata:
  name: old
  ...
spec:
  selector:
    matchLabels:
      batch.kubernetes.io/controller-uid: a8f3d00d-c6d2-11e5-9f87-42010af00002
  ...

接下来你会创建名为 new 的新 Job,并显式地为其设置相同的选择算符。 由于现有 Pod 都具有标签 batch.kubernetes.io/controller-uid=a8f3d00d-c6d2-11e5-9f87-42010af00002, 它们也会被名为 new 的 Job 所控制。

你需要在新 Job 中设置 manualSelector: true, 因为你并未使用系统通常自动为你生成的选择算符。

kind: Job
metadata:
  name: new
  ...
spec:
  manualSelector: true
  selector:
    matchLabels:
      batch.kubernetes.io/controller-uid: a8f3d00d-c6d2-11e5-9f87-42010af00002
  ...

新的 Job 自身会有一个不同于 a8f3d00d-c6d2-11e5-9f87-42010af00002 的唯一 ID。 设置 manualSelector: true 是在告诉系统你知道自己在干什么并要求系统允许这种不匹配的存在。

使用 Finalizer 追踪 Job

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

控制面会跟踪属于任何 Job 的 Pod,并通知是否有任何这样的 Pod 被从 API 服务器中移除。 为了实现这一点,Job 控制器创建的 Pod 带有 Finalizer batch.kubernetes.io/job-tracking。 控制器只有在 Pod 被记入 Job 状态后才会移除 Finalizer,允许 Pod 可以被其他控制器或用户移除。

弹性索引 Job

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

你可以通过同时改变 .spec.parallelism.spec.completions 来扩大或缩小带索引 Job, 从而满足 .spec.parallelism == .spec.completions。 当 API 服务器 上的 ElasticIndexedJob 特性门控被禁用时,.spec.completions 是不可变的。 弹性索引 Job 的使用场景包括需要扩展索引 Job 的批处理工作负载,例如 MPI、Horovord、Ray 和 PyTorch 训练作业。

延迟创建替换 Pod

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

默认情况下,当 Pod 失败或正在终止(具有删除时间戳)时,Job 控制器会立即重新创建 Pod。 这意味着,在某个时间点上,当一些 Pod 正在终止时,为 Job 正运行中的 Pod 数量可以大于 parallelism 或超出每个索引一个 Pod(如果使用 Indexed Job)。

你可以选择仅在终止过程中的 Pod 完全终止(具有 status.phase: Failed)时才创建替换 Pod。 为此,可以设置 .spec.podReplacementPolicy: Failed。 默认的替换策略取决于 Job 是否设置了 podFailurePolicy。对于没有定义 Pod 失败策略的 Job, 省略 podReplacementPolicy 字段相当于选择 TerminatingOrFailed 替换策略: 控制平面在 Pod 删除时立即创建替换 Pod(只要控制平面发现该 Job 的某个 Pod 被设置了 deletionTimestamp)。 对于设置了 Pod 失败策略的 Job,默认的 podReplacementPolicyFailed,不允许其他值。 请参阅 Pod 失败策略以了解更多关于 Job 的 Pod 失败策略的信息。

kind: Job
metadata:
  name: new
  ...
spec:
  podReplacementPolicy: Failed
  ...

如果你的集群启用了此特性门控,你可以检查 Job 的 .status.terminating 字段。 该字段值是当前处于终止过程中的、由该 Job 拥有的 Pod 的数量。

kubectl get jobs/myjob -o yaml
apiVersion: batch/v1
kind: Job
# .metadata 和 .spec 被省略
status:
  terminating: 3 # 三个 Pod 正在终止且还未达到 Failed 阶段

替代方案

裸 Pod

当 Pod 运行所在的节点重启或者失败,Pod 会被终止并且不会被重启。 Job 会重新创建新的 Pod 来替代已终止的 Pod。 因为这个原因,我们建议你使用 Job 而不是独立的裸 Pod, 即使你的应用仅需要一个 Pod。

副本控制器

Job 与副本控制器是彼此互补的。 副本控制器管理的是那些不希望被终止的 Pod (例如,Web 服务器), Job 管理的是那些希望被终止的 Pod(例如,批处理作业)。

正如在 Pod 生命期 中讨论的, Job 仅适合于 restartPolicy 设置为 OnFailureNever 的 Pod。 注意:如果 restartPolicy 未设置,其默认值是 Always

单个 Job 启动控制器 Pod

另一种模式是用唯一的 Job 来创建 Pod,而该 Pod 负责启动其他 Pod, 因此扮演了一种后启动 Pod 的控制器的角色。 这种模式的灵活性更高,但是有时候可能会把事情搞得很复杂,很难入门, 并且与 Kubernetes 的集成度很低。

这种模式的实例之一是用 Job 来启动一个运行脚本的 Pod,脚本负责启动 Spark 主控制器(参见 Spark 示例), 运行 Spark 驱动,之后完成清理工作。

这种方法的优点之一是整个过程得到了 Job 对象的完成保障, 同时维持了对创建哪些 Pod、如何向其分派工作的完全控制能力,

接下来

3.4.2.6 - 已完成 Job 的自动清理

一种用于清理已完成执行的旧 Job 的 TTL 机制。
特性状态: Kubernetes v1.23 [stable]

当你的 Job 已结束时,将 Job 保留在 API 中(而不是立即删除 Job)很有用, 这样你就可以判断 Job 是成功还是失败。

Kubernetes TTL-after-finished 控制器提供了一种 TTL 机制来限制已完成执行的 Job 对象的生命期。

清理已完成的 Job

TTL-after-finished 控制器只支持 Job。你可以通过指定 Job 的 .spec.ttlSecondsAfterFinished 字段来自动清理已结束的 Job(CompleteFailed), 如示例所示。

TTL-after-finished 控制器假设 Job 能在执行完成后的 TTL 秒内被清理。一旦 Job 的状态条件发生变化表明该 Job 是 CompleteFailed,计时器就会启动;一旦 TTL 已过期,该 Job 就能被级联删除。 当 TTL 控制器清理作业时,它将做级联删除操作,即删除 Job 的同时也删除其依赖对象。

Kubernetes 尊重 Job 对象的生命周期保证,例如等待 Finalizer

你可以随时设置 TTL 秒。以下是设置 Job 的 .spec.ttlSecondsAfterFinished 字段的一些示例:

  • 在 Job 清单(manifest)中指定此字段,以便 Job 在完成后的某个时间被自动清理。
  • 手动设置现有的、已完成的 Job 的此字段,以便这些 Job 可被清理。
  • 在创建 Job 时使用修改性质的准入 Webhook 动态设置该字段。集群管理员可以使用它对已完成的作业强制执行 TTL 策略。
  • 使用修改性质的准入 Webhook 在 Job 完成后动态设置该字段,并根据 Job 状态、标签等选择不同的 TTL 值。 对于这种情况,Webhook 需要检测 Job 的 .status 变化,并且仅在 Job 被标记为已完成时设置 TTL。
  • 编写你自己的控制器来管理与特定选择算符匹配的 Job 的清理 TTL。

警告

更新已完成 Job 的 TTL

在创建 Job 或已经执行结束后,你仍可以修改其 TTL 周期,例如 Job 的 .spec.ttlSecondsAfterFinished 字段。 如果你在当前 ttlSecondsAfterFinished 时长已过期后延长 TTL 周期, 即使延长 TTL 的更新得到了成功的 API 响应,Kubernetes 也不保证保留此 Job,

时间偏差

由于 TTL-after-finished 控制器使用存储在 Kubernetes Job 中的时间戳来确定 TTL 是否已过期, 因此该功能对集群中的时间偏差很敏感,这可能导致控制平面在错误的时间清理 Job 对象。

时钟并不总是如此正确,但差异应该很小。 设置非零 TTL 时请注意避免这种风险。

接下来

3.4.2.7 - CronJob

CronJob 通过重复调度启动一次性的 Job。
特性状态: Kubernetes v1.21 [stable]

CronJob 创建基于时隔重复调度的 Job

CronJob 用于执行排期操作,例如备份、生成报告等。 一个 CronJob 对象就像 Unix 系统上的 crontab(cron table)文件中的一行。 它用 Cron 格式进行编写, 并周期性地在给定的调度时间执行 Job。

CronJob 有所限制,也比较特殊。 例如在某些情况下,单个 CronJob 可以创建多个并发任务。 请参阅下面的限制

当控制平面为 CronJob 创建新的 Job 和(间接)Pod 时,CronJob 的 .metadata.name 是命名这些 Pod 的部分基础。 CronJob 的名称必须是一个合法的 DNS 子域值, 但这会对 Pod 的主机名产生意外的结果。为获得最佳兼容性,名称应遵循更严格的 DNS 标签规则。 即使名称是一个 DNS 子域,它也不能超过 52 个字符。这是因为 CronJob 控制器将自动在你所提供的 Job 名称后附加 11 个字符,并且存在 Job 名称的最大长度不能超过 63 个字符的限制。

示例

下面的 CronJob 示例清单会在每分钟打印出当前时间和问候消息:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "* * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox:1.28
            imagePullPolicy: IfNotPresent
            command:
            - /bin/sh
            - -c
            - date; echo Hello from the Kubernetes cluster
          restartPolicy: OnFailure

使用 CronJob 运行自动化任务一文会为你详细讲解此例。

编写 CronJob 声明信息

Cron 时间表语法

.spec.schedule 字段是必需的。该字段的值遵循 Cron 语法:

# ┌───────────── 分钟 (0 - 59)
# │ ┌───────────── 小时 (0 - 23)
# │ │ ┌───────────── 月的某天 (1 - 31)
# │ │ │ ┌───────────── 月份 (1 - 12)
# │ │ │ │ ┌───────────── 周的某天 (0 - 6)(周日到周六)
# │ │ │ │ │                          或者是 sun,mon,tue,web,thu,fri,sat
# │ │ │ │ │
# │ │ │ │ │
# * * * * *

例如 0 0 13 * 5 表示此任务必须在每个星期五的午夜以及每个月的 13 日的午夜开始。

该格式也包含了扩展的 “Vixie cron” 步长值。 FreeBSD 手册中解释如下:

步长可被用于范围组合。范围后面带有 /<数字> 可以声明范围内的步幅数值。 例如,0-23/2 可被用在小时字段来声明命令在其他数值的小时数执行 (V7 标准中对应的方法是 0,2,4,6,8,10,12,14,16,18,20,22)。 步长也可以放在通配符后面,因此如果你想表达 “每两小时”,就用 */2

除了标准语法,还可以使用一些类似 @monthly 的宏:

输入描述相当于
@yearly (或 @annually)每年 1 月 1 日的午夜运行一次0 0 1 1 *
@monthly每月第一天的午夜运行一次0 0 1 * *
@weekly每周的周日午夜运行一次0 0 * * 0
@daily (或 @midnight)每天午夜运行一次0 0 * * *
@hourly每小时的开始一次0 * * * *

为了生成 CronJob 时间表的表达式,你还可以使用 crontab.guru 这类 Web 工具。

任务模板

.spec.jobTemplate为 CronJob 创建的 Job 定义模板,它是必需的。它和 Job 的语法完全一样, 只不过它是嵌套的,没有 apiVersionkind。 你可以为模板化的 Job 指定通用的元数据, 例如标签注解。 有关如何编写一个 Job 的 .spec, 请参考编写 Job 规约

Job 延迟开始的最后期限

.spec.startingDeadlineSeconds 字段是可选的。 它表示 Job 如果由于某种原因错过了调度时间,开始该 Job 的截止时间的秒数。

过了截止时间,CronJob 就不会开始该 Job 的实例(未来的 Job 仍在调度之中)。 例如,如果你有一个每天运行两次的备份 Job,你可能会允许它最多延迟 8 小时开始,但不能更晚, 因为更晚进行的备份将变得没有意义:你宁愿等待下一次计划的运行。

对于错过已配置的最后期限的 Job,Kubernetes 将其视为失败的 Job。 如果你没有为 CronJob 指定 startingDeadlineSeconds,那 Job 就没有最后期限。

如果 .spec.startingDeadlineSeconds 字段被设置(非空), CronJob 控制器将会计算从预期创建 Job 到当前时间的时间差。 如果时间差大于该限制,则跳过此次执行。

例如,如果将其设置为 200,则 Job 控制器允许在实际调度之后最多 200 秒内创建 Job。

并发性规则

.spec.concurrencyPolicy 字段也是可选的。它声明了 CronJob 创建的 Job 执行时发生重叠如何处理。 spec 仅能声明下列规则中的一种:

  • Allow(默认):CronJob 允许并发 Job 执行。
  • Forbid:CronJob 不允许并发执行;如果新 Job 的执行时间到了而老 Job 没有执行完,CronJob 会忽略新 Job 的执行。 另请注意,当老 Job 执行完成时,仍然会考虑 .spec.startingDeadlineSeconds,可能会导致新的 Job 执行。
  • Replace:如果新 Job 的执行时间到了而老 Job 没有执行完,CronJob 会用新 Job 替换当前正在运行的 Job。

请注意,并发性规则仅适用于相同 CronJob 创建的 Job。如果有多个 CronJob,它们相应的 Job 总是允许并发执行的。

调度挂起

通过将可选的 .spec.suspend 字段设置为 true,可以挂起针对 CronJob 执行的任务。

这个设置会影响 CronJob 已经开始的任务。

如果你将此字段设置为 true,后续发生的执行都会被挂起 (这些任务仍然在调度中,但 CronJob 控制器不会启动这些 Job 来运行任务),直到你取消挂起 CronJob 为止。

任务历史限制

.spec.successfulJobsHistoryLimit.spec.failedJobsHistoryLimit 字段是可选的。 这两个字段指定应保留多少已完成和失败的 Job。 默认设置分别为 3 和 1。将限制设置为 0 代表相应类型的 Job 完成后不会保留。

有关自动清理 Job 的其他方式, 请参见自动清理完成的 Job

时区

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

对于没有指定时区的 CronJob, kube-controller-manager 基于本地时区解释排期表(Schedule)。

你可以通过将 .spec.timeZone 设置为一个有效时区的名称, 为 CronJob 指定一个时区。例如设置 .spec.timeZone: "Etc/UTC" 将告诉 Kubernetes 基于世界标准时间解读排期表。

Go 标准库中的时区数据库包含在二进制文件中,并用作备用数据库,以防系统上没有可用的外部数据库。

CronJob 的限制

不支持的时区规范

.spec.schedule 中通过 CRON_TZTZ 变量来指定时区并未得到官方支持(而且从未支持过)。

从 Kubernetes 1.29 版本开始,如果你尝试设定包含 TZCRON_TZ 时区规范的排期表, Kubernetes 将无法创建该资源,并会报告验证错误。 对已经设置 TZCRON_TZ 的 CronJob 进行更新时, 系统会继续向客户端发送警告

修改 CronJob

按照设计,CronJob 包含一个用于 Job 的模板。 如果你修改现有的 CronJob,你所做的更改将应用于修改完成后开始运行的新任务。 已经开始的任务(及其 Pod)将继续运行而不会发生任何变化。 也就是说,CronJob 会更新现有任务,即使这些任务仍在运行。

Job 创建

CronJob 根据其计划编排,在每次该执行任务的时候大约会创建一个 Job。 我们之所以说 "大约",是因为在某些情况下,可能会创建两个 Job,或者不会创建任何 Job。 我们试图使这些情况尽量少发生,但不能完全杜绝。因此,Job 应该是 幂等的

如果 startingDeadlineSeconds 设置为很大的数值或未设置(默认),并且 concurrencyPolicy 设置为 Allow,则 Job 将始终至少运行一次。

对于每个 CronJob,CronJob 控制器(Controller) 检查从上一次调度的时间点到现在所错过了调度次数。如果错过的调度次数超过 100 次, 那么它就不会启动这个 Job,并记录这个错误:

Cannot determine if job needs to be started. Too many missed start time (> 100). Set or decrease .spec.startingDeadlineSeconds or check clock skew.

需要注意的是,如果 startingDeadlineSeconds 字段非空,则控制器会统计从 startingDeadlineSeconds 设置的值到现在而不是从上一个计划时间到现在错过了多少次 Job。 例如,如果 startingDeadlineSeconds200,则控制器会统计在过去 200 秒中错过了多少次 Job。

如果未能在调度时间内创建 CronJob,则计为错过。 例如,如果 concurrencyPolicy 被设置为 Forbid,并且当前有一个调度仍在运行的情况下, 试图调度的 CronJob 将被计算为错过。

例如,假设一个 CronJob 被设置为从 08:30:00 开始每隔一分钟创建一个新的 Job, 并且它的 startingDeadlineSeconds 字段未被设置。如果 CronJob 控制器从 08:29:0010:21:00 终止运行,则该 Job 将不会启动, 因为其错过的调度次数超过了 100。

为了进一步阐述这个概念,假设将 CronJob 设置为从 08:30:00 开始每隔一分钟创建一个新的 Job, 并将其 startingDeadlineSeconds 字段设置为 200 秒。 如果 CronJob 控制器恰好在与上一个示例相同的时间段(08:29:0010:21:00)终止运行, 则 Job 仍将从 10:22:00 开始。 造成这种情况的原因是控制器现在检查在最近 200 秒(即 3 个错过的调度)中发生了多少次错过的 Job 调度,而不是从现在为止的最后一个调度时间开始。

CronJob 仅负责创建与其调度时间相匹配的 Job,而 Job 又负责管理其代表的 Pod。

接下来

  • 了解 CronJob 所依赖的 PodJob 的概念。
  • 阅读 CronJob .spec.schedule 字段的详细格式
  • 有关创建和使用 CronJob 的说明及 CronJob 清单的示例, 请参见使用 CronJob 运行自动化任务
  • CronJob 是 Kubernetes REST API 的一部分, 阅读 CronJob API 参考了解更多细节。

3.4.2.8 - ReplicationController

用于管理可水平扩展的工作负载的旧版 API。 被 Deployment 和 ReplicaSet API 取代。

ReplicationController 确保在任何时候都有特定数量的 Pod 副本处于运行状态。 换句话说,ReplicationController 确保一个 Pod 或一组同类的 Pod 总是可用的。

ReplicationController 如何工作

当 Pod 数量过多时,ReplicationController 会终止多余的 Pod。当 Pod 数量太少时,ReplicationController 将会启动新的 Pod。 与手动创建的 Pod 不同,由 ReplicationController 创建的 Pod 在失败、被删除或被终止时会被自动替换。 例如,在中断性维护(如内核升级)之后,你的 Pod 会在节点上重新创建。 因此,即使你的应用程序只需要一个 Pod,你也应该使用 ReplicationController 创建 Pod。 ReplicationController 类似于进程管理器,但是 ReplicationController 不是监控单个节点上的单个进程,而是监控跨多个节点的多个 Pod。

在讨论中,ReplicationController 通常缩写为 "rc",并作为 kubectl 命令的快捷方式。

一个简单的示例是创建一个 ReplicationController 对象来可靠地无限期地运行 Pod 的一个实例。 更复杂的用例是运行一个多副本服务(如 web 服务器)的若干相同副本。

运行一个示例 ReplicationController

这个示例 ReplicationController 配置运行 nginx Web 服务器的三个副本。

apiVersion: v1
kind: ReplicationController
metadata:
  name: nginx
spec:
  replicas: 3
  selector:
    app: nginx
  template:
    metadata:
      name: nginx
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80

通过下载示例文件并运行以下命令来运行示例任务:

kubectl apply -f https://k8s.io/examples/controllers/replication.yaml

输出类似于:

replicationcontroller/nginx created

使用以下命令检查 ReplicationController 的状态:

kubectl describe replicationcontrollers/nginx

输出类似于:

Name:        nginx
Namespace:   default
Selector:    app=nginx
Labels:      app=nginx
Annotations:    <none>
Replicas:    3 current / 3 desired
Pods Status: 0 Running / 3 Waiting / 0 Succeeded / 0 Failed
Pod Template:
  Labels:       app=nginx
  Containers:
   nginx:
    Image:              nginx
    Port:               80/TCP
    Environment:        <none>
    Mounts:             <none>
  Volumes:              <none>
Events:
  FirstSeen       LastSeen     Count    From                        SubobjectPath    Type      Reason              Message
  ---------       --------     -----    ----                        -------------    ----      ------              -------
  20s             20s          1        {replication-controller }                    Normal    SuccessfulCreate    Created pod: nginx-qrm3m
  20s             20s          1        {replication-controller }                    Normal    SuccessfulCreate    Created pod: nginx-3ntk0
  20s             20s          1        {replication-controller }                    Normal    SuccessfulCreate    Created pod: nginx-4ok8v

在这里,创建了三个 Pod,但没有一个 Pod 正在运行,这可能是因为正在拉取镜像。 稍后,相同的命令可能会显示:

Pods Status:    3 Running / 0 Waiting / 0 Succeeded / 0 Failed

要以机器可读的形式列出属于 ReplicationController 的所有 Pod,可以使用如下命令:

pods=$(kubectl get pods --selector=app=nginx --output=jsonpath={.items..metadata.name})
echo $pods

输出类似于:

nginx-3ntk0 nginx-4ok8v nginx-qrm3m

这里,选择算符与 ReplicationController 的选择算符相同(参见 kubectl describe 输出),并以不同的形式出现在 replication.yaml 中。 --output=jsonpath 选项指定了一个表达式,仅从返回列表中的每个 Pod 中获取名称。

编写一个 ReplicationController 清单

与所有其它 Kubernetes 配置一样,ReplicationController 需要 apiVersionkindmetadata 字段。

当控制平面为 ReplicationController 创建新的 Pod 时,ReplicationController 的 .metadata.name 是命名这些 Pod 的部分基础。ReplicationController 的名称必须是一个合法的 DNS 子域值, 但这可能对 Pod 的主机名产生意外的结果。为获得最佳兼容性,名称应遵循更严格的 DNS 标签规则。

有关使用配置文件的常规信息, 参考对象管理

ReplicationController 也需要一个 .spec 部分

Pod 模板

.spec.template.spec 的唯一必需字段。

.spec.template 是一个 Pod 模板。 它的模式与 Pod 完全相同,只是它是嵌套的,没有 apiVersionkind 属性。

除了 Pod 所需的字段外,ReplicationController 中的 Pod 模板必须指定适当的标签和适当的重新启动策略。 对于标签,请确保不与其他控制器重叠。参考 Pod 选择算符

只允许 .spec.template.spec.restartPolicy 等于 Always,如果没有指定,这是默认值。

对于本地容器重启,ReplicationController 委托给节点上的代理, 例如 Kubelet

ReplicationController 上的标签

ReplicationController 本身可以有标签 (.metadata.labels)。 通常,你可以将这些设置为 .spec.template.metadata.labels; 如果没有指定 .metadata.labels 那么它默认为 .spec.template.metadata.labels。 但是,Kubernetes 允许它们是不同的,.metadata.labels 不会影响 ReplicationController 的行为。

Pod 选择算符

.spec.selector 字段是一个标签选择算符。 ReplicationController 管理标签与选择算符匹配的所有 Pod。 它不区分它创建或删除的 Pod 和其他人或进程创建或删除的 Pod。 这允许在不影响正在运行的 Pod 的情况下替换 ReplicationController。

如果指定了 .spec.template.metadata.labels,它必须和 .spec.selector 相同,否则它将被 API 拒绝。 如果没有指定 .spec.selector,它将默认为 .spec.template.metadata.labels

另外,通常不应直接使用另一个 ReplicationController 或另一个控制器(例如 Job) 来创建其标签与该选择算符匹配的任何 Pod。如果这样做,ReplicationController 会认为它创建了这些 Pod。 Kubernetes 并没有阻止你这样做。

如果你的确创建了多个控制器并且其选择算符之间存在重叠,那么你将不得不自己管理删除操作(参考后文)。

多个副本

你可以通过设置 .spec.replicas 来指定应该同时运行多少个 Pod。 在任何时候,处于运行状态的 Pod 个数都可能高于或者低于设定值。例如,副本个数刚刚被增加或减少时, 或者一个 Pod 处于优雅终止过程中而其替代副本已经提前开始创建时。

如果你没有指定 .spec.replicas,那么它默认是 1。

使用 ReplicationController

删除一个 ReplicationController 以及它的 Pod

要删除一个 ReplicationController 以及它的 Pod,使用 kubectl delete。 kubectl 将 ReplicationController 缩容为 0 并等待以便在删除 ReplicationController 本身之前删除每个 Pod。 如果这个 kubectl 命令被中断,可以重新启动它。

当使用 REST API 或客户端库时,你需要明确地执行这些步骤(缩容副本为 0、 等待 Pod 删除,之后删除 ReplicationController 资源)。

只删除 ReplicationController

你可以删除一个 ReplicationController 而不影响它的任何 Pod。

使用 kubectl,为 kubectl delete 指定 --cascade=orphan 选项。

当使用 REST API 或客户端库时,只需删除 ReplicationController 对象。

一旦原始对象被删除,你可以创建一个新的 ReplicationController 来替换它。 只要新的和旧的 .spec.selector 相同,那么新的控制器将领养旧的 Pod。 但是,它不会做出任何努力使现有的 Pod 匹配新的、不同的 Pod 模板。 如果希望以受控方式更新 Pod 以使用新的 spec,请执行滚动更新操作。

从 ReplicationController 中隔离 Pod

通过更改 Pod 的标签,可以从 ReplicationController 的目标中删除 Pod。 此技术可用于从服务中删除 Pod 以进行调试、数据恢复等。以这种方式删除的 Pod 将被自动替换(假设复制副本的数量也没有更改)。

常见的使用模式

重新调度

如上所述,无论你想要继续运行 1 个 Pod 还是 1000 个 Pod,一个 ReplicationController 都将确保存在指定数量的 Pod,即使在节点故障或 Pod 终止(例如,由于另一个控制代理的操作)的情况下也是如此。

扩缩容

通过设置 replicas 字段,ReplicationController 可以允许扩容或缩容副本的数量。 你可以手动或通过自动扩缩控制代理来控制 ReplicationController 执行此操作。

滚动更新

ReplicationController 的设计目的是通过逐个替换 Pod 以方便滚动更新服务。

#1353 PR 中所述,建议的方法是使用 1 个副本创建一个新的 ReplicationController, 逐个扩容新的(+1)和缩容旧的(-1)控制器,然后在旧的控制器达到 0 个副本后将其删除。 这一方法能够实现可控的 Pod 集合更新,即使存在意外失效的状况。

理想情况下,滚动更新控制器将考虑应用程序的就绪情况,并确保在任何给定时间都有足够数量的 Pod 有效地提供服务。

这两个 ReplicationController 将需要创建至少具有一个不同标签的 Pod, 比如 Pod 主要容器的镜像标签,因为通常是镜像更新触发滚动更新。

多个版本跟踪

除了在滚动更新过程中运行应用程序的多个版本之外,通常还会使用多个版本跟踪来长时间, 甚至持续运行多个版本。这些跟踪将根据标签加以区分。

例如,一个服务可能把具有 tier in (frontend), environment in (prod) 的所有 Pod 作为目标。 现在假设你有 10 个副本的 Pod 组成了这个层。但是你希望能够 canary金丝雀)发布这个组件的新版本。 你可以为大部分副本设置一个 ReplicationController,其中 replicas 设置为 9, 标签为 tier=frontend, environment=prod, track=stable 而为 canary 设置另一个 ReplicationController,其中 replicas 设置为 1, 标签为 tier=frontend, environment=prod, track=canary。 现在这个服务覆盖了 canary 和非 canary Pod。但你可以单独处理 ReplicationController,以测试、监控结果等。

和服务一起使用 ReplicationController

多个 ReplicationController 可以位于一个服务的后面,例如,一部分流量流向旧版本, 一部分流量流向新版本。

一个 ReplicationController 永远不会自行终止,但它不会像服务那样长时间存活。 服务可以由多个 ReplicationController 控制的 Pod 组成,并且在服务的生命周期内 (例如,为了执行 Pod 更新而运行服务),可以创建和销毁许多 ReplicationController。 服务本身和它们的客户端都应该忽略负责维护服务 Pod 的 ReplicationController 的存在。

编写多副本的应用

由 ReplicationController 创建的 Pod 是可替换的,语义上是相同的, 尽管随着时间的推移,它们的配置可能会变得异构。 这显然适合于多副本的无状态服务器,但是 ReplicationController 也可以用于维护主选、 分片和工作池应用程序的可用性。 这样的应用程序应该使用动态的工作分配机制,例如 RabbitMQ 工作队列, 而不是静态的或者一次性定制每个 Pod 的配置,这被认为是一种反模式。 执行的任何 Pod 定制,例如资源的垂直自动调整大小(例如,CPU 或内存), 都应该由另一个在线控制器进程执行,这与 ReplicationController 本身没什么不同。

ReplicationController 的职责

ReplicationController 仅确保所需的 Pod 数量与其标签选择算符匹配,并且是可操作的。 目前,它的计数中只排除终止的 Pod。 未来,可能会考虑系统提供的就绪状态和其他信息, 我们可能会对替换策略添加更多控制, 我们计划发出事件,这些事件可以被外部客户端用来实现任意复杂的替换和/或缩减策略。

ReplicationController 永远被限制在这个狭隘的职责范围内。 它本身既不执行就绪态探测,也不执行活跃性探测。 它不负责执行自动扩缩,而是由外部自动扩缩器控制(如 #492 中所述),后者负责更改其 replicas 字段值。 我们不会向 ReplicationController 添加调度策略(例如, spreading)。 它也不应该验证所控制的 Pod 是否与当前指定的模板匹配,因为这会阻碍自动调整大小和其他自动化过程。 类似地,完成期限、整理依赖关系、配置扩展和其他特性也属于其他地方。 我们甚至计划考虑批量创建 Pod 的机制(查阅 #170)。

ReplicationController 旨在成为可组合的构建基元。 我们希望在它和其他补充原语的基础上构建更高级别的 API 或者工具,以便于将来的用户使用。 kubectl 目前支持的 "macro" 操作(运行、扩缩、滚动更新)就是这方面的概念示例。 例如,我们可以想象类似于 Asgard 的东西管理 ReplicationController、自动定标器、服务、调度策略、金丝雀发布等。

API 对象

在 Kubernetes REST API 中 Replication controller 是顶级资源。 更多关于 API 对象的详细信息可以在 ReplicationController API 对象找到。

ReplicationController 的替代方案

ReplicaSet

ReplicaSet 是下一代 ReplicationController, 支持新的基于集合的标签选择算符。 它主要被 Deployment 用来作为一种编排 Pod 创建、删除及更新的机制。 请注意,我们推荐使用 Deployment 而不是直接使用 ReplicaSet,除非你需要自定义更新编排或根本不需要更新。

Deployment (推荐)

Deployment 是一种更高级别的 API 对象,用于更新其底层 ReplicaSet 及其 Pod。 如果你想要这种滚动更新功能,那么推荐使用 Deployment,因为它们是声明式的、服务端的,并且具有其它特性。

裸 Pod

与用户直接创建 Pod 的情况不同,ReplicationController 能够替换因某些原因被删除或被终止的 Pod, 例如在节点故障或中断节点维护的情况下,例如内核升级。 因此,我们建议你使用 ReplicationController,即使你的应用程序只需要一个 Pod。 可以将其看作类似于进程管理器,它只管理跨多个节点的多个 Pod,而不是单个节点上的单个进程。 ReplicationController 将本地容器重启委托给节点上的某个代理(例如 Kubelet)。

Job

对于预期会自行终止的 Pod (即批处理任务),使用 Job 而不是 ReplicationController。

DaemonSet

对于提供机器级功能(例如机器监控或机器日志记录)的 Pod, 使用 DaemonSet 而不是 ReplicationController。 这些 Pod 的生命期与机器的生命期绑定:它们需要在其他 Pod 启动之前在机器上运行, 并且在机器准备重新启动或者关闭时安全地终止。

接下来

  • 了解 Pod
  • 了解 Depolyment,ReplicationController 的替代品。
  • ReplicationController 是 Kubernetes REST API 的一部分,阅读 ReplicationController 对象定义以了解 replication controllers 的 API。

3.4.3 - 自动扩缩工作负载

通过自动扩缩,你可以用某种方式自动更新你的工作负载。在面对资源需求变化的时候可以使你的集群更灵活、更高效。

在 Kubernetes 中,你可以根据当前的资源需求扩缩工作负载。 这让你的集群可以更灵活、更高效地面对资源需求的变化。

当你扩缩工作负载时,你可以增加或减少工作负载所管理的副本数量,或者就地调整副本的可用资源。

第一种手段称为水平扩缩,第二种称为垂直扩缩

扩缩工作负载有手动和自动两种方式,这取决于你的使用情况。

手动扩缩工作负载

Kubernetes 支持工作负载的手动扩缩。水平扩缩可以使用 kubectl 命令行工具完成。 对于垂直扩缩,你需要更新工作负载的资源定义。

这两种策略的示例见下文。

自动扩缩工作负载

Kubernetes 也支持工作负载的自动扩缩,这也是本页的重点。

在 Kubernetes 中自动扩缩的概念是指自动更新管理一组 Pod 的能力(例如 Deployment)。

水平扩缩工作负载

在 Kubernetes 中,你可以使用 HorizontalPodAutoscaler (HPA) 实现工作负载的自动水平扩缩。

它以 Kubernetes API 资源和控制器的方式实现, 并定期调整工作负载中副本的数量 以满足设置的资源利用率,如 CPU 或内存利用率。

这是一个为 Deployment 部署配置 HorizontalPodAutoscaler 的示例教程

垂直扩缩工作负载

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

你可以使用 VerticalPodAutoscaler (VPA) 实现工作负载的垂直扩缩。 不同于 HPA,VPA 并非默认来源于 Kubernetes,而是一个独立的项目, 参见 on GitHub

安装后,你可以为工作负载创建 CustomResourceDefinitions(CRDs), 定义如何以及何时扩缩被管理副本的资源。

目前,VPA 可以有四种不同的运行模式:

VPA 的不同模式
模式描述
Auto目前是 Recreate,将来可能改为就地更新
RecreateVPA 会在创建 Pod 时分配资源请求,并且当请求的资源与新的建议值区别很大时通过驱逐 Pod 的方式来更新现存的 Pod
InitialVPA 只有在创建时分配资源请求,之后不做更改
OffVPA 不会自动更改 Pod 的资源需求,建议值仍会计算并可在 VPA 对象中查看

就地调整的要求

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

重启 Pod 或其中容器就地调整工作负载的情况下要求 Kubernetes 版本大于 1.27。 此外,特性门控 InPlaceVerticalScaling 需要开启。

InPlacePodVerticalScaling:

启用就地 Pod 垂直扩缩。

根据集群规模自动扩缩

对于需要根据集群规模实现扩缩的工作负载(例如:cluster-dns 或者其他系统组件), 你可以使用 Cluster Proportional Autoscaler。 与 VPA 一样,这个项目不是 Kubernetes 核心项目的一部分,它在 GitHub 上有自己的项目。

集群弹性伸缩器 (Cluster Proportional Autoscaler) 会观测可调度 节点 和 内核数量, 并调整目标工作负载的副本数量。

如果副本的数量需要保持一致,你可以使用 Cluster Proportional Vertical Autoscaler 来根据集群规模进行垂直扩缩。 这个项目目前处于 beta 阶段,你可以在 GitHub 上找到它。

集群弹性伸缩器会扩缩工作负载的副本数量,垂直集群弹性伸缩器 (Cluster Proportional Vertical Autoscaler) 会根据节点和/或核心的数量 调整工作负载的资源请求(例如 Deployment 和 DaemonSet)。

事件驱动型自动扩缩

通过事件驱动实现工作负载的扩缩也是可行的, 例如使用 Kubernetes Event Driven Autoscaler (KEDA)

KEDA 是 CNCF 的毕业项目,能让你根据要处理事件的数量对工作负载进行扩缩,例如队列中消息的数量。 有多种针对不同事件源的适配可供选择。

根据计划自动扩缩

扩缩工作负载的另一种策略是计划进行扩缩,例如在非高峰时段减少资源消耗。

与事件驱动型自动扩缩相似,这种行为可以使用 KEDA 和 Cron scaler 实现。 你可以在计划扩缩器 (Cron scaler) 中定义计划来实现工作负载的横向扩缩。

扩缩集群基础设施

如果扩缩工作负载无法满足你的需求,你也可以扩缩集群基础设施本身。

扩缩集群基础设施通常是指增加或移除节点

阅读集群自动扩缩了解更多信息。

接下来

3.5 - 服务、负载均衡和联网

Kubernetes 网络背后的概念和资源。

Kubernetes 网络模型

集群中每一个 Pod 都会获得自己的、 独一无二的 IP 地址, 这就意味着你不需要显式地在 Pod 之间创建链接,你几乎不需要处理容器端口到主机端口之间的映射。 这将形成一个干净的、向后兼容的模型;在这个模型里,从端口分配、命名、服务发现、 负载均衡、 应用配置和迁移的角度来看,Pod 可以被视作虚拟机或者物理主机。

Kubernetes 强制要求所有网络设施都满足以下基本要求(从而排除了有意隔离网络的策略):

  • Pod 能够与所有其他节点上的 Pod 通信, 且不需要网络地址转译(NAT)
  • 节点上的代理(比如:系统守护进程、kubelet)可以和节点上的所有 Pod 通信

说明:对于支持在主机网络中运行 Pod 的平台(比如:Linux), 当 Pod 挂接到节点的宿主网络上时,它们仍可以不通过 NAT 和所有节点上的 Pod 通信。

这个模型不仅不复杂,而且还和 Kubernetes 的实现从虚拟机向容器平滑迁移的初衷相符, 如果你的任务开始是在虚拟机中运行的,你的虚拟机有一个 IP, 可以和项目中其他虚拟机通信。这里的模型是基本相同的。

Kubernetes 的 IP 地址存在于 Pod 范围内 —— 容器共享它们的网络命名空间 —— 包括它们的 IP 地址和 MAC 地址。 这就意味着 Pod 内的容器都可以通过 localhost 到达对方端口。 这也意味着 Pod 内的容器需要相互协调端口的使用,但是这和虚拟机中的进程似乎没有什么不同, 这也被称为“一个 Pod 一个 IP”模型。

如何实现以上需求是所使用的特定容器运行时的细节。

也可以在 Node 本身请求端口,并用这类端口转发到你的 Pod(称之为主机端口), 但这是一个很特殊的操作。转发方式如何实现也是容器运行时的细节。 Pod 自己并不知道这些主机端口的存在。

Kubernetes 网络解决四方面的问题:

使用 Service 连接到应用教程通过一个实际的示例让你了解 Service 和 Kubernetes 如何联网。

集群网络解释了如何为集群设置网络, 还概述了所涉及的技术。

3.5.1 - 服务(Service)

将在集群中运行的应用通过同一个面向外界的端点公开出去,即使工作负载分散于多个后端也完全可行。

Kubernetes 中 Service 是 将运行在一个或一组 Pod 上的网络应用程序公开为网络服务的方法。

Kubernetes 中 Service 的一个关键目标是让你无需修改现有应用以使用某种不熟悉的服务发现机制。 你可以在 Pod 集合中运行代码,无论该代码是为云原生环境设计的,还是被容器化的老应用。 你可以使用 Service 让一组 Pod 可在网络上访问,这样客户端就能与之交互。

如果你使用 Deployment 来运行你的应用, Deployment 可以动态地创建和销毁 Pod。 在任何时刻,你都不知道有多少个这样的 Pod 正在工作以及它们健康与否; 你可能甚至不知道如何辨别健康的 Pod。 Kubernetes Pod 的创建和销毁是为了匹配集群的预期状态。 Pod 是临时资源(你不应该期待单个 Pod 既可靠又耐用)。

每个 Pod 会获得属于自己的 IP 地址(Kubernetes 期待网络插件来保证这一点)。 对于集群中给定的某个 Deployment,这一刻运行的 Pod 集合可能不同于下一刻运行该应用的 Pod 集合。

这就带来了一个问题:如果某组 Pod(称为“后端”)为集群内的其他 Pod(称为“前端”) 集合提供功能,前端要如何发现并跟踪要连接的 IP 地址,以便其使用负载的后端组件呢?

Kubernetes 中的 Service

Service API 是 Kubernetes 的组成部分,它是一种抽象,帮助你将 Pod 集合在网络上公开出去。 每个 Service 对象定义端点的一个逻辑集合(通常这些端点就是 Pod)以及如何访问到这些 Pod 的策略。

例如,考虑一个无状态的图像处理后端,其中运行 3 个副本(Replicas)。 这些副本是可互换的 —— 前端不需要关心它们调用的是哪个后端。 即便构成后端集合的实际 Pod 可能会发生变化,前端客户端不应该也没必要知道这些, 而且它们也不必亲自跟踪后端的状态变化。

Service 抽象使这种解耦成为可能。

Service 所对应的 Pod 集合通常由你定义的选择算符来确定。 若想了解定义 Service 端点的其他方式,可以查阅不带选择算符的 Service

如果你的工作负载使用 HTTP 通信,你可能会选择使用 Ingress 来控制 Web 流量如何到达该工作负载。Ingress 不是一种 Service,但它可用作集群的入口点。 Ingress 能让你将路由规则整合到同一个资源内,这样你就能将工作负载的多个组件公开出去, 这些组件使用同一个侦听器,但各自独立地运行在集群中。

用于 Kubernetes 的 Gateway API 能够提供 Ingress 和 Service 所不具备的一些额外能力。 Gateway 是使用 CustomResourceDefinitions 实现的一系列扩展 API。 你可以添加 Gateway 到你的集群中,之后就可以使用它们配置如何访问集群中运行的网络服务。

云原生服务发现

如果你想要在自己的应用中使用 Kubernetes API 进行服务发现,可以查询 API 服务器, 寻找匹配的 EndpointSlice 对象。 只要 Service 中的 Pod 集合发生变化,Kubernetes 就会为其更新 EndpointSlice。

对于非本地应用,Kubernetes 提供了在应用和后端 Pod 之间放置网络端口或负载均衡器的方法。

无论采用那种方式,你的负载都可以使用这里的服务发现机制找到希望连接的目标。

定义 Service

Kubernetes 中的 Service 是一个对象 (与 Pod 或 ConfigMap 类似)。你可以使用 Kubernetes API 创建、查看或修改 Service 定义。 通常你会使用 kubectl 这类工具来替你发起这些 API 调用。

例如,假定有一组 Pod,每个 Pod 都在侦听 TCP 端口 9376,并且它们还被打上 app.kubernetes.io/name=MyApp 标签。你可以定义一个 Service 来发布该 TCP 侦听器。

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376

应用上述清单时,系统将创建一个名为 "my-service" 的、 服务类型默认为 ClusterIP 的 Service。 该 Service 指向带有标签 app.kubernetes.io/name: MyApp 的所有 Pod 的 TCP 端口 9376。

Kubernetes 为该服务分配一个 IP 地址(称为 “集群 IP”),供虚拟 IP 地址机制使用。 有关该机制的更多详情,请阅读虚拟 IP 和服务代理

此 Service 的控制器不断扫描与其选择算符匹配的 Pod 集合,然后对 Service 的 EndpointSlice 集合执行必要的更新。

Service 对象的名称必须是有效的 RFC 1035 标签名称

端口定义

Pod 中的端口定义是有名字的,你可以在 Service 的 targetPort 属性中引用这些名字。 例如,我们可以通过以下方式将 Service 的 targetPort 绑定到 Pod 端口:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    app.kubernetes.io/name: proxy
spec:
  containers:
  - name: nginx
    image: nginx:stable
    ports:
      - containerPort: 80
        name: http-web-svc

---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app.kubernetes.io/name: proxy
  ports:
  - name: name-of-service-port
    protocol: TCP
    port: 80
    targetPort: http-web-svc

即使在 Service 中混合使用配置名称相同的多个 Pod,各 Pod 通过不同的端口号支持相同的网络协议, 此机制也可以工作。这一机制为 Service 的部署和演化提供了较高的灵活性。 例如,你可以在后端软件的新版本中更改 Pod 公开的端口号,但不会影响到客户端。

Service 的默认协议是 TCP; 你还可以使用其他受支持的任何协议

由于许多 Service 需要公开多个端口,所以 Kubernetes 为同一服务定义多个端口。 每个端口定义可以具有相同的 protocol,也可以具有不同协议。

没有选择算符的 Service

由于选择算符的存在,Service 的最常见用法是为 Kubernetes Pod 集合提供访问抽象, 但是当与相应的 EndpointSlice 对象一起使用且没有设置选择算符时,Service 也可以为其他类型的后端提供抽象, 包括在集群外运行的后端。

例如:

  • 你希望在生产环境中使用外部数据库集群,但在测试环境中使用自己的数据库。
  • 你希望让你的 Service 指向另一个名字空间(Namespace)中或其它集群中的服务。
  • 你正在将工作负载迁移到 Kubernetes 上来。在评估所采用的方法时,你仅在 Kubernetes 中运行一部分后端。

在所有这些场景中,你都可以定义指定用来匹配 Pod 的选择算符的 Service。例如:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376

由于此 Service 没有选择算符,因此不会自动创建对应的 EndpointSlice(和旧版的 Endpoints)对象。 你可以通过手动添加 EndpointSlice 对象,将 Service 映射到该服务运行位置的网络地址和端口:

apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
  name: my-service-1 # 按惯例将服务的名称用作 EndpointSlice 名称的前缀
  labels:
    # 你应设置 "kubernetes.io/service-name" 标签。
    # 设置其值以匹配服务的名称
    kubernetes.io/service-name: my-service
addressType: IPv4
ports:
  - name: '' # 留空,因为 port 9376 未被 IANA 分配为已注册端口
    appProtocol: http
    protocol: TCP
    port: 9376
endpoints:  # 此列表中的 IP 地址可以按任何顺序显示
  - addresses:
      - "10.4.5.6"
  - addresses:
      - "10.1.2.3"

自定义 EndpointSlices

当为 Service 创建 EndpointSlice 对象时,可以为 EndpointSlice 使用任何名称。 一个名字空间中的各个 EndpointSlice 都必须具有一个唯一的名称。通过在 EndpointSlice 上设置 kubernetes.io/service-name 标签可以将 EndpointSlice 链接到 Service。

对于你自己或在你自己代码中创建的 EndpointSlice,你还应该为 endpointslice.kubernetes.io/managed-by 标签设置一个值。如果你创建自己的控制器代码来管理 EndpointSlice, 请考虑使用类似于 "my-domain.example/name-of-controller" 的值。 如果你使用的是第三方工具,请使用全小写的工具名称,并将空格和其他标点符号更改为短划线 (-)。 如果直接使用 kubectl 之类的工具来管理 EndpointSlice 对象,请使用用来描述这种手动管理的名称, 例如 "staff""cluster-admins"。你要避免使用保留值 "controller"; 该值标识由 Kubernetes 自己的控制平面管理的 EndpointSlice。

访问没有选择算符的 Service

访问没有选择算符的 Service 与有选择算符的 Service 的原理相同。 在没有选择算符的 Service 示例中, 流量被路由到 EndpointSlice 清单中定义的两个端点之一: 通过 TCP 协议连接到 10.1.2.3 或 10.4.5.6 的端口 9376。

ExternalName Service 是 Service 的特例,它没有选择算符,而是使用 DNS 名称。 更多的相关信息,请参阅 ExternalName 一节。

EndpointSlices

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

EndpointSlice 对象表示某个服务的后端网络端点的子集(切片)。

你的 Kubernetes 集群会跟踪每个 EndpointSlice 所表示的端点数量。 如果 Service 的端点太多以至于达到阈值,Kubernetes 会添加另一个空的 EndpointSlice 并在其中存储新的端点信息。 默认情况下,一旦现有 EndpointSlice 都包含至少 100 个端点,Kubernetes 就会创建一个新的 EndpointSlice。 在需要添加额外的端点之前,Kubernetes 不会创建新的 EndpointSlice。

参阅 EndpointSlice 了解有关该 API 的更多信息。

Endpoints

在 Kubernetes API 中,Endpoints (该资源类别为复数形式)定义的是网络端点的列表,通常由 Service 引用, 以定义可以将流量发送到哪些 Pod。

推荐使用 EndpointSlice API 替换 Endpoints。

超出容量的端点

Kubernetes 限制单个 Endpoints 对象中可以容纳的端点数量。 当一个 Service 拥有 1000 个以上支撑端点时,Kubernetes 会截断 Endpoints 对象中的数据。 由于一个 Service 可以链接到多个 EndpointSlice 之上,所以 1000 个支撑端点的限制仅影响旧版的 Endpoints API。

如出现端点过多的情况,Kubernetes 选择最多 1000 个可能的后端端点存储到 Endpoints 对象中, 并在 Endpoints 上设置注解 endpoints.kubernetes.io/over-capacity: truncated。 如果后端 Pod 的数量降至 1000 以下,控制平面也会移除该注解。

请求流量仍会被发送到后端,但任何依赖旧版 Endpoints API 的负载均衡机制最多只能将流量发送到 1000 个可用的支撑端点。

这一 API 限制也意味着你不能手动将 Endpoints 更新为拥有超过 1000 个端点。

应用协议

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

appProtocol 字段提供了一种为每个 Service 端口设置应用协议的方式。 此字段被实现代码用作一种提示信息,以便针对实现能够理解的协议提供更为丰富的行为。 此字段的取值会被映射到对应的 Endpoints 和 EndpointSlice 对象中。

此字段遵循标准的 Kubernetes 标签语法。合法的取值值可以是以下之一:

  • IANA 标准服务名称
  • 由具体实现所定义的、带有 mycompany.com/my-custom-protocol 这类前缀的名称。
  • Kubernetes 定义的前缀名称:
    协议描述
    kubernetes.io/h2c基于明文的 HTTP/2 协议,如 RFC 7540 所述

多端口 Service

对于某些 Service,你需要公开多个端口。Kubernetes 允许你为 Service 对象配置多个端口定义。 为 Service 使用多个端口时,必须为所有端口提供名称,以使它们无歧义。 例如:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 9376
    - name: https
      protocol: TCP
      port: 443
      targetPort: 9377

服务类型

对一些应用的某些部分(如前端),你可能希望将其公开于某外部 IP 地址, 也就是可以从集群外部访问的某个地址。

Kubernetes Service 类型允许指定你所需要的 Service 类型。

可用的 type 值及其行为有:

ClusterIP
通过集群的内部 IP 公开 Service,选择该值时 Service 只能够在集群内部访问。 这也是你没有为服务显式指定 type 时使用的默认值。 你可以使用 Ingress 或者 Gateway API 向公共互联网公开服务。
NodePort
通过每个节点上的 IP 和静态端口(NodePort)公开 Service。 为了让 Service 可通过节点端口访问,Kubernetes 会为 Service 配置集群 IP 地址, 相当于你请求了 type: ClusterIP 的服务。
LoadBalancer
使用云平台的负载均衡器向外部公开 Service。Kubernetes 不直接提供负载均衡组件; 你必须提供一个,或者将你的 Kubernetes 集群与某个云平台集成。
ExternalName
将服务映射到 externalName 字段的内容(例如,映射到主机名 api.foo.bar.example)。 该映射将集群的 DNS 服务器配置为返回具有该外部主机名值的 CNAME 记录。 集群不会为之创建任何类型代理。

服务 API 中的 type 字段被设计为层层递进的形式 - 每层都建立在前一层的基础上。 但是,这种层层递进的形式有一个例外。 你可以在定义 LoadBalancer 服务时禁止负载均衡器分配 NodePort

type: ClusterIP

此默认 Service 类型从你的集群中为此预留的 IP 地址池中分配一个 IP 地址。

其他几种 Service 类型在 ClusterIP 类型的基础上进行构建。

如果你定义的 Service 将 .spec.clusterIP 设置为 "None",则 Kubernetes 不会为其分配 IP 地址。有关详细信息,请参阅无头服务

选择自己的 IP 地址

在创建 Service 的请求中,你可以通过设置 spec.clusterIP 字段来指定自己的集群 IP 地址。 比如,希望复用一个已存在的 DNS 条目,或者遗留系统已经配置了一个固定的 IP 且很难重新配置。

你所选择的 IP 地址必须是合法的 IPv4 或者 IPv6 地址,并且这个 IP 地址在 API 服务器上所配置的 service-cluster-ip-range CIDR 范围内。 如果你尝试创建一个带有非法 clusterIP 地址值的 Service,API 服务器会返回 HTTP 状态码 422, 表示值不合法。

请阅读避免冲突节, 以了解 Kubernetes 如何协助降低两个不同的 Service 试图使用相同 IP 地址的风险和影响。

type: NodePort

如果你将 type 字段设置为 NodePort,则 Kubernetes 控制平面将在 --service-node-port-range 标志所指定的范围内分配端口(默认值:30000-32767)。 每个节点将该端口(每个节点上的相同端口号)上的流量代理到你的 Service。 你的 Service 在其 .spec.ports[*].nodePort 字段中报告已分配的端口。

使用 NodePort 可以让你自由设置自己的负载均衡解决方案, 配置 Kubernetes 不完全支持的环境, 甚至直接公开一个或多个节点的 IP 地址。

对于 NodePort 服务,Kubernetes 额外分配一个端口(TCP、UDP 或 SCTP 以匹配 Service 的协议)。 集群中的每个节点都将自己配置为监听所分配的端口,并将流量转发到与该 Service 关联的某个就绪端点。 通过使用合适的协议(例如 TCP)和适当的端口(分配给该 Service)连接到任何一个节点, 你就能够从集群外部访问 type: NodePort 服务。

选择你自己的端口

如果需要特定的端口号,你可以在 nodePort 字段中指定一个值。 控制平面将或者为你分配该端口,或者报告 API 事务失败。 这意味着你需要自行注意可能发生的端口冲突。 你还必须使用有效的端口号,该端口号在配置用于 NodePort 的范围内。

以下是 type: NodePort 服务的一个清单示例,其中指定了 NodePort 值(在本例中为 30007):

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: NodePort
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    # 默认情况下,为了方便起见,`targetPort` 被设置为与 `port` 字段相同的值。
    - port: 80
      targetPort: 80
      # 可选字段
      # 默认情况下,为了方便起见,Kubernetes 控制平面会从某个范围内分配一个端口号
      #(默认:30000-32767)
      nodePort: 30007

预留 NodePort 端口范围以避免发生冲突

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

为 NodePort 服务分配端口的策略既适用于自动分配的情况,也适用于手动分配的场景。 当某个用于希望创建一个使用特定端口的 NodePort 服务时,该目标端口可能与另一个已经被分配的端口冲突。

为了避免这个问题,用于 NodePort 服务的端口范围被分为两段。 动态端口分配默认使用较高的端口段,并且在较高的端口段耗尽时也可以使用较低的端口段。 用户可以从较低端口段中分配端口,降低端口冲突的风险。

type: NodePort 服务自定义 IP 地址配置

你可以配置集群中的节点使用特定 IP 地址来支持 NodePort 服务。 如果每个节点都连接到多个网络(例如:一个网络用于应用流量,另一网络用于节点和控制平面之间的流量), 你可能想要这样做。

如果你要指定特定的 IP 地址来为端口提供代理,可以将 kube-proxy 的 --nodeport-addresses 标志或 kube-proxy 配置文件中的等效字段 nodePortAddresses 设置为特定的 IP 段。

此标志接受逗号分隔的 IP 段列表(例如 10.0.0.0/8192.0.2.0/25),用来设置 IP 地址范围。 kube-proxy 应视将其视为所在节点的本机地址。

例如,如果你使用 --nodeport-addresses=127.0.0.0/8 标志启动 kube-proxy, 则 kube-proxy 仅选择 NodePort 服务的本地回路接口。 --nodeport-addresses 的默认值是一个空的列表。 这意味着 kube-proxy 将认为所有可用网络接口都可用于 NodePort 服务 (这也与早期的 Kubernetes 版本兼容。)

type: LoadBalancer

在使用支持外部负载均衡器的云平台时,如果将 type 设置为 "LoadBalancer", 则平台会为 Service 提供负载均衡器。 负载均衡器的实际创建过程是异步进行的,关于所制备的负载均衡器的信息将会通过 Service 的 status.loadBalancer 字段公开出来。 例如:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
  clusterIP: 10.0.171.239
  type: LoadBalancer
status:
  loadBalancer:
    ingress:
    - ip: 192.0.2.127

来自外部负载均衡器的流量将被直接重定向到后端各个 Pod 上,云平台决定如何进行负载平衡。

要实现 type: LoadBalancer 的服务,Kubernetes 通常首先进行与请求 type: NodePort 服务类似的更改。cloud-controller-manager 组件随后配置外部负载均衡器, 以将流量转发到所分配的节点端口。

你可以将负载均衡 Service 配置为忽略分配节点端口, 前提是云平台实现支持这点。

某些云平台允许你设置 loadBalancerIP。这时,平台将使用用户指定的 loadBalancerIP 来创建负载均衡器。如果没有设置 loadBalancerIP 字段,平台将会给负载均衡器分配一个临时 IP。 如果设置了 loadBalancerIP,但云平台并不支持这一特性,所设置的 loadBalancerIP 值将会被忽略。

混合协议类型的负载均衡器

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

默认情况下,对于 LoadBalancer 类型的 Service,当其中定义了多个端口时, 所有端口必须使用相同的协议,并且该协议必须是被云平台支持的。

当服务中定义了多个端口时,特性门控 MixedProtocolLBService(从 kube-apiserver 1.24 版本起默认为启用)允许 LoadBalancer 类型的服务使用不同的协议。

禁用负载均衡服务的节点端口分配

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

通过设置 Service 的 spec.allocateLoadBalancerNodePortsfalse,你可以对 LoadBalancer 类型的 Service 禁用节点端口分配操作。 这仅适用于负载均衡器的实现能够直接将流量路由到 Pod 而不是使用节点端口的情况。 默认情况下,spec.allocateLoadBalancerNodePortstrue,LoadBalancer 类型的 Service 也会继续分配节点端口。如果某已有 Service 已被分配节点端口,如果将其属性 spec.allocateLoadBalancerNodePorts 设置为 false,这些节点端口不会被自动释放。 你必须显式地在每个 Service 端口中删除 nodePorts 项以释放对应的端口。

设置负载均衡器实现的类别

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

对于 type 设置为 LoadBalancer 的 Service,spec.loadBalancerClass 字段允许你使用有别于云平台的默认负载均衡器的实现。

默认情况下,.spec.loadBalancerClass 是未设置的,如果集群使用 --cloud-provider 件标志配置了云平台,LoadBalancer 类型 Service 会使用云平台的默认负载均衡器实现。

如果你设置了 .spec.loadBalancerClass,则假定存在某个与所指定的类相匹配的负载均衡器实现在监视 Service 变更。所有默认的负载均衡器实现(例如,由云平台所提供的)都会忽略设置了此字段的 Service。 .spec.loadBalancerClass 只能设置到类型为 LoadBalancer 的 Service 之上, 而且一旦设置之后不可变更。

.spec.loadBalancerClass 的值必须是一个标签风格的标识符, 可以有选择地带有类似 "internal-vip" 或 "example.com/internal-vip" 这类前缀。 没有前缀的名字是保留给最终用户的。

指定负载均衡器状态的 IPMode

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

这是从 Kubernetes 1.29 开始的一个 Alpha 级别特性,通过名为 LoadBalancerIPMode特性门控允许你为 typeLoadBalancer 的服务设置 .status.loadBalancer.ingress.ipMode.status.loadBalancer.ingress.ipMode 指定负载均衡器 IP 的行为方式。 此字段只能在 .status.loadBalancer.ingress.ip 字段也被指定时才能指定。

.status.loadBalancer.ingress.ipMode 有两个可能的值:"VIP" 和 "Proxy"。 默认值是 "VIP",意味着流量被传递到目的地设置为负载均衡器 IP 和端口的节点上。 将此字段设置为 "Proxy" 时会出现两种情况,具体取决于云驱动提供的负载均衡器如何传递流量:

  • 如果流量被传递到节点,然后 DNAT 到 Pod,则目的地将被设置为节点的 IP 和节点端口;
  • 如果流量被直接传递到 Pod,则目的地将被设置为 Pod 的 IP 和端口。

服务实现可以使用此信息来调整流量路由。

内部负载均衡器

在混合环境中,有时有必要在同一(虚拟)网络地址段内路由来自 Service 的流量。

在水平分割(Split-Horizon)DNS 环境中,你需要两个 Service 才能将内部和外部流量都路由到你的端点。

如要设置内部负载均衡器,请根据你所使用的云平台,为 Service 添加以下注解之一:

选择一个标签。

metadata:
  name: my-service
  annotations:
      networking.gke.io/load-balancer-type: "Internal"

metadata:
    name: my-service
    annotations:
        service.beta.kubernetes.io/aws-load-balancer-internal: "true"

metadata:
  name: my-service
  annotations:
      service.beta.kubernetes.io/azure-load-balancer-internal: "true"

metadata:
  name: my-service
  annotations:
      service.kubernetes.io/ibm-load-balancer-cloud-provider-ip-type: "private"

metadata:
  name: my-service
  annotations:
    service.beta.kubernetes.io/openstack-internal-load-balancer: "true"

metadata:
  name: my-service
  annotations:
    service.beta.kubernetes.io/cce-load-balancer-internal-vpc: "true"

metadata:
  annotations:
    service.kubernetes.io/qcloud-loadbalancer-internal-subnetid: subnet-xxxxx

metadata:
  annotations:
    service.beta.kubernetes.io/alibaba-cloud-loadbalancer-address-type: "intranet"

metadata:
  name: my-service
  annotations:
      service.beta.kubernetes.io/oci-load-balancer-internal: true

ExternalName 类型

类型为 ExternalName 的 Service 将 Service 映射到 DNS 名称,而不是典型的选择算符, 例如 my-service 或者 cassandra。你可以使用 spec.externalName 参数指定这些服务。

例如,以下 Service 定义将 prod 名字空间中的 my-service 服务映射到 my.database.example.com

apiVersion: v1
kind: Service
metadata:
  name: my-service
  namespace: prod
spec:
  type: ExternalName
  externalName: my.database.example.com

当查找主机 my-service.prod.svc.cluster.local 时,集群 DNS 服务返回 CNAME 记录, 其值为 my.database.example.com。访问 my-service 的方式与访问其他 Service 的方式相同, 主要区别在于重定向发生在 DNS 级别,而不是通过代理或转发来完成。 如果后来你决定将数据库移到集群中,则可以启动其 Pod,添加适当的选择算符或端点并更改 Service 的 type

无头服务(Headless Services)

有时你并不需要负载均衡,也不需要单独的 Service IP。遇到这种情况,可以通过显式设置 集群 IP(spec.clusterIP)的值为 "None" 来创建无头服务(Headless Service)

你可以使用无头 Service 与其他服务发现机制交互,而不必绑定到 Kubernetes 的实现。

无头 Service 不会获得集群 IP,kube-proxy 不会处理这类 Service, 而且平台也不会为它们提供负载均衡或路由支持。 取决于 Service 是否定义了选择算符,DNS 会以不同的方式被自动配置。

带选择算符的服务

对定义了选择算符的无头 Service,Kubernetes 控制平面在 Kubernetes API 中创建 EndpointSlice 对象,并且修改 DNS 配置返回 A 或 AAAA 记录(IPv4 或 IPv6 地址), 这些记录直接指向 Service 的后端 Pod 集合。

无选择算符的服务

对没有定义选择算符的无头 Service,控制平面不会创建 EndpointSlice 对象。 然而 DNS 系统会执行以下操作之一:

  • 对于 type: ExternalName Service,查找和配置其 CNAME 记录;
  • 对所有其他类型的 Service,针对 Service 的就绪端点的所有 IP 地址,查找和配置 DNS A / AAAA 记录:
    • 对于 IPv4 端点,DNS 系统创建 A 记录。
    • 对于 IPv6 端点,DNS 系统创建 AAAA 记录。

当你定义无选择算符的无头 Service 时,port 必须与 targetPort 匹配。

服务发现

对于在集群内运行的客户端,Kubernetes 支持两种主要的服务发现模式:环境变量和 DNS。

环境变量

当 Pod 运行在某 Node 上时,kubelet 会在其中为每个活跃的 Service 添加一组环境变量。 kubelet 会添加环境变量 {SVCNAME}_SERVICE_HOST{SVCNAME}_SERVICE_PORT。 这里 Service 的名称被转为大写字母,横线被转换成下划线。

例如,一个 Service redis-primary 公开了 TCP 端口 6379, 同时被分配了集群 IP 地址 10.0.0.11,这个 Service 生成的环境变量如下:

REDIS_PRIMARY_SERVICE_HOST=10.0.0.11
REDIS_PRIMARY_SERVICE_PORT=6379
REDIS_PRIMARY_PORT=tcp://10.0.0.11:6379
REDIS_PRIMARY_PORT_6379_TCP=tcp://10.0.0.11:6379
REDIS_PRIMARY_PORT_6379_TCP_PROTO=tcp
REDIS_PRIMARY_PORT_6379_TCP_PORT=6379
REDIS_PRIMARY_PORT_6379_TCP_ADDR=10.0.0.11

Kubernetes 还支持并提供与 Docker Engine 的 "legacy container links" 兼容的变量。 你可以阅读 makeLinkVariables 来了解这是如何在 Kubernetes 中实现的。

DNS

你可以(并且几乎总是应该)使用插件(add-on) 来为 Kubernetes 集群安装 DNS 服务。

能够感知集群的 DNS 服务器(例如 CoreDNS)会监视 Kubernetes API 中的新 Service, 并为每个 Service 创建一组 DNS 记录。如果在整个集群中都启用了 DNS,则所有 Pod 都应该能够通过 DNS 名称自动解析 Service。

例如,如果你在 Kubernetes 命名空间 my-ns 中有一个名为 my-service 的 Service, 则控制平面和 DNS 服务共同为 my-service.my-ns 生成 DNS 记录。 名字空间 my-ns 中的 Pod 应该能够通过按名检索 my-service 来找到服务 (my-service.my-ns 也可以)。

其他名字空间中的 Pod 必须将名称限定为 my-service.my-ns。 这些名称将解析为分配给 Service 的集群 IP。

Kubernetes 还支持命名端口的 DNS SRV(Service)记录。 如果 Service my-service.my-ns 具有名为 http 的端口,且协议设置为 TCP, 则可以用 _http._tcp.my-service.my-ns 执行 DNS SRV 查询以发现 http 的端口号以及 IP 地址。

Kubernetes DNS 服务器是唯一的一种能够访问 ExternalName 类型的 Service 的方式。 关于 ExternalName 解析的更多信息可以查看 Service 与 Pod 的 DNS

虚拟 IP 寻址机制

阅读虚拟 IP 和 Service 代理以了解 Kubernetes 提供的使用虚拟 IP 地址公开服务的机制。

流量策略

你可以设置 .spec.internalTrafficPolicy.spec.externalTrafficPolicy 字段来控制 Kubernetes 如何将流量路由到健康(“就绪”)的后端。

有关详细信息,请参阅流量策略

会话的黏性

如果你想确保来自特定客户端的连接每次都传递到同一个 Pod,你可以配置基于客户端 IP 地址的会话亲和性。可阅读会话亲和性 来进一步学习。

外部 IP

如果有外部 IP 能够路由到一个或多个集群节点上,则 Kubernetes Service 可以在这些 externalIPs 上公开出去。当网络流量进入集群时,如果外部 IP(作为目的 IP 地址)和端口都与该 Service 匹配, Kubernetes 所配置的规则和路由会确保流量被路由到该 Service 的端点之一。

定义 Service 时,你可以为任何服务类型指定 externalIPs

在下面的例子中,名为 my-service 的服务可以在 "198.51.100.32:80" (根据 .spec.externalIPs[].spec.ports[].port 得出)上被客户端使用 TCP 协议访问。

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 49152
  externalIPs:
    - 198.51.100.32

API 对象

Service 是 Kubernetes REST API 中的顶级资源。你可以找到有关 Service 对象 API 的更多详细信息。

接下来

进一步学习 Service 及其在 Kubernetes 中所发挥的作用:

  • 完成使用 Service 连接到应用教程。
  • 阅读 Ingress 文档。Ingress 负责将来自集群外部的 HTTP 和 HTTPS 请求路由给集群内的服务。
  • 阅读 Gateway 文档。Gateway 作为 Kubernetes 的扩展提供比 Ingress 更高的灵活性。

更多上下文,可以阅读以下内容:

3.5.2 - Ingress

使用一种能感知协议配置的机制来解析 URI、主机名称、路径等 Web 概念, 让你的 HTTP(或 HTTPS)网络服务可被访问。 Ingress 概念允许你通过 Kubernetes API 定义的规则将流量映射到不同后端。
特性状态: Kubernetes v1.19 [stable]

Ingress 是对集群中服务的外部访问进行管理的 API 对象,典型的访问方式是 HTTP。

Ingress 可以提供负载均衡、SSL 终结和基于名称的虚拟托管。

术语

为了表达更加清晰,本指南定义以下术语:

  • 节点(Node): Kubernetes 集群中的一台工作机器,是集群的一部分。
  • 集群(Cluster): 一组运行容器化应用程序的 Node,这些应用由 Kubernetes 管理。 在此示例和在大多数常见的 Kubernetes 部署环境中,集群中的节点都不在公共网络中。
  • 边缘路由器(Edge Router): 在集群中强制执行防火墙策略的路由器。 可以是由云提供商管理的网关,也可以是物理硬件。
  • 集群网络(Cluster Network): 一组逻辑的或物理的连接,基于 Kubernetes 网络模型实现集群内的通信。
  • 服务(Service):Kubernetes 服务(Service), 使用标签选择算符(Selectors) 来选择一组 Pod。除非另作说明,否则假定 Service 具有只能在集群网络内路由的虚拟 IP。

Ingress 是什么?

Ingress 提供从集群外部到集群内服务的 HTTP 和 HTTPS 路由。 流量路由由 Ingress 资源所定义的规则来控制。

下面是 Ingress 的一个简单示例,可将所有流量都发送到同一 Service:

ingress-diagram

图. Ingress

通过配置,Ingress 可为 Service 提供外部可访问的 URL、对其流量作负载均衡、 终止 SSL/TLS,以及基于名称的虚拟托管等能力。 Ingress 控制器 负责完成 Ingress 的工作,具体实现上通常会使用某个负载均衡器, 不过也可以配置边缘路由器或其他前端来帮助处理流量。

Ingress 不会随意公开端口或协议。 将 HTTP 和 HTTPS 以外的服务开放到 Internet 时,通常使用 Service.Type=NodePortService.Type=LoadBalancer 类型的 Service。

环境准备

你必须拥有一个 Ingress 控制器 才能满足 Ingress 的要求。仅创建 Ingress 资源本身没有任何效果。

你可能需要部署一个 Ingress 控制器,例如 ingress-nginx。 你可以从许多 Ingress 控制器中进行选择。

理想情况下,所有 Ingress 控制器都应遵从参考规范。 但实际上,各个 Ingress 控制器操作略有不同。

Ingress 资源

一个最小的 Ingress 资源示例:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: minimal-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx-example
  rules:
  - http:
      paths:
      - path: /testpath
        pathType: Prefix
        backend:
          service:
            name: test
            port:
              number: 80

Ingress 需要指定 apiVersionkindmetadataspec 字段。 Ingress 对象的命名必须是合法的 DNS 子域名名称。 关于如何使用配置文件的一般性信息,请参见部署应用配置容器管理资源。 Ingress 经常使用注解(Annotations)来配置一些选项,具体取决于 Ingress 控制器, 例如 rewrite-target 注解。 不同的 Ingress 控制器支持不同的注解。 查看你所选的 Ingress 控制器的文档,以了解其所支持的注解。

Ingress 规约 提供了配置负载均衡器或者代理服务器所需要的所有信息。 最重要的是,其中包含对所有入站请求进行匹配的规则列表。 Ingress 资源仅支持用于转发 HTTP(S) 流量的规则。

如果 ingressClassName 被省略,那么你应该定义一个默认的 Ingress 类

有些 Ingress 控制器不需要定义默认的 IngressClass。比如:Ingress-NGINX 控制器可以通过参数 --watch-ingress-without-class 来配置。 不过仍然推荐下文所示来设置默认的 IngressClass

Ingress 规则

每个 HTTP 规则都包含以下信息:

  • 可选的 host。在此示例中,未指定 host,因此该规则基于所指定 IP 地址来匹配所有入站 HTTP 流量。 如果提供了 host(例如 foo.bar.com),则 rules 适用于所指定的主机。
  • 路径列表(例如 /testpath)。每个路径都有一个由 service.nameservice.port.nameservice.port.number 确定的关联后端。 主机和路径都必须与入站请求的内容相匹配,负载均衡器才会将流量引导到所引用的 Service,
  • backend(后端)是 Service 文档中所述的 Service 和端口名称的组合, 或者是通过 CRD 方式来实现的自定义资源后端。 对于发往 Ingress 的 HTTP(和 HTTPS)请求,如果与规则中的主机和路径匹配, 则会被发送到所列出的后端。

通常会在 Ingress 控制器中配置 defaultBackend(默认后端), 以便为无法与规约中任何路径匹配的所有请求提供服务。

默认后端

没有设置规则的 Ingress 将所有流量发送到同一个默认后端,而在这种情况下 .spec.defaultBackend 则是负责处理请求的那个默认后端。 defaultBackend 通常是 Ingress 控制器的配置选项, 而非在 Ingress 资源中设置。 如果未设置 .spec.rules,则必须设置 .spec.defaultBackend。 如果未设置 defaultBackend,那么如何处理与所有规则都不匹配的流量将交由 Ingress 控制器决定(请参考你的 Ingress 控制器的文档以了解它是如何处理这种情况的)。

如果 Ingress 对象中主机和路径都没有与 HTTP 请求匹配,则流量将被路由到默认后端。

资源后端

Resource 后端是一个 ObjectRef 对象,指向同一名字空间中的另一个 Kubernetes 资源, 将其视为 Ingress 对象。 Resource 后端与 Service 后端是互斥的,在二者均被设置时会无法通过合法性检查。 Resource 后端的一种常见用法是将所有入站数据导向保存静态资产的对象存储后端。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-resource-backend
spec:
  defaultBackend:
    resource:
      apiGroup: k8s.example.com
      kind: StorageBucket
      name: static-assets
  rules:
    - http:
        paths:
          - path: /icons
            pathType: ImplementationSpecific
            backend:
              resource:
                apiGroup: k8s.example.com
                kind: StorageBucket
                name: icon-assets

创建了如上的 Ingress 之后,你可以使用下面的命令查看它:

kubectl describe ingress ingress-resource-backend
Name:             ingress-resource-backend
Namespace:        default
Address:
Default backend:  APIGroup: k8s.example.com, Kind: StorageBucket, Name: static-assets
Rules:
  Host        Path  Backends
  ----        ----  --------
  *
              /icons   APIGroup: k8s.example.com, Kind: StorageBucket, Name: icon-assets
Annotations:  <none>
Events:       <none>

路径类型

Ingress 中的每个路径都需要有对应的路径类型(Path Type)。未明确设置 pathType 的路径无法通过合法性检查。当前支持的路径类型有三种:

  • ImplementationSpecific:对于这种路径类型,匹配方法取决于 IngressClass。 具体实现可以将其作为单独的 pathType 处理或者作与 PrefixExact 类型相同的处理。

  • Exact:精确匹配 URL 路径,且区分大小写。

  • Prefix:基于以 / 分隔的 URL 路径前缀匹配。匹配区分大小写, 并且对路径中各个元素逐个执行匹配操作。 路径元素指的是由 / 分隔符分隔的路径中的标签列表。 如果每个 p 都是请求路径 p 的元素前缀,则请求与路径 p 匹配。

示例

类型路径请求路径匹配与否?
Prefix/(所有路径)
Exact/foo/foo
Exact/foo/bar
Exact/foo/foo/
Exact/foo//foo
Prefix/foo/foo, /foo/
Prefix/foo//foo, /foo/
Prefix/aaa/bb/aaa/bbb
Prefix/aaa/bbb/aaa/bbb
Prefix/aaa/bbb//aaa/bbb是,忽略尾部斜线
Prefix/aaa/bbb/aaa/bbb/是,匹配尾部斜线
Prefix/aaa/bbb/aaa/bbb/ccc是,匹配子路径
Prefix/aaa/bbb/aaa/bbbxyz否,字符串前缀不匹配
Prefix/, /aaa/aaa/ccc是,匹配 /aaa 前缀
Prefix/, /aaa, /aaa/bbb/aaa/bbb是,匹配 /aaa/bbb 前缀
Prefix/, /aaa, /aaa/bbb/ccc是,匹配 / 前缀
Prefix/aaa/ccc否,使用默认后端
混合/foo (Prefix), /foo (Exact)/foo是,优选 Exact 类型

多重匹配

在某些情况下,Ingress 中会有多条路径与同一个请求匹配。这时匹配路径最长者优先。 如果仍然有两条同等的匹配路径,则精确路径类型优先于前缀路径类型。

主机名通配符

主机名可以是精确匹配(例如 “foo.bar.com”)或者使用通配符来匹配 (例如 “*.foo.com”)。 精确匹配要求 HTTP host 头部字段与 host 字段值完全匹配。 通配符匹配则要求 HTTP host 头部字段与通配符规则中的后缀部分相同。

主机host 头部匹配与否?
*.foo.combar.foo.com基于相同的后缀匹配
*.foo.combaz.bar.foo.com不匹配,通配符仅覆盖了一个 DNS 标签
*.foo.comfoo.com不匹配,通配符仅覆盖了一个 DNS 标签
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-wildcard-host
spec:
  rules:
  - host: "foo.bar.com"
    http:
      paths:
      - pathType: Prefix
        path: "/bar"
        backend:
          service:
            name: service1
            port:
              number: 80
  - host: "*.foo.com"
    http:
      paths:
      - pathType: Prefix
        path: "/foo"
        backend:
          service:
            name: service2
            port:
              number: 80

Ingress 类

Ingress 可以由不同的控制器实现,通常使用不同的配置。 每个 Ingress 应当指定一个类,也就是一个对 IngressClass 资源的引用。 IngressClass 资源包含额外的配置,其中包括应当实现该类的控制器名称。

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: external-lb
spec:
  controller: example.com/ingress-controller
  parameters:
    apiGroup: k8s.example.com
    kind: IngressParameters
    name: external-lb

IngressClass 中的 .spec.parameters 字段可用于引用其他资源以提供与该 IngressClass 相关的配置。

参数(parameters)的具体类型取决于你在 IngressClass 的 .spec.controller 字段中指定的 Ingress 控制器。

IngressClass 的作用域

取决于你所使用的 Ingress 控制器,你可能可以使用集群作用域的参数或某个名字空间作用域的参数。

IngressClass 参数的默认作用域是集群范围。

如果你设置了 .spec.parameters 字段且未设置 .spec.parameters.scope 字段,或是将 .spec.parameters.scope 字段设为了 Cluster, 那么该 IngressClass 所引用的即是一个集群作用域的资源。 参数的 kind(和 apiGroup 一起)指向一个集群作用域的 API 类型 (可能是一个定制资源(Custom Resource)),而其 name 字段则进一步确定 该 API 类型的一个具体的、集群作用域的资源。

示例:

---
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: external-lb-1
spec:
  controller: example.com/ingress-controller
  parameters:
    # 此 IngressClass 的配置定义在一个名为 “external-config-1” 的
    # ClusterIngressParameter(API 组为 k8s.example.net)资源中。
    # 这项定义告诉 Kubernetes 去寻找一个集群作用域的参数资源。
    scope: Cluster
    apiGroup: k8s.example.net
    kind: ClusterIngressParameter
    name: external-config-1

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

如果你设置了 .spec.parameters 字段且将 .spec.parameters.scope 字段设为了 Namespace,那么该 IngressClass 将会引用一个名字空间作用域的资源。 .spec.parameters.namespace 必须和此资源所处的名字空间相同。

参数的 kind(和 apiGroup 一起)指向一个命名空间作用域的 API 类型 (例如:ConfigMap),而其 name 则进一步确定指定 API 类型的、 位于你指定的命名空间中的具体资源。

名字空间作用域的参数帮助集群操作者将对工作负载所需的配置数据(比如:负载均衡设置、 API 网关定义)的控制权力委派出去。如果你使用集群作用域的参数,那么你将面临一下情况之一:

  • 每次应用一项新的配置变更时,集群操作团队需要批准其他团队所作的修改。
  • 集群操作团队必须定义具体的准入控制规则,比如 RBAC 角色与角色绑定,以使得应用程序团队可以修改集群作用域的配置参数资源。

IngressClass API 本身是集群作用域的。

这里是一个引用名字空间作用域配置参数的 IngressClass 的示例:

---
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: external-lb-2
spec:
  controller: example.com/ingress-controller
  parameters:
    # 此 IngressClass 的配置定义在一个名为 “external-config” 的
    # IngressParameter(API 组为 k8s.example.com)资源中,
    # 该资源位于 “external-configuration” 名字空间中。
    scope: Namespace
    apiGroup: k8s.example.com
    kind: IngressParameter
    namespace: external-configuration
    name: external-config

已废弃的注解

在 Kubernetes 1.18 版本引入 IngressClass 资源和 ingressClassName 字段之前, Ingress 类是通过 Ingress 中的一个 kubernetes.io/ingress.class 注解来指定的。 这个注解从未被正式定义过,但是得到了 Ingress 控制器的广泛支持。

Ingress 中新的 ingressClassName 字段用来替代该注解,但并非完全等价。 注解通常用于引用实现该 Ingress 的控制器的名称,而这个新的字段则是对一个包含额外 Ingress 配置的 IngressClass 资源的引用,其中包括了 Ingress 控制器的名称。

默认 Ingress 类

你可以将一个特定的 IngressClass 标记为集群默认 Ingress 类。 将某个 IngressClass 资源的 ingressclass.kubernetes.io/is-default-class 注解设置为 true 将确保新的未指定 ingressClassName 字段的 Ingress 能够被赋予这一默认 IngressClass.

有一些 Ingress 控制器不需要定义默认的 IngressClass。比如:Ingress-NGINX 控制器可以通过参数 --watch-ingress-without-class 来配置。 不过仍然推荐 设置默认的 IngressClass

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  labels:
    app.kubernetes.io/component: controller
  name: nginx-example
  annotations:
    ingressclass.kubernetes.io/is-default-class: "true"
spec:
  controller: k8s.io/ingress-nginx

Ingress 类型

由单个 Service 来支持的 Ingress

现有的 Kubernetes 概念允许你暴露单个 Service(参见替代方案)。 你也可以使用 Ingress 并设置无规则的默认后端来完成这类操作。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: test-ingress
spec:
  defaultBackend:
    service:
      name: test
      port:
        number: 80

如果使用 kubectl apply -f 创建此 Ingress,则应该能够查看刚刚添加的 Ingress 的状态:

kubectl get ingress test-ingress
NAME           CLASS         HOSTS   ADDRESS         PORTS   AGE
test-ingress   external-lb   *       203.0.113.123   80      59s

其中 203.0.113.123 是由 Ingress 控制器分配的 IP,用以服务于此 Ingress。

简单扇出

一个扇出(Fanout)配置根据请求的 HTTP URI 将来自同一 IP 地址的流量路由到多个 Service。 Ingress 允许你将负载均衡器的数量降至最低。例如,这样的设置:

ingress-fanout-diagram

图. Ingress 扇出

这将需要一个如下所示的 Ingress:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: simple-fanout-example
spec:
  rules:
  - host: foo.bar.com
    http:
      paths:
      - path: /foo
        pathType: Prefix
        backend:
          service:
            name: service1
            port:
              number: 4200
      - path: /bar
        pathType: Prefix
        backend:
          service:
            name: service2
            port:
              number: 8080

当你使用 kubectl apply -f 创建 Ingress 时:

kubectl describe ingress simple-fanout-example
Name:             simple-fanout-example
Namespace:        default
Address:          178.91.123.132
Default backend:  default-http-backend:80 (10.8.2.3:8080)
Rules:
  Host         Path  Backends
  ----         ----  --------
  foo.bar.com
               /foo   service1:4200 (10.8.0.90:4200)
               /bar   service2:8080 (10.8.0.91:8080)
Annotations:
  nginx.ingress.kubernetes.io/rewrite-target:  /
Events:
  Type     Reason  Age                From                     Message
  ----     ------  ----               ----                     -------
  Normal   ADD     22s                loadbalancer-controller  default/test

此 Ingress 控制器构造一个特定于实现的负载均衡器来供 Ingress 使用, 前提是 Service (service1service2)存在。 当它完成负载均衡器的创建时,你会在 Address 字段看到负载均衡器的地址。

基于名称的虚拟主机服务

基于名称的虚拟主机支持将针对多个主机名的 HTTP 流量路由到同一 IP 地址上。

ingress-namebase-diagram

图. 基于名称实现虚拟托管的 Ingress

以下 Ingress 让后台负载均衡器基于 host 头部字段来路由请求。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: name-virtual-host-ingress
spec:
  rules:
  - host: foo.bar.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: service1
            port:
              number: 80
  - host: bar.foo.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: service2
            port:
              number: 80

如果你所创建的 Ingress 资源没有在 rules 中定义主机,则规则可以匹配指向 Ingress 控制器 IP 地址的所有网络流量,而无需基于名称的虚拟主机。

例如,下面的 Ingress 对象会将请求 first.bar.com 的流量路由到 service1,将请求 second.bar.com 的流量路由到 service2,而将所有其他流量路由到 service3

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: name-virtual-host-ingress-no-third-host
spec:
  rules:
  - host: first.bar.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: service1
            port:
              number: 80
  - host: second.bar.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: service2
            port:
              number: 80
  - http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: service3
            port:
              number: 80

TLS

你可以通过设定包含 TLS 私钥和证书的Secret 来保护 Ingress。 Ingress 资源只支持一个 TLS 端口 443,并假定 TLS 连接终止于 Ingress 节点 (与 Service 及其 Pod 间的流量都以明文传输)。 如果 Ingress 中的 TLS 配置部分指定了不同主机,那么它们将通过 SNI TLS 扩展指定的主机名(如果 Ingress 控制器支持 SNI)在同一端口上进行复用。 TLS Secret 的数据中必须包含键名为 tls.crt 的证书和键名为 tls.key 的私钥, 才能用于 TLS 目的。例如:

apiVersion: v1
kind: Secret
metadata:
  name: testsecret-tls
  namespace: default
data:
  tls.crt: base64 编码的证书
  tls.key: base64 编码的私钥
type: kubernetes.io/tls

在 Ingress 中引用此 Secret 将会告诉 Ingress 控制器使用 TLS 加密从客户端到负载均衡器的通道。 你要确保所创建的 TLS Secret 创建自包含 https-example.foo.com 的公共名称 (Common Name,CN)的证书。这里的公共名称也被称为全限定域名(Fully Qualified Domain Name,FQDN)。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: tls-example-ingress
spec:
  tls:
  - hosts:
      - https-example.foo.com
    secretName: testsecret-tls
  rules:
  - host: https-example.foo.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: service1
            port:
              number: 80

负载均衡

Ingress 控制器启动引导时使用一些适用于所有 Ingress 的负载均衡策略设置, 例如负载均衡算法、后端权重方案等。 更高级的负载均衡概念(例如持久会话、动态权重)尚未通过 Ingress 公开。 你可以通过用于 Service 的负载均衡器来获取这些功能。

值得注意的是,尽管健康检查不是通过 Ingress 直接暴露的,在 Kubernetes 中存在就绪态探针 这类等价的概念,供你实现相同的目的。 请查阅特定控制器的说明文档(例如:nginxGCE) 以了解它们是怎样处理健康检查的。

更新 Ingress

要更新现有的 Ingress 以添加新的 Host,可以通过编辑资源来更新它:

kubectl describe ingress test
Name:             test
Namespace:        default
Address:          178.91.123.132
Default backend:  default-http-backend:80 (10.8.2.3:8080)
Rules:
  Host         Path  Backends
  ----         ----  --------
  foo.bar.com
               /foo   service1:80 (10.8.0.90:80)
Annotations:
  nginx.ingress.kubernetes.io/rewrite-target:  /
Events:
  Type     Reason  Age                From                     Message
  ----     ------  ----               ----                     -------
  Normal   ADD     35s                loadbalancer-controller  default/test
kubectl edit ingress test

这一命令将打开编辑器,允许你以 YAML 格式编辑现有配置。 修改它来增加新的主机:

spec:
  rules:
  - host: foo.bar.com
    http:
      paths:
      - backend:
          service:
            name: service1
            port:
              number: 80
        path: /foo
        pathType: Prefix
  - host: bar.baz.com
    http:
      paths:
      - backend:
          service:
            name: service2
            port:
              number: 80
        path: /foo
        pathType: Prefix
..

保存更改后,kubectl 将更新 API 服务器上的资源,该资源将告诉 Ingress 控制器重新配置负载均衡器。

验证:

kubectl describe ingress test
Name:             test
Namespace:        default
Address:          178.91.123.132
Default backend:  default-http-backend:80 (10.8.2.3:8080)
Rules:
  Host         Path  Backends
  ----         ----  --------
  foo.bar.com
               /foo   service1:80 (10.8.0.90:80)
  bar.baz.com
               /foo   service2:80 (10.8.0.91:80)
Annotations:
  nginx.ingress.kubernetes.io/rewrite-target:  /
Events:
  Type     Reason  Age                From                     Message
  ----     ------  ----               ----                     -------
  Normal   ADD     45s                loadbalancer-controller  default/test

你也可以针对修改后的 Ingress YAML 文件,通过 kubectl replace -f 命令获得同样结果。

跨可用区的失效

不同的云厂商使用不同的技术来实现跨故障域的流量分布。 请查看相关 Ingress 控制器的文档以了解详细信息。

替代方案

不直接使用 Ingress 资源,也有多种方法暴露 Service:

接下来

3.5.3 - Ingress 控制器

为了让 Ingress 在你的集群中工作, 必须有一个 Ingress 控制器正在运行。你需要选择至少一个 Ingress 控制器并确保其已被部署到你的集群中。 本页列出了你可以部署的常见 Ingress 控制器。

为了让 Ingress 资源工作,集群必须有一个正在运行的 Ingress 控制器。

与作为 kube-controller-manager 可执行文件的一部分运行的其他类型的控制器不同, Ingress 控制器不是随集群自动启动的。 基于此页面,你可选择最适合你的集群的 ingress 控制器实现。

Kubernetes 作为一个项目,目前支持和维护 AWSGCENginx Ingress 控制器。

其他控制器

使用多个 Ingress 控制器

你可以使用 Ingress 类在集群中部署任意数量的 Ingress 控制器。 请注意你的 Ingress 类资源的 .metadata.name 字段。 当你创建 Ingress 时,你需要用此字段的值来设置 Ingress 对象的 ingressClassName 字段(请参考 IngressSpec v1 reference)。 ingressClassName 是之前的注解做法的替代。

如果你不为 Ingress 指定 IngressClass,并且你的集群中只有一个 IngressClass 被标记为默认,那么 Kubernetes 会将此集群的默认 IngressClass 应用到 Ingress 上。 IngressClass。 你可以通过将 ingressclass.kubernetes.io/is-default-class 注解 的值设置为 "true" 来将一个 IngressClass 标记为集群默认。

理想情况下,所有 Ingress 控制器都应满足此规范,但各种 Ingress 控制器的操作略有不同。

接下来

3.5.4 - Gateway API

网关(Gateway)API 是一组 API 类别,可提供动态基础设施配置和高级流量路由。

Gateway API 通过使用可扩展的、角色导向的、 协议感知的配置机制来提供网络服务。它是一个附加组件, 包含可提供动态基础设施配置和高级流量路由的 API 类别

设计原则

Gateway API 的设计和架构遵从以下原则:

  • 角色导向: Gateway API 类别是基于负责管理 Kubernetes 服务网络的组织角色建模的:
    • 基础设施提供者: 管理使用多个独立集群为多个租户提供服务的基础设施,例如,云提供商。
    • 集群操作员: 管理集群,通常关注策略、网络访问、应用程序权限等。
    • 应用程序开发人员: 管理在集群中运行的应用程序,通常关注应用程序级配置和 Service 组合。
  • 可移植: Gateway API 规范用自定义资源来定义, 并受到许多实现的支持。
  • 表达能力强: Gateway API 类别支持常见流量路由场景的功能,例如基于标头的匹配、流量加权以及其他只能在 Ingress 中使用自定义注解才能实现的功能。
  • 可扩展的: Gateway 允许在 API 的各个层链接自定义资源。这使得在 API 结构内的适当位置进行精细定制成为可能。

资源模型

Gateway API 具有三种稳定的 API 类别:

  • GatewayClass: 定义一组具有配置相同的网关,由实现该类的控制器管理。

  • Gateway: 定义流量处理基础设施(例如云负载均衡器)的一个实例。

  • HTTPRoute: 定义特定于 HTTP 的规则,用于将流量从网关监听器映射到后端网络端点的表示。 这些端点通常表示为 Service

Gateway API 被组织成不同的 API 类别,这些 API 类别具有相互依赖的关系,以支持组织中角色导向的特点。 一个 Gateway 对象只能与一个 GatewayClass 相关联;GatewayClass 描述负责管理此类 Gateway 的网关控制器。 各个(可以是多个)路由类别(例如 HTTPRoute)可以关联到此 Gateway 对象。 Gateway 可以对能够挂接到其 listeners 的路由进行过滤,从而与路由形成双向信任模型。

下图说明这三个稳定的 Gateway API 类别之间的关系:

此图呈现的是三个稳定的 Gateway API 类别之间的关系

GatewayClass

Gateway 可以由不同的控制器实现,通常具有不同的配置。 Gateway 必须引用某 GatewayClass,而后者中包含实现该类的控制器的名称。

下面是一个最精简的 GatewayClass 示例:

apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: example-class
spec:
  controllerName: example.com/gateway-controller

在此示例中,一个实现了 Gateway API 的控制器被配置为管理某些 GatewayClass 对象, 这些对象的控制器名为 example.com/gateway-controller。 归属于此类的 Gateway 对象将由此实现的控制器来管理。

有关此 API 类别的完整定义,请参阅 GatewayClass

Gateway

Gateway 用来描述流量处理基础设施的一个实例。Gateway 定义了一个网络端点,该端点可用于处理流量, 即对 Service 等后端进行过滤、平衡、拆分等。 例如,Gateway 可以代表某个云负载均衡器,或配置为接受 HTTP 流量的集群内代理服务器。

下面是一个精简的 Gateway 资源示例:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: example-gateway
spec:
  gatewayClassName: example-class
  listeners:
  - name: http
    protocol: HTTP
    port: 80

在此示例中,流量处理基础设施的实例被编程为监听 80 端口上的 HTTP 流量。 由于未指定 addresses 字段,因此对应实现的控制器负责将地址或主机名设置到 Gateway 之上。 该地址用作网络端点,用于处理路由中定义的后端网络端点的流量。

有关此类 API 的完整定义,请参阅 Gateway

HTTPRoute

HTTPRoute 类别指定从 Gateway 监听器到后端网络端点的 HTTP 请求的路由行为。 对于服务后端,实现可以将后端网络端点表示为服务 IP 或服务的支持端点。 HTTPRoute 表示将被应用到下层 Gateway 实现的配置。 例如,定义新的 HTTPRoute 可能会导致在云负载均衡器或集群内代理服务器中配置额外的流量路由。

下面是一个最精简的 HTTPRoute 示例:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: example-httproute
spec:
  parentRefs:
  - name: example-gateway
  hostnames:
  - "www.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /login
    backendRefs:
    - name: example-svc
      port: 8080

在此示例中,来自 Gateway example-gateway 的 HTTP 流量, 如果 Host 的标头设置为 www.example.com 且请求路径指定为 /login, 将被路由到 Service example-svc8080 端口。

有关此类 API 的完整定义,请参阅 HTTPRoute

请求数据流

以下是使用 Gateway 和 HTTPRoute 将 HTTP 流量路由到服务的简单示例:

此图为使用 Gateway 和 HTTPRoute 将 HTTP 流量路由到服务的示例

在此示例中,实现为反向代理的 Gateway 的请求数据流如下:

  1. 客户端开始准备 URL 为 http://www.example.com 的 HTTP 请求
  2. 客户端的 DNS 解析器查询目标名称并了解与 Gateway 关联的一个或多个 IP 地址的映射。
  3. 客户端向 Gateway IP 地址发送请求;反向代理接收 HTTP 请求并使用 Host: 标头来匹配基于 Gateway 和附加的 HTTPRoute 所获得的配置。
  4. 可选的,反向代理可以根据 HTTPRoute 的匹配规则进行请求头和(或)路径匹配。
  5. 可选地,反向代理可以修改请求;例如,根据 HTTPRoute 的过滤规则添加或删除标头。
  6. 最后,反向代理将请求转发到一个或多个后端。

标准合规性

Gateway API 涵盖广泛的功能并得到广泛实现。 这种组合需要明确的标准合规性定义和测试,以确保 API 在任何地方使用时都能提供一致的体验。

请参阅合规性相关的文档, 以了解发布渠道、支持级别和运行合规性测试等详细信息。

从 Ingress 迁移

Gateway API 是 Ingress API 的后继者。 但是其中不包括 Ingress 类型。因此,需要将现有 Ingress 资源一次性转换为 Gateway API 资源。

有关将 Ingress 资源迁移到 Gateway API 资源的详细信息,请参阅 Ingress 迁移指南。

接下来

Gateway API 资源不是由 Kubernetes 原生实现的, 而是被定义为受广泛实现支持的自定义资源。 用户需要安装 Gateway API CRD 或按照所选实现的安装说明进行操作。 安装完成后,使用入门指南来帮助你快速开始使用 Gateway API。

有关所有 Gateway API 类型的其他详细信息,请参阅 API 规范

3.5.5 - EndpointSlice

EndpointSlice API 是 Kubernetes 用于扩缩 Service 以处理大量后端的机制,还允许集群高效更新其健康后端的列表。
特性状态: Kubernetes v1.21 [stable]

Kubernetes 的 EndpointSlice API 提供了一种简单的方法来跟踪 Kubernetes 集群中的网络端点(network endpoints)。EndpointSlices 为 Endpoints 提供了一种可扩缩和可拓展的替代方案。

EndpointSlice API

在 Kubernetes 中,EndpointSlice 包含对一组网络端点的引用。 控制面会自动为设置了选择算符的 Kubernetes Service 创建 EndpointSlice。 这些 EndpointSlice 将包含对与 Service 选择算符匹配的所有 Pod 的引用。 EndpointSlice 通过唯一的协议、端口号和 Service 名称将网络端点组织在一起。 EndpointSlice 的名称必须是合法的 DNS 子域名

例如,下面是 Kubernetes Service example 所拥有的 EndpointSlice 对象示例。

apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
  name: example-abc
  labels:
    kubernetes.io/service-name: example
addressType: IPv4
ports:
  - name: http
    protocol: TCP
    port: 80
endpoints:
  - addresses:
      - "10.1.2.3"
    conditions:
      ready: true
    hostname: pod-1
    nodeName: node-1
    zone: us-west2-a

默认情况下,控制面创建和管理的 EndpointSlice 将包含不超过 100 个端点。 你可以使用 kube-controller-manager--max-endpoints-per-slice 标志设置此值,最大值为 1000。

当涉及如何路由内部流量时,EndpointSlice 可以充当 kube-proxy 的决策依据。

地址类型

EndpointSlice 支持三种地址类型:

  • IPv4
  • IPv6
  • FQDN (完全合格的域名)

每个 EndpointSlice 对象代表一个特定的 IP 地址类型。如果你有一个支持 IPv4 和 IPv6 的 Service, 那么将至少有两个 EndpointSlice 对象(一个用于 IPv4,一个用于 IPv6)。

状况

EndpointSlice API 存储了可能对使用者有用的、有关端点的状况。 这三个状况分别是 readyservingterminating

Ready(就绪)

ready 状况是映射 Pod 的 Ready 状况的。 对于处于运行中的 Pod,它的 Ready 状况被设置为 True,应该将此 EndpointSlice 状况也设置为 true。 出于兼容性原因,当 Pod 处于终止过程中,ready 永远不会为 true。 消费者应参考 serving 状况来检查处于终止中的 Pod 的就绪情况。 该规则的唯一例外是将 spec.publishNotReadyAddresses 设置为 true 的 Service。 这些 Service 的端点将始终将 ready 状况设置为 true

Serving(服务中)

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

serving 状况几乎与 ready 状况相同,不同之处在于它不考虑终止状态。 如果 EndpointSlice API 的使用者关心 Pod 终止时的就绪情况,就应检查 serving 状况。

Terminating(终止中)

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

Terminating 是表示端点是否处于终止中的状况。 对于 Pod 来说,这是设置了删除时间戳的 Pod。

拓扑信息

EndpointSlice 中的每个端点都可以包含一定的拓扑信息。 拓扑信息包括端点的位置,对应节点、可用区的信息。 这些信息体现为 EndpointSlices 的如下端点字段:

  • nodeName - 端点所在的 Node 名称;
  • zone - 端点所处的可用区。

管理

通常,控制面(尤其是端点切片的控制器) 会创建和管理 EndpointSlice 对象。EndpointSlice 对象还有一些其他使用场景, 例如作为服务网格(Service Mesh)的实现。 这些场景都会导致有其他实体或者控制器负责管理额外的 EndpointSlice 集合。

为了确保多个实体可以管理 EndpointSlice 而且不会相互产生干扰, Kubernetes 定义了标签 endpointslice.kubernetes.io/managed-by,用来标明哪个实体在管理某个 EndpointSlice。 端点切片控制器会在自己所管理的所有 EndpointSlice 上将该标签值设置为 endpointslice-controller.k8s.io。 管理 EndpointSlice 的其他实体也应该为此标签设置一个唯一值。

属主关系

在大多数场合下,EndpointSlice 都由某个 Service 所有, (因为)该端点切片正是为该服务跟踪记录其端点。这一属主关系是通过为每个 EndpointSlice 设置一个属主(owner)引用,同时设置 kubernetes.io/service-name 标签来标明的, 目的是方便查找隶属于某 Service 的所有 EndpointSlice。

EndpointSlice 镜像

在某些场合,应用会创建定制的 Endpoints 资源。为了保证这些应用不需要并发的更改 Endpoints 和 EndpointSlice 资源,集群的控制面将大多数 Endpoints 映射到对应的 EndpointSlice 之上。

控制面对 Endpoints 资源进行映射的例外情况有:

  • Endpoints 资源上标签 endpointslice.kubernetes.io/skip-mirror 值为 true
  • Endpoints 资源包含标签 control-plane.alpha.kubernetes.io/leader
  • 对应的 Service 资源不存在。
  • 对应的 Service 的选择算符不为空。

每个 Endpoints 资源可能会被转译到多个 EndpointSlices 中去。 当 Endpoints 资源中包含多个子网或者包含多个 IP 协议族(IPv4 和 IPv6)的端点时, 就有可能发生这种状况。 每个子网最多有 1000 个地址会被镜像到 EndpointSlice 中。

EndpointSlices 的分布问题

每个 EndpointSlice 都有一组端口值,适用于资源内的所有端点。 当为 Service 使用命名端口时,Pod 可能会就同一命名端口获得不同的端口号, 因而需要不同的 EndpointSlice。这有点像 Endpoints 用来对子网进行分组的逻辑。

控制面尝试尽量将 EndpointSlice 填满,不过不会主动地在若干 EndpointSlice 之间执行再平衡操作。这里的逻辑也是相对直接的:

  1. 列举所有现有的 EndpointSlices,移除那些不再需要的端点并更新那些已经变化的端点。
  2. 列举所有在第一步中被更改过的 EndpointSlices,用新增加的端点将其填满。
  3. 如果还有新的端点未被添加进去,尝试将这些端点添加到之前未更改的切片中, 或者创建新切片。

这里比较重要的是,与在 EndpointSlice 之间完成最佳的分布相比,第三步中更看重限制 EndpointSlice 更新的操作次数。例如,如果有 10 个端点待添加,有两个 EndpointSlice 中各有 5 个空位,上述方法会创建一个新的 EndpointSlice 而不是将现有的两个 EndpointSlice 都填满。换言之,与执行多个 EndpointSlice 更新操作相比较, 方法会优先考虑执行一个 EndpointSlice 创建操作。

由于 kube-proxy 在每个节点上运行并监视 EndpointSlice 状态,EndpointSlice 的每次变更都变得相对代价较高,因为这些状态变化要传递到集群中每个节点上。 这一方法尝试限制要发送到所有节点上的变更消息个数,即使这样做可能会导致有多个 EndpointSlice 没有被填满。

在实践中,上面这种并非最理想的分布是很少出现的。大多数被 EndpointSlice 控制器处理的变更都是足够小的,可以添加到某已有 EndpointSlice 中去的。 并且,假使无法添加到已有的切片中,不管怎样都很快就会创建一个新的 EndpointSlice 对象。Deployment 的滚动更新为重新为 EndpointSlice 打包提供了一个自然的机会,所有 Pod 及其对应的端点在这一期间都会被替换掉。

重复的端点

由于 EndpointSlice 变化的自身特点,端点可能会同时出现在不止一个 EndpointSlice 中。鉴于不同的 EndpointSlice 对象在不同时刻到达 Kubernetes 的监视/缓存中, 这种情况的出现是很自然的。

与 Endpoints 的比较

原来的 Endpoints API 提供了在 Kubernetes 中跟踪网络端点的一种简单而直接的方法。随着 Kubernetes 集群和服务逐渐开始为更多的后端 Pod 处理和发送请求, 原来的 API 的局限性变得越来越明显。最明显的是那些因为要处理大量网络端点而带来的挑战。

由于任一 Service 的所有网络端点都保存在同一个 Endpoints 对象中,这些 Endpoints 对象可能变得非常巨大。对于保持稳定的服务(长时间使用同一组端点),影响不太明显; 即便如此,Kubernetes 的一些使用场景也没有得到很好的服务。

当某 Service 存在很多后端端点并且该工作负载频繁扩缩或上线新更改时,对该 Service 的单个 Endpoints 对象的每次更新都意味着(在控制平面内以及在节点和 API 服务器之间)Kubernetes 集群组件之间会出现大量流量。 这种额外的流量在 CPU 使用方面也有开销。

使用 EndpointSlices 时,添加或移除单个 Pod 对于正监视变更的客户端会触发相同数量的更新, 但这些更新消息的大小在大规模场景下要小得多。

EndpointSlices 还支持围绕双栈网络和拓扑感知路由等新功能的创新。

接下来

3.5.6 - 网络策略

如果你希望在 IP 地址或端口层面(OSI 第 3 层或第 4 层)控制网络流量, NetworkPolicy 可以让你为集群内以及 Pod 与外界之间的网络流量指定规则。 你的集群必须使用支持 NetworkPolicy 实施的网络插件。

如果你希望针对 TCP、UDP 和 SCTP 协议在 IP 地址或端口层面控制网络流量, 则你可以考虑为集群中特定应用使用 Kubernetes 网络策略(NetworkPolicy)。 NetworkPolicy 是一种以应用为中心的结构,允许你设置如何允许 Pod 与网络上的各类网络“实体” (我们这里使用实体以避免过度使用诸如“端点”和“服务”这类常用术语, 这些术语在 Kubernetes 中有特定含义)通信。 NetworkPolicy 适用于一端或两端与 Pod 的连接,与其他连接无关。

Pod 可以与之通信的实体是通过如下三个标识符的组合来辩识的:

  1. 其他被允许的 Pod(例外:Pod 无法阻塞对自身的访问)
  2. 被允许的名字空间
  3. IP 组块(例外:与 Pod 运行所在的节点的通信总是被允许的, 无论 Pod 或节点的 IP 地址)

在定义基于 Pod 或名字空间的 NetworkPolicy 时, 你会使用选择算符来设定哪些流量可以进入或离开与该算符匹配的 Pod。

另外,当创建基于 IP 的 NetworkPolicy 时,我们基于 IP 组块(CIDR 范围)来定义策略。

前置条件

网络策略通过网络插件来实现。 要使用网络策略,你必须使用支持 NetworkPolicy 的网络解决方案。 创建一个 NetworkPolicy 资源对象而没有控制器来使它生效的话,是没有任何作用的。

Pod 隔离的两种类型

Pod 有两种隔离:出口的隔离和入口的隔离。它们涉及到可以建立哪些连接。 这里的“隔离”不是绝对的,而是意味着“有一些限制”。 另外的,“非隔离方向”意味着在所述方向上没有限制。这两种隔离(或不隔离)是独立声明的, 并且都与从一个 Pod 到另一个 Pod 的连接有关。

默认情况下,一个 Pod 的出口是非隔离的,即所有外向连接都是被允许的。如果有任何的 NetworkPolicy 选择该 Pod 并在其 policyTypes 中包含 "Egress",则该 Pod 是出口隔离的, 我们称这样的策略适用于该 Pod 的出口。当一个 Pod 的出口被隔离时, 唯一允许的来自 Pod 的连接是适用于出口的 Pod 的某个 NetworkPolicy 的 egress 列表所允许的连接。 针对那些允许连接的应答流量也将被隐式允许。 这些 egress 列表的效果是相加的。

默认情况下,一个 Pod 对入口是非隔离的,即所有入站连接都是被允许的。如果有任何的 NetworkPolicy 选择该 Pod 并在其 policyTypes 中包含 “Ingress”,则该 Pod 被隔离入口, 我们称这种策略适用于该 Pod 的入口。当一个 Pod 的入口被隔离时,唯一允许进入该 Pod 的连接是来自该 Pod 节点的连接和适用于入口的 Pod 的某个 NetworkPolicy 的 ingress 列表所允许的连接。针对那些允许连接的应答流量也将被隐式允许。这些 ingress 列表的效果是相加的。

网络策略是相加的,所以不会产生冲突。如果策略适用于 Pod 某一特定方向的流量, Pod 在对应方向所允许的连接是适用的网络策略所允许的集合。 因此,评估的顺序不影响策略的结果。

要允许从源 Pod 到目的 Pod 的某个连接,源 Pod 的出口策略和目的 Pod 的入口策略都需要允许此连接。 如果任何一方不允许此连接,则连接将会失败。

NetworkPolicy 资源

参阅 NetworkPolicy 来了解资源的完整定义。

下面是一个 NetworkPolicy 的示例:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default
spec:
  podSelector:
    matchLabels:
      role: db
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - ipBlock:
        cidr: 172.17.0.0/16
        except:
        - 172.17.1.0/24
    - namespaceSelector:
        matchLabels:
          project: myproject
    - podSelector:
        matchLabels:
          role: frontend
    ports:
    - protocol: TCP
      port: 6379
  egress:
  - to:
    - ipBlock:
        cidr: 10.0.0.0/24
    ports:
    - protocol: TCP
      port: 5978

必需字段:与所有其他的 Kubernetes 配置一样,NetworkPolicy 需要 apiVersionkindmetadata 字段。关于配置文件操作的一般信息, 请参考配置 Pod 以使用 ConfigMap对象管理

spec:NetworkPolicy 规约 中包含了在一个名字空间中定义特定网络策略所需的所有信息。

podSelector:每个 NetworkPolicy 都包括一个 podSelector, 它对该策略所适用的一组 Pod 进行选择。示例中的策略选择带有 "role=db" 标签的 Pod。 空的 podSelector 选择名字空间下的所有 Pod。

policyTypes:每个 NetworkPolicy 都包含一个 policyTypes 列表,其中包含 IngressEgress 或两者兼具。policyTypes 字段表示给定的策略是应用于进入所选 Pod 的入站流量还是来自所选 Pod 的出站流量,或两者兼有。 如果 NetworkPolicy 未指定 policyTypes 则默认情况下始终设置 Ingress; 如果 NetworkPolicy 有任何出口规则的话则设置 Egress

ingress:每个 NetworkPolicy 可包含一个 ingress 规则的白名单列表。 每个规则都允许同时匹配 fromports 部分的流量。示例策略中包含一条简单的规则: 它匹配某个特定端口,来自三个来源中的一个,第一个通过 ipBlock 指定,第二个通过 namespaceSelector 指定,第三个通过 podSelector 指定。

egress:每个 NetworkPolicy 可包含一个 egress 规则的白名单列表。 每个规则都允许匹配 toport 部分的流量。该示例策略包含一条规则, 该规则将指定端口上的流量匹配到 10.0.0.0/24 中的任何目的地。

所以,该网络策略示例:

  1. 隔离 default 名字空间下 role=db 的 Pod (如果它们不是已经被隔离的话)。

  2. (Ingress 规则)允许以下 Pod 连接到 default 名字空间下的带有 role=db 标签的所有 Pod 的 6379 TCP 端口:

    • default 名字空间下带有 role=frontend 标签的所有 Pod
    • 带有 project=myproject 标签的所有名字空间中的 Pod
    • IP 地址范围为 172.17.0.0–172.17.0.255172.17.2.0–172.17.255.255 (即,除了 172.17.1.0/24 之外的所有 172.17.0.0/16
  3. (Egress 规则)允许 default 名字空间中任何带有标签 role=db 的 Pod 到 CIDR 10.0.0.0/24 下 5978 TCP 端口的连接。

有关更多示例,请参阅声明网络策略演练。

选择器 tofrom 的行为

可以在 ingressfrom 部分或 egressto 部分中指定四种选择器:

podSelector:此选择器将在与 NetworkPolicy 相同的名字空间中选择特定的 Pod,应将其允许作为入站流量来源或出站流量目的地。

namespaceSelector:此选择器将选择特定的名字空间,应将所有 Pod 用作其入站流量来源或出站流量目的地。

namespaceSelector 和 podSelector:一个指定 namespaceSelectorpodSelectorto/from 条目选择特定名字空间中的特定 Pod。 注意使用正确的 YAML 语法;下面的策略:

  ...
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          user: alice
      podSelector:
        matchLabels:
          role: client
  ...

此策略在 from 数组中仅包含一个元素,只允许来自标有 role=client 的 Pod 且该 Pod 所在的名字空间中标有 user=alice 的连接。但是这项策略:

  ...
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          user: alice
    - podSelector:
        matchLabels:
          role: client
  ...

它在 from 数组中包含两个元素,允许来自本地名字空间中标有 role=client 的 Pod 的连接,来自任何名字空间中标有 user=alice 的任何 Pod 的连接。

如有疑问,请使用 kubectl describe 查看 Kubernetes 如何解释该策略。

ipBlock:此选择器将选择特定的 IP CIDR 范围以用作入站流量来源或出站流量目的地。 这些应该是集群外部 IP,因为 Pod IP 存在时间短暂的且随机产生。

集群的入站和出站机制通常需要重写数据包的源 IP 或目标 IP。 在发生这种情况时,不确定在 NetworkPolicy 处理之前还是之后发生, 并且对于网络插件、云提供商、Service 实现等的不同组合,其行为可能会有所不同。

对入站流量而言,这意味着在某些情况下,你可以根据实际的原始源 IP 过滤传入的数据包, 而在其他情况下,NetworkPolicy 所作用的 源IP 则可能是 LoadBalancer 或 Pod 的节点等。

对于出站流量而言,这意味着从 Pod 到被重写为集群外部 IP 的 Service IP 的连接可能会或可能不会受到基于 ipBlock 的策略的约束。

默认策略

默认情况下,如果名字空间中不存在任何策略,则所有进出该名字空间中 Pod 的流量都被允许。 以下示例使你可以更改该名字空间中的默认行为。

默认拒绝所有入站流量

你可以通过创建选择所有 Pod 但不允许任何进入这些 Pod 的入站流量的 NetworkPolicy 来为名字空间创建 "default" 隔离策略。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
spec:
  podSelector: {}
  policyTypes:
  - Ingress

这确保即使没有被任何其他 NetworkPolicy 选择的 Pod 仍将被隔离以进行入口。 此策略不影响任何 Pod 的出口隔离。

允许所有入站流量

如果你想允许一个名字空间中所有 Pod 的所有入站连接,你可以创建一个明确允许的策略。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-ingress
spec:
  podSelector: {}
  ingress:
  - {}
  policyTypes:
  - Ingress

有了这个策略,任何额外的策略都不会导致到这些 Pod 的任何入站连接被拒绝。 此策略对任何 Pod 的出口隔离没有影响。

默认拒绝所有出站流量

你可以通过创建选择所有容器但不允许来自这些容器的任何出站流量的 NetworkPolicy 来为名字空间创建 "default" 隔离策略。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
spec:
  podSelector: {}
  policyTypes:
  - Egress

此策略可以确保即使没有被其他任何 NetworkPolicy 选择的 Pod 也不会被允许流出流量。 此策略不会更改任何 Pod 的入站流量隔离行为。

允许所有出站流量

如果要允许来自名字空间中所有 Pod 的所有连接, 则可以创建一个明确允许来自该名字空间中 Pod 的所有出站连接的策略。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-egress
spec:
  podSelector: {}
  egress:
  - {}
  policyTypes:
  - Egress

有了这个策略,任何额外的策略都不会导致来自这些 Pod 的任何出站连接被拒绝。 此策略对进入任何 Pod 的隔离没有影响。

默认拒绝所有入站和所有出站流量

你可以为名字空间创建 "default" 策略,以通过在该名字空间中创建以下 NetworkPolicy 来阻止所有入站和出站流量。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

此策略可以确保即使没有被其他任何 NetworkPolicy 选择的 Pod 也不会被允许入站或出站流量。

网络流量过滤

NetworkPolicy 是为第 4 层连接 (TCP、UDP 和可选的 SCTP)所定义的。对于所有其他协议,这种网络流量过滤的行为可能因网络插件而异。

deny all 网络策略被定义时,此策略只能保证拒绝 TCP、UDP 和 SCTP 连接。 对于 ARP 或 ICMP 这类其他协议,这种网络流量过滤行为是未定义的。 相同的情况也适用于 allow 规则:当特定 Pod 被允许作为入口源或出口目的地时, 对于(例如)ICMP 数据包会发生什么是未定义的。 ICMP 这类协议可能被某些网络插件所允许,而被另一些网络插件所拒绝。

针对某个端口范围

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

在编写 NetworkPolicy 时,你可以针对一个端口范围而不是某个固定端口。

这一目的可以通过使用 endPort 字段来实现,如下例所示:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: multi-port-egress
  namespace: default
spec:
  podSelector:
    matchLabels:
      role: db
  policyTypes:
    - Egress
  egress:
    - to:
        - ipBlock:
            cidr: 10.0.0.0/24
      ports:
        - protocol: TCP
          port: 32000
          endPort: 32768

上面的规则允许名字空间 default 中所有带有标签 role=db 的 Pod 使用 TCP 协议与 10.0.0.0/24 范围内的 IP 通信,只要目标端口介于 32000 和 32768 之间就可以。

使用此字段时存在以下限制:

  • endPort 字段必须等于或者大于 port 字段的值。
  • 只有在定义了 port 时才能定义 endPort
  • 两个字段的设置值都只能是数字。

按标签选择多个名字空间

在这种情况下,你的 Egress NetworkPolicy 使用名字空间的标签名称来将多个名字空间作为其目标。 为此,你需要为目标名字空间设置标签。例如:

kubectl label namespace frontend namespace=frontend
kubectl label namespace backend namespace=backend

在 NetworkPolicy 文档中的 namespaceSelector 下添加标签。例如:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: egress-namespaces
spec:
  podSelector:
    matchLabels:
      app: myapp
  policyTypes:
  - Egress
  egress:
   - to:
     - namespaceSelector:
       matchExpressions:
       - key: namespace
         operator: In
         values: ["frontend", "backend"]

基于名字指向某名字空间

Kubernetes 控制面会在所有名字空间上设置一个不可变更的标签 kubernetes.io/metadata.name。该标签的值是名字空间的名称。

如果 NetworkPolicy 无法在某些对象字段中指向某名字空间, 你可以使用标准的标签方式来指向特定名字空间。

Pod 生命周期

当新的 NetworkPolicy 对象被创建时,网络插件可能需要一些时间来处理这个新对象。 如果受到 NetworkPolicy 影响的 Pod 在网络插件完成 NetworkPolicy 处理之前就被创建了, 那么该 Pod 可能会最初处于无保护状态,而在 NetworkPolicy 处理完成后被应用隔离规则。

一旦 NetworkPolicy 被网络插件处理,

  1. 所有受给定 NetworkPolicy 影响的新建 Pod 都将在启动前被隔离。 NetworkPolicy 的实现必须确保过滤规则在整个 Pod 生命周期内是有效的, 这个生命周期要从该 Pod 的任何容器启动的第一刻算起。 因为 NetworkPolicy 在 Pod 层面被应用,所以 NetworkPolicy 同样适用于 Init 容器、边车容器和常规容器。
  1. Allow 规则最终将在隔离规则之后被应用(或者可能同时被应用)。 在最糟的情况下,如果隔离规则已被应用,但 allow 规则尚未被应用, 那么新建的 Pod 在初始启动时可能根本没有网络连接。

用户所创建的每个 NetworkPolicy 最终都会被网络插件处理,但无法使用 Kubernetes API 来获知确切的处理时间。

因此,若 Pod 启动时使用非预期的网络连接,它必须保持稳定。 如果你需要确保 Pod 在启动之前能够访问特定的目标,可以使用 Init 容器在 kubelet 启动应用容器之前等待这些目的地变得可达。

每个 NetworkPolicy 最终都会被应用到所选定的所有 Pod 之上。 由于网络插件可能以分布式的方式实现 NetworkPolicy,所以当 Pod 被首次创建时或当 Pod 或策略发生变化时,Pod 可能会看到稍微不一致的网络策略视图。 例如,新建的 Pod 本来应能立即访问 Node 1 上的 Pod A 和 Node 2 上的 Pod B, 但可能你会发现新建的 Pod 可以立即访问 Pod A,但要在几秒后才能访问 Pod B。

NetworkPolicy 和 hostNetwork Pod

针对 hostNetwork Pod 的 NetworkPolicy 行为是未定义的,但应限制为以下两种可能:

  • 网络插件可以从所有其他流量中辨别出 hostNetwork Pod 流量 (包括能够从同一节点上的不同 hostNetwork Pod 中辨别流量), 网络插件还可以像处理 Pod 网络流量一样,对 hostNetwork Pod 应用 NetworkPolicy。
  • 网络插件无法正确辨别 hostNetwork Pod 流量,因此在匹配 podSelectornamespaceSelector 时会忽略 hostNetwork Pod。流向/来自 hostNetwork Pod 的流量的处理方式与流向/来自节点 IP 的所有其他流量一样。(这是最常见的实现方式。)

这适用于以下情形:

  1. hostNetwork Pod 被 spec.podSelector 选中。

      ...
      spec:
        podSelector:
          matchLabels:
            role: client
      ...
    
  1. hostNetwork Pod 在 ingressegress 规则中被 podSelectornamespaceSelector 选中。

      ...
      ingress:
        - from:
          - podSelector:
              matchLabels:
                role: client
      ...
    

同时,由于 hostNetwork Pod 具有与其所在节点相同的 IP 地址,所以它们的连接将被视为节点连接。 例如,你可以使用 ipBlock 规则允许来自 hostNetwork Pod 的流量。

通过网络策略(至少目前还)无法完成的工作

到 Kubernetes 1.30 为止,NetworkPolicy API 还不支持以下功能, 不过你可能可以使用操作系统组件(如 SELinux、OpenVSwitch、IPTables 等等) 或者第七层技术(Ingress 控制器、服务网格实现)或准入控制器来实现一些替代方案。 如果你对 Kubernetes 中的网络安全性还不太了解,了解使用 NetworkPolicy API 还无法实现下面的用户场景是很值得的。

  • 强制集群内部流量经过某公用网关(这种场景最好通过服务网格或其他代理来实现);
  • 与 TLS 相关的场景(考虑使用服务网格或者 Ingress 控制器);
  • 特定于节点的策略(你可以使用 CIDR 来表达这一需求不过你无法使用节点在 Kubernetes 中的其他标识信息来辩识目标节点);
  • 基于名字来选择服务(不过,你可以使用 标签 来选择目标 Pod 或名字空间,这也通常是一种可靠的替代方案);
  • 创建或管理由第三方来实际完成的“策略请求”;
  • 实现适用于所有名字空间或 Pod 的默认策略(某些第三方 Kubernetes 发行版本或项目可以做到这点);
  • 高级的策略查询或者可达性相关工具;
  • 生成网络安全事件日志的能力(例如,被阻塞或接收的连接请求);
  • 显式地拒绝策略的能力(目前,NetworkPolicy 的模型默认采用拒绝操作, 其唯一的能力是添加允许策略);
  • 禁止本地回路或指向宿主的网络流量(Pod 目前无法阻塞 localhost 访问, 它们也无法禁止来自所在节点的访问请求)。

NetworkPolicy 对现有连接的影响

当应用到现有连接的 NetworkPolicy 集合发生变化时,这可能是由于 NetworkPolicy 发生变化或者在现有连接过程中(主体和对等方)策略所选择的名字空间或 Pod 的相关标签发生变化, 此时是否对该现有连接生效是由实现定义的。例如:某个策略的创建导致之前允许的连接被拒绝, 底层网络插件的实现负责定义这个新策略是否会关闭现有的连接。 建议不要以可能影响现有连接的方式修改策略、Pod 或名字空间。

接下来

  • 有关更多示例,请参阅声明网络策略演练。
  • 有关 NetworkPolicy 资源所支持的常见场景的更多信息, 请参见此指南

3.5.7 - Service 与 Pod 的 DNS

你的工作负载可以使用 DNS 发现集群内的 Service,本页说明具体工作原理。

Kubernetes 为 Service 和 Pod 创建 DNS 记录。 你可以使用一致的 DNS 名称而非 IP 地址访问 Service。

Kubernetes 发布有关 Pod 和 Service 的信息,这些信息被用来对 DNS 进行编程。 Kubelet 配置 Pod 的 DNS,以便运行中的容器可以通过名称而不是 IP 来查找服务。

集群中定义的 Service 被赋予 DNS 名称。 默认情况下,客户端 Pod 的 DNS 搜索列表会包含 Pod 自身的名字空间和集群的默认域。

Service 的名字空间

DNS 查询可能因为执行查询的 Pod 所在的名字空间而返回不同的结果。 不指定名字空间的 DNS 查询会被限制在 Pod 所在的名字空间内。 要访问其他名字空间中的 Service,需要在 DNS 查询中指定名字空间。

例如,假定名字空间 test 中存在一个 Pod,prod 名字空间中存在一个服务 data

Pod 查询 data 时没有返回结果,因为使用的是 Pod 的名字空间 test

Pod 查询 data.prod 时则会返回预期的结果,因为查询中指定了名字空间。

DNS 查询可以使用 Pod 中的 /etc/resolv.conf 展开。 Kubelet 为每个 Pod 配置此文件。 例如,对 data 的查询可能被展开为 data.test.svc.cluster.localsearch 选项的取值会被用来展开查询。要进一步了解 DNS 查询,可参阅 resolv.conf 手册页面

nameserver 10.32.0.10
search <namespace>.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

概括起来,名字空间 test 中的 Pod 可以成功地解析 data.prod 或者 data.prod.svc.cluster.local

DNS 记录

哪些对象会获得 DNS 记录呢?

  1. Services
  2. Pods

以下各节详细介绍已支持的 DNS 记录类型和布局。 其它布局、名称或者查询即使碰巧可以工作,也应视为实现细节, 将来很可能被更改而且不会因此发出警告。 有关最新规范请查看 Kubernetes 基于 DNS 的服务发现

Service

A/AAAA 记录

除了无头 Service 之外的 “普通” Service 会被赋予一个形如 my-svc.my-namespace.svc.cluster-domain.example 的 DNS A 和/或 AAAA 记录,取决于 Service 的 IP 协议族(可能有多个)设置。 该名称会解析成对应 Service 的集群 IP。

没有集群 IP 的无头 Service 也会被赋予一个形如 my-svc.my-namespace.svc.cluster-domain.example 的 DNS A 和/或 AAAA 记录。 与普通 Service 不同,这一记录会被解析成对应 Service 所选择的 Pod IP 的集合。 客户端要能够使用这组 IP,或者使用标准的轮转策略从这组 IP 中进行选择。

SRV 记录

Kubernetes 根据普通 Service 或无头 Service 中的命名端口创建 SRV 记录。每个命名端口, SRV 记录格式为 _port-name._port-protocol.my-svc.my-namespace.svc.cluster-domain.example。 普通 Service,该记录会被解析成端口号和域名:my-svc.my-namespace.svc.cluster-domain.example。 无头 Service,该记录会被解析成多个结果,及该服务的每个后端 Pod 各一个 SRV 记录, 其中包含 Pod 端口号和格式为 hostname.my-svc.my-namespace.svc.cluster-domain.example 的域名。

Pod

A/AAAA 记录

在实现 DNS 规范之前, Kube-DNS 版本使用以下 DNS 解析:

pod-ipv4-address.my-namespace.pod.cluster-domain.example

例如,对于一个位于 default 名字空间,IP 地址为 172.17.0.3 的 Pod, 如果集群的域名为 cluster.local,则 Pod 会对应 DNS 名称:

172-17-0-3.default.pod.cluster.local

通过 Service 暴露出来的所有 Pod 都会有如下 DNS 解析名称可用:

pod-ipv4-address.service-name.my-namespace.svc.cluster-domain.example

Pod 的 hostname 和 subdomain 字段

当前,创建 Pod 时其主机名(从 Pod 内部观察)取自 Pod 的 metadata.name 值。

Pod 规约中包含一个可选的 hostname 字段,可以用来指定一个不同的主机名。 当这个字段被设置时,它将优先于 Pod 的名字成为该 Pod 的主机名(同样是从 Pod 内部观察)。 举个例子,给定一个 spec.hostname 设置为 “my-host” 的 Pod, 该 Pod 的主机名将被设置为 “my-host”

Pod 规约还有一个可选的 subdomain 字段,可以用来表明该 Pod 是名字空间的子组的一部分。 举个例子,某 Pod 的 spec.hostname 设置为 “foo”spec.subdomain 设置为 “bar”, 在名字空间 “my-namespace” 中,主机名称被设置成 “foo” 并且对应的完全限定域名(FQDN)为 “foo.bar.my-namespace.svc.cluster-domain.example”(还是从 Pod 内部观察)。

如果 Pod 所在的名字空间中存在一个无头服务,其名称与子域相同, 则集群的 DNS 服务器还会为 Pod 的完全限定主机名返回 A 和/或 AAAA 记录。

示例:

apiVersion: v1
kind: Service
metadata:
  name: busybox-subdomain
spec:
  selector:
    name: busybox
  clusterIP: None
  ports:
  - name: foo # 实际上不需要指定端口号
    port: 1234
---
apiVersion: v1
kind: Pod
metadata:
  name: busybox1
  labels:
    name: busybox
spec:
  hostname: busybox-1
  subdomain: busybox-subdomain
  containers:
  - image: busybox:1.28
    command:
      - sleep
      - "3600"
    name: busybox
---
apiVersion: v1
kind: Pod
metadata:
  name: busybox2
  labels:
    name: busybox
spec:
  hostname: busybox-2
  subdomain: busybox-subdomain
  containers:
  - image: busybox:1.28
    command:
      - sleep
      - "3600"
    name: busybox

鉴于上述服务 “busybox-subdomain” 和将 spec.subdomain 设置为 “busybox-subdomain” 的 Pod, 第一个 Pod 将看到自己的 FQDN 为 “busybox-1.busybox-subdomain.my-namespace.svc.cluster-domain.example”。 DNS 会为此名字提供一个 A 记录和/或 AAAA 记录,指向该 Pod 的 IP。 Pod “busybox1” 和 “busybox2” 都将有自己的地址记录。

EndpointSlice 对象可以为任何端点地址及其 IP 指定 hostname

Pod 的 setHostnameAsFQDN 字段

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

当 Pod 配置为具有全限定域名 (FQDN) 时,其主机名是短主机名。 例如,如果你有一个具有完全限定域名 busybox-1.busybox-subdomain.my-namespace.svc.cluster-domain.example 的 Pod, 则默认情况下,该 Pod 内的 hostname 命令返回 busybox-1,而 hostname --fqdn 命令返回 FQDN。

当你在 Pod 规约中设置了 setHostnameAsFQDN: true 时,kubelet 会将 Pod 的全限定域名(FQDN)作为该 Pod 的主机名记录到 Pod 所在名字空间。 在这种情况下,hostnamehostname --fqdn 都会返回 Pod 的全限定域名。

Pod 的 DNS 策略

DNS 策略可以逐个 Pod 来设定。目前 Kubernetes 支持以下特定 Pod 的 DNS 策略。 这些策略可以在 Pod 规约中的 dnsPolicy 字段设置:

  • "Default": Pod 从运行所在的节点继承名称解析配置。 参考相关讨论获取更多信息。
  • "ClusterFirst": 与配置的集群域后缀不匹配的任何 DNS 查询(例如 "www.kubernetes.io") 都会由 DNS 服务器转发到上游名称服务器。集群管理员可能配置了额外的存根域和上游 DNS 服务器。 参阅相关讨论 了解在这些场景中如何处理 DNS 查询的信息。
  • "ClusterFirstWithHostNet": 对于以 hostNetwork 方式运行的 Pod,应将其 DNS 策略显式设置为 "ClusterFirstWithHostNet"。否则,以 hostNetwork 方式和 "ClusterFirst" 策略运行的 Pod 将会做出回退至 "Default" 策略的行为。
    • 注意:这在 Windows 上不支持。 有关详细信息,请参见下文
  • "None": 此设置允许 Pod 忽略 Kubernetes 环境中的 DNS 设置。Pod 会使用其 dnsConfig 字段所提供的 DNS 设置。 参见 Pod 的 DNS 配置节。

下面的示例显示了一个 Pod,其 DNS 策略设置为 "ClusterFirstWithHostNet", 因为它已将 hostNetwork 设置为 true

apiVersion: v1
kind: Pod
metadata:
  name: busybox
  namespace: default
spec:
  containers:
  - image: busybox:1.28
    command:
      - sleep
      - "3600"
    imagePullPolicy: IfNotPresent
    name: busybox
  restartPolicy: Always
  hostNetwork: true
  dnsPolicy: ClusterFirstWithHostNet

Pod 的 DNS 配置

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

Pod 的 DNS 配置可让用户对 Pod 的 DNS 设置进行更多控制。

dnsConfig 字段是可选的,它可以与任何 dnsPolicy 设置一起使用。 但是,当 Pod 的 dnsPolicy 设置为 "None" 时,必须指定 dnsConfig 字段。

用户可以在 dnsConfig 字段中指定以下属性:

  • nameservers:将用作于 Pod 的 DNS 服务器的 IP 地址列表。 最多可以指定 3 个 IP 地址。当 Pod 的 dnsPolicy 设置为 "None" 时, 列表必须至少包含一个 IP 地址,否则此属性是可选的。 所列出的服务器将合并到从指定的 DNS 策略生成的基本名称服务器,并删除重复的地址。

  • searches:用于在 Pod 中查找主机名的 DNS 搜索域的列表。此属性是可选的。 指定此属性时,所提供的列表将合并到根据所选 DNS 策略生成的基本搜索域名中。 重复的域名将被删除。Kubernetes 最多允许 32 个搜索域。

  • options:可选的对象列表,其中每个对象可能具有 name 属性(必需)和 value 属性(可选)。 此属性中的内容将合并到从指定的 DNS 策略生成的选项。 重复的条目将被删除。

以下是具有自定义 DNS 设置的 Pod 示例:

apiVersion: v1
kind: Pod
metadata:
  namespace: default
  name: dns-example
spec:
  containers:
    - name: test
      image: nginx
  dnsPolicy: "None"
  dnsConfig:
    nameservers:
      - 192.0.2.1 # 这是一个示例
    searches:
      - ns1.svc.cluster-domain.example
      - my.dns.search.suffix
    options:
      - name: ndots
        value: "2"
      - name: edns0

创建上面的 Pod 后,容器 test 会在其 /etc/resolv.conf 文件中获取以下内容:

nameserver 192.0.2.1
search ns1.svc.cluster-domain.example my.dns.search.suffix
options ndots:2 edns0

对于 IPv6 设置,搜索路径和名称服务器应按以下方式设置:

kubectl exec -it dns-example -- cat /etc/resolv.conf

输出类似于:

nameserver 2001:db8:30::a
search default.svc.cluster-domain.example svc.cluster-domain.example cluster-domain.example
options ndots:5

DNS 搜索域列表限制

特性状态: Kubernetes 1.28 [stable]

Kubernetes 本身不限制 DNS 配置,最多可支持 32 个搜索域列表,所有搜索域的总长度不超过 2048。 此限制分别适用于节点的解析器配置文件、Pod 的 DNS 配置和合并的 DNS 配置。

Windows 节点上的 DNS 解析

  • 在 Windows 节点上运行的 Pod 不支持 ClusterFirstWithHostNet。 Windows 将所有带有 . 的名称视为全限定域名(FQDN)并跳过全限定域名(FQDN)解析。
  • 在 Windows 上,可以使用的 DNS 解析器有很多。 由于这些解析器彼此之间会有轻微的行为差别,建议使用 Resolve-DNSName powershell cmdlet 进行名称查询解析。
  • 在 Linux 上,有一个 DNS 后缀列表,当解析全名失败时可以使用。 在 Windows 上,你只能有一个 DNS 后缀, 即与该 Pod 的命名空间相关联的 DNS 后缀(例如:mydns.svc.cluster.local)。 Windows 可以解析全限定域名(FQDN),和使用了该 DNS 后缀的 Services 或者网络名称。 例如,在 default 命名空间中生成一个 Pod,该 Pod 会获得的 DNS 后缀为 default.svc.cluster.local。 在 Windows 的 Pod 中,你可以解析 kubernetes.default.svc.cluster.localkubernetes, 但是不能解析部分限定名称(kubernetes.defaultkubernetes.default.svc)。

接下来

有关管理 DNS 配置的指导, 请查看配置 DNS 服务

3.5.8 - IPv4/IPv6 双协议栈

Kubernetes 允许你配置单协议栈 IPv4 网络、单协议栈 IPv6 网络或同时激活这两种网络的双协议栈网络。本页说明具体配置方法。
特性状态: Kubernetes v1.23 [stable]

IPv4/IPv6 双协议栈网络能够将 IPv4 和 IPv6 地址分配给 PodService

从 1.21 版本开始,Kubernetes 集群默认启用 IPv4/IPv6 双协议栈网络, 以支持同时分配 IPv4 和 IPv6 地址。

支持的功能

Kubernetes 集群的 IPv4/IPv6 双协议栈可提供下面的功能:

  • 双协议栈 Pod 网络(每个 Pod 分配一个 IPv4 和 IPv6 地址)
  • IPv4 和 IPv6 启用的 Service
  • Pod 的集群外出口通过 IPv4 和 IPv6 路由

先决条件

为了使用 IPv4/IPv6 双栈的 Kubernetes 集群,需要满足以下先决条件:

  • Kubernetes 1.20 版本或更高版本,有关更早 Kubernetes 版本的使用双栈 Service 的信息, 请参考对应版本的 Kubernetes 文档。
  • 提供商支持双协议栈网络(云提供商或其他提供商必须能够为 Kubernetes 节点提供可路由的 IPv4/IPv6 网络接口)。
  • 支持双协议栈的网络插件

配置 IPv4/IPv6 双协议栈

如果配置 IPv4/IPv6 双栈,请分配双栈集群网络:

  • kube-apiserver:
    • --service-cluster-ip-range=<IPv4 CIDR>,<IPv6 CIDR>
  • kube-controller-manager:
    • --cluster-cidr=<IPv4 CIDR>,<IPv6 CIDR>
    • --service-cluster-ip-range=<IPv4 CIDR>,<IPv6 CIDR>
    • --node-cidr-mask-size-ipv4|--node-cidr-mask-size-ipv6 对于 IPv4 默认为 /24, 对于 IPv6 默认为 /64
  • kube-proxy:
    • --cluster-cidr=<IPv4 CIDR>,<IPv6 CIDR>
  • kubelet:
    • --node-ip=<IPv4 IP>,<IPv6 IP>
      • 裸机双栈节点(未使用 --cloud-provider 标志定义云平台的节点)需要此选项。 如果你使用了某个云平台并选择覆盖云平台所选择的节点 IP,请设置 --node-ip 选项。
      • (传统的内置云平台实现不支持双栈 --node-ip。)
特性状态: Kubernetes v1.27 [alpha]

Service

你可以使用 IPv4 或 IPv6 地址来创建 Service

Service 的地址族默认为第一个服务集群 IP 范围的地址族(通过 kube-apiserver 的 --service-cluster-ip-range 参数配置)。

当你定义 Service 时,可以选择将其配置为双栈。若要指定所需的行为,你可以设置 .spec.ipFamilyPolicy 字段为以下值之一:

  • SingleStack:单栈 Service。控制面使用第一个配置的服务集群 IP 范围为 Service 分配集群 IP。
  • PreferDualStack
    • 为 Service 分配 IPv4 和 IPv6 集群 IP 地址。
  • RequireDualStack:从 IPv4 和 IPv6 的地址范围分配 Service 的 .spec.ClusterIPs
    • 从基于在 .spec.ipFamilies 数组中第一个元素的地址族的 .spec.ClusterIPs 列表中选择 .spec.ClusterIP

如果你想要定义哪个 IP 族用于单栈或定义双栈 IP 族的顺序,可以通过设置 Service 上的可选字段 .spec.ipFamilies 来选择地址族。

你可以设置 .spec.ipFamily 为以下任何数组值:

  • ["IPv4"]
  • ["IPv6"]
  • ["IPv4","IPv6"] (双栈)
  • ["IPv6","IPv4"] (双栈)

你所列出的第一个地址族用于原来的 .spec.ClusterIP 字段。

双栈 Service 配置场景

以下示例演示多种双栈 Service 配置场景下的行为。

新 Service 的双栈选项

  1. 此 Service 规约中没有显式设定 .spec.ipFamilyPolicy。当你创建此 Service 时,Kubernetes 从所配置的第一个 service-cluster-ip-range 中为 Service 分配一个集群 IP,并设置 .spec.ipFamilyPolicySingleStack。 (无选择算符的 Service无头服务(Headless Service)的行为方式与此相同。)

    apiVersion: v1
    kind: Service
    metadata:
      name: my-service
      labels:
        app.kubernetes.io/name: MyApp
    spec:
      selector:
        app.kubernetes.io/name: MyApp
      ports:
        - protocol: TCP
          port: 80
    
  1. 此 Service 规约显式地将 .spec.ipFamilyPolicy 设置为 PreferDualStack。 当你在双栈集群上创建此 Service 时,Kubernetes 会为此 Service 分配 IPv4 和 IPv6 地址。 控制平面更新 Service 的 .spec 以记录 IP 地址分配。 字段 .spec.ClusterIPs 是主要字段,包含两个分配的 IP 地址;.spec.ClusterIP 是次要字段, 其取值从 .spec.ClusterIPs 计算而来。

    • 对于 .spec.ClusterIP 字段,控制面记录来自第一个服务集群 IP 范围对应的地址族的 IP 地址。
    • 对于单协议栈的集群,.spec.ClusterIPs.spec.ClusterIP 字段都 仅仅列出一个地址。
    • 对于启用了双协议栈的集群,将 .spec.ipFamilyPolicy 设置为 RequireDualStack 时,其行为与 PreferDualStack 相同。
    apiVersion: v1
    kind: Service
    metadata:
      name: my-service
      labels:
        app.kubernetes.io/name: MyApp
    spec:
      ipFamilyPolicy: PreferDualStack
      selector:
        app.kubernetes.io/name: MyApp
      ports:
        - protocol: TCP
          port: 80
    
  1. 下面的 Service 规约显式地在 .spec.ipFamilies 中指定 IPv6IPv4,并将 .spec.ipFamilyPolicy 设定为 PreferDualStack。 当 Kubernetes 为 .spec.ClusterIPs 分配一个 IPv6 和一个 IPv4 地址时, .spec.ClusterIP 被设置成 IPv6 地址,因为它是 .spec.ClusterIPs 数组中的第一个元素, 覆盖其默认值。

    apiVersion: v1
    kind: Service
    metadata:
      name: my-service
      labels:
        app.kubernetes.io/name: MyApp
    spec:
      ipFamilyPolicy: PreferDualStack
      ipFamilies:
      - IPv6
      - IPv4
      selector:
        app.kubernetes.io/name: MyApp
      ports:
        - protocol: TCP
          port: 80
    

现有 Service 的双栈默认值

下面示例演示了在 Service 已经存在的集群上新启用双栈时的默认行为。 (将现有集群升级到 1.21 或者更高版本会启用双协议栈支持。)

  1. 在集群上启用双栈时,控制面会将现有 Service(无论是 IPv4 还是 IPv6)配置 .spec.ipFamilyPolicySingleStack 并设置 .spec.ipFamilies 为 Service 的当前地址族。

    apiVersion: v1
    kind: Service
    metadata:
      name: my-service
      labels:
        app.kubernetes.io/name: MyApp
    spec:
      selector:
        app.kubernetes.io/name: MyApp
      ports:
        - protocol: TCP
          port: 80
    

    你可以通过使用 kubectl 检查现有 Service 来验证此行为。

    kubectl get svc my-service -o yaml
    
    apiVersion: v1
    kind: Service
    metadata:
      labels:
        app.kubernetes.io/name: MyApp
      name: my-service
    spec:
      clusterIP: 10.0.197.123
      clusterIPs:
      - 10.0.197.123
      ipFamilies:
      - IPv4
      ipFamilyPolicy: SingleStack
      ports:
      - port: 80
        protocol: TCP
        targetPort: 80
      selector:
        app.kubernetes.io/name: MyApp
      type: ClusterIP
    status:
      loadBalancer: {}
    
  1. 在集群上启用双栈时,带有选择算符的现有 无头服务 由控制面设置 .spec.ipFamilyPolicySingleStack 并设置 .spec.ipFamilies 为第一个服务集群 IP 范围的地址族(通过配置 kube-apiserver 的 --service-cluster-ip-range 参数),即使 .spec.ClusterIP 的设置值为 None 也如此。

    apiVersion: v1
    kind: Service
    metadata:
      name: my-service
      labels:
        app.kubernetes.io/name: MyApp
    spec:
      selector:
        app.kubernetes.io/name: MyApp
      ports:
        - protocol: TCP
          port: 80
    

    你可以通过使用 kubectl 检查带有选择算符的现有无头服务来验证此行为。

    kubectl get svc my-service -o yaml
    
    apiVersion: v1
    kind: Service
    metadata:
      labels:
        app.kubernetes.io/name: MyApp
      name: my-service
    spec:
      clusterIP: None
      clusterIPs:
      - None
      ipFamilies:
      - IPv4
      ipFamilyPolicy: SingleStack
      ports:
      - port: 80
        protocol: TCP
        targetPort: 80
      selector:
        app.kubernetes.io/name: MyApp
    

在单栈和双栈之间切换 Service

Service 可以从单栈更改为双栈,也可以从双栈更改为单栈。

  1. 要将 Service 从单栈更改为双栈,根据需要将 .spec.ipFamilyPolicySingleStack 改为 PreferDualStackRequireDualStack。 当你将此 Service 从单栈更改为双栈时,Kubernetes 将分配缺失的地址族, 以便现在此 Service具有 IPv4 和 IPv6 地址。 编辑 Service 规约将 .spec.ipFamilyPolicySingleStack 改为 PreferDualStack

    之前:

    spec:
      ipFamilyPolicy: SingleStack
    

    之后:

    spec:
      ipFamilyPolicy: PreferDualStack
    
  1. 要将 Service 从双栈更改为单栈,请将 .spec.ipFamilyPolicyPreferDualStackRequireDualStack 改为 SingleStack。 当你将此 Service 从双栈更改为单栈时,Kubernetes 只保留 .spec.ClusterIPs 数组中的第一个元素,并设置 .spec.ClusterIP 为那个 IP 地址, 并设置 .spec.ipFamilies.spec.ClusterIPs 地址族。

无选择算符的无头服务

对于不带选择算符的无头服务, 若没有显式设置 .spec.ipFamilyPolicy,则 .spec.ipFamilyPolicy 字段默认设置为 RequireDualStack

LoadBalancer 类型 Service

要为你的 Service 提供双栈负载均衡器:

  • .spec.type 字段设置为 LoadBalancer
  • .spec.ipFamilyPolicy 字段设置为 PreferDualStack 或者 RequireDualStack

出站流量

如果你要启用出站流量,以便使用非公开路由 IPv6 地址的 Pod 到达集群外地址 (例如公网),则需要通过透明代理或 IP 伪装等机制使 Pod 使用公共路由的 IPv6 地址。 ip-masq-agent项目 支持在双栈集群上进行 IP 伪装。

Windows 支持

Windows 上的 Kubernetes 不支持单栈“仅 IPv6” 网络。 然而, 对于 Pod 和节点而言,仅支持单栈形式 Service 的双栈 IPv4/IPv6 网络是被支持的。

你可以使用 l2bridge 网络来实现 IPv4/IPv6 双栈联网。

关于 Windows 的不同网络模式,你可以进一步阅读 Windows 上的网络

接下来

3.5.9 - 拓扑感知路由

拓扑感知路由提供了一种机制帮助保持网络流量处于流量发起的区域内。 在集群中 Pod 之间优先使用相同区域的流量有助于提高可靠性、性能(网络延迟和吞吐量)或降低成本。
特性状态: Kubernetes v1.23 [beta]

拓扑感知路由(Toplogy Aware Routing) 调整路由行为,以优先保持流量在其发起区域内。 在某些情况下,这有助于降低成本或提高网络性能。

动机

Kubernetes 集群越来越多地部署在多区域环境中。 拓扑感知路由 提供了一种机制帮助流量保留在其发起所在的区域内。 计算 服务(Service) 的端点时, EndpointSlice 控制器考虑每个端点的物理拓扑(地区和区域),并填充提示字段以将其分配到区域。 诸如 kube-proxy 等集群组件可以使用这些提示,影响流量的路由方式(优先考虑物理拓扑上更近的端点)。

启用拓扑感知路由

你可以通过将 service.kubernetes.io/topology-mode 注解设置为 Auto 来启用 Service 的拓扑感知路由。 当每个区域中有足够的端点可用时,系统将为 EndpointSlices 填充拓扑提示,把每个端点分配给特定区域, 从而使流量被路由到更接近其来源的位置。

何时效果最佳

此特性在以下场景中的工作效果最佳:

1. 入站流量均匀分布

如果大部分流量源自同一个区域,则该流量可能会使分配到该区域的端点子集过载。 当预计入站流量源自同一区域时,不建议使用此特性。

2. 服务在每个区域具有至少 3 个端点

在一个三区域的集群中,这意味着有至少 9 个端点。如果每个区域的端点少于 3 个, 则 EndpointSlice 控制器很大概率(约 50%)无法平均分配端点,而是回退到默认的集群范围的路由方法。

工作原理

“自动”启发式算法会尝试按比例分配一定数量的端点到每个区域。 请注意,这种启发方式对具有大量端点的 Service 效果最佳。

EndpointSlice 控制器

当启用此启发方式时,EndpointSlice 控制器负责在各个 EndpointSlice 上设置提示信息。 控制器按比例给每个区域分配一定比例数量的端点。 这个比例基于在该区域中运行的节点的可分配 CPU 核心数。例如,如果一个区域有 2 个 CPU 核心,而另一个区域只有 1 个 CPU 核心, 那么控制器将给那个有 2 CPU 的区域分配两倍数量的端点。

以下示例展示了提供提示信息后 EndpointSlice 的样子:

apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
  name: example-hints
  labels:
    kubernetes.io/service-name: example-svc
addressType: IPv4
ports:
  - name: http
    protocol: TCP
    port: 80
endpoints:
  - addresses:
      - "10.1.2.3"
    conditions:
      ready: true
    hostname: pod-1
    zone: zone-a
    hints:
      forZones:
        - name: "zone-a"

kube-proxy

kube-proxy 组件依据 EndpointSlice 控制器设置的提示,过滤由它负责路由的端点。 在大多数场合,这意味着 kube-proxy 可以把流量路由到同一个区域的端点。 有时,控制器在另一不同的区域中分配端点,以确保在多个区域之间更平均地分配端点。 这会导致部分流量被路由到其他区域。

保护措施

Kubernetes 控制平面和每个节点上的 kube-proxy 在使用拓扑感知提示信息前,会应用一些保护措施规则。 如果规则无法顺利通过,kube-proxy 将无视区域限制,从集群中的任意位置选择端点。

  1. 端点数量不足: 如果一个集群中,端点数量少于区域数量,控制器不创建任何提示。
  1. 不可能实现均衡分配: 在一些场合中,不可能实现端点在区域中的平衡分配。 例如,假设 zone-a 比 zone-b 大两倍,但只有 2 个端点, 那分配到 zone-a 的端点可能收到比 zone-b 多两倍的流量。 如果控制器不能确保此“期望的过载”值低于每一个区域可接受的阈值,控制器将不添加提示信息。 重要的是,这不是基于实时反馈。所以对于特定的端点仍有可能超载。
  1. 一个或多个 Node 信息不足: 如果任一节点没有设置标签 topology.kubernetes.io/zone, 或没有上报可分配的 CPU 数据,控制平面将不会设置任何拓扑感知提示, 进而 kube-proxy 也就不能根据区域来过滤端点。
  1. 至少一个端点没有设置区域提示: 当这种情况发生时, kube-proxy 会假设从拓扑感知提示到拓扑感知路由(或反方向)的迁移仍在进行中, 在这种场合下过滤 Service 的端点是有风险的,所以 kube-proxy 回退到使用所有端点。
  1. 提示中不存在某区域: 如果 kube-proxy 无法找到提示中指向它当前所在的区域的端点, 它将回退到使用来自所有区域的端点。当你向现有集群新增新的区域时,这种情况发生概率很高。

限制

  • 当 Service 的 internalTrafficPolicy 值设置为 Local 时, 系统将不使用拓扑感知提示信息。你可以在同一集群中的不同 Service 上使用这两个特性, 但不能在同一个 Service 上这么做。
  • 这种方法不适用于大部分流量来自于一部分区域的 Service。 相反,这项技术的假设是入站流量与各区域中节点的服务能力成比例关系。
  • EndpointSlice 控制器在计算各区域的比例时,会忽略未就绪的节点。 在大部分节点未就绪的场景下,这样做会带来非预期的结果。
  • EndpointSlice 控制器忽略设置了 node-role.kubernetes.io/control-planenode-role.kubernetes.io/master 标签的节点。如果工作负载也在这些节点上运行,也可能会产生问题。
  • EndpointSlice 控制器在分派或计算各区域的比例时,并不会考虑 容忍度。 如果 Service 背后的各 Pod 被限制只能运行在集群节点的一个子集上,计算比例时不会考虑这点。
  • 这项技术和自动扩缩容机制之间可能存在冲突。例如,如果大量流量来源于同一个区域, 那只有分配到该区域的端点才可用来处理流量。这会导致 Pod 自动水平扩缩容 要么不能处理这种场景,要么会在别的区域添加 Pod。

自定义启发方式

Kubernetes 的部署方式有很多种,没有一种按区域分配端点的启发式方法能够适用于所有场景。 此特性的一个关键目标是:如果内置的启发方式不能满足你的使用场景,则可以开发自定义的启发方式。 启用自定义启发方式的第一步包含在了 1.27 版本中。 这是一个限制性较强的实现,可能尚未涵盖一些重要的、可进一步探索的场景。

接下来

3.5.10 - Windows 网络

Kubernetes 支持运行 Linux 或 Windows 节点。 你可以在统一集群内混布这两种节点。 本页提供了特定于 Windows 操作系统的网络概述。

Windows 容器网络

Windows 容器网络通过 CNI 插件暴露。 Windows 容器网络的工作方式与虚拟机类似。 每个容器都有一个连接到 Hyper-V 虚拟交换机(vSwitch)的虚拟网络适配器(vNIC)。 主机网络服务(Host Networking Service,HNS)和主机计算服务(Host Compute Service,HCS) 协同创建容器并将容器 vNIC 挂接到网络。 HCS 负责管理容器,而 HNS 负责管理以下网络资源:

  • 虚拟网络(包括创建 vSwitch)
  • Endpoint / vNIC
  • 命名空间
  • 包括数据包封装、负载均衡规则、ACL 和 NAT 规则在内的策略。

Windows HNS 和 vSwitch 实现命名空间划分,且可以按需为 Pod 或容器创建虚拟 NIC。 然而,诸如 DNS、路由和指标等许多配置将存放在 Windows 注册表数据库中, 而不是像 Linux 将这些配置作为文件存放在 /etc 内。 针对容器的 Windows 注册表与主机的注册表是分开的,因此将 /etc/resolv.conf 从主机映射到一个容器的类似概念与 Linux 上的效果不同。 这些必须使用容器环境中运行的 Windows API 进行配置。 因此,实现 CNI 时需要调用 HNS,而不是依赖文件映射将网络详情传递到 Pod 或容器中。

网络模式

Windows 支持五种不同的网络驱动/模式:L2bridge、L2tunnel、Overlay (Beta)、Transparent 和 NAT。 在 Windows 和 Linux 工作节点组成的异构集群中,你需要选择一个同时兼容 Windows 和 Linux 的网络方案。 下表列出了 Windows 支持的树外插件,并给出了何时使用每种 CNI 的建议:

网络驱动描述容器数据包修改网络插件网络插件特点
L2bridge容器挂接到一个外部 vSwitch。容器挂接到下层网络,但物理网络不需要了解容器的 MAC,因为这些 MAC 在入站/出站时被重写。MAC 被重写为主机 MAC,可使用 HNS OutboundNAT 策略将 IP 重写为主机 IP。win-bridgeAzure-CNI、Flannel host-gateway 使用 win-bridgewin-bridge 使用 L2bridge 网络模式,将容器连接到主机的下层,提供最佳性能。节点间连接需要用户定义的路由(UDR)。
L2Tunnel这是 L2bridge 的一种特例,但仅用在 Azure 上。所有数据包都会被发送到应用了 SDN 策略的虚拟化主机。MAC 被重写,IP 在下层网络上可见。Azure-CNIAzure-CNI 允许将容器集成到 Azure vNET,允许容器充分利用 Azure 虚拟网络所提供的能力集合。例如,安全地连接到 Azure 服务或使用 Azure NSG。参考 azure-cni 了解有关示例
Overlay容器被赋予一个 vNIC,连接到外部 vSwitch。每个上层网络都有自己的 IP 子网,由自定义 IP 前缀进行定义。该上层网络驱动使用 VXLAN 封装。用外部头进行封装。win-overlay、Flannel VXLAN(使用 win-overlay)当需要将虚拟容器网络与主机的下层隔离时(例如出于安全原因),应使用 win-overlay。如果你的数据中心的 IP 个数有限,可以将 IP 在不同的上层网络中重用(带有不同的 VNID 标记)。在 Windows Server 2019 上这个选项需要 KB4489899
Transparent(ovn-kubernetes 的特殊用例)需要一个外部 vSwitch。容器挂接到一个外部 vSwitch,由后者通过逻辑网络(逻辑交换机和路由器)实现 Pod 内通信。数据包通过 GENEVESTT 隧道进行封装,以到达其它主机上的 Pod。
数据包基于 OVN 网络控制器提供的隧道元数据信息被转发或丢弃。
南北向通信使用 NAT。
ovn-kubernetes通过 ansible 部署。通过 Kubernetes 策略可以实施分布式 ACL。支持 IPAM。无需 kube-proxy 即可实现负载均衡。无需 iptables/netsh 即可进行 NAT。
NAT(Kubernetes 中未使用容器被赋予一个 vNIC,连接到内部 vSwitch。DNS/DHCP 是使用一个名为 WinNAT 的内部组件实现的MAC 和 IP 重写为主机 MAC/IP。nat放在此处保持完整性。

如上所述,Windows 通过 VXLAN 网络后端Beta 支持;委派给 win-overlay) 和 host-gateway 网络后端(稳定支持;委派给 win-bridge) 也支持 FlannelCNI 插件

此插件支持委派给参考 CNI 插件(win-overlay、win-bridge)之一,配合使用 Windows 上的 Flannel 守护程序(Flanneld),以便自动分配节点子网租赁并创建 HNS 网络。 该插件读取自己的配置文件(cni.conf),并聚合 FlannelD 生成的 subnet.env 文件中的环境变量。 然后,委派给网络管道的参考 CNI 插件之一,并将包含节点分配子网的正确配置发送给 IPAM 插件(例如:host-local)。

对于 Node、Pod 和 Service 对象,TCP/UDP 流量支持以下网络流:

  • Pod → Pod(IP)
  • Pod → Pod(名称)
  • Pod → Service(集群 IP)
  • Pod → Service(PQDN,但前提是没有 ".")
  • Pod → Service(FQDN)
  • Pod → 外部(IP)
  • Pod → 外部(DNS)
  • Node → Pod
  • Pod → Node

IP 地址管理(IPAM)

Windows 支持以下 IPAM 选项:

负载均衡和 Service

Kubernetes Service 是一种抽象:定义了逻辑上的一组 Pod 和一种通过网络访问这些 Pod 的方式。 在包含 Windows 节点的集群中,你可以使用以下类别的 Service:

  • NodePort
  • ClusterIP
  • LoadBalancer
  • ExternalName

Windows 容器网络与 Linux 网络有着很重要的差异。 更多细节和背景信息,参考 Microsoft Windows 容器网络文档

在 Windows 上,你可以使用以下设置来配置 Service 和负载均衡行为:

Windows Service 设置
功能特性描述支持的 Windows 操作系统最低版本启用方式
会话亲和性确保每次都将来自特定客户端的连接传递到同一个 Pod。Windows Server 2022service.spec.sessionAffinity 设为 “ClientIP”
Direct Server Return (DSR)在负载均衡模式中 IP 地址修正和 LBNAT 直接发生在容器 vSwitch 端口;服务流量到达时源 IP 设置为原始 Pod IP。Windows Server 2019在 kube-proxy 中设置以下标志:--feature-gates="WinDSR=true" --enable-dsr=true
保留目标(Preserve-Destination)跳过服务流量的 DNAT,从而在到达后端 Pod 的数据包中保留目标服务的虚拟 IP。也会禁用节点间的转发。Windows Server,version 1903在服务注解中设置 "preserve-destination": "true" 并在 kube-proxy 中启用 DSR。
IPv4/IPv6 双栈网络进出集群和集群内通信都支持原生的 IPv4 间与 IPv6 间流量Windows Server 2019参考 IPv4/IPv6 双栈
客户端 IP 保留确保入站流量的源 IP 得到保留。也会禁用节点间转发。Windows Server 2019service.spec.externalTrafficPolicy 设置为 “Local” 并在 kube-proxy 中启用 DSR。

限制

Windows 节点不支持以下网络功能:

  • 主机网络模式
  • 从节点本身访问本地 NodePort(可以从其他节点或外部客户端进行访问)
  • 为同一 Service 提供 64 个以上后端 Pod(或不同目的地址)
  • 在连接到上层网络的 Windows Pod 之间使用 IPv6 通信
  • 非 DSR 模式中的本地流量策略(Local Traffic Policy)
  • 通过 win-overlaywin-bridge 使用 ICMP 协议,或使用 Azure-CNI 插件进行出站通信。
    具体而言,Windows 数据平面(VFP)不支持 ICMP 数据包转换,这意味着:
    • 指向同一网络内目的地址的 ICMP 数据包(例如 Pod 间的 ping 通信)可正常工作;
    • TCP/UDP 数据包可正常工作;
    • 通过远程网络指向其它地址的 ICMP 数据包(例如通过 ping 从 Pod 到外部公网的通信)无法被转换, 因此无法被路由回到这些数据包的源点;
    • 由于 TCP/UDP 数据包仍可被转换,所以在调试与外界的连接时, 你可以将 ping <destination> 替换为 curl <destination>

其他限制:

  • 由于缺少 CHECK 实现,Windows 参考网络插件 win-bridge 和 win-overlay 未实现 CNI 规约 的 v0.4.0 版本。
  • Flannel VXLAN CNI 插件在 Windows 上有以下限制:
    • 使用 Flannel v0.12.0(或更高版本)时,节点到 Pod 的连接仅适用于本地 Pod。
    • Flannel 仅限于使用 VNI 4096 和 UDP 端口 4789。 有关这些参数的更多详细信息,请参考官方的 Flannel VXLAN 后端文档。

3.5.11 - Service ClusterIP 分配

在 Kubernetes 中,Service 是一种抽象的方式, 用于公开在一组 Pod 上运行的应用。 Service 可以具有集群作用域的虚拟 IP 地址(使用 type: ClusterIP 的 Service)。 客户端可以使用该虚拟 IP 地址进行连接,Kubernetes 通过不同的后台 Pod 对该 Service 的流量进行负载均衡。

Service ClusterIP 是如何分配的?

当 Kubernetes 需要为 Service 分配虚拟 IP 地址时,该分配会通过以下两种方式之一进行:

动态分配
集群的控制面自动从所配置的 IP 范围内为 type: ClusterIP 选择一个空闲 IP 地址。
静态分配
根据为 Service 所配置的 IP 范围,选定并设置你的 IP 地址。

在整个集群中,每个 Service 的 ClusterIP 都必须是唯一的。 尝试使用已分配的 ClusterIP 创建 Service 将返回错误。

为什么需要预留 Service 的 ClusterIP ?

有时你可能希望 Services 在众所周知的 IP 上面运行,以便集群中的其他组件和用户可以使用它们。

最好的例子是集群的 DNS Service。作为一种非强制性的约定,一些 Kubernetes 安装程序 将 Service IP 范围中的第 10 个 IP 地址分配给 DNS 服务。假设将集群的 Service IP 范围配置为 10.96.0.0/16,并且希望 DNS Service IP 为 10.96.0.10,则必须创建如下 Service:

apiVersion: v1
kind: Service
metadata:
  labels:
    k8s-app: kube-dns
    kubernetes.io/cluster-service: "true"
    kubernetes.io/name: CoreDNS
  name: kube-dns
  namespace: kube-system
spec:
  clusterIP: 10.96.0.10
  ports:
  - name: dns
    port: 53
    protocol: UDP
    targetPort: 53
  - name: dns-tcp
    port: 53
    protocol: TCP
    targetPort: 53
  selector:
    k8s-app: kube-dns
  type: ClusterIP

但如前所述,IP 地址 10.96.0.10 尚未被保留。如果在 DNS 启动之前或同时采用动态分配机制创建其他 Service, 则它们有可能被分配此 IP,因此,你将无法创建 DNS Service,因为它会因冲突错误而失败。

如何避免 Service ClusterIP 冲突?

Kubernetes 中用來将 ClusterIP 分配给 Service 的分配策略降低了冲突的风险。

ClusterIP 范围根据公式 min(max(16, cidrSize / 16), 256) 进行划分, 描述为不小于 16 且不大于 256,并在二者之间有一个渐进的步长。

默认情况下,动态 IP 分配使用地址较高的一段,一旦用完,它将使用较低范围。 这将允许用户在冲突风险较低的较低地址段上使用静态分配。

示例

示例 1

此示例使用 IP 地址范围:10.96.0.0/24(CIDR 表示法)作为 Service 的 IP 地址。

范围大小:28 - 2 = 254
带宽偏移量:min(max(16, 256/16), 256) = min(16, 256) = 16
静态带宽起始地址:10.96.0.1
静态带宽结束地址:10.96.0.16
范围结束地址:10.96.0.254

pie showData title 10.96.0.0/24 "静态分配" : 16 "动态分配" : 238

示例 2

此示例使用 IP 地址范围 10.96.0.0/20(CIDR 表示法)作为 Service 的 IP 地址。

范围大小:212 - 2 = 4094
带宽偏移量:min(max(16, 4096/16), 256) = min(256, 256) = 256
静态带宽起始地址:10.96.0.1
静态带宽结束地址:10.96.1.0
范围结束地址:10.96.15.254

pie showData title 10.96.0.0/20 "静态分配" : 256 "动态分配" : 3838

示例 3

此示例使用 IP 地址范围 10.96.0.0/16(CIDR 表示法)作为 Service 的 IP 地址。

范围大小:216 - 2 = 65534
带宽偏移量:min(max(16, 65536/16), 256) = min(4096, 256) = 256
静态带宽起始地址:10.96.0.1
静态带宽结束地址:10.96.1.0
范围结束地址:10.96.255.254

pie showData title 10.96.0.0/16 "静态分配" : 256 "动态分配" : 65278

接下来

3.5.12 - 服务内部流量策略

如果集群中的两个 Pod 想要通信,并且两个 Pod 实际上都在同一节点运行, 服务内部流量策略 可以将网络流量限制在该节点内。 通过集群网络避免流量往返有助于提高可靠性、增强性能(网络延迟和吞吐量)或降低成本。
特性状态: Kubernetes v1.26 [stable]

服务内部流量策略开启了内部流量限制,将内部流量只路由到发起方所处节点内的服务端点。 这里的”内部“流量指当前集群中的 Pod 所发起的流量。 这种机制有助于节省开销,提升效率。

使用服务内部流量策略

你可以通过将 Service.spec.internalTrafficPolicy 项设置为 Local, 来为它指定一个内部专用的流量策略。 此设置就相当于告诉 kube-proxy 对于集群内部流量只能使用节点本地的服务端口。

以下示例展示了把 Service 的 .spec.internalTrafficPolicy 项设为 Local 时, Service 的样子:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
  internalTrafficPolicy: Local

工作原理

kube-proxy 基于 spec.internalTrafficPolicy 的设置来过滤路由的目标服务端点。 当它的值设为 Local 时,只会选择节点本地的服务端点。 当它的值设为 Cluster 或缺省时,Kubernetes 会选择所有的服务端点。

接下来

3.6 - 存储

为集群中的 Pods 提供长期和临时存储的方法。

3.6.1 - 卷

容器中的文件在磁盘上是临时存放的,这给在容器中运行较重要的应用带来一些问题。 当容器崩溃或停止时会出现一个问题。此时容器状态未保存, 因此在容器生命周期内创建或修改的所有文件都将丢失。 在崩溃期间,kubelet 会以干净的状态重新启动容器。 当多个容器在一个 Pod 中运行并且需要共享文件时,会出现另一个问题。 跨所有容器设置和访问共享文件系统具有一定的挑战性。

Kubernetes 卷(Volume) 这一抽象概念能够解决这两个问题。

阅读本文前建议你熟悉一下 Pod

背景

Kubernetes 支持很多类型的卷。 Pod 可以同时使用任意数目的卷类型。 临时卷类型的生命周期与 Pod 相同, 但持久卷可以比 Pod 的存活期长。 当 Pod 不再存在时,Kubernetes 也会销毁临时卷;不过 Kubernetes 不会销毁持久卷。 对于给定 Pod 中任何类型的卷,在容器重启期间数据都不会丢失。

卷的核心是一个目录,其中可能存有数据,Pod 中的容器可以访问该目录中的数据。 所采用的特定的卷类型将决定该目录如何形成的、使用何种介质保存数据以及目录中存放的内容。

使用卷时, 在 .spec.volumes 字段中设置为 Pod 提供的卷,并在 .spec.containers[*].volumeMounts 字段中声明卷在容器中的挂载位置。 容器中的进程看到的文件系统视图是由它们的容器镜像 的初始内容以及挂载在容器中的卷(如果定义了的话)所组成的。 其中根文件系统同容器镜像的内容相吻合。 任何在该文件系统下的写入操作,如果被允许的话,都会影响接下来容器中进程访问文件系统时所看到的内容。

卷挂载在镜像中的指定路径下。 Pod 配置中的每个容器必须独立指定各个卷的挂载位置。

卷不能挂载到其他卷之上(不过存在一种使用 subPath 的相关机制),也不能与其他卷有硬链接。

卷类型

Kubernetes 支持下列类型的卷:

awsElasticBlockStore (已移除)

Kubernetes 1.30 不包含 awsElasticBlockStore 卷类型。

AWSElasticBlockStore 树内存储驱动已在 Kubernetes v1.19 版本中废弃, 并在 v1.27 版本中被完全移除。

Kubernetes 项目建议你转为使用 AWS EBS 第三方存储驱动插件。

azureDisk (已移除)

Kubernetes 1.30 不包含 azureDisk 卷类型。

AzureDisk 树内存储驱动已在 Kubernetes v1.19 版本中废弃,并在 v1.27 版本中被完全移除。

Kubernetes 项目建议你转为使用 Azure Disk 第三方存储驱动插件。

azureFile (已弃用)

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

azureFile 卷类型用来在 Pod 上挂载 Microsoft Azure 文件卷(File Volume)(SMB 2.1 和 3.0)。

更多详情请参考 azureFile 卷插件

azureFile CSI 迁移

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

启用 azureFileCSIMigration 特性后,所有插件操作将从现有的树内插件重定向到 file.csi.azure.com 容器存储接口(CSI)驱动程序。要使用此特性,必须在集群中安装 Azure 文件 CSI 驱动程序, 并且 CSIMigrationAzureFile 特性门控 必须被启用。

Azure 文件 CSI 驱动尚不支持为同一卷设置不同的 fsgroup。 如果 CSIMigrationAzureFile 特性被启用,用不同的 fsgroup 来使用同一卷也是不被支持的。

azureFile CSI 迁移完成

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

要禁止控制器管理器和 kubelet 加载 azureFile 存储插件, 请将 InTreePluginAzureFileUnregister 标志设置为 true

cephfs

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

cephfs 卷允许你将现存的 CephFS 卷挂载到 Pod 中。 不像 emptyDir 那样会在 Pod 被删除的同时也会被删除,cephfs 卷的内容在 Pod 被删除时会被保留,只是卷被卸载了。 这意味着 cephfs 卷可以被预先填充数据,且这些数据可以在 Pod 之间共享。同一 cephfs 卷可同时被多个写者挂载。

更多信息请参考 CephFS 示例

cinder(已移除)

Kubernetes 1.30 不包含 cinder 卷类型。

OpenStack Cinder 树内存储驱动已在 Kubernetes v1.11 版本中废弃, 并在 v1.26 版本中被完全移除。

Kubernetes 项目建议你转为使用 OpenStack Cinder 第三方存储驱动插件。

configMap

configMap 卷提供了向 Pod 注入配置数据的方法。 ConfigMap 对象中存储的数据可以被 configMap 类型的卷引用,然后被 Pod 中运行的容器化应用使用。

引用 configMap 对象时,你可以在卷中通过它的名称来引用。 你可以自定义 ConfigMap 中特定条目所要使用的路径。 下面的配置显示了如何将名为 log-config 的 ConfigMap 挂载到名为 configmap-pod 的 Pod 中:

apiVersion: v1
kind: Pod
metadata:
  name: configmap-pod
spec:
  containers:
    - name: test
      image: busybox:1.28
      command: ['sh', '-c', 'echo "The app is running!" && tail -f /dev/null']
      volumeMounts:
        - name: config-vol
          mountPath: /etc/config
  volumes:
    - name: config-vol
      configMap:
        name: log-config
        items:
          - key: log_level
            path: log_level

log-config ConfigMap 以卷的形式挂载,并且存储在 log_level 条目中的所有内容都被挂载到 Pod 的 /etc/config/log_level 路径下。 请注意,这个路径来源于卷的 mountPathlog_level 键对应的 path

downwardAPI

downwardAPI 卷用于为应用提供 downward API 数据。 在这类卷中,所公开的数据以纯文本格式的只读文件形式存在。

更多详细信息请参考通过文件将 Pod 信息呈现给容器

emptyDir

对于定义了 emptyDir 卷的 Pod,在 Pod 被指派到某节点时此卷会被创建。 就像其名称所表示的那样,emptyDir 卷最初是空的。尽管 Pod 中的容器挂载 emptyDir 卷的路径可能相同也可能不同,但这些容器都可以读写 emptyDir 卷中相同的文件。 当 Pod 因为某些原因被从节点上删除时,emptyDir 卷中的数据也会被永久删除。

emptyDir 的一些用途:

  • 缓存空间,例如基于磁盘的归并排序。
  • 为耗时较长的计算任务提供检查点,以便任务能方便地从崩溃前状态恢复执行。
  • 在 Web 服务器容器服务数据时,保存内容管理器容器获取的文件。

emptyDir.medium 字段用来控制 emptyDir 卷的存储位置。 默认情况下,emptyDir 卷存储在该节点所使用的介质上; 此处的介质可以是磁盘、SSD 或网络存储,这取决于你的环境。 你可以将 emptyDir.medium 字段设置为 "Memory", 以告诉 Kubernetes 为你挂载 tmpfs(基于 RAM 的文件系统)。 虽然 tmpfs 速度非常快,但是要注意它与磁盘不同, 并且你所写入的所有文件都会计入容器的内存消耗,受容器内存限制约束。

你可以通过为默认介质指定大小限制,来限制 emptyDir 卷的存储容量。 此存储是从节点临时存储中分配的。 如果来自其他来源(如日志文件或镜像分层数据)的数据占满了存储,emptyDir 可能会在达到此限制之前发生存储容量不足的问题。

emptyDir 配置示例

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: registry.k8s.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
  - name: cache-volume
    emptyDir:
      sizeLimit: 500Mi

fc (光纤通道)

fc 卷类型允许将现有的光纤通道块存储卷挂载到 Pod 中。 可以使用卷配置中的参数 targetWWNs 来指定单个或多个目标 WWN(World Wide Names)。 如果指定了多个 WWN,targetWWNs 期望这些 WWN 来自多路径连接。

更多详情请参考 FC 示例

gcePersistentDisk(已移除)

Kubernetes 1.30 不包含 gcePersistentDisk 卷类型。

gcePersistentDisk 源代码树内卷存储驱动在 Kubernetes v1.17 版本中被弃用,在 v1.28 版本中被完全移除。

Kubernetes 项目建议你转为使用 Google Compute Engine Persistent Disk CSI 第三方存储驱动插件。

GCE CSI 迁移

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

启用 GCE PD 的 CSIMigration 特性后,所有插件操作将从现有的树内插件重定向到 pd.csi.storage.gke.io 容器存储接口(CSI)驱动程序。 为了使用此特性,必须在集群中上安装 GCE PD CSI 驱动程序

gitRepo (已弃用)

gitRepo 卷是一个卷插件的例子。 该查卷挂载一个空目录,并将一个 Git 代码仓库克隆到这个目录中供 Pod 使用。

下面给出一个 gitRepo 卷的示例:

apiVersion: v1
kind: Pod
metadata:
  name: server
spec:
  containers:
  - image: nginx
    name: nginx
    volumeMounts:
    - mountPath: /mypath
      name: git-volume
  volumes:
  - name: git-volume
    gitRepo:
      repository: "git@somewhere:me/my-git-repository.git"
      revision: "22f1d8406d464b0c0874075539c1f2e96c253775"

glusterfs(已移除)

Kubernetes 1.30 不包含 glusterfs 卷类型。

GlusterFS 树内存储驱动程序在 Kubernetes v1.25 版本中被弃用,然后在 v1.26 版本中被完全移除。

hostPath

hostPath 卷能将主机节点文件系统上的文件或目录挂载到你的 Pod 中。 虽然这不是大多数 Pod 需要的,但是它为一些应用程序提供了强大的逃生舱。

例如,hostPath 的一些用法有:

  • 运行一个需要访问 Docker 内部机制的容器;可使用 hostPath 挂载 /var/lib/docker 路径。
  • 在容器中运行 cAdvisor 时,以 hostPath 方式挂载 /sys
  • 允许 Pod 指定给定的 hostPath 在运行 Pod 之前是否应该存在,是否应该创建以及应该以什么方式存在。

除了必需的 path 属性之外,你可以选择性地为 hostPath 卷指定 type

支持的 type 值如下:

取值行为
空字符串(默认)用于向后兼容,这意味着在安装 hostPath 卷之前不会执行任何检查。
DirectoryOrCreate如果在给定路径上什么都不存在,那么将根据需要创建空目录,权限设置为 0755,具有与 kubelet 相同的组和属主信息。
Directory在给定路径上必须存在的目录。
FileOrCreate如果在给定路径上什么都不存在,那么将在那里根据需要创建空文件,权限设置为 0644,具有与 kubelet 相同的组和所有权。
File在给定路径上必须存在的文件。
Socket在给定路径上必须存在的 UNIX 套接字。
CharDevice在给定路径上必须存在的字符设备。
BlockDevice在给定路径上必须存在的块设备。

当使用这种类型的卷时要小心,因为:

  • HostPath 卷可能会暴露特权系统凭据(例如 Kubelet)或特权 API(例如容器运行时套接字),可用于容器逃逸或攻击集群的其他部分。
  • 具有相同配置(例如基于同一 PodTemplate 创建)的多个 Pod 会由于节点上文件的不同而在不同节点上有不同的行为。
  • 下层主机上创建的文件或目录只能由 root 用户写入。 你需要在特权容器中以 root 身份运行进程,或者修改主机上的文件权限以便容器能够写入 hostPath 卷。

hostPath 配置示例

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: registry.k8s.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-pd
      name: test-volume
  volumes:
  - name: test-volume
    hostPath:
      # 宿主机上目录位置
      path: /data
      # 此字段为可选
      type: Directory

hostPath FileOrCreate 配置示例

apiVersion: v1
kind: Pod
metadata:
  name: test-webserver
spec:
  containers:
  - name: test-webserver
    image: registry.k8s.io/test-webserver:latest
    volumeMounts:
    - mountPath: /var/local/aaa
      name: mydir
    - mountPath: /var/local/aaa/1.txt
      name: myfile
  volumes:
  - name: mydir
    hostPath:
      # 确保文件所在目录成功创建。
      path: /var/local/aaa
      type: DirectoryOrCreate
  - name: myfile
    hostPath:
      path: /var/local/aaa/1.txt
      type: FileOrCreate

iscsi

iscsi 卷能将 iSCSI (基于 IP 的 SCSI) 卷挂载到你的 Pod 中。 不像 emptyDir 那样会在删除 Pod 的同时也会被删除,iscsi 卷的内容在删除 Pod 时会被保留,卷只是被卸载。 这意味着 iscsi 卷可以被预先填充数据,并且这些数据可以在 Pod 之间共享。

iSCSI 的一个特点是它可以同时被多个用户以只读方式挂载。 这意味着你可以用数据集预先填充卷,然后根据需要在尽可能多的 Pod 上使用它。 不幸的是,iSCSI 卷只能由单个使用者以读写模式挂载。不允许同时写入。

更多详情请参考 iSCSI 示例

local

local 卷所代表的是某个被挂载的本地存储设备,例如磁盘、分区或者目录。

local 卷只能用作静态创建的持久卷。不支持动态配置。

hostPath 卷相比,local 卷能够以持久和可移植的方式使用,而无需手动将 Pod 调度到节点。系统通过查看 PersistentVolume 的节点亲和性配置,就能了解卷的节点约束。

然而,local 卷仍然取决于底层节点的可用性,并不适合所有应用程序。 如果节点变得不健康,那么 local 卷也将变得不可被 Pod 访问。使用它的 Pod 将不能运行。 使用 local 卷的应用程序必须能够容忍这种可用性的降低,以及因底层磁盘的耐用性特征而带来的潜在的数据丢失风险。

下面是一个使用 local 卷和 nodeAffinity 的持久卷示例:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: example-pv
spec:
  capacity:
    storage: 100Gi
  volumeMode: Filesystem
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Delete
  storageClassName: local-storage
  local:
    path: /mnt/disks/ssd1
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - example-node

使用 local 卷时,你需要设置 PersistentVolume 对象的 nodeAffinity 字段。 Kubernetes 调度器使用 PersistentVolume 的 nodeAffinity 信息来将使用 local 卷的 Pod 调度到正确的节点。

PersistentVolume 对象的 volumeMode 字段可被设置为 "Block" (而不是默认值 "Filesystem"),以将 local 卷作为原始块设备暴露出来。

使用 local 卷时,建议创建一个 StorageClass 并将其 volumeBindingMode 设置为 WaitForFirstConsumer。要了解更多详细信息,请参考 local StorageClass 示例。 延迟卷绑定的操作可以确保 Kubernetes 在为 PersistentVolumeClaim 作出绑定决策时,会评估 Pod 可能具有的其他节点约束,例如:如节点资源需求、节点选择器、Pod 亲和性和 Pod 反亲和性。

你可以在 Kubernetes 之外单独运行静态驱动以改进对 local 卷的生命周期管理。 请注意,此驱动尚不支持动态配置。 有关如何运行外部 local 卷驱动,请参考 local 卷驱动用户指南

nfs

nfs 卷能将 NFS (网络文件系统) 挂载到你的 Pod 中。 不像 emptyDir 那样会在删除 Pod 的同时也会被删除,nfs 卷的内容在删除 Pod 时会被保存,卷只是被卸载。 这意味着 nfs 卷可以被预先填充数据,并且这些数据可以在 Pod 之间共享。

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: registry.k8s.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /my-nfs-data
      name: test-volume
  volumes:
  - name: test-volume
    nfs:
      server: my-nfs-server.example.com
      path: /my-nfs-volume
      readOnly: true

如需了解用持久卷挂载 NFS 卷的示例,请参考 NFS 示例

persistentVolumeClaim

persistentVolumeClaim 卷用来将持久卷(PersistentVolume)挂载到 Pod 中。 持久卷申领(PersistentVolumeClaim)是用户在不知道特定云环境细节的情况下“申领”持久存储(例如 iSCSI 卷)的一种方法。

更多详情请参考持久卷

portworxVolume(已弃用)

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

portworxVolume 是一个可伸缩的块存储层,能够以超融合(hyperconverged)的方式与 Kubernetes 一起运行。 Portworx 支持对服务器上存储的指纹处理、基于存储能力进行分层以及跨多个服务器整合存储容量。 Portworx 可以以 in-guest 方式在虚拟机中运行,也可以在裸金属 Linux 节点上运行。

portworxVolume 类型的卷可以通过 Kubernetes 动态创建,也可以预先配备并在 Pod 内引用。 下面是一个引用预先配备的 Portworx 卷的示例 Pod:

apiVersion: v1
kind: Pod
metadata:
  name: test-portworx-volume-pod
spec:
  containers:
  - image: registry.k8s.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /mnt
      name: pxvol
  volumes:
  - name: pxvol
    # 此 Portworx 卷必须已经存在
    portworxVolume:
      volumeID: "pxvol"
      fsType: "<fs-type>"

更多详情可以参考 Portworx 卷

Portworx CSI 迁移

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

已针对 Portworx 添加 CSIMigration 特性,但在 Kubernetes 1.23 中默认禁用,因为它处于 Alpha 状态。 自 v1.25 以来它已进入 Beta 阶段,但默认仍关闭。 它将所有插件操作不再指向树内插件(In-Tree Plugin),转而指向 pxd.portworx.com 容器存储接口(Container Storage Interface,CSI)驱动。 Portworx CSI 驱动程序必须安装在集群上。 要启用此特性,需在 kube-controller-manager 和 kubelet 中设置 CSIMigrationPortworx=true

投射(projected)

投射卷能将若干现有的卷来源映射到同一目录上。更多详情请参考投射卷

rbd

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

rbd 卷允许将 Rados 块设备卷挂载到你的 Pod 中。 不像 emptyDir 那样会在删除 Pod 的同时也会被删除,rbd 卷的内容在删除 Pod 时会被保存,卷只是被卸载。 这意味着 rbd 卷可以被预先填充数据,并且这些数据可以在 Pod 之间共享。

RBD 的一个特性是它可以同时被多个用户以只读方式挂载。 这意味着你可以用数据集预先填充卷,然后根据需要在尽可能多的 Pod 中并行地使用卷。 不幸的是,RBD 卷只能由单个使用者以读写模式安装。不允许同时写入。

更多详情请参考 RBD 示例

RBD CSI 迁移

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

启用 RBD 的 CSIMigration 特性后,所有插件操作从现有的树内插件重定向到 rbd.csi.ceph.com CSI 驱动程序。 要使用该特性,必须在集群内安装 Ceph CSI 驱动,并启用 csiMigrationRBD 特性门控。 (请注意,csiMigrationRBD 标志已在 v1.24 版本中移除且替换为 CSIMigrationRBD。)

secret

secret 卷用来给 Pod 传递敏感信息,例如密码。你可以将 Secret 存储在 Kubernetes API 服务器上,然后以文件的形式挂载到 Pod 中,无需直接与 Kubernetes 耦合。 secret 卷由 tmpfs(基于 RAM 的文件系统)提供存储,因此它们永远不会被写入非易失性(持久化的)存储器。

更多详情请参考配置 Secrets

vsphereVolume(已弃用)

vsphereVolume 用来将 vSphere VMDK 卷挂载到你的 Pod 中。 在卸载卷时,卷的内容会被保留。 vSphereVolume 卷类型支持 VMFS 和 VSAN 数据仓库。

进一步信息可参考 vSphere 卷

vSphere CSI 迁移

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

在 Kubernetes 1.30 中,对树内 vsphereVolume 类的所有操作都会被重定向至 csi.vsphere.vmware.com CSI 驱动程序。

vSphere CSI 驱动必须安装到集群上。 你可以在 VMware 的文档页面迁移树内 vSphere 卷插件到 vSphere 容器存储插件 中找到有关如何迁移树内 vsphereVolume 的其他建议。 如果未安装 vSphere CSI 驱动程序,则无法对由树内 vsphereVolume 类型创建的 PV 执行卷操作。

你必须运行 vSphere 7.0u2 或更高版本才能迁移到 vSphere CSI 驱动程序。

如果你正在运行 Kubernetes v1.30,请查阅该 Kubernetes 版本的文档。

vSphere CSI 迁移完成

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

为了避免控制器管理器和 kubelet 加载 vsphereVolume 插件,你需要将 InTreePluginvSphereUnregister 特性设置为 true。你还必须在所有工作节点上安装 csi.vsphere.vmware.com CSI 驱动。

使用 subPath

有时,在单个 Pod 中共享卷以供多方使用是很有用的。 volumeMounts.subPath 属性可用于指定所引用的卷内的子路径,而不是其根路径。

下面例子展示了如何配置某包含 LAMP 堆栈(Linux Apache MySQL PHP)的 Pod 使用同一共享卷。 此示例中的 subPath 配置不建议在生产环境中使用。 PHP 应用的代码和相关数据映射到卷的 html 文件夹,MySQL 数据库存储在卷的 mysql 文件夹中:

apiVersion: v1
kind: Pod
metadata:
  name: my-lamp-site
spec:
    containers:
    - name: mysql
      image: mysql
      env:
      - name: MYSQL_ROOT_PASSWORD
        value: "rootpasswd"
      volumeMounts:
      - mountPath: /var/lib/mysql
        name: site-data
        subPath: mysql
    - name: php
      image: php:7.0-apache
      volumeMounts:
      - mountPath: /var/www/html
        name: site-data
        subPath: html
    volumes:
    - name: site-data
      persistentVolumeClaim:
        claimName: my-lamp-site-data

使用带有扩展环境变量的 subPath

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

使用 subPathExpr 字段可以基于 downward API 环境变量来构造 subPath 目录名。 subPathsubPathExpr 属性是互斥的。

在这个示例中,Pod 使用 subPathExprhostPath/var/log/pods 中创建目录 pod1hostPath 卷采用来自 downwardAPI 的 Pod 名称生成目录名。 宿主机目录 /var/log/pods/pod1 被挂载到容器的 /logs 中。

apiVersion: v1
kind: Pod
metadata:
  name: pod1
spec:
  containers:
  - name: container1
    env:
    - name: POD_NAME
      valueFrom:
        fieldRef:
          apiVersion: v1
          fieldPath: metadata.name
    image: busybox:1.28
    command: [ "sh", "-c", "while [ true ]; do echo 'Hello'; sleep 10; done | tee -a /logs/hello.txt" ]
    volumeMounts:
    - name: workdir1
      mountPath: /logs
      # 包裹变量名的是小括号,而不是大括号
      subPathExpr: $(POD_NAME)
  restartPolicy: Never
  volumes:
  - name: workdir1
    hostPath:
      path: /var/log/pods

资源

emptyDir 卷的存储介质(例如磁盘、SSD 等)是由保存 kubelet 数据的根目录(通常是 /var/lib/kubelet)的文件系统的介质确定。 Kubernetes 对 emptyDir 卷或者 hostPath 卷可以消耗的空间没有限制,容器之间或 Pod 之间也没有隔离。

要了解如何使用资源规约来请求空间, 可参考如何管理资源

树外(Out-of-Tree)卷插件

Out-of-Tree 卷插件包括容器存储接口(CSI)和 FlexVolume(已弃用)。它们使存储供应商能够创建自定义存储插件,而无需将插件源码添加到 Kubernetes 代码仓库。

以前,所有卷插件(如上面列出的卷类型)都是“树内(In-Tree)”的。 “树内”插件是与 Kubernetes 的核心组件一同构建、链接、编译和交付的。 这意味着向 Kubernetes 添加新的存储系统(卷插件)需要将代码合并到 Kubernetes 核心代码库中。

CSI 和 FlexVolume 都允许独立于 Kubernetes 代码库开发卷插件,并作为扩展部署(安装)在 Kubernetes 集群上。

对于希望创建树外(Out-Of-Tree)卷插件的存储供应商, 请参考卷插件常见问题

CSI

容器存储接口 (CSI) 为容器编排系统(如 Kubernetes)定义标准接口,以将任意存储系统暴露给它们的容器工作负载。

更多详情请阅读 CSI 设计方案

一旦在 Kubernetes 集群上部署了 CSI 兼容卷驱动程序,用户就可以使用 csi 卷类型来挂接、挂载 CSI 驱动所提供的卷。

csi 卷可以在 Pod 中以三种方式使用:

存储管理员可以使用以下字段来配置 CSI 持久卷:

  • driver:指定要使用的卷驱动名称的字符串值。 这个值必须与 CSI 驱动程序在 GetPluginInfoResponse 中返回的值相对应;该接口定义在 CSI 规范中。 Kubernetes 使用所给的值来标识要调用的 CSI 驱动程序;CSI 驱动程序也使用该值来辨识哪些 PV 对象属于该 CSI 驱动程序。
  • volumeHandle:唯一标识卷的字符串值。 该值必须与 CSI 驱动在 CreateVolumeResponsevolume_id 字段中返回的值相对应;接口定义在 CSI 规范 中。 在所有对 CSI 卷驱动程序的调用中,引用该 CSI 卷时都使用此值作为 volume_id 参数。
  • readOnly:一个可选的布尔值,指示通过 ControllerPublished 关联该卷时是否设置该卷为只读。默认值是 false。 该值通过 ControllerPublishVolumeRequest 中的 readonly 字段传递给 CSI 驱动。
  • fsType:如果 PV 的 VolumeModeFilesystem,那么此字段指定挂载卷时应该使用的文件系统。 如果卷尚未格式化,并且支持格式化,此值将用于格式化卷。 此值可以通过 ControllerPublishVolumeRequestNodeStageVolumeRequestNodePublishVolumeRequestVolumeCapability 字段传递给 CSI 驱动。
  • volumeAttributes:一个字符串到字符串的映射表,用来设置卷的静态属性。 该映射必须与 CSI 驱动程序返回的 CreateVolumeResponse 中的 volume.attributes 字段的映射相对应; CSI 规范中有相应的定义。 该映射通过ControllerPublishVolumeRequestNodeStageVolumeRequestNodePublishVolumeRequest 中的 volume_context 字段传递给 CSI 驱动。
  • controllerPublishSecretRef:对包含敏感信息的 Secret 对象的引用; 该敏感信息会被传递给 CSI 驱动来完成 CSI ControllerPublishVolumeControllerUnpublishVolume 调用。 此字段是可选的;在不需要 Secret 时可以是空的。 如果 Secret 包含多个 Secret 条目,则所有的 Secret 条目都会被传递。
  • nodeExpandSecretRef:对包含敏感信息的 Secret 对象的引用, 该信息会传递给 CSI 驱动以完成 CSI NodeExpandVolume 调用。 此字段是可选的,如果不需要 Secret,则可能是空的。 如果 Secret 包含多个 Secret 条目,则传递所有 Secret 条目。 当你为节点初始化的卷扩展配置 Secret 数据时,kubelet 会通过 NodeExpandVolume() 调用将该数据传递给 CSI 驱动。为了使用 nodeExpandSecretRef 字段, 你的集群应运行 Kubernetes 1.25 或更高版本, 如果你所运行的 Kubernetes 是 1.25 或 1.26,你必须为每个 kube-apiserver 和每个节点上的 kubelet 启用名为 CSINodeExpandSecret特性门控。 在 Kubernetes 1.27 版本中,此特性已默认启用,无需显式启用特性门控。 在节点初始化的存储大小调整操作期间,你还必须使用支持或需要 Secret 数据的 CSI 驱动。
  • nodePublishSecretRef:对包含敏感信息的 Secret 对象的引用。 该信息传递给 CSI 驱动来完成 CSI NodePublishVolume 调用。 此字段是可选的,如果不需要 Secret,则可能是空的。 如果 Secret 对象包含多个 Secret 条目,则传递所有 Secret 条目。
  • nodeStageSecretRef:对包含敏感信息的 Secret 对象的引用, 该信息会传递给 CSI 驱动以完成 CSI NodeStageVolume 调用。 此字段是可选的,如果不需要 Secret,则可能是空的。 如果 Secret 包含多个 Secret 条目,则传递所有 Secret 条目。

CSI 原始块卷支持

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

具有外部 CSI 驱动程序的供应商能够在 Kubernetes 工作负载中实现原始块卷支持。

你可以和以前一样, 安装自己的带有原始块卷支持的 PV/PVC, 采用 CSI 对此过程没有影响。

CSI 临时卷

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

你可以直接在 Pod 规约中配置 CSI 卷。采用这种方式配置的卷都是临时卷, 无法在 Pod 重新启动后继续存在。 进一步的信息可参阅临时卷

有关如何开发 CSI 驱动的更多信息,请参考 kubernetes-csi 文档

Windows CSI 代理

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

CSI 节点插件需要执行多种特权操作,例如扫描磁盘设备和挂载文件系统等。 这些操作在每个宿主机操作系统上都是不同的。对于 Linux 工作节点而言,容器化的 CSI 节点插件通常部署为特权容器。对于 Windows 工作节点而言,容器化 CSI 节点插件的特权操作是通过 csi-proxy 来支持的。csi-proxy 是一个由社区管理的、独立的可执行二进制文件, 需要被预安装到每个 Windows 节点上。

要了解更多的细节,可以参考你要部署的 CSI 插件的部署指南。

从树内插件迁移到 CSI 驱动程序

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

CSIMigration 特性针对现有树内插件的操作会被定向到相应的 CSI 插件(应已安装和配置)。 因此,操作员在过渡到取代树内插件的 CSI 驱动时,无需对现有存储类、PV 或 PVC(指树内插件)进行任何配置更改。

所支持的操作和特性包括:配备(Provisioning)/删除、挂接(Attach)/解挂(Detach)、 挂载(Mount)/卸载(Unmount)和调整卷大小。

上面的卷类型节列出了支持 CSIMigration 并已实现相应 CSI 驱动程序的树内插件。

下面是支持 Windows 节点上持久性存储的树内插件:

flexVolume(已弃用)

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

FlexVolume 是一个使用基于 exec 的模型来与驱动程序对接的树外插件接口。 用户必须在每个节点上的预定义卷插件路径中安装 FlexVolume 驱动程序可执行文件,在某些情况下,控制平面节点中也要安装。

Pod 通过 flexvolume 树内插件与 FlexVolume 驱动程序交互。 更多详情请参考 FlexVolume README 文档。

下面的 FlexVolume 插件 以 PowerShell 脚本的形式部署在宿主机系统上,支持 Windows 节点:

挂载卷的传播

挂载卷的传播能力允许将容器安装的卷共享到同一 Pod 中的其他容器,甚至共享到同一节点上的其他 Pod。

卷的挂载传播特性由 Container.volumeMounts 中的 mountPropagation 字段控制。 它的值包括:

  • None - 此卷挂载将不会感知到主机后续在此卷或其任何子目录上执行的挂载变化。 类似的,容器所创建的卷挂载在主机上是不可见的。这是默认模式。

    该模式等同于 mount(8) 中描述的 rprivate 挂载传播选项。

    然而,当 rprivate 传播选项不适用时,CRI 运行时可以转为选择 rslave 挂载传播选项 (即 HostToContainer)。当挂载源包含 Docker 守护进程的根目录(/var/lib/docker)时, cri-dockerd (Docker) 已知可以选择 rslave 挂载传播选项。

  • HostToContainer - 此卷挂载将会感知到主机后续针对此卷或其任何子目录的挂载操作。

    换句话说,如果主机在此挂载卷中挂载任何内容,容器将能看到它被挂载在那里。

    类似的,配置了 Bidirectional 挂载传播选项的 Pod 如果在同一卷上挂载了内容,挂载传播设置为 HostToContainer 的容器都将能看到这一变化。

    该模式等同于 mount(8)中描述的 rslave 挂载传播选项。

  • Bidirectional - 这种卷挂载和 HostToContainer 挂载表现相同。 另外,容器创建的卷挂载将被传播回至主机和使用同一卷的所有 Pod 的所有容器。

    该模式等同于 mount(8) 中描述的 rshared 挂载传播选项。

接下来

参考使用持久卷部署 WordPress 和 MySQL 示例。

3.6.2 - 持久卷

本文描述 Kubernetes 中的持久卷(Persistent Volumes)。 建议先熟悉卷(volume)存储类(StorageClass)卷属性类(VolumeAttributesClass)

介绍

存储的管理是一个与计算实例的管理完全不同的问题。 PersistentVolume 子系统为用户和管理员提供了一组 API, 将存储如何制备的细节从其如何被使用中抽象出来。 为了实现这点,我们引入了两个新的 API 资源:PersistentVolume 和 PersistentVolumeClaim。

持久卷(PersistentVolume,PV) 是集群中的一块存储,可以由管理员事先制备, 或者使用存储类(Storage Class)来动态制备。 持久卷是集群资源,就像节点也是集群资源一样。PV 持久卷和普通的 Volume 一样, 也是使用卷插件来实现的,只是它们拥有独立于任何使用 PV 的 Pod 的生命周期。 此 API 对象中记述了存储的实现细节,无论其背后是 NFS、iSCSI 还是特定于云平台的存储系统。

持久卷申领(PersistentVolumeClaim,PVC) 表达的是用户对存储的请求。概念上与 Pod 类似。 Pod 会耗用节点资源,而 PVC 申领会耗用 PV 资源。Pod 可以请求特定数量的资源(CPU 和内存)。同样 PVC 申领也可以请求特定的大小和访问模式 (例如,可以挂载为 ReadWriteOnce、ReadOnlyMany、ReadWriteMany 或 ReadWriteOncePod, 请参阅访问模式)。

尽管 PersistentVolumeClaim 允许用户消耗抽象的存储资源, 常见的情况是针对不同的问题用户需要的是具有不同属性(如,性能)的 PersistentVolume 卷。 集群管理员需要能够提供不同性质的 PersistentVolume, 并且这些 PV 卷之间的差别不仅限于卷大小和访问模式,同时又不能将卷是如何实现的这些细节暴露给用户。 为了满足这类需求,就有了存储类(StorageClass) 资源。

参见基于运行示例的详细演练

卷和申领的生命周期

PV 卷是集群中的资源。PVC 申领是对这些资源的请求,也被用来执行对资源的申领检查。 PV 卷和 PVC 申领之间的互动遵循如下生命周期:

制备

PV 卷的制备有两种方式:静态制备或动态制备。

静态制备

集群管理员创建若干 PV 卷。这些卷对象带有真实存储的细节信息, 并且对集群用户可用(可见)。PV 卷对象存在于 Kubernetes API 中,可供用户消费(使用)。

动态制备

如果管理员所创建的所有静态 PV 卷都无法与用户的 PersistentVolumeClaim 匹配, 集群可以尝试为该 PVC 申领动态制备一个存储卷。 这一制备操作是基于 StorageClass 来实现的:PVC 申领必须请求某个 存储类, 同时集群管理员必须已经创建并配置了该类,这样动态制备卷的动作才会发生。 如果 PVC 申领指定存储类为 "",则相当于为自身禁止使用动态制备的卷。

为了基于存储类完成动态的存储制备,集群管理员需要在 API 服务器上启用 DefaultStorageClass 准入控制器。 举例而言,可以通过保证 DefaultStorageClass 出现在 API 服务器组件的 --enable-admission-plugins 标志值中实现这点;该标志的值可以是逗号分隔的有序列表。 关于 API 服务器标志的更多信息,可以参考 kube-apiserver 文档。

绑定

用户创建一个带有特定存储容量和特定访问模式需求的 PersistentVolumeClaim 对象; 在动态制备场景下,这个 PVC 对象可能已经创建完毕。 控制平面中的控制回路监测新的 PVC 对象,寻找与之匹配的 PV 卷(如果可能的话), 并将二者绑定到一起。 如果为了新的 PVC 申领动态制备了 PV 卷,则控制回路总是将该 PV 卷绑定到这一 PVC 申领。 否则,用户总是能够获得他们所请求的资源,只是所获得的 PV 卷可能会超出所请求的配置。 一旦绑定关系建立,则 PersistentVolumeClaim 绑定就是排他性的, 无论该 PVC 申领是如何与 PV 卷建立的绑定关系。 PVC 申领与 PV 卷之间的绑定是一种一对一的映射,实现上使用 ClaimRef 来记述 PV 卷与 PVC 申领间的双向绑定关系。

如果找不到匹配的 PV 卷,PVC 申领会无限期地处于未绑定状态。 当与之匹配的 PV 卷可用时,PVC 申领会被绑定。 例如,即使某集群上制备了很多 50 Gi 大小的 PV 卷,也无法与请求 100 Gi 大小的存储的 PVC 匹配。当新的 100 Gi PV 卷被加入到集群时, 该 PVC 才有可能被绑定。

使用

Pod 将 PVC 申领当做存储卷来使用。集群会检视 PVC 申领,找到所绑定的卷, 并为 Pod 挂载该卷。对于支持多种访问模式的卷, 用户要在 Pod 中以卷的形式使用申领时指定期望的访问模式。

一旦用户有了申领对象并且该申领已经被绑定, 则所绑定的 PV 卷在用户仍然需要它期间一直属于该用户。 用户通过在 Pod 的 volumes 块中包含 persistentVolumeClaim 节区来调度 Pod,访问所申领的 PV 卷。 相关细节可参阅使用申领作为卷

保护使用中的存储对象

保护使用中的存储对象(Storage Object in Use Protection) 这一功能特性的目的是确保仍被 Pod 使用的 PersistentVolumeClaim(PVC) 对象及其所绑定的 PersistentVolume(PV)对象在系统中不会被删除,因为这样做可能会引起数据丢失。

如果用户删除被某 Pod 使用的 PVC 对象,该 PVC 申领不会被立即移除。 PVC 对象的移除会被推迟,直至其不再被任何 Pod 使用。 此外,如果管理员删除已绑定到某 PVC 申领的 PV 卷,该 PV 卷也不会被立即移除。 PV 对象的移除也要推迟到该 PV 不再绑定到 PVC。

你可以看到当 PVC 的状态为 Terminating 且其 Finalizers 列表中包含 kubernetes.io/pvc-protection 时,PVC 对象是处于被保护状态的。

kubectl describe pvc hostpath
Name:          hostpath
Namespace:     default
StorageClass:  example-hostpath
Status:        Terminating
Volume:
Labels:        <none>
Annotations:   volume.beta.kubernetes.io/storage-class=example-hostpath
               volume.beta.kubernetes.io/storage-provisioner=example.com/hostpath
Finalizers:    [kubernetes.io/pvc-protection]
...

你也可以看到当 PV 对象的状态为 Terminating 且其 Finalizers 列表中包含 kubernetes.io/pv-protection 时,PV 对象是处于被保护状态的。

kubectl describe pv task-pv-volume
Name:            task-pv-volume
Labels:          type=local
Annotations:     <none>
Finalizers:      [kubernetes.io/pv-protection]
StorageClass:    standard
Status:          Terminating
Claim:
Reclaim Policy:  Delete
Access Modes:    RWO
Capacity:        1Gi
Message:
Source:
    Type:          HostPath (bare host directory volume)
    Path:          /tmp/data
    HostPathType:
Events:            <none>

回收(Reclaiming)

当用户不再使用其存储卷时,他们可以从 API 中将 PVC 对象删除, 从而允许该资源被回收再利用。PersistentVolume 对象的回收策略告诉集群, 当其被从申领中释放时如何处理该数据卷。 目前,数据卷可以被 Retained(保留)、Recycled(回收)或 Deleted(删除)。

保留(Retain)

回收策略 Retain 使得用户可以手动回收资源。当 PersistentVolumeClaim 对象被删除时,PersistentVolume 卷仍然存在,对应的数据卷被视为"已释放(released)"。 由于卷上仍然存在这前一申领人的数据,该卷还不能用于其他申领。 管理员可以通过下面的步骤来手动回收该卷:

  1. 删除 PersistentVolume 对象。与之相关的、位于外部基础设施中的存储资产在 PV 删除之后仍然存在。
  2. 根据情况,手动清除所关联的存储资产上的数据。
  3. 手动删除所关联的存储资产。

如果你希望重用该存储资产,可以基于存储资产的定义创建新的 PersistentVolume 卷对象。

删除(Delete)

对于支持 Delete 回收策略的卷插件,删除动作会将 PersistentVolume 对象从 Kubernetes 中移除,同时也会从外部基础设施中移除所关联的存储资产。 动态制备的卷会继承其 StorageClass 中设置的回收策略, 该策略默认为 Delete。管理员需要根据用户的期望来配置 StorageClass; 否则 PV 卷被创建之后必须要被编辑或者修补。 参阅更改 PV 卷的回收策略

回收(Recycle)

如果下层的卷插件支持,回收策略 Recycle 会在卷上执行一些基本的擦除 (rm -rf /thevolume/*)操作,之后允许该卷用于新的 PVC 申领。

不过,管理员可以按参考资料中所述, 使用 Kubernetes 控制器管理器命令行参数来配置一个定制的回收器(Recycler) Pod 模板。此定制的回收器 Pod 模板必须包含一个 volumes 规约,如下例所示:

apiVersion: v1
kind: Pod
metadata:
  name: pv-recycler
  namespace: default
spec:
  restartPolicy: Never
  volumes:
  - name: vol
    hostPath:
      path: /any/path/it/will/be/replaced
  containers:
  - name: pv-recycler
    image: "registry.k8s.io/busybox"
    command: ["/bin/sh", "-c", "test -e /scrub && rm -rf /scrub/..?* /scrub/.[!.]* /scrub/*  && test -z \"$(ls -A /scrub)\" || exit 1"]
    volumeMounts:
    - name: vol
      mountPath: /scrub

定制回收器 Pod 模板中在 volumes 部分所指定的特定路径要替换为正被回收的卷的路径。

PersistentVolume 删除保护 finalizer

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

可以在 PersistentVolume 上添加终结器(Finalizer), 以确保只有在删除对应的存储后才删除具有 Delete 回收策略的 PersistentVolume。

新引入的 kubernetes.io/pv-controllerexternal-provisioner.volume.kubernetes.io/finalizer 终结器仅会被添加到动态制备的卷上。

终结器 kubernetes.io/pv-controller 会被添加到树内插件卷上。 下面是一个例子:

kubectl describe pv pvc-74a498d6-3929-47e8-8c02-078c1ece4d78
Name:            pvc-74a498d6-3929-47e8-8c02-078c1ece4d78
Labels:          <none>
Annotations:     kubernetes.io/createdby: vsphere-volume-dynamic-provisioner
                 pv.kubernetes.io/bound-by-controller: yes
                 pv.kubernetes.io/provisioned-by: kubernetes.io/vsphere-volume
Finalizers:      [kubernetes.io/pv-protection kubernetes.io/pv-controller]
StorageClass:    vcp-sc
Status:          Bound
Claim:           default/vcp-pvc-1
Reclaim Policy:  Delete
Access Modes:    RWO
VolumeMode:      Filesystem
Capacity:        1Gi
Node Affinity:   <none>
Message:
Source:
    Type:               vSphereVolume (a Persistent Disk resource in vSphere)
    VolumePath:         [vsanDatastore] d49c4a62-166f-ce12-c464-020077ba5d46/kubernetes-dynamic-pvc-74a498d6-3929-47e8-8c02-078c1ece4d78.vmdk
    FSType:             ext4
    StoragePolicyName:  vSAN Default Storage Policy
Events:                 <none>

终结器 external-provisioner.volume.kubernetes.io/finalizer 会被添加到 CSI 卷上。下面是一个例子:

Name:            pvc-2f0bab97-85a8-4552-8044-eb8be45cf48d
Labels:          <none>
Annotations:     pv.kubernetes.io/provisioned-by: csi.vsphere.vmware.com
Finalizers:      [kubernetes.io/pv-protection external-provisioner.volume.kubernetes.io/finalizer]
StorageClass:    fast
Status:          Bound
Claim:           demo-app/nginx-logs
Reclaim Policy:  Delete
Access Modes:    RWO
VolumeMode:      Filesystem
Capacity:        200Mi
Node Affinity:   <none>
Message:
Source:
    Type:              CSI (a Container Storage Interface (CSI) volume source)
    Driver:            csi.vsphere.vmware.com
    FSType:            ext4
    VolumeHandle:      44830fa8-79b4-406b-8b58-621ba25353fd
    ReadOnly:          false
    VolumeAttributes:      storage.kubernetes.io/csiProvisionerIdentity=1648442357185-8081-csi.vsphere.vmware.com
                           type=vSphere CNS Block Volume
Events:                <none>

当为特定的树内卷插件启用了 CSIMigration{provider} 特性标志时,kubernetes.io/pv-controller 终结器将被替换为 external-provisioner.volume.kubernetes.io/finalizer 终结器。

预留 PersistentVolume

控制平面可以在集群中将 PersistentVolumeClaims 绑定到匹配的 PersistentVolumes。 但是,如果你希望 PVC 绑定到特定 PV,则需要预先绑定它们。

通过在 PersistentVolumeClaim 中指定 PersistentVolume,你可以声明该特定 PV 与 PVC 之间的绑定关系。如果该 PersistentVolume 存在且未被通过其 claimRef 字段预留给 PersistentVolumeClaim,则该 PersistentVolume 会和该 PersistentVolumeClaim 绑定到一起。

绑定操作不会考虑某些卷匹配条件是否满足,包括节点亲和性等等。 控制面仍然会检查存储类、 访问模式和所请求的存储尺寸都是合法的。

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: foo-pvc
  namespace: foo
spec:
  storageClassName: "" # 此处须显式设置空字符串,否则会被设置为默认的 StorageClass
  volumeName: foo-pv
  ...

此方法无法对 PersistentVolume 的绑定特权做出任何形式的保证。 如果有其他 PersistentVolumeClaim 可以使用你所指定的 PV, 则你应该首先预留该存储卷。你可以将 PV 的 claimRef 字段设置为相关的 PersistentVolumeClaim 以确保其他 PVC 不会绑定到该 PV 卷。

apiVersion: v1
kind: PersistentVolume
metadata:
  name: foo-pv
spec:
  storageClassName: ""
  claimRef:
    name: foo-pvc
    namespace: foo
  ...

如果你想要使用 persistentVolumeReclaimPolicy 属性设置为 Retain 的 PersistentVolume 卷时, 包括你希望复用现有的 PV 卷时,这点是很有用的

扩充 PVC 申领

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

现在,对扩充 PVC 申领的支持默认处于被启用状态。你可以扩充以下类型的卷:

  • azureFile(已弃用)
  • csi
  • flexVolume(已弃用)
  • rbd
  • portworxVolume(已弃用)

只有当 PVC 的存储类中将 allowVolumeExpansion 设置为 true 时,你才可以扩充该 PVC 申领。

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

如果要为某 PVC 请求较大的存储卷,可以编辑 PVC 对象,设置一个更大的尺寸值。 这一编辑操作会触发为下层 PersistentVolume 提供存储的卷的扩充。 Kubernetes 不会创建新的 PV 卷来满足此申领的请求。 与之相反,现有的卷会被调整大小。

CSI 卷的扩充

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

对 CSI 卷的扩充能力默认是被启用的,不过扩充 CSI 卷要求 CSI 驱动支持卷扩充操作。可参阅特定 CSI 驱动的文档了解更多信息。

重设包含文件系统的卷的大小

只有卷中包含的文件系统是 XFS、Ext3 或者 Ext4 时,你才可以重设卷的大小。

当卷中包含文件系统时,只有在 Pod 使用 ReadWrite 模式来使用 PVC 申领的情况下才能重设其文件系统的大小。文件系统扩充的操作或者是在 Pod 启动期间完成,或者在下层文件系统支持在线扩充的前提下在 Pod 运行期间完成。

如果 FlexVolumes 的驱动将 RequiresFSResize 能力设置为 true, 则该 FlexVolume 卷(于 Kubernetes v1.23 弃用)可以在 Pod 重启期间调整大小。

重设使用中 PVC 申领的大小

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

在这种情况下,你不需要删除和重建正在使用某现有 PVC 的 Pod 或 Deployment。 所有使用中的 PVC 在其文件系统被扩充之后,立即可供其 Pod 使用。 此功能特性对于没有被 Pod 或 Deployment 使用的 PVC 而言没有效果。 你必须在执行扩展操作之前创建一个使用该 PVC 的 Pod。

与其他卷类型类似,FlexVolume 卷也可以在被 Pod 使用期间执行扩充操作。

处理扩充卷过程中的失败

如果用户指定的新大小过大,底层存储系统无法满足,PVC 的扩展将不断重试, 直到用户或集群管理员采取一些措施。这种情况是不希望发生的,因此 Kubernetes 提供了以下从此类故障中恢复的方法。

如果扩充下层存储的操作失败,集群管理员可以手动地恢复 PVC 申领的状态并取消重设大小的请求。否则,在没有管理员干预的情况下, 控制器会反复重试重设大小的操作。

  1. 将绑定到 PVC 申领的 PV 卷标记为 Retain 回收策略。
  2. 删除 PVC 对象。由于 PV 的回收策略为 Retain,我们不会在重建 PVC 时丢失数据。
  3. 删除 PV 规约中的 claimRef 项,这样新的 PVC 可以绑定到该卷。 这一操作会使得 PV 卷变为 "可用(Available)"。
  4. 使用小于 PV 卷大小的尺寸重建 PVC,设置 PVC 的 volumeName 字段为 PV 卷的名称。 这一操作将把新的 PVC 对象绑定到现有的 PV 卷。
  5. 不要忘记恢复 PV 卷上设置的回收策略。

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

如果集群中的特性门控 RecoverVolumeExpansionFailure 已启用,在 PVC 的扩展发生失败时,你可以使用比先前请求的值更小的尺寸来重试扩展。 要使用一个更小的尺寸尝试请求新的扩展,请编辑该 PVC 的 .spec.resources 并选择一个比你之前所尝试的值更小的值。 如果由于容量限制而无法成功扩展至更高的值,这将很有用。 如果发生了这种情况,或者你怀疑可能发生了这种情况, 你可以通过指定一个在底层存储制备容量限制内的尺寸来重试扩展。 你可以通过查看 .status.allocatedResourceStatuses 以及 PVC 上的事件来监控调整大小操作的状态。

请注意, 尽管你可以指定比之前的请求更低的存储量,新值必须仍然高于 .status.capacity。 Kubernetes 不支持将 PVC 缩小到小于其当前的尺寸。

持久卷的类型

PV 持久卷是用插件的形式来实现的。Kubernetes 目前支持以下插件:

  • csi - 容器存储接口 (CSI)
  • fc - Fibre Channel (FC) 存储
  • hostPath - HostPath 卷 (仅供单节点测试使用;不适用于多节点集群;请尝试使用 local 卷作为替代)
  • iscsi - iSCSI (SCSI over IP) 存储
  • local - 节点上挂载的本地存储设备
  • nfs - 网络文件系统 (NFS) 存储

以下的持久卷已被弃用。这意味着当前仍是支持的,但是 Kubernetes 将来的发行版会将其移除。

  • azureFile - Azure File (于 v1.21 弃用
  • flexVolume - FlexVolume (于 v1.23 弃用
  • portworxVolume - Portworx 卷 (于 v1.25 弃用
  • vsphereVolume - vSphere VMDK 卷 (于 v1.19 弃用
  • cephfs - CephFS 卷 (于 v1.28 弃用
  • rbd - Rados Block Device (RBD) 卷 (于 v1.28 弃用

旧版本的 Kubernetes 仍支持这些“树内(In-Tree)”持久卷类型:

  • awsElasticBlockStore - AWS Elastic Block Store (EBS) (v1.27 开始不可用
  • azureDisk - Azure Disk (v1.27 开始不可用
  • cinder - Cinder (OpenStack block storage) (v1.27 开始不可用
  • photonPersistentDisk - Photon 控制器持久化盘。(从 v1.15 版本开始将不可用
  • scaleIO - ScaleIO 卷(v1.21 之后不可用
  • flocker - Flocker 存储 (v1.25 之后不可用
  • quobyte - Quobyte 卷 (v1.25 之后不可用
  • storageos - StorageOS 卷 (v1.25 之后不可用

持久卷

每个 PV 对象都包含 spec 部分和 status 部分,分别对应卷的规约和状态。 PersistentVolume 对象的名称必须是合法的 DNS 子域名.

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv0003
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: slow
  mountOptions:
    - hard
    - nfsvers=4.1
  nfs:
    path: /tmp
    server: 172.17.0.2

容量

一般而言,每个 PV 卷都有确定的存储容量。 这是通过 PV 的 capacity 属性设置的, 该属性是一个量纲(Quantity)

目前,存储大小是可以设置和请求的唯一资源。 未来可能会包含 IOPS、吞吐量等属性。

卷模式

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

针对 PV 持久卷,Kubernetes 支持两种卷模式(volumeModes):Filesystem(文件系统)Block(块)volumeMode 是一个可选的 API 参数。 如果该参数被省略,默认的卷模式是 Filesystem

volumeMode 属性设置为 Filesystem 的卷会被 Pod 挂载(Mount) 到某个目录。 如果卷的存储来自某块设备而该设备目前为空,Kuberneretes 会在第一次挂载卷之前在设备上创建文件系统。

你可以将 volumeMode 设置为 Block,以便将卷作为原始块设备来使用。 这类卷以块设备的方式交给 Pod 使用,其上没有任何文件系统。 这种模式对于为 Pod 提供一种使用最快可能方式来访问卷而言很有帮助, Pod 和卷之间不存在文件系统层。另外,Pod 中运行的应用必须知道如何处理原始块设备。 关于如何在 Pod 中使用 volumeMode: Block 的卷, 可参阅原始块卷支持

访问模式

PersistentVolume 卷可以用资源提供者所支持的任何方式挂载到宿主系统上。 如下表所示,提供者(驱动)的能力不同,每个 PV 卷的访问模式都会设置为对应卷所支持的模式值。 例如,NFS 可以支持多个读写客户,但是某个特定的 NFS PV 卷可能在服务器上以只读的方式导出。 每个 PV 卷都会获得自身的访问模式集合,描述的是特定 PV 卷的能力。

访问模式有:

ReadWriteOnce
卷可以被一个节点以读写方式挂载。 ReadWriteOnce 访问模式仍然可以在同一节点上运行的多个 Pod 访问该卷。 对于单个 Pod 的访问,请参考 ReadWriteOncePod 访问模式。
ReadOnlyMany
卷可以被多个节点以只读方式挂载。
ReadWriteMany
卷可以被多个节点以读写方式挂载。
ReadWriteOncePod
特性状态: Kubernetes v1.29 [stable]
卷可以被单个 Pod 以读写方式挂载。 如果你想确保整个集群中只有一个 Pod 可以读取或写入该 PVC, 请使用 ReadWriteOncePod 访问模式。

在命令行接口(CLI)中,访问模式也使用以下缩写形式:

  • RWO - ReadWriteOnce
  • ROX - ReadOnlyMany
  • RWX - ReadWriteMany
  • RWOP - ReadWriteOncePod

重要提醒! 每个卷同一时刻只能以一种访问模式挂载,即使该卷能够支持多种访问模式。

卷插件ReadWriteOnceReadOnlyManyReadWriteManyReadWriteOncePod
AzureFile-
CephFS-
CSI取决于驱动取决于驱动取决于驱动取决于驱动
FC--
FlexVolume取决于驱动-
GCEPersistentDisk--
Glusterfs-
HostPath---
iSCSI--
NFS-
RBD--
VsphereVolume--(Pod 运行于同一节点上时可行)-
PortworxVolume--

每个 PV 可以属于某个类(Class),通过将其 storageClassName 属性设置为某个 StorageClass 的名称来指定。 特定类的 PV 卷只能绑定到请求该类存储卷的 PVC 申领。 未设置 storageClassName 的 PV 卷没有类设定,只能绑定到那些没有指定特定存储类的 PVC 申领。

早前,Kubernetes 使用注解 volume.beta.kubernetes.io/storage-class 而不是 storageClassName 属性。这一注解目前仍然起作用,不过在将来的 Kubernetes 发布版本中该注解会被彻底废弃。

回收策略

目前的回收策略有:

  • Retain -- 手动回收
  • Recycle -- 简单擦除 (rm -rf /thevolume/*)
  • Delete -- 删除存储卷

对于 Kubernetes 1.30 来说,只有 nfshostPath 卷类型支持回收(Recycle)。

挂载选项

Kubernetes 管理员可以指定持久卷被挂载到节点上时使用的附加挂载选项。

以下卷类型支持挂载选项:

  • azureFile
  • cephfs(于 v1.28 中弃用
  • cinder(于 v1.18 中弃用
  • iscsi
  • nfs
  • rbd(于 v1.28 中弃用
  • vsphereVolume

Kubernetes 不对挂载选项执行合法性检查。如果挂载选项是非法的,挂载就会失败。

早前,Kubernetes 使用注解 volume.beta.kubernetes.io/mount-options 而不是 mountOptions 属性。这一注解目前仍然起作用,不过在将来的 Kubernetes 发布版本中该注解会被彻底废弃。

节点亲和性

每个 PV 卷可以通过设置节点亲和性来定义一些约束,进而限制从哪些节点上可以访问此卷。 使用这些卷的 Pod 只会被调度到节点亲和性规则所选择的节点上执行。 要设置节点亲和性,配置 PV 卷 .spec 中的 nodeAffinity持久卷 API 参考关于该字段的更多细节。

阶段

每个持久卷会处于以下阶段(Phase)之一:

Available
卷是一个空闲资源,尚未绑定到任何申领
Bound
该卷已经绑定到某申领
Released
所绑定的申领已被删除,但是关联存储资源尚未被集群回收
Failed
卷的自动回收操作失败

你可以使用 kubectl describe persistentvolume <name> 查看已绑定到 PV 的 PVC 的名称。

阶段转换时间戳

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

持久卷的 .status 字段可以包含 Alpha 状态的 lastPhaseTransitionTime 字段。 该字段保存的是卷上次转换阶段的时间戳。 对于新创建的卷,阶段被设置为 PendinglastPhaseTransitionTime 被设置为当前时间。

PersistentVolumeClaims

每个 PVC 对象都有 specstatus 部分,分别对应申领的规约和状态。 PersistentVolumeClaim 对象的名称必须是合法的 DNS 子域名.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: myclaim
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 8Gi
  storageClassName: slow
  selector:
    matchLabels:
      release: "stable"
    matchExpressions:
      - {key: environment, operator: In, values: [dev]}

访问模式

申领在请求具有特定访问模式的存储时,使用与卷相同的访问模式约定

卷模式

申领使用与卷相同的约定来表明是将卷作为文件系统还是块设备来使用。

资源

申领和 Pod 一样,也可以请求特定数量的资源。在这个上下文中,请求的资源是存储。 卷和申领都使用相同的资源模型

选择算符

申领可以设置标签选择算符 来进一步过滤卷集合。只有标签与选择算符相匹配的卷能够绑定到申领上。 选择算符包含两个字段:

  • matchLabels - 卷必须包含带有此值的标签
  • matchExpressions - 通过设定键(key)、值列表和操作符(operator) 来构造的需求。合法的操作符有 In、NotIn、Exists 和 DoesNotExist。

来自 matchLabelsmatchExpressions 的所有需求都按逻辑与的方式组合在一起。 这些需求都必须被满足才被视为匹配。

申领可以通过为 storageClassName 属性设置 StorageClass 的名称来请求特定的存储类。 只有所请求的类的 PV 卷,即 storageClassName 值与 PVC 设置相同的 PV 卷, 才能绑定到 PVC 申领。

PVC 申领不必一定要请求某个类。如果 PVC 的 storageClassName 属性值设置为 "", 则被视为要请求的是没有设置存储类的 PV 卷,因此这一 PVC 申领只能绑定到未设置存储类的 PV 卷(未设置注解或者注解值为 "" 的 PersistentVolume(PV)对象在系统中不会被删除, 因为这样做可能会引起数据丢失)。未设置 storageClassName 的 PVC 与此大不相同, 也会被集群作不同处理。具体筛查方式取决于 DefaultStorageClass 准入控制器插件 是否被启用。

  • 如果准入控制器插件被启用,则管理员可以设置一个默认的 StorageClass。 所有未设置 storageClassName 的 PVC 都只能绑定到隶属于默认存储类的 PV 卷。 设置默认 StorageClass 的工作是通过将对应 StorageClass 对象的注解 storageclass.kubernetes.io/is-default-class 赋值为 true 来完成的。 如果管理员未设置默认存储类,集群对 PVC 创建的处理方式与未启用准入控制器插件时相同。 如果设定的默认存储类不止一个,当 PVC 被动态制备时将使用最新的默认存储类。
  • 如果准入控制器插件被关闭,则不存在默认 StorageClass 的说法。 所有将 storageClassName 设为 "" 的 PVC 只能被绑定到也将 storageClassName 设为 "" 的 PV。 不过,只要默认的 StorageClass 可用,就可以稍后更新缺少 storageClassName 的 PVC。 如果这个 PVC 更新了,它将不再绑定到也将 storageClassName 设为 "" 的 PV。

参阅可追溯的默认 StorageClass 赋值了解更多详细信息。

取决于安装方法,默认的 StorageClass 可能在集群安装期间由插件管理器(Addon Manager)部署到集群中。

当某 PVC 除了请求 StorageClass 之外还设置了 selector,则这两种需求会按逻辑与关系处理: 只有隶属于所请求类且带有所请求标签的 PV 才能绑定到 PVC。

早前,Kubernetes 使用注解 volume.beta.kubernetes.io/storage-class 而不是 storageClassName 属性。这一注解目前仍然起作用,不过在将来的 Kubernetes 发布版本中该注解会被彻底废弃。

可追溯的默认 StorageClass 赋值

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

你可以创建 PersistentVolumeClaim,而无需为新 PVC 指定 storageClassName。 即使你的集群中不存在默认 StorageClass,你也可以这样做。 在这种情况下,新的 PVC 会按照你的定义进行创建,并且在默认值可用之前,该 PVC 的 storageClassName 保持不设置。

当一个默认的 StorageClass 变得可用时,控制平面会识别所有未设置 storageClassName 的现有 PVC。 对于 storageClassName 为空值或没有此主键的 PVC, 控制平面会更新这些 PVC 以设置其 storageClassName 与新的默认 StorageClass 匹配。 如果你有一个现有的 PVC,其中 storageClassName"", 并且你配置了默认 StorageClass,则此 PVC 将不会得到更新。

为了保持绑定到 storageClassName 设为 "" 的 PV(当存在默认 StorageClass 时), 你需要将关联 PVC 的 storageClassName 设置为 ""

此行为可帮助管理员更改默认 StorageClass,方法是先移除旧的 PVC,然后再创建或设置另一个 PVC。 这一时间窗口内因为没有指定默认值,会导致所创建的未设置 storageClassName 的 PVC 也没有默认值设置, 但由于默认 StorageClass 赋值是可追溯的,这种更改默认值的方式是安全的。

使用申领作为卷

Pod 将申领作为卷来使用,并藉此访问存储资源。 申领必须位于使用它的 Pod 所在的同一名字空间内。 集群在 Pod 的名字空间中查找申领,并使用它来获得申领所使用的 PV 卷。 之后,卷会被挂载到宿主上并挂载到 Pod 中。

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
    - name: myfrontend
      image: nginx
      volumeMounts:
      - mountPath: "/var/www/html"
        name: mypd
  volumes:
    - name: mypd
      persistentVolumeClaim:
        claimName: myclaim

关于名字空间的说明

PersistentVolume 卷的绑定是排他性的。 由于 PersistentVolumeClaim 是名字空间作用域的对象,使用 "Many" 模式(ROXRWX)来挂载申领的操作只能在同一名字空间内进行。

类型为 hostpath 的 PersistentVolume

hostPath PersistentVolume 使用节点上的文件或目录来模拟网络附加(network-attached)存储。 相关细节可参阅 hostPath 卷示例

原始块卷支持

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

以下卷插件支持原始块卷,包括其动态制备(如果支持的话)的卷:

  • CSI
  • FC(光纤通道)
  • iSCSI
  • Local 卷
  • OpenStack Cinder
  • RBD(已弃用)
  • RBD(Ceph 块设备,已弃用)
  • VsphereVolume

使用原始块卷的持久卷

apiVersion: v1
kind: PersistentVolume
metadata:
  name: block-pv
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  volumeMode: Block
  persistentVolumeReclaimPolicy: Retain
  fc:
    targetWWNs: ["50060e801049cfd1"]
    lun: 0
    readOnly: false

申请原始块卷的 PVC 申领

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: block-pvc
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Block
  resources:
    requests:
      storage: 10Gi

在容器中添加原始块设备路径的 Pod 规约

apiVersion: v1
kind: Pod
metadata:
  name: pod-with-block-volume
spec:
  containers:
    - name: fc-container
      image: fedora:26
      command: ["/bin/sh", "-c"]
      args: [ "tail -f /dev/null" ]
      volumeDevices:
        - name: data
          devicePath: /dev/xvda
  volumes:
    - name: data
      persistentVolumeClaim:
        claimName: block-pvc

绑定块卷

如果用户通过 PersistentVolumeClaim 规约的 volumeMode 字段来表明对原始块设备的请求, 绑定规则与之前版本中未在规约中考虑此模式的实现略有不同。 下面列举的表格是用户和管理员可以为请求原始块设备所作设置的组合。 此表格表明在不同的组合下卷是否会被绑定。

静态制备卷的卷绑定矩阵:

PV volumeModePVC volumeModeResult
未指定未指定绑定
未指定Block不绑定
未指定Filesystem绑定
Block未指定不绑定
BlockBlock绑定
BlockFilesystem不绑定
FilesystemFilesystem绑定
FilesystemBlock不绑定
Filesystem未指定绑定

对卷快照及从卷快照中恢复卷的支持

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

卷快照(Volume Snapshot)仅支持树外 CSI 卷插件。 有关细节可参阅卷快照文档。 树内卷插件被弃用。你可以查阅卷插件 FAQ 了解已弃用的卷插件。

基于卷快照创建 PVC 申领

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: restore-pvc
spec:
  storageClassName: csi-hostpath-sc
  dataSource:
    name: new-snapshot-test
    kind: VolumeSnapshot
    apiGroup: snapshot.storage.k8s.io
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi

卷克隆

卷克隆功能特性仅适用于 CSI 卷插件。

基于现有 PVC 创建新的 PVC 申领

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: cloned-pvc
spec:
  storageClassName: my-csi-plugin
  dataSource:
    name: existing-src-pvc-name
    kind: PersistentVolumeClaim
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi

卷填充器(Populator)与数据源

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

Kubernetes 支持自定义的卷填充器。要使用自定义的卷填充器,你必须为 kube-apiserver 和 kube-controller-manager 启用 AnyVolumeDataSource 特性门控

卷填充器利用了 PVC 规约字段 dataSourceRef。 不像 dataSource 字段只能包含对另一个持久卷申领或卷快照的引用, dataSourceRef 字段可以包含对同一命名空间中任何对象的引用(不包含除 PVC 以外的核心资源)。 对于启用了特性门控的集群,使用 dataSourceRefdataSource 更好。

跨名字空间数据源

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

Kubernetes 支持跨名字空间卷数据源。 要使用跨名字空间卷数据源,你必须为 kube-apiserver、kube-controller 管理器启用 AnyVolumeDataSourceCrossNamespaceVolumeDataSource 特性门控。 此外,你必须为 csi-provisioner 启用 CrossNamespaceVolumeDataSource 特性门控。

启用 CrossNamespaceVolumeDataSource 特性门控允许你在 dataSourceRef 字段中指定名字空间。

数据源引用

dataSourceRef 字段的行为与 dataSource 字段几乎相同。 如果其中一个字段被指定而另一个字段没有被指定,API 服务器将给两个字段相同的值。 这两个字段都不能在创建后改变,如果试图为这两个字段指定不同的值,将导致验证错误。 因此,这两个字段将总是有相同的内容。

dataSourceRef 字段和 dataSource 字段之间有两个用户应该注意的区别:

  • dataSource 字段会忽略无效的值(如同是空值), 而 dataSourceRef 字段永远不会忽略值,并且若填入一个无效的值,会导致错误。 无效值指的是 PVC 之外的核心对象(没有 apiGroup 的对象)。
  • dataSourceRef 字段可以包含不同类型的对象,而 dataSource 字段只允许 PVC 和卷快照。

CrossNamespaceVolumeDataSource 特性被启用时,存在其他区别:

  • dataSource 字段仅允许本地对象,而 dataSourceRef 字段允许任何名字空间中的对象。
  • 若指定了 namespace,则 dataSourcedataSourceRef 不会被同步。

用户始终应该在启用了此特性门控的集群上使用 dataSourceRef, 在没有启用该特性门控的集群上使用 dataSource。 在任何情况下都没有必要查看这两个字段。 这两个字段的值看似相同但是语义稍微不一样,是为了向后兼容。 特别是混用旧版本和新版本的控制器时,它们能够互通。

使用卷填充器

卷填充器是能创建非空卷的控制器, 其卷的内容通过一个自定义资源决定。 用户通过使用 dataSourceRef 字段引用自定义资源来创建一个被填充的卷:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: populated-pvc
spec:
  dataSourceRef:
    name: example-name
    kind: ExampleDataSource
    apiGroup: example.storage.k8s.io
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi

因为卷填充器是外部组件,如果没有安装所有正确的组件,试图创建一个使用卷填充器的 PVC 就会失败。 外部控制器应该在 PVC 上产生事件,以提供创建状态的反馈,包括在由于缺少某些组件而无法创建 PVC 的情况下发出警告。

你可以把 alpha 版本的卷数据源验证器 控制器安装到你的集群中。 如果没有填充器处理该数据源的情况下,该控制器会在 PVC 上产生警告事件。 当一个合适的填充器被安装到 PVC 上时,该控制器的职责是上报与卷创建有关的事件,以及在该过程中发生的问题。

使用跨名字空间的卷数据源

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

创建 ReferenceGrant 以允许名字空间属主接受引用。 你通过使用 dataSourceRef 字段指定跨名字空间卷数据源,定义填充的卷。 你必须在源名字空间中已经有一个有效的 ReferenceGrant:

apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
  name: allow-ns1-pvc
  namespace: default
spec:
  from:
  - group: ""
    kind: PersistentVolumeClaim
    namespace: ns1
  to:
  - group: snapshot.storage.k8s.io
    kind: VolumeSnapshot
    name: new-snapshot-demo
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: foo-pvc
  namespace: ns1
spec:
  storageClassName: example
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  dataSourceRef:
    apiGroup: snapshot.storage.k8s.io
    kind: VolumeSnapshot
    name: new-snapshot-demo
    namespace: default
  volumeMode: Filesystem

编写可移植的配置

如果你要编写配置模板和示例用来在很多集群上运行并且需要持久性存储,建议你使用以下模式:

  • 将 PersistentVolumeClaim 对象包含到你的配置包(Bundle)中,和 Deployment 以及 ConfigMap 等放在一起。
  • 不要在配置中包含 PersistentVolume 对象,因为对配置进行实例化的用户很可能 没有创建 PersistentVolume 的权限。
  • 为用户提供在实例化模板时指定存储类名称的能力。
    • 仍按用户提供存储类名称,将该名称放到 persistentVolumeClaim.storageClassName 字段中。 这样会使得 PVC 在集群被管理员启用了存储类支持时能够匹配到正确的存储类,
    • 如果用户未指定存储类名称,将 persistentVolumeClaim.storageClassName 留空(nil)。 这样,集群会使用默认 StorageClass 为用户自动制备一个存储卷。 很多集群环境都配置了默认的 StorageClass,或者管理员也可以自行创建默认的 StorageClass
  • 在你的工具链中,监测经过一段时间后仍未被绑定的 PVC 对象,要让用户知道这些对象, 因为这可能意味着集群不支持动态存储(因而用户必须先创建一个匹配的 PV),或者 集群没有配置存储系统(因而用户无法配置需要 PVC 的工作负载配置)。

接下来

API 参考

阅读以下页面中描述的 API:

3.6.3 - 投射卷

本文档描述 Kubernetes 中的投射卷(Projected Volumes)。 建议先熟悉概念。

介绍

一个 projected 卷可以将若干现有的卷源映射到同一个目录之上。

目前,以下类型的卷源可以被投射:

所有的卷源都要求处于 Pod 所在的同一个名字空间内。更多详细信息, 可参考一体化卷设计文档。

带有 Secret、DownwardAPI 和 ConfigMap 的配置示例

apiVersion: v1
kind: Pod
metadata:
  name: volume-test
spec:
  containers:
  - name: container-test
    image: busybox:1.28
    volumeMounts:
    - name: all-in-one
      mountPath: "/projected-volume"
      readOnly: true
  volumes:
  - name: all-in-one
    projected:
      sources:
      - secret:
          name: mysecret
          items:
            - key: username
              path: my-group/my-username
      - downwardAPI:
          items:
            - path: "labels"
              fieldRef:
                fieldPath: metadata.labels
            - path: "cpu_limit"
              resourceFieldRef:
                containerName: container-test
                resource: limits.cpu
      - configMap:
          name: myconfigmap
          items:
            - key: config
              path: my-group/my-config

带有非默认权限模式设置的 Secret 的配置示例

apiVersion: v1
kind: Pod
metadata:
  name: volume-test
spec:
  containers:
  - name: container-test
    image: busybox:1.28
    volumeMounts:
    - name: all-in-one
      mountPath: "/projected-volume"
      readOnly: true
  volumes:
  - name: all-in-one
    projected:
      sources:
      - secret:
          name: mysecret
          items:
            - key: username
              path: my-group/my-username
      - secret:
          name: mysecret2
          items:
            - key: password
              path: my-group/my-password
              mode: 511

每个被投射的卷源都列举在规约中的 sources 下面。参数几乎相同,只有两个例外:

  • 对于 Secret,secretName 字段被改为 name 以便于 ConfigMap 的命名一致;
  • defaultMode 只能在投射层级设置,不能在卷源层级设置。不过,正如上面所展示的, 你可以显式地为每个投射单独设置 mode 属性。

serviceAccountToken 投射卷

你可以将当前服务账号的令牌注入到 Pod 中特定路径下。例如:

apiVersion: v1
kind: Pod
metadata:
  name: sa-token-test
spec:
  containers:
  - name: container-test
    image: busybox:1.28
    volumeMounts:
    - name: token-vol
      mountPath: "/service-account"
      readOnly: true
  serviceAccountName: default
  volumes:
  - name: token-vol
    projected:
      sources:
      - serviceAccountToken:
          audience: api
          expirationSeconds: 3600
          path: token

示例 Pod 中包含一个投射卷,其中包含注入的服务账号令牌。 此 Pod 中的容器可以使用该令牌访问 Kubernetes API 服务器, 使用 Pod 的 ServiceAccount 进行身份验证。audience 字段包含令牌所针对的受众。 收到令牌的主体必须使用令牌受众中所指定的某个标识符来标识自身,否则应该拒绝该令牌。 此字段是可选的,默认值为 API 服务器的标识。

字段 expirationSeconds 是服务账号令牌预期的生命期长度。默认值为 1 小时, 必须至少为 10 分钟(600 秒)。管理员也可以通过设置 API 服务器的命令行参数 --service-account-max-token-expiration 来为其设置最大值上限。 path 字段给出与投射卷挂载点之间的相对路径。

clusterTrustBundle 投射卷

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

clusterTrustBundle 投射卷源将一个或多个 ClusterTrustBundle 对象的内容作为一个自动更新的文件注入到容器文件系统中。

ClusterTrustBundle 可以通过名称签名者名称被选中。

要按名称选择,可以使用 name 字段指定单个 ClusterTrustBundle 对象。

要按签名者名称选择,可以使用 signerName 字段(也可选用 labelSelector 字段) 指定一组使用给定签名者名称的 ClusterTrustBundle 对象。 如果 labelSelector 不存在,则针对该签名者的所有 ClusterTrustBundles 将被选中。

kubelet 会对所选 ClusterTrustBundle 对象中的证书进行去重,规范化 PEM 表示(丢弃注释和头部), 重新排序证书,并将这些证书写入由 path 指定的文件中。 随着所选 ClusterTrustBundles 的集合或其内容发生变化,kubelet 会保持更新此文件。

默认情况下,如果找不到指定的 ClusterTrustBundle,或者 signerName / labelSelector 与所有 ClusterTrustBundle 都不匹配,kubelet 将阻止 Pod 启动。如果这不是你想要的行为, 可以将 optional 字段设置为 true,Pod 将使用 path 处的空白文件启动。

apiVersion: v1
kind: Pod
metadata:
  name: sa-ctb-name-test
spec:
  containers:
  - name: container-test
    image: busybox
    command: ["sleep", "3600"]
    volumeMounts:
    - name: token-vol
      mountPath: "/root-certificates"
      readOnly: true
  serviceAccountName: default
  volumes:
  - name: root-certificates-vol
    projected:
      sources:
      - clusterTrustBundle:
          name: example
          path: example-roots.pem
      - clusterTrustBundle:
          signerName: "example.com/mysigner"
          labelSelector:
            matchLabels:
              version: live
          path: mysigner-roots.pem
          optional: true

与 SecurityContext 间的关系

关于在投射的服务账号卷中处理文件访问权限的提案 介绍了如何使得所投射的文件具有合适的属主访问权限。

Linux

在包含了投射卷并在 SecurityContext 中设置了 RunAsUser 属性的 Linux Pod 中,投射文件具有正确的属主属性设置, 其中包含了容器用户属主。

当 Pod 中的所有容器在其 PodSecurityContext 或容器 SecurityContext 中设置了相同的 runAsUser 时,kubelet 将确保 serviceAccountToken 卷的内容归该用户所有,并且令牌文件的权限模式会被设置为 0600

Windows

在包含了投射卷并在 SecurityContext 中设置了 RunAsUsername 的 Windows Pod 中, 由于 Windows 中用户账号的管理方式问题,文件的属主无法正确设置。 Windows 在名为安全账号管理器(Security Account Manager,SAM) 的数据库中保存本地用户和组信息。每个容器会维护其自身的 SAM 数据库实例, 宿主系统无法窥视到容器运行期间数据库内容。Windows 容器被设计用来运行操作系统的用户态部分, 与宿主系统之间隔离,因此维护了一个虚拟的 SAM 数据库。 所以,在宿主系统上运行的 kubelet 无法动态为虚拟的容器账号配置宿主文件的属主。 如果需要将宿主机器上的文件与容器共享,建议将它们放到挂载于 C:\ 之外的独立卷中。

默认情况下,所投射的文件会具有如下例所示的属主属性设置:

PS C:\> Get-Acl C:\var\run\secrets\kubernetes.io\serviceaccount\..2021_08_31_22_22_18.318230061\ca.crt | Format-List

Path   : Microsoft.PowerShell.Core\FileSystem::C:\var\run\secrets\kubernetes.io\serviceaccount\..2021_08_31_22_22_18.318230061\ca.crt
Owner  : BUILTIN\Administrators
Group  : NT AUTHORITY\SYSTEM
Access : NT AUTHORITY\SYSTEM Allow  FullControl
         BUILTIN\Administrators Allow  FullControl
         BUILTIN\Users Allow  ReadAndExecute, Synchronize
Audit  :
Sddl   : O:BAG:SYD:AI(A;ID;FA;;;SY)(A;ID;FA;;;BA)(A;ID;0x1200a9;;;BU)

这意味着,所有类似 ContainerAdministrator 的管理员用户都具有读、写和执行访问权限, 而非管理员用户将具有读和执行访问权限。

3.6.4 - 临时卷

本文档描述 Kubernetes 中的 临时卷(Ephemeral Volume)。 建议先了解,特别是 PersistentVolumeClaim 和 PersistentVolume。

有些应用程序需要额外的存储,但并不关心数据在重启后是否仍然可用。 例如,缓存服务经常受限于内存大小,而且可以将不常用的数据转移到比内存慢的存储中,对总体性能的影响并不大。

另有些应用程序需要以文件形式注入的只读数据,比如配置数据或密钥。

临时卷 就是为此类用例设计的。因为卷会遵从 Pod 的生命周期,与 Pod 一起创建和删除, 所以停止和重新启动 Pod 时,不会受持久卷在何处可用的限制。

临时卷在 Pod 规约中以 内联 方式定义,这简化了应用程序的部署和管理。

临时卷的类型

Kubernetes 为了不同的用途,支持几种不同类型的临时卷:

emptyDirconfigMapdownwardAPIsecret 是作为 本地临时存储 提供的。它们由各个节点上的 kubelet 管理。

CSI 临时卷 必须 由第三方 CSI 存储驱动程序提供。

通用临时卷 可以 由第三方 CSI 存储驱动程序提供,也可以由支持动态制备的任何其他存储驱动程序提供。 一些专门为 CSI 临时卷编写的 CSI 驱动程序,不支持动态制备:因此这些驱动程序不能用于通用临时卷。

使用第三方驱动程序的优势在于,它们可以提供 Kubernetes 本身不支持的功能, 例如,与 kubelet 管理的磁盘具有不同性能特征的存储,或者用来注入不同的数据。

CSI 临时卷

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

从概念上讲,CSI 临时卷类似于 configMapdownwardAPIsecret 类型的卷: 在各个本地节点管理卷的存储,并在 Pod 调度到节点后与其他本地资源一起创建。 在这个阶段,Kubernetes 没有重新调度 Pod 的概念。卷创建不太可能失败,否则 Pod 启动将会受阻。 特别是,这些卷 支持感知存储容量的 Pod 调度。 它们目前也没包括在 Pod 的存储资源使用限制中,因为 kubelet 只能对它自己管理的存储强制执行。

下面是使用 CSI 临时存储的 Pod 的示例清单:

kind: Pod
apiVersion: v1
metadata:
  name: my-csi-app
spec:
  containers:
    - name: my-frontend
      image: busybox:1.28
      volumeMounts:
      - mountPath: "/data"
        name: my-csi-inline-vol
      command: [ "sleep", "1000000" ]
  volumes:
    - name: my-csi-inline-vol
      csi:
        driver: inline.storage.kubernetes.io
        volumeAttributes:
          foo: bar

volumeAttributes 决定驱动程序准备什么样的卷。每个驱动程序的属性不尽相同,没有实现标准化。 有关进一步的说明,请参阅每个 CSI 驱动程序的文档。

CSI 驱动程序限制

CSI 临时卷允许用户直接向 CSI 驱动程序提供 volumeAttributes,它会作为 Pod 规约的一部分。 有些 volumeAttributes 通常仅限于管理员使用,允许这一类 volumeAttributes 的 CSI 驱动程序不适合在内联临时卷中使用。 例如,通常在 StorageClass 中定义的参数不应通过使用内联临时卷向用户公开。

如果集群管理员需要限制在 Pod 规约中作为内联卷使用的 CSI 驱动程序,可以这样做:

  • 从 CSIDriver 规约的 volumeLifecycleModes 中删除 Ephemeral,这可以防止驱动程序被用作内联临时卷。
  • 使用准入 Webhook 来限制如何使用此驱动程序。

通用临时卷

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

通用临时卷类似于 emptyDir 卷,因为它为每个 Pod 提供临时数据存放目录, 在最初制备完毕时一般为空。不过通用临时卷也有一些额外的功能特性:

  • 存储可以是本地的,也可以是网络连接的。
  • 卷可以有固定的大小,Pod 不能超量使用。
  • 卷可能有一些初始数据,这取决于驱动程序和参数。

示例:

kind: Pod
apiVersion: v1
metadata:
  name: my-app
spec:
  containers:
    - name: my-frontend
      image: busybox:1.28
      volumeMounts:
      - mountPath: "/scratch"
        name: scratch-volume
      command: [ "sleep", "1000000" ]
  volumes:
    - name: scratch-volume
      ephemeral:
        volumeClaimTemplate:
          metadata:
            labels:
              type: my-frontend-volume
          spec:
            accessModes: [ "ReadWriteOnce" ]
            storageClassName: "scratch-storage-class"
            resources:
              requests:
                storage: 1Gi

生命周期和 PersistentVolumeClaim

关键的设计思想是在 Pod 的卷来源中允许使用 卷申领的参数。 PersistentVolumeClaim 的标签、注解和整套字段集均被支持。 创建这样一个 Pod 后,临时卷控制器在 Pod 所属的命名空间中创建一个实际的 PersistentVolumeClaim 对象,并确保删除 Pod 时,同步删除 PersistentVolumeClaim。

如上设置将触发卷的绑定与/或制备,相应动作或者在 StorageClass 使用即时卷绑定时立即执行,或者当 Pod 被暂时性调度到某节点时执行 (WaitForFirstConsumer 卷绑定模式)。 对于通用的临时卷,建议采用后者,这样调度器就可以自由地为 Pod 选择合适的节点。 对于即时绑定,调度器则必须选出一个节点,使得在卷可用时,能立即访问该卷。

资源所有权而言, 拥有通用临时存储的 Pod 是提供临时存储 (ephemeral storage) 的 PersistentVolumeClaim 的所有者。 当 Pod 被删除时,Kubernetes 垃圾收集器会删除 PVC, 然后 PVC 通常会触发卷的删除,因为存储类的默认回收策略是删除卷。 你可以使用带有 retain 回收策略的 StorageClass 创建准临时 (Quasi-Ephemeral) 本地存储: 该存储比 Pod 寿命长,所以在这种情况下,你需要确保单独进行卷清理。

当这些 PVC 存在时,它们可以像其他 PVC 一样使用。 特别是,它们可以被引用作为批量克隆或快照的数据源。 PVC 对象还保持着卷的当前状态。

PersistentVolumeClaim 的命名

自动创建的 PVC 采取确定性的命名机制:名称是 Pod 名称和卷名称的组合,中间由连字符(-)连接。 在上面的示例中,PVC 将被命名为 my-app-scratch-volume 。 这种确定性的命名机制使得与 PVC 交互变得更容易,因为一旦知道 Pod 名称和卷名,就不必搜索它。

这种命名机制也引入了潜在的冲突,不同的 Pod 之间(名为 “Pod-a” 的 Pod 挂载名为 "scratch" 的卷,和名为 "pod" 的 Pod 挂载名为 “a-scratch” 的卷, 这两者均会生成名为 "pod-a-scratch" 的 PVC),或者在 Pod 和手工创建的 PVC 之间可能出现冲突。

这类冲突会被检测到:如果 PVC 是为 Pod 创建的,那么它只用于临时卷。 此检测基于所有权关系。现有的 PVC 不会被覆盖或修改。 但这并不能解决冲突,因为如果没有正确的 PVC,Pod 就无法启动。

安全

只要用户有权限创建 Pod,就可以使用通用的临时卷间接地创建持久卷申领(PVCs), 即使他们没有权限直接创建 PVCs。集群管理员必须注意这一点。如果这与他们的安全模型相悖, 他们应该使用准入 Webhook

正常的 PVC 的名字空间配额 仍然有效,因此即使允许用户使用这种新机制,他们也不能使用它来规避其他策略。

接下来

kubelet 管理的临时卷

参阅本地临时存储

CSI 临时卷

通用临时卷

3.6.5 - 存储类

本文描述了 Kubernetes 中 StorageClass 的概念。 建议先熟悉持久卷的概念。

StorageClass 为管理员提供了描述存储"类"的方法。 不同的类型可能会映射到不同的服务质量等级或备份策略,或是由集群管理员制定的任意策略。 Kubernetes 本身并不清楚各种类代表的什么。这个类的概念在其他存储系统中有时被称为"配置文件"。

StorageClass API

每个 StorageClass 都包含 provisionerparametersreclaimPolicy 字段, 这些字段会在 StorageClass 需要动态制备 PersistentVolume 时会使用到。

StorageClass 对象的命名很重要,用户使用这个命名来请求生成一个特定的类。 当创建 StorageClass 对象时,管理员设置 StorageClass 对象的命名和其他参数。

管理员可以为没有申请绑定到特定 StorageClass 的 PVC 指定一个默认的存储类: 更多详情请参阅 PersistentVolumeClaim 章节

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: standard
provisioner: kubernetes.io/aws-ebs
parameters:
  type: gp2
reclaimPolicy: Retain
allowVolumeExpansion: true
mountOptions:
  - debug
volumeBindingMode: Immediate

默认 StorageClass

当一个 PVC 没有指定 storageClassName 时,会使用默认的 StorageClass。 集群中只能有一个默认的 StorageClass。如果不小心设置了多个默认的 StorageClass, 在动态制备 PVC 时将使用其中最新的默认设置的 StorageClass。

关于如何设置默认的 StorageClass, 请参见更改默认 StorageClass。 请注意,某些云服务提供商可能已经定义了一个默认的 StorageClass。

存储制备器

每个 StorageClass 都有一个制备器(Provisioner),用来决定使用哪个卷插件制备 PV。 该字段必须指定。

卷插件内置制备器配置示例
AzureFileAzure File
CephFS--
FC--
FlexVolume--
iSCSI--
NFS-NFS
RBDCeph RBD
VsphereVolumevSphere
PortworxVolumePortworx Volume
Local-Local

你不限于指定此处列出的 "内置" 制备器(其名称前缀为 "kubernetes.io" 并打包在 Kubernetes 中)。 你还可以运行和指定外部制备器,这些独立的程序遵循由 Kubernetes 定义的规范。 外部供应商的作者完全可以自由决定他们的代码保存于何处、打包方式、运行方式、使用的插件(包括 Flex)等。 代码仓库 kubernetes-sigs/sig-storage-lib-external-provisioner 包含一个用于为外部制备器编写功能实现的类库。你可以访问代码仓库 kubernetes-sigs/sig-storage-lib-external-provisioner 了解外部驱动列表。

例如,NFS 没有内部制备器,但可以使用外部制备器。 也有第三方存储供应商提供自己的外部制备器。

回收策略

由 StorageClass 动态创建的 PersistentVolume 会在类的 reclaimPolicy 字段中指定回收策略,可以是 Delete 或者 Retain。 如果 StorageClass 对象被创建时没有指定 reclaimPolicy,它将默认为 Delete

通过 StorageClass 手动创建并管理的 PersistentVolume 会使用它们被创建时指定的回收策略。

允许卷扩展

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

PersistentVolume 可以配置为可扩展。将此功能设置为 true 时,允许用户通过编辑相应的 PVC 对象来调整卷大小。

当下层 StorageClass 的 allowVolumeExpansion 字段设置为 true 时,以下类型的卷支持卷扩展。

卷类型及其 Kubernetes 版本要求
卷类型Kubernetes 版本要求
rbd1.11
Azure File1.11
Portworx1.11
FlexVolume1.13
CSI1.14 (alpha), 1.16 (beta)

挂载选项

由 StorageClass 动态创建的 PersistentVolume 将使用类中 mountOptions 字段指定的挂载选项。

如果卷插件不支持挂载选项,却指定了挂载选项,则制备操作会失败。 挂载选项在 StorageClass 和 PV 上都不会做验证。如果其中一个挂载选项无效,那么这个 PV 挂载操作就会失败。

卷绑定模式

volumeBindingMode 字段控制了卷绑定和动态制备应该发生在什么时候。 当未设置时,默认使用 Immediate 模式。

Immediate 模式表示一旦创建了 PersistentVolumeClaim 也就完成了卷绑定和动态制备。 对于由于拓扑限制而非集群所有节点可达的存储后端,PersistentVolume 会在不知道 Pod 调度要求的情况下绑定或者制备。

集群管理员可以通过指定 WaitForFirstConsumer 模式来解决此问题。 该模式将延迟 PersistentVolume 的绑定和制备,直到使用该 PersistentVolumeClaim 的 Pod 被创建。 PersistentVolume 会根据 Pod 调度约束指定的拓扑来选择或制备。 这些包括但不限于资源需求节点筛选器Pod 亲和性和互斥性、 以及污点和容忍度

以下插件支持预创建绑定 PersistentVolume 的 WaitForFirstConsumer 模式:

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

动态制备和预先创建的 PV 也支持 CSI 卷, 但是你需要查看特定 CSI 驱动的文档以查看其支持的拓扑键名和例子。

apiVersion: v1
kind: Pod
metadata:
  name: task-pv-pod
spec:
  nodeSelector:
    kubernetes.io/hostname: kube-01
  volumes:
    - name: task-pv-storage
      persistentVolumeClaim:
        claimName: task-pv-claim
  containers:
    - name: task-pv-container
      image: nginx
      ports:
        - containerPort: 80
          name: "http-server"
      volumeMounts:
        - mountPath: "/usr/share/nginx/html"
          name: task-pv-storage

允许的拓扑结构

当集群操作人员使用了 WaitForFirstConsumer 的卷绑定模式, 在大部分情况下就没有必要将制备限制为特定的拓扑结构。 然而,如果还有需要的话,可以使用 allowedTopologies

这个例子描述了如何将制备卷的拓扑限制在特定的区域, 在使用时应该根据插件支持情况替换 zonezones 参数。

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: standard
provisioner: kubernetes.io/gce-pd
parameters:
  type: pd-standard
volumeBindingMode: WaitForFirstConsumer
allowedTopologies:
- matchLabelExpressions:
  - key: topology.kubernetes.io/zone
    values:
    - us-central-1a
    - us-central-1b

参数

Storage Classes 的参数描述了存储类的卷。取决于制备器,可以接受不同的参数。 例如,参数 type 的值 io1 和参数 iopsPerGB 特定于 EBS PV。 当参数被省略时,会使用默认值。

一个 StorageClass 最多可以定义 512 个参数。这些参数对象的总长度不能超过 256 KiB,包括参数的键和值。

AWS EBS

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: slow
provisioner: kubernetes.io/aws-ebs
parameters:
  type: io1
  iopsPerGB: "10"
  fsType: ext4
  • typeio1gp2sc1st1。详细信息参见 AWS 文档。默认值:gp2
  • zone(已弃用):AWS 区域。如果没有指定 zonezones, 通常卷会在 Kubernetes 集群节点所在的活动区域中轮询调度分配。 zonezones 参数不能同时使用。
  • zones(已弃用):以逗号分隔的 AWS 区域列表。 如果没有指定 zonezones,通常卷会在 Kubernetes 集群节点所在的活动区域中轮询调度分配。 zonezones参数不能同时使用。
  • iopsPerGB:只适用于 io1 卷。每 GiB 每秒 I/O 操作。 AWS 卷插件将其与请求卷的大小相乘以计算 IOPS 的容量, 并将其限制在 20000 IOPS(AWS 支持的最高值,请参阅 AWS 文档)。 这里需要输入一个字符串,即 "10",而不是 10
  • fsType:受 Kubernetes 支持的文件类型。默认值:"ext4"
  • encrypted:指定 EBS 卷是否应该被加密。合法值为 "true" 或者 "false"。 这里需要输入字符串,即 "true",而非 true
  • kmsKeyId:可选。加密卷时使用密钥的完整 Amazon 资源名称。 如果没有提供,但 encrypted 值为 true,AWS 生成一个密钥。关于有效的 ARN 值,请参阅 AWS 文档。

NFS

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: example-nfs
provisioner: example.com/external-nfs
parameters:
  server: nfs-server.example.com
  path: /share
  readOnly: "false"
  • server:NFS 服务器的主机名或 IP 地址。
  • path:NFS 服务器导出的路径。
  • readOnly:是否将存储挂载为只读的标志(默认为 false)。

Kubernetes 不包含内部 NFS 驱动。你需要使用外部驱动为 NFS 创建 StorageClass。 这里有些例子:

vSphere

vSphere 存储类有两种制备器:

树内制备器已经被 弃用。 更多关于 CSI 制备器的详情,请参阅 Kubernetes vSphere CSI 驱动vSphereVolume CSI 迁移

CSI 制备器

vSphere CSI StorageClass 制备器在 Tanzu Kubernetes 集群下运行。示例请参阅 vSphere CSI 仓库

vCP 制备器

以下示例使用 VMware Cloud Provider(vCP)StorageClass 制备器。

  1. 使用用户指定的磁盘格式创建一个 StorageClass。

    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: fast
    provisioner: kubernetes.io/vsphere-volume
    parameters:
      diskformat: zeroedthick
    

    diskformatthinzeroedthickeagerzeroedthick。默认值:"thin"

  1. 在用户指定的数据存储上创建磁盘格式的 StorageClass。

    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: fast
    provisioner: kubernetes.io/vsphere-volume
    parameters:
      diskformat: zeroedthick
      datastore: VSANDatastore
    

    datastore:用户也可以在 StorageClass 中指定数据存储。 卷将在 StorageClass 中指定的数据存储上创建,在这种情况下是 VSANDatastore。 该字段是可选的。 如果未指定数据存储,则将在用于初始化 vSphere Cloud Provider 的 vSphere 配置文件中指定的数据存储上创建该卷。

  1. Kubernetes 中的存储策略管理

    • 使用现有的 vCenter SPBM 策略

      vSphere 用于存储管理的最重要特性之一是基于策略的管理。 基于存储策略的管理(SPBM)是一个存储策略框架,提供单一的统一控制平面的跨越广泛的数据服务和存储解决方案。 SPBM 使得 vSphere 管理员能够克服先期的存储配置挑战,如容量规划、差异化服务等级和管理容量空间。

      SPBM 策略可以在 StorageClass 中使用 storagePolicyName 参数声明。

    • Kubernetes 内的 Virtual SAN 策略支持

      Vsphere Infrastructure(VI)管理员将能够在动态卷配置期间指定自定义 Virtual SAN 存储功能。你现在可以在动态制备卷期间以存储能力的形式定义存储需求,例如性能和可用性。 存储能力需求会转换为 Virtual SAN 策略,之后当持久卷(虚拟磁盘)被创建时, 会将其推送到 Virtual SAN 层。虚拟磁盘分布在 Virtual SAN 数据存储中以满足要求。

      你可以参考基于存储策略的动态制备卷管理, 进一步了解有关持久卷管理的存储策略的详细信息。

有几个 vSphere 例子供你在 Kubernetes for vSphere 中尝试进行持久卷管理。

Ceph RBD

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast
provisioner: kubernetes.io/rbd
parameters:
  monitors: 10.16.153.105:6789
  adminId: kube
  adminSecretName: ceph-secret
  adminSecretNamespace: kube-system
  pool: kube
  userId: kube
  userSecretName: ceph-secret-user
  userSecretNamespace: default
  fsType: ext4
  imageFormat: "2"
  imageFeatures: "layering"
  • monitors:Ceph monitor,逗号分隔。该参数是必需的。
  • adminId:Ceph 客户端 ID,用于在池 ceph 池中创建映像。默认是 "admin"。
  • adminSecretadminId 的 Secret 名称。该参数是必需的。 提供的 secret 必须有值为 "kubernetes.io/rbd" 的 type 参数。
  • adminSecretNamespaceadminSecret 的命名空间。默认是 "default"。
  • pool:Ceph RBD 池。默认是 "rbd"。
  • userId:Ceph 客户端 ID,用于映射 RBD 镜像。默认与 adminId 相同。
  • userSecretName:用于映射 RBD 镜像的 userId 的 Ceph Secret 的名字。 它必须与 PVC 存在于相同的 namespace 中。该参数是必需的。 提供的 secret 必须具有值为 "kubernetes.io/rbd" 的 type 参数,例如以这样的方式创建:

    kubectl create secret generic ceph-secret --type="kubernetes.io/rbd" \
      --from-literal=key='QVFEQ1pMdFhPUnQrSmhBQUFYaERWNHJsZ3BsMmNjcDR6RFZST0E9PQ==' \
      --namespace=kube-system
    
  • userSecretNamespaceuserSecretName 的命名空间。
  • fsType:Kubernetes 支持的 fsType。默认:"ext4"
  • imageFormat:Ceph RBD 镜像格式,"1" 或者 "2"。默认值是 "1"。
  • imageFeatures:这个参数是可选的,只能在你将 imageFormat 设置为 "2" 才使用。 目前支持的功能只是 layering。默认是 "",没有功能打开。

Azure 磁盘

Azure Unmanaged Disk Storage Class(非托管磁盘存储类)

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: slow
provisioner: kubernetes.io/azure-disk
parameters:
  skuName: Standard_LRS
  location: eastus
  storageAccount: azure_storage_account_name
  • skuName:Azure 存储帐户 Sku 层。默认为空。
  • location:Azure 存储帐户位置。默认为空。
  • storageAccount:Azure 存储帐户名称。 如果提供存储帐户,它必须位于与集群相同的资源组中,并且 location 是被忽略的。如果未提供存储帐户,则会在与集群相同的资源组中创建新的存储帐户。

Azure 磁盘 Storage Class(从 v1.7.2 开始)

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: slow
provisioner: kubernetes.io/azure-disk
parameters:
  storageaccounttype: Standard_LRS
  kind: managed
  • storageaccounttype:Azure 存储帐户 Sku 层。默认为空。
  • kind:可能的值是 shareddedicatedmanaged(默认)。 当 kind 的值是 shared 时,所有非托管磁盘都在集群的同一个资源组中的几个共享存储帐户中创建。 当 kind 的值是 dedicated 时,将为在集群的同一个资源组中新的非托管磁盘创建新的专用存储帐户。
  • resourceGroup:指定要创建 Azure 磁盘所属的资源组。必须是已存在的资源组名称。 若未指定资源组,磁盘会默认放入与当前 Kubernetes 集群相同的资源组中。
  • Premium VM 可以同时添加 Standard_LRS 和 Premium_LRS 磁盘,而 Standard 虚拟机只能添加 Standard_LRS 磁盘。
  • 托管虚拟机只能连接托管磁盘,非托管虚拟机只能连接非托管磁盘。

Azure 文件

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: azurefile
provisioner: kubernetes.io/azure-file
parameters:
  skuName: Standard_LRS
  location: eastus
  storageAccount: azure_storage_account_name
  • skuName:Azure 存储帐户 Sku 层。默认为空。
  • location:Azure 存储帐户位置。默认为空。
  • storageAccount:Azure 存储帐户名称。默认为空。 如果不提供存储帐户,会搜索所有与资源相关的存储帐户,以找到一个匹配 skuNamelocation 的账号。 如果提供存储帐户,它必须存在于与集群相同的资源组中,skuNamelocation 会被忽略。
  • secretNamespace:包含 Azure 存储帐户名称和密钥的密钥的名字空间。 默认值与 Pod 相同。
  • secretName:包含 Azure 存储帐户名称和密钥的密钥的名称。 默认值为 azure-storage-account-<accountName>-secret
  • readOnly:指示是否将存储安装为只读的标志。默认为 false,表示"读/写"挂载。 该设置也会影响 VolumeMounts 中的 ReadOnly 设置。

在存储制备期间,为挂载凭证创建一个名为 secretName 的 Secret。如果集群同时启用了 RBAC控制器角色, 为 system:controller:persistent-volume-binder 的 clusterrole 添加 Secret 资源的 create 权限。

在多租户上下文中,强烈建议显式设置 secretNamespace 的值,否则其他用户可能会读取存储帐户凭据。

Portworx 卷

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: portworx-io-priority-high
provisioner: kubernetes.io/portworx-volume
parameters:
  repl: "1"
  snap_interval: "70"
  priority_io: "high"
  • fs:选择的文件系统:none/xfs/ext4(默认:ext4)。
  • block_size:以 Kbytes 为单位的块大小(默认值:32)。
  • repl:同步副本数量,以复制因子 1..3(默认值:1)的形式提供。 这里需要填写字符串,即,"1" 而不是 1
  • io_priority:决定是否从更高性能或者较低优先级存储创建卷 high/medium/low(默认值:low)。
  • snap_interval:触发快照的时钟/时间间隔(分钟)。 快照是基于与先前快照的增量变化,0 是禁用快照(默认:0)。 这里需要填写字符串,即,是 "70" 而不是 70
  • aggregation_level:指定卷分配到的块数量,0 表示一个非聚合卷(默认:0)。 这里需要填写字符串,即,是 "0" 而不是 0
  • ephemeral:指定卷在卸载后进行清理还是持久化。 emptyDir 的使用场景可以将这个值设置为 true, persistent volumes 的使用场景可以将这个值设置为 false (例如 Cassandra 这样的数据库) true/false(默认为 false)。这里需要填写字符串,即, 是 "true" 而不是 true

本地

特性状态: Kubernetes v1.14 [stable]
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer

本地卷还不支持动态制备,然而还是需要创建 StorageClass 以延迟卷绑定, 直到完成 Pod 的调度。这是由 WaitForFirstConsumer 卷绑定模式指定的。

延迟卷绑定使得调度器在为 PersistentVolumeClaim 选择一个合适的 PersistentVolume 时能考虑到所有 Pod 的调度限制。

3.6.6 - 动态卷制备

动态卷制备允许按需创建存储卷。 如果没有动态制备,集群管理员必须手动地联系他们的云或存储提供商来创建新的存储卷, 然后在 Kubernetes 集群创建 PersistentVolume 对象来表示这些卷。 动态制备功能消除了集群管理员预先配置存储的需要。相反,它在用户请求时自动制备存储。

背景

动态卷制备的实现基于 storage.k8s.io API 组中的 StorageClass API 对象。 集群管理员可以根据需要定义多个 StorageClass 对象,每个对象指定一个卷插件(又名 provisioner), 卷插件向卷制备商提供在创建卷时需要的数据卷信息及相关参数。

集群管理员可以在集群中定义和公开多种存储(来自相同或不同的存储系统),每种都具有自定义参数集。 该设计也确保终端用户不必担心存储制备的复杂性和细微差别,但仍然能够从多个存储选项中进行选择。

点击这里查阅有关存储类的更多信息。

启用动态卷制备

要启用动态制备功能,集群管理员需要为用户预先创建一个或多个 StorageClass 对象。 StorageClass 对象定义当动态制备被调用时,哪一个驱动将被使用和哪些参数将被传递给驱动。 StorageClass 对象的名字必须是一个合法的 DNS 子域名。 以下清单创建了一个 StorageClass 存储类 "slow",它提供类似标准磁盘的永久磁盘。

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: slow
provisioner: kubernetes.io/gce-pd
parameters:
  type: pd-standard

以下清单创建了一个 "fast" 存储类,它提供类似 SSD 的永久磁盘。

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast
provisioner: kubernetes.io/gce-pd
parameters:
  type: pd-ssd

使用动态卷制备

用户通过在 PersistentVolumeClaim 中包含存储类来请求动态制备的存储。 在 Kubernetes v1.9 之前,这通过 volume.beta.kubernetes.io/storage-class 注解实现。 然而,这个注解自 v1.6 起就不被推荐使用了。 用户现在能够而且应该使用 PersistentVolumeClaim 对象的 storageClassName 字段。 这个字段的值必须能够匹配到集群管理员配置的 StorageClass 名称(见下面)。

例如,要选择 “fast” 存储类,用户将创建如下的 PersistentVolumeClaim:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: claim1
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: fast
  resources:
    requests:
      storage: 30Gi

该声明会自动制备一块类似 SSD 的永久磁盘。 在删除该声明后,这个卷也会被销毁。

设置默认值的行为

可以在集群上启用动态卷制备,以便在未指定存储类的情况下动态设置所有声明。 集群管理员可以通过以下方式启用此行为:

管理员可以通过向其添加 storageclass.kubernetes.io/is-default-class 注解 来将特定的 StorageClass 标记为默认。 当集群中存在默认的 StorageClass 并且用户创建了一个未指定 storageClassNamePersistentVolumeClaim 时, DefaultStorageClass 准入控制器会自动向其中添加指向默认存储类的 storageClassName 字段。

请注意,如果你在集群的多个 StorageClass 设置 storageclass.kubernetes.io/is-default-class 注解为 true, 并之后创建了未指定 storageClassNamePersistentVolumeClaim, Kubernetes 会使用最新创建的默认 StorageClass。

拓扑感知

多可用区集群中,Pod 可以被分散到某个区域的多个可用区。 单可用区存储后端应该被制备到 Pod 被调度到的可用区。 这可以通过设置卷绑定模式来实现。

3.6.7 - 卷快照

在 Kubernetes 中,卷快照 是一个存储系统上卷的快照,本文假设你已经熟悉了 Kubernetes 的持久卷

介绍

PersistentVolumePersistentVolumeClaim 这两个 API 资源用于给用户和管理员制备卷类似, VolumeSnapshotContentVolumeSnapshot 这两个 API 资源用于给用户和管理员创建卷快照。

VolumeSnapshotContent 是从一个卷获取的一种快照,该卷由管理员在集群中进行制备。 就像持久卷(PersistentVolume)是集群的资源一样,它也是集群中的资源。

VolumeSnapshot 是用户对于卷的快照的请求。它类似于持久卷声明(PersistentVolumeClaim)。

VolumeSnapshotClass 允许指定属于 VolumeSnapshot 的不同属性。在从存储系统的相同卷上获取的快照之间, 这些属性可能有所不同,因此不能通过使用与 PersistentVolumeClaim 相同的 StorageClass 来表示。

卷快照能力为 Kubernetes 用户提供了一种标准的方式来在指定时间点复制卷的内容,并且不需要创建全新的卷。 例如,这一功能使得数据库管理员能够在执行编辑或删除之类的修改之前对数据库执行备份。

当使用该功能时,用户需要注意以下几点:

  • API 对象 VolumeSnapshotVolumeSnapshotContentVolumeSnapshotClassCRD, 不属于核心 API。
  • VolumeSnapshot 支持仅可用于 CSI 驱动。
  • 作为 VolumeSnapshot 部署过程的一部分,Kubernetes 团队提供了一个部署于控制平面的快照控制器, 并且提供了一个叫做 csi-snapshotter 的边车(Sidecar)辅助容器,和 CSI 驱动程序一起部署。 快照控制器监视 VolumeSnapshotVolumeSnapshotContent 对象, 并且负责创建和删除 VolumeSnapshotContent 对象。 边车 csi-snapshotter 监视 VolumeSnapshotContent 对象, 并且触发针对 CSI 端点的 CreateSnapshotDeleteSnapshot 的操作。
  • 还有一个验证性质的 Webhook 服务器,可以对快照对象进行更严格的验证。 Kubernetes 发行版应将其与快照控制器和 CRD(而非 CSI 驱动程序)一起安装。 此服务器应该安装在所有启用了快照功能的 Kubernetes 集群中。
  • CSI 驱动可能实现,也可能没有实现卷快照功能。CSI 驱动可能会使用 csi-snapshotter 来提供对卷快照的支持。详见 CSI 驱动程序文档
  • Kubernetes 负责 CRD 和快照控制器的安装。

卷快照和卷快照内容的生命周期

VolumeSnapshotContents 是集群中的资源。VolumeSnapshots 是对于这些资源的请求。 VolumeSnapshotContentsVolumeSnapshots 之间的交互遵循以下生命周期:

制备卷快照

快照可以通过两种方式进行制备:预制备或动态制备。

预制备

集群管理员创建多个 VolumeSnapshotContents。它们带有存储系统上实际卷快照的详细信息,可以供集群用户使用。 它们存在于 Kubernetes API 中,并且能够被使用。

动态制备

可以从 PersistentVolumeClaim 中动态获取快照,而不用使用已经存在的快照。 在获取快照时,卷快照类 指定要用的特定于存储提供程序的参数。

绑定

在预制备和动态制备场景下,快照控制器处理绑定 VolumeSnapshot 对象和其合适的 VolumeSnapshotContent 对象。 绑定关系是一对一的。

在预制备快照绑定场景下,VolumeSnapshotContent 对象创建之后,才会和 VolumeSnapshot 进行绑定。

快照源的持久性卷声明保护

这种保护的目的是确保在从系统中获取快照时,不会将正在使用的 PersistentVolumeClaim API 对象从系统中删除(因为这可能会导致数据丢失)。

在为某 PersistentVolumeClaim 生成快照时,该 PersistentVolumeClaim 处于被使用状态。 如果删除一个正作为快照源使用的 PersistentVolumeClaim API 对象,该 PersistentVolumeClaim 对象不会立即被移除。 相反,移除 PersistentVolumeClaim 对象的动作会被推迟,直到快照状态变为 ReadyToUse 或快照操作被中止时再执行。

删除

删除 VolumeSnapshot 对象触发删除 VolumeSnapshotContent 操作,并且 DeletionPolicy 会紧跟着执行。 如果 DeletionPolicyDelete,那么底层存储快照会和 VolumeSnapshotContent 一起被删除。 如果 DeletionPolicyRetain,那么底层快照和 VolumeSnapshotContent 都会被保留。

卷快照

每个 VolumeSnapshot 包含一个 spec 和一个 status。

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
  name: new-snapshot-test
spec:
  volumeSnapshotClassName: csi-hostpath-snapclass
  source:
    persistentVolumeClaimName: pvc-test

persistentVolumeClaimNamePersistentVolumeClaim 数据源对快照的名称。 这个字段是动态制备快照中的必填字段。

卷快照可以通过指定 VolumeSnapshotClass 使用 volumeSnapshotClassName 属性来请求特定类。如果没有设置,那么使用默认类(如果有)。

如下面例子所示,对于预制备的快照,需要给快照指定 volumeSnapshotContentName 作为来源。 对于预制备的快照 source 中的volumeSnapshotContentName 字段是必填的。

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
  name: test-snapshot
spec:
  source:
    volumeSnapshotContentName: test-content

卷快照内容

每个 VolumeSnapshotContent 对象包含 spec 和 status。 在动态制备时,快照通用控制器创建 VolumeSnapshotContent 对象。下面是例子:

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotContent
metadata:
  name: snapcontent-72d9a349-aacd-42d2-a240-d775650d2455
spec:
  deletionPolicy: Delete
  driver: hostpath.csi.k8s.io
  source:
    volumeHandle: ee0cfb94-f8d4-11e9-b2d8-0242ac110002
  sourceVolumeMode: Filesystem
  volumeSnapshotClassName: csi-hostpath-snapclass
  volumeSnapshotRef:
    name: new-snapshot-test
    namespace: default
    uid: 72d9a349-aacd-42d2-a240-d775650d2455

volumeHandle 是存储后端创建卷的唯一标识符,在卷创建期间由 CSI 驱动程序返回。 动态设置快照需要此字段。它指出了快照的卷源。

对于预制备快照,你(作为集群管理员)要按如下命令来创建 VolumeSnapshotContent 对象。

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotContent
metadata:
  name: new-snapshot-content-test
spec:
  deletionPolicy: Delete
  driver: hostpath.csi.k8s.io
  source:
    snapshotHandle: 7bdd0de3-aaeb-11e8-9aae-0242ac110002
  sourceVolumeMode: Filesystem
  volumeSnapshotRef:
    name: new-snapshot-test
    namespace: default

snapshotHandle 是存储后端创建卷的唯一标识符。对于预制备的快照,这个字段是必需的。 它指定此 VolumeSnapshotContent 表示的存储系统上的 CSI 快照 ID。

sourceVolumeMode 是创建快照的卷的模式。sourceVolumeMode 字段的值可以是 FilesystemBlock。如果没有指定源卷模式,Kubernetes 会将快照视为未知的源卷模式。

volumeSnapshotRef 字段是对相应的 VolumeSnapshot 的引用。 请注意,当 VolumeSnapshotContent 被创建为预配置快照时。 volumeSnapshotRef 中引用的 VolumeSnapshot 可能还不存在。

转换快照的卷模式

如果在你的集群上安装的 VolumeSnapshots API 支持 sourceVolumeMode 字段,则该 API 可以防止未经授权的用户转换卷的模式。

要检查你的集群是否具有此特性的能力,可以运行如下命令:

kubectl get crd volumesnapshotcontent -o yaml

如果你希望允许用户从现有的 VolumeSnapshot 创建 PersistentVolumeClaim, 但是使用与源卷不同的卷模式,则需要添加注解 snapshot.storage.kubernetes.io/allow-volume-mode-change: "true" 到对应 VolumeSnapshotVolumeSnapshotContent 中。

对于预制备的快照,spec.sourceVolumeMode 需要由集群管理员填充。

启用此特性的 VolumeSnapshotContent 资源示例如下所示:

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotContent
metadata:
  name: new-snapshot-content-test
  annotations:
    - snapshot.storage.kubernetes.io/allow-volume-mode-change: "true"
spec:
  deletionPolicy: Delete
  driver: hostpath.csi.k8s.io
  source:
    snapshotHandle: 7bdd0de3-aaeb-11e8-9aae-0242ac110002
  sourceVolumeMode: Filesystem
  volumeSnapshotRef:
    name: new-snapshot-test
    namespace: default

从快照制备卷

你可以制备一个新卷,该卷预填充了快照中的数据,在 PersistentVolumeClaim 对象中使用 dataSource 字段。

更多详细信息, 请参阅卷快照和从快照还原卷

3.6.8 - 卷快照类

本文档描述了 Kubernetes 中 VolumeSnapshotClass 的概念。建议熟悉 卷快照(Volume Snapshots)存储类(Storage Class)

介绍

就像 StorageClass 为管理员提供了一种在配置卷时描述存储“类”的方法, VolumeSnapshotClass 提供了一种在配置卷快照时描述存储“类”的方法。

VolumeSnapshotClass 资源

每个 VolumeSnapshotClass 都包含 driverdeletionPolicyparameters 字段, 在需要动态配置属于该类的 VolumeSnapshot 时使用。

VolumeSnapshotClass 对象的名称很重要,是用户可以请求特定类的方式。 管理员在首次创建 VolumeSnapshotClass 对象时设置类的名称和其他参数, 对象一旦创建就无法更新。

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
  name: csi-hostpath-snapclass
driver: hostpath.csi.k8s.io
deletionPolicy: Delete
parameters:

管理员可以为未请求任何特定类绑定的 VolumeSnapshots 指定默认的 VolumeSnapshotClass, 方法是设置注解 snapshot.storage.kubernetes.io/is-default-class: "true"

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
  name: csi-hostpath-snapclass
  annotations:
    snapshot.storage.kubernetes.io/is-default-class: "true"
driver: hostpath.csi.k8s.io
deletionPolicy: Delete
parameters:

驱动程序

卷快照类有一个驱动程序,用于确定配置 VolumeSnapshot 的 CSI 卷插件。 此字段必须指定。

删除策略

卷快照类具有 deletionPolicy 属性。 用户可以配置当所绑定的 VolumeSnapshot 对象将被删除时,如何处理 VolumeSnapshotContent 对象。 卷快照类的这个策略可以是 Retain 或者 Delete。这个策略字段必须指定。

如果删除策略是 Delete,那么底层的存储快照会和 VolumeSnapshotContent 对象 一起删除。如果删除策略是 Retain,那么底层快照和 VolumeSnapshotContent 对象都会被保留。

参数

卷快照类具有描述属于该卷快照类的卷快照的参数,可根据 driver 接受不同的参数。

3.6.9 - CSI 卷克隆

本文档介绍 Kubernetes 中克隆现有 CSI 卷的概念。阅读前建议先熟悉

介绍

CSI 卷克隆功能增加了通过在 dataSource 字段中指定存在的 PVC, 来表示用户想要克隆的 卷(Volume)

克隆(Clone),意思是为已有的 Kubernetes 卷创建副本,它可以像任何其它标准卷一样被使用。 唯一的区别就是配置后,后端设备将创建指定完全相同的副本,而不是创建一个“新的”空卷。

从 Kubernetes API 的角度看,克隆的实现只是在创建新的 PVC 时, 增加了指定一个现有 PVC 作为数据源的能力。源 PVC 必须是 bound 状态且可用的(不在使用中)。

用户在使用该功能时,需要注意以下事项:

  • 克隆支持(VolumePVCDataSource)仅适用于 CSI 驱动。
  • 克隆支持仅适用于 动态供应器。
  • CSI 驱动可能实现,也可能未实现卷克隆功能。
  • 仅当 PVC 与目标 PVC 存在于同一命名空间(源和目标 PVC 必须在相同的命名空间)时,才可以克隆 PVC。
  • 支持用一个不同存储类进行克隆。
    • 目标卷和源卷可以是相同的存储类,也可以不同。
    • 可以使用默认的存储类,也可以在 spec 中省略 storageClassName 字段。
  • 克隆只能在两个使用相同 VolumeMode 设置的卷中进行 (如果请求克隆一个块存储模式的卷,源卷必须也是块存储模式)。

制备

克隆卷与其他任何 PVC 一样配置,除了需要增加 dataSource 来引用同一命名空间中现有的 PVC。

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
    name: clone-of-pvc-1
    namespace: myns
spec:
  accessModes:
  - ReadWriteOnce
  storageClassName: cloning
  resources:
    requests:
      storage: 5Gi
  dataSource:
    kind: PersistentVolumeClaim
    name: pvc-1

结果是一个名称为 clone-of-pvc-1 的新 PVC 与指定的源 pvc-1 拥有相同的内容。

使用

一旦新的 PVC 可用,被克隆的 PVC 像其他 PVC 一样被使用。 可以预期的是,新创建的 PVC 是一个独立的对象。 可以独立使用、克隆、快照或删除它,而不需要考虑它的原始数据源 PVC。 这也意味着,源没有以任何方式链接到新创建的 PVC,它也可以被修改或删除,而不会影响到新创建的克隆。

3.6.10 - 存储容量

存储容量是有限的,并且会因为运行 Pod 的节点不同而变化: 网络存储可能并非所有节点都能够访问,或者对于某个节点存储是本地的。

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

本页面描述了 Kubernetes 如何跟踪存储容量以及调度程序如何为了余下的尚未挂载的卷使用该信息将 Pod 调度到能够访问到足够存储容量的节点上。 如果没有跟踪存储容量,调度程序可能会选择一个没有足够容量来提供卷的节点,并且需要多次调度重试。

准备开始

Kubernetes v1.30 包含了对存储容量跟踪的集群级 API 支持。 要使用它,你还必须使用支持容量跟踪的 CSI 驱动程序。请查阅你使用的 CSI 驱动程序的文档, 以了解此支持是否可用,如果可用,该如何使用它。如果你运行的不是 Kubernetes v1.30,请查看对应版本的 Kubernetes 文档。

API

这个特性有两个 API 扩展接口:

  • CSIStorageCapacity 对象:这些对象由 CSI 驱动程序在安装驱动程序的命名空间中产生。 每个对象都包含一个存储类的容量信息,并定义哪些节点可以访问该存储。
  • CSIDriverSpec.StorageCapacity 字段: 设置为 true 时,Kubernetes 调度程序将考虑使用 CSI 驱动程序的卷的存储容量。

调度

如果有以下情况,存储容量信息将会被 Kubernetes 调度程序使用:

  • Pod 使用的卷还没有被创建,
  • 卷使用引用了 CSI 驱动的 StorageClass, 并且使用了 WaitForFirstConsumer 卷绑定模式
  • 驱动程序的 CSIDriver 对象的 StorageCapacity 被设置为 true。

在这种情况下,调度程序仅考虑将 Pod 调度到有足够存储容量的节点上。这个检测非常简单, 仅将卷的大小与 CSIStorageCapacity 对象中列出的容量进行比较,并使用包含该节点的拓扑。

对于具有 Immediate 卷绑定模式的卷,存储驱动程序将决定在何处创建该卷,而不取决于将使用该卷的 Pod。 然后,调度程序将 Pod 调度到创建卷后可使用该卷的节点上。

对于 CSI 临时卷, 调度总是在不考虑存储容量的情况下进行。 这是基于这样的假设:该卷类型仅由节点本地的特殊 CSI 驱动程序使用,并且不需要大量资源。

重新调度

当为带有 WaitForFirstConsumer 的卷的 Pod 来选择节点时,该决定仍然是暂定的。 下一步是要求 CSI 存储驱动程序创建卷,并提示该卷在被选择的节点上可用。

因为 Kubernetes 可能会根据已经过时的存储容量信息来选择一个节点,因此可能无法真正创建卷。 然后就会重置节点选择,Kubernetes 调度器会再次尝试为 Pod 查找节点。

限制

存储容量跟踪增加了调度器第一次尝试即成功的机会,但是并不能保证这一点,因为调度器必须根据可能过期的信息来进行决策。 通常,与没有任何存储容量信息的调度相同的重试机制可以处理调度失败。

当 Pod 使用多个卷时,调度可能会永久失败:一个卷可能已经在拓扑段中创建,而该卷又没有足够的容量来创建另一个卷, 要想从中恢复,必须要进行手动干预,比如通过增加存储容量或者删除已经创建的卷。

接下来

3.6.11 - 特定于节点的卷数限制

此页面描述了各个云供应商可关联至一个节点的最大卷数。

谷歌、亚马逊和微软等云供应商通常对可以关联到节点的卷数量进行限制。 Kubernetes 需要尊重这些限制。否则,在节点上调度的 Pod 可能会卡住去等待卷的关联。

Kubernetes 的默认限制

The Kubernetes 调度器对关联于一个节点的卷数有默认限制:

云服务每节点最大卷数
Amazon Elastic Block Store (EBS)39
Google Persistent Disk16
Microsoft Azure Disk Storage16

自定义限制

你可以通过设置 KUBE_MAX_PD_VOLS 环境变量的值来设置这些限制,然后再启动调度器。 CSI 驱动程序可能具有不同的过程,关于如何自定义其限制请参阅相关文档。

如果设置的限制高于默认限制,请谨慎使用。请参阅云提供商的文档以确保节点可支持你设置的限制。

此限制应用于整个集群,所以它会影响所有节点。

动态卷限制

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

以下卷类型支持动态卷限制。

  • Amazon EBS
  • Google Persistent Disk
  • Azure Disk
  • CSI

对于由内建插件管理的卷,Kubernetes 会自动确定节点类型并确保节点上可关联的卷数目合规。例如:

  • Google Compute Engine环境中, 根据节点类型最多可以将 127 个卷关联到节点。

  • 对于 M5、C5、R5、T3 和 Z1D 类型实例的 Amazon EBS 磁盘,Kubernetes 仅允许 25 个卷关联到节点。 对于 ec2 上的其他实例类型 Amazon Elastic Compute Cloud (EC2), Kubernetes 允许 39 个卷关联至节点。

  • 在 Azure 环境中, 根据节点类型,最多 64 个磁盘可以关联至一个节点。 更多详细信息,请参阅 Azure 虚拟机的数量大小

  • 如果 CSI 存储驱动程序(使用 NodeGetInfo )为节点通告卷数上限,则 kube-scheduler 将遵守该限制值。 参考 CSI 规范 获取更多详细信息。

  • 对于由已迁移到 CSI 驱动程序的树内插件管理的卷,最大卷数将是 CSI 驱动程序报告的卷数。

3.6.12 - 卷健康监测

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

CSI 卷健康监测支持 CSI 驱动从底层的存储系统着手, 探测异常的卷状态,并以事件的形式上报到 PVCPod.

卷健康监测

Kubernetes 卷健康监测是 Kubernetes 容器存储接口(CSI)实现的一部分。 卷健康监测特性由两个组件实现:外部健康监测控制器和 kubelet

如果 CSI 驱动器通过控制器的方式支持卷健康监测特性,那么只要在 CSI 卷上监测到异常卷状态,就会在 PersistentVolumeClaim (PVC) 中上报一个事件。

外部健康监测控制器也会监测节点失效事件。 如果要启动节点失效监测功能,你可以设置标志 enable-node-watchertrue。 当外部健康监测器检测到节点失效事件,控制器会报送一个事件,该事件会在 PVC 上继续上报, 以表明使用此 PVC 的 Pod 正位于一个失效的节点上。

如果 CSI 驱动程序支持节点测的卷健康检测,那当在 CSI 卷上检测到异常卷时, 会在使用该 PVC 的每个 Pod 上触发一个事件。 此外,卷运行状况信息作为 Kubelet VolumeStats 指标公开。 添加了一个新的指标 kubelet_volume_stats_health_status_abnormal。 该指标包括两个标签:namespacepersistentvolumeclaim。 计数为 1 或 0。1 表示卷不正常,0 表示卷正常。更多信息请访问 KEP

接下来

参阅 CSI 驱动程序文档, 可以找出有哪些 CSI 驱动程序实现了此特性。

3.6.13 - Windows 存储

此页面提供特定于 Windows 操作系统的存储概述。

持久存储

Windows 有一个分层文件系统驱动程序用来挂载容器层和创建基于 NTFS 的文件系统拷贝。 容器中的所有文件路径仅在该容器的上下文中解析。

  • 使用 Docker 时,卷挂载只能是容器中的目录,而不能是单个文件。此限制不适用于 containerd。
  • 卷挂载不能将文件或目录映射回宿主文件系统。
  • 不支持只读文件系统,因为 Windows 注册表和 SAM 数据库始终需要写访问权限。不过,Windows 支持只读的卷。
  • 不支持卷的用户掩码和访问许可,因为宿主与容器之间并不共享 SAM,二者之间不存在映射关系。 所有访问许可都是在容器上下文中解析的。

因此,Windows 节点不支持以下存储功能:

  • 卷子路径挂载:只能在 Windows 容器上挂载整个卷
  • Secret 的子路径挂载
  • 宿主挂载映射
  • 只读的根文件系统(映射的卷仍然支持 readOnly
  • 块设备映射
  • 内存作为存储介质(例如 emptyDir.medium 设置为 Memory
  • 类似 UID/GID、各用户不同的 Linux 文件系统访问许可等文件系统特性
  • 使用 DefaultMode 设置 Secret 权限 (因为该特性依赖 UID/GID)
  • 基于 NFS 的存储和卷支持
  • 扩展已挂载卷(resizefs)

使用 Kubernetes , 对数据持久性和 Pod 卷共享有需求的复杂应用也可以部署到 Kubernetes 上。 管理与特定存储后端或协议相关的持久卷时,相关的操作包括:对卷的制备(Provisioning)、 去配(De-provisioning)和调整大小,将卷挂接到 Kubernetes 节点或从节点上解除挂接, 将卷挂载到需要持久数据的 Pod 中的某容器上或从容器上卸载。

卷管理组件作为 Kubernetes 卷插件发布。 Windows 支持以下类型的 Kubernetes 卷插件:

树内(In-Tree)卷插件

以下树内(In-Tree)插件支持 Windows 节点上的持久存储:

3.7 - 配置

Kubernetes 为配置 Pods 提供的资源。

3.7.1 - 配置最佳实践

本文档重点介绍并整合了整个用户指南、入门文档和示例中介绍的配置最佳实践。

这是一份不断改进的文件。 如果你认为某些内容缺失但可能对其他人有用,请不要犹豫,提交 Issue 或提交 PR。

一般配置提示

  • 定义配置时,请指定最新的稳定 API 版本。
  • 在推送到集群之前,配置文件应存储在版本控制中。 这允许你在必要时快速回滚配置更改。 它还有助于集群重新创建和恢复。
  • 使用 YAML 而不是 JSON 编写配置文件。虽然这些格式几乎可以在所有场景中互换使用,但 YAML 往往更加用户友好。
  • 只要有意义,就将相关对象分组到一个文件中。一个文件通常比几个文件更容易管理。 请参阅 guestbook-all-in-one.yaml 文件作为此语法的示例。
  • 另请注意,可以在目录上调用许多 kubectl 命令。 例如,你可以在配置文件的目录中调用 kubectl apply
  • 除非必要,否则不指定默认值:简单的最小配置会降低错误的可能性。
  • 将对象描述放在注释中,以便更好地进行内省。

“独立的“ Pod 与 ReplicaSet、Deployment 和 Job

  • 如果可能,不要使用独立的 Pod(即,未绑定到 ReplicaSetDeployment 的 Pod)。 如果节点发生故障,将不会重新调度这些独立的 Pod。

    Deployment 既可以创建一个 ReplicaSet 来确保预期个数的 Pod 始终可用,也可以指定替换 Pod 的策略(例如 RollingUpdate)。 除了一些显式的 restartPolicy: Never 场景外,Deployment 通常比直接创建 Pod 要好得多。 Job 也可能是合适的选择。

服务

  • 在创建相应的后端工作负载(Deployment 或 ReplicaSet),以及在需要访问它的任何工作负载之前创建 服务。 当 Kubernetes 启动容器时,它提供指向启动容器时正在运行的所有服务的环境变量。 例如,如果存在名为 foo 的服务,则所有容器将在其初始环境中获得以下变量。

    FOO_SERVICE_HOST=<the host the Service is running on>
    FOO_SERVICE_PORT=<the port the Service is running on>
    

    这确实意味着在顺序上的要求 - 必须在 Pod 本身被创建之前创建 Pod 想要访问的任何 Service, 否则将环境变量不会生效。DNS 没有此限制。

  • 一个可选(尽管强烈推荐)的集群插件 是 DNS 服务器。DNS 服务器为新的 Services 监视 Kubernetes API,并为每个创建一组 DNS 记录。 如果在整个集群中启用了 DNS,则所有 Pod 应该能够自动对 Services 进行名称解析。
  • 不要为 Pod 指定 hostPort,除非非常有必要这样做。 当你为 Pod 绑定了 hostPort,那么能够运行该 Pod 的节点就有限了,因为每个 <hostIP, hostPort, protocol> 组合必须是唯一的。 如果你没有明确指定 hostIPprotocol, Kubernetes 将使用 0.0.0.0 作为默认的 hostIP,使用 TCP 作为默认的 protocol

    如果你只需要访问端口以进行调试,则可以使用 apiserver proxykubectl port-forward

    如果你明确需要在节点上公开 Pod 的端口,请在使用 hostPort 之前考虑使用 NodePort 服务。

  • 避免使用 hostNetwork,原因与 hostPort 相同。
  • 当你不需要 kube-proxy 负载均衡时, 使用无头服务ClusterIP 被设置为 None)进行服务发现。

使用标签

  • 定义并使用标签来识别应用程序 或 Deployment 的语义属性,例如 { app.kubernetes.io/name: MyApp, tier: frontend, phase: test, deployment: v3 }。 你可以使用这些标签为其他资源选择合适的 Pod; 例如,一个选择所有 tier: frontend Pod 的服务,或者 app.kubernetes.io/name: MyApp 的所有 phase: test 组件。 有关此方法的示例,请参阅 guestbook

    通过从选择器中省略特定发行版的标签,可以使服务跨越多个 Deployment。 当你需要不停机的情况下更新正在运行的服务,可以使用 Deployment

    Deployment 描述了对象的期望状态,并且如果对该规约的更改被成功应用,则 Deployment 控制器以受控速率将实际状态改变为期望状态。

  • 你可以操纵标签进行调试。 由于 Kubernetes 控制器(例如 ReplicaSet)和服务使用选择器标签来匹配 Pod, 从 Pod 中删除相关标签将阻止其被控制器考虑或由服务提供服务流量。 如果删除现有 Pod 的标签,其控制器将创建一个新的 Pod 来取代它。 这是在“隔离“环境中调试先前“活跃“的 Pod 的有用方法。 要以交互方式删除或添加标签,请使用 kubectl label

使用 kubectl

  • 使用 kubectl apply -f <目录>。 它在 <目录> 中的所有 .yaml.yml.json 文件中查找 Kubernetes 配置,并将其传递给 apply

3.7.2 - ConfigMaps

ConfigMap 是一种 API 对象,用来将非机密性的数据保存到键值对中。使用时, Pod 可以将其用作环境变量、命令行参数或者存储卷中的配置文件。

ConfigMap 将你的环境配置信息和容器镜像解耦,便于应用配置的修改。

动机

使用 ConfigMap 来将你的配置数据和应用程序代码分开。

比如,假设你正在开发一个应用,它可以在你自己的电脑上(用于开发)和在云上 (用于实际流量)运行。 你的代码里有一段是用于查看环境变量 DATABASE_HOST,在本地运行时, 你将这个变量设置为 localhost,在云上,你将其设置为引用 Kubernetes 集群中的 公开数据库组件的 服务

这让你可以获取在云中运行的容器镜像,并且如果有需要的话,在本地调试完全相同的代码。

ConfigMap 对象

ConfigMap 是一个让你可以存储其他对象所需要使用的配置的 API 对象。 和其他 Kubernetes 对象都有一个 spec 不同的是,ConfigMap 使用 databinaryData 字段。这些字段能够接收键-值对作为其取值。databinaryData 字段都是可选的。data 字段设计用来保存 UTF-8 字符串,而 binaryData 则被设计用来保存二进制数据作为 base64 编码的字串。

ConfigMap 的名字必须是一个合法的 DNS 子域名

databinaryData 字段下面的每个键的名称都必须由字母数字字符或者 -_. 组成。在 data 下保存的键名不可以与在 binaryData 下出现的键名有重叠。

从 v1.19 开始,你可以添加一个 immutable 字段到 ConfigMap 定义中, 创建不可变更的 ConfigMap

ConfigMaps 和 Pods

你可以写一个引用 ConfigMap 的 Pod 的 spec,并根据 ConfigMap 中的数据在该 Pod 中配置容器。这个 Pod 和 ConfigMap 必须要在同一个 名字空间 中。

这是一个 ConfigMap 的示例,它的一些键只有一个值,其他键的值看起来像是 配置的片段格式。

apiVersion: v1
kind: ConfigMap
metadata:
  name: game-demo
data:
  # 类属性键;每一个键都映射到一个简单的值
  player_initial_lives: "3"
  ui_properties_file_name: "user-interface.properties"

  # 类文件键
  game.properties: |
    enemy.types=aliens,monsters
    player.maximum-lives=5    
  user-interface.properties: |
    color.good=purple
    color.bad=yellow
    allow.textmode=true    

你可以使用四种方式来使用 ConfigMap 配置 Pod 中的容器:

  1. 在容器命令和参数内
  2. 容器的环境变量
  3. 在只读卷里面添加一个文件,让应用来读取
  4. 编写代码在 Pod 中运行,使用 Kubernetes API 来读取 ConfigMap

这些不同的方法适用于不同的数据使用方式。 对前三个方法,kubelet 使用 ConfigMap 中的数据在 Pod 中启动容器。

第四种方法意味着你必须编写代码才能读取 ConfigMap 和它的数据。然而, 由于你是直接使用 Kubernetes API,因此只要 ConfigMap 发生更改, 你的应用就能够通过订阅来获取更新,并且在这样的情况发生的时候做出反应。 通过直接进入 Kubernetes API,这个技术也可以让你能够获取到不同的名字空间里的 ConfigMap。

下面是一个 Pod 的示例,它通过使用 game-demo 中的值来配置一个 Pod:

apiVersion: v1
kind: Pod
metadata:
  name: configmap-demo-pod
spec:
  containers:
    - name: demo
      image: alpine
      command: ["sleep", "3600"]
      env:
        # 定义环境变量
        - name: PLAYER_INITIAL_LIVES # 请注意这里和 ConfigMap 中的键名是不一样的
          valueFrom:
            configMapKeyRef:
              name: game-demo           # 这个值来自 ConfigMap
              key: player_initial_lives # 需要取值的键
        - name: UI_PROPERTIES_FILE_NAME
          valueFrom:
            configMapKeyRef:
              name: game-demo
              key: ui_properties_file_name
      volumeMounts:
      - name: config
        mountPath: "/config"
        readOnly: true
  volumes:
  # 你可以在 Pod 级别设置卷,然后将其挂载到 Pod 内的容器中
  - name: config
    configMap:
      # 提供你想要挂载的 ConfigMap 的名字
      name: game-demo
      # 来自 ConfigMap 的一组键,将被创建为文件
      items:
      - key: "game.properties"
        path: "game.properties"
      - key: "user-interface.properties"
        path: "user-interface.properties"

ConfigMap 不会区分单行属性值和多行类似文件的值,重要的是 Pods 和其他对象如何使用这些值。

上面的例子定义了一个卷并将它作为 /config 文件夹挂载到 demo 容器内, 创建两个文件,/config/game.properties/config/user-interface.properties, 尽管 ConfigMap 中包含了四个键。 这是因为 Pod 定义中在 volumes 节指定了一个 items 数组。 如果你完全忽略 items 数组,则 ConfigMap 中的每个键都会变成一个与该键同名的文件, 因此你会得到四个文件。

使用 ConfigMap

ConfigMap 可以作为数据卷挂载。ConfigMap 也可被系统的其他组件使用, 而不一定直接暴露给 Pod。例如,ConfigMap 可以保存系统中其他组件要使用的配置数据。

ConfigMap 最常见的用法是为同一命名空间里某 Pod 中运行的容器执行配置。 你也可以单独使用 ConfigMap。

比如,你可能会遇到基于 ConfigMap 来调整其行为的 插件 或者 operator

在 Pod 中将 ConfigMap 当做文件使用

要在一个 Pod 的存储卷中使用 ConfigMap:

  1. 创建一个 ConfigMap 对象或者使用现有的 ConfigMap 对象。多个 Pod 可以引用同一个 ConfigMap。
  2. 修改 Pod 定义,在 spec.volumes[] 下添加一个卷。 为该卷设置任意名称,之后将 spec.volumes[].configMap.name 字段设置为对你的 ConfigMap 对象的引用。
  3. 为每个需要该 ConfigMap 的容器添加一个 .spec.containers[].volumeMounts[]。 设置 .spec.containers[].volumeMounts[].readOnly=true 并将 .spec.containers[].volumeMounts[].mountPath 设置为一个未使用的目录名, ConfigMap 的内容将出现在该目录中。
  4. 更改你的镜像或者命令行,以便程序能够从该目录中查找文件。ConfigMap 中的每个 data 键会变成 mountPath 下面的一个文件名。

下面是一个将 ConfigMap 以卷的形式进行挂载的 Pod 示例:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true
  volumes:
  - name: foo
    configMap:
      name: myconfigmap

你希望使用的每个 ConfigMap 都需要在 spec.volumes 中被引用到。

如果 Pod 中有多个容器,则每个容器都需要自己的 volumeMounts 块,但针对每个 ConfigMap,你只需要设置一个 spec.volumes 块。

被挂载的 ConfigMap 内容会被自动更新

当卷中使用的 ConfigMap 被更新时,所投射的键最终也会被更新。 kubelet 组件会在每次周期性同步时检查所挂载的 ConfigMap 是否为最新。 不过,kubelet 使用的是其本地的高速缓存来获得 ConfigMap 的当前值。 高速缓存的类型可以通过 KubeletConfiguration 结构. 的 configMapAndSecretChangeDetectionStrategy 字段来配置。

ConfigMap 既可以通过 watch 操作实现内容传播(默认形式),也可实现基于 TTL 的缓存,还可以直接经过所有请求重定向到 API 服务器。 因此,从 ConfigMap 被更新的那一刻算起,到新的主键被投射到 Pod 中去, 这一时间跨度可能与 kubelet 的同步周期加上高速缓存的传播延迟相等。 这里的传播延迟取决于所选的高速缓存类型 (分别对应 watch 操作的传播延迟、高速缓存的 TTL 时长或者 0)。

以环境变量方式使用的 ConfigMap 数据不会被自动更新。 更新这些数据需要重新启动 Pod。

使用 Configmap 作为环境变量

使用 Configmap 在 Pod 中设置环境变量

  1. 对于 Pod 规约中的每个容器,为要使用的每个 ConfigMap 键添加一个环境变量到 env[].valueFrom.configMapKeyRef 字段。
  2. 修改你的镜像和/或命令行,以便程序查找指定环境变量中的值。

下面是一个将 ConfigMap 定义为 Pod 环境变量的示例:

apiVersion: v1
kind: Pod
metadata:
  name: env-configmap
spec:
  containers:
  - name: envars-test-container
    image: nginx
    env:
    - name: CONFIGMAP_USERNAME
      valueFrom:
        configMapKeyRef:
          name: myconfigmap
          key: username

需要注意的是,Pod 中环境变量名称允许的字符范围是有限的。 如果某些变量名称不满足这些规则,则即使 Pod 可以被启动,你的容器也无法访问这些环境变量。

不可变更的 ConfigMap

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

Kubernetes 特性 Immutable Secret 和 ConfigMaps 提供了一种将各个 Secret 和 ConfigMap 设置为不可变更的选项。对于大量使用 ConfigMap 的集群 (至少有数万个各不相同的 ConfigMap 给 Pod 挂载)而言,禁止更改 ConfigMap 的数据有以下好处:

  • 保护应用,使之免受意外(不想要的)更新所带来的负面影响。
  • 通过大幅降低对 kube-apiserver 的压力提升集群性能, 这是因为系统会关闭对已标记为不可变更的 ConfigMap 的监视操作。

你可以通过将 immutable 字段设置为 true 创建不可变更的 ConfigMap。 例如:

apiVersion: v1
kind: ConfigMap
metadata:
  ...
data:
  ...
immutable: true

一旦某 ConfigMap 被标记为不可变更,则 无法 逆转这一变化,,也无法更改 databinaryData 字段的内容。你只能删除并重建 ConfigMap。 因为现有的 Pod 会维护一个已被删除的 ConfigMap 的挂载点,建议重新创建这些 Pods。

接下来

3.7.3 - Secret

Secret 是一种包含少量敏感信息例如密码、令牌或密钥的对象。 这样的信息可能会被放在 Pod 规约中或者镜像中。 使用 Secret 意味着你不需要在应用程序代码中包含机密数据。

由于创建 Secret 可以独立于使用它们的 Pod, 因此在创建、查看和编辑 Pod 的工作流程中暴露 Secret(及其数据)的风险较小。 Kubernetes 和在集群中运行的应用程序也可以对 Secret 采取额外的预防措施, 例如避免将敏感数据写入非易失性存储。

Secret 类似于 ConfigMap 但专门用于保存机密数据。

参见 Secret 的信息安全了解详情。

Secret 的使用

你可以将 Secret 用于以下场景:

Kubernetes 控制面也使用 Secret; 例如,引导令牌 Secret 是一种帮助自动化节点注册的机制。

使用场景:在 Secret 卷中带句点的文件

通过定义以句点(.)开头的主键,你可以“隐藏”你的数据。 这些主键代表的是以句点开头的文件或“隐藏”文件。 例如,当以下 Secret 被挂载到 secret-volume 卷上时,该卷中会包含一个名为 .secret-file 的文件,并且容器 dotfile-test-container 中此文件位于路径 /etc/secret-volume/.secret-file 处。

apiVersion: v1
kind: Secret
metadata:
  name: dotfile-secret
data:
  .secret-file: dmFsdWUtMg0KDQo=
---
apiVersion: v1
kind: Pod
metadata:
  name: secret-dotfiles-pod
spec:
  volumes:
    - name: secret-volume
      secret:
        secretName: dotfile-secret
  containers:
    - name: dotfile-test-container
      image: registry.k8s.io/busybox
      command:
        - ls
        - "-l"
        - "/etc/secret-volume"
      volumeMounts:
        - name: secret-volume
          readOnly: true
          mountPath: "/etc/secret-volume"

使用场景:仅对 Pod 中一个容器可见的 Secret

考虑一个需要处理 HTTP 请求,执行某些复杂的业务逻辑,之后使用 HMAC 来对某些消息进行签名的程序。因为这一程序的应用逻辑很复杂, 其中可能包含未被注意到的远程服务器文件读取漏洞, 这种漏洞可能会把私钥暴露给攻击者。

这一程序可以分隔成两个容器中的两个进程:前端容器要处理用户交互和业务逻辑, 但无法看到私钥;签名容器可以看到私钥,并对来自前端的简单签名请求作出响应 (例如,通过本地主机网络)。

采用这种划分的方法,攻击者现在必须欺骗应用服务器来做一些其他操作, 而这些操作可能要比读取一个文件要复杂很多。

Secret 的替代方案

除了使用 Secret 来保护机密数据,你也可以选择一些替代方案。

下面是一些选项:

  • 如果你的云原生组件需要执行身份认证来访问你所知道的、在同一 Kubernetes 集群中运行的另一个应用, 你可以使用 ServiceAccount 及其令牌来标识你的客户端身份。
  • 你可以运行的第三方工具也有很多,这些工具可以运行在集群内或集群外,提供机密数据管理。 例如,这一工具可能是 Pod 通过 HTTPS 访问的一个服务,该服务在客户端能够正确地通过身份认证 (例如,通过 ServiceAccount 令牌)时,提供机密数据内容。
  • 就身份认证而言,你可以为 X.509 证书实现一个定制的签名者,并使用 CertificateSigningRequest 来让该签名者为需要证书的 Pod 发放证书。
  • 你可以使用一个设备插件 来将节点本地的加密硬件暴露给特定的 Pod。例如,你可以将可信任的 Pod 调度到提供可信平台模块(Trusted Platform Module,TPM)的节点上。 这类节点是另行配置的。

你还可以将如上选项的两种或多种进行组合,包括直接使用 Secret 对象本身也是一种选项。

例如:实现(或部署)一个 operator, 从外部服务取回生命期很短的会话令牌,之后基于这些生命期很短的会话令牌来创建 Secret。 运行在集群中的 Pod 可以使用这些会话令牌,而 Operator 则确保这些令牌是合法的。 这种责权分离意味着你可以运行那些不了解会话令牌如何发放与刷新的确切机制的 Pod。

Secret 的类型

创建 Secret 时,你可以使用 Secret 资源的 type 字段,或者与其等价的 kubectl 命令行参数(如果有的话)为其设置类型。 Secret 类型有助于对 Secret 数据进行编程处理。

Kubernetes 提供若干种内置的类型,用于一些常见的使用场景。 针对这些类型,Kubernetes 所执行的合法性检查操作以及对其所实施的限制各不相同。

内置类型用法
Opaque用户定义的任意数据
kubernetes.io/service-account-token服务账号令牌
kubernetes.io/dockercfg~/.dockercfg 文件的序列化形式
kubernetes.io/dockerconfigjson~/.docker/config.json 文件的序列化形式
kubernetes.io/basic-auth用于基本身份认证的凭据
kubernetes.io/ssh-auth用于 SSH 身份认证的凭据
kubernetes.io/tls用于 TLS 客户端或者服务器端的数据
bootstrap.kubernetes.io/token启动引导令牌数据

通过为 Secret 对象的 type 字段设置一个非空的字符串值,你也可以定义并使用自己 Secret 类型(如果 type 值为空字符串,则被视为 Opaque 类型)。

Kubernetes 并不对类型的名称作任何限制。不过,如果你要使用内置类型之一, 则你必须满足为该类型所定义的所有要求。

如果你要定义一种公开使用的 Secret 类型,请遵守 Secret 类型的约定和结构, 在类型名前面添加域名,并用 / 隔开。 例如:cloud-hosting.example.net/cloud-api-credentials

Opaque Secret

当你未在 Secret 清单中显式指定类型时,默认的 Secret 类型是 Opaque。 当你使用 kubectl 来创建一个 Secret 时,你必须使用 generic 子命令来标明要创建的是一个 Opaque 类型的 Secret。 例如,下面的命令会创建一个空的 Opaque 类型的 Secret:

kubectl create secret generic empty-secret
kubectl get secret empty-secret

输出类似于:

NAME           TYPE     DATA   AGE
empty-secret   Opaque   0      2m6s

DATA 列显示 Secret 中保存的数据条目个数。 在这个例子中,0 意味着你刚刚创建了一个空的 Secret。

ServiceAccount 令牌 Secret

类型为 kubernetes.io/service-account-token 的 Secret 用来存放标识某 ServiceAccount 的令牌凭据。 这是为 Pod 提供长期有效 ServiceAccount 凭据的传统机制。

在 Kubernetes v1.22 及更高版本中,推荐的方法是通过使用 TokenRequest API 来获取短期自动轮换的 ServiceAccount 令牌。你可以使用以下方法获取这些短期令牌:

  • 直接调用 TokenRequest API,或者使用像 kubectl 这样的 API 客户端。 例如,你可以使用 kubectl create token 命令。
  • 在 Pod 清单中请求使用投射卷挂载的令牌。 Kubernetes 会创建令牌并将其挂载到 Pod 中。 当挂载令牌的 Pod 被删除时,此令牌会自动失效。 更多细节参阅启动使用服务账号令牌投射的 Pod

使用这种 Secret 类型时,你需要确保对象的注解 kubernetes.io/service-account-name 被设置为某个已有的 ServiceAccount 名称。 如果你同时创建 ServiceAccount 和 Secret 对象,应该先创建 ServiceAccount 对象。

当 Secret 对象被创建之后,某个 Kubernetes 控制器会填写 Secret 的其它字段,例如 kubernetes.io/service-account.uid 注解和 data 字段中的 token 键值(该键包含一个身份认证令牌)。

下面的配置实例声明了一个 ServiceAccount 令牌 Secret:

apiVersion: v1
kind: Secret
metadata:
  name: secret-sa-sample
  annotations:
    kubernetes.io/service-account.name: "sa-name"
type: kubernetes.io/service-account-token
data:
  extra: YmFyCg==

创建了 Secret 之后,等待 Kubernetes 在 data 字段中填充 token 主键。

参考 ServiceAccount 文档了解 ServiceAccount 的工作原理。你也可以查看 Pod 资源中的 automountServiceAccountTokenserviceAccountName 字段文档, 进一步了解从 Pod 中引用 ServiceAccount 凭据。

Docker 配置 Secret

如果你要创建 Secret 用来存放用于访问容器镜像仓库的凭据,则必须选用以下 type 值之一来创建 Secret:

  • kubernetes.io/dockercfg:存放 ~/.dockercfg 文件的序列化形式,它是配置 Docker 命令行的一种老旧形式。Secret 的 data 字段包含名为 .dockercfg 的主键, 其值是用 base64 编码的某 ~/.dockercfg 文件的内容。
  • kubernetes.io/dockerconfigjson:存放 JSON 数据的序列化形式, 该 JSON 也遵从 ~/.docker/config.json 文件的格式规则,而后者是 ~/.dockercfg 的新版本格式。使用此 Secret 类型时,Secret 对象的 data 字段必须包含 .dockerconfigjson 键,其键值为 base64 编码的字符串包含 ~/.docker/config.json 文件的内容。

下面是一个 kubernetes.io/dockercfg 类型 Secret 的示例:

apiVersion: v1
kind: Secret
metadata:
  name: secret-dockercfg
type: kubernetes.io/dockercfg
data:
  .dockercfg: |
    eyJhdXRocyI6eyJodHRwczovL2V4YW1wbGUvdjEvIjp7ImF1dGgiOiJvcGVuc2VzYW1lIn19fQo=    

当你使用清单文件通过 Docker 配置来创建 Secret 时,API 服务器会检查 data 字段中是否存在所期望的主键, 并且验证其中所提供的键值是否是合法的 JSON 数据。 不过,API 服务器不会检查 JSON 数据本身是否是一个合法的 Docker 配置文件内容。

你还可以使用 kubectl 创建一个 Secret 来访问容器仓库时, 当你没有 Docker 配置文件时你可以这样做:

kubectl create secret docker-registry secret-tiger-docker \
  --docker-email=tiger@acme.example \
  --docker-username=tiger \
  --docker-password=pass1234 \
  --docker-server=my-registry.example:5000

此命令创建一个类型为 kubernetes.io/dockerconfigjson 的 Secret。

从这个新的 Secret 中获取 .data.dockerconfigjson 字段并执行数据解码:

kubectl get secret secret-tiger-docker -o jsonpath='{.data.*}' | base64 -d

输出等价于以下 JSON 文档(这也是一个有效的 Docker 配置文件):

{
  "auths": {
    "my-registry.example:5000": {
      "username": "tiger",
      "password": "pass1234",
      "email": "tiger@acme.example",
      "auth": "dGlnZXI6cGFzczEyMzQ="
    }
  }
}

基本身份认证 Secret

kubernetes.io/basic-auth 类型用来存放用于基本身份认证所需的凭据信息。 使用这种 Secret 类型时,Secret 的 data 字段必须包含以下两个键之一:

  • username: 用于身份认证的用户名;
  • password: 用于身份认证的密码或令牌。

以上两个键的键值都是 base64 编码的字符串。 当然你也可以在 Secret 清单中的使用 stringData 字段来提供明文形式的内容。

以下清单是基本身份验证 Secret 的示例:

apiVersion: v1
kind: Secret
metadata:
  name: secret-basic-auth
type: kubernetes.io/basic-auth
stringData:
  username: admin      # kubernetes.io/basic-auth 类型的必需字段
  password: t0p-Secret # kubernetes.io/basic-auth 类型的必需字段

提供基本身份认证类型的 Secret 仅仅是出于方便性考虑。 你也可以使用 Opaque 类型来保存用于基本身份认证的凭据。 不过,使用预定义的、公开的 Secret 类型(kubernetes.io/basic-auth) 有助于帮助其他用户理解 Secret 的目的,并且对其中存在的主键形成一种约定。 API 服务器会检查 Secret 配置中是否提供了所需要的主键。

SSH 身份认证 Secret

Kubernetes 所提供的内置类型 kubernetes.io/ssh-auth 用来存放 SSH 身份认证中所需要的凭据。 使用这种 Secret 类型时,你就必须在其 data (或 stringData) 字段中提供一个 ssh-privatekey 键值对,作为要使用的 SSH 凭据。

下面的清单是一个 SSH 公钥/私钥身份认证的 Secret 示例:

apiVersion: v1
kind: Secret
metadata:
  name: secret-ssh-auth
type: kubernetes.io/ssh-auth
data:
  # 此例中的实际数据被截断
  ssh-privatekey: |
     MIIEpQIBAAKCAQEAulqb/Y ...     

提供 SSH 身份认证类型的 Secret 仅仅是出于方便性考虑。 你可以使用 Opaque 类型来保存用于 SSH 身份认证的凭据。 不过,使用预定义的、公开的 Secret 类型(kubernetes.io/tls) 有助于其他人理解你的 Secret 的用途,也可以就其中包含的主键名形成约定。 Kubernetes API 会验证这种类型的 Secret 中是否设定了所需的主键。

TLS Secret

kubernetes.io/tls Secret 类型用来存放 TLS 场合通常要使用的证书及其相关密钥。

TLS Secret 的一种典型用法是为 Ingress 资源配置传输过程中的数据加密,不过也可以用于其他资源或者直接在负载中使用。 当使用此类型的 Secret 时,Secret 配置中的 data (或 stringData)字段必须包含 tls.keytls.crt 主键,尽管 API 服务器实际上并不会对每个键的取值作进一步的合法性检查。

作为使用 stringData 的替代方法,你可以使用 data 字段来指定 base64 编码的证书和私钥。 有关详细信息,请参阅 Secret 名称和数据的限制

下面的 YAML 包含一个 TLS Secret 的配置示例:

apiVersion: v1
kind: Secret
metadata:
  name: secret-tls
type: kubernetes.io/tls
data:
  # 值为 base64 编码,这样会掩盖它们,但不会提供任何有用的机密性级别
  tls.crt: |
    LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNVakNDQWJzQ0FnMytNQTBHQ1NxR1NJYjNE
    UUVCQlFVQU1JR2JNUXN3Q1FZRFZRUUdFd0pLVURFT01Bd0cKQTFVRUNCTUZWRzlyZVc4eEVEQU9C
    Z05WQkFjVEIwTm9kVzh0YTNVeEVUQVBCZ05WQkFvVENFWnlZVzVyTkVSRQpNUmd3RmdZRFZRUUxF
    dzlYWldKRFpYSjBJRk4xY0hCdmNuUXhHREFXQmdOVkJBTVREMFp5WVc1ck5FUkVJRmRsCllpQkRR
    VEVqTUNFR0NTcUdTSWIzRFFFSkFSWVVjM1Z3Y0c5eWRFQm1jbUZ1YXpSa1pDNWpiMjB3SGhjTk1U
    TXcKTVRFeE1EUTFNVE01V2hjTk1UZ3dNVEV3TURRMU1UTTVXakJMTVFzd0NRWURWUVFHREFKS1VE
    RVBNQTBHQTFVRQpDQXdHWEZSdmEzbHZNUkV3RHdZRFZRUUtEQWhHY21GdWF6UkVSREVZTUJZR0Ex
    VUVBd3dQZDNkM0xtVjRZVzF3CmJHVXVZMjl0TUlHYU1BMEdDU3FHU0liM0RRRUJBUVVBQTRHSUFE
    Q0JoQUo5WThFaUhmeHhNL25PbjJTbkkxWHgKRHdPdEJEVDFKRjBReTliMVlKanV2YjdjaTEwZjVN
    Vm1UQllqMUZTVWZNOU1vejJDVVFZdW4yRFljV29IcFA4ZQpqSG1BUFVrNVd5cDJRN1ArMjh1bklI
    QkphVGZlQ09PekZSUFY2MEdTWWUzNmFScG04L3dVVm16eGFLOGtCOWVaCmhPN3F1TjdtSWQxL2pW
    cTNKODhDQXdFQUFUQU5CZ2txaGtpRzl3MEJBUVVGQUFPQmdRQU1meTQzeE15OHh3QTUKVjF2T2NS
    OEtyNWNaSXdtbFhCUU8xeFEzazlxSGtyNFlUY1JxTVQ5WjVKTm1rWHYxK2VSaGcwTi9WMW5NUTRZ
    RgpnWXcxbnlESnBnOTduZUV4VzQyeXVlMFlHSDYyV1hYUUhyOVNVREgrRlowVnQvRGZsdklVTWRj
    UUFEZjM4aU9zCjlQbG1kb3YrcE0vNCs5a1h5aDhSUEkzZXZ6OS9NQT09Ci0tLS0tRU5EIENFUlRJ
    RklDQVRFLS0tLS0K    
  # 在这个例子中,密钥数据不是真正的 PEM 编码的私钥
  tls.key: |
    RXhhbXBsZSBkYXRhIGZvciB0aGUgVExTIGNydCBmaWVsZA==    

提供 TLS 类型的 Secret 仅仅是出于方便性考虑。 你可以创建 Opaque 类型的 Secret 来保存用于 TLS 身份认证的凭据。 不过,使用已定义和公开的 Secret 类型(kubernetes.io/tls)有助于确保你自己项目中的 Secret 格式的一致性。 API 服务器会验证这种类型的 Secret 是否设定了所需的主键。

要使用 kubectl 创建 TLS Secret,你可以使用 tls 子命令:

kubectl create secret tls my-tls-secret \
  --cert=path/to/cert/file \
  --key=path/to/key/file

公钥/私钥对必须事先存在,--cert 的公钥证书必须采用 .PEM 编码, 并且必须与 --key 的给定私钥匹配。

启动引导令牌 Secret

bootstrap.kubernetes.io/token Secret 类型针对的是节点启动引导过程所用的令牌。 其中包含用来为周知的 ConfigMap 签名的令牌。

启动引导令牌 Secret 通常创建于 kube-system 名字空间内,并以 bootstrap-token-<令牌 ID> 的形式命名; 其中 <令牌 ID> 是一个由 6 个字符组成的字符串,用作令牌的标识。

以 Kubernetes 清单文件的形式,某启动引导令牌 Secret 可能看起来像下面这样:

apiVersion: v1
kind: Secret
metadata:
  name: bootstrap-token-5emitj
  namespace: kube-system
type: bootstrap.kubernetes.io/token
data:
  auth-extra-groups: c3lzdGVtOmJvb3RzdHJhcHBlcnM6a3ViZWFkbTpkZWZhdWx0LW5vZGUtdG9rZW4=
  expiration: MjAyMC0wOS0xM1QwNDozOToxMFo=
  token-id: NWVtaXRq
  token-secret: a3E0Z2lodnN6emduMXAwcg==
  usage-bootstrap-authentication: dHJ1ZQ==
  usage-bootstrap-signing: dHJ1ZQ==

启动引导令牌类型的 Secret 会在 data 字段中包含如下主键:

  • token-id:由 6 个随机字符组成的字符串,作为令牌的标识符。必需。
  • token-secret:由 16 个随机字符组成的字符串,包含实际的令牌机密。必需。
  • description:供用户阅读的字符串,描述令牌的用途。可选。
  • expiration:一个使用 RFC3339 来编码的 UTC 绝对时间,给出令牌要过期的时间。可选。
  • usage-bootstrap-<usage>:布尔类型的标志,用来标明启动引导令牌的其他用途。
  • auth-extra-groups:用逗号分隔的组名列表,身份认证时除被认证为 system:bootstrappers 组之外,还会被添加到所列的用户组中。

你也可以在 Secret 的 stringData 字段中提供值,而无需对其进行 base64 编码:

apiVersion: v1
kind: Secret
metadata:
  # 注意 Secret 的命名方式
  name: bootstrap-token-5emitj
  # 启动引导令牌 Secret 通常位于 kube-system 名字空间
  namespace: kube-system
type: bootstrap.kubernetes.io/token
stringData:
  auth-extra-groups: "system:bootstrappers:kubeadm:default-node-token"
  expiration: "2020-09-13T04:39:10Z"
  # 此令牌 ID 被用于生成 Secret 名称
  token-id: "5emitj"
  token-secret: "kq4gihvszzgn1p0r"
  # 此令牌还可用于 authentication (身份认证)
  usage-bootstrap-authentication: "true"
  # 且可用于 signing (证书签名)
  usage-bootstrap-signing: "true"

使用 Secret

创建 Secret

创建 Secret 有以下几种可选方式:

对 Secret 名称与数据的约束

Secret 对象的名称必须是合法的 DNS 子域名

在为创建 Secret 编写配置文件时,你可以设置 data 与/或 stringData 字段。 datastringData 字段都是可选的。data 字段中所有键值都必须是 base64 编码的字符串。如果不希望执行这种 base64 字符串的转换操作,你可以选择设置 stringData 字段,其中可以使用任何字符串作为其取值。

datastringData 中的键名只能包含字母、数字、-_. 字符。 stringData 字段中的所有键值对都会在内部被合并到 data 字段中。 如果某个主键同时出现在 datastringData 字段中,stringData 所指定的键值具有高优先级。

尺寸限制

每个 Secret 的尺寸最多为 1MiB。施加这一限制是为了避免用户创建非常大的 Secret, 进而导致 API 服务器和 kubelet 内存耗尽。不过创建很多小的 Secret 也可能耗尽内存。 你可以使用资源配额来约束每个名字空间中 Secret(或其他资源)的个数。

编辑 Secret

你可以编辑一个已有的 Secret,除非它是不可变更的。 要编辑一个 Secret,可使用以下方法之一:

你也可以使用 Kustomize 工具编辑数据。 然而这种方法会用编辑过的数据创建新的 Secret 对象。

根据你创建 Secret 的方式以及该 Secret 在 Pod 中被使用的方式,对已有 Secret 对象的更新将自动扩散到使用此数据的 Pod。有关更多信息, 请参阅在 Pod 以文件形式使用 Secret

使用 Secret

Secret 可以以数据卷的形式挂载,也可以作为环境变量 暴露给 Pod 中的容器使用。Secret 也可用于系统中的其他部分,而不是一定要直接暴露给 Pod。 例如,Secret 也可以包含系统中其他部分在替你与外部系统交互时要使用的凭证数据。

Kubernetes 会检查 Secret 的卷数据源,确保所指定的对象引用确实指向类型为 Secret 的对象。因此,如果 Pod 依赖于某 Secret,该 Secret 必须先于 Pod 被创建。

如果 Secret 内容无法取回(可能因为 Secret 尚不存在或者临时性地出现 API 服务器网络连接问题),kubelet 会周期性地重试 Pod 运行操作。kubelet 也会为该 Pod 报告 Event 事件,给出读取 Secret 时遇到的问题细节。

可选的 Secret

当你在 Pod 中引用 Secret 时,你可以将该 Secret 标记为可选,就像下面例子中所展示的那样。 如果可选的 Secret 不存在,Kubernetes 将忽略它。

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true
  volumes:
  - name: foo
    secret:
      secretName: mysecret
      optional: true

默认情况下,Secret 是必需的。在所有非可选的 Secret 都可用之前,Pod 的所有容器都不会启动。

如果 Pod 引用了非可选 Secret 中的特定键,并且该 Secret 确实存在,但缺少所指定的键, 则 Pod 在启动期间会失败。

在 Pod 以文件形式使用 Secret

如果你要在 Pod 中访问来自 Secret 的数据,一种方式是让 Kubernetes 将该 Secret 的值以 文件的形式呈现,该文件存在于 Pod 中一个或多个容器内的文件系统内。

相关的指示说明, 可以参阅使用 Secret 安全地分发凭据

当卷中包含来自 Secret 的数据,而对应的 Secret 被更新,Kubernetes 会跟踪到这一操作并更新卷中的数据。更新的方式是保证最终一致性。

Kubelet 组件会维护一个缓存,在其中保存节点上 Pod 卷中使用的 Secret 的当前主键和取值。 你可以配置 kubelet 如何检测所缓存数值的变化。 kubelet 配置中的 configMapAndSecretChangeDetectionStrategy 字段控制 kubelet 所采用的策略。 默认的策略是 Watch

对 Secret 的更新操作既可以通过 API 的 watch 机制(默认)来传播, 基于设置了生命期的缓存获取,也可以通过 kubelet 的同步回路来从集群的 API 服务器上轮询获取。

因此,从 Secret 被更新到新的主键被投射到 Pod 中,中间存在一个延迟。 这一延迟的上限是 kubelet 的同步周期加上缓存的传播延迟, 其中缓存的传播延迟取决于所选择的缓存类型。 对应上一段中提到的几种传播机制,延迟时长为 watch 的传播延迟、所配置的缓存 TTL 或者对于直接轮询而言是零。

以环境变量的方式使用 Secret

如果需要在 Pod 中以环境变量的形式使用 Secret:

  1. 对于 Pod 规约中的每个容器,针对你要使用的每个 Secret 键,将对应的环境变量添加到 env[].valueFrom.secretKeyRef 中。
  2. 更改你的镜像或命令行,以便程序能够从指定的环境变量找到所需要的值。

相关的指示说明, 可以参阅使用 Secret 数据定义容器变量

需要注意的是,Pod 中环境变量名称允许的字符范围是有限的。 如果某些变量名称不满足这些规则,则即使 Pod 是可以启动的,你的容器也无法访问这些变量。

容器镜像拉取 Secret

如果你尝试从私有仓库拉取容器镜像,你需要一种方式让每个节点上的 kubelet 能够完成与镜像库的身份认证。你可以配置镜像拉取 Secret 来实现这点。 Secret 是在 Pod 层面来配置的。

使用 imagePullSecrets

imagePullSecrets 字段是一个列表,包含对同一名字空间中 Secret 的引用。 你可以使用 imagePullSecrets 将包含 Docker(或其他)镜像仓库密码的 Secret 传递给 kubelet。kubelet 使用此信息来替 Pod 拉取私有镜像。 参阅 PodSpec API 进一步了解 imagePullSecrets 字段。

手动设定 imagePullSecret

你可以通过阅读容器镜像 文档了解如何设置 imagePullSecrets

设置 imagePullSecrets 为自动挂载

你可以手动创建 imagePullSecret,并在一个 ServiceAccount 中引用它。 对使用该 ServiceAccount 创建的所有 Pod,或者默认使用该 ServiceAccount 创建的 Pod 而言,其 imagePullSecrets 字段都会设置为该服务账号。 请阅读向服务账号添加 ImagePullSecrets 来详细了解这一过程。

在静态 Pod 中使用 Secret

你不可以在静态 Pod 中使用 ConfigMap 或 Secret。

使用场景

使用场景:作为容器环境变量

你可以创建 Secret 并使用它为容器设置环境变量

使用场景:带 SSH 密钥的 Pod

创建包含一些 SSH 密钥的 Secret:

kubectl create secret generic ssh-key-secret --from-file=ssh-privatekey=/path/to/.ssh/id_rsa --from-file=ssh-publickey=/path/to/.ssh/id_rsa.pub

输出类似于:

secret "ssh-key-secret" created

你也可以创建一个 kustomization.yaml 文件,在其 secretGenerator 字段中包含 SSH 密钥。

现在你可以创建一个 Pod,在其中访问包含 SSH 密钥的 Secret,并通过卷的方式来使用它:

apiVersion: v1
kind: Pod
metadata:
  name: secret-test-pod
  labels:
    name: secret-test
spec:
  volumes:
  - name: secret-volume
    secret:
      secretName: ssh-key-secret
  containers:
  - name: ssh-test-container
    image: mySshImage
    volumeMounts:
    - name: secret-volume
      readOnly: true
      mountPath: "/etc/secret-volume"

容器命令执行时,秘钥的数据可以在下面的位置访问到:

/etc/secret-volume/ssh-publickey
/etc/secret-volume/ssh-privatekey

容器就可以随便使用 Secret 数据来建立 SSH 连接。

使用场景:带有生产、测试环境凭据的 Pod

这一示例所展示的一个 Pod 会使用包含生产环境凭据的 Secret,另一个 Pod 使用包含测试环境凭据的 Secret。

你可以创建一个带有 secretGenerator 字段的 kustomization.yaml 文件或者运行 kubectl create secret 来创建 Secret。

kubectl create secret generic prod-db-secret --from-literal=username=produser --from-literal=password=Y4nys7f11

输出类似于:

secret "prod-db-secret" created

你也可以创建一个包含测试环境凭据的 Secret:

kubectl create secret generic test-db-secret --from-literal=username=testuser --from-literal=password=iluvtests

输出类似于:

secret "test-db-secret" created

现在生成 Pod:

cat <<EOF > pod.yaml
apiVersion: v1
kind: List
items:
- kind: Pod
  apiVersion: v1
  metadata:
    name: prod-db-client-pod
    labels:
      name: prod-db-client
  spec:
    volumes:
    - name: secret-volume
      secret:
        secretName: prod-db-secret
    containers:
    - name: db-client-container
      image: myClientImage
      volumeMounts:
      - name: secret-volume
        readOnly: true
        mountPath: "/etc/secret-volume"
- kind: Pod
  apiVersion: v1
  metadata:
    name: test-db-client-pod
    labels:
      name: test-db-client
  spec:
    volumes:
    - name: secret-volume
      secret:
        secretName: test-db-secret
    containers:
    - name: db-client-container
      image: myClientImage
      volumeMounts:
      - name: secret-volume
        readOnly: true
        mountPath: "/etc/secret-volume"
EOF

将 Pod 添加到同一 kustomization.yaml 文件中:

cat <<EOF >> kustomization.yaml
resources:
- pod.yaml
EOF

通过下面的命令在 API 服务器上应用所有这些对象:

kubectl apply -k .

两个文件都会在其文件系统中出现下面的文件,文件中内容是各个容器的环境值:

/etc/secret-volume/username
/etc/secret-volume/password

注意这两个 Pod 的规约中只有一个字段不同。 这便于基于相同的 Pod 模板生成具有不同能力的 Pod。

你可以通过使用两个服务账号来进一步简化这一基本的 Pod 规约:

  1. prod-user 服务账号使用 prod-db-secret
  2. test-user 服务账号使用 test-db-secret

Pod 规约简化为:

apiVersion: v1
kind: Pod
metadata:
  name: prod-db-client-pod
  labels:
    name: prod-db-client
spec:
  serviceAccount: prod-db-client
  containers:
  - name: db-client-container
    image: myClientImage

不可更改的 Secret

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

Kubernetes 允许你将特定的 Secret(和 ConfigMap)标记为 不可更改(Immutable)。 禁止更改现有 Secret 的数据有下列好处:

  • 防止意外(或非预期的)更新导致应用程序中断
  • (对于大量使用 Secret 的集群而言,至少数万个不同的 Secret 供 Pod 挂载), 通过将 Secret 标记为不可变,可以极大降低 kube-apiserver 的负载,提升集群性能。 kubelet 不需要监视那些被标记为不可更改的 Secret。

将 Secret 标记为不可更改

你可以通过将 Secret 的 immutable 字段设置为 true 创建不可更改的 Secret。 例如:

apiVersion: v1
kind: Secret
metadata:
  ...
data:
  ...
immutable: true

你也可以更改现有的 Secret,令其不可更改。

Secret 的信息安全问题

尽管 ConfigMap 和 Secret 的工作方式类似,但 Kubernetes 对 Secret 有一些额外的保护。

Secret 通常保存重要性各异的数值,其中很多都可能会导致 Kubernetes 中 (例如,服务账号令牌)或对外部系统的特权提升。 即使某些个别应用能够推导它期望使用的 Secret 的能力, 同一名字空间中的其他应用可能会让这种假定不成立。

只有当某个节点上的 Pod 需要某 Secret 时,对应的 Secret 才会被发送到该节点上。 如果将 Secret 挂载到 Pod 中,kubelet 会将数据的副本保存在在 tmpfs 中, 这样机密的数据不会被写入到持久性存储中。 一旦依赖于该 Secret 的 Pod 被删除,kubelet 会删除来自于该 Secret 的机密数据的本地副本。

同一个 Pod 中可能包含多个容器。默认情况下,你所定义的容器只能访问默认 ServiceAccount 及其相关 Secret。你必须显式地定义环境变量或者将卷映射到容器中,才能为容器提供对其他 Secret 的访问。

针对同一节点上的多个 Pod 可能有多个 Secret。不过,只有某个 Pod 所请求的 Secret 才有可能对 Pod 中的容器可见。因此,一个 Pod 不会获得访问其他 Pod 的 Secret 的权限。

配置 Secret 资源的最小特权访问

为了加强对 Secret 的安全措施,Kubernetes 提供了一种机制: 你可以为 ServiceAccount 添加 kubernetes.io/enforce-mountable-secrets: "true" 注解。

想了解更多信息,你可以参考此注解的文档

接下来

3.7.4 - 为 Pod 和容器管理资源

当你定义 Pod 时可以选择性地为每个 容器设定所需要的资源数量。 最常见的可设定资源是 CPU 和内存(RAM)大小;此外还有其他类型的资源。

当你为 Pod 中的 Container 指定了资源 request(请求) 时, kube-scheduler 就利用该信息决定将 Pod 调度到哪个节点上。 当你为 Container 指定了资源 limit(限制) 时,kubelet 就可以确保运行的容器不会使用超出所设限制的资源。 kubelet 还会为容器预留所 request(请求) 数量的系统资源,供其使用。

请求和限制

如果 Pod 运行所在的节点具有足够的可用资源,容器可能(且可以)使用超出对应资源 request 属性所设置的资源量。不过,容器不可以使用超出其资源 limit 属性所设置的资源量。

例如,如果你将容器的 memory 的请求量设置为 256 MiB,而该容器所处的 Pod 被调度到一个具有 8 GiB 内存的节点上,并且该节点上没有其他 Pod 运行,那么该容器就可以尝试使用更多的内存。

如果你将某容器的 memory 限制设置为 4 GiB,kubelet (和容器运行时)就会确保该限制生效。 容器运行时会禁止容器使用超出所设置资源限制的资源。 例如:当容器中进程尝试使用超出所允许内存量的资源时,系统内核会将尝试申请内存的进程终止, 并引发内存不足(OOM)错误。

限制可以以被动方式来实现(系统会在发现违例时进行干预),或者通过强制生效的方式实现 (系统会避免容器用量超出限制)。不同的容器运行时采用不同方式来实现相同的限制。

资源类型

CPU内存 都是 资源类型。每种资源类型具有其基本单位。 CPU 表达的是计算处理能力,其单位是 Kubernetes CPU。 内存的单位是字节。 对于 Linux 负载,则可以指定巨页(Huge Page)资源。 巨页是 Linux 特有的功能,节点内核在其中分配的内存块比默认页大小大得多。

例如,在默认页面大小为 4KiB 的系统上,你可以指定限制 hugepages-2Mi: 80Mi。 如果容器尝试分配 40 个 2MiB 大小的巨页(总共 80 MiB ),则分配请求会失败。

CPU 和内存统称为 计算资源,或简称为 资源。 计算资源的数量是可测量的,可以被请求、被分配、被消耗。 它们与 API 资源不同。 API 资源(如 Pod 和 Service)是可通过 Kubernetes API 服务器读取和修改的对象。

Pod 和 容器的资源请求和限制

针对每个容器,你都可以指定其资源限制和请求,包括如下选项:

  • spec.containers[].resources.limits.cpu
  • spec.containers[].resources.limits.memory
  • spec.containers[].resources.limits.hugepages-<size>
  • spec.containers[].resources.requests.cpu
  • spec.containers[].resources.requests.memory
  • spec.containers[].resources.requests.hugepages-<size>

尽管你只能逐个容器地指定请求和限制值,考虑 Pod 的总体资源请求和限制也是有用的。 对特定资源而言,Pod 的资源请求/限制 是 Pod 中各容器对该类型资源的请求/限制的总和。

Kubernetes 中的资源单位

CPU 资源单位

CPU 资源的限制和请求以 “cpu” 为单位。 在 Kubernetes 中,一个 CPU 等于 1 个物理 CPU 核 或者 1 个虚拟核, 取决于节点是一台物理主机还是运行在某物理主机上的虚拟机。

你也可以表达带小数 CPU 的请求。 当你定义一个容器,将其 spec.containers[].resources.requests.cpu 设置为 0.5 时, 你所请求的 CPU 是你请求 1.0 CPU 时的一半。 对于 CPU 资源单位,数量 表达式 0.1 等价于表达式 100m,可以看作 “100 millicpu”。 有些人说成是“一百毫核”,其实说的是同样的事情。

CPU 资源总是设置为资源的绝对数量而非相对数量值。 例如,无论容器运行在单核、双核或者 48-核的机器上,500m CPU 表示的是大约相同的计算能力。

内存资源单位

memory 的限制和请求以字节为单位。 你可以使用普通的整数,或者带有以下 数量后缀 的定点数字来表示内存:E、P、T、G、M、k。 你也可以使用对应的 2 的幂数:Ei、Pi、Ti、Gi、Mi、Ki。 例如,以下表达式所代表的是大致相同的值:

128974848、129e6、129M、128974848000m、123Mi

请注意后缀的大小写。如果你请求 400m 临时存储,实际上所请求的是 0.4 字节。 如果有人这样设定资源请求或限制,可能他的实际想法是申请 400Mi 字节(400Mi) 或者 400M 字节。

容器资源示例

以下 Pod 有两个容器。每个容器的请求为 0.25 CPU 和 64MiB(226 字节)内存, 每个容器的资源限制为 0.5 CPU 和 128MiB 内存。 你可以认为该 Pod 的资源请求为 0.5 CPU 和 128 MiB 内存,资源限制为 1 CPU 和 256MiB 内存。

---
apiVersion: v1
kind: Pod
metadata:
  name: frontend
spec:
  containers:
  - name: app
    image: images.my-company.example/app:v4
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"
  - name: log-aggregator
    image: images.my-company.example/log-aggregator:v6
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"

带资源请求的 Pod 如何调度

当你创建一个 Pod 时,Kubernetes 调度程序将为 Pod 选择一个节点。 每个节点对每种资源类型都有一个容量上限:可为 Pod 提供的 CPU 和内存量。 调度程序确保对于每种资源类型,所调度的容器的资源请求的总和小于节点的容量。 请注意,尽管节点上的实际内存或 CPU 资源使用量非常低,如果容量检查失败, 调度程序仍会拒绝在该节点上放置 Pod。 当稍后节点上资源用量增加,例如到达请求率的每日峰值区间时,节点上也不会出现资源不足的问题。

Kubernetes 应用资源请求与限制的方式

当 kubelet 将容器作为 Pod 的一部分启动时,它会将容器的 CPU 和内存请求与限制信息传递给容器运行时。

在 Linux 系统上,容器运行时通常会配置内核 CGroups,负责应用并实施所定义的请求。

  • CPU 限制定义的是容器可使用的 CPU 时间的硬性上限。 在每个调度周期(时间片)期间,Linux 内核检查是否已经超出该限制; 内核会在允许该 cgroup 恢复执行之前会等待。
  • CPU 请求值定义的是一个权重值。如果若干不同的容器(CGroups)需要在一个共享的系统上竞争运行, CPU 请求值大的负载会获得比请求值小的负载更多的 CPU 时间。
  • 内存请求值主要用于(Kubernetes)Pod 调度期间。在一个启用了 CGroup v2 的节点上, 容器运行时可能会使用内存请求值作为设置 memory.minmemory.low 的提示值。
  • 内存限制定义的是 cgroup 的内存限制。如果容器尝试分配的内存量超出限制, 则 Linux 内核的内存不足处理子系统会被激活,并停止尝试分配内存的容器中的某个进程。 如果该进程在容器中 PID 为 1,而容器被标记为可重新启动,则 Kubernetes 会重新启动该容器。
  • Pod 或容器的内存限制也适用于通过内存供应的卷,例如 emptyDir 卷。 kubelet 会跟踪 tmpfs 形式的 emptyDir 卷用量,将其作为容器的内存用量, 而不是临时存储用量。

如果某容器内存用量超过其内存请求值并且所在节点内存不足时,容器所处的 Pod 可能被逐出

每个容器可能被允许也可能不被允许使用超过其 CPU 限制的处理时间。 但是,容器运行时不会由于 CPU 使用率过高而杀死 Pod 或容器。

要确定某容器是否会由于资源限制而无法调度或被杀死,请参阅疑难解答节。

监控计算和内存资源用量

kubelet 会将 Pod 的资源使用情况作为 Pod status 的一部分来报告的。

如果为集群配置了可选的监控工具, 则可以直接从指标 API 或者监控工具获得 Pod 的资源使用情况。

本地临时存储

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

节点通常还可以具有本地的临时性存储,由本地挂接的可写入设备或者有时也用 RAM 来提供支持。 “临时(Ephemeral)”意味着对所存储的数据不提供长期可用性的保证。

Pods 通常可以使用临时性本地存储来实现缓冲区、保存日志等功能。 kubelet 可以为使用本地临时存储的 Pods 提供这种存储空间,允许后者使用 emptyDir 类型的将其挂载到容器中。

kubelet 也使用此类存储来保存节点层面的容器日志、 容器镜像文件以及运行中容器的可写入层。

Kubernetes 允许你跟踪、预留和限制 Pod 可消耗的临时性本地存储数量。

本地临时性存储的配置

Kubernetes 有两种方式支持节点上配置本地临时性存储:

采用这种配置时,你会把所有类型的临时性本地数据(包括 emptyDir 卷、可写入容器层、容器镜像、日志等)放到同一个文件系统中。 作为最有效的 kubelet 配置方式,这意味着该文件系统是专门提供给 Kubernetes (kubelet)来保存数据的。

kubelet 也会生成节点层面的容器日志, 并按临时性本地存储的方式对待之。

kubelet 会将日志写入到所配置的日志目录(默认为 /var/log)下的文件中; 还会针对其他本地存储的数据使用同一个基础目录(默认为 /var/lib/kubelet)。

通常,/var/lib/kubelet/var/log 都是在系统的根文件系统中。kubelet 的设计也考虑到这一点。

你的集群节点当然可以包含其他的、并非用于 Kubernetes 的很多文件系统。

你使用节点上的某个文件系统来保存运行 Pods 时产生的临时性数据:日志和 emptyDir 卷等。你可以使用这个文件系统来保存其他数据(例如:与 Kubernetes 无关的其他系统日志);这个文件系统还可以是根文件系统。

kubelet 也将节点层面的容器日志 写入到第一个文件系统中,并按临时性本地存储的方式对待之。

同时你使用另一个由不同逻辑存储设备支持的文件系统。在这种配置下,你会告诉 kubelet 将容器镜像层和可写层保存到这第二个文件系统上的某个目录中。

第一个文件系统中不包含任何镜像层和可写层数据。

当然,你的集群节点上还可以有很多其他与 Kubernetes 没有关联的文件系统。

kubelet 能够度量其本地存储的用量。 实现度量机制的前提是你已使用本地临时存储所支持的配置之一对节点进行配置。

如果你的节点配置不同于以上预期,kubelet 就无法对临时性本地存储实施资源限制。

为本地临时性存储设置请求和限制

你可以指定 ephemeral-storage 来管理本地临时性存储。 Pod 中的每个容器可以设置以下属性:

  • spec.containers[].resources.limits.ephemeral-storage
  • spec.containers[].resources.requests.ephemeral-storage

ephemeral-storage 的请求和限制是按量纲计量的。 你可以使用一般整数或者定点数字加上下面的后缀来表达存储量:E、P、T、G、M、k。 你也可以使用对应的 2 的幂级数来表达:Ei、Pi、Ti、Gi、Mi、Ki。 例如,下面的表达式所表达的大致是同一个值:

  • 128974848
  • 129e6
  • 129M
  • 123Mi

请注意后缀的大小写。如果你请求 400m 临时存储,实际上所请求的是 0.4 字节。 如果有人这样设定资源请求或限制,可能他的实际想法是申请 400Mi 字节(400Mi) 或者 400M 字节。

在下面的例子中,Pod 包含两个容器。每个容器请求 2 GiB 大小的本地临时性存储。 每个容器都设置了 4 GiB 作为其本地临时性存储的限制。 因此,整个 Pod 的本地临时性存储请求是 4 GiB,且其本地临时性存储的限制为 8 GiB。 该限制值中有 500Mi 可供 emptyDir 卷使用。

apiVersion: v1
kind: Pod
metadata:
  name: frontend
spec:
  containers:
  - name: app
    image: images.my-company.example/app:v4
    resources:
      requests:
        ephemeral-storage: "2Gi"
      limits:
        ephemeral-storage: "4Gi"
    volumeMounts:
    - name: ephemeral
      mountPath: "/tmp"
  - name: log-aggregator
    image: images.my-company.example/log-aggregator:v6
    resources:
      requests:
        ephemeral-storage: "2Gi"
      limits:
        ephemeral-storage: "4Gi"
    volumeMounts:
    - name: ephemeral
      mountPath: "/tmp"
  volumes:
    - name: ephemeral
      emptyDir:
        sizeLimit: 500Mi

带临时性存储的 Pods 的调度行为

当你创建一个 Pod 时,Kubernetes 调度器会为 Pod 选择一个节点来运行之。 每个节点都有一个本地临时性存储的上限,是其可提供给 Pod 使用的总量。 欲了解更多信息, 可参考节点可分配资源节。

调度器会确保所调度的容器的资源请求总和不会超出节点的资源容量。

临时性存储消耗的管理

如果 kubelet 将本地临时性存储作为资源来管理,则 kubelet 会度量以下各处的存储用量:

  • emptyDir 卷,除了 tmpfs emptyDir
  • 保存节点层面日志的目录
  • 可写入的容器镜像层

如果某 Pod 的临时存储用量超出了你所允许的范围,kubelet 会向其发出逐出(eviction)信号,触发该 Pod 被逐出所在节点。

就容器层面的隔离而言,如果某容器的可写入镜像层和日志用量超出其存储限制, kubelet 也会将所在的 Pod 标记为逐出候选。

就 Pod 层面的隔离而言,kubelet 会将 Pod 中所有容器的限制相加,得到 Pod 存储限制的总值。如果所有容器的本地临时性存储用量总和加上 Pod 的 emptyDir 卷的用量超出 Pod 存储限制,kubelet 也会将该 Pod 标记为逐出候选。

kubelet 支持使用不同方式来度量 Pod 的存储用量:

kubelet 按预定周期执行扫描操作,检查 emptyDir 卷、容器日志目录以及可写入容器镜像层。

这一扫描会度量存储空间用量。

扩展资源(Extended Resources)

扩展资源是 kubernetes.io 域名之外的标准资源名称。 它们使得集群管理员能够颁布非 Kubernetes 内置资源,而用户可以使用他们。

使用扩展资源需要两个步骤。首先,集群管理员必须颁布扩展资源。 其次,用户必须在 Pod 中请求扩展资源。

管理扩展资源

节点级扩展资源

节点级扩展资源绑定到节点。

设备插件管理的资源

有关如何颁布在各节点上由设备插件所管理的资源, 请参阅设备插件

其他资源

为了颁布新的节点级扩展资源,集群操作员可以向 API 服务器提交 PATCH HTTP 请求, 以在集群中节点的 status.capacity 中为其配置可用数量。 完成此操作后,节点的 status.capacity 字段中将包含新资源。 kubelet 会异步地对 status.allocatable 字段执行自动更新操作,使之包含新资源。

由于调度器在评估 Pod 是否适合在某节点上执行时会使用节点的 status.allocatable 值, 调度器只会考虑异步更新之后的新值。 在更新节点容量使之包含新资源之后和请求该资源的第一个 Pod 被调度到该节点之间, 可能会有短暂的延迟。

示例:

这是一个示例,显示了如何使用 curl 构造 HTTP 请求,公告主节点为 k8s-master 的节点 k8s-node-1 上存在五个 example.com/foo 资源。

curl --header "Content-Type: application/json-patch+json" \
--request PATCH \
--data '[{"op": "add", "path": "/status/capacity/example.com~1foo", "value": "5"}]' \
http://k8s-master:8080/api/v1/nodes/k8s-node-1/status

集群层面的扩展资源

集群层面的扩展资源并不绑定到具体节点。 它们通常由调度器扩展程序(Scheduler Extenders)管理,这些程序处理资源消耗和资源配额。

你可以在调度器配置 中指定由调度器扩展程序处理的扩展资源。

示例:

下面的调度器策略配置标明集群层扩展资源 "example.com/foo" 由调度器扩展程序处理。

  • 仅当 Pod 请求 "example.com/foo" 时,调度器才会将 Pod 发送到调度器扩展程序。
  • ignoredByScheduler 字段指定调度器不要在其 PodFitsResources 断言中检查 "example.com/foo" 资源。
{
  "kind": "Policy",
  "apiVersion": "v1",
  "extenders": [
    {
      "urlPrefix": "<extender-endpoint>",
      "bindVerb": "bind",
      "managedResources": [
        {
          "name": "example.com/foo",
          "ignoredByScheduler": true
        }
      ]
    }
  ]
}

使用扩展资源

就像 CPU 和内存一样,用户可以在 Pod 的规约中使用扩展资源。 调度器负责资源的核算,确保同时分配给 Pod 的资源总量不会超过可用数量。

API 服务器将扩展资源的数量限制为整数。 有效 数量的示例是 33000m3Ki无效 数量的示例是 0.51500m(因为 1500m 结果等同于 1.5)。

要在 Pod 中使用扩展资源,请在容器规约的 spec.containers[].resources.limits 映射中包含资源名称作为键。

仅当所有资源请求(包括 CPU、内存和任何扩展资源)都被满足时,Pod 才能被调度。 在资源请求无法满足时,Pod 会保持在 PENDING 状态。

示例:

下面的 Pod 请求 2 个 CPU 和 1 个 "example.com/foo"(扩展资源)。

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
  - name: my-container
    image: myimage
    resources:
      requests:
        cpu: 2
        example.com/foo: 1
      limits:
        example.com/foo: 1

PID 限制

进程 ID(PID)限制允许对 kubelet 进行配置,以限制给定 Pod 可以消耗的 PID 数量。 有关信息,请参见 PID 限制

疑难解答

我的 Pod 处于悬决状态且事件信息显示 FailedScheduling

如果调度器找不到该 Pod 可以匹配的任何节点,则该 Pod 将保持未被调度状态, 直到找到一个可以被调度到的位置。每当调度器找不到 Pod 可以调度的地方时, 会产生一个 Event。 你可以使用 kubectl 来查看 Pod 的事件;例如:

kubectl describe pod frontend | grep -A 9999999999 Events
Events:
  Type     Reason            Age   From               Message
  ----     ------            ----  ----               -------
  Warning  FailedScheduling  23s   default-scheduler  0/42 nodes available: insufficient cpu

在上述示例中,由于节点上的 CPU 资源不足,名为 “frontend” 的 Pod 无法被调度。 由于内存不足(PodExceedsFreeMemory)而导致失败时,也有类似的错误消息。 一般来说,如果 Pod 处于悬决状态且有这种类型的消息时,你可以尝试如下几件事情:

  • 向集群添加更多节点。
  • 终止不需要的 Pod,为悬决的 Pod 腾出空间。
  • 检查 Pod 所需的资源是否超出所有节点的资源容量。例如,如果所有节点的容量都是 cpu:1, 那么一个请求为 cpu: 1.1 的 Pod 永远不会被调度。
  • 检查节点上的污点设置。如果集群中节点上存在污点,而新的 Pod 不能容忍污点, 调度器只会考虑将 Pod 调度到不带有该污点的节点上。

你可以使用 kubectl describe nodes 命令检查节点容量和已分配的资源数量。例如:

kubectl describe nodes e2e-test-node-pool-4lw4
Name:            e2e-test-node-pool-4lw4
[ ... 这里忽略了若干行以便阅读 ...]
Capacity:
 cpu:                               2
 memory:                            7679792Ki
 pods:                              110
Allocatable:
 cpu:                               1800m
 memory:                            7474992Ki
 pods:                              110
[ ... 这里忽略了若干行以便阅读 ...]
Non-terminated Pods:        (5 in total)
  Namespace    Name                                  CPU Requests  CPU Limits  Memory Requests  Memory Limits
  ---------    ----                                  ------------  ----------  ---------------  -------------
  kube-system  fluentd-gcp-v1.38-28bv1               100m (5%)     0 (0%)      200Mi (2%)       200Mi (2%)
  kube-system  kube-dns-3297075139-61lj3             260m (13%)    0 (0%)      100Mi (1%)       170Mi (2%)
  kube-system  kube-proxy-e2e-test-...               100m (5%)     0 (0%)      0 (0%)           0 (0%)
  kube-system  monitoring-influxdb-grafana-v4-z1m12  200m (10%)    200m (10%)  600Mi (8%)       600Mi (8%)
  kube-system  node-problem-detector-v0.1-fj7m3      20m (1%)      200m (10%)  20Mi (0%)        100Mi (1%)
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  CPU Requests    CPU Limits    Memory Requests    Memory Limits
  ------------    ----------    ---------------    -------------
  680m (34%)      400m (20%)    920Mi (11%)        1070Mi (13%)

在上面的输出中,你可以看到如果 Pod 请求超过 1.120 CPU 或者 6.23Gi 内存,节点将无法满足。

通过查看 "Pods" 部分,你将看到哪些 Pod 占用了节点上的资源。

Pods 可用的资源量低于节点的资源总量,因为系统守护进程也会使用一部分可用资源。 在 Kubernetes API 中,每个 Node 都有一个 .status.allocatable 字段 (详情参见 NodeStatus)。

字段 .status.allocatable 描述节点上可以用于 Pod 的资源总量(例如:15 个虚拟 CPU、7538 MiB 内存)。关于 Kubernetes 中节点可分配资源的信息, 可参阅为系统守护进程预留计算资源

你可以配置资源配额功能特性以限制每个名字空间可以使用的资源总量。 当某名字空间中存在 ResourceQuota 时,Kubernetes 会在该名字空间中的对象强制实施配额。 例如,如果你为不同的团队分配名字空间,你可以为这些名字空间添加 ResourceQuota。 设置资源配额有助于防止一个团队占用太多资源,以至于这种占用会影响其他团队。

你还需要考虑为这些名字空间设置授权访问: 为名字空间提供全部的写权限时,具有合适权限的人可能删除所有资源, 包括所配置的 ResourceQuota。

我的容器被终止了

你的容器可能因为资源紧张而被终止。要查看容器是否因为遇到资源限制而被杀死, 请针对相关的 Pod 执行 kubectl describe pod

kubectl describe pod simmemleak-hra99

输出类似于:

Name:                           simmemleak-hra99
Namespace:                      default
Image(s):                       saadali/simmemleak
Node:                           kubernetes-node-tf0f/10.240.216.66
Labels:                         name=simmemleak
Status:                         Running
Reason:
Message:
IP:                             10.244.2.75
Containers:
  simmemleak:
    Image:  saadali/simmemleak:latest
    Limits:
      cpu:          100m
      memory:       50Mi
    State:          Running
      Started:      Tue, 07 Jul 2019 12:54:41 -0700
    Last State:     Terminated
      Reason:       OOMKilled
      Exit Code:    137
      Started:      Fri, 07 Jul 2019 12:54:30 -0700
      Finished:     Fri, 07 Jul 2019 12:54:33 -0700
    Ready:          False
    Restart Count:  5
Conditions:
  Type      Status
  Ready     False
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  42s   default-scheduler  Successfully assigned simmemleak-hra99 to kubernetes-node-tf0f
  Normal  Pulled     41s   kubelet            Container image "saadali/simmemleak:latest" already present on machine
  Normal  Created    41s   kubelet            Created container simmemleak
  Normal  Started    40s   kubelet            Started container simmemleak
  Normal  Killing    32s   kubelet            Killing container with id ead3fb35-5cf5-44ed-9ae1-488115be66c6: Need to kill Pod

在上面的例子中,Restart Count: 5 意味着 Pod 中的 simmemleak 容器被终止并且(到目前为止)重启了五次。 原因 OOMKilled 显示容器尝试使用超出其限制的内存量。

你接下来要做的或许是检查应用代码,看看是否存在内存泄露。 如果你发现应用的行为与你所预期的相同,则可以考虑为该容器设置一个更高的内存限制 (也可能需要设置请求值)。

接下来

3.7.5 - 使用 kubeconfig 文件组织集群访问

使用 kubeconfig 文件来组织有关集群、用户、命名空间和身份认证机制的信息。 kubectl 命令行工具使用 kubeconfig 文件来查找选择集群所需的信息,并与集群的 API 服务器进行通信。

默认情况下,kubectl$HOME/.kube 目录下查找名为 config 的文件。 你可以通过设置 KUBECONFIG 环境变量或者设置 --kubeconfig参数来指定其他 kubeconfig 文件。

有关创建和指定 kubeconfig 文件的分步说明, 请参阅配置对多集群的访问

支持多集群、用户和身份认证机制

假设你有多个集群,并且你的用户和组件以多种方式进行身份认证。比如:

  • 正在运行的 kubelet 可能使用证书在进行认证。
  • 用户可能通过令牌进行认证。
  • 管理员可能拥有多个证书集合提供给各用户。

使用 kubeconfig 文件,你可以组织集群、用户和命名空间。你还可以定义上下文,以便在集群和命名空间之间快速轻松地切换。

上下文(Context)

通过 kubeconfig 文件中的 context 元素,使用简便的名称来对访问参数进行分组。 每个 context 都有三个参数:cluster、namespace 和 user。 默认情况下,kubectl 命令行工具使用 当前上下文 中的参数与集群进行通信。

选择当前上下文:

kubectl config use-context

KUBECONFIG 环境变量

KUBECONFIG 环境变量包含一个 kubeconfig 文件列表。 对于 Linux 和 Mac,此列表以英文冒号分隔。对于 Windows,此列表以英文分号分隔。 KUBECONFIG 环境变量不是必需的。 如果 KUBECONFIG 环境变量不存在,kubectl 将使用默认的 kubeconfig 文件:$HOME/.kube/config

如果 KUBECONFIG 环境变量存在,kubectl 将使用 KUBECONFIG 环境变量中列举的文件合并后的有效配置。

合并 kubeconfig 文件

要查看配置,输入以下命令:

kubectl config view

如前所述,输出可能来自单个 kubeconfig 文件,也可能是合并多个 kubeconfig 文件的结果。

以下是 kubectl 在合并 kubeconfig 文件时使用的规则。

  1. 如果设置了 --kubeconfig 参数,则仅使用指定的文件。不进行合并。此参数只能使用一次。

    否则,如果设置了 KUBECONFIG 环境变量,将它用作应合并的文件列表。根据以下规则合并 KUBECONFIG 环境变量中列出的文件:

    • 忽略空文件名。
    • 对于内容无法反序列化的文件,产生错误信息。
    • 第一个设置特定值或者映射键的文件将生效。
    • 永远不会更改值或者映射键。示例:保留第一个文件的上下文以设置 current-context。 示例:如果两个文件都指定了 red-user,则仅使用第一个文件的 red-user 中的值。 即使第二个文件在 red-user 下有非冲突条目,也要丢弃它们。

    有关设置 KUBECONFIG 环境变量的示例, 请参阅设置 KUBECONFIG 环境变量

    否则,使用默认的 kubeconfig 文件($HOME/.kube/config),不进行合并。

  1. 根据此链中的第一个匹配确定要使用的上下文。

    1. 如果存在上下文,则使用 --context 命令行参数。
    2. 使用合并的 kubeconfig 文件中的 current-context

    这种场景下允许空上下文。

  1. 确定集群和用户。此时,可能有也可能没有上下文。根据此链中的第一个匹配确定集群和用户, 这将运行两次:一次用于用户,一次用于集群。

    1. 如果存在用户或集群,则使用命令行参数:--user 或者 --cluster
    2. 如果上下文非空,则从上下文中获取用户或集群。

    这种场景下用户和集群可以为空。

  1. 确定要使用的实际集群信息。此时,可能有也可能没有集群信息。 基于此链构建每个集群信息;第一个匹配项会被采用:

    1. 如果存在集群信息,则使用命令行参数:--server--certificate-authority--insecure-skip-tls-verify
    2. 如果合并的 kubeconfig 文件中存在集群信息属性,则使用这些属性。
    3. 如果没有 server 配置,则配置无效。
  1. 确定要使用的实际用户信息。使用与集群信息相同的规则构建用户信息,但对于每个用户只允许使用一种身份认证技术:

    1. 如果存在用户信息,则使用命令行参数:--client-certificate--client-key--username--password--token
    2. 使用合并的 kubeconfig 文件中的 user 字段。
    3. 如果存在两种冲突技术,则配置无效。
  1. 对于仍然缺失的任何信息,使用其对应的默认值,并可能提示输入身份认证信息。

文件引用

kubeconfig 文件中的文件和路径引用是相对于 kubeconfig 文件的位置。 命令行上的文件引用是相对于当前工作目录的。 在 $HOME/.kube/config 中,相对路径按相对路径存储,而绝对路径按绝对路径存储。

代理

你可以在 kubeconfig 文件中,为每个集群配置 proxy-url 来让 kubectl 使用代理,例如:

apiVersion: v1
kind: Config

clusters:
- cluster:
    proxy-url: http://proxy.example.org:3128
    server: https://k8s.example.org/k8s/clusters/c-xxyyzz
  name: development

users:
- name: developer

contexts:
- context:
  name: development

接下来

3.7.6 - Windows 节点的资源管理

本页概述了 Linux 和 Windows 在资源管理方式上的区别。

在 Linux 节点上,cgroup 用作资源控制的 Pod 边界。 在这个边界内创建容器以便于隔离网络、进程和文件系统。 Linux cgroup API 可用于收集 CPU、I/O 和内存使用统计数据。

与此相反,Windows 中每个容器对应一个作业对象, 与系统命名空间过滤器一起使用,将所有进程包含在一个容器中,提供与主机的逻辑隔离。 (作业对象是一种 Windows 进程隔离机制,不同于 Kubernetes 提及的 Job)。

如果没有命名空间过滤,就无法运行 Windows 容器。 这意味着在主机环境中无法让系统特权生效,因此特权容器在 Windows 上不可用。 容器不能使用来自主机的标识,因为安全帐户管理器(Security Account Manager,SAM)是独立的。

内存管理

Windows 不像 Linux 一样提供杀手(killer)机制,杀死内存不足的进程。 Windows 始终将所有用户态内存分配视为虚拟内存,并强制使用页面文件(pagefile)。

Windows 节点不会为进程过量使用内存。 最终结果是 Windows 不会像 Linux 那样达到内存不足的情况,Windows 将进程页面放到磁盘, 不会因为内存不足(OOM)而终止进程。 如果内存配置过量且所有物理内存都已耗尽,则换页性能就会降低。

CPU 管理

Windows 可以限制为不同进程分配的 CPU 时间长度,但无法保证最小的 CPU 时间长度。

在 Windows 上,kubelet 支持使用命令行标志来设置 kubelet 进程的调度优先级--windows-priorityclass。 与 Windows 主机上运行的其他进程相比,此标志允许 kubelet 进程获取更多的 CPU 时间片。 有关允许值及其含义的更多信息,请访问 Windows 优先级类。 为了确保运行的 Pod 不会耗尽 kubelet 的 CPU 时钟周期, 要将此标志设置为 ABOVE_NORMAL_PRIORITY_CLASS 或更高。

资源预留

为了满足操作系统、容器运行时和 kubelet 等 Kubernetes 主机进程使用的内存和 CPU, 你可以(且应该)用 --kube-reserved 和/或 --system-reserved kubelet 标志来预留内存和 CPU 资源。 在 Windows 上,这些值仅用于计算节点的可分配资源。

在 Windows 上,一种好的做法是预留至少 2GiB 的内存。

要决定预留多少 CPU,需明确每个节点的最大 Pod 密度, 并监控节点上运行的系统服务的 CPU 使用率,然后选择一个满足工作负载需求的值。

3.8 - 安全

确保云原生工作负载安全的一组概念。

Kubernetes 文档的这一部分内容的旨在引导你学习如何更安全地运行工作负载, 以及维护 Kubernetes 集群的基本安全性。

Kubernetes 基于云原生架构,并借鉴了 CNCF 有关云原生信息安全良好实践的建议。

请阅读云原生安全和 Kubernetes, 了解有关如何保护集群及其上运行的应用程序的更广泛背景信息。

Kubernetes 安全机制

Kubernetes 包含多个 API 和安全组件, 以及定义策略的方法,这些策略可以作为你的信息安全管理的一部分。

控制平面保护

任何 Kubernetes 集群的一个关键安全机制是控制对 Kubernetes API 的访问

Kubernetes 希望你配置并使用 TLS, 以便在控制平面内以及控制平面与其客户端之间提供传输中的数据加密。 你还可以为 Kubernetes 控制平面中存储的数据启用静态加密; 这与对你自己的工作负载数据使用静态加密不同,后者可能也是一个好主意。

Secret

Secret API 为需要保密的配置值提供基本保护。

工具负载保护

实施 Pod 安全标准以确保 Pod 及其容器得到适当隔离。如果需要,你还可以使用 RuntimeClass 来配置自定义隔离。

网络策略(NetworkPolicy) 可让控制 Pod 之间或 Pod 与集群外部网络之间的网络流量。

审计

Kubernetes 审计日志记录提供了一组与安全相关、 按时间顺序排列的记录,记录了集群中的操作序列。 集群审计用户、使用 Kubernetes API 的应用程序以及控制平面本身生成的活动。

云提供商安全

如果你在自己的硬件或不同的云平台上运行 Kubernetes 集群,请参阅对应云平台的文档以了解安全最佳实践。 以下是一些流行云提供商的安全文档的链接:

Cloud provider security
IaaS 提供商链接
阿里云https://www.alibabacloud.com/trust-center
亚马逊网络服务https://aws.amazon.com/security
谷歌云平台https://cloud.google.com/security
华为云https://www.huaweicloud.com/intl/en-us/securecenter/overallsafety
IBM 云https://www.ibm.com/cloud/security
微软 Azurehttps://docs.microsoft.com/en-us/azure/security/azure-security
Oracle 云基础设施https://www.oracle.com/security
VMware vSpherehttps://www.vmware.com/security/hardening-guides

策略

你可以使用 Kubernetes 原生机制定义安全策略,例如 NetworkPolicy(对网络数据包过滤的声明式控制) 或 [ValidatingAdmisisonPolicy](/zh-cn/docs/reference/access -authn-authz/validating-admission-policy/) (对某人可以使用 Kubernetes API 进行哪些更改的声明性限制)。

你还可以依赖 Kubernetes 周边更广泛的生态系统的策略实现。 Kubernetes 提供了扩展机制,让这些生态系统项目在源代码审查、 容器镜像审批、API 访问控制、网络等方面实施自己的策略控制。

有关策略机制和 Kubernetes 的更多信息,请阅读策略

接下来

了解相关的 Kubernetes 安全主题:

了解上下文:

获取认证:

阅读本节的更多内容:

3.8.1 - Pod 安全性标准

详细了解 Pod 安全性标准(Pod Security Standards)中所定义的不同策略级别。

Pod 安全性标准定义了三种不同的策略(Policy),以广泛覆盖安全应用场景。 这些策略是叠加式的(Cumulative),安全级别从高度宽松至高度受限。 本指南概述了每个策略的要求。

Profile描述
Privileged不受限制的策略,提供最大可能范围的权限许可。此策略允许已知的特权提升。
Baseline限制性最弱的策略,禁止已知的特权提升。允许使用默认的(规定最少)Pod 配置。
Restricted限制性非常强的策略,遵循当前的保护 Pod 的最佳实践。

Profile 细节

Privileged

Privileged 策略是有目的地开放且完全无限制的策略。 此类策略通常针对由特权较高、受信任的用户所管理的系统级或基础设施级负载。

Privileged 策略定义中限制较少。默认允许的(Allow-by-default)实施机制(例如 gatekeeper) 可以缺省设置为 Privileged。 与此不同,对于默认拒绝(Deny-by-default)的实施机制(如 Pod 安全策略)而言, Privileged 策略应该禁止所有限制。

Baseline

Baseline 策略的目标是便于常见的容器化应用采用,同时禁止已知的特权提升。 此策略针对的是应用运维人员和非关键性应用的开发人员。 下面列举的控制应该被实施(禁止):

Baseline 策略规范
控制(Control)策略(Policy)
HostProcess

Windows Pod 提供了运行 HostProcess 容器 的能力,这使得对 Windows 节点的特权访问成为可能。Baseline 策略中禁止对宿主的特权访问。

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

限制的字段

  • spec.securityContext.windowsOptions.hostProcess
  • spec.containers[*].securityContext.windowsOptions.hostProcess
  • spec.initContainers[*].securityContext.windowsOptions.hostProcess
  • spec.ephemeralContainers[*].securityContext.windowsOptions.hostProcess

准许的取值

  • 未定义、nil
  • false
宿主名字空间

必须禁止共享宿主上的名字空间。

限制的字段

  • spec.hostNetwork
  • spec.hostPID
  • spec.hostIPC

准许的取值

  • 未定义、nil
  • false
特权容器

特权 Pod 会使大多数安全性机制失效,必须被禁止。

限制的字段

  • spec.containers[*].securityContext.privileged
  • spec.initContainers[*].securityContext.privileged
  • spec.ephemeralContainers[*].securityContext.privileged

准许的取值

  • 未定义、nil
  • false
权能

必须禁止添加除下列字段之外的权能。

限制的字段

  • spec.containers[*].securityContext.capabilities.add
  • spec.initContainers[*].securityContext.capabilities.add
  • spec.ephemeralContainers[*].securityContext.capabilities.add

准许的取值

  • 未定义、nil
  • AUDIT_WRITE
  • CHOWN
  • DAC_OVERRIDE
  • FOWNER
  • FSETID
  • KILL
  • MKNOD
  • NET_BIND_SERVICE
  • SETFCAP
  • SETGID
  • SETPCAP
  • SETUID
  • SYS_CHROOT
HostPath 卷

必须禁止 HostPath 卷。

限制的字段

  • spec.volumes[*].hostPath

准许的取值

  • 未定义、nil
宿主端口

应该完全禁止使用宿主端口(推荐)或者至少限制只能使用某确定列表中的端口。

限制的字段

  • spec.containers[*].ports[*].hostPort
  • spec.initContainers[*].ports[*].hostPort
  • spec.ephemeralContainers[*].ports[*].hostPort

准许的取值

AppArmor

在受支持的主机上,默认使用 runtime/default AppArmor 配置。Baseline 策略应避免覆盖或者禁用默认策略,以及限制覆盖一些配置集合的权限。

限制的字段

  • metadata.annotations["container.apparmor.security.beta.kubernetes.io/*"]

准许的取值

  • 未定义、nil
  • runtime/default
  • localhost/*
SELinux

设置 SELinux 类型的操作是被限制的,设置自定义的 SELinux 用户或角色选项是被禁止的。

限制的字段

  • spec.securityContext.seLinuxOptions.type
  • spec.containers[*].securityContext.seLinuxOptions.type
  • spec.initContainers[*].securityContext.seLinuxOptions.type
  • spec.ephemeralContainers[*].securityContext.seLinuxOptions.type

准许的取值

  • 未定义、""
  • container_t
  • container_init_t
  • container_kvm_t

限制的字段

  • spec.securityContext.seLinuxOptions.user
  • spec.containers[*].securityContext.seLinuxOptions.user
  • spec.initContainers[*].securityContext.seLinuxOptions.user
  • spec.ephemeralContainers[*].securityContext.seLinuxOptions.user
  • spec.securityContext.seLinuxOptions.role
  • spec.containers[*].securityContext.seLinuxOptions.role
  • spec.initContainers[*].securityContext.seLinuxOptions.role
  • spec.ephemeralContainers[*].securityContext.seLinuxOptions.role

准许的取值

  • 未定义、""
/proc挂载类型

要求使用默认的 /proc 掩码以减小攻击面。

限制的字段

  • spec.containers[*].securityContext.procMount
  • spec.initContainers[*].securityContext.procMount
  • spec.ephemeralContainers[*].securityContext.procMount

准许的取值

  • 未定义、nil
  • Default
Seccomp

Seccomp 配置必须不能显式设置为 Unconfined

限制的字段

  • spec.securityContext.seccompProfile.type
  • spec.containers[*].securityContext.seccompProfile.type
  • spec.initContainers[*].securityContext.seccompProfile.type
  • spec.ephemeralContainers[*].securityContext.seccompProfile.type

准许的取值

  • 未定义、nil
  • RuntimeDefault
  • Localhost
Sysctls

Sysctls 可以禁用安全机制或影响宿主上所有容器,因此除了若干“安全”的子集之外,应该被禁止。如果某 sysctl 是受容器或 Pod 的名字空间限制,且与节点上其他 Pod 或进程相隔离,可认为是安全的。

限制的字段

  • spec.securityContext.sysctls[*].name

准许的取值

  • 未定义、nil
  • kernel.shm_rmid_forced
  • net.ipv4.ip_local_port_range
  • net.ipv4.ip_unprivileged_port_start
  • net.ipv4.tcp_syncookies
  • net.ipv4.ping_group_range
  • net.ipv4.ip_local_reserved_ports(从 Kubernetes 1.27 开始)
  • net.ipv4.tcp_keepalive_time(从 Kubernetes 1.29 开始)
  • net.ipv4.tcp_fin_timeout(从 Kubernetes 1.29 开始)
  • net.ipv4.tcp_keepalive_intvl(从 Kubernetes 1.29 开始)
  • net.ipv4.tcp_keepalive_probes(从 Kubernetes 1.29 开始)

Restricted

Restricted 策略旨在实施当前保护 Pod 的最佳实践,尽管这样作可能会牺牲一些兼容性。 该类策略主要针对运维人员和安全性很重要的应用的开发人员,以及不太被信任的用户。 下面列举的控制需要被实施(禁止):

Restricted 策略规范
控制策略
Baseline 策略的所有要求。
卷类型

除了限制 HostPath 卷之外,此类策略还限制可以通过 PersistentVolumes 定义的非核心卷类型。

限制的字段

  • spec.volumes[*]

准许的取值

spec.volumes[*] 列表中的每个条目必须将下面字段之一设置为非空值:
  • spec.volumes[*].configMap
  • spec.volumes[*].csi
  • spec.volumes[*].downwardAPI
  • spec.volumes[*].emptyDir
  • spec.volumes[*].ephemeral
  • spec.volumes[*].persistentVolumeClaim
  • spec.volumes[*].projected
  • spec.volumes[*].secret
特权提升(v1.8+)

禁止(通过 SetUID 或 SetGID 文件模式)获得特权提升。这是 v1.25+ 中仅针对 Linux 的策略 (spec.os.name != windows)

限制的字段

  • spec.containers[*].securityContext.allowPrivilegeEscalation
  • spec.initContainers[*].securityContext.allowPrivilegeEscalation
  • spec.ephemeralContainers[*].securityContext.allowPrivilegeEscalation

允许的取值

  • false
以非 root 账号运行

容器必须以非 root 账号运行。

限制的字段

  • spec.securityContext.runAsNonRoot
  • spec.containers[*].securityContext.runAsNonRoot
  • spec.initContainers[*].securityContext.runAsNonRoot
  • spec.ephemeralContainers[*].securityContext.runAsNonRoot

准许的取值

  • true
如果 Pod 级别 spec.securityContext.runAsNonRoot 设置为 true,则允许容器组的安全上下文字段设置为 未定义/nil
非 root 用户(v1.23+)

容器不可以将 runAsUser 设置为 0

限制的字段

  • spec.securityContext.runAsUser
  • spec.containers[*].securityContext.runAsUser
  • spec.initContainers[*].securityContext.runAsUser
  • spec.ephemeralContainers[*].securityContext.runAsUser

准许的取值

  • 所有的非零值
  • undefined/null
Seccomp (v1.19+)

Seccomp Profile 必须被显式设置成一个允许的值。禁止使用 Unconfined Profile 或者指定 不存在的 Profile。这是 v1.25+ 中仅针对 Linux 的策略 (spec.os.name != windows)

限制的字段

  • spec.securityContext.seccompProfile.type
  • spec.containers[*].securityContext.seccompProfile.type
  • spec.initContainers[*].securityContext.seccompProfile.type
  • spec.ephemeralContainers[*].securityContext.seccompProfile.type

准许的取值

  • RuntimeDefault
  • Localhost
如果 Pod 级别的 spec.securityContext.seccompProfile.type 已设置得当,容器级别的安全上下文字段可以为未定义/nil。 反之如果 所有的 容器级别的安全上下文字段已设置, 则 Pod 级别的字段可为 未定义/nil
权能(v1.22+)

容器必须弃用 ALL 权能,并且只允许添加 NET_BIND_SERVICE 权能。这是 v1.25+ 中仅针对 Linux 的策略 (.spec.os.name != "windows")

限制的字段

  • spec.containers[*].securityContext.capabilities.drop
  • spec.initContainers[*].securityContext.capabilities.drop
  • spec.ephemeralContainers[*].securityContext.capabilities.drop

准许的取值

  • 包括 ALL 在内的任意权能列表。

限制的字段

  • spec.containers[*].securityContext.capabilities.add
  • spec.initContainers[*].securityContext.capabilities.add
  • spec.ephemeralContainers[*].securityContext.capabilities.add

准许的取值

  • 未定义、nil
  • NET_BIND_SERVICE

策略实例化

将策略定义从策略实例中解耦出来有助于形成跨集群的策略理解和语言陈述, 以免绑定到特定的下层实施机制。

随着相关机制的成熟,这些机制会按策略分别定义在下面。特定策略的实施方法不在这里定义。

Pod 安全性准入控制器

替代方案

在 Kubernetes 生态系统中还在开发一些其他的替代方案,例如:

Pod OS 字段

Kubernetes 允许你使用运行 Linux 或 Windows 的节点。你可以在一个集群中混用两种类型的节点。 Kubernetes 中的 Windows 与基于 Linux 的工作负载相比有一些限制和差异。 具体而言,许多 Pod securityContext 字段在 Windows 上不起作用

限制性的 Pod Security Standard 变更

Kubernetes v1.25 中的另一个重要变化是限制性的(Restricted) Pod 安全性已更新, 能够处理 pod.spec.os.name 字段。根据 OS 名称,专用于特定 OS 的某些策略对其他 OS 可以放宽限制。

OS 特定的策略控制

仅当 .spec.os.name 不是 windows 时,才需要对以下控制进行限制:

  • 特权提升
  • Seccomp
  • Linux 权能

用户命名空间

用户命名空间是 Linux 特有的功能,可在运行工作负载时提高隔离度。 关于用户命名空间如何与 PodSecurityStandard 协同工作, 请参阅 文档 了解 Pod 如何使用用户命名空间。

常见问题

为什么不存在介于 Privileged 和 Baseline 之间的策略类型

这里定义的三种策略框架有一个明晰的线性递进关系,从最安全(Restricted)到最不安全, 并且覆盖了很大范围的工作负载。特权要求超出 Baseline 策略者通常是特定于应用的需求, 所以我们没有在这个范围内提供标准框架。 这并不意味着在这样的情形下仍然只能使用 Privileged 框架, 只是说处于这个范围的策略需要因地制宜地定义。

SIG Auth 可能会在将来考虑这个范围的框架,前提是有对其他框架的需求。

安全配置与安全上下文的区别是什么?

安全上下文在运行时配置 Pod 和容器。安全上下文是在 Pod 清单中作为 Pod 和容器规约的一部分来定义的, 所代表的是传递给容器运行时的参数。

安全策略则是控制面用来对安全上下文以及安全性上下文之外的参数实施某种设置的机制。 在 2020 年 7 月, Pod 安全性策略已被废弃, 取而代之的是内置的 Pod 安全性准入控制器

沙箱(Sandboxed)Pod 怎么处理?

现在还没有 API 标准来控制 Pod 是否被视作沙箱化 Pod。 沙箱 Pod 可以通过其是否使用沙箱化运行时(如 gVisor 或 Kata Container)来辨别, 不过目前还没有关于什么是沙箱化运行时的标准定义。

沙箱化负载所需要的保护可能彼此各不相同。例如,当负载与下层内核直接隔离开来时, 限制特权化操作的许可就不那么重要。这使得那些需要更多许可权限的负载仍能被有效隔离。

此外,沙箱化负载的保护高度依赖于沙箱化的实现方法。 因此,现在还没有针对所有沙箱化负载的建议配置。

3.8.2 - 服务账号

了解 Kubernetes 中的 ServiceAccount 对象。

本页介绍 Kubernetes 中的 ServiceAccount 对象, 讲述服务账号的工作原理、使用场景、限制、替代方案,还提供了一些资源链接方便查阅更多指导信息。

什么是服务账号?

服务账号是在 Kubernetes 中一种用于非人类用户的账号,在 Kubernetes 集群中提供不同的身份标识。 应用 Pod、系统组件以及集群内外的实体可以使用特定 ServiceAccount 的凭据来将自己标识为该 ServiceAccount。 这种身份可用于许多场景,包括向 API 服务器进行身份认证或实现基于身份的安全策略。

服务账号以 ServiceAccount 对象的形式存在于 API 服务器中。服务账号具有以下属性:

  • 名字空间限定: 每个服务账号都与一个 Kubernetes 名字空间绑定。 每个名字空间在创建时,会获得一个名为 default 的 ServiceAccount

  • 轻量级: 服务账号存在于集群中,并在 Kubernetes API 中定义。你可以快速创建服务账号以支持特定任务。

  • 可移植性: 复杂的容器化工作负载的配置包中可能包括针对系统组件的服务账号定义。 服务账号的轻量级性质和名字空间作用域的身份使得这类配置可移植。

服务账号与用户账号不同,用户账号是集群中通过了身份认证的人类用户。默认情况下, 用户账号不存在于 Kubernetes API 服务器中;相反,API 服务器将用户身份视为不透明数据。 你可以使用多种方法认证为某个用户账号。某些 Kubernetes 发行版可能会添加自定义扩展 API 来在 API 服务器中表示用户账号。

服务账号与用户之间的比较
描述服务账号用户或组
位置Kubernetes API(ServiceAccount 对象)外部
访问控制Kubernetes RBAC 或其他鉴权机制Kubernetes RBAC 或其他身份和访问管理机制
目标用途工作负载、自动化工具

默认服务账号

在你创建集群时,Kubernetes 会自动为集群中的每个名字空间创建一个名为 default 的 ServiceAccount 对象。 在启用了基于角色的访问控制(RBAC)时,Kubernetes 为所有通过了身份认证的主体赋予 默认 API 发现权限。 每个名字空间中的 default 服务账号除了这些权限之外,默认没有其他访问权限。 如果基于角色的访问控制(RBAC)被启用,当你删除名字空间中的 default ServiceAccount 对象时, 控制平面会用新的 ServiceAccount 对象替换它。

如果你在某个名字空间中部署 Pod,并且你没有手动为 Pod 指派 ServiceAccount, Kubernetes 将该名字空间的 default 服务账号指派给这一 Pod。

Kubernetes 服务账号的使用场景

一般而言,你可以在以下场景中使用服务账号来提供身份标识:

  • 你的 Pod 需要与 Kubernetes API 服务器通信,例如在以下场景中:
    • 提供对存储在 Secret 中的敏感信息的只读访问。
    • 授予跨名字空间访问的权限,例如允许 example 名字空间中的 Pod 读取、列举和监视 kube-node-lease 名字空间中的 Lease 对象。
  • 外部服务需要与 Kubernetes API 服务器进行通信。例如,作为 CI/CD 流水线的一部分向集群作身份认证。
  • 你在集群中使用了第三方安全软件,该软件依赖不同 Pod 的 ServiceAccount 身份,按不同上下文对这些 Pod 分组。

如何使用服务账号

要使用 Kubernetes 服务账号,你需要执行以下步骤:

  1. 使用像 kubectl 这样的 Kubernetes 客户端或定义对象的清单(manifest)创建 ServiceAccount 对象。
  2. 使用鉴权机制(如 RBAC)为 ServiceAccount 对象授权。
  1. 在创建 Pod 期间将 ServiceAccount 对象指派给 Pod。

    如果你所使用的是来自外部服务的身份,可以获取 ServiceAccount 令牌,并在该服务中使用这一令牌。

有关具体操作说明,参阅为 Pod 配置服务账号

为 ServiceAccount 授权

你可以使用 Kubernetes 内置的 基于角色的访问控制 (RBAC)机制来为每个服务账号授予所需的最低权限。 你可以创建一个用来授权的角色,然后将此角色绑定到你的 ServiceAccount 上。 RBAC 可以让你定义一组最低权限,使得服务账号权限遵循最小特权原则。 这样使用服务账号的 Pod 不会获得超出其正常运行所需的权限。

有关具体操作说明,参阅 ServiceAccount 权限

使用 ServiceAccount 进行跨名字空间访问

你可以使用 RBAC 允许一个名字空间中的服务账号对集群中另一个名字空间的资源执行操作。 例如,假设你在 dev 名字空间中有一个服务账号和一个 Pod,并且希望该 Pod 可以查看 maintenance 名字空间中正在运行的 Job。你可以创建一个 Role 对象来授予列举 Job 对象的权限。 随后在 maintenance 名字空间中创建 RoleBinding 对象将 Role 绑定到此 ServiceAccount 对象上。 现在,dev 名字空间中的 Pod 可以使用该服务账号列出 maintenance 名字空间中的 Job 对象集合。

将 ServiceAccount 指派给 Pod

要将某 ServiceAccount 指派给某 Pod,你需要在该 Pod 的规约中设置 spec.serviceAccountName 字段。 Kubernetes 将自动为 Pod 提供该 ServiceAccount 的凭据。在 Kubernetes v1.22 及更高版本中, Kubernetes 使用 TokenRequest API 获取一个短期的、自动轮换的令牌, 并以投射卷的形式挂载此令牌。

默认情况下,Kubernetes 会将所指派的 ServiceAccount (无论是 default 服务账号还是你指定的定制 ServiceAccount)的凭据提供给 Pod。

要防止 Kubernetes 自动注入指定的 ServiceAccount 或 default ServiceAccount 的凭据, 可以将 Pod 规约中的 automountServiceAccountToken 字段设置为 false

在 Kubernetes 1.22 之前的版本中,Kubernetes 会将一个长期有效的静态令牌以 Secret 形式提供给 Pod。

手动获取 ServiceAccount 凭据

如果你需要 ServiceAccount 的凭据并将其挂载到非标准位置,或者用于 API 服务器之外的受众,可以使用以下方法之一:

  • TokenRequest API(推荐): 在你自己的应用代码中请求一个短期的服务账号令牌。此令牌会自动过期,并可在过期时被轮换。 如果你有一个旧的、对 Kubernetes 无感知能力的应用,你可以在同一个 Pod 内使用边车容器来获取这些令牌,并将其提供给应用工作负载。
  • 令牌卷投射(同样推荐): 在 Kubernetes v1.20 及更高版本中,使用 Pod 规约告知 kubelet 将服务账号令牌作为投射卷添加到 Pod 中。 所投射的令牌会自动过期,在过期之前 kubelet 会自动轮换此令牌。
  • 服务账号令牌 Secret(不推荐): 你可以将服务账号令牌以 Kubernetes Secret 的形式挂载到 Pod 中。这些令牌不会过期且不会轮换。 不推荐使用此方法,特别是在大规模场景下,这是因为静态、长期有效的凭据存在一定的风险。在 Kubernetes v1.24 及更高版本中, LegacyServiceAccountTokenNoAutoGeneration 特性门控阻止 Kubernetes 自动为 ServiceAccount 创建这些令牌。LegacyServiceAccountTokenNoAutoGeneration 默认被启用, 也就是说,Kubernetes 不会创建这些令牌。

限制对 Secret 的访问

Kubernetes 提供了名为 kubernetes.io/enforce-mountable-secrets 的注解, 你可以添加到你的 ServiceAccount 中。当应用了这个注解后, ServiceAccount 的 Secret 只能挂载到特定类型的资源上,从而增强集群的安全性。

你可以使用以下清单将注解添加到一个 ServiceAccount 中:

apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    kubernetes.io/enforce-mountable-secrets: "true"
  name: my-serviceaccount
  namespace: my-namespace

当此注解设置为 "true" 时,Kubernetes 控制平面确保来自该 ServiceAccount 的 Secret 受到特定挂载限制。

  1. 在 Pod 中作为卷挂载的每个 Secret 的名称必须列在该 Pod 中 ServiceAccount 的 secrets 字段中。
  1. 在 Pod 中使用 envFrom 引用的每个 Secret 的名称也必须列在该 Pod 中 ServiceAccount 的 secrets 字段中。
  1. 在 Pod 中使用 imagePullSecrets 引用的每个 Secret 的名称也必须列在该 Pod 中 ServiceAccount 的 secrets 字段中。

通过理解并执行这些限制,集群管理员可以维护更严格的安全配置,并确保 Secret 仅被适当的资源访问。

对服务账号凭据进行鉴别

ServiceAccount 使用签名的 JSON Web Token (JWT) 来向 Kubernetes API 服务器以及任何其他存在信任关系的系统进行身份认证。根据令牌的签发方式 (使用 TokenRequest 限制时间或使用传统的 Secret 机制),ServiceAccount 令牌也可能有到期时间、受众和令牌开始生效的时间点。 当客户端以 ServiceAccount 的身份尝试与 Kubernetes API 服务器通信时, 客户端会在 HTTP 请求中包含 Authorization: Bearer <token> 标头。 API 服务器按照以下方式检查该持有者令牌的有效性:

  1. 检查令牌签名。
  2. 检查令牌是否已过期。
  3. 检查令牌申明中的对象引用是否当前有效。
  4. 检查令牌是否当前有效。
  5. 检查受众申明。

TokenRequest API 为 ServiceAccount 生成绑定令牌。这种绑定与以该 ServiceAccount 身份运行的客户端(如 Pod)的生命期相关联。有关绑定 Pod 服务账号令牌的 JWT 模式和载荷的示例, 请参阅服务账号令牌卷投射

对于使用 TokenRequest API 签发的令牌,API 服务器还会检查正在使用 ServiceAccount 的特定对象引用是否仍然存在, 方式是通过该对象的唯一 ID 进行匹配。 对于以 Secret 形式挂载到 Pod 中的旧有令牌,API 服务器会基于 Secret 来检查令牌。

有关身份认证过程的更多信息,参考身份认证

在自己的代码中检查服务账号凭据

如果你的服务需要检查 Kubernetes 服务账号凭据,可以使用以下方法:

Kubernetes 项目建议你使用 TokenReview API,因为当你删除某些 API 对象 (如 Secret、ServiceAccount、Pod 和 Node)的时候,此方法将使绑定到这些 API 对象上的令牌失效。 例如,如果删除包含投射 ServiceAccount 令牌的 Pod,则集群立即使该令牌失效, 并且 TokenReview 操作也会立即失败。 如果你使用的是 OIDC 验证,则客户端将继续将令牌视为有效,直到令牌达到其到期时间戳。

你的应用应始终定义其所接受的受众,并检查令牌的受众是否与应用期望的受众匹配。 这有助于将令牌的作用域最小化,这样它只能在你的应用内部使用,而不能在其他地方使用。

替代方案

  • 使用其他机制签发你自己的令牌,然后使用 Webhook 令牌身份认证通过你自己的验证服务来验证持有者令牌。

接下来

3.8.3 - Pod 安全性准入

对 Pod 安全性准入控制器的概述,Pod 安全性准入控制器可以实施 Pod 安全性标准。
特性状态: Kubernetes v1.25 [stable]

Kubernetes Pod 安全性标准(Security Standard) 为 Pod 定义不同的隔离级别。这些标准能够让你以一种清晰、一致的方式定义如何限制 Pod 行为。

Kubernetes 提供了一个内置的 Pod Security 准入控制器来执行 Pod 安全标准 (Pod Security Standard)。 创建 Pod 时在名字空间级别应用这些 Pod 安全限制。

内置 Pod 安全准入强制执行

本页面是 Kubernetes v1.30 文档的一部分。 如果你运行的是其他版本的 Kubernetes,请查阅该版本的文档。

Pod 安全性级别

Pod 安全性准入插件对 Pod 的安全性上下文有一定的要求, 并且依据 Pod 安全性标准所定义的三个级别 (privilegedbaselinerestricted)对其他字段也有要求。 关于这些需求的更进一步讨论,请参阅 Pod 安全性标准页面。

为名字空间设置 Pod 安全性准入控制标签

一旦特性被启用或者安装了 Webhook,你可以配置名字空间以定义每个名字空间中 Pod 安全性准入控制模式。 Kubernetes 定义了一组标签, 你可以设置这些标签来定义某个名字空间上要使用的预定义的 Pod 安全性标准级别。 你所选择的标签定义了检测到潜在违例时, 控制面要采取什么样的动作。

Pod 安全准入模式
模式描述
enforce策略违例会导致 Pod 被拒绝
audit策略违例会触发审计日志中记录新事件时添加审计注解;但是 Pod 仍是被接受的。
warn策略违例会触发用户可见的警告信息,但是 Pod 仍是被接受的。

名字空间可以配置任何一种或者所有模式,或者甚至为不同的模式设置不同的级别。

对于每种模式,决定所使用策略的标签有两个:

# 模式的级别标签用来标示对应模式所应用的策略级别
#
# MODE 必须是 `enforce`、`audit` 或 `warn` 之一
# LEVEL 必须是 `privileged`、baseline` 或 `restricted` 之一
pod-security.kubernetes.io/<MODE>: <LEVEL>

# 可选:针对每个模式版本的版本标签可以将策略锁定到
# 给定 Kubernetes 小版本号所附带的版本(例如 v1.30)
#
# MODE 必须是 `enforce`、`audit` 或 `warn` 之一
# VERSION 必须是一个合法的 Kubernetes 小版本号或者 `latest`
pod-security.kubernetes.io/<MODE>-version: <VERSION>

关于用法示例,可参阅使用名字空间标签来强制实施 Pod 安全标准

负载资源和 Pod 模板

Pod 通常是通过创建 DeploymentJob 这类工作负载对象来间接创建的。 工作负载对象为工作负载资源定义一个 Pod 模板和一个对应的负责基于该模板来创建 Pod 的控制器。 为了尽早地捕获违例状况,auditwarn 模式都应用到负载资源。 不过,enforce 模式并应用到工作负载资源,仅应用到所生成的 Pod 对象上。

豁免

你可以为 Pod 安全性的实施设置豁免(Exemptions) 规则, 从而允许创建一些本来会被与给定名字空间相关的策略所禁止的 Pod。 豁免规则可以在准入控制器配置 中静态配置。

豁免规则必须显式枚举。满足豁免标准的请求会被准入控制器忽略 (所有 enforceauditwarn 行为都会被略过)。 豁免的维度包括:

  • Username: 来自用户名已被豁免的、已认证的(或伪装的)的用户的请求会被忽略。
  • RuntimeClassName: 指定了已豁免的运行时类名称的 Pod 和负载资源会被忽略。
  • Namespace: 位于被豁免的名字空间中的 Pod 和负载资源会被忽略。

策略检查时会对以下 Pod 字段的更新操作予以豁免,这意味着如果 Pod 更新请求仅改变这些字段时,即使 Pod 违反了当前的策略级别,请求也不会被拒绝。

  • 除了对 seccomp 或 AppArmor 注解之外的所有元数据(Metadata)更新操作:
    • seccomp.security.alpha.kubernetes.io/pod (已弃用)
    • container.seccomp.security.alpha.kubernetes.io/* (已弃用)
    • container.apparmor.security.beta.kubernetes.io/*(已弃用)
  • .spec.activeDeadlineSeconds 的合法更新
  • .spec.tolerations 的合法更新

指标

以下是 kube-apiserver 公开的 Prometheus 指标:

  • pod_security_errors_total:此指标表示妨碍正常评估的错误数量。 如果错误是非致命的,kube-apiserver 可能会强制实施最新的受限配置。
  • pod_security_evaluations_total:此指标表示已发生的策略评估的数量, 不包括导出期间被忽略或豁免的请求。
  • pod_security_exemptions_total:该指标表示豁免请求的数量, 不包括被忽略或超出范围的请求。

接下来

如果你正运行较老版本的 Kubernetes,想要升级到不包含 PodSecurityPolicy 的 Kubernetes 版本, 可以参阅从 PodSecurityPolicy 迁移到内置的 PodSecurity 准入控制器

3.8.4 - Pod 安全策略

作为替代,你可以使用下面任一方式执行类似的限制,或者同时使用下面这两种方式。

有关如何迁移, 参阅从 PodSecurityPolicy 迁移到内置的 PodSecurity 准入控制器。 有关移除此 API 的更多信息,参阅 弃用 PodSecurityPolicy:过去、现在、未来

如果所运行的 Kubernetes 不是 v1.30 版本,则需要查看你所使用的 Kubernetes 版本的对应文档。

3.8.5 - Windows 节点的安全性

本篇介绍特定于 Windows 操作系统的安全注意事项和最佳实践。

保护节点上的 Secret 数据

在 Windows 上,来自 Secret 的数据以明文形式写入节点的本地存储 (与在 Linux 上使用 tmpfs / 内存中文件系统不同)。 作为集群操作员,你应该采取以下两项额外措施:

  1. 使用文件 ACL 来保护 Secret 的文件位置。
  2. 使用 BitLocker 进行卷级加密。

容器用户

可以为 Windows Pod 或容器指定 RunAsUsername 以作为特定用户执行容器进程。这大致相当于 RunAsUser

Windows 容器提供两个默认用户帐户,ContainerUser 和 ContainerAdministrator。 在微软的 Windows 容器安全 文档 何时使用 ContainerAdmin 和 ContainerUser 用户帐户 中介绍了这两个用户帐户之间的区别。

在容器构建过程中,可以将本地用户添加到容器镜像中。

Windows 容器还可以通过使用组管理的服务账号作为 Active Directory 身份运行。

Pod 级安全隔离

Windows 节点不支持特定于 Linux 的 Pod 安全上下文机制(例如 SELinux、AppArmor、Seccomp 或自定义 POSIX 权能字)。

Windows 上不支持特权容器。 然而,可以在 Windows 上使用 HostProcess 容器来执行 Linux 上特权容器执行的许多任务。

3.8.6 - Kubernetes API 访问控制

本页面概述了对 Kubernetes API 的访问控制。

用户使用 kubectl、客户端库或构造 REST 请求来访问 Kubernetes API。 人类用户和 Kubernetes 服务账号都可以被鉴权访问 API。 当请求到达 API 时,它会经历多个阶段,如下图所示:

Kubernetes API 请求处理步骤示意图

传输安全

默认情况下,Kubernetes API 服务器在第一个非 localhost 网络接口的 6443 端口上进行监听, 受 TLS 保护。在一个典型的 Kubernetes 生产集群中,API 使用 443 端口。 该端口可以通过 --secure-port 进行变更,监听 IP 地址可以通过 --bind-address 标志进行变更。

API 服务器出示证书。该证书可以使用私有证书颁发机构(CA)签名,也可以基于链接到公认的 CA 的公钥基础架构签名。 该证书和相应的私钥可以通过使用 --tls-cert-file--tls-private-key-file 标志进行设置。

如果你的集群使用私有证书颁发机构,你需要在客户端的 ~/.kube/config 文件中提供该 CA 证书的副本, 以便你可以信任该连接并确认该连接没有被拦截。

你的客户端可以在此阶段出示 TLS 客户端证书。

认证

如上图步骤 1 所示,建立 TLS 后, HTTP 请求将进入认证(Authentication)步骤。 集群创建脚本或者集群管理员配置 API 服务器,使之运行一个或多个身份认证组件。 身份认证组件在认证节中有更详细的描述。

认证步骤的输入整个 HTTP 请求;但是,通常组件只检查头部或/和客户端证书。

认证模块包含客户端证书、密码、普通令牌、引导令牌和 JSON Web 令牌(JWT,用于服务账号)。

可以指定多个认证模块,在这种情况下,服务器依次尝试每个验证模块,直到其中一个成功。

如果请求认证不通过,服务器将以 HTTP 状态码 401 拒绝该请求。 反之,该用户被认证为特定的 username,并且该用户名可用于后续步骤以在其决策中使用。 部分验证器还提供用户的组成员身份,其他则不提供。

鉴权

如上图的步骤 2 所示,将请求验证为来自特定的用户后,请求必须被鉴权。

请求必须包含请求者的用户名、请求的行为以及受该操作影响的对象。 如果现有策略声明用户有权完成请求的操作,那么该请求被鉴权通过。

例如,如果 Bob 有以下策略,那么他只能在 projectCaribou 名称空间中读取 Pod。

{
    "apiVersion": "abac.authorization.kubernetes.io/v1beta1",
    "kind": "Policy",
    "spec": {
        "user": "bob",
        "namespace": "projectCaribou",
        "resource": "pods",
        "readonly": true
    }
}

如果 Bob 执行以下请求,那么请求会被鉴权,因为允许他读取 projectCaribou 名称空间中的对象。

{
  "apiVersion": "authorization.k8s.io/v1beta1",
  "kind": "SubjectAccessReview",
  "spec": {
    "resourceAttributes": {
      "namespace": "projectCaribou",
      "verb": "get",
      "group": "unicorn.example.org",
      "resource": "pods"
    }
  }
}

如果 Bob 在 projectCaribou 名字空间中请求写(createupdate)对象,其鉴权请求将被拒绝。 如果 Bob 在诸如 projectFish 这类其它名字空间中请求读取(get)对象,其鉴权也会被拒绝。

Kubernetes 鉴权要求使用公共 REST 属性与现有的组织范围或云提供商范围的访问控制系统进行交互。 使用 REST 格式很重要,因为这些控制系统可能会与 Kubernetes API 之外的 API 交互。

Kubernetes 支持多种鉴权模块,例如 ABAC 模式、RBAC 模式和 Webhook 模式等。 管理员创建集群时,他们配置应在 API 服务器中使用的鉴权模块。 如果配置了多个鉴权模块,则 Kubernetes 会检查每个模块,任意一个模块鉴权该请求,请求即可继续; 如果所有模块拒绝了该请求,请求将会被拒绝(HTTP 状态码 403)。

要了解更多有关 Kubernetes 鉴权的更多信息,包括有关使用支持鉴权模块创建策略的详细信息, 请参阅鉴权

准入控制

准入控制模块是可以修改或拒绝请求的软件模块。 除鉴权模块可用的属性外,准入控制模块还可以访问正在创建或修改的对象的内容。

准入控制器对创建、修改、删除或(通过代理)连接对象的请求进行操作。 准入控制器不会对仅读取对象的请求起作用。 有多个准入控制器被配置时,服务器将依次调用它们。

这一操作如上图的步骤 3 所示。

与身份认证和鉴权模块不同,如果任何准入控制器模块拒绝某请求,则该请求将立即被拒绝。

除了拒绝对象之外,准入控制器还可以为字段设置复杂的默认值。

可用的准入控制模块在准入控制器中进行了描述。

请求通过所有准入控制器后,将使用检验例程检查对应的 API 对象,然后将其写入对象存储(如步骤 4 所示)。

审计

Kubernetes 审计提供了一套与安全相关的、按时间顺序排列的记录,其中记录了集群中的操作序列。 集群对用户、使用 Kubernetes API 的应用程序以及控制平面本身产生的活动进行审计。

更多信息请参考审计

接下来

阅读更多有关身份认证、鉴权和 API 访问控制的文档:

你可以了解:

  • Pod 如何使用 Secret 获取 API 凭据。

3.8.7 - 基于角色的访问控制良好实践

为集群操作人员提供的良好的 RBAC 设计原则和实践。

Kubernetes RBAC 是一项重要的安全控制措施,用于保证集群用户和工作负载只能访问履行自身角色所需的资源。 在为集群用户设计权限时,请务必确保集群管理员知道可能发生特权提级的地方, 降低因过多权限而导致安全事件的风险。

此文档的良好实践应该与通用 RBAC 文档一起阅读。

通用的良好实践

最小特权

理想情况下,分配给用户和服务帐户的 RBAC 权限应该是最小的。 仅应使用操作明确需要的权限,虽然每个集群会有所不同,但可以应用的一些常规规则:

  • 尽可能在命名空间级别分配权限。授予用户在特定命名空间中的权限时使用 RoleBinding 而不是 ClusterRoleBinding。
  • 尽可能避免通过通配符设置权限,尤其是对所有资源的权限。 由于 Kubernetes 是一个可扩展的系统,因此通过通配符来授予访问权限不仅会授予集群中当前的所有对象类型, 还包含所有未来被创建的所有对象类型。
  • 管理员不应使用 cluster-admin 账号,除非特别需要。为低特权帐户提供 伪装权限 可以避免意外修改集群资源。
  • 避免将用户添加到 system:masters 组。任何属于此组成员的用户都会绕过所有 RBAC 权限检查, 始终具有不受限制的超级用户访问权限,并且不能通过删除 RoleBindingClusterRoleBinding 来取消其权限。顺便说一句,如果集群使用 Webhook 鉴权,此组的成员身份也会绕过该 Webhook(来自属于该组成员的用户的请求永远不会发送到 Webhook)。

最大限度地减少特权令牌的分发

理想情况下,不应为 Pod 分配具有强大权限(例如,在特权提级的风险中列出的任一权限) 的服务帐户。如果工作负载需要比较大的权限,请考虑以下做法:

  • 限制运行此类 Pod 的节点数量。确保你运行的任何 DaemonSet 都是必需的, 并且以最小权限运行,以限制容器逃逸的影响范围。
  • 避免将此类 Pod 与不可信任或公开的 Pod 在一起运行。 考虑使用污点和容忍度节点亲和性Pod 反亲和性确保 Pod 不会与不可信或不太受信任的 Pod 一起运行。 特别注意可信度不高的 Pod 不符合 Restricted Pod 安全标准的情况。

加固

Kubernetes 默认提供访问权限并非是每个集群都需要的。 审查默认提供的 RBAC 权限为安全加固提供了机会。 一般来说,不应该更改 system: 帐户的某些权限,有一些方式来强化现有集群的权限:

  • 审查 system:unauthenticated 组的绑定,并在可能的情况下将其删除, 因为这会给所有能够访问 API 服务器的人以网络级别的权限。
  • 通过设置 automountServiceAccountToken: false 来避免服务账号令牌的默认自动挂载, 有关更多详细信息,请参阅使用默认服务账号令牌。 此参数可覆盖 Pod 服务账号设置,而需要服务账号令牌的工作负载仍可以挂载。

定期检查

定期检查 Kubernetes RBAC 设置是否有冗余条目和提权可能性是至关重要的。 如果攻击者能够创建与已删除用户同名的用户账号, 他们可以自动继承被删除用户的所有权限,尤其是分配给该用户的权限。

Kubernetes RBAC - 权限提权的风险

在 Kubernetes RBAC 中有许多特权,如果被授予, 用户或服务帐户可以提升其在集群中的权限并可能影响集群外的系统。

本节旨在提醒集群操作员需要注意的不同领域, 以确保他们不会无意中授予超出预期的集群访问权限。

列举 Secret

大家都很清楚,若允许对 Secrets 执行 get 访问,用户就获得了访问 Secret 内容的能力。 同样需要注意的是:listwatch 访问也会授权用户获取 Secret 的内容。 例如,当返回 List 响应时(例如,通过 kubectl get secrets -A -o yaml),响应包含所有 Secret 的内容。

工作负载的创建

在一个命名空间中创建工作负载(Pod 或管理 Pod 的工作负载资源) 的权限隐式地授予了对该命名空间中许多其他资源的访问权限,例如可以挂载在 Pod 中的 Secret、ConfigMap 和 PersistentVolume。 此外,由于 Pod 可以被任何服务账号运行, 因此授予创建工作负载的权限也会隐式地授予该命名空间中任何服务账号的 API 访问级别。

可以运行特权 Pod 的用户可以利用该访问权限获得节点访问权限, 并可能进一步提升他们的特权。如果你不完全信任某用户或其他主体, 不相信他们能够创建比较安全且相互隔离的 Pod,你应该强制实施 BaselineRestricted Pod 安全标准。你可以使用 Pod 安全性准入或其他(第三方) 机制来强制实施这些限制。

出于这些原因,命名空间应该用于隔离不同的信任级别或不同租户所需的资源。 遵循最小特权原则并分配最小权限集仍被认为是最佳实践, 但命名空间内的边界概念应视为比较弱。

持久卷的创建

如果允许某人或某个应用创建任意的 PersistentVolume,则这种访问权限包括创建 hostPath 卷, 这意味着 Pod 将可以访问对应节点上的下层主机文件系统。授予该能力会带来安全风险。

不受限制地访问主机文件系统的容器可以通过多种方式提升特权,包括从其他容器读取数据以及滥用系统服务 (例如 kubelet)的凭据。

你应该只允许以下实体具有创建 PersistentVolume 对象的访问权限:

  • 需要此访问权限才能工作的用户(集群操作员)以及你信任的人,
  • Kubernetes 控制平面组件,这些组件基于已配置为自动制备的 PersistentVolumeClaim 创建 PersistentVolume。 这通常由 Kubernetes 提供商或操作员在安装 CSI 驱动程序时进行设置。

在需要访问持久存储的地方,受信任的管理员应创建 PersistentVolume,而受约束的用户应使用 PersistentVolumeClaim 来访问该存储。

访问 Node 的 proxy 子资源

有权访问 Node 对象的 proxy 子资源的用户有权访问 kubelet API, 这允许在他们有权访问的节点上的所有 Pod 上执行命令。 此访问绕过审计日志记录和准入控制,因此在授予对此资源的权限前应小心。

esclate 动词

通常,RBAC 系统会阻止用户创建比他所拥有的更多权限的 ClusterRole。 而 escalate 动词是个例外。如 RBAC 文档 中所述,拥有此权限的用户可以有效地提升他们的权限。

bind 动词

escalate 动作类似,授予此权限的用户可以绕过 Kubernetes 对权限提升的内置保护,用户可以创建并绑定尚不具有的权限的角色。

impersonate 动词

此动词允许用户伪装并获得集群中其他用户的权限。 授予它时应小心,以确保通过其中一个伪装账号不会获得过多的权限。

CSR 和证书颁发

CSR API 允许用户拥有 create CSR 的权限和 update certificatesigningrequests/approval 的权限, 其中签名者是 kubernetes.io/kube-apiserver-client, 通过此签名创建的客户端证书允许用户向集群进行身份验证。 这些客户端证书可以包含任意的名称,包括 Kubernetes 系统组件的副本。 这将有利于特权提级。

令牌请求

拥有 serviceaccounts/tokencreate 权限的用户可以创建 TokenRequest 来发布现有服务帐户的令牌。

控制准入 Webhook

可以控制 validatingwebhookconfigurationsmutatingwebhookconfigurations 的用户可以控制能读取任何允许进入集群的对象的 webhook, 并且在有变更 webhook 的情况下,还可以变更准入的对象。

命名空间修改

可以对命名空间对象执行 patch 操作的用户(通过命名空间内的 RoleBinding 关联到具有该权限的 Role), 可以修改该命名空间的标签。在使用 Pod 安全准入的集群中,这可能允许用户将命名空间配置为比管理员预期更宽松的策略。 对于使用 NetworkPolicy 的集群,用户所设置的标签可能间接导致对某些本不应被允许访问的服务的访问权限被开放。

Kubernetes RBAC - 拒绝服务攻击的风险

对象创建拒绝服务

有权在集群中创建对象的用户根据创建对象的大小和数量可能会创建足够大的对象, 产生拒绝服务状况,如 Kubernetes 使用的 etcd 容易受到 OOM 攻击中的讨论。 允许太不受信任或者不受信任的用户对系统进行有限的访问在多租户集群中是特别重要的。

缓解此问题的一种选择是使用资源配额以限制可以创建的对象数量。

接下来

  • 了解有关 RBAC 的更多信息,请参阅 RBAC 文档

3.8.8 - Kubernetes Secret 良好实践

帮助集群管理员和应用开发者更好管理 Secret 的原理和实践。

在 Kubernetes 中,Secret 是这样一个对象: secret 用于存储敏感信息,如密码、OAuth 令牌和 SSH 密钥。

Secret 允许用户对如何使用敏感信息进行更多的控制,并减少信息意外暴露的风险。 默认情况下,Secret 值被编码为 base64 字符串并以非加密的形式存储,但可以配置为 静态加密(Encrypt at rest)

Pod 可以通过多种方式引用 Secret, 例如在卷挂载中引用或作为环境变量引用。Secret 设计用于机密数据,而 ConfigMap 设计用于非机密数据。

以下良好实践适用于集群管理员和应用开发者。遵从这些指导方针有助于提高 Secret 对象中敏感信息的安全性,还可以更有效地管理你的 Secret。

集群管理员

本节提供了集群管理员可用于提高集群中机密信息安全性的良好实践。

配置静态加密

默认情况下,Secret 对象以非加密的形式存储在 etcd 中。 你配置对在 etcd 中存储的 Secret 数据进行加密。相关的指导信息, 请参阅静态加密 Secret 数据

配置 Secret 资源的最小特权访问

当规划诸如 Kubernetes 基于角色的访问控制 (RBAC) 这类访问控制机制时,需要注意访问 Secret 对象的以下指导信息。 你还应遵从 RBAC 良好实践中的其他指导信息。

  • 组件:限制仅最高特权的系统级组件可以执行 watchlist 访问。 仅在组件的正常行为需要时才授予对 Secret 的 get 访问权限。
  • 人员:限制对 Secret 的 getwatchlist 访问权限。仅允许集群管理员访问 etcd。 这包括只读访问。对于更复杂的访问控制,例如使用特定注解限制对 Secret 的访问,请考虑使用第三方鉴权机制。

如果一个用户可以创建使用某 Secret 的 Pod,则该用户也可以看到该 Secret 的值。 即使集群策略不允许用户直接读取 Secret,同一用户也可能有权限运行 Pod 进而暴露该 Secret。 你可以检测或限制具有此访问权限的用户有意或无意地暴露 Secret 数据所造成的影响。 这里有一些建议:

  • 使用生命期短暂的 Secret
  • 实现对特定事件发出警报的审计规则,例如同一用户并发读取多个 Secret 时发出警报

用于 Secret 管理的附加 ServiceAccount 注解

你还可以在 ServiceAccount 上使用 kubernetes.io/enforce-mountable-secrets 注解来强制执行有关如何在 Pod 中使用 Secret 的特定规则。

更多详细信息,请参阅有关此注解的文档

改进 etcd 管理策略

不再使用 etcd 所使用的持久存储时,考虑擦除或粉碎这些数据。

如果存在多个 etcd 实例,则在实例之间配置加密的 SSL/TLS 通信以保护传输中的 Secret 数据。

配置对外部 Secret 的访问权限

你可以使用第三方 Secret 存储提供商将机密数据保存在你的集群之外,然后配置 Pod 访问该信息。 Kubernetes Secret 存储 CSI 驱动是一个 DaemonSet, 它允许 kubelet 从外部存储中检索 Secret,并将 Secret 作为卷挂载到特定的、你授权访问数据的 Pod。

有关支持的提供商列表,请参阅 Secret 存储 CSI 驱动的提供商

开发者

本节为开发者提供了构建和部署 Kubernetes 资源时用于改进机密数据安全性的良好实践。

限制特定容器集合才能访问 Secret

如果你在一个 Pod 中定义了多个容器,且仅其中一个容器需要访问 Secret,则可以定义卷挂载或环境变量配置, 这样其他容器就不会有访问该 Secret 的权限。

读取后保护 Secret 数据

应用程序从一个环境变量或一个卷读取机密信息的值后仍然需要保护这些值。 例如,你的应用程序必须避免以明文记录 Secret 数据,还必须避免将这些数据传输给不受信任的一方。

避免共享 Secret 清单

如果你通过清单(Manifest)配置 Secret, 同时将该 Secret 数据编码为 base64, 那么共享此文件或将其检入一个源代码仓库就意味着有权读取该清单的所有人都能使用该 Secret。

3.8.9 - 多租户

此页面概述了集群多租户的可用配置选项和最佳实践。

共享集群可以节省成本并简化管理。 然而,共享集群也带来了诸如安全性、公平性和管理嘈杂邻居等挑战。

集群可以通过多种方式共享。在某些情况下,不同的应用可能会在同一个集群中运行。 在其他情况下,同一应用的多个实例可能在同一个集群中运行,每个实例对应一个最终用户。 所有这些类型的共享经常使用一个总括术语 多租户(Multi-Tenancy) 来表述。

虽然 Kubernetes 没有最终用户或租户的一阶概念, 它还是提供了几个特性来帮助管理不同的租户需求。下面将对此进行讨论。

用例

确定如何共享集群的第一步是理解用例,以便你可以评估可用的模式和工具。 一般来说,Kubernetes 集群中的多租户分为两大类,但也可以有许多变体和混合。

多团队

多租户的一种常见形式是在组织内的多个团队之间共享一个集群,每个团队可以操作一个或多个工作负载。 这些工作负载经常需要相互通信,并与位于相同或不同集群上的其他工作负载进行通信。

在这一场景中,团队成员通常可以通过类似 kubectl 等工具直接访问 Kubernetes 资源, 或者通过 GitOps 控制器或其他类型的自动化发布工具间接访问 Kubernetes 资源。 不同团队的成员之间通常存在某种程度的信任, 但 RBAC、配额和网络策略等 Kubernetes 策略对于安全、公平地共享集群至关重要。

多客户

多租户的另一种主要形式通常涉及为客户运行多个工作负载实例的软件即服务 (SaaS) 供应商。 这种业务模型与其部署风格之间的相关非常密切,以至于许多人称之为 “SaaS 租户”。
但是,更好的术语可能是“多客户租户(Multi-Customer Tenancy)”,因为 SaaS 供应商也可以使用其他部署模型, 并且这种部署模型也可以在 SaaS 之外使用。

在这种情况下,客户无权访问集群; 从他们的角度来看,Kubernetes 是不可见的,仅由供应商用于管理工作负载。 成本优化通常是一个关键问题,Kubernetes 策略用于确保工作负载彼此高度隔离。

术语

租户

在讨论 Kubernetes 中的多租户时,“租户”没有单一的定义。 相反,租户的定义将根据讨论的是多团队还是多客户租户而有所不同。

在多团队使用中,租户通常是一个团队, 每个团队通常部署少量工作负载,这些工作负载会随着服务的复杂性而发生规模伸缩。 然而,“团队”的定义本身可能是模糊的, 因为团队可能被组织成更高级别的部门或细分为更小的团队。

相反,如果每个团队为每个新客户部署专用的工作负载,那么他们使用的是多客户租户模型。 在这种情况下,“租户”只是共享单个工作负载的一组用户。 这种租户可能大到整个公司,也可能小到该公司的一个团队。

在许多情况下,同一组织可能在不同的上下文中使用“租户”的两种定义。 例如,一个平台团队可能向多个内部“客户”提供安全工具和数据库等共享服务, 而 SaaS 供应商也可能让多个团队共享一个开发集群。 最后,混合架构也是可能的, 例如,某 SaaS 提供商为每个客户的敏感数据提供独立的工作负载,同时提供多租户共享的服务。

展示共存租户模型的集群

隔离

使用 Kubernetes 设计和构建多租户解决方案有多种方法。 每种方法都有自己的一组权衡,这些权衡会影响隔离级别、实现工作量、操作复杂性和服务成本。

Kubernetes 集群由运行 Kubernetes 软件的控制平面和由工作节点组成的数据平面组成, 租户工作负载作为 Pod 在工作节点上执行。 租户隔离可以根据组织要求应用于控制平面和数据平面。

所提供的隔离级别有时会使用一些术语来描述,例如 “硬性(Hard)” 多租户意味着强隔离, 而 “柔性(Soft)” 多租户意味着较弱的隔离。 特别是,“硬性”多租户通常用于描述租户彼此不信任的情况, 并且大多是从安全和资源共享的角度(例如,防范数据泄露或 DoS 攻击等)。 由于数据平面通常具有更大的攻击面,“硬性”多租户通常需要额外注意隔离数据平面, 尽管控制平面隔离也很关键。

但是,“硬性”和“柔性”这两个术语常常令人困惑,因为没有一种定义能够适用于所有用户。 相反,依据“硬度(Hardness)”或“柔度(Softness)”所定义的广泛谱系则更容易理解, 根据你的需求,可以使用许多不同的技术在集群中维护不同类型的隔离。

在更极端的情况下,彻底放弃所有集群级别的共享并为每个租户分配其专用集群可能更容易或有必要, 如果认为虚拟机所提供的安全边界还不够,甚至可以在专用硬件上运行。 对于托管的 Kubernetes 集群而言,这种方案可能更容易, 其中创建和操作集群的开销至少在一定程度上由云提供商承担。 必须根据管理多个集群的成本和复杂性来评估更强的租户隔离的好处。 Multi-Cluster SIG 负责解决这些类型的用例。

本页的其余部分重点介绍用于共享 Kubernetes 集群的隔离技术。 但是,即使你正在考虑使用专用集群,查看这些建议也可能很有价值, 因为如果你的需求或功能发生变化,它可以让你在未来比较灵活地切换到共享集群。

控制面隔离

控制平面隔离确保不同租户无法访问或影响彼此的 Kubernetes API 资源。

命名空间

在 Kubernetes 中, 命名空间提供了一种在单个集群中隔离 API 资源组的机制。 这种隔离有两个关键维度:

  1. 一个命名空间中的对象名称可以与其他命名空间中的名称重叠,类似于文件夹中的文件。 这允许租户命名他们的资源,而无需考虑其他租户在做什么。
  1. 许多 Kubernetes 安全策略的作用域是命名空间。 例如,RBAC Role 和 NetworkPolicy 是命名空间作用域的资源。 使用 RBAC,可以将用户和服务帐户限制在一个命名空间中。

在多租户环境中,命名空间有助于将租户的工作负载划分到各不相同的逻辑管理单元中。 事实上,一种常见的做法是将每个工作负载隔离在自己的命名空间中, 即使多个工作负载由同一个租户操作。 这可确保每个工作负载都有自己的身份,并且可以使用适当的安全策略进行配置。

命名空间隔离模型需要配置其他几个 Kubernetes 资源、网络插件, 并遵守安全最佳实践以正确隔离租户工作负载。 这些考虑将在下面讨论。

访问控制

控制平面最重要的隔离类型是授权。如果各个团队或其工作负载可以访问或修改彼此的 API 资源, 他们可以更改或禁用所有其他类型的策略,从而取消这些策略可能提供的任何保护。 因此,确保每个租户只对他们需要的命名空间有适当的访问权, 而不是更多,这一点至关重要。这被称为“最小特权原则(Principle of Least Privileges)”。

基于角色的访问控制 (RBAC) 通常用于在 Kubernetes 控制平面中对用户和工作负载(服务帐户)强制执行鉴权。 角色角色绑定是两种 Kubernetes 对象,用来在命名空间级别对应用实施访问控制; 对集群级别的对象访问鉴权也有类似的对象,不过这些对象对于多租户集群不太有用。

在多团队环境中,必须使用 RBAC 来限制租户只能访问合适的命名空间, 并确保集群范围的资源只能由集群管理员等特权用户访问或修改。

如果一个策略最终授予用户的权限比他们所需要的还多, 这可能是一个信号,表明包含受影响资源的命名空间应该被重构为更细粒度的命名空间。 命名空间管理工具可以通过将通用 RBAC 策略应用于不同的命名空间来简化这些细粒度命名空间的管理, 同时在必要时仍允许细粒度策略。

配额

Kubernetes 工作负载消耗节点资源,例如 CPU 和内存。在多租户环境中, 你可以使用资源配额来管理租户工作负载的资源使用情况。 对于多团队场景,各个租户可以访问 Kubernetes API,你可以使用资源配额来限制租户可以创建的 API 资源的数量 (例如:Pod 的数量,或 ConfigMap 的数量)。 对对象计数的限制确保了公平性,并有助于避免嘈杂邻居问题影响共享控制平面的其他租户。

资源配额是命名空间作用域的对象。 通过将租户映射到命名空间, 集群管理员可以使用配额来确保租户不能垄断集群的资源或压垮控制平面。 命名空间管理工具简化了配额的管理。 此外,虽然 Kubernetes 配额仅针对单个命名空间, 但一些命名空间管理工具允许多个命名空间组共享配额, 与内置配额相比,降低了管理员的工作量,同时为其提供了更大的灵活性。

配额可防止单个租户所消耗的资源超过其被分配的份额,从而最大限度地减少嘈杂邻居问题, 即一个租户对其他租户工作负载的性能产生负面影响。

当你对命名空间应用配额时, Kubernetes 要求你还为每个容器指定资源请求和限制。 限制是容器可以消耗的资源量的上限。 根据资源类型,尝试使用超出配置限制的资源的容器将被限制或终止。 当资源请求设置为低于限制时, 每个容器所请求的数量都可以得到保证,但可能仍然存在跨工作负载的一些潜在影响。

配额不能针对所共享的所有资源(例如网络流量)提供保护。 节点隔离(如下所述)可能是解决此问题的更好方法。

数据平面隔离

数据平面隔离确保不同租户的 Pod 和工作负载之间被充分隔离。

网络隔离

默认情况下,Kubernetes 集群中的所有 Pod 都可以相互通信,并且所有网络流量都是未加密的。 这可能导致安全漏洞,导致流量被意外或恶意发送到非预期目的地, 或被受感染节点上的工作负载拦截。

Pod 之间的通信可以使用网络策略来控制, 它使用命名空间标签或 IP 地址范围来限制 Pod 之间的通信。 在需要租户之间严格网络隔离的多租户环境中, 建议从拒绝 Pod 之间通信的默认策略入手, 然后添加一条允许所有 Pod 查询 DNS 服务器以进行名称解析的规则。 有了这样的默认策略之后,你就可以开始添加允许在命名空间内进行通信的更多规则。 另外建议不要在网络策略定义中对 namespaceSelector 字段使用空标签选择算符 “{}”, 以防需要允许在命名空间之间传输流量。 该方案可根据需要进一步细化。 请注意,这仅适用于单个控制平面内的 Pod; 属于不同虚拟控制平面的 Pod 不能通过 Kubernetes 网络相互通信。

命名空间管理工具可以简化默认或通用网络策略的创建。 此外,其中一些工具允许你在整个集群中强制实施一组一致的命名空间标签, 确保它们是你策略的可信基础。

服务网格可以提供更高级的网络隔离, 除了命名空间之外,它还提供基于工作负载身份的 OSI 第 7 层策略。 这些更高层次的策略可以更轻松地管理基于命名空间的多租户, 尤其是存在多个命名空间专用于某一个租户时。 服务网格还经常使用双向 TLS 提供加密能力, 即使在存在受损节点的情况下也能保护你的数据, 并且可以跨专用或虚拟集群工作。 但是,它们的管理可能要复杂得多,并且可能并不适合所有用户。

存储隔离

Kubernetes 提供了若干类型的卷,可以用作工作负载的持久存储。 为了安全和数据隔离,建议使用动态卷制备, 并且应避免使用节点资源的卷类型。

存储类(StorageClass)允许你根据服务质量级别、 备份策略或由集群管理员确定的自定义策略描述集群提供的自定义存储“类”。

Pod 可以使用持久卷申领(PersistentVolumeClaim)请求存储。 PersistentVolumeClaim 是一种命名空间作用域的资源, 它可以隔离存储系统的不同部分,并将隔离出来的存储提供给共享 Kubernetes 集群中的租户专用。 但是,重要的是要注意 PersistentVolume 是集群作用域的资源, 并且其生命周期独立于工作负载和命名空间的生命周期。

例如,你可以为每个租户配置一个单独的 StorageClass,并使用它来加强隔离。 如果一个 StorageClass 是共享的,你应该设置一个回收策略 以确保 PersistentVolume 不能在不同的命名空间中重复使用。

沙箱容器

Kubernetes Pod 由在工作节点上执行的一个或多个容器组成。 容器利用操作系统级别的虚拟化, 因此提供的隔离边界比使用基于硬件虚拟化的虚拟机弱一些。

在共享环境中,攻击者可以利用应用和系统层中未修补的漏洞实现容器逃逸和远程代码执行, 从而允许访问主机资源。 在某些应用中,例如内容管理系统(CMS), 客户可能被授权上传和执行非受信的脚本或代码。 无论哪种情况,都需要使用强隔离进一步隔离和保护工作负载的机制。

沙箱提供了一种在共享集群中隔离运行中的工作负载的方法。 它通常涉及在单独的执行环境(例如虚拟机或用户空间内核)中运行每个 Pod。 当你运行不受信任的代码时(假定工作负载是恶意的),通常建议使用沙箱, 这种隔离是必要的,部分原因是由于容器是在共享内核上运行的进程。 它们从底层主机挂载像 /sys 和 /proc 这样的文件系统, 这使得它们不如在具有自己内核的虚拟机上运行的应用安全。 虽然 seccomp、AppArmor 和 SELinux 等控件可用于加强容器的安全性, 但很难将一套通用规则应用于在共享集群中运行的所有工作负载。 在沙箱环境中运行工作负载有助于将主机隔离开来,不受容器逃逸影响, 在容器逃逸场景中,攻击者会利用漏洞来访问主机系统以及在该主机上运行的所有进程/文件。

虚拟机和用户空间内核是两种流行的沙箱方法。 可以使用以下沙箱实现:

  • gVisor 拦截来自容器的系统调用,并通过用户空间内核运行它们, 用户空间内核采用 Go 编写,对底层主机的访问是受限的
  • Kata Containers 提供了一个安全的容器运行时, 允许你在 VM 中运行容器。Kata 中提供的硬件虚拟化为运行不受信任代码的容器提供了额外的安全层。

节点隔离

节点隔离是另一种可用于将租户工作负载相互隔离的技术。 通过节点隔离,一组节点专用于运行来自特定租户的 Pod,并且禁止混合不同租户 Pod 集合。 这种配置减少了嘈杂的租户问题,因为在一个节点上运行的所有 Pod 都将属于一个租户。 节点隔离的信息泄露风险略低, 因为成功实现容器逃逸的攻击者也只能访问挂载在该节点上的容器和卷。

尽管来自不同租户的工作负载在不同的节点上运行, 仍然很重要的是要注意 kubelet 和 (除非使用虚拟控制平面)API 服务仍然是共享服务。 熟练的攻击者可以使用分配给 kubelet 或节点上运行的其他 Pod 的权限在集群内横向移动并获得对其他节点上运行的租户工作负载的访问权限。 如果这是一个主要问题,请考虑实施补偿控制, 例如使用 seccomp、AppArmor 或 SELinux,或者探索使用沙箱容器,或者为每个租户创建单独的集群。

从计费的角度来看,节点隔离比沙箱容器更容易理解, 因为你可以按节点而不是按 Pod 收费。 它的兼容性和性能问题也较少,而且可能比沙箱容器更容易实现。 例如,可以为每个租户的节点配置污点, 以便只有具有相应容忍度的 Pod 才能在其上运行。 然后可以使用变更性质的 Webhook 自动向部署到租户命名空间中的 Pod 添加容忍度和节点亲和性, 以便它们在为该租户指定的一组特定节点上运行。

节点隔离可以使用将 Pod 指派给节点Virtual Kubelet 来实现。

额外的注意事项

本节讨论与多租户相关的其他 Kubernetes 结构和模式。

API 优先级和公平性

API 优先级和公平性是 Kubernetes 的一个特性, 允许你为集群中运行的某些 Pod 赋予优先级。 当应用调用 Kubernetes API 时,API 服务器会评估分配给 Pod 的优先级。 来自具有较高优先级的 Pod 的调用会在具有较低优先级的 Pod 的调用之前完成。 当争用很激烈时,较低优先级的调用可以排队,直到服务器不那么忙,或者你可以拒绝请求。

使用 API 优先级和公平性在 SaaS 环境中并不常见, 除非你允许客户运行与 Kubernetes API 接口的应用,例如控制器。

服务质量 (QoS)

当你运行 SaaS 应用时, 你可能希望能够为不同的租户提供不同的服务质量 (QoS) 层级。 例如,你可能拥有具有性能保证和功能较差的免费增值服务, 以及具有一定性能保证的收费服务层。 幸运的是,有几个 Kubernetes 结构可以帮助你在共享集群中完成此任务, 包括网络 QoS、存储类以及 Pod 优先级和抢占。 这些都是为了给租户提供他们所支付的服务质量。 让我们从网络 QoS 开始。

通常,节点上的所有 Pod 共享一个网络接口。 如果没有网络 QoS,一些 Pod 可能会以牺牲其他 Pod 为代价不公平地消耗可用带宽。 Kubernetes 带宽插件为网络创建 扩展资源, 以允许你使用 Kubernetes 的 resources 结构,即 requests 和 limits 设置。 通过使用 Linux tc 队列将速率限制应用于 Pod。 请注意,根据支持流量整形文档, 该插件被认为是实验性的,在生产环境中使用之前应该进行彻底的测试。

对于存储 QoS,你可能希望创建具有不同性能特征的不同存储类或配置文件。 每个存储配置文件可以与不同的服务层相关联,该服务层针对 IO、冗余或吞吐量等不同的工作负载进行优化。 可能需要额外的逻辑来允许租户将适当的存储配置文件与其工作负载相关联。

最后,还有 Pod 优先级和抢占, 你可以在其中为 Pod 分配优先级值。 在调度 Pod 时,当没有足够的资源来调度分配了较高优先级的 Pod 时, 调度程序将尝试驱逐具有较低优先级的 Pod。 如果你有一个用例,其中租户在共享集群中具有不同的服务层,例如免费和付费, 你可能希望使用此功能为某些层级提供更高的优先级。

DNS

Kubernetes 集群包括一个域名系统(DNS)服务, 可为所有服务和 Pod 提供从名称到 IP 地址的转换。 默认情况下,Kubernetes DNS 服务允许在集群中的所有命名空间中进行查找。

在多租户环境中,租户可以访问 Pod 和其他 Kubernetes 资源, 或者在需要更强隔离的情况下,可能需要阻止 Pod 在其他名称空间中查找服务。 你可以通过为 DNS 服务配置安全规则来限制跨命名空间的 DNS 查找。 例如,CoreDNS(Kubernetes 的默认 DNS 服务)可以利用 Kubernetes 元数据来限制对命名空间内的 Pod 和服务的查询。 有关更多信息,请阅读 CoreDNS 文档中配置此功能的 示例

当使用各租户独立虚拟控制面模型时, 必须为每个租户配置 DNS 服务或必须使用多租户 DNS 服务。参见一个 CoreDNS 的定制版本支持多租户的示例。

Operators

Operator 模式是管理应用的 Kubernetes 控制器。 Operator 可以简化应用的多个实例的管理,例如数据库服务, 这使它们成为多消费者 (SaaS) 多租户用例中的通用构建块。

在多租户环境中使用 Operators 应遵循一套更严格的准则。具体而言,Operator 应:

  • 支持在不同的租户命名空间内创建资源,而不仅仅是在部署 Operator 的命名空间内。
  • 确保 Pod 配置了资源请求和限制,以确保调度和公平。
  • 支持节点隔离、沙箱容器等数据平面隔离技术的 Pod 配置。

实现

为多租户共享 Kubernetes 集群有两种主要方法: 使用命名空间(即每个租户独立的命名空间) 或虚拟化控制平面(即每个租户独立的虚拟控制平面)。

在这两种情况下,还建议对数据平面隔离和其他考虑事项,如 API 优先级和公平性,进行管理。

Kubernetes 很好地支持命名空间隔离,其资源开销可以忽略不计,并提供了允许租户适当交互的机制, 例如允许服务之间的通信。 但是,它可能很难配置,而且不适用于非命名空间作用域的 Kubernetes 资源,例如自定义资源定义、存储类和 Webhook 等。

控制平面虚拟化允许以更高的资源使用率和更困难的跨租户共享为代价隔离非命名空间作用域的资源。 当命名空间隔离不足但不希望使用专用集群时,这是一个不错的选择, 因为维护专用集群的成本很高(尤其是本地集群), 或者由于专用集群的额外开销较高且缺乏资源共享。 但是,即使在虚拟化控制平面中,你也可能会看到使用命名空间的好处。

以下各节将更详细地讨论这两个选项:

每个租户独立的命名空间

如前所述,你应该考虑将每个工作负载隔离在其自己的命名空间中, 即使你使用的是专用集群或虚拟化控制平面。 这可确保每个工作负载只能访问其自己的资源,例如 ConfigMap 和 Secret, 并允许你为每个工作负载定制专用的安全策略。 此外,最佳实践是为整个集群中的每个命名空间名称提供唯一的名称(即,即使它们位于单独的集群中), 因为这使你将来可以灵活地在专用集群和共享集群之间切换, 或者使用多集群工具,例如服务网格。

相反,在租户级别分配命名空间也有优势, 而不仅仅是工作负载级别, 因为通常有一些策略适用于单个租户拥有的所有工作负载。 然而,这种方案也有自己的问题。 首先,这使得为各个工作负载定制策略变得困难或不可能, 其次,确定应该赋予命名空间的单一级别的 “租户” 可能很困难。 例如,一个组织可能有部门、团队和子团队 - 哪些应该分配一个命名空间?

为了解决这个问题,Kubernetes 提供了 Hierarchical Namespace Controller (HNC), 它允许你将多个命名空间组织成层次结构,并在它们之间共享某些策略和资源。 它还可以帮助你管理命名空间标签、命名空间生命周期和委托管理, 并在相关命名空间之间共享资源配额。 这些功能在多团队和多客户场景中都很有用。

下面列出了提供类似功能并有助于管理命名空间资源的其他项目:

多团队租户

多客户租户

策略引擎

策略引擎提供了验证和生成租户配置的特性:

每个租户独立的虚拟控制面

控制面隔离的另一种形式是使用 Kubernetes 扩展为每个租户提供一个虚拟控制面, 以实现集群范围内 API 资源的分段。 数据平面隔离技术可以与此模型一起使用, 以安全地跨多个租户管理工作节点。

基于虚拟控制面的多租户模型通过为每个租户提供专用控制面组件来扩展基于命名空间的多租户, 从而完全控制集群范围的资源和附加服务。 工作节点在所有租户之间共享,并由租户通常无法访问的 Kubernetes 集群管理。 该集群通常被称为 超集群(Super-Cluster)(或有时称为 host-cluster)。 由于租户的控制面不直接与底层计算资源相关联,因此它被称为虚拟控制平面

虚拟控制面通常由 Kubernetes API 服务器、控制器管理器和 etcd 数据存储组成。 它通过元数据同步控制器与超集群交互, 该控制器跨租户控制面和超集群控制面对变化进行协调。

通过使用每个租户单独的专用控制面,可以解决由于所有租户共享一个 API 服务器而导致的大部分隔离问题。 例如,控制平面中的嘈杂邻居、策略错误配置导致的不可控爆炸半径以及如 Webhook 和 CRD 等集群范围对象之间的冲突。 因此,虚拟控制平面模型特别适用于每个租户都需要访问 Kubernetes API 服务器并期望具有完整集群可管理性的情况。

改进的隔离是以每个租户运行和维护一个单独的虚拟控制平面为代价的。 此外,租户层面的控制面不能解决数据面的隔离问题, 例如节点级的嘈杂邻居或安全威胁。这些仍然必须单独解决。

Kubernetes Cluster API - Nested (CAPN) 项目提供了虚拟控制平面的实现。

其他实现

3.8.10 - Kubernetes API 服务器旁路风险

与 API 服务器及其他组件相关的安全架构信息

Kubernetes API 服务器是外部(用户和服务)与集群交互的主要入口。

API 服务器作为交互的主要入口,还提供了几种关键的内置安全控制, 例如审计日志和准入控制器。 但有一些方式可以绕过这些安全控制从而修改集群的配置或内容。

本页描述了绕过 Kubernetes API 服务器中内置安全控制的几种方式, 以便集群运维人员和安全架构师可以确保这些绕过方式被适当地限制。

静态 Pod

每个节点上的 kubelet 会加载并直接管理集群中存储在指定目录中或从特定 URL 获取的静态 Pod 清单。 API 服务器不管理这些静态 Pod。对该位置具有写入权限的攻击者可以修改从该位置加载的静态 Pod 的配置,或引入新的静态 Pod。

静态 Pod 被限制访问 Kubernetes API 中的其他对象。例如,你不能将静态 Pod 配置为从集群挂载 Secret。 但是,这些 Pod 可以执行其他安全敏感的操作,例如挂载来自下层节点的 hostPath 卷。

默认情况下,kubelet 会创建一个镜像 Pod(Mirror Pod), 以便静态 Pod 在 Kubernetes API 中可见。但是,如果攻击者在创建 Pod 时使用了无效的名字空间名称, 则该 Pod 将在 Kubernetes API 中不可见,只能通过对受影响主机有访问权限的工具发现。

如果静态 Pod 无法通过准入控制,kubelet 不会将 Pod 注册到 API 服务器。但该 Pod 仍然在节点上运行。 有关更多信息,请参阅 kubeadm issue #1541

缓解措施

  • 仅在节点需要时启用 kubelet 静态 Pod 清单功能
  • 如果节点使用静态 Pod 功能,请将对静态 Pod 清单目录或 URL 的文件系统的访问权限限制为需要访问的用户。
  • 限制对 kubelet 配置参数和文件的访问,以防止攻击者设置静态 Pod 路径或 URL。
  • 定期审计并集中报告所有对托管静态 Pod 清单和 kubelet 配置文件的目录或 Web 存储位置的访问。

kubelet API

kubelet 提供了一个 HTTP API,通常暴露在集群工作节点上的 TCP 端口 10250 上。 在某些 Kubernetes 发行版中,API 也可能暴露在控制平面节点上。 对 API 的直接访问允许公开有关运行在节点上的 Pod、这些 Pod 的日志以及在节点上运行的每个容器中执行命令的信息。

当 Kubernetes 集群用户具有对 Node 对象子资源 RBAC 访问权限时,该访问权限可用作与 kubelet API 交互的授权。 实际的访问权限取决于授予了哪些子资源访问权限,详见 kubelet 鉴权

对 kubelet API 的直接访问不受准入控制影响,也不会被 Kubernetes 审计日志记录。 能直接访问此 API 的攻击者可能会绕过能检测或防止某些操作的控制机制。

kubelet API 可以配置为以多种方式验证请求。 默认情况下,kubelet 的配置允许匿名访问。大多数 Kubernetes 提供商将默认值更改为使用 Webhook 和证书身份认证。 这使得控制平面能够确保调用者访问 nodes API 资源或子资源是经过授权的。但控制平面不能确保默认的匿名访问也是如此。

缓解措施

  • 使用 RBAC 等机制限制对 nodes API 对象的子资源的访问。 只在有需要时才授予此访问权限,例如监控服务。
  • 限制对 kubelet 端口的访问。只允许指定和受信任的 IP 地址段访问该端口。
  • 确保将 kubelet 身份验证 设置为 Webhook 或证书模式。
  • 确保集群上未启用不作身份认证的“只读” Kubelet 端口。

etcd API

Kubernetes 集群使用 etcd 作为数据存储。etcd 服务监听 TCP 端口 2379。 只有 Kubernetes API 服务器和你所使用的备份工具需要访问此存储。对该 API 的直接访问允许公开或修改集群中保存的数据。

对 etcd API 的访问通常通过客户端证书身份认证来管理。 由 etcd 信任的证书颁发机构所颁发的任何证书都可以完全访问 etcd 中存储的数据。

对 etcd 的直接访问不受 Kubernetes 准入控制的影响,也不会被 Kubernetes 审计日志记录。 具有对 API 服务器的 etcd 客户端证书私钥的读取访问权限(或可以创建一个新的受信任的客户端证书) 的攻击者可以通过访问集群 Secret 或修改访问规则来获得集群管理员权限。 即使不提升其 Kubernetes RBAC 权限,可以修改 etcd 的攻击者也可以在集群内检索所有 API 对象或创建新的工作负载。

许多 Kubernetes 提供商配置 etcd 为使用双向 TLS(客户端和服务器都验证彼此的证书以进行身份验证)。 尽管存在该特性,但目前还没有被广泛接受的 etcd API 鉴权实现。 由于缺少鉴权模型,任何具有对 etcd 的客户端访问权限的证书都可以用于获得对 etcd 的完全访问权限。 通常,仅用于健康检查的 etcd 客户端证书也可以授予完全读写访问权限。

缓解措施

  • 确保 etcd 所信任的证书颁发机构仅用于该服务的身份认证。
  • 控制对 etcd 服务器证书的私钥以及 API 服务器的客户端证书和密钥的访问。
  • 考虑在网络层面限制对 etcd 端口的访问,仅允许来自特定和受信任的 IP 地址段的访问。

容器运行时套接字

在 Kubernetes 集群中的每个节点上,与容器交互的访问都由容器运行时控制。 通常,容器运行时会公开一个 kubelet 可以访问的 UNIX 套接字。 具有此套接字访问权限的攻击者可以启动新容器或与正在运行的容器进行交互。

在集群层面,这种访问造成的影响取决于在受威胁节点上运行的容器是否可以访问 Secret 或其他机密数据, 攻击者可以使用这些机密数据将权限提升到其他工作节点或控制平面组件。

缓解措施

  • 确保严格控制对容器运行时套接字所在的文件系统访问。如果可能,限制为仅 root 用户可访问。
  • 使用 Linux 内核命名空间等机制将 kubelet 与节点上运行的其他组件隔离。
  • 确保限制或禁止使用包含容器运行时套接字的 hostPath 挂载, 无论是直接挂载还是通过挂载父目录挂载。此外,hostPath 挂载必须设置为只读,以降低攻击者绕过目录限制的风险。
  • 限制用户对节点的访问,特别是限制超级用户对节点的访问。

3.8.11 - 安全检查清单

确保 Kubernetes 集群安全的基线检查清单。

本清单旨在提供一个基本的指导列表,其中包含链接,指向各个主题的更为全面的文档。 此清单不求详尽无遗,是预计会不断演化的。

关于如何阅读和使用本文档:

  • 主题的顺序并不代表优先级的顺序。
  • 在每章节的列表下面的段落中,都详细列举了一些检查清项目。

认证和鉴权

  • 在启动后 system:masters 组不用于用户或组件的身份验证。
  • kube-controller-manager 运行时要启用 --use-service-account-credentials 参数。
  • 根证书要受到保护(或离线 CA,或一个具有有效访问控制的托管型在线 CA)。
  • 中级证书和叶子证书的有效期不要超过未来 3 年。
  • 存在定期访问审查的流程,审查间隔不要超过 24 个月。
  • 遵循基于角色的访问控制良好实践, 以获得与身份验证和授权相关的指导。

在启动后,用户和组件都不应以 system:masters 身份向 Kubernetes API 进行身份验证。 同样,应避免将任何 kube-controller-manager 以 system:masters 运行。 事实上,system:masters 应该只用作一个例外机制,而不是管理员用户。

网络安全

  • 使用的 CNI 插件可支持网络策略。
  • 对集群中的所有工作负载应用入站和出站的网络策略。
  • 落实每个名字空间内的默认网络策略,覆盖所有 Pod,拒绝一切访问。
  • 如果合适,使用服务网格来加密集群内的所有通信。
  • 不在互联网上公开 Kubernetes API、kubelet API 和 etcd。
  • 过滤工作负载对云元数据 API 的访问。
  • 限制使用 LoadBalancer 和 ExternalIP。

许多容器网络接口(Container Network Interface,CNI)插件提供了限制 Pod 可能与之通信的网络资源的功能。 这种限制通常通过网络策略来完成, 网络策略提供了一种名字空间作用域的资源来定义规则。 在每个名字空间中,默认的网络策略会阻塞所有的出入站流量,并选择所有 Pod, 采用允许列表的方法很有用,可以确保不遗漏任何工作负载。

并非所有 CNI 插件都在传输过程中提供加密。 如果所选的插件缺少此功能,一种替代方案是可以使用服务网格来提供该功能。

控制平面的 etcd 数据存储应该实施访问限制控制,并且不要在互联网上公开。 此外,应使用双向 TLS(mTLS)与其进行安全通信。 用在这里的证书机构应该仅用于 etcd。

应该限制外部互联网对 Kubernetes API 服务器未公开的 API 的访问。 请小心,因为许多托管的 Kubernetes 发行版在默认情况下公开了 API 服务器。 当然,你可以使用堡垒机访问服务器。

kubelet API 的访问应该受到限制, 并且不公开,当没有使用 --config 参数来设置配置文件时,默认的身份验证和鉴权设置是过于宽松的。

如果使用云服务供应商托管的 Kubernetes,在没有明确需要的情况下, 也应该限制或阻止从 Pod 对云元数据 API 169.254.169.254 的访问,因为这可能泄露信息。

关于限制使用 LoadBalancer 和 ExternalIP 请参阅 CVE-2020-8554:中间人使用 LoadBalancer 或 ExternalIPDenyServiceExternalIPs 准入控制器获取更多信息。

Pod 安全

  • 仅在必要时才授予 createupdatepatchdelete 工作负载的 RBAC 权限。
  • 对所有名字空间实施适当的 Pod 安全标准策略,并强制执行。
  • 为工作负载设置内存限制值,并确保限制值等于或者不高于请求值。
  • 对敏感工作负载可以设置 CPU 限制。
  • 对于支持 Seccomp 的节点,可以为程序启用合适的系统调用配置文件。
  • 对于支持 AppArmor 或 SELinux 的系统,可以为程序启用合适的配置文件。

RBAC 的授权是至关重要的, 但不能在足够细的粒度上对 Pod 的资源进行授权, 也不足以对管理 Pod 的任何资源进行授权。 唯一的粒度是资源本身上的 API 动作,例如,对 Pod 的 create。 在未指定额外许可的情况下,创建这些资源的权限允许直接不受限制地访问集群的可调度节点。

Pod 安全性标准定义了三种不同的策略: 特权策略(Privileged)、基线策略(Baseline)和限制策略(Restricted),它们限制了 PodSpec 中关于安全的字段的设置。 这些标准可以通过默认启用的新的 Pod 安全性准入或第三方准入 Webhook 在名字空间级别强制执行。 请注意,与它所取代的、已被移除的 PodSecurityPolicy 准入机制相反, Pod 安全性准入可以轻松地与准入 Webhook 和外部服务相结合使用。

restricted Pod 安全准入策略是 Pod 安全性标准集中最严格的策略, 可以在多种种模式下运行, 根据最佳安全实践,逐步地采用 warnaudit 或者 enforce 模式以应用最合适的安全上下文(Security Context)。 尽管如此,对于特定的用例,应该单独审查 Pod 的安全上下文, 以限制 Pod 在预定义的安全性标准之上可能具有的特权和访问权限。

有关 Pod 安全性的实践教程, 请参阅博文 Kubernetes 1.23:Pod 安全性升级到 Beta

为了限制一个 Pod 可以使用的内存和 CPU 资源, 应该设置 Pod 在节点上可消费的内存和 CPU 限制, 从而防止来自恶意的或已被攻破的工作负载的潜在 DoS 攻击。这种策略可以由准入控制器强制执行。 请注意,CPU 限制设置可能会影响 CPU 用量,从而可能会对自动扩缩功能或效率产生意外的影响, 换言之,系统会在可用的 CPU 资源下最大限度地运行进程。

启用 Seccomp

Seccomp 代表安全计算模式(Secure computing mode),这是一个自 Linux 内核版本 2.6.12 被加入的特性。 它可以将进程的特权沙箱化,来限制从用户空间发起的对内核的调用。 Kubernetes 允许你将加载到节点上的 Seccomp 配置文件自动应用于你的 Pod 和容器。

Seccomp 通过减少容器内对 Linux 内核的系统调用(System Call)以缩小攻击面,从而提高工作负载的安全性。 Seccomp 过滤器模式借助 BPF 创建具体系统调用的允许清单或拒绝清单,名为配置文件(Profile)。

从 Kubernetes 1.27 开始,你可以将 RuntimeDefault 设置为工作负载的默认 Seccomp 配置。 你可以阅读相应的安全教程。 此外,Kubernetes Security Profiles Operator 是一个方便在集群中管理和使用 Seccomp 的项目。

启用 AppArmor 或 SELinux

AppArmor

AppArmor 是一个 Linux 内核安全模块, 可以提供一种简单的方法来实现强制访问控制(Mandatory Access Control, MAC)并通过系统日志进行更好地审计。 要在 Kubernetes 中启用 AppArmor,至少需要 1.4 版本。 与 Seccomp 一样,AppArmor 也通过配置文件进行配置, 其中每个配置文件要么在强制(Enforcing)模式下运行,即阻止访问不允许的资源,要么在投诉(Complaining)模式下运行,只报告违规行为。 AppArmor 配置文件是通过注解的方式,以容器为粒度强制执行的,允许进程获得刚好合适的权限。

SELinux

SELinux 也是一个 Linux 内核安全模块,可以提供支持访问控制安全策略的机制,包括强制访问控制(MAC)。 SELinux 标签可以通过 securityContext指配给容器或 Pod。

Pod 布局

  • Pod 布局是根据应用程序的敏感级别来完成的。
  • 敏感应用程序在节点上隔离运行或使用特定的沙箱运行时运行。

处于不同敏感级别的 Pod,例如,应用程序 Pod 和 Kubernetes API 服务器,应该部署到不同的节点上。 节点隔离的目的是防止应用程序容器的逃逸,进而直接访问敏感度更高的应用, 甚至轻松地改变集群工作机制。 这种隔离应该被强制执行,以防止 Pod 集合被意外部署到同一节点上。 可以通过以下功能实现:

节点选择器(Node Selector)
作为 Pod 规约的一部分来设置的键值对,指定 Pod 可部署到哪些节点。 通过 PodNodeSelector 准入控制器可以在名字空间和集群级别强制实施节点选择。
PodTolerationRestriction
容忍度准入控制器, 允许管理员设置在名字空间内允许使用的容忍度。 名字空间中的 Pod 只能使用名字空间对象的注解键上所指定的容忍度,这些键提供默认和允许的容忍度集合。
RuntimeClass
RuntimeClass 是一个用于选择容器运行时配置的特性,容器运行时配置用于运行 Pod 中的容器, 并以性能开销为代价提供或多或少的主机隔离能力。

Secrets

  • 不用 ConfigMap 保存机密数据。
  • 为 Secret API 配置静态加密。
  • 如果合适,可以部署和使用一种机制,负责注入保存在第三方存储中的 Secret。
  • 不应该将服务账号令牌挂载到不需要它们的 Pod 中。
  • 使用绑定的服务账号令牌卷, 而不要使用不会过期的令牌。

Pod 所需的秘密信息应该存储在 Kubernetes Secret 中,而不是像 ConfigMap 这样的替代品中。 存储在 etcd 中的 Secret 资源应该被静态加密。

需要 Secret 的 Pod 应该通过卷自动挂载这些信息, 最好使用 emptyDir.medium 选项存储在内存中。 该机制还可以用于从第三方存储中注入 Secret 作为卷,如 Secret Store CSI 驱动。 与通过 RBAC 来允许 Pod 服务账号访问 Secret 相比,应该优先使用上述机制。这种机制允许将 Secret 作为环境变量或文件添加到 Pod 中。 请注意,与带访问权限控制的文件相比,由于日志的崩溃转储,以及 Linux 的环境变量的非机密性,环境变量方法可能更容易发生泄漏。

不应该将服务账号令牌挂载到不需要它们的 Pod 中。这可以通过在服务账号内将 automountServiceAccountToken 设置为 false 来完成整个名字空间范围的配置,或者也可以单独在 Pod 层面定制。 对于 Kubernetes v1.22 及更高版本, 请使用绑定服务账号作为有时间限制的服务账号凭证。

镜像

  • 尽量减少容器镜像中不必要的内容。
  • 容器镜像配置为以非特权用户身份运行。
  • 对容器镜像的引用是通过 Sha256 摘要实现的,而不是标签(tags), 或者通过准入控制器在部署时验证镜像的数字签名来验证镜像的来源。
  • 在创建和部署过程中定期扫描容器镜像,并对已知的漏洞软件进行修补。

容器镜像应该包含运行其所打包的程序所需要的最少内容。 最好,只使用程序及其依赖项,基于最小的基础镜像来构建镜像。 尤其是,在生产中使用的镜像不应包含 Shell 或调试工具, 因为可以使用临时调试容器进行故障排除。

构建镜像时使用 Dockerfile 中的 USER 指令直接开始使用非特权用户。 安全上下文(Security Context) 允许使用 runAsUserrunAsGroup 来指定使用特定的用户和组来启动容器镜像, 即使没有在镜像清单文件(Manifest)中指定这些配置信息。 不过,镜像层中的文件权限设置可能无法做到在不修改镜像的情况下,使用新的非特权用户来启动进程。

避免使用镜像标签来引用镜像,尤其是 latest 标签,因为标签对应的镜像可以在仓库中被轻松地修改。 首选使用完整的 Sha256 摘要,该摘要对特定镜像清单文件而言是唯一的。 可以通过 ImagePolicyWebhook 强制执行此策略。 镜像签名还可以在部署时由准入控制器自动验证, 以验证其真实性和完整性。

扫描容器镜像可以防止关键性的漏洞随着容器镜像一起被部署到集群中。 镜像扫描应在将容器镜像部署到集群之前完成,通常作为 CI/CD 流水线中的部署过程的一部分来完成。 镜像扫描的目的是获取有关容器镜像中可能存在的漏洞及其预防措施的信息, 例如使用公共漏洞评分系统 (Common Vulnerability Scoring System,CVSS)评分。 如果镜像扫描的结果与管道合性规则匹配,则只有经过正确修补的容器镜像才会最终进入生产环境。

准入控制器

  • 选择启用适当的准入控制器。
  • Pod 安全策略由 Pod 安全准入强制执行,或者和 Webhook 准入控制器一起强制执行。
  • 保证准入链插件和 Webhook 的配置都是安全的。

准入控制器可以帮助提高集群的安全性。 然而,由于它们是对 API 服务器的扩展,其自身可能会带来风险, 所以它们应该得到适当的保护

下面列出了一些准入控制器,可以考虑用这些控制器来增强集群和应用程序的安全状况。 列表中包括了可能在本文档其他部分曾提及的控制器。

第一组准入控制器包括默认启用的插件, 除非你知道自己在做什么,否则请考虑保持它们处于被启用的状态:

CertificateApproval
执行额外的授权检查,以确保审批用户具有审批证书请求的权限。
CertificateSigning
执行其他授权检查,以确保签名用户具有签名证书请求的权限。
CertificateSubjectRestriction
拒绝将 group(或 organization attribute)设置为 system:masters 的所有证书请求。
LimitRanger
强制执行 LimitRange API 约束。
MutatingAdmissionWebhook
允许通过 Webhook 使用自定义控制器,这些控制器可能会变更它所审查的请求。
PodSecurity
Pod Security Policy 的替代品,用于约束所部署 Pod 的安全上下文。
ResourceQuota
强制执行资源配额,以防止资源被过度使用。
ValidatingAdmissionWebhook
允许通过 Webhook 使用自定义控制器,这些控制器不变更它所审查的请求。

第二组包括默认情况下没有启用、但处于正式发布状态的插件,建议启用这些插件以改善你的安全状况:

DenyServiceExternalIPs
拒绝使用 Service.spec.externalIPs 字段,已有的 Service 不受影响,新增或者变更时不允许使用。 这是 CVE-2020-8554:中间人使用 LoadBalancer 或 ExternalIP 的缓解措施。
NodeRestriction
将 kubelet 的权限限制为只能修改其拥有的 Pod API 资源或代表其自身的节点 API 资源。 此插件还可以防止 kubelet 使用 node-restriction.kubernetes.io/ 注解, 攻击者可以使用该注解来访问 kubelet 的凭证,从而影响所控制的节点上的 Pod 布局。

第三组包括默认情况下未启用,但可以考虑在某些场景下启用的插件:

AlwaysPullImages
强制使用最新版本标记的镜像,并确保部署者有权使用该镜像。
ImagePolicyWebhook
允许通过 Webhook 对镜像强制执行额外的控制。

接下来

3.9 - 策略

通过策略管理安全性和最佳实践。

Kubernetes 策略是管理其他配置或运行时行为的一些配置。 Kubernetes 提供了各种形式的策略,具体如下所述:

使用 API 对象应用策略

一些 API 对象可用作策略。以下是一些示例:

使用准入控制器应用策略

准入控制器运行在 API 服务器上, 可以验证或变更 API 请求。某些准入控制器用于应用策略。 例如,AlwaysPullImages 准入控制器会修改新 Pod,将镜像拉取策略设置为 Always

Kubernetes 具有多个内置的准入控制器,可通过 API 服务器的 --enable-admission-plugins 标志进行配置。

关于准入控制器的详细信息(包括可用准入控制器的完整列表),请查阅专门的章节:

使用 ValidatingAdmissionPolicy 应用策略

验证性的准入策略允许使用通用表达式语言 (CEL) 在 API 服务器中执行可配置的验证检查。 例如,ValidatingAdmissionPolicy 可用于禁止使用 latest 镜像标签。

ValidatingAdmissionPolicy 对请求 API 进行操作,可就不合规的配置执行阻止、审计和警告用户等操作。 有关 ValidatingAdmissionPolicy API 的详细信息及示例,请查阅专门的章节:

使用动态准入控制应用策略

动态准入控制器(或准入 Webhook)作为单独的应用在 API 服务器之外运行, 这些应用注册自身后可以接收 Webhook 请求以便对 API 请求进行验证或变更。

动态准入控制器可用于在 API 请求上应用策略并触发其他基于策略的工作流。 动态准入控制器可以执行一些复杂的检查,包括需要读取其他集群资源和外部数据的复杂检查。 例如,镜像验证检查可以从 OCI 镜像仓库中查找数据,以验证容器镜像签名和证明信息。

有关动态准入控制的详细信息,请查阅专门的章节:

实现

Kubernetes 生态系统中正在开发作为灵活策略引擎的动态准入控制器,例如:

使用 Kubelet 配置应用策略

Kubernetes 允许在每个工作节点上配置 Kubelet。一些 Kubelet 配置可以视为策略:

3.9.1 - 限制范围

默认情况下, Kubernetes 集群上的容器运行使用的计算资源没有限制。 使用 Kubernetes 资源配额, 管理员(也称为 集群操作者)可以在一个指定的命名空间内限制集群资源的使用与创建。 在命名空间中,一个 Pod 最多能够使用命名空间的资源配额所定义的 CPU 和内存用量。 作为集群操作者或命名空间级的管理员,你可能也会担心如何确保一个 Pod 不会垄断命名空间内所有可用的资源。

LimitRange 是限制命名空间内可为每个适用的对象类别 (例如 Pod 或 PersistentVolumeClaim) 指定的资源分配量(限制和请求)的策略对象。

一个 LimitRange(限制范围) 对象提供的限制能够做到:

  • 在一个命名空间中实施对每个 Pod 或 Container 最小和最大的资源使用量的限制。
  • 在一个命名空间中实施对每个 PersistentVolumeClaim 能申请的最小和最大的存储空间大小的限制。
  • 在一个命名空间中实施对一种资源的申请值和限制值的比值的控制。
  • 设置一个命名空间中对计算资源的默认申请/限制值,并且自动的在运行时注入到多个 Container 中。

当某命名空间中有一个 LimitRange 对象时,将在该命名空间中实施 LimitRange 限制。

LimitRange 的名称必须是合法的 DNS 子域名

资源限制和请求的约束

  • 管理员在一个命名空间内创建一个 LimitRange 对象。
  • 用户在此命名空间内创建(或尝试创建) Pod 和 PersistentVolumeClaim 等对象。
  • 首先,LimitRange 准入控制器对所有没有设置计算资源需求的所有 Pod(及其容器)设置默认请求值与限制值。
  • 其次,LimitRange 跟踪其使用量以保证没有超出命名空间中存在的任意 LimitRange 所定义的最小、最大资源使用量以及使用量比值。
  • 若尝试创建或更新的对象(Pod 和 PersistentVolumeClaim)违反了 LimitRange 的约束, 向 API 服务器的请求会失败,并返回 HTTP 状态码 403 Forbidden 以及描述哪一项约束被违反的消息。
  • 若你在命名空间中添加 LimitRange 启用了对 cpumemory 等计算相关资源的限制, 你必须指定这些值的请求使用量与限制使用量。否则,系统将会拒绝创建 Pod。
  • LimitRange 的验证仅在 Pod 准入阶段进行,不对正在运行的 Pod 进行验证。 如果你添加或修改 LimitRange,命名空间中已存在的 Pod 将继续不变。
  • 如果命名空间中存在两个或更多 LimitRange 对象,应用哪个默认值是不确定的。

Pod 的 LimitRange 和准入检查

LimitRange 检查所应用的默认值的一致性。 这意味着 LimitRange 设置的 limit 的默认值可能小于客户端提交给 API 服务器的规约中为容器指定的 request 值。 如果发生这种情况,最终 Pod 将无法调度。

例如,你使用如下清单定义一个 LimitRange

apiVersion: v1
kind: LimitRange
metadata:
  name: cpu-resource-constraint
spec:
  limits:
  - default: # 此处定义默认限制值
      cpu: 500m
    defaultRequest: # 此处定义默认请求值
      cpu: 500m
    max: # max 和 min 定义限制范围
      cpu: "1"
    min:
      cpu: 100m
    type: Container

以及一个声明 CPU 资源请求为 700m 但未声明限制值的 Pod:

apiVersion: v1
kind: Pod
metadata:
  name: example-conflict-with-limitrange-cpu
spec:
  containers:
  - name: demo
    image: registry.k8s.io/pause:2.0
    resources:
      requests:
        cpu: 700m

那么该 Pod 将不会被调度,失败并出现类似以下的错误:

Pod "example-conflict-with-limitrange-cpu" is invalid: spec.containers[0].resources.requests: Invalid value: "700m": must be less than or equal to cpu limit

如果你同时设置了 requestlimit,那么即使使用相同的 LimitRange,新 Pod 也会被成功调度:

apiVersion: v1
kind: Pod
metadata:
  name: example-no-conflict-with-limitrange-cpu
spec:
  containers:
  - name: demo
    image: registry.k8s.io/pause:2.0
    resources:
      requests:
        cpu: 700m
      limits:
        cpu: 700m

资源约束示例

能够使用限制范围创建的策略示例有:

  • 在一个有两个节点,8 GiB 内存与16个核的集群中,限制一个命名空间的 Pod 申请 100m 单位,最大 500m 单位的 CPU,以及申请 200Mi,最大 600Mi 的内存。
  • 为 spec 中没有 cpu 和内存需求值的 Container 定义默认 CPU 限制值与需求值 150m,内存默认需求值 300Mi。

在命名空间的总限制值小于 Pod 或 Container 的限制值的总和的情况下,可能会产生资源竞争。 在这种情况下,将不会创建 Container 或 Pod。

竞争和对 LimitRange 的改变都不会影响任何已经创建了的资源。

接下来

关于使用限值的例子,可参阅:

有关上下文和历史信息,请参阅 LimitRanger 设计文档

3.9.2 - 资源配额

当多个用户或团队共享具有固定节点数目的集群时,人们会担心有人使用超过其基于公平原则所分配到的资源量。

资源配额是帮助管理员解决这一问题的工具。

资源配额,通过 ResourceQuota 对象来定义,对每个命名空间的资源消耗总量提供限制。 它可以限制命名空间中某种类型的对象的总数目上限,也可以限制命名空间中的 Pod 可以使用的计算资源的总上限。

资源配额的工作方式如下:

  • 不同的团队可以在不同的命名空间下工作。这可以通过 RBAC 强制执行。

  • 集群管理员可以为每个命名空间创建一个或多个 ResourceQuota 对象。

  • 当用户在命名空间下创建资源(如 Pod、Service 等)时,Kubernetes 的配额系统会跟踪集群的资源使用情况, 以确保使用的资源用量不超过 ResourceQuota 中定义的硬性资源限额。

  • 如果资源创建或者更新请求违反了配额约束,那么该请求会报错(HTTP 403 FORBIDDEN), 并在消息中给出有可能违反的约束。

  • 如果命名空间下的计算资源 (如 cpumemory)的配额被启用, 则用户必须为这些资源设定请求值(request)和约束值(limit),否则配额系统将拒绝 Pod 的创建。 提示: 可使用 LimitRanger 准入控制器来为没有设置计算资源需求的 Pod 设置默认值。

    若想避免这类问题,请参考 演练示例。

ResourceQuota 对象的名称必须是合法的 DNS 子域名

下面是使用命名空间和配额构建策略的示例:

  • 在具有 32 GiB 内存和 16 核 CPU 资源的集群中,允许 A 团队使用 20 GiB 内存 和 10 核的 CPU 资源, 允许 B 团队使用 10 GiB 内存和 4 核的 CPU 资源,并且预留 2 GiB 内存和 2 核的 CPU 资源供将来分配。
  • 限制 "testing" 命名空间使用 1 核 CPU 资源和 1GiB 内存。允许 "production" 命名空间使用任意数量。

在集群容量小于各命名空间配额总和的情况下,可能存在资源竞争。资源竞争时,Kubernetes 系统会遵循先到先得的原则。

不管是资源竞争还是配额的修改,都不会影响已经创建的资源使用对象。

启用资源配额

资源配额的支持在很多 Kubernetes 版本中是默认启用的。 当 API 服务器 的命令行标志 --enable-admission-plugins= 中包含 ResourceQuota 时, 资源配额会被启用。

当命名空间中存在一个 ResourceQuota 对象时,对于该命名空间而言,资源配额就是开启的。

计算资源配额

用户可以对给定命名空间下的可被请求的 计算资源 总量进行限制。

配额机制所支持的资源类型:

资源名称描述
limits.cpu所有非终止状态的 Pod,其 CPU 限额总量不能超过该值。
limits.memory所有非终止状态的 Pod,其内存限额总量不能超过该值。
requests.cpu所有非终止状态的 Pod,其 CPU 需求总量不能超过该值。
requests.memory所有非终止状态的 Pod,其内存需求总量不能超过该值。
hugepages-<size>对于所有非终止状态的 Pod,针对指定尺寸的巨页请求总数不能超过此值。
cpurequests.cpu 相同。
memoryrequests.memory 相同。

扩展资源的资源配额

除上述资源外,在 Kubernetes 1.10 版本中,还添加了对 扩展资源 的支持。

由于扩展资源不可超量分配,因此没有必要在配额中为同一扩展资源同时指定 requestslimits。 对于扩展资源而言,目前仅允许使用前缀为 requests. 的配额项。

以 GPU 拓展资源为例,如果资源名称为 nvidia.com/gpu,并且要将命名空间中请求的 GPU 资源总数限制为 4,则可以如下定义配额:

  • requests.nvidia.com/gpu: 4

有关更多详细信息,请参阅查看和设置配额

存储资源配额

用户可以对给定命名空间下的存储资源 总量进行限制。

此外,还可以根据相关的存储类(Storage Class)来限制存储资源的消耗。

资源名称描述
requests.storage所有 PVC,存储资源的需求总量不能超过该值。
persistentvolumeclaims在该命名空间中所允许的 PVC 总量。
<storage-class-name>.storageclass.storage.k8s.io/requests.storage在所有与 <storage-class-name> 相关的持久卷申领中,存储请求的总和不能超过该值。
<storage-class-name>.storageclass.storage.k8s.io/persistentvolumeclaims在与 storage-class-name 相关的所有持久卷申领中,命名空间中可以存在的持久卷申领总数。

例如,如果一个操作人员针对 gold 存储类型与 bronze 存储类型设置配额, 操作人员可以定义如下配额:

  • gold.storageclass.storage.k8s.io/requests.storage: 500Gi
  • bronze.storageclass.storage.k8s.io/requests.storage: 100Gi

在 Kubernetes 1.8 版本中,本地临时存储的配额支持已经是 Alpha 功能:

资源名称描述
requests.ephemeral-storage在命名空间的所有 Pod 中,本地临时存储请求的总和不能超过此值。
limits.ephemeral-storage在命名空间的所有 Pod 中,本地临时存储限制值的总和不能超过此值。
ephemeral-storagerequests.ephemeral-storage 相同。

对象数量配额

你可以使用以下语法对所有标准的、命名空间域的资源类型进行配额设置:

  • count/<resource>.<group>:用于非核心(core)组的资源
  • count/<resource>:用于核心组的资源

这是用户可能希望利用对象计数配额来管理的一组资源示例。

  • count/persistentvolumeclaims
  • count/services
  • count/secrets
  • count/configmaps
  • count/replicationcontrollers
  • count/deployments.apps
  • count/replicasets.apps
  • count/statefulsets.apps
  • count/jobs.batch
  • count/cronjobs.batch

相同语法也可用于自定义资源。 例如,要对 example.com API 组中的自定义资源 widgets 设置配额,请使用 count/widgets.example.com

当使用 count/* 资源配额时,如果对象存在于服务器存储中,则会根据配额管理资源。 这些类型的配额有助于防止存储资源耗尽。例如,用户可能想根据服务器的存储能力来对服务器中 Secret 的数量进行配额限制。 集群中存在过多的 Secret 实际上会导致服务器和控制器无法启动。 用户可以选择对 Job 进行配额管理,以防止配置不当的 CronJob 在某命名空间中创建太多 Job 而导致集群拒绝服务。

对有限的一组资源上实施一般性的对象数量配额也是可能的。

支持以下类型:

资源名称描述
configmaps在该命名空间中允许存在的 ConfigMap 总数上限。
persistentvolumeclaims在该命名空间中允许存在的 PVC 的总数上限。
pods在该命名空间中允许存在的非终止状态的 Pod 总数上限。Pod 终止状态等价于 Pod 的 .status.phase in (Failed, Succeeded) 为真。
replicationcontrollers在该命名空间中允许存在的 ReplicationController 总数上限。
resourcequotas在该命名空间中允许存在的 ResourceQuota 总数上限。
services在该命名空间中允许存在的 Service 总数上限。
services.loadbalancers在该命名空间中允许存在的 LoadBalancer 类型的 Service 总数上限。
services.nodeports在该命名空间中允许存在的 NodePort 类型的 Service 总数上限。
secrets在该命名空间中允许存在的 Secret 总数上限。

例如,pods 配额统计某个命名空间中所创建的、非终止状态的 Pod 个数并确保其不超过某上限值。 用户可能希望在某命名空间中设置 pods 配额,以避免有用户创建很多小的 Pod, 从而耗尽集群所能提供的 Pod IP 地址。

配额作用域

每个配额都有一组相关的 scope(作用域),配额只会对作用域内的资源生效。 配额机制仅统计所列举的作用域的交集中的资源用量。

当一个作用域被添加到配额中后,它会对作用域相关的资源数量作限制。 如配额中指定了允许(作用域)集合之外的资源,会导致验证错误。

作用域描述
Terminating匹配所有 spec.activeDeadlineSeconds 不小于 0 的 Pod。
NotTerminating匹配所有 spec.activeDeadlineSeconds 是 nil 的 Pod。
BestEffort匹配所有 Qos 是 BestEffort 的 Pod。
NotBestEffort匹配所有 Qos 不是 BestEffort 的 Pod。
PriorityClass匹配所有引用了所指定的优先级类的 Pods。
CrossNamespacePodAffinity匹配那些设置了跨名字空间 (反)亲和性条件的 Pod。

BestEffort 作用域限制配额跟踪以下资源:

  • pods

TerminatingNotTerminatingNotBestEffortPriorityClass 这些作用域限制配额跟踪以下资源:

  • pods
  • cpu
  • memory
  • requests.cpu
  • requests.memory
  • limits.cpu
  • limits.memory

需要注意的是,你不可以在同一个配额对象中同时设置 TerminatingNotTerminating 作用域,你也不可以在同一个配额中同时设置 BestEffortNotBestEffort 作用域。

scopeSelector 支持在 operator 字段中使用以下值:

  • In
  • NotIn
  • Exists
  • DoesNotExist

定义 scopeSelector 时,如果使用以下值之一作为 scopeName 的值,则对应的 operator 只能是 Exists

  • Terminating
  • NotTerminating
  • BestEffort
  • NotBestEffort

如果 operatorInNotIn 之一,则 values 字段必须至少包含一个值。 例如:

  scopeSelector:
    matchExpressions:
      - scopeName: PriorityClass
        operator: In
        values:
          - middle

如果 operatorExistsDoesNotExist,则可以设置 values 字段。

基于优先级类(PriorityClass)来设置资源配额

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

Pod 可以创建为特定的优先级。 通过使用配额规约中的 scopeSelector 字段,用户可以根据 Pod 的优先级控制其系统资源消耗。

仅当配额规范中的 scopeSelector 字段选择到某 Pod 时,配额机制才会匹配和计量 Pod 的资源消耗。

如果配额对象通过 scopeSelector 字段设置其作用域为优先级类, 则配额对象只能跟踪以下资源:

  • pods
  • cpu
  • memory
  • ephemeral-storage
  • limits.cpu
  • limits.memory
  • limits.ephemeral-storage
  • requests.cpu
  • requests.memory
  • requests.ephemeral-storage

本示例创建一个配额对象,并将其与具有特定优先级的 Pod 进行匹配。 该示例的工作方式如下:

  • 集群中的 Pod 可取三个优先级类之一,即 "low"、"medium"、"high"。
  • 为每个优先级创建一个配额对象。

将以下 YAML 保存到文件 quota.yml 中。

apiVersion: v1
kind: List
items:
- apiVersion: v1
  kind: ResourceQuota
  metadata:
    name: pods-high
  spec:
    hard:
      cpu: "1000"
      memory: 200Gi
      pods: "10"
    scopeSelector:
      matchExpressions:
      - operator: In
        scopeName: PriorityClass
        values: ["high"]
- apiVersion: v1
  kind: ResourceQuota
  metadata:
    name: pods-medium
  spec:
    hard:
      cpu: "10"
      memory: 20Gi
      pods: "10"
    scopeSelector:
      matchExpressions:
      - operator: In
        scopeName: PriorityClass
        values: ["medium"]
- apiVersion: v1
  kind: ResourceQuota
  metadata:
    name: pods-low
  spec:
    hard:
      cpu: "5"
      memory: 10Gi
      pods: "10"
    scopeSelector:
      matchExpressions:
      - operator: In
        scopeName: PriorityClass
        values: ["low"]

使用 kubectl create 命令运行以下操作。

kubectl create -f ./quota.yml
resourcequota/pods-high created
resourcequota/pods-medium created
resourcequota/pods-low created

使用 kubectl describe quota 操作验证配额的 Used 值为 0

kubectl describe quota
Name:       pods-high
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     1k
memory      0     200Gi
pods        0     10


Name:       pods-low
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     5
memory      0     10Gi
pods        0     10


Name:       pods-medium
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     10
memory      0     20Gi
pods        0     10

创建优先级为 "high" 的 Pod。 将以下 YAML 保存到文件 high-priority-pod.yml 中。

apiVersion: v1
kind: Pod
metadata:
  name: high-priority
spec:
  containers:
  - name: high-priority
    image: ubuntu
    command: ["/bin/sh"]
    args: ["-c", "while true; do echo hello; sleep 10;done"]
    resources:
      requests:
        memory: "10Gi"
        cpu: "500m"
      limits:
        memory: "10Gi"
        cpu: "500m"
  priorityClassName: high

使用 kubectl create 运行以下操作。

kubectl create -f ./high-priority-pod.yml

确认 "high" 优先级配额 pods-high 的 "Used" 统计信息已更改,并且其他两个配额未更改。

kubectl describe quota
Name:       pods-high
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         500m  1k
memory      10Gi  200Gi
pods        1     10


Name:       pods-low
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     5
memory      0     10Gi
pods        0     10


Name:       pods-medium
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     10
memory      0     20Gi
pods        0     10

跨名字空间的 Pod 亲和性配额

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

集群运维人员可以使用 CrossNamespacePodAffinity 配额作用域来限制哪个名字空间中可以存在包含跨名字空间亲和性规则的 Pod。 更为具体一点,此作用域用来配置哪些 Pod 可以在其 Pod 亲和性规则中设置 namespacesnamespaceSelector 字段。

禁止用户使用跨名字空间的亲和性规则可能是一种被需要的能力, 因为带有反亲和性约束的 Pod 可能会阻止所有其他名字空间的 Pod 被调度到某失效域中。

使用此作用域操作符可以避免某些名字空间(例如下面例子中的 foo-ns)运行特别的 Pod, 这类 Pod 使用跨名字空间的 Pod 亲和性约束,在该名字空间中创建了作用域为 CrossNamespacePodAffinity 的、硬性约束为 0 的资源配额对象。

apiVersion: v1
kind: ResourceQuota
metadata:
  name: disable-cross-namespace-affinity
  namespace: foo-ns
spec:
  hard:
    pods: "0"
  scopeSelector:
    matchExpressions:
    - scopeName: CrossNamespacePodAffinity
      operator: Exists

如果集群运维人员希望默认禁止使用 namespacesnamespaceSelector, 而仅仅允许在特定名字空间中这样做,他们可以将 CrossNamespacePodAffinity 作为一个被约束的资源。方法是为 kube-apiserver 设置标志 --admission-control-config-file,使之指向如下的配置文件:

apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: "ResourceQuota"
  configuration:
    apiVersion: apiserver.config.k8s.io/v1
    kind: ResourceQuotaConfiguration
    limitedResources:
    - resource: pods
      matchScopes:
      - scopeName: CrossNamespacePodAffinity
        operator: Exists

基于上面的配置,只有名字空间中包含作用域为 CrossNamespacePodAffinity 且硬性约束大于或等于使用 namespacesnamespaceSelector 字段的 Pod 个数时,才可以在该名字空间中继续创建在其 Pod 亲和性规则中设置 namespacesnamespaceSelector 的新 Pod。

请求与限制的比较

分配计算资源时,每个容器可以为 CPU 或内存指定请求和约束。 配额可以针对二者之一进行设置。

如果配额中指定了 requests.cpurequests.memory 的值,则它要求每个容器都显式给出对这些资源的请求。 同理,如果配额中指定了 limits.cpulimits.memory 的值,那么它要求每个容器都显式设定对应资源的限制。

查看和设置配额

Kubectl 支持创建、更新和查看配额:

kubectl create namespace myspace
cat <<EOF > compute-resources.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: compute-resources
spec:
  hard:
    requests.cpu: "1"
    requests.memory: 1Gi
    limits.cpu: "2"
    limits.memory: 2Gi
    requests.nvidia.com/gpu: 4
EOF
kubectl create -f ./compute-resources.yaml --namespace=myspace
cat <<EOF > object-counts.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: object-counts
spec:
  hard:
    configmaps: "10"
    persistentvolumeclaims: "4"
    pods: "4"
    replicationcontrollers: "20"
    secrets: "10"
    services: "10"
    services.loadbalancers: "2"
EOF
kubectl create -f ./object-counts.yaml --namespace=myspace
kubectl get quota --namespace=myspace
NAME                    AGE
compute-resources       30s
object-counts           32s
kubectl describe quota compute-resources --namespace=myspace
Name:                    compute-resources
Namespace:               myspace
Resource                 Used  Hard
--------                 ----  ----
limits.cpu               0     2
limits.memory            0     2Gi
requests.cpu             0     1
requests.memory          0     1Gi
requests.nvidia.com/gpu  0     4
kubectl describe quota object-counts --namespace=myspace
Name:                   object-counts
Namespace:              myspace
Resource                Used    Hard
--------                ----    ----
configmaps              0       10
persistentvolumeclaims  0       4
pods                    0       4
replicationcontrollers  0       20
secrets                 1       10
services                0       10
services.loadbalancers  0       2

kubectl 还使用语法 count/<resource>.<group> 支持所有标准的、命名空间域的资源的对象计数配额:

kubectl create namespace myspace
kubectl create quota test --hard=count/deployments.apps=2,count/replicasets.apps=4,count/pods=3,count/secrets=4 --namespace=myspace
kubectl create deployment nginx --image=nginx --namespace=myspace --replicas=2
kubectl describe quota --namespace=myspace
Name:                         test
Namespace:                    myspace
Resource                      Used  Hard
--------                      ----  ----
count/deployments.apps        1     2
count/pods                    2     3
count/replicasets.apps        1     4
count/secrets                 1     4

配额和集群容量

ResourceQuota 与集群资源总量是完全独立的。它们通过绝对的单位来配置。 所以,为集群添加节点时,资源配额不会自动赋予每个命名空间消耗更多资源的能力。

有时可能需要资源配额支持更复杂的策略,比如:

  • 在几个团队中按比例划分总的集群资源。
  • 允许每个租户根据需要增加资源使用量,但要有足够的限制以防止资源意外耗尽。
  • 探测某个命名空间的需求,添加物理节点并扩大资源配额值。

这些策略可以通过将资源配额作为一个组成模块、手动编写一个控制器来监控资源使用情况, 并结合其他信号调整命名空间上的硬性资源配额来实现。

注意:资源配额对集群资源总体进行划分,但它对节点没有限制:来自不同命名空间的 Pod 可能在同一节点上运行。

默认情况下限制特定优先级的资源消耗

有时候可能希望当且仅当某名字空间中存在匹配的配额对象时,才可以创建特定优先级 (例如 "cluster-services")的 Pod。

通过这种机制,操作人员能够限制某些高优先级类仅出现在有限数量的命名空间中, 而并非每个命名空间默认情况下都能够使用这些优先级类。

要实现此目的,应设置 kube-apiserver 的标志 --admission-control-config-file 指向如下配置文件:

apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: "ResourceQuota"
  configuration:
    apiVersion: apiserver.config.k8s.io/v1
    kind: ResourceQuotaConfiguration
    limitedResources:
    - resource: pods
      matchScopes:
      - scopeName: PriorityClass
        operator: In
        values: ["cluster-services"]

现在在 kube-system 名字空间中创建一个资源配额对象:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: pods-cluster-services
spec:
  scopeSelector:
    matchExpressions:
      - operator : In
        scopeName: PriorityClass
        values: ["cluster-services"]
kubectl apply -f https://k8s.io/examples/policy/priority-class-resourcequota.yaml -n kube-system
resourcequota/pods-cluster-services created

在这里,当以下条件满足时可以创建 Pod:

  1. Pod 未设置 priorityClassName
  2. Pod 的 priorityClassName 设置值不是 cluster-services
  3. Pod 的 priorityClassName 设置值为 cluster-services,它将被创建于 kube-system 名字空间中,并且它已经通过了资源配额检查。

如果 Pod 的 priorityClassName 设置为 cluster-services,但要被创建到 kube-system 之外的别的名字空间,则 Pod 创建请求也被拒绝。

接下来

3.9.3 - 进程 ID 约束与预留

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

Kubernetes 允许你限制一个 Pod 中可以使用的进程 ID(PID)数目。 你也可以为每个节点预留一定数量的可分配的 PID, 供操作系统和守护进程(而非 Pod)使用。

进程 ID(PID)是节点上的一种基础资源。很容易就会在尚未超出其它资源约束的时候就已经触及任务个数上限, 进而导致宿主机器不稳定。

集群管理员需要一定的机制来确保集群中运行的 Pod 不会导致 PID 资源枯竭, 甚而造成宿主机上的守护进程(例如 kubelet 或者 kube-proxy 乃至包括容器运行时本身)无法正常运行。 此外,确保 Pod 中 PID 的个数受限对于保证其不会影响到同一节点上其它负载也很重要。

你可以配置 kubelet 限制给定 Pod 能够使用的 PID 个数。 例如,如果你的节点上的宿主操作系统被设置为最多可使用 262144 个 PID, 同时预期节点上会运行的 Pod 个数不会超过 250,那么你可以为每个 Pod 设置 1000 个 PID 的预算,避免耗尽该节点上可用 PID 的总量。 如果管理员系统像 CPU 或内存那样允许对 PID 进行过量分配(Overcommit),他们也可以这样做, 只是会有一些额外的风险。不管怎样,任何一个 Pod 都不可以将整个机器的运行状态破坏。 这类资源限制有助于避免简单的派生炸弹(Fork Bomb)影响到整个集群的运行。

在 Pod 级别设置 PID 限制使得管理员能够保护 Pod 之间不会互相伤害, 不过无法确保所有调度到该宿主机器上的所有 Pod 都不会影响到节点整体。 Pod 级别的限制也无法保护节点代理任务自身不会受到 PID 耗尽的影响。

你也可以预留一定量的 PID,作为节点的额外开销,与分配给 Pod 的 PID 集合独立。 这有点类似于在给操作系统和其它设施预留 CPU、内存或其它资源时所做的操作, 这些任务都在 Pod 及其所包含的容器之外运行。

PID 限制是与计算资源 请求和限制相辅相成的一种机制。不过,你需要用一种不同的方式来设置这一限制: 你需要将其设置到 kubelet 上而不是在 Pod 的 .spec 中为 Pod 设置资源限制。 目前还不支持在 Pod 级别设置 PID 限制。

节点级别 PID 限制

Kubernetes 允许你为系统预留一定量的进程 ID。为了配置预留数量,你可以使用 kubelet 的 --system-reserved--kube-reserved 命令行选项中的参数 pid=<number>。你所设置的参数值分别用来声明为整个系统和 Kubernetes 系统守护进程所保留的进程 ID 数目。

Pod 级别 PID 限制

Kubernetes 允许你限制 Pod 中运行的进程个数。你可以在节点级别设置这一限制, 而不是为特定的 Pod 来将其设置为资源限制。每个节点都可以有不同的 PID 限制设置。 要设置限制值,你可以设置 kubelet 的命令行参数 --pod-max-pids,或者在 kubelet 的配置文件中设置 PodPidsLimit

基于 PID 的驱逐

你可以配置 kubelet 使之在 Pod 行为不正常或者消耗不正常数量资源的时候将其终止。这一特性称作驱逐。 你可以针对不同的驱逐信号配置资源不足的处理。 使用 pid.available 驱逐信号来配置 Pod 使用的 PID 个数的阈值。 你可以设置硬性的和软性的驱逐策略。不过,即使使用硬性的驱逐策略, 如果 PID 个数增长过快,节点仍然可能因为触及节点 PID 限制而进入一种不稳定状态。 驱逐信号的取值是周期性计算的,而不是一直能够强制实施约束。

Pod 级别和节点级别的 PID 限制会设置硬性限制。 一旦触及限制值,工作负载会在尝试获得新的 PID 时开始遇到问题。 这可能会也可能不会导致 Pod 被重新调度,取决于工作负载如何应对这类失败以及 Pod 的存活性和就绪态探测是如何配置的。 可是,如果限制值被正确设置,你可以确保其它 Pod 负载和系统进程不会因为某个 Pod 行为不正常而没有 PID 可用。

接下来

3.9.4 - 节点资源管理器

Kubernetes 提供了一组资源管理器,用于支持延迟敏感的、高吞吐量的工作负载。 资源管理器的目标是协调和优化节点资源,以支持对 CPU、设备和内存(巨页)等资源有特殊需求的 Pod。

主管理器,也叫拓扑管理器(Topology Manager),是一个 Kubelet 组件, 它通过策略, 协调全局的资源管理过程。

各个管理器的配置方式会在专项文档中详细阐述:

3.10 - 调度、抢占和驱逐

在 Kubernetes 中,调度(scheduling)指的是确保 Pod 匹配到合适的节点, 以便 kubelet 能够运行它们。抢占(Preemption)指的是终止低优先级的 Pod 以便高优先级的 Pod 可以调度运行的过程。驱逐(Eviction)是在资源匮乏的节点上,主动让一个或多个 Pod 失效的过程。

在 Kubernetes 中,调度(scheduling)指的是确保 Pod 匹配到合适的节点, 以便 kubelet 能够运行它们。 抢占(Preemption)指的是终止低优先级的 Pod 以便高优先级的 Pod 可以调度运行的过程。 驱逐(Eviction)是在资源匮乏的节点上,主动让一个或多个 Pod 失效的过程。

调度

Pod 干扰

Pod 干扰 是指节点上的 Pod 被自愿或非自愿终止的过程。

自愿干扰是由应用程序所有者或集群管理员有意启动的。非自愿干扰是无意的, 可能由不可避免的问题触发,如节点耗尽资源或意外删除。

3.10.1 - Kubernetes 调度器

在 Kubernetes 中,调度是指将 Pod 放置到合适的节点上,以便对应节点上的 Kubelet 能够运行这些 Pod。

调度概览

调度器通过 Kubernetes 的监测(Watch)机制来发现集群中新创建且尚未被调度到节点上的 Pod。 调度器会将所发现的每一个未调度的 Pod 调度到一个合适的节点上来运行。 调度器会依据下文的调度原则来做出调度选择。

如果你想要理解 Pod 为什么会被调度到特定的节点上, 或者你想要尝试实现一个自定义的调度器,这篇文章将帮助你了解调度。

kube-scheduler

kube-scheduler 是 Kubernetes 集群的默认调度器,并且是集群 控制面 的一部分。 如果你真得希望或者有这方面的需求,kube-scheduler 在设计上允许你自己编写一个调度组件并替换原有的 kube-scheduler。

Kube-scheduler 选择一个最佳节点来运行新创建的或尚未调度(unscheduled)的 Pod。 由于 Pod 中的容器和 Pod 本身可能有不同的要求,调度程序会过滤掉任何不满足 Pod 特定调度需求的节点。 或者,API 允许你在创建 Pod 时为它指定一个节点,但这并不常见,并且仅在特殊情况下才会这样做。

在一个集群中,满足一个 Pod 调度请求的所有节点称之为可调度节点。 如果没有任何一个节点能满足 Pod 的资源请求, 那么这个 Pod 将一直停留在未调度状态直到调度器能够找到合适的 Node。

调度器先在集群中找到一个 Pod 的所有可调度节点,然后根据一系列函数对这些可调度节点打分, 选出其中得分最高的节点来运行 Pod。之后,调度器将这个调度决定通知给 kube-apiserver,这个过程叫做绑定

在做调度决定时需要考虑的因素包括:单独和整体的资源请求、硬件/软件/策略限制、 亲和以及反亲和要求、数据局部性、负载间的干扰等等。

kube-scheduler 中的节点选择

kube-scheduler 给一个 Pod 做调度选择时包含两个步骤:

  1. 过滤
  2. 打分

过滤阶段会将所有满足 Pod 调度需求的节点选出来。 例如,PodFitsResources 过滤函数会检查候选节点的可用资源能否满足 Pod 的资源请求。 在过滤之后,得出一个节点列表,里面包含了所有可调度节点;通常情况下, 这个节点列表包含不止一个节点。如果这个列表是空的,代表这个 Pod 不可调度。

在打分阶段,调度器会为 Pod 从所有可调度节点中选取一个最合适的节点。 根据当前启用的打分规则,调度器会给每一个可调度节点进行打分。

最后,kube-scheduler 会将 Pod 调度到得分最高的节点上。 如果存在多个得分最高的节点,kube-scheduler 会从中随机选取一个。

支持以下两种方式配置调度器的过滤和打分行为:

  1. 调度策略 允许你配置过滤所用的 断言(Predicates) 和打分所用的 优先级(Priorities)
  2. 调度配置 允许你配置实现不同调度阶段的插件, 包括:QueueSortFilterScoreBindReservePermit 等等。 你也可以配置 kube-scheduler 运行不同的配置文件。

接下来

3.10.2 - 将 Pod 指派给节点

你可以约束一个 Pod 以便限制其只能在特定的节点上运行, 或优先在特定的节点上运行。有几种方法可以实现这点,推荐的方法都是用 标签选择算符来进行选择。 通常这样的约束不是必须的,因为调度器将自动进行合理的放置(比如,将 Pod 分散到节点上, 而不是将 Pod 放置在可用资源不足的节点上等等)。但在某些情况下,你可能需要进一步控制 Pod 被部署到哪个节点。例如,确保 Pod 最终落在连接了 SSD 的机器上, 或者将来自两个不同的服务且有大量通信的 Pod 被放置在同一个可用区。

你可以使用下列方法中的任何一种来选择 Kubernetes 对特定 Pod 的调度:

节点标签

与很多其他 Kubernetes 对象类似,节点也有标签。 你可以手动地添加标签。 Kubernetes 也会为集群中所有节点添加一些标准的标签

节点隔离/限制

通过为节点添加标签,你可以准备让 Pod 调度到特定节点或节点组上。 你可以使用这个功能来确保特定的 Pod 只能运行在具有一定隔离性、安全性或监管属性的节点上。

如果使用标签来实现节点隔离,建议选择节点上的 kubelet 无法修改的标签键。 这可以防止受感染的节点在自身上设置这些标签,进而影响调度器将工作负载调度到受感染的节点。

NodeRestriction 准入插件防止 kubelet 使用 node-restriction.kubernetes.io/ 前缀设置或修改标签。

要使用该标签前缀进行节点隔离:

  1. 确保你在使用节点鉴权机制并且已经启用了 NodeRestriction 准入插件
  2. 将带有 node-restriction.kubernetes.io/ 前缀的标签添加到 Node 对象, 然后在节点选择算符中使用这些标签。 例如,example.com.node-restriction.kubernetes.io/fips=trueexample.com.node-restriction.kubernetes.io/pci-dss=true

nodeSelector

nodeSelector 是节点选择约束的最简单推荐形式。你可以将 nodeSelector 字段添加到 Pod 的规约中设置你希望目标节点所具有的节点标签。 Kubernetes 只会将 Pod 调度到拥有你所指定的每个标签的节点上。

进一步的信息可参见将 Pod 指派给节点

亲和性与反亲和性

nodeSelector 提供了一种最简单的方法来将 Pod 约束到具有特定标签的节点上。 亲和性和反亲和性扩展了你可以定义的约束类型。使用亲和性与反亲和性的一些好处有:

  • 亲和性、反亲和性语言的表达能力更强。nodeSelector 只能选择拥有所有指定标签的节点。 亲和性、反亲和性为你提供对选择逻辑的更强控制能力。
  • 你可以标明某规则是“软需求”或者“偏好”,这样调度器在无法找到匹配节点时仍然调度该 Pod。
  • 你可以使用节点上(或其他拓扑域中)运行的其他 Pod 的标签来实施调度约束, 而不是只能使用节点本身的标签。这个能力让你能够定义规则允许哪些 Pod 可以被放置在一起。

亲和性功能由两种类型的亲和性组成:

  • 节点亲和性功能类似于 nodeSelector 字段,但它的表达能力更强,并且允许你指定软规则。
  • Pod 间亲和性/反亲和性允许你根据其他 Pod 的标签来约束 Pod。

节点亲和性

节点亲和性概念上类似于 nodeSelector, 它使你可以根据节点上的标签来约束 Pod 可以调度到哪些节点上。 节点亲和性有两种:

  • requiredDuringSchedulingIgnoredDuringExecution: 调度器只有在规则被满足的时候才能执行调度。此功能类似于 nodeSelector, 但其语法表达能力更强。
  • preferredDuringSchedulingIgnoredDuringExecution: 调度器会尝试寻找满足对应规则的节点。如果找不到匹配的节点,调度器仍然会调度该 Pod。

你可以使用 Pod 规约中的 .spec.affinity.nodeAffinity 字段来设置节点亲和性。 例如,考虑下面的 Pod 规约:

apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: topology.kubernetes.io/zone
            operator: In
            values:
            - antarctica-east1
            - antarctica-west1
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: another-node-label-key
            operator: In
            values:
            - another-node-label-value
  containers:
  - name: with-node-affinity
    image: registry.k8s.io/pause:2.0

在这一示例中,所应用的规则如下:

  • 节点必须包含一个键名为 topology.kubernetes.io/zone 的标签, 并且该标签的取值必须antarctica-east1antarctica-west1
  • 节点最好具有一个键名为 another-node-label-key 且取值为 another-node-label-value 的标签。

你可以使用 operator 字段来为 Kubernetes 设置在解释规则时要使用的逻辑操作符。 你可以使用 InNotInExistsDoesNotExistGtLt 之一作为操作符。

阅读操作符了解有关这些操作的更多信息。

NotInDoesNotExist 可用来实现节点反亲和性行为。 你也可以使用节点污点 将 Pod 从特定节点上驱逐。

参阅使用节点亲和性来为 Pod 指派节点, 以了解进一步的信息。

节点亲和性权重

你可以为 preferredDuringSchedulingIgnoredDuringExecution 亲和性类型的每个实例设置 weight 字段,其取值范围是 1 到 100。 当调度器找到能够满足 Pod 的其他调度请求的节点时,调度器会遍历节点满足的所有的偏好性规则, 并将对应表达式的 weight 值加和。

最终的加和值会添加到该节点的其他优先级函数的评分之上。 在调度器为 Pod 作出调度决定时,总分最高的节点的优先级也最高。

例如,考虑下面的 Pod 规约:

apiVersion: v1
kind: Pod
metadata:
  name: with-affinity-anti-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/os
            operator: In
            values:
            - linux
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: label-1
            operator: In
            values:
            - key-1
      - weight: 50
        preference:
          matchExpressions:
          - key: label-2
            operator: In
            values:
            - key-2
  containers:
  - name: with-node-affinity
    image: registry.k8s.io/pause:2.0

如果存在两个候选节点,都满足 preferredDuringSchedulingIgnoredDuringExecution 规则, 其中一个节点具有标签 label-1:key-1,另一个节点具有标签 label-2:key-2, 调度器会考察各个节点的 weight 取值,并将该权重值添加到节点的其他得分值之上,

逐个调度方案中设置节点亲和性

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

在配置多个调度方案时, 你可以将某个方案与节点亲和性关联起来,如果某个调度方案仅适用于某组特殊的节点时, 这样做是很有用的。 要实现这点,可以在调度器配置中为 NodeAffinity 插件args 字段添加 addedAffinity。例如:

apiVersion: kubescheduler.config.k8s.io/v1beta3
kind: KubeSchedulerConfiguration

profiles:
  - schedulerName: default-scheduler
  - schedulerName: foo-scheduler
    pluginConfig:
      - name: NodeAffinity
        args:
          addedAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
              nodeSelectorTerms:
              - matchExpressions:
                - key: scheduler-profile
                  operator: In
                  values:
                  - foo

这里的 addedAffinity 除遵从 Pod 规约中设置的节点亲和性之外, 还适用于将 .spec.schedulerName 设置为 foo-scheduler。 换言之,为了匹配 Pod,节点需要满足 addedAffinity 和 Pod 的 .spec.NodeAffinity

由于 addedAffinity 对最终用户不可见,其行为可能对用户而言是出乎意料的。 应该使用与调度方案名称有明确关联的节点标签。

Pod 间亲和性与反亲和性

Pod 间亲和性与反亲和性使你可以基于已经在节点上运行的 Pod 的标签来约束 Pod 可以调度到的节点,而不是基于节点上的标签。

Pod 间亲和性与反亲和性的规则格式为“如果 X 上已经运行了一个或多个满足规则 Y 的 Pod, 则这个 Pod 应该(或者在反亲和性的情况下不应该)运行在 X 上”。 这里的 X 可以是节点、机架、云提供商可用区或地理区域或类似的拓扑域, Y 则是 Kubernetes 尝试满足的规则。

你通过标签选择算符 的形式来表达规则(Y),并可根据需要指定选关联的名字空间列表。 Pod 在 Kubernetes 中是名字空间作用域的对象,因此 Pod 的标签也隐式地具有名字空间属性。 针对 Pod 标签的所有标签选择算符都要指定名字空间,Kubernetes 会在指定的名字空间内寻找标签。

你会通过 topologyKey 来表达拓扑域(X)的概念,其取值是系统用来标示域的节点标签键。 相关示例可参见常用标签、注解和污点

Pod 间亲和性与反亲和性的类型

节点亲和性类似,Pod 的亲和性与反亲和性也有两种类型:

  • requiredDuringSchedulingIgnoredDuringExecution
  • preferredDuringSchedulingIgnoredDuringExecution

例如,你可以使用 requiredDuringSchedulingIgnoredDuringExecution 亲和性来告诉调度器, 将两个服务的 Pod 放到同一个云提供商可用区内,因为它们彼此之间通信非常频繁。 类似地,你可以使用 preferredDuringSchedulingIgnoredDuringExecution 反亲和性来将同一服务的多个 Pod 分布到多个云提供商可用区中。

要使用 Pod 间亲和性,可以使用 Pod 规约中的 .affinity.podAffinity 字段。 对于 Pod 间反亲和性,可以使用 Pod 规约中的 .affinity.podAntiAffinity 字段。

调度一组具有 Pod 间亲和性的 Pod

如果当前正被调度的 Pod 在具有自我亲和性的 Pod 序列中排在第一个, 那么只要它满足其他所有的亲和性规则,它就可以被成功调度。 这是通过以下方式确定的:确保集群中没有其他 Pod 与此 Pod 的名字空间和标签选择算符匹配; 该 Pod 满足其自身定义的条件,并且选定的节点满足所指定的所有拓扑要求。 这确保即使所有的 Pod 都配置了 Pod 间亲和性,也不会出现调度死锁的情况。

Pod 亲和性示例

考虑下面的 Pod 规约:

apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - S1
        topologyKey: topology.kubernetes.io/zone
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: security
              operator: In
              values:
              - S2
          topologyKey: topology.kubernetes.io/zone
  containers:
  - name: with-pod-affinity
    image: registry.k8s.io/pause:2.0

本示例定义了一条 Pod 亲和性规则和一条 Pod 反亲和性规则。Pod 亲和性规则配置为 requiredDuringSchedulingIgnoredDuringExecution,而 Pod 反亲和性配置为 preferredDuringSchedulingIgnoredDuringExecution

亲和性规则规定,只有节点属于特定的区域 且该区域中的其他 Pod 已打上 security=S1 标签时,调度器才可以将示例 Pod 调度到此节点上。 例如,如果我们有一个具有指定区域(称之为 "Zone V")的集群,此区域由带有 topology.kubernetes.io/zone=V 标签的节点组成,那么只要 Zone V 内已经至少有一个 Pod 打了 security=S1 标签, 调度器就可以将此 Pod 调度到 Zone V 内的任何节点。相反,如果 Zone V 中没有带有 security=S1 标签的 Pod, 则调度器不会将示例 Pod 调度给该区域中的任何节点。

反亲和性规则规定,如果节点属于特定的区域 且该区域中的其他 Pod 已打上 security=S2 标签,则调度器应尝试避免将 Pod 调度到此节点上。 例如,如果我们有一个具有指定区域(我们称之为 "Zone R")的集群,此区域由带有 topology.kubernetes.io/zone=R 标签的节点组成,只要 Zone R 内已经至少有一个 Pod 打了 security=S2 标签, 调度器应避免将 Pod 分配给 Zone R 内的任何节点。相反,如果 Zone R 中没有带有 security=S2 标签的 Pod, 则反亲和性规则不会影响将 Pod 调度到 Zone R。

查阅设计文档 以进一步熟悉 Pod 亲和性与反亲和性的示例。

你可以针对 Pod 间亲和性与反亲和性为其 operator 字段使用 InNotInExistsDoesNotExist 等值。

阅读操作符了解有关这些操作的更多信息。

原则上,topologyKey 可以是任何合法的标签键。出于性能和安全原因,topologyKey 有一些限制:

  • 对于 Pod 亲和性而言,在 requiredDuringSchedulingIgnoredDuringExecutionpreferredDuringSchedulingIgnoredDuringExecution 中,topologyKey 不允许为空。
  • 对于 requiredDuringSchedulingIgnoredDuringExecution 要求的 Pod 反亲和性, 准入控制器 LimitPodHardAntiAffinityTopology 要求 topologyKey 只能是 kubernetes.io/hostname。如果你希望使用其他定制拓扑逻辑, 你可以更改准入控制器或者禁用之。

除了 labelSelectortopologyKey,你也可以指定 labelSelector 要匹配的名字空间列表,方法是在 labelSelectortopologyKey 所在层同一层次上设置 namespaces。 如果 namespaces 被忽略或者为空,则默认为 Pod 亲和性/反亲和性的定义所在的名字空间。

名字空间选择算符

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

用户也可以使用 namespaceSelector 选择匹配的名字空间,namespaceSelector 是对名字空间集合进行标签查询的机制。 亲和性条件会应用到 namespaceSelector 所选择的名字空间和 namespaces 字段中所列举的名字空间之上。 注意,空的 namespaceSelector{})会匹配所有名字空间,而 null 或者空的 namespaces 列表以及 null 值 namespaceSelector 意味着“当前 Pod 的名字空间”。

matchLabelKeys

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

Kubernetes 在 Pod 亲和性或反亲和性中包含一个可选的 matchLabelKeys 字段。 此字段指定了应与传入 Pod 的标签匹配的标签键,以满足 Pod 的(反)亲和性。

这些键用于从 Pod 的标签中查找值;这些键值标签与使用 labelSelector 字段定义的匹配限制组合(使用 AND 操作)。 这种组合的过滤机制选择将用于 Pod(反)亲和性计算的现有 Pod 集合。

一个常见的用例是在 matchLabelKeys 中使用 pod-template-hash (设置在作为 Deployment 的一部分进行管理的 Pod 上,其中每个版本的值是唯一的)。 在 matchLabelKeys 中使用 pod-template-hash 允许你定位与传入 Pod 相同版本的 Pod, 确保滚动升级不会破坏亲和性。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: application-server
...
spec:
  template:
    spec:
      affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - database
            topologyKey: topology.kubernetes.io/zone
            # 只有在计算 Pod 亲和性时,才考虑指定上线的 Pod。
            # 如果你更新 Deployment,替代的 Pod 将遵循它们自己的亲和性规则
            # (如果在新的 Pod 模板中定义了任何规则)。
            matchLabelKeys:
            - pod-template-hash

mismatchLabelKeys

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

Kubernetes 为 Pod 亲和性或反亲和性提供了一个可选的 mismatchLabelKeys 字段。 此字段指定了在满足 Pod(反)亲和性时,应与传入 Pod 的标签匹配的键。

一个示例用例是确保 Pod 进入指定的拓扑域(节点、区域等),在此拓扑域中只调度来自同一租户或团队的 Pod。 换句话说,你想要避免在同一拓扑域中同时运行来自两个不同租户的 Pod。

apiVersion: v1
kind: Pod
metadata:
  labels:
    # 假设所有相关的 Pod 都设置了 “tenant” 标签
    tenant: tenant-a
...
spec:
  affinity:
    podAffinity:      
      requiredDuringSchedulingIgnoredDuringExecution:
      # 确保与此租户关联的 Pod 落在正确的节点池上
      - matchLabelKeys:
          - tenant
        topologyKey: node-pool
    podAntiAffinity:  
      requiredDuringSchedulingIgnoredDuringExecution:
      # 确保与此租户关联的 Pod 不能调度到用于其他租户的节点上
      - mismatchLabelKeys:
        - tenant # 无论此 Pod 的 “tenant” 标签的值是什么,
                 # 如果节点池中有来自别的租户的任何 Pod 在运行,
                 # 都会阻碍此 Pod 被调度到这些节点池中的节点上
        labelSelector:
          # 我们必须有一个 labelSelector,只选择具有 “tenant” 标签的 Pod,
          # 否则此 Pod 也会与来自 DaemonSet 的 Pod 发生冲突,
          # 而这些 Pod 不应该具有 “tenant” 标签
          matchExpressions:
          - key: tenant
            operator: Exists
        topologyKey: node-pool

更实际的用例

Pod 间亲和性与反亲和性在与更高级别的集合(例如 ReplicaSet、StatefulSet、 Deployment 等)一起使用时,它们可能更加有用。 这些规则使得你可以配置一组工作负载,使其位于所定义的同一拓扑中; 例如优先将两个相关的 Pod 置于相同的节点上。

以一个三节点的集群为例。你使用该集群运行一个带有内存缓存(例如 Redis)的 Web 应用程序。 在此例中,还假设 Web 应用程序和内存缓存之间的延迟应尽可能低。 你可以使用 Pod 间的亲和性和反亲和性来尽可能地将该 Web 服务器与缓存并置。

在下面的 Redis 缓存 Deployment 示例中,副本上设置了标签 app=storepodAntiAffinity 规则告诉调度器避免将多个带有 app=store 标签的副本部署到同一节点上。 因此,每个独立节点上会创建一个缓存实例。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-cache
spec:
  selector:
    matchLabels:
      app: store
  replicas: 3
  template:
    metadata:
      labels:
        app: store
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - store
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: redis-server
        image: redis:3.2-alpine

下例的 Deployment 为 Web 服务器创建带有标签 app=web-store 的副本。 Pod 亲和性规则告诉调度器将每个副本放到存在标签为 app=store 的 Pod 的节点上。 Pod 反亲和性规则告诉调度器决不要在单个节点上放置多个 app=web-store 服务器。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-server
spec:
  selector:
    matchLabels:
      app: web-store
  replicas: 3
  template:
    metadata:
      labels:
        app: web-store
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - web-store
            topologyKey: "kubernetes.io/hostname"
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - store
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: web-app
        image: nginx:1.16-alpine

创建前面两个 Deployment 会产生如下的集群布局,每个 Web 服务器与一个缓存实例并置, 并分别运行在三个独立的节点上。

node-1node-2node-3
webserver-1webserver-2webserver-3
cache-1cache-2cache-3

总体效果是每个缓存实例都非常可能被在同一个节点上运行的某个客户端访问, 这种方法旨在最大限度地减少偏差(负载不平衡)和延迟。

你可能还有使用 Pod 反亲和性的一些其他原因。 参阅 ZooKeeper 教程 了解一个 StatefulSet 的示例,该 StatefulSet 配置了反亲和性以实现高可用, 所使用的是与此例相同的技术。

nodeName

nodeName 是比亲和性或者 nodeSelector 更为直接的形式。nodeName 是 Pod 规约中的一个字段。如果 nodeName 字段不为空,调度器会忽略该 Pod, 而指定节点上的 kubelet 会尝试将 Pod 放到该节点上。 使用 nodeName 规则的优先级会高于使用 nodeSelector 或亲和性与非亲和性的规则。

使用 nodeName 来选择节点的方式有一些局限性:

  • 如果所指代的节点不存在,则 Pod 无法运行,而且在某些情况下可能会被自动删除。
  • 如果所指代的节点无法提供用来运行 Pod 所需的资源,Pod 会失败, 而其失败原因中会给出是否因为内存或 CPU 不足而造成无法运行。
  • 在云环境中的节点名称并不总是可预测的,也不总是稳定的。

下面是一个使用 nodeName 字段的 Pod 规约示例:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx
  nodeName: kube-01

上面的 Pod 只能运行在节点 kube-01 之上。

Pod 拓扑分布约束

你可以使用 拓扑分布约束(Topology Spread Constraints) 来控制 Pod 在集群内故障域之间的分布, 故障域的示例有区域(Region)、可用区(Zone)、节点和其他用户自定义的拓扑域。 这样做有助于提升性能、实现高可用或提升资源利用率。

阅读 Pod 拓扑分布约束 以进一步了解这些约束的工作方式。

操作符

下面是你可以在上述 nodeAffinitypodAffinityoperator 字段中可以使用的所有逻辑运算符。

操作符行为
In标签值存在于提供的字符串集中
NotIn标签值不包含在提供的字符串集中
Exists对象上存在具有此键的标签
DoesNotExist对象上不存在具有此键的标签

以下操作符只能与 nodeAffinity 一起使用。

操作符行为
Gt提供的值将被解析为整数,并且该整数小于通过解析此选择算符命名的标签的值所得到的整数
Lt提供的值将被解析为整数,并且该整数大于通过解析此选择算符命名的标签的值所得到的整数

接下来

3.10.3 - Pod 开销

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

在节点上运行 Pod 时,Pod 本身占用大量系统资源。这些是运行 Pod 内容器所需资源之外的资源。 在 Kubernetes 中,POD 开销 是一种方法,用于计算 Pod 基础设施在容器请求和限制之上消耗的资源。

在 Kubernetes 中,Pod 的开销是根据与 Pod 的 RuntimeClass 相关联的开销在准入时设置的。

在调度 Pod 时,除了考虑容器资源请求的总和外,还要考虑 Pod 开销。 类似地,kubelet 将在确定 Pod cgroups 的大小和执行 Pod 驱逐排序时也会考虑 Pod 开销。

配置 Pod 开销

你需要确保使用一个定义了 overhead 字段的 RuntimeClass

使用示例

要使用 Pod 开销,你需要一个定义了 overhead 字段的 RuntimeClass。 例如,你可以使用以下 RuntimeClass 定义,其中使用了一个虚拟化容器运行时(在这个例子中,Kata Containers 与 Firecracker 虚拟机监视器结合使用), 每个 Pod 使用大约 120MiB 的虚拟机和寄宿操作系统:

# 你需要修改这个示例以匹配实际的运行时名称,
# 以及在你的集群中运行时在 Pod 层面增加的资源开销。
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  name: kata-fc
handler: kata-fc
overhead:
  podFixed:
    memory: "120Mi"
    cpu: "250m"

通过指定 kata-fc RuntimeClass 处理程序创建的工作负载会将内存和 CPU 开销计入资源配额计算、节点调度以及 Pod cgroup 尺寸确定。

假设我们运行下面给出的工作负载示例 test-pod:

apiVersion: v1
kind: Pod
metadata:
  name: test-pod
spec:
  runtimeClassName: kata-fc
  containers:
  - name: busybox-ctr
    image: busybox:1.28
    stdin: true
    tty: true
    resources:
      limits:
        cpu: 500m
        memory: 100Mi
  - name: nginx-ctr
    image: nginx
    resources:
      limits:
        cpu: 1500m
        memory: 100Mi

在准入阶段 RuntimeClass 准入控制器 更新工作负载的 PodSpec 以包含 RuntimeClass 中定义的 overhead。如果 PodSpec 中已定义该字段,该 Pod 将会被拒绝。 在这个例子中,由于只指定了 RuntimeClass 名称,所以准入控制器更新了 Pod,使之包含 overhead

在 RuntimeClass 准入控制器进行修改后,你可以查看更新后的 Pod 开销值:

kubectl get pod test-pod -o jsonpath='{.spec.overhead}'

输出:

map[cpu:250m memory:120Mi]

如果定义了 ResourceQuota, 则容器请求的总量以及 overhead 字段都将计算在内。

当 kube-scheduler 决定在哪一个节点调度运行新的 Pod 时,调度器会兼顾该 Pod 的 overhead 以及该 Pod 的容器请求总量。在这个示例中,调度器将资源请求和开销相加, 然后寻找具备 2.25 CPU 和 320 MiB 内存可用的节点。

一旦 Pod 被调度到了某个节点, 该节点上的 kubelet 将为该 Pod 新建一个 cgroup。 底层容器运行时将在这个 Pod 中创建容器。

如果该资源对每一个容器都定义了一个限制(定义了限制值的 Guaranteed QoS 或者 Burstable QoS),kubelet 会为与该资源(CPU 的 cpu.cfs_quota_us 以及内存的 memory.limit_in_bytes) 相关的 Pod cgroup 设定一个上限。该上限基于 PodSpec 中定义的容器限制总量与 overhead 之和。

对于 CPU,如果 Pod 的 QoS 是 Guaranteed 或者 Burstable,kubelet 会基于容器请求总量与 PodSpec 中定义的 overhead 之和设置 cpu.shares

请看这个例子,验证工作负载的容器请求:

kubectl get pod test-pod -o jsonpath='{.spec.containers[*].resources.limits}'

容器请求总计 2000m CPU 和 200MiB 内存:

map[cpu: 500m memory:100Mi] map[cpu:1500m memory:100Mi]

对照从节点观察到的情况来检查一下:

kubectl describe node | grep test-pod -B2

该输出显示请求了 2250m CPU 以及 320MiB 内存。请求包含了 Pod 开销在内:

  Namespace    Name       CPU Requests  CPU Limits   Memory Requests  Memory Limits  AGE
  ---------    ----       ------------  ----------   ---------------  -------------  ---
  default      test-pod   2250m (56%)   2250m (56%)  320Mi (1%)       320Mi (1%)     36m

验证 Pod cgroup 限制

在工作负载所运行的节点上检查 Pod 的内存 cgroups。在接下来的例子中, 将在该节点上使用具备 CRI 兼容的容器运行时命令行工具 crictl。 这是一个显示 Pod 开销行为的高级示例, 预计用户不需要直接在节点上检查 cgroups。 首先在特定的节点上确定该 Pod 的标识符:

# 在该 Pod 被调度到的节点上执行如下命令:
POD_ID="$(sudo crictl pods --name test-pod -q)"

可以依此判断该 Pod 的 cgroup 路径:

# 在该 Pod 被调度到的节点上执行如下命令:
sudo crictl inspectp -o=json $POD_ID | grep cgroupsPath

执行结果的 cgroup 路径中包含了该 Pod 的 pause 容器。Pod 级别的 cgroup 在即上一层目录。

  "cgroupsPath": "/kubepods/podd7f4b509-cf94-4951-9417-d1087c92a5b2/7ccf55aee35dd16aca4189c952d83487297f3cd760f1bbf09620e206e7d0c27a"

在这个例子中,该 Pod 的 cgroup 路径是 kubepods/podd7f4b509-cf94-4951-9417-d1087c92a5b2。 验证内存的 Pod 级别 cgroup 设置:

# 在该 Pod 被调度到的节点上执行这个命令。
# 另外,修改 cgroup 的名称以匹配为该 Pod 分配的 cgroup。
 cat /sys/fs/cgroup/memory/kubepods/podd7f4b509-cf94-4951-9417-d1087c92a5b2/memory.limit_in_bytes

和预期的一样,这一数值为 320 MiB。

335544320

可观察性

kube-state-metrics 中可以通过 kube_pod_overhead_* 指标来协助确定何时使用 Pod 开销, 以及协助观察以一个既定开销运行的工作负载的稳定性。 该特性在 kube-state-metrics 的 1.9 发行版本中不可用,不过预计将在后续版本中发布。 在此之前,用户需要从源代码构建 kube-state-metrics。

接下来

3.10.4 - Pod 调度就绪态

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

Pod 一旦创建就被认为准备好进行调度。 Kubernetes 调度程序尽职尽责地寻找节点来放置所有待处理的 Pod。 然而,在实际环境中,会有一些 Pod 可能会长时间处于"缺少必要资源"状态。 这些 Pod 实际上以一种不必要的方式扰乱了调度器(以及 Cluster AutoScaler 这类下游的集成方)。

通过指定或删除 Pod 的 .spec.schedulingGates,可以控制 Pod 何时准备好被纳入考量进行调度。

配置 Pod schedulingGates

schedulingGates 字段包含一个字符串列表,每个字符串文字都被视为 Pod 在被认为可调度之前应该满足的标准。 该字段只能在创建 Pod 时初始化(由客户端创建,或在准入期间更改)。 创建后,每个 schedulingGate 可以按任意顺序删除,但不允许添加新的调度门控。

pod-scheduling-gates-diagram

图:Pod SchedulingGates

用法示例

要将 Pod 标记为未准备好进行调度,你可以在创建 Pod 时附带一个或多个调度门控,如下所示:

apiVersion: v1
kind: Pod
metadata:
  name: test-pod
spec:
  schedulingGates:
  - name: foo
  - name: bar
  containers:
  - name: pause
    image: registry.k8s.io/pause:3.6

Pod 创建后,你可以使用以下方法检查其状态:

kubectl get pod test-pod

输出显示它处于 SchedulingGated 状态:

NAME       READY   STATUS            RESTARTS   AGE
test-pod   0/1     SchedulingGated   0          7s

你还可以通过运行以下命令检查其 schedulingGates 字段:

kubectl get pod test-pod -o jsonpath='{.spec.schedulingGates}'

输出是:

[{"name":"example.com/foo"},{"name":"example.com/bar"}]

要通知调度程序此 Pod 已准备好进行调度,你可以通过重新应用修改后的清单来完全删除其 schedulingGates

apiVersion: v1
kind: Pod
metadata:
  name: test-pod
spec:
  containers:
  - name: pause
    image: registry.k8s.io/pause:3.6

你可以通过运行以下命令检查 schedulingGates 是否已被清空:

kubectl get pod test-pod -o jsonpath='{.spec.schedulingGates}'

预计输出为空,你可以通过运行下面的命令来检查它的最新状态:

kubectl get pod test-pod -o wide

鉴于 test-pod 不请求任何 CPU/内存资源,预计此 Pod 的状态会从之前的 SchedulingGated 转变为 Running

NAME       READY   STATUS    RESTARTS   AGE   IP         NODE  
test-pod   1/1     Running   0          15s   10.0.0.4   node-2

可观测性

指标 scheduler_pending_pods 带有一个新标签 "gated", 以区分 Pod 是否已尝试调度但被宣称不可调度,或明确标记为未准备好调度。 你可以使用 scheduler_pending_pods{queue="gated"} 来检查指标结果。

可变 Pod 调度指令

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

当 Pod 具有调度门控时,你可以在某些约束条件下改变 Pod 的调度指令。 在高层次上,你只能收紧 Pod 的调度指令。换句话说,更新后的指令将导致 Pod 只能被调度到它之前匹配的节点子集上。 更具体地说,更新 Pod 的调度指令的规则如下:

  1. 对于 .spec.nodeSelector,只允许增加。如果原来未设置,则允许设置此字段。

  2. 对于 spec.affinity.nodeAffinity,如果当前值为 nil,则允许设置为任意值。

  1. 如果 NodeSelectorTerms 之前为空,则允许设置该字段。 如果之前不为空,则仅允许增加 NodeSelectorRequirementsmatchExpressionsfieldExpressions,且不允许更改当前的 matchExpressionsfieldExpressions。 这是因为 .requiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms 中的条目被执行逻辑或运算,而 nodeSelectorTerms[].matchExpressionsnodeSelectorTerms[].fieldExpressions 中的表达式被执行逻辑与运算。
  1. 对于 .preferredDuringSchedulingIgnoredDuringExecution,所有更新都被允许。 这是因为首选条目不具有权威性,因此策略控制器不会验证这些条目。

接下来

3.10.5 - Pod 拓扑分布约束

你可以使用 拓扑分布约束(Topology Spread Constraints) 来控制 Pod 在集群内故障域之间的分布, 例如区域(Region)、可用区(Zone)、节点和其他用户自定义拓扑域。 这样做有助于实现高可用并提升资源利用率。

你可以将集群级约束设为默认值,或为个别工作负载配置拓扑分布约束。

动机

假设你有一个最多包含二十个节点的集群,你想要运行一个自动扩缩的 工作负载,请问要使用多少个副本? 答案可能是最少 2 个 Pod,最多 15 个 Pod。 当只有 2 个 Pod 时,你倾向于这 2 个 Pod 不要同时在同一个节点上运行: 你所遭遇的风险是如果放在同一个节点上且单节点出现故障,可能会让你的工作负载下线。

除了这个基本的用法之外,还有一些高级的使用案例,能够让你的工作负载受益于高可用性并提高集群利用率。

随着你的工作负载扩容,运行的 Pod 变多,将需要考虑另一个重要问题。 假设你有 3 个节点,每个节点运行 5 个 Pod。这些节点有足够的容量能够运行许多副本; 但与这个工作负载互动的客户端分散在三个不同的数据中心(或基础设施可用区)。 现在你可能不太关注单节点故障问题,但你会注意到延迟高于自己的预期, 在不同的可用区之间发送网络流量会产生一些网络成本。

你决定在正常运营时倾向于将类似数量的副本调度 到每个基础设施可用区,且你想要该集群在遇到问题时能够自愈。

Pod 拓扑分布约束使你能够以声明的方式进行配置。

topologySpreadConstraints 字段

Pod API 包括一个 spec.topologySpreadConstraints 字段。这个字段的用法如下所示:

---
apiVersion: v1
kind: Pod
metadata:
  name: example-pod
spec:
  # 配置一个拓扑分布约束
  topologySpreadConstraints:
    - maxSkew: <integer>
      minDomains: <integer> # 可选;自从 v1.25 开始成为 Beta
      topologyKey: <string>
      whenUnsatisfiable: <string>
      labelSelector: <object>
      matchLabelKeys: <list> # 可选;自从 v1.27 开始成为 Beta
      nodeAffinityPolicy: [Honor|Ignore] # 可选;自从 v1.26 开始成为 Beta
      nodeTaintsPolicy: [Honor|Ignore] # 可选;自从 v1.26 开始成为 Beta
  ### 其他 Pod 字段置于此处

你可以运行 kubectl explain Pod.spec.topologySpreadConstraints 或参阅 Pod API 参考的调度一节, 了解有关此字段的更多信息。

分布约束定义

你可以定义一个或多个 topologySpreadConstraints 条目以指导 kube-scheduler 如何将每个新来的 Pod 与跨集群的现有 Pod 相关联。这些字段包括:

  • maxSkew 描述这些 Pod 可能被不均匀分布的程度。你必须指定此字段且该数值必须大于零。 其语义将随着 whenUnsatisfiable 的值发生变化:

    • 如果你选择 whenUnsatisfiable: DoNotSchedule,则 maxSkew 定义目标拓扑中匹配 Pod 的数量与全局最小值(符合条件的域中匹配的最小 Pod 数量,如果符合条件的域数量小于 MinDomains 则为零) 之间的最大允许差值。例如,如果你有 3 个可用区,分别有 2、2 和 1 个匹配的 Pod,则 MaxSkew 设为 1, 且全局最小值为 1。
    • 如果你选择 whenUnsatisfiable: ScheduleAnyway,则该调度器会更为偏向能够降低偏差值的拓扑域。
  • minDomains 表示符合条件的域的最小数量。此字段是可选的。域是拓扑的一个特定实例。 符合条件的域是其节点与节点选择器匹配的域。

    • 指定的 minDomains 值必须大于 0。你可以结合 whenUnsatisfiable: DoNotSchedule 仅指定 minDomains
    • 当符合条件的、拓扑键匹配的域的数量小于 minDomains 时,拓扑分布将“全局最小值”(global minimum)设为 0, 然后进行 skew 计算。“全局最小值”是一个符合条件的域中匹配 Pod 的最小数量, 如果符合条件的域的数量小于 minDomains,则全局最小值为零。
    • 当符合条件的拓扑键匹配域的个数等于或大于 minDomains 时,该值对调度没有影响。
    • 如果你未指定 minDomains,则约束行为类似于 minDomains 等于 1。
  • topologyKey节点标签的键。如果节点使用此键标记并且具有相同的标签值, 则将这些节点视为处于同一拓扑域中。我们将拓扑域中(即键值对)的每个实例称为一个域。 调度器将尝试在每个拓扑域中放置数量均衡的 Pod。 另外,我们将符合条件的域定义为其节点满足 nodeAffinityPolicy 和 nodeTaintsPolicy 要求的域。

  • whenUnsatisfiable 指示如果 Pod 不满足分布约束时如何处理:

    • DoNotSchedule(默认)告诉调度器不要调度。
    • ScheduleAnyway 告诉调度器仍然继续调度,只是根据如何能将偏差最小化来对节点进行排序。
  • labelSelector 用于查找匹配的 Pod。匹配此标签的 Pod 将被统计,以确定相应拓扑域中 Pod 的数量。 有关详细信息,请参考标签选择算符

  • matchLabelKeys 是一个 Pod 标签键的列表,用于选择需要计算分布方式的 Pod 集合。 这些键用于从 Pod 标签中查找值,这些键值标签与 labelSelector 进行逻辑与运算,以选择一组已有的 Pod, 通过这些 Pod 计算新来 Pod 的分布方式。matchLabelKeyslabelSelector 中禁止存在相同的键。 未设置 labelSelector 时无法设置 matchLabelKeys。Pod 标签中不存在的键将被忽略。 null 或空列表意味着仅与 labelSelector 匹配。

    借助 matchLabelKeys,你无需在变更 Pod 修订版本时更新 pod.spec。 控制器或 Operator 只需要将不同修订版的标签键设为不同的值。 调度器将根据 matchLabelKeys 自动确定取值。例如,如果你正在配置一个 Deployment, 则你可以使用由 Deployment 控制器自动添加的、以 pod-template-hash 为键的标签来区分同一个 Deployment 的不同修订版。

        topologySpreadConstraints:
            - maxSkew: 1
              topologyKey: kubernetes.io/hostname
              whenUnsatisfiable: DoNotSchedule
              labelSelector:
                matchLabels:
                  app: foo
              matchLabelKeys:
                - pod-template-hash
    
  • nodeAffinityPolicy 表示我们在计算 Pod 拓扑分布偏差时将如何处理 Pod 的 nodeAffinity/nodeSelector。 选项为:

    • Honor:只有与 nodeAffinity/nodeSelector 匹配的节点才会包括到计算中。
    • Ignore:nodeAffinity/nodeSelector 被忽略。所有节点均包括到计算中。

    如果此值为 nil,此行为等同于 Honor 策略。

  • nodeTaintsPolicy 表示我们在计算 Pod 拓扑分布偏差时将如何处理节点污点。选项为:

    • Honor:包括不带污点的节点以及污点被新 Pod 所容忍的节点。
    • Ignore:节点污点被忽略。包括所有节点。

    如果此值为 null,此行为等同于 Ignore 策略。

当 Pod 定义了不止一个 topologySpreadConstraint,这些约束之间是逻辑与的关系。 kube-scheduler 会为新的 Pod 寻找一个能够满足所有约束的节点。

节点标签

拓扑分布约束依赖于节点标签来标识每个节点所在的拓扑域。 例如,某节点可能具有标签:

  region: us-east-1
  zone: us-east-1a

假设你有一个 4 节点的集群且带有以下标签:

NAME    STATUS   ROLES    AGE     VERSION   LABELS
node1   Ready    <none>   4m26s   v1.16.0   node=node1,zone=zoneA
node2   Ready    <none>   3m58s   v1.16.0   node=node2,zone=zoneA
node3   Ready    <none>   3m17s   v1.16.0   node=node3,zone=zoneB
node4   Ready    <none>   2m43s   v1.16.0   node=node4,zone=zoneB

那么,从逻辑上看集群如下:

graph TB subgraph "zoneB" n3(Node3) n4(Node4) end subgraph "zoneA" n1(Node1) n2(Node2) end classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000; classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff; classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5; class n1,n2,n3,n4 k8s; class zoneA,zoneB cluster;

一致性

你应该为一个组中的所有 Pod 设置相同的 Pod 拓扑分布约束。

通常,如果你正使用一个工作负载控制器,例如 Deployment,则 Pod 模板会帮你解决这个问题。 如果你混合不同的分布约束,则 Kubernetes 会遵循该字段的 API 定义; 但是,该行为可能更令人困惑,并且故障排除也没那么简单。

你需要一种机制来确保拓扑域(例如云提供商区域)中的所有节点具有一致的标签。 为了避免你需要手动为节点打标签,大多数集群会自动填充知名的标签, 例如 kubernetes.io/hostname。检查你的集群是否支持此功能。

拓扑分布约束示例

示例:一个拓扑分布约束

假设你拥有一个 4 节点集群,其中标记为 foo: bar 的 3 个 Pod 分别位于 node1、node2 和 node3 中:

graph BT subgraph "zoneB" p3(Pod) --> n3(Node3) n4(Node4) end subgraph "zoneA" p1(Pod) --> n1(Node1) p2(Pod) --> n2(Node2) end classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000; classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff; classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5; class n1,n2,n3,n4,p1,p2,p3 k8s; class zoneA,zoneB cluster;

如果你希望新来的 Pod 均匀分布在现有的可用区域,则可以按如下设置其清单:

kind: Pod
apiVersion: v1
metadata:
  name: mypod
  labels:
    foo: bar
spec:
  topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: zone
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        foo: bar
  containers:
  - name: pause
    image: registry.k8s.io/pause:3.1

从此清单看,topologyKey: zone 意味着均匀分布将只应用于存在标签键值对为 zone: <any value> 的节点 (没有 zone 标签的节点将被跳过)。如果调度器找不到一种方式来满足此约束, 则 whenUnsatisfiable: DoNotSchedule 字段告诉该调度器将新来的 Pod 保持在 pending 状态。

如果该调度器将这个新来的 Pod 放到可用区 A,则 Pod 的分布将成为 [3, 1]。 这意味着实际偏差是 2(计算公式为 3 - 1),这违反了 maxSkew: 1 的约定。 为了满足这个示例的约束和上下文,新来的 Pod 只能放到可用区 B 中的一个节点上:

graph BT subgraph "zoneB" p3(Pod) --> n3(Node3) p4(mypod) --> n4(Node4) end subgraph "zoneA" p1(Pod) --> n1(Node1) p2(Pod) --> n2(Node2) end classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000; classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff; classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5; class n1,n2,n3,n4,p1,p2,p3 k8s; class p4 plain; class zoneA,zoneB cluster;

或者

graph BT subgraph "zoneB" p3(Pod) --> n3(Node3) p4(mypod) --> n3 n4(Node4) end subgraph "zoneA" p1(Pod) --> n1(Node1) p2(Pod) --> n2(Node2) end classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000; classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff; classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5; class n1,n2,n3,n4,p1,p2,p3 k8s; class p4 plain; class zoneA,zoneB cluster;

你可以调整 Pod 规约以满足各种要求:

  • maxSkew 更改为更大的值,例如 2,这样新来的 Pod 也可以放在可用区 A 中。
  • topologyKey 更改为 node,以便将 Pod 均匀分布在节点上而不是可用区中。 在上面的例子中,如果 maxSkew 保持为 1,则新来的 Pod 只能放到 node4 节点上。
  • whenUnsatisfiable: DoNotSchedule 更改为 whenUnsatisfiable: ScheduleAnyway, 以确保新来的 Pod 始终可以被调度(假设满足其他的调度 API)。但是,最好将其放置在匹配 Pod 数量较少的拓扑域中。 请注意,这一优先判定会与其他内部调度优先级(如资源使用率等)排序准则一起进行标准化。

示例:多个拓扑分布约束

下面的例子建立在前面例子的基础上。假设你拥有一个 4 节点集群, 其中 3 个标记为 foo: bar 的 Pod 分别位于 node1、node2 和 node3 上:

graph BT subgraph "zoneB" p3(Pod) --> n3(Node3) n4(Node4) end subgraph "zoneA" p1(Pod) --> n1(Node1) p2(Pod) --> n2(Node2) end classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000; classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff; classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5; class n1,n2,n3,n4,p1,p2,p3 k8s; class p4 plain; class zoneA,zoneB cluster;

可以组合使用 2 个拓扑分布约束来控制 Pod 在节点和可用区两个维度上的分布:

kind: Pod
apiVersion: v1
metadata:
  name: mypod
  labels:
    foo: bar
spec:
  topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: zone
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        foo: bar
  - maxSkew: 1
    topologyKey: node
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        foo: bar
  containers:
  - name: pause
    image: registry.k8s.io/pause:3.1

在这种情况下,为了匹配第一个约束,新的 Pod 只能放置在可用区 B 中; 而在第二个约束中,新来的 Pod 只能调度到节点 node4 上。 该调度器仅考虑满足所有已定义约束的选项,因此唯一可行的选择是放置在节点 node4 上。

示例:有冲突的拓扑分布约束

多个约束可能导致冲突。假设有一个跨 2 个可用区的 3 节点集群:

graph BT subgraph "zoneB" p4(Pod) --> n3(Node3) p5(Pod) --> n3 end subgraph "zoneA" p1(Pod) --> n1(Node1) p2(Pod) --> n1 p3(Pod) --> n2(Node2) end classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000; classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff; classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5; class n1,n2,n3,n4,p1,p2,p3,p4,p5 k8s; class zoneA,zoneB cluster;

如果你将 two-constraints.yaml (来自上一个示例的清单)应用到这个集群,你将看到 Pod mypod 保持在 Pending 状态。 出现这种情况的原因为:为了满足第一个约束,Pod mypod 只能放置在可用区 B 中; 而在第二个约束中,Pod mypod 只能调度到节点 node2 上。 两个约束的交集将返回一个空集,且调度器无法放置该 Pod。

为了应对这种情形,你可以提高 maxSkew 的值或修改其中一个约束才能使用 whenUnsatisfiable: ScheduleAnyway。 根据实际情形,例如若你在故障排查时发现某个漏洞修复工作毫无进展,你还可能决定手动删除一个现有的 Pod。

与节点亲和性和节点选择算符的相互作用

如果 Pod 定义了 spec.nodeSelectorspec.affinity.nodeAffinity, 调度器将在偏差计算中跳过不匹配的节点。

示例:带节点亲和性的拓扑分布约束

假设你有一个跨可用区 A 到 C 的 5 节点集群:

graph BT subgraph "zoneB" p3(Pod) --> n3(Node3) n4(Node4) end subgraph "zoneA" p1(Pod) --> n1(Node1) p2(Pod) --> n2(Node2) end classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000; classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff; classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5; class n1,n2,n3,n4,p1,p2,p3 k8s; class p4 plain; class zoneA,zoneB cluster;
graph BT subgraph "zoneC" n5(Node5) end classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000; classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff; classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5; class n5 k8s; class zoneC cluster;

而且你知道可用区 C 必须被排除在外。在这种情况下,可以按如下方式编写清单, 以便将 Pod mypod 放置在可用区 B 上,而不是可用区 C 上。 同样,Kubernetes 也会一样处理 spec.nodeSelector

kind: Pod
apiVersion: v1
metadata:
  name: mypod
  labels:
    foo: bar
spec:
  topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: zone
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        foo: bar
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: zone
            operator: NotIn
            values:
            - zoneC
  containers:
  - name: pause
    image: registry.k8s.io/pause:3.1

隐式约定

这里有一些值得注意的隐式约定:

  • 只有与新来的 Pod 具有相同命名空间的 Pod 才能作为匹配候选者。

  • 调度器会忽略没有任何 topologySpreadConstraints[*].topologyKey 的节点。这意味着:

    1. 位于这些节点上的 Pod 不影响 maxSkew 计算,在上面的例子中, 假设节点 node1 没有标签 "zone",则 2 个 Pod 将被忽略,因此新来的 Pod 将被调度到可用区 A 中。
    2. 新的 Pod 没有机会被调度到这类节点上。在上面的例子中, 假设节点 node5 带有拼写错误的标签 zone-typo: zoneC(且没有设置 zone 标签)。 节点 node5 接入集群之后,该节点将被忽略且针对该工作负载的 Pod 不会被调度到那里。
  • 注意,如果新 Pod 的 topologySpreadConstraints[*].labelSelector 与自身的标签不匹配,将会发生什么。 在上面的例子中,如果移除新 Pod 的标签,则 Pod 仍然可以放置到可用区 B 中的节点上,因为这些约束仍然满足。 然而,在放置之后,集群的不平衡程度保持不变。可用区 A 仍然有 2 个 Pod 带有标签 foo: bar, 而可用区 B 有 1 个 Pod 带有标签 foo: bar。如果这不是你所期望的, 更新工作负载的 topologySpreadConstraints[*].labelSelector 以匹配 Pod 模板中的标签。

集群级别的默认约束

为集群设置默认的拓扑分布约束也是可能的。默认拓扑分布约束在且仅在以下条件满足时才会被应用到 Pod 上:

  • Pod 没有在其 .spec.topologySpreadConstraints 中定义任何约束。
  • Pod 隶属于某个 Service、ReplicaSet、StatefulSet 或 ReplicationController。

默认约束可以设置为调度方案PodTopologySpread 插件参数的一部分。约束的设置采用如前所述的 API, 只是 labelSelector 必须为空。 选择算符是根据 Pod 所属的 Service、ReplicaSet、StatefulSet 或 ReplicationController 来设置的。

配置的示例可能看起来像下面这个样子:

apiVersion: kubescheduler.config.k8s.io/v1beta3
kind: KubeSchedulerConfiguration

profiles:
  - schedulerName: default-scheduler
    pluginConfig:
      - name: PodTopologySpread
        args:
          defaultConstraints:
            - maxSkew: 1
              topologyKey: topology.kubernetes.io/zone
              whenUnsatisfiable: ScheduleAnyway
          defaultingType: List

内置默认约束

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

如果你没有为 Pod 拓扑分布配置任何集群级别的默认约束, kube-scheduler 的行为就像你指定了以下默认拓扑约束一样:

defaultConstraints:
  - maxSkew: 3
    topologyKey: "kubernetes.io/hostname"
    whenUnsatisfiable: ScheduleAnyway
  - maxSkew: 5
    topologyKey: "topology.kubernetes.io/zone"
    whenUnsatisfiable: ScheduleAnyway

此外,原来用于提供等同行为的 SelectorSpread 插件默认被禁用。

如果你不想为集群使用默认的 Pod 分布约束,你可以通过设置 defaultingType 参数为 List, 并将 PodTopologySpread 插件配置中的 defaultConstraints 参数置空来禁用默认 Pod 分布约束:

apiVersion: kubescheduler.config.k8s.io/v1beta3
kind: KubeSchedulerConfiguration

profiles:
  - schedulerName: default-scheduler
    pluginConfig:
      - name: PodTopologySpread
        args:
          defaultConstraints: []
          defaultingType: List

比较 podAffinity 和 podAntiAffinity

在 Kubernetes 中, Pod 间亲和性和反亲和性控制 Pod 彼此的调度方式(更密集或更分散)。

podAffinity
吸引 Pod;你可以尝试将任意数量的 Pod 集中到符合条件的拓扑域中。
podAntiAffinity
驱逐 Pod。如果将此设为 requiredDuringSchedulingIgnoredDuringExecution 模式, 则只有单个 Pod 可以调度到单个拓扑域;如果你选择 preferredDuringSchedulingIgnoredDuringExecution, 则你将丢失强制执行此约束的能力。

要实现更细粒度的控制,你可以设置拓扑分布约束来将 Pod 分布到不同的拓扑域下,从而实现高可用性或节省成本。 这也有助于工作负载的滚动更新和平稳地扩展副本规模。

有关详细信息,请参阅有关 Pod 拓扑分布约束的增强倡议的 动机一节。

已知局限性

  • 当 Pod 被移除时,无法保证约束仍被满足。例如,缩减某 Deployment 的规模时,Pod 的分布可能不再均衡。

    你可以使用 Descheduler 来重新实现 Pod 分布的均衡。

  • 具有污点的节点上匹配的 Pod 也会被统计。 参考 Issue 80921

  • 该调度器不会预先知道集群拥有的所有可用区和其他拓扑域。 拓扑域由集群中存在的节点确定。在自动扩缩的集群中,如果一个节点池(或节点组)的节点数量缩减为零, 而用户正期望其扩容时,可能会导致调度出现问题。 因为在这种情况下,调度器不会考虑这些拓扑域,直至这些拓扑域中至少包含有一个节点。

    你可以通过使用感知 Pod 拓扑分布约束并感知整个拓扑域集的集群自动扩缩工具来解决此问题。

接下来

  • 博客:PodTopologySpread 介绍详细解释了 maxSkew, 并给出了一些进阶的使用示例。
  • 阅读针对 Pod 的 API 参考的调度一节。

3.10.6 - 污点和容忍度

节点亲和性Pod 的一种属性,它使 Pod 被吸引到一类特定的节点 (这可能出于一种偏好,也可能是硬性要求)。 污点(Taint) 则相反——它使节点能够排斥一类特定的 Pod。

容忍度(Toleration) 是应用于 Pod 上的。容忍度允许调度器调度带有对应污点的 Pod。 容忍度允许调度但并不保证调度:作为其功能的一部分, 调度器也会评估其他参数

污点和容忍度(Toleration)相互配合,可以用来避免 Pod 被分配到不合适的节点上。 每个节点上都可以应用一个或多个污点,这表示对于那些不能容忍这些污点的 Pod, 是不会被该节点接受的。

概念

你可以使用命令 kubectl taint 给节点增加一个污点。比如:

kubectl taint nodes node1 key1=value1:NoSchedule

给节点 node1 增加一个污点,它的键名是 key1,键值是 value1,效果是 NoSchedule。 这表示只有拥有和这个污点相匹配的容忍度的 Pod 才能够被分配到 node1 这个节点。

若要移除上述命令所添加的污点,你可以执行:

kubectl taint nodes node1 key1=value1:NoSchedule-

你可以在 Pod 规约中为 Pod 设置容忍度。 下面两个容忍度均与上面例子中使用 kubectl taint 命令创建的污点相匹配, 因此如果一个 Pod 拥有其中的任何一个容忍度,都能够被调度到 node1

tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoSchedule"
tolerations:
- key: "key1"
  operator: "Exists"
  effect: "NoSchedule"

默认的 Kubernetes 调度器在选择一个节点来运行特定的 Pod 时会考虑污点和容忍度。 然而,如果你手动为一个 Pod 指定了 .spec.nodeName,那么选节点操作会绕过调度器; 这个 Pod 将会绑定到你指定的节点上,即使你选择的节点上有 NoSchedule 的污点。 如果这种情况发生,且节点上还设置了 NoExecute 的污点,kubelet 会将 Pod 驱逐出去,除非有适当的容忍度设置。

下面是一个定义了一些容忍度的 Pod 的例子:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  tolerations:
  - key: "example-key"
    operator: "Exists"
    effect: "NoSchedule"

operator 的默认值是 Equal

一个容忍度和一个污点相“匹配”是指它们有一样的键名和效果,并且:

  • 如果 operatorExists(此时容忍度不能指定 value),或者
  • 如果 operatorEqual,则它们的值应该相等。

上述例子中 effect 使用的值为 NoSchedule,你也可以使用另外一个值 PreferNoSchedule

effect 字段的允许值包括:

NoExecute
这会影响已在节点上运行的 Pod,具体影响如下:
  • 如果 Pod 不能容忍这类污点,会马上被驱逐。
  • 如果 Pod 能够容忍这类污点,但是在容忍度定义中没有指定 tolerationSeconds, 则 Pod 还会一直在这个节点上运行。
  • 如果 Pod 能够容忍这类污点,而且指定了 tolerationSeconds, 则 Pod 还能在这个节点上继续运行这个指定的时间长度。 这段时间过去后,节点生命周期控制器从节点驱除这些 Pod。
NoSchedule
除非具有匹配的容忍度规约,否则新的 Pod 不会被调度到带有污点的节点上。 当前正在节点上运行的 Pod 不会被驱逐。
PreferNoSchedule
PreferNoSchedule 是“偏好”或“软性”的 NoSchedule。 控制平面将尝试避免将不能容忍污点的 Pod 调度到的节点上,但不能保证完全避免。

你可以给一个节点添加多个污点,也可以给一个 Pod 添加多个容忍度设置。 Kubernetes 处理多个污点和容忍度的过程就像一个过滤器:从一个节点的所有污点开始遍历, 过滤掉那些 Pod 中存在与之相匹配的容忍度的污点。余下未被过滤的污点的 effect 值决定了 Pod 是否会被分配到该节点。需要注意以下情况:

  • 如果未被忽略的污点中存在至少一个 effect 值为 NoSchedule 的污点, 则 Kubernetes 不会将 Pod 调度到该节点。
  • 如果未被忽略的污点中不存在 effect 值为 NoSchedule 的污点, 但是存在至少一个 effect 值为 PreferNoSchedule 的污点, 则 Kubernetes 会尝试不将 Pod 调度到该节点。
  • 如果未被忽略的污点中存在至少一个 effect 值为 NoExecute 的污点, 则 Kubernetes 不会将 Pod 调度到该节点(如果 Pod 还未在节点上运行), 并且会将 Pod 从该节点驱逐(如果 Pod 已经在节点上运行)。

例如,假设你给一个节点添加了如下污点:

kubectl taint nodes node1 key1=value1:NoSchedule
kubectl taint nodes node1 key1=value1:NoExecute
kubectl taint nodes node1 key2=value2:NoSchedule

假定某个 Pod 有两个容忍度:

tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoSchedule"
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoExecute"

在这种情况下,上述 Pod 不会被调度到上述节点,因为其没有容忍度和第三个污点相匹配。 但是如果在给节点添加上述污点之前,该 Pod 已经在上述节点运行, 那么它还可以继续运行在该节点上,因为第三个污点是三个污点中唯一不能被这个 Pod 容忍的。

通常情况下,如果给一个节点添加了一个 effect 值为 NoExecute 的污点, 则任何不能容忍这个污点的 Pod 都会马上被驱逐,任何可以容忍这个污点的 Pod 都不会被驱逐。 但是,如果 Pod 存在一个 effect 值为 NoExecute 的容忍度指定了可选属性 tolerationSeconds 的值,则表示在给节点添加了上述污点之后, Pod 还能继续在节点上运行的时间。例如,

tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoExecute"
  tolerationSeconds: 3600

这表示如果这个 Pod 正在运行,同时一个匹配的污点被添加到其所在的节点, 那么 Pod 还将继续在节点上运行 3600 秒,然后被驱逐。 如果在此之前上述污点被删除了,则 Pod 不会被驱逐。

使用例子

通过污点和容忍度,可以灵活地让 Pod 避开某些节点或者将 Pod 从某些节点驱逐。 下面是几个使用例子:

  • 专用节点:如果想将某些节点专门分配给特定的一组用户使用,你可以给这些节点添加一个污点(即, kubectl taint nodes nodename dedicated=groupName:NoSchedule), 然后给这组用户的 Pod 添加一个相对应的容忍度 (通过编写一个自定义的准入控制器, 很容易就能做到)。 拥有上述容忍度的 Pod 就能够被调度到上述专用节点,同时也能够被调度到集群中的其它节点。 如果你希望这些 Pod 只能被调度到上述专用节点, 那么你还需要给这些专用节点另外添加一个和上述污点类似的 label(例如:dedicated=groupName), 同时还要在上述准入控制器中给 Pod 增加节点亲和性要求,要求上述 Pod 只能被调度到添加了 dedicated=groupName 标签的节点上。
  • 配备了特殊硬件的节点:在部分节点配备了特殊硬件(比如 GPU)的集群中, 我们希望不需要这类硬件的 Pod 不要被调度到这些特殊节点,以便为后继需要这类硬件的 Pod 保留资源。 要达到这个目的,可以先给配备了特殊硬件的节点添加污点 (例如 kubectl taint nodes nodename special=true:NoSchedulekubectl taint nodes nodename special=true:PreferNoSchedule), 然后给使用了这类特殊硬件的 Pod 添加一个相匹配的容忍度。 和专用节点的例子类似,添加这个容忍度的最简单的方法是使用自定义 准入控制器。 比如,我们推荐使用扩展资源 来表示特殊硬件,给配置了特殊硬件的节点添加污点时包含扩展资源名称, 然后运行一个 ExtendedResourceToleration 准入控制器。此时,因为节点已经被设置污点了,没有对应容忍度的 Pod 不会被调度到这些节点。 但当你创建一个使用了扩展资源的 Pod 时,ExtendedResourceToleration 准入控制器会自动给 Pod 加上正确的容忍度,这样 Pod 就会被自动调度到这些配置了特殊硬件的节点上。 这种方式能够确保配置了特殊硬件的节点专门用于运行需要这些硬件的 Pod, 并且你无需手动给这些 Pod 添加容忍度。
  • 基于污点的驱逐:这是在每个 Pod 中配置的在节点出现问题时的驱逐行为, 接下来的章节会描述这个特性。

基于污点的驱逐

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

当某种条件为真时,节点控制器会自动给节点添加一个污点。当前内置的污点包括:

  • node.kubernetes.io/not-ready:节点未准备好。这相当于节点状况 Ready 的值为 "False"。
  • node.kubernetes.io/unreachable:节点控制器访问不到节点. 这相当于节点状况 Ready 的值为 "Unknown"。
  • node.kubernetes.io/memory-pressure:节点存在内存压力。
  • node.kubernetes.io/disk-pressure:节点存在磁盘压力。
  • node.kubernetes.io/pid-pressure:节点的 PID 压力。
  • node.kubernetes.io/network-unavailable:节点网络不可用。
  • node.kubernetes.io/unschedulable:节点不可调度。
  • node.cloudprovider.kubernetes.io/uninitialized:如果 kubelet 启动时指定了一个“外部”云平台驱动, 它将给当前节点添加一个污点将其标志为不可用。在 cloud-controller-manager 的一个控制器初始化这个节点后,kubelet 将删除这个污点。

在节点被排空时,节点控制器或者 kubelet 会添加带有 NoExecute 效果的相关污点。 此效果被默认添加到 node.kubernetes.io/not-readynode.kubernetes.io/unreachable 污点中。 如果异常状态恢复正常,kubelet 或节点控制器能够移除相关的污点。

在某些情况下,当节点不可达时,API 服务器无法与节点上的 kubelet 进行通信。 在与 API 服务器的通信被重新建立之前,删除 Pod 的决定无法传递到 kubelet。 同时,被调度进行删除的那些 Pod 可能会继续运行在分区后的节点上。

你可以为 Pod 设置 tolerationSeconds,以指定当节点失效或者不响应时, Pod 维系与该节点间绑定关系的时长。

比如,你可能希望在出现网络分裂事件时,对于一个与节点本地状态有着深度绑定的应用而言, 仍然停留在当前节点上运行一段较长的时间,以等待网络恢复以避免被驱逐。 你为这种 Pod 所设置的容忍度看起来可能是这样:

tolerations:
- key: "node.kubernetes.io/unreachable"
  operator: "Exists"
  effect: "NoExecute"
  tolerationSeconds: 6000

DaemonSet 中的 Pod 被创建时, 针对以下污点自动添加的 NoExecute 的容忍度将不会指定 tolerationSeconds

  • node.kubernetes.io/unreachable
  • node.kubernetes.io/not-ready

这保证了出现上述问题时 DaemonSet 中的 Pod 永远不会被驱逐。

基于节点状态添加污点

控制平面使用节点控制器自动创建 与节点状况 对应的、效果为 NoSchedule 的污点。

调度器在进行调度时检查污点,而不是检查节点状况。这确保节点状况不会直接影响调度。 例如,如果 DiskPressure 节点状况处于活跃状态,则控制平面添加 node.kubernetes.io/disk-pressure 污点并且不会调度新的 Pod 到受影响的节点。 如果 MemoryPressure 节点状况处于活跃状态,则控制平面添加 node.kubernetes.io/memory-pressure 污点。

对于新创建的 Pod,可以通过添加相应的 Pod 容忍度来忽略节点状况。 控制平面还在具有除 BestEffort 之外的 QoS 类的 Pod 上添加 node.kubernetes.io/memory-pressure 容忍度。 这是因为 Kubernetes 将 GuaranteedBurstable QoS 类中的 Pod(甚至没有设置内存请求的 Pod) 视为能够应对内存压力,而新创建的 BestEffort Pod 不会被调度到受影响的节点上。

DaemonSet 控制器自动为所有守护进程添加如下 NoSchedule 容忍度,以防 DaemonSet 崩溃:

  • node.kubernetes.io/memory-pressure
  • node.kubernetes.io/disk-pressure
  • node.kubernetes.io/pid-pressure(1.14 或更高版本)
  • node.kubernetes.io/unschedulable(1.10 或更高版本)
  • node.kubernetes.io/network-unavailable只适合主机网络配置

添加上述容忍度确保了向后兼容,你也可以选择自由向 DaemonSet 添加容忍度。

接下来

3.10.7 - 调度框架

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

调度框架是面向 Kubernetes 调度器的一种插件架构, 它由一组直接编译到调度程序中的“插件” API 组成。 这些 API 允许大多数调度功能以插件的形式实现,同时使调度“核心”保持简单且可维护。 请参考调度框架的设计提案 获取框架设计的更多技术信息。

框架工作流程

调度框架定义了一些扩展点。调度器插件注册后在一个或多个扩展点处被调用。 这些插件中的一些可以改变调度决策,而另一些仅用于提供信息。

每次调度一个 Pod 的尝试都分为两个阶段,即调度周期绑定周期

调度周期和绑定周期

调度周期为 Pod 选择一个节点,绑定周期将该决策应用于集群。 调度周期和绑定周期一起被称为“调度上下文”。

调度周期是串行运行的,而绑定周期可能是同时运行的。

如果确定 Pod 不可调度或者存在内部错误,则可以终止调度周期或绑定周期。 Pod 将返回队列并重试。

接口

下图显示了一个 Pod 的调度上下文以及调度框架公开的接口。

一个插件可能实现多个接口,以执行更为复杂或有状态的任务。

某些接口与可以通过调度器配置来设置的调度器扩展点匹配。

调度框架扩展点

PreEnqueue

这些插件在将 Pod 被添加到内部活动队列之前被调用,在此队列中 Pod 被标记为准备好进行调度。

只有当所有 PreEnqueue 插件返回 Success 时,Pod 才允许进入活动队列。 否则,它将被放置在内部无法调度的 Pod 列表中,并且不会获得 Unschedulable 状态。

要了解有关内部调度器队列如何工作的更多详细信息,请阅读 kube-scheduler 调度队列

EnqueueExtension

EnqueueExtension 作为一个接口,插件可以在此接口之上根据集群中的变化来控制是否重新尝试调度被此插件拒绝的 Pod。实现 PreEnqueue、PreFilter、Filter、Reserve 或 Permit 的插件应实现此接口。

QueueingHint

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

QueueingHint 作为一个回调函数,用于决定是否将 Pod 重新排队到活跃队列或回退队列。 每当集群中发生某种事件或变化时,此函数就会被执行。 当 QueueingHint 发现事件可能使 Pod 可调度时,Pod 将被放入活跃队列或回退队列, 以便调度器可以重新尝试调度 Pod。

队列排序

这些插件用于对调度队列中的 Pod 进行排序。 队列排序插件本质上提供 Less(Pod1, Pod2) 函数。 一次只能启动一个队列插件。

PreFilter

这些插件用于预处理 Pod 的相关信息,或者检查集群或 Pod 必须满足的某些条件。 如果 PreFilter 插件返回错误,则调度周期将终止。

Filter

这些插件用于过滤出不能运行该 Pod 的节点。对于每个节点, 调度器将按照其配置顺序调用这些过滤插件。如果任何过滤插件将节点标记为不可行, 则不会为该节点调用剩下的过滤插件。节点可以被同时进行评估。

PostFilter

这些插件在 Filter 阶段后调用,但仅在该 Pod 没有可行的节点时调用。 插件按其配置的顺序调用。如果任何 PostFilter 插件标记节点为 "Schedulable", 则其余的插件不会调用。典型的 PostFilter 实现是抢占,试图通过抢占其他 Pod 的资源使该 Pod 可以调度。

PreScore

这些插件用于执行“前置评分(pre-scoring)”工作,即生成一个可共享状态供 Score 插件使用。 如果 PreScore 插件返回错误,则调度周期将终止。

Score

这些插件用于对通过过滤阶段的节点进行排序。调度器将为每个节点调用每个评分插件。 将有一个定义明确的整数范围,代表最小和最大分数。 在标准化评分阶段之后, 调度器将根据配置的插件权重合并所有插件的节点分数。

NormalizeScore

这些插件用于在调度器计算 Node 排名之前修改分数。 在此扩展点注册的插件被调用时会使用同一插件的 Score 结果。每个插件在每个调度周期调用一次。

例如,假设一个 BlinkingLightScorer 插件基于具有的闪烁指示灯数量来对节点进行排名。

func ScoreNode(_ *v1.pod, n *v1.Node) (int, error) {
    return getBlinkingLightCount(n)
}

然而,最大的闪烁灯个数值可能比 NodeScoreMax 小。要解决这个问题, BlinkingLightScorer 插件还应该注册该扩展点。

func NormalizeScores(scores map[string]int) {
    highest := 0
    for _, score := range scores {
        highest = max(highest, score)
    }
    for node, score := range scores {
        scores[node] = score*NodeScoreMax/highest
    }
}

如果任何 NormalizeScore 插件返回错误,则调度阶段将终止。

Reserve

实现了 Reserve 接口的插件,拥有两个方法,即 ReserveUnreserve, 他们分别支持两个名为 Reserve 和 Unreserve 的信息传递性质的调度阶段。 维护运行时状态的插件(又称"有状态插件")应该使用这两个阶段, 以便在节点上的资源被保留和解除保留给特定的 Pod 时,得到调度器的通知。

Reserve 阶段发生在调度器实际将一个 Pod 绑定到其指定节点之前。 它的存在是为了防止在调度器等待绑定成功时发生竞争情况。 每个 Reserve 插件的 Reserve 方法可能成功,也可能失败; 如果一个 Reserve 方法调用失败,后面的插件就不会被执行,Reserve 阶段被认为失败。 如果所有插件的 Reserve 方法都成功了,Reserve 阶段就被认为是成功的, 剩下的调度周期和绑定周期就会被执行。

如果 Reserve 阶段或后续阶段失败了,则触发 Unreserve 阶段。 发生这种情况时,所有 Reserve 插件的 Unreserve 方法将按照 Reserve 方法调用的相反顺序执行。 这个阶段的存在是为了清理与保留的 Pod 相关的状态。

这个是调度周期的最后一步。 一旦 Pod 处于保留状态,它将在绑定周期结束时触发 Unreserve 插件(失败时)或 PostBind 插件(成功时)。

Permit

Permit 插件在每个 Pod 调度周期的最后调用,用于防止或延迟 Pod 的绑定。 一个允许插件可以做以下三件事之一:

  1. 批准
    一旦所有 Permit 插件批准 Pod 后,该 Pod 将被发送以进行绑定。
  1. 拒绝
    如果任何 Permit 插件拒绝 Pod,则该 Pod 将被返回到调度队列。 这将触发 Reserve 插件中的 Unreserve 阶段。
  1. 等待(带有超时)
    如果一个 Permit 插件返回“等待”结果,则 Pod 将保持在一个内部的“等待中” 的 Pod 列表,同时该 Pod 的绑定周期启动时即直接阻塞直到得到批准。 如果超时发生,等待变成拒绝,并且 Pod 将返回调度队列,从而触发 Reserve 插件中的 Unreserve 阶段。

PreBind

这些插件用于执行 Pod 绑定前所需的所有工作。 例如,一个 PreBind 插件可能需要制备网络卷并且在允许 Pod 运行在该节点之前将其挂载到目标节点上。

如果任何 PreBind 插件返回错误,则 Pod 将被拒绝并且退回到调度队列中。

Bind

Bind 插件用于将 Pod 绑定到节点上。直到所有的 PreBind 插件都完成,Bind 插件才会被调用。 各 Bind 插件按照配置顺序被调用。Bind 插件可以选择是否处理指定的 Pod。 如果某 Bind 插件选择处理某 Pod,剩余的 Bind 插件将被跳过

PostBind

这是个信息传递性质的接口。 PostBind 插件在 Pod 成功绑定后被调用。这是绑定周期的结尾,可用于清理相关的资源。

插件 API

插件 API 分为两个步骤。首先,插件必须完成注册并配置,然后才能使用扩展点接口。 扩展点接口具有以下形式。

type Plugin interface {
    Name() string
}

type QueueSortPlugin interface {
    Plugin
    Less(*v1.pod, *v1.pod) bool
}

type PreFilterPlugin interface {
    Plugin
    PreFilter(context.Context, *framework.CycleState, *v1.pod) error
}

// ...

插件配置

你可以在调度器配置中启用或禁用插件。 如果你在使用 Kubernetes v1.18 或更高版本, 大部分调度插件都在使用中且默认启用。

除了默认的插件,你还可以实现自己的调度插件并且将它们与默认插件一起配置。 你可以访问 scheduler-plugins 了解更多信息。

如果你正在使用 Kubernetes v1.18 或更高版本,你可以将一组插件设置为一个调度器配置文件, 然后定义不同的配置文件来满足各类工作负载。 了解更多关于多配置文件

3.10.8 - 动态资源分配

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

动态资源分配是一个用于在 Pod 之间和 Pod 内部容器之间请求和共享资源的 API。 它是对为通用资源所提供的持久卷 API 的泛化。第三方资源驱动程序负责跟踪和分配资源。 不同类型的资源支持用任意参数进行定义和初始化。

准备开始

Kubernetes v1.30 包含用于动态资源分配的集群级 API 支持, 但它需要被显式启用。 你还必须为此 API 要管理的特定资源安装资源驱动程序。 如果你未运行 Kubernetes v1.30, 请查看对应版本的 Kubernetes 文档。

API

resource.k8s.io/v1alpha2 API 组提供四种类型:

ResourceClass
定义由哪个资源驱动程序处理某种资源,并为其提供通用参数。 集群管理员在安装资源驱动程序时创建 ResourceClass。
ResourceClaim
定义工作负载所需的特定资源实例。 由用户创建(手动管理生命周期,可以在不同的 Pod 之间共享), 或者由控制平面基于 ResourceClaimTemplate 为特定 Pod 创建 (自动管理生命周期,通常仅由一个 Pod 使用)。
ResourceClaimTemplate
定义用于创建 ResourceClaim 的 spec 和一些元数据。 部署工作负载时由用户创建。
PodSchedulingContext
供控制平面和资源驱动程序内部使用, 在需要为 Pod 分配 ResourceClaim 时协调 Pod 调度。

ResourceClass 和 ResourceClaim 的参数存储在单独的对象中,通常使用安装资源驱动程序时创建的 CRD 所定义的类型。

core/v1PodSpecresourceClaims 字段中定义 Pod 所需的 ResourceClaim。 该列表中的条目引用 ResourceClaim 或 ResourceClaimTemplate。 当引用 ResourceClaim 时,使用此 PodSpec 的所有 Pod (例如 Deployment 或 StatefulSet 中的 Pod)共享相同的 ResourceClaim 实例。 引用 ResourceClaimTemplate 时,每个 Pod 都有自己的实例。

容器资源的 resources.claims 列表定义容器可以访问的资源实例, 从而可以实现在一个或多个容器之间共享资源。

下面是一个虚构的资源驱动程序的示例。 该示例将为此 Pod 创建两个 ResourceClaim 对象,每个容器都可以访问其中一个。

apiVersion: resource.k8s.io/v1alpha2
kind: ResourceClass
name: resource.example.com
driverName: resource-driver.example.com
---
apiVersion: cats.resource.example.com/v1
kind: ClaimParameters
name: large-black-cat-claim-parameters
spec:
  color: black
  size: large
---
apiVersion: resource.k8s.io/v1alpha2
kind: ResourceClaimTemplate
metadata:
  name: large-black-cat-claim-template
spec:
  spec:
    resourceClassName: resource.example.com
    parametersRef:
      apiGroup: cats.resource.example.com
      kind: ClaimParameters
      name: large-black-cat-claim-parameters
–--
apiVersion: v1
kind: Pod
metadata:
  name: pod-with-cats
spec:
  containers:
  - name: container0
    image: ubuntu:20.04
    command: ["sleep", "9999"]
    resources:
      claims:
      - name: cat-0
  - name: container1
    image: ubuntu:20.04
    command: ["sleep", "9999"]
    resources:
      claims:
      - name: cat-1
  resourceClaims:
  - name: cat-0
    source:
      resourceClaimTemplateName: large-black-cat-claim-template
  - name: cat-1
    source:
      resourceClaimTemplateName: large-black-cat-claim-template

调度

与原生资源(CPU、RAM)和扩展资源(由设备插件管理,并由 kubelet 公布)不同, 调度器不知道集群中有哪些动态资源, 也不知道如何将它们拆分以满足特定 ResourceClaim 的要求。 资源驱动程序负责这些任务。 资源驱动程序在为 ResourceClaim 保留资源后将其标记为“已分配(Allocated)”。 然后告诉调度器集群中可用的 ResourceClaim 的位置。

ResourceClaim 可以在创建时就进行分配(“立即分配”),不用考虑哪些 Pod 将使用它。 默认情况下采用延迟分配,直到需要 ResourceClaim 的 Pod 被调度时 (即“等待第一个消费者”)再进行分配。

在这种模式下,调度器检查 Pod 所需的所有 ResourceClaim,并创建一个 PodScheduling 对象, 通知负责这些 ResourceClaim 的资源驱动程序,告知它们调度器认为适合该 Pod 的节点。 资源驱动程序通过排除没有足够剩余资源的节点来响应调度器。 一旦调度器有了这些信息,它就会选择一个节点,并将该选择存储在 PodScheduling 对象中。 然后,资源驱动程序为分配其 ResourceClaim,以便资源可用于该节点。 完成后,Pod 就会被调度。

作为此过程的一部分,ResourceClaim 会为 Pod 保留。 目前,ResourceClaim 可以由单个 Pod 独占使用或不限数量的多个 Pod 使用。

除非 Pod 的所有资源都已分配和保留,否则 Pod 不会被调度到节点,这是一个重要特性。 这避免了 Pod 被调度到一个节点但无法在那里运行的情况, 这种情况很糟糕,因为被挂起 Pod 也会阻塞为其保留的其他资源,如 RAM 或 CPU。

监控资源

kubelet 提供了一个 gRPC 服务,以便发现正在运行的 Pod 的动态资源。 有关 gRPC 端点的更多信息,请参阅资源分配报告

预调度的 Pod

当你(或别的 API 客户端)创建设置了 spec.nodeName 的 Pod 时,调度器将被绕过。 如果 Pod 所需的某个 ResourceClaim 尚不存在、未被分配或未为该 Pod 保留,那么 kubelet 将无法运行该 Pod,并会定期重新检查,因为这些要求可能在以后得到满足。

这种情况也可能发生在 Pod 被调度时调度器中未启用动态资源分配支持的时候(原因可能是版本偏差、配置、特性门控等)。 kube-controller-manager 能够检测到这一点,并尝试通过触发分配和/或预留所需的 ResourceClaim 来使 Pod 可运行。

然而,最好避免这种情况,因为分配给节点的 Pod 会锁住一些正常的资源(RAM、CPU), 而这些资源在 Pod 被卡住时无法用于其他 Pod。为了让一个 Pod 在特定节点上运行, 同时仍然通过正常的调度流程进行,请在创建 Pod 时使用与期望的节点精确匹配的节点选择算符:

apiVersion: v1
kind: Pod
metadata:
  name: pod-with-cats
spec:
  nodeSelector:
    kubernetes.io/hostname: name-of-the-intended-node
  ...

你还可以在准入时变更传入的 Pod,取消设置 .spec.nodeName 字段,并改为使用节点选择算符。

启用动态资源分配

动态资源分配是一个 Alpha 特性,只有在启用 DynamicResourceAllocation 特性门控resource.k8s.io/v1alpha2 API 组 时才启用。 有关详细信息,参阅 --feature-gates--runtime-config kube-apiserver 参数。 kube-scheduler、kube-controller-manager 和 kubelet 也需要设置该特性门控。

快速检查 Kubernetes 集群是否支持该功能的方法是列出 ResourceClass 对象:

kubectl get resourceclasses

如果你的集群支持动态资源分配,则响应是 ResourceClass 对象列表或:

No resources found

如果不支持,则会输出如下错误:

error: the server doesn't have a resource type "resourceclasses"

kube-scheduler 的默认配置仅在启用特性门控且使用 v1 配置 API 时才启用 "DynamicResources" 插件。 自定义配置可能需要被修改才能启用它。

除了在集群中启用该功能外,还必须安装资源驱动程序。 欲了解详细信息,请参阅驱动程序的文档。

接下来

3.10.9 - 调度器性能调优

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

作为 kubernetes 集群的默认调度器, kube-scheduler 主要负责将 Pod 调度到集群的 Node 上。

在一个集群中,满足一个 Pod 调度请求的所有 Node 称之为可调度 Node。 调度器先在集群中找到一个 Pod 的可调度 Node,然后根据一系列函数对这些可调度 Node 打分, 之后选出其中得分最高的 Node 来运行 Pod。 最后,调度器将这个调度决定告知 kube-apiserver,这个过程叫做绑定(Binding)

这篇文章将会介绍一些在大规模 Kubernetes 集群下调度器性能优化的方式。

在大规模集群中,你可以调节调度器的表现来平衡调度的延迟(新 Pod 快速就位) 和精度(调度器很少做出糟糕的放置决策)。

你可以通过设置 kube-scheduler 的 percentageOfNodesToScore 来配置这个调优设置。 这个 KubeSchedulerConfiguration 设置决定了调度集群中节点的阈值。

设置阈值

percentageOfNodesToScore 选项接受从 0 到 100 之间的整数值。 0 值比较特殊,表示 kube-scheduler 应该使用其编译后的默认值。 如果你设置 percentageOfNodesToScore 的值超过了 100, kube-scheduler 的表现等价于设置值为 100。

要修改这个值,先编辑 kube-scheduler 的配置文件然后重启调度器。 大多数情况下,这个配置文件是 /etc/kubernetes/config/kube-scheduler.yaml

修改完成后,你可以执行

kubectl get pods -n kube-system | grep kube-scheduler

来检查该 kube-scheduler 组件是否健康。

节点打分阈值

要提升调度性能,kube-scheduler 可以在找到足够的可调度节点之后停止查找。 在大规模集群中,比起考虑每个节点的简单方法相比可以节省时间。

你可以使用整个集群节点总数的百分比作为阈值来指定需要多少节点就足够。 kube-scheduler 会将它转换为节点数的整数值。在调度期间,如果 kube-scheduler 已确认的可调度节点数足以超过了配置的百分比数量, kube-scheduler 将停止继续查找可调度节点并继续进行 打分阶段

调度器如何遍历节点详细介绍了这个过程。

默认阈值

如果你不指定阈值,Kubernetes 使用线性公式计算出一个比例,在 100-节点集群 下取 50%,在 5000-节点的集群下取 10%。这个自动设置的参数的最低值是 5%。

这意味着,调度器至少会对集群中 5% 的节点进行打分,除非用户将该参数设置的低于 5。

如果你想让调度器对集群内所有节点进行打分,则将 percentageOfNodesToScore 设置为 100。

示例

下面就是一个将 percentageOfNodesToScore 参数设置为 50% 的例子。

apiVersion: kubescheduler.config.k8s.io/v1alpha1
kind: KubeSchedulerConfiguration
algorithmSource:
  provider: DefaultProvider

...

percentageOfNodesToScore: 50

调节 percentageOfNodesToScore 参数

percentageOfNodesToScore 的值必须在 1 到 100 之间,而且其默认值是通过集群的规模计算得来的。 另外,还有一个 50 个 Node 的最小值是硬编码在程序中。

值得注意的是,该参数设置后可能会导致只有集群中少数节点被选为可调度节点, 很多节点都没有进入到打分阶段。这样就会造成一种后果, 一个本来可以在打分阶段得分很高的节点甚至都不能进入打分阶段。

由于这个原因,这个参数不应该被设置成一个很低的值。 通常的做法是不会将这个参数的值设置的低于 10。 很低的参数值一般在调度器的吞吐量很高且对节点的打分不重要的情况下才使用。 换句话说,只有当你更倾向于在可调度节点中任意选择一个节点来运行这个 Pod 时, 才使用很低的参数设置。

调度器做调度选择的时候如何覆盖所有的 Node

如果你想要理解这一个特性的内部细节,那么请仔细阅读这一章节。

在将 Pod 调度到节点上时,为了让集群中所有节点都有公平的机会去运行这些 Pod, 调度器将会以轮询的方式覆盖全部的 Node。 你可以将 Node 列表想象成一个数组。调度器从数组的头部开始筛选可调度节点, 依次向后直到可调度节点的数量达到 percentageOfNodesToScore 参数的要求。 在对下一个 Pod 进行调度的时候,前一个 Pod 调度筛选停止的 Node 列表的位置, 将会来作为这次调度筛选 Node 开始的位置。

如果集群中的 Node 在多个区域,那么调度器将从不同的区域中轮询 Node, 来确保不同区域的 Node 接受可调度性检查。如下例,考虑两个区域中的六个节点:

Zone 1: Node 1, Node 2, Node 3, Node 4
Zone 2: Node 5, Node 6

调度器将会按照如下的顺序去评估 Node 的可调度性:

Node 1, Node 5, Node 2, Node 6, Node 3, Node 4

在评估完所有 Node 后,将会返回到 Node 1,从头开始。

接下来

3.10.10 - 资源装箱

在 kube-scheduler 的调度插件 NodeResourcesFit 中存在两种支持资源装箱(bin packing)的策略:MostAllocatedRequestedToCapacityRatio

使用 MostAllocated 策略启用资源装箱

MostAllocated 策略基于资源的利用率来为节点计分,优选分配比率较高的节点。 针对每种资源类型,你可以设置一个权重值以改变其对节点得分的影响。

要为插件 NodeResourcesFit 设置 MostAllocated 策略, 可以使用一个类似于下面这样的调度器配置

apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
- pluginConfig:
  - args:
      scoringStrategy:
        resources:
        - name: cpu
          weight: 1
        - name: memory
          weight: 1
        - name: intel.com/foo
          weight: 3
        - name: intel.com/bar
          weight: 3
        type: MostAllocated
    name: NodeResourcesFit

要进一步了解其它参数及其默认配置,请参阅 NodeResourcesFitArgs 的 API 文档。

使用 RequestedToCapacityRatio 策略来启用资源装箱

RequestedToCapacityRatio 策略允许用户基于请求值与容量的比率,针对参与节点计分的每类资源设置权重。 这一策略使得用户可以使用合适的参数来对扩展资源执行装箱操作,进而提升大规模集群中稀有资源的利用率。 此策略根据所分配资源的一个配置函数来评价节点。 NodeResourcesFit 计分函数中的 RequestedToCapacityRatio 可以通过 scoringStrategy 字段来控制。在 scoringStrategy 字段中,你可以配置两个参数: requestedToCapacityRatioresourcesrequestedToCapacityRatio 参数中的 shape 设置使得用户能够调整函数的算法,基于 utilizationscore 值计算最少请求或最多请求。 resources 参数中包含计分过程中需要考虑的资源的 name,以及对应的 weight, 后者指定了每个资源的权重。

下面是一个配置示例,使用 requestedToCapacityRatio 字段为扩展资源 intel.com/foointel.com/bar 设置装箱行为:

apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
- pluginConfig:
  - args:
      scoringStrategy:
        resources:
        - name: intel.com/foo
          weight: 3
        - name: intel.com/bar
          weight: 3
        requestedToCapacityRatio:
          shape:
          - utilization: 0
            score: 0
          - utilization: 100
            score: 10
        type: RequestedToCapacityRatio
    name: NodeResourcesFit

使用 kube-scheduler 标志 --config=/path/to/config/file 引用 KubeSchedulerConfiguration 文件,可以将配置传递给调度器。

要进一步了解其它参数及其默认配置,可以参阅 NodeResourcesFitArgs 的 API 文档。

调整计分函数

shape 用于指定 RequestedToCapacityRatio 函数的行为。

shape:
  - utilization: 0
    score: 0
  - utilization: 100
    score: 10

上面的参数在 utilization 为 0% 时给节点评分为 0,在 utilization 为 100% 时给节点评分为 10,因此启用了装箱行为。 要启用最少请求(least requested)模式,必须按如下方式反转得分值。

shape:
  - utilization: 0
    score: 10
  - utilization: 100
    score: 0

resources 是一个可选参数,默认值为:

resources:
  - name: cpu
    weight: 1
  - name: memory
    weight: 1

它可以像下面这样用来添加扩展资源:

resources:
  - name: intel.com/foo
    weight: 5
  - name: cpu
    weight: 3
  - name: memory
    weight: 1

weight 参数是可选的,如果未指定,则设置为 1。 同时,weight 不能设置为负值。

节点容量分配的评分

本节适用于希望了解此功能的内部细节的人员。 以下是如何针对给定的一组值来计算节点得分的示例。

请求的资源:

intel.com/foo : 2
memory: 256MB
cpu: 2

资源权重:

intel.com/foo : 5
memory: 1
cpu: 3
FunctionShapePoint {{0, 0}, {100, 10}}

节点 1 配置:

可用:
  intel.com/foo: 4
  memory: 1 GB
  cpu: 8

已用:
  intel.com/foo: 1
  memory: 256MB
  cpu: 1

节点得分:

intel.com/foo  = resourceScoringFunction((2+1),4)
               = (100 - ((4-3)*100/4)
               = (100 - 25)
               = 75                       # requested + used = 75% * available
               = rawScoringFunction(75)
               = 7                        # floor(75/10)

memory         = resourceScoringFunction((256+256),1024)
               = (100 -((1024-512)*100/1024))
               = 50                       # requested + used = 50% * available
               = rawScoringFunction(50)
               = 5                        # floor(50/10)

cpu            = resourceScoringFunction((2+1),8)
               = (100 -((8-3)*100/8))
               = 37.5                     # requested + used = 37.5% * available
               = rawScoringFunction(37.5)
               = 3                        # floor(37.5/10)

NodeScore   =  ((7 * 5) + (5 * 1) + (3 * 3)) / (5 + 1 + 3)
            =  5

节点 2 配置:

可用:
  intel.com/foo: 8
  memory: 1GB
  cpu: 8

已用:
  intel.com/foo: 2
  memory: 512MB
  cpu: 6

节点得分:

intel.com/foo  = resourceScoringFunction((2+2),8)
               = (100 - ((8-4)*100/8)
               = (100 - 50)
               = 50
               = rawScoringFunction(50)
               = 5

memory         = resourceScoringFunction((256+512),1024)
               = (100 -((1024-768)*100/1024))
               = 75
               = rawScoringFunction(75)
               = 7

cpu            = resourceScoringFunction((2+6),8)
               = (100 -((8-8)*100/8))
               = 100
               = rawScoringFunction(100)
               = 10

NodeScore   =  ((5 * 5) + (7 * 1) + (10 * 3)) / (5 + 1 + 3)
            =  7

接下来

3.10.11 - Pod 优先级和抢占

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

Pod 可以有优先级。 优先级表示一个 Pod 相对于其他 Pod 的重要性。 如果一个 Pod 无法被调度,调度程序会尝试抢占(驱逐)较低优先级的 Pod, 以使悬决 Pod 可以被调度。

如何使用优先级和抢占

要使用优先级和抢占:

  1. 新增一个或多个 PriorityClass

  2. 创建 Pod,并将其 priorityClassName 设置为新增的 PriorityClass。 当然你不需要直接创建 Pod;通常,你将会添加 priorityClassName 到集合对象(如 Deployment) 的 Pod 模板中。

继续阅读以获取有关这些步骤的更多信息。

PriorityClass

PriorityClass 是一个无命名空间对象,它定义了从优先级类名称到优先级整数值的映射。 名称在 PriorityClass 对象元数据的 name 字段中指定。 值在必填的 value 字段中指定。值越大,优先级越高。 PriorityClass 对象的名称必须是有效的 DNS 子域名, 并且它不能以 system- 为前缀。

PriorityClass 对象可以设置任何小于或等于 10 亿的 32 位整数值。 这意味着 PriorityClass 对象的值范围是从 -2,147,483,648 到 1,000,000,000(含)。 保留更大的数字,用于表示关键系统 Pod 的内置 PriorityClass。 集群管理员应该为这类映射分别创建独立的 PriorityClass 对象。

PriorityClass 还有两个可选字段:globalDefaultdescriptionglobalDefault 字段表示这个 PriorityClass 的值应该用于没有 priorityClassName 的 Pod。 系统中只能存在一个 globalDefault 设置为 true 的 PriorityClass。 如果不存在设置了 globalDefault 的 PriorityClass, 则没有 priorityClassName 的 Pod 的优先级为零。

description 字段是一个任意字符串。 它用来告诉集群用户何时应该使用此 PriorityClass。

关于 PodPriority 和现有集群的注意事项

  • 如果你升级一个已经存在的但尚未使用此特性的集群,该集群中已经存在的 Pod 的优先级等效于零。

  • 添加一个将 globalDefault 设置为 true 的 PriorityClass 不会改变现有 Pod 的优先级。 此类 PriorityClass 的值仅用于添加 PriorityClass 后创建的 Pod。

  • 如果你删除了某个 PriorityClass 对象,则使用被删除的 PriorityClass 名称的现有 Pod 保持不变, 但是你不能再创建使用已删除的 PriorityClass 名称的 Pod。

PriorityClass 示例

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority
value: 1000000
globalDefault: false
description: "此优先级类应仅用于 XYZ 服务 Pod。"

非抢占式 PriorityClass

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

配置了 preemptionPolicy: Never 的 Pod 将被放置在调度队列中较低优先级 Pod 之前, 但它们不能抢占其他 Pod。等待调度的非抢占式 Pod 将留在调度队列中,直到有足够的可用资源, 它才可以被调度。非抢占式 Pod,像其他 Pod 一样,受调度程序回退的影响。 这意味着如果调度程序尝试这些 Pod 并且无法调度它们,它们将以更低的频率被重试, 从而允许其他优先级较低的 Pod 排在它们之前。

非抢占式 Pod 仍可能被其他高优先级 Pod 抢占。

preemptionPolicy 默认为 PreemptLowerPriority, 这将允许该 PriorityClass 的 Pod 抢占较低优先级的 Pod(现有默认行为也是如此)。 如果 preemptionPolicy 设置为 Never,则该 PriorityClass 中的 Pod 将是非抢占式的。

数据科学工作负载是一个示例用例。用户可以提交他们希望优先于其他工作负载的作业, 但不希望因为抢占运行中的 Pod 而导致现有工作被丢弃。 设置为 preemptionPolicy: Never 的高优先级作业将在其他排队的 Pod 之前被调度, 只要足够的集群资源“自然地”变得可用。

非抢占式 PriorityClass 示例

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority-nonpreempting
value: 1000000
preemptionPolicy: Never
globalDefault: false
description: "This priority class will not cause other pods to be preempted."

Pod 优先级

在你拥有一个或多个 PriorityClass 对象之后, 你可以创建在其规约中指定这些 PriorityClass 名称之一的 Pod。 优先级准入控制器使用 priorityClassName 字段并填充优先级的整数值。 如果未找到所指定的优先级类,则拒绝 Pod。

以下 YAML 是 Pod 配置的示例,它使用在前面的示例中创建的 PriorityClass。 优先级准入控制器检查 Pod 规约并将其优先级解析为 1000000。

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  priorityClassName: high-priority

Pod 优先级对调度顺序的影响

当启用 Pod 优先级时,调度程序会按优先级对悬决 Pod 进行排序, 并且每个悬决的 Pod 会被放置在调度队列中其他优先级较低的悬决 Pod 之前。 因此,如果满足调度要求,较高优先级的 Pod 可能会比具有较低优先级的 Pod 更早调度。 如果无法调度此类 Pod,调度程序将继续并尝试调度其他较低优先级的 Pod。

抢占

Pod 被创建后会进入队列等待调度。 调度器从队列中挑选一个 Pod 并尝试将它调度到某个节点上。 如果没有找到满足 Pod 的所指定的所有要求的节点,则触发对悬决 Pod 的抢占逻辑。 让我们将悬决 Pod 称为 P。抢占逻辑试图找到一个节点, 在该节点中删除一个或多个优先级低于 P 的 Pod,则可以将 P 调度到该节点上。 如果找到这样的节点,一个或多个优先级较低的 Pod 会被从节点中驱逐。 被驱逐的 Pod 消失后,P 可以被调度到该节点上。

用户暴露的信息

当 Pod P 抢占节点 N 上的一个或多个 Pod 时, Pod P 状态的 nominatedNodeName 字段被设置为节点 N 的名称。 该字段帮助调度程序跟踪为 Pod P 保留的资源,并为用户提供有关其集群中抢占的信息。

请注意,Pod P 不一定会调度到“被提名的节点(Nominated Node)”。 调度程序总是在迭代任何其他节点之前尝试“指定节点”。 在 Pod 因抢占而牺牲时,它们将获得体面终止期。 如果调度程序正在等待牺牲者 Pod 终止时另一个节点变得可用, 则调度程序可以使用另一个节点来调度 Pod P。 因此,Pod 规约中的 nominatedNodeNamenodeName 并不总是相同。 此外,如果调度程序抢占节点 N 上的 Pod,但随后比 Pod P 更高优先级的 Pod 到达, 则调度程序可能会将节点 N 分配给新的更高优先级的 Pod。 在这种情况下,调度程序会清除 Pod P 的 nominatedNodeName。 通过这样做,调度程序使 Pod P 有资格抢占另一个节点上的 Pod。

抢占的限制

被抢占牺牲者的体面终止

当 Pod 被抢占时,牺牲者会得到他们的 体面终止期。 它们可以在体面终止期内完成工作并退出。如果它们不这样做就会被杀死。 这个体面终止期在调度程序抢占 Pod 的时间点和待处理的 Pod (P) 可以在节点 (N) 上调度的时间点之间划分出了一个时间跨度。 同时,调度器会继续调度其他待处理的 Pod。当牺牲者退出或被终止时, 调度程序会尝试在待处理队列中调度 Pod。 因此,调度器抢占牺牲者的时间点与 Pod P 被调度的时间点之间通常存在时间间隔。 为了最小化这个差距,可以将低优先级 Pod 的体面终止时间设置为零或一个小数字。

支持 PodDisruptionBudget,但不保证

PodDisruptionBudget (PDB) 允许多副本应用程序的所有者限制因自愿性质的干扰而同时终止的 Pod 数量。 Kubernetes 在抢占 Pod 时支持 PDB,但对 PDB 的支持是基于尽力而为原则的。 调度器会尝试寻找不会因被抢占而违反 PDB 的牺牲者,但如果没有找到这样的牺牲者, 抢占仍然会发生,并且即使违反了 PDB 约束也会删除优先级较低的 Pod。

与低优先级 Pod 之间的 Pod 间亲和性

只有当这个问题的答案是肯定的时,才考虑在一个节点上执行抢占操作: “如果从此节点上删除优先级低于悬决 Pod 的所有 Pod,悬决 Pod 是否可以在该节点上调度?”

如果悬决 Pod 与节点上的一个或多个较低优先级 Pod 具有 Pod 间亲和性, 则在没有这些较低优先级 Pod 的情况下,无法满足 Pod 间亲和性规则。 在这种情况下,调度程序不会抢占节点上的任何 Pod。 相反,它寻找另一个节点。调度程序可能会找到合适的节点, 也可能不会。无法保证悬决 Pod 可以被调度。

我们针对此问题推荐的解决方案是仅针对同等或更高优先级的 Pod 设置 Pod 间亲和性。

跨节点抢占

假设正在考虑在一个节点 N 上执行抢占,以便可以在 N 上调度待处理的 Pod P。 只有当另一个节点上的 Pod 被抢占时,P 才可能在 N 上变得可行。 下面是一个例子:

  • 调度器正在考虑将 Pod P 调度到节点 N 上。
  • Pod Q 正在与节点 N 位于同一区域的另一个节点上运行。
  • Pod P 与 Pod Q 具有 Zone 维度的反亲和(topologyKey:topology.kubernetes.io/zone)设置。
  • Pod P 与 Zone 中的其他 Pod 之间没有其他反亲和性设置。
  • 为了在节点 N 上调度 Pod P,可以抢占 Pod Q,但调度器不会进行跨节点抢占。 因此,Pod P 将被视为在节点 N 上不可调度。

如果将 Pod Q 从所在节点中移除,则不会违反 Pod 间反亲和性约束, 并且 Pod P 可能会被调度到节点 N 上。

如果有足够的需求,并且如果我们找到性能合理的算法, 我们可能会考虑在未来版本中添加跨节点抢占。

故障排除

Pod 优先级和抢占可能会产生不必要的副作用。以下是一些潜在问题的示例以及处理这些问题的方法。

Pod 被不必要地抢占

抢占在资源压力较大时从集群中删除现有 Pod,为更高优先级的悬决 Pod 腾出空间。 如果你错误地为某些 Pod 设置了高优先级,这些无意的高优先级 Pod 可能会导致集群中出现抢占行为。 Pod 优先级是通过设置 Pod 规约中的 priorityClassName 字段来指定的。 优先级的整数值然后被解析并填充到 podSpecpriority 字段。

为了解决这个问题,你可以将这些 Pod 的 priorityClassName 更改为使用较低优先级的类, 或者将该字段留空。默认情况下,空的 priorityClassName 解析为零。

当 Pod 被抢占时,集群会为被抢占的 Pod 记录事件。只有当集群没有足够的资源用于 Pod 时, 才会发生抢占。在这种情况下,只有当悬决 Pod(抢占者)的优先级高于受害 Pod 时才会发生抢占。 当没有悬决 Pod,或者悬决 Pod 的优先级等于或低于牺牲者时,不得发生抢占。 如果在这种情况下发生抢占,请提出问题。

有 Pod 被抢占,但抢占者并没有被调度

当 Pod 被抢占时,它们会收到请求的体面终止期,默认为 30 秒。 如果受害 Pod 在此期限内没有终止,它们将被强制终止。 一旦所有牺牲者都离开,就可以调度抢占者 Pod。

在抢占者 Pod 等待牺牲者离开的同时,可能某个适合同一个节点的更高优先级的 Pod 被创建。 在这种情况下,调度器将调度优先级更高的 Pod 而不是抢占者。

这是预期的行为:具有较高优先级的 Pod 应该取代具有较低优先级的 Pod。

优先级较高的 Pod 在优先级较低的 Pod 之前被抢占

调度程序尝试查找可以运行悬决 Pod 的节点。如果没有找到这样的节点, 调度程序会尝试从任意节点中删除优先级较低的 Pod,以便为悬决 Pod 腾出空间。 如果具有低优先级 Pod 的节点无法运行悬决 Pod, 调度器可能会选择另一个具有更高优先级 Pod 的节点(与其他节点上的 Pod 相比)进行抢占。 牺牲者的优先级必须仍然低于抢占者 Pod。

当有多个节点可供执行抢占操作时,调度器会尝试选择具有一组优先级最低的 Pod 的节点。 但是,如果此类 Pod 具有 PodDisruptionBudget,当它们被抢占时, 则会违反 PodDisruptionBudget,那么调度程序可能会选择另一个具有更高优先级 Pod 的节点。

当存在多个节点抢占且上述场景均不适用时,调度器会选择优先级最低的节点。

Pod 优先级和服务质量之间的相互作用

Pod 优先级和 QoS 类 是两个正交特征,交互很少,并且对基于 QoS 类设置 Pod 的优先级没有默认限制。 调度器的抢占逻辑在选择抢占目标时不考虑 QoS。 抢占会考虑 Pod 优先级并尝试选择一组优先级最低的目标。 仅当移除优先级最低的 Pod 不足以让调度程序调度抢占式 Pod, 或者最低优先级的 Pod 受 PodDisruptionBudget 保护时,才会考虑优先级较高的 Pod。

kubelet 使用优先级来确定 节点压力驱逐 Pod 的顺序。 你可以使用 QoS 类来估计 Pod 最有可能被驱逐的顺序。kubelet 根据以下因素对 Pod 进行驱逐排名:

  1. 对紧俏资源的使用是否超过请求值
  2. Pod 优先级
  3. 相对于请求的资源使用量

有关更多详细信息,请参阅 kubelet 驱逐时 Pod 的选择

当某 Pod 的资源用量未超过其请求时,kubelet 节点压力驱逐不会驱逐该 Pod。 如果优先级较低的 Pod 的资源使用量没有超过其请求,则不会被驱逐。 另一个优先级较高且资源使用量超过其请求的 Pod 可能会被驱逐。

接下来

3.10.12 - 节点压力驱逐

节点压力驱逐是 kubelet 主动终止 Pod 以回收节点上资源的过程。

kubelet 监控集群节点的内存、磁盘空间和文件系统的 inode 等资源。 当这些资源中的一个或者多个达到特定的消耗水平, kubelet 可以主动地使节点上一个或者多个 Pod 失效,以回收资源防止饥饿。

在节点压力驱逐期间,kubelet 将所选 Pod 的阶段 设置为 Failed 并终止 Pod。

节点压力驱逐不同于 API 发起的驱逐

kubelet 并不理会你配置的 PodDisruptionBudget 或者是 Pod 的 terminationGracePeriodSeconds。 如果你使用了软驱逐条件,kubelet 会考虑你所配置的 eviction-max-pod-grace-period。 如果你使用了硬驱逐条件,kubelet 使用 0s 宽限期(立即关闭)来终止 Pod。

自我修复行为

kubelet 在终止最终用户 Pod 之前会尝试回收节点级资源。 例如,它会在磁盘资源不足时删除未使用的容器镜像。

如果 Pod 是由替换失败 Pod 的工作负载资源 (例如 StatefulSet 或者 Deployment)管理, 则控制平面或 kube-controller-manager 会创建新的 Pod 来代替被驱逐的 Pod。

静态 Pod 的自我修复

如果你在面临资源压力的节点上运行静态 Pod,则 kubelet 可能会驱逐该静态 Pod。 由于静态 Pod 始终表示在该节点上运行 Pod 的意图,kubelet 会尝试创建替代 Pod。

创建替代 Pod 时,kubelet 会考虑静态 Pod 的优先级。如果静态 Pod 清单指定了低优先级, 并且集群的控制平面内定义了优先级更高的 Pod,并且节点面临资源压力,则 kubelet 可能无法为该静态 Pod 腾出空间。 即使节点上存在资源压力,kubelet 也会继续尝试运行所有静态 pod。

驱逐信号和阈值

kubelet 使用各种参数来做出驱逐决定,如下所示:

  • 驱逐信号
  • 驱逐条件
  • 监控间隔

驱逐信号

驱逐信号是特定资源在特定时间点的当前状态。 kubelet 使用驱逐信号,通过将信号与驱逐条件进行比较来做出驱逐决定, 驱逐条件是节点上应该可用资源的最小量。

Linux 系统中,kubelet 使用以下驱逐信号:

驱逐信号描述
memory.availablememory.available := node.status.capacity[memory] - node.stats.memory.workingSet
nodefs.availablenodefs.available := node.stats.fs.available
nodefs.inodesFreenodefs.inodesFree := node.stats.fs.inodesFree
imagefs.availableimagefs.available := node.stats.runtime.imagefs.available
imagefs.inodesFreeimagefs.inodesFree := node.stats.runtime.imagefs.inodesFree
pid.availablepid.available := node.stats.rlimit.maxpid - node.stats.rlimit.curproc

在上表中,描述列显示了 kubelet 如何获取信号的值。每个信号支持百分比值或者是字面值。 kubelet 计算相对于与信号有关的总量的百分比值。

memory.available 的值来自 cgroupfs,而不是像 free -m 这样的工具。 这很重要,因为 free -m 在容器中不起作用,如果用户使用 节点可分配资源 这一功能特性,资源不足的判定是基于 cgroup 层次结构中的用户 Pod 所处的局部及 cgroup 根节点作出的。 这个脚本或者 cgroupv2 脚本 重现了 kubelet 为计算 memory.available 而执行的相同步骤。 kubelet 在其计算中排除了 inactive_file(非活动 LRU 列表上基于文件来虚拟的内存的字节数), 因为它假定在压力下内存是可回收的。

kubelet 可识别以下两个特定的文件系统标识符:

  1. nodefs:节点的主要文件系统,用于本地磁盘卷、不受内存支持的 emptyDir 卷、日志存储等。 例如,nodefs 包含 /var/lib/kubelet/
  2. imagefs:可选文件系统,供容器运行时存储容器镜像和容器可写层。

kubelet 会自动发现这些文件系统并忽略节点本地的其它文件系统。kubelet 不支持其他配置。

一些 kubelet 垃圾收集功能已被弃用,以鼓励使用驱逐机制。

现有标志原因
--maximum-dead-containers一旦旧的日志存储在容器的上下文之外就会被弃用
--maximum-dead-containers-per-container一旦旧的日志存储在容器的上下文之外就会被弃用
--minimum-container-ttl-duration一旦旧的日志存储在容器的上下文之外就会被弃用

驱逐条件

你可以为 kubelet 指定自定义驱逐条件,以便在作出驱逐决定时使用。 你可以设置软性的硬性的驱逐阈值。

驱逐条件的形式为 [eviction-signal][operator][quantity],其中:

  • eviction-signal 是要使用的驱逐信号
  • operator 是你想要的关系运算符, 比如 <(小于)。
  • quantity 是驱逐条件数量,例如 1Giquantity 的值必须与 Kubernetes 使用的数量表示相匹配。 你可以使用文字值或百分比(%)。

例如,如果一个节点的总内存为 10GiB 并且你希望在可用内存低于 1GiB 时触发驱逐, 则可以将驱逐条件定义为 memory.available<10%memory.available< 1G(你不能同时使用二者)。

你可以配置软和硬驱逐条件。

软驱逐条件

软驱逐条件将驱逐条件与管理员所必须指定的宽限期配对。 在超过宽限期之前,kubelet 不会驱逐 Pod。 如果没有指定的宽限期,kubelet 会在启动时返回错误。

你可以既指定软驱逐条件宽限期,又指定 Pod 终止宽限期的上限,给 kubelet 在驱逐期间使用。 如果你指定了宽限期的上限并且 Pod 满足软驱逐阈条件,则 kubelet 将使用两个宽限期中的较小者。 如果你没有指定宽限期上限,kubelet 会立即杀死被驱逐的 Pod,不允许其体面终止。

你可以使用以下标志来配置软驱逐条件:

  • eviction-soft:一组驱逐条件,如 memory.available<1.5Gi, 如果驱逐条件持续时长超过指定的宽限期,可以触发 Pod 驱逐。
  • eviction-soft-grace-period:一组驱逐宽限期, 如 memory.available=1m30s,定义软驱逐条件在触发 Pod 驱逐之前必须保持多长时间。
  • eviction-max-pod-grace-period:在满足软驱逐条件而终止 Pod 时使用的最大允许宽限期(以秒为单位)。

硬驱逐条件

硬驱逐条件没有宽限期。当达到硬驱逐条件时, kubelet 会立即杀死 pod,而不会正常终止以回收紧缺的资源。

你可以使用 eviction-hard 标志来配置一组硬驱逐条件, 例如 memory.available<1Gi

kubelet 具有以下默认硬驱逐条件:

  • memory.available<100Mi
  • nodefs.available<10%
  • imagefs.available<15%
  • nodefs.inodesFree<5%(Linux 节点)

只有在没有更改任何参数的情况下,硬驱逐阈值才会被设置成这些默认值。 如果你更改了任何参数的值,则其他参数的取值不会继承其默认值设置,而将被设置为零。 为了提供自定义值,你应该分别设置所有阈值。

驱逐监测间隔

kubelet 根据其配置的 housekeeping-interval(默认为 10s)评估驱逐条件。

节点状况

kubelet 报告节点状况以反映节点处于压力之下, 原因是满足硬性的或软性的驱逐条件,与配置的宽限期无关。

kubelet 根据下表将驱逐信号映射为节点状况:

节点条件驱逐信号描述
MemoryPressurememory.available节点上的可用内存已满足驱逐条件
DiskPressurenodefs.availablenodefs.inodesFreeimagefs.availableimagefs.inodesFree节点的根文件系统或镜像文件系统上的可用磁盘空间和 inode 已满足驱逐条件
PIDPressurepid.available(Linux) 节点上的可用进程标识符已低于驱逐条件

控制平面还将这些节点状况映射为其污点。

kubelet 根据配置的 --node-status-update-frequency 更新节点条件,默认为 10s

节点状况波动

在某些情况下,节点在软驱逐条件上下振荡,而没有保持定义的宽限期。 这会导致报告的节点条件在 truefalse 之间不断切换,从而导致错误的驱逐决策。

为了防止振荡,你可以使用 eviction-pressure-transition-period 标志, 该标志控制 kubelet 在将节点条件转换为不同状态之前必须等待的时间。 过渡期的默认值为 5m

回收节点级资源

kubelet 在驱逐最终用户 Pod 之前会先尝试回收节点级资源。

当报告 DiskPressure 节点状况时,kubelet 会根据节点上的文件系统回收节点级资源。

imagefs

如果节点有一个专用的 imagefs 文件系统供容器运行时使用,kubelet 会执行以下操作:

  • 如果 nodefs 文件系统满足驱逐条件,kubelet 垃圾收集死亡 Pod 和容器。
  • 如果 imagefs 文件系统满足驱逐条件,kubelet 将删除所有未使用的镜像。

没有 imagefs

如果节点只有一个满足驱逐条件的 nodefs 文件系统, kubelet 按以下顺序释放磁盘空间:

  1. 对死亡的 Pod 和容器进行垃圾收集
  2. 删除未使用的镜像

kubelet 驱逐时 Pod 的选择

如果 kubelet 回收节点级资源的尝试没有使驱逐信号低于条件, 则 kubelet 开始驱逐最终用户 Pod。

kubelet 使用以下参数来确定 Pod 驱逐顺序:

  1. Pod 的资源使用是否超过其请求
  2. Pod 优先级
  3. Pod 相对于请求的资源使用情况

因此,kubelet 按以下顺序排列和驱逐 Pod:

  1. 首先考虑资源使用量超过其请求的 BestEffortBurstable Pod。 这些 Pod 会根据它们的优先级以及它们的资源使用级别超过其请求的程度被逐出。
  2. 资源使用量少于请求量的 Guaranteed Pod 和 Burstable Pod 根据其优先级被最后驱逐。

仅当 Guaranteed Pod 中所有容器都被指定了请求和限制并且二者相等时,才保证 Pod 不被驱逐。 这些 Pod 永远不会因为另一个 Pod 的资源消耗而被驱逐。 如果系统守护进程(例如 kubeletjournald) 消耗的资源比通过 system-reservedkube-reserved 分配保留的资源多, 并且该节点只有 GuaranteedBurstable Pod 使用的资源少于其上剩余的请求, 那么 kubelet 必须选择驱逐这些 Pod 中的一个以保持节点稳定性并减少资源匮乏对其他 Pod 的影响。 在这种情况下,它会选择首先驱逐最低优先级的 Pod。

如果你正在运行静态 Pod 并且希望避免其在资源压力下被驱逐,请直接为该 Pod 设置 priority 字段。 静态 Pod 不支持 priorityClassName 字段。

当 kubelet 因 inode 或 进程 ID 不足而驱逐 Pod 时, 它使用 Pod 的相对优先级来确定驱逐顺序,因为 inode 和 PID 没有对应的请求字段。

kubelet 根据节点是否具有专用的 imagefs 文件系统对 Pod 进行不同的排序:

imagefs

如果 nodefs 触发驱逐, kubelet 会根据 nodefs 使用情况(本地卷 + 所有容器的日志)对 Pod 进行排序。

如果 imagefs 触发驱逐,kubelet 会根据所有容器的可写层使用情况对 Pod 进行排序。

没有 imagefs

如果 nodefs 触发驱逐, kubelet 会根据磁盘总用量(本地卷 + 日志和所有容器的可写层)对 Pod 进行排序。

最小驱逐回收

在某些情况下,驱逐 Pod 只会回收少量的紧俏资源。 这可能导致 kubelet 反复达到配置的驱逐条件并触发多次驱逐。

你可以使用 --eviction-minimum-reclaim 标志或 kubelet 配置文件 为每个资源配置最小回收量。 当 kubelet 注意到某个资源耗尽时,它会继续回收该资源,直到回收到你所指定的数量为止。

例如,以下配置设置最小回收量:

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
evictionHard:
  memory.available: "500Mi"
  nodefs.available: "1Gi"
  imagefs.available: "100Gi"
evictionMinimumReclaim:
  memory.available: "0Mi"
  nodefs.available: "500Mi"
  imagefs.available: "2Gi"

在这个例子中,如果 nodefs.available 信号满足驱逐条件, kubelet 会回收资源,直到信号达到 1GiB 的条件, 然后继续回收至少 500MiB 直到信号达到 1.5GiB。

类似地,kubelet 尝试回收 imagefs 资源,直到 imagefs.available 值达到 102Gi, 即 102 GiB 的可用容器镜像存储。如果 kubelet 可以回收的存储量小于 2GiB, 则 kubelet 不会回收任何内容。

对于所有资源,默认的 eviction-minimum-reclaim0

节点内存不足行为

如果节点在 kubelet 能够回收内存之前遇到内存不足(OOM)事件, 则节点依赖 oom_killer 来响应。

kubelet 根据 Pod 的服务质量(QoS)为每个容器设置一个 oom_score_adj 值。

服务质量oom_score_adj
Guaranteed-997
BestEffort1000
Burstablemin(max(2, 1000 - (1000 * memoryRequestBytes) / machineMemoryCapacityBytes), 999)

如果 kubelet 在节点遇到 OOM 之前无法回收内存, 则 oom_killer 根据它在节点上使用的内存百分比计算 oom_score, 然后加上 oom_score_adj 得到每个容器有效的 oom_score。 然后它会杀死得分最高的容器。

这意味着低 QoS Pod 中相对于其调度请求消耗内存较多的容器,将首先被杀死。

与 Pod 驱逐不同,如果容器被 OOM 杀死, kubelet 可以根据其 restartPolicy 重新启动它。

良好实践

以下各小节阐述驱逐配置的好的做法。

可调度的资源和驱逐策略

当你为 kubelet 配置驱逐策略时, 你应该确保调度程序不会在 Pod 触发驱逐时对其进行调度,因为这类 Pod 会立即引起内存压力。

考虑以下场景:

  • 节点内存容量:10GiB
  • 操作员希望为系统守护进程(内核、kubelet 等)保留 10% 的内存容量
  • 操作员希望在节点内存利用率达到 95% 以上时驱逐 Pod,以减少系统 OOM 的概率。

为此,kubelet 启动设置如下:

--eviction-hard=memory.available<500Mi
--system-reserved=memory=1.5Gi

在此配置中,--system-reserved 标志为系统预留了 1GiB 的内存, 即 总内存的 10% + 驱逐条件量

如果 Pod 使用的内存超过其请求值或者系统使用的内存超过 1Gi, 则节点可以达到驱逐条件,这使得 memory.available 信号低于 500MiB 并触发条件。

DaemonSets 和节点压力驱逐

Pod 优先级是做出驱逐决定的主要因素。 如果你不希望 kubelet 驱逐属于 DaemonSet 的 Pod, 请在 Pod 规约中通过指定合适的 priorityClassName 为这些 Pod 提供足够高的 priorityClass。 你还可以使用较低优先级或默认优先级,以便 仅在有足够资源时才运行 DaemonSet Pod。

已知问题

以下部分描述了与资源不足处理相关的已知问题。

kubelet 可能不会立即观察到内存压力

默认情况下,kubelet 轮询 cAdvisor 以定期收集内存使用情况统计信息。 如果该轮询时间窗口内内存使用量迅速增加,kubelet 可能无法足够快地观察到 MemoryPressure, 但是 OOM killer 仍将被调用。

你可以使用 --kernel-memcg-notification 标志在 kubelet 上启用 memcg 通知 API,以便在超过条件时立即收到通知。

如果你不是追求极端利用率,而是要采取合理的过量使用措施, 则解决此问题的可行方法是使用 --kube-reserved--system-reserved 标志为系统分配内存。

active_file 内存未被视为可用内存

在 Linux 上,内核跟踪活动最近最少使用(LRU)列表上的基于文件所虚拟的内存字节数作为 active_file 统计信息。 kubelet 将 active_file 内存区域视为不可回收。 对于大量使用块设备形式的本地存储(包括临时本地存储)的工作负载, 文件和块数据的内核级缓存意味着许多最近访问的缓存页面可能被计为 active_file。 如果这些内核块缓冲区中在活动 LRU 列表上有足够多, kubelet 很容易将其视为资源用量过量并为节点设置内存压力污点,从而触发 Pod 驱逐。

更多细节请参见 https://github.com/kubernetes/kubernetes/issues/43916

你可以通过为可能执行 I/O 密集型活动的容器设置相同的内存限制和内存请求来应对该行为。 你将需要估计或测量该容器的最佳内存限制值。

接下来

3.10.13 - API 发起的驱逐

API 发起的驱逐是一个先调用 Eviction API 创建 Eviction 对象,再由该对象体面地中止 Pod 的过程。

你可以通过直接调用 Eviction API 发起驱逐,也可以通过编程的方式使用 API 服务器的客户端来发起驱逐, 比如 kubectl drain 命令。 此操作创建一个 Eviction 对象,该对象再驱动 API 服务器终止选定的 Pod。

API 发起的驱逐将遵从你的 PodDisruptionBudgetsterminationGracePeriodSeconds 配置。

使用 API 创建 Eviction 对象,就像对 Pod 执行策略控制的 DELETE 操作

调用 Eviction API

你可以使用 Kubernetes 语言客户端 来访问 Kubernetes API 并创建 Eviction 对象。 要执行此操作,你应该用 POST 发出要尝试的请求,类似于下面的示例:

{
  "apiVersion": "policy/v1",
  "kind": "Eviction",
  "metadata": {
    "name": "quux",
    "namespace": "default"
  }
}

{
  "apiVersion": "policy/v1beta1",
  "kind": "Eviction",
  "metadata": {
    "name": "quux",
    "namespace": "default"
  }
}

或者,你可以通过使用 curl 或者 wget 来访问 API 以尝试驱逐操作,类似于以下示例:

curl -v -H 'Content-type: application/json' https://your-cluster-api-endpoint.example/api/v1/namespaces/default/pods/quux/eviction -d @eviction.json

API 发起驱逐的工作原理

当你使用 API 来请求驱逐时,API 服务器将执行准入检查,并通过以下方式之一做出响应:

  • 200 OK:允许驱逐,子资源 Eviction 被创建,并且 Pod 被删除, 类似于发送一个 DELETE 请求到 Pod 地址。
  • 429 Too Many Requests:当前不允许驱逐,因为配置了 PodDisruptionBudget。 你可以稍后再尝试驱逐。你也可能因为 API 速率限制而看到这种响应。
  • 500 Internal Server Error:不允许驱逐,因为存在配置错误, 例如存在多个 PodDisruptionBudgets 引用同一个 Pod。

如果你想驱逐的 Pod 不属于有 PodDisruptionBudget 的工作负载, API 服务器总是返回 200 OK 并且允许驱逐。

如果 API 服务器允许驱逐,Pod 按照如下方式删除:

  1. API 服务器中的 Pod 资源会更新上删除时间戳,之后 API 服务器会认为此 Pod 资源将被终止。 此 Pod 资源还会标记上配置的宽限期。
  2. 本地运行状态的 Pod 所处的节点上的 kubelet 注意到 Pod 资源被标记为终止,并开始优雅停止本地 Pod。
  3. 当 kubelet 停止 Pod 时,控制面从 EndpointEndpointSlice 对象中移除该 Pod。因此,控制器不再将此 Pod 视为有用对象。
  4. Pod 的宽限期到期后,kubelet 强制终止本地 Pod。
  5. kubelet 告诉 API 服务器删除 Pod 资源。
  6. API 服务器删除 Pod 资源。

解决驱逐被卡住的问题

在某些情况下,你的应用可能进入中断状态, 在你干预之前,驱逐 API 总是返回 429500。 例如,如果 ReplicaSet 为你的应用程序创建了 Pod, 但新的 Pod 没有进入 Ready 状态,就会发生这种情况。 在最后一个被驱逐的 Pod 有很长的终止宽限期的情况下,你可能也会注意到这种行为。

如果你注意到驱逐被卡住,请尝试以下解决方案之一:

  • 终止或暂停导致问题的自动化操作,重新启动操作之前,请检查被卡住的应用程序。
  • 等待一段时间后,直接从集群控制平面删除 Pod,而不是使用 Eviction API。

接下来

3.11 - 集群管理

关于创建和管理 Kubernetes 集群的底层细节。

集群管理概述面向任何创建和管理 Kubernetes 集群的读者人群。 我们假设你大概了解一些核心的 Kubernetes 概念

规划集群

查阅安装中的指导,获取如何规划、建立以及配置 Kubernetes 集群的示例。本文所列的文章称为发行版

在选择一个指南前,有一些因素需要考虑:

  • 你是打算在你的计算机上尝试 Kubernetes,还是要构建一个高可用的多节点集群? 请选择最适合你需求的发行版。
  • 你正在使用类似 Google Kubernetes Engine 这样的被托管的 Kubernetes 集群, 还是管理你自己的集群
  • 你的集群是在本地还是云(IaaS) 上?Kubernetes 不能直接支持混合集群。 作为代替,你可以建立多个集群。
  • 如果你在本地配置 Kubernetes, 需要考虑哪种网络模型最适合。
  • 你的 Kubernetes 在裸机上还是虚拟机(VM) 上运行?
  • 你是想运行一个集群,还是打算参与开发 Kubernetes 项目代码? 如果是后者,请选择一个处于开发状态的发行版。 某些发行版只提供二进制发布版,但提供更多的选择。
  • 让你自己熟悉运行一个集群所需的组件

管理集群

保护集群

保护 kubelet

可选集群服务

3.11.1 - 证书

要了解如何为集群生成证书,参阅证书

3.11.2 - 管理资源

你已经部署了应用并通过服务暴露它。然后呢? Kubernetes 提供了一些工具来帮助管理你的应用部署,包括扩缩容和更新。

组织资源配置

许多应用需要创建多个资源,例如 Deployment 和 Service。 可以通过将多个资源组合在同一个文件中(在 YAML 中以 --- 分隔) 来简化对它们的管理。例如:

apiVersion: v1
kind: Service
metadata:
  name: my-nginx-svc
  labels:
    app: nginx
spec:
  type: LoadBalancer
  ports:
  - port: 80
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

可以用创建单个资源相同的方式来创建多个资源:

kubectl apply -f https://k8s.io/examples/application/nginx-app.yaml
service/my-nginx-svc created
deployment.apps/my-nginx created

资源将按照它们在文件中的顺序创建。 因此,最好先指定服务,这样在控制器(例如 Deployment)创建 Pod 时能够 确保调度器可以将与服务关联的多个 Pod 分散到不同节点。

kubectl apply 也接受多个 -f 参数:

kubectl apply -f https://k8s.io/examples/application/nginx/nginx-svc.yaml \
  -f https://k8s.io/examples/application/nginx/nginx-deployment.yaml

建议的做法是,将同一个微服务或同一应用层相关的资源放到同一个文件中, 将同一个应用相关的所有文件按组存放到同一个目录中。 如果应用的各层使用 DNS 相互绑定,你可以将堆栈的所有组件一起部署。

还可以使用 URL 作为配置源,便于直接使用已经提交到 GitHub 上的配置文件进行部署:

kubectl apply -f https://k8s.io/examples/application/nginx/nginx-deployment.yaml
deployment.apps/my-nginx created

kubectl 中的批量操作

资源创建并不是 kubectl 可以批量执行的唯一操作。 kubectl 还可以从配置文件中提取资源名,以便执行其他操作, 特别是删除你之前创建的资源:

kubectl delete -f https://k8s.io/examples/application/nginx-app.yaml
deployment.apps "my-nginx" deleted
service "my-nginx-svc" deleted

在仅有两种资源的情况下,你可以使用"资源类型/资源名"的语法在命令行中 同时指定这两个资源:

kubectl delete deployments/my-nginx services/my-nginx-svc

对于资源数目较大的情况,你会发现使用 -l--selector 指定筛选器(标签查询)能很容易根据标签筛选资源:

kubectl delete deployment,services -l app=nginx
deployment.apps "my-nginx" deleted
service "my-nginx-svc" deleted

由于 kubectl 用来输出资源名称的语法与其所接受的资源名称语法相同, 你可以使用 $()xargs 进行链式操作:

kubectl get $(kubectl create -f docs/concepts/cluster-administration/nginx/ -o name | grep service)
kubectl create -f docs/concepts/cluster-administration/nginx/ -o name | grep service | xargs -i kubectl get {}
NAME           TYPE           CLUSTER-IP   EXTERNAL-IP   PORT(S)      AGE
my-nginx-svc   LoadBalancer   10.0.0.208   <pending>     80/TCP       0s

上面的命令中,我们首先使用 examples/application/nginx/ 下的配置文件创建资源, 并使用 -o name 的输出格式(以"资源/名称"的形式打印每个资源)打印所创建的资源。 然后,我们通过 grep 来过滤 "service",最后再打印 kubectl get 的内容。

如果你碰巧在某个路径下的多个子路径中组织资源,那么也可以递归地在所有子路径上 执行操作,方法是在 --filename,-f 后面指定 --recursive 或者 -R

例如,假设有一个目录路径为 project/k8s/development,它保存开发环境所需的 所有清单,并按资源类型组织:

project/k8s/development
├── configmap
│   └── my-configmap.yaml
├── deployment
│   └── my-deployment.yaml
└── pvc
    └── my-pvc.yaml

默认情况下,对 project/k8s/development 执行的批量操作将停止在目录的第一级, 而不是处理所有子目录。 如果我们试图使用以下命令在此目录中创建资源,则会遇到一个错误:

kubectl apply -f project/k8s/development
error: you must provide one or more resources by argument or filename (.json|.yaml|.yml|stdin)

正确的做法是,在 --filename,-f 后面标明 --recursive 或者 -R 之后:

kubectl apply -f project/k8s/development --recursive
configmap/my-config created
deployment.apps/my-deployment created
persistentvolumeclaim/my-pvc created

--recursive 可以用于接受 --filename,-f 参数的任何操作,例如: kubectl {create,get,delete,describe,rollout} 等。

有多个 -f 参数出现的时候,--recursive 参数也能正常工作:

kubectl apply -f project/k8s/namespaces -f project/k8s/development --recursive
namespace/development created
namespace/staging created
configmap/my-config created
deployment.apps/my-deployment created
persistentvolumeclaim/my-pvc created

如果你有兴趣进一步学习关于 kubectl 的内容,请阅读命令行工具(kubectl)

金丝雀部署(Canary Deployments)

另一个需要多标签的场景是用来区分同一组件的不同版本或者不同配置的多个部署。 常见的做法是部署一个使用金丝雀发布来部署新应用版本 (在 Pod 模板中通过镜像标签指定),保持新旧版本应用同时运行。 这样,新版本在完全发布之前也可以接收实时的生产流量。

例如,你可以使用 track 标签来区分不同的版本。

主要稳定的发行版将有一个 track 标签,其值为 stable

name: frontend
replicas: 3
...
labels:
   app: guestbook
   tier: frontend
   track: stable
...
image: gb-frontend:v3

然后,你可以创建 guestbook 前端的新版本,让这些版本的 track 标签带有不同的值 (即 canary),以便两组 Pod 不会重叠:

name: frontend-canary
replicas: 1
...
labels:
   app: guestbook
   tier: frontend
   track: canary
...
image: gb-frontend:v4

前端服务通过选择标签的公共子集(即忽略 track 标签)来覆盖两组副本, 以便流量可以转发到两个应用:

selector:
   app: guestbook
   tier: frontend

你可以调整 stablecanary 版本的副本数量,以确定每个版本将接收 实时生产流量的比例(在本例中为 3:1)。 一旦有信心,你就可以将新版本应用的 track 标签的值从 canary 替换为 stable,并且将老版本应用删除。

想要了解更具体的示例,请查看 Ghost 部署教程

更新注解

有时,你可能希望将注解附加到资源中。注解是 API 客户端(如工具、库等) 用于检索的任意非标识元数据。这可以通过 kubectl annotate 来完成。例如:

kubectl annotate pods my-nginx-v4-9gw19 description='my frontend running nginx'
kubectl get pods my-nginx-v4-9gw19 -o yaml
apiVersion: v1
kind: pod
metadata:
  annotations:
    description: my frontend running nginx
...

想要了解更多信息,请参考注解kubectl annotate 命令文档。

扩缩你的应用

当应用上的负载增长或收缩时,使用 kubectl 能够实现应用规模的扩缩。 例如,要将 nginx 副本的数量从 3 减少到 1,请执行以下操作:

kubectl scale deployment/my-nginx --replicas=1
deployment.apps/my-nginx scaled

现在,你的 Deployment 管理的 Pod 只有一个了。

kubectl get pods -l app=nginx
NAME                        READY     STATUS    RESTARTS   AGE
my-nginx-2035384211-j5fhi   1/1       Running   0          30m

想要让系统自动选择需要 nginx 副本的数量,范围从 1 到 3,请执行以下操作:

kubectl autoscale deployment/my-nginx --min=1 --max=3
horizontalpodautoscaler.autoscaling/my-nginx autoscaled

现在,你的 nginx 副本将根据需要自动地增加或者减少。

想要了解更多信息,请参考 kubectl scale命令文档、 kubectl autoscale 命令文档和水平 Pod 自动伸缩文档。

就地更新资源

有时,有必要对你所创建的资源进行小范围、无干扰地更新。

kubectl apply

建议在源代码管理中维护一组配置文件 (参见配置即代码), 这样,它们就可以和应用代码一样进行维护和版本管理。 然后,你可以用 kubectl apply 将配置变更应用到集群中。

这个命令将会把推送的版本与以前的版本进行比较,并应用你所做的更改, 但是不会自动覆盖任何你没有指定更改的属性。

kubectl apply -f https://k8s.io/examples/application/nginx/nginx-deployment.yaml
deployment.apps/my-nginx configured

注意,kubectl apply 将为资源增加一个额外的注解,以确定自上次调用以来对配置的更改。 执行时,kubectl apply 会在以前的配置、提供的输入和资源的当前配置之间 找出三方差异,以确定如何修改资源。

目前,新创建的资源是没有这个注解的,所以,第一次调用 kubectl apply 时 将使用提供的输入和资源的当前配置双方之间差异进行比较。 在第一次调用期间,它无法检测资源创建时属性集的删除情况。 因此,kubectl 不会删除它们。

所有后续的 kubectl apply 操作以及其他修改配置的命令,如 kubectl replacekubectl edit,都将更新注解,并允许随后调用的 kubectl apply 使用三方差异进行检查和执行删除。

kubectl edit

或者,你也可以使用 kubectl edit 更新资源:

kubectl edit deployment/my-nginx

这相当于首先 get 资源,在文本编辑器中编辑它,然后用更新的版本 apply 资源:

kubectl get deployment my-nginx -o yaml > /tmp/nginx.yaml
vi /tmp/nginx.yaml
# 做一些编辑,然后保存文件

kubectl apply -f /tmp/nginx.yaml
deployment.apps/my-nginx configured

rm /tmp/nginx.yaml

这使你可以更加容易地进行更重大的更改。 请注意,可以使用 EDITORKUBE_EDITOR 环境变量来指定编辑器。

想要了解更多信息,请参考 kubectl edit 文档。

kubectl patch

你可以使用 kubectl patch 来更新 API 对象。此命令支持 JSON patch、 JSON merge patch、以及 strategic merge patch。 请参考使用 kubectl patch 更新 API 对象kubectl patch

破坏性的更新

在某些情况下,你可能需要更新某些初始化后无法更新的资源字段,或者你可能只想立即进行递归更改, 例如修复 Deployment 创建的不正常的 Pod。若要更改这些字段,请使用 replace --force, 它将删除并重新创建资源。在这种情况下,你可以修改原始配置文件:

kubectl replace -f https://k8s.io/examples/application/nginx/nginx-deployment.yaml --force
deployment.apps/my-nginx deleted
deployment.apps/my-nginx replaced

在不中断服务的情况下更新应用

在某些时候,你最终需要更新已部署的应用,通常都是通过指定新的镜像或镜像标签, 如上面的金丝雀发布的场景中所示。kubectl 支持几种更新操作, 每种更新操作都适用于不同的场景。

我们将指导你通过 Deployment 如何创建和更新应用。

假设你正运行的是 1.14.2 版本的 nginx:

kubectl create deployment my-nginx --image=nginx:1.14.2
deployment.apps/my-nginx created

运行 3 个副本(这样新旧版本可以同时存在)

kubectl scale deployment my-nginx --current-replicas=1 --replicas=3
deployment.apps/my-nginx scaled

要更新到 1.16.1 版本,只需使用我们前面学到的 kubectl 命令将 .spec.template.spec.containers[0].imagenginx:1.14.2 修改为 nginx:1.16.1

kubectl edit deployment/my-nginx

没错,就是这样!Deployment 将在后台逐步更新已经部署的 nginx 应用。 它确保在更新过程中,只有一定数量的旧副本被开闭,并且只有一定基于所需 Pod 数量的新副本被创建。 想要了解更多细节,请参考 Deployment

接下来

3.11.3 - 集群网络系统

集群网络系统是 Kubernetes 的核心部分,但是想要准确了解它的工作原理可是个不小的挑战。 下面列出的是网络系统的的四个主要问题:

  1. 高度耦合的容器间通信:这个已经被 Podlocalhost 通信解决了。
  2. Pod 间通信:这是本文档讲述的重点。
  3. Pod 与 Service 间通信:涵盖在 Service 中。
  4. 外部与 Service 间通信:也涵盖在 Service 中。

Kubernetes 的宗旨就是在应用之间共享机器。 通常来说,共享机器需要两个应用之间不能使用相同的端口,但是在多个应用开发者之间 去大规模地协调端口是件很困难的事情,尤其是还要让用户暴露在他们控制范围之外的集群级别的问题上。

动态分配端口也会给系统带来很多复杂度 - 每个应用都需要设置一个端口的参数, 而 API 服务器还需要知道如何将动态端口数值插入到配置模块中,服务也需要知道如何找到对方等等。 与其去解决这些问题,Kubernetes 选择了其他不同的方法。

要了解 Kubernetes 网络模型,请参阅此处

Kubernetes IP 地址范围

Kubernetes 集群需要从以下组件中配置的可用地址范围中为 Pod、Service 和 Node 分配不重叠的 IP 地址:

  • 网络插件配置为向 Pod 分配 IP 地址。
  • kube-apiserver 配置为向 Service 分配 IP 地址。
  • kubelet 或 cloud-controller-manager 配置为向 Node 分配 IP 地址。
此图展示了 Kubernetes 集群中不同的网络范围

集群网络类型

根据配置的 IP 协议族,Kubernetes 集群可以分为以下几类:

  • 仅 IPv4:网络插件、kube-apiserver 和 kubelet/cloud-controller-manager 配置为仅分配 IPv4 地址。
  • 仅 IPv6:网络插件、kube-apiserver 和 kubelet/cloud-controller-manager 配置为仅分配 IPv6 地址。
  • IPv4/IPv6 或 IPv6/IPv4 双协议栈
    • 网络插件配置为分配 IPv4 和 IPv6 地址。
    • kube-apiserver 配置为分配 IPv4 和 IPv6 地址。
    • kubelet 或 cloud-controller-manager 配置为分配 IPv4 和 IPv6 地址。
    • 所有组件必须就配置的主要 IP 协议族达成一致。

Kubernetes 集群只考虑 Pod、Service 和 Node 对象中存在的 IP 协议族,而不考虑所表示对象的现有 IP。 例如,服务器或 Pod 的接口上可以有多个 IP 地址,但只有 node.status.addressespod.status.ips 中的 IP 地址被认为是实现 Kubernetes 网络模型和定义集群类型的。

如何实现 Kubernetes 的网络模型

网络模型由各节点上的容器运行时来实现。最常见的容器运行时使用 Container Network Interface (CNI) 插件来管理其网络和安全能力。 来自不同供应商 CNI 插件有很多。其中一些仅提供添加和删除网络接口的基本功能, 而另一些则提供更复杂的解决方案,例如与其他容器编排系统集成、运行多个 CNI 插件、高级 IPAM 功能等。

请参阅此页面了解 Kubernetes 支持的网络插件的非详尽列表。

接下来

网络模型的早期设计、运行原理都在联网设计文档里有详细描述。 关于未来的计划,以及旨在改进 Kubernetes 联网能力的一些正在进行的工作,可以参考 SIG Network 的 KEPs

3.11.4 - 日志架构

应用日志可以让你了解应用内部的运行状况。日志对调试问题和监控集群活动非常有用。 大部分现代化应用都有某种日志记录机制。同样地,容器引擎也被设计成支持日志记录。 针对容器化应用,最简单且最广泛采用的日志记录方式就是写入标准输出和标准错误流。

但是,由容器引擎或运行时提供的原生功能通常不足以构成完整的日志记录方案。

例如,如果发生容器崩溃、Pod 被逐出或节点宕机等情况,你可能想访问应用日志。

在集群中,日志应该具有独立的存储,并且其生命周期与节点、Pod 或容器的生命周期相独立。 这个概念叫集群级的日志

集群级日志架构需要一个独立的后端用来存储、分析和查询日志。 Kubernetes 并不为日志数据提供原生的存储解决方案。 相反,有很多现成的日志方案可以集成到 Kubernetes 中。 下面各节描述如何在节点上处理和存储日志。

Pod 和容器日志

Kubernetes 从正在运行的 Pod 中捕捉每个容器的日志。

此示例使用带有一个容器的 Pod 的清单,该容器每秒将文本写入标准输出一次。

apiVersion: v1
kind: Pod
metadata:
  name: counter
spec:
  containers:
  - name: count
    image: busybox:1.28
    args: [/bin/sh, -c,
            'i=0; while true; do echo "$i: $(date)"; i=$((i+1)); sleep 1; done']

要运行此 Pod,请执行以下命令:

kubectl apply -f https://k8s.io/examples/debug/counter-pod.yaml

输出为:

pod/counter created

要获取这些日志,请执行以下 kubectl logs 命令:

kubectl logs counter

输出类似于:

0: Fri Apr  1 11:42:23 UTC 2022
1: Fri Apr  1 11:42:24 UTC 2022
2: Fri Apr  1 11:42:25 UTC 2022

你可以使用 kubectl logs --previous 从容器的先前实例中检索日志。 如果你的 Pod 有多个容器,请如下通过将容器名称追加到该命令并使用 -c 标志来指定要访问哪个容器的日志:

kubectl logs counter -c count

详见 kubectl logs 文档

节点的容器日志处理方式

节点级别的日志记录

容器运行时对写入到容器化应用程序的 stdoutstderr 流的所有输出进行处理和转发。 不同的容器运行时以不同的方式实现这一点;不过它们与 kubelet 的集成都被标准化为 CRI 日志格式

默认情况下,如果容器重新启动,kubelet 会保留一个终止的容器及其日志。 如果一个 Pod 被逐出节点,所对应的所有容器及其日志也会被逐出。

kubelet 通过 Kubernetes API 的特殊功能将日志提供给客户端访问。 访问这个日志的常用方法是运行 kubectl logs

日志轮转

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

kubelet 负责轮换容器日志并管理日志目录结构。 kubelet(使用 CRI)将此信息发送到容器运行时,而运行时则将容器日志写到给定位置。

你可以使用 kubelet 配置文件配置两个 kubelet 配置选项containerLogMaxSize (默认 10Mi)和 containerLogMaxFiles(默认 5)。 这些设置分别允许你分别配置每个日志文件大小的最大值和每个容器允许的最大文件数。

为了在工作负载生成的日志量较大的集群中执行高效的日志轮换,kubelet 还提供了一种机制,基于可以执行多少并发日志轮换以及监控和轮换日志所需要的间隔来调整日志的轮换方式。 你可以使用 kubelet 配置文件 配置两个 kubelet 配置选项containerLogMaxWorkerscontainerLogMonitorInterval

当类似于基本日志示例一样运行 kubectl logs 时, 节点上的 kubelet 会处理请求并直接从日志文件读取。kubelet 将返回该日志文件的内容。

系统组件日志

系统组件有两种类型:通常在容器中运行的组件和直接参与容器运行的组件。例如:

  • kubelet 和容器运行时不在容器中运行。kubelet 运行你的容器 (一起按 Pod 分组)
  • Kubernetes 调度器、控制器管理器和 API 服务器在 Pod 中运行 (通常是静态 Pod。 etcd 组件在控制平面中运行,最常见的也是作为静态 Pod。 如果你的集群使用 kube-proxy,则通常将其作为 DaemonSet 运行。

日志位置

kubelet 和容器运行时写入日志的方式取决于节点使用的操作系统:

在使用 systemd 的 Linux 节点上,kubelet 和容器运行时默认写入 journald。 你要使用 journalctl 来阅读 systemd 日志;例如:journalctl -u kubelet

如果 systemd 不存在,kubelet 和容器运行时将写入到 /var/log 目录中的 .log 文件。 如果你想将日志写入其他地方,你可以通过辅助工具 kube-log-runner 间接运行 kubelet, 并使用该工具将 kubelet 日志重定向到你所选择的目录。

默认情况下,kubelet 指示你的容器运行时将日志写入 /var/log/pods 中的目录。

有关 kube-log-runner 的更多信息,请阅读系统日志

默认情况下,kubelet 将日志写入目录 C:\var\logs 中的文件(注意这不是 C:\var\log)。

尽管 C:\var\log 是这些日志的 Kubernetes 默认位置, 但一些集群部署工具会将 Windows 节点设置为将日志放到 C:\var\log\kubelet

如果你想将日志写入其他地方,你可以通过辅助工具 kube-log-runner 间接运行 kubelet, 并使用该工具将 kubelet 日志重定向到你所选择的目录。

但是,kubelet 默认指示你的容器运行时在目录 C:\var\log\pods 中写入日志。

有关 kube-log-runner 的更多信息,请阅读系统日志


对于在 Pod 中运行的 Kubernetes 集群组件,其日志会写入 /var/log 目录中的文件, 相当于绕过默认的日志机制(组件不会写入 systemd 日志)。 你可以使用 Kubernetes 的存储机制将持久存储映射到运行该组件的容器中。

kubelet 允许将 Pod 日志目录从默认的 /var/log/pods 更改为自定义路径。 可以通过在 kubelet 的配置文件中配置 podLogsDir 参数来进行此调整。

有关 etcd 及其日志的详细信息,请查阅 etcd 文档。 同样,你可以使用 Kubernetes 的存储机制将持久存储映射到运行该组件的容器中。

集群级日志架构

虽然 Kubernetes 没有为集群级日志记录提供原生的解决方案,但你可以考虑几种常见的方法。 以下是一些选项:

  • 使用在每个节点上运行的节点级日志记录代理。
  • 在应用程序的 Pod 中,包含专门记录日志的边车(Sidecar)容器。
  • 将日志直接从应用程序中推送到日志记录后端。

使用节点级日志代理

使用节点级日志代理

你可以通过在每个节点上使用节点级的日志记录代理来实现集群级日志记录。 日志记录代理是一种用于暴露日志或将日志推送到后端的专用工具。 通常,日志记录代理程序是一个容器,它可以访问包含该节点上所有应用程序容器的日志文件的目录。

由于日志记录代理必须在每个节点上运行,推荐以 DaemonSet 的形式运行该代理。

节点级日志在每个节点上仅创建一个代理,不需要对节点上的应用做修改。

容器向标准输出和标准错误输出写出数据,但在格式上并不统一。 节点级代理收集这些日志并将其进行转发以完成汇总。

使用边车容器运行日志代理

你可以通过以下方式之一使用边车(Sidecar)容器:

  • 边车容器将应用程序日志传送到自己的标准输出。
  • 边车容器运行一个日志代理,配置该日志代理以便从应用容器收集日志。

传输数据流的边车容器

带数据流容器的边车容器

利用边车容器,写入到自己的 stdoutstderr 传输流, 你就可以利用每个节点上的 kubelet 和日志代理来处理日志。 边车容器从文件、套接字或 journald 读取日志。 每个边车容器向自己的 stdoutstderr 流中输出日志。

这种方法允许你将日志流从应用程序的不同部分分离开,其中一些可能缺乏对写入 stdoutstderr 的支持。重定向日志背后的逻辑是最小的,因此它的开销不大。 另外,因为 stdoutstderr 由 kubelet 处理,所以你可以使用内置的工具 kubectl logs

例如,某 Pod 中运行一个容器,且该容器使用两个不同的格式写入到两个不同的日志文件。 下面是这个 Pod 的清单:

apiVersion: v1
kind: Pod
metadata:
  name: counter
spec:
  containers:
  - name: count
    image: busybox:1.28
    args:
    - /bin/sh
    - -c
    - >
      i=0;
      while true;
      do
        echo "$i: $(date)" >> /var/log/1.log;
        echo "$(date) INFO $i" >> /var/log/2.log;
        i=$((i+1));
        sleep 1;
      done      
    volumeMounts:
    - name: varlog
      mountPath: /var/log
  volumes:
  - name: varlog
    emptyDir: {}

不建议在同一个日志流中写入不同格式的日志条目,即使你成功地将其重定向到容器的 stdout 流。 相反,你可以创建两个边车容器。每个边车容器可以从共享卷跟踪特定的日志文件, 并将文件内容重定向到各自的 stdout 流。

下面是运行两个边车容器的 Pod 的清单:

apiVersion: v1
kind: Pod
metadata:
  name: counter
spec:
  containers:
  - name: count
    image: busybox:1.28
    args:
    - /bin/sh
    - -c
    - >
      i=0;
      while true;
      do
        echo "$i: $(date)" >> /var/log/1.log;
        echo "$(date) INFO $i" >> /var/log/2.log;
        i=$((i+1));
        sleep 1;
      done      
    volumeMounts:
    - name: varlog
      mountPath: /var/log
  - name: count-log-1
    image: busybox:1.28
    args: [/bin/sh, -c, 'tail -n+1 -F /var/log/1.log']
    volumeMounts:
    - name: varlog
      mountPath: /var/log
  - name: count-log-2
    image: busybox:1.28
    args: [/bin/sh, -c, 'tail -n+1 -F /var/log/2.log']
    volumeMounts:
    - name: varlog
      mountPath: /var/log
  volumes:
  - name: varlog
    emptyDir: {}

现在当你运行这个 Pod 时,你可以运行如下命令分别访问每个日志流:

kubectl logs counter count-log-1

输出类似于:

0: Fri Apr  1 11:42:26 UTC 2022
1: Fri Apr  1 11:42:27 UTC 2022
2: Fri Apr  1 11:42:28 UTC 2022
...
kubectl logs counter count-log-2

输出类似于:

Fri Apr  1 11:42:29 UTC 2022 INFO 0
Fri Apr  1 11:42:30 UTC 2022 INFO 0
Fri Apr  1 11:42:31 UTC 2022 INFO 0
...

如果你在集群中安装了节点级代理,由代理自动获取上述日志流,而无需任何进一步的配置。 如果你愿意,你可以将代理配置为根据源容器解析日志行。

即使对于 CPU 和内存使用率较低的 Pod(CPU 为几毫核,内存为几兆字节),将日志写入一个文件, 将这些日志流写到 stdout 也有可能使节点所需的存储量翻倍。 如果你有一个写入特定文件的应用程序,则建议将 /dev/stdout 设置为目标文件,而不是采用流式边车容器方法。

边车容器还可用于轮转应用程序本身无法轮转的日志文件。 这种方法的一个例子是定期运行 logrotate 的小容器。 但是,直接使用 stdoutstderr 更直接,而将轮转和保留策略留给 kubelet。

集群中安装的节点级代理会自动获取这些日志流,而无需进一步配置。 如果你愿意,你也可以配置代理程序来解析源容器的日志行。

注意,尽管 CPU 和内存使用率都很低(以多个 CPU 毫核指标排序或者按内存的兆字节排序), 向文件写日志然后输出到 stdout 流仍然会成倍地增加磁盘使用率。 如果你的应用向单一文件写日志,通常最好设置 /dev/stdout 作为目标路径, 而不是使用流式的边车容器方式。

如果应用程序本身不能轮转日志文件,则可以通过边车容器实现。 这种方式的一个例子是运行一个小的、定期轮转日志的容器。 然而,还是推荐直接使用 stdoutstderr,将日志的轮转和保留策略交给 kubelet。

具有日志代理功能的边车容器

含日志代理的边车容器

如果节点级日志记录代理程序对于你的场景来说不够灵活, 你可以创建一个带有单独日志记录代理的边车容器,将代理程序专门配置为与你的应用程序一起运行。

下面是两个配置文件,可以用来实现一个带日志代理的边车容器。 第一个文件包含用来配置 fluentd 的 ConfigMap

apiVersion: v1
kind: ConfigMap
metadata:
  name: fluentd-config
data:
  fluentd.conf: |
    <source>
      type tail
      format none
      path /var/log/1.log
      pos_file /var/log/1.log.pos
      tag count.format1
    </source>

    <source>
      type tail
      format none
      path /var/log/2.log
      pos_file /var/log/2.log.pos
      tag count.format2
    </source>

    <match **>
      type google_cloud
    </match>    

第二个清单描述了一个运行 fluentd 边车容器的 Pod。 该 Pod 挂载一个卷,flutend 可以从这个卷上拣选其配置数据。

apiVersion: v1
kind: Pod
metadata:
  name: counter
spec:
  containers:
  - name: count
    image: busybox:1.28
    args:
    - /bin/sh
    - -c
    - >
      i=0;
      while true;
      do
        echo "$i: $(date)" >> /var/log/1.log;
        echo "$(date) INFO $i" >> /var/log/2.log;
        i=$((i+1));
        sleep 1;
      done      
    volumeMounts:
    - name: varlog
      mountPath: /var/log
  - name: count-agent
    image: registry.k8s.io/fluentd-gcp:1.30
    env:
    - name: FLUENTD_ARGS
      value: -c /etc/fluentd-config/fluentd.conf
    volumeMounts:
    - name: varlog
      mountPath: /var/log
    - name: config-volume
      mountPath: /etc/fluentd-config
  volumes:
  - name: varlog
    emptyDir: {}
  - name: config-volume
    configMap:
      name: fluentd-config

从应用中直接暴露日志目录

直接从应用程序暴露日志

从各个应用中直接暴露和推送日志数据的集群日志机制已超出 Kubernetes 的范围。

接下来

3.11.5 - Kubernetes 系统组件指标

通过系统组件指标可以更好地了解系统组个内部发生的情况。系统组件指标对于构建仪表板和告警特别有用。

Kubernetes 组件以 Prometheus 格式生成度量值。 这种格式是结构化的纯文本,旨在使人和机器都可以阅读。

Kubernetes 中组件的指标

在大多数情况下,可以通过 HTTP 访问组件的 /metrics 端点来获取组件的度量值。 对于那些默认情况下不暴露端点的组件,可以使用 --bind-address 标志启用。

这些组件的示例:

在生产环境中,你可能需要配置 Prometheus 服务器或 某些其他指标搜集器以定期收集这些指标,并使它们在某种时间序列数据库中可用。

请注意,kubelet 还会在 /metrics/cadvisor/metrics/resource/metrics/probes 端点中公开度量值。这些度量值的生命周期各不相同。

如果你的集群使用了 RBAC, 则读取指标需要通过基于用户、组或 ServiceAccount 的鉴权,要求具有允许访问 /metrics 的 ClusterRole。 例如:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: prometheus
rules:
  - nonResourceURLs:
      - "/metrics"
    verbs:
      - get

指标生命周期

Alpha 指标 → 稳定的指标 → 弃用的指标 → 隐藏的指标 → 删除的指标

Alpha 指标没有稳定性保证。这些指标可以随时被修改或者删除。

稳定的指标可以保证不会改变。这意味着:

  • 稳定的、不包含已弃用(deprecated)签名的指标不会被删除(或重命名)
  • 稳定的指标的类型不会被更改

已弃用的指标最终将被删除,不过仍然可用。 这类指标包含注解,标明其被废弃的版本。

例如:

  • 被弃用之前:

    # HELP some_counter this counts things
    # TYPE some_counter counter
    some_counter 0
    
  • 被弃用之后:

    # HELP some_counter (Deprecated since 1.15.0) this counts things
    # TYPE some_counter counter
    some_counter 0
    

隐藏的指标不会再被发布以供抓取,但仍然可用。 要使用隐藏指标,请参阅显式隐藏指标节。

删除的指标不再被发布,亦无法使用。

显示隐藏指标

如上所述,管理员可以通过设置可执行文件的命令行参数来启用隐藏指标, 如果管理员错过了上一版本中已经弃用的指标的迁移,则可以把这个用作管理员的逃生门。

show-hidden-metrics-for-version 标志接受版本号作为取值,版本号给出 你希望显示该发行版本中已弃用的指标。 版本表示为 x.y,其中 x 是主要版本,y 是次要版本。补丁程序版本不是必须的, 即使指标可能会在补丁程序发行版中弃用,原因是指标弃用策略规定仅针对次要版本。

该参数只能使用前一个次要版本。如果管理员将先前版本设置为 show-hidden-metrics-for-version, 则先前版本中隐藏的度量值会再度生成。不允许使用过旧的版本,因为那样会违反指标弃用策略。

以指标 A 为例,此处假设 A 在 1.n 中已弃用。根据指标弃用策略,我们可以得出以下结论:

  • 在版本 1.n 中,这个指标已经弃用,且默认情况下可以生成。
  • 在版本 1.n+1 中,这个指标默认隐藏,可以通过命令行参数 show-hidden-metrics-for-version=1.n 来再度生成。
  • 在版本 1.n+2 中,这个指标就将被从代码中移除,不会再有任何逃生窗口。

如果你要从版本 1.12 升级到 1.13,但仍依赖于 1.12 中弃用的指标 A,则应通过命令行设置隐藏指标: --show-hidden-metrics=1.12,并记住在升级到 1.14 版本之前删除此指标依赖项。

组件指标

kube-controller-manager 指标

控制器管理器指标可提供有关控制器管理器性能和运行状况的重要洞察。 这些指标包括通用的 Go 语言运行时指标(例如 go_routine 数量)和控制器特定的度量指标, 例如可用于评估集群运行状况的 etcd 请求延迟或云提供商(AWS、GCE、OpenStack)的 API 延迟等。

从 Kubernetes 1.7 版本开始,详细的云提供商指标可用于 GCE、AWS、Vsphere 和 OpenStack 的存储操作。 这些指标可用于监控持久卷操作的运行状况。

比如,对于 GCE,这些指标称为:

cloudprovider_gce_api_request_duration_seconds { request = "instance_list"}
cloudprovider_gce_api_request_duration_seconds { request = "disk_insert"}
cloudprovider_gce_api_request_duration_seconds { request = "disk_delete"}
cloudprovider_gce_api_request_duration_seconds { request = "attach_disk"}
cloudprovider_gce_api_request_duration_seconds { request = "detach_disk"}
cloudprovider_gce_api_request_duration_seconds { request = "list_disk"}

kube-scheduler 指标

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

调度器会暴露一些可选的指标,报告所有运行中 Pod 所请求的资源和期望的约束值。 这些指标可用来构造容量规划监控面板、访问调度约束的当前或历史数据、 快速发现因为缺少资源而无法被调度的负载,或者将 Pod 的实际资源用量与其请求值进行比较。

kube-scheduler 组件能够辩识各个 Pod 所配置的资源 请求和约束。 在 Pod 的资源请求值或者约束值非零时,kube-scheduler 会以度量值时间序列的形式 生成报告。该时间序列值包含以下标签:

  • 名字空间
  • Pod 名称
  • Pod 调度所处节点,或者当 Pod 未被调度时用空字符串表示
  • 优先级
  • 为 Pod 所指派的调度器
  • 资源的名称(例如,cpu
  • 资源的单位,如果知道的话(例如,cores

一旦 Pod 进入完成状态(其 restartPolicyNeverOnFailure,且 其处于 SucceededFailed Pod 阶段,或者已经被删除且所有容器都具有 终止状态),该时间序列停止报告,因为调度器现在可以调度其它 Pod 来执行。 这两个指标称作 kube_pod_resource_requestkube_pod_resource_limit

指标暴露在 HTTP 端点 /metrics/resources,与调度器上的 /metrics 端点 一样要求相同的访问授权。你必须使用 --show-hidden-metrics-for-version=1.20 标志才能暴露那些稳定性为 Alpha 的指标。

禁用指标

你可以通过命令行标志 --disabled-metrics 来关闭某指标。 在例如某指标会带来性能问题的情况下,这一操作可能是有用的。 标志的参数值是一组被禁止的指标(例如:--disabled-metrics=metric1,metric2)。

指标顺序性保证

在 Alpha 阶段,标志只能接受一组映射值作为可以使用的指标标签。 每个映射值的格式为<指标名称>,<标签名称>=<可用标签列表>,其中 <可用标签列表> 是一个用逗号分隔的、可接受的标签名的列表。

最终的格式看起来会是这样:

--allow-label-value <指标名称>,<标签名称>='<可用值1>,<可用值2>...', <指标名称2>,<标签名称>='<可用值1>, <可用值2>...', ...

下面是一个例子:

--allow-label-value number_count_metric,odd_number='1,3,5', number_count_metric,even_number='2,4,6', date_gauge_metric,weekend='Saturday,Sunday'

除了从 CLI 中指定之外,还可以在配置文件中完成此操作。 你可以使用组件的 --allow-metric-labels-manifest 命令行参数指定该配置文件的路径。 以下是该配置文件的内容示例:

allow-list:
- "metric1,label2": "v1,v2,v3"
- "metric2,label1": "v1,v2,v3"

此外,cardinality_enforcement_unexpected_categorizations_total 元指标记录基数执行期间意外分类的计数,即每当遇到允许列表约束不允许的标签值时。

接下来

3.11.6 - Kubernetes 对象状态的指标

kube-state-metrics,一个生成和公开集群层面指标的插件代理。

Kubernetes API 中 Kubernetes 对象的状态可以被公开为指标。 一个名为 kube-state-metrics 的插件代理可以连接到 Kubernetes API 服务器并公开一个 HTTP 端点,提供集群中各个对象的状态所生成的指标。 此代理公开了关于对象状态的各种信息,如标签和注解、启动和终止时间、对象当前所处的状态或阶段。 例如,针对运行在 Pod 中的容器会创建一个 kube_pod_container_info 指标。 其中包括容器的名称、所属的 Pod 的名称、Pod 所在的命名空间、 容器镜像的名称、镜像的 ID、容器规约中的镜像名称、运行中容器的 ID 和用作标签的 Pod ID。

有能力(例如通过 Prometheus)抓取 kube-state-metrics 端点的外部组件现在可用于实现以下使用场景。

示例:使用来自 kube-state-metrics 的指标查询集群状态

通过 kube-state-metrics 生成的系列指标有助于进一步洞察集群,因为这些指标可用于查询。

如果你使用 Prometheus 或其他采用相同查询语言的工具,则以下 PromQL 查询将返回未就绪的 Pod 数:

count(kube_pod_status_ready{condition="false"}) by (namespace, pod)

示例:基于 kube-state-metrics 发出警报

kube-state-metrics 生成的指标还允许在集群中出现问题时发出警报。

如果你使用 Prometheus 或类似采用相同警报规则语言的工具,若有某些 Pod 处于 Terminating 状态超过 5 分钟,将触发以下警报:

groups:
- name: Pod state
  rules:
  - alert: PodsBlockedInTerminatingState
    expr: count(kube_pod_deletion_timestamp) by (namespace, pod) * count(kube_pod_status_reason{reason="NodeLost"} == 0) by (namespace, pod) > 0
    for: 5m
    labels:
      severity: page
    annotations:
      summary: Pod {{$labels.namespace}}/{{$labels.pod}} blocked in Terminating state.

3.11.7 - 系统日志

系统组件的日志记录集群中发生的事件,这对于调试非常有用。 你可以配置日志的精细度,以展示更多或更少的细节。 日志可以是粗粒度的,如只显示组件内的错误, 也可以是细粒度的,如显示事件的每一个跟踪步骤(比如 HTTP 访问日志、pod 状态更新、控制器动作或调度器决策)。

Klog

klog 是 Kubernetes 的日志库。 klog 为 Kubernetes 系统组件生成日志消息。

Kubernetes 正在进行简化其组件日志的努力。下面的 klog 命令行参数从 Kubernetes v1.23 开始已被废弃, 在 Kubernetes v1.26 中被移除:

  • --add-dir-header
  • --alsologtostderr
  • --log-backtrace-at
  • --log-dir
  • --log-file
  • --log-file-max-size
  • --logtostderr
  • --one-output
  • --skip-headers
  • --skip-log-headers
  • --stderrthreshold

输出总会被写到标准错误输出(stderr)之上,无论输出格式如何。 对输出的重定向将由调用 Kubernetes 组件的软件来处理。 这一软件可以是 POSIX Shell 或者类似 systemd 这样的工具。

在某些场合下,例如对于无发行主体的(distroless)容器或者 Windows 系统服务, 这些替代方案都是不存在的。那么你可以使用 kube-log-runner 可执行文件来作为 Kubernetes 的封装层,完成对输出的重定向。 在很多 Kubernetes 基础镜像中,都包含一个预先构建的可执行程序。 这个程序原来称作 /go-runner,而在服务器和节点的发行版本库中,称作 kube-log-runner

下表展示的是 kube-log-runner 调用与 Shell 重定向之间的对应关系:

用法POSIX Shell(例如 Bash)kube-log-runner <options> <cmd>
合并 stderr 与 stdout,写出到 stdout2>&1kube-log-runner(默认行为 )
将 stderr 与 stdout 重定向到日志文件1>>/tmp/log 2>&1kube-log-runner -log-file=/tmp/log
输出到 stdout 并复制到日志文件中2>&1 | tee -a /tmp/logkube-log-runner -log-file=/tmp/log -also-stdout
仅将 stdout 重定向到日志>/tmp/logkube-log-runner -log-file=/tmp/log -redirect-stderr=false

klog 输出

传统的 klog 原生格式示例:

I1025 00:15:15.525108       1 httplog.go:79] GET /api/v1/namespaces/kube-system/pods/metrics-server-v0.3.1-57c75779f-9p8wg: (1.512ms) 200 [pod_nanny/v0.0.0 (linux/amd64) kubernetes/$Format 10.56.1.19:51756]

消息字符串可能包含换行符:

I1025 00:15:15.525108       1 example.go:79] This is a message
which has a line break.

结构化日志

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

结构化日志记录旨在日志消息中引入统一结构,以便以编程方式提取信息。 你可以方便地用更小的开销来处理结构化日志。 生成日志消息的代码决定其使用传统的非结构化的 klog 还是结构化的日志。

默认的结构化日志消息是以文本形式呈现的,其格式与传统的 klog 保持向后兼容:

<klog header> "<message>" <key1>="<value1>" <key2>="<value2>" ...

示例:

I1025 00:15:15.525108       1 controller_utils.go:116] "Pod status updated" pod="kube-system/kubedns" status="ready"

字符串在输出时会被添加引号。其他数值类型都使用 %+v 来格式化,因此可能导致日志消息会延续到下一行, 具体取决于数据本身

I1025 00:15:15.525108       1 example.go:116] "Example" data="This is text with a line break\nand \"quotation marks\"." someInt=1 someFloat=0.1 someStruct={StringField: First line,
second line.}

上下文日志

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

上下文日志建立在结构化日志之上。 它主要是关于开发人员如何使用日志记录调用:基于该概念的代码将更加灵活, 并且支持在结构化日志 KEP 中描述的额外用例。

如果开发人员在他们的组件中使用额外的函数,比如 WithValuesWithName, 那么日志条目将会包含额外的信息,这些信息会被调用者传递给函数。

对于 Kubernetes 1.30,这一特性是由 StructuredLogging 特性门控所控制的,默认启用。 这个基础设施是在 1.24 中被添加的,并不需要修改组件。 该 component-base/logs/example 命令演示了如何使用新的日志记录调用以及组件如何支持上下文日志记录。

$ cd $GOPATH/src/k8s.io/kubernetes/staging/src/k8s.io/component-base/logs/example/cmd/
$ go run . --help
...
      --feature-gates mapStringBool  A set of key=value pairs that describe feature gates for alpha/experimental features. Options are:
                                     AllAlpha=true|false (ALPHA - default=false)
                                     AllBeta=true|false (BETA - default=false)
                                     ContextualLogging=true|false (BETA - default=true)
$ go run . --feature-gates ContextualLogging=true
...
I0222 15:13:31.645988  197901 example.go:54] "runtime" logger="example.myname" foo="bar" duration="1m0s"
I0222 15:13:31.646007  197901 example.go:55] "another runtime" logger="example" foo="bar" duration="1h0m0s" duration="1m0s"

logger 键和 foo="bar" 会被函数的调用者添加上, 不需修改该函数,它就会记录 runtime 消息和 duration="1m0s" 值。

禁用上下文日志后,WithValuesWithName 什么都不会做, 并且会通过调用全局的 klog 日志记录器记录日志。 因此,这些附加信息不再出现在日志输出中:

$ go run . --feature-gates ContextualLogging=false
...
I0222 15:14:40.497333  198174 example.go:54] "runtime" duration="1m0s"
I0222 15:14:40.497346  198174 example.go:55] "another runtime" duration="1h0m0s" duration="1m0s"

JSON 日志格式

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

--logging-format=json 参数将日志格式从 klog 原生格式改为 JSON 格式。 JSON 日志格式示例(美化输出):

{
   "ts": 1580306777.04728,
   "v": 4,
   "msg": "Pod status updated",
   "pod":{
      "name": "nginx-1",
      "namespace": "default"
   },
   "status": "ready"
}

具有特殊意义的 key:

  • ts - Unix 时间风格的时间戳(必选项,浮点值)
  • v - 精细度(仅用于 info 级别,不能用于错误信息,整数)
  • err - 错误字符串(可选项,字符串)
  • msg - 消息(必选项,字符串)

当前支持 JSON 格式的组件列表:

日志精细度级别

参数 -v 控制日志的精细度。增大该值会增大日志事件的数量。 减小该值可以减小日志事件的数量。增大精细度会记录更多的不太严重的事件。 精细度设置为 0 时只记录关键(critical)事件。

日志位置

有两种类型的系统组件:运行在容器中的组件和不运行在容器中的组件。例如:

  • Kubernetes 调度器和 kube-proxy 在容器中运行。
  • kubelet 和容器运行时不在容器中运行。

在使用 systemd 的系统中,kubelet 和容器运行时写入 journald。 在别的系统中,日志写入 /var/log 目录下的 .log 文件中。 容器中的系统组件总是绕过默认的日志记录机制,写入 /var/log 目录下的 .log 文件。 与容器日志类似,你应该轮转 /var/log 目录下系统组件日志。 在 kube-up.sh 脚本创建的 Kubernetes 集群中,日志轮转由 logrotate 工具配置。 logrotate 工具,每天或者当日志大于 100MB 时,轮转日志。

日志查询

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

为了帮助在节点上调试问题,Kubernetes v1.27 引入了一个特性来查看节点上当前运行服务的日志。 要使用此特性,请确保已为节点启用了 NodeLogQuery 特性门控, 且 kubelet 配置选项 enableSystemLogHandlerenableSystemLogQuery 均被设置为 true。 在 Linux 上,我们假设可以通过 journald 查看服务日志。 在 Windows 上,我们假设可以在应用日志提供程序中查看服务日志。 在两种操作系统上,都可以通过读取 /var/log/ 内的文件查看日志。

假如你被授权与节点对象交互,你可以在所有节点或只是某个子集上试用此特性。 这里有一个从节点中检索 kubelet 服务日志的例子:

# 从名为 node-1.example 的节点中获取 kubelet 日志
kubectl get --raw "/api/v1/nodes/node-1.example/proxy/logs/?query=kubelet"

你也可以获取文件,前提是日志文件位于 kubelet 允许进行日志获取的目录中。 例如你可以从 Linux 节点上的 /var/log 中获取日志。

kubectl get --raw "/api/v1/nodes/<insert-node-name-here>/proxy/logs/?query=/<insert-log-file-name-here>"

kubelet 使用启发方式来检索日志。 如果你还未意识到给定的系统服务正将日志写入到操作系统的原生日志记录程序(例如 journald) 或 /var/log/ 中的日志文件,这会很有帮助。这种启发方式先检查原生的日志记录程序, 如果不可用,则尝试从 /var/log/<servicename>/var/log/<servicename>.log/var/log/<servicename>/<servicename>.log 中检索第一批日志。

可用选项的完整列表如下:

选项描述
bootboot 显示来自特定系统引导的消息
patternpattern 通过提供的兼容 PERL 的正则表达式来过滤日志条目
queryquery 是必需的,指定返回日志的服务或文件
sinceTime显示日志的 RFC3339 起始时间戳(包含)
untilTime显示日志的 RFC3339 结束时间戳(包含)
tailLines指定要从日志的末尾检索的行数;默认为获取全部日志

更复杂的查询示例:

# 从名为 node-1.example 且带有单词 "error" 的节点中获取 kubelet 日志
kubectl get --raw "/api/v1/nodes/node-1.example/proxy/logs/?query=kubelet&pattern=error"

接下来

3.11.8 - 追踪 Kubernetes 系统组件

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

系统组件追踪功能记录各个集群操作的时延信息和这些操作之间的关系。

Kubernetes 组件基于 gRPC 导出器的 OpenTelemetry 协议 发送追踪信息,并用 OpenTelemetry Collector 收集追踪信息,再将其转交给追踪系统的后台。

追踪信息的收集

关于收集追踪信息、以及使用收集器的完整指南,可参见 Getting Started with the OpenTelemetry Collector。 不过,还有一些特定于 Kubernetes 组件的事项值得注意。

默认情况下,Kubernetes 组件使用 gRPC 的 OTLP 导出器来导出追踪信息,将信息写到 IANA OpenTelemetry 端口。 举例来说,如果收集器以 Kubernetes 组件的边车模式运行, 以下接收器配置会收集 span 信息,并将它们写入到标准输出。

receivers:
  otlp:
    protocols:
      grpc:
exporters:
  # 用适合你后端环境的导出器替换此处的导出器
  logging:
    logLevel: debug
service:
  pipelines:
    traces:
      receivers: [otlp]
      exporters: [logging]

组件追踪

kube-apiserver 追踪

kube-apiserver 为传入的 HTTP 请求、传出到 webhook 和 etcd 的请求以及重入的请求生成 span。 由于 kube-apiserver 通常是一个公开的端点,所以它通过出站的请求传播 W3C 追踪上下文, 但不使用入站请求的追踪上下文。

在 kube-apiserver 中启用追踪

要启用追踪特性,需要使用 --tracing-config-file=<<配置文件路径> 为 kube-apiserver 提供追踪配置文件。下面是一个示例配置,它为万分之一的请求记录 span,并使用了默认的 OpenTelemetry 端点。

apiVersion: apiserver.config.k8s.io/v1beta1
kind: TracingConfiguration
# 默认值
#endpoint: localhost:4317
samplingRatePerMillion: 100

有关 TracingConfiguration 结构体的更多信息,请参阅 API 服务器配置 API (v1beta1)

kubelet 追踪

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

kubelet CRI 接口和实施身份验证的 HTTP 服务器被插桩以生成追踪 span。 与 API 服务器一样,端点和采样率是可配置的。 追踪上下文传播也是可以配置的。始终优先采用父 span 的采样决策。 用户所提供的追踪配置采样率将被应用到不带父级的 span。 如果在没有配置端点的情况下启用,将使用默认的 OpenTelemetry Collector 接收器地址 “localhost:4317”。

在 kubelet 中启用追踪

要启用追踪,需应用追踪配置。 以下是 kubelet 配置的示例代码片段,每 10000 个请求中记录一个请求的 span,并使用默认的 OpenTelemetry 端点:

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
featureGates:
  KubeletTracing: true
tracing:
  # 默认值
  #endpoint: localhost:4317
  samplingRatePerMillion: 100

如果 samplingRatePerMillion 被设置为一百万 (1000000),则所有 span 都将被发送到导出器。

Kubernetes v1.30 中的 kubelet 收集与垃圾回收、Pod 同步例程以及每个 gRPC 方法相关的 Span。 kubelet 借助 gRPC 来传播跟踪上下文,以便 CRI-O 和 containerd 这类带有跟踪插桩的容器运行时可以在其导出的 Span 与 kubelet 所提供的跟踪上下文之间建立关联。所得到的跟踪数据会包含 kubelet 与容器运行时 Span 之间的父子链接关系,从而为调试节点问题提供有用的上下文信息。

请注意导出 span 始终会对网络和 CPU 产生少量性能开销,具体取决于系统的总体配置。 如果在启用追踪的集群中出现类似性能问题,可以通过降低 samplingRatePerMillion 或通过移除此配置来彻底禁用追踪来缓解问题。

稳定性

追踪工具仍在积极开发中,未来它会以多种方式发生变化。 这些变化包括:span 名称、附加属性、检测端点等等。 此类特性在达到稳定版本之前,不能保证追踪工具的向后兼容性。

接下来

3.11.9 - Kubernetes 中的代理

本文讲述了 Kubernetes 中所使用的代理。

代理

用户在使用 Kubernetes 的过程中可能遇到几种不同的代理(proxy):

  1. kubectl proxy

    • 运行在用户的桌面或 pod 中
    • 从本机地址到 Kubernetes apiserver 的代理
    • 客户端到代理使用 HTTP 协议
    • 代理到 apiserver 使用 HTTPS 协议
    • 指向 apiserver
    • 添加认证头信息
  1. apiserver proxy

    • 是一个建立在 apiserver 内部的“堡垒”
    • 将集群外部的用户与集群 IP 相连接,这些 IP 是无法通过其他方式访问的
    • 运行在 apiserver 进程内
    • 客户端到代理使用 HTTPS 协议 (如果配置 apiserver 使用 HTTP 协议,则使用 HTTP 协议)
    • 通过可用信息进行选择,代理到目的地可能使用 HTTP 或 HTTPS 协议
    • 可以用来访问 Node、 Pod 或 Service
    • 当用来访问 Service 时,会进行负载均衡
  1. kube proxy

    • 在每个节点上运行
    • 代理 UDP、TCP 和 SCTP
    • 不支持 HTTP
    • 提供负载均衡能力
    • 只用来访问 Service
  1. apiserver 之前的代理/负载均衡器:

    • 在不同集群中的存在形式和实现不同 (如 nginx)
    • 位于所有客户端和一个或多个 API 服务器之间
    • 存在多个 API 服务器时,扮演负载均衡器的角色
  1. 外部服务的云负载均衡器:

    • 由一些云供应商提供 (如 AWS ELB、Google Cloud Load Balancer)
    • Kubernetes 服务类型为 LoadBalancer 时自动创建
    • 通常仅支持 UDP/TCP 协议
    • SCTP 支持取决于云供应商的负载均衡器实现
    • 不同云供应商的云负载均衡器实现不同

Kubernetes 用户通常只需要关心前两种类型的代理,集群管理员通常需要确保后面几种类型的代理设置正确。

请求重定向

代理已经取代重定向功能,重定向功能已被弃用。

3.11.10 - API 优先级和公平性

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

对于集群管理员来说,控制 Kubernetes API 服务器在过载情况下的行为是一项关键任务。 kube-apiserver 有一些控件(例如:命令行标志 --max-requests-inflight--max-mutating-requests-inflight), 可以限制将要接受的未处理的请求,从而防止过量请求入站,潜在导致 API 服务器崩溃。 但是这些标志不足以保证在高流量期间,最重要的请求仍能被服务器接受。

API 优先级和公平性(APF)是一种替代方案,可提升上述最大并发限制。 APF 以更细粒度的方式对请求进行分类和隔离。 它还引入了空间有限的排队机制,因此在非常短暂的突发情况下,API 服务器不会拒绝任何请求。 通过使用公平排队技术从队列中分发请求,这样, 一个行为不佳的控制器就不会饿死其他控制器 (即使优先级相同)。

本功能特性在设计上期望其能与标准控制器一起工作得很好; 这类控制器使用通知组件(Informers)获得信息并对 API 请求的失效作出反应, 在处理失效时能够执行指数型回退。其他客户端也以类似方式工作。

启用/禁用 API 优先级和公平性

API 优先级与公平性(APF)特性由命令行标志控制,默认情况下启用。 有关可用 kube-apiserver 命令行参数以及如何启用和禁用的说明, 请参见参数。 APF 的命令行参数是 "--enable-priority-and-fairness"。 此特性也与某个 API 组相关: (a) 稳定的 v1 版本,在 1.29 中引入,默认启用; (b) v1beta3 版本,默认被启用,在 1.29 中被弃用。 你可以通过添加以下内容来禁用 Beta 版的 v1beta3 API 组:

kube-apiserver \
--runtime-config=flowcontrol.apiserver.k8s.io/v1beta3=false \
  # ...其他配置不变

命令行标志 --enable-priority-fairness=false 将彻底禁用 APF 特性。

概念

APF 特性包含几个不同的功能。 传入的请求通过 FlowSchema 按照其属性分类,并分配优先级。 每个优先级维护自定义的并发限制,加强了隔离度,这样不同优先级的请求,就不会相互饿死。 在同一个优先级内,公平排队算法可以防止来自不同 流(Flow) 的请求相互饿死。 该算法将请求排队,通过排队机制,防止在平均负载较低时,通信量突增而导致请求失败。

优先级

如果未启用 APF,API 服务器中的整体并发量将受到 kube-apiserver 的参数 --max-requests-inflight--max-mutating-requests-inflight 的限制。 启用 APF 后,将对这些参数定义的并发限制进行求和,然后将总和分配到一组可配置的 优先级 中。 每个传入的请求都会分配一个优先级;每个优先级都有各自的限制,设定特定限制允许分发的并发请求数。

例如,默认配置包括针对领导者选举请求、内置控制器请求和 Pod 请求都单独设置优先级。 这表示即使异常的 Pod 向 API 服务器发送大量请求,也无法阻止领导者选举或内置控制器的操作执行成功。

优先级的并发限制会被定期调整,允许利用率较低的优先级将并发度临时借给利用率很高的优先级。 这些限制基于一个优先级可以借出多少个并发度以及可以借用多少个并发度的额定限制和界限, 所有这些均源自下述配置对象。

请求占用的席位

上述并发管理的描述是基线情况。各个请求具有不同的持续时间, 但在与一个优先级的并发限制进行比较时,这些请求在任何给定时刻都以同等方式进行计数。 在这个基线场景中,每个请求占用一个并发单位。 我们用 “席位(Seat)” 一词来表示一个并发单位,其灵感来自火车或飞机上每位乘客占用一个固定座位的供应方式。

但有些请求所占用的席位不止一个。有些请求是服务器预估将返回大量对象的 list 请求。 我们发现这类请求会给服务器带来异常沉重的负担。 出于这个原因,服务器估算将返回的对象数量,并认为请求所占用的席位数与估算得到的数量成正比。

watch 请求的执行时间调整

APF 管理 watch 请求,但这需要考量基线行为之外的一些情况。 第一个关注点是如何判定 watch 请求的席位占用时长。 取决于请求参数不同,对 watch 请求的响应可能以针对所有预先存在的对象 create 通知开头,也可能不这样。 一旦最初的突发通知(如果有)结束,APF 将认为 watch 请求已经用完其席位。

每当向服务器通知创建/更新/删除一个对象时,正常通知都会以并发突发的方式发送到所有相关的 watch 响应流。 为此,APF 认为每个写入请求都会在实际写入完成后花费一些额外的时间来占用席位。 服务器估算要发送的通知数量,并调整写入请求的席位数以及包含这些额外工作后的席位占用时间。

排队

即使在同一优先级内,也可能存在大量不同的流量源。 在过载情况下,防止一个请求流饿死其他流是非常有价值的 (尤其是在一个较为常见的场景中,一个有故障的客户端会疯狂地向 kube-apiserver 发送请求, 理想情况下,这个有故障的客户端不应对其他客户端产生太大的影响)。 公平排队算法在处理具有相同优先级的请求时,实现了上述场景。 每个请求都被分配到某个 流(Flow) 中,该 由对应的 FlowSchema 的名字加上一个 流区分项(Flow Distinguisher) 来标识。 这里的流区分项可以是发出请求的用户、目标资源的名字空间或什么都不是。 系统尝试为不同流中具有相同优先级的请求赋予近似相等的权重。 要启用对不同实例的不同处理方式,多实例的控制器要分别用不同的用户名来执行身份认证。

将请求划分到流中之后,APF 功能将请求分配到队列中。 分配时使用一种称为混洗分片(Shuffle-Sharding)的技术。 该技术可以相对有效地利用队列隔离低强度流与高强度流。

排队算法的细节可针对每个优先等级进行调整,并允许管理员在内存占用、 公平性(当总流量超标时,各个独立的流将都会取得进展)、 突发流量的容忍度以及排队引发的额外延迟之间进行权衡。

豁免请求

某些特别重要的请求不受制于此特性施加的任何限制。 这些豁免可防止不当的流控配置完全禁用 API 服务器。

资源

流控 API 涉及两种资源。 PriorityLevelConfiguration 定义可用的优先级和可处理的并发预算量,还可以微调排队行为。 FlowSchema 用于对每个入站请求进行分类,并与一个 PriorityLevelConfiguration 相匹配。

PriorityLevelConfiguration

一个 PriorityLevelConfiguration 表示单个优先级。每个 PriorityLevelConfiguration 对未完成的请求数有各自的限制,对排队中的请求数也有限制。

PriorityLevelConfiguration 的额定并发限制不是指定请求绝对数量,而是以“额定并发份额”的形式指定。 API 服务器的总并发量限制通过这些份额按例分配到现有 PriorityLevelConfiguration 中, 为每个级别按照数量赋予其额定限制。 集群管理员可以更改 --max-requests-inflight (或 --max-mutating-requests-inflight)的值, 再重新启动 kube-apiserver 来增加或减小服务器的总流量, 然后所有的 PriorityLevelConfiguration 将看到其最大并发增加(或减少)了相同的比例。

一个优先级可以借出的并发数界限以及可以借用的并发数界限在 PriorityLevelConfiguration 表现该优先级的额定限制。 这些界限值乘以额定限制/100.0 并取整,被解析为绝对席位数量。 某优先级的动态调整并发限制范围被约束在 (a) 其额定限制的下限值减去其可借出的席位和 (b) 其额定限制的上限值加上它可以借用的席位之间。 在每次调整时,通过每个优先级推导得出动态限制,具体过程为回收最近出现需求的所有借出的席位, 然后在刚刚描述的界限内共同公平地响应有关这些优先级最近的席位需求。

当入站请求的数量大于分配的 PriorityLevelConfiguration 中允许的并发级别时, type 字段将确定对额外请求的处理方式。 Reject 类型,表示多余的流量将立即被 HTTP 429(请求过多)错误所拒绝。 Queue 类型,表示对超过阈值的请求进行排队,将使用阈值分片和公平排队技术来平衡请求流之间的进度。

公平排队算法支持通过排队配置对优先级微调。 可以在增强建议中阅读算法的详细信息, 但总之:

  • queues 递增能减少不同流之间的冲突概率,但代价是增加了内存使用量。 值为 1 时,会禁用公平排队逻辑,但仍允许请求排队。
  • queueLengthLimit 递增可以在不丢弃任何请求的情况下支撑更大的突发流量, 但代价是增加了等待时间和内存使用量。
  • 修改 handSize 允许你调整过载情况下不同流之间的冲突概率以及单个流可用的整体并发性。

下表显示了有趣的随机分片配置集合,每行显示给定的老鼠(低强度流) 被不同数量的大象挤压(高强度流)的概率。 表来源请参阅: https://play.golang.org/p/Gi0PLgVHiUg

混分切片配置示例
随机分片队列数1 个大象4 个大象16 个大象
12324.428838398950118e-090.114313488300991440.9935089607656024
10321.550093439632541e-080.06264798402235450.9753101519027554
10646.601827268370426e-120.000455713209903707760.49999929150089345
9643.6310049976037345e-110.000455012123041122730.4282314876454858
8642.25929199850899e-100.00048866970530404460.35935114681123076
81286.994461389026097e-133.4055790161620863e-060.02746173137155063
71281.0579122850901972e-116.960839379258192e-060.02406157386340147
72567.597695465552631e-146.728547142019406e-080.0006709661542533682
62562.7134626662687968e-122.9516464018476436e-070.0008895654642000348
65124.116062922897309e-144.982983350480894e-092.26025764343413e-05
610246.337324016514285e-168.09060164312957e-114.517408062903668e-07

FlowSchema

FlowSchema 匹配一些入站请求,并将它们分配给优先级。 每个入站请求都会对 FlowSchema 测试是否匹配, 首先从 matchingPrecedence 数值最低的匹配开始, 然后依次进行,直到首个匹配出现。

当给定的请求与某个 FlowSchema 的 rules 的其中一条匹配,那么就认为该请求与该 FlowSchema 匹配。 判断规则与该请求是否匹配,不仅要求该条规则的 subjects 字段至少存在一个与该请求相匹配, 而且要求该条规则的 resourceRulesnonResourceRules (取决于传入请求是针对资源 URL 还是非资源 URL)字段至少存在一个与该请求相匹配。

对于 subjects 中的 name 字段和资源和非资源规则的 verbsapiGroupsresourcesnamespacesnonResourceURLs 字段, 可以指定通配符 * 来匹配任意值,从而有效地忽略该字段。

FlowSchema 的 distinguisherMethod.type 字段决定了如何把与该模式匹配的请求分散到各个流中。 可能是 ByUser,在这种情况下,一个请求用户将无法饿死其他容量的用户; 或者是 ByNamespace,在这种情况下,一个名字空间中的资源请求将无法饿死其它名字空间的资源请求; 或者为空(或者可以完全省略 distinguisherMethod), 在这种情况下,与此 FlowSchema 匹配的请求将被视为单个流的一部分。 资源和你的特定环境决定了如何选择正确一个 FlowSchema。

默认值

每个 kube-apiserver 会维护两种类型的 APF 配置对象:强制的(Mandatory)和建议的(Suggested)。

强制的配置对象

有四种强制的配置对象对应内置的守护行为。这里的行为是服务器在还未创建对象之前就具备的行为, 而当这些对象存在时,其规约反映了这类行为。四种强制的对象如下:

  • 强制的 exempt 优先级用于完全不受流控限制的请求:它们总是立刻被分发。 强制的 exempt FlowSchema 把 system:masters 组的所有请求都归入该优先级。 如果合适,你可以定义新的 FlowSchema,将其他请求定向到该优先级。
  • 强制的 catch-all 优先级与强制的 catch-all FlowSchema 结合使用, 以确保每个请求都分类。一般而言,你不应该依赖于 catch-all 的配置, 而应适当地创建自己的 catch-all FlowSchema 和 PriorityLevelConfiguration (或使用默认安装的 global-default 配置)。 因为这一优先级不是正常场景下要使用的,catch-all 优先级的并发度份额很小, 并且不会对请求进行排队。

建议的配置对象

建议的 FlowSchema 和 PriorityLevelConfiguration 包含合理的默认配置。 你可以修改这些对象或者根据需要创建新的配置对象。如果你的集群可能承受较重负载, 那么你就要考虑哪种配置最合适。

建议的配置把请求分为六个优先级:

  • node-high 优先级用于来自节点的健康状态更新。
  • system 优先级用于 system:nodes 组(即 kubelet)的与健康状态更新无关的请求; kubelet 必须能连上 API 服务器,以便工作负载能够调度到其上。
  • leader-election 优先级用于内置控制器的领导选举的请求 (特别是来自 kube-system 名字空间中 system:kube-controller-managersystem:kube-scheduler 用户和服务账号,针对 endpointsconfigmapsleases 的请求)。 将这些请求与其他流量相隔离非常重要,因为领导者选举失败会导致控制器发生故障并重新启动, 这反过来会导致新启动的控制器在同步信息时,流量开销更大。
  • workload-high 优先级用于内置控制器的其他请求。
  • workload-low 优先级用于来自所有其他服务帐户的请求,通常包括来自 Pod 中运行的控制器的所有请求。
  • global-default 优先级可处理所有其他流量,例如:非特权用户运行的交互式 kubectl 命令。

建议的 FlowSchema 用来将请求导向上述的优先级内,这里不再一一列举。

强制的与建议的配置对象的维护

每个 kube-apiserver 都独立地维护其强制的与建议的配置对象, 这一维护操作既是服务器的初始行为,也是其周期性操作的一部分。 因此,当存在不同版本的服务器时,如果各个服务器对于配置对象中的合适内容有不同意见, 就可能出现抖动。

每个 kube-apiserver 都会对强制的与建议的配置对象执行初始的维护操作, 之后(每分钟)对这些对象执行周期性的维护。

对于强制的配置对象,维护操作包括确保对象存在并且包含合适的规约(如果存在的话)。 服务器会拒绝创建或更新与其守护行为不一致的规约。

对建议的配置对象的维护操作被设计为允许其规约被重载。删除操作是不允许的, 维护操作期间会重建这类配置对象。如果你不需要某个建议的配置对象, 你需要将它放在一边,并让其规约所产生的影响最小化。 对建议的配置对象而言,其维护方面的设计也支持在上线新的 kube-apiserver 时完成自动的迁移动作,即便可能因为当前的服务器集合存在不同的版本而可能造成抖动仍是如此。

对建议的配置对象的维护操作包括基于服务器建议的规约创建对象 (如果对象不存在的话)。反之,如果对象已经存在,维护操作的行为取决于是否 kube-apiserver 或者用户在控制对象。如果 kube-apiserver 在控制对象, 则服务器确保对象的规约与服务器所给的建议匹配,如果用户在控制对象, 对象的规约保持不变。

关于谁在控制对象这个问题,首先要看对象上的 apf.kubernetes.io/autoupdate-spec 注解。如果对象上存在这个注解,并且其取值为true,则 kube-apiserver 在控制该对象。如果存在这个注解,并且其取值为false,则用户在控制对象。 如果这两个条件都不满足,则需要进一步查看对象的 metadata.generation。 如果该值为 1,则 kube-apiserver 控制对象,否则用户控制对象。 这些规则是在 1.22 发行版中引入的,而对 metadata.generation 的考量是为了便于从之前较简单的行为迁移过来。希望控制建议的配置对象的用户应该将对象的 apf.kubernetes.io/autoupdate-spec 注解设置为 false

对强制的或建议的配置对象的维护操作也包括确保对象上存在 apf.kubernetes.io/autoupdate-spec 这一注解,并且其取值准确地反映了是否 kube-apiserver 在控制着对象。

维护操作还包括删除那些既非强制又非建议的配置,同时注解配置为 apf.kubernetes.io/autoupdate-spec=true 的对象。

健康检查并发豁免

推荐配置没有为本地 kubelet 对 kube-apiserver 执行健康检查的请求进行任何特殊处理 ——它们倾向于使用安全端口,但不提供凭据。 在推荐配置中,这些请求将分配 global-default FlowSchema 和 global-default 优先级, 这样其他流量可以排除健康检查。

如果添加以下 FlowSchema,健康检查请求不受速率限制。

apiVersion: flowcontrol.apiserver.k8s.io/v1beta3
kind: FlowSchema
metadata:
  name: health-for-strangers
spec:
  matchingPrecedence: 1000
  priorityLevelConfiguration:
    name: exempt
  rules:
    - nonResourceRules:
        - nonResourceURLs:
            - "/healthz"
            - "/livez"
            - "/readyz"
          verbs:
            - "*"
      subjects:
        - kind: Group
          group:
            name: "system:unauthenticated"

可观察性

指标

当你开启了 APF 后,kube-apiserver 会暴露额外指标。 监视这些指标有助于判断你的配置是否不当地限制了重要流量, 或者发现可能会损害系统健康的,行为不良的工作负载。

成熟度水平 BETA

  • apiserver_flowcontrol_rejected_requests_total 是一个计数器向量, 记录被拒绝的请求数量(自服务器启动以来累计值), 可按标签 flow_chema(表示与请求匹配的 FlowSchema)、priority_level (表示分配给请该求的优先级)和 reason 分解。 reason 标签将是以下值之一:

    • queue-full,表明已经有太多请求排队
    • concurrency-limit,表示将 PriorityLevelConfiguration 配置为 Reject 而不是 Queue,或者
    • time-out,表示在其排队时间超期的请求仍在队列中。
    • cancelled,表示该请求未被清除锁定,已从队列中移除。
  • apiserver_flowcontrol_dispatched_requests_total 是一个计数器向量, 记录开始执行的请求数量(自服务器启动以来的累积值), 可按 flow_schemapriority_level 分解。
  • apiserver_flowcontrol_current_inqueue_requests 是一个测量向量, 记录排队中的(未执行)请求的瞬时数量,可按 priority_levelflow_schema 分解。

  • apiserver_flowcontrol_current_executing_requests 是一个测量向量, 记录执行中(不在队列中等待)请求的瞬时数量,可按 priority_levelflow_schema 分解。

  • apiserver_flowcontrol_current_executing_seats 是一个测量向量, 记录了按 priority_levelflow_schema 细分的瞬时占用席位数量。

  • apiserver_flowcontrol_request_wait_duration_seconds 是一个直方图向量, 记录了按 flow_schemapriority_levelexecute 标签细分的请求在队列中等待的时长。 execute 标签表示请求是否已开始执行。

成熟度水平 ALPHA

  • apiserver_current_inqueue_requests 是一个测量向量, 记录最近排队请求数量的高水位线, 由标签 request_kind 分组,标签的值为 mutatingreadOnly。 这些高水位线表示在最近一秒钟内看到的最大数字。 它们补充说明了老的测量向量 apiserver_current_inflight_requests (该量保存了最后一个窗口中,正在处理的请求数量的高水位线)。
  • apiserver_current_inqueue_seats 是一个测量向量, 记录了排队请求中每个请求将占用的最大席位数的总和, 按 flow_schemapriority_level 两个标签进行分组。
  • apiserver_flowcontrol_read_vs_write_current_requests 是一个直方图向量, 在每个纳秒结束时记录请求数量的观察值,可按标签 phase(取值为 waitingexecuting) 和 request_kind(取值为 mutatingreadOnly)分解。 每个观察到的值是一个介于 0 和 1 之间的比值,计算方式为请求数除以该请求数的对应限制 (等待的队列长度限制和执行所用的并发限制)。
  • apiserver_flowcontrol_request_concurrency_in_use 是一个规范向量, 包含占用席位的瞬时数量,可按 priority_levelflow_schema 分解。
  • apiserver_flowcontrol_priority_level_request_utilization 是一个直方图向量, 在每个纳秒结束时记录请求数量的观察值, 可按标签 phase(取值为 waitingexecuting)和 priority_level 分解。 每个观察到的值是一个介于 0 和 1 之间的比值,计算方式为请求数除以该请求数的对应限制 (等待的队列长度限制和执行所用的并发限制)。
  • apiserver_flowcontrol_priority_level_seat_utilization 是一个直方图向量, 在每个纳秒结束时记录某个优先级并发度限制利用率的观察值,可按标签 priority_level 分解。 此利用率是一个分数:(占用的席位数)/(并发限制)。 此指标考虑了除 WATCH 之外的所有请求的所有执行阶段(包括写入结束时的正常延迟和额外延迟, 以覆盖相应的通知操作);对于 WATCH 请求,只考虑传递预先存在对象通知的初始阶段。 该向量中的每个直方图也带有 phase: executing(等待阶段没有席位限制)的标签。
  • apiserver_flowcontrol_request_queue_length_after_enqueue 是一个直方图向量, 记录请求队列的长度,可按 priority_levelflow_schema 分解。 每个排队中的请求都会为其直方图贡献一个样本,并在添加请求后立即上报队列的长度。 请注意,这样产生的统计数据与无偏调查不同。

  • apiserver_flowcontrol_request_concurrency_limitapiserver_flowcontrol_nominal_limit_seats 相同。在优先级之间引入并发度借用之前, 此字段始终等于 apiserver_flowcontrol_current_limit_seats (它过去不作为一个独立的指标存在)。
  • apiserver_flowcontrol_lower_limit_seats 是一个测量向量,包含每个优先级的动态并发度限制的下限。
  • apiserver_flowcontrol_upper_limit_seats 是一个测量向量,包含每个优先级的动态并发度限制的上限。
  • apiserver_flowcontrol_demand_seats 是一个直方图向量, 统计每纳秒结束时每个优先级的(席位需求)/(额定并发限制)比率的观察值。 某优先级的席位需求是针对排队的请求和初始执行阶段的请求,在请求的初始和最终执行阶段占用的最大席位数之和。
  • apiserver_flowcontrol_demand_seats_high_watermark 是一个测量向量, 为每个优先级包含了上一个并发度借用调整期间所观察到的最大席位需求。
  • apiserver_flowcontrol_demand_seats_average 是一个测量向量, 为每个优先级包含了上一个并发度借用调整期间所观察到的时间加权平均席位需求。
  • apiserver_flowcontrol_demand_seats_stdev 是一个测量向量, 为每个优先级包含了上一个并发度借用调整期间所观察到的席位需求的时间加权总标准偏差。
  • apiserver_flowcontrol_demand_seats_smoothed 是一个测量向量, 为每个优先级包含了上一个并发度调整期间确定的平滑包络席位需求。
  • apiserver_flowcontrol_target_seats 是一个测量向量, 包含每个优先级触发借用分配问题的并发度目标值。
  • apiserver_flowcontrol_seat_fair_frac 是一个测量向量, 包含了上一个借用调整期间确定的公平分配比例。
  • apiserver_flowcontrol_current_limit_seats 是一个测量向量, 包含每个优先级的上一次调整期间得出的动态并发限制。
  • apiserver_flowcontrol_request_execution_seconds 是一个直方图向量, 记录请求实际执行需要花费的时间, 可按标签 flow_schemapriority_level 分解。
  • apiserver_flowcontrol_watch_count_samples 是一个直方图向量, 记录给定写的相关活动 WATCH 请求数量, 可按标签 flow_schemapriority_level 分解。
  • apiserver_flowcontrol_work_estimated_seats 是一个直方图向量, 记录与估计席位(最初阶段和最后阶段的最多人数)相关联的请求数量, 可按标签 flow_schemapriority_level 分解。
  • apiserver_flowcontrol_request_dispatch_no_accommodation_total 是一个事件数量的计数器,这些事件在原则上可能导致请求被分派, 但由于并发度不足而没有被分派, 可按标签 flow_schemapriority_level 分解。
  • apiserver_flowcontrol_epoch_advance_total 是一个计数器向量, 记录了将优先级进度计向后跳跃以避免数值溢出的尝试次数, 按 priority_levelsuccess 两个标签进行分组。

使用 API 优先级和公平性的最佳实践

当某个给定的优先级级别超过其所被允许的并发数时,请求可能会遇到延迟增加, 或以错误 HTTP 429 (Too Many Requests) 的形式被拒绝。 为了避免这些 APF 的副作用,你可以修改你的工作负载或调整你的 APF 设置,确保有足够的席位来处理请求。

要检测请求是否由于 APF 而被拒绝,可以检查以下指标:

  • apiserver_flowcontrol_rejected_requests_total: 每个 FlowSchema 和 PriorityLevelConfiguration 拒绝的请求总数。
  • apiserver_flowcontrol_current_inqueue_requests: 每个 FlowSchema 和 PriorityLevelConfiguration 中排队的当前请求数。
  • apiserver_flowcontrol_request_wait_duration_seconds:请求在队列中等待的延迟时间。
  • apiserver_flowcontrol_priority_level_seat_utilization: 每个 PriorityLevelConfiguration 的席位利用率。

工作负载修改

为了避免由于 APF 导致请求排队、延迟增加或被拒绝,你可以通过以下方式优化请求:

  • 减少请求执行的速率。在固定时间段内减少请求数量将导致在某一给定时间点需要的席位数更少。
  • 避免同时发出大量消耗较多席位的请求。请求可以被优化为使用更少的席位或降低延迟, 使这些请求占用席位的时间变短。列表请求根据请求期间获取的对象数量可能会占用多个席位。 例如通过使用分页等方式限制列表请求中取回的对象数量,可以在更短时间内使用更少的总席位数。 此外,将列表请求替换为监视请求将需要更低的总并发份额,因为监视请求仅在初始的通知突发阶段占用 1 个席位。 如果在 1.27 及更高版本中使用流式列表,因为集合的整个状态必须以流式传输, 所以监视请求在其初始的通知突发阶段将占用与列表请求相同数量的席位。 请注意,在这两种情况下,监视请求在此初始阶段之后将不再保留任何席位。

请注意,由于请求数量增加或现有请求的延迟增加,APF 可能会导致请求排队或被拒绝。 例如,如果通常需要 1 秒执行的请求开始需要 60 秒,由于延迟增加, 请求所占用的席位时间可能超过了正常情况下的时长,APF 将开始拒绝请求。 如果在没有工作负载显著变化的情况下,APF 开始在多个优先级级别上拒绝请求, 则可能存在控制平面性能的潜在问题,而不是工作负载或 APF 设置的问题。

优先级和公平性设置

你还可以修改默认的 FlowSchema 和 PriorityLevelConfiguration 对象, 或创建新的对象来更好地容纳你的工作负载。

APF 设置可以被修改以实现下述目标:

  • 给予高优先级请求更多的席位。
  • 隔离那些非必要或开销大的请求,因为如果与其他流共享,这些请求可能会耗尽所有并发级别。

给予高优先级请求更多的席位

  1. 如果有可能,你可以通过提高 max-requests-inflightmax-mutating-requests-inflight 参数的值为特定 kube-apiserver 提高所有优先级级别均可用的席位数量。另外, 如果在请求的负载均衡足够好的情况下,水平扩缩 kube-apiserver 实例的数量将提高集群中每个优先级级别的总并发数。
  1. 你可以创建一个新的 FlowSchema,在其中引用并发级别更高的 PriorityLevelConfiguration。 这个新的 PriorityLevelConfiguration 可以是现有的级别,也可以是具有自己一组额定并发份额的新级别。 例如,你可以引入一个新的 FlowSchema 来将请求的 PriorityLevelConfiguration 从全局默认值更改为工作负载较低的级别,以增加用户可用的席位数。 创建一个新的 PriorityLevelConfiguration 将减少为现有级别指定的席位数。 请注意,编辑默认的 FlowSchema 或 PriorityLevelConfiguration 需要将 apf.kubernetes.io/autoupdate-spec 注解设置为 false。
  1. 你还可以为服务于高优先级请求的 PriorityLevelConfiguration 提高 NominalConcurrencyShares。 此外在 1.26 及更高版本中,你可以为有竞争的优先级级别提高 LendablePercent,以便给定优先级级别可以借用更多的席位。

隔离非关键请求以免饿死其他流

为了进行请求隔离,你可以创建一个 FlowSchema,使其主体与发起这些请求的用户匹配, 或者创建一个与请求内容匹配(对应 resourceRules)的 FlowSchema。 接下来,你可以将该 FlowSchema 映射到一个具有较低席位份额的 PriorityLevelConfiguration。

例如,假设来自 default 名字空间中运行的 Pod 的每个事件列表请求使用 10 个席位,并且执行时间为 1 分钟。 为了防止这些开销大的请求影响使用现有服务账号 FlowSchema 的其他 Pod 的请求,你可以应用以下 FlowSchema 将这些列表调用与其他请求隔离开来。

用于隔离列表事件请求的 FlowSchema 对象示例:

apiVersion: flowcontrol.apiserver.k8s.io/v1beta3
kind: FlowSchema
metadata:
  name: list-events-default-service-account
spec:
  distinguisherMethod:
    type: ByUser
  matchingPrecedence: 8000
  priorityLevelConfiguration:
    name: catch-all
  rules:
    - resourceRules:
      - apiGroups:
          - '*'
        namespaces:
          - default
        resources:
          - events
        verbs:
          - list
      subjects:
        - kind: ServiceAccount
          serviceAccount:
            name: default
            namespace: default
  • 这个 FlowSchema 用于抓取 default 名字空间中默认服务账号所发起的所有事件列表调用。 匹配优先级为 8000,低于现有服务账号 FlowSchema 所用的 9000,因此这些列表事件调用将匹配到 list-events-default-service-account 而不是服务账号。
  • 通用 PriorityLevelConfiguration 用于隔离这些请求。通用优先级级别具有非常小的并发份额,并且不对请求进行排队。

接下来

3.11.11 - 安装扩展(Addon)

Add-on 扩展了 Kubernetes 的功能。

本文列举了一些可用的 add-on 以及到它们各自安装说明的链接。该列表并不试图详尽无遗。

联网和网络策略

  • ACI 通过 Cisco ACI 提供集成的容器网络和安全网络。
  • Antrea 在第 3/4 层执行操作,为 Kubernetes 提供网络连接和安全服务。Antrea 利用 Open vSwitch 作为网络的数据面。 Antrea 是一个沙箱级的 CNCF 项目
  • Calico 是一个联网和网络策略供应商。 Calico 支持一套灵活的网络选项,因此你可以根据自己的情况选择最有效的选项,包括非覆盖和覆盖网络,带或不带 BGP。 Calico 使用相同的引擎为主机、Pod 和(如果使用 Istio 和 Envoy)应用程序在服务网格层执行网络策略。
  • Canal 结合 Flannel 和 Calico,提供联网和网络策略。
  • Cilium 是一种网络、可观察性和安全解决方案,具有基于 eBPF 的数据平面。 Cilium 提供了简单的 3 层扁平网络, 能够以原生路由(routing)和覆盖/封装(overlay/encapsulation)模式跨越多个集群, 并且可以使用与网络寻址分离的基于身份的安全模型在 L3 至 L7 上实施网络策略。 Cilium 可以作为 kube-proxy 的替代品;它还提供额外的、可选的可观察性和安全功能。 Cilium 是一个毕业级别的 CNCF 项目
  • CNI-Genie 使 Kubernetes 无缝连接到 Calico、Canal、Flannel 或 Weave 等其中一种 CNI 插件。 CNI-Genie 是一个沙箱级的 CNCF 项目
  • Contiv 为各种用例和丰富的策略框架提供可配置的网络 (带 BGP 的原生 L3、带 vxlan 的覆盖、标准 L2 和 Cisco-SDN/ACI)。 Contiv 项目完全开源。 其安装程序 提供了基于 kubeadm 和非 kubeadm 的安装选项。
  • Contrail 基于 Tungsten Fabric,是一个开源的多云网络虚拟化和策略管理平台。 Contrail 和 Tungsten Fabric 与业务流程系统(例如 Kubernetes、OpenShift、OpenStack 和 Mesos)集成在一起, 为虚拟机、容器或 Pod 以及裸机工作负载提供了隔离模式。
  • Flannel 是一个可以用于 Kubernetes 的 overlay 网络提供者。
  • Gateway API 是一个由 SIG Network 社区管理的开源项目, 为服务网络建模提供一种富有表达力、可扩展和面向角色的 API。
  • Knitter 是在一个 Kubernetes Pod 中支持多个网络接口的插件。
  • Multus 是一个多插件, 可在 Kubernetes 中提供多种网络支持,以支持所有 CNI 插件(例如 Calico、Cilium、Contiv、Flannel), 而且包含了在 Kubernetes 中基于 SRIOV、DPDK、OVS-DPDK 和 VPP 的工作负载。
  • OVN-Kubernetes 是一个 Kubernetes 网络驱动, 基于 OVN(Open Virtual Network)实现,是从 Open vSwitch (OVS) 项目衍生出来的虚拟网络实现。OVN-Kubernetes 为 Kubernetes 提供基于覆盖网络的网络实现, 包括一个基于 OVS 实现的负载均衡器和网络策略。
  • Nodus 是一个基于 OVN 的 CNI 控制器插件, 提供基于云原生的服务功能链 (SFC)。
  • NSX-T 容器插件(NCP) 提供了 VMware NSX-T 与容器协调器(例如 Kubernetes)之间的集成,以及 NSX-T 与基于容器的 CaaS / PaaS 平台(例如关键容器服务(PKS)和 OpenShift)之间的集成。
  • Nuage 是一个 SDN 平台,可在 Kubernetes Pods 和非 Kubernetes 环境之间提供基于策略的联网,并具有可视化和安全监控。
  • Romana 是一个 Pod 网络的第三层解决方案,并支持 NetworkPolicy API。
  • Spiderpool 为 Kubernetes 提供了下层网络和 RDMA 高速网络解决方案,兼容裸金属、虚拟机和公有云等运行环境。
  • Weave Net 提供在网络分组两端参与工作的联网和网络策略,并且不需要额外的数据库。

服务发现

  • CoreDNS 是一种灵活的,可扩展的 DNS 服务器,可以 安装为集群内的 Pod 提供 DNS 服务。

可视化管理

  • Dashboard 是一个 Kubernetes 的 Web 控制台界面。
  • Weave Scope 是一个可视化工具,用于查看你的容器、Pod、服务等。

基础设施

插桩

遗留 Add-on

还有一些其它 add-on 归档在已废弃的 cluster/addons 路径中。

维护完善的 add-on 应该被链接到这里。欢迎提出 PR!

3.12 - Kubernetes 中的 Windows

Kubernetes 支持运行 Microsoft Windows 节点。

Kubernetes 支持运行 Linux 或 Microsoft Windows 的工作节点

CNCF 及其母公司 Linux 基金会采用供应商中立的方法来实现兼容性。可以将你的 Windows 服务器作为工作节点加入 Kubernetes 集群。

无论你的集群使用什么操作系统, 都可以在 Windows 上安装和设置 kubectl

如果你使用的是 Windows 节点,你可以阅读:

或者,要了解概览,请阅读:

3.12.1 - Kubernetes 中的 Windows 容器

在许多组织中,所运行的很大一部分服务和应用是 Windows 应用。 Windows 容器提供了一种封装进程和包依赖项的方式, 从而简化了 DevOps 实践,令 Windows 应用同样遵从云原生模式。

对于同时投入基于 Windows 应用和 Linux 应用的组织而言,他们不必寻找不同的编排系统来管理其工作负载, 使其跨部署的运营效率得以大幅提升,而不必关心所用的操作系统。

Kubernetes 中的 Windows 节点

若要在 Kubernetes 中启用对 Windows 容器的编排,可以在现有的 Linux 集群中包含 Windows 节点。 在 Kubernetes 上调度 Pod 中的 Windows 容器与调度基于 Linux 的容器类似。

为了运行 Windows 容器,你的 Kubernetes 集群必须包含多个操作系统。 尽管你只能在 Linux 上运行控制平面, 你可以部署运行 Windows 或 Linux 的工作节点。

支持 Windows 节点的前提是操作系统为 Windows Server 2019 或 Windows Server 2022。

本文使用术语 Windows 容器表示具有进程隔离能力的 Windows 容器。 Kubernetes 不支持使用 Hyper-V 隔离能力来运行 Windows 容器。

兼容性与局限性

某些节点层面的功能特性仅在使用特定容器运行时时才可用; 另外一些特性则在 Windows 节点上不可用,包括:

  • 巨页(HugePages):Windows 容器当前不支持。
  • 特权容器:Windows 容器当前不支持。 HostProcess 容器提供类似功能。
  • TerminationGracePeriod:需要 containerD。

Windows 节点并不支持共享命名空间的所有功能特性。 有关更多详细信息,请参考 API 兼容性

有关 Kubernetes 测试时所使用的 Windows 版本的详细信息,请参考 Windows 操作系统版本兼容性

从 API 和 kubectl 的角度来看,Windows 容器的行为与基于 Linux 的容器非常相似。 然而,在本节所概述的一些关键功能上,二者存在一些显著差异。

与 Linux 比较

Kubernetes 关键组件在 Windows 上的工作方式与在 Linux 上相同。 本节介绍几个关键的工作负载抽象及其如何映射到 Windows。

  • Pod

    Pod 是 Kubernetes 的基本构建块,是可以创建或部署的最小和最简单的单元。 你不可以在同一个 Pod 中部署 Windows 和 Linux 容器。 Pod 中的所有容器都调度到同一 Node 上,每个 Node 代表一个特定的平台和体系结构。 Windows 容器支持以下 Pod 能力、属性和事件:

    • 每个 Pod 有一个或多个容器,具有进程隔离和卷共享能力
    • Pod status 字段
    • 就绪、存活和启动探针
    • postStart 和 preStop 容器生命周期回调
    • ConfigMap 和 Secret:作为环境变量或卷
    • emptyDir
    • 命名管道形式的主机挂载
    • 资源限制
    • 操作系统字段:

    .spec.os.name 字段应设置为 windows 以表明当前 Pod 使用 Windows 容器。

    如果你将 `.spec.os.name` 字段设置为 `windows`,
    

    则你必须不能在对应 Pod 的 .spec 中设置以下字段:

    • spec.hostPID
    • spec.hostIPC
    • 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
    在上述列表中,通配符(`*`)表示列表中的所有项。
    

    例如,spec.containers[*].securityContext 指代所有容器的 SecurityContext 对象。 如果指定了这些字段中的任意一个,则 API 服务器不会接受此 Pod。

Pod、工作负载资源和 Service 是在 Kubernetes 上管理 Windows 工作负载的关键元素。 然而,它们本身还不足以在动态的云原生环境中对 Windows 工作负载进行恰当的生命周期管理。

kubelet 的命令行选项

某些 kubelet 命令行选项在 Windows 上的行为不同,如下所述:

  • --windows-priorityclass 允许你设置 kubelet 进程的调度优先级 (参考 CPU 资源管理)。
  • --kube-reserved--system-reserved--eviction-hard 标志更新 NodeAllocatable
  • 未实现使用 --enforce-node-allocable 驱逐。
  • 未实现使用 --eviction-hard--eviction-soft 驱逐。
  • 在 Windows 节点上运行时,kubelet 没有内存或 CPU 限制。 --kube-reserved--system-reserved 仅从 NodeAllocatable 中减去,并且不保证为工作负载提供的资源。 有关更多信息,请参考 Windows 节点的资源管理
  • 未实现 MemoryPressure 条件。
  • kubelet 不会执行 OOM 驱逐操作。

API 兼容性

由于操作系统和容器运行时的缘故,Kubernetes API 在 Windows 上的工作方式存在细微差异。 某些工作负载属性是为 Linux 设计的,无法在 Windows 上运行。

从较高的层面来看,以下操作系统概念是不同的:

  • 身份 - Linux 使用 userID(UID)和 groupID(GID),表示为整数类型。 用户名和组名是不规范的,它们只是 /etc/groups/etc/passwd 中的别名, 作为 UID+GID 的后备标识。 Windows 使用更大的二进制安全标识符(SID), 存放在 Windows 安全访问管理器(Security Access Manager,SAM)数据库中。 此数据库在主机和容器之间或容器之间不共享。
  • 文件权限 - Windows 使用基于 SID 的访问控制列表, 而像 Linux 使用基于对象权限和 UID+GID 的位掩码(POSIX 系统)以及可选的访问控制列表。
  • 文件路径 - Windows 上的约定是使用 \ 而不是 /。 Go IO 库通常接受两者,能让其正常工作,但当你设置要在容器内解读的路径或命令行时, 可能需要用 \
  • 信号 - Windows 交互式应用处理终止的方式不同,可以实现以下一种或多种:
    • UI 线程处理包括 WM_CLOSE 在内准确定义的消息。
    • 控制台应用使用控制处理程序(Control Handler)处理 Ctrl-C 或 Ctrl-Break。
    • 服务会注册可接受 SERVICE_CONTROL_STOP 控制码的服务控制处理程序(Service Control Handler)函数。

容器退出码遵循相同的约定,其中 0 表示成功,非零表示失败。 具体的错误码在 Windows 和 Linux 中可能不同。 但是,从 Kubernetes 组件(kubelet、kube-proxy)传递的退出码保持不变。

容器规约的字段兼容性

以下列表记录了 Pod 容器规约在 Windows 和 Linux 之间的工作方式差异:

  • 巨页(Huge page)在 Windows 容器运行时中未实现,且不可用。 巨页需要不可为容器配置的用户特权生效
  • requests.cpurequests.memory - 从节点可用资源中减去请求,因此请求可用于避免一个节点过量供应。 但是,请求不能用于保证已过量供应的节点中的资源。 如果运营商想要完全避免过量供应,则应将设置请求作为最佳实践应用到所有容器。
  • securityContext.allowPrivilegeEscalation - 不能在 Windows 上使用;所有权能字都无法生效。
  • securityContext.capabilities - POSIX 权能未在 Windows 上实现。
  • securityContext.privileged - Windows 不支持特权容器, 可使用 HostProcess 容器代替。
  • securityContext.procMount - Windows 没有 /proc 文件系统。
  • securityContext.readOnlyRootFilesystem - 不能在 Windows 上使用;对于容器内运行的注册表和系统进程,写入权限是必需的。
  • securityContext.runAsGroup - 不能在 Windows 上使用,因为不支持 GID。
  • securityContext.runAsNonRoot - 此设置将阻止以 ContainerAdministrator 身份运行容器,这是 Windows 上与 root 用户最接近的身份。
  • securityContext.runAsUser - 改用 runAsUserName
  • securityContext.seLinuxOptions - 不能在 Windows 上使用,因为 SELinux 特定于 Linux。
  • terminationMessagePath - 这个字段有一些限制,因为 Windows 不支持映射单个文件。 默认值为 /dev/termination-log,因为默认情况下它在 Windows 上不存在,所以能生效。

Pod 规约的字段兼容性

以下列表记录了 Pod 规约在 Windows 和 Linux 之间的工作方式差异:

  • hostIPChostpid - 不能在 Windows 上共享主机命名空间。
  • hostNetwork - 参见下文
  • dnsPolicy - Windows 不支持将 Pod dnsPolicy 设为 ClusterFirstWithHostNet, 因为未提供主机网络。Pod 始终用容器网络运行。
  • podSecurityContext 参见下文
  • shareProcessNamespace - 这是一个 Beta 版功能特性,依赖于 Windows 上未实现的 Linux 命名空间。 Windows 无法共享进程命名空间或容器的根文件系统(root filesystem)。 只能共享网络。
  • terminationGracePeriodSeconds - 这在 Windows 上的 Docker 中没有完全实现, 请参考 GitHub issue。 目前的行为是通过 CTRL_SHUTDOWN_EVENT 发送 ENTRYPOINT 进程,然后 Windows 默认等待 5 秒, 最后使用正常的 Windows 关机行为终止所有进程。 5 秒默认值实际上位于容器内的 Windows 注册表中,因此在构建容器时可以覆盖这个值。
  • volumeDevices - 这是一个 Beta 版功能特性,未在 Windows 上实现。 Windows 无法将原始块设备挂接到 Pod。
  • volumes
    • 如果你定义一个 emptyDir 卷,则你无法将卷源设为 memory
  • 你无法为卷挂载启用 mountPropagation,因为这在 Windows 上不支持。

hostNetwork 的字段兼容性

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

现在,kubelet 可以请求在 Windows 节点上运行的 Pod 使用主机的网络命名空间,而不是创建新的 Pod 网络命名空间。 要启用此功能,请将 --feature-gates=WindowsHostNetwork=true 传递给 kubelet。

Pod 安全上下文的字段兼容性

Pod 的 securityContext 中只有 securityContext.runAsNonRootsecurityContext.windowsOptions 字段在 Windows 上生效。

节点问题检测器

节点问题检测器(参考节点健康监测)初步支持 Windows。 有关更多信息,请访问该项目的 GitHub 页面

Pause 容器

在 Kubernetes Pod 中,首先创建一个基础容器或 “pause” 容器来承载容器。 在 Linux 中,构成 Pod 的 cgroup 和命名空间维持持续存在需要一个进程; 而 pause 进程就提供了这个功能。 属于同一 Pod 的容器(包括基础容器和工作容器)共享一个公共网络端点 (相同的 IPv4 和/或 IPv6 地址,相同的网络端口空间)。 Kubernetes 使用 pause 容器以允许工作容器崩溃或重启,而不会丢失任何网络配置。

Kubernetes 维护一个多体系结构的镜像,包括对 Windows 的支持。 对于 Kubernetes v1.30.0,推荐的 pause 镜像为 registry.k8s.io/pause:3.6。 可在 GitHub 上获得源代码

Microsoft 维护一个不同的多体系结构镜像,支持 Linux 和 Windows amd64, 你可以找到的镜像类似 mcr.microsoft.com/oss/kubernetes/pause:3.6。 此镜像的构建与 Kubernetes 维护的镜像同源,但所有 Windows 可执行文件均由 Microsoft 进行了验证码签名。 如果你正部署到一个需要签名可执行文件的生产或类生产环境, Kubernetes 项目建议使用 Microsoft 维护的镜像。

容器运行时

你需要将容器运行时安装到集群中的每个节点, 这样 Pod 才能在这些节点上运行。

以下容器运行时适用于 Windows:

ContainerD

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

对于运行 Windows 的 Kubernetes 节点,你可以使用 ContainerD 1.4.0+ 作为容器运行时。

学习如何在 Windows 上安装 ContainerD

Mirantis 容器运行时

Mirantis 容器运行时(MCR) 可作为所有 Windows Server 2019 和更高版本的容器运行时。

有关更多信息,请参考在 Windows Server 上安装 MCR

Windows 操作系统版本兼容性

在 Windows 节点上,如果主机操作系统版本必须与容器基础镜像操作系统版本匹配, 则会应用严格的兼容性规则。 仅 Windows Server 2019 作为容器操作系统时,才能完全支持 Windows 容器。

对于 Kubernetes v1.30,Windows 节点(和 Pod)的操作系统兼容性如下:

Windows Server LTSC release
Windows Server 2019
Windows Server 2022
Windows Server SAC release
Windows Server version 20H2

也适用 Kubernetes 版本偏差策略

硬件建议和注意事项

  • 64 位处理器,4 核或更多的 CPU,能够支持虚拟化
  • 8GB 或更多的 RAM
  • 50GB 或更多的可用磁盘空间

有关最新的最低硬件要求信息, 请参考微软文档:Windows Server 的硬件要求。 有关决定生产工作节点资源的指导信息, 请参考 Kubernetes 文档:生产用工作节点

为了优化系统资源,如果图形用户界面不是必需的,最好选择一个不包含 Windows 桌面体验安装选项的 Windows Server 操作系统安装包,因为这种配置通常会释放更多的系统资源。

在估算 Windows 工作节点的磁盘空间时,需要注意 Windows 容器镜像通常比 Linux 容器镜像更大, 单个镜像的容器大小范围从 300MB 到超过 10GB。 此外,需要注意 Windows 容器中的 C: 驱动器默认呈现的虚拟剩余空间为 20GB, 这不是实际的占用空间,而是使用主机上的本地存储时单个容器可以最多占用的磁盘大小。 有关更多详细信息, 请参见在 Windows 上运行容器 - 容器存储文档

获取帮助和故障排查

对 Kubernetes 集群进行故障排查的主要帮助来源应始于故障排查页面。

本节包括了一些其他特定于 Windows 的故障排查帮助。 日志是解决 Kubernetes 中问题的重要元素。 确保在任何时候向其他贡献者寻求故障排查协助时随附了日志信息。 遵照 SIG Windows 日志收集贡献指南中的指示说明。

报告问题和功能请求

如果你发现疑似 bug,或者你想提出功能请求,请按照 SIG Windows 贡献指南 新建一个 Issue。你应该先搜索 Issue 列表,以防之前报告过这个问题,凭你对该问题的经验添加评论, 并随附日志信息。Kubernetes Slack 上的 SIG Windows 频道也是一个很好的途径, 可以在创建工单之前获得一些初始支持和故障排查思路。

验证 Windows 集群的操作性

Kubernetes 项目提供了 Windows 操作准备 规范,配备了结构化的测试套件。 这个套件分为两组测试:核心和扩展。每组测试都包含了针对特定场景的分类测试。 它可以用来验证 Windows 和混合系统(混合了 Linux 节点)的所有功能,实现全面覆盖。

要在新创建的集群上搭建此项目, 请参考项目指南中的说明。

部署工具

kubeadm 工具帮助你部署 Kubernetes 集群,提供管理集群的控制平面以及运行工作负载的节点。

Kubernetes 集群 API 项目也提供了自动部署 Windows 节点的方式。

Windows 分发渠道

有关 Windows 分发渠道的详细阐述,请参考 Microsoft 文档

有关支持模型在内的不同 Windows Server 服务渠道的信息,请参考 Windows Server 服务渠道

3.12.2 - Kubernetes 中的 Windows 容器调度指南

在许多组织中运行的服务和应用程序中,Windows 应用程序构成了很大一部分。 本指南将引导你完成在 Kubernetes 中配置和部署 Windows 容器的步骤。

目标

  • 配置 Deployment 样例以在 Windows 节点上运行 Windows 容器
  • 在 Kubernetes 中突出 Windows 特定的功能

在你开始之前

  • 创建一个 Kubernetes 集群,其中包含一个控制平面和一个运行 Windows Server 的工作节点。
  • 务必请注意,在 Kubernetes 上创建和部署服务和工作负载的行为方式与 Linux 和 Windows 容器的行为方式大致相同。 与集群交互的 kubectl 命令是一致的。 下一小节的示例旨在帮助你快速开始使用 Windows 容器。

快速开始:部署 Windows 容器

以下示例 YAML 文件部署了一个在 Windows 容器内运行的简单 Web 服务器的应用程序。

创建一个名为 win-webserver.yaml 的 Service 规约,其内容如下:

apiVersion: v1
kind: Service
metadata:
  name: win-webserver
  labels:
    app: win-webserver
spec:
  ports:
    # 此 Service 服务的端口
    - port: 80
      targetPort: 80
  selector:
    app: win-webserver
  type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: win-webserver
  name: win-webserver
spec:
  replicas: 2
  selector:
    matchLabels:
      app: win-webserver
  template:
    metadata:
      labels:
        app: win-webserver
      name: win-webserver
    spec:
     containers:
      - name: windowswebserver
        image: mcr.microsoft.com/windows/servercore:ltsc2019
        command:
        - powershell.exe
        - -command
        - "<#code used from https://gist.github.com/19WAS85/5424431#> ; $$listener = New-Object System.Net.HttpListener ; $$listener.Prefixes.Add('http://*:80/') ; $$listener.Start() ; $$callerCounts = @{} ; Write-Host('Listening at http://*:80/') ; while ($$listener.IsListening) { ;$$context = $$listener.GetContext() ;$$requestUrl = $$context.Request.Url ;$$clientIP = $$context.Request.RemoteEndPoint.Address ;$$response = $$context.Response ;Write-Host '' ;Write-Host('> {0}' -f $$requestUrl) ;  ;$$count = 1 ;$$k=$$callerCounts.Get_Item($$clientIP) ;if ($$k -ne $$null) { $$count += $$k } ;$$callerCounts.Set_Item($$clientIP, $$count) ;$$ip=(Get-NetAdapter | Get-NetIpAddress); $$header='<html><body><H1>Windows Container Web Server</H1>' ;$$callerCountsString='' ;$$callerCounts.Keys | % { $$callerCountsString+='<p>IP {0} callerCount {1} ' -f $$ip[1].IPAddress,$$callerCounts.Item($$_) } ;$$footer='</body></html>' ;$$content='{0}{1}{2}' -f $$header,$$callerCountsString,$$footer ;Write-Output $$content ;$$buffer = [System.Text.Encoding]::UTF8.GetBytes($$content) ;$$response.ContentLength64 = $$buffer.Length ;$$response.OutputStream.Write($$buffer, 0, $$buffer.Length) ;$$response.Close() ;$$responseStatus = $$response.StatusCode ;Write-Host('< {0}' -f $$responseStatus)  } ; "
     nodeSelector:
      kubernetes.io/os: windows
  1. 检查所有节点是否健康

    kubectl get nodes
    
  1. 部署 Service 并监视 Pod 更新:

    kubectl apply -f win-webserver.yaml
    kubectl get pods -o wide -w
    

    当 Service 被正确部署时,两个 Pod 都被标记为就绪(Ready)。要退出 watch 命令,请按 Ctrl+C。

  1. 检查部署是否成功。请验证:

    • 当执行 kubectl get pods 命令时,能够从 Linux 控制平面所在的节点上列出两个 Pod。
    • 跨网络的节点到 Pod 通信,从 Linux 控制平面所在的节点上执行 curl 命令来访问 Pod IP 的 80 端口以检查 Web 服务器响应。
    • Pod 间通信,使用 docker execkubectl exec 命令进入容器,并在 Pod 之间(以及跨主机,如果你有多个 Windows 节点)相互进行 ping 操作。
    • Service 到 Pod 的通信,在 Linux 控制平面所在的节点以及独立的 Pod 中执行 curl 命令来访问虚拟的服务 IP(在 kubectl get services 命令下查看)。
    • 服务发现,执行 curl 命令来访问带有 Kubernetes 默认 DNS 后缀的服务名称。
    • 入站连接,在 Linux 控制平面所在的节点上或集群外的机器上执行 curl 命令来访问 NodePort 服务。
    • 出站连接,使用 kubectl exec,从 Pod 内部执行 curl 访问外部 IP。

可观察性

捕捉来自工作负载的日志

日志是可观察性的重要元素;它们使用户能够深入了解工作负载的运行情况,并且是解决问题的关键因素。 由于 Windows 容器和 Windows 容器中的工作负载与 Linux 容器的行为不同,因此用户很难收集日志,从而限制了操作可见性。 例如,Windows 工作负载通常配置为记录到 ETW(Windows 事件跟踪)或向应用程序事件日志推送条目。 LogMonitor 是一个微软开源的工具,是监视 Windows 容器内所配置的日志源的推荐方法。 LogMonitor 支持监视事件日志、ETW 提供程序和自定义应用程序日志,将它们传送到 STDOUT 以供 kubectl logs <pod> 使用。

按照 LogMonitor GitHub 页面中的说明,将其二进制文件和配置文件复制到所有容器, 并为 LogMonitor 添加必要的入口点以将日志推送到标准输出(STDOUT)。

配置容器用户

使用可配置的容器用户名

Windows 容器可以配置为使用不同于镜像默认值的用户名来运行其入口点和进程。 在这里了解更多信息。

使用组托管服务帐户(GMSA)管理工作负载身份

Windows 容器工作负载可以配置为使用组托管服务帐户(Group Managed Service Accounts,GMSA)。 组托管服务帐户是一种特定类型的活动目录(Active Directory)帐户,可提供自动密码管理、 简化的服务主体名称(Service Principal Name,SPN)管理,以及将管理委派给多个服务器上的其他管理员的能力。 配置了 GMSA 的容器可以携带使用 GMSA 配置的身份访问外部活动目录域资源。 在此处了解有关为 Windows 容器配置和使用 GMSA 的更多信息。

污点和容忍度

用户需要使用某种污点(Taint)和节点选择器的组合,以便将 Linux 和 Windows 工作负载各自调度到特定操作系统的节点。 下面概述了推荐的方法,其主要目标之一是该方法不应破坏现有 Linux 工作负载的兼容性。

从 1.25 开始,你可以(并且应该)将每个 Pod 的 .spec.os.name 设置为 Pod 中的容器设计所用于的操作系统。 对于运行 Linux 容器的 Pod,将 .spec.os.name 设置为 linux。 对于运行 Windows 容器的 Pod,将 .spec.os.name 设置为 windows

调度器在将 Pod 分配到节点时并不使用 .spec.os.name 的值。 你应该使用正常的 Kubernetes 机制将 Pod 分配给节点, 以确保集群的控制平面将 Pod 放置到运行适当操作系统的节点上。

.spec.os.name 值对 Windows Pod 的调度没有影响, 因此仍然需要污点和容忍以及节点选择器来确保 Windows Pod 落在适当的 Windows 节点。

确保特定于操作系统的工作负载落到合适的容器主机上

用户可以使用污点(Taint)和容忍度(Toleration)确保将 Windows 容器调度至合适的主机上。 现在,所有的 Kubernetes 节点都有以下默认标签:

  • kubernetes.io/os = [windows|linux]
  • kubernetes.io/arch = [amd64|arm64|...]

如果 Pod 规约没有指定像 "kubernetes.io/os": windows 这样的 nodeSelector, 则 Pod 可以被调度到任何主机上,Windows 或 Linux。 这可能会有问题,因为 Windows 容器只能在 Windows 上运行,而 Linux 容器只能在 Linux 上运行。 最佳实践是使用 nodeSelector。

但是,我们了解到,在许多情况下,用户已经预先存在大量 Linux 容器部署, 以及现成配置的生态系统,例如社区中的 Helm Chart 包和程序化的 Pod 生成案例,例如 Operator。 在这些情况下,你可能不愿更改配置来添加节点选择器。 另一种方法是使用污点。因为 kubelet 可以在注册过程中设置污点, 所以可以很容易地修改为,当只能在 Windows 上运行时,自动添加污点。

例如:--register-with-taints='os=windows:NoSchedule'

通过向所有 Windows 节点添加污点,任何负载都不会被调度到这些节点上(包括现有的 Linux Pod)。 为了在 Windows 节点上调度 Windows Pod,它需要 nodeSelector 和匹配合适的容忍度来选择 Windows。

nodeSelector:
    kubernetes.io/os: windows
    node.kubernetes.io/windows-build: '10.0.17763'
tolerations:
    - key: "os"
      operator: "Equal"
      value: "windows"
      effect: "NoSchedule"

处理同一集群中的多个 Windows 版本

每个 Pod 使用的 Windows Server 版本必须与节点的版本匹配。 如果要在同一个集群中使用多个 Windows Server 版本,则应设置额外的节点标签和节点选择器。

Kubernetes 1.17 自动添加了一个新标签 node.kubernetes.io/windows-build 来简化这一点。 如果你运行的是旧版本,则建议手动将此标签添加到 Windows 节点。

此标签反映了需要匹配以实现兼容性的 Windows 主要、次要和内部版本号。 以下是目前用于每个 Windows Server 版本的值。

产品名称构建号
Windows Server 201910.0.17763
Windows Server, Version 20H210.0.19042
Windows Server 202210.0.20348

使用 RuntimeClass 进行简化

RuntimeClass 可用于简化使用污点和容忍度的流程。 集群管理员可以创建一个用于封装这些污点和容忍度的 RuntimeClass 对象。

  1. 将此文件保存到 runtimeClasses.yml。它包括针对 Windows 操作系统、架构和版本的 nodeSelector

    ---
    apiVersion: node.k8s.io/v1
    kind: RuntimeClass
    metadata:
      name: windows-2019
    handler: example-container-runtime-handler
    scheduling:
      nodeSelector:
        kubernetes.io/os: 'windows'
        kubernetes.io/arch: 'amd64'
        node.kubernetes.io/windows-build: '10.0.17763'
      tolerations:
      - effect: NoSchedule
        key: os
        operator: Equal
        value: "windows"
    
  1. 以集群管理员身份运行 kubectl create -f runtimeClasses.yml

  2. 根据情况,向 Pod 规约中添加 runtimeClassName: windows-2019

    例如:

    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: iis-2019
      labels:
        app: iis-2019
    spec:
      replicas: 1
      template:
        metadata:
          name: iis-2019
          labels:
            app: iis-2019
        spec:
          runtimeClassName: windows-2019
          containers:
          - name: iis
            image: mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019
            resources:
              limits:
                cpu: 1
                memory: 800Mi
              requests:
                cpu: .1
                memory: 300Mi
            ports:
              - containerPort: 80
     selector:
        matchLabels:
          app: iis-2019
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: iis
    spec:
      type: LoadBalancer
      ports:
      - protocol: TCP
        port: 80
      selector:
        app: iis-2019
    

3.13 - 扩展 Kubernetes

改变你的 Kubernetes 集群的行为的若干方法。

Kubernetes 是高度可配置且可扩展的。因此,大多数情况下, 你不需要派生自己的 Kubernetes 副本或者向项目代码提交补丁。

本指南描述定制 Kubernetes 的可选方式。主要针对的读者是希望了解如何针对自身工作环境需要来调整 Kubernetes 的集群管理者。 对于那些充当平台开发人员的开发人员或 Kubernetes 项目的贡献者而言, 他们也会在本指南中找到有用的介绍信息,了解系统中存在哪些扩展点和扩展模式, 以及它们所附带的各种权衡和约束等等。

定制化的方法主要可分为配置扩展两种。 前者主要涉及更改命令行参数、本地配置文件或者 API 资源; 后者则需要额外运行一些程序、网络服务或两者。 本文主要关注扩展

配置

配置文件命令参数的说明位于在线文档的参考一节, 每个可执行文件一个页面:

在托管的 Kubernetes 服务中或者受控安装的发行版本中,命令参数和配置文件不总是可以修改的。 即使它们是可修改的,通常其修改权限也仅限于集群操作员。 此外,这些内容在将来的 Kubernetes 版本中很可能发生变化,设置新参数或配置文件可能需要重启进程。 有鉴于此,应该在没有其他替代方案时才会使用这些命令参数和配置文件。

诸如 ResourceQuotaNetworkPolicy 和基于角色的访问控制(RBAC) 等内置策略 API 都是以声明方式配置策略选项的内置 Kubernetes API。 即使在托管的 Kubernetes 服务和受控的 Kubernetes 安装环境中,API 通常也是可用的。 内置策略 API 遵循与 Pod 这类其他 Kubernetes 资源相同的约定。 当你使用稳定版本的策略 API, 它们与其他 Kubernetes API 一样,采纳的是一种预定义的支持策略。 出于以上原因,在条件允许的情况下,基于策略 API 的方案应该优先于配置文件命令参数

扩展

扩展(Extensions)是一些扩充 Kubernetes 能力并与之深度集成的软件组件。 它们调整 Kubernetes 的工作方式使之支持新的类型和新的硬件种类。

大多数集群管理员会使用一种托管的 Kubernetes 服务或者其某种发行版本。 这类集群通常都预先安装了扩展。因此,大多数 Kubernetes 用户不需要安装扩展, 至于需要自己编写新的扩展的情况就更少了。

扩展模式

Kubernetes 从设计上即支持通过编写客户端程序来将其操作自动化。 任何能够对 Kubernetes API 发出读写指令的程序都可以提供有用的自动化能力。 自动化组件可以运行在集群上,也可以运行在集群之外。 通过遵从本文中的指南,你可以编写高度可用的、运行稳定的自动化组件。 自动化组件通常可以用于所有 Kubernetes 集群,包括托管的集群和受控的安装环境。

编写客户端程序有一种特殊的控制器(Controller)模式, 能够与 Kubernetes 很好地协同工作。控制器通常会读取某个对象的 .spec,或许还会执行一些操作, 之后更新对象的 .status

控制器是 Kubernetes API 的客户端。当 Kubernetes 充当客户端且调用某远程服务时, Kubernetes 将此称作 Webhook。该远程服务称作 Webhook 后端。 与定制的控制器相似,Webhook 也会引入失效点(Point of Failure)。

在 Webhook 模型中,Kubernetes 向远程服务发起网络请求。 在另一种称作可执行文件插件(Binary Plugin) 模型中,Kubernetes 执行某个可执行文件(程序)。 这些可执行文件插件由 kubelet(例如,CSI 存储插件CNI 网络插件) 和 kubectl 使用。

扩展点

下图展示了 Kubernetes 集群中的这些扩展点及其访问集群的客户端。

用符号表示的七个编号的 Kubernetes 扩展点

Kubernetes 扩展点

图示要点

  1. 用户通常使用 kubectl 与 Kubernetes API 交互。 插件定制客户端的行为。 有一些通用的扩展可以应用到不同的客户端,还有一些特定的方式可以扩展 kubectl

  2. API 服务器处理所有请求。API 服务器中的几种扩展点能够使用户对请求执行身份认证、 基于其内容阻止请求、编辑请求内容、处理删除操作等等。 这些扩展点在 API 访问扩展节详述。

  3. API 服务器能提供各种类型的资源(Resources) 服务。 诸如 pods内置资源类型是由 Kubernetes 项目所定义的,无法改变。 请查阅 API 扩展了解如何扩展 Kubernetes API。

  1. Kubernetes 调度器负责决定 Pod 要放置到哪些节点上执行。有几种方式来扩展调度行为,这些方法将在调度器扩展节中展开说明。

  2. Kubernetes 中的很多行为都是通过称为控制器(Controller)的程序来实现的, 这些程序也都是 API 服务器的客户端。控制器常常与定制资源结合使用。 进一步了解请查阅结合使用新的 API 与自动化组件更改内置资源

  1. Kubelet 运行在各个服务器(节点)上,帮助 Pod 展现为虚拟的服务器并在集群网络中拥有自己的 IP。 网络插件使得 Kubernetes 能够采用不同实现技术来连接 Pod 网络。

  2. 你可以使用设备插件集成定制硬件或其他专用的节点本地设施, 使得这些设施可用于集群中运行的 Pod。Kubelet 包括了对使用设备插件的支持。

    kubelet 也会为 Pod 及其容器增加或解除的挂载。 你可以使用存储插件增加对新存储类别和其他卷类型的支持。

扩展点选择流程图

如果你无法确定从何处入手,下面的流程图可能对你有些帮助。 注意,某些方案可能需要同时采用几种类型的扩展。

附带使用场景问题和实现指南的流程图。绿圈表示是;红圈表示否。

选择一个扩展方式的流程图指导


客户端扩展

kubectl 所用的插件是单独的二进制文件,用于添加或替换特定子命令的行为。 kubectl 工具还可以与凭据插件集成。 这些扩展只影响单个用户的本地环境,因此不能强制执行站点范围的策略。

如果你要扩展 kubectl 工具,请阅读用插件扩展 kubectl

API 扩展

定制资源对象

如果你想要定义新的控制器、应用配置对象或者其他声明式 API,并且使用 Kubernetes 工具(如 kubectl)来管理它们,可以考虑向 Kubernetes 添加定制资源

关于定制资源的更多信息,可参见定制资源概念指南

API 聚合层

你可以使用 Kubernetes 的 API 聚合层将 Kubernetes API 与其他服务集成,例如指标

结合使用新 API 与自动化组件

定制资源 API 与控制回路的组合称作控制器模式。 如果你的控制器代替人工操作员根据所需状态部署基础设施,那么控制器也可以遵循 Operator 模式。 Operator 模式用于管理特定的应用;通常,这些应用需要维护状态并需要仔细考虑状态的管理方式。

你还可以创建自己的定制 API 和控制回路来管理其他资源(例如存储)或定义策略(例如访问控制限制)。

更改内置资源

当你通过添加定制资源来扩展 Kubernetes 时,所添加的资源总是会被放在一个新的 API 组中。 你不可以替换或更改现有的 API 组。添加新的 API 不会直接让你影响现有 API(如 Pod)的行为,不过 API 访问扩展能够实现这点。

API 访问扩展

当请求到达 Kubernetes API 服务器时,首先要经过身份认证,之后是鉴权操作, 再之后要经过若干类型的准入控制(某些请求实际上未通过身份认证,需要特殊处理)。 参见控制 Kubernetes API 访问以了解此流程的细节。

Kubernetes 身份认证/授权流程中的每个步骤都提供了扩展点。

身份认证

身份认证负责将所有请求中的头部或证书映射到发出该请求的客户端的用户名。

Kubernetes 提供若干内置的身份认证方法。它也可以运行在某种身份认证代理的后面, 并且可以将来自 Authorization: 头部的令牌发送到某个远程服务 (认证 Webhook 来执行验证操作,以备内置方法无法满足你的要求。

鉴权

鉴权操作负责确定特定的用户是否可以读、写 API 资源或对其执行其他操作。此操作仅在整个资源集合的层面进行。 换言之,它不会基于对象的特定字段作出不同的判决。

如果内置的鉴权选项无法满足你的需要, 你可以使用鉴权 Webhook 来调用用户提供的代码,执行定制的鉴权决定。

动态准入控制

请求的鉴权操作结束之后,如果请求的是写操作, 还会经过准入控制处理步骤。 除了内置的处理步骤,还存在一些扩展点:

  • 镜像策略 Webhook 能够限制容器中可以运行哪些镜像。
  • 为了执行任意的准入控制决定, 可以使用一种通用的准入 Webhook 机制。这类准入 Webhook 可以拒绝创建或更新请求。 一些准入 Webhook 会先修改传入的请求数据,才会由 Kubernetes 进一步处理这些传入请求数据。

基础设施扩展

设备插件

设备插件允许一个节点通过设备插件发现新的 Node 资源(除了内置的类似 CPU 和内存这类资源之外)。

存储插件

容器存储接口 (CSI) 插件提供了一种扩展 Kubernetes 的方式使其支持新类别的卷。 这些卷可以由持久的外部存储提供支持,可以提供临时存储,还可以使用文件系统范型为信息提供只读接口。

Kubernetes 还包括对 FlexVolume 插件的支持,该插件自 Kubernetes v1.23 起被弃用(被 CSI 替代)。

FlexVolume 插件允许用户挂载 Kubernetes 本身不支持的卷类型。 当你运行依赖于 FlexVolume 存储的 Pod 时,kubelet 会调用一个二进制插件来挂载该卷。 归档的 FlexVolume 设计提案对此方法有更多详细说明。

Kubernetes 存储供应商的卷插件 FAQ 包含了有关存储插件的通用信息。

网络插件

你的 Kubernetes 集群需要一个网络插件才能拥有一个正常工作的 Pod 网络, 才能支持 Kubernetes 网络模型的其他方面。

网络插件可以让 Kubernetes 使用不同的网络拓扑和技术。

Kubelet 镜像凭据提供程序插件

特性状态: Kubernetes v1.26 [stable]
Kubelet 镜像凭据提供程序是 Kubelet 动态检索镜像仓库凭据的插件。 当你从与配置匹配的容器镜像仓库中拉取镜像时,这些凭据将被使用。

这些插件可以与外部服务通信或使用本地文件来获取凭据。这样,kubelet 就不需要为每个仓库都设置静态凭据,并且可以支持各种身份验证方法和协议。

有关插件配置的详细信息,请参阅 配置 kubelet 镜像凭据提供程序

调度扩展

调度器是一种特殊的控制器,负责监视 Pod 变化并将 Pod 分派给节点。 默认的调度器可以被整体替换掉,同时继续使用其他 Kubernetes 组件。 或者也可以在同一时刻使用多个调度器

这是一项非同小可的任务,几乎绝大多数 Kubernetes 用户都会发现其实他们不需要修改调度器。

你可以控制哪些调度插件处于激活状态, 或将插件集关联到名字不同的调度器配置文件上。 你还可以编写自己的插件,与一个或多个 kube-scheduler 的扩展点集成。

最后,内置的 kube-scheduler 组件支持 Webhook, 从而允许远程 HTTP 后端(调度器扩展)来为 kube-scheduler 选择的 Pod 所在节点执行过滤和优先排序操作。

接下来

3.13.1 - Operator 模式

Operator 是 Kubernetes 的扩展软件, 它利用定制资源管理应用及其组件。 Operator 遵循 Kubernetes 的理念,特别是在控制器方面。

初衷

Operator 模式 旨在记述(正在管理一个或一组服务的)运维人员的关键目标。 这些运维人员负责一些特定的应用和 Service,他们需要清楚地知道系统应该如何运行、如何部署以及出现问题时如何处理。

在 Kubernetes 上运行工作负载的人们都喜欢通过自动化来处理重复的任务。 Operator 模式会封装你编写的(Kubernetes 本身提供功能以外的)任务自动化代码。

Kubernetes 上的 Operator

Kubernetes 为自动化而生。无需任何修改,你即可以从 Kubernetes 核心中获得许多内置的自动化功能。 你可以使用 Kubernetes 自动化部署和运行工作负载,甚至 可以自动化 Kubernetes 自身。

Kubernetes 的 Operator 模式概念允许你在不修改 Kubernetes 自身代码的情况下, 通过为一个或多个自定义资源关联控制器来扩展集群的能力。 Operator 是 Kubernetes API 的客户端, 充当自定义资源的控制器。

Operator 示例

使用 Operator 可以自动化的事情包括:

  • 按需部署应用
  • 获取/还原应用状态的备份
  • 处理应用代码的升级以及相关改动。例如数据库 Schema 或额外的配置设置
  • 发布一个 Service,要求不支持 Kubernetes API 的应用也能发现它
  • 模拟整个或部分集群中的故障以测试其稳定性
  • 在没有内部成员选举程序的情况下,为分布式应用选择首领角色

想要更详细的了解 Operator?下面是一个示例:

  1. 有一个名为 SampleDB 的自定义资源,你可以将其配置到集群中。
  2. 一个包含 Operator 控制器部分的 Deployment,用来确保 Pod 处于运行状态。
  3. Operator 代码的容器镜像。
  4. 控制器代码,负责查询控制平面以找出已配置的 SampleDB 资源。
  5. Operator 的核心是告诉 API 服务器,如何使现实与代码里配置的资源匹配。
    • 如果添加新的 SampleDB,Operator 将设置 PersistentVolumeClaims 以提供持久化的数据库存储, 设置 StatefulSet 以运行 SampleDB,并设置 Job 来处理初始配置。
    • 如果你删除它,Operator 将建立快照,然后确保 StatefulSet 和 Volume 已被删除。
  6. Operator 也可以管理常规数据库的备份。对于每个 SampleDB 资源,Operator 会确定何时创建(可以连接到数据库并进行备份的)Pod。这些 Pod 将依赖于 ConfigMap 和/或具有数据库连接详细信息和凭据的 Secret。
  7. 由于 Operator 旨在为其管理的资源提供强大的自动化功能,因此它还需要一些额外的支持性代码。 在这个示例中,代码将检查数据库是否正运行在旧版本上, 如果是,则创建 Job 对象为你升级数据库。

部署 Operator

部署 Operator 最常见的方法是将自定义资源及其关联的控制器添加到你的集群中。 跟运行容器化应用一样,控制器通常会运行在控制平面之外。 例如,你可以在集群中将控制器作为 Deployment 运行。

使用 Operator

部署 Operator 后,你可以对 Operator 所使用的资源执行添加、修改或删除操作。 按照上面的示例,你将为 Operator 本身建立一个 Deployment,然后:

kubectl get SampleDB                   # 查找所配置的数据库

kubectl edit SampleDB/example-database # 手动修改某些配置

可以了!Operator 会负责应用所作的更改并保持现有服务处于良好的状态。

编写你自己的 Operator

如果生态系统中没有可以实现你目标的 Operator,你可以自己编写代码。

你还可以使用任何支持 Kubernetes API 客户端的语言或运行时来实现 Operator(即控制器)。

以下是一些库和工具,你可用于编写自己的云原生 Operator。

接下来

3.13.2 - 计算、存储和网络扩展

本节介绍不属于 Kubernetes 本身组成部分的一些集群扩展。 你可以使用这些扩展来增强集群中的节点,或者提供将 Pod 关联在一起的网络结构。

  • CSIFlexVolume 存储插件

    容器存储接口 (CSI) 插件提供了一种扩展 Kubernetes 的方式使其支持新类别的卷。 这些卷可以由持久的外部存储提供支持,可以提供临时存储,还可以使用文件系统范型为信息提供只读接口。

    Kubernetes 还包括对 FlexVolume 插件的扩展支持,该插件自 Kubernetes v1.23 起被弃用(被 CSI 替代)。

    FlexVolume 插件允许用户挂载 Kubernetes 本身不支持的卷类型。 当你运行依赖于 FlexVolume 存储的 Pod 时,kubelet 会调用一个二进制插件来挂载该卷。 归档的 FlexVolume 设计提案对此方法有更多详细说明。

    Kubernetes 存储供应商的卷插件 FAQ 包含了有关存储插件的通用信息。

  • 设备插件

    设备插件允许一个节点发现新的 Node 设施(除了 cpumemory 等内置的节点资源之外), 并向请求资源的 Pod 提供了这些自定义的节点本地设施。

  • 网络插件

    网络插件可以让 Kubernetes 使用不同的网络拓扑和技术。 你的 Kubernetes 集群需要一个 网络插件 才能拥有一个正常工作的 Pod 网络, 才能支持 Kubernetes 网络模型的其他方面。

    Kubernetes 1.30 兼容 CNI 网络插件。

3.13.2.1 - 网络插件

Kubernetes 1.30 支持用于集群联网的容器网络接口 (CNI) 插件。 你必须使用和你的集群相兼容并且满足你的需求的 CNI 插件。 在更广泛的 Kubernetes 生态系统中你可以使用不同的插件(开源和闭源)。

要实现 Kubernetes 网络模型,你需要一个 CNI 插件。

你必须使用与 v0.4.0 或更高版本的 CNI 规范相符合的 CNI 插件。 Kubernetes 推荐使用一个兼容 v1.0.0 CNI 规范的插件(插件可以兼容多个规范版本)。

安装

在网络语境中,容器运行时(Container Runtime)是在节点上的守护进程, 被配置用来为 kubelet 提供 CRI 服务。具体而言,容器运行时必须配置为加载所需的 CNI 插件,从而实现 Kubernetes 网络模型。

要了解容器运行时如何管理 CNI 插件的具体信息,可参见对应容器运行时的文档,例如:

要了解如何安装和管理 CNI 插件的具体信息,可参阅对应的插件或 网络驱动(Networking Provider) 的文档。

网络插件要求

对于插件开发人员以及时常会构建并部署 Kubernetes 的用户而言, 插件可能也需要特定的配置来支持 kube-proxy。 iptables 代理依赖于 iptables,插件可能需要确保 iptables 能够监控容器的网络通信。 例如,如果插件将容器连接到 Linux 网桥,插件必须将 net/bridge/bridge-nf-call-iptables sysctl 参数设置为 1,以确保 iptables 代理正常工作。 如果插件不使用 Linux 网桥,而是使用类似于 Open vSwitch 或者其它一些机制, 它应该确保为代理对容器通信执行正确的路由。

默认情况下,如果未指定 kubelet 网络插件,则使用 noop 插件, 该插件设置 net/bridge/bridge-nf-call-iptables=1,以确保简单的配置 (如带网桥的 Docker)与 iptables 代理正常工作。

本地回路 CNI

除了安装到节点上用于实现 Kubernetes 网络模型的 CNI 插件外,Kubernetes 还需要容器运行时提供一个本地回路接口 lo,用于各个沙箱(Pod 沙箱、虚机沙箱……)。 实现本地回路接口的工作可以通过复用 CNI 本地回路插件来实现, 也可以通过开发自己的代码来实现 (参阅 CRI-O 中的示例)。

支持 hostPort

CNI 网络插件支持 hostPort。你可以使用官方 portmap 插件,它由 CNI 插件团队提供,或者使用你自己的带有 portMapping 功能的插件。

如果你想要启动 hostPort 支持,则必须在 cni-conf-dir 指定 portMappings capability。 例如:

{
  "name": "k8s-pod-network",
  "cniVersion": "0.4.0",
  "plugins": [
    {
      "type": "calico",
      "log_level": "info",
      "datastore_type": "kubernetes",
      "nodename": "127.0.0.1",
      "ipam": {
        "type": "host-local",
        "subnet": "usePodCidr"
      },
      "policy": {
        "type": "k8s"
      },
      "kubernetes": {
        "kubeconfig": "/etc/cni/net.d/calico-kubeconfig"
      }
    },
    {
      "type": "portmap",
      "capabilities": {"portMappings": true},
      "externalSetMarkChain": "KUBE-MARK-MASQ"
    }
  ]
}

支持流量整形

实验功能

CNI 网络插件还支持 Pod 入站和出站流量整形。 你可以使用 CNI 插件团队提供的 bandwidth 插件,也可以使用你自己的具有带宽控制功能的插件。

如果你想要启用流量整形支持,你必须将 bandwidth 插件添加到 CNI 配置文件 (默认是 /etc/cni/net.d)并保证该可执行文件包含在你的 CNI 的 bin 文件夹内 (默认为 /opt/cni/bin)。

{
  "name": "k8s-pod-network",
  "cniVersion": "0.4.0",
  "plugins": [
    {
      "type": "calico",
      "log_level": "info",
      "datastore_type": "kubernetes",
      "nodename": "127.0.0.1",
      "ipam": {
        "type": "host-local",
        "subnet": "usePodCidr"
      },
      "policy": {
        "type": "k8s"
      },
      "kubernetes": {
        "kubeconfig": "/etc/cni/net.d/calico-kubeconfig"
      }
    },
    {
      "type": "bandwidth",
      "capabilities": {"bandwidth": true}
    }
  ]
}

现在,你可以将 kubernetes.io/ingress-bandwidthkubernetes.io/egress-bandwidth 注解添加到 Pod 中。例如:

apiVersion: v1
kind: Pod
metadata:
  annotations:
    kubernetes.io/ingress-bandwidth: 1M
    kubernetes.io/egress-bandwidth: 1M
...

接下来

3.13.2.2 - 设备插件

设备插件可以让你配置集群以支持需要特定于供应商设置的设备或资源,例如 GPU、NIC、FPGA 或非易失性主存储器。
特性状态: Kubernetes v1.26 [stable]

Kubernetes 提供了一个设备插件框架,你可以用它来将系统硬件资源发布到 Kubelet

供应商可以实现设备插件,由你手动部署或作为 DaemonSet 来部署,而不必定制 Kubernetes 本身的代码。目标设备包括 GPU、高性能 NIC、FPGA、 InfiniBand 适配器以及其他类似的、可能需要特定于供应商的初始化和设置的计算资源。

注册设备插件

kubelet 提供了一个 Registration 的 gRPC 服务:

service Registration {
	rpc Register(RegisterRequest) returns (Empty) {}
}

设备插件可以通过此 gRPC 服务在 kubelet 进行注册。在注册期间,设备插件需要发送下面几样内容:

  • 设备插件的 Unix 套接字。
  • 设备插件的 API 版本。
  • ResourceName 是需要公布的。这里 ResourceName 需要遵循扩展资源命名方案, 类似于 vendor-domain/resourcetype。(比如 NVIDIA GPU 就被公布为 nvidia.com/gpu。)

成功注册后,设备插件就向 kubelet 发送它所管理的设备列表,然后 kubelet 负责将这些资源发布到 API 服务器,作为 kubelet 节点状态更新的一部分。

比如,设备插件在 kubelet 中注册了 hardware-vendor.example/foo 并报告了节点上的两个运行状况良好的设备后,节点状态将更新以通告该节点已安装 2 个 "Foo" 设备并且是可用的。

然后,用户可以请求设备作为 Pod 规范的一部分, 参见 Container。 请求扩展资源类似于管理请求和限制的方式, 其他资源,有以下区别:

  • 扩展资源仅可作为整数资源使用,并且不能被过量使用
  • 设备不能在容器之间共享

示例

假设 Kubernetes 集群正在运行一个设备插件,该插件在一些节点上公布的资源为 hardware-vendor.example/foo。 下面就是一个 Pod 示例,请求此资源以运行一个工作负载的示例:

---
apiVersion: v1
kind: Pod
metadata:
  name: demo-pod
spec:
  containers:
    - name: demo-container-1
      image: registry.k8s.io/pause:2.0
      resources:
        limits:
          hardware-vendor.example/foo: 2
#
# 这个 pod 需要两个 hardware-vendor.example/foo 设备
# 而且只能够调度到满足需求的节点上
#
# 如果该节点中有 2 个以上的设备可用,其余的可供其他 Pod 使用

设备插件的实现

设备插件的常规工作流程包括以下几个步骤:

  1. 初始化。在这个阶段,设备插件将执行特定于供应商的初始化和设置,以确保设备处于就绪状态。

  2. 插件使用主机路径 /var/lib/kubelet/device-plugins/ 下的 UNIX 套接字启动一个 gRPC 服务,该服务实现以下接口:

    service DevicePlugin {
       // GetDevicePluginOptions 返回与设备管理器沟通的选项。
       rpc GetDevicePluginOptions(Empty) returns (DevicePluginOptions) {}
    
       // ListAndWatch 返回 Device 列表构成的数据流。
       // 当 Device 状态发生变化或者 Device 消失时,ListAndWatch
       // 会返回新的列表。
       rpc ListAndWatch(Empty) returns (stream ListAndWatchResponse) {}
    
       // Allocate 在容器创建期间调用,这样设备插件可以运行一些特定于设备的操作,
       // 并告诉 kubelet 如何令 Device 可在容器中访问的所需执行的具体步骤
       rpc Allocate(AllocateRequest) returns (AllocateResponse) {}
    
       // GetPreferredAllocation 从一组可用的设备中返回一些优选的设备用来分配,
       // 所返回的优选分配结果不一定会是设备管理器的最终分配方案。
       // 此接口的设计仅是为了让设备管理器能够在可能的情况下做出更有意义的决定。
       rpc GetPreferredAllocation(PreferredAllocationRequest) returns (PreferredAllocationResponse) {}
    
       // PreStartContainer 在设备插件注册阶段根据需要被调用,调用发生在容器启动之前。
       // 在将设备提供给容器使用之前,设备插件可以运行一些诸如重置设备之类的特定于
       // 具体设备的操作,
       rpc PreStartContainer(PreStartContainerRequest) returns (PreStartContainerResponse) {}
    }
    
  1. 插件通过位于主机路径 /var/lib/kubelet/device-plugins/kubelet.sock 下的 UNIX 套接字向 kubelet 注册自身。

  1. 成功注册自身后,设备插件将以提供服务的模式运行,在此期间,它将持续监控设备运行状况, 并在设备状态发生任何变化时向 kubelet 报告。它还负责响应 Allocate gRPC 请求。 在 Allocate 期间,设备插件可能还会做一些特定于设备的准备;例如 GPU 清理或 QRNG 初始化。 如果操作成功,则设备插件将返回 AllocateResponse,其中包含用于访问被分配的设备容器运行时的配置。 kubelet 将此信息传递到容器运行时。

    AllocateResponse 包含零个或多个 ContainerAllocateResponse 对象。 设备插件在这些对象中给出为了访问设备而必须对容器定义所进行的修改。 这些修改包括:

    • 注解
    • 设备节点
    • 环境变量
    • 挂载点
    • 完全限定的 CDI 设备名称

处理 kubelet 重启

设备插件应能监测到 kubelet 重启,并且向新的 kubelet 实例来重新注册自己。 新的 kubelet 实例启动时会删除 /var/lib/kubelet/device-plugins 下所有已经存在的 Unix 套接字。 设备插件需要能够监控到它的 Unix 套接字被删除,并且当发生此类事件时重新注册自己。

设备插件部署

你可以将你的设备插件作为节点操作系统的软件包来部署、作为 DaemonSet 来部署或者手动部署。

规范目录 /var/lib/kubelet/device-plugins 是需要特权访问的, 所以设备插件必须要在被授权的安全的上下文中运行。 如果你将设备插件部署为 DaemonSet,/var/lib/kubelet/device-plugins 目录必须要在插件的 PodSpec 中声明作为 卷(Volume) 被挂载到插件中。

如果你选择 DaemonSet 方法,你可以通过 Kubernetes 进行以下操作: 将设备插件的 Pod 放置在节点上,在出现故障后重新启动守护进程 Pod,来进行自动升级。

API 兼容性

之前版本控制方案要求设备插件的 API 版本与 Kubelet 的版本完全匹配。 自从此特性在 v1.12 中进阶为 Beta 后,这不再是硬性要求。 API 是版本化的,并且自此特性进阶 Beta 后一直表现稳定。 因此,kubelet 升级应该是无缝的,但在稳定之前 API 仍然可能会有变更,还不能保证升级不会中断。

作为一个项目,Kubernetes 建议设备插件开发者:

  • 注意未来版本中设备插件 API 的变更。
  • 支持多个版本的设备插件 API,以实现向后/向前兼容性。

若在需要升级到具有较新设备插件 API 版本的某个 Kubernetes 版本的节点上运行这些设备插件, 请在升级这些节点之前先升级设备插件以支持这两个版本。 采用该方法将确保升级期间设备分配的连续运行。

监控设备插件资源

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

为了监控设备插件提供的资源,监控代理程序需要能够发现节点上正在使用的设备, 并获取元数据来描述哪个指标与容器相关联。 设备监控代理暴露给 Prometheus 的指标应该遵循 Kubernetes Instrumentation Guidelines, 使用 podnamespacecontainer 标签来标识容器。

kubelet 提供了 gRPC 服务来使得正在使用中的设备被发现,并且还为这些设备提供了元数据:

// PodResourcesLister 是一个由 kubelet 提供的服务,用来提供供节点上
// Pod 和容器使用的节点资源的信息
service PodResourcesLister {
    rpc List(ListPodResourcesRequest) returns (ListPodResourcesResponse) {}
    rpc GetAllocatableResources(AllocatableResourcesRequest) returns (AllocatableResourcesResponse) {}
    rpc Get(GetPodResourcesRequest) returns (GetPodResourcesResponse) {}
}

List gRPC 端点

这一 List 端点提供运行中 Pod 的资源信息,包括类似独占式分配的 CPU ID、设备插件所报告的设备 ID 以及这些设备分配所处的 NUMA 节点 ID。 此外,对于基于 NUMA 的机器,它还会包含为容器保留的内存和大页的信息。

从 Kubernetes v1.27 开始,List 端点可以通过 DynamicResourceAllocation API 提供在 ResourceClaims 中分配的当前运行 Pod 的资源信息。 要启用此特性,必须使用以下标志启动 kubelet

--feature-gates=DynamicResourceAllocation=true,KubeletPodResourcesDynamicResources=true
// ListPodResourcesResponse 是 List 函数的响应
message ListPodResourcesResponse {
    repeated PodResources pod_resources = 1;
}

// PodResources 包含关于分配给 Pod 的节点资源的信息
message PodResources {
    string name = 1;
    string namespace = 2;
    repeated ContainerResources containers = 3;
}

// ContainerResources 包含分配给容器的资源的信息
message ContainerResources {
    string name = 1;
    repeated ContainerDevices devices = 2;
    repeated int64 cpu_ids = 3;
    repeated ContainerMemory memory = 4;
    repeated DynamicResource dynamic_resources = 5;
}

// ContainerMemory 包含分配给容器的内存和大页信息
message ContainerMemory {
    string memory_type = 1;
    uint64 size = 2;
    TopologyInfo topology = 3;
}

// Topology 描述资源的硬件拓扑结构
message TopologyInfo {
        repeated NUMANode nodes = 1;
}

// NUMA 代表的是 NUMA 节点
message NUMANode {
        int64 ID = 1;
}

// ContainerDevices 包含分配给容器的设备信息
message ContainerDevices {
    string resource_name = 1;
    repeated string device_ids = 2;
    TopologyInfo topology = 3;
}

// DynamicResource 包含通过 Dynamic Resource Allocation 分配到容器的设备信息
message DynamicResource {
    string class_name = 1;
    string claim_name = 2;
    string claim_namespace = 3;
    repeated ClaimResource claim_resources = 4;
}

// ClaimResource 包含每个插件的资源信息
message ClaimResource {
    repeated CDIDevice cdi_devices = 1 [(gogoproto.customname) = "CDIDevices"];
}

// CDIDevice 指定 CDI 设备信息
message CDIDevice {
    // 完全合格的 CDI 设备名称
    // 例如:vendor.com/gpu=gpudevice1
    // 参阅 CDI 规范中的更多细节:
    // https://github.com/container-orchestrated-devices/container-device-interface/blob/main/SPEC.md
    string name = 1;
}

GetAllocatableResources gRPC 端点

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

端点 GetAllocatableResources 提供工作节点上原始可用的资源信息。 此端点所提供的信息比导出给 API 服务器的信息更丰富。

// AllocatableResourcesResponses 包含 kubelet 所了解到的所有设备的信息
message AllocatableResourcesResponse {
    repeated ContainerDevices devices = 1;
    repeated int64 cpu_ids = 2;
    repeated ContainerMemory memory = 3;
}

ContainerDevices 会向外提供各个设备所隶属的 NUMA 单元这类拓扑信息。 NUMA 单元通过一个整数 ID 来标识,其取值与设备插件所报告的一致。 设备插件注册到 kubelet 时 会报告这类信息。

gRPC 服务通过 /var/lib/kubelet/pod-resources/kubelet.sock 的 UNIX 套接字来提供服务。 设备插件资源的监控代理程序可以部署为守护进程或者 DaemonSet。 规范的路径 /var/lib/kubelet/pod-resources 需要特权来进入, 所以监控代理程序必须要在获得授权的安全的上下文中运行。 如果设备监控代理以 DaemonSet 形式运行,必须要在插件的 PodSpec 中声明将 /var/lib/kubelet/pod-resources 目录以的形式被挂载到设备监控代理中。

Get gRPC 端点

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

Get 端点提供了当前运行 Pod 的资源信息。它会暴露与 List 端点中所述类似的信息。 Get 端点需要当前运行 Pod 的 PodNamePodNamespace

// GetPodResourcesRequest 包含 Pod 相关信息
message GetPodResourcesRequest {
    string pod_name = 1;
    string pod_namespace = 2;
}

要启用此特性,你必须使用以下标志启动 kubelet 服务:

--feature-gates=KubeletPodResourcesGet=true

Get 端点可以提供与动态资源分配 API 所分配的动态资源相关的 Pod 信息。 要启用此特性,你必须确保使用以下标志启动 kubelet 服务:

--feature-gates=KubeletPodResourcesGet=true,DynamicResourceAllocation=true,KubeletPodResourcesDynamicResources=true

设备插件与拓扑管理器的集成

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

拓扑管理器是 Kubelet 的一个组件,它允许以拓扑对齐方式来调度资源。 为了做到这一点,设备插件 API 进行了扩展来包括一个 TopologyInfo 结构体。

message TopologyInfo {
    repeated NUMANode nodes = 1;
}

message NUMANode {
    int64 ID = 1;
}

设备插件希望拓扑管理器可以将填充的 TopologyInfo 结构体作为设备注册的一部分以及设备 ID 和设备的运行状况发送回去。然后设备管理器将使用此信息来咨询拓扑管理器并做出资源分配决策。

TopologyInfo 支持将 nodes 字段设置为 nil 或一个 NUMA 节点的列表。 这样就可以使设备插件通告跨越多个 NUMA 节点的设备。

TopologyInfo 设置为 nil 或为给定设备提供一个空的 NUMA 节点列表表示设备插件没有该设备的 NUMA 亲和偏好。

下面是一个由设备插件为设备填充 TopologyInfo 结构体的示例:

pluginapi.Device{ID: "25102017", Health: pluginapi.Healthy, Topology:&pluginapi.TopologyInfo{Nodes: []*pluginapi.NUMANode{&pluginapi.NUMANode{ID: 0,},}}}

设备插件示例

下面是一些设备插件实现的示例:

接下来

3.13.3 - 扩展 Kubernetes API

3.13.3.1 - 定制资源

定制资源(Custom Resource) 是对 Kubernetes API 的扩展。 本页讨论何时向 Kubernetes 集群添加定制资源,何时使用独立的服务。 本页描述添加定制资源的两种方法以及怎样在二者之间做出抉择。

定制资源

资源(Resource)Kubernetes API 中的一个端点, 其中存储的是某个类别的 API 对象的一个集合。 例如内置的 Pod 资源包含一组 Pod 对象。

定制资源(Custom Resource) 是对 Kubernetes API 的扩展,不一定在默认的 Kubernetes 安装中就可用。定制资源所代表的是对特定 Kubernetes 安装的一种定制。 不过,很多 Kubernetes 核心功能现在都用定制资源来实现,这使得 Kubernetes 更加模块化。

定制资源可以通过动态注册的方式在运行中的集群内或出现或消失,集群管理员可以独立于集群更新定制资源。 一旦某定制资源被安装,用户可以使用 kubectl 来创建和访问其中的对象,就像他们为 Pod 这种内置资源所做的一样。

定制控制器

就定制资源本身而言,它只能用来存取结构化的数据。 当你将定制资源与定制控制器(Custom Controller) 结合时, 定制资源就能够提供真正的声明式 API(Declarative API)

Kubernetes 声明式 API 强制对职权做了一次分离操作。 你声明所用资源的期望状态,而 Kubernetes 控制器使 Kubernetes 对象的当前状态与你所声明的期望状态保持同步。 声明式 API 的这种机制与命令式 API(你指示服务器要做什么,服务器就去做什么)形成鲜明对比。

你可以在一个运行中的集群上部署和更新定制控制器,这类操作与集群的生命周期无关。 定制控制器可以用于任何类别的资源,不过它们与定制资源结合起来时最为有效。 Operator 模式就是将定制资源与定制控制器相结合的。 你可以使用定制控制器来将特定于某应用的领域知识组织起来,以编码的形式构造对 Kubernetes API 的扩展。

我是否应该向我的 Kubernetes 集群添加定制资源?

在创建新的 API 时, 请考虑是将你的 API 与 Kubernetes 集群 API 聚合起来, 还是让你的 API 独立运行。

考虑 API 聚合的情况优选独立 API 的情况
你的 API 是声明式的你的 API 不符合声明式模型。
你希望可以是使用 kubectl 来读写你的新资源类别。不要求 kubectl 支持。
你希望在 Kubernetes UI (如仪表板)中和其他内置类别一起查看你的新资源类别。不需要 Kubernetes UI 支持。
你在开发新的 API。你已经有一个提供 API 服务的程序并且工作良好。
你有意愿取接受 Kubernetes 对 REST 资源路径所作的格式限制,例如 API 组和名字空间。(参阅 API 概述你需要使用一些特殊的 REST 路径以便与已经定义的 REST API 保持兼容。
你的资源可以自然地界定为集群作用域或集群中某个名字空间作用域。集群作用域或名字空间作用域这种二分法很不合适;你需要对资源路径的细节进行控制。
你希望复用 Kubernetes API 支持特性你不需要这类特性。

声明式 API

典型地,在声明式 API 中:

  • 你的 API 包含相对而言为数不多的、尺寸较小的对象(资源)。
  • 对象定义了应用或者基础设施的配置信息。
  • 对象更新操作频率较低。
  • 通常需要人来读取或写入对象。
  • 对象的主要操作是 CRUD 风格的(创建、读取、更新和删除)。
  • 不需要跨对象的事务支持:API 对象代表的是期望状态而非确切实际状态。

命令式 API(Imperative API)与声明式有所不同。 以下迹象表明你的 API 可能不是声明式的:

  • 客户端发出“做这个操作”的指令,之后在该操作结束时获得同步响应。
  • 客户端发出“做这个操作”的指令,并获得一个操作 ID,之后需要检查一个 Operation(操作) 对象来判断请求是否成功完成。
  • 你会将你的 API 类比为远程过程调用(Remote Procedure Call,RPC)。
  • 直接存储大量数据;例如每个对象几 kB,或者存储上千个对象。
  • 需要较高的访问带宽(长期保持每秒数十个请求)。
  • 存储有应用来处理的最终用户数据(如图片、个人标识信息(PII)等)或者其他大规模数据。
  • 在对象上执行的常规操作并非 CRUD 风格。
  • API 不太容易用对象来建模。
  • 你决定使用操作 ID 或者操作对象来表现悬决的操作。

我应该使用一个 ConfigMap 还是一个定制资源?

如果满足以下条件之一,应该使用 ConfigMap:

  • 存在一个已有的、文档完备的配置文件格式约定,例如 mysql.cnfpom.xml
  • 你希望将整个配置文件放到某 configMap 中的一个主键下面。
  • 配置文件的主要用途是针对运行在集群中 Pod 内的程序,供后者依据文件数据配置自身行为。
  • 文件的使用者期望以 Pod 内文件或者 Pod 内环境变量的形式来使用文件数据, 而不是通过 Kubernetes API。
  • 你希望当文件被更新时通过类似 Deployment 之类的资源完成滚动更新操作。

如果以下条件中大多数都被满足,你应该使用定制资源(CRD 或者 聚合 API):

  • 你希望使用 Kubernetes 客户端库和 CLI 来创建和更改新的资源。
  • 你希望 kubectl 能够直接支持你的资源;例如,kubectl get my-object object-name
  • 你希望构造新的自动化机制,监测新对象上的更新事件,并对其他对象执行 CRUD 操作,或者监测后者更新前者。
  • 你希望编写自动化组件来处理对对象的更新。
  • 你希望使用 Kubernetes API 对诸如 .spec.status.metadata 等字段的约定。
  • 你希望对象是对一组受控资源的抽象,或者对其他资源的归纳提炼。

添加定制资源

Kubernetes 提供了两种方式供你向集群中添加定制资源:

  • CRD 相对简单,创建 CRD 可以不必编程。
  • API 聚合需要编程, 但支持对 API 行为进行更多的控制,例如数据如何存储以及在不同 API 版本间如何转换等。

Kubernetes 提供这两种选项以满足不同用户的需求,这样就既不会牺牲易用性也不会牺牲灵活性。

聚合 API 指的是一些下位的 API 服务器,运行在主 API 服务器后面;主 API 服务器以代理的方式工作。这种组织形式称作 API 聚合(API Aggregation,AA) 。 对用户而言,看起来仅仅是 Kubernetes API 被扩展了。

CRD 允许用户创建新的资源类别同时又不必添加新的 API 服务器。 使用 CRD 时,你并不需要理解 API 聚合。

无论以哪种方式安装定制资源,新的资源都会被当做定制资源,以便与内置的 Kubernetes 资源(如 Pods)相区分。

CustomResourceDefinitions

CustomResourceDefinition API 资源允许你定义定制资源。 定义 CRD 对象的操作会使用你所设定的名字和模式定义(Schema)创建一个新的定制资源, Kubernetes API 负责为你的定制资源提供存储和访问服务。 CRD 对象的名称必须是合法的 DNS 子域名

CRD 使得你不必编写自己的 API 服务器来处理定制资源,不过其背后实现的通用性也意味着你所获得的灵活性要比 API 服务器聚合少很多。

关于如何注册新的定制资源、使用新资源类别的实例以及如何使用控制器来处理事件, 相关的例子可参见定制控制器示例

API 服务器聚合

通常,Kubernetes API 中的每个资源都需要处理 REST 请求和管理对象持久性存储的代码。 Kubernetes API 主服务器能够处理诸如 PodService 这些内置资源, 也可以按通用的方式通过 CRD 来处理定制资源。

聚合层(Aggregation Layer) 使得你可以通过编写和部署你自己的 API 服务器来为定制资源提供特殊的实现。 主 API 服务器将针对你要处理的定制资源的请求全部委托给你自己的 API 服务器来处理, 同时将这些资源提供给其所有客户端。

选择添加定制资源的方法

CRD 更为易用;聚合 API 则更为灵活。请选择最符合你的需要的方法。

通常,如果存在以下情况,CRD 可能更合适:

  • 定制资源的字段不多;
  • 你在组织内部使用该资源或者在一个小规模的开源项目中使用该资源,而不是在商业产品中使用。

比较易用性

CRD 比聚合 API 更容易创建。

CRD聚合 API
无需编程。用户可选择任何语言来实现 CRD 控制器。需要编程,并构建可执行文件和镜像。
无需额外运行服务;CRD 由 API 服务器处理。需要额外创建服务,且该服务可能失效。
一旦 CRD 被创建,不需要持续提供支持。Kubernetes 主控节点升级过程中自动会带入缺陷修复。可能需要周期性地从上游提取缺陷修复并更新聚合 API 服务器。
无需处理 API 的多个版本;例如,当你控制资源的客户端时,你可以更新它使之与 API 同步。你需要处理 API 的多个版本;例如,在开发打算与很多人共享的扩展时。

高级特性与灵活性

聚合 API 可提供更多的高级 API 特性,也可对其他特性实行定制;例如,对存储层进行定制。

特性描述CRD聚合 API
合法性检查帮助用户避免错误,允许你独立于客户端版本演化 API。这些特性对于由很多无法同时更新的客户端的场合。可以。大多数验证可以使用 OpenAPI v3.0 合法性检查 来设定。CRDValidationRatcheting 特性门控允许在资源的失败部分未发生变化的情况下,忽略 OpenAPI 指定的失败验证。其他合法性检查操作可以通过添加合法性检查 Webhook来实现。可以,可执行任何合法性检查。
默认值设置同上可以。可通过 OpenAPI v3.0 合法性检查default 关键词(自 1.17 正式发布)或更改性(Mutating)Webhook来实现(不过从 etcd 中读取老的对象时不会执行这些 Webhook)。可以。
多版本支持允许通过两个 API 版本同时提供同一对象。可帮助简化类似字段更名这类 API 操作。如果你能控制客户端版本,这一特性将不再重要。可以可以。
定制存储支持使用具有不同性能模式的存储(例如,要使用时间序列数据库而不是键值存储),或者因安全性原因对存储进行隔离(例如对敏感信息执行加密)。不可以。可以。
定制业务逻辑在创建、读取、更新或删除对象时,执行任意的检查或操作。可以。要使用 Webhook可以。
支持 scale 子资源允许 HorizontalPodAutoscaler 和 PodDisruptionBudget 这类子系统与你的新资源交互。可以可以。
支持 status 子资源允许在用户写入 spec 部分而控制器写入 status 部分时执行细粒度的访问控制。允许在对定制资源的数据进行更改时增加对象的代际(Generation);这需要资源对 spec 和 status 部分有明确划分。可以可以。
其他子资源添加 CRUD 之外的操作,例如 "logs" 或 "exec"。不可以。可以。
strategic-merge-patch新的端点要支持标记了 Content-Type: application/strategic-merge-patch+json 的 PATCH 操作。对于更新既可在本地更改也可在服务器端更改的对象而言是有用的。要了解更多信息,可参见使用 kubectl patch 来更新 API 对象不可以。可以。
支持协议缓冲区新的资源要支持想要使用协议缓冲区(Protocol Buffer)的客户端。不可以。可以。
OpenAPI Schema是否存在新资源类别的 OpenAPI(Swagger)Schema 可供动态从服务器上读取?是否存在机制确保只能设置被允许的字段以避免用户犯字段拼写错误?是否实施了字段类型检查(换言之,不允许在 string 字段设置 int 值)?可以,依据 OpenAPI v3.0 合法性检查 模式(1.16 中进入正式发布状态)。可以。

公共特性

与在 Kubernetes 平台之外实现定制资源相比, 无论是通过 CRD 还是通过聚合 API 来创建定制资源,你都会获得很多 API 特性:

功能特性具体含义
CRUD新的端点支持通过 HTTP 和 kubectl 发起的 CRUD 基本操作
监测(Watch)新的端点支持通过 HTTP 发起的 Kubernetes Watch 操作
发现(Discovery)类似 kubectl 和仪表盘(Dashboard)这类客户端能够自动提供列举、显示、在字段级编辑你的资源的操作
json-patch新的端点支持带 Content-Type: application/json-patch+json 的 PATCH 操作
merge-patch新的端点支持带 Content-Type: application/merge-patch+json 的 PATCH 操作
HTTPS新的端点使用 HTTPS
内置身份认证对扩展的访问会使用核心 API 服务器(聚合层)来执行身份认证操作
内置鉴权授权对扩展的访问可以复用核心 API 服务器所使用的鉴权授权机制;例如,RBAC
Finalizers在外部清除工作结束之前阻止扩展资源被删除
准入 Webhooks在创建、更新和删除操作中对扩展资源设置默认值和执行合法性检查
UI/CLI 展示kubectl 和仪表盘(Dashboard)可以显示扩展资源
区分未设置值和空值客户端能够区分哪些字段是未设置的,哪些字段的值是被显式设置为零值的
生成客户端库Kubernetes 提供通用的客户端库,以及用来生成特定类别客户端库的工具
标签和注解提供涵盖所有对象的公共元数据结构,且工具知晓如何编辑核心资源和定制资源的这些元数据

准备安装定制资源

在向你的集群添加定制资源之前,有些事情需要搞清楚。

第三方代码和新的失效点的问题

尽管添加新的 CRD 不会自动带来新的失效点(Point of Failure),例如导致第三方代码被在 API 服务器上运行, 类似 Helm Charts 这种软件包或者其他安装包通常在提供 CRD 的同时还包含带有第三方代码的 Deployment,负责实现新的定制资源的业务逻辑。

安装聚合 API 服务器时,也总会牵涉到运行一个新的 Deployment。

存储

定制资源和 ConfigMap 一样也会消耗存储空间。创建过多的定制资源可能会导致 API 服务器上的存储空间超载。

聚合 API 服务器可以使用主 API 服务器相同的存储。如果是这样,你也要注意此警告。

身份认证、鉴权授权以及审计

CRD 通常与 API 服务器上的内置资源一样使用相同的身份认证、鉴权授权和审计日志机制。

如果你使用 RBAC 来执行鉴权授权,大多数 RBAC 角色都不会授权对新资源的访问 (除了 cluster-admin 角色以及使用通配符规则创建的其他角色)。 你要显式地为新资源的访问授权。CRD 和聚合 API 通常在交付时会包含针对所添加的类别的新的角色定义。

聚合 API 服务器可能会使用主 API 服务器相同的身份认证、鉴权授权和审计机制,也可能不会。

访问定制资源

Kubernetes 客户端库可用来访问定制资源。 并非所有客户端库都支持定制资源。GoPython 客户端库是支持的。

当你添加了新的定制资源后,可以用如下方式之一访问它们:

  • kubectl
  • Kubernetes 动态客户端
  • 你所编写的 REST 客户端
  • 使用 Kubernetes 客户端生成工具所生成的客户端。 生成客户端的工作有些难度,不过某些项目可能会随着 CRD 或聚合 API 一起提供一个客户端。

接下来

3.13.3.2 - Kubernetes API 聚合层

使用聚合层(Aggregation Layer),用户可以通过附加的 API 扩展 Kubernetes, 而不局限于 Kubernetes 核心 API 提供的功能。 这里的附加 API 可以是现成的解决方案,比如 metrics server, 或者你自己开发的 API。

聚合层不同于 定制资源(Custom Resources)。 后者的目的是让 kube-apiserver 能够识别新的对象类别(Kind)。

聚合层

聚合层在 kube-apiserver 进程内运行。在扩展资源注册之前,聚合层不做任何事情。 要注册 API,你可以添加一个 APIService 对象,用它来 “申领” Kubernetes API 中的 URL 路径。 自此以后,聚合层将把发给该 API 路径的所有内容(例如 /apis/myextension.mycompany.io/v1/…) 转发到已注册的 APIService。

APIService 的最常见实现方式是在集群中某 Pod 内运行扩展 API 服务器(Extension API Server)。 如果你在使用扩展 API 服务器来管理集群中的资源,该扩展 API 服务器(也被写成 "extension-apiserver") 一般需要和一个或多个控制器一起使用。 apiserver-builder 库同时提供构造扩展 API 服务器和控制器框架代码。

响应延迟

扩展 API 服务器(Extension API Server)与 kube-apiserver 之间需要存在低延迟的网络连接。 发现请求需要在五秒钟或更短的时间内完成到 kube-apiserver 的往返。

如果你的扩展 API 服务器无法满足这一延迟要求,应考虑如何更改配置以满足需要。

接下来

或者,学习如何使用 CustomResourceDefinition 扩展 Kubernetes API

4 - 任务

Kubernetes 文档这一部分包含的一些页面展示如何去完成单个任务。 每个任务页面是一般通过给出若干步骤展示如何执行完成某事。

如果你希望编写一个任务页面,参考 创建一个文档拉取请求

4.1 - 安装工具

在你的计算机上设置 Kubernetes 工具。

kubectl

Kubernetes 命令行工具 kubectl, 让你可以对 Kubernetes 集群运行命令。 你可以使用 kubectl 来部署应用、监测和管理集群资源以及查看日志。

有关更多信息,包括 kubectl 操作的完整列表,请参见 kubectl 参考文件

kubectl 可安装在各种 Linux 平台、 macOS 和 Windows 上。 在下面找到你喜欢的操作系统。

kind

kind 让你能够在本地计算机上运行 Kubernetes。 使用这个工具需要你安装 Docker 或者 Podman

kind 的 Quick Start 页面展示开始使用 kind 所需要完成的操作。

查看 kind 的快速入门指南

minikube

kind 类似,minikube 是一个工具, 能让你在本地运行 Kubernetes。 minikube 在你的个人计算机(包括 Windows、macOS 和 Linux PC)上运行一个一体化(all-in-one) 或多节点的本地 Kubernetes 集群,以便你来尝试 Kubernetes 或者开展每天的开发工作。

如果你关注如何安装此工具,可以按官方的 Get Started!指南操作。

查看 minikube 快速入门指南

当你拥有了可工作的 minikube 时, 就可以用它来运行示例应用了。

kubeadm

你可以使用 kubeadm 工具来创建和管理 Kubernetes 集群。 该工具能够执行必要的动作并用一种用户友好的方式启动一个可用的、安全的集群。

安装 kubeadm 展示了如何安装 kubeadm 的过程。一旦安装了 kubeadm, 你就可以使用它来创建一个集群

查看 kubeadm 安装指南

4.1.1 - 在 Linux 系统中安装并设置 kubectl

准备开始

kubectl 版本和集群版本之间的差异必须在一个小版本号内。 例如:v1.30 版本的客户端能与 v1.29、 v1.30 和 v1.31 版本的控制面通信。 用最新兼容版的 kubectl 有助于避免不可预见的问题。

在 Linux 系统中安装 kubectl

在 Linux 系统中安装 kubectl 有如下几种方法:

用 curl 在 Linux 系统中安装 kubectl

  1. 用以下命令下载最新发行版:

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
       

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/arm64/kubectl"
       
  1. 验证该可执行文件(可选步骤)

    下载 kubectl 校验和文件:

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl.sha256"
       

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/arm64/kubectl.sha256"
       

    基于校验和文件,验证 kubectl 的可执行文件:

    echo "$(cat kubectl.sha256)  kubectl" | sha256sum --check
    

    验证通过时,输出为:

    kubectl: OK
    

    验证失败时,sha256 将以非零值退出,并打印如下输出:

    kubectl: FAILED
    sha256sum: WARNING: 1 computed checksum did NOT match
    
  1. 安装 kubectl

    sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
    
  1. 执行测试,以保障你安装的版本是最新的:

    kubectl version --client
    

    或者使用如下命令来查看版本的详细信息:

    kubectl version --client --output=yaml
    

用原生包管理工具安装

  1. 更新 apt 包索引,并安装使用 Kubernetes apt 仓库所需要的包:

    sudo apt-get update
    # apt-transport-https 可以是一个虚拟包;如果是这样,你可以跳过这个包
    sudo apt-get install -y apt-transport-https ca-certificates curl
    
  1. 下载 Kubernetes 软件包仓库的公共签名密钥。 同一个签名密钥适用于所有仓库,因此你可以忽略 URL 中的版本信息:

    # 如果 `/etc/apt/keyrings` 目录不存在,则应在 curl 命令之前创建它,请阅读下面的注释。
    # sudo mkdir -p -m 755 /etc/apt/keyrings
    curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
    sudo chmod 644 /etc/apt/keyrings/kubernetes-apt-keyring.gpg # allow unprivileged APT programs to read this keyring   
    
  1. 添加合适的 Kubernetes apt 仓库。如果你想用 v1.30 之外的 Kubernetes 版本, 请将下面命令中的 v1.30 替换为所需的次要版本:

    # 这会覆盖 /etc/apt/sources.list.d/kubernetes.list 中的所有现存配置
    echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.30/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
    sudo chmod 644 /etc/apt/sources.list.d/kubernetes.list   # 有助于让诸如 command-not-found 等工具正常工作
    
  1. 更新 apt 包索引,然后安装 kubectl:

    sudo apt-get update
    sudo apt-get install -y kubectl
    

  1. 添加 Kubernetes 的 yum 仓库。如果你想使用 v1.30 之外的 Kubernetes 版本, 将下面命令中的 v1.30 替换为所需的次要版本。

    # 这会覆盖 /etc/yum.repos.d/kubernetes.repo 中现存的所有配置
    cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
    [kubernetes]
    name=Kubernetes
    baseurl=https://pkgs.k8s.io/core:/stable:/v1.30/rpm/
    enabled=1
    gpgcheck=1
    gpgkey=https://pkgs.k8s.io/core:/stable:/v1.30/rpm/repodata/repomd.xml.key
    EOF
    
  1. 使用 yum 安装 kubectl:

    sudo yum install -y kubectl
    

  1. 添加 Kubernetes zypper 软件源。如果你想使用不同于 v1.30 的 Kubernetes 版本,请在下面的命令中将 v1.30 替换为所需的次要版本。
# 这将覆盖 /etc/zypp/repos.d/kubernetes.repo 中的任何现有配置。
cat <<EOF | sudo tee /etc/zypp/repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://pkgs.k8s.io/core:/stable:/v1.30/rpm/
enabled=1
gpgcheck=1
gpgkey=https://pkgs.k8s.io/core:/stable:/v1.30/rpm/repodata/repomd.xml.key
EOF
  1. 使用 zypper 安装 kubectl:

    sudo zypper install -y kubectl
    

用其他包管理工具安装

如果你使用的 Ubuntu 或其他 Linux 发行版,内建支持 snap 包管理工具, 则可用 snap 命令安装 kubectl。

snap install kubectl --classic
kubectl version --client

如果你使用 Linux 系统,并且装了 Homebrew 包管理工具, 则可以使用这种方式安装 kubectl。

brew install kubectl
kubectl version --client

验证 kubectl 配置

为了让 kubectl 能发现并访问 Kubernetes 集群,你需要一个 kubeconfig 文件, 该文件在 kube-up.sh 创建集群时,或成功部署一个 Minikube 集群时,均会自动生成。 通常,kubectl 的配置信息存放于文件 ~/.kube/config 中。

通过获取集群状态的方法,检查是否已恰当地配置了 kubectl:

kubectl cluster-info

如果返回一个 URL,则意味着 kubectl 成功地访问到了你的集群。

如果你看到如下所示的消息,则代表 kubectl 配置出了问题,或无法连接到 Kubernetes 集群。

The connection to the server <server-name:port> was refused - did you specify the right host or port?
(访问 <server-name:port> 被拒绝 - 你指定的主机和端口是否有误?)

例如,如果你想在自己的笔记本上(本地)运行 Kubernetes 集群,你需要先安装一个 Minikube 这样的工具,然后再重新运行上面的命令。

如果命令 kubectl cluster-info 返回了 URL,但你还不能访问集群,那可以用以下命令来检查配置是否妥当:

kubectl cluster-info dump

排查"找不到身份验证提供商"的错误信息

在 Kubernetes 1.26 中,kubectl 删除了以下云提供商托管的 Kubernetes 产品的内置身份验证。 这些提供商已经发布了 kubectl 插件来提供特定于云的身份验证。 有关说明,请参阅以下提供商文档:

(还可能会有其他原因会看到相同的错误信息,和这个更改无关。)

kubectl 的可选配置和插件

启用 shell 自动补全功能

kubectl 为 Bash、Zsh、Fish 和 PowerShell 提供自动补全功能,可以为你节省大量的输入。

下面是为 Bash、Fish 和 Zsh 设置自动补全功能的操作步骤。

简介

kubectl 的 Bash 补全脚本可以用命令 kubectl completion bash 生成。 在 Shell 中导入(Sourcing)补全脚本,将启用 kubectl 自动补全功能。

然而,补全脚本依赖于工具 bash-completion, 所以要先安装它(可以用命令 type _init_completion 检查 bash-completion 是否已安装)。

安装 bash-completion

很多包管理工具均支持 bash-completion(参见这里)。 可以通过 apt-get install bash-completionyum install bash-completion 等命令来安装它。

上述命令将创建文件 /usr/share/bash-completion/bash_completion,它是 bash-completion 的主脚本。 依据包管理工具的实际情况,你需要在 ~/.bashrc 文件中手工导入此文件。

要查看结果,请重新加载你的 Shell,并运行命令 type _init_completion。 如果命令执行成功,则设置完成,否则将下面内容添加到文件 ~/.bashrc 中:

source /usr/share/bash-completion/bash_completion

重新加载 Shell,再输入命令 type _init_completion 来验证 bash-completion 的安装状态。

启动 kubectl 自动补全功能

Bash

你现在需要确保一点:kubectl 补全脚本已经导入(sourced)到 Shell 会话中。 可以通过以下两种方法进行设置:


echo 'source <(kubectl completion bash)' >>~/.bashrc


kubectl completion bash | sudo tee /etc/bash_completion.d/kubectl > /dev/null
sudo chmod a+r /etc/bash_completion.d/kubectl

如果 kubectl 有关联的别名,你可以扩展 Shell 补全来适配此别名:

echo 'alias k=kubectl' >>~/.bashrc
echo 'complete -o default -F __start_kubectl k' >>~/.bashrc

两种方式的效果相同。重新加载 Shell 后,kubectl 自动补全功能即可生效。 若要在当前 Shell 会话中启用 Bash 补全功能,源引 ~/.bashrc 文件:

source ~/.bashrc

kubectl 通过命令 kubectl completion fish 生成 Fish 自动补全脚本。 在 shell 中导入(Sourcing)该自动补全脚本,将启动 kubectl 自动补全功能。

为了在所有的 shell 会话中实现此功能,请将下面内容加入到文件 ~/.config/fish/config.fish 中。

kubectl completion fish | source

重新加载 shell 后,kubectl 自动补全功能将立即生效。

kubectl 通过命令 kubectl completion zsh 生成 Zsh 自动补全脚本。 在 Shell 中导入(Sourcing)该自动补全脚本,将启动 kubectl 自动补全功能。

为了在所有的 Shell 会话中实现此功能,请将下面内容加入到文件 ~/.zshrc 中。

source <(kubectl completion zsh)

如果你为 kubectl 定义了别名,kubectl 自动补全将自动使用它。

重新加载 Shell 后,kubectl 自动补全功能将立即生效。

如果你收到 2: command not found: compdef 这样的错误提示,那请将下面内容添加到 ~/.zshrc 文件的开头:

autoload -Uz compinit
compinit

安装 kubectl convert 插件

一个 Kubernetes 命令行工具 kubectl 的插件,允许你将清单在不同 API 版本间转换。 这对于将清单迁移到新的 Kubernetes 发行版上未被废弃的 API 版本时尤其有帮助。 更多信息请访问迁移到非弃用 API

  1. 用以下命令下载最新发行版:

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl-convert"
       

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/arm64/kubectl-convert"
       
  1. 验证该可执行文件(可选步骤)

    下载 kubectl-convert 校验和文件:

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl-convert.sha256"
       

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/arm64/kubectl-convert.sha256"
       

    基于校验和,验证 kubectl-convert 的可执行文件:

    echo "$(cat kubectl-convert.sha256) kubectl-convert" | sha256sum --check
    

    验证通过时,输出为:

    kubectl-convert: OK
    

    验证失败时,sha256 将以非零值退出,并打印输出类似于:

    kubectl-convert: FAILED
    sha256sum: WARNING: 1 computed checksum did NOT match
    
  1. 安装 kubectl-convert

    sudo install -o root -g root -m 0755 kubectl-convert /usr/local/bin/kubectl-convert
    
  1. 验证插件是否安装成功

    kubectl convert --help
    

    如果你没有看到任何错误就代表插件安装成功了。

  1. 安装插件后,清理安装文件:

    rm kubectl-convert kubectl-convert.sha256
    

接下来

4.1.2 - 在 macOS 系统上安装和设置 kubectl

准备开始

kubectl 版本和集群之间的差异必须在一个小版本号之内。 例如:v1.30 版本的客户端能与 v1.29、 v1.30 和 v1.31 版本的控制面通信。 用最新兼容版本的 kubectl 有助于避免不可预见的问题。

在 macOS 系统上安装 kubectl

在 macOS 系统上安装 kubectl 有如下方法:

用 curl 在 macOS 系统上安装 kubectl

  1. 下载最新的发行版:

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/amd64/kubectl"
       

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/arm64/kubectl"
       
  1. 验证可执行文件(可选操作)

    下载 kubectl 的校验和文件:

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/amd64/kubectl.sha256"
       

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/arm64/kubectl.sha256"
       

    根据校验和文件,验证 kubectl:

    echo "$(cat kubectl.sha256)  kubectl" | shasum -a 256 --check
    

    验证通过时,输出如下:

    kubectl: OK
    

    验证失败时,shasum 将以非零值退出,并打印如下输出:

    kubectl: FAILED
    shasum: WARNING: 1 computed checksum did NOT match
    
  1. 将 kubectl 置为可执行文件:

    chmod +x ./kubectl
    
  1. 将可执行文件 kubectl 移动到系统可寻址路径 PATH 内的一个位置:

    sudo mv ./kubectl /usr/local/bin/kubectl
    sudo chown root: /usr/local/bin/kubectl
    
  1. 测试一下,确保你安装的是最新的版本:

    kubectl version --client
    

    或者使用下面命令来查看版本的详细信息:

    kubectl version --client --output=yaml
    
  1. 安装并验证 kubectl 后,删除校验和文件:

    rm kubectl.sha256
    

用 Homebrew 在 macOS 系统上安装

如果你是 macOS 系统,且用的是 Homebrew 包管理工具, 则可以用 Homebrew 安装 kubectl。

  1. 运行安装命令:

    brew install kubectl
    

    brew install kubernetes-cli
    
  1. 测试一下,确保你安装的是最新的版本:

    kubectl version --client
    

用 Macports 在 macOS 系统上安装

如果你用的是 macOS,且用 Macports 包管理工具,则你可以用 Macports 安装 kubectl。

  1. 运行安装命令:

    sudo port selfupdate
    sudo port install kubectl
    
  1. 测试一下,确保你安装的是最新的版本:

    kubectl version --client
    

验证 kubectl 配置

为了让 kubectl 能发现并访问 Kubernetes 集群,你需要一个 kubeconfig 文件, 该文件在 kube-up.sh 创建集群时,或成功部署一个 Minikube 集群时,均会自动生成。 通常,kubectl 的配置信息存放于文件 ~/.kube/config 中。

通过获取集群状态的方法,检查是否已恰当地配置了 kubectl:

kubectl cluster-info

如果返回一个 URL,则意味着 kubectl 成功地访问到了你的集群。

如果你看到如下所示的消息,则代表 kubectl 配置出了问题,或无法连接到 Kubernetes 集群。

The connection to the server <server-name:port> was refused - did you specify the right host or port?
(访问 <server-name:port> 被拒绝 - 你指定的主机和端口是否有误?)

例如,如果你想在自己的笔记本上(本地)运行 Kubernetes 集群,你需要先安装一个 Minikube 这样的工具,然后再重新运行上面的命令。

如果命令 kubectl cluster-info 返回了 URL,但你还不能访问集群,那可以用以下命令来检查配置是否妥当:

kubectl cluster-info dump

排查"找不到身份验证提供商"的错误信息

在 Kubernetes 1.26 中,kubectl 删除了以下云提供商托管的 Kubernetes 产品的内置身份验证。 这些提供商已经发布了 kubectl 插件来提供特定于云的身份验证。 有关说明,请参阅以下提供商文档:

(还可能会有其他原因会看到相同的错误信息,和这个更改无关。)

可选的 kubectl 配置和插件

启用 shell 自动补全功能

kubectl 为 Bash、Zsh、Fish 和 PowerShell 提供自动补全功能,可以为你节省大量的输入。

下面是为 Bash、Fish 和 Zsh 设置自动补全功能的操作步骤。

简介

kubectl 的 Bash 补全脚本可以通过 kubectl completion bash 命令生成。 在你的 Shell 中导入(Sourcing)这个脚本即可启用补全功能。

此外,kubectl 补全脚本依赖于工具 bash-completion, 所以你必须先安装它。

升级 Bash

后续说明假定你已使用 Bash 4.1+。你可以运行以下命令检查 Bash 版本:

echo $BASH_VERSION

如果版本太旧,可以用 Homebrew 安装/升级:

brew install bash

重新加载 Shell,并验证所需的版本已经生效:

echo $BASH_VERSION $SHELL

Homebrew 通常把它安装为 /usr/local/bin/bash

安装 bash-completion

你可以用命令 type _init_completion 测试 bash-completion v2 是否已经安装。 如未安装,用 Homebrew 来安装它:

brew install bash-completion@2

如命令的输出信息所显示的,将如下内容添加到文件 ~/.bash_profile 中:

brew_etc="$(brew --prefix)/etc" && [[ -r "${brew_etc}/profile.d/bash_completion.sh" ]] && . "${brew_etc}/profile.d/bash_completion.sh"

重新加载 Shell,并用命令 type _init_completion 验证 bash-completion v2 已经恰当的安装。

启用 kubectl 自动补全功能

你现在需要确保在所有的 Shell 环境中均已导入(sourced)kubectl 的补全脚本, 有若干种方法可以实现这一点:

  • 在文件 ~/.bash_profile 中导入(Source)补全脚本:

    echo 'source <(kubectl completion bash)' >>~/.bash_profile
    
  • 将补全脚本添加到目录 /usr/local/etc/bash_completion.d 中:

    kubectl completion bash >/usr/local/etc/bash_completion.d/kubectl
    
  • 如果你为 kubectl 定义了别名,则可以扩展 Shell 补全来兼容该别名:

    echo 'alias k=kubectl' >>~/.bash_profile
    echo 'complete -o default -F __start_kubectl k' >>~/.bash_profile
    
  • 如果你是用 Homebrew 安装的 kubectl (如此页面所描述), 则 kubectl 补全脚本应该已经安装到目录 /usr/local/etc/bash_completion.d/kubectl 中了。这种情况下,你什么都不需要做。

总之,重新加载 Shell 之后,kubectl 补全功能将立即生效。

kubectl 通过命令 kubectl completion fish 生成 Fish 自动补全脚本。 在 shell 中导入(Sourcing)该自动补全脚本,将启动 kubectl 自动补全功能。

为了在所有的 shell 会话中实现此功能,请将下面内容加入到文件 ~/.config/fish/config.fish 中。

kubectl completion fish | source

重新加载 shell 后,kubectl 自动补全功能将立即生效。

kubectl 通过命令 kubectl completion zsh 生成 Zsh 自动补全脚本。 在 Shell 中导入(Sourcing)该自动补全脚本,将启动 kubectl 自动补全功能。

为了在所有的 Shell 会话中实现此功能,请将下面内容加入到文件 ~/.zshrc 中。

source <(kubectl completion zsh)

如果你为 kubectl 定义了别名,kubectl 自动补全将自动使用它。

重新加载 Shell 后,kubectl 自动补全功能将立即生效。

如果你收到 2: command not found: compdef 这样的错误提示,那请将下面内容添加到 ~/.zshrc 文件的开头:

autoload -Uz compinit
compinit

安装 kubectl convert 插件

一个 Kubernetes 命令行工具 kubectl 的插件,允许你将清单在不同 API 版本间转换。 这对于将清单迁移到新的 Kubernetes 发行版上未被废弃的 API 版本时尤其有帮助。 更多信息请访问迁移到非弃用 API

  1. 用以下命令下载最新发行版:

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/amd64/kubectl-convert"
       

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/arm64/kubectl-convert"
       
  1. 验证该可执行文件(可选步骤)

    下载 kubectl-convert 校验和文件:

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/amd64/kubectl-convert.sha256"
       

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/arm64/kubectl-convert.sha256"
       

    基于校验和,验证 kubectl-convert 的可执行文件:

    echo "$(cat kubectl-convert.sha256)  kubectl-convert" | shasum -a 256 --check
    

    验证通过时,输出为:

    kubectl-convert: OK
    

    验证失败时,sha256 将以非零值退出,并打印输出类似于:

    kubectl-convert: FAILED
    shasum: WARNING: 1 computed checksum did NOT match
    
  1. 使 kubectl-convert 二进制文件可执行

    chmod +x ./kubectl-convert
    
  1. 将 kubectl-convert 可执行文件移动到系统 PATH 环境变量中的一个位置。

    sudo mv ./kubectl-convert /usr/local/bin/kubectl-convert
    sudo chown root: /usr/local/bin/kubectl-convert
    
  1. 验证插件是否安装成功

    kubectl convert --help
    

    如果你没有看到任何错误就代表插件安装成功了。

  1. 安装插件后,清理安装文件:

    rm kubectl-convert kubectl-convert.sha256
    

在 macOS 上卸载 kubectl

根据你安装 kubectl 的方式,使用以下某种方法来卸载:

使用命令行卸载 kubectl

  1. 找到你系统上的 kubectl 可执行文件:

    which kubectl
    
  1. 移除 kubectl 可执行文件:

    sudo rm <path>
    

    <path> 替换为上一步中找到的 kubectl 可执行文件的路径。 例如,sudo rm /usr/local/bin/kubectl

使用 Homebrew 卸载 kubectl

如果你使用 Homebrew 安装了 kubectl,运行以下命令:

brew remove kubectl

接下来

4.1.3 - 在 Windows 上安装 kubectl

准备开始

kubectl 版本和集群版本之间的差异必须在一个小版本号内。 例如:v1.30 版本的客户端能与 v1.29、 v1.30 和 v1.31 版本的控制面通信。 用最新兼容版的 kubectl 有助于避免不可预见的问题。

在 Windows 上安装 kubectl

在 Windows 系统中安装 kubectl 有如下几种方法:

用 curl 在 Windows 上安装 kubectl

  1. 下载最新补丁版 1.30: kubectl 1.30.0

    如果你已安装了 curl,也可以使用此命令:

    curl.exe -LO "https://dl.k8s.io/release/v1.30.0/bin/windows/amd64/kubectl.exe"
    
  1. 验证该可执行文件(可选步骤)

    下载 kubectl 校验和文件:

    curl.exe -LO "https://dl.k8s.io/v1.30.0/bin/windows/amd64/kubectl.exe.sha256"
    

    基于校验和文件,验证 kubectl 的可执行文件:

    • 在命令行环境中,手工对比 CertUtil 命令的输出与校验和文件:

      CertUtil -hashfile kubectl.exe SHA256
      type kubectl.exe.sha256
      
    • 用 PowerShell 自动验证,用运算符 -eq 来直接取得 TrueFalse 的结果:

      $(Get-FileHash -Algorithm SHA256 .\kubectl.exe).Hash -eq $(Get-Content .\kubectl.exe.sha256)
      
  1. kubectl 二进制文件夹追加或插入到你的 PATH 环境变量中。

  2. 测试一下,确保此 kubectl 的版本和期望版本一致:

    kubectl version --client
    

    或者使用下面命令来查看版本的详细信息:

    kubectl version --client --output=yaml
    

在 Windows 上用 Chocolatey、Scoop 或 winget 安装

  1. 要在 Windows 上安装 kubectl,你可以使用包管理器 Chocolatey、 命令行安装器 Scoop 或包管理器 winget

    choco install kubernetes-cli
    

    scoop install kubectl
    

    winget install -e --id Kubernetes.kubectl
    
  1. 测试一下,确保安装的是最新版本:

    kubectl version --client
    
  1. 导航到你的 home 目录:

    # 当你用 cmd.exe 时,则运行: cd %USERPROFILE%
    cd ~
    
  1. 创建目录 .kube

    mkdir .kube
    
  1. 切换到新创建的目录 .kube

    cd .kube
    
  1. 配置 kubectl,以接入远程的 Kubernetes 集群:

    New-Item config -type file
    

验证 kubectl 配置

为了让 kubectl 能发现并访问 Kubernetes 集群,你需要一个 kubeconfig 文件, 该文件在 kube-up.sh 创建集群时,或成功部署一个 Minikube 集群时,均会自动生成。 通常,kubectl 的配置信息存放于文件 ~/.kube/config 中。

通过获取集群状态的方法,检查是否已恰当地配置了 kubectl:

kubectl cluster-info

如果返回一个 URL,则意味着 kubectl 成功地访问到了你的集群。

如果你看到如下所示的消息,则代表 kubectl 配置出了问题,或无法连接到 Kubernetes 集群。

The connection to the server <server-name:port> was refused - did you specify the right host or port?
(访问 <server-name:port> 被拒绝 - 你指定的主机和端口是否有误?)

例如,如果你想在自己的笔记本上(本地)运行 Kubernetes 集群,你需要先安装一个 Minikube 这样的工具,然后再重新运行上面的命令。

如果命令 kubectl cluster-info 返回了 URL,但你还不能访问集群,那可以用以下命令来检查配置是否妥当:

kubectl cluster-info dump

排查"找不到身份验证提供商"的错误信息

在 Kubernetes 1.26 中,kubectl 删除了以下云提供商托管的 Kubernetes 产品的内置身份验证。 这些提供商已经发布了 kubectl 插件来提供特定于云的身份验证。 有关说明,请参阅以下提供商文档:

(还可能会有其他原因会看到相同的错误信息,和这个更改无关。)

kubectl 可选配置和插件

启用 shell 自动补全功能

kubectl 为 Bash、Zsh、Fish 和 PowerShell 提供自动补全功能,可以为你节省大量的输入。

下面是设置 PowerShell 自动补全功能的操作步骤。

你可以使用命令 kubectl completion powershell 生成 PowerShell 的 kubectl 自动补全脚本。

如果需要自动补全在所有 Shell 会话中生效,请将以下命令添加到 $PROFILE 文件中:

kubectl completion powershell | Out-String | Invoke-Expression

此命令将在每次 PowerShell 启动时重新生成自动补全脚本。你还可以将生成的自动补全脚本添加到 $PROFILE 文件中。

如果需要将自动补全脚本直接添加到 $PROFILE 文件中,请在 PowerShell 命令行运行以下命令:

kubectl completion powershell >> $PROFILE

完成上述操作后重启 Shell,kubectl 的自动补全就可以工作了。

安装 kubectl convert 插件

一个 Kubernetes 命令行工具 kubectl 的插件,允许你将清单在不同 API 版本间转换。 这对于将清单迁移到新的 Kubernetes 发行版上未被废弃的 API 版本时尤其有帮助。 更多信息请访问迁移到非弃用 API

  1. 用以下命令下载最新发行版:

    curl.exe -LO "https://dl.k8s.io/release/v1.30.0/bin/windows/amd64/kubectl-convert.exe"
    
  1. 验证该可执行文件(可选步骤)。

    下载 kubectl-convert 校验和文件:

    curl.exe -LO "https://dl.k8s.io/v1.30.0/bin/windows/amd64/kubectl-convert.exe.sha256"
    

    基于校验和验证 kubectl-convert 的可执行文件:

    • 用提示的命令对 CertUtil 的输出和下载的校验和文件进行手动比较。

      CertUtil -hashfile kubectl-convert.exe SHA256
      type kubectl-convert.exe.sha256
      
    • 使用 PowerShell -eq 操作使验证自动化,获得 True 或者 False 的结果:

      $($(CertUtil -hashfile .\kubectl-convert.exe SHA256)[1] -replace " ", "") -eq $(type .\kubectl-convert.exe.sha256)
      
  1. kubectl-convert 二进制文件夹附加或添加到你的 PATH 环境变量中。

  2. 验证插件是否安装成功。

    kubectl convert --help
    

    如果你没有看到任何错误就代表插件安装成功了。

  1. 安装插件后,清理安装文件:

    del kubectl-convert.exe
    del kubectl-convert.exe.sha256
    

接下来

4.2 - 管理集群

了解管理集群的常见任务。

4.2.1 - 用 kubeadm 进行管理

4.2.1.1 - 使用 kubeadm 进行证书管理

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

kubeadm 生成的客户端证书在 1 年后到期。 本页说明如何使用 kubeadm 管理证书续订,同时也涵盖其他与 kubeadm 证书管理相关的说明。

准备开始

你应该熟悉 Kubernetes 中的 PKI 证书和要求

使用自定义的证书

默认情况下,kubeadm 会生成运行一个集群所需的全部证书。 你可以通过提供你自己的证书来改变这个行为策略。

如果要这样做,你必须将证书文件放置在通过 --cert-dir 命令行参数或者 kubeadm 配置中的 certificatesDir 配置项指明的目录中。默认的值是 /etc/kubernetes/pki

如果在运行 kubeadm init 之前存在给定的证书和私钥对,kubeadm 将不会重写它们。 例如,这意味着你可以将现有的 CA 复制到 /etc/kubernetes/pki/ca.crt/etc/kubernetes/pki/ca.key 中,而 kubeadm 将使用此 CA 对其余证书进行签名。

外部 CA 模式

只提供了 ca.crt 文件但是不提供 ca.key 文件也是可以的 (这只对 CA 根证书可用,其它证书不可用)。 如果所有的其它证书和 kubeconfig 文件已就绪,kubeadm 检测到满足以上条件就会激活 "外部 CA" 模式。kubeadm 将会在没有 CA 密钥文件的情况下继续执行。

否则,kubeadm 将独立运行 controller-manager,附加一个 --controllers=csrsigner 的参数,并且指明 CA 证书和密钥。

PKI 证书和要求包括集群使用外部 CA 的设置指南。

检查证书是否过期

你可以使用 check-expiration 子命令来检查证书何时过期

kubeadm certs check-expiration

输出类似于以下内容:

CERTIFICATE                EXPIRES                  RESIDUAL TIME   CERTIFICATE AUTHORITY   EXTERNALLY MANAGED
admin.conf                 Dec 30, 2020 23:36 UTC   364d                                    no
apiserver                  Dec 30, 2020 23:36 UTC   364d            ca                      no
apiserver-etcd-client      Dec 30, 2020 23:36 UTC   364d            etcd-ca                 no
apiserver-kubelet-client   Dec 30, 2020 23:36 UTC   364d            ca                      no
controller-manager.conf    Dec 30, 2020 23:36 UTC   364d                                    no
etcd-healthcheck-client    Dec 30, 2020 23:36 UTC   364d            etcd-ca                 no
etcd-peer                  Dec 30, 2020 23:36 UTC   364d            etcd-ca                 no
etcd-server                Dec 30, 2020 23:36 UTC   364d            etcd-ca                 no
front-proxy-client         Dec 30, 2020 23:36 UTC   364d            front-proxy-ca          no
scheduler.conf             Dec 30, 2020 23:36 UTC   364d                                    no

CERTIFICATE AUTHORITY   EXPIRES                  RESIDUAL TIME   EXTERNALLY MANAGED
ca                      Dec 28, 2029 23:36 UTC   9y              no
etcd-ca                 Dec 28, 2029 23:36 UTC   9y              no
front-proxy-ca          Dec 28, 2029 23:36 UTC   9y              no

该命令显示 /etc/kubernetes/pki 文件夹中的客户端证书以及 kubeadm(admin.confcontroller-manager.confscheduler.conf) 使用的 kubeconfig 文件中嵌入的客户端证书的到期时间/剩余时间。

另外,kubeadm 会通知用户证书是否由外部管理; 在这种情况下,用户应该小心的手动/使用其他工具来管理证书更新。

自动更新证书

kubeadm 会在控制面升级的时候更新所有证书。

这个功能旨在解决最简单的用例;如果你对此类证书的更新没有特殊要求, 并且定期执行 Kubernetes 版本升级(每次升级之间的间隔时间少于 1 年), 则 kubeadm 将确保你的集群保持最新状态并保持合理的安全性。

如果你对证书更新有更复杂的需求,则可通过将 --certificate-renewal=false 传递给 kubeadm upgrade apply 或者 kubeadm upgrade node,从而选择不采用默认行为。

手动更新证书

你能随时通过 kubeadm certs renew 命令手动更新你的证书,只需带上合适的命令行选项。

此命令用 CA(或者 front-proxy-CA )证书和存储在 /etc/kubernetes/pki 中的密钥执行更新。

执行完此命令之后你需要重启控制面 Pod。因为动态证书重载目前还不被所有组件和证书支持,所有这项操作是必须的。 静态 Pod 是被本地 kubelet 而不是 API 服务器管理,所以 kubectl 不能用来删除或重启他们。 要重启静态 Pod 你可以临时将清单文件从 /etc/kubernetes/manifests/ 移除并等待 20 秒 (参考 KubeletConfiguration 结构中的 fileCheckFrequency 值)。如果 Pod 不在清单目录里,kubelet 将会终止它。 在另一个 fileCheckFrequency 周期之后你可以将文件移回去,kubelet 可以完成 Pod 的重建,而组件的证书更新操作也得以完成。

kubeadm certs renew 可以更新任何特定的证书,或者使用子命令 all 更新所有的证书,如下所示:

kubeadm certs renew all

用 Kubernetes 证书 API 更新证书

本节提供有关如何使用 Kubernetes 证书 API 执行手动证书更新的更多详细信息。

设置一个签名者(Signer)

Kubernetes 证书颁发机构不是开箱即用。你可以配置外部签名者,例如 cert-manager, 也可以使用内置签名者。

内置签名者是 kube-controller-manager 的一部分。

要激活内置签名者,请传递 --cluster-signing-cert-file--cluster-signing-key-file 参数。

如果你正在创建一个新的集群,你可以使用 kubeadm 的配置文件

apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
controllerManager:
  extraArgs:
    cluster-signing-cert-file: /etc/kubernetes/pki/ca.crt
    cluster-signing-key-file: /etc/kubernetes/pki/ca.key

创建证书签名请求 (CSR)

有关使用 Kubernetes API 创建 CSR 的信息, 请参见创建 CertificateSigningRequest

通过外部 CA 更新证书

本节提供有关如何使用外部 CA 执行手动更新证书的更多详细信息。

为了更好的与外部 CA 集成,kubeadm 还可以生成证书签名请求(CSR)。 CSR 表示向 CA 请求客户的签名证书。 在 kubeadm 术语中,通常由磁盘 CA 签名的任何证书都可以作为 CSR 生成。但是,CA 不能作为 CSR 生成。

创建证书签名请求 (CSR)

你可以通过 kubeadm certs renew --csr-only 命令创建证书签名请求。

CSR 和随附的私钥都在输出中给出。 你可以传入一个带有 --csr-dir 的目录,将 CSR 输出到指定位置。 如果未指定 --csr-dir,则使用默认证书目录(/etc/kubernetes/pki)。

证书可以通过 kubeadm certs renew --csr-only 来续订。 和 kubeadm init 一样,可以使用 --csr-dir 标志指定一个输出目录。

CSR 中包含一个证书的名字,域和 IP,但是未指定用法。 颁发证书时,CA 有责任指定正确的证书用法

使用首选方法对证书签名后,必须将证书和私钥复制到 PKI 目录(默认为 /etc/kubernetes/pki)。

证书机构(CA)轮换

kubeadm 并不直接支持对 CA 证书的轮换或者替换。

关于手动轮换或者置换 CA 的更多信息, 可参阅手动轮换 CA 证书

启用已签名的 kubelet 服务证书

默认情况下,kubeadm 所部署的 kubelet 服务证书是自签名(Self-Signed)。 这意味着从 metrics-server 这类外部服务发起向 kubelet 的链接时无法使用 TLS 来完成保护。

要在新的 kubeadm 集群中配置 kubelet 以使用被正确签名的服务证书, 你必须向 kubeadm init 传递如下最小配置数据:

apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
serverTLSBootstrap: true

如果你已经创建了集群,你必须通过执行下面的操作来完成适配:

  • 找到 kube-system 名字空间中名为 kubelet-config-1.30 的 ConfigMap 并编辑之。 在该 ConfigMap 中,kubelet 键下面有一个 KubeletConfiguration 文档作为其取值。编辑该 KubeletConfiguration 文档以设置 serverTLSBootstrap: true
  • 在每个节点上,在 /var/lib/kubelet/config.yaml 文件中添加 serverTLSBootstrap: true 字段,并使用 systemctl restart kubelet 来重启 kubelet。

字段 serverTLSBootstrap 将允许启动引导 kubelet 的服务证书,方式是从 certificates.k8s.io API 处读取。这种方式的一种局限在于这些证书的 CSR(证书签名请求)不能被 kube-controller-manager 中默认的签名组件 kubernetes.io/kubelet-serving 批准。需要用户或者第三方控制器来执行此操作。

可以使用下面的命令来查看 CSR:

kubectl get csr
NAME        AGE     SIGNERNAME                        REQUESTOR                      CONDITION
csr-9wvgt   112s    kubernetes.io/kubelet-serving     system:node:worker-1           Pending
csr-lz97v   1m58s   kubernetes.io/kubelet-serving     system:node:control-plane-1    Pending

你可以执行下面的操作来批准这些请求:

kubectl certificate approve <CSR-名称>

默认情况下,这些服务证书会在一年后过期。 kubeadm 将 KubeletConfigurationrotateCertificates 字段设置为 true;这意味着证书快要过期时,会生成一组针对服务证书的新的 CSR,而这些 CSR 也要被批准才能完成证书轮换。要进一步了解这里的细节, 可参阅证书轮换文档。

如果你在寻找一种能够自动批准这些 CSR 的解决方案,建议你与你的云提供商 联系,询问他们是否有 CSR 签名组件,用来以带外(out-of-band)的方式检查 节点的标识符。

也可以使用第三方定制的控制器:

除非既能够验证 CSR 中的 CommonName,也能检查请求的 IP 和域名, 这类控制器还算不得安全的机制。 只有完成彻底的检查,才有可能避免有恶意的、能够访问 kubelet 客户端证书的第三方为任何 IP 或域名请求服务证书。

为其他用户生成 kubeconfig 文件

在集群创建过程中,kubeadm 对 admin.conf 中的证书进行签名时,将其配置为 Subject: O = system:masters, CN = kubernetes-adminsystem:masters 是一个例外的超级用户组,可以绕过鉴权层(例如 RBAC)。 强烈建议不要将 admin.conf 文件与任何人共享。

你要使用 kubeadm kubeconfig user 命令为其他用户生成 kubeconfig 文件,这个命令支持命令行参数和 kubeadm 配置结构。 以上命令会将 kubeconfig 打印到终端上,也可以使用 kubeadm kubeconfig user ... > somefile.conf 输出到一个文件中。

如下 kubeadm 可以在 --config 后加的配置文件示例:

# example.yaml
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
# kubernetes 将作为 kubeconfig 中集群名称
clusterName: "kubernetes"
# some-dns-address:6443 将作为集群 kubeconfig 文件中服务地址(IP 或者 DNS 名称)
controlPlaneEndpoint: "some-dns-address:6443"
# 从本地挂载集群的 CA 秘钥和 CA 证书
certificatesDir: "/etc/kubernetes/pki"

确保这些设置与所需的目标集群设置相匹配。可以使用以下命令查看现有集群的设置:

kubectl get cm kubeadm-config -n kube-system -o=jsonpath="{.data.ClusterConfiguration}"

以下示例将为在 appdevs 组的 johndoe 用户创建一个有效期为 24 小时的 kubeconfig 文件:

kubeadm kubeconfig user --config example.yaml --org appdevs --client-name johndoe --validity-period 24h

以下示例将为管理员创建一个有效期有一周的 kubeconfig 文件:

kubeadm kubeconfig user --config example.yaml --client-name admin --validity-period 168h

4.2.1.2 - 配置 cgroup 驱动

本页阐述如何配置 kubelet 的 cgroup 驱动以匹配 kubeadm 集群中的容器运行时的 cgroup 驱动。

准备开始

你应该熟悉 Kubernetes 的容器运行时需求

配置容器运行时 cgroup 驱动

容器运行时页面提到, 由于 kubeadm 把 kubelet 视为一个 系统服务来管理, 所以对基于 kubeadm 的安装, 我们推荐使用 systemd 驱动, 不推荐 kubelet 默认cgroupfs 驱动。

此页还详述了如何安装若干不同的容器运行时,并将 systemd 设为其默认驱动。

配置 kubelet 的 cgroup 驱动

kubeadm 支持在执行 kubeadm init 时,传递一个 KubeletConfiguration 结构体。 KubeletConfiguration 包含 cgroupDriver 字段,可用于控制 kubelet 的 cgroup 驱动。

这是一个最小化的示例,其中显式的配置了此字段:

# kubeadm-config.yaml
kind: ClusterConfiguration
apiVersion: kubeadm.k8s.io/v1beta3
kubernetesVersion: v1.21.0
---
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
cgroupDriver: systemd

这样一个配置文件就可以传递给 kubeadm 命令了:

kubeadm init --config kubeadm-config.yaml

使用 cgroupfs 驱动

如仍需使用 cgroupfs 且要防止 kubeadm upgrade 修改现有系统中 KubeletConfiguration 的 cgroup 驱动,你必须显式声明它的值。 此方法应对的场景为:在将来某个版本的 kubeadm 中,你不想使用默认的 systemd 驱动。

参阅以下章节“修改 kubelet 的 ConfigMap ”,了解显式设置该值的方法。

如果你希望配置容器运行时来使用 cgroupfs 驱动, 则必须参考所选容器运行时的文档。

迁移到 systemd 驱动

要将现有 kubeadm 集群的 cgroup 驱动从 cgroupfs 就地升级为 systemd, 需要执行一个与 kubelet 升级类似的过程。 该过程必须包含下面两个步骤:

修改 kubelet 的 ConfigMap

  • 运行 kubectl edit cm kubelet-config -n kube-system

  • 修改现有 cgroupDriver 的值,或者新增如下式样的字段:

    cgroupDriver: systemd
    

    该字段必须出现在 ConfigMap 的 kubelet: 小节下。

更新所有节点的 cgroup 驱动

对于集群中的每一个节点:

  • 执行命令 kubectl drain <node-name> --ignore-daemonsets,以 腾空节点
  • 执行命令 systemctl stop kubelet,以停止 kubelet
  • 停止容器运行时
  • 修改容器运行时 cgroup 驱动为 systemd
  • 在文件 /var/lib/kubelet/config.yaml 中添加设置 cgroupDriver: systemd
  • 启动容器运行时
  • 执行命令 systemctl start kubelet,以启动 kubelet
  • 执行命令 kubectl uncordon <node-name>,以 取消节点隔离

在节点上依次执行上述步骤,确保工作负载有充足的时间被调度到其他节点。

流程完成后,确认所有节点和工作负载均健康如常。

4.2.1.3 - 重新配置 kubeadm 集群

kubeadm 不支持自动重新配置部署在托管节点上的组件的方式。 一种自动化的方法是使用自定义的 operator

要修改组件配置,你必须手动编辑磁盘上关联的集群对象和文件。 本指南展示了实现 kubeadm 集群重新配置所需执行的正确步骤顺序。

准备开始

  • 你需要一个使用 kubeadm 部署的集群
  • 拥有管理员凭据(/etc/kubernetes/admin.conf) 和从安装了 kubectl 的主机到集群中正在运行的 kube-apiserver 的网络连接
  • 在所有主机上安装文本编辑器

重新配置集群

kubeadm 在 ConfigMap 和其他对象中写入了一组集群范围的组件配置选项。 这些对象必须手动编辑,可以使用命令 kubectl edit

kubectl edit 命令将打开一个文本编辑器,你可以在其中直接编辑和保存对象。 你可以使用环境变量 KUBECONFIGKUBE_EDITOR 来指定 kubectl 使用的 kubeconfig 文件和首选文本编辑器的位置。

例如:

KUBECONFIG=/etc/kubernetes/admin.conf KUBE_EDITOR=nano kubectl edit <parameters>

应用集群配置更改

更新 ClusterConfiguration

在集群创建和升级期间,kubeadm 将其 ClusterConfiguration 写入 kube-system 命名空间中名为 kubeadm-config 的 ConfigMap。

要更改 ClusterConfiguration 中的特定选项,你可以使用以下命令编辑 ConfigMap:

kubectl edit cm -n kube-system kubeadm-config

配置位于 data.ClusterConfiguration 键下。

在控制平面节点上反映 ClusterConfiguration 更改

kubeadm 将控制平面组件作为位于 /etc/kubernetes/manifests 目录中的静态 Pod 清单进行管理。 对 apiServercontrollerManagerscheduleretcd键下的 ClusterConfiguration 的任何更改都必须反映在控制平面节点上清单目录中的关联文件中。

此类更改可能包括:

  • extraArgs - 需要更新传递给组件容器的标志列表
  • extraMounts - 需要更新组件容器的卷挂载
  • *SANs - 需要使用更新的主题备用名称编写新证书

在继续进行这些更改之前,请确保你已备份目录 /etc/kubernetes/

要编写新证书,你可以使用:

kubeadm init phase certs <component-name> --config <config-file>

要在 /etc/kubernetes/manifests 中编写新的清单文件,你可以使用以下命令:

# Kubernetes 控制平面组件
kubeadm init phase control-plane <component-name> --config <config-file>
# 本地 etcd
kubeadm init phase etcd local --config <config-file>

<config-file> 内容必须与更新后的 ClusterConfiguration 匹配。 <component-name> 值必须是一个控制平面组件(apiservercontroller-managerscheduler)的名称。

应用 kubelet 配置更改

更新 KubeletConfiguration

在集群创建和升级期间,kubeadm 将其 KubeletConfiguration 写入 kube-system 命名空间中名为 kubelet-config 的 ConfigMap。 你可以使用以下命令编辑 ConfigMap:

kubectl edit cm -n kube-system kubelet-config

配置位于 data.kubelet 键下。

反映 kubelet 的更改

要反映 kubeadm 节点上的更改,你必须执行以下操作:

  • 登录到 kubeadm 节点
  • 运行 kubeadm upgrade node phase kubelet-config 下载最新的 kubelet-config ConfigMap 内容到本地文件 /var/lib/kubelet/config.yaml
  • 编辑文件 /var/lib/kubelet/kubeadm-flags.env 以使用标志来应用额外的配置
  • 使用 systemctl restart kubelet 重启 kubelet 服务

应用 kube-proxy 配置更改

更新 KubeProxyConfiguration

在集群创建和升级期间,kubeadm 将其写入 KubeProxyConfiguration 在名为 kube-proxykube-system 命名空间中的 ConfigMap 中。

此 ConfigMap 由 kube-system 命名空间中的 kube-proxy DaemonSet 使用。

要更改 KubeProxyConfiguration 中的特定选项,你可以使用以下命令编辑 ConfigMap:

kubectl edit cm -n kube-system kube-proxy

配置位于 data.config.conf 键下。

反映 kube-proxy 的更改

更新 kube-proxy ConfigMap 后,你可以重新启动所有 kube-proxy Pod:

获取 Pod 名称:

kubectl get po -n kube-system | grep kube-proxy

使用以下命令删除 Pod:

kubectl delete po -n kube-system <pod-name>

将创建使用更新的 ConfigMap 的新 Pod。

应用 CoreDNS 配置更改

更新 CoreDNS 的 Deployment 和 Service

kubeadm 将 CoreDNS 部署为名为 coredns 的 Deployment,并使用 Service kube-dns, 两者都在 kube-system 命名空间中。

要更新任何 CoreDNS 设置,你可以编辑 Deployment 和 Service:

kubectl edit deployment -n kube-system coredns
kubectl edit service -n kube-system kube-dns

反映 CoreDNS 的更改

应用 CoreDNS 更改后,你可以删除 CoreDNS Pod。

获取 Pod 名称:

kubectl get po -n kube-system | grep coredns

使用以下命令删除 Pod:

kubectl delete po -n kube-system <pod-name>

将创建具有更新的 CoreDNS 配置的新 Pod。

持久化重新配置

在受管节点上执行 kubeadm upgrade 期间,kubeadm 可能会覆盖在创建集群(重新配置)后应用的配置。

持久化 Node 对象重新配置

kubeadm 在特定 Kubernetes 节点的 Node 对象上写入标签、污点、CRI 套接字和其他信息。要更改此 Node 对象的任何内容,你可以使用:

kubectl edit no <node-name>

kubeadm upgrade 期间,此类节点的内容可能会被覆盖。 如果你想在升级后保留对 Node 对象的修改,你可以准备一个 kubectl patch 并将其应用到 Node 对象:

kubectl patch no <node-name> --patch-file <patch-file>

持久化控制平面组件重新配置

控制平面配置的主要来源是存储在集群中的 ClusterConfiguration 对象。 要扩展静态 Pod 清单配置,可以使用 patches

这些补丁文件必须作为文件保留在控制平面节点上,以确保它们可以被 kubeadm upgrade ... --patches <directory> 使用。

如果对 ClusterConfiguration 和磁盘上的静态 Pod 清单进行了重新配置,则必须相应地更新节点特定补丁集。

持久化 kubelet 重新配置

对存储在 /var/lib/kubelet/config.yaml 中的 KubeletConfiguration 所做的任何更改都将在 kubeadm upgrade 时因为下载集群范围内的 kubelet-config ConfigMap 的内容而被覆盖。 要持久保存 kubelet 节点特定的配置,文件 /var/lib/kubelet/config.yaml 必须在升级后手动更新,或者文件 /var/lib/kubelet/kubeadm-flags.env 可以包含标志。 kubelet 标志会覆盖相关的 KubeletConfiguration 选项,但请注意,有些标志已被弃用。

更改 /var/lib/kubelet/config.yaml/var/lib/kubelet/kubeadm-flags.env 后需要重启 kubelet。

接下来

4.2.1.4 - 升级 kubeadm 集群

本页介绍如何将 kubeadm 创建的 Kubernetes 集群从 1.29.x 版本 升级到 1.30.x 版本以及从 1.30.x 升级到 1.30.y(其中 y > x)。略过次版本号的升级是 不被支持的。更多详情请访问版本偏差策略

要查看 kubeadm 创建的有关旧版本集群升级的信息,请参考以下页面:

升级工作的基本流程如下:

  1. 升级主控制平面节点
  2. 升级其他控制平面节点
  3. 升级工作节点

准备开始

  • 务必仔细认真阅读发行说明
  • 集群应使用静态的控制平面和 etcd Pod 或者外部 etcd。
  • 务必备份所有重要组件,例如存储在数据库中应用层面的状态。 kubeadm upgrade 不会影响你的工作负载,只会涉及 Kubernetes 内部的组件,但备份终究是好的。
  • 必须禁用交换分区

附加信息

  • 下述说明了在升级过程中何时腾空每个节点。如果你正在对任何 kubelet 进行小版本升级, 你需要先腾空待升级的节点(或多个节点)。对于控制面节点,其上可能运行着 CoreDNS Pod 或者其它非常重要的负载。更多信息见腾空节点
  • Kubernetes 项目推荐你使用版本匹配的 kubelet 和 kubeadm。 但你也可以使用比 kubeadm 版本更低的 kubelet 版本,前提是该版本仍处于支持的版本范围内。 欲了解更多信息,请访问 kubeadm 与 kubelet 的版本差异
  • 升级后,因为容器规约的哈希值已更改,所有容器都会被重新启动。
  • 要验证 kubelet 服务在升级后是否成功重启,可以执行 systemctl status kubeletjournalctl -xeu kubelet 查看服务日志。
  • 不建议使用 kubeadm upgrade--config 参数和 kubeadm 配置 API 类型来重新配置集群, 这样会产生意想不到的结果。 请按照重新配置 kubeadm 集群中的步骤来进行。

升级 etcd 时的注意事项

由于 kube-apiserver 静态 Pod 始终在运行(即使你已经执行了腾空节点的操作), 因此当你执行包括 etcd 升级在内的 kubeadm 升级时,对服务器正在进行的请求将停滞, 因为要重新启动新的 etcd 静态 Pod。作为一种解决方法,可以在运行 kubeadm upgrade apply 命令之前主动停止 kube-apiserver 进程几秒钟。这样可以允许正在进行的请求完成处理并关闭现有连接, 并最大限度地减少 etcd 停机的后果。此操作可以在控制平面节点上按如下方式完成:

killall -s SIGTERM kube-apiserver # 触发 kube-apiserver 体面关闭
sleep 20 # 等待一下,以完成进行中的请求
kubeadm upgrade ... # 执行 kubeadm 升级命令

更改软件包仓库

如果你正在使用社区版的软件包仓库(pkgs.k8s.io), 你需要启用所需的 Kubernetes 小版本的软件包仓库。 这一点在更改 Kubernetes 软件包仓库文档中有详细说明。

确定要升级到哪个版本

使用操作系统的包管理器找到最新的补丁版本 Kubernetes 1.30:

# 在列表中查找最新的 1.30 版本
# 它看起来应该是 1.30.x-*,其中 x 是最新的补丁版本
sudo apt update
sudo apt-cache madison kubeadm

# 在列表中查找最新的 1.30 版本
# 它看起来应该是 1.30.x-*,其中 x 是最新的补丁版本
sudo yum list --showduplicates kubeadm --disableexcludes=kubernetes

升级控制平面节点

控制面节点上的升级过程应该每次处理一个节点。 首先选择一个要先行升级的控制面节点。该节点上必须拥有 /etc/kubernetes/admin.conf 文件。

执行 “kubeadm upgrade”

对于第一个控制面节点

  1. 升级 kubeadm:

    # 用最新的补丁版本号替换 1.30.x-* 中的 x
    sudo apt-mark unhold kubeadm && \
    sudo apt-get update && sudo apt-get install -y kubeadm='1.30.x-*' && \
    sudo apt-mark hold kubeadm
    

    # 用最新的补丁版本号替换 1.30.x-* 中的 x
    sudo yum install -y kubeadm-'1.30.x-*' --disableexcludes=kubernetes
    
  1. 验证下载操作正常,并且 kubeadm 版本正确:

    kubeadm version
    
  1. 验证升级计划:

    sudo kubeadm upgrade plan
    

    此命令检查你的集群是否可被升级,并取回你要升级的目标版本。 命令也会显示一个包含组件配置版本状态的表格。

  1. 选择要升级到的目标版本,运行合适的命令。例如:

    # 将 x 替换为你为此次升级所选择的补丁版本号
    sudo kubeadm upgrade apply v1.30.x
    

    一旦该命令结束,你应该会看到:

    [upgrade/successful] SUCCESS! Your cluster was upgraded to "v1.30.x". Enjoy!
    
    [upgrade/kubelet] Now that your control plane is upgraded, please proceed with upgrading your kubelets if you haven't already done so.
    
  1. 手动升级你的 CNI 驱动插件。

    你的容器网络接口(CNI)驱动应该提供了程序自身的升级说明。 参阅插件页面查找你的 CNI 驱动, 并查看是否需要其他升级步骤。

    如果 CNI 驱动作为 DaemonSet 运行,则在其他控制平面节点上不需要此步骤。

对于其它控制面节点

与第一个控制面节点相同,但是使用:

sudo kubeadm upgrade node

而不是:

sudo kubeadm upgrade apply

此外,不需要执行 kubeadm upgrade plan 和更新 CNI 驱动插件的操作。

腾空节点

将节点标记为不可调度并驱逐所有负载,准备节点的维护:

# 将 <node-to-drain> 替换为你要腾空的控制面节点名称
kubectl drain <node-to-drain> --ignore-daemonsets

升级 kubelet 和 kubectl

  1. 升级 kubelet 和 kubectl:

    # 用最新的补丁版本替换 1.30.x-* 中的 x
    sudo apt-mark unhold kubelet kubectl && \
    sudo apt-get update && sudo apt-get install -y kubelet='1.30.x-*' kubectl='1.30.x-*' && \
    sudo apt-mark hold kubelet kubectl
    

    # 用最新的补丁版本号替换 1.30.x-* 中的 x
    sudo yum install -y kubelet-'1.30.x-*' kubectl-'1.30.x-*' --disableexcludes=kubernetes
    
  1. 重启 kubelet:

    sudo systemctl daemon-reload
    sudo systemctl restart kubelet
    

解除节点的保护

通过将节点标记为可调度,让其重新上线:

# 将 <node-to-uncordon> 替换为你的节点名称
kubectl uncordon <node-to-uncordon>

升级工作节点

工作节点上的升级过程应该一次执行一个节点,或者一次执行几个节点, 以不影响运行工作负载所需的最小容量。

以下内容演示如何升级 Linux 和 Windows 工作节点:

验证集群的状态

在所有节点上升级 kubelet 后,通过从 kubectl 可以访问集群的任何位置运行以下命令, 验证所有节点是否再次可用:

kubectl get nodes

STATUS 应显示所有节点为 Ready 状态,并且版本号已经被更新。

从故障状态恢复

如果 kubeadm upgrade 失败并且没有回滚,例如由于执行期间节点意外关闭, 你可以再次运行 kubeadm upgrade。 此命令是幂等的,并最终确保实际状态是你声明的期望状态。

要从故障状态恢复,你还可以运行 sudo kubeadm upgrade apply --force 而无需更改集群正在运行的版本。

在升级期间,kubeadm 向 /etc/kubernetes/tmp 目录下的如下备份文件夹写入数据:

  • kubeadm-backup-etcd-<date>-<time>
  • kubeadm-backup-manifests-<date>-<time>

kubeadm-backup-etcd 包含当前控制面节点本地 etcd 成员数据的备份。 如果 etcd 升级失败并且自动回滚也无法修复,则可以将此文件夹中的内容复制到 /var/lib/etcd 进行手工修复。如果使用的是外部的 etcd,则此备份文件夹为空。

kubeadm-backup-manifests 包含当前控制面节点的静态 Pod 清单文件的备份版本。 如果升级失败并且无法自动回滚,则此文件夹中的内容可以复制到 /etc/kubernetes/manifests 目录实现手工恢复。 如果由于某些原因,在升级前后某个组件的清单未发生变化,则 kubeadm 也不会为之生成备份版本。

工作原理

kubeadm upgrade apply 做了以下工作:

  • 检查你的集群是否处于可升级状态:
    • API 服务器是可访问的
    • 所有节点处于 Ready 状态
    • 控制面是健康的
  • 强制执行版本偏差策略。
  • 确保控制面的镜像是可用的或可拉取到服务器上。
  • 如果组件配置要求版本升级,则生成替代配置与/或使用用户提供的覆盖版本配置。
  • 升级控制面组件或回滚(如果其中任何一个组件无法启动)。
  • 应用新的 CoreDNSkube-proxy 清单,并强制创建所有必需的 RBAC 规则。
  • 如果旧文件在 180 天后过期,将创建 API 服务器的新证书和密钥文件并备份旧文件。

kubeadm upgrade node 在其他控制平节点上执行以下操作:

  • 从集群中获取 kubeadm ClusterConfiguration
  • (可选操作)备份 kube-apiserver 证书。
  • 升级控制平面组件的静态 Pod 清单。
  • 为本节点升级 kubelet 配置

kubeadm upgrade node 在工作节点上完成以下工作:

  • 从集群取回 kubeadm ClusterConfiguration
  • 为本节点升级 kubelet 配置。

4.2.1.5 - 升级 Linux 节点

本页讲述了如何升级用 kubeadm 创建的 Linux 工作节点。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

更改软件包仓库

如果你正在使用社区自治的软件包仓库(pkgs.k8s.io), 你需要启用所需的 Kubernetes 小版本的软件包仓库。 这一点在更改 Kubernetes 软件包仓库文档中有详细说明。

升级工作节点

升级 kubeadm

升级 kubeadm:

# 将 1.30.x-* 中的 x 替换为最新的补丁版本
sudo apt-mark unhold kubeadm && \
sudo apt-get update && sudo apt-get install -y kubeadm='1.30.x-*' && \
sudo apt-mark hold kubeadm

# 将 1.30.x-* 中的 x 替换为最新的补丁版本
sudo yum install -y kubeadm-'1.30.x-*' --disableexcludes=kubernetes

执行 "kubeadm upgrade"

对于工作节点,下面的命令会升级本地的 kubelet 配置:

sudo kubeadm upgrade node

腾空节点

将节点标记为不可调度并驱逐所有负载,准备节点的维护:

# 在控制平面节点上执行此命令
# 将 <node-to-drain> 替换为你正腾空的节点的名称
kubectl drain <node-to-drain> --ignore-daemonsets

升级 kubelet 和 kubectl

  1. 升级 kubelet 和 kubectl:

    # 将 1.30.x-* 中的 x 替换为最新的补丁版本
    sudo apt-mark unhold kubelet kubectl && \
    sudo apt-get update && sudo apt-get install -y kubelet='1.30.x-*' kubectl='1.30.x-*' && \
    sudo apt-mark hold kubelet kubectl
    

    # 将 1.30.x-* 中的 x 替换为最新的补丁版本
    sudo yum install -y kubelet-'1.30.x-*' kubectl-'1.30.x-*' --disableexcludes=kubernetes
    
  1. 重启 kubelet:

    sudo systemctl daemon-reload
    sudo systemctl restart kubelet
    

取消对节点的保护

通过将节点标记为可调度,让节点重新上线:

# 在控制平面节点上执行此命令
# 将 <node-to-uncordon> 替换为你的节点名称
kubectl uncordon <node-to-uncordon>

接下来

4.2.1.6 - 升级 Windows 节点

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

本页解释如何升级用 kubeadm 创建的 Windows 节点。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你的 Kubernetes 服务器版本必须不低于版本 1.17. 要获知版本信息,请输入 kubectl version.

升级工作节点

升级 kubeadm

  1. 在 Windows 节点上升级 kubeadm:

    # 将 1.30.0 替换为你希望的版本
    curl.exe -Lo <kubeadm.exe 路径>  "https://dl.k8s.io/v1.30.0/bin/windows/amd64/kubeadm.exe"
    

腾空节点

  1. 在一个能访问到 Kubernetes API 的机器上,将 Windows 节点标记为不可调度并 驱逐其上的所有负载,以便准备节点维护操作:

    # 将 <要腾空的节点> 替换为你要腾空的节点的名称
    kubectl drain <要腾空的节点> --ignore-daemonsets
    

    你应该会看到类似下面的输出:

    node/ip-172-31-85-18 cordoned
    node/ip-172-31-85-18 drained
    

升级 kubelet 配置

  1. 在 Windows 节点上,执行下面的命令来同步新的 kubelet 配置:

    kubeadm upgrade node
    

升级 kubelet 和 kube-proxy

  1. 在 Windows 节点上升级并重启 kubelet:

    stop-service kubelet
    curl.exe -Lo <kubelet.exe 路径> "https://dl.k8s.io/v1.30.0/bin/windows/amd64/kubelet.exe"
    restart-service kubelet
    
  1. 在 Windows 节点上升级并重启 kube-proxy:

    stop-service kube-proxy
    curl.exe -Lo <kube-proxy.exe 路径> "https://dl.k8s.io/v1.30.0/bin/windows/amd64/kube-proxy.exe"
    restart-service kube-proxy
    

对节点执行 uncordon 操作

  1. 从一台能够访问到 Kubernetes API 的机器上,通过将节点标记为可调度,使之 重新上线:

    # 将 <要腾空的节点> 替换为你的节点名称
    kubectl uncordon <要腾空的节点>
    

接下来

4.2.1.7 - 更改 Kubernetes 软件包仓库

本页介绍了如何在升级集群时启用包含 Kubernetes 次要版本的软件包仓库。 这仅适用于使用托管在 pkgs.k8s.io 上社区自治软件包仓库的用户。 启用新的 Kubernetes 小版本的软件包仓库。与传统的软件包仓库不同, 社区自治的软件包仓库所采用的结构为每个 Kubernetes 小版本都有一个专门的软件包仓库。

准备开始

本文假设你已经在使用社区自治的软件包仓库(pkgs.k8s.io)。如果不是这种情况, 强烈建议按照官方公告中所述, 迁移到社区自治的软件包仓库。

验证是否正在使用 Kubernetes 软件包仓库

如果你不确定自己是在使用社区自治的软件包仓库还是在使用老旧的软件包仓库, 可以执行以下步骤进行验证:

打印定义 Kubernetes apt 仓库的文件的内容:

# 在你的系统上,此配置文件可能具有不同的名称
pager /etc/apt/sources.list.d/kubernetes.list

如果你看到类似以下的一行:

deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.29/deb/ /

你正在使用 Kubernetes 软件包仓库,本指南适用于你。 否则,强烈建议按照官方公告中所述, 迁移到 Kubernetes 软件包仓库。

打印定义 Kubernetes yum 仓库的文件的内容:

# 在你的系统上,此配置文件可能具有不同的名称
cat /etc/yum.repos.d/kubernetes.repo

如果你看到的 baseurl 类似以下输出中的 baseurl

[kubernetes]
name=Kubernetes
baseurl=https://pkgs.k8s.io/core:/stable:/v1.29/rpm/
enabled=1
gpgcheck=1
gpgkey=https://pkgs.k8s.io/core:/stable:/v1.29/rpm/repodata/repomd.xml.key
exclude=kubelet kubeadm kubectl

你正在使用 Kubernetes 软件包仓库,本指南适用于你。 否则,强烈建议按照官方公告中所述, 迁移到 Kubernetes 软件包仓库。

打印定义 Kubernetes zypper 仓库的文件的内容:

# 在你的系统上,此配置文件可能具有不同的名称
cat /etc/zypp/repos.d/kubernetes.repo

如果你看到的 baseurl 类似以下输出中的 baseurl

[kubernetes]
name=Kubernetes
baseurl=https://pkgs.k8s.io/core:/stable:/v1.29/rpm/
enabled=1
gpgcheck=1
gpgkey=https://pkgs.k8s.io/core:/stable:/v1.29/rpm/repodata/repomd.xml.key
exclude=kubelet kubeadm kubectl

你正在使用 Kubernetes 软件包仓库,本指南适用于你。 否则,强烈建议按照官方公告中所述, 迁移到 Kubernetes 软件包仓库。

切换到其他 Kubernetes 软件包仓库

在从一个 Kubernetes 小版本升级到另一个版本时,应执行此步骤以获取所需 Kubernetes 小版本的软件包访问权限。

  1. 使用你所选择的文本编辑器打开定义 Kubernetes apt 仓库的文件:

    nano /etc/apt/sources.list.d/kubernetes.list
    

    你应该看到一行包含当前 Kubernetes 小版本的 URL。 例如,如果你正在使用 v1.29,你应该看到类似以下的输出:

    deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.29/deb/ /
    
  1. 将 URL 中的版本更改为下一个可用的小版本,例如:

    deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.30/deb/ /
    
  1. 保存文件并退出文本编辑器。继续按照相关的升级说明进行操作。

  1. 使用你所选择的文本编辑器打开定义 Kubernetes yum 仓库的文件:

    nano /etc/yum.repos.d/kubernetes.repo
    

    你应该看到一个文件包含当前 Kubernetes 小版本的两个 URL。 例如,如果你正在使用 v1.29,你应该看到类似以下的输出:

    [kubernetes]
    name=Kubernetes
    baseurl=https://pkgs.k8s.io/core:/stable:/v1.29/rpm/
    enabled=1
    gpgcheck=1
    gpgkey=https://pkgs.k8s.io/core:/stable:/v1.29/rpm/repodata/repomd.xml.key
    exclude=kubelet kubeadm kubectl cri-tools kubernetes-cni
    
  1. 将这些 URL 中的版本更改为下一个可用的小版本,例如:

    [kubernetes]
    name=Kubernetes
    baseurl=https://pkgs.k8s.io/core:/stable:/v1.30/rpm/
    enabled=1
    gpgcheck=1
    gpgkey=https://pkgs.k8s.io/core:/stable:/v1.30/rpm/repodata/repomd.xml.key
    exclude=kubelet kubeadm kubectl cri-tools kubernetes-cni
    
  1. 保存文件并退出文本编辑器。继续按照相关的升级说明进行操作。

接下来

4.2.2 - 从 dockershim 迁移

本节提供从 dockershim 迁移到其他容器运行时的必备知识。

自从 Kubernetes 1.20 宣布 弃用 dockershim, 各类疑问随之而来:这对各类工作负载和 Kubernetes 部署会产生什么影响。 我们的弃用 Dockershim 常见问题可以帮助你更好地理解这个问题。

Dockershim 在 Kubernetes v1.24 版本已经被移除。 如果你集群内是通过 dockershim 使用 Docker Engine 作为容器运行时,并希望 Kubernetes 升级到 v1.24, 建议你迁移到其他容器运行时或使用其他方法以获得 Docker 引擎支持。

请参阅容器运行时 一节以了解可用的备选项。

带 dockershim 的 Kubernetes 版本 (1.23) 已不再支持, v1.24 很快也将不再支持。

当在迁移过程中遇到麻烦,请上报问题。 那么问题就可以及时修复,你的集群也可以进入移除 dockershim 前的就绪状态。 在 v1.24 支持结束后,如果出现影响集群的严重问题, 你需要联系你的 Kubernetes 供应商以获得支持或一次升级多个版本。

你的集群中可以有不止一种类型的节点,尽管这不是常见的情况。

下面这些任务可以帮助你完成迁移:

接下来

  • 查看容器运行时了解可选的容器运行时。
  • 如果你发现与 dockershim 迁移相关的缺陷或其他技术问题, 可以在 Kubernetes 项目报告问题

4.2.2.1 - 将节点上的容器运行时从 Docker Engine 改为 containerd

本任务给出将容器运行时从 Docker 改为 containerd 所需的步骤。 此任务适用于运行 1.23 或更早版本 Kubernetes 的集群操作人员。 同时,此任务也涉及从 dockershim 迁移到 containerd 的示例场景。 有关其他备选的容器运行时,可查阅 此页面进行拣选。

准备开始

安装 containerd。进一步的信息可参见 containerd 的安装文档。 关于一些特定的环境准备工作,请遵循 containerd 指南

腾空节点

kubectl drain <node-to-drain> --ignore-daemonsets

<node-to-drain> 替换为你所要腾空的节点的名称。

停止 Docker 守护进程

systemctl stop kubelet
systemctl disable docker.service --now

安装 Containerd

遵循此指南 了解安装 containerd 的详细步骤。

  1. 从官方的 Docker 仓库安装 containerd.io 包。关于为你所使用的 Linux 发行版来设置 Docker 仓库,以及安装 containerd.io 包的详细说明, 可参见开始使用 containerd
  1. 配置 containerd:

    sudo mkdir -p /etc/containerd
    containerd config default | sudo tee /etc/containerd/config.toml
    
  1. 重启 containerd:

    sudo systemctl restart containerd
    

启动一个 Powershell 会话,将 $Version 设置为期望的版本(例如:$Version="1.4.3"), 之后运行下面的命令:

  1. 下载 containerd:

    curl.exe -L https://github.com/containerd/containerd/releases/download/v$Version/containerd-$Version-windows-amd64.tar.gz -o containerd-windows-amd64.tar.gz
    tar.exe xvf .\containerd-windows-amd64.tar.gz
    
  1. 解压缩并执行配置:

    Copy-Item -Path ".\bin\" -Destination "$Env:ProgramFiles\containerd" -Recurse -Force
    cd $Env:ProgramFiles\containerd\
    .\containerd.exe config default | Out-File config.toml -Encoding ascii
    
    # 请审查配置信息。取决于你的安装环境,你可能需要调整:
    # - sandbox_image (Kubernetes pause 镜像)
    # - CNI 的 bin_dir 和 conf_dir 的位置
    Get-Content config.toml
    
    # (可选步骤,但强烈建议执行)将 containerd 排除在 Windows Defender 扫描之外
    Add-MpPreference -ExclusionProcess "$Env:ProgramFiles\containerd\containerd.exe"
    
  1. 启动 containerd:

    .\containerd.exe --register-service
    Start-Service containerd
    

配置 kubelet 使用 containerd 作为其容器运行时

编辑文件 /var/lib/kubelet/kubeadm-flags.env,将 containerd 运行时添加到标志中; --container-runtime-endpoint=unix:///run/containerd/containerd.sock

使用 kubeadm 的用户应该知道,kubeadm 工具将每个主机的 CRI 套接字保存在该主机对应的 Node 对象的注解中。 要更改这一注解信息,你可以在一台包含 kubeadm /etc/kubernetes/admin.conf 文件的机器上执行以下命令:

kubectl edit no <node-name>

这一命令会打开一个文本编辑器,供你在其中编辑 Node 对象。 要选择不同的文本编辑器,你可以设置 KUBE_EDITOR 环境变量。

  • 更改 kubeadm.alpha.kubernetes.io/cri-socket 值,将其从 /var/run/dockershim.sock 改为你所选择的 CRI 套接字路径 (例如:unix:///run/containerd/containerd.sock)。

    注意新的 CRI 套接字路径必须带有 unix:// 前缀。

  • 保存文本编辑器中所作的修改,这会更新 Node 对象。

重启 kubelet

systemctl start kubelet

验证节点处于健康状态

运行 kubectl get nodes -o wide,containerd 会显示为我们所更改的节点上的运行时。

移除 Docker Engine

如果节点显示正常,删除 Docker。

sudo yum remove docker-ce docker-ce-cli

sudo apt-get purge docker-ce docker-ce-cli

sudo dnf remove docker-ce docker-ce-cli

sudo apt-get purge docker-ce docker-ce-cli

上面的命令不会移除你的主机上的镜像、容器、卷或者定制的配置文件。 要删除这些内容,参阅 Docker 的指令来卸载 Docker Engine

uncordon 节点

kubectl uncordon <node-to-uncordon>

<node-to-uncordon> 替换为你之前腾空的节点的名称。

4.2.2.2 - 将 Docker Engine 节点从 dockershim 迁移到 cri-dockerd

本页面为你展示如何迁移你的 Docker Engine 节点,使之使用 cri-dockerd 而不是 dockershim。 在以下场景中,你可以遵从这里的步骤执行操作:

  • 你期望不再使用 dockershim,但仍然使用 Docker Engine 来在 Kubernetes 中运行容器。
  • 你希望升级到 Kubernetes v1.30 且你的现有集群依赖于 dockershim, 因此你必须放弃 dockershim,而 cri-dockerd 是你的一种选项。

要进一步了解 dockershim 的移除,请阅读 FAQ 页面

cri-dockerd 是什么?

在 Kubernetes v1.24 及更早版本中,你可以在 Kubernetes 中使用 Docker Engine, 依赖于一个称作 dockershim 的内置 Kubernetes 组件。 dockershim 组件在 Kubernetes v1.24 发行版本中已被移除;不过,一种来自第三方的替代品, cri-dockerd 是可供使用的。cri-dockerd 适配器允许你通过 容器运行时接口(Container Runtime Interface,CRI) 来使用 Docker Engine。

如果你想要迁移到 cri-dockerd 以便继续使用 Docker Engine 作为你的容器运行时, 你需要在所有被影响的节点上执行以下操作:

  1. 安装 cri-dockerd
  2. 隔离(Cordon)并腾空(Drain)该节点;
  3. 配置 kubelet 使用 cri-dockerd
  4. 重新启动 kubelet;
  5. 验证节点处于健康状态。

首先在非关键节点上测试这一迁移过程。

你应该针对所有希望迁移到 cri-dockerd 的节点执行以下步骤。

准备开始

隔离并腾空节点

  1. 隔离节点,阻止新的 Pod 被调度到节点上:

    kubectl cordon <NODE_NAME>
    

    <NODE_NAME> 替换为节点名称。

  1. 腾空节点以安全地逐出所有运行中的 Pod:

    kubectl drain <NODE_NAME> --ignore-daemonsets
    

配置 kubelet 使用 cri-dockerd

下面的步骤适用于用 kubeadm 工具安装的集群。如果你使用不同的工具, 你需要使用针对该工具的配置指令来修改 kubelet。

  1. 在每个被影响的节点上,打开 /var/lib/kubelet/kubeadm-flags.env 文件;
  2. --container-runtime-endpoint 标志,将其设置为 unix:///var/run/cri-dockerd.sock
  3. --container-runtime 标志修改为 remote(在 Kubernetes v1.27 及更高版本中不可用)。

kubeadm 工具将节点上的套接字存储为控制面上 Node 对象的注解。 要为每个被影响的节点更改此套接字:

  1. 编辑 Node 对象的 YAML 表示:

    KUBECONFIG=/path/to/admin.conf kubectl edit no <NODE_NAME>
    

    根据下面的说明执行替换:

    • /path/to/admin.conf:指向 kubectl 配置文件 admin.conf 的路径;
    • <NODE_NAME>:你要修改的节点的名称。
  2. kubeadm.alpha.kubernetes.io/cri-socket 标志从 /var/run/dockershim.sock 更改为 unix:///var/run/cri-dockerd.sock

  3. 保存所作更改。保存时,Node 对象被更新。

重启 kubelet

systemctl restart kubelet

验证节点处于健康状态

要检查节点是否在使用 cri-dockerd 端点, 按照找出你所使用的运行时页面所给的指令操作。 kubelet 的 --container-runtime-endpoint 标志取值应该是 unix:///var/run/cri-dockerd.sock

解除节点隔离

kubectl uncordon <NODE_NAME>

接下来

4.2.2.3 - 查明节点上所使用的容器运行时

本页面描述查明集群中节点所使用的容器运行时 的步骤。

取决于你运行集群的方式,节点所使用的容器运行时可能是事先配置好的, 也可能需要你来配置。如果你在使用托管的 Kubernetes 服务, 可能存在特定于厂商的方法来检查节点上配置的容器运行时。 本页描述的方法应该在能够执行 kubectl 的场合下都可以工作。

准备开始

安装并配置 kubectl。参见安装工具 节了解详情。

查明节点所使用的容器运行时

使用 kubectl 来读取并显示节点信息:

kubectl get nodes -o wide

输出如下面所示。CONTAINER-RUNTIME 列给出容器运行时及其版本。

对于 Docker Engine,输出类似于:

NAME         STATUS   VERSION    CONTAINER-RUNTIME
node-1       Ready    v1.16.15   docker://19.3.1
node-2       Ready    v1.16.15   docker://19.3.1
node-3       Ready    v1.16.15   docker://19.3.1

如果你的容器运行时显示为 Docker Engine,你仍然可能不会被 v1.24 中 dockershim 的移除所影响。 通过检查运行时端点,可以查看你是否在使用 dockershim。 如果你没有使用 dockershim,你就不会被影响。

对于 containerd,输出类似于这样:

# For containerd
NAME         STATUS   VERSION   CONTAINER-RUNTIME
node-1       Ready    v1.19.6   containerd://1.4.1
node-2       Ready    v1.19.6   containerd://1.4.1
node-3       Ready    v1.19.6   containerd://1.4.1

你可以在容器运行时 页面找到与容器运行时相关的更多信息。

检查当前使用的运行时端点

容器运行时使用 Unix Socket 与 kubelet 通信,这一通信使用基于 gRPC 框架的 CRI 协议。kubelet 扮演客户端,运行时扮演服务器端。 在某些情况下,你可能想知道你的节点使用的是哪个 socket。 如若集群是 Kubernetes v1.24 及以后的版本, 或许你想知道当前运行时是否是使用 dockershim 的 Docker Engine。

可以通过检查 kubelet 的参数得知当前使用的是哪个 socket。

  1. 查看 kubelet 进程的启动命令

     tr \\0 ' ' < /proc/"$(pgrep kubelet)"/cmdline
    

    如有节点上没有 tr 或者 pgrep,就需要手动检查 kubelet 的启动命令

  1. 在命令的输出中,查找 --container-runtime--container-runtime-endpoint 标志。

    • 如果你的节点使用 Kubernetes v1.23 或更早的版本,这两个参数不存在, 或者 --container-runtime 标志值不是 remote,则你在通过 dockershim 套接字使用 Docker Engine。 在 Kubernetes v1.27 及以后的版本中,--container-runtime 命令行参数不再可用。
    • 如果设置了 --container-runtime-endpoint 参数,查看套接字名称即可得知当前使用的运行时。 如若套接字 unix:///run/containerd/containerd.sock 是 containerd 的端点。

如果你将节点上的容器运行时从 Docker Engine 改变为 containerd,可在 迁移到不同的运行时 找到更多信息。或者,如果你想在 Kubernetes v1.24 及以后的版本仍使用 Docker Engine, 可以安装 CRI 兼容的适配器实现,如 cri-dockerd

4.2.2.4 - 排查 CNI 插件相关的错误

为了避免 CNI 插件相关的错误,需要验证你正在使用或升级到一个经过测试的容器运行时, 该容器运行时能够在你的 Kubernetes 版本上正常工作。

关于 "Incompatible CNI versions" 和 "Failed to destroy network for sandbox" 错误

在 containerd v1.6.0-v1.6.3 中,当配置或清除 Pod CNI 网络时,如果 CNI 插件没有升级和/或 CNI 配置文件中没有声明 CNI 配置版本时,会出现服务问题。containerd 团队报告说: “这些问题在 containerd v1.6.4 中得到了解决。”

在使用 containerd v1.6.0-v1.6.3 时,如果你不升级 CNI 插件和/或声明 CNI 配置版本, 你可能会遇到以下 "Incompatible CNI versions" 或 "Failed to destroy network for sandbox" 错误状况。

Incompatible CNI versions 错误

如果因为配置版本比插件版本新,导致你的 CNI 插件版本与配置中的插件版本无法正确匹配时, 在启动 Pod 时,containerd 日志可能会显示类似的错误信息:

incompatible CNI versions; config is \"1.0.0\", plugin supports [\"0.1.0\" \"0.2.0\" \"0.3.0\" \"0.3.1\" \"0.4.0\"]"

为了解决这个问题,需要更新你的 CNI 插件和 CNI 配置文件

Failed to destroy network for sandbox 错误

如果 CNI 插件配置中未给出插件的版本, Pod 可能可以运行。但是,停止 Pod 时会产生类似于以下错误:

ERROR[2022-04-26T00:43:24.518165483Z] StopPodSandbox for "b" failed
error="failed to destroy network for sandbox \"bbc85f891eaf060c5a879e27bba9b6b06450210161dfdecfbb2732959fb6500a\": invalid version \"\": the version is empty"

此错误使 Pod 处于未就绪状态,且仍然挂接到某网络名字空间上。 为修复这一问题,编辑 CNI 配置文件以添加缺失的版本信息。 下一次尝试停止 Pod 应该会成功。

更新你的 CNI 插件和 CNI 配置文件

如果你使用 containerd v1.6.0-v1.6.3 并遇到 "Incompatible CNI versions" 或者 "Failed to destroy network for sandbox" 错误,考虑更新你的 CNI 插件并编辑 CNI 配置文件。

以下是针对各节点要执行的典型步骤的概述:

  1. 安全地腾空并隔离节点
  1. 停止容器运行时和 kubelet 服务后,执行以下升级操作:
  • 如果你正在运行 CNI 插件,请将它们升级到最新版本。
  • 如果你使用的是非 CNI 插件,请将它们替换为 CNI 插件,并使用最新版本的插件。
  • 更新插件配置文件以指定或匹配 CNI 规范支持的插件版本, 如后文 "containerd 配置文件示例"章节所示。
  • 对于 containerd,请确保你已安装 CNI loopback 插件的最新版本(v1.0.0 或更高版本)。
  • 将节点组件(例如 kubelet)升级到 Kubernetes v1.24
  • 升级到或安装最新版本的容器运行时。
  1. 通过重新启动容器运行时和 kubelet 将节点重新加入到集群。取消节点隔离(kubectl uncordon <nodename>)。

containerd 配置文件示例

以下示例显示了 containerd 运行时 v1.6.x 的配置, 它支持最新版本的 CNI 规范(v1.0.0)。 请参阅你的插件和网络提供商的文档,以获取有关你系统配置的进一步说明。

在 Kubernetes 中,作为其默认行为,containerd 运行时为 Pod 添加一个本地回路接口:lo。 containerd 运行时通过 CNI 插件 loopback 配置本地回路接口。
loopback 插件作为 containerd 发布包的一部分,扮演 cni 角色。 containerd v1.6.0 及更高版本包括与 CNI v1.0.0 兼容的 loopback 插件以及其他默认 CNI 插件。 loopback 插件的配置由 containerd 内部完成,并被设置为使用 CNI v1.0.0。 这也意味着当这个更新版本的 containerd 启动时,loopback 插件的版本必然是 v1.0.0 或更高版本。

以下 Bash 命令生成一个 CNI 配置示例。这里,cniVersion 字段被设置为配置版本值 1.0.0, 以供 containerd 调用 CNI 桥接插件时使用。

cat << EOF | tee /etc/cni/net.d/10-containerd-net.conflist
{
 "cniVersion": "1.0.0",
 "name": "containerd-net",
 "plugins": [
   {
     "type": "bridge",
     "bridge": "cni0",
     "isGateway": true,
     "ipMasq": true,
     "promiscMode": true,
     "ipam": {
       "type": "host-local",
       "ranges": [
         [{
           "subnet": "10.88.0.0/16"
         }],
         [{
           "subnet": "2001:db8:4860::/64"
         }]
       ],
       "routes": [
         { "dst": "0.0.0.0/0" },
         { "dst": "::/0" }
       ]
     }
   },
   {
     "type": "portmap",
     "capabilities": {"portMappings": true},
     "externalSetMarkChain": "KUBE-MARK-MASQ"
   }
 ]
}
EOF

基于你的用例和网络地址规划,将前面示例中的 IP 地址范围更新为合适的值。

4.2.2.5 - 检查移除 Dockershim 是否对你有影响

Kubernetes 的 dockershim 组件使得你可以把 Docker 用作 Kubernetes 的 容器运行时。 在 Kubernetes v1.24 版本中,内建组件 dockershim 被移除。

本页讲解你的集群把 Docker 用作容器运行时的运作机制, 并提供使用 dockershim 时,它所扮演角色的详细信息, 继而展示了一组操作,可用来检查移除 dockershim 对你的工作负载是否有影响。

检查你的应用是否依赖于 Docker

即使你是通过 Docker 创建的应用容器,也不妨碍你在其他任何容器运行时上运行这些容器。 这种使用 Docker 的方式并不构成对 Docker 作为一个容器运行时的依赖。

当用了别的容器运行时之后,Docker 命令可能不工作,或者产生意外的输出。 下面是判定你是否依赖于 Docker 的方法。

  1. 确认没有特权 Pod 执行 Docker 命令(如 docker ps)、重新启动 Docker 服务(如 systemctl restart docker.service)或修改 Docker 配置文件 /etc/docker/daemon.json
  2. 检查 Docker 配置文件(如 /etc/docker/daemon.json)中容器镜像仓库的镜像(mirror)站点设置。 这些配置通常需要针对不同容器运行时来重新设置。
  3. 检查确保在 Kubernetes 基础设施之外的节点上运行的脚本和应用程序没有执行 Docker 命令。 可能的情况有:
    • SSH 到节点排查故障;
    • 节点启动脚本;
    • 直接安装在节点上的监控和安全代理。
  1. 检查执行上述特权操作的第三方工具。 详细操作请参考从 dockershim 迁移遥测和安全代理
  2. 确认没有对 dockershim 行为的间接依赖。这是一种极端情况,不太可能影响你的应用。 一些工具很可能被配置为使用了 Docker 特性,比如,基于特定指标发警报, 或者在故障排查指令的一个环节中搜索特定的日志信息。 如果你有此类配置的工具,需要在迁移之前,在测试集群上测试这类行为。

Docker 依赖详解

容器运行时是一个软件, 用来运行组成 Kubernetes Pod 的容器。 Kubernetes 负责编排和调度 Pod;在每一个节点上,kubelet 使用抽象的容器运行时接口,所以你可以任意选用兼容的容器运行时。

在早期版本中,Kubernetes 提供的兼容性支持一个容器运行时:Docker。 在 Kubernetes 后来的发展历史中,集群运营人员希望采用别的容器运行时。 于是 CRI 被设计出来满足这类灵活性需求 - 而 kubelet 亦开始支持 CRI。 然而,因为 Docker 在 CRI 规范创建之前就已经存在,Kubernetes 就创建了一个适配器组件 dockershim。 dockershim 适配器允许 kubelet 与 Docker 交互,就好像 Docker 是一个 CRI 兼容的运行时一样。

你可以阅读博文 Kubernetes 正式支持集成 Containerd

Dockershim 和 Containerd CRI 的实现对比图

切换到 Containerd 容器运行时可以消除掉中间环节。 所有相同的容器都可由 Containerd 这类容器运行时来运行。 但是现在,由于直接用容器运行时调度容器,它们对 Docker 是不可见的。 因此,你以前用来检查这些容器的 Docker 工具或漂亮的 UI 都不再可用。

你不能再使用 docker psdocker inspect 命令来获取容器信息。 由于你不能列出容器,因此你不能获取日志、停止容器,甚至不能通过 docker exec 在容器中执行命令。

你仍然可以下载镜像,或者用 docker build 命令创建它们。 但用 Docker 创建、下载的镜像,对于容器运行时和 Kubernetes,均不可见。 为了在 Kubernetes 中使用,需要把镜像推送(push)到某镜像仓库。

已知问题

一些文件系统指标缺失并且指标格式不同

Kubelet /metrics/cadvisor 端点提供 Prometheus 指标, 如 Kubernetes 系统组件指标 中所述。 如果你安装了一个依赖该端点的指标收集器,你可能会看到以下问题:

  • Docker 节点上的指标格式为 k8s_<container-name>_<pod-name>_<namespace>_<pod-uid>_<restart-count>, 但其他运行时的格式不同。例如,在 containerd 节点上它是 <container-id>

  • 一些文件系统指标缺失,如下所示:

    container_fs_inodes_free
    container_fs_inodes_total
    container_fs_io_current
    container_fs_io_time_seconds_total
    container_fs_io_time_weighted_seconds_total
    container_fs_limit_bytes
    container_fs_read_seconds_total
    container_fs_reads_merged_total
    container_fs_sector_reads_total
    container_fs_sector_writes_total
    container_fs_usage_bytes
    container_fs_write_seconds_total
    container_fs_writes_merged_total
    

解决方法

你可以通过使用 cAdvisor 作为一个独立的守护程序来缓解这个问题。

  1. 找到名称格式为 vX.Y.Z-containerd-cri 的最新 cAdvisor 版本(例如 v0.42.0-containerd-cri)。
  2. 按照 cAdvisor Kubernetes Daemonset 中的步骤来创建守护进程。
  3. 将已安装的指标收集器指向使用 cAdvisor 的 /metrics 端点。 该端点提供了全套的 Prometheus 容器指标

替代方案:

  • 使用替代的第三方指标收集解决方案。
  • 从 Kubelet 摘要 API 收集指标,该 API 在 /stats/summary 提供服务。

接下来

4.2.2.6 - 从 dockershim 迁移遥测和安全代理

Kubernetes 对与 Docker Engine 直接集成的支持已被弃用且已经被删除。 大多数应用程序不直接依赖于托管容器的运行时。但是,仍然有大量的遥测和监控代理依赖 docker 来收集容器元数据、日志和指标。 本文汇总了一些信息和链接:信息用于阐述如何探查这些依赖,链接用于解释如何迁移这些代理去使用通用的工具或其他容器运行。

遥测和安全代理

在 Kubernetes 集群中,有几种不同的方式来运行遥测或安全代理。 一些代理在以 DaemonSet 的形式运行或直接在节点上运行时,直接依赖于 Docker Engine。

为什么有些遥测代理会与 Docker Engine 通信?

从历史上看,Kubernetes 是专门为与 Docker Engine 一起工作而编写的。 Kubernetes 负责网络和调度,依靠 Docker Engine 在节点上启动并运行容器(在 Pod 内)。一些与遥测相关的信息,例如 pod 名称, 只能从 Kubernetes 组件中获得。其他数据,例如容器指标,不是容器运行时的责任。 早期遥测代理需要查询容器运行时 Kubernetes 以报告准确的信息。 随着时间的推移,Kubernetes 获得了支持多种运行时的能力, 现在支持任何兼容容器运行时接口的运行时。

一些代理和 Docker 工具紧密绑定。比如代理会用到 docker psdocker top 这类命令来列出容器和进程,用 docker logs 订阅 Docker 的日志。 如果现有集群中的节点使用 Docker Engine,在你切换到其它容器运行时的时候, 这些命令将不再起作用。

识别依赖于 Docker Engine 的 DaemonSet

如果某 Pod 想调用运行在节点上的 dockerd,该 Pod 必须满足以下两个条件之一:

  • 将包含 Docker 守护进程特权套接字的文件系统挂载为一个;或
  • 直接以卷的形式挂载 Docker 守护进程特权套接字的特定路径。

举例来说:在 COS 镜像中,Docker 通过 /var/run/docker.sock 开放其 Unix 域套接字。 这意味着 Pod 的规约中需要包含 hostPath 卷以挂载 /var/run/docker.sock

下面是一个 shell 示例脚本,用于查找包含直接映射 Docker 套接字的挂载点的 Pod。 你也可以删掉 grep '/var/run/docker.sock' 这一代码片段以查看其它挂载信息。

kubectl get pods --all-namespaces \
-o=jsonpath='{range .items[*]}{"\n"}{.metadata.namespace}{":\t"}{.metadata.name}{":\t"}{range .spec.volumes[*]}{.hostPath.path}{", "}{end}{end}' \
| sort \
| grep '/var/run/docker.sock'

检测节点代理对 Docker 的依赖性

在你的集群节点被定制、且在各个节点上均安装了额外的安全和遥测代理的场景下, 一定要和代理的供应商确认:该代理是否依赖于 Docker。

遥测和安全代理的供应商

本节旨在汇总有关可能依赖于容器运行时的各种遥测和安全代理的信息。

我们通过 谷歌文档 提供了为各类遥测和安全代理供应商准备的持续更新的迁移指导。 请与供应商联系,获取从 dockershim 迁移的最新说明。

从 dockershim 迁移

Aqua

无需更改:在运行时变更时可以无缝切换运行。

Datadog

如何迁移: Kubernetes 中对于 Docker 的弃用 名字中包含以下字符串的 Pod 可能访问 Docker Engine:

  • datadog-agent
  • datadog
  • dd-agent

Dynatrace

如何迁移: 在 Dynatrace 上从 Docker-only 迁移到通用容器指标

Containerd 支持公告:在基于 containerd 的 Kubernetes 环境的获取容器的自动化全栈可见性 CRI-O 支持公告:在基于 CRI-O 的 Kubernetes 环境获取容器的自动化全栈可见性(测试版)

名字中包含以下字符串的 Pod 可能访问 Docker:

  • dynatrace-oneagent

Falco

如何迁移: 迁移 Falco 从 dockershim Falco 支持任何与 CRI 兼容的运行时(默认配置中使用 containerd);该文档解释了所有细节。

名字中包含以下字符串的 Pod 可能访问 Docker:

  • falco

Prisma Cloud Compute

在依赖于 CRI(非 Docker)的集群上安装 Prisma Cloud 时,查看 Prisma Cloud 提供的文档

名字中包含以下字符串的 Pod 可能访问 Docker:

  • twistlock-defender-ds

SignalFx (Splunk)

SignalFx Smart Agent(已弃用)在 Kubernetes 集群上使用了多种不同的监视器, 包括 kubernetes-clusterkubelet-stats/kubelet-metricsdocker-container-statskubelet-stats 监视器此前已被供应商所弃用,现支持 kubelet-metricsdocker-container-stats 监视器受 dockershim 移除的影响。 不要为 docker-container-stats 监视器使用 Docker Engine 之外的运行时。

如何从依赖 dockershim 的代理迁移:

  1. 所配置的监视器中移除 docker-container-stats。 注意,若节点上已经安装了 Docker,在非 dockershim 环境中启用此监视器后会导致报告错误的指标; 如果节点未安装 Docker,则无法获得指标。
  2. 启用和配置 kubelet-metrics 监视器。

名字中包含以下字符串的 Pod 可能访问 Docker:

  • signalfx-agent

Yahoo Kubectl Flame

Flame 不支持 Docker 以外的容器运行时,具体可见 https://github.com/yahoo/kubectl-flame/issues/51

4.2.3 - 手动生成证书

在使用客户端证书认证的场景下,你可以通过 easyrsaopensslcfssl 等工具以手工方式生成证书。

easyrsa

easyrsa 支持以手工方式为你的集群生成证书。

  1. 下载、解压、初始化打过补丁的 easyrsa3

    curl -LO https://dl.k8s.io/easy-rsa/easy-rsa.tar.gz
    tar xzf easy-rsa.tar.gz
    cd easy-rsa-master/easyrsa3
    ./easyrsa init-pki
    
  1. 生成新的证书颁发机构(CA)。参数 --batch 用于设置自动模式; 参数 --req-cn 用于设置新的根证书的通用名称(CN)。

    ./easyrsa --batch "--req-cn=${MASTER_IP}@`date +%s`" build-ca nopass
    
  1. 生成服务器证书和秘钥。

    参数 --subject-alt-name 设置 API 服务器的 IP 和 DNS 名称。 MASTER_CLUSTER_IP 用于 API 服务器和控制器管理器,通常取 CIDR 的第一个 IP, 由 --service-cluster-ip-range 的参数提供。 参数 --days 用于设置证书的过期时间。 下面的示例假定你的默认 DNS 域名为 cluster.local

    ./easyrsa --subject-alt-name="IP:${MASTER_IP},"\
    "IP:${MASTER_CLUSTER_IP},"\
    "DNS:kubernetes,"\
    "DNS:kubernetes.default,"\
    "DNS:kubernetes.default.svc,"\
    "DNS:kubernetes.default.svc.cluster,"\
    "DNS:kubernetes.default.svc.cluster.local" \
    --days=10000 \
    build-server-full server nopass
    
  1. 拷贝文件 pki/ca.crtpki/issued/server.crtpki/private/server.key 到你的目录中。
  1. 在 API 服务器的启动参数中添加以下参数:

    --client-ca-file=/yourdirectory/ca.crt
    --tls-cert-file=/yourdirectory/server.crt
    --tls-private-key-file=/yourdirectory/server.key
    

openssl

openssl 支持以手工方式为你的集群生成证书。

  1. 生成一个 2048 位的 ca.key 文件

    openssl genrsa -out ca.key 2048
    
  1. 在 ca.key 文件的基础上,生成 ca.crt 文件(用参数 -days 设置证书有效期)

    openssl req -x509 -new -nodes -key ca.key -subj "/CN=${MASTER_IP}" -days 10000 -out ca.crt
    
  1. 生成一个 2048 位的 server.key 文件:

    openssl genrsa -out server.key 2048
    
  1. 创建一个用于生成证书签名请求(CSR)的配置文件。 保存文件(例如:csr.conf)前,记得用真实值替换掉尖括号中的值(例如:<MASTER_IP>)。 注意:MASTER_CLUSTER_IP 就像前一小节所述,它的值是 API 服务器的服务集群 IP。 下面的例子假定你的默认 DNS 域名为 cluster.local

    [ req ]
    default_bits = 2048
    prompt = no
    default_md = sha256
    req_extensions = req_ext
    distinguished_name = dn
    
    [ dn ]
    C = <country>
    ST = <state>
    L = <city>
    O = <organization>
    OU = <organization unit>
    CN = <MASTER_IP>
    
    [ req_ext ]
    subjectAltName = @alt_names
    
    [ alt_names ]
    DNS.1 = kubernetes
    DNS.2 = kubernetes.default
    DNS.3 = kubernetes.default.svc
    DNS.4 = kubernetes.default.svc.cluster
    DNS.5 = kubernetes.default.svc.cluster.local
    IP.1 = <MASTER_IP>
    IP.2 = <MASTER_CLUSTER_IP>
    
    [ v3_ext ]
    authorityKeyIdentifier=keyid,issuer:always
    basicConstraints=CA:FALSE
    keyUsage=keyEncipherment,dataEncipherment
    extendedKeyUsage=serverAuth,clientAuth
    subjectAltName=@alt_names
    
  1. 基于上面的配置文件生成证书签名请求:

    openssl req -new -key server.key -out server.csr -config csr.conf
    
  1. 基于 ca.key、ca.crt 和 server.csr 等三个文件生成服务端证书:

    openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
        -CAcreateserial -out server.crt -days 10000 \
        -extensions v3_ext -extfile csr.conf -sha256
    
  1. 查看证书签名请求:

    openssl req  -noout -text -in ./server.csr
    
  1. 查看证书:

    openssl x509  -noout -text -in ./server.crt
    

最后,为 API 服务器添加相同的启动参数。

cfssl

cfssl 是另一个用于生成证书的工具。

  1. 下载、解压并准备如下所示的命令行工具。

    注意:你可能需要根据所用的硬件体系架构和 cfssl 版本调整示例命令。

    curl -L https://github.com/cloudflare/cfssl/releases/download/v1.5.0/cfssl_1.5.0_linux_amd64 -o cfssl
    chmod +x cfssl
    curl -L https://github.com/cloudflare/cfssl/releases/download/v1.5.0/cfssljson_1.5.0_linux_amd64 -o cfssljson
    chmod +x cfssljson
    curl -L https://github.com/cloudflare/cfssl/releases/download/v1.5.0/cfssl-certinfo_1.5.0_linux_amd64 -o cfssl-certinfo
    chmod +x cfssl-certinfo
    
  1. 创建一个目录,用它保存所生成的构件和初始化 cfssl:

    mkdir cert
    cd cert
    ../cfssl print-defaults config > config.json
    ../cfssl print-defaults csr > csr.json
    
  1. 创建一个 JSON 配置文件来生成 CA 文件,例如:ca-config.json

    {
      "signing": {
        "default": {
          "expiry": "8760h"
        },
        "profiles": {
          "kubernetes": {
            "usages": [
              "signing",
              "key encipherment",
              "server auth",
              "client auth"
            ],
            "expiry": "8760h"
          }
        }
      }
    }
    
  1. 创建一个 JSON 配置文件,用于 CA 证书签名请求(CSR),例如:ca-csr.json。 确认用你需要的值替换掉尖括号中的值。

    {
      "CN": "kubernetes",
      "key": {
        "algo": "rsa",
        "size": 2048
      },
      "names":[{
        "C": "<country>",
        "ST": "<state>",
        "L": "<city>",
        "O": "<organization>",
        "OU": "<organization unit>"
      }]
    }
    
  1. 生成 CA 秘钥文件(ca-key.pem)和证书文件(ca.pem):

    ../cfssl gencert -initca ca-csr.json | ../cfssljson -bare ca
    
  1. 创建一个 JSON 配置文件,用来为 API 服务器生成秘钥和证书,例如:server-csr.json。 确认用你需要的值替换掉尖括号中的值。MASTER_CLUSTER_IP 是为 API 服务器 指定的服务集群 IP,就像前面小节描述的那样。 以下示例假定你的默认 DNS 域名为cluster.local

    {
      "CN": "kubernetes",
      "hosts": [
        "127.0.0.1",
        "<MASTER_IP>",
        "<MASTER_CLUSTER_IP>",
        "kubernetes",
        "kubernetes.default",
        "kubernetes.default.svc",
        "kubernetes.default.svc.cluster",
        "kubernetes.default.svc.cluster.local"
      ],
      "key": {
        "algo": "rsa",
        "size": 2048
      },
      "names": [{
        "C": "<country>",
        "ST": "<state>",
        "L": "<city>",
        "O": "<organization>",
        "OU": "<organization unit>"
      }]
    }
    
  1. 为 API 服务器生成秘钥和证书,默认会分别存储为server-key.pemserver.pem 两个文件。

    ../cfssl gencert -ca=ca.pem -ca-key=ca-key.pem \
         --config=ca-config.json -profile=kubernetes \
         server-csr.json | ../cfssljson -bare server
    

分发自签名的 CA 证书

客户端节点可能不认可自签名 CA 证书的有效性。 对于非生产环境,或者运行在公司防火墙后的环境,你可以分发自签名的 CA 证书到所有客户节点,并刷新本地列表以使证书生效。

在每一个客户节点,执行以下操作:

sudo cp ca.crt /usr/local/share/ca-certificates/kubernetes.crt
sudo update-ca-certificates
Updating certificates in /etc/ssl/certs...
1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d....
done.

证书 API

你可以通过 certificates.k8s.io API 提供 x509 证书,用来做身份验证, 如管理集群中的 TLS 认证文档所述。

4.2.4 - 管理内存、CPU 和 API 资源

4.2.4.1 - 为命名空间配置默认的内存请求和限制

为命名空间定义默认的内存资源限制,这样在该命名空间中每个新建的 Pod 都会被配置上内存资源限制。

本章介绍如何为命名空间配置默认的内存请求和限制。

一个 Kubernetes 集群可被划分为多个命名空间。 如果你在具有默认内存限制 的命名空间内尝试创建一个 Pod,并且这个 Pod 中的容器没有声明自己的内存资源限制, 那么控制面会为该容器设定默认的内存限制。

Kubernetes 还为某些情况指定了默认的内存请求,本章后面会进行介绍。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

在你的集群里你必须要有创建命名空间的权限。

你的集群中的每个节点必须至少有 2 GiB 的内存。

创建命名空间

创建一个命名空间,以便本练习中所建的资源与集群的其余资源相隔离。

kubectl create namespace default-mem-example

创建 LimitRange 和 Pod

以下为 LimitRange 的示例清单。 清单中声明了默认的内存请求和默认的内存限制。

apiVersion: v1
kind: LimitRange
metadata:
  name: mem-limit-range
spec:
  limits:
  - default:
      memory: 512Mi
    defaultRequest:
      memory: 256Mi
    type: Container

在 default-mem-example 命名空间创建限制范围:

kubectl apply -f https://k8s.io/examples/admin/resource/memory-defaults.yaml --namespace=default-mem-example

现在如果你在 default-mem-example 命名空间中创建一个 Pod, 并且该 Pod 中所有容器都没有声明自己的内存请求和内存限制, 控制面 会将内存的默认请求值 256MiB 和默认限制值 512MiB 应用到 Pod 上。

以下为只包含一个容器的 Pod 的清单。该容器没有声明内存请求和限制。

apiVersion: v1
kind: Pod
metadata:
  name: default-mem-demo
spec:
  containers:
  - name: default-mem-demo-ctr
    image: nginx

创建 Pod:

kubectl apply -f https://k8s.io/examples/admin/resource/memory-defaults-pod.yaml --namespace=default-mem-example

查看 Pod 的详情:

kubectl get pod default-mem-demo --output=yaml --namespace=default-mem-example

输出内容显示该 Pod 的容器有 256 MiB 的内存请求和 512 MiB 的内存限制。 这些都是 LimitRange 设置的默认值。

containers:
- image: nginx
  imagePullPolicy: Always
  name: default-mem-demo-ctr
  resources:
    limits:
      memory: 512Mi
    requests:
      memory: 256Mi

删除你的 Pod:

kubectl delete pod default-mem-demo --namespace=default-mem-example

声明容器的限制而不声明它的请求会怎么样?

以下为只包含一个容器的 Pod 的清单。该容器声明了内存限制,而没有声明内存请求。

apiVersion: v1
kind: Pod
metadata:
  name: default-mem-demo-2
spec:
  containers:
  - name: default-mem-demo-2-ctr
    image: nginx
    resources:
      limits:
        memory: "1Gi"

创建 Pod:

kubectl apply -f https://k8s.io/examples/admin/resource/memory-defaults-pod-2.yaml --namespace=default-mem-example

查看 Pod 的详情:

kubectl get pod default-mem-demo-2 --output=yaml --namespace=default-mem-example

输出结果显示容器的内存请求被设置为它的内存限制相同的值。注意该容器没有被指定默认的内存请求值 256MiB。

resources:
  limits:
    memory: 1Gi
  requests:
    memory: 1Gi

声明容器的内存请求而不声明内存限制会怎么样?

以下为只包含一个容器的 Pod 的清单。该容器声明了内存请求,但没有内存限制:

apiVersion: v1
kind: Pod
metadata:
  name: default-mem-demo-3
spec:
  containers:
  - name: default-mem-demo-3-ctr
    image: nginx
    resources:
      requests:
        memory: "128Mi"

创建 Pod:

kubectl apply -f https://k8s.io/examples/admin/resource/memory-defaults-pod-3.yaml --namespace=default-mem-example

查看 Pod 声明:

kubectl get pod default-mem-demo-3 --output=yaml --namespace=default-mem-example

输出结果显示所创建的 Pod 中,容器的内存请求为 Pod 清单中声明的值。 然而同一容器的内存限制被设置为 512MiB,此值是该命名空间的默认内存限制值。

resources:
  limits:
    memory: 512Mi
  requests:
    memory: 128Mi

设置默认内存限制和请求的动机

如果你的命名空间设置了内存 资源配额, 那么为内存限制设置一个默认值会很有帮助。 以下是内存资源配额对命名空间的施加的三条限制:

  • 命名空间中运行的每个 Pod 中的容器都必须有内存限制。 (如果为 Pod 中的每个容器声明了内存限制, Kubernetes 可以通过将其容器的内存限制相加推断出 Pod 级别的内存限制)。

  • 内存限制用来在 Pod 被调度到的节点上执行资源预留。 预留给命名空间中所有 Pod 使用的内存总量不能超过规定的限制。

  • 命名空间中所有 Pod 实际使用的内存总量也不能超过规定的限制。

当你添加 LimitRange 时:

如果该命名空间中的任何 Pod 的容器未指定内存限制, 控制面将默认内存限制应用于该容器, 这样 Pod 可以在受到内存 ResourceQuota 限制的命名空间中运行。

清理

删除你的命名空间:

kubectl delete namespace default-mem-example

接下来

集群管理员参考

应用开发者参考

4.2.4.2 - 为命名空间配置默认的 CPU 请求和限制

为命名空间定义默认的 CPU 资源限制,在该命名空间中每个新建的 Pod 都会被配置上 CPU 资源限制。

本章介绍如何为命名空间配置默认的 CPU 请求和限制。

一个 Kubernetes 集群可被划分为多个命名空间。 如果你在具有默认 CPU 限制 的命名空间内创建一个 Pod,并且这个 Pod 中任何容器都没有声明自己的 CPU 限制, 那么控制面会为容器设定默认的 CPU 限制。

Kubernetes 在一些特定情况还可以设置默认的 CPU 请求,本文后续章节将会对其进行解释。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

在你的集群里你必须要有创建命名空间的权限。

如果你还不熟悉 Kubernetes 中 1.0 CPU 的含义, 请阅读 CPU 的含义

创建命名空间

创建一个命名空间,以便本练习中创建的资源和集群的其余部分相隔离。

kubectl create namespace default-cpu-example

创建 LimitRange 和 Pod

以下为 LimitRange 的示例清单。 清单中声明了默认 CPU 请求和默认 CPU 限制。

apiVersion: v1
kind: LimitRange
metadata:
  name: cpu-limit-range
spec:
  limits:
  - default:
      cpu: 1
    defaultRequest:
      cpu: 0.5
    type: Container

在命名空间 default-cpu-example 中创建 LimitRange 对象:

kubectl apply -f https://k8s.io/examples/admin/resource/cpu-defaults.yaml --namespace=default-cpu-example

现在如果你在 default-cpu-example 命名空间中创建一个 Pod, 并且该 Pod 中所有容器都没有声明自己的 CPU 请求和 CPU 限制, 控制面会将 CPU 的默认请求值 0.5 和默认限制值 1 应用到 Pod 上。

以下为只包含一个容器的 Pod 的清单。该容器没有声明 CPU 请求和限制。

apiVersion: v1
kind: Pod
metadata:
  name: default-cpu-demo
spec:
  containers:
  - name: default-cpu-demo-ctr
    image: nginx

创建 Pod。

kubectl apply -f https://k8s.io/examples/admin/resource/cpu-defaults-pod.yaml --namespace=default-cpu-example

查看该 Pod 的声明:

kubectl get pod default-cpu-demo --output=yaml --namespace=default-cpu-example

输出显示该 Pod 的唯一的容器有 500m cpu 的 CPU 请求和 1 cpu 的 CPU 限制。 这些是 LimitRange 声明的默认值。

containers:
- image: nginx
  imagePullPolicy: Always
  name: default-cpu-demo-ctr
  resources:
    limits:
      cpu: "1"
    requests:
      cpu: 500m

你只声明容器的限制,而不声明请求会怎么样?

以下为只包含一个容器的 Pod 的清单。该容器声明了 CPU 限制,而没有声明 CPU 请求。

apiVersion: v1
kind: Pod
metadata:
  name: default-cpu-demo-2
spec:
  containers:
  - name: default-cpu-demo-2-ctr
    image: nginx
    resources:
      limits:
        cpu: "1"

创建 Pod:

kubectl apply -f https://k8s.io/examples/admin/resource/cpu-defaults-pod-2.yaml --namespace=default-cpu-example

查看你所创建的 Pod 的规约

kubectl get pod default-cpu-demo-2 --output=yaml --namespace=default-cpu-example

输出显示该容器的 CPU 请求和 CPU 限制设置相同。注意该容器没有被指定默认的 CPU 请求值 0.5 cpu

resources:
  limits:
    cpu: "1"
  requests:
    cpu: "1"

你只声明容器的请求,而不声明它的限制会怎么样?

这里给出了包含一个容器的 Pod 的示例清单。该容器声明了 CPU 请求,而没有声明 CPU 限制。

apiVersion: v1
kind: Pod
metadata:
  name: default-cpu-demo-3
spec:
  containers:
  - name: default-cpu-demo-3-ctr
    image: nginx
    resources:
      requests:
        cpu: "0.75"

创建 Pod:

kubectl apply -f https://k8s.io/examples/admin/resource/cpu-defaults-pod-3.yaml --namespace=default-cpu-example

查看你所创建的 Pod 的规约:

kubectl get pod default-cpu-demo-3 --output=yaml --namespace=default-cpu-example

输出显示你所创建的 Pod 中,容器的 CPU 请求为 Pod 清单中声明的值。 然而同一容器的 CPU 限制被设置为 1 cpu,此值是该命名空间的默认 CPU 限制值。

resources:
  limits:
    cpu: "1"
  requests:
    cpu: 750m

默认 CPU 限制和请求的动机

如果你的命名空间设置了 CPU 资源配额, 为 CPU 限制设置一个默认值会很有帮助。 以下是 CPU 资源配额对命名空间的施加的两条限制:

  • 命名空间中运行的每个 Pod 中的容器都必须有 CPU 限制。

  • CPU 限制用来在 Pod 被调度到的节点上执行资源预留。

预留给命名空间中所有 Pod 使用的 CPU 总量不能超过规定的限制。

当你添加 LimitRange 时:

如果该命名空间中的任何 Pod 的容器未指定 CPU 限制, 控制面将默认 CPU 限制应用于该容器, 这样 Pod 可以在受到 CPU ResourceQuota 限制的命名空间中运行。

清理

删除你的命名空间:

kubectl delete namespace default-cpu-example

接下来

集群管理员参考

应用开发者参考

4.2.4.3 - 配置命名空间的最小和最大内存约束

为命名空间定义一个有效的内存资源限制范围,在该命名空间中每个新创建 Pod 的内存资源是在设置的范围内。

本页介绍如何设置在名字空间 中运行的容器所使用的内存的最小值和最大值。你可以在 LimitRange 对象中指定最小和最大内存值。如果 Pod 不满足 LimitRange 施加的约束, 则无法在名字空间中创建它。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

在你的集群里你必须要有创建命名空间的权限。

集群中的每个节点都必须至少有 1 GiB 的内存可供 Pod 使用。

创建命名空间

创建一个命名空间,以便在此练习中创建的资源与集群的其余资源隔离。

kubectl create namespace constraints-mem-example

创建 LimitRange 和 Pod

下面是 LimitRange 的示例清单:

apiVersion: v1
kind: LimitRange
metadata:
  name: mem-min-max-demo-lr
spec:
  limits:
  - max:
      memory: 1Gi
    min:
      memory: 500Mi
    type: Container

创建 LimitRange:

kubectl apply -f https://k8s.io/examples/admin/resource/memory-constraints.yaml --namespace=constraints-mem-example

查看 LimitRange 的详情:

kubectl get limitrange mem-min-max-demo-lr --namespace=constraints-mem-example --output=yaml

输出显示预期的最小和最大内存约束。 但请注意,即使你没有在 LimitRange 的配置文件中指定默认值,默认值也会自动生成。

  limits:
  - default:
      memory: 1Gi
    defaultRequest:
      memory: 1Gi
    max:
      memory: 1Gi
    min:
      memory: 500Mi
    type: Container

现在,每当在 constraints-mem-example 命名空间中创建 Pod 时,Kubernetes 就会执行下面的步骤:

  • 如果 Pod 中的任何容器未声明自己的内存请求和限制,控制面将为该容器设置默认的内存请求和限制。
  • 确保该 Pod 中的每个容器的内存请求至少 500 MiB。
  • 确保该 Pod 中每个容器内存请求不大于 1 GiB。

以下为包含一个容器的 Pod 清单。该容器声明了 600 MiB 的内存请求和 800 MiB 的内存限制, 这些满足了 LimitRange 施加的最小和最大内存约束。

apiVersion: v1
kind: Pod
metadata:
  name: constraints-mem-demo
spec:
  containers:
  - name: constraints-mem-demo-ctr
    image: nginx
    resources:
      limits:
        memory: "800Mi"
      requests:
        memory: "600Mi"

创建 Pod:

kubectl apply -f https://k8s.io/examples/admin/resource/memory-constraints-pod.yaml --namespace=constraints-mem-example

确认 Pod 正在运行,并且其容器处于健康状态:

kubectl get pod constraints-mem-demo --namespace=constraints-mem-example

查看 Pod 详情:

kubectl get pod constraints-mem-demo --output=yaml --namespace=constraints-mem-example

输出结果显示该 Pod 的容器的内存请求为 600 MiB,内存限制为 800 MiB。 这些满足这个命名空间中 LimitRange 设定的限制范围。

resources:
  limits:
     memory: 800Mi
  requests:
    memory: 600Mi

删除你创建的 Pod:

kubectl delete pod constraints-mem-demo --namespace=constraints-mem-example

尝试创建一个超过最大内存限制的 Pod

以下为包含一个容器的 Pod 的清单。这个容器声明了 800 MiB 的内存请求和 1.5 GiB 的内存限制。

apiVersion: v1
kind: Pod
metadata:
  name: constraints-mem-demo-2
spec:
  containers:
  - name: constraints-mem-demo-2-ctr
    image: nginx
    resources:
      limits:
        memory: "1.5Gi"
      requests:
        memory: "800Mi"

尝试创建 Pod:

kubectl apply -f https://k8s.io/examples/admin/resource/memory-constraints-pod-2.yaml --namespace=constraints-mem-example

输出结果显示 Pod 没有创建成功,因为它定义了一个容器的内存请求超过了允许的值。

Error from server (Forbidden): error when creating "examples/admin/resource/memory-constraints-pod-2.yaml":
pods "constraints-mem-demo-2" is forbidden: maximum memory usage per Container is 1Gi, but limit is 1536Mi.

尝试创建一个不满足最小内存请求的 Pod

以下为只有一个容器的 Pod 的清单。这个容器声明了 100 MiB 的内存请求和 800 MiB 的内存限制。

apiVersion: v1
kind: Pod
metadata:
  name: constraints-mem-demo-3
spec:
  containers:
  - name: constraints-mem-demo-3-ctr
    image: nginx
    resources:
      limits:
        memory: "800Mi"
      requests:
        memory: "100Mi"

尝试创建 Pod:

kubectl apply -f https://k8s.io/examples/admin/resource/memory-constraints-pod-3.yaml --namespace=constraints-mem-example

输出结果显示 Pod 没有创建成功,因为它定义了一个容器的内存请求小于强制要求的最小值:

Error from server (Forbidden): error when creating "examples/admin/resource/memory-constraints-pod-3.yaml":
pods "constraints-mem-demo-3" is forbidden: minimum memory usage per Container is 500Mi, but request is 100Mi.

创建一个没有声明内存请求和限制的 Pod

以下为只有一个容器的 Pod 清单。该容器没有声明内存请求,也没有声明内存限制。

apiVersion: v1
kind: Pod
metadata:
  name: constraints-mem-demo-4
spec:
  containers:
  - name: constraints-mem-demo-4-ctr
    image: nginx

创建 Pod:

kubectl apply -f https://k8s.io/examples/admin/resource/memory-constraints-pod-4.yaml --namespace=constraints-mem-example

查看 Pod 详情:

kubectl get pod constraints-mem-demo-4 --namespace=constraints-mem-example --output=yaml

输出结果显示 Pod 的唯一容器内存请求为 1 GiB,内存限制为 1 GiB。容器怎样获得那些数值呢?

resources:
  limits:
    memory: 1Gi
  requests:
    memory: 1Gi

因为你的 Pod 没有为容器声明任何内存请求和限制,集群会从 LimitRange 获取默认的内存请求和限制。 应用于容器。

这意味着 Pod 的定义会显示这些值。你可以通过 kubectl describe 查看:

# 查看输出结果中的 "Requests:" 的值
kubectl describe pod constraints-mem-demo-4 --namespace=constraints-mem-example

此时,你的 Pod 可能已经运行起来也可能没有运行起来。 回想一下我们本次任务的先决条件是你的每个节点都至少有 1 GiB 的内存。 如果你的每个节点都只有 1 GiB 的内存,那将没有一个节点拥有足够的可分配内存来满足 1 GiB 的内存请求。

删除你的 Pod:

kubectl delete pod constraints-mem-demo-4 --namespace=constraints-mem-example

强制执行内存最小和最大限制

LimitRange 为命名空间设定的最小和最大内存限制只有在 Pod 创建和更新时才会强制执行。 如果你更新 LimitRange,它不会影响此前创建的 Pod。

设置内存最小和最大限制的动因

作为集群管理员,你可能想规定 Pod 可以使用的内存总量限制。例如:

  • 集群的每个节点有 2 GiB 内存。你不想接受任何请求超过 2 GiB 的 Pod,因为集群中没有节点可以满足。
  • 集群由生产部门和开发部门共享。你希望允许产品部门的负载最多耗用 8 GiB 内存, 但是开发部门的负载最多可使用 512 MiB。 这时,你可以为产品部门和开发部门分别创建名字空间,并为各个名字空间设置内存约束。

清理

删除你的命名空间:

kubectl delete namespace constraints-mem-example

接下来

集群管理员参考

应用开发者参考

4.2.4.4 - 为命名空间配置 CPU 最小和最大约束

为命名空间定义一个有效的 CPU 资源限制范围,使得在该命名空间中所有新建 Pod 的 CPU 资源是在你所设置的范围内。

本页介绍如何为命名空间中的容器和 Pod 设置其所使用的 CPU 资源的最小和最大值。你可以通过 LimitRange 对象声明 CPU 的最小和最大值. 如果 Pod 不能满足 LimitRange 的限制,就无法在该命名空间中被创建。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

在你的集群里你必须要有创建命名空间的权限。

集群中的每个节点都必须至少有 1.0 个 CPU 可供 Pod 使用。

请阅读 CPU 的含义 理解 "1 CPU" 在 Kubernetes 中的含义。

创建命名空间

创建一个命名空间,以便本练习中创建的资源和集群的其余资源相隔离。

kubectl create namespace constraints-cpu-example

创建 LimitRange 和 Pod

以下为 LimitRange 的示例清单:

apiVersion: v1
kind: LimitRange
metadata:
  name: cpu-min-max-demo-lr
spec:
  limits:
  - max:
      cpu: "800m"
    min:
      cpu: "200m"
    type: Container

创建 LimitRange:

kubectl apply -f https://k8s.io/examples/admin/resource/cpu-constraints.yaml --namespace=constraints-cpu-example

查看 LimitRange 详情:

kubectl get limitrange cpu-min-max-demo-lr --output=yaml --namespace=constraints-cpu-example

输出结果显示 CPU 的最小和最大限制符合预期。但需要注意的是,尽管你在 LimitRange 的配置文件中你没有声明默认值,默认值也会被自动创建。

limits:
- default:
    cpu: 800m
  defaultRequest:
    cpu: 800m
  max:
    cpu: 800m
  min:
    cpu: 200m
  type: Container

现在,每当你在 constraints-cpu-example 命名空间中创建 Pod 时,或者某些其他的 Kubernetes API 客户端创建了等价的 Pod 时,Kubernetes 就会执行下面的步骤:

  • 如果 Pod 中的任何容器未声明自己的 CPU 请求和限制,控制面将为该容器设置默认的 CPU 请求和限制。

  • 确保该 Pod 中的每个容器的 CPU 请求至少 200 millicpu。

  • 确保该 Pod 中每个容器 CPU 请求不大于 800 millicpu。

以下为某个仅包含一个容器的 Pod 的清单。 该容器声明了 CPU 请求 500 millicpu 和 CPU 限制 800 millicpu。 这些参数满足了 LimitRange 对象为此名字空间规定的 CPU 最小和最大限制。

apiVersion: v1
kind: Pod
metadata:
  name: constraints-cpu-demo
spec:
  containers:
  - name: constraints-cpu-demo-ctr
    image: nginx
    resources:
      limits:
        cpu: "800m"
      requests:
        cpu: "500m"

创建 Pod:

kubectl apply -f https://k8s.io/examples/admin/resource/cpu-constraints-pod.yaml --namespace=constraints-cpu-example

确认 Pod 正在运行,并且其容器处于健康状态:

kubectl get pod constraints-cpu-demo --namespace=constraints-cpu-example

查看 Pod 的详情:

kubectl get pod constraints-cpu-demo --output=yaml --namespace=constraints-cpu-example

输出结果显示该 Pod 的容器的 CPU 请求为 500 millicpu,CPU 限制为 800 millicpu。 这些参数满足 LimitRange 规定的限制范围。

resources:
  limits:
    cpu: 800m
  requests:
    cpu: 500m

删除 Pod

kubectl delete pod constraints-cpu-demo --namespace=constraints-cpu-example

尝试创建一个超过最大 CPU 限制的 Pod

这里给出了包含一个容器的 Pod 清单。容器声明了 500 millicpu 的 CPU 请求和 1.5 CPU 的 CPU 限制。

apiVersion: v1
kind: Pod
metadata:
  name: constraints-cpu-demo-2
spec:
  containers:
  - name: constraints-cpu-demo-2-ctr
    image: nginx
    resources:
      limits:
        cpu: "1.5"
      requests:
        cpu: "500m"

尝试创建 Pod:

kubectl apply -f https://k8s.io/examples/admin/resource/cpu-constraints-pod-2.yaml --namespace=constraints-cpu-example

输出结果表明 Pod 没有创建成功,因为其中定义了一个无法被接受的容器。 该容器之所以无法被接受是因为其中设定了过高的 CPU 限制值:

Error from server (Forbidden): error when creating "examples/admin/resource/cpu-constraints-pod-2.yaml":
pods "constraints-cpu-demo-2" is forbidden: maximum cpu usage per Container is 800m, but limit is 1500m.

尝试创建一个不满足最小 CPU 请求的 Pod

以下为某个只有一个容器的 Pod 的清单。该容器声明了 CPU 请求 100 millicpu 和 CPU 限制 800 millicpu。

apiVersion: v1
kind: Pod
metadata:
  name: constraints-cpu-demo-3
spec:
  containers:
  - name: constraints-cpu-demo-3-ctr
    image: nginx
    resources:
      limits:
        cpu: "800m"
      requests:
        cpu: "100m"

尝试创建 Pod:

kubectl apply -f https://k8s.io/examples/admin/resource/cpu-constraints-pod-3.yaml --namespace=constraints-cpu-example

输出结果显示 Pod 没有创建成功,因为其中定义了一个无法被接受的容器。 该容器无法被接受的原因是其中所设置的 CPU 请求小于最小值的限制:

Error from server (Forbidden): error when creating "examples/admin/resource/cpu-constraints-pod-3.yaml":
pods "constraints-cpu-demo-3" is forbidden: minimum cpu usage per Container is 200m, but request is 100m.

创建一个没有声明 CPU 请求和 CPU 限制的 Pod

以下为一个只有一个容器的 Pod 的清单。该容器没有声明 CPU 请求,也没有声明 CPU 限制。

apiVersion: v1
kind: Pod
metadata:
  name: constraints-cpu-demo-4
spec:
  containers:
  - name: constraints-cpu-demo-4-ctr
    image: vish/stress

创建 Pod:

kubectl apply -f https://k8s.io/examples/admin/resource/cpu-constraints-pod-4.yaml --namespace=constraints-cpu-example

查看 Pod 的详情:

kubectl get pod constraints-cpu-demo-4 --namespace=constraints-cpu-example --output=yaml

输出结果显示 Pod 的唯一容器的 CPU 请求为 800 millicpu,CPU 限制为 800 millicpu。

容器是怎样获得这些数值的呢?

resources:
  limits:
    cpu: 800m
  requests:
    cpu: 800m

因为这一容器没有声明自己的 CPU 请求和限制, 控制面会根据命名空间中配置 LimitRange 设置默认的 CPU 请求和限制

此时,你的 Pod 可能已经运行起来也可能没有运行起来。 回想一下我们本次任务的先决条件是你的每个节点都至少有 1 CPU。 如果你的每个节点都只有 1 CPU,那将没有一个节点拥有足够的可分配 CPU 来满足 800 millicpu 的请求。 如果你在用的节点恰好有 2 CPU,那么有可能有足够的 CPU 来满足 800 millicpu 的请求。

删除你的 Pod:

kubectl delete pod constraints-cpu-demo-4 --namespace=constraints-cpu-example

CPU 最小和最大限制的强制执行

只有当 Pod 创建或者更新时,LimitRange 为命名空间规定的 CPU 最小和最大限制才会被强制执行。 如果你对 LimitRange 进行修改,那不会影响此前创建的 Pod。

最小和最大 CPU 限制范围的动机

作为集群管理员,你可能想设定 Pod 可以使用的 CPU 资源限制。例如:

  • 集群中的每个节点有两个 CPU。你不想接受任何请求超过 2 个 CPU 的 Pod, 因为集群中没有节点可以支持这种请求。
  • 你的生产和开发部门共享一个集群。你想允许生产工作负载消耗 3 个 CPU, 而开发部门工作负载的消耗限制为 1 个 CPU。 你可以为生产和开发创建不同的命名空间,并且为每个命名空间都应用 CPU 限制。

清理

删除你的命名空间:

kubectl delete namespace constraints-cpu-example

接下来

集群管理员参考

应用开发者参考

4.2.4.5 - 为命名空间配置内存和 CPU 配额

为命名空间定义总的 CPU 和内存资源限制。

本文介绍如何为命名空间下运行的所有 Pod 设置总的内存和 CPU 配额。你可以通过使用 ResourceQuota 对象设置配额.

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

在你的集群里你必须要有创建命名空间的权限。

集群中每个节点至少有 1 GiB 的内存。

创建命名空间

创建一个命名空间,以便本练习中创建的资源和集群的其余部分相隔离。

kubectl create namespace quota-mem-cpu-example

创建 ResourceQuota

下面是 ResourceQuota 的示例清单:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: mem-cpu-demo
spec:
  hard:
    requests.cpu: "1"
    requests.memory: 1Gi
    limits.cpu: "2"
    limits.memory: 2Gi

创建 ResourceQuota:

kubectl apply -f https://k8s.io/examples/admin/resource/quota-mem-cpu.yaml --namespace=quota-mem-cpu-example

查看 ResourceQuota 详情:

kubectl get resourcequota mem-cpu-demo --namespace=quota-mem-cpu-example --output=yaml

ResourceQuota 在 quota-mem-cpu-example 命名空间中设置了如下要求:

  • 在该命名空间中的每个 Pod 的所有容器都必须要有内存请求和限制,以及 CPU 请求和限制。
  • 在该命名空间中所有 Pod 的内存请求总和不能超过 1 GiB。
  • 在该命名空间中所有 Pod 的内存限制总和不能超过 2 GiB。
  • 在该命名空间中所有 Pod 的 CPU 请求总和不能超过 1 cpu。
  • 在该命名空间中所有 Pod 的 CPU 限制总和不能超过 2 cpu。

请阅读 CPU 的含义 理解 "1 CPU" 在 Kubernetes 中的含义。

创建 Pod

以下是 Pod 的示例清单:

apiVersion: v1
kind: Pod
metadata:
  name: quota-mem-cpu-demo
spec:
  containers:
  - name: quota-mem-cpu-demo-ctr
    image: nginx
    resources:
      limits:
        memory: "800Mi"
        cpu: "800m"
      requests:
        memory: "600Mi"
        cpu: "400m"

创建 Pod:

kubectl apply -f https://k8s.io/examples/admin/resource/quota-mem-cpu-pod.yaml --namespace=quota-mem-cpu-example

确认 Pod 正在运行,并且其容器处于健康状态:

kubectl get pod quota-mem-cpu-demo --namespace=quota-mem-cpu-example

再查看 ResourceQuota 的详情:

kubectl get resourcequota mem-cpu-demo --namespace=quota-mem-cpu-example --output=yaml

输出结果显示了配额以及有多少配额已经被使用。你可以看到 Pod 的内存和 CPU 请求值及限制值没有超过配额。

status:
  hard:
    limits.cpu: "2"
    limits.memory: 2Gi
    requests.cpu: "1"
    requests.memory: 1Gi
  used:
    limits.cpu: 800m
    limits.memory: 800Mi
    requests.cpu: 400m
    requests.memory: 600Mi

如果有 jq 工具的话,你可以通过(使用 JSONPath) 直接查询 used 字段的值,并且输出整齐的 JSON 格式。

kubectl get resourcequota mem-cpu-demo --namespace=quota-mem-cpu-example -o jsonpath='{ .status.used }' | jq .

尝试创建第二个 Pod

以下为第二个 Pod 的清单:

apiVersion: v1
kind: Pod
metadata:
  name: quota-mem-cpu-demo-2
spec:
  containers:
  - name: quota-mem-cpu-demo-2-ctr
    image: redis
    resources:
      limits:
        memory: "1Gi"
        cpu: "800m"
      requests:
        memory: "700Mi"
        cpu: "400m"

在清单中,你可以看到 Pod 的内存请求为 700 MiB。 请注意新的内存请求与已经使用的内存请求之和超过了内存请求的配额: 600 MiB + 700 MiB > 1 GiB。

尝试创建 Pod:

kubectl apply -f https://k8s.io/examples/admin/resource/quota-mem-cpu-pod-2.yaml --namespace=quota-mem-cpu-example

第二个 Pod 不能被创建成功。输出结果显示创建第二个 Pod 会导致内存请求总量超过内存请求配额。

Error from server (Forbidden): error when creating "examples/admin/resource/quota-mem-cpu-pod-2.yaml":
pods "quota-mem-cpu-demo-2" is forbidden: exceeded quota: mem-cpu-demo,
requested: requests.memory=700Mi,used: requests.memory=600Mi, limited: requests.memory=1Gi

讨论

如你在本练习中所见,你可以用 ResourceQuota 限制命名空间中所有 Pod 的内存请求总量。 同样你也可以限制内存限制总量、CPU 请求总量、CPU 限制总量。

除了可以管理命名空间资源使用的总和,如果你想限制单个 Pod,或者限制这些 Pod 中的容器资源, 可以使用 LimitRange 实现这类的功能。

清理

删除你的命名空间:

kubectl delete namespace quota-mem-cpu-example

接下来

集群管理员参考

应用开发者参考

4.2.4.6 - 配置命名空间下 Pod 配额

限制在命名空间中创建的 Pod 数量。

本文主要介绍如何在命名空间中设置可运行 Pod 总数的配额。 你可以通过使用 ResourceQuota 对象来配置配额。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

在你的集群里你必须要有创建命名空间的权限。

创建一个命名空间

首先创建一个命名空间,这样可以将本次操作中创建的资源与集群其他资源隔离开来。

kubectl create namespace quota-pod-example

创建 ResourceQuota

下面是 ResourceQuota 的示例清单:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: pod-demo
spec:
  hard:
    pods: "2"

创建 ResourceQuota:

kubectl apply -f https://k8s.io/examples/admin/resource/quota-pod.yaml --namespace=quota-pod-example

查看资源配额的详细信息:

kubectl get resourcequota pod-demo --namespace=quota-pod-example --output=yaml

从输出的信息我们可以看到,该命名空间下 Pod 的配额是 2 个,目前创建的 Pod 数为 0, 配额使用率为 0。

spec:
  hard:
    pods: "2"
status:
  hard:
    pods: "2"
  used:
    pods: "0"

下面是一个 Deployment 的示例清单:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-quota-demo
spec:
  selector:
    matchLabels:
      purpose: quota-demo
  replicas: 3
  template:
    metadata:
      labels:
        purpose: quota-demo
    spec:
      containers:
      - name: pod-quota-demo
        image: nginx

在清单中,replicas: 3 告诉 Kubernetes 尝试创建三个新的 Pod, 且运行相同的应用。

创建这个 Deployment:

kubectl apply -f https://k8s.io/examples/admin/resource/quota-pod-deployment.yaml --namespace=quota-pod-example

查看 Deployment 的详细信息:

kubectl get deployment pod-quota-demo --namespace=quota-pod-example --output=yaml

从输出的信息显示,即使 Deployment 指定了三个副本, 也只有两个 Pod 被创建,原因是之前已经定义了配额:

spec:
  ...
  replicas: 3
...
status:
  availableReplicas: 2
...
lastUpdateTime: 2021-04-02T20:57:05Z
    message: 'unable to create pods: pods "pod-quota-demo-1650323038-" is forbidden:
      exceeded quota: pod-demo, requested: pods=1, used: pods=2, limited: pods=2'

资源的选择

在此任务中,你定义了一个限制 Pod 总数的 ResourceQuota, 你也可以限制其他类型对象的总数。 例如,你可以限制在一个命名空间中可以创建的 CronJob 的数量。

清理

删除你的命名空间:

kubectl delete namespace quota-pod-example

接下来

集群管理人员参考

应用开发人员参考

4.2.5 - 安装网络策略驱动

4.2.5.1 - 使用 Antrea 提供 NetworkPolicy

本页展示了如何在 kubernetes 中安装和使用 Antrea CNI 插件。 要了解 Antrea 项目的背景,请阅读 Antrea 介绍

准备开始

你需要拥有一个 kuernetes 集群。 遵循 kubeadm 入门指南自行创建一个。

使用 kubeadm 部署 Antrea

遵循入门指南 为 kubeadm 部署 Antrea 。

接下来

一旦你的集群已经运行,你可以遵循 声明网络策略 来尝试 Kubernetes NetworkPolicy。

4.2.5.2 - 使用 Calico 提供 NetworkPolicy

本页展示了几种在 Kubernetes 上快速创建 Calico 集群的方法。

准备开始

确定你想部署一个云版本还是本地版本的集群。

在 Google Kubernetes Engine (GKE) 上创建一个 Calico 集群

先决条件gcloud

  1. 启动一个带有 Calico 的 GKE 集群,需要加上参数 --enable-network-policy

    语法

    gcloud container clusters create [CLUSTER_NAME] --enable-network-policy
    

    示例

    gcloud container clusters create my-calico-cluster --enable-network-policy
    
  1. 使用如下命令验证部署是否正确。

    kubectl get pods --namespace=kube-system
    

    Calico 的 Pod 名以 calico 打头,检查确认每个 Pod 状态为 Running

使用 kubeadm 创建一个本地 Calico 集群

使用 kubeadm 在 15 分钟内得到一个本地单主机 Calico 集群,请参考 Calico 快速入门

接下来

集群运行后, 你可以按照声明网络策略去尝试使用 Kubernetes NetworkPolicy。

4.2.5.3 - 使用 Cilium 提供 NetworkPolicy

本页展示如何使用 Cilium 提供 NetworkPolicy。

关于 Cilium 的背景知识,请阅读 Cilium 介绍

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

在 Minikube 上部署 Cilium 用于基本测试

为了轻松熟悉 Cilium,你可以根据 Cilium Kubernetes 入门指南 在 minikube 中执行一个 Cilium 的基本 DaemonSet 安装。

要启动 minikube,需要的最低版本为 1.5.2,使用下面的参数运行:

minikube version
minikube version: v1.5.2
minikube start --network-plugin=cni

对于 minikube 你可以使用 Cilium 的 CLI 工具安装它。 为此,先用以下命令下载最新版本的 CLI:

curl -LO https://github.com/cilium/cilium-cli/releases/latest/download/cilium-linux-amd64.tar.gz

然后用以下命令将下载的文件解压缩到你的 /usr/local/bin 目录:

sudo tar xzvfC cilium-linux-amd64.tar.gz /usr/local/bin
rm cilium-linux-amd64.tar.gz

运行上述命令后,你现在可以用以下命令安装 Cilium:

cilium install

随后 Cilium 将自动检测集群配置,并创建和安装合适的组件以成功完成安装。 这些组件为:

  • Secret cilium-ca 中的证书机构 (CA) 和 Hubble(Cilium 的可观测层)所用的证书。
  • 服务账号。
  • 集群角色。
  • ConfigMap。
  • Agent DaemonSet 和 Operator Deployment。

安装之后,你可以用 cilium status 命令查看 Cilium Deployment 的整体状态。 在此处查看 status 命令的预期输出。

入门指南其余的部分用一个示例应用说明了如何强制执行 L3/L4(即 IP 地址 + 端口)的安全策略以及 L7 (如 HTTP)的安全策略。

部署 Cilium 用于生产用途

关于部署 Cilium 用于生产的详细说明,请参见 Cilium Kubernetes 安装指南。 此文档包括详细的需求、说明和生产用途 DaemonSet 文件示例。

了解 Cilium 组件

部署使用 Cilium 的集群会添加 Pod 到 kube-system 命名空间。要查看 Pod 列表,运行:

kubectl get pods --namespace=kube-system -l k8s-app=cilium

你将看到像这样的 Pod 列表:

NAME           READY   STATUS    RESTARTS   AGE
cilium-kkdhz   1/1     Running   0          3m23s
...

你的集群中的每个节点上都会运行一个 cilium Pod,通过使用 Linux BPF 针对该节点上的 Pod 的入站、出站流量实施网络策略控制。

接下来

集群运行后, 你可以按照声明网络策略试用基于 Cilium 的 Kubernetes NetworkPolicy。玩得开心,如果你有任何疑问,请到 Cilium Slack 频道联系我们。

4.2.5.4 - 使用 kube-router 提供 NetworkPolicy

本页展示如何使用 Kube-router 提供 NetworkPolicy。

准备开始

你需要拥有一个运行中的 Kubernetes 集群。如果你还没有集群,可以使用任意的集群 安装程序如 Kops、Bootkube、Kubeadm 等创建一个。

安装 kube-router 插件

kube-router 插件自带一个网络策略控制器,监视来自于 Kubernetes API 服务器的 NetworkPolicy 和 Pod 的变化,根据策略指示配置 iptables 规则和 ipsets 来允许或阻止流量。 请根据 通过集群安装程序尝试 kube-router 指南安装 kube-router 插件。

接下来

在你安装了 kube-router 插件后,可以参考 声明网络策略 去尝试使用 Kubernetes NetworkPolicy。

4.2.5.5 - 使用 Romana 提供 NetworkPolicy

本页展示如何使用 Romana 作为 NetworkPolicy。

准备开始

完成 kubeadm 入门指南中的 1、2、3 步。

使用 kubeadm 安装 Romana

按照容器化安装指南, 使用 kubeadm 安装。

应用网络策略

使用以下的一种方式应用网络策略:

接下来

Romana 安装完成后,你可以按照 声明网络策略 去尝试使用 Kubernetes NetworkPolicy。

4.2.5.6 - 使用 Weave Net 提供 NetworkPolicy

本页展示如何使用 Weave Net 提供 NetworkPolicy。

准备开始

你需要拥有一个 Kubernetes 集群。按照 kubeadm 入门指南 来启动一个。

安装 Weave Net 插件

按照通过插件集成 Kubernetes 指南执行安装。

Kubernetes 的 Weave Net 插件带有 网络策略控制器, 可自动监控 Kubernetes 所有名字空间的 NetworkPolicy 注释, 配置 iptables 规则以允许或阻止策略指示的流量。

测试安装

验证 weave 是否有效。

输入以下命令:

kubectl get pods -n kube-system -o wide

输出类似这样:

NAME                                    READY     STATUS    RESTARTS   AGE       IP              NODE
weave-net-1t1qg                         2/2       Running   0          9d        192.168.2.10    worknode3
weave-net-231d7                         2/2       Running   1          7d        10.2.0.17       worknodegpu
weave-net-7nmwt                         2/2       Running   3          9d        192.168.2.131   masternode
weave-net-pmw8w                         2/2       Running   0          9d        192.168.2.216   worknode2

每个 Node 都有一个 weave Pod,所有 Pod 都是 Running2/2 READY。 (2/2 表示每个 Pod 都有 weaveweave-npc。)

接下来

安装 Weave Net 插件后,你可以参考 声明网络策略 来试用 Kubernetes NetworkPolicy。 如果你有任何疑问,请通过 Slack 上的 #weave-community 频道或者 Weave 用户组 联系我们。

4.2.6 - 使用 Kubernetes API 访问集群

本页展示了如何使用 Kubernetes API 访问集群。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

访问 Kubernetes API

使用 kubectl 进行首次访问

首次访问 Kubernetes API 时,请使用 Kubernetes 命令行工具 kubectl

要访问集群,你需要知道集群位置并拥有访问它的凭证。 通常,当你完成入门指南时,这会自动设置完成,或者由其他人设置好集群并将凭证和位置提供给你。

使用此命令检查 kubectl 已知的位置和凭证:

kubectl config view

许多样例 提供了使用 kubectl 的介绍。完整文档请见 kubectl 手册

直接访问 REST API

kubectl 处理对 API 服务器的定位和身份验证。如果你想通过 http 客户端(如 curlwget 或浏览器)直接访问 REST API,你可以通过多种方式对 API 服务器进行定位和身份验证:

  1. 以代理模式运行 kubectl(推荐)。 推荐使用此方法,因为它用存储的 apiserver 位置并使用自签名证书验证 API 服务器的标识。 使用这种方法无法进行中间人(MITM)攻击。
  2. 另外,你可以直接为 HTTP 客户端提供位置和身份认证。 这适用于被代理混淆的客户端代码。 为防止中间人攻击,你需要将根证书导入浏览器。

使用 Go 或 Python 客户端库可以在代理模式下访问 kubectl。

使用 kubectl 代理

下列命令使 kubectl 运行在反向代理模式下。它处理 API 服务器的定位和身份认证。

像这样运行它:

kubectl proxy --port=8080 &

参见 kubectl 代理 获取更多细节。

然后你可以通过 curl,wget,或浏览器浏览 API,像这样:

curl http://localhost:8080/api/

输出类似如下:

{
  "versions": [
    "v1"
  ],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "10.0.1.149:443"
    }
  ]
}

不使用 kubectl 代理

通过将身份认证令牌直接传给 API 服务器,可以避免使用 kubectl 代理,像这样:

使用 grep/cut 方式:

# 查看所有的集群,因为你的 .kubeconfig 文件中可能包含多个上下文
kubectl config view -o jsonpath='{"Cluster name\tServer\n"}{range .clusters[*]}{.name}{"\t"}{.cluster.server}{"\n"}{end}'

# 从上述命令输出中选择你要与之交互的集群的名称
export CLUSTER_NAME="some_server_name"

# 指向引用该集群名称的 API 服务器
APISERVER=$(kubectl config view -o jsonpath="{.clusters[?(@.name==\"$CLUSTER_NAME\")].cluster.server}")

# 创建一个 secret 来保存默认服务账户的令牌
kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: default-token
  annotations:
    kubernetes.io/service-account.name: default
type: kubernetes.io/service-account-token
EOF

# 等待令牌控制器使用令牌填充 secret:
while ! kubectl describe secret default-token | grep -E '^token' >/dev/null; do
  echo "waiting for token..." >&2
  sleep 1
done

# 获取令牌
TOKEN=$(kubectl get secret default-token -o jsonpath='{.data.token}' | base64 --decode)

# 使用令牌玩转 API
curl -X GET $APISERVER/api --header "Authorization: Bearer $TOKEN" --insecure

输出类似如下:

{
  "kind": "APIVersions",
  "versions": [
    "v1"
  ],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "10.0.1.149:443"
    }
  ]
}

上面例子使用了 --insecure 标志位。这使它易受到 MITM 攻击。 当 kubectl 访问集群时,它使用存储的根证书和客户端证书访问服务器。 (已安装在 ~/.kube 目录下)。 由于集群认证通常是自签名的,因此可能需要特殊设置才能让你的 http 客户端使用根证书。

在一些集群中,API 服务器不需要身份认证;它运行在本地,或由防火墙保护着。 对此并没有一个标准。 配置对 API 的访问 讲解了作为集群管理员可如何对此进行配置。

编程方式访问 API

Kubernetes 官方支持 GoPythonJavadotnetJavaScriptHaskell 语言的客户端库。还有一些其他客户端库由对应作者而非 Kubernetes 团队提供并维护。 参考客户端库了解如何使用其他语言来访问 API 以及如何执行身份认证。

Go 客户端

Go 客户端可以使用与 kubectl 命令行工具相同的 kubeconfig 文件 定位和验证 API 服务器。参见这个 例子

package main

import (
  "context"
  "fmt"
  "k8s.io/apimachinery/pkg/apis/meta/v1"
  "k8s.io/client-go/kubernetes"
  "k8s.io/client-go/tools/clientcmd"
)

func main() {
  // 在 kubeconfig 中使用当前上下文
  // path-to-kubeconfig -- 例如 /root/.kube/config
  config, _ := clientcmd.BuildConfigFromFlags("", "<path-to-kubeconfig>")
  // 创建 clientset
  clientset, _ := kubernetes.NewForConfig(config)
  // 访问 API 以列出 Pod
  pods, _ := clientset.CoreV1().Pods("").List(context.TODO(), v1.ListOptions{})
  fmt.Printf("There are %d pods in the cluster\n", len(pods.Items))
}

如果该应用程序部署为集群中的一个 Pod,请参阅从 Pod 内访问 API

Python 客户端

要使用 Python 客户端,运行下列命令: pip install kubernetes。 参见 Python 客户端库主页了解更多安装选项。

Python 客户端可以使用与 kubectl 命令行工具相同的 kubeconfig 文件 定位和验证 API 服务器。参见这个 例子

from kubernetes import client, config

config.load_kube_config()

v1=client.CoreV1Api()
print("Listing pods with their IPs:")
ret = v1.list_pod_for_all_namespaces(watch=False)
for i in ret.items:
    print("%s\t%s\t%s" % (i.status.pod_ip, i.metadata.namespace, i.metadata.name))

Java 客户端

要安装 Java 客户端,运行:

# 克隆 Java 库
git clone --recursive https://github.com/kubernetes-client/java

# 安装项目文件、POM 等
cd java
mvn install

参阅 https://github.com/kubernetes-client/java/releases 了解当前支持的版本。

Java 客户端可以使用 kubectl 命令行所使用的 kubeconfig 文件 以定位 API 服务器并向其认证身份。 参看此示例

package io.kubernetes.client.examples;

import io.kubernetes.client.ApiClient;
import io.kubernetes.client.ApiException;
import io.kubernetes.client.Configuration;
import io.kubernetes.client.apis.CoreV1Api;
import io.kubernetes.client.models.V1Pod;
import io.kubernetes.client.models.V1PodList;
import io.kubernetes.client.util.ClientBuilder;
import io.kubernetes.client.util.KubeConfig;
import java.io.FileReader;
import java.io.IOException;

/**
 * A simple example of how to use the Java API from an application outside a kubernetes cluster
 *
 * <p>Easiest way to run this: mvn exec:java
 * -Dexec.mainClass="io.kubernetes.client.examples.KubeConfigFileClientExample"
 *
 */
public class KubeConfigFileClientExample {
  public static void main(String[] args) throws IOException, ApiException {

    // file path to your KubeConfig
    String kubeConfigPath = "~/.kube/config";

    // loading the out-of-cluster config, a kubeconfig from file-system
    ApiClient client =
        ClientBuilder.kubeconfig(KubeConfig.loadKubeConfig(new FileReader(kubeConfigPath))).build();

    // set the global default api-client to the in-cluster one from above
    Configuration.setDefaultApiClient(client);

    // the CoreV1Api loads default api-client from global configuration.
    CoreV1Api api = new CoreV1Api();

    // invokes the CoreV1Api client
    V1PodList list = api.listPodForAllNamespaces(null, null, null, null, null, null, null, null, null);
    System.out.println("Listing all pods: ");
    for (V1Pod item : list.getItems()) {
      System.out.println(item.getMetadata().getName());
    }
  }
}

.Net 客户端

要使用 .Net 客户端,运行下面的命令: dotnet add package KubernetesClient --version 1.6.1。 参见 .Net 客户端库页面了解更多安装选项。 关于可支持的版本,参见https://github.com/kubernetes-client/csharp/releases

.Net 客户端可以使用与 kubectl CLI 相同的 kubeconfig 文件来定位并验证 API 服务器。 参见样例

using System;
using k8s;

namespace simple
{
    internal class PodList
    {
        private static void Main(string[] args)
        {
            var config = KubernetesClientConfiguration.BuildDefaultConfig();
            IKubernetes client = new Kubernetes(config);
            Console.WriteLine("Starting Request!");

            var list = client.ListNamespacedPod("default");
            foreach (var item in list.Items)
            {
                Console.WriteLine(item.Metadata.Name);
            }
            if (list.Items.Count == 0)
            {
                Console.WriteLine("Empty!");
            }
        }
    }
}

JavaScript 客户端

要安装 JavaScript 客户端,运行下面的命令: npm install @kubernetes/client-node。 参考https://github.com/kubernetes-client/javascript/releases了解可支持的版本。

JavaScript 客户端可以使用 kubectl 命令行所使用的 kubeconfig 文件 以定位 API 服务器并向其认证身份。 参见此例

const k8s = require('@kubernetes/client-node');

const kc = new k8s.KubeConfig();
kc.loadFromDefault();

const k8sApi = kc.makeApiClient(k8s.CoreV1Api);

k8sApi.listNamespacedPod('default').then((res) => {
    console.log(res.body);
});

Haskell 客户端

参考 https://github.com/kubernetes-client/haskell/releases 了解支持的版本。

Haskell 客户端 可以使用 kubectl 命令行所使用的 kubeconfig 文件 以定位 API 服务器并向其认证身份。 参见此例

exampleWithKubeConfig :: IO ()
exampleWithKubeConfig = do
    oidcCache <- atomically $ newTVar $ Map.fromList []
    (mgr, kcfg) <- mkKubeClientConfig oidcCache $ KubeConfigFile "/path/to/kubeconfig"
    dispatchMime
            mgr
            kcfg
            (CoreV1.listPodForAllNamespaces (Accept MimeJSON))
        >>= print

接下来

4.2.7 - 为节点发布扩展资源

本文展示了如何为节点指定扩展资源(Extended Resource)。 扩展资源允许集群管理员发布节点级别的资源,这些资源在不进行发布的情况下无法被 Kubernetes 感知。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

获取你的节点名称

kubectl get nodes

选择一个节点用于此练习。

在你的一个节点上发布一种新的扩展资源

为在一个节点上发布一种新的扩展资源,需要发送一个 HTTP PATCH 请求到 Kubernetes API server。 例如:假设你的一个节点上带有四个 dongle 资源。 下面是一个 PATCH 请求的示例,该请求为你的节点发布四个 dongle 资源。

PATCH /api/v1/nodes/<your-node-name>/status HTTP/1.1
Accept: application/json
Content-Type: application/json-patch+json
Host: k8s-master:8080

[
  {
    "op": "add",
    "path": "/status/capacity/example.com~1dongle",
    "value": "4"
  }
]

注意:Kubernetes 不需要了解 dongle 资源的含义和用途。 前面的 PATCH 请求告诉 Kubernetes 你的节点拥有四个你称之为 dongle 的东西。

启动一个代理(proxy),以便你可以很容易地向 Kubernetes API server 发送请求:

kubectl proxy

在另一个命令窗口中,发送 HTTP PATCH 请求。 用你的节点名称替换 <your-node-name>

curl --header "Content-Type: application/json-patch+json" \
  --request PATCH \
  --data '[{"op": "add", "path": "/status/capacity/example.com~1dongle", "value": "4"}]' \
  http://localhost:8001/api/v1/nodes/<your-node-name>/status

输出显示该节点的 dongle 资源容量(capacity)为 4:

"capacity": {
  "cpu": "2",
  "memory": "2049008Ki",
  "example.com/dongle": "4",

描述你的节点:

kubectl describe node <your-node-name>

输出再次展示了 dongle 资源:

Capacity:
  cpu: 2
  memory: 2049008Ki
  example.com/dongle: 4

现在,应用开发者可以创建请求一定数量 dongle 资源的 Pod 了。 参见将扩展资源分配给容器

讨论

扩展资源类似于内存和 CPU 资源。例如,正如一个节点拥有一定数量的内存和 CPU 资源, 它们被节点上运行的所有组件共享,该节点也可以拥有一定数量的 dongle 资源, 这些资源同样被节点上运行的所有组件共享。 此外,正如应用开发者可以创建请求一定数量的内存和 CPU 资源的 Pod, 他们也可以创建请求一定数量 dongle 资源的 Pod。

扩展资源对 Kubernetes 是不透明的。Kubernetes 不知道扩展资源含义相关的任何信息。 Kubernetes 只了解一个节点拥有一定数量的扩展资源。 扩展资源必须以整形数量进行发布。 例如,一个节点可以发布 4 个 dongle 资源,但是不能发布 4.5 个。

存储示例

假设一个节点拥有一种特殊类型的磁盘存储,其容量为 800 GiB。 你可以为该特殊存储创建一个名称,如 example.com/special-storage。 然后你就可以按照一定规格的块(如 100 GiB)对其进行发布。 在这种情况下,你的节点将会通知它拥有八个 example.com/special-storage 类型的资源。

Capacity:
 ...
 example.com/special-storage: 8

如果你想要允许针对特殊存储任意(数量)的请求,你可以按照 1 字节大小的块来发布特殊存储。 在这种情况下,你将会发布 800Gi 数量的 example.com/special-storage 类型的资源。

Capacity:
 ...
 example.com/special-storage:  800Gi

然后,容器就能够请求任意数量(多达 800Gi)字节的特殊存储。

Capacity:
 ...
 example.com/special-storage:  800Gi

清理

这里是一个从节点移除 dongle 资源发布的 PATCH 请求。

PATCH /api/v1/nodes/<your-node-name>/status HTTP/1.1
Accept: application/json
Content-Type: application/json-patch+json
Host: k8s-master:8080

[
  {
    "op": "remove",
    "path": "/status/capacity/example.com~1dongle",
  }
]

启动一个代理,以便你可以很容易地向 Kubernetes API 服务器发送请求:

kubectl proxy

在另一个命令窗口中,发送 HTTP PATCH 请求。用你的节点名称替换 <your-node-name>

curl --header "Content-Type: application/json-patch+json" \
  --request PATCH \
  --data '[{"op": "remove", "path": "/status/capacity/example.com~1dongle"}]' \
  http://localhost:8001/api/v1/nodes/<your-node-name>/status

验证 dongle 资源的发布已经被移除:

kubectl describe node <your-node-name> | grep dongle

(你应该看不到任何输出)

接下来

针对应用开发人员

针对集群管理员

4.2.8 - 自动扩缩集群 DNS 服务

本页展示了如何在你的 Kubernetes 集群中启用和配置 DNS 服务的自动扩缩功能。

准备开始

  • 你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

    要获知版本信息,请输入 kubectl version.
  • 本指南假设你的节点使用 AMD64 或 Intel 64 CPU 架构

  • 确保 Kubernetes DNS 已启用。

确定是否 DNS 水平自动扩缩特性已经启用

在 kube-system 命名空间中列出集群中的 Deployment

kubectl get deployment --namespace=kube-system

输出类似如下这样:

NAME                   READY   UP-TO-DATE   AVAILABLE   AGE
...
kube-dns-autoscaler    1/1     1            1           ...
...

如果在输出中看到 “kube-dns-autoscaler”,说明 DNS 水平自动扩缩已经启用, 可以跳到调优 DNS 自动扩缩参数

获取 DNS Deployment 的名称

列出集群内 kube-system 命名空间中的 DNS Deployment:

kubectl get deployment -l k8s-app=kube-dns --namespace=kube-system

输出类似如下这样:

NAME      READY   UP-TO-DATE   AVAILABLE   AGE
...
coredns   2/2     2            2           ...
...

如果看不到 DNS 服务的 Deployment,你也可以通过名字来查找:

kubectl get deployment --namespace=kube-system

并在输出中寻找名称为 corednskube-dns 的 Deployment。

你的扩缩目标为:

Deployment/<your-deployment-name>

其中 <your-deployment-name> 是 DNS Deployment 的名称。 例如,如果你的 DNS Deployment 名称是 coredns,则你的扩展目标是 Deployment/coredns。

启用 DNS 水平自动扩缩

在本节,我们创建一个新的 Deployment。Deployment 中的 Pod 运行一个基于 cluster-proportional-autoscaler-amd64 镜像的容器。

创建文件 dns-horizontal-autoscaler.yaml,内容如下所示:

kind: ServiceAccount
apiVersion: v1
metadata:
  name: kube-dns-autoscaler
  namespace: kube-system
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: system:kube-dns-autoscaler
rules:
  - apiGroups: [""]
    resources: ["nodes"]
    verbs: ["list", "watch"]
  - apiGroups: [""]
    resources: ["replicationcontrollers/scale"]
    verbs: ["get", "update"]
  - apiGroups: ["apps"]
    resources: ["deployments/scale", "replicasets/scale"]
    verbs: ["get", "update"]
# 待以下 issue 修复后,请删除 Configmaps
# kubernetes-incubator/cluster-proportional-autoscaler#16
  - apiGroups: [""]
    resources: ["configmaps"]
    verbs: ["get", "create"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: system:kube-dns-autoscaler
subjects:
  - kind: ServiceAccount
    name: kube-dns-autoscaler
    namespace: kube-system
roleRef:
  kind: ClusterRole
  name: system:kube-dns-autoscaler
  apiGroup: rbac.authorization.k8s.io

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kube-dns-autoscaler
  namespace: kube-system
  labels:
    k8s-app: kube-dns-autoscaler
    kubernetes.io/cluster-service: "true"
spec:
  selector:
    matchLabels:
      k8s-app: kube-dns-autoscaler
  template:
    metadata:
      labels:
        k8s-app: kube-dns-autoscaler
    spec:
      priorityClassName: system-cluster-critical
      securityContext:
        seccompProfile:
          type: RuntimeDefault
        supplementalGroups: [ 65534 ]
        fsGroup: 65534
      nodeSelector:
        kubernetes.io/os: linux
      containers:
      - name: autoscaler
        image: registry.k8s.io/cpa/cluster-proportional-autoscaler:1.8.4
        resources:
            requests:
                cpu: "20m"
                memory: "10Mi"
        command:
          - /cluster-proportional-autoscaler
          - --namespace=kube-system
          - --configmap=kube-dns-autoscaler
          # 应该保持目标与 cluster/addons/dns/kube-dns.yaml.base 同步。
          - --target=<SCALE_TARGET>
          # 当集群使用大节点(有更多核)时,“coresPerReplica”应该占主导地位。
          # 如果使用小节点,“nodesPerReplica“ 应该占主导地位。
          - --default-params={"linear":{"coresPerReplica":256,"nodesPerReplica":16,"preventSinglePointFailure":true,"includeUnschedulableNodes":true}}
          - --logtostderr=true
          - --v=2
      tolerations:
      - key: "CriticalAddonsOnly"
        operator: "Exists"
      serviceAccountName: kube-dns-autoscaler

在此文件中,将 <SCALE_TARGET> 替换成扩缩目标。

进入到包含配置文件的目录中,输入如下命令创建 Deployment:

kubectl apply -f dns-horizontal-autoscaler.yaml

命令成功执行后的输出为:

deployment.apps/kube-dns-autoscaler created

DNS 水平自动扩缩现在已经启用了。

调优 DNS 自动扩缩参数

验证 kube-dns-autoscaler ConfigMap 是否存在:

kubectl get configmap --namespace=kube-system

输出类似于:

NAME                  DATA      AGE
...
kube-dns-autoscaler   1         ...
...

修改此 ConfigMap 中的数据:

kubectl edit configmap kube-dns-autoscaler --namespace=kube-system

找到如下这行内容:

linear: '{"coresPerReplica":256,"min":1,"nodesPerReplica":16}'

根据需要修改对应的字段。“min” 字段表明 DNS 后端的最小数量。 实际后端的数量通过使用如下公式来计算:

replicas = max( ceil( cores × 1/coresPerReplica ) , ceil( nodes × 1/nodesPerReplica ) )

注意 coresPerReplicanodesPerReplica 的值都是浮点数。

背后的思想是,当一个集群使用具有很多核心的节点时,由 coresPerReplica 来控制。 当一个集群使用具有较少核心的节点时,由 nodesPerReplica 来控制。

其它的扩缩模式也是支持的,详情查看 cluster-proportional-autoscaler

禁用 DNS 水平自动扩缩

有几个可供调优的 DNS 水平自动扩缩选项。具体使用哪个选项因环境而异。

选项 1:kube-dns-autoscaler Deployment 缩容至 0 个副本

此选项适用于所有场景。运行如下命令:

kubectl scale deployment --replicas=0 kube-dns-autoscaler --namespace=kube-system

输出如下所示:

deployment.apps/kube-dns-autoscaler scaled

验证当前副本数为 0:

kubectl get rs --namespace=kube-system

输出内容中,在 DESIRED 和 CURRENT 列显示为 0:

NAME                                 DESIRED   CURRENT   READY   AGE
...
kube-dns-autoscaler-6b59789fc8            0         0         0       ...
...

选项 2:删除 kube-dns-autoscaler Deployment

如果 kube-dns-autoscaler 为你所控制,也就说没有人会去重新创建它,可以选择此选项:

kubectl delete deployment kube-dns-autoscaler --namespace=kube-system

输出内容如下所示:

deployment.apps "kube-dns-autoscaler" deleted

选项 3:从主控节点删除 kube-dns-autoscaler 清单文件

如果 kube-dns-autoscaler 在插件管理器 的控制之下,并且具有操作主控节点的写权限,可以使用此选项。

登录到主控节点,删除对应的清单文件。 kube-dns-autoscaler 对应的路径一般为:

/etc/kubernetes/addons/dns-horizontal-autoscaler/dns-horizontal-autoscaler.yaml

当清单文件被删除后,插件管理器将删除 kube-dns-autoscaler Deployment。

理解 DNS 水平自动扩缩工作原理

  • cluster-proportional-autoscaler 应用独立于 DNS 服务部署。

  • autoscaler Pod 运行一个客户端,它通过轮询 Kubernetes API 服务器获取集群中节点和核心的数量。

  • 系统会基于当前可调度的节点个数、核心数以及所给的扩缩参数,计算期望的副本数并应用到 DNS 后端。

  • 扩缩参数和数据点会基于一个 ConfigMap 来提供给 autoscaler,它会在每次轮询时刷新它的参数表, 以与最近期望的扩缩参数保持一致。

  • 扩缩参数是可以被修改的,而且不需要重建或重启 autoscaler Pod。

  • autoscaler 提供了一个控制器接口来支持两种控制模式:linearladder

接下来

4.2.9 - 从轮询切换为基于 CRI 事件的更新来获取容器状态

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

本页展示了如何迁移节点以使用基于事件的更新来获取容器状态。 与依赖轮询的传统方法相比,基于事件的实现可以减少 kubelet 对节点资源的消耗。 你可以将这个特性称为事件驱动的 Pod 生命周期事件生成器 (PLEG)。 这是在 Kubernetes 项目内部针对关键实现细节所用的名称。

基于轮询的方法称为通用 PLEG

准备开始

  • 你需要运行提供此特性的 Kubernetes 版本。 Kubernetes 1.27 提供了对基于事件更新容器状态的 Beta 支持。 此特性处于 Beta 阶段,默认被禁用
  • 你的 Kubernetes 服务器版本必须不低于版本 1.26. 要获知版本信息,请输入 kubectl version. 如果你正在运行不同版本的 Kubernetes,请查阅对应版本的文档。
  • 所使用的容器运行时必须支持容器生命周期事件。 如果容器运行时未声明对容器生命周期事件的支持,即使你已启用了此特性门控, kubelet 也会自动切换回传统的通用 PLEG。

为什么要切换到事件驱动的 PLEG?

  • 通用 PLEG 由于频繁轮询容器状态而产生了不可忽略的开销。
  • 这种开销会被 kubelet 的并行轮询容器状态的机制加剧, 限制了可扩缩性,还会导致性能和可靠性问题。
  • 事件驱动的 PLEG 的目标是通过替换定期轮询来减少闲置时的非必要任务。

切换为事件驱动的 PLEG

  1. 启用特性门控 EventedPLEG 后启动 kubelet。 你可以通过编辑 kubelet 配置文件并重启 kubelet 服务来管理 kubelet 特性门控。 你需要在使用此特性的所有节点上执行此操作。
  1. 确保节点被腾空后再继续。

  2. 启用容器事件生成后启动容器运行时。

    版本 1.7+

    版本 1.26+

    通过验证配置,检查 CRI-O 是否已配置为发送 CRI 事件:

    crio config | grep enable_pod_events
    

    如果已启用,输出应类似于:

    enable_pod_events = true
    

    要启用它,可使用 --enable-pod-events=true 标志或添加以下配置来启动 CRI-O 守护进程:

    [crio.runtime]
    enable_pod_events: true
    
    你的 Kubernetes 服务器版本必须不低于版本 1.26. 要获知版本信息,请输入 kubectl version.
  1. 确认 kubelet 正使用基于事件的容器阶段变更监控。 要检查这一点,可在 kubelet 日志中查找 EventedPLEG 词条。

    输出类似于:

    I0314 11:10:13.909915 1105457 feature_gate.go:249] feature gates: &{map[EventedPLEG:true]}
    

    如果你将 --v 设置为 4 及更高值,你可能会看到更多条目表明 kubelet 正在使用基于事件的容器状态监控。

    I0314 11:12:42.009542 1110177 evented.go:238] "Evented PLEG: Generated pod status from the received event" podUID=3b2c6172-b112-447a-ba96-94e7022912dc
    I0314 11:12:44.623326 1110177 evented.go:238] "Evented PLEG: Generated pod status from the received event" podUID=b3fba5ea-a8c5-4b76-8f43-481e17e8ec40
    I0314 11:12:44.714564 1110177 evented.go:238] "Evented PLEG: Generated pod status from the received event" podUID=b3fba5ea-a8c5-4b76-8f43-481e17e8ec40
    

接下来

4.2.10 - 改变默认 StorageClass

本文展示了如何改变默认的 Storage Class,它用于为没有特殊需求的 PersistentVolumeClaims 配置 volumes。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

为什么要改变默认存储类?

取决于安装模式,你的 Kubernetes 集群可能和一个被标记为默认的已有 StorageClass 一起部署。 这个默认的 StorageClass 以后将被用于动态的为没有特定存储类需求的 PersistentVolumeClaims 配置存储。更多细节请查看 PersistentVolumeClaim 文档

预先安装的默认 StorageClass 可能不能很好的适应你期望的工作负载;例如,它配置的存储可能太过昂贵。 如果是这样的话,你可以改变默认 StorageClass,或者完全禁用它以防止动态配置存储。

删除默认 StorageClass 可能行不通,因为它可能会被你集群中的扩展管理器自动重建。 请查阅你的安装文档中关于扩展管理器的细节,以及如何禁用单个扩展。

改变默认 StorageClass

  1. 列出你的集群中的 StorageClass:

    kubectl get storageclass
    

    输出类似这样:

    NAME                 PROVISIONER               AGE
    standard (default)   kubernetes.io/gce-pd      1d
    gold                 kubernetes.io/gce-pd      1d
    

    默认 StorageClass 以 (default) 标记。

  1. 标记默认 StorageClass 非默认:

    默认 StorageClass 的注解 storageclass.kubernetes.io/is-default-class 设置为 true。 注解的其它任意值或者缺省值将被解释为 false

    要标记一个 StorageClass 为非默认的,你需要改变它的值为 false

    kubectl patch storageclass standard -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}'
    

    这里的 standard 是你选择的 StorageClass 的名字。

  1. 标记一个 StorageClass 为默认的:

    和前面的步骤类似,你需要添加/设置注解 storageclass.kubernetes.io/is-default-class=true

    kubectl patch storageclass <your-class-name> -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
    

    请注意,最多只能有一个 StorageClass 能够被标记为默认。 如果它们中有两个或多个被标记为默认,Kubernetes 将忽略这个注解, 也就是它将表现为没有默认 StorageClass。

  1. 验证你选用的 StorageClass 为默认的:

    kubectl get storageclass
    

    输出类似这样:

    NAME             PROVISIONER               AGE
    standard         kubernetes.io/gce-pd      1d
    gold (default)   kubernetes.io/gce-pd      1d
    

接下来

4.2.11 - 更改 PersistentVolume 的回收策略

本文展示了如何更改 Kubernetes PersistentVolume 的回收策略。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

为什么要更改 PersistentVolume 的回收策略

PersistentVolumes 可以有多种回收策略,包括 "Retain"、"Recycle" 和 "Delete"。 对于动态配置的 PersistentVolumes 来说,默认回收策略为 "Delete"。 这表示当用户删除对应的 PersistentVolumeClaim 时,动态配置的 volume 将被自动删除。 如果 volume 包含重要数据时,这种自动行为可能是不合适的。 那种情况下,更适合使用 "Retain" 策略。 使用 "Retain" 时,如果用户删除 PersistentVolumeClaim,对应的 PersistentVolume 不会被删除。 相反,它将变为 Released 状态,表示所有的数据可以被手动恢复。

更改 PersistentVolume 的回收策略

  1. 列出你集群中的 PersistentVolumes

    kubectl get pv
    

    输出类似于这样:

    NAME                                       CAPACITY   ACCESSMODES   RECLAIMPOLICY   STATUS    CLAIM             STORAGECLASS     REASON    AGE
    pvc-b6efd8da-b7b5-11e6-9d58-0ed433a7dd94   4Gi        RWO           Delete          Bound     default/claim1    manual                     10s
    pvc-b95650f8-b7b5-11e6-9d58-0ed433a7dd94   4Gi        RWO           Delete          Bound     default/claim2    manual                     6s
    pvc-bb3ca71d-b7b5-11e6-9d58-0ed433a7dd94   4Gi        RWO           Delete          Bound     default/claim3    manual                     3s
    

    这个列表同样包含了绑定到每个卷的 claims 名称,以便更容易的识别动态配置的卷。

  1. 选择你的 PersistentVolumes 中的一个并更改它的回收策略:

    kubectl patch pv <your-pv-name> -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}'
    

    这里的 <your-pv-name> 是你选择的 PersistentVolume 的名字。

  1. 验证你选择的 PersistentVolume 拥有正确的策略:

    kubectl get pv
    

    输出类似于这样:

    NAME                                       CAPACITY   ACCESSMODES   RECLAIMPOLICY   STATUS    CLAIM             STORAGECLASS     REASON    AGE
    pvc-b6efd8da-b7b5-11e6-9d58-0ed433a7dd94   4Gi        RWO           Delete          Bound     default/claim1    manual                     40s
    pvc-b95650f8-b7b5-11e6-9d58-0ed433a7dd94   4Gi        RWO           Delete          Bound     default/claim2    manual                     36s
    pvc-bb3ca71d-b7b5-11e6-9d58-0ed433a7dd94   4Gi        RWO           Retain          Bound     default/claim3    manual                     33s
    

    在前面的输出中,你可以看到绑定到申领 default/claim3 的卷的回收策略为 Retain。 当用户删除申领 default/claim3 时,它不会被自动删除。

接下来

参考

4.2.12 - Kubernetes 云管理控制器

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

由于云驱动的开发和发布的步调与 Kubernetes 项目不同,将服务提供商专用代码抽象到 cloud-controller-manager 二进制中有助于云服务厂商在 Kubernetes 核心代码之外独立进行开发。

cloud-controller-manager 可以被链接到任何满足 cloudprovider.Interface 约束的云服务提供商。为了兼容旧版本,Kubernetes 核心项目中提供的 cloud-controller-manager 使用和 kube-controller-manager 相同的云服务类库。 已经在 Kubernetes 核心项目中支持的云服务提供商预计将通过使用 in-tree 的 cloud-controller-manager 过渡为非 Kubernetes 核心代码。

管理

需求

每个云服务都有一套各自的需求用于系统平台的集成,这不应与运行 kube-controller-manager 的需求有太大差异。作为经验法则,你需要:

  • 云服务认证/授权:你的云服务可能需要使用令牌或者 IAM 规则以允许对其 API 的访问
  • kubernetes 认证/授权:cloud-controller-manager 可能需要 RBAC 规则以访问 kubernetes apiserver
  • 高可用:类似于 kube-controller-manager,你可能希望通过主节点选举(默认开启)配置一个高可用的云管理控制器。

运行云管理控制器

你需要对集群配置做适当的修改以成功地运行云管理控制器:

  • kubeletkube-apiserverkube-controller-manager 必须根据用户对外部 CCM 的使用进行设置。 如果用户有一个外部的 CCM(不是 Kubernetes 控制器管理器中的内部云控制器回路), 那么必须添加 --cloud-provider=external 参数。否则,不应添加此参数。

请记住,设置集群使用云管理控制器将用多种方式更改集群行为:

  • 指定了 --cloud-provider=external 的组件将被添加一个 node.cloudprovider.kubernetes.io/uninitialized 的污点,导致其在初始化过程中不可调度(NoSchedule)。 这将标记该节点在能够正常调度前,需要外部的控制器进行二次初始化。 请注意,如果云管理控制器不可用,集群中的新节点会一直处于不可调度的状态。 这个污点很重要,因为调度器可能需要关于节点的云服务特定的信息,比如他们的区域或类型 (高端 CPU、GPU 支持、内存较大、临时实例等)。
  • 集群中节点的云服务信息将不再能够从本地元数据中获取,取而代之的是所有获取节点信息的 API 调用都将通过云管理控制器。这意味着你可以通过限制到 kubelet 云服务 API 的访问来提升安全性。 在更大的集群中你可能需要考虑云管理控制器是否会遇到速率限制, 因为它现在负责集群中几乎所有到云服务的 API 调用。

云管理控制器可以实现:

  • 节点控制器 - 负责使用云服务 API 更新 kubernetes 节点并删除在云服务上已经删除的 kubernetes 节点。
  • 服务控制器 - 负责在云服务上为类型为 LoadBalancer 的 service 提供负载均衡器。
  • 路由控制器 - 负责在云服务上配置网络路由。
  • 如果你使用的是 out-of-tree 提供商,请按需实现其余任意特性。

示例

如果当前 Kubernetes 内核支持你使用的云服务,并且想要采用云管理控制器,请参见 kubernetes 内核中的云管理控制器

对于不在 Kubernetes 核心代码库中的云管理控制器,你可以在云服务厂商或 SIG 领导者的源中找到对应的项目。

对于已经存在于 Kubernetes 内核中的提供商,你可以在集群中将 in-tree 云管理控制器作为守护进程运行。请使用如下指南:

# 这是一个如何将 cloud-controller-manager 安装为集群中的 Daemonset 的示例。
# 本例假定你的主控节点可以运行 pod 并具有角色 node-role.kubernetes.io/master
# 请注意,这里的 Daemonset 不能直接在你的云上工作,此例只是一个指导。

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: cloud-controller-manager
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: system:cloud-controller-manager
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: cloud-controller-manager
  namespace: kube-system
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  labels:
    k8s-app: cloud-controller-manager
  name: cloud-controller-manager
  namespace: kube-system
spec:
  selector:
    matchLabels:
      k8s-app: cloud-controller-manager
  template:
    metadata:
      labels:
        k8s-app: cloud-controller-manager
    spec:
      serviceAccountName: cloud-controller-manager
      containers:
      - name: cloud-controller-manager
        # 对于树内驱动,我们使用 registry.k8s.io/cloud-controller-manager,
        # 镜像可以替换为其他树外驱动的镜像
        image: registry.k8s.io/cloud-controller-manager:v1.8.0
        command:
        - /usr/local/bin/cloud-controller-manager
        - --cloud-provider=[YOUR_CLOUD_PROVIDER]  # 在此处添加你自己的云驱动!
        - --leader-elect=true
        - --use-service-account-credentials
        # 这些标志因每个云驱动而异
        - --allocate-node-cidrs=true
        - --configure-cloud-routes=true
        - --cluster-cidr=172.17.0.0/16
      tolerations:
      # 这一设置是必需的,为了让 CCM 可以自行引导
      - key: node.cloudprovider.kubernetes.io/uninitialized
        value: "true"
        effect: NoSchedule
      # 这些容忍度使得守护进程能够在控制平面节点上运行
      # 如果你的控制平面节点不应该运行 pod,请删除它们
      - key: node-role.kubernetes.io/control-plane
        operator: Exists
        effect: NoSchedule
      - key: node-role.kubernetes.io/master
        operator: Exists
        effect: NoSchedule
      # 这是为了限制 CCM 仅在主节点上运行
      # 节点选择器可能因你的集群设置而异
      nodeSelector:
        node-role.kubernetes.io/master: ""

限制

运行云管理控制器会有一些可能的限制。虽然以后的版本将处理这些限制,但是知道这些生产负载的限制很重要。

对 Volume 的支持

云管理控制器未实现 kube-controller-manager 中的任何 volume 控制器, 因为和 volume 的集成还需要与 kubelet 协作。由于我们引入了 CSI (容器存储接口, container storage interface) 并对弹性 volume 插件添加了更强大的支持, 云管理控制器将添加必要的支持,以使云服务同 volume 更好的集成。 请在这里了解更多关于 out-of-tree CSI volume 插件的信息。

可扩展性

通过云管理控制器查询你的云提供商的 API 以检索所有节点的信息。 对于非常大的集群,请考虑可能的瓶颈,例如资源需求和 API 速率限制。

鸡和蛋的问题

云管理控制器的目标是将云服务特性的开发从 Kubernetes 核心项目中解耦。 不幸的是,Kubernetes 项目的许多方面都假设云服务提供商的特性同项目紧密结合。 因此,这种新架构的采用可能导致某些场景下,当一个请求需要从云服务提供商获取信息时, 在该请求没有完成的情况下云管理控制器不能返回那些信息。

Kubelet 中的 TLS 引导特性是一个很好的例子。 目前,TLS 引导认为 kubelet 有能力从云提供商(或本地元数据服务)获取所有的地址类型(私有、公用等), 但在被初始化之前,云管理控制器不能设置节点地址类型,而这需要 kubelet 拥有 TLS 证书以和 API 服务器通信。

随着整个动议的演进,将来的发行版中将作出改变来解决这些问题。

接下来

要构建和开发你自己的云管理控制器,请阅读 开发云管理控制器 文档。

4.2.13 - 配置 kubelet 镜像凭据提供程序

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

从 Kubernetes v1.20 开始,kubelet 可以使用 exec 插件动态获得针对某容器镜像库的凭据。 kubelet 使用 Kubernetes 版本化 API 通过标准输入输出(标准输入、标准输出和标准错误)和 exec 插件通信。这些插件允许 kubelet 动态请求容器仓库的凭据,而不是将静态凭据存储在磁盘上。 例如,插件可能会与本地元数据服务器通信,以获得 kubelet 正在拉取的镜像的短期凭据。

如果以下任一情况属实,你可能对此功能感兴趣:

  • 需要调用云提供商的 API 来获得镜像库的身份验证信息。
  • 凭据的到期时间很短,需要频繁请求新凭据。
  • 将镜像库凭据存储在磁盘或者 imagePullSecret 是不可接受的。

本指南演示如何配置 kubelet 的镜像凭据提供程序插件机制。

准备开始

  • 你需要一个 Kubernetes 集群,其节点支持 kubelet 凭据提供程序插件。 这种支持在 Kubernetes 1.30 中可用; Kubernetes v1.24 和 v1.25 将此作为 Beta 特性包含在内,默认启用。
  • 凭据提供程序 exec 插件的一种可用的实现。你可以构建自己的插件或使用云提供商提供的插件。
你的 Kubernetes 服务器版本必须不低于版本 v1.26. 要获知版本信息,请输入 kubectl version.

在节点上安装插件

凭据提供程序插件是将由 kubelet 运行的可执行二进制文件。 你需要确保插件可执行文件存在于你的集群的每个节点上,并存储在已知目录中。 稍后配置 kubelet 标志需要该目录。

配置 kubelet

为了使用这个特性,kubelet 需要设置以下两个标志:

  • --image-credential-provider-config —— 凭据提供程序插件配置文件的路径。
  • --image-credential-provider-bin-dir —— 凭据提供程序插件二进制可执行文件所在目录的路径。

配置 kubelet 凭据提供程序

kubelet 会读取通过 --image-credential-provider-config 设定的配置文件, 以确定应该为哪些容器镜像调用哪些 exec 插件。 如果你正在使用基于 ECR-based 插件, 这里有个样例配置文件你可能最终会使用到:

apiVersion: kubelet.config.k8s.io/v1
kind: CredentialProviderConfig
# providers 是将由 kubelet 启用的凭据提供程序帮助插件列表。
# 多个提供程序可能与单个镜像匹配,在这种情况下,来自所有提供程序的凭据将返回到 kubelet。
# 如果为单个镜像调用了多个提供程序,则返回结果会被合并。
# 如果提供程序返回重叠的身份验证密钥,则使用提供程序列表中较早的值。
providers:
  # name 是凭据提供程序的必需名称。 
  # 它必须与 kubelet 看到的提供程序可执行文件的名称相匹配。
  # 可执行文件必须在 kubelet 的 bin 目录中
  # (由 --image-credential-provider-bin-dir 标志设置)。
  - name: ecr-credential-provider
    # matchImages 是一个必需的字符串列表,用于匹配镜像以确定是否应调用此提供程序。
    # 如果其中一个字符串与 kubelet 请求的镜像相匹配,则该插件将被调用并有机会提供凭据。
    # 镜像应包含注册域和 URL 路径。
    #
    # matchImages 中的每个条目都是一个模式字符串,可以选择包含端口和路径。
    # 可以在域中使用通配符,但不能在端口或路径中使用。
    # 支持通配符作为子域(例如 "*.k8s.io" 或 "k8s.*.io")和顶级域(例如 "k8s.*")。
    # 还支持匹配部分子域,如 "app*.k8s.io"。
    # 每个通配符只能匹配一个子域段,因此 "*.io" **不** 匹配 "*.k8s.io"。
    #
    # 当以下所有条件都为真时,镜像和 matchImage 之间存在匹配:
    #
    # - 两者都包含相同数量的域部分并且每个部分都匹配。
    # - matchImages 的 URL 路径必须是目标镜像 URL 路径的前缀。
    # - 如果 matchImages 包含端口,则该端口也必须在镜像中匹配。
    #
    # matchImages 的示例值:
    #
    # - 123456789.dkr.ecr.us-east-1.amazonaws.com
    # - *.azurecr.io
    # - gcr.io
    # - *.*.registry.io
    # - registry.io:8080/path
    matchImages:
      - "*.dkr.ecr.*.amazonaws.com"
      - "*.dkr.ecr.*.amazonaws.com.cn"
      - "*.dkr.ecr-fips.*.amazonaws.com"
      - "*.dkr.ecr.us-iso-east-1.c2s.ic.gov"
      - "*.dkr.ecr.us-isob-east-1.sc2s.sgov.gov"
    # defaultCacheDuration 是插件将在内存中缓存凭据的默认持续时间。
    # 如果插件响应中未提供缓存持续时间。此字段是必需的。
    defaultCacheDuration: "12h"
    # exec CredentialProviderRequest 的必需输入版本。
    # 返回的 CredentialProviderResponse 必须使用与输入相同的编码版本。当前支持的值为:
    # - credentialprovider.kubelet.k8s.io/v1
    apiVersion: credentialprovider.kubelet.k8s.io/v1
    # 执行命令时传递给命令的参数。
    # 可选
    # args:
    #  - --example-argument
    # env 定义了额外的环境变量以暴露给进程。
    # 这些与主机环境以及 client-go 用于将参数传递给插件的变量结合在一起。
    # 可选
    env:
      - name: AWS_PROFILE
        value: example_profile

providers 字段是 kubelet 所使用的已启用插件列表。每个条目都有几个必填字段:

  • name:插件的名称,必须与传入 --image-credential-provider-bin-dir 的目录中存在的可执行二进制文件的名称相匹配。
  • matchImages:字符串列表,用于匹配镜像以确定是否应调用此提供程序。 更多相关信息参见后文。
  • defaultCacheDuration:如果插件未指定缓存时长,kubelet 将在内存中缓存凭据的默认时长。
  • apiVersion:kubelet 和 exec 插件在通信时将使用的 API 版本。

每个凭据提供程序也可以被赋予可选的参数和环境变量。 你可以咨询插件实现者以确定给定插件需要哪些参数和环境变量集。

配置镜像匹配

kubelet 使用每个凭据提供程序的 matchImages 字段来确定是否应该为 Pod 正在使用的给定镜像调用插件。 matchImages 中的每个条目都是一个镜像模式字符串,可以选择包含端口和路径。 可以在域中使用通配符,但不能在端口或路径中使用。 支持通配符作为子域,如 *.k8s.iok8s.*.io,以及顶级域,如 k8s.*。 还支持匹配部分子域,如 app*.k8s.io。每个通配符只能匹配一个子域段, 因此 *.io 不匹配 *.k8s.io

当以下条件全部满足时,镜像名称和 matchImage 条目之间存在匹配:

  • 两者都包含相同数量的域部分并且每个部分都匹配。
  • 匹配图片的 URL 路径必须是目标图片 URL 路径的前缀。
  • 如果 matchImages 包含端口,则该端口也必须在镜像中匹配。

matchImages 模式的一些示例值:

  • 123456789.dkr.ecr.us-east-1.amazonaws.com
  • *.azurecr.io
  • gcr.io
  • *.*.registry.io
  • foo.registry.io:8080/path

接下来

4.2.14 - 配置 API 对象配额

本文讨论如何为 API 对象配置配额,包括 PersistentVolumeClaim 和 Service。 配额限制了可以在命名空间中创建的特定类型对象的数量。 你可以在 ResourceQuota 对象中指定配额。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

创建命名空间

创建一个命名空间以便本例中创建的资源和集群中的其余部分相隔离。

kubectl create namespace quota-object-example

创建 ResourceQuota

下面是一个 ResourceQuota 对象的配置文件:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: object-quota-demo
spec:
  hard:
    persistentvolumeclaims: "1"
    services.loadbalancers: "2"
    services.nodeports: "0"

创建 ResourceQuota:

kubectl apply -f https://k8s.io/examples/admin/resource/quota-objects.yaml --namespace=quota-object-example

查看 ResourceQuota 的详细信息:

kubectl get resourcequota object-quota-demo --namespace=quota-object-example --output=yaml

输出结果表明在 quota-object-example 命名空间中,至多只能有一个 PersistentVolumeClaim, 最多两个 LoadBalancer 类型的服务,不能有 NodePort 类型的服务。

status:
  hard:
    persistentvolumeclaims: "1"
    services.loadbalancers: "2"
    services.nodeports: "0"
  used:
    persistentvolumeclaims: "0"
    services.loadbalancers: "0"
    services.nodeports: "0"

创建 PersistentVolumeClaim

下面是一个 PersistentVolumeClaim 对象的配置文件:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-quota-demo
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 3Gi

创建 PersistentVolumeClaim:

kubectl apply -f https://k8s.io/examples/admin/resource/quota-objects-pvc.yaml --namespace=quota-object-example

确认已创建完 PersistentVolumeClaim:

kubectl get persistentvolumeclaims --namespace=quota-object-example

输出信息表明 PersistentVolumeClaim 存在并且处于 Pending 状态:

NAME             STATUS
pvc-quota-demo   Pending

尝试创建第二个 PersistentVolumeClaim

下面是第二个 PersistentVolumeClaim 的配置文件:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-quota-demo-2
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 4Gi

尝试创建第二个 PersistentVolumeClaim:

kubectl apply -f https://k8s.io/examples/admin/resource/quota-objects-pvc-2.yaml --namespace=quota-object-example

输出信息表明第二个 PersistentVolumeClaim 没有创建成功,因为这会超出命名空间的配额。

persistentvolumeclaims "pvc-quota-demo-2" is forbidden:
exceeded quota: object-quota-demo, requested: persistentvolumeclaims=1,
used: persistentvolumeclaims=1, limited: persistentvolumeclaims=1

说明

下面这些字符串可被用来标识那些能被配额限制的 API 资源:

字符串API 对象
"pods"Pod
"services"Service
"replicationcontrollers"ReplicationController
"resourcequotas"ResourceQuota
"secrets"Secret
"configmaps"ConfigMap
"persistentvolumeclaims"PersistentVolumeClaim
"services.nodeports"NodePort 类型的 Service
"services.loadbalancers"LoadBalancer 类型的 Service

清理

删除你的命名空间:

kubectl delete namespace quota-object-example

接下来

集群管理员参考

应用开发者参考

4.2.15 - 控制节点上的 CPU 管理策略

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

按照设计,Kubernetes 对 Pod 执行相关的很多方面进行了抽象,使得用户不必关心。 然而,为了正常运行,有些工作负载要求在延迟和/或性能方面有更强的保证。 为此,kubelet 提供方法来实现更复杂的负载放置策略,同时保持抽象,避免显式的放置指令。

有关资源管理的详细信息, 请参阅 Pod 和容器的资源管理文档。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你的 Kubernetes 服务器版本必须不低于版本 v1.26. 要获知版本信息,请输入 kubectl version.

如果你正在运行一个旧版本的 Kubernetes,请参阅与该版本对应的文档。

CPU 管理策略

默认情况下,kubelet 使用 CFS 配额 来执行 Pod 的 CPU 约束。 当节点上运行了很多 CPU 密集的 Pod 时,工作负载可能会迁移到不同的 CPU 核, 这取决于调度时 Pod 是否被扼制,以及哪些 CPU 核是可用的。 许多工作负载对这种迁移不敏感,因此无需任何干预即可正常工作。

然而,有些工作负载的性能明显地受到 CPU 缓存亲和性以及调度延迟的影响。 对此,kubelet 提供了可选的 CPU 管理策略,来确定节点上的一些分配偏好。

配置

CPU 管理策略通过 kubelet 参数 --cpu-manager-policyKubeletConfiguration 中的 cpuManagerPolicy 字段来指定。 支持两种策略:

  • none:默认策略。
  • static:允许为节点上具有某些资源特征的 Pod 赋予增强的 CPU 亲和性和独占性。

CPU 管理器定期通过 CRI 写入资源更新,以保证内存中 CPU 分配与 cgroupfs 一致。 同步频率通过新增的 Kubelet 配置参数 --cpu-manager-reconcile-period 来设置。 如果不指定,默认与 --node-status-update-frequency 的周期相同。

Static 策略的行为可以使用 --cpu-manager-policy-options 参数来微调。 该参数采用一个逗号分隔的 key=value 策略选项列表。 如果你禁用 CPUManagerPolicyOptions 特性门控, 则你不能微调 CPU 管理器策略。这种情况下,CPU 管理器仅使用其默认设置运行。

除了顶级的 CPUManagerPolicyOptions 特性门控, 策略选项分为两组:Alpha 质量(默认隐藏)和 Beta 质量(默认可见)。 这些组分别由 CPUManagerPolicyAlphaOptionsCPUManagerPolicyBetaOptions 特性门控来管控。 不同于 Kubernetes 标准,这里是由这些特性门控来管控选项组,因为为每个单独选项都添加一个特性门控过于繁琐。

更改 CPU 管理器策略

由于 CPU 管理器策略只能在 kubelet 生成新 Pod 时应用,所以简单地从 "none" 更改为 "static" 将不会对现有的 Pod 起作用。 因此,为了正确更改节点上的 CPU 管理器策略,请执行以下步骤:

  1. 腾空节点。
  2. 停止 kubelet。
  3. 删除旧的 CPU 管理器状态文件。该文件的路径默认为 /var/lib/kubelet/cpu_manager_state。 这将清除 CPUManager 维护的状态,以便新策略设置的 cpu-sets 不会与之冲突。
  4. 编辑 kubelet 配置以将 CPU 管理器策略更改为所需的值。
  5. 启动 kubelet。

对需要更改其 CPU 管理器策略的每个节点重复此过程。 跳过此过程将导致 kubelet crashlooping 并出现以下错误:

could not restore state from checkpoint: configured policy "static" differs from state checkpoint policy "none", please drain this node and delete the CPU manager checkpoint file "/var/lib/kubelet/cpu_manager_state" before restarting Kubelet

none 策略

none 策略显式地启用现有的默认 CPU 亲和方案,不提供操作系统调度器默认行为之外的亲和性策略。 通过 CFS 配额来实现 Guaranteed PodsBurstable Pods 的 CPU 使用限制。

static 策略

static 策略针对具有整数型 CPU requestsGuaranteed Pod, 它允许该类 Pod 中的容器访问节点上的独占 CPU 资源。这种独占性是使用 cpuset cgroup 控制器来实现的。

此策略管理一个 CPU 共享池,该共享池最初包含节点上所有的 CPU 资源。 可独占性 CPU 资源数量等于节点的 CPU 总量减去通过 kubelet --kube-reserved--system-reserved 参数保留的 CPU 资源。 从 1.17 版本开始,可以通过 kubelet --reserved-cpus 参数显式地指定 CPU 预留列表。 由 --reserved-cpus 指定的显式 CPU 列表优先于由 --kube-reserved--system-reserved 指定的 CPU 预留。 通过这些参数预留的 CPU 是以整数方式,按物理核心 ID 升序从初始共享池获取的。 共享池是 BestEffortBurstable Pod 运行的 CPU 集合。 Guaranteed Pod 中的容器,如果声明了非整数值的 CPU requests,也将运行在共享池的 CPU 上。 只有 Guaranteed Pod 中,指定了整数型 CPU requests 的容器,才会被分配独占 CPU 资源。

Guaranteed Pod 调度到节点上时,如果其容器符合静态分配要求, 相应的 CPU 会被从共享池中移除,并放置到容器的 cpuset 中。 因为这些容器所使用的 CPU 受到调度域本身的限制,所以不需要使用 CFS 配额来进行 CPU 的绑定。 换言之,容器 cpuset 中的 CPU 数量与 Pod 规约中指定的整数型 CPU limit 相等。 这种静态分配增强了 CPU 亲和性,减少了 CPU 密集的工作负载在节流时引起的上下文切换。

考虑以下 Pod 规格的容器:

spec:
  containers:
  - name: nginx
    image: nginx

该 Pod 属于 BestEffort QoS 类型,因为其未指定 requestslimits 值。 所以该容器运行在共享 CPU 池中。

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
      requests:
        memory: "100Mi"

该 Pod 属于 Burstable QoS 类型,因为其资源 requests 不等于 limits,且未指定 cpu 数量。 所以该容器运行在共享 CPU 池中。

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "2"
      requests:
        memory: "100Mi"
        cpu: "1"

该 Pod 属于 Burstable QoS 类型,因为其资源 requests 不等于 limits。 所以该容器运行在共享 CPU 池中。

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "2"
      requests:
        memory: "200Mi"
        cpu: "2"

该 Pod 属于 Guaranteed QoS 类型,因为其 requests 值与 limits相等。 同时,容器对 CPU 资源的限制值是一个大于或等于 1 的整数值。 所以,该 nginx 容器被赋予 2 个独占 CPU。

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "1.5"
      requests:
        memory: "200Mi"
        cpu: "1.5"

该 Pod 属于 Guaranteed QoS 类型,因为其 requests 值与 limits相等。 但是容器对 CPU 资源的限制值是一个小数。所以该容器运行在共享 CPU 池中。

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "2"

该 Pod 属于 Guaranteed QoS 类型,因其指定了 limits 值,同时当未显式指定时, requests 值被设置为与 limits 值相等。 同时,容器对 CPU 资源的限制值是一个大于或等于 1 的整数值。 所以,该 nginx 容器被赋予 2 个独占 CPU。

Static 策略选项

你可以使用以下特性门控根据成熟度级别打开或关闭选项组:

  • CPUManagerPolicyBetaOptions 默认启用。禁用以隐藏 beta 级选项。
  • CPUManagerPolicyAlphaOptions 默认禁用。启用以显示 alpha 级选项。 你仍然必须使用 CPUManagerPolicyOptions kubelet 选项启用每个选项。

静态 CPUManager 策略存在以下策略选项:

  • full-pcpus-only(Beta,默认可见)(1.22 或更高版本)
  • distribute-cpus-across-numa(alpha,默认隐藏)(1.23 或更高版本)
  • align-by-socket(Alpha,默认隐藏)(1.25 或更高版本)

如果使用 full-pcpus-only 策略选项,static 策略总是会分配完整的物理核心。 默认情况下,如果不使用该选项,static 策略会使用拓扑感知最适合的分配方法来分配 CPU。 在启用了 SMT 的系统上,此策略所分配是与硬件线程对应的、独立的虚拟核。 这会导致不同的容器共享相同的物理核心, 该行为进而会导致吵闹的邻居问题

启用该选项之后,只有当一个 Pod 里所有容器的 CPU 请求都能够分配到完整的物理核心时, kubelet 才会接受该 Pod。 如果 Pod 没有被准入,它会被置于 Failed 状态,错误消息是 SMTAlignmentError

如果使用 distribute-cpus-across-numa 策略选项, 在需要多个 NUMA 节点来满足分配的情况下, static 策略会在 NUMA 节点上平均分配 CPU。 默认情况下,CPUManager 会将 CPU 分配到一个 NUMA 节点上,直到它被填满, 剩余的 CPU 会简单地溢出到下一个 NUMA 节点。 这会导致依赖于同步屏障(以及类似的同步原语)的并行代码出现不期望的瓶颈, 因为此类代码的运行速度往往取决于最慢的工作线程 (由于至少一个 NUMA 节点存在可用 CPU 较少的情况,因此速度变慢)。 通过在 NUMA 节点上平均分配 CPU, 应用程序开发人员可以更轻松地确保没有某个工作线程单独受到 NUMA 影响, 从而提高这些类型应用程序的整体性能。

如果指定了 align-by-socket 策略选项,那么在决定如何分配 CPU 给容器时,CPU 将被视为在 CPU 的插槽边界对齐。 默认情况下,CPUManager 在 NUMA 边界对齐 CPU 分配,如果需要从多个 NUMA 节点提取出 CPU 以满足分配,将可能会导致系统性能下降。 尽管 align-by-socket 策略试图确保从 NUMA 节点的最小数量分配所有 CPU,但不能保证这些 NUMA 节点将位于同一个 CPU 的插槽上。 通过指示 CPUManager 在 CPU 的插槽边界而不是 NUMA 边界显式对齐 CPU,我们能够避免此类问题。 注意,此策略选项不兼容 TopologyManagersingle-numa-node 策略,并且不适用于 CPU 的插槽数量大于 NUMA 节点数量的硬件。

可以通过将 full-pcpus-only=true 添加到 CPUManager 策略选项来启用 full-pcpus-only 选项。 同样地,可以通过将 distribute-cpus-across-numa=true 添加到 CPUManager 策略选项来启用 distribute-cpus-across-numa 选项。 当两者都设置时,它们是“累加的”,因为 CPU 将分布在 NUMA 节点的 full-pcpus 块中,而不是单个核心。 可以通过将 align-by-socket=true 添加到 CPUManager 策略选项来启用 align-by-socket 策略选项。 同样,也能够将 distribute-cpus-across-numa=true 添加到 full-pcpus-onlydistribute-cpus-across-numa 策略选项中。

4.2.16 - 控制节点上的拓扑管理策略

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

越来越多的系统利用 CPU 和硬件加速器的组合来支持要求低延迟的任务和高吞吐量的并行计算。 这类负载包括电信、科学计算、机器学习、金融服务和数据分析等。 此类混合系统需要有高性能环境支持。

为了获得最佳性能,需要进行与 CPU 隔离、内存和设备局部性有关的优化。 但是,在 Kubernetes 中,这些优化由各自独立的组件集合来处理。

拓扑管理器(Topology Manager) 是一个 kubelet 组件,旨在协调负责这些优化的一组组件。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你的 Kubernetes 服务器版本必须不低于版本 v1.18. 要获知版本信息,请输入 kubectl version.

拓扑管理器如何工作

在引入拓扑管理器之前,Kubernetes 中的 CPU 和设备管理器相互独立地做出资源分配决策。 这可能会导致在多处理系统上出现不符合期望的资源分配情况;由于这些与期望相左的分配,对性能或延迟敏感的应用将受到影响。 这里的不符合期望意指,例如,CPU 和设备是从不同的 NUMA 节点分配的,因此会导致额外的延迟。

拓扑管理器是一个 Kubelet 组件,扮演信息源的角色,以便其他 Kubelet 组件可以做出与拓扑结构相对应的资源分配决定。

拓扑管理器为组件提供了一个称为 建议提供者(Hint Provider) 的接口,以发送和接收拓扑信息。 拓扑管理器具有一组节点级策略,具体说明如下。

拓扑管理器从 建议提供者 接收拓扑信息,作为表示可用的 NUMA 节点和首选分配指示的位掩码。 拓扑管理器策略对所提供的建议执行一组操作,并根据策略对提示进行约减以得到最优解; 如果存储了与预期不符的建议,则该建议的优选字段将被设置为 false。 在当前策略中,首选是最窄的优选掩码。 所选建议将被存储为拓扑管理器的一部分。 取决于所配置的策略,所选建议可用来决定节点接受或拒绝 Pod。 之后,建议会被存储在拓扑管理器中,供 建议提供者 在作资源分配决策时使用。

拓扑管理器作用域和策略

拓扑管理器目前:

  • 对所有 QoS 类的 Pod 执行对齐操作。
  • 针对建议提供者所提供的拓扑建议,对请求的资源进行对齐。

如果满足这些条件,则拓扑管理器将对齐请求的资源。

为了定制如何进行对齐,拓扑管理器提供了两种不同的方式:scopepolicy

scope 定义了你希望的资源对齐粒度(例如,是在 pod 还是 container 层级上对齐)。 policy 定义了对齐时实际使用的策略(例如,best-effortrestrictedsingle-numa-node 等等)。 可以在下文找到现今可用的各种 scopespolicies 的具体信息。

拓扑管理器作用域

拓扑管理器可以在以下不同的作用域内进行资源对齐:

  • container (默认)
  • pod

在 kubelet 启动时,可以使用 --topology-manager-scope 标志来选择其中任一选项。

容器作用域

默认使用的是 container 作用域。

在该作用域内,拓扑管理器依次进行一系列的资源对齐, 也就是,对(Pod 中的)每一个容器计算单独的对齐。 换句话说,在该特定的作用域内,没有根据特定的 NUMA 节点集来把容器分组的概念。 实际上,拓扑管理器会把单个容器任意地对齐到 NUMA 节点上。

容器分组的概念是在以下的作用域内特别实现的,也就是 pod 作用域。

Pod 作用域

启动 kubelet 时附带 --topology-manager-scope=pod 命令行选项,就可以选择 pod 作用域。

该作用域允许把一个 Pod 里的所有容器作为一个分组,分配到一个共同的 NUMA 节点集。 也就是,拓扑管理器会把一个 Pod 当成一个整体, 并且试图把整个 Pod(所有容器)分配到一个单个的 NUMA 节点或者一个共同的 NUMA 节点集。 以下的例子说明了拓扑管理器在不同的场景下使用的对齐方式:

  • 所有容器可以被分配到一个单一的 NUMA 节点,实际上也是这样分配的;
  • 所有容器可以被分配到一个共享的 NUMA 节点集,实际上也是这样分配的。

整个 Pod 所请求的某种资源总量是根据 有效 request/limit 公式来计算的,因此,对某一种资源而言,该总量等于以下数值中的最大值:

  • 所有应用容器请求之和;
  • 初始容器请求的最大值。

pod 作用域与 single-numa-node 拓扑管理器策略一起使用, 对于延时敏感的工作负载,或者对于进行 IPC 的高吞吐量应用程序,都是特别有价值的。 把这两个选项组合起来,你可以把一个 Pod 里的所有容器都放到一个单个的 NUMA 节点, 使得该 Pod 消除了 NUMA 之间的通信开销。

single-numa-node 策略下,只有当可能的分配方案中存在合适的 NUMA 节点集时,Pod 才会被接受。 重新考虑上述的例子:

  • 节点集只包含单个 NUMA 节点时,Pod 就会被接受,
  • 然而,节点集包含多个 NUMA 节点时,Pod 就会被拒绝 (因为满足该分配方案需要两个或以上的 NUMA 节点,而不是单个 NUMA 节点)。

简要地说,拓扑管理器首先计算出 NUMA 节点集,然后使用拓扑管理器策略来测试该集合, 从而决定拒绝或者接受 Pod。

拓扑管理器策略

拓扑管理器支持四种分配策略。 你可以通过 Kubelet 标志 --topology-manager-policy 设置策略。 所支持的策略有四种:

  • none (默认)
  • best-effort
  • restricted
  • single-numa-node

none 策略

这是默认策略,不执行任何拓扑对齐。

best-effort 策略

对于 Pod 中的每个容器,具有 best-effort 拓扑管理策略的 kubelet 将调用每个建议提供者以确定资源可用性。 使用此信息,拓扑管理器存储该容器的首选 NUMA 节点亲和性。 如果亲和性不是首选,则拓扑管理器将存储该亲和性,并且无论如何都将 Pod 接纳到该节点。

之后建议提供者可以在进行资源分配决策时使用这个信息。

restricted 策略

对于 Pod 中的每个容器,配置了 restricted 拓扑管理策略的 kubelet 调用每个建议提供者以确定其资源可用性。 使用此信息,拓扑管理器存储该容器的首选 NUMA 节点亲和性。 如果亲和性不是首选,则拓扑管理器将从节点中拒绝此 Pod。 这将导致 Pod 处于 Terminated 状态,且 Pod 无法被节点接受。

一旦 Pod 处于 Terminated 状态,Kubernetes 调度器将不会尝试重新调度该 Pod。 建议使用 ReplicaSet 或者 Deployment 来触发重新部署 Pod。 还可以通过实现外部控制环,以触发重新部署具有 Topology Affinity 错误的 Pod。

如果 Pod 被允许运行在某节点,则建议提供者可以在做出资源分配决定时使用此信息。

single-numa-node 策略

对于 Pod 中的每个容器,配置了 single-numa-node 拓扑管理策略的 kubelet 调用每个建议提供者以确定其资源可用性。 使用此信息,拓扑管理器确定是否支持单 NUMA 节点亲和性。 如果支持,则拓扑管理器将存储此信息,然后 建议提供者 可以在做出资源分配决定时使用此信息。 如果不支持,则拓扑管理器将拒绝 Pod 运行于该节点。 这将导致 Pod 处于 Terminated 状态,且 Pod 无法被节点接受。

一旦 Pod 处于 Terminated 状态,Kubernetes 调度器将不会尝试重新调度该 Pod。 建议使用多副本的 Deployment 来触发重新部署 Pod。 还可以通过实现外部控制环,以触发重新部署具有 Topology Affinity 错误的 Pod。

拓扑管理器策略选项

对拓扑管理器策略选项的支持需要启用 TopologyManagerPolicyOptions 特性门控(默认启用)。

你可以使用以下特性门控根据成熟度级别打开和关闭这些选项组:

  • TopologyManagerPolicyBetaOptions 默认启用。启用以显示 Beta 级别选项。
  • TopologyManagerPolicyAlphaOptions 默认禁用。启用以显示 Alpha 级别选项。

你仍然需要使用 TopologyManagerPolicyOptions kubelet 选项来启用每个选项。

存在以下策略选项:

  • prefer-closest-numa-nodes(Beta,默认可见,TopologyManagerPolicyOptionsTopologyManagerPolicyAlphaOptions 特性门控必须被启用)。 prefer-closest-numa-nodes 策略选项在 Kubernetes 1.30 中是 Beta 版。

如果 prefer-closest-numa-nodes 策略选项被指定,则在做出准入决策时 best-effortrestricted 策略将偏向于彼此之间距离较短的一组 NUMA 节点。 你可以通过将 prefer-closest-numa-nodes=true 添加到拓扑管理器策略选项来启用此选项。 默认情况下,如果没有此选项,拓扑管理器会在单个 NUMA 节点或(在需要多个 NUMA 节点时)最小数量的 NUMA 节点上对齐资源。 然而,TopologyManager 无法感知到 NUMA 距离且在做出准入决策时也没有考虑这些距离。 这种限制出现在多插槽以及单插槽多 NUMA 系统中,如果拓扑管理器决定在非相邻 NUMA 节点上对齐资源, 可能导致对执行延迟敏感和高吞吐的应用程序出现明显的性能下降。

Pod 与拓扑管理器策略的交互

考虑以下 Pod 规范中的容器:

spec:
  containers:
  - name: nginx
    image: nginx

该 Pod 以 BestEffort QoS 类运行,因为没有指定资源 requestslimits

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
      requests:
        memory: "100Mi"

由于 requests 数少于 limits,因此该 Pod 以 Burstable QoS 类运行。

如果选择的策略是 none 以外的任何其他策略,拓扑管理器都会评估这些 Pod 的规范。 拓扑管理器会咨询建议提供者,获得拓扑建议。 若策略为 static,则 CPU 管理器策略会返回默认的拓扑建议,因为这些 Pod 并没有显式地请求 CPU 资源。

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "2"
        example.com/device: "1"
      requests:
        memory: "200Mi"
        cpu: "2"
        example.com/device: "1"

此 Pod 独立使用 CPU 请求量,以 Guaranteed QoS 类运行,因为其 requests 值等于 limits 值。

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "300m"
        example.com/device: "1"
      requests:
        memory: "200Mi"
        cpu: "300m"
        example.com/device: "1"

此 Pod 和其他资源共享 CPU 请求量,以 Guaranteed QoS 类运行,因为其 requests 值等于 limits 值。

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        example.com/deviceA: "1"
        example.com/deviceB: "1"
      requests:
        example.com/deviceA: "1"
        example.com/deviceB: "1"

因为未指定 CPU 和内存请求,所以 Pod 以 BestEffort QoS 类运行。

拓扑管理器将考虑以上 Pod。拓扑管理器将咨询建议提供者即 CPU 和设备管理器,以获取 Pod 的拓扑提示。 对于独立使用 CPU 请求量的 Guaranteed Pod,static CPU 管理器策略将返回独占 CPU 相关的拓扑提示, 而设备管理器将返回有关所请求设备的提示。

对于与其他资源 CPU 共享请求量的 Guaranteed Pod,static CPU 管理器策略将返回默认的拓扑提示,因为没有独享的 CPU 请求;而设备管理器 则针对所请求的设备返回有关提示。

在上述两种 Guaranteed Pod 的情况中,none CPU 管理器策略会返回默认的拓扑提示。

对于 BestEffort Pod,由于没有 CPU 请求,static CPU 管理器策略将发送默认拓扑提示, 而设备管理器将为每个请求的设备发送提示。

基于此信息,拓扑管理器将为 Pod 计算最佳提示并存储该信息,并且供 提示提供程序在进行资源分配时使用。

已知的局限性

  1. 拓扑管理器所能处理的最大 NUMA 节点个数是 8。若 NUMA 节点数超过 8, 枚举可能的 NUMA 亲和性并为之生成提示时会发生状态爆炸。
  2. 调度器无法感知拓扑,所以有可能一个 Pod 被调度到一个节点之后,会因为拓扑管理器的缘故在该节点上启动失败。

4.2.17 - 自定义 DNS 服务

本页说明如何配置 DNS Pod,以及定制集群中 DNS 解析过程。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你的集群必须运行 CoreDNS 插件。

你的 Kubernetes 服务器版本必须不低于版本 v1.12. 要获知版本信息,请输入 kubectl version.

介绍

DNS 是使用 插件管理器 集群插件自动启动的 Kubernetes 内置服务。

如果你在使用 Deployment 运行 CoreDNS,则该 Deployment 通常会向外暴露为一个具有 静态 IP 地址 Kubernetes 服务。 kubelet 使用 --cluster-dns=<DNS 服务 IP> 标志将 DNS 解析器的信息传递给每个容器。

DNS 名称也需要域名。你可在 kubelet 中使用 --cluster-domain=<默认本地域名> 标志配置本地域名。

DNS 服务器支持正向查找(A 和 AAAA 记录)、端口发现(SRV 记录)、反向 IP 地址发现(PTR 记录)等。 更多信息,请参见 Service 与 Pod 的 DNS

如果 Pod 的 dnsPolicy 设置为 default,则它将从 Pod 运行所在节点继承名称解析配置。 Pod 的 DNS 解析行为应该与节点相同。 但请参阅已知问题

如果你不想这样做,或者想要为 Pod 使用其他 DNS 配置,则可以使用 kubelet 的 --resolv-conf 标志。将此标志设置为 "" 可以避免 Pod 继承 DNS。 将其设置为有别于 /etc/resolv.conf 的有效文件路径可以设定 DNS 继承不同的配置。

CoreDNS

CoreDNS 是通用的权威 DNS 服务器,可以用作集群 DNS,符合 DNS 规范

CoreDNS ConfigMap 选项

CoreDNS 是模块化且可插拔的 DNS 服务器,每个插件都为 CoreDNS 添加了新功能。 可以通过维护 Corefile,即 CoreDNS 配置文件, 来配置 CoreDNS 服务器。作为一个集群管理员,你可以修改 CoreDNS Corefile 的 ConfigMap, 以更改 DNS 服务发现针对该集群的工作方式。

在 Kubernetes 中,CoreDNS 安装时使用如下默认 Corefile 配置:

apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns
  namespace: kube-system
data:
  Corefile: |
    .:53 {
        errors
        health {
            lameduck 5s
        }
        ready
        kubernetes cluster.local in-addr.arpa ip6.arpa {
            pods insecure
            fallthrough in-addr.arpa ip6.arpa
            ttl 30
        }
        prometheus :9153
        forward . /etc/resolv.conf
        cache 30
        loop
        reload
        loadbalance
    }    

Corefile 配置包括以下 CoreDNS 插件

  • errors:错误记录到标准输出。

  • health:在 http://localhost:8080/health 处提供 CoreDNS 的健康报告。 在这个扩展语法中,lameduck 会使此进程不健康,等待 5 秒后进程被关闭。

  • ready:在端口 8181 上提供的一个 HTTP 端点, 当所有能够表达自身就绪的插件都已就绪时,在此端点返回 200 OK。

  • kubernetes:CoreDNS 将基于服务和 Pod 的 IP 来应答 DNS 查询。 你可以在 CoreDNS 网站找到有关此插件的更多细节

    • 你可以使用 ttl 来定制响应的 TTL。默认值是 5 秒钟。TTL 的最小值可以是 0 秒钟, 最大值为 3600 秒。将 TTL 设置为 0 可以禁止对 DNS 记录进行缓存。
    • pods insecure 选项是为了与 kube-dns 向后兼容。
    • 你可以使用 pods verified 选项,该选项使得仅在相同名字空间中存在具有匹配 IP 的 Pod 时才返回 A 记录。
    • 如果你不使用 Pod 记录,则可以使用 pods disabled 选项。
  • prometheus:CoreDNS 的度量指标值以 Prometheus 格式(也称为 OpenMetrics)在 http://localhost:9153/metrics 上提供。
  • forward: 不在 Kubernetes 集群域内的任何查询都将转发到预定义的解析器 (/etc/resolv.conf)。
  • cache:启用前端缓存。
  • loop:检测简单的转发环,如果发现死循环,则中止 CoreDNS 进程。
  • reload:允许自动重新加载已更改的 Corefile。 编辑 ConfigMap 配置后,请等待两分钟,以使更改生效。
  • loadbalance:这是一个轮转式 DNS 负载均衡器, 它在应答中随机分配 A、AAAA 和 MX 记录的顺序。

你可以通过修改 ConfigMap 来更改默认的 CoreDNS 行为。

使用 CoreDNS 配置存根域和上游域名服务器

CoreDNS 能够使用 forward 插件配置存根域和上游域名服务器。

示例

如果集群操作员在 "10.150.0.1" 处运行了 Consul 域服务器, 且所有 Consul 名称都带有后缀 .consul.local。要在 CoreDNS 中对其进行配置, 集群管理员可以在 CoreDNS 的 ConfigMap 中创建加入以下字段。

consul.local:53 {
    errors
    cache 30
    forward . 10.150.0.1
}

要显式强制所有非集群 DNS 查找通过特定的域名服务器(位于 172.16.0.1),可将 forward 指向该域名服务器,而不是 /etc/resolv.conf

forward .  172.16.0.1

最终的包含默认的 Corefile 配置的 ConfigMap 如下所示:

apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns
  namespace: kube-system
data:
  Corefile: |
    .:53 {
        errors
        health
        kubernetes cluster.local in-addr.arpa ip6.arpa {
           pods insecure
           fallthrough in-addr.arpa ip6.arpa
        }
        prometheus :9153
        forward . 172.16.0.1
        cache 30
        loop
        reload
        loadbalance
    }
    consul.local:53 {
        errors
        cache 30
        forward . 10.150.0.1
    }    

接下来

4.2.18 - 调试 DNS 问题

这篇文章提供了一些关于 DNS 问题诊断的方法。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你的集群必须使用了 CoreDNS 插件 或者其前身,kube-dns

你的 Kubernetes 服务器版本必须不低于版本 v1.6. 要获知版本信息,请输入 kubectl version.

创建一个简单的 Pod 作为测试环境

apiVersion: v1
kind: Pod
metadata:
  name: dnsutils
  namespace: default
spec:
  containers:
  - name: dnsutils
    image: registry.k8s.io/e2e-test-images/jessie-dnsutils:1.3
    command:
      - sleep
      - "infinity"
    imagePullPolicy: IfNotPresent
  restartPolicy: Always

使用上面的清单来创建一个 Pod:

kubectl apply -f https://k8s.io/examples/admin/dns/dnsutils.yaml
pod/dnsutils created

验证其状态:

kubectl get pods dnsutils
NAME      READY     STATUS    RESTARTS   AGE
dnsutils   1/1       Running   0          <some-time>

一旦 Pod 处于运行状态,你就可以在该环境里执行 nslookup。 如果你看到类似下列的内容,则表示 DNS 是正常运行的。

kubectl exec -i -t dnsutils -- nslookup kubernetes.default

输出为:

Server:    10.0.0.10
Address 1: 10.0.0.10

Name:      kubernetes.default
Address 1: 10.0.0.1

如果 nslookup 命令执行失败,请检查下列内容:

先检查本地的 DNS 配置

查看 resolv.conf 文件的内容 (阅读定制 DNS 服务 和 后文的已知问题 ,获取更多信息)

kubectl exec -ti dnsutils -- cat /etc/resolv.conf

验证 search 和 nameserver 的配置是否与下面的内容类似 (注意 search 根据不同的云提供商可能会有所不同):

search default.svc.cluster.local svc.cluster.local cluster.local google.internal c.gce_project_id.internal
nameserver 10.0.0.10
options ndots:5

下列错误表示 CoreDNS (或 kube-dns)插件或者相关服务出现了问题:

kubectl exec -i -t dnsutils -- nslookup kubernetes.default

输出为:

Server:    10.0.0.10
Address 1: 10.0.0.10

nslookup: can't resolve 'kubernetes.default'

或者

kubectl exec -i -t dnsutils -- nslookup kubernetes.default

输出为:

Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

nslookup: can't resolve 'kubernetes.default'

检查 DNS Pod 是否运行

使用 kubectl get pods 命令来验证 DNS Pod 是否运行。

kubectl get pods --namespace=kube-system -l k8s-app=kube-dns

输出为:

NAME                       READY     STATUS    RESTARTS   AGE
...
coredns-7b96bf9f76-5hsxb   1/1       Running   0           1h
coredns-7b96bf9f76-mvmmt   1/1       Running   0           1h
...

如果你发现没有 CoreDNS Pod 在运行,或者该 Pod 的状态是 failed 或者 completed, 那可能这个 DNS 插件在你当前的环境里并没有成功部署,你将需要手动去部署它。

检查 DNS Pod 里的错误

使用 kubectl logs 命令来查看 DNS 容器的日志信息。

如查看 CoreDNS 的日志信息:

kubectl logs --namespace=kube-system -l k8s-app=kube-dns

下列是一个正常运行的 CoreDNS 日志信息:

.:53
2018/08/15 14:37:17 [INFO] CoreDNS-1.2.2
2018/08/15 14:37:17 [INFO] linux/amd64, go1.10.3, 2e322f6
CoreDNS-1.2.2
linux/amd64, go1.10.3, 2e322f6
2018/08/15 14:37:17 [INFO] plugin/reload: Running configuration MD5 = 24e6c59e83ce706f07bcc82c31b1ea1c

查看是否日志中有一些可疑的或者意外的消息。

检查是否启用了 DNS 服务

使用 kubectl get service 命令来检查 DNS 服务是否已经启用。

kubectl get svc --namespace=kube-system

输出为:

NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)             AGE
...
kube-dns     ClusterIP   10.0.0.10      <none>        53/UDP,53/TCP        1h
...

如果你已经创建了 DNS 服务,或者该服务应该是默认自动创建的但是它并没有出现, 请阅读调试服务 来获取更多信息。

DNS 的端点公开了吗?

你可以使用 kubectl get endpoints 命令来验证 DNS 的端点是否公开了。

kubectl get endpoints kube-dns --namespace=kube-system
NAME       ENDPOINTS                       AGE
kube-dns   10.180.3.17:53,10.180.3.17:53    1h

如果你没看到对应的端点,请阅读 调试服务的端点部分。

若需要了解更多的 Kubernetes DNS 例子,请在 Kubernetes GitHub 仓库里查看 cluster-dns 示例

DNS 查询有被接收或者执行吗?

你可以通过给 CoreDNS 的配置文件(也叫 Corefile)添加 log 插件来检查查询是否被正确接收。 CoreDNS 的 Corefile 被保存在一个叫 corednsConfigMap 里,使用下列命令来编辑它:

kubectl -n kube-system edit configmap coredns

然后按下面的例子给 Corefile 添加 log

apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns
  namespace: kube-system
data:
  Corefile: |
    .:53 {
        log
        errors
        health
        kubernetes cluster.local in-addr.arpa ip6.arpa {
          pods insecure
          upstream
          fallthrough in-addr.arpa ip6.arpa
        }
        prometheus :9153
        forward . /etc/resolv.conf
        cache 30
        loop
        reload
        loadbalance
    }    

保存这些更改后,你可能会需要等待一到两分钟让 Kubernetes 把这些更改应用到 CoreDNS 的 Pod 里。

接下来,发起一些查询并依照前文所述查看日志信息,如果 CoreDNS 的 Pod 接收到这些查询, 你将可以在日志信息里看到它们。

下面是日志信息里的查询例子:

.:53
2018/08/15 14:37:15 [INFO] CoreDNS-1.2.0
2018/08/15 14:37:15 [INFO] linux/amd64, go1.10.3, 2e322f6
CoreDNS-1.2.0
linux/amd64, go1.10.3, 2e322f6
2018/09/07 15:29:04 [INFO] plugin/reload: Running configuration MD5 = 162475cdf272d8aa601e6fe67a6ad42f
2018/09/07 15:29:04 [INFO] Reloading complete
172.17.0.18:41675 - [07/Sep/2018:15:29:11 +0000] 59925 "A IN kubernetes.default.svc.cluster.local. udp 54 false 512" NOERROR qr,aa,rd,ra 106 0.000066649s

CoreDNS 是否有足够的权限?

CoreDNS 必须能够列出 serviceendpoint 相关的资源来正确解析服务名称。

示例错误消息:

2022-03-18T07:12:15.699431183Z [INFO] 10.96.144.227:52299 - 3686 "A IN serverproxy.contoso.net.cluster.local. udp 52 false 512" SERVFAIL qr,aa,rd 145 0.000091221s

首先,获取当前的 ClusterRole system:coredns

kubectl describe clusterrole system:coredns -n kube-system

预期输出:

PolicyRule:
  Resources                        Non-Resource URLs  Resource Names  Verbs
  ---------                        -----------------  --------------  -----
  endpoints                        []                 []              [list watch]
  namespaces                       []                 []              [list watch]
  pods                             []                 []              [list watch]
  services                         []                 []              [list watch]
  endpointslices.discovery.k8s.io  []                 []              [list watch]

如果缺少任何权限,请编辑 ClusterRole 来添加它们:

kubectl edit clusterrole system:coredns -n kube-system

EndpointSlices 权限的插入示例:

...
- apiGroups:
  - discovery.k8s.io
  resources:
  - endpointslices
  verbs:
  - list
  - watch
...

你的服务在正确的名字空间中吗?

未指定名字空间的 DNS 查询仅作用于 Pod 所在的名字空间。

如果 Pod 和服务的名字空间不相同,则 DNS 查询必须指定服务所在的名字空间。

该查询仅限于 Pod 所在的名字空间:

kubectl exec -i -t dnsutils -- nslookup <service-name>

指定名字空间的查询:

kubectl exec -i -t dnsutils -- nslookup <service-name>.<namespace>

要进一步了解名字解析,请查看 Pod 与 Service 的 DNS

已知问题

有些 Linux 发行版本(比如 Ubuntu)默认使用一个本地的 DNS 解析器(systemd-resolved)。 systemd-resolved 会用一个存根文件(Stub File)来覆盖 /etc/resolv.conf 内容, 从而可能在上游服务器中解析域名产生转发环(forwarding loop)。 这个问题可以通过手动指定 kubelet 的 --resolv-conf 标志为正确的 resolv.conf(如果是 systemd-resolved, 则这个文件路径为 /run/systemd/resolve/resolv.conf)来解决。 kubeadm 会自动检测 systemd-resolved 并对应的更改 kubelet 的命令行标志。

Kubernetes 的安装并不会默认配置节点的 resolv.conf 文件来使用集群的 DNS 服务,因为这个配置对于不同的发行版本是不一样的。这个问题应该迟早会被解决的。

Linux 的 libc(又名 glibc)默认将 DNS nameserver 记录限制为 3, 而 Kubernetes 需要使用 1 条 nameserver 记录。 这意味着如果本地的安装已经使用了 3 个 nameserver,那么其中有些条目将会丢失。 要解决此限制,节点可以运行 dnsmasq,以提供更多 nameserver 条目。 你也可以使用 kubelet 的 --resolv-conf 标志来解决这个问题。

如果你使用 Alpine 3.17 或更早版本作为你的基础镜像,DNS 可能会由于 Alpine 的设计问题而无法工作。 在 musl 1.24 版本之前,DNS 存根解析器都没有包括 TCP 回退, 这意味着任何超过 512 字节的 DNS 调用都会失败。请将你的镜像升级到 Alpine 3.18 或更高版本。

接下来

4.2.19 - 声明网络策略

本文可以帮助你开始使用 Kubernetes 的 NetworkPolicy API 声明网络策略去管理 Pod 之间的通信

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你的 Kubernetes 服务器版本必须不低于版本 v1.8. 要获知版本信息,请输入 kubectl version.

你首先需要有一个支持网络策略的 Kubernetes 集群。已经有许多支持 NetworkPolicy 的网络提供商,包括:

创建一个nginx Deployment 并且通过服务将其暴露

为了查看 Kubernetes 网络策略是怎样工作的,可以从创建一个nginx Deployment 并且通过服务将其暴露开始

kubectl create deployment nginx --image=nginx
deployment.apps/nginx created

将此 Deployment 以名为 nginx 的 Service 暴露出来:

kubectl expose deployment nginx --port=80
service/nginx exposed

上述命令创建了一个带有一个 nginx 的 Deployment,并将之通过名为 nginx 的 Service 暴露出来。名为 nginx 的 Pod 和 Deployment 都位于 default 名字空间内。

kubectl get svc,pod
NAME                        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE
service/kubernetes          10.100.0.1    <none>        443/TCP    46m
service/nginx               10.100.0.16   <none>        80/TCP     33s

NAME                        READY         STATUS        RESTARTS   AGE
pod/nginx-701339712-e0qfq   1/1           Running       0          35s

通过从 Pod 访问服务对其进行测试

你应该可以从其它的 Pod 访问这个新的 nginx 服务。 要从 default 命名空间中的其它 Pod 来访问该服务。可以启动一个 busybox 容器:

kubectl run busybox --rm -ti --image=busybox:1.28 -- /bin/sh

在你的 Shell 中,运行下面的命令:

wget --spider --timeout=1 nginx
Connecting to nginx (10.100.0.16:80)
remote file exists

限制 nginx 服务的访问

如果想限制对 nginx 服务的访问,只让那些拥有标签 access: true 的 Pod 访问它, 那么可以创建一个如下所示的 NetworkPolicy 对象:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: access-nginx
spec:
  podSelector:
    matchLabels:
      app: nginx
  ingress:
  - from:
    - podSelector:
        matchLabels:
          access: "true"

NetworkPolicy 对象的名称必须是一个合法的 DNS 子域名.

为服务指定策略

使用 kubectl 根据上面的 nginx-policy.yaml 文件创建一个 NetworkPolicy:

kubectl apply -f https://k8s.io/examples/service/networking/nginx-policy.yaml
networkpolicy.networking.k8s.io/access-nginx created

测试没有定义访问标签时访问服务

如果你尝试从没有设定正确标签的 Pod 中去访问 nginx 服务,请求将会超时:

kubectl run busybox --rm -ti --image=busybox:1.28 -- /bin/sh

在 Shell 中运行命令:

wget --spider --timeout=1 nginx
Connecting to nginx (10.100.0.16:80)
wget: download timed out

定义访问标签后再次测试

创建一个拥有正确标签的 Pod,你将看到请求是被允许的:

kubectl run busybox --rm -ti --labels="access=true" --image=busybox:1.28 -- /bin/sh

在 Shell 中运行命令:

wget --spider --timeout=1 nginx
Connecting to nginx (10.100.0.16:80)
remote file exists

4.2.20 - 开发云控制器管理器

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

一个 Kubernetes 控制平面组件, 嵌入了特定于云平台的控制逻辑。 云控制器管理器(Cloud Controller Manager) 允许将你的集群连接到云提供商的 API 之上, 并将与该云平台交互的组件同与你的集群交互的组件分离开来。

通过分离 Kubernetes 和底层云基础设置之间的互操作性逻辑, cloud-controller-manager 组件使云提供商能够以不同于 Kubernetes 主项目的步调发布新特征。

背景

由于云驱动的开发和发布与 Kubernetes 项目本身步调不同,将特定于云环境的代码抽象到 cloud-controller-manager 二进制组件有助于云厂商独立于 Kubernetes 核心代码推进其驱动开发。

Kubernetes 项目提供 cloud-controller-manager 的框架代码,其中包含 Go 语言的接口, 便于你(或者你的云驱动提供者)接驳你自己的实现。这意味着每个云驱动可以通过从 Kubernetes 核心代码导入软件包来实现一个 cloud-controller-manager; 每个云驱动会通过调用 cloudprovider.RegisterCloudProvider 接口来注册其自身实现代码, 从而更新一个用来记录可用云驱动的全局变量。

开发

树外(Out of Tree)

要为你的云环境构建一个树外(Out-of-Tree)云控制器管理器:

  1. 使用满足 cloudprovider.Interface 接口的实现来创建一个 Go 语言包。
  2. 使用来自 Kubernetes 核心代码库的 cloud-controller-manager 中的 main.go 作为 main.go 的模板。如上所述,唯一的区别应该是将导入的云包不同。
  3. main.go 中导入你的云包,确保你的包有一个 init 块来运行 cloudprovider.RegisterCloudProvider

很多云驱动都将其控制器管理器代码以开源代码的形式公开。 如果你在开发一个新的 cloud-controller-manager,你可以选择某个树外(Out-of-Tree) 云控制器管理器作为出发点。

树内(In Tree)

对于树内(In-Tree)驱动,你可以将树内云控制器管理器作为集群中的 DaemonSet 来运行。 有关详细信息,请参阅云控制器管理器管理

4.2.21 - 启用/禁用 Kubernetes API

本页展示怎么用集群的 控制平面. 启用/禁用 API 版本。

通过 API 服务器的命令行参数 --runtime-config=api/<version> , 可以开启/关闭某个指定的 API 版本。 此参数的值是一个逗号分隔的 API 版本列表。 此列表中,后面的值可以覆盖前面的值。

命令行参数 runtime-config 支持两个特殊的值(keys):

  • api/all:指所有已知的 API
  • api/legacy:指过时的 API。过时的 API 就是明确地 弃用 的 API。

例如:为了停用除去 v1 版本之外的全部其他 API 版本, 就用参数 --runtime-config=api/all=false,api/v1=true 启动 kube-apiserver

接下来

阅读完整的文档, 以了解 kube-apiserver 组件。

4.2.22 - 静态加密机密数据

Kubernetes 中允许允许用户编辑的持久 API 资源数据的所有 API 都支持静态加密。 例如,你可以启用静态加密 Secret。 此静态加密是对 etcd 集群或运行 kube-apiserver 的主机上的文件系统的任何系统级加密的补充。

本页展示如何启用和配置静态 API 数据加密。

准备开始

  • 你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

  • 此任务假设你将 Kubernetes API 服务器组件以静态 Pod 方式运行在每个控制平面节点上。

  • 集群的控制平面必须使用 etcd v3.x(主版本 3,任何次要版本)。

  • 要加密自定义资源,你的集群必须运行 Kubernetes v1.26 或更高版本。

  • 在 Kubernetes v1.27 或更高版本中可以使用通配符匹配资源。

要获知版本信息,请输入 kubectl version.

确定是否已启用静态数据加密

默认情况下,API 服务器将资源的明文表示存储在 etcd 中,没有静态加密。

kube-apiserver 进程使用 --encryption-provider-config 参数指定配置文件的路径, 所指定的配置文件的内容将控制 Kubernetes API 数据在 etcd 中的加密方式。 如果你在运行 kube-apiserver 时没有使用 --encryption-provider-config 命令行参数, 则你未启用静态加密。如果你在运行 kube-apiserver 时使用了 --encryption-provider-config 命令行参数,并且此参数所引用的文件指定 identity 提供程序作为加密提供程序列表中的第一个, 则你未启用静态加密(默认的 identity 提供程序不提供任何机密性保护)。

如果你在运行 kube-apiserver 时使用了 --encryption-provider-config 命令行参数, 并且此参数所引用的文件指定一个不是 identity 的提供程序作为加密提供程序列表中的第一个, 则你已启用静态加密。然而此项检查并未告知你先前向加密存储的迁移是否成功。如果你不确定, 请参阅确保所有相关数据都已加密

理解静态数据加密

---
#
# 注意:这是一个示例配置。请勿将其用于你自己的集群!
#
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
      - configmaps
      - pandas.awesome.bears.example # 自定义资源 API
    providers:
      # 此配置不提供数据机密性。
      # 第一个配置的 provider 正在指定将资源存储为纯文本的 "identity" 机制。
      - identity: {} # 纯文本,换言之未加密
      - aesgcm:
          keys:
            - name: key1
              secret: c2VjcmV0IGlzIHNlY3VyZQ==
            - name: key2
              secret: dGhpcyBpcyBwYXNzd29yZA==
      - aescbc:
          keys:
            - name: key1
              secret: c2VjcmV0IGlzIHNlY3VyZQ==
            - name: key2
              secret: dGhpcyBpcyBwYXNzd29yZA==
      - secretbox:
          keys:
            - name: key1
              secret: YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY=
  - resources:
      - events
    providers:
      - identity: {} # 即使如下指定 *.* 也不会加密 events
  - resources:
      - '*.apps' # 通配符匹配需要 Kubernetes 1.27 或更高版本
    providers:
      - aescbc:
          keys:
          - name: key2
            secret: c2VjcmV0IGlzIHNlY3VyZSwgb3IgaXMgaXQ/Cg==
  - resources:
      - '*.*' # 通配符匹配需要 Kubernetes 1.27 或更高版本
    providers:
      - aescbc:
          keys:
          - name: key3
            secret: c2VjcmV0IGlzIHNlY3VyZSwgSSB0aGluaw==

每个 resources 数组项目是一个单独的完整的配置。 resources.resources 字段是应加密的 Kubernetes 资源(例如 Secret、ConfigMap 或其他资源)名称 (resourceresource.group)的数组。

如果自定义资源被添加到 EncryptionConfiguration 并且集群版本为 1.26 或更高版本, 则 EncryptionConfiguration 中提到的任何新创建的自定义资源都将被加密。 在该版本之前存在于 etcd 中的任何自定义资源和配置不会被加密,直到它们被下一次写入到存储为止。 这与内置资源的行为相同。请参阅确保所有 Secret 都已加密一节。

providers 数组是可能的加密提供程序的有序列表,用于你所列出的 API。 每个提供程序支持多个密钥 - 解密时会按顺序尝试这些密钥, 如果这是第一个提供程序,其第一个密钥将被用于加密。

每个条目只能指定一个提供程序类型(可以是 identityaescbc,但不能在同一个项目中同时指定二者)。 列表中的第一个提供程序用于加密写入存储的资源。 当从存储器读取资源时,与存储的数据匹配的所有提供程序将按顺序尝试解密数据。 如果由于格式或密钥不匹配而导致没有提供程序能够读取存储的数据,则会返回一个错误,以防止客户端访问该资源。

EncryptionConfiguration 支持使用通配符指定应加密的资源。 使用 “*.<group>” 加密 group 内的所有资源(例如以上例子中的 “*.apps”)或使用 “*.*” 加密所有资源。“*.” 可用于加密核心组中的所有资源。“*.*” 将加密所有资源,甚至包括 API 服务器启动之后添加的自定义资源。

如果你有一个涵盖资源(resource)的通配符,并且想要过滤掉静态加密的特定类型资源, 则可以通过添加一个单独的 resources 数组项来实现此目的, 其中包含要豁免的资源的名称,还可以在其后跟一个 providers 数组项来指定 identity 提供商。 你可以将此数组项添加到列表中,以便它早于你指定加密的配置(不是 identity 的提供商)出现。

例如,如果启用了 '*.*',并且你想要选择不加密 Event 和 ConfigMap, 请在 resources靠前的位置添加一个新的条目,后跟带有 identity 的 providers 数组项作为提供程序。较为特定的条目必须位于通配符条目之前。

新项目看起来类似于:

  ...
  - resources:
      - configmaps. # 特定于来自核心 API 组的资源,因为结尾是 “.”
      - events
    providers:
      - identity: {}
  # 然后是资源中的其他条目

确保新项列在资源数组中的通配符 “*.*” 项之前,使新项优先。

有关 EncryptionConfiguration 结构体的更多详细信息, 请参阅加密配置 API

可用的提供程序

在为集群的 Kubernetes API 数据配置静态加密之前,你需要选择要使用的提供程序。

下表描述了每个可用的提供程序:

Kubernetes 静态数据加密的提供程序
名称加密类型强度速度密钥长度
identityN/AN/AN/A
不加密写入的资源。当设置为第一个提供程序时,已加密的资源将在新值写入时被解密。
aescbc带有 PKCS#7 填充的 AES-CBC32 字节
由于 CBC 容易受到密文填塞攻击(Padding Oracle Attack),不推荐使用。密钥材料可从控制面主机访问。
aesgcm带有随机数的 AES-GCM每写入 200k 次后必须轮换最快16、24 或者 32 字节
不建议使用,除非实施了自动密钥轮换方案。密钥材料可从控制面主机访问。
kms v1 (自 Kubernetes 1.28 起弃用)针对每个资源使用不同的 DEK 来完成信封加密。最强慢(kms V2 相比32 字节
通过数据加密密钥(DEK)使用 AES-GCM 加密数据; DEK 根据 Key Management Service(KMS)中的配置通过密钥加密密钥(Key Encryption Keys,KEK)加密。 密钥轮换方式简单,每次加密都会生成一个新的 DEK,KEK 的轮换由用户控制。
阅读如何配置 KMS V1 提供程序
kms v2针对每个 API 服务器使用不同的 DEK 来完成信封加密。最强32 字节
通过数据加密密钥(DEK)使用 AES-GCM 加密数据; DEK 根据 Key Management Service(KMS)中的配置通过密钥加密密钥(Key Encryption Keys,KEK)加密。 Kubernetes 基于秘密的种子数为每次加密生成一个新的 DEK。 每当 KEK 轮换时,种子也会轮换。 如果使用第三方工具进行密钥管理,这是一个不错的选择。 从 `v1.29` 开始,该功能处于稳定阶段。
阅读如何配置 KMS V2 提供程序
secretboxXSalsa20 和 Poly1305更快32 字节
使用相对较新的加密技术,在需要高度评审的环境中可能不被接受。密钥材料可从控制面主机访问。

如果你没有另外指定,identity 提供程序将作为默认选项。 identity 提供程序不会加密存储的数据,并且提供无附加的机密保护。

密钥存储

本地密钥存储

使用本地管理的密钥对 Secret 数据进行加密可以防止 etcd 受到威胁,但无法防范主机受到威胁的情况。 由于加密密钥被存储在主机上的 EncryptionConfiguration YAML 文件中,有经验的攻击者可以访问该文件并提取加密密钥。

托管的(KMS)密钥存储

KMS 提供程序使用封套加密:Kubernetes 使用一个数据密钥来加密资源,然后使用托管的加密服务来加密该数据密钥。 Kubernetes 为每个资源生成唯一的数据密钥。API 服务器将数据密钥的加密版本与密文一起存储在 etcd 中; API 服务器在读取资源时,调用托管的加密服务并提供密文和(加密的)数据密钥。 在托管的加密服务中,提供程序使用密钥加密密钥来解密数据密钥,解密数据密钥后恢复为明文。 在控制平面和 KMS 之间的通信需要在传输过程中提供 TLS 这类保护。

使用封套加密会依赖于密钥加密密钥,此密钥不存储在 Kubernetes 中。 就 KMS 而言,如果攻击者意图未经授权地访问明文值,则需要同时入侵 etcd 第三方 KMS 提供程序。

保护加密密钥

你应该采取适当的措施来保护允许解密的机密信息,无论是本地加密密钥还是允许 API 服务器调用 KMS 的身份验证令牌。

即使你依赖提供商来管理主加密密钥(或多个密钥)的使用和生命周期, 你仍然有责任确保托管加密服务的访问控制和其他安全措施满足你的安全需求。

加密你的数据

生成加密密钥

以下步骤假设你没有使用 KMS,因此这些步骤还假设你需要生成加密密钥。 如果你已有加密密钥,请跳至编写加密配置文件

首先生成新的加密密钥,然后使用 base64 对其进行编码:

生成 32 字节随机密钥并对其进行 base64 编码。你可以使用这个命令:

head -c 32 /dev/urandom | base64

如果你想使用 PC 的内置硬件熵源,可以使用 /dev/hwrng 而不是 /dev/urandom。 并非所有 Linux 设备都提供硬件随机数生成器。

生成 32 字节随机密钥并对其进行 base64 编码。你可以使用此命令:

head -c 32 /dev/urandom | base64

生成 32 字节随机密钥并对其进行 base64 编码。你可以使用此命令:

# 不要在已设置随机数生成器种子的会话中运行此命令。
[Convert]::ToBase64String((1..32|%{[byte](Get-Random -Max 256)}))

复制加密密钥

使用安全的文件传输机制,将该加密密钥的副本提供给所有其他控制平面主机。

至少,使用传输加密 - 例如,安全 shell(SSH)。为了提高安全性, 请在主机之间使用非对称加密,或更改你正在使用的方法,以便依赖 KMS 加密。

编辑加密配置文件

创建一个新的加密配置文件。其内容应类似于:

---
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
      - configmaps
      - pandas.awesome.bears.example
    providers:
      - aescbc:
          keys:
            - name: key1
              # 参见以下文本了解有关 Secret 值的详情
              secret: <BASE 64 ENCODED SECRET>
      - identity: {} # 这个回退允许读取未加密的 Secret;
                     # 例如,在初始迁移期间

要创建新的加密密钥(不使用 KMS),请参阅生成加密密钥

使用新的加密配置文件

你将需要把新的加密配置文件挂载到 kube-apiserver 静态 Pod。以下是这个操作的示例:

  1. 将新的加密配置文件保存到控制平面节点上的 /etc/kubernetes/enc/enc.yaml

  2. 编辑 kube-apiserver 静态 Pod 的清单:/etc/kubernetes/manifests/kube-apiserver.yaml, 代码范例如下:

    ---
    #
    # 这是一个静态 Pod 的清单片段。
    # 检查是否适用于你的集群和 API 服务器。
    #
    apiVersion: v1
    kind: Pod
    metadata:
    annotations:
     kubeadm.kubernetes.io/kube-apiserver.advertise-address.endpoint: 10.20.30.40:443
    creationTimestamp: null
    labels:
     app.kubernetes.io/component: kube-apiserver
     tier: control-plane
    name: kube-apiserver
    namespace: kube-system
    spec:
    containers:
    - command:
     - kube-apiserver
     ...
     - --encryption-provider-config=/etc/kubernetes/enc/enc.yaml  # 增加这一行
     volumeMounts:
     ...
     - name: enc                           # 增加这一行
       mountPath: /etc/kubernetes/enc      # 增加这一行
       readOnly: true                      # 增加这一行
     ...
    volumes:
    ...
    - name: enc                             # 增加这一行
     hostPath:                             # 增加这一行
       path: /etc/kubernetes/enc           # 增加这一行
       type: DirectoryOrCreate             # 增加这一行
    ...
    
  1. 重启你的 API 服务器。

你现在已经为一个控制平面主机进行了加密。典型的 Kubernetes 集群有多个控制平面主机,因此需要做的事情更多。

重新配置其他控制平面主机

如果你的集群中有多个 API 服务器,应轮流将更改部署到每个 API 服务器。

确保在每个控制平面主机上使用相同的加密配置。

验证数据已被加密

数据在写入 etcd 时会被加密。重新启动你的 kube-apiserver 后,任何新创建或更新的 Secret 或在 EncryptionConfiguration 中配置的其他资源类别都应在存储时被加密。

如果想要检查,你可以使用 etcdctl 命令行程序来检索你的 Secret 数据内容。

以下示例演示了如何对加密 Secret API 进行检查。

  1. 创建一个新的 Secret,名称为 secret1,命名空间为 default

    kubectl create secret generic secret1 -n default --from-literal=mykey=mydata
    
  1. 使用 etcdctl 命令行工具,从 etcd 中读取 Secret:

    ETCDCTL_API=3 etcdctl get /registry/secrets/default/secret1 [...] | hexdump -C
    

    这里的 [...] 是用来连接 etcd 服务的额外参数。

    例如:

    ETCDCTL_API=3 etcdctl \
       --cacert=/etc/kubernetes/pki/etcd/ca.crt   \
       --cert=/etc/kubernetes/pki/etcd/server.crt \
       --key=/etc/kubernetes/pki/etcd/server.key  \
       get /registry/secrets/default/secret1 | hexdump -C
    

    输出类似于(有删减):

    00000000  2f 72 65 67 69 73 74 72  79 2f 73 65 63 72 65 74  |/registry/secret|
    00000010  73 2f 64 65 66 61 75 6c  74 2f 73 65 63 72 65 74  |s/default/secret|
    00000020  31 0a 6b 38 73 3a 65 6e  63 3a 61 65 73 63 62 63  |1.k8s:enc:aescbc|
    00000030  3a 76 31 3a 6b 65 79 31  3a c7 6c e7 d3 09 bc 06  |:v1:key1:.l.....|
    00000040  25 51 91 e4 e0 6c e5 b1  4d 7a 8b 3d b9 c2 7c 6e  |%Q...l..Mz.=..|n|
    00000050  b4 79 df 05 28 ae 0d 8e  5f 35 13 2c c0 18 99 3e  |.y..(..._5.,...>|
    [...]
    00000110  23 3a 0d fc 28 ca 48 2d  6b 2d 46 cc 72 0b 70 4c  |#:..(.H-k-F.r.pL|
    00000120  a5 fc 35 43 12 4e 60 ef  bf 6f fe cf df 0b ad 1f  |..5C.N`..o......|
    00000130  82 c4 88 53 02 da 3e 66  ff 0a                    |...S..>f..|
    0000013a
    
  1. 验证存储的密钥前缀是否为 k8s:enc:aescbc:v1:,这表明 aescbc provider 已加密结果数据。 确认 etcd 中显示的密钥名称和上述 EncryptionConfiguration 中指定的密钥名称一致。 在此例中,你可以看到在 etcdEncryptionConfiguration 中使用了名为 key1 的加密密钥。

  2. 通过 API 检索,验证 Secret 是否被正确解密:

    kubectl get secret secret1 -n default -o yaml
    

    其输出应该包含 mykey: bXlkYXRh,其中 mydata 的内容使用 base64 进行加密, 请参阅解密 Secret 了解如何完全解码 Secret 内容。

确保所有相关数据都被加密

仅仅确保新对象被加密通常是不够的:你还希望对已经存储的对象进行加密。

例如,你已经配置了集群,使得 Secret 在写入时进行加密。 为每个 Secret 执行替换操作将加密那些对象保持不变的静态内容。

你可以在集群中的所有 Secret 上进行此项变更:

# 以能够读写所有 Secret 的管理员身份运行此命令
kubectl get secrets --all-namespaces -o json | kubectl replace -f -

上面的命令读取所有 Secret,然后使用相同的数据更新这些 Secret,以便应用服务端加密。

防止纯文本检索

如果你想确保对特定 API 类型的唯一访问是使用加密完成的,你可以移除 API 服务器以明文形式读取该 API 的支持数据的能力。

一旦集群中的所有 Secret 都被加密,你就可以删除加密配置中的 identity 部分。例如:

---
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
    providers:
      - aescbc:
          keys:
            - name: key1
              secret: <BASE 64 ENCODED SECRET>
      - identity: {} # 删除此行

…然后依次重新启动每个 API 服务器。此更改可防止 API 服务器访问纯文本 Secret,即使是意外访问也是如此。

轮换解密密钥

在不发生停机的情况下更改 Kubernetes 的加密密钥需要多步操作,特别是在有多个 kube-apiserver 进程正在运行的高可用环境中。

  1. 生成一个新密钥并将其添加为所有控制平面节点上当前提供程序的第二个密钥条目
  2. 重新启动所有 kube-apiserver 进程以确保每台服务器都可以使用新密钥加密任何数据
  3. 对新的加密密钥进行安全备份。如果你丢失了此密钥的所有副本,则需要删除用已丢失的密钥加密的所有资源, 并且在静态加密被破坏期间,工作负载可能无法按预期运行。
  4. 将新密钥设置为 keys 数组中的第一个条目,以便将其用于新编写的静态加密
  5. 重新启动所有 kube-apiserver 进程,以确保每个控制平面主机现在使用新密钥进行加密
  6. 作为特权用户,运行 kubectl get secrets --all-namespaces -o json | kubectl replace -f - 以用新密钥加密所有现有的 Secret
  7. 将所有现有 Secret 更新为使用新密钥并对新密钥进行安全备份后,从配置中删除旧的解密密钥。

解密所有数据

此示例演示如何停止静态加密 Secret API。如果你所加密的是其他 API 类型,请调整对应步骤来适配。

要禁用静态加密,请将 identity 提供程序作为加密配置文件中的第一个条目:

---
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
      # 在此列出你之前静态加密的任何其他资源
    providers:
      - identity: {} # 添加此行
      - aescbc:
          keys:
            - name: key1
              secret: <BASE 64 ENCODED SECRET> # 将其保留在适当的位置并确保它位于 "identity" 之后

然后运行以下命令以强制解密所有 Secret:

kubectl get secrets --all-namespaces -o json | kubectl replace -f -

将所有现有加密资源替换为不使用加密的支持数据后,你可以从 kube-apiserver 中删除加密设置。

配置自动重新加载

你可以配置加密提供程序配置的自动重新加载。 该设置决定了 API 服务器 是仅在启动时加载一次为 --encryption-provider-config 指定的文件, 还是在每次你更改该文件时都自动加载。 启用此选项可允许你在不重启 API 服务器的情况下更改静态加密所需的密钥。

要允许自动重新加载, 可使用 --encryption-provider-config-automatic-reload=true 运行 API 服务器。

接下来

4.2.23 - 解密已静态加密的机密数据

Kubernetes 中允许允许你写入持久性 API 资源数据的所有 API 都支持静态加密。 例如,你可以为 Secret 启用静态加密。 此静态加密是对 etcd 集群或运行 kube-apiserver 的主机上的文件系统的所有系统级加密的补充。

本文介绍如何停止静态加密 API 数据,以便 API 数据以未加密的形式存储。 你可能希望这样做以提高性能;但通常情况下,如果加密某些数据是个好主意,那么继续加密这些数据也是一个好主意。

准备开始

  • 你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

  • 此任务假设你将 Kubernetes API 服务器组件以静态 Pod 方式运行在每个控制平面节点上。

  • 集群的控制平面必须使用 etcd v3.x(主版本 3,任何次要版本)。

  • 要加密自定义资源,你的集群必须运行 Kubernetes v1.26 或更高版本。

  • 你应该有一些已加密的 API 数据。

要获知版本信息,请输入 kubectl version.

确定静态加密是否已被启用

默认情况下,API 服务器使用一个名为 identity 的提供程序来存储资源的明文表示。 默认的 identity 提供程序不提供任何机密性保护。

kube-apiserver 进程接受参数 --encryption-provider-config,该参数指定了配置文件的路径。 如果你指定了一个路径,那么该文件的内容将控制 Kubernetes API 数据在 etcd 中的加密方式。 如果未指定,则表示你未启用静态加密。

该配置文件的格式是 YAML,表示名为 EncryptionConfiguration 的配置 API 类别。 你可以在静态加密配置中查看示例配置。

如果设置了 --encryption-provider-config,检查哪些资源(如 secrets)已配置为进行加密, 并查看所适用的是哪个提供程序。确保该资源类型首选的提供程序 不是 identity; 只有在想要禁用静态加密时,才可将 identity无加密)设置为默认值。 验证资源首选的提供程序是否不是 identity,这意味着写入该类型资源的任何新信息都将按照配置被加密。 如果在任何资源的首选提供程序中看到 identity,这意味着这些资源将以非加密的方式写入 etcd 中。

解密所有数据

本例展示如何停止对 Secret API 进行静态加密。如果你正在加密其他 API 类别,可以相应调整以下步骤。

找到加密配置文件

首先,找到 API 服务器的配置文件。在每个控制平面节点上,kube-apiserver 的静态 Pod 清单指定了一个命令行参数 --encryption-provider-config。你很可能会发现此文件通过 hostPath 卷挂载到静态 Pod 中。 一旦你找到到此卷,就可以在节点文件系统中找到此文件并对其进行检查。

配置 API 服务器以解密对象

要禁用静态加密,将 identity 提供程序设置为加密配置文件中的第一个条目。

例如,如果你现有的 EncryptionConfiguration 文件内容如下:

---
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
    providers:
      - aescbc:
          keys:
            # 你加密时不要使用这个(无效)的示例密钥
            - name: example
              secret: 2KfZgdiq2K0g2YrYpyDYs9mF2LPZhQ==

然后将其更改为:

---
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
    providers:
      - identity: {} # 增加这一行
      - aescbc:
          keys:
            - name: example
              secret: 2KfZgdiq2K0g2YrYpyDYs9mF2LPZhQ==

并重启此节点上的 kube-apiserver Pod。

重新配置其他控制平面主机

如果你的集群中有多个 API 服务器,应轮流对每个 API 服务器部署这些更改。

确保在每个控制平面主机上使用相同的加密配置。

强制解密

然后运行以下命令强制解密所有 Secret:

# 如果你正在解密不同类别的对象,请相应更改 "secrets"
kubectl get secrets --all-namespaces -o json | kubectl replace -f -

一旦你用未加密的后台数据替换了所有现有的已加密资源,即可从 kube-apiserver 中删除这些加密设置。

要移除的命令行选项为:

  • --encryption-provider-config
  • --encryption-provider-config-automatic-reload

再次重启 kube-apiserver Pod 以应用新的配置。

重新配置其他控制平面主机

如果你的集群中有多个 API 服务器,应再次轮流对每个 API 服务器部署这些更改。

确保在每个控制平面主机上使用相同的加密配置。

接下来

4.2.24 - 关键插件 Pod 的调度保证

Kubernetes 核心组件(如 API 服务器、调度器、控制器管理器)在控制平面节点上运行。 但是插件必须在常规集群节点上运行。 其中一些插件对于功能完备的集群至关重要,例如 Heapster、DNS 和 UI。 如果关键插件被逐出(手动或作为升级等其他操作的副作用)或者变成挂起状态,集群可能会停止正常工作。 关键插件进入挂起状态的例子有:集群利用率过高;被逐出的关键插件 Pod 释放了空间,但该空间被之前悬决的 Pod 占用;由于其它原因导致节点上可用资源的总量发生变化。

注意,把某个 Pod 标记为关键 Pod 并不意味着完全避免该 Pod 被逐出;它只能防止该 Pod 变成永久不可用。 被标记为关键性的静态 Pod 不会被逐出。但是,被标记为关键性的非静态 Pod 总是会被重新调度。

标记关键 Pod

要将 Pod 标记为关键性(critical),设置 Pod 的 priorityClassName 为 system-cluster-critical 或者 system-node-criticalsystem-node-critical 是最高级别的可用性优先级,甚至比 system-cluster-critical 更高。

4.2.25 - IP Masquerade Agent 用户指南

此页面展示如何配置和启用 ip-masq-agent

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

IP Masquerade Agent 用户指南

ip-masq-agent 配置 iptables 规则以隐藏位于集群节点 IP 地址后面的 Pod 的 IP 地址。 这通常在将流量发送到集群的 Pod CIDR 范围之外的目的地时使用。

关键术语

  • NAT(网络地址转换): 是一种通过修改 IP 地址头中的源和/或目标地址信息将一个 IP 地址重新映射 到另一个 IP 地址的方法。通常由执行 IP 路由的设备执行。
  • 伪装: NAT 的一种形式,通常用于执行多对一地址转换,其中多个源 IP 地址被隐藏在 单个地址后面,该地址通常是执行 IP 路由的设备。在 Kubernetes 中, 这是节点的 IP 地址。
  • CIDR(无类别域间路由): 基于可变长度子网掩码,允许指定任意长度的前缀。 CIDR 引入了一种新的 IP 地址表示方法,现在通常称为 CIDR 表示法, 其中地址或路由前缀后添加一个后缀,用来表示前缀的位数,例如 192.168.2.0/24。
  • 本地链路: 本地链路是仅对网段或主机所连接的广播域内的通信有效的网络地址。 IPv4 的本地链路地址在 CIDR 表示法的地址块 169.254.0.0/16 中定义。

ip-masq-agent 配置 iptables 规则,以便在将流量发送到集群节点的 IP 和集群 IP 范围之外的目标时处理伪装节点或 Pod 的 IP 地址。这本质上隐藏了集群节点 IP 地址后面的 Pod IP 地址。在某些环境中,去往"外部"地址的流量必须从已知的机器地址发出。 例如,在 Google Cloud 中,任何到互联网的流量都必须来自 VM 的 IP。 使用容器时,如 Google Kubernetes Engine,从 Pod IP 发出的流量将被拒绝出站。 为了避免这种情况,我们必须将 Pod IP 隐藏在 VM 自己的 IP 地址后面 - 通常称为"伪装"。 默认情况下,代理配置为将 RFC 1918 指定的三个私有 IP 范围视为非伪装 CIDR。 这些范围是 10.0.0.0/8172.16.0.0/12192.168.0.0/16。 默认情况下,代理还将链路本地地址(169.254.0.0/16)视为非伪装 CIDR。 代理程序配置为每隔 60 秒从 /etc/config/ip-masq-agent 重新加载其配置, 这也是可修改的。

masq/non-masq example

代理配置文件必须使用 YAML 或 JSON 语法编写,并且可能包含三个可选值:

  • nonMasqueradeCIDRsCIDR 表示法中的字符串列表,用于指定不需伪装的地址范围。
  • masqLinkLocal:布尔值(true/false),表示是否为本地链路前缀 169.254.0.0/16 的流量提供伪装。默认为 false。
  • resyncInterval:代理从磁盘重新加载配置的重试时间间隔。 例如 '30s',其中 's' 是秒,'ms' 是毫秒。

10.0.0.0/8、172.16.0.0/12 和 192.168.0.0/16 范围内的流量不会被伪装。 任何其他流量(假设是互联网)将被伪装。 Pod 访问本地目的地的例子,可以是其节点的 IP 地址、另一节点的地址或集群的 IP 地址范围内的一个 IP 地址。 默认情况下,任何其他流量都将伪装。以下条目展示了 ip-masq-agent 的默认使用的规则:

iptables -t nat -L IP-MASQ-AGENT
target     prot opt source               destination
RETURN     all  --  anywhere             169.254.0.0/16       /* ip-masq-agent: cluster-local traffic should not be subject to MASQUERADE */ ADDRTYPE match dst-type !LOCAL
RETURN     all  --  anywhere             10.0.0.0/8           /* ip-masq-agent: cluster-local traffic should not be subject to MASQUERADE */ ADDRTYPE match dst-type !LOCAL
RETURN     all  --  anywhere             172.16.0.0/12        /* ip-masq-agent: cluster-local traffic should not be subject to MASQUERADE */ ADDRTYPE match dst-type !LOCAL
RETURN     all  --  anywhere             192.168.0.0/16       /* ip-masq-agent: cluster-local traffic should not be subject to MASQUERADE */ ADDRTYPE match dst-type !LOCAL
MASQUERADE  all  --  anywhere             anywhere             /* ip-masq-agent: outbound traffic should be subject to MASQUERADE (this match must come after cluster-local CIDR matches) */ ADDRTYPE match dst-type !LOCAL

默认情况下,在 GCE/Google Kubernetes Engine 中,如果启用了网络策略, 或者你使用的集群 CIDR 不在 10.0.0.0/8 范围内, 则 ip-masq-agent 将在你的集群中运行。 如果你在其他环境中运行,可以将 ip-masq-agent DaemonSet 添加到你的集群中。

创建 ip-masq-agent

通过运行以下 kubectl 指令创建 ip-masq-agent:

kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/ip-masq-agent/master/ip-masq-agent.yaml

你必须同时将适当的节点标签应用于集群中希望代理运行的任何节点。

kubectl label nodes my-node node.kubernetes.io/masq-agent-ds-ready=true

更多信息可以通过 ip-masq-agent 文档这里找到。

在大多数情况下,默认的规则集应该足够;但是,如果你的集群不是这种情况,则可以创建并应用 ConfigMap 来自定义受影响的 IP 范围。 例如,要允许 ip-masq-agent 仅作用于 10.0.0.0/8,你可以在一个名为 "config" 的文件中创建以下 ConfigMap

运行以下命令将 ConfigMap 添加到你的集群:

kubectl create configmap ip-masq-agent --from-file=config --namespace=kube-system

这将更新位于 /etc/config/ip-masq-agent 的一个文件,该文件以 resyncInterval 为周期定期检查并应用于集群节点。 重新同步间隔到期后,你应该看到你的更改在 iptables 规则中体现:

iptables -t nat -L IP-MASQ-AGENT
Chain IP-MASQ-AGENT (1 references)
target     prot opt source               destination
RETURN     all  --  anywhere             169.254.0.0/16       /* ip-masq-agent: cluster-local traffic should not be subject to MASQUERADE */ ADDRTYPE match dst-type !LOCAL
RETURN     all  --  anywhere             10.0.0.0/8           /* ip-masq-agent: cluster-local
MASQUERADE  all  --  anywhere             anywhere             /* ip-masq-agent: outbound traffic should be subject to MASQUERADE (this match must come after cluster-local CIDR matches) */ ADDRTYPE match dst-type !LOCAL

默认情况下,本地链路范围(169.254.0.0/16)也由 ip-masq agent 处理, 该代理设置适当的 iptables 规则。要使 ip-masq-agent 忽略本地链路, 可以在 ConfigMap 中将 masqLinkLocal 设置为 true。

nonMasqueradeCIDRs:
  - 10.0.0.0/8
resyncInterval: 60s
masqLinkLocal: true

4.2.26 - 限制存储使用量

此示例演示如何限制一个名字空间中的存储使用量。

演示中用到了以下资源:ResourceQuotaLimitRangePersistentVolumeClaim

准备开始

  • 你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

    要获知版本信息,请输入 kubectl version.

场景:限制存储使用量

集群管理员代表用户群操作集群,该管理员希望控制单个名字空间可以消耗多少存储空间以控制成本。

该管理员想要限制:

  1. 名字空间中持久卷申领(persistent volume claims)的数量
  2. 每个申领(claim)可以请求的存储量
  3. 名字空间可以具有的累计存储量

使用 LimitRange 限制存储请求

LimitRange 添加到名字空间会为存储请求大小强制设置最小值和最大值。 存储是通过 PersistentVolumeClaim 来发起请求的。 执行限制范围控制的准入控制器会拒绝任何高于或低于管理员所设阈值的 PVC。

在此示例中,请求 10Gi 存储的 PVC 将被拒绝,因为它超过了最大 2Gi。

apiVersion: v1
kind: LimitRange
metadata:
  name: storagelimits
spec:
  limits:
  - type: PersistentVolumeClaim
    max:
      storage: 2Gi
    min:
      storage: 1Gi

当底层存储提供程序需要某些最小值时,将会用到所设置最小存储请求值。 例如,AWS EBS volumes 的最低要求为 1Gi。

使用 StorageQuota 限制 PVC 数目和累计存储容量

管理员可以限制某个名字空间中的 PVC 个数以及这些 PVC 的累计容量。 如果 PVC 的数目超过任一上限值,新的 PVC 将被拒绝。

在此示例中,名字空间中的第 6 个 PVC 将被拒绝,因为它超过了最大计数 5。 或者,当与上面的 2Gi 最大容量限制结合在一起时, 意味着 5Gi 的最大配额不能支持 3 个都是 2Gi 的 PVC。 后者实际上是向名字空间请求 6Gi 容量,而该名字空间已经设置上限为 5Gi。

apiVersion: v1
kind: ResourceQuota
metadata:
  name: storagequota
spec:
  hard:
    persistentvolumeclaims: "5"
    requests.storage: "5Gi"

小结

限制范围对象可以用来设置可请求的存储量上限,而资源配额对象则可以通过申领计数和 累计存储容量有效地限制名字空间耗用的存储量。 这两种机制使得集群管理员能够规划其集群存储预算而不会发生任一项目超量分配的风险。

4.2.27 - 迁移多副本的控制面以使用云控制器管理器

一个 Kubernetes 控制平面组件, 嵌入了特定于云平台的控制逻辑。 云控制器管理器(Cloud Controller Manager) 允许将你的集群连接到云提供商的 API 之上, 并将与该云平台交互的组件同与你的集群交互的组件分离开来。

通过分离 Kubernetes 和底层云基础设置之间的互操作性逻辑, cloud-controller-manager 组件使云提供商能够以不同于 Kubernetes 主项目的步调发布新特征。

背景

作为云驱动提取工作 的一部分,所有特定于云的控制器都必须移出 kube-controller-manager。 所有在 kube-controller-manager 中运行云控制器的现有集群必须迁移到特定于云厂商的 cloud-controller-manager 中运行这些控制器。

领导者迁移(Leader Migration)提供了一种机制,使得 HA 集群可以通过这两个组件之间共享资源锁, 在升级多副本的控制平面时,安全地将“特定于云”的控制器从 kube-controller-manager 迁移到 cloud-controller-manager。 对于单节点控制平面,或者在升级过程中可以容忍控制器管理器不可用的情况,则不需要领导者迁移, 亦可以忽略本指南。

领导者迁移可以通过在 kube-controller-managercloud-controller-manager 上设置 --enable-leader-migration 来启用。 领导者迁移仅在升级期间适用,并且在升级完成后可以安全地禁用或保持启用状态。

本指南将引导你手动将控制平面从内置的云驱动的 kube-controller-manager 升级为 同时运行 kube-controller-managercloud-controller-manager。 如果使用某种工具来部署和管理集群,请参阅对应工具和云驱动的文档以获取迁移的具体说明。

准备开始

假定控制平面正在运行 Kubernetes 版本 N,要升级到版本 N+1。 尽管可以在同一版本内进行迁移,但理想情况下,迁移应作为升级的一部分执行, 以便可以配置的变更可以与发布版本变化对应起来。 N 和 N+1 的确切版本值取决于各个云厂商。例如,如果云厂商构建了一个可与 Kubernetes 1.24 配合使用的 cloud-controller-manager,则 N 可以为 1.23,N+1 可以为 1.24。

控制平面节点应运行 kube-controller-manager 并启用领导者选举,这也是默认设置。 在版本 N 中,树内云驱动必须设置 --cloud-provider 标志,而且 cloud-controller-manager 应该尚未部署。

树外云驱动必须已经构建了一个实现了领导者迁移的 cloud-controller-manager。 如果云驱动导入了 v0.21.0 或更高版本的 k8s.io/cloud-providerk8s.io/controller-manager, 则可以进行领导者迁移。 但是,对 v0.22.0 以下的版本,领导者迁移是一项 Alpha 阶段功能,需要在 cloud-controller-manager 中启用特性门控 ControllerManagerLeaderMigration

本指南假定每个控制平面节点的 kubelet 以静态 Pod 的形式启动 kube-controller-managercloud-controller-manager,静态 Pod 的定义在清单文件中。 如果组件以其他设置运行,请相应地调整这里的步骤。

关于鉴权,本指南假定集群使用 RBAC。如果其他鉴权模式授予 kube-controller-managercloud-controller-manager 组件权限,请以与该模式匹配的方式授予所需的访问权限。

授予访问迁移租约的权限

控制器管理器的默认权限仅允许访问其主租约(Lease)对象。为了使迁移正常进行, 需要授权它访问其他 Lease 对象。

你可以通过修改 system::leader-locking-kube-controller-manager 角色来授予 kube-controller-manager 对 Lease API 的完全访问权限。 本任务指南假定迁移 Lease 的名称为 cloud-provider-extraction-migration

kubectl patch -n kube-system role 'system::leader-locking-kube-controller-manager' -p '{"rules": [ {"apiGroups":[ "coordination.k8s.io"], "resources": ["leases"], "resourceNames": ["cloud-provider-extraction-migration"], "verbs": ["create", "list", "get", "update"] } ]}' --type=merge

system::leader-locking-cloud-controller-manager 角色执行相同的操作。

kubectl patch -n kube-system role 'system::leader-locking-cloud-controller-manager' -p '{"rules": [ {"apiGroups":[ "coordination.k8s.io"], "resources": ["leases"], "resourceNames": ["cloud-provider-extraction-migration"], "verbs": ["create", "list", "get", "update"] } ]}' --type=merge

初始领导者迁移配置

领导者迁移可以选择使用一个表示如何将控制器分配给不同管理器的配置文件。 目前,对于树内云驱动,kube-controller-manager 运行 routeservicecloud-node-lifecycle。以下示例配置显示的是这种分配。

领导者迁移可以不指定配置的情况下启用。请参阅默认配置 以获取更多详细信息。

kind: LeaderMigrationConfiguration
apiVersion: controllermanager.config.k8s.io/v1
leaderName: cloud-provider-extraction-migration
controllerLeaders:
  - name: route
    component: kube-controller-manager
  - name: service
    component: kube-controller-manager
  - name: cloud-node-lifecycle
    component: kube-controller-manager

或者,由于控制器可以在任一控制器管理器下运行,因此将双方的 component 设置为 * 可以使迁移双方的配置文件保持一致。

# 通配符版本
kind: LeaderMigrationConfiguration
apiVersion: controllermanager.config.k8s.io/v1
leaderName: cloud-provider-extraction-migration
controllerLeaders:
  - name: route
    component: *
  - name: service
    component: *
  - name: cloud-node-lifecycle
    component: *

在每个控制平面节点上,请将如上内容保存到 /etc/leadermigration.conf 中, 并更新 kube-controller-manager 清单,以便将文件挂载到容器内的同一位置。 另外,请更新同一清单,添加以下参数:

  • --enable-leader-migration 在控制器管理器上启用领导者迁移
  • --leader-migration-config=/etc/leadermigration.conf 设置配置文件

在每个节点上重新启动 kube-controller-manager。这时,kube-controller-manager 已启用领导者迁移,为迁移准备就绪。

部署云控制器管理器

在版本 N+1 中,如何将控制器分配给不同管理器的预期分配状态可以由新的配置文件表示, 如下所示。请注意,各个 controllerLeaderscomponent 字段从 kube-controller-manager 更改为 cloud-controller-manager。 或者,使用上面提到的通配符版本,它具有相同的效果。

kind: LeaderMigrationConfiguration
apiVersion: controllermanager.config.k8s.io/v1
leaderName: cloud-provider-extraction-migration
controllerLeaders:
  - name: route
    component: cloud-controller-manager
  - name: service
    component: cloud-controller-manager
  - name: cloud-node-lifecycle
    component: cloud-controller-manager

当创建版本 N+1 的控制平面节点时,应将如上内容写入到 /etc/leadermigration.conf。 你需要更新 cloud-controller-manager 的清单,以与版本 N 的 kube-controller-manager 相同的方式挂载配置文件。 类似地,添加 --enable-leader-migration--leader-migration-config=/etc/leadermigration.confcloud-controller-manager 的参数中。

使用已更新的 cloud-controller-manager 清单创建一个新的 N+1 版本的控制平面节点, 同时设置 kube-controller-manager--cloud-provider 标志为 external。 版本为 N+1 的 kube-controller-manager 不能启用领导者迁移, 因为在使用外部云驱动的情况下,它不再运行已迁移的控制器,因此不参与迁移。

请参阅云控制器管理器管理 了解有关如何部署 cloud-controller-manager 的更多细节。

升级控制平面

现在,控制平面同时包含 N 和 N+1 版本的节点。 版本 N 的节点仅运行 kube-controller-manager,而版本 N+1 的节点同时运行 kube-controller-managercloud-controller-manager。 根据配置所指定,已迁移的控制器在版本 N 的 kube-controller-manager 或版本 N+1 的 cloud-controller-manager 下运行,具体取决于哪个控制器管理器拥有迁移租约对象。 任何时候都不会有同一个控制器在两个控制器管理器下运行。

以滚动的方式创建一个新的版本为 N+1 的控制平面节点,并将版本 N 中的一个关闭, 直到控制平面仅包含版本为 N+1 的节点。 如果需要从 N+1 版本回滚到 N 版本,则将 kube-controller-manager 启用了领导者迁移的、 且版本为 N 的节点添加回控制平面,每次替换 N+1 版本中的一个,直到只有版本 N 的节点为止。

(可选)禁用领导者迁移

现在,控制平面已经完成升级,同时运行版本 N+1 的 kube-controller-managercloud-controller-manager。领导者迁移的任务已经结束,可以被安全地禁用以节省一个 Lease 资源。在将来可以安全地重新启用领导者迁移,以完成回滚。

在滚动管理器中,更新 cloud-controller-manager 的清单以同时取消设置 --enable-leader-migration--leader-migration-config= 标志,并删除 /etc/leadermigration.conf 的挂载,最后删除 /etc/leadermigration.conf。 要重新启用领导者迁移,请重新创建配置文件,并将其挂载和启用领导者迁移的标志添加回到 cloud-controller-manager

默认配置

从 Kubernetes 1.22 开始,领导者迁移提供了一个默认配置,它适用于控制器与管理器间默认的分配关系。 可以通过设置 --enable-leader-migration,但不设置 --leader-migration-config= 来启用默认配置。

对于 kube-controller-managercloud-controller-manager,如果没有用参数来启用树内云驱动或者改变控制器属主, 则可以使用默认配置来避免手动创建配置文件。

特殊情况:迁移节点 IPAM 控制器

如果你的云供应商提供了节点 IPAM 控制器的实现,你应该切换到 cloud-controller-manager 中的实现。 通过在其标志中添加 --controllers=*,-nodeipam 来禁用 N+1 版本的 kube-controller-manager 中的节点 IPAM 控制器。 然后将 nodeipam 添加到迁移的控制器列表中。

# 通配符版本,带有 nodeipam
kind: LeaderMigrationConfiguration
apiVersion: controllermanager.config.k8s.io/v1
leaderName: cloud-provider-extraction-migration
controllerLeaders:
  - name: route
    component: *
  - name: service
    component: *
  - name: cloud-node-lifecycle
    component: *
  - name: nodeipam
-   component: *

接下来

4.2.28 - 名字空间演练

Kubernetes 名字空间 有助于不同的项目、团队或客户去共享 Kubernetes 集群。

名字空间通过以下方式实现这点:

  1. 名字设置作用域.
  2. 为集群中的部分资源关联鉴权和策略的机制。

使用多个名字空间是可选的。

此示例演示了如何使用 Kubernetes 名字空间细分集群。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

环境准备

此示例作如下假设:

  1. 你已拥有一个配置好的 Kubernetes 集群
  2. 你已对 Kubernetes 的 Pod服务Deployment 有基本理解。

理解默认名字空间

默认情况下,Kubernetes 集群会在配置集群时实例化一个默认名字空间,用以存放集群所使用的默认 Pod、Service 和 Deployment 集合。

假设你有一个新的集群,你可以通过执行以下操作来检查可用的名字空间:

kubectl get namespaces
NAME      STATUS    AGE
default   Active    13m

创建新的名字空间

在本练习中,我们将创建两个额外的 Kubernetes 名字空间来保存我们的内容。

我们假设一个场景,某组织正在使用共享的 Kubernetes 集群来支持开发和生产:

开发团队希望在集群中维护一个空间,以便他们可以查看用于构建和运行其应用程序的 Pod、Service 和 Deployment 列表。在这个空间里,Kubernetes 资源被自由地加入或移除, 对谁能够或不能修改资源的限制被放宽,以实现敏捷开发。

运维团队希望在集群中维护一个空间,以便他们可以强制实施一些严格的规程, 对谁可以或谁不可以操作运行生产站点的 Pod、Service 和 Deployment 集合进行控制。

该组织可以遵循的一种模式是将 Kubernetes 集群划分为两个名字空间:developmentproduction

让我们创建两个新的名字空间来保存我们的工作。

文件 namespace-dev.yaml 描述了 development 名字空间:

apiVersion: v1
kind: Namespace
metadata:
  name: development
  labels:
    name: development

使用 kubectl 创建 development 名字空间。

kubectl create -f https://k8s.io/examples/admin/namespace-dev.yaml

将下列的内容保存到文件 namespace-prod.yaml 中, 这些内容是对 production 名字空间的描述:

apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    name: production

让我们使用 kubectl 创建 production 名字空间。

kubectl create -f https://k8s.io/examples/admin/namespace-prod.yaml

为了确保一切正常,我们列出集群中的所有名字空间。

kubectl get namespaces --show-labels
NAME          STATUS    AGE       LABELS
default       Active    32m       <none>
development   Active    29s       name=development
production    Active    23s       name=production

在每个名字空间中创建 Pod

Kubernetes 名字空间为集群中的 Pod、Service 和 Deployment 提供了作用域。

与一个名字空间交互的用户不会看到另一个名字空间中的内容。

为了演示这一点,让我们在 development 名字空间中启动一个简单的 Deployment 和 Pod。

我们首先检查一下当前的上下文:

kubectl config view
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: REDACTED
    server: https://130.211.122.180
  name: lithe-cocoa-92103_kubernetes
contexts:
- context:
    cluster: lithe-cocoa-92103_kubernetes
    user: lithe-cocoa-92103_kubernetes
  name: lithe-cocoa-92103_kubernetes
current-context: lithe-cocoa-92103_kubernetes
kind: Config
preferences: {}
users:
- name: lithe-cocoa-92103_kubernetes
  user:
    client-certificate-data: REDACTED
    client-key-data: REDACTED
    token: 65rZW78y8HbwXXtSXuUw9DbP4FLjHi4b
- name: lithe-cocoa-92103_kubernetes-basic-auth
  user:
    password: h5M0FtUUIflBSdI7
    username: admin
kubectl config current-context
lithe-cocoa-92103_kubernetes

下一步是为 kubectl 客户端定义一个上下文,以便在每个名字空间中工作。 "cluster" 和 "user" 字段的值将从当前上下文中复制。

kubectl config set-context dev --namespace=development \
  --cluster=lithe-cocoa-92103_kubernetes \
  --user=lithe-cocoa-92103_kubernetes

kubectl config set-context prod --namespace=production \
  --cluster=lithe-cocoa-92103_kubernetes \
  --user=lithe-cocoa-92103_kubernetes

默认情况下,上述命令会添加两个上下文到 .kube/config 文件中。 你现在可以查看上下文并根据你希望使用的名字空间并在这两个新的请求上下文之间切换。

查看新的上下文:

kubectl config view
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: REDACTED
    server: https://130.211.122.180
  name: lithe-cocoa-92103_kubernetes
contexts:
- context:
    cluster: lithe-cocoa-92103_kubernetes
    user: lithe-cocoa-92103_kubernetes
  name: lithe-cocoa-92103_kubernetes
- context:
    cluster: lithe-cocoa-92103_kubernetes
    namespace: development
    user: lithe-cocoa-92103_kubernetes
  name: dev
- context:
    cluster: lithe-cocoa-92103_kubernetes
    namespace: production
    user: lithe-cocoa-92103_kubernetes
  name: prod
current-context: lithe-cocoa-92103_kubernetes
kind: Config
preferences: {}
users:
- name: lithe-cocoa-92103_kubernetes
  user:
    client-certificate-data: REDACTED
    client-key-data: REDACTED
    token: 65rZW78y8HbwXXtSXuUw9DbP4FLjHi4b
- name: lithe-cocoa-92103_kubernetes-basic-auth
  user:
    password: h5M0FtUUIflBSdI7
    username: admin

让我们切换到 development 名字空间进行操作。

kubectl config use-context dev

你可以使用下列命令验证当前上下文:

kubectl config current-context
dev

此时,我们从命令行向 Kubernetes 集群发出的所有请求都限定在 development 名字空间中。

让我们创建一些内容。

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: snowflake
  name: snowflake
spec:
  replicas: 2
  selector:
    matchLabels:
      app: snowflake
  template:
    metadata:
      labels:
        app: snowflake
    spec:
      containers:
      - image: registry.k8s.io/serve_hostname
        imagePullPolicy: Always
        name: snowflake

应用清单文件来创建 Deployment。

kubectl apply -f https://k8s.io/examples/admin/snowflake-deployment.yaml

我们创建了一个副本大小为 2 的 Deployment,该 Deployment 运行名为 snowflake 的 Pod, 其中包含一个仅提供主机名服务的基本容器。

kubectl get deployment
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
snowflake    2/2     2            2           2m
kubectl get pods -l app=snowflake
NAME                         READY     STATUS    RESTARTS   AGE
snowflake-3968820950-9dgr8   1/1       Running   0          2m
snowflake-3968820950-vgc4n   1/1       Running   0          2m

这很棒,开发人员可以做他们想要的事情,而不必担心影响 production 名字空间中的内容。

让我们切换到 production 名字空间,展示一个名字空间中的资源如何对另一个名字空间不可见。

kubectl config use-context prod

production 名字空间应该是空的,下列命令应该返回的内容为空。

kubectl get deployment
kubectl get pods

生产环境需要以放牛的方式运维,让我们创建一些名为 cattle 的 Pod。

kubectl create deployment cattle --image=registry.k8s.io/serve_hostname --replicas=5
kubectl get deployment
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
cattle       5/5     5            5           10s
kubectl get pods -l run=cattle
NAME                      READY     STATUS    RESTARTS   AGE
cattle-2263376956-41xy6   1/1       Running   0          34s
cattle-2263376956-kw466   1/1       Running   0          34s
cattle-2263376956-n4v97   1/1       Running   0          34s
cattle-2263376956-p5p3i   1/1       Running   0          34s
cattle-2263376956-sxpth   1/1       Running   0          34s

此时,应该很清楚的展示了用户在一个名字空间中创建的资源对另一个名字空间是不可见的。

随着 Kubernetes 中的策略支持的发展,我们将扩展此场景,以展示如何为每个名字空间提供不同的授权规则。

4.2.29 - 操作 Kubernetes 中的 etcd 集群

etcd 是 一致且高可用的键值存储,用作 Kubernetes 所有集群数据的后台数据库。

如果你的 Kubernetes 集群使用 etcd 作为其后台数据库, 请确保你针对这些数据有一份 备份计划。

你可以在官方文档中找到有关 etcd 的深入知识。

准备开始

你需要有一个 Kubernetes 集群,并且必须配置 kubectl 命令行工具以与你的集群通信。 建议参照本指南在至少有两个不充当控制平面的节点上运行此任务。如果你还没有集群, 你可以使用 minikube 创建一个。

先决条件

  • 运行的 etcd 集群个数成员为奇数。

  • etcd 是一个基于领导者(Leader-Based)的分布式系统。确保主节点定期向所有从节点发送心跳,以保持集群稳定。

  • 确保不发生资源不足。

    集群的性能和稳定性对网络和磁盘 I/O 非常敏感。任何资源匮乏都会导致心跳超时, 从而导致集群的不稳定。不稳定的情况表明没有选出任何主节点。 在这种情况下,集群不能对其当前状态进行任何更改,这意味着不能调度新的 Pod。

  • 保持 etcd 集群的稳定对 Kubernetes 集群的稳定性至关重要。 因此,请在专用机器或隔离环境上运行 etcd 集群, 以满足所需资源需求

  • 在生产环境中运行的 etcd 最低推荐版本为 3.4.22+3.5.6+

资源需求

使用有限的资源运行 etcd 只适合测试目的。为了在生产中部署,需要先进的硬件配置。 在生产中部署 etcd 之前,请查看所需资源参考文档

启动 etcd 集群

本节介绍如何启动单节点和多节点 etcd 集群。

单节点 etcd 集群

只为测试目的使用单节点 etcd 集群。

  1. 运行以下命令:

    etcd --listen-client-urls=http://$PRIVATE_IP:2379 \
       --advertise-client-urls=http://$PRIVATE_IP:2379
    
  2. 使用参数 --etcd-servers=$PRIVATE_IP:2379 启动 Kubernetes API 服务器。

    确保将 PRIVATE_IP 设置为 etcd 客户端 IP。

多节点 etcd 集群

出于耐用性和高可用性考量,在生产环境中应以多节点集群的方式运行 etcd,并且定期备份。 建议在生产环境中使用五个成员的集群。 有关该内容的更多信息,请参阅常见问题文档

可以通过静态成员信息或动态发现的方式配置 etcd 集群。 有关集群的详细信息,请参阅 etcd 集群文档

例如,考虑运行以下客户端 URL 的五个成员的 etcd 集群:http://$IP1:2379http://$IP2:2379http://$IP3:2379http://$IP4:2379http://$IP5:2379。 要启动 Kubernetes API 服务器:

  1. 运行以下命令:

    etcd --listen-client-urls=http://$IP1:2379,http://$IP2:2379,http://$IP3:2379,http://$IP4:2379,http://$IP5:2379 --advertise-client-urls=http://$IP1:2379,http://$IP2:2379,http://$IP3:2379,http://$IP4:2379,http://$IP5:2379
    
  2. 使用参数 --etcd-servers=$IP1:2379,$IP2:2379,$IP3:2379,$IP4:2379,$IP5:2379 启动 Kubernetes API 服务器。

    确保将 IP<n> 变量设置为客户端 IP 地址。

使用负载均衡器的多节点 etcd 集群

要运行负载均衡的 etcd 集群:

  1. 建立一个 etcd 集群。
  2. 在 etcd 集群前面配置负载均衡器。例如,让负载均衡器的地址为 $LB
  3. 使用参数 --etcd-servers=$LB:2379 启动 Kubernetes API 服务器。

加固 etcd 集群

对 etcd 的访问相当于集群中的 root 权限,因此理想情况下只有 API 服务器才能访问它。 考虑到数据的敏感性,建议只向需要访问 etcd 集群的节点授予权限。

想要确保 etcd 的安全,可以设置防火墙规则或使用 etcd 提供的安全特性,这些安全特性依赖于 x509 公钥基础设施(PKI)。 首先,通过生成密钥和证书对来建立安全的通信通道。 例如,使用密钥对 peer.keypeer.cert 来保护 etcd 成员之间的通信, 而 client.keyclient.cert 用于保护 etcd 与其客户端之间的通信。 请参阅 etcd 项目提供的示例脚本, 以生成用于客户端身份验证的密钥对和 CA 文件。

安全通信

若要使用安全对等通信对 etcd 进行配置,请指定参数 --peer-key-file=peer.key--peer-cert-file=peer.cert,并使用 HTTPS 作为 URL 模式。

类似地,要使用安全客户端通信对 etcd 进行配置,请指定参数 --key-file=k8sclient.key--cert-file=k8sclient.cert,并使用 HTTPS 作为 URL 模式。 使用安全通信的客户端命令的示例:

ETCDCTL_API=3 etcdctl --endpoints 10.2.0.9:2379 \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  member list

限制 etcd 集群的访问

配置安全通信后,使用 TLS 身份验证来限制只有 Kubernetes API 服务器可以访问 etcd 集群。

例如,考虑由 CA etcd.ca 信任的密钥对 k8sclient.keyk8sclient.cert。 当 etcd 配置为 --client-cert-auth 和 TLS 时,它使用系统 CA 或由 --trusted-ca-file 参数传入的 CA 验证来自客户端的证书。指定参数 --client-cert-auth=true--trusted-ca-file=etcd.ca 将限制对具有证书 k8sclient.cert 的客户端的访问。

一旦正确配置了 etcd,只有具有有效证书的客户端才能访问它。要让 Kubernetes API 服务器访问, 可以使用参数 --etcd-certfile=k8sclient.cert--etcd-keyfile=k8sclient.key--etcd-cafile=ca.cert 配置。

替换失败的 etcd 成员

etcd 集群通过容忍少数成员故障实现高可用性。 但是,要改善集群的整体健康状况,请立即替换失败的成员。当多个成员失败时,逐个替换它们。 替换失败成员需要两个步骤:删除失败成员和添加新成员。

虽然 etcd 在内部保留唯一的成员 ID,但建议为每个成员使用唯一的名称,以避免人为错误。 例如,考虑一个三成员的 etcd 集群。假定 URL 分别为:member1=http://10.0.0.1member2=http://10.0.0.2member3=http://10.0.0.3。当 member1 失败时,将其替换为 member4=http://10.0.0.4

  1. 获取失败的 member1 的成员 ID:

    etcdctl --endpoints=http://10.0.0.2,http://10.0.0.3 member list
    

    显示以下信息:

    8211f1d0f64f3269, started, member1, http://10.0.0.1:2380, http://10.0.0.1:2379
    91bc3c398fb3c146, started, member2, http://10.0.0.2:2380, http://10.0.0.2:2379
    fd422379fda50e48, started, member3, http://10.0.0.3:2380, http://10.0.0.3:2379
    
  1. 执行以下操作之一:

    1. 如果每个 Kubernetes API 服务器都配置为与所有 etcd 成员通信, 请从 --etcd-servers 标志中移除删除失败的成员,然后重新启动每个 Kubernetes API 服务器。
    2. 如果每个 Kubernetes API 服务器都与单个 etcd 成员通信, 则停止与失败的 etcd 通信的 Kubernetes API 服务器。
  1. 停止故障节点上的 etcd 服务器。除了 Kubernetes API 服务器之外的其他客户端可能会造成流向 etcd 的流量, 可以停止所有流量以防止写入数据目录。
  1. 移除失败的成员:

    etcdctl member remove 8211f1d0f64f3269
    

    显示以下信息:

    Removed member 8211f1d0f64f3269 from cluster
    
  1. 增加新成员:

    etcdctl member add member4 --peer-urls=http://10.0.0.4:2380
    

    显示以下信息:

    Member 2be1eb8f84b7f63e added to cluster ef37ad9dc622a7c4
    
  1. 在 IP 为 10.0.0.4 的机器上启动新增加的成员:

    export ETCD_NAME="member4"
    export ETCD_INITIAL_CLUSTER="member2=http://10.0.0.2:2380,member3=http://10.0.0.3:2380,member4=http://10.0.0.4:2380"
    export ETCD_INITIAL_CLUSTER_STATE=existing
    etcd [flags]
    
  1. 执行以下操作之一:

    1. 如果每个 Kubernetes API 服务器都配置为与所有 etcd 成员通信, 则将新增的成员添加到 --etcd-servers 标志,然后重新启动每个 Kubernetes API 服务器。
    2. 如果每个 Kubernetes API 服务器都与单个 etcd 成员通信,请启动在第 2 步中停止的 Kubernetes API 服务器。 然后配置 Kubernetes API 服务器客户端以再次将请求路由到已停止的 Kubernetes API 服务器。 这通常可以通过配置负载均衡器来完成。

有关集群重新配置的详细信息,请参阅 etcd 重构文档

备份 etcd 集群

所有 Kubernetes 对象都存储在 etcd 中。 定期备份 etcd 集群数据对于在灾难场景(例如丢失所有控制平面节点)下恢复 Kubernetes 集群非常重要。 快照文件包含所有 Kubernetes 状态和关键信息。为了保证敏感的 Kubernetes 数据的安全,可以对快照文件进行加密。

备份 etcd 集群可以通过两种方式完成:etcd 内置快照和卷快照。

内置快照

etcd 支持内置快照。快照可以从使用 etcdctl snapshot save 命令的活动成员中创建, 也可以通过从 etcd 数据目录 复制 member/snap/db 文件,该 etcd 数据目录目前没有被 etcd 进程使用。创建快照不会影响成员的性能。

下面是一个示例,用于创建 $ENDPOINT 所提供的键空间的快照到文件 snapshot.db

ETCDCTL_API=3 etcdctl --endpoints $ENDPOINT snapshot save snapshot.db

验证快照:

ETCDCTL_API=3 etcdctl --write-out=table snapshot status snapshot.db
+----------+----------+------------+------------+
|   HASH   | REVISION | TOTAL KEYS | TOTAL SIZE |
+----------+----------+------------+------------+
| fe01cf57 |       10 |          7 | 2.1 MB     |
+----------+----------+------------+------------+

卷快照

如果 etcd 运行在支持备份的存储卷(如 Amazon Elastic Block 存储)上,则可以通过创建存储卷的快照来备份 etcd 数据。

使用 etcdctl 选项的快照

我们还可以使用 etcdctl 提供的各种选项来制作快照。例如:

ETCDCTL_API=3 etcdctl -h 

列出 etcdctl 可用的各种选项。例如,你可以通过指定端点、证书和密钥来制作快照,如下所示:

ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
  --cacert=<trusted-ca-file> --cert=<cert-file> --key=<key-file> \
  snapshot save <backup-file-location>

可以从 etcd Pod 的描述中获得 trusted-ca-filecert-filekey-file

为 etcd 集群扩容

通过交换性能,对 etcd 集群扩容可以提高可用性。缩放不会提高集群性能和能力。 一般情况下不要扩大或缩小 etcd 集群的集合。不要为 etcd 集群配置任何自动缩放组。 强烈建议始终在任何官方支持的规模上运行生产 Kubernetes 集群时使用静态的五成员 etcd 集群。

合理的扩展是在需要更高可靠性的情况下,将三成员集群升级为五成员集群。 请参阅 etcd 重构文档 以了解如何将成员添加到现有集群中的信息。

恢复 etcd 集群

etcd 支持从 major.minor 或其他不同 patch 版本的 etcd 进程中获取的快照进行恢复。 还原操作用于恢复失败的集群的数据。

在启动还原操作之前,必须有一个快照文件。它可以是来自以前备份操作的快照文件, 也可以是来自剩余数据目录的快照文件。

在恢复集群时,使用 --data-dir 选项来指定集群应被恢复到哪个文件夹。

ETCDCTL_API=3 etcdctl --data-dir <data-dir-location> snapshot restore snapshot.db

其中 <data-dir-location> 是将在恢复过程中创建的目录。

另一个例子是先导出 ETCDCTL_API 环境变量:

export ETCDCTL_API=3
etcdctl --data-dir <data-dir-location> snapshot restore snapshot.db

如果 <data-dir-location> 与之前的文件夹相同,请先删除此文件夹并停止 etcd 进程,再恢复集群。 否则,需要在恢复后更改 etcd 配置并重新启动 etcd 进程才能使用新的数据目录。

有关从快照文件还原集群的详细信息和示例,请参阅 etcd 灾难恢复文档

如果还原的集群的访问 URL 与前一个集群不同,则必须相应地重新配置 Kubernetes API 服务器。 在本例中,使用参数 --etcd-servers=$NEW_ETCD_CLUSTER 而不是参数 --etcd-servers=$OLD_ETCD_CLUSTER 重新启动 Kubernetes API 服务器。用相应的 IP 地址替换 $NEW_ETCD_CLUSTER$OLD_ETCD_CLUSTER。 如果在 etcd 集群前面使用负载均衡,则可能需要更新负载均衡器。

如果大多数 etcd 成员永久失败,则认为 etcd 集群失败。在这种情况下,Kubernetes 不能对其当前状态进行任何更改。 虽然已调度的 Pod 可能继续运行,但新的 Pod 无法调度。在这种情况下, 恢复 etcd 集群并可能需要重新配置 Kubernetes API 服务器以修复问题。

升级 etcd 集群

有关 etcd 升级的更多详细信息,请参阅 etcd 升级文档。

维护 etcd 集群

有关 etcd 维护的更多详细信息,请参阅 etcd 维护文档。

你还可以将碎片整理工具作为 Kubernetes CronJob 运行,以确保定期进行碎片整理。 有关详细信息,请参阅 etcd-defrag-cronjob.yaml

4.2.30 - 为系统守护进程预留计算资源

Kubernetes 的节点可以按照 Capacity 调度。默认情况下 pod 能够使用节点全部可用容量。 这是个问题,因为节点自己通常运行了不少驱动 OS 和 Kubernetes 的系统守护进程。 除非为这些系统守护进程留出资源,否则它们将与 Pod 争夺资源并导致节点资源短缺问题。

kubelet 公开了一个名为 'Node Allocatable' 的特性,有助于为系统守护进程预留计算资源。 Kubernetes 推荐集群管理员按照每个节点上的工作负载密度配置 'Node Allocatable'。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你的 Kubernetes 服务器版本必须不低于版本 1.8. 要获知版本信息,请输入 kubectl version.

你的 kubernetes 服务器版本必须至少是 1.17 版本,才能使用 kubelet 命令行选项 --reserved-cpus 设置显式预留 CPU 列表

节点可分配资源

节点容量

Kubernetes 节点上的 'Allocatable' 被定义为 Pod 可用计算资源量。 调度器不会超额申请 'Allocatable'。 目前支持 'CPU'、'memory' 和 'ephemeral-storage' 这几个参数。

可分配的节点暴露为 API 中 v1.Node 对象的一部分,也是 CLI 中 kubectl describe node 的一部分。

kubelet 中,可以为两类系统守护进程预留资源。

启用 QoS 和 Pod 级别的 cgroups

为了恰当地在节点范围实施节点可分配约束,你必须通过 --cgroups-per-qos 标志启用新的 cgroup 层次结构。这个标志是默认启用的。 启用后,kubelet 将在其管理的 cgroup 层次结构中创建所有终端用户的 Pod。

配置 cgroup 驱动

kubelet 支持在主机上使用 cgroup 驱动操作 cgroup 层次结构。 该驱动通过 --cgroup-driver 标志进行配置。

支持的参数值如下:

  • cgroupfs 是默认的驱动,在主机上直接操作 cgroup 文件系统以对 cgroup 沙箱进行管理。
  • systemd 是可选的驱动,使用 init 系统支持的资源的瞬时切片管理 cgroup 沙箱。

取决于相关容器运行时的配置,操作员可能需要选择一个特定的 cgroup 驱动来保证系统正常运行。 例如,如果操作员使用 containerd 运行时提供的 systemd cgroup 驱动时, 必须配置 kubelet 使用 systemd cgroup 驱动。

Kube 预留值

  • Kubelet 标志--kube-reserved=[cpu=100m][,][memory=100Mi][,][ephemeral-storage=1Gi][,][pid=1000]
  • Kubelet 标志--kube-reserved-cgroup=

kube-reserved 用来给诸如 kubelet、容器运行时、节点问题监测器等 Kubernetes 系统守护进程记述其资源预留值。 该配置并非用来给以 Pod 形式运行的系统守护进程预留资源。kube-reserved 通常是节点上 Pod 密度 的函数。

除了 cpu内存ephemeral-storage 之外,pid 可用来指定为 Kubernetes 系统守护进程预留指定数量的进程 ID。

要选择性地对 Kubernetes 系统守护进程上执行 kube-reserved 保护,需要把 kubelet 的 --kube-reserved-cgroup 标志的值设置为 kube 守护进程的父控制组。

推荐将 Kubernetes 系统守护进程放置于顶级控制组之下(例如 systemd 机器上的 runtime.slice)。 理想情况下每个系统守护进程都应该在其自己的子控制组中运行。 请参考这个设计方案, 进一步了解关于推荐控制组层次结构的细节。

请注意,如果 --kube-reserved-cgroup 不存在,Kubelet 将 不会 创建它。 如果指定了一个无效的 cgroup,Kubelet 将会无法启动。就 systemd cgroup 驱动而言, 你要为所定义的 cgroup 设置名称时要遵循特定的模式: 所设置的名字应该是你为 --kube-reserved-cgroup 所给的参数值加上 .slice 后缀。

系统预留值

  • Kubelet 标志--system-reserved=[cpu=100m][,][memory=100Mi][,][ephemeral-storage=1Gi][,][pid=1000]
  • Kubelet 标志--system-reserved-cgroup=

system-reserved 用于为诸如 sshdudev 等系统守护进程记述其资源预留值。 system-reserved 也应该为 kernel 预留 内存,因为目前 kernel 使用的内存并不记在 Kubernetes 的 Pod 上。 同时还推荐为用户登录会话预留资源(systemd 体系中的 user.slice)。

除了 cpu内存ephemeral-storage 之外,pid 可用来指定为 Kubernetes 系统守护进程预留指定数量的进程 ID。

要想为系统守护进程上可选地实施 system-reserved 约束,请指定 kubelet 的 --system-reserved-cgroup 标志值为 OS 系统守护进程的父级控制组。

推荐将 OS 系统守护进程放在一个顶级控制组之下(例如 systemd 机器上的 system.slice)。

请注意,如果 --system-reserved-cgroup 不存在,kubelet 不会 创建它。 如果指定了无效的 cgroup,kubelet 将会失败。就 systemd cgroup 驱动而言, 你在指定 cgroup 名字时要遵循特定的模式: 该名字应该是你为 --system-reserved-cgroup 参数所设置的值加上 .slice 后缀。

显式预留的 CPU 列表

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

Kubelet 标志: --reserved-cpus=0-3 KubeletConfiguration 标志reservedSystemCPUs: 0-3

reserved-cpus 旨在为操作系统守护程序和 Kubernetes 系统守护程序预留一组明确指定编号的 CPU。 reserved-cpus 适用于不打算针对 cpuset 资源为操作系统守护程序和 Kubernetes 系统守护程序定义独立的顶级 cgroups 的系统。 如果 Kubelet 没有 指定参数 --system-reserved-cgroup--kube-reserved-cgroup, 则 reserved-cpus 的设置将优先于 --kube-reserved--system-reserved 选项。

此选项是专门为电信/NFV 用例设计的,在这些用例中不受控制的中断或计时器可能会影响其工作负载性能。 你可以使用此选项为系统或 Kubernetes 守护程序以及中断或计时器显式定义 cpuset, 这样系统上的其余 CPU 可以专门用于工作负载,因不受控制的中断或计时器的影响得以降低。 要将系统守护程序、Kubernetes 守护程序和中断或计时器移动到此选项定义的显式 cpuset 上,应使用 Kubernetes 之外的其他机制。 例如:在 CentOS 系统中,可以使用 tuned 工具集来执行此操作。

驱逐阈值

Kubelet 标志--eviction-hard=[memory.available<500Mi]

节点级别的内存压力将导致系统内存不足,这将影响到整个节点及其上运行的所有 Pod。 节点可以暂时离线直到内存已经回收为止。为了防止系统内存不足(或减少系统内存不足的可能性), kubelet 提供了资源不足管理。 驱逐操作只支持 memoryephemeral-storage。 通过 --eviction-hard 标志预留一些内存后,当节点上的可用内存降至预留值以下时, kubelet 将尝试驱逐 Pod。 如果节点上不存在系统守护进程,Pod 将不能使用超过 capacity-eviction-hard 所指定的资源量。 因此,为驱逐而预留的资源对 Pod 是不可用的。

实施节点可分配约束

Kubelet 标志--enforce-node-allocatable=pods[,][system-reserved][,][kube-reserved]

调度器将 'Allocatable' 视为 Pod 可用的 capacity(资源容量)。

kubelet 默认对 Pod 执行 'Allocatable' 约束。 无论何时,如果所有 Pod 的总用量超过了 'Allocatable',驱逐 Pod 的措施将被执行。 有关驱逐策略的更多细节可以在节点压力驱逐页找到。 可通过设置 kubelet --enforce-node-allocatable 标志值为 pods 控制这个措施。

可选地,通过在同一标志中同时指定 kube-reservedsystem-reserved 值, 可以使 kubelet 强制实施 kube-reservedsystem-reserved 约束。 请注意,要想执行 kube-reserved 或者 system-reserved 约束, 需要对应设置 --kube-reserved-cgroup 或者 --system-reserved-cgroup

一般原则

系统守护进程一般会被按照类似 Guaranteed 的 Pod 一样对待。 系统守护进程可以在与其对应的控制组中出现突发资源用量,这一行为要作为 Kubernetes 部署的一部分进行管理。 例如,kubelet 应该有它自己的控制组并和容器运行时共享 kube-reserved 资源。 不过,如果执行了 kube-reserved 约束,则 kubelet 不可出现突发负载并用光节点的所有可用资源。

在执行 system-reserved 预留策略时请加倍小心,因为它可能导致节点上的关键系统服务出现 CPU 资源短缺、 因为内存不足而被终止或者无法在节点上创建进程。 建议只有当用户详尽地描述了他们的节点以得出精确的估计值, 并且对该组中进程因内存不足而被杀死时,有足够的信心将其恢复时, 才可以强制执行 system-reserved 策略。

  • 作为起步,可以先针对 pods 上执行 'Allocatable' 约束。
  • 一旦用于追踪系统守护进程的监控和告警的机制到位,可尝试基于用量估计的方式执行 kube-reserved 策略。
  • 随着时间推进,在绝对必要的时候可以执行 system-reserved 策略。

随着时间推进和越来越多特性被加入,kube 系统守护进程对资源的需求可能也会增加。 以后 Kubernetes 项目将尝试减少对节点系统守护进程的利用,但目前这件事的优先级并不是最高。 所以,将来的发布版本中 Allocatable 容量是有可能降低的。

示例场景

这是一个用于说明节点可分配(Node Allocatable)计算方式的示例:

  • 节点拥有 32Gi memory16 CPU100Gi Storage 资源
  • --kube-reserved 被设置为 cpu=1,memory=2Gi,ephemeral-storage=1Gi
  • --system-reserved 被设置为 cpu=500m,memory=1Gi,ephemeral-storage=1Gi
  • --eviction-hard 被设置为 memory.available<500Mi,nodefs.available<10%

在这个场景下,'Allocatable' 将会是 14.5 CPUs、28.5Gi 内存以及 88Gi 本地存储。 调度器保证这个节点上的所有 Pod 的内存 requests 总量不超过 28.5Gi,存储不超过 '88Gi'。 当 Pod 的内存使用总量超过 28.5Gi 或者磁盘使用总量超过 88Gi 时,kubelet 将会驱逐它们。 如果节点上的所有进程都尽可能多地使用 CPU,则 Pod 加起来不能使用超过 14.5 CPUs 的资源。

当没有执行 kube-reserved 和/或 system-reserved 策略且系统守护进程使用量超过其预留时, 如果节点内存用量高于 31.5Gi 或 storage 大于 90Gi,kubelet 将会驱逐 Pod。

4.2.31 - 以非 root 用户身份运行 Kubernetes 节点组件

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

这个文档描述了怎样不使用 root 特权,而是通过使用 用户命名空间 去运行 Kubernetes 节点组件(例如 kubelet、CRI、OCI、CNI)。

这种技术也叫做 rootless 模式(Rootless mode)

准备开始

你的 Kubernetes 服务器版本必须不低于版本 1.22. 要获知版本信息,请输入 kubectl version.

使用 Rootless 模式的 Docker/Podman 运行 Kubernetes

kind

kind 支持使用 Rootless 模式的 Docker 或者 Podman 运行 Kubernetes。

请参阅使用 Rootless 模式的 Docker 运行 kind

minikube

minikube 也支持使用 Rootless 模式的 Docker 或 Podman 运行 Kubernetes。

请参阅 Minikube 文档:

在非特权容器内运行 Kubernetes

sysbox

Sysbox 是一个开源容器运行时 (类似于 “runc”),支持在 Linux 用户命名空间隔离的非特权容器内运行系统级工作负载, 比如 Docker 和 Kubernetes。

查看 Sysbox 快速入门指南: Kubernetes-in-Docker 了解更多细节。

Sysbox 支持在非特权容器内运行 Kubernetes, 而不需要 cgroup v2 和 “KubeletInUserNamespace” 特性门控。 Sysbox 通过在容器内暴露特定的 /proc/sys 文件系统, 以及其它一些先进的操作系统虚拟化技术来实现。

直接在主机上运行 Rootless 模式的 Kubernetes

K3s

K3s 实验性支持了 Rootless 模式。

请参阅使用 Rootless 模式运行 K3s 页面中的用法.

Usernetes

Usernetes 是 Kubernetes 的一个参考发行版, 它可以在不使用 root 特权的情况下安装在 $HOME 目录下。

Usernetes 支持使用 containerd 和 CRI-O 作为 CRI 运行时。 Usernetes 支持配置了 Flannel (VXLAN)的多节点集群。

关于用法,请参阅 Usernetes 仓库

手动部署一个在用户命名空间运行 kubelet 的节点

本节提供在用户命名空间手动运行 Kubernetes 的注意事项。

创建用户命名空间

第一步是创建一个 用户命名空间

如果你正在尝试使用用户命名空间的容器(例如 Rootless 模式的 Docker/Podman 或 LXC/LXD) 运行 Kubernetes,那么你已经准备就绪,可以直接跳到下一小节。

否则你需要通过传递参数 CLONE_NEWUSER 调用 unshare(2),自己创建一个命名空间。

用户命名空间也可以通过如下所示的命令行工具取消共享:

在取消命名空间的共享之后,你也必须对其它的命名空间例如 mount 命名空间取消共享。

在取消 mount 命名空间的共享之后,你需要调用 chroot() 或者 pivot_root(), 但是你必须在这个命名空间内挂载可写的文件系统到几个目录上。

请确保这个命名空间内(不是这个命名空间外部)至少以下几个目录是可写的:

  • /etc
  • /run
  • /var/logs
  • /var/lib/kubelet
  • /var/lib/cni
  • /var/lib/containerd (参照 containerd)
  • /var/lib/containers (参照 CRI-O)

创建委派 cgroup 树

除了用户命名空间,你也需要有一个版本为 cgroup v2 的可写 cgroup 树。

如果你在一个采用 systemd 机制的主机上使用用户命名空间的容器(例如 Rootless 模式的 Docker/Podman 或 LXC/LXD)来运行 Kubernetes,那么你已经准备就绪。

否则你必须创建一个具有 Delegate=yes 属性的 systemd 单元,来委派一个具有可写权限的 cgroup 树。

在你的节点上,systemd 必须已经配置为允许委派。更多细节请参阅 Rootless 容器文档的 cgroup v2 部分。

配置网络

节点组件的网络命名空间必须有一个非本地回路的网卡。它可以使用 slirp4netnsVPNKitlxc-user-nic(1) 等工具进行配置。

Pod 的网络命名空间可以使用常规的 CNI 插件配置。对于多节点的网络,已知 Flannel (VXLAN、8472/UDP) 可以正常工作。

诸如 kubelet 端口(10250/TCP)和 NodePort 服务端口之类的端口必须通过外部端口转发器 (例如 RootlessKit、slirp4netns 或 socat(1)) 从节点网络命名空间暴露给主机。

你可以使用 K3s 的端口转发器。更多细节请参阅 在 Rootless 模式下运行 K3s。 该实现可以在 k3s 的 pkg/rootlessports中找到。

配置 CRI

kubelet 依赖于容器运行时。你需要部署一个容器运行时(例如 containerd 或 CRI-O), 并确保它在 kubelet 启动之前已经在用户命名空间内运行。

containerd 1.4 开始支持在用户命名空间运行 containerd 的 CRI 插件。

在用户命名空间运行 containerd 必须进行如下配置:

version = 2

[plugins."io.containerd.grpc.v1.cri"]
# 禁用 AppArmor
  disable_apparmor = true
# 忽略配置 oom_score_adj 时的错误
  restrict_oom_score_adj = true
# 禁用 hugetlb cgroup v2 控制器(因为 systemd 不支持委派 hugetlb controller)
  disable_hugetlb_controller = true

[plugins."io.containerd.grpc.v1.cri".containerd]
# 如果内核 >= 5.11 , 也可以使用 non-fuse overlayfs, 但需要禁用 SELinux
  snapshotter = "fuse-overlayfs"

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
# 我们使用的 cgroupfs 已经被 systemd 委派,所以我们不使用 SystemdCgroup 驱动
# (除非你在命名空间内运行了另一个 systemd)
  SystemdCgroup = false

配置文件的默认路径是 /etc/containerd/config.toml。 可以用 containerd -c /path/to/containerd/config.toml 来指定该路径。

CRI-O 1.22 开始支持在用户命名空间运行 CRI-O。

CRI-O 必须配置一个环境变量 _CRIO_ROOTLESS=1

也推荐使用以下配置:

[crio]
  storage_driver = "overlay"
# 如果内核 >= 5.11 , 也可以使用 non-fuse overlayfs, 但需要禁用 SELinux
  storage_option = ["overlay.mount_program=/usr/local/bin/fuse-overlayfs"]

[crio.runtime]
# 我们使用的 cgroupfs 已经被 systemd 委派,所以我们不使用 "systemd" 驱动
# (除非你在命名空间内运行了另一个 systemd)
  cgroup_manager = "cgroupfs"

配置文件的默认路径是 /etc/containerd/config.toml。 可以用 containerd -c /path/to/containerd/config.toml 来指定该路径。

配置 kubelet

在用户命名空间运行 kubelet 必须进行如下配置:

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
featureGates:
  KubeletInUserNamespace: true
# 我们使用的 cgroupfs 已经被 systemd 委派,所以我们不使用 "systemd" 驱动
# (除非你在命名空间内运行了另一个 systemd)
cgroupDriver: "cgroupfs"

KubeletInUserNamespace 特性门控被启用时, kubelet 会忽略节点内由于配置如下几个 sysctl 参数值而可能产生的错误。

  • vm.overcommit_memory
  • vm.panic_on_oom
  • kernel.panic
  • kernel.panic_on_oops
  • kernel.keys.root_maxkeys
  • kernel.keys.root_maxbytes.

在用户命名空间内, kubelet 也会忽略任何由于打开 /dev/kmsg 而产生的错误。 这个特性门控也允许 kube-proxy 忽略由于配置 RLIMIT_NOFILE 而产生的一个错误。

KubeletInUserNamespace 特性门控从 Kubernetes v1.22 被引入, 标记为 "alpha" 状态。

通过挂载特制的 proc 文件系统 (比如 Sysbox), 也可以在不使用这个特性门控的情况下在用户命名空间运行 kubelet,但这不受官方支持。

配置 kube-proxy

在用户命名空间运行 kube-proxy 需要进行以下配置:

apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: "iptables" # or "userspace"
conntrack:
# 跳过配置 sysctl 的值 "net.netfilter.nf_conntrack_max"
  maxPerCore: 0
# 跳过配置 "net.netfilter.nf_conntrack_tcp_timeout_established"
  tcpEstablishedTimeout: 0s
# 跳过配置 "net.netfilter.nf_conntrack_tcp_timeout_close"
  tcpCloseWaitTimeout: 0s

注意事项

  • 大部分“非本地”的卷驱动(例如 nfsiscsi)不能正常工作。 已知诸如 localhostPathemptyDirconfigMapsecretdownwardAPI 这些本地卷是能正常工作的。

  • 一些 CNI 插件可能不正常工作。已知 Flannel (VXLAN) 是能正常工作的。

更多细节请参阅 rootlesscontaine.rs 站点的 Caveats and Future work 页面。

另请参见

4.2.32 - 安全地清空一个节点

本页展示了如何在确保 PodDisruptionBudget 的前提下, 安全地清空一个节点

准备开始

此任务假定你已经满足了以下先决条件:

  1. 在节点清空期间,不要求应用具有高可用性
  2. 你已经了解了 PodDisruptionBudget 的概念, 并为需要它的应用配置了 PodDisruptionBudget

(可选)配置干扰预算

为了确保你的负载在维护期间仍然可用,你可以配置一个 PodDisruptionBudget。 如果可用性对于正在清空的该节点上运行或可能在该节点上运行的任何应用程序很重要, 首先 配置一个 PodDisruptionBudgets 并继续遵循本指南。

建议为你的 PodDisruptionBudgets 设置 AlwaysAllow 不健康 Pod 驱逐策略, 以在节点清空期间支持驱逐异常的应用程序。 默认行为是等待应用程序的 Pod 变为 健康后, 才能进行清空操作。

使用 kubectl drain 从服务中删除一个节点

在对节点执行维护(例如内核升级、硬件维护等)之前, 可以使用 kubectl drain 从节点安全地逐出所有 Pod。 安全的驱逐过程允许 Pod 的容器体面地终止, 并确保满足指定的 PodDisruptionBudgets

kubectl drain 的成功返回,表明所有的 Pod(除了上一段中描述的被排除的那些), 已经被安全地逐出(考虑到期望的终止宽限期和你定义的 PodDisruptionBudget)。 然后就可以安全地关闭节点, 比如关闭物理机器的电源,如果它运行在云平台上,则删除它的虚拟机。

首先,确定想要清空的节点的名称。可以用以下命令列出集群中的所有节点:

kubectl get nodes

接下来,告诉 Kubernetes 清空节点:

kubectl drain --ignore-daemonsets <节点名称>

如果存在 DaemonSet 管理的 Pod,你将需要为 kubectl 设置 --ignore-daemonsets 以成功地清空节点。 kubectl drain 子命令自身实际上不清空节点上的 DaemonSet Pod 集合: DaemonSet 控制器(作为控制平面的一部分)会立即用新的等效 Pod 替换缺少的 Pod。 DaemonSet 控制器还会创建忽略不可调度污点的 Pod,这种污点允许在你正在清空的节点上启动新的 Pod。

一旦它返回(没有报错), 你就可以下线此节点(或者等价地,如果在云平台上,删除支持该节点的虚拟机)。 如果要在维护操作期间将节点留在集群中,则需要运行:

kubectl uncordon <node name>

然后告诉 Kubernetes,它可以继续在此节点上调度新的 Pod。

并行清空多个节点

kubectl drain 命令一次只能发送给一个节点。 但是,你可以在不同的终端或后台为不同的节点并行地运行多个 kubectl drain 命令。 同时运行的多个 drain 命令仍然遵循你指定的 PodDisruptionBudget

例如,如果你有一个三副本的 StatefulSet, 并设置了一个 PodDisruptionBudget,指定 minAvailable: 2。 如果所有的三个 Pod 处于健康(healthy)状态, 并且你并行地发出多个 drain 命令,那么 kubectl drain 只会从 StatefulSet 中逐出一个 Pod, 因为 Kubernetes 会遵守 PodDisruptionBudget 并确保在任何时候只有一个 Pod 不可用 (最多不可用 Pod 个数的计算方法:replicas - minAvailable)。 任何会导致处于健康(healthy) 状态的副本数量低于指定预算的清空操作都将被阻止。

驱逐 API

如果你不喜欢使用 kubectl drain (比如避免调用外部命令,或者更细化地控制 Pod 驱逐过程), 你也可以用驱逐 API 通过编程的方式达到驱逐的效果。 更多信息,请参阅 API 发起的驱逐

接下来

  • 执行配置 PDB 中的各个步骤, 保护你的应用。

4.2.33 - 保护集群

本文档涉及与保护集群免受意外或恶意访问有关的主题,并对总体安全性提出建议。

准备开始

  • 你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

    要获知版本信息,请输入 kubectl version.

控制对 Kubernetes API 的访问

因为 Kubernetes 是完全通过 API 驱动的,所以,控制和限制谁可以通过 API 访问集群, 以及允许这些访问者执行什么样的 API 动作,就成为了安全控制的第一道防线。

为所有 API 交互使用传输层安全(TLS)

Kubernetes 期望集群中所有的 API 通信在默认情况下都使用 TLS 加密, 大多数安装方法也允许创建所需的证书并且分发到集群组件中。 请注意,某些组件和安装方法可能使用 HTTP 来访问本地端口, 管理员应该熟悉每个组件的设置,以识别可能不安全的流量。

API 认证

安装集群时,选择一个 API 服务器的身份验证机制,去使用与之匹配的公共访问模式。 例如,小型的单用户集群可能希望使用简单的证书或静态承载令牌方法。 更大的集群则可能希望整合现有的、OIDC、LDAP 等允许用户分组的服务器。

所有 API 客户端都必须经过身份验证,即使它是基础设施的一部分,比如节点、代理、调度程序和卷插件。 这些客户端通常使用 服务帐户 或 X509 客户端证书,并在集群启动时自动创建或是作为集群安装的一部分进行设置。

如果你希望获取更多信息,请参考认证参考文档

API 授权

一旦通过身份认证,每个 API 的调用都将通过鉴权检查。 Kubernetes 集成基于角色的访问控制(RBAC)组件, 将传入的用户或组与一组绑定到角色的权限匹配。 这些权限将动作(get、create、delete)和资源(Pod、Service、Node)进行组合,并可在名字空间或者集群范围生效。 Kubernetes 提供了一组可直接使用的角色,这些角色根据客户可能希望执行的操作提供合理的责任划分。 建议你同时使用 NodeRBAC 两个鉴权组件,再与 NodeRestriction 准入插件结合使用。

与身份验证一样,简单而广泛的角色可能适合于较小的集群,但是随着更多的用户与集群交互, 可能需要将团队划分到有更多角色限制的、 单独的名字空间中去。

就鉴权而言,很重要的一点是理解对象上的更新操作如何导致在其它地方发生对应行为。 例如,用户可能不能直接创建 Pod,但允许他们通过创建 Deployment 来创建这些 Pod, 这将让他们间接创建这些 Pod。 同样地,从 API 删除一个节点将导致调度到这些节点上的 Pod 被中止,并在其他节点上重新创建。 原生的角色设计代表了灵活性和常见用例之间的平衡,但须限制的角色应该被仔细审查, 以防止意外的权限升级。如果内置的角色无法满足你的需求,则可以根据使用场景需要创建特定的角色。

如果你希望获取更多信息,请参阅鉴权参考

控制对 Kubelet 的访问

Kubelet 公开 HTTPS 端点,这些端点提供了对节点和容器的强大的控制能力。 默认情况下,Kubelet 允许对此 API 进行未经身份验证的访问。

生产级别的集群应启用 Kubelet 身份认证和授权。

进一步的信息,请参考 Kubelet 身份验证/授权参考

控制运行时负载或用户的能力

Kubernetes 中的授权故意设计成较高抽象级别,侧重于对资源的粗粒度行为。 更强大的控制是 策略 的形式呈现的,根据使用场景限制这些对象如何作用于集群、自身和其他资源。

限制集群上的资源使用

资源配额(Resource Quota)限制了赋予命名空间的资源的数量或容量。 资源配额通常用于限制名字空间可以分配的 CPU、内存或持久磁盘的数量, 但也可以控制每个名字空间中存在多少个 Pod、Service 或 Volume。

限制范围(Limit Range) 限制上述某些资源的最大值或者最小值,以防止用户使用类似内存这样的通用保留资源时请求不合理的过高或过低的值, 或者在没有指定的情况下提供默认限制。

控制容器运行的特权

Pod 定义包含了一个安全上下文, 用于描述一些访问请求,如以某个节点上的特定 Linux 用户(如 root)身份运行, 以特权形式运行,访问主机网络,以及一些在宿主节点上不受约束地运行的其它控制权限等等。

你可以配置 Pod 安全准入来在某个 名字空间中 强制实施特定的 Pod 安全标准(Pod Security Standard), 或者检查安全上的缺陷。

一般来说,大多数应用程序需要对主机资源的有限制的访问, 这样它们可以在不访问主机信息的情况下,成功地以 root 账号(UID 0)运行。 但是,考虑到与 root 用户相关的特权,在编写应用程序容器时,你应该使用非 root 用户运行。 类似地,希望阻止客户端应用程序从其容器中逃逸的管理员,应该应用 BaselineRestricted Pod 安全标准。

防止容器加载不需要的内核模块

如果在某些情况下,Linux 内核会根据需要自动从磁盘加载内核模块, 这类情况的例子有挂接了一个硬件或挂载了一个文件系统。 与 Kubernetes 特别相关的是,即使是非特权的进程也可能导致某些网络协议相关的内核模块被加载, 而这只需创建一个适当类型的套接字。 这就可能允许攻击者利用管理员假定未使用的内核模块中的安全漏洞。

为了防止特定模块被自动加载,你可以将它们从节点上卸载或者添加规则来阻止这些模块。 在大多数 Linux 发行版上,你可以通过创建类似 /etc/modprobe.d/kubernetes-blacklist.conf 这种文件来做到这一点,其中的内容如下所示:

# DCCP is unlikely to be needed, has had multiple serious
# vulnerabilities, and is not well-maintained.
blacklist dccp

# SCTP is not used in most Kubernetes clusters, and has also had
# vulnerabilities in the past.
blacklist sctp

为了更大范围地阻止内核模块被加载,你可以使用 Linux 安全模块(如 SELinux) 来彻底拒绝容器的 module_request 权限,从而防止在任何情况下系统为容器加载内核模块。 (Pod 仍然可以使用手动加载的模块,或者使用由内核代表某些特权进程所加载的模块。)

限制网络访问

基于名字空间的网络策略 允许应用程序作者限制其它名字空间中的哪些 Pod 可以访问自身名字空间内的 Pod 和端口。 现在已经有许多支持网络策略的 Kubernetes 网络驱动

配额(Quota)和限制范围(Limit Range)也可用于控制用户是否可以请求节点端口或负载均衡服务。 在很多集群上,节点端口和负载均衡服务也可控制用户的应用程序是否在集群之外可见。

此外也可能存在一些基于插件或基于环境的网络规则,能够提供额外的保护能力。 例如各节点上的防火墙、物理隔离集群节点以防止串扰或者高级的网络策略等。

限制云元数据 API 访问

云平台(AWS、Azure、GCE 等)经常将 metadata 本地服务暴露给实例。 默认情况下,这些 API 可由运行在实例上的 Pod 访问,并且可以包含 该云节点的凭据或配置数据(如 kubelet 凭据)。 这些凭据可以用于在集群内升级或在同一账户下升级到其他云服务。

在云平台上运行 Kubernetes 时,需要限制对实例凭据的权限,使用 网络策略 限制 Pod 对元数据 API 的访问,并避免使用配置数据来传递机密信息。

控制 Pod 可以访问的节点

默认情况下,对 Pod 可以运行在哪些节点上是没有任何限制的。 Kubernetes 给最终用户提供了 一组丰富的策略用于控制 Pod 所放置的节点位置, 以及基于污点的 Pod 放置和驱逐。 对于许多集群,使用这些策略来分离工作负载可以作为一种约定,要求作者遵守或者通过工具强制。

对于管理员,Beta 阶段的准入插件 PodNodeSelector 可用于强制某名字空间中的 Pod 使用默认的或特定的节点选择算符。 如果最终用户无法改变名字空间,这一机制可以有效地限制特定工作负载中所有 Pod 的放置位置。

保护集群组件免受破坏

本节描述保护集群免受破坏的一些常用模式。

限制访问 etcd

拥有对 API 的 etcd 后端的写访问权限相当于获得了整个集群的 root 权限, 读访问权限也可能被利用,实现相当快速的权限提升。 对于从 API 服务器访问其 etcd 服务器,管理员应该总是使用比较强的凭证,如通过 TLS 客户端证书来实现双向认证。 通常,我们建议将 etcd 服务器隔离到只有 API 服务器可以访问的防火墙后面。

启用审计日志

审计日志是 Beta 特性, 负责记录 API 操作以便在发生破坏时进行事后分析。 建议启用审计日志,并将审计文件归档到安全服务器上。

限制使用 Alpha 和 Beta 特性

Kubernetes 的 Alpha 和 Beta 特性还在努力开发中,可能存在导致安全漏洞的缺陷或错误。 要始终评估 Alpha 和 Beta 特性可能给你的安全态势带来的风险。 当你怀疑存在风险时,可以禁用那些不需要使用的特性。

经常轮换基础设施证书

一项机密信息或凭据的生命期越短,攻击者就越难使用该凭据。 在证书上设置较短的生命期并实现自动轮换是控制安全的一个好方法。 使用身份验证提供程序时,应该使用那些可以控制所发布令牌的合法时长的提供程序, 并尽可能设置较短的生命期。 如果在外部集成场景中使用服务帐户令牌,则应该经常性地轮换这些令牌。 例如,一旦引导阶段完成,就应该撤销用于配置节点的引导令牌,或者取消它的授权。

在启用第三方集成之前,请先审查它们

许多集成到 Kubernetes 的第三方软件或服务都可能改变你的集群的安全配置。 启用集成时,在授予访问权限之前,你应该始终检查扩展所请求的权限。 例如,许多安全性集成中可能要求查看集群上的所有 Secret 的访问权限, 本质上该组件便成为了集群的管理员。 当有疑问时,如果可能的话,将要集成的组件限制在某指定名字空间中运行。

如果执行 Pod 创建操作的组件能够在 kube-system 这类名字空间中创建 Pod, 则这类组件也可能获得意外的权限,因为这些 Pod 可以访问服务账户的 Secret, 或者,如果对应服务帐户被授权访问宽松的 PodSecurityPolicy, 它们就能以较高的权限运行。

如果你使用 Pod 安全准入, 并且允许任何组件在一个允许执行特权 Pod 的名字空间中创建 Pod,这些 Pod 就可能从所在的容器中逃逸,利用被拓宽的访问权限来实现特权提升。

你不应该允许不可信的组件在任何系统名字空间(名字以 kube- 开头)中创建 Pod, 也不允许它们在访问权限授权可被利用来提升特权的名字空间中创建 Pod。

对 Secret 进行静态加密

一般情况下,etcd 数据库包含了通过 Kubernetes API 可以访问到的所有信息, 并且可能为攻击者提供对你的集群的状态的较多的可见性。 你要始终使用经过充分审查的备份和加密方案来加密备份数据, 并考虑在可能的情况下使用全盘加密。

对于 Kubernetes API 中的信息,Kubernetes 支持可选的静态数据加密。 这让你可以确保当 Kubernetes 存储对象(例如 SecretConfigMap)的数据时,API 服务器写入的是加密的对象。 这种加密意味着即使有权访问 etcd 备份数据的某些人也无法查看这些对象的内容。 在 Kubernetes 1.30 中,你也可以加密自定义资源; 针对以 CustomResourceDefinition 形式定义的扩展 API,对其执行静态加密的能力作为 v1.26 版本的一部分已添加到 Kubernetes。

接收安全更新和报告漏洞的警报

请加入 kubernetes-announce 组,这样你就能够收到有关安全公告的邮件。有关如何报告漏洞的更多信息, 请参见安全报告页面。

接下来

4.2.34 - 通过配置文件设置 kubelet 参数

通过保存在硬盘的配置文件设置 kubelet 的部分配置参数,这可以作为命令行参数的替代。

建议通过配置文件的方式提供参数,因为这样可以简化节点部署和配置管理。

创建配置文件

KubeletConfiguration 结构体定义了可以通过文件配置的 kubelet 配置子集,

配置文件必须是这个结构体中参数的 JSON 或 YAML 表现形式。 确保 kubelet 可以读取该文件。

下面是一个 kubelet 配置文件示例:

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
address: "192.168.0.8"
port: 20250
serializeImagePulls: false
evictionHard:
    memory.available:  "100Mi"
    nodefs.available:  "10%"
    nodefs.inodesFree: "5%"
    imagefs.available: "15%"

在此示例中,kubelet 配置为以下设置:

  1. address:kubelet 将在 192.168.0.8 IP 地址上提供服务。
  2. port:kubelet 将在 20250 端口上提供服务。
  3. serializeImagePulls:并行拉取镜像。
  4. evictionHard:kubelet 将在以下情况之一驱逐 Pod:
    • 当节点的可用内存降至 100MiB 以下时。
    • 当节点主文件系统的已使用 inode 超过 95%。
    • 当镜像文件系统的可用空间小于 15% 时。
    • 当节点主文件系统的 inode 超过 95% 正在使用时。

imagefs 是一个可选的文件系统,容器运行时使用它来存储容器镜像和容器可写层。

启动通过配置文件配置的 kubelet 进程

启动 kubelet 需要将 --config 参数设置为 kubelet 配置文件的路径。kubelet 将从此文件加载其配置。

请注意,命令行参数与配置文件有相同的值时,就会覆盖配置文件中的该值。 这有助于确保命令行 API 的向后兼容性。

请注意,kubelet 配置文件中的相对文件路径是相对于 kubelet 配置文件的位置解析的, 而命令行参数中的相对路径是相对于 kubelet 的当前工作目录解析的。

请注意,命令行参数和 kubelet 配置文件的某些默认值不同。 如果设置了 --config,并且没有通过命令行指定值,则 KubeletConfiguration 版本的默认值生效。在上面的例子中,version 是 kubelet.config.k8s.io/v1beta1

kubelet 配置文件的插件目录

自 Kubernetes v1.28.0 起,kubelet 被扩展以支持一个插件配置目录。 该目录的位置可以使用 --config-dir 标志来指定,默认为 "",也就是被禁用状态。

只有在为 kubelet 进程设置环境变量 KUBELET_CONFIG_DROPIN_DIR_ALPHA (该变量的值无关紧要)时才可以设置 --config-dir。对于 Kubernetes v1.30, 如果你未设置该变量而指定了 --config-dir,kubelet 将返回错误并且启动失败。 你不能使用 kubelet 配置文件指定插件配置目录;只能使用 CLI 参数 --config-dir 进行设置。

你可以以类似于 kubelet 配置文件的方式使用 kubelet 配置目录。

例如,你可能想要为所有节点设置一个基准的 kubelet 配置,但你可能想要自定义 address 字段。 可以按如下方式操作:

kubelet 配置文件的主要内容如下:

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
port: 20250
serializeImagePulls: false
evictionHard:
    memory.available:  "200Mi"

--config-dir 目录中某个文件的内容如下:

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
address: "192.168.0.8"

在启动时,kubelet 会合并来自以下几部分的配置:

  • 命令行参数(优先级最低)。
  • kubelet 配置文件。
  • 排序的插件配置文件。
  • 在命令行中指定的特性门控(优先级最高)。

这将产生与之前示例中使用的单个配置文件相同的结果。

接下来

4.2.35 - 通过名字空间共享集群

本页展示如何查看、使用和删除名字空间。 本页同时展示如何使用 Kubernetes 名字空间来划分集群。

准备开始

查看名字空间

列出集群中现有的名字空间:

kubectl get namespaces
NAME              STATUS   AGE
default           Active   11d
kube-node-lease   Active   11d
kube-public       Active   11d
kube-system       Active   11d

初始状态下,Kubernetes 具有四个名字空间:

  • default 无名字空间对象的默认名字空间
  • kube-node-lease 此名字空间保存与每个节点关联的租约(Lease)对象。 节点租约允许 kubelet 发送[心跳](/zh-cn/docs/concepts/architecture/nodes/#heartbeats), 以便控制平面可以检测节点故障。
  • kube-public 自动创建且被所有用户可读的名字空间(包括未经身份认证的)。 此名字空间通常在某些资源在整个集群中可见且可公开读取时被集群使用。 此名字空间的公共方面只是一个约定,而不是一个必要条件。
  • kube-system 由 Kubernetes 系统创建的对象的名字空间

你还可以通过下列命令获取特定名字空间的摘要:

kubectl get namespaces <name>

或用下面的命令获取详细信息:

kubectl describe namespaces <name>
Name:           default
Labels:         <none>
Annotations:    <none>
Status:         Active

No resource quota.

Resource Limits
 Type       Resource    Min Max Default
 ----               --------    --- --- ---
 Container          cpu         -   -   100m

请注意,这些详情同时显示了资源配额(如果存在)以及资源限制区间。

资源配额跟踪并聚合 Namespace 中资源的使用情况, 并允许集群运营者定义 Namespace 可能消耗的 Hard 资源使用限制。

限制区间定义了单个实体在一个 Namespace 中可使用的最小/最大资源量约束。

参阅准入控制:限制区间

名字空间可以处于下列两个阶段中的一个:

  • Active 名字空间正在被使用中
  • Terminating 名字空间正在被删除,且不能被用于新对象。

更多细节,参阅 API 参考中的名字空间

创建名字空间

新建一个名为 my-namespace.yaml 的 YAML 文件,并写入下列内容:

apiVersion: v1
kind: Namespace
metadata:
  name: <insert-namespace-name-here>

然后运行:

kubectl create -f ./my-namespace.yaml

或者,你可以使用下面的命令创建名字空间:

kubectl create namespace <insert-namespace-name-here>

请注意,名字空间的名称必须是一个合法的 DNS 标签

可选字段 finalizers 允许观察者们在名字空间被删除时清除资源。 记住如果指定了一个不存在的终结器,名字空间仍会被创建, 但如果用户试图删除它,它将陷入 Terminating 状态。

更多有关 finalizers 的信息请查阅 设计文档中名字空间部分。

删除名字空间

删除名字空间使用命令:

kubectl delete namespaces <insert-some-namespace-name>

删除是异步的,所以有一段时间你会看到名字空间处于 Terminating 状态。

使用 Kubernetes 名字空间细分你的集群

默认情况下,Kubernetes 集群会在配置集群时实例化一个 default 名字空间,用以存放集群所使用的默认 Pod、Service 和 Deployment 集合。

假设你有一个新的集群,你可以通过执行以下操作来内省可用的名字空间:

kubectl get namespaces
NAME      STATUS    AGE
default   Active    13m

创建新的名字空间

在本练习中,我们将创建两个额外的 Kubernetes 名字空间来保存我们的内容。

在某组织使用共享的 Kubernetes 集群进行开发和生产的场景中:

  • 开发团队希望在集群中维护一个空间,以便他们可以查看用于构建和运行其应用程序的 Pod、Service 和 Deployment 列表。在这个空间里,Kubernetes 资源被自由地加入或移除, 对谁能够或不能修改资源的限制被放宽,以实现敏捷开发。
  • 运维团队希望在集群中维护一个空间,以便他们可以强制实施一些严格的规程, 对谁可以或不可以操作运行生产站点的 Pod、Service 和 Deployment 集合进行控制。

该组织可以遵循的一种模式是将 Kubernetes 集群划分为两个名字空间:developmentproduction。 让我们创建两个新的名字空间来保存我们的工作。

使用 kubectl 创建 development 名字空间。

kubectl create -f https://k8s.io/examples/admin/namespace-dev.json

让我们使用 kubectl 创建 production 名字空间。

kubectl create -f https://k8s.io/examples/admin/namespace-prod.json

为了确保一切正常,列出集群中的所有名字空间。

kubectl get namespaces --show-labels
NAME          STATUS    AGE       LABELS
default       Active    32m       <none>
development   Active    29s       name=development
production    Active    23s       name=production

在每个名字空间中创建 Pod

Kubernetes 名字空间为集群中的 Pod、Service 和 Deployment 提供了作用域。 与一个名字空间交互的用户不会看到另一个名字空间中的内容。 为了演示这一点,让我们在 development 名字空间中启动一个简单的 Deployment 和 Pod。

kubectl create deployment snowflake \
  --image=registry.k8s.io/serve_hostname \
  -n=development --replicas=2

我们创建了一个副本个数为 2 的 Deployment,运行名为 snowflake 的 Pod,其中包含一个负责提供主机名的基本容器。

kubectl get deployment -n=development
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
snowflake    2/2     2            2           2m
kubectl get pods -l app=snowflake -n=development
NAME                         READY     STATUS    RESTARTS   AGE
snowflake-3968820950-9dgr8   1/1       Running   0          2m
snowflake-3968820950-vgc4n   1/1       Running   0          2m

看起来还不错,开发人员能够做他们想做的事,而且他们不必担心会影响到 production 名字空间下面的内容。

让我们切换到 production 名字空间, 展示一下一个名字空间中的资源是如何对另一个名字空间隐藏的。 名字空间 production 应该是空的,下面的命令应该不会返回任何东西。

kubectl get deployment -n=production
kubectl get pods -n=production

生产环境下一般以养牛的方式运行负载,所以让我们创建一些 Cattle(牛)Pod。

kubectl create deployment cattle --image=registry.k8s.io/serve_hostname -n=production
kubectl scale deployment cattle --replicas=5 -n=production

kubectl get deployment -n=production
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
cattle       5/5     5            5           10s
kubectl get pods -l app=cattle -n=production
NAME                      READY     STATUS    RESTARTS   AGE
cattle-2263376956-41xy6   1/1       Running   0          34s
cattle-2263376956-kw466   1/1       Running   0          34s
cattle-2263376956-n4v97   1/1       Running   0          34s
cattle-2263376956-p5p3i   1/1       Running   0          34s
cattle-2263376956-sxpth   1/1       Running   0          34s

此时,应该很清楚地展示了用户在一个名字空间中创建的资源对另一个名字空间是隐藏的。

随着 Kubernetes 中的策略支持的发展,我们将扩展此场景,以展示如何为每个名字空间提供不同的授权规则。

理解使用名字空间的动机

单个集群应该能满足多个用户及用户组的需求(以下称为 “用户社区”)。

Kubernetes 名字空间 帮助不同的项目、团队或客户去共享 Kubernetes 集群。

名字空间通过以下方式实现这点:

  1. 名字设置作用域.
  2. 为集群中的部分资源关联鉴权和策略的机制。

使用多个名字空间是可选的。

每个用户社区都希望能够与其他社区隔离开展工作。 每个用户社区都有自己的:

  1. 资源(Pod、服务、副本控制器等等)
  2. 策略(谁能或不能在他们的社区里执行操作)
  3. 约束(该社区允许多少配额等等)

集群运营者可以为每个唯一用户社区创建名字空间。

名字空间为下列内容提供唯一的作用域:

  1. 命名资源(避免基本的命名冲突)
  2. 将管理权限委派给可信用户
  3. 限制社区资源消耗的能力

用例包括:

  1. 作为集群运营者, 我希望能在单个集群上支持多个用户社区。
  2. 作为集群运营者,我希望将集群分区的权限委派给这些社区中的受信任用户。
  3. 作为集群运营者,我希望能限定每个用户社区可使用的资源量,以限制对使用同一集群的其他用户社区的影响。
  4. 作为集群用户,我希望与我的用户社区相关的资源进行交互,而与其他用户社区在该集群上执行的操作无关。

理解名字空间和 DNS

当你创建服务时,Kubernetes 会创建相应的 DNS 条目。 此条目的格式为 <服务名称>.<名字空间名称>.svc.cluster.local。 这意味着如果容器使用 <服务名称>,它将解析为名字空间本地的服务。 这对于在多个名字空间(如开发、暂存和生产)中使用相同的配置非常有用。 如果要跨名字空间访问,则需要使用完全限定的域名(FQDN)。

接下来

4.2.36 - 升级集群

本页概述升级 Kubernetes 集群的步骤。

升级集群的方式取决于你最初部署它的方式、以及后续更改它的方式。

从高层规划的角度看,要执行的步骤是:

  • 升级控制平面
  • 升级集群中的节点
  • 升级 kubectl 之类的客户端
  • 根据新 Kubernetes 版本带来的 API 变化,调整清单文件和其他资源

准备开始

你必须有一个集群。 本页内容涉及从 Kubernetes 1.29 升级到 Kubernetes 1.30。 如果你的集群未运行 Kubernetes 1.29, 那请参考目标 Kubernetes 版本的文档。

升级方法

kubeadm

如果你的集群是使用 kubeadm 安装工具部署而来, 那么升级集群的详细信息,请参阅升级 kubeadm 集群

升级集群之后,要记得安装最新版本的 kubectl

手动部署

你应该按照下面的操作顺序,手动更新控制平面:

  • etcd (所有实例)
  • kube-apiserver (所有控制平面的宿主机)
  • kube-controller-manager
  • kube-scheduler
  • cloud controller manager (在你用到时)

现在,你应该安装最新版本的 kubectl

对于集群中的每个节点, 首先需要腾空节点, 然后使用一个运行了 kubelet 1.30 版本的新节点替换它; 或者升级此节点的 kubelet,并使节点恢复服务。

其他部署方式

参阅你的集群部署工具对应的文档,了解用于维护的推荐设置步骤。

升级后的任务

切换集群的存储 API 版本

对象序列化到 etcd,是为了提供集群中活动 Kubernetes 资源的内部表示法, 这些对象都使用特定版本的 API 编写。

当底层的 API 更改时,这些对象可能需要用新 API 重写。 如果不能做到这一点,会导致再也不能用 Kubernetes API 服务器解码、使用该对象。

对于每个受影响的对象,请使用最新支持的 API 读取它,然后使用所支持的最新 API 将其写回。

更新清单

升级到新版本 Kubernetes 就可以获取到新的 API。

你可以使用 kubectl convert 命令在不同 API 版本之间转换清单。 例如:

kubectl convert -f pod.yaml --output-version v1

kubectl 替换了 pod.yaml 的内容, 在新的清单文件中,kind 被设置为 Pod(未变), 但 apiVersion 则被修订了。

设备插件

如果你的集群正在运行设备插件(Device Plugin)并且节点需要升级到具有更新的设备插件(Device Plugin) API 版本的 Kubernetes 版本,则必须在升级节点之前升级设备插件以同时支持这两个插件 API 版本, 以确保升级过程中设备分配能够继续成功完成。

有关详细信息,请参阅 API 兼容性kubelet 设备管理器 API 版本

4.2.37 - 在集群中使用级联删除

本页面向你展示如何设置在你的集群执行垃圾收集 时要使用的级联删除 类型。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你还需要创建一个 Deployment 示例 以试验不同类型的级联删除。你需要为每种级联删除类型来重建 Deployment。

检查 Pod 上的属主引用

检查确认你的 Pods 上存在 ownerReferences 字段:

kubectl get pods -l app=nginx --output=yaml

输出中包含 ownerReferences 字段,类似这样:

apiVersion: v1
    ...
    ownerReferences:
    - apiVersion: apps/v1
      blockOwnerDeletion: true
      controller: true
      kind: ReplicaSet
      name: nginx-deployment-6b474476c4
      uid: 4fdcd81c-bd5d-41f7-97af-3a3b759af9a7
    ...

使用前台级联删除

默认情况下,Kubernetes 使用后台级联删除 以删除依赖某对象的其他对象。取决于你的集群所运行的 Kubernetes 版本, 你可以使用 kubectl 或者 Kubernetes API 来切换到前台级联删除。 要获知版本信息,请输入 kubectl version.

你可以使用 kubectl 或者 Kubernetes API 来基于前台级联删除来删除对象。

使用 kubectl

运行下面的命令:

kubectl delete deployment nginx-deployment --cascade=foreground

使用 Kubernetes API

  1. 启动一个本地代理会话:

    kubectl proxy --port=8080
    
  1. 使用 curl 来触发删除操作:

    curl -X DELETE localhost:8080/apis/apps/v1/namespaces/default/deployments/nginx-deployment \
        -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Foreground"}' \
        -H "Content-Type: application/json"
    

    输出中包含 foregroundDeletion finalizer, 类似这样:

    "kind": "Deployment",
    "apiVersion": "apps/v1",
    "metadata": {
        "name": "nginx-deployment",
        "namespace": "default",
        "uid": "d1ce1b02-cae8-4288-8a53-30e84d8fa505",
        "resourceVersion": "1363097",
        "creationTimestamp": "2021-07-08T20:24:37Z",
        "deletionTimestamp": "2021-07-08T20:27:39Z",
        "finalizers": [
          "foregroundDeletion"
        ]
        ...
    

使用后台级联删除

  1. 创建一个 Deployment 示例
  2. 基于你的集群所运行的 Kubernetes 版本,使用 kubectl 或者 Kubernetes API 来删除 Deployment。 要获知版本信息,请输入 kubectl version.

你可以使用 kubectl 或者 Kubernetes API 来执行后台级联删除方式的对象删除操作。

Kubernetes 默认采用后台级联删除方式,如果你在运行下面的命令时不指定 --cascade 标志或者 propagationPolicy 参数时,用这种方式来删除对象。

使用 kubectl

运行下面的命令:

kubectl delete deployment nginx-deployment --cascade=background

使用 Kubernetes API

  1. 启动一个本地代理会话:

    kubectl proxy --port=8080
    
  1. 使用 curl 来触发删除操作:

    curl -X DELETE localhost:8080/apis/apps/v1/namespaces/default/deployments/nginx-deployment \
        -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Background"}' \
        -H "Content-Type: application/json"
    

    输出类似于:

    "kind": "Status",
    "apiVersion": "v1",
    ...
    "status": "Success",
    "details": {
        "name": "nginx-deployment",
        "group": "apps",
        "kind": "deployments",
        "uid": "cc9eefb9-2d49-4445-b1c1-d261c9396456"
    }
    

删除属主对象和孤立的依赖对象

默认情况下,当你告诉 Kubernetes 删除某个对象时, 控制器 也会删除依赖该对象 的其他对象。 取决于你的集群所运行的 Kubernetes 版本,你也可以使用 kubectl 或者 Kubernetes API 来让 Kubernetes 孤立 这些依赖对象。 要获知版本信息,请输入 kubectl version.

使用 kubectl

运行下面的命令:

kubectl delete deployment nginx-deployment --cascade=orphan

使用 Kubernetes API

  1. 启动一个本地代理会话:

    kubectl proxy --port=8080
    
  1. 使用 curl 来触发删除操作:

    curl -X DELETE localhost:8080/apis/apps/v1/namespaces/default/deployments/nginx-deployment \
        -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Orphan"}' \
        -H "Content-Type: application/json"
    

    输出中在 finalizers 字段中包含 orphan,如下所示:

    "kind": "Deployment",
    "apiVersion": "apps/v1",
    "namespace": "default",
    "uid": "6f577034-42a0-479d-be21-78018c466f1f",
    "creationTimestamp": "2021-07-09T16:46:37Z",
    "deletionTimestamp": "2021-07-09T16:47:08Z",
    "deletionGracePeriodSeconds": 0,
    "finalizers": [
      "orphan"
    ],
    ...
    

你可以检查 Deployment 所管理的 Pods 仍然处于运行状态:

kubectl get pods -l app=nginx

接下来

4.2.38 - 使用 KMS 驱动进行数据加密

本页展示了如何配置密钥管理服务(Key Management Service,KMS)驱动和插件以启用 Secret 数据加密。 在 Kubernetes 1.30 中,存在两个版本的 KMS 静态加密方式。 如果可行的话,建议使用 KMS v2,因为(自 Kubernetes v1.28 起)KMS v1 已经被弃用并 (自 Kubernetes v1.29 起)默认被禁用。 KMS v2 提供了比 KMS v1 明显更好的性能特征。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你所需要的 Kubernetes 版本取决于你已选择的 KMS API 版本。Kubernetes 推荐使用 KMS v2。

  • 如果你选择了 KMS API v2,则应使用 Kubernetes v1.30 (如果你正在运行也支持 KMS API v2 的其他 Kubernetes 版本,需查阅该 Kubernetes 版本的文档)。
  • 如果你选择了 KMS API v1 来支持早于 v1.27 版本的集群,或者你有一个仅支持 KMS v1 的旧版 KMS 插件, 那么任何受支持的 Kubernetes 版本都可以良好工作。此 API 自 Kubernetes v1.28 起被弃用。 Kubernetes 不推荐使用此 API。
要获知版本信息,请输入 kubectl version.

KMS v1

特性状态: Kubernetes v1.28 [deprecated]
  • 需要 Kubernetes 1.10.0 或更高版本
  • 对于 1.29 及更高版本,KMS 的 v1 实现默认处于禁用状态。 要启用此特性,设置 --feature-gates=KMSv1=true 以配置 KMS v1 驱动。
  • 你的集群必须使用 etcd v3 或更高版本

KMS v2

特性状态: Kubernetes v1.29 [stable]
  • 你的集群必须使用 etcd v3 或更高版本

KMS 加密和为每个对象加密的密钥

KMS 加密驱动使用封套加密模型来加密 etcd 中的数据。数据使用数据加密密钥(DEK)加密。 这些 DEK 经一个密钥加密密钥(KEK)加密后在一个远端的 KMS 中存储和管理。

如果你使用(已弃用的)KMS v1 实现,每次加密将生成新的 DEK。

对于 KMS v2,每次加密将生成新的 DEK: API 服务器使用密钥派生函数根据秘密的种子数结合一些随机数据生成一次性的数据加密密钥。 种子会在 KEK 轮换时进行轮换 (有关更多详细信息,请参阅下面的“了解 key_id 和密钥轮换”章节)。

KMS 驱动使用 gRPC 通过 UNIX 域套接字与一个特定的 KMS 插件通信。 这个 KMS 插件作为一个 gRPC 服务器被部署在 Kubernetes 控制平面的相同主机上,负责与远端 KMS 的通信。

配置 KMS 驱动

为了在 API 服务器上配置 KMS 驱动,在加密配置文件中的 providers 数组中加入一个类型为 kms 的驱动,并设置下列属性:

KMS v1

  • apiVersion:针对 KMS 驱动的 API 版本。此项留空或设为 v1
  • name:KMS 插件的显示名称。一旦设置,就无法更改。
  • endpoint:gRPC 服务器(KMS 插件)的监听地址。该端点是一个 UNIX 域套接字。
  • cachesize:以明文缓存的数据加密密钥(DEK)的数量。一旦被缓存, 就可以直接使用 DEK 而无需另外调用 KMS;而未被缓存的 DEK 需要调用一次 KMS 才能解包。
  • timeout:在返回一个错误之前,kube-apiserver 等待 kms-plugin 响应的时间(默认是 3 秒)。

KMS v2

  • apiVersion:针对 KMS 驱动的 API 版本。此项设为 v2
  • name:KMS 插件的显示名称。一旦设置,就无法更改。
  • endpoint:gRPC 服务器(KMS 插件)的监听地址。该端点是一个 UNIX 域套接字。
  • timeout:在返回一个错误之前,kube-apiserver 等待 kms-plugin 响应的时间(默认是 3 秒)。

KMS v2 不支持 cachesize 属性。一旦服务器通过调用 KMS 解密了数据加密密钥(DEK), 所有的 DEK 将会以明文形式被缓存。一旦被缓存,DEK 可以无限期地用于解密操作,而无需再次调用 KMS。

参见理解静态配置加密

实现 KMS 插件

为实现一个 KMS 插件,你可以开发一个新的插件 gRPC 服务器或启用一个由你的云服务驱动提供的 KMS 插件。 你可以将这个插件与远程 KMS 集成,并把它部署到 Kubernetes 控制平面上。

启用由云服务驱动支持的 KMS

有关启用云服务驱动特定的 KMS 插件的说明,请咨询你的云服务驱动商。

开发 KMS 插件 gRPC 服务器

你可以使用 Go 语言的存根文件开发 KMS 插件 gRPC 服务器。 对于其他语言,你可以用 proto 文件创建可以用于开发 gRPC 服务器代码的存根文件。

KMS v1

  • 使用 Go:使用存根文件 api.pb.go 中的函数和数据结构开发 gRPC 服务器代码。
  • 使用 Go 以外的其他语言:用 protoc 编译器编译 proto 文件: api.proto 为指定语言生成存根文件。

KMS v2

  • 使用 Go:提供了一个高级简化这个过程。 底层实现可以使用存根文件 api.pb.go 中的函数和数据结构开发 gRPC 服务器代码。

  • 使用 Go 以外的其他语言:用 protoc 编译器编译 proto 文件: api.proto 为指定语言生成存根文件。

然后使用存根文件中的函数和数据结构开发服务器代码。

注意

KMS v1
  • kms 插件版本:v1beta1

    作为对过程调用 Version 的响应,兼容的 KMS 插件应把 v1beta1 作为 VersionResponse.version 版本返回。

  • 消息版本:v1beta1

    所有来自 KMS 驱动的消息都把 version 字段设置为 v1beta1

  • 协议:UNIX 域套接字 (unix)

    该插件被实现为一个在 UNIX 域套接字上侦听的 gRPC 服务器。 该插件部署时应在文件系统上创建一个文件来运行 gRPC UNIX 域套接字连接。 API 服务器(gRPC 客户端)配置了 KMS 驱动(gRPC 服务器)UNIX 域套接字端点,以便与其通信。 通过以 /@ 开头的端点,可以使用一个抽象的 Linux 套接字,即 unix:///@foo。 使用这种类型的套接字时必须小心,因为它们没有 ACL 的概念(与传统的基于文件的套接字不同)。 然而,这些套接字遵从 Linux 网络命名空间约束,因此只能由同一 Pod 中的容器进行访问,除非使用了主机网络。

KMS v2
  • KMS 插件版本:v2

    作为对过程调用 Status 的响应,兼容的 KMS 插件应把 StatusResponse.version 作为其 KMS 兼容版本返回。 该状态响应还应包括 "ok" 作为 StatusResponse.healthz 以及 key_id(远程 KMS KEK ID)作为 StatusResponse.key_id。Kubernetes 项目建议你的插件与稳定的 v2 KMS API 兼容。 Kubernetes 1.30 针对 KMS 还支持 v2beta1 API; Kubernetes 后续版本可能会继续支持该 Beta 版本。

    当一切健康时,API 服务器大约每分钟轮询一次 Status 过程调用, 而插件不健康时每 10 秒钟轮询一次。使用这些插件时要注意优化此调用,因为此调用将经受持续的负载。

  • 加密

    EncryptRequest 过程调用提供明文和一个 UID 以用于日志记录。 响应必须包括密文、使用的 KEK 的 key_id,以及可选的任意元数据,这些元数据可以 帮助 KMS 插件在未来的 DecryptRequest 调用中(通过 annotations 字段)进行解密。 插件必须保证所有不同的明文都会产生不同的响应 (ciphertext, key_id, annotations)

    如果插件返回一个非空的 annotations 映射,则所有映射键必须是完全限定域名, 例如 example.comannotation 的一个示例用例是 {"kms.example.io/remote-kms-auditid":"<远程 KMS 使用的审计 ID>"}

    当 API 服务器运行正常时,并不会高频执行 EncryptRequest 过程调用。 插件实现仍应力求使每个请求的延迟保持在 100 毫秒以下。

  • 解密

    DecryptRequest 过程调用提供 EncryptRequest 中的 (ciphertext, key_id, annotations) 和一个 UID 以用于日志记录。正如预期的那样,它是 EncryptRequest 调用的反向操作。插件必须验证 key_id 是否为其理解的密钥ID - 除非这些插件确定数据是之前自己加密的,否则不应尝试解密。

    在启动时,API 服务器可能会执行数千个 DecryptRequest 过程调用以填充其监视缓存。 因此,插件实现必须尽快执行这些调用,并应力求使每个请求的延迟保持在 10 毫秒以下。

  • 理解 key_id 和密钥轮换

    key_id 是目前使用的远程 KMS KEK 的公共、非机密名称。 它可能会在 API 服务器的常规操作期间记录,因此不得包含任何私有数据。 建议插件实现使用哈希来避免泄漏任何数据。 KMS v2 指标负责在通过 /metrics 端点公开之前对此值进行哈希。

    API 服务器认为从 Status 过程调用返回的 key_id 是权威性的。因此,此值的更改表示远程 KEK 已更改, 并且使用旧 KEK 加密的数据应在执行无操作写入时标记为过期(如下所述)。如果 EncryptRequest 过程调用返回与 Status 不同的 key_id,则响应将被丢弃,并且插件将被认为是不健康的。 因此,插件实现必须保证从 Status 返回的 key_idEncryptRequest 返回的 key_id 相同。 此外,插件必须确保 key_id 是稳定的,并且不会在不同值之间翻转(即在远程 KEK 轮换期间)。

    插件不能重新使用 key_id,即使在先前使用的远程 KEK 被恢复的情况下也是如此。 例如,如果插件使用了 key_id=A,切换到 key_id=B,然后又回到 key_id=A, 那么插件应报告 key_id=A_001 或使用一个新值,如 key_id=C

    由于 API 服务器大约每分钟轮询一次 Status,因此 key_id 轮换并不立即发生。 此外,API 服务器在三分钟内以最近一个有效状态为准。因此, 如果用户想采取被动方法进行存储迁移(即等待),则必须安排迁移在远程 KEK 轮换后的 3 + N + M 分钟内发生 (其中 N 表示插件观察 key_id 更改所需的时间,M 是允许处理配置更改的缓冲区时间 - 建议至少使用 5 分钟)。 请注意,执行 KEK 轮换不需要进行 API 服务器重启。

  • 协议:UNIX 域套接字 (unix)

    该插件被实现为一个在 UNIX 域套接字上侦听的 gRPC 服务器。 该插件部署时应在文件系统上创建一个文件来运行 gRPC UNIX 域套接字连接。 API 服务器(gRPC 客户端)配置了 KMS 驱动(gRPC 服务器)UNIX 域套接字端点,以便与其通信。 通过以 /@ 开头的端点,可以使用一个抽象的 Linux 套接字,即 unix:///@foo。 使用这种类型的套接字时必须小心,因为它们没有 ACL 的概念(与传统的基于文件的套接字不同)。 然而,这些套接字遵从 Linux 网络命名空间,因此只能由同一 Pod 中的容器进行访问,除非使用了主机网络。

将 KMS 插件与远程 KMS 整合

KMS 插件可以用任何受 KMS 支持的协议与远程 KMS 通信。 所有的配置数据,包括 KMS 插件用于与远程 KMS 通信的认证凭据,都由 KMS 插件独立地存储和管理。 KMS 插件可以用额外的元数据对密文进行编码,这些元数据是在把它发往 KMS 进行解密之前可能要用到的 (KMS v2 提供了专用的 annotations 字段简化了这个过程)。

部署 KMS 插件

确保 KMS 插件与 Kubernetes API 服务器运行在同一主机上。

使用 KMS 驱动加密数据

为了加密数据:

  1. 使用适合于 kms 驱动的属性创建一个新的 EncryptionConfiguration 文件,以加密 Secret 和 ConfigMap 等资源。 如果要加密使用 CustomResourceDefinition 定义的扩展 API,你的集群必须运行 Kubernetes v1.26 或更高版本。

  2. 设置 kube-apiserver 的 --encryption-provider-config 参数指向配置文件的位置。

  1. --encryption-provider-config-automatic-reload 布尔参数决定了磁盘内容发生变化时是否应自动重新加载 通过 --encryption-provider-config 设置的文件。这样可以在不重启 API 服务器的情况下进行密钥轮换。

  2. 重启你的 API 服务器。

KMS v1

apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
      - configmaps
      - pandas.awesome.bears.example
    providers:
      - kms:
          name: myKmsPluginFoo
          endpoint: unix:///tmp/socketfile.sock
          cachesize: 100
          timeout: 3s
      - kms:
          name: myKmsPluginBar
          endpoint: unix:///tmp/socketfile.sock
          cachesize: 100
          timeout: 3s

KMS v2

apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
      - configmaps
      - pandas.awesome.bears.example
    providers:
      - kms:
          apiVersion: v2
          name: myKmsPluginFoo
          endpoint: unix:///tmp/socketfile.sock
          timeout: 3s
      - kms:
          apiVersion: v2
          name: myKmsPluginBar
          endpoint: unix:///tmp/socketfile.sock
          timeout: 3s

--encryption-provider-config-automatic-reload 设置为 true 会将所有健康检查集中到同一个健康检查端点。 只有 KMS v1 驱动正使用且加密配置未被自动重新加载时,才能进行独立的健康检查。

下表总结了每个 KMS 版本的健康检查端点:

KMS 配置没有自动重新加载有自动重新加载
仅 KMS v1Individual HealthchecksSingle Healthcheck
仅 KMS v2Single HealthcheckSingle Healthcheck
KMS v1 和 v2Individual HealthchecksSingle Healthcheck
没有 KMSSingle Healthcheck

Single Healthcheck 意味着唯一的健康检查端点是 /healthz/kms-providers

Individual Healthchecks 意味着每个 KMS 插件都有一个对应的健康检查端点, 并且这一端点基于插件在加密配置中的位置确定,例如 /healthz/kms-provider-0/healthz/kms-provider-1 等。

这些健康检查端点路径是由服务器硬编码、生成并控制的。 Individual Healthchecks 的索引序号对应于 KMS 加密配置被处理的顺序。

在执行确保所有 Secret 都加密中所给步骤之前, providers 列表应以 identity: {} 提供程序作为结尾,以允许读取未加密的数据。 加密所有资源后,应移除 identity 提供程序,以防止 API 服务器接受未加密的数据。

有关 EncryptionConfiguration 格式的更多详细信息,请参阅 kube-apiserver 加密 API 参考(v1)

验证数据已经加密

当静态加密被正确配置时,资源将在写入时被加密。 重启 kube-apiserver 后,所有新建或更新的 Secret 或在 EncryptionConfiguration 中配置的其他资源类型在存储时应该已被加密。 要验证这点,你可以用 etcdctl 命令行程序获取私密数据的内容。

  1. 在默认的命名空间里创建一个名为 secret1 的 Secret:

    kubectl create secret generic secret1 -n default --from-literal=mykey=mydata
    
  1. etcdctl 命令行,从 etcd 读取出 Secret:

    ETCDCTL_API=3 etcdctl get /kubernetes.io/secrets/default/secret1 [...] | hexdump -C
    

    其中 [...] 包含连接 etcd 服务器的额外参数。

  1. 验证对于 KMS v1,保存的 Secret 以 k8s:enc:kms:v1: 开头, 对于 KMS v2,保存的 Secret 以 k8s:enc:kms:v2: 开头,这表明 kms 驱动已经对结果数据加密。
  1. 验证通过 API 获取的 Secret 已被正确解密:

    kubectl describe secret secret1 -n default
    

    Secret 应包含 mykey: mydata

确保所有 Secret 都已被加密

当静态加密被正确配置时,资源将在写入时被加密。 这样我们可以执行就地零干预更新来确保数据被加密。

下列命令读取所有 Secret 并更新它们以便应用服务器端加密。如果因为写入冲突导致错误发生, 请重试此命令。对较大的集群,你可能希望根据命名空间或脚本更新去细分 Secret 内容。

kubectl get secrets --all-namespaces -o json | kubectl replace -f -

从本地加密驱动切换到 KMS 驱动

为了从本地加密驱动切换到 kms 驱动并重新加密所有 Secret 内容:

  1. 在配置文件中加入 kms 驱动作为第一个条目,如下列样例所示

    apiVersion: apiserver.config.k8s.io/v1
    kind: EncryptionConfiguration
    resources:
      - resources:
          - secrets
        providers:
          - kms:
              apiVersion: v2
              name : myKmsPlugin
              endpoint: unix:///tmp/socketfile.sock
          - aescbc:
              keys:
                - name: key1
                  secret: <BASE 64 ENCODED SECRET>
    
  1. 重启所有 kube-apiserver 进程。

  2. 运行下列命令使用 kms 驱动强制重新加密所有 Secret。

    kubectl get secrets --all-namespaces -o json | kubectl replace -f -
    

接下来

如果你不想再对 Kubernetes API 中保存的数据加密, 请阅读解密已静态存储的数据

4.2.39 - 使用 CoreDNS 进行服务发现

此页面介绍了 CoreDNS 升级过程以及如何安装 CoreDNS 而不是 kube-dns。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你的 Kubernetes 服务器版本必须不低于版本 v1.9. 要获知版本信息,请输入 kubectl version.

关于 CoreDNS

CoreDNS 是一个灵活可扩展的 DNS 服务器,可以作为 Kubernetes 集群 DNS。 与 Kubernetes 一样,CoreDNS 项目由 CNCF 托管。

通过替换现有集群部署中的 kube-dns,或者使用 kubeadm 等工具来为你部署和升级集群, 可以在你的集群中使用 CoreDNS 而非 kube-dns。

安装 CoreDNS

有关手动部署或替换 kube-dns,请参阅 CoreDNS 网站

迁移到 CoreDNS

使用 kubeadm 升级现有集群

在 Kubernetes 1.21 版本中,kubeadm 移除了对将 kube-dns 作为 DNS 应用的支持。 对于 kubeadm v1.30,所支持的唯一的集群 DNS 应用是 CoreDNS。

当你使用 kubeadm 升级使用 kube-dns 的集群时,你还可以执行到 CoreDNS 的迁移。 在这种场景中,kubeadm 将基于 kube-dns ConfigMap 生成 CoreDNS 配置("Corefile"), 保存存根域和上游名称服务器的配置。

升级 CoreDNS

你可以在 Kubernetes 中的 CoreDNS 版本 页面查看 kubeadm 为不同版本 Kubernetes 所安装的 CoreDNS 版本。

如果你只想升级 CoreDNS 或使用自己的定制镜像,也可以手动升级 CoreDNS。 参看指南和演练 文档了解如何平滑升级。 在升级你的集群过程中,请确保现有 CoreDNS 的配置("Corefile")被保留下来。

如果使用 kubeadm 工具来升级集群,则 kubeadm 可以自动处理保留现有 CoreDNS 配置这一事项。

CoreDNS 调优

当资源利用方面有问题时,优化 CoreDNS 的配置可能是有用的。 有关详细信息,请参阅有关扩缩 CoreDNS 的文档

接下来

你可以通过修改 CoreDNS 的配置("Corefile")来配置 CoreDNS, 以支持比 kube-dns 更多的用例。 请参考 kubernetes CoreDNS 插件的文档 或者 CoreDNS 博客上的博文 Kubernetes 的自定义 DNS 条目, 以了解更多信息。

4.2.40 - 在 Kubernetes 集群中使用 NodeLocal DNSCache

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

本页概述了 Kubernetes 中的 NodeLocal DNSCache 功能。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

引言

NodeLocal DNSCache 通过在集群节点上作为 DaemonSet 运行 DNS 缓存代理来提高集群 DNS 性能。 在当今的体系结构中,运行在 'ClusterFirst' DNS 模式下的 Pod 可以连接到 kube-dns serviceIP 进行 DNS 查询。 通过 kube-proxy 添加的 iptables 规则将其转换为 kube-dns/CoreDNS 端点。 借助这种新架构,Pod 将可以访问在同一节点上运行的 DNS 缓存代理,从而避免 iptables DNAT 规则和连接跟踪。 本地缓存代理将查询 kube-dns 服务以获取集群主机名的缓存缺失(默认为 "cluster.local" 后缀)。

动机

  • 使用当前的 DNS 体系结构,如果没有本地 kube-dns/CoreDNS 实例,则具有最高 DNS QPS 的 Pod 可能必须延伸到另一个节点。 在这种场景下,拥有本地缓存将有助于改善延迟。
  • 跳过 iptables DNAT 和连接跟踪将有助于减少 conntrack 竞争并避免 UDP DNS 条目填满 conntrack 表。
  • 从本地缓存代理到 kube-dns 服务的连接可以升级为 TCP。 TCP conntrack 条目将在连接关闭时被删除,相反 UDP 条目必须超时 (默认 nf_conntrack_udp_timeout 是 30 秒)。
  • 将 DNS 查询从 UDP 升级到 TCP 将减少由于被丢弃的 UDP 包和 DNS 超时而带来的尾部等待时间; 这类延时通常长达 30 秒(3 次重试 + 10 秒超时)。 由于 nodelocal 缓存监听 UDP DNS 查询,应用不需要变更。
  • 在节点级别对 DNS 请求的度量和可见性。
  • 可以重新启用负缓存,从而减少对 kube-dns 服务的查询数量。

架构图

启用 NodeLocal DNSCache 之后,DNS 查询所遵循的路径如下:

NodeLocal DNSCache 流

Nodelocal DNSCache 流

此图显示了 NodeLocal DNSCache 如何处理 DNS 查询。

配置

可以使用以下步骤启动此功能:

  • 根据示例 nodelocaldns.yaml 准备一个清单,把它保存为 nodelocaldns.yaml
  • 如果使用 IPv6,在使用 'IP:Port' 格式的时候需要把 CoreDNS 配置文件里的所有 IPv6 地址用方括号包起来。 如果你使用上述的示例清单, 需要把配置行 L70 修改为: "health [__PILLAR__LOCAL__DNS__]:8080"。
  • 把清单里的变量更改为正确的值:

    kubedns=`kubectl get svc kube-dns -n kube-system -o jsonpath={.spec.clusterIP}`
    domain=<cluster-domain>
    localdns=<node-local-address>
    

    <cluster-domain> 的默认值是 "cluster.local"。<node-local-address> 是 NodeLocal DNSCache 选择的本地侦听 IP 地址。

  • 如果 kube-proxy 运行在 IPTABLES 模式:

    sed -i "s/__PILLAR__LOCAL__DNS__/$localdns/g; s/__PILLAR__DNS__DOMAIN__/$domain/g; s/__PILLAR__DNS__SERVER__/$kubedns/g" nodelocaldns.yaml
    

    node-local-dns Pod 会设置 __PILLAR__CLUSTER__DNS____PILLAR__UPSTREAM__SERVERS__。 在此模式下, node-local-dns Pod 会同时侦听 kube-dns 服务的 IP 地址和 <node-local-address> 的地址,以便 Pod 可以使用其中任何一个 IP 地址来查询 DNS 记录。

  • 如果 kube-proxy 运行在 IPVS 模式:

    sed -i "s/__PILLAR__LOCAL__DNS__/$localdns/g; s/__PILLAR__DNS__DOMAIN__/$domain/g; s/,__PILLAR__DNS__SERVER__//g; s/__PILLAR__CLUSTER__DNS__/$kubedns/g" nodelocaldns.yaml
    

    在此模式下,node-local-dns Pod 只会侦听 <node-local-address> 的地址。 node-local-dns 接口不能绑定 kube-dns 的集群 IP 地址,因为 IPVS 负载均衡使用的接口已经占用了该地址。 node-local-dns Pod 会设置 __PILLAR__UPSTREAM__SERVERS__

  • 运行 kubectl create -f nodelocaldns.yaml

  • 如果 kube-proxy 运行在 IPVS 模式,需要修改 kubelet 的 --cluster-dns 参数 NodeLocal DNSCache 正在侦听的 <node-local-address> 地址。 否则,不需要修改 --cluster-dns 参数,因为 NodeLocal DNSCache 会同时侦听 kube-dns 服务的 IP 地址和 <node-local-address> 的地址。

启用后,node-local-dns Pod 将在每个集群节点上的 kube-system 名字空间中运行。 此 Pod 在缓存模式下运行 CoreDNS, 因此每个节点都可以使用不同插件公开的所有 CoreDNS 指标。

如果要禁用该功能,你可以使用 kubectl delete -f <manifest> 来删除 DaemonSet。 你还应该回滚你对 kubelet 配置所做的所有改动。

StubDomains 和上游服务器配置

node-local-dns Pod 能够自动读取 kube-system 名字空间中 kube-dns ConfigMap 中保存的 StubDomains 和上游服务器信息。ConfigMap 中的内容需要遵从此示例中所给的格式。 node-local-dns ConfigMap 也可被直接修改,使用 Corefile 格式设置 stubDomain 配置。 某些云厂商可能不允许直接修改 node-local-dns ConfigMap 的内容。 在这种情况下,可以更新 kube-dns ConfigMap。

设置内存限制

node-local-dns Pod 使用内存来保存缓存项并处理查询。 由于它们并不监视 Kubernetes 对象变化,集群规模或者 Service/EndpointSlices 的数量都不会直接影响内存用量。内存用量会受到 DNS 查询模式的影响。 根据 CoreDNS 文档,

The default cache size is 10000 entries, which uses about 30 MB when completely filled. (默认的缓存大小是 10000 个表项,当完全填充时会使用约 30 MB 内存)

这一数值是(缓存完全被填充时)每个服务器块的内存用量。 通过设置小一点的缓存大小可以降低内存用量。

并发查询的数量会影响内存需求,因为用来处理查询请求而创建的 Go 协程都需要一定量的内存。 你可以在 forward 插件中使用 max_concurrent 选项设置并发查询数量上限。

如果一个 node-local-dns Pod 尝试使用的内存超出可提供的内存量 (因为系统资源总量的,或者所配置的资源约束)的原因, 操作系统可能会关闭这一 Pod 的容器。 发生这种情况时,被终止的("OOMKilled")容器不会清理其启动期间所添加的定制包过滤规则。 该 node-local-dns 容器应该会被重启(因其作为 DaemonSet 的一部分被管理), 但因上述原因可能每次容器失败时都会导致 DNS 有一小段时间不可用: the packet filtering rules direct DNS queries to a local Pod that is unhealthy (包过滤器规则将 DNS 查询转发到本地某个不健康的 Pod)。

通过不带限制地运行 node-local-dns Pod 并度量其内存用量峰值,你可以为其确定一个合适的内存限制值。 你也可以安装并使用一个运行在 “Recommender Mode(建议者模式)” 的 VerticalPodAutoscaler, 并查看该组件输出的建议信息。

4.2.41 - 在 Kubernetes 集群中使用 sysctl

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

本文档介绍如何通过 sysctl 接口在 Kubernetes 集群中配置和使用内核参数。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

对一些步骤,你需要能够重新配置在你的集群里运行的 kubelet 命令行的选项。

获取 Sysctl 的参数列表

在 Linux 中,管理员可以通过 sysctl 接口修改内核运行时的参数。在 /proc/sys/ 虚拟文件系统下存放许多内核参数。这些参数涉及了多个内核子系统,如:

  • 内核子系统(通常前缀为: kernel.
  • 网络子系统(通常前缀为: net.
  • 虚拟内存子系统(通常前缀为: vm.
  • MDADM 子系统(通常前缀为: dev.
  • 更多子系统请参见内核文档

若要获取完整的参数列表,请执行以下命令:

sudo sysctl -a

安全和非安全的 Sysctl 参数

Kubernetes 将 sysctl 参数分为 安全非安全的安全 的 sysctl 参数除了需要设置恰当的命名空间外,在同一节点上的不同 Pod 之间也必须是 相互隔离的。这意味着 Pod 上设置 安全的 sysctl 参数时:

  • 必须不能影响到节点上的其他 Pod
  • 必须不能损害节点的健康
  • 必须不允许使用超出 Pod 的资源限制的 CPU 或内存资源。

至今为止,大多数 有命名空间的 sysctl 参数不一定被认为是 安全 的。 以下几种 sysctl 参数是 安全的

  • kernel.shm_rmid_forced
  • net.ipv4.ip_local_port_range
  • net.ipv4.tcp_syncookies
  • net.ipv4.ping_group_range(从 Kubernetes 1.18 开始);
  • net.ipv4.ip_unprivileged_port_start(从 Kubernetes 1.22 开始);
  • net.ipv4.ip_local_reserved_ports(从 Kubernetes 1.27 开始,需要 kernel 3.16+);
  • net.ipv4.tcp_keepalive_time(从 Kubernetes 1.29 开始,需要 kernel 4.5+);
  • net.ipv4.tcp_fin_timeout(从 Kubernetes 1.29 开始,需要 kernel 4.6+);
  • net.ipv4.tcp_keepalive_intvl(从 Kubernetes 1.29 开始,需要 kernel 4.5+);
  • net.ipv4.tcp_keepalive_probes(从 Kubernetes 1.29 开始,需要 kernel 4.5+)。

在未来的 Kubernetes 版本中,若 kubelet 支持更好的隔离机制, 则上述列表中将会列出更多 安全的 sysctl 参数。

启用非安全的 Sysctl 参数

所有 安全的 sysctl 参数都默认启用。

所有 非安全的 sysctl 参数都默认禁用,且必须由集群管理员在每个节点上手动开启。 那些设置了不安全 sysctl 参数的 Pod 仍会被调度,但无法正常启动。

参考上述警告,集群管理员只有在一些非常特殊的情况下(如:高可用或实时应用调整), 才可以启用特定的 非安全的 sysctl 参数。 如需启用 非安全的 sysctl 参数,请你在每个节点上分别设置 kubelet 命令行参数,例如:

kubelet --allowed-unsafe-sysctls \
  'kernel.msg*,net.core.somaxconn' ...

如果你使用 Minikube,可以通过 extra-config 参数来配置:

minikube start --extra-config="kubelet.allowed-unsafe-sysctls=kernel.msg*,net.core.somaxconn"...

只有 有命名空间的 sysctl 参数可以通过该方式启用。

设置 Pod 的 Sysctl 参数

目前,在 Linux 内核中,有许多的 sysctl 参数都是 有命名空间的。 这就意味着可以为节点上的每个 Pod 分别去设置它们的 sysctl 参数。 在 Kubernetes 中,只有那些有命名空间的 sysctl 参数可以通过 Pod 的 securityContext 对其进行配置。

以下列出有命名空间的 sysctl 参数,在未来的 Linux 内核版本中,此列表可能会发生变化。

  • kernel.shm*,
  • kernel.msg*,
  • kernel.sem,
  • fs.mqueue.*,
  • 那些可以在容器网络命名空间中设置的 net.*。但是,也有例外(例如 net.netfilter.nf_conntrack_maxnet.netfilter.nf_conntrack_expect_max 可以在容器网络命名空间中设置,但在 Linux 5.12.2 之前它们是无命名空间的)。

没有命名空间的 sysctl 参数称为 节点级别的 sysctl 参数。 如果需要对其进行设置,则必须在每个节点的操作系统上手动地去配置它们, 或者通过在 DaemonSet 中运行特权模式容器来配置。

可使用 Pod 的 securityContext 来配置有命名空间的 sysctl 参数, securityContext 应用于同一个 Pod 中的所有容器。

此示例中,使用 Pod SecurityContext 来对一个安全的 sysctl 参数 kernel.shm_rmid_forced 以及两个非安全的 sysctl 参数 net.core.somaxconnkernel.msgmax 进行设置。 在 Pod 规约中对 安全的非安全的 sysctl 参数不做区分。

apiVersion: v1
kind: Pod
metadata:
  name: sysctl-example
spec:
  securityContext:
    sysctls:
    - name: kernel.shm_rmid_forced
      value: "0"
    - name: net.core.somaxconn
      value: "1024"
    - name: kernel.msgmax
      value: "65536"
  ...

最佳实践方案是将集群中具有特殊 sysctl 设置的节点视为 有污点的,并且只调度需要使用到特殊 sysctl 设置的 Pod 到这些节点上。建议使用 Kubernetes 的污点和容忍度特性 来实现它。

设置了 非安全的 sysctl 参数的 Pod 在禁用了这两种 非安全的 sysctl 参数配置的节点上启动都会失败。 与 节点级别的 sysctl 一样, 建议开启污点和容忍度特性为节点配置污点以便将 Pod 调度到正确的节点之上。

4.2.42 - 使用 NUMA 感知的内存管理器

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

Kubernetes 内存管理器(Memory Manager)为 Guaranteed QoS 类 的 Pods 提供可保证的内存(及大页面)分配能力。

内存管理器使用提示生成协议来为 Pod 生成最合适的 NUMA 亲和性配置。 内存管理器将这类亲和性提示输入给中央管理器(即 Topology Manager)。 基于所给的提示和 Topology Manager(拓扑管理器)的策略设置,Pod 或者会被某节点接受,或者被该节点拒绝。

此外,内存管理器还确保 Pod 所请求的内存是从尽量少的 NUMA 节点分配而来。

内存管理器仅能用于 Linux 主机。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你的 Kubernetes 服务器版本必须不低于版本 v1.21. 要获知版本信息,请输入 kubectl version.

为了使得内存资源与 Pod 规约中所请求的其他资源对齐:

  • CPU 管理器应该被启用,并且在节点(Node)上要配置合适的 CPU 管理器策略, 参见控制 CPU 管理策略
  • 拓扑管理器要被启用,并且要在节点上配置合适的拓扑管理器策略,参见 控制拓扑管理器策略

从 v1.22 开始,内存管理器通过特性门控 MemoryManager 默认启用。

在 v1.22 之前,kubelet 必须在启动时设置如下标志:

--feature-gates=MemoryManager=true

这样内存管理器特性才会被启用。

内存管理器如何运作?

内存管理器目前为 Guaranteed QoS 类中的 Pod 提供可保证的内存(和大页面)分配能力。 若要立即将内存管理器启用,可参照内存管理器配置节中的指南, 之后按将 Pod 放入 Guaranteed QoS 类 节中所展示的,准备并部署一个 Guaranteed Pod。

内存管理器是一个提示驱动组件(Hint Provider),负责为拓扑管理器提供拓扑提示, 后者根据这些拓扑提示对所请求的资源执行对齐操作。 内存管理器也会为 Pods 应用 cgroups 设置(即 cpuset.mems)。 与 Pod 准入和部署流程相关的完整流程图在Memory Manager KEP: Design Overview, 下面也有说明。

Pod 准入与部署流程中的内存管理器

在这个过程中,内存管理器会更新其内部存储于节点映射和内存映射中的计数器, 从而管理有保障的内存分配。

内存管理器在启动和运行期间按下述逻辑更新节点映射(Node Map)。

启动

当节点管理员应用 --reserved-memory 预留内存标志时执行此逻辑。 这时,节点映射会被更新以反映内存的预留,如 Memory Manager KEP: Memory Maps at start-up (with examples) 所说明。

当配置了 Static 策略时,管理员必须提供 --reserved-memory 标志设置。

运行时

参考文献 Memory Manager KEP: Memory Maps at runtime (with examples) 中说明了成功的 Pod 部署是如何影响节点映射的,该文档也解释了可能发生的内存不足 (Out-of-memory,OOM)情况是如何进一步被 Kubernetes 或操作系统处理的。

在内存管理器运作的语境中,一个重要的话题是对 NUMA 分组的管理。 每当 Pod 的内存请求超出单个 NUMA 节点容量时,内存管理器会尝试创建一个包含多个 NUMA 节点的分组,从而扩展内存容量。解决这个问题的详细描述在文档 Memory Manager KEP: How to enable the guaranteed memory allocation over many NUMA nodes? 中。同时,关于 NUMA 分组是如何管理的,你还可以参考文档 Memory Manager KEP: Simulation - how the Memory Manager works? (by examples)

内存管理器配置

其他管理器也要预先配置。接下来,内存管理器特性需要被启用, 并且采用 Static 策略(静态策略)运行。 作为可选操作,可以预留一定数量的内存给系统或者 kubelet 进程以增强节点的稳定性 (预留内存标志)。

策略

内存管理器支持两种策略。你可以通过 kubelet 标志 --memory-manager-policy 来选择一种策略:

  • None (默认)
  • Static

None 策略

这是默认的策略,并且不会以任何方式影响内存分配。该策略的行为好像内存管理器不存在一样。

None 策略返回默认的拓扑提示信息。这种特殊的提示会表明拓扑驱动组件(Hint Provider) (在这里是内存管理器)对任何资源都没有与 NUMA 亲和性关联的偏好。

Static 策略

Guaranteed Pod 而言,Static 内存管理器策略会返回拓扑提示信息, 该信息与内存分配有保障的 NUMA 节点集合有关,并且内存管理器还通过更新内部的节点映射 对象来完成内存预留。

BestEffortBurstable Pod 而言,因为不存在对有保障的内存资源的请求, Static 内存管理器策略会返回默认的拓扑提示,并且不会通过内部的节点映射对象来预留内存。

预留内存标志

节点可分配机制通常被节点管理员用来为 kubelet 或操作系统进程预留 K8S 节点上的系统资源,目的是提高节点稳定性。 有一组专用的标志可用于这个目的,为节点设置总的预留内存量。 此预配置的值接下来会被用来计算节点上对 Pods “可分配的”内存。

Kubernetes 调度器在优化 Pod 调度过程时,会考虑“可分配的”内存。 前面提到的标志包括 --kube-reserved--system-reserved--eviction-threshold。 这些标志值的综合计作预留内存的总量。

为内存管理器而新增加的 --reserved-memory 标志可以(让节点管理员)将总的预留内存进行划分, 并完成跨 NUMA 节点的预留操作。

标志设置的值是一个按 NUMA 节点的不同内存类型所给的内存预留的值的列表,用逗号分开。 可以使用分号作为分隔符来指定跨多个 NUMA 节点的内存预留。 只有在内存管理器特性被启用的语境下,这个参数才有意义。 内存管理器不会使用这些预留的内存来为容器负载分配内存。

例如,如果你有一个可用内存为 10Gi 的 NUMA 节点 "NUMA0",而参数 --reserved-memory 被设置成要在 "NUMA0" 上预留 1Gi 的内存,那么内存管理器会假定节点上只有 9Gi 内存可用于容器负载。

你也可以忽略此参数,不过这样做时,你要清楚,所有 NUMA 节点上预留内存的数量要等于节点可分配特性 所设定的内存量。如果至少有一个节点可分配参数值为非零,你就需要至少为一个 NUMA 节点设置 --reserved-memory。实际上,eviction-hard 阈值默认为 100Mi, 所以当使用 Static 策略时,--reserved-memory 是必须设置的。

此外,应尽量避免如下配置:

  1. 重复的配置,即同一 NUMA 节点或内存类型被设置不同的取值;
  2. 为某种内存类型设置约束值为零;
  3. 使用物理硬件上不存在的 NUMA 节点 ID;
  4. 使用名字不是 memoryhugepages-<size> 的内存类型名称 (特定的 <size> 的大页面也必须存在)。

语法:

--reserved-memory N:memory-type1=value1,memory-type2=value2,...

  • N(整数)- NUMA 节点索引,例如,0
  • memory-type(字符串)- 代表内存类型:
    • memory - 常规内存;
    • hugepages-2Mihugepages-1Gi - 大页面
  • value(字符串) - 预留内存的量,例如 1Gi

用法示例:

--reserved-memory 0:memory=1Gi,hugepages-1Gi=2Gi

或者

--reserved-memory 0:memory=1Gi --reserved-memory 1:memory=2Gi

--reserved-memory '0:memory=1Gi;1:memory=2Gi'

当你为 --reserved-memory 标志指定取值时,必须要遵从之前通过节点可分配特性标志所设置的值。 换言之,对每种内存类型而言都要遵从下面的规则:

sum(reserved-memory(i)) = kube-reserved + system-reserved + eviction-threshold

其中,i 是 NUMA 节点的索引。

如果你不遵守上面的公式,内存管理器会在启动时输出错误信息。

换言之,上面的例子我们一共要预留 3Gi 的常规内存(type=memory),即:

sum(reserved-memory(i)) = reserved-memory(0) + reserved-memory(1) = 1Gi + 2Gi = 3Gi

下面的例子中给出与节点可分配配置相关的 kubelet 命令行参数:

  • --kube-reserved=cpu=500m,memory=50Mi
  • --system-reserved=cpu=123m,memory=333Mi
  • --eviction-hard=memory.available<500Mi

下面是一个正确配置的示例:

--feature-gates=MemoryManager=true
--kube-reserved=cpu=4,memory=4Gi
--system-reserved=cpu=1,memory=1Gi
--memory-manager-policy=Static
--reserved-memory '0:memory=3Gi;1:memory=2148Mi'

我们对上面的配置做一个检查:

  1. kube-reserved + system-reserved + eviction-hard(default) = reserved-memory(0) + reserved-memory(1)
  2. 4GiB + 1GiB + 100MiB = 3GiB + 2148MiB
  3. 5120MiB + 100MiB = 3072MiB + 2148MiB
  4. 5220MiB = 5220MiB (这是对的)

将 Pod 放入 Guaranteed QoS 类

若所选择的策略不是 None,则内存管理器会辨识处于 Guaranteed QoS 类中的 Pod。 内存管理器为每个 Guaranteed Pod 向拓扑管理器提供拓扑提示信息。 对于不在 Guaranteed QoS 类中的其他 Pod,内存管理器向拓扑管理器提供默认的拓扑提示信息。

下面的来自 Pod 清单的片段将 Pod 加入到 Guaranteed QoS 类中。

当 Pod 的 CPU requests 等于 limits 且为整数值时,Pod 将运行在 Guaranteed QoS 类中。

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "2"
        example.com/device: "1"
      requests:
        memory: "200Mi"
        cpu: "2"
        example.com/device: "1"

此外,共享 CPU 的 Pods 在 requests 等于 limits 值时也运行在 Guaranteed QoS 类中。

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "300m"
        example.com/device: "1"
      requests:
        memory: "200Mi"
        cpu: "300m"
        example.com/device: "1"

要注意的是,只有 CPU 和内存请求都被设置时,Pod 才会进入 Guaranteed QoS 类。

故障排查

下面的方法可用来排查为什么 Pod 无法被调度或者被节点拒绝:

  • Pod 状态 - 可表明拓扑亲和性错误
  • 系统日志 - 包含用来调试的有价值的信息,例如,关于所生成的提示信息
  • 状态文件 - 其中包含内存管理器内部状态的转储(包含节点映射和内存映射
  • 从 v1.22 开始,设备插件资源 API 可以用来检索关于为容器预留的内存的信息

Pod 状态 (TopologyAffinityError)

这类错误通常在以下情形出现:

  • 节点缺少足够的资源来满足 Pod 请求
  • Pod 的请求因为特定的拓扑管理器策略限制而被拒绝

错误信息会出现在 Pod 的状态中:

kubectl get pods
NAME         READY   STATUS                  RESTARTS   AGE
guaranteed   0/1     TopologyAffinityError   0          113s

使用 kubectl describe pod <id>kubectl get events 可以获得详细的错误信息。

Warning  TopologyAffinityError  10m   kubelet, dell8  Resources cannot be allocated with Topology locality

系统日志

针对特定的 Pod 搜索系统日志。

内存管理器为 Pod 所生成的提示信息可以在日志中找到。 此外,日志中应该也存在 CPU 管理器所生成的提示信息。

拓扑管理器将这些提示信息进行合并,计算得到唯一的最合适的提示数据。 此最佳提示数据也应该出现在日志中。

最佳提示表明要在哪里分配所有的资源。拓扑管理器会用当前的策略来测试此数据, 并基于得出的结论或者接纳 Pod 到节点,或者将其拒绝。

此外,你可以搜索日志查找与内存管理器相关的其他条目,例如 cgroupscpuset.mems 的更新信息等。

检查节点上内存管理器状态

我们首先部署一个 Guaranteed Pod 示例,其规约如下所示:

apiVersion: v1
kind: Pod
metadata:
  name: guaranteed
spec:
  containers:
  - name: guaranteed
    image: consumer
    imagePullPolicy: Never
    resources:
      limits:
        cpu: "2"
        memory: 150Gi
      requests:
        cpu: "2"
        memory: 150Gi
    command: ["sleep","infinity"]

接下来,我们登录到 Pod 运行所在的节点,检查位于 /var/lib/kubelet/memory_manager_state 的状态文件:

{
   "policyName":"Static",
   "machineState":{
      "0":{
         "numberOfAssignments":1,
         "memoryMap":{
            "hugepages-1Gi":{
               "total":0,
               "systemReserved":0,
               "allocatable":0,
               "reserved":0,
               "free":0
            },
            "memory":{
               "total":134987354112,
               "systemReserved":3221225472,
               "allocatable":131766128640,
               "reserved":131766128640,
               "free":0
            }
         },
         "nodes":[
            0,
            1
         ]
      },
      "1":{
         "numberOfAssignments":1,
         "memoryMap":{
            "hugepages-1Gi":{
               "total":0,
               "systemReserved":0,
               "allocatable":0,
               "reserved":0,
               "free":0
            },
            "memory":{
               "total":135286722560,
               "systemReserved":2252341248,
               "allocatable":133034381312,
               "reserved":29295144960,
               "free":103739236352
            }
         },
         "nodes":[
            0,
            1
         ]
      }
   },
   "entries":{
      "fa9bdd38-6df9-4cf9-aa67-8c4814da37a8":{
         "guaranteed":[
            {
               "numaAffinity":[
                  0,
                  1
               ],
               "type":"memory",
               "size":161061273600
            }
         ]
      }
   },
   "checksum":4142013182
}

从这个状态文件,可以推断 Pod 被同时绑定到两个 NUMA 节点,即:

"numaAffinity":[
   0,
   1
],

术语绑定(pinned)意味着 Pod 的内存使用被(通过 cgroups 配置)限制到这些 NUMA 节点。

这也直接意味着内存管理器已经创建了一个 NUMA 分组,由这两个 NUMA 节点组成, 即索引值分别为 01 的 NUMA 节点。

注意 NUMA 分组的管理是有一个相对复杂的管理器处理的, 相关逻辑的进一步细节可在内存管理器的 KEP 中示例1跨 NUMA 节点节找到。

为了分析 NUMA 组中可用的内存资源,必须对分组内 NUMA 节点对应的条目进行汇总。

例如,NUMA 分组中空闲的“常规”内存的总量可以通过将分组内所有 NUMA 节点上空闲内存加和来计算,即将 NUMA 节点 0 和 NUMA 节点 1"memory" 节 (分别是 "free":0"free": 103739236352)相加,得到此分组中空闲的“常规” 内存总量为 0 + 103739236352 字节。

"systemReserved": 3221225472 这一行表明节点的管理员使用 --reserved-memory 为 NUMA 节点 0 上运行的 kubelet 和系统进程预留了 3221225472 字节 (即 3Gi)。

设备插件资源 API

kubelet 提供了一个 PodResourceLister gRPC 服务来启用对资源和相关元数据的检测。 通过使用它的 List gRPC 端点, 可以获得每个容器的预留内存信息,该信息位于 protobuf 协议的 ContainerMemory 消息中。 只能针对 Guaranteed QoS 类中的 Pod 来检索此信息。

接下来

4.2.43 - 验证已签名容器镜像

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

准备开始

你需要安装以下工具:

验证二进制签名

Kubernetes 发布过程使用 cosign 的无密钥签名对所有二进制工件(压缩包、 SPDX 文件、 独立的二进制文件)签名。要验证一个特定的二进制文件, 获取组件时要包含其签名和证书:

URL=https://dl.k8s.io/release/v1.30.0/bin/linux/amd64
BINARY=kubectl

FILES=(
    "$BINARY"
    "$BINARY.sig"
    "$BINARY.cert"
)

for FILE in "${FILES[@]}"; do
    curl -sSfL --retry 3 --retry-delay 3 "$URL/$FILE" -o "$FILE"
done

然后使用 cosign verify-blob 验证二进制文件:

cosign verify-blob "$BINARY" \
  --signature "$BINARY".sig \
  --certificate "$BINARY".cert \
  --certificate-identity krel-staging@k8s-releng-prod.iam.gserviceaccount.com \
  --certificate-oidc-issuer https://accounts.google.com

验证镜像签名

完整的镜像签名列表请参见发行版本

从这个列表中选择一个镜像,并使用 cosign verify 命令来验证它的签名:

cosign verify registry.k8s.io/kube-apiserver-amd64:v1.30.0 \
  --certificate-identity krel-trust@k8s-releng-prod.iam.gserviceaccount.com \
  --certificate-oidc-issuer https://accounts.google.com \
  | jq .

验证所有控制平面组件镜像

验证最新稳定版(v1.30.0)所有已签名的控制平面组件镜像, 请运行以下命令:

curl -Ls "https://sbom.k8s.io/$(curl -Ls https://dl.k8s.io/release/stable.txt)/release" \
  | grep "SPDXID: SPDXRef-Package-registry.k8s.io" \
  | grep -v sha256 | cut -d- -f3- | sed 's/-/\//' | sed 's/-v1/:v1/' \
  | sort > images.txt
input=images.txt
while IFS= read -r image
do
  cosign verify "$image" \
    --certificate-identity krel-trust@k8s-releng-prod.iam.gserviceaccount.com \
    --certificate-oidc-issuer https://accounts.google.com \
    | jq .
done < "$input"

当你完成某个镜像的验证时,可以在你的 Pod 清单通过摘要值来指定该镜像,例如:

registry-url/image-name@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2

要了解更多信息,请参考镜像拉取策略章节。

使用准入控制器验证镜像签名

有一些非控制平面镜像 (例如 conformance 镜像), 也可以在部署时使用 sigstore policy-controller 控制器验证其签名。以下是一些有助于你开始使用 policy-controller 的资源:

验证软件物料清单

你可以使用 sigstore 证书和签名或相应的 SHA 文件来验证 Kubernetes 软件物料清单(SBOM):

# 检索最新可用的 Kubernetes 发行版本
VERSION=$(curl -Ls https://dl.k8s.io/release/stable.txt)

# 验证 SHA512 sum
curl -Ls "https://sbom.k8s.io/$VERSION/release" -o "$VERSION.spdx"
echo "$(curl -Ls "https://sbom.k8s.io/$VERSION/release.sha512") $VERSION.spdx" | sha512sum --check

# 验证 SHA256 sum
echo "$(curl -Ls "https://sbom.k8s.io/$VERSION/release.sha256") $VERSION.spdx" | sha256sum --check

# 检索 sigstore 签名和证书
curl -Ls "https://sbom.k8s.io/$VERSION/release.sig" -o "$VERSION.spdx.sig"
curl -Ls "https://sbom.k8s.io/$VERSION/release.cert" -o "$VERSION.spdx.cert"

# 验证 sigstore 签名
cosign verify-blob \
    --certificate "$VERSION.spdx.cert" \
    --signature "$VERSION.spdx.sig" \
    --certificate-identity krel-staging@k8s-releng-prod.iam.gserviceaccount.com \
    --certificate-oidc-issuer https://accounts.google.com \
    "$VERSION.spdx"

4.3 - 配置 Pods 和容器

对 Pod 和容器执行常见的配置任务。

4.3.1 - 为容器和 Pod 分配内存资源

此页面展示如何将内存请求(request)和内存限制(limit)分配给一个容器。 我们保障容器拥有它请求数量的内存,但不允许使用超过限制数量的内存。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

你集群中的每个节点必须拥有至少 300 MiB 的内存。

该页面上的一些步骤要求你在集群中运行 metrics-server 服务。 如果你已经有在运行中的 metrics-server,则可以跳过这些步骤。

如果你运行的是 Minikube,可以运行下面的命令启用 metrics-server:

minikube addons enable metrics-server

要查看 metrics-server 或资源指标 API (metrics.k8s.io) 是否已经运行,请运行以下命令:

kubectl get apiservices

如果资源指标 API 可用,则输出结果将包含对 metrics.k8s.io 的引用信息。

NAME
v1beta1.metrics.k8s.io

创建命名空间

创建一个命名空间,以便将本练习中创建的资源与集群的其余部分隔离。

kubectl create namespace mem-example

指定内存请求和限制

要为容器指定内存请求,请在容器资源清单中包含 resources: requests 字段。 同理,要指定内存限制,请包含 resources: limits

在本练习中,你将创建一个拥有一个容器的 Pod。 容器将会请求 100 MiB 内存,并且内存会被限制在 200 MiB 以内。 这是 Pod 的配置文件:

apiVersion: v1
kind: Pod
metadata:
  name: memory-demo
  namespace: mem-example
spec:
  containers:
  - name: memory-demo-ctr
    image: polinux/stress
    resources:
      requests:
        memory: "100Mi"
      limits:
        memory: "200Mi"
    command: ["stress"]
    args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"]

配置文件的 args 部分提供了容器启动时的参数。 "--vm-bytes", "150M" 参数告知容器尝试分配 150 MiB 内存。

开始创建 Pod:

kubectl apply -f https://k8s.io/examples/pods/resource/memory-request-limit.yaml --namespace=mem-example

验证 Pod 中的容器是否已运行:

kubectl get pod memory-demo --namespace=mem-example

查看 Pod 相关的详细信息:

kubectl get pod memory-demo --output=yaml --namespace=mem-example

输出结果显示:该 Pod 中容器的内存请求为 100 MiB,内存限制为 200 MiB。

...
resources:
  requests:
    memory: 100Mi
  limits:
    memory: 200Mi
...

运行 kubectl top 命令,获取该 Pod 的指标数据:

kubectl top pod memory-demo --namespace=mem-example

输出结果显示:Pod 正在使用的内存大约为 162,900,000 字节,约为 150 MiB。 这大于 Pod 请求的 100 MiB,但在 Pod 限制的 200 MiB之内。

NAME                        CPU(cores)   MEMORY(bytes)
memory-demo                 <something>  162856960

删除 Pod:

kubectl delete pod memory-demo --namespace=mem-example

超过容器限制的内存

当节点拥有足够的可用内存时,容器可以使用其请求的内存。 但是,容器不允许使用超过其限制的内存。 如果容器分配的内存超过其限制,该容器会成为被终止的候选容器。 如果容器继续消耗超出其限制的内存,则终止容器。 如果终止的容器可以被重启,则 kubelet 会重新启动它,就像其他任何类型的运行时失败一样。

在本练习中,你将创建一个 Pod,尝试分配超出其限制的内存。 这是一个 Pod 的配置文件,其拥有一个容器,该容器的内存请求为 50 MiB,内存限制为 100 MiB:

apiVersion: v1
kind: Pod
metadata:
  name: memory-demo-2
  namespace: mem-example
spec:
  containers:
  - name: memory-demo-2-ctr
    image: polinux/stress
    resources:
      requests:
        memory: "50Mi"
      limits:
        memory: "100Mi"
    command: ["stress"]
    args: ["--vm", "1", "--vm-bytes", "250M", "--vm-hang", "1"]

在配置文件的 args 部分中,你可以看到容器会尝试分配 250 MiB 内存,这远高于 100 MiB 的限制。

创建 Pod:

kubectl apply -f https://k8s.io/examples/pods/resource/memory-request-limit-2.yaml --namespace=mem-example

查看 Pod 相关的详细信息:

kubectl get pod memory-demo-2 --namespace=mem-example

此时,容器可能正在运行或被杀死。重复前面的命令,直到容器被杀掉:

NAME            READY     STATUS      RESTARTS   AGE
memory-demo-2   0/1       OOMKilled   1          24s

获取容器更详细的状态信息:

kubectl get pod memory-demo-2 --output=yaml --namespace=mem-example

输出结果显示:由于内存溢出(OOM),容器已被杀掉:

lastState:
   terminated:
     containerID: 65183c1877aaec2e8427bc95609cc52677a454b56fcb24340dbd22917c23b10f
     exitCode: 137
     finishedAt: 2017-06-20T20:52:19Z
     reason: OOMKilled
     startedAt: null

本练习中的容器可以被重启,所以 kubelet 会重启它。 多次运行下面的命令,可以看到容器在反复的被杀死和重启:

kubectl get pod memory-demo-2 --namespace=mem-example

输出结果显示:容器被杀掉、重启、再杀掉、再重启……:

kubectl get pod memory-demo-2 --namespace=mem-example
NAME            READY     STATUS      RESTARTS   AGE
memory-demo-2   0/1       OOMKilled   1          37s

kubectl get pod memory-demo-2 --namespace=mem-example
NAME            READY     STATUS    RESTARTS   AGE
memory-demo-2   1/1       Running   2          40s

查看关于该 Pod 历史的详细信息:

kubectl describe pod memory-demo-2 --namespace=mem-example

输出结果显示:该容器反复的在启动和失败:

... Normal  Created   Created container with id 66a3a20aa7980e61be4922780bf9d24d1a1d8b7395c09861225b0eba1b1f8511
... Warning BackOff   Back-off restarting failed container

查看关于集群节点的详细信息:

kubectl describe nodes

输出结果包含了一条练习中的容器由于内存溢出而被杀掉的记录:

Warning OOMKilling Memory cgroup out of memory: Kill process 4481 (stress) score 1994 or sacrifice child

删除 Pod:

kubectl delete pod memory-demo-2 --namespace=mem-example

超过整个节点容量的内存

内存请求和限制是与容器关联的,但将 Pod 视为具有内存请求和限制,也是很有用的。 Pod 的内存请求是 Pod 中所有容器的内存请求之和。 同理,Pod 的内存限制是 Pod 中所有容器的内存限制之和。

Pod 的调度基于请求。只有当节点拥有足够满足 Pod 内存请求的内存时,才会将 Pod 调度至节点上运行。

在本练习中,你将创建一个 Pod,其内存请求超过了你集群中的任意一个节点所拥有的内存。 这是该 Pod 的配置文件,其拥有一个请求 1000 GiB 内存的容器,这应该超过了你集群中任何节点的容量。

apiVersion: v1
kind: Pod
metadata:
  name: memory-demo-3
  namespace: mem-example
spec:
  containers:
  - name: memory-demo-3-ctr
    image: polinux/stress
    resources:
      requests:
        memory: "1000Gi"
      limits:
        memory: "1000Gi"
    command: ["stress"]
    args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"]

创建 Pod:

kubectl apply -f https://k8s.io/examples/pods/resource/memory-request-limit-3.yaml --namespace=mem-example

查看 Pod 状态:

kubectl get pod memory-demo-3 --namespace=mem-example

输出结果显示:Pod 处于 PENDING 状态。 这意味着,该 Pod 没有被调度至任何节点上运行,并且它会无限期的保持该状态:

kubectl get pod memory-demo-3 --namespace=mem-example
NAME            READY     STATUS    RESTARTS   AGE
memory-demo-3   0/1       Pending   0          25s

查看关于 Pod 的详细信息,包括事件:

kubectl describe pod memory-demo-3 --namespace=mem-example

输出结果显示:由于节点内存不足,该容器无法被调度:

Events:
  ...  Reason            Message
       ------            -------
  ...  FailedScheduling  No nodes are available that match all of the following predicates:: Insufficient memory (3).

内存单位

内存资源的基本单位是字节(byte)。你可以使用这些后缀之一,将内存表示为 纯整数或定点整数:E、P、T、G、M、K、Ei、Pi、Ti、Gi、Mi、Ki。 例如,下面是一些近似相同的值:

128974848, 129e6, 129M, 123Mi

删除 Pod:

kubectl delete pod memory-demo-3 --namespace=mem-example

如果你没有指定内存限制

如果你没有为一个容器指定内存限制,则自动遵循以下情况之一:

  • 容器可无限制地使用内存。容器可以使用其所在节点所有的可用内存, 进而可能导致该节点调用 OOM Killer。 此外,如果发生 OOM Kill,没有资源限制的容器将被杀掉的可行性更大。

  • 运行的容器所在命名空间有默认的内存限制,那么该容器会被自动分配默认限制。 集群管理员可用使用 LimitRange 来指定默认的内存限制。

内存请求和限制的目的

通过为集群中运行的容器配置内存请求和限制,你可以有效利用集群节点上可用的内存资源。 通过将 Pod 的内存请求保持在较低水平,你可以更好地安排 Pod 调度。 通过让内存限制大于内存请求,你可以完成两件事:

  • Pod 可以进行一些突发活动,从而更好的利用可用内存。
  • Pod 在突发活动期间,可使用的内存被限制为合理的数量。

清理

删除命名空间。下面的命令会删除你根据这个任务创建的所有 Pod:

kubectl delete namespace mem-example

接下来

应用开发者扩展阅读

集群管理员扩展阅读

4.3.2 - 为容器和 Pods 分配 CPU 资源

本页面展示如何为容器设置 CPU request(请求) 和 CPU limit(限制)。 容器使用的 CPU 不能超过所配置的限制。 如果系统有空闲的 CPU 时间,则可以保证给容器分配其所请求数量的 CPU 资源。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

你的集群必须至少有 1 个 CPU 可用才能运行本任务中的示例。

本页的一些步骤要求你在集群中运行 metrics-server 服务。如果你的集群中已经有正在运行的 metrics-server 服务,可以跳过这些步骤。

如果你正在运行 Minikube,请运行以下命令启用 metrics-server:

minikube addons enable metrics-server

查看 metrics-server(或者其他资源指标 API metrics.k8s.io 服务提供者)是否正在运行, 请键入以下命令:

kubectl get apiservices

如果资源指标 API 可用,则会输出将包含一个对 metrics.k8s.io 的引用。

NAME
v1beta1.metrics.k8s.io

创建一个名字空间

创建一个名字空间,以便将 本练习中创建的资源与集群的其余部分资源隔离。

kubectl create namespace cpu-example

指定 CPU 请求和 CPU 限制

要为容器指定 CPU 请求,请在容器资源清单中包含 resources: requests 字段。 要指定 CPU 限制,请包含 resources:limits

在本练习中,你将创建一个具有一个容器的 Pod。容器将会请求 0.5 个 CPU,而且最多限制使用 1 个 CPU。 这是 Pod 的配置文件:

apiVersion: v1
kind: Pod
metadata:
  name: cpu-demo
  namespace: cpu-example
spec:
  containers:
  - name: cpu-demo-ctr
    image: vish/stress
    resources:
      limits:
        cpu: "1"
      requests:
        cpu: "0.5"
    args:
    - -cpus
    - "2"

配置文件的 args 部分提供了容器启动时的参数。 -cpus "2" 参数告诉容器尝试使用 2 个 CPU。

创建 Pod:

kubectl apply -f https://k8s.io/examples/pods/resource/cpu-request-limit.yaml --namespace=cpu-example

验证所创建的 Pod 处于 Running 状态

kubectl get pod cpu-demo --namespace=cpu-example

查看显示关于 Pod 的详细信息:

kubectl get pod cpu-demo --output=yaml --namespace=cpu-example

输出显示 Pod 中的一个容器的 CPU 请求为 500 milliCPU,并且 CPU 限制为 1 个 CPU。

resources:
  limits:
    cpu: "1"
  requests:
    cpu: 500m

使用 kubectl top 命令来获取该 Pod 的指标:

kubectl top pod cpu-demo --namespace=cpu-example

此示例输出显示 Pod 使用的是 974 milliCPU,即略低于 Pod 配置中指定的 1 个 CPU 的限制。

NAME                        CPU(cores)   MEMORY(bytes)
cpu-demo                    974m         <something>

回想一下,通过设置 -cpu "2",你将容器配置为尝试使用 2 个 CPU, 但是容器只被允许使用大约 1 个 CPU。 容器的 CPU 用量受到限制,因为该容器正尝试使用超出其限制的 CPU 资源。

CPU 单位

CPU 资源以 CPU 单位度量。Kubernetes 中的一个 CPU 等同于:

  • 1 个 AWS vCPU
  • 1 个 GCP核心
  • 1 个 Azure vCore
  • 裸机上具有超线程能力的英特尔处理器上的 1 个超线程

小数值是可以使用的。一个请求 0.5 CPU 的容器保证会获得请求 1 个 CPU 的容器的 CPU 的一半。 你可以使用后缀 m 表示毫。例如 100m CPU、100 milliCPU 和 0.1 CPU 都相同。 精度不能超过 1m。

CPU 请求只能使用绝对数量,而不是相对数量。0.1 在单核、双核或 48 核计算机上的 CPU 数量值是一样的。

删除 Pod:

kubectl delete pod cpu-demo --namespace=cpu-example

设置超过节点能力的 CPU 请求

CPU 请求和限制与都与容器相关,但是我们可以考虑一下 Pod 具有对应的 CPU 请求和限制这样的场景。 Pod 对 CPU 用量的请求等于 Pod 中所有容器的请求数量之和。 同样,Pod 的 CPU 资源限制等于 Pod 中所有容器 CPU 资源限制数之和。

Pod 调度是基于资源请求值来进行的。 仅在某节点具有足够的 CPU 资源来满足 Pod CPU 请求时,Pod 将会在对应节点上运行:

在本练习中,你将创建一个 Pod,该 Pod 的 CPU 请求对于集群中任何节点的容量而言都会过大。 下面是 Pod 的配置文件,其中有一个容器。容器请求 100 个 CPU,这可能会超出集群中任何节点的容量。

apiVersion: v1
kind: Pod
metadata:
  name: cpu-demo-2
  namespace: cpu-example
spec:
  containers:
  - name: cpu-demo-ctr-2
    image: vish/stress
    resources:
      limits:
        cpu: "100"
      requests:
        cpu: "100"
    args:
    - -cpus
    - "2"

创建 Pod:

kubectl apply -f https://k8s.io/examples/pods/resource/cpu-request-limit-2.yaml --namespace=cpu-example

查看该 Pod 的状态:

kubectl get pod cpu-demo-2 --namespace=cpu-example

输出显示 Pod 状态为 Pending。也就是说,Pod 未被调度到任何节点上运行, 并且 Pod 将无限期地处于 Pending 状态:

NAME         READY     STATUS    RESTARTS   AGE
cpu-demo-2   0/1       Pending   0          7m

查看有关 Pod 的详细信息,包含事件:

kubectl describe pod cpu-demo-2 --namespace=cpu-example

输出显示由于节点上的 CPU 资源不足,无法调度容器:

Events:
  Reason                        Message
  ------                        -------
  FailedScheduling      No nodes are available that match all of the following predicates:: Insufficient cpu (3).

删除你的 Pod:

kubectl delete pod cpu-demo-2 --namespace=cpu-example

如果不指定 CPU 限制

如果你没有为容器指定 CPU 限制,则会发生以下情况之一:

  • 容器在可以使用的 CPU 资源上没有上限。因而可以使用所在节点上所有的可用 CPU 资源。

  • 容器在具有默认 CPU 限制的名字空间中运行,系统会自动为容器设置默认限制。 集群管理员可以使用 LimitRange 指定 CPU 限制的默认值。

如果你设置了 CPU 限制但未设置 CPU 请求

如果你为容器指定了 CPU 限制值但未为其设置 CPU 请求,Kubernetes 会自动为其 设置与 CPU 限制相同的 CPU 请求值。类似的,如果容器设置了内存限制值但未设置 内存请求值,Kubernetes 也会为其设置与内存限制值相同的内存请求。

CPU 请求和限制的初衷

通过配置你的集群中运行的容器的 CPU 请求和限制,你可以有效利用集群上可用的 CPU 资源。 通过将 Pod CPU 请求保持在较低水平,可以使 Pod 更有机会被调度。 通过使 CPU 限制大于 CPU 请求,你可以完成两件事:

  • Pod 可能会有突发性的活动,它可以利用碰巧可用的 CPU 资源。

  • Pod 在突发负载期间可以使用的 CPU 资源数量仍被限制为合理的数量。

清理

删除名字空间:

kubectl delete namespace cpu-example

接下来

针对应用开发者

针对集群管理员 {for-cluster-administrators}

4.3.3 - 调整分配给容器的 CPU 和内存资源

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

本页假定你已经熟悉了 Kubernetes Pod 的服务质量

本页说明如何在不重启 Pod 或其容器的情况下调整分配给运行中 Pod 容器的 CPU 和内存资源。 Kubernetes 节点会基于 Pod 的 requests 为 Pod 分配资源, 并基于 Pod 的容器中指定的 limits 限制 Pod 的资源使用。

对于原地调整 Pod 资源而言:

  • 针对 CPU 和内存资源的容器的 requestslimits可变更的
  • Pod 状态中 containerStatusesallocatedResources 字段反映了分配给 Pod 容器的资源。
  • Pod 状态中 containerStatusesresources 字段反映了如同容器运行时所报告的、针对正运行的容器配置的实际资源 requestslimits
  • Pod 状态中 resize 字段显示上次请求待处理的调整状态。此字段可以具有以下值:
    • Proposed:此值表示请求调整已被确认,并且请求已被验证和记录。
    • InProgress:此值表示节点已接受调整请求,并正在将其应用于 Pod 的容器。
    • Deferred:此值意味着在此时无法批准请求的调整,节点将继续重试。 当其他 Pod 退出并释放节点资源时,调整可能会被真正实施。
    • Infeasible:此值是一种信号,表示节点无法承接所请求的调整值。 如果所请求的调整超过节点可分配给 Pod 的最大资源,则可能会发生这种情况。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你的 Kubernetes 服务器版本必须不低于版本 1.27. 要获知版本信息,请输入 kubectl version.

容器调整策略

调整策略允许更精细地控制 Pod 中的容器如何针对 CPU 和内存资源进行调整。 例如,容器的应用程序可以处理 CPU 资源的调整而不必重启, 但是调整内存可能需要应用程序重启,因此容器也必须重启。

为了实现这一点,容器规约允许用户指定 resizePolicy。 针对调整 CPU 和内存可以设置以下重启策略:

  • NotRequired:在运行时调整容器的资源。
  • RestartContainer:重启容器并在重启后应用新资源。

如果未指定 resizePolicy[*].restartPolicy,则默认为 NotRequired

下面的示例显示了一个 Pod,其中 CPU 可以在不重启容器的情况下进行调整,但是内存调整需要重启容器。

apiVersion: v1
kind: Pod
metadata:
  name: qos-demo-5
  namespace: qos-example
spec:
  containers:
    - name: qos-demo-ctr-5
      image: nginx
      resizePolicy:
        - resourceName: cpu
          restartPolicy: NotRequired
        - resourceName: memory
          restartPolicy: RestartContainer
      resources:
        limits:
          memory: "200Mi"
          cpu: "700m"
        requests:
          memory: "200Mi"
          cpu: "700m"

创建具有资源请求和限制的 Pod

你可以通过为 Pod 的容器指定请求和/或限制来创建 Guaranteed 或 Burstable 服务质量类的 Pod。

考虑以下包含一个容器的 Pod 的清单。

apiVersion: v1
kind: Pod
metadata:
  name: qos-demo-5
  namespace: qos-example
spec:
  containers:
  - name: qos-demo-ctr-5
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "700m"
      requests:
        memory: "200Mi"
        cpu: "700m"

qos-example 名字空间中创建该 Pod:

kubectl create namespace qos-example
kubectl create -f https://k8s.io/examples/pods/qos/qos-pod-5.yaml --namespace=qos-example

此 Pod 被分类为 Guaranteed QoS 类,请求 700m CPU 和 200Mi 内存。

查看有关 Pod 的详细信息:

kubectl get pod qos-demo-5 --output=yaml --namespace=qos-example

另请注意,resizePolicy[*].restartPolicy 的值默认为 NotRequired, 表示可以在容器运行的情况下调整 CPU 和内存大小。

spec:
  containers:
    ...
    resizePolicy:
    - resourceName: cpu
      restartPolicy: NotRequired
    - resourceName: memory
      restartPolicy: NotRequired
    resources:
      limits:
        cpu: 700m
        memory: 200Mi
      requests:
        cpu: 700m
        memory: 200Mi
...
  containerStatuses:
...
    name: qos-demo-ctr-5
    ready: true
...
    allocatedResources:
      cpu: 700m
      memory: 200Mi
    resources:
      limits:
        cpu: 700m
        memory: 200Mi
      requests:
        cpu: 700m
        memory: 200Mi
    restartCount: 0
    started: true
...
  qosClass: Guaranteed

更新 Pod 的资源

假设要求的 CPU 需求已上升,现在需要 0.8 CPU。这通常由 VerticalPodAutoscaler (VPA) 这样的实体确定并可能以编程方式应用。

现在对 Pod 的 Container 执行 patch 命令,将容器的 CPU 请求和限制均设置为 800m

kubectl -n qos-example patch pod qos-demo-5 --patch '{"spec":{"containers":[{"name":"qos-demo-ctr-5", "resources":{"requests":{"cpu":"800m"}, "limits":{"cpu":"800m"}}}]}}'

在 Pod 已打补丁后查询其详细信息。

kubectl get pod qos-demo-5 --output=yaml --namespace=qos-example

以下 Pod 规约反映了更新后的 CPU 请求和限制。

spec:
  containers:
    ...
    resources:
      limits:
        cpu: 800m
        memory: 200Mi
      requests:
        cpu: 800m
        memory: 200Mi
...
  containerStatuses:
...
    allocatedResources:
      cpu: 800m
      memory: 200Mi
    resources:
      limits:
        cpu: 800m
        memory: 200Mi
      requests:
        cpu: 800m
        memory: 200Mi
    restartCount: 0
    started: true

观察到 allocatedResources 的值已更新,反映了新的预期 CPU 请求。 这表明节点能够容纳提高后的 CPU 资源需求。

在 Container 状态中,更新的 CPU 资源值显示已应用新的 CPU 资源。 Container 的 restartCount 保持不变,表示已在无需重启容器的情况下调整了容器的 CPU 资源。

清理

删除你的名字空间:

kubectl delete namespace qos-example

接下来

对于应用开发人员

对于集群管理员

4.3.4 - 为 Windows Pod 和容器配置 GMSA

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

本页展示如何为将运行在 Windows 节点上的 Pod 和容器配置 组管理的服务账号(Group Managed Service Accounts,GMSA)。 组管理的服务账号是活动目录(Active Directory)的一种特殊类型, 提供自动化的密码管理、简化的服务主体名称(Service Principal Name,SPN) 管理以及跨多个服务器将管理操作委派给其他管理员等能力。

在 Kubernetes 环境中,GMSA 凭据规约配置为 Kubernetes 集群范围的自定义资源 (Custom Resources)形式。Windows Pod 以及各 Pod 中的每个容器可以配置为使用 GMSA 来完成基于域(Domain)的操作(例如,Kerberos 身份认证),以便与其他 Windows 服务相交互。

准备开始

你需要一个 Kubernetes 集群,以及 kubectl 命令行工具, 且工具必须已配置为能够与你的集群通信。集群预期包含 Windows 工作节点。 本节讨论需要为每个集群执行一次的初始操作。

安装 GMSACredentialSpec CRD

你需要在集群上配置一个用于 GMSA 凭据规约资源的 CustomResourceDefinition(CRD), 以便定义类型为 GMSACredentialSpec 的自定义资源。首先下载 GMSA CRD YAML 并将其保存为 gmsa-crd.yaml。接下来执行 kubectl apply -f gmsa-crd.yaml 安装 CRD。

安装 Webhook 来验证 GMSA 用户

你需要为 Kubernetes 集群配置两个 Webhook,在 Pod 或容器级别填充和检查 GMSA 凭据规约引用。

  1. 一个修改模式(Mutating)的 Webhook,将对 GMSA 的引用(在 Pod 规约中体现为名字) 展开为完整凭据规约的 JSON 形式,并保存回 Pod 规约中。

  2. 一个验证模式(Validating)的 Webhook,确保对 GMSA 的所有引用都是已经授权给 Pod 的服务账号使用的。

安装以上 Webhook 及其相关联的对象需要执行以下步骤:

  1. 创建一个证书密钥对(用于允许 Webhook 容器与集群通信)

  2. 安装一个包含如上证书的 Secret

  3. 创建一个包含核心 Webhook 逻辑的 Deployment

  4. 创建引用该 Deployment 的 Validating Webhook 和 Mutating Webhook 配置

你可以使用这个脚本 来部署和配置上述 GMSA Webhook 及相关联的对象。你还可以在运行脚本时设置 --dry-run=server 选项以便审查脚本将会对集群做出的变更。

脚本所使用的 YAML 模板 也可用于手动部署 Webhook 及相关联的对象,不过需要对其中的参数作适当替换。

在活动目录中配置 GMSA 和 Windows 节点

在配置 Kubernetes 中的 Pod 以使用 GMSA 之前,需要按 Windows GMSA 文档 中描述的那样先在活动目录中准备好期望的 GMSA。 Windows 工作节点(作为 Kubernetes 集群的一部分)需要被配置到活动目录中,以便访问与期望的 GSMA 相关联的秘密凭据数据。这一操作的描述位于 Windows GMSA 文档 中。

创建 GMSA 凭据规约资源

当(如前所述)安装了 GMSACredentialSpec CRD 之后,你就可以配置包含 GMSA 凭据规约的自定义资源了。GMSA 凭据规约中并不包含秘密或敏感数据。 其中包含的信息主要用于容器运行时,便于后者向 Windows 描述容器所期望的 GMSA。 GMSA 凭据规约可以使用 PowerShell 脚本 以 YAML 格式生成。

下面是手动以 JSON 格式生成 GMSA 凭据规约并对其进行 YAML 转换的步骤:

  1. 导入 CredentialSpec 模块ipmo CredentialSpec.psm1

  2. 使用 New-CredentialSpec 来创建一个 JSON 格式的凭据规约。 要创建名为 WebApp1 的 GMSA 凭据规约,调用 New-CredentialSpec -Name WebApp1 -AccountName WebApp1 -Domain $(Get-ADDomain -Current LocalComputer)

  3. 使用 Get-CredentialSpec 来显示 JSON 文件的路径。

  4. 将凭据规约从 JSON 格式转换为 YAML 格式,并添加必要的头部字段 apiVersionkindmetadatacredspec,使其成为一个可以在 Kubernetes 中配置的 GMSACredentialSpec 自定义资源。

下面的 YAML 配置描述的是一个名为 gmsa-WebApp1 的 GMSA 凭据规约:

apiVersion: windows.k8s.io/v1
kind: GMSACredentialSpec
metadata:
  name: gmsa-WebApp1  # 这是随意起的一个名字,将用作引用
credspec:
  ActiveDirectoryConfig:
    GroupManagedServiceAccounts:
    - Name: WebApp1   # GMSA 账号的用户名
      Scope: CONTOSO  # NETBIOS 域名
    - Name: WebApp1   # GMSA 账号的用户名
      Scope: contoso.com # DNS 域名
  CmsPlugins:
  - ActiveDirectory
  DomainJoinConfig:
    DnsName: contoso.com  # DNS 域名
    DnsTreeName: contoso.com # DNS 域名根
    Guid: 244818ae-87ac-4fcd-92ec-e79e5252348a  # GUID
    MachineAccountName: WebApp1 # GMSA 账号的用户名
    NetBiosName: CONTOSO  # NETBIOS 域名
    Sid: S-1-5-21-2126449477-2524075714-3094792973 # GMSA 的 SID

上面的凭据规约资源可以保存为 gmsa-Webapp1-credspec.yaml,之后使用 kubectl apply -f gmsa-Webapp1-credspec.yml 应用到集群上。

配置集群角色以启用对特定 GMSA 凭据规约的 RBAC

你需要为每个 GMSA 凭据规约资源定义集群角色。 该集群角色授权某主体(通常是一个服务账号)对特定的 GMSA 资源执行 use 动作。 下面的示例显示的是一个集群角色,对前文创建的凭据规约 gmsa-WebApp1 执行鉴权。 将此文件保存为 gmsa-webapp1-role.yaml 并执行 kubectl apply -f gmsa-webapp1-role.yaml

# 创建集群角色读取凭据规约
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: webapp1-role
rules:
- apiGroups: ["windows.k8s.io"]
  resources: ["gmsacredentialspecs"]
  verbs: ["use"]
  resourceNames: ["gmsa-WebApp1"]

将角色指派给要使用特定 GMSA 凭据规约的服务账号

你需要将某个服务账号(Pod 配置所对应的那个)绑定到前文创建的集群角色上。 这一绑定操作实际上授予该服务账号使用所指定的 GMSA 凭据规约资源的访问权限。 下面显示的是一个绑定到集群角色 webapp1-role 上的 default 服务账号, 使之能够使用前面所创建的 gmsa-WebApp1 凭据规约资源。

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: allow-default-svc-account-read-on-gmsa-WebApp1
  namespace: default
subjects:
- kind: ServiceAccount
  name: default
  namespace: default
roleRef:
  kind: ClusterRole
  name: webapp1-role
  apiGroup: rbac.authorization.k8s.io

在 Pod 规约中配置 GMSA 凭据规约引用

Pod 规约字段 securityContext.windowsOptions.gmsaCredentialSpecName 可用来设置对指定 GMSA 凭据规约自定义资源的引用。 设置此引用将会配置 Pod 中的所有容器使用所给的 GMSA。 下面是一个 Pod 规约示例,其中包含了对 gmsa-WebApp1 凭据规约的引用:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: with-creds
  name: with-creds
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      run: with-creds
  template:
    metadata:
      labels:
        run: with-creds
    spec:
      securityContext:
        windowsOptions:
          gmsaCredentialSpecName: gmsa-webapp1
      containers:
      - image: mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019
        imagePullPolicy: Always
        name: iis
      nodeSelector:
        kubernetes.io/os: windows

Pod 中的各个容器也可以使用对应容器的 securityContext.windowsOptions.gmsaCredentialSpecName 字段来设置期望使用的 GMSA 凭据规约。例如:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: with-creds
  name: with-creds
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      run: with-creds
  template:
    metadata:
      labels:
        run: with-creds
    spec:
      containers:
      - image: mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019
        imagePullPolicy: Always
        name: iis
        securityContext:
          windowsOptions:
            gmsaCredentialSpecName: gmsa-Webapp1
      nodeSelector:
        kubernetes.io/os: windows

当 Pod 规约中填充了 GMSA 相关字段(如上所述),在集群中应用 Pod 规约时会依次发生以下事件:

  1. Mutating Webhook 解析对 GMSA 凭据规约资源的引用,并将其全部展开, 得到 GMSA 凭据规约的实际内容。

  2. Validating Webhook 确保与 Pod 相关联的服务账号有权在所给的 GMSA 凭据规约上执行 use 动作。

  3. 容器运行时为每个 Windows 容器配置所指定的 GMSA 凭据规约, 这样容器就可以以活动目录中该 GMSA 所代表的身份来执行操作,使用该身份来访问域中的服务。

使用主机名或 FQDN 对网络共享进行身份验证

如果你在使用主机名或 FQDN 从 Pod 连接到 SMB 共享时遇到问题,但能够通过其 IPv4 地址访问共享, 请确保在 Windows 节点上设置了以下注册表项。

reg add "HKLM\SYSTEM\CurrentControlSet\Services\hns\State" /v EnableCompartmentNamespace /t REG_DWORD /d 1

然后需要重新创建正在运行的 Pod 以使行为更改生效。有关如何使用此注册表项的更多信息, 请参见此处

故障排查

如果在你的环境中配置 GMSA 时遇到了困难,你可以采取若干步骤来排查可能的故障。

首先,确保 credspec 已传递给 Pod。为此,你需要先运行 exec 进入到你的一个 Pod 中并检查 nltest.exe /parentdomain 命令的输出。

在下面的例子中,Pod 未能正确地获得凭据规约:

kubectl exec -it iis-auth-7776966999-n5nzr powershell.exe

nltest.exe /parentdomain 导致以下错误:

Getting parent domain failed: Status = 1722 0x6ba RPC_S_SERVER_UNAVAILABLE

如果 Pod 未能正确获得凭据规约,则下一步就要检查与域之间的通信。 首先,从 Pod 内部快速执行一个 nslookup 操作,找到域根。

这一操作会告诉我们三件事情:

  1. Pod 能否访问域控制器(DC)
  2. DC 能否访问 Pod
  3. DNS 是否正常工作

如果 DNS 和通信测试通过,接下来你需要检查是否 Pod 已经与域之间建立了安全通信通道。 要执行这一检查,你需要再次通过 exec 进入到你的 Pod 中并执行 nltest.exe /query 命令。

nltest.exe /query

结果输出如下:

I_NetLogonControl failed: Status = 1722 0x6ba RPC_S_SERVER_UNAVAILABLE

这告诉我们,由于某种原因,Pod 无法使用 credspec 中指定的帐户登录到域。 你可以尝试通过运行以下命令来修复安全通道:

nltest /sc_reset:domain.example

如果命令成功,你将看到类似以下内容的输出:

Flags: 30 HAS_IP  HAS_TIMESERV
Trusted DC Name \\dc10.domain.example
Trusted DC Connection Status Status = 0 0x0 NERR_Success
The command completed successfully

如果以上命令修复了错误,你可以通过将以下生命周期回调添加到你的 Pod 规约中来自动执行该步骤。 如果这些操作没有修复错误,你将需要再次检查你的 credspec 并确认它是正确和完整的。

        image: registry.domain.example/iis-auth:1809v1
        lifecycle:
          postStart:
            exec:
              command: ["powershell.exe","-command","do { Restart-Service -Name netlogon } while ( $($Result = (nltest.exe /query); if ($Result -like '*0x0 NERR_Success*') {return $true} else {return $false}) -eq $false)"]
        imagePullPolicy: IfNotPresent

如果你向你的 Pod 规约中添加如上所示的 lifecycle 节,则 Pod 会自动执行所列举的命令来重启 netlogon 服务,直到 nltest.exe /query 命令返回时没有错误信息。

4.3.5 - 为 Windows 的 Pod 和容器配置 RunAsUserName

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

本页展示如何为运行为在 Windows 节点上运行的 Pod 和容器配置 RunAsUserName。 大致相当于 Linux 上的 runAsUser,允许在容器中以与默认值不同的用户名运行应用。

准备开始

你必须有一个 Kubernetes 集群,并且 kubectl 必须能和集群通信。 集群应该要有 Windows 工作节点,将在其中调度运行 Windows 工作负载的 pod 和容器。

为 Pod 设置 Username

要指定运行 Pod 容器时所使用的用户名,请在 Pod 声明中包含 securityContext (PodSecurityContext) 字段, 并在其内部包含 windowsOptions (WindowsSecurityContextOptions) 字段的 runAsUserName 字段。

你为 Pod 指定的 Windows SecurityContext 选项适用于该 Pod 中(包括 init 容器)的所有容器。

这儿有一个已经设置了 runAsUserName 字段的 Windows Pod 的配置文件:

apiVersion: v1
kind: Pod
metadata:
  name: run-as-username-pod-demo
spec:
  securityContext:
    windowsOptions:
      runAsUserName: "ContainerUser"
  containers:
  - name: run-as-username-demo
    image: mcr.microsoft.com/windows/servercore:ltsc2019
    command: ["ping", "-t", "localhost"]
  nodeSelector:
    kubernetes.io/os: windows

创建 Pod:

kubectl apply -f https://k8s.io/examples/windows/run-as-username-pod.yaml

验证 Pod 容器是否在运行:

kubectl get pod run-as-username-pod-demo

获取该容器的 shell:

kubectl exec -it run-as-username-pod-demo -- powershell

检查运行 shell 的用户的用户名是否正确:

echo $env:USERNAME

输出结果应该是这样:

ContainerUser

为容器设置 Username

要指定运行容器时所使用的用户名,请在容器清单中包含 securityContext (SecurityContext) 字段,并在其内部包含 windowsOptionsWindowsSecurityContextOptions) 字段的 runAsUserName 字段。

你为容器指定的 Windows SecurityContext 选项仅适用于该容器,并且它会覆盖 Pod 级别设置。

这里有一个 Pod 的配置文件,其中只有一个容器,并且在 Pod 级别和容器级别都设置了 runAsUserName

apiVersion: v1
kind: Pod
metadata:
  name: run-as-username-container-demo
spec:
  securityContext:
    windowsOptions:
      runAsUserName: "ContainerUser"
  containers:
  - name: run-as-username-demo
    image: mcr.microsoft.com/windows/servercore:ltsc2019
    command: ["ping", "-t", "localhost"]
    securityContext:
        windowsOptions:
            runAsUserName: "ContainerAdministrator"
  nodeSelector:
    kubernetes.io/os: windows

创建 Pod:

kubectl apply -f https://k8s.io/examples/windows/run-as-username-container.yaml

验证 Pod 容器是否在运行:

kubectl get pod run-as-username-container-demo

获取该容器的 shell:

kubectl exec -it run-as-username-container-demo -- powershell

检查运行 shell 的用户的用户名是否正确(应该是容器级别设置的那个):

echo $env:USERNAME

输出结果应该是这样:

ContainerAdministrator

Windows Username 的局限性

想要使用此功能,在 runAsUserName 字段中设置的值必须是有效的用户名。 它必须是 DOMAIN\USER 这种格式,其中 DOMAIN\ 是可选的。 Windows 用户名不区分大小写。此外,关于 DOMAINUSER 还有一些限制:

  • runAsUserName 字段不能为空,并且不能包含控制字符(ASCII 值:0x00-0x1F0x7F
  • DOMAIN 必须是 NetBios 名称或 DNS 名称,每种名称都有各自的局限性:
    • NetBios 名称:最多 15 个字符,不能以 .(点)开头,并且不能包含以下字符:\ / : * ? " < > |
    • DNS 名称:最多 255 个字符,只能包含字母、数字、点和中划线,并且不能以 .(点)或 -(中划线)开头和结尾。
  • USER 最多不超过 20 个字符,不能包含点或空格,并且不能包含以下字符:" / \ [ ] : ; | = , + * ? < > @

runAsUserName 字段接受的值的一些示例:ContainerAdministratorContainerUserNT AUTHORITY\NETWORK SERVICENT AUTHORITY\LOCAL SERVICE

关于这些限制的更多信息,可以查看这里这里

接下来

4.3.6 - 创建 Windows HostProcess Pod

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

Windows HostProcess 容器让你能够在 Windows 主机上运行容器化负载。 这类容器以普通的进程形式运行,但能够在具有合适用户特权的情况下, 访问主机网络名字空间、存储和设备。HostProcess 容器可用来在 Windows 节点上部署网络插件、存储配置、设备插件、kube-proxy 以及其他组件, 同时不需要配置专用的代理或者直接安装主机服务。

类似于安装安全补丁、事件日志收集等这类管理性质的任务可以在不需要集群操作员登录到每个 Windows 节点的前提下执行。HostProcess 容器可以以主机上存在的任何用户账号来运行, 也可以以主机所在域中的用户账号运行,这样管理员可以通过用户许可权限来限制资源访问。 尽管文件系统和进程隔离都不支持,在启动容器时会在主机上创建一个新的卷, 为其提供一个干净的、整合的工作空间。HostProcess 容器也可以基于现有的 Windows 基础镜像来制作,并且不再有 Windows 服务器容器所带有的那些 兼容性需求, 这意味着基础镜像的版本不必与主机操作系统的版本匹配。 不过,仍然建议你像使用 Windows 服务器容器负载那样,使用相同的基础镜像版本, 这样你就不会有一些未使用的镜像占用节点上的存储空间。HostProcess 容器也支持 在容器卷内执行卷挂载

我何时该使用 Windows HostProcess 容器?

  • 当你准备执行需要访问主机上网络名字空间的任务时,HostProcess 容器能够访问主机上的网络接口和 IP 地址。
  • 当你需要访问主机上的资源,如文件系统、事件日志等等。
  • 需要安装特定的设备驱动或者 Windows 服务时。
  • 需要对管理任务和安全策略进行整合时。使用 HostProcess 容器能够缩小 Windows 节点上所需要的特权范围。

准备开始

本任务指南是特定于 Kubernetes v1.30 的。 如果你运行的不是 Kubernetes v1.30,请移步访问正确 版本的 Kubernetes 文档。

在 Kubernetes v1.30 中,HostProcess 容器功能特性默认是启用的。 kubelet 会直接与 containerd 通信,通过 CRI 将主机进程标志传递过去。 你可以使用 containerd 的最新版本(v1.6+)来运行 HostProcess 容器。 参阅如何安装 containerd

限制

以下限制是与 Kubernetes v1.30 相关的:

  • HostProcess 容器需要 containerd 1.6 或更高版本的 容器运行时, 推荐 containerd 1.7。
  • HostProcess Pods 只能包含 HostProcess 容器。这是在 Windows 操作系统上的约束; 非特权的 Windows 容器不能与主机 IP 名字空间共享虚拟网卡(vNIC)。
  • HostProcess 在主机上以一个进程的形式运行,除了通过 HostProcess 用户账号所实施的资源约束外,不提供任何形式的隔离。HostProcess 容器不支持文件系统或 Hyper-V 隔离。
  • 卷挂载是被支持的,并且要花在到容器卷下。参见卷挂载
  • 默认情况下有一组主机用户账号可供 HostProcess 容器使用。 参见选择用户账号
  • 对资源约束(磁盘、内存、CPU 个数)的支持与主机上进程相同。
  • 不支持命名管道或者 UNIX 域套接字形式的挂载,需要使用主机上的路径名来访问 (例如,\\.\pipe\*)。

HostProcess Pod 配置需求

启用 Windows HostProcess Pod 需要在 Pod 安全配置中设置合适的选项。 在 Pod 安全标准中所定义的策略中, HostProcess Pod 默认是不被 basline 和 restricted 策略支持的。因此建议 HostProcess 运行在与 privileged 模式相看齐的策略下。

当运行在 privileged 策略下时,下面是要启用 HostProcess Pod 创建所需要设置的选项:

privileged 策略规约
控制策略
securityContext.windowsOptions.hostProcess

Windows Pods 提供运行 HostProcess 容器的能力,这类容器能够具有对 Windows 节点的特权访问权限。

可选值

  • true
hostNetwork

Pod 容器 HostProcess 容器必须使用主机的网络名字空间。

可选值

  • true
securityContext.windowsOptions.runAsUserName

关于 HostProcess 容器所要使用的用户的规约,需要设置在 Pod 的规约中。

可选值

  • NT AUTHORITY\SYSTEM
  • NT AUTHORITY\Local service
  • NT AUTHORITY\NetworkService
  • 本地用户组名称(参见下文)
runAsNonRoot

因为 HostProcess 容器有访问主机的特权,runAsNonRoot 字段不可以设置为 true。

可选值

  • 未定义/Nil
  • false

配置清单示例(片段)

spec:
  securityContext:
    windowsOptions:
      hostProcess: true
      runAsUserName: "NT AUTHORITY\\Local service"
  hostNetwork: true
  containers:
  - name: test
    image: image1:latest
    command:
      - ping
      - -t
      - 127.0.0.1
  nodeSelector:
    "kubernetes.io/os": windows

卷挂载

HostProcess 容器支持在容器卷空间中挂载卷的能力。 卷挂载行为将因节点所使用的 containerd 运行时版本而异。

containerd v1.6

在容器内运行的应用能够通过相对或者绝对路径直接访问卷挂载。 环境变量 $CONTAINER_SANDBOX_MOUNT_POINT 在容器创建时被设置为指向容器卷的绝对主机路径。 相对路径是基于 .spec.containers.volumeMounts.mountPath 配置来推导的。

容器内支持通过下面的路径结构来访问服务账号令牌:

  • .\var\run\secrets\kubernetes.io\serviceaccount\
  • $CONTAINER_SANDBOX_MOUNT_POINT\var\run\secrets\kubernetes.io\serviceaccount\

containerd v1.7(及更高版本)

容器内运行的应用可以通过 volumeMount 指定的 mountPath 直接访问卷挂载 (就像 Linux 和非 HostProcess Windows 容器一样)。

为了向后兼容性,卷也可以通过使用由 containerd v1.6 配置的相同相对路径进行访问。

例如,要在容器中访问服务帐户令牌,你将使用以下路径之一:

  • c:\var\run\secrets\kubernetes.io\serviceaccount
  • /var/run/secrets/kubernetes.io/serviceaccount/
  • $CONTAINER_SANDBOX_MOUNT_POINT\var\run\secrets\kubernetes.io\serviceaccount\

资源限制

资源限制(磁盘、内存、CPU 个数)作用到任务之上,并在整个任务上起作用。 例如,如果内存限制设置为 10MB,任何 HostProcess 任务对象所分配的内存不会超过 10MB。 这一行为与其他 Windows 容器类型相同。资源限制的设置方式与编排系统或容器运行时无关。 唯一的区别是用来跟踪资源所进行的磁盘资源用量的计算,出现差异的原因是因为 HostProcess 容器启动引导的方式造成的。

选择用户账号

系统账号

默认情况下,HostProcess 容器支持以三种被支持的 Windows 服务账号之一来运行:

你应该为每个 HostProcess 容器选择一个合适的 Windows 服务账号,尝试限制特权范围, 避免给主机代理意外的(甚至是恶意的)伤害。LocalSystem 服务账号的特权级 在三者之中最高,只有在绝对需要的时候才应该使用。只要可能,应该使用 LocalService 服务账号,因为该账号在三者中特权最低。

本地账号

取决于配置,HostProcess 容器也能够以本地用户账号运行, 从而允许节点操作员为工作负载提供细粒度的访问权限。

要以本地用户运行 HostProcess 容器,必须首先在节点上创建一个本地用户组, 并在部署中在 runAsUserName 字段中指定该本地用户组的名称。 在初始化 HostProcess 容器之前,将创建一个新的临时本地用户账号,并加入到指定的用户组中, 使用这个账号来运行容器。这样做有许多好处,包括不再需要管理本地用户账号的密码。 作为服务账号运行的初始 HostProcess 容器可用于准备用户组,以供后续的 HostProcess 容器使用。

例如:

  1. 在节点上创建本地用户组(这可以在另一个 HostProcess 容器中完成)。

    net localgroup hpc-localgroup /add
    
  1. 为本地用户组授予访问所需资源的权限。这可以通过使用 icacls 这类工具达成。
  1. 针对 Pod 或个别容器,将 runAsUserName 设置为本地用户组的名称。

    securityContext:
      windowsOptions:
        hostProcess: true
        runAsUserName: hpc-localgroup
    
  1. 调度 Pod!

HostProcess 容器的基础镜像

HostProcess 容器可以基于任何现有的 Windows Container 基础镜像进行构建。

此外,还专为 HostProcess 容器创建了一个新的基础镜像!有关更多信息,请查看 windows-host-process-containers-base-image github 项目

HostProcess 容器的故障排查

  • HostProcess 容器因 failed to create user process token: failed to logon user: Access is denied.: unknown 启动失败。

    确保 containerd 以 LocalSystemLocalService 服务帐户运行。 用户账号(即使是管理员账号)没有权限为任何支持的用户账号创建登录令牌。

4.3.7 - 配置 Pod 的服务质量

本页介绍怎样配置 Pod 以让其归属于特定的 服务质量类(Quality of Service class,QoS class). Kubernetes 在 Node 资源不足时使用 QoS 类来就驱逐 Pod 作出决定。

Kubernetes 创建 Pod 时,会将如下 QoS 类之一设置到 Pod 上:

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你还需要能够创建和删除名字空间。

创建名字空间

创建一个名字空间,以便将本练习所创建的资源与集群的其余资源相隔离。

kubectl create namespace qos-example

创建一个 QoS 类为 Guaranteed 的 Pod

对于 QoS 类为 Guaranteed 的 Pod:

  • Pod 中的每个容器都必须指定内存限制和内存请求。
  • 对于 Pod 中的每个容器,内存限制必须等于内存请求。
  • Pod 中的每个容器都必须指定 CPU 限制和 CPU 请求。
  • 对于 Pod 中的每个容器,CPU 限制必须等于 CPU 请求。

这些限制同样适用于初始化容器和应用程序容器。 临时容器(Ephemeral Container) 无法定义资源,因此不受这些约束限制。

下面是包含一个 Container 的 Pod 清单。该 Container 设置了内存请求和内存限制,值都是 200 MiB。 该 Container 设置了 CPU 请求和 CPU 限制,值都是 700 milliCPU:

apiVersion: v1
kind: Pod
metadata:
  name: qos-demo
  namespace: qos-example
spec:
  containers:
  - name: qos-demo-ctr
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "700m"
      requests:
        memory: "200Mi"
        cpu: "700m"

创建 Pod:

kubectl apply -f https://k8s.io/examples/pods/qos/qos-pod.yaml --namespace=qos-example

查看 Pod 详情:

kubectl get pod qos-demo --namespace=qos-example --output=yaml

结果表明 Kubernetes 为 Pod 配置的 QoS 类为 Guaranteed。 结果也确认了 Pod 容器设置了与内存限制匹配的内存请求,设置了与 CPU 限制匹配的 CPU 请求。

spec:
  containers:
    ...
    resources:
      limits:
        cpu: 700m
        memory: 200Mi
      requests:
        cpu: 700m
        memory: 200Mi
    ...
status:
  qosClass: Guaranteed

清理

删除 Pod:

kubectl delete pod qos-demo --namespace=qos-example

创建一个 QoS 类为 Burstable 的 Pod

如果满足下面条件,Kubernetes 将会指定 Pod 的 QoS 类为 Burstable

  • Pod 不符合 Guaranteed QoS 类的标准。
  • Pod 中至少一个 Container 具有内存或 CPU 的请求或限制。

下面是包含一个 Container 的 Pod 清单。该 Container 设置的内存限制为 200 MiB, 内存请求为 100 MiB。

apiVersion: v1
kind: Pod
metadata:
  name: qos-demo-2
  namespace: qos-example
spec:
  containers:
  - name: qos-demo-2-ctr
    image: nginx
    resources:
      limits:
        memory: "200Mi"
      requests:
        memory: "100Mi"

创建 Pod:

kubectl apply -f https://k8s.io/examples/pods/qos/qos-pod-2.yaml --namespace=qos-example

查看 Pod 详情:

kubectl get pod qos-demo-2 --namespace=qos-example --output=yaml

结果表明 Kubernetes 为 Pod 配置的 QoS 类为 Burstable

spec:
  containers:
  - image: nginx
    imagePullPolicy: Always
    name: qos-demo-2-ctr
    resources:
      limits:
        memory: 200Mi
      requests:
        memory: 100Mi
  ...
status:
  qosClass: Burstable

清理

删除 Pod:

kubectl delete pod qos-demo-2 --namespace=qos-example

创建一个 QoS 类为 BestEffort 的 Pod

对于 QoS 类为 BestEffort 的 Pod,Pod 中的 Container 必须没有设置内存和 CPU 限制或请求。

下面是包含一个 Container 的 Pod 清单。该 Container 没有设置内存和 CPU 限制或请求。

apiVersion: v1
kind: Pod
metadata:
  name: qos-demo-3
  namespace: qos-example
spec:
  containers:
  - name: qos-demo-3-ctr
    image: nginx

创建 Pod:

kubectl apply -f https://k8s.io/examples/pods/qos/qos-pod-3.yaml --namespace=qos-example

查看 Pod 详情:

kubectl get pod qos-demo-3 --namespace=qos-example --output=yaml

结果表明 Kubernetes 为 Pod 配置的 QoS 类为 BestEffort

spec:
  containers:
    ...
    resources: {}
  ...
status:
  qosClass: BestEffort

清理

删除 Pod:

kubectl delete pod qos-demo-3 --namespace=qos-example

创建包含两个容器的 Pod

下面是包含两个 Container 的 Pod 清单。一个 Container 指定内存请求为 200 MiB。 另外一个 Container 没有指定任何请求或限制。

apiVersion: v1
kind: Pod
metadata:
  name: qos-demo-4
  namespace: qos-example
spec:
  containers:

  - name: qos-demo-4-ctr-1
    image: nginx
    resources:
      requests:
        memory: "200Mi"

  - name: qos-demo-4-ctr-2
    image: redis

注意此 Pod 满足 Burstable QoS 类的标准。也就是说它不满足 Guaranteed QoS 类标准, 因为它的 Container 之一设有内存请求。

创建 Pod:

kubectl apply -f https://k8s.io/examples/pods/qos/qos-pod-4.yaml --namespace=qos-example

查看 Pod 详情:

kubectl get pod qos-demo-4 --namespace=qos-example --output=yaml

结果表明 Kubernetes 为 Pod 配置的 QoS 类为 Burstable

spec:
  containers:
    ...
    name: qos-demo-4-ctr-1
    resources:
      requests:
        memory: 200Mi
    ...
    name: qos-demo-4-ctr-2
    resources: {}
    ...
status:
  qosClass: Burstable

检视 Pod 的 QoS 类

你也可以只查看你所需要的字段,而不是查看所有字段:

kubectl --namespace=qos-example get pod qos-demo-4 -o jsonpath='{ .status.qosClass}{"\n"}'
Burstable

环境清理

删除名字空间:

kubectl delete namespace qos-example

接下来

应用开发者参考

集群管理员参考

4.3.8 - 为容器分派扩展资源

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

本文介绍如何为容器指定扩展资源。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

在你开始此练习前,请先练习 为节点广播扩展资源。 在那个练习中将配置你的一个节点来广播 dongle 资源。

给 Pod 分派扩展资源

要请求扩展资源,需要在你的容器清单中包括 resources:requests 字段。 扩展资源可以使用任何完全限定名称,只是不能使用 *.kubernetes.io/。 有效的扩展资源名的格式为 example.com/foo,其中 example.com 应被替换为 你的组织的域名,而 foo 则是描述性的资源名称。

下面是包含一个容器的 Pod 配置文件:

apiVersion: v1
kind: Pod
metadata:
  name: extended-resource-demo
spec:
  containers:
  - name: extended-resource-demo-ctr
    image: nginx
    resources:
      requests:
        example.com/dongle: 3
      limits:
        example.com/dongle: 3

在配置文件中,你可以看到容器请求了 3 个 dongles。

创建 Pod:

kubectl apply -f https://k8s.io/examples/pods/resource/extended-resource-pod.yaml

检查 Pod 是否运行正常:

kubectl get pod extended-resource-demo

描述 Pod:

kubectl describe pod extended-resource-demo

输出结果显示 dongle 请求如下:

Limits:
  example.com/dongle: 3
Requests:
  example.com/dongle: 3

尝试创建第二个 Pod

下面是包含一个容器的 Pod 配置文件,容器请求了 2 个 dongles。

apiVersion: v1
kind: Pod
metadata:
  name: extended-resource-demo-2
spec:
  containers:
  - name: extended-resource-demo-2-ctr
    image: nginx
    resources:
      requests:
        example.com/dongle: 2
      limits:
        example.com/dongle: 2

Kubernetes 将不能满足 2 个 dongles 的请求,因为第一个 Pod 已经使用了 4 个可用 dongles 中的 3 个。

尝试创建 Pod:

kubectl apply -f https://k8s.io/examples/pods/resource/extended-resource-pod-2.yaml

描述 Pod:

kubectl describe pod extended-resource-demo-2

输出结果表明 Pod 不能被调度,因为没有一个节点上存在两个可用的 dongles。

Conditions:
  Type    Status
  PodScheduled  False
...
Events:
  ...
  ... Warning   FailedScheduling  pod (extended-resource-demo-2) failed to fit in any node
fit failure summary on nodes : Insufficient example.com/dongle (1)

查看 Pod 的状态:

kubectl get pod extended-resource-demo-2

输出结果表明 Pod 虽然被创建了,但没有被调度到节点上正常运行。Pod 的状态为 Pending:

NAME                       READY     STATUS    RESTARTS   AGE
extended-resource-demo-2   0/1       Pending   0          6m

清理

删除本练习中创建的 Pod:

kubectl delete pod extended-resource-demo
kubectl delete pod extended-resource-demo-2

接下来

应用开发者参考

集群管理员参考

4.3.9 - 配置 Pod 以使用卷进行存储

此页面展示了如何配置 Pod 以使用卷进行存储。

只要容器存在,容器的文件系统就会存在,因此当一个容器终止并重新启动,对该容器的文件系统改动将丢失。 对于独立于容器的持久化存储,你可以使用。 这对于有状态应用程序尤为重要,例如键值存储(如 Redis)和数据库。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

为 Pod 配置卷

在本练习中,你将创建一个运行 Pod,该 Pod 仅运行一个容器并拥有一个类型为 emptyDir 的卷, 在整个 Pod 生命周期中一直存在,即使 Pod 中的容器被终止和重启。以下是 Pod 的配置:

apiVersion: v1
kind: Pod
metadata:
  name: redis
spec:
  containers:
  - name: redis
    image: redis
    volumeMounts:
    - name: redis-storage
      mountPath: /data/redis
  volumes:
  - name: redis-storage
    emptyDir: {}
  1. 创建 Pod:

    kubectl apply -f https://k8s.io/examples/pods/storage/redis.yaml
    
  1. 验证 Pod 中的容器是否正在运行,然后留意 Pod 的更改:

    kubectl get pod redis --watch
    

    输出如下:

    NAME      READY     STATUS    RESTARTS   AGE
    redis     1/1       Running   0          13s
    
  1. 在另一个终端,用 Shell 连接正在运行的容器:

    kubectl exec -it redis -- /bin/bash
    
  1. 在你的 Shell 中,切换到 /data/redis 目录下,然后创建一个文件:

    root@redis:/data# cd /data/redis/
    root@redis:/data/redis# echo Hello > test-file
    
  1. 在你的 Shell 中,列出正在运行的进程:

    root@redis:/data/redis# apt-get update
    root@redis:/data/redis# apt-get install procps
    root@redis:/data/redis# ps aux
    

    输出类似于:

    USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
    redis        1  0.1  0.1  33308  3828 ?        Ssl  00:46   0:00 redis-server *:6379
    root        12  0.0  0.0  20228  3020 ?        Ss   00:47   0:00 /bin/bash
    root        15  0.0  0.0  17500  2072 ?        R+   00:48   0:00 ps aux
    
  1. 在你的 Shell 中,结束 Redis 进程:

    root@redis:/data/redis# kill <pid>
    

    其中 <pid> 是 Redis 进程的 ID (PID)。

  1. 在你原先终端中,留意 Redis Pod 的更改。最终你将会看到和下面类似的输出:

    NAME      READY     STATUS     RESTARTS   AGE
    redis     1/1       Running    0          13s
    redis     0/1       Completed  0         6m
    redis     1/1       Running    1         6m
    

此时,容器已经终止并重新启动。这是因为 Redis Pod 的 restartPolicyAlways

  1. 用 Shell 进入重新启动的容器中:

    kubectl exec -it redis -- /bin/bash
    
  1. 在你的 Shell 中,进入到 /data/redis 目录下,并确认 test-file 文件是否仍然存在。

    root@redis:/data/redis# cd /data/redis/
    root@redis:/data/redis# ls
    test-file
    
  1. 删除为此练习所创建的 Pod:

    kubectl delete pod redis
    

接下来

  • 参阅 Volume
  • 参阅 Pod
  • 除了 emptyDir 提供的本地磁盘存储外,Kubernetes 还支持许多不同的网络附加存储解决方案, 包括 GCE 上的 PD 和 EC2 上的 EBS,它们是关键数据的首选,并将处理节点上的一些细节, 例如安装和卸载设备。了解更多详情请参阅

4.3.10 - 配置 Pod 以使用 PersistentVolume 作为存储

本文将向你介绍如何配置 Pod 使用 PersistentVolumeClaim 作为存储。 以下是该过程的总结:

  1. 你作为集群管理员创建由物理存储支持的 PersistentVolume。你不会将该卷与任何 Pod 关联。

  2. 你现在以开发人员或者集群用户的角色创建一个 PersistentVolumeClaim, 它将自动绑定到合适的 PersistentVolume。

  3. 你创建一个使用以上 PersistentVolumeClaim 作为存储的 Pod。

准备开始

  • 你需要一个包含单个节点的 Kubernetes 集群,并且必须配置 kubectl 命令行工具以便与集群交互。 如果还没有单节点集群,可以使用 Minikube 创建一个。

  • 熟悉持久卷文档。

在你的节点上创建一个 index.html 文件

打开集群中的某个节点的 Shell。 如何打开 Shell 取决于集群的设置。 例如,如果你正在使用 Minikube,那么可以通过输入 minikube ssh 来打开节点的 Shell。

在该节点的 Shell 中,创建一个 /mnt/data 目录:

# 这里假定你的节点使用 "sudo" 来以超级用户角色执行命令
sudo mkdir /mnt/data

/mnt/data 目录中创建一个 index.html 文件:

# 这里再次假定你的节点使用 "sudo" 来以超级用户角色执行命令
sudo sh -c "echo 'Hello from Kubernetes storage' > /mnt/data/index.html"

测试 index.html 文件确实存在:

cat /mnt/data/index.html

输出应该是:

Hello from Kubernetes storage

现在你可以关闭节点的 Shell 了。

创建 PersistentVolume

在本练习中,你将创建一个 hostPath 类型的 PersistentVolume。 Kubernetes 支持用于在单节点集群上开发和测试的 hostPath 类型的 PersistentVolume。 hostPath 类型的 PersistentVolume 使用节点上的文件或目录来模拟网络附加存储。

在生产集群中,你不会使用 hostPath。 集群管理员会提供网络存储资源,比如 Google Compute Engine 持久盘卷、NFS 共享卷或 Amazon Elastic Block Store 卷。 集群管理员还可以使用 StorageClass 来设置动态制备存储

下面是 hostPath PersistentVolume 的配置文件:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: task-pv-volume
  labels:
    type: local
spec:
  storageClassName: manual
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/mnt/data"

此配置文件指定卷位于集群节点上的 /mnt/data 路径。 其配置还指定了卷的容量大小为 10 GB,访问模式为 ReadWriteOnce, 这意味着该卷可以被单个节点以读写方式安装。 此配置文件还在 PersistentVolume 中定义了 StorageClass 的名称manual。 它将用于将 PersistentVolumeClaim 的请求绑定到此 PersistentVolume。

创建 PersistentVolume:

kubectl apply -f https://k8s.io/examples/pods/storage/pv-volume.yaml

查看 PersistentVolume 的信息:

kubectl get pv task-pv-volume

输出结果显示该 PersistentVolume 的状态(STATUS)Available。 这意味着它还没有被绑定给 PersistentVolumeClaim。

NAME             CAPACITY   ACCESSMODES   RECLAIMPOLICY   STATUS      CLAIM     STORAGECLASS   REASON    AGE
task-pv-volume   10Gi       RWO           Retain          Available             manual                   4s

创建 PersistentVolumeClaim

下一步是创建一个 PersistentVolumeClaim。 Pod 使用 PersistentVolumeClaim 来请求物理存储。 在本练习中,你将创建一个 PersistentVolumeClaim,它请求至少 3 GB 容量的卷, 该卷一次最多可以为一个节点提供读写访问。

下面是 PersistentVolumeClaim 的配置文件:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: task-pv-claim
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 3Gi

创建 PersistentVolumeClaim:

kubectl apply -f https://k8s.io/examples/pods/storage/pv-claim.yaml

创建 PersistentVolumeClaim 之后,Kubernetes 控制平面将查找满足申领要求的 PersistentVolume。 如果控制平面找到具有相同 StorageClass 的适当的 PersistentVolume, 则将 PersistentVolumeClaim 绑定到该 PersistentVolume 上。

再次查看 PersistentVolume 信息:

kubectl get pv task-pv-volume

现在输出的 STATUSBound

NAME             CAPACITY   ACCESSMODES   RECLAIMPOLICY   STATUS    CLAIM                   STORAGECLASS   REASON    AGE
task-pv-volume   10Gi       RWO           Retain          Bound     default/task-pv-claim   manual                   2m

查看 PersistentVolumeClaim:

kubectl get pvc task-pv-claim

输出结果表明该 PersistentVolumeClaim 绑定了你的 PersistentVolume task-pv-volume

NAME            STATUS    VOLUME           CAPACITY   ACCESSMODES   STORAGECLASS   AGE
task-pv-claim   Bound     task-pv-volume   10Gi       RWO           manual         30s

创建 Pod

下一步是创建一个使用你的 PersistentVolumeClaim 作为存储卷的 Pod。

下面是此 Pod 的配置文件:

apiVersion: v1
kind: Pod
metadata:
  name: task-pv-pod
spec:
  volumes:
    - name: task-pv-storage
      persistentVolumeClaim:
        claimName: task-pv-claim
  containers:
    - name: task-pv-container
      image: nginx
      ports:
        - containerPort: 80
          name: "http-server"
      volumeMounts:
        - mountPath: "/usr/share/nginx/html"
          name: task-pv-storage


注意 Pod 的配置文件指定了 PersistentVolumeClaim,但没有指定 PersistentVolume。 对 Pod 而言,PersistentVolumeClaim 就是一个存储卷。

创建 Pod:

kubectl apply -f https://k8s.io/examples/pods/storage/pv-pod.yaml

检查 Pod 中的容器是否运行正常:

kubectl get pod task-pv-pod

打开一个 Shell 访问 Pod 中的容器:

kubectl exec -it task-pv-pod -- /bin/bash

在 Shell 中,验证 Nginx 是否正在从 hostPath 卷提供 index.html 文件:

# 一定要在上一步 "kubectl exec" 所返回的 Shell 中执行下面三个命令
apt update
apt install curl
curl http://localhost/

输出结果是你之前写到 hostPath 卷中的 index.html 文件中的内容:

Hello from Kubernetes storage

如果你看到此消息,则证明你已经成功地配置了 Pod 使用 PersistentVolumeClaim 的存储。

清理

删除 Pod、PersistentVolumeClaim 和 PersistentVolume 对象:

kubectl delete pod task-pv-pod
kubectl delete pvc task-pv-claim
kubectl delete pv task-pv-volume

如果你还没有连接到集群中节点的 Shell,可以按之前所做操作,打开一个新的 Shell。

在节点的 Shell 上,删除你所创建的目录和文件:

# 这里假定你使用 "sudo" 来以超级用户的角色执行命令
sudo rm /mnt/data/index.html
sudo rmdir /mnt/data

你现在可以关闭连接到节点的 Shell。

在两个地方挂载相同的 persistentVolume


apiVersion: v1
kind: Pod
metadata:
  name: test
spec:
  containers:
    - name: test
      image: nginx
      volumeMounts:
        # 网站数据挂载
        - name: config
          mountPath: /usr/share/nginx/html
          subPath: html
        # Nginx 配置挂载
        - name: config
          mountPath: /etc/nginx/nginx.conf
          subPath: nginx.conf
  volumes:
    - name: config
      persistentVolumeClaim:
        claimName: test-nfs-claim

你可以在 nginx 容器上执行两个卷挂载:

  • /usr/share/nginx/html 用于静态网站
  • /etc/nginx/nginx.conf 作为默认配置

访问控制

使用组 ID(GID)配置的存储仅允许 Pod 使用相同的 GID 进行写入。 GID 不匹配或缺失将会导致无权访问错误。 为了减少与用户的协调,管理员可以对 PersistentVolume 添加 GID 注解。 这样 GID 就能自动添加到使用 PersistentVolume 的任何 Pod 中。

使用 pv.beta.kubernetes.io/gid 注解的方法如下所示:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv1
  annotations:
    pv.beta.kubernetes.io/gid: "1234"

当 Pod 使用带有 GID 注解的 PersistentVolume 时,注解的 GID 会被应用于 Pod 中的所有容器, 应用的方法与 Pod 的安全上下文中指定的 GID 相同。 每个 GID,无论是来自 PersistentVolume 注解还是来自 Pod 规约,都会被应用于每个容器中运行的第一个进程。

接下来

参考

4.3.11 - 配置 Pod 使用投射卷作存储

本文介绍怎样通过 projected 卷将现有的多个卷资源挂载到相同的目录。 当前,secretconfigMapdownwardAPIserviceAccountToken 卷可以被投射。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

为 Pod 配置投射卷

本练习中,你将使用本地文件来创建用户名和密码 Secret, 然后创建运行一个容器的 Pod, 该 Pod 使用projected 卷将 Secret 挂载到相同的路径下。

下面是 Pod 的配置文件:

apiVersion: v1
kind: Pod
metadata:
  name: test-projected-volume
spec:
  containers:
  - name: test-projected-volume
    image: busybox:1.28
    args:
    - sleep
    - "86400"
    volumeMounts:
    - name: all-in-one
      mountPath: "/projected-volume"
      readOnly: true
  volumes:
  - name: all-in-one
    projected:
      sources:
      - secret:
          name: user
      - secret:
          name: pass
  1. 创建 Secret:

    # 创建包含用户名和密码的文件:
    echo -n "admin" > ./username.txt
    echo -n "1f2d1e2e67df" > ./password.txt
    
    # 在 Secret 中引用上述文件
    kubectl create secret generic user --from-file=./username.txt
    kubectl create secret generic pass --from-file=./password.txt
    
  2. 创建 Pod:

    kubectl apply -f https://k8s.io/examples/pods/storage/projected.yaml
    
  3. 确认 Pod 中的容器运行正常,然后监视 Pod 的变化:

    kubectl get --watch pod test-projected-volume
    

    输出结果和下面类似:

    NAME                    READY     STATUS    RESTARTS   AGE
    test-projected-volume   1/1       Running   0          14s
    
  4. 在另外一个终端中,打开容器的 shell:

    kubectl exec -it test-projected-volume -- /bin/sh
    
  5. 在 shell 中,确认 projected-volume 目录包含你的投射源:

    ls /projected-volume/
    

清理

删除 Pod 和 Secret:

kubectl delete pod test-projected-volume
kubectl delete secret user pass

接下来

4.3.12 - 为 Pod 或容器配置安全上下文

安全上下文(Security Context)定义 Pod 或 Container 的特权与访问控制设置。 安全上下文包括但不限于:

  • AppArmor:使用程序配置来限制个别程序的权能。

  • Seccomp:过滤进程的系统调用。

  • allowPrivilegeEscalation:控制进程是否可以获得超出其父进程的特权。 此布尔值直接控制是否为容器进程设置 no_new_privs标志。 当容器满足一下条件之一时,allowPrivilegeEscalation 总是为 true:

    • 以特权模式运行,或者
    • 具有 CAP_SYS_ADMIN 权能
  • readOnlyRootFilesystem:以只读方式加载容器的根文件系统。

以上条目不是安全上下文设置的完整列表 -- 请参阅 SecurityContext 了解其完整列表。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

为 Pod 设置安全性上下文

要为 Pod 设置安全性设置,可在 Pod 规约中包含 securityContext 字段。securityContext 字段值是一个 PodSecurityContext 对象。你为 Pod 所设置的安全性配置会应用到 Pod 中所有 Container 上。 下面是一个 Pod 的配置文件,该 Pod 定义了 securityContext 和一个 emptyDir 卷:

apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo
spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 3000
    fsGroup: 2000
  volumes:
  - name: sec-ctx-vol
    emptyDir: {}
  containers:
  - name: sec-ctx-demo
    image: busybox:1.28
    command: [ "sh", "-c", "sleep 1h" ]
    volumeMounts:
    - name: sec-ctx-vol
      mountPath: /data/demo
    securityContext:
      allowPrivilegeEscalation: false

在配置文件中,runAsUser 字段指定 Pod 中的所有容器内的进程都使用用户 ID 1000 来运行。runAsGroup 字段指定所有容器中的进程都以主组 ID 3000 来运行。 如果忽略此字段,则容器的主组 ID 将是 root(0)。 当 runAsGroup 被设置时,所有创建的文件也会划归用户 1000 和组 3000。 由于 fsGroup 被设置,容器中所有进程也会是附组 ID 2000 的一部分。 卷 /data/demo 及在该卷中创建的任何文件的属主都会是组 ID 2000。

创建该 Pod:

kubectl apply -f https://k8s.io/examples/pods/security/security-context.yaml

检查 Pod 的容器处于运行状态:

kubectl get pod security-context-demo

开启一个 Shell 进入到运行中的容器:

kubectl exec -it security-context-demo -- sh

在你的 Shell 中,列举运行中的进程:

ps

输出显示进程以用户 1000 运行,即 runAsUser 所设置的值:

PID   USER     TIME  COMMAND
    1 1000      0:00 sleep 1h
    6 1000      0:00 sh
...

在你的 Shell 中,进入 /data 目录列举其内容:

cd /data
ls -l

输出显示 /data/demo 目录的组 ID 为 2000,即 fsGroup 的设置值:

drwxrwsrwx 2 root 2000 4096 Jun  6 20:08 demo

在你的 Shell 中,进入到 /data/demo 目录下创建一个文件:

cd demo
echo hello > testfile

列举 /data/demo 目录下的文件:

ls -l

输出显示 testfile 的组 ID 为 2000,也就是 fsGroup 所设置的值:

-rw-r--r-- 1 1000 2000 6 Jun  6 20:08 testfile

运行下面的命令:

id

输出类似于:

uid=1000 gid=3000 groups=2000

从输出中你会看到 gid 值为 3000,也就是 runAsGroup 字段的值。 如果 runAsGroup 被忽略,则 gid 会取值 0(root),而进程就能够与 root 用户组所拥有以及要求 root 用户组访问权限的文件交互。

退出你的 Shell:

exit

为 Pod 配置卷访问权限和属主变更策略

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

默认情况下,Kubernetes 在挂载一个卷时,会递归地更改每个卷中的内容的属主和访问权限, 使之与 Pod 的 securityContext 中指定的 fsGroup 匹配。 对于较大的数据卷,检查和变更属主与访问权限可能会花费很长时间,降低 Pod 启动速度。 你可以在 securityContext 中使用 fsGroupChangePolicy 字段来控制 Kubernetes 检查和管理卷属主和访问权限的方式。

fsGroupChangePolicy - fsGroupChangePolicy 定义在卷被暴露给 Pod 内部之前对其 内容的属主和访问许可进行变更的行为。此字段仅适用于那些支持使用 fsGroup 来 控制属主与访问权限的卷类型。此字段的取值可以是:

  • OnRootMismatch:只有根目录的属主与访问权限与卷所期望的权限不一致时, 才改变其中内容的属主和访问权限。这一设置有助于缩短更改卷的属主与访问 权限所需要的时间。
  • Always:在挂载卷时总是更改卷中内容的属主和访问权限。

例如:

securityContext:
  runAsUser: 1000
  runAsGroup: 3000
  fsGroup: 2000
  fsGroupChangePolicy: "OnRootMismatch"

将卷权限和所有权更改委派给 CSI 驱动程序

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

如果你部署了一个容器存储接口 (CSI) 驱动,而该驱动支持 VOLUME_MOUNT_GROUP NodeServiceCapability, 在 securityContext 中指定 fsGroup 来设置文件所有权和权限的过程将由 CSI 驱动而不是 Kubernetes 来执行。在这种情况下,由于 Kubernetes 不执行任何所有权和权限更改, fsGroupChangePolicy 不会生效,并且按照 CSI 的规定,CSI 驱动应该使用所指定的 fsGroup 来挂载卷,从而生成了一个对 fsGroup 可读/可写的卷.

为 Container 设置安全性上下文

若要为 Container 设置安全性配置,可以在 Container 清单中包含 securityContext 字段。securityContext 字段的取值是一个 SecurityContext 对象。你为 Container 设置的安全性配置仅适用于该容器本身,并且所指定的设置在与 Pod 层面设置的内容发生重叠时,会重写 Pod 层面的设置。Container 层面的设置不会影响到 Pod 的卷。

下面是一个 Pod 的配置文件,其中包含一个 Container。Pod 和 Container 都有 securityContext 字段:

apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo-2
spec:
  securityContext:
    runAsUser: 1000
  containers:
  - name: sec-ctx-demo-2
    image: gcr.io/google-samples/node-hello:1.0
    securityContext:
      runAsUser: 2000
      allowPrivilegeEscalation: false

创建该 Pod:

kubectl apply -f https://k8s.io/examples/pods/security/security-context-2.yaml

验证 Pod 中的容器处于运行状态:

kubectl get pod security-context-demo-2

启动一个 Shell 进入到运行中的容器内:

kubectl exec -it security-context-demo-2 -- sh

在你的 Shell 中,列举运行中的进程:

ps aux

输出显示进程以用户 2000 运行。该值是在 Container 的 runAsUser 中设置的。 该设置值重写了 Pod 层面所设置的值 1000。

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
2000         1  0.0  0.0   4336   764 ?        Ss   20:36   0:00 /bin/sh -c node server.js
2000         8  0.1  0.5 772124 22604 ?        Sl   20:36   0:00 node server.js
...

退出你的 Shell:

exit

为 Container 设置权能

使用 Linux 权能, 你可以赋予进程 root 用户所拥有的某些特权,但不必赋予其全部特权。 要为 Container 添加或移除 Linux 权能,可以在 Container 清单的 securityContext 节包含 capabilities 字段。

首先,看一下不包含 capabilities 字段时候会发生什么。 下面是一个配置文件,其中没有添加或移除容器的权能:

apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo-3
spec:
  containers:
  - name: sec-ctx-3
    image: gcr.io/google-samples/node-hello:1.0

创建该 Pod:

kubectl apply -f https://k8s.io/examples/pods/security/security-context-3.yaml

验证 Pod 的容器处于运行状态:

kubectl get pod security-context-demo-3

启动一个 Shell 进入到运行中的容器:

kubectl exec -it security-context-demo-3 -- sh

在你的 Shell 中,列举运行中的进程:

ps aux

输出显示容器中进程 ID(PIDs):

USER  PID %CPU %MEM    VSZ   RSS TTY   STAT START   TIME COMMAND
root    1  0.0  0.0   4336   796 ?     Ss   18:17   0:00 /bin/sh -c node server.js
root    5  0.1  0.5 772124 22700 ?     Sl   18:17   0:00 node server.js

在你的 Shell 中,查看进程 1 的状态:

cd /proc/1
cat status

输出显示进程的权能位图:

...
CapPrm:	00000000a80425fb
CapEff:	00000000a80425fb
...

记下进程权能位图,之后退出你的 Shell:

exit

接下来运行一个与前例中容器相同的容器,只是这个容器有一些额外的权能设置。

下面是一个 Pod 的配置,其中运行一个容器。配置为容器添加 CAP_NET_ADMINCAP_SYS_TIME 权能:

apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo-4
spec:
  containers:
  - name: sec-ctx-4
    image: gcr.io/google-samples/node-hello:1.0
    securityContext:
      capabilities:
        add: ["NET_ADMIN", "SYS_TIME"]

创建 Pod:

kubectl apply -f https://k8s.io/examples/pods/security/security-context-4.yaml

启动一个 Shell,进入到运行中的容器:

kubectl exec -it security-context-demo-4 -- sh

在你的 Shell 中,查看进程 1 的权能:

cd /proc/1
cat status

输出显示的是进程的权能位图:

...
CapPrm:	00000000aa0435fb
CapEff:	00000000aa0435fb
...

比较两个容器的权能位图:

00000000a80425fb
00000000aa0435fb

在第一个容器的权能位图中,位 12 和 25 是没有设置的。在第二个容器中,位 12 和 25 是设置了的。位 12 是 CAP_NET_ADMIN 而位 25 则是 CAP_SYS_TIME。 参见 capability.h 了解权能常数的定义。

为容器设置 Seccomp 配置

若要为容器设置 Seccomp 配置(Profile),可在你的 Pod 或 Container 清单的 securityContext 节中包含 seccompProfile 字段。该字段是一个 SeccompProfile 对象,包含 typelocalhostProfile 属性。 type 的合法选项包括 RuntimeDefaultUnconfinedLocalhostlocalhostProfile 只能在 type: Localhost 配置下才可以设置。 该字段标明节点上预先设定的配置的路径,路径是相对于 kubelet 所配置的 Seccomp 配置路径(使用 --root-dir 设置)而言的。

下面是一个例子,设置容器使用节点上容器运行时的默认配置作为 Seccomp 配置:

...
securityContext:
  seccompProfile:
    type: RuntimeDefault

下面是另一个例子,将 Seccomp 的样板设置为位于 <kubelet-根目录>/seccomp/my-profiles/profile-allow.json 的一个预先配置的文件。

...
securityContext:
  seccompProfile:
    type: Localhost
    localhostProfile: my-profiles/profile-allow.json

为 Container 赋予 SELinux 标签

若要给 Container 设置 SELinux 标签,可以在 Pod 或 Container 清单的 securityContext 节包含 seLinuxOptions 字段。 seLinuxOptions 字段的取值是一个 SELinuxOptions 对象。下面是一个应用 SELinux 标签的例子:

...
securityContext:
  seLinuxOptions:
    level: "s0:c123,c456"

高效重打 SELinux 卷标签

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

默认情况下,容器运行时递归地将 SELinux 标签赋予所有 Pod 卷上的所有文件。 为了加快该过程,Kubernetes 使用挂载可选项 -o context=<label> 可以立即改变卷的 SELinux 标签。

要使用这项加速功能,必须满足下列条件:

  • 必须启用 ReadWriteOncePodSELinuxMountReadWriteOncePod 特性门控
  • Pod 必须使用带有对应的 accessModes特性门控 的 PersistentVolumeClaim。
    • 卷具有 accessModes: ["ReadWriteOncePod"],并且 SELinuxMountReadWriteOncePod 特性门控已启用。
    • 或者卷可以使用任何其他访问模式,并且必须启用 SELinuxMountReadWriteOncePodSELinuxMount 特性门控。
  • Pod(或其中使用 PersistentVolumeClaim 的所有容器)必须设置 seLinuxOptions
  • 对应的 PersistentVolume 必须是:
    • 使用传统树内(In-Tree) iscsirbdfs 卷类型的卷。
    • 或者是使用 {< glossary_tooltip text="CSI" term_id="csi" >}} 驱动程序的卷 CSI 驱动程序必须能够通过在 CSIDriver 实例中设置 spec.seLinuxMount: true 以支持 -o context 挂载。

对于所有其他卷类型,重打 SELinux 标签的方式有所不同: 容器运行时为卷中的所有节点(文件和目录)递归地修改 SELinux 标签。 卷中的文件和目录越多,重打标签需要耗费的时间就越长。

管理对 /proc 文件系统的访问

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

对于遵循 OCI 运行时规范的运行时,容器默认运行模式下,存在多个被屏蔽且只读的路径。 这样做的结果是在容器的 mount 命名空间内会存在这些路径,并且这些路径的工作方式与容器是隔离主机时类似, 但容器进程无法写入它们。 被屏蔽的和只读的路径列表如下:

  • 被屏蔽的路径:

    • /proc/asound
    • /proc/acpi
    • /proc/kcore
    • /proc/keys
    • /proc/latency_stats
    • /proc/timer_list
    • /proc/timer_stats
    • /proc/sched_debug
    • /proc/scsi
    • /sys/firmware
  • 只读的路径:

    • /proc/bus
    • /proc/fs
    • /proc/irq
    • /proc/sys
    • /proc/sysrq-trigger

对于某些 Pod,你可能希望绕过默认的路径屏蔽。 最常见的情况是你尝试在 Kubernetes 容器内(在 Pod 内)运行容器。

securityContext 字段 procMount 允许用户请求容器的 /procUnmasked, 或者由容器进程以读写方式挂载。这一设置也适用于不在 /proc 内的 /sys/firmware 路径。

...
securityContext:
  procMount: Unmasked

讨论

Pod 的安全上下文适用于 Pod 中的容器,也适用于 Pod 所挂载的卷(如果有的话)。 尤其是,fsGroupseLinuxOptions 按下面的方式应用到挂载卷上:

  • fsGroup:支持属主管理的卷会被修改,将其属主变更为 fsGroup 所指定的 GID, 并且对该 GID 可写。进一步的细节可参阅 属主变更设计文档

  • seLinuxOptions:支持 SELinux 标签的卷会被重新打标签,以便可被 seLinuxOptions 下所设置的标签访问。通常你只需要设置 level 部分。 该部分设置的是赋予 Pod 中所有容器及卷的 多类别安全性(Multi-Category Security,MCS)标签。

清理

删除之前创建的所有 Pod:

kubectl delete pod security-context-demo
kubectl delete pod security-context-demo-2
kubectl delete pod security-context-demo-3
kubectl delete pod security-context-demo-4

接下来

4.3.13 - 为 Pod 配置服务账号

Kubernetes 提供两种完全不同的方式来为客户端提供支持,这些客户端可能运行在你的集群中, 也可能与你的集群的控制面相关, 需要向 API 服务器完成身份认证。

服务账号(Service Account) 为 Pod 中运行的进程提供身份标识, 并映射到 ServiceAccount 对象。当你向 API 服务器执行身份认证时, 你会将自己标识为某个用户(User)。Kubernetes 能够识别用户的概念, 但是 Kubernetes 自身并不提供 User API。

本服务是关于 ServiceAccount 的,而 ServiceAccount 则确实存在于 Kubernetes 的 API 中。 本指南为你展示为 Pod 配置 ServiceAccount 的一些方法。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

使用默认的服务账号访问 API 服务器

当 Pod 与 API 服务器联系时,Pod 会被认证为某个特定的 ServiceAccount(例如:default)。 在每个名字空间中,至少存在一个 ServiceAccount。

每个 Kubernetes 名字空间至少包含一个 ServiceAccount:也就是该名字空间的默认服务账号, 名为 default。如果你在创建 Pod 时没有指定 ServiceAccount,Kubernetes 会自动将该名字空间中名为 default 的 ServiceAccount 分配给该 Pod。

你可以检视你刚刚创建的 Pod 的细节。例如:

kubectl get pods/<podname> -o yaml

在输出中,你可以看到字段 spec.serviceAccountName。当你在创建 Pod 时未设置该字段时, Kubernetes 自动为 Pod 设置这一属性的取值。

Pod 中运行的应用可以使用这一自动挂载的服务账号凭据来访问 Kubernetes API。 参阅访问集群以进一步了解。

当 Pod 被身份认证为某个 ServiceAccount 时, 其访问能力取决于所使用的鉴权插件和策略

放弃 API 凭据的自动挂载

如果你不希望 kubelet 自动挂载某 ServiceAccount 的 API 访问凭据,你可以选择不采用这一默认行为。 通过在 ServiceAccount 对象上设置 automountServiceAccountToken: false,可以放弃在 /var/run/secrets/kubernetes.io/serviceaccount/token 处自动挂载该服务账号的 API 凭据。

例如:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: build-robot
automountServiceAccountToken: false
...

你也可以选择不给特定 Pod 自动挂载 API 凭据:

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  serviceAccountName: build-robot
  automountServiceAccountToken: false
  ...

如果 ServiceAccount 和 Pod 的 .spec 都设置了 automountServiceAccountToken 值, 则 Pod 上 spec 的设置优先于服务账号的设置。

使用多个服务账号

每个名字空间都至少有一个 ServiceAccount:名为 default 的默认 ServiceAccount 资源。 你可以用下面的命令列举你当前名字空间 中的所有 ServiceAccount 资源:

kubectl get serviceaccounts

输出类似于:

NAME      SECRETS    AGE
default   1          1d

你可以像这样来创建额外的 ServiceAccount 对象:

kubectl apply -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
  name: build-robot
EOF

ServiceAccount 对象的名字必须是一个有效的 DNS 子域名

如果你查询服务账号对象的完整信息,如下所示:

kubectl get serviceaccounts/build-robot -o yaml

输出类似于:

apiVersion: v1
kind: ServiceAccount
metadata:
  creationTimestamp: 2019-06-16T00:12:34Z
  name: build-robot
  namespace: default
  resourceVersion: "272500"
  uid: 721ab723-13bc-11e5-aec2-42010af0021e

你可以使用鉴权插件来设置服务账号的访问许可

要使用非默认的服务账号,将 Pod 的 spec.serviceAccountName 字段设置为你想用的服务账号名称。

只能在创建 Pod 时或者为新 Pod 指定模板时,你才可以设置 serviceAccountName。 你不能更新已经存在的 Pod 的 .spec.serviceAccountName 字段。

清理

如果你尝试了创建前文示例中所给的 build-robot ServiceAccount, 你可以通过运行下面的命令来完成清理操作:

kubectl delete serviceaccount/build-robot

手动为 ServiceAccount 创建 API 令牌

假设你已经有了一个前文所提到的名为 "build-robot" 的服务账号。 你可以使用 kubectl 为该 ServiceAccount 获得一个有时限的 API 令牌:

kubectl create token build-robot

这一命令的输出是一个令牌,你可以使用该令牌来将身份认证为对应的 ServiceAccount。 你可以使用 kubectl create token 命令的 --duration 参数来请求特定的令牌有效期 (实际签发的令牌的有效期可能会稍短一些,也可能会稍长一些)。

当启用了 ServiceAccountTokenNodeBindingServiceAccountTokenNodeBindingValidation 特性,并将 KUBECTL_NODE_BOUND_TOKENS 环境变量设置为 true 时, 可以创建一个直接绑定到 Node 的服务账号令牌:

KUBECTL_NODE_BOUND_TOKENS=true kubectl create token build-robot --bound-object-kind Node --bound-object-name node-001 --bound-object-uid 123...456

此令牌将有效直至其过期或关联的 Node 或服务账户被删除。

手动为 ServiceAccount 创建长期有效的 API 令牌

如果你需要为 ServiceAccount 获得一个 API 令牌,你可以创建一个新的、带有特殊注解 kubernetes.io/service-account.name 的 Secret 对象。

kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: build-robot-secret
  annotations:
    kubernetes.io/service-account.name: build-robot
type: kubernetes.io/service-account-token
EOF

如果你通过下面的命令来查看 Secret:

kubectl get secret/build-robot-secret -o yaml

你可以看到 Secret 中现在包含针对 "build-robot" ServiceAccount 的 API 令牌。

鉴于你所设置的注解,控制面会自动为该 ServiceAccount 生成一个令牌,并将其保存到相关的 Secret 中。控制面也会为已删除的 ServiceAccount 执行令牌清理操作。

kubectl describe secrets/build-robot-secret

输出类似于这样:

Name:           build-robot-secret
Namespace:      default
Labels:         <none>
Annotations:    kubernetes.io/service-account.name: build-robot
                kubernetes.io/service-account.uid: da68f9c6-9d26-11e7-b84e-002dc52800da

Type:   kubernetes.io/service-account-token

Data
====
ca.crt:         1338 bytes
namespace:      7 bytes
token:          ...

当你删除一个与某 Secret 相关联的 ServiceAccount 时,Kubernetes 的控制面会自动清理该 Secret 中长期有效的令牌。

为服务账号添加 ImagePullSecrets

首先,生成一个 imagePullSecret; 接下来,验证该 Secret 已被创建。例如:

  • 为 Pod 设置 imagePullSecret 所描述的,生成一个镜像拉取 Secret:

    kubectl create secret docker-registry myregistrykey --docker-server=<registry name> \
            --docker-username=DUMMY_USERNAME --docker-password=DUMMY_DOCKER_PASSWORD \
            --docker-email=DUMMY_DOCKER_EMAIL
    
  • 检查该 Secret 已经被创建。

    kubectl get secrets myregistrykey
    

    输出类似于这样:

    NAME             TYPE                              DATA    AGE
    myregistrykey    kubernetes.io/.dockerconfigjson   1       1d
    

将镜像拉取 Secret 添加到服务账号

接下来更改名字空间的默认服务账号,将该 Secret 用作 imagePullSecret。

kubectl patch serviceaccount default -p '{"imagePullSecrets": [{"name": "myregistrykey"}]}'

你也可以通过手动编辑该对象来实现同样的效果:

kubectl edit serviceaccount/default

sa.yaml 文件的输出类似于:

你所选择的文本编辑器会被打开,展示如下所示的配置:

apiVersion: v1
kind: ServiceAccount
metadata:
  creationTimestamp: 2021-07-07T22:02:39Z
  name: default
  namespace: default
  resourceVersion: "243024"
  uid: 052fb0f4-3d50-11e5-b066-42010af0d7b6

使用你的编辑器,删掉包含 resourceVersion 主键的行,添加包含 imagePullSecrets: 的行并保存文件。对于 uid 而言,保持其取值与你读到的值一样。

当你完成这些变更之后,所编辑的 ServiceAccount 看起来像是这样:

apiVersion: v1
kind: ServiceAccount
metadata:
  creationTimestamp: 2021-07-07T22:02:39Z
  name: default
  namespace: default
  uid: 052fb0f4-3d50-11e5-b066-42010af0d7b6
imagePullSecrets:
  - name: myregistrykey

检查 imagePullSecrets 已经被设置到新 Pod 上

现在,在当前名字空间中创建新 Pod 并使用默认 ServiceAccount 时, 新 Pod 的 spec.imagePullSecrets 会被自动设置。

kubectl run nginx --image=<registry name>/nginx --restart=Never
kubectl get pod nginx -o=jsonpath='{.spec.imagePullSecrets[0].name}{"\n"}'

输出为:

myregistrykey

服务账号令牌卷投射

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

kubelet 还可以将 ServiceAccount 令牌投射到 Pod 中。你可以指定令牌的期望属性, 例如受众和有效期限。这些属性在 default ServiceAccount 令牌上无法配置。 当 Pod 或 ServiceAccount 被删除时,该令牌也将对 API 无效。

你可以使用类型为 ServiceAccountToken投射卷来为 Pod 的 spec 配置此行为。

来自此投射卷的令牌是一个 JSON Web Token (JWT)。 此令牌的 JSON 载荷遵循明确定义的模式,绑定到 Pod 的令牌的示例载荷如下:

{
  "aud": [  # 匹配请求的受众,或当没有明确请求时匹配 API 服务器的默认受众
    "https://kubernetes.default.svc"
  ],
  "exp": 1731613413,
  "iat": 1700077413,
  "iss": "https://kubernetes.default.svc",  # 匹配传递到 --service-account-issuer 标志的第一个值
  "jti": "ea28ed49-2e11-4280-9ec5-bc3d1d84661a",  # ServiceAccountTokenJTI 特性必须被启用才能出现此申领
  "kubernetes.io": {
    "namespace": "kube-system",
    "node": {  # ServiceAccountTokenPodNodeInfo 特性必须被启用,API 服务器才会添加此节点引用申领
      "name": "127.0.0.1",
      "uid": "58456cb0-dd00-45ed-b797-5578fdceaced"
    },
    "pod": {
      "name": "coredns-69cbfb9798-jv9gn",
      "uid": "778a530c-b3f4-47c0-9cd5-ab018fb64f33"
    },
    "serviceaccount": {
      "name": "coredns",
      "uid": "a087d5a0-e1dd-43ec-93ac-f13d89cd13af"
    },
    "warnafter": 1700081020
  },
  "nbf": 1700077413,
  "sub": "system:serviceaccount:kube-system:coredns"
}

启动使用服务账号令牌投射的 Pod

要为某 Pod 提供一个受众为 vault 并且有效期限为 2 小时的令牌,你可以定义一个与下面类似的 Pod 清单:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - image: nginx
    name: nginx
    volumeMounts:
    - mountPath: /var/run/secrets/tokens
      name: vault-token
  serviceAccountName: build-robot
  volumes:
  - name: vault-token
    projected:
      sources:
      - serviceAccountToken:
          path: vault-token
          expirationSeconds: 7200
          audience: vault

创建此 Pod:

kubectl create -f https://k8s.io/examples/pods/pod-projected-svc-token.yaml

kubelet 组件会替 Pod 请求令牌并将其保存起来;通过将令牌存储到一个可配置的路径以使之在 Pod 内可用;在令牌快要到期的时候刷新它。kubelet 会在令牌存在期达到其 TTL 的 80% 的时候或者令牌生命期超过 24 小时的时候主动请求将其轮换掉。

应用负责在令牌被轮换时重新加载其内容。通常而言,周期性地(例如,每隔 5 分钟) 重新加载就足够了,不必跟踪令牌的实际过期时间。

发现服务账号分发者

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

如果你在你的集群中已经为 ServiceAccount 启用了令牌投射, 那么你也可以利用其发现能力。Kubernetes 提供一种方式来让客户端将一个或多个外部系统进行联邦, 作为标识提供者(Identity Provider),而这些外部系统的角色是依赖方(Relying Party)

当此特性被启用时,Kubernetes API 服务器会通过 HTTP 发布一个 OpenID 提供者配置文档。 该配置文档发布在 /.well-known/openid-configuration 路径。 这里的 OpenID 提供者配置(OpenID Provider Configuration)有时候也被称作 “发现文档(Discovery Document)”。 Kubernetes API 服务器也通过 HTTP 在 /openid/v1/jwks 处发布相关的 JSON Web Key Set(JWKS)。

使用 RBAC 的集群都包含一个的默认 RBAC ClusterRole, 名为 system:service-account-issuer-discovery。 默认的 RBAC ClusterRoleBinding 将此角色分配给 system:serviceaccounts 组, 所有 ServiceAccount 隐式属于该组。这使得集群上运行的 Pod 能够通过它们所挂载的服务账号令牌访问服务账号发现文档。 此外,管理员可以根据其安全性需要以及期望集成的外部系统,选择是否将该角色绑定到 system:authenticatedsystem:unauthenticated

JWKS 响应包含依赖方可以用来验证 Kubernetes 服务账号令牌的公钥数据。 依赖方先会查询 OpenID 提供者配置,之后使用返回响应中的 jwks_uri 来查找 JWKS。

在很多场合,Kubernetes API 服务器都不会暴露在公网上,不过对于缓存并向外提供 API 服务器响应数据的公开末端而言,用户或者服务提供商可以选择将其暴露在公网上。 在这种环境中,可能会重载 OpenID 提供者配置中的 jwks_uri,使之指向公网上可用的末端地址,而不是 API 服务器的地址。 这时需要向 API 服务器传递 --service-account-jwks-uri 参数。 与分发者 URL 类似,此 JWKS URI 也需要使用 https 模式。

接下来

另请参见:

4.3.14 - 从私有仓库拉取镜像

本文介绍如何使用 Secret 从私有的镜像仓库或代码仓库拉取镜像来创建 Pod。 有很多私有镜像仓库正在使用中。这个任务使用的镜像仓库是 Docker Hub

准备开始

  • 你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

  • 要进行此练习,你需要 docker 命令行工具和一个知道密码的 Docker ID
  • 如果你要使用不同的私有的镜像仓库,你需要有对应镜像仓库的命令行工具和登录信息。

登录 Docker 镜像仓库

在个人电脑上,要想拉取私有镜像必须在镜像仓库上进行身份验证。

使用 docker 命令工具来登录到 Docker Hub。 更多详细信息,请查阅 Docker ID accounts 中的 log in 部分。

docker login

当出现提示时,输入你的 Docker ID 和登录凭据(访问令牌或 Docker ID 的密码)。

登录过程会创建或更新保存有授权令牌的 config.json 文件。 查看 Kubernetes 如何解析这个文件

查看 config.json 文件:

cat ~/.docker/config.json

输出结果包含类似于以下内容的部分:

{
    "auths": {
        "https://index.docker.io/v1/": {
            "auth": "c3R...zE2"
        }
    }
}

创建一个基于现有凭据的 Secret

Kubernetes 集群使用 kubernetes.io/dockerconfigjson 类型的 Secret 来通过镜像仓库的身份验证,进而提取私有镜像。

如果你已经运行了 docker login 命令,你可以复制该镜像仓库的凭据到 Kubernetes:

kubectl create secret generic regcred \
    --from-file=.dockerconfigjson=<path/to/.docker/config.json> \
    --type=kubernetes.io/dockerconfigjson

如果你需要更多的设置(例如,为新 Secret 设置名字空间或标签), 则可以在存储 Secret 之前对它进行自定义。 请务必:

  • 将 data 项中的名称设置为 .dockerconfigjson
  • 使用 base64 编码方法对 Docker 配置文件进行编码,然后粘贴该字符串的内容,作为字段 data[".dockerconfigjson"] 的值
  • type 设置为 kubernetes.io/dockerconfigjson

示例:

apiVersion: v1
kind: Secret
metadata:
  name: myregistrykey
  namespace: awesomeapps
data:
  .dockerconfigjson: UmVhbGx5IHJlYWxseSByZWVlZWVlZWVlZWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGx5eXl5eXl5eXl5eXl5eXl5eXl5eSBsbGxsbGxsbGxsbGxsbG9vb29vb29vb29vb29vb29vb29vb29vb29vb25ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubmdnZ2dnZ2dnZ2dnZ2dnZ2dnZ2cgYXV0aCBrZXlzCg==
type: kubernetes.io/dockerconfigjson

如果你收到错误消息:error: no objects passed to create, 这可能意味着 base64 编码的字符串是无效的。如果你收到类似 Secret "myregistrykey" is invalid: data[.dockerconfigjson]: invalid value ... 的错误消息,则表示数据中的 base64 编码字符串已成功解码, 但无法解析为 .docker/config.json 文件。

在命令行上提供凭据来创建 Secret

创建 Secret,命名为 regcred

kubectl create secret docker-registry regcred \
  --docker-server=<你的镜像仓库服务器> \
  --docker-username=<你的用户名> \
  --docker-password=<你的密码> \
  --docker-email=<你的邮箱地址>

在这里:

  • <your-registry-server> 是你的私有 Docker 仓库全限定域名(FQDN)。 DockerHub 使用 https://index.docker.io/v1/
  • <your-name> 是你的 Docker 用户名。
  • <your-pword> 是你的 Docker 密码。
  • <your-email> 是你的 Docker 邮箱。

这样你就成功地将集群中的 Docker 凭据设置为名为 regcred 的 Secret。

检查 Secret regcred

要了解你创建的 regcred Secret 的内容,可以用 YAML 格式进行查看:

kubectl get secret regcred --output=yaml

输出和下面类似:

apiVersion: v1
kind: Secret
metadata:
  ...
  name: regcred
  ...
data:
  .dockerconfigjson: eyJodHRwczovL2luZGV4L ... J0QUl6RTIifX0=
type: kubernetes.io/dockerconfigjson

.dockerconfigjson 字段的值是 Docker 凭据的 base64 表示。

要了解 dockerconfigjson 字段中的内容,请将 Secret 数据转换为可读格式:

kubectl get secret regcred --output="jsonpath={.data.\.dockerconfigjson}" | base64 --decode

输出和下面类似:

{"auths":{"your.private.registry.example.com":{"username":"janedoe","password":"xxxxxxxxxxx","email":"jdoe@example.com","auth":"c3R...zE2"}}}

要了解 auth 字段中的内容,请将 base64 编码过的数据转换为可读格式:

echo "c3R...zE2" | base64 --decode

输出结果中,用户名和密码用 : 链接,类似下面这样:

janedoe:xxxxxxxxxxx

注意,Secret 数据包含与本地 ~/.docker/config.json 文件类似的授权令牌。

这样你就已经成功地将 Docker 凭据设置为集群中的名为 regcred 的 Secret。

创建一个使用你的 Secret 的 Pod

下面是一个 Pod 配置清单示例,该示例中 Pod 需要访问你的 Docker 凭据 regcred

apiVersion: v1
kind: Pod
metadata:
  name: private-reg
spec:
  containers:
  - name: private-reg-container
    image: <your-private-image>
  imagePullSecrets:
  - name: regcred

将上述文件下载到你的计算机中:

curl -L -o my-private-reg-pod.yaml https://k8s.io/examples/pods/private-reg-pod.yaml

my-private-reg-pod.yaml 文件中,使用私有仓库的镜像路径替换 <your-private-image>,例如:

your.private.registry.example.com/janedoe/jdoe-private:v1

要从私有仓库拉取镜像,Kubernetes 需要凭据。 配置文件中的 imagePullSecrets 字段表明 Kubernetes 应该通过名为 regcred 的 Secret 获取凭据。

创建使用了你的 Secret 的 Pod,并检查它是否正常运行:

kubectl apply -f my-private-reg-pod.yaml
kubectl get pod private-reg

此外,如果 Pod 启动失败,状态为 ImagePullBackOff,查看 Pod 事件:

kubectl describe pod private-reg

如果你看到一个原因设为 FailedToRetrieveImagePullSecret 的事件, 那么 Kubernetes 找不到指定名称(此例中为 regcred)的 Secret。 如果你指定 Pod 需要拉取镜像凭据,kubelet 在尝试拉取镜像之前会检查是否可以访问该 Secret。

确保你指定的 Secret 存在,并且其名称拼写正确。

Events:
  ...  Reason                           ...  Message
       ------                                -------
  ...  FailedToRetrieveImagePullSecret  ...  Unable to retrieve some image pull secrets (<regcred>); attempting to pull the image may not succeed.

接下来

4.3.15 - 配置存活、就绪和启动探针

这篇文章介绍如何给容器配置存活(Liveness)、就绪(Readiness)和启动(Startup)探针。

kubelet 使用存活探针来确定什么时候要重启容器。 例如,存活探针可以探测到应用死锁(应用在运行,但是无法继续执行后面的步骤)情况。 重启这种状态下的容器有助于提高应用的可用性,即使其中存在缺陷。

存活探针的常见模式是为就绪探针使用相同的低成本 HTTP 端点,但具有更高的 failureThreshold。 这样可以确保在硬性终止 Pod 之前,将观察到 Pod 在一段时间内处于非就绪状态。

kubelet 使用就绪探针可以知道容器何时准备好接受请求流量,当一个 Pod 内的所有容器都就绪时,才能认为该 Pod 就绪。 这种信号的一个用途就是控制哪个 Pod 作为 Service 的后端。 若 Pod 尚未就绪,会被从 Service 的负载均衡器中剔除。

kubelet 使用启动探针来了解应用容器何时启动。 如果配置了这类探针,存活探针和就绪探针成功之前不会重启,确保这些探针不会影响应用的启动。 启动探针可以用于对慢启动容器进行存活性检测,避免它们在启动运行之前就被杀掉。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

定义存活命令

许多长时间运行的应用最终会进入损坏状态,除非重新启动,否则无法被恢复。 Kubernetes 提供了存活探针来发现并处理这种情况。

在本练习中,你会创建一个 Pod,其中运行一个基于 registry.k8s.io/busybox 镜像的容器。 下面是这个 Pod 的配置文件。

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-exec
spec:
  containers:
  - name: liveness
    image: registry.k8s.io/busybox
    args:
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 30; rm -f /tmp/healthy; sleep 600
    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 5
      periodSeconds: 5

在这个配置文件中,可以看到 Pod 中只有一个 ContainerperiodSeconds 字段指定了 kubelet 应该每 5 秒执行一次存活探测。 initialDelaySeconds 字段告诉 kubelet 在执行第一次探测前应该等待 5 秒。 kubelet 在容器内执行命令 cat /tmp/healthy 来进行探测。 如果命令执行成功并且返回值为 0,kubelet 就会认为这个容器是健康存活的。 如果这个命令返回非 0 值,kubelet 会杀死这个容器并重新启动它。

当容器启动时,执行如下的命令:

/bin/sh -c "touch /tmp/healthy; sleep 30; rm -f /tmp/healthy; sleep 600"

这个容器生命的前 30 秒,/tmp/healthy 文件是存在的。 所以在这最开始的 30 秒内,执行命令 cat /tmp/healthy 会返回成功代码。 30 秒之后,执行命令 cat /tmp/healthy 就会返回失败代码。

创建 Pod:

kubectl apply -f https://k8s.io/examples/pods/probe/exec-liveness.yaml

在 30 秒内,查看 Pod 的事件:

kubectl describe pod liveness-exec

输出结果表明还没有存活探针失败:

Type    Reason     Age   From               Message
----    ------     ----  ----               -------
Normal  Scheduled  11s   default-scheduler  Successfully assigned default/liveness-exec to node01
Normal  Pulling    9s    kubelet, node01    Pulling image "registry.k8s.io/busybox"
Normal  Pulled     7s    kubelet, node01    Successfully pulled image "registry.k8s.io/busybox"
Normal  Created    7s    kubelet, node01    Created container liveness
Normal  Started    7s    kubelet, node01    Started container liveness

35 秒之后,再来看 Pod 的事件:

kubectl describe pod liveness-exec

在输出结果的最下面,有信息显示存活探针失败了,这个失败的容器被杀死并且被重建了。

Type     Reason     Age                From               Message
----     ------     ----               ----               -------
Normal   Scheduled  57s                default-scheduler  Successfully assigned default/liveness-exec to node01
Normal   Pulling    55s                kubelet, node01    Pulling image "registry.k8s.io/busybox"
Normal   Pulled     53s                kubelet, node01    Successfully pulled image "registry.k8s.io/busybox"
Normal   Created    53s                kubelet, node01    Created container liveness
Normal   Started    53s                kubelet, node01    Started container liveness
Warning  Unhealthy  10s (x3 over 20s)  kubelet, node01    Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory
Normal   Killing    10s                kubelet, node01    Container liveness failed liveness probe, will be restarted

再等 30 秒,确认这个容器被重启了:

kubectl get pod liveness-exec

输出结果显示 RESTARTS 的值增加了 1。 请注意,一旦失败的容器恢复为运行状态,RESTARTS 计数器就会增加 1:

NAME            READY     STATUS    RESTARTS   AGE
liveness-exec   1/1       Running   1          1m

定义一个存活态 HTTP 请求接口

另外一种类型的存活探测方式是使用 HTTP GET 请求。 下面是一个 Pod 的配置文件,其中运行一个基于 registry.k8s.io/e2e-test-images/agnhost 镜像的容器。

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-http
spec:
  containers:
  - name: liveness
    image: registry.k8s.io/e2e-test-images/agnhost:2.40
    args:
    - liveness
    livenessProbe:
      httpGet:
        path: /healthz
        port: 8080
        httpHeaders:
        - name: Custom-Header
          value: Awesome
      initialDelaySeconds: 3
      periodSeconds: 3

在这个配置文件中,你可以看到 Pod 也只有一个容器。 periodSeconds 字段指定了 kubelet 每隔 3 秒执行一次存活探测。 initialDelaySeconds 字段告诉 kubelet 在执行第一次探测前应该等待 3 秒。 kubelet 会向容器内运行的服务(服务在监听 8080 端口)发送一个 HTTP GET 请求来执行探测。 如果服务器上 /healthz 路径下的处理程序返回成功代码,则 kubelet 认为容器是健康存活的。 如果处理程序返回失败代码,则 kubelet 会杀死这个容器并将其重启。

返回大于或等于 200 并且小于 400 的任何代码都标示成功,其它返回代码都标示失败。

你可以访问 server.go 阅读服务的源码。 容器存活期间的最开始 10 秒中,/healthz 处理程序返回 200 的状态码。 之后处理程序返回 500 的状态码。

http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
    duration := time.Now().Sub(started)
    if duration.Seconds() > 10 {
        w.WriteHeader(500)
        w.Write([]byte(fmt.Sprintf("error: %v", duration.Seconds())))
    } else {
        w.WriteHeader(200)
        w.Write([]byte("ok"))
    }
})

kubelet 在容器启动之后 3 秒开始执行健康检测。所以前几次健康检查都是成功的。 但是 10 秒之后,健康检查会失败,并且 kubelet 会杀死容器再重新启动容器。

创建一个 Pod 来测试 HTTP 的存活检测:

kubectl apply -f https://k8s.io/examples/pods/probe/http-liveness.yaml

10 秒之后,通过查看 Pod 事件来确认存活探针已经失败,并且容器被重新启动了。

kubectl describe pod liveness-http

在 1.13 之后的版本中,设置本地的 HTTP 代理环境变量不会影响 HTTP 的存活探测。

定义 TCP 的存活探测

第三种类型的存活探测是使用 TCP 套接字。 使用这种配置时,kubelet 会尝试在指定端口和容器建立套接字链接。 如果能建立连接,这个容器就被看作是健康的,如果不能则这个容器就被看作是有问题的。

apiVersion: v1
kind: Pod
metadata:
  name: goproxy
  labels:
    app: goproxy
spec:
  containers:
  - name: goproxy
    image: registry.k8s.io/goproxy:0.1
    ports:
    - containerPort: 8080
    readinessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 15
      periodSeconds: 10
    livenessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 15
      periodSeconds: 20

如你所见,TCP 检测的配置和 HTTP 检测非常相似。 下面这个例子同时使用就绪和存活探针。kubelet 会在容器启动 15 秒后发送第一个就绪探针。 探针会尝试连接 goproxy 容器的 8080 端口。 如果探测成功,这个 Pod 会被标记为就绪状态,kubelet 将继续每隔 10 秒运行一次探测。

除了就绪探针,这个配置包括了一个存活探针。 kubelet 会在容器启动 15 秒后进行第一次存活探测。 与就绪探针类似,存活探针会尝试连接 goproxy 容器的 8080 端口。 如果存活探测失败,容器会被重新启动。

kubectl apply -f https://k8s.io/examples/pods/probe/tcp-liveness-readiness.yaml

15 秒之后,通过看 Pod 事件来检测存活探针:

kubectl describe pod goproxy

定义 gRPC 存活探针

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

如果你的应用实现了 gRPC 健康检查协议, kubelet 可以配置为使用该协议来执行应用存活性检查。 你必须启用 GRPCContainerProbe 特性门控 才能配置依赖于 gRPC 的检查机制。

这个例子展示了如何配置 Kubernetes 以将其用于应用的存活性检查。 类似地,你可以配置就绪探针和启动探针。

下面是一个示例清单:

apiVersion: v1
kind: Pod
metadata:
  name: etcd-with-grpc
spec:
  containers:
  - name: etcd
    image: registry.k8s.io/etcd:3.5.1-0
    command: [ "/usr/local/bin/etcd", "--data-dir",  "/var/lib/etcd", "--listen-client-urls", "http://0.0.0.0:2379", "--advertise-client-urls", "http://127.0.0.1:2379", "--log-level", "debug"]
    ports:
    - containerPort: 2379
    livenessProbe:
      grpc:
        port: 2379
      initialDelaySeconds: 10

要使用 gRPC 探针,必须配置 port 属性。 如果要区分不同类型的探针和不同功能的探针,可以使用 service 字段。 你可以将 service 设置为 liveness,并使你的 gRPC 健康检查端点对该请求的响应与将 service 设置为 readiness 时不同。 这使你可以使用相同的端点进行不同类型的容器健康检查而不是监听两个不同的端口。 如果你想指定自己的自定义服务名称并指定探测类型,Kubernetes 项目建议你使用使用一个可以关联服务和探测类型的名称来命名。 例如:myservice-liveness(使用 - 作为分隔符)。

配置问题(例如:错误的 portservice、未实现健康检查协议) 都被认作是探测失败,这一点与 HTTP 和 TCP 探针类似。

kubectl apply -f https://k8s.io/examples/pods/probe/grpc-liveness.yaml

15 秒钟之后,查看 Pod 事件确认存活性检查并未失败:

kubectl describe pod etcd-with-grpc

当使用 gRPC 探针时,需要注意以下一些技术细节:

  • 这些探针运行时针对的是 Pod 的 IP 地址或其主机名。 请一定配置你的 gRPC 端点使之监听于 Pod 的 IP 地址之上。
  • 这些探针不支持任何身份认证参数(例如 -tls)。
  • 对于内置的探针而言,不存在错误代码。所有错误都被视作探测失败。
  • 如果 ExecProbeTimeout 特性门控被设置为 false,则 grpc-health-probe 不会考虑 timeoutSeconds 设置状态(默认值为 1s), 而内置探针则会在超时时返回失败。

使用命名端口

对于 HTTP 和 TCP 存活检测可以使用命名的 port(gRPC 探针不支持使用命名端口)。

例如:

ports:
- name: liveness-port
  containerPort: 8080

livenessProbe:
  httpGet:
    path: /healthz
    port: liveness-port

使用启动探针保护慢启动容器

有时候,会有一些现有的应用在启动时需要较长的初始化时间。 要这种情况下,若要不影响对死锁作出快速响应的探测,设置存活探测参数是要技巧的。 技巧就是使用相同的命令来设置启动探测,针对 HTTP 或 TCP 检测,可以通过将 failureThreshold * periodSeconds 参数设置为足够长的时间来应对最糟糕情况下的启动时间。

这样,前面的例子就变成了:

ports:
- name: liveness-port
  containerPort: 8080

livenessProbe:
  httpGet:
    path: /healthz
    port: liveness-port
  failureThreshold: 1
  periodSeconds: 10

startupProbe:
  httpGet:
    path: /healthz
    port: liveness-port
  failureThreshold: 30
  periodSeconds: 10

幸亏有启动探测,应用将会有最多 5 分钟(30 * 10 = 300s)的时间来完成其启动过程。 一旦启动探测成功一次,存活探测任务就会接管对容器的探测,对容器死锁作出快速响应。 如果启动探测一直没有成功,容器会在 300 秒后被杀死,并且根据 restartPolicy 来执行进一步处置。

定义就绪探针

有时候,应用会暂时性地无法为请求提供服务。 例如,应用在启动时可能需要加载大量的数据或配置文件,或是启动后要依赖等待外部服务。 在这种情况下,既不想杀死应用,也不想给它发送请求。 Kubernetes 提供了就绪探针来发现并缓解这些情况。 容器所在 Pod 上报还未就绪的信息,并且不接受通过 Kubernetes Service 的流量。

就绪探针的配置和存活探针的配置相似。 唯一区别就是要使用 readinessProbe 字段,而不是 livenessProbe 字段。

readinessProbe:
  exec:
    command:
    - cat
    - /tmp/healthy
  initialDelaySeconds: 5
  periodSeconds: 5

HTTP 和 TCP 的就绪探针配置也和存活探针的配置完全相同。

就绪和存活探测可以在同一个容器上并行使用。 两者共同使用,可以确保流量不会发给还未就绪的容器,当这些探测失败时容器会被重新启动。

配置探针

Probe 有很多配置字段,可以使用这些字段精确地控制启动、存活和就绪检测的行为:

  • initialDelaySeconds:容器启动后要等待多少秒后才启动启动、存活和就绪探针。 如果定义了启动探针,则存活探针和就绪探针的延迟将在启动探针已成功之后才开始计算。 如果 periodSeconds 的值大于 initialDelaySeconds,则 initialDelaySeconds 将被忽略。默认是 0 秒,最小值是 0。
  • periodSeconds:执行探测的时间间隔(单位是秒)。默认是 10 秒。最小值是 1。
  • timeoutSeconds:探测的超时后等待多少秒。默认值是 1 秒。最小值是 1。
  • successThreshold:探针在失败后,被视为成功的最小连续成功数。默认值是 1。 存活和启动探测的这个值必须是 1。最小值是 1。
  • failureThreshold:探针连续失败了 failureThreshold 次之后, Kubernetes 认为总体上检查已失败:容器状态未就绪、不健康、不活跃。 对于启动探针或存活探针而言,如果至少有 failureThreshold 个探针已失败, Kubernetes 会将容器视为不健康并为这个特定的容器触发重启操作。 kubelet 遵循该容器的 terminationGracePeriodSeconds 设置。 对于失败的就绪探针,kubelet 继续运行检查失败的容器,并继续运行更多探针; 因为检查失败,kubelet 将 Pod 的 Ready 状况设置为 false
  • terminationGracePeriodSeconds:为 kubelet 配置从为失败的容器触发终止操作到强制容器运行时停止该容器之前等待的宽限时长。 默认值是继承 Pod 级别的 terminationGracePeriodSeconds 值(如果不设置则为 30 秒),最小值为 1。 更多细节请参见探针级别 terminationGracePeriodSeconds

HTTP 探测

HTTP Probes 允许针对 httpGet 配置额外的字段:

  • host:连接使用的主机名,默认是 Pod 的 IP。也可以在 HTTP 头中设置 "Host" 来代替。
  • scheme:用于设置连接主机的方式(HTTP 还是 HTTPS)。默认是 "HTTP"。
  • path:访问 HTTP 服务的路径。默认值为 "/"。
  • httpHeaders:请求中自定义的 HTTP 头。HTTP 头字段允许重复。
  • port:访问容器的端口号或者端口名。如果数字必须在 1~65535 之间。

对于 HTTP 探测,kubelet 发送一个 HTTP 请求到指定的端口和路径来执行检测。 除非 httpGet 中的 host 字段设置了,否则 kubelet 默认是给 Pod 的 IP 地址发送探测。 如果 scheme 字段设置为了 HTTPS,kubelet 会跳过证书验证发送 HTTPS 请求。 大多数情况下,不需要设置 host 字段。 这里有个需要设置 host 字段的场景,假设容器监听 127.0.0.1,并且 Pod 的 hostNetwork 字段设置为了 true。那么 httpGet 中的 host 字段应该设置为 127.0.0.1。 可能更常见的情况是如果 Pod 依赖虚拟主机,你不应该设置 host 字段,而是应该在 httpHeaders 中设置 Host

针对 HTTP 探针,kubelet 除了必需的 Host 头部之外还发送两个请求头部字段:

  • User-Agent:默认值是 kube-probe/1.30,其中 1.30 是 kubelet 的版本号。
  • Accept:默认值 */*

你可以通过为探测设置 httpHeaders 来重载默认的头部字段值。例如:

livenessProbe:
  httpGet:
    httpHeaders:
      - name: Accept
        value: application/json

startupProbe:
  httpGet:
    httpHeaders:
      - name: User-Agent
        value: MyUserAgent

你也可以通过将这些头部字段定义为空值,从请求中去掉这些头部字段。

livenessProbe:
  httpGet:
    httpHeaders:
      - name: Accept
        value: ""

startupProbe:
  httpGet:
    httpHeaders:
      - name: User-Agent
        value: ""

TCP 探测

对于 TCP 探测而言,kubelet 在节点上(不是在 Pod 里面)发起探测连接, 这意味着你不能在 host 参数上配置服务名称,因为 kubelet 不能解析服务名称。

探针层面的 terminationGracePeriodSeconds

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

在 1.25 及以上版本中,用户可以指定一个探针层面的 terminationGracePeriodSeconds 作为探针规约的一部分。 当 Pod 层面和探针层面的 terminationGracePeriodSeconds 都已设置,kubelet 将使用探针层面设置的值。

当设置 terminationGracePeriodSeconds 时,请注意以下事项:

  • kubelet 始终优先选用探针级别 terminationGracePeriodSeconds 字段 (如果它存在于 Pod 上)。
  • 如果你已经为现有 Pod 设置了 terminationGracePeriodSeconds 字段并且不再希望使用针对每个探针的终止宽限期,则必须删除现有的这类 Pod。

例如:

spec:
  terminationGracePeriodSeconds: 3600  # Pod 级别设置
  containers:
  - name: test
    image: ...

    ports:
    - name: liveness-port
      containerPort: 8080

    livenessProbe:
      httpGet:
        path: /healthz
        port: liveness-port
      failureThreshold: 1
      periodSeconds: 60
      # 重载 Pod 级别的 terminationGracePeriodSeconds
      terminationGracePeriodSeconds: 60

探针层面的 terminationGracePeriodSeconds 不能用于就绪态探针。 这一设置将被 API 服务器拒绝。

接下来

你也可以阅读以下的 API 参考资料:

4.3.16 - 将 Pod 分配给节点

此页面显示如何将 Kubernetes Pod 指派给 Kubernetes 集群中的特定节点。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

给节点添加标签

  1. 列出你的集群中的节点, 包括这些节点上的标签:

    kubectl get nodes --show-labels
    

    输出类似如下:

    NAME      STATUS    ROLES    AGE     VERSION        LABELS
    worker0   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker0
    worker1   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker1
    worker2   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker2
    
  1. 从你的节点中选择一个,为它添加标签:

    kubectl label nodes <your-node-name> disktype=ssd
    

    <your-node-name> 是你选择的节点的名称。

  1. 验证你选择的节点确实带有 disktype=ssd 标签:

    kubectl get nodes --show-labels
    

    输出类似如下:

    NAME      STATUS    ROLES    AGE     VERSION        LABELS
    worker0   Ready     <none>   1d      v1.13.0        ...,disktype=ssd,kubernetes.io/hostname=worker0
    worker1   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker1
    worker2   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker2
    

    在前面的输出中,你可以看到 worker0 节点有 disktype=ssd 标签。

创建一个将被调度到你选择的节点的 Pod

此 Pod 配置文件描述了一个拥有节点选择器 disktype: ssd 的 Pod。这表明该 Pod 将被调度到有 disktype=ssd 标签的节点。

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  nodeSelector:
    disktype: ssd
  1. 使用该配置文件创建一个 Pod,该 Pod 将被调度到你选择的节点上:

    kubectl create -f https://k8s.io/examples/pods/pod-nginx.yaml
    
  1. 验证 Pod 确实运行在你选择的节点上:

    kubectl get pods --output=wide
    

    输出类似如下:

    NAME     READY     STATUS    RESTARTS   AGE    IP           NODE
    nginx    1/1       Running   0          13s    10.200.0.4   worker0
    

创建一个会被调度到特定节点上的 Pod

你也可以通过设置 nodeName 将某个 Pod 调度到特定的节点。

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  nodeName: foo-node # 调度 Pod 到特定的节点
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent

使用此配置文件来创建一个 Pod,该 Pod 将只能被调度到 foo-node 节点。

接下来

4.3.17 - 用节点亲和性把 Pod 分配到节点

本页展示在 Kubernetes 集群中,如何使用节点亲和性把 Kubernetes Pod 分配到特定节点。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你的 Kubernetes 服务器版本必须不低于版本 v1.10. 要获知版本信息,请输入 kubectl version.

给节点添加标签

  1. 列出集群中的节点及其标签:

    kubectl get nodes --show-labels
    

    输出类似于此:

    NAME      STATUS    ROLES    AGE     VERSION        LABELS
    worker0   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker0
    worker1   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker1
    worker2   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker2
    
  1. 选择一个节点,给它添加一个标签:

    kubectl label nodes <your-node-name> disktype=ssd
    

    其中 <your-node-name> 是你所选节点的名称。

  1. 验证你所选节点具有 disktype=ssd 标签:

    kubectl get nodes --show-labels
    

    输出类似于此:

    NAME      STATUS    ROLES    AGE     VERSION        LABELS
    worker0   Ready     <none>   1d      v1.13.0        ...,disktype=ssd,kubernetes.io/hostname=worker0
    worker1   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker1
    worker2   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker2
    

    在前面的输出中,可以看到 worker0 节点有一个 disktype=ssd 标签。

依据强制的节点亲和性调度 Pod

下面清单描述了一个 Pod,它有一个节点亲和性配置 requiredDuringSchedulingIgnoredDuringExecutiondisktype=ssd。 这意味着 pod 只会被调度到具有 disktype=ssd 标签的节点上。

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: disktype
            operator: In
            values:
            - ssd            
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  1. 执行(Apply)此清单来创建一个调度到所选节点上的 Pod:

    kubectl apply -f https://k8s.io/examples/pods/pod-nginx-required-affinity.yaml
    
  1. 验证 Pod 已经在所选节点上运行:

    kubectl get pods --output=wide
    

    输出类似于此:

    NAME     READY     STATUS    RESTARTS   AGE    IP           NODE
    nginx    1/1       Running   0          13s    10.200.0.4   worker0
    

使用首选的节点亲和性调度 Pod

本清单描述了一个 Pod,它有一个节点亲和性设置 preferredDuringSchedulingIgnoredDuringExecutiondisktype: ssd。 这意味着 Pod 将首选具有 disktype=ssd 标签的节点。

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  affinity:
    nodeAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: disktype
            operator: In
            values:
            - ssd          
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  1. 执行此清单创建一个会调度到所选节点上的 Pod:

    kubectl apply -f https://k8s.io/examples/pods/pod-nginx-preferred-affinity.yaml
    
  1. 验证 Pod 是否在所选节点上运行:

    kubectl get pods --output=wide
    

    输出类似于此:

    NAME     READY     STATUS    RESTARTS   AGE    IP           NODE
    nginx    1/1       Running   0          13s    10.200.0.4   worker0
    

接下来

进一步了解节点亲和性

4.3.18 - 配置 Pod 初始化

本文介绍在应用容器运行前,怎样利用 Init 容器初始化 Pod。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

创建一个包含 Init 容器的 Pod

本例中你将创建一个包含一个应用容器和一个 Init 容器的 Pod。Init 容器在应用容器启动前运行完成。

下面是 Pod 的配置文件:

apiVersion: v1
kind: Pod
metadata:
  name: init-demo
spec:
  containers:
  - name: nginx
    image: nginx
    ports:
    - containerPort: 80
    volumeMounts:
    - name: workdir
      mountPath: /usr/share/nginx/html
  # 这些容器在 Pod 初始化期间运行
  initContainers:
  - name: install
    image: busybox:1.28
    command:
    - wget
    - "-O"
    - "/work-dir/index.html"
    - http://info.cern.ch
    volumeMounts:
    - name: workdir
      mountPath: "/work-dir"
  dnsPolicy: Default
  volumes:
  - name: workdir
    emptyDir: {}

配置文件中,你可以看到应用容器和 Init 容器共享了一个卷。

Init 容器将共享卷挂载到了 /work-dir 目录,应用容器将共享卷挂载到了 /usr/share/nginx/html 目录。 Init 容器执行完下面的命令就终止:

wget -O /work-dir/index.html http://info.cern.ch

请注意 Init 容器在 nginx 服务器的根目录写入 index.html

创建 Pod:

kubectl apply -f https://k8s.io/examples/pods/init-containers.yaml

检查 nginx 容器运行正常:

kubectl get pod init-demo

结果表明 nginx 容器运行正常:

NAME        READY     STATUS    RESTARTS   AGE
init-demo   1/1       Running   0          1m

通过 shell 进入 init-demo Pod 中的 nginx 容器:

kubectl exec -it init-demo -- /bin/bash

在 shell 中,发送个 GET 请求到 nginx 服务器:

root@nginx:~# apt-get update
root@nginx:~# apt-get install curl
root@nginx:~# curl localhost

结果表明 nginx 正在为 Init 容器编写的 web 页面服务:

<html><head></head><body><header>
<title>http://info.cern.ch</title>
</header>

<h1>http://info.cern.ch - home of the first website</h1>
  ...
<li><a href="http://info.cern.ch/hypertext/WWW/TheProject.html">Browse the first website</a></li>
  ...

接下来

4.3.19 - 为容器的生命周期事件设置处理函数

这个页面将演示如何为容器的生命周期事件挂接处理函数。Kubernetes 支持 postStart 和 preStop 事件。 当一个容器启动后,Kubernetes 将立即发送 postStart 事件;在容器被终结之前, Kubernetes 将发送一个 preStop 事件。容器可以为每个事件指定一个处理程序。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

定义 postStart 和 preStop 处理函数

在本练习中,你将创建一个包含一个容器的 Pod,该容器为 postStart 和 preStop 事件提供对应的处理函数。

下面是对应 Pod 的配置文件:

apiVersion: v1
kind: Pod
metadata:
  name: lifecycle-demo
spec:
  containers:
  - name: lifecycle-demo-container
    image: nginx
    lifecycle:
      postStart:
        exec:
          command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
      preStop:
        exec:
          command: ["/bin/sh","-c","nginx -s quit; while killall -0 nginx; do sleep 1; done"]

在上述配置文件中,你可以看到 postStart 命令在容器的 /usr/share 目录下写入文件 message。 命令 preStop 负责优雅地终止 nginx 服务。当因为失效而导致容器终止时,这一处理方式很有用。

创建 Pod:

kubectl apply -f https://k8s.io/examples/pods/lifecycle-events.yaml

验证 Pod 中的容器已经运行:

kubectl get pod lifecycle-demo

使用 shell 连接到你的 Pod 里的容器:

kubectl exec -it lifecycle-demo -- /bin/bash

在 shell 中,验证 postStart 处理函数创建了 message 文件:

root@lifecycle-demo:/# cat /usr/share/message

命令行输出的是 postStart 处理函数所写入的文本:

Hello from the postStart handler

讨论

Kubernetes 在容器创建后立即发送 postStart 事件。 然而,postStart 处理函数的调用不保证早于容器的入口点(entrypoint) 的执行。postStart 处理函数与容器的代码是异步执行的,但 Kubernetes 的容器管理逻辑会一直阻塞等待 postStart 处理函数执行完毕。 只有 postStart 处理函数执行完毕,容器的状态才会变成 RUNNING。

Kubernetes 在容器结束前立即发送 preStop 事件。除非 Pod 宽限期限超时, Kubernetes 的容器管理逻辑会一直阻塞等待 preStop 处理函数执行完毕。 更多细节请参阅 Pod 的生命周期

接下来

参考

4.3.20 - 配置 Pod 使用 ConfigMap

很多应用在其初始化或运行期间要依赖一些配置信息。 大多数时候,存在要调整配置参数所设置的数值的需求。 ConfigMap 是 Kubernetes 的一种机制,可让你将配置数据注入到应用的 Pod 内部。

ConfigMap 概念允许你将配置清单与镜像内容分离,以保持容器化的应用程序的可移植性。 例如,你可以下载并运行相同的容器镜像来启动容器, 用于本地开发、系统测试或运行实时终端用户工作负载。

本页提供了一系列使用示例,这些示例演示了如何创建 ConfigMap 以及配置 Pod 使用存储在 ConfigMap 中的数据。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你需要安装 wget 工具。如果你有不同的工具,例如 curl,而没有 wget, 则需要调整下载示例数据的步骤。

创建 ConfigMap

你可以使用 kubectl create configmap 或者在 kustomization.yaml 中的 ConfigMap 生成器来创建 ConfigMap。

使用 kubectl create configmap 创建 ConfigMap

你可以使用 kubectl create configmap 命令基于目录文件或者字面值来创建 ConfigMap:

kubectl create configmap <映射名称> <数据源>

其中,<映射名称> 是为 ConfigMap 指定的名称,<数据源> 是要从中提取数据的目录、 文件或者字面值。ConfigMap 对象的名称必须是合法的 DNS 子域名

在你基于文件来创建 ConfigMap 时,<数据源> 中的键名默认取自文件的基本名, 而对应的值则默认为文件的内容。

你可以使用 kubectl describe 或者 kubectl get 获取有关 ConfigMap 的信息。

基于一个目录来创建 ConfigMap

你可以使用 kubectl create configmap 基于同一目录中的多个文件创建 ConfigMap。 当你基于目录来创建 ConfigMap 时,kubectl 识别目录下文件名可以作为合法键名的文件, 并将这些文件打包到新的 ConfigMap 中。普通文件之外的所有目录项都会被忽略 (例如:子目录、符号链接、设备、管道等等)。

创建本地目录:

mkdir -p configure-pod-container/configmap/

现在,下载示例的配置并创建 ConfigMap:

# 将示例文件下载到 `configure-pod-container/configmap/` 目录
wget https://kubernetes.io/examples/configmap/game.properties -O configure-pod-container/configmap/game.properties
wget https://kubernetes.io/examples/configmap/ui.properties -O configure-pod-container/configmap/ui.properties

# 创建 ConfigMap
kubectl create configmap game-config --from-file=configure-pod-container/configmap/

以上命令将 configure-pod-container/configmap 目录下的所有文件,也就是 game.propertiesui.properties 打包到 game-config ConfigMap 中。你可以使用下面的命令显示 ConfigMap 的详细信息:

kubectl describe configmaps game-config

输出类似以下内容:

Name:         game-config
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
game.properties:
----
enemies=aliens
lives=3
enemies.cheat=true
enemies.cheat.level=noGoodRotten
secret.code.passphrase=UUDDLRLRBABAS
secret.code.allowed=true
secret.code.lives=30
ui.properties:
----
color.good=purple
color.bad=yellow
allow.textmode=true
how.nice.to.look=fairlyNice

configure-pod-container/configmap/ 目录中的 game.propertiesui.properties 文件出现在 ConfigMap 的 data 部分。

kubectl get configmaps game-config -o yaml

输出类似以下内容:

apiVersion: v1
kind: ConfigMap
metadata:
  creationTimestamp: 2022-02-18T18:52:05Z
  name: game-config
  namespace: default
  resourceVersion: "516"
  uid: b4952dc3-d670-11e5-8cd0-68f728db1985
data:
  game.properties: |
    enemies=aliens
    lives=3
    enemies.cheat=true
    enemies.cheat.level=noGoodRotten
    secret.code.passphrase=UUDDLRLRBABAS
    secret.code.allowed=true
    secret.code.lives=30    
  ui.properties: |
    color.good=purple
    color.bad=yellow
    allow.textmode=true
    how.nice.to.look=fairlyNice    

基于文件创建 ConfigMap

你可以使用 kubectl create configmap 基于单个文件或多个文件创建 ConfigMap。

例如:

kubectl create configmap game-config-2 --from-file=configure-pod-container/configmap/game.properties

将产生以下 ConfigMap:

kubectl describe configmaps game-config-2

输出类似以下内容:

Name:         game-config-2
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
game.properties:
----
enemies=aliens
lives=3
enemies.cheat=true
enemies.cheat.level=noGoodRotten
secret.code.passphrase=UUDDLRLRBABAS
secret.code.allowed=true
secret.code.lives=30

你可以多次使用 --from-file 参数,从多个数据源创建 ConfigMap。

kubectl create configmap game-config-2 --from-file=configure-pod-container/configmap/game.properties --from-file=configure-pod-container/configmap/ui.properties

你可以使用以下命令显示 game-config-2 ConfigMap 的详细信息:

kubectl describe configmaps game-config-2

输出类似以下内容:

Name:         game-config-2
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
game.properties:
----
enemies=aliens
lives=3
enemies.cheat=true
enemies.cheat.level=noGoodRotten
secret.code.passphrase=UUDDLRLRBABAS
secret.code.allowed=true
secret.code.lives=30
ui.properties:
----
color.good=purple
color.bad=yellow
allow.textmode=true
how.nice.to.look=fairlyNice

使用 --from-env-file 选项基于 env 文件创建 ConfigMap,例如:

# Env 文件包含环境变量列表。其中适用以下语法规则:
# 这些语法规则适用:
#   Env 文件中的每一行必须为 VAR=VAL 格式。
#   以#开头的行(即注释)将被忽略。
#   空行将被忽略。
#   引号不会被特殊处理(即它们将成为 ConfigMap 值的一部分)。

# 将示例文件下载到 `configure-pod-container/configmap/` 目录
wget https://kubernetes.io/examples/configmap/game-env-file.properties -O configure-pod-container/configmap/game-env-file.properties
wget https://kubernetes.io/examples/configmap/ui-env-file.properties -O configure-pod-container/configmap/ui-env-file.properties

# Env 文件 `game-env-file.properties` 如下所示
cat configure-pod-container/configmap/game-env-file.properties
enemies=aliens
lives=3
allowed="true"

# 此注释和上方的空行将被忽略
kubectl create configmap game-config-env-file \
       --from-env-file=configure-pod-container/configmap/game-env-file.properties

将产生以下 ConfigMap。查看 ConfigMap:

kubectl get configmap game-config-env-file -o yaml

输出类似以下内容:

apiVersion: v1
kind: ConfigMap
metadata:
  creationTimestamp: 2019-12-27T18:36:28Z
  name: game-config-env-file
  namespace: default
  resourceVersion: "809965"
  uid: d9d1ca5b-eb34-11e7-887b-42010a8002b8
data:
  allowed: '"true"'
  enemies: aliens
  lives: "3"

从 Kubernetes 1.23 版本开始,kubectl 支持多次指定 --from-env-file 参数来从多个数据源创建 ConfigMap。

kubectl create configmap config-multi-env-files \
        --from-env-file=configure-pod-container/configmap/game-env-file.properties \
        --from-env-file=configure-pod-container/configmap/ui-env-file.properties

将产生以下 ConfigMap:

kubectl get configmap config-multi-env-files -o yaml

输出类似以下内容:

apiVersion: v1
kind: ConfigMap
metadata:
  creationTimestamp: 2019-12-27T18:38:34Z
  name: config-multi-env-files
  namespace: default
  resourceVersion: "810136"
  uid: 252c4572-eb35-11e7-887b-42010a8002b8
data:
  allowed: '"true"'
  color: purple
  enemies: aliens
  how: fairlyNice
  lives: "3"
  textmode: "true"

定义从文件创建 ConfigMap 时要使用的键

在使用 --from-file 参数时,你可以定义在 ConfigMap 的 data 部分出现键名, 而不是按默认行为使用文件名:

kubectl create configmap game-config-3 --from-file=<我的键名>=<文件路径>

<我的键名> 是你要在 ConfigMap 中使用的键名,<文件路径> 是你想要键所表示的数据源文件的位置。

例如:

kubectl create configmap game-config-3 --from-file=game-special-key=configure-pod-container/configmap/game.properties

将产生以下 ConfigMap:

kubectl get configmaps game-config-3 -o yaml

输出类似以下内容:

apiVersion: v1
kind: ConfigMap
metadata:
  creationTimestamp: 2022-02-18T18:54:22Z
  name: game-config-3
  namespace: default
  resourceVersion: "530"
  uid: 05f8da22-d671-11e5-8cd0-68f728db1985
data:
  game-special-key: |
    enemies=aliens
    lives=3
    enemies.cheat=true
    enemies.cheat.level=noGoodRotten
    secret.code.passphrase=UUDDLRLRBABAS
    secret.code.allowed=true
    secret.code.lives=30    

根据字面值创建 ConfigMap

你可以将 kubectl create configmap--from-literal 参数一起使用, 通过命令行定义文字值:

kubectl create configmap special-config --from-literal=special.how=very --from-literal=special.type=charm

你可以传入多个键值对。命令行中提供的每对键值在 ConfigMap 的 data 部分中均表示为单独的条目。

kubectl get configmaps special-config -o yaml

输出类似以下内容:

apiVersion: v1
kind: ConfigMap
metadata:
  creationTimestamp: 2022-02-18T19:14:38Z
  name: special-config
  namespace: default
  resourceVersion: "651"
  uid: dadce046-d673-11e5-8cd0-68f728db1985
data:
  special.how: very
  special.type: charm

基于生成器创建 ConfigMap

你还可以基于生成器(Generators)创建 ConfigMap,然后将其应用于集群的 API 服务器上创建对象。 生成器应在目录内的 kustomization.yaml 中指定。

基于文件生成 ConfigMap

例如,要基于 configure-pod-container/configmap/game.properties 文件生成一个 ConfigMap:

# 创建包含 ConfigMapGenerator 的 kustomization.yaml 文件
cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: game-config-4
  options:
    labels:
      game-config: config-4
  files:
  - configure-pod-container/configmap/game.properties
EOF

应用(Apply)kustomization 目录创建 ConfigMap 对象:

kubectl apply -k .
configmap/game-config-4-m9dm2f92bt created

你可以像这样检查 ConfigMap 已经被创建:

kubectl get configmap
NAME                       DATA   AGE
game-config-4-m9dm2f92bt   1      37s

也可以这样:

kubectl describe configmaps/game-config-4-m9dm2f92bt
Name:         game-config-4-m9dm2f92bt
Namespace:    default
Labels:       game-config=config-4
Annotations:  kubectl.kubernetes.io/last-applied-configuration:
                {"apiVersion":"v1","data":{"game.properties":"enemies=aliens\nlives=3\nenemies.cheat=true\nenemies.cheat.level=noGoodRotten\nsecret.code.p...

Data
====
game.properties:
----
enemies=aliens
lives=3
enemies.cheat=true
enemies.cheat.level=noGoodRotten
secret.code.passphrase=UUDDLRLRBABAS
secret.code.allowed=true
secret.code.lives=30
Events:  <none>

请注意,生成的 ConfigMap 名称具有通过对内容进行散列而附加的后缀, 这样可以确保每次修改内容时都会生成新的 ConfigMap。

定义从文件生成 ConfigMap 时要使用的键

在 ConfigMap 生成器中,你可以定义一个非文件名的键名。 例如,从 configure-pod-container/configmap/game.properties 文件生成 ConfigMap, 但使用 game-special-key 作为键名:

# 创建包含 ConfigMapGenerator 的 kustomization.yaml 文件
cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: game-config-5
  options:
    labels:
      game-config: config-5
  files:
  - game-special-key=configure-pod-container/configmap/game.properties
EOF

应用 Kustomization 目录创建 ConfigMap 对象。

kubectl apply -k .
configmap/game-config-5-m67dt67794 created

基于字面值生成 ConfigMap

此示例向你展示如何使用 Kustomize 和 kubectl,基于两个字面键/值对 special.type=charmspecial.how=very 创建一个 ConfigMap。 为了实现这一点,你可以配置 ConfigMap 生成器。 创建(或替换)kustomization.yaml,使其具有以下内容。

---
# 基于字面创建 ConfigMap 的 kustomization.yaml 内容
configMapGenerator:
- name: special-config-2
  literals:
  - special.how=very
  - special.type=charm
EOF

应用 Kustomization 目录创建 ConfigMap 对象。

kubectl apply -k .
configmap/special-config-2-c92b5mmcf2 created

临时清理

在继续之前,清理你创建的一些 ConfigMap:

kubectl delete configmap special-config
kubectl delete configmap env-config
kubectl delete configmap -l 'game-config in (config-4,config-5)'

现在你已经学会了定义 ConfigMap,你可以继续下一节,学习如何将这些对象与 Pod 一起使用。


使用 ConfigMap 数据定义容器环境变量

使用单个 ConfigMap 中的数据定义容器环境变量

  1. 在 ConfigMap 中将环境变量定义为键值对:

    kubectl create configmap special-config --from-literal=special.how=very
    
  1. 将 ConfigMap 中定义的 special.how 赋值给 Pod 规约中的 SPECIAL_LEVEL_KEY 环境变量。

    apiVersion: v1
    kind: Pod
    metadata:
      name: dapi-test-pod
    spec:
      containers:
        - name: test-container
          image: registry.k8s.io/busybox
          command: [ "/bin/sh", "-c", "env" ]
          env:
            # 定义环境变量
            - name: SPECIAL_LEVEL_KEY
              valueFrom:
                configMapKeyRef:
                  # ConfigMap 包含你要赋给 SPECIAL_LEVEL_KEY 的值
                  name: special-config
                  # 指定与取值相关的键名
                  key: special.how
      restartPolicy: Never
    

    创建 Pod:

    kubectl create -f https://kubernetes.io/examples/pods/pod-single-configmap-env-variable.yaml
    

    现在,Pod 的输出包含环境变量 SPECIAL_LEVEL_KEY=very

使用来自多个 ConfigMap 的数据定义容器环境变量

与前面的示例一样,首先创建 ConfigMap。 这是你将使用的清单:

apiVersion: v1
kind: ConfigMap
metadata:
  name: special-config
  namespace: default
data:
  special.how: very
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: env-config
  namespace: default
data:
  log_level: INFO
  • 创建 ConfigMap:

    kubectl create -f https://kubernetes.io/examples/configmap/configmaps.yaml
    
  • 在 Pod 规约中定义环境变量。

    apiVersion: v1
    kind: Pod
    metadata:
      name: dapi-test-pod
    spec:
      containers:
        - name: test-container
          image: registry.k8s.io/busybox
          command: [ "/bin/sh", "-c", "env" ]
          env:
            - name: SPECIAL_LEVEL_KEY
              valueFrom:
                configMapKeyRef:
                  name: special-config
                  key: special.how
            - name: LOG_LEVEL
              valueFrom:
                configMapKeyRef:
                  name: env-config
                  key: log_level
      restartPolicy: Never
    

    创建 Pod:

    kubectl create -f https://kubernetes.io/examples/pods/pod-multiple-configmap-env-variable.yaml
    

    现在,Pod 的输出包含环境变量 SPECIAL_LEVEL_KEY=veryLOG_LEVEL=INFO

    一旦你乐意继续前进,删除该 Pod:

    kubectl delete pod dapi-test-pod --now
    

将 ConfigMap 中的所有键值对配置为容器环境变量

  • 创建一个包含多个键值对的 ConfigMap。

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: special-config
      namespace: default
    data:
      SPECIAL_LEVEL: very
      SPECIAL_TYPE: charm
    

    创建 ConfigMap:

    kubectl create -f https://kubernetes.io/examples/configmap/configmap-multikeys.yaml
    
  • 使用 envFrom 将所有 ConfigMap 的数据定义为容器环境变量,ConfigMap 中的键成为 Pod 中的环境变量名称。

    apiVersion: v1
    kind: Pod
    metadata:
      name: dapi-test-pod
    spec:
      containers:
        - name: test-container
          image: registry.k8s.io/busybox
          command: [ "/bin/sh", "-c", "env" ]
          envFrom:
          - configMapRef:
              name: special-config
      restartPolicy: Never
    

    创建 Pod:

    kubectl create -f https://kubernetes.io/examples/pods/pod-configmap-envFrom.yaml
    

    现在,Pod 的输出包含环境变量 SPECIAL_LEVEL=verySPECIAL_TYPE=charm

    一旦你乐意继续前进,删除该 Pod:

    kubectl delete pod dapi-test-pod --now
    

在 Pod 命令中使用 ConfigMap 定义的环境变量

你可以使用 $(VAR_NAME) Kubernetes 替换语法在容器的 commandargs 属性中使用 ConfigMap 定义的环境变量。

例如,以下 Pod 清单:

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: registry.k8s.io/busybox
      command: [ "/bin/echo", "$(SPECIAL_LEVEL_KEY) $(SPECIAL_TYPE_KEY)" ]
      env:
        - name: SPECIAL_LEVEL_KEY
          valueFrom:
            configMapKeyRef:
              name: special-config
              key: SPECIAL_LEVEL
        - name: SPECIAL_TYPE_KEY
          valueFrom:
            configMapKeyRef:
              name: special-config
              key: SPECIAL_TYPE
  restartPolicy: Never

通过运行下面命令创建该 Pod:

kubectl create -f https://kubernetes.io/examples/pods/pod-configmap-env-var-valueFrom.yaml

此 Pod 在 test-container 容器中产生以下输出:

kubectl logs dapi-test-pod
very charm

一旦你乐意继续前进,删除该 Pod:

kubectl delete pod dapi-test-pod --now

将 ConfigMap 数据添加到一个卷中

如基于文件创建 ConfigMap 中所述,当你使用 --from-file 创建 ConfigMap 时,文件名成为存储在 ConfigMap 的 data 部分中的键, 文件内容成为键对应的值。

本节中的示例引用了一个名为 special-config 的 ConfigMap:

apiVersion: v1
kind: ConfigMap
metadata:
  name: special-config
  namespace: default
data:
  SPECIAL_LEVEL: very
  SPECIAL_TYPE: charm

创建 ConfigMap:

kubectl create -f https://kubernetes.io/examples/configmap/configmap-multikeys.yaml

使用存储在 ConfigMap 中的数据填充卷

在 Pod 规约的 volumes 部分下添加 ConfigMap 名称。 这会将 ConfigMap 数据添加到 volumeMounts.mountPath 所指定的目录 (在本例中为 /etc/config)。 command 部分列出了名称与 ConfigMap 中的键匹配的目录文件。

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: registry.k8s.io/busybox
      command: [ "/bin/sh", "-c", "ls /etc/config/" ]
      volumeMounts:
      - name: config-volume
        mountPath: /etc/config
  volumes:
    - name: config-volume
      configMap:
        # 提供包含要添加到容器中的文件的 ConfigMap 的名称
        name: special-config
  restartPolicy: Never

创建 Pod:

kubectl create -f https://kubernetes.io/examples/pods/pod-configmap-volume.yaml

Pod 运行时,命令 ls /etc/config/ 产生下面的输出:

SPECIAL_LEVEL
SPECIAL_TYPE

文本数据会展现为 UTF-8 字符编码的文件。如果使用其他字符编码, 可以使用 binaryData(详情参阅 ConfigMap 对象)。

一旦你乐意继续前进,删除该 Pod:

kubectl delete pod dapi-test-pod --now

将 ConfigMap 数据添加到卷中的特定路径

使用 path 字段为特定的 ConfigMap 项目指定预期的文件路径。 在这里,ConfigMap 中键 SPECIAL_LEVEL 的内容将挂载在 config-volume 卷中 /etc/config/keys 文件中。

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: registry.k8s.io/busybox
      command: [ "/bin/sh","-c","cat /etc/config/keys" ]
      volumeMounts:
      - name: config-volume
        mountPath: /etc/config
  volumes:
    - name: config-volume
      configMap:
        name: special-config
        items:
        - key: SPECIAL_LEVEL
          path: keys
  restartPolicy: Never

创建 Pod:

kubectl create -f https://kubernetes.io/examples/pods/pod-configmap-volume-specific-key.yaml

当 Pod 运行时,命令 cat /etc/config/keys 产生以下输出:

very

删除该 Pod:

kubectl delete pod dapi-test-pod --now

你可以将密钥投射到特定路径,语法请参阅 Secret 指南中的相应部分。 你可以设置密钥的 POSIX 权限,语法请参阅 Secret 指南中的相应部分。

映射键到指定路径并设置文件访问权限

你可以将指定键名投射到特定目录,也可以逐个文件地设定访问权限。 Secret 指南中为这一语法提供了解释。

可选引用

ConfigMap 引用可以被标记为可选。 如果 ConfigMap 不存在,则挂载的卷将为空。 如果 ConfigMap 存在,但引用的键不存在,则挂载点下的路径将不存在。 有关更多信息,请参阅可选 ConfigMap 细节。

挂载的 ConfigMap 会被自动更新

当已挂载的 ConfigMap 被更新时,所投射的内容最终也会被更新。 这适用于 Pod 启动后可选引用的 ConfigMap 重新出现的情况。

Kubelet 在每次定期同步时都会检查所挂载的 ConfigMap 是否是最新的。 然而,它使用其基于 TTL 机制的本地缓存来获取 ConfigMap 的当前值。 因此,从 ConfigMap 更新到新键映射到 Pod 的总延迟可能与 kubelet 同步周期(默认为 1 分钟)+ kubelet 中 ConfigMap 缓存的 TTL(默认为 1 分钟)一样长。 你可以通过更新 Pod 的一个注解来触发立即刷新。

了解 ConfigMap 和 Pod

ConfigMap API 资源将配置数据存储为键值对。 数据可以在 Pod 中使用,也可以用来提供系统组件(如控制器)的配置。 ConfigMap 与 Secret 类似, 但是提供的是一种处理不含敏感信息的字符串的方法。 用户和系统组件都可以在 ConfigMap 中存储配置数据。

ConfigMap 的 data 字段包含配置数据。如下例所示,它可以简单 (如用 --from-literal 的单个属性定义)或复杂 (如用 --from-file 的配置文件或 JSON blob 定义)。

apiVersion: v1
kind: ConfigMap
metadata:
  creationTimestamp: 2016-02-18T19:14:38Z
  name: example-config
  namespace: default
data:
  # 使用 --from-literal 定义的简单属性
  example.property.1: hello
  example.property.2: world
  # 使用 --from-file 定义复杂属性的例子
  example.property.file: |-
    property.1=value-1
    property.2=value-2
    property.3=value-3    

kubectl 从非 ASCII 或 UTF-8 编码的输入创建 ConfigMap 时, 该工具将这些输入放入 ConfigMap 的 binaryData 字段,而不是 data 字段。 文本和二进制数据源都可以组合在一个 ConfigMap 中。

如果你想查看 ConfigMap 中的 binaryData 键(及其值), 可以运行 kubectl get configmap -o jsonpath='{.binaryData}' <name>

Pod 可以从使用 databinaryData 的 ConfigMap 中加载数据。

可选的 ConfigMap

你可以在 Pod 规约中将对 ConfigMap 的引用标记为可选(optional)。 如果 ConfigMap 不存在,那么它在 Pod 中为其提供数据的配置(例如:环境变量、挂载的卷)将为空。 如果 ConfigMap 存在,但引用的键不存在,那么数据也是空的。

例如,以下 Pod 规约将 ConfigMap 中的环境变量标记为可选:

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: gcr.io/google_containers/busybox
      command: ["/bin/sh", "-c", "env"]
      env:
        - name: SPECIAL_LEVEL_KEY
          valueFrom:
            configMapKeyRef:
              name: a-config
              key: akey
              optional: true # 将环境变量标记为可选
  restartPolicy: Never

当你运行这个 Pod 并且名称为 a-config 的 ConfigMap 不存在时,输出空值。 当你运行这个 Pod 并且名称为 a-config 的 ConfigMap 存在, 但是在 ConfigMap 中没有名称为 akey 的键时,控制台输出也会为空。 如果你确实在名为 a-config 的 ConfigMap 中为 akey 设置了键值, 那么这个 Pod 会打印该值,然后终止。

你也可以在 Pod 规约中将 ConfigMap 提供的卷和文件标记为可选。 此时 Kubernetes 将总是为卷创建挂载路径,即使引用的 ConfigMap 或键不存在。 例如,以下 Pod 规约将所引用得 ConfigMap 的卷标记为可选:

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: gcr.io/google_containers/busybox
      command: ["/bin/sh", "-c", "ls /etc/config"]
      volumeMounts:
      - name: config-volume
        mountPath: /etc/config
  volumes:
    - name: config-volume
      configMap:
        name: no-config
        optional: true # 将引用的 ConfigMap 的卷标记为可选
  restartPolicy: Never

限制

  • 在 Pod 规约中引用某个 ConfigMap 之前,必须先创建这个对象, 或者在 Pod 规约中将 ConfigMap 标记为 optional(请参阅可选的 ConfigMaps)。 如果所引用的 ConfigMap 不存在,并且没有将应用标记为 optional 则 Pod 将无法启动。 同样,引用 ConfigMap 中不存在的主键也会令 Pod 无法启动,除非你将 Configmap 标记为 optional
  • 如果你使用 envFrom 来基于 ConfigMap 定义环境变量,那么无效的键将被忽略。 Pod 可以被启动,但无效名称将被记录在事件日志中(InvalidVariableNames)。 日志消息列出了每个被跳过的键。例如:

    kubectl get events
    

    输出与此类似:

    LASTSEEN FIRSTSEEN COUNT NAME          KIND  SUBOBJECT  TYPE      REASON                            SOURCE                MESSAGE
    0s       0s        1     dapi-test-pod Pod              Warning   InvalidEnvironmentVariableNames   {kubelet, 127.0.0.1}  Keys [1badkey, 2alsobad] from the EnvFrom configMap default/myconfig were skipped since they are considered invalid environment variable names.
    
  • ConfigMap 位于确定的名字空间中。 每个 ConfigMap 只能被同一名字空间中的 Pod 引用。
  • 你不能将 ConfigMap 用于静态 Pod, 因为 Kubernetes 不支持这种用法。

清理现场

删除你创建那些的 ConfigMap 和 Pod:

kubectl delete configmaps/game-config configmaps/game-config-2 configmaps/game-config-3 \
               configmaps/game-config-env-file
kubectl delete pod dapi-test-pod --now

# 你可能已经删除了下一组内容
kubectl delete configmaps/special-config configmaps/env-config
kubectl delete configmap -l 'game-config in (config-4,config-5)'

如果你创建了一个目录 configure-pod-container 并且不再需要它,你也应该删除这个目录, 或者将该目录移动到回收站/删除文件的位置。

接下来

4.3.21 - 在 Pod 中的容器之间共享进程命名空间

此页面展示如何为 Pod 配置进程命名空间共享。 当启用进程命名空间共享时,容器中的进程对同一 Pod 中的所有其他容器都是可见的。

你可以使用此功能来配置协作容器,比如日志处理 sidecar 容器, 或者对那些不包含诸如 shell 等调试实用工具的镜像进行故障排查。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

配置 Pod

使用 Pod .spec 中的 shareProcessNamespace 字段可以启用进程命名空间共享。例如:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  shareProcessNamespace: true
  containers:
  - name: nginx
    image: nginx
  - name: shell
    image: busybox:1.28
    securityContext:
      capabilities:
        add:
        - SYS_PTRACE
    stdin: true
    tty: true
  1. 在集群中创建 nginx Pod:

    kubectl apply -f https://k8s.io/examples/pods/share-process-namespace.yaml
    
  1. 获取容器 shell,执行 ps

    kubectl attach -it nginx -c shell
    

    如果没有看到命令提示符,请按 enter 回车键。在容器 shell 中:

    # 在 “shell” 容器中运行以下命令
    ps ax
    

    输出类似于:

    PID   USER     TIME  COMMAND
        1 root      0:00 /pause
        8 root      0:00 nginx: master process nginx -g daemon off;
       14 101       0:00 nginx: worker process
       15 root      0:00 sh
       21 root      0:00 ps ax
    

你可以在其他容器中对进程发出信号。例如,发送 SIGHUPnginx 以重启工作进程。 此操作需要 SYS_PTRACE 权能。

# 在 “shell” 容器中运行以下命令
kill -HUP 8   # 如有必要,更改 “8” 以匹配 nginx 领导进程的 PID
ps ax

输出类似于:

PID   USER     TIME  COMMAND
    1 root      0:00 /pause
    8 root      0:00 nginx: master process nginx -g daemon off;
   15 root      0:00 sh
   22 101       0:00 nginx: worker process
   23 root      0:00 ps ax

甚至可以使用 /proc/$pid/root 链接访问另一个容器的文件系统。

# 在 “shell” 容器中运行以下命令
# 如有必要,更改 “8” 为 Nginx 进程的 PID
head /proc/8/root/etc/nginx/nginx.conf

输出类似于:

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;

理解进程命名空间共享

Pod 共享许多资源,因此它们共享进程命名空间是很有意义的。 不过,有些容器可能希望与其他容器隔离,因此了解这些差异很重要:

  1. 容器进程不再具有 PID 1。 在没有 PID 1 的情况下,一些容器拒绝启动 (例如,使用 systemd 的容器),或者拒绝执行 kill -HUP 1 之类的命令来通知容器进程。 在具有共享进程命名空间的 Pod 中,kill -HUP 1 将通知 Pod 沙箱(在上面的例子中是 /pause)。
  1. 进程对 Pod 中的其他容器可见。 这包括 /proc 中可见的所有信息, 例如作为参数或环境变量传递的密码。这些仅受常规 Unix 权限的保护。
  1. 容器文件系统通过 /proc/$pid/root 链接对 Pod 中的其他容器可见。 这使调试更加容易, 但也意味着文件系统安全性只受文件系统权限的保护。

4.3.22 - 为 Pod 配置 user 名字空间

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

本页展示如何为无状态 Pod 配置 user 名字空间。可以将容器内的用户与主机上的用户隔离开来。

在容器中以 root 用户运行的进程可以以不同的(非 root)用户在宿主机上运行;换句话说, 进程在 user 名字空间内部拥有执行操作的全部特权,但在 user 名字空间外部并没有执行操作的特权。

你可以使用这个特性来减少有害的容器对同一宿主机上其他容器的影响。 有些安全脆弱性问题被评为 HIGHCRITICAL,但当 user 名字空间被启用时, 它们是无法被利用的。相信 user 名字空间也能减轻一些未来的漏洞影响。

在不使用 user 名字空间的情况下,对于以 root 用户运行的容器而言,发生容器逃逸时, 容器将拥有在宿主机上的 root 特权。如果容器被赋予了某些权限,则这些权限在宿主机上同样有效。 当使用 user 名字空间时这些都不可能发生。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你的 Kubernetes 服务器版本必须不低于版本 v1.25. 要获知版本信息,请输入 kubectl version.

  • 节点的操作系统必须为 Linux
  • 你需要在宿主机上执行命令
  • 你需要能够通过 exec 操作进入 Pod
  • 你需要启用 UserNamespacesSupport 特性门控

你所使用的集群必须包括至少一个符合 要求 的节点,以便为 Pod 配置 user 名字空间。

如果你有混合节点,并且只有部分节点支持为 Pod 配置 user 名字空间, 你还需要确保配置了 user 名字空间的 Pod 被调度到合适的节点。

运行一个使用 user 名字空间的 Pod

为一个 Pod 启用 user 名字空间需要设置 .spechostUsers 字段为 false。例如:

apiVersion: v1
kind: Pod
metadata:
  name: userns
spec:
  hostUsers: false
  containers:
  - name: shell
    command: ["sleep", "infinity"]
    image: debian
  1. 在你的集群上创建 Pod:

    kubectl apply -f https://k8s.io/examples/pods/user-namespaces-stateless.yaml
    
  1. 挂接到容器上并执行 readlink /proc/self/ns/user

    kubectl attach -it userns bash
    

运行这个命令:

readlink /proc/self/ns/user

输出类似于:

user:[4026531837]

还运行:

cat /proc/self/uid_map

输出类似于:

0  833617920      65536

然后,在主机中打开一个 Shell 并运行相同的命令。

readlink 命令显示进程运行所在的用户命名空间。在主机上和容器内运行时应该有所不同。

容器内 uid_map 文件的最后一个数字必须是 65536,在主机上它必须是更大的数字。

如果你在 user 名字空间中运行 kubelet,则需要将在 Pod 中运行命令的输出与在主机中运行的输出进行比较:

readlink /proc/$pid/ns/user

使用 kubelet 的进程号代替 $pid

4.3.23 - 创建静态 Pod

静态 Pod 在指定的节点上由 kubelet 守护进程直接管理,不需要 API 服务器监管。 与由控制面管理的 Pod(例如,Deployment) 不同;kubelet 监视每个静态 Pod(在它失败之后重新启动)。

静态 Pod 始终都会绑定到特定节点的 Kubelet 上。

kubelet 会尝试通过 Kubernetes API 服务器为每个静态 Pod 自动创建一个镜像 Pod。 这意味着节点上运行的静态 Pod 对 API 服务来说是可见的,但是不能通过 API 服务器来控制。 Pod 名称将把以连字符开头的节点主机名作为后缀。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

本文假定你在使用 Docker 来运行 Pod, 并且你的节点是运行着 Fedora 操作系统。 其它发行版或者 Kubernetes 部署版本上操作方式可能不一样。

创建静态 Pod

可以通过文件系统上的配置文件或者 Web 网络上的配置文件来配置静态 Pod。

文件系统上的静态 Pod 声明文件

声明文件是标准的 Pod 定义文件,以 JSON 或者 YAML 格式存储在指定目录。路径设置在 Kubelet 配置文件staticPodPath: <目录> 字段,kubelet 会定期的扫描这个文件夹下的 YAML/JSON 文件来创建/删除静态 Pod。 注意 kubelet 扫描目录的时候会忽略以点开头的文件。

例如:下面是如何以静态 Pod 的方式启动一个简单 web 服务:

  1. 选择一个要运行静态 Pod 的节点。在这个例子中选择 my-node1

    ssh my-node1
    
  1. 选择一个目录,比如在 /etc/kubernetes/manifests 目录来保存 Web 服务 Pod 的定义文件,例如 /etc/kubernetes/manifests/static-web.yaml

    # 在 kubelet 运行的节点上执行以下命令
    mkdir -p /etc/kubernetes/manifests/
    cat <<EOF >/etc/kubernetes/manifests/static-web.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: static-web
      labels:
        role: myrole
    spec:
      containers:
        - name: web
          image: nginx
          ports:
            - name: web
              containerPort: 80
              protocol: TCP
    EOF
    
  1. 在该节点上配置 kubelet,在 kubelet 配置文件中设定 staticPodPath 值。 欲了解更多信息,请参考通过配置文件设定 kubelet 参数

    另一个已弃用的方法是,在该节点上通过命令行参数配置 kubelet,以便从本地查找静态 Pod 清单。 若使用这种弃用的方法,请启动 kubelet 时加上 --pod-manifest-path=/etc/kubernetes/manifests/ 参数。

  1. 重启 kubelet。在 Fedora 上,你将使用下面的命令:

    # 在 kubelet 运行的节点上执行以下命令
    systemctl restart kubelet
    

Web 网上的静态 Pod 声明文件

Kubelet 根据 --manifest-url=<URL> 参数的配置定期的下载指定文件,并且转换成 JSON/YAML 格式的 Pod 定义文件。 与文件系统上的清单文件使用方式类似,kubelet 调度获取清单文件。 如果静态 Pod 的清单文件有改变,kubelet 会应用这些改变。

按照下面的方式来:

  1. 创建一个 YAML 文件,并保存在 Web 服务器上,这样你就可以将该文件的 URL 传递给 kubelet。

    apiVersion: v1
    kind: Pod
    metadata:
      name: static-web
      labels:
        role: myrole
    spec:
      containers:
        - name: web
          image: nginx
          ports:
            - name: web
              containerPort: 80
              protocol: TCP
    
  1. 通过在选择的节点上使用 --manifest-url=<manifest-url> 配置运行 kubelet。 在 Fedora 添加下面这行到 /etc/kubernetes/kubelet

    KUBELET_ARGS="--cluster-dns=10.254.0.10 --cluster-domain=kube.local --manifest-url=<manifest-url>"
    
  1. 重启 kubelet。在 Fedora 上,你将运行如下命令:

    # 在 kubelet 运行的节点上执行以下命令
    systemctl restart kubelet
    

观察静态 Pod 的行为

当 kubelet 启动时,会自动启动所有定义的静态 Pod。 当定义了一个静态 Pod 并重新启动 kubelet 时,新的静态 Pod 就应该已经在运行了。

可以在节点上运行下面的命令来查看正在运行的容器(包括静态 Pod):

# 在 kubelet 运行的节点上执行以下命令
crictl ps

输出可能会像这样:

CONTAINER       IMAGE                                 CREATED           STATE      NAME    ATTEMPT    POD ID
129fd7d382018   docker.io/library/nginx@sha256:...    11 minutes ago    Running    web     0          34533c6729106

可以在 API 服务上看到镜像 Pod:

kubectl get pods
NAME                  READY   STATUS    RESTARTS        AGE
static-web-my-node1   1/1     Running   0               2m

静态 Pod 上的标签被传播到镜像 Pod。 你可以通过选择算符使用这些标签。

如果你用 kubectl 从 API 服务上删除镜像 Pod,kubelet 不会移除静态 Pod:

kubectl delete pod static-web-my-node1
pod "static-web-my-node1" deleted

可以看到 Pod 还在运行:

kubectl get pods
NAME                  READY   STATUS    RESTARTS   AGE
static-web-my-node1   1/1     Running   0          4s

回到 kubelet 运行所在的节点上,你可以手动停止容器。 可以看到过了一段时间后 kubelet 会发现容器停止了并且会自动重启 Pod:

# 在 kubelet 运行的节点上执行以下命令
# 把 ID 换为你的容器的 ID
crictl stop 129fd7d382018
sleep 20
crictl ps
CONTAINER       IMAGE                                 CREATED           STATE      NAME    ATTEMPT    POD ID
89db4553e1eeb   docker.io/library/nginx@sha256:...    19 seconds ago    Running    web     1          34533c6729106

一旦你找到合适的容器,你就可以使用 crictl 获取该容器的日志。

# 在容器运行所在的节点上执行以下命令
crictl logs <container_id>
10.240.0.48 - - [16/Nov/2022:12:45:49 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.47.0" "-"
10.240.0.48 - - [16/Nov/2022:12:45:50 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.47.0" "-"
10.240.0.48 - - [16/Nove/2022:12:45:51 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.47.0" "-"

若要找到如何使用 crictl 进行调试的更多信息, 请访问使用 crictl 对 Kubernetes 节点进行调试

动态增加和删除静态 Pod

运行中的 kubelet 会定期扫描配置的目录(比如例子中的 /etc/kubernetes/manifests 目录)中的变化, 并且根据文件中出现/消失的 Pod 来添加/删除 Pod。

# 这里假定你在用主机文件系统上的静态 Pod 配置文件
# 在容器运行所在的节点上执行以下命令
mv /etc/kubernetes/manifests/static-web.yaml /tmp
sleep 20
crictl ps
# 可以看到没有 nginx 容器在运行
mv /tmp/static-web.yaml  /etc/kubernetes/manifests/
sleep 20
crictl ps
CONTAINER       IMAGE                                 CREATED           STATE      NAME    ATTEMPT    POD ID
f427638871c35   docker.io/library/nginx@sha256:...    19 seconds ago    Running    web     1          34533c6729106

接下来

4.3.24 - 将 Docker Compose 文件转换为 Kubernetes 资源

Kompose 是什么?它是一个转换工具,可将 compose (即 Docker Compose)所组装的所有内容转换成容器编排器(Kubernetes 或 OpenShift)可识别的形式。

更多信息请参考 Kompose 官网 http://kompose.io

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

安装 Kompose

我们有很多种方式安装 Kompose。首选方式是从最新的 GitHub 发布页面下载二进制文件。

Kompose 通过 GitHub 发布,发布周期为三星期。 你可以在 GitHub 发布页面上看到所有当前版本。

# Linux
curl -L https://github.com/kubernetes/kompose/releases/download/v1.26.0/kompose-linux-amd64 -o kompose

# macOS
curl -L https://github.com/kubernetes/kompose/releases/download/v1.26.0/kompose-darwin-amd64 -o kompose

# Windows
curl -L https://github.com/kubernetes/kompose/releases/download/v1.26.0/kompose-windows-amd64.exe -o kompose.exe

chmod +x kompose
sudo mv ./kompose /usr/local/bin/kompose

或者,你可以下载 tar 包

go get 命令从主分支拉取最新的开发变更的方法安装 Kompose。

go get -u github.com/kubernetes/kompose

Kompose 位于 EPEL CentOS 代码仓库。 如果你还没有安装并启用 EPEL 代码仓库, 请运行命令 sudo yum install epel-release

如果你的系统中已经启用了 EPEL, 你就可以像安装其他软件包一样安装 Kompose。

sudo yum -y install kompose

Kompose 位于 Fedora 24、25 和 26 的代码仓库。你可以像安装其他软件包一样安装 Kompose。

sudo dnf -y install kompose

在 macOS 上你可以通过 Homebrew 安装 Kompose 的最新版本:

brew install kompose

使用 Kompose

只需几步,我们就把你从 Docker Compose 带到 Kubernetes。 你只需要一个现有的 docker-compose.yml 文件。

  1. 进入 docker-compose.yml 文件所在的目录。如果没有,请使用下面这个进行测试。

    version: "2"
    
    services:
    
      redis-master:
        image: registry.k8s.io/redis:e2e
        ports:
          - "6379"
    
      redis-slave:
        image: gcr.io/google_samples/gb-redisslave:v3
        ports:
          - "6379"
        environment:
          - GET_HOSTS_FROM=dns
    
      frontend:
        image: gcr.io/google-samples/gb-frontend:v4
        ports:
          - "80:80"
        environment:
          - GET_HOSTS_FROM=dns
        labels:
          kompose.service.type: LoadBalancer
    
  1. 要将 docker-compose.yml 转换为 kubectl 可用的文件,请运行 kompose convert 命令进行转换,然后运行 kubectl apply -f <output file> 进行创建。

    kompose convert                           
    

    输出类似于:

    INFO Kubernetes file "frontend-tcp-service.yaml" created 
    INFO Kubernetes file "redis-master-service.yaml" created 
    INFO Kubernetes file "redis-slave-service.yaml" created 
    INFO Kubernetes file "frontend-deployment.yaml" created 
    INFO Kubernetes file "redis-master-deployment.yaml" created 
    INFO Kubernetes file "redis-slave-deployment.yaml" created
    
    kubectl apply -f frontend-tcp-service.yaml,redis-master-service.yaml,redis-slave-service.yaml,frontend-deployment.yaml,redis-master-deployment.yaml,redis-slave-deployment.yaml
    

    输出类似于:

    service/frontend-tcp created
    service/redis-master created
    service/redis-slave created
    deployment.apps/frontend created
    deployment.apps/redis-master created
    deployment.apps/redis-slave created
    

    你部署的应用在 Kubernetes 中运行起来了。

  1. 访问你的应用。

    如果你在开发过程中使用 minikube,请执行:

    minikube service frontend
    

    否则,我们要查看一下你的服务使用了什么 IP!

    kubectl describe svc frontend
    
    Name:                     frontend-tcp
    Namespace:                default
    Labels:                   io.kompose.service=frontend-tcp
    Annotations:              kompose.cmd: kompose convert
                              kompose.service.type: LoadBalancer
                              kompose.version: 1.26.0 (40646f47)
    Selector:                 io.kompose.service=frontend
    Type:                     LoadBalancer
    IP Family Policy:         SingleStack
    IP Families:              IPv4
    IP:                       10.43.67.174
    IPs:                      10.43.67.174
    Port:                     80  80/TCP
    TargetPort:               80/TCP
    NodePort:                 80  31254/TCP
    Endpoints:                10.42.0.25:80
    Session Affinity:         None
    External Traffic Policy:  Cluster
    Events:
      Type    Reason                Age   From                Message
      ----    ------                ----  ----                -------
      Normal  EnsuringLoadBalancer  62s   service-controller  Ensuring load balancer
      Normal  AppliedDaemonSet      62s   service-controller  Applied LoadBalancer DaemonSet kube-system/svclb-frontend-tcp-9362d276
    

    如果你使用的是云驱动,你的 IP 将在 LoadBalancer Ingress 字段给出。

    curl http://192.0.2.89
    
  1. 清理。

    你完成示例应用 Deployment 的测试之后,只需在 Shell 中运行以下命令,就能删除用过的资源。

    kubectl delete -f frontend-tcp-service.yaml,redis-master-service.yaml,redis-slave-service.yaml,frontend-deployment.yaml,redis-master-deployment.yaml,redis-slave-deployment.yaml
    

用户指南

Kompose 支持两种驱动:OpenShift 和 Kubernetes。 你可以通过全局选项 --provider 选择驱动。如果没有指定, 会将 Kubernetes 作为默认驱动。

kompose convert

Kompose 支持将 V1、V2 和 V3 版本的 Docker Compose 文件转换为 Kubernetes 和 OpenShift 资源对象。

Kubernetes kompose convert 示例

kompose --file docker-voting.yml convert
WARN Unsupported key networks - ignoring
WARN Unsupported key build - ignoring
INFO Kubernetes file "worker-svc.yaml" created
INFO Kubernetes file "db-svc.yaml" created
INFO Kubernetes file "redis-svc.yaml" created
INFO Kubernetes file "result-svc.yaml" created
INFO Kubernetes file "vote-svc.yaml" created
INFO Kubernetes file "redis-deployment.yaml" created
INFO Kubernetes file "result-deployment.yaml" created
INFO Kubernetes file "vote-deployment.yaml" created
INFO Kubernetes file "worker-deployment.yaml" created
INFO Kubernetes file "db-deployment.yaml" created
ls
db-deployment.yaml  docker-compose.yml         docker-gitlab.yml  redis-deployment.yaml  result-deployment.yaml  vote-deployment.yaml  worker-deployment.yaml
db-svc.yaml         docker-voting.yml          redis-svc.yaml     result-svc.yaml        vote-svc.yaml           worker-svc.yaml

你也可以同时提供多个 docker-compose 文件进行转换:

kompose -f docker-compose.yml -f docker-guestbook.yml convert
INFO Kubernetes file "frontend-service.yaml" created         
INFO Kubernetes file "mlbparks-service.yaml" created         
INFO Kubernetes file "mongodb-service.yaml" created          
INFO Kubernetes file "redis-master-service.yaml" created     
INFO Kubernetes file "redis-slave-service.yaml" created      
INFO Kubernetes file "frontend-deployment.yaml" created      
INFO Kubernetes file "mlbparks-deployment.yaml" created      
INFO Kubernetes file "mongodb-deployment.yaml" created       
INFO Kubernetes file "mongodb-claim0-persistentvolumeclaim.yaml" created
INFO Kubernetes file "redis-master-deployment.yaml" created  
INFO Kubernetes file "redis-slave-deployment.yaml" created   
ls
mlbparks-deployment.yaml  mongodb-service.yaml                       redis-slave-service.jsonmlbparks-service.yaml  
frontend-deployment.yaml  mongodb-claim0-persistentvolumeclaim.yaml  redis-master-service.yaml
frontend-service.yaml     mongodb-deployment.yaml                    redis-slave-deployment.yaml
redis-master-deployment.yaml

当提供多个 docker-compose 文件时,配置将会合并。任何通用的配置都将被后续文件覆盖。

OpenShift kompose convert 示例

kompose --provider openshift --file docker-voting.yml convert
WARN [worker] Service cannot be created because of missing port.
INFO OpenShift file "vote-service.yaml" created             
INFO OpenShift file "db-service.yaml" created               
INFO OpenShift file "redis-service.yaml" created            
INFO OpenShift file "result-service.yaml" created           
INFO OpenShift file "vote-deploymentconfig.yaml" created    
INFO OpenShift file "vote-imagestream.yaml" created         
INFO OpenShift file "worker-deploymentconfig.yaml" created  
INFO OpenShift file "worker-imagestream.yaml" created       
INFO OpenShift file "db-deploymentconfig.yaml" created      
INFO OpenShift file "db-imagestream.yaml" created           
INFO OpenShift file "redis-deploymentconfig.yaml" created   
INFO OpenShift file "redis-imagestream.yaml" created        
INFO OpenShift file "result-deploymentconfig.yaml" created  
INFO OpenShift file "result-imagestream.yaml" created  

kompose 还支持为服务中的构建指令创建 buildconfig。 默认情况下,它使用当前 git 分支的 remote 仓库作为源仓库,使用当前分支作为构建的源分支。 你可以分别使用 --build-repo--build-branch 选项指定不同的源仓库和分支。

kompose --provider openshift --file buildconfig/docker-compose.yml convert
WARN [foo] Service cannot be created because of missing port.
INFO OpenShift Buildconfig using git@github.com:rtnpro/kompose.git::master as source.
INFO OpenShift file "foo-deploymentconfig.yaml" created     
INFO OpenShift file "foo-imagestream.yaml" created          
INFO OpenShift file "foo-buildconfig.yaml" created

其他转换方式

默认的 kompose 转换会生成 yaml 格式的 Kubernetes DeploymentService 对象。 你可以选择通过 -j 参数生成 json 格式的对象。 你也可以替换生成 Replication Controllers 对象、 DaemonSetHelm Chart。

kompose convert -j
INFO Kubernetes file "redis-svc.json" created
INFO Kubernetes file "web-svc.json" created
INFO Kubernetes file "redis-deployment.json" created
INFO Kubernetes file "web-deployment.json" created

*-deployment.json 文件中包含 Deployment 对象。

kompose convert --replication-controller
INFO Kubernetes file "redis-svc.yaml" created
INFO Kubernetes file "web-svc.yaml" created
INFO Kubernetes file "redis-replicationcontroller.yaml" created
INFO Kubernetes file "web-replicationcontroller.yaml" created

*-replicationcontroller.yaml 文件包含 Replication Controller 对象。 如果你想指定副本数(默认为 1),可以使用 --replicas 参数: kompose convert --replication-controller --replicas 3

kompose convert --daemon-set
INFO Kubernetes file "redis-svc.yaml" created
INFO Kubernetes file "web-svc.yaml" created
INFO Kubernetes file "redis-daemonset.yaml" created
INFO Kubernetes file "web-daemonset.yaml" created

*-daemonset.yaml 文件包含 DaemonSet 对象。

如果你想生成 Helm 可用的 Chart, 只需简单的执行下面的命令:

kompose convert -c
INFO Kubernetes file "web-svc.yaml" created
INFO Kubernetes file "redis-svc.yaml" created
INFO Kubernetes file "web-deployment.yaml" created
INFO Kubernetes file "redis-deployment.yaml" created
chart created in "./docker-compose/"
tree docker-compose/
docker-compose
├── Chart.yaml
├── README.md
└── templates
    ├── redis-deployment.yaml
    ├── redis-svc.yaml
    ├── web-deployment.yaml
    └── web-svc.yaml

这个 Chart 结构旨在为构建 Helm Chart 提供框架。

标签

kompose 支持 docker-compose.yml 文件中用于 Kompose 的标签, 以便在转换时明确定义 Service 的行为。

  • kompose.service.type 定义要创建的 Service 类型。例如:

    version: "2"
    services:
      nginx:
        image: nginx
        dockerfile: foobar
        build: ./foobar
        cap_add:
          - ALL
        container_name: foobar
        labels:
          kompose.service.type: nodeport
    
  • kompose.service.expose 定义是否允许从集群外部访问 Service。 如果该值被设置为 "true",提供程序将自动设置端点, 对于任何其他值,该值将被设置为主机名。 如果在 Service 中定义了多个端口,则选择第一个端口作为公开端口。

    • 如果使用 Kubernetes 驱动,会有一个 Ingress 资源被创建,并且假定已经配置了相应的 Ingress 控制器。
    • 如果使用 OpenShift 驱动,则会有一个 route 被创建。

    例如:

    version: "2"
    services:
      web:
        image: tuna/docker-counter23
        ports:
         - "5000:5000"
        links:
         - redis
        labels:
          kompose.service.expose: "counter.example.com"
      redis:
        image: redis:3.0
        ports:
         - "6379"
    

当前支持的选项有:

kompose.service.typenodeport / clusterip / loadbalancer
kompose.service.exposetrue / hostname

重启

如果你想创建没有控制器的普通 Pod,可以使用 docker-compose 的 restart 结构来指定这一行为。请参考下表了解 restart 的不同参数。

docker-compose restart创建的对象Pod restartPolicy
""控制器对象Always
always控制器对象Always
on-failurePodOnFailure
noPodNever

例如,pival Service 将在这里变成 Pod。这个容器计算 pi 的取值。

version: '2'

services:
  pival:
    image: perl
    command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
    restart: "on-failure"

关于 Deployment Config 的提醒

如果 Docker Compose 文件中为服务声明了卷,Deployment(Kubernetes)或 DeploymentConfig(OpenShift)策略会从 “RollingUpdate”(默认)变为 “Recreate”。 这样做的目的是为了避免服务的多个实例同时访问卷。

如果 Docker Compose 文件中的服务名包含 _(例如 web_service), 那么将会被替换为 -,服务也相应的会重命名(例如 web-service)。 Kompose 这样做的原因是 “Kubernetes” 不允许对象名称中包含 _

请注意,更改服务名称可能会破坏一些 docker-compose 文件。

Docker Compose 版本

Kompose 支持的 Docker Compose 版本包括:1、2 和 3。 对 2.1 和 3.2 版本的支持还有限,因为它们还在实验阶段。

所有三个版本的兼容性列表, 请查看我们的转换文档, 文档中列出了所有不兼容的 Docker Compose 关键字。

4.3.25 - 通过配置内置准入控制器实施 Pod 安全标准

Kubernetes 提供一种内置的准入控制器 用来强制实施 Pod 安全性标准。 你可以配置此准入控制器来设置集群范围的默认值和豁免选项

准备开始

Pod 安全性准入(Pod Security Admission)在 Kubernetes v1.22 作为 Alpha 特性发布, 在 Kubernetes v1.23 中作为 Beta 特性默认可用。从 1.25 版本起, 此特性进阶至正式发布(Generally Available)。

要获知版本信息,请输入 kubectl version.

如果未运行 Kubernetes 1.30, 你可以切换到与当前运行的 Kubernetes 版本所对应的文档。

配置准入控制器

apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: PodSecurity
  configuration:
    apiVersion: pod-security.admission.config.k8s.io/v1
    kind: PodSecurityConfiguration
    # 当未设置 mode 标签时会应用的默认设置
    #
    # level 标签必须是以下取值之一:
    # - "privileged" (默认)
    # - "baseline"
    # - "restricted"
    #
    # version 标签必须是如下取值之一:
    # - "latest" (默认) 
    # - 诸如 "v1.30" 这类版本号
    defaults:
      enforce: "privileged"
      enforce-version: "latest"
      audit: "privileged"
      audit-version: "latest"
      warn: "privileged"
      warn-version: "latest"
    exemptions:
      # 要豁免的已认证用户名列表
      usernames: []
      # 要豁免的运行时类名称列表
      runtimeClasses: []
      # 要豁免的名字空间列表
      namespaces: []

4.3.26 - 使用名字空间标签来实施 Pod 安全性标准

名字空间可以打上标签以强制执行 Pod 安全性标准特权(privileged)基线(baseline)受限(restricted) 这三种策略涵盖了广泛安全范围,并由 Pod 安全准入控制器实现。

准备开始

Pod 安全性准入(Pod Security Admission)在 Kubernetes v1.23 中作为 Beta 特性默认可用。 从 1.25 版本起,此特性进阶至正式发布(Generally Available)。

要获知版本信息,请输入 kubectl version.

通过名字空间标签来要求实施 baseline Pod 容器标准

下面的清单定义了一个 my-baseline-namespace 名字空间,其中

  • 阻止任何不满足 baseline 策略要求的 Pod;
  • 针对任何无法满足 restricted 策略要求的、已创建的 Pod 为用户生成警告信息, 并添加审计注解;
  • baselinerestricted 策略的版本锁定到 v1.30。
apiVersion: v1
kind: Namespace
metadata:
  name: my-baseline-namespace
  labels:
    pod-security.kubernetes.io/enforce: baseline
    pod-security.kubernetes.io/enforce-version: v1.30

    # 我们将这些标签设置为我们所 _期望_ 的 `enforce` 级别
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/audit-version: v1.30
    pod-security.kubernetes.io/warn: restricted
    pod-security.kubernetes.io/warn-version: v1.30

使用 kubectl label 为现有名字空间添加标签

在刚开始为名字空间评估安全性策略变更时,使用 --dry-run 标志是很有用的。 Pod 安全性标准会在 dry run(试运行) 模式下运行,在这种模式下会生成新策略如何处理现有 Pod 的信息, 但不会真正更新策略。

kubectl label --dry-run=server --overwrite ns --all \
    pod-security.kubernetes.io/enforce=baseline

应用到所有名字空间

如果你是刚刚开始使用 Pod 安全性标准,一种比较合适的初始步骤是针对所有名字空间为类似 baseline 这种比较严格的安全级别配置审计注解。

kubectl label --overwrite ns --all \
  pod-security.kubernetes.io/audit=baseline \
  pod-security.kubernetes.io/warn=baseline

注意,这里没有设置 enforce 级别,因而没有被显式评估的名字空间可以被识别出来。 你可以使用下面的命令列举那些没有显式设置 enforce 级别的名字空间:

kubectl get namespaces --selector='!pod-security.kubernetes.io/enforce'

应用到单个名字空间

你也可以更新特定的名字空间。下面的命令将 enforce=restricted 策略应用到 my-existing-namespace 名字空间,将 restricted 策略的版本锁定到 v1.30。

kubectl label --overwrite ns my-existing-namespace \
  pod-security.kubernetes.io/enforce=restricted \
  pod-security.kubernetes.io/enforce-version=v1.30

4.3.27 - 从 PodSecurityPolicy 迁移到内置的 PodSecurity 准入控制器

本页面描述从 PodSecurityPolicy 迁移到内置的 PodSecurity 准入控制器的过程。 这一迁移过程可以通过综合使用试运行、auditwarn 模式等来实现, 尽管在使用了变更式 PSP 时会变得有些困难。

准备开始

你的 Kubernetes 服务器版本必须不低于版本 v1.22. 要获知版本信息,请输入 kubectl version.

如果你目前运行的 Kubernetes 版本不是 1.30, 你可能要切换本页面以查阅你实际所运行的 Kubernetes 版本文档。

本页面假定你已经熟悉 Pod 安全性准入的基本概念。

方法概览

你可以采取多种策略来完成从 PodSecurityPolicy 到 Pod 安全性准入 (Pod Security Admission)的迁移。 下面是一种可能的迁移路径,其目标是尽可能降低生产环境不可用的风险, 以及安全性仍然不足的风险。

  1. 确定 Pod 安全性准入是否对于你的使用场景而言比较合适。
  2. 审查名字空间访问权限。
  3. 简化、标准化 PodSecurityPolicy。
  4. 更新名字空间:
    1. 确定合适的 Pod 安全性级别;
    2. 验证该 Pod 安全性级别可工作;
    3. 实施该 Pod 安全性级别;
    4. 绕过 PodSecurityPolicy。
  5. 审阅名字空间创建过程。
  6. 禁用 PodSecurityPolicy。

0. 确定是否 Pod 安全性准入适合你

Pod 安全性准入被设计用来直接满足最常见的安全性需求,并提供一组可用于多个集群的安全性级别。 不过,这一机制比 PodSecurityPolicy 的灵活度要低。 值得注意的是,PodSecurityPolicy 所支持的以下特性是 Pod 安全性准入所不支持的:

  • 设置默认的安全性约束 - Pod 安全性准入是一个非变更性质的准入控制器, 这就意味着它不会在对 Pod 进行合法性检查之前更改其配置。如果你之前依赖于 PSP 的这方面能力, 你或者需要更改你的负载以满足 Pod 安全性约束,或者需要使用一个 变更性质的准入 Webhook 来执行相应的变更。进一步的细节可参见后文的简化和标准化 PodSecurityPolicy
  • 粒度小于名字空间的策略 - PodSecurityPolicy 允许你为不同的服务账户或用户绑定不同策略, 即使这些服务账户或用户隶属于同一个名字空间。这一方法有很多缺陷,不建议使用。 不过如果你的确需要这种功能,你就需要使用第三方的 Webhook。 唯一的例外是当你只需要完全针对某用户或者 RuntimeClasses 赋予豁免规则时, Pod 安全性准入的确也为豁免规则暴露一些 静态配置

即便 Pod 安全性准入无法满足你的所有需求,该机制也是设计用作其他策略实施机制的 补充,因此可以和其他准入 Webhook 一起运行,进而提供一种有用的兜底机制。

1. 审查名字空间访问权限

Pod 安全性准入是通过名字空间上的标签 来控制的。这也就是说,任何能够更新(或通过 patch 部分更新或创建) 名字空间的人都可以更改该名字空间的 Pod 安全性级别,而这可能会被利用来绕过约束性更强的策略。 在继续执行迁移操作之前,请确保只有被信任的、有特权的用户具有这类名字空间访问权限。 不建议将这类强大的访问权限授予不应获得权限提升的用户,不过如果你必须这样做, 你需要使用一个准入 Webhook 来针对为 Namespace 对象设置 Pod 安全性级别设置额外的约束。

2. 简化、标准化 PodSecurityPolicy

在本节中,你会削减变更性质的 PodSecurityPolicy,去掉 Pod 安全性标准范畴之外的选项。 针对要修改的、已存在的 PodSecurityPolicy,你应该将这里所建议的更改写入到其离线副本中。 所克隆的 PSP 应该与原来的副本名字不同,并且按字母序要排到原副本之前 (例如,可以向 PSP 名字前加一个 0)。 先不要在 Kubernetes 中创建新的策略 - 这类操作会在后文的推出更新的策略部分讨论。

2.a. 去掉纯粹变更性质的字段

如果某个 PodSecurityPolicy 能够变更字段,你可能会在关掉 PodSecurityPolicy 时发现有些 Pod 无法满足 Pod 安全性级别。为避免这类状况, 你应该在执行切换操作之前去掉所有 PSP 的变更操作。 不幸的是,PSP 没有对变更性和验证性字段做清晰的区分,所以这一迁移操作也不够简单直接。

你可以先去掉那些纯粹变更性质的字段,留下验证策略中的其他内容。 这些字段(也列举于将 PodSecurityPolicy 映射到 Pod 安全性标准参考中) 包括:

  • .spec.defaultAllowPrivilegeEscalation
  • .spec.runtimeClass.defaultRuntimeClassName
  • .metadata.annotations['seccomp.security.alpha.kubernetes.io/defaultProfileName']
  • .metadata.annotations['apparmor.security.beta.kubernetes.io/defaultProfileName']
  • .spec.defaultAddCapabilities - 尽管理论上是一个混合了变更性与验证性功能的字段, 这里的设置应该被合并到 .spec.allowedCapabilities 中,后者会执行相同的验证操作, 但不会执行任何变更动作。

2.b. 去掉 Pod 安全性标准未涉及的选项

PodSecurityPolicy 中有一些字段未被 Pod 安全性准入机制覆盖。如果你必须使用这些选项, 你需要在 Pod 安全性准入之外部署准入 Webhook 以补充这一能力,而这类操作不在本指南范围。

首先,你可以去掉 Pod 安全性标准所未覆盖的那些验证性字段。这些字段 (也列举于将 PodSecurityPolicy 映射到 Pod 安全性标准参考中, 标记为“无意见”)有:

  • .spec.allowedHostPaths
  • .spec.allowedFlexVolumes
  • .spec.allowedCSIDrivers
  • .spec.forbiddenSysctls
  • .spec.runtimeClass

你也可以去掉以下字段,这些字段与 POSIX/UNIX 用户组控制有关。

  • .spec.runAsGroup
  • .spec.supplementalGroups
  • .spec.fsGroup

剩下的变更性字段是为了适当支持 Pod 安全性标准所需要的,因而需要逐个处理:

  • .spec.requiredDropCapabilities - 需要此字段来为 Restricted 配置去掉 ALL 设置。
  • .spec.seLinux - (仅针对带有 MustRunAs 规则的变更性设置)需要此字段来满足 Baseline 和 Restricted 配置所需要的 SELinux 需求。
  • .spec.runAsUser - (仅针对带有 RunAsAny 规则的非变更性设置)需要此字段来为 Restricted 配置保证 RunAsNonRoot
  • .spec.allowPrivilegeEscalation - (如果设置为 false 则为变更性设置) 需要此字段来支持 Restricted 配置。

2.c. 推出更新的 PSP

接下来,你可以将更新后的策略推出到你的集群上。在继续操作时,你要非常小心, 因为去掉变更性质的选项可能导致有些工作负载缺少必需的配置。

针对更新后的每个 PodSecurityPolicy:

  1. 识别运行于原 PSP 之下的 Pod。可以通过 kubernetes.io/psp 注解来完成。 例如,使用 kubectl:

    PSP_NAME="original" # 设置你要检查的 PSP 的名称
    kubectl get pods --all-namespaces -o jsonpath="{range .items[?(@.metadata.annotations.kubernetes\.io\/psp=='$PSP_NAME')]}{.metadata.namespace} {.metadata.name}{'\n'}{end}"
    
  1. 比较运行中的 Pod 与原来的 Pod 规约,确定 PodSecurityPolicy 是否更改过这些 Pod。 对于通过工作负载资源所创建的 Pod, 你可以比较 Pod 和控制器资源中的 PodTemplate。如果发现任何变更,则原来的 Pod 或者 PodTemplate 需要被更新以加上所希望的配置。要审查的字段包括:

    • .metadata.annotations['container.apparmor.security.beta.kubernetes.io/*'] (将 * 替换为每个容器的名称)
    • .spec.runtimeClassName
    • .spec.securityContext.fsGroup
    • .spec.securityContext.seccompProfile
    • .spec.securityContext.seLinuxOptions
    • .spec.securityContext.supplementalGroups
    • 对于容器,在 .spec.containers[*].spec.initContainers[*] 之下,检查下面字段:
    • .securityContext.allowPrivilegeEscalation
    • .securityContext.capabilities.add
    • .securityContext.capabilities.drop
    • .securityContext.readOnlyRootFilesystem
    • .securityContext.runAsGroup
    • .securityContext.runAsNonRoot
    • .securityContext.runAsUser
    • .securityContext.seccompProfile
    • .securityContext.seLinuxOptions
  1. 创建新的 PodSecurityPolicy。如果存在 Role 或 ClusterRole 对象为用户授权了在所有 PSP 上使用 use 动词的权限,则所使用的的会是新创建的 PSP 而不是其变更性的副本。
  2. 更新你的鉴权配置,为访问新的 PSP 授权。在 RBAC 机制下,这意味着需要更新所有为原 PSP 授予 use 访问权限的 Role 或 ClusterRole 对象,使之也对更新后的 PSP 授权。
  1. 验证:经过一段时间后,重新执行步骤 1 中所给的命令,查看是否有 Pod 仍在使用原来的 PSP。 注意,在新的策略被推出到集群之后,Pod 需要被重新创建才可以执行全面验证。
  2. (可选)一旦你已经验证原来的 PSP 不再被使用,你就可以删除这些 PSP。

3. 更新名字空间

下面的步骤需要在集群中的所有名字空间上执行。所列步骤中的命令使用变量 $NAMESPACE 来引用所更新的名字空间。

3.a. 识别合适的 Pod 安全级别

首先请回顾 Pod 安全性标准内容, 并了解三个安全级别。

为你的名字空间选择 Pod 安全性级别有几种方法:

  1. 根据名字空间的安全性需求来确定 - 如果你熟悉某名字空间的预期访问级别, 你可以根据这类需求来选择合适的安全级别,就像大家在为新集群确定安全级别一样。
  1. 根据现有的 PodSecurityPolicy 来确定 - 基于将 PodSecurityPolicy 映射到 Pod 安全性标准 参考资料,你可以将各个 PSP 映射到某个 Pod 安全性标准级别。如果你的 PSP 不是基于 Pod 安全性标准的,你可能或者需要选择一个至少与该 PSP 一样宽松的级别, 或者选择一个至少与其一样严格的级别。使用下面的命令你可以查看被 Pod 使用的 PSP 有哪些:

    kubectl get pods -n $NAMESPACE -o jsonpath="{.items[*].metadata.annotations.kubernetes\.io\/psp}" | tr " " "\n" | sort -u
    
  1. 根据现有 Pod 来确定 - 使用检查 Pod 安全性级别小节所述策略, 你可以测试 Baseline 和 Restricted 级别,检查它们是否对于现有负载而言足够宽松, 并选择二者之间特权级较低的合法级别。

3.b. 检查 Pod 安全性级别

一旦你已经为名字空间选择了 Pod 安全性级别(或者你正在尝试多个不同级别), 先进行测试是个不错的主意(如果使用 Privileged 级别,则可略过此步骤)。 Pod 安全性包含若干工具可用来测试和安全地推出安全性配置。

首先,你可以试运行新策略,这个过程可以针对所应用的策略评估当前在名字空间中运行的 Pod,但不会令新策略马上生效:

# $LEVEL 是要试运行的级别,可以是 "baseline" 或 "restricted"
kubectl label --dry-run=server --overwrite ns $NAMESPACE pod-security.kubernetes.io/enforce=$LEVEL

此命令会针对在所提议的级别下不再合法的所有 现存 Pod 返回警告信息。

第二种办法在抓取当前未运行的负载方面表现的更好:audit 模式。 运行于 audit 模式(而非 enforcing 模式)下时,违反策略级别的 Pod 会被记录到审计日志中, 经过一段时间后可以在日志中查看到,但这些 Pod 不会被拒绝。 warning 模式的工作方式与此类似,不过会立即向用户返回告警信息。 你可以使用下面的命令为名字空间设置 audit 模式的级别:

kubectl label --overwrite ns $NAMESPACE pod-security.kubernetes.io/audit=$LEVEL

当以上两种方法输出意料之外的违例状况时,你就需要或者更新发生违例的负载以满足策略需求, 或者放宽名字空间上的 Pod 安全性级别。

3.c. 实施 Pod 安全性级别

当你对可以安全地在名字空间上实施的级别比较满意时,你可以更新名字空间来实施所期望的级别:

kubectl label --overwrite ns $NAMESPACE pod-security.kubernetes.io/enforce=$LEVEL

3.d. 绕过 PodSecurityPolicy

最后,你可以通过将 完全特权的 PSP 绑定到某名字空间中所有服务账户上,在名字空间层面绕过所有 PodSecurityPolicy。

# 下面集群范围的命令只需要执行一次
kubectl apply -f privileged-psp.yaml
kubectl create clusterrole privileged-psp --verb use --resource podsecuritypolicies.policy --resource-name privileged

# 逐个名字空间地禁用
kubectl create -n $NAMESPACE rolebinding disable-psp --clusterrole privileged-psp --group system:serviceaccounts:$NAMESPACE

由于特权 PSP 是非变更性的,PSP 准入控制器总是优选非变更性的 PSP, 上面的操作会确保对应名字空间中的所有 Pod 不再会被 PodSecurityPolicy 所更改或限制。

按上述操作逐个名字空间地禁用 PodSecurityPolicy 这种做法的好处是, 如果出现问题,你可以很方便地通过删除 RoleBinding 来回滚所作的更改。 你所要做的只是确保之前存在的 PodSecurityPolicy 还在。

# 撤销 PodSecurityPolicy 的禁用
kubectl delete -n $NAMESPACE rolebinding disable-psp

4. 审阅名字空间创建过程

现在,现有的名字空间都已被更新,强制实施 Pod 安全性准入, 你应该确保你用来管控新名字空间创建的流程与/或策略也被更新,这样合适的 Pod 安全性配置会被应用到新的名字空间上。

你也可以静态配置 Pod 安全性准入控制器,为尚未打标签的名字空间设置默认的 enforce、audit 与/或 warn 级别。 详细信息可参阅配置准入控制器页面。

5. 禁用 PodSecurityPolicy

最后,你已为禁用 PodSecurityPolicy 做好准备。要禁用 PodSecurityPolicy, 你需要更改 API 服务器上的准入配置: 我如何关闭某个准入控制器?

如果需要验证 PodSecurityPolicy 准入控制器不再被启用,你可以通过扮演某个无法访问任何 PodSecurityPolicy 的用户来执行测试(参见 PodSecurityPolicy 示例), 或者通过检查 API 服务器的日志来进行验证。在启动期间,API 服务器会输出日志行,列举所挂载的准入控制器插件。

I0218 00:59:44.903329      13 plugins.go:158] Loaded 16 mutating admission controller(s) successfully in the following order: NamespaceLifecycle,LimitRanger,ServiceAccount,NodeRestriction,TaintNodesByCondition,Priority,DefaultTolerationSeconds,ExtendedResourceToleration,PersistentVolumeLabel,DefaultStorageClass,StorageObjectInUseProtection,RuntimeClass,DefaultIngressClass,MutatingAdmissionWebhook.
I0218 00:59:44.903350      13 plugins.go:161] Loaded 14 validating admission controller(s) successfully in the following order: LimitRanger,ServiceAccount,PodSecurity,Priority,PersistentVolumeClaimResize,RuntimeClass,CertificateApproval,CertificateSigning,CertificateSubjectRestriction,DenyServiceExternalIPs,ValidatingAdmissionWebhook,ResourceQuota.

你应该会看到 PodSecurity(在 validating admission controllers 列表中), 并且两个列表中都不应该包含 PodSecurityPolicy

一旦你确定 PSP 准入控制器已被禁用(并且这种状况已经持续了一段时间, 这样你才会比较确定不需要回滚),你就可以放心地删除你的 PodSecurityPolicy 以及所关联的所有 Role、ClusterRole、RoleBinding、ClusterRoleBinding 等对象 (仅需要确保他们不再授予其他不相关的访问权限)。

4.4 - 监控、日志和调试

设置监控和日志记录以对集群进行故障排除或调试容器化应用程序。

有时候事情会出错。本指南旨在解决这些问题。它包含两个部分:

  • 应用排错 - 针对部署代码到 Kubernetes 并想知道代码为什么不能正常运行的用户。
  • 集群排错 - 针对集群管理员以及 Kubernetes 集群表现异常的用户。

你也应该查看所用发行版本的已知问题。

获取帮助

如果你的问题在上述指南中没有得到答案,你还有另外几种方式从 Kubernetes 团队获得帮助。

问题

本网站上的文档针对回答各类问题进行了结构化组织和分类。 概念部分解释 Kubernetes 体系结构以及每个组件的工作方式, 安装部分提供了安装的实用说明。 任务部分展示了如何完成常用任务, 教程部分则提供对现实世界、特定行业或端到端开发场景的更全面的演练。 参考部分提供了详细的 Kubernetes API 文档 和命令行 (CLI) 接口的文档,例如kubectl

求救!我的问题还没有解决!我现在需要帮助!

Stack Exchange、Stack Overflow 或 Server Fault

若你对容器化应用有软件开发相关的疑问,你可以在 Stack Overflow 上询问。

若你有集群管理配置相关的疑问,你可以在 Server Fault 上询问。

还有几个更专业的 Stack Exchange 网站,很适合在这些地方询问有关 DevOps软件工程信息安全 (InfoSec) 领域中 Kubernetes 的问题。

社区中的其他人可能已经问过和你类似的问题,也可能能够帮助解决你的问题。

Kubernetes 团队还会监视带有 Kubernetes 标签的帖子。 如果现有的问题对你没有帮助,请在问一个新问题之前,确保你的问题切合 Stack OverflowServer Fault 或 Stack Exchange 的主题, 并通读如何提出新问题的指导说明!

Slack

Kubernetes 社区中有很多人在 #kubernetes-users 这一 Slack 频道聚集。 Slack 需要注册;你可以请求一份邀请, 并且注册是对所有人开放的。欢迎你随时来问任何问题。 一旦注册了,就可以访问通过 Web 浏览器或者 Slack 专用的应用访问 Slack 上的 Kubernetes 组织

一旦你完成了注册,就可以浏览各种感兴趣主题的频道列表(一直在增长)。 例如,Kubernetes 新人可能还想加入 #kubernetes-novice 频道。又比如,开发人员应该加入 #kubernetes-contributors 频道。

还有许多国家/地区语言频道。请随时加入这些频道以获得本地化支持和信息:

Country / language specific Slack channels
国家频道
中国#cn-users, #cn-events
芬兰#fi-users
法国#fr-users, #fr-events
德国#de-users, #de-events
印度#in-users, #in-events
意大利#it-users, #it-events
日本#jp-users, #jp-events
韩国#kr-users
荷兰#nl-users
挪威#norw-users
波兰#pl-users
俄罗斯#ru-users
西班牙#es-users
瑞典#se-users
土耳其#tr-users, #tr-events

论坛

欢迎你加入 Kubernetes 官方论坛 discuss.kubernetes.io

Bug 和功能请求

如果你发现一个看起来像 Bug 的问题,或者你想提出一个功能请求,请使用 GitHub 问题跟踪系统

在提交问题之前,请搜索现有问题列表以查看是否其中已涵盖你的问题。

如果提交 Bug,请提供如何重现问题的详细信息,例如:

  • Kubernetes 版本:kubectl version
  • 云平台、OS 发行版、网络配置和 Docker 版本
  • 重现问题的步骤

4.4.1 - 集群故障排查

调试常见的集群问题。

本篇文档是介绍集群故障排查的;我们假设对于你碰到的问题,你已经排除了是由应用程序造成的。 对于应用的调试,请参阅应用故障排查指南。 你也可以访问故障排查来获取更多的信息。

有关 kubectl 的故障排查, 请参阅 kubectl 故障排查

列举集群节点

调试的第一步是查看所有的节点是否都已正确注册。

运行以下命令:

kubectl get nodes

验证你所希望看见的所有节点都能够显示出来,并且都处于 Ready 状态。

为了了解你的集群的总体健康状况详情,你可以运行:

kubectl cluster-info dump

示例:调试关闭/无法访问的节点

有时在调试时查看节点的状态很有用 —— 例如,因为你注意到在节点上运行的 Pod 的奇怪行为, 或者找出为什么 Pod 不会调度到节点上。与 Pod 一样,你可以使用 kubectl describe nodekubectl get node -o yaml 来检索有关节点的详细信息。 例如,如果节点关闭(与网络断开连接,或者 kubelet 进程挂起并且不会重新启动等), 你将看到以下内容。请注意显示节点为 NotReady 的事件,并注意 Pod 不再运行(它们在 NotReady 状态五分钟后被驱逐)。

kubectl get nodes
NAME                     STATUS       ROLES     AGE     VERSION
kube-worker-1            NotReady     <none>    1h      v1.23.3
kubernetes-node-bols     Ready        <none>    1h      v1.23.3
kubernetes-node-st6x     Ready        <none>    1h      v1.23.3
kubernetes-node-unaj     Ready        <none>    1h      v1.23.3
kubectl describe node kube-worker-1
Name:               kube-worker-1
Roles:              <none>
Labels:             beta.kubernetes.io/arch=amd64
                    beta.kubernetes.io/os=linux
                    kubernetes.io/arch=amd64
                    kubernetes.io/hostname=kube-worker-1
                    kubernetes.io/os=linux
Annotations:        kubeadm.alpha.kubernetes.io/cri-socket: /run/containerd/containerd.sock
                    node.alpha.kubernetes.io/ttl: 0
                    volumes.kubernetes.io/controller-managed-attach-detach: true
CreationTimestamp:  Thu, 17 Feb 2022 16:46:30 -0500
Taints:             node.kubernetes.io/unreachable:NoExecute
                    node.kubernetes.io/unreachable:NoSchedule
Unschedulable:      false
Lease:
  HolderIdentity:  kube-worker-1
  AcquireTime:     <unset>
  RenewTime:       Thu, 17 Feb 2022 17:13:09 -0500
Conditions:
  Type                 Status    LastHeartbeatTime                 LastTransitionTime                Reason              Message
  ----                 ------    -----------------                 ------------------                ------              -------
  NetworkUnavailable   False     Thu, 17 Feb 2022 17:09:13 -0500   Thu, 17 Feb 2022 17:09:13 -0500   WeaveIsUp           Weave pod has set this
  MemoryPressure       Unknown   Thu, 17 Feb 2022 17:12:40 -0500   Thu, 17 Feb 2022 17:13:52 -0500   NodeStatusUnknown   Kubelet stopped posting node status.
  DiskPressure         Unknown   Thu, 17 Feb 2022 17:12:40 -0500   Thu, 17 Feb 2022 17:13:52 -0500   NodeStatusUnknown   Kubelet stopped posting node status.
  PIDPressure          Unknown   Thu, 17 Feb 2022 17:12:40 -0500   Thu, 17 Feb 2022 17:13:52 -0500   NodeStatusUnknown   Kubelet stopped posting node status.
  Ready                Unknown   Thu, 17 Feb 2022 17:12:40 -0500   Thu, 17 Feb 2022 17:13:52 -0500   NodeStatusUnknown   Kubelet stopped posting node status.
Addresses:
  InternalIP:  192.168.0.113
  Hostname:    kube-worker-1
Capacity:
  cpu:                2
  ephemeral-storage:  15372232Ki
  hugepages-2Mi:      0
  memory:             2025188Ki
  pods:               110
Allocatable:
  cpu:                2
  ephemeral-storage:  14167048988
  hugepages-2Mi:      0
  memory:             1922788Ki
  pods:               110
System Info:
  Machine ID:                 9384e2927f544209b5d7b67474bbf92b
  System UUID:                aa829ca9-73d7-064d-9019-df07404ad448
  Boot ID:                    5a295a03-aaca-4340-af20-1327fa5dab5c
  Kernel Version:             5.13.0-28-generic
  OS Image:                   Ubuntu 21.10
  Operating System:           linux
  Architecture:               amd64
  Container Runtime Version:  containerd://1.5.9
  Kubelet Version:            v1.23.3
  Kube-Proxy Version:         v1.23.3
Non-terminated Pods:          (4 in total)
  Namespace                   Name                                 CPU Requests  CPU Limits  Memory Requests  Memory Limits  Age
  ---------                   ----                                 ------------  ----------  ---------------  -------------  ---
  default                     nginx-deployment-67d4bdd6f5-cx2nz    500m (25%)    500m (25%)  128Mi (6%)       128Mi (6%)     23m
  default                     nginx-deployment-67d4bdd6f5-w6kd7    500m (25%)    500m (25%)  128Mi (6%)       128Mi (6%)     23m
  kube-system                 kube-proxy-dnxbz                     0 (0%)        0 (0%)      0 (0%)           0 (0%)         28m
  kube-system                 weave-net-gjxxp                      100m (5%)     0 (0%)      200Mi (10%)      0 (0%)         28m
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource           Requests     Limits
  --------           --------     ------
  cpu                1100m (55%)  1 (50%)
  memory             456Mi (24%)  256Mi (13%)
  ephemeral-storage  0 (0%)       0 (0%)
  hugepages-2Mi      0 (0%)       0 (0%)
Events:
...
kubectl get node kube-worker-1 -o yaml
apiVersion: v1
kind: Node
metadata:
  annotations:
    kubeadm.alpha.kubernetes.io/cri-socket: /run/containerd/containerd.sock
    node.alpha.kubernetes.io/ttl: "0"
    volumes.kubernetes.io/controller-managed-attach-detach: "true"
  creationTimestamp: "2022-02-17T21:46:30Z"
  labels:
    beta.kubernetes.io/arch: amd64
    beta.kubernetes.io/os: linux
    kubernetes.io/arch: amd64
    kubernetes.io/hostname: kube-worker-1
    kubernetes.io/os: linux
  name: kube-worker-1
  resourceVersion: "4026"
  uid: 98efe7cb-2978-4a0b-842a-1a7bf12c05f8
spec: {}
status:
  addresses:
  - address: 192.168.0.113
    type: InternalIP
  - address: kube-worker-1
    type: Hostname
  allocatable:
    cpu: "2"
    ephemeral-storage: "14167048988"
    hugepages-2Mi: "0"
    memory: 1922788Ki
    pods: "110"
  capacity:
    cpu: "2"
    ephemeral-storage: 15372232Ki
    hugepages-2Mi: "0"
    memory: 2025188Ki
    pods: "110"
  conditions:
  - lastHeartbeatTime: "2022-02-17T22:20:32Z"
    lastTransitionTime: "2022-02-17T22:20:32Z"
    message: Weave pod has set this
    reason: WeaveIsUp
    status: "False"
    type: NetworkUnavailable
  - lastHeartbeatTime: "2022-02-17T22:20:15Z"
    lastTransitionTime: "2022-02-17T22:13:25Z"
    message: kubelet has sufficient memory available
    reason: KubeletHasSufficientMemory
    status: "False"
    type: MemoryPressure
  - lastHeartbeatTime: "2022-02-17T22:20:15Z"
    lastTransitionTime: "2022-02-17T22:13:25Z"
    message: kubelet has no disk pressure
    reason: KubeletHasNoDiskPressure
    status: "False"
    type: DiskPressure
  - lastHeartbeatTime: "2022-02-17T22:20:15Z"
    lastTransitionTime: "2022-02-17T22:13:25Z"
    message: kubelet has sufficient PID available
    reason: KubeletHasSufficientPID
    status: "False"
    type: PIDPressure
  - lastHeartbeatTime: "2022-02-17T22:20:15Z"
    lastTransitionTime: "2022-02-17T22:15:15Z"
    message: kubelet is posting ready status. AppArmor enabled
    reason: KubeletReady
    status: "True"
    type: Ready
  daemonEndpoints:
    kubeletEndpoint:
      Port: 10250
  nodeInfo:
    architecture: amd64
    bootID: 22333234-7a6b-44d4-9ce1-67e31dc7e369
    containerRuntimeVersion: containerd://1.5.9
    kernelVersion: 5.13.0-28-generic
    kubeProxyVersion: v1.23.3
    kubeletVersion: v1.23.3
    machineID: 9384e2927f544209b5d7b67474bbf92b
    operatingSystem: linux
    osImage: Ubuntu 21.10
    systemUUID: aa829ca9-73d7-064d-9019-df07404ad448

查看日志

目前,深入挖掘集群需要登录相关机器。以下是相关日志文件的位置。 在基于 systemd 的系统上,你可能需要使用 journalctl 而不是检查日志文件。

控制平面节点

  • /var/log/kube-apiserver.log —— API 服务器,负责提供 API 服务
  • /var/log/kube-scheduler.log —— 调度器,负责制定调度决策
  • /var/log/kube-controller-manager.log —— 运行大多数 Kubernetes 内置控制器的组件,除了调度(kube-scheduler 处理调度)。

工作节点

  • /var/log/kubelet.log —— 负责在节点运行容器的 kubelet 所产生的日志
  • /var/log/kube-proxy.log —— 负责将流量转发到服务端点的 kube-proxy 所产生的日志

集群故障模式

这是可能出错的事情的不完整列表,以及如何调整集群设置以缓解问题。

故障原因

  • 虚拟机关闭
  • 集群内或集群与用户之间的网络分区
  • Kubernetes 软件崩溃
  • 持久存储(例如 GCE PD 或 AWS EBS 卷)的数据丢失或不可用
  • 操作员错误,例如配置错误的 Kubernetes 软件或应用程序软件

具体情况

  • API 服务器所在的 VM 关机或者 API 服务器崩溃
    • 结果
      • 不能停止、更新或者启动新的 Pod、服务或副本控制器
      • 现有的 Pod 和服务在不依赖 Kubernetes API 的情况下应该能继续正常工作
  • API 服务器的后端存储丢失
    • 结果
      • kube-apiserver 组件未能成功启动并变健康
      • kubelet 将不能访问 API 服务器,但是能够继续运行之前的 Pod 和提供相同的服务代理
      • 在 API 服务器重启之前,需要手动恢复或者重建 API 服务器的状态
  • Kubernetes 服务组件(节点控制器、副本控制器管理器、调度器等)所在的 VM 关机或者崩溃
    • 当前,这些控制器是和 API 服务器在一起运行的,它们不可用的现象是与 API 服务器类似的
    • 将来,这些控制器也会复制为多份,并且可能不在运行于同一节点上
    • 它们没有自己的持久状态
  • 单个节点(VM 或者物理机)关机
    • 结果
      • 此节点上的所有 Pod 都停止运行
  • 网络分裂
    • 结果
      • 分区 A 认为分区 B 中所有的节点都已宕机;分区 B 认为 API 服务器宕机 (假定主控节点所在的 VM 位于分区 A 内)。
  • kubelet 软件故障
    • 结果
      • 崩溃的 kubelet 就不能在其所在的节点上启动新的 Pod
      • kubelet 可能删掉 Pod 或者不删
      • 节点被标识为非健康态
      • 副本控制器会在其它的节点上启动新的 Pod
  • 集群操作错误
    • 结果
      • 丢失 Pod 或服务等等
      • 丢失 API 服务器的后端存储
      • 用户无法读取 API
      • 等等

缓解措施

  • 措施:对于 IaaS 上的 VM,使用 IaaS 的自动 VM 重启功能

    • 缓解:API 服务器 VM 关机或 API 服务器崩溃
    • 缓解:Kubernetes 服务组件所在的 VM 关机或崩溃
  • 措施: 对于运行 API 服务器和 etcd 的 VM,使用 IaaS 提供的可靠的存储(例如 GCE PD 或者 AWS EBS 卷)

    • 缓解:API 服务器后端存储的丢失
  • 措施:使用高可用性的配置

    • 缓解:主控节点 VM 关机或者主控节点组件(调度器、API 服务器、控制器管理器)崩溃
      • 将容许一个或多个节点或组件同时出现故障
    • 缓解:API 服务器后端存储(例如 etcd 的数据目录)丢失
      • 假定你使用了高可用的 etcd 配置
  • 措施:定期对 API 服务器的 PD 或 EBS 卷执行快照操作

    • 缓解:API 服务器后端存储丢失
    • 缓解:一些操作错误的场景
    • 缓解:一些 Kubernetes 软件本身故障的场景
  • 措施:在 Pod 的前面使用副本控制器或服务

    • 缓解:节点关机
    • 缓解:kubelet 软件故障
  • 措施:应用(容器)设计成容许异常重启

    • 缓解:节点关机
    • 缓解:kubelet 软件故障

接下来

4.4.1.1 - kubectl 故障排查

本文讲述研判和诊断 kubectl 相关的问题。 如果你在访问 kubectl 或连接到集群时遇到问题,本文概述了各种常见的情况和可能的解决方案, 以帮助确定和解决可能的原因。

准备开始

  • 你需要有一个 Kubernetes 集群。
  • 你还需要安装好 kubectl,参见安装工具

验证 kubectl 设置

确保你已在本机上正确安装和配置了 kubectl。 检查 kubectl 版本以确保其是最新的,并与你的集群兼容。

检查 kubectl 版本:

kubectl version

你将看到类似的输出:

Client Version: version.Info{Major:"1", Minor:"27", GitVersion:"v1.27.4",GitCommit:"fa3d7990104d7c1f16943a67f11b154b71f6a132", GitTreeState:"clean",BuildDate:"2023-07-19T12:20:54Z", GoVersion:"go1.20.6", Compiler:"gc", Platform:"linux/amd64"}
Kustomize Version: v5.0.1
Server Version: version.Info{Major:"1", Minor:"27", GitVersion:"v1.27.3",GitCommit:"25b4e43193bcda6c7328a6d147b1fb73a33f1598", GitTreeState:"clean",BuildDate:"2023-06-14T09:47:40Z", GoVersion:"go1.20.5", Compiler:"gc", Platform:"linux/amd64"}

如果你看到 Unable to connect to the server: dial tcp <server-ip>:8443: i/o timeout, 而不是 Server Version,则需要解决 kubectl 与你集群的连接问题。

确保按照 kubectl 官方安装文档安装了 kubectl, 并正确配置了 $PATH 环境变量。

检查 kubeconfig

kubectl 需要一个 kubeconfig 文件来连接到 Kubernetes 集群。 kubeconfig 文件通常位于 ~/.kube/config 目录下。确保你有一个有效的 kubeconfig 文件。 如果你没有 kubeconfig 文件,可以从 Kubernetes 管理员那里获取,或者可以从 Kubernetes 控制平面的 /etc/kubernetes/admin.conf 目录复制这个文件。如果你在云平台上部署了 Kubernetes 集群并且丢失了你的 kubeconfig 文件,则可以使用云厂商的工具重新生成它。参考云厂商的文档以重新生成 kubeconfig 文件。

检查 $KUBECONFIG 环境变量是否配置正确。你可以设置 $KUBECONFIG 环境变量,或者在 kubectl 中使用 --kubeconfig 参数来指定 kubeconfig 文件的目录。

检查 VPN 连接

如果你正在使用虚拟专用网络(VPN)访问 Kubernetes 集群,请确保你的 VPN 连接是可用且稳定的。 有时,VPN 断开连接可能会导致与集群的连接问题。重新连接到 VPN,并尝试再次访问集群。

身份认证和鉴权

如果你正在使用基于令牌的身份认证,并且 kubectl 返回有关身份认证令牌或身份认证服务器地址的错误, 校验 Kubernetes 身份认证令牌和身份认证服务器地址是否被配置正确。

如果 kubectl 返回有关鉴权的错误,确保你正在使用有效的用户凭据,并且你具有访问所请求资源的权限。

验证上下文

Kubernetes 支持多个集群和上下文。 确保你正在使用正确的上下文与集群进行交互。

列出可用的上下文:

kubectl config get-contexts

切换到合适的上下文:

kubectl config use-context <context-name>

API 服务器和负载均衡器

kube-apiserver 服务器是 Kubernetes 集群的核心组件。如果 API 服务器或运行在 API 服务器前面的负载均衡器不可达或没有响应,你将无法与集群进行交互。

通过使用 ping 命令检查 API 服务器的主机是否可达。检查集群的网络连接和防火墙。 如果你使用云厂商部署集群,请检查云厂商对集群的 API 服务器的健康检查状态。

验证负载均衡器(如果使用)的状态,确保其健康且转发流量到 API 服务器。

TLS 问题

Kubernetes API 服务器默认只为 HTTPS 请求提供服务。在这种情况下, TLS 问题可能会因各种原因而出现,例如证书过期或信任链有效性。

你可以在 kubeconfig 文件中找到 TLS 证书,此文件位于 ~/.kube/config 目录下。 certificate-authority 属性包含 CA 证书,而 client-certificate 属性则包含客户端证书。

验证这些证书的到期时间:

openssl x509 -noout -dates -in $(kubectl config view --minify --output 'jsonpath={.clusters[0].cluster.certificate-authority}')

输出为:

notBefore=Sep  2 08:34:12 2023 GMT
notAfter=Aug 31 08:34:12 2033 GMT
openssl x509 -noout -dates -in $(kubectl config view --minify --output 'jsonpath={.users[0].user.client-certificate}')

输出为:

notBefore=Sep  2 08:34:12 2023 GMT
notAfter=Sep  2 08:34:12 2026 GMT

验证 kubectl 助手

某些 kubectl 身份认证助手提供了便捷访问 Kubernetes 集群的方式。 如果你使用了这些助手并且遇到连接问题,确保必要的配置仍然存在。

查看 kubectl 配置以了解身份认证细节:

kubectl config view

如果你之前使用了辅助工具(例如 kubectl-oidc-login),确保它仍然安装和配置正确。

4.4.1.2 - 资源监控工具

要扩展应用程序并提供可靠的服务,你需要了解应用程序在部署时的行为。 你可以通过检测容器、PodService 和整个集群的特征来检查 Kubernetes 集群中应用程序的性能。 Kubernetes 在每个级别上提供有关应用程序资源使用情况的详细信息。 此信息使你可以评估应用程序的性能,以及在何处可以消除瓶颈以提高整体性能。

在 Kubernetes 中,应用程序监控不依赖单个监控解决方案。在新集群上, 你可以使用资源度量完整度量管道来收集监视统计信息。

资源度量管道

资源指标管道提供了一组与集群组件,例如 Horizontal Pod Autoscaler 控制器以及 kubectl top 实用程序相关的有限度量。 这些指标是由轻量级的、短期、内存存储的 metrics-server 收集, 并通过 metrics.k8s.io 公开。

metrics-server 发现集群中的所有节点,并且查询每个节点的 kubelet 以获取 CPU 和内存使用情况。 kubelet 充当 Kubernetes 主节点与节点之间的桥梁,管理机器上运行的 Pod 和容器。 kubelet 将每个 Pod 转换为其组成的容器,并通过容器运行时接口从容器运行时获取各个容器使用情况统计信息。 如果某个容器运行时使用 Linux cgroups 和名字空间来实现容器。 并且这一容器运行时不发布资源用量统计信息, 那么 kubelet 可以直接查找这些统计信息(使用来自 cAdvisor 的代码)。 无论这些统计信息如何到达,kubelet 都会通过 metrics-server Resource Metrics API 公开聚合的 Pod 资源用量统计信息。 该 API 在 kubelet 的经过身份验证和只读的端口上的 /metrics/resource/v1beta1 中提供。

完整度量管道

一个完整度量管道可以让你访问更丰富的度量。 Kubernetes 还可以根据集群的当前状态,使用 Pod 水平自动扩缩器等机制, 通过自动调用扩展或调整集群来响应这些度量。 监控管道从 kubelet 获取度量值,然后通过适配器将它们公开给 Kubernetes, 方法是实现 custom.metrics.k8s.ioexternal.metrics.k8s.io API。

Kubernetes 在设计上保证能够与 OpenMetrics 一同使用, OpenMetrics 是 CNCF 可观测性和分析 - 监控项目之一, 它构建于 Prometheus 暴露格式之上, 并对其进行了扩展,这些扩展几乎 100% 向后兼容。

如果你浏览 CNCF Landscape, 你可以看到许多监控项目,它们可以用在 Kubernetes 上,抓取指标数据并利用这些数据来观测你的集群, 选择哪种工具或哪些工具可以满足你的需求,这完全取决于你自己。 CNCF 的可观测性和分析景观包括了各种开源软件、付费的软件即服务(SaaS)以及其他混合商业产品。

当你设计和实现一个完整的指标监控数据管道时,你可以将监控数据反馈给 Kubernetes。 例如,HorizontalPodAutoscaler 可以使用处理过的指标数据来计算出你的工作负载组件运行了多少个 Pod。

将完整的指标管道集成到 Kubernetes 实现中超出了 Kubernetes 文档的范围,因为可能的解决方案具有非常广泛的范围。

监控平台的选择在很大程度上取决于你的需求、预算和技术资源。 Kubernetes 不推荐任何特定的指标管道; 可使用许多选项。 你的监控系统应能够处理 OpenMetrics 指标传输标准, 并且需要选择最适合基础设施平台的整体设计和部署。

接下来

了解其他调试工具,包括:

4.4.1.3 - 资源指标管道

对于 Kubernetes,Metrics API 提供了一组基本的指标,以支持自动伸缩和类似的用例。 该 API 提供有关节点和 Pod 的资源使用情况的信息, 包括 CPU 和内存的指标。如果将 Metrics API 部署到集群中, 那么 Kubernetes API 的客户端就可以查询这些信息,并且可以使用 Kubernetes 的访问控制机制来管理权限。

HorizontalPodAutoscaler (HPA) 和 VerticalPodAutoscaler (VPA) 使用 metrics API 中的数据调整工作负载副本和资源,以满足客户需求。

你也可以通过 kubectl top 命令来查看资源指标。

图 1 说明了资源指标管道的架构。

flowchart RL subgraph cluster[集群] direction RL S[

] A[Metrics-
Server] subgraph B[节点] direction TB D[cAdvisor] --> C[kubelet] E[容器
运行时] --> D E1[容器
运行时] --> D P[Pod 数据] -.- C end L[API
服务器] W[HPA] C ---->|节点层面
资源指标| A -->|metrics
API| L --> W end L ---> K[kubectl
top] classDef box fill:#fff,stroke:#000,stroke-width:1px,color:#000; class W,B,P,K,cluster,D,E,E1 box classDef spacewhite fill:#ffffff,stroke:#fff,stroke-width:0px,color:#000 class S spacewhite classDef k8s fill:#326ce5,stroke:#fff,stroke-width:1px,color:#fff; class A,L,C k8s

图 1. 资源指标管道

图中从右到左的架构组件包括以下内容:

  • cAdvisor: 用于收集、聚合和公开 Kubelet 中包含的容器指标的守护程序。

  • kubelet: 用于管理容器资源的节点代理。 可以使用 /metrics/resource/stats kubelet API 端点访问资源指标。

  • 节点层面资源指标: kubelet 提供的 API,用于发现和检索可通过 /metrics/resource 端点获得的每个节点的汇总统计信息。

  • metrics-server: 集群插件组件,用于收集和聚合从每个 kubelet 中提取的资源指标。 API 服务器提供 Metrics API 以供 HPA、VPA 和 kubectl top 命令使用。Metrics Server 是 Metrics API 的参考实现。

  • Metrics API: Kubernetes API 支持访问用于工作负载自动缩放的 CPU 和内存。 要在你的集群中进行这项工作,你需要一个提供 Metrics API 的 API 扩展服务器。

Metrics API

特性状态: Kubernetes 1.8 [beta]

metrics-server 实现了 Metrics API。此 API 允许你访问集群中节点和 Pod 的 CPU 和内存使用情况。 它的主要作用是将资源使用指标提供给 K8s 自动缩放器组件。

下面是一个 minikube 节点的 Metrics API 请求示例,通过 jq 管道处理以便于阅读:

kubectl get --raw "/apis/metrics.k8s.io/v1beta1/nodes/minikube" | jq '.'

这是使用 curl 来执行的相同 API 调用:

curl http://localhost:8080/apis/metrics.k8s.io/v1beta1/nodes/minikube

响应示例:

{
  "kind": "NodeMetrics",
  "apiVersion": "metrics.k8s.io/v1beta1",
  "metadata": {
    "name": "minikube",
    "selfLink": "/apis/metrics.k8s.io/v1beta1/nodes/minikube",
    "creationTimestamp": "2022-01-27T18:48:43Z"
  },
  "timestamp": "2022-01-27T18:48:33Z",
  "window": "30s",
  "usage": {
    "cpu": "487558164n",
    "memory": "732212Ki"
  }
}

下面是一个 kube-system 命名空间中的 kube-scheduler-minikube Pod 的 Metrics API 请求示例, 通过 jq 管道处理以便于阅读:

kubectl get --raw "/apis/metrics.k8s.io/v1beta1/namespaces/kube-system/pods/kube-scheduler-minikube" | jq '.'

这是使用 curl 来完成的相同 API 调用:

curl http://localhost:8080/apis/metrics.k8s.io/v1beta1/namespaces/kube-system/pods/kube-scheduler-minikube

响应示例:

{
  "kind": "PodMetrics",
  "apiVersion": "metrics.k8s.io/v1beta1",
  "metadata": {
    "name": "kube-scheduler-minikube",
    "namespace": "kube-system",
    "selfLink": "/apis/metrics.k8s.io/v1beta1/namespaces/kube-system/pods/kube-scheduler-minikube",
    "creationTimestamp": "2022-01-27T19:25:00Z"
  },
  "timestamp": "2022-01-27T19:24:31Z",
  "window": "30s",
  "containers": [
    {
      "name": "kube-scheduler",
      "usage": {
        "cpu": "9559630n",
        "memory": "22244Ki"
      }
    }
  ]
}

Metrics API 在 k8s.io/metrics 代码库中定义。 你必须启用 API 聚合层并为 metrics.k8s.io API 注册一个 APIService

要了解有关 Metrics API 的更多信息, 请参阅资源 Resource Metrics API Designmetrics-server 代码库Resource Metrics API

度量资源用量

CPU

CPU 报告为以 cpu 为单位测量的平均核心使用率。在 Kubernetes 中, 一个 cpu 相当于云提供商的 1 个 vCPU/Core,以及裸机 Intel 处理器上的 1 个超线程。

该值是通过对内核提供的累积 CPU 计数器(在 Linux 和 Windows 内核中)取一个速率得出的。 用于计算 CPU 的时间窗口显示在 Metrics API 的窗口字段下。

要了解更多关于 Kubernetes 如何分配和测量 CPU 资源的信息,请参阅 CPU 的含义

内存

内存报告为在收集度量标准的那一刻的工作集大小,以字节为单位。

在理想情况下,“工作集”是在内存压力下无法释放的正在使用的内存量。 然而,工作集的计算因主机操作系统而异,并且通常大量使用启发式算法来产生估计。

Kubernetes 模型中,容器工作集是由容器运行时计算的与相关容器关联的匿名内存。 工作集指标通常还包括一些缓存(文件支持)内存,因为主机操作系统不能总是回收页面。

要了解有关 Kubernetes 如何分配和测量内存资源的更多信息, 请参阅内存的含义

Metrics 服务器

metrics-server 从 kubelet 中获取资源指标,并通过 Metrics API 在 Kubernetes API 服务器中公开它们,以供 HPA 和 VPA 使用。 你还可以使用 kubectl top 命令查看这些指标。

metrics-server 使用 Kubernetes API 来跟踪集群中的节点和 Pod。metrics-server 服务器通过 HTTP 查询每个节点以获取指标。 metrics-server 还构建了 Pod 元数据的内部视图,并维护 Pod 健康状况的缓存。 缓存的 Pod 健康信息可通过 metrics-server 提供的扩展 API 获得。

例如,对于 HPA 查询,metrics-server 需要确定哪些 Pod 满足 Deployment 中的标签选择器。

metrics-server 调用 kubelet API 从每个节点收集指标。根据它使用的 metrics-server 版本:

  • 版本 v0.6.0+ 中,使用指标资源端点 /metrics/resource
  • 旧版本中使用 Summary API 端点 /stats/summary

接下来

了解更多 metrics-server,参阅 metrics-server 代码库

你还可以查看以下内容:

若要了解 kubelet 如何提供节点指标以及你可以如何通过 Kubernetes API 访问这些指标, 请阅读节点指标数据

4.4.1.4 - 节点健康监测

节点问题检测器(Node Problem Detector) 是一个守护程序,用于监视和报告节点的健康状况。 你可以将节点问题探测器以 DaemonSet 或独立守护程序运行。 节点问题检测器从各种守护进程收集节点问题,并以节点 ConditionEvent 的形式报告给 API 服务器。

要了解如何安装和使用节点问题检测器,请参阅 节点问题探测器项目文档

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

局限性

启用节点问题检测器

一些云供应商将节点问题检测器以插件形式启用。 你还可以使用 kubectl 或创建插件 DaemonSet 来启用节点问题探测器。

使用 kubectl 启用节点问题检测器

kubectl 提供了节点问题探测器最灵活的管理。 你可以覆盖默认配置使其适合你的环境或检测自定义节点问题。例如:

  1. 创建类似于 node-strought-detector.yaml 的节点问题检测器配置:

    apiVersion: apps/v1
    kind: DaemonSet
    metadata:
      name: node-problem-detector-v0.1
      namespace: kube-system
      labels:
        k8s-app: node-problem-detector
        version: v0.1
        kubernetes.io/cluster-service: "true"
    spec:
      selector:
        matchLabels:
          k8s-app: node-problem-detector  
          version: v0.1
          kubernetes.io/cluster-service: "true"
      template:
        metadata:
          labels:
            k8s-app: node-problem-detector
            version: v0.1
            kubernetes.io/cluster-service: "true"
        spec:
          hostNetwork: true
          containers:
          - name: node-problem-detector
            image: registry.k8s.io/node-problem-detector:v0.1
            securityContext:
              privileged: true
            resources:
              limits:
                cpu: "200m"
                memory: "100Mi"
              requests:
                cpu: "20m"
                memory: "20Mi"
            volumeMounts:
            - name: log
              mountPath: /log
              readOnly: true
          volumes:
          - name: log
            hostPath:
              path: /var/log/
  2. 使用 kubectl 启动节点问题检测器:

    kubectl apply -f https://k8s.io/examples/debug/node-problem-detector.yaml
    

使用插件 Pod 启用节点问题检测器

如果你使用的是自定义集群引导解决方案,不需要覆盖默认配置, 可以利用插件 Pod 进一步自动化部署。

创建 node-strick-detector.yaml,并在控制平面节点上保存配置到插件 Pod 的目录 /etc/kubernetes/addons/node-problem-detector

覆盖配置文件

构建节点问题检测器的 docker 镜像时,会嵌入 默认配置

不过,你可以像下面这样使用 ConfigMap 将其覆盖:

  1. 更改 config/ 中的配置文件

  2. 创建 ConfigMap node-strick-detector-config

    kubectl create configmap node-problem-detector-config --from-file=config/
    
  3. 更改 node-problem-detector.yaml 以使用 ConfigMap:

    apiVersion: apps/v1
       kind: DaemonSet
       metadata:
         name: node-problem-detector-v0.1
         namespace: kube-system
         labels:
           k8s-app: node-problem-detector
           version: v0.1
           kubernetes.io/cluster-service: "true"
       spec:
         selector:
           matchLabels:
             k8s-app: node-problem-detector  
             version: v0.1
             kubernetes.io/cluster-service: "true"
         template:
           metadata:
             labels:
               k8s-app: node-problem-detector
               version: v0.1
               kubernetes.io/cluster-service: "true"
           spec:
             hostNetwork: true
             containers:
             - name: node-problem-detector
               image: registry.k8s.io/node-problem-detector:v0.1
               securityContext:
                 privileged: true
               resources:
                 limits:
                   cpu: "200m"
                   memory: "100Mi"
                 requests:
                   cpu: "20m"
                   memory: "20Mi"
               volumeMounts:
               - name: log
                 mountPath: /log
                 readOnly: true
               - name: config # 使用 ConfigMap 卷中的数据覆盖 config/ 目录内容
                 mountPath: /config
                 readOnly: true
             volumes:
             - name: log
               hostPath:
                 path: /var/log/
             - name: config # 定义 ConfigMap 卷
               configMap:
                 name: node-problem-detector-config
  4. 使用新的配置文件重新创建节点问题检测器:

    # 如果你正在运行节点问题检测器,请先删除,然后再重新创建
    kubectl delete -f https://k8s.io/examples/debug/node-problem-detector.yaml
    kubectl apply -f https://k8s.io/examples/debug/node-problem-detector-configmap.yaml
    

如果节点问题检测器作为集群插件运行,则不支持覆盖配置。 插件管理器不支持 ConfigMap

问题守护程序

问题守护程序是节点问题检测器的子守护程序。 它监视特定类型的节点问题并报告给节点问题检测器。 支持下面几种类型的问题守护程序。

  • SystemLogMonitor 类型的守护程序根据预定义的规则监视系统日志并报告问题和指标。 你可以针对不同的日志源自定义配置如 filelogkmsgkernelabrtsystemd
  • SystemStatsMonitor 类型的守护程序收集各种与健康相关的系统统计数据作为指标。 你可以通过更新其配置文件来自定义其行为。
  • CustomPluginMonitor 类型的守护程序通过运行用户定义的脚本来调用和检查各种节点问题。 你可以使用不同的自定义插件监视器来监视不同的问题,并通过更新 配置文件 来定制守护程序行为。
  • HealthChecker 类型的守护程序检查节点上的 kubelet 和容器运行时的健康状况。

增加对其他日志格式的支持

系统日志监视器目前支持基于文件的日志、journald 和 kmsg。 可以通过实现一个新的 log watcher 来添加额外的日志源。

添加自定义插件监视器

你可以通过开发自定义插件来扩展节点问题检测器,以执行以任何语言编写的任何监控脚本。 监控脚本必须符合退出码和标准输出的插件协议。 有关更多信息,请参阅 插件接口提案.

导出器

导出器(Exporter)向特定后端报告节点问题和/或指标。 支持下列导出器:

  • Kubernetes exporter:此导出器向 Kubernetes API 服务器报告节点问题。 临时问题报告为事件,永久性问题报告为节点状况。

  • Prometheus exporter:此导出器在本地将节点问题和指标报告为 Prometheus(或 OpenMetrics)指标。 你可以使用命令行参数指定导出器的 IP 地址和端口。

  • Stackdriver exporter:此导出器向 Stackdriver Monitoring API 报告节点问题和指标。 可以使用配置文件自定义导出行为。

建议和限制

建议在集群中运行节点问题检测器以监控节点运行状况。 运行节点问题检测器时,你可以预期每个节点上的额外资源开销。 通常这是可接受的,因为:

  • 内核日志增长相对缓慢。
  • 已经为节点问题检测器设置了资源限制。
  • 即使在高负载下,资源使用也是可接受的。有关更多信息,请参阅节点问题检测器 基准结果

4.4.1.5 - 使用 crictl 对 Kubernetes 节点进行调试

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

crictl 是 CRI 兼容的容器运行时命令行接口。 你可以使用它来检查和调试 Kubernetes 节点上的容器运行时和应用程序。 crictl 和它的源代码在 cri-tools 代码库。

准备开始

crictl 需要带有 CRI 运行时的 Linux 操作系统。

安装 crictl

你可以从 cri-tools 发布页面 下载一个压缩的 crictl 归档文件,用于几种不同的架构。 下载与你的 kubernetes 版本相对应的版本。 提取它并将其移动到系统路径上的某个位置,例如 /usr/local/bin/

一般用法

crictl 命令有几个子命令和运行时参数。 有关详细信息,请使用 crictl helpcrictl <subcommand> help 获取帮助信息。

你可以用以下方法之一来为 crictl 设置端点:

  • 设置参数 --runtime-endpoint--image-endpoint
  • 设置环境变量 CONTAINER_RUNTIME_ENDPOINTIMAGE_SERVICE_ENDPOINT
  • 在配置文件 --config=/etc/crictl.yaml 中设置端点。 要设置不同的文件,可以在运行 crictl 时使用 --config=PATH_TO_FILE 标志。

你还可以在连接到服务器并启用或禁用调试时指定超时值,方法是在配置文件中指定 timeoutdebug 值,或者使用 --timeout--debug 命令行参数。

要查看或编辑当前配置,请查看或编辑 /etc/crictl.yaml 的内容。 例如,使用 containerd 容器运行时的配置会类似于这样:

runtime-endpoint: unix:///var/run/containerd/containerd.sock
image-endpoint: unix:///var/run/containerd/containerd.sock
timeout: 10
debug: true

要进一步了解 crictl,参阅 crictl 文档

crictl 命令示例

打印 Pod 清单

打印所有 Pod 的清单:

crictl pods

输出类似于:

POD ID              CREATED              STATE               NAME                         NAMESPACE           ATTEMPT
926f1b5a1d33a       About a minute ago   Ready               sh-84d7dcf559-4r2gq          default             0
4dccb216c4adb       About a minute ago   Ready               nginx-65899c769f-wv2gp       default             0
a86316e96fa89       17 hours ago         Ready               kube-proxy-gblk4             kube-system         0
919630b8f81f1       17 hours ago         Ready               nvidia-device-plugin-zgbbv   kube-system         0

根据名称打印 Pod 清单:

crictl pods --name nginx-65899c769f-wv2gp

输出类似于这样:

POD ID              CREATED             STATE               NAME                     NAMESPACE           ATTEMPT
4dccb216c4adb       2 minutes ago       Ready               nginx-65899c769f-wv2gp   default             0

根据标签打印 Pod 清单:

crictl pods --label run=nginx

输出类似于这样:

POD ID              CREATED             STATE               NAME                     NAMESPACE           ATTEMPT
4dccb216c4adb       2 minutes ago       Ready               nginx-65899c769f-wv2gp   default             0

打印镜像清单

打印所有镜像清单:

crictl images

输出类似于这样:

IMAGE                                     TAG                 IMAGE ID            SIZE
busybox                                   latest              8c811b4aec35f       1.15MB
k8s-gcrio.azureedge.net/hyperkube-amd64   v1.10.3             e179bbfe5d238       665MB
k8s-gcrio.azureedge.net/pause-amd64       3.1                 da86e6ba6ca19       742kB
nginx                                     latest              cd5239a0906a6       109MB

根据仓库打印镜像清单:

crictl images nginx

输出类似于这样:

IMAGE               TAG                 IMAGE ID            SIZE
nginx               latest              cd5239a0906a6       109MB

只打印镜像 ID:

crictl images -q

输出类似于这样:

sha256:8c811b4aec35f259572d0f79207bc0678df4c736eeec50bc9fec37ed936a472a
sha256:e179bbfe5d238de6069f3b03fccbecc3fb4f2019af741bfff1233c4d7b2970c5
sha256:da86e6ba6ca197bf6bc5e9d900febd906b133eaa4750e6bed647b0fbe50ed43e
sha256:cd5239a0906a6ccf0562354852fae04bc5b52d72a2aff9a871ddb6bd57553569

打印容器清单

打印所有容器清单:

crictl ps -a

输出类似于这样:

CONTAINER ID        IMAGE                                                                                                             CREATED             STATE               NAME                       ATTEMPT
1f73f2d81bf98       busybox@sha256:141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47                                   7 minutes ago       Running             sh                         1
9c5951df22c78       busybox@sha256:141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47                                   8 minutes ago       Exited              sh                         0
87d3992f84f74       nginx@sha256:d0a8828cccb73397acb0073bf34f4d7d8aa315263f1e7806bf8c55d8ac139d5f                                     8 minutes ago       Running             nginx                      0
1941fb4da154f       k8s-gcrio.azureedge.net/hyperkube-amd64@sha256:00d814b1f7763f4ab5be80c58e98140dfc69df107f253d7fdd714b30a714260a   18 hours ago        Running             kube-proxy                 0

打印正在运行的容器清单:

crictl ps

输出类似于这样:

CONTAINER ID        IMAGE                                                                                                             CREATED             STATE               NAME                       ATTEMPT
1f73f2d81bf98       busybox@sha256:141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47                                   6 minutes ago       Running             sh                         1
87d3992f84f74       nginx@sha256:d0a8828cccb73397acb0073bf34f4d7d8aa315263f1e7806bf8c55d8ac139d5f                                     7 minutes ago       Running             nginx                      0
1941fb4da154f       k8s-gcrio.azureedge.net/hyperkube-amd64@sha256:00d814b1f7763f4ab5be80c58e98140dfc69df107f253d7fdd714b30a714260a   17 hours ago        Running             kube-proxy                 0

在正在运行的容器上执行命令

crictl exec -i -t 1f73f2d81bf98 ls

输出类似于这样:

bin   dev   etc   home  proc  root  sys   tmp   usr   var

获取容器日志

获取容器的所有日志:

crictl logs 87d3992f84f74

输出类似于这样:

10.240.0.96 - - [06/Jun/2018:02:45:49 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.47.0" "-"
10.240.0.96 - - [06/Jun/2018:02:45:50 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.47.0" "-"
10.240.0.96 - - [06/Jun/2018:02:45:51 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.47.0" "-"

获取最近的 N 行日志:

crictl logs --tail=1 87d3992f84f74

输出类似于这样:

10.240.0.96 - - [06/Jun/2018:02:45:51 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.47.0" "-"

运行 Pod 沙盒

crictl 运行 Pod 沙盒对容器运行时排错很有帮助。 在运行的 Kubernetes 集群中,沙盒会随机地被 kubelet 停止和删除。

  1. 编写下面的 JSON 文件:

    {
      "metadata": {
        "name": "nginx-sandbox",
        "namespace": "default",
        "attempt": 1,
        "uid": "hdishd83djaidwnduwk28bcsb"
      },
      "log_directory": "/tmp",
      "linux": {
      }
    }
    
  1. 使用 crictl runp 命令应用 JSON 文件并运行沙盒。

    crictl runp pod-config.json
    

    返回了沙盒的 ID。

创建容器

crictl 创建容器对容器运行时排错很有帮助。 在运行的 Kubernetes 集群中,容器最终将被 kubelet 停止和删除。

  1. 拉取 busybox 镜像

    crictl pull busybox
    
    Image is up to date for busybox@sha256:141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47
    
  1. 创建 Pod 和容器的配置:

    Pod 配置

    {
      "metadata": {
        "name": "busybox-sandbox",
        "namespace": "default",
        "attempt": 1,
        "uid": "aewi4aeThua7ooShohbo1phoj"
      },
      "log_directory": "/tmp",
      "linux": {
      }
    }
    

    容器配置

    {
      "metadata": {
        "name": "busybox"
      },
      "image":{
        "image": "busybox"
      },
      "command": [
        "top"
      ],
      "log_path":"busybox.log",
      "linux": {
      }
    }
    
  1. 创建容器,传递先前创建的 Pod 的 ID、容器配置文件和 Pod 配置文件。返回容器的 ID。

    crictl create f84dd361f8dc51518ed291fbadd6db537b0496536c1d2d6c05ff943ce8c9a54f container-config.json pod-config.json
    
  1. 查询所有容器并确认新创建的容器状态为 Created

    crictl ps -a
    

    输出类似于这样:

    CONTAINER ID        IMAGE               CREATED             STATE               NAME                ATTEMPT
    3e025dd50a72d       busybox             32 seconds ago      Created             busybox             0
    

启动容器

要启动容器,要将容器 ID 传给 crictl start

crictl start 3e025dd50a72d956c4f14881fbb5b1080c9275674e95fb67f965f6478a957d60

输出类似于这样:

3e025dd50a72d956c4f14881fbb5b1080c9275674e95fb67f965f6478a957d60

确认容器的状态为 Running

crictl ps

输出类似于这样:

CONTAINER ID   IMAGE    CREATED              STATE    NAME     ATTEMPT
3e025dd50a72d  busybox  About a minute ago   Running  busybox  0

接下来

4.4.1.6 - Windows 调试技巧

工作节点级别排障

  1. 我的 Pod 都卡在 “Container Creating” 或者不断重启

    确保你的 pause 镜像跟你的 Windows 版本兼容。 查看 Pause 容器 以了解最新的或建议的 pause 镜像,或者了解更多信息。

  1. 我的 Pod 状态显示 'ErrImgPull' 或者 'ImagePullBackOff'

    保证你的 Pod 被调度到兼容的 Windows 节点上。

    关于如何为你的 Pod 指定一个兼容节点, 可以查看这个指可以查看这个指南 以了解更多的信息。

网络排障

  1. 我的 Windows Pod 没有网络连接

    如果你使用的是虚拟机,请确保所有 VM 网卡上都已启用 MAC spoofing。

  1. 我的 Windows Pod 不能 ping 通外界资源

    Windows Pod 没有为 ICMP 协议编写出站规则,但 TCP/UDP 是支持的。当试图演示与集群外部资源的连接时, 可以把 ping <IP> 替换为 curl <IP> 命令。

    如果你仍然遇到问题,很可能你需要额外关注 cni.conf 的配置。你可以随时编辑这个静态文件。更新配置将应用于新的 Kubernetes 资源。

    Kubernetes 的网络需求之一(查看 Kubernetes 模型) 是集群通信不需要内部的 NAT。 为了遵守这一要求,对于你不希望发生的出站 NAT 通信,这里有一个 ExceptionList。 然而,这也意味着你需要从 ExceptionList 中去掉你试图查询的外部 IP。 只有这样,来自你的 Windows Pod 的流量才会被正确地 SNAT 转换,以接收来自外部环境的响应。 就此而言,你的 cni.conf 中的 ExceptionList 应该如下所示:

    "ExceptionList": [
        "10.244.0.0/16",  # 集群子网
        "10.96.0.0/12",   # 服务子网
        "10.127.130.0/24" # 管理(主机)子网
    ]
    
  1. 我的 Windows 节点无法访问 NodePort 类型 Service

    从节点本身访问本地 NodePort 失败,是一个已知的限制。 你可以从其他节点或外部客户端正常访问 NodePort。

  1. 容器的 vNIC 和 HNS 端点正在被删除

    hostname-override 参数没有传递给 kube-proxy 时可能引发这一问题。想要解决这个问题,用户需要将主机名传递给 kube-proxy,如下所示:

    C:\k\kube-proxy.exe --hostname-override=$(hostname)
    
  1. 我的 Windows 节点无法通过服务 IP 访问我的服务

    这是 Windows 上网络栈的一个已知限制。但是 Windows Pod 可以访问 Service IP。

  1. 启动 kubelet 时找不到网络适配器

    Windows 网络栈需要一个虚拟适配器才能使 Kubernetes 网络工作。 如果以下命令没有返回结果(在管理员模式的 shell 中), 则意味着创建虚拟网络失败,而虚拟网络的存在是 kubelet 正常工作的前提:

    Get-HnsNetwork | ? Name -ieq "cbr0"
    Get-NetAdapter | ? Name -Like "vEthernet (Ethernet*"
    

    如果主机的网络适配器不是 "Ethernet",通常有必要修改 start.ps1 脚本的 InterfaceName 参数。否则,如果虚拟网络创建过程出错,请检查 start-kubelet.ps1 脚本的输出。

  1. DNS 解析工作异常

    查阅这一节了解 Windows 系统上的 DNS 限制。

  1. kubectl port-forward 失败,错误为 "unable to do port forwarding: wincat not found"

    在 Kubernetes 1.15 中,pause 基础架构容器 mcr.microsoft.com/oss/kubernetes/pause:3.6 中包含 wincat.exe 来实现端口转发。 请确保使用 Kubernetes 的受支持版本。如果你想构建自己的 pause 基础架构容器, 请确保其中包含 wincat

  1. 我的 Kubernetes 安装失败,因为我的 Windows 服务器节点使用了代理服务器

    如果使用了代理服务器,必须定义下面的 PowerShell 环境变量:

    [Environment]::SetEnvironmentVariable("HTTP_PROXY", "http://proxy.example.com:80/", [EnvironmentVariableTarget]::Machine)
    [Environment]::SetEnvironmentVariable("HTTPS_PROXY", "http://proxy.example.com:443/", [EnvironmentVariableTarget]::Machine)
    

Flannel 故障排查

  1. 使用 Flannel 时,我的节点在重新加入集群后出现问题

    当先前删除的节点重新加入集群时, flannelD 尝试为节点分配一个新的 Pod 子网。 用户应该在以下路径中删除旧的 Pod 子网配置文件:

    Remove-Item C:\k\SourceVip.json
    Remove-Item C:\k\SourceVipRequest.json
    
  1. Flanneld 卡在 "Waiting for the Network to be created"

    关于这个问题有很多报告; 很可能是 Flannel 网络管理 IP 的设置时机问题。 一个变通方法是重新启动 start.ps1 或按如下方式手动重启:

    [Environment]::SetEnvironmentVariable("NODE_NAME", "<Windows 工作节点主机名>")
    C:\flannel\flanneld.exe --kubeconfig-file=c:\k\config --iface=<Windows 工作节点 IP> --ip-masq=1 --kube-subnet-mgr=1
    
  1. 我的 Windows Pod 无法启动,因为缺少 /run/flannel/subnet.env

    这表明 Flannel 没有正确启动。你可以尝试重启 flanneld.exe 或者你可以将 Kubernetes 控制节点的 /run/flannel/subnet.env 文件手动拷贝到 Windows 工作节点上,放在 C:\run\flannel\subnet.env; 并且将 FLANNEL_SUBNET 行修改为不同取值。例如,如果期望节点子网为 10.244.4.1/24:

    FLANNEL_NETWORK=10.244.0.0/16
    FLANNEL_SUBNET=10.244.4.1/24
    FLANNEL_MTU=1500
    FLANNEL_IPMASQ=true
    

进一步探查

如果这些步骤都不能解决你的问题,你可以通过以下方式获得关于在 Kubernetes 中运行 Windows 容器的帮助:

4.4.1.7 - 审计

Kubernetes 审计(Auditing) 功能提供了与安全相关的、按时间顺序排列的记录集, 记录每个用户、使用 Kubernetes API 的应用以及控制面自身引发的活动。

审计功能使得集群管理员能够回答以下问题:

  • 发生了什么?
  • 什么时候发生的?
  • 谁触发的?
  • 活动发生在哪个(些)对象上?
  • 在哪观察到的?
  • 它从哪触发的?
  • 活动的后续处理行为是什么?

审计记录最初产生于 kube-apiserver 内部。每个请求在不同执行阶段都会生成审计事件;这些审计事件会根据特定策略被预处理并写入后端。 策略确定要记录的内容和用来存储记录的后端,当前的后端支持日志文件和 webhook。

每个请求都可被记录其相关的阶段(stage)。已定义的阶段有:

  • RequestReceived - 此阶段对应审计处理器接收到请求后, 并且在委托给其余处理器之前生成的事件。
  • ResponseStarted - 在响应消息的头部发送后,响应消息体发送前生成的事件。 只有长时间运行的请求(例如 watch)才会生成这个阶段。
  • ResponseComplete - 当响应消息体完成并且没有更多数据需要传输的时候。
  • Panic - 当 panic 发生时生成。

审计日志记录功能会增加 API server 的内存消耗,因为需要为每个请求存储审计所需的某些上下文。 内存消耗取决于审计日志记录的配置。

审计策略

审计策略定义了关于应记录哪些事件以及应包含哪些数据的规则。 审计策略对象结构定义在 audit.k8s.io API 组。 处理事件时,将按顺序与规则列表进行比较。第一个匹配规则设置事件的审计级别(Audit Level)。 已定义的审计级别有:

  • None - 符合这条规则的日志将不会记录。
  • Metadata - 记录请求的元数据(请求的用户、时间戳、资源、动词等等), 但是不记录请求或者响应的消息体。
  • Request - 记录事件的元数据和请求的消息体,但是不记录响应的消息体。 这不适用于非资源类型的请求。
  • RequestResponse - 记录事件的元数据,请求和响应的消息体。这不适用于非资源类型的请求。

你可以使用 --audit-policy-file 标志将包含策略的文件传递给 kube-apiserver。 如果不设置该标志,则不记录事件。 注意 rules 字段必须在审计策略文件中提供。没有(0)规则的策略将被视为非法配置。

以下是一个审计策略文件的示例:

apiVersion: audit.k8s.io/v1 # 这是必填项。
kind: Policy
# 不要在 RequestReceived 阶段为任何请求生成审计事件。
omitStages:
  - "RequestReceived"
rules:
  # 在日志中用 RequestResponse 级别记录 Pod 变化。
  - level: RequestResponse
    resources:
    - group: ""
      # 资源 "pods" 不匹配对任何 Pod 子资源的请求,
      # 这与 RBAC 策略一致。
      resources: ["pods"]
  # 在日志中按 Metadata 级别记录 "pods/log"、"pods/status" 请求
  - level: Metadata
    resources:
    - group: ""
      resources: ["pods/log", "pods/status"]

  # 不要在日志中记录对名为 "controller-leader" 的 configmap 的请求。
  - level: None
    resources:
    - group: ""
      resources: ["configmaps"]
      resourceNames: ["controller-leader"]

  # 不要在日志中记录由 "system:kube-proxy" 发出的对端点或服务的监测请求。
  - level: None
    users: ["system:kube-proxy"]
    verbs: ["watch"]
    resources:
    - group: "" # core API 组
      resources: ["endpoints", "services"]

  # 不要在日志中记录对某些非资源 URL 路径的已认证请求。
  - level: None
    userGroups: ["system:authenticated"]
    nonResourceURLs:
    - "/api*" # 通配符匹配。
    - "/version"

  # 在日志中记录 kube-system 中 configmap 变更的请求消息体。
  - level: Request
    resources:
    - group: "" # core API 组
      resources: ["configmaps"]
    # 这个规则仅适用于 "kube-system" 名字空间中的资源。
    # 空字符串 "" 可用于选择非名字空间作用域的资源。
    namespaces: ["kube-system"]

  # 在日志中用 Metadata 级别记录所有其他名字空间中的 configmap 和 secret 变更。
  - level: Metadata
    resources:
    - group: "" # core API 组
      resources: ["secrets", "configmaps"]

  # 在日志中以 Request 级别记录所有其他 core 和 extensions 组中的资源操作。
  - level: Request
    resources:
    - group: "" # core API 组
    - group: "extensions" # 不应包括在内的组版本。

  # 一个抓取所有的规则,将在日志中以 Metadata 级别记录所有其他请求。
  - level: Metadata
    # 符合此规则的 watch 等长时间运行的请求将不会
    # 在 RequestReceived 阶段生成审计事件。
    omitStages:
      - "RequestReceived"

你可以使用最低限度的审计策略文件在 Metadata 级别记录所有请求:

# 在 Metadata 级别为所有请求生成日志
apiVersion: audit.k8s.io/v1beta1
kind: Policy
rules:
- level: Metadata

如果你在打磨自己的审计配置文件,你可以使用为 Google Container-Optimized OS 设计的审计配置作为出发点。你可以参考 configure-helper.sh 脚本,该脚本能够生成审计策略文件。你可以直接在脚本中看到审计策略的绝大部份内容。

你也可以参考 Policy 配置参考 以获取有关已定义字段的详细信息。

审计后端

审计后端实现将审计事件导出到外部存储。kube-apiserver 默认提供两个后端:

  • Log 后端,将事件写入到文件系统
  • Webhook 后端,将事件发送到外部 HTTP API

在这所有情况下,审计事件均遵循 Kubernetes API 在 audit.k8s.io API 组 中定义的结构。

Log 后端

Log 后端将审计事件写入 JSONlines 格式的文件。 你可以使用以下 kube-apiserver 标志配置 Log 审计后端:

  • --audit-log-path 指定用来写入审计事件的日志文件路径。不指定此标志会禁用日志后端。- 意味着标准化
  • --audit-log-maxage 定义保留旧审计日志文件的最大天数
  • --audit-log-maxbackup 定义要保留的审计日志文件的最大数量
  • --audit-log-maxsize 定义审计日志文件轮转之前的最大大小(兆字节)

如果你的集群控制面以 Pod 的形式运行 kube-apiserver,记得要通过 hostPath 卷来访问策略文件和日志文件所在的目录,这样审计记录才会持久保存下来。例如:

  - --audit-policy-file=/etc/kubernetes/audit-policy.yaml
  - --audit-log-path=/var/log/kubernetes/audit/audit.log

接下来挂载数据卷:

...
volumeMounts:
  - mountPath: /etc/kubernetes/audit-policy.yaml
    name: audit
    readOnly: true
  - mountPath: /var/log/kubernetes/audit/
    name: audit-log
    readOnly: false

最后配置 hostPath

...
volumes:
- name: audit
  hostPath:
    path: /etc/kubernetes/audit-policy.yaml
    type: File

- name: audit-log
  hostPath:
    path: /var/log/kubernetes/audit/
    type: DirectoryOrCreate

Webhook 后端

Webhook 后端将审计事件发送到远程 Web API,该远程 API 应该暴露与 kube-apiserver 形式相同的 API,包括其身份认证机制。你可以使用如下 kube-apiserver 标志来配置 Webhook 审计后端:

  • --audit-webhook-config-file 设置 Webhook 配置文件的路径。Webhook 配置文件实际上是一个 kubeconfig 文件
  • --audit-webhook-initial-backoff 指定在第一次失败后重发请求等待的时间。随后的请求将以指数退避重试。

Webhook 配置文件使用 kubeconfig 格式指定服务的远程地址和用于连接它的凭据。

事件批处理

日志和 Webhook 后端都支持批处理。以 Webhook 为例,以下是可用参数列表。要获取日志 后端的同样参数,请在参数名称中将 webhook 替换为 log。 默认情况下,在 webhook 中批处理是被启用的,在 log 中批处理是被禁用的。 同样,默认情况下,在 webhook 中启用带宽限制,在 log 中禁用带宽限制。

  • --audit-webhook-mode 定义缓存策略,可选值如下:
    • batch - 以批处理缓存事件和异步的过程。这是默认值。
    • blocking - 在 API 服务器处理每个单独事件时,阻塞其响应。
    • blocking-strict - 与 blocking 相同,不过当审计日志在 RequestReceived 阶段失败时,整个 API 服务请求会失效。

以下参数仅用于 batch 模式:

  • --audit-webhook-batch-buffer-size 定义 batch 之前要缓存的事件数。 如果传入事件的速率溢出缓存区,则会丢弃事件。
  • --audit-webhook-batch-max-size 定义一个 batch 中的最大事件数。
  • --audit-webhook-batch-max-wait 无条件 batch 队列中的事件前等待的最大事件。
  • --audit-webhook-batch-throttle-qps 每秒生成的最大批次数。
  • --audit-webhook-batch-throttle-burst 在达到允许的 QPS 前,同一时刻允许存在的最大 batch 生成数。

参数调整

需要设置参数以适应 API 服务器上的负载。

例如,如果 kube-apiserver 每秒收到 100 个请求,并且每个请求仅在 ResponseStartedResponseComplete 阶段进行审计,则应该考虑每秒生成约 200 个审计事件。 假设批处理中最多有 100 个事件,则应将限制级别设置为每秒至少 2 个查询。 假设后端最多需要 5 秒钟来写入事件,你应该设置缓冲区大小以容纳最多 5 秒的事件, 即 10 个 batch,即 1000 个事件。

但是,在大多数情况下,默认参数应该足够了,你不必手动设置它们。 你可以查看 kube-apiserver 公开的以下 Prometheus 指标,并在日志中监控审计子系统的状态。

  • apiserver_audit_event_total 包含所有暴露的审计事件数量的指标。
  • apiserver_audit_error_total 在暴露时由于发生错误而被丢弃的事件的数量。

日志条目截断

日志后端和 Webhook 后端都支持限制所输出的事件大小。 例如,下面是可以为日志后端配置的标志列表:

  • audit-log-truncate-enabled:是否弃用事件和批次的截断处理。
  • audit-log-truncate-max-batch-size:向下层后端发送的各批次的最大字节数。
  • audit-log-truncate-max-event-size:向下层后端发送的审计事件的最大字节数。

默认情况下,截断操作在 webhooklog 后端都是被禁用的,集群管理员需要设置 audit-log-truncate-enabledaudit-webhook-truncate-enabled 标志来启用此操作。

接下来

4.4.1.8 - 使用 telepresence 在本地开发和调试服务

Kubernetes 应用程序通常由多个独立的服务组成,每个服务都在自己的容器中运行。 在远端的 Kubernetes 集群上开发和调试这些服务可能很麻烦, 需要在运行的容器上打开 Shell, 以运行调试工具。

telepresence 是一个工具,用于简化本地开发和调试服务的过程,同时可以将服务代理到远程 Kubernetes 集群。 telepresence 允许你使用自定义工具(例如调试器和 IDE)调试本地服务, 并能够让此服务完全访问 ConfigMap、Secret 和远程集群上运行的服务。

本文档描述如何在本地使用 telepresence 开发和调试远程集群上运行的服务。

准备开始

  • Kubernetes 集群安装完毕
  • 配置好 kubectl 与集群交互
  • Telepresence 安装完毕

从本机连接到远程 Kubernetes 集群

安装 telepresence 后,运行 telepresence connect 来启动它的守护进程并将本地工作站连接到远程 Kubernetes 集群。

$ telepresence connect
 
Launching Telepresence Daemon
...
Connected to context default (https://<cluster public IP>)

你可以通过 curl 使用 Kubernetes 语法访问服务,例如:curl -ik https://kubernetes.default

开发和调试现有的服务

在 Kubernetes 上开发应用程序时,通常对单个服务进行编程或调试。 服务可能需要访问其他服务以进行测试和调试。 一种选择是使用连续部署流水线,但即使最快的部署流水线也会在程序或调试周期中引入延迟。

使用 telepresence intercept $SERVICE_NAME --port $LOCAL_PORT:$REMOTE_PORT 命令创建一个 "拦截器" 用于重新路由远程服务流量。

环境变量:

  • $SERVICE_NAME 是本地服务名称
  • $LOCAL_PORT 是服务在本地工作站上运行的端口
  • $REMOTE_PORT 是服务在集群中侦听的端口

运行此命令会告诉 Telepresence 将远程流量发送到本地服务,而不是远程 Kubernetes 集群中的服务中。 在本地编辑保存服务源代码,并在访问远程应用时查看相应变更会立即生效。 还可以使用调试器或任何其他本地开发工具运行本地服务。

Telepresence 是如何工作的?

Telepresence 会在远程集群中运行的现有应用程序容器旁边安装流量代理 Sidecar。 当它捕获进入 Pod 的所有流量请求时,不是将其转发到远程集群中的应用程序, 而是路由所有流量(当创建全局拦截器时) 或流量的一个子集(当创建自定义拦截器时) 到本地开发环境。

接下来

如果你对实践教程感兴趣, 请查看本教程, 其中介绍了如何在 Google Kubernetes Engine 上本地开发 Guestbook 应用程序。

如需进一步了解,请访问 Telepresence 官方网站

4.4.1.9 - 用 Kubectl 调试 Kubernetes 节点

本页演示如何使用 kubectl debug 命令调试在 Kubernetes 集群上运行的节点

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你的 Kubernetes 服务器版本必须不低于版本 1.2. 要获知版本信息,请输入 kubectl version.

你需要有权限创建 Pod 并将这些新 Pod 分配到任意节点。 你还需要被授权创建能够访问主机上文件系统的 Pod。

使用 kubectl debug node 调试节点

使用 kubectl debug node 命令将 Pod 部署到要排查故障的节点上。 此命令在你无法使用 SSH 连接节点时比较有用。 当 Pod 被创建时,Pod 会在节点上打开一个交互的 Shell。 要在名为 “mynode” 的节点上创建一个交互式 Shell,运行:

kubectl debug node/mynode -it --image=ubuntu
Creating debugging pod node-debugger-mynode-pdx84 with container debugger on node mynode.
If you don't see a command prompt, try pressing enter.
root@mynode:/#

调试命令有助于收集信息和排查问题。 你可能使用的命令包括 ipifconfigncpingps 等等。 你还可以从各种包管理器安装 mtrtcpdumpcurl 等其他工具。

用于调试的 Pod 可以访问节点的根文件系统,该文件系统挂载在 Pod 中的 /host 路径。 如果你在 filesystem 名字空间中运行 kubelet, 则正调试的 Pod 将看到此名字空间的根,而不是整个节点的根。 对于典型的 Linux 节点,你可以查看以下路径找到一些重要的日志:

/host/var/log/kubelet.log
负责在节点上运行容器的 kubelet 所产生的日志。
/host/var/log/kube-proxy.log
负责将流量导向到 Service 端点的 kube-proxy 所产生的日志。
/host/var/log/containerd.log
在节点上运行的 containerd 进程所产生的日志。
/host/var/log/syslog
显示常规消息以及系统相关信息。
/host/var/log/kern.log
显示内核日志。

当在节点上创建一个调试会话时,需谨记:

  • kubectl debug 根据节点的名称自动生成新 Pod 的名称。
  • 节点的根文件系统将被挂载在 /host
  • 尽管容器运行在主机 IPC、Network 和 PID 名字空间中,但 Pod 没有特权。 这意味着读取某些进程信息可能会失败,这是因为访问这些信息仅限于超级用户 (superuser)。 例如,chroot /host 将失败。如果你需要一个有特权的 Pod,请手动创建。

清理现场

当你使用正调试的 Pod 完成时,将其删除:

kubectl get pods
NAME                          READY   STATUS       RESTARTS   AGE
node-debugger-mynode-pdx84    0/1     Completed    0          8m1s
# 相应更改 Pod 名称
kubectl delete pod node-debugger-mynode-pdx84 --now
pod "node-debugger-mynode-pdx84" deleted

4.4.2 - 应用故障排除

调试常见的容器应用问题.

该文档包含一组用于解决容器化应用程序问题的资源。 它涵盖了诸如 Kubernetes 资源(如 Pod、Service 或 StatefulSets)的常见问题、 关于理解容器终止消息的建议以及调试正在运行的容器的方法。

4.4.2.1 - 调试 Pod

本指南帮助用户调试那些部署到 Kubernetes 上后没有正常运行的应用。 本指南 并非 指导用户如何调试集群。 如果想调试集群的话,请参阅这里

诊断问题

故障排查的第一步是先给问题分类。问题是什么?是关于 Pod、Replication Controller 还是 Service?

调试 Pod

调试 Pod 的第一步是查看 Pod 信息。用如下命令查看 Pod 的当前状态和最近的事件:

kubectl describe pods ${POD_NAME}

查看一下 Pod 中的容器所处的状态。这些容器的状态都是 Running 吗?最近有没有重启过?

后面的调试都是要依靠 Pod 的状态的。

Pod 停滞在 Pending 状态

如果一个 Pod 停滞在 Pending 状态,表示 Pod 没有被调度到节点上。 通常这是因为某种类型的资源不足导致无法调度。 查看上面的 kubectl describe ... 命令的输出,其中应该显示了为什么没被调度的原因。 常见原因如下:

  • 资源不足: 你可能耗尽了集群上所有的 CPU 或内存。此时,你需要删除 Pod、调整资源请求或者为集群添加节点。 更多信息请参阅计算资源文档

  • 使用了 hostPort: 如果绑定 Pod 到 hostPort,那么能够运行该 Pod 的节点就有限了。 多数情况下,hostPort 是非必要的,而应该采用 Service 对象来暴露 Pod。 如果确实需要使用 hostPort,那么集群中节点的个数就是所能创建的 Pod 的数量上限。

Pod 停滞在 Waiting 状态

如果 Pod 停滞在 Waiting 状态,则表示 Pod 已经被调度到某工作节点,但是无法在该节点上运行。 同样,kubectl describe ... 命令的输出可能很有用。 Waiting 状态的最常见原因是拉取镜像失败。要检查的有三个方面:

  • 确保镜像名字拼写正确。
  • 确保镜像已被推送到镜像仓库。
  • 尝试手动是否能拉取镜像。例如,如果你在你的 PC 上使用 Docker,请运行 docker pull <镜像>

Pod 停滞在 terminating 状态

如果 Pod 停滞在 Terminating 状态,表示已发出删除 Pod 的请求, 但控制平面无法删除该 Pod 对象。

如果 Pod 拥有 Finalizer 并且集群中安装了准入 Webhook, 可能会导致控制平面无法移除 Finalizer,从而导致 Pod 出现此问题。

要确认这种情况,请检查你的集群中是否有 ValidatingWebhookConfiguration 或 MutatingWebhookConfiguration 处理 pods 资源的 UPDATE 操作。

如果 Webhook 是由第三方提供的:

  • 确保你使用的是最新版。
  • 禁用处理 UPDATE 操作的 Webhook。
  • 向相关供应商报告问题。

如果你是 Webhook 的作者:

  • 对于变更性质的 Webhook,请确保在处理 UPDATE 操作时不要更改不可变字段。 例如,一般不允许更改 containers
  • 对于验证性质的 Webhook,请确保你的验证策略仅被应用于新的更改之上。换句话说, 你应该允许存在违规的现有 Pod 通过验证。这样可以确保在安装验证性质的 Webhook 之前创建的 Pod 可以继续运行。

Pod 处于 Crashing 或别的不健康状态

一旦 Pod 被调度, 就可以采用调试运行中的 Pod 中的方法来进一步调试。

Pod 处于 Running 态但是没有正常工作

如果 Pod 行为不符合预期,很可能 Pod 描述(例如你本地机器上的 mypod.yaml)中有问题, 并且该错误在创建 Pod 时被忽略掉,没有报错。 通常,Pod 的定义中节区嵌套关系错误、字段名字拼错的情况都会引起对应内容被忽略掉。 例如,如果你误将 command 写成 commnd,Pod 虽然可以创建, 但它不会执行你期望它执行的命令行。

可以做的第一件事是删除你的 Pod,并尝试带有 --validate 选项重新创建。 例如,运行 kubectl apply --validate -f mypod.yaml。 如果 command 被误拼成 commnd,你将会看到下面的错误信息:

I0805 10:43:25.129850   46757 schema.go:126] unknown field: commnd
I0805 10:43:25.129973   46757 schema.go:129] this may be a false alarm, see https://github.com/kubernetes/kubernetes/issues/6842
pods/mypod

接下来就要检查的是 API 服务器上的 Pod 与你所期望创建的是否匹配 (例如,你原本使用本机上的一个 YAML 文件来创建 Pod)。 例如,运行 kubectl get pods/mypod -o yaml > mypod-on-apiserver.yaml, 之后手动比较 mypod.yaml 与从 API 服务器取回的 Pod 描述。 从 API 服务器处获得的 YAML 通常包含一些创建 Pod 所用的 YAML 中不存在的行,这是正常的。 不过,如果如果源文件中有些行在 API 服务器版本中不存在,则意味着 Pod 规约是有问题的。

调试副本控制器

副本控制器相对比较简单直接。它们要么能创建 Pod,要么不能。 如果不能创建 Pod,请参阅上述说明调试 Pod。

你也可以使用 kubectl describe rc ${CONTROLLER_NAME} 命令来检视副本控制器相关的事件。

调试 Service

服务支持在多个 Pod 间负载均衡。 有一些常见的问题可以造成服务无法正常工作。 以下说明将有助于调试服务的问题。

首先,验证服务是否有端点。对于每一个 Service 对象,API 服务器为其提供对应的 endpoints 资源。

通过如下命令可以查看 endpoints 资源:

kubectl get endpoints ${SERVICE_NAME}

确保 Endpoints 与服务成员 Pod 个数一致。 例如,如果你的 Service 用来运行 3 个副本的 nginx 容器,你应该会在 Service 的 Endpoints 中看到 3 个不同的 IP 地址。

服务缺少 Endpoints

如果没有 Endpoints,请尝试使用 Service 所使用的标签列出 Pod。 假定你的服务包含如下标签选择算符:

...
spec:
  - selector:
     name: nginx
     type: frontend

你可以使用如下命令列出与选择算符相匹配的 Pod,并验证这些 Pod 是否归属于创建的服务:

kubectl get pods --selector=name=nginx,type=frontend

验证 Pod 的 containerPort 与服务的 targetPort 是否匹配。

网络流量未被转发

请参阅调试 Service 了解更多信息。

接下来

如果上述方法都不能解决你的问题, 请按照调试 Service 文档中的介绍, 确保你的 Service 处于 Running 状态,有 Endpoints 被创建,Pod 真的在提供服务; DNS 服务已配置并正常工作,iptables 规则也已安装并且 kube-proxy 也没有异常行为。

你也可以访问故障排查文档来获取更多信息。

4.4.2.2 - 调试 Service

对于新安装的 Kubernetes,经常出现的问题是 Service 无法正常运行。你已经通过 Deployment(或其他工作负载控制器)运行了 Pod,并创建 Service , 但是当你尝试访问它时,没有任何响应。此文档有望对你有所帮助并找出问题所在。

在 Pod 中运行命令

对于这里的许多步骤,你可能希望知道运行在集群中的 Pod 看起来是什么样的。 最简单的方法是运行一个交互式的 busybox Pod:

kubectl run -it --rm --restart=Never busybox --image=gcr.io/google-containers/busybox sh

如果你已经有了你想使用的正在运行的 Pod,则可以运行以下命令去进入:

kubectl exec <POD-NAME> -c <CONTAINER-NAME> -- <COMMAND>

设置

为了完成本次实践的任务,我们先运行几个 Pod。 由于你可能正在调试自己的 Service,所以,你可以使用自己的信息进行替换, 或者你也可以跟着教程并开始下面的步骤来获得第二个数据点。

kubectl create deployment hostnames --image=registry.k8s.io/serve_hostname
deployment.apps/hostnames created

kubectl 命令将打印创建或变更的资源的类型和名称,它们可以在后续命令中使用。 让我们将这个 deployment 的副本数扩至 3。

kubectl scale deployment hostnames --replicas=3
deployment.apps/hostnames scaled

请注意这与你使用以下 YAML 方式启动 Deployment 类似:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: hostnames
  name: hostnames
spec:
  selector:
    matchLabels:
      app: hostnames
  replicas: 3
  template:
    metadata:
      labels:
        app: hostnames
    spec:
      containers:
      - name: hostnames
        image: registry.k8s.io/serve_hostname

"app" 标签是 kubectl create deployment 根据 Deployment 名称自动设置的。

确认你的 Pod 是运行状态:

kubectl get pods -l app=hostnames
NAME                        READY     STATUS    RESTARTS   AGE
hostnames-632524106-bbpiw   1/1       Running   0          2m
hostnames-632524106-ly40y   1/1       Running   0          2m
hostnames-632524106-tlaok   1/1       Running   0          2m

你还可以确认你的 Pod 是否正在提供服务。你可以获取 Pod IP 地址列表并直接对其进行测试。

kubectl get pods -l app=hostnames \
    -o go-template='{{range .items}}{{.status.podIP}}{{"\n"}}{{end}}'
10.244.0.5
10.244.0.6
10.244.0.7

用于本教程的示例容器通过 HTTP 在端口 9376 上提供其自己的主机名, 但是如果要调试自己的应用程序,则需要使用你的 Pod 正在侦听的端口号。

在 Pod 内运行:

for ep in 10.244.0.5:9376 10.244.0.6:9376 10.244.0.7:9376; do
    wget -qO- $ep
done

输出类似这样:

hostnames-632524106-bbpiw
hostnames-632524106-ly40y
hostnames-632524106-tlaok

如果此时你没有收到期望的响应,则你的 Pod 状态可能不健康,或者可能没有在你认为正确的端口上进行监听。 你可能会发现 kubectl logs 命令对于查看正在发生的事情很有用, 或者你可能需要通过kubectl exec 直接进入 Pod 中并从那里进行调试。

假设到目前为止一切都已按计划进行,那么你可以开始调查为何你的 Service 无法正常工作。

Service 是否存在?

细心的读者会注意到我们实际上尚未创建 Service —— 这是有意而为之。 这一步有时会被遗忘,这是首先要检查的步骤。

那么,如果我尝试访问不存在的 Service 会怎样? 假设你有另一个 Pod 通过名称匹配到 Service,你将得到类似结果:

wget -O- hostnames
Resolving hostnames (hostnames)... failed: Name or service not known.
wget: unable to resolve host address 'hostnames'

首先要检查的是该 Service 是否真实存在:

kubectl get svc hostnames
No resources found.
Error from server (NotFound): services "hostnames" not found

让我们创建 Service。 和以前一样,在这次实践中 —— 你可以在此处使用自己的 Service 的内容。

kubectl expose deployment hostnames --port=80 --target-port=9376
service/hostnames exposed

重新运行查询命令:

kubectl get svc hostnames
NAME        TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
hostnames   ClusterIP   10.0.1.175   <none>        80/TCP    5s

现在你知道了 Service 确实存在。

同前,此步骤效果与通过 YAML 方式启动 Service 一样:

apiVersion: v1
kind: Service
metadata:
  labels:
    app: hostnames
  name: hostnames
spec:
  selector:
    app: hostnames
  ports:
  - name: default
    protocol: TCP
    port: 80
    targetPort: 9376

为了突出配置范围的完整性,你在此处创建的 Service 使用的端口号与 Pods 不同。 对于许多真实的 Service,这些值可以是相同的。

是否存在影响目标 Pod 的网络策略入站规则?

如果你部署了任何可能影响到 hostnames-* Pod 的传入流量的网络策略入站规则, 则需要对其进行检查。

详细信息,请参阅网络策略

Service 是否可通过 DNS 名字访问?

通常客户端通过 DNS 名称来匹配到 Service。

从相同命名空间下的 Pod 中运行以下命令:

nslookup hostnames
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      hostnames
Address 1: 10.0.1.175 hostnames.default.svc.cluster.local

如果失败,那么你的 Pod 和 Service 可能位于不同的命名空间中, 请尝试使用限定命名空间的名称(同样在 Pod 内运行):

nslookup hostnames.default
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      hostnames.default
Address 1: 10.0.1.175 hostnames.default.svc.cluster.local

如果成功,那么需要调整你的应用,使用跨命名空间的名称去访问它, 或者在相同的命名空间中运行应用和 Service。如果仍然失败,请尝试一个完全限定的名称:

nslookup hostnames.default.svc.cluster.local
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      hostnames.default.svc.cluster.local
Address 1: 10.0.1.175 hostnames.default.svc.cluster.local

注意这里的后缀:"default.svc.cluster.local"。"default" 是我们正在操作的命名空间。 "svc" 表示这是一个 Service。"cluster.local" 是你的集群域,在你自己的集群中可能会有所不同。

你也可以在集群中的节点上尝试此操作:

nslookup hostnames.default.svc.cluster.local 10.0.0.10
Server:         10.0.0.10
Address:        10.0.0.10#53

Name:   hostnames.default.svc.cluster.local
Address: 10.0.1.175

如果你能够使用完全限定的名称查找,但不能使用相对名称,则需要检查你 Pod 中的 /etc/resolv.conf 文件是否正确。在 Pod 中运行以下命令:

cat /etc/resolv.conf

你应该可以看到类似这样的输出:

nameserver 10.0.0.10
search default.svc.cluster.local svc.cluster.local cluster.local example.com
options ndots:5

nameserver 行必须指示你的集群的 DNS Service, 它是通过 --cluster-dns 标志传递到 kubelet 的。

search 行必须包含一个适当的后缀,以便查找 Service 名称。 在本例中,它查找本地命名空间(default.svc.cluster.local)中的服务和所有命名空间 (svc.cluster.local)中的服务,最后在集群(cluster.local)中查找服务的名称。 根据你自己的安装情况,可能会有额外的记录(最多 6 条)。 集群后缀是通过 --cluster-domain 标志传递给 kubelet 的。 本文中,我们假定后缀是 “cluster.local”。 你的集群配置可能不同,这种情况下,你应该在上面的所有命令中更改它。

options 行必须设置足够高的 ndots,以便 DNS 客户端库考虑搜索路径。 在默认情况下,Kubernetes 将这个值设置为 5,这个值足够高,足以覆盖它生成的所有 DNS 名称。

是否存在 Service 能通过 DNS 名称访问?

如果上面的方式仍然失败,DNS 查找不到你需要的 Service ,你可以后退一步, 看看还有什么其它东西没有正常工作。 Kubernetes 主 Service 应该一直是工作的。在 Pod 中运行如下命令:

nslookup kubernetes.default
Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      kubernetes.default
Address 1: 10.0.0.1 kubernetes.default.svc.cluster.local

如果失败,你可能需要转到本文的 kube-proxy 节, 或者甚至回到文档的顶部重新开始,但不是调试你自己的 Service ,而是调试 DNS Service。

Service 能够通过 IP 访问么?

假设你已经确认 DNS 工作正常,那么接下来要测试的是你的 Service 能否通过它的 IP 正常访问。 从集群中的一个 Pod,尝试访问 Service 的 IP(从上面的 kubectl get 命令获取)。

for i in $(seq 1 3); do 
    wget -qO- 10.0.1.175:80
done

输出应该类似这样:

hostnames-632524106-bbpiw
hostnames-632524106-ly40y
hostnames-632524106-tlaok

如果 Service 状态是正常的,你应该得到正确的响应。 如果没有,有很多可能出错的地方,请继续阅读。

Service 的配置是否正确?

这听起来可能很愚蠢,但你应该两次甚至三次检查你的 Service 配置是否正确,并且与你的 Pod 匹配。 查看你的 Service 配置并验证它:

kubectl get service hostnames -o json
{
    "kind": "Service",
    "apiVersion": "v1",
    "metadata": {
        "name": "hostnames",
        "namespace": "default",
        "uid": "428c8b6c-24bc-11e5-936d-42010af0a9bc",
        "resourceVersion": "347189",
        "creationTimestamp": "2015-07-07T15:24:29Z",
        "labels": {
            "app": "hostnames"
        }
    },
    "spec": {
        "ports": [
            {
                "name": "default",
                "protocol": "TCP",
                "port": 80,
                "targetPort": 9376,
                "nodePort": 0
            }
        ],
        "selector": {
            "app": "hostnames"
        },
        "clusterIP": "10.0.1.175",
        "type": "ClusterIP",
        "sessionAffinity": "None"
    },
    "status": {
        "loadBalancer": {}
    }
}
  • 你想要访问的 Service 端口是否在 spec.ports[] 中列出?
  • targetPort 对你的 Pod 来说正确吗(许多 Pod 使用与 Service 不同的端口)?
  • 如果你想使用数值型端口,那么它的类型是一个数值(9376)还是字符串 “9376”?
  • 如果你想使用名称型端口,那么你的 Pod 是否暴露了一个同名端口?
  • 端口的 protocol 和 Pod 的是否对应?

Service 有 Endpoints 吗?

如果你已经走到了这一步,你已经确认你的 Service 被正确定义,并能通过 DNS 解析。 现在,让我们检查一下,你运行的 Pod 确实是被 Service 选中的。

早些时候,我们已经看到 Pod 是运行状态。我们可以再检查一下:

kubectl get pods -l app=hostnames
NAME                        READY     STATUS    RESTARTS   AGE
hostnames-632524106-bbpiw   1/1       Running   0          1h
hostnames-632524106-ly40y   1/1       Running   0          1h
hostnames-632524106-tlaok   1/1       Running   0          1h

-l app=hostnames 参数是在 Service 上配置的标签选择器。

“AGE” 列表明这些 Pod 已经启动一个小时了,这意味着它们运行良好,而未崩溃。

"RESTARTS" 列表明 Pod 没有经常崩溃或重启。经常性崩溃可能导致间歇性连接问题。 如果重启次数过大,通过调试 Pod 了解相关技术。

在 Kubernetes 系统中有一个控制回路,它评估每个 Service 的选择算符,并将结果保存到 Endpoints 对象中。

kubectl get endpoints hostnames
NAME        ENDPOINTS
hostnames   10.244.0.5:9376,10.244.0.6:9376,10.244.0.7:9376

这证实 Endpoints 控制器已经为你的 Service 找到了正确的 Pods。 如果 ENDPOINTS 列的值为 <none>,则应检查 Service 的 spec.selector 字段, 以及你实际想选择的 Pod 的 metadata.labels 的值。 常见的错误是输入错误或其他错误,例如 Service 想选择 app=hostnames,但是 Deployment 指定的是 run=hostnames。在 1.18之前的版本中 kubectl run 也可以被用来创建 Deployment。

Pod 工作正常吗?

至此,你知道你的 Service 已存在,并且已匹配到你的Pod。在本实验的开始,你已经检查了 Pod 本身。 让我们再次检查 Pod 是否确实在工作 - 你可以绕过 Service 机制并直接转到 Pod, 如上面的 Endpoints 所示。

在 Pod 中运行:

for ep in 10.244.0.5:9376 10.244.0.6:9376 10.244.0.7:9376; do
    wget -qO- $ep
done

输出应该类似这样:

hostnames-632524106-bbpiw
hostnames-632524106-ly40y
hostnames-632524106-tlaok

你希望 Endpoint 列表中的每个 Pod 都返回自己的主机名。 如果情况并非如此(或你自己的 Pod 的正确行为是什么),你应调查发生了什么事情。

kube-proxy 正常工作吗?

如果你到达这里,则说明你的 Service 正在运行,拥有 Endpoints,Pod 真正在提供服务。 此时,整个 Service 代理机制是可疑的。让我们一步一步地确认它没问题。

Service 的默认实现(在大多数集群上应用的)是 kube-proxy。 这是一个在每个节点上运行的程序,负责配置用于提供 Service 抽象的机制之一。 如果你的集群不使用 kube-proxy,则以下各节将不适用,你将必须检查你正在使用的 Service 的实现方式。

kube-proxy 正常运行吗? {#is-kube-proxy working}

确认 kube-proxy 正在节点上运行。在节点上直接运行,你将会得到类似以下的输出:

ps auxw | grep kube-proxy
root  4194  0.4  0.1 101864 17696 ?    Sl Jul04  25:43 /usr/local/bin/kube-proxy --master=https://kubernetes-master --kubeconfig=/var/lib/kube-proxy/kubeconfig --v=2

下一步,确认它并没有出现明显的失败,比如连接主节点失败。要做到这一点,你必须查看日志。 访问日志的方式取决于你节点的操作系统。 在某些操作系统上日志是一个文件,如 /var/log/messages kube-proxy.log, 而其他操作系统使用 journalctl 访问日志。你应该看到输出类似于:

I1027 22:14:53.995134    5063 server.go:200] Running in resource-only container "/kube-proxy"
I1027 22:14:53.998163    5063 server.go:247] Using iptables Proxier.
I1027 22:14:54.038140    5063 proxier.go:352] Setting endpoints for "kube-system/kube-dns:dns-tcp" to [10.244.1.3:53]
I1027 22:14:54.038164    5063 proxier.go:352] Setting endpoints for "kube-system/kube-dns:dns" to [10.244.1.3:53]
I1027 22:14:54.038209    5063 proxier.go:352] Setting endpoints for "default/kubernetes:https" to [10.240.0.2:443]
I1027 22:14:54.038238    5063 proxier.go:429] Not syncing iptables until Services and Endpoints have been received from master
I1027 22:14:54.040048    5063 proxier.go:294] Adding new service "default/kubernetes:https" at 10.0.0.1:443/TCP
I1027 22:14:54.040154    5063 proxier.go:294] Adding new service "kube-system/kube-dns:dns" at 10.0.0.10:53/UDP
I1027 22:14:54.040223    5063 proxier.go:294] Adding new service "kube-system/kube-dns:dns-tcp" at 10.0.0.10:53/TCP

如果你看到有关无法连接主节点的错误消息,则应再次检查节点配置和安装步骤。

kube-proxy 无法正确运行的可能原因之一是找不到所需的 conntrack 二进制文件。 在一些 Linux 系统上,这也是可能发生的,这取决于你如何安装集群, 例如,你是手动开始一步步安装 Kubernetes。如果是这样的话,你需要手动安装 conntrack 包(例如,在 Ubuntu 上使用 sudo apt install conntrack),然后重试。

Kube-proxy 可以以若干模式之一运行。在上述日志中,Using iptables Proxier 行表示 kube-proxy 在 "iptables" 模式下运行。 最常见的另一种模式是 "ipvs"。

Iptables 模式

在 "iptables" 模式中, 你应该可以在节点上看到如下输出:

iptables-save | grep hostnames
-A KUBE-SEP-57KPRZ3JQVENLNBR -s 10.244.3.6/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x00004000/0x00004000
-A KUBE-SEP-57KPRZ3JQVENLNBR -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.3.6:9376
-A KUBE-SEP-WNBA2IHDGP2BOBGZ -s 10.244.1.7/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x00004000/0x00004000
-A KUBE-SEP-WNBA2IHDGP2BOBGZ -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.1.7:9376
-A KUBE-SEP-X3P2623AGDH6CDF3 -s 10.244.2.3/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x00004000/0x00004000
-A KUBE-SEP-X3P2623AGDH6CDF3 -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.2.3:9376
-A KUBE-SERVICES -d 10.0.1.175/32 -p tcp -m comment --comment "default/hostnames: cluster IP" -m tcp --dport 80 -j KUBE-SVC-NWV5X2332I4OT4T3
-A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -m statistic --mode random --probability 0.33332999982 -j KUBE-SEP-WNBA2IHDGP2BOBGZ
-A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-X3P2623AGDH6CDF3
-A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -j KUBE-SEP-57KPRZ3JQVENLNBR

对于每个 Service 的每个端口,应有 1 条 KUBE-SERVICES 规则、一个 KUBE-SVC-<hash> 链。 对于每个 Pod 末端,在那个 KUBE-SVC-<hash> 链中应该有一些规则与之对应,还应该 有一个 KUBE-SEP-<hash> 链与之对应,其中包含为数不多的几条规则。 实际的规则数量可能会根据你实际的配置(包括 NodePort 和 LoadBalancer 服务)有所不同。

IPVS 模式

在 "ipvs" 模式中, 你应该在节点下看到如下输出:

ipvsadm -ln
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
...
TCP  10.0.1.175:80 rr
  -> 10.244.0.5:9376               Masq    1      0          0
  -> 10.244.0.6:9376               Masq    1      0          0
  -> 10.244.0.7:9376               Masq    1      0          0
...

对于每个 Service 的每个端口,还有 NodePort,External IP 和 LoadBalancer 类型服务 的 IP,kube-proxy 将创建一个虚拟服务器。 对于每个 Pod 末端,它将创建相应的真实服务器。 在此示例中,服务主机名(10.0.1.175:80)拥有 3 个末端(10.244.0.5:937610.244.0.6:937610.244.0.7:9376)。

kube-proxy 是否在执行代理操作?

假设你确实遇到上述情况之一,请重试从节点上通过 IP 访问你的 Service :

curl 10.0.1.175:80
hostnames-632524106-bbpiw

如果这步操作仍然失败,请查看 kube-proxy 日志中的特定行,如:

Setting endpoints for default/hostnames:default to [10.244.0.5:9376 10.244.0.6:9376 10.244.0.7:9376]

如果你没有看到这些,请尝试将 -v 标志设置为 4 并重新启动 kube-proxy,然后再查看日志。

边缘案例: Pod 无法通过 Service IP 连接到它本身

这听起来似乎不太可能,但是确实可能发生,并且应该可以工作。

如果网络没有为“发夹模式(Hairpin)”流量生成正确配置, 通常当 kube-proxyiptables 模式运行,并且 Pod 与桥接网络连接时,就会发生这种情况。 kubelet 提供了 hairpin-mode 标志。 如果 Service 的末端尝试访问自己的 Service VIP,则该端点可以把流量负载均衡回来到它们自身。 hairpin-mode 标志必须被设置为 hairpin-veth 或者 promiscuous-bridge

诊断此类问题的常见步骤如下:

  • 确认 hairpin-mode 被设置为 hairpin-vethpromiscuous-bridge。 你应该可以看到下面这样。本例中 hairpin-mode 被设置为 promiscuous-bridge

    ps auxw | grep kubelet
    
    root      3392  1.1  0.8 186804 65208 ?        Sl   00:51  11:11 /usr/local/bin/kubelet --enable-debugging-handlers=true --config=/etc/kubernetes/manifests --allow-privileged=True --v=4 --cluster-dns=10.0.0.10 --cluster-domain=cluster.local --configure-cbr0=true --cgroup-root=/ --system-cgroups=/system --hairpin-mode=promiscuous-bridge --runtime-cgroups=/docker-daemon --kubelet-cgroups=/kubelet --babysit-daemons=true --max-pods=110 --serialize-image-pulls=false --outofdisk-transition-frequency=0
    
  • 确认有效的 hairpin-mode。要做到这一点,你必须查看 kubelet 日志。 访问日志取决于节点的操作系统。在一些操作系统上,它是一个文件,如 /var/log/kubelet.log, 而其他操作系统则使用 journalctl 访问日志。请注意,由于兼容性, 有效的 hairpin-mode 可能不匹配 --hairpin-mode 标志。在 kubelet.log 中检查是否有带有关键字 hairpin 的日志行。应该有日志行指示有效的 hairpin-mode,就像下面这样。

    I0629 00:51:43.648698    3252 kubelet.go:380] Hairpin mode set to "promiscuous-bridge"
    
  • 如果有效的发夹模式是 hairpin-veth, 要保证 Kubelet 有操作节点上 /sys 的权限。 如果一切正常,你将会看到如下输出:

    for intf in /sys/devices/virtual/net/cbr0/brif/*; do cat $intf/hairpin_mode; done
    
    1
    1
    1
    1
    
  • 如果有效的发卡模式是 promiscuous-bridge, 要保证 Kubelet 有操作节点上 Linux 网桥的权限。如果 cbr0 桥正在被使用且被正确设置,你将会看到如下输出:

    ifconfig cbr0 |grep PROMISC
    
    UP BROADCAST RUNNING PROMISC MULTICAST  MTU:1460  Metric:1
    
  • 如果以上步骤都不能解决问题,请寻求帮助。

寻求帮助

如果你走到这一步,那么就真的是奇怪的事情发生了。你的 Service 正在运行,有 Endpoints 存在, 你的 Pods 也确实在提供服务。你的 DNS 正常,iptables 规则已经安装,kube-proxy 看起来也正常。 然而 Service 还是没有正常工作。这种情况下,请告诉我们,以便我们可以帮助调查!

通过 Slack 或者 Forum 或者 GitHub 联系我们。

接下来

访问故障排查概述文档获取更多信息。

4.4.2.3 - 调试 StatefulSet

此任务展示如何调试 StatefulSet。

准备开始

  • 你需要有一个 Kubernetes 集群,已配置好的 kubectl 命令行工具与你的集群进行通信。
  • 你应该有一个运行中的 StatefulSet,以便用于调试。

调试 StatefulSet

StatefulSet 在创建 Pod 时为其设置了 app.kubernetes.io/name=MyApp 标签,列出仅属于某 StatefulSet 的所有 Pod 时,可以使用以下命令:

kubectl get pods -l app.kubernetes.io/name=MyApp

如果你发现列出的任何 Pod 长时间处于 UnknownTerminating 状态,请参阅 删除 StatefulSet Pod 了解如何处理它们的说明。 你可以参考调试 Pod 来调试 StatefulSet 中的各个 Pod。

接下来

进一步了解如何调试 Init 容器

4.4.2.4 - 确定 Pod 失败的原因

本文介绍如何编写和读取容器的终止消息。

终止消息为容器提供了一种方法,可以将有关致命事件的信息写入某个位置, 在该位置可以通过仪表板和监控软件等工具轻松检索和显示致命事件。 在大多数情况下,你放入终止消息中的信息也应该写入 常规 Kubernetes 日志

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

读写终止消息

在本练习中,你将创建运行一个容器的 Pod。 配置文件指定在容器启动时要运行的命令。

apiVersion: v1
kind: Pod
metadata:
  name: termination-demo
spec:
  containers:
  - name: termination-demo-container
    image: debian
    command: ["/bin/sh"]
    args: ["-c", "sleep 10 && echo Sleep expired > /dev/termination-log"]
  1. 基于 YAML 配置文件创建 Pod:

    kubectl apply -f https://k8s.io/examples/debug/termination.yaml   
    

    YAML 文件中,在 commandargs 字段,你可以看到容器休眠 10 秒然后将 "Sleep expired" 写入 /dev/termination-log 文件。 容器写完 "Sleep expired" 消息后就终止了。

  1. 显示 Pod 的信息:

    kubectl get pod termination-demo
    

    重复前面的命令直到 Pod 不再运行。

  1. 显示 Pod 的详细信息:

    kubectl get pod termination-demo --output=yaml
    

    输出结果包含 "Sleep expired" 消息:

    apiVersion: v1
    kind: Pod
    ...
        lastState:
          terminated:
            containerID: ...
            exitCode: 0
            finishedAt: ...
            message: |
              Sleep expired          
            ...
    
  1. 使用 Go 模板过滤输出结果,使其只含有终止消息:

    kubectl get pod termination-demo -o go-template="{{range .status.containerStatuses}}{{.lastState.terminated.message}}{{end}}"
    

    如果你正在运行多容器 Pod,则可以使用 Go 模板来包含容器的名称。这样,你可以发现哪些容器出现故障:

    kubectl get pod multi-container-pod -o go-template='{{range .status.containerStatuses}}{{printf "%s:\n%s\n\n" .name .lastState.terminated.message}}{{end}}'
    

定制终止消息

Kubernetes 从容器的 terminationMessagePath 字段中指定的终止消息文件中检索终止消息, 默认值为 /dev/termination-log。 通过定制这个字段,你可以告诉 Kubernetes 使用不同的文件。 Kubernetes 使用指定文件中的内容在成功和失败时填充容器的状态消息。

终止消息旨在简要说明最终状态,例如断言失败消息。 kubelet 会截断长度超过 4096 字节的消息。

所有容器的总消息长度限制为 12KiB,将会在每个容器之间平均分配。 例如,如果有 12 个容器(initContainerscontainers), 每个容器都有 1024 字节的可用终止消息空间。

默认的终止消息路径是 /dev/termination-log。 Pod 启动后不能设置终止消息路径。

在下例中,容器将终止消息写入 /tmp/my-log 给 Kubernetes 来检索:

apiVersion: v1
kind: Pod
metadata:
  name: msg-path-demo
spec:
  containers:
  - name: msg-path-demo-container
    image: debian
    terminationMessagePath: "/tmp/my-log"

此外,用户可以设置容器的 terminationMessagePolicy 字段,以便进一步自定义。 此字段默认为 "File",这意味着仅从终止消息文件中检索终止消息。 通过将 terminationMessagePolicy 设置为 "FallbackToLogsOnError",你就可以告诉 Kubernetes,在容器因错误退出时,如果终止消息文件为空,则使用容器日志输出的最后一块作为终止消息。 日志输出限制为 2048 字节或 80 行,以较小者为准。

接下来

4.4.2.5 - 调试 Init 容器

此页显示如何核查与 Init 容器执行相关的问题。 下面的示例命令行将 Pod 称为 <pod-name>,而 Init 容器称为 <init-container-1><init-container-2>

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

检查 Init 容器的状态

显示你的 Pod 的状态:

kubectl get pod <pod-name>

例如,状态 Init:1/2 表明两个 Init 容器中的一个已经成功完成:

NAME         READY     STATUS     RESTARTS   AGE
<pod-name>   0/1       Init:1/2   0          7s

更多状态值及其含义请参考理解 Pod 的状态

获取 Init 容器详情

查看 Init 容器运行的更多详情:

kubectl describe pod <pod-name>

例如,对于包含两个 Init 容器的 Pod 可能显示如下信息:

Init Containers:
  <init-container-1>:
    Container ID:    ...
    ...
    State:           Terminated
      Reason:        Completed
      Exit Code:     0
      Started:       ...
      Finished:      ...
    Ready:           True
    Restart Count:   0
    ...
  <init-container-2>:
    Container ID:    ...
    ...
    State:           Waiting
      Reason:        CrashLoopBackOff
    Last State:      Terminated
      Reason:        Error
      Exit Code:     1
      Started:       ...
      Finished:      ...
    Ready:           False
    Restart Count:   3
    ...

你还可以通过编程方式读取 Pod Spec 上的 status.initContainerStatuses 字段,了解 Init 容器的状态:

kubectl get pod nginx --template '{{.status.initContainerStatuses}}'

此命令将返回与原始 JSON 中相同的信息.

通过 Init 容器访问日志

与 Pod 名称一起传递 Init 容器名称,以访问容器的日志。

kubectl logs <pod-name> -c <init-container-2>

运行 Shell 脚本的 Init 容器在执行 Shell 脚本时输出命令本身。 例如,你可以在 Bash 中通过在脚本的开头运行 set -x 来实现。

理解 Pod 的状态

Init: 开头的 Pod 状态汇总了 Init 容器执行的状态。 下表介绍调试 Init 容器时可能看到的一些状态值示例。

状态含义
Init:N/MPod 包含 M 个 Init 容器,其中 N 个已经运行完成。
Init:ErrorInit 容器已执行失败。
Init:CrashLoopBackOffInit 容器执行总是失败。
PendingPod 还没有开始执行 Init 容器。
PodInitializing or RunningPod 已经完成执行 Init 容器。

4.4.2.6 - 调试运行中的 Pod

本页解释如何在节点上调试运行中(或崩溃)的 Pod。

准备开始

  • 你的 Pod 应该已经被调度并正在运行中, 如果你的 Pod 还没有运行,请参阅调试 Pod

  • 对于一些高级调试步骤,你应该知道 Pod 具体运行在哪个节点上,并具有在该节点上运行命令的 shell 访问权限。 你不需要任何访问权限就可以使用 kubectl 去运行一些标准调试步骤。

使用 kubectl describe pod 命令获取 Pod 详情

与之前的例子类似,我们使用一个 Deployment 来创建两个 Pod。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        resources:
          limits:
            memory: "128Mi"
            cpu: "500m"
        ports:
        - containerPort: 80

使用如下命令创建 Deployment:

kubectl apply -f https://k8s.io/examples/application/nginx-with-request.yaml
deployment.apps/nginx-deployment created

使用如下命令查看 Pod 状态:

kubectl get pods
NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-67d4bdd6f5-cx2nz   1/1     Running   0          13s
nginx-deployment-67d4bdd6f5-w6kd7   1/1     Running   0          13s

我们可以使用 kubectl describe pod 命令来查询每个 Pod 的更多信息,比如:

kubectl describe pod nginx-deployment-67d4bdd6f5-w6kd7
Name:         nginx-deployment-67d4bdd6f5-w6kd7
Namespace:    default
Priority:     0
Node:         kube-worker-1/192.168.0.113
Start Time:   Thu, 17 Feb 2022 16:51:01 -0500
Labels:       app=nginx
              pod-template-hash=67d4bdd6f5
Annotations:  <none>
Status:       Running
IP:           10.88.0.3
IPs:
  IP:           10.88.0.3
  IP:           2001:db8::1
Controlled By:  ReplicaSet/nginx-deployment-67d4bdd6f5
Containers:
  nginx:
    Container ID:   containerd://5403af59a2b46ee5a23fb0ae4b1e077f7ca5c5fb7af16e1ab21c00e0e616462a
    Image:          nginx
    Image ID:       docker.io/library/nginx@sha256:2834dc507516af02784808c5f48b7cbe38b8ed5d0f4837f16e78d00deb7e7767
    Port:           80/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Thu, 17 Feb 2022 16:51:05 -0500
    Ready:          True
    Restart Count:  0
    Limits:
      cpu:     500m
      memory:  128Mi
    Requests:
      cpu:        500m
      memory:     128Mi
    Environment:  <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-bgsgp (ro)
Conditions:
  Type              Status
  Initialized       True 
  Ready             True 
  ContainersReady   True 
  PodScheduled      True 
Volumes:
  kube-api-access-bgsgp:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
QoS Class:                   Guaranteed
Node-Selectors:              <none>
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  34s   default-scheduler  Successfully assigned default/nginx-deployment-67d4bdd6f5-w6kd7 to kube-worker-1
  Normal  Pulling    31s   kubelet            Pulling image "nginx"
  Normal  Pulled     30s   kubelet            Successfully pulled image "nginx" in 1.146417389s
  Normal  Created    30s   kubelet            Created container nginx
  Normal  Started    30s   kubelet            Started container nginx

在这里,你可以看到有关容器和 Pod 的配置信息(标签、资源需求等), 以及有关容器和 Pod 的状态信息(状态、就绪、重启计数、事件等)。

容器状态是 Waiting、Running 和 Terminated 之一。 根据状态的不同,还有对应的额外的信息 —— 在这里你可以看到, 对于处于运行状态的容器,系统会告诉你容器的启动时间。

Ready 指示是否通过了最后一个就绪态探测。 (在本例中,容器没有配置就绪态探测;如果没有配置就绪态探测,则假定容器已经就绪。)

Restart Count 告诉你容器已重启的次数; 这些信息对于定位配置了 “Always” 重启策略的容器持续崩溃问题非常有用。

目前,唯一与 Pod 有关的状态是 Ready 状况,该状况表明 Pod 能够为请求提供服务, 并且应该添加到相应服务的负载均衡池中。

最后,你还可以看到与 Pod 相关的近期事件。 “From” 标明记录事件的组件, “Reason” 和 “Message” 告诉你发生了什么。

例子: 调试 Pending 状态的 Pod

可以使用事件来调试的一个常见的场景是,你创建 Pod 无法被调度到任何节点。 比如,Pod 请求的资源比较多,没有任何一个节点能够满足,或者它指定了一个标签,没有节点可匹配。 假定我们创建之前的 Deployment 时指定副本数是 5(不再是 2),并且请求 600 毫核(不再是 500), 对于一个 4 个节点的集群,若每个节点只有 1 个 CPU,这时至少有一个 Pod 不能被调度。 (需要注意的是,其他集群插件 Pod,比如 fluentd、skydns 等等会在每个节点上运行, 如果我们需求 1000 毫核,将不会有 Pod 会被调度。)

kubectl get pods
NAME                                READY     STATUS    RESTARTS   AGE
nginx-deployment-1006230814-6winp   1/1       Running   0          7m
nginx-deployment-1006230814-fmgu3   1/1       Running   0          7m
nginx-deployment-1370807587-6ekbw   1/1       Running   0          1m
nginx-deployment-1370807587-fg172   0/1       Pending   0          1m
nginx-deployment-1370807587-fz9sd   0/1       Pending   0          1m

为了查找 Pod nginx-deployment-1370807587-fz9sd 没有运行的原因,我们可以使用 kubectl describe pod 命令描述 Pod,查看其事件:

kubectl describe pod nginx-deployment-1370807587-fz9sd
  Name:		nginx-deployment-1370807587-fz9sd
  Namespace:	default
  Node:		/
  Labels:		app=nginx,pod-template-hash=1370807587
  Status:		Pending
  IP:
  Controllers:	ReplicaSet/nginx-deployment-1370807587
  Containers:
    nginx:
      Image:	nginx
      Port:	80/TCP
      QoS Tier:
        memory:	Guaranteed
        cpu:	Guaranteed
      Limits:
        cpu:	1
        memory:	128Mi
      Requests:
        cpu:	1
        memory:	128Mi
      Environment Variables:
  Volumes:
    default-token-4bcbi:
      Type:	Secret (a volume populated by a Secret)
      SecretName:	default-token-4bcbi
  Events:
    FirstSeen	LastSeen	Count	From			        SubobjectPath	Type		Reason			    Message
    ---------	--------	-----	----			        -------------	--------	------			    -------
    1m		    48s		    7	    {default-scheduler }			        Warning		FailedScheduling	pod (nginx-deployment-1370807587-fz9sd) failed to fit in any node
  fit failure on node (kubernetes-node-6ta5): Node didn't have enough resource: CPU, requested: 1000, used: 1420, capacity: 2000
  fit failure on node (kubernetes-node-wul5): Node didn't have enough resource: CPU, requested: 1000, used: 1100, capacity: 2000

这里你可以看到由调度器记录的事件,它表明了 Pod 不能被调度的原因是 FailedScheduling(也可能是其他值)。 其 message 部分表明没有任何节点拥有足够多的资源。

要纠正这种情况,可以使用 kubectl scale 更新 Deployment,以指定 4 个或更少的副本。 (或者你可以让 Pod 继续保持这个状态,这是无害的。)

你在 kubectl describe pod 结尾处看到的事件都保存在 etcd 中, 并提供关于集群中正在发生的事情的高级信息。 如果需要列出所有事件,可使用命令:

kubectl get events

但是,需要注意的是,事件是区分名字空间的。 如果你对某些名字空间域的对象(比如 my-namespace 名字下的 Pod)的事件感兴趣, 你需要显式地在命令行中指定名字空间:

kubectl get events --namespace=my-namespace

查看所有 namespace 的事件,可使用 --all-namespaces 参数。

除了 kubectl describe pod 以外,另一种获取 Pod 额外信息(除了 kubectl get pod)的方法 是给 kubectl get pod 增加 -o yaml 输出格式参数。 该命令将以 YAML 格式为你提供比 kubectl describe pod 更多的信息 —— 实际上是系统拥有的关于 Pod 的所有信息。 在这里,你将看到注解(没有标签限制的键值元数据,由 Kubernetes 系统组件在内部使用)、 重启策略、端口和卷等。

kubectl get pod nginx-deployment-1006230814-6winp -o yaml
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: "2022-02-17T21:51:01Z"
  generateName: nginx-deployment-67d4bdd6f5-
  labels:
    app: nginx
    pod-template-hash: 67d4bdd6f5
  name: nginx-deployment-67d4bdd6f5-w6kd7
  namespace: default
  ownerReferences:
  - apiVersion: apps/v1
    blockOwnerDeletion: true
    controller: true
    kind: ReplicaSet
    name: nginx-deployment-67d4bdd6f5
    uid: 7d41dfd4-84c0-4be4-88ab-cedbe626ad82
  resourceVersion: "1364"
  uid: a6501da1-0447-4262-98eb-c03d4002222e
spec:
  containers:
  - image: nginx
    imagePullPolicy: Always
    name: nginx
    ports:
    - containerPort: 80
      protocol: TCP
    resources:
      limits:
        cpu: 500m
        memory: 128Mi
      requests:
        cpu: 500m
        memory: 128Mi
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: kube-api-access-bgsgp
      readOnly: true
  dnsPolicy: ClusterFirst
  enableServiceLinks: true
  nodeName: kube-worker-1
  preemptionPolicy: PreemptLowerPriority
  priority: 0
  restartPolicy: Always
  schedulerName: default-scheduler
  securityContext: {}
  serviceAccount: default
  serviceAccountName: default
  terminationGracePeriodSeconds: 30
  tolerations:
  - effect: NoExecute
    key: node.kubernetes.io/not-ready
    operator: Exists
    tolerationSeconds: 300
  - effect: NoExecute
    key: node.kubernetes.io/unreachable
    operator: Exists
    tolerationSeconds: 300
  volumes:
  - name: kube-api-access-bgsgp
    projected:
      defaultMode: 420
      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
status:
  conditions:
  - lastProbeTime: null
    lastTransitionTime: "2022-02-17T21:51:01Z"
    status: "True"
    type: Initialized
  - lastProbeTime: null
    lastTransitionTime: "2022-02-17T21:51:06Z"
    status: "True"
    type: Ready
  - lastProbeTime: null
    lastTransitionTime: "2022-02-17T21:51:06Z"
    status: "True"
    type: ContainersReady
  - lastProbeTime: null
    lastTransitionTime: "2022-02-17T21:51:01Z"
    status: "True"
    type: PodScheduled
  containerStatuses:
  - containerID: containerd://5403af59a2b46ee5a23fb0ae4b1e077f7ca5c5fb7af16e1ab21c00e0e616462a
    image: docker.io/library/nginx:latest
    imageID: docker.io/library/nginx@sha256:2834dc507516af02784808c5f48b7cbe38b8ed5d0f4837f16e78d00deb7e7767
    lastState: {}
    name: nginx
    ready: true
    restartCount: 0
    started: true
    state:
      running:
        startedAt: "2022-02-17T21:51:05Z"
  hostIP: 192.168.0.113
  phase: Running
  podIP: 10.88.0.3
  podIPs:
  - ip: 10.88.0.3
  - ip: 2001:db8::1
  qosClass: Guaranteed
  startTime: "2022-02-17T21:51:01Z"

检查 Pod 的日志

首先,查看受到影响的容器的日志:

kubectl logs ${POD_NAME} ${CONTAINER_NAME}

如果你的容器之前崩溃过,你可以通过下面命令访问之前容器的崩溃日志:

kubectl logs --previous ${POD_NAME} ${CONTAINER_NAME}

使用容器 exec 进行调试

如果 容器镜像 包含调试程序, 比如从 Linux 和 Windows 操作系统基础镜像构建的镜像,你可以使用 kubectl exec 命令 在特定的容器中运行一些命令:

kubectl exec ${POD_NAME} -c ${CONTAINER_NAME} -- ${CMD} ${ARG1} ${ARG2} ... ${ARGN}

例如,要查看正在运行的 Cassandra Pod 中的日志,可以运行:

kubectl exec cassandra -- cat /var/log/cassandra/system.log

你可以在 kubectl exec 命令后面加上 -i-t 来运行一个连接到你的终端的 Shell,比如:

kubectl exec -it cassandra -- sh

若要了解更多内容,可查看获取正在运行容器的 Shell

使用临时调试容器来进行调试

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

当由于容器崩溃或容器镜像不包含调试程序(例如无发行版镜像等) 而导致 kubectl exec 无法运行时,临时容器对于排除交互式故障很有用。

使用临时容器来调试的例子

你可以使用 kubectl debug 命令来给正在运行中的 Pod 增加一个临时容器。 首先,像示例一样创建一个 pod:

kubectl run ephemeral-demo --image=registry.k8s.io/pause:3.1 --restart=Never

本节示例中使用 pause 容器镜像,因为它不包含调试程序,但是这个方法适用于所有容器镜像。

如果你尝试使用 kubectl exec 来创建一个 shell,你将会看到一个错误,因为这个容器镜像中没有 shell。

kubectl exec -it ephemeral-demo -- sh
OCI runtime exec failed: exec failed: container_linux.go:346: starting container process caused "exec: \"sh\": executable file not found in $PATH": unknown

你可以改为使用 kubectl debug 添加调试容器。 如果你指定 -i 或者 --interactive 参数,kubectl 将自动挂接到临时容器的控制台。

kubectl debug -it ephemeral-demo --image=busybox:1.28 --target=ephemeral-demo
Defaulting debug container name to debugger-8xzrl.
If you don't see a command prompt, try pressing enter.
/ #

此命令添加一个新的 busybox 容器并将其挂接到该容器。--target 参数指定另一个容器的进程命名空间。 这个指定进程命名空间的操作是必需的,因为 kubectl run 不能在它创建的 Pod 中启用共享进程命名空间

你可以使用 kubectl describe 查看新创建的临时容器的状态:

kubectl describe pod ephemeral-demo
...
Ephemeral Containers:
  debugger-8xzrl:
    Container ID:   docker://b888f9adfd15bd5739fefaa39e1df4dd3c617b9902082b1cfdc29c4028ffb2eb
    Image:          busybox
    Image ID:       docker-pullable://busybox@sha256:1828edd60c5efd34b2bf5dd3282ec0cc04d47b2ff9caa0b6d4f07a21d1c08084
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Wed, 12 Feb 2020 14:25:42 +0100
    Ready:          False
    Restart Count:  0
    Environment:    <none>
    Mounts:         <none>
...

使用 kubectl delete 来移除已经结束掉的 Pod:

kubectl delete pod ephemeral-demo

通过 Pod 副本调试

有些时候 Pod 的配置参数使得在某些情况下很难执行故障排查。 例如,在容器镜像中不包含 shell 或者你的应用程序在启动时崩溃的情况下, 就不能通过运行 kubectl exec 来排查容器故障。 在这些情况下,你可以使用 kubectl debug 来创建 Pod 的副本,通过更改配置帮助调试。

在添加新的容器时创建 Pod 副本

当应用程序正在运行但其表现不符合预期时,你会希望在 Pod 中添加额外的调试工具, 这时添加新容器是很有用的。

例如,应用的容器镜像是建立在 busybox 的基础上, 但是你需要 busybox 中并不包含的调试工具。 你可以使用 kubectl run 模拟这个场景:

kubectl run myapp --image=busybox:1.28 --restart=Never -- sleep 1d

通过运行以下命令,建立 myapp 的一个名为 myapp-debug 的副本, 新增了一个用于调试的 Ubuntu 容器,

kubectl debug myapp -it --image=ubuntu --share-processes --copy-to=myapp-debug
Defaulting debug container name to debugger-w7xmf.
If you don't see a command prompt, try pressing enter.
root@myapp-debug:/#

不要忘了清理调试 Pod:

kubectl delete pod myapp myapp-debug

在改变 Pod 命令时创建 Pod 副本

有时更改容器的命令很有用,例如添加调试标志或因为应用崩溃。

为了模拟应用崩溃的场景,使用 kubectl run 命令创建一个立即退出的容器:

kubectl run --image=busybox:1.28 myapp -- false

使用 kubectl describe pod myapp 命令,你可以看到容器崩溃了:

Containers:
  myapp:
    Image:         busybox
    ...
    Args:
      false
    State:          Waiting
      Reason:       CrashLoopBackOff
    Last State:     Terminated
      Reason:       Error
      Exit Code:    1

你可以使用 kubectl debug 命令创建该 Pod 的一个副本, 在该副本中命令改变为交互式 shell:

kubectl debug myapp -it --copy-to=myapp-debug --container=myapp -- sh
If you don't see a command prompt, try pressing enter.
/ #

现在你有了一个可以执行类似检查文件系统路径或者手动运行容器命令的交互式 shell。

不要忘了清理调试 Pod:

kubectl delete pod myapp myapp-debug

在更改容器镜像时拷贝 Pod

在某些情况下,你可能想要改动一个行为异常的 Pod,即从其正常的生产容器镜像更改为包含调试构建程序或其他实用程序的镜像。

下面的例子,用 kubectl run 创建一个 Pod:

kubectl run myapp --image=busybox:1.28 --restart=Never -- sleep 1d

现在可以使用 kubectl debug 创建一个拷贝并将其容器镜像更改为 ubuntu

kubectl debug myapp --copy-to=myapp-debug --set-image=*=ubuntu

--set-imagecontainer_name=image 使用相同的 kubectl set image 语法。 *=ubuntu 表示把所有容器的镜像改为 ubuntu

kubectl delete pod myapp myapp-debug

在节点上通过 shell 来进行调试

如果这些方法都不起作用,你可以找到运行 Pod 的节点,然后创建一个 Pod 运行在该节点上。 你可以通过 kubectl debug 在节点上创建一个交互式 Shell:

kubectl debug node/mynode -it --image=ubuntu
Creating debugging pod node-debugger-mynode-pdx84 with container debugger on node mynode.
If you don't see a command prompt, try pressing enter.
root@ek8s:/#

当在节点上创建调试会话,注意以下要点:

  • kubectl debug 基于节点的名字自动生成新的 Pod 的名字。
  • 节点的根文件系统会被挂载在 /host
  • 新的调试容器运行在主机 IPC 名字空间、主机网络名字空间以及主机 PID 名字空间内, Pod 没有特权,因此读取某些进程信息可能会失败,并且 chroot /host 也可能会失败。
  • 如果你需要一个特权 Pod,需要手动创建。

当你完成节点调试时,不要忘记清理调试 Pod:

kubectl delete pod node-debugger-mynode-pdx84

4.4.2.7 - 获取正在运行容器的 Shell

本文介绍怎样使用 kubectl exec 命令获取正在运行容器的 Shell。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

获取容器的 Shell

在本练习中,你将创建包含一个容器的 Pod。容器运行 nginx 镜像。下面是 Pod 的配置文件:

apiVersion: v1
kind: Pod
metadata:
  name: shell-demo
spec:
  volumes:
  - name: shared-data
    emptyDir: {}
  containers:
  - name: nginx
    image: nginx
    volumeMounts:
    - name: shared-data
      mountPath: /usr/share/nginx/html
  hostNetwork: true
  dnsPolicy: Default

创建 Pod:

kubectl apply -f https://k8s.io/examples/application/shell-demo.yaml

检查容器是否运行正常:

kubectl get pod shell-demo

获取正在运行容器的 Shell:

kubectl exec --stdin --tty shell-demo -- /bin/bash

在 shell 中,打印根目录:

# 在容器内运行如下命令
ls /

在 shell 中,实验其他命令。下面是一些示例:

# 你可以在容器中运行这些示例命令
ls /
cat /proc/mounts
cat /proc/1/maps
apt-get update
apt-get install -y tcpdump
tcpdump
apt-get install -y lsof
lsof
apt-get install -y procps
ps aux
ps aux | grep nginx

编写 nginx 的根页面

再看一下 Pod 的配置文件。该 Pod 有个 emptyDir 卷,容器将该卷挂载到了 /usr/share/nginx/html

在 shell 中,在 /usr/share/nginx/html 目录创建一个 index.html 文件:

# 在容器内运行如下命令
echo 'Hello shell demo' > /usr/share/nginx/html/index.html

在 shell 中,向 nginx 服务器发送 GET 请求:

# 在容器内运行如下命令
apt-get update
apt-get install curl
curl http://localhost/

输出结果显示了你在 index.html 中写入的文本。

Hello shell demo

当用完 shell 后,输入 exit 退出。

exit # 快速退出容器内的 Shell

在容器中运行单个命令

在普通的命令窗口(而不是 shell)中,打印环境运行容器中的变量:

kubectl exec shell-demo -- env

实验运行其他命令。下面是一些示例:

kubectl exec shell-demo -- ps aux
kubectl exec shell-demo -- ls /
kubectl exec shell-demo -- cat /proc/1/mounts

当 Pod 包含多个容器时打开 shell

如果 Pod 有多个容器,--container 或者 -c 可以在 kubectl exec 命令中指定容器。 例如,你有个名为 my-pod 的 Pod,该 Pod 有两个容器分别为 main-apphealper-app。 下面的命令将会打开一个 shell 访问 main-app 容器。

kubectl exec -i -t my-pod --container main-app -- /bin/bash

接下来

4.5 - 管理 Kubernetes 对象

用声明式和命令式范型与 Kubernetes API 交互。

4.5.1 - 使用配置文件对 Kubernetes 对象进行声明式管理

你可以通过在一个目录中存储多个对象配置文件、并使用 kubectl apply 来递归地创建和更新对象来创建、更新和删除 Kubernetes 对象。 这种方法会保留对现有对象已作出的修改,而不会将这些更改写回到对象配置文件中。 kubectl diff 也会给你呈现 apply 将作出的变更的预览。

准备开始

安装 kubectl

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

权衡取舍

kubectl 工具能够支持三种对象管理方式:

  • 指令式命令
  • 指令式对象配置
  • 声明式对象配置

关于每种对象管理的优缺点的讨论,可参见 Kubernetes 对象管理

概览

声明式对象管理需要用户对 Kubernetes 对象定义和配置有比较深刻的理解。 如果你还没有这方面的知识储备,请先阅读下面的文档:

以下是本文档中使用的术语的定义:

  • 对象配置文件/配置文件:一个定义 Kubernetes 对象的配置的文件。 本主题展示如何将配置文件传递给 kubectl apply。 配置文件通常存储于类似 Git 这种源码控制系统中。
  • 现时对象配置/现时配置:由 Kubernetes 集群所观测到的对象的现时配置值。 这些配置保存在 Kubernetes 集群存储(通常是 etcd)中。
  • 声明式配置写者/声明式写者:负责更新现时对象的人或者软件组件。 本主题中的声明式写者负责改变对象配置文件并执行 kubectl apply 命令以写入变更。

如何创建对象

使用 kubectl apply 来创建指定目录中配置文件所定义的所有对象,除非对应对象已经存在:

kubectl apply -f <目录>

此操作会在每个对象上设置 kubectl.kubernetes.io/last-applied-configuration: '{...}' 注解。注解值中包含了用来创建对象的配置文件的内容。

下面是一个对象配置文件示例:

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

执行 kubectl diff 可以打印出将被创建的对象:

kubectl diff -f https://k8s.io/examples/application/simple_deployment.yaml

使用 kubectl apply 来创建对象:

kubectl apply -f https://k8s.io/examples/application/simple_deployment.yaml

使用 kubectl get 打印其现时配置:

kubectl get -f https://k8s.io/examples/application/simple_deployment.yaml -o yaml

输出显示注解 kubectl.kubernetes.io/last-applied-configuration 被写入到现时配置中,并且其内容与配置文件相同:

kind: Deployment
metadata:
  annotations:
    # ...
    # 此为 simple_deployment.yaml 的 JSON 表示
    # 在对象创建时由 kubectl apply 命令写入
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"apps/v1","kind":"Deployment",
      "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
      "spec":{"minReadySeconds":5,"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
      "spec":{"containers":[{"image":"nginx:1.14.2","name":"nginx",
      "ports":[{"containerPort":80}]}]}}}}      
  # ...
spec:
  # ...
  minReadySeconds: 5
  selector:
    matchLabels:
      # ...
      app: nginx
  template:
    metadata:
      # ...
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.14.2
        # ...
        name: nginx
        ports:
        - containerPort: 80
        # ...
      # ...
    # ...
  # ...

如何更新对象

你也可以使用 kubectl apply 来更新某个目录中定义的所有对象,即使那些对象已经存在。 这一操作会隐含以下行为:

  1. 在现时配置中设置配置文件中出现的字段;
  2. 在现时配置中清除配置文件中已删除的字段。
kubectl diff -f <目录>
kubectl apply -f <目录>

下面是一个配置文件示例:

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

使用 kubectl apply 来创建对象:

kubectl apply -f https://k8s.io/examples/application/simple_deployment.yaml

使用 kubectl get 打印现时配置:

kubectl get -f https://k8s.io/examples/application/simple_deployment.yaml -o yaml

输出显示,注解 kubectl.kubernetes.io/last-applied-configuration 被写入到现时配置中,并且其取值与配置文件内容相同。

kind: Deployment
metadata:
  annotations:
    # ...
    # 此为 simple_deployment.yaml 的 JSON 表示
    # 在对象创建时由 kubectl apply 命令写入
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"apps/v1","kind":"Deployment",
      "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
      "spec":{"minReadySeconds":5,"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
      "spec":{"containers":[{"image":"nginx:1.14.2","name":"nginx",
      "ports":[{"containerPort":80}]}]}}}}      
  # ...
spec:
  # ...
  minReadySeconds: 5
  selector:
    matchLabels:
      # ...
      app: nginx
  template:
    metadata:
      # ...
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.14.2
        # ...
        name: nginx
        ports:
        - containerPort: 80
        # ...
      # ...
    # ...
  # ...

通过 kubectl scale 命令直接更新现时配置中的 replicas 字段。 这一命令没有使用 kubectl apply

kubectl scale deployment/nginx-deployment --replicas=2

使用 kubectl get 来打印现时配置:

kubectl get deployment nginx-deployment -o yaml

输出显示,replicas 字段已经被设置为 2,而 last-applied-configuration 注解中并不包含 replicas 字段。

apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    # ...
    # 注意注解中并不包含 replicas
    # 这是因为更新并不是通过 kubectl apply 来执行的
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"apps/v1","kind":"Deployment",
      "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
      "spec":{"minReadySeconds":5,"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
      "spec":{"containers":[{"image":"nginx:1.14.2","name":"nginx",
      "ports":[{"containerPort":80}]}]}}}}      
  # ...
spec:
  replicas: 2 # 由 scale 命令填写
  # ...
  minReadySeconds: 5
  selector:
    matchLabels:
      # ...
      app: nginx
  template:
    metadata:
      # ...
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.14.2
        # ...
        name: nginx
        ports:
        - containerPort: 80
      # ...

现在更新 simple_deployment.yaml 配置文件,将镜像文件从 nginx:1.14.2 更改为 nginx:1.16.1,同时删除minReadySeconds 字段:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.16.1 # 更新该镜像
        ports:
        - containerPort: 80

应用对配置文件所作更改:

kubectl diff -f https://k8s.io/examples/application/update_deployment.yaml
kubectl apply -f https://k8s.io/examples/application/update_deployment.yaml

使用 kubectl get 打印现时配置:

kubectl get -f https://k8s.io/examples/application/update_deployment.yaml -o yaml

输出显示现时配置中发生了以下更改:

  • 字段 replicas 保留了 kubectl scale 命令所设置的值:2; 之所以该字段被保留是因为配置文件中并没有设置 replicas
  • 字段 image 的内容已经从 nginx:1.14.2 更改为 nginx:1.16.1
  • 注解 last-applied-configuration 内容被更改为新的镜像名称。
  • 字段 minReadySeconds 被移除。
  • 注解 last-applied-configuration 中不再包含 minReadySeconds 字段。
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    # ...
    # 注解中包含更新后的镜像 nginx 1.16.1
    # 但是其中并不包含更改后的 replicas 值 2
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"apps/v1","kind":"Deployment",
      "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
      "spec":{"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
      "spec":{"containers":[{"image":"nginx:1.16.1","name":"nginx",
      "ports":[{"containerPort":80}]}]}}}}      
    # ...
spec:
  replicas: 2 # 由 `kubectl scale` 设置,被 `kubectl apply` 命令忽略
  # minReadySeconds 被 `kubectl apply` 清除
  # ...
  selector:
    matchLabels:
      # ...
      app: nginx
  template:
    metadata:
      # ...
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.16.1 # 由 `kubectl apply` 设置
        # ...
        name: nginx
        ports:
        - containerPort: 80
        # ...
      # ...
    # ...
  # ...

如何删除对象

有两种方法来删除 kubectl apply 管理的对象。

建议操作:kubectl delete -f <文件名>

使用指令式命令来手动删除对象是建议的方法,因为这种方法更为明确地给出了要删除的内容是什么, 且不容易造成用户不小心删除了其他对象的情况。

kubectl delete -f <文件名>

替代方式:kubectl apply -f <目录> --prune

作为 kubectl delete 操作的替代方式,你可以在本地文件系统的目录中的清单文件被删除之后, 使用 kubectl apply 来辩识要删除的对象。

在 Kubernetes 1.30 中,kubectl apply 可使用两种剪裁模式:

  • 基于 Allowlist 的剪裁:这种模式自 kubectl v1.5 版本开始就存在, 但由于其设计存在易用性、正确性和性能问题,因此仍处于 Alpha 阶段。 基于 ApplySet 的模式设计用于取代这种模式。
  • 基于 ApplySet 的剪裁:apply set 是一个服务器端对象(默认是一个 Secret), kubectl 可以使用它来在 apply 操作中准确高效地跟踪集合成员。 这种模式在 kubectl v1.27 中以 Alpha 引入,作为基于 Allowlist 剪裁的替代方案。

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

要使用基于 Allowlist 的剪裁,可以添加以下标志到你的 kubectl apply 调用:

  • --prune:删除之前应用的、不在当前调用所传递的集合中的对象。
  • --prune-allowlist:一个需要考虑进行剪裁的组-版本-类别(group-version-kind, GVK)列表。 这个标志是可选的,但强烈建议使用,因为它的默认值是同时作用于命名空间和集群的部分类型列表, 这可能会产生令人意外的结果。
  • --selector/-l:使用标签选择算符以约束要剪裁的对象的集合。此标志是可选的,但强烈建议使用。
  • --all:用于替代 --selector/-l 以显式选择之前应用的类型为 Allowlist 的所有对象。

基于 Allowlist 的剪裁会查询 API 服务器以获取与给定标签(如果有)匹配的所有允许列出的 GVK 对象, 并尝试将返回的活动对象配置与对象清单文件进行匹配。如果一个对象与查询匹配,并且它在目录中没有对应的清单, 但它有一个 kubectl.kubernetes.io/last-applied-configuration 注解,则它将被删除。

kubectl apply -f <目录> --prune -l <标签> --prune-allowlist=<gvk 列表>

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

要使用基于 ApplySet 的剪裁,请设置 KUBECTL_APPLYSET=true 环境变量, 并添加以下标志到你的 kubectl apply 调用中:

  • --prune:删除之前应用的、不在当前调用所传递的集合中的对象。
  • --applyset:是 kubectl 可以使用的对象的名称,用于在 apply 操作中准确高效地跟踪集合成员。
KUBECTL_APPLYSET=true kubectl apply -f <目录> --prune --applyset=<名称>

默认情况下,所使用的 ApplySet 父对象的类别是 Secret。 不过也可以按格式 --applyset=configmaps/<name> 使用 ConfigMap。 使用 Secret 或 ConfigMap 时,如果对应对象尚不存在,kubectl 将创建这些对象。

还可以使用自定义资源作为 ApplySet 父对象。 要启用此功能,请为定义目标资源的 CRD 打上标签:applyset.kubernetes.io/is-parent-type: true。 然后,创建你想要用作 ApplySet 父级的对象(kubectl 不会自动为自定义资源执行此操作)。 最后,按以下方式在 applyset 标志中引用该对象: --applyset=<resource>.<group>/<name> (例如 widgets.custom.example.com/widget-name)。

使用基于 ApplySet 的剪裁时,kubectl 会在将集合中的对象发送到服务器之前将标签 applyset.kubernetes.io/part-of=<parentID> 添加到集合中的每个对象上。 出于性能原因,它还会将该集合包含的资源类型和命名空间列表收集到当前父对象上的注解中。 最后,在 apply 操作结束时,它会在 API 服务器上查找由 applyset.kubernetes.io/part-of=<parentID> 标签定义的、属于此集合所对应命名空间(或适用的集群作用域)中对应类型的对象。

注意事项和限制:

  • 每个对象最多可以是一个集合的成员。
  • 当使用任何名命名空间的父级(包括默认的 Secret)时, --namespace 标志是必需的。这意味着跨越多个命名空间的 ApplySet 必须使用集群作用域的自定义资源作为父对象。
  • 要安全地在多个目录中使用基于 ApplySet 的剪裁,请为每个目录使用唯一的 ApplySet 名称。

如何查看对象

你可以使用 kubectl get 并指定 -o yaml 选项来查看现时对象的配置:

kubectl get -f <文件名 | URL> -o yaml

apply 操作是如何计算配置差异并合并变更的?

kubectl apply 更新对象的现时配置,它是通过向 API 服务器发送一个 patch 请求来执行更新动作的。所提交的补丁中定义了对现时对象配置中特定字段的更新。 kubectl apply 命令会使用当前的配置文件、现时配置以及现时配置中保存的 last-applied-configuration 注解内容来计算补丁更新内容。

合并补丁计算

kubectl apply 命令将配置文件的内容写入到 kubectl.kubernetes.io/last-applied-configuration 注解中。 这些内容用来识别配置文件中已经移除的、因而也需要从现时配置中删除的字段。 用来计算要删除或设置哪些字段的步骤如下:

  1. 计算要删除的字段,即在 last-applied-configuration 中存在但在配置文件中不再存在的字段。
  2. 计算要添加或设置的字段,即在配置文件中存在但其取值与现时配置不同的字段。

下面是一个例子。假定此文件是某 Deployment 对象的配置文件:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.16.1 # 更新该镜像
        ports:
        - containerPort: 80

同时假定同一 Deployment 对象的现时配置如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    # ...
    # 注意注解中并不包含 replicas
    # 这是因为更新并不是通过 kubectl apply 来执行的
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"apps/v1","kind":"Deployment",
      "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
      "spec":{"minReadySeconds":5,"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
      "spec":{"containers":[{"image":"nginx:1.14.2","name":"nginx",
      "ports":[{"containerPort":80}]}]}}}}      
  # ...
spec:
  replicas: 2 # 按规模填写
  # ...
  minReadySeconds: 5
  selector:
    matchLabels:
      # ...
      app: nginx
  template:
    metadata:
      # ...
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.14.2
        # ...
        name: nginx
        ports:
        - containerPort: 80
      # ...

下面是 kubectl apply 将执行的合并计算:

  1. 通过读取 last-applied-configuration 并将其与配置文件中的值相比较, 计算要删除的字段。 对于本地对象配置文件中显式设置为空的字段,清除其在现时配置中的设置, 无论这些字段是否出现在 last-applied-configuration 中。 在此例中,minReadySeconds 出现在 last-applied-configuration 注解中, 但并不存在于配置文件中。 动作: 从现时配置中删除 minReadySeconds 字段。
  2. 通过读取配置文件中的值并将其与现时配置相比较,计算要设置的字段。 在这个例子中,配置文件中的 image 值与现时配置中的 image 不匹配。 动作:设置现时配置中的 image 值。
  3. 设置 last-applied-configuration 注解的内容,使之与配置文件匹配。
  4. 将第 1、2、3 步骤得出的结果合并,构成向 API 服务器发送的补丁请求内容。

下面是此合并操作之后形成的现时配置:

apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    # ...
    # 注解中包含更新后的镜像 nginx 1.16.1,
    # 但是其中并不包含更改后的 replicas 值 2
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"apps/v1","kind":"Deployment",
      "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
      "spec":{"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
      "spec":{"containers":[{"image":"nginx:1.16.1","name":"nginx",
      "ports":[{"containerPort":80}]}]}}}}      
    # ...
spec:
  selector:
    matchLabels:
      # ...
      app: nginx
  replicas: 2 # 由 `kubectl scale` 设置,被 `kubectl apply` 命令忽略
  # minReadySeconds  此字段被 `kubectl apply` 清除
  # ...
  template:
    metadata:
      # ...
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.16.1 # 由 `kubectl apply` 设置
        # ...
        name: nginx
        ports:
        - containerPort: 80
        # ...
      # ...
    # ...
  # ...

不同类型字段的合并方式

配置文件中的特定字段与现时配置合并时,合并方式取决于字段类型。 字段类型有几种:

  • 基本类型:字段类型为 stringintegerboolean 之一。 例如:imagereplicas 字段都是基本类型字段。

    动作: 替换。

  • map:也称作 object。类型为 map 或包含子域的复杂结构。例如,labelsannotationsspecmetadata 都是 map。

    动作: 合并元素或子字段。

  • list:包含元素列表的字段,其中每个元素可以是基本类型或 map。 例如,containersportsargs 都是 list。

    动作: 不一定。

kubectl apply 更新某个 map 或 list 字段时,它通常不会替换整个字段, 而是会更新其中的各个子元素。例如,当合并 Deployment 的 spec 时,kubectl 并不会将其整个替换掉。相反,实际操作会是对 replicas 这类 spec 的子字段来执行比较和更新。

合并对基本类型字段的更新

基本类型字段会被替换或清除。

字段在对象配置文件中字段在现时对象配置中字段在 last-applied-configuration动作
-将配置文件中值设置到现时配置上。
-将配置文件中值设置到现时配置上。
-从现时配置中移除。
-什么也不做。保持现时值。

合并对 map 字段的变更

用来表示映射的字段在合并时会逐个子字段或元素地比较:

键存在于对象配置文件中键存在于现时对象配置中键存在于 last-applied-configuration动作
-比较子域取值。
-将现时配置设置为本地配置值。
-从现时配置中删除键。
-什么也不做,保留现时值。

合并 list 类型字段的变更

对 list 类型字段的变更合并会使用以下三种策略之一:

  • 如果 list 所有元素都是基本类型则替换整个 list。
  • 如果 list 中元素是复合结构则逐个元素执行合并操作。
  • 合并基本类型元素构成的 list。

策略的选择是基于各个字段做出的。

如果 list 中元素都是基本类型则替换整个 list

将整个 list 视为一个基本类型字段。或者整个替换或者整个删除。 此操作会保持 list 中元素顺序不变

示例: 使用 kubectl apply 来更新 Pod 中 Container 的 args 字段。 此操作会将现时配置中的 args 值设为配置文件中的值。 所有之前添加到现时配置中的 args 元素都会丢失。 配置文件中的 args 元素的顺序在被添加到现时配置中时保持不变。

# last-applied-configuration 值
    args: ["a", "b"]

# 配置文件值
    args: ["a", "c"]

# 现时配置
    args: ["a", "b", "d"]

# 合并结果
    args: ["a", "c"]

解释: 合并操作将配置文件中的值当做新的 list 值。

如果 list 中元素为复合类型则逐个执行合并

此操作将 list 视为 map,并将每个元素中的特定字段当做其主键。 逐个元素地执行添加、删除或更新操作。结果顺序无法得到保证。

此合并策略会使用每个字段上的一个名为 patchMergeKey 的特殊标签。 Kubernetes 源代码中为每个字段定义了 patchMergeKeytypes.go。 当合并由 map 组成的 list 时,给定元素中被设置为 patchMergeKey 的字段会被当做该元素的 map 键值来使用。

例如: 使用 kubectl apply 来更新 Pod 规约中的 containers 字段。 此操作会将 containers 列表视作一个映射来执行合并,每个元素的主键为 name

# last-applied-configuration 值
    containers:
    - name: nginx
      image: nginx:1.16
    - name: nginx-helper-a # 键 nginx-helper-a 会被删除
      image: helper:1.3
    - name: nginx-helper-b # 键 nginx-helper-b 会被保留
      image: helper:1.3

# 配置文件值
    containers:
    - name: nginx
      image: nginx:1.16
    - name: nginx-helper-b
      image: helper:1.3
    - name: nginx-helper-c # 键 nginx-helper-c 会被添加
      image: helper:1.3

# 现时配置
    containers:
    - name: nginx
      image: nginx:1.16
    - name: nginx-helper-a
      image: helper:1.3
    - name: nginx-helper-b
      image: helper:1.3
      args: ["run"]        # 字段会被保留
    - name: nginx-helper-d # 键 nginx-helper-d 会被保留
      image: helper:1.3

# 合并结果
    containers:
    - name: nginx
      image: nginx:1.16
      # 元素 nginx-helper-a 被删除
    - name: nginx-helper-b
      image: helper:1.3
      args: ["run"]        # 字段被保留
    - name: nginx-helper-c # 新增元素
      image: helper:1.3
    - name: nginx-helper-d # 此元素被忽略(保留)
      image: helper:1.3

解释:

  • 名为 "nginx-helper-a" 的容器被删除,因为配置文件中不存在同名的容器。
  • 名为 "nginx-helper-b" 的容器的现时配置中的 args 被保留。 kubectl apply 能够辩识出现时配置中的容器 "nginx-helper-b" 与配置文件 中的容器 "nginx-helper-b" 相同,即使它们的字段值有些不同(配置文件中未给定 args 值)。这是因为 patchMergeKey 字段(name)的值在两个版本中都一样。
  • 名为 "nginx-helper-c" 的容器是新增的,因为在配置文件中的这个容器尚不存在于现时配置中。
  • 名为 "nginx-helper-d" 的容器被保留下来,因为在 last-applied-configuration 中没有与之同名的元素。

合并基本类型元素 list

在 Kubernetes 1.5 中,尚不支持对由基本类型元素构成的 list 进行合并。

默认字段值

API 服务器会在对象创建时其中某些字段未设置的情况下在现时配置中为其设置默认值。

下面是一个 Deployment 的配置文件。文件未设置 strategy

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

使用 kubectl apply 创建对象:

kubectl apply -f https://k8s.io/examples/application/simple_deployment.yaml

使用 kubectl get 打印现时配置:

kubectl get -f https://k8s.io/examples/application/simple_deployment.yaml -o yaml

输出显示 API 在现时配置中为某些字段设置了默认值。 这些字段在配置文件中并未设置。

apiVersion: apps/v1
kind: Deployment
# ...
spec:
  selector:
    matchLabels:
      app: nginx
  minReadySeconds: 5
  replicas: 1           # API 服务器所设默认值
  strategy:
    rollingUpdate:      # API 服务器基于 strategy.type 所设默认值
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate # API 服务器所设默认值
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.14.2
        imagePullPolicy: IfNotPresent    # API 服务器所设默认值
        name: nginx
        ports:
        - containerPort: 80
          protocol: TCP       # API 服务器所设默认值
        resources: {}         # API 服务器所设默认值
        terminationMessagePath: /dev/termination-log    # API 服务器所设默认值
      dnsPolicy: ClusterFirst       # API 服务器所设默认值
      restartPolicy: Always         # API 服务器所设默认值
      securityContext: {}           # API 服务器所设默认值
      terminationGracePeriodSeconds: 30        # API 服务器所设默认值
# ...

在补丁请求中,已经设置了默认值的字段不会被重新设回其默认值, 除非在补丁请求中显式地要求清除。对于默认值取决于其他字段的某些字段而言, 这可能会引发一些意想不到的行为。当所依赖的其他字段后来发生改变时, 基于它们所设置的默认值只能在显式执行清除操作时才会被更新。

为此,建议在配置文件中为服务器设置默认值的字段显式提供定义, 即使所给的定义与服务器端默认值设定相同。 这样可以使得辩识无法被服务器重新基于默认值来设置的冲突字段变得容易。

示例:

# last-applied-configuration
spec:
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

# 配置文件
spec:
  strategy:
    type: Recreate   # 更新的值
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

# 现时配置
spec:
  strategy:
    type: RollingUpdate    # 默认设置的值
    rollingUpdate:         # 基于 type 设置的默认值
      maxSurge : 1
      maxUnavailable: 1
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

# 合并后的结果 - 出错!
spec:
  strategy:
    type: Recreate     # 更新的值:与 rollingUpdate 不兼容
    rollingUpdate:     # 默认设置的值:与 "type: Recreate" 冲突
      maxSurge : 1
      maxUnavailable: 1
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

解释:

  1. 用户创建 Deployment,未设置 strategy.type
  2. 服务器为 strategy.type 设置默认值 RollingUpdate,并为 strategy.rollingUpdate 设置默认值。
  3. 用户改变 strategy.typeRecreate。字段 strategy.rollingUpdate 仍会取其默认设置值,尽管服务器期望该字段被清除。 如果 strategy.rollingUpdate 值最初于配置文件中定义, 则它们需要被清除这一点就更明确一些。
  4. apply 操作失败,因为 strategy.rollingUpdate 未被清除。 strategy.rollingupdatestrategy.typeRecreate 不可被设定。

建议:以下字段应该在对象配置文件中显式定义:

  • 如 Deployment、StatefulSet、Job、DaemonSet、ReplicaSet 和 ReplicationController 这类负载的选择算符和 PodTemplate 标签
  • Deployment 的上线策略

如何清除服务器端按默认值设置的字段或者被其他写者设置的字段

没有出现在配置文件中的字段可以通过将其值设置为 null 并应用配置文件来清除。 对于由服务器按默认值设置的字段,清除操作会触发重新为字段设置新的默认值。

如何将字段的属主在配置文件和直接指令式写者之间切换

更改某个对象字段时,应该采用下面的方法:

  • 使用 kubectl apply.
  • 直接写入到现时配置,但不更改配置文件本身,例如使用 kubectl scale

将属主从直接指令式写者更改为配置文件

将字段添加到配置文件。针对该字段,不再直接执行对现时配置的修改。 修改均通过 kubectl apply 来执行。

将属主从配置文件改为直接指令式写者

在 Kubernetes 1.5 中,将字段的属主从配置文件切换到某指令式写者需要手动执行以下步骤:

  • 从配置文件中删除该字段;
  • 将字段从现时对象的 kubectl.kubernetes.io/last-applied-configuration 注解中删除。

更改管理方法

Kubernetes 对象在同一时刻应该只用一种方法来管理。 从一种方法切换到另一种方法是可能的,但这一切换是一个手动过程。

从指令式命令管理切换到声明式对象配置

从指令式命令管理切换到声明式对象配置管理的切换包含以下几个手动步骤:

  1. 将现时对象导出到本地配置文件:

    kubectl get <kind>/<name> -o yaml > <kind>_<name>.yaml
    
  2. 手动移除配置文件中的 status 字段。

  1. 设置对象上的 kubectl.kubernetes.io/last-applied-configuration 注解:

    kubectl replace --save-config -f <kind>_<name>.yaml
    
  2. 更改过程,使用 kubectl apply 专门管理对象。

从指令式对象配置切换到声明式对象配置

  1. 在对象上设置 kubectl.kubernetes.io/last-applied-configuration 注解:

    kubectl replace --save-config -f <kind>_<name>.yaml
    
  2. 自此排他性地使用 kubectl apply 来管理对象。

定义控制器选择算符和 PodTemplate 标签

建议的方法是定义一个不可变更的 PodTemplate 标签, 仅用于控制器选择算符且不包含其他语义性的含义。

示例:

selector:
  matchLabels:
      controller-selector: "apps/v1/deployment/nginx"
template:
  metadata:
    labels:
      controller-selector: "apps/v1/deployment/nginx"

接下来

4.5.2 - 使用 Kustomize 对 Kubernetes 对象进行声明式管理

Kustomize 是一个独立的工具,用来通过 kustomization 文件 定制 Kubernetes 对象。

从 1.14 版本开始,kubectl 也开始支持使用 kustomization 文件来管理 Kubernetes 对象。 要查看包含 kustomization 文件的目录中的资源,执行下面的命令:

kubectl kustomize <kustomization_directory>

要应用这些资源,使用 --kustomize-k 参数来执行 kubectl apply

kubectl apply -k <kustomization_directory>

准备开始

安装 kubectl

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

Kustomize 概述

Kustomize 是一个用来定制 Kubernetes 配置的工具。它提供以下功能特性来管理应用配置文件:

  • 从其他来源生成资源
  • 为资源设置贯穿性(Cross-Cutting)字段
  • 组织和定制资源集合

生成资源

ConfigMap 和 Secret 包含其他 Kubernetes 对象(如 Pod)所需要的配置或敏感数据。 ConfigMap 或 Secret 中数据的来源往往是集群外部,例如某个 .properties 文件或者 SSH 密钥文件。 Kustomize 提供 secretGeneratorconfigMapGenerator,可以基于文件或字面值来生成 Secret 和 ConfigMap。

configMapGenerator

要基于文件来生成 ConfigMap,可以在 configMapGeneratorfiles 列表中添加表项。 下面是一个根据 .properties 文件中的数据条目来生成 ConfigMap 的示例:

# 生成一个  application.properties 文件
cat <<EOF >application.properties
FOO=Bar
EOF

cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: example-configmap-1
  files:
  - application.properties
EOF

所生成的 ConfigMap 可以使用下面的命令来检查:

kubectl kustomize ./

所生成的 ConfigMap 为:

apiVersion: v1
data:
  application.properties: |
    FOO=Bar    
kind: ConfigMap
metadata:
  name: example-configmap-1-8mbdf7882g

要从 env 文件生成 ConfigMap,请在 configMapGenerator 中的 envs 列表中添加一个条目。 下面是一个用来自 .env 文件的数据生成 ConfigMap 的例子:

# 创建一个 .env 文件
cat <<EOF >.env
FOO=Bar
EOF

cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: example-configmap-1
  envs:
  - .env
EOF

可以使用以下命令检查生成的 ConfigMap:

kubectl kustomize ./

生成的 ConfigMap 为:

apiVersion: v1
data:
  FOO: Bar
kind: ConfigMap
metadata:
  name: example-configmap-1-42cfbf598f

ConfigMap 也可基于字面的键值偶对来生成。要基于键值偶对来生成 ConfigMap, 在 configMapGeneratorliterals 列表中添加表项。下面是一个例子, 展示如何使用键值偶对中的数据条目来生成 ConfigMap 对象:

cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: example-configmap-2
  literals:
  - FOO=Bar
EOF

可以用下面的命令检查所生成的 ConfigMap:

kubectl kustomize ./

所生成的 ConfigMap 为:

apiVersion: v1
data:
  FOO: Bar
kind: ConfigMap
metadata:
  name: example-configmap-2-g2hdhfc6tk

要在 Deployment 中使用生成的 ConfigMap,使用 configMapGenerator 的名称对其进行引用。 Kustomize 将自动使用生成的名称替换该名称。

这是使用生成的 ConfigMap 的 deployment 示例:

# 创建一个 application.properties 文件
cat <<EOF >application.properties
FOO=Bar
EOF

cat <<EOF >deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  labels:
    app: my-app
spec:
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: app
        image: my-app
        volumeMounts:
        - name: config
          mountPath: /config
      volumes:
      - name: config
        configMap:
          name: example-configmap-1
EOF

cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
configMapGenerator:
- name: example-configmap-1
  files:
  - application.properties
EOF

生成 ConfigMap 和 Deployment:

kubectl kustomize ./

生成的 Deployment 将通过名称引用生成的 ConfigMap:

apiVersion: v1
data:
  application.properties: |
    FOO=Bar    
kind: ConfigMap
metadata:
  name: example-configmap-1-g4hk9g2ff8
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: my-app
  name: my-app
spec:
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - image: my-app
        name: app
        volumeMounts:
        - mountPath: /config
          name: config
      volumes:
      - configMap:
          name: example-configmap-1-g4hk9g2ff8
        name: config

secretGenerator

你可以基于文件或者键值偶对来生成 Secret。要使用文件内容来生成 Secret, 在 secretGenerator 下面的 files 列表中添加表项。 下面是一个根据文件中数据来生成 Secret 对象的示例:

# 创建一个 password.txt 文件
cat <<EOF >./password.txt
username=admin
password=secret
EOF

cat <<EOF >./kustomization.yaml
secretGenerator:
- name: example-secret-1
  files:
  - password.txt
EOF

所生成的 Secret 如下:

apiVersion: v1
data:
  password.txt: dXNlcm5hbWU9YWRtaW4KcGFzc3dvcmQ9c2VjcmV0Cg==
kind: Secret
metadata:
  name: example-secret-1-t2kt65hgtb
type: Opaque

要基于键值偶对字面值生成 Secret,先要在 secretGeneratorliterals 列表中添加表项。下面是基于键值偶对中数据条目来生成 Secret 的示例:

cat <<EOF >./kustomization.yaml
secretGenerator:
- name: example-secret-2
  literals:
  - username=admin
  - password=secret
EOF

所生成的 Secret 如下:

apiVersion: v1
data:
  password: c2VjcmV0
  username: YWRtaW4=
kind: Secret
metadata:
  name: example-secret-2-t52t6g96d8
type: Opaque

与 ConfigMap 一样,生成的 Secret 可以通过引用 secretGenerator 的名称在 Deployment 中使用:

# 创建一个 password.txt 文件
cat <<EOF >./password.txt
username=admin
password=secret
EOF

cat <<EOF >deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  labels:
    app: my-app
spec:
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: app
        image: my-app
        volumeMounts:
        - name: password
          mountPath: /secrets
      volumes:
      - name: password
        secret:
          secretName: example-secret-1
EOF

cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
secretGenerator:
- name: example-secret-1
  files:
  - password.txt
EOF

generatorOptions

所生成的 ConfigMap 和 Secret 都会包含内容哈希值后缀。 这是为了确保内容发生变化时,所生成的是新的 ConfigMap 或 Secret。 要禁止自动添加后缀的行为,用户可以使用 generatorOptions。 除此以外,为生成的 ConfigMap 和 Secret 指定贯穿性选项也是可以的。

cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: example-configmap-3
  literals:
  - FOO=Bar
generatorOptions:
  disableNameSuffixHash: true
  labels:
    type: generated
  annotations:
    note: generated
EOF

运行 kubectl kustomize ./ 来查看所生成的 ConfigMap:

apiVersion: v1
data:
  FOO: Bar
kind: ConfigMap
metadata:
  annotations:
    note: generated
  labels:
    type: generated
  name: example-configmap-3

设置贯穿性字段

在项目中为所有 Kubernetes 对象设置贯穿性字段是一种常见操作。 贯穿性字段的一些使用场景如下:

  • 为所有资源设置相同的名字空间
  • 为所有对象添加相同的前缀或后缀
  • 为对象添加相同的标签集合
  • 为对象添加相同的注解集合

下面是一个例子:

# 创建一个 deployment.yaml
cat <<EOF >./deployment.yaml
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
EOF

cat <<EOF >./kustomization.yaml
namespace: my-namespace
namePrefix: dev-
nameSuffix: "-001"
commonLabels:
  app: bingo
commonAnnotations:
  oncallPager: 800-555-1212
resources:
- deployment.yaml
EOF

执行 kubectl kustomize ./ 查看这些字段都被设置到 Deployment 资源上:

apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    oncallPager: 800-555-1212
  labels:
    app: bingo
  name: dev-nginx-deployment-001
  namespace: my-namespace
spec:
  selector:
    matchLabels:
      app: bingo
  template:
    metadata:
      annotations:
        oncallPager: 800-555-1212
      labels:
        app: bingo
    spec:
      containers:
      - image: nginx
        name: nginx

组织和定制资源

一种常见的做法是在项目中构造资源集合并将其放到同一个文件或目录中管理。 Kustomize 提供基于不同文件来组织资源并向其应用补丁或者其他定制的能力。

组织

Kustomize 支持组合不同的资源。kustomization.yaml 文件的 resources 字段定义配置中要包含的资源列表。 你可以将 resources 列表中的路径设置为资源配置文件的路径。 下面是由 Deployment 和 Service 构成的 NGINX 应用的示例:

# 创建 deployment.yaml 文件
cat <<EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports:
        - containerPort: 80
EOF

# 创建 service.yaml 文件
cat <<EOF > service.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  ports:
  - port: 80
    protocol: TCP
  selector:
    run: my-nginx
EOF

# 创建 kustomization.yaml 来组织以上两个资源
cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
- service.yaml
EOF

kubectl kustomize ./ 所得到的资源中既包含 Deployment 也包含 Service 对象。

定制

补丁文件(Patches)可以用来对资源执行不同的定制。 Kustomize 通过 patchesStrategicMergepatchesJson6902 支持不同的打补丁机制。 patchesStrategicMerge 的内容是一个文件路径的列表,其中每个文件都应可解析为 策略性合并补丁(Strategic Merge Patch)。 补丁文件中的名称必须与已经加载的资源的名称匹配。 建议构造规模较小的、仅做一件事情的补丁。 例如,构造一个补丁来增加 Deployment 的副本个数;构造另外一个补丁来设置内存限制。

# 创建 deployment.yaml 文件
cat <<EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports:
        - containerPort: 80
EOF

# 生成一个补丁 increase_replicas.yaml
cat <<EOF > increase_replicas.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  replicas: 3
EOF

# 生成另一个补丁 set_memory.yaml
cat <<EOF > set_memory.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  template:
    spec:
      containers:
      - name: my-nginx
        resources:
          limits:
            memory: 512Mi
EOF

cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
patchesStrategicMerge:
- increase_replicas.yaml
- set_memory.yaml
EOF

执行 kubectl kustomize ./ 来查看 Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      run: my-nginx
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - image: nginx
        name: my-nginx
        ports:
        - containerPort: 80
        resources:
          limits:
            memory: 512Mi

并非所有资源或者字段都支持策略性合并补丁。为了支持对任何资源的任何字段进行修改, Kustomize 提供通过 patchesJson6902 来应用 JSON 补丁的能力。 为了给 JSON 补丁找到正确的资源,需要在 kustomization.yaml 文件中指定资源的组(group)、 版本(version)、类别(kind)和名称(name)。 例如,为某 Deployment 对象增加副本个数的操作也可以通过 patchesJson6902 来完成:

# 创建一个 deployment.yaml 文件
cat <<EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports:
        - containerPort: 80
EOF

# 创建一个 JSON 补丁文件
cat <<EOF > patch.yaml
- op: replace
  path: /spec/replicas
  value: 3
EOF

# 创建一个 kustomization.yaml
cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml

patchesJson6902:
- target:
    group: apps
    version: v1
    kind: Deployment
    name: my-nginx
  path: patch.yaml
EOF

执行 kubectl kustomize ./ 以查看 replicas 字段被更新:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      run: my-nginx
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - image: nginx
        name: my-nginx
        ports:
        - containerPort: 80

除了补丁之外,Kustomize 还提供定制容器镜像或者将其他对象的字段值注入到容器中的能力,并且不需要创建补丁。 例如,你可以通过在 kustomization.yaml 文件的 images 字段设置新的镜像来更改容器中使用的镜像。

cat <<EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports:
        - containerPort: 80
EOF

cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
images:
- name: nginx
  newName: my.image.registry/nginx
  newTag: 1.4.0
EOF

执行 kubectl kustomize ./ 以查看所使用的镜像已被更新:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      run: my-nginx
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - image: my.image.registry/nginx:1.4.0
        name: my-nginx
        ports:
        - containerPort: 80

有些时候,Pod 中运行的应用可能需要使用来自其他对象的配置值。 例如,某 Deployment 对象的 Pod 需要从环境变量或命令行参数中读取读取 Service 的名称。 由于在 kustomization.yaml 文件中添加 namePrefixnameSuffix 时 Service 名称可能发生变化,建议不要在命令参数中硬编码 Service 名称。 对于这种使用场景,Kustomize 可以通过 vars 将 Service 名称注入到容器中。

# 创建一个 deployment.yaml 文件(引用此处的文档分隔符)
cat <<'EOF' > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        command: ["start", "--host", "$(MY_SERVICE_NAME)"]
EOF

# 创建一个 service.yaml 文件
cat <<EOF > service.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  ports:
  - port: 80
    protocol: TCP
  selector:
    run: my-nginx
EOF

cat <<EOF >./kustomization.yaml
namePrefix: dev-
nameSuffix: "-001"

resources:
- deployment.yaml
- service.yaml

vars:
- name: MY_SERVICE_NAME
  objref:
    kind: Service
    name: my-nginx
    apiVersion: v1
EOF

执行 kubectl kustomize ./ 以查看注入到容器中的 Service 名称是 dev-my-nginx-001

apiVersion: apps/v1
kind: Deployment
metadata:
  name: dev-my-nginx-001
spec:
  replicas: 2
  selector:
    matchLabels:
      run: my-nginx
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - command:
        - start
        - --host
        - dev-my-nginx-001
        image: nginx
        name: my-nginx

基准(Bases)与覆盖(Overlays)

Kustomize 中有 基准(bases)覆盖(overlays) 的概念区分。 基准 是包含 kustomization.yaml 文件的一个目录,其中包含一组资源及其相关的定制。 基准可以是本地目录或者来自远程仓库的目录,只要其中存在 kustomization.yaml 文件即可。 覆盖 也是一个目录,其中包含将其他 kustomization 目录当做 bases 来引用的 kustomization.yaml 文件。 基准不了解覆盖的存在,且可被多个覆盖所使用。 覆盖则可以有多个基准,且可针对所有基准中的资源执行组织操作,还可以在其上执行定制。

# 创建一个包含基准的目录
mkdir base
# 创建 base/deployment.yaml
cat <<EOF > base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
EOF

# 创建 base/service.yaml 文件
cat <<EOF > base/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  ports:
  - port: 80
    protocol: TCP
  selector:
    run: my-nginx
EOF

# 创建 base/kustomization.yaml
cat <<EOF > base/kustomization.yaml
resources:
- deployment.yaml
- service.yaml
EOF

此基准可在多个覆盖中使用。你可以在不同的覆盖中添加不同的 namePrefix 或其他贯穿性字段。 下面是两个使用同一基准的覆盖:

mkdir dev
cat <<EOF > dev/kustomization.yaml
resources:
- ../base
namePrefix: dev-
EOF

mkdir prod
cat <<EOF > prod/kustomization.yaml
resources:
- ../base
namePrefix: prod-
EOF

如何使用 Kustomize 来应用、查看和删除对象

kubectl 命令中使用 --kustomize-k 参数来识别被 kustomization.yaml 所管理的资源。 注意 -k 要指向一个 kustomization 目录。例如:

kubectl apply -k <kustomization 目录>/

假定使用下面的 kustomization.yaml

# 创建 deployment.yaml 文件
cat <<EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports:
        - containerPort: 80
EOF

# 创建 kustomization.yaml
cat <<EOF >./kustomization.yaml
namePrefix: dev-
commonLabels:
  app: my-nginx
resources:
- deployment.yaml
EOF

执行下面的命令来应用 Deployment 对象 dev-my-nginx

> kubectl apply -k ./
deployment.apps/dev-my-nginx created

运行下面的命令之一来查看 Deployment 对象 dev-my-nginx

kubectl get -k ./
kubectl describe -k ./

执行下面的命令来比较 Deployment 对象 dev-my-nginx 与清单被应用之后集群将处于的状态:

kubectl diff -k ./

执行下面的命令删除 Deployment 对象 dev-my-nginx

> kubectl delete -k ./
deployment.apps "dev-my-nginx" deleted

Kustomize 功能特性列表

字段类型解释
namespacestring为所有资源添加名字空间
namePrefixstring此字段的值将被添加到所有资源名称前面
nameSuffixstring此字段的值将被添加到所有资源名称后面
commonLabelsmap[string]string要添加到所有资源和选择算符的标签
commonAnnotationsmap[string]string要添加到所有资源的注解
resources[]string列表中的每个条目都必须能够解析为现有的资源配置文件
configMapGenerator[]ConfigMapArgs列表中的每个条目都会生成一个 ConfigMap
secretGenerator[]SecretArgs列表中的每个条目都会生成一个 Secret
generatorOptionsGeneratorOptions更改所有 ConfigMap 和 Secret 生成器的行为
bases[]string列表中每个条目都应能解析为一个包含 kustomization.yaml 文件的目录
patchesStrategicMerge[]string列表中每个条目都能解析为某 Kubernetes 对象的策略性合并补丁
patchesJson6902[]Patch列表中每个条目都能解析为一个 Kubernetes 对象和一个 JSON 补丁
vars[]Var每个条目用来从某资源的字段来析取文字
images[]Image每个条目都用来更改镜像的名称、标记与/或摘要,不必生成补丁
configurations[]string列表中每个条目都应能解析为一个包含 Kustomize 转换器配置 的文件
crds[]string列表中每个条目都应能够解析为 Kubernetes 类别的 OpenAPI 定义文件

接下来

4.5.3 - 使用指令式命令管理 Kubernetes 对象

使用构建在 kubectl 命令行工具中的指令式命令可以直接快速创建、更新和删除 Kubernetes 对象。本文档解释这些命令的组织方式以及如何使用它们来管理活跃对象。

准备开始

安装kubectl

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

权衡取舍

kubectl 工具能够支持三种对象管理方式:

  • 指令式命令
  • 指令式对象配置
  • 声明式对象配置

关于每种对象管理的优缺点的讨论,可参见 Kubernetes 对象管理

如何创建对象

kubectl 工具支持动词驱动的命令,用来创建一些最常见的对象类别。 命令的名称设计使得不熟悉 Kubernetes 对象类型的用户也能做出判断。

  • run:创建一个新的 Pod 来运行一个容器。
  • expose:创建一个新的 Service 对象为若干 Pod 提供流量负载均衡。
  • autoscale:创建一个新的 Autoscaler 对象来自动对某控制器(例如:Deployment) 执行水平扩缩。

kubectl 命令也支持一些对象类型驱动的创建命令。 这些命令可以支持更多的对象类别,并且在其动机上体现得更为明显, 不过要求用户了解它们所要创建的对象的类别。

  • create <对象类别> [<子类别>] <实例名称>

某些对象类别拥有自己的子类别,可以在 create 命令中设置。 例如,Service 对象有 ClusterIP、LoadBalancer 和 NodePort 三种子类别。 下面是一个创建 NodePort 子类别的 Service 的示例:

kubectl create service nodeport <服务名称>

在前述示例中,create service nodeport 命令也称作 create service 命令的子命令。 可以使用 -h 标志找到一个子命令所支持的参数和标志。

kubectl create service nodeport -h

如何更新对象

kubectl 命令也支持一些动词驱动的命令,用来执行一些常见的更新操作。 这些命令的设计是为了让一些不了解 Kubernetes 对象的用户也能执行更新操作, 但不需要了解哪些字段必须设置:

  • scale:对某控制器进行水平扩缩以便通过更新控制器的副本个数来添加或删除 Pod。
  • annotate:为对象添加或删除注解。
  • label:为对象添加或删除标签。

kubectl 命令也支持由对象的某一方面来驱动的更新命令。 设置对象的这一方面可能对不同类别的对象意味着不同的字段:

  • set <字段>:设置对象的某一方面。

kubectl 工具支持以下额外的方式用来直接更新活跃对象,不过这些操作要求 用户对 Kubernetes 对象的模式定义有很好的了解:

  • edit:通过在编辑器中打开活跃对象的配置,直接编辑其原始配置。
  • patch:通过使用补丁字符串(Patch String)直接更改某活跃对象的特定字段。 关于补丁字符串的更详细信息,参见 API 约定 的 patch 节。

如何删除对象

你可以使用 delete 命令从集群中删除一个对象:

  • delete <类别>/<名称>
kubectl delete deployment/nginx

如何查看对象

用来打印对象信息的命令有好几个:

  • get:打印匹配到的对象的基本信息。使用 get -h 可以查看选项列表。
  • describe:打印匹配到的对象的详细信息的汇集版本。
  • logs:打印 Pod 中运行的容器的 stdout 和 stderr 输出。

使用 set 命令在创建对象之前修改对象

有些对象字段在 create 命令中没有对应的标志。 在这些场景中,你可以使用 setcreate 命令的组合来在对象创建之前设置字段值。 这是通过将 create 命令的输出用管道方式传递给 set 命令来实现的,最后执行 create 命令来创建对象。 下面是一个例子:

kubectl create service clusterip my-svc --clusterip="None" -o yaml --dry-run=client | kubectl set selector --local -f - 'environment=qa' -o yaml | kubectl create -f -
  1. 命令 kubectl create service -o yaml --dry-run=client 创建 Service 的配置, 但将其以 YAML 格式在标准输出上打印而不是发送给 API 服务器。
  2. 命令 kubectl set selector --local -f - -o yaml 从标准输入读入配置, 并将更新后的配置以 YAML 格式输出到标准输出。
  3. 命令 kubectl create -f - 使用标准输入上获得的配置创建对象。

在创建之前使用 --edit 更改对象

你可以用 kubectl create --edit 来在对象被创建之前执行任意的变更。 下面是一个例子:

kubectl create service clusterip my-svc --clusterip="None" -o yaml --dry-run=client > /tmp/srv.yaml
kubectl create --edit -f /tmp/srv.yaml
  1. 命令 kubectl create service 创建 Service 的配置并将其保存到 /tmp/srv.yaml 文件。
  2. 命令 kubectl create --edit 在创建 Service 对象打开其配置文件进行编辑。

接下来

4.5.4 - 使用配置文件对 Kubernetes 对象进行命令式管理

可以使用 kubectl 命令行工具以及用 YAML 或 JSON 编写的对象配置文件来创建、更新和删除 Kubernetes 对象。 本文档说明了如何使用配置文件定义和管理对象。

准备开始

安装 kubectl

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

权衡

kubectl 工具支持三种对象管理:

  • 命令式命令
  • 命令式对象配置
  • 声明式对象配置

参见 Kubernetes 对象管理 中关于每种对象管理的优缺点的讨论。

如何创建对象

你可以使用 kubectl create -f 从配置文件创建一个对象。 更多细节参阅 kubernetes API 参考

  • kubectl create -f <filename|url>

如何更新对象

你可以使用 kubectl replace -f 根据配置文件更新活动对象。

  • kubectl replace -f <filename|url>

如何删除对象

你可以使用 kubectl delete -f 删除配置文件中描述的对象。

  • kubectl delete -f <filename|url>

如何查看对象

你可以使用 kubectl get -f 查看有关配置文件中描述的对象的信息。

  • kubectl get -f <filename|url> -o yaml

-o yaml 标志指定打印完整的对象配置。使用 kubectl get -h 查看选项列表。

局限性

当完全定义每个对象的配置并将其记录在其配置文件中时,createreplacedelete 命令会很好的工作。 但是,当更新一个活动对象,并且更新没有合并到其配置文件中时,下一次执行 replace 时,更新将丢失。 如果控制器,例如 HorizontalPodAutoscaler ,直接对活动对象进行更新,则会发生这种情况。 这有一个例子:

  1. 从配置文件创建一个对象。
  2. 另一个源通过更改某些字段来更新对象。
  3. 从配置文件中替换对象。在步骤2中所做的其他源的更改将丢失。

如果需要支持同一对象的多个编写器,则可以使用 kubectl apply 来管理该对象。

从 URL 创建和编辑对象而不保存配置

假设你具有对象配置文件的 URL。 你可以在创建对象之前使用 kubectl create --edit 对配置进行更改。 这对于指向可以由读者修改的配置文件的教程和任务特别有用。

kubectl create -f <url> --edit

从命令式命令迁移到命令式对象配置

从命令式命令迁移到命令式对象配置涉及几个手动步骤。

  1. 将活动对象导出到本地对象配置文件:

    kubectl get <kind>/<name> -o yaml > <kind>_<name>.yaml
    
  1. 从对象配置文件中手动删除状态字段。
  1. 对于后续的对象管理,只能使用 replace

    kubectl replace -f <kind>_<name>.yaml
    

定义控制器选择器和 PodTemplate 标签

推荐的方法是定义单个不变的 PodTemplate 标签,该标签仅由控制器选择器使用,而没有其他语义。

标签示例:

selector:
  matchLabels:
      controller-selector: "apps/v1/deployment/nginx"
template:
  metadata:
    labels:
      controller-selector: "apps/v1/deployment/nginx"

接下来

4.5.5 - 使用 kubectl patch 更新 API 对象

使用 kubectl patch 更新 Kubernetes API 对象。做一个策略性的合并 patch 或 JSON 合并 patch。

这个任务展示如何使用 kubectl patch 就地更新 API 对象。 这个任务中的练习演示了一个策略性合并 patch 和一个 JSON 合并 patch。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

使用策略合并 patch 更新 Deployment

下面是具有两个副本的 Deployment 的配置文件。每个副本是一个 Pod,有一个容器:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: patch-demo
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: patch-demo-ctr
        image: nginx
      tolerations:
      - effect: NoSchedule
        key: dedicated
        value: test-team

创建 Deployment:

kubectl apply -f https://k8s.io/examples/application/deployment-patch.yaml

查看与 Deployment 相关的 Pod:

kubectl get pods

输出显示 Deployment 有两个 Pod。1/1 表示每个 Pod 有一个容器:

NAME                        READY     STATUS    RESTARTS   AGE
patch-demo-28633765-670qr   1/1       Running   0          23s
patch-demo-28633765-j5qs3   1/1       Running   0          23s

把运行的 Pod 的名字记下来。稍后,你将看到这些 Pod 被终止并被新的 Pod 替换。

此时,每个 Pod 都有一个运行 nginx 镜像的容器。现在假设你希望每个 Pod 有两个容器:一个运行 nginx,另一个运行 redis。

创建一个名为 patch-file.yaml 的文件。内容如下:

spec:
  template:
    spec:
      containers:
      - name: patch-demo-ctr-2
        image: redis

修补你的 Deployment:

kubectl patch deployment patch-demo --patch-file patch-file.yaml

查看修补后的 Deployment:

kubectl get deployment patch-demo --output yaml

输出显示 Deployment 中的 PodSpec 有两个容器:

containers:
- image: redis
  imagePullPolicy: Always
  name: patch-demo-ctr-2
  ...
- image: nginx
  imagePullPolicy: Always
  name: patch-demo-ctr
  ...

查看与 patch Deployment 相关的 Pod:

kubectl get pods

输出显示正在运行的 Pod 与以前运行的 Pod 有不同的名称。Deployment 终止了旧的 Pod, 并创建了两个符合更新后的 Deployment 规约的新 Pod。2/2 表示每个 Pod 有两个容器:

NAME                          READY     STATUS    RESTARTS   AGE
patch-demo-1081991389-2wrn5   2/2       Running   0          1m
patch-demo-1081991389-jmg7b   2/2       Running   0          1m

仔细查看其中一个 patch-demo Pod:

kubectl get pod <your-pod-name> --output yaml

输出显示 Pod 有两个容器:一个运行 nginx,一个运行 redis:

containers:
- image: redis
  ...
- image: nginx
  ...

策略性合并类的 patch 的说明

你在前面的练习中所做的 patch 称为 策略性合并 patch(Strategic Merge Patch)。 请注意,patch 没有替换 containers 列表。相反,它向列表中添加了一个新 Container。换句话说, patch 中的列表与现有列表合并。当你在列表中使用策略性合并 patch 时,并不总是这样。 在某些情况下,列表是替换的,而不是合并的。

对于策略性合并 patch,列表可以根据其 patch 策略进行替换或合并。 patch 策略由 Kubernetes 源代码中字段标记中的 patchStrategy 键的值指定。 例如,PodSpec 结构体的 Containers 字段的 patchStrategymerge

type PodSpec struct {
  ...
  Containers []Container `json:"containers" patchStrategy:"merge" patchMergeKey:"name" ...`
  ...
}

你还可以在 OpenApi 规范中看到 patch 策略:

"io.k8s.api.core.v1.PodSpec": {
    ...,
    "containers": {
        "description": "List of containers belonging to the pod.  ...."
    },
    "x-kubernetes-patch-merge-key": "name",
    "x-kubernetes-patch-strategy": "merge"
}

你可以在 Kubernetes API 文档 中看到 patch 策略。

创建一个名为 patch-file-tolerations.yaml 的文件。内容如下:

spec:
  template:
    spec:
      tolerations:
      - effect: NoSchedule
        key: disktype
        value: ssd

对 Deployment 执行 patch 操作:

kubectl patch deployment patch-demo --patch-file patch-file-tolerations.yaml

查看修补后的 Deployment:

kubectl get deployment patch-demo --output yaml

输出结果显示 Deployment 中的 PodSpec 只有一个容忍度设置:

tolerations:
- effect: NoSchedule
  key: disktype
  value: ssd

请注意,PodSpec 中的 tolerations 列表被替换,而不是合并。这是因为 PodSpec 的 tolerations 的字段标签中没有 patchStrategy 键。所以策略合并 patch 操作使用默认的 patch 策略,也就是 replace

type PodSpec struct {
  ...
  Tolerations []Toleration `json:"tolerations,omitempty" protobuf:"bytes,22,opt,name=tolerations"`
  ...
}

使用 JSON 合并 patch 更新 Deployment

策略性合并 patch 不同于 JSON 合并 patch。 使用 JSON 合并 patch,如果你想更新列表,你必须指定整个新列表。新的列表完全取代现有的列表。

kubectl patch 命令有一个 type 参数,你可以将其设置为以下值之一:

参数值合并类型
jsonJSON Patch, RFC 6902
mergeJSON Merge Patch, RFC 7386
strategic策略合并 patch

有关 JSON patch 和 JSON 合并 patch 的比较,查看 JSON patch 和 JSON 合并 patch

type 参数的默认值是 strategic。在前面的练习中,我们做了一个策略性的合并 patch。

下一步,在相同的 Deployment 上执行 JSON 合并 patch。创建一个名为 patch-file-2 的文件。内容如下:

spec:
  template:
    spec:
      containers:
      - name: patch-demo-ctr-3
        image: gcr.io/google-samples/node-hello:1.0

在 patch 命令中,将 type 设置为 merge

kubectl patch deployment patch-demo --type merge --patch-file patch-file-2.yaml

查看修补后的 Deployment:

kubectl get deployment patch-demo --output yaml

patch 中指定的 containers 列表只有一个 Container。 输出显示你所给出的 Container 列表替换了现有的 containers 列表。

spec:
  containers:
  - image: gcr.io/google-samples/node-hello:1.0
    ...
    name: patch-demo-ctr-3

列出正运行的 Pod:

kubectl get pods

在输出中,你可以看到已经终止了现有的 Pod,并创建了新的 Pod。1/1 表示每个新 Pod 只运行一个容器。

NAME                          READY     STATUS    RESTARTS   AGE
patch-demo-1307768864-69308   1/1       Running   0          1m
patch-demo-1307768864-c86dc   1/1       Running   0          1m

使用带 retainKeys 策略的策略合并 patch 更新 Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: retainkeys-demo
spec:
  selector:
    matchLabels:
      app: nginx
  strategy:
    rollingUpdate:
      maxSurge: 30%
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: retainkeys-demo-ctr
        image: nginx

创建 Deployment:

kubectl apply -f https://k8s.io/examples/application/deployment-retainkeys.yaml

这时,Deployment 被创建,并使用 RollingUpdate 策略。

创建一个名为 patch-file-no-retainkeys.yaml 的文件,内容如下:

spec:
  strategy:
    type: Recreate

修补你的 Deployment:

kubectl patch deployment retainkeys-demo --type strategic --patch-file patch-file-no-retainkeys.yaml

在输出中,你可以看到,当 spec.strategy.rollingUpdate 已经拥有取值定义时, 将其 type 设置为 Recreate 是不可能的。

The Deployment "retainkeys-demo" is invalid: spec.strategy.rollingUpdate: Forbidden: may not be specified when strategy `type` is 'Recreate'

更新 type 取值的同时移除 spec.strategy.rollingUpdate 现有值的方法是为策略性合并操作设置 retainKeys 策略:

创建另一个名为 patch-file-retainkeys.yaml 的文件,内容如下:

spec:
  strategy:
    $retainKeys:
    - type
    type: Recreate

使用此 patch,我们表达了希望只保留 strategy 对象的 type 键。 这样,在 patch 操作期间 rollingUpdate 会被删除。

使用新的 patch 重新修补 Deployment:

kubectl patch deployment retainkeys-demo --type strategic --patch-file patch-file-retainkeys.yaml

检查 Deployment 的内容:

kubectl get deployment retainkeys-demo --output yaml

输出显示 Deployment 中的 strategy 对象不再包含 rollingUpdate 键:

spec:
  strategy:
    type: Recreate
  template:

关于使用 retainKeys 策略的策略合并 patch 操作的说明

在前文练习中所执行的称作 retainKeys 策略的策略合并 patch(Strategic Merge Patch with retainKeys Strategy)。 这种方法引入了一种新的 $retainKey 指令,具有如下策略:

  • 其中包含一个字符串列表;
  • 所有需要被保留的字段必须在 $retainKeys 列表中给出;
  • 对于已有的字段,会和对象上对应的内容合并;
  • 在修补操作期间,未找到的字段都会被清除;
  • 列表 $retainKeys 中的所有字段必须 patch 操作所给字段的超集,或者与之完全一致。

策略 retainKeys 并不能对所有对象都起作用。它仅对那些 Kubernetes 源码中 patchStrategy 字段标志值包含 retainKeys 的字段有用。 例如 DeploymentSpec 结构的 Strategy 字段就包含了 patchStrategyretainKeys 的标志。

type DeploymentSpec struct {
  ...
  // +patchStrategy=retainKeys
  Strategy DeploymentStrategy `json:"strategy,omitempty" patchStrategy:"retainKeys" ...`
  ...
}

你也可以查看 OpenAPI 规范中的 retainKeys 策略:

"io.k8s.api.apps.v1.DeploymentSpec": {
    ...,
    "strategy": {
        "$ref": "#/definitions/io.k8s.api.apps.v1.DeploymentStrategy",
        "description": "The deployment strategy to use to replace existing pods with new ones.",
        "x-kubernetes-patch-strategy": "retainKeys"
    },
    ....
}

而且你也可以在 Kubernetes API 文档中看到 retainKey 策略。

kubectl patch 命令的其他形式

kubectl patch 命令使用 YAML 或 JSON。它可以接受以文件形式提供的补丁,也可以接受直接在命令行中给出的补丁。

创建一个文件名称是 patch-file.json 内容如下:

{
   "spec": {
      "template": {
         "spec": {
            "containers": [
               {
                  "name": "patch-demo-ctr-2",
                  "image": "redis"
               }
            ]
         }
      }
   }
}

以下命令是等价的:

kubectl patch deployment patch-demo --patch-file patch-file.yaml
kubectl patch deployment patch-demo --patch 'spec:\n template:\n  spec:\n   containers:\n   - name: patch-demo-ctr-2\n     image: redis'

kubectl patch deployment patch-demo --patch-file patch-file.json
kubectl patch deployment patch-demo --patch '{"spec": {"template": {"spec": {"containers": [{"name": "patch-demo-ctr-2","image": "redis"}]}}}}'

使用 kubectl patch--subresource 更新一个对象的副本数

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

使用 kubectl 命令(如 get、patch、edit 和 replace)时带上 --subresource=[subresource-name] 标志, 可以获取和更新资源的 statusscale 子资源(适用于 kubectl v1.24 或更高版本)。 这个标志可用于带有 statusscale 子资源的所有 API 资源 (内置资源和 CR 资源)。 Deployment 是支持这些子资源的其中一个例子。

下面是有两个副本的 Deployment 的清单。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2 # 告知 Deployment 运行 2 个与该模板匹配的 Pod
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

创建 Deployment:

kubectl apply -f https://k8s.io/examples/application/deployment.yaml

查看与 Deployment 关联的 Pod:

kubectl get pods -l app=nginx

在输出中,你可以看到此 Deployment 有两个 Pod。例如:

NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-7fb96c846b-22567   1/1     Running   0          47s
nginx-deployment-7fb96c846b-mlgns   1/1     Running   0          47s

现在用 --subresource=[subresource-name] 标志修补此 Deployment:

kubectl patch deployment nginx-deployment --subresource='scale' --type='merge' -p '{"spec":{"replicas":3}}'

输出为:

scale.autoscaling/nginx-deployment patched

查看与你所修补的 Deployment 关联的 Pod:

kubectl get pods -l app=nginx

在输出中,你可以看到一个新的 Pod 被创建,因此现在你有 3 个正在运行的 Pod。

NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-7fb96c846b-22567   1/1     Running   0          107s
nginx-deployment-7fb96c846b-lxfr2   1/1     Running   0          14s
nginx-deployment-7fb96c846b-mlgns   1/1     Running   0          107s

查看所修补的 Deployment:

kubectl get deployment nginx-deployment -o yaml
...
spec:
  replicas: 3
  ...
status:
  ...
  availableReplicas: 3
  readyReplicas: 3
  replicas: 3

总结

在本练习中,你使用 kubectl patch 更改了 Deployment 对象的当前配置。 你没有更改最初用于创建 Deployment 对象的配置文件。 用于更新 API 对象的其他命令包括 kubectl annotatekubectl editkubectl replacekubectl scalekubectl apply

接下来

4.6 - 管理 Secrets

使用 Secrets 管理机密配置数据。

4.6.1 - 使用 kubectl 管理 Secret

使用 kubectl 命令行创建 Secret 对象。

本页向你展示如何使用 kubectl 命令行工具来创建、编辑、管理和删除。 Kubernetes Secrets

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

创建 Secret

Secret 对象用来存储敏感数据,如 Pod 用于访问服务的凭据。例如,为访问数据库,你可能需要一个 Secret 来存储所需的用户名及密码。

你可以通过在命令中传递原始数据,或将凭据存储文件中,然后再在命令行中创建 Secret。以下命令 将创建一个存储用户名 admin 和密码 S!B\*d$zDsb= 的 Secret。

使用原始数据

执行以下命令:

kubectl create secret generic db-user-pass \
    --from-literal=username=admin \
    --from-literal=password='S!B\*d$zDsb='

你必须使用单引号 '' 转义字符串中的特殊字符,如 $\*=! 。否则,你的 shell 将会解析这些字符。

使用源文件

  1. 将凭据保存到文件:

    echo -n 'admin' > ./username.txt
    echo -n 'S!B\*d$zDsb=' > ./password.txt
    

    -n 标志用来确保生成文件的文末没有多余的换行符。这很重要,因为当 kubectl 读取文件并将内容编码为 base64 字符串时,额外的换行符也会被编码。 你不需要对文件中包含的字符串中的特殊字符进行转义。

  1. kubectl 命令中传递文件路径:

    kubectl create secret generic db-user-pass \
        --from-file=./username.txt \
        --from-file=./password.txt
    

    默认键名为文件名。你也可以通过 --from-file=[key=]source 设置键名,例如:

    kubectl create secret generic db-user-pass \
        --from-file=username=./username.txt \
        --from-file=password=./password.txt
    

无论使用哪种方法,输出都类似于:

secret/db-user-pass created

验证 Secret

检查 Secret 是否已创建:

kubectl get secrets

输出类似于:

NAME              TYPE       DATA      AGE
db-user-pass      Opaque     2         51s

查看 Secret 的细节:

kubectl describe secret db-user-pass

输出类似于:

Name:            db-user-pass
Namespace:       default
Labels:          <none>
Annotations:     <none>

Type:            Opaque

Data
====
password:    12 bytes
username:    5 bytes

kubectl getkubectl describe 命令默认不显示 Secret 的内容。 这是为了防止 Secret 被意外暴露或存储在终端日志中。

解码 Secret

  1. 查看你所创建的 Secret 内容

    kubectl get secret db-user-pass -o jsonpath='{.data}'
    

    输出类似于:

    { "password": "UyFCXCpkJHpEc2I9", "username": "YWRtaW4=" }
    
  1. 解码 password 数据:

    echo 'UyFCXCpkJHpEc2I9' | base64 --decode
    

    输出类似于:

    S!B\*d$zDsb=
    
    kubectl get secret db-user-pass -o jsonpath='{.data.password}' | base64 --decode
    

编辑 Secret

你可以编辑一个现存的 Secret 对象,除非它是不可改变的。 要想编辑一个 Secret,请执行以下命令:

kubectl edit secrets <secret-name>

这将打开默认编辑器,并允许你更新 data 字段中的 base64 编码的 Secret 值,示例如下:

#请编辑下面的对象。以“#”开头的行将被忽略,
#空文件将中止编辑。如果在保存此文件时发生错误,
#则将重新打开该文件并显示相关的失败。
apiVersion: v1
data:
  password: UyFCXCpkJHpEc2I9
  username: YWRtaW4=
kind: Secret
metadata:
  creationTimestamp: "2022-06-28T17:44:13Z"
  name: db-user-pass
  namespace: default
  resourceVersion: "12708504"
  uid: 91becd59-78fa-4c85-823f-6d44436242ac
type: Opaque

清理

要想删除一个 Secret,请执行以下命令:

kubectl delete secret db-user-pass

接下来

4.6.2 - 使用配置文件管理 Secret

使用资源配置文件创建 Secret 对象。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

创建 Secret

你可以先用 JSON 或 YAML 格式在一个清单文件中定义 Secret 对象,然后创建该对象。 Secret 资源包含 2 个键值对:datastringDatadata 字段用来存储 base64 编码的任意数据。 提供 stringData 字段是为了方便,它允许 Secret 使用未编码的字符串。 datastringData 的键必须由字母、数字、-_. 组成。

以下示例使用 data 字段在 Secret 中存储两个字符串:

  1. 将这些字符串转换为 base64:

    echo -n 'admin' | base64
    echo -n '1f2d1e2e67df' | base64
    

    输出类似于:

    YWRtaW4=
    MWYyZDFlMmU2N2Rm
    
  1. 创建清单:

    apiVersion: v1
    kind: Secret
    metadata:
      name: mysecret
    type: Opaque
    data:
      username: YWRtaW4=
      password: MWYyZDFlMmU2N2Rm
    

    注意,Secret 对象的名称必须是有效的 DNS 子域名

  1. 使用 kubectl apply 创建 Secret:

    kubectl apply -f ./secret.yaml
    

    输出类似于:

    secret/mysecret created
    

若要验证 Secret 被创建以及想要解码 Secret 数据, 请参阅使用 kubectl 管理 Secret

创建 Secret 时提供未编码的数据

对于某些场景,你可能希望使用 stringData 字段。 这个字段可以将一个非 base64 编码的字符串直接放入 Secret 中, 当创建或更新该 Secret 时,此字段将被编码。

上述用例的实际场景可能是这样:当你部署应用时,使用 Secret 存储配置文件, 你希望在部署过程中,填入部分内容到该配置文件。

例如,如果你的应用程序使用以下配置文件:

apiUrl: "https://my.api.com/api/v1"
username: "<user>"
password: "<password>"

你可以使用以下定义将其存储在 Secret 中:

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
stringData:
  config.yaml: |
    apiUrl: "https://my.api.com/api/v1"
    username: <user>
    password: <password>    

当你检索 Secret 数据时,此命令将返回编码的值,并不是你在 stringData 中提供的纯文本值。

例如,如果你运行以下命令:

kubectl get secret mysecret -o yaml

输出类似于:

apiVersion: v1
data:
  config.yaml: YXBpVXJsOiAiaHR0cHM6Ly9teS5hcGkuY29tL2FwaS92MSIKdXNlcm5hbWU6IHt7dXNlcm5hbWV9fQpwYXNzd29yZDoge3twYXNzd29yZH19
kind: Secret
metadata:
  creationTimestamp: 2018-11-15T20:40:59Z
  name: mysecret
  namespace: default
  resourceVersion: "7225"
  uid: c280ad2e-e916-11e8-98f2-025000000001
type:

同时指定 datastringData

如果你在 datastringData 中设置了同一个字段,则使用来自 stringData 中的值。

例如,如果你定义以下 Secret:

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  username: YWRtaW4=
stringData:
  username: administrator

所创建的 Secret 对象如下:

apiVersion: v1
data:
  username: YWRtaW5pc3RyYXRvcg==
kind: Secret
metadata:
  creationTimestamp: 2018-11-15T20:46:46Z
  name: mysecret
  namespace: default
  resourceVersion: "7579"
  uid: 91460ecb-e917-11e8-98f2-025000000001
type: Opaque

YWRtaW5pc3RyYXRvcg== 解码成 administrator

编辑 Secret

要编辑使用清单创建的 Secret 中的数据,请修改清单中的 datastringData 字段并将此清单文件应用到集群。 你可以编辑现有的 Secret 对象,除非它是不可变的

例如,如果你想将上一个示例中的密码更改为 birdsarentreal,请执行以下操作:

  1. 编码新密码字符串:

    echo -n 'birdsarentreal' | base64
    

    输出类似于:

    YmlyZHNhcmVudHJlYWw=
    
  1. 使用你的新密码字符串更新 data 字段:

    apiVersion: v1
    kind: Secret
    metadata:
      name: mysecret
    type: Opaque
    data:
      username: YWRtaW4=
      password: YmlyZHNhcmVudHJlYWw=
    
  1. 将清单应用到你的集群:

    kubectl apply -f ./secret.yaml
    

    输出类似于:

    secret/mysecret configured
    

Kubernetes 更新现有的 Secret 对象。具体而言,kubectl 工具发现存在一个同名的现有 Secret 对象。 kubectl 获取现有对象,计划对其进行更改,并将更改后的 Secret 对象提交到你的集群控制平面。

如果你指定了 kubectl apply --server-side,则 kubectl 使用服务器端应用(Server-Side Apply)

清理

删除你创建的 Secret:

kubectl delete secret mysecret

接下来

4.6.3 - 使用 Kustomize 管理 Secret

使用 kustomization.yaml 文件创建 Secret 对象。

kubectl 支持使用 Kustomize 对象管理工具来管理 Secret 和 ConfigMap。你可以使用 Kustomize 创建资源生成器(Resource Generator), 该生成器会生成一个 Secret,让你能够通过 kubectl 应用到 API 服务器。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

创建 Secret

你可以在 kustomization.yaml 文件中定义 secreteGenerator 字段, 并在定义中引用其它本地文件、.env 文件或文字值生成 Secret。 例如:下面的指令为用户名 admin 和密码 1f2d1e2e67df 创建 Kustomization 文件。

创建 Kustomization 文件


secretGenerator:
- name: database-creds
  literals:
  - username=admin
  - password=1f2d1e2e67df

  1. 将凭据存储在文件中。文件名是 Secret 的 key 值:

    echo -n 'admin' > ./username.txt
    echo -n '1f2d1e2e67df' > ./password.txt
    

    -n 标志确保文件结尾处没有换行符。

  1. 创建 kustomization.yaml 文件:

    secretGenerator:
    - name: database-creds
      files:
      - username.txt
      - password.txt
    

你也可以使用 .env 文件在 kustomization.yaml 中定义 secretGenerator。 例如下面的 kustomization.yaml 文件从 .env.secret 文件获取数据:

secretGenerator:
- name: db-user-pass
  envs:
  - .env.secret

在所有情况下,你都不需要对取值作 base64 编码。 YAML 文件的名称必须kustomization.yamlkustomization.yml

应用 kustomization 文件

若要创建 Secret,应用包含 kustomization 文件的目录。

kubectl apply -k <目录路径>

输出类似于:

secret/database-creds-5hdh7hhgfk created

生成 Secret 时,Secret 的名称最终是由 name 字段和数据的哈希值拼接而成。 这将保证每次修改数据时生成一个新的 Secret。

要验证 Secret 是否已创建并解码 Secret 数据,

kubectl get -k <directory-path> -o jsonpath='{.data}' 

输出类似于:

{ "password": "UyFCXCpkJHpEc2I9", "username": "YWRtaW4=" }
echo 'UyFCXCpkJHpEc2I9' | base64 --decode

输出类似于:

S!B\*d$zDsb=

更多信息参阅 使用 kubectl 管理 Secret使用 Kustomize 对 Kubernetes 对象进行声明式管理

编辑 Secret

  1. kustomization.yaml 文件中,修改诸如 password 等数据。

  2. 应用包含 kustomization 文件的目录:

    kubectl apply -k <目录路径>
    

    输出类似于:

    secret/db-user-pass-6f24b56cc8 created
    

编辑过的 Secret 被创建为一个新的 Secret 对象,而不是更新现有的 Secret 对象。 你可能需要在 Pod 中更新对该 Secret 的引用。

清理

要删除 Secret,请使用 kubectl

kubectl delete secret db-user-pass

接下来

4.7 - 给应用注入数据

给你的工作负载 Pod 指定配置和其他数据。

4.7.1 - 为容器设置启动时要执行的命令和参数

本页将展示如何为 Pod 中容器设置启动时要执行的命令及其参数。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

创建 Pod 时设置命令及参数

创建 Pod 时,可以为其下的容器设置启动时要执行的命令及其参数。如果要设置命令,就填写在配置文件的 command 字段下,如果要设置命令的参数,就填写在配置文件的 args 字段下。 一旦 Pod 创建完成,该命令及其参数就无法再进行更改了。

如果在配置文件中设置了容器启动时要执行的命令及其参数,那么容器镜像中自带的命令与参数将会被覆盖而不再执行。 如果配置文件中只是设置了参数,却没有设置其对应的命令,那么容器镜像中自带的命令会使用该新参数作为其执行时的参数。

本示例中,将创建一个只包含单个容器的 Pod。在此 Pod 配置文件中设置了一个命令与两个参数:

apiVersion: v1
kind: Pod
metadata:
  name: command-demo
  labels:
    purpose: demonstrate-command
spec:
  containers:
  - name: command-demo-container
    image: debian
    command: ["printenv"]
    args: ["HOSTNAME", "KUBERNETES_PORT"]
  restartPolicy: OnFailure
  1. 基于 YAML 文件创建一个 Pod:

    kubectl apply -f https://k8s.io/examples/pods/commands.yaml
    
  1. 获取正在运行的 Pod:

    kubectl get pods
    

    查询结果显示在 command-demo 这个 Pod 下运行的容器已经启动完成。

  1. 如果要获取容器启动时执行命令的输出结果,可以通过 Pod 的日志进行查看:

    kubectl logs command-demo
    

    日志中显示了 HOSTNAME 与 KUBERNETES_PORT 这两个环境变量的值:

    command-demo
    tcp://10.3.240.1:443
    

使用环境变量来设置参数

在上面的示例中,我们直接将一串字符作为命令的参数。除此之外,我们还可以将环境变量作为命令的参数。

env:
- name: MESSAGE
  value: "hello world"
command: ["/bin/echo"]
args: ["$(MESSAGE)"]

这意味着你可以将那些用来设置环境变量的方法应用于设置命令的参数,其中包括了 ConfigMapSecret

在 Shell 来执行命令

有时候,你需要在 Shell 脚本中运行命令。 例如,你要执行的命令可能由多个命令组合而成,或者它就是一个 Shell 脚本。 这时,就可以通过如下方式在 Shell 中执行命令:

command: ["/bin/sh"]
args: ["-c", "while true; do echo hello; sleep 10;done"]

接下来

4.7.2 - 定义相互依赖的环境变量

本页展示了如何为 Kubernetes Pod 中的容器定义相互依赖的环境变量。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

为容器定义相互依赖的环境变量

当创建一个 Pod 时,你可以为运行在 Pod 中的容器设置相互依赖的环境变量。 若要设置相互依赖的环境变量,你可以在配置清单文件的 envvalue 中使用 $(VAR_NAME)。

在本练习中,你会创建一个单容器的 Pod。 此 Pod 的配置文件定义了一个已定义常用用法的相互依赖的环境变量。 下面是此 Pod 的配置清单:

apiVersion: v1
kind: Pod
metadata:
  name: dependent-envars-demo
spec:
  containers:
    - name: dependent-envars-demo
      args:
        - while true; do echo -en '\n'; printf UNCHANGED_REFERENCE=$UNCHANGED_REFERENCE'\n'; printf SERVICE_ADDRESS=$SERVICE_ADDRESS'\n';printf ESCAPED_REFERENCE=$ESCAPED_REFERENCE'\n'; sleep 30; done;
      command:
        - sh
        - -c
      image: busybox:1.28
      env:
        - name: SERVICE_PORT
          value: "80"
        - name: SERVICE_IP
          value: "172.17.0.1"
        - name: UNCHANGED_REFERENCE
          value: "$(PROTOCOL)://$(SERVICE_IP):$(SERVICE_PORT)"
        - name: PROTOCOL
          value: "https"
        - name: SERVICE_ADDRESS
          value: "$(PROTOCOL)://$(SERVICE_IP):$(SERVICE_PORT)"
        - name: ESCAPED_REFERENCE
          value: "$$(PROTOCOL)://$(SERVICE_IP):$(SERVICE_PORT)"
  1. 依据清单创建 Pod:

    kubectl apply -f https://k8s.io/examples/pods/inject/dependent-envars.yaml
    
    pod/dependent-envars-demo created
    
  1. 列出运行的 Pod:

    kubectl get pods dependent-envars-demo
    
    NAME                      READY     STATUS    RESTARTS   AGE
    dependent-envars-demo     1/1       Running   0          9s
    
  1. 检查 Pod 中运行容器的日志:

    kubectl logs pod/dependent-envars-demo
    
    
    UNCHANGED_REFERENCE=$(PROTOCOL)://172.17.0.1:80
    SERVICE_ADDRESS=https://172.17.0.1:80
    ESCAPED_REFERENCE=$(PROTOCOL)://172.17.0.1:80
    

如上所示,你已经定义了 SERVICE_ADDRESS 的正确依赖引用, UNCHANGED_REFERENCE 的错误依赖引用, 并跳过了 ESCAPED_REFERENCE 的依赖引用。

如果环境变量被引用时已事先定义,则引用可以正确解析, 比如 SERVICE_ADDRESS 的例子。

请注意,env 列表中的顺序很重要。如果某环境变量定义出现在列表的尾部, 则在解析列表前部环境变量时不会视其为“已被定义”。 这就是为什么 UNCHANGED_REFERENCE 在上面的示例中解析 $(PROTOCOL) 失败的原因。

当环境变量未定义或仅包含部分变量时,未定义的变量会被当做普通字符串对待, 比如 UNCHANGED_REFERENCE 的例子。 注意,解析不正确的环境变量通常不会阻止容器启动。

$(VAR_NAME) 这样的语法可以用两个 $ 转义,即:$$(VAR_NAME)。 无论引用的变量是否定义,转义的引用永远不会展开。 这一点可以从上面 ESCAPED_REFERENCE 的例子得到印证。

接下来

4.7.3 - 为容器设置环境变量

本页将展示如何为 Kubernetes Pod 下的容器设置环境变量。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

为容器设置一个环境变量

创建 Pod 时,可以为其下的容器设置环境变量。通过配置文件的 env 或者 envFrom 字段来设置环境变量。

envenvFrom 字段具有不同的效果。

env :可以为容器设置环境变量,直接为你所给的每个变量指定一个值。

envFrom :你可以通过引用 ConfigMap 或 Secret 来设置容器的环境变量。 使用 envFrom 时,引用的 ConfigMap 或 Secret 中的所有键值对都被设置为容器的环境变量。 你也可以指定一个通用的前缀字符串。

你可以阅读有关 ConfigMapSecret 的更多信息。

本页介绍如何使用 env

本示例中,将创建一个只包含单个容器的 Pod。此 Pod 的配置文件中设置环境变量的名称为 DEMO_GREETING, 其值为 "Hello from the environment"。下面是此 Pod 的配置清单:

apiVersion: v1
kind: Pod
metadata:
  name: envar-demo
  labels:
    purpose: demonstrate-envars
spec:
  containers:
  - name: envar-demo-container
    image: gcr.io/google-samples/node-hello:1.0
    env:
    - name: DEMO_GREETING
      value: "Hello from the environment"
    - name: DEMO_FAREWELL
      value: "Such a sweet sorrow"
  1. 基于配置清单创建一个 Pod:

    kubectl apply -f https://k8s.io/examples/pods/inject/envars.yaml
    
  1. 获取正在运行的 Pod 信息:

    kubectl get pods -l purpose=demonstrate-envars
    
    查询结果应为:
    
    NAME            READY     STATUS    RESTARTS   AGE
    envar-demo      1/1       Running   0          9s
    
  1. 列出 Pod 容器的环境变量:

    kubectl exec envar-demo -- printenv
    
    打印结果应为:
    
    NODE_VERSION=4.4.2
    EXAMPLE_SERVICE_PORT_8080_TCP_ADDR=10.3.245.237
    HOSTNAME=envar-demo
    ...
    DEMO_GREETING=Hello from the environment
    DEMO_FAREWELL=Such a sweet sorrow
    

在配置中使用环境变量

你在 Pod 的配置中定义的、位于 .spec.containers[*].env[*] 下的环境变量 可以在配置的其他地方使用,例如可用在为 Pod 的容器设置的命令和参数中。 在下面的示例配置中,环境变量 GREETINGHONORIFICNAME 分别设置为 Warm greetings toThe Most HonorableKubernetes。 环境变量 MESSAGE 将所有这些环境变量的集合组合起来, 然后再传递给容器 env-print-demo 的 CLI 参数中使用。

环境变量名由字母、数字、下划线、点或连字符组成,但第一个字符不能是数字。 如果启用了 RelaxedEnvironmentVariableValidation 特性门控, 则所有可打印的 ASCII 字符("=" 除外)都可以用于环境变量名。

apiVersion: v1
kind: Pod
metadata:
  name: print-greeting
spec:
  containers:
  - name: env-print-demo
    image: bash
    env:
    - name: GREETING
      value: "Warm greetings to"
    - name: HONORIFIC
      value: "The Most Honorable"
    - name: NAME
      value: "Kubernetes"
    command: ["echo"]
    args: ["$(GREETING) $(HONORIFIC) $(NAME)"]

创建后,命令 echo Warm greetings to The Most Honorable Kubernetes 将在容器中运行。

接下来

4.7.4 - 通过环境变量将 Pod 信息呈现给容器

此页面展示 Pod 如何使用 downward API 通过环境变量把自身的信息呈现给 Pod 中运行的容器。 你可以使用环境变量来呈现 Pod 的字段、容器字段或两者。

在 Kubernetes 中有两种方式可以将 Pod 和容器字段呈现给运行中的容器:

这两种呈现 Pod 和容器字段的方式统称为 downward API。

Service 是 Kubernetes 管理的容器化应用之间的主要通信模式,因此在运行时能发现这些 Service 是很有帮助的。

这里 阅读更多关于访问 Service 的信息。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

用 Pod 字段作为环境变量的值

在这部分练习中,你将创建一个包含一个容器的 Pod。并将 Pod 级别的字段作为环境变量投射到正在运行的容器中。

apiVersion: v1
kind: Pod
metadata:
  name: dapi-envars-fieldref
spec:
  containers:
    - name: test-container
      image: registry.k8s.io/busybox
      command: [ "sh", "-c"]
      args:
      - while true; do
          echo -en '\n';
          printenv MY_NODE_NAME MY_POD_NAME MY_POD_NAMESPACE;
          printenv MY_POD_IP MY_POD_SERVICE_ACCOUNT;
          sleep 10;
        done;
      env:
        - name: MY_NODE_NAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        - name: MY_POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: MY_POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: MY_POD_IP
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
        - name: MY_POD_SERVICE_ACCOUNT
          valueFrom:
            fieldRef:
              fieldPath: spec.serviceAccountName
  restartPolicy: Never

这个清单中,你可以看到五个环境变量。env 字段定义了一组环境变量。 数组中第一个元素指定 MY_NODE_NAME 这个环境变量从 Pod 的 spec.nodeName 字段获取变量值。 同样,其它环境变量也是从 Pod 的字段获取它们的变量值。

创建 Pod:

kubectl apply -f https://k8s.io/examples/pods/inject/dapi-envars-pod.yaml

验证 Pod 中的容器运行正常:

# 如果新创建的 Pod 还是处于不健康状态,请重新运行此命令几次。
kubectl get pods

查看容器日志:

kubectl logs dapi-envars-fieldref

输出信息显示了所选择的环境变量的值:

minikube
dapi-envars-fieldref
default
172.17.0.4
default

要了解为什么这些值出现在日志中,请查看配置文件中的 commandargs 字段。 当容器启动时,它将五个环境变量的值写入标准输出。每十秒重复执行一次。

接下来,进入 Pod 中运行的容器,打开一个 Shell:

kubectl exec -it dapi-envars-fieldref -- sh

在 Shell 中,查看环境变量:

# 在容器内的 `shell` 中运行
printenv

输出信息显示环境变量已经设置为 Pod 字段的值。

MY_POD_SERVICE_ACCOUNT=default
...
MY_POD_NAMESPACE=default
MY_POD_IP=172.17.0.4
...
MY_NODE_NAME=minikube
...
MY_POD_NAME=dapi-envars-fieldref

使用容器字段作为环境变量的值

前面的练习中,你将 Pod 级别的字段作为环境变量的值。 接下来这个练习中,你将传递属于 Pod 定义的字段,但这些字段取自特定容器而不是整个 Pod。

这里是只包含一个容器的 Pod 的清单:

apiVersion: v1
kind: Pod
metadata:
  name: dapi-envars-resourcefieldref
spec:
  containers:
    - name: test-container
      image: registry.k8s.io/busybox:1.24
      command: [ "sh", "-c"]
      args:
      - while true; do
          echo -en '\n';
          printenv MY_CPU_REQUEST MY_CPU_LIMIT;
          printenv MY_MEM_REQUEST MY_MEM_LIMIT;
          sleep 10;
        done;
      resources:
        requests:
          memory: "32Mi"
          cpu: "125m"
        limits:
          memory: "64Mi"
          cpu: "250m"
      env:
        - name: MY_CPU_REQUEST
          valueFrom:
            resourceFieldRef:
              containerName: test-container
              resource: requests.cpu
        - name: MY_CPU_LIMIT
          valueFrom:
            resourceFieldRef:
              containerName: test-container
              resource: limits.cpu
        - name: MY_MEM_REQUEST
          valueFrom:
            resourceFieldRef:
              containerName: test-container
              resource: requests.memory
        - name: MY_MEM_LIMIT
          valueFrom:
            resourceFieldRef:
              containerName: test-container
              resource: limits.memory
  restartPolicy: Never

这个清单中,你可以看到四个环境变量。env 字段定义了一组环境变量。 数组中第一个元素指定 MY_CPU_REQUEST 这个环境变量从容器的 requests.cpu 字段获取变量值。同样,其它的环境变量也是从特定于这个容器的字段中获取它们的变量值。

创建 Pod:

kubectl apply -f https://k8s.io/examples/pods/inject/dapi-envars-container.yaml

验证 Pod 中的容器运行正常:

# 如果新创建的 Pod 还是处于不健康状态,请重新运行此命令几次。
kubectl get pods

查看容器日志:

kubectl logs dapi-envars-resourcefieldref

输出信息显示了所选择的环境变量的值:

1
1
33554432
67108864

接下来

在旧版 API 参考中阅读有关 Pod、容器和环境变量的信息:

4.7.5 - 通过文件将 Pod 信息呈现给容器

此页面描述 Pod 如何使用 downwardAPI 把自己的信息呈现给 Pod 中运行的容器。 downwardAPI 卷可以呈现 Pod 和容器的字段。

在 Kubernetes 中,有两种方式可以将 Pod 和容器字段呈现给运行中的容器:

这两种呈现 Pod 和容器字段的方式都称为 downward API

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

存储 Pod 字段

在这部分的练习中,你将创建一个包含一个容器的 Pod,并将 Pod 级别的字段作为文件投射到正在运行的容器中。 Pod 的清单如下:

apiVersion: v1
kind: Pod
metadata:
  name: kubernetes-downwardapi-volume-example
  labels:
    zone: us-est-coast
    cluster: test-cluster1
    rack: rack-22
  annotations:
    build: two
    builder: john-doe
spec:
  containers:
    - name: client-container
      image: registry.k8s.io/busybox
      command: ["sh", "-c"]
      args:
      - while true; do
          if [[ -e /etc/podinfo/labels ]]; then
            echo -en '\n\n'; cat /etc/podinfo/labels; fi;
          if [[ -e /etc/podinfo/annotations ]]; then
            echo -en '\n\n'; cat /etc/podinfo/annotations; fi;
          sleep 5;
        done;
      volumeMounts:
        - name: podinfo
          mountPath: /etc/podinfo
  volumes:
    - name: podinfo
      downwardAPI:
        items:
          - path: "labels"
            fieldRef:
              fieldPath: metadata.labels
          - path: "annotations"
            fieldRef:
              fieldPath: metadata.annotations

在 Pod 清单中,你可以看到 Pod 有一个 downwardAPI 类型的卷,并且挂载到容器中的 /etc/podinfo 目录。

查看 downwardAPI 下面的 items 数组。 数组的每个元素定义一个 downwardAPI 卷。 第一个元素指示 Pod 的 metadata.labels 字段的值保存在名为 labels 的文件中。 第二个元素指示 Pod 的 annotations 字段的值保存在名为 annotations 的文件中。

创建 Pod:

kubectl apply -f https://k8s.io/examples/pods/inject/dapi-volume.yaml

验证 Pod 中的容器运行正常:

kubectl get pods

查看容器的日志:

kubectl logs kubernetes-downwardapi-volume-example

输出显示 labels 文件和 annotations 文件的内容:

cluster="test-cluster1"
rack="rack-22"
zone="us-est-coast"

build="two"
builder="john-doe"

进入 Pod 中运行的容器,打开一个 Shell:

kubectl exec -it kubernetes-downwardapi-volume-example -- sh

在该 Shell中,查看 labels 文件:

/# cat /etc/podinfo/labels

输出显示 Pod 的所有标签都已写入 labels 文件:

cluster="test-cluster1"
rack="rack-22"
zone="us-est-coast"

同样,查看 annotations 文件:

/# cat /etc/podinfo/annotations

查看 /etc/podinfo 目录下的文件:

/# ls -laR /etc/podinfo

在输出中可以看到,labelsannotations 文件都在一个临时子目录中。 在这个例子中,这个临时子目录为 ..2982_06_02_21_47_53.299460680。 在 /etc/podinfo 目录中,..data 是指向该临时子目录的符号链接。 另外在 /etc/podinfo 目录中,labelsannotations 也是符号链接。

drwxr-xr-x  ... Feb 6 21:47 ..2982_06_02_21_47_53.299460680
lrwxrwxrwx  ... Feb 6 21:47 ..data -> ..2982_06_02_21_47_53.299460680
lrwxrwxrwx  ... Feb 6 21:47 annotations -> ..data/annotations
lrwxrwxrwx  ... Feb 6 21:47 labels -> ..data/labels

/etc/..2982_06_02_21_47_53.299460680:
total 8
-rw-r--r--  ... Feb  6 21:47 annotations
-rw-r--r--  ... Feb  6 21:47 labels

用符号链接可实现元数据的动态原子性刷新;更新将写入一个新的临时目录, 然后通过使用 rename(2) 完成 ..data 符号链接的原子性更新。

退出 Shell:

/# exit

存储容器字段

前面的练习中,你使用 downward API 使 Pod 级别的字段可以被 Pod 内正在运行的容器访问。 接下来这个练习,你将只传递由 Pod 定义的部分的字段到 Pod 内正在运行的容器中, 但这些字段取自特定容器而不是整个 Pod。 下面是一个同样只有一个容器的 Pod 的清单:

apiVersion: v1
kind: Pod
metadata:
  name: kubernetes-downwardapi-volume-example-2
spec:
  containers:
    - name: client-container
      image: registry.k8s.io/busybox:1.24
      command: ["sh", "-c"]
      args:
      - while true; do
          echo -en '\n';
          if [[ -e /etc/podinfo/cpu_limit ]]; then
            echo -en '\n'; cat /etc/podinfo/cpu_limit; fi;
          if [[ -e /etc/podinfo/cpu_request ]]; then
            echo -en '\n'; cat /etc/podinfo/cpu_request; fi;
          if [[ -e /etc/podinfo/mem_limit ]]; then
            echo -en '\n'; cat /etc/podinfo/mem_limit; fi;
          if [[ -e /etc/podinfo/mem_request ]]; then
            echo -en '\n'; cat /etc/podinfo/mem_request; fi;
          sleep 5;
        done;
      resources:
        requests:
          memory: "32Mi"
          cpu: "125m"
        limits:
          memory: "64Mi"
          cpu: "250m"
      volumeMounts:
        - name: podinfo
          mountPath: /etc/podinfo
  volumes:
    - name: podinfo
      downwardAPI:
        items:
          - path: "cpu_limit"
            resourceFieldRef:
              containerName: client-container
              resource: limits.cpu
              divisor: 1m
          - path: "cpu_request"
            resourceFieldRef:
              containerName: client-container
              resource: requests.cpu
              divisor: 1m
          - path: "mem_limit"
            resourceFieldRef:
              containerName: client-container
              resource: limits.memory
              divisor: 1Mi
          - path: "mem_request"
            resourceFieldRef:
              containerName: client-container
              resource: requests.memory
              divisor: 1Mi

在这个清单中,你可以看到 Pod 有一个 downwardAPI, 并且这个卷会挂载到 Pod 内的单个容器的 /etc/podinfo 目录。

查看 downwardAPI 下面的 items 数组。 数组的每个元素定义一个 downwardAPI 卷。

第一个元素指定在名为 client-container 的容器中, 以 1m 所指定格式的 limits.cpu 字段的值应推送到名为 cpu_limit 的文件中。 divisor 字段是可选的,默认值为 1。1 的除数表示 CPU 资源的核数或内存资源的字节数。

创建 Pod:

kubectl apply -f https://k8s.io/examples/pods/inject/dapi-volume-resources.yaml

打开一个 Shell,进入 Pod 中运行的容器:

kubectl exec -it kubernetes-downwardapi-volume-example-2 -- sh

在 Shell 中,查看 cpu_limit 文件:

# 在容器内的 Shell 中运行
cat /etc/podinfo/cpu_limit

你可以使用同样的命令查看 cpu_requestmem_limitmem_request 文件。

投射键名到指定路径并且指定文件权限

你可以将键名投射到指定路径并且指定每个文件的访问权限。 更多信息,请参阅 Secret

接下来

  • 参阅 Pod spec API 的定义,其中包括了容器(Pod 的一部分)的定义。
  • 参阅你可以使用 downward API 公开的可用字段列表。

阅读旧版的 API 参考中关于卷的内容:

  • 参阅 Volume API 定义,该 API 在 Pod 中定义通用卷以供容器访问。
  • 参阅 DownwardAPIVolumeSource API 定义,该 API 定义包含 Downward API 信息的卷。
  • 参阅 DownwardAPIVolumeFile API 定义,该 API 包含对对象或资源字段的引用,用于在 Downward API 卷中填充文件。
  • 参阅 ResourceFieldSelector API 定义,该 API 指定容器资源及其输出格式。

4.7.6 - 使用 Secret 安全地分发凭据

本文展示如何安全地将敏感数据(如密码和加密密钥)注入到 Pod 中。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

将 Secret 数据转换为 base-64 形式

假设用户想要有两条 Secret 数据:用户名 my-app 和密码 39528$vdg7Jb。 首先使用 Base64 编码将用户名和密码转化为 base-64 形式。 下面是一个使用常用的 base64 程序的示例:

echo -n 'my-app' | base64
echo -n '39528$vdg7Jb' | base64

结果显示 base-64 形式的用户名为 bXktYXBw, base-64 形式的密码为 Mzk1MjgkdmRnN0pi

创建 Secret

这里是一个配置文件,可以用来创建存有用户名和密码的 Secret:

apiVersion: v1
kind: Secret
metadata:
  name: test-secret
data:
  username: bXktYXBw
  password: Mzk1MjgkdmRnN0pi
  1. 创建 Secret:

    kubectl apply -f https://k8s.io/examples/pods/inject/secret.yaml
    
  1. 查看 Secret 相关信息:

    kubectl get secret test-secret
    

    输出:

    NAME          TYPE      DATA      AGE
    test-secret   Opaque    2         1m
    
  1. 查看 Secret 相关的更多详细信息:

    kubectl describe secret test-secret
    

    输出:

    Name:       test-secret
    Namespace:  default
    Labels:     <none>
    Annotations:    <none>
    
    Type:   Opaque
    
    Data
    ====
    password:   13 bytes
    username:   7 bytes
    

直接用 kubectl 创建 Secret

如果你希望略过 Base64 编码的步骤,你也可以使用 kubectl create secret 命令直接创建 Secret。例如:

kubectl create secret generic test-secret --from-literal='username=my-app' --from-literal='password=39528$vdg7Jb'

这是一种更为方便的方法。 前面展示的详细分解步骤有助于了解究竟发生了什么事情。

创建一个可以通过卷访问 Secret 数据的 Pod

这里是一个可以用来创建 Pod 的配置文件:

apiVersion: v1
kind: Pod
metadata:
  name: secret-test-pod
spec:
  containers:
    - name: test-container
      image: nginx
      volumeMounts:
        # name 必须与下面的卷名匹配
        - name: secret-volume
          mountPath: /etc/secret-volume
          readOnly: true
  # Secret 数据通过一个卷暴露给该 Pod 中的容器
  volumes:
    - name: secret-volume
      secret:
        secretName: test-secret
  1. 创建 Pod:

    kubectl apply -f https://k8s.io/examples/pods/inject/secret-pod.yaml
    
  2. 确认 Pod 正在运行:

    kubectl get pod secret-test-pod
    

    输出:

    NAME              READY     STATUS    RESTARTS   AGE
    secret-test-pod   1/1       Running   0          42m
    
  3. 获取一个 Shell 进入 Pod 中运行的容器:

    kubectl exec -i -t secret-test-pod -- /bin/bash
    
  4. Secret 数据通过挂载在 /etc/secret-volume 目录下的卷暴露在容器中。

    在 Shell 中,列举 /etc/secret-volume 目录下的文件:

    # 在容器中 Shell 运行下面命令
    ls /etc/secret-volume
    

    输出包含两个文件,每个对应一个 Secret 数据条目:

    password username
    
  5. 在 Shell 中,显示 usernamepassword 文件的内容:

    # 在容器中 Shell 运行下面命令
    echo "$( cat /etc/secret-volume/username )"
    echo "$( cat /etc/secret-volume/password )"
    

    输出为用户名和密码:

    my-app
    39528$vdg7Jb
    

修改你的镜像或命令行,使程序在 mountPath 目录下查找文件。 Secret data 映射中的每个键都成为该目录中的文件名。

映射 Secret 键到特定文件路径

你还可以控制卷内 Secret 键的映射路径。 使用 .spec.volumes[].secret.items 字段来改变每个键的目标路径。

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true
  volumes:
  - name: foo
    secret:
      secretName: mysecret
      items:
      - key: username
        path: my-group/my-username

当你部署此 Pod 时,会发生以下情况:

  • 来自 mysecret 的键 username 可以在路径 /etc/foo/my-group/my-username 下供容器使用,而不是路径 /etc/foo/username
  • 来自该 Secret 的键 password 没有映射到任何路径。

如果你使用 .spec.volumes[].secret.items 明确地列出键,请考虑以下事项:

  • 只有在 items 字段中指定的键才会被映射。
  • 要使用 Secret 中全部的键,那么全部的键都必须列在 items 字段中。
  • 所有列出的键必须存在于相应的 Secret 中。否则,该卷不被创建。

为 Secret 键设置 POSIX 权限

你可以为单个 Secret 键设置 POSIX 文件访问权限位。 如果不指定任何权限,默认情况下使用 0644。 你也可以为整个 Secret 卷设置默认的 POSIX 文件模式,需要时你可以重写单个键的权限。

例如,可以像这样指定默认模式:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
  volumes:
  - name: foo
    secret:
      secretName: mysecret
      defaultMode: 0400

Secret 被挂载在 /etc/foo 目录下;所有由 Secret 卷挂载创建的文件的访问许可都是 0400

使用 Secret 数据定义容器变量

在你的容器中,你可以以环境变量的方式使用 Secret 中的数据。

如果容器已经使用了在环境变量中的 Secret,除非容器重新启动,否则容器将无法感知到 Secret 的更新。 有第三方解决方案可以在 Secret 改变时触发容器重启。

使用来自 Secret 中的数据定义容器变量

  • 定义环境变量为 Secret 中的键值偶对:

    kubectl create secret generic backend-user --from-literal=backend-username='backend-admin'
    
  • 在 Pod 规约中,将 Secret 中定义的值 backend-username 赋给 SECRET_USERNAME 环境变量。

    apiVersion: v1
    kind: Pod
    metadata:
      name: env-single-secret
    spec:
      containers:
      - name: envars-test-container
        image: nginx
        env:
        - name: SECRET_USERNAME
          valueFrom:
            secretKeyRef:
              name: backend-user
              key: backend-username
    
  • 创建 Pod:

    kubectl create -f https://k8s.io/examples/pods/inject/pod-single-secret-env-variable.yaml
    
  • 在 Shell 中,显示容器环境变量 SECRET_USERNAME 的内容:

    kubectl exec -i -t env-single-secret -- /bin/sh -c 'echo $SECRET_USERNAME'
    

    输出类似于:

    backend-admin
    

使用来自多个 Secret 的数据定义环境变量

  • 和前面的例子一样,先创建 Secret:

    kubectl create secret generic backend-user --from-literal=backend-username='backend-admin'
    kubectl create secret generic db-user --from-literal=db-username='db-admin'
    
  • 在 Pod 规约中定义环境变量:

    apiVersion: v1
    kind: Pod
    metadata:
      name: envvars-multiple-secrets
    spec:
      containers:
      - name: envars-test-container
        image: nginx
        env:
        - name: BACKEND_USERNAME
          valueFrom:
            secretKeyRef:
              name: backend-user
              key: backend-username
        - name: DB_USERNAME
          valueFrom:
            secretKeyRef:
              name: db-user
              key: db-username
    
  • 创建 Pod:

    kubectl create -f https://k8s.io/examples/pods/inject/pod-multiple-secret-env-variable.yaml
    
  • 在你的 Shell 中,显示容器环境变量的内容:

    kubectl exec -i -t envvars-multiple-secrets -- /bin/sh -c 'env | grep _USERNAME'
    

    输出类似于:

    DB_USERNAME=db-admin
    BACKEND_USERNAME=backend-admin
    

将 Secret 中的所有键值偶对定义为环境变量

  • 创建包含多个键值偶对的 Secret:

    kubectl create secret generic test-secret --from-literal=username='my-app' --from-literal=password='39528$vdg7Jb'
    
  • 使用 envFrom 来将 Secret 中的所有数据定义为环境变量。 Secret 中的键名成为容器中的环境变量名:

    apiVersion: v1
    kind: Pod
    metadata:
      name: envfrom-secret
    spec:
      containers:
      - name: envars-test-container
        image: nginx
        envFrom:
        - secretRef:
            name: test-secret
    
  • 创建 Pod:

    kubectl create -f https://k8s.io/examples/pods/inject/pod-secret-envFrom.yaml
    
  • 在 Shell 中,显示环境变量 usernamepassword 的内容:

    kubectl exec -i -t envfrom-secret -- /bin/sh -c 'echo "username: $username\npassword: $password\n"'
    

    输出类似于:

    username: my-app
    password: 39528$vdg7Jb
    

示例:使用 Secret 为 Pod 提供生产环境或测试环境的凭据

此示例展示的是一个使用了包含生产环境凭据的 Secret 的 Pod 和一个使用了包含测试环境凭据的 Secret 的 Pod。

  1. 创建用于生产环境凭据的 Secret:

    kubectl create secret generic prod-db-secret --from-literal=username=produser --from-literal=password=Y4nys7f11
    

    输出类似于:

    secret "prod-db-secret" created
    
  1. 为测试环境凭据创建 Secret。

    kubectl create secret generic test-db-secret --from-literal=username=testuser --from-literal=password=iluvtests
    

    输出类似于:

    secret "test-db-secret" created
    
  1. 创建 Pod 清单:

    cat <<EOF > pod.yaml
    apiVersion: v1
    kind: List
    items:
    - kind: Pod
      apiVersion: v1
      metadata:
        name: prod-db-client-pod
        labels:
          name: prod-db-client
      spec:
        volumes:
        - name: secret-volume
          secret:
            secretName: prod-db-secret
        containers:
        - name: db-client-container
          image: myClientImage
          volumeMounts:
          - name: secret-volume
            readOnly: true
            mountPath: "/etc/secret-volume"
    - kind: Pod
      apiVersion: v1
      metadata:
        name: test-db-client-pod
        labels:
          name: test-db-client
      spec:
        volumes:
        - name: secret-volume
          secret:
            secretName: test-db-secret
        containers:
        - name: db-client-container
          image: myClientImage
          volumeMounts:
          - name: secret-volume
            readOnly: true
            mountPath: "/etc/secret-volume"
    EOF
    
  1. 通过运行以下命令将所有这些对象应用到 API 服务器:

    kubectl create -f pod.yaml
    

两个容器的文件系统中都将存在以下文件,其中包含每个容器环境的值:

/etc/secret-volume/username
/etc/secret-volume/password

你可以通过使用两个服务账号进一步简化基础 Pod 规约:

  1. 带有 prod-db-secretprod-user
  2. 带有 test-db-secrettest-user

Pod 规约精简为:

apiVersion: v1
kind: Pod
metadata:
  name: prod-db-client-pod
  labels:
    name: prod-db-client
spec:
  serviceAccount: prod-db-client
  containers:
  - name: db-client-container
    image: myClientImage

参考

接下来

4.8 - 运行应用

运行和管理无状态和有状态的应用。

4.8.1 - 使用 Deployment 运行一个无状态应用

本文介绍如何通过 Kubernetes Deployment 对象去运行一个应用。

教程目标

  • 创建一个 nginx Deployment。
  • 使用 kubectl 列举该 Deployment 的相关信息。
  • 更新该 Deployment。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你的 Kubernetes 服务器版本必须不低于版本 v1.9. 要获知版本信息,请输入 kubectl version.

创建并了解一个 nginx Deployment

你可以通过创建一个 Kubernetes Deployment 对象来运行一个应用, 且你可以在一个 YAML 文件中描述 Deployment。例如,下面这个 YAML 文件描述了一个运行 nginx:1.14.2 Docker 镜像的 Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2 # 告知 Deployment 运行 2 个与该模板匹配的 Pod
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80
  1. 通过 YAML 文件创建一个 Deployment:

    kubectl apply -f https://k8s.io/examples/application/deployment.yaml
    
  1. 显示该 Deployment 的相关信息:

    kubectl describe deployment nginx-deployment
    

    输出类似于这样:

    Name:     nginx-deployment
    Namespace:    default
    CreationTimestamp:  Tue, 30 Aug 2016 18:11:37 -0700
    Labels:     app=nginx
    Annotations:    deployment.kubernetes.io/revision=1
    Selector:   app=nginx
    Replicas:   2 desired | 2 updated | 2 total | 2 available | 0 unavailable
    StrategyType:   RollingUpdate
    MinReadySeconds:  0
    RollingUpdateStrategy:  1 max unavailable, 1 max surge
    Pod Template:
      Labels:       app=nginx
      Containers:
        nginx:
        Image:              nginx:1.14.2
        Port:               80/TCP
        Environment:        <none>
        Mounts:             <none>
      Volumes:              <none>
    Conditions:
      Type          Status  Reason
      ----          ------  ------
      Available     True    MinimumReplicasAvailable
      Progressing   True    NewReplicaSetAvailable
    OldReplicaSets:   <none>
    NewReplicaSet:    nginx-deployment-1771418926 (2/2 replicas created)
    No events.
    
  1. 列出该 Deployment 创建的 Pod:

    kubectl get pods -l app=nginx
    

    输出类似于这样:

    NAME                                READY     STATUS    RESTARTS   AGE
    nginx-deployment-1771418926-7o5ns   1/1       Running   0          16h
    nginx-deployment-1771418926-r18az   1/1       Running   0          16h
    
  1. 展示某一个 Pod 信息:

    kubectl describe pod <pod-name>
    

    这里的 <pod-name> 是某一 Pod 的名称。

更新 Deployment

你可以通过应用一个新的 YAML 文件来更新 Deployment。下面的 YAML 文件指定该 Deployment 镜像更新为 nginx 1.16.1。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.16.1 # 将 nginx 版本从 1.14.2 更新为 1.16.1
        ports:
        - containerPort: 80
  1. 应用新的 YAML:

    kubectl apply -f https://k8s.io/examples/application/deployment-update.yaml
    
  1. 查看该 Deployment 以新的名称创建 Pod 同时删除旧的 Pod:

    kubectl get pods -l app=nginx
    

通过增加副本数来扩缩应用

你可以通过应用新的 YAML 文件来增加 Deployment 中 Pod 的数量。 下面的 YAML 文件将 replicas 设置为 4,指定该 Deployment 应有 4 个 Pod:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 4 # 将副本数从 2 更新为 4
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.16.1
        ports:
        - containerPort: 80
  1. 应用新的 YAML 文件:

    kubectl apply -f https://k8s.io/examples/application/deployment-scale.yaml
    
  1. 验证该 Deployment 有 4 个 Pod:

    kubectl get pods -l app=nginx
    

    输出的结果类似于:

    NAME                               READY     STATUS    RESTARTS   AGE
    nginx-deployment-148880595-4zdqq   1/1       Running   0          25s
    nginx-deployment-148880595-6zgi1   1/1       Running   0          25s
    nginx-deployment-148880595-fxcez   1/1       Running   0          2m
    nginx-deployment-148880595-rwovn   1/1       Running   0          2m
    

删除 Deployment

基于名称删除 Deployment:

kubectl delete deployment nginx-deployment

ReplicationController —— 旧的方式

创建一个多副本应用首选方法是使用 Deployment,该 Deployment 内部将轮流使用 ReplicaSet。 在 Deployment 和 ReplicaSet 被引入到 Kubernetes 之前,多副本应用通过 ReplicationController 来配置。

接下来

4.8.2 - 运行一个单实例有状态应用

本文介绍在 Kubernetes 中如何使用 PersistentVolume 和 Deployment 运行一个单实例有状态应用。 该示例应用是 MySQL。

教程目标

  • 在你的环境中创建一个引用磁盘的 PersistentVolume。
  • 创建一个 MySQL Deployment。
  • 在集群内以一个已知的 DNS 名称将 MySQL 暴露给其他 Pod。

准备开始

部署 MySQL

你可以通过创建一个 Kubernetes Deployment 并使用 PersistentVolumeClaim 将其连接到 某已有的 PersistentVolume 来运行一个有状态的应用。 例如,这里的 YAML 描述的是一个运行 MySQL 的 Deployment,其中引用了 PersistentVolumeClaim。 文件为 /var/lib/mysql 定义了卷挂载,并创建了一个 PersistentVolumeClaim,寻找一个 20G 大小的卷。 该申领可以通过现有的满足需求的卷来满足,也可以通过动态供应卷的机制来满足。

注意:在配置的 YAML 文件中定义密码的做法是不安全的。具体安全解决方案请参考 Kubernetes Secrets

apiVersion: v1
kind: Service
metadata:
  name: mysql
spec:
  ports:
  - port: 3306
  selector:
    app: mysql
  clusterIP: None
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - image: mysql:5.6
        name: mysql
        env:
          # 在实际中使用 secret
        - name: MYSQL_ROOT_PASSWORD
          value: password
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: mysql-persistent-storage
          mountPath: /var/lib/mysql
      volumes:
      - name: mysql-persistent-storage
        persistentVolumeClaim:
          claimName: mysql-pv-claim
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mysql-pv-volume
  labels:
    type: local
spec:
  storageClassName: manual
  capacity:
    storage: 20Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/mnt/data"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pv-claim
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
  1. 部署 YAML 文件中定义的 PV 和 PVC:

    kubectl apply -f https://k8s.io/examples/application/mysql/mysql-pv.yaml
    
  1. 部署 YAML 文件中定义的 Deployment:

    kubectl apply -f https://k8s.io/examples/application/mysql/mysql-deployment.yaml
    
  1. 展示 Deployment 相关信息:

    kubectl describe deployment mysql
    

    输出类似于:

    Name:                 mysql
    Namespace:            default
    CreationTimestamp:    Tue, 01 Nov 2016 11:18:45 -0700
    Labels:               app=mysql
    Annotations:          deployment.kubernetes.io/revision=1
    Selector:             app=mysql
    Replicas:             1 desired | 1 updated | 1 total | 0 available | 1 unavailable
    StrategyType:         Recreate
    MinReadySeconds:      0
    Pod Template:
      Labels:       app=mysql
      Containers:
       mysql:
        Image:      mysql:5.6
        Port:       3306/TCP
        Environment:
          MYSQL_ROOT_PASSWORD:      password
        Mounts:
          /var/lib/mysql from mysql-persistent-storage (rw)
      Volumes:
       mysql-persistent-storage:
        Type:       PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
        ClaimName:  mysql-pv-claim
        ReadOnly:   false
    Conditions:
      Type          Status  Reason
      ----          ------  ------
      Available     False   MinimumReplicasUnavailable
      Progressing   True    ReplicaSetUpdated
    OldReplicaSets:       <none>
    NewReplicaSet:        mysql-63082529 (1/1 replicas created)
    Events:
      FirstSeen    LastSeen    Count    From                SubobjectPath    Type        Reason            Message
      ---------    --------    -----    ----                -------------    --------    ------            -------
      33s          33s         1        {deployment-controller }             Normal      ScalingReplicaSet Scaled up replica set mysql-63082529 to 1
    
  1. 列举出 Deployment 创建的 Pod:

    kubectl get pods -l app=mysql
    

    输出类似于:

    NAME                   READY     STATUS    RESTARTS   AGE
    mysql-63082529-2z3ki   1/1       Running   0          3m
    
  1. 查看 PersistentVolumeClaim:

    kubectl describe pvc mysql-pv-claim
    

    输出类似于:

    Name:         mysql-pv-claim
    Namespace:    default
    StorageClass:
    Status:       Bound
    Volume:       mysql-pv-volume
    Labels:       <none>
    Annotations:    pv.kubernetes.io/bind-completed=yes
                    pv.kubernetes.io/bound-by-controller=yes
    Capacity:     20Gi
    Access Modes: RWO
    Events:       <none>
    

访问 MySQL 实例

前面 YAML 文件中创建了一个允许集群内其他 Pod 访问的数据库 Service。该 Service 中选项 clusterIP: None 让 Service 的 DNS 名称直接解析为 Pod 的 IP 地址。 当在一个 Service 下只有一个 Pod 并且不打算增加 Pod 的数量这是最好的。

运行 MySQL 客户端以连接到服务器:

kubectl run -it --rm --image=mysql:5.6 --restart=Never mysql-client -- mysql -h mysql -ppassword

此命令在集群内创建一个新的 Pod 并运行 MySQL 客户端,并通过 Service 连接到服务器。 如果连接成功,你就知道有状态的 MySQL 数据库正处于运行状态。

Waiting for pod default/mysql-client-274442439-zyp6i to be running, status is Pending, pod ready: false
If you don't see a command prompt, try pressing enter.

mysql>

更新

Deployment 中镜像或其他部分同往常一样可以通过 kubectl apply 命令更新。 以下是特定于有状态应用的一些注意事项:

  • 不要对应用进行规模扩缩。这里的设置仅适用于单实例应用。下层的 PersistentVolume 仅只能挂载到一个 Pod 上。对于集群级有状态应用,请参考 StatefulSet 文档
  • 在 Deployment 的 YAML 文件中使用 strategy: type: Recreate。 该选项指示 Kubernetes 使用滚动升级。滚动升级无法工作,因为这里一次不能运行多个 Pod。在使用更新的配置文件创建新的 Pod 前,Recreate 策略将保证先停止第一个 Pod。

删除 Deployment

通过名称删除部署的对象:

kubectl delete deployment,svc mysql
kubectl delete pvc mysql-pv-claim
kubectl delete pv mysql-pv-volume

如果通过手动的方式供应 PersistentVolume,那么也需要手动删除它以释放下层资源。 如果是用动态供应方式创建的 PersistentVolume,在删除 PersistentVolumeClaim 后 PersistentVolume 将被自动删除。 一些存储服务(比如 EBS 和 PD)也会在 PersistentVolume 被删除时自动回收下层资源。

接下来

4.8.3 - 运行一个有状态的应用程序

本页展示如何使用 StatefulSet 控制器运行一个有状态的应用程序。此例是多副本的 MySQL 数据库。 示例应用的拓扑结构有一个主服务器和多个副本,使用异步的基于行(Row-Based) 的数据复制。

准备开始

  • 本教程假定你熟悉 PersistentVolumesStatefulSet, 以及其他核心概念,例如 PodServiceConfigMap
  • 熟悉 MySQL 会有所帮助,但是本教程旨在介绍对其他系统应该有用的常规模式。
  • 你正在使用默认命名空间或不包含任何冲突对象的另一个命名空间。

教程目标

  • 使用 StatefulSet 部署多副本 MySQL 拓扑架构。
  • 发送 MySQL 客户端请求。
  • 观察对宕机的抵抗力。
  • 扩缩 StatefulSet 的规模。

部署 MySQL

MySQL 示例部署包含一个 ConfigMap、两个 Service 与一个 StatefulSet。

创建一个 ConfigMap

使用以下的 YAML 配置文件创建 ConfigMap :

apiVersion: v1
kind: ConfigMap
metadata:
  name: mysql
  labels:
    app: mysql
    app.kubernetes.io/name: mysql
data:
  primary.cnf: |
    # 仅在主服务器上应用此配置
    [mysqld]
    log-bin    
  replica.cnf: |
    # 仅在副本服务器上应用此配置
    [mysqld]
    super-read-only    

kubectl apply -f https://k8s.io/examples/application/mysql/mysql-configmap.yaml

这个 ConfigMap 提供 my.cnf 覆盖设置,使你可以独立控制 MySQL 主服务器和副本服务器的配置。 在这里,你希望主服务器能够将复制日志提供给副本服务器, 并且希望副本服务器拒绝任何不是通过复制进行的写操作。

ConfigMap 本身没有什么特别之处,因而也不会出现不同部分应用于不同的 Pod 的情况。 每个 Pod 都会在初始化时基于 StatefulSet 控制器提供的信息决定要查看的部分。

创建 Service

使用以下 YAML 配置文件创建服务:

# 为 StatefulSet 成员提供稳定的 DNS 表项的无头服务(Headless Service)
apiVersion: v1
kind: Service
metadata:
  name: mysql
  labels:
    app: mysql
    app.kubernetes.io/name: mysql
spec:
  ports:
  - name: mysql
    port: 3306
  clusterIP: None
  selector:
    app: mysql
---
# 用于连接到任一 MySQL 实例执行读操作的客户端服务
# 对于写操作,你必须连接到主服务器:mysql-0.mysql
apiVersion: v1
kind: Service
metadata:
  name: mysql-read
  labels:
    app: mysql
    app.kubernetes.io/name: mysql
    readonly: "true"
spec:
  ports:
  - name: mysql
    port: 3306
  selector:
    app: mysql
kubectl apply -f https://k8s.io/examples/application/mysql/mysql-services.yaml

这个无头 Service 给 StatefulSet 控制器 为集合中每个 Pod 创建的 DNS 条目提供了一个宿主。 因为无头服务名为 mysql,所以可以通过在同一 Kubernetes 集群和命名空间中的任何其他 Pod 内解析 <Pod 名称>.mysql 来访问 Pod。

客户端 Service 称为 mysql-read,是一种常规 Service,具有其自己的集群 IP。 该集群 IP 在报告就绪的所有 MySQL Pod 之间分配连接。 可能的端点集合包括 MySQL 主节点和所有副本节点。

请注意,只有读查询才能使用负载平衡的客户端 Service。 因为只有一个 MySQL 主服务器,所以客户端应直接连接到 MySQL 主服务器 Pod (通过其在无头 Service 中的 DNS 条目)以执行写入操作。

创建 StatefulSet

最后,使用以下 YAML 配置文件创建 StatefulSet:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql
      app.kubernetes.io/name: mysql
  serviceName: mysql
  replicas: 3
  template:
    metadata:
      labels:
        app: mysql
        app.kubernetes.io/name: mysql
    spec:
      initContainers:
      - name: init-mysql
        image: mysql:5.7
        command:
        - bash
        - "-c"
        - |
          set -ex
          # 基于 Pod 序号生成 MySQL 服务器的 ID。
          [[ $HOSTNAME =~ -([0-9]+)$ ]] || exit 1
          ordinal=${BASH_REMATCH[1]}
          echo [mysqld] > /mnt/conf.d/server-id.cnf
          # 添加偏移量以避免使用 server-id=0 这一保留值。
          echo server-id=$((100 + $ordinal)) >> /mnt/conf.d/server-id.cnf
          # 将合适的 conf.d 文件从 config-map 复制到 emptyDir。
          if [[ $ordinal -eq 0 ]]; then
            cp /mnt/config-map/primary.cnf /mnt/conf.d/
          else
            cp /mnt/config-map/replica.cnf /mnt/conf.d/
          fi          
        volumeMounts:
        - name: conf
          mountPath: /mnt/conf.d
        - name: config-map
          mountPath: /mnt/config-map
      - name: clone-mysql
        image: gcr.io/google-samples/xtrabackup:1.0
        command:
        - bash
        - "-c"
        - |
          set -ex
          # 如果已有数据,则跳过克隆。
          [[ -d /var/lib/mysql/mysql ]] && exit 0
          # 跳过主实例(序号索引 0)的克隆。
          [[ `hostname` =~ -([0-9]+)$ ]] || exit 1
          ordinal=${BASH_REMATCH[1]}
          [[ $ordinal -eq 0 ]] && exit 0
          # 从原来的对等节点克隆数据。
          ncat --recv-only mysql-$(($ordinal-1)).mysql 3307 | xbstream -x -C /var/lib/mysql
          # 准备备份。
          xtrabackup --prepare --target-dir=/var/lib/mysql          
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
          subPath: mysql
        - name: conf
          mountPath: /etc/mysql/conf.d
      containers:
      - name: mysql
        image: mysql:5.7
        env:
        - name: MYSQL_ALLOW_EMPTY_PASSWORD
          value: "1"
        ports:
        - name: mysql
          containerPort: 3306
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
          subPath: mysql
        - name: conf
          mountPath: /etc/mysql/conf.d
        resources:
          requests:
            cpu: 500m
            memory: 1Gi
        livenessProbe:
          exec:
            command: ["mysqladmin", "ping"]
          initialDelaySeconds: 30
          periodSeconds: 10
          timeoutSeconds: 5
        readinessProbe:
          exec:
            # 检查我们是否可以通过 TCP 执行查询(skip-networking 是关闭的)。
            command: ["mysql", "-h", "127.0.0.1", "-e", "SELECT 1"]
          initialDelaySeconds: 5
          periodSeconds: 2
          timeoutSeconds: 1
      - name: xtrabackup
        image: gcr.io/google-samples/xtrabackup:1.0
        ports:
        - name: xtrabackup
          containerPort: 3307
        command:
        - bash
        - "-c"
        - |
          set -ex
          cd /var/lib/mysql

          # 确定克隆数据的 binlog 位置(如果有的话)。
          if [[ -f xtrabackup_slave_info && "x$(<xtrabackup_slave_info)" != "x" ]]; then
            # XtraBackup 已经生成了部分的 “CHANGE MASTER TO” 查询
            # 因为我们从一个现有副本进行克隆。(需要删除末尾的分号!)
            cat xtrabackup_slave_info | sed -E 's/;$//g' > change_master_to.sql.in
            # 在这里要忽略 xtrabackup_binlog_info (它是没用的)。
            rm -f xtrabackup_slave_info xtrabackup_binlog_info
          elif [[ -f xtrabackup_binlog_info ]]; then
            # 我们直接从主实例进行克隆。解析 binlog 位置。
            [[ `cat xtrabackup_binlog_info` =~ ^(.*?)[[:space:]]+(.*?)$ ]] || exit 1
            rm -f xtrabackup_binlog_info xtrabackup_slave_info
            echo "CHANGE MASTER TO MASTER_LOG_FILE='${BASH_REMATCH[1]}',\
                  MASTER_LOG_POS=${BASH_REMATCH[2]}" > change_master_to.sql.in
          fi

          # 检查我们是否需要通过启动复制来完成克隆。
          if [[ -f change_master_to.sql.in ]]; then
            echo "Waiting for mysqld to be ready (accepting connections)"
            until mysql -h 127.0.0.1 -e "SELECT 1"; do sleep 1; done

            echo "Initializing replication from clone position"
            mysql -h 127.0.0.1 \
                  -e "$(<change_master_to.sql.in), \
                          MASTER_HOST='mysql-0.mysql', \
                          MASTER_USER='root', \
                          MASTER_PASSWORD='', \
                          MASTER_CONNECT_RETRY=10; \
                        START SLAVE;" || exit 1
            # 如果容器重新启动,最多尝试一次。
            mv change_master_to.sql.in change_master_to.sql.orig
          fi

          # 当对等点请求时,启动服务器发送备份。
          exec ncat --listen --keep-open --send-only --max-conns=1 3307 -c \
            "xtrabackup --backup --slave-info --stream=xbstream --host=127.0.0.1 --user=root"          
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
          subPath: mysql
        - name: conf
          mountPath: /etc/mysql/conf.d
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
      volumes:
      - name: conf
        emptyDir: {}
      - name: config-map
        configMap:
          name: mysql
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 10Gi
kubectl apply -f https://k8s.io/examples/application/mysql/mysql-statefulset.yaml

你可以通过运行以下命令查看启动进度:

kubectl get pods -l app=mysql --watch

一段时间后,你应该看到所有 3 个 Pod 进入 Running 状态:

NAME      READY     STATUS    RESTARTS   AGE
mysql-0   2/2       Running   0          2m
mysql-1   2/2       Running   0          1m
mysql-2   2/2       Running   0          1m

输入 Ctrl+C 结束监视操作。

此清单使用多种技术来管理作为 StatefulSet 的一部分的有状态 Pod。 下一节重点介绍其中的一些技巧,以解释 StatefulSet 创建 Pod 时发生的状况。

了解有状态的 Pod 初始化

StatefulSet 控制器按序数索引顺序地每次启动一个 Pod。 它一直等到每个 Pod 报告就绪才再启动下一个 Pod。

此外,控制器为每个 Pod 分配一个唯一、稳定的名称,形如 <statefulset 名称>-<序数索引>, 其结果是 Pod 名为 mysql-0mysql-1mysql-2

上述 StatefulSet 清单中的 Pod 模板利用这些属性来执行 MySQL 副本的有序启动。

生成配置

在启动 Pod 规约中的任何容器之前,Pod 首先按顺序运行所有的 Init 容器

第一个名为 init-mysql 的 Init 容器根据序号索引生成特殊的 MySQL 配置文件。

该脚本通过从 Pod 名称的末尾提取索引来确定自己的序号索引,而 Pod 名称由 hostname 命令返回。 然后将序数(带有数字偏移量以避免保留值)保存到 MySQL conf.d 目录中的文件 server-id.cnf。 这一操作将 StatefulSet 所提供的唯一、稳定的标识转换为 MySQL 服务器 ID, 而这些 ID 也是需要唯一性、稳定性保证的。

通过将内容复制到 conf.d 中,init-mysql 容器中的脚本也可以应用 ConfigMap 中的 primary.cnfreplica.cnf。 由于示例部署结构由单个 MySQL 主节点和任意数量的副本节点组成, 因此脚本仅将序数 0 指定为主节点,而将其他所有节点指定为副本节点。

与 StatefulSet 控制器的部署顺序保证相结合, 可以确保 MySQL 主服务器在创建副本服务器之前已准备就绪,以便它们可以开始复制。

克隆现有数据

通常,当新 Pod 作为副本节点加入集合时,必须假定 MySQL 主节点可能已经有数据。 还必须假设复制日志可能不会一直追溯到时间的开始。

这些保守的假设是允许正在运行的 StatefulSet 随时间扩大和缩小而不是固定在其初始大小的关键。

第二个名为 clone-mysql 的 Init 容器,第一次在带有空 PersistentVolume 的副本 Pod 上启动时,会在从属 Pod 上执行克隆操作。 这意味着它将从另一个运行中的 Pod 复制所有现有数据,使此其本地状态足够一致, 从而可以开始从主服务器复制。

MySQL 本身不提供执行此操作的机制,因此本示例使用了一种流行的开源工具 Percona XtraBackup。 在克隆期间,源 MySQL 服务器性能可能会受到影响。 为了最大程度地减少对 MySQL 主服务器的影响,该脚本指示每个 Pod 从序号较低的 Pod 中克隆。 可以这样做的原因是 StatefulSet 控制器始终确保在启动 Pod N+1 之前 Pod N 已准备就绪。

开始复制

Init 容器成功完成后,应用容器将运行。MySQL Pod 由运行实际 mysqld 服务的 mysql 容器和充当辅助工具的 xtrabackup 容器组成。

xtrabackup sidecar 容器查看克隆的数据文件,并确定是否有必要在副本服务器上初始化 MySQL 复制。 如果是这样,它将等待 mysqld 准备就绪,然后使用从 XtraBackup 克隆文件中提取的复制参数执行 CHANGE MASTER TOSTART SLAVE 命令。

一旦副本服务器开始复制后,它会记住其 MySQL 主服务器,并且如果服务器重新启动或连接中断也会自动重新连接。 另外,因为副本服务器会以其稳定的 DNS 名称查找主服务器(mysql-0.mysql), 即使由于重新调度而获得新的 Pod IP,它们也会自动找到主服务器。

最后,开始复制后,xtrabackup 容器监听来自其他 Pod 的连接,处理其数据克隆请求。 如果 StatefulSet 扩大规模,或者下一个 Pod 失去其 PersistentVolumeClaim 并需要重新克隆, 则此服务器将无限期保持运行。

发送客户端请求

你可以通过运行带有 mysql:5.7 镜像的临时容器并运行 mysql 客户端二进制文件, 将测试查询发送到 MySQL 主服务器(主机名 mysql-0.mysql)。

kubectl run mysql-client --image=mysql:5.7 -i --rm --restart=Never --\
  mysql -h mysql-0.mysql <<EOF
CREATE DATABASE test;
CREATE TABLE test.messages (message VARCHAR(250));
INSERT INTO test.messages VALUES ('hello');
EOF

使用主机名 mysql-read 将测试查询发送到任何报告为就绪的服务器:

kubectl run mysql-client --image=mysql:5.7 -i -t --rm --restart=Never --\
  mysql -h mysql-read -e "SELECT * FROM test.messages"

你应该获得如下输出:

Waiting for pod default/mysql-client to be running, status is Pending, pod ready: false
+---------+
| message |
+---------+
| hello   |
+---------+
pod "mysql-client" deleted

为了演示 mysql-read 服务在服务器之间分配连接,你可以在循环中运行 SELECT @@server_id

kubectl run mysql-client-loop --image=mysql:5.7 -i -t --rm --restart=Never --\
  bash -ic "while sleep 1; do mysql -h mysql-read -e 'SELECT @@server_id,NOW()'; done"

你应该看到报告的 @@server_id 发生随机变化,因为每次尝试连接时都可能选择了不同的端点:

+-------------+---------------------+
| @@server_id | NOW()               |
+-------------+---------------------+
|         100 | 2006-01-02 15:04:05 |
+-------------+---------------------+
+-------------+---------------------+
| @@server_id | NOW()               |
+-------------+---------------------+
|         102 | 2006-01-02 15:04:06 |
+-------------+---------------------+
+-------------+---------------------+
| @@server_id | NOW()               |
+-------------+---------------------+
|         101 | 2006-01-02 15:04:07 |
+-------------+---------------------+

要停止循环时可以按 Ctrl+C ,但是让它在另一个窗口中运行非常有用, 这样你就可以看到以下步骤的效果。

模拟 Pod 和 Node 失效

为了证明从副本节点缓存而不是单个服务器读取数据的可用性提高,请在使 Pod 退出 Ready 状态时,保持上述 SELECT @@server_id 循环一直运行。

破坏就绪态探测

mysql 容器的就绪态探测 运行命令 mysql -h 127.0.0.1 -e 'SELECT 1',以确保服务器已启动并能够执行查询。

迫使就绪态探测失败的一种方法就是中止该命令:

kubectl exec mysql-2 -c mysql -- mv /usr/bin/mysql /usr/bin/mysql.off

此命令会进入 Pod mysql-2 的实际容器文件系统,重命名 mysql 命令,导致就绪态探测无法找到它。 几秒钟后, Pod 会报告其中一个容器未就绪。你可以通过运行以下命令进行检查:

kubectl get pod mysql-2

READY 列中查找 1/2

NAME      READY     STATUS    RESTARTS   AGE
mysql-2   1/2       Running   0          3m

此时,你应该会看到 SELECT @@server_id 循环继续运行,尽管它不再报告 102。 回想一下,init-mysql 脚本将 server-id 定义为 100 + $ordinal, 因此服务器 ID 102 对应于 Pod mysql-2

现在修复 Pod,几秒钟后它应该重新出现在循环输出中:

kubectl exec mysql-2 -c mysql -- mv /usr/bin/mysql.off /usr/bin/mysql

删除 Pod

如果删除了 Pod,则 StatefulSet 还会重新创建 Pod,类似于 ReplicaSet 对无状态 Pod 所做的操作。

kubectl delete pod mysql-2

StatefulSet 控制器注意到不再存在 mysql-2 Pod,于是创建一个具有相同名称并链接到相同 PersistentVolumeClaim 的新 Pod。 你应该看到服务器 ID 102 从循环输出中消失了一段时间,然后又自行出现。

腾空节点

如果你的 Kubernetes 集群具有多个节点,则可以通过发出以下 drain 命令来模拟节点停机(就好像节点在被升级)。

首先确定 MySQL Pod 之一在哪个节点上:

kubectl get pod mysql-2 -o wide

节点名称应显示在最后一列中:

NAME      READY     STATUS    RESTARTS   AGE       IP            NODE
mysql-2   2/2       Running   0          15m       10.244.5.27   kubernetes-node-9l2t

接下来,通过运行以下命令腾空节点,该命令将其保护起来,以使新的 Pod 不能调度到该节点, 然后逐出所有现有的 Pod。将 <节点名称> 替换为在上一步中找到的节点名称。

# 关于对其他负载的影响,参见前文建议
kubectl drain <节点名称> --force --delete-local-data --ignore-daemonsets

现在,你可以监视 Pod 被重新调度到其他节点上:

kubectl get pod mysql-2 -o wide --watch

它看起来应该像这样:

NAME      READY   STATUS          RESTARTS   AGE       IP            NODE
mysql-2   2/2     Terminating     0          15m       10.244.1.56   kubernetes-node-9l2t
[...]
mysql-2   0/2     Pending         0          0s        <none>        kubernetes-node-fjlm
mysql-2   0/2     Init:0/2        0          0s        <none>        kubernetes-node-fjlm
mysql-2   0/2     Init:1/2        0          20s       10.244.5.32   kubernetes-node-fjlm
mysql-2   0/2     PodInitializing 0          21s       10.244.5.32   kubernetes-node-fjlm
mysql-2   1/2     Running         0          22s       10.244.5.32   kubernetes-node-fjlm
mysql-2   2/2     Running         0          30s       10.244.5.32   kubernetes-node-fjlm

再次,你应该看到服务器 ID 102SELECT @@server_id 循环输出中消失一段时间,然后再次出现。

现在去掉节点保护(Uncordon),使其恢复为正常模式:

kubectl uncordon <节点名称>

扩展副本节点数量

使用 MySQL 复制时,你可以通过添加副本节点来扩展读取查询的能力。 对于 StatefulSet,你可以使用单个命令实现此目的:

kubectl scale statefulset mysql --replicas=5

运行下面的命令,监视新的 Pod 启动:

kubectl get pods -l app=mysql --watch

一旦 Pod 启动,你应该看到服务器 ID 103104 开始出现在 SELECT @@server_id 循环输出中。

你还可以验证这些新服务器在存在之前已添加了数据:

kubectl run mysql-client --image=mysql:5.7 -i -t --rm --restart=Never --\
  mysql -h mysql-3.mysql -e "SELECT * FROM test.messages"
Waiting for pod default/mysql-client to be running, status is Pending, pod ready: false
+---------+
| message |
+---------+
| hello   |
+---------+
pod "mysql-client" deleted

向下缩容操作也是很平滑的:

kubectl scale statefulset mysql --replicas=3

你可以通过运行以下命令查看此效果:

kubectl get pvc -l app=mysql

这表明,尽管将 StatefulSet 缩小为 3,所有 5 个 PVC 仍然存在:

NAME           STATUS    VOLUME                                     CAPACITY   ACCESSMODES   AGE
data-mysql-0   Bound     pvc-8acbf5dc-b103-11e6-93fa-42010a800002   10Gi       RWO           20m
data-mysql-1   Bound     pvc-8ad39820-b103-11e6-93fa-42010a800002   10Gi       RWO           20m
data-mysql-2   Bound     pvc-8ad69a6d-b103-11e6-93fa-42010a800002   10Gi       RWO           20m
data-mysql-3   Bound     pvc-50043c45-b1c5-11e6-93fa-42010a800002   10Gi       RWO           2m
data-mysql-4   Bound     pvc-500a9957-b1c5-11e6-93fa-42010a800002   10Gi       RWO           2m

如果你不打算重复使用多余的 PVC,则可以删除它们:

kubectl delete pvc data-mysql-3
kubectl delete pvc data-mysql-4

清理现场

  1. 通过在终端上按 Ctrl+C 取消 SELECT @@server_id 循环,或从另一个终端运行以下命令:

    kubectl delete pod mysql-client-loop --now
    
  1. 删除 StatefulSet。这也会开始终止 Pod。

    kubectl delete statefulset mysql
    
  1. 验证 Pod 消失。它们可能需要一些时间才能完成终止。

    kubectl get pods -l app=mysql
    

    当上述命令返回如下内容时,你就知道 Pod 已终止:

    No resources found.
    
  1. 删除 ConfigMap、Service 和 PersistentVolumeClaim。

    kubectl delete configmap,service,pvc -l app=mysql
    
  1. 如果你手动制备 PersistentVolume,则还需要手动删除它们,并释放下层资源。 如果你使用了动态制备器,当得知你删除 PersistentVolumeClaim 时,它将自动删除 PersistentVolume。 一些动态制备器(例如用于 EBS 和 PD 的制备器)也会在删除 PersistentVolume 时释放下层资源。

接下来

4.8.4 - 扩缩 StatefulSet

本文介绍如何扩缩 StatefulSet。StatefulSet 的扩缩指的是增加或者减少副本个数。

准备开始

  • StatefulSets 仅适用于 Kubernetes 1.5 及以上版本。 要查看你的 Kubernetes 版本,运行 kubectl version

  • 不是所有 Stateful 应用都能很好地执行扩缩操作。 如果你不是很确定是否要扩缩你的 StatefulSet,可先参阅 StatefulSet 概念 或者 StatefulSet 教程

  • 仅当你确定你的有状态应用的集群是完全健康的,才可执行扩缩操作.

扩缩 StatefulSet

使用 kubectl 扩缩 StatefulSet

首先,找到你要扩缩的 StatefulSet。

kubectl get statefulsets <statefulset 名称>

更改 StatefulSet 中副本个数:

kubectl scale statefulsets <statefulset 名称> --replicas=<新的副本数>

对 StatefulSet 执行就地更新

另外, 你可以就地更新 StatefulSet。

如果你的 StatefulSet 最初通过 kubectl applykubectl create --save-config 创建, 你可以更新 StatefulSet 清单中的 .spec.replicas,然后执行命令 kubectl apply

kubectl apply -f <更新后的 statefulset 文件>

否则,可以使用 kubectl edit 编辑副本字段:

kubectl edit statefulsets <statefulset 名称>

或者使用 kubectl patch

kubectl patch statefulsets <statefulset 名称> -p '{"spec":{"replicas":<new-replicas>}}'

故障排查

缩容操作无法正常工作

当 Stateful 所管理的任何 Pod 不健康时,你不能对该 StatefulSet 执行缩容操作。 仅当 StatefulSet 的所有 Pod 都处于运行状态和 Ready 状况后才可缩容。

如果 spec.replicas 大于 1,Kubernetes 无法判定 Pod 不健康的原因。 Pod 不健康可能是由于永久性故障造成也可能是瞬态故障。 瞬态故障可能是节点升级或维护而引起的节点重启造成的。

如果该 Pod 不健康是由于永久性故障导致,则在不纠正该故障的情况下进行缩容可能会导致 StatefulSet 进入一种状态,其成员 Pod 数量低于应正常运行的副本数。 这种状态也许会导致 StatefulSet 不可用。

如果由于瞬态故障而导致 Pod 不健康并且 Pod 可能再次变为可用,那么瞬态错误可能会干扰你对 StatefulSet 的扩容/缩容操作。一些分布式数据库在同时有节点加入和离开时会遇到问题。 在这些情况下,最好是在应用级别进行分析扩缩操作的状态,并且只有在确保 Stateful 应用的集群是完全健康时才执行扩缩操作。

接下来

4.8.5 - 删除 StatefulSet

本任务展示如何删除 StatefulSet

准备开始

  • 本任务假设在你的集群上已经运行了由 StatefulSet 创建的应用。

删除 StatefulSet

你可以像删除 Kubernetes 中的其他资源一样删除 StatefulSet: 使用 kubectl delete 命令,并按文件或者名字指定 StatefulSet。

kubectl delete -f <file.yaml>
kubectl delete statefulsets <statefulset 名称>

删除 StatefulSet 之后,你可能需要单独删除关联的无头服务(Headless Service)。

kubectl delete service <Service 名称>

当通过 kubectl 删除 StatefulSet 时,StatefulSet 会被缩容为 0。 属于该 StatefulSet 的所有 Pod 也被删除。 如果你只想删除 StatefulSet 而不删除 Pod,使用 --cascade=orphan

kubectl delete -f <file.yaml> --cascade=orphan

通过将 --cascade=orphan 传递给 kubectl delete,在删除 StatefulSet 对象之后, StatefulSet 管理的 Pod 会被保留下来。如果 Pod 具有标签 app.kubernetes.io/name=MyApp, 则可以按照如下方式删除它们:

kubectl delete pods -l app.kubernetes.io/name=MyApp

持久卷

删除 StatefulSet 管理的 Pod 并不会删除关联的卷。这是为了确保你有机会在删除卷之前从卷中复制数据。 在 Pod 已经终止后删除 PVC 可能会触发删除背后的 PV 持久卷,具体取决于存储类和回收策略。 永远不要假定在 PVC 删除后仍然能够访问卷。

完全删除 StatefulSet

要删除 StatefulSet 中的所有内容,包括关联的 Pod, 你可以运行如下所示的一系列命令:

grace=$(kubectl get pods <stateful-set-pod> --template '{{.spec.terminationGracePeriodSeconds}}')
kubectl delete statefulset -l app.kubernetes.io/name=MyApp
sleep $grace
kubectl delete pvc -l app.kubernetes.io/name=MyApp

在上面的例子中,Pod 的标签为 app.kubernetes.io/name=MyApp;适当地替换你自己的标签。

强制删除 StatefulSet 的 Pod

如果你发现 StatefulSet 的某些 Pod 长时间处于 'Terminating' 或者 'Unknown' 状态, 则可能需要手动干预以强制从 API 服务器中删除这些 Pod。这是一项有点危险的任务。 详细信息请阅读强制删除 StatefulSet 的 Pod

接下来

进一步了解强制删除 StatefulSet 的 Pod

4.8.6 - 强制删除 StatefulSet 中的 Pod

本文介绍如何删除 StatefulSet 管理的 Pod,并解释这样操作时需要记住的一些注意事项。

准备开始

  • 这是一项相当高级的任务,并且可能会违反 StatefulSet 固有的某些属性。
  • 请先熟悉下面列举的注意事项再开始操作。

StatefulSet 注意事项

在正常操作 StatefulSet 时,永远不需要强制删除 StatefulSet 管理的 Pod。 StatefulSet 控制器负责创建、 扩缩和删除 StatefulSet 管理的 Pod。此控制器尽力确保指定数量的从序数 0 到 N-1 的 Pod 处于活跃状态并准备就绪。StatefulSet 确保在任何时候,集群中最多只有一个具有给定标识的 Pod。 这就是所谓的由 StatefulSet 提供的最多一个(At Most One) Pod 的语义。

应谨慎进行手动强制删除操作,因为它可能会违反 StatefulSet 固有的至多一个的语义。 StatefulSet 可用于运行分布式和集群级的应用,这些应用需要稳定的网络标识和可靠的存储。 这些应用通常配置为具有固定标识固定数量的成员集合。 具有相同身份的多个成员可能是灾难性的,并且可能导致数据丢失 (例如票选系统中的脑裂场景)。

删除 Pod

你可以使用下面的命令执行体面地删除 Pod:

kubectl delete pods <pod>

为了让上面操作能够体面地终止 Pod,Pod 一定不能设置 pod.Spec.TerminationGracePeriodSeconds 为 0。 将 pod.Spec.TerminationGracePeriodSeconds 设置为 0 秒的做法是不安全的,强烈建议 StatefulSet 类型的 Pod 不要使用。体面删除是安全的,并且会在 kubelet 从 API 服务器中删除资源名称之前确保 体面地结束 Pod

当某个节点不可达时,不会引发自动删除 Pod。在无法访问的节点上运行的 Pod 在超时后会进入 “Terminating” 或者 “Unknown” 状态。 当用户尝试体面地删除无法访问的节点上的 Pod 时 Pod 也可能会进入这些状态。 从 API 服务器上删除处于这些状态 Pod 的仅有可行方法如下:

  • 删除 Node 对象(要么你来删除, 要么节点控制器 来删除)
  • 无响应节点上的 kubelet 开始响应,杀死 Pod 并从 API 服务器上移除 Pod 对象
  • 用户强制删除 Pod

推荐使用第一种或者第二种方法。 如果确认节点已经不可用了(比如,永久断开网络、断电等), 则应删除 Node 对象。 如果节点遇到网裂问题,请尝试解决该问题或者等待其解决。 当网裂愈合时,kubelet 将完成 Pod 的删除并从 API 服务器上释放其名字。

通常,Pod 一旦不在节点上运行,或者管理员删除了节点,系统就会完成其删除动作。 你也可以通过强制删除 Pod 来绕过这一机制。

强制删除

强制删除不会等待来自 kubelet 对 Pod 已终止的确认消息。 无论强制删除是否成功杀死了 Pod,它都会立即从 API 服务器中释放该名字。 这将让 StatefulSet 控制器创建一个具有相同标识的替身 Pod;因而可能导致正在运行 Pod 的重复, 并且如果所述 Pod 仍然可以与 StatefulSet 的成员通信,则将违反 StatefulSet 所要保证的最多一个的语义。

当你强制删除 StatefulSet 类型的 Pod 时,你要确保有问题的 Pod 不会再和 StatefulSet 管理的其他 Pod 通信并且可以安全地释放其名字以便创建替代 Pod。

如果要使用 kubectl 1.5 以上版本强制删除 Pod,请执行下面命令:

kubectl delete pods <pod> --grace-period=0 --force

如果你使用 kubectl 的 1.4 以下版本,则应省略 --force 选项:

kubectl delete pods <pod> --grace-period=0

如果在执行这些命令后 Pod 仍处于 Unknown 状态,请使用以下命令从集群中删除 Pod:

kubectl patch pod <pod> -p '{"metadata":{"finalizers":null}}'

请始终谨慎地执行强制删除 StatefulSet 类型的 Pod,并充分了解强制删除操作所涉及的风险。

接下来

进一步了解调试 StatefulSet

4.8.7 - Pod 水平自动扩缩

在 Kubernetes 中,HorizontalPodAutoscaler 自动更新工作负载资源 (例如 Deployment 或者 StatefulSet), 目的是自动扩缩工作负载以满足需求。

水平扩缩意味着对增加的负载的响应是部署更多的 Pod。 这与“垂直(Vertical)”扩缩不同,对于 Kubernetes, 垂直扩缩意味着将更多资源(例如:内存或 CPU)分配给已经为工作负载运行的 Pod。

如果负载减少,并且 Pod 的数量高于配置的最小值, HorizontalPodAutoscaler 会指示工作负载资源(Deployment、StatefulSet 或其他类似资源)缩减。

水平 Pod 自动扩缩不适用于无法扩缩的对象(例如:DaemonSet。)

HorizontalPodAutoscaler 被实现为 Kubernetes API 资源和控制器

资源决定了控制器的行为。 在 Kubernetes 控制平面内运行的水平 Pod 自动扩缩控制器会定期调整其目标(例如:Deployment)的所需规模,以匹配观察到的指标, 例如,平均 CPU 利用率、平均内存利用率或你指定的任何其他自定义指标。

使用水平 Pod 自动扩缩演练示例

HorizontalPodAutoscaler 是如何工作的?

graph BT hpa[Pod 水平自动扩缩] --> scale[规模] subgraph rc[RC / Deployment] scale end scale -.-> pod1[Pod 1] scale -.-> pod2[Pod 2] scale -.-> pod3[Pod N] classDef hpa fill:#D5A6BD,stroke:#1E1E1D,stroke-width:1px,color:#1E1E1D; classDef rc fill:#F9CB9C,stroke:#1E1E1D,stroke-width:1px,color:#1E1E1D; classDef scale fill:#B6D7A8,stroke:#1E1E1D,stroke-width:1px,color:#1E1E1D; classDef pod fill:#9FC5E8,stroke:#1E1E1D,stroke-width:1px,color:#1E1E1D; class hpa hpa; class rc rc; class scale scale; class pod1,pod2,pod3 pod

图 1. HorizontalPodAutoscaler 控制 Deployment 及其 ReplicaSet 的规模

Kubernetes 将水平 Pod 自动扩缩实现为一个间歇运行的控制回路(它不是一个连续的过程)。间隔由 kube-controller-manager--horizontal-pod-autoscaler-sync-period 参数设置(默认间隔为 15 秒)。

在每个时间段内,控制器管理器都会根据每个 HorizontalPodAutoscaler 定义中指定的指标查询资源利用率。 控制器管理器找到由 scaleTargetRef 定义的目标资源,然后根据目标资源的 .spec.selector 标签选择 Pod, 并从资源指标 API(针对每个 Pod 的资源指标)或自定义指标获取指标 API(适用于所有其他指标)。

  • 对于按 Pod 统计的资源指标(如 CPU),控制器从资源指标 API 中获取每一个 HorizontalPodAutoscaler 指定的 Pod 的度量值,如果设置了目标使用率,控制器获取每个 Pod 中的容器资源使用情况, 并计算资源使用率。如果设置了 target 值,将直接使用原始数据(不再计算百分比)。 接下来,控制器根据平均的资源使用率或原始值计算出扩缩的比例,进而计算出目标副本数。

    需要注意的是,如果 Pod 某些容器不支持资源采集,那么控制器将不会使用该 Pod 的 CPU 使用率。 下面的算法细节章节将会介绍详细的算法。

  • 如果 Pod 使用自定义指示,控制器机制与资源指标类似,区别在于自定义指标只使用原始值,而不是使用率。
  • 如果 Pod 使用对象指标和外部指标(每个指标描述一个对象信息)。 这个指标将直接根据目标设定值相比较,并生成一个上面提到的扩缩比例。 在 autoscaling/v2 版本 API 中,这个指标也可以根据 Pod 数量平分后再计算。

HorizontalPodAutoscaler 的常见用途是将其配置为从聚合 APImetrics.k8s.iocustom.metrics.k8s.ioexternal.metrics.k8s.io)获取指标。 metrics.k8s.io API 通常由名为 Metrics Server 的插件提供,需要单独启动。有关资源指标的更多信息, 请参阅 Metrics Server

Metrics API 的支持解释了这些不同 API 的稳定性保证和支持状态。

HorizontalPodAutoscaler 控制器访问支持扩缩的相应工作负载资源(例如:Deployment 和 StatefulSet)。 这些资源每个都有一个名为 scale 的子资源,该接口允许你动态设置副本的数量并检查它们的每个当前状态。 有关 Kubernetes API 子资源的一般信息, 请参阅 Kubernetes API 概念

算法细节

从最基本的角度来看,Pod 水平自动扩缩控制器根据当前指标和期望指标来计算扩缩比例。

期望副本数 = ceil[当前副本数 * (当前指标 / 期望指标)]

例如,如果当前指标值为 200m,而期望值为 100m,则副本数将加倍, 因为 200.0 / 100.0 == 2.0 如果当前值为 50m,则副本数将减半, 因为 50.0 / 100.0 == 0.5。如果比率足够接近 1.0(在全局可配置的容差范围内,默认为 0.1), 则控制平面会跳过扩缩操作。

如果 HorizontalPodAutoscaler 指定的是 targetAverageValuetargetAverageUtilization, 那么将会把指定 Pod 度量值的平均值做为 currentMetricValue

在检查容差并决定最终值之前,控制平面还会考虑是否缺少任何指标, 以及有多少 Pod Ready

所有设置了删除时间戳的 Pod(带有删除时间戳的对象正在关闭/移除的过程中)都会被忽略, 所有失败的 Pod 都会被丢弃。

如果某个 Pod 缺失度量值,它将会被搁置,只在最终确定扩缩数量时再考虑。

当使用 CPU 指标来扩缩时,任何还未就绪(还在初始化,或者可能是不健康的)状态的 Pod 最近的指标度量值采集于就绪状态前的 Pod,该 Pod 也会被搁置。

由于技术限制,HorizontalPodAutoscaler 控制器在确定是否保留某些 CPU 指标时无法准确确定 Pod 首次就绪的时间。 相反,如果 Pod 未准备好并在其启动后的一个可配置的短时间窗口内转换为准备好,它会认为 Pod “尚未准备好”。 该值使用 --horizontal-pod-autoscaler-initial-readiness-delay 标志配置,默认值为 30 秒。 一旦 Pod 准备就绪,如果它发生在自启动后较长的、可配置的时间内,它就会认为任何向准备就绪的转换都是第一个。 该值由 -horizontal-pod-autoscaler-cpu-initialization-period 标志配置,默认为 5 分钟。

在排除掉被搁置的 Pod 后,扩缩比例就会根据 currentMetricValue/desiredMetricValue 计算出来。

如果缺失某些度量值,控制平面会更保守地重新计算平均值,在需要缩小时假设这些 Pod 消耗了目标值的 100%, 在需要放大时假设这些 Pod 消耗了 0% 目标值。这可以在一定程度上抑制扩缩的幅度。

此外,如果存在任何尚未就绪的 Pod,工作负载会在不考虑遗漏指标或尚未就绪的 Pod 的情况下进行扩缩, 控制器保守地假设尚未就绪的 Pod 消耗了期望指标的 0%,从而进一步降低了扩缩的幅度。

考虑到尚未准备好的 Pod 和缺失的指标后,控制器会重新计算使用率。 如果新的比率与扩缩方向相反,或者在容差范围内,则控制器不会执行任何扩缩操作。 在其他情况下,新比率用于决定对 Pod 数量的任何更改。

注意,平均利用率的原始值是通过 HorizontalPodAutoscaler 状态体现的, 而不考虑尚未准备好的 Pod 或缺少的指标,即使使用新的使用率也是如此。

如果创建 HorizontalPodAutoscaler 时指定了多个指标, 那么会按照每个指标分别计算扩缩副本数,取最大值进行扩缩。 如果任何一个指标无法顺利地计算出扩缩副本数(比如,通过 API 获取指标时出错), 并且可获取的指标建议缩容,那么本次扩缩会被跳过。 这表示,如果一个或多个指标给出的 desiredReplicas 值大于当前值,HPA 仍然能实现扩容。

最后,在 HPA 控制器执行扩缩操作之前,会记录扩缩建议信息。 控制器会在操作时间窗口中考虑所有的建议信息,并从中选择得分最高的建议。 这个值可通过 kube-controller-manager 服务的启动参数 --horizontal-pod-autoscaler-downscale-stabilization 进行配置, 默认值为 5 分钟。 这个配置可以让系统更为平滑地进行缩容操作,从而消除短时间内指标值快速波动产生的影响。

API 对象

HorizontalPodAutoscaler 是 Kubernetes autoscaling API 组中的 API 资源。 当前的稳定版本可以在 autoscaling/v2 API 版本中找到,其中包括对基于内存和自定义指标执行扩缩的支持。 在使用 autoscaling/v1 时,autoscaling/v2 中引入的新字段作为注释保留。

创建 HorizontalPodAutoscaler 对象时,需要确保所给的名称是一个合法的 DNS 子域名。 有关 API 对象的更多信息,请查阅 HorizontalPodAutoscaler 对象文档

工作量规模的稳定性

在使用 HorizontalPodAutoscaler 管理一组副本的规模时,由于评估的指标的动态特性, 副本的数量可能会经常波动。这有时被称为 抖动(thrashing)波动(flapping)。 它类似于控制论中的 滞后(hysteresis) 概念。

滚动升级时扩缩

Kubernetes 允许你在 Deployment 上执行滚动更新。在这种情况下,Deployment 为你管理下层的 ReplicaSet。 当你为一个 Deployment 配置自动扩缩时,你要为每个 Deployment 绑定一个 HorizontalPodAutoscaler。 HorizontalPodAutoscaler 管理 Deployment 的 replicas 字段。 Deployment Controller 负责设置下层 ReplicaSet 的 replicas 字段, 以便确保在上线及后续过程副本个数合适。

如果你对一个副本个数被自动扩缩的 StatefulSet 执行滚动更新,该 StatefulSet 会直接管理它的 Pod 集合(不存在类似 ReplicaSet 这样的中间资源)。

对资源指标的支持

HPA 的任何目标资源都可以基于其中的 Pod 的资源用量来实现扩缩。 在定义 Pod 规约时,类似 cpumemory 这类资源请求必须被设定。 这些设定值被用来确定资源利用量并被 HPA 控制器用来对目标资源完成扩缩操作。 要使用基于资源利用率的扩缩,可以像下面这样指定一个指标源:

type: Resource
resource:
  name: cpu
  target:
    type: Utilization
    averageUtilization: 60

基于这一指标设定,HPA 控制器会维持扩缩目标中的 Pods 的平均资源利用率在 60%。 利用率是 Pod 的当前资源用量与其请求值之间的比值。 关于如何计算利用率以及如何计算平均值的细节可参考算法小节。

容器资源指标

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

HorizontalPodAutoscaler API 也支持容器指标源,这时 HPA 可以跟踪记录一组 Pod 中各个容器的资源用量,进而触发扩缩目标对象的操作。 容器资源指标的支持使得你可以为特定 Pod 中最重要的容器配置规模扩缩阈值。 例如,如果你有一个 Web 应用和一个提供记录日志功能的边车容器,你可以基于 Web 应用的资源用量来执行扩缩,忽略边车容器的存在及其资源用量。

如果你更改扩缩目标对象,令其使用新的、包含一组不同的容器的 Pod 规约,你就需要修改 HPA 的规约才能基于新添加的容器来执行规模扩缩操作。 如果指标源中指定的容器不存在或者仅存在于部分 Pod 中,那么这些 Pod 会被忽略, HPA 会重新计算资源用量值。参阅算法小节进一步了解计算细节。 要使用容器资源用量来完成自动扩缩,可以像下面这样定义指标源:

type: ContainerResource
containerResource:
  name: cpu
  container: application
  target:
    type: Utilization
    averageUtilization: 60

在上面的例子中,HPA 控制器会对目标对象执行扩缩操作以确保所有 Pod 中 application 容器的平均 CPU 用量为 60%。

扩展自定义指标

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

(之前的 autoscaling/v2beta2 API 版本将此功能作为 beta 功能提供)

如果你使用 autoscaling/v2 API 版本,则可以将 HorizontalPodAutoscaler 配置为基于自定义指标(未内置于 Kubernetes 或任何 Kubernetes 组件)进行扩缩。 HorizontalPodAutoscaler 控制器能够从 Kubernetes API 查询这些自定义指标。

有关要求,请参阅对 Metrics APIs 的支持

基于多个指标来执行扩缩

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

(之前的 autoscaling/v2beta2 API 版本将此功能作为 beta 功能提供)

如果你使用 autoscaling/v2 API 版本,你可以为 HorizontalPodAutoscaler 指定多个指标以进行扩缩。 HorizontalPodAutoscaler 控制器评估每个指标,并根据该指标提出一个新的比例。 HorizontalPodAutoscaler 采用为每个指标推荐的最大比例, 并将工作负载设置为该大小(前提是这不大于你配置的总体最大值)。

对 Metrics API 的支持

默认情况下,HorizontalPodAutoscaler 控制器会从一系列的 API 中检索度量值。 集群管理员需要确保下述条件,以保证 HPA 控制器能够访问这些 API:

  • 启用了 API 聚合层

  • 相应的 API 已注册:

    • 对于资源指标,将使用 metrics.k8s.io API, 一般由 metrics-server 提供。 它可以作为集群插件启动。

    • 对于自定义指标,将使用 custom.metrics.k8s.io API。 它由其他度量指标方案厂商的“适配器(Adapter)” API 服务器提供。 检查你的指标管道以查看是否有可用的 Kubernetes 指标适配器。

    • 对于外部指标,将使用 external.metrics.k8s.io API。 可能由上面的自定义指标适配器提供。

关于指标来源以及其区别的更多信息,请参阅相关的设计文档, HPA V2custom.metrics.k8s.ioexternal.metrics.k8s.io

关于如何使用它们的示例, 请参考使用自定义指标的教程使用外部指标的教程

可配置的扩缩行为

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

(之前的 autoscaling/v2beta2 API 版本将此功能作为 beta 功能提供)

如果你使用 v2 HorizontalPodAutoscaler API,你可以使用 behavior 字段 (请参阅 API 参考) 来配置单独的放大和缩小行为。你可以通过在行为字段下设置 scaleUp 和/或 scaleDown 来指定这些行为。

你可以指定一个“稳定窗口”,以防止扩缩目标的副本计数发生波动。 扩缩策略还允许你在扩缩时控制副本的变化率。

扩缩策略

可以在规约的 behavior 部分中指定一个或多个扩缩策略。当指定多个策略时, 允许最大更改量的策略是默认选择的策略。以下示例显示了缩小时的这种行为:

behavior:
  scaleDown:
    policies:
    - type: Pods
      value: 4
      periodSeconds: 60
    - type: Percent
      value: 10
      periodSeconds: 60

periodSeconds 表示在过去的多长时间内要求策略值为真。 你可以设置 periodSeconds 的最大值为 1800(半小时)。 第一个策略(Pods)允许在一分钟内最多缩容 4 个副本。第二个策略(Percent) 允许在一分钟内最多缩容当前副本个数的百分之十。

由于默认情况下会选择容许更大程度作出变更的策略,只有 Pod 副本数大于 40 时, 第二个策略才会被采用。如果副本数为 40 或者更少,则应用第一个策略。 例如,如果有 80 个副本,并且目标必须缩小到 10 个副本,那么在第一步中将减少 8 个副本。 在下一轮迭代中,当副本的数量为 72 时,10% 的 Pod 数为 7.2,但是这个数字向上取整为 8。 在 autoscaler 控制器的每个循环中,将根据当前副本的数量重新计算要更改的 Pod 数量。 当副本数量低于 40 时,应用第一个策略(Pods),一次减少 4 个副本。

可以指定扩缩方向的 selectPolicy 字段来更改策略选择。 通过设置 Min 的值,它将选择副本数变化最小的策略。 将该值设置为 Disabled 将完全禁用该方向的扩缩。

稳定窗口

当用于扩缩的指标不断波动时,稳定窗口用于限制副本计数的波动。 自动扩缩算法使用此窗口来推断先前的期望状态并避免对工作负载规模进行不必要的更改。

例如,在以下示例代码段中,为 scaleDown 指定了稳定窗口。

behavior:
  scaleDown:
    stabilizationWindowSeconds: 300

当指标显示目标应该缩容时,自动扩缩算法查看之前计算的期望状态,并使用指定时间间隔内的最大值。 在上面的例子中,过去 5 分钟的所有期望状态都会被考虑。

这近似于滚动最大值,并避免了扩缩算法频繁删除 Pod 而又触发重新创建等效 Pod。

默认行为

要使用自定义扩缩,不必指定所有字段。 只有需要自定义的字段才需要指定。 这些自定义值与默认值合并。 默认值与 HPA 算法中的现有行为匹配。

behavior:
  scaleDown:
    stabilizationWindowSeconds: 300
    policies:
    - type: Percent
      value: 100
      periodSeconds: 15
  scaleUp:
    stabilizationWindowSeconds: 0
    policies:
    - type: Percent
      value: 100
      periodSeconds: 15
    - type: Pods
      value: 4
      periodSeconds: 15
    selectPolicy: Max

用于缩小稳定窗口的时间为 300 秒(或是 --horizontal-pod-autoscaler-downscale-stabilization 参数设定值)。 只有一种缩容的策略,允许 100% 删除当前运行的副本,这意味着扩缩目标可以缩小到允许的最小副本数。 对于扩容,没有稳定窗口。当指标显示目标应该扩容时,目标会立即扩容。 这里有两种策略,每 15 秒最多添加 4 个 Pod 或 100% 当前运行的副本数,直到 HPA 达到稳定状态。

示例:更改缩容稳定窗口

将下面的 behavior 配置添加到 HPA 中,可提供一个 1 分钟的自定义缩容稳定窗口:

behavior:
  scaleDown:
    stabilizationWindowSeconds: 60

示例:限制缩容速率

将下面的 behavior 配置添加到 HPA 中,可限制 Pod 被 HPA 删除速率为每分钟 10%:

behavior:
  scaleDown:
    policies:
    - type: Percent
      value: 10
      periodSeconds: 60

为了确保每分钟删除的 Pod 数不超过 5 个,可以添加第二个缩容策略,大小固定为 5,并将 selectPolicy 设置为最小值。 将 selectPolicy 设置为 Min 意味着 autoscaler 会选择影响 Pod 数量最小的策略:

behavior:
  scaleDown:
    policies:
    - type: Percent
      value: 10
      periodSeconds: 60
    - type: Pods
      value: 5
      periodSeconds: 60
    selectPolicy: Min

示例:禁用缩容

selectPolicy 的值 Disabled 会关闭对给定方向的缩容。 因此使用以下策略,将会阻止缩容:

behavior:
  scaleDown:
    selectPolicy: Disabled

kubectl 对 HorizontalPodAutoscaler 的支持

与每个 API 资源一样,HorizontalPodAutoscaler 都被 kubectl 以标准方式支持。 你可以使用 kubectl create 命令创建一个新的自动扩缩器。 你可以通过 kubectl get hpa 列出自动扩缩器或通过 kubectl describe hpa 获取详细描述。 最后,你可以使用 kubectl delete hpa 删除自动扩缩器。

此外,还有一个特殊的 kubectl autoscale 命令用于创建 HorizontalPodAutoscaler 对象。 例如,执行 kubectl autoscale rs foo --min=2 --max=5 --cpu-percent=80 将为 ReplicaSet foo 创建一个自动扩缩器,目标 CPU 利用率设置为 80%,副本数在 2 到 5 之间。

隐式维护状态禁用

你可以在不必更改 HPA 配置的情况下隐式地为某个目标禁用 HPA。 如果此目标的期望副本个数被设置为 0,而 HPA 的最小副本个数大于 0, 则 HPA 会停止调整目标(并将其自身的 ScalingActive 状况设置为 false), 直到你通过手动调整目标的期望副本个数或 HPA 的最小副本个数来重新激活。

将 Deployment 和 StatefulSet 迁移到水平自动扩缩

当启用 HPA 时,建议从它们的清单中删除 Deployment 和/或 StatefulSet 的 spec.replicas 的值。 如果不这样做,则只要应用对该对象的更改,例如通过 kubectl apply -f deployment.yaml, 这将指示 Kubernetes 将当前 Pod 数量扩缩到 spec.replicas 键的值。这可能不是所希望的, 并且当 HPA 处于活动状态时可能会很麻烦。

请记住,删除 spec.replicas 可能会导致 Pod 计数一次性降级,因为此键的默认值为 1 (参考 Deployment Replicas)。 更新后,除 1 之外的所有 Pod 都将开始其终止程序。之后的任何部署应用程序都将正常运行, 并根据需要遵守滚动更新配置。你可以根据修改部署的方式选择以下两种方法之一来避免这种降级:

  1. kubectl apply edit-last-applied deployment/<Deployment 名称>
  2. 在编辑器中,删除 spec.replicas。当你保存并退出编辑器时,kubectl 会应用更新。 在此步骤中不会更改 Pod 计数。
  3. 你现在可以从清单中删除 spec.replicas。如果你使用源代码管理, 还应提交你的更改或采取任何其他步骤来修改源代码,以适应你如何跟踪更新。
  4. 从这里开始,你可以运行 kubectl apply -f deployment.yaml

使用服务器端 Apply 机制, 你可以遵循交出所有权说明, 该指南涵盖了这个确切的用例。

接下来

如果你在集群中配置自动扩缩, 你可能还需要考虑使用集群自动扩缩来确保所运行的节点数目合适。

有关 HorizontalPodAutoscaler 的更多信息:

4.8.8 - HorizontalPodAutoscaler 演练

HorizontalPodAutoscaler(简称 HPA ) 自动更新工作负载资源(例如 Deployment 或者 StatefulSet), 目的是自动扩缩工作负载以满足需求。

水平扩缩意味着对增加的负载的响应是部署更多的 Pod。 这与“垂直(Vertical)”扩缩不同,对于 Kubernetes, 垂直扩缩意味着将更多资源(例如:内存或 CPU)分配给已经为工作负载运行的 Pod。

如果负载减少,并且 Pod 的数量高于配置的最小值, HorizontalPodAutoscaler 会指示工作负载资源(Deployment、StatefulSet 或其他类似资源)缩减。

本文档将引导你完成启用 HorizontalPodAutoscaler 以自动管理示例 Web 应用程序的扩缩的示例。 此示例工作负载是运行一些 PHP 代码的 Apache httpd。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你的 Kubernetes 服务器版本必须不低于版本 1.23. 要获知版本信息,请输入 kubectl version.

如果你运行的是旧版本的 Kubernetes,请参阅该版本的文档版本 (可用的文档版本)。

按照本演练进行操作,你需要一个部署并配置了 Metrics Server 的集群。 Kubernetes Metrics Server 从集群中的 kubelets 收集资源指标, 并通过 Kubernetes API 公开这些指标, 使用 APIService 添加代表指标读数的新资源。

要了解如何部署 Metrics Server,请参阅 metrics-server 文档

如果你正在运行 Minikube,运行以下命令以启用 metrics-server:

minikube addons enable metrics-server

运行 php-apache 服务器并暴露服务

为了演示 HorizontalPodAutoscaler,你将首先启动一个 Deployment 用 hpa-example 镜像运行一个容器, 然后使用以下清单文件将其暴露为一个 服务(Service)

apiVersion: apps/v1
kind: Deployment
metadata:
  name: php-apache
spec:
  selector:
    matchLabels:
      run: php-apache
  template:
    metadata:
      labels:
        run: php-apache
    spec:
      containers:
      - name: php-apache
        image: registry.k8s.io/hpa-example
        ports:
        - containerPort: 80
        resources:
          limits:
            cpu: 500m
          requests:
            cpu: 200m
---
apiVersion: v1
kind: Service
metadata:
  name: php-apache
  labels:
    run: php-apache
spec:
  ports:
  - port: 80
  selector:
    run: php-apache

为此,运行下面的命令:

kubectl apply -f https://k8s.io/examples/application/php-apache.yaml
deployment.apps/php-apache created
service/php-apache created

创建 HorizontalPodAutoscaler

现在服务器正在运行,使用 kubectl 创建自动扩缩器。 kubectl autoscale 子命令是 kubectl 的一部分, 可以帮助你执行此操作。

你将很快运行一个创建 HorizontalPodAutoscaler 的命令, 该 HorizontalPodAutoscaler 维护由你在这些说明的第一步中创建的 php-apache Deployment 控制的 Pod 存在 1 到 10 个副本。

粗略地说,HPA 控制器将增加和减少副本的数量 (通过更新 Deployment)以保持所有 Pod 的平均 CPU 利用率为 50%。 Deployment 然后更新 ReplicaSet —— 这是所有 Deployment 在 Kubernetes 中工作方式的一部分 —— 然后 ReplicaSet 根据其 .spec 的更改添加或删除 Pod。

由于每个 Pod 通过 kubectl run 请求 200 milli-cores,这意味着平均 CPU 使用率为 100 milli-cores。 有关算法的更多详细信息, 请参阅算法详细信息

创建 HorizontalPodAutoscaler:

kubectl autoscale deployment php-apache --cpu-percent=50 --min=1 --max=10
horizontalpodautoscaler.autoscaling/php-apache autoscaled

你可以通过运行以下命令检查新制作的 HorizontalPodAutoscaler 的当前状态:

# 你可以使用 “hpa” 或 “horizontalpodautoscaler”;任何一个名字都可以。
kubectl get hpa

输出类似于:

NAME         REFERENCE                     TARGET    MINPODS   MAXPODS   REPLICAS   AGE
php-apache   Deployment/php-apache/scale   0% / 50%  1         10        1          18s

(如果你看到其他具有不同名称的 HorizontalPodAutoscalers,这意味着它们已经存在,这通常不是问题)。

请注意当前的 CPU 利用率是 0%,这是由于我们尚未发送任何请求到服务器 (TARGET 列显示了相应 Deployment 所控制的所有 Pod 的平均 CPU 利用率)。

增加负载

接下来,看看自动扩缩器如何对增加的负载做出反应。 为此,你将启动一个不同的 Pod 作为客户端。 客户端 Pod 中的容器在无限循环中运行,向 php-apache 服务发送查询。

# 在单独的终端中运行它
# 以便负载生成继续,你可以继续执行其余步骤
kubectl run -i --tty load-generator --rm --image=busybox:1.28 --restart=Never -- /bin/sh -c "while sleep 0.01; do wget -q -O- http://php-apache; done"

现在执行:

# 准备好后按 Ctrl+C 结束观察
kubectl get hpa php-apache --watch

一分钟时间左右之后,通过以下命令,我们可以看到 CPU 负载升高了;例如:

NAME         REFERENCE                     TARGET      MINPODS   MAXPODS   REPLICAS   AGE
php-apache   Deployment/php-apache/scale   305% / 50%  1         10        1          3m

然后,更多的副本被创建。例如:

NAME         REFERENCE                     TARGET      MINPODS   MAXPODS   REPLICAS   AGE
php-apache   Deployment/php-apache/scale   305% / 50%  1         10        7          3m

这时,由于请求增多,CPU 利用率已经升至请求值的 305%。 可以看到,Deployment 的副本数量已经增长到了 7:

kubectl get deployment php-apache

你应该会看到与 HorizontalPodAutoscaler 中的数字与副本数匹配

NAME         READY   UP-TO-DATE   AVAILABLE   AGE
php-apache   7/7      7           7           19m

停止产生负载

要完成该示例,请停止发送负载。

在我们创建 busybox 容器的终端中,输入 <Ctrl> + C 来终止负载的产生。

然后验证结果状态(大约一分钟后):

# 准备好后按 Ctrl+C 结束观察
kubectl get hpa php-apache --watch

输出类似于:

NAME         REFERENCE                     TARGET       MINPODS   MAXPODS   REPLICAS   AGE
php-apache   Deployment/php-apache/scale   0% / 50%     1         10        1          11m

Deployment 也显示它已经缩小了:

kubectl get deployment php-apache
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
php-apache   1/1     1            1           27m

一旦 CPU 利用率降至 0,HPA 会自动将副本数缩减为 1。

自动扩缩完成副本数量的改变可能需要几分钟的时间。

基于多项度量指标和自定义度量指标自动扩缩

利用 autoscaling/v2 API 版本,你可以在自动扩缩 php-apache 这个 Deployment 时使用其他度量指标。

首先,将 HorizontalPodAutoscaler 的 YAML 文件改为 autoscaling/v2 格式:

kubectl get hpa php-apache -o yaml > /tmp/hpa-v2.yaml

在编辑器中打开 /tmp/hpa-v2.yaml,你应看到如下所示的 YAML 文件:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50
status:
  observedGeneration: 1
  lastScaleTime: <some-time>
  currentReplicas: 1
  desiredReplicas: 1
  currentMetrics:
  - type: Resource
    resource:
      name: cpu
      current:
        averageUtilization: 0
        averageValue: 0

需要注意的是,targetCPUUtilizationPercentage 字段已经被名为 metrics 的数组所取代。 CPU 利用率这个度量指标是一个 resource metric(资源度量指标),因为它表示容器上指定资源的百分比。 除 CPU 外,你还可以指定其他资源度量指标。默认情况下,目前唯一支持的其他资源度量指标为内存。 只要 metrics.k8s.io API 存在,这些资源度量指标就是可用的,并且他们不会在不同的 Kubernetes 集群中改变名称。

你还可以指定资源度量指标使用绝对数值,而不是百分比,你需要将 target.typeUtilization 替换成 AverageValue,同时设置 target.averageValue 而非 target.averageUtilization 的值。

还有两种其他类型的度量指标,他们被认为是 custom metrics(自定义度量指标): 即 Pod 度量指标和 Object 度量指标。 这些度量指标可能具有特定于集群的名称,并且需要更高级的集群监控设置。

第一种可选的度量指标类型是 Pod 度量指标。这些指标从某一方面描述了 Pod, 在不同 Pod 之间进行平均,并通过与一个目标值比对来确定副本的数量。 它们的工作方式与资源度量指标非常相像,只是它们支持 target 类型为 AverageValue

Pod 度量指标通过如下代码块定义:

type: Pods
pods:
  metric:
    name: packets-per-second
  target:
    type: AverageValue
    averageValue: 1k

第二种可选的度量指标类型是对象 (Object)度量指标。 这些度量指标用于描述在相同名字空间中的别的对象,而非 Pod。 请注意这些度量指标不一定来自某对象,它们仅用于描述这些对象。 对象度量指标支持的 target 类型包括 ValueAverageValue。 如果是 Value 类型,target 值将直接与 API 返回的度量指标比较, 而对于 AverageValue 类型,API 返回的度量值将按照 Pod 数量拆分, 然后再与 target 值比较。 下面的 YAML 文件展示了一个表示 requests-per-second 的度量指标。

type: Object
object:
  metric:
    name: requests-per-second
  describedObject:
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    name: main-route
  target:
    type: Value
    value: 2k

如果你指定了多个上述类型的度量指标,HorizontalPodAutoscaler 将会依次考量各个指标。 HorizontalPodAutoscaler 将会计算每一个指标所提议的副本数量,然后最终选择一个最高值。

比如,如果你的监控系统能够提供网络流量数据,你可以通过 kubectl edit 命令将上述 Horizontal Pod Autoscaler 的定义更改为:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50
  - type: Pods
    pods:
      metric:
        name: packets-per-second
      target:
        type: AverageValue
        averageValue: 1k
  - type: Object
    object:
      metric:
        name: requests-per-second
      describedObject:
        apiVersion: networking.k8s.io/v1
        kind: Ingress
        name: main-route
      target:
        type: Value
        value: 10k
status:
  observedGeneration: 1
  lastScaleTime: <some-time>
  currentReplicas: 1
  desiredReplicas: 1
  currentMetrics:
  - type: Resource
    resource:
      name: cpu
    current:
      averageUtilization: 0
      averageValue: 0
  - type: Object
    object:
      metric:
        name: requests-per-second
      describedObject:
        apiVersion: networking.k8s.io/v1
        kind: Ingress
        name: main-route
      current:
        value: 10k

这样,你的 HorizontalPodAutoscaler 将会尝试确保每个 Pod 的 CPU 利用率在 50% 以内, 每秒能够服务 1000 个数据包请求, 并确保所有在 Ingress 后的 Pod 每秒能够服务的请求总数达到 10000 个。

基于更特别的度量值来扩缩

许多度量流水线允许你通过名称或附加的标签来描述度量指标。 对于所有非资源类型度量指标(Pod、Object 和后面将介绍的 External), 可以额外指定一个标签选择算符。例如,如果你希望收集包含 verb 标签的 http_requests 度量指标,可以按如下所示设置度量指标块,使得扩缩操作仅针对 GET 请求执行:

type: Object
object:
  metric:
    name: http_requests
    selector: {matchLabels: {verb: GET}}

这个选择算符使用与 Kubernetes 标签选择算符相同的语法。 如果名称和标签选择算符匹配到多个系列,监测管道会决定如何将多个系列合并成单个值。 选择算符是可以累加的,它不会选择目标以外的对象(类型为 Pods 的目标 Pod 或者类型为 Object 的目标对象)。

运行在 Kubernetes 上的应用程序可能需要基于与 Kubernetes 集群中的任何对象没有明显关系的度量指标进行自动扩缩, 例如那些描述与任何 Kubernetes 名字空间中的服务都无直接关联的度量指标。 在 Kubernetes 1.10 及之后版本中,你可以使用外部度量指标(external metrics)。

使用外部度量指标时,需要了解你所使用的监控系统,相关的设置与使用自定义指标时类似。 外部度量指标使得你可以使用你的监控系统的任何指标来自动扩缩你的集群。 你需要在 metric 块中提供 nameselector,同时将类型由 Object 改为 External。 如果 metricSelector 匹配到多个度量指标,HorizontalPodAutoscaler 将会把它们加和。 外部度量指标同时支持 ValueAverageValue 类型,这与 Object 类型的度量指标相同。

例如,如果你的应用程序处理来自主机上消息队列的任务, 为了让每 30 个任务有 1 个工作者实例,你可以将下面的内容添加到 HorizontalPodAutoscaler 的配置中。

- type: External
  external:
    metric:
      name: queue_messages_ready
      selector:
        matchLabels:
          queue: "worker_tasks"
    target:
      type: AverageValue
      averageValue: 30

如果可能,还是推荐定制度量指标而不是外部度量指标,因为这便于让系统管理员加固定制度量指标 API。 而外部度量指标 API 可以允许访问所有的度量指标。 当暴露这些服务时,系统管理员需要仔细考虑这个问题。

附录:Horizontal Pod Autoscaler 状态条件

使用 autoscaling/v2 格式的 HorizontalPodAutoscaler 时,你将可以看到 Kubernetes 为 HorizongtalPodAutoscaler 设置的状态条件(Status Conditions)。 这些状态条件可以显示当前 HorizontalPodAutoscaler 是否能够执行扩缩以及是否受到一定的限制。

status.conditions 字段展示了这些状态条件。 可以通过 kubectl describe hpa 命令查看当前影响 HorizontalPodAutoscaler 的各种状态条件信息:

kubectl describe hpa cm-test
Name:                           cm-test
Namespace:                      prom
Labels:                         <none>
Annotations:                    <none>
CreationTimestamp:              Fri, 16 Jun 2017 18:09:22 +0000
Reference:                      ReplicationController/cm-test
Metrics:                        ( current / target )
  "http_requests" on pods:      66m / 500m
Min replicas:                   1
Max replicas:                   4
ReplicationController pods:     1 current / 1 desired
Conditions:
  Type                  Status  Reason                  Message
  ----                  ------  ------                  -------
  AbleToScale           True    ReadyForNewScale        the last scale time was sufficiently old as to warrant a new scale
  ScalingActive         True    ValidMetricFound        the HPA was able to successfully calculate a replica count from pods metric http_requests
  ScalingLimited        False   DesiredWithinRange      the desired replica count is within the acceptable range
Events:

对于上面展示的这个 HorizontalPodAutoscaler,我们可以看出有若干状态条件处于健康状态。 首先,AbleToScale 表明 HPA 是否可以获取和更新扩缩信息,以及是否存在阻止扩缩的各种回退条件。 其次,ScalingActive 表明 HPA 是否被启用(即目标的副本数量不为零)以及是否能够完成扩缩计算。 当这一状态为 False 时,通常表明获取度量指标存在问题。 最后一个条件 ScalingLimited 表明所需扩缩的值被 HorizontalPodAutoscaler 所定义的最大或者最小值所限制(即已经达到最大或者最小扩缩值)。 这通常表明你可能需要调整 HorizontalPodAutoscaler 所定义的最大或者最小副本数量的限制了。

量纲

HorizontalPodAutoscaler 和 度量指标 API 中的所有的度量指标使用 Kubernetes 中称为量纲(Quantity)的特殊整数表示。 例如,数量 10500m 用十进制表示为 10.5。 如果可能的话,度量指标 API 将返回没有后缀的整数,否则返回以千分单位的数量。 这意味着你可能会看到你的度量指标在 11500m(也就是在十进制记数法中的 11.5)之间波动。

其他可能的情况

以声明式方式创建 Autoscaler

除了使用 kubectl autoscale 命令,也可以使用以下清单以声明方式创建 HorizontalPodAutoscaler:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50

使用如下命令创建 Autoscaler:

kubectl create -f https://k8s.io/examples/application/hpa/php-apache.yaml
horizontalpodautoscaler.autoscaling/php-apache created

4.8.9 - 为应用程序设置干扰预算(Disruption Budget)

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

本文展示如何限制应用程序的并发干扰数量,在允许集群管理员管理集群节点的同时保证高可用。

准备开始

你的 Kubernetes 服务器版本必须不低于版本 v1.21. 要获知版本信息,请输入 kubectl version.
  • 你是 Kubernetes 集群中某应用的所有者,该应用有高可用要求。
  • 你应了解如何部署无状态应用 和/或有状态应用
  • 你应当已经阅读过关于 Pod 干扰的文档。
  • 用户应当与集群所有者或服务提供者确认其遵从 Pod 干扰预算(Pod Disruption Budgets)的规则。

用 PodDisruptionBudget 来保护应用

  1. 确定想要使用 PodDisruptionBudget(PDB)来保护的应用。
  2. 考虑应用对干扰的反应。
  3. 以 YAML 文件形式定义 PDB。
  4. 通过 YAML 文件创建 PDB 对象。

确定要保护的应用

用户想要保护通过内置的 Kubernetes 控制器指定的应用,这是最常见的使用场景:

  • Deployment
  • ReplicationController
  • ReplicaSet
  • StatefulSet

在这种情况下,在控制器的 .spec.selector 字段中做记录,并在 PDB 的 .spec.selector 字段中加入同样的选择算符。

从 1.15 版本开始,PDB 支持启用 Scale 子资源 的自定义控制器。

用户也可以用 PDB 来保护不受上述控制器控制的 Pod,或任意的 Pod 集合,但是正如 任意工作负载和任意选择算符中描述的,这里存在一些限制。

考虑应用对干扰的反应

确定在自发干扰时,多少实例可以在短时间内同时关闭。

  • 无状态的前端:
    • 关注:不能降低服务能力 10% 以上。
      • 解决方案:例如,使用 PDB,指定其 minAvailable 值为 90%。
  • 单实例有状态应用:
    • 关注:不要在不通知的情况下终止该应用。
      • 可能的解决方案 1:不使用 PDB,并忍受偶尔的停机。
      • 可能的解决方案 2:设置 maxUnavailable=0 的 PDB。 意为(Kubernetes 范畴之外的)集群操作人员需要在终止应用前与用户协商, 协商后准备停机,然后删除 PDB 表示准备接受干扰,后续再重新创建。
  • 多实例有状态应用,如 Consul、ZooKeeper 或 etcd:
    • 关注:不要将实例数量减少至低于仲裁规模,否则将出现写入失败。
      • 可能的解决方案 1:设置 maxUnavailable 值为 1 (适用于不同规模的应用)。
      • 可能的解决方案 2:设置 minAvailable 值为仲裁规模(例如规模为 5 时设置为 3)。 (允许同时出现更多的干扰)。
  • 可重新启动的批处理任务:
    • 关注:自发干扰的情况下,需要确保任务完成。
      • 可能的解决方案:不创建 PDB。任务控制器会创建一个替换 Pod。

指定百分比时的舍入逻辑

minAvailablemaxUnavailable 的值可以表示为整数或百分比。

  • 指定整数值时,它表示 Pod 个数。例如,如果将 minAvailable 设置为 10, 那么即使在干扰期间,也必须始终有 10 个 Pod 可用。
  • 通过将值设置为百分比的字符串表示形式(例如 "50%")来指定百分比时,它表示占总 Pod 数的百分比。 例如,如果将 minAvailable 设置为 "50%",则干扰期间至少 50% 的 Pod 保持可用。

如果将值指定为百分比,则可能无法映射到确切数量的 Pod。例如,如果你有 7 个 Pod, 并且你将 minAvailable 设置为 "50%",具体是 3 个 Pod 或 4 个 Pod 必须可用并非显而易见。 Kubernetes 采用向上取整到最接近的整数的办法,因此在这种情况下,必须有 4 个 Pod。 当你将 maxUnavailable 值指定为一个百分比时,Kubernetes 将可以干扰的 Pod 个数向上取整。 因此干扰可以超过你定义的 maxUnavailable 百分比。 你可以检查控制此行为的代码

指定 PodDisruptionBudget

一个 PodDisruptionBudget 有 3 个字段:

  • 标签选择算符 .spec.selector 用于指定其所作用的 Pod 集合,该字段为必需字段。
  • .spec.minAvailable 表示驱逐后仍须保证可用的 Pod 数量。即使因此影响到 Pod 驱逐 (即该条件在和 Pod 驱逐发生冲突时优先保证)。 minAvailable 值可以是绝对值,也可以是百分比。
  • .spec.maxUnavailable (Kubernetes 1.7 及更高的版本中可用)表示驱逐后允许不可用的 Pod 的最大数量。其值可以是绝对值或是百分比。

用户在同一个 PodDisruptionBudget 中只能够指定 maxUnavailableminAvailable 中的一个。 maxUnavailable 只能够用于控制存在相应控制器的 Pod 的驱逐(即不受控制器控制的 Pod 不在 maxUnavailable 控制范围内)。在下面的示例中, “所需副本”指的是相应控制器的 scale,控制器对 PodDisruptionBudget 所选择的 Pod 进行管理。

示例 1:设置 minAvailable 值为 5 的情况下,驱逐时需保证 PodDisruptionBudget 的 selector 选中的 Pod 中 5 个或 5 个以上处于健康状态。

示例 2:设置 minAvailable 值为 30% 的情况下,驱逐时需保证 Pod 所需副本的至少 30% 处于健康状态。

示例 3:设置 maxUnavailable 值为 5 的情况下,驱逐时需保证所需副本中最多 5 个处于不可用状态。

示例 4:设置 maxUnavailable 值为 30% 的情况下,只要不健康的副本数量不超过所需副本总数的 30% (取整到最接近的整数),就允许驱逐。如果所需副本的总数仅为一个,则仍允许该单个副本中断, 从而导致不可用性实际达到 100%。

在典型用法中,干扰预算会被用于一个控制器管理的一组 Pod 中 —— 例如:一个 ReplicaSet 或 StatefulSet 中的 Pod。

如果你将 maxUnavailable 的值设置为 0%(或 0)或设置 minAvailable 值为 100%(或等于副本数) 则会阻止所有的自愿驱逐。 当你为 ReplicaSet 等工作负载对象设置阻止自愿驱逐时,你将无法成功地腾空运行其中一个 Pod 的节点。 如果你尝试腾空正在运行着被阻止驱逐的 Pod 的节点,则腾空永远不会完成。 按照 PodDisruptionBudget 的语义,这是允许的。

用户可以在下面看到 Pod 干扰预算定义的示例,它们与带有 app: zookeeper 标签的 Pod 相匹配:

使用 minAvailable 的 PDB 示例:

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: zk-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: zookeeper

使用 maxUnavailable 的 PDB 示例:

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: zk-pdb
spec:
  maxUnavailable: 1
  selector:
    matchLabels:
      app: zookeeper

例如,如果上述 zk-pdb 选择的是一个规格为 3 的 StatefulSet 对应的 Pod, 那么上面两种规范的含义完全相同。 推荐使用 maxUnavailable,因为它自动响应控制器副本数量的变化。

创建 PDB 对象

你可以使用 kubectl 创建或更新 PDB 对象。

kubectl apply -f mypdb.yaml

检查 PDB 的状态

使用 kubectl 来确认 PDB 被创建。

假设用户的名字空间下没有匹配 app: zookeeper 的 Pod,用户会看到类似下面的信息:

kubectl get poddisruptionbudgets
NAME     MIN AVAILABLE   MAX UNAVAILABLE   ALLOWED DISRUPTIONS   AGE
zk-pdb   2               N/A               0                     7s

假设有匹配的 Pod(比如说 3 个),那么用户会看到类似下面的信息:

kubectl get poddisruptionbudgets
NAME     MIN AVAILABLE   MAX UNAVAILABLE   ALLOWED DISRUPTIONS   AGE
zk-pdb   2               N/A               1                     7s

ALLOWED DISRUPTIONS 值非 0 意味着干扰控制器已经感知到相应的 Pod,对匹配的 Pod 进行统计, 并更新了 PDB 的状态。

用户可以通过以下命令获取更多 PDB 状态相关信息:

kubectl get poddisruptionbudgets zk-pdb -o yaml
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  annotations:

  creationTimestamp: "2020-03-04T04:22:56Z"
  generation: 1
  name: zk-pdb

status:
  currentHealthy: 3
  desiredHealthy: 2
  disruptionsAllowed: 1
  expectedPods: 3
  observedGeneration: 1

Pod 的健康

如果 Pod 的 .status.conditions 中包含 type="Ready"status="True" 的项, 则当前实现将其视为健康的 Pod。这些 Pod 通过 PDB 状态中的 .status.currentHealthy 字段被跟踪。

不健康的 Pod 驱逐策略

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

守护应用程序的 PodDisruptionBudget 通过不允许驱逐健康的 Pod 来确保 .status.currentHealthy 的 Pod 数量不低于 .status.desiredHealthy 中指定的数量。通过使用 .spec.unhealthyPodEvictionPolicy, 你还可以定义条件来判定何时应考虑驱逐不健康的 Pod。未指定策略时的默认行为对应于 IfHealthyBudget 策略。

策略包含:

IfHealthyBudget
对于运行中但还不健康的 Pod(.status.phase="Running"),只有所守护的应用程序不受干扰 (.status.currentHealthy 至少等于 .status.desiredHealthy)时才能被驱逐。

此策略确保已受干扰的应用程序所运行的 Pod 会尽可能成为健康。 这对腾空节点有负面影响,可能会因 PDB 守护的应用程序行为错误而阻止腾空。 更具体地说,这些应用程序的 Pod 处于 CrashLoopBackOff 状态 (由于漏洞或错误配置)或其 Pod 只是未能报告 Ready 状况。

AlwaysAllow
运行中但还不健康的 Pod(.status.phase="Running")将被视为已受干扰且可以被驱逐, 与是否满足 PDB 中的判决条件无关。

这意味着受干扰的应用程序所运行的 Pod 可能没有机会恢复健康。 通过使用此策略,集群管理器可以轻松驱逐由 PDB 所守护的行为错误的应用程序。 更具体地说,这些应用程序的 Pod 处于 CrashLoopBackOff 状态 (由于漏洞或错误配置)或其 Pod 只是未能报告 Ready 状况。

任意工作负载和任意选择算符

如果你只针对内置的工作负载资源(Deployment、ReplicaSet、StatefulSet 和 ReplicationController) 或在实现了 scale 子资源自定义资源使用 PDB, 并且 PDB 选择算符与 Pod 所属资源的选择算符完全匹配,那么可以跳过这一节。

你可以针对由其他资源、某个 "operator" 控制的或者“裸的(不受控制器控制)” Pod 使用 PDB,但存在以下限制:

  • 只能够使用 .spec.minAvailable,而不能够使用 .spec.maxUnavailable
  • 只能够使用整数作为 .spec.minAvailable 的值,而不能使用百分比。

你无法使用其他的可用性配置,因为如果没有被支持的属主资源,Kubernetes 无法推导出 Pod 的总数。

你可以使用能够选择属于工作负载资源的 Pod 的子集或超集的选择算符。 驱逐 API 将不允许驱逐被多个 PDB 覆盖的任何 Pod,因此大多数用户都希望避免重叠的选择算符。 重叠 PDB 的一种合理用途是将 Pod 从一个 PDB 转交到另一个 PDB 的场合。

4.8.10 - 从 Pod 中访问 Kubernetes API

本指南演示了如何从 Pod 中访问 Kubernetes API。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

从 Pod 中访问 API

从 Pod 内部访问 API 时,定位 API 服务器和向服务器认证身份的操作与外部客户端场景不同。

从 Pod 使用 Kubernetes API 的最简单的方法就是使用官方的 客户端库。 这些库可以自动发现 API 服务器并进行身份验证。

使用官方客户端库

从一个 Pod 内部连接到 Kubernetes API 的推荐方式为:

在以上场景中,客户端库都使用 Pod 的服务账号凭据来与 API 服务器安全地通信。

直接访问 REST API

在运行在 Pod 中时,你的容器可以通过获取 KUBERNETES_SERVICE_HOSTKUBERNETES_SERVICE_PORT_HTTPS 环境变量为 Kubernetes API 服务器生成一个 HTTPS URL。 API 服务器的集群内地址也发布到 default 命名空间中名为 kubernetes 的 Service 中, 从而 Pod 可以引用 kubernetes.default.svc 作为本地 API 服务器的 DNS 名称。

向 API 服务器进行身份认证的推荐做法是使用 服务账号凭据。 默认情况下,每个 Pod 与一个服务账号关联,该服务账号的凭据(令牌)放置在此 Pod 中每个容器的文件系统树中的 /var/run/secrets/kubernetes.io/serviceaccount/token 处。

如果证书包可用,则凭据包被放入每个容器的文件系统树中的 /var/run/secrets/kubernetes.io/serviceaccount/ca.crt 处, 且将被用于验证 API 服务器的服务证书。

最后,用于命名空间域 API 操作的默认命名空间放置在每个容器中的 /var/run/secrets/kubernetes.io/serviceaccount/namespace 文件中。

使用 kubectl proxy

如果你希望不使用官方客户端库就完成 API 查询,可以将 kubectl proxy 作为 command 在 Pod 中启动一个边车(Sidecar)容器。这样,kubectl proxy 自动完成对 API 的身份认证,并将其暴露到 Pod 的 localhost 接口,从而 Pod 中的其他容器可以直接使用 API。

不使用代理

通过将认证令牌直接发送到 API 服务器,也可以避免运行 kubectl proxy 命令。 内部的证书机制能够为连接提供保护。

# 指向内部 API 服务器的主机名
APISERVER=https://kubernetes.default.svc

# 服务账号令牌的路径
SERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount

# 读取 Pod 的名字空间
NAMESPACE=$(cat ${SERVICEACCOUNT}/namespace)

# 读取服务账号的持有者令牌
TOKEN=$(cat ${SERVICEACCOUNT}/token)

# 引用内部证书机构(CA)
CACERT=${SERVICEACCOUNT}/ca.crt

# 使用令牌访问 API
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api

输出类似于:

{
  "kind": "APIVersions",
  "versions": ["v1"],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "10.0.1.149:443"
    }
  ]
}

4.9 - 运行 Jobs

使用并行处理运行 Jobs。

4.9.1 - 使用 CronJob 运行自动化任务

本页演示如何使用 Kubernetes CronJob 对象运行自动化任务。

准备开始

  • 你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

创建 CronJob

CronJob 需要一个配置文件。 以下是针对一个 CronJob 的清单,该 CronJob 每分钟运行一个简单的演示任务:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "* * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox:1.28
            imagePullPolicy: IfNotPresent
            command:
            - /bin/sh
            - -c
            - date; echo Hello from the Kubernetes cluster
          restartPolicy: OnFailure

执行以下命令以运行此 CronJob 示例:

kubectl create -f https://k8s.io/examples/application/job/cronjob.yaml

输出类似于:

cronjob.batch/hello created

创建好 CronJob 后,使用下面的命令来获取其状态:

kubectl get cronjob hello

输出类似于:

NAME    SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
hello   */1 * * * *   False     0        <none>          10s

就像你从命令返回结果看到的那样,CronJob 还没有调度或执行任何任务。大约需要一分钟任务才能创建好。

kubectl get jobs --watch

输出类似于:

NAME               COMPLETIONS   DURATION   AGE
hello-4111706356   0/1                      0s
hello-4111706356   0/1           0s         0s
hello-4111706356   1/1           5s         5s

现在你已经看到了一个运行中的任务被 “hello” CronJob 调度。 你可以停止监视这个任务,然后再次查看 CronJob 就能看到它调度任务:

kubectl get cronjob hello

输出类似于:

NAME    SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
hello   */1 * * * *   False     0        50s             75s

你应该能看到 hello CronJob 在 LAST SCHEDULE 声明的时间点成功地调度了一次任务。 目前有 0 个活跃的任务,这意味着任务执行完毕或者执行失败。

现在,找到最后一次调度任务创建的 Pod 并查看一个 Pod 的标准输出。

# 在你的系统上将 "hello-4111706356" 替换为 Job 名称
pods=$(kubectl get pods --selector=job-name=hello-4111706356 --output=jsonpath={.items..metadata.name})

查看 Pod 日志:

kubectl logs $pods

输出类似于:

Fri Feb 22 11:02:09 UTC 2019
Hello from the Kubernetes cluster

删除 CronJob

当你不再需要 CronJob 时,可以用 kubectl delete cronjob <cronjob name> 删掉它:

kubectl delete cronjob hello

删除 CronJob 会清除它创建的所有任务和 Pod,并阻止它创建额外的任务。 你可以查阅垃圾收集

4.9.2 - 使用工作队列进行粗粒度并行处理

本例中,你将会运行包含多个并行工作进程的 Kubernetes Job。

本例中,每个 Pod 一旦被创建,会立即从任务队列中取走一个工作单元并完成它,然后将工作单元从队列中删除后再退出。

下面是本次示例的主要步骤:

  1. 启动一个消息队列服务。 本例中,我们使用 RabbitMQ,你也可以用其他的消息队列服务。 在实际工作环境中,你可以创建一次消息队列服务然后在多个任务中重复使用。

  2. 创建一个队列,放上消息数据。 每个消息表示一个要执行的任务。本例中,每个消息是一个整数值。 我们将基于这个整数值执行很长的计算操作。

  3. 启动一个在队列中执行这些任务的 Job。 该 Job 启动多个 Pod。每个 Pod 从消息队列中取走一个任务,处理任务,然后退出。

准备开始

你应当熟悉 Job 的基本用法(非并行的),请参考 Job

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你需要一个容器镜像仓库,用来向其中上传镜像以在集群中运行。

此任务示例还假设你已在本地安装了 Docker。

启动消息队列服务

本例使用了 RabbitMQ,但你可以更改该示例,使用其他 AMQP 类型的消息服务。

在实际工作中,在集群中一次性部署某个消息队列服务,之后在很多 Job 中复用,包括需要长期运行的服务。

按下面的方法启动 RabbitMQ:

# 为 StatefulSet 创建一个 Service 来使用
kubectl create -f https://kubernetes.io/examples/application/job/rabbitmq/rabbitmq-service.yaml
service "rabbitmq-service" created
kubectl create -f https://kubernetes.io/examples/application/job/rabbitmq/rabbitmq-statefulset.yaml
statefulset "rabbitmq" created

测试消息队列服务

现在,我们可以试着访问消息队列。我们将会创建一个临时的可交互的 Pod, 在它上面安装一些工具,然后用队列做实验。

首先创建一个临时的可交互的 Pod:

# 创建一个临时的可交互的 Pod
kubectl run -i --tty temp --image ubuntu:22.04
Waiting for pod default/temp-loe07 to be running, status is Pending, pod ready: false
... [ previous line repeats several times .. hit return when it stops ] ...

请注意你的 Pod 名称和命令提示符将会不同。

接下来安装 amqp-tools,这样你就能用消息队列了。 下面是在该 Pod 的交互式 shell 中需要运行的命令:

apt-get update && apt-get install -y curl ca-certificates amqp-tools python3 dnsutils

后续,你将制作一个包含这些包的容器镜像。

接着,你将要验证可以发现 RabbitMQ 服务:

# 在 Pod 内运行这些命令
# 请注意 rabbitmq-service 拥有一个由 Kubernetes 提供的 DNS 名称:

nslookup rabbitmq-service
Server:        10.0.0.10
Address:    10.0.0.10#53

Name:    rabbitmq-service.default.svc.cluster.local
Address: 10.0.147.152

(IP 地址会有所不同)

如果 kube-dns 插件没有正确安装,上一步可能会出错。 你也可以在环境变量中找到该服务的 IP 地址。

# 在 Pod 内运行此检查
env | grep RABBITMQ_SERVICE | grep HOST
RABBITMQ_SERVICE_SERVICE_HOST=10.0.147.152

(IP 地址会有所不同)

接下来,你将验证是否可以创建队列以及发布和使用消息。

# 在 Pod 内运行这些命令
# 下一行,rabbitmq-service 是访问 rabbitmq-service 的主机名。5672是 rabbitmq 的标准端口。

export BROKER_URL=amqp://guest:guest@rabbitmq-service:5672

# 如果上一步中你不能解析 "rabbitmq-service",可以用下面的命令替换:
BROKER_URL=amqp://guest:guest@$RABBITMQ_SERVICE_SERVICE_HOST:5672

# 现在创建队列:

/usr/bin/amqp-declare-queue --url=$BROKER_URL -q foo -d foo
foo

向队列推送一条消息:

/usr/bin/amqp-publish --url=$BROKER_URL -r foo -p -b Hello

# 然后取回它:

/usr/bin/amqp-consume --url=$BROKER_URL -q foo -c 1 cat && echo
Hello

最后一个命令中,amqp-consume 工具从队列中取走了一个消息,并把该消息传递给了随机命令的标准输出。 在这种情况下,cat 会打印它从标准输入中读取的字符,echo 会添加回车符以便示例可读。

为队列增加任务

现在用一些模拟任务填充队列。在此示例中,任务是多个待打印的字符串。

实践中,消息的内容可以是:

  • 待处理的文件名
  • 程序额外的参数
  • 数据库表的关键字范围
  • 模拟任务的配置参数
  • 待渲染的场景的帧序列号

如果有大量的数据需要被 Job 的所有 Pod 读取,典型的做法是把它们放在一个共享文件系统中, 如 NFS(Network File System 网络文件系统),并以只读的方式挂载到所有 Pod,或者 Pod 中的程序从类似 HDFS (Hadoop Distributed File System 分布式文件系统)的集群文件系统中读取。

例如,你将创建队列并使用 AMQP 命令行工具向队列中填充消息。实践中,你可以写个程序来利用 AMQP 客户端库来填充这些队列。

# 在你的计算机上运行此命令,而不是在 Pod 中
/usr/bin/amqp-declare-queue --url=$BROKER_URL -q job1  -d
job1

将这几项添加到队列中:

for f in apple banana cherry date fig grape lemon melon
do
  /usr/bin/amqp-publish --url=$BROKER_URL -r job1 -p -b $f
done

你给队列中填充了 8 个消息。

创建容器镜像

现在你可以创建一个做为 Job 来运行的镜像。

这个 Job 将用 amqp-consume 实用程序从队列中读取消息并进行实际工作。 这里给出一个非常简单的示例程序:

#!/usr/bin/env python

# Just prints standard out and sleeps for 10 seconds.
import sys
import time
print("Processing " + sys.stdin.readlines()[0])
time.sleep(10)

赋予脚本执行权限:

chmod +x worker.py

现在,编译镜像。创建一个临时目录,切换到这个目录。下载 Dockerfileworker.py。 无论哪种情况,都可以用下面的命令编译镜像:

docker build -t job-wq-1 .

对于 Docker Hub, 给你的应用镜像打上标签, 标签为你的用户名,然后用下面的命令推送到 Hub。用你的 Hub 用户名替换 <username>

docker tag job-wq-1 <username>/job-wq-1
docker push <username>/job-wq-1

如果你使用替代的镜像仓库,请标记该镜像并将其推送到那里。

定义 Job

这里给出一个 Job 的清单。你需要复制一份 Job 清单的副本(将其命名为 ./job.yaml), 并编辑容器镜像的名称以匹配使用的名称。

apiVersion: batch/v1
kind: Job
metadata:
  name: job-wq-1
spec:
  completions: 8
  parallelism: 2
  template:
    metadata:
      name: job-wq-1
    spec:
      containers:
      - name: c
        image: gcr.io/<project>/job-wq-1
        env:
        - name: BROKER_URL
          value: amqp://guest:guest@rabbitmq-service:5672
        - name: QUEUE
          value: job1
      restartPolicy: OnFailure

本例中,每个 Pod 使用队列中的一个消息然后退出。 这样,Job 的完成计数就代表了完成的工作项的数量。 这就是示例清单将 .spec.completions 设置为 8 的原因。

运行 Job

运行 Job:

这假设你已经下载并编辑了清单

kubectl apply -f ./job.yaml

你可以等待 Job 在某个超时时间后成功:

# 状况名称的检查不区分大小写
kubectl wait --for=condition=complete --timeout=300s job/job-wq-1

接下来查看 Job:

kubectl describe jobs/job-wq-1
Name:             job-wq-1
Namespace:        default
Selector:         controller-uid=41d75705-92df-11e7-b85e-fa163ee3c11f
Labels:           controller-uid=41d75705-92df-11e7-b85e-fa163ee3c11f
                  job-name=job-wq-1
Annotations:      <none>
Parallelism:      2
Completions:      8
Start Time:       Wed, 06 Sep 2022 16:42:02 +0000
Pods Statuses:    0 Running / 8 Succeeded / 0 Failed
Pod Template:
  Labels:       controller-uid=41d75705-92df-11e7-b85e-fa163ee3c11f
                job-name=job-wq-1
  Containers:
   c:
    Image:      container-registry.example/causal-jigsaw-637/job-wq-1
    Port:
    Environment:
      BROKER_URL:       amqp://guest:guest@rabbitmq-service:5672
      QUEUE:            job1
    Mounts:             <none>
  Volumes:              <none>
Events:
  FirstSeen  LastSeen   Count    From    SubobjectPath    Type      Reason              Message
  ─────────  ────────   ─────    ────    ─────────────    ──────    ──────              ───────
  27s        27s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-hcobb
  27s        27s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-weytj
  27s        27s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-qaam5
  27s        27s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-b67sr
  26s        26s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-xe5hj
  15s        15s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-w2zqe
  14s        14s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-d6ppa
  14s        14s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-p17e0

该 Job 的所有 Pod 都已成功!你完成了。

替代方案

本文所讲述的处理方法的好处是你不需要修改你的 "worker" 程序使其知道工作队列的存在。 你可以将未修改的工作程序包含在容器镜像中。

使用此方法需要你运行消息队列服务。如果不方便运行消息队列服务, 你也许会考虑另外一种任务模式

本文所述的方法为每个工作项创建了一个 Pod。 如果你的工作项仅需数秒钟,为每个工作项创建 Pod 会增加很多的常规消耗。 考虑另一种设计,例如精细并行工作队列示例, 这种方案可以实现每个 Pod 执行多个工作项。

示例中,你使用了 amqp-consume 从消息队列读取消息并执行真正的程序。 这样的好处是你不需要修改你的程序使其知道队列的存在。 要了解怎样使用客户端库和工作队列通信, 请参考精细并行工作队列示例

友情提醒

如果设置的完成数量小于队列中的消息数量,会导致一部分消息项不会被执行。

如果设置的完成数量大于队列中的消息数量,当队列中所有的消息都处理完成后, Job 也会显示为未完成。Job 将创建 Pod 并阻塞等待消息输入。 你需要建立自己的机制来发现何时有工作要做,并测量队列的大小,设置匹配的完成数量。

当发生下面两种情况时,即使队列中所有的消息都处理完了,Job 也不会显示为完成状态:

  • amqp-consume 命令拿到消息和容器成功退出之间的时间段内,执行杀死容器操作;
  • 在 kubelet 向 API 服务器传回 Pod 成功运行之前,发生节点崩溃。

4.9.3 - 带 Pod 间通信的 Job

在此例中,你将以索引完成模式运行一个 Job, 并通过配置使得该 Job 所创建的各 Pod 之间可以使用 Pod 主机名而不是 Pod IP 地址进行通信。

某 Job 内的 Pod 之间可能需要通信。每个 Pod 中运行的用户工作负载可以查询 Kubernetes API 服务器以获知其他 Pod 的 IP,但使用 Kubernetes 内置的 DNS 解析会更加简单。

索引完成模式下的 Job 自动将 Pod 的主机名设置为 ${jobName}-${completionIndex} 的格式。 你可以使用此格式确定性地构建 Pod 主机名并启用 Pod 通信,无需创建到 Kubernetes 控制平面的客户端连接来通过 API 请求获取 Pod 主机名/IP。

此配置可用于需要 Pod 联网但不想依赖 Kubernetes API 服务器网络连接的使用场景。

准备开始

你应该已熟悉了 Job 的基本用法。

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你的 Kubernetes 服务器版本必须不低于版本 v1.21. 要获知版本信息,请输入 kubectl version.

启动带 Pod 间通信的 Job

要在某 Job 中启用使用 Pod 主机名的 Pod 间通信,你必须执行以下操作:

  1. 对于 Job 所创建的那些 Pod, 使用一个有效的标签选择算符创建无头服务。 该无头服务必须位于与该 Job 相同的名字空间内。 实现这一目的的一种简单的方式是使用 job-name: <任务名称> 作为选择算符, 因为 job-name 标签将由 Kubernetes 自动添加。 此配置将触发 DNS 系统为运行 Job 的 Pod 创建其主机名的记录。

  2. 通过将以下值包括到你的 Job 模板规约中,针对该 Job 的 Pod,将无头服务配置为其子域服务:

    subdomain: <无头服务的名称>
    

示例

以下是启用通过 Pod 主机名来完成 Pod 间通信的 Job 示例。 只有在使用主机名成功 ping 通所有 Pod 之后,此 Job 才会结束。

apiVersion: v1
kind: Service
metadata:
  name: headless-svc
spec:
  clusterIP: None # clusterIP 必须为 None 以创建无头服务
  selector:
    job-name: example-job # 必须与 Job 名称匹配
---
apiVersion: batch/v1
kind: Job
metadata:
  name: example-job
spec:
  completions: 3
  parallelism: 3
  completionMode: Indexed
  template:
    spec:
      subdomain: headless-svc # 必须与 Service 名称匹配
      restartPolicy: Never
      containers:
      - name: example-workload
        image: bash:latest
        command:
        - bash
        - -c
        - |
          for i in 0 1 2
          do
            gotStatus="-1"
            wantStatus="0"             
            while [ $gotStatus -ne $wantStatus ]
            do                                       
              ping -c 1 example-job-${i}.headless-svc > /dev/null 2>&1
              gotStatus=$?                
              if [ $gotStatus -ne $wantStatus ]; then
                echo "Failed to ping pod example-job-${i}.headless-svc, retrying in 1 second..."
                sleep 1
              fi
            done                                                         
            echo "Successfully pinged pod: example-job-${i}.headless-svc"
          done          

应用上述示例之后,使用 <Pod 主机名>.<无头服务名> 通过网络到达彼此。 你应看到类似以下的输出:

kubectl logs example-job-0-qws42
Failed to ping pod example-job-0.headless-svc, retrying in 1 second...
Successfully pinged pod: example-job-0.headless-svc
Successfully pinged pod: example-job-1.headless-svc
Successfully pinged pod: example-job-2.headless-svc

4.9.4 - 使用工作队列进行精细的并行处理

在此示例中,你将运行一个 Kubernetes Job,该 Job 将多个并行任务作为工作进程运行, 每个任务在单独的 Pod 中运行。

在这个例子中,当每个 Pod 被创建时,它会从一个任务队列中获取一个工作单元,处理它,然后重复,直到到达队列的尾部。

下面是这个示例的步骤概述:

  1. 启动存储服务用于保存工作队列。 在这个例子中,你将使用 Redis 来存储工作项。 在上一个例子中, 你使用了 RabbitMQ。 在这个例子中,由于 AMQP 不能为客户端提供一个良好的方法来检测一个有限长度的工作队列是否为空, 你将使用 Redis 和一个自定义的工作队列客户端库。 在实践中,你可能会设置一个类似于 Redis 的存储库,并将其同时用于多项任务或其他事务的工作队列。
  1. 创建一个队列,然后向其中填充消息。 每个消息表示一个将要被处理的工作任务。 在这个例子中,消息是一个我们将用于进行长度计算的整数。
  1. 启动一个 Job 对队列中的任务进行处理。这个 Job 启动了若干个 Pod。 每个 Pod 从消息队列中取出一个工作任务,处理它,然后重复,直到到达队列的尾部。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你将需要一个容器镜像仓库,可以向其中上传镜像以在集群中运行。 此示例使用的是 Docker Hub, 当然你可以将其调整为别的容器镜像仓库。

此任务示例还假设你已在本地安装了 Docker,并使用 Docker 来构建容器镜像。

熟悉基本的、非并行的 Job

启动 Redis

对于这个例子,为了简单起见,你将启动一个单实例的 Redis。 了解如何部署一个可伸缩、高可用的 Redis 例子,请查看 Redis 示例

你也可以直接下载如下文件:

要启动一个 Redis 实例,你需要创建 Redis Pod 和 Redis 服务:

kubectl apply -f https://k8s.io/examples/application/job/redis/redis-pod.yaml
kubectl apply -f https://k8s.io/examples/application/job/redis/redis-service.yaml

使用任务填充队列

现在,让我们往队列里添加一些“任务”。在这个例子中,我们的任务是一些将被打印出来的字符串。

启动一个临时的可交互的 Pod 用于运行 Redis 命令行界面。

kubectl run -i --tty temp --image redis --command "/bin/sh"

输出类似于:

Waiting for pod default/redis2-c7h78 to be running, status is Pending, pod ready: false
Hit enter for command prompt

现在按回车键,启动 Redis 命令行界面,然后创建一个存在若干个工作项的列表。

redis-cli -h redis
redis:6379> rpush job2 "apple"
(integer) 1
redis:6379> rpush job2 "banana"
(integer) 2
redis:6379> rpush job2 "cherry"
(integer) 3
redis:6379> rpush job2 "date"
(integer) 4
redis:6379> rpush job2 "fig"
(integer) 5
redis:6379> rpush job2 "grape"
(integer) 6
redis:6379> rpush job2 "lemon"
(integer) 7
redis:6379> rpush job2 "melon"
(integer) 8
redis:6379> rpush job2 "orange"
(integer) 9
redis:6379> lrange job2 0 -1
1) "apple"
2) "banana"
3) "cherry"
4) "date"
5) "fig"
6) "grape"
7) "lemon"
8) "melon"
9) "orange"

因此,这个键为 job2 的列表就是工作队列。

注意:如果你还没有正确地配置 Kube DNS,你可能需要将上面的第一步改为 redis-cli -h $REDIS_SERVICE_HOST

创建容器镜像

现在你已准备好创建一个镜像来处理该队列中的工作。

你将使用一个带有 Redis 客户端的 Python 工作程序从消息队列中读出消息。

这里提供了一个简单的 Redis 工作队列客户端库,名为 rediswq.py下载)。

Job 中每个 Pod 内的“工作程序” 使用工作队列客户端库获取工作。具体如下:

#!/usr/bin/env python

import time
import rediswq

host="redis"
# 如果你未在运行 Kube-DNS,请取消下面两行的注释
# import os
# host = os.getenv("REDIS_SERVICE_HOST")

q = rediswq.RedisWQ(name="job2", host=host)
print("Worker with sessionID: " +  q.sessionID())
print("Initial queue state: empty=" + str(q.empty()))
while not q.empty():
  item = q.lease(lease_secs=10, block=True, timeout=2)
  if item is not None:
    itemstr = item.decode("utf-8")
    print("Working on " + itemstr)
    time.sleep(10) # 将你的实际工作放在此处来取代 sleep
    q.complete(item)
  else:
    print("Waiting for work")
print("Queue empty, exiting")

你也可以下载 worker.pyrediswq.pyDockerfile 文件。然后构建容器镜像。 以下是使用 Docker 进行镜像构建的示例:

docker build -t job-wq-2 .

Push 镜像

对于 Docker Hub,请先用你的用户名给镜像打上标签, 然后使用下面的命令 push 你的镜像到仓库。请将 <username> 替换为你自己的 Hub 用户名。

docker tag job-wq-2 <username>/job-wq-2
docker push <username>/job-wq-2

你需要将镜像 push 到一个公共仓库或者 配置集群访问你的私有仓库

定义一个 Job

以下是你将创建的 Job 的清单:

apiVersion: batch/v1
kind: Job
metadata:
  name: job-wq-2
spec:
  parallelism: 2
  template:
    metadata:
      name: job-wq-2
    spec:
      containers:
      - name: c
        image: gcr.io/myproject/job-wq-2
      restartPolicy: OnFailure

在这个例子中,每个 Pod 处理了队列中的多个项目,直到队列中没有项目时便退出。 因为是由工作程序自行检测工作队列是否为空,并且 Job 控制器不知道工作队列的存在, 这依赖于工作程序在完成工作时发出信号。 工作程序以成功退出的形式发出信号表示工作队列已经为空。 所以,只要有任意一个工作程序成功退出,控制器就知道工作已经完成了,所有的 Pod 将很快会退出。 因此,你不需要设置 Job 的完成次数。Job 控制器还是会等待其它 Pod 完成。

运行 Job

现在运行这个 Job:

# 这假设你已经下载并编辑了清单
kubectl apply -f ./job.yaml

稍等片刻,然后检查这个 Job:

kubectl describe jobs/job-wq-2
Name:             job-wq-2
Namespace:        default
Selector:         controller-uid=b1c7e4e3-92e1-11e7-b85e-fa163ee3c11f
Labels:           controller-uid=b1c7e4e3-92e1-11e7-b85e-fa163ee3c11f
                  job-name=job-wq-2
Annotations:      <none>
Parallelism:      2
Completions:      <unset>
Start Time:       Mon, 11 Jan 2022 17:07:59 +0000
Pods Statuses:    1 Running / 0 Succeeded / 0 Failed
Pod Template:
  Labels:       controller-uid=b1c7e4e3-92e1-11e7-b85e-fa163ee3c11f
                job-name=job-wq-2
  Containers:
   c:
    Image:              container-registry.example/exampleproject/job-wq-2
    Port:
    Environment:        <none>
    Mounts:             <none>
  Volumes:              <none>
Events:
  FirstSeen    LastSeen    Count    From            SubobjectPath    Type        Reason            Message
  ---------    --------    -----    ----            -------------    --------    ------            -------
  33s          33s         1        {job-controller }                Normal      SuccessfulCreate  Created pod: job-wq-2-lglf8

你可以等待 Job 成功,等待时长有超时限制:

# 状况名称的检查不区分大小写
kubectl wait --for=condition=complete --timeout=300s job/job-wq-2
kubectl logs pods/job-wq-2-7r7b2
Worker with sessionID: bbd72d0a-9e5c-4dd6-abf6-416cc267991f
Initial queue state: empty=False
Working on banana
Working on date
Working on lemon

你可以看到,此 Job 中的一个 Pod 处理了若干个工作单元。

替代方案

如果你不方便运行一个队列服务或者修改你的容器用于运行一个工作队列,你可以考虑其它的 Job 模式

如果你有持续的后台处理业务,那么可以考虑使用 ReplicaSet 来运行你的后台业务, 和运行一个类似 https://github.com/resque/resque 的后台处理库。

4.9.5 - 使用索引作业完成静态工作分配下的并行处理

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

在此示例中,你将运行一个使用多个并行工作进程的 Kubernetes Job。 每个 worker 都是在自己的 Pod 中运行的不同容器。 Pod 具有控制平面自动设置的索引编号(index number), 这些编号使得每个 Pod 能识别出要处理整个任务的哪个部分。

Pod 索引在注解 batch.kubernetes.io/job-completion-index 中呈现,具体表示为一个十进制值字符串。 为了让容器化的任务进程获得此索引,你可以使用 downward API 机制发布注解的值。为方便起见, 控制平面自动设置 Downward API 以在 JOB_COMPLETION_INDEX 环境变量中公开索引。

以下是此示例中步骤的概述:

  1. 定义使用带索引完成信息的 Job 清单。 Downward API 使你可以将 Pod 索引注解作为环境变量或文件传递给容器。
  2. 根据该清单启动一个带索引(Indexed)的 Job

准备开始

你应该已经熟悉 Job 的基本的、非并行的用法。

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你的 Kubernetes 服务器版本必须不低于版本 v1.21. 要获知版本信息,请输入 kubectl version.

选择一种方法

要从工作程序访问工作项,你有几个选项:

  1. 读取 JOB_COMPLETION_INDEX 环境变量。Job 控制器自动将此变量链接到包含完成索引的注解。
  2. 读取包含完整索引的文件。
  3. 假设你无法修改程序,你可以使用脚本包装它, 该脚本使用上述任意方法读取索引并将其转换为程序可以用作输入的内容。

对于此示例,假设你选择了选项 3 并且想要运行 rev 实用程序。 这个程序接受一个文件作为参数并按逆序打印其内容。

rev data.txt

你将使用 busybox 容器镜像中的 rev 工具。

由于这只是一个例子,每个 Pod 只做一小部分工作(反转一个短字符串)。 例如,在实际工作负载中,你可能会创建一个表示基于场景数据制作 60 秒视频任务的 Job 。 此视频渲染 Job 中的每个工作项都将渲染该视频剪辑的特定帧。 索引完成意味着 Job 中的每个 Pod 都知道通过从剪辑开始计算帧数,来确定渲染和发布哪一帧。

定义索引作业

这是一个使用 Indexed 完成模式的示例 Job 清单:

apiVersion: batch/v1
kind: Job
metadata:
  name: 'indexed-job'
spec:
  completions: 5
  parallelism: 3
  completionMode: Indexed
  template:
    spec:
      restartPolicy: Never
      initContainers:
      - name: 'input'
        image: 'docker.io/library/bash'
        command:
        - "bash"
        - "-c"
        - |
          items=(foo bar baz qux xyz)
          echo ${items[$JOB_COMPLETION_INDEX]} > /input/data.txt          
        volumeMounts:
        - mountPath: /input
          name: input
      containers:
      - name: 'worker'
        image: 'docker.io/library/busybox'
        command:
        - "rev"
        - "/input/data.txt"
        volumeMounts:
        - mountPath: /input
          name: input
      volumes:
      - name: input
        emptyDir: {}

在上面的示例中,你使用 Job 控制器为所有容器设置的内置 JOB_COMPLETION_INDEX 环境变量。 Init 容器 将索引映射到一个静态值,并将其写入一个文件,该文件通过 emptyDir 卷 与运行 worker 的容器共享。或者,你可以 通过 Downward API 定义自己的环境变量 将索引发布到容器。你还可以选择从 包含 ConfigMap 的环境变量或文件 加载值列表。

或者也可以直接 使用 Downward API 将注解值作为卷文件传递, 如下例所示:

apiVersion: batch/v1
kind: Job
metadata:
  name: 'indexed-job'
spec:
  completions: 5
  parallelism: 3
  completionMode: Indexed
  template:
    spec:
      restartPolicy: Never
      containers:
      - name: 'worker'
        image: 'docker.io/library/busybox'
        command:
        - "rev"
        - "/input/data.txt"
        volumeMounts:
        - mountPath: /input
          name: input
      volumes:
      - name: input
        downwardAPI:
          items:
          - path: "data.txt"
            fieldRef:
              fieldPath: metadata.annotations['batch.kubernetes.io/job-completion-index']

执行 Job {running-the-job}

现在执行 Job:

# 使用第一种方法(依赖于 $JOB_COMPLETION_INDEX)
kubectl apply -f https://kubernetes.io/examples/application/job/indexed-job.yaml

当你创建此 Job 时,控制平面会创建一系列 Pod,你指定的每个索引都会运行一个 Pod。 .spec.parallelism 的值决定了一次可以运行多少个 Pod, 而 .spec.completions 决定了 Job 总共创建了多少个 Pod。

因为 .spec.parallelism 小于 .spec.completions, 所以控制平面在启动更多 Pod 之前,将等待第一批的某些 Pod 完成。

你可以等待 Job 成功,等待时间可以设置超时限制:

# 状况名称的检查不区分大小写
kubectl wait --for=condition=complete --timeout=300s job/indexed-job

现在,描述 Job 并检查它是否成功。

kubectl describe jobs/indexed-job

输出类似于:

Name:              indexed-job
Namespace:         default
Selector:          controller-uid=bf865e04-0b67-483b-9a90-74cfc4c3e756
Labels:            controller-uid=bf865e04-0b67-483b-9a90-74cfc4c3e756
                   job-name=indexed-job
Annotations:       <none>
Parallelism:       3
Completions:       5
Start Time:        Thu, 11 Mar 2021 15:47:34 +0000
Pods Statuses:     2 Running / 3 Succeeded / 0 Failed
Completed Indexes: 0-2
Pod Template:
  Labels:  controller-uid=bf865e04-0b67-483b-9a90-74cfc4c3e756
           job-name=indexed-job
  Init Containers:
   input:
    Image:      docker.io/library/bash
    Port:       <none>
    Host Port:  <none>
    Command:
      bash
      -c
      items=(foo bar baz qux xyz)
      echo ${items[$JOB_COMPLETION_INDEX]} > /input/data.txt

    Environment:  <none>
    Mounts:
      /input from input (rw)
  Containers:
   worker:
    Image:      docker.io/library/busybox
    Port:       <none>
    Host Port:  <none>
    Command:
      rev
      /input/data.txt
    Environment:  <none>
    Mounts:
      /input from input (rw)
  Volumes:
   input:
    Type:       EmptyDir (a temporary directory that shares a pod's lifetime)
    Medium:
    SizeLimit:  <unset>
Events:
  Type    Reason            Age   From            Message
  ----    ------            ----  ----            -------
  Normal  SuccessfulCreate  4s    job-controller  Created pod: indexed-job-njkjj
  Normal  SuccessfulCreate  4s    job-controller  Created pod: indexed-job-9kd4h
  Normal  SuccessfulCreate  4s    job-controller  Created pod: indexed-job-qjwsz
  Normal  SuccessfulCreate  1s    job-controller  Created pod: indexed-job-fdhq5
  Normal  SuccessfulCreate  1s    job-controller  Created pod: indexed-job-ncslj

在此示例中,你使用每个索引的自定义值运行 Job。 你可以检查其中一个 Pod 的输出:

kubectl logs indexed-job-fdhq5 # 更改它以匹配来自该 Job 的 Pod 的名称

输出类似于:

xuq

4.9.6 - 使用展开的方式进行并行处理

本任务展示基于一个公共的模板运行多个Jobs。 你可以用这种方法来并行执行批处理任务。

在本任务示例中,只有三个工作条目:applebananacherry。 示例任务处理每个条目时打印一个字符串之后结束。

参考在真实负载中使用 Job了解更适用于真实使用场景的模式。

准备开始

你应先熟悉基本的、非并行的 Job 的用法。

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

任务中的基本模板示例要求安装命令行工具 sed。 要使用较高级的模板示例,你需要安装 Python, 并且要安装 Jinja2 模板库。

一旦 Python 已经安装好,你可以运行下面的命令安装 Jinja2:

pip install --user jinja2

基于模板创建 Job

首先,将以下作业模板下载到名为 job-tmpl.yaml 的文件中。

apiVersion: batch/v1
kind: Job
metadata:
  name: process-item-$ITEM
  labels:
    jobgroup: jobexample
spec:
  template:
    metadata:
      name: jobexample
      labels:
        jobgroup: jobexample
    spec:
      containers:
      - name: c
        image: busybox:1.28
        command: ["sh", "-c", "echo Processing item $ITEM && sleep 5"]
      restartPolicy: Never
 # 使用 curl 下载 job-tmpl.yaml
curl -L -s -O https://k8s.io/examples/application/job/job-tmpl.yaml

你所下载的文件不是一个合法的 Kubernetes 清单。 这里的模板只是 Job 对象的 yaml 表示,其中包含一些占位符,在使用它之前需要被填充。 $ITEM 语法对 Kubernetes 没有意义。

基于模板创建清单

下面的 Shell 代码片段使用 sed 将字符串 $ITEM 替换为循环变量, 并将结果写入到一个名为 jobs 的临时目录。

# 展开模板文件到多个文件中,每个文件对应一个要处理的条目
mkdir ./jobs
for i in apple banana cherry
do
  cat job-tmpl.yaml | sed "s/\$ITEM/$i/" > ./jobs/job-$i.yaml
done

检查上述脚本的输出:

ls jobs/

输出类似于:

job-apple.yaml
job-banana.yaml
job-cherry.yaml

你可以使用任何一种模板语言(例如:Jinja2、ERB),或者编写一个程序来 生成 Job 清单。

基于清单创建 Job

接下来用一个 kubectl 命令创建所有的 Job:

kubectl create -f ./jobs

输出类似于:

job.batch/process-item-apple created
job.batch/process-item-banana created
job.batch/process-item-cherry created

现在检查 Job:

kubectl get jobs -l jobgroup=jobexample

输出类似于:

NAME                  COMPLETIONS   DURATION   AGE
process-item-apple    1/1           14s        22s
process-item-banana   1/1           12s        21s
process-item-cherry   1/1           12s        20s

使用 kubectl 的 -l 选项可以仅选择属于当前 Job 组的对象 (系统中可能存在其他不相关的 Job)。

你可以使用相同的 标签选择算符 来过滤 Pods:

kubectl get pods -l jobgroup=jobexample

输出类似于:

NAME                        READY     STATUS      RESTARTS   AGE
process-item-apple-kixwv    0/1       Completed   0          4m
process-item-banana-wrsf7   0/1       Completed   0          4m
process-item-cherry-dnfu9   0/1       Completed   0          4m

我们可以用下面的命令查看所有 Job 的输出:

kubectl logs -f -l jobgroup=jobexample

输出类似于:

Processing item apple
Processing item banana
Processing item cherry

清理

# 删除所创建的 Job
# 集群会自动清理 Job 对应的 Pod
kubectl delete job -l jobgroup=jobexample

使用高级模板参数

第一个例子中,模板的每个示例都有一个参数 而该参数也用在 Job 名称中。不过,对象 名称 被限制只能使用某些字符。

这里的略微复杂的例子使用 Jinja 模板语言 来生成清单,并基于清单来生成对象,每个 Job 都有多个参数。

在本任务中,你将会使用一个一行的 Python 脚本,将模板转换为一组清单文件。

首先,复制下面的 Job 对象模板到一个名为 job.yaml.jinja2 的文件。

{% set params = [{ "name": "apple", "url": "http://dbpedia.org/resource/Apple", },
                  { "name": "banana", "url": "http://dbpedia.org/resource/Banana", },
                  { "name": "cherry", "url": "http://dbpedia.org/resource/Cherry" }]
%}
{% for p in params %}
{% set name = p["name"] %}
{% set url = p["url"] %}
---
apiVersion: batch/v1
kind: Job
metadata:
  name: jobexample-{{ name }}
  labels:
    jobgroup: jobexample
spec:
  template:
    metadata:
      name: jobexample
      labels:
        jobgroup: jobexample
    spec:
      containers:
      - name: c
        image: busybox:1.28
        command: ["sh", "-c", "echo Processing URL {{ url }} && sleep 5"]
      restartPolicy: Never
{% endfor %}

上面的模板使用 python 字典列表(第 1-4 行)定义每个作业对象的参数。 然后使用 for 循环为每组参数(剩余行)生成一个作业 yaml 对象。 我们利用了多个 YAML 文档(这里的 Kubernetes 清单)可以用 --- 分隔符连接的事实。 我们可以将输出直接传递给 kubectl 来创建对象。

接下来我们用单行的 Python 程序将模板展开。

alias render_template='python -c "from jinja2 import Template; import sys; print(Template(sys.stdin.read()).render());"'

使用 render_template 将参数和模板转换成一个 YAML 文件,其中包含 Kubernetes 资源清单:

# 此命令需要之前定义的别名
cat job.yaml.jinja2 | render_template > jobs.yaml

你可以查看 jobs.yaml 以验证 render_template 脚本是否正常工作。

当你对输出结果比较满意时,可以用管道将其输出发送给 kubectl,如下所示:

cat job.yaml.jinja2 | render_template | kubectl apply -f -

Kubernetes 接收清单文件并执行你所创建的 Job。

清理

# 删除所创建的 Job
# 集群会自动清理 Job 对应的 Pod
kubectl delete job -l jobgroup=jobexample

在真实负载中使用 Job

在真实的负载中,每个 Job 都会执行一些重要的计算,例如渲染电影的一帧, 或者处理数据库中的若干行。这时,$ITEM 参数将指定帧号或行范围。

在此任务中,你运行一个命令通过取回 Pod 的日志来收集其输出。 在真实应用场景中,Job 的每个 Pod 都会在结束之前将其输出写入到某持久性存储中。 你可以为每个 Job 指定 PersistentVolume 卷,或者使用其他外部存储服务。 例如,如果你在渲染视频帧,你可能会使用 HTTP 协议将渲染完的帧数据 用 'PUT' 请求发送到某 URL,每个帧使用不同的 URl。

Job 和 Pod 上的标签

你创建了 Job 之后,Kubernetes 自动为 Job 的 Pod 添加 标签,以便能够将一个 Job 的 Pod 与另一个 Job 的 Pod 区分开来。

在本例中,每个 Job 及其 Pod 模板有一个标签:jobgroup=jobexample

Kubernetes 自身对标签名 jobgroup 没有什么要求。 为创建自同一模板的所有 Job 使用同一标签使得我们可以方便地同时操作组中的所有作业。 在第一个例子中,你使用模板来创建了若干 Job。 模板确保每个 Pod 都能够获得相同的标签,这样你可以用一条命令检查这些模板化 Job 所生成的全部 Pod。

替代方案

如果你有计划创建大量 Job 对象,你可能会发现:

  • 即使使用标签,管理这么多 Job 对象也很麻烦。
  • 如果你一次性创建很多 Job,很可能会给 Kubernetes 控制面带来很大压力。 一种替代方案是,Kubernetes API 可能对请求施加速率限制,通过 429 返回 状态值临时拒绝你的请求。
  • 你可能会受到 Job 相关的资源配额 限制:如果你在一个批量请求中触发了太多的任务,API 服务器会永久性地拒绝你的某些请求。

还有一些其他作业模式 可供选择,这些模式都能用来处理大量任务而又不会创建过多的 Job 对象。

你也可以考虑编写自己的控制器 来自动管理 Job 对象。

4.9.7 - 使用 Pod 失效策略处理可重试和不可重试的 Pod 失效

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

本文向你展示如何结合默认的 Pod 回退失效策略来使用 Pod 失效策略, 以改善 Job 内处理容器级别或 Pod 级别的失效。

Pod 失效策略的定义可以帮助你:

  • 避免不必要的 Pod 重试,以更好地利用计算资源。
  • 避免由于 Pod 干扰(例如抢占API 发起的驱逐或基于污点的驱逐) 而造成的 Job 失败。

准备开始

你应该已熟悉了 Job 的基本用法。

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你的 Kubernetes 服务器版本必须不低于版本 v1.25. 要获知版本信息,请输入 kubectl version.

确保特性门控 PodDisruptionConditionsJobPodFailurePolicy 在你的集群中均已启用。

使用 Pod 失效策略以避免不必要的 Pod 重试

借用以下示例,你可以学习在 Pod 失效表明有一个不可重试的软件漏洞时如何使用 Pod 失效策略来避免不必要的 Pod 重启。

首先,基于配置创建一个 Job:

apiVersion: batch/v1
kind: Job
metadata:
  name: job-pod-failure-policy-failjob
spec:
  completions: 8
  parallelism: 2
  template:
    spec:
      restartPolicy: Never
      containers:
      - name: main
        image: docker.io/library/bash:5
        command: ["bash"]
        args:
        - -c
        - echo "Hello world! I'm going to exit with 42 to simulate a software bug." && sleep 30 && exit 42
  backoffLimit: 6
  podFailurePolicy:
    rules:
    - action: FailJob
      onExitCodes:
        containerName: main
        operator: In
        values: [42]

运行以下命令:

kubectl create -f job-pod-failure-policy-failjob.yaml

大约 30 秒后,整个 Job 应被终止。通过运行以下命令来查看 Job 的状态:

kubectl get jobs -l job-name=job-pod-failure-policy-failjob -o yaml

在 Job 状态中,看到一个任务状况为 Failed,其 reason 字段等于 PodFailurePolicy。 此外,message 字段包含有关 Job 终止更详细的信息,例如: Container main for pod default/job-pod-failure-policy-failjob-8ckj8 failed with exit code 42 matching FailJob rule at index 0

为了比较,如果 Pod 失效策略被禁用,将会让 Pod 重试 6 次,用时至少 2 分钟。

清理

删除你创建的 Job:

kubectl delete jobs/job-pod-failure-policy-failjob

集群自动清理这些 Pod。

使用 Pod 失效策略来忽略 Pod 干扰

通过以下示例,你可以学习如何使用 Pod 失效策略将 Pod 重试计数器朝着 .spec.backoffLimit 限制递增来忽略 Pod 干扰。

  1. 基于配置创建 Job:

    apiVersion: batch/v1
    kind: Job
    metadata:
      name: job-pod-failure-policy-ignore
    spec:
      completions: 4
      parallelism: 2
      template:
        spec:
          restartPolicy: Never
          containers:
          - name: main
            image: docker.io/library/bash:5
            command: ["bash"]
            args:
            - -c
            - echo "Hello world! I'm going to exit with 0 (success)." && sleep 90 && exit 0
      backoffLimit: 0
      podFailurePolicy:
        rules:
        - action: Ignore
          onPodConditions:
          - type: DisruptionTarget
    

    运行以下命令:

    kubectl create -f job-pod-failure-policy-ignore.yaml
    
  1. 运行以下这条命令检查将 Pod 调度到的 nodeName

    nodeName=$(kubectl get pods -l job-name=job-pod-failure-policy-ignore -o jsonpath='{.items[0].spec.nodeName}')
    
  1. 腾空该节点以便在 Pod 完成任务之前将其驱逐(90 秒内):

    kubectl drain nodes/$nodeName --ignore-daemonsets --grace-period=0
    
  1. 查看 .status.failed 以检查针对 Job 的计数器未递增:

    kubectl get jobs -l job-name=job-pod-failure-policy-ignore -o yaml
    
  1. 解除节点的保护:

    kubectl uncordon nodes/$nodeName
    

Job 恢复并成功完成。

为了比较,如果 Pod 失效策略被禁用,Pod 干扰将使得整个 Job 终止(随着 .spec.backoffLimit 设置为 0)。

清理

删除你创建的 Job:

kubectl delete jobs/job-pod-failure-policy-ignore

集群自动清理 Pod。

基于自定义 Pod 状况使用 Pod 失效策略避免不必要的 Pod 重试

根据以下示例,你可以学习如何基于自定义 Pod 状况使用 Pod 失效策略避免不必要的 Pod 重启。

  1. 首先基于配置创建一个 Job:

    apiVersion: batch/v1
    kind: Job
    metadata:
      name: job-pod-failure-policy-config-issue
    spec:
      completions: 8
      parallelism: 2
      template:
        spec:
          restartPolicy: Never
          containers:
          - name: main
            image: "non-existing-repo/non-existing-image:example"
      backoffLimit: 6
      podFailurePolicy:
        rules:
        - action: FailJob
          onPodConditions:
          - type: ConfigIssue
    

    执行以下命令:

    kubectl create -f job-pod-failure-policy-config-issue.yaml
    

    请注意,镜像配置不正确,因为该镜像不存在。

  1. 通过执行以下命令检查任务 Pod 的状态:

    kubectl get pods -l job-name=job-pod-failure-policy-config-issue -o yaml
    

    你将看到类似以下输出:

    containerStatuses:
    - image: non-existing-repo/non-existing-image:example
       ...
       state:
       waiting:
          message: Back-off pulling image "non-existing-repo/non-existing-image:example"
          reason: ImagePullBackOff
          ...
    phase: Pending
    

    请注意,Pod 依然处于 Pending 阶段,因为它无法拉取错误配置的镜像。 原则上讲这可能是一个暂时问题,镜像还是会被拉取。然而这种情况下, 镜像不存在,因为我们通过一个自定义状况表明了这个事实。

  1. 添加自定义状况。执行以下命令先准备补丁:

    cat <<EOF > patch.yaml
    status:
      conditions:
      - type: ConfigIssue
        status: "True"
        reason: "NonExistingImage"
        lastTransitionTime: "$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
    EOF
    

    其次,执行以下命令选择通过任务创建的其中一个 Pod:

    podName=$(kubectl get pods -l job-name=job-pod-failure-policy-config-issue -o jsonpath='{.items[0].metadata.name}')
    

    随后执行以下命令将补丁应用到其中一个 Pod 上:

    kubectl patch pod $podName --subresource=status --patch-file=patch.yaml
    

    如果被成功应用,你将看到类似以下的一条通知:

    pod/job-pod-failure-policy-config-issue-k6pvp patched
    
  1. 执行以下命令删除此 Pod 将其过渡到 Failed 阶段:

    kubectl delete pods/$podName
    
  1. 执行以下命令查验 Job 的状态:

    kubectl get jobs -l job-name=job-pod-failure-policy-config-issue -o yaml
    

    在 Job 状态中,看到任务 Failed 状况的 reason 字段等于 PodFailurePolicy。 此外,message 字段包含了与 Job 终止相关的更多详细信息,例如: Pod default/job-pod-failure-policy-config-issue-k6pvp has condition ConfigIssue matching FailJob rule at index 0

清理

删除你创建的 Job:

kubectl delete jobs/job-pod-failure-policy-config-issue

集群自动清理 Pod。

替代方案

通过指定 Job 的 .spec.backoffLimit 字段,你可以完全依赖 Pod 回退失效策略。 然而在许多情况下,难题在于如何找到一个平衡,为 .spec.backoffLimit 设置一个较小的值以避免不必要的 Pod 重试, 同时这个值又足以确保 Job 不会因 Pod 干扰而终止。

4.10 - 访问集群中的应用程序

配置负载平衡、端口转发或设置防火墙或 DNS 配置,以访问集群中的应用程序。

4.10.1 - 部署和访问 Kubernetes 仪表板(Dashboard)

Dashboard 是基于网页的 Kubernetes 用户界面。 你可以使用 Dashboard 将容器应用部署到 Kubernetes 集群中,也可以对容器应用排错,还能管理集群资源。 你可以使用 Dashboard 获取运行在集群中的应用的概览信息,也可以创建或者修改 Kubernetes 资源 (如 Deployment、Job、DaemonSet 等等)。 例如,你可以对 Deployment 实现弹性伸缩、发起滚动升级、重启 Pod 或者使用向导创建新的应用。

Dashboard 同时展示了 Kubernetes 集群中的资源状态信息和所有报错信息。

Kubernetes Dashboard UI

部署 Dashboard UI

默认情况下不会部署 Dashboard,可以通过以下命令部署:

# 添加 kubernetes-dashboard 仓库
helm repo add kubernetes-dashboard https://kubernetes.github.io/dashboard/
# 使用 kubernetes-dashboard Chart 部署名为 `kubernetes-dashboard` 的 Helm Release
helm upgrade --install kubernetes-dashboard kubernetes-dashboard/kubernetes-dashboard --create-namespace --namespace kubernetes-dashboard

访问 Dashboard 用户界面

为了保护你的集群数据,默认情况下,Dashboard 会使用最少的 RBAC 配置进行部署。 当前,Dashboard 仅支持使用 Bearer 令牌登录。 要为此样本演示创建令牌,你可以按照 创建示例用户 上的指南进行操作。

命令行代理

你可以使用 kubectl 命令行工具来启用 Dashboard 访问,命令如下:

kubectl proxy

kubectl 会使得 Dashboard 可以通过 http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/ 访问。

UI 只能通过执行这条命令的机器进行访问。更多选项参见 kubectl proxy --help

欢迎界面

当访问空集群的 Dashboard 时,你会看到欢迎界面。 页面包含一个指向此文档的链接,以及一个用于部署第一个应用程序的按钮。 此外,你可以看到在默认情况下有哪些默认系统应用运行在 kube-system 名字空间 中,比如 Dashboard 自己。

Kubernetes Dashboard 欢迎页面

部署容器化应用

通过一个简单的部署向导,你可以使用 Dashboard 将容器化应用作为一个 Deployment 和可选的 Service 进行创建和部署。你可以手工指定应用的详细配置,或者上传一个包含应用配置的 YAML 或 JSON 清单文件。

点击任何页面右上角的 CREATE 按钮以开始。

指定应用的详细配置

部署向导需要你提供以下信息:

  • 应用名称(必填):应用的名称。内容为 应用名称标签 会被添加到任何将被部署的 Deployment 和 Service。

    在选定的 Kubernetes 名字空间 中, 应用名称必须唯一。必须由小写字母开头,以数字或者小写字母结尾, 并且只含有小写字母、数字和中划线(-)。小于等于24个字符。开头和结尾的空格会被忽略。

  • 容器镜像(必填):公共镜像仓库上的 Docker 容器镜像 或者私有镜像仓库 (通常是 Google Container Registry 或者 Docker Hub)的 URL。容器镜像参数说明必须以冒号结尾。
  • Pod 的数量(必填):你希望应用程序部署的 Pod 的数量。值必须为正整数。

    系统会创建一个 Deployment 以保证集群中运行期望的 Pod 数量。

  • 服务(可选):对于部分应用(比如前端),你可能想对外暴露一个 Service,这个 Service 可能用的是集群之外的公网 IP 地址(外部 Service)。

    其它只能对集群内部可见的 Service 称为内部 Service。

    不管哪种 Service 类型,如果你选择创建一个 Service,而且容器在一个端口上开启了监听(入向的), 那么你需要定义两个端口。创建的 Service 会把(入向的)端口映射到容器可见的目标端口。 该 Service 会把流量路由到你部署的 Pod。支持 TCP 协议和 UDP 协议。 这个 Service 的内部 DNS 解析名就是之前你定义的应用名称的值。

如果需要,你可以打开 Advanced Options 部分,这里你可以定义更多设置:

  • 描述:这里你输入的文本会作为一个 注解 添加到 Deployment,并显示在应用的详细信息中。
  • 标签:应用默认使用的 标签是应用名称和版本。 你可以为 Deployment、Service(如果有)定义额外的标签,比如 release(版本)、 environment(环境)、tier(层级)、partition(分区) 和 release track(版本跟踪)。

    例子:

    release=1.0
    tier=frontend
    environment=pod
    track=stable
    
  • 名字空间:Kubernetes 支持多个虚拟集群依附于同一个物理集群。 这些虚拟集群被称为名字空间, 可以让你将资源划分为逻辑命名的组。

    Dashboard 通过下拉菜单提供所有可用的名字空间,并允许你创建新的名字空间。 名字空间的名称最长可以包含 63 个字母或数字和中横线(-),但是不能包含大写字母。

    名字空间的名称不能只包含数字。如果名字被设置成一个数字,比如 10,pod 就

    在名字空间创建成功的情况下,默认会使用新创建的名字空间。如果创建失败,那么第一个名字空间会被选中。

  • 镜像拉取 Secret:如果要使用私有的 Docker 容器镜像,需要拉取 Secret 凭证。

    Dashboard 通过下拉菜单提供所有可用的 Secret,并允许你创建新的 Secret。 Secret 名称必须遵循 DNS 域名语法,比如 new.image-pull.secret。 Secret 的内容必须是 base64 编码的,并且在一个 .dockercfg 文件中声明。Secret 名称最大可以包含 253 个字符。

    在镜像拉取 Secret 创建成功的情况下,默认会使用新创建的 Secret。 如果创建失败,则不会使用任何 Secret。

  • CPU 需求(核数)内存需求(MiB):你可以为容器定义最小的 资源限制。 默认情况下,Pod 没有 CPU 和内存限制。
  • 运行命令运行命令参数:默认情况下,你的容器会运行 Docker 镜像的默认入口命令。 你可以使用 command 选项覆盖默认值。
  • 以特权模式运行:这个设置决定了在 特权容器 中运行的进程是否像主机中使用 root 运行的进程一样。 特权容器可以使用诸如操纵网络堆栈和访问设备的功能。
  • 环境变量:Kubernetes 通过 环境变量 暴露 Service。你可以构建环境变量,或者将环境变量的值作为参数传递给你的命令。 它们可以被应用用于查找 Service。值可以通过 $(VAR_NAME) 语法关联其他变量。

上传 YAML 或者 JSON 文件

Kubernetes 支持声明式配置。所有的配置都存储在清单文件 (YAML 或者 JSON 配置文件)中。 这些清单使用 Kubernetes API 定义的资源模式。

作为一种替代在部署向导中指定应用详情的方式,你可以在一个或多个清单文件中定义应用,并且使用 Dashboard 上传文件。

使用 Dashboard

以下各节描述了 Kubernetes Dashboard UI 视图;包括它们提供的内容,以及怎么使用它们。

当在集群中定义 Kubernetes 对象时,Dashboard 会在初始视图中显示它们。 默认情况下只会显示默认名字空间中的对象,可以通过更改导航栏菜单中的名字空间筛选器进行改变。

Dashboard 展示大部分 Kubernetes 对象,并将它们分组放在几个菜单类别中。

管理概述

集群和名字空间管理的视图,Dashboard 会列出节点、名字空间和持久卷,并且有它们的详细视图。 节点列表视图包含从所有节点聚合的 CPU 和内存使用的度量值。 详细信息视图显示了一个节点的度量值,它的规格、状态、分配的资源、事件和这个节点上运行的 Pod。

负载

显示选中的名字空间中所有运行的应用。 视图按照负载类型(例如:Deployment、ReplicaSet、StatefulSet)罗列应用,并且每种负载都可以单独查看。 列表总结了关于负载的可执行信息,比如一个 ReplicaSet 的就绪状态的 Pod 数量,或者目前一个 Pod 的内存用量。

工作负载的详情视图展示了对象的状态、详细信息和相互关系。 例如,ReplicaSet 所控制的 Pod,或者 Deployment 所关联的新 ReplicaSet 和 HorizontalPodAutoscalers。

服务

展示允许暴露给外网服务和允许集群内部发现的 Kubernetes 资源。 因此,Service 和 Ingress 视图展示他们关联的 Pod、给集群连接使用的内部端点和给外部用户使用的外部端点。

存储

存储视图展示持久卷申领(PVC)资源,这些资源被应用程序用来存储数据。

ConfigMap 和 Secret

展示的所有 Kubernetes 资源是在集群中运行的应用程序的实时配置。 通过这个视图可以编辑和管理配置对象,并显示那些默认隐藏的 Secret。

日志查看器

Pod 列表和详细信息页面可以链接到 Dashboard 内置的日志查看器。 查看器可以深入查看属于同一个 Pod 的不同容器的日志。

日志浏览

接下来

更多信息,参见 Kubernetes Dashboard 项目页面

4.10.2 - 访问集群

本文阐述多种与集群交互的方法。

使用 kubectl 完成集群的第一次访问

当你第一次访问 Kubernetes API 的时候,我们建议你使用 Kubernetes CLI 工具 kubectl

访问集群时,你需要知道集群的地址并且拥有访问的凭证。通常, 这些在你通过启动安装安装集群时都是自动安装好的, 或者其他人安装时也应该提供了凭证和集群地址。

通过以下命令检查 kubectl 是否知道集群地址及凭证:

kubectl config view

有许多例子介绍了如何使用 kubectl, 可以在 kubectl 参考中找到更完整的文档。

直接访问 REST API

Kubectl 处理 apiserver 的定位和身份验证。 如果要使用 curl 或 wget 等 http 客户端或浏览器直接访问 REST API, 可以通过多种方式查找和验证:

  • 以代理模式运行 kubectl。
    • 推荐此方式。
    • 使用已存储的 apiserver 地址。
    • 使用自签名的证书来验证 apiserver 的身份。杜绝 MITM 攻击。
    • 对 apiserver 进行身份验证。
    • 未来可能会实现智能化的客户端负载均衡和故障恢复。
  • 直接向 http 客户端提供位置和凭证。
    • 可选的方案。
    • 适用于代理可能引起混淆的某些客户端类型。
    • 需要引入根证书到你的浏览器以防止 MITM 攻击。

使用 kubectl proxy

以下命令以反向代理的模式运行 kubectl。它处理 apiserver 的定位和验证。 像这样运行:

kubectl proxy --port=8080

参阅 kubectl proxy 获取更多详细信息。

然后,你可以使用 curl、wget 或浏览器访问 API,如果是 IPv6 则用 [::1] 替换 localhost, 如下所示:

curl http://localhost:8080/api/

输出类似于:

{
  "kind": "APIVersions",
  "versions": [
    "v1"
  ],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "10.0.1.149:443"
    }
  ]
}

不使用 kubectl proxy

使用 kubectl applykubectl describe secret ... 及 grep 和剪切操作来为 default 服务帐户创建令牌,如下所示:

首先,创建 Secret,请求默认 ServiceAccount 的令牌:

kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: default-token
  annotations:
    kubernetes.io/service-account.name: default
type: kubernetes.io/service-account-token
EOF

接下来,等待令牌控制器使用令牌填充 Secret:

while ! kubectl describe secret default-token | grep -E '^token' >/dev/null; do
  echo "waiting for token..." >&2
  sleep 1
done

捕获并使用生成的令牌:

APISERVER=$(kubectl config view --minify | grep server | cut -f 2- -d ":" | tr -d " ")
TOKEN=$(kubectl describe secret default-token | grep -E '^token' | cut -f2 -d':' | tr -d " ")
curl $APISERVER/api --header "Authorization: Bearer $TOKEN" --insecure

输出类似于:

{
  "kind": "APIVersions",
  "versions": [
    "v1"
  ],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "10.0.1.149:443"
    }
  ]
}

jsonpath 方法实现:

APISERVER=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')
TOKEN=$(kubectl get secret default-token -o jsonpath='{.data.token}' | base64 --decode)
curl $APISERVER/api --header "Authorization: Bearer $TOKEN" --insecure

输出类似于:

{
  "kind": "APIVersions",
  "versions": [
    "v1"
  ],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "10.0.1.149:443"
    }
  ]
}

上面的例子使用了 --insecure 参数,这使得它很容易受到 MITM 攻击。 当 kubectl 访问集群时,它使用存储的根证书和客户端证书来访问服务器 (它们安装在 ~/.kube 目录中)。 由于集群证书通常是自签名的,因此可能需要特殊配置才能让你的 http 客户端使用根证书。

在一些集群中,apiserver 不需要身份验证;它可能只服务于 localhost,或者被防火墙保护, 这个没有一定的标准。 配置对 API 的访问 描述了集群管理员如何进行配置。此类方法可能与未来的高可用性支持相冲突。

以编程方式访问 API

Kubernetes 官方提供对 GoPython 的客户端库支持。

Go 客户端

  • 想要获得这个库,请运行命令:go get k8s.io/client-go@kubernetes-<kubernetes-version-number>, 有关详细安装说明,请参阅 INSTALL.md。 请参阅 https://github.com/kubernetes/client-go 以查看支持的版本。
  • 基于这个 client-go 客户端库编写应用程序。 请注意,client-go 定义了自己的 API 对象,因此如果需要,请从 client-go 而不是从主存储库 导入 API 定义,例如,import "k8s.io/client-go/kubernetes" 才是对的。

Go 客户端可以像 kubectl CLI 一样使用相同的 kubeconfig 文件 来定位和验证 apiserver。可参阅 示例

如果应用程序以 Pod 的形式部署在集群中,那么请参阅 下一章

Python 客户端

如果想要使用 Python 客户端, 请运行命令:pip install kubernetes。参阅 Python Client Library page 以获得更详细的安装参数。

Python 客户端可以像 kubectl CLI 一样使用相同的 kubeconfig 文件 来定位和验证 apiserver,可参阅 示例

其它语言

目前有多个客户端库 为其它语言提供访问 API 的方法。 参阅其它库的相关文档以获取他们是如何验证的。

从 Pod 中访问 API

当你从 Pod 中访问 API 时,定位和验证 API 服务器会有些许不同。

请参阅从 Pod 中访问 API 了解更多详情。

访问集群上运行的服务

上一节介绍了如何连接到 Kubernetes API 服务器。 有关连接到 Kubernetes 集群上运行的其他服务的信息, 请参阅访问集群服务

请求重定向

重定向功能已弃用并被删除。请改用代理(见下文)。

多种代理

使用 Kubernetes 时可能会遇到几种不同的代理:

  1. kubectl 代理

    • 在用户的桌面或 Pod 中运行
    • 代理从本地主机地址到 Kubernetes apiserver
    • 客户端到代理将使用 HTTP
    • 代理到 apiserver 使用 HTTPS
    • 定位 apiserver
    • 添加身份验证头部
  1. apiserver 代理

    • 内置于 apiserver 中
    • 将集群外部的用户连接到集群 IP,否则这些 IP 可能无法访问
    • 运行在 apiserver 进程中
    • 客户端代理使用 HTTPS(也可配置为 http)
    • 代理将根据可用的信息决定使用 HTTP 或者 HTTPS 代理到目标
    • 可用于访问节点、Pod 或服务
    • 在访问服务时进行负载平衡
  1. kube proxy

    • 运行在每个节点上
    • 代理 UDP 和 TCP
    • 不能代理 HTTP
    • 提供负载均衡
    • 只能用来访问服务
  1. 位于 apiserver 之前的 Proxy/Load-balancer:

    • 存在和实现因集群而异(例如 nginx)
    • 位于所有客户和一个或多个 apiserver 之间
    • 如果有多个 apiserver,则充当负载均衡器
  1. 外部服务上的云负载均衡器:

    • 由一些云提供商提供(例如 AWS ELB,Google Cloud Load Balancer)
    • 当 Kubernetes 服务类型为 LoadBalancer 时自动创建
    • 只使用 UDP/TCP
    • 具体实现因云提供商而异。

除了前两种类型之外,Kubernetes 用户通常不需要担心任何其他问题。 集群管理员通常会确保后者的正确配置。

4.10.3 - 配置对多集群的访问

本文展示如何使用配置文件来配置对多个集群的访问。 在将集群、用户和上下文定义在一个或多个配置文件中之后,用户可以使用 kubectl config use-context 命令快速地在集群之间进行切换。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要检查 kubectl 是否安装, 执行 kubectl version --client 命令。kubectl 的版本应该与集群的 API 服务器使用同一次版本号

定义集群、用户和上下文

假设用户有两个集群,一个用于开发工作(development),一个用于测试工作(test)。 在 development 集群中,前端开发者在名为 frontend 的名字空间下工作, 存储开发者在名为 storage 的名字空间下工作。在 test 集群中, 开发人员可能在默认名字空间下工作,也可能视情况创建附加的名字空间。 访问开发集群需要通过证书进行认证。 访问测试集群需要通过用户名和密码进行认证。

创建名为 config-exercise 的目录。在 config-exercise 目录中,创建名为 config-demo 的文件,其内容为:

apiVersion: v1
kind: Config
preferences: {}

clusters:
- cluster:
  name: development
- cluster:
  name: test

users:
- name: developer
- name: experimenter

contexts:
- context:
  name: dev-frontend
- context:
  name: dev-storage
- context:
  name: exp-test

配置文件描述了集群、用户名和上下文。config-demo 文件中含有描述两个集群、 两个用户和三个上下文的框架。

进入 config-exercise 目录。输入以下命令,将集群详细信息添加到配置文件中:

kubectl config --kubeconfig=config-demo set-cluster development --server=https://1.2.3.4 --certificate-authority=fake-ca-file
kubectl config --kubeconfig=config-demo set-cluster test --server=https://5.6.7.8 --insecure-skip-tls-verify

将用户详细信息添加到配置文件中:

kubectl config --kubeconfig=config-demo set-credentials developer --client-certificate=fake-cert-file --client-key=fake-key-seefile
kubectl config --kubeconfig=config-demo set-credentials experimenter --username=exp --password=some-password

将上下文详细信息添加到配置文件中:

kubectl config --kubeconfig=config-demo set-context dev-frontend --cluster=development --namespace=frontend --user=developer
kubectl config --kubeconfig=config-demo set-context dev-storage --cluster=development --namespace=storage --user=developer
kubectl config --kubeconfig=config-demo set-context exp-test --cluster=test --namespace=default --user=experimenter

打开 config-demo 文件查看添加的详细信息。也可以使用 config view 命令进行查看:

kubectl config --kubeconfig=config-demo view

输出展示了两个集群、两个用户和三个上下文:

apiVersion: v1
clusters:
- cluster:
    certificate-authority: fake-ca-file
    server: https://1.2.3.4
  name: development
- cluster:
    insecure-skip-tls-verify: true
    server: https://5.6.7.8
  name: test
contexts:
- context:
    cluster: development
    namespace: frontend
    user: developer
  name: dev-frontend
- context:
    cluster: development
    namespace: storage
    user: developer
  name: dev-storage
- context:
    cluster: test
    namespace: default
    user: experimenter
  name: exp-test
current-context: ""
kind: Config
preferences: {}
users:
- name: developer
  user:
    client-certificate: fake-cert-file
    client-key: fake-key-file
- name: experimenter
  user:
    # 文档说明(本注释不属于命令输出)。
    # 将密码保存到 Kubernetes 客户端配置有风险。
    # 一个较好的替代方式是使用凭据插件并单独保存这些凭据。
    # 参阅 https://kubernetes.io/zh-cn/docs/reference/access-authn-authz/authentication/#client-go-credential-plugins
    password: some-password
    username: exp

其中的 fake-ca-filefake-cert-filefake-key-file 是证书文件路径名的占位符。 你需要更改这些值,使之对应你的环境中证书文件的实际路径名。

有时你可能希望在这里使用 BASE64 编码的数据而不是一个个独立的证书文件。 如果是这样,你需要在键名上添加 -data 后缀。例如, certificate-authority-dataclient-certificate-dataclient-key-data

每个上下文包含三部分(集群、用户和名字空间),例如, dev-frontend 上下文表明:使用 developer 用户的凭证来访问 development 集群的 frontend 名字空间。

设置当前上下文:

kubectl config --kubeconfig=config-demo use-context dev-frontend

现在当输入 kubectl 命令时,相应动作会应用于 dev-frontend 上下文中所列的集群和名字空间, 同时,命令会使用 dev-frontend 上下文中所列用户的凭证。

使用 --minify 参数,来查看与当前上下文相关联的配置信息。

kubectl config --kubeconfig=config-demo view --minify

输出结果展示了 dev-frontend 上下文相关的配置信息:

apiVersion: v1
clusters:
- cluster:
    certificate-authority: fake-ca-file
    server: https://1.2.3.4
  name: development
contexts:
- context:
    cluster: development
    namespace: frontend
    user: developer
  name: dev-frontend
current-context: dev-frontend
kind: Config
preferences: {}
users:
- name: developer
  user:
    client-certificate: fake-cert-file
    client-key: fake-key-file

现在假设用户希望在测试集群中工作一段时间。

将当前上下文更改为 exp-test

kubectl config --kubeconfig=config-demo use-context exp-test

现在你发出的所有 kubectl 命令都将应用于 test 集群的默认名字空间。 同时,命令会使用 exp-test 上下文中所列用户的凭证。

查看更新后的当前上下文 exp-test 相关的配置:

kubectl config --kubeconfig=config-demo view --minify

最后,假设用户希望在 development 集群中的 storage 名字空间下工作一段时间。

将当前上下文更改为 dev-storage

kubectl config --kubeconfig=config-demo use-context dev-storage

查看更新后的当前上下文 dev-storage 相关的配置:

kubectl config --kubeconfig=config-demo view --minify

创建第二个配置文件

config-exercise 目录中,创建名为 config-demo-2 的文件,其中包含以下内容:

apiVersion: v1
kind: Config
preferences: {}

contexts:
- context:
    cluster: development
    namespace: ramp
    user: developer
  name: dev-ramp-up

上述配置文件定义了一个新的上下文,名为 dev-ramp-up

设置 KUBECONFIG 环境变量

查看是否有名为 KUBECONFIG 的环境变量。 如有,保存 KUBECONFIG 环境变量当前的值,以便稍后恢复。 例如:

Linux

export KUBECONFIG_SAVED="$KUBECONFIG"

Windows PowerShell

$Env:KUBECONFIG_SAVED=$ENV:KUBECONFIG

KUBECONFIG 环境变量是配置文件路径的列表,该列表在 Linux 和 Mac 中以冒号分隔, 在 Windows 中以分号分隔。 如果有 KUBECONFIG 环境变量,请熟悉列表中的配置文件。

临时添加两条路径到 KUBECONFIG 环境变量中。例如:

Linux

export KUBECONFIG="${KUBECONFIG}:config-demo:config-demo-2"

Windows PowerShell

$Env:KUBECONFIG=("config-demo;config-demo-2")

config-exercise 目录中输入以下命令:

kubectl config view

输出展示了 KUBECONFIG 环境变量中所列举的所有文件合并后的信息。 特别地,注意合并信息中包含来自 config-demo-2 文件的 dev-ramp-up 上下文和来自 config-demo 文件的三个上下文:

contexts:
- context:
    cluster: development
    namespace: frontend
    user: developer
  name: dev-frontend
- context:
    cluster: development
    namespace: ramp
    user: developer
  name: dev-ramp-up
- context:
    cluster: development
    namespace: storage
    user: developer
  name: dev-storage
- context:
    cluster: test
    namespace: default
    user: experimenter
  name: exp-test

关于 kubeconfig 文件如何合并的更多信息, 请参考使用 kubeconfig 文件组织集群访问

探索 $HOME/.kube 目录

如果用户已经拥有一个集群,可以使用 kubectl 与集群进行交互, 那么很可能在 $HOME/.kube 目录下有一个名为 config 的文件。

进入 $HOME/.kube 目录,看看那里有什么文件。通常会有一个名为 config 的文件,目录中可能还有其他配置文件。请简单地熟悉这些文件的内容。

将 $HOME/.kube/config 追加到 KUBECONFIG 环境变量中

如果有 $HOME/.kube/config 文件,并且还未列在 KUBECONFIG 环境变量中, 那么现在将它追加到 KUBECONFIG 环境变量中。例如:

Linux

export KUBECONFIG="${KUBECONFIG}:${HOME}/.kube/config"

Windows Powershell

$Env:KUBECONFIG="$Env:KUBECONFIG;$HOME\.kube\config"

在配置练习目录中输入以下命令,查看当前 KUBECONFIG 环境变量中列举的所有文件合并后的配置信息:

kubectl config view

清理

KUBECONFIG 环境变量还原为原始值。例如:

Linux

export KUBECONFIG="$KUBECONFIG_SAVED"

Windows PowerShell

$Env:KUBECONFIG=$ENV:KUBECONFIG_SAVED

检查 kubeconfig 所表示的主体

你在通过集群的身份验证后将获得哪些属性(用户名、组),这一点并不总是很明显。 如果你同时管理多个集群,这可能会更具挑战性。

对于你所选择的 Kubernetes 客户端上下文,有一个 kubectl 子命令可以检查用户名等主体属性: kubectl alpha auth whoami

更多细节请参阅通过 API 访问客户端的身份验证信息

接下来

4.10.4 - 使用端口转发来访问集群中的应用

本文展示如何使用 kubectl port-forward 连接到在 Kubernetes 集群中运行的 MongoDB 服务。 这种类型的连接对数据库调试很有用。

准备开始

  • 你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

    你的 Kubernetes 服务器版本必须不低于版本 v1.10. 要获知版本信息,请输入 kubectl version.

创建 MongoDB Deployment 和服务

  1. 创建一个运行 MongoDB 的 Deployment:

    kubectl apply -f https://k8s.io/examples/application/mongodb/mongo-deployment.yaml
    

    成功执行的命令的输出可以证明创建了 Deployment:

    deployment.apps/mongo created
    

    查看 Pod 状态,检查其是否准备就绪:

    kubectl get pods
    

    输出显示创建的 Pod:

    NAME                     READY   STATUS    RESTARTS   AGE
    mongo-75f59d57f4-4nd6q   1/1     Running   0          2m4s
    

    查看 Deployment 状态:

    kubectl get deployment
    

    输出显示创建的 Deployment:

    NAME    READY   UP-TO-DATE   AVAILABLE   AGE
    mongo   1/1     1            1           2m21s
    

    该 Deployment 自动管理一个 ReplicaSet。查看该 ReplicaSet 的状态:

    kubectl get replicaset
    

    输出显示 ReplicaSet 已被创建:

    NAME               DESIRED   CURRENT   READY   AGE
    mongo-75f59d57f4   1         1         1       3m12s
    
  1. 创建一个在网络上公开的 MongoDB 服务:

    kubectl apply -f https://k8s.io/examples/application/mongodb/mongo-service.yaml
    

    成功执行的命令的输出可以证明 Service 已经被创建:

    service/mongo created
    

    检查所创建的 Service:

    kubectl get service mongo
    

    输出显示已被创建的 Service:

    NAME    TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)     AGE
    mongo   ClusterIP   10.96.41.183   <none>        27017/TCP   11s
    
  1. 验证 MongoDB 服务是否运行在 Pod 中并且在监听 27017 端口:

    # 将 mongo-75f59d57f4-4nd6q 改为 Pod 的名称
    kubectl get pod mongo-75f59d57f4-4nd6q --template='{{(index (index .spec.containers 0).ports 0).containerPort}}{{"\n"}}'
    

    输出应该显示对应 Pod 中 MongoDB 的端口:

    27017
    

    27017 是 MongoDB 的官方 TCP 端口。

转发一个本地端口到 Pod 端口

  1. kubectl port-forward 允许使用资源名称 (例如 Pod 名称)来选择匹配的 Pod 来进行端口转发。

    # 将 mongo-75f59d57f4-4nd6q 改为 Pod 的名称
    kubectl port-forward mongo-75f59d57f4-4nd6q 28015:27017
    

    这相当于

    kubectl port-forward pods/mongo-75f59d57f4-4nd6q 28015:27017
    

    或者

    kubectl port-forward deployment/mongo 28015:27017
    

    或者

    kubectl port-forward replicaset/mongo-75f59d57f4 28015:27017
    

    或者

    kubectl port-forward service/mongo 28015:27017
    

    以上所有命令都有效。输出类似于:

    Forwarding from 127.0.0.1:28015 -> 27017
    Forwarding from [::1]:28015 -> 27017
    
  1. 启动 MongoDB 命令行接口:

    mongosh --port 28015
    
  1. 在 MongoDB 命令行提示符下,输入 ping 命令:

    db.runCommand( { ping: 1 } )
    

    成功的 ping 请求应该返回:

    { ok: 1 }
    

(可选操作)让 kubectl 来选择本地端口

如果你不需要指定特定的本地端口,你可以让 kubectl 来选择和分配本地端口, 这样你就不需要管理本地端口冲突。该命令使用稍微不同的语法:

kubectl port-forward deployment/mongo :27017

kubectl 工具会找到一个未被使用的本地端口号(避免使用低段位的端口号, 因为他们可能会被其他应用程序使用)。输出类似于:

Forwarding from 127.0.0.1:63753 -> 27017
Forwarding from [::1]:63753 -> 27017

讨论

与本地 28015 端口建立的连接将被转发到运行 MongoDB 服务器的 Pod 的 27017 端口。 通过此连接,你可以使用本地工作站来调试在 Pod 中运行的数据库。

接下来

进一步了解 kubectl port-forward

4.10.5 - 使用服务来访问集群中的应用

本文展示如何创建一个 Kubernetes 服务对象,能让外部客户端访问在集群中运行的应用。 该服务为一个应用的两个运行实例提供负载均衡。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

教程目标

  • 运行 Hello World 应用的两个实例。
  • 创建一个服务对象来暴露 NodePort。
  • 使用服务对象来访问正在运行的应用。

为运行在两个 Pod 中的应用创建一个服务

这是应用程序部署的配置文件:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-world
spec:
  selector:
    matchLabels:
      run: load-balancer-example
  replicas: 2
  template:
    metadata:
      labels:
        run: load-balancer-example
    spec:
      containers:
        - name: hello-world
          image: gcr.io/google-samples/node-hello:1.0
          ports:
            - containerPort: 8080
              protocol: TCP
  1. 在你的集群中运行一个 Hello World 应用。 使用上面的文件创建应用程序 Deployment:

    kubectl apply -f https://k8s.io/examples/service/access/hello-application.yaml
    

    上面的命令创建一个 Deployment 对象 和一个关联的 ReplicaSet 对象。 这个 ReplicaSet 有两个 Pod, 每个 Pod 都运行着 Hello World 应用。

  1. 展示 Deployment 的信息:

    kubectl get deployments hello-world
    kubectl describe deployments hello-world
    
  1. 展示你的 ReplicaSet 对象信息:

    kubectl get replicasets
    kubectl describe replicasets
    
  1. 创建一个服务对象来暴露 Deployment:

    kubectl expose deployment hello-world --type=NodePort --name=example-service
    
  1. 展示 Service 信息:

    kubectl describe services example-service
    

    输出类似于:

    Name:                   example-service
    Namespace:              default
    Labels:                 run=load-balancer-example
    Annotations:            <none>
    Selector:               run=load-balancer-example
    Type:                   NodePort
    IP:                     10.32.0.16
    Port:                   <unset> 8080/TCP
    TargetPort:             8080/TCP
    NodePort:               <unset> 31496/TCP
    Endpoints:              10.200.1.4:8080,10.200.2.5:8080
    Session Affinity:       None
    Events:                 <none>
    

    注意 Service 中的 NodePort 值。例如在上面的输出中,NodePort 值是 31496。

  1. 列出运行 Hello World 应用的 Pod:

    kubectl get pods --selector="run=load-balancer-example" --output=wide
    

    输出类似于:

    NAME                           READY   STATUS    ...  IP           NODE
    hello-world-2895499144-bsbk5   1/1     Running   ...  10.200.1.4   worker1
    hello-world-2895499144-m1pwt   1/1     Running   ...  10.200.2.5   worker2
    
  1. 获取运行 Hello World 的 pod 的其中一个节点的公共 IP 地址。如何获得此地址取决于你设置集群的方式。 例如,如果你使用的是 Minikube,则可以通过运行 kubectl cluster-info 来查看节点地址。 如果你使用的是 Google Compute Engine 实例, 则可以使用 gcloud compute instances list 命令查看节点的公共地址。

  2. 在你选择的节点上,创建一个防火墙规则以开放节点端口上的 TCP 流量。 例如,如果你的服务的 NodePort 值为 31568,请创建一个防火墙规则以允许 31568 端口上的 TCP 流量。 不同的云提供商提供了不同方法来配置防火墙规则。

  3. 使用节点地址和 node port 来访问 Hello World 应用:

    curl http://<public-node-ip>:<node-port>
    

    这里的 <public-node-ip> 是你节点的公共 IP 地址,<node-port> 是你服务的 NodePort 值。 对于请求成功的响应是一个 hello 消息:

    Hello Kubernetes!
    

使用服务配置文件

作为 kubectl expose 的替代方法, 你可以使用服务配置文件来创建服务。

清理现场

想要删除服务,输入以下命令:

kubectl delete services example-service

想要删除运行 Hello World 应用的 Deployment、ReplicaSet 和 Pod,输入以下命令:

kubectl delete deployment hello-world

接下来

跟随教程使用 Service 连接到应用

4.10.6 - 使用 Service 把前端连接到后端

本任务会描述如何创建前端(Frontend)微服务和后端(Backend)微服务。后端微服务是一个 hello 欢迎程序。 前端通过 nginx 和一个 Kubernetes 服务 暴露后端所提供的服务。

教程目标

  • 使用部署对象(Deployment object)创建并运行一个 hello 后端微服务
  • 使用一个 Service 对象将请求流量发送到后端微服务的多个副本
  • 同样使用一个 Deployment 对象创建并运行一个 nginx 前端微服务
  • 配置前端微服务将请求流量发送到后端微服务
  • 使用 type=LoadBalancer 的 Service 对象将前端微服务暴露到集群外部

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

本任务使用外部负载均衡服务, 所以需要对应的可支持此功能的环境。如果你的环境不能支持,你可以使用 NodePort 类型的服务代替。

使用部署对象(Deployment)创建后端

后端是一个简单的 hello 欢迎微服务应用。这是后端应用的 Deployment 配置文件:

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
spec:
  selector:
    matchLabels:
      app: hello
      tier: backend
      track: stable
  replicas: 3
  template:
    metadata:
      labels:
        app: hello
        tier: backend
        track: stable
    spec:
      containers:
        - name: hello
          image: "gcr.io/google-samples/hello-go-gke:1.0"
          ports:
            - name: http
              containerPort: 80
...

创建后端 Deployment:

kubectl apply -f https://k8s.io/examples/service/access/backend-deployment.yaml

查看后端的 Deployment 信息:

kubectl describe deployment backend

输出类似于:

Name:                           backend
Namespace:                      default
CreationTimestamp:              Mon, 24 Oct 2016 14:21:02 -0700
Labels:                         app=hello
                                tier=backend
                                track=stable
Annotations:                    deployment.kubernetes.io/revision=1
Selector:                       app=hello,tier=backend,track=stable
Replicas:                       3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType:                   RollingUpdate
MinReadySeconds:                0
RollingUpdateStrategy:          1 max unavailable, 1 max surge
Pod Template:
  Labels:       app=hello
                tier=backend
                track=stable
  Containers:
   hello:
    Image:              "gcr.io/google-samples/hello-go-gke:1.0"
    Port:               80/TCP
    Environment:        <none>
    Mounts:             <none>
  Volumes:              <none>
Conditions:
  Type          Status  Reason
  ----          ------  ------
  Available     True    MinimumReplicasAvailable
  Progressing   True    NewReplicaSetAvailable
OldReplicaSets:                 <none>
NewReplicaSet:                  hello-3621623197 (3/3 replicas created)
Events:
...

创建 hello Service 对象

将请求从前端发送到后端的关键是后端 Service。Service 创建一个固定 IP 和 DNS 解析名入口, 使得后端微服务总是可达。Service 使用 选择算符 来寻找目标 Pod。

首先,浏览 Service 的配置文件:

---
apiVersion: v1
kind: Service
metadata:
  name: hello
spec:
  selector:
    app: hello
    tier: backend
  ports:
  - protocol: TCP
    port: 80
    targetPort: http
...

配置文件中,你可以看到名为 hello 的 Service 将流量路由到包含 app: hellotier: backend 标签的 Pod。

创建后端 Service:

kubectl apply -f https://k8s.io/examples/service/access/backend-service.yaml

此时,你已经有了一个运行着 hello 应用的三个副本的 backend Deployment,你也有了 一个 Service 用于路由网络流量。不过,这个服务在集群外部无法访问也无法解析。

创建前端

现在你已经有了运行中的后端应用,你可以创建一个可在集群外部访问的前端,并通过代理 前端的请求连接到后端。

前端使用被赋予后端 Service 的 DNS 名称将请求发送到后端工作 Pods。这一 DNS 名称为 hello,也就是 examples/service/access/backend-service.yaml 配置 文件中 name 字段的取值。

前端 Deployment 中的 Pods 运行一个 nginx 镜像,这个已经配置好的镜像会将请求转发 给后端的 hello Service。下面是 nginx 的配置文件:

# Backend 是 nginx 的内部标识符,用于命名以下特定的 upstream
upstream Backend {
    # hello 是 Kubernetes 中的后端服务所使用的内部 DNS 名称
    server hello;
}

server { listen 80;

location / {
    # 以下语句将流量通过代理方式转发到名为 Backend 的上游
    proxy_pass http://Backend;
}

}

与后端类似,前端用包含一个 Deployment 和一个 Service。后端与前端服务之间的一个 重要区别是前端 Service 的配置文件包含了 type: LoadBalancer,也就是说,Service 会使用你的云服务商的默认负载均衡设备,从而实现从集群外访问的目的。

---
apiVersion: v1
kind: Service
metadata:
  name: frontend
spec:
  selector:
    app: hello
    tier: frontend
  ports:
  - protocol: "TCP"
    port: 80
    targetPort: 80
  type: LoadBalancer
...
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  selector:
    matchLabels:
      app: hello
      tier: frontend
      track: stable
  replicas: 1
  template:
    metadata:
      labels:
        app: hello
        tier: frontend
        track: stable
    spec:
      containers:
        - name: nginx
          image: "gcr.io/google-samples/hello-frontend:1.0"
          lifecycle:
            preStop:
              exec:
                command: ["/usr/sbin/nginx","-s","quit"]
...

创建前端 Deployment 和 Service:

kubectl apply -f https://k8s.io/examples/service/access/frontend-deployment.yaml
kubectl apply -f https://k8s.io/examples/service/access/frontend-service.yaml

通过输出确认两个资源都已经被创建:

deployment.apps/frontend created
service/frontend created

与前端 Service 交互

一旦你创建了 LoadBalancer 类型的 Service,你可以使用这条命令查看外部 IP:

kubectl get service frontend --watch

外部 IP 字段的生成可能需要一些时间。如果是这种情况,外部 IP 会显示为 <pending>

NAME       TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)  AGE
frontend   LoadBalancer   10.51.252.116   <pending>     80/TCP   10s

当外部 IP 地址被分配可用时,配置会更新,在 EXTERNAL-IP 头部下显示新的 IP:

NAME       TYPE           CLUSTER-IP      EXTERNAL-IP        PORT(S)  AGE
frontend   LoadBalancer   10.51.252.116   XXX.XXX.XXX.XXX    80/TCP   1m

这一新的 IP 地址就可以用来从集群外与 frontend 服务交互了。

通过前端发送流量

前端和后端已经完成连接了。你可以使用 curl 命令通过你的前端 Service 的外部 IP 访问服务端点。

curl http://${EXTERNAL_IP} # 将 EXTERNAL_IP 替换为你之前看到的外部 IP

输出显示后端生成的消息:

{"message":"Hello"}

清理现场

要删除服务,输入下面的命令:

kubectl delete services frontend backend

要删除在前端和后端应用中运行的 Deployment、ReplicaSet 和 Pod,输入下面的命令:

kubectl delete deployment frontend backend

接下来

4.10.7 - 创建外部负载均衡器

本文展示如何创建一个外部负载均衡器。

创建服务时,你可以选择自动创建云网络负载均衡器。 负载均衡器提供外部可访问的 IP 地址,可将流量发送到集群节点上的正确端口上 (假设集群在支持的环境中运行,并配置了正确的云负载均衡器驱动包)。

你还可以使用 Ingress 代替 Service。 更多信息,请参阅 Ingress 文档。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你的集群必须在已经支持配置外部负载均衡器的云或其他环境中运行。

创建服务

基于清单文件创建服务

要创建外部负载均衡器,请将以下内容添加到你的 Service 清单文件:

    type: LoadBalancer

你的清单文件可能会如下所示:

apiVersion: v1
kind: Service
metadata:
  name: example-service
spec:
  selector:
    app: example
  ports:
    - port: 8765
      targetPort: 9376
  type: LoadBalancer

使用 kubectl 创建 Service

你也可以使用 kubectl expose 命令及其 --type=LoadBalancer 参数创建服务:

kubectl expose deployment example --port=8765 --target-port=9376 \
        --name=example-service --type=LoadBalancer

此命令通过使用与引用资源(在上面的示例的情况下,名为 exampleDeployment) 相同的选择器来创建一个新的服务。

更多信息(包括更多的可选参数),请参阅 kubectl expose 指南

找到你的 IP 地址

你可以通过 kubectl 获取服务信息,找到为你的服务创建的 IP 地址:

kubectl describe services example-service

这将获得类似如下输出:

Name:                     example-service
Namespace:                default
Labels:                   app=example
Annotations:              <none>
Selector:                 app=example
Type:                     LoadBalancer
IP Families:              <none>
IP:                       10.3.22.96
IPs:                      10.3.22.96
LoadBalancer Ingress:     192.0.2.89
Port:                     <unset>  8765/TCP
TargetPort:               9376/TCP
NodePort:                 <unset>  30593/TCP
Endpoints:                172.17.0.3:9376
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

负载均衡器的 IP 地址列在 LoadBalancer Ingress 旁边。

保留客户端源 IP

默认情况下,目标容器中看到的源 IP 将不是客户端的原始源 IP。 要启用保留客户端 IP,可以在服务的 .spec 中配置以下字段:

  • .spec.externalTrafficPolicy - 表示此 Service 是否希望将外部流量路由到节点本地或集群范围的端点。 有两个可用选项:Cluster(默认)和 LocalCluster 隐藏了客户端源 IP,可能导致第二跳到另一个节点,但具有良好的整体负载分布。 Local 保留客户端源 IP 并避免 LoadBalancer 和 NodePort 类型服务的第二跳, 但存在潜在的不均衡流量传播风险。
  • .spec.healthCheckNodePort - 指定服务的 healthcheck nodePort(数字端口号)。 如果你未指定 healthCheckNodePort,服务控制器从集群的 NodePort 范围内分配一个端口。 你可以通过设置 API 服务器的命令行选项 --service-node-port-range 来配置上述范围。 在服务 type 设置为 LoadBalancer 并且 externalTrafficPolicy 设置为 Local 时, Service 将会使用用户指定的 healthCheckNodePort 值(如果你指定了它)。

可以通过在服务的清单文件中将 externalTrafficPolicy 设置为 Local 来激活此功能。比如:

apiVersion: v1
kind: Service
metadata:
  name: example-service
spec:
  selector:
    app: example
  ports:
    - port: 8765
      targetPort: 9376
  externalTrafficPolicy: Local
  type: LoadBalancer

保留源 IP 时的注意事项和限制

一些云服务供应商的负载均衡服务不允许你为每个目标配置不同的权重。

由于每个目标在向节点发送流量方面的权重相同,因此外部流量不会在不同 Pod 之间平均负载。 外部负载均衡器不知道每个节点上用作目标的 Pod 数量。

NumServicePods << NumNodesNumServicePods >> NumNodes 时, 即使没有权重,也会看到接近相等的分布。

内部 Pod 到 Pod 的流量应该与 ClusterIP 服务类似,所有 Pod 的概率相同。

回收负载均衡器

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

在通常情况下,应在删除 LoadBalancer 类型 Service 后立即清除云服务供应商中的相关负载均衡器资源。 但是,众所周知,在删除关联的服务后,云资源被孤立的情况很多。 引入了针对服务负载均衡器的终结器保护,以防止这种情况发生。 通过使用终结器,在删除相关的负载均衡器资源之前,也不会删除服务资源。

具体来说,如果服务具有 type LoadBalancer,则服务控制器将附加一个名为 service.kubernetes.io/load-balancer-cleanup 的终结器。 仅在清除负载均衡器资源后才能删除终结器。 即使在诸如服务控制器崩溃之类的极端情况下,这也可以防止负载均衡器资源悬空。

外部负载均衡器供应商

请务必注意,此功能的数据路径由 Kubernetes 集群外部的负载均衡器提供。

当服务 type 设置为 LoadBalancer 时,Kubernetes 向集群中的 Pod 提供的功能等同于 type 设置为 ClusterIP,并通过使用托管了相关 Kubernetes Pod 的节点作为条目对负载均衡器 (从外部到 Kubernetes)进行编程来扩展它。 Kubernetes 控制平面自动创建外部负载均衡器、健康检查(如果需要)和包过滤规则(如果需要)。 一旦云服务供应商为负载均衡器分配了 IP 地址,控制平面就会查找该外部 IP 地址并将其填充到 Service 对象中。

接下来

4.10.8 - 列出集群中所有运行容器的镜像

本文展示如何使用 kubectl 来列出集群中所有运行 Pod 的容器的镜像

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

在本练习中,你将使用 kubectl 来获取集群中运行的所有 Pod,并格式化输出来提取每个 Pod 中的容器列表。

列出所有命名空间下的所有容器镜像

  • 使用 kubectl get pods --all-namespaces 获取所有命名空间下的所有 Pod
  • 使用 -o jsonpath={.items[*].spec['initContainers', 'containers'][*].image} 来格式化输出,以仅包含容器镜像名称。 这将以递归方式从返回的 json 中解析出 image 字段。
    • 参阅 jsonpath 说明 获取更多关于如何使用 jsonpath 的信息。
  • 使用标准化工具来格式化输出:tr, sort, uniq
    • 使用 tr 以用换行符替换空格
    • 使用 sort 来对结果进行排序
    • 使用 uniq 来聚合镜像计数
kubectl get pods --all-namespaces -o jsonpath="{.items[*].spec.containers[*].image}" |\
tr -s '[[:space:]]' '\n' |\
sort |\
uniq -c

jsonpath 解释如下:

  • .items[*]: 对于每个返回的值
  • .spec: 获取 spec
  • ['initContainers', 'containers'][*]: 对于每个容器
  • .image: 获取镜像

按 Pod 列出容器镜像

可以使用 range 操作进一步控制格式化,以单独操作每个元素。

kubectl get pods --all-namespaces -o jsonpath='{range .items[*]}{"\n"}{.metadata.name}{":\t"}{range .spec.containers[*]}{.image}{", "}{end}{end}' |\
sort

列出以标签过滤后的 Pod 的所有容器镜像

要获取匹配特定标签的 Pod,请使用 -l 参数。以下匹配仅与标签 app=nginx 相符的 Pod。

kubectl get pods --all-namespaces -o jsonpath="{.items[*].spec.containers[*].image}" -l app=nginx

列出以命名空间过滤后的 Pod 的所有容器镜像

要获取匹配特定命名空间的 Pod,请使用 namespace 参数。以下仅匹配 kube-system 命名空间下的 Pod。

kubectl get pods --namespace kube-system -o jsonpath="{.items[*].spec.containers[*].image}"

使用 go-template 代替 jsonpath 来获取容器镜像

作为 jsonpath 的替代,Kubectl 支持使用 go-templates 来格式化输出:

kubectl get pods --all-namespaces -o go-template --template="{{range .items}}{{range .spec.containers}}{{.image}} {{end}}{{end}}"

接下来

参考

4.10.9 - 在 Minikube 环境中使用 NGINX Ingress 控制器配置 Ingress

Ingress是一种 API 对象, 其中定义了一些规则使得集群中的服务可以从集群外访问。 Ingress 控制器负责满足 Ingress 中所设置的规则。

本节为你展示如何配置一个简单的 Ingress,根据 HTTP URI 将服务请求路由到服务 webweb2

准备开始

本教程假设你正在使用 minikube 运行一个本地 Kubernetes 集群。 参阅安装工具了解如何安装 minikube

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你的 Kubernetes 服务器版本必须不低于版本 1.19. 要获知版本信息,请输入 kubectl version.

如果你使用的是较早的 Kubernetes 版本,请切换到该版本的文档。

创建一个 Minikube 集群

如果你还未在本地搭建集群,运行 minikube start 创建集群。

启用 Ingress 控制器

  1. 为了启用 NGINIX Ingress 控制器,可以运行下面的命令:

    minikube addons enable ingress
    
  1. 检查验证 NGINX Ingress 控制器处于运行状态:

    kubectl get pods -n ingress-nginx
    

    输出类似于:

    NAME                                        READY   STATUS      RESTARTS    AGE
    ingress-nginx-admission-create-g9g49        0/1     Completed   0          11m
    ingress-nginx-admission-patch-rqp78         0/1     Completed   1          11m
    ingress-nginx-controller-59b45fb494-26npt   1/1     Running     0          11m
    

部署一个 Hello World 应用

  1. 使用下面的命令创建一个 Deployment:

    kubectl create deployment web --image=gcr.io/google-samples/hello-app:1.0
    

    输出:

    deployment.apps/web created
    
  1. 将 Deployment 暴露出来:

    kubectl expose deployment web --type=NodePort --port=8080
    

    输出类似于:

    service/web exposed
    
  1. 验证 Service 已经创建,并且可以从节点端口访问:

    kubectl get service web
    

    输出类似于:

    NAME      TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
    web       NodePort   10.104.133.249   <none>        8080:31637/TCP   12m
    
  1. 使用节点端口信息访问服务:

    minikube service web --url
    

    输出类似于:

    http://172.17.0.15:31637
    
    curl http://172.17.0.15:31637
    

    输出类似于:

    Hello, world!
    Version: 1.0.0
    Hostname: web-55b8c6998d-8k564
    

    你现在应该可以通过 Minikube 的 IP 地址和节点端口来访问示例应用了。 下一步是让自己能够通过 Ingress 资源来访问应用。

创建一个 Ingress

下面是一个定义 Ingress 的配置文件,负责通过 hello-world.info 将请求转发到你的服务。

  1. 根据下面的 YAML 创建文件 example-ingress.yaml

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: example-ingress
      annotations:
        nginx.ingress.kubernetes.io/rewrite-target: /$1
    spec:
      rules:
        - host: hello-world.info
          http:
            paths:
              - path: /
                pathType: Prefix
                backend:
                  service:
                    name: web
                    port:
                      number: 8080
  1. 通过运行下面的命令创建 Ingress 对象:

    kubectl apply -f https://k8s.io/examples/service/networking/example-ingress.yaml
    

    输出类似于:

    ingress.networking.k8s.io/example-ingress created
    
  1. 验证 IP 地址已被设置:

    kubectl get ingress
    

    接下来你将会在 ADDRESS 列中看到 IPv4 地址,例如:

    NAME              CLASS    HOSTS              ADDRESS        PORTS   AGE
    example-ingress   <none>   hello-world.info   172.17.0.15    80      38s
    
  1. 验证 Ingress 控制器能够转发请求流量:

    curl --resolve "hello-world.info:80:$( minikube ip )" -i http://hello-world.info
    

    你应该看到类似输出:

    Hello, world!
    Version: 1.0.0
    Hostname: web-55b8c6998d-8k564
    

    你也可以从浏览器访问 hello-world.info

    • 可选

      查看 Minikube 报告的外部 IP 地址:

      minikube ip
      

      将类似以下这一行添加到你计算机上的 /etc/hosts 文件的末尾(需要管理员访问权限):

      172.17.0.15 hello-world.info
      

      更改完成后,在浏览器中访问 URL hello-world.info,请求将被发送到 Minikube。

创建第二个 Deployment

  1. 使用下面的命令创建第二个 Deployment:

    kubectl create deployment web2 --image=gcr.io/google-samples/hello-app:2.0
    

    输出类似于:

    deployment.apps/web2 created
    
  1. 将第二个 Deployment 暴露出来:

    kubectl expose deployment web2 --port=8080 --type=NodePort
    

    输出类似于:

    service/web2 exposed
    

编辑现有的 Ingress

  1. 编辑现有的 example-ingress.yaml,在文件最后添加以下行:

    - path: /v2
      pathType: Prefix
      backend:
        service:
          name: web2
          port:
            number: 8080
    
  1. 应用变更:

    kubectl apply -f example-ingress.yaml
    

    输出类似于:

    ingress.networking/example-ingress configured
    

测试你的 Ingress

  1. 访问 Hello World 应用的第一个版本:

    curl --resolve "hello-world.info:80:$( minikube ip )" -i http://hello-world.info
    

    输出类似于:

    Hello, world!
    Version: 1.0.0
    Hostname: web-55b8c6998d-8k564
    
  1. 访问 Hello World 应用的第二个版本:

    curl --resolve "hello-world.info:80:$( minikube ip )" -i http://hello-world.info/v2
    

    输出类似于:

    Hello, world!
    Version: 2.0.0
    Hostname: web2-75cd47646f-t8cjk
    

接下来

4.10.10 - 同 Pod 内的容器使用共享卷通信

本文旨在说明如何让一个 Pod 内的两个容器使用一个卷(Volume)进行通信。 参阅如何让两个进程跨容器通过 共享进程名字空间

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

创建一个包含两个容器的 Pod

在这个练习中,你会创建一个包含两个容器的 Pod。两个容器共享一个卷用于他们之间的通信。 Pod 的配置文件如下:

apiVersion: v1
kind: Pod
metadata:
  name: two-containers
spec:

  restartPolicy: Never

  volumes:
  - name: shared-data
    emptyDir: {}

  containers:

  - name: nginx-container
    image: nginx
    volumeMounts:
    - name: shared-data
      mountPath: /usr/share/nginx/html

  - name: debian-container
    image: debian
    volumeMounts:
    - name: shared-data
      mountPath: /pod-data
    command: ["/bin/sh"]
    args: ["-c", "echo Hello from the debian container > /pod-data/index.html"]

在配置文件中,你可以看到 Pod 有一个共享卷,名为 shared-data

配置文件中的第一个容器运行了一个 nginx 服务器。共享卷的挂载路径是 /usr/share/nginx/html。 第二个容器是基于 debian 镜像的,有一个 /pod-data 的挂载路径。第二个容器运行了下面的命令然后终止。

echo Hello from the debian container > /pod-data/index.html

注意,第二个容器在 nginx 服务器的根目录下写了 index.html 文件。

创建一个包含两个容器的 Pod:

kubectl apply -f https://k8s.io/examples/pods/two-container-pod.yaml

查看 Pod 和容器的信息:

kubectl get pod two-containers --output=yaml

这是输出的一部分:

apiVersion: v1
kind: Pod
metadata:
  ...
  name: two-containers
  namespace: default
  ...
spec:
  ...
  containerStatuses:

  - containerID: docker://c1d8abd1 ...
    image: debian
    ...
    lastState:
      terminated:
        ...
    name: debian-container
    ...

  - containerID: docker://96c1ff2c5bb ...
    image: nginx
    ...
    name: nginx-container
    ...
    state:
      running:
    ...

你可以看到 debian 容器已经被终止了,而 nginx 服务器依然在运行。

进入 nginx 容器的 shell:

kubectl exec -it two-containers -c nginx-container -- /bin/bash

在 shell 中,确认 nginx 还在运行。

root@two-containers:/# apt-get update
root@two-containers:/# apt-get install curl procps
root@two-containers:/# ps aux

输出类似于这样:

USER       PID  ...  STAT START   TIME COMMAND
root         1  ...  Ss   21:12   0:00 nginx: master process nginx -g daemon off;

回忆一下,debian 容器在 nginx 的根目录下创建了 index.html 文件。 使用 curl 向 nginx 服务器发送一个 GET 请求:

root@two-containers:/# curl localhost

输出表示 nginx 向外提供了 debian 容器所写就的页面:

Hello from the debian container

讨论

Pod 能有多个容器的主要原因是为了支持辅助应用(helper applications),以协助主应用(primary application)。 辅助应用的典型例子是数据抽取,数据推送和代理。辅助应用和主应用经常需要相互通信。 就如这个练习所示,通信通常是通过共享文件系统完成的,或者,也通过回环网络接口 localhost 完成。 举个网络接口的例子,web 服务器带有一个协助程序用于拉取 Git 仓库的更新。

在本练习中的卷为 Pod 生命周期中的容器相互通信提供了一种方法。如果 Pod 被删除或者重建了, 任何共享卷中的数据都会丢失。

接下来

4.10.11 - 为集群配置 DNS

Kubernetes 提供 DNS 集群插件,大多数支持的环境默认情况下都会启用。 在 Kubernetes 1.11 及其以后版本中,推荐使用 CoreDNS, kubeadm 默认会安装 CoreDNS。

要了解关于如何为 Kubernetes 集群配置 CoreDNS 的更多信息,参阅 定制 DNS 服务。 关于如何利用 kube-dns 配置 kubernetes DNS 的演示例子,参阅 Kubernetes DNS 插件示例

4.10.12 - 访问集群上运行的服务

本文展示了如何连接 Kubernetes 集群上运行的服务。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

访问集群上运行的服务

在 Kubernetes 里,节点Pod服务 都有自己的 IP。 许多情况下,集群上的节点 IP、Pod IP 和某些服务 IP 是路由不可达的, 所以不能从集群之外访问它们,例如从你自己的台式机。

连接方式

你有多种可选方式从集群外连接节点、Pod 和服务:

  • 通过公网 IP 访问服务
    • 使用类型为 NodePortLoadBalancer 的 Service,可以从外部访问它们。 请查阅 Servicekubectl expose 文档。
    • 取决于你的集群环境,你可以仅把 Service 暴露在你的企业网络环境中,也可以将其暴露在 因特网上。需要考虑暴露的服务是否安全,它是否有自己的用户认证?
    • 将 Pod 放置于 Service 背后。如果要访问一个副本集合中特定的 Pod,例如用于调试目的, 请给 Pod 指定一个独特的标签并创建一个新服务选择该标签。
    • 大部分情况下,都不需要应用开发者通过节点 IP 直接访问节点。
  • 通过 Proxy 动词访问服务、节点或者 Pod
    • 在访问远程服务之前,利用 API 服务器执行身份认证和鉴权。 如果你的服务不够安全,无法暴露到因特网中,或者需要访问节点 IP 上的端口, 又或者出于调试目的,可使用这种方式。
    • 代理可能给某些应用带来麻烦
    • 此方式仅适用于 HTTP/HTTPS
    • 进一步的描述在这里
    • 从集群中的 node 或者 pod 访问。
  • 从集群中的一个节点或 Pod 访问
    • 运行一个 Pod,然后使用 kubectl exec 连接到它的 Shell,从那个 Shell 连接其他的节点、Pod 和 Service。
    • 某些集群可能允许你 SSH 到集群中的节点。你可能可以从那儿访问集群服务。 这是一个非标准的方式,可能在一些集群上能工作,但在另一些上却不能。 浏览器和其他工具可能已经安装也可能没有安装。集群 DNS 可能不会正常工作。

发现内置服务

典型情况下,kube-system 名字空间中会启动集群的几个服务。 使用 kubectl cluster-info 命令获取这些服务的列表:

kubectl cluster-info

输出类似于:

Kubernetes master is running at https://192.0.2.1
elasticsearch-logging is running at https://192.0.2.1/api/v1/namespaces/kube-system/services/elasticsearch-logging/proxy
kibana-logging is running at https://192.0.2.1/api/v1/namespaces/kube-system/services/kibana-logging/proxy
kube-dns is running at https://192.0.2.1/api/v1/namespaces/kube-system/services/kube-dns/proxy
grafana is running at https://192.0.2.1/api/v1/namespaces/kube-system/services/monitoring-grafana/proxy
heapster is running at https://192.0.2.1/api/v1/namespaces/kube-system/services/monitoring-heapster/proxy

这一输出显示了用 proxy 动词访问每个服务时可用的 URL。例如,此集群 (使用 Elasticsearch)启用了集群层面的日志。如果提供合适的凭据,可以通过 https://192.0.2.1/api/v1/namespaces/kube-system/services/elasticsearch-logging/proxy/ 访问,或通过一个 kubectl proxy 来访问: http://localhost:8080/api/v1/namespaces/kube-system/services/elasticsearch-logging/proxy/

手动构建 API 服务器代理 URLs

如前所述,你可以使用 kubectl cluster-info 命令取得服务的代理 URL。 为了创建包含服务末端、后缀和参数的代理 URLs,你可以在服务的代理 URL 中添加: http://kubernetes_master_address/api/v1/namespaces/namespace_name/services/service_name[:port_name]/proxy

如果还没有为你的端口指定名称,你可以不用在 URL 中指定 port_name。 对于命名和未命名端口,你还可以使用端口号代替 port_name

默认情况下,API 服务器使用 HTTP 为你的服务提供代理。 要使用 HTTPS,请在服务名称前加上 https:http://<kubernetes_master_address>/api/v1/namespaces/<namespace_name>/services/<service_name>/proxy URL 的 <service_name> 段支持的格式为:

  • <service_name> - 使用 http 代理到默认或未命名端口
  • <service_name>:<port_name> - 使用 http 代理到指定的端口名称或端口号
  • https:<service_name>: - 使用 https 代理到默认或未命名端口(注意尾随冒号)
  • https:<service_name>:<port_name> - 使用 https 代理到指定的端口名称或端口号
示例
  • 如要访问 Elasticsearch 服务末端 _search?q=user:kimchy,你可以使用以下地址:

    http://192.0.2.1/api/v1/namespaces/kube-system/services/elasticsearch-logging/proxy/_search?q=user:kimchy
    
  • 如要访问 Elasticsearch 集群健康信息_cluster/health?pretty=true,你可以使用以下地址:

    https://192.0.2.1/api/v1/namespaces/kube-system/services/elasticsearch-logging/proxy/_cluster/health?pretty=true
    

    健康信息与下面的例子类似:

    {
      "cluster_name" : "kubernetes_logging",
      "status" : "yellow",
      "timed_out" : false,
      "number_of_nodes" : 1,
      "number_of_data_nodes" : 1,
      "active_primary_shards" : 5,
      "active_shards" : 5,
      "relocating_shards" : 0,
      "initializing_shards" : 0,
      "unassigned_shards" : 5
    }
    
  • 如要访问 https Elasticsearch 服务健康信息 _cluster/health?pretty=true,你可以使用以下地址:

    https://192.0.2.1/api/v1/namespaces/kube-system/services/https:elasticsearch-logging:/proxy/_cluster/health?pretty=true
    

通过 Web 浏览器访问集群中运行的服务

你或许能够将 API 服务器代理的 URL 放入浏览器的地址栏,然而:

  • Web 服务器通常不能传递令牌,所以你可能需要使用基本(密码)认证。 API 服务器可以配置为接受基本认证,但你的集群可能并没有这样配置。
  • 某些 Web 应用可能无法工作,特别是那些使用客户端 Javascript 构造 URL 的 应用,所构造的 URL 可能并不支持代理路径前缀。

4.11 - 扩展 Kubernetes

了解针对工作环境需要来调整 Kubernetes 集群的进阶方法。

4.11.1 - 配置聚合层

配置聚合层可以允许 Kubernetes apiserver 使用其它 API 扩展,这些 API 不是核心 Kubernetes API 的一部分。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

身份认证流程

与自定义资源定义(CRD)不同,除标准的 Kubernetes apiserver 外,Aggregation API 还涉及另一个服务器:扩展 apiserver。 Kubernetes apiserver 将需要与你的扩展 apiserver 通信,并且你的扩展 apiserver 也需要与 Kubernetes apiserver 通信。 为了确保此通信的安全,Kubernetes apiserver 使用 x509 证书向扩展 apiserver 认证。

本节介绍身份认证和鉴权流程的工作方式以及如何配置它们。

大致流程如下:

  1. Kubernetes apiserver:对发出请求的用户身份认证,并对请求的 API 路径执行鉴权。
  2. Kubernetes apiserver:将请求转发到扩展 apiserver
  3. 扩展 apiserver:认证来自 Kubernetes apiserver 的请求
  4. 扩展 apiserver:对来自原始用户的请求鉴权
  5. 扩展 apiserver:执行

本节的其余部分详细描述了这些步骤。

该流程可以在下图中看到。

聚合层认证流程

以上泳道的来源可以在本文档的源码中找到。

Kubernetes Apiserver 认证和授权

由扩展 apiserver 服务的对 API 路径的请求以与所有 API 请求相同的方式开始: 与 Kubernetes apiserver 的通信。该路径已通过扩展 apiserver 在 Kubernetes apiserver 中注册。

用户与 Kubernetes apiserver 通信,请求访问路径。 Kubernetes apiserver 使用它的标准认证和授权配置来对用户认证,以及对特定路径的鉴权。

有关对 Kubernetes 集群认证的概述, 请参见对集群认证。 有关对 Kubernetes 集群资源的访问鉴权的概述, 请参见鉴权概述

到目前为止,所有内容都是标准的 Kubernetes API 请求,认证与鉴权。

Kubernetes apiserver 现在准备将请求发送到扩展 apiserver。

Kubernetes Apiserver 代理请求

Kubernetes apiserver 现在将请求发送或代理到注册以处理该请求的扩展 apiserver。 为此,它需要了解几件事:

  1. Kubernetes apiserver 应该如何向扩展 apiserver 认证,以通知扩展 apiserver 通过网络发出的请求来自有效的 Kubernetes apiserver?

  2. Kubernetes apiserver 应该如何通知扩展 apiserver 原始请求已通过认证的用户名和组?

为提供这两条信息,你必须使用若干标志来配置 Kubernetes apiserver。

Kubernetes Apiserver 客户端认证

Kubernetes apiserver 通过 TLS 连接到扩展 apiserver,并使用客户端证书认证。 你必须在启动时使用提供的标志向 Kubernetes apiserver 提供以下内容:

  • 通过 --proxy-client-key-file 指定私钥文件
  • 通过 --proxy-client-cert-file 签名的客户端证书文件
  • 通过 --requestheader-client-ca-file 签署客户端证书文件的 CA 证书
  • 通过 --requestheader-allowed-names 在签署的客户端证书中有效的公用名(CN)

Kubernetes apiserver 将使用由 --proxy-client-*-file 指示的文件来向扩展 apiserver认证。 为了使合规的扩展 apiserver 能够将该请求视为有效,必须满足以下条件:

  1. 连接必须使用由 CA 签署的客户端证书,该证书的证书位于 --requestheader-client-ca-file 中。
  2. 连接必须使用客户端证书,该客户端证书的 CN 是 --requestheader-allowed-names 中列出的证书之一。

使用这些选项启动时,Kubernetes apiserver 将:

  1. 使用它们向扩展 apiserver 认证。
  2. kube-system 命名空间中创建一个名为 extension-apiserver-authentication 的 ConfigMap, 它将在其中放置 CA 证书和允许的 CN。 反过来,扩展 apiserver 可以检索这些内容以验证请求。

请注意,Kubernetes apiserver 使用相同的客户端证书对所有扩展 apiserver 认证。 它不会为每个扩展 apiserver 创建一个客户端证书,而是创建一个证书作为 Kubernetes apiserver 认证。所有扩展 apiserver 请求都重复使用相同的请求。

原始请求用户名和组

当 Kubernetes apiserver 将请求代理到扩展 apiserver 时, 它将向扩展 apiserver 通知原始请求已成功通过其验证的用户名和组。 它在其代理请求的 HTTP 头部中提供这些。你必须将要使用的标头名称告知 Kubernetes apiserver。

  • 通过 --requestheader-username-headers 标明用来保存用户名的头部
  • 通过 --requestheader-group-headers 标明用来保存 group 的头部
  • 通过 --requestheader-extra-headers-prefix 标明用来保存拓展信息前缀的头部

这些头部名称也放置在 extension-apiserver-authentication ConfigMap 中, 因此扩展 apiserver 可以检索和使用它们。

扩展 Apiserver 认证请求

扩展 apiserver 在收到来自 Kubernetes apiserver 的代理请求后, 必须验证该请求确实确实来自有效的身份验证代理, 该认证代理由 Kubernetes apiserver 履行。扩展 apiserver 通过以下方式对其认证:

  1. 如上所述,从 kube-system 中的 ConfigMap 中检索以下内容:

    • 客户端 CA 证书
    • 允许名称(CN)列表
    • 用户名,组和其他信息的头部
  2. 使用以下证书检查 TLS 连接是否已通过认证:

    • 由其证书与检索到的 CA 证书匹配的 CA 签名。
    • 在允许的 CN 列表中有一个 CN,除非列表为空,在这种情况下允许所有 CN。
    • 从适当的头部中提取用户名和组。

如果以上均通过,则该请求是来自合法认证代理(在本例中为 Kubernetes apiserver) 的有效代理请求。

请注意,扩展 apiserver 实现负责提供上述内容。 默认情况下,许多扩展 apiserver 实现利用 k8s.io/apiserver/ 软件包来做到这一点。 也有一些实现可能支持使用命令行选项来覆盖这些配置。

为了具有检索 configmap 的权限,扩展 apiserver 需要适当的角色。 在 kube-system 名字空间中有一个默认角色 extension-apiserver-authentication-reader 可用于设置。

扩展 Apiserver 对请求鉴权

扩展 apiserver 现在可以验证从标头检索的user/group是否有权执行给定请求。 通过向 Kubernetes apiserver 发送标准 SubjectAccessReview 请求来实现。

为了使扩展 apiserver 本身被鉴权可以向 Kubernetes apiserver 提交 SubjectAccessReview 请求, 它需要正确的权限。 Kubernetes 包含一个具有相应权限的名为 system:auth-delegator 的默认 ClusterRole, 可以将其授予扩展 apiserver 的服务帐户。

扩展 Apiserver 执行

如果 SubjectAccessReview 通过,则扩展 apiserver 执行请求。

启用 Kubernetes Apiserver 标志

通过以下 kube-apiserver 标志启用聚合层。 你的服务提供商可能已经为你完成了这些工作:

    --requestheader-client-ca-file=<path to aggregator CA cert>
    --requestheader-allowed-names=front-proxy-client
    --requestheader-extra-headers-prefix=X-Remote-Extra-
    --requestheader-group-headers=X-Remote-Group
    --requestheader-username-headers=X-Remote-User
    --proxy-client-cert-file=<path to aggregator proxy cert>
    --proxy-client-key-file=<path to aggregator proxy key>

CA 重用和冲突

Kubernetes apiserver 有两个客户端 CA 选项:

  • --client-ca-file
  • --requestheader-client-ca-file

这些功能中的每个功能都是独立的;如果使用不正确,可能彼此冲突。

  • --client-ca-file:当请求到达 Kubernetes apiserver 时,如果启用了此选项, 则 Kubernetes apiserver 会检查请求的证书。 如果它是由 --client-ca-file 引用的文件中的 CA 证书之一签名的, 并且用户是公用名 CN= 的值,而组是组织 O= 的取值,则该请求被视为合法请求。 请参阅关于 TLS 身份验证的文档

  • --requestheader-client-ca-file:当请求到达 Kubernetes apiserver 时, 如果启用此选项,则 Kubernetes apiserver 会检查请求的证书。 如果它是由文件引用中的 --requestheader-client-ca-file 所签署的 CA 证书之一签名的, 则该请求将被视为潜在的合法请求。 然后,Kubernetes apiserver 检查通用名称 CN= 是否是 --requestheader-allowed-names 提供的列表中的名称之一。 如果名称允许,则请求被批准;如果不是,则请求被拒绝。

如果同时提供了 --client-ca-file--requestheader-client-ca-file, 则首先检查 --requestheader-client-ca-file CA,然后再检查 --client-ca-file。 通常,这些选项中的每一个都使用不同的 CA(根 CA 或中间 CA)。 常规客户端请求与 --client-ca-file 相匹配,而聚合请求要与 --requestheader-client-ca-file 相匹配。 但是,如果两者都使用同一个 CA,则通常会通过 --client-ca-file 传递的客户端请求将失败,因为 CA 将与 --requestheader-client-ca-file 中的 CA 匹配,但是通用名称 CN= 将不匹配 --requestheader-allowed-names 中可接受的通用名称之一。 这可能导致你的 kubelet 和其他控制平面组件以及最终用户无法向 Kubernetes apiserver 认证。

因此,请对用于控制平面组件和最终用户鉴权的 --client-ca-file 选项和用于聚合 apiserver 鉴权的 --requestheader-client-ca-file 选项使用不同的 CA 证书。

如果你未在运行 API 服务器的主机上运行 kube-proxy,则必须确保使用以下 kube-apiserver 标志启用系统:

--enable-aggregator-routing=true

注册 APIService 对象

你可以动态配置将哪些客户端请求代理到扩展 apiserver。以下是注册示例:

apiVersion: apiregistration.k8s.io/v1
kind: APIService
metadata:
  name: <注释对象名称>
spec:
  group: <扩展 Apiserver 的 API 组名>
  version: <扩展 Apiserver 的 API 版本>
  groupPriorityMinimum: <APIService 对应组的优先级, 参考 API 文档>
  versionPriority: <版本在组中的优先排序, 参考 API 文档>
  service:
    namespace: <拓展 Apiserver 服务的名字空间>
    name: <拓展 Apiserver 服务的名称>
  caBundle: <PEM 编码的 CA 证书,用于对 Webhook 服务器的证书签名>

APIService 对象的名称必须是合法的路径片段名称

调用扩展 apiserver

一旦 Kubernetes apiserver 确定应将请求发送到扩展 apiserver, 它需要知道如何调用它。

service 部分是对扩展 apiserver 的服务的引用。 服务的名字空间和名字是必需的。端口是可选的,默认为 443。

下面是一个扩展 apiserver 的配置示例,它被配置为在端口 1234 上调用。 并针对 ServerName my-service-name.my-service-namespace.svc 使用自定义的 CA 包来验证 TLS 连接使用自定义 CA 捆绑包的 my-service-name.my-service-namespace.svc

apiVersion: apiregistration.k8s.io/v1
kind: APIService
...
spec:
  ...
  service:
    namespace: my-service-namespace
    name: my-service-name
    port: 1234
  caBundle: "Ci0tLS0tQk...<base64-encoded PEM bundle>...tLS0K"
...

接下来

4.11.2 - 使用自定义资源

4.11.2.1 - 使用 CustomResourceDefinition 扩展 Kubernetes API

本页展示如何使用 CustomResourceDefinition定制资源(Custom Resource) 安装到 Kubernetes API 上。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你的 Kubernetes 服务器版本必须不低于版本 1.16. 要获知版本信息,请输入 kubectl version.

如果你在使用较老的、仍处于被支持范围的 Kubernetes 版本, 请切换到该版本的文档查看对于你的集群而言有用的建议。

创建 CustomResourceDefinition

当你创建新的 CustomResourceDefinition(CRD)时,Kubernetes API 服务器会为你所指定的每个版本生成一个新的 RESTful 资源路径。 基于 CRD 对象所创建的自定义资源可以是名字空间作用域的,也可以是集群作用域的, 取决于 CRD 对象 spec.scope 字段的设置。

与其它的内置对象一样,删除名字空间也将删除该名字空间中的所有自定义对象。 CustomResourceDefinitions 本身是无名字空间的,可在所有名字空间中访问。

例如,如果你将下面的 CustomResourceDefinition 保存到 resourcedefinition.yaml 文件:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  # 名字必需与下面的 spec 字段匹配,并且格式为 '<名称的复数形式>.<组名>'
  name: crontabs.stable.example.com
spec:
  # 组名称,用于 REST API: /apis/<组>/<版本>
  group: stable.example.com
  # 列举此 CustomResourceDefinition 所支持的版本
  versions:
    - name: v1
      # 每个版本都可以通过 served 标志来独立启用或禁止
      served: true
      # 其中一个且只有一个版本必需被标记为存储版本
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                cronSpec:
                  type: string
                image:
                  type: string
                replicas:
                  type: integer
  # 可以是 Namespaced 或 Cluster
  scope: Namespaced
  names:
    # 名称的复数形式,用于 URL:/apis/<组>/<版本>/<名称的复数形式>
    plural: crontabs
    # 名称的单数形式,作为命令行使用时和显示时的别名
    singular: crontab
    # kind 通常是单数形式的驼峰命名(CamelCased)形式。你的资源清单会使用这一形式。
    kind: CronTab
    # shortNames 允许你在命令行使用较短的字符串来匹配资源
    shortNames:
    - ct

之后创建它:

kubectl apply -f resourcedefinition.yaml

这样一个新的受名字空间约束的 RESTful API 端点会被创建在:

/apis/stable.example.com/v1/namespaces/*/crontabs/...

此端点 URL 自此可以用来创建和管理定制对象。对象的 kind 将是来自你上面创建时 所用的 spec 中指定的 CronTab

创建端点的操作可能需要几秒钟。你可以监测你的 CustomResourceDefinition 的 Established 状况变为 true,或者监测 API 服务器的发现信息等待你的资源出现在 那里。

创建定制对象

在创建了 CustomResourceDefinition 对象之后,你可以创建定制对象(Custom Objects)。定制对象可以包含定制字段。这些字段可以包含任意的 JSON 数据。 在下面的例子中,在类别为 CronTab 的定制对象中,设置了cronSpecimage 定制字段。类别 CronTab 来自你在上面所创建的 CRD 的规约。

如果你将下面的 YAML 保存到 my-crontab.yaml

apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
  name: my-new-cron-object
spec:
  cronSpec: "* * * * */5"
  image: my-awesome-cron-image

并执行创建命令:

kubectl apply -f my-crontab.yaml

你就可以使用 kubectl 来管理你的 CronTab 对象了。例如:

kubectl get crontab

应该会输出如下列表:

NAME                 AGE
my-new-cron-object   6s

使用 kubectl 时,资源名称是大小写不敏感的,而且你既可以使用 CRD 中所定义的单数 形式或复数形式,也可以使用其短名称:

kubectl get ct -o yaml

你可以看到输出中包含了你创建定制对象时在 YAML 文件中指定的定制字段 cronSpecimage

apiVersion: v1
items:
- apiVersion: stable.example.com/v1
  kind: CronTab
  metadata:
    annotations:
      kubectl.kubernetes.io/last-applied-configuration: |
        {"apiVersion":"stable.example.com/v1","kind":"CronTab","metadata":{"annotations":{},"name":"my-new-cron-object","namespace":"default"},"spec":{"cronSpec":"* * * * */5","image":"my-awesome-cron-image"}}        
    creationTimestamp: "2021-06-20T07:35:27Z"
    generation: 1
    name: my-new-cron-object
    namespace: default
    resourceVersion: "1326"
    uid: 9aab1d66-628e-41bb-a422-57b8b3b1f5a9
  spec:
    cronSpec: '* * * * */5'
    image: my-awesome-cron-image
kind: List
metadata:
  resourceVersion: ""
  selfLink: ""

删除 CustomResourceDefinition

当你删除某 CustomResourceDefinition 时,服务器会卸载其 RESTful API 端点,并删除服务器上存储的所有定制对象。

kubectl delete -f resourcedefinition.yaml
kubectl get crontabs
Error from server (NotFound): Unable to list {"stable.example.com" "v1" "crontabs"}: the server could not
find the requested resource (get crontabs.stable.example.com)

如果你在以后创建相同的 CustomResourceDefinition 时,该 CRD 会是一个空的结构。

设置结构化的模式

CustomResource 对象在定制字段中保存结构化的数据,这些字段和内置的字段 apiVersionkindmetadata 等一起存储,不过内置的字段都会被 API 服务器隐式完成合法性检查。有了 OpenAPI v3.0 检查 能力之后,你可以设置一个模式(Schema),在创建和更新定制对象时,这一模式会被用来 对对象内容进行合法性检查。参阅下文了解这类模式的细节和局限性。

apiextensions.k8s.io/v1 版本中,CustomResourceDefinition 的这一结构化模式 定义是必需的。 在 CustomResourceDefinition 的 beta 版本中,结构化模式定义是可选的。

结构化模式本身是一个 OpenAPI v3.0 验证模式,其中:

  1. 为对象根(root)设置一个非空的 type 值(藉由 OpenAPI 中的 type),对每个 object 节点的每个字段(藉由 OpenAPI 中的 propertiesadditionalProperties)以及 array 节点的每个条目(藉由 OpenAPI 中的 items)也要设置非空的 type 值, 除非:
    • 节点包含属性 x-kubernetes-int-or-string: true
    • 节点包含属性 x-kubernetes-preserve-unknown-fields: true
  2. 对于 object 的每个字段或 array 中的每个条目,如果其定义中包含 allOfanyOfoneOfnot,则模式也要指定这些逻辑组合之外的字段或条目(试比较例 1 和例 2)。
  3. allOfanyOfoneOfnot 上下文内不设置 descriptiontypedefaultadditionalProperties 或者 nullable。此规则的例外是 x-kubernetes-int-or-string 的两种模式(见下文)。
  4. 如果 metadata 被设置,则只允许对 metadata.namemetadata.generateName 设置约束。

非结构化的例 1:

allOf:
- properties:
    foo:
      ...

违反了第 2 条规则。下面的是正确的:

properties:
  foo:
    ...
allOf:
- properties:
    foo:
      ...

非结构化的例 2:

allOf:
- items:
    properties:
      foo:
        ...

违反了第 2 条规则。下面的是正确的:

items:
  properties:
    foo:
      ...
allOf:
- items:
    properties:
      foo:
        ...

非结构化的例 3:

properties:
  foo:
    pattern: "abc"
  metadata:
    type: object
    properties:
      name:
        type: string
        pattern: "^a"
      finalizers:
        type: array
        items:
          type: string
          pattern: "my-finalizer"
anyOf:
- properties:
    bar:
      type: integer
      minimum: 42
  required: ["bar"]
  description: "foo bar object"

不是一个结构化的模式,因为其中存在以下违例:

  • 根节点缺失 type 设置(规则 1)
  • foo 的 type 缺失(规则 1)
  • anyOf 中的 bar 未在外部指定(规则 2)
  • bartype 位于 anyOf 中(规则 3)
  • anyOf 中设置了 description (规则 3)
  • metadata.finalizers 不可以被限制 (规则 4)

作为对比,下面的 YAML 所对应的模式则是结构化的:

type: object
description: "foo bar object"
properties:
  foo:
    type: string
    pattern: "abc"
  bar:
    type: integer
  metadata:
    type: object
    properties:
      name:
        type: string
        pattern: "^a"
anyOf:
- properties:
    bar:
      minimum: 42
  required: ["bar"]

如果违反了结构化模式规则,CustomResourceDefinition 的 NonStructural 状况中会包含报告信息。

字段剪裁

CustomResourceDefinition 在集群的持久性存储 etcd 中保存经过合法性检查的资源数据。 就像原生的 Kubernetes 资源,例如 ConfigMap, 如果你指定了 API 服务器所无法识别的字段,则该未知字段会在保存资源之前被 剪裁(Pruned) 掉(删除)。

apiextensions.k8s.io/v1beta1 转换到 apiextensions.k8s.io/v1 的 CRD 可能没有结构化的模式定义,因此其 spec.preserveUnknownFields 可能为 true

对于使用 apiextensions.k8s.io/v1beta1 且将 spec.preserveUnknownFields 设置为 true 创建的旧 CustomResourceDefinition 对象,有以下表现:

  • 裁剪未启用。
  • 可以存储任意数据。

为了与 apiextensions.k8s.io/v1 兼容,将你的定制资源定义更新为:

  1. 使用结构化的 OpenAPI 模式。
  2. spec.preserveUnknownFields 设置为 false

如果你将下面的 YAML 保存到 my-crontab.yaml 文件:

apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
  name: my-new-cron-object
spec:
  cronSpec: "* * * * */5"
  image: my-awesome-cron-image
  someRandomField: 42

并创建之:

kubectl create --validate=false -f my-crontab.yaml -o yaml

输出类似于:

apiVersion: stable.example.com/v1
kind: CronTab
metadata:
  creationTimestamp: 2017-05-31T12:56:35Z
  generation: 1
  name: my-new-cron-object
  namespace: default
  resourceVersion: "285"
  uid: 9423255b-4600-11e7-af6a-28d2447dc82b
spec:
  cronSpec: '* * * * */5'
  image: my-awesome-cron-image

注意其中的字段 someRandomField 已经被剪裁掉。

本例中通过 --validate=false 命令行选项 关闭了客户端的合法性检查以展示 API 服务器的行为, 因为 OpenAPI 合法性检查模式也会发布到 客户端,kubectl 也会检查未知的字段并在对象被发送到 API 服务器之前就拒绝它们。

控制剪裁

默认情况下,定制资源的所有版本中的所有未规定的字段都会被剪裁掉。 通过在结构化的 OpenAPI v3 检查模式定义 中为特定字段的子树添加 x-kubernetes-preserve-unknown-fields: true 属性, 可以选择不对其执行剪裁操作。

例如:

type: object
properties:
  json:
    x-kubernetes-preserve-unknown-fields: true

字段 json 可以保存任何 JSON 值,其中内容不会被剪裁掉。

你也可以部分地指定允许的 JSON 数据格式;例如:

type: object
properties:
  json:
    x-kubernetes-preserve-unknown-fields: true
    type: object
    description: this is arbitrary JSON

通过这样设置,JSON 中只能设置 object 类型的值。

对于所指定的每个属性(或 additionalProperties),剪裁会再次被启用。

type: object
properties:
  json:
    x-kubernetes-preserve-unknown-fields: true
    type: object
    properties:
      spec:
        type: object
        properties:
          foo:
            type: string
          bar:
            type: string

对于上述定义,如果提供的数值如下:

json:
  spec:
    foo: abc
    bar: def
    something: x
  status:
    something: x

则该值会被剪裁为:

json:
  spec:
    foo: abc
    bar: def
  status:
    something: x

这意味着所指定的 spec 对象中的 something 字段被剪裁掉,而其外部的内容都被保留。

IntOrString

模式定义中标记了 x-kubernetes-int-or-string: true 的节点不受前述规则 1 约束,因此下面的定义是结构化的模式:

type: object
properties:
  foo:
    x-kubernetes-int-or-string: true

此外,所有这类节点也不再受规则 3 约束,也就是说,下面两种模式是被允许的 (注意,仅限于这两种模式,不支持添加新字段的任何其他变种):

x-kubernetes-int-or-string: true
anyOf:
  - type: integer
  - type: string
...

x-kubernetes-int-or-string: true
allOf:
  - anyOf:
      - type: integer
      - type: string
  - ... # zero or more
...

在以上两种规约中,整数值和字符串值都会被认为是合法的。

合法性检查模式定义的发布时x-kubernetes-int-or-string: true 会被展开为上述两种模式之一。

RawExtension

RawExtensions(就像在 k8s.io/apimachinery 项目中 runtime.RawExtension 所定义的那样) 可以保存完整的 Kubernetes 对象,也就是,其中会包含 apiVersionkind 字段。

通过 x-kubernetes-embedded-resource: true 来设定这些嵌套对象的规约 (无论是完全无限制还是部分指定都可以)是可能的。例如:

type: object
properties:
  foo:
    x-kubernetes-embedded-resource: true
    x-kubernetes-preserve-unknown-fields: true

这里,字段 foo 包含一个完整的对象,例如:

foo:
  apiVersion: v1
  kind: Pod
  spec:
    ...

由于字段上设置了 x-kubernetes-preserve-unknown-fields: true,其中的内容不会 被剪裁。不过,在这个语境中,x-kubernetes-preserve-unknown-fields: true 的 使用是可选的。

设置了 x-kubernetes-embedded-resource: true 之后,apiVersionkindmetadata 都是隐式设定并隐式完成合法性验证。

提供 CRD 的多个版本

关于如何为你的 CustomResourceDefinition 提供多个版本的支持, 以及如何将你的对象从一个版本迁移到另一个版本, 详细信息可参阅 CustomResourceDefinition 的版本管理

高级主题

Finalizers

Finalizer 能够让控制器实现异步的删除前(Pre-delete)回调。 与内置对象类似,定制对象也支持 Finalizer。

你可以像下面一样为定制对象添加 Finalizer:

apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
  finalizers:
  - stable.example.com/finalizer

自定义 Finalizer 的标识符包含一个域名、一个正向斜线和 finalizer 的名称。 任何控制器都可以在任何对象的 finalizer 列表中添加新的 finalizer。

对带有 Finalizer 的对象的第一个删除请求会为其 metadata.deletionTimestamp 设置一个值,但不会真的删除对象。一旦此值被设置,finalizers 列表中的表项只能被移除。 在列表中仍然包含 finalizer 时,无法强制删除对应的对象。

metadata.deletionTimestamp 字段被设置时,监视该对象的各个控制器会执行它们所能处理的 finalizer,并在完成处理之后将其从列表中移除。 每个控制器负责将其 finalizer 从列表中删除。

metadata.deletionGracePeriodSeconds 的取值控制对更新的轮询周期。

一旦 finalizers 列表为空时,就意味着所有 finalizer 都被执行过, Kubernetes 会最终删除该资源,

合法性检查

定制资源是通过 OpenAPI v3 模式定义 来执行合法性检查的,当启用验证规则特性时,通过 x-kubernetes-validations 验证, 你可以通过使用准入控制 Webhook 来添加额外的合法性检查逻辑。

此外,对模式定义存在以下限制:

  • 以下字段不可设置:

    • definitions
    • dependencies
    • deprecated
    • discriminator
    • id
    • patternProperties
    • readOnly
    • writeOnly
    • xml
    • $ref
  • 字段 uniqueItems 不可设置为 true

  • 字段 additionalProperties 不可设置为 false

  • 字段 additionalPropertiesproperties 互斥,不可同时使用

验证规则特性被启用并且 CustomResourceDefinition 模式是一个结构化的模式定义时, x-kubernetes-validations 扩展可以使用通用表达式语言 (CEL)表达式来验证定制资源。

关于对某些 CustomResourceDefinition 特性所必需的限制, 可参见结构化的模式定义小节。

模式定义是在 CustomResourceDefinition 中设置的。在下面的例子中, CustomResourceDefinition 对定制对象执行以下合法性检查:

  • spec.cronSpec 必须是一个字符串,必须是正则表达式所描述的形式;
  • spec.replicas 必须是一个整数,且其最小值为 1、最大值为 10。

将此 CustomResourceDefinition 保存到 resourcedefinition.yaml 文件中:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: crontabs.stable.example.com
spec:
  group: stable.example.com
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        # openAPIV3Schema 是验证自定义对象的模式。
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                cronSpec:
                  type: string
                  pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$'
                image:
                  type: string
                replicas:
                  type: integer
                  minimum: 1
                  maximum: 10
  scope: Namespaced
  names:
    plural: crontabs
    singular: crontab
    kind: CronTab
    shortNames:
    - ct

并创建 CustomResourceDefinition:

kubectl apply -f resourcedefinition.yaml

对于一个创建 CronTab 类别对象的定制对象的请求而言,如果其字段中包含非法值,则 该请求会被拒绝。 在下面的例子中,定制对象中包含带非法值的字段:

  • spec.cronSpec 与正则表达式不匹配
  • spec.replicas 数值大于 10。

如果你将下面的 YAML 保存到 my-crontab.yaml

apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
  name: my-new-cron-object
spec:
  cronSpec: "* * * *"
  image: my-awesome-cron-image
  replicas: 15

并尝试创建定制对象:

kubectl apply -f my-crontab.yaml

你会看到下面的错误信息:

The CronTab "my-new-cron-object" is invalid: []: Invalid value: map[string]interface {}{"apiVersion":"stable.example.com/v1", "kind":"CronTab", "metadata":map[string]interface {}{"name":"my-new-cron-object", "namespace":"default", "deletionTimestamp":interface {}(nil), "deletionGracePeriodSeconds":(*int64)(nil), "creationTimestamp":"2017-09-05T05:20:07Z", "uid":"e14d79e7-91f9-11e7-a598-f0761cb232d1", "clusterName":""}, "spec":map[string]interface {}{"cronSpec":"* * * *", "image":"my-awesome-cron-image", "replicas":15}}:
validation failure list:
spec.cronSpec in body should match '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$'
spec.replicas in body should be less than or equal to 10

如果所有字段都包含合法值,则对象创建的请求会被接受。

将下面的 YAML 保存到 my-crontab.yaml 文件:

apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
  name: my-new-cron-object
spec:
  cronSpec: "* * * * */5"
  image: my-awesome-cron-image
  replicas: 5

并创建定制对象:

kubectl apply -f my-crontab.yaml
crontab "my-new-cron-object" created

验证逐步升级

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

你需要启用 CRDValidationRatcheting 特性门控, 才能使用这种行为,并将其应用到集群中的所有 CustomResourceDefinition。

只要你启用了此特性门控,Kubernetes 就会对 CustomResourceDefinition 实施验证逐步升级。 即使更新后的资源无效,API 服务器也愿意接受对资源的更新, 只要资源中未通过验证的每个部分都没有被更新操作改变。 换句话说,资源中任何无效的部分如果仍然无效,那它必须之前就是错误的。 你不能使用此机制来更新一个有效资源,使其变为无效。

此特性使得 CRD 的作者能够在某些条件下有信心地向 OpenAPIV3 模式定义中添加新的验证。 用户可以安全地更新到新的模式定义,而不必提升对象的版本或破坏工作流。

尽管大多数放在 CRD 的 OpenAPIV3 模式定义中的验证都支持逐步升级,仍存在一些例外。 Kubernetes 1.30 下实现的验证逐步升级不支持下面所列举的 OpenAPIV3 模式检查, 如果检查时发现违例,会和以往一样抛出错误:

  • 量词
    • allOf
    • oneOf
    • anyOf
    • not
    • 以及这些字段的下级字段中的所有合法性检查
  • x-kubernetes-validations

    在 Kubernetes 1.28 中,CRD 验证规则被逐步升级所忽略。 从 Kubernetes 1.29 的 Alpha 2 开始,x-kubernetes-validations 的检查也用逐步升级机制处理。

    转换规则(Transition Rules)永远不会被逐步升级机制处理:只有那些不使用 oldSelf 的规则引发的错误会在其值未更改时自动按逐步升级机制处理。

  • x-kubernetes-list-type

    更改子模式的列表类型引发的错误不会被逐步升级机制处理。 例如,在具有重复项的列表上添加 set 一定会出错。

  • x-kubernetes-map-keys

    由于更改列表模式定义的映射键而引起的错误将不会被逐步升级机制处理。

  • required

    由于更改必需字段列表而引起的错误将不会被逐步升级处理。

  • properties

    添加、移除、修改属性的名称不会被逐步升级处理,但如果属性名称保持不变, 如果更改各属性的模式定义和子模式定义中的合法性检查规则,可能会被逐步升级机制处理。

  • additionalProperties

    移除先前指定的 additionalProperties 合法性检查时,不会被逐步升级机制处理。

  • metadata

    因更改对象的 metadata 中的字段而引起的错误不会被逐步升级机制处理。

合法性检查规则

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

验证规则使用通用表达式语言(CEL)来验证定制资源的值。 验证规则使用 x-kubernetes-validations 扩展包含在 CustomResourceDefinition 模式定义中。

规则的作用域是模式定义中 x-kubernetes-validations 扩展所在的位置。 CEL 表达式中的 self 变量被绑定到限定作用域的取值。

所有验证规则都是针对当前对象的:不支持跨对象或有状态的验证规则。

例如:

  ...
  openAPIV3Schema:
    type: object
    properties:
      spec:
        type: object
        x-kubernetes-validations:
          - rule: "self.minReplicas <= self.replicas"
            message: "replicas should be greater than or equal to minReplicas."
          - rule: "self.replicas <= self.maxReplicas"
            message: "replicas should be smaller than or equal to maxReplicas."
        properties:
          ...
          minReplicas:
            type: integer
          replicas:
            type: integer
          maxReplicas:
            type: integer
        required:
          - minReplicas
          - replicas
          - maxReplicas

将拒绝创建这个定制资源的请求:

apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
  name: my-new-cron-object
spec:
  minReplicas: 0
  replicas: 20
  maxReplicas: 10

返回响应为:

The CronTab "my-new-cron-object" is invalid:
* spec: Invalid value: map[string]interface {}{"maxReplicas":10, "minReplicas":0, "replicas":20}: replicas should be smaller than or equal to maxReplicas.

x-kubernetes-validations 可以有多条规则。

x-kubernetes-validations 下的 rule 代表将由 CEL 评估的表达式。

message 代表验证失败时显示的信息。如果消息没有设置,上述响应将是:

The CronTab "my-new-cron-object" is invalid:
* spec: Invalid value: map[string]interface {}{"maxReplicas":10, "minReplicas":0, "replicas":20}: failed rule: self.replicas <= self.maxReplicas

当 CRD 被创建/更新时,验证规则被编译。 如果验证规则的编译失败,CRD 的创建/更新请求将失败。 编译过程也包括类型检查。

编译失败:

  • no_matching_overload:此函数没有参数类型的重载。

    例如,像 self == true 这样的规则对一个整数类型的字段将得到错误:

    Invalid value: apiextensions.ValidationRule{Rule:"self == true", Message:""}: compilation failed: ERROR: \<input>:1:6: found no matching overload for '_==_' applied to '(int, bool)'
    
  • no_such_field:不包含所需的字段。

    例如,针对一个不存在的字段,像 self.nonExistingField > 0 这样的规则将返回错误:

    Invalid value: apiextensions.ValidationRule{Rule:"self.nonExistingField > 0", Message:""}: compilation failed: ERROR: \<input>:1:5: undefined field 'nonExistingField'
    
  • invalid argument:对宏的无效参数。

    例如,像 has(self) 这样的规则将返回错误:

    Invalid value: apiextensions.ValidationRule{Rule:"has(self)", Message:""}: compilation failed: ERROR: <input>:1:4: invalid argument to has() macro
    

验证规则例子:

规则目的
self.minReplicas <= self.replicas && self.replicas <= self.maxReplicas验证定义副本数的三个字段大小顺序是否正确
'Available' in self.stateCounts验证映射中是否存在键名为 Available的条目
(size(self.list1) == 0) != (size(self.list2) == 0)检查两个列表之一是非空的,但不是二者都非空
!('MY_KEY' in self.map1) || self['MY_KEY'].matches('^[a-zA-Z]*$')如果某个特定的键在映射中,验证映射中对应键的取值
self.envars.filter(e, e.name = 'MY_ENV').all(e, e.value.matches('^[a-zA-Z]*$')验证一个 listMap 中主键 'name' 为 'MY_ENV' 的表项的取值
has(self.expired) && self.created + self.ttl < self.expired验证 'Expired' 日期是否晚于 'Create' 日期加上 'ttl' 时长
self.health.startsWith('ok')验证 'health' 字符串字段有前缀 'ok'
self.widgets.exists(w, w.key == 'x' && w.foo < 10)验证键为 'x' 的 listMap 项的 'foo' 属性是否小于 10
type(self) == string ? self == '100%' : self == 1000在 int 型和 string 型两种情况下验证 int-or-string 字段
self.metadata.name.startsWith(self.prefix)验证对象的名称是否以另一个字段值为前缀
self.set1.all(e, !(e in self.set2))验证两个 listSet 是否不相交
size(self.names) == size(self.details) && self.names.all(n, n in self.details)验证 'details' 映射中的 'names' 来自于 listSet
size(self.clusters.filter(c, c.name == self.primary)) == 1验证 'primary' 属性在 'clusters' listMap 中出现一次且只有一次

参考:CEL 中支持的求值

  • 如果规则的作用域是某资源的根,则它可以对 CRD 的 OpenAPIv3 模式表达式中声明的任何字段进行字段选择, 以及 apiVersionkindmetadata.namemetadata.generateName。 这包括在同一表达式中对 specstatus 的字段进行选择:

      ...
      openAPIV3Schema:
        type: object
        x-kubernetes-validations:
          - rule: "self.status.availableReplicas >= self.spec.minReplicas"
        properties:
            spec:
              type: object
              properties:
                minReplicas:
                  type: integer
                ...
            status:
              type: object
              properties:
                availableReplicas:
                  type: integer
    
  • 如果规则的作用域是具有属性的对象,那么可以通过 self.field 对该对象的可访问属性进行字段选择, 而字段存在与否可以通过 has(self.field) 来检查。 在 CEL 表达式中,Null 值的字段被视为不存在的字段。

      ...
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            x-kubernetes-validations:
              - rule: "has(self.foo)"
            properties:
              ...
              foo:
                type: integer
    
  • 如果规则的作用域是一个带有 additionalProperties 的对象(即map),那么 map 的值 可以通过 self[mapKey] 访问,map 的包含性可以通过 mapKey in self 检查, map 中的所有条目可以通过 CEL 宏和函数如 self.all(...) 访问。

      ...
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            x-kubernetes-validations:
              - rule: "self['xyz'].foo > 0"
            additionalProperties:
              ...
              type: object
              properties:
                foo:
                  type: integer
    
  • 如果规则的作用域是 array,则 array 的元素可以通过 self[i] 访问,也可以通过宏和函数访问。

      ...
      openAPIV3Schema:
        type: object
        properties:
          ...
          foo:
            type: array
            x-kubernetes-validations:
              - rule: "size(self) == 1"
            items:
              type: string
    
  • 如果规则的作用域为标量,则 self 将绑定到标量值。

      ...
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            properties:
              ...
              foo:
                type: integer
                x-kubernetes-validations:
                - rule: "self > 0"
    

例子:

规则作用域字段类型规则示例
根对象self.status.actual <= self.spec.maxDesired
对象映射self.components['Widget'].priority < 10
整数列表self.values.all(value, value >= 0 && value < 100)
字符串self.startsWith('kube')

apiVersionkindmetadata.namemetadata.generateName 始终可以从对象的根目录和任何带有 x-kubernetes-embedded-resource 注解的对象访问。 其他元数据属性都不可访问。

通过 x-kubernetes-preserve-unknown-fields 保存在定制资源中的未知数据在 CEL 表达中无法访问。 这包括:

  • 使用 x-kubernetes-preserve-unknown-fields 的对象模式保留的未知字段值。

  • 属性模式为"未知类型(Unknown Type)"的对象属性。一个"未知类型"被递归定义为:

    • 一个没有类型的模式,x-kubernetes-preserve-unknown-fields 设置为 true。
    • 一个数组,其中项目模式为"未知类型"
    • 一个 additionalProperties 模式为"未知类型"的对象

只有 [a-zA-Z_.-/][a-zA-Z0-9_.-/]* 形式的属性名是可访问的。 当在表达式中访问时,可访问的属性名称会根据以下规则进行转义:

转义序列属性名称等效为
__underscores____
__dot__.
__dash__-
__slash__/
__{keyword}__CEL 保留关键字

注意:CEL 保留关键字需要与要转义的确切属性名匹配(例如,单词 sprint 中的 int 不会转义)。

转义的例子:

属性名转义属性名规则
namespaceself.__namespace__ > 0
x-propself.x__dash__prop > 0
redact__dself.redact__underscores__d > 0
stringself.startsWith('kube')

setmapx-Kubernetes-list-type 的数组的等值比较会忽略元素顺序,即 [1,2] == [2,1]。 使用 x-kubernetes-list-type 对数组进行串联时,使用 List 类型的语义:

  • setX + Y 执行一个并集操作,其中 X 中所有元素的数组位置被保留, Y 中不相交的元素被追加,保留其部分顺序。

  • mapX + Y执行合并,其中 X 中所有键的数组位置被保留, 但当 XY 的键集相交时,其值被 Y 中的值覆盖。 Y 中键值不相交的元素被附加,保留其部分顺序。

以下是 OpenAPIV3 和 CEL 类型之间的声明类型映射:

OpenAPIv3 类型CEL 类型
带有 Properties 的对象对象 / "消息类型"
带有 AdditionalProperties 的对象map
带有 x-kubernetes-embedded-type 的对象对象 / "消息类型",'apiVersion'、'kind'、'metadata.name' 和 'metadata.generateName' 都隐式包含在模式中
带有 x-kubernetes-preserve-unknown-fields 的对象对象 / "消息类型",未知字段无法从 CEL 表达式中访问
x-kubernetes-int-or-string可能是整数或字符串的动态对象,可以用 type(value) 来检查类型
数组list
带有 x-kubernetes-list-type=map 的数组列表,基于集合等值和唯一键名保证的 map 组成
带有 x-kubernetes-list-type=set 的数组列表,基于集合等值和唯一键名保证的 set 组成
布尔值boolean
数字 (各种格式)double
整数 (各种格式)int (64)
'null'null_type
字符串string
带有 format=byte (base64 编码)字符串bytes
带有 format=date 字符串timestamp (google.protobuf.Timestamp)
带有 format=datetime 字符串timestamp (google.protobuf.Timestamp)
带有 format=duration 字符串duration (google.protobuf.Duration)

参考:CEL 类型OpenAPI 类型Kubernetes 结构化模式

messageExpression 字段

message 字段定义因验证规则失败时提示的字符串,与它类似, messageExpression 允许你使用 CEL 表达式构造消息字符串。 这使你可以在验证失败消息中插入更详细的信息。messageExpression 必须计算为字符串,并且可以使用在 rule 字段中可用的变量。 例如:

x-kubernetes-validations:
- rule: "self.x <= self.maxLimit"
  messageExpression: '"x exceeded max limit of " + string(self.maxLimit)'

请记住,CEL 字符串连接(+ 运算符)不会自动转换为字符串。 如果你有一个非字符串标量,请使用 string(<value>) 函数将标量转换为字符串,如上例所示。

messageExpression 必须计算为一个字符串,并且在编写 CRD 时进行检查。 请注意,可以在同一个规则上设置 messagemessageExpression,如果两者都存在,则将使用 messageExpression。 但是,如果 messageExpression 计算出错,则将使用 message 中定义的字符串,而 messageExpression 的错误将被打印到日志。 如果在 messageExpression 中定义的 CEL 表达式产生一个空字符串或包含换行符的字符串,也会发生这种回退。

如果满足上述条件之一且未设置 message 字段,则将使用默认的检查失败消息。

messageExpression 是一个 CEL 表达式, 因此验证函数的资源使用中所列出的限制也适用于它。 如果在 messageExpression 执行期间由于资源限制而导致计算停止,则不会继续处理其他合法性检查规则。

messageExpression 设置是可选的。

message 字段

如果你要设置一个静态消息,可以提供 message 而不是 messageExpression。 如果合法性检查失败,则 message 的值将被用作不透明的错误字符串。

message 设置是可选的。

reason 字段

你可以在 validation 中添加一个机器可读的验证失败原因,以便在请求未通过此验证规则时返回。

例如:

x-kubernetes-validations:
- rule: "self.x <= self.maxLimit"
  reason: "FieldValueInvalid"

返回给调用者的 HTTP 状态码将与第一个失败的验证规则的原因匹配。 目前支持的原因有:"FieldValueInvalid"、"FieldValueForbidden"、"FieldValueRequired"、"FieldValueDuplicate"。 如果未设置或原因未知,默认使用 "FieldValueInvalid"。

reason 设置是可选的。

fieldPath 字段

你可以指定在验证失败时返回的字段路径。

例如:

x-kubernetes-validations:
- rule: "self.foo.test.x <= self.maxLimit"
  fieldPath: ".foo.test.x"

在上面的示例中,验证检查字段 x 的值应小于 maxLimit 的值。 如果未指定 fieldPath,当验证失败时,fieldPath 将默认为 self 的作用范围。 如果指定了 fieldPath,返回的错误将正确地将 fieldPath 指向字段 x 的位置。

fieldPath 值必须是相对 JSON 路径,且限定为此 x-kubernetes-validations 扩展在模式定义中的位置。 此外,它应该指向模式定义中的一个现有字段。例如,当验证检查 testMap 映射下的特定属性 foo 时, 你可以将 fieldPath 设置为 ".testMap.foo".testMap['foo']'。 如果验证要求检查两个列表中的唯一属性,fieldPath 可以设置为其中一个列表。 例如,它可以设置为 .testList1.testList2。它目前支持引用现有字段的取子操作。 更多信息请参阅 Kubernetes 中的 JSONPath 支持fieldPath 字段不支持按数字下表索引数组。

fieldPath 设置是可选的。

optionalOldSelf 字段

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

要使用此字段,必须启用特性 CRDValidationRatcheting

optionalOldSelf 字段是一个布尔字段,它会改变下文所述的转换规则的行为。 通常,在对象创建期间或在更新中引入新值时,如果无法确定 oldSelf,则不会处理转换规则。

如果 optionalOldSelf 设置为 true,则一定会处理转换规则,并且 oldSelf 的类型会被更改为 CEL Optional 类型。

optionalOldSelf 在以下情况下很有用, 模式的作者希望拥有比默认的基于相等性的行为的控制力更强的工具, 以便对新值引入更严格的约束,同时仍允许旧值通过旧的验证进行 "grandfathered"(溯源)或作逐步升级处理。

示例用法:

CEL描述
`self.foo == "foo"
[oldSelf.orValue(""), self].all(x, ["OldCase1", "OldCase2"].exists(case, x == case))
oldSelf.optMap(o, o.size()).orValue(0) < 4

验证函数

可用的函数包括:

转换规则

包含引用标识符 oldSself 的表达式的规则被隐式视为 转换规则(Transition Rule)。 转换规则允许模式作者阻止两个原本有效的状态之间的某些转换。例如:

type: string
enum: ["low", "medium", "high"]
x-kubernetes-validations:
- rule: "!(self == 'high' && oldSelf == 'low') && !(self == 'low' && oldSelf == 'high')"
  message: cannot transition directly between 'low' and 'high'

与其他规则不同,转换规则仅适用于满足以下条件的操作:

  • 更新现有对象的操作。转换规则从不适用于创建操作。
  • 旧的值和新的值都存在。仍然可以通过在父节点上放置转换规则来检查值是否已被添加或移除。 转换规则从不应用于定制资源创建。当被放置在可选字段上时,转换规则将不适用于设置或取消设置该字段的更新操作。
  • 被转换规则验证的模式节点的路径必须解析到一个在旧对象和新对象之间具有可比性的节点。 例如,列表项和它们的后代(spec.foo[10].bar)不一定能在现有对象和后来对同一对象的更新之间产生关联。

如果一个模式节点包含一个永远不能应用的转换规则,在 CRD 写入时将会产生错误,例如: "path: update rule rule cannot be set on schema because the schema or its parent schema is not mergeable"。

转换规则只允许在模式的 可关联部分(Correlatable Portions) 中使用。 如果所有 array 父模式都是 x-kubernetes-list-type=map类型的,那么该模式的一部分就是可关联的; 任何 set 或者 atomic 数组父模式都不支持确定性地将 selfoldSelf 关联起来。

这是一些转换规则的例子:

转换规则样例
用例规则
不可变self.foo == oldSelf.foo
赋值后禁止修改/删除oldSelf != 'bar' || self == 'bar' or !has(oldSelf.field) || has(self.field)
仅附加的 setself.all(element, element in oldSelf)
如果之前的值为 X,则新值只能为 A 或 B,不能为 Y 或 ZoldSelf != 'X' || self in ['A', 'B']
单调(非递减)计数器self >= oldSelf

验证函数的资源使用

当你创建或更新一个使用验证规则的 CustomResourceDefinition 时, API 服务器会检查运行这些验证规则可能产生的影响。 如果一个规则的执行成本过高,API 服务器会拒绝创建或更新操作,并返回一个错误信息。

运行时也使用类似的系统来观察解释器的行动。如果解释器执行了太多的指令,规则的执行将被停止,并且会产生一个错误。

每个 CustomResourceDefinition 也被允许有一定数量的资源来完成其所有验证规则的执行。 如果在创建时估计其规则的总和超过了这个限制,那么也会发生验证错误。

如果你只指定那些无论输入量有多大都要花费相同时间的规则,你不太可能遇到验证的资源预算问题。

例如,一个断言 self.foo == 1 的规则本身不存在因为资源预算组验证而导致被拒绝的风险。

但是,如果 foo 是一个字符串,而你定义了一个验证规则 self.foo.contains("someString"), 这个规则需要更长的时间来执行,取决于 foo 有多长。

另一个例子是如果 foo 是一个数组,而你指定了验证规则 self.foo.all(x, x > 5)。 如果没有给出 foo 的长度限制,成本系统总是假设最坏的情况,这将发生在任何可以被迭代的事物上(list、map 等)。

因此,通过 maxItemsmaxPropertiesmaxLength 进行限制被认为是最佳实践, 以在验证规则中处理任何内容,以防止在成本估算期间验证错误。例如,给定具有一个规则的模式:

openAPIV3Schema:
  type: object
  properties:
    foo:
      type: array
      items:
        type: string
      x-kubernetes-validations:
        - rule: "self.all(x, x.contains('a string'))"

API 服务器以验证预算为由拒绝该规则,并显示错误:

spec.validation.openAPIV3Schema.properties[spec].properties[foo].x-kubernetes-validations[0].rule: Forbidden:
CEL rule exceeded budget by more than 100x (try simplifying the rule, or adding maxItems, maxProperties, and
maxLength where arrays, maps, and strings are used)

这个拒绝会发生是因为 self.all 意味着对 foo 中的每一个字符串调用 contains(), 而这又会检查给定的字符串是否包含 'a string'。如果没有限制,这是一个非常昂贵的规则。

如果你不指定任何验证限制,这个规则的估计成本将超过每条规则的成本限制。 但如果你在适当的地方添加限制,该规则将被允许:

openAPIV3Schema:
  type: object
  properties:
    foo:
      type: array
      maxItems: 25
      items:
        type: string
        maxLength: 10
      x-kubernetes-validations:
        - rule: "self.all(x, x.contains('a string'))"

成本评估系统除了考虑规则本身的估计成本外,还考虑到规则将被执行的次数。 例如,下面这个规则的估计成本与前面的例子相同(尽管该规则现在被定义在单个数组项上):

openAPIV3Schema:
  type: object
  properties:
    foo:
      type: array
      maxItems: 25
      items:
        type: string
        x-kubernetes-validations:
          - rule: "self.contains('a string'))"
        maxLength: 10

如果在一个列表内部的一个列表有一个使用 self.all 的验证规则,那就会比具有相同规则的非嵌套列表的成本高得多。 一个在非嵌套列表中被允许的规则可能需要在两个嵌套列表中设置较低的限制才能被允许。 例如,即使没有设置限制,下面的规则也是允许的:

openAPIV3Schema:
  type: object
  properties:
    foo:
      type: array
      items:
        type: integer
    x-kubernetes-validations:
      - rule: "self.all(x, x == 5)"

但是同样的规则在下面的模式中(添加了一个嵌套数组)产生了一个验证错误:

openAPIV3Schema:
  type: object
  properties:
    foo:
      type: array
      items:
        type: array
        items:
          type: integer
        x-kubernetes-validations:
          - rule: "self.all(x, x == 5)"

这是因为 foo 的每一项本身就是一个数组,而每一个子数组依次调用 self.all。 在使用验证规则的地方,尽可能避免嵌套的列表和字典。

设置默认值

设置默认值的功能允许在 OpenAPI v3 合法性检查模式定义中设置默认值:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: crontabs.stable.example.com
spec:
  group: stable.example.com
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        # openAPIV3Schema 是用来检查定制对象的模式定义
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                cronSpec:
                  type: string
                  pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$'
                  default: "5 0 * * *"
                image:
                  type: string
                replicas:
                  type: integer
                  minimum: 1
                  maximum: 10
                  default: 1
  scope: Namespaced
  names:
    plural: crontabs
    singular: crontab
    kind: CronTab
    shortNames:
    - ct

使用此 CRD 定义时,cronSpecreplicas 都会被设置默认值:

apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
  name: my-new-cron-object
spec:
  image: my-awesome-cron-image

会生成:

apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
  name: my-new-cron-object
spec:
  cronSpec: "5 0 * * *"
  image: my-awesome-cron-image
  replicas: 1

默认值设定的行为发生在定制对象上:

  • 在向 API 服务器发送的请求中,基于请求版本的设定设置默认值;
  • 在从 etcd 读取对象时,使用存储版本来设置默认值;
  • 在 Mutating 准入控制插件执行非空的补丁操作时,基于准入 Webhook 对象 版本设置默认值。

从 etcd 中读取数据时所应用的默认值设置不会被写回到 etcd 中。 需要通过 API 执行更新请求才能将这种方式设置的默认值写回到 etcd。

默认值一定会被剪裁(除了 metadata 字段的默认值设置),且必须通过所提供的模式定义的检查。

针对 x-kubernetes-embedded-resource: true 节点(或者包含 metadata 字段的结构的默认值) 的 metadata 字段的默认值设置不会在 CustomResourceDefinition 创建时被剪裁, 而是在处理请求的字段剪裁阶段被删除。

设置默认值和字段是否可为空(Nullable)

对于未设置其 nullable 标志的字段或者将该标志设置为 false 的字段,其空值(Null) 会在设置默认值之前被剪裁掉。如果对应字段存在默认值,则默认值会被赋予该字段。 当 nullable 被设置为 true 时,字段的空值会被保留,且不会在设置默认值时被覆盖。

例如,给定下面的 OpenAPI 模式定义:

type: object
properties:
  spec:
    type: object
    properties:
      foo:
        type: string
        nullable: false
        default: "default"
      bar:
        type: string
        nullable: true
      baz:
        type: string

像下面这样创建一个为 foobarbaz 设置空值的对象时:

spec:
  foo: null
  bar: null
  baz: null

其结果会是这样:

spec:
  foo: "default"
  bar: null

其中的 foo 字段被剪裁掉并重新设置默认值,因为该字段是不可为空的。 bar 字段的 nullable: true 使得其能够保有其空值。 baz 字段则被完全剪裁掉,因为该字段是不可为空的,并且没有默认值设置。

以 OpenAPI 形式发布合法性检查模式

CustomResourceDefinition 的结构化的启用了剪裁的 OpenAPI v3 合法性检查模式会在 Kubernetes API 服务器上作为 OpenAPI 3 和 OpenAPI v2 发布出来。建议使用 OpenAPI v3 文档,因为它是 CustomResourceDefinition OpenAPI v3 验证模式的无损表示,而 OpenAPI v2 表示有损转换。

kubectl 命令行工具会基于所发布的模式定义来执行客户端的合法性检查 (kubectl createkubectl apply),为定制资源的模式定义提供解释(kubectl explain)。 所发布的模式还可被用于其他目的,例如生成客户端或者生成文档。

Compatibility with OpenAPI V2

为了与 OpenAPI V2 兼容,OpenAPI v3 验证模式会对 OpenAPI v2 模式进行有损转换。 该模式显示在 OpenAPI v2 规范中的 definitions paths 字段中。 OpenAPI v3 合法性检查模式定义会被转换为 OpenAPI v2 模式定义,并出现在 OpenAPI v2 规范definitionspaths 字段中。

在转换过程中会发生以下修改,目的是保持与 1.13 版本以前的 kubectl 工具兼容。 这些修改可以避免 kubectl 过于严格,以至于拒绝它无法理解的 OpenAPI 模式定义。 转换过程不会更改 CRD 中定义的合法性检查模式定义,因此不会影响到 API 服务器中的合法性检查

  1. 以下字段会被移除,因为它们在 OpenAPI v2 中不支持。

    • 字段 allOfanyOfoneOfnot 会被删除
  2. 如果设置了 nullable: true,我们会丢弃 typenullableitemsproperties OpenAPI v2 无法表达 Nullable。为了避免 kubectl 拒绝正常的对象,这一转换是必要的。

额外的打印列

kubectl 工具依赖服务器端的输出格式化。你的集群的 API 服务器决定 kubectl get 命令要显示的列有哪些。 你可以为 CustomResourceDefinition 定制这些要打印的列。 下面的例子添加了 SpecReplicasAge 列:

将此 CustomResourceDefinition 保存到 resourcedefinition.yaml 文件:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: crontabs.stable.example.com
spec:
  group: stable.example.com
  scope: Namespaced
  names:
    plural: crontabs
    singular: crontab
    kind: CronTab
    shortNames:
    - ct
  versions:
  - name: v1
    served: true
    storage: true
    schema:
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            properties:
              cronSpec:
                type: string
              image:
                type: string
              replicas:
                type: integer
    additionalPrinterColumns:
    - name: Spec
      type: string
      description: The cron spec defining the interval a CronJob is run
      jsonPath: .spec.cronSpec
    - name: Replicas
      type: integer
      description: The number of jobs launched by the CronJob
      jsonPath: .spec.replicas
    - name: Age
      type: date
      jsonPath: .metadata.creationTimestamp

创建 CustomResourceDefinition:

kubectl apply -f resourcedefinition.yaml

使用前文中的 my-crontab.yaml 创建一个实例。

启用服务器端打印输出:

kubectl get crontab my-new-cron-object

注意输出中的 NAMESPECREPLICASAGE 列:

NAME                 SPEC        REPLICAS   AGE
my-new-cron-object   * * * * *   1          7s

优先级

每个列都包含一个 priority(优先级)字段。当前,优先级用来区分标准视图(Standard View)和宽视图(Wide View)(使用 -o wide 标志)中显示的列:

  • 优先级为 0 的列会在标准视图中显示。
  • 优先级大于 0 的列只会在宽视图中显示。

类型

列的 type 字段可以是以下值之一 (比较 OpenAPI v3 数据类型):

  • integer – 非浮点数字
  • number – 浮点数字
  • string – 字符串
  • booleantruefalse
  • date – 显示为以自此时间戳以来经过的时长

如果定制资源中的值与列中指定的类型不匹配,该值会被忽略。 你可以通过定制资源的合法性检查来确保取值类型是正确的。

格式

列的 format 字段可以是以下值之一:

  • int32
  • int64
  • float
  • double
  • byte
  • date
  • date-time
  • password

列的 format 字段控制 kubectl 打印对应取值时采用的风格。

子资源

定制资源支持 /status/scale 子资源。

通过在 CustomResourceDefinition 中定义 statusscale, 可以有选择地启用这些子资源。

Status 子资源

当启用了 status 子资源时,对应定制资源的 /status 子资源会被暴露出来。

  • status 和 spec 内容分别用定制资源内的 .status.spec JSON 路径来表达;

  • /status 子资源的 PUT 请求要求使用定制资源对象作为其输入,但会忽略 status 之外的所有内容。

  • /status 子资源的 PUT 请求仅对定制资源的 status 内容进行合法性检查。

  • 对定制资源的 PUTPOSTPATCH 请求会忽略 status 内容的改变。

  • 对所有变更请求,除非改变是针对 .metadata.status.metadata.generation 的取值都会增加。

  • 在 CRD OpenAPI 合法性检查模式定义的根节点,只允许存在以下结构:

    • description
    • example
    • exclusiveMaximum
    • exclusiveMinimum
    • externalDocs
    • format
    • items
    • maximum
    • maxItems
    • maxLength
    • minimum
    • minItems
    • minLength
    • multipleOf
    • pattern
    • properties
    • required
    • title
    • type
    • uniqueItems

Scale 子资源

当启用了 scale 子资源时,定制资源的 /scale 子资源就被暴露出来。 针对 /scale 所发送的对象是 autoscaling/v1.Scale

为了启用 scale 子资源,CustomResourceDefinition 定义了以下字段:

  • specReplicasPath 指定定制资源内与 scale.spec.replicas 对应的 JSON 路径。

    • 此字段为必需值。
    • 只可以使用 .spec 下的 JSON 路径,只可使用带句点的路径。
    • 如果定制资源的 specReplicasPath 下没有取值,则针对 /scale 子资源执行 GET 操作时会返回错误。
  • statusReplicasPath 指定定制资源内与 scale.status.replicas 对应的 JSON 路径。

    • 此字段为必需值。
    • 只可以使用 .status 下的 JSON 路径,只可使用带句点的路径。
    • 如果定制资源的 statusReplicasPath 下没有取值,则针对 /scale 子资源的副本个数状态值默认为 0。
  • labelSelectorPath 指定定制资源内与 Scale.Status.Selector 对应的 JSON 路径。

    • 此字段为可选值。
    • 此字段必须设置才能使用 HPA 和 VPA。
    • 只可以使用 .status.spec 下的 JSON 路径,只可使用带句点的路径。
    • 如果定制资源的 labelSelectorPath 下没有取值,则针对 /scale 子资源的选择算符状态值默认为空字符串。
    • 此 JSON 路径所指向的字段必须是一个字符串字段(而不是复合的选择算符结构), 其中包含标签选择算符串行化的字符串形式。

在下面的例子中,statusscale 子资源都被启用。

将此 CustomResourceDefinition 保存到 resourcedefinition.yaml 文件:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: crontabs.stable.example.com
spec:
  group: stable.example.com
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                cronSpec:
                  type: string
                image:
                  type: string
                replicas:
                  type: integer
            status:
              type: object
              properties:
                replicas:
                  type: integer
                labelSelector:
                  type: string
      # subresources 描述定制资源的子资源
      subresources:
        # status 启用 status 子资源
        status: {}
        # scale 启用 scale 子资源
        scale:
          # specReplicasPath 定义定制资源中对应 scale.spec.replicas 的 JSON 路径
          specReplicasPath: .spec.replicas
          # statusReplicasPath 定义定制资源中对应 scale.status.replicas 的 JSON 路径 
          statusReplicasPath: .status.replicas
          # labelSelectorPath  定义定制资源中对应 scale.status.selector 的 JSON 路径 
          labelSelectorPath: .status.labelSelector
  scope: Namespaced
  names:
    plural: crontabs
    singular: crontab
    kind: CronTab
    shortNames:
    - ct

之后创建此 CustomResourceDefinition:

kubectl apply -f resourcedefinition.yaml

CustomResourceDefinition 对象创建完毕之后,你可以创建定制对象,。

如果你将下面的 YAML 保存到 my-crontab.yaml 文件:

apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
  name: my-new-cron-object
spec:
  cronSpec: "* * * * */5"
  image: my-awesome-cron-image
  replicas: 3

并创建定制对象:

kubectl apply -f my-crontab.yaml

那么会创建新的、命名空间作用域的 RESTful API 端点:

/apis/stable.example.com/v1/namespaces/*/crontabs/status

/apis/stable.example.com/v1/namespaces/*/crontabs/scale

定制资源可以使用 kubectl scale 命令来扩缩其规模。 例如下面的命令将前面创建的定制资源的 .spec.replicas 设置为 5:

kubectl scale --replicas=5 crontabs/my-new-cron-object
crontabs "my-new-cron-object" scaled

kubectl get crontabs my-new-cron-object -o jsonpath='{.spec.replicas}'
5

你可以使用 PodDisruptionBudget 来保护启用了 scale 子资源的定制资源。

分类

分类(Categories)是定制资源所归属的分组资源列表(例如,all)。 你可以使用 kubectl get <分类名称> 来列举属于某分类的所有资源。

下面的示例在 CustomResourceDefinition 中将 all 添加到分类列表中, 并展示了如何使用 kubectl get all 来输出定制资源:

将下面的 CustomResourceDefinition 保存到 resourcedefinition.yaml 文件中:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: crontabs.stable.example.com
spec:
  group: stable.example.com
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                cronSpec:
                  type: string
                image:
                  type: string
                replicas:
                  type: integer
  scope: Namespaced
  names:
    plural: crontabs
    singular: crontab
    kind: CronTab
    shortNames:
    - ct
    # categories 是定制资源所归属的分类资源列表
    categories:
    - all

之后创建此 CRD:

kubectl apply -f resourcedefinition.yaml

创建了 CustomResourceDefinition 对象之后,你可以创建定制对象。

将下面的 YAML 保存到 my-crontab.yaml 中:

apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
  name: my-new-cron-object
spec:
  cronSpec: "* * * * */5"
  image: my-awesome-cron-image

并创建定制对象:

kubectl apply -f my-crontab.yaml

你可以在使用 kubectl get 时指定分类:

kubectl get all

输出中将包含类别为 CronTab 的定制资源:

NAME                          AGE
crontabs/my-new-cron-object   3s

接下来

4.11.2.2 - CustomResourceDefinition 的版本

本页介绍如何添加版本信息到 CustomResourceDefinitions。 目的是标明 CustomResourceDefinitions 的稳定级别或者服务于 API 升级。 API 升级时需要在不同 API 表示形式之间进行转换。 本页还描述如何将对象从一个版本升级到另一个版本。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你应该对定制资源有一些初步了解。

你的 Kubernetes 服务器版本必须不低于版本 v1.16. 要获知版本信息,请输入 kubectl version.

概览

CustomResourceDefinition API 提供了引入和升级 CustomResourceDefinition 新版本所用的工作流程。

创建 CustomResourceDefinition 时,会在 CustomResourceDefinition spec.versions 列表设置适当的稳定级别和版本号。例如,v1beta1 表示第一个版本尚未稳定。 所有定制资源对象将首先用这个版本保存。

创建 CustomResourceDefinition 后,客户端可以开始使用 v1beta1 API。

稍后可能需要添加新版本,例如 v1

添加新版本:

  1. 选择一种转化策略。由于定制资源对象需要能够两种版本都可用, 这意味着它们有时会以与存储版本不同的版本来提供服务。为了能够做到这一点, 有时必须在它们存储的版本和提供的版本之间进行转换。如果转换涉及模式变更, 并且需要自定义逻辑,则应该使用 Webhook 来完成。如果没有模式变更, 则可使用默认的 None 转换策略,为不同版本提供服务时只有 apiVersion 字段会被改变。
  2. 如果使用转换 Webhook,请创建并部署转换 Webhook。更多详细信息请参见 Webhook 转换
  3. 更新 CustomResourceDefinition,将新版本设置为 served:true,加入到 spec.versions 列表。另外,还要设置 spec.conversion 字段为所选的转换策略。 如果使用转换 Webhook,请配置 spec.conversion.webhookClientConfig 来调用 Webhook。

添加新版本后,客户端可以逐步迁移到新版本。 让某些客户使用旧版本的同时支持其他人使用新版本是相当安全的。

将存储的对象迁移到新版本:

  1. 请参阅将现有对象升级到新的存储版本节。

    对于客户来说,在将对象升级到新的存储版本之前、期间和之后使用旧版本和新版本都是安全的。

删除旧版本:

  1. 确保所有客户端都已完全迁移到新版本。 可以查看 kube-apiserver 的日志以识别仍通过旧版本进行访问的所有客户端。
  2. spec.versions 列表中将旧版本的 served 设置为 false。 如果仍有客户端意外地使用旧版本,他们可能开始会报告采用旧版本尝试访问定制资源的错误消息。 如果发生这种情况,请将旧版本的 served:true 恢复,然后迁移余下的客户端使用新版本,然后重复此步骤。
  3. 确保已完成将现有对象升级到新存储版本的步骤。
    1. 在 CustomResourceDefinition 的 spec.versions 列表中,确认新版本的 storage 已被设置为 true
    2. 确认旧版本不在 CustomResourceDefinition status.storedVersions 中。
  4. 从 CustomResourceDefinition spec.versions 列表中删除旧版本。
  5. 在转换 Webhooks 中放弃对旧版本的转换支持。

指定多个版本

CustomResourceDefinition API 的 versions 字段可用于支持你所开发的定制资源的多个版本。 版本可以具有不同的模式,并且转换 Webhook 可以在多个版本之间转换定制资源。 在适当的情况下,Webhook 转换应遵循 Kubernetes API 约定。 具体来说,请查阅 API 变更文档 以获取一些有用的问题和建议。

下面的示例显示了两个版本的 CustomResourceDefinition。 第一个例子中假设所有的版本使用相同的模式而它们之间没有转换。 YAML 中的注释提供了更多背景信息。

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  # name 必须匹配后面 spec 中的字段,且使用格式 <plural>.<group>
  name: crontabs.example.com
spec:
  # 组名,用于 REST API: /apis/<group>/<version>
  group: example.com
  # 此 CustomResourceDefinition 所支持的版本列表
  versions:
  - name: v1beta1
    # 每个 version 可以通过 served 标志启用或禁止
    served: true
    # 有且只能有一个 version 必须被标记为存储版本
    storage: true
    # schema 是必需字段
    schema:
      openAPIV3Schema:
        type: object
        properties:
          host:
            type: string
          port:
            type: string
  - name: v1
    served: true
    storage: false
    schema:
      openAPIV3Schema:
        type: object
        properties:
          host:
            type: string
          port:
            type: string
  # conversion 节是 Kubernetes 1.13+ 版本引入的,其默认值为无转换,即 strategy 子字段设置为 None。
  conversion:
    # None 转换假定所有版本采用相同的模式定义,仅仅将定制资源的 apiVersion 设置为合适的值.
    strategy: None
  # 可以是 Namespaced 或 Cluster
  scope: Namespaced
  names:
    # 名称的复数形式,用于 URL: /apis/<group>/<version>/<plural>
    plural: crontabs
    # 名称的单数形式,用于在命令行接口和显示时作为其别名
    singular: crontab
    # kind 通常是驼峰编码(CamelCased)的单数形式,用于资源清单中
    kind: CronTab
    # shortNames 允许你在命令行接口中使用更短的字符串来匹配你的资源
    shortNames:
    - ct

# 在 v1.16 中被弃用以推荐使用 apiextensions.k8s.io/v1
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  # name 必须匹配后面 spec 中的字段,且使用格式 <plural>.<group>
  name: crontabs.example.com
spec:
  # 组名,用于 REST API: /apis/<group>/<version>
  group: example.com
  # 此 CustomResourceDefinition 所支持的版本列表
  versions:
  - name: v1beta1
    # 每个 version 可以通过 served 标志启用或禁止
    served: true
    # 有且只能有一个 version 必须被标记为存储版本
    storage: true
  - name: v1
    served: true
    storage: false
  validation:
    openAPIV3Schema:
      type: object
      properties:
        host:
          type: string
        port:
          type: string
  # conversion 节是 Kubernetes 1.13+ 版本引入的,其默认值为无转换,即 strategy 子字段设置为 None。
  conversion:
    # None 转换假定所有版本采用相同的模式定义,仅仅将定制资源的 apiVersion 设置为合适的值.
    strategy: None
  # 可以是 Namespaced 或 Cluster
  scope: Namespaced
  names:
    # 名称的复数形式,用于 URL: /apis/<group>/<version>/<plural>
    plural: crontabs
    # 名称的单数形式,用于在命令行接口和显示时作为其别名
    singular: crontab
    # kind 通常是大驼峰编码(PascalCased)的单数形式,用于资源清单中
    kind: CronTab
    # shortNames 允许你在命令行接口中使用更短的字符串来匹配你的资源
    shortNames:
    - ct

你可以将 CustomResourceDefinition 存储在 YAML 文件中,然后使用 kubectl apply 来创建它。

kubectl apply -f my-versioned-crontab.yaml

在创建之后,API 服务器开始在 HTTP REST 端点上为每个已启用的版本提供服务。 在上面的示例中,API 版本可以在 /apis/example.com/v1beta1/apis/example.com/v1 处获得。

版本优先级

不考虑 CustomResourceDefinition 中版本被定义的顺序,kubectl 使用具有最高优先级的版本作为访问对象的默认版本。 优先级是通过解析 name 字段来确定版本号、稳定性(GA、Beta 或 Alpha) 以及该稳定性级别内的序列。

用于对版本进行排序的算法在设计上与 Kubernetes 项目对 Kubernetes 版本进行排序的方式相同。 版本以 v 开头跟一个数字,一个可选的 beta 或者 alpha 和一个可选的附加数字作为版本信息。 从广义上讲,版本字符串可能看起来像 v2 或者 v2beta1。 使用以下算法对版本进行排序:

  • 遵循 Kubernetes 版本模式的条目在不符合条件的条目之前进行排序。
  • 对于遵循 Kubernetes 版本模式的条目,版本字符串的数字部分从最大到最小排序。
  • 如果第一个数字后面有字符串 betaalpha,它们首先按去掉 betaalpha 之后的版本号排序(相当于 GA 版本),之后按 beta 先、alpha 后的顺序排序,
  • 如果 betaalpha 之后还有另一个数字,那么也会针对这些数字从大到小排序。
  • 不符合上述格式的字符串按字母顺序排序,数字部分不经过特殊处理。 请注意,在下面的示例中,foo1 排在 foo10 之前。 这与遵循 Kubernetes 版本模式的条目的数字部分排序不同。

如果查看以下版本排序列表,这些规则就容易懂了:

- v10
- v2
- v1
- v11beta2
- v10beta3
- v3beta1
- v12alpha1
- v11alpha2
- foo1
- foo10

对于指定多个版本中的示例,版本排序顺序为 v1,后跟着 v1beta1。 这导致了 kubectl 命令使用 v1 作为默认版本,除非所提供的对象指定了版本。

版本废弃

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

从 v1.19 开始,CustomResourceDefinition 可以指示其定义的资源的特定版本已废弃。 当该资源的已废弃版本发出 API 请求时,会在 API 响应中以报头的形式返回警告消息。 如果需要,可以自定义每个不推荐使用的资源版本的警告消息。

定制的警告消息应该标明废弃的 API 组、版本和类别(kind), 并且应该标明应该使用(如果有的话)哪个 API 组、版本和类别作为替代。

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
  name: crontabs.example.com
spec:
  group: example.com
  names:
    plural: crontabs
    singular: crontab
    kind: CronTab
  scope: Namespaced
  versions:
  - name: v1alpha1
    served: true
    storage: false
    # 此属性标明此定制资源的 v1alpha1 版本已被弃用。
    # 发给此版本的 API 请求会在服务器响应中收到警告消息头。
    deprecated: true
    # 此属性设置用来覆盖返回给发送 v1alpha1 API 请求的客户端的默认警告信息。
    deprecationWarning: "example.com/v1alpha1 CronTab is deprecated; see http://example.com/v1alpha1-v1 for instructions to migrate to example.com/v1 CronTab"
    schema: ...
  - name: v1beta1
    served: true
    # 此属性标明该定制资源的 v1beta1 版本已被弃用。
    # 发给此版本的 API 请求会在服务器响应中收到警告消息头。
    # 针对此版本的请求所返回的是默认的警告消息。
    deprecated: true
    schema: ...
  - name: v1
    served: true
    storage: true
    schema: ...

# 在 v1.16 中弃用以推荐使用  apiextensions.k8s.io/v1
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: crontabs.example.com
spec:
  group: example.com
  names:
    plural: crontabs
    singular: crontab
    kind: CronTab
  scope: Namespaced
  validation: ...
  versions:
  - name: v1alpha1
    served: true
    storage: false
    # 此属性标明此定制资源的 v1alpha1 版本已被弃用。
    # 发给此版本的 API 请求会在服务器响应中收到警告消息头。
    deprecated: true
    # 此属性设置用来覆盖返回给发送 v1alpha1 API 请求的客户端的默认警告信息。
    deprecationWarning: "example.com/v1alpha1 CronTab is deprecated; see http://example.com/v1alpha1-v1 for instructions to migrate to example.com/v1 CronTab"
  - name: v1beta1
    served: true
    # 此属性标明该定制资源的 v1beta1 版本已被弃用。
    # 发给此版本的 API 请求会在服务器响应中收到警告消息头。
    # 针对此版本的请求所返回的是默认的警告消息。
    deprecated: true
  - name: v1
    served: true
    storage: true

版本删除

在为所有提供旧版本自定义资源的集群将现有存储数据迁移到新 API 版本,并且从 CustomResourceDefinition 的 status.storedVersions 中删除旧版本之前,无法从 CustomResourceDefinition 清单文件中删除旧 API 版本。

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
  name: crontabs.example.com
spec:
  group: example.com
  names:
    plural: crontabs
    singular: crontab
    kind: CronTab
  scope: Namespaced
  versions:
  - name: v1beta1
    # 此属性标明该自定义资源的 v1beta1 版本已不再提供。
    # 发给此版本的 API 请求会在服务器响应中收到未找到的错误。
    served: false
    schema: ...
  - name: v1
    served: true
    # 新提供的版本应该设置为存储版本。
    storage: true
    schema: ...

Webhook 转换

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

上面的例子在版本之间有一个 None 转换,它只在转换时设置 apiVersion 字段而不改变对象的其余部分。 API 服务器还支持在需要转换时调用外部服务的 webhook 转换。例如:

  • 定制资源的请求版本与其存储版本不同。
  • 使用某版本创建了 Watch 请求,但所更改对象以另一版本存储。
  • 定制资源的 PUT 请求所针对版本与存储版本不同。

为了涵盖所有这些情况并优化 API 服务器所作的转换,转换请求可以包含多个对象, 以便减少外部调用。Webhook 应该独立执行各个转换。

编写一个转换 Webhook 服务器

请参考定制资源转换 Webhook 服务器的实现; 该实现在 Kubernetes e2e 测试中得到验证。 Webhook 处理由 API 服务器发送的 ConversionReview 请求,并在 ConversionResponse 中封装发回转换结果。 请注意,请求包含需要独立转换的定制资源列表,这些对象在被转换之后不能改变其在列表中的顺序。 该示例服务器的组织方式使其可以复用于其他转换。大多数常见代码都位于 framework 文件中, 只留下一个函数用于实现不同的转换。

被允许的变更

转换 Webhook 不可以更改被转换对象的 metadata 中除 labelsannotations 之外的任何属性。 尝试更改 nameUIDnamespace 时都会导致引起转换的请求失败。 所有其他变更都被忽略。

部署转换 Webhook 服务

用于部署转换 Webhook 的文档与准入 Webhook 服务示例相同。 这里的假设是转换 Webhook 服务器被部署为 default 名字空间中名为 example-conversion-webhook-server 的服务,并在路径 /crdconvert 上处理请求。

配置 CustomResourceDefinition 以使用转换 Webhook

通过修改 spec 中的 conversion 部分,可以扩展 None 转换示例来使用转换 Webhook。

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  # name 必须匹配后面 spec 中的字段,且使用格式 <plural>.<group>
  name: crontabs.example.com
spec:
  # 组名,用于 REST API: /apis/<group>/<version>
  group: example.com
  # 此 CustomResourceDefinition 所支持的版本列表
  versions:
  - name: v1beta1
    # 每个 version 可以通过 served 标志启用或禁止
    served: true
    # 有且只能有一个 version 必须被标记为存储版本
    storage: true
    # 当不存在顶级模式定义时,每个版本(version)可以定义其自身的模式
    schema:
      openAPIV3Schema:
        type: object
        properties:
          hostPort:
            type: string
  - name: v1
    served: true
    storage: false
    schema:
      openAPIV3Schema:
        type: object
        properties:
          host:
            type: string
          port:
            type: string
  conversion:
    # Webhook strategy 告诉 API 服务器调用外部 Webhook 来完成定制资源之间的转换
    strategy: Webhook
    # 当 strategy 为 "Webhook" 时,webhook 属性是必需的,该属性配置将被 API 服务器调用的 Webhook 端点
    webhook:
      # conversionReviewVersions 标明 Webhook 所能理解或偏好使用的
      # ConversionReview 对象版本。
      # API 服务器所能理解的列表中的第一个版本会被发送到 Webhook
      # Webhook 必须按所接收到的版本响应一个 ConversionReview 对象
      conversionReviewVersions: ["v1","v1beta1"]
      clientConfig:
        service:
          namespace: default
          name: example-conversion-webhook-server
          path: /crdconvert
        caBundle: "Ci0tLS0tQk...<base64-encoded PEM bundle>...tLS0K"
  # 可以是 Namespaced 或 Cluster
  scope: Namespaced
  names:
    # 名称的复数形式,用于 URL: /apis/<group>/<version>/<plural>
    plural: crontabs
    # 名称的单数形式,用于在命令行接口和显示时作为其别名
    singular: crontab
    # kind 通常是驼峰编码(CamelCased)的单数形式,用于资源清单中
    kind: CronTab
    # shortNames 允许你在命令行接口中使用更短的字符串来匹配你的资源
    shortNames:
    - ct

# 在 v1.16 中被弃用以推荐使用 apiextensions.k8s.io/v1
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  # name 必须匹配后面 spec 中的字段,且使用格式 <plural>.<group>
  name: crontabs.example.com
spec:
  # 组名,用于 REST API: /apis/<group>/<version>
  group: example.com
  # 裁剪掉下面的 OpenAPI 模式中未曾定义的对象字段
  preserveUnknownFields: false
  # 此 CustomResourceDefinition 所支持的版本列表
  versions:
  - name: v1beta1
    # 每个 version 可以通过 served 标志启用或禁止
    served: true
    # 有且只能有一个 version 必须被标记为存储版本
    storage: true
    # 当不存在顶级模式定义时,每个版本(version)可以定义其自身的模式
    schema:
      openAPIV3Schema:
        type: object
        properties:
          hostPort:
            type: string
  - name: v1
    served: true
    storage: false
    schema:
      openAPIV3Schema:
        type: object
        properties:
          host:
            type: string
          port:
            type: string
  conversion:
    # Webhook strategy 告诉 API 服务器调用外部 Webhook 来完成定制资源
    strategy: Webhook
    # 当 strategy 为 "Webhook" 时,webhookClientConfig 属性是必需的
    # 该属性配置将被 API 服务器调用的 Webhook 端点
    webhookClientConfig:
      service:
        namespace: default
        name: example-conversion-webhook-server
        path: /crdconvert
      caBundle: "Ci0tLS0tQk...<base64-encoded PEM bundle>...tLS0K"
  # 可以是 Namespaced 或 Cluster
  scope: Namespaced
  names:
    # 名称的复数形式,用于 URL: /apis/<group>/<version>/<plural>
    plural: crontabs
    # 名称的单数形式,用于在命令行接口和显示时作为其别名
    singular: crontab
    # kind 通常是驼峰编码(CamelCased)的单数形式,用于资源清单中
    kind: CronTab
    # shortNames 允许你在命令行接口中使用更短的字符串来匹配你的资源
    shortNames:
    - ct

你可以将 CustomResourceDefinition 保存在 YAML 文件中,然后使用 kubectl apply 来应用它。

kubectl apply -f my-versioned-crontab-with-conversion.yaml

在应用新更改之前,请确保转换服务器已启动并正在运行。

调用 Webhook

API 服务器一旦确定请求应发送到转换 Webhook,它需要知道如何调用 Webhook。 这是在 webhookClientConfig 中指定的 Webhook 配置。

转换 Webhook 可以通过 URL 或服务引用来调用,并且可以选择包含自定义 CA 包, 以用于验证 TLS 连接。

URL

url 以标准 URL 形式给出 Webhook 的位置(scheme://host:port/path)。 host 不应引用集群中运行的服务,而应通过指定 service 字段来提供服务引用。 在某些 API 服务器中,host 可以通过外部 DNS 进行解析(即 kube-apiserver 无法解析集群内 DNS,那样会违反分层规则)。 host 也可以是 IP 地址。

请注意,除非你非常小心地在所有运行着可能调用 Webhook 的 API 服务器的主机上运行此 Webhook, 否则将 localhost127.0.0.1 用作 host 是风险很大的。 这样的安装可能是不可移植的,或者不容易在一个新的集群中运行。

HTTP 协议必须为 https;URL 必须以 https:// 开头。

尝试使用用户或基本身份验证(例如,使用 user:password@)是不允许的。 URL 片段(#...)和查询参数(?...)也是不允许的。

下面是为调用 URL 来执行转换 Webhook 的示例,其中期望使用系统信任根来验证 TLS 证书,因此未指定 caBundle:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
...
spec:
  ...
  conversion:
    strategy: Webhook
    webhook:
      clientConfig:
        url: "https://my-webhook.example.com:9443/my-webhook-path"
...

# 在 v1.16 中已弃用以推荐使用 apiextensions.k8s.io/v1
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
...
spec:
  ...
  conversion:
    strategy: Webhook
    webhookClientConfig:
      url: "https://my-webhook.example.com:9443/my-webhook-path"
...

服务引用

webhookClientConfig 内部的 service 段是对转换 Webhook 服务的引用。 如果 Webhook 在集群中运行,则应使用 service 而不是 url。 服务的名字空间和名称是必需的。端口是可选的,默认为 443。 路径是可选的,默认为/

下面配置中,服务配置为在端口 1234、子路径 /my-path 上被调用。 例子中针对 ServerName my-service-name.my-service-namespace.svc, 使用自定义 CA 包验证 TLS 连接。

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
...
spec:
  ...
  conversion:
    strategy: Webhook
    webhook:
      clientConfig:
        service:
          namespace: my-service-namespace
          name: my-service-name
          path: /my-path
          port: 1234
        caBundle: "Ci0tLS0tQk...<base64-encoded PEM bundle>...tLS0K"
...

#  v1.16 中被弃用以推荐使用 apiextensions.k8s.io/v1
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
...
spec:
  ...
  conversion:
    strategy: Webhook
    webhookClientConfig:
      service:
        namespace: my-service-namespace
        name: my-service-name
        path: /my-path
        port: 1234
      caBundle: "Ci0tLS0tQk...<base64-encoded PEM bundle>...tLS0K"
...

Webhook 请求和响应

请求

向 Webhook 发起请求的动词是 POST,请求的 Content-Typeapplication/json。 请求的主题为 JSON 序列化形式的 apiextensions.k8s.io API 组的 ConversionReview API 对象。

Webhook 可以在其 CustomResourceDefinition 中使用 conversionReviewVersions 字段设置它们接受的 ConversionReview 对象的版本:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
...
spec:
  ...
  conversion:
    strategy: Webhook
    webhook:
      conversionReviewVersions: ["v1", "v1beta1"]
      ...

创建 apiextensions.k8s.io/v1 版本的自定义资源定义时, conversionReviewVersions 是必填字段。 Webhook 要求支持至少一个 ConversionReview 当前和以前的 API 服务器可以理解的版本。

# v1.16 已弃用以推荐使用 apiextensions.k8s.io/v1
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
...
spec:
  ...
  conversion:
    strategy: Webhook
    conversionReviewVersions: ["v1", "v1beta1"]
    ...

创建 apiextensions.k8s.io/v1beta1 定制资源定义时若未指定 conversionReviewVersions,则默认值为 v1beta1。

API 服务器将 conversionReviewVersions 列表中他们所支持的第一个 ConversionReview 资源版本发送给 Webhook。 如果列表中的版本都不被 API 服务器支持,则无法创建自定义资源定义。 如果某 API 服务器遇到之前创建的转换 Webhook 配置,并且该配置不支持 API 服务器知道如何发送的任何 ConversionReview 版本,调用 Webhook 的尝试会失败。

下面的示例显示了包含在 ConversionReview 对象中的数据, 该请求意在将 CronTab 对象转换为 example.com/v1

{
  "apiVersion": "apiextensions.k8s.io/v1",
  "kind": "ConversionReview",
  "request": {
    # 用来唯一标识此转换调用的随机 UID
    "uid": "705ab4f5-6393-11e8-b7cc-42010a800002",
    
    # 对象要转换到的目标 API 组和版本
    "desiredAPIVersion": "example.com/v1",
    
    # 要转换的对象列表,其中可能包含一个或多个对象,版本可能相同也可能不同
    "objects": [
      {
        "kind": "CronTab",
        "apiVersion": "example.com/v1beta1",
        "metadata": {
          "creationTimestamp": "2019-09-04T14:03:02Z",
          "name": "local-crontab",
          "namespace": "default",
          "resourceVersion": "143",
          "uid": "3415a7fc-162b-4300-b5da-fd6083580d66"
        },
        "hostPort": "localhost:1234"
      },
      {
        "kind": "CronTab",
        "apiVersion": "example.com/v1beta1",
        "metadata": {
          "creationTimestamp": "2019-09-03T13:02:01Z",
          "name": "remote-crontab",
          "resourceVersion": "12893",
          "uid": "359a83ec-b575-460d-b553-d859cedde8a0"
        },
        "hostPort": "example.com:2345"
      }
    ]
  }
}

{
  # v1.16 中已废弃以推荐使用 apiextensions.k8s.io/v1
  "apiVersion": "apiextensions.k8s.io/v1beta1",
  "kind": "ConversionReview",
  "request": {
    # 用来唯一标识此转换调用的随机 UID
    "uid": "705ab4f5-6393-11e8-b7cc-42010a800002",
    
    # 对象要转换到的目标 API 组和版本
    "desiredAPIVersion": "example.com/v1",
    
    # 要转换的对象列表,其中可能包含一个或多个对象,版本可能相同也可能不同
    "objects": [
      {
        "kind": "CronTab",
        "apiVersion": "example.com/v1beta1",
        "metadata": {
          "creationTimestamp": "2019-09-04T14:03:02Z",
          "name": "local-crontab",
          "namespace": "default",
          "resourceVersion": "143",
          "uid": "3415a7fc-162b-4300-b5da-fd6083580d66"
        },
        "hostPort": "localhost:1234"
      },
      {
        "kind": "CronTab",
        "apiVersion": "example.com/v1beta1",
        "metadata": {
          "creationTimestamp": "2019-09-03T13:02:01Z",
          "name": "remote-crontab",
          "resourceVersion": "12893",
          "uid": "359a83ec-b575-460d-b553-d859cedde8a0"
        },
        "hostPort": "example.com:2345"
      }
    ]
  }
}

响应

Webhook 响应包含 200 HTTP 状态代码、Content-Type: application/json, 在主体中包含 JSON 序列化形式的数据,在 response 节中给出 ConversionReview 对象(与发送的版本相同)。

如果转换成功,则 Webhook 应该返回包含以下字段的 response 节:

  • uid,从发送到 webhook 的 request.uid 复制而来
  • result,设置为 {"status":"Success"}}
  • convertedObjects,包含来自 request.objects 的所有对象,均已转换为 request.desiredVersion

Webhook 的最简单成功响应示例:

{
  "apiVersion": "apiextensions.k8s.io/v1",
  "kind": "ConversionReview",
  "response": {
    # 必须与 <request.uid> 匹配
    "uid": "705ab4f5-6393-11e8-b7cc-42010a800002",
    "result": {
      "status": "Success"
    },
    # 这里的对象必须与 request.objects 中的对象顺序相同并且其 apiVersion 被设置为 <request.desiredAPIVersion>。
    # kind、metadata.uid、metadata.name 和 metadata.namespace 等字段都不可被 Webhook 修改。
    # Webhook 可以更改 metadata.labels 和 metadata.annotations 字段值。
    # Webhook 对 metadata 中其他字段的更改都会被忽略
    "convertedObjects": [
      {
        "kind": "CronTab",
        "apiVersion": "example.com/v1",
        "metadata": {
          "creationTimestamp": "2019-09-04T14:03:02Z",
          "name": "local-crontab",
          "namespace": "default",
          "resourceVersion": "143",
          "uid": "3415a7fc-162b-4300-b5da-fd6083580d66"
        },
        "host": "localhost",
        "port": "1234"
      },
      {
        "kind": "CronTab",
        "apiVersion": "example.com/v1",
        "metadata": {
          "creationTimestamp": "2019-09-03T13:02:01Z",
          "name": "remote-crontab",
          "resourceVersion": "12893",
          "uid": "359a83ec-b575-460d-b553-d859cedde8a0"
        },
        "host": "example.com",
        "port": "2345"
      }
    ]
  }
}

{
  # v1.16 中已弃用以推荐使用  apiextensions.k8s.io/v1
  "apiVersion": "apiextensions.k8s.io/v1beta1",
  "kind": "ConversionReview",
  "response": {
    # 必须与 <request.uid> 匹配
    "uid": "705ab4f5-6393-11e8-b7cc-42010a800002",
    "result": {
      "status": "Failed"
    },
    # 这里的对象必须与 request.objects 中的对象顺序相同并且其 apiVersion 被设置为 <request.desiredAPIVersion>。
    # kind、metadata.uid、metadata.name 和 metadata.namespace 等字段都不可被 Webhook 修改。
    # Webhook 可以更改 metadata.labels 和 metadata.annotations 字段值。
    # Webhook 对 metadata 中其他字段的更改都会被忽略。
    "convertedObjects": [
      {
        "kind": "CronTab",
        "apiVersion": "example.com/v1",
        "metadata": {
          "creationTimestamp": "2019-09-04T14:03:02Z",
          "name": "local-crontab",
          "namespace": "default",
          "resourceVersion": "143",
          "uid": "3415a7fc-162b-4300-b5da-fd6083580d66"
        },
        "host": "localhost",
        "port": "1234"
      },
      {
        "kind": "CronTab",
        "apiVersion": "example.com/v1",
        "metadata": {
          "creationTimestamp": "2019-09-03T13:02:01Z",
          "name": "remote-crontab",
          "resourceVersion": "12893",
          "uid": "359a83ec-b575-460d-b553-d859cedde8a0"
        },
        "host": "example.com",
        "port": "2345"
      }
    ]
  }
}

如果转换失败,则 Webhook 应该返回包含以下字段的 response 节:

  • uid,从发送到 Webhook 的 request.uid 复制而来
  • result,设置为 {"status": "Failed"}

来自 Webhook 的响应示例,指示转换请求失败,并带有可选消息:

{
  "apiVersion": "apiextensions.k8s.io/v1",
  "kind": "ConversionReview",
  "response": {
    "uid": "<value from request.uid>",
    "result": {
      "status": "Failed",
      "message": "hostPort could not be parsed into a separate host and port"
    }
  }
}

{
  # v1.16 中弃用以推荐使用 apiextensions.k8s.io/v1
  "apiVersion": "apiextensions.k8s.io/v1beta1",
  "kind": "ConversionReview",
  "response": {
    "uid": "<value from request.uid>",
    "result": {
      "status": "Failed",
      "message": "hostPort could not be parsed into a separate host and port"
    }
  }
}

编写、读取和更新版本化的 CustomResourceDefinition 对象

写入对象时,将存储为写入时指定的存储版本。如果存储版本发生变化, 现有对象永远不会被自动转换。然而,新创建或被更新的对象将以新的存储版本写入。 对象写入的版本不再被支持是有可能的。

当读取对象时,你需要在路径中指定版本。 你可以请求当前提供的任意版本的对象。 如果所指定的版本与对象的存储版本不同,Kubernetes 会按所请求的版本将对象返回, 但磁盘上存储的对象不会更改。

在为读取请求提供服务时正返回的对象会发生什么取决于 CRD 的 spec.conversion 中指定的内容:

  • 如果所指定的 strategy 值是默认的 None,则针对对象的唯一修改是更改其 apiVersion 字符串, 并且可能修剪未知字段(取决于配置)。 请注意,如果存储和请求版本之间的模式不同,这不太可能导致好的结果。 尤其是如果在相同的数据类不同版本中采用不同字段来表示时,不应使用此策略。
  • 如果指定了 Webhook 转换,则此机制将控制转换。

如果你更新一个现有对象,它将以当前的存储版本被重写。 这是可以将对象从一个版本改到另一个版本的唯一办法。

为了说明这一点,请考虑以下假设的一系列事件:

  1. 存储版本是 v1beta1。你创建一个对象。该对象以版本 v1beta1 存储。
  2. 你将为 CustomResourceDefinition 添加版本 v1,并将其指定为存储版本。 此处 v1v1beta1 的模式是相同的,这通常是在 Kubernetes 生态系统中将 API 提升为稳定版时的情况。
  3. 你使用版本 v1beta1 来读取你的对象,然后你再次用版本 v1 读取对象。 除了 apiVersion 字段之外,返回的两个对象是完全相同的。
  4. 你创建一个新对象。该对象存储为版本 v1。 你现在有两个对象,其中一个是 v1beta1,另一个是 v1
  5. 你更新第一个对象。该对象现在以版本 v1 保存,因为 v1 是当前的存储版本。

以前的存储版本

API 服务器在状态字段 storedVersions 中记录曾被标记为存储版本的每个版本。 对象可能以任何曾被指定为存储版本的版本保存。 存储中不会出现从未成为存储版本的版本的对象。

将现有对象升级到新的存储版本

弃用版本并删除其支持时,请选择存储升级过程。

选项 1: 使用存储版本迁移程序(Storage Version Migrator)

  1. 运行存储版本迁移程序
  2. 从 CustomResourceDefinition 的 status.storedVersions 字段中去掉老的版本。

选项 2: 手动将现有对象升级到新的存储版本

以下是从 v1beta1 升级到 v1 的示例过程。

  1. 在 CustomResourceDefinition 文件中将 v1 设置为存储版本,并使用 kubectl 应用它。 storedVersions现在是 v1beta1, v1
  2. 编写升级过程以列出所有现有对象并使用相同内容将其写回存储。 这会强制后端使用当前存储版本(即 v1)写入对象。
  3. 从 CustomResourceDefinition 的 status.storedVersions 字段中删除 v1beta1

4.11.3 - 安装一个扩展的 API server

安装扩展的 API 服务器来使用聚合层以让 Kubernetes API 服务器使用 其它 API 进行扩展, 这些 API 不是核心 Kubernetes API 的一部分。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

安装一个扩展的 API 服务器来使用聚合层

以下步骤描述如何 在一个高层次 设置一个扩展的 apiserver。无论你使用的是 YAML 配置还是使用 API,这些步骤都适用。 目前我们正在尝试区分出两者的区别。有关使用 YAML 配置的具体示例,你可以在 Kubernetes 库中查看 sample-apiserver

或者,你可以使用现有的第三方解决方案,例如 apiserver-builder, 它将生成框架并自动执行以下所有步骤。

  1. 确保启用了 APIService API(检查 --runtime-config)。默认应该是启用的,除非被特意关闭了。
  2. 你可能需要制定一个 RBAC 规则,以允许你添加 APIService 对象,或让你的集群管理员创建一个。 (由于 API 扩展会影响整个集群,因此不建议在实时集群中对 API 扩展进行测试/开发/调试)
  3. 创建 Kubernetes 命名空间,扩展的 api-service 将运行在该命名空间中。
  4. 创建(或获取)用来签署服务器证书的 CA 证书,扩展 api-server 中将使用该证书做 HTTPS 连接。
  5. 为 api-server 创建一个服务端的证书(或秘钥)以使用 HTTPS。这个证书应该由上述的 CA 签署。 同时应该还要有一个 Kube DNS 名称的 CN,这是从 Kubernetes 服务派生而来的, 格式为 <service name>.<service name namespace>.svc
  6. 使用命名空间中的证书(或秘钥)创建一个 Kubernetes secret。
  7. 为扩展 api-server 创建一个 Kubernetes Deployment,并确保以卷的方式挂载了 Secret。 它应该包含对扩展 api-server 镜像的引用。Deployment 也应该在同一个命名空间中。
  1. 确保你的扩展 apiserver 从该卷中加载了那些证书,并在 HTTPS 握手过程中使用它们。
  2. 在你的命名空间中创建一个 Kubernetes 服务账号。
  3. 为资源允许的操作创建 Kubernetes 集群角色。
  4. 用你命名空间中的服务账号创建一个 Kubernetes 集群角色绑定,绑定到你创建的角色上。
  5. 用你命名空间中的服务账号创建一个 Kubernetes 集群角色绑定,绑定到 system:auth-delegator 集群角色,以将 auth 决策委派给 Kubernetes 核心 API 服务器。
  6. 以你命名空间中的服务账号创建一个 Kubernetes 集群角色绑定,绑定到 extension-apiserver-authentication-reader 角色。 这将让你的扩展 api-server 能够访问 extension-apiserver-authentication configmap。
  1. 创建一个 Kubernetes apiservice。 上述的 CA 证书应该使用 base64 编码,剥离新行并用作 apiservice 中的 spec.caBundle。 该资源不应放到任何名字空间。如果使用了 kube-aggregator API,那么只需要传入 PEM 编码的 CA 绑定,因为 base 64 编码已经完成了。
  2. 使用 kubectl 来获得你的资源。 它应该返回 "找不到资源"。此消息表示一切正常,但你目前还没有创建该资源类型的对象。

接下来

4.11.4 - 配置多个调度器

Kubernetes 自带了一个默认调度器,其详细描述请查阅 这里。 如果默认调度器不适合你的需求,你可以实现自己的调度器。 而且,你甚至可以和默认调度器一起同时运行多个调度器,并告诉 Kubernetes 为每个 Pod 使用哪个调度器。 让我们通过一个例子讲述如何在 Kubernetes 中运行多个调度器。

关于实现调度器的具体细节描述超出了本文范围。 请参考 kube-scheduler 的实现,规范示例代码位于 pkg/scheduler

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

打包调度器

将调度器可执行文件打包到容器镜像中。出于示例目的,可以使用默认调度器 (kube-scheduler)作为第二个调度器。 克隆 GitHub 上 Kubernetes 源代码, 并编译构建源代码。

git clone https://github.com/kubernetes/kubernetes.git
cd kubernetes
make

创建一个包含 kube-scheduler 二进制文件的容器镜像。用于构建镜像的 Dockerfile 内容如下:

FROM busybox
ADD ./_output/local/bin/linux/amd64/kube-scheduler /usr/local/bin/kube-scheduler

将文件保存为 Dockerfile,构建镜像并将其推送到镜像仓库。 此示例将镜像推送到 Google 容器镜像仓库(GCR)。 有关详细信息,请阅读 GCR 文档。 或者,你也可以使用 Docker Hub。 有关更多详细信息,请参阅 Docker Hub 文档

# 这里使用的镜像名称和仓库只是一个例子
docker build -t gcr.io/my-gcp-project/my-kube-scheduler:1.0 .
gcloud docker -- push gcr.io/my-gcp-project/my-kube-scheduler:1.0

为调度器定义 Kubernetes Deployment

现在将调度器放在容器镜像中,为它创建一个 Pod 配置,并在 Kubernetes 集群中 运行它。但是与其在集群中直接创建一个 Pod,不如使用 Deployment。 Deployment 管理一个 ReplicaSet, ReplicaSet 再管理 Pod,从而使调度器能够免受一些故障的影响。 以下是 Deployment 配置,将其保存为 my-scheduler.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-scheduler
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: my-scheduler-as-kube-scheduler
subjects:
- kind: ServiceAccount
  name: my-scheduler
  namespace: kube-system
roleRef:
  kind: ClusterRole
  name: system:kube-scheduler
  apiGroup: rbac.authorization.k8s.io
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: my-scheduler-config
  namespace: kube-system
data:
  my-scheduler-config.yaml: |
    apiVersion: kubescheduler.config.k8s.io/v1beta2
    kind: KubeSchedulerConfiguration
    profiles:
      - schedulerName: my-scheduler
    leaderElection:
      leaderElect: false    
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: my-scheduler-as-volume-scheduler
subjects:
- kind: ServiceAccount
  name: my-scheduler
  namespace: kube-system
roleRef:
  kind: ClusterRole
  name: system:volume-scheduler
  apiGroup: rbac.authorization.k8s.io
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    component: scheduler
    tier: control-plane
  name: my-scheduler
  namespace: kube-system
spec:
  selector:
    matchLabels:
      component: scheduler
      tier: control-plane
  replicas: 1
  template:
    metadata:
      labels:
        component: scheduler
        tier: control-plane
        version: second
    spec:
      serviceAccountName: my-scheduler
      containers:
      - command:
        - /usr/local/bin/kube-scheduler
        - --config=/etc/kubernetes/my-scheduler/my-scheduler-config.yaml
        image: gcr.io/my-gcp-project/my-kube-scheduler:1.0
        livenessProbe:
          httpGet:
            path: /healthz
            port: 10259
            scheme: HTTPS
          initialDelaySeconds: 15
        name: kube-second-scheduler
        readinessProbe:
          httpGet:
            path: /healthz
            port: 10259
            scheme: HTTPS
        resources:
          requests:
            cpu: '0.1'
        securityContext:
          privileged: false
        volumeMounts:
          - name: config-volume
            mountPath: /etc/kubernetes/my-scheduler
      hostNetwork: false
      hostPID: false
      volumes:
        - name: config-volume
          configMap:
            name: my-scheduler-config

在以上的清单中,你使用 KubeSchedulerConfiguration 来自定义调度器实现的行为。当使用 --config 选项进行初始化时,该配置被传递到 kube-schedulermy-scheduler-config ConfigMap 存储配置数据。 my-scheduler Deployment 的 Pod 将 my-scheduler-config ConfigMap 挂载为一个卷。

在前面提到的调度器配置中,你的调度器呈现为一个 KubeSchedulerProfile

还要注意,我们创建了一个专用的服务账号 my-scheduler 并将集群角色 system:kube-scheduler 绑定到它,以便它可以获得与 kube-scheduler 相同的权限。

请参阅 kube-scheduler 文档 获取其他命令行参数以及 Scheduler 配置参考 获取自定义 kube-scheduler 配置的详细说明。

在集群中运行第二个调度器

为了在 Kubernetes 集群中运行我们的第二个调度器,在 Kubernetes 集群中创建上面配置中指定的 Deployment:

kubectl create -f my-scheduler.yaml

验证调度器 Pod 正在运行:

kubectl get pods --namespace=kube-system

输出类似于:

NAME                                           READY     STATUS    RESTARTS   AGE
....
my-scheduler-lnf4s-4744f                       1/1       Running   0          2m
...

此列表中,除了默认的 kube-scheduler Pod 之外,你应该还能看到处于 “Running” 状态的 my-scheduler Pod。

启用领导者选举

要在启用了 leader 选举的情况下运行多调度器,你必须执行以下操作:

更新你的 YAML 文件中的 my-scheduler-config ConfigMap 里的 KubeSchedulerConfiguration 相关字段如下:

  • leaderElection.leaderElect to true
  • leaderElection.resourceNamespace to <lock-object-namespace>
  • leaderElection.resourceName to <lock-object-name>

如果在集群上启用了 RBAC,则必须更新 system:kube-scheduler 集群角色。 将调度器名称添加到应用了 endpointsleases 资源的规则的 resourceNames 中,如以下示例所示:

kubectl edit clusterrole system:kube-scheduler
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
  name: system:kube-scheduler
rules:
  - apiGroups:
      - coordination.k8s.io
    resources:
      - leases
    verbs:
      - create
  - apiGroups:
      - coordination.k8s.io
    resourceNames:
      - kube-scheduler
      - my-scheduler
    resources:
      - leases
    verbs:
      - get
      - update
  - apiGroups:
      - ""
    resourceNames:
      - kube-scheduler
      - my-scheduler
    resources:
      - endpoints
    verbs:
      - delete
      - get
      - patch
      - update

为 Pod 指定调度器

现在第二个调度器正在运行,创建一些 Pod,并指定它们由默认调度器或部署的调度器进行调度。 为了使用特定的调度器调度给定的 Pod,在那个 Pod 的 spec 中指定调度器的名称。让我们看看三个例子。

  • Pod spec 没有任何调度器名称

    apiVersion: v1
    kind: Pod
    metadata:
      name: no-annotation
      labels:
        name: multischeduler-example
    spec:
      containers:
      - name: pod-with-no-annotation-container
        image: registry.k8s.io/pause:2.0

    如果未提供调度器名称,则会使用 default-scheduler 自动调度 pod。

    将此文件另存为 pod1.yaml,并将其提交给 Kubernetes 集群。

    kubectl create -f pod1.yaml
    
  • Pod spec 设置为 default-scheduler

    apiVersion: v1
    kind: Pod
    metadata:
      name: annotation-default-scheduler
      labels:
        name: multischeduler-example
    spec:
      schedulerName: default-scheduler
      containers:
      - name: pod-with-default-annotation-container
        image: registry.k8s.io/pause:2.0
    

    通过将调度器名称作为 spec.schedulerName 参数的值来指定调度器。 在这种情况下,我们提供默认调度器的名称,即 default-scheduler

    将此文件另存为 pod2.yaml,并将其提交给 Kubernetes 集群。

    kubectl create -f pod2.yaml
    
  • Pod spec 设置为 my-scheduler

    apiVersion: v1
    kind: Pod
    metadata:
      name: annotation-second-scheduler
      labels:
        name: multischeduler-example
    spec:
      schedulerName: my-scheduler
      containers:
      - name: pod-with-second-annotation-container
        image: registry.k8s.io/pause:2.0
    

    在这种情况下,我们指定此 Pod 使用我们部署的 my-scheduler 来调度。 请注意,spec.schedulerName 参数的值应该与调度器提供的 KubeSchedulerProfile 中的 schedulerName 字段相匹配。

    将此文件另存为 pod3.yaml,并将其提交给 Kubernetes 集群。

    kubectl create -f pod3.yaml
    

确认所有三个 Pod 都在运行。

kubectl get pods

验证是否使用所需的调度器调度了 Pod

为了更容易地完成这些示例,我们没有验证 Pod 实际上是使用所需的调度程序调度的。 我们可以通过更改 Pod 的顺序和上面的部署配置提交来验证这一点。 如果我们在提交调度器部署配置之前将所有 Pod 配置提交给 Kubernetes 集群, 我们将看到注解了 annotation-second-scheduler 的 Pod 始终处于 Pending 状态, 而其他两个 Pod 被调度。 一旦我们提交调度器部署配置并且我们的新调度器开始运行,注解了 annotation-second-scheduler 的 Pod 就能被调度。

或者,可以查看事件日志中的 Scheduled 条目,以验证是否由所需的调度器调度了 Pod。

kubectl get events

你也可以使用自定义调度器配置 或自定义容器镜像,用于集群的主调度器,方法是在相关控制平面节点上修改其静态 Pod 清单。

4.11.5 - 使用 HTTP 代理访问 Kubernetes API

本文说明如何使用 HTTP 代理访问 Kubernetes API。

准备开始

  • 你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

    要获知版本信息,请输入 kubectl version.

如果你的集群中还没有任何应用,使用如下命令启动一个 Hello World 应用:

kubectl create deployment node-hello --image=gcr.io/google-samples/node-hello:1.0 --port=8080

使用 kubectl 启动代理服务器

使用如下命令启动 Kubernetes API 服务器的代理:

kubectl proxy --port=8080

探究 Kubernetes API

当代理服务器在运行时,你可以通过 curlwget 或者浏览器访问 API。

获取 API 版本:

curl http://localhost:8080/api/

输出应该类似这样:

{
  "kind": "APIVersions",
  "versions": [
    "v1"
  ],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "10.0.2.15:8443"
    }
  ]
}

获取 Pod 列表:

curl http://localhost:8080/api/v1/namespaces/default/pods

输出应该类似这样:

{
  "kind": "PodList",
  "apiVersion": "v1",
  "metadata": {
    "resourceVersion": "33074"
  },
  "items": [
    {
      "metadata": {
        "name": "kubernetes-bootcamp-2321272333-ix8pt",
        "generateName": "kubernetes-bootcamp-2321272333-",
        "namespace": "default",
        "uid": "ba21457c-6b1d-11e6-85f7-1ef9f1dab92b",
        "resourceVersion": "33003",
        "creationTimestamp": "2016-08-25T23:43:30Z",
        "labels": {
          "pod-template-hash": "2321272333",
          "run": "kubernetes-bootcamp"
        },
        ...
}

接下来

想了解更多信息,请参阅 kubectl 代理

4.11.6 - 使用 SOCKS5 代理访问 Kubernetes API

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

本文展示了如何使用 SOCKS5 代理访问远程 Kubernetes 集群的 API。 当你要访问的集群不直接在公共 Internet 上公开其 API 时,这很有用。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你的 Kubernetes 服务器版本必须不低于版本 v1.24. 要获知版本信息,请输入 kubectl version.

你需要 SSH 客户端软件(ssh 工具),并在远程服务器上运行 SSH 服务。 你必须能够登录到远程服务器上的 SSH 服务。

任务上下文

图 1 表示你将在此任务中实现的目标。

  • 你有一台在后面的步骤中被称为本地计算机的客户端计算机,你将在这台计算机上创建与 Kubernetes API 对话的请求。
  • Kubernetes 服务器/API 托管在远程服务器上。
  • 你将使用 SSH 客户端和服务器软件在本地和远程服务器之间创建安全的 SOCKS5 隧道。 客户端和 Kubernetes API 之间的 HTTPS 流量将流经 SOCKS5 隧道,该隧道本身通过 SSH 进行隧道传输。
graph LR; subgraph local[本地客户端机器] client([客户端])-. 本地
流量.-> local_ssh[本地 SSH
SOCKS5 代理]; end local_ssh[SSH
SOCKS5
代理]-- SSH 隧道 -->sshd subgraph remote[远程服务器] sshd[SSH
服务器]-- 本地流量 -->service1; end client([客户端])-. 通过代理传递的
HTTPS 流量 .->service1[Kubernetes API]; classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000; classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff; classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5; class ingress,service1,service2,pod1,pod2,pod3,pod4 k8s; class client plain; class cluster cluster;

图 1. SOCKS5 教程组件

使用 SSH 创建 SOCKS5 代理

下面的命令在你的客户端计算机和远程 SOCKS 服务器之间启动一个 SOCKS5 代理:

# 运行此命令后,SSH 隧道继续在前台运行
ssh -D 1080 -q -N username@kubernetes-remote-server.example
  • -D 1080: 在本地端口 1080 上打开一个 SOCKS 代理。
  • -q: 静音模式。导致大多数警告和诊断消息被抑制。
  • -N: 不执行远程命令。仅用于转发端口。
  • username@kubernetes-remote-server.example:运行 Kubernetes 集群的远程 SSH 服务器(例如:堡垒主机)。

客户端配置

要通过代理访问 Kubernetes API 服务器,你必须指示 kubectl 通过我们之前创建的 SOCKS5 代理发送查询。 这可以通过设置适当的环境变量或通过 kubeconfig 文件中的 proxy-url 属性来实现。 使用环境变量:

export HTTPS_PROXY=socks5://localhost:1080

要始终在特定的 kubectl 上下文中使用此设置,请在 ~/.kube/config 文件中为相关的 cluster 条目设置 proxy-url 属性。例如:

apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: LRMEMMW2 # 简化以便阅读
    # “Kubernetes API”服务器,换言之,kubernetes-remote-server.example 的 IP 地址
    server: https://<API_SERVER_IP_ADDRESS>:6443
    # 上图中的 “SSH SOCKS5代理”(内置 DNS 解析)
    proxy-url: socks5://localhost:1080
  name: default
contexts:
- context:
    cluster: default
    user: default
  name: default
current-context: default
kind: Config
preferences: {}
users:
- name: default
  user:
    client-certificate-data: LS0tLS1CR== # 节略,为了便于阅读
    client-key-data: LS0tLS1CRUdJT=      # 节略,为了便于阅读

一旦你通过前面提到的 SSH 命令创建了隧道,并定义了环境变量或 proxy-url 属性, 你就可以通过该代理与你的集群交互。例如:

kubectl get pods
NAMESPACE     NAME                                     READY   STATUS      RESTARTS   AGE
kube-system   coredns-85cb69466-klwq8                  1/1     Running     0          5m46s

清理

通过在运行它的终端上按 CTRL+C 来停止 SSH 端口转发进程。

在终端中键入 unset https_proxy 以停止通过代理转发 http 流量。

进一步阅读

4.11.7 - 设置 Konnectivity 服务

Konnectivity 服务为控制平面提供集群通信的 TCP 级别代理。

准备开始

你需要有一个 Kubernetes 集群,并且 kubectl 命令可以与集群通信。 建议在至少有两个不充当控制平面主机的节点的集群上运行本教程。 如果你还没有集群,可以使用 minikube 创建一个集群。

配置 Konnectivity 服务

接下来的步骤需要出口配置,比如:

apiVersion: apiserver.k8s.io/v1beta1
kind: EgressSelectorConfiguration
egressSelections:
# 由于我们要控制集群的出站流量,所以将 “cluster” 用作 name。
# 其他支持的值有 “etcd” 和 “controlplane”。
- name: cluster
  connection:
    # 这一属性将控制 API 服务器 Konnectivity 服务器之间的协议。
    # 支持的值为 “GRPC” 和 “HTTPConnect”。
    # 最终用户不会察觉这两种模式之间的差异。
    # 你需要将 Konnectivity 服务器设为在相同模式下工作。
    proxyProtocol: GRPC
    transport:
      # 此属性控制 API 服务器使用哪种传输方式与 Konnectivity 服务器通信。
      # 如果 Konnectivity 服务器与 API 服务器位于同一台机器上,建议使用 UDS。
      # 你需要将 Konnectivity 服务器配置为侦听同一个 UDS 套接字。
      # 另一个支持的传输方式是 “tcp”。
      # 你将需要设置 TLS config 以确保 TCP 传输的安全。
      uds:
        udsName: /etc/kubernetes/konnectivity-server/konnectivity-server.socket

你需要配置 API 服务器来使用 Konnectivity 服务,并将网络流量定向到集群节点:

  1. 确保服务账号令牌卷投射特性被启用。 该特性自 Kubernetes v1.20 起默认已被启用。

  2. 创建一个出站流量配置文件,比如 admin/konnectivity/egress-selector-configuration.yaml

  3. 将 API 服务器的 --egress-selector-config-file 参数设置为你的 API 服务器的离站流量配置文件路径。

  4. 如果你在使用 UDS 连接,须将卷配置添加到 kube-apiserver:

    spec:
      containers:
        volumeMounts:
        - name: konnectivity-uds
          mountPath: /etc/kubernetes/konnectivity-server
          readOnly: false
      volumes:
      - name: konnectivity-uds
        hostPath:
          path: /etc/kubernetes/konnectivity-server
          type: DirectoryOrCreate
    

为 konnectivity-server 生成或者取得证书和 kubeconfig 文件。 例如,你可以使用 OpenSSL 命令行工具,基于存放在某控制面主机上 /etc/kubernetes/pki/ca.crt 文件中的集群 CA 证书来发放一个 X.509 证书。

openssl req -subj "/CN=system:konnectivity-server" -new -newkey rsa:2048 -nodes -out konnectivity.csr -keyout konnectivity.key
openssl x509 -req -in konnectivity.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out konnectivity.crt -days 375 -sha256
SERVER=$(kubectl config view -o jsonpath='{.clusters..server}')
kubectl --kubeconfig /etc/kubernetes/konnectivity-server.conf config set-credentials system:konnectivity-server --client-certificate konnectivity.crt --client-key konnectivity.key --embed-certs=true
kubectl --kubeconfig /etc/kubernetes/konnectivity-server.conf config set-cluster kubernetes --server "$SERVER" --certificate-authority /etc/kubernetes/pki/ca.crt --embed-certs=true
kubectl --kubeconfig /etc/kubernetes/konnectivity-server.conf config set-context system:konnectivity-server@kubernetes --cluster kubernetes --user system:konnectivity-server
kubectl --kubeconfig /etc/kubernetes/konnectivity-server.conf config use-context system:konnectivity-server@kubernetes
rm -f konnectivity.crt konnectivity.key konnectivity.csr

接下来,你需要部署 Konnectivity 服务器和代理。 kubernetes-sigs/apiserver-network-proxy 是一个参考实现。

在控制面节点上部署 Konnectivity 服务。 下面提供的 konnectivity-server.yaml 配置清单假定在你的集群中 Kubernetes 组件都是部署为静态 Pod 的。 如果不是,你可以将 Konnectivity 服务部署为 DaemonSet。

apiVersion: v1
kind: Pod
metadata:
  name: konnectivity-server
  namespace: kube-system
spec:
  priorityClassName: system-cluster-critical
  hostNetwork: true
  containers:
  - name: konnectivity-server-container
    image: registry.k8s.io/kas-network-proxy/proxy-server:v0.0.37
    command: ["/proxy-server"]
    args: [
            "--logtostderr=true",
            # 下一行需与 egressSelectorConfiguration 中设置的值一致。
            "--uds-name=/etc/kubernetes/konnectivity-server/konnectivity-server.socket",
            "--delete-existing-uds-file",
            # 下面两行假定 Konnectivity 服务器被部署在与 apiserver 相同的机器上,
            # 并且该 API 服务器的证书和密钥位于指定的位置。
            "--cluster-cert=/etc/kubernetes/pki/apiserver.crt",
            "--cluster-key=/etc/kubernetes/pki/apiserver.key",
            # 下一行需与 egressSelectorConfiguration 中设置的值一致。
            "--mode=grpc",
            "--server-port=0",
            "--agent-port=8132",
            "--admin-port=8133",
            "--health-port=8134",
            "--agent-namespace=kube-system",
            "--agent-service-account=konnectivity-agent",
            "--kubeconfig=/etc/kubernetes/konnectivity-server.conf",
            "--authentication-audience=system:konnectivity-server"
            ]
    livenessProbe:
      httpGet:
        scheme: HTTP
        host: 127.0.0.1
        port: 8134
        path: /healthz
      initialDelaySeconds: 30
      timeoutSeconds: 60
    ports:
    - name: agentport
      containerPort: 8132
      hostPort: 8132
    - name: adminport
      containerPort: 8133
      hostPort: 8133
    - name: healthport
      containerPort: 8134
      hostPort: 8134
    volumeMounts:
    - name: k8s-certs
      mountPath: /etc/kubernetes/pki
      readOnly: true
    - name: kubeconfig
      mountPath: /etc/kubernetes/konnectivity-server.conf
      readOnly: true
    - name: konnectivity-uds
      mountPath: /etc/kubernetes/konnectivity-server
      readOnly: false
  volumes:
  - name: k8s-certs
    hostPath:
      path: /etc/kubernetes/pki
  - name: kubeconfig
    hostPath:
      path: /etc/kubernetes/konnectivity-server.conf
      type: FileOrCreate
  - name: konnectivity-uds
    hostPath:
      path: /etc/kubernetes/konnectivity-server
      type: DirectoryOrCreate

在你的集群中部署 Konnectivity 代理:

apiVersion: apps/v1
# 作为另一种替代方案,你可以将代理部署为 Deployment。
# 没有必要在每个节点上都有一个代理。
kind: DaemonSet
metadata:
  labels:
    addonmanager.kubernetes.io/mode: Reconcile
    k8s-app: konnectivity-agent
  namespace: kube-system
  name: konnectivity-agent
spec:
  selector:
    matchLabels:
      k8s-app: konnectivity-agent
  template:
    metadata:
      labels:
        k8s-app: konnectivity-agent
    spec:
      priorityClassName: system-cluster-critical
      tolerations:
        - key: "CriticalAddonsOnly"
          operator: "Exists"
      containers:
        - image: us.gcr.io/k8s-artifacts-prod/kas-network-proxy/proxy-agent:v0.0.37
          name: konnectivity-agent
          command: ["/proxy-agent"]
          args: [
                  "--logtostderr=true",
                  "--ca-cert=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt",
                  # 由于 konnectivity 服务器以 hostNetwork=true 运行,
                  # 所以这是控制面节点的 IP 地址。
                  "--proxy-server-host=35.225.206.7",
                  "--proxy-server-port=8132",
                  "--admin-server-port=8133",
                  "--health-server-port=8134",
                  "--service-account-token-path=/var/run/secrets/tokens/konnectivity-agent-token"
                  ]
          volumeMounts:
            - mountPath: /var/run/secrets/tokens
              name: konnectivity-agent-token
          livenessProbe:
            httpGet:
              port: 8134
              path: /healthz
            initialDelaySeconds: 15
            timeoutSeconds: 15
      serviceAccountName: konnectivity-agent
      volumes:
        - name: konnectivity-agent-token
          projected:
            sources:
              - serviceAccountToken:
                  path: konnectivity-agent-token
                  audience: system:konnectivity-server

最后,如果你的集群启用了 RBAC,请创建相关的 RBAC 规则:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: system:konnectivity-server
  labels:
    kubernetes.io/cluster-service: "true"
    addonmanager.kubernetes.io/mode: Reconcile
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:auth-delegator
subjects:
  - apiGroup: rbac.authorization.k8s.io
    kind: User
    name: system:konnectivity-server
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: konnectivity-agent
  namespace: kube-system
  labels:
    kubernetes.io/cluster-service: "true"
    addonmanager.kubernetes.io/mode: Reconcile

4.12 - TLS

了解如何使用传输层安全性( TLS )保护集群中的流量。

4.12.1 - 管理集群中的 TLS 认证

Kubernetes 提供 certificates.k8s.io API,可让你配置由你控制的证书颁发机构(CA) 签名的 TLS 证书。 你的工作负载可以使用这些 CA 和证书来建立信任。

certificates.k8s.io API使用的协议类似于 ACME 草案

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你需要 cfssl 工具。 你可以从 https://github.com/cloudflare/cfssl/releases 下载 cfssl

本文中某些步骤使用 jq 工具。如果你没有 jq,你可以通过操作系统的软件源安装, 或者从 https://jqlang.github.io/jq/ 获取。

集群中的 TLS 信任

信任 Pod 中运行的应用程序所提供的自定义 CA 通常需要一些额外的应用程序配置。 你需要将 CA 证书包添加到 TLS 客户端或服务器信任的 CA 证书列表中。 例如,你可以使用 Golang TLS 配置通过解析证书链并将解析的证书添加到 tls.Config 结构中的 RootCAs 字段中。

请求证书

以下部分演示如何为通过 DNS 访问的 Kubernetes 服务创建 TLS 证书。

创建证书签名请求

通过运行以下命令生成私钥和证书签名请求(或 CSR):

cat <<EOF | cfssl genkey - | cfssljson -bare server
{
  "hosts": [
    "my-svc.my-namespace.svc.cluster.local",
    "my-pod.my-namespace.pod.cluster.local",
    "192.0.2.24",
    "10.0.34.2"
  ],
  "CN": "my-pod.my-namespace.pod.cluster.local",
  "key": {
    "algo": "ecdsa",
    "size": 256
  }
}
EOF

其中 192.0.2.24 是服务的集群 IP,my-svc.my-namespace.svc.cluster.local 是服务的 DNS 名称,10.0.34.2 是 Pod 的 IP,而 my-pod.my-namespace.pod.cluster.local 是 Pod 的 DNS 名称。 你能看到的输出类似于:

2022/02/01 11:45:32 [INFO] generate received request
2022/02/01 11:45:32 [INFO] received CSR
2022/02/01 11:45:32 [INFO] generating key: ecdsa-256
2022/02/01 11:45:32 [INFO] encoded CSR

此命令生成两个文件;它生成包含 PEM 编码 PKCS#10 证书请求的 server.csr, 以及 PEM 编码密钥的 server-key.pem,用于待生成的证书。

创建证书签名请求(CSR)对象发送到 Kubernetes API

你可以使用以下命令创建 CSR 清单(YAML 格式),并发送到 API 服务器:

cat <<EOF | kubectl apply -f -
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
  name: my-svc.my-namespace
spec:
  request: $(cat server.csr | base64 | tr -d '\n')
  signerName: example.com/serving
  usages:
  - digital signature
  - key encipherment
  - server auth
EOF

请注意,在步骤 1 中创建的 server.csr 文件是 base64 编码并存储在 .spec.request 字段中的。你还要求提供 “digital signature(数字签名)”, “密钥加密(key encipherment)” 和 “服务器身份验证(server auth)” 密钥用途, 由 example.com/serving 示例签名程序签名的证书。 你也可以要求使用特定的 signerName。更多信息可参阅 支持的签署者名称

在 API server 中可以看到这些 CSR 处于 Pending 状态。执行下面的命令你将可以看到:

kubectl describe csr my-svc.my-namespace
Name:                   my-svc.my-namespace
Labels:                 <none>
Annotations:            <none>
CreationTimestamp:      Tue, 01 Feb 2022 11:49:15 -0500
Requesting User:        yourname@example.com
Signer:                 example.com/serving
Status:                 Pending
Subject:
        Common Name:    my-pod.my-namespace.pod.cluster.local
        Serial Number:
Subject Alternative Names:
        DNS Names:      my-pod.my-namespace.pod.cluster.local
                        my-svc.my-namespace.svc.cluster.local
        IP Addresses:   192.0.2.24
                        10.0.34.2
Events: <none>

批准证书签名请求(CSR)

证书签名请求 的批准或者是通过自动批准过程完成的,或由集群管理员一次性完成。 如果你被授权批准证书请求,你可以使用 kubectl 来手动完成此操作;例如:

kubectl certificate approve my-svc.my-namespace
certificatesigningrequest.certificates.k8s.io/my-svc.my-namespace approved

你现在应该能看到如下输出:

kubectl get csr
NAME                  AGE   SIGNERNAME            REQUESTOR              REQUESTEDDURATION   CONDITION
my-svc.my-namespace   10m   example.com/serving   yourname@example.com   <none>              Approved

这意味着证书请求已被批准,并正在等待请求的签名者对其签名。

签名证书签名请求(CSR)

接下来,你将扮演证书签署者的角色,颁发证书并将其上传到 API 服务器。

签名者通常会使用其 signerName 查看对象的 CertificateSigningRequest API, 检查它们是否已被批准,为这些请求签署证书,并使用已颁发的证书更新 API 对象状态。

创建证书颁发机构

你需要授权在新证书上提供数字签名。

首先,通过运行以下命令创建签名证书:

cat <<EOF | cfssl gencert -initca - | cfssljson -bare ca
{
  "CN": "My Example Signer",
  "key": {
    "algo": "rsa",
    "size": 2048
  }
}
EOF

你应该看到类似于以下的输出:

2022/02/01 11:50:39 [INFO] generating a new CA key and certificate from CSR
2022/02/01 11:50:39 [INFO] generate received request
2022/02/01 11:50:39 [INFO] received CSR
2022/02/01 11:50:39 [INFO] generating key: rsa-2048
2022/02/01 11:50:39 [INFO] encoded CSR
2022/02/01 11:50:39 [INFO] signed certificate with serial number 263983151013686720899716354349605500797834580472

这会产生一个证书颁发机构密钥文件(ca-key.pem)和证书(ca.pem)。

颁发证书

{
    "signing": {
        "default": {
            "usages": [
                "digital signature",
                "key encipherment",
                "server auth"
            ],
            "expiry": "876000h",
            "ca_constraint": {
                "is_ca": false
            }
        }
    }
}

使用 server-signing-config.json 签名配置、证书颁发机构密钥文件和证书来签署证书请求:

kubectl get csr my-svc.my-namespace -o jsonpath='{.spec.request}' | \
  base64 --decode | \
  cfssl sign -ca ca.pem -ca-key ca-key.pem -config server-signing-config.json - | \
  cfssljson -bare ca-signed-server

你应该看到类似于以下的输出:

2022/02/01 11:52:26 [INFO] signed certificate with serial number 576048928624926584381415936700914530534472870337

这会生成一个签名的服务证书文件,ca-signed-server.pem

上传签名证书

最后,在 API 对象的状态中填充签名证书:

kubectl get csr my-svc.my-namespace -o json | \
  jq '.status.certificate = "'$(base64 ca-signed-server.pem | tr -d '\n')'"' | \
  kubectl replace --raw /apis/certificates.k8s.io/v1/certificatesigningrequests/my-svc.my-namespace/status -f -

批准 CSR 并上传签名证书后,运行:

kubectl get csr

输入类似于:

NAME                  AGE   SIGNERNAME            REQUESTOR              REQUESTEDDURATION   CONDITION
my-svc.my-namespace   20m   example.com/serving   yourname@example.com   <none>              Approved,Issued

下载证书并使用它

现在,作为请求用户,你可以通过运行以下命令下载颁发的证书并将其保存到 server.crt 文件中:

CSR 被签署并获得批准后,你应该看到以下内容:

kubectl get csr my-svc.my-namespace -o jsonpath='{.status.certificate}' \
    | base64 --decode > server.crt

现在你可以将 server.crtserver-key.pem 填充到 Secret 中, 稍后你可以将其挂载到 Pod 中(例如,用于提供 HTTPS 的网络服务器)。

kubectl create secret tls server --cert server.crt --key server-key.pem
secret/server created

最后,你可以将 ca.pem 填充到 ConfigMap 并将其用作信任根来验证服务证书:

kubectl create configmap example-serving-ca --from-file ca.crt=ca.pem
configmap/example-serving-ca created

批准证书签名请求(CSR)

Kubernetes 管理员(具有适当权限)可以使用 kubectl certificate approvekubectl certificate deny 命令手动批准(或拒绝)证书签名请求(CSR)。 但是,如果你打算大量使用此 API,则可以考虑编写自动化的证书控制器。

无论上述机器或人使用 kubectl,“批准者”的作用是验证 CSR 满足如下两个要求:

  1. CSR 的 subject 控制用于签署 CSR 的私钥。这解决了伪装成授权主体的第三方的威胁。 在上述示例中,此步骤将验证该 Pod 控制了用于生成 CSR 的私钥。
  2. CSR 的 subject 被授权在请求的上下文中执行。 这点用于处理不期望的主体被加入集群的威胁。 在上述示例中,此步骤将是验证该 Pod 是否被允许加入到所请求的服务中。

当且仅当满足这两个要求时,审批者应该批准 CSR,否则拒绝 CSR。

有关证书批准和访问控制的更多信息, 请阅读证书签名请求参考页。

给集群管理员的一个建议

本页面假设已经为 certificates API 配置了签名者。 Kubernetes 控制器管理器提供了一个签名者的默认实现。要启用它,请为控制器管理器设置 --cluster-signing-cert-file--cluster-signing-key-file 参数, 使之取值为你的证书机构的密钥对的路径。

4.12.2 - 手动轮换 CA 证书

本页展示如何手动轮换证书机构(CA)证书。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

  • 要了解 Kubernetes 中用户认证的更多信息,参阅 认证
  • 要了解与 CA 证书最佳实践有关的更多信息, 参阅单根 CA

手动轮换 CA 证书

  1. 将新的 CA 证书和私钥(例如:ca.crtca.keyfront-proxy-ca.crtfront-proxy-client.key)分发到所有控制面节点,放在其 Kubernetes 证书目录下。
  1. 更新 kube-controller-manager--root-ca-file 标志,使之同时包含老的和新的 CA,之后重启 kube-controller-manager。

    自此刻起,所创建的所有ServiceAccount 都会获得同时包含老的 CA 和新的 CA 的 Secret。

  1. 等待该控制器管理器更新服务账号 Secret 中的 ca.crt,使之同时包含老的和新的 CA 证书。

    如果在 API 服务器使用新的 CA 之前启动了新的 Pod,这些新的 Pod 也会获得此更新并且同时信任老的和新的 CA 证书。

  1. 重启所有使用集群内配置的 Pod(例如:kube-proxy、CoreDNS 等),以便这些 Pod 能够使用与 ServiceAccount 相关联的 Secret 中的、已更新的证书机构数据。

    • 确保 CoreDNS、kube-proxy 和其他使用集群内配置的 Pod 都正按预期方式工作。
  2. 将老的和新的 CA 都追加到 kube-apiserver 配置的 --client-ca-file--kubelet-certificate-authority 标志所指的文件。

  3. 将老的和新的 CA 都追加到 kube-scheduler 配置的 --client-ca-file 标志所指的文件。

  1. 通过替换 client-certificate-dataclient-key-data 中的内容,更新用户账号的证书。

    有关为独立用户账号创建证书的更多信息,可参阅 为用户帐号配置证书

    另外,还要更新 kubeconfig 文件中的 certificate-authority-data 节, 使之包含 Base64 编码的老的和新的证书机构数据。

  1. 更新 云控制器管理器(Cloud Controller Manager)--root-ca-file 标志值,使之同时包含老的和新的 CA,之后重新启动 cloud-controller-manager。

  1. 遵循下列步骤执行滚动更新

    1. 重新启动所有其他被聚合的 API 服务器 或者 Webhook 处理程序,使之信任新的 CA 证书。

    2. 在所有节点上更新 kubelet 配置中的 clientCAFile 所指文件以及 kubelet.conf 中的 certificate-authority-data 并重启 kubelet 以同时使用老的和新的 CA 证书。

      如果你的 kubelet 并未使用客户端证书轮换,则在所有节点上更新 kubelet.confclient-certificate-dataclient-key-data 以及 kubelet 客户端证书文件(通常位于 /var/lib/kubelet/pki 目录下)

    1. 使用用新的 CA 签名的证书 (apiserver.crtapiserver-kubelet-client.crtfront-proxy-client.crt) 来重启 API 服务器。 你可以使用现有的私钥,也可以使用新的私钥。 如果你改变了私钥,则要将更新的私钥也放到 Kubernetes 证书目录下。
      由于集群中的 Pod 既信任老的 CA 也信任新的 CA,Pod 中的客户端会经历短暂的连接断开状态,
    

    之后再使用新的 CA 所签名的证书连接到新的 API 服务器。

      * 重启 kube-scheduler 以使用并信任新的
     CA 证书。
    
    • 确保控制面组件的日志中没有 TLS 相关的错误信息。
    1. 为 Daemonset 和 Deployment 添加注解,从而触发较安全的滚动更新,替换 Pod。
    for namespace in $(kubectl get namespace -o jsonpath='{.items[*].metadata.name}'); do
        for name in $(kubectl get deployments -n $namespace -o jsonpath='{.items[*].metadata.name}'); do
            kubectl patch deployment -n ${namespace} ${name} -p '{"spec":{"template":{"metadata":{"annotations":{"ca-rotation": "1"}}}}}';
        done
        for name in $(kubectl get daemonset -n $namespace -o jsonpath='{.items[*].metadata.name}'); do
            kubectl patch daemonset -n ${namespace} ${name} -p '{"spec":{"template":{"metadata":{"annotations":{"ca-rotation": "1"}}}}}';
        done
    done
    
      取决于你在如何使用 StatefulSet,你可能需要对其执行类似的滚动替换操作。
    
  1. 如果你的集群使用启动引导令牌来添加节点,则需要更新 kube-public 名字空间下的 ConfigMap cluster-info,使之包含新的 CA 证书。

    base64_encoded_ca="$(base64 -w0 /etc/kubernetes/pki/ca.crt)"
    
    kubectl get cm/cluster-info --namespace kube-public -o yaml | \
       /bin/sed "s/\(certificate-authority-data:\).*/\1 ${base64_encoded_ca}/" | \
       kubectl apply -f -
    
  1. 验证集群的功能正常。

    1. 检查控制面组件以及 kubeletkube-proxy 的日志,确保其中没有抛出 TLS 错误, 参阅查看日志

    2. 验证被聚合的 API 服务器的日志,以及所有使用集群内配置的 Pod 的日志。

  1. 完成集群功能的检查之后:

    1. 更新所有的服务账号令牌,使之仅包含新的 CA 证书。

      • 使用集群内 kubeconfig 的 Pod 最终也需要被重启,以获得新的服务账号 Secret 数据,这样就不会有 Pod 再依赖老的集群 CA。
    2. 从 kubeconfig 文件和 --client-ca-file 以及 --root-ca-file 标志所指向的文件 中去除老的 CA 数据,之后重启控制面组件。

    3. 在每个节点上,移除 clientCAFile 标志所指向的文件,以删除老的 CA 数据,并从 kubelet kubeconfig 文件中去掉老的 CA,重启 kubelet。 你应该用滚动更新的方式来执行这一步骤的操作。

      如果你的集群允许你执行这一变更,你也可以通过替换节点而不是重新配置节点的方式来将其上线。

4.12.3 - 为 kubelet 配置证书轮换

本文展示如何在 kubelet 中启用并配置证书轮换。

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

准备开始

  • 要求 Kubernetes 1.8.0 或更高的版本

概述

Kubelet 使用证书进行 Kubernetes API 的认证。 默认情况下,这些证书的签发期限为一年,所以不需要太频繁地进行更新。

Kubernetes 包含特性 kubelet 证书轮换, 在当前证书即将过期时, 将自动生成新的秘钥,并从 Kubernetes API 申请新的证书。 一旦新的证书可用,它将被用于与 Kubernetes API 间的连接认证。

启用客户端证书轮换

kubelet 进程接收 --rotate-certificates 参数,该参数决定 kubelet 在当前使用的 证书即将到期时,是否会自动申请新的证书。

kube-controller-manager 进程接收 --cluster-signing-duration 参数 (在 1.19 版本之前为 --experimental-cluster-signing-duration),用来 控制签发证书的有效期限。

理解证书轮换配置

当 kubelet 启动时,如被配置为自举(使用--bootstrap-kubeconfig 参数),kubelet 会使用其初始证书连接到 Kubernetes API ,并发送证书签名的请求。 可以通过以下方式查看证书签名请求的状态:

kubectl get csr

最初,来自节点上 kubelet 的证书签名请求处于 Pending 状态。 如果证书签名请求满足特定条件, 控制器管理器会自动批准,此时请求会处于 Approved 状态。 接下来,控制器管理器会签署证书, 证书的有效期限由 --cluster-signing-duration 参数指定,签署的证书会被附加到证书签名请求中。

Kubelet 会从 Kubernetes API 取回签署的证书,并将其写入磁盘,存储位置通过 --cert-dir 参数指定。 然后 kubelet 会使用新的证书连接到 Kubernetes API。

当签署的证书即将到期时,kubelet 会使用 Kubernetes API,自动发起新的证书签名请求。 该请求会发生在证书的有效时间剩下 30% 到 10% 之间的任意时间点。 同样地,控制器管理器会自动批准证书请求,并将签署的证书附加到证书签名请求中。 Kubelet 会从 Kubernetes API 取回签署的证书,并将其写入磁盘。 然后它会更新与 Kubernetes API 的连接,使用新的证书重新连接到 Kubernetes API。

4.13 - 管理集群守护进程

执行 DaemonSet 管理的常见任务,例如执行滚动更新。

4.13.1 - 对 DaemonSet 执行滚动更新

本文介绍了如何对 DaemonSet 执行滚动更新。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

DaemonSet 更新策略

DaemonSet 有两种更新策略:

  • OnDelete:使用 OnDelete 更新策略时,在更新 DaemonSet 模板后,只有当你手动删除老的 DaemonSet Pod 之后,新的 DaemonSet Pod 才会被自动创建。跟 Kubernetes 1.6 以前的版本类似。
  • RollingUpdate:这是默认的更新策略。使用 RollingUpdate 更新策略时,在更新 DaemonSet 模板后, 老的 DaemonSet Pod 将被终止,并且将以受控方式自动创建新的 DaemonSet Pod。 更新期间,最多只能有 DaemonSet 的一个 Pod 运行于每个节点上。

执行滚动更新

要启用 DaemonSet 的滚动更新功能,必须设置 .spec.updateStrategy.typeRollingUpdate

你可能想设置 .spec.updateStrategy.rollingUpdate.maxUnavailable(默认为 1)、 .spec.minReadySeconds(默认为 0)和 .spec.updateStrategy.rollingUpdate.maxSurge (默认为 0)。

创建带有 RollingUpdate 更新策略的 DaemonSet

下面的 YAML 包含一个 DaemonSet,其更新策略为 'RollingUpdate':

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd-elasticsearch
  namespace: kube-system
  labels:
    k8s-app: fluentd-logging
spec:
  selector:
    matchLabels:
      name: fluentd-elasticsearch
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
  template:
    metadata:
      labels:
        name: fluentd-elasticsearch
    spec:
      tolerations:
      # 这些容忍度设置是为了让该守护进程集在控制平面节点上运行
      # 如果你不希望自己的控制平面节点运行 Pod,可以删除它们
      - key: node-role.kubernetes.io/master
        effect: NoSchedule
      containers:
      - name: fluentd-elasticsearch
        image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
      terminationGracePeriodSeconds: 30
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers

检查了 DaemonSet 清单中更新策略的设置之后,创建 DaemonSet:

kubectl create -f https://k8s.io/examples/controllers/fluentd-daemonset.yaml

另一种方式是如果你希望使用 kubectl apply 来更新 DaemonSet 的话, 也可以使用 kubectl apply 来创建 DaemonSet:

kubectl apply -f https://k8s.io/examples/controllers/fluentd-daemonset.yaml

检查 DaemonSet 的滚动更新策略

首先,检查 DaemonSet 的更新策略,确保已经将其设置为 RollingUpdate

kubectl get ds/fluentd-elasticsearch -o go-template='{{.spec.updateStrategy.type}}{{"\n"}}' -n kube-system

如果还没在系统中创建 DaemonSet,请使用以下命令检查 DaemonSet 的清单:

kubectl apply -f https://k8s.io/examples/controllers/fluentd-daemonset.yaml --dry-run=client -o go-template='{{.spec.updateStrategy.type}}{{"\n"}}'

两个命令的输出都应该为:

RollingUpdate

如果输出不是 RollingUpdate,请返回并相应地修改 DaemonSet 对象或者清单。

更新 DaemonSet 模板

RollingUpdate DaemonSet 的 .spec.template 的任何更新都将触发滚动更新。 这可以通过几个不同的 kubectl 命令来完成。

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd-elasticsearch
  namespace: kube-system
  labels:
    k8s-app: fluentd-logging
spec:
  selector:
    matchLabels:
      name: fluentd-elasticsearch
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
  template:
    metadata:
      labels:
        name: fluentd-elasticsearch
    spec:
      tolerations:
      # 这些容忍度设置是为了让该守护进程集在控制平面节点上运行
      # 如果你不希望自己的控制平面节点运行 Pod,可以删除它们
      - key: node-role.kubernetes.io/control-plane
        operator: Exists
        effect: NoSchedule
      - key: node-role.kubernetes.io/master
        operator: Exists
        effect: NoSchedule
      containers:
      - name: fluentd-elasticsearch
        image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2
        resources:
          limits:
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 200Mi
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
      terminationGracePeriodSeconds: 30
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers

声明式命令

如果你使用配置文件来更新 DaemonSet,请使用 kubectl apply

kubectl apply -f https://k8s.io/examples/controllers/fluentd-daemonset-update.yaml

指令式命令

如果你使用指令式命令来更新 DaemonSets,请使用 kubectl edit

kubectl edit ds/fluentd-elasticsearch -n kube-system
只更新容器镜像

如果你只需要更新 DaemonSet 模板里的容器镜像,比如 .spec.template.spec.containers[*].image, 请使用 kubectl set image

kubectl set image ds/fluentd-elasticsearch fluentd-elasticsearch=quay.io/fluentd_elasticsearch/fluentd:v2.6.0 -n kube-system

监视滚动更新状态

最后,观察 DaemonSet 最新滚动更新的进度:

kubectl rollout status ds/fluentd-elasticsearch -n kube-system

当滚动更新完成时,输出结果如下:

daemonset "fluentd-elasticsearch" successfully rolled out

故障排查

DaemonSet 滚动更新卡住

有时,DaemonSet 滚动更新可能卡住,以下是一些可能的原因:

一些节点可用资源耗尽

DaemonSet 滚动更新可能会卡住,其 Pod 至少在某个节点上无法调度运行。 当节点上可用资源耗尽时, 这是可能的。

发生这种情况时,通过对 kubectl get nodes 和下面命令行的输出作比较, 找出没有调度 DaemonSet Pod 的节点:

kubectl get pods -l name=fluentd-elasticsearch -o wide -n kube-system

一旦找到这些节点,从节点上删除一些非 DaemonSet Pod,为新的 DaemonSet Pod 腾出空间。

不完整的滚动更新

如果最近的 DaemonSet 模板更新被破坏了,比如,容器处于崩溃循环状态或者容器镜像不存在 (通常由于拼写错误),就会发生 DaemonSet 滚动更新中断。

要解决此问题,需再次更新 DaemonSet 模板。新的滚动更新不会被以前的不健康的滚动更新阻止。

时钟偏差

如果在 DaemonSet 中指定了 .spec.minReadySeconds,主控节点和工作节点之间的时钟偏差会使 DaemonSet 无法检测到正确的滚动更新进度。

清理

从名字空间中删除 DaemonSet:

kubectl delete ds fluentd-elasticsearch -n kube-system

接下来

4.13.2 - 对 DaemonSet 执行回滚

本文展示了如何对 DaemonSet 执行回滚。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你的 Kubernetes 服务器版本必须不低于版本 1.7. 要获知版本信息,请输入 kubectl version.

你应该已经了解如何为 DaemonSet 执行滚动更新

对 DaemonSet 执行回滚

步骤 1:找到想要 DaemonSet 回滚到的历史修订版本(revision)

如果只想回滚到最后一个版本,可以跳过这一步。

列出 DaemonSet 的所有版本:

kubectl rollout history daemonset <daemonset-name>

此命令返回 DaemonSet 版本列表:

daemonsets "<daemonset-name>"
REVISION        CHANGE-CAUSE
1               ...
2               ...
...
  • 在创建时,DaemonSet 的变化原因从 kubernetes.io/change-cause 注解(annotation) 复制到其修订版本中。用户可以在 kubectl 命令中设置 --record=true, 将执行的命令记录在变化原因注解中。

执行以下命令,来查看指定版本的详细信息:

kubectl rollout history daemonset <daemonset-name> --revision=1

该命令返回相应修订版本的详细信息:

daemonsets "<daemonset-name>" with revision #1
Pod Template:
Labels:       foo=bar
Containers:
app:
 Image:        ...
 Port:         ...
 Environment:  ...
 Mounts:       ...
Volumes:      ...

步骤 2:回滚到指定版本

# 在 --to-revision 中指定你从步骤 1 中获取的修订版本
kubectl rollout undo daemonset <daemonset-name> --to-revision=<revision>

如果成功,命令会返回:

daemonset "<daemonset-name>" rolled back

步骤 3:监视 DaemonSet 回滚进度

kubectl rollout undo daemonset 向服务器表明启动 DaemonSet 回滚。 真正的回滚是在集群的 控制面 异步完成的。

执行以下命令,来监视 DaemonSet 回滚进度:

kubectl rollout status ds/<daemonset-name>

回滚完成时,输出形如:

daemonset "<daemonset-name>" successfully rolled out

理解 DaemonSet 修订版本

在前面的 kubectl rollout history 步骤中,你获得了一个修订版本列表,每个修订版本都存储在名为 ControllerRevision 的资源中。

要查看每个修订版本中保存的内容,可以找到 DaemonSet 修订版本的原生资源:

kubectl get controllerrevision -l <daemonset-selector-key>=<daemonset-selector-value>

该命令返回 ControllerRevisions 列表:

NAME                               CONTROLLER                     REVISION   AGE
<daemonset-name>-<revision-hash>   DaemonSet/<daemonset-name>     1          1h
<daemonset-name>-<revision-hash>   DaemonSet/<daemonset-name>     2          1h

每个 ControllerRevision 中存储了相应 DaemonSet 版本的注解和模板。

kubectl rollout undo 选择特定的 ControllerRevision,并用 ControllerRevision 中存储的模板代替 DaemonSet 的模板。 kubectl rollout undo 相当于通过其他命令(如 kubectl editkubectl apply) 将 DaemonSet 模板更新至先前的版本。

故障排查

4.13.3 - 仅在某些节点上运行 Pod

本页演示了你如何能够仅在某些节点上作为 DaemonSet 的一部分运行Pod

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

仅在某些节点上运行 Pod

设想一下你想要运行 DaemonSet, 但你只需要在配备了本地固态 (SSD) 存储的节点上运行这些守护进程 Pod。 例如,Pod 可以向节点提供缓存服务,而缓存仅在低延迟本地存储可用时才有用。

第 1 步:为节点打标签

在配有 SSD 的节点上打标签 ssd=true

kubectl label nodes example-node-1 example-node-2 ssd=true

第 2 步:创建清单

让我们创建一个 DaemonSet, 它将仅在打了 SSD 标签的节点上制备守护进程 Pod。

接下来,使用 nodeSelector 确保 DaemonSet 仅在 ssd 标签设为 "true" 的节点上运行 Pod。

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: ssd-driver
  labels:
    app: nginx
spec:
  selector:
    matchLabels:
      app: ssd-driver-pod
  template:
    metadata:
      labels:
        app: ssd-driver-pod
    spec:
      nodeSelector:
        ssd: "true"
      containers:
        - name: example-container
          image: example-image

第 3 步:创建 DaemonSet

使用 kubectl createkubectl apply 从清单创建 DaemonSet。

让我们为另一个节点打上标签 ssd=true

kubectl label nodes example-node-3 ssd=true

节点打上标签后将自动触发控制平面(具体而言是 DaemonSet 控制器)在该节点上运行新的守护进程 Pod。

kubectl get pods -o wide

输出类似于:

NAME                              READY     STATUS    RESTARTS   AGE    IP      NODE
<daemonset-name><some-hash-01>    1/1       Running   0          13s    .....   example-node-1
<daemonset-name><some-hash-02>    1/1       Running   0          13s    .....   example-node-2
<daemonset-name><some-hash-03>    1/1       Running   0          5s     .....   example-node-3

4.14 - 网络

了解如何为你的集群配置网络。

4.14.1 - 使用 HostAliases 向 Pod /etc/hosts 文件添加条目

当 DNS 配置以及其它选项不合理的时候,通过向 Pod 的 /etc/hosts 文件中添加条目, 可以在 Pod 级别覆盖对主机名的解析。你可以通过 PodSpec 的 HostAliases 字段来添加这些自定义条目。

建议通过使用 HostAliases 来进行修改,因为该文件由 Kubelet 管理,并且 可以在 Pod 创建/重启过程中被重写。

默认 hosts 文件内容

让我们从一个 Nginx Pod 开始,该 Pod 被分配一个 IP:

kubectl run nginx --image nginx
pod/nginx created

检查 Pod IP:

kubectl get pods --output=wide
NAME     READY     STATUS    RESTARTS   AGE    IP           NODE
nginx    1/1       Running   0          13s    10.200.0.4   worker0

主机文件的内容如下所示:

kubectl exec nginx -- cat /etc/hosts
# Kubernetes-managed hosts file.
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
fe00::0	ip6-mcastprefix
fe00::1	ip6-allnodes
fe00::2	ip6-allrouters
10.200.0.4	nginx

默认情况下,hosts 文件只包含 IPv4 和 IPv6 的样板内容,像 localhost 和主机名称。

通过 HostAliases 增加额外条目

除了默认的样板内容,你可以向 hosts 文件添加额外的条目。 例如,要将 foo.localbar.local 解析为 127.0.0.1, 将 foo.remotebar.remote 解析为 10.1.2.3,你可以在 .spec.hostAliases 下为 Pod 配置 HostAliases。

apiVersion: v1
kind: Pod
metadata:
  name: hostaliases-pod
spec:
  restartPolicy: Never
  hostAliases:
  - ip: "127.0.0.1"
    hostnames:
    - "foo.local"
    - "bar.local"
  - ip: "10.1.2.3"
    hostnames:
    - "foo.remote"
    - "bar.remote"
  containers:
  - name: cat-hosts
    image: busybox:1.28
    command:
    - cat
    args:
    - "/etc/hosts"

你可以使用以下命令用此配置启动 Pod:

kubectl apply -f https://k8s.io/examples/service/networking/hostaliases-pod.yaml
pod/hostaliases-pod created

检查 Pod 详情,查看其 IPv4 地址和状态:

kubectl get pod --output=wide
NAME                READY     STATUS      RESTARTS   AGE       IP              NODE
hostaliases-pod     0/1       Completed   0          6s        10.200.0.5      worker0

hosts 文件的内容看起来类似如下所示:

kubectl logs hostaliases-pod
# Kubernetes-managed hosts file.
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
fe00::0	ip6-mcastprefix
fe00::1	ip6-allnodes
fe00::2	ip6-allrouters
10.200.0.5	hostaliases-pod

# Entries added by HostAliases.
127.0.0.1	foo.local	bar.local
10.1.2.3	foo.remote	bar.remote

在最下面额外添加了一些条目。

为什么 kubelet 管理 hosts 文件?

kubelet 管理每个Pod 容器的 hosts 文件,以防止容器运行时在容器已经启动后修改文件。 由于历史原因,Kubernetes 总是使用 Docker Engine 作为其容器运行时,而 Docker Engine 将在容器启动后修改 /etc/hosts 文件。

当前的 Kubernetes 可以使用多种容器运行时;即便如此,kubelet 管理在每个容器中创建 hosts文件, 以便你使用任何容器运行时运行容器时,结果都符合预期。

4.14.2 - 扩展 Service IP 范围

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

本文将介绍如何扩展分配给集群的现有 Service IP 范围。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你的 Kubernetes 服务器版本必须不低于版本 v1.29. 要获知版本信息,请输入 kubectl version.

API

如果 Kubernetes 集群的 kube-apiserver 启用了 MultiCIDRServiceAllocator 特性门控networking.k8s.io/v1alpha1 API,集群将创建一个新的 ServiceCIDR 对象, 该对象采用 kubernetes 这个众所周知的名称并基于 kube-apiserver 的 --service-cluster-ip-range 命令行参数的值来使用 IP 地址范围。

kubectl get servicecidr
NAME         CIDRS          AGE
kubernetes   10.96.0.0/28   17d

公认的 kubernetes Service 将 kube-apiserver 的端点暴露给 Pod, 计算出默认 ServiceCIDR 范围中的第一个 IP 地址,并将该 IP 地址用作其集群 IP 地址。

kubectl get service kubernetes
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   17d

在本例中,默认 Service 使用具有对应 IPAddress 对象的 ClusterIP 10.96.0.1。

kubectl get ipaddress 10.96.0.1
NAME        PARENTREF
10.96.0.1   services/default/kubernetes

ServiceCIDR 受到 终结器 的保护, 以避免留下孤立的 Service ClusterIP;只有在存在包含现有 IPAddress 的另一个子网或者没有属于此子网的 IPAddress 时,才会移除终结器。

扩展 Service 可用的 IP 数量

有时候用户需要增加可供 Service 使用的 IP 地址数量。 以前,增加 Service 范围是一个可能导致数据丢失的破坏性操作。 有了这个新的特性后,用户只需添加一个新的 ServiceCIDR 对象,便能增加可用地址的数量。

添加新的 ServiceCIDR

对于 Service 范围为 10.96.0.0/28 的集群,只有 2^(32-28) - 2 = 14 个可用的 IP 地址。 kubernetes.default Service 始终会被创建;在这个例子中,你只剩下了 13 个可能的 Service。

for i in $(seq 1 13); do kubectl create service clusterip "test-$i" --tcp 80 -o json | jq -r .spec.clusterIP; done
10.96.0.11
10.96.0.5
10.96.0.12
10.96.0.13
10.96.0.14
10.96.0.2
10.96.0.3
10.96.0.4
10.96.0.6
10.96.0.7
10.96.0.8
10.96.0.9
error: failed to create ClusterIP service: Internal error occurred: failed to allocate a serviceIP: range is full

通过创建一个扩展或新增 IP 地址范围的新 ServiceCIDR,你可以提高 Service 可用的 IP 地址数量。

cat <EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1alpha1
kind: ServiceCIDR
metadata:
  name: newcidr1
spec:
  cidrs:
  - 10.96.0.0/24
EOF
servicecidr.networking.k8s.io/newcidr1 created

这将允许你创建新的 Service,其 ClusterIP 将从这个新的范围中选取。

for i in $(seq 13 16); do kubectl create service clusterip "test-$i" --tcp 80 -o json | jq -r .spec.clusterIP; done
10.96.0.48
10.96.0.200
10.96.0.121
10.96.0.144

删除 ServiceCIDR

如果存在依赖于 ServiceCIDR 的 IPAddress,你将无法删除 ServiceCIDR。

kubectl delete servicecidr newcidr1
servicecidr.networking.k8s.io "newcidr1" deleted

Kubernetes 在 ServiceCIDR 上使用一个终结器来跟踪这种依赖关系。

kubectl get servicecidr newcidr1 -o yaml
apiVersion: networking.k8s.io/v1alpha1
kind: ServiceCIDR
metadata:
  creationTimestamp: "2023-10-12T15:11:07Z"
  deletionGracePeriodSeconds: 0
  deletionTimestamp: "2023-10-12T15:12:45Z"
  finalizers:
    - networking.k8s.io/service-cidr-finalizer
  name: newcidr1
  resourceVersion: "1133"
  uid: 5ffd8afe-c78f-4e60-ae76-cec448a8af40
spec:
  cidrs:
    - 10.96.0.0/24
status:
  conditions:
    - lastTransitionTime: "2023-10-12T15:12:45Z"
      message:
        There are still IPAddresses referencing the ServiceCIDR, please remove
        them or create a new ServiceCIDR
      reason: OrphanIPAddress
      status: "False"
      type: Ready

移除一些 Service,这些 Service 包含阻止删除 ServiceCIDR 的 IP 地址:

for i in $(seq 13 16); do kubectl delete service "test-$i" ; done
service "test-13" deleted
service "test-14" deleted
service "test-15" deleted
service "test-16" deleted

控制平面会注意到这种移除操作。控制平面随后会移除其终结器,以便真正移除待删除的 ServiceCIDR。

kubectl get servicecidr newcidr1
Error from server (NotFound): servicecidrs.networking.k8s.io "newcidr1" not found

4.14.3 - 验证 IPv4/IPv6 双协议栈

本文分享了如何验证 IPv4/IPv6 双协议栈的 Kubernetes 集群。

准备开始

  • 驱动程序对双协议栈网络的支持 (云驱动或其他方式必须能够为 Kubernetes 节点提供可路由的 IPv4/IPv6 网络接口)
  • 一个能够支持双协议栈网络的 网络插件
  • 启用双协议栈 集群
你的 Kubernetes 服务器版本必须不低于版本 v1.23. 要获知版本信息,请输入 kubectl version.

验证寻址

验证节点寻址

每个双协议栈节点应分配一个 IPv4 块和一个 IPv6 块。 通过运行以下命令来验证是否配置了 IPv4/IPv6 Pod 地址范围。 将示例节点名称替换为集群中的有效双协议栈节点。 在此示例中,节点的名称为 k8s-linuxpool1-34450317-0

kubectl get nodes k8s-linuxpool1-34450317-0 -o go-template --template='{{range .spec.podCIDRs}}{{printf "%s\n" .}}{{end}}'
10.244.1.0/24
2001:db8::/64

应该分配一个 IPv4 块和一个 IPv6 块。

验证节点是否检测到 IPv4 和 IPv6 接口。用集群中的有效节点替换节点名称。 在此示例中,节点名称为 k8s-linuxpool1-34450317-0

kubectl get nodes k8s-linuxpool1-34450317-0 -o go-template --template='{{range .status.addresses}}{{printf "%s: %s\n" .type .address}}{{end}}'
Hostname: k8s-linuxpool1-34450317-0
InternalIP: 10.0.0.5
InternalIP: 2001:db8:10::5

验证 Pod 寻址

验证 Pod 已分配了 IPv4 和 IPv6 地址。用集群中的有效 Pod 替换 Pod 名称。 在此示例中,Pod 名称为 pod01

kubectl get pods pod01 -o go-template --template='{{range .status.podIPs}}{{printf "%s\n" .ip}}{{end}}'
10.244.1.4
2001:db8::4

你也可以通过 status.podIPs 使用 Downward API 验证 Pod IP。 以下代码段演示了如何通过容器内称为 MY_POD_IPS 的环境变量公开 Pod 的 IP 地址。

env:
- name: MY_POD_IPS
  valueFrom:
    fieldRef:
      fieldPath: status.podIPs

使用以下命令打印出容器内部 MY_POD_IPS 环境变量的值。 该值是一个逗号分隔的列表,与 Pod 的 IPv4 和 IPv6 地址相对应。

kubectl exec -it pod01 -- set | grep MY_POD_IPS
MY_POD_IPS=10.244.1.4,2001:db8::4

Pod 的 IP 地址也将被写入容器内的 /etc/hosts 文件中。 在双栈 Pod 上执行 cat /etc/hosts 命令操作。 从输出结果中,你可以验证 Pod 的 IPv4 和 IPv6 地址。

kubectl exec -it pod01 -- cat /etc/hosts
# Kubernetes-managed hosts file.
127.0.0.1    localhost
::1    localhost ip6-localhost ip6-loopback
fe00::0    ip6-localnet
fe00::0    ip6-mcastprefix
fe00::1    ip6-allnodes
fe00::2    ip6-allrouters
10.244.1.4    pod01
2001:db8::4    pod01

验证服务

创建以下未显式定义 .spec.ipFamilyPolicy 的 Service。 Kubernetes 将从首个配置的 service-cluster-ip-range 给 Service 分配集群 IP, 并将 .spec.ipFamilyPolicy 设置为 SingleStack

apiVersion: v1
kind: Service
metadata:
  name: my-service
  labels:
    app.kubernetes.io/name: MyApp
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - protocol: TCP
      port: 80

使用 kubectl 查看 Service 的 YAML 定义。

kubectl get svc my-service -o yaml

该 Service 通过在 kube-controller-manager 的 --service-cluster-ip-range 标志设置的第一个配置范围,将 .spec.ipFamilyPolicy 设置为 SingleStack, 将 .spec.clusterIP 设置为 IPv4 地址。

apiVersion: v1
kind: Service
metadata:
  name: my-service
  namespace: default
spec:
  clusterIP: 10.0.217.164
  clusterIPs:
  - 10.0.217.164
  ipFamilies:
  - IPv4
  ipFamilyPolicy: SingleStack
  ports:
  - port: 80
    protocol: TCP
    targetPort: 9376
  selector:
    app.kubernetes.io/name: MyApp
  sessionAffinity: None
  type: ClusterIP
status:
  loadBalancer: {}

创建以下显式定义 .spec.ipFamilies 数组中的第一个元素为 IPv6 的 Service。 Kubernetes 将 service-cluster-ip-range 配置的 IPv6 地址范围给 Service 分配集群 IP, 并将 .spec.ipFamilyPolicy 设置为 SingleStack

apiVersion: v1
kind: Service
metadata:
  name: my-service
  labels:
    app.kubernetes.io/name: MyApp
spec:
  ipFamilies:
  - IPv6
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - protocol: TCP
      port: 80

使用 kubectl 查看 Service 的 YAML 定义。

kubectl get svc my-service -o yaml

该 Service 通过在 kube-controller-manager 的 --service-cluster-ip-range 标志设置的 IPv6 地址范围,将 .spec.ipFamilyPolicy 设置为 SingleStack, 将 .spec.clusterIP 设置为 IPv6 地址。

apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/name: MyApp
  name: my-service
spec:
  clusterIP: 2001:db8:fd00::5118
  clusterIPs:
  - 2001:db8:fd00::5118
  ipFamilies:
  - IPv6
  ipFamilyPolicy: SingleStack
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app.kubernetes.io/name: MyApp
  sessionAffinity: None
  type: ClusterIP
status:
  loadBalancer: {}

创建以下显式定义 .spec.ipFamilyPolicyPreferDualStack 的 Service。 Kubernetes 将分配 IPv4 和 IPv6 地址(因为该集群启用了双栈), 并根据 .spec.ipFamilies 数组中第一个元素的地址族, 从 .spec.ClusterIPs 列表中选择 .spec.ClusterIP

apiVersion: v1
kind: Service
metadata:
  name: my-service
  labels:
    app.kubernetes.io/name: MyApp
spec:
  ipFamilyPolicy: PreferDualStack
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - protocol: TCP
      port: 80

使用 kubectl describe 验证服务是否从 IPv4 和 IPv6 地址块中获取了集群 IP。 然后你就可以通过 IP 和端口,验证对服务的访问。

kubectl describe svc -l app.kubernetes.io/name=MyApp
Name:              my-service
Namespace:         default
Labels:            app.kubernetes.io/name=MyApp
Annotations:       <none>
Selector:          app.kubernetes.io/name=MyApp
Type:              ClusterIP
IP Family Policy:  PreferDualStack
IP Families:       IPv4,IPv6
IP:                10.0.216.242
IPs:               10.0.216.242,2001:db8:fd00::af55
Port:              <unset>  80/TCP
TargetPort:        9376/TCP
Endpoints:         <none>
Session Affinity:  None
Events:            <none>

创建双协议栈负载均衡服务

如果云提供商支持配置启用 IPv6 的外部负载均衡器,则创建如下 Service 时将 .spec.ipFamilyPolicy 设置为 PreferDualStack, 并将 spec.ipFamilies 字段 的第一个元素设置为 IPv6,将 type 字段设置为 LoadBalancer

apiVersion: v1
kind: Service
metadata:
  name: my-service
  labels:
    app.kubernetes.io/name: MyApp
spec:
  ipFamilyPolicy: PreferDualStack
  ipFamilies:
  - IPv6
  type: LoadBalancer
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - protocol: TCP
      port: 80

检查服务:

kubectl get svc -l app.kubernetes.io/name=MyApp

验证服务是否从 IPv6 地址块中接收到 CLUSTER-IP 地址以及 EXTERNAL-IP。 然后,你可以通过 IP 和端口验证对服务的访问。

NAME         TYPE           CLUSTER-IP            EXTERNAL-IP        PORT(S)        AGE
my-service   LoadBalancer   2001:db8:fd00::7ebc   2603:1030:805::5   80:30790/TCP   35s

4.15 - 调度 GPU

配置和调度 GPU 成一类资源以供集群中节点使用。
特性状态: Kubernetes v1.26 [stable]

Kubernetes 支持使用设备插件来跨集群中的不同节点管理 AMD 和 NVIDIA GPU(图形处理单元),目前处于稳定状态。

本页介绍用户如何使用 GPU 以及当前存在的一些限制。

使用设备插件

Kubernetes 实现了设备插件(Device Plugin),让 Pod 可以访问类似 GPU 这类特殊的硬件功能特性。

作为集群管理员,你要在节点上安装来自对应硬件厂商的 GPU 驱动程序,并运行来自 GPU 厂商的对应设备插件。以下是一些厂商说明的链接:

一旦你安装了插件,你的集群就会暴露一个自定义可调度的资源,例如 amd.com/gpunvidia.com/gpu

你可以通过请求这个自定义的 GPU 资源在你的容器中使用这些 GPU,其请求方式与请求 cpumemory 时相同。 不过,在如何指定自定义设备的资源请求方面存在一些限制。

GPU 只能在 limits 部分指定,这意味着:

  • 你可以指定 GPU 的 limits 而不指定其 requests,因为 Kubernetes 将默认使用限制值作为请求值。
  • 你可以同时指定 limitsrequests,不过这两个值必须相等。
  • 你不可以仅指定 requests 而不指定 limits

以下是一个 Pod 请求 GPU 的示例清单:

apiVersion: v1
kind: Pod
metadata:
  name: example-vector-add
spec:
  restartPolicy: OnFailure
  containers:
    - name: example-vector-add
      image: "registry.example/example-vector-add:v42"
      resources:
        limits:
          gpu-vendor.example/example-gpu: 1 # 请求 1 个 GPU

管理配有不同类型 GPU 的集群

如果集群内部的不同节点上有不同类型的 NVIDIA GPU, 那么你可以使用节点标签和节点选择器来将 Pod 调度到合适的节点上。

例如:

# 为你的节点加上它们所拥有的加速器类型的标签
kubectl label nodes node1 accelerator=example-gpu-x100
kubectl label nodes node2 accelerator=other-gpu-k915

这个标签键 accelerator 只是一个例子;如果你愿意,可以使用不同的标签键。

自动节点标签

作为管理员,你可以通过部署 Kubernetes Node Feature Discovery (NFD) 来自动发现所有启用 GPU 的节点并为其打标签。NFD 检测 Kubernetes 集群中每个节点上可用的硬件特性。 通常,NFD 被配置为以节点标签广告这些特性,但 NFD 也可以添加扩展的资源、注解和节点污点。 NFD 兼容所有支持版本的 Kubernetes。 NFD 默认会为检测到的特性创建特性标签。 管理员可以利用 NFD 对具有某些具体特性的节点添加污点,以便只有请求这些特性的 Pod 可以被调度到这些节点上。

你还需要一个 NFD 插件,将适当的标签添加到你的节点上; 这些标签可以是通用的,也可以是供应商特定的。你的 GPU 供应商可能会为 NFD 提供第三方插件; 更多细节请查阅他们的文档。

apiVersion: v1
kind: Pod
metadata:
  name: example-vector-add
spec:
  restartPolicy: OnFailure
  # 你可以使用 Kubernetes 节点亲和性将此 Pod 调度到提供其容器所需的那种 GPU 的节点上
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: "gpu.gpu-vendor.example/installed-memory"
            operator: Gt #(大于)
            values: ["40535"]
          - key: "feature.node.kubernetes.io/pci-10.present" # NFD 特性标签
            values: ["true"] #(可选)仅调度到具有 PCI 设备 10 的节点上
  containers:
    - name: example-vector-add
      image: "registry.example/example-vector-add:v42"
      resources:
        limits:
          gpu-vendor.example/example-gpu: 1 # 请求 1 个 GPU

GPU 供应商实现

4.16 - 管理巨页(HugePage)

将巨页作为集群中的可调度资源来配置和管理
特性状态: Kubernetes v1.30 [stable]

Kubernetes 支持在 Pod 应用中使用预先分配的巨页。本文描述了用户如何使用巨页,以及当前的限制。

准备开始

为了使节点能够上报巨页容量,Kubernetes 节点必须预先分配巨页

节点能够预先分配多种规格的巨页。例如,在 /etc/default/grub 中的以下这一行分配了 2*1GiB 的 1 GiB 页面和 512*2 MiB 的 2 MiB 页面。

GRUB_CMDLINE_LINUX="hugepagesz=1G hugepages=2 hugepagesz=2M hugepages=512"

节点将自动发现并报告所有巨页资源作为可调度资源。

当你描述 Node 时,你应该在 CapacityAllocatable 节中看到类似以下内容:

Capacity:
  cpu:                ...
  ephemeral-storage:  ...
  hugepages-1Gi:      2Gi
  hugepages-2Mi:      1Gi
  memory:             ...
  pods:               ...
Allocatable:
  cpu:                ...
  ephemeral-storage:  ...
  hugepages-1Gi:      2Gi
  hugepages-2Mi:      1Gi
  memory:             ...
  pods:               ...

API

用户可以通过在容器级别的资源需求中使用资源名称 hugepages-<size> 来使用巨页,其中的 size 是特定节点上支持的以整数值表示的最小二进制单位。 例如,如果一个节点支持 2048KiB 和 1048576KiB 页面大小,它将公开可调度的资源 hugepages-2Mihugepages-1Gi。与 CPU 或内存不同,巨页不支持过量使用(overcommit)。 注意,在请求巨页资源时,还必须请求内存或 CPU 资源。

同一 Pod 的 spec 中可能会消耗不同尺寸的巨页。在这种情况下,它必须对所有挂载卷使用 medium: HugePages-<hugepagesize> 标识。

apiVersion: v1
kind: Pod
metadata:
  name: huge-pages-example
spec:
  containers:
  - name: example
    image: fedora:latest
    command:
    - sleep
    - inf
    volumeMounts:
    - mountPath: /hugepages-2Mi
      name: hugepage-2mi
    - mountPath: /hugepages-1Gi
      name: hugepage-1gi
    resources:
      limits:
        hugepages-2Mi: 100Mi
        hugepages-1Gi: 2Gi
        memory: 100Mi
      requests:
        memory: 100Mi
  volumes:
  - name: hugepage-2mi
    emptyDir:
      medium: HugePages-2Mi
  - name: hugepage-1gi
    emptyDir:
      medium: HugePages-1Gi

Pod 只有在请求同一大小的巨页时才使用 medium:HugePages

apiVersion: v1
kind: Pod
metadata:
  name: huge-pages-example
spec:
  containers:
  - name: example
    image: fedora:latest
    command:
    - sleep
    - inf
    volumeMounts:
    - mountPath: /hugepages
      name: hugepage
    resources:
      limits:
        hugepages-2Mi: 100Mi
        memory: 100Mi
      requests:
        memory: 100Mi
  volumes:
  - name: hugepage
    emptyDir:
      medium: HugePages
  • 巨页的资源请求值必须等于其限制值。该条件在指定了资源限制,而没有指定请求的情况下默认成立。
  • 巨页是被隔离在 Pod 作用域的,因此每个容器在 spec 中都对 cgroup 沙盒有自己的限制。
  • 巨页可用于 EmptyDir 卷,不过 EmptyDir 卷所使用的巨页数量不能够超出 Pod 请求的巨页数量。
  • 通过带有 SHM_HUGETLBshmget() 使用巨页的应用,必须运行在一个与 proc/sys/vm/hugetlb_shm_group 匹配的补充组下。
  • 通过 ResourceQuota 资源,可以使用 hugepages-<size> 标记控制每个命名空间下的巨页使用量, 类似于使用 cpumemory 来控制其他计算资源。

4.17 - 用插件扩展 kubectl

通过创建和安装 kubectl 插件扩展 kubectl。

本指南演示了如何为 kubectl 安装和编写扩展。 通过将核心 kubectl 命令看作与 Kubernetes 集群交互的基本构建块, 集群管理员可以将插件视为一种利用这些构建块创建更复杂行为的方法。 插件用新的子命令扩展了 kubectl,允许新的和自定义的特性不包括在 kubectl 的主要发行版中。

准备开始

你需要安装一个可用的 kubectl 可执行文件。

安装 kubectl 插件

插件是一个独立的可执行文件,名称以 kubectl- 开头。 要安装插件,将其可执行文件移动到 PATH 中的任何位置。

你也可以使用 Krew 来发现和安装开源的 kubectl 插件。 Krew 是一个由 Kubernetes SIG CLI 社区维护的插件管理器。

发现插件

kubectl 提供一个命令 kubectl plugin list,用于搜索 PATH 查找有效的插件可执行文件。 执行此命令将遍历 PATH 中的所有文件。任何以 kubectl- 开头的可执行文件都将在这个命令的输出中以它们在 PATH 中出现的顺序显示。 任何以 kubectl- 开头的文件如果不可执行,都将包含一个警告。 对于任何相同的有效插件文件,都将包含一个警告。

你可以使用 Krew 从社区策划的插件索引 中发现和安装 kubectl 插件。

限制

目前无法创建覆盖现有 kubectl 命令的插件。 例如,创建一个插件 kubectl-version 将导致该插件永远不会被执行, 因为现有的 kubectl version 命令总是优先于它执行。 由于这个限制,也不可能使用插件将新的子命令添加到现有的 kubectl 命令中。 例如,通过将插件命名为 kubectl-create-foo 来添加子命令 kubectl create foo 将导致该插件被忽略。

对于任何试图这样做的有效插件 kubectl plugin list 的输出中将显示警告。

编写 kubectl 插件

你可以用任何编程语言或脚本编写插件,允许你编写命令行命令。

不需要安装插件或预加载,插件可执行程序从 kubectl 二进制文件接收继承的环境, 插件根据其名称确定它希望实现的命令路径。 例如,名为 kubectl-foo 的插件提供了命令 kubectl foo。 必须将插件的可执行文件安装在 PATH 中的某个位置。

示例插件

#!/bin/bash

# 可选的参数处理
if [[ "$1" == "version" ]]
then
    echo "1.0.0"
    exit 0
fi

# 可选的参数处理
if [[ "$1" == "config" ]]
then
    echo $KUBECONFIG
    exit 0
fi

echo "I am a plugin named kubectl-foo"

使用插件

要使用某插件,先要使其可执行:

sudo chmod +x ./kubectl-foo

并将它放在你的 PATH 中的任何地方:

sudo mv ./kubectl-foo /usr/local/bin

你现在可以调用你的插件作为 kubectl 命令:

kubectl foo
I am a plugin named kubectl-foo

所有参数和标记按原样传递给可执行文件:

kubectl foo version
1.0.0

所有环境变量也按原样传递给可执行文件:

export KUBECONFIG=~/.kube/config
kubectl foo config
/home/<user>/.kube/config
KUBECONFIG=/etc/kube/config kubectl foo config
/etc/kube/config

此外,传递给插件的第一个参数总是调用它的位置的绝对路径(在上面的例子中,$0 将等于 /usr/local/bin/kubectl-foo)。

命名插件

如上面的例子所示,插件根据文件名确定要实现的命令路径,插件所针对的命令路径中的每个子命令都由破折号(-)分隔。 例如,当用户调用命令 kubectl foo bar baz 时,希望调用该命令的插件的文件名为 kubectl-foo-bar-baz

参数和标记处理

kubectl 插件必须解析并检查传递给它们的所有参数。 参阅使用命令行运行时包了解针对 插件开发人员的 Go 库的细节。

这里是一些用户调用你的插件的时候提供额外标志和参数的场景。 这些场景时基于上述案例中的 kubectl-foo-bar-baz 插件的。

如果你运行 kubectl foo bar baz arg1 --flag=value arg2,kubectl 的插件机制将首先尝试找到 最长可能名称的插件,在本例中是 kubectl-foo-bar-baz-arg1。 当没有找到这个插件时,kubectl 就会将最后一个以破折号分隔的值视为参数(在本例中为 arg1), 并尝试找到下一个最长的名称 kubectl-foo-bar-baz。 在找到具有此名称的插件后,它将调用该插件,并在其名称之后将所有参数和标志传递给插件进程。

示例:

# 创建一个插件
echo -e '#!/bin/bash\n\necho "My first command-line argument was $1"' > kubectl-foo-bar-baz
sudo chmod +x ./kubectl-foo-bar-baz

# 将插件放到 PATH 下完成"安装"
sudo mv ./kubectl-foo-bar-baz /usr/local/bin

# 确保 kubectl 能够识别我们的插件
kubectl plugin list
The following kubectl-compatible plugins are available:

/usr/local/bin/kubectl-foo-bar-baz
# 测试通过 "kubectl" 命令来调用我们的插件时可行的
# 即使我们给插件传递一些额外的参数或标志
kubectl foo bar baz arg1 --meaningless-flag=true
My first command-line argument was arg1

正如你所看到的,你的插件是基于用户指定的 kubectl 命令找到的, 所有额外的参数和标记都是按原样传递给插件可执行文件的。

带有破折号和下划线的名称

虽然 kubectl 插件机制在插件文件名中使用破折号(-)分隔插件处理的子命令序列, 但是仍然可以通过在文件名中使用下划线(_)来创建命令行中包含破折号的插件命令。

例子:

# 创建文件名中包含下划线的插件
echo -e '#!/bin/bash\n\necho "I am a plugin with a dash in my name"' > ./kubectl-foo_bar
sudo chmod +x ./kubectl-foo_bar
  
# 将插件放到 PATH 下
sudo mv ./kubectl-foo_bar /usr/local/bin

# 现在可以通过 kubectl 来调用插件
kubectl foo-bar
I am a plugin with a dash in my name

请注意,在插件文件名中引入下划线并不会阻止你使用 kubectl foo_bar 之类的命令。 可以使用破折号(-)或下划线(_)调用上面示例中的命令:

# 我们的插件也可以用破折号来调用
kubectl foo-bar
I am a plugin with a dash in my name
# 你也可以使用下划线来调用我们的定制命令
kubectl foo_bar
I am a plugin with a dash in my name

命名冲突和弊端

可以在 PATH 的不同位置提供多个文件名相同的插件, 例如,给定一个 PATH 为: PATH=/usr/local/bin/plugins:/usr/local/bin/moreplugins, 在 /usr/local/bin/plugins/usr/local/bin/moreplugins 中可以存在一个插件 kubectl-foo 的副本,这样 kubectl plugin list 命令的输出就是:

PATH=/usr/local/bin/plugins:/usr/local/bin/moreplugins kubectl plugin list
The following kubectl-compatible plugins are available:
  
/usr/local/bin/plugins/kubectl-foo
/usr/local/bin/moreplugins/kubectl-foo
  - warning: /usr/local/bin/moreplugins/kubectl-foo is overshadowed by a similarly named plugin: /usr/local/bin/plugins/kubectl-foo
  
error: one plugin warning was found

在上面的场景中 /usr/local/bin/moreplugins/kubectl-foo 下的警告告诉你这个插件永远不会被执行。 相反,首先出现在你 PATH 中的可执行文件 /usr/local/bin/plugins/kubectl-foo 总是首先被 kubectl 插件机制找到并执行。

解决这个问题的一种方法是你确保你希望与 kubectl 一起使用的插件的位置总是在你的 PATH 中首先出现。 例如,如果你总是想使用 /usr/local/bin/moreplugins/kubectl foo, 那么在调用 kubectl 命令 kubectl foo 时,你只需将路径的值更改为 /usr/local/bin/moreplugins:/usr/local/bin/plugins

调用最长的可执行文件名

对于插件文件名而言还有另一种弊端,给定用户 PATH 中的两个插件 kubectl-foo-barkubectl-foo-bar-bazkubectl 插件机制总是为给定的用户命令选择尽可能长的插件名称。下面的一些例子进一步的说明了这一点:

# 对于给定的 kubectl 命令,最长可能文件名的插件是被优先选择的
kubectl foo bar baz
Plugin kubectl-foo-bar-baz is executed
kubectl foo bar
Plugin kubectl-foo-bar is executed
kubectl foo bar baz buz
Plugin kubectl-foo-bar-baz is executed, with "buz" as its first argument
kubectl foo bar buz
Plugin kubectl-foo-bar is executed, with "buz" as its first argument

这种设计选择确保插件子命令可以跨多个文件实现,如果需要,这些子命令可以嵌套在"父"插件命令下:

ls ./plugin_command_tree
kubectl-parent
kubectl-parent-subcommand
kubectl-parent-subcommand-subsubcommand

检查插件警告

你可以使用前面提到的 kubectl plugin list 命令来确保你的插件可以被 kubectl 看到, 并且验证没有警告防止它被称为 kubectl 命令。

kubectl plugin list
The following kubectl-compatible plugins are available:
  
test/fixtures/pkg/kubectl/plugins/kubectl-foo
/usr/local/bin/kubectl-foo
  - warning: /usr/local/bin/kubectl-foo is overshadowed by a similarly named plugin: test/fixtures/pkg/kubectl/plugins/kubectl-foo
plugins/kubectl-invalid
  - warning: plugins/kubectl-invalid identified as a kubectl plugin, but it is not executable

error: 2 plugin warnings were found

使用命令行运行时包

如果你在编写 kubectl 插件,而且你选择使用 Go 语言,你可以利用 cli-runtime 工具库。

这些库提供了一些辅助函数,用来解析和更新用户的 kubeconfig 文件,向 API 服务器发起 REST 风格的请求,或者将参数绑定到某配置上, 抑或将其打印输出。

关于 CLI Runtime 仓库所提供的工具的使用实例,可参考 CLI 插件示例 项目。

分发 kubectl 插件

如果你开发了一个插件给别人使用,你应该考虑如何为其封装打包、如何分发软件 以及将来的更新到用户。

Krew

Krew 提供了一种对插件进行打包和分发的跨平台方式。 基于这种方式,你会在所有的目标平台(Linux、Windows、macOS 等)使用同一 种打包形式,包括为用户提供更新。 Krew 也维护一个插件索引(plugin index) 以便其他人能够发现你的插件并安装之。

原生的与特定平台的包管理

另一种方式是,你可以使用传统的包管理器(例如 Linux 上 的 aptyum, Windows 上的 Chocolatey、macOs 上的 Homebrew)。 只要能够将新的可执行文件放到用户的 PATH 路径上某处,这种包管理器就符合需要。 作为一个插件作者,如果你选择这种方式来分发,你就需要自己来管理和更新 你的 kubectl 插件的分发包,包括所有平台和所有发行版本。

源代码

你也可以发布你的源代码,例如,发布为某个 Git 仓库。 如果你选择这条路线,希望使用该插件的用户必须取回代码、配置一个构造环境 (如果需要编译的话)并部署该插件。 如果你也提供编译后的软件包,或者使用 Krew,那就会大大简化安装过程了。

接下来

  • 查看 CLI 插件库示例,查看用 Go 编写的插件的详细示例
  • 如有任何问题,请随时联系 SIG CLI 团队
  • 了解 Krew,一个 kubectl 插件管理器。

5 - 教程

Kubernetes 文档的这一部分包含教程。 每个教程展示了如何完成一个比单个任务更大的目标。 通常一个教程有几个部分,每个部分都有一系列步骤。在浏览每个教程之前, 你可能希望将标准化术语表页面添加到书签,供以后参考。

基础知识

配置

无状态应用程序

有状态应用程序

服务

安全

接下来

如果你要编写教程,请参阅内容页面类型 以获取有关教程页面类型的信息。

5.1 - 你好,Minikube

本教程向你展示如何使用 Minikube 在 Kubernetes 上运行一个应用示例。 教程提供了容器镜像,使用 NGINX 来对所有请求做出回应。

教程目标

  • 将一个示例应用部署到 Minikube。
  • 运行应用程序。
  • 查看应用日志。

准备开始

本教程假设你已经安装了 minikube。 有关安装说明,请参阅 minikube start步骤 1

你还需要安装 kubectl。 有关安装说明,请参阅安装工具

创建 Minikube 集群

minikube start

打开仪表板

打开 Kubernetes 仪表板。你可以通过两种不同的方式执行此操作:

打开一个新的终端,然后运行:

# 启动一个新的终端,并保持此命令运行。
minikube dashboard

现在,切换回运行 minikube start 的终端。

如果你不想 Minikube 为你打开 Web 浏览器,可以使用 --url 标志运行 dashboard 子命令。 minikube 会输出一个 URL,你可以在你喜欢的浏览器中打开该 URL。

打开一个新的终端,然后运行:

# 启动一个新的终端,并保持此命令运行。
minikube dashboard --url

现在,你可以使用此 URL 并切换回运行 minikube start 的终端。

创建 Deployment

Kubernetes Pod 是由一个或多个为了管理和联网而绑定在一起的容器构成的组。本教程中的 Pod 只有一个容器。 Kubernetes Deployment 检查 Pod 的健康状况,并在 Pod 中的容器终止的情况下重新启动新的容器。 Deployment 是管理 Pod 创建和扩展的推荐方法。

  1. 使用 kubectl create 命令创建管理 Pod 的 Deployment。该 Pod 根据提供的 Docker 镜像运行容器。

    # 运行包含 Web 服务器的测试容器镜像
    kubectl create deployment hello-node --image=registry.k8s.io/e2e-test-images/agnhost:2.39 -- /agnhost netexec --http-port=8080
    
  1. 查看 Deployment:

    kubectl get deployments
    

    输出结果类似于这样:

    NAME         READY   UP-TO-DATE   AVAILABLE   AGE
    hello-node   1/1     1            1           1m
    
  1. 查看 Pod:

    kubectl get pods
    

    输出结果类似于这样:

    NAME                          READY     STATUS    RESTARTS   AGE
    hello-node-5f76cf6ccf-br9b5   1/1       Running   0          1m
    
  1. 查看集群事件:

    kubectl get events
    
  1. 查看 kubectl 配置:

    kubectl config view
    
  1. 查看 Pod 中容器的应用程序日志。

    kubectl logs hello-node-5f76cf6ccf-br9b5
    

    输出类似于:

    I0911 09:19:26.677397       1 log.go:195] Started HTTP server on port 8080
    I0911 09:19:26.677586       1 log.go:195] Started UDP server on port  8081
    

创建 Service

默认情况下,Pod 只能通过 Kubernetes 集群中的内部 IP 地址访问。 要使得 hello-node 容器可以从 Kubernetes 虚拟网络的外部访问,你必须将 Pod 暴露为 Kubernetes Service

  1. 使用 kubectl expose 命令将 Pod 暴露给公网:

    kubectl expose deployment hello-node --type=LoadBalancer --port=8080
    

    这里的 --type=LoadBalancer 参数表明你希望将你的 Service 暴露到集群外部。

    测试镜像中的应用程序代码仅监听 TCP 8080 端口。 如果你用 kubectl expose 暴露了其它的端口,客户端将不能访问其它端口。

  1. 查看你创建的 Service:

    kubectl get services
    

    输出结果类似于这样:

    NAME         TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
    hello-node   LoadBalancer   10.108.144.78   <pending>     8080:30369/TCP   21s
    kubernetes   ClusterIP      10.96.0.1       <none>        443/TCP          23m
    

    对于支持负载均衡器的云服务平台而言,平台将提供一个外部 IP 来访问该服务。 在 Minikube 上,LoadBalancer 使得服务可以通过命令 minikube service 访问。

  1. 运行下面的命令:

    minikube service hello-node
    

    这将打开一个浏览器窗口,为你的应用程序提供服务并显示应用的响应。

启用插件

Minikube 有一组内置的插件, 可以在本地 Kubernetes 环境中启用、禁用和打开。

  1. 列出当前支持的插件:

    minikube addons list
    

    输出结果类似于这样:

    addon-manager: enabled
    dashboard: enabled
    default-storageclass: enabled
    efk: disabled
    freshpod: disabled
    gvisor: disabled
    helm-tiller: disabled
    ingress: disabled
    ingress-dns: disabled
    logviewer: disabled
    metrics-server: disabled
    nvidia-driver-installer: disabled
    nvidia-gpu-device-plugin: disabled
    registry: disabled
    registry-creds: disabled
    storage-provisioner: enabled
    storage-provisioner-gluster: disabled
    
  1. 启用插件,例如 metrics-server

    minikube addons enable metrics-server
    

    输出结果类似于这样:

    The 'metrics-server' addon is enabled
    
  1. 查看通过安装该插件所创建的 Pod 和 Service:

    kubectl get pod,svc -n kube-system
    

    输出结果类似于这样:

    NAME                                        READY     STATUS    RESTARTS   AGE
    pod/coredns-5644d7b6d9-mh9ll                1/1       Running   0          34m
    pod/coredns-5644d7b6d9-pqd2t                1/1       Running   0          34m
    pod/metrics-server-67fb648c5                1/1       Running   0          26s
    pod/etcd-minikube                           1/1       Running   0          34m
    pod/influxdb-grafana-b29w8                  2/2       Running   0          26s
    pod/kube-addon-manager-minikube             1/1       Running   0          34m
    pod/kube-apiserver-minikube                 1/1       Running   0          34m
    pod/kube-controller-manager-minikube        1/1       Running   0          34m
    pod/kube-proxy-rnlps                        1/1       Running   0          34m
    pod/kube-scheduler-minikube                 1/1       Running   0          34m
    pod/storage-provisioner                     1/1       Running   0          34m
    
    NAME                           TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)             AGE
    service/metrics-server         ClusterIP   10.96.241.45    <none>        80/TCP              26s
    service/kube-dns               ClusterIP   10.96.0.10      <none>        53/UDP,53/TCP       34m
    service/monitoring-grafana     NodePort    10.99.24.54     <none>        80:30002/TCP        26s
    service/monitoring-influxdb    ClusterIP   10.111.169.94   <none>        8083/TCP,8086/TCP   26s
    
  1. 检查 metrics-server 的输出:

    kubectl top pods
    

    输出类似于:

    NAME                         CPU(cores)   MEMORY(bytes)   
    hello-node-ccf4b9788-4jn97   1m           6Mi             
    

    如果你看到以下消息,请等待并重试:

    error: Metrics API not available
    
  1. 禁用 metrics-server

    minikube addons disable metrics-server
    

    输出结果类似于这样:

    metrics-server was successfully disabled
    

清理

现在可以清理你在集群中创建的资源:

kubectl delete service hello-node
kubectl delete deployment hello-node

停止 Minikube 集群:

minikube stop

可选地,删除 Minikube 虚拟机(VM):

# 可选的
minikube delete

如果你还想使用 Minikube 进一步学习 Kubernetes,那就不需要删除 Minikube。

结论

本页介绍了启动和运行 minikube 集群的基本知识,现在部署应用的准备工作已经完成。

接下来

5.2 - 学习 Kubernetes 基础知识

Shutdown of interactive tutorials

The interactive tutorials on this website are being shut down. The Kubernetes project hopes to reinstate a similar interactive learning option in the long term.

The shutdown follows O'Reilly Media's 2019 acquisition of Katacoda.

Kubernetes is grateful to O'Reilly and Katacoda for many years of helping people take their first steps in learning Kubernetes.

The tutorials will cease to function after the 31st of March, 2023. For more information, see "Free Katacoda Kubernetes Tutorials Are Shutting Down."

Kubernetes 基础

本教程介绍了 Kubernetes 集群编排系统的基础知识。每个模块包含关于 Kubernetes 主要特性和概念的一些背景信息,并包括一个在线互动教程。这些互动教程让你可以自己管理一个简单的集群及其容器化应用程序。

使用互动教程,你可以学习:

  • 在集群上部署容器化应用程序
  • 弹性部署
  • 使用新的软件版本,更新容器化应用程序
  • 调试容器化应用程序

这些教程使用 Katacoda 在你的浏览器中运行一个运行着 Minikube 的虚拟终端,Minikube 是 Kubernetes 的一个小规模本地部署,可以运行在任何地方。不需要安装任何软件或进行任何配置;每个交互性教程都直接从你的网页浏览器上运行。


Kubernetes 可以为你做些什么?

通过现代的 Web 服务,用户希望应用程序能够 24/7 全天候使用,开发人员希望每天可以多次发布部署新版本的应用程序。 容器化可以帮助软件包达成这些目标,使应用程序能够以简单快速的方式发布和更新,而无需停机。Kubernetes 帮助你确保这些容器化的应用程序在你想要的时间和地点运行,并帮助应用程序找到它们需要的资源和工具。Kubernetes 是一个可用于生产的开源平台,根据 Google 容器集群方面积累的经验,以及来自社区的最佳实践而设计。


5.2.1 - 创建集群

了解 Kubernetes 集群并使用 Minikube 创建一个简单的集群。

5.2.1.1 - 使用 Minikube 创建集群

了解 Kubernetes 集群。 了解 Minikube。 启动一个 Kubernetes 集群。

目标

  • 了解 Kubernetes 集群。
  • 了解 Minikube。
  • 在你的电脑上开启一个 Kubernetes 集群。

Kubernetes 集群

Kubernetes 协调一个高可用计算机集群,每个计算机作为独立单元互相连接工作。 Kubernetes 中的抽象允许你将容器化的应用部署到集群,而无需将它们绑定到某个特定的独立计算机。 为了使用这种新的部署模型,应用需要以将应用与单个主机分离的方式打包:它们需要被容器化。 与过去的那种应用直接以包的方式深度与主机集成的部署模型相比,容器化应用更灵活、更可用。 Kubernetes 以更高效的方式跨集群自动分发和调度应用容器。 Kubernetes 是一个开源平台,并且可应用于生产环境。

一个 Kubernetes 集群包含两种类型的资源:

  • 控制面调度整个集群
  • 节点负责运行应用

总结:

  • Kubernetes 集群
  • Minikube

Kubernetes 是一个生产级别的开源平台,可协调在计算机集群内和跨计算机集群的应用容器的部署(调度)和执行.


集群图


控制面负责管理整个集群。 控制面协调集群中的所有活动,例如调度应用、维护应用的所需状态、应用扩容以及推出新的更新。

节点是一个虚拟机或者物理机,它在 Kubernetes 集群中充当工作机器的角色。 每个节点都有 Kubelet,它管理节点而且是节点与控制面通信的代理。 节点还应该具有用于​​处理容器操作的工具,例如 Docker 或 rkt。 处理生产级流量的 Kubernetes 集群至少应具有三个节点,因为如果一个节点出现故障其对应的 etcd 成员和控制面实例都会丢失, 并且冗余会受到影响。你可以通过添加更多控制面节点来降低这种风险。

控制面管理集群,节点用于托管正在运行的应用。

在 Kubernetes 上部署应用时,你告诉控制面启动应用容器。 控制面就编排容器在集群的节点上运行。 节点使用控制面暴露的 Kubernetes API 与控制面通信。终端用户也可以使用 Kubernetes API 与集群交互。

Kubernetes 既可以部署在物理机上也可以部署在虚拟机上。你可以使用 Minikube 开始部署 Kubernetes 集群。 Minikube 是一种轻量级的 Kubernetes 实现,可在本地计算机上创建 VM 并部署仅包含一个节点的简单集群。 Minikube 可用于 Linux、macOS 和 Windows 系统。Minikube CLI 提供了用于引导集群工作的多种操作, 包括启动、停止、查看状态和删除。

既然你已经知道 Kubernetes 是什么,访问你好 Minikube, 在你的电脑上试试这个。

完成这一步之后,继续使用 kubectl 创建一个 Deployment

5.2.1.2 - 交互式教程 - 创建集群

内容不可用

创建集群的交互式教程不可用。 有关更多信息,请参阅停服公告

5.2.2 - 部署应用

5.2.2.1 - 使用 kubectl 创建 Deployment

学习应用的部署。 使用 kubectl 在 Kubernetes 上部署第一个应用。

目标

  • 学习应用的部署。
  • 使用 kubectl 在 Kubernetes 上部署第一个应用。

Kubernetes 部署

一旦运行了 Kubernetes 集群, 就可以在其上部署容器化应用。为此,你需要创建 Kubernetes Deployment。 Deployment 指挥 Kubernetes 如何创建和更新应用的实例。 创建 Deployment 后,Kubernetes 控制平面将 Deployment 中包含的应用实例调度到集群中的各个节点上。

创建应用实例后,Kubernetes Deployment 控制器会持续监视这些实例。 如果托管实例的节点关闭或被删除,则 Deployment 控制器会将该实例替换为集群中另一个节点上的实例。 这提供了一种自我修复机制来解决机器故障维护问题。

在没有 Kubernetes 这种编排系统之前,安装脚本通常用于启动应用,但它们不允许从机器故障中恢复。 通过创建应用实例并使它们在节点之间运行,Kubernetes Deployment 提供了一种与众不同的应用管理方法。

总结:

  • Deployment
  • kubectl

Deployment 负责创建和更新应用的实例


部署你在 Kubernetes 上的第一个应用


你可以使用 Kubernetes 命令行界面 kubectl 创建和管理 Deployment。 kubectl 使用 Kubernetes API 与集群进行交互。在本单元中,你将学习创建在 Kubernetes 集群上运行应用的 Deployment 所需的最常见的 kubectl 命令。

创建 Deployment 时,你需要指定应用的容器镜像以及要运行的副本数。 你可以稍后通过更新 Deployment 来更改该信息; 模块 56 讨论了如何扩展和更新 Deployment。

应用需要打包成一种受支持的容器格式,以便部署在 Kubernetes 上

对于你第一次部署,你将使用打包在 Docker 容器中的 hello-node 应用,该应用使用 NGINX 回显所有请求。 (如果你尚未尝试创建 hello-node 应用并使用容器进行部署,则可以首先按照 Hello Minikube 教程中的说明进行操作)。

你也需要安装好 kubectl。如果你需要安装 kubectl,参阅安装工具

现在你已经了解了部署的内容,让我们部署第一个应用!


kubectl 基础知识

kubectl 命令的常见格式是:kubectl 操作资源

这会对指定的资源(类似 nodedeployment)执行指定的操作(类似 create、describe 或 delete)。 你可以在子命令之后使用 --help 获取可能参数相关的更多信息(例如:kubectl get nodes --help)。

通过运行 kubectl version 命令,查看 kubectl 是否被配置为与你的集群通信。

查验 kubectl 是否已安装,你能同时看到客户端和服务器版本。

要查看集群中的节点,运行 kubectl get nodes 命令。

你可以看到可用的节点。稍后 Kubernetes 将根据节点可用的资源选择在哪里部署应用。

部署一个应用

让我们使用 kubectl create deployment 命令在 Kubernetes 上部署第一个应用。 我们需要提供 Deployment 命令以及应用镜像位置(包括托管在 Docker hub 之外的镜像的完整仓库地址)。

kubectl create deployment kubernetes-bootcamp --image=gcr.io/google-samples/kubernetes-bootcamp:v1

很好!你刚刚通过创建 Deployment 部署了第一个应用。这个过程中执行了以下一些操作:

  • 搜索应用实例可以运行的合适节点(我们只有一个可用的节点)
  • 调度应用在此节点上运行
  • 配置集群在需要时将实例重新调度到新的节点上

要列出你的 Deployment,使用 kubectl get deployments 命令:

kubectl get deployments

我们看到有 1 个 Deployment 运行应用的单个实例。这个实例运行在节点上的一个容器内。

查看应用

在 Kubernetes 内运行的 Pod 运行在一个私有的、隔离的网络上。 默认这些 Pod 可以从同一 Kubernetes 集群内的其他 Pod 和服务看到,但超出这个网络后则看不到。 当我们使用 kubectl 时,我们通过 API 端点交互与应用进行通信。

模块 4 中我们将讲述如何将应用暴露到 Kubernetes 集群外的其他选项。 同样作为基础教程,我们不会在这里详细解释 Pod 是什么,后面的主题会介绍它。

kubectl proxy 命令可以创建一个代理,将通信转发到集群范围的私有网络。 按下 Ctrl-C 此代理可以被终止,且在此代理运行期间不会显示任何输出。

你需要打开第二个终端窗口来运行此代理。

kubectl proxy

现在我们在主机(终端)和 Kubernetes 集群之间有一个连接。此代理能够从这些终端直接访问 API。

你可以看到通过代理端点托管的所有 API。 例如,我们可以使用以下 curl 命令直接通过 API 查询版本:

curl http://localhost:8001/version

API 服务器将基于也能通过代理访问的 Pod 名称为每个 Pod 自动创建端点。

首先我们需要获取 Pod 名称,我们将存储到环境变量 POD_NAME 中:

export POD_NAME=$(kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}')
echo Name of the Pod: $POD_NAME

你可以运行以下命令通过代理的 API 访问 Pod:

curl http://localhost:8001/api/v1/namespaces/default/pods/$POD_NAME/

为了不使用代理也能访问新的 Deployment,需要一个 Service,这将在下一个 模块 4 中讲述。

如果你准备好了,请访问查看 Pod 和 Node

5.2.2.2 - 交互式教程 - 部署应用

内容不可用

部署应用的交互式教程不可用。 有关更多信息,请参阅停服公告

5.2.3 - 了解你的应用

5.2.3.1 - 查看 Pod 和节点

学习如何使用 kubectl get、kubectl describe、kubectl logs 和 kubectl exec 排除 Kubernetes 应用故障。

目标

  • 了解 Kubernetes Pod。
  • 了解 Kubernetes 节点。
  • 对已部署的应用故障排除。

Kubernetes Pod

在模块 2 中创建 Deployment 时, Kubernetes 创建了一个 Pod 来托管你的应用实例。Pod 是 Kubernetes 抽象出来的, 表示一组一个或多个应用容器(如 Docker),以及这些容器的一些共享资源。这些资源包括:

  • 共享存储,当作卷
  • 网络,作为唯一的集群 IP 地址
  • 有关每个容器如何运行的信息,例如容器镜像版本或要使用的特定端口

Pod 为特定于应用的“逻辑主机”建模,并且可以包含相对紧耦合的不同应用容器。 例如,Pod 可能既包含带有 Node.js 应用的容器,也包含另一个不同的容器, 用于提供 Node.js 网络服务器要发布的数据。Pod 中的容器共享 IP 地址和端口, 始终位于同一位置并且共同调度,并在同一节点上的共享上下文中运行。

Pod 是 Kubernetes 平台上的原子单元。当我们在 Kubernetes 上创建 Deployment 时, 该 Deployment 会在其中创建包含容器的 Pod(而不是直接创建容器)。 每个 Pod 都与调度它的节点绑定,并保持在那里直到终止(根据重启策略)或删除。 如果节点发生故障,则会在集群中的其他可用节点上调度相同的 Pod。

总结:

  • Pod
  • 节点
  • Kubectl 主要命令

Pod 是一个或多个应用容器(例如 Docker)的组合,包括共享存储(卷)、IP 地址和有关如何运行它们的信息。


Pod 概览


节点

Pod 总是运行在节点上。节点是 Kubernetes 中参与计算的机器,可以是虚拟机或物理计算机,具体取决于集群。 每个节点由控制面管理。节点可以有多个 Pod,Kubernetes 控制面会自动处理在集群中的节点上调度 Pod。 控制面的自动调度考量了每个节点上的可用资源。

每个 Kubernetes 节点至少运行:

  • Kubelet,负责 Kubernetes 控制面和节点之间通信的进程;它管理机器上运行的 Pod 和容器。
  • 容器运行时(如 Docker)负责从镜像仓库中提取容器镜像、解压缩容器以及运行应用。

如果容器紧耦合并且需要共享磁盘等资源,则只应将其编排在一个 Pod 中。


节点概览


使用 kubectl 进行故障排除

在模块 2 中, 你使用了 kubectl 命令行界面。你将继续在第 3 个模块中使用 kubectl 来获取有关已部署应用及其环境的信息。 最常见的操作可以使用以下 kubectl 子命令完成:

  • kubectl get - 列出资源
  • kubectl describe - 显示有关资源的详细信息
  • kubectl logs - 打印 Pod 中容器的日志
  • kubectl exec - 在 Pod 中的容器上执行命令

你可以使用这些命令查看应用的部署时间、当前状态、运行位置以及配置。

现在我们了解了有关集群组件和命令行的更多信息,让我们来探索一下我们的应用。

节点是 Kubernetes 中负责计算的机器,可能是虚拟机或物理计算机,具体取决于集群。 多个 Pod 可以在一个节点上运行。

检查应用配置

让我们验证之前场景中部署的应用是否在运行。我们将使用 kubectl get 命令查看现存的 Pod:

kubectl get pods

如果没有 Pod 在运行,请等几秒,让 Pod 再次列出。一旦看到一个 Pod 在运行,就可以继续操作。

接下来,要查看 Pod 内有哪些容器以及使用了哪些镜像来构建这些容器,我们运行 kubectl describe pods 命令:

kubectl describe pods

我们在这里看到了 Pod 的容器相关详情:IP 地址、使用的端口以及 Pod 生命期有关的事件列表。

describe 子命令的输出宽泛,涵盖了一些我们还未讲到的概念,但不用担心, 这节课结束时你就会熟悉这些概念了。

注:describe子命令可用于获取有关大多数 Kubernetes 原语的详细信息, 包括节点、Pod 和 Deployment。describe 的输出设计为人类可读的信息,而不是脚本化的信息。

在终端中显示应用

回想一下,Pod 运行在隔离的、私有的网络中,因此我们需要代理访问它们,这样才能进行调试和交互。 为了做到这一点,我们将使用 kubectl proxy 命令在第二个终端中运行一个代理。 打开一个新的终端窗口,在这个新的终端中运行以下命令:

kubectl proxy

现在我们再次获取 Pod 命令并直接通过代理查询该 Pod。 要获取 Pod 命令并将其存到 POD_NAME 环境变量中,运行以下命令:

export POD_NAME="$(kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}')"
echo Name of the Pod: $POD_NAME

要查看应用的输出,运行 curl 请求:

curl http://localhost:8001/api/v1/namespaces/default/pods/$POD_NAME:8080/proxy/

URL 是到 Pod API 的路由。

查看容器日志

应用通常发送到标准输出的所有内容都成为 Pod 内容器的日志。 我们可以使用 kubectl logs 命令检索这些日志:

kubectl logs "$POD_NAME"

注:我们不需要指定容器名称,因为在 Pod 内只有一个容器。

在容器上执行命令

一旦 Pod 启动并运行,我们就可以直接在容器上执行命令。 为此,我们使用 exec 子命令,并将 Pod 的名称作为参数。让我们列出环境变量:

kubectl exec "$POD_NAME" -- env

另外值得一提的是,由于 Pod 中只有一个容器,所以容器本身的名称可以被省略。

接下来,让我们在 Pod 的容器中启动一个 bash 会话:

kubectl exec -ti $POD_NAME -- bash

现在我们在运行 Node.js 应用的容器上有一个打开的控制台。该应用的源代码位于 server.js 文件中:

cat server.js

你可以通过运行 curl 命令查看应用是否启动:

curl http://localhost:8080

注:在这里我们使用了 localhost,因为我们在 NodeJS Pod 内执行了此命令。 如果你无法连接到 localhost:8080,请确保你已经运行了 kubectl exec 命令,并且从 Pod 内启动了该命令。

要关闭你的容器连接,键入 exit

如果你准备好了,请继续学习使用服务来暴露你的应用

5.2.3.2 - 交互式教程 - 探索你的应用

内容不可用

探索应用的交互式教程不可用。 有关更多信息,请参阅停服公告

5.2.4 - 公开地暴露你的应用

5.2.4.1 - 使用 Service 暴露你的应用

了解 Kubernetes 中的 Service。 理解标签和选择算符如何关联到 Service。 在 Kubernetes 集群外暴露应用。

目标

  • 了解 Kubernetes 中的 Service
  • 了解标签(Label)和选择算符(Selector)如何与 Service 关联
  • 在 Kubernetes 集群外用 Service 暴露应用

Kubernetes Service 总览

Kubernetes Pod 是转瞬即逝的。 Pod 拥有 生命周期。 当一个工作节点挂掉后, 在节点上运行的 Pod 也会消亡。 ReplicaSet 会自动地通过创建新的 Pod 驱动集群回到目标状态,以保证应用正常运行。 换一个例子,考虑一个具有 3 个副本的用作图像处理的后端程序。 这些副本是可替换的。前端系统不应该关心后端副本,即使某个 Pod 丢失或被重新创建。 此外,Kubernetes 集群中的每个 Pod 都有一个唯一的 IP 地址,即使是在同一个 Node 上的 Pod 也是如此, 因此需要一种方法来自动协调 Pod 之间的变化,以便应用保持运行。

Kubernetes 中的服务(Service)是一种抽象概念,它定义了 Pod 的逻辑集和访问 Pod 的协议。 Service 使从属 Pod 之间的松耦合成为可能。 和所有 Kubernetes 对象清单一样, Service 用 YAML 或者 JSON 来定义。 Service 下的一组 Pod 通常由一个 标签选择算符 来标记 (请参阅下面的说明为什么你可能想要一个 spec 中不包含 selector 的 Service)。

尽管每个 Pod 都有一个唯一的 IP 地址,但是如果没有 Service,这些 IP 不会被公开到集群外部。 Service 允许你的应用接收流量。 通过设置 Service 的 spec 中的 type,你可以用不同的方式公开 Service:

  • ClusterIP(默认)- 在集群的内部 IP 上公开 Service。这种类型使得 Service 只能从集群内访问。
  • NodePort - 使用 NAT 在集群中每个选定 Node 的相同端口上公开 Service 。使用<NodeIP>:<NodePort> 从集群外部访问 Service。是 ClusterIP 的超集。
  • LoadBalancer - 在当前云中创建一个外部负载均衡器(如果支持的话),并为 Service 分配一个固定的外部IP。是 NodePort 的超集。
  • ExternalName - 将 Service 映射到 externalName 字段的内容(例如 foo.bar.example.com),通过返回带有该名称的 CNAME 记录实现。不设置任何类型的代理。这种类型需要 kube-dns 的 v1.7 或更高版本,或者 CoreDNS 的 0.8 或更高版本。

关于不同 Service 类型的更多信息可以在使用源 IP 教程找到。 也请参阅 使用 Service 连接到应用

另外,需要注意的是有一些 Service 的用例不需要在 spec 中定义 selector。 一个创建时未设置 selector 的 Service 也不会创建相应的 Endpoint 对象。 这允许用户手动将 Service 映射到特定的端点。 没有 selector 的另一种可能是你在严格使用 type: ExternalName 服务。

总结

  • 将 Pod 暴露给外部通信
  • 跨多个 Pod 的负载均衡
  • 使用标签(Label)

Kubernetes 的 Service 是一个抽象层,它定义了一组 Pod 的逻辑集,并为这些 Pod 支持外部流量暴露、负载平衡和服务发现。


Service 和 Label

Service 为一组 Pod 提供流量路由。Service 是一种抽象,允许 Kubernetes 中的 Pod 死亡和复制,而不会影响应用。 在依赖的 Pod(如应用中的前端和后端组件)之间进行发现和路由是由 Kubernetes Service 处理的。

Service 通过标签和选择算符来匹配一组 Pod, 它们是允许对 Kubernetes 中的对象进行逻辑操作的一种分组原语。 标签是附加在对象上的键/值对,可以以多种方式使用:

  • 指定用于开发、测试和生产的对象
  • 嵌入版本标记
  • 使用标记将对象分类


标签可以在对象创建时或之后附加到对象上。它们可以随时被修改。现在使用 Service 发布我们的应用并添加一些标签。


第一步:创建新 Service

让我们来验证我们的应用正在运行。我们将使用 kubectl get 命令并查找现有的 Pod:

kubectl get pods

如果没有 Pod 正在运行,则意味着之前教程中的对象已被清理。这时, 请返回并参考 使用 kubectl 创建 Deployment 教程重新创建 Deployment。 请等待几秒钟,然后再次列举 Pod。一旦看到一个 Pod 正在运行,你就可以继续了。

接下来,让我们列举当前集群中的 Service:

kubectl get services

我们有一个名为 kubernetes 的 Service ,它在 minikube 启动集群时默认创建。 要创建一个新的 Service 然后暴露给外部流量,我们将使用 expose 命令,并将 NodePort 作为参数。

kubectl expose deployment/kubernetes-bootcamp --type="NodePort" --port 8080

让我们再次运行 get services 子命令:

kubectl get services

我们现在有一个运行中的 Service 名为 kubernetes-bootcamp。 这里我们看到 Service 收到了一个唯一的集群内 IP(cluster-IP),一个内部端口和一个外部 IP (external-IP)(Node 的 IP)。

要得到外部打开的端口号(对于 type: NodePort 的服务),我们需要运行 describe service 子命令:

kubectl describe services/kubernetes-bootcamp

创建一个名为 NODE_PORT 的环境变量,它的值为所分配的 Node 端口:

export NODE_PORT="$(kubectl get services/kubernetes-bootcamp -o go-template='{{(index .spec.ports 0).nodePort}}')"
echo "NODE_PORT=$NODE_PORT"

现在我们可以使用 curl、Node 的 IP 地址和对外暴露的端口,来测试应用是否已经被公开到了集群外部:

curl http://"$(minikube ip):$NODE_PORT"

然后我们就会收到服务器的响应。Service 已经被暴露。

第二步:使用标签

Deployment 自动给我们的 Pod 创建了一个标签。通过 describe deployment 子命令你可以看到那个标签的名称(对应 key):

kubectl describe deployment

让我们使用这个标签来查询 Pod 列表。我们将使用 kubectl get pods 命令和 -l 参数,后面给出标签值:

kubectl get pods -l app=kubernetes-bootcamp

你可以用同样的方法列出现有的 Service:

kubectl get services -l app=kubernetes-bootcamp

获取 Pod 的名称,然后存放到 POD_NAME 环境变量:

export POD_NAME="$(kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}')"
echo "Name of the Pod: $POD_NAME"

要应用一个新的标签,我们使用 label 子命令,接着是对象类型、对象名称和新的标签:

kubectl label pods "$POD_NAME" version=v1

这将会在我们的 Pod 上应用一个新标签(我们把应用版本锁定到 Pod 上),然后我们可以通过 describe pods 命令检查它:

kubectl describe pods "$POD_NAME"

我们可以看到现在标签已经被附加到我们的 Pod 上。我们可以通过新的标签来查询 Pod 列表:

kubectl get pods -l version=v1

我们看到了对应的 Pod。

第三步:删除一个 Service

要删除一个 Service 你可以使用 delete service 子命令。这里也可以使用标签:

kubectl delete service -l app=kubernetes-bootcamp

确认对应的 Service 已经消失:

kubectl get services

这里确认了我们的 Service 已经被删除。要确认路由已经不再被公开,你可以 curl 之前公开的 IP 和端口:

curl http://"$(minikube ip):$NODE_PORT"

这证明了集群外部已经不再可以访问应用。 你可以通过在 Pod 内部运行 curl 确认应用仍在运行:

kubectl exec -ti $POD_NAME -- curl http://localhost:8080

这里我们看到应用是运行状态。这是因为 Deployment 正在管理应用。 要关闭应用,你还需要删除 Deployment。

准备好之后,继续学习运行应用的多个实例

5.2.4.2 - 交互式教程 - 暴露你的应用

内容不可用

暴露应用的交互式教程不可用。 有关更多信息,请参阅停服公告

5.2.5 - 扩缩你的应用

5.2.5.1 - 运行应用程序的多个实例

使用 kubectl 扩缩现有的应用程序

目标

  • 用 kubectl 扩缩应用程序

扩缩应用程序

之前我们创建了一个 Deployment, 然后通过 Service 让其可以公开访问。 Deployment 仅创建了一个 Pod 用于运行这个应用程序。当流量增加时,我们需要扩容应用程序满足用户需求。

如果你还没有学习过之前的章节, 从 使用 Minikube 创建集群 开始。

扩缩 是通过改变 Deployment 中的副本数量来实现的。

小结:

  • 扩缩一个 Deployment

通过在使用 kubectl create deployment 命令时设置 --replicas 参数,你可以在启动 Deployment 时创建多个实例。


扩缩概述


对 Deployment 横向扩容将保证新的 Pod 被创建并调度到有可用资源的 Node 上,扩容会将 Pod 数量增加至新的预期状态。 Kubernetes 还支持 Pod 的自动扩缩容, 但这并不在本教程的讨论范围内。 将 Pods 数量收缩到 0 也是可以的,这会终止指定 Deployment 上所有的 Pod。

运行应用程序的多个实例,需要有方法在它们之间分配流量。Service 有一个集成的负载均衡器, 将网络流量分配到一个可公开访问的 Deployment 的所有 Pod 上。 服务将会一直通过端点来监视 Pod 的运行,保证流量只分配到可用的 Pod 上。

扩缩是通过改变 Deployment 中的副本数量来实现的。


一旦有了多个应用实例,就可以进行滚动更新而无需停机。我们将会在教程的下一节介绍这些。 现在让我们进入终端,来扩缩我们的应用程序。


扩容 Deployment

要列出你的 Deployment,使用 get deployments 子命令:

kubectl get deployments

输出应该类似于:

NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
kubernetes-bootcamp   1/1     1            1           11m

我们应该有 1 个 Pod。如果没有,请再次运行该命令。结果显示:

  • NAME 列出 Deployment 在集群中的名称。
  • READY 显示当前/预期(CURRENT/DESIRED)副本数的比例。
  • UP-TO-DATE 显示为了达到预期状态,而被更新的副本的数量。
  • AVAILABLE 显示应用程序有多少个副本对你的用户可用。
  • AGE 显示应用程序的运行时间。

要查看由 Deployment 创建的 ReplicaSet,运行:

kubectl get rs

注意 ReplicaSet 名称总是遵循 [DEPLOYMENT-NAME]-[RANDOM-STRING] 的格式。 随机字符串是使用 pod-template-hash 作为种子随机生成的。

该输出有两个重要的列是:

  • DESIRED 显示了应用程序的预期副本数量,这是你在创建 Deployment 时定义的。这就是预期状态(desired state)。
  • CURRENT 显示了当前正在运行的副本数量。

接下来,让我们扩容 Deployment 到 4 个副本。 我们将使用 kubectl scale 命令,后面给出 Deployment 类型、名称和预期的实例数量:

kubectl scale deployments/kubernetes-bootcamp --replicas=4

要再次列举出你的 Deployment,使用 get deployments

kubectl get deployments

更改已应用,我们有 4 个应用程序实例可用。接下来,让我们检查 Pod 的数量是否发生变化:

kubectl get pods -o wide

现在有 4 个 Pod,各有不同的 IP 地址。这一变化会记录到 Deployment 的事件日志中。 要检查这一点,请使用 describe 子命令:

kubectl describe deployments/kubernetes-bootcamp

你还可以从该命令的输出中看到,现在有 4 个副本。

负载均衡

让我们来检查 Service 是否在进行流量负载均衡。要查找对外公开的 IP 和端口, 我们可以使用在教程之前部份学到的 describe services

kubectl describe services/kubernetes-bootcamp

export NODE_PORT="$(kubectl get services/kubernetes-bootcamp -o go-template='{{(index .spec.ports 0).nodePort}}')"

echo NODE_PORT=$NODE_PORT

接下来,我们将使用 curl 访问对外公开的 IP 和端口。多次执行以下命令:

curl http://"$(minikube ip):$NODE_PORT"

我们每个请求都命中了不同的 Pod,这证明负载均衡正在工作。

缩容

要将 Deployment 缩容到 2 个副本,请再次运行 scale 子命令:

kubectl scale deployments/kubernetes-bootcamp --replicas=2

要列举 Deployment 以检查是否应用了更改,使用 get deployments 子命令:

kubectl get deployments

副本数量减少到了 2 个,要列出 Pod 的数量,使用 get pods

kubectl get pods -o wide

这证实了有 2 个 Pod 被终止。

准备好之后,继续学习执行滚动更新

5.2.5.2 - 交互教程 - 缩放你的应用

内容不可用

缩放集群中所运行应用的交互式教程不可用。 有关更多信息,请参阅停服公告

5.2.6 - 更新你的应用

5.2.6.1 - 执行滚动更新

使用 kubectl 执行滚动更新。

目标

  • 使用 kubectl 执行滚动更新。

更新应用程序

用户希望应用程序始终可用,而开发人员则需要每天多次部署它们的新版本。 在 Kubernetes 中,这些是通过滚动更新(Rolling Updates)完成的。 滚动更新 允许通过使用新的实例逐步更新 Pod 实例,实现零停机的 Deployment 更新。 新的 Pod 将被调度到具有可用资源的节点上。

在前面的模块中,我们将应用程序扩展为运行多个实例。这是在不影响应用程序可用性的情况下执行更新的要求。 默认情况下,更新期间不可用的 pod 的最大值和可以创建的新 pod 数都是 1。这两个选项都可以配置为(pod)数字或百分比。 在 Kubernetes 中,更新是经过版本控制的,任何 Deployment 更新都可以恢复到以前的(稳定)版本。

摘要:

  • 更新应用

滚动更新通过逐步更新 Pod 实例并替换为新的实例,从而允许在 Deployment 更新过程中实现零停机。


滚动更新概述


与应用程序扩展类似,如果 Deployment 是公开的,Service 在更新期间仅将流量负载均衡到可用的 Pod。 可用的 Pod 是指应用程序对于用户可用的实例。

滚动更新允许以下操作:

  • 将应用程序从一个环境升级到另一个环境(通过容器镜像更新)
  • 回滚到以前的版本
  • 持续集成和持续交付应用程序,无需停机

如果 Deployment 是公开的,Service 在更新期间仅将流量负载均衡到可用的 Pod。


在下面的交互式教程中,我们将应用程序更新为新版本,并执行回滚。


更新应用的版本

要列举 Deployment,请运行 get deployments 子命令:

kubectl get deployments

要列举运行中的 Pod,请运行 get pods 子命令:

kubectl get pods

要查看应用程序当前的版本,请运行 describe pods 子命令, 然后查找 Image 字段:

kubectl describe pods

要更新应用程序的镜像版本到 v2,请使用 set image 子命令,后面给出 Deployment 名称和新的镜像版本:

kubectl set image deployments/kubernetes-bootcamp kubernetes-bootcamp=jocatalin/kubernetes-bootcamp:v2

此命令通知 Deployment 为应用程序使用不同的镜像,并启动滚动更新。 要检查新 Pod 的状态,并查看旧 Pod 的终止状况,请使用 get pods 子命令:

kubectl get pods

第二步:验证更新

首先,检查应用是否正在运行。要查找应用暴露的 IP 地址和端口,请运行 describe services 子命令:

kubectl describe services/kubernetes-bootcamp

创建名为 NODE_PORT 的环境变量,值为已被分配的 Node 端口:

export NODE_PORT="$(kubectl get services/kubernetes-bootcamp -o go-template='{{(index .spec.ports 0).nodePort}}')"
echo "NODE_PORT=$NODE_PORT"

接下来,针对所暴露的 IP 和端口执行 curl

curl http://"$(minikube ip):$NODE_PORT"

你每次执行 curl 命令,都会命中不同的 Pod。注意现在所有的 Pod 都运行着最新版本(v2)。

你也可以通过运行 rollout status 来确认此次更新:

kubectl rollout status deployments/kubernetes-bootcamp

要查看应用程序当前的镜像版本,请运行 describe pods 子命令:

kubectl describe pods

在输出的 Image 字段,确认你正在运行最新的版本(v2)。

回滚更新

让我们执行另一次更新,并尝试部署一个标记为 v10 的镜像:

kubectl set image deployments/kubernetes-bootcamp kubernetes-bootcamp=gcr.io/google-samples/kubernetes-bootcamp:v10

使用 get deployments 来查看 Deployment 的状态:

kubectl get deployments

注意,这里的输出列出的可用 Pod 数量不符合预期。 运行 get pods 子命令来列举所有的 Pod:

kubectl get pods

注意此处部分 Pod 的状态是 ImagePullBackOff

要深入了解此问题,请运行 describe pods 子命令:

kubectl describe pods

在受影响的 Pod 的 Events 部分, 可以注意到镜像的 v10 版本在仓库中不存在。

要回滚 Deployment 到上一个工作的版本,请使用 rollout undo 子命令:

kubectl rollout undo deployments/kubernetes-bootcamp

rollout undo 命令会恢复 Deployment 到先前的已知状态(v2 的镜像)。 更新是受版本控制的,你可以恢复 Deployment 到任何先前已知状态。

使用 get pods 子命令再次列举 Pod:

kubectl get pods

有四个 Pod 正在运行。要检查这些 Pod 部署的镜像, 请使用 describe pods 子命令:

kubectl describe pods

Deployment 再次使用了稳定版本的应用程序(v2)。回滚成功。

记得清理本地集群:

kubectl delete deployments/kubernetes-bootcamp services/kubernetes-bootcamp

5.2.6.2 - 交互式教程 - 更新你的应用

要与终端交互,请使用桌面/平板电脑版本

5.3 - 配置

5.3.1 - 示例:配置 java 微服务

5.3.1.1 - 使用 MicroProfile、ConfigMaps、Secrets 实现外部化应用配置

在本教程中,你会学到如何以及为什么要实现外部化微服务应用配置。 具体来说,你将学习如何使用 Kubernetes ConfigMaps 和 Secrets 设置环境变量, 然后在 MicroProfile config 中使用它们。

准备开始

创建 Kubernetes ConfigMaps 和 Secrets

在 Kubernetes 中,为 docker 容器设置环境变量有几种不同的方式,比如: Dockerfile、kubernetes.yml、Kubernetes ConfigMaps、和 Kubernetes Secrets。 在本教程中,你将学到怎么用后两个方式去设置你的环境变量,而环境变量的值将注入到你的微服务里。 使用 ConfigMaps 和 Secrets 的一个好处是他们能在多个容器间复用, 比如赋值给不同的容器中的不同环境变量。

ConfigMaps 是存储非机密键值对的 API 对象。 在互动教程中,你会学到如何用 ConfigMap 来保存应用名字。 ConfigMap 的更多信息,你可以在这里找到文档。

Secrets 尽管也用来存储键值对,但区别于 ConfigMaps 的是:它针对机密/敏感数据,且存储格式为 Base64 编码。 secrets 的这种特性使得它适合于存储证书、密钥、令牌,上述内容你将在交互教程中实现。 Secrets 的更多信息,你可以在这里找到文档。

从代码外部化配置

外部化应用配置之所以有用处,是因为配置常常根据环境的不同而变化。 为了实现此功能,我们用到了 Java 上下文和依赖注入(Contexts and Dependency Injection, CDI)、MicroProfile 配置。 MicroProfile config 是 MicroProfile 的功能特性, 是一组开放 Java 技术,用于开发、部署云原生微服务。

CDI 提供一套标准的依赖注入能力,使得应用程序可以由相互协作的、松耦合的 beans 组装而成。 MicroProfile Config 为 app 和微服务提供从各种来源,比如应用、运行时、环境,获取配置参数的标准方法。 基于来源定义的优先级,属性可以自动的合并到单独一组应用可以通过 API 访问到的属性。 CDI & MicroProfile 都会被用在互动教程中, 用来从 Kubernetes ConfigMaps 和 Secrets 获得外部提供的属性,并注入应用程序代码中。

很多开源框架、运行时支持 MicroProfile Config。 对于整个互动教程,你都可以使用开放的库、灵活的开源 Java 运行时,去构建并运行云原生的 apps 和微服务。 然而,任何 MicroProfile 兼容的运行时都可以用来做替代品。

教程目标

  • 创建 Kubernetes ConfigMap 和 Secret
  • 使用 MicroProfile Config 注入微服务配置

示例:使用 MicroProfile、ConfigMaps、Secrets 实现外部化应用配置

启动互动教程

5.3.1.2 - 互动教程 - 配置 java 微服务

如需要与终端交互,请使用台式机/平板电脑版

5.3.2 - 使用 ConfigMap 来配置 Redis

这篇文档基于配置 Pod 以使用 ConfigMap 这个任务,提供了一个使用 ConfigMap 来配置 Redis 的真实案例。

教程目标

  • 使用 Redis 配置的值创建一个 ConfigMap
  • 创建一个 Redis Pod,挂载并使用创建的 ConfigMap
  • 验证配置已经被正确应用

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

真实世界的案例:使用 ConfigMap 来配置 Redis

按照下面的步骤,使用 ConfigMap 中的数据来配置 Redis 缓存。

首先创建一个配置模块为空的 ConfigMap:

cat <<EOF >./example-redis-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: example-redis-config
data:
  redis-config: ""
EOF

应用上面创建的 ConfigMap 以及 Redis Pod 清单:

kubectl apply -f example-redis-config.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/pods/config/redis-pod.yaml

检查 Redis pod 清单的内容,并注意以下几点:

  • spec.volumes[1] 创建一个名为 config 的卷。
  • spec.volumes[1].configMap.items[0] 下的 keypath 会将来自 example-redis-config ConfigMap 中的 redis-config 键公开在 config 卷上一个名为 redis.conf 的文件中。
  • 然后 config 卷被 spec.containers[0].volumeMounts[1] 挂载在 /redis-master

这样做的最终效果是将上面 example-redis-config 配置中 data.redis-config 的数据作为 Pod 中的 /redis-master/redis.conf 公开。

apiVersion: v1
kind: Pod
metadata:
  name: redis
spec:
  containers:
  - name: redis
    image: redis:5.0.4
    command:
      - redis-server
      - "/redis-master/redis.conf"
    env:
    - name: MASTER
      value: "true"
    ports:
    - containerPort: 6379
    resources:
      limits:
        cpu: "0.1"
    volumeMounts:
    - mountPath: /redis-master-data
      name: data
    - mountPath: /redis-master
      name: config
  volumes:
    - name: data
      emptyDir: {}
    - name: config
      configMap:
        name: example-redis-config
        items:
        - key: redis-config
          path: redis.conf

检查创建的对象:

kubectl get pod/redis configmap/example-redis-config 

你应该可以看到以下输出:

NAME        READY   STATUS    RESTARTS   AGE
pod/redis   1/1     Running   0          8s

NAME                             DATA   AGE
configmap/example-redis-config   1      14s

回顾一下,我们在 example-redis-config ConfigMap 保留了空的 redis-config 键:

kubectl describe configmap/example-redis-config

你应该可以看到一个空的 redis-config 键:

Name:         example-redis-config
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
redis-config:

使用 kubectl exec 进入 pod,运行 redis-cli 工具检查当前配置:

kubectl exec -it redis -- redis-cli

查看 maxmemory

127.0.0.1:6379> CONFIG GET maxmemory

它应该显示默认值 0:

1) "maxmemory"
2) "0"

同样,查看 maxmemory-policy

127.0.0.1:6379> CONFIG GET maxmemory-policy

它也应该显示默认值 noeviction

1) "maxmemory-policy"
2) "noeviction"

现在,向 example-redis-config ConfigMap 添加一些配置:

apiVersion: v1
kind: ConfigMap
metadata:
  name: example-redis-config
data:
  redis-config: |
    maxmemory 2mb
    maxmemory-policy allkeys-lru    

应用更新的 ConfigMap:

kubectl apply -f example-redis-config.yaml

确认 ConfigMap 已更新:

kubectl describe configmap/example-redis-config

你应该可以看到我们刚刚添加的配置:

Name:         example-redis-config
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
redis-config:
----
maxmemory 2mb
maxmemory-policy allkeys-lru

通过 kubectl exec 使用 redis-cli 再次检查 Redis Pod,查看是否已应用配置:

kubectl exec -it redis -- redis-cli

查看 maxmemory

127.0.0.1:6379> CONFIG GET maxmemory

它保持默认值 0:

1) "maxmemory"
2) "0"

同样,maxmemory-policy 保留为默认设置 noeviction

127.0.0.1:6379> CONFIG GET maxmemory-policy

返回:

1) "maxmemory-policy"
2) "noeviction"

配置值未更改,因为需要重新启动 Pod 才能从关联的 ConfigMap 中获取更新的值。 让我们删除并重新创建 Pod:

kubectl delete pod redis
kubectl apply -f https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/pods/config/redis-pod.yaml

现在,最后一次重新检查配置值:

kubectl exec -it redis -- redis-cli

查看 maxmemory

127.0.0.1:6379> CONFIG GET maxmemory

现在,它应该返回更新后的值 2097152:

1) "maxmemory"
2) "2097152"

同样,maxmemory-policy 也已更新:

127.0.0.1:6379> CONFIG GET maxmemory-policy

现在它反映了期望值 allkeys-lru

1) "maxmemory-policy"
2) "allkeys-lru"

删除创建的资源,清理你的工作:

kubectl delete pod/redis configmap/example-redis-config

接下来

5.4 - 安全

对于运行 Kubernetes 集群的大多数组织和人员来说,安全是一个重要问题。 你可以在 Kubernetes 文档的其他地方找到基本的安全检查清单

要了解如何部署和管理 Kubernetes 的安全的方方面面,你可以按照本部分中的教程进行操作。

5.4.1 - 在集群级别应用 Pod 安全标准

Pod 安全是一个准入控制器,当新的 Pod 被创建时,它会根据 Kubernetes Pod 安全标准 进行检查。这是在 v1.25 中达到正式发布(GA)的功能。 本教程将向你展示如何在集群级别实施 baseline Pod 安全标准, 该标准将标准配置应用于集群中的所有名字空间。

要将 Pod 安全标准应用于特定名字空间, 请参阅在名字空间级别应用 Pod 安全标准

如果你正在运行 v1.30 以外的 Kubernetes 版本, 请查阅该版本的文档。

准备开始

在你的工作站中安装以下内容:

本教程演示了你可以对完全由你控制的 Kubernetes 集群所配置的内容。 如果你正在学习如何为一个无法配置控制平面的托管集群配置 Pod 安全准入, 请参阅在名字空间级别应用 Pod 安全标准

正确选择要应用的 Pod 安全标准

Pod 安全准入 允许你使用以下模式应用内置的 Pod 安全标准enforceauditwarn

要收集信息以便选择最适合你的配置的 Pod 安全标准,请执行以下操作:

  1. 创建一个没有应用 Pod 安全标准的集群:

    kind create cluster --name psa-wo-cluster-pss
    

    输出类似于:

    Creating cluster "psa-wo-cluster-pss" ...
    ✓ Ensuring node image (kindest/node:v1.30.0) 🖼
    ✓ Preparing nodes 📦
    ✓ Writing configuration 📜
    ✓ Starting control-plane 🕹️
    ✓ Installing CNI 🔌
    ✓ Installing StorageClass 💾
    Set kubectl context to "kind-psa-wo-cluster-pss"
    You can now use your cluster with:
    
    kubectl cluster-info --context kind-psa-wo-cluster-pss
    
    Thanks for using kind! 😊
    
  1. 将 kubectl 上下文设置为新集群:

    kubectl cluster-info --context kind-psa-wo-cluster-pss
    

    输出类似于:

    Kubernetes control plane is running at https://127.0.0.1:61350
    
    CoreDNS is running at https://127.0.0.1:61350/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
    
    To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
    
  1. 获取集群中的名字空间列表:

    kubectl get ns
    

    输出类似于:

    NAME                 STATUS   AGE
    default              Active   9m30s
    kube-node-lease      Active   9m32s
    kube-public          Active   9m32s
    kube-system          Active   9m32s
    local-path-storage   Active   9m26s
    
  1. 使用 --dry-run=server 来了解应用不同的 Pod 安全标准时会发生什么:

    1. Privileged

      kubectl label --dry-run=server --overwrite ns --all \
      pod-security.kubernetes.io/enforce=privileged
      

      输出类似于:

      namespace/default labeled
      namespace/kube-node-lease labeled
      namespace/kube-public labeled
      namespace/kube-system labeled
      namespace/local-path-storage labeled
      
    2. Baseline

      kubectl label --dry-run=server --overwrite ns --all \
      pod-security.kubernetes.io/enforce=baseline
      

      输出类似于:

      namespace/default labeled
      namespace/kube-node-lease labeled
      namespace/kube-public labeled
      Warning: existing pods in namespace "kube-system" violate the new PodSecurity enforce level "baseline:latest"
      Warning: etcd-psa-wo-cluster-pss-control-plane (and 3 other pods): host namespaces, hostPath volumes
      Warning: kindnet-vzj42: non-default capabilities, host namespaces, hostPath volumes
      Warning: kube-proxy-m6hwf: host namespaces, hostPath volumes, privileged
      namespace/kube-system labeled
      namespace/local-path-storage labeled
      
    3. Restricted

      kubectl label --dry-run=server --overwrite ns --all \
      pod-security.kubernetes.io/enforce=restricted
      

      输出类似于:

      namespace/default labeled
      namespace/kube-node-lease labeled
      namespace/kube-public labeled
      Warning: existing pods in namespace "kube-system" violate the new PodSecurity enforce level "restricted:latest"
      Warning: coredns-7bb9c7b568-hsptc (and 1 other pod): unrestricted capabilities, runAsNonRoot != true, seccompProfile
      Warning: etcd-psa-wo-cluster-pss-control-plane (and 3 other pods): host namespaces, hostPath volumes, allowPrivilegeEscalation != false, unrestricted capabilities, restricted volume types, runAsNonRoot != true
      Warning: kindnet-vzj42: non-default capabilities, host namespaces, hostPath volumes, allowPrivilegeEscalation != false, unrestricted capabilities, restricted volume types, runAsNonRoot != true, seccompProfile
      Warning: kube-proxy-m6hwf: host namespaces, hostPath volumes, privileged, allowPrivilegeEscalation != false, unrestricted capabilities, restricted volume types, runAsNonRoot != true, seccompProfile
      namespace/kube-system labeled
      Warning: existing pods in namespace "local-path-storage" violate the new PodSecurity enforce level "restricted:latest"
      Warning: local-path-provisioner-d6d9f7ffc-lw9lh: allowPrivilegeEscalation != false, unrestricted capabilities, runAsNonRoot != true, seccompProfile
      namespace/local-path-storage labeled
      

从前面的输出中,你会注意到应用 privileged Pod 安全标准不会显示任何名字空间的警告。 然而,baselinerestricted 标准都有警告,特别是在 kube-system 名字空间中。

设置模式、版本和标准

在本节中,你将以下 Pod 安全标准应用于最新(latest)版本:

  • enforce 模式下的 baseline 标准。
  • warnaudit 模式下的 restricted 标准。

baseline Pod 安全标准提供了一个方便的中间立场,能够保持豁免列表简短并防止已知的特权升级。

此外,为了防止 kube-system 中的 Pod 失败,你将免除该名字空间应用 Pod 安全标准。

在你自己的环境中实施 Pod 安全准入时,请考虑以下事项:

  1. 根据应用于集群的风险状况,更严格的 Pod 安全标准(如 restricted)可能是更好的选择。

  2. kube-system 名字空间进行赦免会允许 Pod 在其中以 privileged 模式运行。 对于实际使用,Kubernetes 项目强烈建议你应用严格的 RBAC 策略来限制对 kube-system 的访问, 遵循最小特权原则。

  3. 创建一个配置文件,Pod 安全准入控制器可以使用该文件来实现这些 Pod 安全标准:

    mkdir -p /tmp/pss
    cat <<EOF > /tmp/pss/cluster-level-pss.yaml
    apiVersion: apiserver.config.k8s.io/v1
    kind: AdmissionConfiguration
    plugins:
    - name: PodSecurity
      configuration:
        apiVersion: pod-security.admission.config.k8s.io/v1
        kind: PodSecurityConfiguration
        defaults:
          enforce: "baseline"
          enforce-version: "latest"
          audit: "restricted"
          audit-version: "latest"
          warn: "restricted"
          warn-version: "latest"
        exemptions:
          usernames: []
          runtimeClasses: []
          namespaces: [kube-system]
    EOF
    
  1. 在创建集群时配置 API 服务器使用此文件:

    cat <<EOF > /tmp/pss/cluster-config.yaml
    kind: Cluster
    apiVersion: kind.x-k8s.io/v1alpha4
    nodes:
    - role: control-plane
      kubeadmConfigPatches:
      - |
        kind: ClusterConfiguration
        apiServer:
            extraArgs:
              admission-control-config-file: /etc/config/cluster-level-pss.yaml
            extraVolumes:
              - name: accf
                hostPath: /etc/config
                mountPath: /etc/config
                readOnly: false
                pathType: "DirectoryOrCreate"
      extraMounts:
      - hostPath: /tmp/pss
        containerPath: /etc/config
        # optional: if set, the mount is read-only.
        # default false
        readOnly: false
        # optional: if set, the mount needs SELinux relabeling.
        # default false
        selinuxRelabel: false
        # optional: set propagation mode (None, HostToContainer or Bidirectional)
        # see https://kubernetes.io/docs/concepts/storage/volumes/#mount-propagation
        # default None
        propagation: None
    EOF
    
  1. 创建一个使用 Pod 安全准入的集群来应用这些 Pod 安全标准:

    kind create cluster --name psa-with-cluster-pss --config /tmp/pss/cluster-config.yaml
    

    输出类似于:

    Creating cluster "psa-with-cluster-pss" ...
     ✓ Ensuring node image (kindest/node:v1.30.0) 🖼
     ✓ Preparing nodes 📦
     ✓ Writing configuration 📜
     ✓ Starting control-plane 🕹️
     ✓ Installing CNI 🔌
     ✓ Installing StorageClass 💾
    Set kubectl context to "kind-psa-with-cluster-pss"
    You can now use your cluster with:
    
    kubectl cluster-info --context kind-psa-with-cluster-pss
    
    Have a question, bug, or feature request? Let us know! https://kind.sigs.k8s.io/#community 🙂
    
  1. 将 kubectl 指向集群:

    kubectl cluster-info --context kind-psa-with-cluster-pss
    

    输出类似于:

    Kubernetes control plane is running at https://127.0.0.1:63855
    CoreDNS is running at https://127.0.0.1:63855/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
    
    To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
    
  1. 在 default 名字空间下创建一个 Pod:

    apiVersion: v1
    kind: Pod
    metadata:
      name: nginx
    spec:
      containers:
        - image: nginx
          name: nginx
          ports:
            - containerPort: 80
    
    kubectl apply -f https://k8s.io/examples/security/example-baseline-pod.yaml
    

    这个 Pod 正常启动,但输出包含警告:

    Warning: would violate PodSecurity "restricted:latest": allowPrivilegeEscalation != false (container "nginx" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container "nginx" must set securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (pod or container "nginx" must set securityContext.runAsNonRoot=true), seccompProfile (pod or container "nginx" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")
    pod/nginx created
    

清理

现在通过运行以下命令删除你上面创建的集群:

kind delete cluster --name psa-with-cluster-pss
kind delete cluster --name psa-wo-cluster-pss

接下来

  • 运行一个 shell 脚本 一次执行前面的所有步骤:
    1. 创建一个基于 Pod 安全标准的集群级别配置
    2. 创建一个文件让 API 服务器消费这个配置
    3. 创建一个集群,用这个配置创建一个 API 服务器
    4. 设置 kubectl 上下文为这个新集群
    5. 创建一个最小的 Pod yaml 文件
    6. 应用这个文件,在新集群中创建一个 Pod
  • Pod 安全准入
  • Pod 安全标准
  • 在名字空间级别应用 Pod 安全标准

5.4.2 - 在名字空间级别应用 Pod 安全标准

Pod Security Admission 是一个准入控制器,在创建 Pod 时应用 Pod 安全标准。 这是在 v1.25 中达到正式发布(GA)的功能。 在本教程中,你将应用 baseline Pod 安全标准,每次一个名字空间。

你还可以在集群级别一次将 Pod 安全标准应用于多个名称空间。 有关说明,请参阅在集群级别应用 Pod 安全标准

准备开始

在你的工作站中安装以下内容:

创建集群

  1. 按照如下方式创建一个 kind 集群:

    kind create cluster --name psa-ns-level
    

    输出类似于:

    Creating cluster "psa-ns-level" ...
     ✓ Ensuring node image (kindest/node:v1.30.0) 🖼 
     ✓ Preparing nodes 📦  
     ✓ Writing configuration 📜 
     ✓ Starting control-plane 🕹️ 
     ✓ Installing CNI 🔌 
     ✓ Installing StorageClass 💾 
    Set kubectl context to "kind-psa-ns-level"
    You can now use your cluster with:
    
    kubectl cluster-info --context kind-psa-ns-level
    
    Not sure what to do next? 😅  Check out https://kind.sigs.k8s.io/docs/user/quick-start/
    
  1. 将 kubectl 上下文设置为新集群:

    kubectl cluster-info --context kind-psa-ns-level
    

    输出类似于:

    Kubernetes control plane is running at https://127.0.0.1:50996
    CoreDNS is running at https://127.0.0.1:50996/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
    
    To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
    

创建名字空间

创建一个名为 example 的新名字空间:

kubectl create ns example

输出类似于:

namespace/example created

为该命名空间启用 Pod 安全标准检查

  1. 使用内置 Pod 安全准入所支持的标签在此名字空间上启用 Pod 安全标准。 在这一步中,我们将根据最新版本(默认值)对基线 Pod 安全标准发出警告。

    kubectl label --overwrite ns example \
       pod-security.kubernetes.io/warn=baseline \
       pod-security.kubernetes.io/warn-version=latest
    
  1. 你可以使用标签在任何名字空间上配置多个 Pod 安全标准检查。 以下命令将强制(enforce) 执行基线(baseline)Pod 安全标准, 但根据最新版本(默认值)对受限(restricted)Pod 安全标准执行警告(warn)和审核(audit)。

    kubectl label --overwrite ns example \
      pod-security.kubernetes.io/enforce=baseline \
      pod-security.kubernetes.io/enforce-version=latest \
      pod-security.kubernetes.io/warn=restricted \
      pod-security.kubernetes.io/warn-version=latest \
      pod-security.kubernetes.io/audit=restricted \
      pod-security.kubernetes.io/audit-version=latest
    

验证 Pod 安全标准

  1. example 名字空间中创建一个基线 Pod:

    kubectl apply -n example -f https://k8s.io/examples/security/example-baseline-pod.yaml
    

    Pod 确实启动正常;输出包括一条警告信息。例如:

    Warning: would violate PodSecurity "restricted:latest": allowPrivilegeEscalation != false (container "nginx" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container "nginx" must set securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (pod or container "nginx" must set securityContext.runAsNonRoot=true), seccompProfile (pod or container "nginx" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")
    pod/nginx created
    
  1. default 名字空间中创建一个基线 Pod:

    kubectl apply -n default -f https://k8s.io/examples/security/example-baseline-pod.yaml
    

    输出类似于:

    pod/nginx created
    

Pod 安全标准实施和警告设置仅被应用到 example 名字空间。 以上 Pod 安全标准仅被应用到 example 名字空间。 你可以在没有警告的情况下在 default 名字空间中创建相同的 Pod。

清理

现在通过运行以下命令删除你上面创建的集群:

kind delete cluster --name psa-ns-level

接下来

5.4.3 - 使用 AppArmor 限制容器对资源的访问

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

AppArmor 是一个 Linux 内核安全模块, 它补充了基于标准 Linux 用户和组的权限,将程序限制在一组有限的资源中。 AppArmor 可以配置为任何应用程序减少潜在的攻击面,并且提供更加深入的防御。 它通过调整配置文件进行配置,以允许特定程序或容器所需的访问, 如 Linux 权能字、网络访问、文件权限等。 每个配置文件都可以在 强制(enforcing) 模式(阻止访问不允许的资源)或 投诉(complain) 模式(仅报告冲突)下运行。

在 Kubernetes 中,AppArmor 可以通过限制允许容器执行的操作, 和/或通过系统日志提供更好的审计来帮助你运行更安全的部署。 但是,重要的是要记住 AppArmor 不是灵丹妙药, 只能做部分事情来防止应用程序代码中的漏洞。 提供良好的限制性配置文件,并从其他角度强化你的应用程序和集群非常重要。

教程目标

  • 查看如何在节点上加载配置文件示例
  • 了解如何在 Pod 上强制执行配置文件
  • 了解如何检查配置文件是否已加载
  • 查看违反配置文件时会发生什么
  • 查看无法加载配置文件时会发生什么

准备开始

AppArmor 是一个可选的内核模块和 Kubernetes 特性,因此请在继续之前验证你的节点是否支持它:

  1. AppArmor 内核模块已启用 —— 要使 Linux 内核强制执行 AppArmor 配置文件, 必须安装并且启动 AppArmor 内核模块。默认情况下,有几个发行版支持该模块, 如 Ubuntu 和 SUSE,还有许多发行版提供可选支持。要检查模块是否已启用,请检查 /sys/module/apparmor/parameters/enabled 文件:

    cat /sys/module/apparmor/parameters/enabled
    Y
    

    kubelet 会先验证主机上是否已启用 AppArmor,然后再接纳显式配置了 AppArmor 的 Pod。

  1. 容器运行时支持 AppArmor —— 所有常见的 Kubernetes 支持的容器运行时都应该支持 AppArmor, 包括 CRI-Ocontainerd。 请参考相应的运行时文档并验证集群是否满足使用 AppArmor 的要求。
  1. 配置文件已加载 —— 通过指定每个容器应使用的 AppArmor 配置文件, AppArmor 会被应用到 Pod 上。如果所指定的配置文件未加载到内核, kubelet 将拒绝 Pod。 通过检查 /sys/kernel/security/apparmor/profiles 文件, 可以查看节点加载了哪些配置文件。例如:

    ssh gke-test-default-pool-239f5d02-gyn2 "sudo cat /sys/kernel/security/apparmor/profiles | sort"
    
    apparmor-test-deny-write (enforce)
    apparmor-test-audit-write (enforce)
    docker-default (enforce)
    k8s-nginx (enforce)
    

    有关在节点上加载配置文件的详细信息,请参见使用配置文件设置节点

保护 Pod

AppArmor 配置文件是按逐个容器的形式来设置的。 要指定用来运行 Pod 容器的 AppArmor 配置文件,请向 Pod 的 metadata 添加注解:

container.apparmor.security.beta.kubernetes.io/<container_name>: <profile_ref>

<container_name> 的名称是配置文件所针对的容器的名称,<profile_def> 则设置要应用的配置文件。 <profile_ref> 可以是以下取值之一:

  • runtime/default 应用运行时的默认配置
  • localhost/<profile_name> 应用在主机上加载的名为 <profile_name> 的配置文件
  • unconfined 表示不加载配置文件

有关注解和配置文件名称格式的详细信息,请参阅 API 参考

要验证是否应用了配置文件, 你可以通过检查容器根进程的进程属性来检查该进程是否正在使用正确的配置文件运行:

kubectl exec <pod_name> -- cat /proc/1/attr/current

输出应如下所示:

k8s-apparmor-example-deny-write (enforce)

你还可以通过检查容器的 proc attr,直接验证容器的根进程是否以正确的配置文件运行:

kubectl exec <pod_name> -- cat /proc/1/attr/current
k8s-apparmor-example-deny-write (enforce)

举例

本例假设你已经设置了一个集群使用 AppArmor 支持。

首先,将要使用的配置文件加载到节点上,此配置文件拒绝所有文件写入:

#include <tunables/global>

profile k8s-apparmor-example-deny-write flags=(attach_disconnected) {
  #include <abstractions/base>

  file,

  # 拒绝所有文件写入
  deny /** w,
}

由于不知道 Pod 将被调度到哪里,该配置文件需要加载到所有节点上。 在本例中,我们将使用 SSH 来安装概要文件, 但是在使用配置文件设置节点中讨论了其他方法。

# 此示例假设节点名称与主机名称匹配,并且可通过 SSH 访问。
NODES=($(kubectl get nodes -o name))
for NODE in ${NODES[*]}; do ssh $NODE 'sudo apparmor_parser -q <<EOF
#include <tunables/global>

profile k8s-apparmor-example-deny-write flags=(attach_disconnected) {
  #include <abstractions/base>

  file,

  # Deny all file writes.
  deny /** w,
}
EOF'
done

接下来,运行一个带有拒绝写入配置文件的简单 “Hello AppArmor” Pod:

apiVersion: v1
kind: Pod
metadata:
  name: hello-apparmor
  annotations:
    # 告知 Kubernetes 去应用 AppArmor 配置 "k8s-apparmor-example-deny-write"。
    # 请注意,如果节点上运行的 Kubernetes 不是 1.4 或更高版本,此注解将被忽略。
    container.apparmor.security.beta.kubernetes.io/hello: localhost/k8s-apparmor-example-deny-write
spec:
  containers:
  - name: hello
    image: busybox:1.28
    command: [ "sh", "-c", "echo 'Hello AppArmor!' && sleep 1h" ]
kubectl create -f hello-apparmor.yaml

你可以通过检查其 /proc/1/attr/current 来验证容器是否确实使用该配置文件运行:

kubectl exec hello-apparmor -- cat /proc/1/attr/current

输出应该是:

k8s-apparmor-example-deny-write (enforce)

最后,你可以看到,如果你通过写入文件来违反配置文件会发生什么:

kubectl exec hello-apparmor -- touch /tmp/test
touch: /tmp/test: Permission denied
error: error executing remote command: command terminated with non-zero exit code: Error executing in Docker Container: 1

最后,看看如果你尝试指定尚未加载的配置文件会发生什么:

kubectl create -f /dev/stdin <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: hello-apparmor-2
  annotations:
    container.apparmor.security.beta.kubernetes.io/hello: localhost/k8s-apparmor-example-allow-write
spec:
  containers:
  - name: hello
    image: busybox:1.28
    command: [ "sh", "-c", "echo 'Hello AppArmor!' && sleep 1h" ]
EOF
pod/hello-apparmor-2 created

虽然 Pod 创建成功,但进一步检查会发现它陷入 pending 状态:

kubectl describe pod hello-apparmor-2
Name:          hello-apparmor-2
Namespace:     default
Node:          gke-test-default-pool-239f5d02-x1kf/10.128.0.27
Start Time:    Tue, 30 Aug 2016 17:58:56 -0700
Labels:        <none>
Annotations:   container.apparmor.security.beta.kubernetes.io/hello=localhost/k8s-apparmor-example-allow-write
Status:        Pending
...
Events:
  Type     Reason     Age              From               Message
  ----     ------     ----             ----               -------
  Normal   Scheduled  10s              default-scheduler  Successfully assigned default/hello-apparmor to gke-test-default-pool-239f5d02-x1kf
  Normal   Pulled     8s               kubelet            Successfully pulled image "busybox:1.28" in 370.157088ms (370.172701ms including waiting)
  Normal   Pulling    7s (x2 over 9s)  kubelet            Pulling image "busybox:1.28"
  Warning  Failed     7s (x2 over 8s)  kubelet            Error: failed to get container spec opts: failed to generate apparmor spec opts: apparmor profile not found k8s-apparmor-example-allow-write
  Normal   Pulled     7s               kubelet            Successfully pulled image "busybox:1.28" in 90.980331ms (91.005869ms including waiting)

事件提供错误消息及其原因,具体措辞与运行时相关:

  Warning  Failed     7s (x2 over 8s)  kubelet            Error: failed to get container spec opts: failed to generate apparmor spec opts: apparmor profile not found 

管理

使用配置文件设置节点

Kubernetes 目前不提供任何本地机制来将 AppArmor 配置文件加载到节点上。 可以通过自定义基础设施或工具(例如 Kubernetes Security Profiles Operator) 加载配置文件。

调度程序不知道哪些配置文件加载到哪个节点上,因此必须将全套配置文件加载到每个节点上。 另一种方法是为节点上的每个配置文件(或配置文件类)添加节点标签, 并使用节点选择器确保 Pod 在具有所需配置文件的节点上运行。

编写配置文件

获得正确指定的 AppArmor 配置文件可能是一件棘手的事情。幸运的是,有一些工具可以帮助你做到这一点:

  • aa-genprofaa-logprof 通过监视应用程序的活动和日志并准许它所执行的操作来生成配置文件规则。 AppArmor 文档提供了进一步的指导。
  • bane 是一个用于 Docker的 AppArmor 配置文件生成器,它使用一种简化的画像语言(profile language)。

想要调试 AppArmor 的问题,你可以检查系统日志,查看具体拒绝了什么。 AppArmor 将详细消息记录到 dmesg, 错误通常可以在系统日志中或通过 journalctl 找到。 更多详细信息参见 AppArmor 失败

API 参考

Pod 注解

指定容器将使用的配置文件:

  • 键名container.apparmor.security.beta.kubernetes.io/<container_name>, 其中 <container_name> 与 Pod 中某容器的名称匹配。 可以为 Pod 中的每个容器指定单独的配置文件。
  • 键值:对配置文件的引用,如下所述

配置文件引用

任何其他配置文件引用格式无效。

接下来

其他资源:

5.4.4 - 使用 seccomp 限制容器的系统调用

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

Seccomp 代表安全计算(Secure Computing)模式,自 2.6.12 版本以来,一直是 Linux 内核的一个特性。 它可以用来沙箱化进程的权限,限制进程从用户态到内核态的调用。 Kubernetes 能使你自动将加载到节点上的 seccomp 配置文件应用到你的 Pod 和容器。

识别你的工作负载所需要的权限是很困难的。在本篇教程中, 你将了解如何将 seccomp 配置文件加载到本地的 Kubernetes 集群中, 如何将它们应用到 Pod,以及如何开始制作只为容器进程提供必要的权限的配置文件。

教程目标

  • 了解如何在节点上加载 seccomp 配置文件
  • 了解如何将 seccomp 配置文件应用到容器上
  • 观察容器进程对系统调用的审计
  • 观察指定的配置文件缺失时的行为
  • 观察违反 seccomp 配置文件的行为
  • 了解如何创建细粒度的 seccomp 配置文件
  • 了解如何应用容器运行时所默认的 seccomp 配置文件

准备开始

为了完成本篇教程中的所有步骤,你必须安装 kindkubectl

本教程中使用的命令假设你使用 Docker 作为容器运行时。 (kind 创建的集群可以在内部使用不同的容器运行时)。 你也可以使用 Podman,但如果使用了 Podman, 你必须执行特定的指令才能顺利完成任务。

本篇教程演示的某些示例仍然是 Beta 状态(自 v1.25 起),另一些示例则仅使用 seccomp 正式发布的功能。 你应该确保,针对你使用的版本, 正确配置了集群。

本篇教程也使用了 curl 工具来下载示例到你的计算机上。 你可以使用其他自己偏好的工具来自适应这些步骤。

下载示例 seccomp 配置文件

这些配置文件的内容将在稍后进行分析, 现在先将它们下载到名为 profiles/ 的目录中,以便将它们加载到集群中。

{
    "defaultAction": "SCMP_ACT_LOG"
}

{
    "defaultAction": "SCMP_ACT_ERRNO"
}

{
    "defaultAction": "SCMP_ACT_ERRNO",
    "architectures": [
        "SCMP_ARCH_X86_64",
        "SCMP_ARCH_X86",
        "SCMP_ARCH_X32"
    ],
    "syscalls": [
        {
            "names": [
                "accept4",
                "epoll_wait",
                "pselect6",
                "futex",
                "madvise",
                "epoll_ctl",
                "getsockname",
                "setsockopt",
                "vfork",
                "mmap",
                "read",
                "write",
                "close",
                "arch_prctl",
                "sched_getaffinity",
                "munmap",
                "brk",
                "rt_sigaction",
                "rt_sigprocmask",
                "sigaltstack",
                "gettid",
                "clone",
                "bind",
                "socket",
                "openat",
                "readlinkat",
                "exit_group",
                "epoll_create1",
                "listen",
                "rt_sigreturn",
                "sched_yield",
                "clock_gettime",
                "connect",
                "dup2",
                "epoll_pwait",
                "execve",
                "exit",
                "fcntl",
                "getpid",
                "getuid",
                "ioctl",
                "mprotect",
                "nanosleep",
                "open",
                "poll",
                "recvfrom",
                "sendto",
                "set_tid_address",
                "setitimer",
                "writev"
            ],
            "action": "SCMP_ACT_ALLOW"
        }
    ]
}

执行这些命令:

mkdir ./profiles
curl -L -o profiles/audit.json https://k8s.io/examples/pods/security/seccomp/profiles/audit.json
curl -L -o profiles/violation.json https://k8s.io/examples/pods/security/seccomp/profiles/violation.json
curl -L -o profiles/fine-grained.json https://k8s.io/examples/pods/security/seccomp/profiles/fine-grained.json
ls profiles

你应该看到在最后一步的末尾列出有三个配置文件:

audit.json  fine-grained.json  violation.json

使用 kind 创建本地 Kubernetes 集群

为简单起见,kind 可用来创建加载了 seccomp 配置文件的单节点集群。 Kind 在 Docker 中运行 Kubernetes,因此集群的每个节点都是一个容器。 这允许将文件挂载到每个容器的文件系统中,类似于将文件加载到节点上。

apiVersion: kind.x-k8s.io/v1alpha4
kind: Cluster
nodes:
- role: control-plane
  extraMounts:
  - hostPath: "./profiles"
    containerPath: "/var/lib/kubelet/seccomp/profiles"

下载该示例 kind 配置,并将其保存到名为 kind.yaml 的文件中:

curl -L -O https://k8s.io/examples/pods/security/seccomp/kind.yaml

你可以通过设置节点的容器镜像来设置特定的 Kubernetes 版本。 有关此类配置的更多信息, 参阅 kind 文档中节点小节。 本篇教程假定你正在使用 Kubernetes v1.30。

作为 Beta 特性,你可以将 Kubernetes 配置为使用容器运行时默认首选的配置文件, 而不是回退到 Unconfined。 如果你想尝试,请在继续之前参阅 启用使用 RuntimeDefault 作为所有工作负载的默认 seccomp 配置文件

有了 kind 配置后,使用该配置创建 kind 集群:

kind create cluster --config=kind.yaml

新的 Kubernetes 集群准备就绪后,找出作为单节点集群运行的 Docker 容器:

docker ps

你应该看到输出中名为 kind-control-plane 的容器正在运行。 输出类似于:

CONTAINER ID        IMAGE                  COMMAND                  CREATED             STATUS              PORTS                       NAMES
6a96207fed4b        kindest/node:v1.18.2   "/usr/local/bin/entr…"   27 seconds ago      Up 24 seconds       127.0.0.1:42223->6443/tcp   kind-control-plane

如果观察该容器的文件系统, 你应该会看到 profiles/ 目录已成功加载到 kubelet 的默认 seccomp 路径中。 使用 docker exec 在 Pod 中运行命令:

# 将 6a96207fed4b 更改为你从 “docker ps” 看到的容器 ID
docker exec -it 6a96207fed4b ls /var/lib/kubelet/seccomp/profiles
audit.json  fine-grained.json  violation.json

你已验证这些 seccomp 配置文件可用于在 kind 中运行的 kubelet。

创建使用容器运行时默认 seccomp 配置文件的 Pod

大多数容器运行时都提供了一组合理的、默认被允许或默认被禁止的系统调用。 你可以通过将 Pod 或容器的安全上下文中的 seccomp 类型设置为 RuntimeDefault 来为你的工作负载采用这些默认值。

这是一个 Pod 的清单,它要求其所有容器使用 RuntimeDefault seccomp 配置文件:

apiVersion: v1
kind: Pod
metadata:
  name: default-pod
  labels:
    app: default-pod
spec:
  securityContext:
    seccompProfile:
      type: RuntimeDefault
  containers:
  - name: test-container
    image: hashicorp/http-echo:0.2.3
    args:
    - "-text=just made some more syscalls!"
    securityContext:
      allowPrivilegeEscalation: false

创建此 Pod:

kubectl apply -f https://k8s.io/examples/pods/security/seccomp/ga/default-pod.yaml
kubectl get pod default-pod

此 Pod 应该显示为已成功启动:

NAME        READY   STATUS    RESTARTS   AGE
default-pod 1/1     Running   0          20s

在进入下一节之前先删除 Pod:

kubectl delete pod default-pod --wait --now

使用 seccomp 配置文件创建 Pod 以进行系统调用审计

首先,将 audit.json 配置文件应用到新的 Pod 上,该配置文件将记录进程的所有系统调用。

这是该 Pod 的清单:

apiVersion: v1
kind: Pod
metadata:
  name: audit-pod
  labels:
    app: audit-pod
spec:
  securityContext:
    seccompProfile:
      type: Localhost
      localhostProfile: profiles/audit.json
  containers:
  - name: test-container
    image: hashicorp/http-echo:0.2.3
    args:
    - "-text=just made some syscalls!"
    securityContext:
      allowPrivilegeEscalation: false

在集群中创建 Pod:

kubectl apply -f https://k8s.io/examples/pods/security/seccomp/ga/audit-pod.yaml

此配置文件不限制任何系统调用,因此 Pod 应该成功启动。

kubectl get pod audit-pod
NAME        READY   STATUS    RESTARTS   AGE
audit-pod   1/1     Running   0          30s

为了能够与容器暴露的端点交互, 创建一个 NodePort 类型的 Service, 允许从 kind 控制平面容器内部访问端点。

kubectl expose pod audit-pod --type NodePort --port 5678

检查 Service 在节点上分配的端口。

kubectl get service audit-pod

输出类似于:

NAME        TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
audit-pod   NodePort   10.111.36.142   <none>        5678:32373/TCP   72s

现在,你可以使用 curl 从 kind 控制平面容器内部访问该端点,位于该服务所公开的端口上。 使用 docker exec 在属于该控制平面容器的容器中运行 curl 命令:

# 将 6a96207fed4b 更改为你从 “docker ps” 看到的控制平面容器 ID 和端口号 32373
docker exec -it 6a96207fed4b curl localhost:32373
just made some syscalls!

你可以看到该进程正在运行,但它实际上进行了哪些系统调用? 因为这个 Pod 在本地集群中运行,你应该能够在本地系统的 /var/log/syslog 中看到它们。 打开一个新的终端窗口并 tail 来自 http-echo 的调用的输出:

# 在你的计算机上,日志路径可能不是 "/var/log/syslog"
tail -f /var/log/syslog | grep 'http-echo'

你应该已经看到了一些由 http-echo 进行的系统调用的日志, 如果你在控制平面容器中再次运行 curl,你会看到更多的输出被写入到日志。

例如:

Jul  6 15:37:40 my-machine kernel: [369128.669452] audit: type=1326 audit(1594067860.484:14536): auid=4294967295 uid=0 gid=0 ses=4294967295 pid=29064 comm="http-echo" exe="/http-echo" sig=0 arch=c000003e syscall=51 compat=0 ip=0x46fe1f code=0x7ffc0000
Jul  6 15:37:40 my-machine kernel: [369128.669453] audit: type=1326 audit(1594067860.484:14537): auid=4294967295 uid=0 gid=0 ses=4294967295 pid=29064 comm="http-echo" exe="/http-echo" sig=0 arch=c000003e syscall=54 compat=0 ip=0x46fdba code=0x7ffc0000
Jul  6 15:37:40 my-machine kernel: [369128.669455] audit: type=1326 audit(1594067860.484:14538): auid=4294967295 uid=0 gid=0 ses=4294967295 pid=29064 comm="http-echo" exe="/http-echo" sig=0 arch=c000003e syscall=202 compat=0 ip=0x455e53 code=0x7ffc0000
Jul  6 15:37:40 my-machine kernel: [369128.669456] audit: type=1326 audit(1594067860.484:14539): auid=4294967295 uid=0 gid=0 ses=4294967295 pid=29064 comm="http-echo" exe="/http-echo" sig=0 arch=c000003e syscall=288 compat=0 ip=0x46fdba code=0x7ffc0000
Jul  6 15:37:40 my-machine kernel: [369128.669517] audit: type=1326 audit(1594067860.484:14540): auid=4294967295 uid=0 gid=0 ses=4294967295 pid=29064 comm="http-echo" exe="/http-echo" sig=0 arch=c000003e syscall=0 compat=0 ip=0x46fd44 code=0x7ffc0000
Jul  6 15:37:40 my-machine kernel: [369128.669519] audit: type=1326 audit(1594067860.484:14541): auid=4294967295 uid=0 gid=0 ses=4294967295 pid=29064 comm="http-echo" exe="/http-echo" sig=0 arch=c000003e syscall=270 compat=0 ip=0x4559b1 code=0x7ffc0000
Jul  6 15:38:40 my-machine kernel: [369188.671648] audit: type=1326 audit(1594067920.488:14559): auid=4294967295 uid=0 gid=0 ses=4294967295 pid=29064 comm="http-echo" exe="/http-echo" sig=0 arch=c000003e syscall=270 compat=0 ip=0x4559b1 code=0x7ffc0000
Jul  6 15:38:40 my-machine kernel: [369188.671726] audit: type=1326 audit(1594067920.488:14560): auid=4294967295 uid=0 gid=0 ses=4294967295 pid=29064 comm="http-echo" exe="/http-echo" sig=0 arch=c000003e syscall=202 compat=0 ip=0x455e53 code=0x7ffc0000

通过查看每一行的 syscall= 条目,你可以开始了解 http-echo 进程所需的系统调用。 虽然这些不太可能包含它使用的所有系统调用,但它可以作为此容器的 seccomp 配置文件的基础。

在转到下一节之前删除该 Service 和 Pod:

kubectl delete service audit-pod --wait
kubectl delete pod audit-pod --wait --now

使用导致违规的 seccomp 配置文件创建 Pod

出于演示目的,将配置文件应用于不允许任何系统调用的 Pod 上。

此演示的清单是:

apiVersion: v1
kind: Pod
metadata:
  name: violation-pod
  labels:
    app: violation-pod
spec:
  securityContext:
    seccompProfile:
      type: Localhost
      localhostProfile: profiles/violation.json
  containers:
  - name: test-container
    image: hashicorp/http-echo:0.2.3
    args:
    - "-text=just made some syscalls!"
    securityContext:
      allowPrivilegeEscalation: false

尝试在集群中创建 Pod:

kubectl apply -f https://k8s.io/examples/pods/security/seccomp/ga/violation-pod.yaml

Pod 已创建,但存在问题。 如果你检查 Pod 状态,你应该看到它没有启动。

kubectl get pod violation-pod
NAME            READY   STATUS             RESTARTS   AGE
violation-pod   0/1     CrashLoopBackOff   1          6s

如上例所示,http-echo 进程需要相当多的系统调用。 这里 seccomp 已通过设置 "defaultAction": "SCMP_ACT_ERRNO" 被指示为在发生任何系统调用时报错。 这是非常安全的,但消除了做任何有意义的事情的能力。 你真正想要的是只给工作负载它们所需要的权限。

在进入下一节之前删除该 Pod:

kubectl delete pod violation-pod --wait --now

使用只允许必要的系统调用的 seccomp 配置文件创建 Pod

如果你看一看 fine-grained.json 配置文件, 你会注意到第一个示例的 syslog 中看到的一些系统调用, 其中配置文件设置为 "defaultAction": "SCMP_ACT_LOG"。 现在的配置文件设置 "defaultAction": "SCMP_ACT_ERRNO", 但在 "action": "SCMP_ACT_ALLOW" 块中明确允许一组系统调用。 理想情况下,容器将成功运行,并且你看到没有消息发送到 syslog

此示例的清单是:

apiVersion: v1
kind: Pod
metadata:
  name: fine-pod
  labels:
    app: fine-pod
spec:
  securityContext:
    seccompProfile:
      type: Localhost
      localhostProfile: profiles/fine-grained.json
  containers:
  - name: test-container
    image: hashicorp/http-echo:0.2.3
    args:
    - "-text=just made some syscalls!"
    securityContext:
      allowPrivilegeEscalation: false

在你的集群中创建 Pod:

kubectl apply -f https://k8s.io/examples/pods/security/seccomp/ga/fine-pod.yaml
kubectl get pod fine-pod

此 Pod 应该显示为已成功启动:

NAME        READY   STATUS    RESTARTS   AGE
fine-pod   1/1     Running   0          30s

打开一个新的终端窗口并使用 tail 来监视提到来自 http-echo 的调用的日志条目:

# 你计算机上的日志路径可能与 “/var/log/syslog” 不同
tail -f /var/log/syslog | grep 'http-echo'

接着,使用 NodePort Service 公开 Pod:

kubectl expose pod fine-pod --type NodePort --port 5678

检查节点上的 Service 分配了什么端口:

kubectl get service fine-pod

输出类似于:

NAME        TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
fine-pod    NodePort   10.111.36.142   <none>        5678:32373/TCP   72s

使用 curl 从 kind 控制平面容器内部访问端点:

# 将 6a96207fed4b 更改为你从 “docker ps” 看到的控制平面容器 ID 和端口号 32373
docker exec -it 6a96207fed4b curl localhost:32373
just made some syscalls!

你应该在 syslog 中看不到任何输出。 这是因为配置文件允许所有必要的系统调用,并指定如果调用列表之外的系统调用应发生错误。 从安全角度来看,这是一种理想的情况,但需要在分析程序时付出一些努力。 如果有一种简单的方法可以在不需要太多努力的情况下更接近这种安全性,那就太好了。

在进入下一节之前删除该 Service 和 Pod:

kubectl delete service fine-pod --wait
kubectl delete pod fine-pod --wait --now

启用使用 RuntimeDefault 作为所有工作负载的默认 seccomp 配置文件

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

要采用为 Seccomp(安全计算模式)设置默认配置文件这一行为,你必须使用在想要启用此行为的每个节点上启用 --seccomp-default 命令行标志来运行 kubelet。

如果启用,kubelet 将会默认使用 RuntimeDefault seccomp 配置文件, (这一配置文件是由容器运行时定义的),而不是使用 Unconfined(禁用 seccomp)模式。 默认的配置文件旨在提供一组限制性较强且能保留工作负载功能的安全默认值。 不同容器运行时及其不同发布版本之间的默认配置文件可能有所不同, 例如在比较来自 CRI-O 和 containerd 的配置文件时。

与其他工作负载相比,某些工作负载可能需要更少的系统调用限制。 这意味着即使使用 RuntimeDefault 配置文件,它们也可能在运行时失败。 要应对此类故障,你可以:

  • 显式地以 Unconfined 模式运行工作负载。
  • 禁用节点的 SeccompDefault 特性。同时,确保工作负载被调度到禁用该特性的节点上。
  • 为工作负载创建自定义 seccomp 配置文件。

如果你将此特性引入到类似的生产集群中, Kubernetes 项目建议你在部分节点上启用此特性门控, 然后在整个集群范围内推出更改之前,测试工作负载执行情况。

你可以在相关的 Kubernetes 增强提案(KEP) 中找到可能的升级和降级策略的更详细信息: 默认启用 Seccomp

Kubernetes 1.30 允许你配置 Seccomp 配置文件, 当 Pod 的规约未定义特定的 Seccomp 配置文件时应用该配置文件。 但是,你仍然需要为合适的节点启用这种设置默认配置的能力。

如果你正在运行 Kubernetes 1.30 集群并希望启用该特性,请使用 --seccomp-default 命令行参数运行 kubelet, 或通过 kubelet 配置文件启用。

要在 kind 启用特性门控, 请确保 kind 提供所需的最低 Kubernetes 版本, 并在 kind 配置中 启用 SeccompDefault 特性:

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
  - role: control-plane
    image: kindest/node:v1.28.0@sha256:9f3ff58f19dcf1a0611d11e8ac989fdb30a28f40f236f59f0bea31fb956ccf5c
    kubeadmConfigPatches:
      - |
        kind: JoinConfiguration
        nodeRegistration:
          kubeletExtraArgs:
            seccomp-default: "true"        
  - role: worker
    image: kindest/node:v1.28.0@sha256:9f3ff58f19dcf1a0611d11e8ac989fdb30a28f40f236f59f0bea31fb956ccf5c
    kubeadmConfigPatches:
      - |
        kind: JoinConfiguration
        nodeRegistration:
          kubeletExtraArgs:
            seccomp-default: "true"        

如果集群已就绪,则运行一个 Pod:

kubectl run --rm -it --restart=Never --image=alpine alpine -- sh

现在默认的 seccomp 配置文件应该已经生效。 这可以通过使用 docker exec 为 kind 上的容器运行 crictl inspect 来验证:

docker exec -it kind-worker bash -c \
    'crictl inspect $(crictl ps --name=alpine -q) | jq .info.runtimeSpec.linux.seccomp'
{
  "defaultAction": "SCMP_ACT_ERRNO",
  "architectures": ["SCMP_ARCH_X86_64", "SCMP_ARCH_X86", "SCMP_ARCH_X32"],
  "syscalls": [
    {
      "names": ["..."]
    }
  ]
}

接下来

你可以了解有关 Linux seccomp 的更多信息:

5.5 - 无状态的应用

5.5.1 - 公开外部 IP 地址以访问集群中应用程序

此页面显示如何创建公开外部 IP 地址的 Kubernetes 服务对象。

准备开始

  • 安装 kubectl
  • 使用 Google Kubernetes Engine 或 Amazon Web Services 等云供应商创建 Kubernetes 集群。 本教程创建了一个外部负载均衡器, 需要云供应商。
  • 配置 kubectl 与 Kubernetes API 服务器通信。有关说明,请参阅云供应商文档。

教程目标

  • 运行 Hello World 应用程序的五个实例。
  • 创建一个公开外部 IP 地址的 Service 对象。
  • 使用 Service 对象访问正在运行的应用程序。

为一个在五个 pod 中运行的应用程序创建服务

  1. 在集群中运行 Hello World 应用程序:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      labels:
        app.kubernetes.io/name: load-balancer-example
      name: hello-world
    spec:
      replicas: 5
      selector:
        matchLabels:
          app.kubernetes.io/name: load-balancer-example
      template:
        metadata:
          labels:
            app.kubernetes.io/name: load-balancer-example
        spec:
          containers:
          - image: gcr.io/google-samples/node-hello:1.0
            name: hello-world
            ports:
            - containerPort: 8080
    
    kubectl apply -f https://k8s.io/examples/service/load-balancer-example.yaml
    

    前面的命令创建一个 Deployment 对象和一个关联的 ReplicaSet 对象。 ReplicaSet 有五个 Pod, 每个都运行 Hello World 应用程序。

  1. 显示有关 Deployment 的信息:

    kubectl get deployments hello-world
    kubectl describe deployments hello-world
    
  1. 显示有关 ReplicaSet 对象的信息:

    kubectl get replicasets
    kubectl describe replicasets
    
  1. 创建公开 Deployment 的 Service 对象:

    kubectl expose deployment hello-world --type=LoadBalancer --name=my-service
    
  1. 显示有关 Service 的信息:

    kubectl get services my-service
    

    输出类似于:

    NAME         TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)    AGE
    my-service   LoadBalancer   10.3.245.137   104.198.205.71   8080/TCP   54s
    
  1. 显示有关 Service 的详细信息:

    kubectl describe services my-service
    

    输出类似于:

    Name:           my-service
    Namespace:      default
    Labels:         app.kubernetes.io/name=load-balancer-example
    Annotations:    <none>
    Selector:       app.kubernetes.io/name=load-balancer-example
    Type:           LoadBalancer
    IP:             10.3.245.137
    LoadBalancer Ingress:   104.198.205.71
    Port:           <unset> 8080/TCP
    NodePort:       <unset> 32377/TCP
    Endpoints:      10.0.0.6:8080,10.0.1.6:8080,10.0.1.7:8080 + 2 more...
    Session Affinity:   None
    Events:         <none>
    

    记下服务公开的外部 IP 地址(LoadBalancer Ingress)。 在本例中,外部 IP 地址是 104.198.205.71。还要注意 PortNodePort 的值。 在本例中,Port 是 8080,NodePort 是 32377。

  1. 在前面的输出中,你可以看到服务有几个端点: 10.0.0.6:8080、10.0.1.6:8080、10.0.1.7:8080 和另外两个, 这些都是正在运行 Hello World 应用程序的 Pod 的内部地址。 要验证这些是 Pod 地址,请输入以下命令:

    kubectl get pods --output=wide
    

    输出类似于:

    NAME                         ...  IP         NODE
    hello-world-2895499144-1jaz9 ...  10.0.1.6   gke-cluster-1-default-pool-e0b8d269-1afc
    hello-world-2895499144-2e5uh ...  10.0.1.8   gke-cluster-1-default-pool-e0b8d269-1afc
    hello-world-2895499144-9m4h1 ...  10.0.0.6   gke-cluster-1-default-pool-e0b8d269-5v7a
    hello-world-2895499144-o4z13 ...  10.0.1.7   gke-cluster-1-default-pool-e0b8d269-1afc
    hello-world-2895499144-segjf ...  10.0.2.5   gke-cluster-1-default-pool-e0b8d269-cpuc
    
  1. 使用外部 IP 地址(LoadBalancer Ingress)访问 Hello World 应用程序:

    curl http://<external-ip>:<port>
    

    其中 <external-ip> 是你的服务的外部 IP 地址(LoadBalancer Ingress), <port> 是你的服务描述中的 port 的值。 如果你正在使用 minikube,输入 minikube service my-service 将在浏览器中自动打开 Hello World 应用程序。

    成功请求的响应是一条问候消息:

    Hello Kubernetes!
    

清理现场

要删除 Service,请输入以下命令:

kubectl delete services my-service

要删除正在运行 Hello World 应用程序的 Deployment、ReplicaSet 和 Pod,请输入以下命令:

kubectl delete deployment hello-world

接下来

进一步了解使用 Service 连接到应用

5.5.2 - 示例:使用 Redis 部署 PHP 留言板应用

本教程向你展示如何使用 Kubernetes 和 Docker 构建和部署一个简单的 (非面向生产的) 多层 Web 应用。本例由以下组件组成:

  • 单实例 Redis 以保存留言板条目
  • 多个 Web 前端实例

教程目标

  • 启动 Redis 领导者(Leader)
  • 启动两个 Redis 跟随者(Follower)
  • 公开并查看前端服务
  • 清理

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你的 Kubernetes 服务器版本必须不低于版本 v1.14. 要获知版本信息,请输入 kubectl version.

启动 Redis 数据库

留言板应用使用 Redis 存储数据。

创建 Redis Deployment

下面包含的清单文件指定了一个 Deployment 控制器,该控制器运行一个 Redis Pod 副本。

# 来源:https://cloud.google.com/kubernetes-engine/docs/tutorials/guestbook
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-leader
  labels:
    app: redis
    role: leader
    tier: backend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
        role: leader
        tier: backend
    spec:
      containers:
      - name: leader
        image: "docker.io/redis:6.0.5"
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
        ports:
        - containerPort: 6379
  1. 在下载清单文件的目录中启动终端窗口。

  2. redis-leader-deployment.yaml 文件中应用 Redis Deployment:

    kubectl apply -f https://k8s.io/examples/application/guestbook/redis-leader-deployment.yaml
    
  1. 查询 Pod 列表以验证 Redis Pod 是否正在运行:

    kubectl get pods
    

    响应应该与此类似:

    NAME                           READY   STATUS    RESTARTS   AGE
    redis-leader-fb76b4755-xjr2n   1/1     Running   0          13s
    
  1. 运行以下命令查看 Redis Deployment 中的日志:

    kubectl logs -f deployment/redis-leader
    

创建 Redis 领导者服务

留言板应用需要往 Redis 中写数据。因此,需要创建 Service 来转发 Redis Pod 的流量。Service 定义了访问 Pod 的策略。

# 来源:https://cloud.google.com/kubernetes-engine/docs/tutorials/guestbook
apiVersion: v1
kind: Service
metadata:
  name: redis-leader
  labels:
    app: redis
    role: leader
    tier: backend
spec:
  ports:
  - port: 6379
    targetPort: 6379
  selector:
    app: redis
    role: leader
    tier: backend
  1. 使用下面的 redis-leader-service.yaml 文件创建 Redis 的服务:

    kubectl apply -f https://k8s.io/examples/application/guestbook/redis-leader-service.yaml
    
  1. 查询服务列表验证 Redis 服务是否正在运行:

    kubectl get service
    

    响应应该与此类似:

    NAME           TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
    kubernetes     ClusterIP   10.0.0.1     <none>        443/TCP    1m
    redis-leader   ClusterIP   10.103.78.24 <none>        6379/TCP   16s
    

设置 Redis 跟随者

尽管 Redis 领导者只有一个 Pod,你可以通过添加若干 Redis 跟随者来将其配置为高可用状态, 以满足流量需求。

# 来源:https://cloud.google.com/kubernetes-engine/docs/tutorials/guestbook
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-follower
  labels:
    app: redis
    role: follower
    tier: backend
spec:
  replicas: 2
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
        role: follower
        tier: backend
    spec:
      containers:
      - name: follower
        image: us-docker.pkg.dev/google-samples/containers/gke/gb-redis-follower:v2
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
        ports:
        - containerPort: 6379
  1. 应用下面的 redis-follower-deployment.yaml 文件创建 Redis Deployment:

    kubectl apply -f https://k8s.io/examples/application/guestbook/redis-follower-deployment.yaml
    
  1. 通过查询 Pods 列表,验证两个 Redis 跟随者副本在运行:

    kubectl get pods
    

    响应应该类似于这样:

    NAME                             READY   STATUS    RESTARTS   AGE
    redis-follower-dddfbdcc9-82sfr   1/1     Running   0          37s
    redis-follower-dddfbdcc9-qrt5k   1/1     Running   0          38s
    redis-leader-fb76b4755-xjr2n     1/1     Running   0          11m
    

创建 Redis 跟随者服务

Guestbook 应用需要与 Redis 跟随者通信以读取数据。 为了让 Redis 跟随者可被发现,你必须创建另一个 Service

# 来源:https://cloud.google.com/kubernetes-engine/docs/tutorials/guestbook
apiVersion: v1
kind: Service
metadata:
  name: redis-follower
  labels:
    app: redis
    role: follower
    tier: backend
spec:
  ports:
    # 此服务应使用的端口
  - port: 6379
  selector:
    app: redis
    role: follower
    tier: backend
  1. 应用如下所示 redis-follower-service.yaml 文件中的 Redis Service:

    kubectl apply -f https://k8s.io/examples/application/guestbook/redis-follower-service.yaml
    
  1. 查询 Service 列表,验证 Redis 服务在运行:

    kubectl get service
    

    响应应该类似于这样:

    NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
    kubernetes       ClusterIP   10.96.0.1       <none>        443/TCP    3d19h
    redis-follower   ClusterIP   10.110.162.42   <none>        6379/TCP   9s
    redis-leader     ClusterIP   10.103.78.24    <none>        6379/TCP   6m10s
    

设置并公开留言板前端

现在你有了一个为 Guestbook 应用配置的 Redis 存储处于运行状态, 接下来可以启动 Guestbook 的 Web 服务器了。 与 Redis 跟随者类似,前端也是使用 Kubernetes Deployment 来部署的。

Guestbook 应用使用 PHP 前端。该前端被配置成与后端的 Redis 跟随者或者领导者服务通信,具体选择哪个服务取决于请求是读操作还是写操作。 前端对外暴露一个 JSON 接口,并提供基于 jQuery-Ajax 的用户体验。

创建 Guestbook 前端 Deployment

# 来源:https://cloud.google.com/kubernetes-engine/docs/tutorials/guestbook
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 3
  selector:
    matchLabels:
        app: guestbook
        tier: frontend
  template:
    metadata:
      labels:
        app: guestbook
        tier: frontend
    spec:
      containers:
      - name: php-redis
        image: us-docker.pkg.dev/google-samples/containers/gke/gb-frontend:v5
        env:
        - name: GET_HOSTS_FROM
          value: "dns"
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
        ports:
        - containerPort: 80
  1. 应用来自 frontend-deployment.yaml 文件的前端 Deployment:

    kubectl apply -f https://k8s.io/examples/application/guestbook/frontend-deployment.yaml
    
  1. 查询 Pod 列表,验证三个前端副本正在运行:

    kubectl get pods -l app=guestbook -l tier=frontend
    

    响应应该与此类似:

    NAME                        READY   STATUS    RESTARTS   AGE
    frontend-85595f5bf9-5tqhb   1/1     Running   0          47s
    frontend-85595f5bf9-qbzwm   1/1     Running   0          47s
    frontend-85595f5bf9-zchwc   1/1     Running   0          47s
    

创建前端服务

应用的 Redis 服务只能在 Kubernetes 集群中访问,因为服务的默认类型是 ClusterIPClusterIP 为服务指向的 Pod 集提供一个 IP 地址。这个 IP 地址只能在集群中访问。

如果你希望访客能够访问你的 Guestbook,你必须将前端服务配置为外部可见的, 以便客户端可以从 Kubernetes 集群之外请求服务。 然而即便使用了 ClusterIP,Kubernetes 用户仍可以通过 kubectl port-forward 访问服务。

# 来源:https://cloud.google.com/kubernetes-engine/docs/tutorials/guestbook
apiVersion: v1
kind: Service
metadata:
  name: frontend
  labels:
    app: guestbook
    tier: frontend
spec:
  # 如果你的集群支持,请取消注释以下内容以自动为前端服务创建一个外部负载均衡 IP。
  # type: LoadBalancer
  #type: LoadBalancer
  ports:
    # 此服务应使用的端口
  - port: 80
  selector:
    app: guestbook
    tier: frontend
  1. 应用来自 frontend-service.yaml 文件中的前端服务:

    kubectl apply -f https://k8s.io/examples/application/guestbook/frontend-service.yaml
    
  1. 查询 Service 列表以验证前端服务正在运行:

    kubectl get services
    

    响应应该与此类似:

    NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
    frontend         ClusterIP   10.97.28.230    <none>        80/TCP     19s
    kubernetes       ClusterIP   10.96.0.1       <none>        443/TCP    3d19h
    redis-follower   ClusterIP   10.110.162.42   <none>        6379/TCP   5m48s
    redis-leader     ClusterIP   10.103.78.24    <none>        6379/TCP   11m
    

通过 kubectl port-forward 查看前端服务

  1. 运行以下命令将本机的 8080 端口转发到服务的 80 端口。

    kubectl port-forward svc/frontend 8080:80
    

    响应应该与此类似:

    Forwarding from 127.0.0.1:8080 -> 80
    Forwarding from [::1]:8080 -> 80
    
  1. 在浏览器中加载 http://localhost:8080 页面以查看 Guestbook。

通过 LoadBalancer 查看前端服务

如果你部署了 frontend-service.yaml,需要找到用来查看 Guestbook 的 IP 地址。

  1. 运行以下命令以获取前端服务的 IP 地址。

    kubectl get service frontend
    

    响应应该与此类似:

    NAME       TYPE           CLUSTER-IP      EXTERNAL-IP        PORT(S)        AGE
    frontend   LoadBalancer   10.51.242.136   109.197.92.229     80:32372/TCP   1m
    
  1. 复制这里的外部 IP 地址,然后在浏览器中加载页面以查看留言板。

扩展 Web 前端

你可以根据需要执行伸缩操作,这是因为服务器本身被定义为使用一个 Deployment 控制器的 Service。

  1. 运行以下命令扩展前端 Pod 的数量:

    kubectl scale deployment frontend --replicas=5
    
  1. 查询 Pod 列表验证正在运行的前端 Pod 的数量:

    kubectl get pods
    

    响应应该类似于这样:

    NAME                             READY   STATUS    RESTARTS   AGE
    frontend-85595f5bf9-5df5m        1/1     Running   0          83s
    frontend-85595f5bf9-7zmg5        1/1     Running   0          83s
    frontend-85595f5bf9-cpskg        1/1     Running   0          15m
    frontend-85595f5bf9-l2l54        1/1     Running   0          14m
    frontend-85595f5bf9-l9c8z        1/1     Running   0          14m
    redis-follower-dddfbdcc9-82sfr   1/1     Running   0          97m
    redis-follower-dddfbdcc9-qrt5k   1/1     Running   0          97m
    redis-leader-fb76b4755-xjr2n     1/1     Running   0          108m
    
  1. 运行以下命令缩小前端 Pod 的数量:

    kubectl scale deployment frontend --replicas=2
    
  1. 查询 Pod 列表验证正在运行的前端 Pod 的数量:

    kubectl get pods
    

    响应应该类似于这样:

    NAME                             READY   STATUS    RESTARTS   AGE
    frontend-85595f5bf9-cpskg        1/1     Running   0          16m
    frontend-85595f5bf9-l9c8z        1/1     Running   0          15m
    redis-follower-dddfbdcc9-82sfr   1/1     Running   0          98m
    redis-follower-dddfbdcc9-qrt5k   1/1     Running   0          98m
    redis-leader-fb76b4755-xjr2n     1/1     Running   0          109m
    

清理现场

删除 Deployments 和服务还会删除正在运行的 Pod。 使用标签用一个命令删除多个资源。

  1. 运行以下命令以删除所有 Pod、Deployment 和 Service。

    kubectl delete deployment -l app=redis
    kubectl delete service -l app=redis
    kubectl delete deployment frontend
    kubectl delete service frontend
    

    响应应该是:

    deployment.apps "redis-follower" deleted
    deployment.apps "redis-leader" deleted
    deployment.apps "frontend" deleted
    service "frontend" deleted
    
  1. 查询 Pod 列表,确认没有 Pod 在运行:

    kubectl get pods
    

    响应应该是:

    No resources found in default namespace.
    

接下来

5.6 - 有状态的应用

5.6.1 - StatefulSet 基础

本教程介绍了如何使用 StatefulSet 来管理应用。 演示了如何创建、删除、扩容/缩容和更新 StatefulSet 的 Pod。

准备开始

在开始本教程之前,你应该熟悉以下 Kubernetes 的概念:

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你应该配置 kubectl 的上下文使用 default 命名空间。 如果你使用的是现有集群,请确保可以使用该集群的 default 命名空间进行练习。 理想情况下,在没有运行任何实际工作负载的集群中进行练习。

阅读有关 StatefulSet 的概念页面也很有用。

教程目标

StatefulSet 旨在与有状态的应用及分布式系统一起使用。然而在 Kubernetes 上管理有状态应用和分布式系统是一个宽泛而复杂的话题。 为了演示 StatefulSet 的基本特性,并且不使前后的主题混淆,你将会使用 StatefulSet 部署一个简单的 Web 应用。

在阅读本教程后,你将熟悉以下内容:

  • 如何创建 StatefulSet
  • StatefulSet 怎样管理它的 Pod
  • 如何删除 StatefulSet
  • 如何对 StatefulSet 进行扩容/缩容
  • 如何更新一个 StatefulSet 的 Pod

创建 StatefulSet

作为开始,使用如下示例创建一个 StatefulSet。它和 StatefulSet 概念中的示例相似。 它创建了一个 Headless Service nginx 用来发布 StatefulSet web 中的 Pod 的 IP 地址。

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: registry.k8s.io/nginx-slim:0.8
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi

你需要使用至少两个终端窗口。在第一个终端中,使用 kubectl get 来监视 StatefulSet 的 Pod 的创建情况。

# 使用此终端运行指定 --watch 的命令
# 当你被要求开始一个新的 watch 时结束这个 watch
kubectl get pods --watch -l app=nginx

在另一个终端中,使用 kubectl apply 来创建 Headless Service 和 StatefulSet。

kubectl apply -f https://k8s.io/examples/application/web/web.yaml
service/nginx created
statefulset.apps/web created

上面的命令创建了两个 Pod,每个都运行了一个 NginX Web 服务器。 获取 nginx Service:

kubectl get service nginx
NAME      TYPE         CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
nginx     ClusterIP    None         <none>        80/TCP    12s

然后获取 web StatefulSet,以验证两者均已成功创建:

kubectl get statefulset web
NAME      DESIRED   CURRENT   AGE
web       2         1         20s

顺序创建 Pod

对于一个拥有 n 个副本的 StatefulSet,Pod 被部署时是按照 {0..n-1} 的序号顺序创建的。 在第一个终端中使用 kubectl get 检查输出。这个输出最终将看起来像下面的样子。

# 不要开始一个新的 watch
# 这应该已经处于 Running 状态
kubectl get pods --watch -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     0/1       Pending   0          0s
web-0     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         19s
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         18s

请注意,直到 web-0 Pod 处于 Running(请参阅 Pod 阶段) 并 Ready(请参阅 Pod 状况中的 type)状态后,web-1 Pod 才会被启动。

StatefulSet 中的 Pod

StatefulSet 中的每个 Pod 拥有一个唯一的顺序索引和稳定的网络身份标识。

检查 Pod 的顺序索引

获取 StatefulSet 的 Pod:

kubectl get pods -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          1m
web-1     1/1       Running   0          1m

如同 StatefulSet 概念中所提到的, StatefulSet 中的每个 Pod 拥有一个具有黏性的、独一无二的身份标志。 这个标志基于 StatefulSet 控制器分配给每个 Pod 的唯一顺序索引。 Pod 名称的格式为 <statefulset 名称>-<序号索引>web StatefulSet 拥有两个副本,所以它创建了两个 Pod:web-0web-1

使用稳定的网络身份标识

每个 Pod 都拥有一个基于其顺序索引的稳定的主机名。使用 kubectl exec 在每个 Pod 中执行 hostname

for i in 0 1; do kubectl exec "web-$i" -- sh -c 'hostname'; done
web-0
web-1

使用 kubectl run 运行一个提供 nslookup 命令的容器,该命令来自于 dnsutils 包。 通过对 Pod 的主机名执行 nslookup,你可以检查这些主机名在集群内部的 DNS 地址:

kubectl run -i --tty --image busybox:1.28 dns-test --restart=Never --rm

这将启动一个新的 Shell。在新 Shell 中运行:

# 在 dns-test 容器 Shell 中运行以下命令
nslookup web-0.nginx

输出类似于:

Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-0.nginx
Address 1: 10.244.1.6

nslookup web-1.nginx
Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-1.nginx
Address 1: 10.244.2.6

(现在可以退出容器 Shell:exit

Headless service 的 CNAME 指向 SRV 记录(记录每个 Running 和 Ready 状态的 Pod)。 SRV 记录指向一个包含 Pod IP 地址的记录表项。

在一个终端中监视 StatefulSet 的 Pod:

# 启动一个新的 watch
# 当你看到删除完成后结束这个 watch
kubectl get pod --watch -l app=nginx

在另一个终端中使用 kubectl delete 删除 StatefulSet 中所有的 Pod:

kubectl delete pod -l app=nginx
pod "web-0" deleted
pod "web-1" deleted

等待 StatefulSet 重启它们,并且两个 Pod 都变成 Running 和 Ready 状态:

# 这应该已经处于 Running 状态
kubectl get pod --watch -l app=nginx
NAME      READY     STATUS              RESTARTS   AGE
web-0     0/1       ContainerCreating   0          0s
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          2s
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         34s

使用 kubectl execkubectl run 查看 Pod 的主机名和集群内部的 DNS 表项。 首先,查看 Pod 的主机名:

for i in 0 1; do kubectl exec web-$i -- sh -c 'hostname'; done
web-0
web-1

然后,运行:

kubectl run -i --tty --image busybox:1.28 dns-test --restart=Never --rm

这将启动一个新的 Shell。在新 Shell 中,运行:

# 在 dns-test 容器 Shell 中运行以下命令
nslookup web-0.nginx

输出类似于:

Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-0.nginx
Address 1: 10.244.1.7

nslookup web-1.nginx
Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-1.nginx
Address 1: 10.244.2.8

(现在可以退出容器 Shell:exit

Pod 的序号、主机名、SRV 条目和记录名称没有改变,但和 Pod 相关联的 IP 地址可能发生了改变。 在本教程中使用的集群中它们就改变了。这就是为什么不要在其他应用中使用 StatefulSet 中 Pod 的 IP 地址进行连接,这点很重要。

发现 StatefulSet 中特定的 Pod

如果你需要查找并连接一个 StatefulSet 的活动成员,你应该查询 Headless Service 的 CNAME。 和 CNAME 相关联的 SRV 记录只会包含 StatefulSet 中处于 Running 和 Ready 状态的 Pod。

如果你的应用已经实现了用于测试是否已存活(liveness)并就绪(readiness)的连接逻辑, 你可以使用 Pod 的 SRV 记录(web-0.nginx.default.svc.cluster.localweb-1.nginx.default.svc.cluster.local)。因为它们是稳定的,并且当你的 Pod 的状态变为 Running 和 Ready 时,你的应用就能够发现它们的地址。

写入稳定的存储

获取 web-0web-1 的 PersistentVolumeClaims:

kubectl get pvc -l app=nginx

输出类似于:

NAME        STATUS    VOLUME                                     CAPACITY   ACCESSMODES   AGE
www-web-0   Bound     pvc-15c268c7-b507-11e6-932f-42010a800002   1Gi        RWO           48s
www-web-1   Bound     pvc-15c79307-b507-11e6-932f-42010a800002   1Gi        RWO           48s

StatefulSet 控制器创建了两个 PersistentVolumeClaims, 绑定到两个 PersistentVolumes

由于本教程使用的集群配置为动态制备 PersistentVolume 卷,所有的 PersistentVolume 卷都是自动创建和绑定的。

NginX Web 服务器默认会加载位于 /usr/share/nginx/html/index.html 的 index 文件。 StatefulSet spec 中的 volumeMounts 字段保证了 /usr/share/nginx/html 文件夹由一个 PersistentVolume 卷支持。

将 Pod 的主机名写入它们的 index.html 文件并验证 NginX Web 服务器使用该主机名提供服务:

for i in 0 1; do kubectl exec "web-$i" -- sh -c 'echo "$(hostname)" > /usr/share/nginx/html/index.html'; done

for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done
web-0
web-1

在一个终端监视 StatefulSet 的 Pod:

kubectl get pod -w -l app=nginx

在另一个终端删除 StatefulSet 所有的 Pod:

# 当你到达该部分的末尾时结束此 watch
# 在开始“扩展 StatefulSet” 时,你将启动一个新的 watch。
kubectl get pod --watch -l app=nginx
pod "web-0" deleted
pod "web-1" deleted

在第一个终端里检查 kubectl get 命令的输出,等待所有 Pod 变成 Running 和 Ready 状态。

# 这应该已经处于 Running 状态
kubectl get pod --watch -l app=nginx
NAME      READY     STATUS              RESTARTS   AGE
web-0     0/1       ContainerCreating   0          0s
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          2s
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         34s

验证所有 Web 服务器在继续使用它们的主机名提供服务:

for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done
web-0
web-1

虽然 web-0web-1 被重新调度了,但它们仍然继续监听各自的主机名,因为和它们的 PersistentVolumeClaim 相关联的 PersistentVolume 卷被重新挂载到了各自的 volumeMount 上。 不管 web-0web-1 被调度到了哪个节点上,它们的 PersistentVolume 卷将会被挂载到合适的挂载点上。

扩容/缩容 StatefulSet

扩容/缩容 StatefulSet 指增加或减少它的副本数。这通过更新 replicas 字段完成。 你可以使用 kubectl scale 或者 kubectl patch 来扩容/缩容一个 StatefulSet。

扩容

在一个终端窗口监视 StatefulSet 的 Pod:

# 如果你已经有一个正在运行的 wach,你可以继续使用它。
# 否则,就启动一个。
# 当 StatefulSet 有 5 个健康的 Pod 时结束此 watch
kubectl get pods --watch -l app=nginx

在另一个终端窗口使用 kubectl scale 扩展副本数为 5:

kubectl scale sts web --replicas=5
statefulset.apps/web scaled

在第一个 终端中检查 kubectl get 命令的输出,等待增加的 3 个 Pod 的状态变为 Running 和 Ready。

# 这应该已经处于 Running 状态
kubectl get pod --watch -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          2h
web-1     1/1       Running   0          2h
NAME      READY     STATUS    RESTARTS   AGE
web-2     0/1       Pending   0          0s
web-2     0/1       Pending   0         0s
web-2     0/1       ContainerCreating   0         0s
web-2     1/1       Running   0         19s
web-3     0/1       Pending   0         0s
web-3     0/1       Pending   0         0s
web-3     0/1       ContainerCreating   0         0s
web-3     1/1       Running   0         18s
web-4     0/1       Pending   0         0s
web-4     0/1       Pending   0         0s
web-4     0/1       ContainerCreating   0         0s
web-4     1/1       Running   0         19s

StatefulSet 控制器扩展了副本的数量。 如同创建 StatefulSet 所述,StatefulSet 按序号索引顺序创建各个 Pod,并且会等待前一个 Pod 变为 Running 和 Ready 才会启动下一个 Pod。

缩容

在一个终端监视 StatefulSet 的 Pod:

kubectl get pods -w -l app=nginx
# 当 StatefulSet 只有 3 个 Pod 时结束此 watch
kubectl get pod --watch -l app=nginx

在另一个终端使用 kubectl patch 将 StatefulSet 缩容回三个副本:

kubectl patch sts web -p '{"spec":{"replicas":3}}'
statefulset.apps/web patched

等待 web-4web-3 状态变为 Terminating。

kubectl get pods -w -l app=nginx
# 这应该已经处于 Running 状态
kubectl get pods --watch -l app=nginx
NAME      READY     STATUS              RESTARTS   AGE
web-0     1/1       Running             0          3h
web-1     1/1       Running             0          3h
web-2     1/1       Running             0          55s
web-3     1/1       Running             0          36s
web-4     0/1       ContainerCreating   0          18s
NAME      READY     STATUS    RESTARTS   AGE
web-4     1/1       Running   0          19s
web-4     1/1       Terminating   0         24s
web-4     1/1       Terminating   0         24s
web-3     1/1       Terminating   0         42s
web-3     1/1       Terminating   0         42s

顺序终止 Pod

控制器会按照与 Pod 序号索引相反的顺序每次删除一个 Pod。在删除下一个 Pod 前会等待上一个被完全关闭。

获取 StatefulSet 的 PersistentVolumeClaims:

kubectl get pvc -l app=nginx
NAME        STATUS    VOLUME                                     CAPACITY   ACCESSMODES   AGE
www-web-0   Bound     pvc-15c268c7-b507-11e6-932f-42010a800002   1Gi        RWO           13h
www-web-1   Bound     pvc-15c79307-b507-11e6-932f-42010a800002   1Gi        RWO           13h
www-web-2   Bound     pvc-e1125b27-b508-11e6-932f-42010a800002   1Gi        RWO           13h
www-web-3   Bound     pvc-e1176df6-b508-11e6-932f-42010a800002   1Gi        RWO           13h
www-web-4   Bound     pvc-e11bb5f8-b508-11e6-932f-42010a800002   1Gi        RWO           13h

五个 PersistentVolumeClaims 和五个 PersistentVolume 卷仍然存在。 查看 Pod 的稳定存储,我们发现当删除 StatefulSet 的 Pod 时,挂载到 StatefulSet 的 Pod 的 PersistentVolume 卷不会被删除。 当这种删除行为是由 StatefulSet 缩容引起时也是一样的。

更新 StatefulSet

StatefulSet 控制器支持自动更新。 更新策略由 StatefulSet API 对象的 spec.updateStrategy 字段决定。这个特性能够用来更新一个 StatefulSet 中 Pod 的容器镜像、资源请求和限制、标签和注解。

有两个有效的更新策略:RollingUpdate(默认)和 OnDelete

滚动更新

RollingUpdate 更新策略会更新一个 StatefulSet 中的所有 Pod,采用与序号索引相反的顺序并遵循 StatefulSet 的保证。

你可以通过指定 .spec.updateStrategy.rollingUpdate.partition 将使用 RollingUpdate 策略的 StatefulSet 的更新拆分为多个分区 。你将在本教程中稍后练习此操作。

首先,尝试一个简单的滚动更新。

在一个终端窗口中对 web StatefulSet 执行 patch 操作来再次改变容器镜像:

kubectl patch statefulset web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"gcr.io/google_containers/nginx-slim:0.8"}]'
statefulset.apps/web patched

在另一个终端监控 StatefulSet 中的 Pod:

# 滚动完成后结束此 watch
#
# 如果你不确定,请让它再运行一分钟
kubectl get pod -l app=nginx --watch

输出类似于:

NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          7m
web-1     1/1       Running   0          7m
web-2     1/1       Running   0          8m
web-2     1/1       Terminating   0         8m
web-2     1/1       Terminating   0         8m
web-2     0/1       Terminating   0         8m
web-2     0/1       Terminating   0         8m
web-2     0/1       Terminating   0         8m
web-2     0/1       Terminating   0         8m
web-2     0/1       Pending   0         0s
web-2     0/1       Pending   0         0s
web-2     0/1       ContainerCreating   0         0s
web-2     1/1       Running   0         19s
web-1     1/1       Terminating   0         8m
web-1     0/1       Terminating   0         8m
web-1     0/1       Terminating   0         8m
web-1     0/1       Terminating   0         8m
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         6s
web-0     1/1       Terminating   0         7m
web-0     1/1       Terminating   0         7m
web-0     0/1       Terminating   0         7m
web-0     0/1       Terminating   0         7m
web-0     0/1       Terminating   0         7m
web-0     0/1       Terminating   0         7m
web-0     0/1       Pending   0         0s
web-0     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         10s

StatefulSet 里的 Pod 采用和序号相反的顺序更新。在更新下一个 Pod 前,StatefulSet 控制器终止每个 Pod 并等待它们变成 Running 和 Ready。 请注意,虽然在顺序后继者变成 Running 和 Ready 之前 StatefulSet 控制器不会更新下一个 Pod,但它仍然会重建任何在更新过程中发生故障的 Pod,使用的是它们现有的版本。

已经接收到更新请求的 Pod 将会被恢复为更新的版本,没有收到请求的 Pod 则会被恢复为之前的版本。 像这样,控制器尝试继续使应用保持健康并在出现间歇性故障时保持更新的一致性。

获取 Pod 来查看它们的容器镜像:

for p in 0 1 2; do kubectl get pod "web-$p" --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'; echo; done
registry.k8s.io/nginx-slim:0.8
registry.k8s.io/nginx-slim:0.8
registry.k8s.io/nginx-slim:0.8

StatefulSet 中的所有 Pod 现在都在运行之前的容器镜像。

分段更新

你可以通过指定 .spec.updateStrategy.rollingUpdate.partition 将使用 RollingUpdate 策略的 StatefulSet 的更新拆分为多个分区

有关更多上下文,你可以阅读 StatefulSet 概念页面中的分区滚动更新

你可以使用 .spec.updateStrategy.rollingUpdate 中的 partition 字段对 StatefulSet 执行更新的分段操作。 对于此更新,你将保持 StatefulSet 中现有 Pod 不变,同时更改 StatefulSet 的 Pod 模板。 然后,你(或通过教程之外的一些外部自动化工具)可以触发准备好的更新。

web StatefulSet 执行 Patch 操作,为 updateStrategy 字段添加一个分区:

# "partition" 的值决定更改适用于哪些序号
# 确保使用比 StatefulSet 的最后一个序号更大的数字
kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":3}}}}'
statefulset.apps/web patched

再次 Patch StatefulSet 来改变此 StatefulSet 使用的容器镜像:

kubectl patch statefulset web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"registry.k8s.io/nginx-slim:0.7"}]'
statefulset.apps/web patched

删除 StatefulSet 中的 Pod:

kubectl delete pod web-2
pod "web-2" deleted

等待替代的 Pod 变成 Running 和 Ready。

# 当你看到 web-2 运行正常时结束 watch
kubectl get pod -l app=nginx --watch
NAME      READY     STATUS              RESTARTS   AGE
web-0     1/1       Running             0          4m
web-1     1/1       Running             0          4m
web-2     0/1       ContainerCreating   0          11s
web-2     1/1       Running   0         18s

获取 Pod 的容器镜像:

kubectl get pod web-2 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
registry.k8s.io/nginx-slim:0.8

请注意,虽然更新策略是 RollingUpdate,StatefulSet 还是会使用原始的容器镜像恢复 Pod。 这是因为 Pod 的序号比 updateStrategy 指定的 partition 更小。

金丝雀发布

现在,你将尝试对分段的变更进行金丝雀发布

你可以通过减少上文指定的 partition 来进行金丝雀发布,以测试修改后的模板。

通过 patch 命令修改 StatefulSet 来减少分区:

# “partition” 的值应与 StatefulSet 现有的最高序号相匹配
kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":2}}}}'
statefulset.apps/web patched

控制平面会触发 web-2 的替换(先优雅地 删除 现有 Pod,然后在删除完成后创建一个新的 Pod)。 等待新的 web-2 Pod 变成 Running 和 Ready。

# 这应该已经处于 Running 状态
kubectl get pod -l app=nginx --watch
NAME      READY     STATUS              RESTARTS   AGE
web-0     1/1       Running             0          4m
web-1     1/1       Running             0          4m
web-2     0/1       ContainerCreating   0          11s
web-2     1/1       Running   0         18s

获取 Pod 的容器:

kubectl get pod web-2 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
registry.k8s.io/nginx-slim:0.7

当你改变 partition 时,StatefulSet 会自动更新 web-2 Pod,这是因为 Pod 的序号大于或等于 partition

删除 web-1 Pod:

kubectl delete pod web-1
pod "web-1" deleted

等待 web-1 变成 Running 和 Ready。

# 这应该已经处于 Running 状态
kubectl get pod -l app=nginx --watch

输出类似于:

NAME      READY     STATUS        RESTARTS   AGE
web-0     1/1       Running       0          6m
web-1     0/1       Terminating   0          6m
web-2     1/1       Running       0          2m
web-1     0/1       Terminating   0         6m
web-1     0/1       Terminating   0         6m
web-1     0/1       Terminating   0         6m
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         18s

获取 web-1 Pod 的容器镜像:

kubectl get pod web-1 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
registry.k8s.io/nginx-slim:0.8

web-1 被按照原来的配置恢复,因为 Pod 的序号小于分区。当指定了分区时,如果更新了 StatefulSet 的 .spec.template,则所有序号大于或等于分区的 Pod 都将被更新。 如果一个序号小于分区的 Pod 被删除或者终止,它将被按照原来的配置恢复。

分阶段的发布

你可以使用类似金丝雀发布的方法执行一次分阶段的发布 (例如一次线性的、等比的或者指数形式的发布)。 要执行一次分阶段的发布,你需要设置 partition 为希望控制器暂停更新的序号。

分区当前为 2,请将其设置为 0

kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":0}}}}'
statefulset.apps/web patched

等待 StatefulSet 中的所有 Pod 变成 Running 和 Ready。

# 这应该已经处于 Running 状态
kubectl get pod -l app=nginx --watch

输出类似于:

NAME      READY     STATUS              RESTARTS   AGE
web-0     1/1       Running             0          3m
web-1     0/1       ContainerCreating   0          11s
web-2     1/1       Running             0          2m
web-1     1/1       Running   0         18s
web-0     1/1       Terminating   0         3m
web-0     1/1       Terminating   0         3m
web-0     0/1       Terminating   0         3m
web-0     0/1       Terminating   0         3m
web-0     0/1       Terminating   0         3m
web-0     0/1       Terminating   0         3m
web-0     0/1       Pending   0         0s
web-0     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         3s

获取 StatefulSet 中 Pod 的容器镜像详细信息:

for p in 0 1 2; do kubectl get pod "web-$p" --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'; echo; done
registry.k8s.io/nginx-slim:0.7
registry.k8s.io/nginx-slim:0.7
registry.k8s.io/nginx-slim:0.7

partition 改变为 0 以允许 StatefulSet 继续更新过程。

OnDelete 策略

通过将 .spec.template.updateStrategy.type 设置为 OnDelete,你可以为 StatefulSet 选择此更新策略。

web StatefulSet 执行 patch 操作,以使用 OnDelete 更新策略:

kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"OnDelete"}}}'
statefulset.apps/web patched

当你选择这个更新策略并修改 StatefulSet 的 .spec.template 字段时,StatefulSet 控制器将不会自动更新 Pod。 你需要自己手动管理发布,或使用单独的自动化工具来管理发布。

删除 StatefulSet

StatefulSet 同时支持非级联级联删除。使用非级联方式删除 StatefulSet 时,StatefulSet 的 Pod 不会被删除。使用级联删除时,StatefulSet 和它的 Pod 都会被删除。

阅读在集群中使用级联删除, 以了解通用的级联删除。

非级联删除

在一个终端窗口监视 StatefulSet 中的 Pod。

# 当 StatefulSet 没有 Pod 时结束此 watch
kubectl get pods --watch -l app=nginx

使用 kubectl delete 删除 StatefulSet。请确保提供了 --cascade=orphan 参数给命令。这个参数告诉 Kubernetes 只删除 StatefulSet 而不要删除它的任何 Pod。

kubectl delete statefulset web --cascade=orphan
statefulset.apps "web" deleted

获取 Pod 来检查它们的状态:

kubectl get pods -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          6m
web-1     1/1       Running   0          7m
web-2     1/1       Running   0          5m

虽然 web 已经被删除了,但所有 Pod 仍然处于 Running 和 Ready 状态。 删除 web-0

kubectl delete pod web-0
pod "web-0" deleted

获取 StatefulSet 的 Pod:

kubectl get pods -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-1     1/1       Running   0          10m
web-2     1/1       Running   0          7m

由于 web StatefulSet 已经被删除,web-0 没有被重新启动。

在一个终端监控 StatefulSet 的 Pod。

# 让 watch 一直运行到你下次启动 watch 为止
kubectl get pods --watch -l app=nginx

在另一个终端里重新创建 StatefulSet。请注意,除非你删除了 nginx Service(你不应该这样做),你将会看到一个错误,提示 Service 已经存在。

kubectl apply -f https://k8s.io/examples/application/web/web.yaml
statefulset.apps/web created
service/nginx unchanged

请忽略这个错误。它仅表示 kubernetes 进行了一次创建 nginx Headless Service 的尝试,尽管那个 Service 已经存在。

在第一个终端中运行并检查 kubectl get 命令的输出。

# 这应该已经处于 Running 状态
kubectl get pods --watch -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-1     1/1       Running   0          16m
web-2     1/1       Running   0          2m
NAME      READY     STATUS    RESTARTS   AGE
web-0     0/1       Pending   0          0s
web-0     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         18s
web-2     1/1       Terminating   0         3m
web-2     0/1       Terminating   0         3m
web-2     0/1       Terminating   0         3m
web-2     0/1       Terminating   0         3m

当重新创建 web StatefulSet 时,web-0 被第一个重新启动。 由于 web-1 已经处于 Running 和 Ready 状态,当 web-0 变成 Running 和 Ready 时, StatefulSet 会接收这个 Pod。由于你重新创建的 StatefulSet 的 replicas 等于 2, 一旦 web-0 被重新创建并且 web-1 被认为已经处于 Running 和 Ready 状态时,web-2 将会被终止。

现在再看看被 Pod 的 Web 服务器加载的 index.html 的内容:

for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done
web-0
web-1

尽管你同时删除了 StatefulSet 和 web-0 Pod,但它仍然使用最初写入 index.html 文件的主机名进行服务。 这是因为 StatefulSet 永远不会删除和一个 Pod 相关联的 PersistentVolume 卷。 当你重建这个 StatefulSet 并且重新启动了 web-0 时,它原本的 PersistentVolume 卷会被重新挂载。

级联删除

在一个终端窗口监视 StatefulSet 里的 Pod。

# 让它运行直到下一页部分
kubectl get pods --watch -l app=nginx

在另一个窗口中再次删除这个 StatefulSet,这次省略 --cascade=orphan 参数。

kubectl delete statefulset web
statefulset.apps "web" deleted

在第一个终端检查 kubectl get 命令的输出,并等待所有的 Pod 变成 Terminating 状态。

# 这应该已经处于 Running 状态
kubectl get pods --watch -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          11m
web-1     1/1       Running   0          27m
NAME      READY     STATUS        RESTARTS   AGE
web-0     1/1       Terminating   0          12m
web-1     1/1       Terminating   0         29m
web-0     0/1       Terminating   0         12m
web-0     0/1       Terminating   0         12m
web-0     0/1       Terminating   0         12m
web-1     0/1       Terminating   0         29m
web-1     0/1       Terminating   0         29m
web-1     0/1       Terminating   0         29m

如同你在缩容章节看到的,这些 Pod 按照与其序号索引相反的顺序每次终止一个。 在终止一个 Pod 前,StatefulSet 控制器会等待 Pod 后继者被完全终止。

kubectl delete service nginx
service "nginx" deleted

再一次重新创建 StatefulSet 和 Headless Service:

kubectl apply -f https://k8s.io/examples/application/web/web.yaml
service/nginx created
statefulset.apps/web created

当 StatefulSet 所有的 Pod 变成 Running 和 Ready 时,获取它们的 index.html 文件的内容:

for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done
web-0
web-1

即使你已经删除了 StatefulSet 和它的全部 Pod,这些 Pod 将会被重新创建并挂载它们的 PersistentVolume 卷,并且 web-0web-1 将继续使用它的主机名提供服务。

最后删除 nginx Service:

kubectl delete service nginx
service "nginx" deleted

并且删除 web StatefulSet:

kubectl delete statefulset web
statefulset "web" deleted

Pod 管理策略

对于某些分布式系统来说,StatefulSet 的顺序性保证是不必要和/或者不应该的。 这些系统仅仅要求唯一性和身份标志。

你可以指定 Pod 管理策略以避免这个严格的顺序; 你可以选择 OrderedReady(默认)或 Parallel

OrderedReady Pod 管理策略

OrderedReady Pod 管理策略是 StatefulSet 的默认选项。它告诉 StatefulSet 控制器遵循上文展示的顺序性保证。

Parallel Pod 管理策略

Parallel Pod 管理策略告诉 StatefulSet 控制器并行的终止所有 Pod, 在启动或终止另一个 Pod 前,不必等待这些 Pod 变成 Running 和 Ready 或者完全终止状态。

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  podManagementPolicy: "Parallel"
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: registry.k8s.io/nginx-slim:0.8
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi

这份清单和你在上文下载的完全一样,只是 web StatefulSet 的 .spec.podManagementPolicy 设置成了 Parallel

在一个终端窗口监视 StatefulSet 中的 Pod。

# 让 watch 一直运行直到本节结束
kubectl get pod -l app=nginx --watch

在另一个终端窗口创建清单中的 StatefulSet 和 Service:

kubectl apply -f https://k8s.io/examples/application/web/web-parallel.yaml
service/nginx created
statefulset.apps/web created

查看你在第一个终端中运行的 kubectl get 命令的输出。

# 这应该已经处于 Running 状态
kubectl get pod -l app=nginx --watch
NAME      READY     STATUS    RESTARTS   AGE
web-0     0/1       Pending   0          0s
web-0     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-1     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         10s
web-1     1/1       Running   0         10s

StatefulSet 控制器几乎同时启动了 web-0web-1

保持第二个终端打开,并在另一个终端窗口中扩容 StatefulSet:

kubectl scale statefulset/web --replicas=4
statefulset.apps/web scaled

kubectl get 命令运行的终端里检查它的输出。

web-3     0/1       Pending   0         0s
web-3     0/1       Pending   0         0s
web-3     0/1       Pending   0         7s
web-3     0/1       ContainerCreating   0         7s
web-2     1/1       Running   0         10s
web-3     1/1       Running   0         26s

StatefulSet 启动了两个新的 Pod,而且在启动第二个之前并没有等待第一个变成 Running 和 Ready 状态。

清理现场

你应该打开两个终端,准备在清理过程中运行 kubectl 命令。

kubectl delete sts web
# sts is an abbreviation for statefulset

你可以监视 kubectl get 来查看那些 Pod 被删除:

# 当你看到需要的内容后结束 watch
kubectl get pod -l app=nginx --watch
web-3     1/1       Terminating   0         9m
web-2     1/1       Terminating   0         9m
web-3     1/1       Terminating   0         9m
web-2     1/1       Terminating   0         9m
web-1     1/1       Terminating   0         44m
web-0     1/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-3     0/1       Terminating   0         9m
web-2     0/1       Terminating   0         9m
web-1     0/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-2     0/1       Terminating   0         9m
web-2     0/1       Terminating   0         9m
web-2     0/1       Terminating   0         9m
web-1     0/1       Terminating   0         44m
web-1     0/1       Terminating   0         44m
web-1     0/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-3     0/1       Terminating   0         9m
web-3     0/1       Terminating   0         9m
web-3     0/1       Terminating   0         9m

在删除过程中,StatefulSet 将并发的删除所有 Pod,在删除一个 Pod 前不会等待它的顺序后继者终止。

关闭 kubectl get 命令运行的终端并删除 nginx Service:

kubectl delete svc nginx

删除本教程中用到的 PersistentVolume 卷的持久化存储介质:

kubectl get pvc
NAME        STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
www-web-0   Bound    pvc-2bf00408-d366-4a12-bad0-1869c65d0bee   1Gi        RWO            standard       25m
www-web-1   Bound    pvc-ba3bfe9c-413e-4b95-a2c0-3ea8a54dbab4   1Gi        RWO            standard       24m
www-web-2   Bound    pvc-cba6cfa6-3a47-486b-a138-db5930207eaf   1Gi        RWO            standard       15m
www-web-3   Bound    pvc-0c04d7f0-787a-4977-8da3-d9d3a6d8d752   1Gi        RWO            standard       15m
www-web-4   Bound    pvc-b2c73489-e70b-4a4e-9ec1-9eab439aa43e   1Gi        RWO            standard       14m
kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM               STORAGECLASS   REASON   AGE
pvc-0c04d7f0-787a-4977-8da3-d9d3a6d8d752   1Gi        RWO            Delete           Bound    default/www-web-3   standard                15m
pvc-2bf00408-d366-4a12-bad0-1869c65d0bee   1Gi        RWO            Delete           Bound    default/www-web-0   standard                25m
pvc-b2c73489-e70b-4a4e-9ec1-9eab439aa43e   1Gi        RWO            Delete           Bound    default/www-web-4   standard                14m
pvc-ba3bfe9c-413e-4b95-a2c0-3ea8a54dbab4   1Gi        RWO            Delete           Bound    default/www-web-1   standard                24m
pvc-cba6cfa6-3a47-486b-a138-db5930207eaf   1Gi        RWO            Delete           Bound    default/www-web-2   standard                15m
kubectl delete pvc www-web-0 www-web-1 www-web-2 www-web-3 www-web-4
persistentvolumeclaim "www-web-0" deleted
persistentvolumeclaim "www-web-1" deleted
persistentvolumeclaim "www-web-2" deleted
persistentvolumeclaim "www-web-3" deleted
persistentvolumeclaim "www-web-4" deleted
kubectl get pvc
No resources found in default namespace.

5.6.2 - 示例:使用持久卷部署 WordPress 和 MySQL

本示例描述了如何通过 Minikube 在 Kubernetes 上安装 WordPress 和 MySQL。 这两个应用都使用 PersistentVolumes 和 PersistentVolumeClaims 保存数据。

PersistentVolume(PV)是在集群里由管理员手动制备或 Kubernetes 通过 StorageClass 动态制备的一块存储。 PersistentVolumeClaim 是用户对存储的请求,该请求可由某个 PV 来满足。 PersistentVolumes 和 PersistentVolumeClaims 独立于 Pod 生命周期而存在, 在 Pod 重启、重新调度甚至删除过程中用于保存数据。

教程目标

  • 创建 PersistentVolumeClaims 和 PersistentVolumes
  • 创建 kustomization.yaml 以使用
    • Secret 生成器
    • MySQL 资源配置
    • WordPress 资源配置
  • kubectl apply -k ./ 来应用整个 kustomization 目录
  • 清理

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要获知版本信息,请输入 kubectl version.

此例在 kubectl 1.27 或者更高版本有效。

下载下面的配置文件:

  1. mysql-deployment.yaml

  2. wordpress-deployment.yaml

创建 PersistentVolumeClaims 和 PersistentVolumes

MySQL 和 Wordpress 都需要一个 PersistentVolume 来存储数据。 它们的 PersistentVolumeClaims 将在部署步骤中创建。

许多集群环境都安装了默认的 StorageClass。如果在 PersistentVolumeClaim 中未指定 StorageClass, 则使用集群的默认 StorageClass。

创建 PersistentVolumeClaim 时,将根据 StorageClass 配置动态制备一个 PersistentVolume。

创建 kustomization.yaml

创建 Secret 生成器

Secret 是存储诸如密码或密钥之类敏感数据的对象。 从 1.14 开始,kubectl 支持使用一个 kustomization 文件来管理 Kubernetes 对象。 你可以通过 kustomization.yaml 中的生成器创建一个 Secret。

通过以下命令在 kustomization.yaml 中添加一个 Secret 生成器。 你需要将 YOUR_PASSWORD 替换为自己要用的密码。

cat <<EOF >./kustomization.yaml
secretGenerator:
- name: mysql-pass
  literals:
  - password=YOUR_PASSWORD
EOF

补充 MySQL 和 WordPress 的资源配置

以下清单文件描述的是一个单实例的 MySQL Deployment。MySQL 容器将 PersistentVolume 挂载在 /var/lib/mysqlMYSQL_ROOT_PASSWORD 环境变量根据 Secret 设置数据库密码。

apiVersion: v1
kind: Service
metadata:
  name: wordpress-mysql
  labels:
    app: wordpress
spec:
  ports:
    - port: 3306
  selector:
    app: wordpress
    tier: mysql
  clusterIP: None
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pv-claim
  labels:
    app: wordpress
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wordpress-mysql
  labels:
    app: wordpress
spec:
  selector:
    matchLabels:
      app: wordpress
      tier: mysql
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: wordpress
        tier: mysql
    spec:
      containers:
      - image: mysql:8.0
        name: mysql
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-pass
              key: password
        - name: MYSQL_DATABASE
          value: wordpress
        - name: MYSQL_USER
          value: wordpress
        - name: MYSQL_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-pass
              key: password
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: mysql-persistent-storage
          mountPath: /var/lib/mysql
      volumes:
      - name: mysql-persistent-storage
        persistentVolumeClaim:
          claimName: mysql-pv-claim

以下清单文件描述的是一个单实例 WordPress Deployment。WordPress 容器将 PersistentVolume 挂载到 /var/www/html,用于保存网站数据文件。 WORDPRESS_DB_HOST 环境变量设置上面定义的 MySQL Service 的名称,WordPress 将通过 Service 访问数据库。 WORDPRESS_DB_PASSWORD 环境变量根据使用 kustomize 生成的 Secret 设置数据库密码。

apiVersion: v1
kind: Service
metadata:
  name: wordpress
  labels:
    app: wordpress
spec:
  ports:
    - port: 80
  selector:
    app: wordpress
    tier: frontend
  type: LoadBalancer
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: wp-pv-claim
  labels:
    app: wordpress
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wordpress
  labels:
    app: wordpress
spec:
  selector:
    matchLabels:
      app: wordpress
      tier: frontend
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: wordpress
        tier: frontend
    spec:
      containers:
      - image: wordpress:6.2.1-apache
        name: wordpress
        env:
        - name: WORDPRESS_DB_HOST
          value: wordpress-mysql
        - name: WORDPRESS_DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-pass
              key: password
        - name: WORDPRESS_DB_USER
          value: wordpress
        ports:
        - containerPort: 80
          name: wordpress
        volumeMounts:
        - name: wordpress-persistent-storage
          mountPath: /var/www/html
      volumes:
      - name: wordpress-persistent-storage
        persistentVolumeClaim:
          claimName: wp-pv-claim
  1. 下载 MySQL Deployment 配置文件。

    curl -LO https://k8s.io/examples/application/wordpress/mysql-deployment.yaml
    
  1. 下载 WordPress 配置文件。

    curl -LO https://k8s.io/examples/application/wordpress/wordpress-deployment.yaml
    
  1. 将上述内容追加到 kustomization.yaml 文件。

    cat <<EOF >>./kustomization.yaml
    resources:
      - mysql-deployment.yaml
      - wordpress-deployment.yaml
    EOF
    

应用和验证

kustomization.yaml 包含用于部署 WordPress 网站以及 MySQL 数据库的所有资源。你可以通过以下方式应用目录:

kubectl apply -k ./

现在,你可以验证所有对象是否存在。

  1. 通过运行以下命令验证 Secret 是否存在:

    kubectl get secrets
    

    响应应如下所示:

    NAME                    TYPE                                  DATA   AGE
    mysql-pass-c57bb4t7mf   Opaque                                1      9s
    
  1. 验证是否已动态制备 PersistentVolume:

    kubectl get pvc
    

    响应应如下所示:

    NAME             STATUS    VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS       AGE
    mysql-pv-claim   Bound     pvc-8cbd7b2e-4044-11e9-b2bb-42010a800002   20Gi       RWO            standard           77s
    wp-pv-claim      Bound     pvc-8cd0df54-4044-11e9-b2bb-42010a800002   20Gi       RWO            standard           77s
    
  1. 通过运行以下命令来验证 Pod 是否正在运行:

    kubectl get pods
    

    响应应如下所示:

    NAME                               READY     STATUS    RESTARTS   AGE
    wordpress-mysql-1894417608-x5dzt   1/1       Running   0          40s
    
  1. 通过运行以下命令来验证 Service 是否正在运行:

    kubectl get services wordpress
    

    响应应如下所示:

    NAME        TYPE            CLUSTER-IP   EXTERNAL-IP   PORT(S)        AGE
    wordpress   LoadBalancer    10.0.0.89    <pending>     80:32406/TCP   4m
    
  1. 运行以下命令以获取 WordPress 服务的 IP 地址:

    minikube service wordpress --url
    

    响应应如下所示:

    http://1.2.3.4:32406
    
  1. 复制 IP 地址,然后将页面加载到浏览器中来查看你的站点。

    你应该看到类似于以下屏幕截图的 WordPress 设置页面。

    wordpress-init

清理现场

  1. 运行以下命令删除你的 Secret、Deployment、Service 和 PersistentVolumeClaims:

    kubectl delete -k ./
    

接下来

5.6.3 - 示例:使用 StatefulSet 部署 Cassandra

本教程描述了如何在 Kubernetes 上运行 Apache Cassandra。 数据库 Cassandra 需要永久性存储提供数据持久性(应用状态)。 在此示例中,自定义 Cassandra seed provider 使数据库在接入 Cassandra 集群时能够发现新的 Cassandra 实例。

使用StatefulSet可以更轻松地将有状态的应用程序部署到你的 Kubernetes 集群中。 有关本教程中使用的功能的更多信息, 请参阅 StatefulSet

教程目标

  • 创建并验证 Cassandra 无头(headless)Service
  • 使用 StatefulSet 创建一个 Cassandra ring。
  • 验证 StatefulSet。
  • 修改 StatefulSet。
  • 删除 StatefulSet 及其 Pod

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要完成本教程,你应该已经熟悉 PodServiceStatefulSet

额外的 Minikube 设置说明

为 Cassandra 创建无头(headless) Services

在 Kubernetes 中,一个 Service 描述了一组执行相同任务的 Pod

以下 Service 用于在 Cassandra Pod 和集群中的客户端之间进行 DNS 查找:

apiVersion: v1
kind: Service
metadata:
  labels:
    app: cassandra
  name: cassandra
spec:
  clusterIP: None
  ports:
  - port: 9042
  selector:
    app: cassandra

创建一个 Service 来跟踪 cassandra-service.yaml 文件中的所有 Cassandra StatefulSet:

kubectl apply -f https://k8s.io/examples/application/cassandra/cassandra-service.yaml

验证(可选)

获取 Cassandra Service。

kubectl get svc cassandra

响应是:

NAME        TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
cassandra   ClusterIP   None         <none>        9042/TCP   45s

如果没有看到名为 cassandra 的服务,则表示创建失败。 请阅读调试服务,以解决常见问题。

使用 StatefulSet 创建 Cassandra Ring

下面包含的 StatefulSet 清单创建了一个由三个 Pod 组成的 Cassandra ring。

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: cassandra
  labels:
    app: cassandra
spec:
  serviceName: cassandra
  replicas: 3
  selector:
    matchLabels:
      app: cassandra
  template:
    metadata:
      labels:
        app: cassandra
    spec:
      terminationGracePeriodSeconds: 1800
      containers:
      - name: cassandra
        image: gcr.io/google-samples/cassandra:v13
        imagePullPolicy: Always
        ports:
        - containerPort: 7000
          name: intra-node
        - containerPort: 7001
          name: tls-intra-node
        - containerPort: 7199
          name: jmx
        - containerPort: 9042
          name: cql
        resources:
          limits:
            cpu: "500m"
            memory: 1Gi
          requests:
            cpu: "500m"
            memory: 1Gi
        securityContext:
          capabilities:
            add:
              - IPC_LOCK
        lifecycle:
          preStop:
            exec:
              command: 
              - /bin/sh
              - -c
              - nodetool drain
        env:
          - name: MAX_HEAP_SIZE
            value: 512M
          - name: HEAP_NEWSIZE
            value: 100M
          - name: CASSANDRA_SEEDS
            value: "cassandra-0.cassandra.default.svc.cluster.local"
          - name: CASSANDRA_CLUSTER_NAME
            value: "K8Demo"
          - name: CASSANDRA_DC
            value: "DC1-K8Demo"
          - name: CASSANDRA_RACK
            value: "Rack1-K8Demo"
          - name: POD_IP
            valueFrom:
              fieldRef:
                fieldPath: status.podIP
        readinessProbe:
          exec:
            command:
            - /bin/bash
            - -c
            - /ready-probe.sh
          initialDelaySeconds: 15
          timeoutSeconds: 5
        # 这些卷挂载是持久的。它们类似内联申领,但并不完全相同,
        # 因为这些卷挂载的名称需要与 StatefulSet 中某 Pod 卷完全匹配。
        volumeMounts:
        - name: cassandra-data
          mountPath: /cassandra_data
  # 这些将被控制器转换为卷申领,并挂载在上述路径。
  # 请勿将此设置用于生产环境,除非使用了 GCEPersistentDisk 或其他 SSD 持久盘。
  volumeClaimTemplates:
  - metadata:
      name: cassandra-data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: fast
      resources:
        requests:
          storage: 1Gi
---
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: fast
provisioner: k8s.io/minikube-hostpath
parameters:
  type: pd-ssd

使用 cassandra-statefulset.yaml 文件创建 Cassandra StatefulSet:

# 如果你能未经修改地应用 cassandra-statefulset.yaml,请使用此命令
kubectl apply -f https://k8s.io/examples/application/cassandra/cassandra-statefulset.yaml

如果你为了适合你的集群需要修改 cassandra-statefulset.yaml, 下载 https://k8s.io/examples/application/cassandra/cassandra-statefulset.yaml, 然后应用修改后的清单。

# 如果使用本地的 cassandra-statefulset.yaml ,请使用此命令
kubectl apply -f cassandra-statefulset.yaml

验证 Cassandra StatefulSet

  1. 获取 Cassandra StatefulSet:

    kubectl get statefulset cassandra
    

    响应应该与此类似:

    NAME        DESIRED   CURRENT   AGE
    cassandra   3         0         13s
    

    StatefulSet 资源会按顺序部署 Pod。

  1. 获取 Pod 查看已排序的创建状态:

    kubectl get pods -l="app=cassandra"
    

    响应应该与此类似:

    NAME          READY     STATUS              RESTARTS   AGE
    cassandra-0   1/1       Running             0          1m
    cassandra-1   0/1       ContainerCreating   0          8s
    

    这三个 Pod 要花几分钟的时间才能部署。部署之后,相同的命令将返回类似于以下的输出:

    NAME          READY     STATUS    RESTARTS   AGE
    cassandra-0   1/1       Running   0          10m
    cassandra-1   1/1       Running   0          9m
    cassandra-2   1/1       Running   0          8m
    
  1. 运行第一个 Pod 中的 Cassandra nodetool, 以显示 ring 的状态。

    kubectl exec -it cassandra-0 -- nodetool status
    

    响应应该与此类似:

    Datacenter: DC1-K8Demo
    ======================
    Status=Up/Down
    |/ State=Normal/Leaving/Joining/Moving
    --  Address     Load       Tokens       Owns (effective)  Host ID                               Rack
    UN  172.17.0.5  83.57 KiB  32           74.0%             e2dd09e6-d9d3-477e-96c5-45094c08db0f  Rack1-K8Demo
    UN  172.17.0.4  101.04 KiB  32           58.8%             f89d6835-3a42-4419-92b3-0e62cae1479c  Rack1-K8Demo
    UN  172.17.0.6  84.74 KiB  32           67.1%             a6a1e8c2-3dc5-4417-b1a0-26507af2aaad  Rack1-K8Demo
    

修改 Cassandra StatefulSet

使用 kubectl edit 修改 Cassandra StatefulSet 的大小。

  1. 运行以下命令:

    kubectl edit statefulset cassandra
    

    此命令你的终端中打开一个编辑器。需要更改的是 replicas 字段。下面是 StatefulSet 文件的片段示例:

    # 请编辑以下对象。以 '#' 开头的行将被忽略,
    # 且空文件将放弃编辑。如果保存此文件时发生错误,
    # 将重新打开并显示相关故障。
    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
      creationTimestamp: 2016-08-13T18:40:58Z
      generation: 1
      labels:
      app: cassandra
      name: cassandra
      namespace: default
      resourceVersion: "323"
      uid: 7a219483-6185-11e6-a910-42010a8a0fc0
    spec:
      replicas: 3
    
  1. 将副本数(replicas)更改为 4,然后保存清单。

    StatefulSet 现在可以扩展到运行 4 个 Pod。

  2. 获取 Cassandra StatefulSet 验证更改:

    kubectl get statefulset cassandra
    

    响应应该与此类似:

    NAME        DESIRED   CURRENT   AGE
    cassandra   4         4         36m
    

清理现场

删除或缩小 StatefulSet 不会删除与 StatefulSet 关联的卷。 这个设置是出于安全考虑,因为你的数据比自动清除所有相关的 StatefulSet 资源更有价值。

  1. 运行以下命令(连在一起成为一个单独的命令)删除 Cassandra StatefulSet 中的所有内容:

    grace=$(kubectl get pod cassandra-0 -o=jsonpath='{.spec.terminationGracePeriodSeconds}') \
      && kubectl delete statefulset -l app=cassandra \
      && echo "Sleeping ${grace} seconds" 1>&2 \
      && sleep $grace \
      && kubectl delete persistentvolumeclaim -l app=cassandra
    
  1. 运行以下命令,删除你为 Cassandra 设置的 Service:

    kubectl delete service -l app=cassandra
    

Cassandra 容器环境变量

本教程中的 Pod 使用来自 Google 容器镜像库gcr.io/google-samples/cassandra:v13 镜像。上面的 Docker 镜像基于 debian-base, 并且包含 OpenJDK 8。

该镜像包括来自 Apache Debian 存储库的标准 Cassandra 安装。 通过使用环境变量,你可以更改插入到 cassandra.yaml 中的值。

环境变量默认值
CASSANDRA_CLUSTER_NAME'Test Cluster'
CASSANDRA_NUM_TOKENS32
CASSANDRA_RPC_ADDRESS0.0.0.0

接下来

5.6.4 - 运行 ZooKeeper,一个分布式协调系统

本教程展示了在 Kubernetes 上使用 StatefulSetPodDisruptionBudgetPodAntiAffinity 特性运行 Apache Zookeeper

准备开始

在开始本教程前,你应该熟悉以下 Kubernetes 概念。

你需要一个至少包含四个节点的集群,每个节点至少 2 个 CPU 和 4 GiB 内存。 在本教程中你将会隔离(Cordon)和腾空(Drain )集群的节点。 这意味着集群节点上所有的 Pod 将会被终止并移除。这些节点也会暂时变为不可调度。 在本教程中你应该使用一个独占的集群,或者保证你造成的干扰不会影响其它租户。

本教程假设你的集群已配置为动态制备 PersistentVolume。 如果你的集群没有配置成这样,在开始本教程前,你需要手动准备三个 20 GiB 的卷。

教程目标

在学习本教程后,你将熟悉下列内容。

  • 如何使用 StatefulSet 部署一个 ZooKeeper ensemble。
  • 如何一致地配置 ensemble。
  • 如何在 ensemble 中分布 ZooKeeper 服务器的部署。
  • 如何在计划维护中使用 PodDisruptionBudget 确保服务可用性。

ZooKeeper

Apache ZooKeeper 是一个分布式的开源协调服务,用于分布式系统。 ZooKeeper 允许你读取、写入数据和发现数据更新。 数据按层次结构组织在文件系统中,并复制到 ensemble(一个 ZooKeeper 服务器的集合) 中所有的 ZooKeeper 服务器。对数据的所有操作都是原子的和顺序一致的。 ZooKeeper 通过 Zab 一致性协议在 ensemble 的所有服务器之间复制一个状态机来确保这个特性。

Ensemble 使用 Zab 协议选举一个领导者,在选举出领导者前不能写入数据。 一旦选举出了领导者,ensemble 使用 Zab 保证所有写入被复制到一个 quorum, 然后这些写入操作才会被确认并对客户端可用。 如果没有遵照加权 quorums,一个 quorum 表示包含当前领导者的 ensemble 的多数成员。 例如,如果 ensemble 有 3 个服务器,一个包含领导者的成员和另一个服务器就组成了一个 quorum。 如果 ensemble 不能达成一个 quorum,数据将不能被写入。

ZooKeeper 在内存中保存它们的整个状态机,但是每个改变都被写入一个在存储介质上的持久 WAL(Write Ahead Log)。 当一个服务器出现故障时,它能够通过回放 WAL 恢复之前的状态。 为了防止 WAL 无限制的增长,ZooKeeper 服务器会定期的将内存状态快照保存到存储介质。 这些快照能够直接加载到内存中,所有在这个快照之前的 WAL 条目都可以被安全的丢弃。

创建一个 ZooKeeper Ensemble

下面的清单包含一个无头服务、 一个 Service、 一个 PodDisruptionBudget 和一个 StatefulSet

apiVersion: v1
kind: Service
metadata:
  name: zk-hs
  labels:
    app: zk
spec:
  ports:
  - port: 2888
    name: server
  - port: 3888
    name: leader-election
  clusterIP: None
  selector:
    app: zk
---
apiVersion: v1
kind: Service
metadata:
  name: zk-cs
  labels:
    app: zk
spec:
  ports:
  - port: 2181
    name: client
  selector:
    app: zk
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: zk-pdb
spec:
  selector:
    matchLabels:
      app: zk
  maxUnavailable: 1
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: zk
spec:
  selector:
    matchLabels:
      app: zk
  serviceName: zk-hs
  replicas: 3
  updateStrategy:
    type: RollingUpdate
  podManagementPolicy: OrderedReady
  template:
    metadata:
      labels:
        app: zk
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchExpressions:
                  - key: "app"
                    operator: In
                    values:
                    - zk
              topologyKey: "kubernetes.io/hostname"
      containers:
      - name: kubernetes-zookeeper
        imagePullPolicy: Always
        image: "registry.k8s.io/kubernetes-zookeeper:1.0-3.4.10"
        resources:
          requests:
            memory: "1Gi"
            cpu: "0.5"
        ports:
        - containerPort: 2181
          name: client
        - containerPort: 2888
          name: server
        - containerPort: 3888
          name: leader-election
        command:
        - sh
        - -c
        - "start-zookeeper \
          --servers=3 \
          --data_dir=/var/lib/zookeeper/data \
          --data_log_dir=/var/lib/zookeeper/data/log \
          --conf_dir=/opt/zookeeper/conf \
          --client_port=2181 \
          --election_port=3888 \
          --server_port=2888 \
          --tick_time=2000 \
          --init_limit=10 \
          --sync_limit=5 \
          --heap=512M \
          --max_client_cnxns=60 \
          --snap_retain_count=3 \
          --purge_interval=12 \
          --max_session_timeout=40000 \
          --min_session_timeout=4000 \
          --log_level=INFO"
        readinessProbe:
          exec:
            command:
            - sh
            - -c
            - "zookeeper-ready 2181"
          initialDelaySeconds: 10
          timeoutSeconds: 5
        livenessProbe:
          exec:
            command:
            - sh
            - -c
            - "zookeeper-ready 2181"
          initialDelaySeconds: 10
          timeoutSeconds: 5
        volumeMounts:
        - name: datadir
          mountPath: /var/lib/zookeeper
      securityContext:
        runAsUser: 1000
        fsGroup: 1000
  volumeClaimTemplates:
  - metadata:
      name: datadir
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 10Gi

打开一个命令行终端,使用命令 kubectl apply 创建这个清单。

kubectl apply -f https://k8s.io/examples/application/zookeeper/zookeeper.yaml

这个操作创建了 zk-hs 无头服务、zk-cs 服务、zk-pdb PodDisruptionBudget 和 zk StatefulSet。

service/zk-hs created
service/zk-cs created
poddisruptionbudget.policy/zk-pdb created
statefulset.apps/zk created

使用命令 kubectl get 查看 StatefulSet 控制器创建的几个 Pod。

kubectl get pods -w -l app=zk

一旦 zk-2 Pod 变成 Running 和 Ready 状态,请使用 CRTL-C 结束 kubectl。

NAME      READY     STATUS    RESTARTS   AGE
zk-0      0/1       Pending   0          0s
zk-0      0/1       Pending   0         0s
zk-0      0/1       ContainerCreating   0         0s
zk-0      0/1       Running   0         19s
zk-0      1/1       Running   0         40s
zk-1      0/1       Pending   0         0s
zk-1      0/1       Pending   0         0s
zk-1      0/1       ContainerCreating   0         0s
zk-1      0/1       Running   0         18s
zk-1      1/1       Running   0         40s
zk-2      0/1       Pending   0         0s
zk-2      0/1       Pending   0         0s
zk-2      0/1       ContainerCreating   0         0s
zk-2      0/1       Running   0         19s
zk-2      1/1       Running   0         40s

StatefulSet 控制器创建 3 个 Pod,每个 Pod 包含一个 ZooKeeper 服务容器。

促成 Leader 选举

由于在匿名网络中没有用于选举 leader 的终止算法,Zab 要求显式的进行成员关系配置, 以执行 leader 选举。Ensemble 中的每个服务器都需要具有一个独一无二的标识符, 所有的服务器均需要知道标识符的全集,并且每个标识符都需要和一个网络地址相关联。

使用命令 kubectl exec 获取 zk StatefulSet 中 Pod 的主机名。

for i in 0 1 2; do kubectl exec zk-$i -- hostname; done

StatefulSet 控制器基于每个 Pod 的序号索引为它们各自提供一个唯一的主机名。 主机名采用 <statefulset 名称>-<序数索引> 的形式。 由于 zk StatefulSet 的 replicas 字段设置为 3,这个集合的控制器将创建 3 个 Pod,主机名为:zk-0zk-1zk-2

zk-0
zk-1
zk-2

ZooKeeper ensemble 中的服务器使用自然数作为唯一标识符, 每个服务器的标识符都保存在服务器的数据目录中一个名为 myid 的文件里。

检查每个服务器的 myid 文件的内容。

for i in 0 1 2; do echo "myid zk-$i";kubectl exec zk-$i -- cat /var/lib/zookeeper/data/myid; done

由于标识符为自然数并且序号索引是非负整数,你可以在序号上加 1 来生成一个标识符。

myid zk-0
1
myid zk-1
2
myid zk-2
3

获取 zk StatefulSet 中每个 Pod 的全限定域名(Fully Qualified Domain Name,FQDN)。

for i in 0 1 2; do kubectl exec zk-$i -- hostname -f; done

zk-hs Service 为所有 Pod 创建了一个域:zk-hs.default.svc.cluster.local

zk-0.zk-hs.default.svc.cluster.local
zk-1.zk-hs.default.svc.cluster.local
zk-2.zk-hs.default.svc.cluster.local

Kubernetes DNS 中的 A 记录将 FQDN 解析成为 Pod 的 IP 地址。 如果 Kubernetes 重新调度这些 Pod,这个 A 记录将会使用这些 Pod 的新 IP 地址完成更新, 但 A 记录的名称不会改变。

ZooKeeper 在一个名为 zoo.cfg 的文件中保存它的应用配置。 使用 kubectl execzk-0 Pod 中查看 zoo.cfg 文件的内容。

kubectl exec zk-0 -- cat /opt/zookeeper/conf/zoo.cfg

文件底部为 server.1server.2server.3,其中的 123 分别对应 ZooKeeper 服务器的 myid 文件中的标识符。 它们被设置为 zk StatefulSet 中的 Pods 的 FQDNs。

clientPort=2181
dataDir=/var/lib/zookeeper/data
dataLogDir=/var/lib/zookeeper/log
tickTime=2000
initLimit=10
syncLimit=2000
maxClientCnxns=60
minSessionTimeout= 4000
maxSessionTimeout= 40000
autopurge.snapRetainCount=3
autopurge.purgeInterval=0
server.1=zk-0.zk-hs.default.svc.cluster.local:2888:3888
server.2=zk-1.zk-hs.default.svc.cluster.local:2888:3888
server.3=zk-2.zk-hs.default.svc.cluster.local:2888:3888

达成共识

一致性协议要求每个参与者的标识符唯一。 在 Zab 协议里任何两个参与者都不应该声明相同的唯一标识符。 对于让系统中的进程协商哪些进程已经提交了哪些数据而言,这是必须的。 如果有两个 Pod 使用相同的序号启动,这两个 ZooKeeper 服务器会将自己识别为相同的服务器。

kubectl get pods -w -l app=zk
NAME      READY     STATUS    RESTARTS   AGE
zk-0      0/1       Pending   0          0s
zk-0      0/1       Pending   0         0s
zk-0      0/1       ContainerCreating   0         0s
zk-0      0/1       Running   0         19s
zk-0      1/1       Running   0         40s
zk-1      0/1       Pending   0         0s
zk-1      0/1       Pending   0         0s
zk-1      0/1       ContainerCreating   0         0s
zk-1      0/1       Running   0         18s
zk-1      1/1       Running   0         40s
zk-2      0/1       Pending   0         0s
zk-2      0/1       Pending   0         0s
zk-2      0/1       ContainerCreating   0         0s
zk-2      0/1       Running   0         19s
zk-2      1/1       Running   0         40s

每个 Pod 的 A 记录仅在 Pod 变成 Ready 状态时被录入。 因此,ZooKeeper 服务器的 FQDN 只会解析到一个端点, 而那个端点将是申领其 myid 文件中所配置标识的唯一 ZooKeeper 服务器。

zk-0.zk-hs.default.svc.cluster.local
zk-1.zk-hs.default.svc.cluster.local
zk-2.zk-hs.default.svc.cluster.local

这保证了 ZooKeeper 的 zoo.cfg 文件中的 servers 属性代表了一个正确配置的 ensemble。

server.1=zk-0.zk-hs.default.svc.cluster.local:2888:3888
server.2=zk-1.zk-hs.default.svc.cluster.local:2888:3888
server.3=zk-2.zk-hs.default.svc.cluster.local:2888:3888

当服务器使用 Zab 协议尝试提交一个值的时候,它们会达成一致并成功提交这个值 (如果领导者选举成功并且至少有两个 Pod 处于 Running 和 Ready 状态), 或者将会失败(如果没有满足上述条件中的任意一条)。 当一个服务器承认另一个服务器的代写时不会有状态产生。

Ensemble 健康检查

最基本的健康检查是向一个 ZooKeeper 服务器写入一些数据,然后从另一个服务器读取这些数据。

使用 zkCli.sh 脚本在 zk-0 Pod 上写入 world 到路径 /hello

kubectl exec zk-0 zkCli.sh create /hello world
WATCHER::

WatchedEvent state:SyncConnected type:None path:null
Created /hello

使用下面的命令从 zk-1 Pod 获取数据。

kubectl exec zk-1 zkCli.sh get /hello

你在 zk-0 上创建的数据在 ensemble 中所有的服务器上都是可用的。

WATCHER::

WatchedEvent state:SyncConnected type:None path:null
world
cZxid = 0x100000002
ctime = Thu Dec 08 15:13:30 UTC 2016
mZxid = 0x100000002
mtime = Thu Dec 08 15:13:30 UTC 2016
pZxid = 0x100000002
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0

提供持久存储

如同在 ZooKeeper 一节所提到的, ZooKeeper 提交所有的条目到一个持久 WAL,并周期性的将内存快照写入存储介质。 对于使用一致性协议实现一个复制状态机的应用来说, 使用 WAL 提供持久化是一种常用的技术,对于普通的存储应用也是如此。

使用 kubectl delete 删除 zk StatefulSet。

kubectl delete statefulset zk
statefulset.apps "zk" deleted

观察 StatefulSet 中的 Pod 变为终止状态。

kubectl get pods -w -l app=zk

zk-0 完全终止时,使用 CRTL-C 结束 kubectl。

zk-2      1/1       Terminating   0         9m
zk-0      1/1       Terminating   0         11m
zk-1      1/1       Terminating   0         10m
zk-2      0/1       Terminating   0         9m
zk-2      0/1       Terminating   0         9m
zk-2      0/1       Terminating   0         9m
zk-1      0/1       Terminating   0         10m
zk-1      0/1       Terminating   0         10m
zk-1      0/1       Terminating   0         10m
zk-0      0/1       Terminating   0         11m
zk-0      0/1       Terminating   0         11m
zk-0      0/1       Terminating   0         11m

重新应用 zookeeper.yaml 中的清单。

kubectl apply -f https://k8s.io/examples/application/zookeeper/zookeeper.yaml

zk StatefulSet 将会被创建。由于清单中的其他 API 对象已经存在,所以它们不会被修改。

观察 StatefulSet 控制器重建 StatefulSet 的 Pod。

kubectl get pods -w -l app=zk

一旦 zk-2 Pod 处于 Running 和 Ready 状态,使用 CRTL-C 停止 kubectl 命令。

NAME      READY     STATUS    RESTARTS   AGE
zk-0      0/1       Pending   0          0s
zk-0      0/1       Pending   0         0s
zk-0      0/1       ContainerCreating   0         0s
zk-0      0/1       Running   0         19s
zk-0      1/1       Running   0         40s
zk-1      0/1       Pending   0         0s
zk-1      0/1       Pending   0         0s
zk-1      0/1       ContainerCreating   0         0s
zk-1      0/1       Running   0         18s
zk-1      1/1       Running   0         40s
zk-2      0/1       Pending   0         0s
zk-2      0/1       Pending   0         0s
zk-2      0/1       ContainerCreating   0         0s
zk-2      0/1       Running   0         19s
zk-2      1/1       Running   0         40s

zk-2 Pod 中获取你在健康检查中输入的值。

kubectl exec zk-2 zkCli.sh get /hello

尽管 zk StatefulSet 中所有的 Pod 都已经被终止并重建过, ensemble 仍然使用原来的数值提供服务。

WATCHER::

WatchedEvent state:SyncConnected type:None path:null
world
cZxid = 0x100000002
ctime = Thu Dec 08 15:13:30 UTC 2016
mZxid = 0x100000002
mtime = Thu Dec 08 15:13:30 UTC 2016
pZxid = 0x100000002
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0

zk StatefulSet 的 spec 中的 volumeClaimTemplates 字段标识了将要为每个 Pod 准备的 PersistentVolume。

volumeClaimTemplates:
  - metadata:
      name: datadir
      annotations:
        volume.alpha.kubernetes.io/storage-class: anything
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 20Gi

StatefulSet 控制器为 StatefulSet 中的每个 Pod 生成一个 PersistentVolumeClaim

获取 StatefulSetPersistentVolumeClaim

kubectl get pvc -l app=zk

StatefulSet 重新创建它的 Pod 时,Pod 的 PersistentVolume 会被重新挂载。

NAME           STATUS    VOLUME                                     CAPACITY   ACCESSMODES   AGE
datadir-zk-0   Bound     pvc-bed742cd-bcb1-11e6-994f-42010a800002   20Gi       RWO           1h
datadir-zk-1   Bound     pvc-bedd27d2-bcb1-11e6-994f-42010a800002   20Gi       RWO           1h
datadir-zk-2   Bound     pvc-bee0817e-bcb1-11e6-994f-42010a800002   20Gi       RWO           1h

StatefulSet 的容器 template 中的 volumeMounts 一节使得 PersistentVolume 被挂载到 ZooKeeper 服务器的数据目录。

volumeMounts:
- name: datadir
  mountPath: /var/lib/zookeeper

zk StatefulSet 中的一个 Pod 被(重新)调度时,它总是拥有相同的 PersistentVolume, 挂载到 ZooKeeper 服务器的数据目录。 即使在 Pod 被重新调度时,所有对 ZooKeeper 服务器的 WAL 的写入和它们的全部快照都仍然是持久的。

确保一致性配置

如同在促成领导者选举达成一致 小节中提到的,ZooKeeper ensemble 中的服务器需要一致性的配置来选举一个领导者并形成一个 quorum。它们还需要 Zab 协议的一致性配置来保证这个协议在网络中正确的工作。 在这次的示例中,我们通过直接将配置写入代码清单中来达到该目的。

获取 zk StatefulSet。

kubectl get sts zk -o yaml
    ...
    command:
      - sh
      - -c
      - "start-zookeeper \
        --servers=3 \
        --data_dir=/var/lib/zookeeper/data \
        --data_log_dir=/var/lib/zookeeper/data/log \
        --conf_dir=/opt/zookeeper/conf \
        --client_port=2181 \
        --election_port=3888 \
        --server_port=2888 \
        --tick_time=2000 \
        --init_limit=10 \
        --sync_limit=5 \
        --heap=512M \
        --max_client_cnxns=60 \
        --snap_retain_count=3 \
        --purge_interval=12 \
        --max_session_timeout=40000 \
        --min_session_timeout=4000 \
        --log_level=INFO"
...

用于启动 ZooKeeper 服务器的命令将这些配置作为命令行参数传给了 ensemble。 你也可以通过环境变量来传入这些配置。

配置日志

zkGenConfig.sh 脚本产生的一个文件控制了 ZooKeeper 的日志行为。 ZooKeeper 使用了 Log4j 并默认使用基于文件大小和时间的滚动文件追加器作为日志配置。

zk StatefulSet 的一个 Pod 中获取日志配置。

kubectl exec zk-0 cat /usr/etc/zookeeper/log4j.properties

下面的日志配置会使 ZooKeeper 进程将其所有的日志写入标志输出文件流中。

zookeeper.root.logger=CONSOLE
zookeeper.console.threshold=INFO
log4j.rootLogger=${zookeeper.root.logger}
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Threshold=${zookeeper.console.threshold}
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n

这是在容器里安全记录日志的最简单的方法。 由于应用的日志被写入标准输出,Kubernetes 将会为你处理日志轮转。 Kubernetes 还实现了一个智能保存策略, 保证写入标准输出和标准错误流的应用日志不会耗尽本地存储介质。

使用命令 kubectl logs 从一个 Pod 中取回最后 20 行日志。

kubectl logs zk-0 --tail 20

使用 kubectl logs 或者从 Kubernetes Dashboard 可以查看写入到标准输出和标准错误流中的应用日志。

2016-12-06 19:34:16,236 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52740
2016-12-06 19:34:16,237 [myid:1] - INFO  [Thread-1136:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52740 (no session established for client)
2016-12-06 19:34:26,155 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52749
2016-12-06 19:34:26,155 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52749
2016-12-06 19:34:26,156 [myid:1] - INFO  [Thread-1137:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52749 (no session established for client)
2016-12-06 19:34:26,222 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52750
2016-12-06 19:34:26,222 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52750
2016-12-06 19:34:26,226 [myid:1] - INFO  [Thread-1138:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52750 (no session established for client)
2016-12-06 19:34:36,151 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52760
2016-12-06 19:34:36,152 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52760
2016-12-06 19:34:36,152 [myid:1] - INFO  [Thread-1139:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52760 (no session established for client)
2016-12-06 19:34:36,230 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52761
2016-12-06 19:34:36,231 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52761
2016-12-06 19:34:36,231 [myid:1] - INFO  [Thread-1140:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52761 (no session established for client)
2016-12-06 19:34:46,149 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52767
2016-12-06 19:34:46,149 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52767
2016-12-06 19:34:46,149 [myid:1] - INFO  [Thread-1141:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52767 (no session established for client)
2016-12-06 19:34:46,230 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52768
2016-12-06 19:34:46,230 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52768
2016-12-06 19:34:46,230 [myid:1] - INFO  [Thread-1142:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52768 (no session established for client)

Kubernetes 支持与多种日志方案集成。 你可以选择一个最适合你的集群和应用的日志解决方案。 对于集群级别的日志输出与整合,可以考虑部署一个 边车容器 来轮转和提供日志数据。

配置非特权用户

在容器中允许应用以特权用户运行这条最佳实践是值得商讨的。 如果你的组织要求应用以非特权用户运行,你可以使用 SecurityContext 控制运行容器入口点所使用的用户。

zk StatefulSet 的 Pod 的 template 包含了一个 SecurityContext

securityContext:
  runAsUser: 1000
  fsGroup: 1000

在 Pod 的容器内部,UID 1000 对应用户 zookeeper,GID 1000 对应用户组 zookeeper。

zk-0 Pod 获取 ZooKeeper 进程信息。

kubectl exec zk-0 -- ps -elf

由于 securityContext 对象的 runAsUser 字段被设置为 1000 而不是 root, ZooKeeper 进程将以 zookeeper 用户运行。

F S UID        PID  PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
4 S zookeep+     1     0  0  80   0 -  1127 -      20:46 ?        00:00:00 sh -c zkGenConfig.sh && zkServer.sh start-foreground
0 S zookeep+    27     1  0  80   0 - 1155556 -    20:46 ?        00:00:19 /usr/lib/jvm/java-8-openjdk-amd64/bin/java -Dzookeeper.log.dir=/var/log/zookeeper -Dzookeeper.root.logger=INFO,CONSOLE -cp /usr/bin/../build/classes:/usr/bin/../build/lib/*.jar:/usr/bin/../share/zookeeper/zookeeper-3.4.9.jar:/usr/bin/../share/zookeeper/slf4j-log4j12-1.6.1.jar:/usr/bin/../share/zookeeper/slf4j-api-1.6.1.jar:/usr/bin/../share/zookeeper/netty-3.10.5.Final.jar:/usr/bin/../share/zookeeper/log4j-1.2.16.jar:/usr/bin/../share/zookeeper/jline-0.9.94.jar:/usr/bin/../src/java/lib/*.jar:/usr/bin/../etc/zookeeper: -Xmx2G -Xms2G -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only=false org.apache.zookeeper.server.quorum.QuorumPeerMain /usr/bin/../etc/zookeeper/zoo.cfg

默认情况下,当 Pod 的 PersistentVolume 被挂载到 ZooKeeper 服务器的数据目录时, 它只能被 root 用户访问。这个配置将阻止 ZooKeeper 进程写入它的 WAL 及保存快照。

zk-0 Pod 上获取 ZooKeeper 数据目录的文件权限。

kubectl exec -ti zk-0 -- ls -ld /var/lib/zookeeper/data

由于 securityContext 对象的 fsGroup 字段设置为 1000, Pod 的 PersistentVolume 的所有权属于 zookeeper 用户组, 因而 ZooKeeper 进程能够成功地读写数据。

drwxr-sr-x 3 zookeeper zookeeper 4096 Dec  5 20:45 /var/lib/zookeeper/data

管理 ZooKeeper 进程

ZooKeeper 文档 指出 “你将需要一个监管程序用于管理每个 ZooKeeper 服务进程(JVM)”。 在分布式系统中,使用一个看门狗(监管程序)来重启故障进程是一种常用的模式。

更新 Ensemble

zk StatefulSet 的更新策略被设置为了 RollingUpdate

你可以使用 kubectl patch 更新分配给每个服务器的 cpus 的数量。

kubectl patch sts zk --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/resources/requests/cpu", "value":"0.3"}]'
statefulset.apps/zk patched

使用 kubectl rollout status 观测更新状态。

kubectl rollout status sts/zk
waiting for statefulset rolling update to complete 0 pods at revision zk-5db4499664...
Waiting for 1 pods to be ready...
Waiting for 1 pods to be ready...
waiting for statefulset rolling update to complete 1 pods at revision zk-5db4499664...
Waiting for 1 pods to be ready...
Waiting for 1 pods to be ready...
waiting for statefulset rolling update to complete 2 pods at revision zk-5db4499664...
Waiting for 1 pods to be ready...
Waiting for 1 pods to be ready...
statefulset rolling update complete 3 pods at revision zk-5db4499664...

这项操作会逆序地依次终止每一个 Pod,并用新的配置重新创建。 这样做确保了在滚动更新的过程中 quorum 依旧保持工作。

使用 kubectl rollout history 命令查看历史或先前的配置。

kubectl rollout history sts/zk

输出类似于:

statefulsets "zk"
REVISION
1
2

使用 kubectl rollout undo 命令撤销这次的改动。

kubectl rollout undo sts/zk

输出类似于:

statefulset.apps/zk rolled back

处理进程故障

重启策略 控制 Kubernetes 如何处理一个 Pod 中容器入口点的进程故障。 对于 StatefulSet 中的 Pod 来说,Always 是唯一合适的 RestartPolicy,也是默认值。 你应该绝不覆盖有状态应用的默认策略。

检查 zk-0 Pod 中运行的 ZooKeeper 服务器的进程树。

kubectl exec zk-0 -- ps -ef

作为容器入口点的命令的 PID 为 1,Zookeeper 进程是入口点的子进程,PID 为 27。

UID        PID  PPID  C STIME TTY          TIME CMD
zookeep+     1     0  0 15:03 ?        00:00:00 sh -c zkGenConfig.sh && zkServer.sh start-foreground
zookeep+    27     1  0 15:03 ?        00:00:03 /usr/lib/jvm/java-8-openjdk-amd64/bin/java -Dzookeeper.log.dir=/var/log/zookeeper -Dzookeeper.root.logger=INFO,CONSOLE -cp /usr/bin/../build/classes:/usr/bin/../build/lib/*.jar:/usr/bin/../share/zookeeper/zookeeper-3.4.9.jar:/usr/bin/../share/zookeeper/slf4j-log4j12-1.6.1.jar:/usr/bin/../share/zookeeper/slf4j-api-1.6.1.jar:/usr/bin/../share/zookeeper/netty-3.10.5.Final.jar:/usr/bin/../share/zookeeper/log4j-1.2.16.jar:/usr/bin/../share/zookeeper/jline-0.9.94.jar:/usr/bin/../src/java/lib/*.jar:/usr/bin/../etc/zookeeper: -Xmx2G -Xms2G -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only=false org.apache.zookeeper.server.quorum.QuorumPeerMain /usr/bin/../etc/zookeeper/zoo.cfg

在一个终端观察 zk StatefulSet 中的 Pod。

kubectl get pod -w -l app=zk

在另一个终端杀掉 Pod zk-0 中的 ZooKeeper 进程。

 kubectl exec zk-0 -- pkill java

ZooKeeper 进程的终结导致了它父进程的终止。由于容器的 RestartPolicy 是 Always,所以父进程被重启。

NAME      READY     STATUS    RESTARTS   AGE
zk-0      1/1       Running   0          21m
zk-1      1/1       Running   0          20m
zk-2      1/1       Running   0          19m
NAME      READY     STATUS    RESTARTS   AGE
zk-0      0/1       Error     0          29m
zk-0      0/1       Running   1         29m
zk-0      1/1       Running   1         29m

如果你的应用使用一个脚本(例如 zkServer.sh)来启动一个实现了应用业务逻辑的进程, 这个脚本必须和子进程一起结束。这保证了当实现应用业务逻辑的进程故障时, Kubernetes 会重启这个应用的容器。

存活性测试

你的应用配置为自动重启故障进程,但这对于保持一个分布式系统的健康来说是不够的。 许多场景下,一个系统进程可以是活动状态但不响应请求,或者是不健康状态。 你应该使用存活性探针来通知 Kubernetes 你的应用进程处于不健康状态,需要被重启。

zk StatefulSet 的 Pod 的 template 一节指定了一个存活探针。

  livenessProbe:
    exec:
      command:
      - sh
      - -c
      - "zookeeper-ready 2181"
    initialDelaySeconds: 15
    timeoutSeconds: 5

这个探针调用一个简单的 Bash 脚本,使用 ZooKeeper 的四字缩写 ruok 来测试服务器的健康状态。

OK=$(echo ruok | nc 127.0.0.1 $1)
if [ "$OK" == "imok" ]; then
    exit 0
else
    exit 1
fi

在一个终端窗口中使用下面的命令观察 zk StatefulSet 中的 Pod。

kubectl get pod -w -l app=zk

在另一个窗口中,从 Pod zk-0 的文件系统中删除 zookeeper-ready 脚本。

kubectl exec zk-0 -- rm /opt/zookeeper/bin/zookeeper-ready

当 ZooKeeper 进程的存活探针探测失败时,Kubernetes 将会为你自动重启这个进程, 从而保证 ensemble 中不健康状态的进程都被重启。

kubectl get pod -w -l app=zk
NAME      READY     STATUS    RESTARTS   AGE
zk-0      1/1       Running   0          1h
zk-1      1/1       Running   0          1h
zk-2      1/1       Running   0          1h
NAME      READY     STATUS    RESTARTS   AGE
zk-0      0/1       Running   0          1h
zk-0      0/1       Running   1         1h
zk-0      1/1       Running   1         1h

就绪性测试

就绪不同于存活。如果一个进程是存活的,它是可调度和健康的。 如果一个进程是就绪的,它应该能够处理输入。存活是就绪的必要非充分条件。 在许多场景下,特别是初始化和终止过程中,一个进程可以是存活但没有就绪的。

如果你指定了一个就绪探针,Kubernetes 将保证在就绪检查通过之前, 你的应用不会接收到网络流量。

对于一个 ZooKeeper 服务器来说,存活即就绪。 因此 zookeeper.yaml 清单中的就绪探针和存活探针完全相同。

  readinessProbe:
    exec:
      command:
      - sh
      - -c
      - "zookeeper-ready 2181"
    initialDelaySeconds: 15
    timeoutSeconds: 5

虽然存活探针和就绪探针是相同的,但同时指定它们两者仍然重要。 这保证了 ZooKeeper ensemble 中只有健康的服务器能接收网络流量。

容忍节点故障

ZooKeeper 需要一个 quorum 来提交数据变动。对于一个拥有 3 个服务器的 ensemble 来说, 必须有两个服务器是健康的,写入才能成功。 在基于 quorum 的系统里,成员被部署在多个故障域中以保证可用性。 为了防止由于某台机器断连引起服务中断,最佳实践是防止应用的多个实例在相同的机器上共存。

默认情况下,Kubernetes 可以把 StatefulSet 的 Pod 部署在相同节点上。 对于你创建的 3 个服务器的 ensemble 来说, 如果有两个服务器并存于相同的节点上并且该节点发生故障时,ZooKeeper 服务将中断, 直至至少其中一个 Pod 被重新调度。

你应该总是提供多余的容量以允许关键系统进程在节点故障时能够被重新调度。 如果你这样做了,服务故障就只会持续到 Kubernetes 调度器重新调度某个 ZooKeeper 服务器为止。 但是,如果希望你的服务在容忍节点故障时无停服时间,你应该设置 podAntiAffinity

使用下面的命令获取 zk StatefulSet 中的 Pod 的节点。

for i in 0 1 2; do kubectl get pod zk-$i --template {{.spec.nodeName}}; echo ""; done

zk StatefulSet 中所有的 Pod 都被部署在不同的节点。

kubernetes-node-cxpk
kubernetes-node-a5aq
kubernetes-node-2g2d

这是因为 zk StatefulSet 中的 Pod 指定了 PodAntiAffinity

affinity:
  podAntiAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
            - key: "app"
              operator: In
              values:
                - zk
        topologyKey: "kubernetes.io/hostname"

requiredDuringSchedulingIgnoredDuringExecution 告诉 Kubernetes 调度器, 在以 topologyKey 指定的域中,绝对不要把带有键为 app、值为 zk 的标签 的两个 Pod 调度到相同的节点。topologyKey kubernetes.io/hostname 表示 这个域是一个单独的节点。 使用不同的规则、标签和选择算符,你能够通过这种技术把你的 ensemble 分布 在不同的物理、网络和电力故障域之间。

节点维护期间保持应用可用

在本节中你将会隔离(Cordon)和腾空(Drain)节点。 如果你是在一个共享的集群里使用本教程,请保证不会影响到其他租户。

上一小节展示了如何在节点之间分散 Pod 以在计划外的节点故障时保证服务存活。 但是你也需要为计划内维护引起的临时节点故障做准备。

使用此命令获取你的集群中的节点。

kubectl get nodes

使用 kubectl cordon 隔离你的集群中除 4 个节点以外的所有节点。

kubectl cordon <node-name>

使用下面的命令获取 zk-pdb PodDisruptionBudget

kubectl get pdb zk-pdb

max-unavailable 字段指示 Kubernetes 在任何时候,zk StatefulSet 至多有一个 Pod 是不可用的。

NAME      MIN-AVAILABLE   MAX-UNAVAILABLE   ALLOWED-DISRUPTIONS   AGE
zk-pdb    N/A             1                 1

在一个终端中,使用下面的命令观察 zk StatefulSet 中的 Pod。

kubectl get pods -w -l app=zk

在另一个终端中,使用下面的命令获取 Pod 当前调度的节点。

for i in 0 1 2; do kubectl get pod zk-$i --template {{.spec.nodeName}}; echo ""; done
kubernetes-node-pb41
kubernetes-node-ixsl
kubernetes-node-i4c4

使用 kubectl drain 来隔离和腾空 zk-0 Pod 调度所在的节点。

kubectl drain $(kubectl get pod zk-0 --template {{.spec.nodeName}}) --ignore-daemonsets --force --delete-emptydir-data

输出类似于:

node "kubernetes-node-pb41" cordoned

WARNING: Deleting pods not managed by ReplicationController, ReplicaSet, Job, or DaemonSet: fluentd-cloud-logging-kubernetes-node-pb41, kube-proxy-kubernetes-node-pb41; Ignoring DaemonSet-managed pods: node-problem-detector-v0.1-o5elz
pod "zk-0" deleted
node "kubernetes-node-pb41" drained

由于你的集群中有 4 个节点, kubectl drain 执行成功,zk-0 被调度到其它节点。

NAME      READY     STATUS    RESTARTS   AGE
zk-0      1/1       Running   2          1h
zk-1      1/1       Running   0          1h
zk-2      1/1       Running   0          1h
NAME      READY     STATUS        RESTARTS   AGE
zk-0      1/1       Terminating   2          2h
zk-0      0/1       Terminating   2         2h
zk-0      0/1       Terminating   2         2h
zk-0      0/1       Terminating   2         2h
zk-0      0/1       Pending   0         0s
zk-0      0/1       Pending   0         0s
zk-0      0/1       ContainerCreating   0         0s
zk-0      0/1       Running   0         51s
zk-0      1/1       Running   0         1m

在第一个终端中持续观察 StatefulSet 的 Pod 并腾空 zk-1 调度所在的节点。

kubectl drain $(kubectl get pod zk-1 --template {{.spec.nodeName}}) --ignore-daemonsets --force --delete-emptydir-data

输出类似于:

kubernetes-node-ixsl" cordoned
WARNING: Deleting pods not managed by ReplicationController, ReplicaSet, Job, or DaemonSet: fluentd-cloud-logging-kubernetes-node-ixsl, kube-proxy-kubernetes-node-ixsl; Ignoring DaemonSet-managed pods: node-problem-detector-v0.1-voc74
pod "zk-1" deleted
node "kubernetes-node-ixsl" drained

zk-1 Pod 不能被调度,这是因为 zk StatefulSet 包含了一个防止 Pod 共存的 PodAntiAffinity 规则,而且只有两个节点可用于调度, 这个 Pod 将保持在 Pending 状态。

kubectl get pods -w -l app=zk

输出类似于:

NAME      READY     STATUS              RESTARTS   AGE
zk-0      1/1       Running             2          1h
zk-1      1/1       Running             0          1h
zk-2      1/1       Running             0          1h
NAME      READY     STATUS              RESTARTS   AGE
zk-0      1/1       Terminating         2          2h
zk-0      0/1       Terminating         2          2h
zk-0      0/1       Terminating         2          2h
zk-0      0/1       Terminating         2          2h
zk-0      0/1       Pending             0          0s
zk-0      0/1       Pending             0          0s
zk-0      0/1       ContainerCreating   0          0s
zk-0      0/1       Running             0          51s
zk-0      1/1       Running             0          1m
zk-1      1/1       Terminating         0          2h
zk-1      0/1       Terminating         0          2h
zk-1      0/1       Terminating         0          2h
zk-1      0/1       Terminating         0          2h
zk-1      0/1       Pending             0          0s
zk-1      0/1       Pending             0          0s

继续观察 StatefulSet 中的 Pod 并腾空 zk-2 调度所在的节点。

kubectl drain $(kubectl get pod zk-2 --template {{.spec.nodeName}}) --ignore-daemonsets --force --delete-emptydir-data

输出类似于:

node "kubernetes-node-i4c4" cordoned

WARNING: Deleting pods not managed by ReplicationController, ReplicaSet, Job, or DaemonSet: fluentd-cloud-logging-kubernetes-node-i4c4, kube-proxy-kubernetes-node-i4c4; Ignoring DaemonSet-managed pods: node-problem-detector-v0.1-dyrog
WARNING: Ignoring DaemonSet-managed pods: node-problem-detector-v0.1-dyrog; Deleting pods not managed by ReplicationController, ReplicaSet, Job, or DaemonSet: fluentd-cloud-logging-kubernetes-node-i4c4, kube-proxy-kubernetes-node-i4c4
There are pending pods when an error occurred: Cannot evict pod as it would violate the pod's disruption budget.
pod/zk-2

使用 CTRL-C 终止 kubectl。

你不能腾空第三个节点,因为驱逐 zk-2 将和 zk-budget 冲突。 然而这个节点仍然处于隔离状态(Cordoned)。

使用 zkCli.shzk-0 取回你的健康检查中输入的数值。

kubectl exec zk-0 zkCli.sh get /hello

由于遵守了 PodDisruptionBudget,服务仍然可用。

WatchedEvent state:SyncConnected type:None path:null
world
cZxid = 0x200000002
ctime = Wed Dec 07 00:08:59 UTC 2016
mZxid = 0x200000002
mtime = Wed Dec 07 00:08:59 UTC 2016
pZxid = 0x200000002
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0

使用 kubectl uncordon 来取消对第一个节点的隔离。

kubectl uncordon kubernetes-node-pb41

输出类似于:

node "kubernetes-node-pb41" uncordoned

zk-1 被重新调度到了这个节点。等待 zk-1 变为 Running 和 Ready 状态。

kubectl get pods -w -l app=zk

输出类似于:

NAME      READY     STATUS             RESTARTS  AGE
zk-0      1/1       Running            2         1h
zk-1      1/1       Running            0         1h
zk-2      1/1       Running            0         1h
NAME      READY     STATUS             RESTARTS  AGE
zk-0      1/1       Terminating        2         2h
zk-0      0/1       Terminating        2         2h
zk-0      0/1       Terminating        2         2h
zk-0      0/1       Terminating        2         2h
zk-0      0/1       Pending            0         0s
zk-0      0/1       Pending            0         0s
zk-0      0/1       ContainerCreating  0         0s
zk-0      0/1       Running            0         51s
zk-0      1/1       Running            0         1m
zk-1      1/1       Terminating        0         2h
zk-1      0/1       Terminating        0         2h
zk-1      0/1       Terminating        0         2h
zk-1      0/1       Terminating        0         2h
zk-1      0/1       Pending            0         0s
zk-1      0/1       Pending            0         0s
zk-1      0/1       Pending            0         12m
zk-1      0/1       ContainerCreating  0         12m
zk-1      0/1       Running            0         13m
zk-1      1/1       Running            0         13m

尝试腾空 zk-2 调度所在的节点。

kubectl drain $(kubectl get pod zk-2 --template {{.spec.nodeName}}) --ignore-daemonsets --force --delete-emptydir-data

输出类似于:

node "kubernetes-node-i4c4" already cordoned
WARNING: Deleting pods not managed by ReplicationController, ReplicaSet, Job, or DaemonSet: fluentd-cloud-logging-kubernetes-node-i4c4, kube-proxy-kubernetes-node-i4c4; Ignoring DaemonSet-managed pods: node-problem-detector-v0.1-dyrog
pod "heapster-v1.2.0-2604621511-wht1r" deleted
pod "zk-2" deleted
node "kubernetes-node-i4c4" drained

这次 kubectl drain 执行成功。

取消第二个节点的隔离,以允许 zk-2 被重新调度。

kubectl uncordon kubernetes-node-ixsl

输出类似于:

node "kubernetes-node-ixsl" uncordoned

你可以同时使用 kubectl drainPodDisruptionBudgets 来保证你的服务在维护过程中仍然可用。 如果使用了腾空操作来隔离节点并在节点离线之前驱逐了 Pod, 那么设置了干扰预算的服务将会遵守该预算。 你应该总是为关键服务分配额外容量,这样它们的 Pod 就能够迅速的重新调度。

清理现场

  • 使用 kubectl uncordon 解除你集群中所有节点的隔离。
  • 你需要删除在本教程中使用的 PersistentVolume 的持久存储介质。 请遵循必须的步骤,基于你的环境、存储配置和制备方法,保证回收所有的存储。

5.7 - Service

5.7.1 - 使用 Service 连接到应用

Kubernetes 连接容器的模型

既然有了一个持续运行、可复制的应用,我们就能够将它暴露到网络上。

Kubernetes 假设 Pod 可与其它 Pod 通信,不管它们在哪个主机上。 Kubernetes 给每一个 Pod 分配一个集群私有 IP 地址,所以没必要在 Pod 与 Pod 之间创建连接或将容器的端口映射到主机端口。 这意味着同一个 Pod 内的所有容器能通过 localhost 上的端口互相连通,集群中的所有 Pod 也不需要通过 NAT 转换就能够互相看到。 本文档的剩余部分详述如何在上述网络模型之上运行可靠的服务。

本教程使用一个简单的 Nginx 服务器来演示概念验证原型。

在集群中暴露 Pod

我们在之前的示例中已经做过,然而让我们以网络连接的视角再重做一遍。 创建一个 Nginx Pod,注意其中包含一个容器端口的规约:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports:
        - containerPort: 80

这使得可以从集群中任何一个节点来访问它。检查节点,该 Pod 正在运行:

kubectl apply -f ./run-my-nginx.yaml
kubectl get pods -l run=my-nginx -o wide
NAME                        READY     STATUS    RESTARTS   AGE       IP            NODE
my-nginx-3800858182-jr4a2   1/1       Running   0          13s       10.244.3.4    kubernetes-minion-905m
my-nginx-3800858182-kna2y   1/1       Running   0          13s       10.244.2.5    kubernetes-minion-ljyd

检查 Pod 的 IP 地址:

kubectl get pods -l run=my-nginx -o custom-columns=POD_IP:.status.podIPs
    POD_IP
    [map[ip:10.244.3.4]]
    [map[ip:10.244.2.5]]

你应该能够通过 ssh 登录到集群中的任何一个节点上,并使用诸如 curl 之类的工具向这两个 IP 地址发出查询请求。 需要注意的是,容器 不会 使用该节点上的 80 端口,也不会使用任何特定的 NAT 规则去路由流量到 Pod 上。 这意味着你可以使用相同的 containerPort 在同一个节点上运行多个 Nginx Pod, 并且可以从集群中任何其他的 Pod 或节点上使用为 Pod 分配的 IP 地址访问到它们。 如果你想的话,你依然可以将宿主节点的某个端口的流量转发到 Pod 中,但是出于网络模型的原因,你不必这么做。

如果对此好奇,请参考 Kubernetes 网络模型

创建 Service

我们有一组在一个扁平的、集群范围的地址空间中运行 Nginx 服务的 Pod。 理论上,你可以直接连接到这些 Pod,但如果某个节点宕机会发生什么呢? Pod 会终止,Deployment 内的 ReplicaSet 将创建新的 Pod,且使用不同的 IP。这正是 Service 要解决的问题。

Kubernetes Service 是集群中提供相同功能的一组 Pod 的抽象表达。 当每个 Service 创建时,会被分配一个唯一的 IP 地址(也称为 clusterIP)。 这个 IP 地址与 Service 的生命周期绑定在一起,只要 Service 存在,它就不会改变。 可以配置 Pod 使它与 Service 进行通信,Pod 知道与 Service 通信将被自动地负载均衡到该 Service 中的某些 Pod 上。

可以使用 kubectl expose 命令为 2 个 Nginx 副本创建一个 Service:

kubectl expose deployment/my-nginx
service/my-nginx exposed

这等价于使用 kubectl create -f 命令及如下的 yaml 文件创建:

apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  ports:
  - port: 80
    protocol: TCP
  selector:
    run: my-nginx

上述规约将创建一个 Service,该 Service 会将所有具有标签 run: my-nginx 的 Pod 的 TCP 80 端口暴露到一个抽象的 Service 端口上(targetPort:容器接收流量的端口;port: 可任意取值的抽象的 Service 端口,其他 Pod 通过该端口访问 Service)。 查看 Service API 对象以了解 Service 所能接受的字段列表。 查看你的 Service 资源:

kubectl get svc my-nginx
NAME       TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
my-nginx   ClusterIP   10.0.162.149   <none>        80/TCP    21s

正如前面所提到的,一个 Service 由一组 Pod 提供支撑。这些 Pod 通过 EndpointSlices 暴露出来。 Service Selector 将持续评估,结果被 POST 到使用标签与该 Service 连接的一个 EndpointSlice。 当 Pod 终止后,它会自动从包含该 Pod 的 EndpointSlices 中移除。 新的能够匹配上 Service Selector 的 Pod 将被自动地为该 Service 添加到 EndpointSlice 中。 检查 Endpoint,注意到 IP 地址与在第一步创建的 Pod 是相同的。

kubectl describe svc my-nginx
Name:                my-nginx
Namespace:           default
Labels:              run=my-nginx
Annotations:         <none>
Selector:            run=my-nginx
Type:                ClusterIP
IP Family Policy:    SingleStack
IP Families:         IPv4
IP:                  10.0.162.149
IPs:                 10.0.162.149
Port:                <unset> 80/TCP
TargetPort:          80/TCP
Endpoints:           10.244.2.5:80,10.244.3.4:80
Session Affinity:    None
Events:              <none>
kubectl get endpointslices -l kubernetes.io/service-name=my-nginx
NAME             ADDRESSTYPE   PORTS   ENDPOINTS               AGE
my-nginx-7vzhx   IPv4          80      10.244.2.5,10.244.3.4   21s

现在,你应该能够从集群中任意节点上使用 curl 命令向 <CLUSTER-IP>:<PORT> 发送请求以访问 Nginx Service。 注意 Service IP 完全是虚拟的,它从来没有走过网络,如果对它如何工作的原理感到好奇, 可以进一步阅读服务代理的内容。

访问 Service

Kubernetes 支持两种查找服务的主要模式:环境变量和 DNS。前者开箱即用,而后者则需要 CoreDNS 集群插件

环境变量

当 Pod 在节点上运行时,kubelet 会针对每个活跃的 Service 为 Pod 添加一组环境变量。 这就引入了一个顺序的问题。为解释这个问题,让我们先检查正在运行的 Nginx Pod 的环境变量(你的环境中的 Pod 名称将会与下面示例命令中的不同):

kubectl exec my-nginx-3800858182-jr4a2 -- printenv | grep SERVICE
KUBERNETES_SERVICE_HOST=10.0.0.1
KUBERNETES_SERVICE_PORT=443
KUBERNETES_SERVICE_PORT_HTTPS=443

能看到环境变量中并没有你创建的 Service 相关的值。这是因为副本的创建先于 Service。 这样做的另一个缺点是,调度器可能会将所有 Pod 部署到同一台机器上,如果该机器宕机则整个 Service 都会离线。 要改正的话,我们可以先终止这 2 个 Pod,然后等待 Deployment 去重新创建它们。 这次 Service 会 先于 副本存在。这将实现调度器级别的 Pod 按 Service 分布(假定所有的节点都具有同样的容量),并提供正确的环境变量:

kubectl scale deployment my-nginx --replicas=0; kubectl scale deployment my-nginx --replicas=2;
kubectl get pods -l run=my-nginx -o wide
NAME                        READY     STATUS    RESTARTS   AGE     IP            NODE
my-nginx-3800858182-e9ihh   1/1       Running   0          5s      10.244.2.7    kubernetes-minion-ljyd
my-nginx-3800858182-j4rm4   1/1       Running   0          5s      10.244.3.8    kubernetes-minion-905m

你可能注意到,Pod 具有不同的名称,这是因为它们是被重新创建的。

kubectl exec my-nginx-3800858182-e9ihh -- printenv | grep SERVICE
KUBERNETES_SERVICE_PORT=443
MY_NGINX_SERVICE_HOST=10.0.162.149
KUBERNETES_SERVICE_HOST=10.0.0.1
MY_NGINX_SERVICE_PORT=80
KUBERNETES_SERVICE_PORT_HTTPS=443

DNS

Kubernetes 提供了一个自动为其它 Service 分配 DNS 名字的 DNS 插件 Service。 你可以通过如下命令检查它是否在工作:

kubectl get services kube-dns --namespace=kube-system
NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)         AGE
kube-dns   ClusterIP   10.0.0.10    <none>        53/UDP,53/TCP   8m

本段剩余的内容假设你已经有一个拥有持久 IP 地址的 Service(my-nginx),以及一个为其 IP 分配名称的 DNS 服务器。 这里我们使用 CoreDNS 集群插件(应用名为 kube-dns), 所以在集群中的任何 Pod 中,你都可以使用标准方法(例如:gethostbyname())与该 Service 通信。 如果 CoreDNS 没有在运行,你可以参照 CoreDNS README 或者安装 CoreDNS 来启用它。 让我们运行另一个 curl 应用来进行测试:

kubectl run curl --image=radial/busyboxplus:curl -i --tty --rm
Waiting for pod default/curl-131556218-9fnch to be running, status is Pending, pod ready: false
Hit enter for command prompt

然后,按回车并执行命令 nslookup my-nginx

[ root@curl-131556218-9fnch:/ ]$ nslookup my-nginx
Server:    10.0.0.10
Address 1: 10.0.0.10

Name:      my-nginx
Address 1: 10.0.162.149

保护 Service

到现在为止,我们只在集群内部访问了 Nginx 服务器。在将 Service 暴露到因特网之前,我们希望确保通信信道是安全的。 为实现这一目的,需要:

  • 用于 HTTPS 的自签名证书(除非已经有了一个身份证书)
  • 使用证书配置的 Nginx 服务器
  • 使 Pod 可以访问证书的 Secret

你可以从 Nginx https 示例获取所有上述内容。 你需要安装 go 和 make 工具。如果你不想安装这些软件,可以按照后文所述的手动执行步骤执行操作。简要过程如下:

make keys KEY=/tmp/nginx.key CERT=/tmp/nginx.crt
kubectl create secret tls nginxsecret --key /tmp/nginx.key --cert /tmp/nginx.crt
secret/nginxsecret created
kubectl get secrets
NAME                  TYPE                                  DATA      AGE
nginxsecret           kubernetes.io/tls                     2         1m

以下是 configmap:

kubectl create configmap nginxconfigmap --from-file=default.conf

你可以在 Kubernetes examples 项目代码仓库中找到 default.conf 示例。

configmap/nginxconfigmap created
kubectl get configmaps
NAME             DATA   AGE
nginxconfigmap   1      114s

你可以使用以下命令来查看 nginxconfigmap ConfigMap 的细节:

kubectl describe configmap  nginxconfigmap

输出类似于:

Name:         nginxconfigmap
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
default.conf:
----
server {
        listen 80 default_server;
        listen [::]:80 default_server ipv6only=on;

        listen 443 ssl;

        root /usr/share/nginx/html;
        index index.html;

        server_name localhost;
        ssl_certificate /etc/nginx/ssl/tls.crt;
        ssl_certificate_key /etc/nginx/ssl/tls.key;

        location / {
                try_files $uri $uri/ =404;
        }
}

BinaryData
====

Events:  <none>

以下是你在运行 make 时遇到问题时要遵循的手动步骤(例如,在 Windows 上):

# 创建公钥和相对应的私钥
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /d/tmp/nginx.key -out /d/tmp/nginx.crt -subj "/CN=my-nginx/O=my-nginx"
# 对密钥实施 base64 编码
cat /d/tmp/nginx.crt | base64
cat /d/tmp/nginx.key | base64

如下所示,使用上述命令的输出来创建 yaml 文件。base64 编码的值应全部放在一行上。

apiVersion: "v1"
kind: "Secret"
metadata:
  name: "nginxsecret"
  namespace: "default"
type: kubernetes.io/tls
data:
  tls.crt: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURIekNDQWdlZ0F3SUJBZ0lKQUp5M3lQK0pzMlpJTUEwR0NTcUdTSWIzRFFFQkJRVUFNQ1l4RVRBUEJnTlYKQkFNVENHNW5hVzU0YzNaak1SRXdEd1lEVlFRS0V3aHVaMmx1ZUhOMll6QWVGdzB4TnpFd01qWXdOekEzTVRKYQpGdzB4T0RFd01qWXdOekEzTVRKYU1DWXhFVEFQQmdOVkJBTVRDRzVuYVc1NGMzWmpNUkV3RHdZRFZRUUtFd2h1CloybHVlSE4yWXpDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBSjFxSU1SOVdWM0IKMlZIQlRMRmtobDRONXljMEJxYUhIQktMSnJMcy8vdzZhU3hRS29GbHlJSU94NGUrMlN5ajBFcndCLzlYTnBwbQppeW1CL3JkRldkOXg5UWhBQUxCZkVaTmNiV3NsTVFVcnhBZW50VWt1dk1vLzgvMHRpbGhjc3paenJEYVJ4NEo5Ci82UVRtVVI3a0ZTWUpOWTVQZkR3cGc3dlVvaDZmZ1Voam92VG42eHNVR0M2QURVODBpNXFlZWhNeVI1N2lmU2YKNHZpaXdIY3hnL3lZR1JBRS9mRTRqakxCdmdONjc2SU90S01rZXV3R0ljNDFhd05tNnNTSzRqYUNGeGpYSnZaZQp2by9kTlEybHhHWCtKT2l3SEhXbXNhdGp4WTRaNVk3R1ZoK0QrWnYvcW1mMFgvbVY0Rmo1NzV3ajFMWVBocWtsCmdhSXZYRyt4U1FVQ0F3RUFBYU5RTUU0d0hRWURWUjBPQkJZRUZPNG9OWkI3YXc1OUlsYkROMzhIYkduYnhFVjcKTUI4R0ExVWRJd1FZTUJhQUZPNG9OWkI3YXc1OUlsYkROMzhIYkduYnhFVjdNQXdHQTFVZEV3UUZNQU1CQWY4dwpEUVlKS29aSWh2Y05BUUVGQlFBRGdnRUJBRVhTMW9FU0lFaXdyMDhWcVA0K2NwTHI3TW5FMTducDBvMm14alFvCjRGb0RvRjdRZnZqeE04Tzd2TjB0clcxb2pGSW0vWDE4ZnZaL3k4ZzVaWG40Vm8zc3hKVmRBcStNZC9jTStzUGEKNmJjTkNUekZqeFpUV0UrKzE5NS9zb2dmOUZ3VDVDK3U2Q3B5N0M3MTZvUXRUakViV05VdEt4cXI0Nk1OZWNCMApwRFhWZmdWQTRadkR4NFo3S2RiZDY5eXM3OVFHYmg5ZW1PZ05NZFlsSUswSGt0ejF5WU4vbVpmK3FqTkJqbWZjCkNnMnlwbGQ0Wi8rUUNQZjl3SkoybFIrY2FnT0R4elBWcGxNSEcybzgvTHFDdnh6elZPUDUxeXdLZEtxaUMwSVEKQ0I5T2wwWW5scE9UNEh1b2hSUzBPOStlMm9KdFZsNUIyczRpbDlhZ3RTVXFxUlU9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
  tls.key: "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2RhaURFZlZsZHdkbFIKd1V5eFpJWmVEZWNuTkFhbWh4d1NpeWF5N1AvOE9ta3NVQ3FCWmNpQ0RzZUh2dGtzbzlCSzhBZi9WemFhWm9zcApnZjYzUlZuZmNmVUlRQUN3WHhHVFhHMXJKVEVGSzhRSHA3VkpMcnpLUC9QOUxZcFlYTE0yYzZ3MmtjZUNmZitrCkU1bEVlNUJVbUNUV09UM3c4S1lPNzFLSWVuNEZJWTZMMDUrc2JGQmd1Z0ExUE5JdWFubm9UTWtlZTRuMG4rTDQKb3NCM01ZUDhtQmtRQlAzeE9JNHl3YjREZXUraURyU2pKSHJzQmlIT05Xc0RadXJFaXVJMmdoY1kxeWIyWHI2UAozVFVOcGNSbC9pVG9zQngxcHJHclk4V09HZVdPeGxZZmcvbWIvNnBuOUYvNWxlQlkrZStjSTlTMkQ0YXBKWUdpCkwxeHZzVWtGQWdNQkFBRUNnZ0VBZFhCK0xkbk8ySElOTGo5bWRsb25IUGlHWWVzZ294RGQwci9hQ1Zkank4dlEKTjIwL3FQWkUxek1yall6Ry9kVGhTMmMwc0QxaTBXSjdwR1lGb0xtdXlWTjltY0FXUTM5SjM0VHZaU2FFSWZWNgo5TE1jUHhNTmFsNjRLMFRVbUFQZytGam9QSFlhUUxLOERLOUtnNXNrSE5pOWNzMlY5ckd6VWlVZWtBL0RBUlBTClI3L2ZjUFBacDRuRWVBZmI3WTk1R1llb1p5V21SU3VKdlNyblBESGtUdW1vVlVWdkxMRHRzaG9reUxiTWVtN3oKMmJzVmpwSW1GTHJqbGtmQXlpNHg0WjJrV3YyMFRrdWtsZU1jaVlMbjk4QWxiRi9DSmRLM3QraTRoMTVlR2ZQegpoTnh3bk9QdlVTaDR2Q0o3c2Q5TmtEUGJvS2JneVVHOXBYamZhRGR2UVFLQmdRRFFLM01nUkhkQ1pKNVFqZWFKClFGdXF4cHdnNzhZTjQyL1NwenlUYmtGcVFoQWtyczJxWGx1MDZBRzhrZzIzQkswaHkzaE9zSGgxcXRVK3NHZVAKOWRERHBsUWV0ODZsY2FlR3hoc0V0L1R6cEdtNGFKSm5oNzVVaTVGZk9QTDhPTm1FZ3MxMVRhUldhNzZxelRyMgphRlpjQ2pWV1g0YnRSTHVwSkgrMjZnY0FhUUtCZ1FEQmxVSUUzTnNVOFBBZEYvL25sQVB5VWs1T3lDdWc3dmVyClUycXlrdXFzYnBkSi9hODViT1JhM05IVmpVM25uRGpHVHBWaE9JeXg5TEFrc2RwZEFjVmxvcG9HODhXYk9lMTAKMUdqbnkySmdDK3JVWUZiRGtpUGx1K09IYnRnOXFYcGJMSHBzUVpsMGhucDBYSFNYVm9CMUliQndnMGEyOFVadApCbFBtWmc2d1BRS0JnRHVIUVV2SDZHYTNDVUsxNFdmOFhIcFFnMU16M2VvWTBPQm5iSDRvZUZKZmcraEppSXlnCm9RN3hqWldVR3BIc3AyblRtcHErQWlSNzdyRVhsdlhtOElVU2FsbkNiRGlKY01Pc29RdFBZNS9NczJMRm5LQTQKaENmL0pWb2FtZm1nZEN0ZGtFMXNINE9MR2lJVHdEbTRpb0dWZGIwMllnbzFyb2htNUpLMUI3MkpBb0dBUW01UQpHNDhXOTVhL0w1eSt5dCsyZ3YvUHM2VnBvMjZlTzRNQ3lJazJVem9ZWE9IYnNkODJkaC8xT2sybGdHZlI2K3VuCnc1YytZUXRSTHlhQmd3MUtpbGhFZDBKTWU3cGpUSVpnQWJ0LzVPbnlDak9OVXN2aDJjS2lrQ1Z2dTZsZlBjNkQKckliT2ZIaHhxV0RZK2Q1TGN1YSt2NzJ0RkxhenJsSlBsRzlOZHhrQ2dZRUF5elIzT3UyMDNRVVV6bUlCRkwzZAp4Wm5XZ0JLSEo3TnNxcGFWb2RjL0d5aGVycjFDZzE2MmJaSjJDV2RsZkI0VEdtUjZZdmxTZEFOOFRwUWhFbUtKCnFBLzVzdHdxNWd0WGVLOVJmMWxXK29xNThRNTBxMmk1NVdUTThoSDZhTjlaMTltZ0FGdE5VdGNqQUx2dFYxdEYKWSs4WFJkSHJaRnBIWll2NWkwVW1VbGc9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K"

现在使用文件创建 Secret:

kubectl apply -f nginxsecrets.yaml
kubectl get secrets
NAME                  TYPE                                  DATA      AGE
nginxsecret           kubernetes.io/tls                     2         1m

现在修改 Nginx 副本以启动一个使用 Secret 中的证书的 HTTPS 服务器以及相应的用于暴露其端口(80 和 443)的 Service:

apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  type: NodePort
  ports:
  - port: 8080
    targetPort: 80
    protocol: TCP
    name: http
  - port: 443
    protocol: TCP
    name: https
  selector:
    run: my-nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 1
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      volumes:
      - name: secret-volume
        secret:
          secretName: nginxsecret
      - name: configmap-volume
        configMap:
          name: nginxconfigmap
      containers:
      - name: nginxhttps
        image: bprashanth/nginxhttps:1.0
        ports:
        - containerPort: 443
        - containerPort: 80
        volumeMounts:
        - mountPath: /etc/nginx/ssl
          name: secret-volume
        - mountPath: /etc/nginx/conf.d
          name: configmap-volume

关于 nginx-secure-app 清单,值得注意的几点如下:

  • 它将 Deployment 和 Service 的规约放在了同一个文件中。
  • Nginx 服务器通过 80 端口处理 HTTP 流量,通过 443 端口处理 HTTPS 流量,而 Nginx Service 则暴露了这两个端口。
  • 每个容器能通过挂载在 /etc/nginx/ssl 的卷访问密钥。卷和密钥需要在 Nginx 服务器启动 之前 配置好。
kubectl delete deployments,svc my-nginx; kubectl create -f ./nginx-secure-app.yaml

这时,你可以从任何节点访问到 Nginx 服务器。

kubectl get pods -l run=my-nginx -o custom-columns=POD_IP:.status.podIPs
    POD_IP
    [map[ip:10.244.3.5]]
node $ curl -k https://10.244.3.5
...
<h1>Welcome to nginx!</h1>

注意最后一步我们是如何提供 -k 参数执行 curl 命令的,这是因为在证书生成时, 我们不知道任何关于运行 nginx 的 Pod 的信息,所以不得不在执行 curl 命令时忽略 CName 不匹配的情况。 通过创建 Service,我们连接了在证书中的 CName 与在 Service 查询时被 Pod 使用的实际 DNS 名字。 让我们从一个 Pod 来测试(为了方便,这里使用同一个 Secret,Pod 仅需要使用 nginx.crt 去访问 Service):

apiVersion: apps/v1
kind: Deployment
metadata:
  name: curl-deployment
spec:
  selector:
    matchLabels:
      app: curlpod
  replicas: 1
  template:
    metadata:
      labels:
        app: curlpod
    spec:
      volumes:
      - name: secret-volume
        secret:
          secretName: nginxsecret
      containers:
      - name: curlpod
        command:
        - sh
        - -c
        - while true; do sleep 1; done
        image: radial/busyboxplus:curl
        volumeMounts:
        - mountPath: /etc/nginx/ssl
          name: secret-volume
kubectl apply -f ./curlpod.yaml
kubectl get pods -l app=curlpod
NAME                               READY     STATUS    RESTARTS   AGE
curl-deployment-1515033274-1410r   1/1       Running   0          1m
kubectl exec curl-deployment-1515033274-1410r -- curl https://my-nginx --cacert /etc/nginx/ssl/tls.crt
...
<title>Welcome to nginx!</title>
...

暴露 Service

对应用的某些部分,你可能希望将 Service 暴露在一个外部 IP 地址上。 Kubernetes 支持两种实现方式:NodePort 和 LoadBalancer。 在上一段创建的 Service 使用了 NodePort,因此,如果你的节点有一个公网 IP,那么 Nginx HTTPS 副本已经能够处理因特网上的流量。

kubectl get svc my-nginx -o yaml | grep nodePort -C 5
  uid: 07191fb3-f61a-11e5-8ae5-42010af00002
spec:
  clusterIP: 10.0.162.149
  ports:
  - name: http
    nodePort: 31704
    port: 8080
    protocol: TCP
    targetPort: 80
  - name: https
    nodePort: 32453
    port: 443
    protocol: TCP
    targetPort: 443
  selector:
    run: my-nginx
kubectl get nodes -o yaml | grep ExternalIP -C 1
    - address: 104.197.41.11
      type: ExternalIP
    allocatable:
--
    - address: 23.251.152.56
      type: ExternalIP
    allocatable:
...

$ curl https://<EXTERNAL-IP>:<NODE-PORT> -k
...
<h1>Welcome to nginx!</h1>

让我们重新创建一个 Service 以使用云负载均衡器。 将 my-nginx Service 的 TypeNodePort 改成 LoadBalancer

kubectl edit svc my-nginx
kubectl get svc my-nginx
NAME       TYPE           CLUSTER-IP     EXTERNAL-IP        PORT(S)               AGE
my-nginx   LoadBalancer   10.0.162.149   xx.xxx.xxx.xxx     8080:30163/TCP        21s
curl https://<EXTERNAL-IP> -k
...
<title>Welcome to nginx!</title>

EXTERNAL-IP 列中的 IP 地址能在公网上被访问到。CLUSTER-IP 只能从集群/私有云网络中访问。

注意,在 AWS 上,类型 LoadBalancer 的服务会创建一个 ELB,且 ELB 使用主机名(比较长),而不是 IP。 ELB 的主机名太长以至于不能适配标准 kubectl get svc 的输出,所以需要通过执行 kubectl describe service my-nginx 命令来查看它。 可以看到类似如下内容:

kubectl describe service my-nginx
...
LoadBalancer Ingress:   a320587ffd19711e5a37606cf4a74574-1142138393.us-east-1.elb.amazonaws.com
...

接下来

5.7.2 - 使用源 IP

运行在 Kubernetes 集群中的应用程序通过 Service 抽象发现彼此并相互通信,它们也用 Service 与外部世界通信。 本文解释了发送到不同类型 Service 的数据包的源 IP 会发生什么情况,以及如何根据需要切换此行为。

准备开始

术语表

本文使用了下列术语:

NAT
网络地址转换
Source NAT
替换数据包上的源 IP;在本页面中,这通常意味着替换为节点的 IP 地址
Destination NAT
替换数据包上的目标 IP;在本页面中,这通常意味着替换为 Pod 的 IP 地址
VIP
一个虚拟 IP 地址,例如分配给 Kubernetes 中每个 Service 的 IP 地址
Kube-proxy
一个网络守护程序,在每个节点上协调 Service VIP 管理

先决条件

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

示例使用一个小型 nginx Web 服务器,服务器通过 HTTP 标头返回它接收到的请求的源 IP。 你可以按如下方式创建它:

kubectl create deployment source-ip-app --image=registry.k8s.io/echoserver:1.4

输出为:

deployment.apps/source-ip-app created

教程目标

  • 通过多种类型的 Service 暴露一个简单应用
  • 了解每种 Service 类型如何处理源 IP NAT
  • 了解保留源 IP 所涉及的权衡

Type=ClusterIP 类型 Service 的源 IP

如果你在 iptables 模式(默认)下运行 kube-proxy,则从集群内发送到 ClusterIP 的数据包永远不会进行源 NAT。 你可以通过在运行 kube-proxy 的节点上获取 http://localhost:10249/proxyMode 来查询 kube-proxy 模式。

kubectl get nodes

输出类似于:

NAME                           STATUS     ROLES    AGE     VERSION
kubernetes-node-6jst   Ready      <none>   2h      v1.13.0
kubernetes-node-cx31   Ready      <none>   2h      v1.13.0
kubernetes-node-jj1t   Ready      <none>   2h      v1.13.0

在其中一个节点上获取代理模式(kube-proxy 监听 10249 端口):

# 在要查询的节点上的 Shell 中运行
curl http://localhost:10249/proxyMode

输出为:

iptables

你可以通过在源 IP 应用程序上创建 Service 来测试源 IP 保留:

kubectl expose deployment source-ip-app --name=clusterip --port=80 --target-port=8080

输出为:

service/clusterip exposed
kubectl get svc clusterip

输出类似于:

NAME         TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
clusterip    ClusterIP   10.0.170.92   <none>        80/TCP    51s

并从同一集群中的 Pod 中访问 ClusterIP

kubectl run busybox -it --image=busybox:1.28 --restart=Never --rm

输出类似于:

Waiting for pod default/busybox to be running, status is Pending, pod ready: false
If you don't see a command prompt, try pressing enter.

然后,你可以在该 Pod 中运行命令:

# 从 “kubectl run” 的终端中运行
ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
3: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1460 qdisc noqueue
    link/ether 0a:58:0a:f4:03:08 brd ff:ff:ff:ff:ff:ff
    inet 10.244.3.8/24 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::188a:84ff:feb0:26a5/64 scope link
       valid_lft forever preferred_lft forever

然后使用 wget 查询本地 Web 服务器:

# 将 “10.0.170.92” 替换为 Service 中名为 “clusterip” 的 IPv4 地址
wget -qO - 10.0.170.92
CLIENT VALUES:
client_address=10.244.3.8
command=GET
...

不管客户端 Pod 和服务器 Pod 位于同一节点还是不同节点,client_address 始终是客户端 Pod 的 IP 地址。

Type=NodePort 类型 Service 的源 IP

默认情况下,发送到 Type=NodePort 的 Service 的数据包会经过源 NAT 处理。你可以通过创建一个 NodePort 的 Service 来测试这点:

kubectl expose deployment source-ip-app --name=nodeport --port=80 --target-port=8080 --type=NodePort

输出为:

service/nodeport exposed
NODEPORT=$(kubectl get -o jsonpath="{.spec.ports[0].nodePort}" services nodeport)
NODES=$(kubectl get nodes -o jsonpath='{ $.items[*].status.addresses[?(@.type=="InternalIP")].address }')

如果你在云供应商上运行,你可能需要为上面报告的 nodes:nodeport 打开防火墙规则。 现在你可以尝试通过上面分配的节点端口从集群外部访问 Service。

for node in $NODES; do curl -s $node:$NODEPORT | grep -i client_address; done

输出类似于:

client_address=10.180.1.1
client_address=10.240.0.5
client_address=10.240.0.3

请注意,这些并不是正确的客户端 IP,它们是集群的内部 IP。这是所发生的事情:

  • 客户端发送数据包到 node2:nodePort
  • node2 使用它自己的 IP 地址替换数据包的源 IP 地址(SNAT)
  • node2 将数据包上的目标 IP 替换为 Pod IP
  • 数据包被路由到 node1,然后到端点
  • Pod 的回复被路由回 node2
  • Pod 的回复被发送回给客户端

用图表示:

图 1:源 IP NodePort

如图。使用 SNAT 的源 IP(Type=NodePort)

为避免这种情况,Kubernetes 有一个特性可以保留客户端源 IP。 如果将 service.spec.externalTrafficPolicy 设置为 Local, kube-proxy 只会将代理请求代理到本地端点,而不会将流量转发到其他节点。 这种方法保留了原始源 IP 地址。如果没有本地端点,则发送到该节点的数据包将被丢弃, 因此你可以在任何数据包处理规则中依赖正确的源 IP,你可能会应用一个数据包使其通过该端点。

设置 service.spec.externalTrafficPolicy 字段如下:

kubectl patch svc nodeport -p '{"spec":{"externalTrafficPolicy":"Local"}}'

输出为:

service/nodeport patched

现在,重新运行测试:

for node in $NODES; do curl --connect-timeout 1 -s $node:$NODEPORT | grep -i client_address; done

输出类似于:

client_address=198.51.100.79

请注意,你只从运行端点 Pod 的节点得到了回复,这个回复有正确的客户端 IP。

这是发生的事情:

  • 客户端将数据包发送到没有任何端点的 node2:nodePort
  • 数据包被丢弃
  • 客户端发送数据包到必有端点的 node1:nodePort
  • node1 使用正确的源 IP 地址将数据包路由到端点

用图表示:

图 2:源 IP NodePort

如图。源 IP(Type=NodePort)保存客户端源 IP 地址

Type=LoadBalancer 类型 Service 的源 IP

默认情况下,发送到 Type=LoadBalancer 的 Service 的数据包经过源 NAT处理,因为所有处于 Ready 状态的可调度 Kubernetes 节点对于负载均衡的流量都是符合条件的。 因此,如果数据包到达一个没有端点的节点,系统会将其代理到一个带有端点的节点,用该节点的 IP 替换数据包上的源 IP(如上一节所述)。

你可以通过负载均衡器上暴露 source-ip-app 进行测试:

kubectl expose deployment source-ip-app --name=loadbalancer --port=80 --target-port=8080 --type=LoadBalancer

输出为:

service/loadbalancer exposed

打印 Service 的 IP 地址:

kubectl get svc loadbalancer

输出类似于:

NAME           TYPE           CLUSTER-IP    EXTERNAL-IP       PORT(S)   AGE
loadbalancer   LoadBalancer   10.0.65.118   203.0.113.140     80/TCP    5m

接下来,发送请求到 Service 的 的外部 IP(External-IP):

curl 203.0.113.140

输出类似于:

CLIENT VALUES:
client_address=10.240.0.5
...

然而,如果你在 Google Kubernetes Engine/GCE 上运行, 将相同的 service.spec.externalTrafficPolicy 字段设置为 Local, 故意导致健康检查失败,从而强制没有端点的节点把自己从负载均衡流量的可选节点列表中删除。

用图表示:

具有 externalTrafficPolicy 的源 IP

你可以通过设置注解进行测试:

kubectl patch svc loadbalancer -p '{"spec":{"externalTrafficPolicy":"Local"}}'

你应该能够立即看到 Kubernetes 分配的 service.spec.healthCheckNodePort 字段:

kubectl get svc loadbalancer -o yaml | grep -i healthCheckNodePort

输出类似于:

  healthCheckNodePort: 32122

service.spec.healthCheckNodePort 字段指向每个在 /healthz 路径上提供健康检查的节点的端口。你可以这样测试:

kubectl get pod -o wide -l app=source-ip-app

输出类似于:

NAME                            READY     STATUS    RESTARTS   AGE       IP             NODE
source-ip-app-826191075-qehz4   1/1       Running   0          20h       10.180.1.136   kubernetes-node-6jst

使用 curl 获取各个节点上的 /healthz 端点:

# 在你选择的节点上本地运行
curl localhost:32122/healthz
1 Service Endpoints found

在不同的节点上,你可能会得到不同的结果:

# 在你选择的节点上本地运行
curl localhost:32122/healthz
No Service Endpoints Found

控制平面上运行的控制器负责分配云负载均衡器。 同一个控制器还在每个节点上分配指向此端口/路径的 HTTP 健康检查。 等待大约 10 秒,让 2 个没有端点的节点健康检查失败,然后使用 curl 查询负载均衡器的 IPv4 地址:

curl 203.0.113.140

输出类似于:

CLIENT VALUES:
client_address=198.51.100.79
...

跨平台支持

只有部分云提供商为 Type=LoadBalancer 的 Service 提供保存源 IP 的支持。 你正在运行的云提供商可能会以几种不同的方式满足对负载均衡器的请求:

  1. 使用终止客户端连接并打开到你的节点/端点的新连接的代理。 在这种情况下,源 IP 将始终是云 LB 的源 IP,而不是客户端的源 IP。

  2. 使用数据包转发器,这样客户端发送到负载均衡器 VIP 的请求最终会到达具有客户端源 IP 的节点,而不是中间代理。

第一类负载均衡器必须使用负载均衡器和后端之间商定的协议来传达真实的客户端 IP, 例如 HTTP 转发X-FORWARDED-FOR 标头,或代理协议。 第二类负载均衡器可以通过创建指向存储在 Service 上的 service.spec.healthCheckNodePort 字段中的端口的 HTTP 健康检查来利用上述功能。

清理现场

删除 Service:

kubectl delete svc -l app=source-ip-app

删除 Deployment、ReplicaSet 和 Pod:

kubectl delete deployment source-ip-app

接下来

5.7.3 - 探索 Pod 及其端点的终止行为

一旦你参照使用 Service 连接到应用中概述的那些步骤使用 Service 连接到了你的应用,你就有了一个持续运行的多副本应用暴露在了网络上。 本教程帮助你了解 Pod 的终止流程,探索实现连接排空的几种方式。

Pod 及其端点的终止过程

你经常会遇到需要终止 Pod 的场景,例如为了升级或缩容。 为了改良应用的可用性,实现一种合适的活跃连接排空机制变得重要。

本教程将通过使用一个简单的 nginx Web 服务器演示此概念, 解释 Pod 终止的流程及其与相应端点状态和移除的联系。

端点终止的示例流程

以下是 Pod 终止文档中所述的流程示例。

假设你有包含单个 nginx 副本(仅用于演示目的)的一个 Deployment 和一个 Service:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      terminationGracePeriodSeconds: 120 # 超长优雅期
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80
        lifecycle:
          preStop:
            exec:
              # 实际生产环境中的 Pod 终止可能需要执行任何时长,但不会超过 terminationGracePeriodSeconds。
              # 在本例中,只需挂起至少 terminationGracePeriodSeconds 所指定的持续时间,
              # 在 120 秒时容器将被强制终止。
              # 请注意,在所有这些时间点 nginx 都将继续处理请求。
              command: [
                "/bin/sh", "-c", "sleep 180"
              ]
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app: nginx
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80

现在使用以上文件创建 Deployment Pod 和 Service:

kubectl apply -f pod-with-graceful-termination.yaml
kubectl apply -f explore-graceful-termination-nginx.yaml

一旦 Pod 和 Service 开始运行,你就可以获取对应的所有 EndpointSlices 的名称:

kubectl get endpointslice

输出类似于:

NAME                  ADDRESSTYPE   PORTS   ENDPOINTS                 AGE
nginx-service-6tjbr   IPv4          80      10.12.1.199,10.12.1.201   22m

你可以查看其 status 并验证已经有一个端点被注册:

kubectl get endpointslices -o json -l kubernetes.io/service-name=nginx-service

输出类似于:

{
    "addressType": "IPv4",
    "apiVersion": "discovery.k8s.io/v1",
    "endpoints": [
        {
            "addresses": [
                "10.12.1.201"
            ],
            "conditions": {
                "ready": true,
                "serving": true,
                "terminating": false
                          }
        }
    ]
}

现在让我们终止这个 Pod 并验证该 Pod 正在遵从体面终止期限的配置进行终止:

kubectl delete pod nginx-deployment-7768647bf9-b4b9s

查看所有 Pod:

kubectl get pods

输出类似于:

NAME                                READY   STATUS        RESTARTS      AGE
nginx-deployment-7768647bf9-b4b9s   1/1     Terminating   0             4m1s
nginx-deployment-7768647bf9-rkxlw   1/1     Running       0             8s

你可以看到新的 Pod 已被调度。

当系统在为新的 Pod 创建新的端点时,旧的端点仍处于 Terminating 状态:

kubectl get endpointslice -o json nginx-service-6tjbr

输出类似于:

{
    "addressType": "IPv4",
    "apiVersion": "discovery.k8s.io/v1",
    "endpoints": [
        {
            "addresses": [
                "10.12.1.201"
            ],
            "conditions": {
                "ready": false,
                "serving": true,
                "terminating": true
            },
            "nodeName": "gke-main-default-pool-dca1511c-d17b",
            "targetRef": {
                "kind": "Pod",
                "name": "nginx-deployment-7768647bf9-b4b9s",
                "namespace": "default",
                "uid": "66fa831c-7eb2-407f-bd2c-f96dfe841478"
            },
            "zone": "us-central1-c"
        },
    ]
        {
            "addresses": [
                "10.12.1.202"
            ],
            "conditions": {
                "ready": true,
                "serving": true,
                "terminating": false
            },
            "nodeName": "gke-main-default-pool-dca1511c-d17b",
            "targetRef": {
                "kind": "Pod",
                "name": "nginx-deployment-7768647bf9-rkxlw",
                "namespace": "default",
                "uid": "722b1cbe-dcd7-4ed4-8928-4a4d0e2bbe35"
            },
            "zone": "us-central1-c"
        }
}

这种设计使得应用可以在终止期间公布自己的状态,而客户端(如负载均衡器)则可以实现连接排空功能。 这些客户端可以检测到正在终止的端点,并为这些端点实现特殊的逻辑。

在 Kubernetes 中,正在终止的端点始终将其 ready 状态设置为 false。 这是为了满足向后兼容的需求,确保现有的负载均衡器不会将 Pod 用于常规流量。 如果需要排空正被终止的 Pod 上的流量,可以将 serving 状况作为实际的就绪状态。

当 Pod 被删除时,旧的端点也会被删除。

接下来

6 - 参考

这是 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

外部 API

这些是 Kubernetes 项目所定义的 API,但不是由核心项目实现的:

设计文档

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

6.1 - 词汇表

6.2 - API 概述

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

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

Kubernetes API 参考 列出了 Kubernetes v1.30 版本的 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 中存储。

接下来

6.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/*)。 名字空间作用域的资源类型会在其名字空间被删除时也被删除, 并且对该资源类型的访问是由定义在名字空间域中的授权检查来控制的。

注意: 核心资源使用 /api 而不是 /apis,并且不包含 GROUP 路径段。

例如:

  • /api/v1/namespaces
  • /api/v1/pods
  • /api/v1/namespaces/my-namespace/pods
  • /apis/apps/v1/deployments
  • /apis/apps/v1/namespaces/my-namespace/deployments
  • /apis/apps/v1/namespaces/my-namespace/deployments/my-deployment

你还可以访问资源集合(例如:列出所有 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 操作 (例如 createdeletepatchupdate)的通知。 每个更改通知都是一个 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.27 [alpha]

在大型集群检索某些资源类型的集合可能会导致控制平面的资源使用量(主要是 RAM)显著增加。 为了减轻其影响并简化 list + watch 模式的用户体验, Kubernetes 1.27 版本引入了一个 alpha 功能,支持在 watch 请求中请求初始状态 (之前在 list 请求中请求)。

如果启用了 WatchList 特性门控, 可以通过在 watch 请求中指定 sendInitialEvents=true 作为查询字符串参数来实现这一功能。 如果指定了这个参数,API 服务器将使用合成的初始事件(类型为 ADDED)来启动监视流, 以构建所有现有对象的完整状态;如果请求还带有 allowWatchBookmarks=true 选项, 则继续发送 BOOKMARK 事件。 BOOKMARK 事件包括已被同步的资源版本。 发送 BOOKMARK 事件后,API 服务器会像处理所有其他 watch 请求一样继续执行。

当你在查询字符串中设置 sendInitialEvents=true 时, Kubernetes 还要求你将 resourceVersionMatch 的值设置为 NotOlderThan。 如果你在查询字符串中提供 resourceVersion 而没有提供值或者根本没有提供这个参数, 这一请求将被视为 一致性读(Consistent Read) 请求; 当状态至少被同步到开始处理一致性读操作时,才会发送 BOOKMARK 事件。 如果你(在查询字符串中)指定了 resourceVersion,则只要需要等状态同步到所给资源版本时, BOOKMARK 事件才会被发送。

示例

举个例子:你想监视一组 Pod。对于该集合,当前资源版本为 10245,并且有两个 Pod:foobar。 接下来你发送了以下请求(通过使用 resourceVersion= 设置空的资源版本来明确请求一致性读), 这样做的结果是可能收到如下事件序列:

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

{
  "type": "ADDED",
  "object": {"kind": "Pod", "apiVersion": "v1", "metadata": {"resourceVersion": "8467", "name": "foo"}, ...}
}
{
  "type": "ADDED",
  "object": {"kind": "Pod", "apiVersion": "v1", "metadata": {"resourceVersion": "5726", "name": "bar"}, ...}
}
{
  "type": "BOOKMARK",
  "object": {"kind": "Pod", "apiVersion": "v1", "metadata": {"resourceVersion": "10245"} }
}
...
<followed by regular watch stream starting from resourceVersion="10245">

响应压缩

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

APIResponseCompression 是一个选项,允许 API 服务器压缩 getlist 请求的响应, 减少占用的网络带宽并提高大规模集群的性能。此选项自 Kubernetes 1.16 以来默认启用, 可以通过在 API 服务器上的 --feature-gates 标志中包含 APIResponseCompression=false 来禁用。

特别是对于大型资源或集合, API 响应压缩可以显著减小其响应的大小。例如,针对 Pod 的 list 请求可能会返回数百 KB 甚至几 MB 的数据, 具体大小取决于 Pod 数量及其属性。通过压缩响应,可以节省网络带宽并降低延迟。

要验证 APIResponseCompression 是否正常工作,你可以使用一个 Accept-Encoding 头向 API 服务器发送一个 getlist 请求,并检查响应大小和头信息。例如:

GET /api/v1/pods
Accept-Encoding: gzip
---
200 OK
Content-Type: application/json
content-encoding: gzip
...

content-encoding 头表示响应使用 gzip 进行了压缩。

分块检视大体量结果

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

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

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 动词 getcreateupdatepatchdeleteproxy 仅支持单一资源。 这些具有单一资源支持的动词不支持在有序或无序列表或事务中一起提交多个资源。

当客户端(包括 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.27 [stable]

从 1.25 开始,当使用可以提交数据的 HTTP 动词(POSTPUTPATCH)时, 将通过服务器上的校验检测到对象中无法识别或重复的字段。 校验的级别可以是 IgnoreWarn(默认值) 和 Strict 之一。

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

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

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

字段校验级别可通过查询参数 fieldValidation 来设置。

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

kubectl 工具使用 --validate 标志设置字段校验层级。 该字段可取的值包括 ignorewarnstrict,同时还接受值 true(相当于 strict)和 false(相当于 ignore)。 kubectl 默认的校验设置是 --validate=true ,这意味着执行严格的服务端字段校验。

当 kubectl 无法连接到启用字段校验的 API 服务器(Kubernetes 1.27 之前的 API 服务器)时, 将回退到使用客户端的字段校验。 客户端校验将在 kubectl 未来版本中被完全删除。

试运行

特性状态: 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 提供了多种更新现有对象的方式。 你可以阅读选择更新机制以了解哪种方法可能最适合你的用例。

你可以使用 HTTP PUT 覆盖(update)ConfigMap 等现有资源。 对于 PUT 请求,客户端需要指定 resourceVersion(从要更新的对象中获取此项)。 Kubernetes 使用该 resourceVersion 信息,这样 API 服务器可以检测丢失的更新并拒绝对集群来说过期的客户端所发出的请求。 如果资源已发生变化(即客户端提供的 resourceVersion 已过期),API 服务器将返回 409 Conflict 错误响应。

客户端除了发送 PUT 请求之外,还可以发送指令给 API 服务器对现有资源执行 patch 操作。 patch 通常适用于客户端希望进行的更改并不依赖于现有数据的场景。 需要有效检测丢失更新的客户端应该考虑根据现有 resourceVersion 来进行有条件的请求 (HTTP PUT 或 HTTP PATCH),并在存在冲突时作必要的重试。

Kubernetes API 支持四种不同的 PATCH 操作,具体取决于它们所对应的 HTTP Content-Type 标头:

application/apply-patch+yaml
Server Side Apply YAML(基于 YAML 的 Kubernetes 扩展)。 所有 JSON 文档都是有效的 YAML,因此你也可以使用此媒体类型提交 JSON。 更多细节参阅服务器端应用序列化。 对于 Kubernetes,这一 PATCH 请求在对象不存在时成为 create 操作;在对象已存在时成为 patch 操作。
application/json-patch+json
JSON Patch,如 RFC6902 中定义。 JSON Patch 是对资源执行的一个操作序列;例如 {"op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ]}。 对于 Kubernetes,这一 PATCH 请求即是一个 patch 操作。

使用 application/json-patch+jsonpatch 可以包含用于验证一致性的条件, 如果这些条件不满足,则允许此操作失败(例如避免丢失更新)。

application/merge-patch+json
JSON Merge Patch,如 RFC7386 中定义。 JSON Merge Patch 实质上是资源的部分表示。提交的 JSON 与当前资源合并以创建一个新资源,然后将其保存。 对于 Kubernetes,这个 PATCH 请求是一个 patch 操作。
application/strategic-merge-patch+json
Strategic Merge Patch(基于 JSON 的 Kubernetes 扩展)。 Strategic Merge Patch 是 JSON Merge Patch 的自定义实现。 你只能在内置 API 或具有特殊支持的聚合 API 服务器中使用 Strategic Merge Patch。 你不能针对任何使用 CustomResourceDefinition 定义的 API 来使用 application/strategic-merge-patch+json

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

对于服务器端应用,Kubernetes 在对象尚不存在时将请求视为 create,否则视为 patch。 对于其他在 HTTP 层面使用 PATCH 的请求,逻辑上的 Kubernetes 操作始终是 patch

更多细节参阅服务器端应用

选择更新机制

HTTP PUT 替换现有资源

update(HTTP PUT)操作实现简单且灵活,但也存在一些缺点:

  • 你需要处理对象的 resourceVersion 在客户端读取和写回之间发生变化所造成的冲突。 Kubernetes 总是会检测到此冲突,但你作为客户端开发者需要实现重试机制。
  • 如果你在本地解码对象,可能会意外丢失字段。例如你在使用 client-go 时, 可能会收到客户端不知道如何处理的一些字段,而客户端在构造更新时会将这些字段丢弃。
  • 如果对象上存在大量争用(即使是在你不打算编辑的某字段或字段集上),你可能会难以发送更新。 对于体量较大或字段较多的对象,这个问题会更为严重。

使用 JSON Patch 的 HTTP PATCH

patch 更新很有帮助,因为:

  • 由于你只发送差异,所以你在 PATCH 请求中需要发送的数据较少。
  • 你可以依赖于现有值进行更改,例如将特定字段的值复制到注解中。
  • update(HTTP PUT)不同,即使存在对无关字段的频繁更改,你的更改也可以立即生效: 你通常无需重试。
    • 如果你要特别小心避免丢失更新,仍然可能需要指定 resourceVersion(以匹配现有对象)。
    • 编写一些重试逻辑以处理错误仍然是一个良好的实践。
  • 你可以通过测试条件来精确地构造特定的更新条件。 例如,如果现有值与你期望的值匹配,你可以递增计数器而无需读取它。 即使自上次写入以来对象以其他方式发生了更改,你也可以做到这一点而不会遇到丢失更新的风险。 (如果测试条件失败,你可以回退为读取当前值,然后写回更改的数字)。

然而:

  • 你需要更多本地(客户端)逻辑来构建补丁;如果你拥有实现了 JSON Patch 的库, 或者针对 Kubernetes 生成特定的 JSON Patch 的库,将非常有帮助。
  • 作为客户端软件的开发者,你在构建补丁(HTTP 请求体)时需要小心,避免丢弃字段(操作顺序很重要)。

使用服务器端应用的 HTTP PATCH

服务器端应用(Server-Side Apply)具有一些明显的优势:

  • 仅需一次轮询:通常无需先执行 GET 请求。
    • 并且你仍然可以检测到意外更改造成的冲突
    • 合适的时候,你可以选择强制覆盖冲突
  • 客户端实现简单
  • 你可以轻松获得原子级别的 create 或 update 操作,无需额外工作 (类似于某些 SQL 语句中的 UPSERT

然而:

  • 服务器端应用不适合依赖对象当前值的字段更改
  • 你只能更新对象。Kubernetes HTTP API 中的某些资源不是对象(它们没有 .metadata 字段), 并且服务器端应用只能用于 Kubernetes 对象。

资源版本

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

你不能假设资源版本是数字的或可排序的。 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 服务器可能会无限期地等待(直到请求超时)资源版本变为可用。

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

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

Kubernetes 支持多个应用程序协作管理一个对象的字段。 服务器端应用为集群的控制平面提供了一种可选机制,用于跟踪对对象字段的更改。 在特定资源级别,服务器端应用记录并跟踪有关控制该对象字段的信息。

服务器端应用协助用户和控制器通过声明式配置的方式管理他们的资源。 客户提交他们完整描述的意图,声明式地创建和修改对象

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

与客户端应用对比小节解释了服务器端应用与最初的客户端 kubectl apply 实现的区别。

字段管理

Kubernetes API 服务器跟踪所有新建对象的受控字段(Managed Fields)

当尝试应用对象时,由另一个管理器拥有的字段且具有不同值,将导致冲突。 这样做是为了表明操作可能会撤消另一个合作者的更改。 可以强制写入具有托管字段的对象,在这种情况下,任何冲突字段的值都将被覆盖,并且所有权将被转移。

每当字段的值确实发生变化时,所有权就会从其当前管理器转移到进行更改的管理器。

服务器端应用会检查是否存在其他字段管理器也拥有该字段。 如果该字段不属于任何其他字段管理器,则该字段将被设置为其默认值(如果有),或者以其他方式从对象中删除。 同样的规则也适用于作为列表(list)、关联列表或键值对(map)的字段。

用户管理字段这件事,在服务器端应用的场景中,意味着用户依赖并期望字段的值不要改变。 最后一次对字段值做出断言的用户将被记录到当前字段管理器。 这可以通过发送 POSTcreate)、PUTupdate)、或非应用的 PATCHpatch) 显式更改字段管理器详细信息来实现。 还可以通过在服务器端应用操作中包含字段的值来声明和记录字段管理器。

如果两个或以上的应用者均把同一个字段设置为相同值,他们将共享此字段的所有权。 后续任何改变共享字段值的尝试,不管由那个应用者发起,都会导致冲突。 共享字段的所有者可以放弃字段的所有权,这只需发出不包含该字段的服务器端应用 patch 请求即可。

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

如果从清单中删除某个字段并应用该清单,则服务器端应用会检查是否有其他字段管理器也拥有该字段。 如果该字段不属于任何其他字段管理器,则服务器会将其从活动对象中删除,或者重置为其默认值(如果有)。 同样的规则也适用于关联列表(list)或键值对(map)。

与(旧版)由 kubectl 所管理的注解 kubectl.kubernetes.io/last-applied configuration 相比,服务器端应用使用了一种更具声明式的方法, 它跟踪用户(或客户端)的字段管理,而不是用户上次应用的状态。 作为服务器端应用的副作用,哪个字段管理器管理的对象的哪个字段的相关信息也会变得可用。

示例

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

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: test-cm
  namespace: default
  labels:
    test-label: test
  managedFields:
  - manager: kubectl
    operation: Apply # 注意大写: “Apply” (或者 “Update”)
    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

示例的 ConfigMap 对象在 .metadata.managedFields 中包含字段管理记录。 字段管理记录包括关于管理实体本身的基本信息,以及关于被管理的字段和相关操作(ApplyUpdate)的详细信息。 如果最后更改该字段的请求是服务器端应用的patch操作,则 operation 的值为 Apply;否则为 Update

还有另一种可能的结果。客户端会提交无效的请求体。 如果完整描述的意图没有构造出有效的对象,则请求失败。

但是,可以通过 update 或不使用服务器端应用的 patch 操作去更新 .metadata.managedFields。 强烈不鼓励这么做,但当发生如下情况时, 比如 managedFields 进入不一致的状态(显然不应该发生这种情况), 这么做也是一个合理的尝试。

managedFields 的格式在 Kubernetes API 参考中描述

冲突

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

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

  • 不覆盖前值,放弃管理权: 如果应用者不再关注该字段的值, 应用者可以从资源的本地模型中删掉它,并在省略该字段的情况下发送请求。 这就保持了原值不变,并从 managedFields 的应用者条目中删除该字段。

  • 不覆盖前值,成为共享的管理器: 如果应用者仍然关注字段值,并不想覆盖它, 他们可以更改资源的本地模型中该字段的值,以便与服务器上对象的值相匹配, 然后基于本地更新发出新请求。这样做会保持值不变, 并使得该字段的管理由应用者与已经声称管理该字段的所有其他字段管理者共享。

字段管理器

管理器识别出正在修改对象的工作流程(在冲突时尤其有用), 并且可以作为修改请求的一部分,通过 fieldManager 查询参数来指定。 当你 Apply 某个资源时,需要指定 fieldManager 参数。 对于其他更新,API 服务器使用 “User-Agent:” HTTP 头(如果存在)推断字段管理器标识。

当你使用 kubectl 工具执行服务器端应用操作时,kubectl 默认情况下会将管理器标识设置为 “kubectl”

序列化

在协议层面,Kubernetes 用 YAML 来表示 Server-Side Apply 的消息体, 媒体类型为 application/apply-patch+yaml

序列化与 Kubernetes 对象相同,只是客户端不需要发送完整的对象。

以下是服务器端应用消息正文的示例(完整描述的意图):

{
  "apiVersion": "v1",
  "kind": "ConfigMap"
}

(这个请求将导致无更改的更新,前提是它作为 patch 请求的主体发送到有效的 v1/configmaps 资源, 并且请求中设置了合适的 Content-Type)。

字段管理范围内的操作

考虑字段管理的 Kubernetes API 操作包括:

  1. 服务器端应用(HTTP PATCH,内容类型为 application/apply-patch+yaml
  2. 替换现有对象(对 Kubernetes 而言是 update;HTTP 层面表现为 PUT

这两种操作都会更新 .metadata.managedFields,但行为略有不同。

除非你指定要强制重写,否则应用操作在遇到字段级冲突时总是会失败; 相比之下,如果你使用 update 进行的更改会影响托管字段,那么冲突从来不会导致操作失败。

所有服务器端应用的 patch 请求都必须提供 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 的方式运行。 更新操作执行成功,并更改了 data 字段中的一个值, 并使得该字段的管理器被改为 kube-controller-manager

如果尝试把更新操作改为服务器端应用,那么这一尝试会因为所有权冲突的原因,导致操作失败。

合并策略

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

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

Kubernetes API(以及为 Kubernetes 实现该 API 的 Go 代码)都允许定义合并策略标记(Merge Strategy Markers)。 这些标记描述 Kubernetes 对象中各字段所支持的合并策略。 Kubernetes 1.16 和 1.17 中添加了一些标记, 对一个 CustomResourceDefinition 来说,你可以在定义自定义资源时设置这些标记。

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

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

atomic 列表类型是递归的。

(在 Kubernetes 的 Go 代码中, 这些标记以注释的形式给出,代码作者不需要用字段标记的形式重复指定它们)。

自定义资源和服务器端应用

默认情况下,服务器端应用将自定义资源视为无结构的数据。 所有键被视为 struct 数据类型的字段,所有列表都被视为 atomic 形式。

如果 CustomResourceDefinition 定义了的 schema 包含在上一小节合并策略中定义的注解, 那么在合并此类型的对象时,就会使用这些注解。

拓扑变化时的兼容性

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

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

当某 listTypemapTypestructTypeatomic 改为 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。 因此,其他管理器可以在不引起冲突的情况下更改或删除这些字段。

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

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

  • 应用的对象必须包含控制器关注的所有字段。
  • 对于在控制器没有执行过应用操作之前就已经存在的字段,不能删除。 (控制器在这种用例环境下,依然可以发送一个 patchupdate
  • 对象不必事先读取,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 启用自动扩缩,例如:

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

现在,用户希望从他们的配置中删除 replicas,从而避免与 HorizontalPodAutoscaler(HPA)及其控制器发生冲突。 然而,这里存在一个竞态: 在 HPA 需要调整 .spec.replicas 之前会有一个时间窗口, 如果在 HPA 写入字段并成为新的属主之前,用户删除了 .spec.replicas, 那 API 服务器就会把 .spec.replicas 的值设为 1(Deployment 的默认副本数)。 这不是用户希望发生的事情,即使是暂时的——它很可能会导致正在运行的工作负载降级。

这里有两个解决方案:

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

首先,用户新定义一个只包含 replicas 字段的新清单:

# 将此文件另存为 'nginx-deployment-replicas-only.yaml'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 3

用户使用私有字段管理器名称应用该清单。在本例中,用户选择了 handover-to-hpa

kubectl apply -f 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 设置了一个新值, 该临时字段管理器将不再拥有任何字段,会被自动删除。 这里无需进一步清理。

在管理器之间转移所有权

通过在配置文件中把一个字段设置为相同的值,多个字段管理器可以在彼此之间转移字段的所有权, 从而实现字段所有权的共享。 当某管理器共享了字段的所有权,管理器中任何一个成员都可以从其应用的配置中删除该字段, 从而放弃所有权,并完成了所有权向其他字段管理器的转移。

与客户端应用的对比

服务器端应用意味着既可以替代原来 kubectl apply 子命令的客户端实现, 也可供控制器作为实施变更的简单有效机制。

kubectl 管理的 last-applied 注解相比, 服务器端应用使用一种更具声明性的方法来跟踪对象的字段管理,而不是记录用户最后一次应用的状态。 这意味着,使用服务器端应用的副作用,就是字段管理器所管理的对象的每个字段的相关信息也会变得可用。

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

这和客户端应用(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 格式作为 PATCH 请求的主体发送到资源的 URI。 应用配置时,你应该始终包含对要定义的结果(如所需状态)重要的所有字段。

所有 JSON 消息都是有效的 YAML。一些客户端使用 YAML 请求体指定服务器端应用请求, 而这些 YAML 同样是合法的 JSON。

访问控制和权限

由于服务端应用是一种 PATCH 类型的操作, 所以一个主体(例如 Kubernetes RBAC 的 Role)需要 patch 权限才能编辑存量资源,还需要 create 权限才能使用服务器端应用创建新资源。

清除 managedFields

通过使用 patch(JSON Merge Patch, Strategic Merge Patch, JSON Patch)覆盖对象, 或者通过 update(HTTP PUT),可以从对象中剥离所有 managedFields; 换句话说,通过除了 apply 之外的所有写操作均可实现这点。 清除 managedFields 字段的操作可以通过用空条目覆盖 managedFields 字段的方式实现。以下是两个示例:

PATCH /api/v1/namespaces/default/configmaps/example-cm
Accept: application/json
Content-Type: application/merge-patch+json

{
  "metadata": {
    "managedFields": [
      {}
    ]
  }
}
PATCH /api/v1/namespaces/default/configmaps/example-cm
Accept: application/json
Content-Type: application/json-patch+json
If-Match: 1234567890123456789

[{"op": "replace", "path": "/metadata/managedFields", "value": [{}]}]

这一操作将用只包含一个空条目的列表来覆盖 managedFields, 从而实现从对象中整体去除 managedFields。 注意,只把 managedFields 设置为空列表并不会重置该字段。 这一设计是有意为之的,目的是避免 managedFields 被与该字段无关的客户删除。

在某些场景中,执行重置操作的同时还会给出对 managedFields 之外的别的字段的变更, 对于这类操作,managedFields 首先会被重置,其他变更被压后处理。 其结果是,应用者取得了同一个请求中所有字段的所有权。

接下来

你可以阅读 Kubernetes API 参考中的 metadata 顶级字段的 managedFields

6.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
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/kr8s-org/kr8s
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/kube-rs/kube
Rustgithub.com/ynqa/kubernetes-rust
Scalagithub.com/hagay3/skuber
Scalagithub.com/hnaderi/scala-k8s
Scalagithub.com/joan38/kubernetes-client
Swiftgithub.com/swiftkube/client

6.2.4 - Kubernetes 中的通用表达式语言

通用表达式语言 (Common Expression Language, CEL) 用于声明 Kubernetes API 的验证规则、策略规则和其他限制或条件。

CEL 表达式在 API 服务器中直接进行处理, 这使得 CEL 成为许多可扩展性用例的便捷替代方案,而无需使用类似 Webhook 这种进程外机制。 只要控制平面的 API 服务器组件保持可用状态,你的 CEL 表达式就会继续执行。

语言概述

CEL 语言的语法直观简单, 类似于 C、C++、Java、JavaScript 和 Go 中的表达式。

CEL 的设计目的是嵌入应用程序中。每个 CEL "程序" 都是一个单独的表达式,其评估结果为单个值。 CEL 表达式通常是短小的 "一行式",可以轻松嵌入到 Kubernetes API 资源的字符串字段中。

对 CEL 程序的输入是各种 “变量”。包含 CEL 的每个 Kubernetes API 字段都在 API 文档中声明了字段可使用哪些变量。例如,在 CustomResourceDefinition 的 x-kubernetes-validations[i].rules 字段中,selfoldSelf 变量可用, 并且分别指代要由 CEL 表达式验证的自定义资源数据的前一个状态和当前状态。 其他 Kubernetes API 字段可能声明不同的变量。请查阅 API 字段的 API 文档以了解该字段可使用哪些变量。

CEL 表达式示例:

CEL 表达式例子和每个表达式的用途
规则用途
self.minReplicas <= self.replicas && self.replicas <= self.maxReplicas验证定义副本的三个字段被正确排序
'Available' in self.stateCounts验证映射中存在主键为 'Available' 的条目
(self.list1.size() == 0) != (self.list2.size() == 0)验证两个列表中有一个非空,但不是两个都非空
self.envars.filter(e, e.name = 'MY_ENV').all(e, e.value.matches('^[a-zA-Z]*$')验证 listMap 条目的 'value' 字段,其主键字段 'name' 是 'MY_ENV'
has(self.expired) && self.created + self.ttl < self.expired验证 'expired' 日期在 'create' 日期加上 'ttl' 持续时间之后
self.health.startsWith('ok')验证 'health' 字符串字段具有前缀 'ok'
self.widgets.exists(w, w.key == 'x' && w.foo < 10)验证具有键 'x' 的 listMap 项的 'foo' 属性小于 10
type(self) == string ? self == '99%' : self == 42验证 int-or-string 字段是否同时具备 int 和 string 的属性
self.metadata.name == 'singleton'验证某对象的名称与特定的值匹配(使其成为一个特例)
self.set1.all(e, !(e in self.set2))验证两个 listSet 不相交
self.names.size() == self.details.size() && self.names.all(n, n in self.details)验证 'details' 映射是由 'names' listSet 中的各项键入的

CEL 选项、语言特性和库

CEL 配置了以下选项、库和语言特性,这些特性是在所列的 Kubernetes 版本中引入的:

CEL 选项、库或语言特性包含的内容可用性
标准宏hasallexistsexists_onemapfilter所有 Kubernetes 版本
标准函数参见官方标准定义列表所有 Kubernetes 版本
同质聚合字面量所有 Kubernetes 版本
默认 UTC 时区所有 Kubernetes 版本
迫切验证声明所有 Kubernetes 版本
扩展字符串库,v1charAtindexOflastIndexOflowerAsciiupperAsciireplacesplitjoinsubstringtrim所有 Kubernetes 版本
Kubernetes 列表库参见 Kubernetes 列表库所有 Kubernetes 版本
Kubernetes 正则表达式库参见 Kubernetes 正则表达式库所有 Kubernetes 版本
[Kubernetes URL 库]参见 Kubernetes URL 库所有 Kubernetes 版本
Kubernetes 鉴权组件库参见 Kubernetes 鉴权组件库所有 Kubernetes 版本
Kubernetes 数量库参见 Kubernetes 数量库Kubernetes v1.29+
CEL 可选类型参见 CEL 可选类型Kubernetes v1.29+
CEL CrossTypeNumericComparisons参见 CEL CrossTypeNumericComparisonsKubernetes v1.29+

CEL 函数、特性和语言设置支持 Kubernetes 控制平面回滚。 例如,CEL 可选值(Optional Values) 是在 Kubernetes 1.29 引入的,因此只有该版本或更新的 API 服务器才会接受使用 CEL Optional Values 的 CEL 表达式的写入请求。 但是,当集群回滚到 Kubernetes 1.28 时,已经存储在 API 资源中的使用了 "CEL Optional Values" 的 CEL 表达式将继续正确评估。

Kubernetes CEL 库

除了 CEL 社区库之外,Kubernetes 还包括在 Kubernetes 中使用 CEL 时所有可用的 CEL 库。

Kubernetes 列表库

列表库包括 indexOflastIndexOf,这两个函数的功能类似于同名的字符串函数。 这些函数返回提供的元素在列表中的第一个或最后一个位置索引。

列表库还包括 minmaxsumsum 可以用于所有数字类型以及持续时间类型。 minmax 可用于所有可比较的类型。

isSorted 也作为一个便捷的函数提供,并且支持所有可比较的类型。

例如:

使用列表库函数的 CEL 表达式例子
CEL 表达式用途
names.isSorted()验证名称列表是否按字母顺序排列
items.map(x, x.weight).sum() == 1.0验证对象列表的 “weight” 总和为 1.0
lowPriorities.map(x, x.priority).max() < highPriorities.map(x, x.priority).min()验证两组优先级不重叠
names.indexOf('should-be-first') == 1如果是特定值,则使用列表中的第一个名称

更多信息请查阅 Go 文档: Kubernetes 列表库

Kubernetes 正则表达式库

除了 CEL 标准库提供的 matches 函数外,正则表达式库还提供了 findfindAll, 使得更多种类的正则表达式运算成为可能。

例如:

使用正则表达式库函数的 CEL 表达式例子
CEL 表达式用途
"abc 123".find('[0-9]*')找到字符串中的第一个数字
"1, 2, 3, 4".findAll('[0-9]*').map(x, int(x)).sum() < 100验证字符串中的数字之和小于 100

更多信息请查阅 Go 文档: Kubernetes 正则表达式库

Kubernetes URL 库

为了更轻松、更安全地处理 URL,添加了以下函数:

  • isURL(string) 按照 Go 的 net/url 检查字符串是否是一个有效的 URL。该字符串必须是一个绝对 URL。
  • url(string) URL 将字符串转换为 URL,如果字符串不是一个有效的 URL,则返回错误。

一旦通过 url 函数解析,所得到的 URL 对象就具有 getSchemegetHostgetHostnamegetPortgetEscapedPathgetQuery 访问器。

例如:

使用 URL 库函数的 CEL 表达式例子
CEL 表达式用途
url('https://example.com:80/').getHost()获取 URL 的 'example.com:80' 主机部分
url('https://example.com/path with spaces/').getEscapedPath()返回 '/path%20with%20spaces/'

更多信息请查阅 Go 文档: Kubernetes URL 库

Kubernetes 鉴权组件库

在 API 中使用 CEL 表达式,可以使用类型为 Authorizer 的变量, 这个鉴权组件可用于对请求的主体(已认证用户)执行鉴权检查。

API 资源检查的过程如下:

  1. 指定要检查的组和资源:Authorizer.group(string).resource(string) ResourceCheck
  2. 可以调用以下任意组合的构建器函数(Builder Function),以进一步缩小鉴权检查范围。 注意这些函数将返回接收者的类型,并且可以串接起来:
    • ResourceCheck.subresource(string) ResourceCheck
    • ResourceCheck.namespace(string) ResourceCheck
    • ResourceCheck.name(string) ResourceCheck
  3. 调用 ResourceCheck.check(verb string) Decision 来执行鉴权检查。
  4. 调用 allowed() boolreason() string 来查验鉴权检查的结果。

对非资源访问的鉴权过程如下:

  1. 仅指定路径:Authorizer.path(string) PathCheck
  2. 调用 PathCheck.check(httpVerb string) Decision 来执行鉴权检查。
  3. 调用 allowed() boolreason() string 来查验鉴权检查的结果。

对于服务账号执行鉴权检查的方式:

  • Authorizer.serviceAccount(namespace string, name string) Authorizer
使用 URL 库函数的 CEL 表达式示例
CEL 表达式用途
authorizer.group('').resource('pods').namespace('default').check('create').allowed()如果主体(用户或服务账号)被允许在 default 名字空间中创建 Pod,返回 true。
authorizer.path('/healthz').check('get').allowed()检查主体(用户或服务账号)是否有权限向 /healthz API 路径发出 HTTP GET 请求。
authorizer.serviceAccount('default', 'myserviceaccount').resource('deployments').check('delete').allowed()检查服务账号是否有权限删除 Deployment。

更多信息请参阅 Go 文档: Kubernetes Authz library

Kubernetes 数量库

Kubernetes 1.28 添加了对数量字符串(例如 1.5G、512k、20Mi)的操作支持。

  • isQuantity(string) 根据 Kubernetes 的 resource.Quantity, 检查字符串是否是有效的 Quantity。
  • quantity(string) Quantity 将字符串转换为 Quantity,如果字符串不是有效的数量,则会报错。

一旦通过 quantity 函数解析,得到的 Quantity 对象将具有以下成员函数库:

Quantity 的可用成员函数
成员函数CEL 返回值描述
isInteger()bool仅当 asInteger 可以被安全调用且不出错时,才返回 true
asInteger()int将当前值作为 int64 的表示返回,如果转换会导致溢出或精度丢失,则会报错
asApproximateFloat()float返回数量的 float64 表示,可能会丢失精度。如果数量的值超出了 float64 的范围,则返回 +Inf/-Inf
sign()int如果数量为正,则返回 1;如果数量为负,则返回 -1;如果数量为零,则返回 0
add(<Quantity>)Quantity返回两个数量的和
add(<int>)Quantity返回数量和整数的和
sub(<Quantity>)Quantity返回两个数量的差
sub(<int>)Quantity返回数量减去整数的差
isLessThan(<Quantity>)bool如果接收值小于操作数,则返回 true
isGreaterThan(<Quantity>)bool如果接收值大于操作数,则返回 true
compareTo(<Quantity>)int将接收值与操作数进行比较,如果它们相等,则返回 0;如果接收值大于操作数,则返回 1;如果接收值小于操作数,则返回 -1

例如:

使用 URL 库函数的 CEL 表达式示例
CEL 表达式用途
quantity("500000G").isInteger()测试转换为整数是否会报错
quantity("50k").asInteger()精确转换为整数
quantity("9999999999999999999999999999999999999G").asApproximateFloat()松散转换为浮点数
quantity("50k").add("20k")两个数量相加
quantity("50k").sub(20000)从数量中减去整数
quantity("50k").add(20).sub(quantity("100k")).sub(-50000)链式相加和减去整数和数量
quantity("200M").compareTo(quantity("0.2G"))比较两个数量
quantity("150Mi").isGreaterThan(quantity("100Mi"))测试数量是否大于接收值
quantity("50M").isLessThan(quantity("100M"))测试数量是否小于接收值

类型检查

CEL 是一种逐渐类型化的语言

一些 Kubernetes API 字段包含完全经过类型检查的 CEL 表达式。 例如,CustomResourceDefinition 验证规则就是完全经过类型检查的。

一些 Kubernetes API 字段包含部分经过类型检查的 CEL 表达式。 部分经过类型检查的表达式是指一些变量是静态类型,而另一些变量是动态类型的表达式。 例如在 ValidatingAdmissionPolicy 的 CEL 表达式中,request 变量是有类型的,但 object 变量是动态类型的。 因此,包含 request.namex 的表达式将无法通过类型检查,因为 namex 字段未定义。 然而,即使对于 object 所引用的资源种类没有定义 namex 字段, object.namex 也会通过类型检查,因为 object 是动态类型。

在 CEL 中,has() 宏可用于检查动态类型变量的字段是否可访问,然后再尝试访问该字段的值。 例如:

has(object.namex) ? object.namex == 'special' : request.name == 'special'

类型系统集成

表格显示了 OpenAPIv3 类型和 CEL 类型之间的关系
OpenAPIv3 类型CEL 类型
设置了 properties 的 'object'object / "message type" (type(<object>) 评估为 selfType<uniqueNumber>.path.to.object.from.self
设置了 AdditionalProperties 的 'object'map
设置了 x-kubernetes-embedded-type 的 'object'object / "message type",'apiVersion'、'kind'、'metadata.name' 和 'metadata.generateName' 被隐式包含在模式中
设置了 x-kubernetes-preserve-unknown-fields 的 'object'object / "message type",CEL 表达式中不可访问的未知字段
x-kubernetes-int-or-stringint 或 string 的并集,self.intOrString < 100 || self.intOrString == '50%' 对于 50"50%"都评估为 true
'array'list
设置了 x-kubernetes-list-type=map 的 'array'list,具有基于 Equality 和唯一键保证的 map
设置了 x-kubernetes-list-type=set 的 'array'list,具有基于 Equality 和唯一条目保证的 set
'boolean'boolean
'number' (所有格式)double
'integer' (所有格式)int (64)
**非等价 **uint (64)
'null'null_type
'string'string
设置了 format=byte 的 'string'(以 base64 编码)bytes
设置了 format=date 的 'string'timestamp (google.protobuf.Timestamp)
设置了 format=datetime 的 'string'timestamp (google.protobuf.Timestamp)
设置了 format=duration 的 'string'duration (google.protobuf.Duration)

另见:CEL 类型OpenAPI 类型Kubernetes 结构化模式

x-kubernetes-list-typesetmap 的数组进行相等比较时会忽略元素顺序。 例如,如果这些数组代表 Kubernetes 的 set 值,则 [1, 2] == [2, 1]

使用 x-kubernetes-list-type 的数组进行串接时,使用 list 类型的语义:

  • setX + Y 执行并集操作,保留 X 中所有元素的数组位置, 将 Y 中非交集元素追加到 X 中,保留它们的部分顺序。
  • mapX + Y 执行合并操作,保留 X 中所有键的数组位置, 但是当 XY 的键集相交时,将 Y 中的值覆盖 X 中的值。 将 Y 中非交集键的元素附加到 X 中,保留它们的部分顺序。

转义

仅形如 [a-zA-Z_.-/][a-zA-Z0-9_.-/]* 的 Kubernetes 资源属性名可以从 CEL 中访问。 当在表达式中访问可访问的属性名时,会根据以下规则进行转义:

CEL 标识符转义规则表
转义序列等价的属性名
__underscores____
__dot__.
__dash__-
__slash__/
__{keyword}__CEL 保留的 关键字

当你需要转义 CEL 的任一 保留的 关键字时,你需要使用下划线转义来完全匹配属性名 (例如,sprint 这个单词中的 int 不会被转义,也不需要被转义)。

转义示例:

转义的 CEL 标识符例子
属性名称带有转义的属性名称的规则
namespaceself.__namespace__ > 0
x-propself.x__dash__prop > 0
redact__dself.redact__underscores__d > 0
stringself.startsWith('kube')

资源约束

CEL 不是图灵完备的,提供了多种生产安全控制手段来限制执行时间。 CEL 的资源约束特性提供了关于表达式复杂性的反馈,并帮助保护 API 服务器免受过度的资源消耗。 CEL 的资源约束特性用于防止 CEL 评估消耗过多的 API 服务器资源。

资源约束特性的一个关键要素是 CEL 定义的成本单位,它是一种跟踪 CPU 利用率的方式。 成本单位独立于系统负载和硬件。成本单位也是确定性的;对于任何给定的 CEL 表达式和输入数据, 由 CEL 解释器评估表达式将始终产生相同的成本。

CEL 的许多核心运算具有固定成本。例如比较(例如 <)这类最简单的运算成本为 1。 有些运算具有更高的固定成本,例如列表字面声明具有 40 个成本单位的固定基础成本。

调用本地代码实现的函数时,基于运算的时间复杂度估算其成本。 举例而言:matchfind 这类使用正则表达式的运算使用 length(regexString)*length(inputString) 的近似成本进行估算。 这个近似的成本反映了 Go 的 RE2 实现的最坏情况的时间复杂度。

运行时成本预算

所有由 Kubernetes 评估的 CEL 表达式都受到运行时成本预算的限制。 运行时成本预算是通过在解释 CEL 表达式时增加成本单元计数器来计算实际 CPU 利用率的估算值。 如果 CEL 解释器执行的指令太多,将超出运行时成本预算,表达式的执行将停止,并将出现错误。

一些 Kubernetes 资源定义了额外的运行时成本预算,用于限制多个表达式的执行。 如果所有表达式的成本总和超过预算,表达式的执行将停止,并将出现错误。 例如,自定义资源的验证具有针对验证自定义资源所评估的所有 验证规则每个验证 运行时成本预算。

估算的成本限制

对于某些 Kubernetes 资源,API 服务器还可能检查 CEL 表达式的最坏情况估计运行时间是否过于昂贵而无法执行。 如果是,则 API 服务器会拒绝包含 CEL 表达式的创建或更新操作,以防止 CEL 表达式被写入 API 资源。 此特性提供了更强的保证,即写入 API 资源的 CEL 表达式将在运行时进行评估,而不会超过运行时成本预算。

6.2.5 - 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 版本的发布同步记述此弃用决定。 Widget 资源仍会在 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 个发布版本

规则 #5c:不可以为了支持稳定性更差的 CLI 元素而弃用现有命令行(CLI)元素

类似于 API 的规则 #3,如果命令行的某个元素被替换为另一种实现方式, 例如通过重命名现有元素或者通过使用来自文件的配置替代命令行参数, 那么推荐的替代方式的稳定性必须相同或更高。

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

弃用某功能特性或行为

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

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

如果特性或行为正在替换为需要处理才能适应变更的替代实现,你应尽可能简化过渡。 如果替代实现在 Kubernetes 组织的控制下,则适用以下规则:

规则 #8:不得因为偏好稳定性更差的替代实现而弃用现有特性或行为。

例如,不可以因为偏好某 Beta 阶段的替代方式而弃用对应的已正式发布(GA)的特性。 然而,Kubernetes 项目鼓励用户在替代实现达到相同成熟水平之前就采用并过渡到替代实现。 这对于探索某特性的全新用例或对替代实现提供早期反馈尤为重要。

替代实现有时可能是外部工具或产品,例如某特性可能从 kubelet 迁移到不受 Kubernetes 项目控制的容器运行时。 在这种情况下,此规则不再适用,但你必须努力确保存在一种过渡途径能够不影响组件的成熟水平。 以容器运行时为例,这个努力可能包括尝试确保流行的容器运行时在实现对应的替代行为时,能够提供相同稳定性水平的版本。

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

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

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

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

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

弃用

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

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

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

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

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

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

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

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

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

弃用度量值

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

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

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

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

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

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

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

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

例外

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

6.2.6 - 已弃用 API 的迁移指南

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

各发行版本中移除的 API

v1.32

v1.32 发行版本将停止提供以下已弃用的 API 版本:

流控制资源

FlowSchema 和 PriorityLevelConfiguration 的 flowcontrol.apiserver.k8s.io/v1beta3 API 版本将不再在 v1.32 中提供。

  • 迁移清单和 API 客户端以使用 flowcontrol.apiserver.k8s.io/v1 API 版本(自 v1.29 起可用)。
  • 所有现有的持久对象都可以通过新的 API 访问。
  • flowcontrol.apiserver.k8s.io/v1 中的显着变化:
    • PriorityLevelConfiguration 的 spec.limited.nominalConcurrencyShares 字段仅在未指定时默认为 30,并且显式值 0 时不会更改为 30。

v1.29

v1.29 发行版本将停止提供以下已弃用的 API 版本:

流控制资源

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

  • 迁移清单和 API 客户端使用 flowcontrol.apiserver.k8s.io/v1 API 版本(自 v1.29 版本开始可用), 或 flowcontrol.apiserver.k8s.io/v1beta3 API 版本(自 v1.26 起可用);
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • flowcontrol.apiserver.k8s.io/v1 中的显着变化:
    • PriorityLevelConfiguration 的 spec.limited.assuredConcurrencyShares 字段已被重命名为 spec.limited.nominalConcurrencyShares,仅在未指定时默认为 30, 并且显式值 0 不会更改为 30。
  • flowcontrol.apiserver.k8s.io/v1beta3 中需要额外注意的变更:
    • PriorityLevelConfiguration 的 spec.limited.assuredConcurrencyShares 字段已被更名为 spec.limited.nominalConcurrencyShares

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 版本:

流控制资源

从 v1.26 版本开始不再提供 flowcontrol.apiserver.k8s.io/v1beta1 API 版本的 FlowSchema 和 PriorityLevelConfiguration。

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

HorizontalPodAutoscaler

从 v1.26 版本开始不再提供 autoscaling/v2beta2 API 版本的 HorizontalPodAutoscaler。

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

v1.25

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

CronJob

从 v1.25 版本开始不再提供 batch/v1beta1 API 版本的 CronJob。

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

EndpointSlice

从 v1.25 版本开始不再提供 discovery.k8s.io/v1beta1 API 版本的 EndpointSlice。

  • 迁移清单和 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

从 v1.25 版本开始不再提供 events.k8s.io/v1beta1 API 版本的 Event。

  • 迁移清单和 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

从 v1.25 版本开始不再提供 autoscaling/v2beta1 API 版本的 HorizontalPodAutoscaler。

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

PodDisruptionBudget

从 v1.25 版本开始不再提供 policy/v1beta1 API 版本的 PodDisruptionBudget。

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

PodSecurityPolicy

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

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

RuntimeClass

从 v1.25 版本开始不再提供 node.k8s.io/v1beta1 API 版本中的 RuntimeClass。

  • 迁移清单和 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.k8s.io/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

使用 1.19 及更高版本中可用的客户端警告、指标和审计信息 来定位在何处使用了已弃用的 API。

迁移到未被弃用的 API

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

  • 更改 YAML 文件引用未被弃用的 API

    你可以用 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 参考文档

6.2.7 - 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.30 [alpha]

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

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

6.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,已更改为空路径。如果发行人的 OIDC 发现 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 或者其他服务)。 或者,你也可以选择自己运行一个身份服务,例如 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 分钟。
  • --authentication-token-webhook-version 决定是使用 authentication.k8s.io/v1beta1 还是 authenticationk8s.io/v1 版本的 TokenReview 对象从 Webhook 发送/接收信息。 默认为“v1beta1”。

配置文件使用 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))或者设置附加字段, 执行伪装操作的用户必须具有对所伪装的类别(usergroupuid 等)执行 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
  }
}

为客户端提供的对身份验证信息的 API 访问

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

如果集群启用了此 API,你可以使用 SelfSubjectReview API 来了解 Kubernetes 集群如何映射你的身份验证信息从而将你识别为某客户端。无论你是作为用户(通常代表一个真的人)还是作为 ServiceAccount 进行身份验证,这一 API 都可以使用。

SelfSubjectReview 对象没有任何可配置的字段。 Kubernetes API 服务器收到请求后,将使用用户属性填充 status 字段并将其返回给用户。

请求示例(主体将是 SelfSubjectReview):

POST /apis/authentication.k8s.io/v1/selfsubjectreviews
{
  "apiVersion": "authentication.k8s.io/v1",
  "kind": "SelfSubjectReview"
}

响应示例:

{
  "apiVersion": "authentication.k8s.io/v1",
  "kind": "SelfSubjectReview",
  "status": {
    "userInfo": {
      "name": "jane.doe",
      "uid": "b6c7cfd4-f166-11ec-8ea0-0242ac120002",
      "groups": [
        "viewers",
        "editors",
        "system:authenticated"
      ],
      "extra": {
        "provider_id": ["token.company.example"]
      }
    }
  }
}

为了方便,Kubernetes 提供了 kubectl auth whoami 命令。 执行此命令将产生以下输出(但将显示不同的用户属性):

  • 简单的输出示例

    ATTRIBUTE         VALUE
    Username          jane.doe
    Groups            [system:authenticated]
    
  • 包括额外属性的复杂示例

    ATTRIBUTE         VALUE
    Username          jane.doe
    UID               b79dbf30-0c6a-11ed-861d-0242ac120002
    Groups            [students teachers system:authenticated]
    Extra: skills     [reading learning]
    Extra: subjects   [math sports]
    

通过提供 output 标志,也可以打印结果的 JSON 或 YAML 表现形式:

{
  "apiVersion": "authentication.k8s.io/v1alpha1",
  "kind": "SelfSubjectReview",
  "status": {
    "userInfo": {
      "username": "jane.doe",
      "uid": "b79dbf30-0c6a-11ed-861d-0242ac120002",
      "groups": [
        "students",
        "teachers",
        "system:authenticated"
      ],
      "extra": {
        "skills": [
          "reading",
          "learning"
        ],
        "subjects": [
          "math",
          "sports"
        ]
      }
    }
  }
}

apiVersion: authentication.k8s.io/v1
kind: SelfSubjectReview
status:
  userInfo:
    username: jane.doe
    uid: b79dbf30-0c6a-11ed-861d-0242ac120002
    groups:
    - students
    - teachers
    - system:authenticated
    extra:
      skills:
      - reading
      - learning
      subjects:
      - math
      - sports

在 Kubernetes 集群中使用复杂的身份验证流程时,例如如果你使用 Webhook 令牌身份验证身份验证代理时, 此特性极其有用。

默认情况下,所有经过身份验证的用户都可以在 APISelfSubjectReview 特性被启用时创建 SelfSubjectReview 对象。 这是 system:basic-user 集群角色允许的操作。

接下来

6.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 实现细节 了解更多信息。

6.3.3 - 证书和证书签名请求

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

Kubernetes 证书和信任包(trust bundle)API 可以通过为 Kubernetes API 的客户端提供编程接口, 实现 X.509 凭据的自动化制备, 从而请求并获取证书颁发机构 (CA) 发布的 X.509 证书

此外,Kubernetes 还对分发信任包提供了实验性(Alpha)支持。

证书签名请求

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

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 小时后自动删除
  • 所有请求:在颁发的证书过期后自动删除

证书签名鉴权

授权创建 CertificateSigningRequest 和检索 CertificateSigningRequest:

  • verbs(动词): creategetlistwatch, group(组):certificates.k8s.io, resource(资源):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, resource(资源):certificatesigningrequests
  • verbs(动词): update, group(组):certificates.k8s.io, resource(资源):certificatesigningrequests/approval
  • verbs(动词):approve, group(组):certificates.k8s.io, resource(资源):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, resource(资源):certificatesigningrequests
  • verbs(动词):update, group(组):certificates.k8s.io, resource(资源):certificatesigningrequests/status
  • verbs(动词):sign, group(组):certificates.k8s.io, resource(资源):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

签名者

签名者抽象地代表可能签署或已签署安全证书的一个或多个实体。

任何要在特定集群以外提供的签名者都应该提供关于签名者工作方式的信息, 以便消费者可以理解这对于 CertifcateSigningRequests 和(如果启用的) ClusterTrustBundles 的意义。此类信息包括:

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

如果要让信任锚点(根证书)可用,应该将其与 CertificateSigningRequest 及其 status.certificate 字段分开处理。例如,你可以使用 ClusterTrustBundle。

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"]["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"]["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 位 - 不允许。

kube-controller-manager 为每个内置签名者实现了控制平面签名。 注意:所有这些故障仅在 kube-controller-manager 日志中报告。

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

签名

控制平面签名者

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..."

批准和驳回

签名者基于 CertificateSigningRequest 签发证书之前, 通常会检查 CSR 的签发是否已被批准

控制平面的自动化批准

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 # 你可以将此字段设置为任意字符串
    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 # 你可以将此字段设置为任意字符串
    type: Denied

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

集群信任包

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

ClusterTrustBundles 是一个作用域为集群的对象,向集群内的对象分发 X.509 信任锚点(根证书)。 此对象旨在与 CertificateSigningRequests 中的签名者概念协同工作。

ClusterTrustBundles 可以使用两种模式: 签名者关联签名者未关联

常见属性和验证

所有 ClusterTrustBundle 对象都对其 trustBundle 字段的内容进行强大的验证。 该字段必须包含一个或多个经 DER 序列化的 X.509 证书,每个证书都封装在 PEM CERTIFICATE 块中。 这些证书必须解析为有效的 X.509 证书。

诸如块间数据和块内标头之类的 PEM 特性在对象验证期间要么被拒绝,要么可能被对象的消费者忽略。 此外,消费者被允许使用自己的任意但稳定的排序方式重新排序 bundle 中的证书。

ClusterTrustBundle 对象应该在集群内被视为全局可读的。 如果集群使用 RBAC 鉴权, 则所有 ServiceAccount 都具有默认授权,允许它们 getlistwatch 所有 ClusterTrustBundle 对象。如果你使用自己的鉴权机制,并且在集群中启用了 ClusterTrustBundles,则应设置等效规则以使这些对象在集群内公开,使这些对象按预期工作。

如果你没有默认在集群中列出集群信任包的权限,则可以扮演具有访问权限的 ServiceAccount, 以查看可用的 ClusterTrustBundle:

kubectl get clustertrustbundles --as='system:serviceaccount:mynamespace:default'

签名者关联的 ClusterTrustBundles

签名者关联的 ClusterTrustBundles 与签名者名称关联,例如:

apiVersion: certificates.k8s.io/v1alpha1
kind: ClusterTrustBundle
metadata:
  name: example.com:mysigner:foo
spec:
  signerName: example.com/mysigner
  trustBundle: "<... PEM data ...>"

这些 ClusterTrustBundle 预期由集群中的特定签名者控制器维护,因此它们具有多个安全特性:

  • 要创建或更新与一个签名者关联的 ClusterTrustBundle,你必须获准证明该签名者 (自定义鉴权动词 attest API 组 certificates.k8s.io;资源路径 signers)。 你可以为特定资源名称 <signerNameDomain>/<signerNamePath> 或匹配 <signerNameDomain>/* 等模式来配置鉴权。
  • 与签名者关联的 ClusterTrustBundle 必须使用从其 spec.signerName 字段派生的前缀命名。 斜杠 (/) 被替换为英文冒号 (:),最后追加一个英文冒号。后跟任意名称。 例如,签名者 example.com/mysigner 可以关联到 ClusterTrustBundle example.com:mysigner:<arbitrary-name>

与签名者关联的 ClusterTrustBundle 通常通过组合签名者名称有关的 字段选择算符 或单独使用标签选择算符在工作负载中被消耗。

签名者未关联的 ClusterTrustBundles

签名者未关联的 ClusterTrustBundles 具有空白的 spec.signerName 字段,例如:

apiVersion: certificates.k8s.io/v1alpha1
kind: ClusterTrustBundle
metadata:
  name: foo
spec:
  # 未指定 signerName 所以该字段留空
  trustBundle: "<... PEM data ...>"

它们主要用于集群配置场景。每个与签名者未关联的 ClusterTrustBundle 都是一个独立的对象, 与签名者关联的 ClusterTrustBundle 的惯常分组行为形成了对比。

与签名者为关联的 ClusterTrustBundle 没有 attest 动词要求。 相反,你可以使用通常的机制(如基于角色的访问控制)直接控制对它们的访问。

为了将它们与与签名者关联的 ClusterTrustBundle 区分开来,与签名者未关联的 ClusterTrustBundle 的名称必须不包含英文冒号 (:)。

如何为用户签发证书

为了让普通用户能够通过认证并调用 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 -subj "/CN=myuser"

创建 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"
    

批准 CertificateSigningRequest

使用 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 文件。

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

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

接下来

6.3.4 - 准入控制器参考

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

什么是准入控制插件?

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

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

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

Kubernetes 1.30 中的准入控制器由下面的列表组成, 并编译进 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.30 中,默认启用的插件有:

CertificateApproval, CertificateSigning, CertificateSubjectRestriction, DefaultIngressClass, DefaultStorageClass, DefaultTolerationSeconds, LimitRanger, MutatingAdmissionWebhook, NamespaceLifecycle, PersistentVolumeClaimResize, PodSecurity, Priority, ResourceQuota, RuntimeClass, ServiceAccount, StorageObjectInUseProtection, TaintNodesByCondition, ValidatingAdmissionPolicy, 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]

类别:验证。

PodSecurity 准入控制器在新 Pod 被准入之前对其进行检查, 根据请求的安全上下文和 Pod 所在名字空间允许的 Pod 安全性标准的限制来确定新 Pod 是否应该被准入。

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

PodSecurity 取代了一个名为 PodSecurityPolicy 的旧准入控制器。

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

类别:验证。

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

此准入控制器将拒绝任何尝试设置以下 SecurityContext 字段的 Pod:

  • .spec.securityContext.supplementalGroups
  • .spec.securityContext.seLinuxOptions
  • .spec.securityContext.runAsUser
  • .spec.securityContext.fsGroup
  • .spec.(init)Containers[*].securityContext.seLinuxOptions
  • .spec.(init)Containers[*].securityContext.runAsUser

有关此插件的更多历史背景,请参阅 Kubernetes 博客中这篇有关 PodSecurityPolicy 及其移除的文章: The birth of PodSecurityPolicy。 这篇文章详细地介绍了 PodSecurityPolicy 的历史背景以及 Pod 的 securityContext 字段的诞生。

ServiceAccount

类别:变更和验证。

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

关于 kubernetes.io/enforce-mountable-secrets 注解:尽管注解的名称表明它只涉及 Secret 的挂载, 但其执行范围也扩展到 Pod 上下文中 Secret 的其他使用方式。 因此,确保所有引用的 Secret 在 ServiceAccount 中被正确指定是至关重要的。

StorageObjectInUseProtection

类别:变更。

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

TaintNodesByCondition

类别:变更。

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

ValidatingAdmissionPolicy

类别:验证。

此准入控制器针对传入的匹配请求实现 CEL 校验。当 validatingadmissionpolicyadmissionregistration.k8s.io/v1alpha1 特性门控组/版本被启用时, 此特性被启用。如果任意 ValidatingAdmissionPolicy 失败,则请求失败。

ValidatingAdmissionWebhook

类别:验证。

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

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

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

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

6.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 服务器不会验证客户端的身份,认为其是 API 服务器。 如果你需要双向 TLS 或其他方式来验证客户端, 请参阅如何对 API 服务器进行身份认证

部署准入 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/zh-cn/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。

这个例子展示了一个变更性质的 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

匹配请求:matchConditions

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

如果你需要细粒度地过滤请求,你可以为 Webhook 定义匹配条件。 如果你发现匹配规则、objectSelectorsnamespaceSelectors 仍然不能提供你想要的何时进行 HTTP 调用的过滤条件,那么添加这些条件会很有用。 匹配条件是 CEL 表达式。 所有匹配条件都必须为 true 才能调用 Webhook。

以下是一个例子,说明了匹配条件的几种不同用法:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
webhooks:
  - name: my-webhook.example.com
    matchPolicy: Equivalent
    rules:
      - operations: ['CREATE','UPDATE']
        apiGroups: ['*']
        apiVersions: ['*']
        resources: ['*']
    failurePolicy: 'Ignore' # 失败时继续处理请求但跳过 Webhook (可选值)
    sideEffects: None
    clientConfig:
      service:
        namespace: my-namespace
        name: my-webhook
      caBundle: '<omitted>'
    # 你可以为每个 Webhook 配置最多 64 个 matchConditions
    matchConditions:
      - name: 'exclude-leases' # 每个匹配条件必须有唯一的名称
        expression: '!(request.resource.group == "coordination.k8s.io" && request.resource.resource == "leases")' # 匹配非租约资源
      - name: 'exclude-kubelet-requests'
        expression: '!("system:nodes" in request.userInfo.groups)' # 匹配非节点用户发出的请求
      - name: 'rbac' # 跳过 RBAC 请求,该请求将由第二个 Webhook 处理
        expression: 'request.resource.group != "rbac.authorization.k8s.io"'

  # 这个示例演示了如何使用 “authorizer”。
  # 授权检查比简单的表达式更复杂,因此在这个示例中,使用第二个 Webhook 来针对 RBAC 请求进行处理。
  # 两个 Webhook 都可以由同一个端点提供服务。
  - name: rbac.my-webhook.example.com
    matchPolicy: Equivalent
    rules:
      - operations: ['CREATE','UPDATE']
        apiGroups: ['rbac.authorization.k8s.io']
        apiVersions: ['*']
        resources: ['*']
    failurePolicy: 'Fail' # 失败时拒绝请求 (默认值)
    sideEffects: None
    clientConfig:
      service:
        namespace: my-namespace
        name: my-webhook
      caBundle: '<omitted>'
    # 你可以为每个 Webhook 配置最多 64 个 matchConditions
    matchConditions:
      - name: 'breakglass'
        # 跳过由授权给 “breakglass” 的用户在这个 Webhook 上发起的请求。
        # “breakglass” API 不需要在这个检查之外存在。
        expression: '!authorizer.group("admissionregistration.k8s.io").resource("validatingwebhookconfigurations").name("my-webhook.example.com").check("breakglass").allowed()'

匹配条件可以访问以下 CEL 变量:

  • object - 来自传入请求的对象。对于 DELETE 请求,该值为 null。 该对象版本可能根据 matchPolicy 进行转换。
  • oldObject - 现有对象。对于 CREATE 请求,该值为 null。
  • request - AdmissionReview 的请求部分,不包括 object 和 oldObject。
  • authorizer - 一个 CEL 鉴权组件。可用于对请求的主体(经过身份认证的用户)执行鉴权检查。 更多详细信息,请参阅 Kubernetes CEL 库文档中的 Authz
  • authorizer.requestResource - 对配置的请求资源(组、资源、(子资源)、名字空间、名称)进行授权检查的快捷方式。

了解有关 CEL 表达式的更多信息,请参阅 Kubernetes 参考文档中的通用表达式语言

如果在对匹配条件求值时出现错误,则不会调用 Webhook。根据以下方式确定是否拒绝请求:

  1. 如果任何一个匹配条件求值结果为 false(不管其他错误),API 服务器将跳过 Webhook。
  1. 否则:

调用 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 名字空间。

6.3.6 - 管理服务账号

ServiceAccount 为 Pod 中运行的进程提供了一个身份。

Pod 内的进程可以使用其关联服务账号的身份,向集群的 API 服务器进行身份认证。

有关服务账号的介绍, 请参阅配置服务账号

本任务指南阐述有关 ServiceAccount 的几个概念。 本指南还讲解如何获取或撤销代表 ServiceAccount 的令牌。

准备开始

你必须拥有一个 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.30 在内最近的几个版本中,使用 TokenRequest API 直接获得 API 凭据, 并使用投射卷挂载到 Pod 中。使用这种方法获得的令牌具有绑定的生命周期, 当挂载的 Pod 被删除时这些令牌将自动失效。

你仍然可以手动创建 Secret 来保存服务账号令牌;例如在你需要一个永不过期的令牌的时候。

一旦你手动创建一个 Secret 并将其关联到 ServiceAccount, Kubernetes 控制平面就会自动将令牌填充到该 Secret 中。

清理自动生成的传统 ServiceAccount 令牌

在 1.24 版本之前,Kubernetes 自动为 ServiceAccount 生成基于 Secret 的令牌。 为了区分自动生成的令牌和手动创建的令牌,Kubernetes 会检查 ServiceAccount 的 Secret 字段是否有引用。如果该 Secret 被 secrets 字段引用, 它被视为自动生成的传统令牌。否则,它被视为手动创建的传统令牌。例如:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: build-robot
  namespace: default
secrets:
  - name: build-robot-secret # 对于手动生成的令牌通常不会存在此字段

从 1.29 版本开始,如果传统 ServiceAccount 令牌在一定时间段(默认设置为一年)内未被使用,则会被标记为无效。 在定义的时间段(同样默认为一年)持续未被使用的令牌将由控制平面自动清除。

如果用户使用一个无效的自动生成的令牌,令牌验证器将执行以下操作:

  1. 为键值对 authentication.k8s.io/legacy-token-invalidated: <secret name>/<namespace> 添加审计注解,
  2. invalid_legacy_auto_token_uses_total 指标计数加一,
  3. 更新 Secret 标签 kubernetes.io/legacy-token-last-used 为新日期,
  4. 返回一个提示令牌已经无效的报错。

当收到这个校验报错时,用户可以通过移除 kubernetes.io/legacy-token-invalid-since 标签更新 Secret,以临时允许使用此令牌。

以下是一个自动生成的传统令牌示例,它被标记了 kubernetes.io/legacy-token-last-usedkubernetes.io/legacy-token-invalid-since 标签:

apiVersion: v1
kind: Secret
metadata:
  name: build-robot-secret
  namespace: default
  labels:
    kubernetes.io/legacy-token-last-used: 2022-10-24
    kubernetes.io/legacy-token-invalid-since: 2023-10-25
  annotations:
    kubernetes.io/service-account.name: build-robot
type: kubernetes.io/service-account-token

控制平面细节

ServiceAccount 控制器

ServiceAccount 控制器管理名字空间内的 ServiceAccount, 并确保每个活跃的名字空间中都存在名为 default 的 ServiceAccount。

令牌控制器

服务账号令牌控制器作为 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 进行复制。

传统 ServiceAccount 令牌追踪控制器

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

此控制器在 kube-system 命名空间中生成名为 kube-apiserver-legacy-service-account-token-tracking 的 ConfigMap。 这个 ConfigMap 记录了系统开始监视传统服务账户令牌的时间戳。

传统 ServiceAccount 令牌清理器

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

传统 ServiceAccount 令牌清理器作为 kube-controller-manager 的一部分运行, 每 24 小时检查一次,查看是否有任何自动生成的传统 ServiceAccount 令牌在特定时间段内未被使用。如果有的话,清理器会将这些令牌标记为无效。

清理器的工作方式是首先检查控制平面创建的 ConfigMap(前提是启用了 LegacyServiceAccountTokenTracking)。如果当前时间是 ConfigMap 所包含日期之后的特定时间段,清理器会遍历集群中的 Secret 列表, 并评估每个类型为 kubernetes.io/service-account-token 的 Secret。

如果一个 Secret 满足以下所有条件,清理器会将其标记为无效:

  • Secret 是自动生成的,意味着它被 ServiceAccount 双向引用。
  • Secret 当前没有被任何 Pod 挂载。
  • Secret 自从创建或上次使用以来的特定时间段未被使用过。

清理器通过向 Secret 添加名为 kubernetes.io/legacy-token-invalid-since 的标签, 并将此值设置为当前日期,来标记 Secret 为无效。 如果一个无效的 Secret 在特定时间段内未被使用,清理器将会删除它。

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 小时)结束之后过期。在令牌过期之前,kubelet 还会刷新该令牌。 该令牌绑定到特定的 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

清理

如果创建了一个 examplens 名字空间进行试验,你可以移除它:

kubectl delete namespace examplens

接下来

6.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
    • 可以用来获取其他工作负载专用的信息,例如数据库主机名。
  • 挂载该名字空间里其他工作负载的卷
    • 可以用来获取其他工作负载专用的信息,并且更改它。

接下来

6.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 这类工具描述或修补 RBAC 对象

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)是将角色中定义的权限赋予一个或者一组用户。 它包含若干主体(Subject)(用户、组或服务账户)的列表和对这些主体所获得的角色的引用。 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" 名字空间中的 Secret 对象,因为 RoleBinding 所在的名字空间(由其 metadata 决定)是 "development"。

apiVersion: rbac.authorization.k8s.io/v1
# 此角色绑定使得用户 "dave" 能够读取 "development" 名字空间中的 Secret
# 你需要一个名为 "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"]

你可以使用通配符 * 批量引用所有的 resourcesapiGroupsverbs 对象,无需逐一引用。 对于 nonResourceURLs,你可以将通配符 * 作为后缀实现全局通配, 对于 resourceNames,空集表示没有任何限制。 下面的示例对 example.com API 组中所有当前和未来资源执行所有动作。 这类似于内置的 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允许对名字空间的大多数对象有只读权限。 它不允许查看角色或角色绑定。

此角色不允许查看 Secret,因为读取 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 的迁移后,应该调整集群的访问控制,确保相关的策略满足你的信息安全需求。

6.3.9 - 使用 ABAC 鉴权

基于属性的访问控制(Attribute-based access control,ABAC)定义了访问控制范例, ABAC 通过使用将属性组合在一起的策略来向用户授予访问权限。

策略文件格式

要启用 ABAC 模式,可以在启动时指定 --authorization-policy-file=SOME_FILENAME--authorization-mode=ABAC

此文件格式是每行一个 JSON 对象,不应存在外层的列表或映射,每行应只有一个映射。

每一行都是一个“策略对象”,策略对象是具有以下属性的映射:

  • 版本控制属性:
    • 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 操作。 非资源匹配属性仅适用于 get 操作。

鉴权算法

请求具有与策略对象的属性对应的属性。

当接收到请求时,属性是确定的。未知属性设置为其类型的零值(例如:空字符串、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": "*"}}
    
  1. kubelet 可以读取所有 Pod:

    {"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "kubelet", "namespace": "*", "resource": "pods", "readonly": true}}
    
  1. 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}}
    
  1. 任何人都可以对所有非资源路径进行只读请求:

    {"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": "*"}}
    

完整文件示例

服务账号的快速说明

每个服务账号都有对应的 ABAC 用户名,服务账号的用户名是根据命名约定生成的:

system:serviceaccount:<namespace>:<serviceaccountname>

创建新的命名空间也会导致创建一个新的服务账号:

system:serviceaccount:<namespace>:default

例如,如果你要使用 ABAC 将(kube-system 命名空间中)的默认服务账号完整权限授予 API, 则可以将此行添加到策略文件中:

{"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec":{"user":"system:serviceaccount:kube-system:default","namespace":"*","resource":"*","apiGroup":"*"}}

API 服务器将需要被重新启动以获取新的策略行。

6.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 的请求。

6.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 都是可接受的

6.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

6.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

6.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
  9. 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:bootstrappers 组的成员, 它需要被 授权 创建证书签名请求(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,之后基于鉴权结果执行批复操作。 为了避免与其它批复组件发生冲突,内置的批复组件不会显式地拒绝任何 CSR。 该组件仅是忽略未被授权的请求。控制器也会作为垃圾收集的一部分清除已过期的证书。

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 上 RotateKubeletServerCertificate 特性的支持(默认启用)。

你可以配置 kubelet 使其在现有凭据过期时通过创建新的 CSR 来轮换其客户端证书。 要启用此功能,请使用 kubelet 配置文件rotateCertificates 字段或将以下命令行参数传递给 kubelet(已弃用):

--rotate-certificates

启用 RotateKubeletServerCertificate 会让 kubelet 在启动引导其客户端凭据之后请求一个服务证书对该服务证书执行轮换操作。 要启用此特性,请使用 kubelet 配置文件serverTLSBootstrap 字段将以下命令行参数传递给 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 批复

CSR 可以在编译进控制器管理器内部的批复工作流之外被批复。

签名控制器并不会立即对所有证书请求执行签名操作。相反, 它会等待这些请求被某具有适当特权的用户标记为 “Approved(已批准)”状态。 这一流程有意允许由外部批复控制器来自动执行的批复, 或者由控制器管理器内置的批复控制器来自动批复。 不过,集群管理员也可以使用 kubectl 来手动批准证书请求。 管理员可以通过 kubectl get csr 来列举所有的 CSR,使用 kubectl descsribe csr <name> 来描述某个 CSR 的细节。 管理员可以使用 kubectl certificate approve <name 来批准某 CSR,或者 kubectl certificate deny <name> 来拒绝某 CSR。

6.3.15 - 验证准入策略(ValidatingAdmissionPolicy)

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

本页面提供验证准入策略(Validating Admission Policy)的概述。

什么是验证准入策略?

验证准入策略提供一种声明式的、进程内的替代方案来验证准入 Webhook。

验证准入策略使用通用表达语言 (Common Expression Language,CEL) 来声明策略的验证规则。 验证准入策略是高度可配置的,使配置策略的作者能够根据集群管理员的需要, 定义可以参数化并限定到资源的策略。

哪些资源构成策略

策略通常由三种资源构成:

  • ValidatingAdmissionPolicy 描述策略的抽象逻辑(想想看:“这个策略确保一个特定标签被设置为一个特定值”)。

  • 一个 ValidatingAdmissionPolicyBinding 将上述资源联系在一起,并提供作用域。 如果你只想为 Pods 设置一个 owner 标签,你就需要在这个绑定中指定这个限制。

  • 参数资源为 ValidatingAdmissionPolicy 提供信息,使其成为一个具体的声明 (想想看:“owner 标签必须被设置为以 .company.com 结尾的形式")。 参数资源的模式(Schema)使用诸如 ConfigMap 或 CRD 这类原生类型定义。 ValidatingAdmissionPolicy 对象指定它们期望参数资源所呈现的类型。

至少要定义一个 ValidatingAdmissionPolicy 和一个相对应的 ValidatingAdmissionPolicyBinding 才能使策略生效。

如果 ValidatingAdmissionPolicy 不需要参数配置,不设置 ValidatingAdmissionPolicy 中的 spec.paramKind 即可。

准备开始

  • 确保 ValidatingAdmissionPolicy 特性门控被启用。
  • 确保 admissionregistration.k8s.io/v1beta1 API 被启用。

开始使用验证准入策略

验证准入策略是集群控制平面的一部分。你应该非常谨慎地编写和部署它们。下面介绍如何快速试验验证准入策略。

创建一个 ValidatingAdmissionPolicy

以下是一个 ValidatingAdmissionPolicy 的示例:

apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingAdmissionPolicy
metadata:
  name: "demo-policy.example.com"
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
    - apiGroups:   ["apps"]
      apiVersions: ["v1"]
      operations:  ["CREATE", "UPDATE"]
      resources:   ["deployments"]
  validations:
    - expression: "object.spec.replicas <= 5"

spec.validations 包含使用通用表达式语言 (CEL) 来验证请求的 CEL 表达式。 如果表达式的计算结果为 false,则根据 spec.failurePolicy 字段强制执行验证检查处理。

要配置一个在某集群中使用的验证准入策略,需要一个绑定。 以下是一个 ValidatingAdmissionPolicyBinding 的示例:

apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: "demo-binding-test.example.com"
spec:
  policyName: "demo-policy.example.com"
  validationActions: [Deny]
  matchResources:
    namespaceSelector:
      matchLabels:
        environment: test

尝试创建副本集合不满足验证表达式的 Deployment 时,将返回包含以下消息的错误:

ValidatingAdmissionPolicy 'demo-policy.example.com' with binding 'demo-binding-test.example.com' denied request: failed expression: object.spec.replicas <= 5

上面提供的是一个简单的、无配置参数的 ValidatingAdmissionPolicy。

验证操作

每个 ValidatingAdmissionPolicyBinding 必须指定一个或多个 validationActions 来声明如何执行策略的 validations

支持的 validationActions 包括:

  • Deny: 验证失败会导致请求被拒绝。
  • Warn: 验证失败会作为警告报告给请求客户端。
  • Audit: 验证失败会包含在 API 请求的审计事件中。

例如,要同时向客户端发出验证失败的警告并记录验证失败的审计记录,请使用以下配置:

validationActions: [Warn, Audit]

DenyWarn 不能一起使用,因为这种组合会不必要地将验证失败重复输出到 API 响应体和 HTTP 警告头中。

如果 validation 求值为 false,则始终根据这些操作执行。 由 failurePolicy 定义的失败仅在 failurePolicy 设置为 Fail(或未指定)时根据这些操作执行, 否则这些失败将被忽略。

有关验证失败审计注解的详细信息,请参见 审计注解:验证失败

参数资源

参数资源允许策略配置与其定义分开。 一个策略可以定义 paramKind,给出参数资源的 GVK, 然后一个策略绑定可以通过名称(通过 policyName)将某策略与某特定的参数资源(通过 paramRef)联系起来。

如果需要参数配置,下面是一个带有参数配置的 ValidatingAdmissionPolicy 的例子:

apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingAdmissionPolicy
metadata:
  name: "replicalimit-policy.example.com"
spec:
  failurePolicy: Fail
  paramKind:
    apiVersion: rules.example.com/v1
    kind: ReplicaLimit
  matchConstraints:
    resourceRules:
    - apiGroups:   ["apps"]
      apiVersions: ["v1"]
      operations:  ["CREATE", "UPDATE"]
      resources:   ["deployments"]
  validations:
    - expression: "object.spec.replicas <= params.maxReplicas"
      reason: Invalid

ValidatingAdmissionPolicy 的 spec.paramKind 字段指定用于参数化此策略的资源类型。 在这个例子中,它是由自定义资源 ReplicaLimit 配置的。 在这个例子中请注意 CEL 表达式是如何通过 CEL params 变量引用参数的,如:params.maxReplicasspec.matchConstraints 指定此策略要检查哪些资源。 请注意,诸如 ConfigMap 之类的原生类型也可以用作参数引用。

spec.validations 字段包含 CEL 表达式。 如果表达式的计算结果为 false,则根据 spec.failurePolicy 字段强制执行验证检查操作。

验证准入策略的作者负责提供 ReplicaLimit 参数 CRD。

要配置一个在某集群中使用的验证准入策略,需要创建绑定和参数资源。 以下是 ValidatingAdmissionPolicyBinding 集群范围参数的示例 - 相同的参数将用于验证与绑定匹配的每个资源请求:

apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: "replicalimit-binding-test.example.com"
spec:
  policyName: "replicalimit-policy.example.com"
  validationActions: [Deny]
  paramRef:
    name: "replica-limit-test.example.com"
    namespace: "default"
  matchResources:
    namespaceSelector:
      matchLabels:
        environment: test

请注意,此绑定将参数应用于 test 环境中所有资源的策略中。

参数资源可以如下:

apiVersion: rules.example.com/v1
kind: ReplicaLimit
metadata:
  name: "replica-limit-test.example.com"
  namespace: "default"
maxReplicas: 3

此策略参数资源将限制 Deployment 最多有 3 个副本。 一个准入策略可以有多个绑定。 要绑定所有的其他环境,限制 maxReplicas 为 100,请创建另一个 ValidatingAdmissionPolicyBinding:

apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: "replicalimit-binding-nontest"
spec:
  policyName: "replicalimit-policy.example.com"
  validationActions: [Deny]
  paramRef:
    name: "replica-limit-prod.example.com"
    namespace: "default"
  matchResources:
    namespaceSelector:
      matchExpressions:
      - key: environment
        operator: NotIn
        values:
        - test

请注意,此绑定将不同的参数应用于不在 test 环境中的资源。

并有一个参数资源:

apiVersion: rules.example.com/v1
kind: ReplicaLimit
metadata:
  name: "replica-limit-prod.example.com"
maxReplicas: 100

对于每个准入请求,API 服务器都会评估与请求匹配的每个(策略、绑定、参数)组合的 CEL 表达式。 要获得准入资格,必须通过所有评估。

如果多个绑定与请求匹配,则将为每个绑定评估策略,并且它们必须全部通过评估,策略才会被视为通过。

如果多个参数与同一个绑定匹配,则系统将为每个参数评估策略规则,并且这些规则也必须全部通过才能认为该绑定通过。 多个绑定之间可以在匹配条件存在重叠。系统针对匹配的绑定参数所有组合来评估策略。如果多个绑定与其匹配, 或者同一个绑定与多个参数匹配,则策略甚至可以被多次评估。

绑定可以包含相互重叠的匹配条件。策略会针对每个匹配的绑定进行计算。 在上面的例子中,nontest 策略绑定可以被定义为一个全局策略:

apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: "replicalimit-binding-global"
spec:
  policyName: "replicalimit-policy.example.com"
  validationActions: [Deny]
  params: "replica-limit-clusterwide.example.com"
  matchResources:
    namespaceSelector:
      matchExpressions:
      - key: environment
        operator: Exists

如果参数资源尚未被绑定,代表参数资源的 params 对象将不会被设置, 所以对于需要参数资源的策略,添加一个检查来确保参数资源被绑定,这会很有用。 如果策略的 paramKind 未指定或绑定的 paramRef 未指定,则不会绑定参数资源, 并且 params 将为空。

对于需要参数配置的场景,我们建议在 spec.validations[0].expression 中添加一个参数检查:

- expression: "params != null"
  message: "params missing but required to bind to this policy"

可选参数

将可选参数作为参数资源的一部分,并且只在参数存在时执行检查操作,这样做会比较方便。 CEL 提供了 has() 方法,它检查传递给它的键是否存在。CEL 还实现了布尔短路逻辑。 如果逻辑 OR 的前半部分计算为 true,则不会计算另一半(因为无论如何整个 OR 的结果都为真)。

结合这两者,我们可以提供一种验证可选参数的方法:

!has(params.optionalNumber) || (params.optionalNumber >= 5 && params.optionalNumber <= 10)

在这里,我们首先用 !has(params.optionalNumber) 检查可选参数是否存在。

  • 如果 optionalNumber 没有被定义,那么表达式就会短路,因为 !has(params.optionalNumber) 的计算结果为 true。
  • 如果 optionalNumber 被定义了,那么将计算 CEL 表达式的后半部分, 并且 optionalNumber 将被检查以确保它包含一个 5 到 10 之间的值(含 5 到 10)。

按命名空间设置的参数

作为 ValidatingAdmissionPolicy 及其 ValidatingAdmissionPolicyBinding 的作者, 你可以选择指定其作用于集群范围还是某个命名空间。如果你为绑定的 paramRef 指定 namespace, 则控制平面仅在该命名空间中搜索参数。

但是,如果 ValidatingAdmissionPolicyBinding 中未指定 namespace,则 API 服务器可以在请求所针对的命名空间中搜索相关参数。 例如,如果你请求修改 default 命名空间中的 ConfigMap,并且存在未设置 namespace 的相关 ValidatingAdmissionPolicyBinding,则 API 服务器在 default 命名空间中查找参数对象。 此设计支持依赖于所操作资源的命名空间的策略配置,以实现更精细的控制。

参数选择算符

除了在绑定中用 name 来指定参数之外,你还可以选择设置标签选择算符, 这样对于与策略的 paramKind 参数匹配,且位于参数的 namespace(如果适用)内的所有资源, 如果与标签选择算符匹配,都会被评估。有关标签选择算符如何匹配资源的更多信息, 请参阅选择算符

如果发现多个参数满足条件,则会针对所找到的每个参数来评估策略规则,并将结果进行“与”运算。

如果设置了 namespace,则只有所提供的命名空间中类别为 paramKind 的对象才会被匹配。 否则,当 namespace 为空且 paramKind 为命名空间作用域的资源时,使用被准入请求中指定的 namespace

鉴权检查

我们为参数资源引入了鉴权检查。 用户应该对 ValidatingAdmissionPolicy 中的 paramKindValidatingAdmissionPolicyBinding 中的 paramRef 所引用的资源有 read 权限。

请注意,如果 paramKind 中的资源没能通过 restmapper 解析,则用户需要拥有对组的所有资源的 read 访问权限。

失效策略

failurePolicy 定义了如何处理错误配置和准入策略的 CEL 表达式取值为 error 的情况。

允许的值是 IgnoreFail

  • Ignore 意味着调用 ValidatingAdmissionPolicy 的错误被忽略,允许 API 请求继续。
  • Fail 意味着调用 ValidatingAdmissionPolicy 的错误导致准入失败并拒绝 API 请求。

请注意,failurePolicy 是在 ValidatingAdmissionPolicy 中定义的:

apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingAdmissionPolicy
spec:
...
failurePolicy: Ignore # The default is "Fail"
validations:
- expression: "object.spec.xyz == params.x"  

检查表达式

spec.validations[i].expression 代表将使用 CEL 来计算表达式。 要了解更多信息,请参阅 CEL 语言规范。 CEL 表达式可以访问按 CEL 变量来组织的 Admission 请求/响应的内容,以及其他一些有用的变量 :

  • 'object' - 来自传入请求的对象。对于 DELETE 请求,该值为 null。
  • 'oldObject' - 现有对象。对于 CREATE 请求,该值为 null。
  • 'request' - 准入请求的属性。
  • 'params' - 被计算的策略绑定引用的参数资源。如果未设置 paramKind,该值为 null。
  • authorizer - 一个 CEL 鉴权组件。可以用来为请求的主体(经过身份验证的用户)执行鉴权检查。 更多细节可以参考 Kubernetes CEL 库的文档中的 Authz
  • authorizer.requestResource - 针对请求资源(组、资源、(子资源)、命名空间、名称)所配置的鉴权检查的快捷方式。

总是可以从对象的根访问的属性有 apiVersionkindmetadata.namemetadata.generateName。 其他元数据属性不能访问。

列表类型为 "set" 或 "map" 的数组上的等价关系比较会忽略元素顺序,即 [1, 2] == [2, 1]。 使用 x-kubernetes-list-type 连接数组时使用列表类型的语义:

  • 'set': X + Y 执行并集,其中 X 中所有元素的数组位置被保留,Y 中不相交的元素被追加,保留其元素的偏序关系。
  • 'map':X + Y 执行合并,保留 X 中所有键的数组位置,但是当 XY 的键集相交时,其值被 Y 的值覆盖。 Y 中键值不相交的元素被追加,保留其元素之间的偏序关系。

检查表达式示例

表达式目的
object.minReplicas <= object.replicas && object.replicas <= object.maxReplicas检查定义副本的三个字段是否大小关系正确
'Available' in object.stateCounts检查映射中是否存在键为 Available 的条目
(size(object.list1) == 0) != (size(object.list2) == 0)检查两个列表是否有且只有一个非空
!('MY_KEY' in object.map1) || object['MY_KEY'].matches('^[a-zA-Z]*$')检查映射中存在特定的键时其取值符合某规则
object.envars.filter(e, e.name == 'MY_ENV').all(e, e.value.matches('^[a-zA-Z]*$')验证 listMap 中所有键名为 "MY_ENV" 的条目的 “value” 字段,确保其符合规则
has(object.expired) && object.created + object.ttl < object.expired检查 expired 日期在 create 日期加上 ttl 时长之后
object.health.startsWith('ok')检查 health 字符串字段的取值有 “ok” 前缀
object.widgets.exists(w, w.key == 'x' && w.foo < 10)对于 listMap 中键为 “x” 的条目,检查该条目的 "foo" 属性的值是否小于 10
type(object) == string ? object == '100%' : object == 1000对于 int-or-string 字段,分别处理类型为 int 或 string 的情况
object.metadata.name.startsWith(object.prefix)检查对象名称是否以另一个字段值为前缀
object.set1.all(e, !(e in object.set2))检查两个 listSet 是否不相交
size(object.names) == size(object.details) && object.names.all(n, n in object.details)检查映射 “details” 所有的键和 listSet “names” 中的条目是否一致
size(object.clusters.filter(c, c.name == object.primary)) == 1检查 “primary” 字段的值在 listMap “clusters” 中只出现一次

了解关于 CEL 规则的更多信息, 请阅读 CEL 支持的求值表达式

spec.validation[i].reason 表示一个机器可读的描述,说明为什么这次检查失败。 如果这是列表中第一个失败的检查,其原因以及相应的 HTTP 响应代码会被用在给客户端的 HTTP 响应中。 目前支持的原因有:UnauthorizedForbiddenInvalidRequestEntityTooLarge。 如果未设置,将在对客户端的响应中使用 StatusReasonInvalid

匹配请求:matchConditions

如果需要进行精细的请求过滤,可以为 ValidatingAdmissionPolicy 定义 匹配条件(match conditions)。 如果发现匹配规则、objectSelectorsnamespaceSelectors 仍无法提供所需的过滤功能,则使用这些条件非常有用。 匹配条件是 CEL 表达式。 所有匹配条件必须求值为 true 时才会对资源进行评估。

以下示例说明了匹配条件的几个不同用法:

apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicy
metadata:
  name: "demo-policy.example.com"
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
      - apiGroups:   ["*"]
        apiVersions: ["*"]
        operations:  ["CREATE", "UPDATE"]
        resources:   ["*"]
  matchConditions:
    - name: 'exclude-leases' # 每个匹配条件必须有一个唯一的名称
      expression: '!(request.resource.group == "coordination.k8s.io" && request.resource.resource == "leases")' # 匹配非租约资源
    - name: 'exclude-kubelet-requests'
      expression: '!("system:nodes" in request.userInfo.groups)' # 匹配非节点用户发出的请求
    - name: 'rbac' # 跳过 RBAC 请求
      expression: 'request.resource.group != "rbac.authorization.k8s.io"'
  validations:
    - expression: "!object.metadata.name.contains('demo') || object.metadata.namespace == 'demo'"

这些匹配条件可以访问与验证表达式相同的 CEL 变量。

在评估匹配条件时出现错误时,将不会评估策略。根据以下方式确定是否拒绝请求:

  1. 如果任何一个匹配条件求值结果为 false(不管其他错误),API 服务器将跳过 Webhook。

  2. 否则:

审计注解

auditAnnotations 可用于在 API 请求的审计事件中包括审计注解。

例如,以下是带有审计注解的准入策略:

apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicy
metadata:
  name: "demo-policy.example.com"
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
    - apiGroups:   ["apps"]
      apiVersions: ["v1"]
      operations:  ["CREATE", "UPDATE"]
      resources:   ["deployments"]
  validations:
    - key: "high-replica-count"
      expression: "object.spec.replicas > 50"
      messageExpression: "'Deployment spec.replicas set to ' + string(object.spec.replicas)"

当使用此准入策略验证 API 请求时,生成的审计事件将如下所示:

# 记录的审计事件
{
    "kind": "Event",
    "apiVersion": "audit.k8s.io/v1",
    "annotations": {
        "demo-policy.example.com/high-replica-count": "Deployment spec.replicas set to 128"
        # 其他注解
        ...
    }
    # 其他字段
    ...
}

在此示例中,只有 Deployment 的 spec.replicas 大于 50 时才会包含注解, 否则 CEL 表达式将求值为 null,并且不会包含注解。

请注意,审计注解键以 ValidatingAdmissionWebhook 的名称和 / 为前缀。 如果另一个准入控制器(例如准入 Webhook)使用完全相同的审计注解键, 则第一个包括审计注解值的准入控制器将出现在审计事件中,而所有其他值都将被忽略。

消息表达式

为了在策略拒绝请求时返回更友好的消息,我们在 spec.validations[i].messageExpression 中使用 CEL 表达式来构造消息。 与验证表达式类似,消息表达式可以访问 objectoldObjectrequestparams。 但是,与验证不同,消息表达式必须求值为字符串。

例如,为了在策略引用参数时更好地告知用户拒绝原因,我们可以有以下验证:

apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicy
metadata:
  name: "deploy-replica-policy.example.com"
spec:
  paramKind:
    apiVersion: rules.example.com/v1
    kind: ReplicaLimit
  matchConstraints:
    resourceRules:
    - apiGroups:   ["apps"]
      apiVersions: ["v1"]
      operations:  ["CREATE", "UPDATE"]
      resources:   ["deployments"]
  validations:
  - expression: "object.spec.replicas <= params.maxReplicas"
    messageExpression: "'object.spec.replicas must be no greater than ' + string(params.maxReplicas)"
    reason: Invalid

在创建限制副本为 3 的 Params 对象并设置绑定之后,当我们尝试创建具有 5 个副本的 Deployment 时,我们将收到以下消息:

$ kubectl create deploy --image=nginx nginx --replicas=5
error: failed to create deployment: deployments.apps "nginx" is forbidden: ValidatingAdmissionPolicy 'deploy-replica-policy.example.com' with binding 'demo-binding-test.example.com' denied request: object.spec.replicas must be no greater than 3

这比静态消息 "too many replicas" 更具说明性。

如果既定义了消息表达式,又在 spec.validations[i].message 中定义了静态消息, 则消息表达式优先于静态消息。 但是,如果消息表达式求值失败,则将使用静态消息。 此外,如果消息表达式求值为多行字符串,则会丢弃求值结果并使用静态消息(如果存在)。 请注意,静态消息也要检查是否存在多行字符串。

类型检查

创建或更新策略定义时,验证过程将解析它包含的表达式,在发现错误时报告语法错误并拒绝该定义。 之后,引用的变量将根据 spec.matchConstraints 的匹配类型检查类型错误,包括缺少字段和类型混淆。 类型检查的结果可以从 status.typeChecking 中获得。 status.typeChecking 的存在表示类型检查已完成,而空的 status.typeChecking 表示未发现错误。

例如,给定以下策略定义:

apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingAdmissionPolicy
metadata:
  name: "deploy-replica-policy.example.com"
spec:
  matchConstraints:
    resourceRules:
    - apiGroups:   ["apps"]
      apiVersions: ["v1"]
      operations:  ["CREATE", "UPDATE"]
      resources:   ["deployments"]
  validations:
  - expression: "object.replicas > 1" # should be "object.spec.replicas > 1"
    message: "must be replicated"
    reason: Invalid

status 字段将提供以下信息:

status:
  typeChecking:
    expressionWarnings:
    - fieldRef: spec.validations[0].expression
      warning: |-
        apps/v1, Kind=Deployment: ERROR: <input>:1:7: undefined field 'replicas'
         | object.replicas > 1
         | ......^        

如果在 spec.matchConstraints 中匹配了多个资源,则所有匹配的资源都将进行检查。 例如,以下策略定义:

apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingAdmissionPolicy
metadata:
  name: "replica-policy.example.com"
spec:
  matchConstraints:
    resourceRules:
    - apiGroups:   ["apps"]
      apiVersions: ["v1"]
      operations:  ["CREATE", "UPDATE"]
      resources:   ["deployments","replicasets"]
  validations:
  - expression: "object.replicas > 1" # should be "object.spec.replicas > 1"
    message: "must be replicated"
    reason: Invalid

将具有多个类型,并在警告消息中提供每种类型的类型检查结果。

status:
  typeChecking:
    expressionWarnings:
    - fieldRef: spec.validations[0].expression
      warning: |-
        apps/v1, Kind=Deployment: ERROR: <input>:1:7: undefined field 'replicas'
         | object.replicas > 1
         | ......^
        apps/v1, Kind=ReplicaSet: ERROR: <input>:1:7: undefined field 'replicas'
         | object.replicas > 1
         | ......^        

类型检查具有以下限制:

  • 没有通配符匹配。 如果 spec.matchConstraints.resourceRules 中的任何一个 apiGroupsapiVersionsresources 包含 "*",则不会检查与 "*" 匹配的类型。
  • 匹配的类型数量最多为 10 种。这是为了防止手动指定过多类型的策略消耗过多计算资源。 按升序处理组、版本,然后是资源,忽略第 11 个及其之后的组合。
  • 类型检查不会以任何方式影响策略行为。即使类型检查检测到错误,策略也将继续评估。 如果在评估期间出现错误,则失败策略将决定其结果。
  • 类型检查不适用于 CRD(自定义资源定义),包括匹配的 CRD 类型和 paramKind 的引用。 对 CRD 的支持将在未来发布中推出。

变量组合

如果表达式变得太复杂,或者表达式的一部分可重用且进行评估时计算开销较大,可以将表达式的某些部分提取为变量。 变量是一个命名表达式,后期可以在其他表达式中的 variables 中引用。

spec:
  variables:
    - name: foo
      expression: "'foo' in object.spec.metadata.labels ? object.spec.metadata.labels['foo'] : 'default'"
  validations:
    - expression: variables.foo == 'bar'

变量在首次引用时会被延迟求值。评估期间发生的任何错误都将在评估引用表达式期间报告, 结果和可能的错误都会被记录下来,且在运行时开销中仅计为一次。

变量的顺序很重要,因为一个变量可以引用在它之前定义的其他变量。 对顺序的要求可以防止循环引用。

以下是强制镜像仓库名称与其命名空间中定义的环境相匹配的一个较复杂示例。

# 此策略强制除 "exempt" Deployment 或任何不属于 “example.com” 组织的容器
#(例如常见的 sidecar)外的 Deployment 的所有容器的镜像库与其命名空间的环境标签相匹配。
# 例如,如果命名空间的标签为 {"environment": "staging"},则所有容器镜像必须是
# staging.example.com/* 或根本不包含 “example.com”,除非 Deployment 有
# {"exempt": "true"} 标签。
apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingAdmissionPolicy
metadata:
  name: "image-matches-namespace-environment.policy.example.com"
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
    - apiGroups:   ["apps"]
      apiVersions: ["v1"]
      operations:  ["CREATE", "UPDATE"]
      resources:   ["deployments"]
  variables:
  - name: environment
    expression: "'environment' in namespaceObject.metadata.labels ? namespaceObject.metadata.labels['environment'] : 'prod'"
  - name: exempt
    expression: "'exempt' in object.metadata.labels && object.metadata.labels['exempt'] == 'true'"
  - name: containers
    expression: "object.spec.template.spec.containers"
  - name: containersToCheck
    expression: "variables.containers.filter(c, c.image.contains('example.com/'))"
  validations:
  - expression: "variables.exempt || variables.containersToCheck.all(c, c.image.startsWith(variables.environment + '.'))"
    messageExpression: "'only ' + variables.environment + ' images are allowed in namespace ' + namespaceObject.metadata.name"

在此策略被绑定到 default 命名空间(标签为 environment: prod)的情况下, 以下创建 Deployment 的尝试将被拒绝。

kubectl create deploy --image=dev.example.com/nginx invalid

错误信息类似于:

error: failed to create deployment: deployments.apps "invalid" is forbidden: ValidatingAdmissionPolicy 'image-matches-namespace-environment.policy.example.com' with binding 'demo-binding-test.example.com' denied request: only prod images are allowed in namespace default

6.4 - 众所周知的标签、注解和污点

Kubernetes 将所有标签和注解保留在 kubernetes.iok8s.io 名字空间中。

本文档既可作为值的参考,也可作为分配值的协调点。

API 对象上使用的标签、注解和污点

apf.kubernetes.io/autoupdate-spec

类别:注解

例子:apf.kubernetes.io/autoupdate-spec: "true"

用于:FlowSchemaPriorityLevelConfiguration 对象

如果在 FlowSchema 或 PriorityLevelConfiguration 上将此注解设置为 true, 那么该对象的 spec 将由 kube-apiserver 进行管理。如果 API 服务器不识别 APF 对象, 并且你对其添加了自动更新的注解,则 API 服务器将删除整个对象。否则,API 服务器不管理对象规约。 更多细节参阅维护强制性和建议的配置对象

app.kubernetes.io/component

类别:标签

例子:app.kubernetes.io/component: "database"

用于:所有对象(通常用于工作负载资源)。

应用架构中的组件。

推荐标签之一。

app.kubernetes.io/created-by(已弃用)

类别:标签

示例:app.kubernetes.io/created-by: "controller-manager"

用于:所有对象(通常用于工作负载资源)。

创建此资源的控制器/用户。

app.kubernetes.io/instance

类别:标签

示例:app.kubernetes.io/instance: "mysql-abcxzy"

用于:所有对象(通常用于工作负载资源)。

标识应用实例的唯一名称。要分配一个不唯一的名称,可使用 app.kubernetes.io/name

推荐标签之一。

app.kubernetes.io/managed-by

类别:标签

示例:app.kubernetes.io/managed-by: "helm"

用于:所有对象(通常用于工作负载资源)。

用于管理应用操作的工具。

推荐标签之一。

app.kubernetes.io/name

类别:标签

示例:app.kubernetes.io/name: "mysql"

用于:所有对象(通常用于工作负载资源)。

应用的名称。

推荐标签之一。

app.kubernetes.io/part-of

类别:标签

示例:app.kubernetes.io/part-of: "wordpress"

用于:所有对象(通常用于工作负载资源)。

此应用所属的更高级别应用的名称。

推荐标签之一。

app.kubernetes.io/version

类别:标签

示例:app.kubernetes.io/version: "5.7.21"

用于:所有对象(通常用于工作负载资源)。

值的常见形式包括:

推荐标签之一。

applyset.kubernetes.io/additional-namespaces (alpha)

类别:注解

示例:applyset.kubernetes.io/additional-namespaces: "namespace1,namespace2"

用于:作为 ApplySet 父对象使用的对象。

此注解处于 alpha 阶段。 对于 Kubernetes 1.30 版本,如果定义它们的 CustomResourceDefinition 打了 applyset.kubernetes.io/is-parent-type 标签, 那么你可以在 Secret、ConfigMaps 或自定义资源上使用此注解。

规范的部分功能用来实现 在 kubectl 中基于 ApplySet 的删除。 此注解应用于父对象,这些父对象用于跟踪 ApplySet 以将 ApplySet 的作用域扩展到父对象自己的命名空间(如果有的话)之外。 注解的值是以逗号分隔的命名空间的名字列表,不包含在其中找到对象的父命名空间。

applyset.kubernetes.io/contains-group-resources (alpha)

类别:注解

示例:applyset.kubernetes.io/contains-group-resources: "certificates.cert-manager.io,configmaps,deployments.apps,secrets,services"

用于:作为 ApplySet 父对象使用的对象。

此注解处于 alpha 阶段。 对于 Kubernetes 1.30 版本, 如果定义它们的 CustomResourceDefinition 打了 applyset.kubernetes.io/is-parent-type 标签, 那么你可以在 Secret、ConfigMaps 或自定义资源上使用此注解。

规范的部分功能用来实现 在 kubectl 中基于 ApplySet 的删除。 此注解应用于父对象, 这些父对象用于跟踪 ApplySet 以优化 ApplySet 成员对象列表。 它在 AppySet 规范中是可选的,因为工具可以执行发现或使用不同的优化。 然而,对于 Kubernetes 1.30 版本,它是 kubectl 必需的。 当存在时,注解的值必须是一个以逗号分隔的 group-kinds 列表,采用完全限定的名称格式,例如 <resource>.<group>

applyset.kubernetes.io/id (alpha)

类别:标签

示例:applyset.kubernetes.io/id: "applyset-0eFHV8ySqp7XoShsGvyWFQD3s96yqwHmzc4e0HR1dsY-v1"

用于:作为 ApplySet 父对象使用的对象。

此注解处于 alpha 阶段。 对于 Kubernetes 1.30 版本, 如果定义它们的 CustomResourceDefinition 打了 applyset.kubernetes.io/is-parent-type 标签,那么你可以在 Secret、ConfigMaps 或自定义资源上使用此注解。

规范的部分功能用来实现 在 kubectl 中基于 ApplySet 的删除。 此标签使对象成为 AppySet 父对象。 它的值是 ApplySet 的唯一 ID,该 ID 派生自父对象本身的标识。 该 ID 必须 是所在对象的 group-kind-name-namespace 的 hash 的 base64 编码(使用 RFC4648 的 URL 安全编码), 格式为: <base64(sha256(<name>.<namespace>.<kind>.<group>))>。 此标签的值与对象 UID 之间没有关系。

applyset.kubernetes.io/is-parent-type (alpha)

类别:标签

示例:applyset.kubernetes.io/is-parent-type: "true"

用于:自定义资源 (CRD)

此注解处于 alpha 阶段。 规范的部分功能用来实现 在 kubectl 中基于 ApplySet 的删除。 你可以在 CustomResourceDefinition (CRD) 上设置这个标签, 以将它定义的自定义资源类型(而不是 CRD 本身)标识为 ApplySet 的允许父类。 这个标签唯一允许的值是 "true";如果你想将一个 CRD 标记为不是 ApplySet 的有效父级,请省略这个标签。

applyset.kubernetes.io/part-of (alpha)

类别:标签

示例:applyset.kubernetes.io/part-of: "applyset-0eFHV8ySqp7XoShsGvyWFQD3s96yqwHmzc4e0HR1dsY-v1"

用于:所有对象。

此注解处于 alpha 阶段。 规范的部分功能用来实现 在 kubectl 中基于 ApplySet 的删除。 此标签使对象成为 ApplySet 的成员。 标签的值 必须 与父对象上的 applyset.kubernetes.io/id 标签的值相匹配。

applyset.kubernetes.io/tooling (alpha)

类别:注解

示例:applyset.kubernetes.io/tooling: "kubectl/v1.30"

用于:作为 ApplySet 父对象使用的对象。

此注解处于 alpha 阶段。 对于 Kubernetes 1.30 版本, 如果定义它们的 CustomResourceDefinition 打了 applyset.kubernetes.io/is-parent-type 标签,那么你可以在 Secret、ConfigMaps 或自定义资源上使用此注解。

规范的部分功能用来实现 在 kubectl 中基于 ApplySet 的删除。 此注解应用于父对象,这些父对象用于跟踪 ApplySet 以指示哪个工具管理 AppySet。 工具应该拒绝改变属于其他工具 ApplySets。 该值必须采用 <toolname>/<semver> 格式。

apps.kubernetes.io/pod-index (beta)

类别:标签

例子:apps.kubernetes.io/pod-index: "0"

用于:Pod

当 StatefulSet 控制器为 StatefulSet 创建 Pod 时,该控制器会在 Pod 上设置这个标签。 标签的值是正在创建的 Pod 的序号索引。

更多细节参阅 StatefulSet 主题中的 Pod 索引标签。 请注意,PodIndexLabel 特性门控必须被启用,才能将此标签添加到 Pod 上。

cluster-autoscaler.kubernetes.io/safe-to-evict

类别:注解

例子:cluster-autoscaler.kubernetes.io/safe-to-evict: "true"

用于:Pod

当这个注解设置为 "true" 时,即使其他规则通常会阻止驱逐操作,也会允许该集群自动扩缩器驱逐一个 Pod。 集群自动扩缩器从不驱逐将此注解显式设置为 "false" 的 Pod;你可以针对要保持运行的重要 Pod 设置此注解。 如果未设置此注解,则集群自动扩缩器将遵循其 Pod 级别的行为。

config.kubernetes.io/local-config

类别:注解

例子:config.kubernetes.io/local-config: "true"

用于:所有对象

该注解用于清单中的对象,表示某对象是本地配置,不应提交到 Kubernetes API。

对于这个注解,当值为 "true" 时,表示该对象仅被客户端工具使用,不应提交到 API 服务器。

当值为 "false" 时,可以用来声明该对象应提交到 API 服务器,即使它是本地对象。

该注解是 Kubernetes 资源模型 (KRM) 函数规范的一部分,被 Kustomize 和其他类似的第三方工具使用。 例如,Kustomize 会从其最终构建输出中删除带有此注解的对象。

container.apparmor.security.beta.kubernetes.io/* (beta)

类别:注解

例子:container.apparmor.security.beta.kubernetes.io/my-container: my-custom-profile

用于:Pod

此注解允许你为 Kubernetes Pod 中的容器指定 AppArmor 安全配置文件。 更多细节参阅 AppArmor 教程。 该教程演示了如何使用 AppArmor 限制容器的权能和访问权限。

所指定的配置文件定义了容器进程必须遵守的规则集和限制集。这有助于针对容器实施安全策略和隔离措施。

internal.config.kubernetes.io/* (保留的前缀)

类别:注解

用于:所有对象

该前缀被保留,供遵从 Kubernetes 资源模型 (KRM) 函数规范的编排工具内部使用。 带有该前缀的注解仅在编排过程中使用,不会持久化到文件系统。 换句话说,编排工具应从本地文件系统读取文件时设置这些注解,并在将函数输出写回文件系统时删除它们。

除非特定注解另有说明,KRM 函数不得修改带有此前缀的注解。 这使得编排工具可以添加额外的内部注解,而不需要更改现有函数。

internal.config.kubernetes.io/path

类别:注解

例子:internal.config.kubernetes.io/path: "relative/file/path.yaml"

用于:所有对象

此注解记录了加载对象清单文件的(斜线分隔、与操作系统无关)相对路径。 该路径相对于文件系统上由编排工具确定的固定位置。

该注解是 Kubernetes 资源模型 (KRM) 函数规范的一部分,被 Kustomize 和其他类似的第三方工具使用。

KRM 函数不应在输入对象上修改此注解,除非它正在修改引用的文件。 KRM 函数可以在它所生成的对象上包含这个注解。

internal.config.kubernetes.io/index

类别:注解

例子:internal.config.kubernetes.io/index: "2"

用于:所有对象

该注解记录了包含对象的 YAML 文档在加载对象的清单文件中的零索引位置。 请注意,YAML 文档由三个破折号 (---) 分隔,每个文档可以包含一个对象。 如果未指定此注解,则该值为 0。

该注解是 Kubernetes 资源模型 (KRM) 函数规范的一部分,被 Kustomize 和其他类似的第三方工具使用。

KRM 函数不应在输入对象上修改此注解,除非它正在修改引用的文件。 KRM 函数可以在它所生成的对象上包含这个注解。

kubernetes.io/arch

类别:标签

例子:kubernetes.io/arch: "amd64"

用于:Node

Kubelet 使用 Go 定义的 runtime.GOARCH 填充它。如果你混合使用 ARM 和 X86 节点,这会很方便。

kubernetes.io/os

类别:标签

例子:kubernetes.io/os: "linux"

用于:Node,Pod

对于节点,kubelet 会根据 Go 定义的 runtime.GOOS 填充这个值。 你可以很方便地在集群中混合使用操作系统(例如:混合使用 Linux 和 Windows 节点)。

你还可以在 Pod 上设置这个标签。 Kubernetes 允许你为此标签设置任何值;如果你使用此标签, 你应该将其设置为与该 Pod 实际使用的操作系统相对应的 Go runtime.GOOS 字符串。

当 Pod 的 kubernetes.io/os 标签值与节点上的标签值不匹配时,节点上的 kubelet 不会运行该 Pod。 但是,kube-scheduler 并未考虑这一点。 另外,如果你为 Pod 指定的操作系统与运行该 kubelet 的节点操作系统不相同,那么 kubelet 会拒绝运行该 Pod。 请查看 Pod 操作系统 了解更多详情。

kubernetes.io/metadata.name

类别:标签

例子:kubernetes.io/metadata.name: "mynamespace"

用于:Namespace

Kubernetes API 服务器(控制平面 的一部分)在所有 Namespace 上设置此标签。标签值被设置 Namespace 的名称。你无法更改此标签的值。

如果你想使用标签选择算符定位特定 Namespace,这很有用。

kubernetes.io/limit-ranger

类别:注解

例子:kubernetes.io/limit-ranger: "LimitRanger plugin set: cpu, memory request for container nginx; cpu, memory limit for container nginx"

用于:Pod

Kubernetes 默认不提供任何资源限制,这意味着除非你明确定义限制,否则你的容器将可以无限消耗 CPU 和内存。 你可以为 Pod 定义默认请求或默认限制。为此,你可以在相关命名空间中创建一个 LimitRange。 在你定义 LimitRange 后部署的 Pod 将受到这些限制。 注解 kubernetes.io/limit-ranger 记录了为 Pod 指定的资源默认值,以及成功应用这些默认值。 有关更多详细信息,请阅读 LimitRanges

addonmanager.kubernetes.io/mode

类别:标签

示例:addonmanager.kubernetes.io/mode: "Reconcile"

用于:所有对象。

要指定如何管理外接插件,你可以使用 addonmanager.kubernetes.io/mode 标签。 这个标签可以有三个标签之一:ReconcileEnsureExists,或者 Ignore

  • Reconcile:插件资源将定期与预期状态协调。如果有任何差异,插件管理器将根据需要重新创建、重新配置或删除资源。 如果没有指定标签,此值是默认值。
  • EnsureExists:插件资源将仅检查是否存在,但在创建后不会修改。当没有具有该名称的资源实例时, 外接程序管理器将创建或重新创建资源。
  • Ignore:插件资源将被忽略。此模式对于与外接插件管理器不兼容或由其他控制器管理的插件程序非常有用。

有关详细信息,请参见 Addon-manager

beta.kubernetes.io/arch (已弃用)

类别:标签

此标签已被弃用。请改用 kubernetes.io/arch

beta.kubernetes.io/os (已弃用)

类别:标签

此标签已被弃用。请改用 kubernetes.io/os

kube-aggregator.kubernetes.io/automanaged

类别:标签

例子:kube-aggregator.kubernetes.io/automanaged: "onstart"

用于:APIService

kube-apiserver 会在由 API 服务器自动创建的所有 APIService 对象上设置这个标签。 该标签标记了控制平面应如何管理该 APIService。你不应自行添加、修改或删除此标签。

有两个可能的值:

  • onstart:API 服务器应在启动时协调 APIService,但在其他时间不会进行协调。
  • true:API 服务器应持续协调此 APIService。

service.alpha.kubernetes.io/tolerate-unready-endpoints(已弃用)

类别:注解

用于:StatefulSet

Service 上的这个注解表示 Endpoints 控制器是否应该继续为未准备好的 Pod 创建 Endpoints。 这些 Service 的 Endpoints 保留其 DNS 记录,并从 kubelet 启动 Pod 中的所有容器并将其标记为 Running 的那一刻起继续接收 Service 的流量,直到 kubelet 停止所有容器并从 API 服务器删除 Pod 为止。

kubernetes.io/hostname

类别:标签

例子:kubernetes.io/hostname: "ip-172-20-114-199.ec2.internal"

用于:Node

Kubelet 使用主机名填充此标签。请注意,可以通过将 --hostname-override 标志传递给 kubelet 来替代“实际”主机名。

此标签也用作拓扑层次结构的一部分。有关详细信息,请参阅 topology.kubernetes.io/zone

kubernetes.io/change-cause

类别:注解

例子:kubernetes.io/change-cause: "kubectl edit --record deployment foo"

用于:所有对象

此注解是对某些事物发生变更的原因的最佳猜测。

--record 添加到可能会更改对象的 kubectl 命令时会填充它。

kubernetes.io/description

类别:注解

例子:kubernetes.io/description: "Description of K8s object."

用于:所有对象

此注解用于描述给定对象的特定行为。

kubernetes.io/enforce-mountable-secrets

类别:注解

例子:kubernetes.io/enforce-mountable-secrets: "true"

用于:ServiceAccount

此注解的值必须为 true 才能生效。此注解表示作为此服务账号运行的 Pod 只能引用在服务账号的 secrets 字段中指定的 Secret API 对象。

node.kubernetes.io/exclude-from-external-load-balancers

类别:标签

例子:node.kubernetes.io/exclude-from-external-load-balancers

用于:Node

Kubernetes 自动在其创建的集群上启用 ServiceNodeExclusion 特性门控。 在一个集群上启用此特性门控后,你可以添加标签到特定的 Worker 节点,将这些节点从后端服务器列表排除在外。 以下命令可用于从后端集的后端服务器列表中排除一个 Worker 节点:

kubectl label nodes <node-name> node.kubernetes.io/exclude-from-external-load-balancers=true

controller.kubernetes.io/pod-deletion-cost

类别:注解

例子:controller.kubernetes.io/pod-deletion-cost: "10"

用于:Pod

该注解用于设置 Pod 删除成本允许用户影响 ReplicaSet 缩减顺序。注解解析为 int32 类型。

cluster-autoscaler.kubernetes.io/enable-ds-eviction

类别:注解

例子:cluster-autoscaler.kubernetes.io/enable-ds-eviction: "true"

用于:Pod

该注解控制 DaemonSet Pod 是否应由 ClusterAutoscaler 驱逐。 该注解需要在 DaemonSet 清单中的 DaemonSet Pod 上指定。 当该注解设为 "true" 时,即使其他规则通常会阻止驱逐,也将允许 ClusterAutoscaler 驱逐 DaemonSet Pod。 要取消允许 ClusterAutoscaler 驱逐 DaemonSet Pod,你可以为重要的 DaemonSet Pod 将该注解设为 "false"。 如果未设置该注解,则 Cluster Autoscaler 将遵循其整体行为(即根据其配置驱逐 DaemonSet)。

kubernetes.io/ingress-bandwidth

类别:注解

示例:kubernetes.io/ingress-bandwidth: 10M

用于:Pod

你可以对 Pod 应用服务质量流量控制并有效限制其可用带宽。 入站流量(到 Pod)通过控制排队的数据包来处理,以有效地处理数据。 要限制 Pod 的带宽,请编写对象定义 JSON 文件并使用 kubernetes.io/ingress-bandwidth 注解指定数据流量速度。用于指定入站的速率单位是每秒, 作为量纲(Quantity)。 例如,10M 表示每秒 10 兆比特。

kubernetes.io/egress-bandwidth

类别:注解

示例:kubernetes.io/egress-bandwidth: 10M

用于:Pod

出站流量(来自 pod)由策略控制,策略只是丢弃超过配置速率的数据包。 你为一个 Pod 所设置的限制不会影响其他 Pod 的带宽。 要限制 Pod 的带宽,请编写对象定义 JSON 文件并使用 kubernetes.io/egress-bandwidth 注解指定数据流量速度。 用于指定出站的速率单位是每秒比特数, 以量纲(Quantity)的形式给出。 例如,10M 表示每秒 10 兆比特。

beta.kubernetes.io/instance-type (已弃用)

类别:标签

node.kubernetes.io/instance-type

类别:标签

例子:node.kubernetes.io/instance-type: "m3.medium"

用于:Node

Kubelet 使用云驱动定义的实例类型填充它。 仅当你使用云驱动时才会设置此项。如果你希望将某些工作负载定位到某些实例类型, 则此设置非常方便,但通常你希望依靠 Kubernetes 调度程序来执行基于资源的调度。 你应该基于属性而不是实例类型来调度(例如:需要 GPU,而不是需要 g2.2xlarge)。

failure-domain.beta.kubernetes.io/region (已弃用)

类别:标签

failure-domain.beta.kubernetes.io/zone (已弃用)

类别:标签

pv.kubernetes.io/bind-completed

类别:注解

例子:pv.kubernetes.io/bind-completed: "yes"

用于:PersistentVolumeClaim

当在 PersistentVolumeClaim (PVC) 上设置此注解时,表示 PVC 的生命周期已通过初始绑定设置。 当存在此注解时,该信息会改变控制平面解释 PVC 对象状态的方式。此注解的值对 Kubernetes 无关紧要。

pv.kubernetes.io/bound-by-controller

类别:注解

例子:pv.kubernetes.io/bound-by-controller: "yes"

用于:PersistentVolume、PersistentVolumeClaim

如果此注解设置在 PersistentVolume 或 PersistentVolumeClaim 上,则表示存储绑定 (PersistentVolume → PersistentVolumeClaim,或 PersistentVolumeClaim → PersistentVolume) 已由控制器配置完毕。 如果未设置此注解,且存在存储绑定,则缺少该注解意味着绑定是手动完成的。此注解的值无关紧要。

pv.kubernetes.io/provisioned-by

类别:注解

例子:pv.kubernetes.io/provisioned-by: "kubernetes.io/rbd"

用于:PersistentVolume

此注解被添加到已由 Kubernetes 动态制备的 PersistentVolume (PV)。 它的值是创建卷的卷插件的名称。它同时服务于用户(显示 PV 的来源)和 Kubernetes(识别其决策中动态制备的 PV)。

pv.kubernetes.io/migrated-to

类别:注解

例子:pv.kubernetes.io/migrated-to: pd.csi.storage.gke.io

用于:PersistentVolume、PersistentVolumeClaim

它被添加到 PersistentVolume (PV) 和 PersistentVolumeClaim (PVC),应该由其相应的 CSI 驱动程序通过 CSIMigration 特性门控动态制备/删除。设置此注解后,Kubernetes 组件将“停止”, 而 external-provisioner 将作用于对象。

statefulset.kubernetes.io/pod-name

类别:标签

例子:statefulset.kubernetes.io/pod-name: "mystatefulset-7"

当 StatefulSet 控制器为 StatefulSet 创建 Pod 时,控制平面会在该 Pod 上设置此标签。标签的值是正在创建的 Pod 的名称。

有关详细信息,请参阅 StatefulSet 主题中的 Pod 名称标签

scheduler.alpha.kubernetes.io/node-selector

类别:注解

例子:scheduler.alpha.kubernetes.io/node-selector: "name-of-node-selector"

用于:Namespace

PodNodeSelector 使用此注解键为名字空间中的 Pod 设置节点选择算符。

topology.kubernetes.io/region

类别:标签

例子:topology.kubernetes.io/region: "us-east-1"

用于:Node、PersistentVolume

请参阅 topology.kubernetes.io/zone

topology.kubernetes.io/zone

类别:标签

例子:topology.kubernetes.io/zone: "us-east-1c"

用于:Node、PersistentVolume

在 Node 上kubelet 或外部 cloud-controller-manager 使用 cloudprovider 提供的信息填充它。 仅当你使用 cloudprovider 时才会设置此项。 但是,如果它在你的拓扑中有意义,你应该考虑在 Node 上设置它。

在 PersistentVolume 上:拓扑感知卷配置器将自动在 PersistentVolume 上设置 Node 亲和性约束。

一个 Zone 代表一个逻辑故障域。Kubernetes 集群通常跨越多个 Zone 以提高可用性。 虽然 Zone 的确切定义留给基础设施实现,但 Zone 的常见属性包括 Zone 内非常低的网络延迟、Zone 内的免费网络流量以及与其他 Zone 的故障独立性。例如,一个 Zone 内的 Node 可能共享一个网络交换机, 但不同 Zone 中的 Node 无法共享交换机。

一个 Region 代表一个更大的域,由一个或多个 Zone 组成。Kubernetes 集群跨多个 Region 并不常见, 虽然 Zone 或 Region 的确切定义留给基础设施实现, 但 Region 的共同属性包括它们之间的网络延迟比它们内部更高,它们之间的网络流量成本非零, 以及与其他 Zone 或 Region 的故障独立性。例如,一个 Region 内的 Node 可能共享电力基础设施 (例如 UPS 或发电机),但不同 Region 的 Node 通常不会共享电力基础设施。

Kubernetes 对 Zone 和 Region 的结构做了一些假设:

  1. Zone 和 Region 是分层的:Zone 是 Region 的严格子集,没有 Zone 可以在两个 Region 中;

  2. Zone 名称跨 Region 是唯一的;例如,Region “africa-east-1” 可能由 Zone “africa-east-1a” 和 “africa-east-1b” 组成。

你可以大胆假设拓扑标签不会改变。尽管严格地讲标签是可变的, 但节点的用户可以假设给定节点只能通过销毁和重新创建才能完成 Zone 间移动。

Kubernetes 可以通过多种方式使用这些信息。例如,调度程序会自动尝试将 ReplicaSet 中的 Pod 分布在单 Zone 集群中的多个节点上(以便减少节点故障的影响,请参阅 kubernetes.io/hostname)。 对于多 Zone 集群,这种分布行为也适用于 Zone(以减少 Zone 故障的影响)。 Zone 级别的 Pod 分布是通过 SelectorSpreadPriority 实现的。

SelectorSpreadPriority 是一个尽力而为的放置机制。如果集群中的 Zone 是异构的 (例如:节点数量不同、节点类型不同或 Pod 资源需求有别等),这种放置机制可能会让你的 Pod 无法实现跨 Zone 均匀分布。 如果需要,你可以使用同质 Zone(节点数量和类型均相同)来减少不均匀分布的可能性。

调度程序还将(通过 VolumeZonePredicate 条件)确保申领给定卷的 Pod 仅被放置在与该卷相同的 Zone 中。 卷不能跨 Zone 挂接。

你应该考虑手动添加标签(或添加对 PersistentVolumeLabel 的支持)。 基于 PersistentVolumeLabel,调度程序可以防止 Pod 挂载来自其他 Zone 的卷。 如果你的基础架构没有此限制,则不需要将 Zone 标签添加到卷上。

volume.beta.kubernetes.io/storage-provisioner (已弃用)

类别:注解

例子:volume.beta.kubernetes.io/storage-provisioner: "k8s.io/minikube-hostpath"

用于:PersistentVolumeClaim

此注解自 v1.23 已被弃用。 参见 volume.kubernetes.io/storage-provisioner

volume.beta.kubernetes.io/storage-class(已弃用)

类别:注解

例子:volume.beta.kubernetes.io/storage-class: "example-class"

用于:PersistentVolume、PersistentVolumeClaim

此注解可以为 PersistentVolume(PV)或 PersistentVolumeClaim(PVC)指定 StorageClass。 当 storageClassName 属性和 volume.beta.kubernetes.io/storage-class 注解均被指定时, 注解 volume.beta.kubernetes.io/storage-class 将优先于 storageClassName 属性。

此注解已被弃用。作为替代方案,你应该为 PersistentVolumeClaim 或 PersistentVolume 设置 storageClassName 字段

volume.beta.kubernetes.io/mount-options(已弃用)

类别:注解

例子:volume.beta.kubernetes.io/mount-options: "ro,soft"

用于:PersistentVolume

针对 PersistentVolume 挂载到一个节点上的情形, Kubernetes 管理员可以指定更多的挂载选项

volume.kubernetes.io/storage-provisioner

类别:注解

用于:PersistentVolumeClaim

此注解将被添加到根据需要动态制备的 PVC 上。 其取值是假设为 PVC 制备卷时卷插件的名称。

volume.kubernetes.io/selected-node

类别:注解

用于:PersistentVolumeClaim

此注解被添加到调度程序所触发的 PVC 上,对应的 PVC 需要被动态制备。注解值是选定节点的名称。

volumes.kubernetes.io/controller-managed-attach-detach

类别:注解

用于:Node

如果节点已在其自身上设置了注解 volumes.kubernetes.io/controller-managed-attach-detach, 那么它的存储挂接和解除挂接的操作是交由 卷挂接/解除挂接控制器来管理的。

注解的值并不重要。

node.kubernetes.io/windows-build

类别:标签

例子:node.kubernetes.io/windows-build: "10.0.17763"

用于:Node

当 kubelet 在 Microsoft Windows 上运行时,它会自动标记其所在节点以记录所使用的 Windows Server 的版本。

标签的值采用 “MajorVersion.MinorVersion.BuildNumber” 格式。

service.kubernetes.io/headless

类别:标签

例子:service.kubernetes.io/headless: ""

用于:Service

当拥有的 Service 是无头类型时,控制平面将此标签添加到 Endpoints 对象。

service.kubernetes.io/topology-aware-hints(已弃用)

例子:service.kubernetes.io/topology-aware-hints: "Auto"

用于:Service

此注解曾用于在 Service 中启用拓扑感知提示(topology aware hints)。 然而,拓扑感知提示已经做了更名操作, 此概念现在名为拓扑感知路由(topology aware routing)。 在 Service 上将该注解设置为 Auto 会配置 Kubernetes 控制平面, 以将拓扑提示添加到该 Service 关联的 EndpointSlice 上。你也可以显式地将该注解设置为 Disabled

如果你使用的是早于 1.30 的 Kubernetes 版本, 请查阅该版本对应的文档,了解其拓扑感知路由的工作方式。

此注解没有其他有效值。如果你不希望为 Service 启用拓扑感知提示,不要添加此注解。

service.kubernetes.io/topology-mode

类别:注解

例子:service.kubernetes.io/topology-mode: Auto

用于:Service

此注解提供了一种定义 Service 如何处理网络拓扑的方式; 例如,你可以配置 Service,以便 Kubernetes 更倾向于将客户端和服务器之间的流量保持在同一拓扑区域内。 在某些情况下,这有助于降低成本或提高网络性能。

更多细节参阅拓扑感知路由

kubernetes.io/service-name

类别:标签

例子:kubernetes.io/service-name: "my-website"

用于:EndpointSlice

Kubernetes 使用这个标签将 EndpointSlices服务关联。

这个标签记录了 EndpointSlice 后备服务的名称。 所有 EndpointSlice 都应将此标签设置为其关联服务的名称。

kubernetes.io/service-account.name

类别:注解

示例:kubernetes.io/service-account.name: "sa-name"

用于:Secret

这个注解记录了令牌(存储在 kubernetes.io/service-account-token 类型的 Secret 中)所代表的 ServiceAccount 的名称

kubernetes.io/service-account.uid

类别:注解

示例:kubernetes.io/service-account.uid: da68f9c6-9d26-11e7-b84e-002dc52800da

用于:Secret

该注解记录了令牌(存储在 kubernetes.io/service-account-token 类型的 Secret 中)所代表的 ServiceAccount 的唯一 ID

kubernetes.io/legacy-token-last-used

类别:标签

例子:kubernetes.io/legacy-token-last-used: 2022-10-24

用于:Secret

控制面仅为 kubernetes.io/service-account-token 类型的 Secret 添加此标签。 该标签的值记录着控制面最近一次接到客户端使用服务账号令牌进行身份验证请求的日期(ISO 8601 格式,UTC 时区)

如果上一次使用老的令牌的时间在集群获得此特性(添加于 Kubernetes v1.26)之前,则不会设置此标签。

endpointslice.kubernetes.io/managed-by

类别:标签

例子:endpointslice.kubernetes.io/managed-by: "controller"

用于:EndpointSlice

用于标示管理 EndpointSlice 的控制器或实体。该标签旨在使不同的 EndpointSlice 对象能够由同一集群内的不同控制器或实体管理。

endpointslice.kubernetes.io/skip-mirror

类别:标签

例子:endpointslice.kubernetes.io/skip-mirror: "true"

用于:Endpoints

可以在 Endpoints 资源上将此标签设置为 "true",以指示 EndpointSliceMirroring 控制器不应使用 EndpointSlice 镜像此 Endpoints 资源。

service.kubernetes.io/service-proxy-name

类别:标签

例子:service.kubernetes.io/service-proxy-name: "foo-bar"

用于:Service

kube-proxy 自定义代理会使用这个标签,它将服务控制委托给自定义代理。

experimental.windows.kubernetes.io/isolation-type (已弃用)

类别:注解

例子:experimental.windows.kubernetes.io/isolation-type: "hyperv"

用于:Pod

此注解用于运行具有 Hyper-V 隔离的 Windows 容器。

ingressclass.kubernetes.io/is-default-class

类别:注解

例子:ingressclass.kubernetes.io/is-default-class: "true"

用于:IngressClass

当单个 IngressClass 资源将此注解设置为 "true"时,新的未指定 Ingress 类的 Ingress 资源将被设置为此默认类。

kubernetes.io/ingress.class (已弃用)

类别:注解

用于:Ingress

storageclass.kubernetes.io/is-default-class

类别:注解

例子:storageclass.kubernetes.io/is-default-class: "true"

用于:StorageClass

当单个 StorageClass 资源将此注解设置为 "true" 时,新的未指定存储类的 PersistentVolumeClaim 资源将被设置为此默认类。

alpha.kubernetes.io/provided-node-ip (alpha)

类别:注解

例子:alpha.kubernetes.io/provided-node-ip: "10.0.0.1"

用于:Node

kubelet 可以在 Node 上设置此注解来表示其配置的 IPv4 与/或 IPv6 地址。

如果 kubelet 被启动时 --cloud-provider 标志设置为任一云驱动(包括外部云驱动和传统树内云驱动) kubelet 会在 Node 上设置此注解以表示从命令行标志(--node-ip)设置的 IP 地址。 云控制器管理器通过云驱动验证此 IP 是否有效。

batch.kubernetes.io/job-completion-index

类别:注解、标签

例子:batch.kubernetes.io/job-completion-index: "3"

用于:Pod

kube-controller-manager 中的 Job 控制器为使用 Indexed 完成模式创建的 Pod 设置此标签和注解。

请注意,PodIndexLabel 特性门控必须被启用,才能将其添加为 Pod 的标签,否则它只会用作注解。

batch.kubernetes.io/cronjob-scheduled-timestamp

类别:注解

例子:batch.kubernetes.io/cronjob-scheduled-timestamp: "2016-05-19T03:00:00-07:00"

用于:CronJob 所控制的 Job 和 Pod

此注解在 Job 是 CronJob 的一部分时用于记录 Job 的原始(预期)创建时间戳。 控制平面会将该值设置为 RFC3339 格式的时间戳。如果 Job 属于设置了时区的 CronJob, 则时间戳以该时区为基准。否则,时间戳以 controller-manager 的本地时间为准。

kubectl.kubernetes.io/default-container

类别:注解

例子:kubectl.kubernetes.io/default-container: "front-end-app"

此注解的值是此 Pod 的默认容器名称。例如,未指定 -c--container 标志时执行 kubectl logskubectl exec 命令将使用此默认容器。

kubectl.kubernetes.io/default-logs-container(已弃用)

类别:注解

例子:kubectl.kubernetes.io/default-logs-container: "front-end-app"

此注解的值是针对此 Pod 的默认日志记录容器的名称。例如,不带 -c--container 标志的 kubectl logs 将使用此默认容器。

kubectl.kubernetes.io/last-applied-configuration

类别:注解

例子:参见以下代码片段

    kubectl.kubernetes.io/last-applied-configuration: >
      {"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"name":"example","namespace":"default"},"spec":{"selector":{"matchLabels":{"app.kubernetes.io/name":foo}},"template":{"metadata":{"labels":{"app.kubernetes.io/name":"foo"}},"spec":{"containers":[{"image":"container-registry.example/foo-bar:1.42","name":"foo-bar","ports":[{"containerPort":42}]}]}}}}      

用于:所有对象

kubectl 命令行工具使用此注解作为一种旧的机制来跟踪变更。 该机制已被服务器端应用取代。

endpoints.kubernetes.io/over-capacity

类别:注解

例子:endpoints.kubernetes.io/over-capacity:truncated

用于:Endpoints

如果关联的 服务(Service) 有超过 1000 个后备端点, 则控制平面将此注解添加到 Endpoints 对象。 此注解表示 Endpoints 对象已超出容量,并且已将 Endpoints 数截断为 1000。

如果后端端点的数量低于 1000,则控制平面将移除此注解。

control-plane.alpha.kubernetes.io/leader(已弃用)

类别:注解

例子:control-plane.alpha.kubernetes.io/leader={"holderIdentity":"controller-0","leaseDurationSeconds":15,"acquireTime":"2023-01-19T13:12:57Z","renewTime":"2023-01-19T13:13:54Z","leaderTransitions":1}

用于:Endpoints

控制平面先前在 Endpoints 对象上设置此注解。此注解提供以下细节:

  • 当前的领导者是谁。
  • 获取当前领导权的时间。
  • 租约(领导权)的持续时间,以秒为单位。
  • 当前租约(当前领导权)应被续约的时间。
  • 过去发生的领导权转换次数。

Kubernetes 现在使用租约来管理 Kubernetes 控制平面的领导者分配。

batch.kubernetes.io/job-tracking (已弃用)

类别:注解

例子:batch.kubernetes.io/job-tracking: ""

用于:Job

Job 上存在此注解表明控制平面正在使用 Finalizer 追踪 Job

添加或删除此注解不再有效(Kubernetes v1.27 及更高版本), 所有 Job 均通过 Finalizer 进行追踪。

job-name (deprecated)

类别:标签

示例:job-name: "pi"

用于:由 Jobs 控制的 Jobs 和 Pods

controller-uid (deprecated)

类别:标签

示例:controller-uid: "$UID"

用于:由 Jobs 控制的 Job 和 Pod

batch.kubernetes.io/job-name

类别:标签

示例:batch.kubernetes.io/job-name: "pi"

用于:由 Job 控制的 Job 和 Pod

这个标签被用作一种用户友好的方式来获得与某个 Job 相对应的 Pod。 job-name 来自 Job 的 name 并且允许以一种简单的方式获得与 Job 对应的 Pod。

batch.kubernetes.io/controller-uid

类别:标签

示例:batch.kubernetes.io/controller-uid: "$UID"

用于:由 Job 控制的 Job 和 Pod

这个标签被用作一种编程方式来获得对应于某个 Job 的所有 Pod。 controller-uid 是在 selector 字段中设置的唯一标识符, 因此 Job 控制器可以获取所有对应的 Pod。

scheduler.alpha.kubernetes.io/defaultTolerations

类别:注解

例子:scheduler.alpha.kubernetes.io/defaultTolerations: '[{"operator": "Equal", "value": "value1", "effect": "NoSchedule", "key": "dedicated-node"}]'

用于:Namespace

此注解需要启用 PodTolerationRestriction 准入控制器。此注解键允许为某个命名空间分配容忍度,在这个命名空间中创建的所有新 Pod 都会被添加这些容忍度。

scheduler.alpha.kubernetes.io/tolerationsWhitelist

类别:注解

示例:scheduler.alpha.kubernetes.io/tolerationsWhitelist: '[{"operator": "Exists", "effect": "NoSchedule", "key": "dedicated-node"}]'

用于:命名空间

此注解只有在启用(Alpha) PodTolerationRestriction 控制器时才生效。注解值是一个 JSON 文档,它为它所注解的命名空间定义了一个允许容忍的列表。 当你创建一个 Pod 或修改其容忍度时,API 服务器将检查容忍度,以查看它们是否在允许列表中。 只有在检查成功的情况下,Pod 才被允操作。

scheduler.alpha.kubernetes.io/preferAvoidPods(已弃用)

类别:注解

用于:Node

此注解需要启用 NodePreferAvoidPods 调度插件。 该插件自 Kubernetes 1.22 起已被弃用。 请改用污点和容忍度

node.kubernetes.io/not-ready

类别:污点

例子:node.kubernetes.io/not-ready: "NoExecute"

用于:Node

Node 控制器通过监控 Node 的健康状况来检测 Node 是否准备就绪,并相应地添加或删除此污点。

node.kubernetes.io/unreachable

类别:污点

例子:node.kubernetes.io/unreachable: "NoExecute"

用于:Node

Node 控制器将此污点添加到对应节点状况 ReadyUnknown 的 Node 上。

node.kubernetes.io/unschedulable

类别:污点

例子:node.kubernetes.io/unschedulable: "NoSchedule"

用于:Node

在初始化 Node 期间,为避免竞争条件,此污点将被添加到 Node 上。

node.kubernetes.io/memory-pressure

类别:污点

例子:node.kubernetes.io/memory-pressure: "NoSchedule"

用于:Node

kubelet 根据在 Node 上观察到的 memory.availableallocatableMemory.available 检测内存压力。 然后将观察到的值与可以在 kubelet 上设置的相应阈值进行比较,以确定是否应添加/删除 Node 状况和污点。

node.kubernetes.io/disk-pressure

类别:污点

例子:node.kubernetes.io/disk-pressure :"NoSchedule"

用于:Node

kubelet 根据在 Node 上观察到的 imagefs.availableimagefs.inodesFreenodefs.availablenodefs.inodesFree(仅限 Linux )检测磁盘压力。 然后将观察到的值与可以在 kubelet 上设置的相应阈值进行比较,以确定是否应添加/删除 Node 状况和污点。

node.kubernetes.io/network-unavailable

类别:污点

例子:node.kubernetes.io/network-unavailable: "NoSchedule"

用于:Node

当使用的云驱动指示需要额外的网络配置时,此注解最初由 kubelet 设置。 只有云上的路由被正确地配置了,此污点才会被云驱动移除。

node.kubernetes.io/pid-pressure

类别:污点

例子:node.kubernetes.io/pid-pressure: "NoSchedule"

用于:Node

kubelet 检查 /proc/sys/kernel/pid_max 大小的 D 值和 Kubernetes 在 Node 上消耗的 PID, 以获取可用 PID 数量,并将其作为 pid.available 指标值。 然后该指标与在 kubelet 上设置的相应阈值进行比较,以确定是否应该添加/删除 Node 状况和污点。

node.kubernetes.io/out-of-service

类别:污点

例子:node.kubernetes.io/out-of-service:NoExecute

用于:Node

用户可以手动将污点添加到节点,将其标记为停止服务。 如果 kube-controller-manager 上启用了 NodeOutOfServiceVolumeDetach 特性门控, 并且一个节点被这个污点标记为停止服务,如果节点上的 Pod 没有对应的容忍度, 这类 Pod 将被强制删除,并且,针对在节点上被终止 Pod 的卷分离操作将被立即执行。

node.cloudprovider.kubernetes.io/uninitialized

类别:污点

例子:node.cloudprovider.kubernetes.io/uninitialized: "NoSchedule"

用于:Node

在使用“外部”云驱动启动 kubelet 时,在 Node 上设置此污点以将其标记为不可用,直到来自 cloud-controller-manager 的控制器初始化此 Node,然后移除污点。

node.cloudprovider.kubernetes.io/shutdown

类别:污点

例子:node.cloudprovider.kubernetes.io/shutdown: "NoSchedule"

用于:Node

如果 Node 处于云驱动所指定的关闭状态,则 Node 会相应地被设置污点,对应的污点和效果为 node.cloudprovider.kubernetes.io/shutdownNoSchedule

feature.node.kubernetes.io/*

类别:标签

示例:feature.node.kubernetes.io/network-sriov.capable: "true"

用于:节点

这些特性作为标签在运行 NFD 的节点上的 KubernetesNode 对象中公布。 所有内置的标签都使用 feature.node.kubernetes.io 标签命名空间,并且格式为 feature.node.kubernetes.io/<feature-name>: <true>。 NFD 有许多用于创建特定于供应商和应用程序的标签的扩展点。 有关详细信息,请参阅定制资源.

nfd.node.kubernetes.io/master.version

类别:注解

示例:nfd.node.kubernetes.io/master.version: "v0.6.0"

用于:节点

对于调度 NFD-master的节点, 此注解记录 NFD-master 的版本。它仅用于提供信息。

nfd.node.kubernetes.io/worker.version

类别:注解

示例:nfd.node.kubernetes.io/worker.version: "v0.4.0"

用于:节点

这个注解记录 NFD-worker 的版本(如果在节点上运行了一个 NFD-worker 的话)。 它只用于提供信息。

nfd.node.kubernetes.io/feature-labels

类别:注解

示例:nfd.node.kubernetes.io/feature-labels: "cpu-cpuid.ADX,cpu-cpuid.AESNI,cpu-hardware_multithreading,kernel-version.full"

用于:节点

此注解记录由 Node Feature Discovery (NFD) 管理的以逗号分隔的节点特性标签列表。NFD 将其用于内部机制。你不应该自己编辑这个注解。

nfd.node.kubernetes.io/extended-resources

类别:注解

示例:nfd.node.kubernetes.io/extended-resources: "accelerator.acme.example/q500,example.com/coprocessor-fx5"

用于:节点

此注解记录由 Node Feature Discovery (NFD) 管理的以逗号分隔的扩展资源列表。 NFD 将其用于内部机制。你不应该自己编辑这个注解。

nfd.node.kubernetes.io/node-name

类别:标签

例子:nfd.node.kubernetes.io/node-name: node-1

用于:Node

此标签指定哪个节点是 NodeFeature 对象的目标节点。 NodeFeature 对象的创建者必须设置此标签,而此对象的使用者应该使用此标签过滤为某个节点指定的特性。

service.beta.kubernetes.io/aws-load-balancer-access-log-emit-interval (beta)

示例:service.beta.kubernetes.io/aws-load-balancer-access-log-emit-interval: "5"

用于:Service

与 AWS 弹性负载均衡集成的云控制器管理器会根据此注解为 Service 配置负载均衡器。 此注解值决定负载均衡器写入日志条目的频率。例如,如果你将该值设置为 5,则日志的写入间隔为 5 秒。

service.beta.kubernetes.io/aws-load-balancer-access-log-enabled (beta)

示例:service.beta.kubernetes.io/aws-load-balancer-access-log-enabled: "false"

用于:Service

与 AWS 弹性负载均衡集成的云控制器管理器会根据此注解为 Service 配置负载均衡器。 如果你将此注解设置为 "true",则访问日志将被启用。

service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-name (beta)

示例:service.beta.kubernetes.io/aws-load-balancer-access-log-enabled: example

用于:Service

与 AWS 弹性负载均衡集成的云控制器管理器会根据此注解为 Service 配置负载均衡器。 负载均衡器将日志写入到一个你指定名称的 S3 桶。

service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-prefix (beta)

示例:service.beta.kubernetes.io/aws-load-balancer-access-log-enabled: "/example"

用于:Service

与 AWS 弹性负载均衡集成的云控制器管理器会根据此注解为 Service 配置负载均衡器。 负载均衡器用你指定的前缀写入日志对象。

service.beta.kubernetes.io/aws-load-balancer-additional-resource-tags (beta)

示例:service.beta.kubernetes.io/aws-load-balancer-additional-resource-tags: "Environment=demo,Project=example"

用于:Service

与 AWS 弹性负载均衡集成的云控制器管理器会根据此注解取值中逗号分隔的键/值对为负载均衡器配置标记(这是 AWS 的一个概念)。

service.beta.kubernetes.io/aws-load-balancer-alpn-policy (beta)

示例:service.beta.kubernetes.io/aws-load-balancer-alpn-policy: HTTP2Optional

用于:Service

AWS 负载均衡器控制器使用此注解。 参见 AWS 负载均衡器控制器文档中的注解

service.beta.kubernetes.io/aws-load-balancer-attributes (beta)

示例:service.beta.kubernetes.io/aws-load-balancer-attributes: "deletion_protection.enabled=true"

用于:Service

AWS 负载均衡器控制器使用此注解。 参见 AWS 负载均衡器控制器文档中的注解

service.beta.kubernetes.io/aws-load-balancer-backend-protocol (beta)

示例:service.beta.kubernetes.io/aws-load-balancer-backend-protocol: tcp

用于:Service

与 AWS 弹性负载均衡集成的云控制器管理器会根据此注解取值配置负载均衡器的监听器。

service.beta.kubernetes.io/aws-load-balancer-connection-draining-enabled (beta)

示例:service.beta.kubernetes.io/aws-load-balancer-connection-draining-enabled: "false"

用于:Service

与 AWS 弹性负载均衡集成的云控制器管理器会根据此注解取值配置负载均衡器。 负载均衡器的连接排空设置取决于你所设置的值。

service.beta.kubernetes.io/aws-load-balancer-connection-draining-timeout (beta)

示例:service.beta.kubernetes.io/aws-load-balancer-connection-draining-timeout: "60"

用于:Service

如果你为 type: LoadBalancer 的服务配置连接排空, 且你使用 AWS 云服务,则集成机制将根据此注解来配置排空期。 你所设置的值决定了排空超时秒数。

service.beta.kubernetes.io/aws-load-balancer-ip-address-type (beta)

示例:service.beta.kubernetes.io/aws-load-balancer-ip-address-type: ipv4

用于:Service

AWS 负载均衡器控制器使用此注解。 参见 AWS 负载均衡器控制器文档中的注解

service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout (beta)

示例:service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout: "60"

用于:Service

与 AWS 弹性负载均衡集成的云控制器管理器会根据此注解配置负载均衡器。 负载均衡器配置了一个空闲超时时间(以秒为单位)应用到其连接。 如果在空闲超时时间到期之前没有发送或接收任何数据,负载均衡器将关闭连接。

service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled (beta)

示例:service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"

用于:Service

与 AWS 弹性负载均衡集成的云控制器管理器会根据此注解配置负载均衡器。 如果你将此注解设置为 "true",每个负载均衡器节点将在所有启用的 可用区中的注册目标上均匀地分发请求。 如果你禁用跨区负载均衡,则每个负载均衡器节点仅在其可用区中跨注册目标均匀地分发请求。

service.beta.kubernetes.io/aws-load-balancer-eip-allocations (beta)

示例:service.beta.kubernetes.io/aws-load-balancer-eip-allocations: "eipalloc-01bcdef23bcdef456,eipalloc-def1234abc4567890"

用于:Service

与 AWS 弹性负载均衡集成的云控制器管理器会根据此注解配置负载均衡器。 取值是逗号分隔的弹性 IP 地址分配 ID 列表。

此注解仅与 type: LoadBalancer 的 Service 相关,其中负载均衡器是 AWS Network Load Balancer。

service.beta.kubernetes.io/aws-load-balancer-extra-security-groups (beta)

示例:service.beta.kubernetes.io/aws-load-balancer-extra-security-groups: "sg-12abcd3456,sg-34dcba6543"

用于:Service

与 AWS 弹性负载均衡集成的云控制器管理器会根据此注解配置负载均衡器。 此注解值是一个逗号分隔的附加 AWS VPC 安全组列表,用于配置负载均衡器。

service.beta.kubernetes.io/aws-load-balancer-healthcheck-healthy-threshold (beta)

示例:service.beta.kubernetes.io/aws-load-balancer-healthcheck-healthy-threshold: "3"

用于:Service

与 AWS 弹性负载均衡集成的云控制器管理器会根据此注解配置负载均衡器。 此注解值指定后端需要连续成功多少次健康检查才能被视为流量健康。

service.beta.kubernetes.io/aws-load-balancer-healthcheck-interval (beta)

示例:service.beta.kubernetes.io/aws-load-balancer-healthcheck-interval: "30"

用于:Service

与 AWS 弹性负载均衡集成的云控制器管理器会根据此注解配置负载均衡器。 此注解值指定负载均衡器进行健康检查探测之间的间隔秒数。

service.beta.kubernetes.io/aws-load-balancer-healthcheck-path (beta)

示例:service.beta.kubernetes.io/aws-load-balancer-healthcheck-path: /healthcheck

用于:Service

与 AWS 弹性负载均衡集成的云控制器管理器会根据此注解配置负载均衡器。 此注解值决定了 HTTP 健康检查所用的 URL 的路径部分。

service.beta.kubernetes.io/aws-load-balancer-healthcheck-port (beta)

示例:service.beta.kubernetes.io/aws-load-balancer-healthcheck-port: "24"

用于:Service

与 AWS 弹性负载均衡集成的云控制器管理器会根据此注解配置负载均衡器。 此注解值决定了负载均衡器执行健康检查时连接到哪个端口。

service.beta.kubernetes.io/aws-load-balancer-healthcheck-protocol (beta)

示例:service.beta.kubernetes.io/aws-load-balancer-healthcheck-protocol: TCP

用于:Service

与 AWS 弹性负载均衡集成的云控制器管理器会根据此注解配置负载均衡器。 此注解值决定了负载均衡器如何检查后端目标的健康。

service.beta.kubernetes.io/aws-load-balancer-healthcheck-timeout (beta)

示例:service.beta.kubernetes.io/aws-load-balancer-healthcheck-timeout: "3"

用于:Service

与 AWS 弹性负载均衡集成的云控制器管理器会根据此注解配置负载均衡器。 此注解值指定探测还未成功之前将自动视为已失败的秒数。

service.beta.kubernetes.io/aws-load-balancer-healthcheck-unhealthy-threshold (beta)

示例:service.beta.kubernetes.io/aws-load-balancer-healthcheck-unhealthy-threshold: "3"

用于:Service

与 AWS 弹性负载均衡集成的云控制器管理器会根据此注解配置负载均衡器。 此注解值指定后端需要连续多少次失败的健康检查才被视为流量不健康。

service.beta.kubernetes.io/aws-load-balancer-internal (beta)

示例:service.beta.kubernetes.io/aws-load-balancer-internal: "true"

用于:Service

与 AWS 弹性负载均衡集成的云控制器管理器会根据此注解配置负载均衡器。 当你将此注解设置为 "true" 时,此集成机制将配置一个内部负载均衡器。

如果你使用 AWS 负载均衡器控制器, 参见 service.beta.kubernetes.io/aws-load-balancer-scheme

service.beta.kubernetes.io/aws-load-balancer-manage-backend-security-group-rules (beta)

示例:service.beta.kubernetes.io/aws-load-balancer-manage-backend-security-group-rules: "true"

用于:Service

AWS 负载均衡器控制器使用此注解。 参见 AWS 负载均衡器控制器文档中的注解

service.beta.kubernetes.io/aws-load-balancer-name (beta)

示例:service.beta.kubernetes.io/aws-load-balancer-name: my-elb

用于:Service

如果你对 Service 上设置了这个注解,并且还使用 service.beta.kubernetes.io/aws-load-balancer-type: "external" 为该 Service 添加了注解,并在集群中使用了 AWS 负载均衡器控制器,那么 AWS 负载均衡器控制器将该负载均衡器的名称设置为针对这个注解设置的值。

参见 AWS 负载均衡器控制器文档中的注解

service.beta.kubernetes.io/aws-load-balancer-nlb-target-type (beta)

示例:service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: "true"

用于:Service

AWS 负载均衡器控制器使用此注解。 参见 AWS 负载均衡器控制器文档中的注解

service.beta.kubernetes.io/aws-load-balancer-private-ipv4-addresses (beta)

示例:service.beta.kubernetes.io/aws-load-balancer-private-ipv4-addresses: "198.51.100.0,198.51.100.64"

用于:Service

AWS 负载均衡器控制器使用此注解。 参见 AWS 负载均衡器控制器文档中的注解

service.beta.kubernetes.io/aws-load-balancer-proxy-protocol (beta)

示例:service.beta.kubernetes.io/aws-load-balancer-proxy-protocol: "*"

用于:Service

官方的 Kubernetes 与 AWS 弹性负载均衡集成会根据此注解配置负载均衡器。 唯一允许的值是 "*",表示负载均衡器应该使用 PROXY 协议将 TCP 连接封装到后端 Pod 中。

service.beta.kubernetes.io/aws-load-balancer-scheme (beta)

示例:service.beta.kubernetes.io/aws-load-balancer-scheme: internal

用于:Service

AWS 负载均衡器控制器使用此注解。 参见 AWS 负载均衡器控制器文档中的注解

service.beta.kubernetes.io/aws-load-balancer-security-groups(已弃用)

例子:service.beta.kubernetes.io/aws-load-balancer-security-groups: "sg-53fae93f,sg-8725gr62r"

用于:Service

AWS 负载均衡器控制器使用此注解来指定要附加到 AWS 负载均衡器的安全组的逗号分隔列表。 安全名称和 ID 均被支持,其中名称匹配 Name 标记,而不是 groupName 属性。

当将此注解添加到 Service 时,负载均衡器控制器会将注解引用的安全组附加到负载均衡器上。 如果你省略了此注解,AWS 负载均衡器控制器会自动创建一个新的安全组并将其附加到负载均衡器上。

service.beta.kubernetes.io/load-balancer-source-ranges (deprecated)

示例:service.beta.kubernetes.io/load-balancer-source-ranges: "192.0.2.0/25"

用于:Service

AWS 负载均衡器控制器使用此注解。 你应该为 Service 设置 .spec.loadBalancerSourceRanges 作为替代方案。

service.beta.kubernetes.io/aws-load-balancer-ssl-cert (beta)

示例:service.beta.kubernetes.io/aws-load-balancer-ssl-cert: "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"

用于:Service

官方与 AWS 弹性负载均衡的集成会根据此注解为 type: LoadBalancer 的服务配置 TLS。 该注解的值是负载均衡器的监听器应该使用的 X.509 证书的 AWS 资源名称(ARN)。

(TLS 协议基于一种更老的、简称为 SSL 的技术)。

service.beta.kubernetes.io/aws-load-balancer-ssl-negotiation-policy (beta)

示例:service.beta.kubernetes.io/aws-load-balancer-ssl-negotiation-policy: ELBSecurityPolicy-TLS-1-2-2017-01

官方与 AWS 弹性负载均衡的集成会根据此注解为 type: LoadBalancer 的服务配置 TLS。 该注解的值是与客户端对等方进行 TLS 协商的 AWS 策略的名称。

service.beta.kubernetes.io/aws-load-balancer-ssl-ports (beta)

示例:service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "*"

官方与 AWS 弹性负载均衡的集成会根据此注解为 type: LoadBalancer 的服务配置 TLS。 此注解的值可以是 "*"(这意味着所有负载均衡器的端口应使用 TLS)或逗号分隔的端口号列表。

service.beta.kubernetes.io/aws-load-balancer-subnets (beta)

示例:service.beta.kubernetes.io/aws-load-balancer-subnets: "private-a,private-b"

Kubernetes 官方与 AWS 的集成使用此注解来配置负载均衡器,并决定在哪些 AWS 可用区部署托管的负载均衡服务。 该值可以是逗号分隔的子网名称列表或逗号分隔的子网 ID 列表。

service.beta.kubernetes.io/aws-load-balancer-target-group-attributes (beta)

示例:service.beta.kubernetes.io/aws-load-balancer-target-group-attributes: "stickiness.enabled=true,stickiness.type=source_ip"

用于:Service

AWS 负载均衡器控制器使用此注解。 参见 AWS 负载均衡器控制器文档中的注解

service.beta.kubernetes.io/aws-load-balancer-target-node-labels (beta)

示例:service.beta.kubernetes.io/aws-load-balancer-target-node-labels: "kubernetes.io/os=Linux,topology.kubernetes.io/region=us-east-2"

Kubernetes 官方与 AWS 的集成使用此注解来确定集群中的哪些节点应被视为负载均衡器的有效目标。

service.beta.kubernetes.io/aws-load-balancer-type (beta)

示例:service.beta.kubernetes.io/aws-load-balancer-type: external

Kubernetes 官方与 AWS 的集成使用此注解来决定 AWS 云提供商是否应管理 type: LoadBalancer 的服务。

有两个允许的值:

nlb
云控制器管理器配置 Network Load Balancer
external
云控制器管理器不配置任何负载均衡器

如果你在 AWS 上部署 type: LoadBalancer 的服务,并且没有设置任何 service.beta.kubernetes.io/aws-load-balancer-type 注解,AWS 集成将部署经典的弹性负载均衡器。 这种行为是不带注解的默认行为,除非你另有指定。

当你针对 type: LoadBalancer 的服务将此注解设置为 external, 并且你的集群中已经成功部署了 AWS 负载均衡器控制器时, AWS 负载均衡器控制器将尝试根据服务规约部署一个负载均衡器。

pod-security.kubernetes.io/enforce

类别:标签

例子:pod-security.kubernetes.io/enforce: "baseline"

用于:Namespace

必须privilegedbaselinerestricted 之一,它们对应于 Pod 安全标准 级别。 特别地,enforce 标签 禁止 在带标签的 Namespace 中创建任何不符合指示级别要求的 Pod。

请请参阅在名字空间级别实施 Pod 安全性了解更多信息。

pod-security.kubernetes.io/enforce-version

类别:标签

例子:pod-security.kubernetes.io/enforce-version: "1.30"

用于:Namespace

必须latest 或格式为 v<MAJOR>.<MINOR> 的有效 Kubernetes 版本。 此注解决定了在验证提交的 Pod 时要应用的 Pod 安全标准策略的版本。

请参阅在名字空间级别实施 Pod 安全性了解更多信息。

pod-security.kubernetes.io/audit

类别:标签

例子:pod-security.kubernetes.io/audit: "baseline"

用于:Namespace

必须是与 Pod 安全标准 级别相对应的 privilegedbaselinerestricted 之一。 具体来说,audit 标签不会阻止在带标签的 Namespace 中创建不符合指示级别要求的 Pod, 但会向该 Pod 添加审计注解。

请参阅在名字空间级别实施 Pod 安全性了解更多信息。

pod-security.kubernetes.io/audit-version

类别:标签

例子:pod-security.kubernetes.io/audit-version: "1.30"

用于:Namespace

必须latest 或格式为 v<MAJOR>.<MINOR> 的有效 Kubernetes 版本。 此注解决定了在验证提交的 Pod 时要应用的 Pod 安全标准策略的版本。

请参阅在名字空间级别实施 Pod 安全性了解更多信息。

pod-security.kubernetes.io/warn

类别:标签

例子:pod-security.kubernetes.io/warn: "baseline"

用于:Namespace

必须是与 Pod 安全标准级别相对应的 privilegedbaselinerestricted 之一。特别地, warn 标签不会阻止在带标签的 Namespace 中创建不符合指示级别概述要求的 Pod,但会在这样做后向用户返回警告。 请注意,在创建或更新包含 Pod 模板的对象时也会显示警告,例如 Deployment、Jobs、StatefulSets 等。

请参阅在名字空间级别实施 Pod 安全性了解更多信息。

pod-security.kubernetes.io/warn-version

类别:标签

例子:pod-security.kubernetes.io/warn-version: "1.30"

用于:Namespace

必须latest 或格式为 v<MAJOR>.<MINOR> 的有效 Kubernetes 版本。 此注解决定了在验证提交的 Pod 时要应用的 Pod 安全标准策略的版本。 请注意,在创建或更新包含 Pod 模板的对象时也会显示警告, 例如 Deployment、Job、StatefulSet 等。

请参阅在名字空间级别实施 Pod 安全性了解更多信息。

rbac.authorization.kubernetes.io/autoupdate

类别:注解

例子:rbac.authorization.kubernetes.io/autoupdate: "false"

用于:ClusterRole、ClusterRoleBinding、Role、RoleBinding

当在 kube-apiserver 创建的默认 RBAC 对象上将此注解设置为 "true" 时, 这些对象会在服务器启动时自动更新以添加缺少的权限和主体(额外的权限和主体留在原处)。 要防止自动更新特定的 Role 或 RoleBinding,请将此注解设置为 "false"。 如果你创建自己的 RBAC 对象并将此注解设置为 "false",则 kubectl auth reconcile (允许协调在清单中给出的任意 RBAC 对象) 尊重此注解并且不会自动添加缺少的权限和主体。

kubernetes.io/psp(已弃用)

类别:注解

例如:kubernetes.io/psp: restricted

用于:Pod

这个注解只在你使用 PodSecurityPolicies 时才有意义。 Kubernetes v1.30 不支持 PodSecurityPolicy API。

当 PodSecurityPolicy 准入控制器接受一个 Pod 时,会修改该 Pod,并给这个 Pod 添加此注解。 注解的值是用来对 Pod 进行验证检查的 PodSecurityPolicy 的名称。

seccomp.security.alpha.kubernetes.io/pod (非功能性)

类别:注解

用于:Pod

v1.25 之前的 Kubernetes 允许你使用此注解配置 seccomp 行为。 请参考 使用 seccomp 限制容器的系统调用, 了解为 Pod 指定 seccomp 限制的受支持方法。

container.seccomp.security.alpha.kubernetes.io/[NAME] (非功能性)

类别:注解

用于:Pod

v1.25 之前的 Kubernetes 允许你使用此注解配置 seccomp 行为。 请参考 使用 seccomp 限制容器的系统调用 了解为 Pod 指定 seccomp 限制的受支持方法。

snapshot.storage.kubernetes.io/allow-volume-mode-change

类别:注解

例子:snapshot.storage.kubernetes.io/allow-volume-mode-change: "true"

用于:VolumeSnapshotContent

值可以是 true 或者 false。取值决定了当从 VolumeSnapshot 创建 PersistentVolumeClaim 时, 用户是否可以修改源卷的模式。

更多信息请参阅转换快照的卷模式Kubernetes CSI 开发者文档

scheduler.alpha.kubernetes.io/critical-pod(已弃用)

类别:注解

例子:scheduler.alpha.kubernetes.io/critical-pod: ""

用于:Pod

此注解让 Kubernetes 控制平面知晓某个 Pod 是一个关键的 Pod,这样 descheduler 将不会移除该 Pod。

用于审计的注解

审计注解页面上查看更多详细信息。

kubeadm

kubeadm.alpha.kubernetes.io/cri-socket

类别:注解

例子:kubeadm.alpha.kubernetes.io/cri-socket: unix:///run/containerd/container.sock

用于:Node

kubeadm 用来保存 init/join 时提供给 kubeadm 以后使用的 CRI 套接字信息的注解。 kubeadm 使用此信息为 Node 对象设置注解。 此注解仍然是 “alpha” 阶段,因为理论上这应该是 KubeletConfiguration 中的一个字段。

kubeadm.kubernetes.io/etcd.advertise-client-urls

类别:注解

例子:kubeadm.kubernetes.io/etcd.advertise-client-urls: https://172.17.0.18:2379

用于:Pod

kubeadm 为本地管理的 etcd Pod 设置的注解,用来跟踪 etcd 客户端应连接到的 URL 列表。 这主要用于 etcd 集群健康检查目的。

kubeadm.kubernetes.io/kube-apiserver.advertise-address.endpoint

类别:注解

例子:kubeadm.kubernetes.io/kube-apiserver.advertise-address.endpoint: https://172.17.0.18:6443

用于:Pod

kubeadm 为本地管理的 kube-apiserver Pod 设置的注解,用以跟踪该 API 服务器实例的公开宣告地址/端口端点。

kubeadm.kubernetes.io/component-config.hash

类别:注解

用于:ConfigMap

例子:kubeadm.kubernetes.io/component-config.hash: 2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae

kubeadm 为它所管理的 ConfigMaps 设置的注解,用于配置组件。它包含一个哈希(SHA-256)值, 用于确定用户是否应用了不同于特定组件的 kubeadm 默认设置的设置。

node-role.kubernetes.io/control-plane

类别:标签

用于:节点

用来指示该节点用于运行控制平面组件的标记标签。Kubeadm 工具将此标签应用于其管理的控制平面节点。 其他集群管理工具通常也会设置此污点。

你可以使用此标签来标记控制平面节点,以便更容易地将 Pod 仅安排到这些节点上,或者避免在控制平面上运行 Pod。 如果设置了此标签,EndpointSlice 控制器 在计算拓扑感知提示时将忽略该节点。

node-role.kubernetes.io/control-plane

类别:污点

示例:node-role.kubernetes.io/control-plane:NoSchedule

用于:节点

Kubeadm 应用在控制平面节点上的污点, 用来限制启动 Pod,并且只允许特定 Pod 可调度到这些节点上。

如果应用此污点,则控制平面节点只允许对其进行关键工作负载调度。可以在特定节点上使用以下命令手动删除此污染。

kubectl taint nodes <node-name> node-role.kubernetes.io/control-plane:NoSchedule-

node-role.kubernetes.io/master(已弃用)

类别:污点

用于:Node

例子:node-role.kubernetes.io/master:NoSchedule

kubeadm 先前应用在控制平面节点上的污点,仅允许在其上调度关键工作负载。 替换为 node-role.kubernetes.io/control-plane; kubeadm 不再设置或使用这个废弃的污点。

6.4.1 - 审计注解

该页面作为 kubernetes.io 名字空间的审计注解的参考。这些注解适用于 API 组 audit.k8s.io 中的 Event 对象。

pod-security.kubernetes.io/exempt

例子:pod-security.kubernetes.io/exempt: namespace

必须是对应于 Pod 安全豁免维度的 usernamespaceruntimeClass 之一。 此注解指示 PodSecurity 基于哪个维度的强制豁免执行。

pod-security.kubernetes.io/enforce-policy

例子:pod-security.kubernetes.io/enforce-policy: restricted:latest

必须是对应于 Pod 安全标准级别的 privileged:<版本>baseline:<版本>restricted:<版本>, 关联的版本必须latest 或格式为 v<MAJOR>.<MINOR> 的有效 Kubernetes 版本。 此注解通知有关在 PodSecurity 准入期间允许或拒绝 Pod 的执行级别。

有关详细信息,请参阅 Pod 安全标准

pod-security.kubernetes.io/audit-violations

例子:pod-security.kubernetes.io/audit-violations: would violate PodSecurity "restricted:latest": allowPrivilegeEscalation != false (container "example" must set securityContext.allowPrivilegeEscalation=false), ...

注解值给出审计策略违规的详细说明,它包含所违反的 Pod 安全标准级别以及 PodSecurity 执行中违反的特定策略及对应字段。

有关详细信息,请参阅 Pod 安全标准

authorization.k8s.io/decision

例子:authorization.k8s.io/decision: "forbid"

此注解在 Kubernetes 审计日志中表示请求是否获得授权。

有关详细信息,请参阅审计

authorization.k8s.io/reason

例子:authorization.k8s.io/reason: "Human-readable reason for the decision"

此注解给出了 Kubernetes 审计日志中 decision 的原因。

有关详细信息,请参阅审计

missing-san.invalid-cert.kubernetes.io/$hostname

例子:missing-san.invalid-cert.kubernetes.io/example-svc.example-namespace.svc: "relies on a legacy Common Name field instead of the SAN extension for subject validation"

由 Kubernetes v1.24 及更高版本使用

此注解表示 webhook 或聚合 API 服务器正在使用缺少 subjectAltNames 的无效证书。 Kubernetes 1.19 已经默认禁用,且 Kubernetes 1.23 已经移除对这些证书的支持。

使用这些证书向端点发出的请求将失败。 使用这些证书的服务应尽快替换它们,以避免在 Kubernetes 1.23+ 环境中运行时中断。

Go 文档中有更多关于此的信息: X.509 CommonName 弃用

insecure-sha1.invalid-cert.kubernetes.io/$hostname

例子:insecure-sha1.invalid-cert.kubernetes.io/example-svc.example-namespace.svc: "uses an insecure SHA-1 signature"

由 Kubernetes v1.24 及更高版本使用

此注解表示 webhook 或聚合 API 服务器所使用的是使用 SHA-1 签名的不安全证书。 Kubernetes 1.24 已经默认禁用,并将在未来的版本中删除对这些证书的支持。

使用这些证书的服务应尽快替换它们,以确保正确保护连接并避免在未来版本中出现中断。

Go 文档中有更多关于此的信息: 拒绝 SHA-1 证书

validation.policy.admission.k8s.io/validation_failure

例子:validation.policy.admission.k8s.io/validation_failure: '[{"message": "Invalid value", {"policy": "policy.example.com", {"binding": "policybinding.example.com", {"expressionIndex": "1", {"validationActions": ["Audit"]}]'

由 Kubernetes v1.27 及更高版本使用。

此注解表示 API 请求的准入策略验证评估为 false, 或者当策略配置为 failurePolicy: Fail 时验证报错。

注解的值是一个 JSON 对象。JSON 中的 message 字段提供了有关验证失败的信息。

JSON 中的 policybindingexpressionIndex 分别标识了 ValidatingAdmissionPolicy 的名称、 ValidatingAdmissionPolicyBinding 的名称以及失败的 CEL 表达式在策略 validations 中的索引。

validationActions 显示针对此验证失败采取的操作。 有关 validationActions 的更多详细信息, 请参阅验证准入策略

6.5 - Kubernetes API

Kubernetes API 是通过 RESTful 接口提供 Kubernetes 功能服务并负责集群状态存储的应用程序。

Kubernetes 资源和"意向记录"都是作为 API 对象储存的,并可以通过调用 RESTful 风格的 API 进行修改。 API 允许以声明方式管理配置。 用户可以直接和 Kubernetes API 交互,也可以通过 kubectl 这样的工具进行交互。 核心的 Kubernetes API 是很灵活的,可以扩展以支持定制资源。

6.5.1 - 工作负载资源

6.5.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 的组。matchLabelKeys 和 labelSelector 中禁止存在相同的键。未设置 labelSelector 时无法设置 matchLabelKeys。 新来的 Pod 标签中不存在的键将被忽略。null 或空的列表意味着仅与 labelSelector 匹配。

    这是一个 Beta 字段,需要启用 MatchLabelKeysInPodTopologySpread 特性门控(默认启用)。

    • 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 特性标志默认启用的 Beta 级别特性。

    • topologySpreadConstraints.nodeTaintsPolicy (string)

    nodeTaintsPolicy 表示我们在计算 Pod 拓扑分布偏差时将如何处理节点污点。选项为:

    • Honor:包括不带污点的节点以及新来 Pod 具有容忍度且带有污点的节点。
    • Ignore:节点污点被忽略。包括所有节点。

    如果此值为 nil,此行为等同于 Ignore 策略。 这是通过 NodeInclusionPolicyInPodTopologySpread 特性标志默认启用的 Beta 级别特性。

  • 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、fsGroup(如果指定了的话) 和在容器镜像中为容器进程的 uid 定义的组成员身份,以及这里所给的列表。

      如果未指定,则不会向任何容器添加其他组。 注意,在容器镜像中为容器进程的 uid 定义的组成员身份仍然有效, 即使它们未包含在此列表中也是如此。 注意,当 spec.os.namewindows 时,不能设置此字段。

    • 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 确定容器是否应作为"主机进程"容器运行。 一个 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 特性的服务器才能使用此字段。

  • resourceClaims ([]PodResourceClaim)

    补丁策略:retainKeys,基于键 name 合并

    映射:键 name 的唯一值将在合并过程中保留

resourceClaims 定义了在允许 Pod 启动之前必须分配和保留哪些 ResourceClaims。 这些资源将可供那些按名称使用它们的容器使用。

这是一个 Alpha 特性的字段,需要启用 DynamicResourceAllocation 特性门控来开启此功能。

此字段不可变更。

PodResourceClaim 通过 ClaimSource 引用一个 ResourceClaim。 它为 ClaimSource 添加一个名称,作为 Pod 内 ResourceClaim 的唯一标识。 需要访问 ResourceClaim 的容器可使用此名称引用它。

  • resourceClaims.name (string), 必需

    在 Pod 中,name 是此资源声明的唯一标识。此字段值必须是 DNS_LABEL。

  • resourceClaims.source (ClaimSource)

    source 描述了在哪里可以找到 resourceClaim

    ClaimSource 描述对 ResourceClaim 的引用。

    应该设置且仅设置如下字段之一。此类型的消费者必须将空对象视为具有未知值。

    • resourceClaims.source.resourceClaimName (string)

      resourceClaimName 是与此 Pod 位于同一命名空间中的 ResourceClaim 对象的名称。

    • resourceClaims.source.resourceClaimTemplateName (string)

      resourceClaimTemplateName 是与此 Pod 位于同一命名空间中的 ResourceClaimTemplate 对象的名称。

      该模板将用于创建一个新的 ResourceClaim,新的 ResourceClaim 将被绑定到此 Pod。 删除此 Pod 时,ResourceClaim 也将被删除。 Pod 名称和资源名称,连同生成的组件,将用于为 ResourceClaim 形成唯一名称, 该名称将记录在 pod.status.resourceClaimStatuses 中。

      不属于此 Pod 但与此名称重名的现有 ResourceClaim 不会被用于此 Pod, 以避免错误地使用不相关的资源。Pod 的调度和启动动作会因此而被阻塞, 直到不相关的 ResourceClaim 被删除。

      此字段是不可变更的,创建 ResourceClaim 后控制平面不会对相应的 ResourceClaim 进行任何更改。

  • schedulingGates ([]PodSchedulingGate)

    补丁策略:基于 name 键合并

    映射:键 name 的唯一值将在合并过程中保留

    schedulingGates 是一个不透明的值列表,如果指定,将阻止调度 Pod。 如果 schedulingGates 不为空,则 Pod 将保持 SchedulingGated 状态,调度程序将不会尝试调度 Pod。

    SchedulingGates 只能在 Pod 创建时设置,并且只能在创建之后删除。

    此特性为 Beta 特性,需要通过 PodSchedulingReadiness 特性门控启用。

    PodSchedulingGate 与 Pod 相关联以保护其调度。

    • schedulingGates.name (string),必需

      调度门控的名称,每个调度门控的 name 字段取值必须唯一。

已弃用

  • 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 (string)

      卷中的路径,容器中的卷应该这一路径安装。默认为 ""(卷的根)。

    • volumeMounts.subPathExpr (string)

      应安装容器卷的卷内的扩展路径。行为类似于 subPath,但环境变量引用 $(VAR_NAME) 使用容器的环境进行扩展。默认为 ""(卷的根)。subPathExprsubPath 是互斥的。

  • volumeDevices ([]VolumeDevice)

    补丁策略:基于 devicePath 键合并

    volumeDevices 是容器要使用的块设备列表。

    volumeDevice 描述了容器内原始块设备的映射。

    • volumeDevices.devicePath (string),必需

      devicePath 是设备将被映射到的容器内的路径。

    • volumeDevices.name (string),必需

      name 必须与 Pod 中的 persistentVolumeClaim 的名称匹配

资源

  • resources(ResourceRequirements)

    此容器所需的计算资源。无法更新。更多信息: https://kubernetes.io/zh-cn/docs/concepts/configuration/manage-resources-containers/

    ResourceRequirements 描述计算资源需求。

    • resources.claims ([]ResourceClaim)

      映射:键 name 的唯一值将在合并过程中保留

      claims 列出此容器使用的资源名称,资源名称在 spec.resourceClaims 中定义。

      这是一个 Alpha 特性字段,需要启用 DynamicResourceAllocation 功能门控开启此特性。

      此字段不可变更,只能在容器级别设置。

      ResourceClaim 引用 PodSpec.resourceClaims 中的一项。

      • resources.claims.name (string),必需

        name 必须与使用该字段 Pod 的 pod.spec.resourceClaims 中的一个条目的名称相匹配。它使该资源在容器内可用。

  • resizePolicy ([]ContainerResizePolicy)

    原子性: 将在合并期间被替换

    容器的资源调整策略。

    ContainerResizePolicy 表示容器的资源大小调整策略

    • resizePolicy.resourceName (string), 必需

      该资源调整策略适用的资源名称。支持的值:cpu、memory。

    • resizePolicy.restartPolicy (string), 必需

      重启策略,会在调整指定资源大小时使用该策略。如果未指定,则默认为 NotRequired。

生命周期

  • 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

  • restartPolicy (string)

    restartPolicy 定义 Pod 中各个容器的重新启动行为。 该字段仅适用于 Init 容器,唯一允许的值是 "Always"。 对于非 Init 容器或未指定此字段的情况,重新启动行为由 Pod 的重启策略和容器类型来定义。 将 restartPolicy 设置为 "Always" 会产生以下效果:该 Init 容器将在退出后持续重新启动,直到所有常规容器终止。 一旦所有常规容器已完成,所有具有 restartPolicy 为 "Always" 的 Init 容器将被关闭。 这种生命期与正常的 Init 容器不同,通常被称为 "sidecar" 容器。 虽然此 Init 容器仍然在 Init 容器序列中启动,但它在进入下一个 Init 容器之前并不等待容器完成。 相反,在此 Init 容器被启动后或在任意 startupProbe 已成功完成后下一个 Init 容器将立即启动。

安全上下文

  • 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 确定容器是否应作为 "主机进程" 容器运行。 一个 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。

  • tty (boolean) 这个容器是否应该为自己分配一个 TTY,同时需要设置 stdin 为真。默认为 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 的名称匹配。

  • resizePolicy ([]ContainerResizePolicy)

    原子性: 将在合并期间被替换

    容器的资源调整策略。

    ContainerResizePolicy 表示容器的资源大小调整策略

    • resizePolicy.resourceName (string), 必需

      该资源调整策略适用的资源名称。支持的值:cpu、memory。

    • resizePolicy.restartPolicy (string), 必需

      重启策略,会在调整指定资源大小时使用该策略。如果未指定,则默认为 NotRequired。

生命周期

  • terminationMessagePath (string)

    可选字段。挂载到容器文件系统的路径,用于写入容器终止消息的文件。 写入的消息旨在成为简短的最终状态,例如断言失败消息。如果超出 4096 字节,将被节点截断。 所有容器的总消息长度将限制为 12 KB。默认为 /dev/termination-log。无法更新。

  • terminationMessagePolicy (string)

    指示应如何填充终止消息。字段值为 File 表示将使用 terminateMessagePath 的内容来填充成功和失败的容器状态消息。 如果终止消息文件为空并且容器因错误退出,字段值 FallbackToLogsOnError 表示将使用容器日志输出的最后一块。日志输出限制为 2048 字节或 80 行,以较小者为准。 默认为 File。无法更新。

  • restartPolicy (string)

    这是针对容器的重启策略,用于管理 Pod 内每个容器的重启行为。 此字段仅适用于 Init 容器,在临时容器上无法设置此字段。

调试

  • 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 确定容器是否应作为 "主机进程" 容器运行。 一个 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

  • resources (ResourceRequirements)

    临时容器不允许使用资源。临时容器使用已分配给 Pod 的空闲资源。

    ResourceRequirements 描述计算资源的需求。

    • resources.claims ([]ResourceClaim)

      映射:键 name 的唯一值将在合并过程中保留

      claims 列出了此容器使用的资源名称,资源名称在 spec.resourceClaims 中定义。

      这是一个 Alpha 特性字段,需要启用 DynamicResourceAllocation 功能门控开启此特性。

      此字段不可变更,只能在容器级别设置。

      ResourceClaim 引用 PodSpec.ResourceClaims 中的一项。

      • resources.claims.name (string),必需

        name 必须与使用该字段 Pod 的 pod.spec.resourceClaims 中的一个条目的名称相匹配。它使该资源在容器内可用。

  • 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 端口的操作。

    • 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)

    hostIP 存储分配给 Pod 的主机的 IP 地址。如果 Pod 尚未启动,则为空。 Pod 可以被调度到 kubelet 有问题的节点上,这意味着即使有节点被分配给 Pod,hostIP 也不会被更新。

  • hostIPs ([]HostIP)

    补丁策略:基于 ip 键合并

    原子性:将在合并期间被替换

    hostIPs 存储分配给主机的 IP 地址列表。如果此字段被指定,则第一个条目必须与 hostIP 字段匹配。 如果 Pod 尚未启动,则此列表为空。Pod 可以被调度到 kubelet 有问题的节点上, 这意味着即使有节点被分配给此 Pod,HostIPs 也不会被更新。

    HostIP 表示分配给主机的单个 IP 地址。

    • hostIPs.ip (string)

      ip 是分配给主机的 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 的 podIP 地址。至少在集群内可路由。如果尚未分配则为空。

  • podIPs ([]PodIP)

    补丁策略:基于 ip 键合并

    podIPs 保存分配给 Pod 的 IP 地址。如果指定了该字段,则第 0 个条目必须与 podIP 字段值匹配。 Pod 最多可以为 IPv4 和 IPv6 各分配 1 个值。如果尚未分配 IP,则此列表为空。

    podIP 表示分配给 Pod 的单个 IP 地址。

    • podIP.ip (string)

      ip 是分配给 Pod 的 IP 地址。

  • ephemeralContainerStatuses ([]ContainerStatus)

    已在此 Pod 中运行的任何临时容器的状态。

    ContainerStatus 包含此容器当前状态的详细信息。

  • resourceClaimStatuses ([]PodResourceClaimStatus)

    补丁策略:retainKeys,基于键 name 合并

    映射:键 name 的唯一值将在合并过程中保留

    资源申领的状态。

    对于每个引用 ResourceClaimTemplate 的 PodResourceClaim,PodResourceClaimStatus 被存储在 PodStatus 中。它存储为对应 ResourceClaim 生成的名称。

    • resourceClaimStatuses.name (string), required

      Name 在 Pod 中唯一地标识此资源申领。 此名称必须与 pod.spec.resourceClaims 中的条目名称匹配,这意味着字符串必须是 DNS_LABEL。

    • resourceClaimStatuses.resourceClaimName (string)

      resourceClaimName 是为 Pod 在其名字空间中生成的 ResourceClaim 的名称。 如果此项未被设置,则不需要生成 ResourceClaim。在这种情况下,可以忽略 pod.spec.resourceClaims 这个条目。

  • resize (string)

    Pod 容器所需的资源大小调整状态。如果没有待处理的资源调整大小,则它为空。 对容器资源的任何更改都会自动将其设置为"建议"值。

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,必需

响应

200 (Pod): OK

201 (Pod): Created

401: Unauthorized

update 替换指定 Pod 的 ephemeralcontainers

HTTP 请求

PUT /api/v1/namespaces/{namespace}/pods/{name}/ephemeralcontainers

参数

  • name (路径参数): string,必需

    Pod 的名称

  • namespace (路径参数): string,必需

    namespace

  • bodyPod,必需

响应

200 (Pod): OK

201 (Pod): Created

401: Unauthorized

update 替换指定 Pod 的状态

HTTP 请求

PUT /api/v1/namespaces/{namespace}/pods/{name}/status

参数

  • name (路径参数): string,必需

    Pod 的名称

  • namespace (路径参数): string,必需

    namespace

  • bodyPod,必需

响应

200 (Pod): OK

201 (Pod): Created

401: Unauthorized

patch 部分更新指定 Pod

HTTP 请求

PATCH /api/v1/namespaces/{namespace}/pods/{name}

参数

  • name (路径参数): string,必需

    Pod 的名称

  • namespace (路径参数): string,必需

    namespace

  • bodyPatch,必需

响应

200 (Pod): OK

201 (Pod): Created

401: Unauthorized

patch 部分更新指定 Pod 的 ephemeralcontainers

HTTP 请求

PATCH /api/v1/namespaces/{namespace}/pods/{name}/ephemeralcontainers

参数

  • name (路径参数): string,必需

    Pod 的名称。

  • namespace (路径参数): string,必需

    namespace

  • bodyPatch,必需

响应

200 (Pod): OK

201 (Pod): Created

401: Unauthorized

patch 部分更新指定 Pod 的状态

HTTP 请求

PATCH /api/v1/namespaces/{namespace}/pods/{name}/status

参数

  • name (路径参数): string,必需

    Pod 的名称。

  • namespace (路径参数): string,必需

    namespace

  • bodyPatch,必需

响应

200 (Pod): OK

201 (Pod): Created

401: Unauthorized

delete 删除一个 Pod

HTTP 请求

DELETE /api/v1/namespaces/{namespace}/pods/{name}

参数

  • name (路径参数): string,必需

    Pod 的名称。

  • namespace (路径参数): string,必需

    namespace

  • bodyDeleteOptions

响应

200 (Pod): OK

202 (Pod): Accepted

401: Unauthorize

deletecollection 删除 Pod 的集合

HTTP 请求

DELETE /api/v1/namespaces/{namespace}/pods

参数

  • continue (查询参数): string

    continue

  • dryRun (查询参数): string

    dryRun

响应

200 (Status): OK

401: Unauthorized

6.5.1.2 - PodTemplate

PodTemplate 描述一种模板,用来为预定义的 Pod 生成副本。

apiVersion: v1

import "k8s.io/api/core/v1"

PodTemplate

PodTemplate 描述一种模板,用来为预定义的 Pod 生成副本。


PodTemplateSpec

PodTemplateSpec 描述基于某模板所创建的 Pod 所应具有的数据。


PodTemplateList

PodTemplateList 是 PodTemplate 对象的列表。


  • items ([]PodTemplate),必需

    PodTemplate 对象列表。

操作


get 读取指定的 PodTemplate

HTTP 请求

GET /api/v1/namespaces/{namespace}/podtemplates/{name}

参数

  • name路径参数):string,必需

    PodTemplate 的名称

  • namespace路径参数):string,必需

    namespace

  • pretty查询参数):string

    pretty

响应

200 (PodTemplate): OK

401: Unauthorized

list 列出或监视 PodTemplate 类型的对象

HTTP 请求

GET /api/v1/namespaces/{namespace}/podtemplates

参数

  • namespace路径参数):string,必需

    namespace

  • continue查询参数):string

    continue

  • limit查询参数):integer

    limit

  • pretty查询参数):string

    pretty

  • watch查询参数):boolean

    watch

响应

200 (PodTemplateList): OK

401: Unauthorized

list 列出或监视 PodTemplate 类型的对象

HTTP 请求

GET /api/v1/podtemplates

参数

  • continue查询参数):string

    continue

  • limit查询参数):integer

    limit

  • pretty查询参数):string

    pretty

  • watch查询参数):boolean

    watch

响应

200 (PodTemplateList): OK

401: Unauthorized

create 创建一个 PodTemplate

HTTP 请求

POST /api/v1/namespaces/{namespace}/podtemplates

参数

  • namespace路径参数):string,必需

    namespace

  • dryRun查询参数):string

    dryRun

  • pretty查询参数):string

    pretty

响应

200 (PodTemplate): OK

201 (PodTemplate): Created

202 (PodTemplate): Accepted

401: Unauthorized

update 替换指定的 PodTemplate

HTTP 请求

PUT /api/v1/namespaces/{namespace}/podtemplates/{name}

参数

  • name路径参数):string,必需

    PodTemplate 的名称

  • namespace路径参数):string,必需

    namespace

  • dryRun查询参数):string

    dryRun

  • pretty查询参数):string

    pretty

响应

200 (PodTemplate): OK

201 (PodTemplate): Created

401: Unauthorized

patch 部分更新指定的 PodTemplate

HTTP 请求

PATCH /api/v1/namespaces/{namespace}/podtemplates/{name}

参数

  • name路径参数):string,必需

    PodTemplate 的名称

  • namespace路径参数):string,必需

    namespace

  • dryRun查询参数):string

    dryRun

  • force查询参数):boolean

    force

  • pretty查询参数):string

    pretty

响应

200 (PodTemplate): OK

201 (PodTemplate): Created

401: Unauthorized

delete 删除一个 PodTemplate

HTTP 请求

DELETE /api/v1/namespaces/{namespace}/podtemplates/{name}

参数

  • name路径参数):string,必需

    PodTemplate 的名称

  • dryRun查询参数):string

    dryRun

  • pretty查询参数):string

    pretty

响应

200 (PodTemplate): OK

202 (PodTemplate): Accepted

401: Unauthorized

deletecollection 删除 PodTemplate 的集合

HTTP 请求

DELETE /api/v1/namespaces/{namespace}/podtemplates

参数

  • continue查询参数):string

    continue

  • dryRun查询参数):string

    dryRun

  • limit查询参数):integer

    limit

  • pretty查询参数):string

    pretty

响应

200 (Status): OK

401: Unauthorized

6.5.1.3 - ReplicationController

ReplicationController 表示一个副本控制器的配置。

apiVersion: v1

import "k8s.io/api/core/v1"

ReplicationController

ReplicationController 表示一个副本控制器的配置。


  • apiVersion: v1

  • kind: ReplicationController

ReplicationControllerSpec

ReplicationControllerSpec 表示一个副本控制器的规约。


ReplicationControllerStatus

ReplicationControllerStatus 表示一个副本控制器的当前状态。


  • readyReplicas (int32)

    此副本控制器所用的就绪副本的数量。

  • fullyLabeledReplicas (int32)

    标签与副本控制器的 Pod 模板标签匹配的 Pod 数量。

  • conditions ([]ReplicationControllerCondition)

    补丁策略:按照键 type 合并

    表示副本控制器当前状态的最新可用观测值。

    ReplicationControllerCondition 描述某个点的副本控制器的状态。

    • conditions.status (string),必需

      状况的状态,取值为 True、False 或 Unknown 之一。

    • conditions.type (string),必需

      副本控制器状况的类型。

    • conditions.lastTransitionTime (Time)

      状况上次从一个状态转换为另一个状态的时间。

      Time 是对 time.Time 的封装。Time 支持对 YAML 和 JSON 进行正确封包。 为 time 包的许多函数方法提供了封装器。

    • conditions.message (string)

    这是一条人类可读的消息,指示有关上次转换的详细信息。

    • conditions.reason (string)

      状况上次转换的原因。

  • observedGeneration (int64)

    observedGeneration 反映了最近观测到的副本控制器的生成情况。

ReplicationControllerList

ReplicationControllerList 是副本控制器的集合。


  • apiVersion: v1

  • kind: ReplicationControllerList

操作


get 读取指定的 ReplicationController

HTTP 请求

GET /api/v1/namespaces/{namespace}/replicationcontrollers/{name}

参数

  • name (路径参数): string,必需

    ReplicationController 的名称

  • namespace (路径参数): string,必需

    namespace

  • pretty (查询参数): string

    pretty

响应

200 (ReplicationController): OK

401: Unauthorized

get 读取指定的 ReplicationController 的状态

HTTP 请求

GET /api/v1/namespaces/{namespace}/replicationcontrollers/{name}/status

参数

  • name (路径参数): string,必需

    ReplicationController 的名称

  • namespace (路径参数): string,必需

    namespace

  • pretty (查询参数): string

    pretty

响应

200 (ReplicationController): OK

401: Unauthorized

list 列出或监视 ReplicationController 类别的对象

HTTP 请求

GET /api/v1/namespaces/{namespace}/replicationcontrollers

参数

响应

200 (ReplicationControllerList): OK

401: Unauthorized

list 列出或监视 ReplicationController 类别的对象

HTTP 请求

GET /api/v1/replicationcontrollers

参数

响应

200 (ReplicationControllerList): OK

401: Unauthorized

create 创建 ReplicationController

HTTP 请求

POST /api/v1/namespaces/{namespace}/replicationcontrollers

参数

响应

200 (ReplicationController): OK

201 (ReplicationController): Created

202 (ReplicationController): Accepted

401: Unauthorized

update 替换指定的 ReplicationController

HTTP 请求

PUT /api/v1/namespaces/{namespace}/replicationcontrollers/{name}

参数

响应

200 (ReplicationController): OK

201 (ReplicationController): Created

401: Unauthorized

update 替换指定的 ReplicationController 的状态

HTTP 请求

PUT /api/v1/namespaces/{namespace}/replicationcontrollers/{name}/status

参数

响应

200 (ReplicationController): OK

201 (ReplicationController): Created

401: Unauthorized

patch 部分更新指定的 ReplicationController

HTTP 请求

PATCH /api/v1/namespaces/{namespace}/replicationcontrollers/{name}

参数

  • name (路径参数): string,必需

    ReplicationController 的名称

  • namespace (路径参数): string,必需

    namespace

  • body: Patch,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • force (查询参数): boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (ReplicationController): OK

201 (ReplicationController): Created

401: Unauthorized

patch 部分更新指定的 ReplicationController 的状态

HTTP 请求

PATCH /api/v1/namespaces/{namespace}/replicationcontrollers/{name}/status

参数

  • name (路径参数): string,必需

    ReplicationController 的名称

  • namespace (路径参数): string,必需

    namespace

  • body: Patch,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • force (查询参数): boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (ReplicationController): OK

201 (ReplicationController): Created

401: Unauthorized

delete 删除 ReplicationController

HTTP 请求

DELETE /api/v1/namespaces/{namespace}/replicationcontrollers/{name}

参数

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除 ReplicationController 的集合

HTTP 请求

DELETE /api/v1/namespaces/{namespace}/replicationcontrollers

参数

响应

200 (Status): OK

401: Unauthorized

6.5.1.4 - ReplicaSet

ReplicaSet 确保在任何给定的时刻都在运行指定数量的 Pod 副本。

apiVersion: apps/v1

import "k8s.io/api/apps/v1"

ReplicaSet

ReplicaSet 确保在任何给定的时刻都在运行指定数量的 Pod 副本。


  • apiVersion: apps/v1

  • kind: ReplicaSet

ReplicaSetSpec

ReplicaSetSpec 是 ReplicaSet 的规约。


ReplicaSetStatus

ReplicaSetStatus 表示 ReplicaSet 的当前状态。


  • readyReplicas (int32)

    readyReplicas 是此 ReplicaSet 在就绪状况下处理的目标 Pod 数量。

  • fullyLabeledReplicas (int32)

    标签与 ReplicaSet 的 Pod 模板标签匹配的 Pod 数量。

  • conditions ([]ReplicaSetCondition)

    补丁策略:按照键 type 合并

    表示副本集当前状态的最新可用观测值。

    ReplicaSetCondition 描述某个点的副本集状态。

    • conditions.status (string),必需

      状况的状态,取值为 True、False 或 Unknown 之一。

    • conditions.type (string),必需

      副本集状况的类型。

    • conditions.lastTransitionTime (Time)

      状况上次从一个状态转换为另一个状态的时间。

      Time 是对 time.Time 的封装。Time 支持对 YAML 和 JSON 进行正确封包。 为 time 包的许多函数方法提供了封装器。

    • conditions.message (string)

      这是一条人类可读的消息,指示有关上次转换的详细信息。

    • conditions.reason (string)

      状况上次转换的原因。

  • observedGeneration (int64)

    observedGeneration 反映了最近观测到的 ReplicaSet 生成情况。

ReplicaSetList

ReplicaSetList 是多个 ReplicaSet 的集合。


  • apiVersion: apps/v1

  • kind: ReplicaSetList

操作


get 读取指定的 ReplicaSet

HTTP 请求

GET /apis/apps/v1/namespaces/{namespace}/replicasets/{name}

参数

  • name (路径参数): string,必需

    ReplicaSet 的名称

  • namespace (路径参数): string,必需

    namespace

  • pretty (查询参数): string

    pretty

响应

200 (ReplicaSet): OK

401: Unauthorized

get 读取指定的 ReplicaSet 的状态

HTTP 请求

GET /apis/apps/v1/namespaces/{namespace}/replicasets/{name}/status

参数

  • name (路径参数): string,必需

    ReplicaSet 的名称

  • namespace (路径参数): string,必需

    namespace

  • pretty (查询参数): string

    pretty

响应

200 (ReplicaSet): OK

401: Unauthorized

list 列出或监视 ReplicaSet 类别的对象

HTTP 请求

GET /apis/apps/v1/namespaces/{namespace}/replicasets

参数

响应

200 (ReplicaSetList): OK

401: Unauthorized

list 列出或监视 ReplicaSet 类别的对象

HTTP 请求

GET /apis/apps/v1/replicasets

参数

响应

200 (ReplicaSetList): OK

401: Unauthorized

create 创建 ReplicaSet

HTTP 请求

POST /apis/apps/v1/namespaces/{namespace}/replicasets

参数

响应

200 (ReplicaSet): OK

201 (ReplicaSet): Created

202 (ReplicaSet): Accepted

401: Unauthorized

update 替换指定的 ReplicaSet

HTTP 请求

PUT /apis/apps/v1/namespaces/{namespace}/replicasets/{name}

参数

  • name (路径参数): string,必需

    ReplicaSet 的名称

  • namespace (路径参数): string,必需

    namespace

  • body: ReplicaSet,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • pretty (查询参数): string

    pretty

响应

200 (ReplicaSet): OK

201 (ReplicaSet): Created

401: Unauthorized

update 替换指定的 ReplicaSet 的状态

HTTP 请求

PUT /apis/apps/v1/namespaces/{namespace}/replicasets/{name}/status

参数

  • name (路径参数): string,必需

    ReplicaSet 的名称

  • namespace (路径参数): string,必需

    namespace

  • body: ReplicaSet,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • pretty (查询参数): string

    pretty

响应

200 (ReplicaSet): OK

201 (ReplicaSet): Created

401: Unauthorized

patch 部分更新指定的 ReplicaSet

HTTP 请求

PATCH /apis/apps/v1/namespaces/{namespace}/replicasets/{name}

参数

  • name (路径参数): string,必需

    ReplicaSet 的名称

  • namespace (路径参数): string,必需

    namespace

  • body: Patch,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • force (查询参数): boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (ReplicaSet): OK

201 (ReplicaSet): Created

401: Unauthorized

patch 部分更新指定的 ReplicaSet 的状态

HTTP 请求

PATCH /apis/apps/v1/namespaces/{namespace}/replicasets/{name}/status

参数

  • name (路径参数): string,必需

    ReplicaSet 的名称

  • namespace (路径参数): string,必需

    namespace

  • body: Patch,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • force (查询参数): boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (ReplicaSet): OK

201 (ReplicaSet): Created

401: Unauthorized

delete 删除 ReplicaSet

HTTP 请求

DELETE /apis/apps/v1/namespaces/{namespace}/replicasets/{name}

参数

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除 ReplicaSet 的集合

HTTP 请求

DELETE /apis/apps/v1/namespaces/{namespace}/replicasets

参数

响应

200 (Status): OK

401: Unauthorized

6.5.1.5 - Deployment

Deployment 使得 Pod 和 ReplicaSet 能够进行声明式更新。

apiVersion: apps/v1

import "k8s.io/api/apps/v1"

Deployment

Deployment 使得 Pod 和 ReplicaSet 能够进行声明式更新。


  • apiVersion: apps/v1

  • kind: Deployment

DeploymentSpec

DeploymentSpec 定义 Deployment 预期行为的规约。


  • selector (LabelSelector),必需

    供 Pod 所用的标签选择算符。通过此字段选择现有 ReplicaSet 的 Pod 集合, 被选中的 ReplicaSet 将受到这个 Deployment 的影响。此字段必须与 Pod 模板的标签匹配。

  • template (PodTemplateSpec),必需

    template 描述将要创建的 Pod。template.spec.restartPolicy 唯一被允许的值是 Always

  • replicas (int32)

    预期 Pod 的数量。这是一个指针,用于辨别显式零和未指定的值。默认为 1。

  • minReadySeconds (int32)

    新建的 Pod 在没有任何容器崩溃的情况下就绪并被系统视为可用的最短秒数。 默认为 0(Pod 就绪后即被视为可用)。

  • strategy (DeploymentStrategy)

    补丁策略:retainKeys

    将现有 Pod 替换为新 Pod 时所用的部署策略。

    DeploymentStrategy 描述如何将现有 Pod 替换为新 Pod。

    • strategy.type (string)

      部署的类型。取值可以是 “Recreate” 或 “RollingUpdate”。默认为 RollingUpdate。

    • strategy.rollingUpdate (RollingUpdateDeployment)

      滚动更新这些配置参数。仅当 type = RollingUpdate 时才出现。

      控制滚动更新预期行为的规约。

      • strategy.rollingUpdate.maxSurge (IntOrString)

        超出预期的 Pod 数量之后可以调度的最大 Pod 数量。该值可以是一个绝对数(例如: 5)或一个预期 Pod 的百分比(例如:10%)。如果 MaxUnavailable 为 0,则此字段不能为 0。 通过向上取整计算得出一个百分比绝对数。默认为 25%。例如:当此值设为 30% 时, 如果滚动更新启动,则可以立即对 ReplicaSet 扩容,从而使得新旧 Pod 总数不超过预期 Pod 数量的 130%。 一旦旧 Pod 被杀死,则可以再次对新的 ReplicaSet 扩容, 确保更新期间任何时间运行的 Pod 总数最多为预期 Pod 数量的 130%。

        IntOrString 是可以保存 int32 或字符串的一个类型。 当用于 JSON 或 YAML 编组和取消编组时,它会产生或消费内部类型。 例如,这允许你拥有一个可以接受名称或数值的 JSON 字段。

      • strategy.rollingUpdate.maxUnavailable (IntOrString)

        更新期间可能不可用的最大 Pod 数量。该值可以是一个绝对数(例如: 5)或一个预期 Pod 的百分比(例如:10%)。通过向下取整计算得出一个百分比绝对数。 如果 MaxSurge 为 0,则此字段不能为 0。默认为 25%。 例如:当此字段设为 30%,则在滚动更新启动时 ReplicaSet 可以立即缩容为预期 Pod 数量的 70%。 一旦新的 Pod 就绪,ReplicaSet 可以再次缩容,接下来对新的 ReplicaSet 扩容, 确保更新期间任何时间可用的 Pod 总数至少是预期 Pod 数量的 70%。

        IntOrString 是可以保存 int32 或字符串的一个类型。 当用于 JSON 或 YAML 编组和取消编组时,它会产生或消费内部类型。 例如,这允许你拥有一个可以接受名称或数值的 JSON 字段。

  • revisionHistoryLimit (int32)

    保留允许回滚的旧 ReplicaSet 的数量。这是一个指针,用于辨别显式零和未指定的值。默认为 10。

  • progressDeadlineSeconds (int32)

    Deployment 在被视为失败之前取得进展的最大秒数。Deployment 控制器将继续处理失败的部署, 原因为 ProgressDeadlineExceeded 的状况将被显示在 Deployment 状态中。 请注意,在 Deployment 暂停期间将不会估算进度。默认为 600s。

  • paused (boolean)

    指示部署被暂停。

DeploymentStatus

DeploymentStatus 是最近观测到的 Deployment 状态。


  • replicas (int32)

    此部署所针对的(其标签与选择算符匹配)未终止 Pod 的总数。

  • availableReplicas (int32)

    此部署针对的可用(至少 minReadySeconds 才能就绪)的 Pod 总数。

  • readyReplicas (int32)

    readyReplicas 是此 Deployment 在就绪状况下处理的目标 Pod 数量。

  • unavailableReplicas (int32)

    此部署针对的不可用 Pod 总数。这是 Deployment 具有 100% 可用容量时仍然必需的 Pod 总数。 它们可能是正在运行但还不可用的 Pod,也可能是尚未创建的 Pod。

  • updatedReplicas (int32)

    此 Deployment 所针对的未终止 Pod 的总数,这些 Pod 采用了预期的模板规约。

  • collisionCount (int32)

    供 Deployment 所用的哈希冲突计数。 Deployment 控制器在需要为最新的 ReplicaSet 创建名称时将此字段用作冲突预防机制。

  • conditions ([]DeploymentCondition)

    补丁策略:按照键 type 合并

    表示 Deployment 当前状态的最新可用观测值。

    DeploymentCondition 描述某个点的部署状态。

    • conditions.status (string),必需

      状况的状态,取值为 True、False 或 Unknown 之一。

    • conditions.type (string),必需

      Deployment 状况的类型。

    • conditions.lastTransitionTime (Time)

      状况上次从一个状态转换为另一个状态的时间。

      Time 是对 time.Time 的封装。Time 支持对 YAML 和 JSON 进行正确封包。 为 time 包的许多函数方法提供了封装器。

    • conditions.lastUpdateTime (Time)

      上次更新此状况的时间。

      Time 是对 time.Time 的封装。Time 支持对 YAML 和 JSON 进行正确封包。 为 time 包的许多函数方法提供了封装器。

    • conditions.message (string)

      这是一条人类可读的消息,指示有关上次转换的详细信息。

    • conditions.reason (string)

      状况上次转换的原因。

  • observedGeneration (int64)

    Deployment 控制器观测到的代数(Generation)。

DeploymentList

DeploymentList 是 Deployment 的列表。


  • apiVersion: apps/v1

  • kind: DeploymentList

  • metadata (ListMeta)

    标准的列表元数据。

  • items ([]Deployment),必需

    items 是 Deployment 的列表。

操作


get 读取指定的 Deployment

HTTP 请求

GET /apis/apps/v1/namespaces/{namespace}/deployments/{name}

参数

  • name (路径参数): string,必需

    Deployment 的名称

  • namespace (路径参数): string,必需

    namespace

  • pretty (查询参数): string

    pretty

响应

200 (Deployment): OK

401: Unauthorized

get 读取指定的 Deployment 的状态

HTTP 请求

GET /apis/apps/v1/namespaces/{namespace}/deployments/{name}/status

参数

  • name (路径参数): string,必需

    Deployment 的名称

  • namespace (路径参数): string,必需

    namespace

  • pretty (查询参数): string

    pretty

响应

200 (Deployment): OK

401: Unauthorized

list 列出或监视 Deployment 类别的对象

HTTP 请求

GET /apis/apps/v1/namespaces/{namespace}/deployments

参数

响应

200 (DeploymentList): OK

401: Unauthorized

list 列出或监视 Deployment 类别的对象

HTTP 请求

GET /apis/apps/v1/deployments

参数

响应

200 (DeploymentList): OK

401: Unauthorized

create 创建 Deployment

HTTP 请求

POST /apis/apps/v1/namespaces/{namespace}/deployments

参数

响应

200 (Deployment): OK

201 (Deployment): Created

202 (Deployment): Accepted

401: Unauthorized

update 替换指定的 Deployment

HTTP 请求

PUT /apis/apps/v1/namespaces/{namespace}/deployments/{name}

参数

  • name (路径参数): string,必需

    Deployment 的名称

  • namespace (路径参数): string,必需

    namespace

  • body: Deployment,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • pretty (查询参数): string

    pretty

响应

200 (Deployment): OK

201 (Deployment): Created

401: Unauthorized

update 替换指定的 Deployment 的状态

HTTP 请求

PUT /apis/apps/v1/namespaces/{namespace}/deployments/{name}/status

参数

  • name (路径参数): string,必需

    Deployment 的名称

  • namespace (路径参数): string,必需

    namespace

  • body: Deployment,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • pretty (查询参数): string

    pretty

响应

200 (Deployment): OK

201 (Deployment): Created

401: Unauthorized

patch 部分更新指定的 Deployment

HTTP 请求

PATCH /apis/apps/v1/namespaces/{namespace}/deployments/{name}

参数

  • name (路径参数): string,必需

    Deployment 的名称

  • namespace (路径参数): string,必需

    namespace

  • body: Patch,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • force (查询参数): boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (Deployment): OK

201 (Deployment): Created

401: Unauthorized

patch 部分更新指定的 Deployment 的状态

HTTP 请求

PATCH /apis/apps/v1/namespaces/{namespace}/deployments/{name}/status

参数

  • name (路径参数): string,必需

    Deployment 的名称

  • namespace (路径参数): string,必需

    namespace

  • body: Patch,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • force (查询参数): boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (Deployment): OK

201 (Deployment): Created

401: Unauthorized

delete 删除 Deployment

HTTP 请求

DELETE /apis/apps/v1/namespaces/{namespace}/deployments/{name}

参数

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除 Deployment 的集合

HTTP 请求

DELETE /apis/apps/v1/namespaces/{namespace}/deployments

参数

响应

200 (Status): OK

401: Unauthorized

6.5.1.6 - StatefulSet

StatefulSet 表示一组具有一致身份的 Pod

apiVersion: apps/v1

import "k8s.io/api/apps/v1"

StatefulSet

StatefulSet 表示一组具有一致身份的 Pod。身份定义为:

  • 网络:一个稳定的 DNS 和主机名。
  • 存储:根据要求提供尽可能多的 VolumeClaim。

StatefulSet 保证给定的网络身份将始终映射到相同的存储身份。


StatefulSetSpec

StatefulSetSpec 是 StatefulSet 的规约。


  • serviceName (string), 必需

    serviceName 是管理此 StatefulSet 服务的名称。 该服务必须在 StatefulSet 之前即已存在,并负责该集合的网络标识。 Pod 会获得符合以下模式的 DNS/主机名: pod-specific-string.serviceName.default.svc.cluster.local。 其中 “pod-specific-string” 由 StatefulSet 控制器管理。

  • template (PodTemplateSpec), 必需

    template 是用来描述 Pod 的对象,检测到副本不足时将创建所描述的 Pod。 经由 StatefulSet 创建的每个 Pod 都将满足这个模板,但与 StatefulSet 的其余 Pod 相比,每个 Pod 具有唯一的标识。 每个 Pod 将以 <statefulsetname>-<podindex> 格式命名。 例如,名为 "web" 且索引号为 "3" 的 StatefulSet 中的 Pod 将被命名为 "web-3"。 template.spec.restartPolicy 唯一被允许的值是 Always

  • replicas (int32)

    replicas 是给定模板的所需的副本数。之所以称作副本,是因为它们是相同模板的实例, 不过各个副本也具有一致的身份。如果未指定,则默认为 1。

  • updateStrategy (StatefulSetUpdateStrategy)

    updateStrategy 是一个 StatefulSetUpdateStrategy,表示当对 template 进行修订时,用何种策略更新 StatefulSet 中的 Pod 集合。

    StatefulSetUpdateStrategy 表示 StatefulSet 控制器将用于执行更新的策略。其中包括为指定策略执行更新所需的额外参数。

    • updateStrategy.type (string)

    type 表示 StatefulSetUpdateStrategy 的类型,默认为 RollingUpdate。

    • updateStrategy.rollingUpdate (RollingUpdateStatefulSetStrategy)

      当 type 为 RollingUpdate 时,使用 rollingUpdate 来传递参数。

      RollingUpdateStatefulSetStrategy 用于为 rollingUpdate 类型的更新传递参数。

      • updateStrategy.rollingUpdate.maxUnavailable (IntOrString)

        更新期间不可用的 Pod 个数上限。取值可以是绝对数量(例如:5)或所需 Pod 的百分比(例如:10%)。 绝对数是通过四舍五入的百分比计算得出的。不能为 0,默认为 1。 此字段为 Alpha 级别,仅被启用 MaxUnavailableStatefulSet 特性的服务器支持。 此字段适用于 0 到 replicas-1 范围内的所有 Pod。这意味着如果在 0 到 replicas-1 范围内有任何不可用的 Pod, 这些 Pod 将被计入 maxUnavailable 中。

        IntOrString 是一种可以包含 int32 或字符串数值的类型。在 JSON 或 YAML 编组和解组时, 会生成或使用内部类型。例如,此类型允许你定义一个可以接受名称或数字的 JSON 字段。

      • updateStrategy.rollingUpdate.partition (int32)

        partition 表示 StatefulSet 应该被分区进行更新时的序数。 在滚动更新期间,序数在 replicas-1 和 partition 之间的所有 Pod 都会被更新。 序数在 partition-1 和 0 之间的所有 Pod 保持不变。 这一属性有助于进行金丝雀部署。默认值为 0。

  • podManagementPolicy (string)

    podManagementPolicy 控制在初始规模扩展期间、替换节点上的 Pod 或缩减集合规模时如何创建 Pod。 默认策略是 “OrderedReady”,各个 Pod 按升序创建的(pod-0,然后是pod-1 等), 控制器将等到每个 Pod 都准备就绪后再继续。缩小集合规模时,Pod 会以相反的顺序移除。 另一种策略是 “Parallel”,意味着并行创建 Pod 以达到预期的规模而无需等待,并且在缩小规模时将立即删除所有 Pod。

  • revisionHistoryLimit (int32)

    revisionHistoryLimit 是在 StatefulSet 的修订历史中维护的修订个数上限。 修订历史中包含并非由当前所应用的 StatefulSetSpec 版本未表示的所有修订版本。默认值为 10。

  • volumeClaimTemplates ([]PersistentVolumeClaim)

    volumeClaimTemplates 是允许 Pod 引用的申领列表。 StatefulSet controller 负责以维持 Pod 身份不变的方式将网络身份映射到申领之上。 此列表中的每个申领至少必须在模板的某个容器中存在匹配的(按 name 匹配)volumeMount。 此列表中的申领优先于模板中具有相同名称的所有卷。

  • minReadySeconds (int32)

    新创建的 Pod 应准备就绪(其任何容器都未崩溃)的最小秒数,以使其被视为可用。 默认为 0(Pod 准备就绪后将被视为可用)。

  • persistentVolumeClaimRetentionPolicy (StatefulSetPersistentVolumeClaimRetentionPolicy)

    persistentVolumeClaimRetentionPolicy 描述从 VolumeClaimTemplates 创建的持久卷申领的生命周期。 默认情况下,所有持久卷申领都根据需要创建并被保留到手动删除。 此策略允许更改申领的生命周期,例如在 StatefulSet 被删除或其中 Pod 集合被缩容时删除持久卷申领。 此属性需要启用 StatefulSetAutoDeletePVC 特性门控。特性处于 Alpha 阶段。可选。

    StatefulSetPersistentVolumeClaimRetentionPolicy 描述了用于从 StatefulSet VolumeClaimTemplate 创建的 PVC 的策略

    • persistentVolumeClaimRetentionPolicy.whenDeleted (string)

      whenDeleted 指定当 StatefulSet 被删除时,基于 StatefulSet VolumeClaimTemplates 所创建的 PVC 会发生什么。 默认策略 Retain 使 PVC 不受 StatefulSet 被删除的影响。Delete 策略会导致这些 PVC 也被删除。

    • persistentVolumeClaimRetentionPolicy.whenScaled (string)

      whenScaled 指定当 StatefulSet 缩容时,基于 StatefulSet volumeClaimTemplates 创建的 PVC 会发生什么。 默认策略 Retain 使 PVC 不受缩容影响。 Delete 策略会导致超出副本个数的所有的多余 Pod 所关联的 PVC 被删除。

  • ordinals (StatefulSetOrdinals)

    ordinals 控制 StatefulSet 中副本索引的编号。 默认序数行为是将索引 "0" 设置给第一个副本,对于每个额外请求的副本,该索引加一。 使用 ordinals 字段需要启用 Beta 级别的 StatefulSetStartOrdinal 特性门控。

    StatefulSetOrdinals 描述此 StatefulSet 中用于副本序数赋值的策略。

    • ordinals.start (int32)

      start 是代表第一个副本索引的数字。它可用于从替代索引(例如:从 1 开始索引)而非默认的从 0 索引来为副本设置编号,
      

      还可用于编排从一个 StatefulSet 到另一个 StatefulSet 的渐进式副本迁移动作。如果设置了此值,副本索引范围为 [.spec.ordinals.start, .spec.ordinals.start + .spec.replicas)。如果不设置,则默认为 0。 副本索引范围为 [0, .spec.replicas)。

StatefulSetStatus

StatefulSetStatus 表示 StatefulSet 的当前状态。


  • replicas (int32), 必需

    replicas 是 StatefulSet 控制器创建的 Pod 个数。

  • readyReplicas (int32)

    readyReplicas 是为此 StatefulSet 创建的、状况为 Ready 的 Pod 个数。

  • currentReplicas (int32)

    currentReplicas 是 StatefulSet 控制器根据 currentReplicas 所指的 StatefulSet 版本创建的 Pod 个数。

  • updatedReplicas (int32)

    updatedReplicas 是 StatefulSet 控制器根据 updateRevision 所指的 StatefulSet 版本创建的 Pod 个数。

  • availableReplicas (int32)

    此 StatefulSet 所对应的可用 Pod 总数(就绪时长至少为 minReadySeconds)。

  • collisionCount (int32)

    collisionCount 是 StatefulSet 的哈希冲突计数。 StatefulSet controller 在需要为最新的 controllerRevision 创建名称时使用此字段作为避免冲突的机制。

  • conditions ([]StatefulSetCondition)

    补丁策略:根据 type 键执行合并操作

    表示 StatefulSet 当前状态的最新可用观察结果。

    StatefulSetCondition 描述了 StatefulSet 在某个点的状态。

    • conditions.status (string), 必需

      状况的状态为 True、False、Unknown 之一。

    • conditions.type (string), 必需

      StatefulSet 状况的类型。

    • conditions.lastTransitionTime (Time)

      最近一次状况从一种状态转换到另一种状态的时间。

      Time 是 time.Time 的包装器,它支持对 YAML 和 JSON 的正确编组。 time 包的许多工厂方法提供了包装器。

    • conditions.message (string)

      一条人类可读的消息,指示有关转换的详细信息。

    • conditions.reason (string)

      状况最后一次转换的原因。

  • currentRevision (string)

    currentRevision,如果不为空,表示用于在序列 [0,currentReplicas) 之间生成 Pod 的 StatefulSet 的版本。

  • updateRevision (string)

    updateRevision,如果不为空,表示用于在序列 [replicas-updatedReplicas,replicas) 之间生成 Pod 的 StatefulSet 的版本。

  • observedGeneration (int64)

    observedGeneration 是 StatefulSet 的最新一代。它对应于 StatefulSet 的代数,由 API 服务器在变更时更新。

StatefulSetList

StatefulSetList 是 StatefulSet 的集合。


  • items ([]StatefulSet), 必需

    items 是 StatefulSet 的列表。

操作


get 读取指定的 StatefulSet

HTTP 请求

GET /apis/apps/v1/namespaces/{namespace}/statefulsets/{name}

参数

  • name (路径参数): string, 必需

    StatefulSet 的名称。

  • namespace (路径参数): string, 必需

    namespace

  • pretty (查询参数): string

    pretty

响应

200 (StatefulSet): OK

401: Unauthorized

get 读取指定 StatefulSet 的状态

HTTP 请求

GET /apis/apps/v1/namespaces/{namespace}/statefulsets/{name}/status

参数

  • name (路径参数): string, 必需

    StatefulSet 的名称。

  • namespace (路径参数): string, 必需

    namespace

  • pretty (查询参数): string

    pretty

响应

200 (StatefulSet): OK

401: Unauthorized

list 列出或监视 StatefulSet 类型的对象

HTTP 请求

GET /apis/apps/v1/namespaces/{namespace}/statefulsets

参数

  • namespace (路径参数): string, 必需

    namespace

  • limit (查询参数): integer

    limit

  • pretty (查询参数): string

    pretty

  • watch (查询参数): boolean

    watch

响应

200 (StatefulSetList): OK

401: Unauthorized

list 列出或监视 StatefulSet 类型的对象

HTTP 请求

GET /apis/apps/v1/statefulsets

参数

  • limit (查询参数): integer

    limit

  • pretty (查询参数): string

    pretty

  • watch (查询参数): boolean

    watch

响应

200 (StatefulSetList): OK

401: Unauthorized

create 创建一个 StatefulSet

HTTP 请求

POST /apis/apps/v1/namespaces/{namespace}/statefulsets

参数

  • namespace (路径参数): string, 必需

    namespace

  • dryRun (查询参数): string

    dryRun

  • pretty (查询参数): string

pretty

响应

200 (StatefulSet): OK

201 (StatefulSet): Created

202 (StatefulSet): Accepted

401: Unauthorized

update 替换指定的 StatefulSet

HTTP 请求

PUT /apis/apps/v1/namespaces/{namespace}/statefulsets/{name}

参数

  • name (路径参数): string, 必需

    StatefulSet 的名称 。

  • namespace (路径参数): string, 必需

    namespace

  • dryRun (查询参数): string

    dryRun

  • pretty (查询参数): string

    pretty

响应

200 (StatefulSet): OK

201 (StatefulSet): Created

401: Unauthorized

update 替换指定 StatefulSet 的状态

HTTP 请求

PUT /apis/apps/v1/namespaces/{namespace}/statefulsets/{name}/status

参数

  • name (路径参数): string, 必需

    StatefulSet 的名称。

  • namespace (路径参数): string, required

    namespace

  • dryRun (查询参数): string

    dryRun

  • pretty (查询参数): string

    pretty

响应

200 (StatefulSet): OK

201 (StatefulSet): Created

401: Unauthorized

patch 部分更新指定的 StatefulSet

HTTP 请求

PATCH /apis/apps/v1/namespaces/{namespace}/statefulsets/{name}

参数

  • name (路径参数): string, 必需

    StatefulSet 的名称。

  • namespace (路径参数): string, 必需

    namespace

  • dryRun (查询参数): string

    dryRun

  • force (查询参数): boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (StatefulSet): OK

201 (StatefulSet): Created

401: Unauthorized

patch 部分更新指定 StatefulSet 的状态

HTTP 请求

PATCH /apis/apps/v1/namespaces/{namespace}/statefulsets/{name}/status

参数

  • name (路径参数): string, 必需

    StatefulSet 的名称。

  • namespace (路径参数): string, 必需

    namespace

  • dryRun (查询参数): string

    dryRun

  • force (查询参数): boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (StatefulSet): OK

201 (StatefulSet): Created

401: Unauthorized

delete 删除一个 StatefulSet

HTTP 请求

DELETE /apis/apps/v1/namespaces/{namespace}/statefulsets/{name}

参数

  • name (路径参数): string, 必需

    StatefulSet 的名称。

  • dryRun (查询参数): string

    dryRun

  • pretty (查询参数): string

    pretty

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除 StatefulSet 的集合

HTTP 请求

DELETE /apis/apps/v1/namespaces/{namespace}/statefulsets

参数

  • dryRun (查询参数): string

    dryRun

  • limit (查询参数): integer

    limit

  • pretty (查询参数): string

    pretty

响应

200 (Status): OK

401: Unauthorized

6.5.1.7 - ControllerRevision

ControllerRevision 实现了状态数据的不可变快照。

apiVersion: apps/v1

import "k8s.io/api/apps/v1"

ControllerRevision

ControllerRevision 实现了状态数据的不可变快照。 客户端负责序列化和反序列化对象,包含对象内部状态。 成功创建 ControllerRevision 后,将无法对其进行更新。 API 服务器将无法成功验证所有尝试改变 data 字段的请求。 但是,可以删除 ControllerRevisions。 请注意,由于 DaemonSet 和 StatefulSet 控制器都使用它来进行更新和回滚,所以这个对象是 Beta 版。 但是,它可能会在未来版本中更改名称和表示形式,客户不应依赖其稳定性。 它主要供控制器内部使用。


  • apiVersion: apps/v1
  • kind: ControllerRevision
  • revision (int64),必需

    revision 表示 data 表示的状态的修订。

  • data (RawExtension)

    data 是状态的序列化表示。

    RawExtension 用于以外部版本来保存扩展数据。

    要使用它,请生成一个字段,在外部、版本化结构中以 RawExtension 作为其类型,在内部结构中以 Object 作为其类型。 你还需要注册你的各个插件类型。

    // 内部包:

    type MyAPIObject struct {  
      runtime.TypeMeta `json:",inline"`   
      MyPlugin runtime.Object `json:"myPlugin"`  
    } 
    
    type PluginA struct {  
      AOption string `json:"aOption"`  
    }
    

    // 外部包:

    type MyAPIObject struct {  
      runtime.TypeMeta `json:",inline"`  
      MyPlugin runtime.RawExtension `json:"myPlugin"`    
    } 
    
    type PluginA struct {  
      AOption string `json:"aOption"`  
    }
    

    // 在网络上,JSON 看起来像这样:

    {  
      "kind":"MyAPIObject",  
      "apiVersion":"v1",  
      "myPlugin": {  
        "kind":"PluginA",  
        "aOption":"foo",  
      },  
    }
    

    那么会发生什么? 解码首先使用 json 或 yaml 将序列化数据解组到你的外部 MyAPIObject 中。 这会导致原始 JSON 被存储下来,但不会被解包。 下一步是复制(使用 pkg/conversion)到内部结构中。 runtime 包的 DefaultScheme 安装了转换函数,它将解析存储在 RawExtension 中的 JSON, 将其转换为正确的对象类型,并将其存储在 Object 中。 (TODO:如果对象是未知类型,将创建并存储一个 runtime.Unknown对象。)

ControllerRevisionList

ControllerRevisionList 是一个包含 ControllerRevision 对象列表的资源。


  • apiVersion: apps/v1
  • kind: ControllerRevisionList

操作


get 读取特定的 ControllerRevision

HTTP 请求

GET /apis/apps/v1/namespaces/{namespace}/controllerrevisions/{name}

参数

  • name路径参数):string,必需

    ControllerRevision 的名称

  • namespace路径参数):string,必需

    namespace

  • pretty查询参数):string

    pretty

响应

200 (ControllerRevision): OK

401: Unauthorized

list 列出或监视 ControllerRevision 类别的对象

HTTP 请求

GET /apis/apps/v1/namespaces/{namespace}/controllerrevisions

参数

  • namespace路径参数):string,必需

    namespace

  • continue查询参数):string

    continue

  • limit (查询参数)): integer

    limit

  • pretty查询参数):string

    pretty

  • watch查询参数): boolean

    watch

响应

200 (ControllerRevisionList): OK

401: Unauthorized

list 列出或监视 ControllerRevision 类别的对象

HTTP 请求

GET /apis/apps/v1/controllerrevisions

参数

  • continue查询参数):string

    continue

  • limit查询参数): integer

    limit

  • pretty查询参数):string

    pretty

  • watch查询参数): boolean

    watch

响应

200 (ControllerRevisionList): OK

401: Unauthorized

create 创建一个 ControllerRevision

HTTP 请求

POST /apis/apps/v1/namespaces/{namespace}/controllerrevisions

参数

  • namespace路径参数):string,必需

    namespace

  • dryRun查询参数):string

    dryRun

  • pretty查询参数):string

    pretty

响应

200 (ControllerRevision): OK

201 (ControllerRevision): Created

202 (ControllerRevision): Accepted

401: Unauthorized

update 替换特定的 ControllerRevision

HTTP 参数

PUT /apis/apps/v1/namespaces/{namespace}/controllerrevisions/{name}

参数

  • name路径参数):string,必需

    ControllerRevision 的名称

  • namespace路径参数):string,必需

    namespace

  • dryRun查询参数):string

    dryRun

  • pretty查询参数):string

    pretty

响应

200 (ControllerRevision): OK

201 (ControllerRevision): Created

401: Unauthorized

patch 部分更新特定的 ControllerRevision

HTTP 请求

PATCH /apis/apps/v1/namespaces/{namespace}/controllerrevisions/{name}

参数

  • name路径参数):string,必需

    ControllerRevision 的名称

  • namespace路径参数):string,必需

    namespace

  • dryRun查询参数):string

    dryRun

  • force查询参数): boolean

    force

  • pretty查询参数):string

    pretty

响应

200 (ControllerRevision): OK

201 (ControllerRevision): Created

401: Unauthorized

delete 删除一个 ControllerRevision

HTTP 请求

DELETE /apis/apps/v1/namespaces/{namespace}/controllerrevisions/{name}

参数

  • name路径参数):string,必需

    ControllerRevision 的名称

  • namespace路径参数):string,必需

    namespace

  • dryRun查询参数):string

    dryRun

  • pretty查询参数):string

    pretty

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除 ControllerRevision 集合

HTTP 请求

DELETE /apis/apps/v1/namespaces/{namespace}/controllerrevisions

参数

  • namespace路径参数):string,必需

    namespace

  • continue查询参数):string

    continue

  • dryRun查询参数):string

    dryRun

  • limit查询参数): integer

    limit

  • pretty查询参数):string

    pretty

响应

200 (Status): OK

401: Unauthorized

6.5.1.8 - DaemonSet

DaemonSet 表示守护进程集的配置。

apiVersion: apps/v1

import "k8s.io/api/apps/v1"

DaemonSet

DaemonSet 表示守护进程集的配置。


  • apiVersion: apps/v1

  • kind: DaemonSet

DaemonSetSpec

DaemonSetSpec 是守护进程集的规约。


  • minReadySeconds (int32)

    新建的 DaemonSet Pod 应该在没有任何容器崩溃的情况下处于就绪状态的最小秒数,这样它才会被认为是可用的。 默认为 0(Pod 准备就绪后将被视为可用)。

  • updateStrategy (DaemonSetUpdateStrategy)

    用新 Pod 替换现有 DaemonSet Pod 的更新策略。

    DaemonSetUpdateStrategy 是一个结构体,用于控制 DaemonSet 的更新策略。

    • updateStrategy.type (string)

      守护进程集更新的类型。可以是 "RollingUpdate" 或 "OnDelete"。默认为 RollingUpdate。

    • updateStrategy.rollingUpdate (RollingUpdateDaemonSet)

      滚动更新配置参数。仅在 type 值为 "RollingUpdate" 时出现。

      用于控制守护进程集滚动更新的预期行为的规约。

      • updateStrategy.rollingUpdate.maxSurge (IntOrString)

        对于拥有可用 DaemonSet Pod 的节点而言,在更新期间可以拥有更新后的 DaemonSet Pod 的最大节点数。 属性值可以是绝对数量(例如:5)或所需 Pod 的百分比(例如:10%)。 如果 maxUnavailable 为 0,则该值不能为 0。绝对数是通过四舍五入从百分比计算得出的,最小值为 1。 默认值为 0。示例:当设置为 30% 时,最多为节点总数的 30% 节点上应该运行守护进程 Pod (即 status.desiredNumberScheduled) 可以在旧 Pod 标记为已删除之前创建一个新 Pod。更新首先在 30% 的节点上启动新的 Pod。 一旦更新的 Pod 可用(就绪时长至少 minReadySeconds 秒),该节点上的旧 DaemonSet pod 就会被标记为已删除。 如果旧 Pod 因任何原因变得不可用(Ready 转换为 false、被驱逐或节点被腾空), 则会立即在该节点上创建更新的 Pod,而不考虑激增限制。 允许激增意味着如果就绪检查失败,任何给定节点上的守护进程集消耗的资源可能会翻倍, 因此资源密集型守护进程集应该考虑到它们可能会在中断期间导致驱逐。

        IntOrString 是一种可以容纳 int32 或字符串的类型。在 JSON 或 YAML 编组和解组中使用时,它会生成或使用内部类型。 例如,这允许你拥有一个可以接受名称或数字的 JSON 字段。

      • updateStrategy.rollingUpdate.maxUnavailable (IntOrString)

        更新期间不可用的 DaemonSet Pod 的最大数量。值可以是绝对数(例如:5)或更新开始时 DaemonSet Pod 总数的百分比(例如:10%)。 绝对数是通过四舍五入的百分比计算得出的。如果 maxSurge 为 0,则此值不能为 0 默认值为 1。 例如:当设置为 30% 时,最多节点总数 30% 的、应该运行守护进程的节点总数(即 status.desiredNumberScheduled) 可以在任何给定时间停止更新。更新首先停止最多 30% 的 DaemonSet Pod, 然后在它们的位置启动新的 DaemonSet Pod。 一旦新的 Pod 可用,它就会继续处理其他 DaemonSet Pod,从而确保在更新期间至少 70% 的原始 DaemonSet Pod 数量始终可用。

        IntOrString 是一种可以保存 int32 或字符串的类型。在 JSON 或 YAML 编组和解组中使用时,它会生成或使用内部类型。例如,这允许你拥有一个可以接受名称或数字的 JSON 字段。

  • revisionHistoryLimit (int32)

    用来允许回滚而保留的旧历史记录的数量。此字段是个指针,用来区分明确的零值和未指定的指针。默认值是 10。

DaemonSetStatus

DaemonSetStatus 表示守护进程集的当前状态。


  • numberReady (int32),必需

    numberReady 是应该运行守护进程 Pod 并且有一个或多个 DaemonSet Pod 以就绪条件运行的节点数。

  • numberAvailable (int32)

    应该运行守护进程 Pod 并有一个或多个守护进程 Pod 正在运行和可用(就绪时长超过 spec.minReadySeconds)的节点数量。

  • numberUnavailable (int32)

    应该运行守护进程 Pod 并且没有任何守护进程 Pod 正在运行且可用(至少已就绪 spec.minReadySeconds 秒)的节点数。

  • updatedNumberScheduled (int32)

    正在运行更新后的守护进程 Pod 的节点总数。

  • collisionCount (int32)

    DaemonSet 的哈希冲突计数。DaemonSet 控制器在需要为最新的 ControllerRevision 创建名称时使用此字段作为避免冲突的机制。

  • conditions ([]DaemonSetCondition)

    补丁策略:根据 type 键合并

    表示 DaemonSet 当前状态的最新可用观测信息。

    DaemonSet Condition 描述了 DaemonSet 在某一时刻的状态。

    • conditions.status (string),必需

      状况的状态,True、False、Unknown 之一。

    • conditions.type (string),必需

      DaemonSet 状况的类型。

    • conditions.lastTransitionTime (Time)

      状况上次从一种状态转换到另一种状态的时间。

      Time 是对 time.Time 的封装,支持正确编码为 YAML 和 JSON。time 包为许多工厂方法提供了封装器。

    • conditions.message (string)

      一条人类可读的消息,指示有关转换的详细信息。

    • conditions.reason (string)

      状况最后一次转换的原因。

  • observedGeneration (int64)

    守护进程集控制器观察到的最新一代。

DaemonSetList

DaemonSetList 是守护进程集的集合。


  • apiVersion: apps/v1

  • kind: DaemonSetList

Operations


get 读取指定的 DaemonSet

HTTP 请求

GET /apis/apps/v1/namespaces/{namespace}/daemonsets/{name}

参数

  • name (路径参数): string, 必需

    DaemonSet 的名称

  • namespace (路径参数): string, 必需

    namespace

  • pretty (查询参数): string

    pretty

响应

200 (DaemonSet): OK

401: 未授权

get 读取指定的 DaemonSet 的状态

HTTP 请求

GET /apis/apps/v1/namespaces/{namespace}/daemonsets/{name}/status

参数

  • name (路径参数): string, 必需

    DaemonSet 的名称

  • namespace (路径参数): string, 必需

    namespace

  • pretty (查询参数): string

    pretty

响应

200 (DaemonSet): OK

401: 未授权

list 列表或查看 DaemonSet 类型的对象

HTTP 请求

GET /apis/apps/v1/namespaces/{namespace}/daemonsets

参数

  • namespace (路径参数): string, 必需

    namespace

  • limit (查询参数): integer

    limit

  • pretty (查询参数): string

    pretty

响应

200 (DaemonSetList): OK

401: 未授权

list 列表或查看 DaemonSet 类型的对象

HTTP 请求

GET /apis/apps/v1/daemonsets

参数

  • limit (查询参数): integer

    limit

  • pretty (查询参数): string

    pretty

响应

200 (DaemonSetList): OK

401: 未授权

create 创建一个 DaemonSet

HTTP 请求

POST /apis/apps/v1/namespaces/{namespace}/daemonsets

参数

响应

200 (DaemonSet): OK

201 (DaemonSet): 创建完成

202 (DaemonSet): 已接受

401: 未授权

update 替换指定的 DaemonSet

HTTP 请求

PUT /apis/apps/v1/namespaces/{namespace}/daemonsets/{name}

参数

  • name (路径参数): string,必需

    DaemonSet 的名称

  • namespace (路径参数): string,必需

    namespace

  • body: DaemonSet,必需

响应

200 (DaemonSet): OK

201 (DaemonSet): 已创建

401: 未授权

update 替换指定 DaemonSet 的状态

HTTP 请求

PUT /apis/apps/v1/namespaces/{namespace}/daemonsets/{name}/status

参数

  • name (路径参数): string, 必需

    DaemonSet 的名称

  • namespace (路径参数): string, 必需

    namespace

  • body: DaemonSet, 必需

响应

200 (DaemonSet): OK

201 (DaemonSet): 已创建

401: 未授权

patch 部分更新指定的 DaemonSet

HTTP 请求

PATCH /apis/apps/v1/namespaces/{namespace}/daemonsets/{name}

参数

  • name (路径参数): string, 必需

    DaemonSet 的名称

  • namespace (路径参数): string, 必需

    namespace

  • body: Patch, 必需

  • pretty (查询参数): string

    pretty

响应

200 (DaemonSet): OK

201 (DaemonSet): 已创建

401: 未授权

patch 部分更新指定 DaemonSet 的状态

HTTP 请求

PATCH /apis/apps/v1/namespaces/{namespace}/daemonsets/{name}/status

参数

  • name (路径参数): string, 必需

    DaemonSet 的名称

  • namespace (路径参数): string, 必需

    namespace

  • body: Patch, 必需

  • pretty (查询参数): string

    pretty

响应

200 (DaemonSet): OK

201 (DaemonSet): 已创建

401: 未授权

delete 删除一个 DaemonSet

HTTP 请求

DELETE /apis/apps/v1/namespaces/{namespace}/daemonsets/{name}

参数

  • name (路径参数): string,必需

    DaemonSet 的名称

  • namespace (路径参数): string,必需

    namespace

  • body: DeleteOptions

响应

200 (Status): OK

202 (Status): 已接受

401: 未授权

deletecollection 删除 DaemonSet 的集合

HTTP 请求

DELETE /apis/apps/v1/namespaces/{namespace}/daemonsets

参数

  • continue (查询参数): string

    continue

  • dryRun (查询参数): string

    dryRun

响应

200 (Status): OK

401: 未授权

6.5.1.9 - Job

Job 表示单个任务的配置。

apiVersion: batch/v1

import "k8s.io/api/batch/v1"

Job

Job 表示单个任务的配置。


  • apiVersion: batch/v1

  • kind: Job

JobSpec

JobSpec 描述了任务执行的情况。


Replicas

Lifecycle

  • completionMode (string)

    completionMode 指定如何跟踪 Pod 完成情况。它可以是 NonIndexed(默认)或者 Indexed

    NonIndexed 表示当有 .spec.completions 个成功完成的 Pod 时,认为 Job 完成。每个 Pod 完成都是彼此同源的。

    Indexed 意味着 Job 的各个 Pod 会获得对应的完成索引值,从 0 到(.spec.completions - 1),可在注解 "batch.kubernetes.io/job-completion-index" 中找到。当每个索引都对应有一个成功完成的 Pod 时, 该任务被认为是完成的。 当值为 Indexed 时,必须指定 .spec.completions 并且 .spec.parallelism 必须小于或等于 10^5。 此外,Pod 名称采用 $(job-name)-$(index)-$(random-string) 的形式,Pod 主机名采用 $(job-name)-$(index) 的形式。

    将来可能添加更多的完成模式。如果 Job 控制器发现它无法识别的模式 (这种情况在升级期间由于版本偏差可能发生),则控制器会跳过 Job 的更新。

  • backoffLimit (int32)

    指定标记此任务失败之前的重试次数。默认值为 6。

  • activeDeadlineSeconds (int64)

    系统尝试终止任务之前任务可以持续活跃的持续时间(秒),时间长度是相对于 startTime 的; 字段值必须为正整数。如果任务被挂起(在创建期间或因更新而挂起), 则当任务再次恢复时,此计时器会被停止并重置。

  • ttlSecondsAfterFinished (int32)

    ttlSecondsAfterFinished 限制已完成执行(完成或失败)的任务的生命周期。如果设置了这个字段, 在 Job 完成 ttlSecondsAfterFinished 秒之后,就可以被自动删除。 当 Job 被删除时,它的生命周期保证(例如终结器)会被考察。 如果未设置此字段,则任务不会被自动删除。如果此字段设置为零,则任务在完成后即可立即删除。

  • suspend (boolean)

    suspend 指定 Job 控制器是否应该创建 Pod。如果创建 Job 时将 suspend 设置为 true,则 Job 控制器不会创建任何 Pod。 如果 Job 在创建后被挂起(即标志从 false 变为 true),则 Job 控制器将删除与该 Job 关联的所有活动 Pod。 用户必须设计他们的工作负载来优雅地处理这个问题。暂停 Job 将重置 Job 的 startTime 字段, 也会重置 ActiveDeadlineSeconds 计时器。默认为 false。

Selector

Beta 级别

  • podFailurePolicy (PodFailurePolicy)

    指定处理失效 Pod 的策略。特别是,它允许指定采取关联操作需要满足的一组操作和状况。 如果为空,则应用默认行为:由该任务的 .status.failed 字段表示的失效 Pod 的计数器将递增, 并针对 backoffLimit 进行检查。此字段不能与 restartPolicy=OnFailure 结合使用。

    此字段是 Beta 级别。当 JobPodFailurePolicy 特性门控被启用时(默认被启用),可以使用此字段。

    PodFailurePolicy 描述失效的 Pod 如何影响 backoffLimit。

    • podFailurePolicy.rules ([]PodFailurePolicyRule),必需

      原子: 将在合并期间被替换

      Pod 失效策略规则的列表。这些规则按顺序进行评估。一旦某规则匹配 Pod 失效,则其余规将被忽略。 当没有规则匹配 Pod 失效时,将应用默认的处理方式: Pod 失效的计数器递增并针对 backoffLimit 进行检查。最多允许 20 个。

      PodFailurePolicyRule 描述当满足要求时如何处理一个 Pod 失效。 在每个规则中可以使用 onExitCodes 和 onPodConditions 之一,但不能同时使用二者。

      • podFailurePolicy.rules.action (string),必需

        指定当要求满足时对 Pod 失效采取的操作。可能的值是:

        • FailJob:表示 Pod 的任务被标记为 Failed 且所有正在运行的 Pod 都被终止。
        • FailIndex:表示 Pod 对应的索引被标记为 Failed 且 Pod 不会被重新启动。 此值是 Alpha 级别的。当 JobBackoffLimitPerIndex 特性门控被启用时(默认被禁用),可以使用此值。
        • Ignore:表示 .backoffLimit 的计数器没有递增,并创建了一个替代 Pod。

        • Count:表示以默认方式处理该 Pod,计数器朝着 .backoffLimit 的方向递增。

        后续会考虑增加其他值。客户端应通过跳过此规则对未知的操作做出反应。

      • podFailurePolicy.rules.onPodConditions ([]PodFailurePolicyOnPodConditionsPattern),必需

        原子: 将在合并期间被替换

        表示对 Pod 状况的要求。该要求表示为 Pod 状况模式的一个列表。 如果至少一个模式与实际的 Pod 状况匹配,则满足此要求。最多允许 20 个。

        PodFailurePolicyOnPodConditionsPattern 描述与实际 Pod 状况类型匹配的模式。

        • podFailurePolicy.rules.onPodConditions.status (string),必需

          指定必需的 Pod 状况状态。要匹配一个 Pod 状况,指定的状态必须等于该 Pod 状况状态。默认为 True。

        • podFailurePolicy.rules.onPodConditions.type (string),必需

          指定必需的 Pod 状况类型。要匹配一个 Pod 状况,指定的类型必须等于该 Pod 状况类型。

      • podFailurePolicy.rules.onExitCodes (PodFailurePolicyOnExitCodesRequirement)

        表示容器退出码有关的要求。

        PodFailurePolicyOnExitCodesRequirement 描述根据容器退出码处理失效 Pod 的要求。 特别是,它为每个应用容器和 Init 容器状态查找在 Pod 状态中分别用 .status.containerStatuses 和 .status.initContainerStatuses 字段表示的 .state.terminated.exitCode。 成功完成的容器(退出码 0)被排除在此要求检查之外。

        • podFailurePolicy.rules.onExitCodes.operator (string),必需

          表示容器退出码和指定值之间的关系。成功完成的容器(退出码 0)被排除在此要求检查之外。可能的值为:

          • In:如果至少一个容器退出码(如果有多个容器不受 'containerName' 字段限制,则可能是多个退出码) 在一组指定值中,则满足要求。

          • NotIn:如果至少一个容器退出码(如果有多个容器不受 'containerName' 字段限制,则可能是多个退出码) 不在一组指定值中,则满足要求。

          后续会考虑增加其他值。客户端应通过假设不满足要求来对未知操作符做出反应。

        • podFailurePolicy.rules.onExitCodes.values ([]int32),必需

          集合:合并期间保留唯一值

          指定值集。每个返回的容器退出码(在多个容器的情况下可能是多个)将根据该操作符有关的这个值集进行检查。 值的列表必须有序且不得包含重复项。值 '0' 不能用于 In 操作符。至少需要 1 个。最多允许 255 个。

        • podFailurePolicy.rules.onExitCodes.containerName (string)

          将退出码的检查限制为具有指定名称的容器。当为 null 时,该规则适用于所有容器。 当被指定时,它应与 Pod 模板中的容器名称或 initContainer 名称之一匹配。

Alpha 级别

  • backoffLimitPerIndex(int32)

    指定在将特定索引的 Pod 标记为失败之前在对该 Pod 重试次数的限制。 启用后,各索引的失败次数将保存在 Pod 的 batch.kubernetes.io/job-index-failure-count 注解中。 仅当 Job 的 completionMode=Indexed 且 Pod 的重启策略为 Never 时才能设置此字段。 此字段是不可变更的。此字段是 Alpha 级别的。 当 JobBackoffLimitPerIndex 特性门控被启用时(默认被禁用),可以使用此字段。

  • maxFailedIndexes(int32)

    指定在 backoffLimitPerIndex 被设置时、标记 Job 为失败之前所允许的最大失败索引数。 一旦失败的索引数超过此数值,整个 Job 将被标记为 Failed 并终止执行。 如果不设置此字段(对应为 null),则作业继续执行其所有索引,且 Job 会被标记 Complete 状况。 此字段只能在设置 backoffLimitPerIndex 时指定。此字段值可以是 null 或完成次数之内的值。 当完成次数大于 10^5 时,此字段是必需的且必须小于等于 10^4。 此字段是 Alpha 级别的。当 JobBackoffLimitPerIndex 特性门控被启用时(默认禁用),可以使用此字段。

  • podReplacementPolicy(string)

    podReplacementPolicy 指定何时创建替代的 Pod。可能的值包括:

    • TerminatingOrFailed:表示当 Pod 处于终止中(具有 metadata.deletionTimestamp)或失败时,重新创建 Pod。
    • Failed:表示在创建替代的 Pod 之前,等待先前创建的 Pod 完全终止(处于 Failed 或 Succeeded 阶段)。

    当使用 podFailurePolicy 时,Failed 是唯一允许值。 当不使用 podFailurePolicy 时,允许使用 TerminatingOrFailed 和 Failed。 这是一个 Alpha 级别的字段。启用 JobPodReplacementPolicy 特性门控才能使用此字段。

JobStatus

JobStatus 表示 Job 的当前状态。


  • startTime (Time)

    表示任务控制器开始处理任务的时间。在挂起状态下创建 Job 时,直到第一次恢复时才会设置此字段。 每次从暂停中恢复任务时都会重置此字段。它表示为 RFC3339 格式的 UTC 时间。

    Time 是 time.Time 的包装器,支持正确编码为 YAML 和 JSON。time 包提供的许多工厂方法都提供了包装器。

  • completionTime (Time)

    表示任务完成的时间。不能保证对多个独立操作按发生的先后顺序设置。此字段表示为 RFC3339 格式的 UTC 时间。 仅当任务成功完成时才设置完成时间。

    Time 是 time.Time 的包装器,支持正确编码为 YAML 和 JSON。time 包提供的许多工厂方法都提供了包装器。

  • active (int32)

    待处理和正在运行的 Pod 的数量。

  • failed (int32)

    进入 Failed 阶段的 Pod 数量。

  • succeeded (int32)

    进入 Succeeded 阶段的 Pod 数量。

  • completedIndexes (string)

    completedIndexes 以文本格式保存 .spec.completionMode 设置为 "Indexed" 的 Pod 已完成的索引。 索引用十进制整数表示,用逗号分隔。数字是按递增的顺序排列的。三个或更多的连续数字被压缩, 用系列的第一个和最后一个元素表示,用连字符分开。例如,如果完成的索引是 1、3、4、5 和 7,则表示为 "1、3-5、7"。

  • conditions ([]JobCondition)

    补丁策略:根据 type 键合并

    原子: 将在合并期间被替换

    对象当前状态的最新可用观察结果。当任务失败时,其中一个状况的类型为 “Failed”,状态为 true。 当任务被暂停时,其中一个状况的类型为 “Suspended”,状态为true;当任务被恢复时,该状况的状态将变为 false。 任务完成时,其中一个状况的类型为 "Complete",状态为 true。更多信息: https://kubernetes.io/zh-cn/docs/concepts/workloads/controllers/jobs-run-to-completion/

    JobCondition 描述任务的当前状况。

    • conditions.status (string), 必需

      状况的状态:True、False、Unknown 之一。

    • conditions.type (string), 必需

      任务状况的类型:Completed 或 Failed。

    • conditions.lastProbeTime (Time)

      最后一次探测的时间。

      Time 是对 time.Time 的封装,支持正确编码为 YAML 和 JSON。我们为 time 包提供的许多工厂方法提供了封装器。

    • conditions.lastTransitionTime (Time)

      上一次从一种状况转换到另一种状况的时间。

      Time 是 time.Time 的包装器,支持正确编码为 YAML 和 JSON。time 包提供的许多工厂方法都提供了包装器。

    • conditions.message (string)

      表示上次转换信息的人类可读消息。

    • conditions.reason (string)

      状况最后一次转换的(简要)原因

  • uncountedTerminatedPods (UncountedTerminatedPods)

    UncountedTerminatedPods 保存已终止但尚未被任务控制器纳入状态计数器中的 Pod 的 UID 的集合。

    任务控制器所创建 Pod 带有终结器。当 Pod 终止(成功或失败)时,控制器将执行三个步骤以在任务状态中对其进行说明:

    1. 将 Pod UID 添加到此字段的列表中。
    2. 去掉 Pod 中的终结器。
    3. 从数组中删除 Pod UID,同时为相应的计数器加一。

    使用此字段可能无法跟踪旧任务,在这种情况下,该字段保持为空。

    UncountedTerminatedPods 持有已经终止的 Pod 的 UID,但还没有被计入工作状态计数器中。

    • uncountedTerminatedPods.failed ([]string)

      集合:合并期间保留唯一值

      failed 字段包含已失败 Pod 的 UID。

    • uncountedTerminatedPods.succeeded ([]string)

      集合:合并期间保留唯一值

      succeeded 包含已成功的 Pod 的 UID。

Beta 级别

  • ready (int32)

    状况为 Ready 的 Pod 数量。

    此字段为 Beta 级别。当特性门控 JobReadyPods 启用(默认启用)时,任务控制器会填充该字段。

Alpha 级别

  • failedIndexes (string)

    当 backoffLimitPerIndex=true 时,failedIndexes 保存失败的索引。 索引以文本格式表示,类似于 completedIndexes 字段,即这些索引是使用逗号分隔的十进制整数。 这些数字按升序列出。三个或更多连续的数字会被压缩,整个序列表示为第一个数字、连字符和最后一个数字。 例如,如果失败的索引是 1、3、4、5 和 7,则表示为 "1,3-5,7"。 该字段是 Alpha 级别的。当 JobBackoffLimitPerIndex 特性门控被启用时(默认被禁用),可以使用此字段。

  • terminating(int32)

    正在终止的 Pod 数量(处于 Pending 或 Running 阶段且具有 deletionTimestamp)。

    此字段是 Alpha 级别的。当特性门控 JobPodReplacementPolicy 被启用时(默认被禁用), Job 控制器会填充该字段。

JobList

JobList 是 Job 的集合。


  • apiVersion: batch/v1

  • kind: JobList

操作


get 读取指定的 Job

HTTP 请求

GET /apis/batch/v1/namespaces/{namespace}/jobs/{name}

参数

  • name (路径参数):string,必需

    Job 的名称。

  • namespace (路径参数): string, 必需

    namespace

  • pretty (查询参数): string

    pretty

响应

200 (Job): OK

401: Unauthorized

get 读取指定任务的状态

HTTP 请求

GET /apis/batch/v1/namespaces/{namespace}/jobs/{name}/status

参数

  • name (路径参数): string, 必需

    Job 的名称。

  • namespace (路径参数): string, 必需

    namespace

  • pretty (查询参数): string

    pretty

响应

200 (Job): OK

401: Unauthorized

list 列举或监测 Job 类别的对象

HTTP 请求

GET /apis/batch/v1/namespaces/{namespace}/jobs

参数

  • namespace (路径参数): string, 必需

    namespace

  • limit (查询参数): integer

    limit

  • pretty (查询参数): string

    pretty

响应

200 (JobList): OK

401: Unauthorized

list 列举或监测 Job 类别的对象

HTTP 请求

GET /apis/batch/v1/jobs

参数

  • limit (查询参数): integer

    limit

  • pretty (查询参数): string

    pretty

响应

200 (JobList): OK

401: Unauthorized

create 创建一个 Job

HTTP 请求

POST /apis/batch/v1/namespaces/{namespace}/jobs

参数

  • namespace (路径参数): string, 必需

    namespace

  • body: Job, 必需

响应

200 (Job): OK

201 (Job): Created

202 (Job): Accepted

401: Unauthorized

update 替换指定的 Job

HTTP 请求

PUT /apis/batch/v1/namespaces/{namespace}/jobs/{name}

参数

  • name (路径参数): string, 必需

    Job 的名称。

  • namespace (路径参数): string, 必需

    namespace

  • body: Job, 必需

响应

200 (Job): OK

201 (Job): Created

401: Unauthorized

update 替换指定 Job 的状态

HTTP 请求

PUT /apis/batch/v1/namespaces/{namespace}/jobs/{name}/status

参数

  • name (路径参数): string, 必需

    Job 的名称。

  • namespace (路径参数): string, 必需

    namespace

  • body: Job, 必需

响应

200 (Job): OK

201 (Job): Created

401: Unauthorized

patch 部分更新指定的 Job

HTTP 请求

PATCH /apis/batch/v1/namespaces/{namespace}/jobs/{name}

参数

  • name (路径参数): string, 必需

    Job 的名称。

  • namespace (路径参数): string, 必需

    namespace

  • body: Patch, 必需

  • pretty (查询参数): string

    pretty

响应

200 (Job): OK

201 (Job): Created

401: Unauthorized

patch 部分更新指定 Job 的状态

HTTP 请求

PATCH /apis/batch/v1/namespaces/{namespace}/jobs/{name}/status

参数

  • name (路径参数): string, 必需

    Job 的名称。

  • namespace (路径参数): string, 必需

    namespace

  • body: Patch, 必需

  • pretty (查询参数): string

    pretty

响应

200 (Job): OK

201 (Job): Created

401: Unauthorized

delete 删除一个 Job

HTTP 请求

DELETE /apis/batch/v1/namespaces/{namespace}/jobs/{name}

参数

  • name (路径参数): string, 必需

    Job 的名称。

  • namespace (路径参数): string, 必需

    namespace

  • body: DeleteOptions

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除 Job 的集合

HTTP 请求

DELETE /apis/batch/v1/namespaces/{namespace}/jobs

参数

  • continue (查询参数): string

    continue

  • dryRun (查询参数): string

    dryRun

响应

200 (Status): OK

401: Unauthorized

6.5.1.10 - CronJob

CronJob 代表单个定时作业 (Cron Job) 的配置。

apiVersion: batch/v1

import "k8s.io/api/batch/v1"

CronJob

CronJob 代表单个定时作业(Cron Job) 的配置。


CronJobSpec

CronJobSpec 描述了作业的执行方式和实际将运行的时间。


  • timeZone (string)

    给定时间表的时区名称,请参阅 https://en.wikipedia.org/wiki/List_of_tz_database_time_zones。 如果未指定,这将默认为 kube-controller-manager 进程的时区。 有效时区名称和时区偏移量的设置由 API 服务器在 CronJob 验证期间从系统范围的时区数据库进行加载, 在执行期间由控制器管理器从系统范围的时区数据库进行加载。 如果找不到系统范围的时区数据库,则转而使用该数据库的捆绑版本。 如果时区名称在 CronJob 的生命周期内或由于主机配置更改而变得无效,该控制器将停止创建新的 Job, 并将创建一个原因为 UnknownTimeZone 的系统事件。更多信息,请请参阅 https://kubernetes.io/zh-cn/docs/concepts/workloads/controllers/cron-jobs/#time-zones

  • concurrencyPolicy (string)

    指定如何处理作业的并发执行。 有效值为:

    • "Allow" (默认):允许 CronJobs 并发运行;
    • "Forbid":禁止并发运行,如果上一次运行尚未完成则跳过下一次运行;
    • "Replace":取消当前正在运行的作业并将其替换为新作业。
  • startingDeadlineSeconds (int64)

    可选字段。当作业因为某种原因错过预定时间时,设定作业的启动截止时间(秒)。错过排期的作业将被视为失败的作业。

  • suspend (boolean)

    这个标志告诉控制器暂停后续的执行,它不适用于已经开始的执行。默认为 false。

  • successfulJobsHistoryLimit (int32)

    要保留的成功完成作业数。值必须是非负整数。默认值为 3。

  • failedJobsHistoryLimit (int32)

    要保留的以失败状态结束的作业个数。值必须是非负整数。默认值为 1。

CronJobStatus

CronJobStatus 表示某个定时作业的当前状态。


  • active ([]ObjectReference)

    Atomic: 将在合并过程中被替换

    指向当前正在运行的作业的指针列表。

  • lastScheduleTime (Time)

    上次成功调度作业的时间信息。

    Time 是对 time.Time 的封装,它支持对 YAML 和 JSON 的正确编排。为 time 包提供的许多工厂方法模式提供了包装器。

  • lastSuccessfulTime (Time)

    上次成功完成作业的时间信息。

    Time 是对 time.Time 的封装,它支持对 YAML 和 JSON 的正确编排。为 time 包提供的许多工厂方法模式提供了包装器。

CronJobList

CronJobList 是定时作业的集合。


  • apiVersion: batch/v1

  • kind: CronJobList

  • items ([]CronJob), required

    items 是 CronJob 的列表。

操作


get 查看指定的 CronJob

HTTP 请求

GET /apis/batch/v1/namespaces/{namespace}/cronjobs/{name}

参数

  • name (路径参数): string, 必需

    CronJob 的名称

  • namespace (路径参数): string, 必需

    namespace

  • pretty (查询参数): string

    pretty

响应

200 (CronJob): OK

401: Unauthorized

get 查看指定 CronJob 的状态

HTTP 请求

GET /apis/batch/v1/namespaces/{namespace}/cronjobs/{name}/status

参数

  • name (路径参数): string, 必需

    CronJob 的名称

  • namespace (路径参数): string, 必需

    namespace

  • pretty (查询参数): string

    pretty

响应

200 (CronJob): OK

401: Unauthorized

list 查看或监视 CronJob 类别的对象

HTTP 请求

GET /apis/batch/v1/namespaces/{namespace}/cronjobs

参数

  • namespace (路径参数): string, 必需

    namespace

  • limit (查询参数): integer

    limit

  • pretty (查询参数): string

    pretty

  • watch (查询参数): boolean

    watch

响应

200 (CronJobList): OK

401: Unauthorized

list 查看或监视 CronJob 类型的对象

HTTP 请求

GET /apis/batch/v1/cronjobs

参数

  • limit (in query): integer

    limit

  • pretty (in query): string

    pretty

  • watch (in query): boolean

    watch

响应

200 (CronJobList): OK

401: Unauthorized

create 创建一个 CronJob

HTTP 请求

POST /apis/batch/v1/namespaces/{namespace}/cronjobs

参数

  • namespace (路径参数): string, 必需

    namespace

  • dryRun (查询参数): string

    dryRun

  • pretty (查询参数): string

    pretty

响应

200 (CronJob): OK

201 (CronJob): Created

202 (CronJob): Accepted

401: Unauthorized

update 替换指定的 CronJob

HTTP 请求

PUT /apis/batch/v1/namespaces/{namespace}/cronjobs/{name}

参数

  • name (路径参数): string, 必需

    CronJob 的名称

  • namespace (路径参数): string, 必需

    namespace

  • dryRun (查询参数): string

    dryRun

  • pretty (查询参数): string

    pretty

响应

200 (CronJob): OK

201 (CronJob): Created

401: Unauthorized

update 替换指定 CronJob 的状态

HTTP 请求

PUT /apis/batch/v1/namespaces/{namespace}/cronjobs/{name}/status

参数

  • name (路径参数): string, 必需

    CronJob 的名称

  • namespace (路径参数): string, 必需

    namespace

  • dryRun (查询参数): string

    dryRun

  • pretty (查询参数): string

    pretty

响应

200 (CronJob): OK

201 (CronJob): Created

401: Unauthorized

patch 部分更新指定的 CronJob

HTTP 请求

PATCH /apis/batch/v1/namespaces/{namespace}/cronjobs/{name}

参数

  • name (路径参数): string, 必需

    CronJob 的名称

  • namespace (路径参数): string, 必需

    namespace

  • dryRun (查询参数): string

    dryRun

  • force (查询参数): boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (CronJob): OK

201 (CronJob): Created

401: Unauthorized

patch 部分更新指定 CronJob 的状态

HTTP 请求

PATCH /apis/batch/v1/namespaces/{namespace}/cronjobs/{name}/status

参数

  • name (路径参数): string, 必需

    CronJob 的名称

  • namespace (路径参数): string, 必需

    namespace

  • dryRun (参数参数): string

    dryRun

  • force (参数参数): boolean

    force

  • pretty (参数参数): string

    pretty

响应

200 (CronJob): OK

201 (CronJob): Created

401: Unauthorized

delete 删除一个 CronJob

HTTP 请求

DELETE /apis/batch/v1/namespaces/{namespace}/cronjobs/{name}

参数

  • name (路径参数): string, 必需

    CronJob 的名称

  • namespace (路径参数): string, 必需

    namespace

  • dryRun (查询参数): string

    dryRun

  • pretty (查询参数): string

    pretty

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除一组 CronJob

HTTP 请求

DELETE /apis/batch/v1/namespaces/{namespace}/cronjobs

参数

  • namespace (路径参数): string, 必需

    namespace

  • dryRun (查询参数): string

    dryRun

  • limit (查询参数): integer

    limit

  • pretty (查询参数): string

    pretty

响应

200 (Status): OK

401: Unauthorized

6.5.1.11 - HorizontalPodAutoscaler

水平 Pod 自动缩放器的配置。

apiVersion: autoscaling/v1

import "k8s.io/api/autoscaling/v1"

HorizontalPodAutoscaler

水平 Pod 自动缩放器的配置。


  • apiVersion: autoscaling/v1

  • kind: HorizontalPodAutoscaler

HorizontalPodAutoscalerSpec

水平 Pod 自动缩放器的规约。


  • maxReplicas (int32),必填

    maxReplicas 是自动扩缩器可以设置的 Pod 数量上限; 不能小于 minReplicas。

  • minReplicas (int32)

    minReplicas 是自动缩放器可以缩减的副本数的下限。 它默认为 1 个 Pod。 如果启用了 alpha 特性门禁 HPAScaleToZero 并且配置了至少一个 Object 或 External 度量标准, 则 minReplicas 允许为 0。 只要至少有一个度量值可用,缩放就处于活动状态。

  • targetCPUUtilizationPercentage (int32)

    targetCPUUtilizationPercentage 是所有 Pod 的目标平均 CPU 利用率(以请求 CPU 的百分比表示); 如果未指定,将使用默认的自动缩放策略。

HorizontalPodAutoscalerStatus

水平 Pod 自动缩放器的当前状态


  • currentReplicas (int32),必填

    currentReplicas 是此自动缩放器管理的 Pod 的当前副本数。

  • desiredReplicas (int32),必填

    desiredReplicas 是此自动缩放器管理的 Pod 副本的所需数量。

  • currentCPUUtilizationPercentage (int32)

    currentCPUUtilizationPercentage 是当前所有 Pod 的平均 CPU 利用率, 以请求 CPU 的百分比表示, 例如:70 表示平均 Pod 现在正在使用其请求 CPU 的 70%。

  • lastScaleTime (Time)

    lastScaleTime 是上次 HorizontalPodAutoscaler 缩放 Pod 的数量; 自动缩放器用它来控制 Pod 数量的更改频率。

    Time 是 time.Time 的包装类,支持正确地序列化为 YAML 和 JSON。 为 time 包提供的许多工厂方法提供了包装类。

  • observedGeneration (int64)

    observedGeneration 是此自动缩放器观察到的最新一代。

HorizontalPodAutoscalerList

水平 Pod 自动缩放器对象列表。


  • apiVersion: autoscaling/v1

  • kind: HorizontalPodAutoscalerList

  • metadata (ListMeta)

    标准的列表元数据。

操作


get 读取特定的 HorizontalPodAutoscaler

HTTP 请求

GET /apis/autoscaling/v1/namespaces/{namespace}/horizontalpodautoscalers/{name}

参数

  • name路径参数): string,必填

    HorizontalPodAutoscaler 的名称。

  • namespace路径参数): string,必填

    namespace

  • pretty查询参数): string

    pretty

响应

200 (HorizontalPodAutoscaler): OK

401: Unauthorized

get 读取特定 HorizontalPodAutoscaler 的状态

HTTP 请求

GET /apis/autoscaling/v1/namespaces/{namespace}/horizontalpodautoscalers/{name}/status

参数

  • name路径参数): string,必填

    HorizontalPodAutoscaler 的名称。

  • namespace路径参数): string,必填

    namespace

  • pretty查询参数): string

    pretty

响应

200 (HorizontalPodAutoscaler): OK

401: Unauthorized

list 列出或监视 HorizontalPodAutoscaler 类别的对象

HTTP 参数

GET /apis/autoscaling/v1/namespaces/{namespace}/horizontalpodautoscalers

参数

  • namespace路径参数): string,必填

    namespace

  • continue查询参数): string

    continue

  • limit查询参数): integer

    limit

  • pretty查询参数): string

    pretty

  • watch查询参数): boolean

    watch

响应

200 (HorizontalPodAutoscalerList): OK

401: Unauthorized

list 列出或监视 HorizontalPodAutoscaler 类别的对象

HTTP 请求

GET /apis/autoscaling/v1/horizontalpodautoscalers

参数

  • continue (查询参数*): string

    continue

  • limit查询参数): integer

    limit

  • pretty查询参数): string

    pretty

  • watch查询参数): boolean

    watch

响应

200 (HorizontalPodAutoscalerList): OK

401: Unauthorized

create 创建一个 HorizontalPodAutoscaler

HTTP 请求

POST /apis/autoscaling/v1/namespaces/{namespace}/horizontalpodautoscalers

参数

  • namespace路径参数): string,必填

    namespace

  • dryRun查询参数): string

    dryRun

  • pretty查询参数): string

    pretty

响应

200 (HorizontalPodAutoscaler): OK

201 (HorizontalPodAutoscaler): Created

202 (HorizontalPodAutoscaler): Accepted

401: Unauthorized

update 替换特定的 HorizontalPodAutoscaler

HTTP 请求

PUT /apis/autoscaling/v1/namespaces/{namespace}/horizontalpodautoscalers/{name}

参数

  • name路径参数): string,必填

    HorizontalPodAutoscaler 的名称

  • namespace路径参数): string,必填

    namespace

  • dryRun查询参数): string

    dryRun

  • pretty查询参数): string

    pretty

响应

200 (HorizontalPodAutoscaler): OK

201 (HorizontalPodAutoscaler): Created

401: Unauthorized

update 替换特定 HorizontalPodAutoscaler 的状态

HTTP 请求

PUT /apis/autoscaling/v1/namespaces/{namespace}/horizontalpodautoscalers/{name}/status

参数

  • name路径参数): string,必填

    HorizontalPodAutoscaler 的名称

  • namespace路径参数): string,必填

    namespace

  • dryRun查询参数): string

    dryRun

  • pretty查询参数): string

    pretty

响应

200 (HorizontalPodAutoscaler): OK

201 (HorizontalPodAutoscaler): Created

401: Unauthorized

patch 部分更新特定的 HorizontalPodAutoscaler

HTTP 请求

PATCH /apis/autoscaling/v1/namespaces/{namespace}/horizontalpodautoscalers/{name}

参数

  • name路径参数): string,必填

    HorizontalPodAutoscaler 的名称

  • namespace路径参数): string,必填

    namespace

  • dryRun查询参数): string

    dryRun

  • force查询参数): boolean

    force

  • pretty查询参数): string

    pretty

响应

200 (HorizontalPodAutoscaler): OK

201 (HorizontalPodAutoscaler): Created

401: Unauthorized

patch 部分更新特定 HorizontalPodAutoscaler 的状态

HTTP 请求

PATCH /apis/autoscaling/v1/namespaces/{namespace}/horizontalpodautoscalers/{name}/status

参数

  • name路径参数): string,必填

    HorizontalPodAutoscaler 的名称

  • namespace路径参数): string,必填

    namespace

  • dryRun查询参数): string

    dryRun

  • force查询参数): boolean

    force

  • pretty查询参数): string

    pretty

响应

200 (HorizontalPodAutoscaler): OK

201 (HorizontalPodAutoscaler): Created

401: Unauthorized

delete 删除一个 HorizontalPodAutoscaler

HTTP 请求

DELETE /apis/autoscaling/v1/namespaces/{namespace}/horizontalpodautoscalers/{name}

参数

  • name路径参数): string,必填

    HorizontalPodAutoscaler 的名称

  • namespace路径参数): string,必填

    namespace

  • dryRun查询参数): string

    dryRun

  • pretty查询参数): string

    pretty

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除 HorizontalPodAutoscaler 的集合

HTTP 请求

DELETE /apis/autoscaling/v1/namespaces/{namespace}/horizontalpodautoscalers

参数

  • namespace路径参数): string,必填

    namespace

  • continue查询参数): string

    continue

  • dryRun查询参数): string

    dryRun

  • limit查询参数): integer

    limit

  • pretty查询参数): string

    pretty

响应

200 (Status): OK

401: Unauthorized

6.5.1.12 - HorizontalPodAutoscaler

HorizontalPodAutoscaler 是水平 Pod 自动扩缩器的配置,它根据指定的指标自动管理实现 scale 子资源的任何资源的副本数。

apiVersion: autoscaling/v2

import "k8s.io/api/autoscaling/v2"

HorizontalPodAutoscaler

HorizontalPodAutoscaler 是水平 Pod 自动扩缩器的配置, 它根据指定的指标自动管理实现 scale 子资源的任何资源的副本数。


HorizontalPodAutoscalerSpec

HorizontalPodAutoscalerSpec 描述了 HorizontalPodAutoscaler 预期的功能。


  • maxReplicas (int32),必需

    maxReplicas 是自动扩缩器可以扩容的副本数的上限。不能小于 minReplicas。

  • scaleTargetRef (CrossVersionObjectReference),必需

    scaleTargetRef 指向要扩缩的目标资源,用于收集 Pod 的相关指标信息以及实际更改的副本数。

    CrossVersionObjectReference 包含足够的信息来让你识别出所引用的资源。

    • scaleTargetRef.name (string),必需

      name 是被引用对象的名称;更多信息:https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names/#names

    • scaleTargetRef.apiVersion (string)

      apiVersion 是被引用对象的 API 版本。

  • minReplicas (int32)

    minReplicas 是自动扩缩器可以缩减的副本数的下限。它默认为 1 个 Pod。 如果启用了 Alpha 特性门控 HPAScaleToZero 并且配置了至少一个 Object 或 External 度量指标, 则 minReplicas 允许为 0。只要至少有一个度量值可用,扩缩就处于活动状态。

  • behavior (HorizontalPodAutoscalerBehavior)

    behavior 配置目标在扩容(Up)和缩容(Down)两个方向的扩缩行为(分别用 scaleUp 和 scaleDown 字段)。 如果未设置,则会使用默认的 HPAScalingRules 进行扩缩容。

    HorizontalPodAutoscalerBehavior 配置目标在扩容(Up)和缩容(Down)两个方向的扩缩行为 (分别用 scaleUp 和 scaleDown 字段)。

    • behavior.scaleDown (HPAScalingRules)

      scaleDown 是缩容策略。如果未设置,则默认值允许缩减到 minReplicas 数量的 Pod, 具有 300 秒的稳定窗口(使用最近 300 秒的最高推荐值)。

      HPAScalingRules 为一个方向配置扩缩行为。在根据 HPA 的指标计算 desiredReplicas 后应用这些规则。 可以通过指定扩缩策略来限制扩缩速度。可以通过指定稳定窗口来防止抖动, 因此不会立即设置副本数,而是选择稳定窗口中最安全的值。

      • behavior.scaleDown.policies ([]HPAScalingPolicy)

        原子性:将在合并时被替换

        policies 是可在扩缩容过程中使用的潜在扩缩策略的列表。必须至少指定一个策略,否则 HPAScalingRules 将被视为无效而丢弃。

        HPAScalingPolicy 是一个单一的策略,它必须在指定的过去时间间隔内保持为 true。

        • behavior.scaleDown.policies.type (string),必需

          type 用于指定扩缩策略。

        • behavior.scaleDown.policies.value (int32),必需

          value 包含策略允许的更改量。它必须大于零。

        • behavior.scaleDown.policies.periodSeconds (int32),必需

          periodSeconds 表示策略应该保持为 true 的时间窗口长度。 periodSeconds 必须大于零且小于或等于 1800(30 分钟)。

      • behavior.scaleDown.selectPolicy (string)

        selectPolicy 用于指定应该使用哪个策略。如果未设置,则使用默认值 Max。

      • behavior.scaleDown.stabilizationWindowSeconds (int32)

        stabilizationWindowSeconds 是在扩缩容时应考虑的之前建议的秒数。stabilizationWindowSeconds 必须大于或等于零且小于或等于 3600(一小时)。如果未设置,则使用默认值:

        • 扩容:0(不设置稳定窗口)。
        • 缩容:300(即稳定窗口为 300 秒)。
    • behavior.scaleUp (HPAScalingRules)

      scaleUp 是用于扩容的扩缩策略。如果未设置,则默认值为以下值中的较高者:

      • 每 60 秒增加不超过 4 个 Pod
      • 每 60 秒 Pod 数量翻倍

      不使用稳定窗口。

      HPAScalingRules 为一个方向配置扩缩行为。在根据 HPA 的指标计算 desiredReplicas 后应用这些规则。 可以通过指定扩缩策略来限制扩缩速度。可以通过指定稳定窗口来防止抖动, 因此不会立即设置副本数,而是选择稳定窗口中最安全的值。

      • behavior.scaleUp.policies ([]HPAScalingPolicy)

        原子性:将在合并时被替换

        policies 是可在扩缩容过程中使用的潜在扩缩策略的列表。必须至少指定一个策略,否则 HPAScalingRules 将被视为无效而丢弃。

        HPAScalingPolicy 是一个单一的策略,它必须在指定的过去时间间隔内保持为 true。

        • behavior.scaleUp.policies.type (string),必需

          type 用于指定扩缩策略。

          - **behavior.scaleUp.policies.value** (int32),必需
        

        value 包含策略允许的更改量。它必须大于零。

        • behavior.scaleUp.policies.periodSeconds (int32),必需

          periodSeconds 表示策略应该保持为 true 的时间窗口长度。 periodSeconds 必须大于零且小于或等于 1800(30 分钟)。

      • behavior.scaleUp.selectPolicy (string)

        selectPolicy 用于指定应该使用哪个策略。如果未设置,则使用默认值 Max。

      • behavior.scaleUp.stabilizationWindowSeconds (int32)

        stabilizationWindowSeconds 是在扩缩容时应考虑的之前建议的秒数。stabilizationWindowSeconds 必须大于或等于零且小于或等于 3600(一小时)。如果未设置,则使用默认值:

        • 扩容:0(不设置稳定窗口)。
        • 缩容:300(即稳定窗口为 300 秒)。
  • metrics ([]MetricSpec)

    原子性:将在合并时被替换

    metrics 包含用于计算预期副本数的规约(将使用所有指标的最大副本数)。 预期副本数是通过将目标值与当前值之间的比率乘以当前 Pod 数来计算的。 因此,使用的指标必须随着 Pod 数量的增加而减少,反之亦然。 有关每种类别的指标必须如何响应的更多信息,请参阅各个指标源类别。 如果未设置,默认指标将设置为 80% 的平均 CPU 利用率。

    MetricSpec 指定如何基于单个指标进行扩缩容(一次只能设置 type 和一个其他匹配字段)

    • metrics.type (string),必需

      type 是指标源的类别。它取值是 “ContainerResource”、“External”、“Object”、“Pods” 或 “Resource” 之一, 每个类别映射到对象中的一个对应的字段。注意:“ContainerResource” 类别在特性门控 HPAContainerMetrics 启用时可用。

    • metrics.containerResource (ContainerResourceMetricSource)

      containerResource 是指 Kubernetes 已知的资源指标(例如在请求和限制中指定的那些), 描述当前扩缩目标中每个 Pod 中的单个容器(例如 CPU 或内存)。 此类指标内置于 Kubernetes 中,在使用 “pods” 源的、按 Pod 计算的普通指标之外,还具有一些特殊的扩缩选项。 这是一个 Alpha 特性,可以通过 HPAContainerMetrics 特性标志启用。

      ContainerResourceMetricSource 指示如何根据请求和限制中指定的 Kubernetes 已知的资源指标进行扩缩容, 此结构描述当前扩缩目标中的每个 Pod(例如 CPU 或内存)。在与目标值比较之前,这些值先计算平均值。 此类指标内置于 Kubernetes 中,并且在使用 “Pods” 源的、按 Pod 统计的普通指标之外支持一些特殊的扩缩选项。 只应设置一种 “target” 类别。

      • metrics.containerResource.container (string),必需

        container 是扩缩目标的 Pod 中容器的名称。

      - **metrics.containerResource.name** (string),必需
      

      name 是相关资源的名称。

      • metrics.containerResource.target (MetricTarget),必需

        target 指定给定指标的目标值。

        MetricTarget 定义特定指标的目标值、平均值或平均利用率

        • metrics.containerResource.target.type (string),必需

          type 表示指标类别是 UtilizationValueAverageValue

        • metrics.containerResource.target.averageUtilization (int32)

          averageUtilization 是跨所有相关 Pod 的资源指标均值的目标值, 表示为 Pod 资源请求值的百分比。目前仅对 “Resource” 指标源类别有效。

        • metrics.containerResource.target.averageValue (Quantity)

          是跨所有相关 Pod 的指标均值的目标值(以数量形式给出)。

        • metrics.containerResource.target.value (Quantity)

          value 是指标的目标值(以数量形式给出)。

    • metrics.external (ExternalMetricSource)

      external 指的是不与任何 Kubernetes 对象关联的全局指标。 这一字段允许基于来自集群外部运行的组件(例如云消息服务中的队列长度,或来自运行在集群外部的负载均衡器的 QPS)的信息进行自动扩缩容。

      ExternalMetricSource 指示如何基于 Kubernetes 对象无关的指标 (例如云消息传递服务中的队列长度,或来自集群外部运行的负载均衡器的 QPS)执行扩缩操作。

      • metrics.external.metric (MetricIdentifier),必需

        metric 通过名称和选择算符识别目标指标。

        MetricIdentifier 定义指标的名称和可选的选择算符

        • metrics.external.metric.name (string),必需

          name 是给定指标的名称。

        • metrics.external.metric.selector (LabelSelector)

          selector 是给定指标的标准 Kubernetes 标签选择算符的字符串编码形式。 设置后,它作为附加参数传递给指标服务器,以获取更具体的指标范围。 未设置时,仅 metricName 参数将用于收集指标。

      • metrics.external.target (MetricTarget),必需

        target 指定给定指标的目标值。

        MetricTarget 定义特定指标的目标值、平均值或平均利用率

        • metrics.external.target.type (string),必需

          type 表示指标类别是 UtilizationValueAverageValue

        • metrics.external.target.averageUtilization (int32)

          averageUtilization 是跨所有相关 Pod 得到的资源指标均值的目标值, 表示为 Pod 资源请求值的百分比。目前仅对 “Resource” 指标源类别有效。

        • metrics.external.target.averageValue (Quantity)

          averageValue 是跨所有相关 Pod 得到的指标均值的目标值(以数量形式给出)。

        • metrics.external.target.value (Quantity)

          value 是指标的目标值(以数量形式给出)。

    • metrics.object (ObjectMetricSource)

      object 是指描述单个 Kubernetes 对象的指标(例如,Ingress 对象上的 hits-per-second)。

      ObjectMetricSource 表示如何根据描述 Kubernetes 对象的指标进行扩缩容(例如,Ingress 对象的 hits-per-second

      • metrics.object.metric (MetricIdentifier),必需

        metric 通过名称和选择算符识别目标指标。

        MetricIdentifier 定义指标的名称和可选的选择算符

        • metrics.object.metric.name (string),必需

          name 是给定指标的名称。

        • metrics.object.metric.selector (LabelSelector)

          selector 是给定指标的标准 Kubernetes 标签选择算符的字符串编码形式。 设置后,它作为附加参数传递给指标服务器,以获取更具体的指标范围。 未设置时,仅 metricName 参数将用于收集指标。

      • metrics.object.target (MetricTarget),必需

        target 表示给定指标的目标值。

        MetricTarget 定义特定指标的目标值、平均值或平均利用率

        • metrics.object.target.type (string),必需

          type 表示指标类别是 UtilizationValueAverageValue

        • metrics.object.target.averageUtilization (int32)

          averageUtilization 是跨所有相关 Pod 得出的资源指标均值的目标值, 表示为 Pod 资源请求值的百分比。目前仅对 “Resource” 指标源类别有效。

        • metrics.object.target.averageValue (Quantity)

          averageValue 是跨所有 Pod 得出的指标均值的目标值(以数量形式给出)。

        • metrics.object.target.value (Quantity)

          value 是指标的目标值(以数量形式给出)。

    • metrics.pods (PodsMetricSource)

      pods 是指描述当前扩缩目标中每个 Pod 的指标(例如,transactions-processed-per-second)。 在与目标值进行比较之前,这些指标值将被平均。

      PodsMetricSource 表示如何根据描述当前扩缩目标中每个 Pod 的指标进行扩缩容(例如,transactions-processed-per-second)。 在与目标值进行比较之前,这些指标值将被平均。

      • metrics.pods.metric (MetricIdentifier),必需

        metric 通过名称和选择算符识别目标指标。

        MetricIdentifier 定义指标的名称和可选的选择算符

        • metrics.pods.metric.name (string),必需

          name 是给定指标的名称。

        • metrics.pods.metric.selector (LabelSelector)

          selector 是给定指标的标准 Kubernetes 标签选择算符的字符串编码形式。 设置后,它作为附加参数传递给指标服务器,以获取更具体的指标范围。 未设置时,仅 metricName 参数将用于收集指标。

      • metrics.pods.target (MetricTarget),必需

        target 表示给定指标的目标值。

        MetricTarget 定义特定指标的目标值、平均值或平均利用率

        • metrics.pods.target.type (string),必需

          type 表示指标类别是 UtilizationValueAverageValue

        • metrics.pods.target.averageUtilization (int32)

          averageUtilization 是跨所有相关 Pod 得出的资源指标均值的目标值, 表示为 Pod 资源请求值的百分比。目前仅对 “Resource” 指标源类别有效。

        • metrics.pods.target.averageValue (Quantity)

          averageValue 是跨所有 Pod 得出的指标均值的目标值(以数量形式给出)。

        • metrics.pods.target.value (Quantity)

          value 是指标的目标值(以数量形式给出)。

    • metrics.resource (ResourceMetricSource)

      resource 是指 Kubernetes 已知的资源指标(例如在请求和限制中指定的那些), 此结构描述当前扩缩目标中的每个 Pod(例如 CPU 或内存)。此类指标内置于 Kubernetes 中, 并且在使用 “Pods” 源的、按 Pod 统计的普通指标之外支持一些特殊的扩缩选项。

      ResourceMetricSource 指示如何根据请求和限制中指定的 Kubernetes 已知的资源指标进行扩缩容, 此结构描述当前扩缩目标中的每个 Pod(例如 CPU 或内存)。在与目标值比较之前,这些指标值将被平均。 此类指标内置于 Kubernetes 中,并且在使用 “Pods” 源的、按 Pod 统计的普通指标之外支持一些特殊的扩缩选项。 只应设置一种 “target” 类别。

      • metrics.resource.name (string),必需

        name 是相关资源的名称。

      • metrics.resource.target (MetricTarget),必需

        target 指定给定指标的目标值。

        MetricTarget 定义特定指标的目标值、平均值或平均利用率

        • metrics.resource.target.type (string),必需

          type 表示指标类别是 UtilizationValueAverageValue

        • metrics.resource.target.averageUtilization (int32)

          averageUtilization 是跨所有相关 Pod 得出的资源指标均值的目标值, 表示为 Pod 资源请求值的百分比。目前仅对 “Resource” 指标源类别有效。

        • metrics.resource.target.averageValue (Quantity)

          averageValue 是跨所有 Pod 得出的指标均值的目标值(以数量形式给出)。

        • metrics.resource.target.value (Quantity)

          value 是指标的目标值(以数量形式给出)。

HorizontalPodAutoscalerStatus

HorizontalPodAutoscalerStatus 描述了水平 Pod 自动扩缩器的当前状态。


  • desiredReplicas (int32),必需

    desiredReplicas 是此自动扩缩器管理的 Pod 的所期望的副本数,由自动扩缩器最后计算。

  • conditions ([]HorizontalPodAutoscalerCondition)

    补丁策略:基于键 type 合并

    Map:合并时将保留 type 键的唯一值

    conditions 是此自动扩缩器扩缩其目标所需的一组条件,并指示是否满足这些条件。

    HorizontalPodAutoscalerCondition 描述 HorizontalPodAutoscaler 在某一时间点的状态。

    • conditions.status (string),必需

      status 是状况的状态(True、False、Unknown)。

    • conditions.type (string),必需

      type 描述当前状况。

    • conditions.lastTransitionTime (Time)

      lastTransitionTime 是状况最近一次从一种状态转换到另一种状态的时间。

      Time 是对 time.Time 的封装。Time 支持对 YAML 和 JSON 进行正确封包。为 time 包的许多函数方法提供了封装器。

    • conditions.message (string)

      message 是一个包含有关转换的可读的详细信息。

    • conditions.reason (string)

      reason 是状况最后一次转换的原因。

  • currentMetrics ([]MetricStatus)

    原子性:将在合并期间被替换

    currentMetrics 是此自动扩缩器使用的指标的最后读取状态。

    MetricStatus 描述了单个指标的最后读取状态。

    • currentMetrics.type (string),必需

      type 是指标源的类别。它取值是 “ContainerResource”、“External”、“Object”、“Pods” 或 “Resource” 之一, 每个类别映射到对象中的一个对应的字段。注意:“ContainerResource” 类别在特性门控 HPAContainerMetrics 启用时可用。

    • currentMetrics.containerResource (ContainerResourceMetricStatus)

      containerResource 是指 Kubernetes 已知的一种资源指标(例如在请求和限制中指定的那些), 描述当前扩缩目标中每个 Pod 中的单个容器(例如 CPU 或内存)。 此类指标内置于 Kubernetes 中,并且在使用 "Pods" 源的、按 Pod 统计的普通指标之外支持一些特殊的扩缩选项。

      ContainerResourceMetricStatus 指示如何根据请求和限制中指定的 Kubernetes 已知的资源指标进行扩缩容, 此结构描述当前扩缩目标中的每个 Pod(例如 CPU 或内存)。此类指标内置于 Kubernetes 中, 并且在使用 “Pods” 源的、按 Pod 统计的普通指标之外支持一些特殊的扩缩选项。

      • currentMetrics.containerResource.container (string),必需

        container 是扩缩目标的 Pod 中的容器名称。

      • currentMetrics.containerResource.current (MetricValueStatus),必需

        current 包含给定指标的当前值。

        MetricValueStatus 保存指标的当前值

        • currentMetrics.containerResource.current.averageUtilization (int32)

          averageUtilization 是跨所有相关 Pod 得出的资源指标均值的当前值,表示为 Pod 资源请求值的百分比。

        • currentMetrics.containerResource.current.averageValue (Quantity)

          averageValue 是跨所有相关 Pod 的指标均值的当前值(以数量形式给出)。

        • currentMetrics.containerResource.current.value (Quantity)

          value 是指标的当前值(以数量形式给出)。

      • currentMetrics.containerResource.name (string),必需

        name 是相关资源的名称。

    • currentMetrics.external (ExternalMetricStatus)

      external 指的是不与任何 Kubernetes 对象关联的全局指标。这一字段允许基于来自集群外部运行的组件 (例如云消息服务中的队列长度,或来自集群外部运行的负载均衡器的 QPS)的信息进行自动扩缩。

      ExternalMetricStatus 表示与任何 Kubernetes 对象无关的全局指标的当前值。

      • currentMetrics.external.current (MetricValueStatus),必需

        current 包含给定指标的当前值。

        MetricValueStatus 保存指标的当前值

        • currentMetrics.external.current.averageUtilization (int32)

          averageUtilization 是跨所有相关 Pod 得出的资源指标均值的当前值,表示为 Pod 资源请求值的百分比。

        • currentMetrics.external.current.averageValue (Quantity)

          averageValue 是跨所有相关 Pod 的指标均值的当前值(以数量形式给出)。

        • currentMetrics.external.current.value (Quantity)

          value 是指标的当前值(以数量形式给出)。

      • currentMetrics.external.metric (MetricIdentifier),必需

        metric 通过名称和选择算符识别目标指标。

        MetricIdentifier 定义指标的名称和可选的选择算符

        • currentMetrics.external.metric.name (string),必需

          name 是给定指标的名称。

        • currentMetrics.external.metric.selector (LabelSelector)

          selector 是给定指标的标准 Kubernetes 标签选择算符的字符串编码形式。 设置后,它作为附加参数传递给指标服务器,以获取更具体的指标范围。 未设置时,仅 metricName 参数将用于收集指标。

    • currentMetrics.object (ObjectMetricStatus)

      object 是指描述单个 Kubernetes 对象的指标(例如,Ingress 对象的 hits-per-second)。

      ObjectMetricStatus 表示描述 Kubernetes 对象的指标的当前值(例如,Ingress 对象的 hits-per-second)。

      • currentMetrics.object.current (MetricValueStatus),必需

        current 包含给定指标的当前值。

        MetricValueStatus 保存指标的当前值

        • currentMetrics.object.current.averageUtilization (int32)

          averageUtilization 是跨所有相关 Pod 得出的资源指标均值的当前值,表示为 Pod 资源请求值的百分比。

        • currentMetrics.object.current.averageValue (Quantity)

          averageValue 是跨所有相关 Pod 的指标均值的当前值(以数量形式给出)。

        • currentMetrics.object.current.value (Quantity)

          value 是指标的当前值(以数量形式给出)。

      • currentMetrics.object.metric (MetricIdentifier),必需

        metric 通过名称和选择算符识别目标指标。

        MetricIdentifier 定义指标的名称和可选的选择算符

        • currentMetrics.object.metric.name (string),必需

          name 是给定指标的名称。

        • currentMetrics.object.metric.selector (LabelSelector)

          selector 是给定指标的标准 Kubernetes 标签选择算符的字符串编码形式。 设置后,它作为附加参数传递给指标服务器,以获取更具体的指标范围。 未设置时,仅 metricName 参数将用于收集指标。

    • currentMetrics.pods (PodsMetricStatus)

      pods 是指描述当前扩缩目标中每个 Pod 的指标(例如,transactions-processed-per-second)。 在与目标值进行比较之前,这些指标值将被平均。

      PodsMetricStatus 表示描述当前扩缩目标中每个 Pod 的指标的当前值(例如,transactions-processed-per-second)。

      • currentMetrics.pods.current (MetricValueStatus),必需

        current 包含给定指标的当前值。

        MetricValueStatus 保存指标的当前值

        • currentMetrics.pods.current.averageUtilization (int32)

          averageUtilization 是跨所有相关 Pod 得出的资源指标均值的当前值,表示为 Pod 资源请求值的百分比。

        • currentMetrics.pods.current.averageValue (Quantity)

          averageValue 是跨所有相关 Pod 的指标均值的当前值(以数量形式给出)。

        • currentMetrics.pods.current.value (Quantity)

          value 是指标的当前值(以数量形式给出)。

      • currentMetrics.pods.metric (MetricIdentifier),必需

        metric 通过名称和选择算符识别目标指标。

        MetricIdentifier 定义指标的名称和可选的选择算符

        • currentMetrics.pods.metric.name (string),必需

          name 是给定指标的名称。

        • currentMetrics.pods.metric.selector (LabelSelector)

          selector 是给定指标的标准 Kubernetes 标签选择算符的字符串编码形式。 设置后,它作为附加参数传递给指标服务器,以获取更具体的指标范围。 未设置时,仅 metricName 参数将用于收集指标。

    • currentMetrics.resource (ResourceMetricStatus)

      resource 是指 Kubernetes 已知的资源指标(例如在请求和限制中指定的那些), 此结构描述当前扩缩目标中的每个 Pod(例如 CPU 或内存)。此类指标内置于 Kubernetes 中, 并且在使用 “Pods” 源的、按 Pod 统计的普通指标之外支持一些特殊的扩缩选项。

      ResourceMetricSource 指示如何根据请求和限制中指定的 Kubernetes 已知的资源指标进行扩缩容, 此结构描述当前扩缩目标中的每个 Pod(例如 CPU 或内存)。在与目标值比较之前,这些指标值将被平均。 此类指标内置于 Kubernetes 中,并且在使用 “Pods” 源的、按 Pod 统计的普通指标之外支持一些特殊的扩缩选项。

      • currentMetrics.resource.current (MetricValueStatus),必需

        current 包含给定指标的当前值。

        MetricValueStatus 保存指标的当前值

        • currentMetrics.resource.current.averageUtilization (int32)

          averageUtilization 是跨所有相关 Pod 得出的资源指标均值的当前值, 表示为 Pod 资源请求值的百分比。

        • currentMetrics.resource.current.averageValue (Quantity)

          averageValue 是跨所有相关 Pod 的指标均值的当前值(以数量形式给出)。

        • currentMetrics.resource.current.value (Quantity)

          value 是指标的当前值(以数量形式给出)。

      • currentMetrics.resource.name (string),必需

        name 是相关资源的名称。

  • currentReplicas (int32)

    currentReplicas 是此自动扩缩器管理的 Pod 的当前副本数,如自动扩缩器最后一次看到的那样。

  • lastScaleTime (Time)

    lastScaleTime 是 HorizontalPodAutoscaler 上次扩缩 Pod 数量的时间,自动扩缩器使用它来控制更改 Pod 数量的频率。

    Time 是对 time.Time 的封装。Time 支持对 YAML 和 JSON 进行正确封包。为 time 包的许多函数方法提供了封装器。

  • observedGeneration (int64)

    observedGeneration 是此自动扩缩器观察到的最新一代。

HorizontalPodAutoscalerList

HorizontalPodAutoscalerList 是水平 Pod 自动扩缩器对象列表。


  • apiVersion: autoscaling/v2

  • kind: HorizontalPodAutoscalerList

  • metadata (ListMeta)

    metadata 是标准的列表元数据。

Operations


get 读取指定的 HorizontalPodAutoscaler

HTTP 请求

GET /apis/autoscaling/v2/namespaces/{namespace}/horizontalpodautoscalers/{name}

参数

  • name路径参数): string,必需

    HorizontalPodAutoscaler 的名称。

  • namespace路径参数): string,必需

    namespace

  • pretty查询参数): string

    pretty

响应

200 (HorizontalPodAutoscaler): OK

401: Unauthorized

get 读取指定 HorizontalPodAutoscaler 的状态

HTTP 请求

GET /apis/autoscaling/v2/namespaces/{namespace}/horizontalpodautoscalers/{name}/status

参数

  • name路径参数): string,必需

    HorizontalPodAutoscaler 的名称。

  • namespace路径参数): string,必需

    namespace

  • pretty查询参数): string

    pretty

响应

200 (HorizontalPodAutoscaler): OK

401: Unauthorized

list 列出或观察 HorizontalPodAutoscaler 类别的对象

HTTP 请求

GET /apis/autoscaling/v2/namespaces/{namespace}/horizontalpodautoscalers

参数

响应

200 (HorizontalPodAutoscalerList): OK

401: Unauthorized

list 列出或观察 HorizontalPodAutoscaler 类别的对象

HTTP 请求

GET /apis/autoscaling/v2/horizontalpodautoscalers

参数

响应

200 (HorizontalPodAutoscalerList): OK

401: Unauthorized

create 创建一个 HorizontalPodAutoscaler

HTTP 请求

POST /apis/autoscaling/v2/namespaces/{namespace}/horizontalpodautoscalers

参数

响应

200 (HorizontalPodAutoscaler): OK

201 (HorizontalPodAutoscaler): Created

202 (HorizontalPodAutoscaler): Accepted

401: Unauthorized

update 替换指定的 HorizontalPodAutoscaler

HTTP 请求

PUT /apis/autoscaling/v2/namespaces/{namespace}/horizontalpodautoscalers/{name}

参数

  • name路径参数): string,必需

    HorizontalPodAutoscaler 的名称。

响应

200 (HorizontalPodAutoscaler): OK

201 (HorizontalPodAutoscaler): Created

401: Unauthorized

update 替换指定 HorizontalPodAutoscaler 的状态

HTTP 请求

PUT /apis/autoscaling/v2/namespaces/{namespace}/horizontalpodautoscalers/{name}/status

参数

  • name路径参数): string,必需

    HorizontalPodAutoscaler 的名称。

响应

200 (HorizontalPodAutoscaler): OK

201 (HorizontalPodAutoscaler): Created

401: Unauthorized

patch 部分更新指定的 HorizontalPodAutoscaler

HTTP 请求

PATCH /apis/autoscaling/v2/namespaces/{namespace}/horizontalpodautoscalers/{name}

参数

  • name路径参数): string,必需

    HorizontalPodAutoscaler 的名称。

  • namespace路径参数): string,必需

    namespace

  • bodyPatch,必需

  • dryRun查询参数): string

    dryRun

  • fieldManager查询参数): string

    fieldManager

  • fieldValidation查询参数): string

    fieldValidation

  • force查询参数): boolean

    force

  • pretty查询参数): string

    pretty

响应

200 (HorizontalPodAutoscaler): OK

201 (HorizontalPodAutoscaler): Created

401: Unauthorized

patch 部分更新指定 HorizontalPodAutoscaler 的状态

HTTP 请求

PATCH /apis/autoscaling/v2/namespaces/{namespace}/horizontalpodautoscalers/{name}/status

参数

  • name路径参数): string,必需

    HorizontalPodAutoscaler 的名称。

  • namespace路径参数): string,必需

    namespace

  • bodyPatch,必需

  • dryRun查询参数): string

    dryRun

  • fieldManager查询参数): string

    fieldManager

  • fieldValidation查询参数): string

    fieldValidation

  • force查询参数): boolean

    force

  • pretty查询参数): string

    pretty

响应

200 (HorizontalPodAutoscaler): OK

201 (HorizontalPodAutoscaler): Created

401: Unauthorized

delete 删除一个 HorizontalPodAutoscaler

HTTP 请求

DELETE /apis/autoscaling/v2/namespaces/{namespace}/horizontalpodautoscalers/{name}

参数

  • name路径参数): string,必需

    HorizontalPodAutoscaler 的名称。

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除 HorizontalPodAutoscaler 的集合

HTTP 请求

DELETE /apis/autoscaling/v2/namespaces/{namespace}/horizontalpodautoscalers

参数

响应

200 (Status): OK

401: Unauthorized

6.5.1.13 - PriorityClass

PriorityClass 定义了从优先级类名到优先级数值的映射。

apiVersion: scheduling.k8s.io/v1

import "k8s.io/api/scheduling/v1"

PriorityClass

PriorityClass 定义了从优先级类名到优先级数值的映射。 该值可以是任何有效的整数。


  • apiVersion: scheduling.k8s.io/v1
  • kind: PriorityClass
  • value (int32),必需

    value 表示此优先级的整数值。这是 Pod 在其 Pod 规约中有此类名称时收到的实际优先级。

  • description (string)

    description 是一个任意字符串,通常提供有关何时应使用此优先级的指南。

  • globalDefault (boolean)

    globalDefault 指定是否应将此 PriorityClass 视为没有任何优先级类的 Pod 的默认优先级。 只有一个 PriorityClass 可以标记为 globalDefault。 但是,如果存在多个 PriorityClasses 且其 globalDefault 字段设置为 true, 则将使用此类全局默认 PriorityClasses 的最小值作为默认优先级。

  • preemptionPolicy (string)

    PreemptionPolicy 是抢占优先级较低的 Pod 的策略。 可选值:Never、PreemptLowerPriority。 如果未设置,则默认为 PreemptLowerPriority。

PriorityClassList

PriorityClassList 是优先级类的集合。


  • apiVersion: scheduling.k8s.io/v1
  • kind: PriorityClassList
  • items ([]PriorityClass),必需

    items 是 PriorityClasses 的列表

操作


get 读取特定的 PriorityClass

HTTP 请求

GET /apis/scheduling.k8s.io/v1/priorityclasses/{name}

参数

  • name路径参数): string,必需

    PriorityClass 名称

  • pretty查询参数):string

    pretty

响应

200 (PriorityClass): OK

401: Unauthorized

list 列出或观察 PriorityClass类的对象

HTTP 请求

GET /apis/scheduling.k8s.io/v1/priorityclasses

参数

  • continue查询参数):string

    continue

  • limit查询参数):integer

    limit

  • pretty查询参数):string

    pretty

  • watch查询参数):boolean

    watch

响应

200 (PriorityClassList): OK

401: Unauthorized

create 创建一个 PriorityClass

HTTP 请求

POST /apis/scheduling.k8s.io/v1/priorityclasses

参数

  • dryRun查询参数):string

    dryRun

  • pretty查询参数):string

    pretty

响应

200 (PriorityClass): OK

201 (PriorityClass): Created

202 (PriorityClass): Accepted

401: Unauthorized

update 替换指定的 PriorityClass

HTTP 请求

PUT /apis/scheduling.k8s.io/v1/priorityclasses/{name}

参数

  • name路径参数): string,必需

    PriorityClass 名称

  • dryRun查询参数):string

    dryRun

  • pretty查询参数):string

    pretty

响应

200 (PriorityClass): OK

201 (PriorityClass): Created

401: Unauthorized

patch 部分更新特定的 PriorityClass

HTTP 请求

PATCH /apis/scheduling.k8s.io/v1/priorityclasses/{name}

参数

  • name路径参数): string,必需

    PriorityClass 名称

  • dryRun查询参数):string

    dryRun

  • force查询参数):boolean

    force

  • pretty查询参数):string

    pretty

响应

200 (PriorityClass): OK

201 (PriorityClass): Created

401: Unauthorized

delete 删除一个 PriorityClass

HTTP 请求

DELETE /apis/scheduling.k8s.io/v1/priorityclasses/{name}

参数

  • name路径参数): string,必需

    PriorityClass 名称。

  • dryRun查询参数):string

    dryRun

  • pretty查询参数):string

    pretty

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除 PriorityClass 集合

HTTP 请求

DELETE /apis/scheduling.k8s.io/v1/priorityclasses

参数

  • continue查询参数):string

    continue

  • dryRun查询参数):string

    dryRun

  • limit查询参数):integer

    limit

  • pretty查询参数):string

    pretty

响应

200 (Status): OK

401: Unauthorized

6.5.1.14 - PodSchedulingContext v1alpha2

PodSchedulingContext 对象包含使用 "WaitForFirstConsumer" 分配模式的 ResourceClaims 来调度 Pod 所需的信息。

apiVersion: resource.k8s.io/v1alpha2

import "k8s.io/api/resource/v1alpha2"

PodSchedulingContext

PodSchedulingContext 对象包含调度某些 Pod 所需要的额外信息,这些 Pod 使用了 “WaitForFirstConsumer” 分配模式的 ResourceClaim。

本功能特性是 Alpha 级别的特性,需要启用 DynamicResourceAllocation 特性门控。


  • apiVersion: resource.k8s.io/v1alpha2

  • kind: PodSchedulingContext

  • metadata (ObjectMeta)

    标准的对象元数据。

PodSchedulingContextSpec

PodSchedulingContextSpec 描述了 Pod 所需要的资源在哪里。


  • potentialNodes ([]string)

    集合:合并期间保留唯一值

    potentialNodes 列出可以运行 Pod 的节点。

    该字段的大小限制为 128。对于许多集群来说,这已经足够大了。 较大的集群可能需要更多的尝试去找到一个适合所有待定资源的节点。 这个限制值可能会在以后提高,但不会降低。

  • selectedNode (string)

    selectedNode 是一个节点,由 Pod 引用的 ResourceClaim 将在此节点上尝试, 且尝试的分配模式是 “WaitForFirstConsumer”。

PodSchedulingContextStatus

PodSchedulingContextStatus 描述 Pod 的资源可以从哪里分配。


  • resourceClaims ([]ResourceClaimSchedulingStatus)

    映射:键 name 的唯一值将在合并过程中保留

    resourceClaims 描述了每个 pod.spec.resourceClaim 条目的资源可用性, 其中对应的 ResourceClaim 使用 “WaitForFirstConsumer” 分配模式。

    ResourceClaimSchedulingStatus 包含关于一个采用 “WaitForFirstConsumer” 分配模式的特定 ResourceClaim 的信息。

    • resourceClaims.name (string)

      name 与 pod.spec.resourceClaims[*].name 字段匹配。

    • resourceClaims.unsuitableNodes ([]string)

      集合:合并期间保留唯一值

      unsuitableNodes 列出 ResourceClaim 无法被分配的节点。

      该字段的大小限制为 128,与 PodSchedulingSpec.PotentialNodes 相同。 这可能会在以后增加,但不会减少。

PodSchedulingContextList

PodSchedulingContextList 是 Pod 调度对象的集合。


  • apiVersion: resource.k8s.io/v1alpha2

  • kind: PodSchedulingContextList

  • metadata (ListMeta)

    标准的列表元数据。

操作


get 读取指定的 PodSchedulingContext

HTTP 请求

GET /apis/resource.k8s.io/v1alpha2/namespaces/{namespace}/podschedulingcontexts/{name}

参数

  • name路径参数):string,必需

    PodSchedulingContext 的名称。

  • namespace路径参数):string,必需

    namespace

  • pretty查询参数):string

    pretty

响应

200 (PodSchedulingContext): OK

401: Unauthorized

get 读取指定 PodSchedulingContext 的状态

HTTP 请求

GET /apis/resource.k8s.io/v1alpha2/namespaces/{namespace}/podschedulingcontexts/{name}/status

参数

  • name路径参数):string,必需

    PodSchedulingContext 的名称。

  • namespace路径参数):string,必需

    namespace

  • pretty查询参数):string

    pretty

响应

200 (PodSchedulingContext): OK

401: Unauthorized

list 列出或监视 PodSchedulingContext 类别的对象

HTTP 请求

GET /apis/resource.k8s.io/v1alpha2/namespaces/{namespace}/podschedulingcontexts

参数

响应

200 (PodSchedulingContextList): OK

401: Unauthorized

list 列出或监视 PodSchedulingContext 类别的对象

HTTP 请求

GET /apis/resource.k8s.io/v1alpha2/podschedulingcontexts

参数

响应

200 (PodSchedulingContextList): OK

401: Unauthorized

create 创建 PodSchedulingContext

HTTP 请求

POST /apis/resource.k8s.io/v1alpha2/namespaces/{namespace}/podschedulingcontexts

参数

响应

200 (PodSchedulingContext): OK

201 (PodSchedulingContext): Created

202 (PodSchedulingContext): Accepted

401: Unauthorized

update 替换指定的 PodSchedulingContext

HTTP 请求

PUT /apis/resource.k8s.io/v1alpha2/namespaces/{namespace}/podschedulingcontexts/{name}

参数

  • name路径参数):string,必需

    PodSchedulingContext 的名称。

  • namespace路径参数):string,必需

    namespace

  • body: PodSchedulingContext,必需

  • dryRun查询参数):string

    dryRun

  • fieldManager查询参数):string

    fieldManager

  • fieldValidation查询参数):string

    fieldValidation

  • pretty查询参数):string

    pretty

响应

200 (PodSchedulingContext): OK

201 (PodSchedulingContext): Created

401: Unauthorized

update 替换指定 PodSchedulingContext 的状态

HTTP 请求

PUT /apis/resource.k8s.io/v1alpha2/namespaces/{namespace}/podschedulingcontexts/{name}/status

参数

  • name路径参数):string,必需

    PodSchedulingContext 的名称。

  • namespace路径参数):string,必需

    namespace

  • body: PodSchedulingContext,必需

  • dryRun查询参数):string

    dryRun

  • fieldManager查询参数):string

    fieldManager

  • fieldValidation查询参数):string

    fieldValidation

  • pretty查询参数):string

    pretty

响应

200 (PodSchedulingContext): OK

201 (PodSchedulingContext): Created

401: Unauthorized

patch 部分更新指定的 PodSchedulingContext

HTTP 请求

PATCH /apis/resource.k8s.io/v1alpha2/namespaces/{namespace}/podschedulingcontexts/{name}

参数

  • name路径参数):string,必需

    PodSchedulingContext 的名称。

  • namespace路径参数):string,必需

    namespace

  • body: Patch,必需

  • dryRun查询参数):string

    dryRun

  • fieldManager查询参数):string

    fieldManager

  • fieldValidation查询参数):string

    fieldValidation

  • force查询参数):boolean

    force

  • pretty查询参数):string

    pretty

响应

200 (PodSchedulingContext): OK

201 (PodSchedulingContext): Created

401: Unauthorized

patch 部分更新指定 PodSchedulingContext 的状态

HTTP 请求

PATCH /apis/resource.k8s.io/v1alpha2/namespaces/{namespace}/podschedulingcontexts/{name}/status

参数

  • name路径参数):string,必需

    PodSchedulingContext 的名称。

  • namespace路径参数):string,必需

    namespace

  • body: Patch,必需

  • dryRun查询参数):string

    dryRun

  • fieldManager查询参数):string

    fieldManager

  • fieldValidation查询参数):string

    fieldValidation

  • force查询参数):boolean

    force

  • pretty查询参数):string

    pretty

响应

200 (PodSchedulingContext): OK

201 (PodSchedulingContext): Created

401: Unauthorized

delete 删除 PodSchedulingContext

HTTP 请求

DELETE /apis/resource.k8s.io/v1alpha2/namespaces/{namespace}/podschedulingcontexts/{name}

参数

  • name路径参数):string,必需

    PodSchedulingContext 的名称。

  • namespace路径参数):string,必需

    namespace

  • body: DeleteOptions

  • dryRun查询参数):string

    dryRun

  • gracePeriodSeconds查询参数):integer

    gracePeriodSeconds

  • pretty查询参数):string

    pretty

  • propagationPolicy查询参数):string

    propagationPolicy

响应

200 (PodSchedulingContext): OK

202 (PodSchedulingContext): Accepted

401: Unauthorized

deletecollection 删除 PodSchedulingContext 的集合

HTTP 请求

DELETE /apis/resource.k8s.io/v1alpha2/namespaces/{namespace}/podschedulingcontexts

参数

响应

200 (Status): OK

401: Unauthorized

6.5.1.15 - ResourceClaim v1alpha2

ResourceClaim 描述资源使用者需要哪些资源。

apiVersion: resource.k8s.io/v1alpha2

import "k8s.io/api/resource/v1alpha2"

ResourceClaim

ResourceClaim 描述资源使用者需要哪些资源。它的状态跟踪资源是否已被分配以及产生的属性是什么。

这是一个 Alpha 级别的资源类型,需要启用 DynamicResourceAllocation 特性门控。


  • apiVersion: resource.k8s.io/v1alpha2

  • kind: ResourceClaim

  • spec (ResourceClaimSpec),必需

    spec 描述了需要被分配的资源所需的属性。它只能在创建 ResourceClaim 时设置一次。

  • status (ResourceClaimStatus)

    status 描述资源是否可用以及具有哪些属性。

ResourceClaimSpec

ResourceClaimSpec 定义资源如何被分配。


  • resourceClassName (string),必需

    resourceClassName 通过部署驱动时创建的 ResourceClass 名称来引用驱动和附加参数。

  • allocationMode (string)

    分配可以立即开始或在 Pod 想要使用资源时开始。"WaitForFirstConsumer" 是默认值。

  • parametersRef (ResourceClaimParametersReference)

    parametersRef 引用一个单独的对象,包含驱动为申领分配资源时将使用的任意参数。

    此对象必须与 ResourceClaim 在同一个名字空间中。

    ResourceClaimParametersReference 包含足够信息,便于你定位 ResourceClaim 的参数。 该对象必须与 ResourceClaim 在相同的名字空间中。

    • parametersRef.kind (string),必需

      kind 是所引用资源的类别。这个值与参数对象元数据中的值相同,例如 "ConfigMap"。

    • parametersRef.name (string),必需

      name 是所引用资源的名称。

    • parametersRef.apiGroup (string)

      apiGroup 是所引用资源的组。对于核心 API 而言此值为空。 字段值与创建资源时所用的 apiVersion 中的组匹配。

ResourceClaimStatus

ResourceClaimStatus 跟踪资源是否已被分配以及产生的属性是什么。


  • allocation (AllocationResult)

    一旦某资源或资源集已被成功分配,资源驱动就会设置 allocation 的值。 如果此项未被设置,则表示资源尚未被分配。

    AllocationResult 包含已分配资源的属性。

    • allocation.availableOnNodes (NodeSelector)

      在资源驱动完成资源分配之后,将设置此字段以通知调度器可以将使用了 ResourceClaim 的 Pod 调度到哪里。

      设置此字段是可选的。如果字段值为空,表示资源可以在任何地方访问。

      节点选择算符表示对一组节点执行一个或多个标签查询的结果的并集; 也就是说,它表示由节点选择算符条件表示的选择算符的逻辑或计算结果。

      • allocation.availableOnNodes.nodeSelectorTerms ([]NodeSelectorTerm),必需

        必需。节点选择算符条件的列表。这些条件以逻辑或进行计算。

        一个 null 或空的节点选择算符条件不会与任何对象匹配。条件中的要求会按逻辑与的关系来计算。 TopologySelectorTerm 类别实现了 NodeSelectorTerm 的子集。

        • allocation.availableOnNodes.nodeSelectorTerms.matchExpressions ([]NodeSelectorRequirement)

          基于节点标签所设置的节点选择算符要求的列表。

        • allocation.availableOnNodes.nodeSelectorTerms.matchFields ([]NodeSelectorRequirement)

          基于节点字段所设置的节点选择算符要求的列表。

    • allocation.resourceHandles ([]ResourceHandle)

      原子性:将在合并期间被替换

      resourceHandles 包含应在申领的整个生命期中保持的、与某资源分配所关联的状态。 每个 resourceHandle 包含应向特定 kubelet 插件传递的数据, 一旦资源落到某具体节点上,这些数据就会被传递给该插件。 此数据将在成功分配后由驱动返回,并对 Kubernetes 不透明。 必要时驱动文档可能会向用户阐述如何解读这些数据。

      设置此字段是可选的。它最大可以有 32 个条目。如果为 null(或为空), 则假定此分配将由某个确定的 kubelet 插件处理, 不会附加 resourceHandle 数据。所调用的 kubelet 插件的名称将与嵌入此 AllocationResult 的 ResourceClaimStatus 中设置的 driverName 匹配。

      resourceHandle 保存不透明的资源数据,以供特定的 kubelet 插件处理。

      • allocation.resourceHandles.data (string)

        data 包含与此 resourceHandle 关联的不透明数据。 data 由资源驱动中的控制器组件设置,该驱动的名字与嵌入此 resourceHandle 的 ResourceClaimStatus 中设置的 driverName 相同。 data 在分配时进行设置,供 kubelet 插件处理;所指的插件的名称与此 resourceHandle 所设置的 driverName 相同。

        该字段的最大值为 16KiB。此值在未来可能会增加,但不会减少。

      • allocation.resourceHandles.driverName (string)

        driverName 指定资源驱动的名称;一旦 resourceHandle 落到某具体节点, 就应调用该驱动对应的 kubelet 插件来处理此数据。 字段值可能与嵌入此 resourceHandle 的 ResourceClaimStatus 中设置的 driverName 不同。

    • allocation.shareable (boolean)

      shareable 确定资源是否同时支持多个使用者。

  • deallocationRequested (boolean)

    deallocationRequested 表示某 ResourceClaim 将被取消分配。

    出现请求后,驱动必须释放此申领,重置此字段并清除 allocation 字段。

    在 deallocationRequested 被设置时,不能将新的使用者添加到 reservedFor。

  • driverName (string)

    driverName 是在确定了分配之后,从 ResourceClass 复制而来的驱动名称。

  • reservedFor ([]ResourceClaimConsumerReference)

    Map:合并期间根据键 uid 保留不重复的值

    reservedFor 标明目前哪些实体允许使用申领。如果引用未为某个 Pod 预留的 ResourceClaim,则该 Pod 将不会启动。

    最多可以有 32 个这样的预留。这一限制可能会在未来放宽,但不会减少。

    ResourceClaimConsumerReference 包含足够的信息以便定位 ResourceClaim 的使用者。 用户必须是与 ResourceClaim 在同一名字空间中的资源。

    • reservedFor.name (string),必需

      name 是所引用资源的名称。

    • reservedFor.resource (string),必需

      resource 是所引用资源的类别,例如 "pods"。

    • reservedFor.uid (string),必需

      uid 用于唯一标识资源的某实例。

    • reservedFor.apiGroup (string)

      apiGroup 是所引用资源的组。对于核心 API 而言此值为空。 字段值与创建资源时所用的 apiVersion 中的组匹配。

ResourceClaimList

ResourceClaimList 是申领的集合。


  • apiVersion: resource.k8s.io/v1alpha2

  • kind: ResourceClaimList

  • metadata (ListMeta)

    标准的列表元数据。

  • items ([]ResourceClaim),必需

    items 是资源申领的列表。

操作


get 读取指定的 ResourceClaim

HTTP 请求

GET /apis/resource.k8s.io/v1alpha2/namespaces/{namespace}/resourceclaims/{name}

参数

  • name路径参数):string,必需

    ResourceClaim 的名称。

  • namespace路径参数):string,必需

    namespace

  • pretty查询参数):string

    pretty

响应

200 (ResourceClaim): OK

401: Unauthorized

get 读取指定 ResourceClaim 的状态

HTTP 请求

GET /apis/resource.k8s.io/v1alpha2/namespaces/{namespace}/resourceclaims/{name}/status

参数

  • name路径参数):string,必需

    ResourceClaim 的名称。

  • namespace路径参数):string,必需

    namespace

  • pretty查询参数):string

    pretty

响应

200 (ResourceClaim): OK

401: Unauthorized

list 列出或监视 ResourceClaim 类别的对象

HTTP 请求

GET /apis/resource.k8s.io/v1alpha2/namespaces/{namespace}/resourceclaims

参数

响应

200 (ResourceClaimList): OK

401: Unauthorized

list 列出或监视 ResourceClaim 类别的对象

HTTP 请求

GET /apis/resource.k8s.io/v1alpha2/resourceclaims

参数

响应

200 (ResourceClaimList): OK

401: Unauthorized

create 创建 ResourceClaim

HTTP 请求

POST /apis/resource.k8s.io/v1alpha2/namespaces/{namespace}/resourceclaims

参数

响应

200 (ResourceClaim): OK

201 (ResourceClaim): Created

202 (ResourceClaim): Accepted

401: Unauthorized

update 替换指定的 ResourceClaim

HTTP 请求

PUT /apis/resource.k8s.io/v1alpha2/namespaces/{namespace}/resourceclaims/{name}

参数

  • name路径参数):string,必需

    ResourceClaim 的名称。

  • namespace路径参数):string,必需

    namespace

  • body: ResourceClaim,必需

  • dryRun查询参数):string

    dryRun

  • fieldManager查询参数):string

    fieldManager

  • fieldValidation查询参数):string

    fieldValidation

  • pretty查询参数):string

    pretty

响应

200 (ResourceClaim): OK

201 (ResourceClaim): Created

401: Unauthorized

update 替换指定 ResourceClaim 的状态

HTTP 请求

PUT /apis/resource.k8s.io/v1alpha2/namespaces/{namespace}/resourceclaims/{name}/status

参数

  • name路径参数):string,必需

    ResourceClaim 的名称。

  • namespace路径参数):string,必需

    namespace

  • body: ResourceClaim,必需

  • dryRun查询参数):string

    dryRun

  • fieldManager查询参数):string

    fieldManager

  • fieldValidation查询参数):string

    fieldValidation

  • pretty查询参数):string

    pretty

响应

200 (ResourceClaim): OK

201 (ResourceClaim): Created

401: Unauthorized

patch 部分更新指定的 ResourceClaim

HTTP 请求

PATCH /apis/resource.k8s.io/v1alpha2/namespaces/{namespace}/resourceclaims/{name}

参数

  • name路径参数):string,必需

    ResourceClaim 的名称。

  • namespace路径参数):string,必需

    namespace

  • body: Patch,必需

  • dryRun查询参数):string

    dryRun

  • fieldManager查询参数):string

    fieldManager

  • fieldValidation查询参数):string

    fieldValidation

  • force查询参数):boolean

    force

  • pretty查询参数):string

    pretty

响应

200 (ResourceClaim): OK

201 (ResourceClaim): Created

401: Unauthorized

patch 部分更新指定 ResourceClaim 的状态

HTTP 请求

PATCH /apis/resource.k8s.io/v1alpha2/namespaces/{namespace}/resourceclaims/{name}/status

参数

  • name路径参数):string,必需

    ResourceClaim 的名称。

  • namespace路径参数):string,必需

    namespace

  • body: Patch,必需

  • dryRun查询参数):string

    dryRun

  • fieldManager查询参数):string

    fieldManager

  • fieldValidation查询参数):string

    fieldValidation

  • force查询参数):boolean

    force

  • pretty查询参数):string

    pretty

响应

200 (ResourceClaim): OK

201 (ResourceClaim): Created

401: Unauthorized

delete 删除 ResourceClaim

HTTP 请求

DELETE /apis/resource.k8s.io/v1alpha2/namespaces/{namespace}/resourceclaims/{name}

参数

响应

200 (ResourceClaim): OK

202 (ResourceClaim): Accepted

401: Unauthorized

deletecollection 删除 ResourceClaim 的集合

HTTP 请求

DELETE /apis/resource.k8s.io/v1alpha2/namespaces/{namespace}/resourceclaims

参数

响应

200 (Status): OK

401: Unauthorized

6.5.1.16 - ResourceClaimTemplate v1alpha2

ResourceClaimTemplate 用于生成 ResourceClaim 对象。

apiVersion: resource.k8s.io/v1alpha2

import "k8s.io/api/resource/v1alpha2"

ResourceClaimTemplate

ResourceClaimTemplate 用于生成 ResourceClaim 对象。


  • apiVersion: resource.k8s.io/v1alpha2

  • kind: ResourceClaimTemplate

  • metadata (ObjectMeta)

    标准的对象元数据。

  • spec (ResourceClaimTemplateSpec), 必需

    描述要生成的 ResourceClaim。

    该字段是不可变的。当需要时,控制平面将为 Pod 创建一个 ResourceClaim,然后不再对其进行更新。

ResourceClaimTemplateSpec

ResourceClaimTemplateSpec 包含针对 ResourceClaim 的元数据和字段。


spec* (ResourceClaimSpec), 必需

ResourceClaim 的规约。整个内容将不加修改地复制到从模板创建的 ResourceClaim 中。 与 ResourceClaim 中相同的字段在此处也是有效的。

  • metadata (ObjectMeta)

    ObjectMeta 可以包含创建 PVC 时将要复制到其中的标签和注解。 不允许设置其他字段,并且即便设置了也会在验证期间被拒绝。

ResourceClaimTemplateList

ResourceClaimTemplateList 是申领模板的集合。


  • apiVersion: resource.k8s.io/v1alpha2

  • kind: ResourceClaimTemplateList

  • metadata (ListMeta)

    标准的列表元数据。

操作


get 读取指定的 ResourceClaimTemplate

HTTP 请求

GET /apis/resource.k8s.io/v1alpha2/namespaces/{namespace}/resourceclaimtemplates/{name}

参数

  • name (路径参数): string, 必需

    name of the ResourceClaimTemplate

  • namespace (路径参数): string, 必需

    namespace

  • pretty (查询参数): string

    pretty

响应

200 (ResourceClaimTemplate): OK

401: Unauthorized

list 列出或监视 ResourceClaimTemplate 类别的对象

HTTP 请求

GET /apis/resource.k8s.io/v1alpha2/namespaces/{namespace}/resourceclaimtemplates

参数

响应

200 (ResourceClaimTemplateList): OK

401: Unauthorized

list 列出或监视 ResourceClaimTemplate 类别的对象

HTTP 请求

GET /apis/resource.k8s.io/v1alpha2/resourceclaimtemplates

参数

响应

200 (ResourceClaimTemplateList): OK

401: Unauthorized

create 创建 ResourceClaimTemplate

HTTP 请求

POST /apis/resource.k8s.io/v1alpha2/namespaces/{namespace}/resourceclaimtemplates

参数

响应

200 (ResourceClaimTemplate): OK

201 (ResourceClaimTemplate): Created

202 (ResourceClaimTemplate): Accepted

401: Unauthorized

update 替换指定的 ResourceClaimTemplate

HTTP 请求

PUT /apis/resource.k8s.io/v1alpha2/namespaces/{namespace}/resourceclaimtemplates/{name}

参数

响应

200 (ResourceClaimTemplate): OK

201 (ResourceClaimTemplate): Created

401: Unauthorized

patch 部分更新指定的 ResourceClaimTemplate

HTTP 请求

PATCH /apis/resource.k8s.io/v1alpha2/namespaces/{namespace}/resourceclaimtemplates/{name}

参数

  • name (路径参数): string, 必需

    name of the ResourceClaimTemplate

  • namespace (路径参数): string, 必需

    namespace

  • body: Patch, 必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • force (查询参数): boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (ResourceClaimTemplate): OK

201 (ResourceClaimTemplate): Created

401: Unauthorized

delete 删除 ResourceClaimTemplate

HTTP 请求

DELETE /apis/resource.k8s.io/v1alpha2/namespaces/{namespace}/resourceclaimtemplates/{name}

参数

响应

200 (ResourceClaimTemplate): OK

202 (ResourceClaimTemplate): Accepted

401: Unauthorized

deletecollection 删除 ResourceClaimTemplate 的集合

HTTP 请求

DELETE /apis/resource.k8s.io/v1alpha2/namespaces/{namespace}/resourceclaimtemplates

参数

响应

200 (Status): OK

401: Unauthorized

6.5.1.17 - ResourceClass v1alpha2

ResourceClass 由管理员用于影响资源被分配的方式。

apiVersion: resource.k8s.io/v1alpha2

import "k8s.io/api/resource/v1alpha2"

ResourceClass

ResourceClass 由管理员用于影响资源被分配的方式。

这是一个 Alpha 级别的资源类型,需要启用 DynamicResourceAllocation 特性门控。


  • apiVersion: resource.k8s.io/v1alpha2

  • kind: ResourceClass

  • driverName (string),必需

    driverName 定义动态资源驱动的名称,该驱动用于分配使用此类的 ResourceClaim。

    资源驱动具有唯一的名称,以正向的域名顺序(acme.example.com)给出。

  • parametersRef (ResourceClassParametersReference)

    parametersRef 引用任意一个独立的对象,其中可以包含驱动在分配使用此类的资源时所用的参数。 动态资源驱动可以区分存储在此处的参数和存储在 ResourceClaimSpec 中的参数。

    ResourceClassParametersReference 包含了足够的信息,让你可以定位针对 ResourceClass 的参数。

    • parametersRef.kind (string),必需

      kind 是所引用资源的类别。这个值与参数对象元数据中的值相同。

    • parametersRef.name (string),必需

      name 是所引用资源的名称。

    • parametersRef.apiGroup (string)

      apiGroup 是所引用资源的组。对于核心 API 而言此值为空。这与创建资源时所用的 apiVersion 中的组匹配。

    • parametersRef.namespace (string)

      包含被引用的资源的名字空间。对于集群作用域的资源必须为空,对于名字空间作用域的资源必须不为空。

  • suitableNodes (NodeSelector)

    当 Pod 使用还未分配的 ResourceClaim 时,调度程序在尝试查找适合 Pod 的节点时, 将仅考虑与选择算符匹配的节点。

    设置此字段是可选的,如果设置为 null,则所有节点都是候选者。

    节点选择算符表示针对一组节点执行一个或多个标签查询的结果的并集; 也就是说,它表示由节点选择算符条件表示的选择算符的逻辑或计算结果。

    • suitableNodes.nodeSelectorTerms ([]NodeSelectorTerm),必需

      必需。节点选择算符条件的列表。这些条件会按逻辑或的关系来计算。

      Null 或空的节点选择算符条件不会与任何对象匹配。这些条件会按逻辑与的关系来计算。 TopologySelectorTerm 类别实现了 NodeSelectorTerm 的子集。

      • suitableNodes.nodeSelectorTerms.matchExpressions ([]NodeSelectorRequirement)

        基于节点标签所设置的节点选择算符要求的列表。

      • suitableNodes.nodeSelectorTerms.matchFields ([]NodeSelectorRequirement)

        基于节点字段所设置的节点选择算符要求的列表。

ResourceClassList

ResourceClassList 是资源类的集合。


  • apiVersion: resource.k8s.io/v1alpha2

  • kind: ResourceClassList

  • metadata (ListMeta)

    标准的列表元数据。

  • items ([]ResourceClass),必需

    items 是资源类的列表。

操作


get 读取指定的 ResourceClass

HTTP 请求

GET /apis/resource.k8s.io/v1alpha2/resourceclasses/{name}

参数

  • name路径参数):string,必需

    ResourceClass 的名称。

  • pretty查询参数):string

    pretty

响应

200 (ResourceClass): OK

401: Unauthorized

list 列出或监视 ResourceClass 类别的对象

HTTP 请求

GET /apis/resource.k8s.io/v1alpha2/resourceclasses

参数

响应

200 (ResourceClassList): OK

401: Unauthorized

create 创建 ResourceClass

HTTP 请求

POST /apis/resource.k8s.io/v1alpha2/resourceclasses

参数

响应

200 (ResourceClass): OK

201 (ResourceClass): Created

202 (ResourceClass): Accepted

401: Unauthorized

update 替换指定的 ResourceClass

HTTP 请求

PUT /apis/resource.k8s.io/v1alpha2/resourceclasses/{name}

参数

  • name路径参数):string,必需

    ResourceClass 的名称。

  • body: ResourceClass,必需

  • dryRun查询参数):string

    dryRun

  • fieldManager查询参数):string

    fieldManager

  • fieldValidation查询参数):string

    fieldValidation

  • pretty查询参数):string

    pretty

响应

200 (ResourceClass): OK

201 (ResourceClass): Created

401: Unauthorized

patch 部分更新指定的 ResourceClass

HTTP 请求

PATCH /apis/resource.k8s.io/v1alpha2/resourceclasses/{name}

参数

  • name路径参数):string,必需

    ResourceClass 的名称。

  • body: Patch,必需

  • dryRun查询参数):string

    dryRun

  • fieldManager查询参数):string

    fieldManager

  • fieldValidation查询参数):string

    fieldValidation

  • force查询参数):boolean

    force

  • pretty查询参数):string

    pretty

响应

200 (ResourceClass): OK

201 (ResourceClass): Created

401: Unauthorized

delete 删除 ResourceClass

HTTP 请求

DELETE /apis/resource.k8s.io/v1alpha2/resourceclasses/{name}

参数

响应

200 (ResourceClass): OK

202 (ResourceClass): Accepted

401: Unauthorized

deletecollection 删除 ResourceClass 的集合

HTTP 请求

DELETE /apis/resource.k8s.io/v1alpha2/resourceclasses

参数

响应

200 (Status): OK

401: Unauthorized

6.5.2 - Service 资源

6.5.2.1 - Service

Service 是软件服务(例如 mysql)的命名抽象,包含代理要侦听的本地端口(例如 3306)和一个选择算符,选择算符用来确定哪些 Pod 将响应通过代理发送的请求。

apiVersion: v1

import "k8s.io/api/core/v1”

Service

Service 是软件服务(例如 mysql)的命名抽象,包含代理要侦听的本地端口(例如 3306)和一个选择算符, 选择算符用来确定哪些 Pod 将响应通过代理发送的请求。


ServiceSpec

ServiceSpec 描述用户在服务上创建的属性。


  • selector (map[string]string)

    将 Service 流量路由到具有与此 selector 匹配的标签键值对的 Pod。 如果为空或不存在,则假定该服务有一个外部进程管理其端点,Kubernetes 不会修改该端点。 仅适用于 ClusterIP、NodePort 和 LoadBalancer 类型。如果类型为 ExternalName,则忽略。 更多信息: https://kubernetes.io/docs/concepts/services-networking/service/

  • ports ([]ServicePort)

    Patch strategy:基于键 type 合并

    Map:合并时将保留 type 键的唯一值

    此 Service 公开的端口列表。 更多信息: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies

    ServicePort 包含有关 ServicePort 的信息。

    • ports.port (int32),必需

      Service 将公开的端口。

    • ports.targetPort (IntOrString)

      在 Service 所针对的 Pod 上要访问的端口号或名称。 编号必须在 1 到 65535 的范围内。名称必须是 IANA_SVC_NAME。 如果此值是一个字符串,将在目标 Pod 的容器端口中作为命名端口进行查找。 如果未指定字段,则使用 port 字段的值(直接映射)。 对于 clusterIP 为 None 的服务,此字段将被忽略, 应忽略不设或设置为 port 字段的取值。 更多信息: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service

      IntOrString 是一种可以保存 int32 或字符串的类型。 在 JSON 或 YAML 编组和解组中使用时,它会生成或使用内部类型。 例如,这允许您拥有一个可以接受名称或数字的 JSON 字段。

    • ports.protocol (string)

      此端口的 IP 协议。支持 “TCP”、“UDP” 和 “SCTP”。默认为 TCP。

    • ports.name (string)

      Service 中此端口的名称。这必须是 DNS_LABEL。 ServiceSpec 中的所有端口的名称都必须唯一。 在考虑 Service 的端点时,这一字段值必须与 EndpointPort 中的 name 字段相同。 如果此服务上仅定义一个 ServicePort,则为此字段为可选。

    • ports.nodePort (int32)

      当类型为 NodePort 或 LoadBalancer 时,Service 公开在节点上的端口, 通常由系统分配。如果指定了一个在范围内且未使用的值,则将使用该值,否则操作将失败。 如果在创建的 Service 需要该端口时未指定该字段,则会分配端口。 如果在创建不需要该端口的 Service时指定了该字段,则会创建失败。 当更新 Service 时,如果不再需要此字段(例如,将类型从 NodePort 更改为 ClusterIP),这个字段将被擦除。 更多信息: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport

    • ports.appProtocol (string)

      此端口的应用协议,用作实现的提示,为他们理解的协议提供更丰富的行为。此字段遵循标准
      

      Kubernetes 标签语法,有效值包括:

        * 无前缀协议名称 - 保留用于 IANA 标准服务名称(根据 RFC-6335 和 https://www.iana.org/assignments/service-names)。
      
  • type (string)

    type 确定 Service 的公开方式。默认为 ClusterIP。 有效选项为 ExternalName、ClusterIP、NodePort 和 LoadBalancer。 ClusterIP 为端点分配一个集群内部 IP 地址用于负载均衡。 Endpoints 由 selector 确定,如果未设置 selector,则需要通过手动构造 Endpoints 或 EndpointSlice 的对象来确定。 如果 clusterIP 为 None,则不分配虚拟 IP,并且 Endpoints 作为一组端点而不是虚拟 IP 发布。 NodePort 建立在 ClusterIP 之上,并在每个节点上分配一个端口,该端口路由到与 clusterIP 相同的 Endpoints。 LoadBalancer 基于 NodePort 构建并创建一个外部负载均衡器(如果当前云支持),该负载均衡器路由到与 clusterIP 相同的 Endpoints。 externalName 将此 Service 别名为指定的 externalName。其他几个字段不适用于 ExternalName Service。 更多信息: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types

  • ipFamilies ([]string)

    原子: 将在合并期间被替换

    iPFamilies 是分配给此服务的 IP 协议(例如 IPv4、IPv6)的列表。 该字段通常根据集群配置和 ipFamilyPolicy 字段自动设置。 如果手动指定该字段,且请求的协议在集群中可用,且 ipFamilyPolicy 允许,则使用;否则服务创建将失败。 该字段修改是有条件的:它允许添加或删除辅助 IP 协议,但不允许更改服务的主要 IP 协议。 有效值为 “IPv4” 和 “IPv6”。 该字段仅适用于 ClusterIP、NodePort 和 LoadBalancer 类型的服务,并且确实可用于“无头”服务。 更新服务设置类型为 ExternalName 时,该字段将被擦除。

    该字段最多可以包含两个条目(双栈系列,按任意顺序)。 如果指定,这些协议栈必须对应于 clusterIPs 字段的值。 clusterIP 和 ipFamilies 都由 ipFamilyPolicy 字段管理。

  • ipFamilyPolicy (string)

    iPFamilyPolicy 表示此服务请求或要求的双栈特性。 如果没有提供值,则此字段将被设置为 SingleStack。 服务可以是 “SingleStack”(单个 IP 协议)、 “PreferDualStack”(双栈配置集群上的两个 IP 协议或单栈集群上的单个 IP 协议) 或 “RequireDualStack”(双栈上的两个 IP 协议配置的集群,否则失败)。 ipFamilies 和 clusterIPs 字段取决于此字段的值。 更新服务设置类型为 ExternalName 时,此字段将被擦除。

  • clusterIP (string)

    clusterIP 是服务的 IP 地址,通常是随机分配的。 如果地址是手动指定的,在范围内(根据系统配置),且没有被使用,它将被分配给服务,否则创建服务将失败。 clusterIP 一般不会被更改,除非 type 被更改为 ExternalName (ExternalName 需要 clusterIP 为空)或 type 已经是 ExternalName 时,可以更改 clusterIP(在这种情况下,可以选择指定此字段)。 可选值 “None”、空字符串 (“”) 或有效的 IP 地址。 clusterIP 为 “None” 时会生成“无头服务”(无虚拟 IP),这在首选直接 Endpoint 连接且不需要代理时很有用。 仅适用于 ClusterIP、NodePort、和 LoadBalancer 类型的服务。 如果在创建 ExternalName 类型的 Service 时指定了 clusterIP,则创建将失败。 更新 Service type 为 ExternalName 时,clusterIP 会被移除。 更多信息: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies

  • clusterIPs ([]string)

    原子: 将在合并期间被替换

    clusterIPs 是分配给该 Service 的 IP 地址列表,通常是随机分配的。 如果地址是手动指定的,在范围内(根据系统配置),且没有被使用,它将被分配给 Service;否则创建 Service 失败。 clusterIP 一般不会被更改,除非 type 被更改为 ExternalName (ExternalName 需要 clusterIPs 为空)或 type 已经是 ExternalName 时,可以更改 clusterIPs(在这种情况下,可以选择指定此字段)。 可选值 “None”、空字符串 (“”) 或有效的 IP 地址。 clusterIPs 为 “None” 时会生成“无头服务”(无虚拟 IP),这在首选直接 Endpoint 连接且不需要代理时很有用。 适用于 ClusterIP、NodePort、和 LoadBalancer 类型的服务。 如果在创建 ExternalName 类型的 Service 时指定了 clusterIPs,则会创建失败。 更新 Service type 为 ExternalName 时,该字段将被移除。如果未指定此字段,则将从 clusterIP 字段初始化。 如果指定 clusterIPs,客户端必须确保 clusterIPs[0] 和 clusterIP 一致。

    clusterIPs 最多可包含两个条目(双栈系列,按任意顺序)。 这些 IP 必须与 ipFamilies 的值相对应。 clusterIP 和 ipFamilies 都由 ipFamilyPolicy 管理。 更多信息: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies

  • externalIPs ([]string)

    externalIPs 是一个 IP 列表,集群中的节点会为此 Service 接收针对这些 IP 地址的流量。 这些 IP 不被 Kubernetes 管理。用户需要确保流量可以到达具有此 IP 的节点。 一个常见的例子是不属于 Kubernetes 系统的外部负载均衡器。

  • sessionAffinity (string)

    支持 “ClientIP” 和 “None”。用于维护会话亲和性。 启用基于客户端 IP 的会话亲和性。必须是 ClientIP 或 None。默认为 None。 更多信息: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies

  • loadBalancerIP (string)

    仅适用于服务类型:LoadBalancer。此功能取决于底层云提供商是否支持负载均衡器。 如果云提供商不支持该功能,该字段将被忽略。 已弃用:该字段信息不足,且其含义因实现而异。此字段是不可移植的,并且可能不支持双栈。。 我们鼓励用户在可用时使用特定于实现的注解。

  • loadBalancerSourceRanges ([]string)

    如果设置了此字段并且被平台支持,将限制通过云厂商的负载均衡器的流量到指定的客户端 IP。 如果云提供商不支持该功能,该字段将被忽略。 更多信息: https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/

  • loadBalancerClass (string)

    loadBalancerClass 是此 Service 所属的负载均衡器实现的类。 如果设置了此字段,则字段值必须是标签风格的标识符,带有可选前缀,例如 ”internal-vip” 或 “example.com/internal-vip”。 无前缀名称是为最终用户保留的。该字段只能在 Service 类型为 “LoadBalancer” 时设置。 如果未设置此字段,则使用默认负载均衡器实现。默认负载均衡器现在通常通过云提供商集成完成,但应适用于任何默认实现。 如果设置了此字段,则假定负载均衡器实现正在监测具有对应负载均衡器类的 Service。 任何默认负载均衡器实现(例如云提供商)都应忽略设置此字段的 Service。 只有在创建或更新的 Service 的 type 为 “LoadBalancer” 时,才可设置此字段。 一经设定,不可更改。当 Service 的 type 更新为 “LoadBalancer” 之外的其他类型时,此字段将被移除。

  • externalName (string)

    externalName 是发现机制将返回的外部引用,作为此服务的别名(例如 DNS CNAME 记录)。 不涉及代理。必须是小写的 RFC-1123 主机名 (https://tools.ietf.org/html/rfc1123), 并且要求 typeExternalName

  • externalTrafficPolicy (string)

    externalTrafficPolicy 描述了节点如何分发它们在 Service 的“外部访问”地址 (NodePort、ExternalIP 和 LoadBalancer IP)接收到的服务流量。 如果设置为 “Local”,代理将以一种假设外部负载均衡器将负责在节点之间服务流量负载均衡, 因此每个节点将仅向服务的节点本地端点传递流量,而不会伪装客户端源 IP。 (将丢弃错误发送到没有端点的节点的流量。) “Cluster” 默认值使用负载均衡路由到所有端点的策略(可能会根据拓扑和其他特性进行修改)。 请注意,从集群内部发送到 External IP 或 LoadBalancer IP 的流量始终具有 “Cluster” 语义, 但是从集群内部发送到 NodePort 的客户端需要在选择节点时考虑流量路由策略。

  • internalTrafficPolicy (string)

    InternalTrafficPolicy 描述节点如何分发它们在 ClusterIP 上接收到的服务流量。 如果设置为 “Local”,代理将假定 Pod 只想与在同一节点上的服务端点通信,如果没有本地端点,它将丢弃流量。 “Cluster” 默认将流量路由到所有端点(可能会根据拓扑和其他特性进行修改)。

  • healthCheckNodePort (int32)

    healthCheckNodePort 指定 Service 的健康检查节点端口。 仅适用于 type 为 LoadBalancer 且 externalTrafficPolicy 设置为 Local 的情况。 如果为此字段设定了一个值,该值在合法范围内且没有被使用,则使用所指定的值。 如果未设置此字段,则自动分配字段值。外部系统(例如负载平衡器)可以使用此端口来确定给定节点是否拥有此服务的端点。 在创建不需要 healthCheckNodePort 的 Service 时指定了此字段,则 Service 创建会失败。 要移除 healthCheckNodePort,需要更改 Service 的 type。 该字段一旦设置就无法更改。

  • publishNotReadyAddresses (boolean)

    publishNotReadyAddresses 表示任何处理此 Service 端点的代理都应忽略任何准备就绪/未准备就绪的指示。 设置此字段的主要场景是为 StatefulSet 的服务提供支持,使之能够为其 Pod 传播 SRV DNS 记录,以实现对等发现。 为 Service 生成 Endpoints 和 EndpointSlice 资源的 Kubernetes 控制器对字段的解读是, 即使 Pod 本身还没有准备好,所有端点都可被视为 “已就绪”。 对于代理而言,如果仅使用 Kubernetes 通过 Endpoints 或 EndpointSlice 资源所生成的端点, 则可以安全地假设这种行为。

  • sessionAffinityConfig (SessionAffinityConfig)

    sessionAffinityConfig 包含会话亲和性的配置。

    SessionAffinityConfig 表示会话亲和性的配置。

    • sessionAffinityConfig.clientIP (ClientIPConfig)

      clientIP 包含基于客户端 IP 的会话亲和性的配置。

      ClientIPConfig 表示基于客户端 IP 的会话亲和性的配置。

      • sessionAffinityConfig.clientIP.timeoutSeconds (int32)

        timeoutSeconds 指定 ClientIP 类型会话的维系时间秒数。 如果 ServiceAffinity == "ClientIP",则该值必须 >0 && <=86400(1 天)。默认值为 10800(3 小时)。

  • allocateLoadBalancerNodePorts (boolean)

    allocateLoadBalancerNodePorts 定义了是否会自动为 LoadBalancer 类型的 Service 分配 NodePort。默认为 true。 如果集群负载均衡器不依赖 NodePort,则可以设置此字段为 false。 如果调用者(通过指定一个值)请求特定的 NodePort,则无论此字段如何,都会接受这些请求。 该字段只能设置在 type 为 LoadBalancer 的 Service 上,如果 type 更改为任何其他类型,该字段将被移除。

ServiceStatus

ServiceStatus 表示 Service 的当前状态。


  • conditions ([]Condition)

    Patch strategy: 在 type 上合并

    Map: 键类型的唯一值将在合并期间保留

    服务的当前状态。

    condition 包含此 API 资源某一方面当前的状态详细信息。

    • conditions.lastTransitionTime(Time),必需

      lastTransitionTime 是状况最近一次状态转化的时间。 变化应该发生在下层状况发生变化的时候。如果不知道下层状况发生变化的时间, 那么使用 API 字段更改的时间是可以接受的。

      Time 是 time.Time 的包装类,支持正确地序列化为 YAML 和 JSON。 为 time 包提供的许多工厂方法提供了包装类。

    • conditions.message (string),必需

      message 是人类可读的消息,有关转换的详细信息,可以是空字符串。

    • conditions.reason (string),必需

      reason 包含一个程序标识符,指示 condition 最后一次转换的原因。 特定条件类型的生产者可以定义该字段的预期值和含义,以及这些值是否被视为有保证的 API。 该值应该是 CamelCase 字符串且不能为空。

    • conditions.status (string),必需

      condition 的状态,True、False、Unknown 之一。

    • conditions.type (string),必需

      CamelCase 或 foo.example.com/CamelCase 中的条件类型。

    • conditions.observedGeneration (int64)

      observedGeneration 表示设置 condition 基于的 .metadata.generation 的过期次数。 例如,如果 .metadata.generation 当前为 12,但 .status.conditions[x].observedGeneration 为 9, 则 condition 相对于实例的当前状态已过期。

  • loadBalancer (LoadBalancerStatus)

    loadBalancer 包含负载均衡器的当前状态(如果存在)。

    LoadBalancerStatus 表示负载均衡器的状态。

    • loadBalancer.ingress ([]LoadBalancerIngress)

      ingress 是一个包含负载均衡器 Ingress 点的列表。Service 的流量需要被发送到这些 Ingress 点。

      LoadBalancerIngress 表示负载平衡器入口点的状态: 用于服务的流量是否被发送到入口点。

      • loadBalancer.ingress.hostname (string)

        hostname 是为基于 DNS 的负载均衡器 Ingress 点(通常是 AWS 负载均衡器)设置的。

      • loadBalancer.ingress.ip (string)

        ip 是为基于 IP 的负载均衡器 Ingress 点(通常是 GCE 或 OpenStack 负载均衡器)设置的。

      • loadBalancer.ingress.ipMode (string)

          ipMode 指定负载平衡器 IP 的行为方式,并且只能在设置了 ip 字段时指定。
        

        将其设置为 VIP 表示流量将传送到节点,并将目标设置为负载均衡器的 IP 和端口。 将其设置为 Proxy 表示将流量传送到节点或 Pod,并将目标设置为节点的 IP 和节点端口或 Pod 的 IP 和端口。 服务实现可以使用此信息来调整流量路由。

      • loadBalancer.ingress.ports ([]PortStatus)

        Atomic:将在合并期间被替换

        ports 是 Service 的端口列表。如果设置了此字段,Service 中定义的每个端口都应该在此列表中。

        • loadBalancer.ingress.ports.port (int32),必需

          port 是所记录的服务端口状态的端口号。

        • loadBalancer.ingress.ports.protocol (string),必需

          protocol 是所记录的服务端口状态的协议。支持的值为:TCPUDPSCTP

        • loadBalancer.ingress.ports.error (string)

          error 是记录 Service 端口的问题。 错误的格式应符合以下规则:

          • 内置错误原因应在此文件中指定,应使用 CamelCase 名称。
          • 云提供商特定错误原因的名称必须符合格式 foo.example.com/CamelCase。

ServiceList

ServiceList 包含一个 Service 列表。


  • items([]Service),必需

    Service 列表

操作


get 读取指定的 Service

HTTP 请求

GET /api/v1/namespaces/{namespace}/services/{name}

参数

  • name (查询参数):string,必需

    Service 名称

  • namespace路径参数):string,必需

    namespace

  • pretty查询参数):string

    pretty

响应

200(Service): OK

401: Unauthorized

get 读取指定 Service 的状态

HTTP 请求

GET /api/v1/namespaces/{namespace}/services/{name}/status

参数

  • name (路径参数):string,必需

    Service 名称

  • namespace路径参数):string,必需

    namespace

  • pretty查询参数):string

    pretty

响应

200(Service): OK

401: Unauthorized

list 列出或监测 Service 类型的对象

HTTP 请求

GET /api/v1/namespaces/{namespace}/services

参数

响应

200(ServiceList): OK

401: Unauthorized

list 列出或监测 Service 类型的对象

HTTP 请求

GET /api/v1/services

参数

响应

200(ServiceList): OK

401: Unauthorized

create 创建一个 Service

HTTP 请求

POST /api/v1/namespaces/{namespace}/services

参数

响应

200(Service): OK

201(Service): Created

202(Service): Accepted

401: Unauthorized

update 替换指定的 Service

HTTP 请求

PUT /api/v1/namespaces/{namespace}/services/{name}

参数

  • name (路径参数):string,必需

    Service 名称

  • namespace路径参数):string,必需

    namespace

  • body: Service,必需

  • dryRun查询参数):string

    dryRun

  • fieldManager查询参数):string

    fieldManager

  • fieldValidation查询参数):string

    fieldValidation

  • pretty查询参数):string

    pretty

响应

200(Service): OK

201(Service): Created

401: Unauthorized

update 替换指定 Service 的状态

HTTP 请求

PUT /api/v1/namespaces/{namespace}/services/{name}/status

参数

  • name (路径参数):string,必需

    Service 名称

  • namespace路径参数):string,必需

    namespace

  • body: Service,必需

  • dryRun查询参数):string

    dryRun

  • fieldManager查询参数):string

    fieldManager

  • fieldValidation查询参数):string

    fieldValidation

  • pretty查询参数):string

    pretty

响应

200(Service): OK

201(Service): Created

401: Unauthorized

patch 部分更新指定的 Service

HTTP 请求

PATCH /api/v1/namespaces/{namespace}/services/{name}

参数

  • name (路径参数):string,必需

    Service 名称

  • namespace路径参数):string,必需

    namespace

  • body: Patch,必需

  • dryRun查询参数):string

    dryRun

  • fieldManager查询参数):string

    fieldManager

  • fieldValidation查询参数):string

    fieldValidation

  • force查询参数):boolean

    force

  • pretty查询参数):string

    pretty

响应

200(Service): OK

201(Service): Created

401: Unauthorized

patch 部分更新指定 Service 的状态

HTTP 请求

PATCH /api/v1/namespaces/{namespace}/services/{name}/status

参数

  • name (路径参数):string,必需

    Service 名称

  • namespace路径参数):string,必需

    namespace

  • body: Patch,必需

  • dryRun查询参数):string

    dryRun

  • fieldManager查询参数):string

    fieldManager

  • fieldValidation查询参数):string

    fieldValidation

  • force查询参数):boolean

    force

  • pretty查询参数):string

    pretty

响应

200(Service): OK

201(Service): Created

401: Unauthorized

delete 删除 Service

HTTP 请求

DELETE /api/v1/namespaces/{namespace}/services/{name}

参数

响应

200(Service): OK

202(Service): Accepted

401: Unauthorized

deletecollection 删除 Service 集合

HTTP 请求

DELETE /api/v1/namespaces/{namespace}/services

参数

响应

200(Status): OK

401: Unauthorized

6.5.2.2 - Endpoints

Endpoints 是实现实际服务的端点的集合。

apiVersion: v1

import "k8s.io/api/core/v1"

Endpoints

Endpoints 是实现实际服务的端点的集合。举例:

 Name: "mysvc",
 Subsets: [
   {
     Addresses: [{"ip": "10.10.1.1"}, {"ip": "10.10.2.2"}],
     Ports: [{"name": "a", "port": 8675}, {"name": "b", "port": 309}]
   },
   {
     Addresses: [{"ip": "10.10.3.3"}],
     Ports: [{"name": "a", "port": 93}, {"name": "b", "port": 76}]
   },
]

  • apiVersion: v1

  • kind: Endpoints

  • metadata (ObjectMeta)

    标准的对象元数据。更多信息: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata

  • subsets ([]EndpointSubset)

    所有端点的集合是所有 subsets 的并集。不同地址会根据其 IP 地址被放入不同子集。 对于具有多个端口的单个地址,如果其中一些端口已就绪,而另一些端口未就绪(因为它们来自不同的容器), 将导致地址显示在不同端口的不同子集中。 任何地址都不可以同时出现在 addresses 和 notReadyAddress 中的相同子集内。

    EndpointSubset 是一组具有公共端口集的地址。扩展的端点集是 addresses 和 ports 的笛卡尔乘积。例如假设:

    { Addresses: [{"ip": "10.10.1.1"}, {"ip": "10.10.2.2"}], Ports: [{"name": "a", "port": 8675}, {"name": "b", "port": 309}] }

    则最终的端点集可以看作:

    a: [ 10.10.1.1:8675, 10.10.2.2:8675 ], b: [ 10.10.1.1:309, 10.10.2.2:309 ]*

    • subsets.addresses ([]EndpointAddress)

      提供标记为就绪的相关端口的 IP 地址。 这些端点应该被认为是负载均衡器和客户端可以安全使用的。

      EndpointAddress 是描述单个 IP 地址的元组。

      • subsets.addresses.ip (string), 必需

        端点的 IP。不可以是本地回路(127.0.0.0/8 或 ::1)、 链路本地(169.254.0.0/16 或 fe80::/10)或链路本地多播(224.0.0.0/24 或 ff02::/16))地址。

      • subsets.addresses.hostname (string)

        端点主机名称。

      • subsets.addresses.nodeName (string)

        可选:承载此端点的节点。此字段可用于确定一个节点的本地端点。

      • subsets.addresses.targetRef (ObjectReference)

        对提供端点的对象的引用。

    • subsets.notReadyAddresses ([]EndpointAddress)

      提供相关端口但由于尚未完成启动、最近未通过就绪态检查或最近未通过活跃性检查而被标记为当前未就绪的 IP 地址。 EndpointAddress 是描述单个 IP 地址的元组。

      • subsets.notReadyAddresses.ip (string), 必需

        端点的 IP。不可以是本地环路(127.0.0.0/8 或 ::1)、 链路本地(169.254.0.0/16 或 fe80::/10)或链路本地多播(224.0.0.0/24 或 ff02::/16)地址。

      • subsets.notReadyAddresses.hostname (string)

        端点主机名称。

      • subsets.notReadyAddresses.nodeName (string)

        可选:承载此端点的节点。此字段可用于确定节点的本地端点。

      • subsets.notReadyAddresses.targetRef (ObjectReference)

        对提供端点的对象的引用。

    • subsets.ports ([]EndpointPort)

      相关 IP 地址上可用的端口号。

      EndpointPort 是描述单个端口的元组。

      • subsets.ports.port (int32), 必需

        端点的端口号。

      • subsets.ports.protocol (string)

        此端口的 IP 协议。必须是 UDP、TCP 或 SCTP。默认值为 TCP。

      • subsets.ports.name (string)

        端口的名称。此字段必须与相应 ServicePort 中的 name 字段匹配。必须是 DNS_LABEL。 仅当定义了一个端口时才可选。

      • subsets.ports.appProtocol (string)

        端口的应用程序协议。这被用作实现的提示,为他们理解的协议提供更丰富的行为。 此字段遵循标准的 Kubernetes 标签语法。有效值为:

EndpointsList

EndpointsList 是端点列表。


操作


get 读取指定的 Endpoints

HTTP 请求

GET /api/v1/namespaces/{namespace}/endpoints/{name}

参数

  • name (路径参数):string,必需

    Endpoints 的名称。

  • namespace (路径参数):string,必需

    namespace

  • pretty (查询参数):string

    pretty

响应

200 (Endpoints): OK

401: Unauthorized

list 列出或监测 Endpoints 类型的对象

HTTP 请求

GET /api/v1/namespaces/{namespace}/endpoints

参数

  • namespace (路径参数):string,必需

    namespace

  • continue (查询参数):string

    continue

  • limit (查询参数):integer

    limit

  • pretty (查询参数):string

    pretty

  • watch (查询参数):boolean

    watch

响应

200 (EndpointsList): OK

401: Unauthorized

list 列出或监测 Endpoints 类型的对象

HTTP 请求

GET /api/v1/endpoints

参数

  • limit (查询参数):integer

    limit

  • pretty (查询参数):string

    pretty

响应

200 (EndpointsList): OK

401: Unauthorized

create 创建 Endpoints

HTTP 请求

POST /api/v1/namespaces/{namespace}/endpoints

参数

响应

200 (Endpoints): OK

201 (Endpoints): Created

202 (Endpoints): Accepted

401: Unauthorized

update 替换指定的 Endpoints

HTTP 请求

PUT /api/v1/namespaces/{namespace}/endpoints/{name}

参数

  • name (路径参数):string,必需

    Endpoints 名称

响应

200 (Endpoints): OK

201 (Endpoints): Created

401: Unauthorized

patch 部分更新指定的 Endpoints

HTTP 请求

PATCH /api/v1/namespaces/{namespace}/endpoints/{name}

参数

  • name (路径参数):string,必需

    Endpoints 名称

  • namespace (路径参数):string,必需

    namespace

  • body: Patch, 必需

  • pretty (查询参数):string

    pretty

响应

200 (Endpoints): OK

201 (Endpoints): Created

401: Unauthorized

delete 删除 Endpoints

HTTP 请求

DELETE /api/v1/namespaces/{namespace}/endpoints/{name}

参数

  • name (路径参数):string,必需

    Endpoints 名称

  • dryRun (查询参数):string

    dryRun

  • pretty (查询参数):string

    pretty

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除 Endpoints 组

HTTP 请求

DELETE /api/v1/namespaces/{namespace}/endpoints

参数

  • continue (查询参数):string

    continue

  • dryRun (查询参数):string

    dryRun

  • limit (查询参数):integer

    limit

  • pretty (查询参数):string

    pretty

响应

200 (Status): OK

401: Unauthorized

6.5.2.3 - EndpointSlice

EndpointSlice 是实现某 Service 的端点的子集.

apiVersion: discovery.k8s.io/v1

import "k8s.io/api/discovery/v1"

EndpointSlice

EndpointSlice 是实现某 Service 的端点的子集。一个 Service 可以有多个 EndpointSlice 对象与之对应, 必须将所有的 EndpointSlice 拼接起来才能形成一套完整的端点集合。Service 通过标签来选择 EndpointSlice。


  • apiVersion:discovery.k8s.io/v1

  • kind:EndpointSlice

  • metadata (ObjectMeta)

    标准的对象元数据。

  • addressType (string), 必需

    addressType 指定当前 EndpointSlice 携带的地址类型。一个 EndpointSlice 只能携带同一类型的地址。 EndpointSlice 对象创建完成后不可以再更改 addressType 字段。 目前支持的地址类型为:

    • IPv4:表示 IPv4 地址。
    • IPv6:表示 IPv6 地址。
    • FQDN:表示完全限定域名。
  • endpoints ([]Endpoint), 必需

    原子性:合并期间将被替换

    endpoints 是当前 EndpointSlice 中一组唯一的端点。每个 EndpointSlice 最多可以包含 1000 个端点。

    端点是实现某 Service 的一个逻辑“后端”。

    • endpoints.addresses ([]string), 必需

      集合:不重复的值在合并期间会被保留

      本端点的地址。此字段的内容会根据对应的 EndpointSlice addressType 字段的值进行解释。 消费者必须根据自身能力处理不同类型的地址。此字段必须至少包含 1 个地址,最多不超过 100 个地址。 假定这些地址都是可替换的,而且客户端也可能选择只用第一个元素。参阅: https://issue.k8s.io/106267

    • endpoints.conditions (EndpointConditions)

      conditions 包含和本端点当前状态有关的信息。

      EndpointConditions 是端点的当前状况。

      • endpoints.conditions.ready (boolean)

        ready 说明此端点已经准备好根据相关的系统映射接收流量。nil 值表示状态未知。 在大多数情况下,消费者应将这种未知状态视为就绪(ready)。 考虑到兼容性,对于正在结束状态下的端点,永远不能将 ready 设置为“true”, 除非正常的就绪行为被显式覆盖,例如当关联的服务设置了 publishNotReadyAddresses 标志时。

      • endpoints.conditions.serving (boolean)

        serving 和 ready 非常相似。唯一的不同在于, 即便某端点的状态为 Terminating 也可以设置 serving。 对于处在终止过程中的就绪端点,此状况应被设置为 “true”。 如果设置为 nil,则消费者应该以 ready 值为准。

      • endpoints.conditions.terminating (boolean)

        terminating 说明当前端点正在终止过程中。nil 值表示状态未知。 消费者应将这种未知状态视为端点并不处于终止过程中。

    • endpoints.deprecatedTopology (map[string]string)

      deprecatedTopology 包含 v1beta1 API 的拓扑信息部分。目前已经弃用了此字段, 移除 v1beta1 API 时(不早于 Kubernetes v1.24)会一起移除此字段。 此字段目前仍然可以存储值,但是不能通过 v1 API 写入数据。 向此字段写入数据的任何尝试都会被忽略,并且不会通知用户。 移除此字段后,可以在 zone 和 nodeName 字段中查看拓扑信息。

    • endpoints.hints (EndpointHints)

      hints 是关于应该如何使用某端点的提示信息。

      EndpointHints 提供应该如何使用某端点的提示信息。

      • endpoints.hints.forZones ([]ForZone)

        原子性:合并期间将被替换

        forZones 表示应该由哪个可用区调用此端点从才能激活拓扑感知路由。

        ForZone 指示应该由哪些可用区调度此端点。

        • endpoints.hints.forZones.name (string), 必需

          name 代表可用区的名称。

    • endpoints.hostname (string)

      此端点的主机名称。端点的使用者可以通过此字段区分各个端点(例如,通过 DNS 域名)。 使用同一主机名称的多个端点应被视为可替换(例如,DNS 中的多个 A 记录)。 必须为小写字母,并且需要通过 DNS Label (RFC 1123) 验证。

    • endpoints.nodeName (string)

      nodeName 是托管此端点的 Node 的名称,使用 nodeName 可以决定 Node 本地有哪些端点。

    • endpoints.targetRef (ObjectReference)

      targetRef 是对代表此端点的 Kubernetes 对象的引用。

    • endpoints.zone (string)

      zone 是此端点所在的可用区(Zone)的名称。

  • ports ([]EndpointPort)

    原子性:合并期间会被替代

    ports 列出了当前 EndpointSlice 中各个端点所暴露的网络端口。每个端口的名称不得重复。 当 ports 列表为空时,表示没有已经指定暴露哪些端口。如果端口值被定义为 nil,表示暴露“所有端口”。 每个 EndpointSlice 最多可以包含 100 个端口。

    EndpointPort 是 EndpointSlice 使用的端口。

    • ports.port (int32)

      port 表示端点的端口号。如果未指定,就不限制端口,且必须根据消费者的具体环境进行解释。

    • ports.protocol (string)

      protocol 表示此端口的 IP 协议。必须为 UDP、TCP 或 SCTP。默认为 TCP。

    • ports.name (string)

      name 表示此端口的名称。EndpointSlice 中所有端口的名称都不得重复。 如果 EndpointSlice 是基于 Kubernetes Service 创建的, 那么此端口的名称和 Service.ports[].name 字段的值一致。默认为空字符串。 名称必须是空字符串,或者必须通过 DNS_LABEL 验证:

      • 最多包含 63 个字符。
      • 必须包含英文小写字母或'-'。
      • 必须以字母开头并以字母结尾。
    • ports.appProtocol (string)

      此端口的应用层协议。字段值被用作提示,允许协议实现为其所理解的协议提供更丰富的行为。 此字段遵循标准的 Kubernetes 标签句法。有效的取值是:

EndpointSliceList

EndpointSliceList 是 EndpointSlice 的列表。


  • apiVersion:discovery.k8s.io/v1

  • kind:EndpointSliceList

  • metadata (ListMeta)

    标准的列表元数据

  • items ([]EndpointSlice), 必需

    items 是 EndpointSlice 列表

操作


get 读取指定的 EndpointSlice

HTTP 请求

GET /apis/discovery.k8s.io/v1/namespaces/{namespace}/endpointslices/{name}

参数

  • name (路径参数):string, 必需

    EndpointSlice 的名称

  • namespace (路径参数):string, 必需

    namespace

  • pretty (查询参数):string

    pretty

响应

200 (EndpointSlice):OK

401:Unauthorized

list 列举或监测 EndpointSlice 类别的对象

HTTP 请求

GET /apis/discovery.k8s.io/v1/namespaces/{namespace}/endpointslices

参数

  • namespace (路径参数):string, 必需

    namespace

  • continue (查询参数):string

    continue

  • limit (查询参数):integer

    limit

  • pretty (查询参数):string

    pretty

  • watch (查询参数):boolean

    watch

响应

200 (EndpointSliceList): OK

401:Unauthorized

list 列举或监测 EndpointSlice 类别的对象

HTTP 请求

GET /apis/discovery.k8s.io/v1/endpointslices

参数

  • continue (查询参数):string

    continue

  • limit (查询参数):integer limit
  • pretty (查询参数):string pretty
  • watch (查询参数):boolean

    watch

响应

200 (EndpointSliceList):OK

401:Unauthorized

create 创建 EndpointSlice

HTTP 请求

POST /apis/discovery.k8s.io/v1/namespaces/{namespace}/endpointslices

参数

  • dryRun (查询参数):string

    dryRun

  • pretty (查询参数):string

    pretty

响应

200 (EndpointSlice):OK

201 (EndpointSlice):Created

202 (EndpointSlice):Accepted

401:Unauthorized

update 替换指定的 EndpointSlice

HTTP 请求

PUT /apis/discovery.k8s.io/v1/namespaces/{namespace}/endpointslices/{name}

参数

  • name (路径参数):string, 必需

    EndpointSlice 的名称

  • dryRun (查询参数):string

    dryRun

  • pretty (查询参数):string

    pretty

响应

200 (EndpointSlice):OK

201 (EndpointSlice):Created

401:Unauthorized

patch 部分更新指定的 EndpointSlice

HTTP 请求

PATCH /apis/discovery.k8s.io/v1/namespaces/{namespace}/endpointslices/{name}

参数

  • name (路径参数): string, 必需

    EndpointSlice 的名称

  • namespace (路径参数):string, 必需

    namespace

  • bodyPatch, 必需

  • dryRun (查询参数):string

    dryRun

  • force (查询参数):boolean

    force

  • pretty (查询参数):string

    pretty

响应

200 (EndpointSlice):OK

201 (EndpointSlice):Created

401:Unauthorized

delete 删除 EndpointSlice

HTTP 请求

DELETE /apis/discovery.k8s.io/v1/namespaces/{namespace}/endpointslices/{name}

参数

  • name (路径参数):string, 必需

    EndpointSlice 的名称

  • dryRun (查询参数):string

    dryRun

  • pretty (查询参数):string

    pretty

响应

200 (Status):OK

202 (Status):Accepted

401:Unauthorized

deletecollection 删除 EndpointSlice 的集合

HTTP 请求

DELETE /apis/discovery.k8s.io/v1/namespaces/{namespace}/endpointslices

参数

  • continue (查询参数):string

    continue

  • dryRun (查询参数):string

    dryRun

  • limit (查询参数):integer

    limit

  • pretty (查询参数):string

    pretty

响应

200 (Status):OK

401:Unauthorized

6.5.2.4 - Ingress

Ingress 是允许入站连接到达后端定义的端点的规则集合。

apiVersion: networking.k8s.io/v1

import "k8s.io/api/networking/v1"

Ingress

Ingress 是允许入站连接到达后端定义的端点的规则集合。 Ingress 可以配置为向服务提供外部可访问的 URL、负载均衡流量、终止 SSL、提供基于名称的虚拟主机等。


IngressSpec

IngressSpec 描述用户希望存在的 Ingress。


  • defaultBackend (IngressBackend)

    defaultBackend 是负责处理与任何规则都不匹配的请求的后端。 如果未指定 rules,则必须指定 defaultBackend。 如果未设置 defaultBackend,则不符合任何 rules 的请求的处理将由 Ingress 控制器决定。

  • ingressClassName (string)

    ingressClassName 是 IngressClass 集群资源的名称。 Ingress 控制器实现使用此字段来了解它们是否应该通过传递连接(控制器 -> IngressClass -> Ingress 资源)为该 Ingress 资源提供服务。尽管 kubernetes.io/ingress.class 注解(简单的常量名称)从未正式定义, 但它被 Ingress 控制器广泛支持,以在 Ingress 控制器和 Ingress 资源之间创建直接绑定。 新创建的 Ingress 资源应该优先选择使用该字段。但是,即使注解已被正式弃用, 出于向后兼容性的原因,Ingress 控制器仍应能够处理该注解(如果存在)。

  • rules ([]IngressRule)

    Atomic: 将在合并期间被替换

    rules 是用于配置 Ingress 的主机规则列表。如果未指定或没有规则匹配,则所有流量都将发送到默认后端。

    IngressRule 表示将指定主机下的路径映射到相关后端服务的规则。 传入请求首先评估主机匹配,然后路由到与匹配的 IngressRuleValue 关联的后端。

    • rules.host (string)

      host 是 RFC 3986 定义的网络主机的完全限定域名。请注意以下与 RFC 3986 中定义的 URI 的 “host” 部分的偏差:

      1. 不允许 IP。当前 IngressRuleValue 只能应用于父 Ingress Spec 中的 IP。

      2. 由于不允许使用端口,因此不理会 “:” 分隔符。 当前 Ingress 的端口隐式为:

        • :80 用于 http
        • :443 用于 https

      这两种情况在未来都可能发生变化。入站请求在通过 IngressRuleValue 处理之前先进行 host 匹配。 如果主机未指定,Ingress 将根据指定的 IngressRuleValue 规则路由所有流量。

      host 可以是 “精确“ 的,设置为一个不含终止句点的网络主机域名(例如 “foo.bar.com” ), 也可以是一个 “通配符”,设置为以单个通配符标签为前缀的域名(例如 “.foo.com”)。 通配符 “” 必须单独显示为第一个 DNS 标签,并且仅与单个标签匹配。 你不能单独使用通配符作为标签(例如,Host=“*”)。请求将按以下方式与主机字段匹配:

      1. 如果 host 是精确匹配的,则如果 http Host 标头等于 host 值,则请求与此规则匹配。
      2. 如果 host 是用通配符给出的,那么如果 HTTP Host 标头与通配符规则的后缀(删除第一个标签)相同, 则请求与此规则匹配。
    • rules.http (HTTPIngressRuleValue)

      HTTPIngressRuleValue 是指向后端的 http 选择算符列表。例如 http://<host>/<path>?<searchpart> -> 后端, 其中 url 的部分对应于 RFC 3986,此资源将用于匹配最后一个 “/” 之后和第一个 “?” 之前的所有内容或 “#”。

      • rules.http.paths ([]HTTPIngressPath),必需

        Atomic: 将在合并期间被替换

        paths 是一个将请求映射到后端的路径集合。

        HTTPIngressPath 将路径与后端关联。与路径匹配的传入 URL 将转发到后端。

        • rules.http.paths.backend (IngressBackend),必需

          backend 定义将流量转发到的引用服务端点。

        • rules.http.paths.pathType (string),必需

          pathType 决定如何解释 path 匹配。pathType 可以是以下值之一:

          • Exact:与 URL 路径完全匹配。

          • Prefix:根据按 “/” 拆分的 URL 路径前缀进行匹配。 匹配是按路径元素逐个元素完成。路径元素引用的是路径中由“/”分隔符拆分的标签列表。 如果每个 p 都是请求路径 p 的元素前缀,则请求与路径 p 匹配。 请注意,如果路径的最后一个元素是请求路径中的最后一个元素的子字符串,则匹配不成功 (例如 /foo/bar 匹配 /foo/bar/baz,但不匹配 /foo/barbaz)。

          • ImplementationSpecific:路径匹配的解释取决于 IngressClass。 实现可以将其视为单独的路径类型,也可以将其视为前缀或确切的路径类型。 实现需要支持所有路径类型。

        • rules.http.paths.path (string)

          path 要与传入请求的路径进行匹配。 目前,它可以包含 RFC 3986 定义的 URL 的常规 “路径” 部分所不允许的字符。 路径必须以 “/” 开头,并且在 pathType 值为 “Exact” 或 “Prefix” 时必须存在。

  • tls ([]IngressTLS)

    Atomic: 将在合并期间被替换

    tls 表示 TLS 配置。目前,Ingress 仅支持一个 TLS 端口 443。 如果此列表的多个成员指定了不同的主机,如果实现 Ingress 的 Ingress 控制器支持 SNI, 则它们将根据通过 SNI TLS 扩展指定的主机名在同一端口上多路复用。

    IngressTLS 描述与 Ingress 相关的传输层安全性。

    • tls.hosts ([]string)

      Atomic: 将在合并期间被替换

      hosts 是 TLS 证书中包含的主机列表。 此列表中的值必须与 tlsSecret 中使用的名称匹配。 默认为实现此 Ingress 的负载均衡控制器的通配符主机设置(如果未指定)。

    • tls.secretName (string)

      secretName 是用于终止端口 443 上 TLS 通信的 Secret 的名称。 字段是可选的,以允许仅基于 SNI 主机名的 TLS 路由。 如果侦听器中的 SNI 主机与入口规则使用的 “Host” 标头字段冲突,则 SNI 主机用于终止,Host 标头的值用于路由。

IngressBackend

IngressBackend 描述给定服务和端口的所有端点。


  • resource (TypedLocalObjectReference)

    resource 是对 Ingress 对象所在命名空间中另一个 Kubernetes 资源的引用。 如果指定了 resource,则不得指定 service.name 和 service.port。 此字段是一个与 service 互斥的设置。

  • service (IngressServiceBackend)

    service 引用一个 Service 作为后端。此字段是一个与 resource 互斥的设置。

    IngressServiceBackend 引用一个 Kubernetes Service 作为后端。

    • service.name (string),必需

    name 是引用的服务。服务必须与 Ingress 对象位于同一命名空间中。

    • service.port (ServiceBackendPort)

    所引用的服务的端口。IngressServiceBackend 需要端口名或端口号。

    ServiceBackendPort 是被引用的服务的端口。

    • service.port.name (string)

      name 是服务上的端口名称。此字段是一个与 number 互斥的设置。

    • service.port.number (int32)

      number 是服务上的数字形式端口号(例如 80)。此字段是一个与 name 互斥的设置。

IngressStatus

IngressStatus 描述 Ingress 的当前状态。


  • loadBalancer (IngressLoadBalancerStatus)

    loadBalancer 包含负载均衡器的当前状态。

    IngressLoadBalancerStatus 表示负载均衡器的状态。

    • loadBalancer.ingress ([]IngressLoadBalancerIngress)

      ingress 是一个包含负载均衡器入口点的列表。

      IngressLoadBalancerIngress 表示负载均衡器入口点的状态。

      • loadBalancer.ingress.hostname (string)

        hostname 是为基于 DNS 的负载平衡器入口点所设置的主机名。

      • loadBalancer.ingress.ip (string)

        ip 是为基于 IP 的负载平衡器入口点设置的 IP。

      • loadBalancer.ingress.ports ([]IngressPortStatus)

        Atomic: 将在合并期间被替换

        ports 提供有关此 LoadBalancer 公开端口的信息。

        IngressPortStatus 表示服务端口的错误情况

        • loadBalancer.ingress.ports.port (int32),必需

          port 是入栈端口的端口号

        • loadBalancer.ingress.ports.protocol (string),必需

          protocol 是入栈端口的协议。支持的值为:“TCP”、“UDP”、“SCTP”。

        • loadBalancer.ingress.ports.error (string)

          error 用来记录服务端口的问题。错误的格式应符合以下规则:

          • 应在此文件中指定内置错误码,并且错误码应使用驼峰法命名。
          • 特定于云驱动的错误码名称必须符合 foo.example.com/CamelCase 格式。

IngressList

IngressList 是 Ingress 的集合。


  • items ([]Ingress),必需

    items 是 Ingress 的列表。

操作


get 读取指定的 Ingress

HTTP 请求

GET /apis/networking.k8s.io/v1/namespaces/{namespace}/ingresses/{name}

参数

  • name (路径参数):string,必需

    Ingress 的名称。

  • namespace (路径参数):string,必需

    namespace

  • pretty (查询参数): string

    pretty

响应

200 (Ingress): OK

401: Unauthorized

get 读取指定 Ingress 状态

HTTP 请求

GET /apis/networking.k8s.io/v1/namespaces/{namespace}/ingresses/{name}/status

参数

  • name (路径参数):string,必需

    Ingress 的名称。

  • namespace (路径参数):string,必需

    namespace

  • pretty (查询参数): string

    pretty

响应

200 (Ingress): OK

401: Unauthorized

list 列出或监测 Ingress 类型对象

HTTP 请求

GET /apis/networking.k8s.io/v1/namespaces/{namespace}/ingresses

参数

  • namespace (路径参数):string,必需

    namespace

  • limit (查询参数): integer

    limit

  • pretty (查询参数): string

    pretty

  • watch (查询参数): boolean

    watch

响应

200 (IngressList): OK

401: Unauthorized

list 列出或监测 Ingress 类型对象

HTTP 请求

GET /apis/networking.k8s.io/v1/ingresses

参数

  • limit (查询参数): integer

    limit

  • pretty (查询参数): string

    pretty

  • watch (查询参数): boolean

    watch

响应

200 (IngressList): OK

401: Unauthorized

create 创建一个 Ingress

HTTP 请求

POST /apis/networking.k8s.io/v1/namespaces/{namespace}/ingresses

参数

  • dryRun (查询参数): string

    dryRun

  • pretty (查询参数): string

    pretty

响应

200 (Ingress): OK

201 (Ingress): Created

202 (Ingress): Accepted

401: Unauthorized

update 替换指定的 Ingress

HTTP 请求

PUT /apis/networking.k8s.io/v1/namespaces/{namespace}/ingresses/{name}

参数

  • name (路径参数): string,必需

    Ingress 的名称。

  • namespace (路径参数): string,必需

    namespace

  • body: Ingress,必需

  • dryRun (查询参数): string

    dryRun

  • pretty (查询参数): string

    pretty

响应

200 (Ingress): OK

201 (Ingress): Created

401: Unauthorized

update 替换指定 Ingress 的状态

HTTP 请求

PUT /apis/networking.k8s.io/v1/namespaces/{namespace}/ingresses/{name}/status

参数

  • name (路径参数):string,必需

    Ingress 的名称。

  • namespace (路径参数):string,必需

    namespace

  • body: Ingress,必需

  • dryRun (查询参数): string

    dryRun

  • pretty (查询参数): string

    pretty

响应

200 (Ingress): OK

201 (Ingress): Created

401: Unauthorized

patch 部分更新指定的 Ingress

HTTP 请求

PATCH /apis/networking.k8s.io/v1/namespaces/{namespace}/ingresses/{name}

参数

  • name (路径参数):string,必需

    Ingress 的名称。

  • namespace (路径参数):string,必需

    namespace

  • body: Patch,必需

  • dryRun (查询参数): string

    dryRun

  • force (查询参数): boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (Ingress): OK

201 (Ingress): Created

401: Unauthorized

patch 部分更新指定 Ingress 的状态

HTTP 请求

PATCH /apis/networking.k8s.io/v1/namespaces/{namespace}/ingresses/{name}/status

参数

  • name (路径参数):string,必需

    Ingress 的名称。

  • namespace (路径参数):string,必需

    namespace

  • body: Patch,必需

  • dryRun (查询参数): string

    dryRun

  • force (查询参数): boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (Ingress): OK

201 (Ingress): Created

401: Unauthorized

delete 删除一个 Ingress

HTTP 请求

DELETE /apis/networking.k8s.io/v1/namespaces/{namespace}/ingresses/{name}

参数

  • name (路径参数):string,必需

    Ingress 的名称。

  • namespace (路径参数):string,必需

    namespace

  • body: DeleteOptions

  • dryRun (查询参数): string

    dryRun

  • pretty (查询参数): string

    pretty

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除 Ingress 的集合

HTTP 请求

DELETE /apis/networking.k8s.io/v1/namespaces/{namespace}/ingresses

参数

  • dryRun (查询参数): string

    dryRun

  • limit (查询参数): integer

    limit

  • pretty (查询参数): string

    pretty

响应

200 (Status): OK

401: Unauthorized

6.5.2.5 - IngressClass

IngressClass 代表 Ingress 的类,被 Ingress 的规约引用。

apiVersion: networking.k8s.io/v1

import "k8s.io/api/networking/v1"

IngressClass

IngressClass 代表 Ingress 的类,被 Ingress 的规约引用。 ingressclass.kubernetes.io/is-default-class 注解可以用来标明一个 IngressClass 应该被视为默认的 Ingress 类。 当某个 IngressClass 资源将此注解设置为 true 时, 没有指定类的新 Ingress 资源将被分配到此默认类。


IngressClassSpec

IngressClassSpec 提供有关 Ingress 类的信息。


  • controller (string)

    controller 是指应该处理此类的控制器名称。 这允许由同一控制器控制不同“口味”。例如,对于同一个实现的控制器你可能有不同的参数。 此字段应该指定为长度不超过 250 个字符的域前缀路径,例如 “acme.io/ingress-controller”。 该字段是不可变的。

  • parameters (IngressClassParametersReference)

    parameters 是指向控制器中包含额外配置的自定义资源的链接。 如果控制器不需要额外的属性,这是可选的。

    IngressClassParametersReference 标识一个 API 对象。这可以用来指定一个集群或者命名空间范围的资源

    • parameters.kind (string),必需

      kind 是被引用资源的类型。

    • parameters.name (string),必需

      name 是被引用资源的名称。

    • parameters.apiGroup (string)

      apiGroup 是被引用资源的组。 如果未指定 apiGroup,则被指定的 kind 必须在核心 API 组中。 对于任何其他第三方类型,apiGroup 是必需的。

    • parameters.namespace (string)

      namespace 是被引用资源的命名空间。 当范围被设置为 “namespace” 时,此字段是必需的; 当范围被设置为 “Cluster” 时,此字段必须不设置。

    • parameters.scope (string)

      scope 表示是否引用集群或者命名空间范围的资源。 这可以设置为“集群”(默认)或者“命名空间”。

IngressClassList

IngressClassList 是 IngressClasses 的集合。


  • apiVersion: networking.k8s.io/v1

  • kind: IngressClassList

  • metadata (ListMeta)

    标准的列表元数据。

  • items ([]IngressClass),必需

    items 是 IngressClasses 的列表。

操作


get 读取指定的 IngressClass

HTTP 请求

GET /apis/networking.k8s.io/v1/ingressclasses/{name}

参数

  • name路径参数):string,必需

    IngressClass 的名称

  • pretty查询参数):string

    pretty

响应

200 (IngressClass): OK

401: Unauthorized

list 列出或监视 IngressClass 类型的对象

HTTP 请求

GET /apis/networking.k8s.io/v1/ingressclasses

参数

  • continue查询参数):string

    continue

  • limit查询参数):integer

    limit

  • pretty查询参数):string

    pretty

  • watch查询参数):boolean

    watch

响应

200 (IngressClassList): OK

401: Unauthorized

create 创建一个 IngressClass

HTTP 请求

POST /apis/networking.k8s.io/v1/ingressclasses

参数

  • dryRun查询参数):string

    dryRun

  • pretty查询参数):string

    pretty

响应

200 (IngressClass): OK

201 (IngressClass): Created

202 (IngressClass): Accepted

401: Unauthorized

update 替换指定的 IngressClass

HTTP 请求

PUT /apis/networking.k8s.io/v1/ingressclasses/{name}

参数

  • name路径参数):string,必需

    IngressClass 的名称

  • dryRun查询参数):string

    dryRun

  • pretty查询参数):string

    pretty

响应

200 (IngressClass): OK

201 (IngressClass): Created

401: Unauthorized

patch 部分更新指定的 IngressClass

HTTP 请求

PATCH /apis/networking.k8s.io/v1/ingressclasses/{name}

参数

  • name路径参数):string,必需

    IngressClass 的名称

  • dryRun查询参数):string

    dryRun

  • force查询参数):boolean

    force

  • pretty查询参数):string

    pretty

响应

200 (IngressClass): OK

201 (IngressClass): Created

401: Unauthorized

delete 删除一个 IngressClass

HTTP 请求

DELETE /apis/networking.k8s.io/v1/ingressclasses/{name}

参数

  • name路径参数):string,必需

    IngressClass 的名称

  • body: DeleteOptions

  • dryRun查询参数):string

    dryRun

  • pretty查询参数):string

    pretty

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除 IngressClass 的集合

DELETE /apis/networking.k8s.io/v1/ingressclasses

参数

  • continue查询参数):string

    continue

  • dryRun查询参数):string

    dryRun

  • limit查询参数):integer

    limit

  • pretty查询参数):string

    pretty

响应

200 (Status): OK

401: Unauthorized

6.5.3 - 配置和存储资源

6.5.3.1 - ConfigMap

ConfigMap 包含供 Pod 使用的配置数据。

apiVersion: v1

import "k8s.io/api/core/v1"

ConfigMap

ConfigMap 包含供 Pod 使用的配置数据。


  • apiVersion: v1

  • kind: ConfigMap

  • binaryData (map[string][]byte)

    binaryData 包含二进制数据。 每个键必须由字母、数字、“-”、“_” 或 “.” 组成。 binaryData 可以包含不在 UTF-8 范围中的字节序列。 binaryData 中存储的键不得与 data 字段中的键重叠,这在验证过程中是强制要求。 使用此字段需要 apiserver 和 kubelet 的版本高于 1.10。

  • data (map[string]string)

    data 包含配置数据。 每个键必须由字母、数字、“-”、“_” 或 “.” 组成。 如果值包含非 UTF-8 字节序列,则必须使用 binaryData 字段。 data 中存储的键不得与 binaryData 字段中的键重叠,这在验证过程中是强制要求。

  • immutable (boolean)

    如果 immutable 设为 true, 则确保不会更新 ConfigMap 中存储的数据(只能修改对象元数据)。 如果未设为 true,则可以随时修改此字段。 默认为 nil。

ConfigMapList

ConfigMapList 是包含 ConfigMap 对象列表的资源。


  • apiVersion: v1

  • kind: ConfigMapList

操作


get 读取指定的 ConfigMap

HTTP 请求

GET /api/v1/namespaces/{namespace}/configmaps/{name}

参数

  • name (路径参数): string,必需

    ConfigMap 的名称

  • namespace (路径参数): string,必需

    namespace

  • pretty (查询参数): string

    pretty

响应

200 (ConfigMap): OK

401: Unauthorized

list 列出或观测类别为 ConfigMap 的对象

HTTP 请求

GET /api/v1/namespaces/{namespace}/configmaps

参数

响应

200 (ConfigMapList): OK

401: Unauthorized

list 列出或观测类别为 ConfigMap 的对象

HTTP 请求

GET /api/v1/configmaps

参数

响应

200 (ConfigMapList): OK

401: Unauthorized

create 创建 ConfigMap

HTTP 请求

POST /api/v1/namespaces/{namespace}/configmaps

参数

响应

200 (ConfigMap): OK

201 (ConfigMap): Created

202 (ConfigMap): Accepted

401: Unauthorized

update 替换指定的 ConfigMap

HTTP 请求

PUT /api/v1/namespaces/{namespace}/configmaps/{name}

参数

  • name (路径参数): string,必需

    ConfigMap 的名称

  • namespace (路径参数): string,必需

    namespace

  • body: ConfigMap,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • pretty (查询参数): string

    pretty

响应

200 (ConfigMap): OK

201 (ConfigMap): Created

401: Unauthorized

patch 部分更新指定的 ConfigMap

HTTP 请求

PATCH /api/v1/namespaces/{namespace}/configmaps/{name}

参数

  • name (路径参数): string,必需

    ConfigMap 的名称

  • namespace (路径参数): string,必需

    namespace

  • body: Patch,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • force (查询参数): boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (ConfigMap): OK

201 (ConfigMap): Created

401: Unauthorized

delete 删除 ConfigMap

HTTP 请求

DELETE /api/v1/namespaces/{namespace}/configmaps/{name}

参数

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除 ConfigMap 的集合

HTTP 请求

DELETE /api/v1/namespaces/{namespace}/configmaps

参数

响应

200 (Status): OK

401: Unauthorized

6.5.3.2 - Secret

Secret 包含某些类别的秘密数据。

apiVersion: v1

import "k8s.io/api/core/v1"

Secret

Secret 包含某些类别的秘密数据。 data 字段值的总字节必须小于 MaxSecretSize 字节。


  • apiVersion: v1

  • kind: Secret

  • immutable (boolean)

    如果 immutable 设为 true,则确保不会更新 Secret 中存储的数据(只能修改对象元数据)。 如果未设为 true,则可以随时修改此字段。 默认为 nil。

  • stringData (map[string]string)

    stringData 允许指定字符串格式的非二进制秘密数据。 为了方便起见,它作为只写输入字段提供。 写入时将所有键和值合并到 data 字段,且覆盖任何现有的值。 从 API 读取时绝不会输出 stringData 字段。

  • type (string)

    用于满足程序化方式处理秘密数据。 更多信息: https://kubernetes.io/zh-cn/docs/concepts/configuration/secret/#secret-types

SecretList

SecretList 是 Secret 的列表。


  • apiVersion: v1

  • kind: SecretList

操作


get 读取指定的 Secret

HTTP 请求

GET /api/v1/namespaces/{namespace}/secrets/{name}

参数

  • name (路径参数): string,必需

    Secret 的名称

  • namespace (路径参数): string,必需

    namespace

  • pretty (查询参数): string

    pretty

响应

200 (Secret): OK

401: Unauthorized

list 列出或观测类别为 Secret 的对象

HTTP 请求

GET /api/v1/namespaces/{namespace}/secrets

参数

响应

200 (SecretList): OK

401: Unauthorized

list 列出或观测类别为 Secret 的对象

HTTP 请求

GET /api/v1/secrets

参数

响应

200 (SecretList): OK

401: Unauthorized

create 创建 Secret

HTTP 请求

POST /api/v1/namespaces/{namespace}/secrets

参数

响应

200 (Secret): OK

201 (Secret): Created

202 (Secret): Accepted

401: Unauthorized

update 替换指定的 Secret

HTTP 请求

PUT /api/v1/namespaces/{namespace}/secrets/{name}

参数

  • name (路径参数): string,必需

    Secret 的名称

  • namespace (路径参数): string,必需

    namespace

  • body: Secret,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • pretty (查询参数): string

    pretty

响应

200 (Secret): OK

201 (Secret): Created

401: Unauthorized

patch 部分更新指定的 Secret

HTTP 请求

PATCH /api/v1/namespaces/{namespace}/secrets/{name}

参数

  • name (路径参数): string,必需

    Secret 的名称

  • namespace (路径参数): string,必需

    namespace

  • body: Patch,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • force (查询参数): boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (Secret): OK

201 (Secret): Created

401: Unauthorized

delete 删除 Secret

HTTP 请求

DELETE /api/v1/namespaces/{namespace}/secrets/{name}

参数

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除 Secret 的集合

HTTP 请求

DELETE /api/v1/namespaces/{namespace}/secrets

参数

响应

200 (Status): OK

401: Unauthorized

6.5.3.3 - Volume

Volume 表示 Pod 中一个有名字的卷,可以由 Pod 中的任意容器进行访问。

import "k8s.io/api/core/v1"

Volume

Volume 表示 Pod 中一个有名字的卷,可以由 Pod 中的任意容器进行访问。


暴露的持久卷

投射

  • configMap (ConfigMapVolumeSource)

    configMap 表示应填充此卷的 configMap。

    将 ConfigMap 适配到一个卷中。目标 ConfigMap 的 data 字段的内容将以文件的形式呈现在一个卷中, 使用 data 字段中的键名作为文件名,除非 items 元素中已经填充了由键名到路径的特定映射。 ConfigMap 卷支持所有权管理和 SELinux 重新打标签。

    • configMap.name (string)

    被引用资源的名称。更多信息: https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names/#names

    • configMap.optional (boolean)

      optional 指定是否所引用的 ConfigMap 或其键必须已经被定义。

    • configMap.defaultMode (int32)

      defaultMode 是可选的:默认情况下,模式位用于为已创建的文件设置权限。 必须是 0000 到 0777 之间的八进制值或 0 到 511 之间的十进制值。 YAML 既接受八进制值也接受十进制值,JSON 针对模式位需要十进制值。此字段默认为 0644。 路径内的目录不受此设置的影响。这可能与影响文件模式的其他选项(如 fsGroup)有冲突,且结果可以是其他模式位也被设置。

    • configMap.items ([]KeyToPath)

      如果未指定 items,则所引用的 ConfigMap 的 data 字段中的每个键值对将作为一个文件被投射到卷中, 这个文件的名称是键名,而文件的内容是键的取值。 如果指定 items,则所列出的键将被投射到指定的路径中,且不会显示未列出的键。 如果指定的键不在 ConfigMap 中,则卷设置将出错,除非对应的键被标记为可选。 路径必须是相对路径,不能包含 “..” 路径,也不能以 “..” 开头。

  • secret (SecretVolumeSource)

    secret 表示用来填充此卷的 Secret。更多信息: https://kubernetes.io/zh-cn/docs/concepts/storage/volumes#secret

    将 Secret 适配到一个卷中。 目标 Secret 的 data 字段的内容将以文件的形式呈现在一个卷中,使用 data 字段中的键名作为文件名。 Secret 卷支持所有权管理和 SELinux 重新打标签。

    • secret.defaultMode (int32)

      defaultMode 是可选的:默认情况下,模式位用于为已创建的文件设置权限。 必须是 0000 到 0777 之间的八进制值或 0 到 511 之间的十进制值。 YAML 既接受八进制值也接受十进制值,JSON 针对模式位需要十进制值。此字段默认为 0644。 路径内的目录不受此设置的影响。 这可能与影响文件模式的其他选项(如 fsGroup)有冲突,且结果可以是其他模式位也被设置。

    • secret.items ([]KeyToPath)

      如果未指定 items,则所引用的 Secret 的 data 字段中的每个键值对将作为一个文件被投射到卷中, 这个文件的名称是键名,而文件的内容是键的取值。 如果指定 items,则所列出的键将被投射到指定的路径中,且不会显示未列出的键。 如果指定的键不在 Secret 中,则卷设置将出错,除非对应的键被标记为可选。 路径必须是相对路径,不能包含 “..” 路径,也不能以 “..” 开头。

  • downwardAPI (DownwardAPIVolumeSource)

    downwardAPI 表示有关 Pod 的 Downward API,用来填充此卷。

    DownwardAPIVolumeSource 表示包含 Downward API 信息的一个卷。Downward API 卷支持所有权管理和 SELinux 重新打标签。

    • downwardAPI.defaultMode (int32)

      可选:默认情况下,模式位用于已创建的文件。 必须是可选的:默认情况下,模式位用于为已创建的文件设置权限。 必须是 0000 到 0777 之间的八进制值或 0 到 511 之间的十进制值。 YAML 既接受八进制值也接受十进制值,JSON 针对模式位需要十进制值。此字段默认为 0644。 路径内的目录不受此设置的影响。这可能与影响文件模式的其他选项(如 fsGroup)有冲突,且结果可以是其他模式位也被设置。

    • downwardAPI.items ([]DownwardAPIVolumeFile)

      items 是 Downward API 卷文件的列表。

  • projected (ProjectedVolumeSource)

    这是供 Secret、ConfigMap 和 Downward API 等所有资源使用的投射项。

    表示一个投射的卷源。

    • projected.defaultMode (int32)

      defaultMode 是默认情况下用于为已创建的文件设置权限的模式位。 必须是 0000 到 0777 之间的八进制值或 0 到 511 之间的十进制值。 YAML 既接受八进制值也接受十进制值,JSON 针对模式位需要十进制值。 路径内的目录不受此设置的影响。 这可能与影响文件模式的其他选项(如 fsGroup)有冲突,且结果可以是其他模式位也被设置。

    • projected.sources ([]VolumeProjection)

      sources 是卷投射的列表。

      这里的投射项目可能与其他受支持的卷类型一起进行投射。

      • projected.sources.configMap (ConfigMapProjection)

        与要投射的 ConfigMap 数据有关的 ConfigMap 信息。

        将 ConfigMap 适配到一个投射的卷中。 目标 ConfigMap 的 Data 字段的内容将以文件的形式呈现在一个被投射的卷中, 使用 data 字段中的键名作为文件名,除非 items 元素中已经填充了由键名到路径的特定映射。 请注意,这等同于没有默认模式的 ConfigMap 卷源。

        • projected.sources.configMap.items ([]KeyToPath)

          如果未指定 items,则所引用的 ConfigMap 的 data 字段中的每个键值对将作为一个文件被投射到卷中, 这个文件的名称是键名,而文件的内容是键的取值。 如果指定 items,则所列出的键将被投射到指定的路径中,且不会显示未列出的键。 如果指定的键不在 ConfigMap 中,则卷设置将出错,除非对应的键被标记为可选。 路径必须是相对路径,不能包含 “..” 路径,也不能以 “..” 开头。

      • projected.sources.downwardAPI (DownwardAPIProjection)

        与要投射的 downward API 数据有关的 downward API 信息。

        表示投射到投射卷的 Downward API 信息。请注意,这等同于没有默认模式的 downwardAPI 卷源。

        • projected.sources.downwardAPI.items ([]DownwardAPIVolumeFile)

          items 是 DownwardAPIVolume 文件的列表。

      • projected.sources.secret (SecretProjection)

        与要投射的 Secret 数据有关的 Secret 信息。

        将 Secret 适配到一个投射卷中。 目标 Secret 的 data 字段的内容将以文件的形式呈现在一个投射卷中,使用 data 字段中的键名作为文件名。 请注意,这等同于没有默认模式的 Secret 卷源。

        • projected.sources.secret.optional (boolean)

          optional 字段指定是否 Secret 或其键必须已经定义。

        • projected.sources.secret.items ([]KeyToPath)

          如果未指定 items,则所引用的 Secret 的 data 字段中的每个键值对将作为一个文件被投射到卷中, 这个文件的名称是键名,而文件的内容是键的取值。 如果指定 items,则所列出的键将被投射到指定的路径中,且不会显示未列出的键。 如果指定的键不在 Secret 中,则卷设置将出错,除非对应的键被标记为可选。 路径必须是相对路径,不能包含 “..” 路径,也不能以 “..” 开头。

      • projected.sources.serviceAccountToken (ServiceAccountTokenProjection)

        serviceAccountToken 是与要投射的服务账号令牌数据有关的信息。

        ServiceAccountTokenProjection 表示一个投射的服务账号令牌卷。 这种投射可用于将服务账号令牌插入到 Pod 运行时文件系统,供访问 API(Kubernetes API Server 或其他)使用。

        • projected.sources.serviceAccountToken.path (string),必需

          path 是相对于令牌投射目标文件的挂载点的路径。

        • projected.sources.serviceAccountToken.audience (string)

          audience 是令牌的目标受众。 令牌的接收方必须用令牌受众中指定的一个标识符来标识自己,否则应拒绝此令牌。 受众默认为 apiserver 的标识符。

        • projected.sources.serviceAccountToken.expirationSeconds (int64)

          expirationSeconds 是所请求的服务账号令牌的有效期。 当令牌即将到期时,kubelet 卷插件将主动轮换服务账号令牌。 如果令牌超过其生存时间的 80% 或令牌超过 24 小时,kubelet 将开始尝试轮换令牌。 默认为 1 小时且必须至少为 10 分钟。

本地/临时目录

持久卷

  • awsElasticBlockStore (AWSElasticBlockStoreVolumeSource)

    awsElasticBlockStore 表示挂接到 kubelet 的主机随后暴露给 Pod 的一个 AWS Disk 资源。更多信息: https://kubernetes.io/zh-cn/docs/concepts/storage/volumes#awselasticblockstore

    表示 AWS 上的 Persistent Disk 资源。挂载到一个容器之前 AWS EBS 磁盘必须存在。 该磁盘还必须与 kubelet 位于相同的 AWS 区域中。AWS EBS 磁盘只能以读/写一次进行挂载。 AWS EBS 卷支持所有权管理和 SELinux 重新打标签。

    • awsElasticBlockStore.partition (int32)

      partition 是你要挂载的卷中的分区。如果省略,则默认为按卷名称进行挂载。例如:对于卷 /dev/sda1, 将分区指定为 “1”。类似地,/dev/sda 的卷分区为 “0”(或可以将属性留空)。

    • awsElasticBlockStore.readOnly (boolean)

      readOnly 值为 true 将使得卷挂载被强制设置为 readOnly。更多信息: https://kubernetes.io/zh-cn/docs/concepts/storage/volumes#awselasticblockstore

  • azureDisk (AzureDiskVolumeSource)

    azureDisk 表示挂载到主机上并绑定挂载到 Pod 上的 Azure 数据盘。

    azureDisk 表示挂载到主机上并绑定挂载到 Pod 上的 Azure 数据盘。

    • azureDisk.diskName (string),必需

      diskName 是 Blob 存储中数据盘的名称。

    • azureDisk.diskURI (string),必需

      diskURI 是 Blob 存储中数据盘的 URI。

    • azureDisk.cachingMode (string)

      cachingMode 是主机缓存(Host Caching)模式:None、Read Only、Read Write。

    • azureDisk.fsType (string)

      fsType 是要挂载的文件系统类型。必须是主机操作系统所支持的文件系统类型之一。 例如 “ext4”、“xfs”、“ntfs”。如果未指定,则隐式推断为 “ext4”。

    • azureDisk.kind (string)

      kind 预期值包括:

      • Shared:每个存储帐户多个 Blob 磁盘;
      • Dedicated:每个存储帐户单个 Blob 磁盘;
      • Managed:azure 托管的数据盘(仅托管的可用性集合中)。

      默认为 Shared。

    • azureDisk.readOnly (boolean)

      readOnly 默认为 false(读/写)。此处的 readOnly 将强制设置卷挂载中的 readOnly 属性。

  • azureFile (AzureFileVolumeSource)

    azureDisk 表示挂载到主机上并绑定挂载到 Pod 上的 Azure File Service。

    azureFile 表示挂载到主机上并绑定挂载到 Pod 上的 Azure File Service。

    • azureFile.secretName (string),必需

      secretName 是包含 Azure 存储账号名称和主键的 Secret 的名称。

    • azureFile.shareName (string),必需

      shareName 是 Azure 共享名称。

    • azureFile.readOnly (boolean)

      readOnly 默认为 false(读/写)。此处的 readOnly 将强制设置卷挂载中的 readOnly 属性。

  • cinder (CinderVolumeSource)

    cinder 表示 kubelet 主机上挂接和挂载的 Cinder 卷。更多信息: https://examples.k8s.io/mysql-cinder-pd/README.md

    表示 Openstack 中的一个 Cinder 卷资源。挂载到一个容器之前 Cinder 卷必须已经存在。 该卷还必须与 kubelet 位于相同的地区中。cinder 卷支持所有权管理和 SELinux 重新打标签。

    • cinder.fsType (string)

      fsType 是要挂载的文件系统类型。必须是主机操作系统所支持的文件系统类型之一。例如:“ext4”、“xfs”、“ntfs”。 如果未指定,则隐式推断为“ext4”。更多信息: https://examples.k8s.io/mysql-cinder-pd/README.md

    • cinder.readOnly (boolean)

      readOnly 默认为 false(读/写)。此处的 readOnly 将强制设置卷挂载中的 readOnly 属性。 更多信息: https://examples.k8s.io/mysql-cinder-pd/README.md

    • cinder.secretRef (LocalObjectReference)

      secretRef 是可选的。指向 Secret 对象,内含的参数用于连接到 OpenStack。

  • csi (CSIVolumeSource)

    csi 表示由某个外部容器存储接口(Container Storage Interface,CSI)驱动处理的临时存储(Beta 特性)。

    表示要挂载的卷的源位置,由外部 CSI 驱动进行管理。

    • csi.driver (string),必需

      driver 是处理此卷的 CSI 驱动的名称。咨询你的管理员以获取在集群中注册的正确名称。

    • csi.fsType (string)

      要挂载的 fsType。例如 “ext4”、“xfs”、“ntfs”。 如果未提供,则将空值传递给关联的 CSI 驱动,以便决定要应用的默认文件系统。

    • csi.nodePublishSecretRef (LocalObjectReference)

      nodePublishSecretRef 是对包含敏感信息的 Secret 对象的引用, 该 Secret 对象将被传递到 CSI 驱动以完成 CSI NodePublishVolume 和 NodeUnpublishVolume 调用。 此字段是可选的,如果不需要 Secret,则此字段可以为空。 如果 Secret 对象包含多个 Secret,则所有 Secret 引用将被传递。

    • csi.readOnly (boolean)

      readOnly 指定供卷使用的只读配置。默认为 false(读/写)。

    • csi.volumeAttributes (map[string]string)

      volumeAttributes 存储传递给 CSI 驱动且特定于驱动的属性。查阅你的驱动文档,了解支持的值。

  • ephemeral (EphemeralVolumeSource)

    ephemeral 表示由一个集群存储驱动处理的卷。此卷的生命周期与定义该卷的 Pod 相关联。 Pod 启动前创建此卷,Pod 移除时删除此卷。

    使用此字段的情形包括: a) 仅在 Pod 运行时才需要此卷, b) 需要从快照恢复或容量跟踪等正常卷的功能特性, c) 通过存储类指定存储驱动,以及 d) 存储驱动支持通过 PersistentVolumeClaim 进行动态卷制备 (有关此卷类型和 PersistentVolumeClaim 之间连接的更多信息,请参考 EphemeralVolumeSource)。

    对于持续时间超过单个 Pod 生命周期的卷,使用 PersistentVolumeClaim 或某种特定于供应商的 API。

    如果打算以这种方式使用 CSI 驱动,则将 CSI 用于轻量级本地临时卷。更多的相关信息,请参考驱动文档。

    一个 Pod 可以同时使用临时卷和持久卷这两种类别的卷。

    表示由一个正常存储驱动处理的临时卷。

    • ephemeral.volumeClaimTemplate (PersistentVolumeClaimTemplate)

      将用于创建独立的 PVC 以制备卷。 嵌入了 EphemeralVolumeSource 的 Pod 将是 PVC 的所有者,即 PVC 将与 Pod 一起删除。 PVC 的名称将是 <pod 名称>-<卷名称>,其中 <卷名称> 是来自 PodSpec.Volumes 数组条目的名称。 如果串联的名称对于 PVC 无效(例如太长),则 Pod 验证将拒绝该 Pod。

      如果具有此名称的现有 PVC 不属于此 Pod,则这一 PVC 将 不会 被用于此 Pod,以避免错误地使用不相关的卷。 如果出现这种情况,Pod 的启动操作会被阻塞直到不相关的 PVC 被移除。 如果 Pod 准备使用这样一个预先创建的 PVC,那么一旦此 Pod 出现,就必须更新 PVC, 将其属主引用指向该 Pod。通常没有必要这样做,但这对手动重构损坏的集群时可能很有用。

      此字段是只读的,PVC 被创建后 Kubernetes 不会对其进行任何更改。

      必需,不能为 nil。

      PersistentVolumeClaimTemplate 用于作为 EphemeralVolumeSource 的一部分生成 PersistentVolumeClaim 对象。

      • ephemeral.volumeClaimTemplate.spec (PersistentVolumeClaimSpec),必需

        PersistentVolumeClaim 的规约。整个规约的内容将被原封不动地复制到从此模板创建的 PVC 中。 与 PersistentVolumeClaim 相同的字段在此处也有效。

      • ephemeral.volumeClaimTemplate.metadata (ObjectMeta)

        可能包含一些标签和注解,在创建 PVC 时,这些数据会被复制到 PVC 中。 在验证期间,其他字段都不允许设置,即便设置也会在验证阶段被拒绝。

  • fc (FCVolumeSource)

    fc 表示挂接到 kubelet 的主机随后暴露给 Pod 的一个 Fibre Channel 资源。

    表示 Fibre Channel 卷。Fibre Channel 卷只能以读/写一次进行挂载。 Fibre Channel 卷支持所有权管理和 SELinux 重新打标签。

    • fc.fsType (string)

      fsType 是要挂载的文件系统类型。必须是主机操作系统所支持的文件系统类型之一。 例如 “ext4”、“xfs”、“ntfs”。如果未指定,则隐式推断为 “ext4”。

    • fc.lun (int32)

      lun 是可选的:FC 目标 lun 编号。

    • fc.readOnly (boolean)

      readOnly 是可选的。默认为 false(读/写)。此处的 readOnly 将强制设置卷挂载中的 readOnly 属性。

    • fc.targetWWNs ([]string)

      targetWWNs 是可选的。FC 目标全球名称(WWN)。

    • fc.wwids ([]string)

      wwids 是可选的。FC 卷全球识别号(wwids)。 必须设置 wwids 或 targetWWNs 及 lun 的组合,但不能同时设置两者。

  • flexVolume (FlexVolumeSource)

    flexVolume 表示使用基于 exec 的插件制备/挂接的通用卷资源。

    flexVolume 表示使用基于 exec 的插件制备/挂接的通用卷资源。

    • flexVolume.driver (string),必需

      driver 是供此卷使用的驱动的名称。

    • flexVolume.fsType (string)

      fsType 是要挂载的文件系统类型。必须是主机操作系统所支持的文件系统类型之一。例如 “ext4”、“xfs”、“ntfs”。 默认的文件系统取决于 flexVolume 脚本。

    • flexVolume.options (map[string]string)

      options 是可选的。此字段包含额外的命令选项(如果有)。

    • flexVolume.readOnly (boolean)

      readOnly 是可选的。默认为 false(读/写)。此处的 readOnly 将强制设置卷挂载中的 readOnly 属性。

    • flexVolume.secretRef (LocalObjectReference)

      secretRef 是可选的。secretRef 是对包含敏感信息的 Secret 对象的引用,该 Secret 会被传递到插件脚本。 如果未指定 Secret 对象,则此字段可以为空。如果 Secret 对象包含多个 Secret,则所有 Secret 被传递到插件脚本。

  • flocker (FlockerVolumeSource)

    flocker 表示挂接到一个 kubelet 主机的 Flocker 卷。Flocker 卷依赖于正在运行的 Flocker 控制服务。

    表示 Flocker 代理挂载的 Flocker 卷。应设置一个且仅设置 datasetName 和 datasetUUID 中的一个。 Flocker 卷不支持所有权管理或 SELinux 重新打标签。

    • flocker.datasetName (string)

      datasetName 是存储为元数据的数据集的名称。Flocker 数据集的名称应视为已弃用。

    • flocker.datasetUUID (string)

      datasetUUID 是数据集的 UUID。这是 Flocker 数据集的唯一标识符。

  • iscsi (ISCSIVolumeSource)

    iscsi 表示挂接到 kubelet 的主机随后暴露给 Pod 的一个 ISCSI Disk 资源。更多信息: https://examples.k8s.io/volumes/iscsi/README.md

    表示一个 ISCSI 磁盘。ISCSI 卷只能以读/写一次进行挂载。ISCSI 卷支持所有权管理和 SELinux 重新打标签。

    • iscsi.iqn (string),必需

      iqn 是目标 iSCSI 限定名称。

    • iscsi.lun (int32),必需

      lun 表示 iSCSI 目标逻辑单元号。

    • iscsi.targetPortal (string),必需

      targetPortal 是 iSCSI 目标门户。 如果不是默认端口(通常是 TCP 端口 860 和 3260),则 Portal 为 IP 或 ip_addr:port。

    • iscsi.chapAuthDiscovery (boolean)

      chapAuthDiscovery 定义是否支持 iSCSI Discovery CHAP 身份认证。

    • iscsi.chapAuthSession (boolean)

      chapAuthSession 定义是否支持 iSCSI Session CHAP 身份认证。

    • iscsi.fsType (string)

      fsType 是你要挂载的卷的文件系统类型。提示:确保主机操作系统支持此文件系统类型。 例如:“ext4”、“xfs”、“ntfs”。如果未指定,则隐式推断为 “ext4”。更多信息: https://kubernetes.io/zh-cn/docs/concepts/storage/volumes#iscsi

    • iscsi.initiatorName (string)

      initiatorName 是自定义的 iSCSI 发起程序名称(iSCSI Initiator Name)。 如果同时用 iscsiInterface 指定 initiatorName,将为连接创建新的 iSCSI 接口 <目标门户>:<卷名称>。

    • iscsi.iscsiInterface (string)

      iscsiInterface 是使用 iSCSI 传输的接口名称。默认为 “default”(tcp)。

    • iscsi.portals ([]string)

      portals 是 iSCSI 目标门户列表(iSCSI Target Portal List)。 如果不是默认端口(通常是 TCP 端口 860 和 3260),则 Portal 为 IP 或 ip_addr:port。

    • iscsi.readOnly (boolean)

      此处的 readOnly 将强制设置卷挂载中的 readOnly 属性。默认为 false。

    • iscsi.secretRef (LocalObjectReference)

      secretRef 是 iSCSI 目标和发起程序身份认证所用的 CHAP Secret。

  • photonPersistentDisk (PhotonPersistentDiskVolumeSource)

    photonPersistentDisk 表示 kubelet 主机上挂接和挂载的 PhotonController 持久磁盘。

    表示 Photon Controller 持久磁盘资源。

    • photonPersistentDisk.pdID (string),必需

      pdID 是标识 Photon Controller 持久磁盘的 ID。

    • photonPersistentDisk.fsType (string)

      fsType 是要挂载的文件系统类型。必须是主机操作系统所支持的文件系统类型之一。 例如 “ext4”、“xfs”、“ntfs”。如果未指定,则隐式推断为 “ext4”。

  • portworxVolume (PortworxVolumeSource)

    portworxVolume 表示 kubelet 主机上挂接和挂载的 portworx 卷。

    PortworxVolumeSource 表示 Portworx 卷资源。

    • portworxVolume.volumeID (string),必需

      volumeID 唯一标识 Portworx 卷。

    • portworxVolume.fsType (string)

      fSType 表示要挂载的文件系统类型。必须是主机操作系统支持的文件系统类型。例如 “ext4”、“xfs”。 如果未指定,则隐式推断为 “ext4”。

    • portworxVolume.readOnly (boolean)

      readOnly 默认为 false(读/写)。此处的 readOnly 将强制设置卷挂载中的 readOnly 属性。

  • quobyte (QuobyteVolumeSource)

    quobyte 表示在共享 Pod 生命周期的主机上挂载的 Quobyte。

    表示在 Pod 的生命周期内持续的 Quobyte 挂载。Quobyte 卷不支持所有权管理或 SELinux 重新打标签。

    • quobyte.registry (string),必需

      registry 表示将一个或多个 Quobyte Registry 服务指定为 host:port 对的字符串形式 (多个条目用英文逗号分隔),用作卷的中央注册表。

    • quobyte.volume (string),必需

      volume 是按名称引用已创建的 Quobyte 卷的字符串。

    • quobyte.group (string)

      group 是将卷访问映射到的组。默认为无组。

    • quobyte.readOnly (boolean)

      此处 readOnly 将强制使用只读权限挂载 Quobyte 卷。默认为 false。

    • quobyte.tenant (string)

      tenant 拥有 Backend Used 中给定的 Quobyte 卷,随动态制备的 Quobyte 卷一起使用,值由插件设置。

    • quobyte.user (string)

      user 是将卷访问映射到的用户。默认为 serivceaccount 用户。

  • scaleIO (ScaleIOVolumeSource)

    scaleIO 表示 Kubernetes 节点上挂接和挂载的 ScaleIO 持久卷。

    ScaleIOVolumeSource 表示一个 ScaleIO 持久卷。

    • scaleIO.gateway (string),必需

      gateway 是 ScaleIO API 网关的主机地址。

    • scaleIO.secretRef (LocalObjectReference),必需

      secretRef 引用到 ScaleIO 用户的 Secret 和其他敏感信息。如果未提供此项,则 Login 操作将失败。

    • scaleIO.system (string),必需

      system 是存储系统的名称,与 ScaleIO 中的配置相同。

    • scaleIO.fsType (string)

      fsType 是要挂载的文件系统类型。必须是主机操作系统所支持的文件系统类型之一。例如 “ext4”、“xfs”、“ntfs”。默认为 “xfs”。

    • scaleIO.protectionDomain (string)

      protectionDomain 是 ScaleIO 保护域(ScaleIO Protection Domain)的名称,用于已配置的存储。

    • scaleIO.readOnly (boolean)

      readOnly 默认为 false(读/写)。此处的 readOnly 将强制设置卷挂载中的 readOnly 属性。

    • scaleIO.sslEnabled (boolean)

      sslEnabled 标志启用/禁用与网关的 SSL 通信,默认为 false。

    • scaleIO.storageMode (string)

      storageMode 指示卷所用的存储应是 ThickProvisioned 或 ThinProvisioned。默认为 ThinProvisioned。

    • scaleIO.storagePool (string)

      storagePool 是与保护域关联的 ScaleIO Storage Pool。

    • scaleIO.volumeName (string)

      volumeName 是在与此卷源关联的 ScaleIO 系统中已创建的卷的名称。

  • storageos (StorageOSVolumeSource)

    storageOS 表示 Kubernetes 节点上挂接和挂载的 StorageOS 卷。

    表示 StorageOS 持久卷资源。

    • storageos.fsType (string)

      fsType 是要挂载的文件系统类型。必须是主机操作系统所支持的文件系统类型之一。 例如 “ext4”、“xfs”、“ntfs”。如果未指定,则隐式推断为 “ext4”。

    • storageos.readOnly (boolean)

      readOnly 默认为 false(读/写)。此处的 readOnly 将强制设置卷挂载中的 readOnly 属性。

    • storageos.secretRef (LocalObjectReference)

      secretRef 指定用于获取 StorageOS API 凭据的 Secret。如果未指定,则将尝试使用默认值。

    • storageos.volumeName (string)

      volumeName 是 StorageOS 卷的人类可读名称。这些卷名称在一个名字空间内是唯一的。

    • storageos.volumeNamespace (string)

      volumeNamespace 指定 StorageOS 内卷的作用域。如果未指定名字空间,则将使用 Pod 的名字空间。 这个设置使得 Kubernetes 的名字作用域可以在 StorageOS 内进行映射,实现更紧密的集成。 将 volumeName 设为任何名称以重载默认的行为。如果你未在 StorageOS 内使用名字空间,则设为“default”。 将创建 StorageOS 内预先不存在的名字空间。

  • vsphereVolume (VsphereVirtualDiskVolumeSource)

    vsphereVolume 表示 kubelet 主机上挂接和挂载的 vSphere 卷。

    表示 vSphere 卷资源。

    • vsphereVolume.volumePath (string),必需

      volumePath 是标识 vSphere 卷 vmdk 的路径。

    • vsphereVolume.fsType (string)

      fsType 是要挂载的文件系统类型。必须是主机操作系统所支持的文件系统类型之一。 例如 “ext4”、“xfs”、“ntfs”。如果未指定,则隐式推断为 “ext4”。

    • vsphereVolume.storagePolicyID (string)

      storagePolicyID 是与 StoragePolicyName 关联的基于存储策略的管理(SPBM)配置文件 ID。

    • vsphereVolume.storagePolicyName (string)

      storagePolicyName 是基于存储策略的管理(SPBM)配置文件名称。

已弃用

  • gitRepo (GitRepoVolumeSource)

    gitRepo 表示特定修订版本的 git 仓库。(注意:GitRepo 已被弃用。)如果与为某容器提速 Git 仓库, 可以先将 emptyDir 挂载到 InitContainer 上,由后者使用 git 克隆仓库,然后将 emptyDir 挂载到 Pod 的容器中。

    表示用 Git 仓库的内容进行填充的一个卷。Git 仓库卷不支持所有权管理。Git 仓库卷支持 SELinux 重新打标签。 (注意:GitRepo 已被弃用。)如果与为某容器提速 Git 仓库, 可以先将 emptyDir 挂载到 InitContainer 上,由后者使用 git 克隆仓库,然后将 emptyDir 挂载到 Pod 的容器中。

    • gitRepo.repository (string),必需

      repository 是仓库的 URL。

    • gitRepo.directory (string)

      directory 是目标目录的名称。不得包含 “..” 或以 “..” 开头。如果提供了 “.”,则卷目录将是 Git 仓库。 否则,如果指定,卷将用给定名称的子目录中存放 Git 仓库。

    • gitRepo.revision (string)

      revision 是指定修订版本的提交哈希值。

DownwardAPIVolumeFile

DownwardAPIVolumeFile 表示创建包含 Pod 字段的文件的信息。


  • path (string),必需

    必需。path 是要创建的文件的相对路径名称。不得使用绝对路径,也不得包含 “..” 路径。 必须用 UTF-8 进行编码。相对路径的第一项不得用 “..” 开头。

  • fieldRef (ObjectFieldSelector)

    必需。选择 Pod 的字段:仅支持注解、标签、名称和名字空间。

  • mode (int32)

    可选:模式位用于设置文件的权限,必须是 0000 到 0777 之间的八进制值或 0 到 511 之间的十进制值。 YAML 既接受八进制值也接受十进制值,JSON 针对模式位需要十进制值。 如果未指定,则将使用卷 defaultMode。 这可能与影响文件模式的其他选项(如 fsGroup)有冲突,且结果可以是其他模式位也被设置。

  • resourceFieldRef (ResourceFieldSelector)

    选择容器的资源:目前仅支持资源限制与请求(limits.cpu、limits.memory、requests.cpu 和 requests.memory)。

KeyToPath

将一个字符串键映射到卷中的一个路径。


  • key (string),必需

    key 是要投射的键。

  • path (string),必需

    path 是将键映射到的文件的相对路径。不能是绝对路径。不能包含路径元素 “..”。不能以字符串 “..” 开头。

  • mode (int32)

    mode 是可选的:模式位用于为文件设置权限。必须是 0000 到 0777 之间的八进制值或 0 到 511 之间的十进制值。 YAML 既接受八进制值也接受十进制值,JSON 针对模式位需要十进制值。 如果未指定,则将使用卷 defaultMode。 这可能与影响文件模式的其他选项(如 fsGroup)有冲突,且结果可以是其他模式位也被设置。

6.5.3.4 - PersistentVolumeClaim

PersistentVolumeClaim 是用户针对一个持久卷的请求和申领。

apiVersion: v1

import "k8s.io/api/core/v1"

PersistentVolumeClaim

PersistentVolumeClaim 是用户针对一个持久卷的请求和申领。


  • apiVersion: v1

  • kind: PersistentVolumeClaim

PersistentVolumeClaimSpec

PersistentVolumeClaimSpec 描述存储设备的常用参数,并支持通过 source 来设置特定于提供商的属性。


  • resources (ResourceRequirements)

    resources 表示卷应拥有的最小资源。 如果启用了 RecoverVolumeExpansionFailure 功能特性,则允许用户指定这些资源要求, 此值必须低于之前的值,但必须高于申领的状态字段中记录的容量。更多信息: https://kubernetes.io/zh-cn/docs/concepts/storage/persistent-volumes#resources

    ResourceRequirements 描述计算资源要求。

    • resources.claims ([]ResourceClaim)

      集合:键 name 的唯一值将在合并期间被保留

      claims 列出了此容器使用的、在 spec.resourceClaims 中定义的资源的名称。

      这是一个 Alpha 字段,需要启用 DynamicResourceAllocation 特性门控。

      此字段是不可变的。

      <a name="ResourceClaim"></a>
      

      ResourceClaim 引用 PodSpec.ResourceClaims 中的一个条目。

      • resources.claims.name (string),必需

        对于使用此字段的 Pod,name 必须与 pod.spec.resourceClaims 中的一个条目的名称匹配。

    limits 描述允许的最大计算资源量。更多信息: https://kubernetes.io/zh-cn/docs/concepts/configuration/manage-resources-containers/

  • volumeName (string)

    volumeName 是对此申领所对应的 PersistentVolume 的绑定引用。

  • storageClassName (string)

    storageClassName 是此申领所要求的 StorageClass 名称。更多信息: https://kubernetes.io/zh-cn/docs/concepts/storage/persistent-volumes#class-1

  • volumeMode (string)

    volumeMode 定义申领需要哪种类别的卷。当申领规约中未包含此字段时,意味着取值为 Filesystem。

Beta 级别

  • dataSource (TypedLocalObjectReference)

    dataSource 字段可用于二选一:

    • 现有的 VolumeSnapshot 对象(snapshot.storage.k8s.io/VolumeSnapshot)

    • 现有的 PVC (PersistentVolumeClaim)

    如果制备器或外部控制器可以支持指定的数据源,则它将根据指定数据源的内容创建新的卷。 当 AnyVolumeDataSource 特性门控被启用时,dataSource 内容将被复制到 dataSourceRef, 当 dataSourceRef.namespace 未被指定时,dataSourceRef 内容将被复制到 dataSource。 如果名字空间被指定,则 dataSourceRef 不会被复制到 dataSource。

  • dataSourceRef (TypedObjectReference)

    dataSourceRef 指定一个对象,当需要非空卷时,可以使用它来为卷填充数据。 此字段值可以是来自非空 API 组(非核心对象)的任意对象,或一个 PersistentVolumeClaim 对象。 如果设置了此字段,则仅当所指定对象的类型与所安装的某些卷填充器或动态制备器匹配时,卷绑定才会成功。 此字段将替换 dataSource 字段的功能,因此如果两个字段非空,其取值必须相同。 为了向后兼容,当未在 dataSourceRef 中指定名字空间时, 如果(dataSource 和 dataSourceRef)其中一个字段为空且另一个字段非空,则两个字段将被自动设为相同的值。 在 dataSourceRef 中指定名字空间时,dataSource 未被设置为相同的值且必须为空。 dataSource 和 dataSourceRef 之间有三个重要的区别:

    • dataSource 仅允许两个特定类型的对象,而 dataSourceRef 允许任何非核心对象以及 PersistentVolumeClaim 对象。
    • dataSource 忽略不允许的值(这类值会被丢弃),而 dataSourceRef 保留所有值并在指定不允许的值时产生错误。
    • dataSource 仅允许本地对象,而 dataSourceRef 允许任意名字空间中的对象。

    (Beta) 使用此字段需要启用 AnyVolumeDataSource 特性门控。 (Alpha) 使用 dataSourceRef 的名字空间字段需要启用 CrossNamespaceVolumeDataSource 特性门控。

    • dataSourceRef.kind (string),必需

    kind 是正被引用的资源的类型。

    • dataSourceRef.name (string),必需

      name 是正被引用的资源的名称。

    • dataSourceRef.apiGroup (string)

    apiGroup 是正被引用的资源的组。如果 apiGroup 未被指定,则指定的 kind 必须在核心 API 组中。 对于任何第三方类型,apiGroup 是必需的。

    • dataSourceRef.namespace (string)

      namespace 是正被引用的资源的名字空间。请注意,当指定一个名字空间时, 在引用的名字空间中 gateway.networking.k8s.io/ReferenceGrant 对象是必需的, 以允许该名字空间的所有者接受引用。有关详细信息,请参阅 ReferenceGrant 文档。 (Alpha) 此字段需要启用 CrossNamespaceVolumeDataSource 特性门控。

PersistentVolumeClaimStatus

PersistentVolumeClaimStatus 是持久卷申领的当前状态。


  • allocatedResourceStatuses (map[string]string)

    allocatedResourceStatuses 存储为给定 PVC 而调整大小的资源的状态。键名遵循标准的 Kubernetes 标签语法。 有效值为:

    • 未加前缀的键:
      • storage - 卷的容量。
    • 自定义资源必须使用实现定义的带前缀的名称,如 "example.com/my-custom-resource"。 除上述值之外,未加前缀或具有 kubernetes.io 前缀的键被视为保留键,因此不能使用。

    ClaimResourceStatus 可以处于以下任一状态:

    • ControllerResizeInProgress:大小调整控制器开始在控制平面中调整卷大小时所设置的状态。
    • ControllerResizeFailed:大小调整控制器出现致命错误导致大小调整失败时所设置的状态。
    • NodeResizePending:大小调整控制器已完成对卷大小的调整但需要在节点上进一步调整卷大小时的状态。
    • NodeResizeInProgress:kubelet 开始调整卷大小时所设置的状态。
    • NodeResizeFailed:kubelet 在出现致命错误而导致大小调整失败时所设置的状态。 临时错误不会设置 NodeResizeFailed。

    例如:如果扩展 PVC 以获取更多的容量,则此字段可以是以下状态之一:

    • pvc.status.allocatedResourceStatus['storage'] = "ControllerResizeInProgress"
      • pvc.status.allocatedResourceStatus['storage'] = "ControllerResizeFailed"
      • pvc.status.allocatedResourceStatus['storage'] = "NodeResizePending"
      • pvc.status.allocatedResourceStatus['storage'] = "NodeResizeInProgress"
      • pvc.status.allocatedResourceStatus['storage'] = "NodeResizeFailed" 当未设置此字段时,表示没有针对给定 PVC 执行大小调整操作。

    如果控制器收到具有先前未知的 resourceName 或 ClaimResourceStatus 的 PVC 更新, 则该控制器应忽略此项更新才能按预期工作。例如,仅负责调整卷容量大小的控制器应忽略更改与 PVC 关联的其他合法资源的 PVC 更新。

    这是一个 Alpha 字段,需要启用 RecoverVolumeExpansionFailure 功能特性。

  • allocatedResources (map[string]Quantity)

    allocatedResources 跟踪分配给 PVC 的资源,包括其容量。键名遵循标准的 Kubernetes 标签语法。 有效值为:

    • 未加前缀的键:
      • storage - 卷的容量。
    • 自定义资源必须使用实现定义的带前缀的名称,如 "example.com/my-custom-resource"。 除上述值之外,未加前缀或具有 kubernetes.io 前缀的键被视为保留键,因此不能使用。

    当出现卷扩充操作请求时,此字段可能大于实际的容量。 就存储配额而言,将使用 allocatedResources 和 PVC.spec.resources 二者中的更大值。 如果未设置 allocatedResources,则 PVC.spec.resources 单独用于配额计算。 如果减小一个卷扩充容量请求,则仅当没有正在进行的扩充操作且实际卷容量等于或小于请求的容量时, 才会减小 allocatedResources。

    如果控制器收到具有先前未知的 resourceName 的 PVC 更新,则该控制器应忽略此项更新才能按预期工作。 例如,仅负责调整卷容量大小的控制器应忽略更改与 PVC 关联的其他合法资源的 PVC 更新。

    这是一个 Alpha 字段,需要启用 RecoverVolumeExpansionFailure 功能特性。

  • capacity (map[string]Quantity)

    capacity 表示底层卷的实际资源。

  • conditions ([]PersistentVolumeClaimCondition)

    补丁策略:按照键 type 合并

    conditions 是持久卷声明的当前的状况。 如果正在调整底层持久卷的大小,则状况将被设为 “ResizeStarted”。

    PersistentVolumeClaimCondition 包含有关 PVC 状态的详细信息。

  • conditions.status (string),必需

  • conditions.type (string),必需

  • conditions.lastProbeTime (Time)

    lastProbeTime 是我们探测 PVC 状况的时间。

    Time 是 time.Time 的包装类,支持正确地序列化为 YAML 和 JSON。 为 time 包提供的许多工厂方法提供了包装类。

  • conditions.lastTransitionTime (Time)

    lastTransitionTime 是状况从一个状态转换为另一个状态的时间。

    Time 是 time.Time 的包装类,支持正确地序列化为 YAML 和 JSON。 为 time 包提供的许多工厂方法提供了包装类。

  • conditions.message (string)

    message 是人类可读的消息,指示有关上一次转换的详细信息。

  • conditions.reason (string)

    reason 是唯一的,它应该是一个机器可理解的简短字符串,指明上次状况转换的原因。 如果它报告 “ResizeStarted”,则意味着正在调整底层持久卷的大小。

  • phase (string)

    phase 表示 PersistentVolumeClaim 的当前阶段。

PersistentVolumeClaimList

PersistentVolumeClaimList 是 PersistentVolumeClaim 各项的列表。


  • apiVersion: v1

  • kind: PersistentVolumeClaimList

操作


get 读取指定的 PersistentVolumeClaim

HTTP 请求

GET /api/v1/namespaces/{namespace}/persistentvolumeclaims/{name}

参数

  • name (路径参数): string,必需

    PersistentVolumeClaim 的名称

  • namespace (路径参数): string,必需

    namespace

  • pretty (查询参数): string

    pretty

响应

200 (PersistentVolumeClaim): OK

401: Unauthorized

get 读取指定的 PersistentVolumeClaim 的状态

HTTP 请求

GET /api/v1/namespaces/{namespace}/persistentvolumeclaims/{name}/status

参数

  • name (路径参数): string,必需

    PersistentVolumeClaim 的名称

  • namespace (路径参数): string,必需

    namespace

  • pretty (查询参数): string

    pretty

响应

200 (PersistentVolumeClaim): OK

401: Unauthorized

list 列出或观测类别为 PersistentVolumeClaim 的对象

HTTP 请求

GET /api/v1/namespaces/{namespace}/persistentvolumeclaims

参数

响应

200 (PersistentVolumeClaimList): OK

401: Unauthorized

list 列出或观测类别为 PersistentVolumeClaim 的对象

HTTP 请求

GET /api/v1/persistentvolumeclaims

参数

响应

200 (PersistentVolumeClaimList): OK

401: Unauthorized

create 创建 PersistentVolumeClaim

HTTP 请求

POST /api/v1/namespaces/{namespace}/persistentvolumeclaims

参数

响应

200 (PersistentVolumeClaim): OK

201 (PersistentVolumeClaim): Created

202 (PersistentVolumeClaim): Accepted

401: Unauthorized

update 替换指定的 PersistentVolumeClaim

HTTP 请求

PUT /api/v1/namespaces/{namespace}/persistentvolumeclaims/{name}

参数

响应

200 (PersistentVolumeClaim): OK

201 (PersistentVolumeClaim): Created

401: Unauthorized

update 替换指定的 PersistentVolumeClaim 的状态

HTTP 请求

PUT /api/v1/namespaces/{namespace}/persistentvolumeclaims/{name}/status

参数

响应

200 (PersistentVolumeClaim): OK

201 (PersistentVolumeClaim): Created

401: Unauthorized

patch 部分更新指定的 PersistentVolumeClaim

HTTP 请求

PATCH /api/v1/namespaces/{namespace}/persistentvolumeclaims/{name}

参数

  • name (路径参数): string,必需

    PersistentVolumeClaim 的名称

  • namespace (路径参数): string,必需

    namespace

  • body: Patch,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • force (查询参数): boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (PersistentVolumeClaim): OK

201 (PersistentVolumeClaim): Created

401: Unauthorized

patch 部分更新指定的 PersistentVolumeClaim 的状态

HTTP 请求

PATCH /api/v1/namespaces/{namespace}/persistentvolumeclaims/{name}/status

参数

  • name (路径参数): string,必需

    PersistentVolumeClaim 的名称

  • namespace (路径参数): string,必需

    namespace

  • body: Patch,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • force (查询参数): boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (PersistentVolumeClaim): OK

201 (PersistentVolumeClaim): Created

401: Unauthorized

delete 删除 PersistentVolumeClaim

HTTP 请求

DELETE /api/v1/namespaces/{namespace}/persistentvolumeclaims/{name}

参数

响应

200 (PersistentVolumeClaim): OK

202 (PersistentVolumeClaim): Accepted

401: Unauthorized

deletecollection 删除 PersistentVolumeClaim 的集合

HTTP 请求

DELETE /api/v1/namespaces/{namespace}/persistentvolumeclaims

参数

响应

200 (Status): OK

401: Unauthorized

6.5.3.5 - PersistentVolume

PersistentVolume (PV) 是管理员制备的一个存储资源。

apiVersion: v1

import "k8s.io/api/core/v1"

PersistentVolume

PersistentVolume (PV) 是管理员制备的一个存储资源。它类似于一个节点。更多信息: https://kubernetes.io/zh-cn/docs/concepts/storage/persistent-volumes


  • apiVersion: v1

  • kind: PersistentVolume

PersistentVolumeSpec

PersistentVolumeSpec 是持久卷的规约。


  • nodeAffinity (VolumeNodeAffinity)

    nodeAffinity 定义可以从哪些节点访问此卷的约束限制。此字段会影响调度使用此卷的 Pod。

    VolumeNodeAffinity 定义可以从哪些节点访问此卷的约束限制。

    • nodeAffinity.required (NodeSelector)

    required 指定必须满足的硬性节点约束。

    节点选择器表示在一组节点上一个或多个标签查询结果的并集; 也就是说,它表示由节点选择器条件表示的选择器的逻辑或计算结果。

    • nodeAffinity.required.nodeSelectorTerms ([]NodeSelectorTerm),必需

      必需。节点选择器条件的列表。这些条件是逻辑或的计算结果。

      一个 null 或空的节点选择器条件不会与任何对象匹配。这些条件会按逻辑与的关系来计算。 TopologySelectorTerm 类别实现了 NodeSelectorTerm 的子集。

      • nodeAffinity.required.nodeSelectorTerms.matchExpressions ([]NodeSelectorRequirement)

        基于节点标签所设置的节点选择器要求的列表。

      • nodeAffinity.required.nodeSelectorTerms.matchFields ([]NodeSelectorRequirement)

        基于节点字段所设置的节点选择器要求的列表。

  • persistentVolumeReclaimPolicy (string)

    persistentVolumeReclaimPolicy 定义当从持久卷声明释放持久卷时会发生什么。 有效的选项为 Retain(手动创建 PersistentVolumes 所用的默认值)、 Delete(动态制备 PersistentVolumes 所用的默认值)和 Recycle(已弃用)。 Recycle 选项必须被 PersistentVolume 下层的卷插件所支持才行。更多信息: https://kubernetes.io/zh-cn/docs/concepts/storage/persistent-volumes#reclaiming

  • storageClassName (string)

    storageClassName 是这个持久卷所属于的 StorageClass 的名称。 空值意味着此卷不属于任何 StorageClass。

  • volumeMode (string)

    volumeMode 定义一个卷是带着已格式化的文件系统来使用还是保持在原始块状态来使用。 当 spec 中未包含此字段时,意味着取值为 Filesystem。

Local

  • local (LocalVolumeSource)

    local 表示具有节点亲和性的直连式存储。

    local 表示具有节点亲和性的直连式存储(Beta 特性)。

    • local.path (string),必需

      指向节点上卷的完整路径。它可以是一个目录或块设备(磁盘、分区...)。

    • local.fsType (string)

      fsType 是要挂载的文件系统类型。它仅适用于 path 是一个块设备的情况。 必须是主机操作系统所支持的文件系统类型之一。例如 “ext4”、“xfs”、“ntfs”。 在未指定的情况下,默认值是自动选择一个文件系统。

持久卷

  • awsElasticBlockStore (AWSElasticBlockStoreVolumeSource)

    awsElasticBlockStore 表示挂接到 kubelet 的主机随后暴露给 Pod 的一个 AWS Disk 资源。 更多信息: https://kubernetes.io/zh-cn/docs/concepts/storage/volumes#awselasticblockstore

    表示 AWS 上的 Persistent Disk 资源。挂载到一个容器之前 AWS EBS 磁盘必须存在。 该磁盘还必须与 kubelet 位于相同的 AWS 区域中。AWS EBS 磁盘只能以读/写一次进行挂载。 AWS EBS 卷支持所有权管理和 SELinux 重新打标签。

  • azureDisk (AzureDiskVolumeSource)

    azureDisk 表示主机上挂载的 Azure Data Disk 并绑定挂载到 Pod 上。

    azureDisk 表示主机上挂载的 Azure Data Disk 并绑定挂载到 Pod 上。

    • azureDisk.diskName (string),必需

      diskName 是 Blob 存储中数据盘的名称。

    • azureDisk.diskURI (string),必需

      diskURI 是 Blob 存储中数据盘的 URI。

    • azureDisk.cachingMode (string)

      cachingMode 是主机缓存(Host Caching)模式:None、Read Only、Read Write。

    • azureDisk.fsType (string)

      fsType 是要挂载的文件系统类型。必须是主机操作系统所支持的文件系统类型之一。 例如 “ext4”、“xfs”、“ntfs”。如果未指定,则隐式推断为 “ext4”。

    • azureDisk.kind (string)

      kind 预期值包括:

      • Shared:每个存储帐户多个 Blob 磁盘;
      • Dedicated:每个存储帐户单个 Blob 磁盘;
      • Managed:azure 托管的数据盘(仅托管的可用性集合中)。 默认为 Shared。
    • azureDisk.readOnly (boolean)

      readOnly 默认为 false(读/写)。此处 readOnly 将在 VolumeMounts 中强制设置 readOnly。

  • azureFile (AzureFilePersistentVolumeSource)

    azureDisk 表示主机上挂载的 Azure File Service 并绑定挂载到 Pod 上。

    azureFile 表示主机上挂载的 Azure File Service 并绑定挂载到 Pod 上。

    • azureFile.secretName (string),必需

      secretName 是包含 Azure 存储账号名称和主键的 Secret 的名称。

    • azureFile.shareName (string),必需

      shareName 是 azure Share Name。

    • azureFile.readOnly (boolean)

      readOnly 默认为 false(读/写)。此处 readOnly 将在 VolumeMounts 中强制设置 readOnly。

    • azureFile.secretNamespace (string)

      secretNamespace 是包含 Azure 存储账号名称和主键的 Secret 的名字空间,默认与 Pod 相同。

  • cephfs (CephFSPersistentVolumeSource)

    cephfs 表示在主机上挂载的 Ceph FS,该文件系统挂载与 Pod 的生命周期相同。

    表示在 Pod 的生命周期内持续的 Ceph Filesystem 挂载。cephfs 卷不支持所有权管理或 SELinux 重新打标签。

    • cephfs.secretRef (SecretReference)

      secretRef 是可选的。secretRef 是针对用户到身份认证 Secret 的引用,默认为空。更多信息: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it

      SecretReference 表示对某 Secret 的引用,其中包含足够的信息来访问任何名字空间中的 Secret。

      • cephfs.secretRef.name (string)

        name 在名字空间内是唯一的,以引用一个 Secret 资源。

      • cephfs.secretRef.namespace (string)

        namespace 指定一个名字空间,Secret 名称在该名字空间中必须唯一。

    • cephfs.user (string)

      user 是可选的。user 是 rados 用户名,默认为 admin。更多信息: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it

  • cinder (CinderPersistentVolumeSource)

    cinder 表示 kubelet 主机上挂接和挂载的 Cinder 卷。更多信息: https://examples.k8s.io/mysql-cinder-pd/README.md

    表示 OpenStack 中的一个 Cinder 卷资源。挂载到一个容器之前 Cinder 卷必须已经存在。 该卷还必须与 kubelet 位于相同的地区中。cinder 卷支持所有权管理和 SELinux 重新打标签。

    • cinder.secretRef (SecretReference)

      secretRef 是可选的。指向 Secret 对象,内含的参数用于连接到 OpenStack。

      SecretReference 表示对某 Secret 的引用,其中包含足够的信息来访问任何名字空间中的 Secret。

      • cinder.secretRef.name (string)

        name 在名字空间内是唯一的,以引用一个 Secret 资源。

      • cinder.secretRef.namespace (string)

        namespace 指定一个名字空间,Secret 名称在该名字空间中必须唯一。

  • csi (CSIPersistentVolumeSource)

    csi 表示由一个外部 CSI 驱动处理的存储(Beta 特性)。

    表示由一个外部 CSI 卷驱动管理的存储(Beta 特性)。

    • csi.driver (string),必需

      driver 是此卷所使用的驱动的名称。必需。

    • csi.volumeHandle (string),必需

      volumeHandle 是 CSI 卷插件的 CreateVolume 所返回的唯一卷名称,用于在所有后续调用中引用此卷。必需。

    • csi.controllerExpandSecretRef (SecretReference)

      controllerExpandSecretRef 是对包含敏感信息的 Secret 对象的引用, 该 Secret 会被传递到 CSI 驱动以完成 CSI ControllerExpandVolume 调用。 此字段是可选的,且如果不需要 Secret,则此字段可以为空。 如果 Secret 对象包含多个 Secret,则所有 Secret 被传递。

      SecretReference 表示对某 Secret 的引用,其中包含足够的信息来访问任何名字空间中的 Secret。

      • csi.controllerExpandSecretRef.name (string)

        name 在名字空间内是唯一的,以引用一个 Secret 资源。

      • csi.controllerExpandSecretRef.namespace (string)

        namespace 指定一个名字空间,Secret 名称在该名字空间中必须唯一。

    • csi.controllerPublishSecretRef (SecretReference)

      controllerPublishSecretRef 是对包含敏感信息的 Secret 对象的引用, 该 Secret 会被传递到 CSI 驱动以完成 CSI ControllerPublishVolume 和 ControllerUnpublishVolume 调用。 此字段是可选的,且如果不需要 Secret,则此字段可以为空。 如果 Secret 对象包含多个 Secret,则所有 Secret 被传递。

      SecretReference 表示对某 Secret 的引用,其中包含足够的信息来访问任何名字空间中的 Secret。

      • csi.controllerPublishSecretRef.name (string)

        name 在名字空间内是唯一的,以引用一个 Secret 资源。

      • csi.controllerPublishSecretRef.namespace (string)

        namespace 指定一个名字空间,Secret 名称在该名字空间中必须唯一。

    • csi.fsType (string)

      要挂载的 fsType。必须是主机操作系统所支持的文件系统类型之一。例如 “ext4”、“xfs”、“ntfs”。

    • csi.nodeExpandSecretRef (SecretReference)

      nodeExpandSecretRef 是对包含敏感信息的 Secret 对象的引用, 从而传递到 CSI 驱动以完成 CSI NodeExpandVolume 和 NodeUnpublishVolume 调用。 这是一个 Beta 字段,通过 CSINodeExpandSecret 特性门控默认启用。 此字段是可选的,且如果不需要 Secret,则此字段可以为空。 如果 Secret 对象包含多个 Secret,则所有 Secret 被传递。

      SecretReference 表示对某 Secret 的引用,其中包含足够的信息来访问任何名字空间中的 Secret。

      • csi.nodeExpandSecretRef.name (string)

        name 在名字空间内是唯一的,以引用一个 Secret 资源。

      • csi.nodeExpandSecretRef.namespace (string)

        namespace 指定一个名字空间,Secret 名称在该名字空间中必须唯一。

    • csi.nodePublishSecretRef (SecretReference)

      nodePublishSecretRef 是对包含敏感信息的 Secret 对象的引用, 以传递到 CSI 驱动以完成 CSI NodePublishVolume 和 NodeUnpublishVolume 调用。 此字段是可选的,且如果不需要 Secret,则此字段可以为空。 如果 Secret 对象包含多个 Secret,则所有 Secret 被传递。

      SecretReference 表示对某 Secret 的引用,其中包含足够的信息来访问任何名字空间中的 Secret。

      • csi.nodePublishSecretRef.name (string)

        name 在名字空间内是唯一的,以引用一个 Secret 资源。

      • csi.nodePublishSecretRef.namespace (string)

        namespace 定义了 Secret 名称必须唯一的空间。

    • csi.nodeStageSecretRef (SecretReference)

      nodeStageSecretRef 是对包含敏感信息的 Secret 对象的引用, 从而传递到 CSI 驱动以完成 CSI NodeStageVolume、NodeStageVolume 和 NodeUnstageVolume 调用。 此字段是可选的,且如果不需要 Secret,则此字段可以为空。 如果 Secret 对象包含多个 Secret,则所有 Secret 被传递。

      SecretReference 表示对某 Secret 的引用,其中包含足够的信息来访问任何名字空间中的 Secret。

      • csi.nodeStageSecretRef.name (string)

        name 在名字空间内是唯一的,以引用一个 Secret 资源。

      • csi.nodeStageSecretRef.namespace (string)

        namespace 指定一个名字空间,Secret 名称在该名字空间中必须唯一。

    • csi.readOnly (boolean)

      传递到 ControllerPublishVolumeRequest 的 readOnly 值。默认为 false(读/写)。

    • csi.volumeAttributes (map[string]string)

      要发布的卷的 volumeAttributes。

  • fc (FCVolumeSource)

    fc 表示挂接到 kubelet 的主机并随后暴露给 Pod 的一个光纤通道(FC)资源。

    表示光纤通道卷。光纤通道卷只能以读/写一次进行挂载。光纤通道卷支持所有权管理和 SELinux 重新打标签。

    • fc.fsType (string)

      fsType 是要挂载的文件系统类型。必须是主机操作系统所支持的文件系统类型之一。 例如 “ext4”、“xfs”、“ntfs”。如果未指定,则隐式推断为 “ext4”。

    • fc.lun (int32)

      lun 是可选的。FC 目标 lun 编号。

    • fc.readOnly (boolean)

      readOnly 是可选的。默认为 false(读/写)。 此处 readOnly 将在 VolumeMounts 中强制设置 readOnly。

    • fc.targetWWNs ([]string)

      targetWWNs 是可选的。FC 目标全球名称(WWN)。

    • fc.wwids ([]string)

      wwids 是可选的。FC 卷全球识别号(wwids)。 必须设置 wwids 或 targetWWNs 及 lun 的组合,但不能同时设置两者。

  • flexVolume (FlexPersistentVolumeSource)

    flexVolume 表示使用基于 exec 的插件制备/挂接的通用卷资源。

    FlexPersistentVolumeSource 表示使用基于 exec 的插件制备/挂接的通用持久卷资源。

    • flexVolume.driver (string),必需

      driver 是此卷所使用的驱动的名称。

    • flexVolume.fsType (string)

      fsType 是要挂载的文件系统类型。必须是主机操作系统所支持的文件系统类型之一。 例如 “ext4”、“xfs”、“ntfs”。默认的文件系统取决于 flexVolume 脚本。

    • flexVolume.options (map[string]string)

      options 是可选的。此字段包含额外的命令选项(如果有)。

    • flexVolume.readOnly (boolean)

      readOnly 是可选的。默认为 false(读/写)。 此处 readOnly 将在 VolumeMounts 中强制设置 readOnly。

    • flexVolume.secretRef (SecretReference)

      secretRef 是可选的。secretRef 是对包含敏感信息的 Secret 对象的引用,从而传递到插件脚本。 如果未指定 Secret 对象,则此字段可以为空。如果 Secret 对象包含多个 Secret,则所有 Secret 被传递到插件脚本。

      SecretReference 表示对某 Secret 的引用,其中包含足够的信息来访问任何名字空间中的 Secret。

      • flexVolume.secretRef.name (string)

        name 在名字空间内是唯一的,以引用一个 Secret 资源。

      • flexVolume.secretRef.namespace (string)

        namespace 指定一个名字空间,Secret 名称在该名字空间中必须唯一。

  • flocker (FlockerVolumeSource)

    flocker 表示挂接到 kubelet 的主机并暴露给 Pod 供其使用的 Flocker 卷。 这取决于所运行的 Flocker 控制服务。

    表示 Flocker 代理挂载的 Flocker 卷。应设置且仅设置 datasetName 和 datasetUUID 中的一个。 Flocker 卷不支持所有权管理或 SELinux 重新打标签。

    • flocker.datasetName (string)

      datasetName 是存储为元数据的数据集的名称。针对 Flocker 有关数据集的名称应视为已弃用。

    • flocker.datasetUUID (string)

      datasetUUID 是数据集的 UUID。这是 Flocker 数据集的唯一标识符。

  • iscsi (ISCSIPersistentVolumeSource)

    iscsi 表示挂接到 kubelet 的主机随后暴露给 Pod 的一个 ISCSI Disk 资源。由管理员进行制备。

    ISCSIPersistentVolumeSource 表示一个 ISCSI 磁盘。ISCSI 卷只能以读/写一次进行挂载。ISCSI 卷支持所有权管理和 SELinux 重新打标签。

    • iscsi.iqn (string),必需

      iqn 是目标 iSCSI 限定名称(Target iSCSI Qualified Name)。

    • iscsi.lun (int32),必需

      lun 是 iSCSI 目标逻辑单元号(iSCSI Target Lun)。

    • iscsi.targetPortal (string),必需

      targetPortal 是 iSCSI 目标门户(iSCSI Target Portal)。 如果不是默认端口(通常是 TCP 端口 860 和 3260),则 Portal 为 IP 或 ip_addr:port。

    • iscsi.chapAuthDiscovery (boolean)

      chapAuthDiscovery 定义是否支持 iSCSI Discovery CHAP 身份认证。

    • iscsi.chapAuthSession (boolean)

      chapAuthSession 定义是否支持 iSCSI Session CHAP 身份认证。

    • iscsi.fsType (string)

      fsType 是你要挂载的卷的文件系统类型。提示:确保主机操作系统支持此文件系统类型。 例如:“ext4”、“xfs”、“ntfs”。如果未指定,则隐式推断为 “ext4”。更多信息: https://kubernetes.io/zh-cn/docs/concepts/storage/volumes#iscsi

    • iscsi.initiatorName (string)

      initiatorName 是自定义的 iSCSI 发起程序名称(iSCSI Initiator Name)。 如果同时用 iscsiInterface 指定 initiatorName,将为连接创建新的 iSCSI 接口 <目标门户>:<卷名称>。

    • iscsi.iscsiInterface (string)

      iscsiInterface 是使用 iSCSI 传输的接口名称。默认为 “default”(tcp)。

    • iscsi.portals ([]string)

      portals 是 iSCSI 目标门户列表(iSCSI Target Portal List)。 如果不是默认端口(通常是 TCP 端口 860 和 3260),则 Portal 为 IP 或 ip_addr:port。

    • iscsi.readOnly (boolean)

      此处 readOnly 将在 VolumeMounts 中强制设置 readOnly。默认为 false。

    • iscsi.secretRef (SecretReference)

      secretRef 是 iSCSI 目标和发起程序身份认证所用的 CHAP Secret。

      SecretReference 表示对某 Secret 的引用,其中包含足够的信息来访问任何名字空间中的 Secret。

      • iscsi.secretRef.name (string)

        name 在名字空间内是唯一的,以引用一个 Secret 资源。

      • iscsi.secretRef.namespace (string)

        namespace 指定一个名字空间,Secret 名称在该名字空间中必须唯一。

  • photonPersistentDisk (PhotonPersistentDiskVolumeSource)

    photonPersistentDisk 表示 kubelet 主机上挂接和挂载的 PhotonController 持久磁盘。

    表示 Photon Controller 持久磁盘资源。

    • photonPersistentDisk.pdID (string),必需

      pdID 是标识 Photon Controller 持久磁盘的 ID。

    • photonPersistentDisk.fsType (string)

      fsType 是要挂载的文件系统类型。必须是主机操作系统所支持的文件系统类型之一。 例如 “ext4”、“xfs”、“ntfs”。如果未指定,则隐式推断为 “ext4”。

  • portworxVolume (PortworxVolumeSource)

    portworxVolume 表示 kubelet 主机上挂接和挂载的 portworx 卷。

    PortworxVolumeSource 表示 Portworx 卷资源。

    • portworxVolume.volumeID (string),必需

      volumeID 唯一标识 Portworx 卷。

    • portworxVolume.fsType (string)

      fSType 表示要挂载的文件系统类型。必须是主机操作系统所支持的文件系统类型之一。 例如 “ext4”、“xfs”。如果未指定,则隐式推断为 “ext4”。

    • portworxVolume.readOnly (boolean)

      readOnly 默认为 false(读/写)。此处 readOnly 将在 VolumeMounts 中强制设置 readOnly。

  • quobyte (QuobyteVolumeSource)

    quobyte 表示在共享 Pod 生命周期的主机上挂载的 Quobyte。

    表示在 Pod 的生命周期内持续的 Quobyte 挂载。Quobyte 卷不支持所有权管理或 SELinux 重新打标签。

    • quobyte.registry (string),必需

      registry 表示将一个或多个 Quobyte Registry 服务指定为 host:port 对的字符串形式(多个条目用英文逗号分隔),用作卷的中央注册表。

    • quobyte.volume (string),必需

      volume 是一个字符串,通过名称引用已创建的 Quobyte 卷。

    • quobyte.group (string)

      group 是将卷访问映射到的组。默认为无组。

    • quobyte.readOnly (boolean)

      此处 readOnly 将强制使用只读权限挂载 Quobyte 卷。默认为 false。

    • quobyte.tenant (string)

      后台中拥有给定 Quobyte 卷的租户。用于动态制备的 Quobyte 卷,其值由插件设置。

    • quobyte.user (string)

      user 是将卷访问映射到的用户。默认为 serivceaccount 用户。

  • scaleIO (ScaleIOPersistentVolumeSource)

    scaleIO 表示 Kubernetes 节点上挂接和挂载的 ScaleIO 持久卷。

    ScaleIOPersistentVolumeSource 表示一个 ScaleIO 持久卷。

    • scaleIO.gateway (string),必需

      gateway 是 ScaleIO API 网关的主机地址。

    • scaleIO.secretRef (SecretReference),必需

      secretRef 引用包含 ScaleIO 用户和其他敏感信息的 Secret。如果未提供此项,则 Login 操作将失败。

      SecretReference 表示对某 Secret 的引用,其中包含足够的信息来访问任何名字空间中的 Secret。

      • scaleIO.secretRef.name (string)

        name 在名字空间内是唯一的,以引用一个 Secret 资源。

      • scaleIO.secretRef.namespace (string)

        namespace 指定一个名字空间,Secret 名称在该名字空间中必须唯一。

    • scaleIO.system (string),必需

      system 是 ScaleIO 中所配置的存储系统的名称。

    • scaleIO.fsType (string)

      fsType 是要挂载的文件系统类型。必须是主机操作系统所支持的文件系统类型之一。 例如 “ext4”、“xfs”、“ntfs”。默认为 “xfs”。

    • scaleIO.protectionDomain (string)

      protectionDomain 是 ScaleIO 保护域(ScaleIO Protection Domain)的名称,用于已配置的存储。

    • scaleIO.readOnly (boolean)

      readOnly 默认为 false(读/写)。此处 readOnly 将在 VolumeMounts 中强制设置 readOnly。

    • scaleIO.sslEnabled (boolean)

      sslEnabled 是启用/禁用与网关(Gateway)进行 SSL 通信的标志,默认为 false。

    • scaleIO.storageMode (string)

      storageMode 指示卷所用的存储应是 ThickProvisioned 或 ThinProvisioned。 默认为 ThinProvisioned。

    • scaleIO.storagePool (string)

      storagePool 是与保护域关联的 ScaleIO Storage Pool。

    • scaleIO.volumeName (string)

      volumeName 是在与此卷源关联的 ScaleIO 系统中已创建的卷的名称。

  • storageos (StorageOSPersistentVolumeSource)

    storageOS 表示一个 StorageOS 卷,该卷被挂接到 kubelet 的主机并挂载到 Pod 中。更多信息: https://examples.k8s.io/volumes/storageos/README.md

    表示 StorageOS 持久卷资源。

    • storageos.fsType (string)

      fsType 是要挂载的文件系统类型。必须是主机操作系统所支持的文件系统类型之一。 例如 “ext4”、“xfs”、“ntfs”。如果未指定,则隐式推断为 “ext4”。

    • storageos.readOnly (boolean)

      readOnly 默认为 false(读/写)。此处 readOnly 将在 VolumeMounts 中强制设置 readOnly。

    • storageos.secretRef (ObjectReference)

      secretRef 指定用于获取 StorageOS API 凭据的 Secret。如果未指定,则将尝试使用默认值。

    • storageos.volumeName (string)

      volumeName 是 StorageOS 卷的人类可读名称。这些卷名称在一个名字空间内是唯一的。

    • storageos.volumeNamespace (string)

      volumeNamespace 指定 StorageOS 内卷的作用域。如果未指定名字空间,则将使用 Pod 的名字空间。 这一字段的存在允许 Kubernetes 中名称作用域与 StorageOS 进行映射,实现更紧密的集成。 将 volumeName 设为任何名称均可以重载默认的行为。 如果你未在 StorageOS 内使用名字空间,则设为 “default”。 StorageOS 内预先不存在的名字空间会被创建。

  • vsphereVolume (VsphereVirtualDiskVolumeSource)

    vsphereVolume 表示 kubelet 主机上挂接和挂载的 vSphere 卷。

    表示 vSphere 卷资源。

    • vsphereVolume.volumePath (string),必需

      volumePath 是标识 vSphere 卷 vmdk 的路径。

    • vsphereVolume.fsType (string)

      fsType 是要挂载的文件系统类型。必须是主机操作系统所支持的文件系统类型之一。 例如 “ext4”、“xfs”、“ntfs”。如果未指定,则隐式推断为 “ext4”。

    • vsphereVolume.storagePolicyID (string)

      storagePolicyID 是与 StoragePolicyName 关联的基于存储策略的管理(SPBM)配置文件 ID。

    • vsphereVolume.storagePolicyName (string)

      storagePolicyName 是基于存储策略的管理(SPBM)配置文件名称。

PersistentVolumeStatus

PersistentVolumeStatus 是持久卷的当前状态。


  • lastPhaseTransitionTime (Time)

    lastPhaseTransitionTime 是从一个阶段转换到另一个阶段的时间,每次卷阶段转换时都会自动重置为当前时间。 这是一个 Alpha 字段,需要启用 PersistentVolumeLastPhaseTransitionTime 特性。

    Time 是 time.Time 的包装器,支持正确编组为 YAML 和 JSON,它为 time 包提供的许多工厂方法提供了包装器。

  • message (string)

    message 是一条人类可读的消息,指明有关卷为何处于此状态的详细信息。

  • phase (string)

    phase 表示一个卷是否可用,是否绑定到一个 PVC 或是否由某个 PVC 释放。更多信息: https://kubernetes.io/zh-cn/docs/concepts/storage/persistent-volumes#phase

  • reason (string)

    reason 是一个描述任何故障的简短 CamelCase 字符串,用于机器解析并在 CLI 中整齐地显示。

PersistentVolumeList

PersistentVolumeList 是 PersistentVolume 各项的列表。


  • apiVersion: v1

  • kind: PersistentVolumeList

操作


get 读取指定的 PersistentVolume

HTTP 请求

GET /api/v1/persistentvolumes/{name}

参数

  • name (路径参数): string,必需

    PersistentVolume 的名称

  • pretty (查询参数): string

    pretty

响应

200 (PersistentVolume): OK

401: Unauthorized

get 读取指定的 PersistentVolume 的状态

HTTP 请求

GET /api/v1/persistentvolumes/{name}/status

参数

  • name (路径参数): string,必需

    PersistentVolume 的名称

  • pretty (查询参数): string

    pretty

响应

200 (PersistentVolume): OK

401: Unauthorized

list 列出或观测类别为 PersistentVolume 的对象

HTTP 请求

GET /api/v1/persistentvolumes

参数

响应

200 (PersistentVolumeList): OK

401: Unauthorized

create 创建 PersistentVolume

HTTP 请求

POST /api/v1/persistentvolumes

参数

响应

200 (PersistentVolume): OK

201 (PersistentVolume): Created

202 (PersistentVolume): Accepted

401: Unauthorized

update 替换指定的 PersistentVolume

HTTP 请求

PUT /api/v1/persistentvolumes/{name}

参数

响应

200 (PersistentVolume): OK

201 (PersistentVolume): Created

401: Unauthorized

update 替换指定的 PersistentVolume 的状态

HTTP 请求

PUT /api/v1/persistentvolumes/{name}/status

参数

响应

200 (PersistentVolume): OK

201 (PersistentVolume): Created

401: Unauthorized

patch 部分更新指定的 PersistentVolume

HTTP 请求

PATCH /api/v1/persistentvolumes/{name}

参数

  • name (路径参数): string,必需

    PersistentVolume 的名称

  • body: Patch,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • force (查询参数): boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (PersistentVolume): OK

201 (PersistentVolume): Created

401: Unauthorized

patch 部分更新指定的 PersistentVolume 的状态

HTTP 请求

PATCH /api/v1/persistentvolumes/{name}/status

参数

  • name (路径参数): string,必需

    PersistentVolume 的名称

  • body: Patch,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • force (查询参数): boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (PersistentVolume): OK

201 (PersistentVolume): Created

401: Unauthorized

delete 删除 PersistentVolume

HTTP 请求

DELETE /api/v1/persistentvolumes/{name}

参数

响应

200 (PersistentVolume): OK

202 (PersistentVolume): Accepted

401: Unauthorized

deletecollection 删除 PersistentVolume 的集合

HTTP 请求

DELETE /api/v1/persistentvolumes

参数

响应

200 (Status): OK

401: Unauthorized

6.5.3.6 - StorageClass

StorageClass 为可以动态制备 PersistentVolume 的存储类描述参数。

apiVersion: storage.k8s.io/v1

import "k8s.io/api/storage/v1"

StorageClass

StorageClass 为可以动态制备 PersistentVolume 的存储类描述参数。

StorageClass 是不受名字空间作用域限制的;按照 etcd 设定的存储类的名称位于 ObjectMeta.Name 中。


  • apiVersion: storage.k8s.io/v1

  • kind: StorageClass

  • allowedTopologies ([]TopologySelectorTerm)

    原子性:将在合并期间被替换

    allowedTopologies 限制可以动态制备卷的节点拓扑。每个卷插件定义其自己支持的拓扑规约。 空的 TopologySelectorTerm 列表意味着没有拓扑限制。 只有启用 VolumeScheduling 功能特性的服务器才能使用此字段。

    拓扑选择器条件表示标签查询的结果。 一个 null 或空的拓扑选择器条件不会匹配任何对象。各个条件的要求按逻辑与的关系来计算。 此选择器作为 NodeSelectorTerm 所提供功能的子集。此功能为 Alpha 特性,将来可能会变更。

  • allowedTopologies.matchLabelExpressions ([]TopologySelectorLabelRequirement)

    按标签设置的拓扑选择器要求的列表。

    拓扑选择器要求是与给定标签匹配的一个选择器。此功能为 Alpha 特性,将来可能会变更。

    • allowedTopologies.matchLabelExpressions.key (string),必需

      选择器所针对的标签键。

    • allowedTopologies.matchLabelExpressions.values ([]string),必需

      字符串数组。一个值必须与要选择的标签匹配。values 中的每个条目按逻辑或的关系来计算。

  • mountOptions ([]string)

    mountOptions 控制此存储类动态制备的 PersistentVolume 的挂载配置。 (例如 ["ro", "soft"])。 系统对选项作检查——如果有一个选项无效,则这些 PV 的挂载将失败。

  • parameters (map[string]string)

    parameters 包含应创建此存储类卷的制备器的参数。

  • reclaimPolicy (string)

    reclaimPolicy 控制此存储类动态制备的 PersistentVolume 的 reclaimPolicy。默认为 Delete。

  • volumeBindingMode (string)

    volumeBindingMode 指示应该如何制备和绑定 PersistentVolumeClaim。 未设置时,将使用 VolumeBindingImmediate。 只有启用 VolumeScheduling 功能特性的服务器才能使用此字段。

StorageClassList

StorageClassList 是存储类的集合。


  • apiVersion: storage.k8s.io/v1

  • kind: StorageClassList

操作


get 读取指定的 StorageClass

HTTP 请求

GET /apis/storage.k8s.io/v1/storageclasses/{name}

参数

  • name (路径参数): string,必需

    StorageClass 的名称

  • pretty (查询参数): string

    pretty

响应

200 (StorageClass): OK

401: Unauthorized

list 列出或观测类别为 StorageClass 的对象

HTTP 请求

GET /apis/storage.k8s.io/v1/storageclasses

参数

响应

200 (StorageClassList): OK

401: Unauthorized

create 创建 StorageClass

HTTP 请求

POST /apis/storage.k8s.io/v1/storageclasses

参数

响应

200 (StorageClass): OK

201 (StorageClass): Created

202 (StorageClass): Accepted

401: Unauthorized

update 替换指定的 StorageClass

HTTP 请求

PUT /apis/storage.k8s.io/v1/storageclasses/{name}

参数

  • name (路径参数): string,必需

    StorageClass 的名称

  • body: StorageClass,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • pretty (查询参数): string

    pretty

响应

200 (StorageClass): OK

201 (StorageClass): Created

401: Unauthorized

patch 部分更新指定的 StorageClass

HTTP 请求

PATCH /apis/storage.k8s.io/v1/storageclasses/{name}

参数

  • name (路径参数): string,必需

    StorageClass 的名称

  • body: Patch,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • force (查询参数): boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (StorageClass): OK

201 (StorageClass): Created

401: Unauthorized

delete 删除 StorageClass

HTTP 请求

DELETE /apis/storage.k8s.io/v1/storageclasses/{name}

参数

响应

200 (StorageClass): OK

202 (StorageClass): Accepted

401: Unauthorized

deletecollection 删除 StorageClass 的集合

HTTP 请求

DELETE /apis/storage.k8s.io/v1/storageclasses

参数

响应

200 (Status): OK

401: Unauthorized

6.5.3.7 - VolumeAttachment

VolumeAttachment 抓取将指定卷挂接到指定节点或从指定节点解除挂接指定卷的意图。

apiVersion: storage.k8s.io/v1

import "k8s.io/api/storage/v1"

VolumeAttachment

VolumeAttachment 抓取将指定卷挂接到指定节点或从指定节点解除挂接指定卷的意图。

VolumeAttachment 对象未划分命名空间。


  • apiVersion: storage.k8s.io/v1

  • kind: VolumeAttachment

  • status (VolumeAttachmentStatus)

    status 表示 VolumeAttachment 请求的状态。由完成挂接或解除挂接操作的实体(即外部挂接器)进行填充。

VolumeAttachmentSpec

VolumeAttachmentSpec 是 VolumeAttachment 请求的规约。


  • attacher (string),必需

    attacher 表示必须处理此请求的卷驱动的名称。这是由 GetPluginName() 返回的名称。

  • nodeName (string),必需

    nodeName 表示卷应挂接到的节点。

  • source (VolumeAttachmentSource),必需

    source 表示应挂接的卷。

    VolumeAttachmentSource 表示应挂接的卷。现在只能通过外部挂接器挂接 PersistenVolume, 将来我们可能还允许 Pod 中的内联卷。只能设置一个成员。

    • source.inlineVolumeSpec (PersistentVolumeSpec)

      inlineVolumeSpec 包含挂接由 Pod 的内联 VolumeSource 定义的持久卷时所有必需的信息。 仅为 CSIMigation 功能填充此字段。 它包含从 Pod 的内联 VolumeSource 转换为 PersistentVolumeSpec 的字段。 此字段处于 Beta 阶段,且只有启用 CSIMigration 功能的服务器才能使用此字段。

    • source.persistentVolumeName (string)

      persistentVolumeName 是要挂接的持久卷的名称。

VolumeAttachmentStatus

VolumeAttachmentStatus 是 VolumeAttachment 请求的状态。


  • attached (boolean),必需

    attached 表示卷被成功挂接。此字段只能由完成挂接操作的实体(例如外部挂接器)进行设置。

  • attachError (VolumeError)

    attachError 表示挂接操作期间遇到的最后一个错误,如果有。 此字段只能由完成挂接操作的实体(例如外部挂接器)进行设置。

    VolumeError 抓取卷操作期间遇到的一个错误。

    • attachError.message (string)

      message 表示挂接或解除挂接操作期间遇到的错误。 此字符串可以放入日志,因此它不应包含敏感信息。

    • attachError.time (Time)

      遇到错误的时间。

      time 是 time.Time 的包装类,支持正确地序列化为 YAML 和 JSON。 为 time 包提供的许多工厂方法提供了包装类。

  • attachmentMetadata (map[string]string)

    成功挂接时,attachmentMetadata 字段将由挂接操作返回的任何信息进行填充, 这些信息必须传递到后续的 WaitForAttach 或 Mount 调用中。 此字段只能由完成挂接操作的实体(例如外部挂接器)进行设置。

  • detachError (VolumeError)

    detachError 表示解除挂接操作期间遇到的最后一个错误,如果有。 此字段只能由完成解除挂接操作的实体(例如外部挂接器)进行设置。

    VolumeError 抓取卷操作期间遇到的一个错误。

    • detachError.message (string)

      message 表示挂接或解除挂接操作期间遇到的错误。 此字符串可以放入日志,因此它不应包含敏感信息。

    • detachError.time (Time)

      time 表示遇到错误的时间。

      time 是 time.Time 的包装类,支持正确地序列化为 YAML 和 JSON。 为 time 包提供的许多工厂方法提供了包装类。

VolumeAttachmentList

VolumeAttachmentList 是 VolumeAttachment 对象的集合。


  • apiVersion: storage.k8s.io/v1

  • kind: VolumeAttachmentList

操作


get 读取指定的 VolumeAttachment

HTTP 请求

GET /apis/storage.k8s.io/v1/volumeattachments/{name}

参数

  • name (路径参数): string,必需

    VolumeAttachment 的名称

  • pretty (查询参数): string

    pretty

响应

200 (VolumeAttachment): OK

401: Unauthorized

get 读取指定的 VolumeAttachment 的状态

HTTP 请求

GET /apis/storage.k8s.io/v1/volumeattachments/{name}/status

参数

  • name (路径参数): string,必需

    VolumeAttachment 的名称

  • pretty (查询参数): string

    pretty

响应

200 (VolumeAttachment): OK

401: Unauthorized

list 列出或观测类别为 VolumeAttachment 的对象

HTTP 请求

GET /apis/storage.k8s.io/v1/volumeattachments

参数

响应

200 (VolumeAttachmentList): OK

401: Unauthorized

create 创建 VolumeAttachment

HTTP 请求

POST /apis/storage.k8s.io/v1/volumeattachments

参数

响应

200 (VolumeAttachment): OK

201 (VolumeAttachment): Created

202 (VolumeAttachment): Accepted

401: Unauthorized

update 替换指定的 VolumeAttachment

HTTP 请求

PUT /apis/storage.k8s.io/v1/volumeattachments/{name}

参数

响应

200 (VolumeAttachment): OK

201 (VolumeAttachment): Created

401: Unauthorized

update 替换指定的 VolumeAttachment 的状态

HTTP 请求

PUT /apis/storage.k8s.io/v1/volumeattachments/{name}/status

参数

响应

200 (VolumeAttachment): OK

201 (VolumeAttachment): Created

401: Unauthorized

patch 部分更新指定的 VolumeAttachment

HTTP 请求

PATCH /apis/storage.k8s.io/v1/volumeattachments/{name}

参数

  • name (路径参数): string,必需

    VolumeAttachment 的名称

  • body: Patch,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • force (查询参数): boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (VolumeAttachment): OK

201 (VolumeAttachment): Created

401: Unauthorized

patch 部分更新指定的 VolumeAttachment 的状态

HTTP 请求

PATCH /apis/storage.k8s.io/v1/volumeattachments/{name}/status

参数

  • name (路径参数): string,必需

    VolumeAttachment 的名称

  • body: Patch,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • force (查询参数): boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (VolumeAttachment): OK

201 (VolumeAttachment): Created

401: Unauthorized

delete 删除 VolumeAttachment

HTTP 请求

DELETE /apis/storage.k8s.io/v1/volumeattachments/{name}

参数

响应

200 (VolumeAttachment): OK

202 (VolumeAttachment): Accepted

401: Unauthorized

deletecollection 删除 VolumeAttachment 的集合

HTTP 请求

DELETE /apis/storage.k8s.io/v1/volumeattachments

参数

响应

200 (Status): OK

401: Unauthorized

6.5.3.8 - CSIDriver

CSIDriver 抓取集群上部署的容器存储接口(CSI)卷驱动有关的信息。

apiVersion: storage.k8s.io/v1

import "k8s.io/api/storage/v1"

CSIDriver

CSIDriver 抓取集群上部署的容器存储接口(CSI)卷驱动有关的信息。 Kubernetes 挂接/解除挂接控制器使用此对象来决定是否需要挂接。 Kubelet 使用此对象决定挂载时是否需要传递 Pod 信息。 CSIDriver 对象未划分命名空间。


  • apiVersion: storage.k8s.io/v1

  • kind: CSIDriver

CSIDriverSpec

CSIDriverSpec 是 CSIDriver 的规约。


  • attachRequired (boolean)

    attachRequired 表示这个 CSI 卷驱动需要挂接操作 (因为它实现了 CSI ControllerPublishVolume() 方法), Kubernetes 挂接/解除挂接控制器应调用挂接卷接口, 以检查卷挂接(volumeattachment)状态并在继续挂载之前等待卷被挂接。 CSI 外部挂接器与 CSI 卷驱动配合使用,并在挂接操作完成时更新 volumeattachment 状态。 如果 CSIDriverRegistry 特性门控被启用且此值指定为 false,将跳过挂接操作。 否则将调用挂接操作。

    此字段不可变更。

  • fsGroupPolicy (string)

    fsGroupPolicy 定义底层卷是否支持在挂载之前更改卷的所有权和权限。 有关更多详细信息,请参考特定的 FSGroupPolicy 值。

    此字段不可变更。

    默认为 ReadWriteOnceWithFSType,这会检查每个卷,以决定 Kubernetes 是否应修改卷的所有权和权限。 采用默认策略时,如果定义了 fstype 且卷的访问模式包含 ReadWriteOnce,将仅应用定义的 fsGroup。

  • podInfoOnMount (boolean)

    如果 podInfoOnMount 设为 true,则表示在挂载操作期间这个 CSI 卷驱动需要更多的 Pod 信息(例如 podName 和 podUID 等)。 如果设为 false,则挂载时将不传递 Pod 信息。默认为 false。

    CSI 驱动将 podInfoOnMount 指定为驱动部署的一部分。 如果为 true,Kubelet 将在 CSI NodePublishVolume() 调用中作为 VolumeContext 传递 Pod 信息。 CSI 驱动负责解析和校验作为 VolumeContext 传递进来的信息。

    如果 podInfoOnMount 设为 true,将传递以下 VolumeConext。 此列表可能变大,但将使用前缀。

    • "csi.storage.k8s.io/pod.name": pod.name
    • "csi.storage.k8s.io/pod.namespace": pod.namespace
    • "csi.storage.k8s.io/pod.uid": string(pod.UID)
    • "csi.storage.k8s.io/ephemeral": 如果此卷是 CSIVolumeSource 定义的一个临时内联卷,则为 “true”,否则为 “false”

    “csi.storage.k8s.io/ephemeral” 是 Kubernetes 1.16 中一个新的功能特性。 只有同时支持 “Persistent” 和 “Ephemeral” VolumeLifecycleMode 的驱动,此字段才是必需的。 其他驱动可以保持禁用 Pod 信息或忽略此字段。 由于 Kubernetes 1.15 不支持此字段,所以在这类集群上部署驱动时,只能支持一种模式。 该部署就决定了是哪种模式,例如通过驱动的命令行参数。

    此字段不可变更。

  • requiresRepublish (boolean)

    requiresRepublish 表示 CSI 驱动想要 NodePublishVolume 被周期性地调用, 以反映已挂载卷中的任何可能的变化。 此字段默认为 false。

    注:成功完成对 NodePublishVolume 的初始调用后,对 NodePublishVolume 的后续调用只应更新卷的内容。 新的挂载点将不会被运行的容器察觉。

  • seLinuxMount (boolean)

    seLinuxMount 指定 CSI 驱动是否支持 "-o context" 挂载选项。

    当值为 “true” 时,CSI 驱动必须确保该 CSI 驱动提供的所有卷可以分别用不同的 -o context 选项进行挂载。 这对于将卷作为块设备上的文件系统或作为独立共享卷提供的存储后端来说是典型的方法。 当 Kubernetes 挂载在 Pod 中使用的已显式设置 SELinux 上下文的 ReadWriteOncePod 卷时, 将使用 "-o context=xyz" 挂载选项调用 NodeStage / NodePublish。 未来可能会扩展到其他的卷访问模式(AccessModes)。在任何情况下,Kubernetes 都会确保该卷仅使用同一 SELinux 上下文进行挂载。

    当值为 “false” 时,Kubernetes 不会将任何特殊的 SELinux 挂载选项传递给驱动。 这通常用于代表更大共享文件系统的子目录的卷。

    默认为 “false”。

  • storageCapacity (boolean)

    如果设为 true,则 storageCapacity 表示 CSI 卷驱动希望 Pod 调度时考虑存储容量, 驱动部署将通过创建包含容量信息的 CSIStorageCapacity 对象来报告该存储容量。

    部署驱动时可以立即启用这个检查。 这种情况下,只有此驱动部署已发布某些合适的 CSIStorageCapacity 对象, 才会继续制备新的卷,然后进行绑定。

    换言之,可以在未设置此字段或此字段为 false 的情况下部署驱动, 并且可以在发布存储容量信息后再修改此字段。

    此字段在 Kubernetes 1.22 及更早版本中不可变更,但现在可以变更。

  • tokenRequests ([]TokenRequest)

    原子性:将在合并期间被替换

    tokenRequests 表示 CSI 驱动需要供挂载卷所用的 Pod 的服务帐户令牌,进行必要的鉴权。 Kubelet 将在 CSI NodePublishVolume 调用中传递 VolumeContext 中的令牌。 CSI 驱动应解析和校验以下 VolumeContext:

    "csi.storage.k8s.io/serviceAccount.tokens": {
      "<audience>": {
        "token": <token>,
        "expirationTimestamp": <expiration timestamp in RFC3339>,
      },
      ...
    }
    

    注:每个 tokenRequest 中的受众应该不同,且最多有一个令牌是空字符串。 要在令牌过期后接收一个新的令牌,requiresRepublish 可用于周期性地触发 NodePublishVolume。

    tokenRequest 包含一个服务帐户令牌的参数。

    • tokenRequests.audience (string),必需

    audience 是 “TokenRequestSpec” 中令牌的目标受众。 它默认为 kube apiserver 的受众。

    • tokenRequests.expirationSeconds (int64)

      expirationSeconds 是 “TokenRequestSpec” 中令牌的有效期。 它具有与 “TokenRequestSpec” 中 “expirationSeconds” 相同的默认值。

  • volumeLifecycleModes ([]string)

    集合:唯一值将在合并期间被保留

    volumeLifecycleModes 定义这个 CSI 卷驱动支持哪种类别的卷。 如果列表为空,则默认值为 “Persistent”,这是 CSI 规范定义的用法, 并通过常用的 PV/PVC 机制在 Kubernetes 中实现。

    另一种模式是 “Ephemeral”。 在这种模式下,在 Pod 规约中用 CSIVolumeSource 以内联方式定义卷,其生命周期与该 Pod 的生命周期相关联。 驱动必须感知到这一点,因为只有针对这种卷才会接收到 NodePublishVolume 调用。

    有关实现此模式的更多信息,请参阅 https://kubernetes-csi.github.io/docs/ephemeral-local-volumes.html。 驱动可以支持其中一种或多种模式,将来可能会添加更多模式。

    此字段处于 Beta 阶段。此字段不可变更。

CSIDriverList

CSIDriverList 是 CSIDriver 对象的集合。


  • apiVersion: storage.k8s.io/v1

  • kind: CSIDriverList

操作


get 读取指定的 CSIDriver

HTTP 请求

GET /apis/storage.k8s.io/v1/csidrivers/{name}

参数

  • name (路径参数): string,必需

    CSIDriver 的名称。

  • pretty (查询参数): string

    pretty

响应

200 (CSIDriver): OK

401: Unauthorized

list 列出或观测类别为 CSIDriver 的对象

HTTP 请求

GET /apis/storage.k8s.io/v1/csidrivers

参数

响应

200 (CSIDriverList): OK

401: Unauthorized

create 创建 CSIDriver

HTTP 请求

POST /apis/storage.k8s.io/v1/csidrivers

参数

响应

200 (CSIDriver): OK

201 (CSIDriver): Created

202 (CSIDriver): Accepted

401: Unauthorized

update 替换指定的 CSIDriver

HTTP 请求

PUT /apis/storage.k8s.io/v1/csidrivers/{name}

参数

  • name (路径参数): string,必需

    CSIDriver 的名称。

  • body: CSIDriver,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • pretty (查询参数): string

    pretty

响应

200 (CSIDriver): OK

201 (CSIDriver): Created

401: Unauthorized

patch 部分更新指定的 CSIDriver

HTTP 请求

PATCH /apis/storage.k8s.io/v1/csidrivers/{name}

参数

  • name (路径参数): string,必需

    CSIDriver 的名称。

  • body: Patch,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • force (查询参数): boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (CSIDriver): OK

201 (CSIDriver): Created

401: Unauthorized

delete 删除 CSIDriver

HTTP 请求

DELETE /apis/storage.k8s.io/v1/csidrivers/{name}

参数

响应

200 (CSIDriver): OK

202 (CSIDriver): Accepted

401: Unauthorized

deletecollection 删除 CSIDriver 的集合

HTTP 请求

DELETE /apis/storage.k8s.io/v1/csidrivers

参数

响应

200 (Status): OK

401: Unauthorized

6.5.3.9 - CSINode

CSINode 包含节点上安装的所有 CSI 驱动有关的信息。

apiVersion: storage.k8s.io/v1

import "k8s.io/api/storage/v1"

CSINode

CSINode 包含节点上安装的所有 CSI 驱动有关的信息。CSI 驱动不需要直接创建 CSINode 对象。 只要这些驱动使用 node-driver-registrar 边车容器,kubelet 就会自动为 CSI 驱动填充 CSINode 对象, 作为 kubelet 插件注册操作的一部分。CSINode 的名称与节点名称相同。 如果不存在此对象,则说明该节点上没有可用的 CSI 驱动或 Kubelet 版本太低无法创建该对象。 CSINode 包含指向相应节点对象的 OwnerReference。


  • apiVersion: storage.k8s.io/v1

  • kind: CSINode

  • metadata (ObjectMeta)

    标准的对象元数据。metadata.name 必须是 Kubernetes 节点的名称。

  • spec (CSINodeSpec),必需

    spec 是 CSINode 的规约。

CSINodeSpec

CSINodeSpec 包含一个节点上安装的所有 CSI 驱动规约有关的信息。


  • drivers ([]CSINodeDriver),必需

    补丁策略:按照键 name 合并

    drivers 是节点上存在的所有 CSI 驱动的信息列表。如果列表中的所有驱动均被卸载,则此字段可以为空。

    CSINodeDriver 包含一个节点上安装的一个 CSI 驱动规约有关的信息。

    • drivers.name (string),必需

      name 表示该对象引用的 CSI 驱动的名称。此字段值必须是针对该驱动由 CSI GetPluginName() 调用返回的相同名称。

    • drivers.nodeID (string),必需

      从驱动角度来看,这是节点的 nodeID。 对于未与节点共享相同命名法的存储系统,此字段使得 Kubernetes 能够与之进行通信。 例如,Kubernetes 可能将给定节点视为 "node1",但存储系统可以将同一节点视为 "nodeA"。 当 Kubernetes 向存储系统发出一条命令将一个卷挂接到特定的节点时, 它可以藉此字段使用存储系统所理解的 ID 引用节点名称,例如使用 “nodeA” 而不是 “node1”。 此字段是必需的。

    • drivers.allocatable (VolumeNodeResources)

      allocatable 表示一个节点上可供调度的卷资源。此字段处于 beta 阶段。

      VolumeNodeResources 是调度卷时所用的一组资源限制。

      • drivers.allocatable.count (int32)

        这是一个节点上可使用的、由 CSI 驱动管理的独立卷个数的上限。 挂接并挂载到一个节点上的卷被视为被使用一次,不是两次。 相同的规则适用于同一个节点上多个 Pod 之间共享的同一个卷。 如果未指定此字段,则该节点上支持的卷数量是无限的。

    • drivers.topologyKeys ([]string)

      topologyKeys 是驱动支持的键的列表。 在集群上初始化一个驱动时,该驱动将提供一组自己理解的拓扑键 (例如 “company.com/zone”、“company.com/region”)。 在一个节点上初始化一个驱动时,该驱动将提供相同的拓扑键和值。 Kubelet 将在其自己的节点对象上将这些拓扑键暴露为标签。 当 Kubernetes 进行拓扑感知的制备时,可以使用此列表决定应从节点对象中检索哪些标签并传回驱动。 不同的节点可以使用不同的拓扑键。 如果驱动不支持拓扑,则此字段可以为空。

CSINodeList

CSINodeList 是 CSINode 对象的集合。


  • apiVersion: storage.k8s.io/v1

  • kind: CSINodeList

操作


get 读取指定的 CSINode

HTTP 请求

GET /apis/storage.k8s.io/v1/csinodes/{name}

参数

  • name (路径参数): string,必需

    CSINode 的名称

  • pretty (查询参数): string

    pretty

响应

200 (CSINode): OK

401: Unauthorized

list 列出或观测类别为 CSINode 的对象

HTTP 请求

GET /apis/storage.k8s.io/v1/csinodes

参数

响应

200 (CSINodeList): OK

401: Unauthorized

create 创建 CSINode

HTTP 请求

POST /apis/storage.k8s.io/v1/csinodes

参数

响应

200 (CSINode): OK

201 (CSINode): Created

202 (CSINode): Accepted

401: Unauthorized

update 替换指定的 CSINode

HTTP 请求

PUT /apis/storage.k8s.io/v1/csinodes/{name}

参数

  • name (路径参数): string,必需

    CSINode 的名称

  • body: CSINode,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • pretty (查询参数): string

    pretty

响应

200 (CSINode): OK

201 (CSINode): Created

401: Unauthorized

patch 部分更新指定的 CSINode

HTTP 请求

PATCH /apis/storage.k8s.io/v1/csinodes/{name}

参数

  • name (路径参数): string,必需

    CSINode 的名称

  • body: Patch,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • force (查询参数): boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (CSINode): OK

201 (CSINode): Created

401: Unauthorized

delete 删除 CSINode

HTTP 请求

DELETE /apis/storage.k8s.io/v1/csinodes/{name}

参数

响应

200 (CSINode): OK

202 (CSINode): Accepted

401: Unauthorized

deletecollection 删除 CSINode 的集合

HTTP 请求

DELETE /apis/storage.k8s.io/v1/csinodes

参数

响应

200 (Status): OK

401: Unauthorized

6.5.3.10 - CSIStorageCapacity

CSIStorageCapacity 存储一个 CSI GetCapacity 调用的结果。

apiVersion: storage.k8s.io/v1

import "k8s.io/api/storage/v1"

CSIStorageCapacity

CSIStorageCapacity 存储一个 CSI GetCapacity 调用的结果。 对于给定的 StorageClass,此结构描述了特定拓扑段中可用的容量。 当考虑在哪里实例化新的 PersistentVolume 时可以使用此项。

例如,此结构可以描述如下内容:

  • “standard” 的 StorageClass 容量为 “1234 GiB”,可用于 “topology.kubernetes.io/zone=us-east1”
  • “localssd” 的 StorageClass 容量为 “10 GiB”,可用于 “kubernetes.io/hostname=knode-abc123”

以下三种情况均暗示了某些组合没有可用的容量:

  • 不存在拓扑和存储类名称合适的对象
  • 这种对象存在,但容量未设置
  • 这种对象存在,但容量为零

这些对象的制作方可以决定哪种方法更合适。

当 CSI 驱动选择使用 CSIDriverSpec.StorageCapacity 进行容量感知调度时,kube-scheduler 会使用这些对象。 该调度器将 MaximumVolumeSize 与 pending 卷的请求大小进行比较,以过滤掉不合适的节点。 如果未设置 MaximumVolumeSize,则回退为与不太精确的容量(Capacity)进行比较。 如果还是未设置,则该调度器假定容量不足并尝试某些其他节点。


  • apiVersion: storage.k8s.io/v1

  • kind: CSIStorageCapacity

  • storageClassName (string),必需

    storageClassName 是已报告容量所对应的 StorageClass 的名称。 它必须满足与 StorageClass 对象名称相同的要求(非空,DNS 子域名)。 如果该对象不再存在,则 CSIStorageCapacity 对象将被废弃且应由创建者移除。 此字段不可变更。

  • capacity (Quantity)

    capacity 是 CSI 驱动在其 GetCapacityResponse 中为 GetCapacityRequest 报告的值,其拓扑和参数与之前的字段匹配。

    该语义目前(CSI 规范 1.2)定义为:可用于制备卷的可用存储容量(单位为字节)。 如果未设置,则该信息目前不可用。

  • maximumVolumeSize (Quantity)

    maximumVolumeSize 是 CSI 驱动在其 GetCapacityResponse 中为 GetCapacityRequest 报告的值,其拓扑和参数与之前的字段匹配。

    自从 CSI 规范 1.4.0 起,这定义为 CreateVolumeRequest.capacity_range.required_bytes 字段中可以使用的最大值, 以便用 GetCapacityRequest 中相同的参数创建一个卷。 Kubernetes API 中的相应值是卷声明中的 ResourceRequirements.Requests。

  • nodeTopology (LabelSelector)

    nodeTopology 定义了哪些节点有权访问已报告容量的存储。 如果未设置,则不能从集群中的任意节点访问此存储。 如果留空,则可以从所有节点访问此存储。此字段不可变更。

CSIStorageCapacityList

CSIStorageCapacityList 是 CSIStorageCapacity 对象的集合。


  • apiVersion: storage.k8s.io/v1

  • kind: CSIStorageCapacityList

操作


get 读取指定的 CSIStorageCapacity

HTTP 请求

GET /apis/storage.k8s.io/v1/namespaces/{namespace}/csistoragecapacities/{name}

参数

  • name (路径参数): string,必需

    CSIStorageCapacity 的名称

  • namespace (路径参数): string,必需

    namespace

  • pretty (查询参数): string

    pretty

响应

200 (CSIStorageCapacity): OK

401: Unauthorized

list 列出或观测类别为 CSIStorageCapacity 的对象

HTTP 请求

GET /apis/storage.k8s.io/v1/namespaces/{namespace}/csistoragecapacities

参数

响应

200 (CSIStorageCapacityList): OK

401: Unauthorized

list 列出或观测类别为 CSIStorageCapacity 的对象

HTTP 请求

GET /apis/storage.k8s.io/v1/csistoragecapacities

参数

响应

200 (CSIStorageCapacityList): OK

401: Unauthorized

create 创建 CSIStorageCapacity

HTTP 请求

POST /apis/storage.k8s.io/v1/namespaces/{namespace}/csistoragecapacities

参数

响应

200 (CSIStorageCapacity): OK

201 (CSIStorageCapacity): Created

202 (CSIStorageCapacity): Accepted

401: Unauthorized

update 替换指定的 CSIStorageCapacity

HTTP 请求

PUT /apis/storage.k8s.io/v1/namespaces/{namespace}/csistoragecapacities/{name}

参数

响应

200 (CSIStorageCapacity): OK

201 (CSIStorageCapacity): Created

401: Unauthorized

patch 部分更新指定的 CSIStorageCapacity

HTTP 请求

PATCH /apis/storage.k8s.io/v1/namespaces/{namespace}/csistoragecapacities/{name}

参数

  • name (路径参数): string,必需

    CSIStorageCapacity 的名称

  • namespace (路径参数): string,必需

    namespace

  • body: Patch,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • force (查询参数): boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (CSIStorageCapacity): OK

201 (CSIStorageCapacity): Created

401: Unauthorized

delete 删除 CSIStorageCapacity

HTTP 请求

DELETE /apis/storage.k8s.io/v1/namespaces/{namespace}/csistoragecapacities/{name}

参数

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除 CSIStorageCapacity 的集合

HTTP 请求

DELETE /apis/storage.k8s.io/v1/namespaces/{namespace}/csistoragecapacities

参数

响应

200 (Status): OK

401: Unauthorized

6.5.4 - 身份认证资源

6.5.4.1 - ServiceAccount

ServiceAccount 将以下内容绑定在一起:1. 用户可以理解的名称,也可能是外围系统理解的身份标识 2. 可以验证和授权的主体 3. 一组 Secret。

apiVersion: v1

import "k8s.io/api/core/v1"

ServiceAccount

ServiceAccount 将以下内容绑定在一起:

  • 用户可以理解的名称,也可能是外围系统理解的身份标识
  • 可以验证和授权的主体
  • 一组 Secret

ServiceAccountList

ServiceAccountList 是 ServiceAccount 对象的列表


操作


get 读取指定的 ServiceAccount

HTTP 请求

GET /api/v1/namespaces/{namespace}/serviceaccounts/{name}

参数

  • name (路径参数): string, 必需

    ServiceAccount 的名称。

  • namespace (路径参数): string,必需

    namespace

  • pretty (查询参数): string

    pretty

响应

200 (ServiceAccount): OK

401: Unauthorized

list 列出或监控 ServiceAccount 类型的对象

HTTP 请求

GET /api/v1/namespaces/{namespace}/serviceaccounts

参数

  • namespace (路径参数): string,必需

    namespace

  • limit (查询参数): integer

    limit

  • pretty (查询参数): string

    pretty

  • sendInitialEvents (查询参数): boolean

sendInitialEvents

  • watch (查询参数): boolean

    watch

响应

200 (ServiceAccountList): OK

401: Unauthorized

list 列出或监控 ServiceAccount 类型的对象

HTTP 请求

GET /api/v1/serviceaccounts

参数

  • limit (查询参数): integer

    limit

  • pretty (查询参数): string

    pretty

  • sendInitialEvents (查询参数): boolean

sendInitialEvents

  • watch (查询参数): boolean

    watch

响应

200 (ServiceAccountList): OK

401: Unauthorized

create 创建一个 ServiceAccount

HTTP 请求

POST /api/v1/namespaces/{namespace}/serviceaccounts

参数

  • namespace (路径参数): string,必需

    namespace

  • dryRun (查询参数): string

    dryRun

  • pretty (查询参数): string

    pretty

响应

200 (ServiceAccount): OK

201 (ServiceAccount): Created

202 (ServiceAccount): Accepted

401: Unauthorized

update 替换指定的 ServiceAccount

HTTP 请求

PUT /api/v1/namespaces/{namespace}/serviceaccounts/{name}

参数

  • name (路径参数): string,必需

    ServiceAccount 的名称。

  • namespace (路径参数): string,必需

    namespace

  • dryRun (查询参数): string

    dryRun

  • pretty (查询参数): string

    pretty

响应

200 (ServiceAccount): OK

201 (ServiceAccount): Created

401: Unauthorized

patch 部分更新指定的 ServiceAccount

HTTP 请求

PATCH /api/v1/namespaces/{namespace}/serviceaccounts/{name}

参数

  • name (路径参数): string,必需

    ServiceAccount 的名称。

  • namespace (路径参数): string,必需

    namespace

  • body: Patch, required

  • dryRun (查询参数): string

    dryRun

  • force (查询参数): boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (ServiceAccount): OK

201 (ServiceAccount): Created

401: Unauthorized

delete 删除一个 ServiceAccount

HTTP 请求

DELETE /api/v1/namespaces/{namespace}/serviceaccounts/{name}

参数

  • name (路径参数): string,必需

    ServiceAccount 的名称。

  • dryRun (查询参数): string

    dryRun

响应

200 (ServiceAccount): OK

202 (ServiceAccount): Accepted

401: Unauthorized

deletecollection 删除 ServiceAccount 的集合

HTTP 请求

DELETE /api/v1/namespaces/{namespace}/serviceaccounts

参数

  • dryRun (查询参数): string

    dryRun

  • limit (查询参数): integer

    limit

  • pretty (查询参数): string

    pretty

  • sendInitialEvents (查询参数): boolean

sendInitialEvents

响应

200 (Status): OK

401: Unauthorized

6.5.4.2 - TokenRequest

TokenRequest 为给定的服务账号请求一个令牌。

apiVersion: authentication.k8s.io/v1

import "k8s.io/api/authentication/v1"

TokenRequest

TokenRequest 为给定的服务账号请求一个令牌。


  • apiVersion: authentication.k8s.io/v1

  • kind: TokenRequest

TokenRequestSpec

TokenRequestSpec 包含客户端提供的令牌请求参数。


  • audiences ([]string),必需

    audiences 是令牌预期的受众。 令牌的接收方必须在令牌的受众列表中用一个标识符来标识自己,否则应拒绝该令牌。 为多个受众签发的令牌可用于认证所列举的任意受众的身份,但这意味着目标受众彼此之间的信任程度较高。

  • boundObjectRef (BoundObjectReference)

    boundObjectRef 是对令牌所绑定的一个对象的引用。该令牌只有在绑定对象存在时才有效。 注:API 服务器的 TokenReview 端点将校验 boundObjectRef,但其他受众可能不用这样。 如果你想要快速撤销,请为 expirationSeconds 设一个较小的值。

    BoundObjectReference 是对令牌所绑定的一个对象的引用。

    • boundObjectRef.apiVersion (string)

    引用对象的 API 版本。

    • boundObjectRef.kind (string)

      引用对象的类别。有效的类别为 “Pod” 和 “Secret”。

    • boundObjectRef.name (string)

      引用对象的名称。

    • boundObjectRef.uid (string) 引用对象的 UID。

  • expirationSeconds (int64)

    expirationSeconds 是请求生效的持续时间。 令牌签发方可能返回一个生效期不同的令牌,因此客户端需要检查响应中的 “expiration” 字段。

TokenRequestStatus

TokenRequestStatus 是一个令牌请求的结果。


  • expirationTimestamp (Time),必需

    expirationTimestamp 是已返回令牌的到期时间。

    Time 是 time.Time 的包装器,支持正确编组为 YAML 和 JSON。为 time 包提供的许多工厂方法提供了包装器。

  • token (string),必需

    token 是不透明的持有者令牌(Bearer Token)。

操作


create 创建 ServiceAccount 的令牌

HTTP 请求

POST /api/v1/namespaces/{namespace}/serviceaccounts/{name}/token

参数

  • name (路径参数): string,必需

    TokenRequest 的名称

  • namespace (路径参数): string,必需

    namespace

  • body: TokenRequest,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • pretty (查询参数): string

    pretty

响应

200 (TokenRequest): OK

201 (TokenRequest): Created

202 (TokenRequest): Accepted

401: Unauthorized

6.5.4.3 - TokenReview

TokenReview 尝试通过验证令牌来确认已知用户。

apiVersion: authentication.k8s.io/v1

import "k8s.io/api/authentication/v1"

TokenReview

TokenReview 尝试通过验证令牌来确认已知用户。 注意:TokenReview 请求可能会被 kube-apiserver 中的 webhook 令牌验证器插件缓存。


TokenReviewSpec

TokenReviewPec 是对令牌身份验证请求的描述。


  • audiences ([]string)

    audiences 是带有令牌的资源服务器标识为受众的标识符列表。 受众感知令牌身份验证器将验证令牌是否适用于此列表中的至少一个受众。 如果未提供受众,受众将默认为 Kubernetes API 服务器的受众。

  • token (string)

    token 是不透明的持有者令牌(Bearer Token)。

TokenReviewStatus

TokenReviewStatus 是令牌认证请求的结果。


  • audiences ([]string)

    audiences 是身份验证者选择的与 TokenReview 和令牌兼容的受众标识符。标识符是 TokenReviewSpec 受众和令牌受众的交集中的任何标识符。设置 spec.audiences 字段的 TokenReview API 的客户端应验证在 status.audiences 字段中返回了兼容的受众标识符, 以确保 TokenReview 服务器能够识别受众。如果 TokenReview 返回一个空的 status.audience 字段,其中 status.authenticated 为 “true”, 则该令牌对 Kubernetes API 服务器的受众有效。

  • authenticated (boolean)

    authenticated 表示令牌与已知用户相关联。

  • error (string)

    error 表示无法检查令牌

  • user (UserInfo)

    user 是与提供的令牌关联的 UserInfo。

    UserInfo 保存实现 user.Info 接口所需的用户信息

    • user.extra (map[string][]string)

      验证者提供的任何附加信息。

    • user.groups ([]string)

      此用户所属的组的名称。

    • user.uid (string)

      跨时间标识此用户的唯一值。如果删除此用户并添加另一个同名用户,他们将拥有不同的 UID。

    • user.username (string)

      在所有活跃用户中唯一标识此用户的名称。

操作


create 创建一个TokenReview

HTTP 请求

POST /apis/authentication.k8s.io/v1/tokenreviews

参数

响应

200 (TokenReview): OK

201 (TokenReview): Created

202 (TokenReview): Accepted

401: Unauthorized

6.5.4.4 - CertificateSigningRequest

CertificateSigningRequest 对象提供了一种通过提交证书签名请求并异步批准和颁发 x509 证书的机制。

apiVersion: certificates.k8s.io/v1

import "k8s.io/api/certificates/v1"

证书签名请求 CertificateSigningRequest

CertificateSigningRequest 对象提供了一种通过提交证书签名请求并异步批准和颁发 x509 证书的机制。

Kubelets 使用 CertificateSigningRequest API 来获取:

  1. 向 kube-apiserver 进行身份认证的客户端证书(使用 “kubernetes.io/kube-apiserver-client-kubelet” signerName)。
  2. kube-apiserver 可以安全连接到 TLS 端点的服务证书(使用 “kubernetes.io/kubelet-serving” signerName)。

此 API 可用于请求客户端证书以向 kube-apiserver 进行身份验证(使用 “kubernetes.io/kube-apiserver-client” 签名者名称),或从自定义非 Kubernetes 签名者那里获取证书。


  • apiVersion: certificates.k8s.io/v1

  • kind: CertificateSigningRequest

  • metadata (ObjectMeta)

  • spec ( CertificateSigningRequestSpec),必需

    spec 包含证书请求,并且在创建后是不可变的。 只有 request、signerName、expirationSeconds 和 usages 字段可以在创建时设置。 其他字段由 Kubernetes 派生,用户无法修改。

  • status ( CertificateSigningRequestStatus)

    status 包含有关请求是被批准还是拒绝的信息,以及签名者颁发的证书或指示签名者失败的状况。

CertificateSigningRequestSpec

CertificateSigningRequestSpec 包含证书请求。


  • signerName (string),必需

    signerName 表示请求的签名者,是一个限定名。

    CertificateSigningRequests 的 list/watch 请求可以使用 “spec.signerName=NAME” 字段选择器进行过滤。

    众所周知的 Kubernetes 签名者有:

    1. “kubernetes.io/kube-apiserver-client”:颁发客户端证书,用于向 kube-apiserver 进行身份验证。 对此签名者的请求永远不会被 kube-controller-manager 自动批准, 可以由 kube-controller-manager 中的 “csrsigning” 控制器颁发。
    2. “kubernetes.io/kube-apiserver-client-kubelet”:颁发客户端证书,kubelet 用于向 kube-apiserver 进行身份验证。 对此签名者的请求可以由 kube-controller-manager 中的 “csrapproving” 控制器自动批准, 并且可以由 kube-controller-manager 中的 “csrsigning” 控制器颁发。
    3. “kubernetes.io/kubelet-serving” 颁发服务证书,kubelet 用于服务 TLS 端点,kube-apiserver 可以安全的连接到这些端点。 对此签名者的请求永远不会被 kube-controller-manager 自动批准, 可以由 kube-controller-manager 中的 “csrsigning” 控制器颁发。

    更多详细信息,请访问 https://kubernetes.io/zh-cn/docs/reference/access-authn-authz/certificate-signing-requests/#kubernetes-signers

    也可以指定自定义 signerName。签名者定义如下:

    1. 信任分发:信任(CA 证书包)是如何分发的。
    2. 许可的主体:当请求不允许的主体时的行为。
    3. 请求中必需、许可或禁止的 x509 扩展(包括是否允许 subjectAltNames、哪些类型、对允许值的限制) 以及请求不允许的扩展时的行为。
    4. 必需、许可或禁止的密钥用途/扩展密钥用途。
    5. 过期/证书生命周期:是否由签名者确定,管理员可配置。
    6. 是否允许申请 CA 证书。
  • expirationSeconds (int32)

    expirationSeconds 是所颁发证书的所请求的有效期。 证书签署者可以颁发具有不同有效期的证书, 因此客户端必须检查颁发证书中 notBefore 和 notAfter 字段之间的增量以确定实际持续时间。

    众所周知的 Kubernetes 签名者在 v1.22+ 版本内实现将遵守此字段, 只要请求的持续时间不大于最大持续时间,它们将遵守 Kubernetes 控制管理器的 --cluster-signing-duration CLI 标志。

    由于各种原因,证书签名者可能忽略此字段:

    1. 不认识此字段的旧签名者(如 v1.22 版本之前的实现)
    2. 配置的最大持续时间小于请求持续时间的签名者
    3. 配置的最小持续时间大于请求持续时间的签名者

    expirationSeconds 的最小有效值为 600,即 10 分钟。

  • extra (map[string][]string)

    extra 包含创建 CertificateSigningRequest 的用户的额外属性。 在创建时由 API 服务器填充,且不可变。

  • groups ([]string)

    Atomic:将在合并过程中被替换

    groups 包含创建 CertificateSigningRequest 的用户的组成员关系。 在创建时由 API 服务器填充,且不可变。

  • uid (string)

    uid 包含创建 CertificateSigningRequest 的用户的 uid 。 在创建时由 API 服务器填充,且不可变。

  • usages ([]string)

    Atomic:将在合并期间被替换

    usages 指定颁发证书中请求的一组密钥用途。

    TLS 客户端证书的请求通常要求:"digital signature"、"key encipherment"、"client auth"。

    TLS 服务证书的请求通常要求:"key encipherment"、"digital signature"、"server auth"。

    有效值: "signing"、"digital signature"、"content commitment"、 "key encipherment"、"key agreement"、"data encipherment"、 "cert sign"、"crl sign"、"encipher only"、"decipher only"、"any"、 "server auth"、"client auth"、 "code signing"、"email protection"、"s/mime"、 "ipsec end system"、"ipsec tunnel"、"ipsec user"、 "timestamping"、"ocsp signing"、"microsoft sgc"、"netscape sgc"。

  • username (string)

    username 包含创建 CertificateSigningRequest 的用户名。 在创建时由 API 服务器填充,且不可变。

CertificateSigningRequestStatus

CertificateSigningRequestStatus 包含用于指示请求的批准/拒绝/失败状态和颁发证书的状况。


  • certificate ([]byte)

    Atomic:将在合并期间被替换

    certificate 在出现 Approved 状况后,由签名者使用已颁发的证书填充。 这个字段通过 /status 子资源设置。填充后,该字段将不可变。

    如果证书签名请求被拒绝,则添加类型为 “Denied” 的状况,并且保持该字段为空。 如果签名者不能颁发证书,则添加类型为 “Failed” 的状况,并且保持该字段为空。

    验证要求:

    1. 证书必须包含一个或多个 PEM 块。
    2. 所有的 PEM 块必须有 “CERTIFICATE” 标签,不包含头和编码的数据, 必须是由 BER 编码的 ASN.1 证书结构,如 RFC5280 第 4 节所述。
    3. 非 PEM 内容可能出现在 “CERTIFICATE”PEM 块之前或之后,并且是未验证的, 允许如 RFC7468 5.2 节中描述的解释性文本。

    如果存在多个 PEM 块,并且所请求的 spec.signerName 的定义没有另外说明, 那么第一个块是颁发的证书,后续的块应该被视为中间证书并在 TLS 握手中呈现。

    证书编码为 PEM 格式。

    当序列化为 JSON 或 YAML 时,数据额外采用 base64 编码,它包括:

    base64(
        -----BEGIN CERTIFICATE-----
        ...
        -----END CERTIFICATE-----
    )
    
  • conditions ([]CertificateSigningRequestCondition)

    Map:键类型的唯一值将在合并期间保留

    应用于请求的状况。已知的状况有 "Approved"、"Denied" 与 "Failed"。

    CertificateSigningRequestCondition 描述 CertificateSigningRequest 对象的状况。

    • conditions.status (string),必需

    状况的状态,True、False、Unknown 之一。Approved、Denied 与 Failed 的状况不可以是 "False" 或 "Unknown"。

    • conditions.type (string),必需

    状况的类型。已知的状况是 "Approved"、"Denied" 与 "Failed"。

    通过 /approval 子资源添加 “Approved” 状况,表示请求已被批准并且应由签名者颁发。

    通过 /approval 子资源添加 “Denied” 状况,指示请求被拒绝并且不应由签名者颁发。

    通过 /status 子资源添加 “Failed” 状况,表示签名者未能颁发证书。

    Approved 和 Denied 状况是相互排斥的。Approved、Denied 和 Failed 状况一旦添加就无法删除。

    给定类型只允许设置一种状况。

    • conditions.lastTransitionTime (Time)

      lastTransitionTime 是状况上一次从一种状态转换到另一种状态的时间。 如果未设置,当添加新状况类型或更改现有状况的状态时,服务器默认为当前时间。

      Time 是 time.Time 的包装器,支持正确编码为 YAML 和 JSON。为 time 包提供的许多工厂方法提供了包装器。

    • conditions.lastUpdateTime (Time)

    lastUpdateTime 是该状况最后一次更新的时间。

    Time 是 time.Time 的包装器,支持正确编组为 YAML 和 JSON。为 time 包提供的许多工厂方法提供了包装器。

    • conditions.message (string)

    message 包含一个人类可读的消息,包含关于请求状态的详细信息。

    • conditions.reason (string)

    reason 表示请求状态的简短原因。

CertificateSigningRequestList

CertificateSigningRequestList 是 CertificateSigningRequest 对象的集合。


  • apiVersion: certificates.k8s.io/v1

  • kind: CertificateSigningRequestList

  • metadata (ListMeta)

  • items ([]CertificateSigningRequest),必需

    items 是 CertificateSigningRequest 对象的集合。

操作


get 读取指定的 CertificateSigningRequest

HTTP 请求

GET /apis/certificates.k8s.io/v1/certificatesigningrequests/{name}

参数

  • name (路径参数): string,必需

    CertificateSigningRequest 的名称。

  • pretty (查询参数): string

    pretty

响应

200 (CertificateSigningRequest): OK

401: Unauthorized

get 读取指定 CertificateSigningRequest 的批准信息

HTTP 请求

GET /apis/certificates.k8s.io/v1/certificatesigningrequests/{name}/approval

参数

  • name (路径参数): string,必需

    CertificateSigningRequest 的名称。

  • pretty (查询参数): string

    pretty

响应

200 (CertificateSigningRequest): OK

401: Unauthorized

get 读取指定 CertificateSigningRequest 的状态

HTTP 请求

GET /apis/certificates.k8s.io/v1/certificatesigningrequests/{name}/status

参数

  • name (路径参数): string,必需

    CertificateSigningRequest 的名称。

  • pretty (查询参数): string

    pretty

响应

200 (CertificateSigningRequest): OK

401: Unauthorized

list list 或 watch CertificateSigningRequest 类型的对象

HTTP 请求

GET /apis/certificates.k8s.io/v1/certificatesigningrequests

参数

响应

200 (CertificateSigningRequestList): OK

401: Unauthorized

create 创建一个 CertificateSigningRequest

HTTP 请求

POST /apis/certificates.k8s.io/v1/certificatesigningrequests

参数

响应

200 (CertificateSigningRequest): OK

201 (CertificateSigningRequest): Created

202 (CertificateSigningRequest): Accepted

401: Unauthorized

update 替换指定的 CertificateSigningRequest

HTTP 请求

PUT /apis/certificates.k8s.io/v1/certificatesigningrequests/{name}

参数

响应

200 (CertificateSigningRequest): OK

201 (CertificateSigningRequest): Created

401: Unauthorized

update 替换对指定 CertificateSigningRequest 的批准信息

HTTP 请求

PUT /apis/certificates.k8s.io/v1/certificatesigningrequests/{name}/approval

参数

响应

200 (CertificateSigningRequest): OK

201 (CertificateSigningRequest): Created

401: Unauthorized

update 替换指定 CertificateSigningRequest 的状态

HTTP 请求

PUT /apis/certificates.k8s.io/v1/certificatesigningrequests/{name}/status

参数

响应

200 (CertificateSigningRequest): OK

201 (CertificateSigningRequest): Created

401: Unauthorized

patch 部分更新指定的 CertificateSigningRequest

HTTP 请求

PATCH /apis/certificates.k8s.io/v1/certificatesigningrequests/{name}

参数

  • name (路径参数): string,必需

    CertificateSigningRequest 的名称。

  • body: Patch,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

响应

200 (CertificateSigningRequest): OK

201 (CertificateSigningRequest): Created

401: Unauthorized

patch 部分更新指定 CertificateSigningRequest 的批准信息

HTTP 请求

PATCH /apis/certificates.k8s.io/v1/certificatesigningrequests/{name}/approval

参数

  • name (路径参数): string,必需

    CertificateSigningRequest 的名称。

  • body: Patch,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

响应

200 (CertificateSigningRequest): OK

201 (CertificateSigningRequest): Created

401: Unauthorized

patch 部分更新指定 CertificateSigningRequest 的状态

HTTP 请求

PATCH /apis/certificates.k8s.io/v1/certificatesigningrequests/{name}/status

参数

  • name (路径参数): string,必需

    CertificateSigningRequest 的名称。

  • body: Patch,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

响应

200 (CertificateSigningRequest): OK

201 (CertificateSigningRequest): Created

401: Unauthorized

delete 删除一个 CertificateSigningRequest

HTTP 请求

DELETE /apis/certificates.k8s.io/v1/certificatesigningrequests/{name}

参数

  • name (路径参数): string,必需

    CertificateSigningRequest 的名称。

  • body: DeleteOptions

  • dryRun (查询参数): string

    dryRun

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除 CertificateSigningRequest 集合

HTTP 请求

DELETE /apis/certificates.k8s.io/v1/certificatesigningrequests

参数

响应

200 (Status): OK

401: Unauthorized

6.5.4.5 - ClusterTrustBundle v1alpha1

ClusterTrustBundle 是一个集群范围的容器,用于存放 X.509 信任锚(根证书)。

apiVersion: certificates.k8s.io/v1alpha1

import "k8s.io/api/certificates/v1alpha1"

ClusterTrustBundle

ClusterTrustBundle 是一个集群范围的容器,用于存放 X.509 信任锚(根证书)。

ClusterTrustBundle 对象被视为可被集群中的任何已通过身份验证的用户读取, 因为此对象可以由使用 clusterTrustBundle 投射的 Pod 挂载。 所有服务账号默认都有对 ClusterTrustBundle 的读取权限。 对于仅对集群具有命名空间级访问权限的用户,可以通过伪装他们可以访问的服务账号来读取 ClusterTrustBundle。

ClusterTrustBundle 可以选择与特定的签名程序相关联,此时它包含该签名程序的一组有效信任锚。 签名程序可以有多个关联的 ClusterTrustBundle; 对于该签名程序而言每个 ClusterTrustBundle 都是独立的一组信任锚。 准入控制用于确保只有对签名程序有访问权限的用户才能创建或修改相应的捆绑包。


  • apiVersion: certificates.k8s.io/v1alpha1

  • kind: ClusterTrustBundle

ClusterTrustBundleSpec

ClusterTrustBundleSpec 包含签名程序和信任锚。


  • trustBundle (string),必需

    trustBundle 包含此捆绑包的各个 X.509 信任锚,这个 PEM 捆绑包是采用 PEM 包装的 DER 格式的若干 X.509 证书。

    数据必须仅由可解析为有效 X.509 证书的 PEM 证书块组成。 每个证书必须包含设置了 CA 标志的基本约束扩展。 API 服务器将拒绝包含重复证书或使用 PEM 块头的对象。

    ClusterTrustBundles 的使用者(包括 kubelet)可以根据自己的逻辑对此文件中的证书块进行重新排序和去重, 也可以删除 PEM 块头和块间数据。

  • signerName (string)

    signerName 表示关联的签名程序(如果有)。

    要创建或更新设置了 signerName 属性的 ClusterTrustBundle,你必须具备以下集群范围的权限:

    group=certificates.k8s.io resource=signers resourceName=\<签名程序名称> verb=attest

    如果 signerName 不为空,则 ClusterTrustBundle 对象的名称必须以签名程序名称作为前缀(将斜杠转换为冒号)。 例如,对于签名程序名称 example.com/foo,有效的 ClusterTrustBundle 对象名称包括 example.com:foo:abcexample.com:foo:v1

    如果 signerName 为空,则 ClusterTrustBundle 对象的名称不能具有此类前缀。

    针对 ClusterTrustBundles 的列举/监视请求可以使用 spec.signerName=NAME 字段选择算符针对此字段进行过滤。

ClusterTrustBundleList

ClusterTrustBundleList 是 ClusterTrustBundle 对象的集合。


  • apiVersion: certificates.k8s.io/v1alpha1

  • kind: ClusterTrustBundleList

  • metadata (ListMeta)

    metadata 包含列表的元数据。

  • items ([]ClusterTrustBundle),必需

    items 是 ClusterTrustBundle 对象的集合。

操作


get 读取指定的 ClusterTrustBundle

HTTP 请求

GET /apis/certificates.k8s.io/v1alpha1/clustertrustbundles/{name}

参数

  • name路径参数):string,必需

    ClusterTrustBundle 的名称。

  • pretty查询参数):string

    pretty

响应

200 (ClusterTrustBundle): OK

401: Unauthorized

list 列举或监视类别为 ClusterTrustBundle 的对象

HTTP 请求

GET /apis/certificates.k8s.io/v1alpha1/clustertrustbundles

参数

响应

200 (ClusterTrustBundleList): OK

401: Unauthorized

create 创建 ClusterTrustBundle

HTTP 请求

POST /apis/certificates.k8s.io/v1alpha1/clustertrustbundles

参数

响应

200 (ClusterTrustBundle): OK

201 (ClusterTrustBundle): Created

202 (ClusterTrustBundle): Accepted

401: Unauthorized

update 替换指定的 ClusterTrustBundle

HTTP 请求

PUT /apis/certificates.k8s.io/v1alpha1/clustertrustbundles/{name}

参数

  • name路径参数):string,必需

    ClusterTrustBundle 的名称。

  • body: ClusterTrustBundle,必需

  • dryRun查询参数):string

    dryRun

  • fieldManager查询参数):string

    fieldManager

  • fieldValidation查询参数):string

    fieldValidation

  • pretty查询参数):string

    pretty

响应

200 (ClusterTrustBundle): OK

201 (ClusterTrustBundle): Created

401: Unauthorized

patch 部分更新指定的 ClusterTrustBundle

HTTP 请求

PATCH /apis/certificates.k8s.io/v1alpha1/clustertrustbundles/{name}

参数

  • name路径参数):string,必需

    ClusterTrustBundle 的名称。

  • body: Patch,必需

  • dryRun查询参数):string

    dryRun

  • fieldManager查询参数):string

    fieldManager

  • fieldValidation查询参数):string

    fieldValidation

  • force查询参数):boolean

    force

  • pretty查询参数):string

    pretty

响应

200 (ClusterTrustBundle): OK

201 (ClusterTrustBundle): Created

401: Unauthorized

delete 删除 ClusterTrustBundle

HTTP 请求

DELETE /apis/certificates.k8s.io/v1alpha1/clustertrustbundles/{name}

参数

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除 ClusterTrustBundle 的集合

HTTP 请求

DELETE /apis/certificates.k8s.io/v1alpha1/clustertrustbundles

参数

响应

200 (Status): OK

401: Unauthorized

6.5.4.6 - SelfSubjectReview

SelfSubjectReview 包含 kube-apiserver 所拥有的与发出此请求的用户有关的用户信息。

apiVersion: authentication.k8s.io/v1

import "k8s.io/api/authentication/v1"

SelfSubjectReview

SelfSubjectReview 包含 kube-apiserver 所拥有的与发出此请求的用户有关的用户信息。 使用伪装时,用户将收到被伪装用户的用户信息。 如果使用伪装或请求头部进行身份验证,则所有额外的键都将被忽略大小写并以小写形式返回结果。


SelfSubjectReviewStatus

SelfSubjectReviewStatus 由 kube-apiserver 进行填充并发送回用户。


  • userInfo (UserInfo)

    发出此请求的用户的用户属性。

    userInfo 包含实现 user.Info 接口所需的用户相关信息。

    • userInfo.extra (map[string][]string)

      由身份认证组件提供的所有附加信息。

    • userInfo.groups ([]string)

      此用户所属的用户组的名称。

    • userInfo.uid (string)

      跨时间标识此用户的唯一值。如果此用户被删除且另一个同名用户被添加,他们将具有不同的 UID。

    • userInfo.username (string)

      在所有活跃用户中标识此用户的名称。

操作


create 创建 SelfSubjectReview

HTTP 请求

POST /apis/authentication.k8s.io/v1/selfsubjectreviews

参数

响应

200 (SelfSubjectReview): OK

201 (SelfSubjectReview): Created

202 (SelfSubjectReview): Accepted

401: Unauthorized

6.5.5 - 鉴权资源

6.5.5.1 - LocalSubjectAccessReview

LocalSubjectAccessReview 检查用户或组是否可以在给定的命名空间内执行某操作。

apiVersion: authorization.k8s.io/v1

import "k8s.io/api/authorization/v1"

LocalSubjectAccessReview

LocalSubjectAccessReview 检查用户或组是否可以在给定的命名空间内执行某操作。 划分命名空间范围的资源简化了命名空间范围的策略设置,例如权限检查。


  • apiVersion: authorization.k8s.io/v1

  • kind: LocalSubjectAccessReview

  • metadata (ObjectMeta)

    标准的列表元数据。 更多信息:https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata

  • spec (SubjectAccessReviewSpec),必需

    spec 包含有关正在评估的请求的信息。 spec.namespace 必须是你的请求所针对的命名空间。 如果留空,则会被设置默认值。

  • status (SubjectAccessReviewStatus)

    status 由服务器填写,表示请求是否被允许。

操作


create 创建 LocalSubjectAccessReview

HTTP 请求

POST /apis/authorization.k8s.io/v1/namespaces/{namespace}/localsubjectaccessreviews

参数

响应

200 (LocalSubjectAccessReview): OK

201 (LocalSubjectAccessReview): Created

202 (LocalSubjectAccessReview): Accepted

401: Unauthorized

6.5.5.2 - SelfSubjectAccessReview

SelfSubjectAccessReview 检查当前用户是否可以执行某操作。

apiVersion: authorization.k8s.io/v1

import "k8s.io/api/authorization/v1"

SelfSubjectAccessReview

SelfSubjectAccessReview 检查当前用户是否可以执行某操作。 不填写 spec.namespace 表示 “在所有命名空间中”。 Self 是一个特殊情况,因为用户应始终能够检查自己是否可以执行某操作。


  • apiVersion: authorization.k8s.io/v1

  • kind: SelfSubjectAccessReview

SelfSubjectAccessReviewSpec

SelfSubjectAccessReviewSpec 是访问请求的描述。 resourceAuthorizationAttributes 和 nonResourceAuthorizationAttributes 二者必须设置其一,并且只能设置其一。


  • nonResourceAttributes (NonResourceAttributes)

    nonResourceAttributes 描述非资源访问请求的信息。

    nonResourceAttributes 包括提供给 Authorizer 接口进行非资源请求鉴权时所用的属性。

    • nonResourceAttributes.path (string)

      path 是请求的 URL 路径。

    • nonResourceAttributes.verb (string)

      verb 是标准的 HTTP 动作。

  • resourceAttributes (ResourceAttributes)

    resourceAuthorizationAttributes 描述资源访问请求的信息。

    resourceAttributes 包括提供给 Authorizer 接口进行资源请求鉴权时所用的属性。

    • resourceAttributes.group (string)

      group 是资源的 API 组。 "*" 表示所有组。

    • resourceAttributes.name (string)

      name 是 "get" 正在请求或 "delete" 已删除的资源的名称。 ""(空字符串)表示所有资源。

    • resourceAttributes.namespace (string)

      namespace 是正在请求的操作的命名空间。 目前,无命名空间和所有命名空间之间没有区别。 对于 LocalSubjectAccessReviews,默认为 ""(空字符串)。 对于集群范围的资源,默认为 ""(空字符串)。 对于来自 SubjectAccessReview 或 SelfSubjectAccessReview 的命名空间范围的资源, ""(空字符串)表示 "all"(所有资源)。

    • resourceAttributes.resource (string)

      resource 是现有的资源类别之一。 "*" 表示所有资源类别。

    • resourceAttributes.subresource (string)

      subresource 是现有的资源类型之一。 "" 表示无。

    • resourceAttributes.verb (string)

      verb 是 kubernetes 资源 API 动作,例如 get、list、watch、create、update、delete、proxy。 "*" 表示所有动作。

    • resourceAttributes.version (string)

      version 是资源的 API 版本。 "*" 表示所有版本。

操作


create 创建 SelfSubjectAccessReview

HTTP 请求

POST /apis/authorization.k8s.io/v1/selfsubjectaccessreviews

参数

响应

200 (SelfSubjectAccessReview): OK

201 (SelfSubjectAccessReview): Created

202 (SelfSubjectAccessReview): Accepted

401: Unauthorized

6.5.5.3 - SelfSubjectRulesReview

SelfSubjectRulesReview 枚举当前用户可以在某命名空间内执行的操作集合。

apiVersion: authorization.k8s.io/v1

import "k8s.io/api/authorization/v1"

SelfSubjectRulesReview

SelfSubjectRulesReview 枚举当前用户可以在某命名空间内执行的操作集合。 返回的操作列表可能不完整,具体取决于服务器的鉴权模式以及评估过程中遇到的任何错误。 SelfSubjectRulesReview 应由 UI 用于显示/隐藏操作,或让最终用户尽快理解自己的权限。 SelfSubjectRulesReview 不得被外部系统使用以驱动鉴权决策, 因为这会引起混淆代理人(Confused deputy)、缓存有效期/吊销(Cache lifetime/revocation)和正确性问题。 SubjectAccessReview 和 LocalAccessReview 是遵从 API 服务器所做鉴权决策的正确方式。


  • apiVersion: authorization.k8s.io/v1

  • kind: SelfSubjectRulesReview

  • status (SubjectRulesReviewStatus)

    status 由服务器填写,表示用户可以执行的操作的集合。

    SubjectRulesReviewStatus 包含规则检查的结果。 此检查可能不完整,具体取决于服务器配置的 Authorizer 的集合以及评估期间遇到的任何错误。 由于鉴权规则是叠加的,所以如果某个规则出现在列表中,即使该列表不完整,也可以安全地假定该主体拥有该权限。

    • status.incomplete (boolean),必需

      当此调用返回的规则不完整时,incomplete 结果为 true。 这种情况常见于 Authorizer(例如外部 Authorizer)不支持规则评估时。

    • status.nonResourceRules ([]NonResourceRule),必需

      nonResourceRules 是允许主体对非资源执行路径执行的操作列表。 该列表顺序不重要,可以包含重复项,还可能不完整。

      nonResourceRule 包含描述非资源路径的规则的信息。

      • status.nonResourceRules.verbs ([]string),必需

        verb 是 kubernetes 非资源 API 动作的列表,例如 get、post、put、delete、patch、head、options。 * 表示所有动作。

      • status.nonResourceRules.nonResourceURLs ([]string)

        nonResourceURLs 是用户应有权访问的一组部分 URL。 允许使用 *,但仅能作为路径中最后一段且必须用于完整的一段。 * 表示全部。

    • status.resourceRules ([]ResourceRule),必需

      resourceRules 是允许主体对资源执行的操作的列表。 该列表顺序不重要,可以包含重复项,还可能不完整。

      resourceRule 是允许主体对资源执行的操作的列表。该列表顺序不重要,可以包含重复项,还可能不完整。

      • status.resourceRules.verbs ([]string),必需

        verb 是 kubernetes 资源 API 动作的列表,例如 get、list、watch、create、update、delete、proxy。 * 表示所有动作。

      • status.resourceRules.apiGroups ([]string)

        apiGroups 是包含资源的 APIGroup 的名称。 如果指定了多个 API 组,则允许对任何 API 组中枚举的资源之一请求任何操作。 * 表示所有 APIGroup。

      • status.resourceRules.resourceNames ([]string)

        resourceNames 是此规则所适用的资源名称白名单,可选。 空集合意味着允许所有资源。 * 表示所有资源。

      • status.resourceRules.resources ([]string)

        resources 是此规则所适用的资源的列表。 * 表示指定 APIGroup 中的所有资源。 */foo 表示指定 APIGroup 中所有资源的子资源 "foo"。

    • status.evaluationError (string)

      evaluationError 可以与 rules 一起出现。 它表示在规则评估期间发生错误,例如 Authorizer 不支持规则评估以及 resourceRules 和/或 nonResourceRules 可能不完整。

SelfSubjectRulesReviewSpec

SelfSubjectRulesReviewSpec 定义 SelfSubjectRulesReview 的规范。


  • namespace (string)

    namespace 是要评估规则的命名空间。 必需。

操作


create 创建 SelfSubjectRulesReview

HTTP 请求

POST /apis/authorization.k8s.io/v1/selfsubjectrulesreviews

参数

响应

200 (SelfSubjectRulesReview): OK

201 (SelfSubjectRulesReview): Created

202 (SelfSubjectRulesReview): Accepted

401: Unauthorized

6.5.5.4 - SubjectAccessReview

SubjectAccessReview 检查用户或组是否可以执行某操作。

apiVersion: authorization.k8s.io/v1

import "k8s.io/api/authorization/v1"

SubjectAccessReview

SubjectAccessReview 检查用户或组是否可以执行某操作。


SubjectAccessReviewSpec

SubjectAccessReviewSpec 是访问请求的描述。 resourceAuthorizationAttributes 和 nonResourceAuthorizationAttributes 二者必须设置其一,并且只能设置其一。


  • extra (map[string][]string)

    extra 对应于来自鉴权器的 user.Info.GetExtra() 方法。 由于这是针对 Authorizer 的输入,所以它需要在此处反映。

  • groups ([]string)

    groups 是你正在测试的组。

  • nonResourceAttributes (NonResourceAttributes)

    nonResourceAttributes 描述非资源访问请求的信息。

    nonResourceAttributes 包括提供给 Authorizer 接口进行非资源请求鉴权时所用的属性。

    • nonResourceAttributes.path (string)

      path 是请求的 URL 路径。

    • nonResourceAttributes.verb (string)

      verb 是标准的 HTTP 动作。

  • resourceAttributes (ResourceAttributes)

    resourceAuthorizationAttributes 描述资源访问请求的信息。

    resourceAttributes 包括提供给 Authorizer 接口进行资源请求鉴权时所用的属性。

    • resourceAttributes.group (string)

      group 是资源的 API 组。 "*" 表示所有资源。

    • resourceAttributes.name (string)

      name 是 "get" 正在请求或 "delete" 已删除的资源。 ""(空字符串)表示所有资源。

    • resourceAttributes.namespace (string)

      namespace 是正在请求的操作的命名空间。 目前,无命名空间和所有命名空间之间没有区别。 对于 LocalSubjectAccessReviews,默认为 ""(空字符串)。 对于集群范围的资源,默认为 ""(空字符串)。 对于来自 SubjectAccessReview 或 SelfSubjectAccessReview 的命名空间范围的资源, ""(空字符串)表示 "all"(所有资源)。

    • resourceAttributes.resource (string)

      resource 是现有的资源类别之一。 "*" 表示所有资源类别。

    • resourceAttributes.subresource (string)

      subresource 是现有的资源类别之一。 "" 表示无子资源。

    • resourceAttributes.verb (string)

      verb 是 kubernetes 资源的 API 动作,例如 get、list、watch、create、update、delete、proxy。 "*" 表示所有动作。

    • resourceAttributes.version (string)

      version 是资源的 API 版本。 "*" 表示所有版本。

  • uid (string)

    有关正在请求的用户的 UID 信息。

  • user (string)

    user 是你正在测试的用户。 如果你指定 “user” 而不是 “groups”,它将被解读为“如果 user 不是任何组的成员,将会怎样”。

SubjectAccessReviewStatus

SubjectAccessReviewStatus


  • allowed (boolean),必需

    allowed 是必需的。 如果允许该操作,则为 true,否则为 false。

  • denied (boolean)

    denied 是可选的。 如果拒绝该操作,则为 true,否则为 false。 如果 allowed 和 denied 均为 false,则 Authorizer 对是否鉴权操作没有意见。 如果 allowed 为 true,则 denied 不能为 true。

  • evaluationError (string)

    evaluationError 表示鉴权检查期间发生一些错误。 出现错误的情况下完全有可能继续确定鉴权状态。 例如,RBAC 可能缺少一个角色,但仍存在足够多的角色进行绑定,进而了解请求有关的原因。

  • reason (string)

    reason 是可选的。 它表示为什么允许或拒绝请求。

操作


create 创建 SubjectAccessReview

HTTP 请求

POST /apis/authorization.k8s.io/v1/subjectaccessreviews

参数

响应

200 (SubjectAccessReview): OK

201 (SubjectAccessReview): Created

202 (SubjectAccessReview): Accepted

401: Unauthorized

6.5.5.5 - ClusterRole

ClusterRole 是一个集群级别的 PolicyRule 逻辑分组,可以被 RoleBinding 或 ClusterRoleBinding 作为一个单元引用。

apiVersion: rbac.authorization.k8s.io/v1

import "k8s.io/api/rbac/v1"

ClusterRole

ClusterRole 是一个集群级别的 PolicyRule 逻辑分组, 可以被 RoleBinding 或 ClusterRoleBinding 作为一个单元引用。


  • apiVersion: rbac.authorization.k8s.io/v1

  • kind: ClusterRole

  • metadata (ObjectMeta)

    标准的对象元数据。

  • aggregationRule (AggregationRule)

    aggregationRule 是一个可选字段,用于描述如何构建这个 ClusterRole 的 rules。 如果设置了 aggregationRule,则 rules 将由控制器管理,对 rules 的直接变更会被该控制器阻止。

    aggregationRule 描述如何定位并聚合其它 ClusterRole 到此 ClusterRole。

    • aggregationRule.clusterRoleSelectors ([]LabelSelector)

      clusterRoleSelectors 包含一个选择器的列表,用于查找 ClusterRole 并创建规则。 如果发现任何选择器匹配的 ClusterRole,将添加其对应的权限。

  • rules ([]PolicyRule)

    rules 包含了这个 ClusterRole 的所有 PolicyRule。

    PolicyRule 包含描述一个策略规则的信息,但不包含该规则适用于哪个主体或适用于哪个命名空间的信息。

    • rules.apiGroups ([]string)

      apiGroups 是包含资源的 apiGroup 的名称。 如果指定了多个 API 组,则允许针对任何 API 组中的其中一个枚举资源来请求任何操作。 "" 表示核心 API 组,“*” 表示所有 API 组。

    • rules.resources ([]string)

      resources 是此规则所适用的资源的列表。“*” 表示所有资源。

    • rules.verbs ([]string),必需

      verbs 是适用于此规则中所包含的所有 ResourceKinds 的动作。 “*” 表示所有动作。

    • rules.resourceNames ([]string)

      resourceNames 是此规则所适用的资源名称白名单,可选。 空集合意味着允许所有资源。

    • rules.nonResourceURLs ([]string)

      nonResourceURLs 是用户应有权访问的一组部分 URL。 允许使用 “*”,但仅能作为路径中最后一段且必须用于完整的一段, 因为非资源 URL 没有划分命名空间。 此字段仅适用于从 ClusterRoleBinding 引用的 ClusterRole。 rules 可以应用到 API 资源(如 “pod” 或 “secret”)或非资源 URL 路径(如 “/api”), 但不能同时应用于两者。

ClusterRoleList

ClusterRoleList 是 ClusterRole 的集合。


  • apiVersion: rbac.authorization.k8s.io/v1

  • kind: ClusterRoleList

  • metadata (ListMeta)

    标准的对象元数据。

  • items ([]ClusterRole),必需

    items 是 ClusterRole 的列表。

操作


get 读取指定的 ClusterRole

HTTP 请求

GET /apis/rbac.authorization.k8s.io/v1/clusterroles/{name}

参数

  • name路径参数):string,必需

    ClusterRole 的名称

  • pretty查询参数):string

    pretty

响应

200 (ClusterRole): OK

401: Unauthorized

list 列出或观测类别为 ClusterRole 的对象

HTTP 请求

GET /apis/rbac.authorization.k8s.io/v1/clusterroles

参数

响应

200 (ClusterRoleList): OK

401: Unauthorized

create 创建一个 ClusterRole

HTTP 请求

POST /apis/rbac.authorization.k8s.io/v1/clusterroles

参数

响应

200 (ClusterRole): OK

201 (ClusterRole): Created

202 (ClusterRole): Accepted

401: Unauthorized

update 替换指定的 ClusterRole

HTTP 请求

PUT /apis/rbac.authorization.k8s.io/v1/clusterroles/{name}

参数

  • name路径参数):string,必需

    ClusterRole 的名称

  • bodyClusterRole,必需

  • dryRun查询参数):string

    dryRun

  • fieldManager查询参数):string

    fieldManager

  • fieldValidation查询参数):string

    fieldValidation

  • pretty查询参数):string

    pretty

响应

200 (ClusterRole): OK

201 (ClusterRole): Created

401: Unauthorized

patch 部分更新指定的 ClusterRole

HTTP 请求

PATCH /apis/rbac.authorization.k8s.io/v1/clusterroles/{name}

参数

  • name路径参数):string,必需

    ClusterRole 的名称

  • bodyPatch,必需

  • dryRun查询参数):string

    dryRun

  • fieldManager查询参数):string

    fieldManager

  • fieldValidation查询参数):string

    fieldValidation

  • force查询参数):boolean

    force

  • pretty查询参数):string

    pretty

响应

200 (ClusterRole): OK

201 (ClusterRole): Created

401: Unauthorized

delete 删除一个 ClusterRole

HTTP 请求

DELETE /apis/rbac.authorization.k8s.io/v1/clusterroles/{name}

参数

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除 ClusterRole 的集合

HTTP 请求

DELETE /apis/rbac.authorization.k8s.io/v1/clusterroles

参数

响应

200 (Status): OK

401: Unauthorized

6.5.5.6 - ClusterRoleBinding

ClusterRoleBinding 引用 ClusterRole,但不包含它。

apiVersion: rbac.authorization.k8s.io/v1

import "k8s.io/api/rbac/v1"

ClusterRoleBinding

ClusterRoleBinding 引用 ClusterRole,但不包含它。 它可以引用全局命名空间中的 ClusterRole,并通过 Subject 添加主体信息。


  • apiVersion: rbac.authorization.k8s.io/v1

  • kind: ClusterRoleBinding

  • metadata (ObjectMeta)

    标准对象的元数据。

  • roleRef (RoleRef),必需

    RoleRef 只能引用全局命名空间中的 ClusterRole。 如果无法解析 RoleRef,则 Authorizer 必定返回一个错误。这个字段是不可变的。

    RoleRef 包含指向正被使用的角色的信息。

    • roleRef.apiGroup (string),必需

      apiGroup 是被引用资源的组

    • roleRef.kind (string),必需

      kind 是被引用的资源的类别

    • roleRef.name (string),必需

      name 是被引用的资源的名称

  • subjects ([]Subject)

    Subjects 包含角色所适用的对象的引用。

    Subject 包含对角色绑定所适用的对象或用户标识的引用。其中可以包含直接 API 对象的引用或非对象(如用户名和组名)的值。

    • subjects.kind (string),必需

    被引用的对象的类别。这个 API 组定义的值是 UserGroupServiceAccount。 如果 Authorizer 无法识别类别值,则 Authorizer 应报告一个错误。

    • subjects.name (string),必需

      被引用的对象的名称。

    • subjects.apiGroup (string)

    apiGroup 包含被引用主体的 API 组。对于 ServiceAccount 主体默认为 ""。 对于 User 和 Group 主体,默认为 "rbac.authorization.k8s.io"。

    • subjects.namespace (string)

      被引用对象的命名空间。 如果对象类别是 "User" 或 "Group" 等非命名空间作用域的对象且该值不为空, 则 Authorizer 应报告一个错误。

ClusterRoleBindingList

ClusterRoleBindingList 是 ClusterRoleBinding 的集合。


  • apiVersion: rbac.authorization.k8s.io/v1

  • kind: ClusterRoleBindingList

操作


get 读取指定的 ClusterRoleBinding

HTTP 请求

GET /apis/rbac.authorization.k8s.io/v1/clusterrolebindings/{name}

参数

  • name (路径参数): string,必需

    ClusterRoleBinding 的名称

  • pretty (查询参数): string

    pretty

响应

200 (ClusterRoleBinding): OK

401: Unauthorized

list 列出或观测类别为 ClusterRoleBinding 的对象

HTTP 请求

GET /apis/rbac.authorization.k8s.io/v1/clusterrolebindings

参数

响应

200 (ClusterRoleBindingList): OK

401: Unauthorized

create 创建 ClusterRoleBinding

HTTP 请求

POST /apis/rbac.authorization.k8s.io/v1/clusterrolebindings

参数

响应

200 (ClusterRoleBinding): OK

201 (ClusterRoleBinding): Created

202 (ClusterRoleBinding): Accepted

401: Unauthorized

update 替换指定的 ClusterRoleBinding

HTTP 请求

PUT /apis/rbac.authorization.k8s.io/v1/clusterrolebindings/{name}

参数

  • name (路径参数): string,必需

    ClusterRoleBinding 的名称

  • body: ClusterRoleBinding,必需

响应

200 (ClusterRoleBinding): OK

201 (ClusterRoleBinding): Created

401: Unauthorized

patch 部分更新指定的 ClusterRoleBinding

HTTP 请求

PATCH /apis/rbac.authorization.k8s.io/v1/clusterrolebindings/{name}

参数

  • name (路径参数): string,必需

    ClusterRoleBinding 的名称

  • body: Patch,必需

响应

200 (ClusterRoleBinding): OK

201 (ClusterRoleBinding): Created

401: Unauthorized

delete 删除 ClusterRoleBinding

HTTP 请求

DELETE /apis/rbac.authorization.k8s.io/v1/clusterrolebindings/{name}

参数

  • name (路径参数): string,必需

    ClusterRoleBinding 的名称

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除 ClusterRoleBinding 的集合

HTTP 请求

DELETE /apis/rbac.authorization.k8s.io/v1/clusterrolebindings

参数

响应

200 (Status): OK

401: Unauthorized

6.5.5.7 - Role

Role 是一个按命名空间划分的 PolicyRule 逻辑分组,可以被 RoleBinding 作为一个单元引用。

apiVersion: rbac.authorization.k8s.io/v1

import "k8s.io/api/rbac/v1"

Role

Role 是一个按命名空间划分的 PolicyRule 逻辑分组,可以被 RoleBinding 作为一个单元引用。


  • apiVersion: rbac.authorization.k8s.io/v1

  • kind: Role

  • metadata (ObjectMeta)

    标准的对象元数据。

  • rules ([]PolicyRule)

    rules 包含了这个 Role 的所有 PolicyRule。

    PolicyRule 包含描述一个策略规则的信息,但不包含该规则适用于哪个主体或适用于哪个命名空间的信息。

    • rules.apiGroups ([]string)

      apiGroups 是包含资源的 apiGroup 的名称。 如果指定了多个 API 组,则允许对任何 API 组中的其中一个枚举资源来请求任何操作。 "" 表示核心 API 组,“*” 表示所有 API 组。

    • rules.resources ([]string)

      resources 是此规则所适用的资源的列表。 “*” 表示所有资源。

    • rules.verbs ([]string),必需

      verbs 是适用于此规则中所包含的所有 ResourceKinds 的动作。 “*” 表示所有动作。

    • rules.resourceNames ([]string)

      resourceNames 是此规则所适用的资源名称白名单,可选。 空集合意味着允许所有资源。

    • rules.nonResourceURLs ([]string)

      nonResourceURLs 是用户应有权访问的一组部分 URL。 允许使用 “*”,但仅能作为路径中最后一段且必须用于完整的一段, 因为非资源 URL 没有划分命名空间。 此字段仅适用于从 ClusterRoleBinding 引用的 ClusterRole。 rules 可以应用到 API 资源(如 “pod” 或 “secret”)或非资源 URL 路径(如 “/api”), 但不能同时应用于两者。

RoleList

RoleList 是 Role 的集合。


  • apiVersion: rbac.authorization.k8s.io/v1

  • kind: RoleList

  • metadata (ListMeta)

    标准的对象元数据。

  • items ([]Role),必需

    items 是 Role 的列表。

操作


get 读取指定的 Role

HTTP 请求

GET /apis/rbac.authorization.k8s.io/v1/namespaces/{namespace}/roles/{name}

参数

  • name路径参数):string,必需

    Role 的名称

  • namespace路径参数):string,必需

    namespace

  • pretty查询参数):string

    pretty

响应

200 (Role): OK

401: Unauthorized

list 列出或观测类别为 Role 的对象

HTTP 请求

GET /apis/rbac.authorization.k8s.io/v1/namespaces/{namespace}/roles

参数

响应

200 (RoleList): OK

401: Unauthorized

list 列出或观测类别为 Role 的对象

HTTP 请求

GET /apis/rbac.authorization.k8s.io/v1/roles

参数

响应

200 (RoleList): OK

401: Unauthorized

create 创建 Role

HTTP 请求

POST /apis/rbac.authorization.k8s.io/v1/namespaces/{namespace}/roles

参数

响应

200 (Role): OK

201 (Role): Created

202 (Role): Accepted

401: Unauthorized

update 替换指定的 Role

HTTP 请求

PUT /apis/rbac.authorization.k8s.io/v1/namespaces/{namespace}/roles/{name}

参数

  • name路径参数):string,必需

    Role 的名称

  • namespace路径参数):string,必需

    namespace

  • body: Role,必需

  • dryRun查询参数):string

    dryRun

  • fieldManager查询参数):string

    fieldManager

  • fieldValidation查询参数):string

    fieldValidation

  • pretty查询参数):string

    pretty

响应

200 (Role): OK

201 (Role): Created

401: Unauthorized

patch 部分更新指定的 Role

HTTP 请求

PATCH /apis/rbac.authorization.k8s.io/v1/namespaces/{namespace}/roles/{name}

参数

  • name路径参数):string,必需

    Role 的名称

  • namespace路径参数):string,必需

    namespace

  • body: Patch,必需

  • dryRun查询参数):string

    dryRun

  • fieldManager查询参数):string

    fieldManager

  • fieldValidation查询参数):string

    fieldValidation

  • force查询参数):boolean

    force

  • pretty查询参数):string

    pretty

响应

200 (Role): OK

201 (Role): Created

401: Unauthorized

delete 删除 Role

HTTP 请求

DELETE /apis/rbac.authorization.k8s.io/v1/namespaces/{namespace}/roles/{name}

参数

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除 Role 的集合

HTTP 请求

DELETE /apis/rbac.authorization.k8s.io/v1/namespaces/{namespace}/roles

参数

响应

200 (Status): OK

401: Unauthorized

6.5.5.8 - RoleBinding

RoleBinding 引用一个角色,但不包含它。

apiVersion: rbac.authorization.k8s.io/v1

import "k8s.io/api/rbac/v1"

RoleBinding

RoleBinding 引用一个角色,但不包含它。 RoleBinding 可以引用相同命名空间中的 Role 或全局命名空间中的 ClusterRole。 RoleBinding 通过 Subjects 和所在的命名空间信息添加主体信息。 处于给定命名空间中的 RoleBinding 仅在该命名空间中有效。


  • apiVersion: rbac.authorization.k8s.io/v1

  • kind: RoleBinding

  • metadata (ObjectMeta)

标准的对象元数据。

  • roleRef (RoleRef),必需

    roleRef 可以引用当前命名空间中的 Role 或全局命名空间中的 ClusterRole。 如果无法解析 roleRef,则 Authorizer 必定返回一个错误。这个字段是不可变的。

    roleRef 包含指向正被使用的角色的信息。

  • roleRef.apiGroup (string),必需

    apiGroup 是被引用资源的组

  • roleRef.kind (string),必需

    kind 是被引用的资源的类别

  • roleRef.name (string),必需

    name 是被引用的资源的名称

  • subjects ([]Subject)

    subjects 包含角色所适用的对象的引用。

    Subject 包含对角色绑定所适用的对象或用户标识的引用。其中可以包含直接 API 对象的引用或非对象(如用户名和组名)的值。

    • subjects.kind (string),必需

      被引用的对象的类别。 这个 API 组定义的值是 UserGroupServiceAccount。 如果 Authorizer 无法识别类别值,则 Authorizer 应报告一个错误。

  • subjects.name (string),必需

    被引用的对象的名称。

  • subjects.apiGroup (string)

    apiGroup 包含被引用主体的 API 组。 对于 ServiceAccount 主体默认为 ""。 对于 User 和 Group 主体,默认为 "rbac.authorization.k8s.io"。

  • subjects.namespace (string)

    被引用的对象的命名空间。 如果对象类别是 “User” 或 “Group” 等非命名空间作用域的对象且该值不为空, 则 Authorizer 应报告一个错误。

RoleBindingList

RoleBindingList 是 RoleBinding 的集合。


  • apiVersion: rbac.authorization.k8s.io/v1

  • kind: RoleBindingList

  • metadata (ListMeta)

标准的对象元数据。

  • items ([]RoleBinding),必需

    items 是 RoleBinding 的列表。

操作


get 读取指定的 RoleBinding

HTTP 请求

GET /apis/rbac.authorization.k8s.io/v1/namespaces/{namespace}/rolebindings/{name}

参数

  • name (路径参数): string,必需

    RoleBinding 的名称

  • namespace (路径参数): string,必需

    namespace

  • pretty (查询参数): string

    pretty

响应

200 (RoleBinding): OK

401: Unauthorized

list 列出或观测类别为 RoleBinding 的对象

HTTP 请求

GET /apis/rbac.authorization.k8s.io/v1/namespaces/{namespace}/rolebindings

参数

响应

200 (RoleBindingList): OK

401: Unauthorized

list 列出或观测类别为 RoleBinding 的对象

HTTP 请求

GET /apis/rbac.authorization.k8s.io/v1/rolebindings

参数

响应

200 (RoleBindingList): OK

401: Unauthorized

create 创建 RoleBinding

HTTP 请求

POST /apis/rbac.authorization.k8s.io/v1/namespaces/{namespace}/rolebindings

参数

响应

200 (RoleBinding): OK

201 (RoleBinding): Created

202 (RoleBinding): Accepted

401: Unauthorized

update 替换指定的 RoleBinding

HTTP 请求

PUT /apis/rbac.authorization.k8s.io/v1/namespaces/{namespace}/rolebindings/{name}

参数

  • name (路径参数): string,必需

    RoleBinding 的名称

  • namespace (路径参数): string,必需

    namespace

  • body: RoleBinding,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • pretty (查询参数): string

    pretty

响应

200 (RoleBinding): OK

201 (RoleBinding): Created

401: Unauthorized

patch 部分更新指定的 RoleBinding

HTTP 请求

PATCH /apis/rbac.authorization.k8s.io/v1/namespaces/{namespace}/rolebindings/{name}

参数

  • name (路径参数): string,必需

    RoleBinding 的名称

  • namespace (路径参数): string,必需

    namespace

  • body: Patch,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • force (查询参数): boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (RoleBinding): OK

201 (RoleBinding): Created

401: Unauthorized

delete 删除 RoleBinding

HTTP 请求

DELETE /apis/rbac.authorization.k8s.io/v1/namespaces/{namespace}/rolebindings/{name}

参数

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除 RoleBinding 的集合

HTTP 请求

DELETE /apis/rbac.authorization.k8s.io/v1/namespaces/{namespace}/rolebindings

参数

响应

200 (Status): OK

401: Unauthorized

6.5.6 - 策略资源

6.5.6.1 - LimitRange

LimitRange 设置名字空间中每个资源类别的资源用量限制。

apiVersion: v1

import "k8s.io/api/core/v1"

LimitRange

LimitRange 设置名字空间中每个资源类别的资源用量限制。


  • apiVersion: v1

  • kind: LimitRange

LimitRangeSpec

LimitRangeSpec 定义与类别匹配的资源的最小/最大使用限制。


  • limits ([]LimitRangeItem),必需

    limits 是强制执行的 LimitRangeItem 对象的列表。

    LimitRangeItem 定义与类别匹配的任意资源的最小/最大使用限制。

    • limits.type (string),必需

      此限制应用到的资源的类型。

    • limits.default (map[string]Quantity)

      资源限制被省略时按资源名称设定的默认资源要求限制值。

    • limits.defaultRequest (map[string]Quantity)

      defaultRequest 是资源请求被省略时按资源名称设定的默认资源要求请求值。

    • limits.max (map[string]Quantity)

      按资源名称针对这种类别的最大使用约束。

    • limits.maxLimitRequestRatio (map[string]Quantity)

      如果指定 maxLimitRequestRatio,则所指定的资源必须设置非零的请求和限制值, 且限制除以请求小于或等于这里列举的值;此属性用来表示所指定资源的最大突发用量。

    • limits.min (map[string]Quantity)

      按资源名称区分的,针对这种类别对象的最小用量约束。

LimitRangeList

LimitRangeList 是 LimitRange 项的列表。


  • apiVersion: v1

  • kind: LimitRangeList

操作


get 读取指定的 LimitRange

HTTP 请求

GET /api/v1/namespaces/{namespace}/limitranges/{name}

参数

  • name (路径参数): string,必需

    LimitRange 的名称

  • namespace (路径参数): string,必需

    namespace

  • pretty (查询参数): string

    pretty

响应

200 (LimitRange): OK

401: Unauthorized

list 列出或监视 LimitRange 类别的对象

HTTP 请求

GET /api/v1/namespaces/{namespace}/limitranges

参数

响应

200 (LimitRangeList): OK

401: Unauthorized

list 列出或监视 LimitRange 类别的对象

HTTP 请求

GET /api/v1/limitranges

参数

响应

200 (LimitRangeList): OK

401: Unauthorized

create 创建 LimitRange

HTTP 请求

POST /api/v1/namespaces/{namespace}/limitranges

参数

响应

200 (LimitRange): OK

201 (LimitRange): Created

202 (LimitRange): Accepted

401: Unauthorized

update 替换指定的 LimitRange

HTTP 请求

PUT /api/v1/namespaces/{namespace}/limitranges/{name}

参数

  • name (路径参数): string,必需

    LimitRange 的名称

  • namespace (路径参数): string,必需

    namespace

  • body: LimitRange,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • pretty (查询参数): string

    pretty

响应

200 (LimitRange): OK

201 (LimitRange): Created

401: Unauthorized

patch 部分更新指定的 LimitRange

HTTP 请求

PATCH /api/v1/namespaces/{namespace}/limitranges/{name}

参数

  • name (路径参数): string,必需

    LimitRange 的名称

  • namespace (路径参数): string,必需

    namespace

  • body: Patch,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • force (查询参数): boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (LimitRange): OK

201 (LimitRange): Created

401: Unauthorized

delete 删除 LimitRange

HTTP 请求

DELETE /api/v1/namespaces/{namespace}/limitranges/{name}

参数

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除 LimitRange 的集合

HTTP 请求

DELETE /api/v1/namespaces/{namespace}/limitranges

参数

响应

200 (Status): OK

401: Unauthorized

6.5.6.2 - ResourceQuota

ResourceQuota 设置每个命名空间强制执行的聚合配额限制。

apiVersion: v1

import "k8s.io/api/core/v1"

ResourceQuota

ResourceQuota 设置每个命名空间强制执行的聚合配额限制。


ResourceQuotaSpec

ResourceQuotaSpec 定义为 Quota 强制执行所需的硬限制。


  • hard (map[string]Quantity)

    hard 是每种指定资源所需的硬性限制集合。 更多信息: https://kubernetes.io/docs/concepts/policy/resource-quotas/

  • scopeSelector (ScopeSelector)

    scopeSelector 也是一组过滤器的集合,和 scopes 类似, 必须匹配配额所跟踪的每个对象,但使用 ScopeSelectorOperator 结合可能的值来表示。 对于要匹配的资源,必须同时匹配 scopes 和 scopeSelector(如果在 spec 中设置了的话)。

    scope 选择算符表示的是由限定范围的资源选择算符进行 逻辑与 计算得出的结果。

    • scopeSelector.matchExpressions ([]ScopedResourceSelectorRequirement)

      按资源范围划分的范围选择算符需求列表。

      限定范围的资源选择算符需求是一种选择算符,包含值、范围名称和将二者关联起来的运算符。

      • scopeSelector.matchExpressions.operator (string),必需

        表示范围与一组值之间的关系。有效的运算符为 In、NotIn、Exists、DoesNotExist。

      • scopeSelector.matchExpressions.scopeName (string),必需

        选择器所适用的范围的名称。

      • scopeSelector.matchExpressions.values ([]string)

        字符串值数组。 如果操作符是 In 或 NotIn,values 数组必须是非空的。 如果操作符是 Exists 或 DoesNotExist,values 数组必须为空。 该数组将在策略性合并补丁操作期间被替换。

  • scopes ([]string)

    一个匹配被配额跟踪的所有对象的过滤器集合。 如果没有指定,则默认匹配所有对象。

ResourceQuotaStatus

ResourceQuotaStatus 定义硬性限制和观测到的用量。


ResourceQuotaList

ResourceQuotaList 是 ResourceQuota 列表。


操作


get 读取指定的 ResourceQuota

HTTP 请求

GET /api/v1/namespaces/{namespace}/resourcequotas/{name}

参数

  • name路径参数): string, 必需

    ResourceQuota 的名称

  • namespace路径参数): string, 必需

    namespace

  • pretty查询参数): string

    pretty

响应

200 (ResourceQuota): OK

401: Unauthorized

get 读取指定的 ResourceQuota 的状态

HTTP 请求

GET /api/v1/namespaces/{namespace}/resourcequotas/{name}/status

参数

  • name路径参数): string, 必需

    ResourceQuota 的名称

  • namespace路径参数): string, 必需

    namespace

  • pretty查询参数): string

    pretty

响应

200 (ResourceQuota): OK

401: Unauthorized

list 列出或监视 ResourceQuota 类别的对象

HTTP 请求

GET /api/v1/namespaces/{namespace}/resourcequotas

参数

响应

200 (ResourceQuotaList): OK

401: Unauthorized

list 列出或监视 ResourceQuota 类别的对象

HTTP 请求

GET /api/v1/resourcequotas

参数

响应

200 (ResourceQuotaList): OK

401: Unauthorized

create 创建一个 ResourceQuota

HTTP 请求

POST /api/v1/namespaces/{namespace}/resourcequotas

参数

响应

200 (ResourceQuota): OK

201 (ResourceQuota): Created

202 (ResourceQuota): Accepted

401: Unauthorized

update 更新指定的 ResourceQuota

HTTP 请求

PUT /api/v1/namespaces/{namespace}/resourcequotas/{name}

参数

  • name路径参数): string, 必需

    ResourceQuota 的名称

  • namespace路径参数): string, 必需

    namespace

  • body: ResourceQuota, 必需

  • dryRun查询参数): string

    dryRun

  • fieldManager查询参数): string

    fieldManager

  • fieldValidation查询参数): string

    fieldValidation

  • pretty查询参数): string

    pretty

响应

200 (ResourceQuota): OK

201 (ResourceQuota): Created

401: Unauthorized

update 更新指定 ResourceQuota 的状态

HTTP 请求

PUT /api/v1/namespaces/{namespace}/resourcequotas/{name}/status

参数

  • name路径参数): string, 必需

    ResourceQuota 的名称

  • namespace路径参数): string, 必需

    namespace

  • body: ResourceQuota, 必需

  • dryRun查询参数): string

    dryRun

  • fieldManager查询参数): string

    fieldManager

  • fieldValidation查询参数): string

    fieldValidation

  • pretty查询参数): string

    pretty

响应

200 (ResourceQuota): OK

201 (ResourceQuota): Created

401: Unauthorized

patch 部分更新指定的 ResourceQuota

HTTP 请求

PATCH /api/v1/namespaces/{namespace}/resourcequotas/{name}

参数

  • name路径参数): string, 必需

    ResourceQuota 的名称

  • namespace路径参数): string, 必需

    namespace

  • body: Patch, 必需

  • dryRun查询参数): string

    dryRun

  • fieldManager查询参数): string

    fieldManager

  • fieldValidation查询参数): string

    fieldValidation

  • force查询参数): boolean

    force

  • pretty查询参数): string

    pretty

响应

200 (ResourceQuota): OK

201 (ResourceQuota): Created

401: Unauthorized

patch 部分更新指定 ResourceQuota 的状态

HTTP 请求

PATCH /api/v1/namespaces/{namespace}/resourcequotas/{name}/status

参数

  • name路径参数): string, 必需

    ResourceQuota 的名称

  • namespace路径参数): string, 必需

    namespace

  • body: Patch, 必需

  • dryRun查询参数): string

    dryRun

  • fieldManager查询参数): string

    fieldManager

  • fieldValidation查询参数): string

    fieldValidation

  • force查询参数): boolean

    force

  • pretty查询参数): string

    pretty

响应

200 (ResourceQuota): OK

201 (ResourceQuota): Created

401: Unauthorized

delete 删除 ResourceQuota

HTTP 请求

DELETE /api/v1/namespaces/{namespace}/resourcequotas/{name}

参数

响应

200 (ResourceQuota): OK

202 (ResourceQuota): Accepted

401: Unauthorized

deletecollection 删除 ResourceQuota 的集合

HTTP 请求

DELETE /api/v1/namespaces/{namespace}/resourcequotas

参数

响应

200 (Status): OK

401: Unauthorized

6.5.6.3 - NetworkPolicy

NetworkPolicy 描述针对一组 Pod 所允许的网络流量。

apiVersion: networking.k8s.io/v1

import "k8s.io/api/networking/v1"

NetworkPolicy

NetworkPolicy 描述针对一组 Pod 所允许的网络流量。


NetworkPolicySpec

NetworkPolicySpec 定义特定 NetworkPolicy 所需的所有信息.


  • podSelector (LabelSelector),必需

    podSelector 选择此网络策略所适用的一组 Pod。一组 Ingress 入口策略将应用于此字段选择的所有 Pod。 多个网络策略可以选择同一组 Pod。 在这种情况下,这些列表条目的 Ingress 规则效果会被叠加。此字段不是可选的,并且遵循标准标签选择算符语义。 空值的 podSelector 匹配此命名空间中的所有 Pod。

  • policyTypes ([]string)

    policyTypes 是 NetworkPolicy 相关的规则类型列表。有效选项为 [“Ingress”][“Egress”][“Ingress”, “Egress”]。 如果不指定此字段,则默认值取决是否存在 Ingress 或 Egress 规则;规则里包含 Egress 部分的策略将会影响出站流量, 并且所有策略(无论它们是否包含 Ingress 部分)都将会影响 入站流量。 如果要仅定义出站流量策略,则必须明确指定 [ "Egress" ]。 同样,如果要定义一个指定拒绝所有出站流量的策略,则必须指定一个包含 “Egress” 的 policyTypes 值 (因为这样不包含 Egress 部分的策略,将会被默认为只有 [ "Ingress" ] )。此字段在 1.8 中为 Beta。

  • ingress ([]NetworkPolicyIngressRule)

    ingress 是应用到所选 Pod 的入站规则列表。在没有被任何 NetworkPolicy 选择到 Pod 的情况下(同时假定集群策略允许对应流量), 或者如果流量源是 Pod 的本地节点,或者流量与所有 NetworkPolicy 中的至少一个入站规则(Ingress) 匹配, 则进入 Pod 的流量是被允许的。如果此字段为空,则此 NetworkPolicy 不允许任何入站流量 (这种设置用来确保它所选择的 Pod 在默认情况下是被隔离的)。

    NetworkPolicyIngressRule 定义 NetworkPolicySpec 的 podSelector 所选 Pod 的入站规则的白名单列表, 流量必须同时匹配 ports 和 from。

    • ingress.from ([]NetworkPolicyPeer)

      from 是流量来源列表,列表中的来源可以访问被此规则选中的 Pod。此列表中的流量来源使用逻辑或操作进行组合。 如果此字段为空值或缺失(未设置), 则此规则匹配所有流量来源(也即允许所有入站流量)。如果此字段存在并且至少包含一项来源,则仅当流量与来自列表中的至少一项匹配时, 此规则才允许流量访问被选中的 Pod 集合。

      NetworkPolicyPeer 描述了允许进出流量的对等点。这个参数只允许某些字段组合。

      • ingress.from.ipBlock (IPBlock)

        ipBlock 针对特定 IPBlock 定义策略。如果设置了此字段,则不可以设置其他字段。

        IPBlock 定义一个特定的 CIDR 范围(例如 192.168.1.0/242001:db8::/64), 来自这个 IP 范围的流量来源将会被允许访问与 NetworkPolicySpec 的 podSelector 匹配的 Pod 集合。 except 字段则设置应排除在此规则之外的 CIDR 范围。

        • ingress.from.ipBlock.cidr (string),必需

          cidr 是表示 IP 组块的字符串,例如 "192.168.1.0/24""2001:db8::/64"

        • ingress.from.ipBlock.except ([]string)

          except 是一个由 CIDR 范围组成的列表,其中指定的 CIDR 都应排除在此 IP 区块范围之外。 例如 "192.168.1.0/24""2001:db8::/64"。 如果 except 字段的值超出 ipBlock.cidr 的范围则被视为无效策略。

      • ingress.from.namespaceSelector (LabelSelector)

        namespaceSelector 使用集群范围标签来选择特定的 Namespace。此字段遵循标准标签选择算符语义; 如果此字段存在但为空值,则会选择所有名字空间。

        如果 podSelector 也被定义了, 那么 NetworkPolicyPeer 将选择那些同时满足 namespaceSelector 所选名字空间下和 podSelector 规则匹配的 Pod。 反之选择 namespaceSelector 所选名字空间下所有的 Pod。

      • ingress.from.podSelector (LabelSelector)

        podSelector 是负责选择 Pod 的标签选择算符。该字段遵循标准标签选择算符语义;如果字段存在但为空值,则选择所有 Pod。

        如果 namespaceSelector 也被定义,那么 NetworkPolicyPeer 将选择那些同时满足 namespaceSelector 定义的名字空间下和 podSelector 规则匹配的 Pod。 反之会在策略所在的名字空间中选择与 podSelector 匹配的 Pod。

    • ingress.ports ([]NetworkPolicyPort)

      ports 是在此规则选中的 Pod 上应可访问的端口列表。此列表中的个项目使用逻辑或操作组合。如果此字段为空或缺失, 则此规则匹配所有端口(进入流量可访问任何端口)。 如果此字段存在并且包含至少一个有效值,则此规则仅在流量至少匹配列表中的一个端口时才允许访问。

      NetworkPolicyPort 定义可以连接的端口

      • ingress.ports.port (IntOrString)

        port 表示符合给定协议的端口。字段值可以是 Pod 上的数字或命名端口。如果未提供此字段,则匹配所有端口名和端口号。 如果定义了,则仅允许对给定的协议和端口的入口流量。

        IntOrString 是一种可以包含 int32 或字符串值的类型。在 JSON 或 YAML 编组和解组中使用时,它会生成或使用内部类型。 例如,这允许你拥有一个可以接受名称或数字的 JSON 字段。

      • ingress.ports.endPort (int32)

        endPort 表示如果设置了此字段,则策略应该允许 port 与 endPort 之间(包含二者)的所有端口。 如果未定义 port 或将 port 字段值为命名端口(字符串),则不可以使用 endPort。 endPort 必须等于或大于 port 值。

      • ingress.ports.protocol (string)

        protocol 表示流量必须匹配的网络协议(TCP、UDP 或 SCTP)。如果未指定,此字段默认为 TCP。

  • egress ([]NetworkPolicyEgressRule)

    egress 是应用到所选 Pod 的出站规则的列表。如果没有 NetworkPolicy 选中指定 Pod(并且其他集群策略也允许出口流量), 或者在所有通过 podSelector 选中了某 Pod 的 NetworkPolicy 中,至少有一条出站规则与出站流量匹配, 则该 Pod 的出站流量是被允许的。 如果此字段为空,则此 NetworkPolicy 拒绝所有出站流量(这策略可以确保它所选中的 Pod 在默认情况下是被隔离的)。 egress 字段在 1.8 中为 Beta 级别。

    NetworkPolicyEgressRule 针对被 NetworkPolicySpec 的 podSelector 所选中 Pod,描述其被允许的出站流量。 流量必须同时匹配 ports 和 to 设置。此类型在 1.8 中为 Beta 级别。

    • egress.to ([]NetworkPolicyPeer)

      to 是针对此规则所选择的 Pod 的出口流量的目的地列表。此列表中的目的地使用逻辑或操作进行组合。如果此字段为空或缺失, 则此规则匹配所有目的地(流量不受目的地限制)。如果此字段存在且至少包含一项目的地,则仅当流量与目标列表中的至少一个匹配时, 此规则才允许出口流量。

      NetworkPolicyPeer 描述允许进出流量的对等点。这个对象只允许某些字段组合。

      • egress.to.ipBlock (IPBlock)

        ipBlock 针对特定的 IP 区块定义策略。如果设置了此字段,则其他不可以设置其他字段。

        IPBlock 描述一个特定的 CIDR 范围(例如 192.168.1.0/242001:db8::/64), 与 NetworkPolicySpec 的 podSelector 匹配的 Pod 将被允许连接到这个 IP 范围,作为其出口流量目的地。 except 字段则设置了不被此规则影响的 CIDR 范围。

        • egress.to.ipBlock.cidr (string),必需

          cidr 是用来表达 IP 组块的字符串,例如 "192.168.1.0/24""2001:db8::/64"

        • egress.to.ipBlock.except ([]string)

          except 定义不应包含在 ipBlock 内的 CIDR 范围列表。例如 "192.168.1.0/24""2001:db8::/64"。 如果 except 的值超出 ipBlock.cidr 的范围则被拒绝。

      • egress.to.namespaceSelector (LabelSelector)

        namespaceSelector 使用集群范围标签来选择特定的名字空间。该字段遵循标准标签选择算符语义; 如果字段存在但为空值,那会选择所有名字空间。

        如果 podSelector 也被定义了, 那么 NetworkPolicyPeer 将选择那些同时满足 namespaceSelector 指定的名字空间下和 podSelector 规则匹配的 Pod。 反之选择 namespaceSelector 指定的名字空间下所有的 Pod。

      • egress.to.podSelector (LabelSelector)

        podSelector 是负责选择一组 Pod 的标签选择算符。该字段遵循标准标签选择算符语义; 如果字段存在但为空值,则选择所有 Pod。

        如果 egress.to.namespaceSelector 也被定义,则 NetworkPolicyPeer 将选择 namespaceSelector 所指定的名字空间下和 podSelector 规则匹配的 Pod。 反之会在策略所属的名字空间中选择与 podSelector 匹配的 Pod。

    • egress.ports ([]NetworkPolicyPort)

      ports 是出站流量目的地的端口列表。此列表中的各个项目使用逻辑或操作进行组合。如果此字段为空或缺失, 则此规则匹配所有端口(可访问出口流量目的地的任何端口)。如果此字段存在并且包含至少一个有效值, 则此规则仅在流量与列表中的至少一个端口匹配时才允许访问。

      NetworkPolicyPort 定义出口流量目的地的端口。

      • egress.ports.port (IntOrString)

        port 表示符合给定协议的端口。字段值可以是 Pod 上的数字或命名端口。如果未提供此字段,则匹配所有端口名和端口号。 如果定义此字段,则仅允许针对给定的协议和端口的出站流量。

        IntOrString 是一种可以包含 int32 或字符串值的类型。在 JSON 或 YAML 编组和解组中使用时,它会生成或使用内部类型。 例如,这允许你拥有一个可以接受名称或数字的 JSON 字段。

      - **egress.ports.endPort** (int32)
      

      如果设置了 endPort,则用来指定策略所允许的从 port 到 endPort 的端口范围(包含边界值)。 如果未设置 port 或 port 字段值为端口名称(字符串),则不可以指定 endPort。 endPort 必须等于或大于 port 值。

      • egress.ports.protocol (string)

        protocol 表示流量必须匹配的网络协议(TCP、UDP 或 SCTP)。如果未指定,此字段默认为 TCP。

NetworkPolicyList

NetworkPolicyList 是 NetworkPolicy 的集合。


  • apiVersion: networking.k8s.io/v1

  • kind: NetworkPolicyList

  • items ([]NetworkPolicy),必需

    items 是 NetworkPolicy 的列表。

操作


get 读取指定的 NetworkPolicy

HTTP 请求

GET /apis/networking.k8s.io/v1/namespaces/{namespace}/networkpolicies/{name}

参数

  • name (路径参数): string,必需

    NetworkPolicy 的名称。

  • namespace (路径参数): string,必需

    namespace

  • pretty (查询参数): string

    pretty

响应

200 (NetworkPolicy): OK

401: Unauthorized

list 列出或监视 NetworkPolicy 类型的对象

HTTP 请求

GET /apis/networking.k8s.io/v1/namespaces/{namespace}/networkpolicies

参数

  • namespace (路径参数): string,必需

    namespace

  • limit (查询参数): integer

    limit

  • pretty (查询参数): string

    pretty

  • watch (查询参数): boolean

    watch

响应

200 (NetworkPolicyList): OK

401: Unauthorized

list 列出或监视 NetworkPolicy 类

HTTP Request

GET /apis/networking.k8s.io/v1/networkpolicies

参数

  • limit (查询参数): integer

    limit

  • pretty (查询参数): string

    pretty

  • watch (查询参数): boolean

    watch

响应

200 (NetworkPolicyList): OK

401: Unauthorized

create 创建 NetworkPolicy

HTTP 请求

POST /apis/networking.k8s.io/v1/namespaces/{namespace}/networkpolicies

参数

  • namespace (路径参数): string,必需

  • body: NetworkPolicy,必需

响应

200 (NetworkPolicy): OK

201 (NetworkPolicy): Created

202 (NetworkPolicy): Accepted

401: Unauthorized

update 替换指定的 NetworkPolicy

HTTP 请求

PUT /apis/networking.k8s.io/v1/namespaces/{namespace}/networkpolicies/{name}

参数

  • name (路径参数): string,必需

    NetworkPolicy 的名称。

响应

200 (NetworkPolicy): OK

201 (NetworkPolicy): Created

401: Unauthorized

patch 部分更新指定的 NetworkPolicy

HTTP 请求

PATCH /apis/networking.k8s.io/v1/namespaces/{namespace}/networkpolicies/{name}

参数

  • name (路径参数): string,必需

    NetworkPolicy 的名称。

  • namespace (路径参数): string,必需

    namespace

  • body: Patch,必需

响应

200 (NetworkPolicy): OK

201 (NetworkPolicy): Created

401: Unauthorized

delete 删除 NetworkPolicy

HTTP 请求

DELETE /apis/networking.k8s.io/v1/namespaces/{namespace}/networkpolicies/{name}

参数

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除 NetworkPolicy 的集合

HTTP 请求

DELETE /apis/networking.k8s.io/v1/namespaces/{namespace}/networkpolicies

参数

响应

200 (Status): OK

401: Unauthorized

6.5.6.4 - PodDisruptionBudget

PodDisruptionBudget 是一个对象,用于定义可能对一组 Pod 造成的最大干扰。

apiVersion: policy/v1

import "k8s.io/api/policy/v1"

PodDisruptionBudget

PodDisruptionBudget 是一个对象,用于定义可能对一组 Pod 造成的最大干扰。


  • apiVersion: policy/v1

  • kind: PodDisruptionBudget

PodDisruptionBudgetSpec

PodDisruptionBudgetSpec 是对 PodDisruptionBudget 的描述。


  • maxUnavailable (IntOrString)

    如果 “selector” 所选中的 Pod 中最多有 “maxUnavailable” Pod 在驱逐后不可用(即去掉被驱逐的 Pod 之后),则允许驱逐。 例如,可以通过将此字段设置为 0 来阻止所有自愿驱逐。此字段是与 “minAvailable” 互斥的设置。

    IntOrString 是一种可以包含 int32 或字符串数值的类型。在 JSON 或 YAML 编组和解组时, 会生成或使用内部类型。例如,此类型允许你定义一个可以接受名称或数字的 JSON 字段。

  • minAvailable (IntOrString)

    如果 “selector” 所选中的 Pod 中,至少 “minAvailable” 个 Pod 在驱逐后仍然可用(即去掉被驱逐的 Pod 之后),则允许驱逐。 因此,你可以通过将此字段设置为 “100%” 来禁止所有自愿驱逐。

    IntOrString 是一种可以包含 int32 或字符串数值的类型。在 JSON 或 YAML 编组和解组时, 会生成或使用内部类型。例如,此类型允许你定义一个可以接受名称或数字的 JSON 字段。

  • selector (LabelSelector)

    标签查询,用来选择其驱逐由干扰预算来管理的 Pod 集合。 选择算符为 null 时将不会匹配任何 Pod,而空 ({}) 选择算符将选中名字空间内的所有 Pod。

  • unhealthyPodEvictionPolicy (string)

    unhealthyPodEvictionPolicy 定义不健康的 Pod 应被考虑驱逐时的标准。 当前的实现将健康的 Pod 视为具有 status.conditions 项且 type="Ready"、status="True" 的 Pod。

    有效的策略是 IfHealthyBudget 和 AlwaysAllow。 如果没有策略被指定,则使用与 IfHealthyBudget 策略对应的默认行为。

    IfHealthyBudget 策略意味着正在运行(status.phase="Running")但还不健康的 Pod 只有在被守护的应用未受干扰(status.currentHealthy 至少等于 status.desiredHealthy) 时才能被驱逐。健康的 Pod 将受到 PDB 的驱逐。

    AlwaysAllow 策略意味着无论是否满足 PDB 中的条件,所有正在运行(status.phase="Running")但还不健康的 Pod 都被视为受干扰且可以被驱逐。这意味着受干扰应用的透视运行 Pod 可能没有机会变得健康。 健康的 Pod 将受到 PDB 的驱逐。

    将来可能会添加其他策略。如果客户端在该字段遇到未识别的策略,则做出驱逐决定的客户端应禁止驱逐不健康的 Pod。

    该字段是 Alpha 级别的。当特性门控 PDBUnhealthyPodEvictionPolicy 被启用(默认禁用)时,驱逐 API 使用此字段。

PodDisruptionBudgetStatus

PodDisruptionBudgetStatus 表示有关此 PodDisruptionBudget 状态的信息。状态可能会反映系统的实际状态。


  • currentHealthy (int32), 必需

    当前健康 Pod 的数量。

  • desiredHealthy (int32), 必需

    健康 Pod 的最小期望值。

  • disruptionsAllowed (int32), 必需

    当前允许的 Pod 干扰计数。

  • expectedPods (int32), 必需

    此干扰预算计入的 Pod 总数

  • conditions ([]Condition)

    补丁策略:根据 type 键执行合并操作

    Map:键 type 的唯一值将在合并期间被保留

    conditions 包含 PDB 的状况。干扰控制器会设置 DisruptionAllowed 状况。 以下是 reason 字段的已知值(将来可能会添加其他原因):

    • SyncFailed:控制器遇到错误并且无法计算允许的干扰计数。因此不允许任何干扰,且状况的状态将变为 False。
    • InsufficientPods:Pod 的数量只能小于或等于 PodDisruptionBudget 要求的数量。 不允许任何干扰,且状况的状态将是 False。
    • SufficientPods:Pod 个数超出 PodDisruptionBudget 所要求的阈值。 此状况为 True 时,基于 disruptsAllowed 属性确定所允许的干扰数目。

    Condition 包含此 API 资源当前状态的一个方面的详细信息。

    • conditions.lastTransitionTime (Time), 必需

      lastTransitionTime 是状况最近一次从一种状态转换到另一种状态的时间。 这种变化通常出现在下层状况发生变化的时候。如果无法了解下层状况变化,使用 API 字段更改的时间也是可以接受的。

      Time 是 time.Time 的包装器,它支持对 YAML 和 JSON 的正确编组。 time 包的许多工厂方法提供了包装器。

    • conditions.message (string), 必需

      message 是一条人类可读的消息,指示有关转换的详细信息。它可能是一个空字符串。

    • conditions.reason (string), 必需

      reason 包含一个程序标识符,指示状况最后一次转换的原因。 特定状况类型的生产者可以定义该字段的预期值和含义,以及这些值是否可被视为有保证的 API。 该值应该是 CamelCase 字符串。此字段不能为空。

    • conditions.status (string), 必需

      状况的状态为 True、False、Unknown 之一。

    • conditions.type (string), 必需

      CamelCase 或 foo.example.com/CamelCase 形式的状况类型。

    • conditions.observedGeneration (int64)

      observedGeneration 表示设置状况时所基于的 .metadata.generation。 例如,如果 .metadata.generation 当前为 12,但 .status.conditions[x].observedGeneration 为 9, 则状况相对于实例的当前状态已过期。

  • disruptedPods (map[string]Time)

    disruptedPods 包含有关 Pod 的一些信息,这些 Pod 的驱逐操作已由 API 服务器上的 eviction 子资源处理程序处理, 但尚未被 PodDisruptionBudget 控制器观察到。 从 API 服务器处理驱逐请求到 PDB 控制器看到该 Pod 已标记为删除(或超时后),Pod 将记录在此映射中。 映射中的键名是 Pod 的名称,键值是 API 服务器处理驱逐请求的时间。 如果删除没有发生并且 Pod 仍然存在,PodDisruptionBudget 控制器将在一段时间后自动将 Pod 从列表中删除。 如果一切顺利,此映射大部分时间应该是空的。映射中的存在大量条目可能表明 Pod 删除存在问题。

    Time 是 time.Time 的包装器,它支持对 YAML 和 JSON 的正确编组。 time 包的许多工厂方法提供了包装器。

  • observedGeneration (int64)

    更新此 PDB 状态时观察到的最新一代。 DisruptionsAllowed 和其他状态信息仅在 observedGeneration 等于 PDB 的对象的代数时才有效。

PodDisruptionBudgetList

PodDisruptionBudgetList 是 PodDisruptionBudget 的集合。


  • apiVersion: policy/v1

  • kind: PodDisruptionBudgetList

操作


get 读取指定的 PodDisruptionBudget

HTTP 请求

GET /apis/policy/v1/namespaces/{namespace}/poddisruptionbudgets/{name}

参数

  • name (路径参数): string, 必需

    PodDisruptionBudget 的名称。

  • namespace (路径参数): string, 必需

    namespace

  • pretty (查询参数): string

    pretty

响应

200 (PodDisruptionBudget): OK

401: Unauthorized

get 读取指定 PodDisruptionBudget 的状态

HTTP 请求

GET /apis/policy/v1/namespaces/{namespace}/poddisruptionbudgets/{name}/status

参数

  • name (路径参数): string, 必需

    PodDisruptionBudget 的名称。

  • namespace (路径参数): string, 必需

    namespace

  • pretty (查询参数): string

    pretty

响应

200 (PodDisruptionBudget): OK

401: Unauthorized

list 列出或监视 PodDisruptionBudget 类型的对象

HTTP 请求

GET /apis/policy/v1/namespaces/{namespace}/poddisruptionbudgets

参数

  • namespace (路径参数): string, 必需

    namespace

  • limit (查询参数): integer

    limit

  • pretty (查询参数): string

    pretty

  • watch (查询参数): boolean

    watch

响应

200 (PodDisruptionBudgetList): OK

401: Unauthorized

list 列出或监视 PodDisruptionBudget 类型的对象

HTTP 请求

GET /apis/policy/v1/poddisruptionbudgets

参数

  • limit (查询参数): integer

    limit

  • pretty (查询参数): string

    pretty

  • watch (查询参数): boolean

    watch

响应

200 (PodDisruptionBudgetList): OK

401: Unauthorized

create 创建一个 PodDisruptionBudget

HTTP 请求

POST /apis/policy/v1/namespaces/{namespace}/poddisruptionbudgets

参数

  • namespace (路径参数): string, 必需

    namespace

  • dryRun (查询参数): string

    dryRun

  • pretty (查询参数): string

    pretty

响应

200 (PodDisruptionBudget): OK

201 (PodDisruptionBudget): Created

202 (PodDisruptionBudget): Accepted

401: Unauthorized

update 替换指定的 PodDisruptionBudget

HTTP 请求

PUT /apis/policy/v1/namespaces/{namespace}/poddisruptionbudgets/{name}

参数

  • name (路径参数): string, 必需

    PodDisruptionBudget 的名称。

  • namespace (路径参数): string, 必需

    namespace

  • dryRun (查询参数): string

    dryRun

响应

200 (PodDisruptionBudget): OK

201 (PodDisruptionBudget): Created

401: Unauthorized

update 替换指定 PodDisruptionBudget 的状态

HTTP 请求

PUT /apis/policy/v1/namespaces/{namespace}/poddisruptionbudgets/{name}/status

参数

  • name (路径参数): string, 必需

    PodDisruptionBudget 的名称。

  • namespace (路径参数): string, 必需

    namespace

响应

200 (PodDisruptionBudget): OK

201 (PodDisruptionBudget): Created

401: Unauthorized

patch 部分更新指定的 PodDisruptionBudget

HTTP 请求

PATCH /apis/policy/v1/namespaces/{namespace}/poddisruptionbudgets/{name}

参数

  • name (路径参数): string, 必需

    PodDisruptionBudget 的名称

  • namespace (路径参数): string, 必需

    namespace

  • dryRun (查询参数): string

    dryRun

  • force (查询参数): boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (PodDisruptionBudget): OK

201 (PodDisruptionBudget): Created

401: Unauthorized

patch 部分更新指定 PodDisruptionBudget 的状态

HTTP 请求

PATCH /apis/policy/v1/namespaces/{namespace}/poddisruptionbudgets/{name}/status

参数

  • name (路径参数): string, 必需

    PodDisruptionBudget 的名称。

  • namespace (路径参数): string, 必需

    namespace

  • dryRun (查询参数): string

    dryRun

  • force (查询参数): boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (PodDisruptionBudget): OK

201 (PodDisruptionBudget): Created

401: Unauthorized

delete 删除 PodDisruptionBudget

HTTP 请求

DELETE /apis/policy/v1/namespaces/{namespace}/poddisruptionbudgets/{name}

参数

  • name (路径参数): string, 必需

    PodDisruptionBudget 的名称。

  • dryRun (查询参数): string

    dryRun

  • pretty (查询参数): string

    pretty

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除 PodDisruptionBudget 的集合

HTTP Request

DELETE /apis/policy/v1/namespaces/{namespace}/poddisruptionbudgets

参数

  • dryRun (查询参数): string

    dryRun

  • limit (查询参数): integer

    limit

  • pretty (查询参数): string

    pretty

响应

200 (Status): OK

401: Unauthorized

6.5.6.5 - IPAddress v1alpha1

IPAddress 表示单个 IP 族的单个 IP。

apiVersion: networking.k8s.io/v1alpha1

import "k8s.io/api/networking/v1alpha1"

IPAddress

IPAddress 表示单个 IP 族的单个 IP。此对象旨在供操作 IP 地址的 API 使用。 此对象由 Service 核心 API 用于分配 IP 地址。 IP 地址可以用不同的格式表示,为了保证 IP 地址的唯一性,此对象的名称是格式规范的 IP 地址。 IPv4 地址由点分隔的四个十进制数字组成,前导零可省略;IPv6 地址按照 RFC 5952 的定义来表示。 有效值:192.168.1.5、2001:db8::1 或 2001:db8:aaaa:bbbb:cccc:dddd:eeee:1。 无效值:10.01.2.3 或 2001:db8:0:0:0::1。


  • apiVersion: networking.k8s.io/v1alpha1

  • kind: IPAddress

IPAddressSpec

IPAddressSpec 描述 IP 地址中的属性。


  • parentRef (ParentReference)

    parentRef 引用挂接 IPAddress 的资源。IPAddress 必须引用一个父对象。

    ParentReference 描述指向父对象的引用。

    • parentRef.group (string)

      group 是被引用的对象的组。

    • parentRef.name (string)

      name 是被引用的对象的名称。

    • parentRef.namespace (string)

      namespace 是被引用的对象的名字空间。

    • parentRef.resource (string)

      resource 是被引用的对象的资源。

    • parentRef.uid (string)

      uid 是被引用的对象的唯一标识符(uid)。

IPAddressList

IPAddressList 包含 IPAddress 的列表。


  • apiVersion: networking.k8s.io/v1alpha1

  • kind: IPAddressList

操作


get 读取指定的 IPAddress

HTTP 请求

GET /apis/networking.k8s.io/v1alpha1/ipaddresses/{name}

参数

  • name路径参数):string,必需

    IPAddress 的名称。

  • pretty查询参数):string

    pretty

响应

200 (IPAddress): OK

401: Unauthorized

list 列举或监视类别为 IPAddress 的对象

HTTP 请求

GET /apis/networking.k8s.io/v1alpha1/ipaddresses

参数

响应

200 (IPAddressList): OK

401: Unauthorized

create 创建 IPAddress

HTTP 请求

POST /apis/networking.k8s.io/v1alpha1/ipaddresses

参数

响应

200 (IPAddress): OK

201 (IPAddress): Created

202 (IPAddress): Accepted

401: Unauthorized

update 替换指定的 IPAddress

HTTP 请求

PUT /apis/networking.k8s.io/v1alpha1/ipaddresses/{name}

参数

  • name路径参数):string,必需

    IPAddress 的名称。

  • body: IPAddress,必需

  • dryRun查询参数):string

    dryRun

  • fieldManager查询参数):string

    fieldManager

  • fieldValidation查询参数):string

    fieldValidation

  • pretty查询参数):string

    pretty

响应

200 (IPAddress): OK

201 (IPAddress): Created

401: Unauthorized

patch 部分更新指定的 IPAddress

HTTP 请求

PATCH /apis/networking.k8s.io/v1alpha1/ipaddresses/{name}

参数

  • name路径参数):string,必需

    IPAddress 的名称。

  • body: Patch,必需

  • dryRun查询参数):string

    dryRun

  • fieldManager查询参数):string

    fieldManager

  • fieldValidation查询参数):string

    fieldValidation

  • force查询参数):boolean

    force

  • pretty查询参数):string

    pretty

响应

200 (IPAddress): OK

201 (IPAddress): Created

401: Unauthorized

delete 删除 IPAddress

HTTP 请求

DELETE /apis/networking.k8s.io/v1alpha1/ipaddresses/{name}

参数

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除 IPAddress 的集合

HTTP 请求

DELETE /apis/networking.k8s.io/v1alpha1/ipaddresses

参数

响应

200 (Status): OK

401: Unauthorized

6.5.7 - 扩展资源

6.5.7.1 - CustomResourceDefinition

CustomResourceDefinition 表示应在 API 服务器上公开的资源。

apiVersion: apiextensions.k8s.io/v1

import "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"

CustomResourceDefinition

CustomResourceDefinition 表示应在 API 服务器上公开的资源。其名称必须采用 <.spec.name>.<.spec.group> 格式。


CustomResourceDefinitionSpec

CustomResourceDefinitionSpec 描述了用户希望资源的呈现方式。


  • group (string),必需

    group 是自定义资源的 API 组。自定义资源在 /apis/<group>/... 下提供。 必须与 CustomResourceDefinition 的名称匹配(格式为 <names.plural>.<group>)。

  • names (CustomResourceDefinitionNames),必需

    names 表示自定义资源的资源和种类名称。

    CustomResourceDefinitionNames 表示提供此 CustomResourceDefinition 资源的名称。

    • names.kind (string),必需

      kind 是资源的序列化类型。它通常是驼峰命名的单数形式。自定义资源实例将使用此值作为 API 调用中的 kind 属性。

    • names.plural (string),必需

      plural 是所提供的资源的复数名称,自定义资源在 /apis/<group>/<version>/.../<plural> 下提供。 必须与 CustomResourceDefinition 的名称匹配(格式为 <names.plural>.<group>)。必须全部小写。

    • names.categories ([]string)

      categories 是自定义资源所属的分组资源列表(例如 'all')。 它在 API 发现文档中发布,并支持客户端像 kubectl get all 这样的调用。

    • names.listKind (string)

      listKind 是此资源列表的序列化类型。默认为 "kindList"。

    • names.shortNames ([]string)

      shortNames 是资源的短名称,在 API 发现文档中公开,并支持客户端调用,如 kubectl get <shortname>。必须全部小写。

    • names.singular (string)

      singular 是资源的单数名称。必须全部小写。默认为小写 kind

  • scope (string),必需

    scope 表示自定义资源是集群作用域还是命名空间作用域。允许的值为 ClusterNamespaced

  • versions ([]CustomResourceDefinitionVersion),必需

    versions 是自定义资源的所有 API 版本的列表。版本名称用于计算服务版本在 API 发现中列出的顺序。 如果版本字符串与 Kubernetes 的版本号形式类似,则它将被排序在非 Kubernetes 形式版本字符串之前。 Kubernetes 的版本号字符串按字典顺序排列。Kubernetes 版本号以 “v” 字符开头, 后面是一个数字(主版本),然后是可选字符串 “alpha” 或 “beta” 和另一个数字(次要版本)。 它们首先按 GA > beta > alpha 排序(其中 GA 是没有 beta 或 alpha 等后缀的版本),然后比较主要版本, 最后是比较次要版本。版本排序列表示例:v10、v2、v1、v11beta2、v10beta3、v3beta1、v12alpha1、v11alpha2、foo1、foo10。

    CustomResourceDefinitionVersion 描述 CRD 的一个版本。

    • versions.name (string),必需

      name 是版本名称,例如 “v1”、“v2beta1” 等。如果 served 是 true,自定义资源在 /apis/<group>/<version>/... 版本下提供。

    • versions.served (boolean),必需

      served 是用于启用/禁用该版本通过 REST API 提供服务的标志。

    • versions.storage (boolean),必需

      storage 表示在将自定义资源持久保存到存储时,应使用此版本。有且仅有一个版本的 storage=true。

    • versions.additionalPrinterColumns ([]CustomResourceColumnDefinition)

      additionalPrinterColumns 表示在表输出中返回的附加列。 有关详细信息,请参阅 https://kubernetes.io/zh-cn/docs/reference/using-api/api-concepts/#receiving-resources-as-tables。 如果没有指定列,则显示自定义资源存活时间(AGE)列。

      CustomResourceColumnDefinition 指定用于服务器端打印的列。

      • versions.additionalPrinterColumns.jsonPath (string),必需

        jsonPath 是一个简单的 JSON 路径(使用数组表示法),它对每个自定义资源进行评估,以生成该列的值。

      • versions.additionalPrinterColumns.name (string),必需

        name 是便于阅读的列名称。

      • versions.additionalPrinterColumns.type (string),必需

        type 是此列的 OpenAPI 类型定义。有关详细信息, 请参阅 https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types

      • versions.additionalPrinterColumns.description (string)

        description 是该列的可读性描述。

      • versions.additionalPrinterColumns.format (string)

        format 是这个列的可选 OpenAPI 类型定义。'name' 格式应用于主标识符列,以帮助客户端识别列是资源名称。 有关详细信息,请参阅 https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types

      • versions.additionalPrinterColumns.priority (int32)

        priority 是一个定义此列相对于其他列的相对重要性的整数。数字越低,优先级越高。 在空间有限的情况下,可以省略的列的优先级应大于 0。

    • versions.deprecated (boolean)

      deprecated 表示此版本的自定义资源 API 已弃用。设置为 true 时,对此版本的 API 请求会在服务器响应头信息中带有警告(warning)信息。此值默认为 false。

    • versions.deprecationWarning (string)

      deprecationWarning 会覆盖返回给 API 客户端的默认警告。只能在 deprecated 为 true 时设置。 默认警告表示此版本已弃用,建议使用最新的同等或更高稳定性版本(如果存在)。

    • versions.schema (CustomResourceValidation)

      schema 描述了用于验证、精简和默认此版本的自定义资源的模式。

      CustomResourceValidation 是 CustomResources 的验证方法列表。

      • versions.schema.openAPIV3Schema (JSONSchemaProps)

        openAPIV3Schema 是用于验证和精简的 OpenAPI v3 模式。

    • versions.subresources (CustomResourceSubresources)

      subresources 指定此版本已定义的自定义资源具有哪些子资源。

      CustomResourceSubresources 定义了 CustomResources 子资源的状态和规模。

      • versions.subresources.scale (CustomResourceSubresourceScale)

        scale 表示自定义资源应该提供一个 /scale 子资源,该子资源返回一个 autoscaling/v1 Scale 对象。

        CustomResourceSubresourceScale 定义了如何为 CustomResources 的 scale 子资源提供服务。

        • versions.subresources.scale.specReplicasPath (string),必需

          specReplicasPath 定义对应于 Scale 的自定义资源内的 JSON 路径 spec.replicas。 只允许没有数组表示法的 JSON 路径。必须是 .spec 下的 JSON 路径。 如果自定义资源中的给定路径下没有值,那么 GET /scale 子资源将返回错误。

        • versions.subresources.scale.statusReplicasPath (string),必需

          statusReplicasPath 定义对应于 Scale 的自定义资源内的 JSON 路径 status.replicas。 只允许不带数组表示法的 JSON 路径。必须是 .status 下的 JSON 路径。 如果自定义资源中给定路径下没有值,则 /scale 子资源中的 status.replicas 值将默认为 0。

        • versions.subresources.scale.labelSelectorPath (string)

          labelSelectorPath 定义对应于 Scale 的自定义资源内的 JSON 路径 status.selector。 只允许不带数组表示法的 JSON 路径。必须是 .status.spec 下的路径。 必须设置为与 HorizontalPodAutoscaler 一起使用。 此 JSON 路径指向的字段必须是字符串字段(不是复杂的选择器结构),其中包含字符串形式的序列化标签选择器。 更多信息: https://kubernetes.io/zh-cn/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions#scale-subresource。 如果自定义资源中给定路径下没有值,则 /scale 子资源中的 status.selector 默认值为空字符串。

      • versions.subresources.status (CustomResourceSubresourceStatus)

        status 表示自定义资源应该为 /status 子资源服务。当启用时:

        1. 对自定义资源主端点的请求会忽略对对象 status 节的改变;
        2. 对自定义资源 /status 子资源的请求忽略对对象 status 节以外的任何变化。

        CustomResourceSubresourceStatus 定义了如何为自定义资源提供 status 子资源。 状态由 CustomResource 中的 .status JSON 路径表示。设置后,

        • 为自定义资源提供一个 /status 子资源。
        • /status 子资源发出的 PUT 请求时,需要提供自定义资源对象,服务器端会忽略对 status 节以外的任何内容更改。
        • 对自定义资源的 PUT/POST/PATCH 请求会忽略对 status 节的更改。
  • conversion (CustomResourceConversion)

    conversion 定义了 CRD 的转换设置。

    CustomResourceConversion 描述了如何转换不同版本的自定义资源。

    • conversion.strategy (string),必需

      strategy 指定如何在版本之间转换自定义资源。允许的值为:

      • "None":转换器仅更改 apiVersion 并且不会触及自定义资源中的任何其他字段。
      • "Webhook":API 服务器将调用外部 Webhook 进行转换。此选项需要其他信息。这要求 spec.preserveUnknownFields 为 false,并且设置 spec.conversion.webhook。
    • conversion.webhook (WebhookConversion)

      webhook 描述了如何调用转换 Webhook。当 strategy 设置为 "Webhook" 时有效。

      WebhookConversion 描述了如何调用转换 Webhook

      • conversion.webhook.conversionReviewVersions ([]string),必需

        conversionReviewVersions 是 Webhook 期望的 ConversionReview 版本的有序列表。 API 服务器将使用它支持的列表中的第一个版本。如果 API 服务器不支持此列表中指定的版本,则自定义资源的转换将失败。 如果持久化的 Webhook 配置指定了允许的版本但其中不包括 API 服务器所了解的任何版本,则对 Webhook 的调用将失败。

      • conversion.webhook.clientConfig (WebhookClientConfig)

        如果 strategy 是 Webhook,那么 clientConfig 是关于如何调用 Webhook 的说明。

        WebhookClientConfig 包含与 Webhook 建立 TLS 连接的信息。

        • conversion.webhook.clientConfig.caBundle ([]byte)

          caBundle 是一个 PEM 编码的 CA 包,用于验证 Webhook 服务器的服务证书。 如果未指定,则使用 API 服务器上的系统根证书。

        • conversion.webhook.clientConfig.service (ServiceReference)

          service 是对此 Webhook 服务的引用。必须指定 service 或 url 字段之一。

          如果在集群中运行 Webhook,那么你应该使用 service

          ServiceReference 保存对 Service.legacy.k8s.io 的一个引用。

          • conversion.webhook.clientConfig.service.name (string),必需

            name 是服务的名称。必需。

          • conversion.webhook.clientConfig.service.namespace (string),必需

            namespace 是服务的命名空间。必需。

          • conversion.webhook.clientConfig.service.path (string)

            path 是一个可选的 URL 路径,Webhook 将通过该路径联系服务。

          • conversion.webhook.clientConfig.service.port (int32)

            port 是 Webhook 联系的可选服务端口。port 应该是一个有效的端口号(1-65535,包含)。 为实现向后兼容,默认端口号为 443。

        • conversion.webhook.clientConfig.url (string)

          url 以标准 URL 的形式(scheme://host:port/path)给出 Webhook 的位置。urlservice 必须指定一个且只能指定一个。

          host 不应引用集群中运行的服务;若使用集群内服务应改为使用 service 字段。 host 值可能会通过外部 DNS 解析(例如,kube-apiserver 无法解析集群内 DNS,因为这将违反分层规则)。 host 也可能是 IP 地址。

          请注意,使用 localhost127.0.0.1 作为 host 是有风险的, 除非你非常小心地在所有运行 API 服务器的主机上运行这个 Webhook,因为这些 API 服务器可能需要调用这个 Webhook。 这样的安装可能是不可移植的,也就是说,不容易在一个新的集群中复现。

          scheme 必须是 "https";URL 必须以 "https://" 开头。

          路径(path)是可选的,如果存在,则可以是 URL 中允许的任何字符串。 你可以使用路径传递一个任意字符串给 Webhook,例如,一个集群标识符。

          不允许使用用户或基本认证,例如 "user:password@",是不允许的。片段("#...")和查询参数("?...")也是不允许的。

  • preserveUnknownFields (boolean)

    preserveUnknownFields 表示将对象写入持久性存储时应保留 OpenAPI 模式中未规定的对象字段。 apiVersion、kind、元数据(metadata)和元数据中的已知字段始终保留。不推荐使用此字段,而建议在 spec.versions[*].schema.openAPIV3Schema 中设置 x-preserve-unknown-fields 为 true。 更多详细信息参见: https://kubernetes.io/zh-cn/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#field-pruning

JSONSchemaProps

JSONSchemaProps 是JSON 模式(JSON-Schema),遵循其规范草案第 4 版(http://json-schema.org/)。


  • $ref (string)

  • $schema (string)

  • additionalItems (JSONSchemaPropsOrBool)

    JSONSchemaPropsOrBool 表示 JSONSchemaProps 或布尔值。布尔属性默认为 true。

  • additionalProperties (JSONSchemaPropsOrBool)

    JSONSchemaPropsOrBool 表示 JSONSchemaProps 或布尔值。布尔属性默认为 true。

  • allOf ([]JSONSchemaProps)

  • anyOf ([]JSONSchemaProps)

  • default (JSON)

    default 是未定义对象字段的默认值。设置默认值操作是 CustomResourceDefaulting 特性门控所控制的一个 Beta 特性。 应用默认值设置时要求 spec.preserveUnknownFields 为 false。

    JSON 表示任何有效的 JSON 值。支持以下类型:bool、int64、float64、string、[]interface{}、map[string]interface{} 和 nil。

  • definitions (map[string]JSONSchemaProps)

  • dependencies (map[string]JSONSchemaPropsOrStringArray)

    JSONSchemaPropsOrStringArray 表示 JSONSchemaProps 或字符串数组。

  • description (string)

  • enum ([]JSON)

    JSON 表示任何有效的 JSON 值。支持以下类型:bool、int64、float64、string、[]interface{}、map[string]interface{} 和 nil。

  • example (JSON)

    JSON 表示任何有效的 JSON 值。支持以下类型:bool、int64、float64、string、[]interface{}、map[string]interface{} 和 nil。

  • exclusiveMaximum (boolean)

  • exclusiveMinimum (boolean)

  • externalDocs (ExternalDocumentation)

    ExternalDocumentation 允许引用外部资源作为扩展文档。

    • externalDocs.description (string)

    • externalDocs.url (string)

  • format (string)

    format 是 OpenAPI v3 格式字符串。未知格式将被忽略。以下格式会被验证合法性:

    • bsonobjectid:一个 bson 对象的 ID,即一个 24 个字符的十六进制字符串
    • uri:由 Go 语言 net/url.ParseRequestURI 解析得到的 URI
    • email:由 Go 语言 net/mail.ParseAddress 解析得到的电子邮件地址
    • hostname:互联网主机名的有效表示,由 RFC 1034 第 3.1 节 [RFC1034] 定义
    • ipv4:由 Go 语言 net.ParseIP 解析得到的 IPv4 协议的 IP
    • ipv6:由 Go 语言 net.ParseIP 解析得到的 IPv6 协议的 IP
    • cidr:由 Go 语言 net.ParseCIDR 解析得到的 CIDR
    • mac:由 Go 语言 net.ParseMAC 解析得到的一个 MAC 地址
    • uuid:UUID,允许大写字母,满足正则表达式 (?i)^[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}$
    • uuid3:UUID3,允许大写字母,满足正则表达式 (?i)^[0-9a-f]{8}-?[0-9a-f]{4}-?3[0-9a-f]{3}-?[0-9a-f]{4}-?[0-9a-f]{12}$
    • uuid4:UUID4,允许大写字母,满足正则表达式 (?i)^[0-9a-f]{8}-?[0-9a-f]{4}-?4[0-9a-f]{3}-?[89ab][0-9a-f]{3}-?[0-9a-f]{12}$
    • uuid5:UUID5,允许大写字母,满足正则表达式 (?i)^[0-9a-f]{8}-?[0-9a-f]{4}-?5[0-9a-f]{3}-?[89ab][0-9a-f]{3}-?[0-9a-f]{12}$
    • isbn:一个 ISBN10 或 ISBN13 数字字符串,如 "0321751043" 或 "978-0321751041"
    • isbn10:一个 ISBN10 数字字符串,如 "0321751043"
    • isbn13:一个 ISBN13 号码字符串,如 "978-0321751041"
    • creditcard:信用卡号码,满足正则表达式 ^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})$, 其中混合任意非数字字符
    • ssn:美国社会安全号码,满足正则表达式 ^\d{3}[- ]?\d{2}[- ]?\d{4}$
    • hexcolor:一个十六进制的颜色编码,如 "#FFFFFF",满足正则表达式 ^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$
    • rgbcolor:一个 RGB 颜色编码 例如 "rgb(255,255,255)"
    • byte:base64 编码的二进制数据
    • password:任何类型的字符串
    • date:类似 "2006-01-02" 的日期字符串,由 RFC3339 中的完整日期定义
    • duration:由 Go 语言 time.ParseDuration 解析的持续时长字符串,如 "22 ns",或与 Scala 持续时间格式兼容。
    • datetime:一个日期时间字符串,如 "2014-12-15T19:30:20.000Z",由 RFC3339 中的 date-time 定义。
  • id (string)

  • items (JSONSchemaPropsOrArray)

    JSONSchemaPropsOrArray 表示可以是 JSONSchemaProps 或 JSONSchemaProps 数组的值。这里目的主要用于序列化。

  • maxItems (int64)

  • maxLength (int64)

  • maxProperties (int64)

  • maximum (double)

  • minItems (int64)

  • minLength (int64)

  • minProperties (int64)

  • minimum (double)

  • multipleOf (double)

  • not (JSONSchemaProps)

  • nullable (boolean)

  • oneOf ([]JSONSchemaProps)

  • pattern (string)

  • patternProperties (map[string]JSONSchemaProps)

  • properties (map[string]JSONSchemaProps)

  • required ([]string)

  • title (string)

  • type (string)

  • uniqueItems (boolean)

  • x-kubernetes-embedded-resource (boolean)

    x-kubernetes-embedded-resource 定义该值是一个嵌入式 Kubernetes runtime.Object,具有 TypeMeta 和 ObjectMeta。 类型必须是对象。允许进一步限制嵌入对象。会自动验证 kind、apiVersion 和 metadata 等字段值。 x-kubernetes-preserve-unknown-fields 允许为 true,但如果对象已完全指定 (除 kind、apiVersion、metadata 之外),则不必为 true。

  • x-kubernetes-int-or-string (boolean)

    x-kubernetes-int-or-string 指定此值是整数或字符串。如果为 true,则允许使用空类型, 并且如果遵循以下模式之一,则允许作为 anyOf 的子类型:

    1. anyOf:
      • type: integer
      • type: string
    2. allOf:
      • anyOf:
        • type: integer
        • type: string
      • (可以有选择地包含其他类型)
  • x-kubernetes-list-map-keys ([]string)

    X-kubernetes-list-map-keys 通过指定用作 map 索引的键来使用 x-kubernetes-list-type map 注解数组。

    这个标签必须只用于 "x-kubernetes-list-type" 扩展设置为 "map" 的列表。 而且,为这个属性指定的值必须是子结构的标量类型的字段(不支持嵌套)。

    指定的属性必须是必需的或具有默认值,以确保所有列表项都存在这些属性。

  • x-kubernetes-list-type (string)

    x-kubernetes-list-type 注解一个数组以进一步描述其拓扑。此扩展名只能用于列表,并且可能有 3 个可能的值:

    1. atomic: 列表被视为单个实体,就像标量一样。原子列表在更新时将被完全替换。这个扩展可以用于任何类型的列表(结构,标量,…)。
    2. set: set 是不能有多个具有相同值的列表。每个值必须是标量、具有 x-kubernetes-map-type atomic 的对象或具有 x-kubernetes-list-type atomic 的数组。
    3. map: 这些列表类似于映射表,因为它们的元素具有用于标识它们的非索引键。合并时保留顺序。 map 标记只能用于元数类型为 object 的列表。 数组默认为原子数组。
  • x-kubernetes-map-type (string)

    x-kubernetes-map-type 注解一个对象以进一步描述其拓扑。此扩展只能在 type 为 object 时使用,并且可能有 2 个可能的值:

    1. granular: 这些 map 是真实的映射(键值对),每个字段都是相互独立的(它们都可以由不同的角色来操作)。 这是所有 map 的默认行为。
    2. atomic:map 被视为单个实体,就像标量一样。原子 map 更新后将被完全替换。
  • x-kubernetes-preserve-unknown-fields (boolean)

    x-kubernetes-preserve-unknown-fields 针对未在验证模式中指定的字段,禁止 API 服务器的解码步骤剪除这些字段。 这一设置对字段的影响是递归的,但在模式中指定了嵌套 properties 或 additionalProperties 时,会切换回正常的字段剪除行为。 该值可为 true 或 undefined,不能为 false。

  • x-kubernetes-validations ([]ValidationRule)

    补丁策略:基于键 rule 合并

    Map:合并时将保留 rule 键的唯一值

    x-kubernetes-validations 描述了用 CEL 表达式语言编写的验证规则列表。此字段是 Alpha 级别。 使用此字段需要启用 CustomResourceValidationExpressions 特性门控。

    ValidationRule 描述用 CEL 表达式语言编写的验证规则。

    • x-kubernetes-validations.rule (string),必需

      rule 表示将由 CEL 评估的表达式。参考: https://github.com/google/cel-spec。 rule 的作用域为模式中的 x-kubernetes-validation 扩展所在的位置。CEL 表达式中的 self 与作用域值绑定。 例子:rule 的作用域是一个具有状态子资源的资源根:{"rule": "self.status.actual <= self.spec.maxDesired"}。

      如果 rule 的作用域是一个带有属性的对象,那么该对象的可访问属性是通过 self 进行字段选择的, 并且可以通过 has(self.field) 来检查字段是否存在。在 CEL 表达式中,Null 字段被视为不存在的字段。 如果该 rule 的作用域是一个带有附加属性的对象(例如一个 map),那么该 map 的值可以通过 self[mapKey]来访问,map 是否包含某主键可以通过 mapKey in self 来检查。 map 中的所有条目都可以通过 CEL 宏和函数(如 self.all(...))访问。 如果 rule 的作用域是一个数组,数组的元素可以通过 self[i] 访问,也可以通过宏和函数访问。 如果 rule 的作用域为标量,self 绑定到标量值。举例:

      • rule 作用域为对象映射:{"rule": "self.components['Widget'].priority < 10"}
      • rule 作用域为整数列表:{"rule": "self.values.all(value, value >= 0 && value < 100)"}
      • rule 作用域为字符串值:{"rule": "self.startsWith('kube')"}

      apiVersionkindmetadata.namemetadata.generateName 总是可以从对象的根和任何带 x-kubernetes-embedded-resource 注解的对象访问。其他元数据属性都无法访问。

      在 CEL 表达式中无法访问通过 x-kubernetes-preserve-unknown-fields 保存在自定义资源中的未知数据。 这包括:

      • 由包含 x-kubernetes-preserve-unknown-fields 的对象模式所保留的未知字段值;

      • 属性模式为 "未知类型" 的对象属性。"未知类型" 递归定义为:

        • 没有设置 type 但 x-kubernetes-preserve-unknown-fields 设置为 true 的模式。
        • 条目模式为"未知类型"的数组。
        • additionalProperties 模式为"未知类型"的对象。

      只有名称符合正则表达式 [a-zA-Z_.-/][a-zA-Z0-9_.-/]* 的属性才可被访问。 在表达式中访问属性时,可访问的属性名称根据以下规则进行转义:

      • '__' 转义为 'underscores'

      • '.' 转义为 'dot'

      • '-' 转义为 'dash'

      • '/' 转义为 'slash'

      • 恰好匹配 CEL 保留关键字的属性名称转义为 '{keyword}' 。这里的关键字具体包括: "true","false","null","in","as","break","const","continue","else","for","function","if", "import","let","loop","package","namespace","return"。 举例:

        • 规则访问名为 "namespace" 的属性:{"rule": "self.__namespace__ > 0"}
        • 规则访问名为 "x-prop" 的属性:{"rule": "self.x__dash__prop > 0"}
        • 规则访问名为 "redact__d" 的属性:{"rule": "self.redact__underscores__d > 0"}

      对 x-kubernetes-list-type 为 'set' 或 'map' 的数组进行比较时忽略元素顺序,如:[1, 2] == [2, 1]。 使用 x-kubernetes-list-type 对数组进行串接使用下列类型的语义:

      • 'set':X + Y 执行合并,其中 X 保留所有元素的数组位置,并附加不相交的元素 Y,保留其局部顺序。
      • 'map':X + Y 执行合并,保留 X 中所有键的数组位置,但当 XY 的键集相交时,会被 Y 中的值覆盖。 添加 Y 中具有不相交键的元素,保持其局顺序。
    • x-kubernetes-validations.fieldPath (string)

      fieldPath 表示验证失败时返回的字段路径。 它必须是相对 JSON 路径(即,支持数组表示法),范围仅限于此 x-kubernetes-validations 扩展在模式的位置,并引用现有字段。 例如,当验证检查 testMap 映射下是否有 foo 属性时,可以将 fieldPath 设置为 .testMap.foo。 如果验证需要确保两个列表具有各不相同的属性,则可以将 fieldPath 设置到其中任一列表,例如 .testList。 它支持使用子操作引用现有字段,而不支持列表的数字索引。 有关更多信息,请参阅 Kubernetes 中的 JSONPath 支持。 因为其不支持数组的数字索引,所以对于包含特殊字符的字段名称,请使用 ['specialName'] 来引用字段名称。 例如,对于出现在列表 testList 中的属性 foo.34$,fieldPath 可以设置为 .testList['foo.34$']

    • x-kubernetes-validations.message (string)

      message 表示验证失败时显示的消息。如果规则包含换行符,则需要该消息。消息不能包含换行符。 如果未设置,则消息为 "failed rule: {Rule}",如:"must be a URL with the host matching spec.host"

    • x-kubernetes-validations.messageExpression (string)

      messageExpression 声明一个 CEL 表达式,其计算结果是此规则失败时返回的验证失败消息。 由于 messageExpression 用作失败消息,因此它的值必须是一个字符串。 如果在规则中同时存在 message 和 messageExpression,则在验证失败时使用 messageExpression。 如果是 messageExpression 出现运行时错误,则会记录运行时错误,并生成验证失败消息, 就好像未设置 messageExpression 字段一样。如果 messageExpression 求值为空字符串、 只包含空格的字符串或包含换行符的字符串,则验证失败消息也将像未设置 messageExpression 字段一样生成, 并记录 messageExpression 生成空字符串/只包含空格的字符串/包含换行符的字符串的事实。 messageExpression 可以访问的变量与规则相同;唯一的区别是返回类型。 例如:"x must be less than max ("+string(self.max)+")"。

    • x-kubernetes-validations.reason (string)

      reason 提供机器可读的验证失败原因,当请求未通过此验证规则时,该原因会返回给调用者。 返回给调用者的 HTTP 状态代码将与第一个失败的验证规则的原因相匹配。 目前支持的原因有:FieldValueInvalidFieldValueForbiddenFieldValueRequiredFieldValueDuplicate。 如果未设置,则默认使用 FieldValueInvalid。 所有未来添加的原因在读取该值时必须被客户端接受,未知原因应被视为 FieldValueInvalid

CustomResourceDefinitionStatus

CustomResourceDefinitionStatus 表示 CustomResourceDefinition 的状态。


  • acceptedNames (CustomResourceDefinitionNames)

    acceptedNames 是实际用于服务发现的名称。它们可能与规约(spec)中的名称不同。

    CustomResourceDefinitionNames 表示提供此 CustomResourceDefinition 资源的名称。

    • acceptedNames.kind (string),必需

      kind 是资源的序列化类型。它通常是驼峰命名的单数形式。自定义资源实例将使用此值作为 API 调用中的 kind 属性。

    • acceptedNames.plural (string),必需

      plural 是所提供的资源的复数名称,自定义资源在 /apis/<group>/<version>/.../<plural> 下提供。 必须与 CustomResourceDefinition 的名称匹配(格式为 <names.plural>.<group>)。必须全部小写。

    • acceptedNames.categories ([]string)

      categories 是此自定义资源所属的分组资源列表(例如 'all')。 它在 API 发现文档中发布,并被客户端用于支持像 kubectl get all 这样的调用。

    • acceptedNames.listKind (string)

      listKind 是此资源列表的序列化类型。默认为 "<kind>List"。

    • acceptedNames.shortNames ([]string)

      shortNames 是资源的短名称,在 API 发现文档中公开,并支持客户端调用,如 kubectl get <shortname>。必须全部小写。

    • acceptedNames.singular (string)

      singular 是资源的单数名称。必须全部小写。默认为小写形式的 kind

  • conditions ([]CustomResourceDefinitionCondition)

    Map:合并时将保留 type 键的唯一值

    conditions 表示 CustomResourceDefinition 特定方面的状态

    CustomResourceDefinitionCondition 包含此 Pod 当前状况的详细信息。

    • conditions.status (string),必需

      status 表示状况(Condition)的状态,取值为 True、False 或 Unknown 之一。

    • conditions.type (string),必需

      type 是条件的类型。类型包括:Established、NamesAccepted 和 Terminating。

    • conditions.lastTransitionTime (Time)

      lastTransitionTime 是上一次发生状况状态转换的时间。

      Time 是对 time.Time 的封装。Time 支持对 YAML 和 JSON 进行正确封包。为 time 包的许多函数方法提供了封装器。

    • conditions.message (string)

      message 是有关上次转换的详细可读信息。

    • conditions.reason (string)

      reason 表述状况上次转换原因的、驼峰格式命名的、唯一的一个词。

  • storedVersions ([]string)

    storedVersions 列出了曾经被持久化的所有 CustomResources 版本。跟踪这些版本可以为 etcd 中的存储版本提供迁移路径。 该字段是可变的,因此迁移控制器可以完成到另一个版本的迁移(确保存储中没有遗留旧对象),然后从该列表中删除其余版本。 当版本在此列表中时,则不能从 spec.versions 中删除。

CustomResourceDefinitionList

CustomResourceDefinitionList 是 CustomResourceDefinition 对象的列表。


Operations


get 读取指定的 CustomResourceDefinition

HTTP 请求

GET /apis/apiextensions.k8s.io/v1/customresourcedefinitions/{name}

参数

  • name路径参数):string,必需

    CustomResourceDefinition 的名称。

  • pretty查询参数):string

    pretty

响应

200 (CustomResourceDefinition): OK

401: Unauthorized

get 读取指定 CustomResourceDefinition 的状态

HTTP 请求

GET /apis/apiextensions.k8s.io/v1/customresourcedefinitions/{name}/status

参数

  • name路径参数):string,必需

    CustomResourceDefinition 的名称。

  • pretty查询参数):string

    pretty

响应

200 (CustomResourceDefinition): OK

401: Unauthorized

list 列出或观察 CustomResourceDefinition 类型的对象

HTTP 请求

GET /apis/apiextensions.k8s.io/v1/customresourcedefinitions

参数

响应

200 (CustomResourceDefinitionList): OK

401: Unauthorized

create 创建一个 CustomResourceDefinition

HTTP 请求

POST /apis/apiextensions.k8s.io/v1/customresourcedefinitions

参数

响应

200 (CustomResourceDefinition): OK

201 (CustomResourceDefinition): Created

202 (CustomResourceDefinition): Accepted

401: Unauthorized

update 替换指定的 CustomResourceDefinition

HTTP 请求

PUT /apis/apiextensions.k8s.io/v1/customresourcedefinitions/{name}

参数

响应

200 (CustomResourceDefinition): OK

201 (CustomResourceDefinition): Created

401: Unauthorized

update 替换指定 CustomResourceDefinition 的状态

HTTP 请求

PUT /apis/apiextensions.k8s.io/v1/customresourcedefinitions/{name}/status

参数

响应

200 (CustomResourceDefinition): OK

201 (CustomResourceDefinition): Created

401: Unauthorized

patch 部分更新指定的 CustomResourceDefinition

HTTP 请求

PATCH /apis/apiextensions.k8s.io/v1/customresourcedefinitions/{name}

参数

  • name路径参数):string,必需

    CustomResourceDefinition 的名称。

  • bodyPatch,必需

  • dryRun查询参数):string

    dryRun

  • fieldManager查询参数):string

    fieldManager

  • fieldValidation查询参数):string

    fieldValidation

  • force查询参数):boolean

    force

  • pretty查询参数):string

    pretty

响应

200 (CustomResourceDefinition): OK

201 (CustomResourceDefinition): Created

401: Unauthorized

patch 部分更新指定 CustomResourceDefinition 的状态

HTTP 请求

PATCH /apis/apiextensions.k8s.io/v1/customresourcedefinitions/{name}/status

参数

  • name路径参数):string,必需

    CustomResourceDefinition 的名称。

  • bodyPatch,必需

  • dryRun查询参数):string

    dryRun

  • fieldManager查询参数):string

    fieldManager

  • fieldValidation查询参数):string

    fieldValidation

  • force查询参数):boolean

    force

  • pretty查询参数):string

    pretty

响应

200 (CustomResourceDefinition): OK

201 (CustomResourceDefinition): Created

401: Unauthorized

delete 删除一个 CustomResourceDefinition

HTTP 请求

DELETE /apis/apiextensions.k8s.io/v1/customresourcedefinitions/{name}

参数

  • name路径参数):string,必需

    CustomResourceDefinition 的名称。

  • bodyDeleteOptions

  • dryRun查询参数):string

    dryRun

  • gracePeriodSeconds查询参数):integer

    gracePeriodSeconds

  • pretty查询参数):string

    pretty

  • propagationPolicy查询参数):string

    propagationPolicy

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除 CustomResourceDefinition 的集合

HTTP 请求

DELETE /apis/apiextensions.k8s.io/v1/customresourcedefinitions

参数

响应

200 (Status): OK

401: Unauthorized

6.5.7.2 - MutatingWebhookConfiguration

MutatingWebhookConfiguration 描述准入 Webhook 的配置,该 Webhook 可在更改对象的情况下接受或拒绝对象请求

apiVersion: admissionregistration.k8s.io/v1

import "k8s.io/api/admissionregistration/v1"

MutatingWebhookConfiguration

MutatingWebhookConfiguration 描述准入 Webhook 的配置,该 Webhook 可接受或拒绝对象请求,并且可能变更对象。


  • apiVersion:admissionregistration.k8s.io/v1

  • kind:MutatingWebhookConfiguration

  • webhooks ([]MutatingWebhook)

    补丁策略:根据 name 键执行合并操作

    webhooks 是 Webhook 及其所影响的资源和操作的列表。

    MutatingWebhook 描述了一个准入 Webhook 及其适用的资源和操作。

    • webhooks.admissionReviewVersions ([]string), 必需

      admissionReviewVersions 是 Webhook 期望的 AdmissionReview 版本的优选顺序列表。 API 服务器将尝试使用它所支持的版本列表中的第一个版本。如果 API 服务器不支持此列表中设置的任何版本,则此对象将验证失败。 如果持久化的 Webhook 配置指定了所允许的版本,但其中不包括 API 服务器所知道的任何版本, 则对 Webhook 的调用将失败并根据失败策略进行处理。

    • webhooks.clientConfig (WebhookClientConfig), 必需

      clientConfig 定义了如何与 Webhook 通信。必需。

      WebhookClientConfig 包含与 Webhook 建立 TLS 连接的信息

      • webhooks.clientConfig.caBundle ([]byte)

        caBundle 是一个 PEM 编码的 CA 包,将用于验证 Webhook 的服务证书。如果未指定,则使用 apiserver 上的系统信任根。

      • webhooks.clientConfig.service (ServiceReference)

        service 是对此 Webhook 的服务的引用。必须指定 serviceurl 之一。

        如果 Webhook 在集群中运行,那么你应该使用 service

        ServiceReference 包含对 Service.legacy.k8s.io 的引用

        • webhooks.clientConfig.service.name (string), 必需

          name 是服务的名称。必需。

        • webhooks.clientConfig.service.namespace (string), 必需

          namespace 是服务的命名空间。必需。

        • webhooks.clientConfig.service.path (string)

          path 是一个可选的 URL 路径,在针对此服务的所有请求中都会发送此路径。

        • webhooks.clientConfig.service.port (int32)

          如果指定了,则为托管 Webhook 的服务上的端口。默认为 443 以实现向后兼容。 port 应该是一个有效的端口号(包括 1-65535)。

      • webhooks.clientConfig.url (string)

        url 以标准 URL 形式(scheme://host:port/path)给出了 Webhook 的位置。必须指定 urlservice 中的一个。

        host 不能用来引用集群中运行的服务;这种情况应改用 service 字段。在某些 API 服务器上,可能会通过外部 DNS 解析 host 值。 (例如,kube-apiserver 无法解析集群内 DNS,因为这会违反分层原理)。host 也可以是 IP 地址。

        请注意,使用 localhost127.0.0.1 作为 host 是有风险的,除非你非常小心地在运行 apiserver 的所有主机上运行此 Webhook, 而这些 API 服务器可能需要调用此 Webhook。此类部署可能是不可移植的,即不容易在新集群中重复安装。

        该方案必须是 “https”;URL 必须以 “https://” 开头。

        路径是可选的,如果存在,可以是 URL 中允许的任何字符串。你可以使用路径将任意字符串传递给 Webhook,例如集群标识符。

        不允许使用用户或基本身份验证,例如不允许使用 “user:password@”。 不允许使用片段(“#...”)和查询参数(“?...”)。

    • webhooks.name (string), 必需

      准入 Webhook 的名称。应该是完全限定的名称,例如 imagepolicy.kubernetes.io,其中 “imagepolicy” 是 Webhook 的名称, kubernetes.io 是组织的名称。必需。

    • webhooks.sideEffects (string), 必需

      sideEffects 说明此 Webhook 是否有副作用。可接受的值为:None、NoneOnDryRun (通过 v1beta1 创建的 Webhook 也可以指定 Some 或 Unknown)。 具有副作用的 Webhook 必须实现协调系统,因为请求可能会被准入链中的未来步骤拒绝,因此需要能够撤消副作用。 如果请求与带有 sideEffects == Unknown 或 Some 的 Webhook 匹配,则带有 dryRun 属性的请求将被自动拒绝。

    • webhooks.failurePolicy (string)

      failurePolicy 定义如何处理来自准入端点的无法识别的错误 - 允许的值是 Ignore 或 Fail。默认为 Fail。

    • webhooks.matchConditions ([]MatchCondition)

      补丁策略:根据 name 键执行合并操作

      映射:键 name 的唯一值将在合并过程中保留

      matchConditions 是将请求发送到此 webhook 之前必须满足的条件列表。 匹配条件过滤已经被 rules、namespaceSelector、objectSelector 匹配的请求。 matchConditions 取值为空列表时匹配所有请求。最多允许 64 个匹配条件。

      精确匹配逻辑是(按顺序):

      1. 如果任一 matchCondition 的计算结果为 FALSE,则跳过该 webhook。
      2. 如果所有 matchConditions 的计算结果为 TRUE,则调用该 webhook。
      3. 如果任一 matchCondition 的计算结果为错误(但都不是 FALSE):
        • 如果 failurePolicy=Fail,拒绝该请求;
        • 如果 failurePolicy=Ignore,忽略错误并跳过该 webhook。

      这是一个 Beta 功能特性,由 AdmissionWebhookMatchConditions 特性门控管理。

      MatchCondition 表示将请求发送到 Webhook 之前必须满足的条件。

      • webhooks.matchConditions.expression (string), 必需

        expression 表示将由 CEL 求值的表达式。求值结果必须是 bool 值。CEL 表达式可以访问 以 CEL 变量的形式给出的 AdmissionRequest 和 Authorizer 的内容:

        • 'object' - 来自传入请求的对象。对于 DELETE 请求,该值为 null。

        • 'oldObject' - 现有对象。对于 CREATE 请求,该值为 null。

        • 'request' - 准入请求的属性(/pkg/apis/admission/types.go#AdmissionRequest)。

        • 'authorizer' - CEL 授权者。可用于对请求的主体(用户或服务账号)执行授权检查。

          参阅: https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz

        • 'authorizer.requestResource' - CEL ResourceCheck 从"授权方"构建并配置请求资源。

        CEL 文档: https://kubernetes.io/zh-cn/docs/reference/using-api/cel/

        此字段为必需字段。

        • webhooks.matchConditions.name (string), 必需

          name 是此匹配条件的标识符,用于 MatchConditions 的策略性合并, 以及提供用于日志目的的标识符。一个好的 name 应该是对相关表达式的描述。 name 必须是由字母数字字符 -_. 组成的限定名称, 并且必须以字母、数字字符开头和结尾(例如 MyNamemy.name123-abc, 用于验证 name 的正则表达式是 ([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])。 带有可选的 DNS 子域前缀和 /(例如 example.com/MyName

          此字段为必需字段。

    • webhooks.matchPolicy (string)

      matchPolicy 定义了如何使用 “rules” 列表来匹配传入的请求。允许的值为 “Exact” 或 “Equivalent”。

      • Exact: 仅当请求与指定规则完全匹配时才匹配请求。 例如,如果可以通过 apps/v1、apps/v1beta1 和 extensions/v1beta1 修改 deployments 资源, 但 “rules” 仅包含 apiGroups:["apps"]、apiVersions:["v1"]、resources:["deployments"], 对 apps/v1beta1 或 extensions/v1beta1 的请求不会被发送到 Webhook。

      • Equivalent: 如果针对的资源包含在 “rules” 中,即使请求是通过另一个 API 组或版本提交,也会匹配。 例如,如果可以通过 apps/v1、apps/v1beta1 和 extensions/v1beta1 修改 deployments 资源, 并且 “rules” 仅包含 apiGroups:["apps"]、apiVersions:["v1"]、resources:["deployments "], 对 apps/v1beta1 或 extensions/v1beta1 的请求将被转换为 apps/v1 并发送到 Webhook。

      默认为 “Equivalent”。

    • webhooks.namespaceSelector (LabelSelector)

      namespaceSelector 根据对象的命名空间是否与 selector 匹配来决定是否在该对象上运行 Webhook。 如果对象本身是 Namespace,则针对 object.metadata.labels 执行匹配。 如果对象是其他集群作用域资源,则永远不会跳过 Webhook 的匹配动作。

      例如,为了针对 “runlevel” 不为 “0” 或 “1” 的名字空间中的所有对象运行 Webhook; 你可以按如下方式设置 selector :

      "namespaceSelector": {
        "matchExpressions": [
          {
            "key": "runlevel",
            "operator": "NotIn",
            "values": [
              "0",
              "1"
            ]
          }
        ]
      }
      

      相反,如果你只想针对 “environment” 为 “prod” 或 “staging” 的名字空间中的对象运行 Webhook; 你可以按如下方式设置 selector:

      "namespaceSelector": {
        "matchExpressions": [
          {
            "key": "environment",
            "operator": "In",
            "values": [
              "prod",
              "staging"
            ]
          }
        ]
      }
      

      有关标签选择算符的更多示例,请参阅 https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/labels

      默认为空的 LabelSelector,匹配所有对象。

    • webhooks.objectSelector (LabelSelector)

      objectSelector 根据对象是否具有匹配的标签来决定是否运行 Webhook。 objectSelector 针对将被发送到 Webhook 的 oldObject 和 newObject 进行评估,如果任一对象与选择器匹配,则视为匹配。 空对象(create 时为 oldObject,delete 时为 newObject)或不能有标签的对象(如 DeploymentRollback 或 PodProxyOptions 对象) 认为是不匹配的。 仅当 Webhook 支持时才能使用对象选择器,因为最终用户可以通过设置标签来跳过准入 Webhook。 默认为空的 LabelSelector,匹配所有内容。

    • webhooks.reinvocationPolicy (string)

      reinvocationPolicy 表示这个 Webhook 是否可以被多次调用,作为一次准入评估的一部分。可取值有 “Never” 和 “IfNeeded”。

      • Never: 在一次录取评估中,Webhook 被调用的次数不会超过一次。
      • IfNeeded:如果被录取的对象在被最初的 Webhook 调用后又被其他录取插件修改, 那么该 Webhook 将至少被额外调用一次作为录取评估的一部分。 指定此选项的 Webhook 必须是幂等的,能够处理它们之前承认的对象。 注意:不保证额外调用的次数正好为1。 如果额外的调用导致对对象的进一步修改,Webhook 不保证会再次被调用。 使用该选项的 Webhook 可能会被重新排序,以最小化额外调用的数量。 在保证所有的变更都完成后验证一个对象,使用验证性质的准入 Webhook 代替。

      默认值为 “Never” 。

    • webhooks.rules ([]RuleWithOperations)

      rules 描述了 Webhook 关心的资源/子资源上有哪些操作。Webhook 关心操作是否匹配任何rules。 但是,为了防止 ValidatingAdmissionWebhooks 和 MutatingAdmissionWebhooks 将集群置于只能完全禁用插件才能恢复的状态, ValidatingAdmissionWebhooks 和 MutatingAdmissionWebhooks 永远不会在处理 ValidatingWebhookConfiguration 和 MutatingWebhookConfiguration 对象的准入请求时被调用。

      RuleWithOperations 是操作和资源的元组。建议确保所有元组组合都是有效的。

      • webhooks.rules.apiGroups ([]string)

        Atomic: 将在合并期间被替换

        apiGroups 是资源所属的 API 组列表。* 是所有组。 如果存在 *,则列表的长度必须为 1。必需。

      • webhooks.rules.apiVersions ([]string)

        Atomic: 将在合并期间被替换

        apiVersions 是资源所属的 API 版本列表。* 是所有版本。 如果存在 *,则列表的长度必须为 1。必需。

      • webhooks.rules.operations ([]string)

        Atomic: 将在合并期间被替换

        operations 是准入 Webhook 所关心的操作 —— CREATE、UPDATE、DELETE、CONNECT 或用来指代所有已知操作以及将来可能添加的准入操作的 *。 如果存在 *,则列表的长度必须为 1。必需。

      • webhooks.rules.resources ([]string)

        Atomic: 将在合并期间被替换

        resources 是此规则适用的资源列表。

        • pods 表示 pods,'pods/log' 表示 pods 的日志子资源。* 表示所有资源,但不是子资源。
        • pods/* 表示 pods 的所有子资源,
        • */scale 表示所有 scale 子资源,
        • */* 表示所有资源及其子资源。

        如果存在通配符,则验证规则将确保资源不会相互重叠。

        根据所指定的对象,可能不允许使用子资源。必需。

      • webhooks.rules.scope (string)

        scope 指定此规则的范围。有效值为 “Cluster”, “Namespaced” 和 “”。 “Cluster” 表示只有集群范围的资源才会匹配此规则。 Namespace API 对象是集群范围的。 “Namespaced” 意味着只有命名空间作用域的资源会匹配此规则。 “” 表示没有范围限制。 子资源与其父资源的作用域相同。默认为 “*”。

    • webhooks.timeoutSeconds (int32)

      timeoutSeconds 指定此 Webhook 的超时时间。 超时后,Webhook 的调用将被忽略或 API 调用将根据失败策略失败。 超时值必须在 1 到 30 秒之间。默认为 10 秒。

MutatingWebhookConfigurationList

MutatingWebhookConfigurationList 是 MutatingWebhookConfiguration 的列表。


  • apiVersion: admissionregistration.k8s.io/v1

  • kind: MutatingWebhookConfigurationList

操作


get 读取指定的 MutatingWebhookConfiguration

HTTP 请求

GET /apis/admissionregistration.k8s.io/v1/mutatingwebhookconfigurations/{name}

参数

  • name路径参数):string, 必需

    MutatingWebhookConfiguration 的名称。

  • pretty查询参数):string

    pretty

响应

200 (MutatingWebhookConfiguration): OK

401: Unauthorized

list 列出或观察 MutatingWebhookConfiguration 类型的对象

HTTP 请求

GET /apis/admissionregistration.k8s.io/v1/mutatingwebhookconfigurations

参数

  • continue查询参数):string

    continue

  • limit查询参数):integer

    limit

  • pretty查询参数):string

    pretty

  • watch查询参数):boolean

    watch

响应

200 (MutatingWebhookConfigurationList): OK

401: Unauthorized

create 创建一个 MutatingWebhookConfiguration

HTTP 请求

POST /apis/admissionregistration.k8s.io/v1/mutatingwebhookconfigurations

参数

  • dryRun查询参数):string

    dryRun

  • pretty查询参数):string

    pretty

响应

200 (MutatingWebhookConfiguration): OK

201 (MutatingWebhookConfiguration): Created

202 (MutatingWebhookConfiguration): Accepted

401: Unauthorized

update 替换指定的 MutatingWebhookConfiguration

HTTP 请求

PUT /apis/admissionregistration.k8s.io/v1/mutatingwebhookconfigurations/{name}

参数

  • name路径参数):string, 必需

    MutatingWebhookConfiguration 的名称。

  • dryRun查询参数):string

    dryRun

  • pretty查询参数):string

    pretty

响应

200 (MutatingWebhookConfiguration): OK

201 (MutatingWebhookConfiguration): Created

401: Unauthorized

patch 部分更新指定的 MutatingWebhookConfiguration

HTTP 请求

PATCH /apis/admissionregistration.k8s.io/v1/mutatingwebhookconfigurations/{name}

参数

  • name路径参数):string, 必需

    MutatingWebhookConfiguration 的名称。

  • dryRun查询参数):string

    dryRun

  • force查询参数):boolean

    force

  • pretty查询参数):string

    pretty

响应

200 (MutatingWebhookConfiguration): OK

201 (MutatingWebhookConfiguration): Created

401: Unauthorized

delete 删除 MutatingWebhookConfiguration

HTTP 请求

DELETE /apis/admissionregistration.k8s.io/v1/mutatingwebhookconfigurations/{name}

参数

  • name路径参数):string, 必需

    MutatingWebhookConfiguration 的名称。

  • body: DeleteOptions

  • dryRun查询参数):string

    dryRun

  • pretty查询参数):string

    pretty

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除 MutatingWebhookConfiguration 的集合

HTTP 请求

DELETE /apis/admissionregistration.k8s.io/v1/mutatingwebhookconfigurations

参数

  • continue查询参数):string

    continue

  • dryRun查询参数):string

    dryRun

  • limit查询参数):integer

    limit

  • pretty查询参数):string

    pretty

响应

200 (Status): OK

401: Unauthorized

6.5.7.3 - ValidatingWebhookConfiguration

ValidatingWebhookConfiguration 描述准入 Webhook 的配置,该 Webhook 可在不更改对象的情况下接受或拒绝对象请求

apiVersion: admissionregistration.k8s.io/v1

import "k8s.io/api/admissionregistration/v1"

ValidatingWebhookConfiguration

ValidatingWebhookConfiguration 描述准入 Webhook 的配置,该 Webhook 可在不更改对象的情况下接受或拒绝对象请求。


  • apiVersion: admissionregistration.k8s.io/v1

  • kind: ValidatingWebhookConfiguration

  • webhooks ([]ValidatingWebhook)

    补丁策略:根据 name 键执行合并操作

    webhooks 是 Webhook 以及受影响的资源和操作的列表。

    ValidatingWebhook 描述了一个准入 Webhook 及其适用的资源和操作。

    • webhooks.admissionReviewVersions ([]string), 必需

      admissionReviewVersions 是 Webhook 期望的首选 AdmissionReview 版本的有序列表。 API 服务器将尝试使用它支持的列表中的第一个版本。如果 API 服务器不支持此列表中指定的版本,则此对象将验证失败。 如果持久化的 Webhook 配置指定了允许的版本,并且不包括 API 服务器已知的任何版本,则对 Webhook 的调用将失败并受失败策略的约束。

    • webhooks.clientConfig (WebhookClientConfig), 必需

      clientConfig 定义了如何与 Webhook 通信。必需。

      WebhookClientConfig 包含与 Webhook 建立 TLS 连接的信息

      • webhooks.clientConfig.caBundle ([]byte)

        caBundle 是一个 PEM 编码的 CA 包,将用于验证 Webhook 的服务证书。如果未指定,则使用 apiserver 上的系统信任根。

      • webhooks.clientConfig.service (ServiceReference)

        service 是对此 Webhook 服务的引用。必须指定 serviceurl

        如果 Webhook 在集群中运行,那么你应该使用 service

        ServiceReference 持有对 Service.legacy.k8s.io 的引用

        • webhooks.clientConfig.service.name (string), 必需

          name 是服务的名称。必需。

        • webhooks.clientConfig.service.namespace (string), 必需

          namespace 是服务的命名空间。必需。

        • webhooks.clientConfig.service.path (string)

          path 是一个可选的 URL 路径,它将发送任何请求到此服务。

        • webhooks.clientConfig.service.port (int32)

          如果指定,则为托管 Webhook 的服务上的端口。默认为 443 以实现向后兼容性。port 应该是一个有效的端口号(包括 1-65535)。

      • webhooks.clientConfig.url (string)

        url 以标准 URL 形式(scheme://host:port/path)给出了 Webhook 的位置。必须指定 urlservice 中的一个。

        host 不应指代在集群中运行的服务;请改用 service 字段。在某些 apiserver 中,可能会通过外部 DNS 解析 host。 (例如,kube-apiserver 无法解析集群内 DNS,因为这会违反分层原理)。host 也可以是 IP 地址。

        请注意,使用 localhost127.0.0.1 作为 host 是有风险的,除非你非常小心地在运行 apiserver 的所有主机上运行此 Webhook, 而这些 API 服务器可能需要调用此 Webhook。此类部署可能是不可移植的,即不容易在新集群中重复安装。

        该方案必须是 “https”;URL 必须以 “https://” 开头。

        路径是可选的,如果存在,可以是 URL 中允许的任何字符串。你可以使用路径将任意字符串传递给 Webhook,例如集群标识符。

        不允许使用用户或基本身份验证,例如不允许使用 “user:password@”。 不允许使用片段(“#...”)和查询参数(“?...”)。

    • webhooks.name (string), 必需

      准入 Webhook 的名称。应该是完全限定的名称,例如 imagepolicy.kubernetes.io,其中 “imagepolicy” 是 Webhook 的名称, kubernetes.io 是组织的名称。必需。

    • webhooks.sideEffects (string), 必需

    sideEffects 说明此 Webhook 是否有副作用。可接受的值为:None、NoneOnDryRun(通过 v1beta1 创建的 Webhook 也可以指定 Some 或 Unknown)。 具有副作用的 Webhook 必须实现协调系统,因为请求可能会被准入链中的未来步骤拒绝,因此需要能够撤消副作用。 如果请求与带有 sideEffects == Unknown 或 Some 的 Webhook 匹配,则带有 dryRun 属性的请求将被自动拒绝。

    • webhooks.failurePolicy (string)

    failurePolicy 定义了如何处理来自准入端点的无法识别的错误 - 允许的值是 Ignore 或 Fail。默认为 Fail。

    • webhooks.matchConditions ([]MatchCondition)

    补丁策略:根据 name 键的取值合并

    Map:name 键的唯一值将在合并期间保留

    matchConditions 是将请求发送到此 webhook 之前必须满足的条件列表。 匹配条件过滤已经被 rules、namespaceSelector、objectSelector 匹配的请求。 matchConditions 取值为空列表时匹配所有请求。最多允许 64 个匹配条件。

    精确匹配逻辑是(按顺序):

    1. 如果任一 matchCondition 的计算结果为 FALSE,则跳过该 webhook。
    2. 如果所有 matchConditions 的计算结果为 TRUE,则调用该 webhook。
    3. 如果任一 matchCondition 的计算结果为错误(但都不是 FALSE):
      • 如果 failurePolicy=Fail,拒绝该请求;
      • 如果 failurePolicy=Ignore,忽略错误并跳过该 webhook。

    这是一个 Beta 功能特性,由 AdmissionWebhookMatchConditions 特性门控管理。

    MatchCondition 表示将请求发送到 Webhook 之前必须满足的条件。

    • webhooks.matchConditions.expression (string), 必需

    expression 表示将由 CEL 求值的表达式。求值结果必须是 bool 值。CEL 表达式可以访问 以 CEL 变量的形式给出的 AdmissionRequest 和 Authorizer 的内容:

    • 'object' - 来自传入请求的对象。对于 DELETE 请求,该值为 null。

    • 'oldObject' - 现有对象。对于 CREATE 请求,该值为 null。

    • 'request' - 准入请求的属性(/pkg/apis/admission/types.go#AdmissionRequest)。

    • 'authorizer' - CEL 授权者。可用于对请求的主体(用户或服务帐户)执行授权检查。

      参阅:https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz

    • 'authorizer.requestResource' - CEL ResourceCheck 从"授权方"构建并配置请求资源。

    CEL 文档:https://kubernetes.io/zh-cn/docs/reference/using-api/cel/

    此字段为必需字段。

    • webhooks.matchConditions.name (string), 必需

    name 是此匹配条件的标识符,用于 MatchConditions 的策略性合并, 以及提供用于日志目的的标识符。一个好的 name 应该是对相关表达式的描述。 name 必须是由字母数字字符 -_. 组成的限定名称, 并且必须以字母、数字字符开头和结尾(例如 MyNamemy.name123-abc, 用于验证 name 的正则表达式是 ([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])。 带有可选的 DNS 子域前缀和 /(例如 example.com/MyName

    此字段为必需字段。

    • webhooks.matchPolicy (string)

      matchPolicy 定义了如何使用 "rules" 列表来匹配传入的请求。允许的值为 "Exact" 或 "Equivalent"。

      • Exact: 仅当请求与指定规则完全匹配时才匹配请求。 例如,如果可以通过 apps/v1、apps/v1beta1 和 extensions/v1beta1 修改 deployments 资源, 但 “rules” 仅包含 apiGroups:["apps"]、apiVersions:["v1"]、resources:["deployments "], 对 apps/v1beta1 或 extensions/v1beta1 的请求不会被发送到 Webhook。

      • Equivalent: 如果针对的资源包含在 “rules” 中,即使是通过另一个 API 组或版本,也视作匹配请求。 例如,如果可以通过 apps/v1、apps/v1beta1 和 extensions/v1beta1 修改 deployments 资源, 并且 “rules” 仅包含 apiGroups:["apps"]、apiVersions:["v1"]、resources:["deployments "], 对 apps/v1beta1 或 extensions/v1beta1 的请求将被转换为 apps/v1 并发送到 Webhook。

      默认为 “Equivalent”。

    • webhooks.namespaceSelector (LabelSelector)

      namespaceSelector 根据对象的命名空间是否与 selector 匹配来决定是否在该对象上运行 Webhook。 
      

      如果对象本身是命名空间,则在 object.metadata.labels 上执行匹配。 如果对象是另一个集群范围的资源,则永远不会跳过 Webhook 执行匹配。

      例如,在命名空间与 “0” 或 “1” 的 “runlevel” 不关联的任何对象上运行 Webhook; 你可以按如下方式设置 selector :

      "namespaceSelector": {
      "matchExpressions": [
        {
          "key": "runlevel",
          "operator": "NotIn",
          "values": [
            "0",
            "1"
          ]
        }
      ]
      }
      
      相反,如果你只想在命名空间与 “prod” 或 “staging” 的 “environment” 相关联的对象上运行 Webhook;
      

      你可以按如下方式设置 selector:

      "namespaceSelector": {
      "matchExpressions": [
        {
          "key": "environment",
          "operator": "In",
          "values": [
            "prod",
            "staging"
          ]
        }
      ]
      }
      
      有关标签选择算符的更多示例,请参阅 
      

      https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/labels

      默认为空的 LabelSelector,匹配所有对象。

    • webhooks.objectSelector (LabelSelector)

      objectSelector 根据对象是否具有匹配的标签来决定是否运行 Webhook。 objectSelector 针对将被发送到 Webhook 的 oldObject 和 newObject 进行评估,如果任一对象与选择器匹配,则视为匹配。 空对象(create 时为 oldObject,delete 时为 newObject)或不能有标签的对象(如 DeploymentRollback 或 PodProxyOptions 对象) 认为是不匹配的。 仅当 Webhook 支持时才能使用对象选择器,因为最终用户可以通过设置标签来跳过准入 webhook。 默认为空的 LabelSelector,匹配所有内容。

    • webhooks.rules ([]RuleWithOperations)

      rules 描述了 Webhook 关心的资源/子资源上有哪些操作。Webhook 关心操作是否匹配任何rules。 但是,为了防止 ValidatingAdmissionWebhooks 和 MutatingAdmissionWebhooks 将集群置于只能完全禁用插件才能恢复的状态, ValidatingAdmissionWebhooks 和 MutatingAdmissionWebhooks 永远不会在处理 ValidatingWebhookConfiguration 和 MutatingWebhookConfiguration 对象的准入请求被调用。

      RuleWithOperations 是操作和资源的元组。建议确保所有元组组合都是有效的。

      • webhooks.rules.apiGroups ([]string)

        原子性: 合并期间会被替换

        apiGroups 是资源所属的 API 组列表。'' 是所有组。 如果存在 '',则列表的长度必须为 1。必需。

      • webhooks.rules.apiVersions ([]string)

        原子性: 合并期间会被替换

        apiVersions 是资源所属的 API 版本列表。'' 是所有版本。 如果存在 '',则列表的长度必须为 1。必需。

      • webhooks.rules.operations ([]string)

        原子性: 合并期间会被替换

        operations 是准入 Webhook 所关心的操作 —— CREATE、UPDATE、DELETE、CONNECT 或用来指代所有已知操作以及将来可能添加的准入操作的 *。 如果存在 '*',则列表的长度必须为 1。必需。

      • webhooks.rules.resources ([]string)

        原子性: 合并期间会被替换

        resources 是此规则适用的资源列表。

        • 'pods' 表示 pods,'pods/log' 表示 pods 的日志子资源。'*' 表示所有资源,但不是子资源。
        • 'pods/*' 表示 pods 的所有子资源,
        • '*/scale' 表示所有 scale 子资源,
        • '/' 表示所有资源及其子资源。

        如果存在通配符,则验证规则将确保资源不会相互重叠。

        根据所指定的对象,可能不允许使用子资源。必需。

      • webhooks.rules.scope (string)

        scope 指定此规则的范围。有效值为 "Cluster", "Namespaced" 和 ""。 "Cluster" 表示只有集群范围的资源才会匹配此规则。 Namespace API 对象是集群范围的。 "Namespaced" 意味着只有命名空间作用域的资源会匹配此规则。 "" 表示没有范围限制。 子资源与其父资源的作用域相同。默认为 "*"。

    • webhooks.timeoutSeconds (int32)

      timeoutSeconds 指定此 Webhook 的超时时间。超时后,Webhook 的调用将被忽略或 API 调用将根据失败策略失败。 超时值必须在 1 到 30 秒之间。默认为 10 秒。

ValidatingWebhookConfigurationList

ValidatingWebhookConfigurationList 是 ValidatingWebhookConfiguration 的列表。


  • apiVersion: admissionregistration.k8s.io/v1

  • kind: ValidatingWebhookConfigurationList

操作


get 读取指定的 ValidatingWebhookConfiguration

HTTP 请求

GET /apis/admissionregistration.k8s.io/v1/validatingwebhookconfigurations/{name}

参数

  • name (路径参数): string, 必需

    ValidatingWebhookConfiguration 的名称。

  • pretty (查询参数): string

    pretty

响应

200 (ValidatingWebhookConfiguration): OK

401: Unauthorized

list 列出或观察 ValidatingWebhookConfiguration 类型的对象

HTTP 请求

GET /apis/admissionregistration.k8s.io/v1/validatingwebhookconfigurations

参数

  • limit (查询参数): integer

    limit

  • pretty (查询参数): string

    pretty

  • watch (查询参数): boolean

    watch

响应

200 (ValidatingWebhookConfigurationList): OK

401: Unauthorized

create 创建一个 ValidatingWebhookConfiguration

HTTP 请求

POST /apis/admissionregistration.k8s.io/v1/validatingwebhookconfigurations

参数

  • dryRun (查询参数): string

    dryRun

  • pretty (查询参数): string

    pretty

响应

200 (ValidatingWebhookConfiguration): OK

201 (ValidatingWebhookConfiguration): Created

202 (ValidatingWebhookConfiguration): Accepted

401: Unauthorized

update 替换指定的 ValidatingWebhookConfiguration

HTTP 请求

PUT /apis/admissionregistration.k8s.io/v1/validatingwebhookconfigurations/{name}

参数

  • name (路径参数): string, 必需

    ValidatingWebhookConfiguration 的名称。

  • dryRun (查询参数): string

    dryRun

  • pretty (查询参数): string

    pretty

响应

200 (ValidatingWebhookConfiguration): OK

201 (ValidatingWebhookConfiguration): Created

401: Unauthorized

patch 部分更新指定的 ValidatingWebhookConfiguration

HTTP 请求

PATCH /apis/admissionregistration.k8s.io/v1/validatingwebhookconfigurations/{name}

参数

  • name (路径参数): string, 必需

    ValidatingWebhookConfiguration 的名称。

  • dryRun (查询参数): string

    dryRun

  • force (查询参数): boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (ValidatingWebhookConfiguration): OK

201 (ValidatingWebhookConfiguration): Created

401: Unauthorized

delete 删除 ValidatingWebhookConfiguration

HTTP 请求

DELETE /apis/admissionregistration.k8s.io/v1/validatingwebhookconfigurations/{name}

参数

  • name (路径参数): string, 必需

    ValidatingWebhookConfiguration 的名称。

  • body: DeleteOptions

  • dryRun (查询参数): string

    dryRun

  • pretty (查询参数): string

    pretty

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除 ValidatingWebhookConfiguration 的集合

HTTP 请求

DELETE /apis/admissionregistration.k8s.io/v1/validatingwebhookconfigurations

参数

  • dryRun (查询参数): string

    dryRun

  • limit (查询参数): integer

    limit

  • pretty (查询参数): string

    pretty

响应

200 (Status): OK

401: Unauthorized

6.5.8 - 集群资源

6.5.8.1 - Node

Node 是 Kubernetes 中的工作节点。

apiVersion: v1

import "k8s.io/api/core/v1"

Node

Node 是 Kubernetes 中的工作节点。 每个节点在缓存中(即在 etcd 中)都有一个唯一的标识符。


  • apiVersion: v1

  • kind: Node

NodeSpec

NodeSpec 描述了创建节点时使用的属性。


  • configSource (NodeConfigSource)

    已弃用:以前用于为 DynamicKubeletConfig 功能指定节点配置的来源。此功能已删除。

    NodeConfigSource 指定节点配置的来源。指定一个子字段(不包括元数据)必须为非空。此 API 自 1.22的版本起已被弃用

    • configSource.configMap (ConfigMapNodeConfigSource)

      configMap 是对 Node 的 ConfigMap 的引用。

      ConfigMapNodeConfigSource 包含引用某 ConfigMap 作为节点配置源的信息。 此 API 自 1.22 版本起已被弃用: https://git.k8s.io/enhancements/keps/sig-node/281-dynamic-kubelet-configuration

      • configSource.configMap.kubeletConfigKey (string), 必需

        kubeletConfigKey 声明所引用的 ConfigMap 的哪个键对应于 KubeletConfiguration 结构体, 该字段在所有情况下都是必需的。

      • configSource.configMap.name (string), 必需

        name 是被引用的 ConfigMap 的 metadata.name。 此字段在所有情况下都是必需的。

      • configSource.configMap.namespace (string), 必需

        namespace 是所引用的 ConfigMap 的 metadata.namespace。 此字段在所有情况下都是必需的。

      • configSource.configMap.resourceVersion (string)

        resourceVersion 是所引用的 ConfigMap 的 metadata.resourceVersion。 该字段在 Node.spec 中是禁止的,在 Node.status 中是必需的。

      • configSource.configMap.uid (string)

        uid 是所引用的 ConfigMap 的 metadata.uid。 该字段在 Node.spec 中是禁止的,在 Node.status 中是必需的。

  • externalID (string)

    已弃用。并非所有 kubelet 都会设置此字段。 1.13 的版本之后会删除该字段。参见: https://issues.k8s.io/61966

  • podCIDR (string)

    podCIDR 表示分配给节点的 Pod IP 范围。

  • podCIDRs ([]string)

    podCIDRs 表示分配给节点以供该节点上的 Pod 使用的 IP 范围。 如果指定了该字段,则第 0 个条目必须与 podCIDR 字段匹配。 对于 IPv4 和 IPv6,它最多可以包含 1 个值。

  • providerID (string)

    云提供商分配的节点ID,格式为:<ProviderName>://<ProviderSpecificNodeID>

  • taints ([]Taint)

    如果设置了,则为节点的污点。

    此污点附加到的节点对任何不容忍污点的 Pod 都有 “影响”。

    • taints.effect (string), 必需

      必需的。污点对不容忍污点的 Pod 的影响。合法的 effect 值有 NoSchedule、PreferNoSchedule 和 NoExecute。

    • taints.key (string), 必需

      必需的。被应用到节点上的污点的键。

    • taints.timeAdded (Time)

      timeAdded 表示添加污点的时间。它仅适用于 NoExecute 的污点。

      Time 是 time.Time 的包装器,它支持对 YAML 和 JSON 的正确编组。 time 包的许多工厂方法提供了包装器。

    • taints.value (string)

      与污点键对应的污点值。

  • unschedulable (boolean)

    unschedulable 控制新 Pod 的节点可调度性。 默认情况下,节点是可调度的。 更多信息: https://kubernetes.io/zh-cn/docs/concepts/architecture/nodes/#manual-node-administration

NodeStatus

NodeStatus 是有关节点当前状态的信息。


  • addresses ([]NodeAddress)

    补丁策略:根据 type 键执行合并操作

    节点可到达的地址列表。从云提供商处查询(如果有)。 更多信息: https://kubernetes.io/zh-cn/docs/concepts/architecture/nodes/#addresses

    注意:该字段声明为可合并,但合并键不够唯一,合并时可能导致数据损坏。 调用者应改为使用完全替换性质的补丁操作。 有关示例,请参见 https://pr.k8s.io/79391

    消费者应假设地址可以在节点的生命期内发生变化。 然而在一些例外情况下这是不可能的,例如在自身状态中继承 Node 地址的 Pod 或 downward API (status.hostIP) 的消费者。

    NodeAddress 包含节点地址的信息。

    • addresses.address (string), 必需

      节点地址。

    • addresses.type (string), 必需

      节点地址类型,Hostname、ExternalIP 或 InternalIP 之一。

  • allocatable (map[string]Quantity)

    allocatable 表示节点的可用于调度的资源。默认为容量。

  • capacity (map[string]Quantity)

    capacity 代表一个节点的总资源。 更多信息: https://kubernetes.io/zh-cn/docs/concepts/storage/persistent-volumes/#capacity

  • conditions ([]NodeCondition)

    补丁策略:根据 type 键执行合并操作

    conditions 是当前观测到的节点状况的数组。 更多信息: https://kubernetes.io/zh-cn/docs/concepts/architecture/nodes/#condition

    NodeCondition 包含节点状况的信息。

    • conditions.status (string), 必需

      状况的状态为 True、False、Unknown 之一。

    • conditions.type (string), 必需

      节点状况的类型。

    • conditions.lastHeartbeatTime (Time)

      给定状况最近一次更新的时间。

      Time 是 time.Time 的包装器,它支持对 YAML 和 JSON 的正确编组。 time 包的许多工厂方法提供了包装器。

    • conditions.lastTransitionTime (Time)

      状况最近一次从一种状态转换到另一种状态的时间。

      Time 是 time.Time 的包装器,它支持对 YAML 和 JSON 的正确编组。 time 包的许多工厂方法提供了包装器。

    • conditions.message (string)

      指示有关上次转换详细信息的人类可读消息。

    • conditions.reason (string)

      (简要)状况最后一次转换的原因。

  • config (NodeConfigStatus)

    通过动态 Kubelet 配置功能分配给节点的配置状态。

    NodeConfigStatus 描述了由 Node.spec.configSource 分配的配置的状态。

    • config.active (NodeConfigSource)

      active 报告节点正在使用的检查点配置。 active 将代表已分配配置的当前版本或当前 LastKnownGood 配置,具体取决于尝试使用已分配配置是否会导致错误。

      NodeConfigSource 指定节点配置的来源。指定一个子字段(不包括元数据)必须为非空。此 API 自 1.22 版本起已弃用

      • config.active.configMap (ConfigMapNodeConfigSource)

        configMap 是对 Node 的 ConfigMap 的引用。

        ConfigMapNodeConfigSource 包含引用某 ConfigMap 作为节点配置源的信息。 此 API 自 1.22 版本起已被弃用: https://git.k8s.io/enhancements/keps/sig-node/281-dynamic-kubelet-configuration

        • config.active.configMap.kubeletConfigKey (string), 必需

          kubeletConfigKey 声明所引用的 ConfigMap 的哪个键对应于 KubeletConfiguration 结构体, 该字段在所有情况下都是必需的。

        • config.active.configMap.name (string), 必需

          name 是所引用的 ConfigMap 的 metadata.name。 此字段在所有情况下都是必需的。

        • config.active.configMap.namespace (string), 必需

          namespace 是所引用的 ConfigMap 的 metadata.namespace。 此字段在所有情况下都是必需的。

        • config.active.configMap.resourceVersion (string)

          resourceVersion 是所引用的 ConfigMap 的 metadata.resourceVersion。 该字段在 Node.spec 中是禁止的,在 Node.status 中是必需的。

        • config.active.configMap.uid (string)

          uid 是所引用的 ConfigMap 的 metadata.uid。 该字段在 Node.spec 中是禁止的,在 Node.status 中是必需的。

    • config.assigned (NodeConfigSource)

      assigned 字段报告节点将尝试使用的检查点配置。 当 Node.spec.configSource 被更新时,节点将所关联的配置负载及指示预期配置的记录通过检查点操作加载到本地磁盘。 节点参考这条记录来选择它的配置检查点,并在 assigned 中报告这条记录。 仅在记录被保存到磁盘后才会更新 status 中的 assigned。 当 kubelet 重新启动时,它会尝试通过加载和验证由 assigned 标识的检查点有效负载来使 assigned 配置成为 active 配置。

      NodeConfigSource 指定节点配置的来源。指定一个子字段(不包括元数据)必须为非空。此 API 自 1.22 版本起已弃用

      • config.assigned.configMap (ConfigMapNodeConfigSource)

        configMap 是对 Node 的 ConfigMap 的引用。

        ConfigMapNodeConfigSource 包含引用某 ConfigMap 为节点配置源的信息。 此 API 自 1.22 版本起已被弃用: https://git.k8s.io/enhancements/keps/sig-node/281-dynamic-kubelet-configuration

        • config.assigned.configMap.kubeletConfigKey (string), 必需

          kubeletConfigKey 声明所引用的 ConfigMap 的哪个键对应于 KubeletConfiguration 结构体, 该字段在所有情况下都是必需的。

        • config.assigned.configMap.name (string), 必需

          name 是所引用的 ConfigMap 的 metadata.name。 此字段在所有情况下都是必需的。

        • config.assigned.configMap.namespace (string), 必需

          namespace 是所引用的 ConfigMap 的 metadata.namespace。 此字段在所有情况下都是必需的。

        • config.assigned.configMap.resourceVersion (string)

          resourceVersion 是所引用的 ConfigMap 的 metadata.resourceVersion。 该字段在 Node.spec 中是禁止的,在 Node.status 中是必需的。

        • config.assigned.configMap.uid (string)

          uid 是所引用的 ConfigMap 的 metadata.uid。 该字段在 Node.spec 中是禁止的,在 Node.status 中是必需的。

    • config.error (string)

      error 描述了在 spec.configSource 与活动配置间协调时发生的所有问题。 可能会发生的情况,例如,尝试将 spec.configSource 通过检查点操作复制到到本地 assigned 记录时, 尝试对与 spec.configSource 关联的有效负载执行检查点操作,尝试加​​载或验证 assigned 的配置时。 同步配置时可能会在不同位置发生错误,较早的错误(例如下载或检查点错误)不会导致回滚到 LastKnownGood, 并且可能会在 Kubelet 重试后解决。 后期发生的错误(例如加载或验证检查点配置)将导致回滚到 LastKnownGood。 在后一种情况下,通常可以通过修复 spec.sonfigSource 中 assigned 配置来解决错误。 你可以通过在 Kubelet 日志中搜索错误消息来找到更多的调试信息。 error 是错误状态的人类可读描述;机器可以检查 error 是否为空,但不应依赖跨 Kubelet 版本的 error 文本的稳定性。

    • config.lastKnownGood (NodeConfigSource)

      lastKnownGood 报告节点在尝试使用 assigned 配置时遇到错误时将回退到的检查点配置。 当节点确定 assigned 配置稳定且正确时,assigned 配置会成为 lastKnownGood 配置。 这当前实施为从更新分配配置的本地记录开始的 10 分钟浸泡期。 如果在此期间结束时分配的配置依旧处于活动状态,则它将成为 lastKnownGood。 请注意,如果 spec.configSource 重置为 nil(使用本地默认值), LastKnownGood 也会立即重置为 nil,因为始终假定本地默认配置是好的。 你不应该对节点确定配置稳定性和正确性的方法做出假设,因为这可能会在将来发生变化或变得可配置。

      NodeConfigSource 指定节点配置的来源。指定一个子字段(不包括元数据)必须为非空。此 API 自 1.22 版本起已弃用

      • config.lastKnownGood.configMap (ConfigMapNodeConfigSource)

        configMap 是对 Node 的 ConfigMap 的引用。

        ConfigMapNodeConfigSource 包含引用某 ConfigMap 作为节点配置源的信息。 此 API 自 1.22 版本起已被弃用: https://git.k8s.io/enhancements/keps/sig-node/281-dynamic-kubelet-configuration

        • config.lastKnownGood.configMap.kubeletConfigKey (string), 必需

          kubeletConfigKey 声明所引用的 ConfigMap 的哪个键对应于 KubeletConfiguration 结构体, 该字段在所有情况下都是必需的。

        • config.lastKnownGood.configMap.name (string), 必需

          name 是所引用的 ConfigMap 的 metadata.name。 此字段在所有情况下都是必需的。

        • config.lastKnownGood.configMap.namespace (string), 必需

          namespace 是所引用的 ConfigMap 的 metadata.namespace。 此字段在所有情况下都是必需的。

        • config.lastKnownGood.configMap.resourceVersion (string)

          resourceVersion 是所引用的 ConfigMap 的 metadata.resourceVersion。 该字段在 Node.spec 中是禁止的,在 Node.status 中是必需的。

        • config.lastKnownGood.configMap.uid (string)

          uid 是所引用的 ConfigMap 的 metadata.uid。 该字段在 Node.spec 中是禁止的,在 Node.status 中是必需的。

  • daemonEndpoints (NodeDaemonEndpoints)

    在节点上运行的守护进程的端点。

    NodeDaemonEndpoints 列出了节点上运行的守护进程打开的端口。

    • daemonEndpoints.kubeletEndpoint (DaemonEndpoint)

      Kubelet 正在侦听的端点。

      DaemonEndpoint 包含有关单个 Daemon 端点的信息。

      • daemonEndpoints.kubeletEndpoint.Port (int32), 必需

        给定端点的端口号。

  • images ([]ContainerImage)

    该节点上的容器镜像列表。

    描述一个容器镜像

    • images.names ([]string)

      已知此镜像的名称。 例如 ["kubernetes.example/hyperkube:v1.0.7", "cloud-vendor.registry.example/cloud-vendor/hyperkube:v1.0.7"]

    • images.sizeBytes (int64)

      镜像的大小(以字节为单位)。

  • nodeInfo (NodeSystemInfo)

    用于唯一标识节点的 ids/uuids 集。 更多信息: https://kubernetes.io/zh-cn/docs/concepts/architecture/nodes/#info

    NodeSystemInfo 是一组用于唯一标识节点的 ids/uuids。

    • nodeInfo.architecture (string), 必需

      节点报告的 architecture。

    • nodeInfo.bootID (string), 必需

      节点报告的 bootID。

    • nodeInfo.containerRuntimeVersion (string), 必需

      节点通过运行时远程 API 报告的 ContainerRuntime 版本(例如 containerd://1.4.2)。

    • nodeInfo.kernelVersion (string), 必需

      节点来自 “uname -r” 报告的内核版本(例如 3.16.0-0.bpo.4-amd64)。

    • nodeInfo.kubeProxyVersion (string), 必需

      节点报告的 KubeProxy 版本。

    • nodeInfo.kubeletVersion (string), 必需

      节点报告的 Kubelet 版本。

    • nodeInfo.operatingSystem (string), 必需

      节点上报的操作系统。

    • nodeInfo.osImage (string), 必需

      节点从 /etc/os-release 报告的操作系统映像(例如 Debian GNU/Linux 7 (wheezy))。

  • phase (string)

    NodePhase 是最近观测到的节点的生命周期阶段。 更多信息: https://kubernetes.io/zh-cn/docs/concepts/architecture/nodes/#phase

    该字段从未填充,现在已被弃用。

  • volumesAttached ([]AttachedVolume)

    附加到节点的卷的列表。

    AttachedVolume 描述附加到节点的卷

    • volumesAttached.devicePath (string), 必需

      devicePath 表示卷应该可用的设备路径。

    • volumesAttached.name (string), 必需

      附加卷的名称。

  • volumesInUse ([]string)

    节点正在使用(安装)的可附加卷的列表。

NodeList

NodeList 是已注册到 master 的所有节点的完整列表。


  • items ([]Node), 必需

    节点的列表。

操作


get 读取指定节点

HTTP 请求

GET /api/v1/nodes/{name}

参数

  • name (路径参数): string, 必需

    节点的名称。

  • pretty (路径参数): string

    pretty

响应

200 (Node): OK

401: Unauthorized

get 读取指定节点的状态

HTTP 请求

GET /api/v1/nodes/{name}/status

参数

  • name (路径参数): string, 必需

    节点的名称。

  • pretty (查询参数): string

    pretty

响应

200 (Node): OK

401: Unauthorized

list 列出或监视节点类型的对象

HTTP 请求

GET /api/v1/nodes

参数

响应

200 (NodeList): OK

401: Unauthorized

create 创建一个节点

HTTP 请求

POST /api/v1/nodes

参数

响应

200 (Node): OK

201 (Node): Created

202 (Node): Accepted

401: Unauthorized

update 替换指定节点

HTTP 请求

PUT /api/v1/nodes/{name}

参数

  • name (路径参数): string, 必需

    节点的名称。

  • body: Node, 必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • pretty (查询参数): string

    pretty

响应

200 (Node): OK

201 (Node): Created

401: Unauthorized

update 替换指定节点的状态

HTTP 请求

PUT /api/v1/nodes/{name}/status

参数

  • name (路径参数): string, 必需

    节点的名称。

  • body: Node, 必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • pretty (查询参数): string

    pretty

响应

200 (Node): OK

201 (Node): Created

401: Unauthorized

patch 部分更新指定节点

HTTP 请求

PATCH /api/v1/nodes/{name}

参数

  • name (路径参数): string, 必需

    节点的名称。

  • body: Patch, 必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • force (查询参数): boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (Node): OK

201 (Node): Created

401: Unauthorized

patch 部分更新指定节点的状态

HTTP 请求

PATCH /api/v1/nodes/{name}/status

参数

  • name (路径参数): string, 必需

    节点的名称。

  • body: Patch, 必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • force (查询参数): boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (Node): OK

201 (Node): Created

401: Unauthorized

delete 删除一个节点

HTTP 请求

DELETE /api/v1/nodes/{name}

参数

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除节点的集合

HTTP 请求

DELETE /api/v1/nodes

响应

200 (Status): OK

401: Unauthorized

6.5.8.2 - Namespace

Namespace 为名字提供作用域。

apiVersion: v1

import "k8s.io/api/core/v1"

Namespace

Namespace 为名字提供作用域。使用多个命名空间是可选的。


NamespaceSpec

NamespaceSpec 用于描述 Namespace 的属性。


NamespaceStatus

NamespaceStatus 表示 Namespace 的当前状态信息。


  • conditions ([]NamespaceCondition)

    补丁策略:基于 type 健合并

    表示命名空间当前状态的最新可用状况。

    NamespaceCondition 包含命名空间状态的详细信息。

    • conditions.status (string),必需

      状况(condition)的状态,取值为 True、False 或 Unknown 之一。

    • conditions.type (string), 必需

    命名空间控制器状况的类型。

    • conditions.lastTransitionTime (Time)

      **Time 是对 time.Time 的封装。Time 支持对 YAML 和 JSON 进行正确封包。为 time 包的许多函数方法提供了封装器。**
      
    • conditions.message (string)

    • conditions.reason (string)

  • phase (string)

    phase 是命名空间的当前生命周期阶段。更多信息: https://kubernetes.io/zh-cn/docs/tasks/administer-cluster/namespaces/

NamespaceList

NamespaceList 是一个命名空间列表。


操作


get 读取指定的 Namespace

HTTP 请求

GET /api/v1/namespaces/{name}

参数

  • name (路径参数):string,必需

    Namespace 的名称

  • pretty (查询参数):string

    pretty

响应

200 (Namespace):OK

401:Unauthorized

get 读取指定 Namespace 的状态

HTTP 请求

GET /api/v1/namespaces/{name}/status

参数

  • name (路径参数):string,必需

    Namespace 的名称

  • pretty (查询参数):string

    pretty

响应

200 (Namespace):OK

401:Unauthorized

list 列出或者检查类别为 Namespace 的对象

HTTP 请求

GET /api/v1/namespaces

参数

  • continue (查询参数):string

    continue

  • limit (查询参数):integer

    limit

  • pretty (查询参数):string

    pretty

  • watch (查询参数):boolean

    watch

响应

200 (NamespaceList):OK

401:Unauthorized

create 创建一个 Namespace

HTTP 请求

POST /api/v1/namespaces

参数

  • dryRun (查询参数):string

    dryRun

  • pretty (查询参数):string

    pretty

响应

200 (Namespace):OK

201 (Namespace):Created

202 (Namespace):Accepted

401:Unauthorized

update 替换指定的 Namespace

HTTP 请求

PUT /api/v1/namespaces/{name}

参数

  • name (路径参数):string,必需

    Namespace 的名称

  • body: Namespace, 必需

  • dryRun (查询参数):string

    dryRun

  • pretty (查询参数):string

    pretty

响应

200 (Namespace):OK

201 (Namespace):Created

401:Unauthorized

update 替换指定 Namespace 的终结器

HTTP 请求

PUT /api/v1/namespaces/{name}/finalize

参数

  • name (路径参数):string,必需

    Namespace 的名称

  • body: Namespace,必需

  • dryRun (查询参数):string

    dryRun

  • pretty (查询参数):string

    pretty

响应

200 (Namespace):OK

201 (Namespace):Created

401:Unauthorized

update 替换指定 Namespace 的状态

HTTP 请求

PUT /api/v1/namespaces/{name}/status

参数

  • name (路径阐述):string,必需

    Namespace 的名称

  • body: Namespace,必需

  • dryRun (查询参数):string

    dryRun

  • pretty (查询参数):string

    pretty

响应

200 (Namespace):OK

201 (Namespace):Created

401: Unauthorized

patch 部分更新指定的 Namespace

HTTP 请求

PATCH /api/v1/namespaces/{name}

参数

  • name (路径参数):string,必需

    Namespace 的名称

  • dryRun (查询参数):string

    dryRun

  • force (查询参数):boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (Namespace):OK

201 (Namespace):Created

401: Unauthorized

patch 部分更新指定 Namespace 的状态

HTTP 请求

PATCH /api/v1/namespaces/{name}/status

参数

  • name (路径参数):string,必需

    Namespace 的名称

  • dryRun (查询参数):string

    dryRun

  • force (查询参数): boolean

    force

  • pretty (查询参数):string

    pretty

响应

200 (Namespace):OK

201 (Namespace):Created

401:Unauthorized

delete 删除一个 Namespace

HTTP 请求

DELETE /api/v1/namespaces/{name}

参数

  • name (路径参数):string,必需

    Namespace 的名称

  • body: DeleteOptions

  • dryRun (查询参数):string

    dryRun

  • pretty (查询参数):string

    pretty

响应

200 (Status):OK

202 (Status):Accepted

401:Unauthorized

6.5.8.3 - Event

Event 是集群中某个事件的报告。

apiVersion: events.k8s.io/v1

import "k8s.io/api/events/v1"

Event

Event 是集群中某个事件的报告。它一般表示系统的某些状态变化。 Event 的保留时间有限,触发器和消息可能会随着时间的推移而演变。 事件消费者不应假定给定原因的事件的时间所反映的是一致的下层触发因素,或具有该原因的事件的持续存在。 Events 应被视为通知性质的、尽最大努力而提供的补充数据。


  • eventTime (MicroTime),必需

    evenTime 是该事件首次被观察到的时间。它是必需的。

    MicroTime 是微秒级精度的 Time 版本

  • action (string)

    action 是针对相关对象所采取的或已失败的动作。字段值是机器可读的。对于新的 Event,此字段不能为空, 且最多为 128 个字符。

  • deprecatedCount (int32)

    deprecatedCount 是确保与 core.v1 Event 类型向后兼容的已弃用字段。

  • deprecatedFirstTimestamp (Time)

    deprecatedFirstTimestamp 是确保与 core.v1 Event 类型向后兼容的已弃用字段。

    Time 是对 time.Time 的封装。Time 支持对 YAML 和 JSON 进行正确封包。为 time 包的许多函数方法提供了封装器。

  • deprecatedLastTimestamp (Time)

    deprecatedLastTimestamp 是确保与 core.v1 Event 类型向后兼容的已弃用字段。

    Time 是对 time.Time 的封装。Time 支持对 YAML 和 JSON 进行正确封包。为 time 包的许多函数方法提供了封装器。

  • deprecatedSource (EventSource)

    deprecatedSource 是确保与 core.v1 Event 类型向后兼容的已弃用字段。

    EventSource 包含事件信息。

    • deprecatedSource.component (string)

      生成事件的组件。

    • deprecatedSource.host (string)

      产生事件的节点名称。

  • note (string)

    node 是对该操作状态的可读描述。注释的最大长度是 1kB,但是库应该准备好处理最多 64kB 的值。

  • reason (string)

    reason 是采取行动的原因。它是人类可读的。对于新的 Event,此字段不能为空,且最多为128个字符。

  • regarding (ObjectReference)

    关于包含此 Event 所涉及的对象。在大多数情况下,所指的是报告事件的控制器所实现的一个 Object。 例如 ReplicaSetController 实现了 ReplicaSet,这个事件被触发是因为控制器对 ReplicaSet 对象做了一些变化。

  • related (ObjectReference)

    related 是用于更复杂操作的、可选的、从属性的对象。例如,当 regarding 对象触发 related 对象的创建或删除时。

  • reportingController (string)

    reportingController 是触发该事件的控制器的名称,例如 kubernetes.io/kubelet。对于新的 Event,此字段不能为空。

  • reportingInstance (string)

    reportingInstance 为控制器实例的 ID,例如 kubelet-xyzf。对于新的 Event,此字段不能为空,且最多为 128 个字符。

  • series (EventSeries)

    series 是该事件代表的事件系列的数据,如果是单事件,则为 nil。

    EventSeries 包含一系列事件的信息,即一段时间内持续发生的事情。 EventSeries 的更新频率由事件报告者决定。 默认事件报告程序在 "k8s.io/client-go/tools/events/event_broadcaster.go" 展示在发生心跳时该结构如何被更新,可以指导定制的报告者实现。

    • series.count (int32),必需

      count 是到最后一次心跳时间为止在该系列中出现的次数。

    • series.lastObservedTime (MicroTime),必需

      lastObservedTime 是在最后一次心跳时间之前看到最后一个 Event 的时间。

      MicroTime 是微秒级精度的 Time 版本。

  • type (string)

    type 是该事件的类型(Normal、Warning),未来可能会添加新的类型。字段值是机器可读的。 对于新的 Event,此字段不能为空。

EventList

EventList 是一个 Event 对象列表。


  • items ([]Event),必需

    items 是模式(Schema)对象的列表。

操作


get 读取特定 Event

HTTP 请求

GET /apis/events.k8s.io/v1/namespaces/{namespace}/events/{name}

参数

  • name (路径参数):string,必需

    Event 名称

  • namespace (路径参数):string,必需

    namespace

  • pretty (路径参数):string

    pretty

响应

200 (Event): OK

401: Unauthorized

list 列出或观察事件类型对象

HTTP 请求

GET /apis/events.k8s.io/v1/namespaces/{namespace}/events

参数

  • namespace (路径参数):string,必需

    namespace

  • continue (查询参数):string

    continue

  • limit (查询参数):integer

    limit

  • pretty (查询参数):string

    pretty

  • watch (查询参数):boolean

    watch

响应

200 (EventList): OK

401: Unauthorized

list 列出或观察事件类型对象

HTTP 请求

GET /apis/events.k8s.io/v1/events

参数

  • continue (查询参数):string

    continue

  • limit (查询参数):integer

    limit

  • pretty (查询参数):string

    pretty

  • watch (查询参数):boolean

    watch

响应

200 (EventList): OK

401: Unauthorized

create 创建一个 Event

HTTP 请求

POST /apis/events.k8s.io/v1/namespaces/{namespace}/events

参数

  • namespace (查询参数):string,必需

    namespace

  • dryRun (查询参数):string

    dryRun

  • pretty (查询参数):string

    pretty

响应

200 (Event): OK

201 (Event): Created

202 (Event): Accepted

401: Unauthorized

update 替换指定 Event

HTTP 请求

PUT /apis/events.k8s.io/v1/namespaces/{namespace}/events/{name}

参数

  • name (路径参数):string,必需

    Event 名称

  • namespace (路径参数):string,必需

    namespace

  • dryRun (查询参数):必需

    dryRun

  • pretty (查询参数):string

    pretty

响应

200 (Event): OK

201 (Event): Created

401: Unauthorized

patch 部分更新指定的 Event

HTTP 请求

PATCH /apis/events.k8s.io/v1/namespaces/{namespace}/events/{name}

参数

  • name (路径参数):string,必需

    Event 名称

  • namespace (路径参数):string,必需

    namespace

  • dryRun (查询参数):string

    dryRun

  • force (查询参数):boolean

    force

  • pretty (查询参数):string

    pretty

响应

200 (Event): OK

201 (Event): Created

401: Unauthorized

delete 删除 Event

HTTP 请求

DELETE /apis/events.k8s.io/v1/namespaces/{namespace}/events/{name}

参数

  • name (路径参数):string,必需

    Event 名称

  • dryRun (查询参数):string

    dryRun

  • pretty (查询参数):string

    pretty

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除 Event 集合

HTTP 请求

DELETE /apis/events.k8s.io/v1/namespaces/{namespace}/events

参数

  • continue (查询参数):string

    continue

  • dryRun (查询参数):string

    dryRun

  • limit (查询参数):integer

    limit

  • pretty (查询参数):string

    pretty

响应

200 (Status): OK

401: Unauthorized

6.5.8.4 - APIService

APIService 是用来表示一个特定的 GroupVersion 的服务器

apiVersion: apiregistration.k8s.io/v1

import "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"

APIService

APIService 是用来表示一个特定的 GroupVersion 的服务器。名称必须为 "version.group"。


APIServiceSpec

APIServiceSpec 包含用于定位和与服务器通信的信息。仅支持 HTTPS 协议,但是你可以禁用证书验证。


  • groupPriorityMinimum (int32), 必需

    groupPriorityMininum 是这个组至少应该具有的优先级。优先级高表示客户端优先选择该组。 请注意,该组的其他版本可能会指定更高的 groupPriorityMininum 值,使得整个组获得更高的优先级。 主排序基于 groupPriorityMinimum 值,从高到低排序(20 在 10 之前)。 次要排序基于对象名称的字母顺序(v1.bar 在 v1.foo 之前)。 我们建议这样配置:*.k8s.io(扩展除外)值设置为 18000,PaaS(OpenShift、Deis)建议值为 2000 左右。

  • versionPriority (int32), 必需

    versionPriority 控制该 API 版本在其组中的排序,必须大于零。主排序基于 versionPriority, 从高到低排序(20 在 10 之前)。因为在同一个组里,这个数字可以很小,可能是几十。 在版本优先级相等的情况下,版本字符串将被用来计算组内的顺序。如果版本字符串是与 Kubernetes 的版本号形式类似, 则它将排序在 Kubernetes 形式版本字符串之前。Kubernetes 的版本号字符串按字典顺序排列。 Kubernetes 版本号以 “v” 字符开头,后面是一个数字(主版本),然后是可选字符串 “alpha” 或 “beta” 和另一个数字(次要版本)。 它们首先按 GA > beta > alpha 排序(其中 GA 是没有 beta 或 alpha 等后缀的版本),然后比较主要版本, 最后是比较次要版本。版本排序列表示例:v10、v2、v1、v11beta2、v10beta3、v3beta1、v12alpha1、v11alpha2、foo1、foo10。

  • caBundle ([]byte)

    原子性:将在合并期间被替换

    caBundle 是一个 PEM 编码的 CA 包,用于验证 API 服务器的服务证书。如果未指定, 则使用 API 服务器上的系统根证书。

  • group (string)

    group 是此服务器主机的 API 组名称。

  • insecureSkipTLSVerify (boolean)

    insecureSkipTLSVerify 代表在与此服务器通信时禁用 TLS 证书验证。强烈建议不要这样做。你应该使用 caBundle。

  • service (ServiceReference)

    service 是对该 API 服务器的服务的引用。它只能在端口 443 上通信。如果 service 是 nil, 则意味着 API groupversion 的处理是在当前服务器上本地处理的。服务调用被直接委托给正常的处理程序链来完成。

    ServiceReference 保存对 Service.legacy.k8s.io 的一个引用。

    • service.name (string)

      name 是服务的名称
      
    • service.namespace (string)

      namespace 是服务的命名空间
      
    • service.port (int32)

      如果指定,则为托管 Webhook 的服务上的端口。为实现向后兼容,默认端口号为 443。
      

      port 应该是一个有效的端口号(1-65535,包含)。

  • version (string)

    version 是此服务器的 API 版本。例如:“v1”。

APIServiceStatus

APIServiceStatus 包含有关 API 服务器的派生信息


  • conditions ([]APIServiceCondition)

    补丁策略:基于键 type 合并

    Map:合并时将保留 type 键的唯一值

    APIService 的当前服务状态。

    APIServiceCondition 描述 APIService 在特定点的状态

    • conditions.status (string), 必需

      status 表示状况(Condition)的状态,取值为 True、False 或 Unknown 之一。
      
    • conditions.type (string), 必需

      type 是状况的类型。
      
    • conditions.lastTransitionTime (Time)

      上一次发生状况状态转换的时间。
      

      Time 是对 time.Time 的封装。Time 支持对 YAML 和 JSON 进行正确封包。为 time 包的许多函数方法提供了封装器。
      
    • conditions.message (string)

      指示上次转换的详细可读信息。  
      
    • conditions.reason (string)

      表述状况上次转换原因的、驼峰格式命名的、唯一的一个词。
      

APIServiceList

APIServiceList 是 APIService 对象的列表。


Operations


get 读取指定的 APIService

HTTP 请求

GET /apis/apiregistration.k8s.io/v1/apiservices/{name}

参数

  • name路径参数):string,必需

    APIService 名称

  • pretty查询参数):string

    pretty

响应

200 (APIService): OK

401: Unauthorized

get 读取指定 APIService 的状态

HTTP 请求

GET /apis/apiregistration.k8s.io/v1/apiservices/{name}/status

参数

  • name路径参数):string,必需

    APIService 名称

  • pretty查询参数):string

    pretty

响应

200 (APIService): OK

401: Unauthorized

list 列出或观察 APIService 类的对象

HTTP 请求

GET /apis/apiregistration.k8s.io/v1/apiservices

参数

响应

200 (APIServiceList): OK

401: Unauthorized

create 创建一个 APIService

HTTP 请求

POST /apis/apiregistration.k8s.io/v1/apiservices

参数

响应

200 (APIService): OK

201 (APIService): Created

202 (APIService): Accepted

401: Unauthorized

update 替换指定的 APIService

HTTP 请求

PUT /apis/apiregistration.k8s.io/v1/apiservices/{name}

参数

  • name路径参数):string, 必需

    APIService 名称

  • bodyAPIService, 必需

  • dryRun查询参数):string

    dryRun

  • fieldManager查询参数):string

    fieldManager

  • fieldValidation查询参数):string

    fieldValidation

  • pretty查询参数):string

    pretty

响应

200 (APIService): OK

201 (APIService): Created

401: Unauthorized

update 替换指定 APIService 的 status

HTTP 请求

PUT /apis/apiregistration.k8s.io/v1/apiservices/{name}/status

参数

  • name路径参数):string, 必需

    APIService 名称

  • bodyAPIService, 必需

  • dryRun查询参数):string

    dryRun

  • fieldManager查询参数):string

    fieldManager

  • fieldValidation查询参数):string

    fieldValidation

  • pretty查询参数):string

    pretty

响应

200 (APIService): OK

201 (APIService): Created

401: Unauthorized

patch 部分更新指定的 APIService

HTTP 请求

PATCH /apis/apiregistration.k8s.io/v1/apiservices/{name}

参数

  • name路径参数):string, 必需

    APIService 名称

  • bodyPatch, 必需

  • dryRun查询参数):string

    dryRun

  • fieldManager查询参数):string

    fieldManager

  • fieldValidation查询参数):string

    fieldValidation

  • force查询参数):boolean

    force

  • pretty查询参数):string

    pretty

响应

200 (APIService): OK

201 (APIService): Created

401: Unauthorized

patch 部分更新指定 APIService 的 status

HTTP 请求

PATCH /apis/apiregistration.k8s.io/v1/apiservices/{name}/status

参数

  • name路径参数):string, 必需

    APIService 名称

  • bodyPatch, 必需

  • dryRun查询参数):string

    dryRun

  • fieldManager查询参数):string

    fieldManager

  • fieldValidation查询参数):string

    fieldValidation

  • force查询参数):boolean

    force

  • pretty查询参数):string

    pretty

响应

200 (APIService): OK

201 (APIService): Created

401: Unauthorized

delete 删除一个 APIService

HTTP 请求

DELETE /apis/apiregistration.k8s.io/v1/apiservices/{name}

参数

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除 APIService 集合

HTTP 请求

DELETE /apis/apiregistration.k8s.io/v1/apiservices

参数

响应

200 (Status): OK

401: Unauthorized

6.5.8.5 - Lease

Lease 定义了租约的概念。

apiVersion: coordination.k8s.io/v1

import "k8s.io/api/coordination/v1"

Lease

Lease 定义了租约的概念。


LeaseSpec

LeaseSpec 是一个 Lease 的规约。


  • acquireTime (MicroTime)

    acquireTime 是当前租约被获取的时间。

    MicroTime 是微秒级精确时间的版本。

  • holderIdentity (string)

    holderIdentity 包含当前租约持有人的身份。

  • leaseDurationSeconds (int32)

    leaseDurationSeconds 是租约候选人需要等待强制获取租约的持续时间。这是相对于上次观察到的 renewTime 的度量。

  • leaseTransitions (int32)

    leaseTransitions 是租约持有人之间的转换次数。

  • renewTime (MicroTime)

    renewTime 是当前租约持有人上次更新租约的时间。

    MicroTime 是具有微秒级精度的时间版本。

LeaseList

LeaseList 是 Lease 对象的列表。


操作


get 读取指定的 Lease

HTTP 请求

GET /apis/coordination.k8s.io/v1/namespaces/{namespace}/leases/{name}

参数

  • name (路径参数): string, 必需

    Lease 名称。

  • namespace (路径参数): string, 必需

    namespace

  • pretty (查询参数): string

    pretty

响应

200 (Lease): OK

401: Unauthorized

list 列出或监视 Lease 类对象

HTTP 请求

GET /apis/coordination.k8s.io/v1/namespaces/{namespace}/leases

参数

响应

200 (LeaseList): OK

401: Unauthorized

list 列出或监视 Lease 类对象

HTTP 请求

GET /apis/coordination.k8s.io/v1/leases

参数

响应

200 (LeaseList): OK

401: Unauthorized

create 创建 Lease

HTTP 请求

POST /apis/coordination.k8s.io/v1/namespaces/{namespace}/leases

参数

响应

200 (Lease): OK

201 (Lease): Created

202 (Lease): Accepted

401: Unauthorized

update 替换指定的 Lease

HTTP 请求

PUT /apis/coordination.k8s.io/v1/namespaces/{namespace}/leases/{name}

参数

  • name (路径参数): string, 必需

    Lease 的名称。

  • namespace (路径参数): string, 必需

    namespace

  • body: Lease, 必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • pretty (查询参数): string

    pretty

响应

200 (Lease): OK

201 (Lease): Created

401: Unauthorized

patch 部分更新指定的 Lease

HTTP 请求

PATCH /apis/coordination.k8s.io/v1/namespaces/{namespace}/leases/{name}

参数

  • name (路径参数): string, 必需

    Lease 的名称。

  • namespace (路径参数): string, 必需

    namespace

  • body: Patch, 必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • force (查询参数): boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (Lease): OK

201 (Lease): Created

401: Unauthorized

delete 删除一个 Lease

HTTP 请求

DELETE /apis/coordination.k8s.io/v1/namespaces/{namespace}/leases/{name}

参数

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除 Lease 收款

HTTP 请求

DELETE /apis/coordination.k8s.io/v1/namespaces/{namespace}/leases

参数

响应

200 (Status): OK

401: Unauthorized

6.5.8.6 - RuntimeClass

RuntimeClass 定义集群中支持的容器运行时类。

apiVersion: node.k8s.io/v1

import "k8s.io/api/node/v1"

RuntimeClass

RuntimeClass 定义集群中支持的容器运行时类。 RuntimeClass 用于确定哪个容器运行时用于运行某 Pod 中的所有容器。 RuntimeClass 由用户或集群制备程序手动定义,并在 PodSpec 中引用。 Kubelet 负责在运行 Pod 之前解析 RuntimeClassName 引用。 有关更多详细信息,请参阅 https://kubernetes.io/zh-cn/docs/concepts/containers/runtime-class/


  • apiVersion: node.k8s.io/v1

  • kind: RuntimeClass

  • metadata (ObjectMeta)

    更多信息: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata

  • handler (string),必需

    handler 指定底层运行时和配置,在 CRI 实现过程中将使用这些运行时和配置来处理这个类的 Pod。 可能的值特定于节点和 CRI 配置。 假定所有 handler 可用于每个节点上,且同一名称的 handler 在所有节点上是等效的。 例如,一个名为 “runc” 的 handler 可能指定 runc OCI 运行时将(使用原生 Linux 容器) 用于运行 Pod 中的容器。该 handler 必须采用小写,遵从 DNS Label (RFC 1123) 要求,且是不可变更的。

  • scheduling (Scheduling)

    scheduling 包含调度约束,这些约束用来确保以这个 RuntimeClass 运行的 Pod 被调度到支持此运行时类的节点。 如果 scheduling 设为空,则假定所有节点支持此 RuntimeClass。

    Scheduling 指定支持 RuntimeClass 的节点的调度约束。

    • scheduling.nodeSelector (map[string]string)

      nodeSelector 列出支持此 RuntimeClass 的节点上必须存在的标签。 使用此 RuntimeClass 的 Pod 只能调度到与这个选择算符匹配的节点上。 RuntimeClass nodeSelector 与 Pod 现有的 nodeSelector 合并。 任何冲突均会使得该 Pod 在准入时被拒绝。

    • scheduling.tolerations ([]Toleration)

      原子性:将在合并期间被替换

      tolerations 在准入期间追加到以此 RuntimeClass 运行的 Pod(不包括重复项)上, 本质上是求取 Pod 和 RuntimeClass 所容忍的节点并集。

      附加此容忍度的 Pod 将容忍用匹配运算符 operator 运算后与三元组 <key,value,effect> 匹配的任何污点。

      • scheduling.tolerations.key (string)

        key 是容忍度所应用到的污点键。空意味着匹配所有污点键。 如果键为空,则运算符必须为 Exists;这个组合意味着匹配所有值和所有键。

      • scheduling.tolerations.operator (string)

        operator 表示一个键与值的关系。有效的运算符为 Exists 和 Equal。默认为 Equal。 Exists 等价于将值设置为通配符的情况,因此一个 Pod 可以容忍特定类别的所有污点。

      • scheduling.tolerations.value (string)

        value 是容忍度匹配到的污点值。如果运算符为 Exists,则值应为空,否则就是一个普通字符串。

      • scheduling.tolerations.effect (string)

        effect 表示匹配度污点效果。空意味着匹配所有污点效果。 当指定值时,允许的值为 NoSchedule、PreferNoSchedule 或 NoExecute。

      • scheduling.tolerations.tolerationSeconds (int64)

        tolerationSeconds 表示容忍度容忍污点的时间段(必须是 NoExecute 的效果,否则忽略此字段)。 默认情况下,不设置此字段,这意味着永远容忍污点(不驱逐)。零和负值将被系统视为 0(立即驱逐)。

RuntimeClassList

RuntimeClassList 是 RuntimeClass 对象的列表。


  • apiVersion: node.k8s.io/v1

  • kind: RuntimeClassList

操作


get 读取指定的 RuntimeClass

HTTP 请求

GET /apis/node.k8s.io/v1/runtimeclasses/{name}

参数
  • name (路径参数): string,必需

    RuntimeClass 的名称

  • pretty (查询参数): string

    pretty

响应

200 (RuntimeClass): OK

401: Unauthorized

list 列出或监视 RuntimeClass 类别的对象

HTTP 请求

GET /apis/node.k8s.io/v1/runtimeclasses

参数

响应

200 (RuntimeClassList): OK

401: Unauthorized

create 创建 RuntimeClass

HTTP 请求

POST /apis/node.k8s.io/v1/runtimeclasses

参数

响应

200 (RuntimeClass): OK

201 (RuntimeClass): Created

202 (RuntimeClass): Accepted

401: Unauthorized

update 替换指定的 RuntimeClass

HTTP 请求

PUT /apis/node.k8s.io/v1/runtimeclasses/{name}

参数
  • name (路径参数): string,必需

    RuntimeClass 的名称

  • body: RuntimeClass,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • pretty (查询参数): string

    pretty

响应

200 (RuntimeClass): OK

201 (RuntimeClass): Created

401: Unauthorized

patch 部分更新指定的 RuntimeClass

HTTP 请求

PATCH /apis/node.k8s.io/v1/runtimeclasses/{name}

参数
  • name (路径参数): string,必需

    RuntimeClass 的名称

  • body: Patch,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • force (查询参数): boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (RuntimeClass): OK

201 (RuntimeClass): Created

401: Unauthorized

delete 删除 RuntimeClass

HTTP 请求

DELETE /apis/node.k8s.io/v1/runtimeclasses/{name}

参数

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除 RuntimeClass 的集合

HTTP 请求

DELETE /apis/node.k8s.io/v1/runtimeclasses

参数

响应

200 (Status): OK

401: Unauthorized

6.5.8.7 - FlowSchema v1beta3

FlowSchema 定义一组流的模式。

apiVersion: flowcontrol.apiserver.k8s.io/v1beta3

import "k8s.io/api/flowcontrol/v1beta3"

FlowSchema

FlowSchema 定义一组流的模式。请注意,一个流由属性类似的一组入站 API 请求组成, 用一对字符串进行标识:FlowSchema 的名称和一个 “流区分项”。


  • apiVersion: flowcontrol.apiserver.k8s.io/v1beta3

  • kind: FlowSchema

FlowSchemaSpec

FlowSchemaSpec 描述 FlowSchema 的规约看起来是怎样的。


  • priorityLevelConfiguration (PriorityLevelConfigurationReference),必需

    priorityLevelConfiguration 应引用集群中的 PriorityLevelConfiguration。 如果此应用无法被解析,则忽略此 FlowSchema,并在其状态中将其标记为无效。必需。

    PriorityLevelConfigurationReference 包含指向正被使用的 “request-priority” 的信息。

    • priorityLevelConfiguration.name (string),必需

      name 是正被引用的优先级配置的名称。必需。

  • distinguisherMethod (FlowDistinguisherMethod)

    distinguisherMethod 定义如何为匹配此模式的请求来计算流区分项。 nil 表示该区分项被禁用,且因此将始终为空字符串。

    FlowDistinguisherMethod 指定流区分项的方法。

    • distinguisherMethod.type (string),必需

      type 是流区分项的类型。支持的类型为 “ByUser” 和 “ByNamespace”。必需。

  • matchingPrecedence (int32)

    matchingPrecedence 用于选择与给定请求匹配的一个 FlowSchema。 选中的 FlowSchema 是某个 MatchingPrecedence 数值最小(我们视其为逻辑上最大)的 FlowSchema。 每个 MatchingPrecedence 值必须在 [1,10000] 的范围内。 请注意,如果未指定优先顺序,则其默认设为 1000。

  • rules ([]PolicyRulesWithSubjects)

    原子性:将在合并期间被替换

    rules 描述哪些请求将与这个流模式匹配。只有当至少一条规则与请求匹配时, 才视为此 FlowSchema 与该请求匹配。如果字段值为空表,则 FlowSchema 不会与任何请求匹配。

    PolicyRulesWithSubjects 给出针对 API 服务器请求的一个测试。 该测试将检查发出请求的主体、所请求的动作和要操作的资源。 只有同时满足以下两个条件时,才表示此 PolicyRulesWithSubjects 与请求匹配: (a) 至少一个主体成员与请求匹配且 (b) 至少 resourceRules 或 nonResourceRules 的一个成员与请求匹配。

    • rules.subjects ([]Subject),必需

      原子性:将在合并期间被替换

      subjects 是此规则相关的普通用户、服务账号或组的列表。在这个列表中必须至少有一个成员。 同时包含 system:authenticated 和 system:unauthenticated 用户组的列表会与每个请求匹配。 此字段为必需。

      Subject 用来与匹配请求的发起方,请求的发起方由请求身份认证系统识别出来。 有三种方式来匹配一个发起方:按用户、按组或按服务账号。

      • rules.subjects.kind (string),必需

        kind 标示其他字段中的哪个字段必须非空。必需。

      • rules.subjects.group (GroupSubject)

        group 根据用户组名称进行匹配。

        GroupSubject 保存组类别主体的详细信息。

      • rules.subjects.serviceAccount (ServiceAccountSubject)

        serviceAccount 与 ServiceAccount 对象进行匹配。

        ServiceAccountSubject 保存服务账号类别主体的详细信息。

        • rules.subjects.serviceAccount.name (string),必需

          name 是要匹配的 ServiceAccount 对象的名称,可使用 * 匹配所有名称。必需。

        • rules.subjects.serviceAccount.namespace (string),必需

          namespace 是要匹配的 ServiceAccount 对象的名字空间。必需。

      • rules.subjects.user (UserSubject)

        user 根据用户名进行匹配。

        UserSubject 保存用户类别主体的详细信息。

        • rules.subjects.user.name (string),必需

          name 是要匹配的用户名,可使用 * 匹配所有用户名。必需。

    • rules.nonResourceRules ([]NonResourcePolicyRule)

      原子性:将在合并期间被替换

      nonResourceRules 是由 NonResourcePolicyRule 对象构成的列表, 根据请求的动作和目标非资源 URL 来识别匹配的请求。

      NonResourcePolicyRule 是根据请求的动作和目标非资源 URL 来匹配非资源请求的一种规则。 只有满足以下两个条件时,NonResourcePolicyRule 才会匹配一个请求: (a) 至少 verbs 的一个成员与请求匹配且 (b) 至少 nonResourceURLs 的一个成员与请求匹配。

      • rules.nonResourceRules.nonResourceURLs ([]string),必需

        集合:合并期间保留唯一值

        nonResourceURLs 是用户有权访问的一组 URL 前缀,不可以为空。 此字段为必需设置的字段。例如:

        • "/healthz" 是合法的
        • "/hea*" 是不合法的
        • "/hea" 是合法的但不会与任何项匹配
        • "/hea/*" 也不会与任何项匹配
        • "/healthz/*" 与所有组件自身的的健康检查路径匹配。 * 与所有非资源 URL 匹配。如果存在此字符,则必须是唯一的输入项。 必需。
      • rules.nonResourceRules.verbs ([]string),必需

        集合:在合并期间保留唯一值

        verbs 是与动作匹配的列表,不可以为空。* 与所有动作匹配。 如果存在此字符,则必须是唯一的输入项。必需。

    • rules.resourceRules ([]ResourcePolicyRule)

      原子性:将在合并期间被替换

      resourceRules 是 ResourcePolicyRule 对象的列表,根据请求的动作和目标资源识别匹配的请求。 resourceRulesnonResourceRules 两者必须至少有一个非空。

      ResourcePolicyRule 是用来匹配资源请求的规则,对请求的动作和目标资源进行测试。 只有满足以下条件时,ResourcePolicyRule 才会与某个资源请求匹配: (a) 至少 verbs 的一个成员与请求的动作匹配, (b) 至少 apiGroups 的一个成员与请求的 API 组匹配, (c) 至少 resources 的一个成员与请求的资源匹配, (d) 要么 (d1) 请求未指定一个名字空间(即,namespace=="")且 clusterScope 为 true, 要么 (d2) 请求指定了一个名字空间,且至少 namespaces 的一个成员与请求的名字空间匹配。

      • rules.resourceRules.apiGroups ([]string),必需

        集合:合并期间保留唯一值

        apiGroups 是与 API 组匹配的列表,不可以为空。* 表示与所有 API 组匹配。 如果存在此字符,则其必须是唯一的条目。必需。

      • rules.resourceRules.resources ([]string),必需

        集合:合并期间保留唯一值

        resources 是匹配的资源(即小写和复数)的列表,如果需要的话,还可以包括子资源。 例如 [ "services", "nodes/status" ]。此列表不可以为空。 * 表示与所有资源匹配。如果存在此字符,则必须是唯一的条目。必需。

      • rules.resourceRules.verbs ([]string),必需

        集合:合并期间保留唯一值

        verbs 是匹配的动作的列表,不可以为空。* 表示与所有动作匹配。 如果存在此字符,则必须是唯一的条目。必需。

      • rules.resourceRules.clusterScope (boolean)

        clusterScope 表示是否与未指定名字空间 (出现这种情况的原因是该资源没有名字空间或请求目标面向所有名字空间)的请求匹配。 如果此字段被省略或为 false,则 namespaces 字段必须包含一个非空的列表。

      • rules.resourceRules.namespaces ([]string)

        集合:合并期间保留唯一值

        namespaces 是限制匹配的目标的名字空间的列表。 指定一个目标名字空间的请求只会在以下某一个情况满足时进行匹配: (a) 此列表包含该目标名字空间或 (b) 此列表包含 *。 请注意,* 与所指定的任何名字空间匹配,但不会与未指定 名字空间的请求匹配 (请参阅 clusterScope 字段)。此列表可以为空,但前提是 clusterScope 为 true。

FlowSchemaStatus

FlowSchemaStatus 表示 FlowSchema 的当前状态。


  • conditions ([]FlowSchemaCondition)

    补丁策略:根据键 type 合并

    Map:合并期间保留根据键 type 保留其唯一值

    conditions 是 FlowSchema 当前状况的列表。

    FlowSchemaCondition 描述 FlowSchema 的状况。

    • conditions.lastTransitionTime (Time)

      lastTransitionTime 是状况上一次从一个状态转换为另一个状态的时间。

      Time 是对 time.Time 的封装。Time 支持对 YAML 和 JSON 进行正确封包。 为 time 包的许多函数方法提供了封装器。

    • conditions.message (string)

      message 是一条人类可读的消息,表示上一次转换有关的详细信息。

    • conditions.reason (string)

      reason 是状况上次转换原因的、驼峰格式命名的、唯一的一个词。

    • conditions.status (string)

      status 是状况的状态。可以是 True、False、Unknown。必需。

    • conditions.type (string)

      type 是状况的类型。必需。

FlowSchemaList

FlowSchemaList 是 FlowSchema 对象的列表。


  • apiVersion: flowcontrol.apiserver.k8s.io/v1beta3

  • kind: FlowSchemaList

操作


get 读取指定的 FlowSchema

HTTP 请求

GET /apis/flowcontrol.apiserver.k8s.io/v1beta3/flowschemas/{name}

参数

  • name (路径参数): string,必需

    FlowSchema 的名称

  • pretty (查询参数): string

    pretty

响应

200 (FlowSchema): OK

401: Unauthorized

get 读取指定 FlowSchema 的状态

HTTP 请求

GET /apis/flowcontrol.apiserver.k8s.io/v1beta3/flowschemas/{name}/status

参数

  • name (路径参数): string,必需

    FlowSchema 的名称

  • pretty (查询参数): string

    pretty

响应

200 (FlowSchema): OK

401: Unauthorized

list 列出或监视 FlowSchema 类别的对象

HTTP 请求

GET /apis/flowcontrol.apiserver.k8s.io/v1beta3/flowschemas

参数

响应

200 (FlowSchemaList): OK

401: Unauthorized

create 创建 FlowSchema

HTTP 请求

POST /apis/flowcontrol.apiserver.k8s.io/v1beta3/flowschemas

参数

响应

200 (FlowSchema): OK

201 (FlowSchema): Created

202 (FlowSchema): Accepted

401: Unauthorized

update 替换指定的 FlowSchema

HTTP 请求

PUT /apis/flowcontrol.apiserver.k8s.io/v1beta3/flowschemas/{name}

参数

  • name (路径参数): string,必需

    FlowSchema 的名称

  • body: FlowSchema,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • pretty (查询参数): string

    pretty

响应

200 (FlowSchema): OK

201 (FlowSchema): Created

401: Unauthorized

update 替换指定的 FlowSchema 的状态

HTTP 请求

PUT /apis/flowcontrol.apiserver.k8s.io/v1beta3/flowschemas/{name}/status

参数

  • name (路径参数): string,必需

    FlowSchema 的名称

  • body: FlowSchema,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • pretty (查询参数): string

    pretty

响应

200 (FlowSchema): OK

201 (FlowSchema): Created

401: Unauthorized

patch 部分更新指定的 FlowSchema

HTTP 请求

PATCH /apis/flowcontrol.apiserver.k8s.io/v1beta3/flowschemas/{name}

参数

  • name (路径参数): string,必需

    FlowSchema 的名称

  • body: Patch,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • force (查询参数): boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (FlowSchema): OK

201 (FlowSchema): Created

401: Unauthorized

patch 部分更新指定的 FlowSchema 的状态

HTTP 请求

PATCH /apis/flowcontrol.apiserver.k8s.io/v1beta3/flowschemas/{name}/status

参数

  • name (路径参数): string,必需

    FlowSchema 的名称

  • body: Patch,必需

  • dryRun (查询参数): string

    dryRun

  • fieldManager (查询参数): string

    fieldManager

  • fieldValidation (查询参数): string

    fieldValidation

  • force (查询参数): boolean

    force

  • pretty (查询参数): string

    pretty

响应

200 (FlowSchema): OK

201 (FlowSchema): Created

401: Unauthorized

delete 删除 FlowSchema

HTTP 请求

DELETE /apis/flowcontrol.apiserver.k8s.io/v1beta3/flowschemas/{name}

参数

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除 FlowSchema 的集合

HTTP 请求

DELETE /apis/flowcontrol.apiserver.k8s.io/v1beta3/flowschemas

参数

响应

200 (Status): OK

401: Unauthorized

6.5.8.8 - PriorityLevelConfiguration v1beta3

PriorityLevelConfiguration 表示一个优先级的配置。

apiVersion: flowcontrol.apiserver.k8s.io/v1beta3

import "k8s.io/api/flowcontrol/v1beta3"

PriorityLevelConfiguration

PriorityLevelConfiguration 表示一个优先级的配置。


  • apiVersion: flowcontrol.apiserver.k8s.io/v1beta3

  • kind: PriorityLevelConfiguration

PriorityLevelConfigurationSpec

PriorityLevelConfigurationSpec 指定一个优先级的配置。


  • type (string),必需

    type 指示此优先级是否遵从有关请求执行的限制。 取值为 "Exempt" 意味着此优先级的请求不遵从某个限制(且因此从不排队)且不会减损其他优先级可用的容量。 取值为 "Limited" 意味着 (a) 此优先级的请求遵从这些限制且 (b) 服务器某些受限的容量仅可用于此优先级。必需。

  • exempt (ExemptPriorityLevelConfiguration)

    exempt 指定了对于豁免优先级的请求如何处理。 如果 type 取值为 "Limited",则此字段必须为空。 如果 type 取值为 "Exempt",则此字段可以非空。 如果为空且 type 取值为 "Exempt",则应用 ExemptPriorityLevelConfiguration 的默认值。

    ExemptPriorityLevelConfiguration 描述豁免请求处理的可配置方面。 在强制豁免配置对象中,与 spec 中的其余部分不同,此处字段的取值可以被授权用户修改。

    • exempt.lendablePercent (int32)

      lendablePercent 规定该级别的 NominalCL 可被其他优先级租借的百分比。 此字段的值必须在 0 到 100 之间,包括 0 和 100,默认为 0。 其他级别可以从该级别借用的席位数被称为此级别的 LendableConcurrencyLimit(LendableCL),定义如下。

      LendableCL(i) = round( NominalCL(i) * lendablePercent(i)/100.0 )

    • exempt.nominalConcurrencyShares (int32)

      nominalConcurrencyShares(NCS)也被用来计算该级别的 NominalConcurrencyLimit(NominalCL)。 字段值是为该优先级保留的执行席位的数量。这一设置不限制此优先级别的调度行为, 但会通过借用机制影响其他优先级。服务器的并发限制(ServerCL)会按照各个优先级的 NCS 值按比例分配:

      NominalCL(i) = ceil( ServerCL * NCS(i) / sum_ncs ) sum_ncs = sum[priority level k] NCS(k)

      较大的数字意味着更大的标称并发限制,且将影响其他优先级。此字段的默认值为零。

  • limited (LimitedPriorityLevelConfiguration)

    limited 指定如何为某个受限的优先级处理请求。 当且仅当 type"Limited" 时,此字段必须为非空。

    LimitedPriorityLevelConfiguration 指定如何处理需要被限制的请求。它解决两个问题:

    • 如何限制此优先级的请求?
    • 应如何处理超出此限制的请求?
    • limited.borrowingLimitPercent (int32)

      borrowingLimitPercent 配置如果存在,则可用来限制此优先级可以从其他优先级中租借多少资源。 该限制被称为该级别的 BorrowingConcurrencyLimit(BorrowingCL),它限制了该级别可以同时租借的资源总数。 该字段保存了该限制与该级别标称并发限制之比。当此字段非空时,必须为正整数,并按以下方式计算限制值:

      BorrowingCL(i) = round(NominalCL(i) * borrowingLimitPercent(i) / 100.0)

      该字段值可以大于100,表示该优先级可以大于自己标称并发限制(NominalCL)。当此字段为 nil 时,表示无限制。

    • limited.lendablePercent (int32)

      lendablePercent 规定了 NominalCL 可被其他优先级租借资源数百分比。 此字段的值必须在 0 到 100 之间,包括 0 和 100,默认为 0。 其他级别可以从该级别借用的资源数被称为此级别的 LendableConcurrencyLimit(LendableCL),定义如下。

      LendableCL(i) = round( NominalCL(i) * lendablePercent(i)/100.0 )

    • limited.limitResponse (LimitResponse)

      limitResponse 指示如何处理当前无法立即执行的请求。

      LimitResponse 定义如何处理当前无法立即执行的请求。

      • limited.limitResponse.type (string),必需

        type 是 “Queue” 或 “Reject”。此字段必须设置。 “Queue” 意味着在到达时无法被执行的请求可以被放到队列中,直到它们被执行或者队列长度超出限制为止。 “Reject” 意味着到达时无法执行的请求将被拒绝。

      • limited.limitResponse.queuing (QueuingConfiguration)

        queuing 包含排队所用的配置参数。只有 type"Queue" 时,此字段才可以为非空。

        QueuingConfiguration 保存排队所用的配置参数。

        • limited.limitResponse.queuing.handSize (int32)

          handSize 是一个小的正数,用于配置如何将请求随机分片到队列中。 当以该优先级将请求排队时,将对请求的流标识符(字符串对)进行哈希计算, 该哈希值用于打乱队列队列的列表,并处理此处指定的一批请求。 请求被放入这一批次中最短的队列中。 handSize 不得大于 queues,并且应该明显更小(以便几个大的流量不会使大多数队列饱和)。 有关设置此字段的更多详细指导,请参阅面向用户的文档。此字段的默认值为 8。

        • limited.limitResponse.queuing.queueLengthLimit (int32)

          queueLengthLimit 是任意时刻允许在此优先级的给定队列中等待的请求数上限; 额外的请求将被拒绝。 此值必须是正数。如果未指定,则默认为 50。

        • limited.limitResponse.queuing.queues (int32)

          queues 是这个优先级的队列数。此队列在每个 API 服务器上独立存在。此值必须是正数。 将其设置为 1 相当于禁止了混洗分片操作,进而使得对相关流模式的区分方法不再有意义。 此字段的默认值为 64。

    • limited.nominalConcurrencyShares (int32)

      nominalConcurrencyShares(NCS)用于计算该优先级的标称并发限制(NominalCL)。 NCS 表示可以在此优先级同时运行的席位数量上限,包括来自本优先级的请求, 以及从此优先级租借席位的其他级别的请求。 服务器的并发度限制(ServerCL)根据 NCS 值按比例分别给各 Limited 优先级:

      NominalCL(i) = ceil( ServerCL * NCS(i) / sum_ncs ) sum_ncs = sum[priority level k] NCS(k)

      较大的数字意味着更大的标称并发限制,但是这将牺牲其他优先级的资源。该字段的默认值为 30。

PriorityLevelConfigurationStatus

PriorityLevelConfigurationStatus 表示 “请求优先级” 的当前状况。


  • conditions ([]PriorityLevelConfigurationCondition)

    Map:合并期间保留根据键 type 保留其唯一值

    conditions 是 “请求优先级” 的当前状况。

    PriorityLevelConfigurationCondition 定义优先级的状况。

    • conditions.lastTransitionTime (Time)

      lastTransitionTime 是状况上次从一个状态转换为另一个状态的时间。

      Time 是对 time.Time 的封装。Time 支持对 YAML 和 JSON 进行正确封包。 为 time 包的许多函数方法提供了封装器。

    • conditions.message (string)

      message 是人类可读的消息,指示有关上次转换的详细信息。

    • conditions.reason (string)

      reason 是状况上次转换原因的、驼峰格式命名的、唯一的一个词。

    • conditions.status (string)

      status 表示状况的状态,取值为 True、False 或 Unknown 之一。必需。

    • conditions.type (string)

      type 表示状况的类型,必需。

PriorityLevelConfigurationList

PriorityLevelConfigurationList 是 PriorityLevelConfiguration 对象的列表。


  • apiVersion: flowcontrol.apiserver.k8s.io/v1beta3

  • kind: PriorityLevelConfigurationList

操作


get 读取指定的 PriorityLevelConfiguration

HTTP 请求

GET /apis/flowcontrol.apiserver.k8s.io/v1beta3/prioritylevelconfigurations/{name}

参数

  • name路径参数):string,必需

    PriorityLevelConfiguration 的名称。

  • pretty查询参数):string

    pretty

响应

200 (PriorityLevelConfiguration): OK

401: Unauthorized

get 读取指定的 PriorityLevelConfiguration 的状态

HTTP 请求

GET /apis/flowcontrol.apiserver.k8s.io/v1beta3/prioritylevelconfigurations/{name}/status

参数

  • name路径参数):string,必需

    PriorityLevelConfiguration 的名称。

  • pretty查询参数):string

    pretty

响应

200 (PriorityLevelConfiguration): OK

401: Unauthorized

list 列出或监视 PriorityLevelConfiguration 类别的对象

HTTP 请求

GET /apis/flowcontrol.apiserver.k8s.io/v1beta3/prioritylevelconfigurations

参数

响应

200 (PriorityLevelConfigurationList): OK

401: Unauthorized

create 创建 PriorityLevelConfiguration

HTTP 请求

POST /apis/flowcontrol.apiserver.k8s.io/v1beta3/prioritylevelconfigurations

参数

响应

200 (PriorityLevelConfiguration): OK

201 (PriorityLevelConfiguration): Created

202 (PriorityLevelConfiguration): Accepted

401: Unauthorized

update 替换指定的 PriorityLevelConfiguration

HTTP 请求

PUT /apis/flowcontrol.apiserver.k8s.io/v1beta3/prioritylevelconfigurations/{name}

参数

响应

200 (PriorityLevelConfiguration): OK

201 (PriorityLevelConfiguration): Created

401: Unauthorized

update 替换指定的 PriorityLevelConfiguration 的状态

HTTP 请求

PUT /apis/flowcontrol.apiserver.k8s.io/v1beta3/prioritylevelconfigurations/{name}/status

参数

响应

200 (PriorityLevelConfiguration): OK

201 (PriorityLevelConfiguration): Created

401: Unauthorized

patch 部分更新指定的 PriorityLevelConfiguration

HTTP 请求

PATCH /apis/flowcontrol.apiserver.k8s.io/v1beta3/prioritylevelconfigurations/{name}

参数

  • name路径参数):string,必需

    PriorityLevelConfiguration 的名称。

  • body: Patch,必需

  • dryRun查询参数):string

    dryRun

  • fieldManager查询参数):string

    fieldManager

  • fieldValidation查询参数):string

    fieldValidation

  • force查询参数):boolean

    force

  • pretty查询参数):string

    pretty

响应

200 (PriorityLevelConfiguration): OK

201 (PriorityLevelConfiguration): Created

401: Unauthorized

patch 部分更新指定的 PriorityLevelConfiguration 的状态

HTTP 请求

PATCH /apis/flowcontrol.apiserver.k8s.io/v1beta3/prioritylevelconfigurations/{name}/status

参数

  • name路径参数):string,必需

    PriorityLevelConfiguration 的名称。

  • body: Patch,必需

  • dryRun查询参数):string

    dryRun

  • fieldManager查询参数):string

    fieldManager

  • fieldValidation查询参数):string

    fieldValidation

  • force查询参数):boolean

    force

  • pretty查询参数):string

    pretty

响应

200 (PriorityLevelConfiguration): OK

201 (PriorityLevelConfiguration): Created

401: Unauthorized

delete 删除 PriorityLevelConfiguration

HTTP 请求

DELETE /apis/flowcontrol.apiserver.k8s.io/v1beta3/prioritylevelconfigurations/{name}

参数

  • name路径参数):string,必需

    PriorityLevelConfiguration 的名称。

  • body: DeleteOptions

  • dryRun查询参数):string

    dryRun

  • gracePeriodSeconds查询参数):integer

    gracePeriodSeconds

  • pretty查询参数):string

    pretty

  • propagationPolicy查询参数):string

    propagationPolicy

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除 PriorityLevelConfiguration 的集合

HTTP 请求

DELETE /apis/flowcontrol.apiserver.k8s.io/v1beta3/prioritylevelconfigurations

参数

响应

200 (Status): OK

401: Unauthorized

6.5.8.9 - Binding

Binding 将一个对象与另一个对象联系起来; 例如,一个 Pod 被调度程序绑定到一个节点。

apiVersion: v1

import "k8s.io/api/core/v1"

Binding

Binding 将一个对象与另一个对象联系起来; 例如,一个 Pod 被调度程序绑定到一个节点。 已在 1.7 版本弃用,请使用 Pod 的 binding 子资源。


标准对象的元数据, 更多信息: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata

  • target (ObjectReference), 必需

    要绑定到标准对象的目标对象。

操作


create 创建一个 Binding

HTTP 请求

POST /api/v1/namespaces/{namespace}/bindings

参数

响应

200 (Binding): OK

201 (Binding): Created

202 (Binding): Accepted

401: Unauthorized

create 创建 Pod 的绑定

HTTP 请求

POST /api/v1/namespaces/{namespace}/pods/{name}/binding

参数

  • name (路径参数): string, 必需

    Binding 的名称

  • namespace (路径参数): string, 必需

    namespace

  • body: Binding, 必需

响应

200 (Binding): OK

201 (Binding): Created

202 (Binding): Accepted

401: Unauthorized

6.5.8.10 - ComponentStatus

ComponentStatus(和 ComponentStatusList)保存集群检验信息。

apiVersion: v1

import "k8s.io/api/core/v1"

ComponentStatus

ComponentStatus(和 ComponentStatusList)保存集群检验信息。 已废弃:该 API 在 v1.19 及更高版本中废弃。


  • apiVersion: v1

  • kind: ComponentStatus

  • metadata (ObjectMeta)

    标准的对象元数据。更多信息: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata

  • conditions ([]ComponentCondition)

    补丁策略:基于键 type 合并

    观测到的组件状况的列表。

    组件状况相关信息。

    • conditions.status (string),必需

      组件状况的状态。“Healthy” 的有效值为:“True”、“False” 或 “Unknown”。

    • conditions.type (string),必需

    组件状况的类型。有效值:“Healthy”

    • conditions.error (string)

      组件状况的错误码。例如,一个健康检查错误码。

    • conditions.message (string)

      组件状况相关消息。例如,有关健康检查的信息。

ComponentStatusList

作为 ComponentStatus 对象列表,所有组件状况的状态。 已废弃:该 API 在 v1.19 及更高版本中废弃。


  • apiVersion: v1

  • kind: ComponentStatusList

操作


get 读取指定的 ComponentStatus

HTTP 请求

GET /api/v1/componentstatuses/{name}

参数

  • name (路径参数): string,必需

    ComponentStatus 的名称

  • pretty (查询参数): string

    pretty

响应

200 (ComponentStatus): OK

401: Unauthorized

list 列出 ComponentStatus 类别的对象

HTTP 请求

GET /api/v1/componentstatuses

参数

响应

200 (ComponentStatusList): OK

401: Unauthorized

6.5.8.11 - ClusterCIDR v1alpha1

ClusterCIDR 表示启用 MultiCIDRRangeAllocator 时针对每个节点 Pod CIDR 分配进行的单个配置(参阅针对 kube-controller-manager 的配置)。

apiVersion: networking.k8s.io/v1alpha1

import "k8s.io/api/networking/v1alpha1"

ClusterCIDR

ClusterCIDR 表示启用 MultiCIDRRangeAllocator 时针对每个节点 Pod CIDR 分配进行的单个配置 (参阅针对 kube-controller-manager 的配置)。 一个集群可能有任意数量的 ClusterCIDR 资源,在为节点分配 CIDR 时将考虑所有这些资源。 当节点选择算符与相关节点匹配并且有空闲 CIDR 可供分配时,ClusterCIDR 才能有资格用于给定的节点。 在存在多个匹配的 ClusterCIDR 资源的情况下,分配器将尝试使用内部启发式算法来解决这一问题, 但可以使用节点选择算符与该节点匹配的任何 ClusterCIDR。


ClusterCIDRSpec

ClusterCIDRSpec 定义 ClusterCIDR 的预期状态。


  • perNodeHostBits (int32),必需

    perNodeHostBits 定义每个节点要配置的主机位数。 子网掩码决定有多少地址用于网络位和主机位。 例如 IPv4 地址 192.168.0.0/24 将该地址拆分为 24 位用于网络部分和 8 位用于主机部分。 要分配 256 个 IP,需将此字段设置为 8(针对 IPv4 的掩码为 /24,针对 IPv6 的掩码为 /120)。 最小值为 4(16 个 IP)。此字段是不可变的。

  • ipv4 (string)

    ipv4 以 CIDR 表示法定义 IPv4 IP 块(例如 “10.0.0.0/8”)。 必须至少指定 ipv4 和 ipv6 之一。该字段是不可变的。

  • ipv6 (string)

    ipv6 以 CIDR 表示法定义 IPv6 IP 块(例如 “2001:db8::/64”)。 必须至少指定 ipv4 和 ipv6 之一。 该字段是不可变的。

  • nodeSelector (NodeSelector)

    nodeSelector 定义该配置适用于哪些节点。 空白或值为 nil 的 nodeSelector 选择所有节点。该字段是不可变的。

    节点选择算符表示在一组节点上一个或多个标签查询结果的并集; 也就是说,它表示由节点选择算符条件表示的选择算符的逻辑或计算结果。

    • nodeSelector.nodeSelectorTerms ([]NodeSelectorTerm),必需

      必需。节点选择算符条件的列表。这些条件以逻辑与进行计算。

      一个 null 或空的节点选择算符条件不会与任何对象匹配。这些要求会按逻辑与的关系来计算。 TopologySelectorTerm 类别实现了 NodeSelectorTerm 的子集。

      • nodeSelector.nodeSelectorTerms.matchExpressions ([]NodeSelectorRequirement)

        基于节点标签所设置的节点选择算符要求的列表。

      • nodeSelector.nodeSelectorTerms.matchFields ([]NodeSelectorRequirement)

        基于节点字段所设置的节点选择算符要求的列表。

ClusterCIDRList

ClusterCIDRList 包含 ClusterCIDR 的列表。


  • items ([]ClusterCIDR),必需

    items 是 ClusterCIDRs 的列表。

操作


get 读取指定的 ClusterCIDR

HTTP 请求

GET /apis/networking.k8s.io/v1alpha1/clustercidrs/{name}

参数

  • name (路径参数):string,必需

    ClusterCIDR 的名称

  • pretty (查询参数):string

    pretty

响应

200 (ClusterCIDR): OK

401: Unauthorized

list 列出或监视 ClusterCIDR 类别的对象

HTTP 请求

GET /apis/networking.k8s.io/v1alpha1/clustercidrs

参数

响应

200 (ClusterCIDRList): OK

401: Unauthorized

create 创建 ClusterCIDR

HTTP 请求

POST /apis/networking.k8s.io/v1alpha1/clustercidrs

参数

响应

200 (ClusterCIDR): OK

201 (ClusterCIDR): Created

202 (ClusterCIDR): Accepted

401: Unauthorized

update 替换指定的 ClusterCIDR

HTTP 请求

PUT /apis/networking.k8s.io/v1alpha1/clustercidrs/{name}

参数

  • name (路径参数):string,必需

    ClusterCIDR 的名称

  • body: ClusterCIDR,必需

  • dryRun (查询参数):string

    dryRun

  • fieldManager (查询参数):string

    fieldManager

  • fieldValidation (查询参数):string

    fieldValidation

  • pretty (查询参数):string

    pretty

响应

200 (ClusterCIDR): OK

201 (ClusterCIDR): Created

401: Unauthorized

patch 部分更新指定的 ClusterCIDR

HTTP 请求

PATCH /apis/networking.k8s.io/v1alpha1/clustercidrs/{name}

参数

  • name (路径参数):string,必需

    ClusterCIDR 的名称

  • body: Patch,必需

  • dryRun (查询参数):string

    dryRun

  • fieldManager (查询参数):string

    fieldManager

  • fieldValidation (查询参数):string

    fieldValidation

  • force (查询参数):boolean

    force

  • pretty (查询参数):string

    pretty

响应

200 (ClusterCIDR): OK

201 (ClusterCIDR): Created

401: Unauthorized

delete 删除 ClusterCIDR

HTTP 请求

DELETE /apis/networking.k8s.io/v1alpha1/clustercidrs/{name}

参数

响应

200 (Status): OK

202 (Status): Accepted

401: Unauthorized

deletecollection 删除 ClusterCIDR 的集合

HTTP 请求

DELETE /apis/networking.k8s.io/v1alpha1/clustercidrs

参数

响应

200 (Status): OK

401: Unauthorized

6.5.9 - 公共定义

6.5.9.1 - DeleteOptions

删除 API 对象时可以提供 DeleteOptions。

import "k8s.io/apimachinery/pkg/apis/meta/v1"

删除 API 对象时可以提供 DeleteOptions。


  • dryRun ([]string)

    该值如果存在,则表示不应保留修改。 无效或无法识别的 dryRun 指令将导致错误响应并且不会进一步处理请求。有效值为:

    • All:处理所有试运行阶段(Dry Run Stages)
  • gracePeriodSeconds (int64)

    表示对象被删除之前的持续时间(以秒为单位)。 值必须是非负整数。零值表示立即删除。如果此值为 nil,则将使用指定类型的默认宽限期。如果未指定,则为每个对象的默认值。

  • orphanDependents (boolean)

    已弃用:该字段将在 1.7 中弃用,请使用 propagationPolicy 字段。 该字段表示依赖对象是否应该是孤儿。如果为 true/false,对象的 finalizers 列表中会被添加上或者移除掉 “orphan” 终结器(Finalizer)。 可以设置此字段或者设置 propagationPolicy 字段,但不能同时设置以上两个字段。

  • preconditions (Preconditions)

    先决条件必须在执行删除之前完成。如果无法满足这些条件,将返回 409(冲突)状态。

    执行操作(更新、删除等)之前必须满足先决条件。

    • preconditions.resourceVersion (string)

      指定目标资源版本(resourceVersion)。

    • preconditions.uid (string)

      指定目标 UID。

  • propagationPolicy (string)

    表示是否以及如何执行垃圾收集。可以设置此字段或 orphanDependents 字段,但不能同时设置二者。 默认策略由 metadata.finalizers 中现有终结器(Finalizer)集合和特定资源的默认策略决定。 可接受的值为:Orphan - 令依赖对象成为孤儿对象;Background - 允许垃圾收集器在后台删除依赖项;Foreground - 一个级联策略,前台删除所有依赖项。

6.5.9.2 - LabelSelector

标签选择器是对一组资源的标签查询。

import "k8s.io/apimachinery/pkg/apis/meta/v1"

标签选择器是对一组资源的标签查询。 matchLabelsmatchExpressions 的结果按逻辑与的关系组合。 一个 empty 标签选择器匹配所有对象。一个 null 标签选择器不匹配任何对象。


  • matchExpressions ([]LabelSelectorRequirement)

    matchExpressions 是标签选择器要求的列表,这些要求的结果按逻辑与的关系来计算。

    标签选择器要求是包含值、键和关联键和值的运算符的选择器。

    • matchExpressions.key (string),必需

      key 是选择器应用的标签键。

    • matchExpressions.operator (string),必需

      operator 表示键与一组值的关系。有效的运算符包括 InNotInExistsDoesNotExist

    • matchExpressions.values ([]string)

      values 是一个字符串值数组。如果运算符为 InNotIn,则 values 数组必须为非空。 如果运算符是 ExistsDoesNotExist,则 values 数组必须为空。 该数组在策略性合并补丁(Strategic Merge Patch)期间被替换。

  • matchLabels (map[string]string)

    matchLabels 是 {key,value} 键值对的映射。

    matchLabels 映射中的单个 {key,value} 键值对相当于 matchExpressions 的一个元素, 其键字段为 key,运算符为 Invalues 数组仅包含 value

    所表达的需求最终要按逻辑与的关系组合。

6.5.9.3 - ListMeta

ListMeta 描述了合成资源必须具有的元数据,包括列表和各种状态对象。

import "k8s.io/apimachinery/pkg/apis/meta/v1"

ListMeta 描述了合成资源必须具有的元数据,包括列表和各种状态对象。 一个资源仅能有 {ObjectMeta, ListMeta} 中的一个。


  • continue (string)

    如果用户对返回的条目数量设置了限制,则 continue 可能被设置,表示服务器有更多可用的数据。 该值是不透明的,可用于向提供此列表服务的端点发出另一个请求,以检索下一组可用的对象。 如果服务器配置已更改或时间已过去几分钟,则可能无法继续提供一致的列表。 除非你在错误消息中收到此令牌(token),否则使用此 continue 值时返回的 resourceVersion 字段应该和第一个响应中的值是相同的。

  • remainingItemCount (int64)

    remainingItemCount 是列表中未包含在此列表响应中的后续项目的数量。 如果列表请求包含标签或字段选择器,则剩余项目的数量是未知的,并且在序列化期间该字段将保持未设置和省略。 如果列表是完整的(因为它没有分块或者这是最后一个块),那么就没有剩余的项目,并且在序列化过程中该字段将保持未设置和省略。 早于 v1.15 的服务器不设置此字段。remainingItemCount 的预期用途是估计集合的大小。 客户端不应依赖于设置准确的 remainingItemCount

  • selfLink (string)

    selfLink 表示此对象的 URL,由系统填充,只读。

    已弃用:selfLink 是一个遗留的只读字段,不再由系统填充。

6.5.9.4 - LocalObjectReference

LocalObjectReference 包含足够的信息,可以让你在同一命名空间内找到引用的对象。

import "k8s.io/api/core/v1"

LocalObjectReference 包含足够的信息,可以让你在同一命名空间(namespace)内找到引用的对象。


6.5.9.5 - NodeSelectorRequirement

节点选择算符需求是一个选择算符,其中包含值集、主键以及一个将键和值集关联起来的操作符。

import "k8s.io/api/core/v1"

节点选择算符需求是一个选择算符,其中包含值集、主键以及一个将键和值集关联起来的操作符。


  • key (string),必需

    选择算符所适用的标签主键。

  • operator (string),必需

    代表主键与值集之间的关系。合法的 operator 值包括 InNotInExistsDoesNotExistGtLt

  • values ([]string)

    一个由字符串值组成的数组。如果 operator 是 InNotIn,则 values 数组不能为空。 如果 operator 为 ExistsDoesNotExist,则 values 数组只能为空。 如果 operator 为 GtLt,则 values 数组只能包含一个元素,并且该元素会被解释为整数。 在执行策略性合并补丁操作时,此数组会被整体替换。

6.5.9.6 - ObjectFieldSelector

ObjectFieldSelector 选择对象的 APIVersioned 字段。

import "k8s.io/api/core/v1"

ObjectFieldSelector 选择对象的 APIVersioned 字段。


  • fieldPath (string),必需

    在指定 API 版本中要选择的字段的路径。

  • apiVersion (string)

    fieldPath 写入时所使用的模式版本,默认为 "v1"。

6.5.9.7 - ObjectMeta

ObjectMeta 是所有持久化资源必须具有的元数据,其中包括用户必须创建的所有对象。

import "k8s.io/apimachinery/pkg/apis/meta/v1"

ObjectMeta 是所有持久化资源必须具有的元数据,其中包括用户必须创建的所有对象。


  • name (string)

    name 在命名空间内必须是唯一的。创建资源时需要,尽管某些资源可能允许客户端请求自动地生成适当的名称。 名称主要用于创建幂等性和配置定义。无法更新。更多信息: https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names#names

  • generateName (string)

    generateName 是一个可选前缀,由服务器使用,仅在未提供 name 字段时生成唯一名称。 如果使用此字段,则返回给客户端的名称将与传递的名称不同。该值还将与唯一的后缀组合。 提供的值与 name 字段具有相同的验证规则,并且可能会根据所需的后缀长度被截断,以使该值在服务器上唯一。

    如果指定了此字段并且生成的名称存在,则服务器将不会返回 409 ——相反,它将返回 201 Created 或 500, 原因是 ServerTimeout 指示在分配的时间内找不到唯一名称,客户端应重试(可选,在 Retry-After 标头中指定的时间之后)。

    仅在未指定 name 时应用。更多信息: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency

  • namespace (string)

    namespace 定义了一个值空间,其中每个名称必须唯一。空命名空间相当于 “default” 命名空间,但 “default” 是规范表示。 并非所有对象都需要限定在命名空间中——这些对象的此字段的值将为空。

    必须是 DNS_LABEL。无法更新。更多信息: https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/namespaces

  • labels (map[string]string)

    可用于组织和分类(确定范围和选择)对象的字符串键和值的映射。 可以匹配 ReplicationController 和 Service 的选择算符。更多信息: https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/labels

  • annotations (map[string]string)

    annotations 是一个非结构化的键值映射,存储在资源中,可以由外部工具设置以存储和检索任意元数据。 它们不可查询,在修改对象时应保留。更多信息: https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/annotations

系统字段

  • finalizers ([]string)

    在从注册表中删除对象之前该字段必须为空。 每个条目都是负责的组件的标识符,各组件将从列表中删除自己对应的条目。 如果对象的 deletionTimestamp 非空,则只能删除此列表中的条目。 终结器可以按任何顺序处理和删除。没有按照顺序执行, 因为它引入了终结器卡住的重大风险。finalizers 是一个共享字段, 任何有权限的参与者都可以对其进行重新排序。如果按顺序处理终结器列表, 那么这可能导致列表中第一个负责终结器的组件正在等待列表中靠后负责终结器的组件产生的信号(字段值、外部系统或其他), 从而导致死锁。在没有强制排序的情况下,终结者可以在它们之间自由排序, 并且不容易受到列表中排序更改的影响。

  • managedFields ([]ManagedFieldsEntry)

    managedFields 将 workflow-id 和版本映射到由该工作流管理的字段集。 这主要用于内部管理,用户通常不需要设置或理解该字段。 工作流可以是用户名、控制器名或特定应用路径的名称,如 “ci-cd”。 字段集始终存在于修改对象时工作流使用的版本。

    ManagedFieldsEntry 是一个 workflow-id,一个 FieldSet,也是该字段集适用的资源的组版本。

    • managedFields.apiVersion (string)

      apiVersion 定义此字段集适用的资源的版本。 格式是 “group/version”,就像顶级 apiVersion 字段一样。 必须跟踪字段集的版本,因为它不能自动转换。

    • managedFields.fieldsType (string)

      FieldsType 是不同字段格式和版本的鉴别器。 目前只有一个可能的值:“FieldsV1”

    • managedFields.fieldsV1 (FieldsV1)

      FieldsV1 包含类型 “FieldsV1” 中描述的第一个 JSON 版本格式。

      FieldsV1 以 JSON 格式将一组字段存储在像 Trie 这样的数据结构中。

      每个键或是 . 表示字段本身,并且始终映射到一个空集, 或是一个表示子字段或元素的字符串。该字符串将遵循以下四种格式之一:

      1. f:<name>,其中 <name> 是结构中字段的名称,或映射中的键
      2. v:<value>,其中 <value> 是列表项的精确 json 格式值
      3. i:<index>,其中 <index> 是列表中项目的位置
      4. k:<keys>,其中 <keys> 是列表项的关键字段到其唯一值的映射。 如果一个键映射到一个空的 Fields 值,则该键表示的字段是集合的一部分。

      确切的格式在 sigs.k8s.io/structured-merge-diff 中定义。

    • managedFields.manager (string)

      manager 是管理这些字段的工作流的标识符。

    • managedFields.operation (string)

      operation 是导致创建此 managedFields 表项的操作类型。 此字段的仅有合法值是 “Apply” 和 “Update”。

    • managedFields.subresource (string)

      subresource 是用于更新该对象的子资源的名称,如果对象是通过主资源更新的,则为空字符串。 该字段的值用于区分管理者,即使他们共享相同的名称。例如,状态更新将不同于使用相同管理者名称的常规更新。 请注意,apiVersion 字段与 subresource 字段无关,它始终对应于主资源的版本。

    • managedFields.time (Time)

      time 是添加 managedFields 条目时的时间戳。 如果一个字段被添加、管理器更新任一所属字段值或移除一个字段,该时间戳也会更新。 从此条目中移除一个字段时该时间戳不会更新,因为另一个管理器将它接管了。

      time 是 time.Time 的包装类,支持正确地序列化为 YAML 和 JSON。 为 time 包提供的许多工厂方法提供了包装类。

  • ownerReferences ([]OwnerReference)

    补丁策略:根据 uid 键执行合并操作

    此对象所依赖的对象列表。如果列表中的所有对象都已被删除,则该对象将被垃圾回收。 如果此对象由控制器管理,则此列表中的条目将指向此控制器,controller 字段设置为 true。 管理控制器不能超过一个。

    OwnerReference 包含足够可以让你识别属主对象的信息。 属主对象必须与依赖对象位于同一命名空间中,或者是集群作用域的,因此没有命名空间字段。

只读字段

  • creationTimestamp (Time)

    creationTimestamp 是一个时间戳,表示创建此对象时的服务器时间。 不能保证在单独的操作中按发生前的顺序设置。 客户端不得设置此值。它以 RFC3339 形式表示,并采用 UTC。

    由系统填充。只读。列表为空。更多信息: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata

    time 是 time.Time 的包装类,支持正确地序列化为 YAML 和 JSON。 为 time 包提供的许多工厂方法提供了包装类。

  • deletionGracePeriodSeconds (int64)

    此对象从系统中删除之前允许正常终止的秒数。 仅当设置了 deletionTimestamp 时才设置。 只能缩短。只读。

  • deletionTimestamp (Time)

    deletionTimestamp 是删除此资源的 RFC 3339 日期和时间。 该字段在用户请求体面删除时由服务器设置,客户端不能直接设置。 一旦 finalizers 列表为空,该资源预计将在此字段中的时间之后被删除 (不再从资源列表中可见,并且无法通过名称访问)。 只要 finalizers 列表包含项目,就阻止删除。一旦设置了 deletionTimestamp, 该值可能不会被取消设置或在未来进一步设置,尽管它可能会缩短或在此时间之前可能会删除资源。 例如,用户可能要求在 30 秒内删除一个 Pod。 Kubelet 将通过向 Pod 中的容器发送体面的终止信号来做出反应。 30 秒后,Kubelet 将向容器发送硬终止信号(SIGKILL), 并在清理后从 API 中删除 Pod。在网络存在分区的情况下, 此对象可能在此时间戳之后仍然存在,直到管理员或自动化进程可以确定资源已完全终止。 如果未设置,则未请求体面删除该对象。

    请求体面删除时由系统填充。只读。更多信息: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata

    Time 是 time.Time 的包装类,支持正确地序列化为 YAML 和 JSON。 为 time 包提供的许多工厂方法提供了包装类。

  • generation (int64)

    表示期望状态的特定生成的序列号。由系统填充。只读。

  • resourceVersion (string)

    一个不透明的值,表示此对象的内部版本,客户端可以使用该值来确定对象是否已被更改。 可用于乐观并发、变更检测以及对资源或资源集的监听操作。 客户端必须将这些值视为不透明的,且未更改地传回服务器。 它们可能仅对特定资源或一组资源有效。

    由系统填充。只读。客户端必须将值视为不透明。更多信息: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency

  • selfLink (string)

    selfLink 是表示此对象的 URL。由系统填充。只读。

    已弃用。Kubernetes 将在 1.20 版本中停止传播该字段,并计划在 1.21 版本中删除该字段。

  • uid (string)

    UID 是该对象在时间和空间上的唯一值。它通常由服务器在成功创建资源时生成,并且不允许使用 PUT 操作更改。

    由系统填充。只读。更多信息: https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names#uids

6.5.9.8 - ObjectReference

ObjectReference 包含足够的信息,可以让你检查或修改引用的对象。

import "k8s.io/api/core/v1"

ObjectReference 包含足够的信息,允许你检查或修改引用的对象。


  • apiVersion (string)

    被引用者的 API 版本。

  • fieldPath (string)

    如果引用的是对象的某个对象是整个对象,则该字符串而不是应包含的 JSON/Go 字段有效访问语句, 例如 desiredState.manifest.containers[ 2 ]。例如,如果对象引用针对的是 Pod 中的一个容器, 此字段取值类似于:spec.containers{name}name 指触发的容器的名称), 或者如果没有指定容器名称,spec.containers[ 2 ](此 Pod 中索引为 2 的容器)。 选择这种只是为了有一些定义好的语法来引用对象的部分。

6.5.9.9 - Patch

提供 Patch 是为了给 Kubernetes PATCH 请求正文提供一个具体的名称和类型。

import "k8s.io/apimachinery/pkg/apis/meta/v1"

提供 Patch 是为了给 Kubernetes PATCH 请求正文提供一个具体的名称和类型。


6.5.9.10 - Quantity

数量(Quantity)是数字的定点表示。

import "k8s.io/apimachinery/pkg/api/resource"

数量(Quantity)是数字的定点表示。 除了 String() 和 AsInt64() 的访问接口之外, 它以 JSON 和 YAML 形式提供方便的打包和解包方法。

序列化格式如下:

<quantity>        ::= <signedNumber><suffix>
  (注意 <suffix> 可能为空,例如 <decimalSI> 的 "" 情形。) </br>
<digit>           ::= 0 | 1 | ... | 9 </br>
<digits>          ::= <digit> | <digit><digits> </br>
<number>          ::= <digits> | <digits>.<digits> | <digits>. | .<digits> </br>
<sign>            ::= "+" | "-" </br>
<signedNumber>    ::= <number> | <sign><number> </br>
<suffix>          ::= <binarySI> | <decimalExponent> | <decimalSI> </br>
<binarySI>        ::= Ki | Mi | Gi | Ti | Pi | Ei 
  (国际单位制度;查阅: http://physics.nist.gov/cuu/Units/binary.html)</br>
<decimalSI>       ::= m | "" | k | M | G | T | P | E 
  (注意,1024 = 1ki 但 1000 = 1k;我没有选择大写。) </br>
<decimalExponent> ::= "e" <signedNumber> | "E" <signedNumber> </br>

无论使用三种指数形式中哪一种,没有数量可以表示大于 263-1 的数,也不可能超过 3 个小数位。 更大或更精确的数字将被截断或向上取整(例如:0.1m 将向上取整为 1m)。 如果将来我们需要更大或更小的数量,可能会扩展。

当从字符串解析数量时,它将记住它具有的后缀类型,并且在序列化时将再次使用相同类型。

在序列化之前,数量将以“规范形式”放置。这意味着指数或者后缀将被向上或向下调整(尾数相应增加或减少),并确保:

  • 没有精度丢失
  • 不会输出小数数字
  • 指数(或后缀)尽可能大。

除非数量是负数,否则将省略正负号。

例如:

  • 1.5 将会被序列化成 “1500m”
  • 1.5Gi 将会被序列化成 “1536Mi”

请注意,数量永远不会在内部以浮点数表示。这是本设计的重中之重。

只要它们格式正确,非规范值仍将解析,但将以其规范形式重新输出(所以应该总是使用规范形式,否则不要执行 diff 比较)。

这种格式旨在使得很难在不撰写某种特殊处理代码的情况下使用这些数字,进而希望实现者也使用定点实现。


6.5.9.11 - ResourceFieldSelector

ResourceFieldSelector 表示容器资源(CPU,内存)及其输出格式。

import "k8s.io/api/core/v1"

ResourceFieldSelector 表示容器资源(CPU,内存)及其输出格式。


  • resource (string),必需

    必需:选择的资源。

  • containerName (string)

    容器名称:对卷必需,对环境变量可选。

  • divisor (Quantity)

    指定所公开的资源的输出格式,默认值为 “1”。

6.5.9.12 - Status

状态(Status)是不返回其他对象的调用的返回值。

import "k8s.io/apimachinery/pkg/apis/meta/v1"

状态(Status)是不返回其他对象的调用的返回值。


  • apiVersion (string)

    apiVersion 定义对象表示的版本化模式。 服务器应将已识别的模式转换为最新的内部值,并可能拒绝无法识别的值。 更多信息: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources

  • code (int32)

    此状态的建议 HTTP 返回代码,如果未设置,则为 0。

  • details (StatusDetails)

    与原因(Reason)相关的扩展数据。每个原因都可以定义自己的扩展细节。 此字段是可选的,并且不保证返回的数据符合任何模式,除非由原因类型定义。

    StatusDetails 是一组附加属性,可以由服务器设置以提供有关响应的附加信息。 状态对象的原因字段定义将设置哪些属性。 客户端必须忽略与每个属性的定义类型不匹配的字段,并且应该假定任何属性可能为空、无效或未定义。

    • details.causes ([]StatusCause)

      causes 数组包含与 StatusReason 故障相关的更多详细信息。 并非所有 StatusReasons 都可以提供详细的原因。

      StatusCause 提供有关 api.Status 失败的更多信息,包括遇到多个错误的情况。

      • details.causes.field (string)

        导致此错误的资源字段,由其 JSON 序列化命名。 可能包括嵌套属性的点和后缀表示法。数组是从零开始索引的。 由于字段有多个错误,字段可能会在一系列原因中出现多次。可选。

        示例:

        • “name”:当前资源上的字段 “name”
        • “items[0].name”:“items” 中第一个数组条目上的字段 “name”
      • details.causes.message (string)

        对错误原因的可读描述。该字段可以按原样呈现给读者。

      • details.causes.reason (string)

        错误原因的机器可读描述。如果此值为空,则没有可用信息。

    • details.group (string)

      与状态 StatusReason 关联的资源的组属性。

    • details.kind (string)

      与状态 StatusReason 关联的资源的类别属性。 在某些操作上可能与请求的资源类别不同。 更多信息: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds

    • details.name (string)

      与状态 StatusReason 关联的资源的名称属性(当有一个可以描述的名称时)。

    • details.retryAfterSeconds (int32)

      如果指定,则应重试操作前的时间(以秒为单位)。 一些错误可能表明客户端必须采取替代操作——对于这些错误,此字段可能指示在采取替代操作之前等待多长时间。

    • details.uid (string)

      资源的 UID(当有单个可以描述的资源时)。更多信息: https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names#uids

  • kind (string)

    kind 是一个字符串值,表示此对象表示的 REST 资源。 服务器可以从客户端提交请求的端点推断出这一点。 无法更新。驼峰式规则。更多信息: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds

  • message (string)

    此操作状态的人类可读描述。

  • metadata (ListMeta)

    标准的列表元数据。更多信息: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds

  • reason (string)

    机器可读的说明,说明此操作为何处于“失败”状态。 如果此值为空,则没有可用信息。 reason 澄清了 HTTP 状态代码,但不会覆盖它。

  • status (string)

    操作状态。“Success”或“Failure” 之一。更多信息: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status

6.5.9.13 - TypedLocalObjectReference

TypedLocalObjectReference 包含足够的信息,可以让你在同一个名称空间中定位指定类型的引用对象。

import "k8s.io/api/core/v1"

TypedLocalObjectReference 包含足够的信息,可以让你在同一个名称空间中定位特定类型的引用对象。


  • kind (string),必需

    kind 是被引用的资源的类型。

  • name (string),必需

    name 是被引用的资源的名称。

  • apiGroup (string)

    apiGroup 是被引用资源的组。如果不指定 apiGroup,则指定的 kind 必须在核心 API 组中。 对于任何其它第三方类型,都需要 apiGroup。

6.5.10 - 其他资源

6.5.10.1 - ValidatingAdmissionPolicyBindingList v1beta1

apiVersion: admissionregistration.k8s.io/v1beta1

import "k8s.io/api/admissionregistration/v1beta1"

6.5.11 - 常用参数

allowWatchBookmarks

allowWatchBookmarks 字段请求类型为 BOOKMARK 的监视事件。 没有实现书签的服务器可能会忽略这个标志,并根据服务器的判断发送书签。 客户端不应该假设书签会在任何特定的时间间隔返回,也不应该假设服务器会在会话期间发送任何书签事件。 如果当前请求不是 watch 请求,则忽略该字段。


continue

当需要从服务器检索更多结果时,应该设置 continue 选项。由于这个值是服务器定义的, 客户端只能使用先前查询结果中具有相同查询参数的 continue 值(continue 值除外), 服务器可能拒绝它识别不到的 continue 值。 如果指定的 continue 值不再有效,无论是由于过期(通常是 5 到 15 分钟) 还是服务器上的配置更改,服务器将响应 "410 ResourceExpired" 错误和一个 continue 令牌。 如果客户端需要一个一致的列表,它必须在没有 continue 字段的情况下重新发起 list 请求。 否则,客户端可能会发送另一个带有 410 错误令牌的 list 请求,服务器将响应从下一个键开始的列表, 但列表数据来自最新的快照,这与之前的列表结果不一致。 第一个列表请求之后被创建、修改或删除的对象将被包含在响应中,只要它们的键是在“下一个键”之后。

当 watch 字段为 true 时,不支持此字段。客户端可以从服务器返回的最后一个 resourceVersion 值开始监视,就不会错过任何修改。


dryRun

表示不应该持久化所请求的修改。无效或无法识别的 dryRun 指令将导致错误响应, 并且服务器不再对请求进行进一步处理。有效值为:

  • All: 将处理所有的演练阶段

fieldManager

fieldManager 是与进行这些更改的参与者或实体相关联的名称。 长度小于或128个字符且仅包含可打印字符,如 https://pkg.go.dev/unicode#IsPrint 所定义。


fieldSelector

限制所返回对象的字段的选择器。默认为返回所有字段。


fieldValidation

fieldValidation 指示服务器如何处理请求(POST/PUT/PATCH)中包含未知或重复字段的对象。 有效值为:

  • Ignore:这将忽略从对象中默默删除的所有未知字段,并将忽略除解码器遇到的最后一个重复字段之外的所有字段。 这是在 v1.23 之前的默认行为。
  • Warn:这将针对从对象中删除的各个未知字段以及所遇到的各个重复字段,分别通过标准警告响应头发出警告。 如果没有其他错误,请求仍然会成功,并且只会保留所有重复字段中的最后一个。 这是 v1.23+ 版本中的默认设置。
  • Strict:如果从对象中删除任何未知字段,或者存在任何重复字段,将使请求失败并返回 BadRequest 错误。

从服务器返回的错误将包含所有遇到的未知和重复的字段。


force

Force 将“强制”应用请求。这意味着用户将重新获得他人拥有的冲突领域。 对于非应用补丁请求,Force 标志必须不设置。


gracePeriodSeconds

删除对象前的持续时间(秒数)。值必须为非负整数。取值为 0 表示立即删除。 如果该值为 nil,将使用指定类型的默认宽限期。如果没有指定,默认为每个对象的设置值。 0 表示立即删除。


labelSelector

通过标签限制返回对象列表的选择器。默认为返回所有对象。


limit

limit 是一个列表调用返回的最大响应数。如果有更多的条目,服务器会将列表元数据上的 'continue' 字段设置为一个值,该值可以用于相同的初始查询来检索下一组结果。 设置 limit 可能会在所有请求的对象被过滤掉的情况下返回少于请求的条目数量(下限为零), 并且客户端应该只根据 continue 字段是否存在来确定是否有更多的结果可用。 服务器可能选择不支持 limit 参数,并将返回所有可用的结果。 如果指定了 limit 并且 continue 字段为空,客户端可能会认为没有更多的结果可用。 如果 watch 为 true,则不支持此字段。

服务器保证在使用 continue 时返回的对象将与不带 limit 的列表调用相同, 也就是说,在发出第一个请求后所创建、修改或删除的对象将不包含在任何后续的继续请求中。 这有时被称为一致性快照,确保使用 limit 的客户端在分块接收非常大的结果的客户端能够看到所有可能的对象。 如果对象在分块列表期间被更新,则返回计算第一个列表结果时存在的对象版本。


namespace

对象名称和身份验证范围,例如用于团队和项目。


pretty

如果设置为 'true',那么输出是规范的打印。


propagationPolicy

该字段决定是否以及如何执行垃圾收集。可以设置此字段或 OrphanDependents,但不能同时设置。 默认策略由 metadata.finalizers 和特定资源的默认策略设置决定。可接受的值是:

  • 'Orphan':孤立依赖项;
  • 'Background':允许垃圾回收器后台删除依赖;
  • 'Foreground':一个级联策略,前台删除所有依赖项。

resourceVersion

resourceVersion 对请求所针对的资源版本设置约束。 详情请参见 https://kubernetes.io/zh-cn/docs/reference/using-api/api-concepts/#resource-versions

默认不设置。


resourceVersionMatch

resourceVersionMatch 字段决定如何将 resourceVersion 应用于列表调用。 强烈建议对设置了 resourceVersion 的列表调用设置 resourceVersion 匹配, 具体请参见 https://kubernetes.io/zh-cn/docs/reference/using-api/api-concepts/#resource-versions

默认不设置。


sendInitialEvents

sendInitialEvents=true 可以和 watch=true 一起设置。 在这种情况下,监视通知流将从合成事件开始,以生成集合中对象的当前状态。 一旦发送了所有此类事件,将发送合成的 "Bookmark" 事件。"bookmark" 将报告对象集合对应的 ResourceVersion(RV),并标有 "k8s.io/initial-events-end": "true" 注解。 之后,监视通知流将照常进行,发送与所监视的对象的变更(在 RV 之后)对应的监视事件。

当设置了 sendInitialEvents 选项时,我们还需要设置 resourceVersionMatch 选项。watch 请求的语义如下:

  • resourceVersionMatch = NotOlderThan 被解释为"数据至少与提供的 resourceVersion 一样新", 最迟当状态同步到与 ListOptions 提供的版本一样新的 resourceVersion 时, 发送 bookmark 事件。如果 resourceVersion 未设置,这将被解释为"一致读取", 最迟当状态同步到开始处理请求的那一刻时,发送 bookmark 事件。
  • resourceVersionMatch 设置为任何其他值或返回 unsetInvalid 错误。

如果 resourceVersion=""resourceVersion="0"(出于向后兼容性原因),默认为 true,否则默认为 false。


timeoutSeconds

list/watch 调用的超时秒数。这选项限制调用的持续时间,无论是否有活动。


watch

监视对所述资源的更改,并将其这类变更以添加、更新和删除通知流的形式返回。指定 resourceVersion。


6.6 - 插桩

6.6.1 - Kubernetes 组件 SLI 指标

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

默认情况下,Kubernetes 1.30 会为每个 Kubernetes 组件的二进制文件发布服务等级指标(SLI)。 此指标端点被暴露在每个组件提供 HTTPS 服务的端口上,路径为 /metrics/slis。 从 v1.27 版本开始,对每个 Kubernetes 组件而言, ComponentSLIs 特性门控都是默认启用的。

SLI 指标

启用 SLI 指标时,每个 Kubernetes 组件暴露两个指标,按照健康检查添加标签:

  • 计量值(表示健康检查的当前状态)
  • 计数值(记录观察到的每个健康检查状态的累计次数)

你可以使用此指标信息计算每个组件的可用性统计信息。例如,API 服务器检查 etcd 的健康。 你可以计算并报告 etcd 的可用或不可用情况,具体由其客户端(即 API 服务器)进行报告。

Prometheus 计量表数据看起来类似于:

# HELP kubernetes_healthcheck [ALPHA] This metric records the result of a single healthcheck.
# TYPE kubernetes_healthcheck gauge
kubernetes_healthcheck{name="autoregister-completion",type="healthz"} 1
kubernetes_healthcheck{name="autoregister-completion",type="readyz"} 1
kubernetes_healthcheck{name="etcd",type="healthz"} 1
kubernetes_healthcheck{name="etcd",type="readyz"} 1
kubernetes_healthcheck{name="etcd-readiness",type="readyz"} 1
kubernetes_healthcheck{name="informer-sync",type="readyz"} 1
kubernetes_healthcheck{name="log",type="healthz"} 1
kubernetes_healthcheck{name="log",type="readyz"} 1
kubernetes_healthcheck{name="ping",type="healthz"} 1
kubernetes_healthcheck{name="ping",type="readyz"} 1

而计数器数据看起来类似于:

# HELP kubernetes_healthchecks_total [ALPHA] This metric records the results of all healthcheck.
# TYPE kubernetes_healthchecks_total counter
kubernetes_healthchecks_total{name="autoregister-completion",status="error",type="readyz"} 1
kubernetes_healthchecks_total{name="autoregister-completion",status="success",type="healthz"} 15
kubernetes_healthchecks_total{name="autoregister-completion",status="success",type="readyz"} 14
kubernetes_healthchecks_total{name="etcd",status="success",type="healthz"} 15
kubernetes_healthchecks_total{name="etcd",status="success",type="readyz"} 15
kubernetes_healthchecks_total{name="etcd-readiness",status="success",type="readyz"} 15
kubernetes_healthchecks_total{name="informer-sync",status="error",type="readyz"} 1
kubernetes_healthchecks_total{name="informer-sync",status="success",type="readyz"} 14
kubernetes_healthchecks_total{name="log",status="success",type="healthz"} 15
kubernetes_healthchecks_total{name="log",status="success",type="readyz"} 15
kubernetes_healthchecks_total{name="ping",status="success",type="healthz"} 15
kubernetes_healthchecks_total{name="ping",status="success",type="readyz"} 15

使用此类数据

组件 SLI 指标端点旨在以高频率被抓取。 高频率抓取意味着你最终会获得更细粒度的计量信号,然后可以将其用于计算 SLO。 /metrics/slis 端点为各个 Kubernetes 组件提供了计算可用性 SLO 所需的原始数据。

6.6.2 - CRI Pod 和容器指标

通过 CRI 收集 Pod 和容器指标
特性状态: Kubernetes v1.23 [alpha]

kubelet 通过 cAdvisor 收集 Pod 和容器指标。作为一个 Alpha 特性, Kubernetes 允许你通过容器运行时接口(CRI) 配置收集 Pod 和容器指标。要使用基于 CRI 的收集机制,你必须启用 PodAndContainerStatsFromCRI 特性门控 并使用兼容的 CRI 实现(containerd >= 1.6.0, CRI-O >= 1.23.0)。

CRI Pod 和容器指标

当启用 PodAndContainerStatsFromCRI 时,Kubelet 轮询底层容器运行时以获取 Pod 和容器统计信息,而不是直接使用 cAdvisor 检查主机系统。同直接使用 cAdvisor 收集信息相比,依靠容器运行时获取这些信息的好处包括:

  • 潜在的性能改善,如果容器运行时在正常操作中已经收集了这些信息。 在这种情况下,这些数据可以被重用,而不是由 Kubelet 再次进行聚合。
  • 这种做法进一步解耦了 Kubelet 和容器运行时。 对于使用 Kubelet 来在主机上运行进程的容器运行时,其行为可用 cAdvisor 观测; 对于其他运行时(例如,使用虚拟化的容器运行时)而言, 这种做法提供了允许收集容器运行时指标的可能性。

6.6.3 - 节点指标数据

访问 kubelet 所观测到的节点、卷、Pod 和容器级别指标的机制。

kubelet 在节点、卷、Pod 和容器级别收集统计信息,并在 概要 API 中输出这些信息。

你可以通过 Kubernetes API 服务器将代理的请求发送到 stats 概要 API。

下面是一个名为 minikube 的节点的概要 API 请求示例:

kubectl get --raw "/api/v1/nodes/minikube/proxy/stats/summary"

下面是使用 curl 所执行的相同 API 调用:

# 你需要先运行 "kubectl proxy"
# 更改 8080 为 "kubectl proxy" 指派的端口
curl http://localhost:8080/api/v1/nodes/minikube/proxy/stats/summary

概要指标 API 源

默认情况下,Kubernetes 使用 kubelet 内运行的嵌入式 cAdvisor 获取节点概要指标数据。如果你在自己的集群中启用 PodAndContainerStatsFromCRI 特性门控, 且你通过容器运行时接口 (CRI) 使用支持统计访问的容器运行时, 则 kubelet 将使用 CRI 来获取 Pod 和容器级别的指标数据, 而不是 cAdvisor 来获取。

接下来

集群故障排查任务页面讨论了如何使用依赖这些数据的指标管道。

6.7 - Kubernetes 问题和安全

6.7.1 - Kubernetes 问题追踪

要报告安全问题,请遵循 Kubernetes 安全问题公开流程

使用 GitHub Issues 跟踪 Kubernetes 编码工作和公开问题。

与安全性相关的公告将发送到 kubernetes-security-announce@googlegroups.com 邮件列表。

6.7.2 - Kubernetes 安全和信息披露

本页面介绍 Kubernetes 安全和信息披露相关的内容。

安全公告

加入 kubernetes-security-announce 组,以获取关于安全性和主要 API 公告的电子邮件。

报告一个漏洞

我们非常感谢向 Kubernetes 开源社区报告漏洞的安全研究人员和用户。 所有的报告都由社区志愿者进行彻底调查。

如需报告,请将你的漏洞提交给 Kubernetes 漏洞赏金计划。 这样做可以使得社区能够在标准化的响应时间内对漏洞进行分类和处理。

你还可以通过电子邮件向私有 security@kubernetes.io 列表发送电子邮件,邮件中应该包含 所有 Kubernetes 错误报告 所需的详细信息。

你可以使用安全响应委员会成员的 GPG 密钥加密你的发往邮件列表的邮件。揭示问题时不需要使用 GPG 来加密。

我应该在什么时候报告漏洞?

  • 你认为在 Kubernetes 中发现了一个潜在的安全漏洞
  • 你不确定漏洞如何影响 Kubernetes
  • 你认为你在 Kubernetes 依赖的另一个项目中发现了一个漏洞
    • 对于具有漏洞报告和披露流程的项目,请直接在该项目处报告

我什么时候不应该报告漏洞?

  • 你需要调整 Kubernetes 组件安全性的帮助
  • 你需要应用与安全相关更新的帮助
  • 你的问题与安全无关

安全漏洞响应

每个报告在 3 个工作日内由安全响应委员会成员确认和分析, 这将启动安全发布过程

与安全响应委员会共享的任何漏洞信息都保留在 Kubernetes 项目中,除非有必要修复该问题,否则不会传播到其他项目。

随着安全问题从分类、识别修复、发布计划等方面的进展,我们将不断更新报告。

公开披露时间

公开披露日期由 Kubernetes 安全响应委员会和 bug 提交者协商。 我们倾向于在能够为用户提供缓解措施之后尽快完全披露该 bug。

当 bug 或其修复还没有被完全理解,解决方案没有经过良好的测试,或者为了处理供应商协调问题时,延迟披露是合理的。

信息披露的时间范围从即时(尤其是已经公开的)到几周不等。 对于具有直接缓解措施的漏洞,我们希望报告日期到披露日期的间隔是 7 天。 在设置披露日期方面,Kubernetes 安全响应委员会拥有最终决定权。

6.7.3 - 官方 CVE 订阅源

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

这是由 Kubernetes 安全响应委员会(Security Response Committee, SRC)公布的经社区维护的官方 CVE 列表。 更多细节请参阅 Kubernetes 安全和信息披露

Kubernetes 项目以 JSON FeedRSS feed 格式就已发布的安全问题提供了可通过程序访问的提要。 你可以通过执行以下命令来查阅这些安全问题:

链接到 JSON 格式

curl -Lv https://k8s.io/docs/reference/issues-security/official-cve-feed/index.json

链接到 RSS 格式

curl -Lv https://k8s.io/docs/reference/issues-security/official-cve-feed/feed.xml
Kubernetes CVE 列表 (last updated: 23 Apr 2024 23:45:53 UTC)
CVE ID问题描述CVE GitHub Issue URL
CVE-2024-3177Bypassing mountable secrets policy imposed by the ServiceAccount admission plugin#124336
CVE-2023-5528Insufficient input sanitization in in-tree storage plugin leads to privilege escalation on Windows nodes#121879
CVE-2023-3955Insufficient input sanitization on Windows nodes leads to privilege escalation#119595
CVE-2023-3893Insufficient input sanitization on kubernetes-csi-proxy leads to privilege escalation#119594
CVE-2023-3676Insufficient input sanitization on Windows nodes leads to privilege escalation#119339
CVE-2023-2431Bypass of seccomp profile enforcement#118690
CVE-2023-2727, CVE-2023-2728Bypassing policies imposed by the ImagePolicyWebhook and bypassing mountable secrets policy imposed by the ServiceAccount admission plugin#118640
CVE-2023-2878secrets-store-csi-driver discloses service account tokens in logs#118419
CVE-2022-3294Node address isn't always verified when proxying#113757
CVE-2022-3162Unauthorized read of Custom Resources#113756
CVE-2022-3172Aggregated API server can cause clients to be redirected (SSRF)#112513
CVE-2021-25749`runAsNonRoot` logic bypass for Windows containers#112192
CVE-2021-25741Symlink Exchange Can Allow Host Filesystem Access#104980
CVE-2021-25737Holes in EndpointSlice Validation Enable Host Network Hijack#102106
CVE-2021-3121Processes may panic upon receipt of malicious protobuf messages#101435
CVE-2021-25735Validating Admission Webhook does not observe some previous fields#100096
CVE-2020-8554Man in the middle using LoadBalancer or ExternalIPs#97076
CVE-2020-8566Ceph RBD adminSecrets exposed in logs when loglevel >= 4#95624
CVE-2020-8565Incomplete fix for CVE-2019-11250 allows for token leak in logs when logLevel >= 9#95623
CVE-2020-8564Docker config secrets leaked when file is malformed and log level >= 4#95622
CVE-2020-8563Secret leaks in kube-controller-manager when using vSphere provider#95621
CVE-2020-8557Node disk DOS by writing to container /etc/hosts#93032
CVE-2020-8559Privilege escalation from compromised node to cluster#92914
CVE-2020-8558Node setting allows for neighboring hosts to bypass localhost boundary#92315
CVE-2020-8555Half-Blind SSRF in kube-controller-manager#91542
CVE-2020-10749IPv4 only clusters susceptible to MitM attacks via IPv6 rogue router advertisements#91507
CVE-2019-11254kube-apiserver Denial of Service vulnerability from malicious YAML payloads#89535
CVE-2020-8552apiserver DoS (oom)#89378
CVE-2020-8551Kubelet DoS via API#89377
CVE-2019-11251kubectl cp symlink vulnerability#87773
CVE-2018-1002102Unvalidated redirect#85867
CVE-2019-11255CSI volume snapshot, cloning and resizing features can result in unauthorized volume data access or mutation#85233
CVE-2019-11253Kubernetes API Server JSON/YAML parsing vulnerable to resource exhaustion attack#83253
CVE-2019-11250Bearer tokens are revealed in logs#81114
CVE-2019-11248/debug/pprof exposed on kubelet's healthz port#81023
CVE-2019-11249Incomplete fixes for CVE-2019-1002101 and CVE-2019-11246, kubectl cp potential directory traversal#80984
CVE-2019-11247API server allows access to custom resources via wrong scope#80983
CVE-2019-11245container uid changes to root after first restart or if image is already pulled to the node#78308
CVE-2019-11243rest.AnonymousClientConfig() does not remove the serviceaccount credentials from config created by rest.InClusterConfig()#76797
CVE-2019-11244`kubectl:-http-cache=<world-accessible dir>` creates world-writeable cached schema files#76676
CVE-2019-1002100json-patch requests can exhaust apiserver resources#74534
CVE-2018-1002105proxy request handling in kube-apiserver can leave vulnerable TCP connections#71411
CVE-2018-1002101smb mount security issue#65750
CVE-2018-1002100Kubectl copy doesn't check for paths outside of it's destination directory.#61297
CVE-2017-1002102atomic writer volume handling allows arbitrary file deletion in host filesystem#60814
CVE-2017-1002101subpath volume mount handling allows arbitrary file access in host filesystem#60813
CVE-2017-1002100Azure PV should be Private scope not Container scope#47611
CVE-2017-1000056PodSecurityPolicy admission plugin authorizes incorrectly#43459

此订阅源会自动刷新,但从宣布 CVE 到可在此订阅源中找到对应的 CVE 会有一个明显却很小的延迟(几分钟到几小时)。

此订阅源的真实来源是一组 GitHub Issue,通过受控和受限的标签 official-cve-feed 进行过滤。 原始数据存放在 Google Cloud Bucket 中,只有社区少数受信任的成员可以写入。

6.8 - 节点参考信息

本部分包含以下有关节点的参考主题:

你还可以从 Kubernetes 文档的其他地方阅读节点的详细参考信息,包括:

6.8.1 - Kubelet Checkpoint API

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

为容器生成检查点这个功能可以为一个正在运行的容器创建有状态的拷贝。 一旦容器有一个有状态的拷贝,你就可以将其移动到其他计算机进行调试或类似用途。

如果你将通过检查点操作生成的容器数据移动到能够恢复该容器的一台计算机, 所恢复的容器将从之前检查点操作执行的时间点继续运行。 你也可以检视所保存的数据,前提是你拥有这类操作的合适工具。

创建容器的检查点可能会产生安全隐患。 通常,一个检查点包含执行检查点操作时容器中所有进程的所有内存页。 这意味着以前存在于内存中的一切内容现在都在本地磁盘上获得。 这里的内容包括一切私密数据和可能用于加密的密钥。 底层 CRI 实现(该节点上的容器运行时)应创建只有 root 用户可以访问的检查点存档。 另外重要的是记住:如果检查点存档被转移到另一个系统,该检查点存档的所有者将可以读取所有内存页。

操作

post 对指定的容器执行检查点操作

告知 kubelet 对指定 Pod 中的特定容器执行检查点操作。

查阅 Kubelet 身份验证/鉴权参考了解如何控制对 kubelet 检查点接口的访问。

Kubelet 将对底层 CRI 实现请求执行检查点操作。 在该检查点请求中,Kubelet 将检查点存档的名称设置为 checkpoint-<pod 全称>-<容器名称>-<时间戳>.tar, 还会请求将该检查点存档存储到其根目录(由 --root-dir 定义)下的 checkpoints 子目录中。 这个目录默认为 /var/lib/kubelet/checkpoints

检查点存档的格式为 tar,可以使用 tar 的一种实现来读取。存档文件的内容取决于底层 CRI 实现(该节点的容器运行时)。

HTTP 请求

POST /checkpoint/{namespace}/{pod}/{container}

参数

  • namespace (路径参数):string,必需

    名字空间(Namespace)
  • pod (路径参数):string,必需

    Pod
  • container (路径参数):string,必需

    容器(Container)
  • timeout (查询参数):integer

    等待检查点创建完成的超时时间(单位为秒)。 如果超时值为零或未设定,将使用默认的 CRI 超时时间值。 生成检查点所需的时长直接取决于容器所用的内存。容器使用的内存越多,创建相应检查点所需的时间越长。

响应

200: OK

401: Unauthorized

404: Not Found(如果 ContainerCheckpoint 特性门控被禁用)

404: Not Found(如果指定的 namespacepodcontainer 无法被找到)

500: Internal Server Error(如果执行检查点操作期间 CRI 实现遇到一个错误(参阅错误消息了解更多细节))

500: Internal Server Error(如果 CRI 实现未实现检查点 CRI API(参阅错误消息了解更多细节))

6.8.2 - 关于 dockershim 移除和使用兼容 CRI 运行时的文章

这是关于 Kubernetes 弃用和移除 dockershim 或使用兼容 CRI 的容器运行时相关的文章和其他页面的列表。

Kubernetes 项目

你可以通过 GitHub 问题 Dockershim 移除反馈和问题 提供反馈。 (k/kubernetes/#106917)

外部来源

6.8.3 - 由 kubelet 填充的节点标签

Kubernetes 节点预先填充了一组标准 标签

你还可以通过 kubelet 配置或使用 Kubernetes API 在节点上设置自己的标签。

预设标签

Kubernetes 在节点上设置的预设标签有:

接下来

6.8.4 - Kubelet 设备管理器 API 版本

本页详述了 Kubernetes 设备插件 API 与不同版本的 Kubernetes 本身之间的版本兼容性。

兼容性矩阵

v1alpha1v1beta1
Kubernetes 1.21-
Kubernetes 1.22-
Kubernetes 1.23-
Kubernetes 1.24-
Kubernetes 1.25-
Kubernetes 1.26-

简要说明:

  • 设备插件 API 和 Kubernetes 版本中的特性或 API 对象完全相同。
  • + 设备插件 API 具有 Kubernetes 集群中可能不存在的特性或 API 对象, 不是因为设备插件 API 添加了额外的新 API 调用,就是因为服务器移除了旧的 API 调用。 但它们的共同点是(大多数其他 API)都能工作。 请注意,Alpha API 可能会在次要版本的迭代过程中消失或出现重大变更。
  • - Kubernetes 集群具有设备插件 API 无法使用的特性,不是因为服务器添加了额外的 API 调用, 就是因为设备插件 API 移除了旧的 API 调用。但它们的共同点是(大多数 API)都能工作。

6.8.5 - 节点状态

在 Kubernetes 中,节点的状态是管理 Kubernetes 集群的一个关键方面。在本文中,我们将简要介绍如何监控和维护节点状态以确保集群的健康和稳定。

节点状态字段

一个节点的状态包含以下信息:

你可以使用 kubectl 来查看节点状态和其他细节信息:

kubectl describe node <节点名称>

下面对输出的每个部分进行详细描述。

地址

这些字段的用法取决于你的云服务商或者物理机配置。

  • HostName:由节点的内核报告。可以通过 kubelet 的 --hostname-override 参数覆盖。
  • ExternalIP:通常是节点的可外部路由(从集群外可访问)的 IP 地址。
  • InternalIP:通常是节点的仅可在集群内部路由的 IP 地址。

状况

conditions 字段描述了所有 Running 节点的状况。状况的示例包括:

节点状况及每种状况适用场景的描述
节点状况描述
Ready如节点是健康的并已经准备好接收 Pod 则为 TrueFalse 表示节点不健康而且不能接收 Pod;Unknown 表示节点控制器在最近 node-monitor-grace-period 期间(默认 40 秒)没有收到节点的消息
DiskPressureTrue 表示节点存在磁盘空间压力,即磁盘可用量低,否则为 False
MemoryPressureTrue 表示节点存在内存压力,即节点内存可用量低,否则为 False
PIDPressureTrue 表示节点存在进程压力,即节点上进程过多;否则为 False
NetworkUnavailableTrue 表示节点网络配置不正确;否则为 False

在 Kubernetes API 中,节点的状况表示节点资源中 .status 的一部分。 例如,以下 JSON 结构描述了一个健康节点:

"conditions": [
  {
    "type": "Ready",
    "status": "True",
    "reason": "KubeletReady",
    "message": "kubelet is posting ready status",
    "lastHeartbeatTime": "2019-06-05T18:38:35Z",
    "lastTransitionTime": "2019-06-05T11:41:27Z"
  }
]

当节点上出现问题时,Kubernetes 控制面会自动创建与影响节点的状况对应的 污点。 例如当 Ready 状况的 status 保持 UnknownFalse 的时间长于 kube-controller-manager 的 NodeMonitorGracePeriod(默认为 40 秒)时, 会造成 Unknown 状态下为节点添加 node.kubernetes.io/unreachable 污点或在 False 状态下为节点添加 node.kubernetes.io/not-ready 污点。

这些污点会影响悬决的 Pod,因为调度器在将 Pod 分配到节点时会考虑节点的污点。 已调度到节点的当前 Pod 可能会由于施加的 NoExecute 污点被驱逐。 Pod 还可以设置容忍度, 使得这些 Pod 仍然能够调度到且继续运行在设置了特定污点的节点上。

进一步的细节可参阅基于污点的驱逐根据状况为节点设置污点

容量(Capacity)与可分配(Allocatable)

这两个值描述节点上的可用资源:CPU、内存和可以调度到节点上的 Pod 的个数上限。

capacity 块中的字段标示节点拥有的资源总量。 allocatable 块指示节点上可供普通 Pod 使用的资源量。

你可以通过学习如何在节点上预留计算资源 来进一步了解有关容量和可分配资源的信息。

信息(Info)

Info 指的是节点的一般信息,如内核版本、Kubernetes 版本(kubeletkube-proxy 版本)、 容器运行时详细信息,以及节点使用的操作系统。 kubelet 从节点收集这些信息并将其发布到 Kubernetes API。

心跳

Kubernetes 节点发送的心跳帮助你的集群确定每个节点的可用性,并在检测到故障时采取行动。

对于节点,有两种形式的心跳:

与节点的 .status 更新相比,Lease 是一种轻量级资源。 使用 Lease 来表达心跳在大型集群中可以减少这些更新对性能的影响。

kubelet 负责创建和更新节点的 .status,以及更新它们对应的 Lease。

  • 当节点状态发生变化时,或者在配置的时间间隔内没有更新事件时,kubelet 会更新 .status.status 更新的默认间隔为 5 分钟(比节点不可达事件的 40 秒默认超时时间长很多)。
  • kubelet 会创建并每 10 秒(默认更新间隔时间)更新 Lease 对象。 Lease 的更新独立于节点的 .status 更新而发生。 如果 Lease 的更新操作失败,kubelet 会采用指数回退机制,从 200 毫秒开始重试, 最长重试间隔为 7 秒钟。

6.9 - 网络参考

Kubernetes 文档的这一部分提供了 Kubernetes 网络的参考详细信息。

6.9.1 - Service 所用的协议

如果你配置 Service, 你可以从 Kubernetes 支持的任何网络协议中选择一个协议。

Kubernetes 支持以下协议用于 Service:

当你定义 Service 时, 你还可以指定其使用的应用协议

本文详细说明了一些特殊场景,这些场景通常均使用 TCP 作为传输协议:

支持的协议

Service 端口的 protocol 有 3 个有效值:

SCTP

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

当使用支持 SCTP 流量的网络插件时,你可以为大多数 Service 使用 SCTP。 对于 type: LoadBalancer Service,对 SCTP 的支持情况取决于提供此项设施的云供应商(大部分不支持)。

运行 Windows 的节点不支持 SCTP。

支持多宿主 SCTP 关联

对多宿主 SCTP 关联的支持要求 CNI 插件可以支持为 Pod 分配多个接口和 IP 地址。

针对多宿主 SCTP 关联的 NAT 需要在对应的内核模块具有特殊的逻辑。

TCP

你可以将 TCP 用于任何类别的 Service,这是默认的网络协议。

UDP

你可以将 UDP 用于大多数 Service。对于 type: LoadBalancer Service, UDP 的支持与否取决于提供此项设施的云供应商。

特殊场景

HTTP

如果你的云供应商支持负载均衡,而且尤其是该云供应商的负载均衡器实现了 HTTP/HTTPS 反向代理, 可将流量转发到该 Service 的后端端点,那么你就可以使用 LoadBalancer 模式的 Service 以便在 Kubernetes 集群外部配置负载均衡器。

通常,你将 Service 协议设置为 TCP, 并添加一个注解 (一般取决于你的云供应商)配置负载均衡器,以便在 HTTP 级别处理流量。 此配置也可能包括为你的工作负载提供 HTTPS(基于 TLS 的 HTTP)并反向代理纯 HTTP。

你可能还想指定连接的应用协议http 还是 https。如果从负载均衡器到工作负载的会话是不带 TLS 的 HTTP,请使用 http; 如果从负载均衡器到工作负载的会话使用 TLS 加密,请使用 https

PROXY 协议

如果你的云供应商支持此协议,你可以使用设置为 type: LoadBalancer 的 Service, 在 Kubernetes 本身的外部配置负载均衡器,以转发用 PROXY 协议封装的连接。

负载均衡器然后发送一个初始的八位元组系列来描述传入的连接,这类似于以下示例(PROXY 协议 v1):

PROXY TCP4 192.0.2.202 10.0.42.7 12345 7\r\n

代理协议前导码之后的数据是来自客户端的原始数据。 当任何一侧关闭连接时,负载均衡器也会触发连接关闭并在可行的情况下发送所有残留数据。

通常,你会将 Service 协议定义为 TCP。 你还会设置一个特定于云供应商的注解,将负载均衡器配置为以 PROXY 协议封装所有传入的连接。

TLS

如果你的云供应商支持 TLS,你可以使用设置为 type: LoadBalancer 的 Service 作为设置外部反向代理的一种方式,其中从客户端到负载均衡器的连接是 TLS 加密的且该负载均衡器是 TLS 对等服务器。从负载均衡器到工作负载的连接可以是 TLS,或可能是纯文本。 你可以使用的确切选项取决于你的云供应商或自定义 Service 实现。

通常,你会将协议设置为 TCP 并设置一个注解(通常特定于你的云供应商), 将负载均衡器配置为充当一个 TLS 服务器。你将使用特定于云供应商的机制来配置 TLS 身份 (作为服务器,也可能作为连接到工作负载的客户端)。

6.9.2 - 端口和协议

当你在一个有严格网络边界的环境里运行 Kubernetes,例如拥有物理网络防火墙或者拥有公有云中虚拟网络的自有数据中心, 了解 Kubernetes 组件使用了哪些端口和协议是非常有用的。

控制面

协议方向端口范围目的使用者
TCP入站6443Kubernetes API server所有
TCP入站2379-2380etcd server client APIkube-apiserver, etcd
TCP入站10250Kubelet API自身, 控制面
TCP入站10259kube-scheduler自身
TCP入站10257kube-controller-manager自身

尽管 etcd 的端口也列举在控制面的部分,但你也可以在外部自己托管 etcd 集群或者自定义端口。

工作节点

协议方向端口范围目的使用者
TCP入站10250Kubelet API自身, 控制面
TCP入站30000-32767NodePort Services†所有

NodePort Services的默认端口范围。

所有默认端口都可以重新配置。当使用自定义的端口时,你需要打开这些端口来代替这里提到的默认端口。

一个常见的例子是 API 服务器的端口有时会配置为 443。或者你也可以使用默认端口, 把 API 服务器放到一个监听 443 端口的负载均衡器后面,并且路由所有请求到 API 服务器的默认端口。

6.9.3 - 虚拟 IP 和服务代理

Kubernetes 集群中的每个 节点会运行一个 kube-proxy (除非你已经部署了自己的替换组件来替代 kube-proxy)。

kube-proxy 组件负责除 typeExternalName 以外的服务,实现虚拟 IP 机制。

一个时不时出现的问题是,为什么 Kubernetes 依赖代理将入站流量转发到后端。 其他方案呢?例如,是否可以配置具有多个 A 值(或 IPv6 的 AAAA)的 DNS 记录, 使用轮询域名解析?

使用代理转发方式实现 Service 的原因有以下几个:

  • DNS 的实现不遵守记录的 TTL 约定的历史由来已久,在记录过期后可能仍有结果缓存。
  • 有些应用只做一次 DNS 查询,然后永久缓存结果。
  • 即使应用程序和库进行了适当的重新解析,TTL 取值较低或为零的 DNS 记录可能会给 DNS 带来很大的压力, 从而变得难以管理。

在下文中,你可以了解到 kube-proxy 各种实现方式的工作原理。 总的来说,你应该注意到,在运行 kube-proxy 时, 可能会修改内核级别的规则(例如,可能会创建 iptables 规则), 在某些情况下,这些规则直到重启才会被清理。 因此,运行 kube-proxy 这件事应该只由了解在计算机上使用低级别、特权网络代理服务会带来的后果的管理员执行。 尽管 kube-proxy 可执行文件支持 cleanup 功能,但这个功能并不是官方特性,因此只能根据具体情况使用。

本文中的一些细节会引用这样一个例子: 运行了 3 个 Pod 副本的无状态图像处理后端工作负载。 这些副本是可互换的;前端不需要关心它们调用了哪个后端副本。 即使组成这一组后端程序的 Pod 实际上可能会发生变化, 前端客户端不应该也没必要知道,而且也不需要跟踪这一组后端的状态。

代理模式

kube-proxy 会根据不同配置以不同的模式启动。

在 Linux 节点上,kube-proxy 的可用模式是:

iptables
kube-proxy 在 Linux 上使用 iptables 配置数据包转发规则的一种模式。
ipvs
kube-proxy 使用 ipvs 配置数据包转发规则的一种模式。

Windows 上的 kube-proxy 只有一种模式可用:

kernelspace
kube-proxy 在 Windows 内核中配置数据包转发规则的一种模式。

iptables 代理模式

此代理模式仅适用于 Linux 节点。

在这种模式下,kube-proxy 监视 Kubernetes 控制平面,获知对 Service 和 EndpointSlice 对象的添加和删除操作。 对于每个 Service,kube-proxy 会添加 iptables 规则,这些规则捕获流向 Service 的 clusterIPport 的流量, 并将这些流量重定向到 Service 后端集合中的其中之一。 对于每个端点,它会添加指向一个特定后端 Pod 的 iptables 规则。

默认情况下,iptables 模式下的 kube-proxy 会随机选择一个后端。

使用 iptables 处理流量的系统开销较低,因为流量由 Linux netfilter 处理, 无需在用户空间和内核空间之间切换。这种方案也更为可靠。

如果 kube-proxy 以 iptables 模式运行,并且它选择的第一个 Pod 没有响应, 那么连接会失败。这与用户空间模式不同: 在后者这种情况下,kube-proxy 会检测到与第一个 Pod 的连接失败, 并会自动用不同的后端 Pod 重试。

你可以使用 Pod 就绪探针来验证后端 Pod 是否健康。 这样可以避免 kube-proxy 将流量发送到已知失败的 Pod 上。

iptables 模式下 Service 的虚拟 IP 机制

示例

例如,考虑本页中前面描述的图像处理应用程序。 当创建后端 Service 时,Kubernetes 控制平面会分配一个虚拟 IP 地址,例如 10.0.0.1。 对于这个例子而言,假设 Service 端口是 1234。 集群中的所有 kube-proxy 实例都会观察到新 Service 的创建。

当节点上的 kube-proxy 观察到新 Service 时,它会添加一系列 iptables 规则, 这些规则从虚拟 IP 地址重定向到更多 iptables 规则,每个 Service 都定义了这些规则。 每个 Service 规则链接到每个后端端点的更多规则, 并且每个端点规则将流量重定向(使用目标 NAT)到后端。

当客户端连接到 Service 的虚拟 IP 地址时,iptables 规则会生效。 会选择一个后端(基于会话亲和性或随机选择),并将数据包重定向到后端,无需重写客户端 IP 地址。

当流量通过节点端口或负载均衡器进入时,也会执行相同的基本流程, 只是在这些情况下,客户端 IP 地址会被更改。

优化 iptables 模式性能

在大型集群(有数万个 Pod 和 Service)中,当 Service(或其 EndpointSlice)发生变化时 iptables 模式的 kube-proxy 在更新内核中的规则时可能要用较长时间。 你可以通过(kube-proxy --config <path> 指定的)kube-proxy 配置文件iptables中的选项来调整 kube-proxy 的同步行为:

...
iptables:
  minSyncPeriod: 1s
  syncPeriod: 30s
...
minSyncPeriod

minSyncPeriod 参数设置尝试同步 iptables 规则与内核之间的最短时长。 如果是 0s,那么每次有任一 Service 或 Endpoint 发生变更时,kube-proxy 都会立即同步这些规则。 这种方式在较小的集群中可以工作得很好,但如果在很短的时间内很多东西发生变更时,它会导致大量冗余工作。 例如,如果你有一个由 Deployment 支持的 Service,共有 100 个 Pod,你删除了这个 Deployment, 且设置了 minSyncPeriod: 0s,kube-proxy 最终会从 iptables 规则中逐个删除 Service 的 Endpoint, 总共更新 100 次。使用较大的 minSyncPeriod 值时,多个 Pod 删除事件将被聚合在一起, 因此 kube-proxy 最终可能会进行例如 5 次更新,每次移除 20 个端点, 这样在 CPU 利用率方面更有效率,能够更快地同步所有变更。

minSyncPeriod 的值越大,可以聚合的工作越多, 但缺点是每个独立的变更可能最终要等待整个 minSyncPeriod 周期后才能被处理, 这意味着 iptables 规则要用更多时间才能与当前的 API 服务器状态同步。

默认值 1s 适用于大多数集群,在大型集群中,可能需要将其设置为更大的值。 (特别是,如果 kube-proxy 的 sync_proxy_rules_duration_seconds 指标表明平均时间远大于 1 秒, 那么提高 minSyncPeriod 可能会使更新更有效率。)

更新原有的 minSyncPeriod 配置

旧版本的 kube-proxy 在每次同步时为所有 Service 更新规则; 这在大型集群中会造成性能问题(更新延迟),建议的解决方案是设置较大的 minSyncPeriod。 自 Kubernetes v1.28 开始,kube-proxy 的 iptables 模式采用了更精简的方法, 只有在 Service 或 EndpointSlice 实际发生变化时才会进行更新。

如果你之前覆盖了 minSyncPeriod,你应该尝试删除该覆盖并让 kube-proxy 使用默认值(1s)或至少比升级前使用的值小。

如果你运行的不是 Kubernetes 1.30 版本的 kube-proxy, 请检查你实际运行的版本的行为和相关建议。

syncPeriod

syncPeriod 参数控制与单次 Service 和 EndpointSlice 的变更没有直接关系的少数同步操作。 特别是,它控制 kube-proxy 在外部组件已干涉 kube-proxy 的 iptables 规则时通知的速度。 在大型集群中,kube-proxy 也仅在每隔 syncPeriod 时长执行某些清理操作,以避免不必要的工作。

在大多数情况下,提高 syncPeriod 预计不会对性能产生太大影响, 但在过去,有时将其设置为非常大的值(例如 1h)很有用。 现在不再推荐这种做法,因为它对功能的破坏可能会超过对性能的改进。

IPVS 代理模式

此代理模式仅适用于 Linux 节点。

ipvs 模式下,kube-proxy 监视 Kubernetes Service 和 EndpointSlice, 然后调用 netlink 接口创建 IPVS 规则, 并定期与 Kubernetes Service 和 EndpointSlice 同步 IPVS 规则。 该控制回路确保 IPVS 状态与期望的状态保持一致。 访问 Service 时,IPVS 会将流量导向到某一个后端 Pod。

IPVS 代理模式基于 netfilter 回调函数,类似于 iptables 模式, 但它使用哈希表作为底层数据结构,在内核空间中生效。 这意味着 IPVS 模式下的 kube-proxy 比 iptables 模式下的 kube-proxy 重定向流量的延迟更低,同步代理规则时性能也更好。 与其他代理模式相比,IPVS 模式还支持更高的网络流量吞吐量。

IPVS 为将流量均衡到后端 Pod 提供了更多选择:

  • rr(轮询):流量被平均分发给后端服务器。

  • wrr(加权轮询):流量基于服务器的权重被路由到后端服务器。 高权重的服务器接收新的连接并处理比低权重服务器更多的请求。

  • lc(最少连接):将更多流量分配给活跃连接数较少的服务器。

  • wlc(加权最少连接):将更多流量按照服务器权重分配给连接数较少的服务器,即基于连接数除以权重。

  • lblc(基于地域的最少连接):如果服务器未超载且可用,则针对相同 IP 地址的流量被发送到同一后端服务器; 否则,流量被发送到连接较少的服务器,并在未来的流量分配中保持这一分配决定。

  • lblcr(带副本的基于地域的最少连接):针对相同 IP 地址的流量被发送到连接数最少的服务器。 如果所有后端服务器都超载,则选择连接较少的服务器并将其添加到目标集中。 如果目标集在指定时间内未发生变化,则从此集合中移除负载最高的服务器,以避免副本的负载过高。
  • sh(源哈希):通过查找基于源 IP 地址的静态分配哈希表,将流量发送到某后端服务器。

  • dh(目标哈希):通过查找基于目标地址的静态分配哈希表,将流量发送到某后端服务器。

  • sed(最短预期延迟):流量被转发到具有最短预期延迟的后端服务器。 如果流量被发送给服务器,预期延迟为 (C + 1) / U,其中 C 是服务器上的连接数, U 是服务器的固定服务速率(权重)。

  • nq(永不排队):流量被发送到一台空闲服务器(如果有的话),而不是等待一台快速服务器; 如果所有服务器都忙碌,算法将退回到 sed 行为。

IPVS 模式下 Service 的虚拟 IP 地址机制

kernelspace 代理模式

此代理模式仅适用于 Windows 节点。

kube-proxy 在 Windows 虚拟过滤平台 (VFP)(Windows vSwitch 的扩展)中配置数据包过滤规则。 这些规则处理节点级虚拟网络中的封装数据包,并重写数据包,使目标 IP 地址(和第 2 层信息)正确, 以便将数据包路由到正确的目的地。Windows VFP 类似于 Linux nftablesiptables 等工具。 Windows VFP 是最初为支持虚拟机网络而实现的 Hyper-V Switch 的扩展。

当节点上的 Pod 将流量发送到某虚拟 IP 地址,且 kube-proxy 选择不同节点上的 Pod 作为负载均衡目标时,kernelspace 代理模式会重写该数据包以将其发送到对应目标后端 Pod。 Windows 主机网络服务(HSN)会配置数据包重写规则,确保返回流量看起来来自虚拟 IP 地址, 而不是特定的后端 Pod。

kernelspace 模式的 Direct Server Return(DSR)

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

作为基本操作的替代方案,托管服务后端 Pod 的节点可以直接应用数据包重写, 而不用将此工作交给运行客户端 Pod 的节点来执行。这称为 Direct Server Return(DSR)

要使用这种技术,你必须使用 --enable-dsr 命令行参数运行 kube-proxy 启用 WinDSR 特性门控

即使两个 Pod 在同一节点上运行,DSR 也可优化 Pod 的返回流量。

会话亲和性

在这些代理模型中,绑定到 Service IP:Port 的流量被代理到合适的后端, 客户端不需要知道任何关于 Kubernetes、Service 或 Pod 的信息。

如果要确保来自特定客户端的连接每次都传递给同一个 Pod, 你可以通过设置 Service 的 .spec.sessionAffinityClientIP 来设置基于客户端 IP 地址的会话亲和性(默认为 None)。

会话粘性超时

你还可以通过设置 Service 的 .spec.sessionAffinityConfig.clientIP.timeoutSeconds 来设置最大会话粘性时间(默认值为 10800,即 3 小时)。

将 IP 地址分配给 Service

与实际路由到固定目标的 Pod IP 地址不同,Service IP 实际上不是由单个主机回答的。 相反,kube-proxy 使用数据包处理逻辑(例如 Linux 的 iptables) 来定义虚拟 IP 地址,这些地址会按需被透明重定向。

当客户端连接到 VIP 时,其流量会自动传输到适当的端点。 实际上,Service 的环境变量和 DNS 是根据 Service 的虚拟 IP 地址(和端口)填充的。

避免冲突

Kubernetes 的主要哲学之一是, 你不应需要在完全不是你的问题的情况下面对可能导致你的操作失败的情形。 对于 Service 资源的设计,也就是如果你选择的端口号可能与其他人的选择冲突, 就不应该让你自己选择 IP 地址。这是一种失败隔离。

为了允许你为 Service 选择 IP 地址,我们必须确保没有任何两个 Service 会发生冲突。 Kubernetes 通过从为 API 服务器配置的 service-cluster-ip-range CIDR 范围内为每个 Service 分配自己的 IP 地址来实现这一点。

IP 地址分配追踪

为了确保每个 Service 都获得唯一的 IP,内部分配器在创建每个 Service 之前更新 etcd 中的全局分配映射,这种更新操作具有原子性。 映射对象必须存在于数据库中,这样 Service 才能获得 IP 地址分配, 否则创建将失败,并显示无法分配 IP 地址。

在控制平面中,后台控制器负责创建该映射(从使用内存锁定的旧版本的 Kubernetes 迁移时需要这一映射)。 Kubernetes 还使用控制器来检查无效的分配(例如,因管理员干预而导致无效分配) 以及清理已分配但没有 Service 使用的 IP 地址。

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

如果你启用 MultiCIDRServiceAllocator 特性门控networking.k8s.io/v1alpha1 API 组, 控制平面将用一个新的分配器替换现有的 etcd 分配器,使用 IPAddress 对象而不是内部的全局分配映射。 与每个 Service 关联的 ClusterIP 地址将有一个对应的 IPAddress 对象。

后台控制器也被一个新的控制器取代,来处理新的 IPAddress 对象和从旧的分配器模型的迁移。

新分配器的主要好处之一是它取消了对 service-cluster-ip-range 的大小限制,对 IPv4 没有大小限制, 对于 IPv6 用户可以使用等于或大于 /64 的掩码(以前是 /108)。

用户现在能够检查分配给他们的 Service 的 IP 地址,Kubernetes 扩展, 如 Gateway API 可以使用这个新的 IPAddress 对象类别来增强 Kubernetes 的网络能力,解除内置 Service API 的限制。

kubectl get services
NAME         TYPE        CLUSTER-IP        EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   2001:db8:1:2::1   <none>        443/TCP   3d1h
kubectl get ipaddresses
NAME              PARENTREF
2001:db8:1:2::1   services/default/kubernetes
2001:db8:1:2::a   services/kube-system/kube-dns

Service 虚拟 IP 地址的地址段

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

Kubernetes 根据配置的 service-cluster-ip-range 的大小使用公式 min(max(16, cidrSize / 16), 256)ClusterIP 范围分为两段。 该公式可以解释为:介于 16 和 256 之间,并在上下界之间存在渐进阶梯函数的分配。

Kubernetes 优先通过从高段中选择来为 Service 分配动态 IP 地址, 这意味着如果要将特定 IP 地址分配给 type: ClusterIP Service, 则应手动从段中分配 IP 地址。该方法降低了分配导致冲突的风险。

如果你禁用 ServiceIPStaticSubrange 特性门控, 则 Kubernetes 用于手动分配和动态分配的 IP 共享单个地址池,这适用于 type: ClusterIP 的 Service。

流量策略

你可以设置 .spec.internalTrafficPolicy.spec.externalTrafficPolicy 字段来控制 Kubernetes 如何将流量路由到健康(“就绪”)的后端。

内部流量策略

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

你可以设置 .spec.internalTrafficPolicy 字段来控制来自内部源的流量如何被路由。 有效值为 ClusterLocal。 将字段设置为 Cluster 会将内部流量路由到所有准备就绪的端点, 将字段设置为 Local 仅会将流量路由到本地节点准备就绪的端点。 如果流量策略为 Local 但没有本地节点端点,那么 kube-proxy 会丢弃该流量。

外部流量策略

你可以设置 .spec.externalTrafficPolicy 字段来控制从外部源路由的流量。 有效值为 ClusterLocal。 将字段设置为 Cluster 会将外部流量路由到所有准备就绪的端点, 将字段设置为 Local 仅会将流量路由到本地节点上准备就绪的端点。 如果流量策略为 Local 并且没有本地节点端点, 那么 kube-proxy 不会转发与相关 Service 相关的任何流量。

流向正终止的端点的流量

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

如果为 kube-proxy 启用了 ProxyTerminatingEndpoints 特性门控且流量策略为 Local, 则节点的 kube-proxy 将使用更复杂的算法为 Service 选择端点。 启用此特性时,kube-proxy 会检查节点是否具有本地端点以及是否所有本地端点都标记为正在终止过程中。 如果有本地端点并且所有本地端点都被标记为处于终止过程中, 则 kube-proxy 会将转发流量到这些正在终止过程中的端点。 否则,kube-proxy 会始终选择将流量转发到并未处于终止过程中的端点。

这种对处于终止过程中的端点的转发行为使得 NodePortLoadBalancer Service 能有条不紊地腾空设置了 externalTrafficPolicy: Local 时的连接。

当一个 Deployment 被滚动更新时,处于负载均衡器后端的节点可能会将该 Deployment 的 N 个副本缩减到 0 个副本。在某些情况下,外部负载均衡器可能在两次执行健康检查探针之间将流量发送到具有 0 个副本的节点。 将流量路由到处于终止过程中的端点可确保正在缩减 Pod 的节点能够正常接收流量, 并逐渐降低指向那些处于终止过程中的 Pod 的流量。 到 Pod 完成终止时,外部负载均衡器应该已经发现节点的健康检查失败并从后端池中完全移除该节点。

接下来

要了解有关 Service 的更多信息, 请阅读使用 Service 连接应用

也可以:

6.10 - 安装工具

6.10.1 - Kubeadm

Kubeadm 是一个提供了 kubeadm initkubeadm join 的工具, 作为创建 Kubernetes 集群的 “快捷途径” 的最佳实践。

kubeadm 通过执行必要的操作来启动和运行最小可用集群。 按照设计,它只关注启动引导,而非配置机器。同样的, 安装各种 “锦上添花” 的扩展,例如 Kubernetes Dashboard、 监控方案、以及特定云平台的扩展,都不在讨论范围内。

相反,我们希望在 kubeadm 之上构建更高级别以及更加合规的工具, 理想情况下,使用 kubeadm 作为所有部署工作的基准将会更加易于创建一致性集群。

如何安装

要安装 kubeadm, 请查阅 安装指南.

接下来

6.10.1.1 - 创建 Kubeadm

6.10.1.1.1 -

kubeadm:轻松创建一个安全的 Kubernetes 集群

摘要

┌──────────────────────────────────────────────────────────┐
│ KUBEADM                                                  │
│ 轻松创建一个安全的 Kubernetes 集群                       │
│                                                          │
│ 给我们反馈意见的地址:                                   │
│ https://github.com/kubernetes/kubeadm/issues             │
└──────────────────────────────────────────────────────────┘

用途示例:

创建一个有两台机器的集群,包含一个主节点(用来控制集群)和一个工作节点(运行你的 Pod 和 Deployment 等工作负载)。

┌──────────────────────────────────────────────────────────┐
│ 在第一台机器上:                                            │
├──────────────────────────────────────────────────────────┤
│ control-plane# kubeadm init                              │
└──────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────┐
│ 在第二台机器上:                                            │
├──────────────────────────────────────────────────────────┤
│ worker# kubeadm join &lt;arguments-returned-from-init&gt;│
└──────────────────────────────────────────────────────────┘

你可以重复第二步,向集群添加更多机器。

选项

-h, --help

kubeadm 操作的帮助信息。

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

6.10.1.1.2 -

处理 Kubernetes 证书的相关命令。

概要

处理 Kubernetes 证书相关的命令。

kubeadm certs [flags]

选项

-h, --help

certs 操作的帮助命令。

继承于父命令的选项

--rootfs string

[实验] 到'真实'主机根文件系统的路径。

6.10.1.1.3 -

生成证书密钥。

概要

该命令将打印出可以与 "init" 命令一起使用的安全的随机生成的证书密钥。

你也可以使用 kubeadm init --upload-certs 而无需指定证书密钥; 此命令将为你生成并打印一个证书密钥。

kubeadm certs certificate-key [flags]

选项

-h, --help

certificate-key 操作的帮助命令。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.4 -

为一个 Kubernetes 集群检查证书的到期时间。

概要

检查 kubeadm 管理的本地 PKI 中证书的到期时间。

kubeadm certs check-expiration [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存证书的路径。

--config string

到 kubeadm 配置文件的路径。

-h, --help

check-expiration 操作的帮助命令。

--kubeconfig string     默认为:"/etc/kubernetes/admin.conf"

在和集群连接时使用该 kubeconfig 文件。 如果此标志未被设置,那么将会在一些标准的位置去搜索存在的 kubeconfig 文件。

继承于父命令的选项

--rootfs string

[实验] 到'真实'主机根文件系统的路径。

6.10.1.1.5 -

生成密钥和证书签名请求

概要

为运行控制平面所需的所有证书生成密钥和证书签名请求(CSR)。该命令会生成部分 kubeconfig 文件, 其中 "users > user > client-key-data" 字段包含私钥数据,并为每个 kubeconfig 文件创建一个随附的 ".csr" 文件。

此命令设计用于 Kubeadm 外部 CA 模式。 它生成你可以提交给外部证书颁发机构进行签名的 CSR。

你需要使用 ".crt" 作为文件扩展名将 PEM 编码的签名证书与密钥文件一起保存。 或者,对于 kubeconfig 文件,PEM 编码的签名证书应使用 base64 编码, 并添加到 "users > user > client-certificate-data" 字段。

kubeadm certs generate-csr [flags]

示例

# 以下命令将为所有控制平面证书和 kubeconfig 文件生成密钥和 CSR:
kubeadm certs generate-csr --kubeconfig-dir /tmp/etc-k8s --cert-dir /tmp/etc-k8s/pki

选项

--cert-dir string

保存证书的路径。

--config string

到 kubeadm 配置文件的路径。

-h, --help

generate-csr 操作的帮助命令。

--kubeconfig-dir string     默认值:"/etc/kubernetes"

保存 kubeconfig 文件的路径。

继承于父命令的选项

--rootfs string

[实验] 到'真实'主机根文件系统的路径。

6.10.1.1.6 -

为 Kubernetes 集群更新证书

概要

此命令并非设计用来单独运行。请参阅可用子命令列表。

kubeadm certs renew [flags]

选项

-h, --help

renew 操作的帮助命令

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.7 -

续订 kubeconfig 文件中嵌入的证书,供管理员和 kubeadm 自身使用。

概要

续订 kubeconfig 文件中嵌入的证书,供管理员和 kubeadm 自身使用。

无论证书的到期日期如何,续订都是无条件进行的;SAN 等额外属性将基于现有文件/证书,因此无需重新提供它们。

默认情况下,续订会尝试使用由 kubeadm 管理的本地 PKI 中的证书机构;作为替代方案, 也可以使用 K8s 证书 API 进行证书续订,或者(作为最后一种选择)生成 CSR 请求。

续订后,为了使更改生效,需要重新启动控制平面组件,并最终重新分发更新的证书,以防证书文件在其他地方使用。

kubeadm certs renew admin.conf [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存证书的路径。

--config string

到 kubeadm 配置文件的路径。

-h, --help

admin.conf 操作的帮助命令。

--kubeconfig string     Default: "/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。 如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

--use-api
使用 Kubernetes 证书 API 续订证书。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.8 -

续订所有可用证书。

概要

续订运行控制平面所需的所有已知证书。续订是无条件进行的,与到期日期无关。续订也可以单独运行以进行更多控制。

kubeadm certs renew all [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

存储证书的路径。

--config string

kubeadm 配置文件的路径。

-h, --help

all 操作的帮助命令。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。 如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

--use-api
使用 Kubernetes 证书 API 续订证书。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.9 -

续订 apiserver 用于访问 etcd 的证书。

概要

续订 apiserver 用于访问 etcd 的证书。

无论证书的到期日期如何,续订都会无条件地进行;SAN 等额外属性将基于现有文件/证书,因此无需重新提供它们。

默认情况下,续订尝试使用在 kubeadm 所管理的本地 PKI 中的证书颁发机构;作为替代方案, 可以使用 K8s 证书 API 进行证书更新,或者作为最后一个选择来生成 CSR 请求。

续订后,为了使更改生效,需要重新启动控制平面组件,并最终重新分发更新的证书,以防文件在其他地方使用。

kubeadm certs renew apiserver-etcd-client [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

存储证书的路径。

--config string

kubeadm 配置文件的路径。

-h, --help

apiserver-etcd-client 操作的帮助命令。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。 如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.10 -

续订 apiserver 用于连接 kubelet 的证书。

概要

续订 apiserver 用于连接 kubelet 的证书。

无论证书的到期日期如何,续订都会无条件地进行;SAN 等额外属性将基于现有文件/证书,因此无需重新提供它们。

默认情况下,续订尝试使用位于 kubeadm 所管理的本地 PKI 中的证书颁发机构;作为替代方案, 也可能调用 K8s 证书 API 进行证书更新;亦或者,作为最后一个选择,生成 CSR 请求。

续订后,为了使更改生效,需要重新启动控制平面组件,并最终重新分发更新的证书,以防文件在其他地方使用。

kubeadm certs renew apiserver-kubelet-client [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

存储证书的路径。

--config string

kubeadm 配置文件的路径。

-h, --help

apiserver-kubelet-client 操作的帮助命令。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。 如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

--use-api
使用 Kubernetes 证书 API 续订证书。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.11 -

续订用于提供 Kubernetes API 的证书。

概要

续订用于提供 Kubernetes API 的证书。

无论证书的到期日期如何,续订都会无条件地进行;SAN 等额外属性将基于现有文件/证书,因此无需重新提供它们。

默认情况下,续订尝试在 kubeadm 管理的本地 PKI 中使用证书颁发机构;作为替代方案, 可以使用 K8s 证书 API 进行证书更新,或者作为最后一个选择来生成 CSR 请求。

续订后,为了使更改生效,需要重新启动控制平面组件,并最终重新分发更新的证书,以防文件在其他地方使用。

kubeadm certs renew apiserver [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存证书的路径。

--config string

kubeadm 配置文件的路径。

-h, --help

apiserver 子操作的帮助命令。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。 如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.12 -

续订 kubeconfig 文件中嵌入的证书,以供控制器管理器(Controller Manager)使用。

概要

续订 kubeconfig 文件中嵌入的证书,以供控制器管理器(Controller Manager)使用。

续订无条件地进行,与证书的到期日期无关;SAN 等额外属性将基于现有的文件/证书,因此无需重新提供它们。

默认情况下,续订会尝试使用 kubeadm 管理的本地 PKI 中的证书颁发机构;作为替代方案, 可以使用 K8s 证书 API 进行证书续订;亦或者,作为最后一种选择,生成 CSR 请求。

续订后,为了使更改生效,需要重新启动控制平面组件,并最终重新分发更新的证书,以防文件在其他地方使用。

kubeadm certs renew controller-manager.conf [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存证书的路径。

--config string

kubeadm 配置文件的路径。

-h, --help

controller-manager.conf 操作的帮助命令。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。 如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

--use-api
使用 Kubernetes 证书 API 续订证书。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.13 -

续订存活态探针的证书,用于对 etcd 执行健康检查。

概要

续订存活态探针的证书,用于对 etcd 执行健康检查。

无论证书的到期日期如何,续订都是无条件进行的;SAN 等额外属性将基于现有文件/证书,因此无需重新提供它们。

默认情况下,续订会尝试使用由 kubeadm 管理的本地 PKI 中的证书机构;作为替代方案, 也可以使用 K8s certificate API 进行证书续订,或者(作为最后一种选择)生成 CSR 请求。

续订后,为了使更改生效,需要重新启动控制平面组件,并最终重新分发更新的证书,以防证书文件在其他地方使用。

kubeadm certs renew etcd-healthcheck-client [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存证书的路径。

--config string

kubeadm 配置文件的路径。

-h, --help

etcd-healthcheck-client 操作的帮助命令。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。 如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.14 -

续订 etcd 节点间用来相互通信的证书。

概要

续订 etcd 节点间用来相互通信的证书。

无论证书的到期日期如何,续订都是无条件进行的;SAN 等额外属性将基于现有文件/证书,因此无需重新提供它们。

默认情况下,续订会尝试使用由 kubeadm 管理的本地 PKI 中的证书机构; 作为替代方案,也可以使用 K8s certificate API 进行证书续订,或者(作为最后一种选择)生成 CSR 请求。

续订后,为了使更改生效,需要重新启动控制平面组件,并最终重新分发续订的证书,以防证书文件在其他地方使用。

kubeadm certs renew etcd-peer [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存证书的路径。

--config string

kubeadm 配置文件的路径。

-h, --help
etcd-peer 操作的帮助命令。
--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。 如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

--use-api

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.15 -

续订用于提供 etcd 服务的证书。

概要

续订用于提供 etcd 服务的证书。

续订无条件地进行,与证书的到期日期无关;SAN 等额外属性将基于现有的文件/证书,因此无需重新提供它们。

默认情况下,续订会尝试在 kubeadm 管理的本地 PKI 中使用证书颁发机构;作为替代方案, 可以使用 K8s 证书 API 进行证书续订,或者作为最后一种选择来生成 CSR 请求。

续订后,为了使更改生效,需要重新启动控制平面组件,并最终重新分发续订的证书,以防文件在其他地方使用。

kubeadm certs renew etcd-server [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存证书的路径。

--config string

kubeadm 配置文件的路径。

-h, --help

etcd-server 操作的帮助命令。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。 如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

--use-api

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.16 -

为前端代理客户端续订证书。

概要

为前端代理客户端续订证书。

无论证书的到期日期如何,续订都会无条件地进行;SAN 等额外属性将基于现有文件/证书,因此无需重新提供它们。

默认情况下,续订尝试使用位于 kubeadm 所管理的本地 PKI 中的证书颁发机构;作为替代方案, 也可以使用 K8s certificate API 进行证书续订;亦或者,作为最后一种方案,生成 CSR 请求。

续订后,为了使更改生效,需要重新启动控制平面组件,并最终重新分发更新的证书,以防文件在其他地方使用。

kubeadm certs renew front-proxy-client [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

存储证书的路径。

--config string

kubeadm 配置文件的路径。

-h, --help

front-proxy-client 操作的帮助命令。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。 如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

--use-api
使用 Kubernetes certificate API 续订证书。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.17 -

续订 kubeconfig 文件中嵌入的证书,以供调度管理器使用。

概要

续订 kubeconfig 文件中嵌入的证书,以供调度管理器使用。

续订无条件地进行,与证书的到期日期无关;SAN 等额外属性将基于现有的文件/证书,因此无需重新提供它们。

默认情况下,续订会尝试使用在 kubeadm 所管理的本地 PKI 中的证书颁发机构;作为替代方案, 也可以使用 K8s certificate API 进行证书续订;亦或者,作为最后一种选择,生成 CSR 请求。

续订后,为了使更改生效,需要重新启动控制平面组件,并最终重新分发更新的证书,以防文件在其他地方使用。

kubeadm certs renew scheduler.conf [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存证书的路径。

--config string

kubeadm 配置文件的路径。

-h, --help

scheduler.conf 操作的帮助命令。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。 如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.18 -

为 super-admin 对嵌入于 kubeconfig 文件中的证书续期。

概要

为 super-admin 对嵌入于 kubeconfig 文件中的证书续期。

续期操作将无条件进行,不论证书的到期日期是何时;诸如 SAN 之类的额外属性将基于现有文件/证书,无需重新提供。

默认情况下,续期会尝试使用由 kubeadm 管理的本地 PKI 中的证书颁发机构; 作为替代方案,可以使用 K8s 证书 API 进行证书续期,或者作为最后的选项,生成一个 CSR 请求。

续期后,为了使更改生效,需要重启控制平面组件,并且如果该文件在其他地方使用,最终需要重新分发续期后的证书。

kubeadm certs renew super-admin.conf [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存证书的路径。

--config string

kubeadm 配置文件的路径。

-h, --help

super-admin.conf 的帮助信息。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。 如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

从父命令继承的选项

--rootfs string

[实验性功能] 指向‘真实’宿主根文件系统的路径。

6.10.1.1.19 -

为指定的 shell(bash 或 zsh)输出 shell 补全代码。

概要

为指定的 shell(bash 或 zsh)输出 shell 补全代码。 必须激活 shell 代码以提供交互式 kubeadm 命令补全。这可以通过加载 .bash_profile 文件完成。

注意:此功能依赖于 bash-completion 框架。

在 Mac 上使用 homebrew 安装:

brew install bash-completion

安装后,必须激活 bash_completion。这可以通过在 .bash_profile 文件中添加下面的命令行来完成:

source $(brew --prefix)/etc/bash_completion

如果在 Linux 上没有安装 bash-completion,请通过你的发行版的包管理器安装 bash-completion 软件包。

zsh 用户注意事项:[1] zsh 自动补全仅在 v5.2 及以上版本中支持。

kubeadm completion SHELL [flags]

示例

# 在 Mac 上使用 homebrew 安装 bash completion
brew install bash-completion
printf "\n# Bash completion support\nsource $(brew --prefix)/etc/bash_completion\n" >> $HOME/.bash_profile
source $HOME/.bash_profile

# 将 bash 版本的 kubeadm 自动补全代码加载到当前 shell 中
source <(kubeadm completion bash)

# 将 bash 自动补全完成代码写入文件并且从 .bash_profile 文件加载它
printf "\n# Kubeadm shell completion\nsource '$HOME/.kube/kubeadm_completion.bash.inc'\n" >> $HOME/.bash_profile
source $HOME/.bash_profile

# 将 zsh 版本的 kubeadm 自动补全代码加载到当前 shell 中
source <(kubeadm completion zsh)

选项

-h, --help

completion 操作的帮助命令。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.20 -

管理持久化在 ConfigMap 中的 kubeadm 集群的配置。

概要

kube-system 命名空间里有一个名为 "kubeadm-config" 的 ConfigMap,kubeadm 用它来存储有关集群的内部配置。 kubeadm CLI v1.8.0+ 通过一个配置自动创建该 ConfigMap,这个配置是和 'kubeadm init' 共用的。 但是你如果使用 kubeadm v1.7.x 或更低的版本初始化集群,那么必须使用 'config upload' 命令创建此 ConfigMap。 这是必要的操作,目的是使 'kubeadm upgrade' 能够正确地配置升级后的集群。

kubeadm config [flags]

选项

-h, --help

config 操作的帮助命令。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

用于和集群通信的 kubeconfig 文件。如果它没有被设置,那么 kubeadm 将会搜索一个已经存在于标准路径的 kubeconfig 文件。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.21 -

与 kubeadm 使用的容器镜像交互

概要

与 kubeadm 使用的容器镜像交互。

kubeadm config images [flags]

选项

-h, --help

images 的帮助命令。

从父命令继承的选项

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

用于和集群通信的 kubeconfig 文件。如果它没有被设置,那么 kubeadm 将会搜索一个已经存在于标准路径的 kubeconfig 文件。

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.22 -

打印 kubeadm 要使用的镜像列表。配置文件用于自定义镜像或镜像存储库。

概要

打印 kubeadm 要使用的镜像列表。配置文件用于自定义镜像或镜像存储库。

kubeadm config images list [flags]

选项

--allow-missing-template-keys     默认值:true

如果设置为 true,则在模板中缺少字段或哈希表的键时忽略模板中的任何错误。 仅适用于 golang 和 jsonpath 输出格式。

--config string

kubeadm 配置文件的路径。

-o, --experimental-output string     默认值:"text"

输出格式:text|json|yaml|go-template|go-template-file|template|templatefile|jsonpath|jsonpath-as-json|jsonpath-file 其中之一。

--feature-gates string

一组键值对(key=value),用于描述各种特性。这些选项是:
EtcdLearnerMode=true|false (ALPHA - 默认值=false)
PublicKeysECDSA=true|false (ALPHA - 默认值=false)
RootlessControlPlane=true|false (ALPHA - 默认值=false)
UpgradeAddonsBeforeControlPlane=true|false (DEPRECATED - 默认值=false)

-h, --help

list 操作的帮助命令。

--image-repository string     默认值:"registry.k8s.io"

选择要从中拉取控制平面镜像的容器仓库。

--kubernetes-version string     默认值:"stable-1"

为控制平面选择一个特定的 Kubernetes 版本。

--show-managed-fields

如果为 true,则在以 JSON 或 YAML 格式打印对象时保留 managedFields。

从父命令继承的选项

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

用于和集群通信的 kubeconfig 文件。如果它没有被设置,那么 kubeadm 将会搜索一个已经存在于标准路径的 kubeconfig 文件。

--rootfs string

[实验] 到“真实”主机根文件系统的路径。

6.10.1.1.23 -

拉取 kubeadm 使用的镜像。

概要

拉取 kubeadm 使用的镜像。

kubeadm config images pull [flags]

选项

--config string

kubeadm 配置文件的路径。

--cri-socket string

要连接的 CRI 套接字的路径。如果为空,则 kubeadm 将尝试自动检测此值;仅当安装了多个 CRI 或具有非标准 CRI 插槽时,才使用此选项。

--feature-gates string
一系列键值对(key=value),用于描述各种特性。可选项是:
EtcdLearnerMode=true|false (ALPHA - 默认值=false)
PublicKeysECDSA=true|false (ALPHA - 默认值=false)
RootlessControlPlane=true|false (ALPHA - 默认值=false)
UpgradeAddonsBeforeControlPlane=true|false (DEPRECATED - 默认值=false)
-h, --help

pull 操作的帮助命令。

--image-repository string     默认值:"registry.k8s.io"

选择用于拉取控制平面镜像的容器仓库。

--kubernetes-version string     默认值:"stable-1"

为控制平面选择一个特定的 Kubernetes 版本。

从父命令继承的选项

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

用于和集群通信的 kubeconfig 文件。如果它没有被设置,那么 kubeadm 将会搜索一个已经存在于标准路径的 kubeconfig 文件。

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.24 -

从文件中读取旧版本的 kubeadm 配置的 API 类型,并为新版本输出类似的配置对象

概要

此命令允许你在 CLI 工具中将本地旧版本的配置对象转换为最新支持的版本,而无需变更集群中的任何内容。 在此版本的 kubeadm 中,支持以下 API 版本:

  • kubeadm.k8s.io/v1beta3

因此,无论你在此处传递 --old-config 参数的版本是什么,当写入到 stdout 或 --new-config (如果已指定)时, 都会读取、反序列化、默认、转换、验证和重新序列化 API 对象。

换句话说,如果你将此文件传递给 "kubeadm init",则该命令的输出就是 kubeadm 实际上在内部读取的内容。

kubeadm config migrate [flags]

选项

-h, --help

migrate 操作的帮助信息。

--new-config string

使用新的 API 版本生成的 kubeadm 配置文件的路径。这个路径是可选的。如果没有指定,输出将被写到 stdout。

--old-config string

使用旧 API 版本且应转换的 kubeadm 配置文件的路径。此参数是必需的。

从父命令继承的选项

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

用于和集群通信的 kubeconfig 文件。如果未设置,那么 kubeadm 将会搜索一个已经存在于标准路径的 kubeconfig 文件。

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.25 -

打印配置

概要

此命令打印子命令所提供的配置信息。相关细节可参阅: https://pkg.go.dev/k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm#section-directories

kubeadm config print [flags]

选项

-h, --help

print 命令的帮助信息。

从父命令继承而来的选项

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。如此标志未设置,将在一组标准位置中搜索现有的 kubeconfig 文件。

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.26 -

打印用于 'kubeadm init' 的默认 init 配置。

概要

此命令打印对象,例如用于 'kubeadm init' 的默认 init 配置对象。

请注意,Bootstrap Token 字段之类的敏感值已替换为 "abcdef.0123456789abcdef" 之类的占位符值以通过验证,但不执行创建令牌的实际计算。

kubeadm config print init-defaults [flags]

选项

--component-configs strings

以逗号分隔的组件配置 API 对象的列表,打印其默认值。可用值:[KubeProxyConfiguration KubeletConfiguration]。 如果未设置此参数,则不会打印任何组件配置。

-h, --help

init-defaults 操作的帮助命令。

从父命令继承的选项

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.27 -

打印默认的节点添加配置,该配置可用于 'kubeadm join' 命令。

概要

此命令打印对象,例如用于 'kubeadm join' 的默认 join 配置对象。

请注意,诸如启动引导令牌字段之类的敏感值已替换为 "abcdef.0123456789abcdef" 之类的占位符值以通过验证, 但不执行创建令牌的实际计算。

kubeadm config print join-defaults [flags]

选项

-h, --help

join-defaults 操作的帮助命令。

从父命令继承的选项

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.28 -

打印默认的 reset 配置,该配置可用于 'kubeadm reset' 命令。

概要

此命令打印 'kubeadm reset' 所用的默认 reset 配置等这类对象。

请注意,诸如启动引导令牌(Bootstrap Token)字段这类敏感值已替换为 "abcdef.0123456789abcdef" 这类占位符值用来通过合法性检查,但不执行创建令牌的实际计算。

kubeadm config print reset-defaults [flags]

选项

-h, --help

reset-defaults 操作的帮助命令。

从父命令继承的选项

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时所使用的 kubeconfig 文件。 如果该参数未被设置,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

--rootfs string

[试验性] 指向“真实”主机根文件系统的路径。

6.10.1.1.29 -

读取包含 kubeadm 配置 API 的文件,并报告所有验证问题。

概要

这个命令允许你验证 kubeadm 配置 API 文件并报告所有警告和错误。 如果没有错误,退出状态将为零;否则,将为非零。 诸如未知 API 字段等任何解包问题都会触发错误。 未知的 API 版本和具有无效值的字段也会触发错误。 根据输入文件的内容,可能会报告任何其他错误或警告。

在这个版本的 kubeadm 中,支持以下 API 版本:

  • kubeadm.k8s.io/v1beta3
kubeadm config validate [flags]

选项

--allow-experimental-api

允许验证试验性的、未发布的 API。

--config string

指向 kubeadm 配置文件的路径。

-h, --help

validate 的帮助命令。

从父命令继承而来的选项

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

在与集群通信时要使用的 kubeconfig 文件。 如果此标志未被设置,则会在一组标准位置中搜索现有的 kubeconfig 文件。

--rootfs string

[试验性] 指向“真实”主机根文件系统的路径。

6.10.1.1.30 -

运行此命令以安装 Kubernetes 控制平面。

概要

运行此命令来搭建 Kubernetes 控制平面节点。

"init" 命令执行以下阶段:

preflight                    Run pre-flight checks
certs                        Certificate generation
  /ca                          Generate the self-signed Kubernetes CA to provision identities for other Kubernetes components
  /apiserver                   Generate the certificate for serving the Kubernetes API
  /apiserver-kubelet-client    Generate the certificate for the API server to connect to kubelet
  /front-proxy-ca              Generate the self-signed CA to provision identities for front proxy
  /front-proxy-client          Generate the certificate for the front proxy client
  /etcd-ca                     Generate the self-signed CA to provision identities for etcd
  /etcd-server                 Generate the certificate for serving etcd
  /etcd-peer                   Generate the certificate for etcd nodes to communicate with each other
  /etcd-healthcheck-client     Generate the certificate for liveness probes to healthcheck etcd
  /apiserver-etcd-client       Generate the certificate the apiserver uses to access etcd
  /sa                          Generate a private key for signing service account tokens along with its public key
kubeconfig                   Generate all kubeconfig files necessary to establish the control plane and the admin kubeconfig file
  /admin                       Generate a kubeconfig file for the admin to use and for kubeadm itself
  /kubelet                     Generate a kubeconfig file for the kubelet to use *only* for cluster bootstrapping purposes
  /controller-manager          Generate a kubeconfig file for the controller manager to use
  /scheduler                   Generate a kubeconfig file for the scheduler to use
etcd                         Generate static Pod manifest file for local etcd
  /local                       Generate the static Pod manifest file for a local, single-node local etcd instance
control-plane                Generate all static Pod manifest files necessary to establish the control plane
  /apiserver                   Generates the kube-apiserver static Pod manifest
  /controller-manager          Generates the kube-controller-manager static Pod manifest
  /scheduler                   Generates the kube-scheduler static Pod manifest
kubelet-start                Write kubelet settings and (re)start the kubelet
upload-config                Upload the kubeadm and kubelet configuration to a ConfigMap
  /kubeadm                     Upload the kubeadm ClusterConfiguration to a ConfigMap
  /kubelet                     Upload the kubelet component config to a ConfigMap
upload-certs                 Upload certificates to kubeadm-certs
mark-control-plane           Mark a node as a control-plane
bootstrap-token              Generates bootstrap tokens used to join a node to a cluster
kubelet-finalize             Updates settings relevant to the kubelet after TLS bootstrap
  /experimental-cert-rotation  Enable kubelet client certificate rotation
addon                        Install required addons for passing conformance tests
  /coredns                     Install the CoreDNS addon to a Kubernetes cluster
  /kube-proxy                  Install the kube-proxy addon to a Kubernetes cluster
show-join-command            Show the join command for control-plane and worker node
kubeadm init [flags]

选项

--apiserver-advertise-address string

API 服务器所公布的其正在监听的 IP 地址。如果未设置,则使用默认网络接口。

--apiserver-bind-port int32     默认值:6443

API 服务器绑定的端口。

--apiserver-cert-extra-sans strings

用于 API Server 服务证书的可选附加主题备用名称(SAN)。可以是 IP 地址和 DNS 名称。

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存和存储证书的路径。

--certificate-key string

用于加密 kubeadm-certs Secret 中的控制平面证书的密钥。

--config string

kubeadm 配置文件的路径。

--control-plane-endpoint string

为控制平面指定一个稳定的 IP 地址或 DNS 名称。

--cri-socket string

要连接的 CRI 套接字的路径。如果为空,则 kubeadm 将尝试自动检测此值; 仅当安装了多个 CRI 或具有非标准 CRI 套接字时,才使用此选项。

--dry-run

不做任何更改;只输出将要执行的操作。

--feature-gates string
一组用来描述各种功能特性的键值(key=value)对。选项是:
EtcdLearnerMode=true|false (ALPHA - 默认值=false)
PublicKeysECDSA=true|false (ALPHA - 默认值=false)
RootlessControlPlane=true|false (ALPHA - 默认值=false)
UpgradeAddonsBeforeControlPlane=true|false (DEPRECATED - 默认值=false)
-h, --help

init 操作的帮助命令。

--ignore-preflight-errors strings

错误将显示为警告的检查列表;例如:'IsPrivilegedUser,Swap'。取值为 'all' 时将忽略检查中的所有错误。

--image-repository string     默认值:"registry.k8s.io"

选择用于拉取控制平面镜像的容器仓库。

--kubernetes-version string     默认值:"stable-1"

为控制平面选择一个特定的 Kubernetes 版本。

--node-name string

指定节点的名称。

--patches string

它包含名为 "target[suffix][+patchtype].extension" 的文件的目录的路径。 例如,"kube-apiserver0+merge.yaml"或仅仅是 "etcd.json"。 "target" 可以是 "kube-apiserver"、"kube-controller-manager"、"kube-scheduler"、"etcd"、"kubeletconfiguration" 之一。 "patchtype" 可以是 "strategic"、"merge" 或者 "json" 之一, 并且它们与 kubectl 支持的补丁格式相同。 默认的 "patchtype" 是 "strategic"。 "extension" 必须是"json" 或"yaml"。 "suffix" 是一个可选字符串,可用于确定首先按字母顺序应用哪些补丁。

--pod-network-cidr string

指明 Pod 网络可以使用的 IP 地址段。如果设置了这个参数,控制平面将会为每一个节点自动分配 CIDR。

--service-cidr string     默认值:"10.96.0.0/12"

为服务的虚拟 IP 地址另外指定 IP 地址段。

--service-dns-domain string     默认值:"cluster.local"
为服务另外指定域名,例如:"myorg.internal"。
--skip-certificate-key-print

不要打印用于加密控制平面证书的密钥。

--skip-phases strings

要跳过的阶段列表。

--skip-token-print

跳过打印 'kubeadm init' 生成的默认引导令牌。

--token string

这个令牌用于建立控制平面节点与工作节点间的双向通信。 格式为 [a-z0-9]{6}.[a-z0-9]{16} - 示例:abcdef.0123456789abcdef

--token-ttl duration     默认值:24h0m0s

令牌被自动删除之前的持续时间(例如 1s,2m,3h)。如果设置为 '0',则令牌将永不过期。

--upload-certs

将控制平面证书上传到 kubeadm-certs Secret。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.31 -

使用此命令可以调用 init 工作流程的单个阶段。

概要

使用此命令可以调用 init 工作流程的单个阶段。

选项

-h, --help

phase 操作的帮助命令。

继承于父命令的选择项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.32 -

安装必要的插件以通过一致性测试。

概要

此命令并非设计用来单独运行。请参阅可用子命令列表。

kubeadm init phase addon [flags]

选项

-h, --help

addon 操作的帮助命令。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.33 -

安装所有插件。

概要

安装所有插件(addon)。

kubeadm init phase addon all [flags]

选项

--apiserver-advertise-address string

API 服务器所公布的其正在监听的 IP 地址。如果未设置,则将使用默认网络接口。

--apiserver-bind-port int32     默认值:6443

API 服务器绑定的端口。

--config string

kubeadm 配置文件的路径。

--control-plane-endpoint string

为控制平面指定一个稳定的 IP 地址或 DNS 名称。

--dry-run

不做任何更改;只输出将要执行的操作。

--feature-gates string
一组键值对(key=value),描述了各种特征。选项包括:
EtcdLearnerMode=true|false (ALPHA - 默认值=false)
PublicKeysECDSA=true|false (ALPHA - 默认值=false)
RootlessControlPlane=true|false (ALPHA - 默认值=false)
UpgradeAddonsBeforeControlPlane=true|false (DEPRECATED - 默认值=false)
-h, --help

all 操作的帮助命令。

--image-repository string     默认值:"registry.k8s.io"

选择用于拉取控制平面镜像的容器仓库。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

--kubernetes-version string     默认值:"stable-1"

为控制平面选择特定的 Kubernetes 版本。

--pod-network-cidr string

指定 Pod 网络的 IP 地址范围。如果已设置,控制平面将自动为每个节点分配 CIDR。

--service-cidr string     默认值:"10.96.0.0/12"

为服务 VIP 使用 IP 地址的其他范围。

--service-dns-domain string     默认值:"cluster.local"
为服务使用其他域名,例如 "myorg.internal"。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.34 -

将 CoreDNS 插件安装到 Kubernetes 集群。

概要

通过 API 服务器安装 CoreDNS 附加组件。请注意,即使 DNS 服务器已部署,在安装 CNI 之前 DNS 服务器不会被调度执行。

kubeadm init phase addon coredns [flags]

选项

--config string

kubeadm 配置文件的路径。

--dry-run

不做任何更改;只输出将要执行的操作。

--feature-gates string

一组用来描述各种特性门控的键值(key=value)对。选项是:
EtcdLearnerMode=true|false (ALPHA - 默认值=false)
PublicKeysECDSA=true|false (ALPHA - 默认值=false)
RootlessControlPlane=true|false (ALPHA - 默认值=false)
UpgradeAddonsBeforeControlPlane=true|false (DEPRECATED - 默认值=false)

-h, --help

coredns 操作的帮助命令。

--image-repository string     默认值:"registry.k8s.io"

选择用于拉取控制平面镜像的容器仓库。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

--kubernetes-version string     默认值:"stable-1"

为控制平面选择特定的 Kubernetes 版本。

--print-manifest

向 STDOUT 输出插件清单,而不是安装这些插件。

--service-cidr string     默认值:"10.96.0.0/12"

为服务 VIP 选择 IP 地址范围。

--service-dns-domain string     默认值:"cluster.local"

为服务使用其它域名,例如:"myorg.internal"。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.35 -

将 kube-proxy 插件安装到 Kubernetes 集群

概要

通过 API 服务器安装 kube-proxy 附加组件。

kubeadm init phase addon kube-proxy [flags]

选项

--apiserver-advertise-address string

API 服务器所公布的其正在监听的 IP 地址。如果未设置,则将使用默认网络接口。

--apiserver-bind-port int32     默认值: 6443

API 服务器绑定的端口。

--config string

kubeadm 配置文件的路径。

--control-plane-endpoint string

为控制平面指定一个稳定的 IP 地址或 DNS 名称。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

kube-proxy 操作的帮助命令。

--image-repository string     默认值:"registry.k8s.io"

选择用于拉取控制平面镜像的容器仓库。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

--kubernetes-version string     默认值:"stable-1"

为控制平面选择特定的 Kubernetes 版本。

--pod-network-cidr string

指定 Pod 网络的 IP 地址范围。如果已设置,控制平面将自动为每个节点分配 CIDR。

--print-manifest

向 STDOUT 打印插件清单,而非安装这些插件。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.36 -

生成用于将节点加入集群的引导令牌

概要

启动引导令牌(bootstrap token)用于在即将加入集群的节点和控制平面节点之间建立双向信任。

该命令使启动引导令牌(bootstrap token)所需的所有配置生效,然后创建初始令牌。

kubeadm init phase bootstrap-token [flags]

示例

# 进行所有引导令牌配置,并创建一个初始令牌,功能上与 kubeadm init 生成的令牌等效。
kubeadm init phase bootstrap-token

选项

--config string

kubeadm 配置文件的路径。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

bootstrap-token 操作的帮助命令。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

用于和集群通信的 kubeconfig 文件。如果它没有被设置,那么 kubeadm 将会搜索一个已经存在于标准路径的 kubeconfig 文件。

--skip-token-print

跳过打印 'kubeadm init' 生成的默认引导令牌。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.37 -

证书生成。

概要

此命令不应单独运行。请参阅可用子命令列表。

kubeadm init phase certs [flags]

选项

-h, --help

certs 操作的帮助命令。

从父指令中继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.38 -

生成所有证书。

概要

生成所有证书。

kubeadm init phase certs all [flags]

选项

--apiserver-advertise-address string

API 服务器所公布的其正在监听的 IP 地址。如果未设置,将使用默认网络接口。

--apiserver-cert-extra-sans strings

用于 API 服务器服务证书的可选额外替代名称(SAN)。可以同时使用 IP 地址和 DNS 名称。

--cert-dir string     默认值:"/etc/kubernetes/pki"

证书的存储路径。

--config string

kubeadm 配置文件的路径。

--control-plane-endpoint string

为控制平面指定一个稳定的 IP 地址或 DNS 名称。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

all 操作的帮助命令。

--kubernetes-version string     默认值:"stable-1"

为控制平面选择特定的 Kubernetes 版本。

--service-cidr string     默认值:"10.96.0.0/12"

VIP 服务使用其它的 IP 地址范围。

--service-dns-domain string     默认值:"cluster.local"

服务使用其它的域名,例如:"myorg.internal"。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.39 -

生成 apiserver 用来访问 etcd 的证书。

概要

生成 apiserver 用于访问 etcd 的证书,并将其保存到 apiserver-etcd-client.crt 和 apiserver-etcd-client.key 文件中。

如果两个文件都已存在,则 kubeadm 将跳过生成步骤,使用现有文件。

Alpha 免责声明:此命令目前处于 Alpha 阶段。

kubeadm init phase certs apiserver-etcd-client [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

证书的存储路径。

--config string

kubeadm 配置文件的路径。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

apiserver-etcd-client 操作的帮助命令。

--kubernetes-version string     默认值:"stable-1"

为控制平面指定特定的 Kubernetes 版本。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.40 -

生成供 API 服务器连接 kubelet 的证书。

概要

生成供 API 服务器连接 kubelet 的证书,并将其保存到 apiserver-kubelet-client.crt 和 apiserver-kubelet-client.key 文件中。

如果两个文件都已存在,则 kubeadm 将跳过生成步骤,使用现有文件。

Alpha 免责声明:此命令目前处于 Alpha 阶段。

kubeadm init phase certs apiserver-kubelet-client [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

存储证书的路径。

--config string

kubeadm 配置文件路径。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

apiserver-kubelet-client 操作的帮助命令。

--kubernetes-version string     默认值:"stable-1"

为控制平面指定特定的 Kubernetes 版本。

继承于父命令的选项

--rootfs string

[实验] 指向宿主机上的 '实际' 根文件系统的路径。

6.10.1.1.41 -

生成用于服务 Kubernetes API 的证书。

概要

生成用于服务 Kubernetes API 的证书,并将其保存到 apiserver.crt 和 apiserver.key 文件中。

如果两个文件都已存在,则 kubeadm 将跳过生成步骤,使用现有文件。

Alpha 免责声明:此命令目前处于 Alpha 阶段。

kubeadm init phase certs apiserver [flags]

选项

--apiserver-advertise-address string

API 服务器所公布的其正在监听的 IP 地址。如果未设置,则使用默认的网络接口。

--apiserver-cert-extra-sans strings

用于 API Server 服务证书的可选附加主体备用名称(SAN)。可以是 IP 地址和 DNS 名称。

--cert-dir string     默认值:"/etc/kubernetes/pki"

证书的存储路径。

--config string

kubeadm 配置文件的路径。

--control-plane-endpoint string

为控制平面指定一个稳定的 IP 地址或 DNS 名称。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

apiserver 操作的帮助命令。

--kubernetes-version string     默认值:"stable-1"

为控制平面指定特定的 Kubernetes 版本。

--service-cidr string     默认值:"10.96.0.0/12"

指定服务 VIP 可使用的其他 IP 地址段。

--service-dns-domain string     默认值:"cluster.local"

为服务使用其他域名,例如 "myorg.internal"。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.42 -

生成自签名的 Kubernetes CA 以便为其他 Kubernetes 组件提供身份标识。

概要

生成自签名的 Kubernetes CA 以便为其他 Kubernetes 组件提供身份标识,并将其保存到 ca.crt 和 ca.key 文件中。

如果两个文件都已存在,则 kubeadm 将跳过生成步骤,使用现有文件。

Alpha 免责声明:此命令目前处于 Alpha 阶段。

kubeadm init phase certs ca [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

证书的存储路径。

--config string

kubeadm 配置文件的路径。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

ca 操作的帮助命令。

--kubernetes-version string     默认值:"stable-1"

为控制平面选择特定的 Kubernetes 版本。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.43 -

生成用于为 etcd 设置身份的自签名 CA。

概要

生成用于为 etcd 设置身份的自签名 CA,并将其保存到 etcd/ca.crt 和 etcd/ca.key 文件中。

如果两个文件都已存在,则 kubeadm 将跳过生成步骤,使用现有文件。

kubeadm init phase certs etcd-ca [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

证书的存储路径。

--config string

kubeadm 配置文件的路径。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

etcd-ca 操作的帮助命令。

--kubernetes-version string     默认值:"stable-1"

为控制平面选择特定的 Kubernetes 版本。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.44 -

生成用于 etcd 健康检查的活跃性探针的证书。

概要

生成用于 etcd 健康检查的活跃性探针的证书,并将其保存到 etcd/healthcheck-client.crt 和 etcd/healthcheck-client.key 文件中。

如果两个文件都已存在,则 kubeadm 将跳过生成步骤,使用现有文件。

kubeadm init phase certs etcd-healthcheck-client [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

证书存储的路径。

--config string

kubeadm 配置文件的路径。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

etcd-healthcheck-client 操作的帮助命令。

--kubernetes-version string     默认值:"stable-1"

为控制平面选择特定的 Kubernetes 版本。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.45 -

生成 etcd 节点相互通信的证书。

概要

生成 etcd 节点相互通信的证书,并将其保存到 etcd/peer.crt 和 etcd/peer.key 文件中。

默认 SAN 为 localhost、127.0.0.1、127.0.0.1、:: 1

如果两个文件都已存在,则 kubeadm 将跳过生成步骤,使用现有文件。

Alpha 免责声明:此命令当前为 Alpha 功能。

kubeadm init phase certs etcd-peer [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存和存储证书的路径。

--config string

kubeadm 配置文件的路径。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

etcd-peer 操作的帮助命令。

--kubernetes-version string     默认值:"stable-1"

为控制平面指定特定的 Kubernetes 版本。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.46 -

生成用于提供 etcd 服务的证书。

概要

生成用于提供 etcd 服务的证书,并将其保存到 etcd/server.crt 和 etcd/server.key 文件中。

默认 SAN 为 localhost、127.0.0.1、127.0.0.1、:: 1

如果两个文件都已存在,则 kubeadm 将跳过生成步骤,使用现有文件。

Alpha 免责声明:此命令当前为 Alpha 功能。

kubeadm init phase certs etcd-server [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存和存储证书的路径。

--config string

kubeadm 配置文件的路径。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

etcd-server 操作的帮助命令。

--kubernetes-version string     默认值:"stable-1"

为控制平面指定特定的 Kubernetes 版本。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.47 -

生成自签名 CA 来提供前端代理的身份。

概要

生成自签名 CA 来提供前端代理的身份,并将其保存到 front-proxy-ca.cert 和 front-proxy-ca.key 文件中。

如果两个文件都已存在,kubeadm 将跳过生成步骤并将使用现有文件。

Alpha 免责声明:此命令目前是 Alpha 阶段。

kubeadm init phase certs front-proxy-ca [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

存储证书的路径。

--config string

kubeadm 配置文件的路径。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

front-proxy-ca 操作的帮助命令。

--kubernetes-version string     默认值:"stable-1"

为控制平面选择特定的 Kubernetes 版本。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.48 -

为前端代理客户端生成证书。

概要

为前端代理客户端生成证书,并将其保存到 front-proxy-client.crt 和 front-proxy-client.key 文件中。

如果两个文件都已存在,kubeadm 将跳过生成步骤并将使用现有文件。

Alpha 免责声明:此命令目前是 Alpha 阶段。

kubeadm init phase certs front-proxy-client [flags]

选项

--cert-dir string     默认:"/etc/kubernetes/pki"

存储证书的路径。

--config string

kubeadm 配置文件的路径。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

front-proxy-client 操作的帮助命令。

--kubernetes-version string     默认值:"stable-1"

为控制平面选择特定的 Kubernetes 版本。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.49 -

生成用来签署服务账号令牌的私钥及其公钥。

概要

生成用来签署服务账号令牌的私钥及其公钥,并将其保存到 sa.key 和 sa.pub 文件中。

如果两个文件都已存在,则 kubeadm 会跳过生成步骤,而将使用现有文件。

kubeadm init phase certs sa [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存和存储证书的路径。

-h, --help

sa 操作的帮助命令。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.50 -

生成建立控制平面所需的静态 Pod 清单文件。

概要

此命令并非设计用来单独运行。请参阅可用子命令列表。

kubeadm init phase control-plane [flags]

选项

-h, --help

control-plane 操作的帮助命令。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.51 -

生成所有静态 Pod 清单文件。

概要

生成所有的静态 Pod 清单文件。

kubeadm init phase control-plane all [flags]

示例

# 为控制平面组件生成静态 Pod 清单文件,其功能等效于 kubeadm init 生成的文件。
kubeadm init phase control-plane all

# 使用从某配置文件中读取的选项为生成静态 Pod 清单文件。
kubeadm init phase control-plane all --config config.yaml

选项

--apiserver-advertise-address string

API 服务器所公布的其正在监听的 IP 地址。如果未设置,将使用默认的网络接口。

--apiserver-bind-port int32     默认值:6443

API 服务器要绑定的端口。

--apiserver-extra-args <逗号分割的 'key=value' 对>

形式为 <flagname>=<value> 的一组额外参数,用来传递给 API 服务器, 或者覆盖其默认配置值。

--cert-dir string     默认值:"/etc/kubernetes/pki"

存储证书的路径。

--config string

kubeadm 配置文件的路径。

--control-plane-endpoint string

为控制平面选择一个稳定的 IP 地址或者 DNS 名称。

--controller-manager-extra-args <逗号分割的 'key=value' 对>

一组形式为 <flagname>=<value> 的额外参数,用来传递给控制管理器(Controller Manager) 或覆盖其默认设置值。

--dry-run
不做任何更改;只输出将要执行的操作。
--feature-gates string

一组用来描述各种特性门控的键值(key=value)对。选项是:
EtcdLearnerMode=true|false (ALPHA - 默认值=false)
PublicKeysECDSA=true|false (ALPHA - 默认值=false)
RootlessControlPlane=true|false (ALPHA - 默认值=false)
UpgradeAddonsBeforeControlPlane=true|false (DEPRECATED - 默认值=false)

-h, --help

all 操作的帮助命令。

--image-repository string     默认值:"registry.k8s.io"

选择用于拉取控制平面镜像的容器仓库。

--kubernetes-version string     默认值:"stable-1"

为控制平面选择指定的 Kubernetes 版本。

--patches string

包含名为 "target[suffix][+patchtype].extension" 的文件的目录的路径。 例如,"kube-apiserver0+merge.yaml"或是简单的 "etcd.json"。 "target" 可以是 "kube-apiserver"、"kube-controller-manager"、"kube-scheduler"、"etcd"、"kubeletconfiguration" 之一。 "patchtype" 可以是 "strategic"、"merge" 或者 "json" 之一, 并且它们与 kubectl 支持的补丁格式相匹配。 默认的 "patchtype" 是 "strategic"。 "extension" 必须是 "json" 或 "yaml"。 "suffix" 是一个可选字符串,可用于确定首先按字母顺序应用哪些补丁。

--pod-network-cidr string

指定 Pod 网络的 IP 地址范围。如果设置了此标志,控制平面将自动地为每个节点分配 CIDR。

--scheduler-extra-args <逗号分割的 'key=value' 对>

一组形式为 <flagname>=<value> 的额外参数,用来传递给调度器(Scheduler) 或覆盖其默认设置值。

--service-cidr string     默认值:"10.96.0.0/12"

为服务 VIP 选择 IP 地址范围。

从父指令继承的选项

--rootfs string

[实验] 指向'真实'宿主机的根文件系统的路径。

6.10.1.1.52 -

生成 kube-apiserver 静态 Pod 清单。

概要

生成 kube-apiserver 静态 Pod 清单。

kubeadm init phase control-plane apiserver [flags]

选项

--apiserver-advertise-address string

API 服务器所公布的其正在监听的 IP 地址。如果未设置,将使用默认网络接口。

--apiserver-bind-port int32     默认值: 6443

要绑定到 API 服务器的端口。

--apiserver-extra-args <逗号分隔的 'key=value' 对>

一组 <flagname>=<value> 形式的额外参数,用来传递给 API 服务器或者覆盖其默认参数配置。

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存和存储证书的路径。

--config string

kubeadm 配置文件的路径。

--control-plane-endpoint string

为控制平面指定一个稳定的 IP 地址或 DNS 名称。

--dry-run

不做任何更改;只输出将要执行的操作。

--feature-gates string

一组键值对,用于描述各种功能特性的特性门控。选项是:
EtcdLearnerMode=true|false (ALPHA - 默认值=false)
PublicKeysECDSA=true|false (ALPHA - 默认值=false)
RootlessControlPlane=true|false (ALPHA - 默认值=false)
UpgradeAddonsBeforeControlPlane=true|false (DEPRECATED - 默认值=false)

-h, --help

apiserver 操作的帮助命令。

--image-repository string     默认值:"registry.k8s.io"

选择要从中拉取控制平面镜像的容器仓库。

--kubernetes-version string     默认值:"stable-1"

为控制平面选择特定的 Kubernetes 版本。

--patches string

包含名为 "target[suffix][+patchtype].extension" 的文件的目录的路径。 例如,"kube-apiserver0+merge.yaml"或仅仅是 "etcd.json"。 "target" 可以是 "kube-apiserver"、"kube-controller-manager"、"kube-scheduler"、"etcd"、"kubeletconfiguration" 之一。 "patchtype" 可以是 "strategic"、"merge" 或者 "json" 之一, 并且它们与 kubectl 支持的补丁格式相同。 默认的 "patchtype" 是 "strategic"。 "extension" 必须是"json" 或"yaml"。 "suffix" 是一个可选字符串,可用于确定首先按字母顺序应用哪些补丁。

--service-cidr string     默认值:"10.96.0.0/12"

指定服务 VIP 使用 IP 地址的其他范围。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统路径。

6.10.1.1.53 -

生成 kube-controller-manager 静态 Pod 清单。

概要

生成 kube-controller-manager 静态 Pod 清单。

kubeadm init phase control-plane controller-manager [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

存储证书的路径。

--config string

kubeadm 配置文件的路径。

--controller-manager-extra-args <comma-separated 'key=value' pairs>

一组 <flagname>=< 形式的额外参数,传递给控制器管理器(Controller Manager) 或者覆盖其默认配置值

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

controller-manager 操作的帮助命令。

--image-repository string     默认值:"registry.k8s.ioo"

选择要从中拉取控制平面镜像的容器仓库。

--kubernetes-version string     默认值:"stable-1"

为控制平面选择特定的 Kubernetes 版本。

--patches string

包含名为 "target[suffix][+patchtype].extension" 的文件的目录。 例如,"kube-apiserver0+merge.yaml" 或者 "etcd.json"。 "target" 可以是 "kube-apiserver"、"kube-controller-manager"、"kube-scheduler"、"etcd"、"kubeletconfiguration" 之一。 "patchtype" 可以是 "strategic"、"merge" 或 "json" 之一,分别与 kubectl 所支持的 patch 格式相匹配。默认的 "patchtype" 是 "strategic"。 "extension" 必须是 "json" 或 "yaml"。 "suffix" 是一个可选的字符串,用来确定按字母顺序排序时首先应用哪些 patch。

--pod-network-cidr string

指定 Pod 网络的 IP 地址范围。如果设置,控制平面将自动为每个节点分配 CIDR。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.54 -

生成 kube-scheduler 静态 Pod 清单。

概要

生成 kube-scheduler 静态 Pod 清单。

kubeadm init phase control-plane scheduler [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

存储证书的路径。

--config string

kubeadm 配置文件的路径。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

scheduler 操作的帮助命令。

--image-repository string     默认值:"registry.k8s.io"

选择要从中拉取控制平面镜像的容器仓库。

--kubernetes-version string     默认值:"stable-1"

为控制平面选择特定的 Kubernetes 版本。

--patches string
包含名为 "target[suffix][+patchtype].extension" 的文件的目录。 例如,"kube-apiserver0+merge.yaml" 或者 "etcd.json"。 "target" 可以是 "kube-apiserver"、"kube-controller-manager"、"kube-scheduler"、"etcd"、"kubeletconfiguration" 之一。 "patchtype" 可以是 "strategic"、"merge"、"json" 之一,分别与 kubectl 所支持的 patch 格式相匹配。默认的 "patchtype" 是 "strategic"。 "extension" 必须是 "json" 或 "yaml"。 "suffix" 是一个可选的字符串,用来确定按字母顺序排序时首先应用哪些 patch。
--scheduler-extra-args <逗号分隔的 'key=value' 键值对>

一组 <flagname>=<value> 形式的额外参数,用来传递给调度器或者覆盖其默认参数配置。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.55 -

为本地 etcd 生成静态 Pod 的清单文件。

概要

此命令并非设计用来单独运行。请参阅可用子命令列表。

kubeadm init phase etcd [flags]

选项

-h, --help

etcd 操作的帮助命令。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.56 -

为本地单节点 etcd 实例生成静态 Pod 清单文件。

概要

为本地单节点 etcd 实例生成静态 Pod 清单文件。

kubeadm init phase etcd local [flags]

示例

# 为 etcd 生成静态 Pod 清单文件,其功能等效于 kubeadm init 生成的文件。
kubeadm init phase etcd local

# 使用从配置文件读取的选项为 etcd 生成静态 Pod 清单文件。
kubeadm init phase etcd local --config config.yaml

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

存储证书的路径。

--config string

kubeadm 配置文件的路径。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

local 操作的帮助命令。

--image-repository string     默认值:"registry.k8s.io"

选择要从中拉取控制平面镜像的容器仓库。

--patches string

包含名为 "target[suffix][+patchtype].extension" 的文件的目录的路径。 例如,"kube-apiserver0+merge.yaml" 或是简单的 "etcd.json"。 "target" 可以是 "kube-apiserver"、"kube-controller-manager"、"kube-scheduler"、"etcd" 、"kubeletconfiguration" 之一。 "patchtype" 可以是 "strategic"、"merge" 或者 "json" 之一, 并且它们与 kubectl 支持的补丁格式相匹配。 默认的 "patchtype" 是 "strategic"。 "extension" 必须是 "json" 或 "yaml"。 "suffix" 是一个可选字符串,可用于确定首先按字母顺序应用哪些补丁。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.57 -

生成所有建立控制平面和管理员(admin)所需的 kubeconfig 文件。

概要

此命令并非设计用来单独运行。请阅读可用子命令列表。

kubeadm init phase kubeconfig [flags]

选项

-h, --help

kubeconfig 操作的帮助命令

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.58 -

为管理员(admin)和 kubeadm 本身生成 kubeconfig 文件。

概要

为管理员和 kubeadm 本身生成 kubeconfig 文件,并将其保存到 admin.conf 文件中。

kubeadm init phase kubeconfig admin [flags]

选项

--apiserver-advertise-address string

API 服务器所公布的其正在监听的 IP 地址。如果未设置,则使用默认的网络接口。

--apiserver-bind-port int32     默认值:6443

要绑定到 API 服务器的端口。

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存和存储证书的路径。

--config string

kubeadm 配置文件的路径。

--control-plane-endpoint string

为控制平面指定一个稳定的 IP 地址或 DNS 名称。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

admin 操作的帮助命令。

--kubeconfig-dir string     默认值:"/etc/kubernetes"

kubeconfig 文件的保存路径。

--kubernetes-version string     默认值:"stable-1"

为控制平面指定特定的 Kubernetes 版本。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.59 -

生成所有 kubeconfig 文件。

概要

生成所有 kubeconfig 文件。

kubeadm init phase kubeconfig all [flags]

选项

--apiserver-advertise-address string

API 服务器所公布的其正在监听的 IP 地址。如果没有设置,将使用默认的网络接口。

--apiserver-bind-port int32     默认值:6443

要绑定到 API 服务器的端口。

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存和存储证书的路径。

--config string

kubeadm 配置文件的路径。

--control-plane-endpoint string

为控制平面指定一个稳定的 IP 地址或 DNS 名称。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

all 操作的帮助命令。

--kubeconfig-dir string     默认值:"/etc/kubernetes"

kubeconfig 文件的保存路径。

--kubernetes-version string     默认值:"stable-1"

为控制平面指定特定的 Kubernetes 版本。

--node-name string

指定节点名称。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.60 -

生成控制器管理器要使用的 kubeconfig 文件。

概要

生成控制器管理器要使用的 kubeconfig 文件,并保存到 controller-manager.conf 文件中。

kubeadm init phase kubeconfig controller-manager [flags]

选项

--apiserver-advertise-address string

API 服务器所公布的其正在监听的 IP 地址。如果未设置,则使用默认的网络接口。

--apiserver-bind-port int32     默认值:6443

要绑定到 API 服务器的端口。

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存和存储证书的路径。

--config string

kubeadm 配置文件的路径。

--control-plane-endpoint string

为控制平面指定一个稳定的 IP 地址或 DNS 名称。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

controller-manager 操作的帮助命令。

--kubeconfig-dir string     默认值:"/etc/kubernetes"

kubeconfig 文件的保存路径。

--kubernetes-version string     默认值:"stable-1"

为控制平面指定特定的 Kubernetes 版本。

继承于父命令的选项

--rootfs 字符串

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.61 -

为 kubelet 生成一个 kubeconfig 文件,仅仅用于集群引导目的。

概要

生成 kubelet 要使用的 kubeconfig 文件,并将其保存到 kubelet.conf 文件。

请注意,该操作目的是用于引导集群。在控制平面启动之后,应该从 CSR API 请求所有 kubelet 凭据。

kubeadm init phase kubeconfig kubelet [flags]

选项

--apiserver-advertise-address string

API 服务器所公布的其正在监听的 IP 地址。如果未设置,则使用默认的网络接口。

--apiserver-bind-port int32     默认值:6443

要绑定到 API 服务器的端口。

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存和存储证书的路径。

--config string

kubeadm 配置文件的路径。

--control-plane-endpoint string

为控制平面指定一个稳定的 IP 地址或 DNS 名称。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

kubelet 操作的帮助命令。

--kubeconfig-dir string     默认值:"/etc/kubernetes"

kubeconfig 文件的保存路径。

--kubernetes-version string     默认值:"stable-1"

为控制平面选择特定的 Kubernetes 版本。

--node-name string

指定节点的名称。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.62 -

生成调度器使用的 kubeconfig 文件。

概要

生成调度器(scheduler)要使用的 kubeconfig 文件,并保存到 scheduler.conf 文件中。

kubeadm init phase kubeconfig scheduler [flags]

选项

--apiserver-advertise-address string

API 服务器所公布的其正在监听的 IP 地址。如果未设置,则使用默认的网络接口。

--apiserver-bind-port int32     默认值:6443

要绑定到 API 服务器的端口。

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存和存储证书的路径。

--config string

kubeadm 配置文件的路径。

--control-plane-endpoint string

为控制平面指定一个稳定的 IP 地址或 DNS 名称。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

scheduler 操作的帮助命令。

--kubeconfig-dir string     默认值:"/etc/kubernetes"

kubeconfig 文件的保存路径。

--kubernetes-version string     默认值:"stable-1"

为控制平面指定特定的 Kubernetes 版本。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.63 -

为 super-admin 生成一个 kubeconfig 文件。

概要

为 super-admin 生成一个 kubeconfig 文件,并将其保存到 super-admin.conf 文件中。

kubeadm init phase kubeconfig super-admin [flags]

选项

--apiserver-advertise-address string

API 服务器所公布其监听的 IP 地址。如果未设置,则使用默认的网络接口。

--apiserver-bind-port int32     默认值:6443

API 服务器绑定的端口。

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存和存储证书的路径。

--config string

kubeadm 配置文件的路径。

--control-plane-endpoint string

为控制平面指定一个稳定的 IP 地址或 DNS 名称。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

super-admin 的帮助信息。

--kubeconfig-dir string     默认值:"/etc/kubernetes"

保存 kubeconfig 文件的路径。

--kubernetes-version string     默认值:"stable-1"

为控制平面指定一个特定的 Kubernetes 版本。

从父命令继承的选项

--rootfs string

[实验性功能] 指向‘真实’宿主根文件系统的路径。

6.10.1.1.64 -

TLS 引导后更新与 kubelet 相关的设置

概要

TLS 引导后更新与 kubelet 相关的设置

kubeadm init phase kubelet-finalize [flags]

示例

  # 在 TLS 引导后更新与 kubelet 相关的设置
  kubeadm init phase kubelet-finalize all --config

选项

-h, --help

kubelet-finalize 操作的帮助命令

继承于父命令的选项

--rootfs string

[实验] 到'真实'主机根文件系统的路径。

6.10.1.1.65 -

运行 kubelet-finalize 的所有阶段

概要

运行 kubelet-finalize 的所有阶段

kubeadm init phase kubelet-finalize all [flags]

示例

# 在 TLS 引导后更新与 kubelet 相关的设置
kubeadm init phase kubelet-finalize all --config

选项

--cert-dir string     默认值: "/etc/kubernetes/pki"

保存和存储证书的路径。

--config string

kubeadm 配置文件的路径。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

all 操作的帮助命令

继承于父命令的选项

--rootfs string

[实验] 到'真实'主机根文件系统的路径。

6.10.1.1.66 -

启用 kubelet 客户端证书轮换

概要

启用 kubelet 客户端证书轮换

kubeadm init phase kubelet-finalize experimental-cert-rotation [flags]

选项

--cert-dir string     Default: "/etc/kubernetes/pki"

保存和存储证书的路径。

--config string

kubeadm 配置文件的路径。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

experimental-cert-rotation 操作的帮助命令

继承于父命令的选项

--rootfs string

[实验] 到'真实'主机根文件系统的路径。

6.10.1.1.67 -

编写 kubelet 配置并(重新)启动 kubelet

概要

使用 kubelet 配置文件编写一个文件,并使用特定节点的 kubelet 设置编写一个环境文件,然后(重新)启动 kubelet。

kubeadm init phase kubelet-start [flags]

示例

# 将来自 InitConfiguration 文件中的 kubelet 参数写入一个动态环境文件。
kubeadm init phase kubelet-start --config config.yaml

选项

--config string

kubeadm 配置文件的路径。

--cri-socket string

连接到 CRI 套接字的路径。如果为空,则 kubeadm 将尝试自动检测该值;仅当安装了多个 CRI 或具有非标准 CRI 套接字时,才使用此选项。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

kubelet-start 操作的帮助命令

--node-name string

指定节点名称。

--patches string

目录路径,指向包含名为 “target[suffix][+patchtype].extension” 的文件的目录。 例如,"kube-apiserver0+merge.yaml" 或 "etcd.json" 这种简单形式。 "target" 可以是 “kube-apiserver”、“kube-controller-manager”、“kube-scheduler”、“etcd”、“kubeletconfiguration” 之一, "patchtype" 可以是 "strategic"、"merge" 或 "json" 之一,并且它们与 kubectl 支持的补丁格式匹配。 默认的 "patchtype" 为 "strategic"。 "extension" 必须为 "json" 或 "yaml"。 "suffix" 是一个可选字符串,可用于确定按字母顺序首先应用哪些补丁。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.68 -

标记节点为控制平面节点。

概要

标记节点为控制平面节点。

kubeadm init phase mark-control-plane [flags]

示例

# 将控制平面标签和污点应用于当前节点,其功能等效于 kubeadm init 执行的操作
kubeadm init phase mark-control-plane --config config.yaml

# 将控制平面标签和污点应用于特定节点
kubeadm init phase mark-control-plane --node-name myNode

选项

--config string

kubeadm 配置文件的路径。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

mark-control-plane 操作的帮助命令。

--node-name string

指定节点名称。

从父命令继承的选项

--rootfs 字符串

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.69 -

运行预检。

概要

运行 kubeadm init 前的预检。

kubeadm init phase preflight [flags]

示例

# 使用配置文件对 kubeadm init 进行预检
kubeadm init phase preflight --config kubeadm-config.yaml

选项

--config string

kubeadm 配置文件的路径。

--cri-socket string

要连接的 CRI 套接字的路径。如果该值为空,kubeadm 将尝试自动检测; 仅当你安装了多个 CRI 或使用非标准的 CRI 套接字时才应使用此选项。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

preflight 操作的帮助命令。

--ignore-preflight-errors strings

错误将显示为警告的检查列表。例如:'IsPrivilegedUser,Swap'。取值为 'all' 时将忽略检查中的所有错误。

继承于父命令的选项

--rootfs string

[实验] 到'真实'主机根文件系统的路径。

6.10.1.1.70 -

显示针对控制平面和工作节点的 join 命令。

概要

显示针对控制平面和工作节点的 join 命令。

kubeadm init phase show-join-command [flags]

选项

-h, --help

show-join-command 操作的帮助命令

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.71 -

将证书上传到 kubeadm-certs。

概要

将控制平面证书上传到 kubeadm-certs Secret。

kubeadm init phase upload-certs [flags]

选项

--certificate-key string

用于加密 kubeadm-certs Secret 中的控制平面证书的密钥。

--config string

kubeadm 配置文件的路径。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

upload-certs 操作的帮助命令。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

用来与集群通信的 kubeconfig 文件。 如果此标志未设置,则可以在一组标准的位置搜索现有的 kubeconfig 文件。

--skip-certificate-key-print

不要打印输出用于加密控制平面证书的密钥。

--upload-certs

将控制平面证书上传到 kubeadm-certs Secret。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.72 -

上传 kubeadm 和 kubelet 配置到 ConfigMap 中。

概要

此命令并非设计用来单独运行。请参阅可用的子命令列表。

kubeadm init phase upload-config [flags]

选项

-h, --help

upload-config 操作的帮助命令。

从父命令中继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.73 -

将所有配置上传到 ConfigMap。

概要

将所有配置上传到 ConfigMap

kubeadm init phase upload-config all [flags]

选项

--config string

kubeadm 配置文件的路径。

--cri-socket string

要连接的 CRI 套接字的路径。如果该值为空,kubeadm 将尝试自动检测; 仅当你安装了多个 CRI 或使用非标准的 CRI 套接字时才应使用此选项。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

all 操作的帮助命令。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.74 -

将 kubeadm ClusterConfiguration 上传到 ConfigMap。

概要

将 kubeadm ClusterConfiguration 上传到 kube-system 命名空间中名为 kubeadm-config 的 ConfigMap 中。 这样就可以正确配置系统组件,并在升级时提供无缝的用户体验。

另外,可以使用 kubeadm 配置。

kubeadm init phase upload-config kubeadm [flags]

示例

# 上传集群配置
kubeadm init phase upload-config --config=myConfig.yaml

选项

--config string

kubeadm 配置文件的路径。

--cri-socket string

要连接的 CRI 套接字的路径。如果该值为空,kubeadm 将尝试自动检测; 仅当你安装了多个 CRI 或使用非标准的 CRI 套接字时才应使用此选项。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

kubeadm 操作的帮助命令。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.75 -

将 kubelet 组件配置上传到 ConfigMap。

概要

将从 kubeadm InitConfiguration 对象提取的 kubelet 配置上传到集群中的 kubelet-config ConfigMap。

kubeadm init phase upload-config kubelet [flags]

示例

# 将 kubelet 配置从 kubeadm 配置文件上传到集群中的 ConfigMap。
kubeadm init phase upload-config kubelet --config kubeadm.yaml

选项

--config string

到 kubeadm 配置文件的路径。

--cri-socket string

要连接的 CRI 套接字的路径。如果该值为空,kubeadm 将尝试自动检测; 仅当你安装了多个 CRI 或使用非标准的 CRI 套接字时才应使用此选项。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

kubelet 操作的帮助命令。

-- kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。如果未设置该标签, 则可以通过一组标准路径来寻找已有的 kubeconfig 文件。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.76 -

在你希望加入现有集群的任何机器上运行它。

摘要

当节点加入 kubeadm 初始化的集群时,我们需要建立双向信任。 这个过程可以分解为发现(让待加入节点信任 Kubernetes 控制平面节点)和 TLS 引导(让 Kubernetes 控制平面节点信任待加入节点)两个部分。

有两种主要的发现方案。 第一种方案是使用共享令牌和 API 服务器的 IP 地址。 第二种是以文件形式提供标准 kubeconfig 文件的一个子集。 发现/kubeconfig 文件支持令牌、client-go 鉴权插件(“exec”)、“tokenFile" 和 "authProvider"。该文件可以是本地文件,也可以通过 HTTPS URL 下载。 格式是 kubeadm join --discovery-token abcdef.1234567890abcdef 1.2.3.4:6443kubeadm join --discovery-file path/to/file.conf 或者 kubeadm join --discovery-file https://url/file.conf。 只能使用其中一种。 如果发现信息是从 URL 加载的,必须使用 HTTPS。 此外,在这种情况下,主机安装的 CA 包用于验证连接。

如果使用共享令牌进行发现,还应该传递 --discovery-token-ca-cert-hash 参数来验证 Kubernetes 控制平面节点提供的根证书颁发机构(CA)的公钥。 此参数的值指定为 "<hash-type>:<hex-encoded-value>", 其中支持的哈希类型为 "sha256"。哈希是通过 Subject Public Key Info(SPKI)对象的字节计算的(如 RFC7469)。 这个值可以从 "kubeadm init" 的输出中获得,或者可以使用标准工具进行计算。 可以多次重复 --discovery-token-ca-cert-hash 参数以允许多个公钥。

如果无法提前知道 CA 公钥哈希,则可以通过 --discovery-token-unsafe-skip-ca-verification 参数禁用此验证。 这削弱了 kubeadm 安全模型,因为其他节点可能会模仿 Kubernetes 控制平面节点。

TLS 引导机制也通过共享令牌驱动。 这用于向 Kubernetes 控制平面节点进行临时的身份验证,以提交本地创建的密钥对的证书签名请求(CSR)。 默认情况下,kubeadm 将设置 Kubernetes 控制平面节点自动批准这些签名请求。 这个令牌通过 --tls-bootstrap-token abcdef.1234567890abcdef 参数传入。

通常两个部分会使用相同的令牌。 在这种情况下可以使用 --token 参数,而不是单独指定每个令牌。

"join [api-server-endpoint]" 命令执行下列阶段:

preflight              Run join pre-flight checks
control-plane-prepare  Prepare the machine for serving a control plane
  /download-certs        [EXPERIMENTAL] Download certificates shared among control-plane nodes from the kubeadm-certs Secret
  /certs                 Generate the certificates for the new control plane components
  /kubeconfig            Generate the kubeconfig for the new control plane components
  /control-plane         Generate the manifests for the new control plane components
kubelet-start          Write kubelet settings, certificates and (re)start the kubelet
control-plane-join     Join a machine as a control plane instance
  /etcd                  Add a new local etcd member
  /update-status         Register the new control-plane node into the ClusterStatus maintained in the kubeadm-config ConfigMap (DEPRECATED)
  /mark-control-plane    Mark a node as a control-plane
kubeadm join [api-server-endpoint] [flags]

选项

--apiserver-advertise-address string

如果该节点托管一个新的控制平面实例,则 API 服务器将公布其正在侦听的 IP 地址。如果未设置,则使用默认网络接口。

--apiserver-bind-port int32     默认值: 6443

如果节点应该托管新的控制平面实例,则为 API 服务器要绑定的端口。

--certificate-key string

使用此密钥可以解密由 init 上传的证书 Secret。

--config string

kubeadm 配置文件的路径。

--control-plane

在此节点上创建一个新的控制平面实例。

--cri-socket string

要连接的 CRI 套接字的路径。如果为空,则 kubeadm 将尝试自动检测此值; 仅当安装了多个 CRI 或存在非标准的 CRI 套接字时,才使用此选项。

--discovery-file string

对于基于文件的发现,给出用于加载集群信息的文件或者 URL。

--discovery-token string

对于基于令牌的发现,该令牌用于验证从 API 服务器获取的集群信息。

--discovery-token-ca-cert-hash stringSlice

对基于令牌的发现,验证根 CA 公钥是否与此哈希匹配 (格式:"<type>:<value>")。

--discovery-token-unsafe-skip-ca-verification

对于基于令牌的发现,允许在未关联 --discovery-token-ca-cert-hash 参数的情况下添加节点。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

join 操作的帮助命令。

--ignore-preflight-errors strings

错误将显示为警告的检查列表;例如:'IsPrivilegedUser,Swap'。取值为 'all' 时将忽略检查中的所有错误。

--node-name string

指定节点的名称。

--patches string

包含名为 "target[suffix][+patchtype].extension" 的文件的目录的路径。 例如,"kube-apiserver0+merge.yaml" 或仅仅是 "etcd.json"。 "target" 可以是 “kube-apiserver”、“kube-controller-manager”、“kube-scheduler”、“etcd”、“kubeletconfiguration” 之一, "patchtype" 可以是 "strategic"、"merge" 或 "json" 之一,并且它们与 kubectl 支持的补丁格式匹配。 默认的 "patchtype" 为 "strategic"。 "extension" 必须为 "json" 或 "yaml"。 "suffix" 是一个可选字符串,可用于确定首先按字母顺序应用哪些补丁。

--skip-phases strings

要跳过的阶段列表。

--tls-bootstrap-token string

指定在加入节点时用于临时通过 Kubernetes 控制平面进行身份验证的令牌。

--token string

如果未提供这些值,则将它们用于 discovery-token 令牌和 tls-bootstrap 令牌。

从父命令继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

6.10.1.1.77 -

使用此命令来调用 join 工作流程的某个阶段。

概要

使用此命令来调用 join 工作流程的某个阶段。

选项

-h, --help

phase 操作的帮助命令。

从父命令中继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

6.10.1.1.78 -

添加作为控制平面实例的机器。

概要

添加作为控制平面实例的机器。

kubeadm join phase control-plane-join [flags]

示例

# 将机器作为控制平面实例加入
kubeadm join phase control-plane-join all

选项

-h, --help

control-plane-join 操作的帮助命令。

从父命令中继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.79 -

添加作为控制平面实例的机器。

概要

添加作为控制平面实例的机器。

kubeadm join phase control-plane-join all [flags]

选项

--apiserver-advertise-address string

如果该节点托管一个新的控制平面实例,则 API 服务器将公布其正在侦听的 IP 地址。如果未设置,则使用默认网络接口。

--config string

kubeadm 配置文件的路径。

--control-plane

在此节点上创建一个新的控制平面实例。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

all 操作的帮助命令。

--node-name string

指定节点名称。

--patches string

包含名为 “target[suffix][+patchtype].extension” 的文件的目录的路径。 例如,“kube-apiserver0+merge.yaml” 或只是 “etcd.json”。 “target” 可以是 “kube-apiserver”、“kube-controller-manager”、“kube-scheduler”、“etcd”、“kubeletconfiguration” 之一。 “patchtype” 可以是 “strategic”、“merge” 或 “json”,它们匹配 kubectl 支持的补丁格式。 默认的 “patchtype” 是 “strategic”。“extension” 必须是 “json” 或 “yaml”。 “suffix” 是一个可选字符串,可用于基于字母数字顺序确定首先应用哪些补丁。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.80 -

添加一个新的本地 etcd 成员。

概要

添加新的本地 etcd 成员。

kubeadm join phase control-plane-join etcd [flags]

选项

--apiserver-advertise-address string

如果该节点托管一个新的控制平面实例,则 API 服务器将公布其正在侦听的 IP 地址。如果未设置,则使用默认网络接口。

--config string

kubeadm 配置文件的路径。

--control-plane

在此节点上创建一个新的控制平面实例。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

etcd 操作的帮助命令。

--node-name string

指定节点的名称。

--patches string

包含名为 “target[suffix][+patchtype].extension” 的文件的目录的路径。 例如,“kube-apiserver0+merge.yaml” 或只是 “etcd.json”。 “target” 可以是 “kube-apiserver”、“kube-controller-manager”、“kube-scheduler”、“etcd” 、”kubeletconfiguration”之一。 “patchtype” 可以是 “strategic”、“merge” 或 “json”,它们匹配 kubectl 支持的补丁格式。 默认的 “patchtype” 是 “strategic”。“extension” 必须是 “json” 或 “yaml”。 “suffix” 是一个可选字符串,可用于基于字母数字顺序确定首先应用哪些补丁。

从父命令中继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.81 -

将节点标记为控制平面节点。

概要

将节点标记为控制平面节点。

kubeadm join phase control-plane-join mark-control-plane [flags]

选项

--config string

kubeadm 配置文件的路径。

--control-plane

在此节点上创建一个新的控制平面实例。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

mark-control-plane 操作的帮助命令。

--node-name string

指定节点的名称。

从父命令中继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.82 -

将新的控制平面节点注册到 kubeadm-config ConfigMap 维护的 ClusterStatus 中(已弃用)。

概要

将新的控制平面节点注册到 kubeadm-config ConfigMap 维护的 ClusterStatus 中(已弃用)。

kubeadm join phase control-plane-join update-status [flags]

选项

--apiserver-advertise-address string

如果该节点托管一个新的控制平面实例,则 API 服务器将公布其正在侦听的 IP 地址。如果未设置,则使用默认网络接口。

--config string

kubeadm 配置文件的路径。

--control-plane

在此节点上创建一个新的控制平面实例。

-h, --help

update-status 操作的帮助命令。

--node-name string

指定节点名称。

从父命令中继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.83 -

准备为控制平面服务的机器。

概要

准备为控制平面服务的机器。

kubeadm join phase control-plane-prepare [flags]

示例

# 准备为控制平面服务的机器
kubeadm join phase control-plane-prepare all

选项

-h, --help

control-plane-prepare 操作的帮助命令。

从父命令中继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

6.10.1.1.84 -

准备为控制平面服务的机器。

概要

准备为控制平面服务的机器。

kubeadm join phase control-plane-prepare all [api-server-endpoint] [flags]

选项

--apiserver-advertise-address string

如果该节点托管一个新的控制平面实例,则 API 服务器将公布其正在侦听的 IP 地址。如果未设置,则使用默认网络接口。

--apiserver-bind-port int32     默认值:6443

如果该节点托管一个新的控制平面实例,则为 API 服务器要绑定的端口。

--certificate-key string

使用此密钥解密由 init 上传的证书 Secret。

--config string

kubeadm 配置文件的路径。

--control-plane

在此节点上创建一个新的控制平面实例。

--discovery-file string

对于基于文件的发现,给出用于加载集群信息的文件或者 URL。

--discovery-token string

对于基于令牌的发现,该令牌用于验证从 API 服务器获取的集群信息。

--discovery-token-ca-cert-hash strings

对于基于令牌的发现,请验证根 CA 公钥是否匹配此哈希值(格式:"<type>:<value>")。

--discovery-token-unsafe-skip-ca-verification

对于基于令牌的发现,允许在未关联 --discovery-token-ca-cert-hash 参数的情况下添加节点。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help
all 操作的帮助命令。
--node-name string

指定节点名称。

--patches string

包含名为 “target[suffix][+patchtype].extension” 的文件的目录的路径。 例如,“kube-apiserver0+merge.yaml” 或只是 “etcd.json”。 “target” 可以是 “kube-apiserver”、“kube-controller-manager”、“kube-scheduler”、“etcd”、“kubeletconfiguration” 之一。 “patchtype” 可以是 “strategic”、“merge” 或 “json”,它们匹配 kubectl 支持的补丁格式。 默认的 “patchtype” 是 “strategic”。“extension” 必须是 “json” 或 “yaml”。 “suffix” 是一个可选字符串,可用于基于字母数字顺序确定首先应用哪些补丁。

--tls-bootstrap-token string

指定在加入节点时用于临时通过 Kubernetes 控制平面进行身份验证的令牌。

--token string

如果未提供这些值,则将它们用于 discovery-token 令牌和 tls-bootstrap 令牌。

从父命令继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

6.10.1.1.85 -

为新的控制平面组件生成证书。

概要

为新的控制平面组件生成证书。

kubeadm join phase control-plane-prepare certs [api-server-endpoint] [flags]

选项

--apiserver-advertise-address string

如果该节点托管一个新的控制平面实例,则 API 服务器将公布其正在侦听的 IP 地址。如果未设置,则使用默认网络接口。

--config string

kubeadm 配置文件的路径。

--control-plane

在此节点上创建一个新的控制平面实例。

--discovery-file string

对于基于文件的发现,给出用于加载集群信息的文件或者 URL。

--discovery-token string

对于基于令牌的发现,该令牌用于验证从 API 服务器获取的集群信息。

--discovery-token-ca-cert-hash strings

对于基于令牌的发现,请验证根 CA 公钥是否匹配此哈希值(格式:"<type>:<value>")。

--discovery-token-unsafe-skip-ca-verification

对于基于令牌的发现,允许在未关联 --discovery-token-ca-cert-hash 参数的情况下添加节点。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

certs 操作的帮助命令。

--node-name string

指定节点名称。

--tls-bootstrap-token string

指定在加入节点时用于临时通过 Kubernetes 控制平面进行身份验证的令牌。

--token string

如果未提供这些值,则将它们用于 discovery-token 令牌和 tls-bootstrap 令牌。

从父命令继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

6.10.1.1.86 -

为新的控制平面组件生成清单。

概要

为新的控制平面组件生成清单(manifest)。

kubeadm join phase control-plane-prepare control-plane [flags]

选项

--apiserver-advertise-address string

对于将要托管新的控制平面实例的节点,指定 API 服务器将公布的其正在侦听的 IP 地址。如果未设置,则使用默认网络接口。

--apiserver-bind-port int32     默认值:6443

针对将要托管新的控制平面实例的节点,设置 API 服务器要绑定的端口。

--config string

kubeadm 配置文件的路径。

--control-plane

在此节点上创建一个新的控制平面实例。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

control-plane 操作的帮助命令。

--patches string

包含名为 "target[suffix][+patchtype].extension" 的文件的目录的路径。 例如 "kube-apiserver0+merge.yaml" 或仅仅是 "etcd.json"。 "target" 可以是 "kube-apiserver"、"kube-controller-manager"、 "kube-scheduler"、"etcd"、"kubeletconfiguration" 之一。 "patchtype" 可以是 "strategic"、"merge" 或者 "json" 之一, 并且它们与 kubectl 支持的补丁格式相同。 默认的 "patchtype" 是 "strategic"。 "extension" 必须是 "json" 或 "yaml"。 "suffix" 是一个可选字符串,可用于确定首先按字母顺序应用哪些补丁。

从父命令中继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

6.10.1.1.87 -

[实验] 从 kubeadm-certs Secret 下载控制平面节点之间共享的证书。

概要

[实验] 从 kubeadm-certs Secret 下载控制平面节点之间共享的证书。

kubeadm join phase control-plane-prepare download-certs [api-server-endpoint] [flags]

选项

--certificate-key string

使用此密钥可以解密由 init 上传的证书 Secret。

--config string

kubeadm 配置文件的路径。

--control-plane

在此节点上创建一个新的控制平面实例。

--discovery-file string

对于基于文件的发现,给出用于加载集群信息的文件或者 URL。

--discovery-token string

对于基于令牌的发现,该令牌用于验证从 API 服务器获取的集群信息。

--discovery-token-ca-cert-hash stringSlice

对于基于令牌的发现,请验证根 CA 公钥是否匹配此哈希值(格式:"<type>:<value>")。

--discovery-token-unsafe-skip-ca-verification

对于基于令牌的发现,允许在未关联 --discovery-token-ca-cert-hash 参数的情况下添加节点。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

download-certs 操作的帮助命令。

--tls-bootstrap-token string

指定在加入节点时用于临时通过 Kubernetes 控制平面进行身份验证的令牌。

--token string

如果未提供这些值,则将它们用于 discovery-token 令牌和 tls-bootstrap 令牌。

从父命令中继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

6.10.1.1.88 -

为新的控制平面组件生成 kubeconfig。

概要

为新的控制平面组件生成 kubeconfig。

kubeadm join phase control-plane-prepare kubeconfig [api-server-endpoint] [flags]

选项

--certificate-key string

使用此密钥可以解密由 init 上传的证书 Secret。

--config string

kubeadm 配置文件的路径。

--control-plane

在此节点上创建一个新的控制平面实例。

--discovery-file string

对于基于文件的发现,给出用于加载集群信息的文件或者 URL。

--discovery-token string

对于基于令牌的发现,该令牌用于验证从 API 服务器获取的集群信息。

--discovery-token-ca-cert-hash stringSlice

对于基于令牌的发现,请验证根 CA 公钥是否匹配此哈希值(格式:"<type>:<value>")。

--discovery-token-unsafe-skip-ca-verification

对于基于令牌的发现,允许在未关联 --discovery-token-ca-cert-hash 参数的情况下添加节点。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

kubeconfig 操作的帮助命令。

--tls-bootstrap-token string

指定在加入节点时用于临时通过 Kubernetes 控制平面进行身份验证的令牌。

--token string

如果未提供这些值,则将它们用于 discovery-token 令牌和 tls-bootstrap 令牌。

从父命令中继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

6.10.1.1.89 -

配置 kubelet、证书并(重新)启动 kubelet。

概要

生成一个包含 KubeletConfiguration 的文件和一个包含特定于节点的 kubelet 配置的环境文件,然后(重新)启动 kubelet。

kubeadm join phase kubelet-start [api-server-endpoint] [flags]

选项

--config string
kubeadm 配置文件的路径。
--cri-socket string

提供给 CRI 套接字建立连接的路径。如果为空,则 kubeadm 将尝试自动检测该值; 仅当安装了多个 CRI 或存在非标准的 CRI 套接字时,才使用此选项。

--discovery-file string

对于基于文件的发现,给出用于加载集群信息的文件或者 URL。

--discovery-token string

对于基于令牌的发现,该令牌用于验证从 API 服务器获取的集群信息。

--discovery-token-ca-cert-hash stringSlice

对于基于令牌的发现,验证根 CA 公钥是否匹配此哈希值(格式:"<type>:<value>")。

--discovery-token-unsafe-skip-ca-verification

对于基于令牌的发现,允许在未关联 --discovery-token-ca-cert-hash 参数的情况下添加节点。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

kubelet-start 操作的帮助命令。

--node-name string

指定节点名称。

--patches string

目录路径,指向的目录中包含名为 “target[suffix][+patchtype].extension” 的文件。 例如,"kube-apiserver0+merge.yaml" 或 "etcd.json" 这种简单形式。 "target" 可以是 “kube-apiserver”、“kube-controller-manager”、“kube-scheduler”、“etcd”、“kubeletconfiguration” 之一, "patchtype" 可以是 "strategic"、"merge" 或 "json" 之一,并且它们与 kubectl 支持的补丁格式匹配。 默认的 "patchtype" 为 "strategic"。 "extension" 必须为 "json" 或 "yaml"。 "suffix" 是一个可选字符串,可用于确定按字母顺序首先应用哪些补丁。

--tls-bootstrap-token string

指定在加入节点时用于临时通过 Kubernetes 控制平面进行身份验证的令牌。

--token string

如果未提供这些值,则将它们用于 discovery-token 令牌和 tls-bootstrap 令牌。

从父命令继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

6.10.1.1.90 -

运行 join 命令前检查。

概要

运行 kubeadm join 命令添加节点前检查。

kubeadm join phase preflight [api-server-endpoint] [flags]

示例

# 使用配置文件运行 kubeadm join 命令添加节点前检查。
kubeadm join phase preflight --config kubeadm-config.yaml

选项

--apiserver-advertise-address string

对于将要托管新的控制平面实例的节点,指定 API 服务器将公布的其正在侦听的 IP 地址。如果未设置,则使用默认网络接口。

--apiserver-bind-port int32     默认值:6443

针对将要托管新的控制平面实例的节点,设置 API 服务器要绑定的端口。

--certificate-key string

使用此密钥可以解密由 `init` 操作上传的证书 Secret。

--config string

kubeadm 配置文件的路径。

--control-plane

在此节点上创建一个新的控制平面实例。

--cri-socket string

提供给 CRI 套接字建立连接的路径。如果为空,则 kubeadm 将尝试自动检测该值; 仅当安装了多个 CRI 或存在非标准的 CRI 套接字时,才使用此选项。

--discovery-file string

对于基于文件的发现,给出用于加载集群信息的文件或者 URL。

--discovery-token string

对于基于令牌的发现,该令牌用于验证从 API 服务器获取的集群信息。

--discovery-token-ca-cert-hash strings

对于基于令牌的发现,验证根 CA 公钥是否匹配此哈希值(格式:"<type>:<value>")。

--discovery-token-unsafe-skip-ca-verification

对于基于令牌的发现,允许在未关联 --discovery-token-ca-cert-hash 参数的情况下添加节点。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

preflight 操作的帮助命令。

--ignore-preflight-errors stringSlice

错误将显示为警告的检查列表;例如:'IsPrivilegedUser,Swap'。取值为 'all' 时将忽略检查中的所有错误。

--node-name string

指定节点名称。

--tls-bootstrap-token string

指定在加入节点时用于临时通过 Kubernetes 控制平面进行身份验证的令牌。

--token string

如果未提供这些值,则将它们用于 discovery-token 令牌和 tls-bootstrap 令牌。

从父命令继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

6.10.1.1.91 -

kubeconfig 文件工具。

概要

kubeconfig 文件工具。

选项

-h, --help
kubeconfig 操作的帮助命令。

从父命令继承的选项

--rootfs string
[实验] 到 '真实' 主机根文件系统的路径。

6.10.1.1.92 -

为其他用户输出一个 kubeconfig 文件。

概要

为其他用户输出一个 kubeconfig 文件。

kubeadm kubeconfig user [flags]

示例

# 为一个名为 foo 的其他用户输出 kubeconfig 文件
kubeadm kubeconfig user --client-name=foo

# 使用 kubeadm 配置文件 bar 为另一个名为 foo 的用户输出 kubeconfig 文件
kubeadm alpha kubeconfig user --client-name=foo --config=bar

选项

--client-name string
用户名。如果生成客户端证书,则用作其 CN。
--config string
指向 kubeadm 配置文件的路径。
-h, --help
user 操作的帮助命令。
--org strings
客户端证书的组织。如果创建客户端证书,此值将用作其 O 字段值。
--token string
应该用此令牌做为 kubeconfig 的身份验证机制,而不是客户端证书。
--validity-period duration     Default: 8760h0m0s

客户证书的合法期限。所设置值为相对当前时间的偏移。

从父命令继承的选项

--rootfs string
[实验] 指向 '真实' 宿主机的根目录。

6.10.1.1.93 -

尽最大努力还原通过 'kubeadm init' 或者 'kubeadm join' 操作对主机所作的更改。

概要

尽最大努力还原通过 'kubeadm init' 或者 'kubeadm join' 操作对主机所作的更改。

"reset" 命令执行以下阶段:

preflight              Run reset pre-flight checks
remove-etcd-member     Remove a local etcd member.
cleanup-node           Run cleanup node.
kubeadm reset [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

存储证书的目录路径。如果已指定,则需要清空此目录。

--cleanup-tmp-dir

清理 "/etc/kubernetes/tmp" 目录。

--config string

kubeadm 配置文件的路径。

--cri-socket string

要连接的 CRI 套接字的路径。如果为空,则 kubeadm 将尝试自动检测此值; 仅当安装了多个 CRI 或存在非标准的 CRI 套接字时,才使用此选项。

--dry-run

不做任何更改;只输出将要执行的操作。

-f, --force

在不提示确认的情况下重置节点。

-h, --help

reset 操作的帮助命令。

--ignore-preflight-errors strings

错误将显示为警告的检查列表;例如:'IsPrivilegedUser,Swap'。取值为 'all' 时将忽略检查中的所有错误。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。如果未设置该标志,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

--skip-phases strings

要跳过的阶段列表。

从父命令继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

6.10.1.1.94 -

使用此命令来调用 reset 工作流程的某个阶段。

概要

使用此命令来调用 reset 工作流程的某个阶段。

选项

-h, --help

phase 操作的帮助命令。

从父命令继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

6.10.1.1.95 -

执行 cleanup node(清理节点)操作。

概要

执行 cleanup node(清理节点)操作。

kubeadm reset phase cleanup-node [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

存储证书的目录路径。如果已指定,则需要清空此目录。

--cleanup-tmp-dir

清理 "/etc/kubernetes/tmp" 目录。

--cri-socket string

要连接的 CRI 套接字的路径。如果为空,则 kubeadm 将尝试自动检测此值;仅当安装了多个 CRI 或具有非标准 CRI 套接字时,才使用此选项。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

cleanup-node 操作的帮助命令。

从父命令继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

6.10.1.1.96 -

kubeadm reset(重置)前运行启动前检查。

概要

kubeadm reset(重置)前运行启动前检查。

kubeadm reset phase preflight [flags]

选项

--dry-run

不做任何更改;只输出将要执行的操作。

-f, --force

在不提示确认的情况下重置节点。

-h, --help

preflight 操作的帮助命令。

--ignore-preflight-errors strings

错误将显示为警告的检查列表;例如:'IsPrivilegedUser,Swap'。取值为 'all' 时将忽略检查中的所有错误。

从父命令继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

6.10.1.1.97 -

删除本地 etcd 成员。

概要

删除控制平面节点的本地 etcd 成员。

kubeadm reset phase remove-etcd-member [flags]

选项

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

remove-etcd-member 操作的帮助命令。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 Kubeconfig 文件。如果未设置该标志,则可以在默认位置中查找现有的 Kubeconfig 文件。

从父命令继承的选项

--rootfs string

[实验] 到'真实'主机根文件系统的路径。

6.10.1.1.98 -

管理引导令牌。

概要

此命令管理引导令牌(bootstrap token)。它是可选的,仅适用于高级用例。

简而言之,引导令牌(bootstrap token)用于在客户端和服务器之间建立双向信任。 当客户端(例如,即将加入集群的节点)需要时,可以使用引导令牌相信正在与之通信的服务器。 然后可以使用具有 “签名” 的引导令牌。

引导令牌还可以作为一种允许对 API 服务器进行短期身份验证的方法(令牌用作 API 服务器信任客户端的方式),例如用于执行 TLS 引导程序。

引导令牌准确来说是什么?

  • 它是位于 kube-system 命名空间中类型为 “bootstrap.kubernetes.io/token” 的一个 Secret。
  • 引导令牌的格式必须为 “[a-z0-9]{6}.[a-z0-9]{16}”,前一部分是公共令牌 ID,而后者是令牌秘钥,必须在任何情况下都保密!
  • 必须将 Secret 的名称命名为 “bootstrap-token-(token-id)”。

你可以在此处阅读有关引导令牌(bootstrap token)的更多信息: /docs/admin/bootstrap-tokens/

kubeadm token [flags]

选项

--dry-run

是否启用 `dry-run` 模式。

-h, --help

token 操作的帮助命令。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。如果未设置,则搜索一组标准位置以查找现有 kubeconfig 文件。

从父命令继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

6.10.1.1.99 -

在服务器上创建引导令牌。

概要

这个命令将为你创建一个引导令牌。 你可以设置此令牌的用途,"有效时间" 和可选的人性化的描述。

这里的 [token] 是指将要生成的实际令牌。 该令牌应该是一个通过安全机制生成的随机令牌,形式为 "[a-z0-9]{6}.[a-z0-9]{16}"。 如果没有给出 [token],kubeadm 将生成一个随机令牌。

kubeadm token create [token]

选项

--certificate-key string

当与 “--print-join-command” 一起使用时,打印作为控制平面加入集群时所需的所有 “kubeadm join” 标志。 要创建新的证书密钥,你必须使用 “kubeadm init phase upload-certs --upload-certs”。

--config string

kubeadm 配置文件的路径。

--description string

针对令牌用途的人性化的描述。

--groups stringSlice     默认值:[system:bootstrappers:kubeadm:default-node-token]

此令牌用于身份验证时将对其他组进行身份验证。必须匹配 "\\Asystem:bootstrappers:[a-z0-9:-]{0,255}[a-z0-9]\\z"

-h, --help

create 操作的帮助命令。

--print-join-command

不仅仅打印令牌,而是打印使用令牌加入集群所需的完整 'kubeadm join' 参数。

--ttl duration     默认值:24h0m0s

令牌有效时间,超过该时间令牌被自动删除。(例如:1s, 2m, 3h)。如果设置为 '0',令牌将永远不过期。

--usages stringSlice     默认值:[signing,authentication]

描述可以使用此令牌的方式。你可以多次使用 `--usages` 或者提供一个以逗号分隔的选项列表。合法选项有: [signing,authentication]

从父命令继承的选项

--dry-run

是否启用 `dry-run` 运行模式。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

用于和集群通信的 kubeconfig 文件。如果它没有被设置,那么 kubeadm 将会搜索一个已经存在于标准路径的 kubeconfig 文件。

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

6.10.1.1.100 -

删除服务器上的引导令牌。

概要

这个命令将为你删除指定的引导令牌列表。

[token-value] 是要删除的 "[a-z0-9]{6}.[a-z0-9]{16}" 形式的完整令牌或者是 "[a-z0-9]{6}" 形式的令牌 ID。

kubeadm token delete [token-value] ...

选项

-h, --help

delete 操作的帮助命令。

从父命令继承的选项

--dry-run

是否启用 `dry-run` 运行模式。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

用于和集群通信的 kubeconfig 文件。如果它没有被设置,那么 kubeadm 将会搜索一个已经存在于标准路径的 kubeconfig 文件。

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

6.10.1.1.101 -

生成并打印一个引导令牌,但不要在服务器上创建它。

概要

此命令将打印一个随机生成的可以被 "init" 和 "join" 命令使用的引导令牌。

你不必使用此命令来生成令牌。你可以自己设定,只要格式符合 "[a-z0-9]{6}.[a-z0-9]{16}"。 之所以提供这个命令是为了方便生成规定格式的令牌。

你也可以使用 "kubeadm init" 并且不指定令牌,该命令会生成一个令牌并打印出来。

kubeadm token generate [flags]

选项

-h, --help

generate 操作的帮助命令。

从父命令继承的选项

--dry-run

是否启用 `dry-run` 模式。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

用于和集群通信的 kubeconfig 文件。如果它没有被设置,那么 kubeadm 将会搜索一个已经存在于标准路径的 kubeconfig 文件。

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

6.10.1.1.102 -

列出服务器上的引导令牌。

概要

此命令将为你列出所有的引导令牌。

kubeadm token list [flags]

选项

--allow-missing-template-keys     默认值:true

如果设置为 true,则在模板中缺少字段或哈希表的键时忽略模板中的任何错误。 仅适用于 golang 和 jsonpath 输出格式。

-o, --experimental-output string     默认值:"text"

输出格式:text|json|yaml|go-template|go-template-file|template|templatefile|jsonpath|jsonpath-as-json|jsonpath-file 其中之一

-h, --help

list 操作的帮助命令。

--show-managed-fields

如果为 true,则在以 JSON 或 YAML 格式打印对象时保留 managedFields。

从父命令继承的选项

--dry-run

是否启用 `dry-run` 模式。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

用于和集群通信的 kubeconfig 文件。如果它没有被设置,那么 kubeadm 将会搜索一个已经存在于标准路径的 kubeconfig 文件。

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

6.10.1.1.103 -

此命令能将集群平滑升级到新版本。

概要

此命令能将集群平滑升级到新版本。

kubeadm upgrade [flags]

选项

-h, --help

upgrade 操作的帮助命令。

继承于父命令的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

6.10.1.1.104 -

将 Kubernetes 集群升级到指定版本。

概要

将 Kubernetes 集群升级到指定版本。

kubeadm upgrade apply [version]

选项

--allow-experimental-upgrades

显示 Kubernetes 的不稳定版本作为升级替代方案,并允许升级到 Kubernetes 的 Alpha、Beta 或 RC 版本。

--allow-release-candidate-upgrades

显示 Kubernetes 的候选版本作为升级替代方案,并允许升级到 Kubernetes 的 RC 版本。

--certificate-renewal     Default: true

执行升级期间更改的组件所使用的证书的更新。

--config string

kubeadm 配置文件的路径。

--dry-run

不要更改任何状态,只输出要执行的操作。

--etcd-upgrade     默认值: true

执行 etcd 的升级。

--feature-gates string

一组键值对,用于描述各种功能。选项包括:
EtcdLearnerMode=true|false (ALPHA - 默认值=false)
PublicKeysECDSA=true|false (ALPHA - 默认值=false)
RootlessControlPlane=true|false (ALPHA - 默认值=false)
UpgradeAddonsBeforeControlPlane=true|false (DEPRECATED - 默认值=false)

-f, --force

强制升级,但可能无法满足某些要求。这也意味着非交互模式。

-h, --help

apply 操作的帮助命令。

-ignore-preflight-errors strings

错误将显示为警告的检查列表;例如:'IsPrivilegedUser,Swap'。取值为 'all' 时将忽略检查中的所有错误。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。如果未设置标志,则在相关目录下搜索以查找现有 kubeconfig 文件。

--patches string

包含名为 "target[suffix][+patchtype].extension" 的文件的目录的路径。 例如,"kube-apiserver0+merge.yaml"或是简单的 "etcd.json"。 "target" 可以是 "kube-apiserver"、"kube-controller-manager"、"kube-scheduler"、"etcd"、"kubeletconfiguration" 之一。 "patchtype" 可以是 "strategic"、"merge" 或者 "json" 之一, 并且它们与 kubectl 支持的补丁格式相同。 默认的 "patchtype" 是 "strategic"。 "extension" 必须是 "json" 或 "yaml"。 "suffix" 是一个可选字符串,可用于确定首先按字母顺序应用哪些补丁。

--print-config

指定是否应打印将在升级中使用的配置文件。

-y, --yes

执行升级,不提示确认(非交互模式)。

从父命令继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

6.10.1.1.105 -

显示哪些差异将被应用于现有的静态 Pod 资源清单。参考:kubeadm upgrade apply --dry-run

概述

显示哪些差异将被应用于现有的静态 Pod 资源清单。参考:kubeadm upgrade apply --dry-run

kubeadm upgrade diff [version] [flags]

选项

--api-server-manifest string     默认值:"/etc/kubernetes/manifests/kube-apiserver.yaml"

API 服务器清单的路径。

--config string

kubeadm 配置文件的路径。

-c, --context-lines int     默认值:3
diff 涉及了多少行上下文。
--controller-manager-manifest string     默认值:"/etc/kubernetes/manifests/kube-controller-manager.yaml"

控制器清单的路径。

-h, --help

diff 操作的帮助命令

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件,如果标志是未设置,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

--scheduler-manifest string     默认值:"/etc/kubernetes/manifests/kube-scheduler.yaml"

调度程序清单的路径。

从父命令继承的选项

--rootfs string

[实验] 指向“真实”主机根文件系统的路径。

6.10.1.1.106 -

升级集群中某个节点的命令。

概要

升级集群中某个节点的命令。

"node" 命令执行以下阶段:

preflight       执行节点升级前检查
control-plane   如果存在的话,升级部署在该节点上的管理面实例
kubelet-config  更新该节点上的 kubelet 配置
kubeadm upgrade node [flags]

选项

--certificate-renewal     默认值: true

对升级期间变化的组件所使用的证书执行续订。

--dry-run

不更改任何状态,只输出将要执行的操作。

--etcd-upgrade     默认值: true

执行 etcd 的升级。

-h, --help

node 操作的帮助命令。

--ignore-preflight-errors strings

其错误将显示为警告的检查列表。示例:'IsPrivilegedUser,Swap'。值 'all' 忽略所有检查中的错误。

--kubeconfig string     默认值: "/etc/kubernetes/admin.conf"

用于与集群交互的 kubeconfig 文件。如果参数未指定,将从一系列标准位置检索存在的 kubeconfig 文件。

--patches string

包含名为 "target[suffix][+patchtype].extension" 的文件的目录的路径。 例如,"kube-apiserver0+merge.yaml"或仅仅是 "etcd.json"。 "target" 可以是 "kube-apiserver"、"kube-controller-manager"、"kube-scheduler"、"etcd"、"kubeletconfiguration" 之一。 "patchtype" 可以是 "strategic"、"merge" 或者 "json" 之一, 并且它们与 kubectl 支持的补丁格式相同。 默认的 "patchtype" 是 "strategic"。 "extension" 必须是"json" 或"yaml"。 "suffix" 是一个可选字符串,可用于确定首先按字母顺序应用哪些补丁。

--skip-phases strings

要跳过的阶段的列表。

从父命令继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

6.10.1.1.107 -

使用此命令调用 node 工作流的某个阶段。

概要

使用此命令调用 node 工作流的某个阶段。

选项

-h, --help

phase 操作的帮助命令。

从父命令继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

6.10.1.1.108 -

升级部署在此节点上的控制平面实例,如果有的话。

概要

升级部署在此节点上的控制平面实例,如果有的话。

kubeadm upgrade node phase control-plane [flags]

选项

--certificate-renewal

续订在升级期间变更的组件所使用的证书。

--dry-run

不改变任何状态,只输出将要执行的动作。

--etcd-upgrade     默认值: true

执行 etcd 的升级。

-h, --help

control-plane 操作的帮助命令。

--kubeconfig string     默认值: "/etc/kubernetes/admin.conf"

用于和集群通信的 KubeConfig 文件。如果它没有被设置,那么 kubeadm 将会搜索一个已经存在于标准路径的 KubeConfig 文件。

--patches string
包含名为 "target[suffix][+patchtype].extension" 的文件的目录的路径。 例如,"kube-apiserver0+merge.yaml" 或仅仅是 "etcd.json"。 "target" 可以是 "kube-apiserver"、"kube-controller-manager"、"kube-scheduler"、"etcd"、"kubeletconfiguration" 之一。 "patchtype" 可以是 "strategic"、"merge" 或 "json" 之一,并且它们与 kubectl 支持的补丁格式匹配。 默认的 "patchtype" 为 "strategic"。"extension" 必须为 "json" 或 "yaml"。 "suffix" 是一个可选字符串,可用于确定首先按字母顺序应用哪些补丁。

从父命令继承的选项

--rootfs string

[实验] 指向 “真实” 主机根文件系统的路径。

6.10.1.1.109 -

升级此节点的 kubelet 配置。

概要

从集群中 ConfigMap kubelet-config 下载 kubelet 配置。

kubeadm upgrade node phase kubelet-config [flags]

选项

--dry-run

不改变任何状态,只输出将要执行的操作。

-h, --help

kubelet-config 操作的帮助命令。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

用于和集群通信的 kubeconfig 文件。如果它没有被设置,那么 kubeadm 将会搜索一个已经存在于标准路径的 kubeconfig 文件。

--patches string

目录路径,指向的目录中包含名为 “target[suffix][+patchtype].extension” 的文件。 例如,"kube-apiserver0+merge.yaml" 或 "etcd.json" 这种简单形式。 "target" 可以是 “kube-apiserver”、“kube-controller-manager”、“kube-scheduler”、“etcd”、“kubeletconfiguration” 之一, "patchtype" 可以是 "strategic"、"merge" 或 "json" 之一,并且它们与 kubectl 支持的补丁格式匹配。 默认的 "patchtype" 为 "strategic"。 "extension" 必须为 "json" 或 "yaml"。 "suffix" 是一个可选字符串,可用于确定按字母顺序首先应用哪些补丁。

从父命令继承的选项

--rootfs string

[实验] 到'真实'主机根文件系统的路径。

6.10.1.1.110 -

执行升级节点的预检。

概要

执行 kubeadm 升级节点的预检。

kubeadm upgrade node phase preflight [flags]

选项

-h, --help

preflight 操作的帮助命令。

--ignore-preflight-errors strings

错误将显示为警告的检查清单。示例:'IsPrivilegedUser,Swap'。值为 'all' 表示忽略所有检查的错误。

继承于父命令的选项

--rootfs string

[实验] 指向 “真实” 主机根文件系统的路径。

6.10.1.1.111 -

概述

检查可升级到哪些版本,并验证你当前的集群是否可升级。 要跳过互联网检查,请传递可选的 [version] 参数

kubeadm upgrade plan [version] [flags]

选项

--allow-experimental-upgrades

显示不稳定版本的 Kubernetes 作为升级替代方案,并允许升级到 Kubernetes 的 Alpha、Beta 或 RC 版本。

--allow-release-candidate-upgrades

显示 Kubernetes 的发行候选版本作为升级选择,并允许升级到 Kubernetes 的 RC 版本。

--config string

配置文件的路径。

--feature-gates string

一组描述各种特征特性门控的键值对。选项有:
EtcdLearnerMode=true|false (ALPHA - 默认值=false)
PublicKeysECDSA=true|false (ALPHA - 默认值=false)
RootlessControlPlane=true|false (ALPHA - 默认值=false)
UpgradeAddonsBeforeControlPlane=true|false (DEPRECATED - 默认值=false)

-h, --help

plan 操作的帮助命令。

--ignore-preflight-errors strings

其错误将显示为警告的检查列表。例如:'IsPrivilegedUser,Swap'。值 'all' 忽略所有检查错误。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。如果标志为未设置,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

-o, --output string      默认值:"text"

实验性功能;输出格式为 text|json|yaml 其中的一种。

--print-config

指定是否打印将在升级中使用的配置文件。

--show-managed-fields

如果开启,以 JSON 或 YAML 格式打印对象时会保留 managedField。

从父命令继承的选项

--rootfs string

[实验] 指向 “真实” 宿主机根文件系统的路径。

6.10.1.1.112 -

打印 kubeadm 的版本。

概要

打印 kubeadm 的版本。

kubeadm version [flags]

选项

-h, --help

version 操作的帮助命令。

-o, --output string

输出格式;可用的选项有 'yaml'、'json' 和 'short'。

从父命令继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

6.10.1.1.113 -

此目录下的所有文件都是从其他仓库自动生成的。 不要人工编辑它们。 你必须在上游仓库中编辑它们

6.10.1.2 - kubeadm init

此命令初始化一个 Kubernetes 控制平面节点。

运行此命令以安装 Kubernetes 控制平面。

概要

运行此命令来搭建 Kubernetes 控制平面节点。

"init" 命令执行以下阶段:

preflight                    Run pre-flight checks
certs                        Certificate generation
  /ca                          Generate the self-signed Kubernetes CA to provision identities for other Kubernetes components
  /apiserver                   Generate the certificate for serving the Kubernetes API
  /apiserver-kubelet-client    Generate the certificate for the API server to connect to kubelet
  /front-proxy-ca              Generate the self-signed CA to provision identities for front proxy
  /front-proxy-client          Generate the certificate for the front proxy client
  /etcd-ca                     Generate the self-signed CA to provision identities for etcd
  /etcd-server                 Generate the certificate for serving etcd
  /etcd-peer                   Generate the certificate for etcd nodes to communicate with each other
  /etcd-healthcheck-client     Generate the certificate for liveness probes to healthcheck etcd
  /apiserver-etcd-client       Generate the certificate the apiserver uses to access etcd
  /sa                          Generate a private key for signing service account tokens along with its public key
kubeconfig                   Generate all kubeconfig files necessary to establish the control plane and the admin kubeconfig file
  /admin                       Generate a kubeconfig file for the admin to use and for kubeadm itself
  /kubelet                     Generate a kubeconfig file for the kubelet to use *only* for cluster bootstrapping purposes
  /controller-manager          Generate a kubeconfig file for the controller manager to use
  /scheduler                   Generate a kubeconfig file for the scheduler to use
etcd                         Generate static Pod manifest file for local etcd
  /local                       Generate the static Pod manifest file for a local, single-node local etcd instance
control-plane                Generate all static Pod manifest files necessary to establish the control plane
  /apiserver                   Generates the kube-apiserver static Pod manifest
  /controller-manager          Generates the kube-controller-manager static Pod manifest
  /scheduler                   Generates the kube-scheduler static Pod manifest
kubelet-start                Write kubelet settings and (re)start the kubelet
upload-config                Upload the kubeadm and kubelet configuration to a ConfigMap
  /kubeadm                     Upload the kubeadm ClusterConfiguration to a ConfigMap
  /kubelet                     Upload the kubelet component config to a ConfigMap
upload-certs                 Upload certificates to kubeadm-certs
mark-control-plane           Mark a node as a control-plane
bootstrap-token              Generates bootstrap tokens used to join a node to a cluster
kubelet-finalize             Updates settings relevant to the kubelet after TLS bootstrap
  /experimental-cert-rotation  Enable kubelet client certificate rotation
addon                        Install required addons for passing conformance tests
  /coredns                     Install the CoreDNS addon to a Kubernetes cluster
  /kube-proxy                  Install the kube-proxy addon to a Kubernetes cluster
show-join-command            Show the join command for control-plane and worker node
kubeadm init [flags]

选项

--apiserver-advertise-address string

API 服务器所公布的其正在监听的 IP 地址。如果未设置,则使用默认网络接口。

--apiserver-bind-port int32     默认值:6443

API 服务器绑定的端口。

--apiserver-cert-extra-sans strings

用于 API Server 服务证书的可选附加主题备用名称(SAN)。可以是 IP 地址和 DNS 名称。

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存和存储证书的路径。

--certificate-key string

用于加密 kubeadm-certs Secret 中的控制平面证书的密钥。

--config string

kubeadm 配置文件的路径。

--control-plane-endpoint string

为控制平面指定一个稳定的 IP 地址或 DNS 名称。

--cri-socket string

要连接的 CRI 套接字的路径。如果为空,则 kubeadm 将尝试自动检测此值; 仅当安装了多个 CRI 或具有非标准 CRI 套接字时,才使用此选项。

--dry-run

不做任何更改;只输出将要执行的操作。

--feature-gates string
一组用来描述各种功能特性的键值(key=value)对。选项是:
EtcdLearnerMode=true|false (ALPHA - 默认值=false)
PublicKeysECDSA=true|false (ALPHA - 默认值=false)
RootlessControlPlane=true|false (ALPHA - 默认值=false)
UpgradeAddonsBeforeControlPlane=true|false (DEPRECATED - 默认值=false)
-h, --help

init 操作的帮助命令。

--ignore-preflight-errors strings

错误将显示为警告的检查列表;例如:'IsPrivilegedUser,Swap'。取值为 'all' 时将忽略检查中的所有错误。

--image-repository string     默认值:"registry.k8s.io"

选择用于拉取控制平面镜像的容器仓库。

--kubernetes-version string     默认值:"stable-1"

为控制平面选择一个特定的 Kubernetes 版本。

--node-name string

指定节点的名称。

--patches string

它包含名为 "target[suffix][+patchtype].extension" 的文件的目录的路径。 例如,"kube-apiserver0+merge.yaml"或仅仅是 "etcd.json"。 "target" 可以是 "kube-apiserver"、"kube-controller-manager"、"kube-scheduler"、"etcd"、"kubeletconfiguration" 之一。 "patchtype" 可以是 "strategic"、"merge" 或者 "json" 之一, 并且它们与 kubectl 支持的补丁格式相同。 默认的 "patchtype" 是 "strategic"。 "extension" 必须是"json" 或"yaml"。 "suffix" 是一个可选字符串,可用于确定首先按字母顺序应用哪些补丁。

--pod-network-cidr string

指明 Pod 网络可以使用的 IP 地址段。如果设置了这个参数,控制平面将会为每一个节点自动分配 CIDR。

--service-cidr string     默认值:"10.96.0.0/12"

为服务的虚拟 IP 地址另外指定 IP 地址段。

--service-dns-domain string     默认值:"cluster.local"
为服务另外指定域名,例如:"myorg.internal"。
--skip-certificate-key-print

不要打印用于加密控制平面证书的密钥。

--skip-phases strings

要跳过的阶段列表。

--skip-token-print

跳过打印 'kubeadm init' 生成的默认引导令牌。

--token string

这个令牌用于建立控制平面节点与工作节点间的双向通信。 格式为 [a-z0-9]{6}.[a-z0-9]{16} - 示例:abcdef.0123456789abcdef

--token-ttl duration     默认值:24h0m0s

令牌被自动删除之前的持续时间(例如 1s,2m,3h)。如果设置为 '0',则令牌将永不过期。

--upload-certs

将控制平面证书上传到 kubeadm-certs Secret。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

Init 命令的工作流程

kubeadm init 命令通过执行下列步骤来启动一个 Kubernetes 控制平面节点。

  1. 在做出变更前运行一系列的预检项来验证系统状态。一些检查项目仅仅触发警告, 其它的则会被视为错误并且退出 kubeadm,除非问题得到解决或者用户指定了 --ignore-preflight-errors=<错误列表> 参数。
  1. 生成一个自签名的 CA 证书来为集群中的每一个组件建立身份标识。 用户可以通过将其放入 --cert-dir 配置的证书目录中(默认为 /etc/kubernetes/pki) 来提供他们自己的 CA 证书以及/或者密钥。 APIServer 证书将为任何 --apiserver-cert-extra-sans 参数值提供附加的 SAN 条目,必要时将其小写。
  1. 将 kubeconfig 文件写入 /etc/kubernetes/ 目录以便 kubelet、控制器管理器和调度器用来连接到 API 服务器,它们每一个都有自己的身份标识。再编写额外的 kubeconfig 文件,将 kubeadm 作为管理实体(admin.conf)和可以绕过 RBAC 的超级管理员用户(super-admin.conf)。
  1. 为 API 服务器、控制器管理器和调度器生成静态 Pod 的清单文件。假使没有提供一个外部的 etcd 服务的话,也会为 etcd 生成一份额外的静态 Pod 清单文件。

    静态 Pod 的清单文件被写入到 /etc/kubernetes/manifests 目录; kubelet 会监视这个目录以便在系统启动的时候创建 Pod。

    一旦控制平面的 Pod 都运行起来,kubeadm init 的工作流程就继续往下执行。

  1. 对控制平面节点应用标签和污点标记以便不会在它上面运行其它的工作负载。
  1. 生成令牌,将来其他节点可使用该令牌向控制平面注册自己。如 kubeadm token 文档所述,用户可以选择通过 --token 提供令牌。
  1. 为了使得节点能够遵照启动引导令牌TLS 启动引导 这两份文档中描述的机制加入到集群中,kubeadm 会执行所有的必要配置:

    • 创建一个 ConfigMap 提供添加集群节点所需的信息,并为该 ConfigMap 设置相关的 RBAC 访问规则。

    • 允许启动引导令牌访问 CSR 签名 API。

    • 配置自动签发新的 CSR 请求。

    更多相关信息,请查看 kubeadm join

  1. 通过 API 服务器安装一个 DNS 服务器 (CoreDNS) 和 kube-proxy 附加组件。 在 Kubernetes v1.11 和更高版本中,CoreDNS 是默认的 DNS 服务器。 请注意,尽管已部署 DNS 服务器,但直到安装 CNI 时才调度它。

在 kubeadm 中使用 init 阶段

Kubeadm 允许你使用 kubeadm init phase 命令分阶段创建控制平面节点。

要查看阶段和子阶段的有序列表,可以调用 kubeadm init --help。 该列表将位于帮助屏幕的顶部,每个阶段旁边都有一个描述。 注意,通过调用 kubeadm init,所有阶段和子阶段都将按照此确切顺序执行。

某些阶段具有唯一的标志,因此,如果要查看可用选项的列表,请添加 --help,例如:

sudo kubeadm init phase control-plane controller-manager --help

你也可以使用 --help 查看特定父阶段的子阶段列表:

sudo kubeadm init phase control-plane --help

kubeadm init 还公开了一个名为 --skip-phases 的参数,该参数可用于跳过某些阶段。 参数接受阶段名称列表,并且这些名称可以从上面的有序列表中获取。

例如:

sudo kubeadm init phase control-plane all --config=configfile.yaml
sudo kubeadm init phase etcd local --config=configfile.yaml
# 你现在可以修改控制平面和 etcd 清单文件
sudo kubeadm init --skip-phases=control-plane,etcd --config=configfile.yaml

该示例将执行的操作是基于 configfile.yaml 中的配置在 /etc/kubernetes/manifests 中写入控制平面和 etcd 的清单文件。 这允许你修改文件,然后使用 --skip-phases 跳过这些阶段。 通过调用最后一个命令,你将使用自定义清单文件创建一个控制平面节点。

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

或者,你可以使用 InitConfiguration 下的 skipPhases 字段。

结合一份配置文件来使用 kubeadm init

通过一份配置文件而不是使用命令行参数来配置 kubeadm init 命令是可能的, 但是一些更加高级的功能只能够通过配置文件设定。 这份配置文件通过 --config 选项参数指定的, 它必须包含 ClusterConfiguration 结构,并可能包含更多由 ---\n 分隔的结构。 在某些情况下,可能不允许将 --config 与其他标志混合使用。

可以使用 kubeadm config print 命令打印出默认配置。

如果你的配置没有使用最新版本,推荐使用 kubeadm config migrate 命令进行迁移。

关于配置的字段和用法的更多信息,你可以访问 API 参考页面

使用 kubeadm init 时设置特性门控

Kubeadm 支持一组独有的特性门控,只能在 kubeadm init 创建集群期间使用。 这些特性可以控制集群的行为。特性门控会在毕业到 GA 后被移除。

你可以使用 --feature-gates 标志来为 kubeadm init 设置特性门控, 或者你可以在用 --config 传递配置文件时添加条目到 featureGates 字段中。

直接传递 Kubernetes 核心组件的特性门控给 kubeadm 是不支持的。 相反,可以通过使用 kubeadm API 的自定义组件来传递。

特性门控的列表:

kubeadm 特性门控
特性默认值AlphaBetaGA
EtcdLearnerModetrue1.271.29-
PublicKeysECDSAfalse1.19--
RootlessControlPlanefalse1.22--

特性门控的描述:

EtcdLearnerMode
启用此特性门控后,当加入新的控制平面节点时,将创建一个新的 etcd 成员作为学习者(learner),并仅在 etcd 数据完全对齐后进级为投票成员(voting member)。
PublicKeysECDSA
可用于创建集群时使用 ECDSA 证书而不是默认 RSA 算法。 支持用 kubeadm certs renew 更新现有 ECDSA 证书, 但你不能在集群运行期间或升级期间切换 RSA 和 ECDSA 算法。
RootlessControlPlane
设置此标志来配置 kubeadm 所部署的控制平面组件中的静态 Pod 容器 kube-apiserverkube-controller-managerkube-scheduleretcd 以非 root 用户身份运行。如果未设置该标志,则这些组件以 root 身份运行。 你可以在升级到更新版本的 Kubernetes 之前更改此特性门控的值。

已弃用特性门控的列表:

kubeadm 弃用的特性门控
特性默认值
UpgradeAddonsBeforeControlPlanefalse

特性门控描述:

UpgradeAddonsBeforeControlPlane
这是一个在 Kubernetes v1.28 中引入的默认禁用的特性门控, 目的是在集群升级期间允许重新激活旧版且已弃用的行为。对于早于 v1.28 的 kubeadm 版本, 在 kubeadm upgrade apply 期间会立即升级集群插件(包括 CoreDNS 和 kube-proxy), 而不管是否有其他未升级的控制平面实例。这可能导致兼容性问题。从 v1.28 开始, kubeadm 默认采用的模式是在开始升级插件之前始终检查是否所有控制平面实例都已完成升级。 此行为适用于 kubeadm upgrade applykubeadm upgrade node。 kubeadm 通过检查 kube-apiserver Pod 的镜像来确定控制平面实例是否已升级。 你必须按顺序执行控制平面实例的升级, 或者至少确保在所有其他控制平面实例完全升级之前不启动最后一个控制平面实例的升级, 并且在最后一个控制平面实例升级完成后再执行插件的升级。 这个弃用的 UpgradeAddonsBeforeControlPlane 特性门控使你有机会保留旧的升级行为。 你不应该需要这种旧的行为;如果确实需要,请考虑更改集群或升级流程, 因为此特性门控将在未来的版本中被移除。

已移除的特性门控列表:

kubeadm 已移除的特性门控
特性AlphaBetaGA移除
IPv6DualStack1.161.211.231.24
UnversionedKubeletConfigMap1.221.231.251.26

特性门控的描述:

IPv6DualStack
在 IP 双栈特性处于开发过程中时,此标志有助于配置组件的双栈支持。有关 Kubernetes 双栈支持的更多详细信息,请参阅 kubeadm 的双栈支持
UnversionedKubeletConfigMap
此标志控制 kubeadm 存储 kubelet 配置数据的 ConfigMap 的名称。 在未指定此标志或设置为 true 的情况下,此 ConfigMap 被命名为 kubelet-config。 如果将此标志设置为 false,则此 ConfigMap 的名称会包括 Kubernetes 的主要版本和次要版本 (例如:kubelet-config-1.30)。 kubeadm 会确保用于读写 ConfigMap 的 RBAC 规则适合你设置的值。 当 kubeadm 写入此 ConfigMap 时(在 kubeadm initkubeadm upgrade apply 期间), kubeadm 根据 UnversionedKubeletConfigMap 的设置值来执行操作。 当读取此 ConfigMap 时(在执行 kubeadm joinkubeadm resetkubeadm upgrade 等操作期间), kubeadm 尝试首先使用无版本(后缀)的 ConfigMap 名称; 如果不成功,kubeadm 将回退到使用该 ConfigMap 的旧(带版本号的)名称。

添加 kube-proxy 参数

kubeadm 配置中有关 kube-proxy 的说明请查看:

使用 kubeadm 启用 IPVS 模式的说明请查看:

向控制平面组件传递自定义的命令行参数

有关向控制平面组件传递命令行参数的说明请查看:

在没有互联网连接的情况下运行 kubeadm

要在没有互联网连接的情况下运行 kubeadm,你必须提前拉取所需的控制平面镜像。

你可以使用 kubeadm config images 子命令列出并拉取镜像:

kubeadm config images list
kubeadm config images pull

你可以通过 --configkubeadm 配置文件 传递给上述命令来控制 kubernetesVersionimageRepository 字段。

kubeadm 需要的所有默认 registry.k8s.io 镜像都支持多种硬件体系结构。

使用自定义的镜像

默认情况下,kubeadm 会从 registry.k8s.io 仓库拉取镜像。如果请求的 Kubernetes 版本是 CI 标签 (例如 ci/latest),则使用 gcr.io/k8s-staging-ci-images

你可以通过使用带有配置文件的 kubeadm 来重写此操作。 允许的自定义功能有:

  • 提供影响镜像版本的 kubernetesVersion
  • 使用其他的 imageRepository 来代替 registry.k8s.io
  • 为 etcd 或 CoreDNS 提供特定的 imageRepositoryimageTag

由于向后兼容的原因,使用 imageRepository 所指定的定制镜像库可能与默认的 registry.k8s.io 镜像路径不同。例如,某镜像的子路径可能是 registry.k8s.io/subpath/image, 但使用自定义仓库时默认为 my.customrepository.io/image

确保将镜像推送到 kubeadm 可以使用的自定义仓库的路径中,你必须:

  • 使用 kubeadm config images {list|pull}registry.k8s.io 的默认路径中拉取镜像。
  • 将镜像推送到 kubeadm config images list --config=config.yaml 的路径, 其中 config.yaml 包含自定义的 imageRepository 和/或用于 etcd 和 CoreDNS 的 imageTag
  • 将相同的 config.yaml 传递给 kubeadm init

定制沙箱(pause)镜像

如果需要为这些组件设置定制的镜像, 你需要在你的容器运行时中完成一些配置。 参阅你的容器运行时的文档以了解如何改变此设置。 对于某些容器运行时而言, 你可以在容器运行时主题下找到一些建议。

将控制平面证书上传到集群

通过将参数 --upload-certs 添加到 kubeadm init,你可以将控制平面证书临时上传到集群中的 Secret。 请注意,此 Secret 将在 2 小时后自动过期。这些证书使用 32 字节密钥加密,可以使用 --certificate-key 指定该密钥。 通过将 --control-plane--certificate-key 传递给 kubeadm join, 可以在添加其他控制平面节点时使用相同的密钥下载证书。

以下阶段命令可用于证书到期后重新上传证书:

kubeadm init phase upload-certs --upload-certs --config=SOME_YAML_FILE

如果未将预定义的证书密钥传递给 kubeadm initkubeadm init phase upload-certs, 则会自动生成一个新密钥。

以下命令可用于按需生成新密钥:

kubeadm certs certificate-key

使用 kubeadm 管理证书

有关使用 kubeadm 进行证书管理的详细信息, 请参阅使用 kubeadm 进行证书管理。 该文档包括有关使用外部 CA、自定义证书和证书续订的信息。

管理 kubeadm 为 kubelet 提供的 systemd 配置文件

kubeadm 包自带了关于 systemd 如何运行 kubelet 的配置文件。 请注意 kubeadm 客户端命令行工具永远不会修改这份 systemd 配置文件。 这份 systemd 配置文件属于 kubeadm DEB/RPM 包。

有关更多信息,请阅读管理 systemd 的 kubeadm 内嵌文件

结合 CRI 运行时使用 kubeadm

默认情况下,kubeadm 尝试检测你的容器运行环境。有关此检测的更多详细信息,请参见 kubeadm CRI 安装指南

设置节点的名称

默认情况下,kubeadm 基于机器的主机地址分配一个节点名称。你可以使用 --node-name 参数覆盖此设置。 此标识将合适的 --hostname-override 值传递给 kubelet。

要注意,重载主机名可能会与云驱动发生冲突

kubeadm 自动化

除了像文档 kubeadm 基础教程中所描述的那样, 将从 kubeadm init 取得的令牌复制到每个节点,你还可以并行地分发令牌以实现更简单的自动化。 要实现自动化,你必须知道控制平面节点启动后将拥有的 IP 地址,或使用 DNS 名称或负载均衡器的地址。

  1. 生成一个令牌。这个令牌必须采用的格式为:<6 个字符的字符串>.<16 个字符的字符串>。 更加正式的说法是,它必须符合正则表达式:[a-z0-9]{6}\.[a-z0-9]{16}

    kubeadm 可以为你生成一个令牌:

    kubeadm token generate
    
  1. 使用这个令牌同时启动控制平面节点和工作节点。这些节点一旦运行起来应该就会互相寻找对方并且形成集群。 同样的 --token 参数可以同时用于 kubeadm initkubeadm join 命令。
  1. 当接入其他控制平面节点时,可以对 --certificate-key 执行类似的操作。可以使用以下方式生成密钥:

    kubeadm certs certificate-key
    

一旦集群启动起来,你就可以从控制平面节点的 /etc/kubernetes/admin.conf 文件获取管理凭证, 并使用这个凭证同集群通信。

一旦集群启动起来,你就可以从控制平面节点中的 /etc/kubernetes/admin.conf 文件获取管理凭证或通过为其他用户生成的 kubeconfig 文件与集群通信。

注意这种搭建集群的方式在安全保证上会有一些宽松,因为这种方式不允许使用 --discovery-token-ca-cert-hash 来验证根 CA 的哈希值 (因为当配置节点的时候,它还没有被生成)。 更多信息请参阅 kubeadm join 文档。

接下来

6.10.1.3 - kubeadm join

此命令用来初始化 Kubernetes 工作节点并将其加入集群。

在你希望加入现有集群的任何机器上运行它。

摘要

当节点加入 kubeadm 初始化的集群时,我们需要建立双向信任。 这个过程可以分解为发现(让待加入节点信任 Kubernetes 控制平面节点)和 TLS 引导(让 Kubernetes 控制平面节点信任待加入节点)两个部分。

有两种主要的发现方案。 第一种方案是使用共享令牌和 API 服务器的 IP 地址。 第二种是以文件形式提供标准 kubeconfig 文件的一个子集。 发现/kubeconfig 文件支持令牌、client-go 鉴权插件(“exec”)、“tokenFile" 和 "authProvider"。该文件可以是本地文件,也可以通过 HTTPS URL 下载。 格式是 kubeadm join --discovery-token abcdef.1234567890abcdef 1.2.3.4:6443kubeadm join --discovery-file path/to/file.conf 或者 kubeadm join --discovery-file https://url/file.conf。 只能使用其中一种。 如果发现信息是从 URL 加载的,必须使用 HTTPS。 此外,在这种情况下,主机安装的 CA 包用于验证连接。

如果使用共享令牌进行发现,还应该传递 --discovery-token-ca-cert-hash 参数来验证 Kubernetes 控制平面节点提供的根证书颁发机构(CA)的公钥。 此参数的值指定为 "<hash-type>:<hex-encoded-value>", 其中支持的哈希类型为 "sha256"。哈希是通过 Subject Public Key Info(SPKI)对象的字节计算的(如 RFC7469)。 这个值可以从 "kubeadm init" 的输出中获得,或者可以使用标准工具进行计算。 可以多次重复 --discovery-token-ca-cert-hash 参数以允许多个公钥。

如果无法提前知道 CA 公钥哈希,则可以通过 --discovery-token-unsafe-skip-ca-verification 参数禁用此验证。 这削弱了 kubeadm 安全模型,因为其他节点可能会模仿 Kubernetes 控制平面节点。

TLS 引导机制也通过共享令牌驱动。 这用于向 Kubernetes 控制平面节点进行临时的身份验证,以提交本地创建的密钥对的证书签名请求(CSR)。 默认情况下,kubeadm 将设置 Kubernetes 控制平面节点自动批准这些签名请求。 这个令牌通过 --tls-bootstrap-token abcdef.1234567890abcdef 参数传入。

通常两个部分会使用相同的令牌。 在这种情况下可以使用 --token 参数,而不是单独指定每个令牌。

"join [api-server-endpoint]" 命令执行下列阶段:

preflight              Run join pre-flight checks
control-plane-prepare  Prepare the machine for serving a control plane
  /download-certs        [EXPERIMENTAL] Download certificates shared among control-plane nodes from the kubeadm-certs Secret
  /certs                 Generate the certificates for the new control plane components
  /kubeconfig            Generate the kubeconfig for the new control plane components
  /control-plane         Generate the manifests for the new control plane components
kubelet-start          Write kubelet settings, certificates and (re)start the kubelet
control-plane-join     Join a machine as a control plane instance
  /etcd                  Add a new local etcd member
  /update-status         Register the new control-plane node into the ClusterStatus maintained in the kubeadm-config ConfigMap (DEPRECATED)
  /mark-control-plane    Mark a node as a control-plane
kubeadm join [api-server-endpoint] [flags]

选项

--apiserver-advertise-address string

如果该节点托管一个新的控制平面实例,则 API 服务器将公布其正在侦听的 IP 地址。如果未设置,则使用默认网络接口。

--apiserver-bind-port int32     默认值: 6443

如果节点应该托管新的控制平面实例,则为 API 服务器要绑定的端口。

--certificate-key string

使用此密钥可以解密由 init 上传的证书 Secret。

--config string

kubeadm 配置文件的路径。

--control-plane

在此节点上创建一个新的控制平面实例。

--cri-socket string

要连接的 CRI 套接字的路径。如果为空,则 kubeadm 将尝试自动检测此值; 仅当安装了多个 CRI 或存在非标准的 CRI 套接字时,才使用此选项。

--discovery-file string

对于基于文件的发现,给出用于加载集群信息的文件或者 URL。

--discovery-token string

对于基于令牌的发现,该令牌用于验证从 API 服务器获取的集群信息。

--discovery-token-ca-cert-hash stringSlice

对基于令牌的发现,验证根 CA 公钥是否与此哈希匹配 (格式:"<type>:<value>")。

--discovery-token-unsafe-skip-ca-verification

对于基于令牌的发现,允许在未关联 --discovery-token-ca-cert-hash 参数的情况下添加节点。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

join 操作的帮助命令。

--ignore-preflight-errors strings

错误将显示为警告的检查列表;例如:'IsPrivilegedUser,Swap'。取值为 'all' 时将忽略检查中的所有错误。

--node-name string

指定节点的名称。

--patches string

包含名为 "target[suffix][+patchtype].extension" 的文件的目录的路径。 例如,"kube-apiserver0+merge.yaml" 或仅仅是 "etcd.json"。 "target" 可以是 “kube-apiserver”、“kube-controller-manager”、“kube-scheduler”、“etcd”、“kubeletconfiguration” 之一, "patchtype" 可以是 "strategic"、"merge" 或 "json" 之一,并且它们与 kubectl 支持的补丁格式匹配。 默认的 "patchtype" 为 "strategic"。 "extension" 必须为 "json" 或 "yaml"。 "suffix" 是一个可选字符串,可用于确定首先按字母顺序应用哪些补丁。

--skip-phases strings

要跳过的阶段列表。

--tls-bootstrap-token string

指定在加入节点时用于临时通过 Kubernetes 控制平面进行身份验证的令牌。

--token string

如果未提供这些值,则将它们用于 discovery-token 令牌和 tls-bootstrap 令牌。

从父命令继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

join 工作流

kubeadm join 初始化 Kubernetes 工作节点或控制平面节点并将其添加到集群中。 对于工作节点,该操作包括以下步骤:

  1. kubeadm 从 API 服务器下载必要的集群信息。 默认情况下,它使用引导令牌和 CA 密钥哈希来验证数据的真实性。 也可以通过文件或 URL 直接发现根 CA。
  1. 一旦知道集群信息,kubelet 就可以开始 TLS 引导过程。

    TLS 引导程序使用共享令牌与 Kubernetes API 服务器进行临时的身份验证,以提交证书签名请求 (CSR); 默认情况下,控制平面自动对该 CSR 请求进行签名。

  1. 最后,kubeadm 配置本地 kubelet 使用分配给节点的确定标识连接到 API 服务器。

对于控制平面节点,执行额外的步骤:

  1. 从集群下载控制平面节点之间共享的证书(如果用户明确要求)。

  2. 生成控制平面组件清单、证书和 kubeconfig。

  3. 添加新的本地 etcd 成员。

使用 kubeadm 的 join phase 命令

Kubeadm 允许你使用 kubeadm join phase 分阶段将节点加入集群。

要查看阶段和子阶段的有序列表,可以调用 kubeadm join --help。 该列表将位于帮助屏幕的顶部,每个阶段旁边都有一个描述。 注意,通过调用 kubeadm join,所有阶段和子阶段都将按照此确切顺序执行。

有些阶段具有唯一的标志,因此,如果要查看可用选项列表,请添加 --help,例如:

kubeadm join phase kubelet-start --help

类似于 kubeadm init phase 命令, kubeadm join phase 允许你使用 --skip-phases 标志跳过阶段列表。

例如:

sudo kubeadm join --skip-phases=preflight --config=config.yaml
特性状态: Kubernetes v1.22 [beta]

或者,你可以使用 JoinConfiguration 中的 skipPhases 字段。

发现要信任的集群 CA

Kubeadm 的发现有几个选项,每个选项都有安全性上的优缺点。 适合你的环境的正确方法取决于节点是如何准备的以及你对网络的安全性期望 和节点的生命周期特点。

带 CA 锁定模式的基于令牌的发现

这是 kubeadm 的默认模式。 在这种模式下,kubeadm 下载集群配置(包括根 CA)并使用令牌验证它, 并且会验证根 CA 的公钥与所提供的哈希是否匹配, 以及 API 服务器证书在根 CA 下是否有效。

CA 键哈希格式为 sha256:<hex_encoded_hash>。 默认情况下,哈希值会打印在 kubeadm init 命令输出的末尾 或者从 kubeadm token create --print-join-command 命令的输出信息中返回。 它使用标准格式(请参考 RFC7469) 并且也能通过第三方工具或者制备系统进行计算。 例如,使用 OpenSSL CLI:

openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'

kubeadm join 命令示例

对于工作节点:

kubeadm join --discovery-token abcdef.1234567890abcdef --discovery-token-ca-cert-hash sha256:1234..cdef 1.2.3.4:6443

对于控制面节点:

kubeadm join --discovery-token abcdef.1234567890abcdef --discovery-token-ca-cert-hash sha256:1234..cdef --control-plane 1.2.3.4:6443

如果使用 --upload-certs 调用 kubeadm init 命令, 你也可以对控制平面节点调用带 --certificate-key 参数的 join 命令, 将证书复制到该节点。

优势:

  • 允许引导节点安全地发现控制平面节点的信任根,即使其他工作节点或网络受到损害。

  • 方便手动执行,因为所需的所有信息都可放到一个 kubeadm join 命令中。

劣势:

  • CA 哈希通常在控制平面节点被提供之前是不知道的,这使得构建使用 kubeadm 的自动化配置工具更加困难。 通过预先生成 CA,你可以解除这个限制。

无 CA 锁定模式的基于令牌的发现

此模式仅依靠对称令牌来签署 (HMAC-SHA256) 为控制平面建立信任根的发现信息。 要使用该模式,加入节点必须使用 --discovery-token-unsafe-skip-ca-verification 跳过 CA 公钥的哈希验证。 如果可以,你应该考虑使用其他模式。

kubeadm join 命令示例

kubeadm join --token abcdef.1234567890abcdef --discovery-token-unsafe-skip-ca-verification 1.2.3.4:6443

优势

  • 仍然可以防止许多网络级攻击。

  • 可以提前生成令牌并与控制平面节点和工作节点共享,这样控制平面节点和工作节点就可以并行引导而无需协调。 这允许它在许多配置场景中使用。

劣势

  • 如果攻击者能够通过某些漏洞窃取引导令牌,那么他们可以使用该令牌(连同网络级访问) 为其它处于引导过程中的节点提供假冒的控制平面节点。 在你的环境中,这可能是一个适当的折衷方法,也可能不是。

基于 HTTPS 或文件发现

这种方案提供了一种带外方式在控制平面节点和引导节点之间建立信任根。 如果使用 kubeadm 构建自动配置,请考虑使用此模式。 发现文件的格式为常规的 Kubernetes kubeconfig 文件。

如果发现文件不包含凭据,则将使用 TLS 发现令牌。

kubeadm join 命令示例:

  • kubeadm join --discovery-file path/to/file.conf(本地文件)

  • kubeadm join --discovery-file https://url/file.conf(远程 HTTPS URL)

优势:

  • 允许引导节点安全地发现控制平面节点的信任根,即使网络或其他工作节点受到损害。

劣势:

  • 要求你有某种方法将发现信息从控制平面节点传送到引导节点。 如果发现文件包含凭据,你必须对其保密并通过安全通道进行传输。 这可能通过你的云提供商或供应工具来实现。

将自定义 kubelet 凭据与 kubeadm join 结合使用

要允许 kubeadm join 使用预定义的 kubelet 凭据并跳过客户端 TLS 引导程序和新节点的 CSR 批准:

  1. 从集群中带有 /etc/kubernetes/pki/ca.key 的工作控制平面节点执行 kubeadm kubeconfig user --org system:nodes --client-name system:node:$NODE > kubelet.conf$NODE 必须设置为新节点的名称。
  2. 手动修改生成的 kubelet.conf 以调整集群名称和服务器端点, 或运行 kubeadm kubeconfig user --config(它接受 InitConfiguration)。

如果集群没有 ca.key 文件,你必须在外部对 kubelet.conf 中嵌入的证书进行签名。

  1. 将生成的 kubelet.conf 复制为新节点上的 /etc/kubernetes/kubelet.conf
  2. 在新节点上带着标志 --ignore-preflight-errors=FileAvailable--etc-kubernetes-kubelet.conf 执行 kubeadm join

确保你的安装更加安全

Kubeadm 的默认值可能不适用于所有人。 本节说明如何以牺牲可用性为代价来加强 kubeadm 安装。

关闭节点客户端证书的自动批准

默认情况下,Kubernetes 启用了 CSR 自动批准器,如果在身份验证时使用启动引导令牌, 它会批准对 kubelet 的任何客户端证书的请求。 如果不希望集群自动批准 kubelet 客户端证书,可以通过执行以下命令关闭它:

kubectl delete clusterrolebinding kubeadm:node-autoapprove-bootstrap

关闭后,kubeadm join 操作将会被阻塞,直到管理员已经手动批准了在途中的 CSR 才会继续:

  1. 使用 kubectl get csr,你可以看到原来的 CSR 处于 Pending 状态。

    kubectl get csr
    

    输出类似于:

    NAME                                                   AGE       REQUESTOR                 CONDITION
    node-csr-c69HXe7aYcqkS1bKmH4faEnHAWxn6i2bHZ2mD04jZyQ   18s       system:bootstrap:878f07   Pending
    
  1. kubectl certificate approve 允许管理员批准 CSR。 此操作告知证书签名控制器向请求者颁发一个证书,该证书具有 CSR 中所请求的那些属性。

    kubectl certificate approve node-csr-c69HXe7aYcqkS1bKmH4faEnHAWxn6i2bHZ2mD04jZyQ
    

    输出类似于:

    certificatesigningrequest "node-csr-c69HXe7aYcqkS1bKmH4faEnHAWxn6i2bHZ2mD04jZyQ" approved
    
  1. 这会将 CRS 资源更改为 Active 状态。

    kubectl get csr
    

    输出类似于:

    NAME                                                   AGE       REQUESTOR                 CONDITION
    node-csr-c69HXe7aYcqkS1bKmH4faEnHAWxn6i2bHZ2mD04jZyQ   1m        system:bootstrap:878f07   Approved,Issued
    

这迫使工作流只有在运行了 kubectl certificate approve 后,kubeadm join 才能成功。

关闭对 cluster-info ConfigMap 的公开访问

为了实现使用令牌作为唯一验证信息的加入工作流,默认情况下会公开带有验证控制平面节点标识所需数据的 ConfigMap。 虽然此 ConfigMap 中没有私有数据,但一些用户可能希望无论如何都关闭它。 这样做需要禁用 kubeadm join 工作流的 --discovery-token 参数。 以下是实现步骤:

  • 从 API 服务器获取 cluster-info 文件:
kubectl -n kube-public get cm cluster-info -o jsonpath='{.data.kubeconfig}' | tee cluster-info.yaml

输出类似于:

apiVersion: v1
kind: Config
clusters:
- cluster:
    certificate-authority-data: <ca-cert>
    server: https://<ip>:<port>
  name: ""
contexts: []
current-context: ""
preferences: {}
users: []
  • 使用 cluster-info.yaml 文件作为 kubeadm join --discovery-file 参数。

  • 关闭 cluster-info ConfigMap 的公开访问:

    kubectl -n kube-public delete rolebinding kubeadm:bootstrap-signer-clusterinfo
    

这些命令应该在执行 kubeadm init 之后、在 kubeadm join 之前执行。

使用带有配置文件的 kubeadm join

可以用配置文件替代命令行参数的方法配置 kubeadm join,一些进阶功能也只有在使用配置文件时才可选用。 该文件通过 --config 参数来传递,并且文件中必须包含 JoinConfiguration 结构。 在某些情况下,不允许将 --config 与其他标志混合使用。

使用 kubeadm config print 命令可以打印默认配置。

如果你的配置没有使用最新版本, 推荐使用 kubeadm config migrate 命令转换。

有关配置的字段和用法的更多信息,你可以导航到我们的 API 参考页

接下来

  • kubeadm init 初始化 Kubernetes 控制平面节点。
  • kubeadm token 管理 kubeadm join 的令牌。
  • kubeadm resetkubeadm initkubeadm join 对主机的更改恢复到之前状态。

6.10.1.4 - kubeadm upgrade

kubeadm upgrade 是一个对用户友好的命令,它将复杂的升级逻辑包装在一个命令后面,支持升级的规划和实际执行。

kubeadm upgrade 指南

本文档概述 使用 kubeadm 执行升级的步骤。 与 kubeadm 旧版本相关的文档,请参阅 Kubernetes 网站的旧版文档。

你可以使用 kubeadm upgrade diff 来查看将应用于静态 Pod 清单的更改。

在 Kubernetes v1.15.0 和更高版本中,kubeadm upgrade applykubeadm upgrade node 也将自动续订该节点上的 kubeadm 托管证书,包括存储在 kubeconfig 文件中的证书。 要选择退出,可以传递参数 --certificate-renewal=false。 有关证书续订的更多详细信息请参见证书管理文档

kubeadm upgrade plan

概述

检查可升级到哪些版本,并验证你当前的集群是否可升级。 要跳过互联网检查,请传递可选的 [version] 参数

kubeadm upgrade plan [version] [flags]

选项

--allow-experimental-upgrades

显示不稳定版本的 Kubernetes 作为升级替代方案,并允许升级到 Kubernetes 的 Alpha、Beta 或 RC 版本。

--allow-release-candidate-upgrades

显示 Kubernetes 的发行候选版本作为升级选择,并允许升级到 Kubernetes 的 RC 版本。

--config string

配置文件的路径。

--feature-gates string

一组描述各种特征特性门控的键值对。选项有:
EtcdLearnerMode=true|false (ALPHA - 默认值=false)
PublicKeysECDSA=true|false (ALPHA - 默认值=false)
RootlessControlPlane=true|false (ALPHA - 默认值=false)
UpgradeAddonsBeforeControlPlane=true|false (DEPRECATED - 默认值=false)

-h, --help

plan 操作的帮助命令。

--ignore-preflight-errors strings

其错误将显示为警告的检查列表。例如:'IsPrivilegedUser,Swap'。值 'all' 忽略所有检查错误。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。如果标志为未设置,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

-o, --output string      默认值:"text"

实验性功能;输出格式为 text|json|yaml 其中的一种。

--print-config

指定是否打印将在升级中使用的配置文件。

--show-managed-fields

如果开启,以 JSON 或 YAML 格式打印对象时会保留 managedField。

从父命令继承的选项

--rootfs string

[实验] 指向 “真实” 宿主机根文件系统的路径。

kubeadm upgrade apply

将 Kubernetes 集群升级到指定版本。

概要

将 Kubernetes 集群升级到指定版本。

kubeadm upgrade apply [version]

选项

--allow-experimental-upgrades

显示 Kubernetes 的不稳定版本作为升级替代方案,并允许升级到 Kubernetes 的 Alpha、Beta 或 RC 版本。

--allow-release-candidate-upgrades

显示 Kubernetes 的候选版本作为升级替代方案,并允许升级到 Kubernetes 的 RC 版本。

--certificate-renewal     Default: true

执行升级期间更改的组件所使用的证书的更新。

--config string

kubeadm 配置文件的路径。

--dry-run

不要更改任何状态,只输出要执行的操作。

--etcd-upgrade     默认值: true

执行 etcd 的升级。

--feature-gates string

一组键值对,用于描述各种功能。选项包括:
EtcdLearnerMode=true|false (ALPHA - 默认值=false)
PublicKeysECDSA=true|false (ALPHA - 默认值=false)
RootlessControlPlane=true|false (ALPHA - 默认值=false)
UpgradeAddonsBeforeControlPlane=true|false (DEPRECATED - 默认值=false)

-f, --force

强制升级,但可能无法满足某些要求。这也意味着非交互模式。

-h, --help

apply 操作的帮助命令。

-ignore-preflight-errors strings

错误将显示为警告的检查列表;例如:'IsPrivilegedUser,Swap'。取值为 'all' 时将忽略检查中的所有错误。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。如果未设置标志,则在相关目录下搜索以查找现有 kubeconfig 文件。

--patches string

包含名为 "target[suffix][+patchtype].extension" 的文件的目录的路径。 例如,"kube-apiserver0+merge.yaml"或是简单的 "etcd.json"。 "target" 可以是 "kube-apiserver"、"kube-controller-manager"、"kube-scheduler"、"etcd"、"kubeletconfiguration" 之一。 "patchtype" 可以是 "strategic"、"merge" 或者 "json" 之一, 并且它们与 kubectl 支持的补丁格式相同。 默认的 "patchtype" 是 "strategic"。 "extension" 必须是 "json" 或 "yaml"。 "suffix" 是一个可选字符串,可用于确定首先按字母顺序应用哪些补丁。

--print-config

指定是否应打印将在升级中使用的配置文件。

-y, --yes

执行升级,不提示确认(非交互模式)。

从父命令继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

kubeadm upgrade diff

显示哪些差异将被应用于现有的静态 Pod 资源清单。参考:kubeadm upgrade apply --dry-run

概述

显示哪些差异将被应用于现有的静态 Pod 资源清单。参考:kubeadm upgrade apply --dry-run

kubeadm upgrade diff [version] [flags]

选项

--api-server-manifest string     默认值:"/etc/kubernetes/manifests/kube-apiserver.yaml"

API 服务器清单的路径。

--config string

kubeadm 配置文件的路径。

-c, --context-lines int     默认值:3
diff 涉及了多少行上下文。
--controller-manager-manifest string     默认值:"/etc/kubernetes/manifests/kube-controller-manager.yaml"

控制器清单的路径。

-h, --help

diff 操作的帮助命令

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件,如果标志是未设置,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

--scheduler-manifest string     默认值:"/etc/kubernetes/manifests/kube-scheduler.yaml"

调度程序清单的路径。

从父命令继承的选项

--rootfs string

[实验] 指向“真实”主机根文件系统的路径。

kubeadm upgrade node

升级集群中某个节点的命令。

概要

升级集群中某个节点的命令。

"node" 命令执行以下阶段:

preflight       执行节点升级前检查
control-plane   如果存在的话,升级部署在该节点上的管理面实例
kubelet-config  更新该节点上的 kubelet 配置
kubeadm upgrade node [flags]

选项

--certificate-renewal     默认值: true

对升级期间变化的组件所使用的证书执行续订。

--dry-run

不更改任何状态,只输出将要执行的操作。

--etcd-upgrade     默认值: true

执行 etcd 的升级。

-h, --help

node 操作的帮助命令。

--ignore-preflight-errors strings

其错误将显示为警告的检查列表。示例:'IsPrivilegedUser,Swap'。值 'all' 忽略所有检查中的错误。

--kubeconfig string     默认值: "/etc/kubernetes/admin.conf"

用于与集群交互的 kubeconfig 文件。如果参数未指定,将从一系列标准位置检索存在的 kubeconfig 文件。

--patches string

包含名为 "target[suffix][+patchtype].extension" 的文件的目录的路径。 例如,"kube-apiserver0+merge.yaml"或仅仅是 "etcd.json"。 "target" 可以是 "kube-apiserver"、"kube-controller-manager"、"kube-scheduler"、"etcd"、"kubeletconfiguration" 之一。 "patchtype" 可以是 "strategic"、"merge" 或者 "json" 之一, 并且它们与 kubectl 支持的补丁格式相同。 默认的 "patchtype" 是 "strategic"。 "extension" 必须是"json" 或"yaml"。 "suffix" 是一个可选字符串,可用于确定首先按字母顺序应用哪些补丁。

--skip-phases strings

要跳过的阶段的列表。

从父命令继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

接下来

  • 如果你使用 kubeadm v1.7.x 或更低版本初始化集群,则可以参考 kubeadm 配置 配置集群用于 kubeadm upgrade

6.10.1.5 - kubeadm config

kubeadm init 执行期间,kubeadm 将 ClusterConfiguration 对象上传 到你的集群的 kube-system 名字空间下名为 kubeadm-config 的 ConfigMap 对象中。 然后在 kubeadm joinkubeadm resetkubeadm upgrade 执行期间读取此配置。

你可以使用 kubeadm config print 命令打印默认静态配置, kubeadm 运行 kubeadm init and kubeadm join 时将使用此配置。

更多有关 initjoin 的信息请浏览使用带配置文件的 kubeadm init使用带配置文件的 kubeadm join

有关使用 kubeadm 的配置 API 的更多信息, 请浏览使用 kubeadm API 来自定义组件

你可以使用 kubeadm config migrate 来转换旧配置文件, 把其中已弃用的 API 版本更新为受支持的 API 版本。

kubeadm config validate 可用于验证配置文件。

kubeadm config images listkubeadm config images pull 可以用来列出和拉取 kubeadm 所需的镜像。

kubeadm config print

打印配置

概要

此命令打印子命令所提供的配置信息。相关细节可参阅: https://pkg.go.dev/k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm#section-directories

kubeadm config print [flags]

选项

-h, --help

print 命令的帮助信息。

从父命令继承而来的选项

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。如此标志未设置,将在一组标准位置中搜索现有的 kubeconfig 文件。

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

kubeadm config print init-defaults

打印用于 'kubeadm init' 的默认 init 配置。

概要

此命令打印对象,例如用于 'kubeadm init' 的默认 init 配置对象。

请注意,Bootstrap Token 字段之类的敏感值已替换为 "abcdef.0123456789abcdef" 之类的占位符值以通过验证,但不执行创建令牌的实际计算。

kubeadm config print init-defaults [flags]

选项

--component-configs strings

以逗号分隔的组件配置 API 对象的列表,打印其默认值。可用值:[KubeProxyConfiguration KubeletConfiguration]。 如果未设置此参数,则不会打印任何组件配置。

-h, --help

init-defaults 操作的帮助命令。

从父命令继承的选项

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

kubeadm config print join-defaults

打印默认的节点添加配置,该配置可用于 'kubeadm join' 命令。

概要

此命令打印对象,例如用于 'kubeadm join' 的默认 join 配置对象。

请注意,诸如启动引导令牌字段之类的敏感值已替换为 "abcdef.0123456789abcdef" 之类的占位符值以通过验证, 但不执行创建令牌的实际计算。

kubeadm config print join-defaults [flags]

选项

-h, --help

join-defaults 操作的帮助命令。

从父命令继承的选项

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

kubeadm config migrate

从文件中读取旧版本的 kubeadm 配置的 API 类型,并为新版本输出类似的配置对象

概要

此命令允许你在 CLI 工具中将本地旧版本的配置对象转换为最新支持的版本,而无需变更集群中的任何内容。 在此版本的 kubeadm 中,支持以下 API 版本:

  • kubeadm.k8s.io/v1beta3

因此,无论你在此处传递 --old-config 参数的版本是什么,当写入到 stdout 或 --new-config (如果已指定)时, 都会读取、反序列化、默认、转换、验证和重新序列化 API 对象。

换句话说,如果你将此文件传递给 "kubeadm init",则该命令的输出就是 kubeadm 实际上在内部读取的内容。

kubeadm config migrate [flags]

选项

-h, --help

migrate 操作的帮助信息。

--new-config string

使用新的 API 版本生成的 kubeadm 配置文件的路径。这个路径是可选的。如果没有指定,输出将被写到 stdout。

--old-config string

使用旧 API 版本且应转换的 kubeadm 配置文件的路径。此参数是必需的。

从父命令继承的选项

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

用于和集群通信的 kubeconfig 文件。如果未设置,那么 kubeadm 将会搜索一个已经存在于标准路径的 kubeconfig 文件。

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

kubeadm config validate

读取包含 kubeadm 配置 API 的文件,并报告所有验证问题。

概要

这个命令允许你验证 kubeadm 配置 API 文件并报告所有警告和错误。 如果没有错误,退出状态将为零;否则,将为非零。 诸如未知 API 字段等任何解包问题都会触发错误。 未知的 API 版本和具有无效值的字段也会触发错误。 根据输入文件的内容,可能会报告任何其他错误或警告。

在这个版本的 kubeadm 中,支持以下 API 版本:

  • kubeadm.k8s.io/v1beta3
kubeadm config validate [flags]

选项

--allow-experimental-api

允许验证试验性的、未发布的 API。

--config string

指向 kubeadm 配置文件的路径。

-h, --help

validate 的帮助命令。

从父命令继承而来的选项

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

在与集群通信时要使用的 kubeconfig 文件。 如果此标志未被设置,则会在一组标准位置中搜索现有的 kubeconfig 文件。

--rootfs string

[试验性] 指向“真实”主机根文件系统的路径。

kubeadm config images list

打印 kubeadm 要使用的镜像列表。配置文件用于自定义镜像或镜像存储库。

概要

打印 kubeadm 要使用的镜像列表。配置文件用于自定义镜像或镜像存储库。

kubeadm config images list [flags]

选项

--allow-missing-template-keys     默认值:true

如果设置为 true,则在模板中缺少字段或哈希表的键时忽略模板中的任何错误。 仅适用于 golang 和 jsonpath 输出格式。

--config string

kubeadm 配置文件的路径。

-o, --experimental-output string     默认值:"text"

输出格式:text|json|yaml|go-template|go-template-file|template|templatefile|jsonpath|jsonpath-as-json|jsonpath-file 其中之一。

--feature-gates string

一组键值对(key=value),用于描述各种特性。这些选项是:
EtcdLearnerMode=true|false (ALPHA - 默认值=false)
PublicKeysECDSA=true|false (ALPHA - 默认值=false)
RootlessControlPlane=true|false (ALPHA - 默认值=false)
UpgradeAddonsBeforeControlPlane=true|false (DEPRECATED - 默认值=false)

-h, --help

list 操作的帮助命令。

--image-repository string     默认值:"registry.k8s.io"

选择要从中拉取控制平面镜像的容器仓库。

--kubernetes-version string     默认值:"stable-1"

为控制平面选择一个特定的 Kubernetes 版本。

--show-managed-fields

如果为 true,则在以 JSON 或 YAML 格式打印对象时保留 managedFields。

从父命令继承的选项

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

用于和集群通信的 kubeconfig 文件。如果它没有被设置,那么 kubeadm 将会搜索一个已经存在于标准路径的 kubeconfig 文件。

--rootfs string

[实验] 到“真实”主机根文件系统的路径。

kubeadm config images pull

拉取 kubeadm 使用的镜像。

概要

拉取 kubeadm 使用的镜像。

kubeadm config images pull [flags]

选项

--config string

kubeadm 配置文件的路径。

--cri-socket string

要连接的 CRI 套接字的路径。如果为空,则 kubeadm 将尝试自动检测此值;仅当安装了多个 CRI 或具有非标准 CRI 插槽时,才使用此选项。

--feature-gates string
一系列键值对(key=value),用于描述各种特性。可选项是:
EtcdLearnerMode=true|false (ALPHA - 默认值=false)
PublicKeysECDSA=true|false (ALPHA - 默认值=false)
RootlessControlPlane=true|false (ALPHA - 默认值=false)
UpgradeAddonsBeforeControlPlane=true|false (DEPRECATED - 默认值=false)
-h, --help

pull 操作的帮助命令。

--image-repository string     默认值:"registry.k8s.io"

选择用于拉取控制平面镜像的容器仓库。

--kubernetes-version string     默认值:"stable-1"

为控制平面选择一个特定的 Kubernetes 版本。

从父命令继承的选项

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

用于和集群通信的 kubeconfig 文件。如果它没有被设置,那么 kubeadm 将会搜索一个已经存在于标准路径的 kubeconfig 文件。

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

接下来

  • kubeadm upgrade 将 Kubernetes 集群升级到更新版本 [kubeadm upgrade]

6.10.1.6 - kubeadm reset

该命令尽力还原由 kubeadm initkubeadm join 所做的更改。

尽最大努力还原通过 'kubeadm init' 或者 'kubeadm join' 操作对主机所作的更改。

概要

尽最大努力还原通过 'kubeadm init' 或者 'kubeadm join' 操作对主机所作的更改。

"reset" 命令执行以下阶段:

preflight              Run reset pre-flight checks
remove-etcd-member     Remove a local etcd member.
cleanup-node           Run cleanup node.
kubeadm reset [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

存储证书的目录路径。如果已指定,则需要清空此目录。

--cleanup-tmp-dir

清理 "/etc/kubernetes/tmp" 目录。

--config string

kubeadm 配置文件的路径。

--cri-socket string

要连接的 CRI 套接字的路径。如果为空,则 kubeadm 将尝试自动检测此值; 仅当安装了多个 CRI 或存在非标准的 CRI 套接字时,才使用此选项。

--dry-run

不做任何更改;只输出将要执行的操作。

-f, --force

在不提示确认的情况下重置节点。

-h, --help

reset 操作的帮助命令。

--ignore-preflight-errors strings

错误将显示为警告的检查列表;例如:'IsPrivilegedUser,Swap'。取值为 'all' 时将忽略检查中的所有错误。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。如果未设置该标志,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

--skip-phases strings

要跳过的阶段列表。

从父命令继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

Reset 工作流程

kubeadm reset 负责从使用 kubeadm initkubeadm join 命令创建的文件中清除节点本地文件系统。 对于控制平面节点,reset 还从 etcd 集群中删除该节点的本地 etcd Stacked 部署的成员。

kubeadm reset phase 可用于执行上述工作流程的各个阶段。 要跳过阶段列表,你可以使用 --skip-phases 参数,该参数的工作方式类似于 kubeadm joinkubeadm init 阶段运行器。

外部 etcd 清理

如果使用了外部 etcd,kubeadm reset 将不会删除任何 etcd 中的数据。这意味着,如果再次使用相同的 etcd 端点运行 kubeadm init,你将看到先前集群的状态。

要清理 etcd 中的数据,建议你使用 etcdctl 这样的客户端,例如:

etcdctl del "" --prefix

更多详情请参考 etcd 文档

接下来

  • 参考 kubeadm init 来初始化 Kubernetes 主节点。
  • 参考 kubeadm join 来初始化 Kubernetes 工作节点并加入集群。

6.10.1.7 - kubeadm token

使用引导令牌进行身份验证所描述的,引导令牌用于在即将加入集群的节点和主节点间建立双向认证。

kubeadm init 创建了一个有效期为 24 小时的令牌,下面的命令允许你管理令牌,也可以创建和管理新的令牌。

kubeadm token create

在服务器上创建引导令牌。

概要

这个命令将为你创建一个引导令牌。 你可以设置此令牌的用途,"有效时间" 和可选的人性化的描述。

这里的 [token] 是指将要生成的实际令牌。 该令牌应该是一个通过安全机制生成的随机令牌,形式为 "[a-z0-9]{6}.[a-z0-9]{16}"。 如果没有给出 [token],kubeadm 将生成一个随机令牌。

kubeadm token create [token]

选项

--certificate-key string

当与 “--print-join-command” 一起使用时,打印作为控制平面加入集群时所需的所有 “kubeadm join” 标志。 要创建新的证书密钥,你必须使用 “kubeadm init phase upload-certs --upload-certs”。

--config string

kubeadm 配置文件的路径。

--description string

针对令牌用途的人性化的描述。

--groups stringSlice     默认值:[system:bootstrappers:kubeadm:default-node-token]

此令牌用于身份验证时将对其他组进行身份验证。必须匹配 "\\Asystem:bootstrappers:[a-z0-9:-]{0,255}[a-z0-9]\\z"

-h, --help

create 操作的帮助命令。

--print-join-command

不仅仅打印令牌,而是打印使用令牌加入集群所需的完整 'kubeadm join' 参数。

--ttl duration     默认值:24h0m0s

令牌有效时间,超过该时间令牌被自动删除。(例如:1s, 2m, 3h)。如果设置为 '0',令牌将永远不过期。

--usages stringSlice     默认值:[signing,authentication]

描述可以使用此令牌的方式。你可以多次使用 `--usages` 或者提供一个以逗号分隔的选项列表。合法选项有: [signing,authentication]

从父命令继承的选项

--dry-run

是否启用 `dry-run` 运行模式。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

用于和集群通信的 kubeconfig 文件。如果它没有被设置,那么 kubeadm 将会搜索一个已经存在于标准路径的 kubeconfig 文件。

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

kubeadm token delete

删除服务器上的引导令牌。

概要

这个命令将为你删除指定的引导令牌列表。

[token-value] 是要删除的 "[a-z0-9]{6}.[a-z0-9]{16}" 形式的完整令牌或者是 "[a-z0-9]{6}" 形式的令牌 ID。

kubeadm token delete [token-value] ...

选项

-h, --help

delete 操作的帮助命令。

从父命令继承的选项

--dry-run

是否启用 `dry-run` 运行模式。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

用于和集群通信的 kubeconfig 文件。如果它没有被设置,那么 kubeadm 将会搜索一个已经存在于标准路径的 kubeconfig 文件。

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

kubeadm token generate

生成并打印一个引导令牌,但不要在服务器上创建它。

概要

此命令将打印一个随机生成的可以被 "init" 和 "join" 命令使用的引导令牌。

你不必使用此命令来生成令牌。你可以自己设定,只要格式符合 "[a-z0-9]{6}.[a-z0-9]{16}"。 之所以提供这个命令是为了方便生成规定格式的令牌。

你也可以使用 "kubeadm init" 并且不指定令牌,该命令会生成一个令牌并打印出来。

kubeadm token generate [flags]

选项

-h, --help

generate 操作的帮助命令。

从父命令继承的选项

--dry-run

是否启用 `dry-run` 模式。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

用于和集群通信的 kubeconfig 文件。如果它没有被设置,那么 kubeadm 将会搜索一个已经存在于标准路径的 kubeconfig 文件。

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

kubeadm token list

列出服务器上的引导令牌。

概要

此命令将为你列出所有的引导令牌。

kubeadm token list [flags]

选项

--allow-missing-template-keys     默认值:true

如果设置为 true,则在模板中缺少字段或哈希表的键时忽略模板中的任何错误。 仅适用于 golang 和 jsonpath 输出格式。

-o, --experimental-output string     默认值:"text"

输出格式:text|json|yaml|go-template|go-template-file|template|templatefile|jsonpath|jsonpath-as-json|jsonpath-file 其中之一

-h, --help

list 操作的帮助命令。

--show-managed-fields

如果为 true,则在以 JSON 或 YAML 格式打印对象时保留 managedFields。

从父命令继承的选项

--dry-run

是否启用 `dry-run` 模式。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

用于和集群通信的 kubeconfig 文件。如果它没有被设置,那么 kubeadm 将会搜索一个已经存在于标准路径的 kubeconfig 文件。

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

接下来

  • kubeadm join 引导 Kubernetes 工作节点并将其加入集群

6.10.1.8 - kubeadm version

此命令用来输出 kubeadm 的版本。

打印 kubeadm 的版本。

概要

打印 kubeadm 的版本。

kubeadm version [flags]

选项

-h, --help

version 操作的帮助命令。

-o, --output string

输出格式;可用的选项有 'yaml'、'json' 和 'short'。

从父命令继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

6.10.1.9 - kubeadm alpha

目前在 kubeadm alpha 之下没有试验性质的命令。

接下来

  • 用来启动引导 Kubernetes 控制平面节点的 kubeadm init 命令
  • 用来将节点连接到集群的 kubeadm join 命令
  • 用来还原 kubeadm initkubeadm join 操作对主机所做的任何更改的 kubeadm reset 命令

6.10.1.10 - kubeadm certs

kubeadm certs 提供管理证书的工具。关于如何使用这些命令的细节,可参见 使用 kubeadm 管理证书

kubeadm certs

用来操作 Kubernetes 证书的一组命令。

处理 Kubernetes 证书的相关命令。

概要

处理 Kubernetes 证书相关的命令。

kubeadm certs [flags]

选项

-h, --help

certs 操作的帮助命令。

继承于父命令的选项

--rootfs string

[实验] 到'真实'主机根文件系统的路径。

kubeadm certs renew

你可以使用 all 子命令来续订所有 Kubernetes 证书,也可以选择性地续订部分证书。 更多的相关细节,可参见 手动续订证书

为 Kubernetes 集群更新证书

概要

此命令并非设计用来单独运行。请参阅可用子命令列表。

kubeadm certs renew [flags]

选项

-h, --help

renew 操作的帮助命令

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

续订所有可用证书。

概要

续订运行控制平面所需的所有已知证书。续订是无条件进行的,与到期日期无关。续订也可以单独运行以进行更多控制。

kubeadm certs renew all [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

存储证书的路径。

--config string

kubeadm 配置文件的路径。

-h, --help

all 操作的帮助命令。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。 如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

--use-api
使用 Kubernetes 证书 API 续订证书。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

续订 kubeconfig 文件中嵌入的证书,供管理员和 kubeadm 自身使用。

概要

续订 kubeconfig 文件中嵌入的证书,供管理员和 kubeadm 自身使用。

无论证书的到期日期如何,续订都是无条件进行的;SAN 等额外属性将基于现有文件/证书,因此无需重新提供它们。

默认情况下,续订会尝试使用由 kubeadm 管理的本地 PKI 中的证书机构;作为替代方案, 也可以使用 K8s 证书 API 进行证书续订,或者(作为最后一种选择)生成 CSR 请求。

续订后,为了使更改生效,需要重新启动控制平面组件,并最终重新分发更新的证书,以防证书文件在其他地方使用。

kubeadm certs renew admin.conf [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存证书的路径。

--config string

到 kubeadm 配置文件的路径。

-h, --help

admin.conf 操作的帮助命令。

--kubeconfig string     Default: "/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。 如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

--use-api
使用 Kubernetes 证书 API 续订证书。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

续订 apiserver 用于访问 etcd 的证书。

概要

续订 apiserver 用于访问 etcd 的证书。

无论证书的到期日期如何,续订都会无条件地进行;SAN 等额外属性将基于现有文件/证书,因此无需重新提供它们。

默认情况下,续订尝试使用在 kubeadm 所管理的本地 PKI 中的证书颁发机构;作为替代方案, 可以使用 K8s 证书 API 进行证书更新,或者作为最后一个选择来生成 CSR 请求。

续订后,为了使更改生效,需要重新启动控制平面组件,并最终重新分发更新的证书,以防文件在其他地方使用。

kubeadm certs renew apiserver-etcd-client [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

存储证书的路径。

--config string

kubeadm 配置文件的路径。

-h, --help

apiserver-etcd-client 操作的帮助命令。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。 如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

续订 apiserver 用于连接 kubelet 的证书。

概要

续订 apiserver 用于连接 kubelet 的证书。

无论证书的到期日期如何,续订都会无条件地进行;SAN 等额外属性将基于现有文件/证书,因此无需重新提供它们。

默认情况下,续订尝试使用位于 kubeadm 所管理的本地 PKI 中的证书颁发机构;作为替代方案, 也可能调用 K8s 证书 API 进行证书更新;亦或者,作为最后一个选择,生成 CSR 请求。

续订后,为了使更改生效,需要重新启动控制平面组件,并最终重新分发更新的证书,以防文件在其他地方使用。

kubeadm certs renew apiserver-kubelet-client [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

存储证书的路径。

--config string

kubeadm 配置文件的路径。

-h, --help

apiserver-kubelet-client 操作的帮助命令。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。 如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

--use-api
使用 Kubernetes 证书 API 续订证书。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

续订用于提供 Kubernetes API 的证书。

概要

续订用于提供 Kubernetes API 的证书。

无论证书的到期日期如何,续订都会无条件地进行;SAN 等额外属性将基于现有文件/证书,因此无需重新提供它们。

默认情况下,续订尝试在 kubeadm 管理的本地 PKI 中使用证书颁发机构;作为替代方案, 可以使用 K8s 证书 API 进行证书更新,或者作为最后一个选择来生成 CSR 请求。

续订后,为了使更改生效,需要重新启动控制平面组件,并最终重新分发更新的证书,以防文件在其他地方使用。

kubeadm certs renew apiserver [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存证书的路径。

--config string

kubeadm 配置文件的路径。

-h, --help

apiserver 子操作的帮助命令。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。 如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

续订 kubeconfig 文件中嵌入的证书,以供控制器管理器(Controller Manager)使用。

概要

续订 kubeconfig 文件中嵌入的证书,以供控制器管理器(Controller Manager)使用。

续订无条件地进行,与证书的到期日期无关;SAN 等额外属性将基于现有的文件/证书,因此无需重新提供它们。

默认情况下,续订会尝试使用 kubeadm 管理的本地 PKI 中的证书颁发机构;作为替代方案, 可以使用 K8s 证书 API 进行证书续订;亦或者,作为最后一种选择,生成 CSR 请求。

续订后,为了使更改生效,需要重新启动控制平面组件,并最终重新分发更新的证书,以防文件在其他地方使用。

kubeadm certs renew controller-manager.conf [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存证书的路径。

--config string

kubeadm 配置文件的路径。

-h, --help

controller-manager.conf 操作的帮助命令。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。 如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

--use-api
使用 Kubernetes 证书 API 续订证书。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

续订存活态探针的证书,用于对 etcd 执行健康检查。

概要

续订存活态探针的证书,用于对 etcd 执行健康检查。

无论证书的到期日期如何,续订都是无条件进行的;SAN 等额外属性将基于现有文件/证书,因此无需重新提供它们。

默认情况下,续订会尝试使用由 kubeadm 管理的本地 PKI 中的证书机构;作为替代方案, 也可以使用 K8s certificate API 进行证书续订,或者(作为最后一种选择)生成 CSR 请求。

续订后,为了使更改生效,需要重新启动控制平面组件,并最终重新分发更新的证书,以防证书文件在其他地方使用。

kubeadm certs renew etcd-healthcheck-client [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存证书的路径。

--config string

kubeadm 配置文件的路径。

-h, --help

etcd-healthcheck-client 操作的帮助命令。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。 如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

续订 etcd 节点间用来相互通信的证书。

概要

续订 etcd 节点间用来相互通信的证书。

无论证书的到期日期如何,续订都是无条件进行的;SAN 等额外属性将基于现有文件/证书,因此无需重新提供它们。

默认情况下,续订会尝试使用由 kubeadm 管理的本地 PKI 中的证书机构; 作为替代方案,也可以使用 K8s certificate API 进行证书续订,或者(作为最后一种选择)生成 CSR 请求。

续订后,为了使更改生效,需要重新启动控制平面组件,并最终重新分发续订的证书,以防证书文件在其他地方使用。

kubeadm certs renew etcd-peer [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存证书的路径。

--config string

kubeadm 配置文件的路径。

-h, --help
etcd-peer 操作的帮助命令。
--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。 如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

--use-api

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

续订用于提供 etcd 服务的证书。

概要

续订用于提供 etcd 服务的证书。

续订无条件地进行,与证书的到期日期无关;SAN 等额外属性将基于现有的文件/证书,因此无需重新提供它们。

默认情况下,续订会尝试在 kubeadm 管理的本地 PKI 中使用证书颁发机构;作为替代方案, 可以使用 K8s 证书 API 进行证书续订,或者作为最后一种选择来生成 CSR 请求。

续订后,为了使更改生效,需要重新启动控制平面组件,并最终重新分发续订的证书,以防文件在其他地方使用。

kubeadm certs renew etcd-server [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存证书的路径。

--config string

kubeadm 配置文件的路径。

-h, --help

etcd-server 操作的帮助命令。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。 如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

--use-api

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

为前端代理客户端续订证书。

概要

为前端代理客户端续订证书。

无论证书的到期日期如何,续订都会无条件地进行;SAN 等额外属性将基于现有文件/证书,因此无需重新提供它们。

默认情况下,续订尝试使用位于 kubeadm 所管理的本地 PKI 中的证书颁发机构;作为替代方案, 也可以使用 K8s certificate API 进行证书续订;亦或者,作为最后一种方案,生成 CSR 请求。

续订后,为了使更改生效,需要重新启动控制平面组件,并最终重新分发更新的证书,以防文件在其他地方使用。

kubeadm certs renew front-proxy-client [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

存储证书的路径。

--config string

kubeadm 配置文件的路径。

-h, --help

front-proxy-client 操作的帮助命令。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。 如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

--use-api
使用 Kubernetes certificate API 续订证书。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

续订 kubeconfig 文件中嵌入的证书,以供调度管理器使用。

概要

续订 kubeconfig 文件中嵌入的证书,以供调度管理器使用。

续订无条件地进行,与证书的到期日期无关;SAN 等额外属性将基于现有的文件/证书,因此无需重新提供它们。

默认情况下,续订会尝试使用在 kubeadm 所管理的本地 PKI 中的证书颁发机构;作为替代方案, 也可以使用 K8s certificate API 进行证书续订;亦或者,作为最后一种选择,生成 CSR 请求。

续订后,为了使更改生效,需要重新启动控制平面组件,并最终重新分发更新的证书,以防文件在其他地方使用。

kubeadm certs renew scheduler.conf [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存证书的路径。

--config string

kubeadm 配置文件的路径。

-h, --help

scheduler.conf 操作的帮助命令。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。 如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

kubeadm certs certificate-key

此命令可用来生成一个新的控制面证书密钥。密钥可以作为 --certificate-key 标志的取值传递给 kubeadm initkubeadm join 命令,从而在添加新的控制面节点时能够自动完成证书复制。

生成证书密钥。

概要

该命令将打印出可以与 "init" 命令一起使用的安全的随机生成的证书密钥。

你也可以使用 kubeadm init --upload-certs 而无需指定证书密钥; 此命令将为你生成并打印一个证书密钥。

kubeadm certs certificate-key [flags]

选项

-h, --help

certificate-key 操作的帮助命令。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

kubeadm certs check-expiration

此命令检查 kubeadm 所管理的本地 PKI 中的证书是否以及何时过期。 更多的相关细节,可参见 检查证书过期

为一个 Kubernetes 集群检查证书的到期时间。

概要

检查 kubeadm 管理的本地 PKI 中证书的到期时间。

kubeadm certs check-expiration [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存证书的路径。

--config string

到 kubeadm 配置文件的路径。

-h, --help

check-expiration 操作的帮助命令。

--kubeconfig string     默认为:"/etc/kubernetes/admin.conf"

在和集群连接时使用该 kubeconfig 文件。 如果此标志未被设置,那么将会在一些标准的位置去搜索存在的 kubeconfig 文件。

继承于父命令的选项

--rootfs string

[实验] 到'真实'主机根文件系统的路径。

kubeadm certs generate-csr

此命令可用来为所有控制面证书和 kubeconfig 文件生成密钥和 CSR(签名请求)。 用户可以根据自身需要选择 CA 为 CSR 签名。

生成密钥和证书签名请求

概要

为运行控制平面所需的所有证书生成密钥和证书签名请求(CSR)。该命令会生成部分 kubeconfig 文件, 其中 "users > user > client-key-data" 字段包含私钥数据,并为每个 kubeconfig 文件创建一个随附的 ".csr" 文件。

此命令设计用于 Kubeadm 外部 CA 模式。 它生成你可以提交给外部证书颁发机构进行签名的 CSR。

你需要使用 ".crt" 作为文件扩展名将 PEM 编码的签名证书与密钥文件一起保存。 或者,对于 kubeconfig 文件,PEM 编码的签名证书应使用 base64 编码, 并添加到 "users > user > client-certificate-data" 字段。

kubeadm certs generate-csr [flags]

示例

# 以下命令将为所有控制平面证书和 kubeconfig 文件生成密钥和 CSR:
kubeadm certs generate-csr --kubeconfig-dir /tmp/etc-k8s --cert-dir /tmp/etc-k8s/pki

选项

--cert-dir string

保存证书的路径。

--config string

到 kubeadm 配置文件的路径。

-h, --help

generate-csr 操作的帮助命令。

--kubeconfig-dir string     默认值:"/etc/kubernetes"

保存 kubeconfig 文件的路径。

继承于父命令的选项

--rootfs string

[实验] 到'真实'主机根文件系统的路径。

接下来

  • 用来启动引导 Kubernetes 控制面节点的 kubeadm init 命令
  • 用来将节点连接到集群的 kubeadm join 命令
  • 用来回滚 kubeadm initkubeadm join 对当前主机所做修改的 kubeadm reset 命令

6.10.1.11 - kubeadm init phase

kubeadm init phase 能确保调用引导过程的原子步骤。 因此,如果希望自定义应用,则可以让 kubeadm 做一些工作,然后填补空白。

kubeadm init phasekubeadm init 工作流 一致,后台都使用相同的代码。

kubeadm init phase preflight

使用此命令可以在控制平面节点上执行启动前检查。

运行预检。

概要

运行 kubeadm init 前的预检。

kubeadm init phase preflight [flags]

示例

# 使用配置文件对 kubeadm init 进行预检
kubeadm init phase preflight --config kubeadm-config.yaml

选项

--config string

kubeadm 配置文件的路径。

--cri-socket string

要连接的 CRI 套接字的路径。如果该值为空,kubeadm 将尝试自动检测; 仅当你安装了多个 CRI 或使用非标准的 CRI 套接字时才应使用此选项。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

preflight 操作的帮助命令。

--ignore-preflight-errors strings

错误将显示为警告的检查列表。例如:'IsPrivilegedUser,Swap'。取值为 'all' 时将忽略检查中的所有错误。

继承于父命令的选项

--rootfs string

[实验] 到'真实'主机根文件系统的路径。

kubeadm init phase kubelet-start

此阶段将写入 kubelet 配置文件和环境文件,然后启动 kubelet。

编写 kubelet 配置并(重新)启动 kubelet

概要

使用 kubelet 配置文件编写一个文件,并使用特定节点的 kubelet 设置编写一个环境文件,然后(重新)启动 kubelet。

kubeadm init phase kubelet-start [flags]

示例

# 将来自 InitConfiguration 文件中的 kubelet 参数写入一个动态环境文件。
kubeadm init phase kubelet-start --config config.yaml

选项

--config string

kubeadm 配置文件的路径。

--cri-socket string

连接到 CRI 套接字的路径。如果为空,则 kubeadm 将尝试自动检测该值;仅当安装了多个 CRI 或具有非标准 CRI 套接字时,才使用此选项。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

kubelet-start 操作的帮助命令

--node-name string

指定节点名称。

--patches string

目录路径,指向包含名为 “target[suffix][+patchtype].extension” 的文件的目录。 例如,"kube-apiserver0+merge.yaml" 或 "etcd.json" 这种简单形式。 "target" 可以是 “kube-apiserver”、“kube-controller-manager”、“kube-scheduler”、“etcd”、“kubeletconfiguration” 之一, "patchtype" 可以是 "strategic"、"merge" 或 "json" 之一,并且它们与 kubectl 支持的补丁格式匹配。 默认的 "patchtype" 为 "strategic"。 "extension" 必须为 "json" 或 "yaml"。 "suffix" 是一个可选字符串,可用于确定按字母顺序首先应用哪些补丁。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

kubeadm init phase certs

该阶段可用于创建 kubeadm 所需的所有证书。

证书生成。

概要

此命令不应单独运行。请参阅可用子命令列表。

kubeadm init phase certs [flags]

选项

-h, --help

certs 操作的帮助命令。

从父指令中继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

生成所有证书。

概要

生成所有证书。

kubeadm init phase certs all [flags]

选项

--apiserver-advertise-address string

API 服务器所公布的其正在监听的 IP 地址。如果未设置,将使用默认网络接口。

--apiserver-cert-extra-sans strings

用于 API 服务器服务证书的可选额外替代名称(SAN)。可以同时使用 IP 地址和 DNS 名称。

--cert-dir string     默认值:"/etc/kubernetes/pki"

证书的存储路径。

--config string

kubeadm 配置文件的路径。

--control-plane-endpoint string

为控制平面指定一个稳定的 IP 地址或 DNS 名称。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

all 操作的帮助命令。

--kubernetes-version string     默认值:"stable-1"

为控制平面选择特定的 Kubernetes 版本。

--service-cidr string     默认值:"10.96.0.0/12"

VIP 服务使用其它的 IP 地址范围。

--service-dns-domain string     默认值:"cluster.local"

服务使用其它的域名,例如:"myorg.internal"。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

生成自签名的 Kubernetes CA 以便为其他 Kubernetes 组件提供身份标识。

概要

生成自签名的 Kubernetes CA 以便为其他 Kubernetes 组件提供身份标识,并将其保存到 ca.crt 和 ca.key 文件中。

如果两个文件都已存在,则 kubeadm 将跳过生成步骤,使用现有文件。

Alpha 免责声明:此命令目前处于 Alpha 阶段。

kubeadm init phase certs ca [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

证书的存储路径。

--config string

kubeadm 配置文件的路径。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

ca 操作的帮助命令。

--kubernetes-version string     默认值:"stable-1"

为控制平面选择特定的 Kubernetes 版本。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

生成用于服务 Kubernetes API 的证书。

概要

生成用于服务 Kubernetes API 的证书,并将其保存到 apiserver.crt 和 apiserver.key 文件中。

如果两个文件都已存在,则 kubeadm 将跳过生成步骤,使用现有文件。

Alpha 免责声明:此命令目前处于 Alpha 阶段。

kubeadm init phase certs apiserver [flags]

选项

--apiserver-advertise-address string

API 服务器所公布的其正在监听的 IP 地址。如果未设置,则使用默认的网络接口。

--apiserver-cert-extra-sans strings

用于 API Server 服务证书的可选附加主体备用名称(SAN)。可以是 IP 地址和 DNS 名称。

--cert-dir string     默认值:"/etc/kubernetes/pki"

证书的存储路径。

--config string

kubeadm 配置文件的路径。

--control-plane-endpoint string

为控制平面指定一个稳定的 IP 地址或 DNS 名称。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

apiserver 操作的帮助命令。

--kubernetes-version string     默认值:"stable-1"

为控制平面指定特定的 Kubernetes 版本。

--service-cidr string     默认值:"10.96.0.0/12"

指定服务 VIP 可使用的其他 IP 地址段。

--service-dns-domain string     默认值:"cluster.local"

为服务使用其他域名,例如 "myorg.internal"。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

生成供 API 服务器连接 kubelet 的证书。

概要

生成供 API 服务器连接 kubelet 的证书,并将其保存到 apiserver-kubelet-client.crt 和 apiserver-kubelet-client.key 文件中。

如果两个文件都已存在,则 kubeadm 将跳过生成步骤,使用现有文件。

Alpha 免责声明:此命令目前处于 Alpha 阶段。

kubeadm init phase certs apiserver-kubelet-client [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

存储证书的路径。

--config string

kubeadm 配置文件路径。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

apiserver-kubelet-client 操作的帮助命令。

--kubernetes-version string     默认值:"stable-1"

为控制平面指定特定的 Kubernetes 版本。

继承于父命令的选项

--rootfs string

[实验] 指向宿主机上的 '实际' 根文件系统的路径。

生成自签名 CA 来提供前端代理的身份。

概要

生成自签名 CA 来提供前端代理的身份,并将其保存到 front-proxy-ca.cert 和 front-proxy-ca.key 文件中。

如果两个文件都已存在,kubeadm 将跳过生成步骤并将使用现有文件。

Alpha 免责声明:此命令目前是 Alpha 阶段。

kubeadm init phase certs front-proxy-ca [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

存储证书的路径。

--config string

kubeadm 配置文件的路径。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

front-proxy-ca 操作的帮助命令。

--kubernetes-version string     默认值:"stable-1"

为控制平面选择特定的 Kubernetes 版本。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

为前端代理客户端生成证书。

概要

为前端代理客户端生成证书,并将其保存到 front-proxy-client.crt 和 front-proxy-client.key 文件中。

如果两个文件都已存在,kubeadm 将跳过生成步骤并将使用现有文件。

Alpha 免责声明:此命令目前是 Alpha 阶段。

kubeadm init phase certs front-proxy-client [flags]

选项

--cert-dir string     默认:"/etc/kubernetes/pki"

存储证书的路径。

--config string

kubeadm 配置文件的路径。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

front-proxy-client 操作的帮助命令。

--kubernetes-version string     默认值:"stable-1"

为控制平面选择特定的 Kubernetes 版本。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

生成用于为 etcd 设置身份的自签名 CA。

概要

生成用于为 etcd 设置身份的自签名 CA,并将其保存到 etcd/ca.crt 和 etcd/ca.key 文件中。

如果两个文件都已存在,则 kubeadm 将跳过生成步骤,使用现有文件。

kubeadm init phase certs etcd-ca [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

证书的存储路径。

--config string

kubeadm 配置文件的路径。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

etcd-ca 操作的帮助命令。

--kubernetes-version string     默认值:"stable-1"

为控制平面选择特定的 Kubernetes 版本。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

生成用于提供 etcd 服务的证书。

概要

生成用于提供 etcd 服务的证书,并将其保存到 etcd/server.crt 和 etcd/server.key 文件中。

默认 SAN 为 localhost、127.0.0.1、127.0.0.1、:: 1

如果两个文件都已存在,则 kubeadm 将跳过生成步骤,使用现有文件。

Alpha 免责声明:此命令当前为 Alpha 功能。

kubeadm init phase certs etcd-server [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存和存储证书的路径。

--config string

kubeadm 配置文件的路径。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

etcd-server 操作的帮助命令。

--kubernetes-version string     默认值:"stable-1"

为控制平面指定特定的 Kubernetes 版本。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

生成 etcd 节点相互通信的证书。

概要

生成 etcd 节点相互通信的证书,并将其保存到 etcd/peer.crt 和 etcd/peer.key 文件中。

默认 SAN 为 localhost、127.0.0.1、127.0.0.1、:: 1

如果两个文件都已存在,则 kubeadm 将跳过生成步骤,使用现有文件。

Alpha 免责声明:此命令当前为 Alpha 功能。

kubeadm init phase certs etcd-peer [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存和存储证书的路径。

--config string

kubeadm 配置文件的路径。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

etcd-peer 操作的帮助命令。

--kubernetes-version string     默认值:"stable-1"

为控制平面指定特定的 Kubernetes 版本。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

生成用于 etcd 健康检查的活跃性探针的证书。

概要

生成用于 etcd 健康检查的活跃性探针的证书,并将其保存到 etcd/healthcheck-client.crt 和 etcd/healthcheck-client.key 文件中。

如果两个文件都已存在,则 kubeadm 将跳过生成步骤,使用现有文件。

kubeadm init phase certs etcd-healthcheck-client [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

证书存储的路径。

--config string

kubeadm 配置文件的路径。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

etcd-healthcheck-client 操作的帮助命令。

--kubernetes-version string     默认值:"stable-1"

为控制平面选择特定的 Kubernetes 版本。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

生成 apiserver 用来访问 etcd 的证书。

概要

生成 apiserver 用于访问 etcd 的证书,并将其保存到 apiserver-etcd-client.crt 和 apiserver-etcd-client.key 文件中。

如果两个文件都已存在,则 kubeadm 将跳过生成步骤,使用现有文件。

Alpha 免责声明:此命令目前处于 Alpha 阶段。

kubeadm init phase certs apiserver-etcd-client [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

证书的存储路径。

--config string

kubeadm 配置文件的路径。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

apiserver-etcd-client 操作的帮助命令。

--kubernetes-version string     默认值:"stable-1"

为控制平面指定特定的 Kubernetes 版本。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

生成用来签署服务账号令牌的私钥及其公钥。

概要

生成用来签署服务账号令牌的私钥及其公钥,并将其保存到 sa.key 和 sa.pub 文件中。

如果两个文件都已存在,则 kubeadm 会跳过生成步骤,而将使用现有文件。

kubeadm init phase certs sa [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存和存储证书的路径。

-h, --help

sa 操作的帮助命令。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

kubeadm init phase kubeconfig

可以通过调用 all 子命令来创建所有必需的 kubeconfig 文件,或者分别调用它们。

生成所有建立控制平面和管理员(admin)所需的 kubeconfig 文件。

概要

此命令并非设计用来单独运行。请阅读可用子命令列表。

kubeadm init phase kubeconfig [flags]

选项

-h, --help

kubeconfig 操作的帮助命令

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

生成所有 kubeconfig 文件。

概要

生成所有 kubeconfig 文件。

kubeadm init phase kubeconfig all [flags]

选项

--apiserver-advertise-address string

API 服务器所公布的其正在监听的 IP 地址。如果没有设置,将使用默认的网络接口。

--apiserver-bind-port int32     默认值:6443

要绑定到 API 服务器的端口。

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存和存储证书的路径。

--config string

kubeadm 配置文件的路径。

--control-plane-endpoint string

为控制平面指定一个稳定的 IP 地址或 DNS 名称。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

all 操作的帮助命令。

--kubeconfig-dir string     默认值:"/etc/kubernetes"

kubeconfig 文件的保存路径。

--kubernetes-version string     默认值:"stable-1"

为控制平面指定特定的 Kubernetes 版本。

--node-name string

指定节点名称。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

为管理员(admin)和 kubeadm 本身生成 kubeconfig 文件。

概要

为管理员和 kubeadm 本身生成 kubeconfig 文件,并将其保存到 admin.conf 文件中。

kubeadm init phase kubeconfig admin [flags]

选项

--apiserver-advertise-address string

API 服务器所公布的其正在监听的 IP 地址。如果未设置,则使用默认的网络接口。

--apiserver-bind-port int32     默认值:6443

要绑定到 API 服务器的端口。

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存和存储证书的路径。

--config string

kubeadm 配置文件的路径。

--control-plane-endpoint string

为控制平面指定一个稳定的 IP 地址或 DNS 名称。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

admin 操作的帮助命令。

--kubeconfig-dir string     默认值:"/etc/kubernetes"

kubeconfig 文件的保存路径。

--kubernetes-version string     默认值:"stable-1"

为控制平面指定特定的 Kubernetes 版本。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

为 kubelet 生成一个 kubeconfig 文件,仅仅用于集群引导目的。

概要

生成 kubelet 要使用的 kubeconfig 文件,并将其保存到 kubelet.conf 文件。

请注意,该操作目的是用于引导集群。在控制平面启动之后,应该从 CSR API 请求所有 kubelet 凭据。

kubeadm init phase kubeconfig kubelet [flags]

选项

--apiserver-advertise-address string

API 服务器所公布的其正在监听的 IP 地址。如果未设置,则使用默认的网络接口。

--apiserver-bind-port int32     默认值:6443

要绑定到 API 服务器的端口。

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存和存储证书的路径。

--config string

kubeadm 配置文件的路径。

--control-plane-endpoint string

为控制平面指定一个稳定的 IP 地址或 DNS 名称。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

kubelet 操作的帮助命令。

--kubeconfig-dir string     默认值:"/etc/kubernetes"

kubeconfig 文件的保存路径。

--kubernetes-version string     默认值:"stable-1"

为控制平面选择特定的 Kubernetes 版本。

--node-name string

指定节点的名称。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

生成控制器管理器要使用的 kubeconfig 文件。

概要

生成控制器管理器要使用的 kubeconfig 文件,并保存到 controller-manager.conf 文件中。

kubeadm init phase kubeconfig controller-manager [flags]

选项

--apiserver-advertise-address string

API 服务器所公布的其正在监听的 IP 地址。如果未设置,则使用默认的网络接口。

--apiserver-bind-port int32     默认值:6443

要绑定到 API 服务器的端口。

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存和存储证书的路径。

--config string

kubeadm 配置文件的路径。

--control-plane-endpoint string

为控制平面指定一个稳定的 IP 地址或 DNS 名称。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

controller-manager 操作的帮助命令。

--kubeconfig-dir string     默认值:"/etc/kubernetes"

kubeconfig 文件的保存路径。

--kubernetes-version string     默认值:"stable-1"

为控制平面指定特定的 Kubernetes 版本。

继承于父命令的选项

--rootfs 字符串

[实验] 到 '真实' 主机根文件系统的路径。

生成调度器使用的 kubeconfig 文件。

概要

生成调度器(scheduler)要使用的 kubeconfig 文件,并保存到 scheduler.conf 文件中。

kubeadm init phase kubeconfig scheduler [flags]

选项

--apiserver-advertise-address string

API 服务器所公布的其正在监听的 IP 地址。如果未设置,则使用默认的网络接口。

--apiserver-bind-port int32     默认值:6443

要绑定到 API 服务器的端口。

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存和存储证书的路径。

--config string

kubeadm 配置文件的路径。

--control-plane-endpoint string

为控制平面指定一个稳定的 IP 地址或 DNS 名称。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

scheduler 操作的帮助命令。

--kubeconfig-dir string     默认值:"/etc/kubernetes"

kubeconfig 文件的保存路径。

--kubernetes-version string     默认值:"stable-1"

为控制平面指定特定的 Kubernetes 版本。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

kubeadm init phase control-plane

使用此阶段,可以为控制平面组件创建所有必需的静态 Pod 文件。

生成建立控制平面所需的静态 Pod 清单文件。

概要

此命令并非设计用来单独运行。请参阅可用子命令列表。

kubeadm init phase control-plane [flags]

选项

-h, --help

control-plane 操作的帮助命令。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

生成所有静态 Pod 清单文件。

概要

生成所有的静态 Pod 清单文件。

kubeadm init phase control-plane all [flags]

示例

# 为控制平面组件生成静态 Pod 清单文件,其功能等效于 kubeadm init 生成的文件。
kubeadm init phase control-plane all

# 使用从某配置文件中读取的选项为生成静态 Pod 清单文件。
kubeadm init phase control-plane all --config config.yaml

选项

--apiserver-advertise-address string

API 服务器所公布的其正在监听的 IP 地址。如果未设置,将使用默认的网络接口。

--apiserver-bind-port int32     默认值:6443

API 服务器要绑定的端口。

--apiserver-extra-args <逗号分割的 'key=value' 对>

形式为 <flagname>=<value> 的一组额外参数,用来传递给 API 服务器, 或者覆盖其默认配置值。

--cert-dir string     默认值:"/etc/kubernetes/pki"

存储证书的路径。

--config string

kubeadm 配置文件的路径。

--control-plane-endpoint string

为控制平面选择一个稳定的 IP 地址或者 DNS 名称。

--controller-manager-extra-args <逗号分割的 'key=value' 对>

一组形式为 <flagname>=<value> 的额外参数,用来传递给控制管理器(Controller Manager) 或覆盖其默认设置值。

--dry-run
不做任何更改;只输出将要执行的操作。
--feature-gates string

一组用来描述各种特性门控的键值(key=value)对。选项是:
EtcdLearnerMode=true|false (ALPHA - 默认值=false)
PublicKeysECDSA=true|false (ALPHA - 默认值=false)
RootlessControlPlane=true|false (ALPHA - 默认值=false)
UpgradeAddonsBeforeControlPlane=true|false (DEPRECATED - 默认值=false)

-h, --help

all 操作的帮助命令。

--image-repository string     默认值:"registry.k8s.io"

选择用于拉取控制平面镜像的容器仓库。

--kubernetes-version string     默认值:"stable-1"

为控制平面选择指定的 Kubernetes 版本。

--patches string

包含名为 "target[suffix][+patchtype].extension" 的文件的目录的路径。 例如,"kube-apiserver0+merge.yaml"或是简单的 "etcd.json"。 "target" 可以是 "kube-apiserver"、"kube-controller-manager"、"kube-scheduler"、"etcd"、"kubeletconfiguration" 之一。 "patchtype" 可以是 "strategic"、"merge" 或者 "json" 之一, 并且它们与 kubectl 支持的补丁格式相匹配。 默认的 "patchtype" 是 "strategic"。 "extension" 必须是 "json" 或 "yaml"。 "suffix" 是一个可选字符串,可用于确定首先按字母顺序应用哪些补丁。

--pod-network-cidr string

指定 Pod 网络的 IP 地址范围。如果设置了此标志,控制平面将自动地为每个节点分配 CIDR。

--scheduler-extra-args <逗号分割的 'key=value' 对>

一组形式为 <flagname>=<value> 的额外参数,用来传递给调度器(Scheduler) 或覆盖其默认设置值。

--service-cidr string     默认值:"10.96.0.0/12"

为服务 VIP 选择 IP 地址范围。

从父指令继承的选项

--rootfs string

[实验] 指向'真实'宿主机的根文件系统的路径。

生成 kube-apiserver 静态 Pod 清单。

概要

生成 kube-apiserver 静态 Pod 清单。

kubeadm init phase control-plane apiserver [flags]

选项

--apiserver-advertise-address string

API 服务器所公布的其正在监听的 IP 地址。如果未设置,将使用默认网络接口。

--apiserver-bind-port int32     默认值: 6443

要绑定到 API 服务器的端口。

--apiserver-extra-args <逗号分隔的 'key=value' 对>

一组 <flagname>=<value> 形式的额外参数,用来传递给 API 服务器或者覆盖其默认参数配置。

--cert-dir string     默认值:"/etc/kubernetes/pki"

保存和存储证书的路径。

--config string

kubeadm 配置文件的路径。

--control-plane-endpoint string

为控制平面指定一个稳定的 IP 地址或 DNS 名称。

--dry-run

不做任何更改;只输出将要执行的操作。

--feature-gates string

一组键值对,用于描述各种功能特性的特性门控。选项是:
EtcdLearnerMode=true|false (ALPHA - 默认值=false)
PublicKeysECDSA=true|false (ALPHA - 默认值=false)
RootlessControlPlane=true|false (ALPHA - 默认值=false)
UpgradeAddonsBeforeControlPlane=true|false (DEPRECATED - 默认值=false)

-h, --help

apiserver 操作的帮助命令。

--image-repository string     默认值:"registry.k8s.io"

选择要从中拉取控制平面镜像的容器仓库。

--kubernetes-version string     默认值:"stable-1"

为控制平面选择特定的 Kubernetes 版本。

--patches string

包含名为 "target[suffix][+patchtype].extension" 的文件的目录的路径。 例如,"kube-apiserver0+merge.yaml"或仅仅是 "etcd.json"。 "target" 可以是 "kube-apiserver"、"kube-controller-manager"、"kube-scheduler"、"etcd"、"kubeletconfiguration" 之一。 "patchtype" 可以是 "strategic"、"merge" 或者 "json" 之一, 并且它们与 kubectl 支持的补丁格式相同。 默认的 "patchtype" 是 "strategic"。 "extension" 必须是"json" 或"yaml"。 "suffix" 是一个可选字符串,可用于确定首先按字母顺序应用哪些补丁。

--service-cidr string     默认值:"10.96.0.0/12"

指定服务 VIP 使用 IP 地址的其他范围。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统路径。

生成 kube-controller-manager 静态 Pod 清单。

概要

生成 kube-controller-manager 静态 Pod 清单。

kubeadm init phase control-plane controller-manager [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

存储证书的路径。

--config string

kubeadm 配置文件的路径。

--controller-manager-extra-args <comma-separated 'key=value' pairs>

一组 <flagname>=< 形式的额外参数,传递给控制器管理器(Controller Manager) 或者覆盖其默认配置值

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

controller-manager 操作的帮助命令。

--image-repository string     默认值:"registry.k8s.ioo"

选择要从中拉取控制平面镜像的容器仓库。

--kubernetes-version string     默认值:"stable-1"

为控制平面选择特定的 Kubernetes 版本。

--patches string

包含名为 "target[suffix][+patchtype].extension" 的文件的目录。 例如,"kube-apiserver0+merge.yaml" 或者 "etcd.json"。 "target" 可以是 "kube-apiserver"、"kube-controller-manager"、"kube-scheduler"、"etcd"、"kubeletconfiguration" 之一。 "patchtype" 可以是 "strategic"、"merge" 或 "json" 之一,分别与 kubectl 所支持的 patch 格式相匹配。默认的 "patchtype" 是 "strategic"。 "extension" 必须是 "json" 或 "yaml"。 "suffix" 是一个可选的字符串,用来确定按字母顺序排序时首先应用哪些 patch。

--pod-network-cidr string

指定 Pod 网络的 IP 地址范围。如果设置,控制平面将自动为每个节点分配 CIDR。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

生成 kube-scheduler 静态 Pod 清单。

概要

生成 kube-scheduler 静态 Pod 清单。

kubeadm init phase control-plane scheduler [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

存储证书的路径。

--config string

kubeadm 配置文件的路径。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

scheduler 操作的帮助命令。

--image-repository string     默认值:"registry.k8s.io"

选择要从中拉取控制平面镜像的容器仓库。

--kubernetes-version string     默认值:"stable-1"

为控制平面选择特定的 Kubernetes 版本。

--patches string
包含名为 "target[suffix][+patchtype].extension" 的文件的目录。 例如,"kube-apiserver0+merge.yaml" 或者 "etcd.json"。 "target" 可以是 "kube-apiserver"、"kube-controller-manager"、"kube-scheduler"、"etcd"、"kubeletconfiguration" 之一。 "patchtype" 可以是 "strategic"、"merge"、"json" 之一,分别与 kubectl 所支持的 patch 格式相匹配。默认的 "patchtype" 是 "strategic"。 "extension" 必须是 "json" 或 "yaml"。 "suffix" 是一个可选的字符串,用来确定按字母顺序排序时首先应用哪些 patch。
--scheduler-extra-args <逗号分隔的 'key=value' 键值对>

一组 <flagname>=<value> 形式的额外参数,用来传递给调度器或者覆盖其默认参数配置。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

kubeadm init phase etcd

根据静态 Pod 文件,使用以下阶段创建一个本地 etcd 实例。

为本地 etcd 生成静态 Pod 的清单文件。

概要

此命令并非设计用来单独运行。请参阅可用子命令列表。

kubeadm init phase etcd [flags]

选项

-h, --help

etcd 操作的帮助命令。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

为本地单节点 etcd 实例生成静态 Pod 清单文件。

概要

为本地单节点 etcd 实例生成静态 Pod 清单文件。

kubeadm init phase etcd local [flags]

示例

# 为 etcd 生成静态 Pod 清单文件,其功能等效于 kubeadm init 生成的文件。
kubeadm init phase etcd local

# 使用从配置文件读取的选项为 etcd 生成静态 Pod 清单文件。
kubeadm init phase etcd local --config config.yaml

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

存储证书的路径。

--config string

kubeadm 配置文件的路径。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

local 操作的帮助命令。

--image-repository string     默认值:"registry.k8s.io"

选择要从中拉取控制平面镜像的容器仓库。

--patches string

包含名为 "target[suffix][+patchtype].extension" 的文件的目录的路径。 例如,"kube-apiserver0+merge.yaml" 或是简单的 "etcd.json"。 "target" 可以是 "kube-apiserver"、"kube-controller-manager"、"kube-scheduler"、"etcd" 、"kubeletconfiguration" 之一。 "patchtype" 可以是 "strategic"、"merge" 或者 "json" 之一, 并且它们与 kubectl 支持的补丁格式相匹配。 默认的 "patchtype" 是 "strategic"。 "extension" 必须是 "json" 或 "yaml"。 "suffix" 是一个可选字符串,可用于确定首先按字母顺序应用哪些补丁。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

kubeadm init phase upload-config

可以使用此命令将 kubeadm 配置上传到集群。或者使用 kubeadm config

上传 kubeadm 和 kubelet 配置到 ConfigMap 中。

概要

此命令并非设计用来单独运行。请参阅可用的子命令列表。

kubeadm init phase upload-config [flags]

选项

-h, --help

upload-config 操作的帮助命令。

从父命令中继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

将所有配置上传到 ConfigMap。

概要

将所有配置上传到 ConfigMap

kubeadm init phase upload-config all [flags]

选项

--config string

kubeadm 配置文件的路径。

--cri-socket string

要连接的 CRI 套接字的路径。如果该值为空,kubeadm 将尝试自动检测; 仅当你安装了多个 CRI 或使用非标准的 CRI 套接字时才应使用此选项。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

all 操作的帮助命令。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

将 kubeadm ClusterConfiguration 上传到 ConfigMap。

概要

将 kubeadm ClusterConfiguration 上传到 kube-system 命名空间中名为 kubeadm-config 的 ConfigMap 中。 这样就可以正确配置系统组件,并在升级时提供无缝的用户体验。

另外,可以使用 kubeadm 配置。

kubeadm init phase upload-config kubeadm [flags]

示例

# 上传集群配置
kubeadm init phase upload-config --config=myConfig.yaml

选项

--config string

kubeadm 配置文件的路径。

--cri-socket string

要连接的 CRI 套接字的路径。如果该值为空,kubeadm 将尝试自动检测; 仅当你安装了多个 CRI 或使用非标准的 CRI 套接字时才应使用此选项。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

kubeadm 操作的帮助命令。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

将 kubelet 组件配置上传到 ConfigMap。

概要

将从 kubeadm InitConfiguration 对象提取的 kubelet 配置上传到集群中的 kubelet-config ConfigMap。

kubeadm init phase upload-config kubelet [flags]

示例

# 将 kubelet 配置从 kubeadm 配置文件上传到集群中的 ConfigMap。
kubeadm init phase upload-config kubelet --config kubeadm.yaml

选项

--config string

到 kubeadm 配置文件的路径。

--cri-socket string

要连接的 CRI 套接字的路径。如果该值为空,kubeadm 将尝试自动检测; 仅当你安装了多个 CRI 或使用非标准的 CRI 套接字时才应使用此选项。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

kubelet 操作的帮助命令。

-- kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。如果未设置该标签, 则可以通过一组标准路径来寻找已有的 kubeconfig 文件。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

kubeadm init phase upload-certs

使用以下阶段将控制平面证书上传到集群。默认情况下,证书和加密密钥会在两个小时后过期。

将证书上传到 kubeadm-certs。

概要

将控制平面证书上传到 kubeadm-certs Secret。

kubeadm init phase upload-certs [flags]

选项

--certificate-key string

用于加密 kubeadm-certs Secret 中的控制平面证书的密钥。

--config string

kubeadm 配置文件的路径。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

upload-certs 操作的帮助命令。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

用来与集群通信的 kubeconfig 文件。 如果此标志未设置,则可以在一组标准的位置搜索现有的 kubeconfig 文件。

--skip-certificate-key-print

不要打印输出用于加密控制平面证书的密钥。

--upload-certs

将控制平面证书上传到 kubeadm-certs Secret。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

kubeadm init phase mark-control-plane

使用以下阶段来给作为控制平面的节点 打标签(label)和记录污点(taint)。

标记节点为控制平面节点。

概要

标记节点为控制平面节点。

kubeadm init phase mark-control-plane [flags]

示例

# 将控制平面标签和污点应用于当前节点,其功能等效于 kubeadm init 执行的操作
kubeadm init phase mark-control-plane --config config.yaml

# 将控制平面标签和污点应用于特定节点
kubeadm init phase mark-control-plane --node-name myNode

选项

--config string

kubeadm 配置文件的路径。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

mark-control-plane 操作的帮助命令。

--node-name string

指定节点名称。

从父命令继承的选项

--rootfs 字符串

[实验] 到 '真实' 主机根文件系统的路径。

kubeadm init phase bootstrap-token

使用以下阶段来配置引导令牌。

生成用于将节点加入集群的引导令牌

概要

启动引导令牌(bootstrap token)用于在即将加入集群的节点和控制平面节点之间建立双向信任。

该命令使启动引导令牌(bootstrap token)所需的所有配置生效,然后创建初始令牌。

kubeadm init phase bootstrap-token [flags]

示例

# 进行所有引导令牌配置,并创建一个初始令牌,功能上与 kubeadm init 生成的令牌等效。
kubeadm init phase bootstrap-token

选项

--config string

kubeadm 配置文件的路径。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

bootstrap-token 操作的帮助命令。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

用于和集群通信的 kubeconfig 文件。如果它没有被设置,那么 kubeadm 将会搜索一个已经存在于标准路径的 kubeconfig 文件。

--skip-token-print

跳过打印 'kubeadm init' 生成的默认引导令牌。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

kubeadm init phase kubelet-finalize

使用以下阶段在 TLS 引导后更新与 kubelet 相关的设置。 你可以使用 all 子命令来运行所有 kubelet-finalize 阶段。

TLS 引导后更新与 kubelet 相关的设置

概要

TLS 引导后更新与 kubelet 相关的设置

kubeadm init phase kubelet-finalize [flags]

示例

  # 在 TLS 引导后更新与 kubelet 相关的设置
  kubeadm init phase kubelet-finalize all --config

选项

-h, --help

kubelet-finalize 操作的帮助命令

继承于父命令的选项

--rootfs string

[实验] 到'真实'主机根文件系统的路径。

运行 kubelet-finalize 的所有阶段

概要

运行 kubelet-finalize 的所有阶段

kubeadm init phase kubelet-finalize all [flags]

示例

# 在 TLS 引导后更新与 kubelet 相关的设置
kubeadm init phase kubelet-finalize all --config

选项

--cert-dir string     默认值: "/etc/kubernetes/pki"

保存和存储证书的路径。

--config string

kubeadm 配置文件的路径。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

all 操作的帮助命令

继承于父命令的选项

--rootfs string

[实验] 到'真实'主机根文件系统的路径。

启用 kubelet 客户端证书轮换

概要

启用 kubelet 客户端证书轮换

kubeadm init phase kubelet-finalize experimental-cert-rotation [flags]

选项

--cert-dir string     Default: "/etc/kubernetes/pki"

保存和存储证书的路径。

--config string

kubeadm 配置文件的路径。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

experimental-cert-rotation 操作的帮助命令

继承于父命令的选项

--rootfs string

[实验] 到'真实'主机根文件系统的路径。

kubeadm init phase addon

可以使用 all 子命令安装所有可用的插件,或者有选择性地安装它们。

安装必要的插件以通过一致性测试。

概要

此命令并非设计用来单独运行。请参阅可用子命令列表。

kubeadm init phase addon [flags]

选项

-h, --help

addon 操作的帮助命令。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

安装所有插件。

概要

安装所有插件(addon)。

kubeadm init phase addon all [flags]

选项

--apiserver-advertise-address string

API 服务器所公布的其正在监听的 IP 地址。如果未设置,则将使用默认网络接口。

--apiserver-bind-port int32     默认值:6443

API 服务器绑定的端口。

--config string

kubeadm 配置文件的路径。

--control-plane-endpoint string

为控制平面指定一个稳定的 IP 地址或 DNS 名称。

--dry-run

不做任何更改;只输出将要执行的操作。

--feature-gates string
一组键值对(key=value),描述了各种特征。选项包括:
EtcdLearnerMode=true|false (ALPHA - 默认值=false)
PublicKeysECDSA=true|false (ALPHA - 默认值=false)
RootlessControlPlane=true|false (ALPHA - 默认值=false)
UpgradeAddonsBeforeControlPlane=true|false (DEPRECATED - 默认值=false)
-h, --help

all 操作的帮助命令。

--image-repository string     默认值:"registry.k8s.io"

选择用于拉取控制平面镜像的容器仓库。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

--kubernetes-version string     默认值:"stable-1"

为控制平面选择特定的 Kubernetes 版本。

--pod-network-cidr string

指定 Pod 网络的 IP 地址范围。如果已设置,控制平面将自动为每个节点分配 CIDR。

--service-cidr string     默认值:"10.96.0.0/12"

为服务 VIP 使用 IP 地址的其他范围。

--service-dns-domain string     默认值:"cluster.local"
为服务使用其他域名,例如 "myorg.internal"。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

将 CoreDNS 插件安装到 Kubernetes 集群。

概要

通过 API 服务器安装 CoreDNS 附加组件。请注意,即使 DNS 服务器已部署,在安装 CNI 之前 DNS 服务器不会被调度执行。

kubeadm init phase addon coredns [flags]

选项

--config string

kubeadm 配置文件的路径。

--dry-run

不做任何更改;只输出将要执行的操作。

--feature-gates string

一组用来描述各种特性门控的键值(key=value)对。选项是:
EtcdLearnerMode=true|false (ALPHA - 默认值=false)
PublicKeysECDSA=true|false (ALPHA - 默认值=false)
RootlessControlPlane=true|false (ALPHA - 默认值=false)
UpgradeAddonsBeforeControlPlane=true|false (DEPRECATED - 默认值=false)

-h, --help

coredns 操作的帮助命令。

--image-repository string     默认值:"registry.k8s.io"

选择用于拉取控制平面镜像的容器仓库。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

--kubernetes-version string     默认值:"stable-1"

为控制平面选择特定的 Kubernetes 版本。

--print-manifest

向 STDOUT 输出插件清单,而不是安装这些插件。

--service-cidr string     默认值:"10.96.0.0/12"

为服务 VIP 选择 IP 地址范围。

--service-dns-domain string     默认值:"cluster.local"

为服务使用其它域名,例如:"myorg.internal"。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

将 kube-proxy 插件安装到 Kubernetes 集群

概要

通过 API 服务器安装 kube-proxy 附加组件。

kubeadm init phase addon kube-proxy [flags]

选项

--apiserver-advertise-address string

API 服务器所公布的其正在监听的 IP 地址。如果未设置,则将使用默认网络接口。

--apiserver-bind-port int32     默认值: 6443

API 服务器绑定的端口。

--config string

kubeadm 配置文件的路径。

--control-plane-endpoint string

为控制平面指定一个稳定的 IP 地址或 DNS 名称。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

kube-proxy 操作的帮助命令。

--image-repository string     默认值:"registry.k8s.io"

选择用于拉取控制平面镜像的容器仓库。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 kubeconfig 文件。如果未设置该参数,则可以在一组标准位置中搜索现有的 kubeconfig 文件。

--kubernetes-version string     默认值:"stable-1"

为控制平面选择特定的 Kubernetes 版本。

--pod-network-cidr string

指定 Pod 网络的 IP 地址范围。如果已设置,控制平面将自动为每个节点分配 CIDR。

--print-manifest

向 STDOUT 打印插件清单,而非安装这些插件。

继承于父命令的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

有关 v1beta3 配置中每个字段的更多详细信息,可以访问 API

接下来

6.10.1.12 - kubeadm join phase

kubeadm join phase 使你能够调用 join 过程的基本原子步骤。 因此,如果希望执行自定义操作,可以让 kubeadm 做一些工作,然后由用户来补足剩余操作。

kubeadm join phasekubeadm join 工作流程 一致,后台都使用相同的代码。

kubeadm join phase

使用此命令来调用 join 工作流程的某个阶段。

概要

使用此命令来调用 join 工作流程的某个阶段。

选项

-h, --help

phase 操作的帮助命令。

从父命令中继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

kubeadm join phase preflight

使用此命令可以在即将加入集群的节点上执行启动前检查。

运行 join 命令前检查。

概要

运行 kubeadm join 命令添加节点前检查。

kubeadm join phase preflight [api-server-endpoint] [flags]

示例

# 使用配置文件运行 kubeadm join 命令添加节点前检查。
kubeadm join phase preflight --config kubeadm-config.yaml

选项

--apiserver-advertise-address string

对于将要托管新的控制平面实例的节点,指定 API 服务器将公布的其正在侦听的 IP 地址。如果未设置,则使用默认网络接口。

--apiserver-bind-port int32     默认值:6443

针对将要托管新的控制平面实例的节点,设置 API 服务器要绑定的端口。

--certificate-key string

使用此密钥可以解密由 `init` 操作上传的证书 Secret。

--config string

kubeadm 配置文件的路径。

--control-plane

在此节点上创建一个新的控制平面实例。

--cri-socket string

提供给 CRI 套接字建立连接的路径。如果为空,则 kubeadm 将尝试自动检测该值; 仅当安装了多个 CRI 或存在非标准的 CRI 套接字时,才使用此选项。

--discovery-file string

对于基于文件的发现,给出用于加载集群信息的文件或者 URL。

--discovery-token string

对于基于令牌的发现,该令牌用于验证从 API 服务器获取的集群信息。

--discovery-token-ca-cert-hash strings

对于基于令牌的发现,验证根 CA 公钥是否匹配此哈希值(格式:"<type>:<value>")。

--discovery-token-unsafe-skip-ca-verification

对于基于令牌的发现,允许在未关联 --discovery-token-ca-cert-hash 参数的情况下添加节点。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

preflight 操作的帮助命令。

--ignore-preflight-errors stringSlice

错误将显示为警告的检查列表;例如:'IsPrivilegedUser,Swap'。取值为 'all' 时将忽略检查中的所有错误。

--node-name string

指定节点名称。

--tls-bootstrap-token string

指定在加入节点时用于临时通过 Kubernetes 控制平面进行身份验证的令牌。

--token string

如果未提供这些值,则将它们用于 discovery-token 令牌和 tls-bootstrap 令牌。

从父命令继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

kubeadm join phase control-plane-prepare

使用此阶段,你可以准备一个作为控制平面的节点。

准备为控制平面服务的机器。

概要

准备为控制平面服务的机器。

kubeadm join phase control-plane-prepare [flags]

示例

# 准备为控制平面服务的机器
kubeadm join phase control-plane-prepare all

选项

-h, --help

control-plane-prepare 操作的帮助命令。

从父命令中继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

准备为控制平面服务的机器。

概要

准备为控制平面服务的机器。

kubeadm join phase control-plane-prepare all [api-server-endpoint] [flags]

选项

--apiserver-advertise-address string

如果该节点托管一个新的控制平面实例,则 API 服务器将公布其正在侦听的 IP 地址。如果未设置,则使用默认网络接口。

--apiserver-bind-port int32     默认值:6443

如果该节点托管一个新的控制平面实例,则为 API 服务器要绑定的端口。

--certificate-key string

使用此密钥解密由 init 上传的证书 Secret。

--config string

kubeadm 配置文件的路径。

--control-plane

在此节点上创建一个新的控制平面实例。

--discovery-file string

对于基于文件的发现,给出用于加载集群信息的文件或者 URL。

--discovery-token string

对于基于令牌的发现,该令牌用于验证从 API 服务器获取的集群信息。

--discovery-token-ca-cert-hash strings

对于基于令牌的发现,请验证根 CA 公钥是否匹配此哈希值(格式:"<type>:<value>")。

--discovery-token-unsafe-skip-ca-verification

对于基于令牌的发现,允许在未关联 --discovery-token-ca-cert-hash 参数的情况下添加节点。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help
all 操作的帮助命令。
--node-name string

指定节点名称。

--patches string

包含名为 “target[suffix][+patchtype].extension” 的文件的目录的路径。 例如,“kube-apiserver0+merge.yaml” 或只是 “etcd.json”。 “target” 可以是 “kube-apiserver”、“kube-controller-manager”、“kube-scheduler”、“etcd”、“kubeletconfiguration” 之一。 “patchtype” 可以是 “strategic”、“merge” 或 “json”,它们匹配 kubectl 支持的补丁格式。 默认的 “patchtype” 是 “strategic”。“extension” 必须是 “json” 或 “yaml”。 “suffix” 是一个可选字符串,可用于基于字母数字顺序确定首先应用哪些补丁。

--tls-bootstrap-token string

指定在加入节点时用于临时通过 Kubernetes 控制平面进行身份验证的令牌。

--token string

如果未提供这些值,则将它们用于 discovery-token 令牌和 tls-bootstrap 令牌。

从父命令继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

[实验] 从 kubeadm-certs Secret 下载控制平面节点之间共享的证书。

概要

[实验] 从 kubeadm-certs Secret 下载控制平面节点之间共享的证书。

kubeadm join phase control-plane-prepare download-certs [api-server-endpoint] [flags]

选项

--certificate-key string

使用此密钥可以解密由 init 上传的证书 Secret。

--config string

kubeadm 配置文件的路径。

--control-plane

在此节点上创建一个新的控制平面实例。

--discovery-file string

对于基于文件的发现,给出用于加载集群信息的文件或者 URL。

--discovery-token string

对于基于令牌的发现,该令牌用于验证从 API 服务器获取的集群信息。

--discovery-token-ca-cert-hash stringSlice

对于基于令牌的发现,请验证根 CA 公钥是否匹配此哈希值(格式:"<type>:<value>")。

--discovery-token-unsafe-skip-ca-verification

对于基于令牌的发现,允许在未关联 --discovery-token-ca-cert-hash 参数的情况下添加节点。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

download-certs 操作的帮助命令。

--tls-bootstrap-token string

指定在加入节点时用于临时通过 Kubernetes 控制平面进行身份验证的令牌。

--token string

如果未提供这些值,则将它们用于 discovery-token 令牌和 tls-bootstrap 令牌。

从父命令中继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

为新的控制平面组件生成证书。

概要

为新的控制平面组件生成证书。

kubeadm join phase control-plane-prepare certs [api-server-endpoint] [flags]

选项

--apiserver-advertise-address string

如果该节点托管一个新的控制平面实例,则 API 服务器将公布其正在侦听的 IP 地址。如果未设置,则使用默认网络接口。

--config string

kubeadm 配置文件的路径。

--control-plane

在此节点上创建一个新的控制平面实例。

--discovery-file string

对于基于文件的发现,给出用于加载集群信息的文件或者 URL。

--discovery-token string

对于基于令牌的发现,该令牌用于验证从 API 服务器获取的集群信息。

--discovery-token-ca-cert-hash strings

对于基于令牌的发现,请验证根 CA 公钥是否匹配此哈希值(格式:"<type>:<value>")。

--discovery-token-unsafe-skip-ca-verification

对于基于令牌的发现,允许在未关联 --discovery-token-ca-cert-hash 参数的情况下添加节点。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

certs 操作的帮助命令。

--node-name string

指定节点名称。

--tls-bootstrap-token string

指定在加入节点时用于临时通过 Kubernetes 控制平面进行身份验证的令牌。

--token string

如果未提供这些值,则将它们用于 discovery-token 令牌和 tls-bootstrap 令牌。

从父命令继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

为新的控制平面组件生成 kubeconfig。

概要

为新的控制平面组件生成 kubeconfig。

kubeadm join phase control-plane-prepare kubeconfig [api-server-endpoint] [flags]

选项

--certificate-key string

使用此密钥可以解密由 init 上传的证书 Secret。

--config string

kubeadm 配置文件的路径。

--control-plane

在此节点上创建一个新的控制平面实例。

--discovery-file string

对于基于文件的发现,给出用于加载集群信息的文件或者 URL。

--discovery-token string

对于基于令牌的发现,该令牌用于验证从 API 服务器获取的集群信息。

--discovery-token-ca-cert-hash stringSlice

对于基于令牌的发现,请验证根 CA 公钥是否匹配此哈希值(格式:"<type>:<value>")。

--discovery-token-unsafe-skip-ca-verification

对于基于令牌的发现,允许在未关联 --discovery-token-ca-cert-hash 参数的情况下添加节点。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

kubeconfig 操作的帮助命令。

--tls-bootstrap-token string

指定在加入节点时用于临时通过 Kubernetes 控制平面进行身份验证的令牌。

--token string

如果未提供这些值,则将它们用于 discovery-token 令牌和 tls-bootstrap 令牌。

从父命令中继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

为新的控制平面组件生成清单。

概要

为新的控制平面组件生成清单(manifest)。

kubeadm join phase control-plane-prepare control-plane [flags]

选项

--apiserver-advertise-address string

对于将要托管新的控制平面实例的节点,指定 API 服务器将公布的其正在侦听的 IP 地址。如果未设置,则使用默认网络接口。

--apiserver-bind-port int32     默认值:6443

针对将要托管新的控制平面实例的节点,设置 API 服务器要绑定的端口。

--config string

kubeadm 配置文件的路径。

--control-plane

在此节点上创建一个新的控制平面实例。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

control-plane 操作的帮助命令。

--patches string

包含名为 "target[suffix][+patchtype].extension" 的文件的目录的路径。 例如 "kube-apiserver0+merge.yaml" 或仅仅是 "etcd.json"。 "target" 可以是 "kube-apiserver"、"kube-controller-manager"、 "kube-scheduler"、"etcd"、"kubeletconfiguration" 之一。 "patchtype" 可以是 "strategic"、"merge" 或者 "json" 之一, 并且它们与 kubectl 支持的补丁格式相同。 默认的 "patchtype" 是 "strategic"。 "extension" 必须是 "json" 或 "yaml"。 "suffix" 是一个可选字符串,可用于确定首先按字母顺序应用哪些补丁。

从父命令中继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

kubeadm join phase kubelet-start

使用此阶段,你可以配置 kubelet 设置、证书和(重新)启动 kubelet。

配置 kubelet、证书并(重新)启动 kubelet。

概要

生成一个包含 KubeletConfiguration 的文件和一个包含特定于节点的 kubelet 配置的环境文件,然后(重新)启动 kubelet。

kubeadm join phase kubelet-start [api-server-endpoint] [flags]

选项

--config string
kubeadm 配置文件的路径。
--cri-socket string

提供给 CRI 套接字建立连接的路径。如果为空,则 kubeadm 将尝试自动检测该值; 仅当安装了多个 CRI 或存在非标准的 CRI 套接字时,才使用此选项。

--discovery-file string

对于基于文件的发现,给出用于加载集群信息的文件或者 URL。

--discovery-token string

对于基于令牌的发现,该令牌用于验证从 API 服务器获取的集群信息。

--discovery-token-ca-cert-hash stringSlice

对于基于令牌的发现,验证根 CA 公钥是否匹配此哈希值(格式:"<type>:<value>")。

--discovery-token-unsafe-skip-ca-verification

对于基于令牌的发现,允许在未关联 --discovery-token-ca-cert-hash 参数的情况下添加节点。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

kubelet-start 操作的帮助命令。

--node-name string

指定节点名称。

--patches string

目录路径,指向的目录中包含名为 “target[suffix][+patchtype].extension” 的文件。 例如,"kube-apiserver0+merge.yaml" 或 "etcd.json" 这种简单形式。 "target" 可以是 “kube-apiserver”、“kube-controller-manager”、“kube-scheduler”、“etcd”、“kubeletconfiguration” 之一, "patchtype" 可以是 "strategic"、"merge" 或 "json" 之一,并且它们与 kubectl 支持的补丁格式匹配。 默认的 "patchtype" 为 "strategic"。 "extension" 必须为 "json" 或 "yaml"。 "suffix" 是一个可选字符串,可用于确定按字母顺序首先应用哪些补丁。

--tls-bootstrap-token string

指定在加入节点时用于临时通过 Kubernetes 控制平面进行身份验证的令牌。

--token string

如果未提供这些值,则将它们用于 discovery-token 令牌和 tls-bootstrap 令牌。

从父命令继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

kubeadm join phase control-plane-join

使用此阶段,你可以将节点作为控制平面实例加入。

添加作为控制平面实例的机器。

概要

添加作为控制平面实例的机器。

kubeadm join phase control-plane-join [flags]

示例

# 将机器作为控制平面实例加入
kubeadm join phase control-plane-join all

选项

-h, --help

control-plane-join 操作的帮助命令。

从父命令中继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

添加作为控制平面实例的机器。

概要

添加作为控制平面实例的机器。

kubeadm join phase control-plane-join all [flags]

选项

--apiserver-advertise-address string

如果该节点托管一个新的控制平面实例,则 API 服务器将公布其正在侦听的 IP 地址。如果未设置,则使用默认网络接口。

--config string

kubeadm 配置文件的路径。

--control-plane

在此节点上创建一个新的控制平面实例。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

all 操作的帮助命令。

--node-name string

指定节点名称。

--patches string

包含名为 “target[suffix][+patchtype].extension” 的文件的目录的路径。 例如,“kube-apiserver0+merge.yaml” 或只是 “etcd.json”。 “target” 可以是 “kube-apiserver”、“kube-controller-manager”、“kube-scheduler”、“etcd”、“kubeletconfiguration” 之一。 “patchtype” 可以是 “strategic”、“merge” 或 “json”,它们匹配 kubectl 支持的补丁格式。 默认的 “patchtype” 是 “strategic”。“extension” 必须是 “json” 或 “yaml”。 “suffix” 是一个可选字符串,可用于基于字母数字顺序确定首先应用哪些补丁。

从父命令继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

添加一个新的本地 etcd 成员。

概要

添加新的本地 etcd 成员。

kubeadm join phase control-plane-join etcd [flags]

选项

--apiserver-advertise-address string

如果该节点托管一个新的控制平面实例,则 API 服务器将公布其正在侦听的 IP 地址。如果未设置,则使用默认网络接口。

--config string

kubeadm 配置文件的路径。

--control-plane

在此节点上创建一个新的控制平面实例。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

etcd 操作的帮助命令。

--node-name string

指定节点的名称。

--patches string

包含名为 “target[suffix][+patchtype].extension” 的文件的目录的路径。 例如,“kube-apiserver0+merge.yaml” 或只是 “etcd.json”。 “target” 可以是 “kube-apiserver”、“kube-controller-manager”、“kube-scheduler”、“etcd” 、”kubeletconfiguration”之一。 “patchtype” 可以是 “strategic”、“merge” 或 “json”,它们匹配 kubectl 支持的补丁格式。 默认的 “patchtype” 是 “strategic”。“extension” 必须是 “json” 或 “yaml”。 “suffix” 是一个可选字符串,可用于基于字母数字顺序确定首先应用哪些补丁。

从父命令中继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

将新的控制平面节点注册到 kubeadm-config ConfigMap 维护的 ClusterStatus 中(已弃用)。

概要

将新的控制平面节点注册到 kubeadm-config ConfigMap 维护的 ClusterStatus 中(已弃用)。

kubeadm join phase control-plane-join update-status [flags]

选项

--apiserver-advertise-address string

如果该节点托管一个新的控制平面实例,则 API 服务器将公布其正在侦听的 IP 地址。如果未设置,则使用默认网络接口。

--config string

kubeadm 配置文件的路径。

--control-plane

在此节点上创建一个新的控制平面实例。

-h, --help

update-status 操作的帮助命令。

--node-name string

指定节点名称。

从父命令中继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

将节点标记为控制平面节点。

概要

将节点标记为控制平面节点。

kubeadm join phase control-plane-join mark-control-plane [flags]

选项

--config string

kubeadm 配置文件的路径。

--control-plane

在此节点上创建一个新的控制平面实例。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

mark-control-plane 操作的帮助命令。

--node-name string

指定节点的名称。

从父命令中继承的选项

--rootfs string

[实验] 到 '真实' 主机根文件系统的路径。

接下来

6.10.1.13 - kubeadm kubeconfig

kubeadm kubeconfig 提供用来管理 kubeconfig 文件的工具。

如果希望查看如何使用 kubeadm kubeconfig user 的示例,请参阅 为其他用户生成 kubeconfig 文件.

kubeadm kubeconfig

kubeconfig 文件工具。

概要

kubeconfig 文件工具。

选项

-h, --help
kubeconfig 操作的帮助命令。

从父命令继承的选项

--rootfs string
[实验] 到 '真实' 主机根文件系统的路径。

kubeadm kubeconfig user

此命令可用来为其他用户生成一个 kubeconfig 文件。

为其他用户输出一个 kubeconfig 文件。

概要

为其他用户输出一个 kubeconfig 文件。

kubeadm kubeconfig user [flags]

示例

# 为一个名为 foo 的其他用户输出 kubeconfig 文件
kubeadm kubeconfig user --client-name=foo

# 使用 kubeadm 配置文件 bar 为另一个名为 foo 的用户输出 kubeconfig 文件
kubeadm alpha kubeconfig user --client-name=foo --config=bar

选项

--client-name string
用户名。如果生成客户端证书,则用作其 CN。
--config string
指向 kubeadm 配置文件的路径。
-h, --help
user 操作的帮助命令。
--org strings
客户端证书的组织。如果创建客户端证书,此值将用作其 O 字段值。
--token string
应该用此令牌做为 kubeconfig 的身份验证机制,而不是客户端证书。
--validity-period duration     Default: 8760h0m0s

客户证书的合法期限。所设置值为相对当前时间的偏移。

从父命令继承的选项

--rootfs string
[实验] 指向 '真实' 宿主机的根目录。

6.10.1.14 - kubeadm reset phase

kubeadm reset phase 使你能够调用 reset 过程的基本原子步骤。 因此,如果希望执行自定义操作,可以让 kubeadm 做一些工作,然后由用户来补足剩余操作。

kubeadm reset phasekubeadm reset 工作流程 一致,后台都使用相同的代码。

kubeadm reset phase

使用此命令来调用 reset 工作流程的某个阶段。

概要

使用此命令来调用 reset 工作流程的某个阶段。

选项

-h, --help

phase 操作的帮助命令。

从父命令继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

kubeadm reset phase preflight

使用此阶段,你可以在要重置的节点上执行启动前检查阶段。

kubeadm reset(重置)前运行启动前检查。

概要

kubeadm reset(重置)前运行启动前检查。

kubeadm reset phase preflight [flags]

选项

--dry-run

不做任何更改;只输出将要执行的操作。

-f, --force

在不提示确认的情况下重置节点。

-h, --help

preflight 操作的帮助命令。

--ignore-preflight-errors strings

错误将显示为警告的检查列表;例如:'IsPrivilegedUser,Swap'。取值为 'all' 时将忽略检查中的所有错误。

从父命令继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

kubeadm reset phase remove-etcd-member

使用此阶段,你可以从 etcd 集群中删除此控制平面节点的 etcd 成员。

删除本地 etcd 成员。

概要

删除控制平面节点的本地 etcd 成员。

kubeadm reset phase remove-etcd-member [flags]

选项

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

remove-etcd-member 操作的帮助命令。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

与集群通信时使用的 Kubeconfig 文件。如果未设置该标志,则可以在默认位置中查找现有的 Kubeconfig 文件。

从父命令继承的选项

--rootfs string

[实验] 到'真实'主机根文件系统的路径。

kubeadm reset phase cleanup-node

使用此阶段,你可以在此节点上执行清理工作。

执行 cleanup node(清理节点)操作。

概要

执行 cleanup node(清理节点)操作。

kubeadm reset phase cleanup-node [flags]

选项

--cert-dir string     默认值:"/etc/kubernetes/pki"

存储证书的目录路径。如果已指定,则需要清空此目录。

--cleanup-tmp-dir

清理 "/etc/kubernetes/tmp" 目录。

--cri-socket string

要连接的 CRI 套接字的路径。如果为空,则 kubeadm 将尝试自动检测此值;仅当安装了多个 CRI 或具有非标准 CRI 套接字时,才使用此选项。

--dry-run

不做任何更改;只输出将要执行的操作。

-h, --help

cleanup-node 操作的帮助命令。

从父命令继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

接下来

6.10.1.15 - kubeadm upgrade phase

在 Kubernetes v1.15.0 版本中,kubeadm 引入了对 kubeadm upgrade node 阶段的初步支持。其他 kubeadm upgrade 子命令如 apply 等阶段将在未来发行版中添加。

kubeadm upgrade node phase

使用此阶段,可以选择执行辅助控制平面或工作节点升级的单独步骤。请注意,kubeadm upgrade apply 命令仍然必须在主控制平面节点上调用。

使用此命令调用 node 工作流的某个阶段。

概要

使用此命令调用 node 工作流的某个阶段。

选项

-h, --help

phase 操作的帮助命令。

从父命令继承的选项

--rootfs string

[实验] 指向 '真实' 宿主机根文件系统的路径。

执行升级节点的预检。

概要

执行 kubeadm 升级节点的预检。

kubeadm upgrade node phase preflight [flags]

选项

-h, --help

preflight 操作的帮助命令。

--ignore-preflight-errors strings

错误将显示为警告的检查清单。示例:'IsPrivilegedUser,Swap'。值为 'all' 表示忽略所有检查的错误。

继承于父命令的选项

--rootfs string

[实验] 指向 “真实” 主机根文件系统的路径。

升级部署在此节点上的控制平面实例,如果有的话。

概要

升级部署在此节点上的控制平面实例,如果有的话。

kubeadm upgrade node phase control-plane [flags]

选项

--certificate-renewal

续订在升级期间变更的组件所使用的证书。

--dry-run

不改变任何状态,只输出将要执行的动作。

--etcd-upgrade     默认值: true

执行 etcd 的升级。

-h, --help

control-plane 操作的帮助命令。

--kubeconfig string     默认值: "/etc/kubernetes/admin.conf"

用于和集群通信的 KubeConfig 文件。如果它没有被设置,那么 kubeadm 将会搜索一个已经存在于标准路径的 KubeConfig 文件。

--patches string
包含名为 "target[suffix][+patchtype].extension" 的文件的目录的路径。 例如,"kube-apiserver0+merge.yaml" 或仅仅是 "etcd.json"。 "target" 可以是 "kube-apiserver"、"kube-controller-manager"、"kube-scheduler"、"etcd"、"kubeletconfiguration" 之一。 "patchtype" 可以是 "strategic"、"merge" 或 "json" 之一,并且它们与 kubectl 支持的补丁格式匹配。 默认的 "patchtype" 为 "strategic"。"extension" 必须为 "json" 或 "yaml"。 "suffix" 是一个可选字符串,可用于确定首先按字母顺序应用哪些补丁。

从父命令继承的选项

--rootfs string

[实验] 指向 “真实” 主机根文件系统的路径。

升级此节点的 kubelet 配置。

概要

从集群中 ConfigMap kubelet-config 下载 kubelet 配置。

kubeadm upgrade node phase kubelet-config [flags]

选项

--dry-run

不改变任何状态,只输出将要执行的操作。

-h, --help

kubelet-config 操作的帮助命令。

--kubeconfig string     默认值:"/etc/kubernetes/admin.conf"

用于和集群通信的 kubeconfig 文件。如果它没有被设置,那么 kubeadm 将会搜索一个已经存在于标准路径的 kubeconfig 文件。

--patches string

目录路径,指向的目录中包含名为 “target[suffix][+patchtype].extension” 的文件。 例如,"kube-apiserver0+merge.yaml" 或 "etcd.json" 这种简单形式。 "target" 可以是 “kube-apiserver”、“kube-controller-manager”、“kube-scheduler”、“etcd”、“kubeletconfiguration” 之一, "patchtype" 可以是 "strategic"、"merge" 或 "json" 之一,并且它们与 kubectl 支持的补丁格式匹配。 默认的 "patchtype" 为 "strategic"。 "extension" 必须为 "json" 或 "yaml"。 "suffix" 是一个可选字符串,可用于确定按字母顺序首先应用哪些补丁。

从父命令继承的选项

--rootfs string

[实验] 到'真实'主机根文件系统的路径。

接下来

6.10.1.16 - 实现细节

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

kubeadm initkubeadm join 结合在一起提供了良好的用户体验, 因为从头开始创建实践最佳而配置最基本的 Kubernetes 集群。 但是,kubeadm 如何 做到这一点可能并不明显。

本文档提供了更多幕后的详细信息,旨在分享有关 Kubernetes 集群最佳实践的知识。

核心设计原则

kubeadm initkubeadm join 设置的集群该是:

  • 安全的:它应采用最新的最佳实践,例如:
    • 实施 RBAC 访问控制
    • 使用节点鉴权机制(Node Authorizer)
    • 在控制平面组件之间使用安全通信
    • 在 API 服务器和 kubelet 之间使用安全通信
    • 锁定 kubelet API
    • 锁定对系统组件(例如 kube-proxy 和 CoreDNS)的 API 的访问
    • 锁定启动引导令牌(Bootstrap Token)可以访问的内容
  • 用户友好:用户只需要运行几个命令即可:
    • kubeadm init
    • export KUBECONFIG=/etc/kubernetes/admin.conf
    • kubectl apply -f <所选网络.yaml>
    • kubeadm join --token <令牌> <端点>:<端口>
  • 可扩展的
    • 应偏向任何特定的网络提供商,不涉及配置集群网络
    • 应该可以使用配置文件来自定义各种参数

常量以及众所周知的值和路径

为了降低复杂性并简化基于 kubeadm 的高级工具的开发,对于众所周知的路径和文件名, kubeadm 使用了一组有限的常量值。

Kubernetes 目录 /etc/kubernetes 在应用程序中是一个常量, 因为在大多数情况下它显然是给定的路径,并且是最直观的位置;其他路径常量和文件名有:

  • /etc/kubernetes/manifests 作为 kubelet 查找静态 Pod 清单的路径。静态 Pod 清单的名称为:

    • etcd.yaml
    • kube-apiserver.yaml
    • kube-controller-manager.yaml
    • kube-scheduler.yaml
  • /etc/kubernetes/ 作为带有控制平面组件身份标识的 kubeconfig 文件的路径。kubeconfig 文件的名称为:
    • kubelet.conf (在 TLS 引导时名称为 bootstrap-kubelet.conf)
    • controller-manager.conf
    • scheduler.conf
    • admin.conf 用于集群管理员和 kubeadm 本身
  • 证书和密钥文件的名称:
    • ca.crtca.key 用于 Kubernetes 证书颁发机构
    • apiserver.crtapiserver.key 用于 API 服务器证书
    • apiserver-kubelet-client.crtapiserver-kubelet-client.key 用于 API 服务器安全地连接到 kubelet 的客户端证书
    • sa.pubsa.key 用于控制器管理器签署 ServiceAccount 时使用的密钥
    • front-proxy-ca.crtfront-proxy-ca.key 用于前端代理证书颁发机构
    • front-proxy-client.crtfront-proxy-client.key 用于前端代理客户端

kubeadm init 工作流程内部设计

kubeadm init 内部工作流程 包含一系列要执行的原子性工作任务,如 kubeadm init 中所述。

kubeadm init phase 命令允许用户分别调用每个任务,并最终提供可重用且可组合的 API 或工具箱, 其他 Kubernetes 引导工具、任何 IT 自动化工具和高级用户都可以使用它来创建自定义集群。

预检

Kubeadm 在启动 init 之前执行一组预检,目的是验证先决条件并避免常见的集群启动问题。 用户可以使用 --ignore-preflight-errors 选项跳过特定的预检或全部检查。

  • [警告] 如果要使用的 Kubernetes 版本(由 --kubernetes-version 标志指定)比 kubeadm CLI 版本至少高一个小版本。
  • Kubernetes 系统要求:
    • 如果在 linux上运行:
      • [错误] 如果内核早于最低要求的版本
      • [错误] 如果未设置所需的 cgroups 子系统
  • [错误] 如果 CRI 端点未应答
  • [错误] 如果用户不是 root 用户
  • [错误] 如果机器主机名不是有效的 DNS 子域
  • [警告] 如果通过网络查找无法访问主机名
  • [错误] 如果 kubelet 版本低于 kubeadm 支持的最低 kubelet 版本(当前小版本 -1)
  • [错误] 如果 kubelet 版本比所需的控制平面板版本至少高一个小版本(不支持的版本偏差)
  • [警告] 如果 kubelet 服务不存在或已被禁用
  • [警告] 如果 firewalld 处于活动状态
  • [错误] 如果 API ​​服务器绑定的端口或 10250/10251/10252 端口已被占用
  • [错误] 如果 /etc/kubernetes/manifest 文件夹已经存在并且不为空
  • [错误] 如果 /proc/sys/net/bridge/bridge-nf-call-iptables 文件不存在或不包含 1
  • [错误] 如果建议地址是 ipv6,并且 /proc/sys/net/bridge/bridge-nf-call-ip6tables 不存在或不包含 1
  • [错误] 如果启用了交换分区
  • [错误] 如果命令路径中没有 conntrackipiptablesmountnsenter 命令
  • [警告] 如果命令路径中没有 ebtablesethtoolsocattctouchcrictl 命令
  • [警告] 如果 API 服务器、控制器管理器、调度程序的其他参数标志包含一些无效选项
  • [警告] 如果与 https://API.AdvertiseAddress:API.BindPort 的连接通过代理
  • [警告] 如果服务子网的连接通过代理(仅检查第一个地址)
  • [警告] 如果 Pod 子网的连接通过代理(仅检查第一个地址)
  • 如果提供了外部 etcd:
    • [错误] 如果 etcd 版本低于最低要求版本
    • [错误] 如果指定了 etcd 证书或密钥,但无法找到
  • 如果未提供外部 etcd(因此将安装本地 etcd):
    • [错误] 如果端口 2379 已被占用
    • [错误] 如果 Etcd.DataDir 文件夹已经存在并且不为空
  • 如果授权模式为 ABAC:
    • [错误] 如果 abac_policy.json 不存在
  • 如果授权方式为 Webhook
    • [错误] 如果 webhook_authz.conf 不存在

请注意:

  1. 可以使用 kubeadm init phase preflight 命令单独触发预检。

生成必要的证书

Kubeadm 生成用于不同目的的证书和私钥对:

  • Kubernetes 集群的自签名证书颁发机构会保存到 ca.crt 文件和 ca.key 私钥文件中
  • 用于 API 服务器的服务证书,使用 ca.crt 作为 CA 生成,并将证书保存到 apiserver.crt 文件中,私钥保存到 apiserver.key 文件中。该证书应包含以下备用名称:

    • Kubernetes 服务的内部 clusterIP(服务 CIDR 的第一个地址。 例如:如果服务的子网是 10.96.0.0/12,则为 10.96.0.1
    • Kubernetes DNS 名称,例如:如果 --service-dns-domain 标志值是 cluster.local, 则为 kubernetes.default.svc.cluster.local; 加上默认的 DNS 名称 kubernetes.default.svckubernetes.defaultkubernetes
    • 节点名称
    • --apiserver-advertise-address
    • 用户指定的其他备用名称
  • 用于 API 服务器安全连接到 kubelet 的客户端证书,使用 ca.crt 作为 CA 生成, 并保存到 apiserver-kubelet-client.crt,私钥保存到 apiserver-kubelet-client.key 文件中。该证书应该在 system:masters 组织中。
  • 用于签名 ServiceAccount 令牌的私钥保存到 sa.key 文件中,公钥保存到 sa.pub 文件中。
  • 用于前端代理的证书颁发机构保存到 front-proxy-ca.crt 文件中,私钥保存到 front-proxy-ca.key 文件中
  • 前端代理客户端的客户端证书,使用 front-proxy-ca.crt 作为 CA 生成,并保存到 front-proxy-client.crt 文件中,私钥保存到 front-proxy-client.key 文件中

证书默认情况下存储在 /etc/kubernetes/pki 中,但是该目录可以使用 --cert-dir 标志进行配置。

请注意:

  1. 如果证书和私钥对都存在,并且其内容经过评估符合上述规范,将使用现有文件, 并且跳过给定证书的生成阶段。 这意味着用户可以将现有的 CA 复制到 /etc/kubernetes/pki/ca.{crt,key}, kubeadm 将使用这些文件对其余证书进行签名。 请参阅使用自定义证书
  2. 仅对 CA 来说,如果所有其他证书和 kubeconfig 文件都已就位,则可以只提供 ca.crt 文件, 而不提供 ca.key 文件。 kubeadm 能够识别出这种情况并启用 ExternalCA,这也意味着了控制器管理器中的 csrsigner 控制器将不会启动。 -->
  1. 如果 kubeadm 在外部 CA 模式 下运行,所有证书必须由用户提供,因为 kubeadm 无法自行生成证书。
  2. 如果在 --dry-run 模式下执行 kubeadm,证书文件将写入一个临时文件夹中。
  3. 可以使用 kubeadm init phase certs all 命令单独生成证书。

为控制平面组件生成 kubeconfig 文件

Kubeadm 生成具有用于控制平面组件身份标识的 kubeconfig 文件:

  • 供 kubelet 在 TLS 引导期间使用的 kubeconfig 文件——/etc/kubernetes/bootstrap-kubelet.conf。 在此文件中,有一个引导令牌或内嵌的客户端证书,向集群表明此节点身份。

    此客户端证书应:

    • 根据节点鉴权模块的要求,属于 system:nodes 组织
    • 具有通用名称(CN):system:node:<小写主机名>
  • 控制器管理器的 kubeconfig 文件 —— /etc/kubernetes/controller-manager.conf; 在此文件中嵌入了一个具有控制器管理器身份标识的客户端证书。 此客户端证书应具有 CN:system:kube-controller-manager, 该 CN 由 RBAC 核心组件角色 默认定义的。
  • 调度器的 kubeconfig 文件 —— /etc/kubernetes/scheduler.conf; 此文件中嵌入了具有调度器身份标识的客户端证书。此客户端证书应具有 CN:system:kube-scheduler, 该 CN 由 RBAC 核心组件角色 默认定义的。

另外,用于 kubeadm 本身和 admin 的 kubeconfig 文件也被生成并保存到 /etc/kubernetes/admin.conf 文件中。 此处的 admin 定义为正在管理集群并希望完全控制集群(root)的实际人员。 内嵌的 admin 客户端证书应是 system:masters 组织的成员, 这一组织名由默认的 RBAC 面向用户的角色绑定 定义。它还应包括一个 CN。kubeadm 使用 kubernetes-admin CN。

请注意:

  1. ca.crt 证书内嵌在所有 kubeconfig 文件中。
  2. 如果给定的 kubeconfig 文件存在且其内容经过评估符合上述规范,则 kubeadm 将使用现有文件, 并跳过给定 kubeconfig 的生成阶段。
  3. 如果 kubeadm 以 ExternalCA 模式 运行,则所有必需的 kubeconfig 也必须由用户提供,因为 kubeadm 不能自己生成。
  4. 如果在 --dry-run 模式下执行 kubeadm,则 kubeconfig 文件将写入一个临时文件夹中。
  5. 可以使用 kubeadm init phase kubeconfig all 命令分别生成 kubeconfig 文件。

为控制平面组件生成静态 Pod 清单

Kubeadm 将用于控制平面组件的静态 Pod 清单文件写入 /etc/kubernetes/manifests 目录。 Kubelet 启动后会监视这个目录以便创建 Pod。

静态 Pod 清单有一些共同的属性:

  • 所有静态 Pod 都部署在 kube-system 名字空间

  • 所有静态 Pod 都打上 tier:control-planecomponent:{组件名称} 标签

  • 所有静态 Pod 均使用 system-node-critical 优先级

  • 所有静态 Pod 都设置了 hostNetwork:true,使得控制平面在配置网络之前启动;结果导致:

    • 控制器管理器和调度器用来调用 API 服务器的地址为 127.0.0.1
    • 如果使用本地 etcd 服务器,则 etcd-servers 地址将设置为 127.0.0.1:2379
  • 同时为控制器管理器和调度器启用了领导者选举
  • 控制器管理器和调度器将引用 kubeconfig 文件及其各自的唯一标识
  • 将自定义参数传递给控制平面组件 中所述,所有静态 Pod 都会获得用户指定的额外标志
  • 所有静态 Pod 都会获得用户指定的额外卷(主机路径)

请注意:

  1. 所有镜像默认从 registry.k8s.io 拉取。关于自定义镜像仓库, 请参阅使用自定义镜像
  2. 如果在 --dry-run 模式下执行 kubeadm,则静态 Pod 文件写入一个临时文件夹中。
  3. 可以使用 kubeadm init phase control-plane all 命令分别生成主控组件的静态 Pod 清单。

API 服务器

API 服务器的静态 Pod 清单会受到用户提供的以下参数的影响:

  • 要绑定的 apiserver-advertise-addressapiserver-bind-port; 如果未提供,则这些值默认为机器上默认网络接口的 IP 地址和 6443 端口。
  • service-cluster-ip-range 给 service 使用
  • 如果指定了外部 etcd 服务器,则应指定 etcd-servers 地址和相关的 TLS 设置 (etcd-cafileetcd-certfileetcd-keyfile); 如果未提供外部 etcd 服务器,则将使用本地 etcd(通过主机网络)
  • 如果指定了云提供商,则配置相应的 --cloud-provider,如果该路径存在,则配置 --cloud-config (这是实验性的,是 Alpha 版本,将在以后的版本中删除)

无条件设置的其他 API 服务器标志有:

  • --insecure-port=0 禁止到 API 服务器不安全的连接
  • --enable-bootstrap-token-auth=true 启用 BootstrapTokenAuthenticator 身份验证模块。 更多细节请参见 TLS 引导
  • --allow-privileged 设为 true(诸如 kube-proxy 这些组件有此要求)
  • --requestheader-client-ca-file 设为 front-proxy-ca.crt
  • --enable-admission-plugins 设为:

    • PersistentVolumeLabel 将区域(Region)或区(Zone)标签附加到由云提供商定义的 PersistentVolumes (此准入控制器已被弃用并将在以后的版本中删除)。 如果未明确选择使用 gceaws 作为云提供商,则默认情况下,v1.9 以后的版本 kubeadm 都不会部署。
  • --kubelet-preferred-address-types 设为 InternalIP,ExternalIP,Hostname; 这使得在节点的主机名无法解析的环境中,kubectl log 和 API 服务器与 kubelet 的其他通信可以工作
  • 使用在前面步骤中生成的证书的标志:

    • --client-ca-file 设为 ca.crt
    • --tls-cert-file 设为 apiserver.crt
    • --tls-private-key-file 设为 apiserver.key
    • --kubelet-client-certificate 设为 apiserver-kubelet-client.crt
    • --kubelet-client-key 设为 apiserver-kubelet-client.key
    • --service-account-key-file 设为 sa.pub
    • --requestheader-client-ca-file 设为 front-proxy-ca.crt
    • --proxy-client-cert-file 设为 front-proxy-client.crt
    • --proxy-client-key-file 设为 front-proxy-client.key
  • 其他用于保护前端代理( API 聚合层) 通信的标志:

    • --requestheader-username-headers=X-Remote-User
    • --requestheader-group-headers=X-Remote-Group
    • --requestheader-extra-headers-prefix=X-Remote-Extra-
    • --requestheader-allowed-names=front-proxy-client

控制器管理器

控制器管理器的静态 Pod 清单受用户提供的以下参数的影响:

  • 如果调用 kubeadm 时指定了 --pod-network-cidr 参数, 则可以通过以下方式启用某些 CNI 网络插件所需的子网管理器功能:

    • 设置 --allocate-node-cidrs=true
    • 根据给定 CIDR 设置 --cluster-cidr--node-cidr-mask-size 标志
  • 如果指定了云提供商,则指定相应的 --cloud-provider,如果存在这样的配置文件, 则指定 --cloud-config 路径(此为试验性功能,是 Alpha 版本,将在以后的版本中删除)。

其他无条件设置的标志包括:

  • --controllers 为 TLS 引导程序启用所有默认控制器以及 BootstrapSignerTokenCleaner 控制器。详细信息请参阅 TLS 引导

  • --use-service-account-credentials 设为 true

  • 使用先前步骤中生成的证书的标志:

    • --root-ca-file 设为 ca.crt
    • 如果禁用了 External CA 模式,则 --cluster-signing-cert-file 设为 ca.crt,否则设为 ""
    • 如果禁用了 External CA 模式,则 --cluster-signing-key-file 设为 ca.key,否则设为 ""
    • --service-account-private-key-file 设为 sa.key

调度器

调度器的静态 Pod 清单不受用户提供的参数的影响。

为本地 etcd 生成静态 Pod 清单

如果你指定的是外部 etcd,则应跳过此步骤,否则 kubeadm 会生成静态 Pod 清单文件, 以创建在 Pod 中运行的、具有以下属性的本地 etcd 实例:

  • localhost:2379 上监听并使用 HostNetwork=true
  • hostPathdataDir 挂载到主机的文件系统
  • 用户指定的任何其他标志

请注意:

  1. etcd 容器镜像默认从 registry.gcr.io 拉取。有关自定义镜像仓库, 请参阅使用自定义镜像
  2. 如果你以 --dry-run 模式执行 kubeadm 命令,etcd 的静态 Pod 清单将被写入一个临时文件夹。
  3. 你可以使用 'kubeadm init phase etcd local' 命令为本地 etcd 直接调用静态 Pod 清单生成逻辑。

等待控制平面启动

kubeadm 等待(最多 4m0s),直到 localhost:6443/healthz(kube-apiserver 存活)返回 ok。 但是为了检测死锁条件,如果 localhost:10255/healthz(kubelet 存活)或 localhost:10255/healthz/syncloop(kubelet 就绪)未能在 40s 和 60s 内未返回 ok, 则 kubeadm 会快速失败。

kubeadm 依靠 kubelet 拉取控制平面镜像并将其作为静态 Pod 正确运行。 控制平面启动后,kubeadm 将完成以下段落中描述的任务。

将 kubeadm ClusterConfiguration 保存在 ConfigMap 中以供以后参考

kubeadm 将传递给 kubeadm init 的配置保存在 kube-system 名字空间下名为 kubeadm-config 的 ConfigMap 中。

这将确保将来执行的 kubeadm 操作(例如 kubeadm upgrade)将能够确定实际/当前集群状态, 并根据该数据做出新的决策。

请注意:

  1. 在保存 ClusterConfiguration 之前,从配置中删除令牌等敏感信息。
  2. 可以使用 kubeadm init phase upload-config 命令单独上传主控节点配置。

将节点标记为控制平面

一旦控制平面可用,kubeadm 将执行以下操作:

  • 给节点打上 node-role.kubernetes.io/control-plane="" 标签,标记其为控制平面
  • 给节点打上 node-role.kubernetes.io/control-plane:NoSchedule 污点

请注意,标记控制面的这个阶段可以单独通过 kubeadm init phase mark-control-plane 命令来实现。

  • 给节点打上 node-role.kubernetes.io/master:NoSchedulenode-role.kubernetes.io/control-plane:NoSchedule 污点

请注意:

  1. node-role.kubernetes.io/master 污点是已废弃的,将会在 kubeadm 1.25 版本中移除
  2. 可以使用 kubeadm init phase mark-control-plane 命令单独触发控制平面标记

为即将加入的节点加入 TLS 启动引导

Kubeadm 使用引导令牌认证 将新节点连接到现有集群;更多的详细信息, 请参见设计提案

kubeadm init 确保为该过程正确配置了所有内容,这包括以下步骤以及设置 API 服务器和控制器标志,如前几段所述。

请注意:

  1. 可以使用 kubeadm init phase bootstrap-token 命令配置节点的 TLS 引导,执行以下段落中描述的所有配置步骤; 或者每个步骤都单独触发。

创建引导令牌

kubeadm init 创建第一个引导令牌,该令牌是自动生成的或由用户提供的 --token 标志的值;如引导令牌规范文档中所述,令牌应保存在 kube-system 名字空间下名为 bootstrap-token-<令牌 ID> 的 Secret 中。

请注意:

  1. kubeadm init 创建的默认令牌将用于在 TLS 引导过程中验证临时用户; 这些用户会成为 system:bootstrappers:kubeadm:default-node-token 组的成员。
  2. 令牌的有效期有限,默认为 24 小时(间隔可以通过 -token-ttl 标志进行更改)。
  3. 可以使用 kubeadm token 命令创建其他令牌,这些令牌还提供其他有用的令牌管理功能。

允许加入的节点调用 CSR API

Kubeadm 确保 system:bootstrappers:kubeadm:default-node-token 组中的用户能够访问证书签名 API。

这是通过在上述组与默认 RBAC 角色 system:node-bootstrapper 之间创建名为 kubeadm:kubelet-bootstrap 的 ClusterRoleBinding 来实现的。

为新的引导令牌设置自动批准

Kubeadm 确保 csrapprover 控制器自动批准引导令牌的 CSR 请求。

这是通过在 system:bootstrappers:kubeadm:default-node-token 用户组和 system:certificates.k8s.io:certificatesigningrequests:nodeclient 默认角色之间 创建名为 kubeadm:node-autoapprove-bootstrap 的 ClusterRoleBinding 来实现的。

还应创建 system:certificates.k8s.io:certificatesigningrequests:nodeclient 角色, 授予对 /apis/certificates.k8s.io/certificatesigningrequests/nodeclient 执行 POST 的权限。

通过自动批准设置节点证书轮换

Kubeadm 确保节点启用了证书轮换,csrapprover 控制器将自动批准节点的新证书的 CSR 请求。

这是通过在 system:nodes 组和 system:certificates.k8s.io:certificatesigningrequests:selfnodeclient 默认角色之间创建名为 kubeadm:node-autoapprove-certificate-rotation 的 ClusterRoleBinding 来实现的。

创建公共 cluster-info ConfigMap

本步骤在 kube-public 名字空间中创建名为 cluster-info 的 ConfigMap。

另外,它创建一个 Role 和一个 RoleBinding,为未经身份验证的用户授予对 ConfigMap 的访问权限(即 RBAC 组 system:unauthenticated 中的用户)。

请注意:

  1. cluster-info ConfigMap 的访问 不受 速率限制。 如果你把 API 服务器暴露到外网,这可能是一个问题,也可能不是; 这里最坏的情况是 DoS 攻击,攻击者使用 kube-apiserver 能够处理的所有动态请求来为 cluster-info ConfigMap 提供服务。

安装插件

Kubeadm 通过 API 服务器安装内部 DNS 服务器和 kube-proxy 插件。

请注意:

  1. 此步骤可以调用 'kubeadm init phase addon all' 命令单独执行。

代理

kube-system 名字空间中创建一个用于 kube-proxy 的 ServiceAccount; 然后以 DaemonSet 的方式部署 kube-proxy:

  • 主控节点凭据(ca.crttoken)来自 ServiceAccount
  • API 服务器节点的位置(URL)来自 ConfigMap
  • kube-proxy 的 ServiceAccount 绑定了 system:node-proxier ClusterRole 中的特权

DNS

  • CoreDNS 服务的名称为 kube-dns。这样做是为了防止当用户将集群 DNS 从 kube-dns 切换到 CoreDNS 时出现服务中断。--config 方法在 这里 有描述。

  • kube-system 名字空间中创建 CoreDNS 的 ServiceAccount

  • coredns 的 ServiceAccount 绑定了 system:coredns ClusterRole 中的特权

在 Kubernetes 1.21 版本中,kubeadm 对 kube-dns 的支持被移除。 你可以在 kubeadm 使用 CoreDNS,即使相关的 Service 名字仍然是 kube-dns

kubeadm join 步骤内部设计

kubeadm init 类似,kubeadm join 内部工作流由一系列待执行的原子工作任务组成。

这分为发现(让该节点信任 Kubernetes 的主控节点)和 TLS 引导 (让 Kubernetes 的主控节点信任该节点)。

请参阅使用引导令牌进行身份验证 或相应的设计提案

预检

kubeadm 在开始执行之前执行一组预检,目的是验证先决条件,避免常见的集群启动问题。

请注意:

  1. kubeadm join 预检基本上是 kubeadm init 预检的一个子集。
  2. 从 1.24 开始,kubeadm 使用 crictl 与所有已知的 CRI 端点进行通信。
  3. 从 1.9 开始,kubeadm 支持加入在 Windows 上运行的节点;在这种情况下, 将跳过 Linux 特定的控制参数。
  4. 在任何情况下,用户都可以通过 --ignore-preflight-errors 选项跳过特定的预检(或者进而跳过所有预检)。

发现 cluster-info

主要有两种发现方案。第一种是使用一个共享令牌以及 API 服务器的 IP 地址。 第二种是提供一个文件(它是标准 kubeconfig 文件的子集)。

共享令牌发现

如果带 --discovery-token 参数调用 kubeadm join,则使用了令牌发现功能; 在这种情况下,节点基本上从 kube-public 名字空间中的 cluster-info ConfigMap 中检索集群 CA 证书。

为了防止“中间人”攻击,采取了以下步骤:

  • 首先,通过不安全连接检索 CA 证书(这是可能的,因为 kubeadm init 授予 system:unauthenticated 的用户对 cluster-info 访问权限)。

  • 然后 CA 证书通过以下验证步骤:

    • 基本验证:使用令牌 ID 而不是 JWT 签名
    • 公钥验证:使用提供的 --discovery-token-ca-cert-hash。这个值来自 kubeadm init 的输出, 或者可以使用标准工具计算(哈希值是按 RFC7469 中主体公钥信息(SPKI)对象的字节计算的) --discovery-token-ca-cert-hash 标志可以重复多次,以允许多个公钥。
    • 作为附加验证,通过安全连接检索 CA 证书,然后与初始检索的 CA 进行比较。

请注意:

  1. 通过 --discovery-token-unsafe-skip-ca-verification 标志可以跳过公钥验证; 这削弱了 kubeadm 安全模型,因为其他人可能冒充 Kubernetes 主控节点。

文件/HTTPS 发现

如果带 --discovery-file 参数调用 kubeadm join,则使用文件发现功能; 该文件可以是本地文件或通过 HTTPS URL 下载;对于 HTTPS,主机安装的 CA 包用于验证连接。

通过文件发现,集群 CA 证书是文件本身提供;事实上,这个发现文件是一个 kubeconfig 文件, 只设置了 servercertificate-authority-data 属性, 如 kubeadm join 参考文档中所述,当与集群建立连接时,kubeadm 尝试访问 cluster-info ConfigMap, 如果可用,就使用它。

TLS 引导

知道集群信息后,kubeadm 将写入文件 bootstrap-kubelet.conf,从而允许 kubelet 执行 TLS 引导。

TLS 引导机制使用共享令牌对 Kubernetes API 服务器进行临时身份验证, 以便为本地创建的密钥对提交证书签名请求(CSR)。

该请求会被自动批准,并且该操作保存 ca.crt 文件和 kubelet.conf 文件,用于 kubelet 加入集群,同时删除 bootstrap-kubelet.conf

请注意:

  • 临时身份验证根据 kubeadm init 过程中保存的令牌进行验证(或者使用 kubeadm token 创建的其他令牌)
  • 临时身份验证解析到 system:bootstrappers:kubeadm:default-node-token 组的一个用户成员, 该成员在 kubeadm init 过程中被授予对 CSR API 的访问权
  • 根据 kubeadm init 过程的配置,自动 CSR 审批由 csrapprover 控制器管理

6.11 - 命令行工具 (kubectl)

Kubernetes 提供 kubectl 是使用 Kubernetes API 与 Kubernetes 集群的控制面进行通信的命令行工具。

这个工具叫做 kubectl

针对配置信息,kubectl$HOME/.kube 目录中查找一个名为 config 的配置文件。 你可以通过设置 KUBECONFIG 环境变量或设置 --kubeconfig

参数来指定其它 kubeconfig 文件。

本文概述了 kubectl 语法和命令操作描述,并提供了常见的示例。 有关每个命令的详细信息,包括所有受支持的参数和子命令, 请参阅 kubectl 参考文档。

有关安装说明,请参见安装 kubectl; 如需快速指南,请参见备忘单。 如果你更习惯使用 docker 命令行工具, Docker 用户的 kubectl 介绍了一些 Kubernetes 的等价命令。

语法

使用以下语法从终端窗口运行 kubectl 命令:

kubectl [command] [TYPE] [NAME] [flags]

其中 commandTYPENAMEflags 分别是:

  • command:指定要对一个或多个资源执行的操作,例如 creategetdescribedelete

  • TYPE:指定资源类型。资源类型不区分大小写, 可以指定单数、复数或缩写形式。例如,以下命令输出相同的结果:

    kubectl get pod pod1
    kubectl get pods pod1
    kubectl get po pod1
    
  • NAME:指定资源的名称。名称区分大小写。 如果省略名称,则显示所有资源的详细信息。例如:kubectl get pods

    在对多个资源执行操作时,你可以按类型和名称指定每个资源,或指定一个或多个文件:

  • 要按类型和名称指定资源:

  • 要对所有类型相同的资源进行分组,请执行以下操作:TYPE1 name1 name2 name<#>
    例子:kubectl get pod example-pod1 example-pod2

  • 分别指定多个资源类型:TYPE1/name1 TYPE1/name2 TYPE2/name3 TYPE<#>/name<#>
    例子:kubectl get pod/example-pod1 replicationcontroller/example-rc1

  • 用一个或多个文件指定资源:-f file1 -f file2 -f file<#>

  • 使用 YAML 而不是 JSON, 因为 YAML 对用户更友好, 特别是对于配置文件。
    例子:kubectl get -f ./pod.yaml

  • flags: 指定可选的参数。例如,可以使用 -s--server 参数指定 Kubernetes API 服务器的地址和端口。

如果你需要帮助,在终端窗口中运行 kubectl help

集群内身份验证和命名空间覆盖

默认情况下,kubectl 命令首先确定它是否在 Pod 中运行,从而被视为在集群中运行。 它首先检查 KUBERNETES_SERVICE_HOSTKUBERNETES_SERVICE_PORT 环境变量以及 /var/run/secrets/kubernetes.io/serviceaccount/token 中是否存在服务帐户令牌文件。 如果三个条件都被满足,则假定在集群内进行身份验证。

为保持向后兼容性,如果在集群内身份验证期间设置了 POD_NAMESPACE 环境变量,它将覆盖服务帐户令牌中的默认命名空间。 任何依赖默认命名空间的清单或工具都会受到影响。

POD_NAMESPACE 环境变量

如果设置了 POD_NAMESPACE 环境变量,对命名空间资源的 CLI 操作对象将使用该变量值作为默认值。 例如,如果该变量设置为 seattlekubectl get pods 将返回 seattle 命名空间中的 Pod。 这是因为 Pod 是一个命名空间资源,且命令中没有提供命名空间。

直接使用 --namespace <value> 会覆盖此行为。

kubectl 如何处理 ServiceAccount 令牌

假设:

  • 有 Kubernetes 服务帐户令牌文件挂载在 /var/run/secrets/kubernetes.io/serviceaccount/token 上,并且
  • 设置了 KUBERNETES_SERVICE_HOST 环境变量,并且
  • 设置了 KUBERNETES_SERVICE_PORT 环境变量,并且
  • 你没有在 kubectl 命令行上明确指定命名空间。

然后 kubectl 假定它正在你的集群中运行。 kubectl 工具查找该 ServiceAccount 的命名空间 (该命名空间与 Pod 的命名空间相同)并针对该命名空间进行操作。 这与集群外运行的情况不同; 当 kubectl 在集群外运行并且你没有指定命名空间时, kubectl 命令会针对你在客户端配置中为当前上下文设置的命名空间进行操作。 要为你的 kubectl 更改默认的命名空间,你可以使用以下命令:

kubectl config set-context --current --namespace=<namespace-name>

操作

下表包含所有 kubectl 操作的简短描述和普通语法:

操作语法描述
alphakubectl alpha SUBCOMMAND [flags]列出与 alpha 特性对应的可用命令,这些特性在 Kubernetes 集群中默认情况下是不启用的。
annotatekubectl annotate (-f FILENAME | TYPE NAME | TYPE/NAME) KEY_1=VAL_1 ... KEY_N=VAL_N [--overwrite] [--all] [--resource-version=version] [flags]添加或更新一个或多个资源的注解。
api-resourceskubectl api-resources [flags]列出可用的 API 资源。
api-versionskubectl api-versions [flags]列出可用的 API 版本。
applykubectl apply -f FILENAME [flags]从文件或 stdin 对资源应用配置更改。
attachkubectl attach POD -c CONTAINER [-i] [-t] [flags]挂接到正在运行的容器,查看输出流或与容器(stdin)交互。
authkubectl auth [flags] [options]检查授权。
autoscalekubectl autoscale (-f FILENAME | TYPE NAME | TYPE/NAME) [--min=MINPODS] --max=MAXPODS [--cpu-percent=CPU] [flags]自动扩缩由副本控制器管理的一组 pod。
certificatekubectl certificate SUBCOMMAND [options]修改证书资源。
cluster-infokubectl cluster-info [flags]显示有关集群中主服务器和服务的端口信息。
completionkubectl completion SHELL [options]为指定的 Shell(Bash 或 Zsh)输出 Shell 补齐代码。
configkubectl config SUBCOMMAND [flags]修改 kubeconfig 文件。有关详细信息,请参阅各个子命令。
convertkubectl convert -f FILENAME [options]在不同的 API 版本之间转换配置文件。配置文件可以是 YAML 或 JSON 格式。注意 - 需要安装 kubectl-convert 插件。
cordonkubectl cordon NODE [options]将节点标记为不可调度。
cpkubectl cp <file-spec-src> <file-spec-dest> [options]从容器复制文件、目录或将文件、目录复制到容器。
createkubectl create -f FILENAME [flags]从文件或 stdin 创建一个或多个资源。
deletekubectl delete (-f FILENAME | TYPE [NAME | /NAME | -l label | --all]) [flags]基于文件、标准输入或通过指定标签选择器、名称、资源选择器或资源本身,删除资源。
describekubectl describe (-f FILENAME | TYPE [NAME_PREFIX | /NAME | -l label]) [flags]显示一个或多个资源的详细状态。
diffkubectl diff -f FILENAME [flags]在当前起作用的配置和文件或标准输之间作对比 (BETA)
drainkubectl drain NODE [options]腾空节点以准备维护。
editkubectl edit (-f FILENAME | TYPE NAME | TYPE/NAME) [flags]使用默认编辑器编辑和更新服务器上一个或多个资源的定义。
eventskubectl events列举事件。
execkubectl exec POD [-c CONTAINER] [-i] [-t] [flags] [-- COMMAND [args...]]对 Pod 中的容器执行命令。
explainkubectl explain TYPE [--recursive=false] [flags]获取多种资源的文档。例如 Pod、Node、Service 等。
exposekubectl expose (-f FILENAME | TYPE NAME | TYPE/NAME) [--port=port] [--protocol=TCP|UDP] [--target-port=number-or-name] [--name=name] [--external-ip=external-ip-of-service] [--type=type] [flags]将副本控制器、Service 或 Pod 作为新的 Kubernetes 服务暴露。
getkubectl get (-f FILENAME | TYPE [NAME | /NAME | -l label]) [--watch] [--sort-by=FIELD] [[-o | --output]=OUTPUT_FORMAT] [flags]列出一个或多个资源。
kustomizekubectl kustomize[flags] [options]`列出从 kustomization.yaml 文件中的指令生成的一组 API 资源。参数必须是包含文件的目录的路径,或者是 git 存储库 URL,其路径后缀相对于存储库根目录指定了相同的路径。
labelkubectl label (-f FILENAME | TYPE NAME | TYPE/NAME) KEY_1=VAL_1 ... KEY_N=VAL_N [--overwrite] [--all] [--resource-version=version] [flags]添加或更新一个或多个资源的标签。
logskubectl logs POD [-c CONTAINER] [--follow] [flags]打印 Pod 中容器的日志。
optionskubectl options全局命令行选项列表,这些选项适用于所有命令。
patchkubectl patch (-f FILENAME | TYPE NAME | TYPE/NAME) --patch PATCH [flags]使用策略合并流程更新资源的一个或多个字段。
pluginkubectl plugin [flags] [options]提供用于与插件交互的实用程序。
port-forwardkubectl port-forward POD [LOCAL_PORT:]REMOTE_PORT [...[LOCAL_PORT_N:]REMOTE_PORT_N] [flags]将一个或多个本地端口转发到一个 Pod。
proxykubectl proxy [--port=PORT] [--www=static-dir] [--www-prefix=prefix] [--api-prefix=prefix] [flags]运行访问 Kubernetes API 服务器的代理。
replacekubectl replace -f FILENAME基于文件或标准输入替换资源。
rolloutkubectl rollout SUBCOMMAND [options]管理资源的上线。有效的资源类型包括:Deployment、 DaemonSet 和 StatefulSet。
runkubectl run NAME --image=image [--env="key=value"] [--port=port] [--dry-run=server | client | none] [--overrides=inline-json] [flags]在集群上运行指定的镜像。
scalekubectl scale (-f FILENAME | TYPE NAME | TYPE/NAME) --replicas=COUNT [--resource-version=version] [--current-replicas=count] [flags]更新指定副本控制器的大小。
setkubectl set SUBCOMMAND [options]配置应用资源。
taintkubectl taint NODE NAME KEY_1=VAL_1:TAINT_EFFECT_1 ... KEY_N=VAL_N:TAINT_EFFECT_N [options]更新一个或多个节点上的污点。
top`kubectl top (PODNODE) [flags] [options]`
uncordonkubectl uncordon NODE [options]将节点标记为可调度。
versionkubectl version [--client] [flags]显示运行在客户端和服务器上的 Kubernetes 版本。
waitkubectl wait ([-f FILENAME] | resource.group/resource.name | resource.group [(-l label | --all)]) [--for=delete|--for condition=available] [options]实验特性:等待一种或多种资源的特定状况。

了解更多有关命令操作的信息, 请参阅 kubectl 参考文档。

资源类型

下表列出所有受支持的资源类型及其缩写别名。

(以下输出可以通过 kubectl api-resources 获取,内容以 Kubernetes 1.25.0 版本为准。)

资源名缩写名API 版本按命名空间资源类型
bindingsv1trueBinding
componentstatusescsv1falseComponentStatus
configmapscmv1trueConfigMap
endpointsepv1trueEndpoints
eventsevv1trueEvent
limitrangeslimitsv1trueLimitRange
namespacesnsv1falseNamespace
nodesnov1falseNode
persistentvolumeclaimspvcv1truePersistentVolumeClaim
persistentvolumespvv1falsePersistentVolume
podspov1truePod
podtemplatesv1truePodTemplate
replicationcontrollersrcv1trueReplicationController
resourcequotasquotav1trueResourceQuota
secretsv1trueSecret
serviceaccountssav1trueServiceAccount
servicessvcv1trueService
mutatingwebhookconfigurationsadmissionregistration.k8s.io/v1falseMutatingWebhookConfiguration
validatingwebhookconfigurationsadmissionregistration.k8s.io/v1falseValidatingWebhookConfiguration
customresourcedefinitionscrd,crdsapiextensions.k8s.io/v1falseCustomResourceDefinition
apiservicesapiregistration.k8s.io/v1falseAPIService
controllerrevisionsapps/v1trueControllerRevision
daemonsetsdsapps/v1trueDaemonSet
deploymentsdeployapps/v1trueDeployment
replicasetsrsapps/v1trueReplicaSet
statefulsetsstsapps/v1trueStatefulSet
tokenreviewsauthentication.k8s.io/v1falseTokenReview
localsubjectaccessreviewsauthorization.k8s.io/v1trueLocalSubjectAccessReview
selfsubjectaccessreviewsauthorization.k8s.io/v1falseSelfSubjectAccessReview
selfsubjectrulesreviewsauthorization.k8s.io/v1falseSelfSubjectRulesReview
subjectaccessreviewsauthorization.k8s.io/v1falseSubjectAccessReview
horizontalpodautoscalershpaautoscaling/v2trueHorizontalPodAutoscaler
cronjobscjbatch/v1trueCronJob
jobsbatch/v1trueJob
certificatesigningrequestscsrcertificates.k8s.io/v1falseCertificateSigningRequest
leasescoordination.k8s.io/v1trueLease
endpointslicesdiscovery.k8s.io/v1trueEndpointSlice
eventsevevents.k8s.io/v1trueEvent
flowschemasflowcontrol.apiserver.k8s.io/v1beta2falseFlowSchema
prioritylevelconfigurationsflowcontrol.apiserver.k8s.io/v1beta2falsePriorityLevelConfiguration
ingressclassesnetworking.k8s.io/v1falseIngressClass
ingressesingnetworking.k8s.io/v1trueIngress
networkpoliciesnetpolnetworking.k8s.io/v1trueNetworkPolicy
runtimeclassesnode.k8s.io/v1falseRuntimeClass
poddisruptionbudgetspdbpolicy/v1truePodDisruptionBudget
podsecuritypoliciespsppolicy/v1beta1falsePodSecurityPolicy
clusterrolebindingsrbac.authorization.k8s.io/v1falseClusterRoleBinding
clusterrolesrbac.authorization.k8s.io/v1falseClusterRole
rolebindingsrbac.authorization.k8s.io/v1trueRoleBinding
rolesrbac.authorization.k8s.io/v1trueRole
priorityclassespcscheduling.k8s.io/v1falsePriorityClass
csidriversstorage.k8s.io/v1falseCSIDriver
csinodesstorage.k8s.io/v1falseCSINode
csistoragecapacitiesstorage.k8s.io/v1trueCSIStorageCapacity
storageclassesscstorage.k8s.io/v1falseStorageClass
volumeattachmentsstorage.k8s.io/v1falseVolumeAttachment

输出选项

有关如何格式化或排序某些命令的输出的信息,请参阅以下章节。有关哪些命令支持不同输出选项的详细信息, 请参阅 kubectl 参考文档。

格式化输出

所有 kubectl 命令的默认输出格式都是人类可读的纯文本格式。要以特定格式在终端窗口输出详细信息, 可以将 -o--output 参数添加到受支持的 kubectl 命令中。

语法

kubectl [command] [TYPE] [NAME] -o <output_format>

取决于具体的 kubectl 操作,支持的输出格式如下:

输出格式描述
-o custom-columns=<spec>使用逗号分隔的自定义列列表打印表。
-o custom-columns-file=<filename>使用 <filename> 文件中的自定义列模板打印表。
-o json输出 JSON 格式的 API 对象
-o jsonpath=<template>打印 jsonpath 表达式定义的字段
-o jsonpath-file=<filename>打印 <filename> 文件中 jsonpath 表达式定义的字段。
-o name仅打印资源名称而不打印任何其他内容。
-o wide以纯文本格式输出,包含所有附加信息。对于 Pod 包含节点名。
-o yaml输出 YAML 格式的 API 对象。
示例

在此示例中,以下命令将单个 Pod 的详细信息输出为 YAML 格式的对象:

kubectl get pod web-pod-13je7 -o yaml

请记住:有关每个命令支持哪种输出格式的详细信息, 请参阅 kubectl 参考文档。

自定义列

要定义自定义列并仅将所需的详细信息输出到表中,可以使用 custom-columns 选项。 你可以选择内联定义自定义列或使用模板文件:-o custom-columns=<spec>-o custom-columns-file=<filename>

示例

内联:

kubectl get pods <pod-name> -o custom-columns=NAME:.metadata.name,RSRC:.metadata.resourceVersion

模板文件:

kubectl get pods <pod-name> -o custom-columns-file=template.txt

其中,template.txt 文件包含:

NAME          RSRC
metadata.name metadata.resourceVersion

运行这两个命令之一的结果类似于:

NAME           RSRC
submit-queue   610995

Server-side 列

kubectl 支持从服务器接收关于对象的特定列信息。 这意味着对于任何给定的资源,服务器将返回与该资源相关的列和行,以便客户端打印。 通过让服务器封装打印的细节,这允许在针对同一集群使用的客户端之间提供一致的人类可读输出。

此功能默认启用。要禁用它,请将该 --server-print=false 参数添加到 kubectl get 命令中。

例子

要打印有关 Pod 状态的信息,请使用如下命令:

kubectl get pods <pod-name> --server-print=false

输出类似于:

NAME       AGE
pod-name   1m

排序列表对象

要将对象排序后输出到终端窗口,可以将 --sort-by 参数添加到支持的 kubectl 命令。 通过使用 --sort-by 参数指定任何数字或字符串字段来对对象进行排序。 要指定字段,请使用 jsonpath 表达式。

语法

kubectl [command] [TYPE] [NAME] --sort-by=<jsonpath_exp>
示例

要打印按名称排序的 Pod 列表,请运行:

kubectl get pods --sort-by=.metadata.name

示例:常用操作

使用以下示例集来帮助你熟悉运行常用 kubectl 操作:

kubectl apply - 以文件或标准输入为准应用或更新资源。

# 使用 example-service.yaml 中的定义创建 Service。
kubectl apply -f example-service.yaml

# 使用 example-controller.yaml 中的定义创建 replication controller。
kubectl apply -f example-controller.yaml

# 使用 <directory> 路径下的任意 .yaml、.yml 或 .json 文件 创建对象。
kubectl apply -f <directory>

kubectl get - 列出一个或多个资源。

# 以纯文本输出格式列出所有 Pod。
kubectl get pods

# 以纯文本输出格式列出所有 Pod,并包含附加信息(如节点名)。
kubectl get pods -o wide

# 以纯文本输出格式列出具有指定名称的副本控制器。提示:你可以使用别名 'rc' 缩短和替换 'replicationcontroller' 资源类型。
kubectl get replicationcontroller <rc-name>

# 以纯文本输出格式列出所有副本控制器和 Service。
kubectl get rc,services

# 以纯文本输出格式列出所有守护程序集,包括未初始化的守护程序集。
kubectl get ds --include-uninitialized

# 列出在节点 server01 上运行的所有 Pod
kubectl get pods --field-selector=spec.nodeName=server01

kubectl describe - 显示一个或多个资源的详细状态,默认情况下包括未初始化的资源。

# 显示名为 <pod-name> 的 Pod 的详细信息。
kubectl describe nodes <node-name>

# 显示名为 <pod-name> 的 Pod 的详细信息。
kubectl describe pods/<pod-name>

# 显示由名为 <rc-name> 的副本控制器管理的所有 Pod 的详细信息。
# 记住:副本控制器创建的任何 Pod 都以副本控制器的名称为前缀。
kubectl describe pods <rc-name>

# 描述所有的 Pod
kubectl describe pods

kubectl delete - 基于文件、标准输入或通过指定标签选择器、名称、资源选择器或资源来删除资源。

# 使用 pod.yaml 文件中指定的类型和名称删除 Pod。
kubectl delete -f pod.yaml

# 删除所有带有 '<label-key>=<label-value>' 标签的 Pod 和 Service。
kubectl delete pods,services -l <label-key>=<label-value>

# 删除所有 Pod,包括未初始化的 Pod。
kubectl delete pods --all

kubectl exec - 对 Pod 中的容器执行命令。

# 从 Pod <pod-name> 中获取运行 'date' 的输出。默认情况下,输出来自第一个容器。
kubectl exec <pod-name> -- date

# 运行输出 'date' 获取在 Pod <pod-name> 中容器 <container-name> 的输出。
kubectl exec <pod-name> -c <container-name> -- date

# 获取一个交互 TTY 并在 Pod  <pod-name> 中运行 /bin/bash。默认情况下,输出来自第一个容器。
kubectl exec -ti <pod-name> -- /bin/bash

kubectl logs - 打印 Pod 中容器的日志。

# 返回 Pod <pod-name> 的日志快照。
kubectl logs <pod-name>

# 从 Pod <pod-name> 开始流式传输日志。这类似于 'tail -f' Linux 命令。
kubectl logs -f <pod-name>

kubectl diff - 查看集群建议更新的差异。

# “pod.json” 中包含的差异资源。
kubectl diff -f pod.json

# 从标准输入读取的差异文件。
cat service.yaml | kubectl diff -f -

示例:创建和使用插件

使用以下示例来帮助你熟悉编写和使用 kubectl 插件:

# 用任何语言创建一个简单的插件,并为生成的可执行文件命名
# 以前缀 "kubectl-" 开始
cat ./kubectl-hello
#!/bin/sh

# 这个插件打印单词 "hello world"
echo "hello world"

这个插件写好了,把它变成可执行的:

sudo chmod a+x ./kubectl-hello

# 并将其移动到路径中的某个位置
sudo mv ./kubectl-hello /usr/local/bin
sudo chown root:root /usr/local/bin

# 你现在已经创建并"安装了"一个 kubectl 插件。
# 你可以开始使用这个插件,从 kubectl 调用它,就像它是一个常规命令一样
kubectl hello
hello world
# 你可以"卸载"一个插件,只需从你的 $PATH 中删除它
sudo rm /usr/local/bin/kubectl-hello

为了查看可用的所有 kubectl 插件,你可以使用 kubectl plugin list 子命令:

kubectl plugin list

输出类似于:

The following kubectl-compatible plugins are available:

/usr/local/bin/kubectl-hello
/usr/local/bin/kubectl-foo
/usr/local/bin/kubectl-bar

kubectl plugin list 指令也可以向你告警哪些插件被运行,或是被其它插件覆盖了,例如:

sudo chmod -x /usr/local/bin/kubectl-foo # 删除执行权限
kubectl plugin list
The following kubectl-compatible plugins are available:

/usr/local/bin/kubectl-hello
/usr/local/bin/kubectl-foo
  - warning: /usr/local/bin/kubectl-foo identified as a plugin, but it is not executable
/usr/local/bin/kubectl-bar

error: one plugin warning was found

你可以将插件视为在现有 kubectl 命令之上构建更复杂功能的一种方法:

cat ./kubectl-whoami

接下来的几个示例假设你已经将 kubectl-whoami 设置为以下内容:

#!/bin/bash

#这个插件利用 `kubectl config` 命令基于当前所选上下文输出当前用户的信息
kubectl config view --template='{{ range .contexts }}{{ if eq .name "'$(kubectl config current-context)'" }}Current user: {{ printf "%s\n" .context.user }}{{ end }}{{ end }}'

运行以上命令将为你提供一个输出,其中包含 KUBECONFIG 文件中当前上下文的用户:

#!/bin/bash
# 使文件成为可执行的
sudo chmod +x ./kubectl-whoami

# 然后移动到你的路径中
sudo mv ./kubectl-whoami /usr/local/bin

kubectl whoami
Current user: plugins-user

接下来

6.11.1 - kubectl 快速参考

本页列举常用的 kubectl 命令和参数。

Kubectl 自动补全

BASH

source <(kubectl completion bash) # 在 bash 中设置当前 shell 的自动补全,要先安装 bash-completion 包
echo "source <(kubectl completion bash)" >> ~/.bashrc # 在你的 bash shell 中永久地添加自动补全

你还可以在补全时为 kubectl 使用一个速记别名:

alias k=kubectl
complete -o default -F __start_kubectl k

ZSH

source <(kubectl completion zsh)  # 在 zsh 中设置当前 shell 的自动补全
echo '[[ $commands[kubectl] ]] && source <(kubectl completion zsh)' >> ~/.zshrc # 在你的 zsh shell 中永久地添加自动补全

FISH

需要 kubectl 版本 1.23 或更高版本。

echo 'kubectl completion fish | source' >> ~/.config/fish/config.fish  # 将 kubectl 自动补全永久添加到你的 Fish shell 中

关于 --all-namespaces 的一点说明

我们经常用到 --all-namespaces 参数,你应该要知道它的简写:

kubectl -A

Kubectl 上下文和配置

设置 kubectl 与哪个 Kubernetes 集群进行通信并修改配置信息。 查看使用 kubeconfig 跨集群授权访问 文档获取配置文件详细信息。

kubectl config view # 显示合并的 kubeconfig 配置

# 同时使用多个 kubeconfig 文件并查看合并的配置
KUBECONFIG=~/.kube/config:~/.kube/kubconfig2

kubectl config view

# 显示合并的 kubeconfig 配置和原始证书数据以及公开的 Secret
kubectl config view --raw

# 获取 e2e 用户的密码
kubectl config view -o jsonpath='{.users[?(@.name == "e2e")].user.password}'

# 获取 e2e 用户的证书
kubectl config view --raw -o jsonpath='{.users[?(.name == "e2e")].user.client-certificate-data}' | base64 -d

kubectl config view -o jsonpath='{.users[].name}'    # 显示第一个用户
kubectl config view -o jsonpath='{.users[*].name}'   # 获取用户列表
kubectl config get-contexts                          # 显示上下文列表
kubectl config get-contexts -o name                  # 获取所有上下文的名称
kubectl config current-context                       # 展示当前所处的上下文
kubectl config use-context my-cluster-name           # 设置默认的上下文为 my-cluster-name

kubectl config set-cluster my-cluster-name           # 在 kubeconfig 中设置集群条目

# 在 kubeconfig 中配置代理服务器的 URL,以用于该客户端的请求
kubectl config set-cluster my-cluster-name --proxy-url=my-proxy-url

# 添加新的用户配置到 kubeconf 中,使用 basic auth 进行身份认证
kubectl config set-credentials kubeuser/foo.kubernetes.com --username=kubeuser --password=kubepassword

# 在指定上下文中持久性地保存名字空间,供所有后续 kubectl 命令使用
kubectl config set-context --current --namespace=ggckad-s2

# 使用特定的用户名和名字空间设置上下文
kubectl config set-context gce --user=cluster-admin --namespace=foo \
  && kubectl config use-context gce

kubectl config unset users.foo                       # 删除用户 foo

# 设置或显示 context / namespace 的短别名
# (仅适用于 bash 和 bash 兼容的 shell,在使用 kn 设置命名空间之前要先设置 current-context)
alias kx='f() { [ "$1" ] && kubectl config use-context $1 || kubectl config current-context ; } ; f'
alias kn='f() { [ "$1" ] && kubectl config set-context --current --namespace $1 || kubectl config view --minify | grep namespace | cut -d" " -f6 ; } ; f'

Kubectl apply

apply 通过定义 Kubernetes 资源的文件来管理应用。 它通过运行 kubectl apply 在集群中创建和更新资源。 这是在生产中管理 Kubernetes 应用的推荐方法。 参见 Kubectl 文档

创建对象

Kubernetes 配置可以用 YAML 或 JSON 定义。可以使用的文件扩展名有 .yaml.yml.json

kubectl apply -f ./my-manifest.yaml                  # 创建资源
kubectl apply -f ./my1.yaml -f ./my2.yaml            # 使用多个文件创建
kubectl apply -f ./dir                               # 基于目录下的所有清单文件创建资源
kubectl apply -f https://example.com/manifest.yaml   # 从 URL 中创建资源(注意:这是一个示例域名,不包含有效的清单)
kubectl create deployment nginx --image=nginx        # 启动单实例 nginx

# 创建一个打印 “Hello World” 的 Job
kubectl create job hello --image=busybox:1.28 -- echo "Hello World" 

# 创建一个打印 “Hello World” 间隔 1 分钟的 CronJob
kubectl create cronjob hello --image=busybox:1.28   --schedule="*/1 * * * *" -- echo "Hello World"    

kubectl explain pods                          # 获取 Pod 清单的文档说明

# 从标准输入创建多个 YAML 对象
kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: busybox-sleep
spec:
  containers:
  - name: busybox
    image: busybox:1.28
    args:
    - sleep
    - "1000000"
---
apiVersion: v1
kind: Pod
metadata:
  name: busybox-sleep-less
spec:
  containers:
  - name: busybox
    image: busybox:1.28
    args:
    - sleep
    - "1000"
EOF

# 创建有多个 key 的 Secret
kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  password: $(echo -n "s33msi4" | base64 -w0)
  username: $(echo -n "jane" | base64 -w0)
EOF

查看和查找资源

# get 命令的基本输出
kubectl get services                          # 列出当前命名空间下的所有 Service
kubectl get pods --all-namespaces             # 列出所有命名空间下的全部的 Pod
kubectl get pods -o wide                      # 列出当前命名空间下的全部 Pod 并显示更详细的信息
kubectl get deployment my-dep                 # 列出某个特定的 Deployment
kubectl get pods                              # 列出当前命名空间下的全部 Pod
kubectl get pod my-pod -o yaml                # 获取一个 Pod 的 YAML

# describe 命令的详细输出
kubectl describe nodes my-node
kubectl describe pods my-pod

# 列出当前名字空间下所有 Service,按名称排序
kubectl get services --sort-by=.metadata.name

# 列出 Pod,按重启次数排序
kubectl get pods --sort-by='.status.containerStatuses[0].restartCount'

# 列举所有 PV 持久卷,按容量排序
kubectl get pv --sort-by=.spec.capacity.storage

# 获取包含 app=cassandra 标签的所有 Pod 的 version 标签
kubectl get pods --selector=app=cassandra -o \
  jsonpath='{.items[*].metadata.labels.version}'

# 检索带有 “.” 键值,例如 'ca.crt'
kubectl get configmap myconfig \
  -o jsonpath='{.data.ca\.crt}'

# 检索一个 base64 编码的值,其中的键名应该包含减号而不是下划线
kubectl get secret my-secret --template='{{index .data "key-name-with-dashes"}}'

# 获取所有工作节点(使用选择算符以排除标签名称为 'node-role.kubernetes.io/control-plane' 的结果)
kubectl get node --selector='!node-role.kubernetes.io/control-plane'

# 获取当前命名空间中正在运行的 Pod
kubectl get pods --field-selector=status.phase=Running

# 获取全部节点的 ExternalIP 地址
kubectl get nodes -o jsonpath='{.items[*].status.addresses[?(@.type=="ExternalIP")].address}'

# 列出属于某个特定 RC 的 Pod 的名称
# 在转换对于 jsonpath 过于复杂的场合,"jq" 命令很有用;可以在 https://jqlang.github.io/jq/ 找到它
sel=${$(kubectl get rc my-rc --output=json | jq -j '.spec.selector | to_entries | .[] | "\(.key)=\(.value),"')%?}
echo $(kubectl get pods --selector=$sel --output=jsonpath={.items..metadata.name})

# 显示所有 Pod 的标签(或任何其他支持标签的 Kubernetes 对象)
kubectl get pods --show-labels

# 检查哪些节点处于就绪状态
JSONPATH='{range .items[*]}{@.metadata.name}:{range @.status.conditions[*]}{@.type}={@.status};{end}{end}' \
 && kubectl get nodes -o jsonpath="$JSONPATH" | grep "Ready=True"
 
# 使用自定义列检查哪些节点处于就绪状态
kubectl get node -o custom-columns='NODE_NAME:.metadata.name,STATUS:.status.conditions[?(@.type=="Ready")].status'

# 不使用外部工具来输出解码后的 Secret
kubectl get secret my-secret -o go-template='{{range $k,$v := .data}}{{"### "}}{{$k}}{{"\n"}}{{$v|base64decode}}{{"\n\n"}}{{end}}'

# 列出被一个 Pod 使用的全部 Secret
kubectl get pods -o json | jq '.items[].spec.containers[].env[]?.valueFrom.secretKeyRef.name' | grep -v null | sort | uniq

# 列举所有 Pod 中初始化容器的容器 ID(containerID)
# 可用于在清理已停止的容器时避免删除初始化容器
kubectl get pods --all-namespaces -o jsonpath='{range .items[*].status.initContainerStatuses[*]}{.containerID}{"\n"}{end}' | cut -d/ -f3

# 列出事件(Event),按时间戳排序
kubectl get events --sort-by=.metadata.creationTimestamp

# 列出所有警告事件
kubectl events --types=Warning

# 比较当前的集群状态和假定某清单被应用之后的集群状态
kubectl diff -f ./my-manifest.yaml

# 生成一个句点分隔的树,其中包含为节点返回的所有键
# 在复杂的嵌套JSON结构中定位键时非常有用
kubectl get nodes -o json | jq -c 'paths|join(".")'

# 生成一个句点分隔的树,其中包含为 Pod 等返回的所有键
kubectl get pods -o json | jq -c 'paths|join(".")'

# 假设你的 Pod 有默认的容器和默认的名字空间,并且支持 'env' 命令,可以使用以下脚本为所有 Pod 生成 ENV 变量。
# 该脚本也可用于在所有的 Pod 里运行任何受支持的命令,而不仅仅是 'env'。
for pod in $(kubectl get po --output=jsonpath={.items..metadata.name}); do echo $pod && kubectl exec -it $pod -- env; done

# 获取一个 Deployment 的 status 子资源
kubectl get deployment nginx-deployment --subresource=status

更新资源

kubectl set image deployment/frontend www=image:v2               # 滚动更新 "frontend" Deployment 的 "www" 容器镜像
kubectl rollout history deployment/frontend                      # 检查 Deployment 的历史记录,包括版本
kubectl rollout undo deployment/frontend                         # 回滚到上次部署版本
kubectl rollout undo deployment/frontend --to-revision=2         # 回滚到特定部署版本
kubectl rollout status -w deployment/frontend                    # 监视 "frontend" Deployment 的滚动升级状态直到完成
kubectl rollout restart deployment/frontend                      # 轮替重启 "frontend" Deployment

cat pod.json | kubectl replace -f -                              # 通过传入到标准输入的 JSON 来替换 Pod

# 强制替换,删除后重建资源。会导致服务不可用。
kubectl replace --force -f ./pod.json

# 为多副本的 nginx 创建服务,使用 80 端口提供服务,连接到容器的 8000 端口
kubectl expose rc nginx --port=80 --target-port=8000

# 将某单容器 Pod 的镜像版本(标签)更新到 v4
kubectl get pod mypod -o yaml | sed 's/\(image: myimage\):.*$/\1:v4/' | kubectl replace -f -

kubectl label pods my-pod new-label=awesome                      # 添加标签
kubectl label pods my-pod new-label-                             # 移除标签
kubectl label pods my-pod new-label=new-value --overwrite        # 覆盖现有的值
kubectl annotate pods my-pod icon-url=http://goo.gl/XXBTWq       # 添加注解
kubectl annotate pods my-pod icon-url-                           # 移除注解
kubectl autoscale deployment foo --min=2 --max=10                # 对 "foo" Deployment 自动扩缩容

部分更新资源

# 部分更新某节点
kubectl patch node k8s-node-1 -p '{"spec":{"unschedulable":true}}'

# 更新容器的镜像;spec.containers[*].name 是必需的。因为它是一个合并性质的主键。
kubectl patch pod valid-pod -p '{"spec":{"containers":[{"name":"kubernetes-serve-hostname","image":"new image"}]}}'

# 使用带位置数组的 JSON patch 更新容器的镜像
kubectl patch pod valid-pod --type='json' -p='[{"op": "replace", "path": "/spec/containers/0/image", "value":"new image"}]'

# 使用带位置数组的 JSON patch 禁用某 Deployment 的 livenessProbe
kubectl patch deployment valid-deployment  --type json   -p='[{"op": "remove", "path": "/spec/template/spec/containers/0/livenessProbe"}]'

# 在带位置数组中添加元素
kubectl patch sa default --type='json' -p='[{"op": "add", "path": "/secrets/1", "value": {"name": "whatever" } }]'

# 通过修正 scale 子资源来更新 Deployment 的副本数
kubectl patch deployment nginx-deployment --subresource='scale' --type='merge' -p '{"spec":{"replicas":2}}'

编辑资源

使用你偏爱的编辑器编辑 API 资源。

kubectl edit svc/docker-registry                      # 编辑名为 docker-registry 的服务
KUBE_EDITOR="nano" kubectl edit svc/docker-registry   # 使用其他编辑器

对资源进行扩缩

kubectl scale --replicas=3 rs/foo                                 # 将名为 'foo' 的副本集扩缩到 3 副本
kubectl scale --replicas=3 -f foo.yaml                            # 将在 "foo.yaml" 中的特定资源扩缩到 3 个副本
kubectl scale --current-replicas=2 --replicas=3 deployment/mysql  # 如果名为 mysql 的 Deployment 的副本当前是 2,那么将它扩缩到 3
kubectl scale --replicas=5 rc/foo rc/bar rc/baz                   # 扩缩多个副本控制器

删除资源

kubectl delete -f ./pod.json                                              # 删除在 pod.json 中指定的类型和名称的 Pod
kubectl delete pod unwanted --now                                         # 删除 Pod 且无宽限期限(无优雅时段)
kubectl delete pod,service baz foo                                        # 删除名称为 "baz" 和 "foo" 的 Pod 和服务
kubectl delete pods,services -l name=myLabel                              # 删除包含 name=myLabel 标签的 Pod 和服务
kubectl -n my-ns delete pod,svc --all                                     # 删除在 my-ns 名字空间中全部的 Pod 和服务
# 删除所有与 pattern1 或 pattern2 awk 模式匹配的 Pod
kubectl get pods  -n mynamespace --no-headers=true | awk '/pattern1|pattern2/{print $1}' | xargs  kubectl delete -n mynamespace pod

与运行中的 Pod 进行交互

kubectl logs my-pod                                 # 获取 Pod 日志(标准输出)
kubectl logs -l name=myLabel                        # 获取含 name=myLabel 标签的 Pod 的日志(标准输出)
kubectl logs my-pod --previous                      # 获取上个容器实例的 Pod 日志(标准输出)
kubectl logs my-pod -c my-container                 # 获取 Pod 容器的日志(标准输出, 多容器场景)
kubectl logs -l name=myLabel -c my-container        # 获取含 name=myLabel 标签的 Pod 容器日志(标准输出, 多容器场景)
kubectl logs my-pod -c my-container --previous      # 获取 Pod 中某容器的上个实例的日志(标准输出, 多容器场景)
kubectl logs -f my-pod                              # 流式输出 Pod 的日志(标准输出)
kubectl logs -f my-pod -c my-container              # 流式输出 Pod 容器的日志(标准输出, 多容器场景)
kubectl logs -f -l name=myLabel --all-containers    # 流式输出含 name=myLabel 标签的 Pod 的所有日志(标准输出)
kubectl run -i --tty busybox --image=busybox:1.28 -- sh  # 以交互式 Shell 运行 Pod
kubectl run nginx --image=nginx -n mynamespace      # 在 “mynamespace” 命名空间中运行单个 nginx Pod
kubectl run nginx --image=nginx --dry-run=client -o yaml > pod.yaml
                                                    # 为运行 nginx Pod 生成规约并将其写入到名为 pod.yaml 的文件

kubectl attach my-pod -i                            # 挂接到一个运行的容器中
kubectl port-forward my-pod 5000:6000               # 在本地计算机上侦听端口 5000 并转发到 my-pod 上的端口 6000
kubectl exec my-pod -- ls /                         # 在已有的 Pod 中运行命令(单容器场景)
kubectl exec --stdin --tty my-pod -- /bin/sh        # 使用交互 shell 访问正在运行的 Pod (一个容器场景)
kubectl exec my-pod -c my-container -- ls /         # 在已有的 Pod 中运行命令(多容器场景)
kubectl top pod                                     # 显示默认命名空间中所有 Pod 的度量值
kubectl top pod POD_NAME --containers               # 显示给定 Pod 和其中容器的度量值
kubectl top pod POD_NAME --sort-by=cpu              # 显示给定 Pod 的指标并且按照 'cpu' 或者 'memory' 排序

从容器中复制文件和目录

kubectl cp /tmp/foo_dir my-pod:/tmp/bar_dir            # 将 /tmp/foo_dir 本地目录复制到远程当前命名空间中 Pod 中的 /tmp/bar_dir
kubectl cp /tmp/foo my-pod:/tmp/bar -c my-container    # 将 /tmp/foo 本地文件复制到远程 Pod 中特定容器的 /tmp/bar 下
kubectl cp /tmp/foo my-namespace/my-pod:/tmp/bar       # 将 /tmp/foo 本地文件复制到远程 “my-namespace” 命名空间内指定 Pod 中的 /tmp/bar
kubectl cp my-namespace/my-pod:/tmp/foo /tmp/bar       # 将 /tmp/foo 从远程 Pod 复制到本地 /tmp/bar
tar cf - /tmp/foo | kubectl exec -i -n my-namespace my-pod -- tar xf - -C /tmp/bar  # 将 /tmp/foo 本地文件复制到远程 “my-namespace” 命名空间中 Pod 中的 /tmp/bar
kubectl exec -n my-namespace my-pod -- tar cf - /tmp/foo | tar xf - -C /tmp/bar    # 将 /tmp/foo 从远程 Pod 复制到本地 /tmp/bar

与 Deployments 和 Services 进行交互

kubectl logs deploy/my-deployment                         # 获取一个 Deployment 的 Pod 的日志(单容器例子)
kubectl logs deploy/my-deployment -c my-container         # 获取一个 Deployment 的 Pod 的日志(多容器例子)

kubectl port-forward svc/my-service 5000                  # 侦听本地端口 5000 并转发到 Service 后端端口 5000
kubectl port-forward svc/my-service 5000:my-service-port  # 侦听本地端口 5000 并转发到名字为 <my-service-port> 的 Service 目标端口

kubectl port-forward deploy/my-deployment 5000:6000       # 侦听本地端口 5000 并转发到 <my-deployment> 创建的 Pod 里的端口 6000
kubectl exec deploy/my-deployment -- ls                   # 在 Deployment 里的第一个 Pod 的第一个容器里运行命令(单容器和多容器例子)

与节点和集群进行交互

kubectl cordon my-node                                                # 标记 my-node 节点为不可调度
kubectl drain my-node                                                 # 对 my-node 节点进行清空操作,为节点维护做准备
kubectl uncordon my-node                                              # 标记 my-node 节点为可以调度
kubectl top node                                                      # 显示所有节点的度量值
kubectl top node my-node                                              # 显示给定节点的度量值
kubectl cluster-info                                                  # 显示主控节点和服务的地址
kubectl cluster-info dump                                             # 将当前集群状态转储到标准输出
kubectl cluster-info dump --output-directory=/path/to/cluster-state   # 将当前集群状态输出到 /path/to/cluster-state

# 查看当前节点上存在的现有污点
kubectl get nodes -o='custom-columns=NodeName:.metadata.name,TaintKey:.spec.taints[*].key,TaintValue:.spec.taints[*].value,TaintEffect:.spec.taints[*].effect'

# 如果已存在具有指定键和效果的污点,则替换其值为指定值
kubectl taint nodes foo dedicated=special-user:NoSchedule

资源类型

列出所支持的全部资源类型和它们的简称、 API 组、 是否是名字空间作用域Kind

kubectl api-resources

用于探索 API 资源的其他操作:

kubectl api-resources --namespaced=true      # 所有命名空间作用域的资源
kubectl api-resources --namespaced=false     # 所有非命名空间作用域的资源
kubectl api-resources -o name                # 用简单格式列举所有资源(仅显示资源名称)
kubectl api-resources -o wide                # 用扩展格式列举所有资源(又称 "wide" 格式)
kubectl api-resources --verbs=list,get       # 支持 "list" 和 "get" 请求动词的所有资源
kubectl api-resources --api-group=extensions # "extensions" API 组中的所有资源

格式化输出

要以特定格式将详细信息输出到终端窗口,将 -o(或者 --output)参数添加到支持的 kubectl 命令中。

输出格式描述
-o=custom-columns=<spec>使用逗号分隔的自定义列来打印表格
-o=custom-columns-file=<filename>使用 <filename> 文件中的自定义列模板打印表格
-o=go-template=<template>打印在 golang 模板中定义的字段
-o=go-template-file=<filename>打印在 <filename> 文件中由 golang 模板定义的字段
-o=json输出 JSON 格式的 API 对象
-o=jsonpath=<template>打印 jsonpath 表达式中定义的字段
-o=jsonpath-file=<filename>打印在 <filename> 文件中定义的 jsonpath 表达式所指定的字段
-o=name仅打印资源名称而不打印其他内容
-o=wide以纯文本格式输出额外信息,对于 Pod 来说,输出中包含了节点名称
-o=yaml输出 YAML 格式的 API 对象

使用 -o=custom-columns 的示例:

# 集群中运行着的所有镜像
kubectl get pods -A -o=custom-columns='DATA:spec.containers[*].image'

# 列举 default 名字空间中运行的所有镜像,按 Pod 分组
kubectl get pods --namespace default --output=custom-columns="NAME:.metadata.name,IMAGE:.spec.containers[*].image"

# 除 "registry.k8s.io/coredns:1.6.2" 之外的所有镜像
kubectl get pods -A -o=custom-columns='DATA:spec.containers[?(@.image!="registry.k8s.io/coredns:1.6.2")].image'

# 输出 metadata 下面的所有字段,无论 Pod 名字为何
kubectl get pods -A -o=custom-columns='DATA:metadata.*'

有关更多示例,请参看 kubectl 参考文档

Kubectl 日志输出详细程度和调试

Kubectl 日志输出详细程度是通过 -v 或者 --v 来控制的,参数后跟一个数字表示日志的级别。 Kubernetes 通用的日志习惯和相关的日志级别在 这里有相应的描述。

详细程度描述
--v=0用于那些应该 始终 对运维人员可见的信息,因为这些信息一般很有用。
--v=1如果你不想要看到冗余信息,此值是一个合理的默认日志级别。
--v=2输出有关服务的稳定状态的信息以及重要的日志消息,这些信息可能与系统中的重大变化有关。这是建议大多数系统设置的默认日志级别。
--v=3包含有关系统状态变化的扩展信息。
--v=4包含调试级别的冗余信息。
--v=5跟踪级别的详细程度。
--v=6显示所请求的资源。
--v=7显示 HTTP 请求头。
--v=8显示 HTTP 请求内容。
--v=9显示 HTTP 请求内容而且不截断内容。

接下来

6.11.2 - kubectl 命令

kubectl 命令参考

6.11.3 - kubectl

简介

kubectl 管理控制 Kubernetes 集群。

更多信息请查阅命令行工具kubectl)。

kubectl [flags]

选项

--add-dir-header
设置为 true 表示添加文件目录到日志信息头中
--alsologtostderr
表示将日志输出到文件的同时输出到 stderr
--as string
以指定用户的身份执行操作
--as-group stringArray
模拟指定的组来执行操作,可以使用这个标志来指定多个组。
--azure-container-registry-config string
包含 Azure 容器仓库配置信息的文件的路径。
--cache-dir string     默认值:"$HOME/.kube/cache"
默认缓存目录
--certificate-authority string
指向证书机构的 cert 文件路径
--client-certificate string
TLS 使用的客户端证书路径
--client-key string
TLS 使用的客户端密钥文件路径
--cloud-provider-gce-l7lb-src-cidrs cidrs     默认值:130.211.0.0/22,35.191.0.0/16
在 GCE 防火墙中开放的 CIDR,用来进行 L7 LB 流量代理和健康检查。
--cloud-provider-gce-lb-src-cidrs cidrs     默认值:130.211.0.0/22,209.85.152.0/22,209.85.204.0/22,35.191.0.0/16
在 GCE 防火墙中开放的 CIDR,用来进行 L4 LB 流量代理和健康检查。
--cluster string
要使用的 kubeconfig 集群的名称
--context string
要使用的 kubeconfig 上下文的名称
--default-not-ready-toleration-seconds int     默认值:300
表示 `notReady` 状态的容忍度秒数:默认情况下,`NoExecute` 被添加到尚未具有此容忍度的每个 Pod 中。
--default-unreachable-toleration-seconds int     默认值:300
表示 `unreachable` 状态的容忍度秒数:默认情况下,`NoExecute` 被添加到尚未具有此容忍度的每个 Pod 中。
-h, --help
kubectl 操作的帮助命令
--insecure-skip-tls-verify
设置为 true,则表示不会检查服务器证书的有效性。这样会导致你的 HTTPS 连接不安全。
--kubeconfig string
CLI 请求使用的 kubeconfig 配置文件的路径。
--log-backtrace-at traceLocation     默认值:0
当日志机制运行到指定文件的指定行(file:N)时,打印调用堆栈信息
--log-dir string
如果不为空,则将日志文件写入此目录
--log-file string
如果不为空,则将使用此日志文件
--log-file-max-size uint     默认值:1800
定义日志文件的最大尺寸。单位为兆字节。如果值设置为 0,则表示日志文件大小不受限制。
--log-flush-frequency duration     默认值:5s
两次日志刷新操作之间的最长时间(秒)
--logtostderr     默认值:true
日志输出到 stderr 而不是文件中
--match-server-version
要求客户端版本和服务端版本相匹配
-n, --namespace string
如果存在,CLI 请求将使用此命名空间
--one-output
如果为 true,则只将日志写入初始严重级别(而不是同时写入所有较低的严重级别)。
--password string
API 服务器进行基本身份验证的密码
--profile string     默认值:"none"
要记录的性能指标的名称。可取(none|cpu|heap|goroutine|threadcreate|block|mutex)其中之一。
--profile-output string     默认值:"profile.pprof"
用于转储所记录的性能信息的文件名
--request-timeout string     默认值:"0"
放弃单个服务器请求之前的等待时间,非零值需要包含相应时间单位(例如:1s、2m、3h)。零值则表示不做超时要求。
-s, --server string
Kubernetes API 服务器的地址和端口
--skip-headers
设置为 true 则表示跳过在日志消息中出现 header 前缀信息
--skip-log-headers
设置为 true 则表示在打开日志文件时跳过 header 信息
--stderrthreshold severity     默认值:2
等于或高于此阈值的日志将输出到标准错误输出(stderr)
--token string
用于对 API 服务器进行身份认证的持有者令牌
--user string
指定使用 kubeconfig 配置文件中的用户名
--username string
用于 API 服务器的基本身份验证的用户名
-v, --v Level
指定输出日志的日志详细级别
--version version[=true]
打印 kubectl 版本信息并退出
--vmodule moduleSpec
以逗号分隔的 pattern=N 设置列表,用于过滤文件的日志记录

环境变量

KUBECONFIG
kubectl 的配置 ("kubeconfig") 文件的路径。默认值:"$HOME/.kube/config"
KUBECTL_COMMAND_HEADERS
设置为 false 时,将关闭额外的 HTTP 标头,不再详细说明被调用的 kubectl 命令(此变量适用于 Kubernetes v1.22 或更高版本)
KUBECTL_EXPLAIN_OPENAPIV3
切换对 `kubectl explain` 的调用是否使用可用的新 OpenAPIv3 数据源。OpenAPIV3 自 Kubernetes 1.24 起默认被启用。
KUBECTL_ENABLE_CMD_SHADOW
当设置为 true 时,如果子命令不存在,外部插件可以用作内置命令的子命令。 此功能处于 alpha 阶段,只能用于 create 命令(例如 kubectl create networkpolicy)。
KUBECTL_REMOTE_COMMAND_WEBSOCKETS
当设置为 true 时,kubectl exec、cp 和 attach 命令将尝试使用 WebSocket 协议进行流式传输。 如果升级到 WebSocket 失败,这些命令将回退为使用当前的 SPDY 协议。

另请参见

  • kubectl cordon - 标记节点为不可调度的
  • kubectl cp - 将文件和目录拷入/拷出容器
  • kubectl create - 通过文件或标准输入来创建资源
  • kubectl debug - 创建用于排查工作负载和节点故障的调试会话
  • kubectl delete - 通过文件名、标准输入、资源和名字删除资源, 或者通过资源和标签选择算符来删除资源

6.11.4 - JSONPath 支持

kubectl 支持 JSONPath 模板。

JSONPath 模板由 {} 包起来的 JSONPath 表达式组成。Kubectl 使用 JSONPath 表达式来过滤 JSON 对象中的特定字段并格式化输出。 除了原始的 JSONPath 模板语法,以下函数和语法也是有效的:

  1. 使用双引号将 JSONPath 表达式内的文本引起来。
  2. 使用 rangeend 运算符来迭代列表。
  3. 使用负片索引后退列表。负索引不会“环绕”列表,并且只要 -index + listLength> = 0 就有效。

给定 JSON 输入:

{
  "kind": "List",
  "items":[
    {
      "kind":"None",
      "metadata":{
        "name":"127.0.0.1",
        "labels":{
          "kubernetes.io/hostname":"127.0.0.1"
        }
      },
      "status":{
        "capacity":{"cpu":"4"},
        "addresses":[{"type": "LegacyHostIP", "address":"127.0.0.1"}]
      }
    },
    {
      "kind":"None",
      "metadata":{"name":"127.0.0.2"},
      "status":{
        "capacity":{"cpu":"8"},
        "addresses":[
          {"type": "LegacyHostIP", "address":"127.0.0.2"},
          {"type": "another", "address":"127.0.0.3"}
        ]
      }
    }
  ],
  "users":[
    {
      "name": "myself",
      "user": {}
    },
    {
      "name": "e2e",
      "user": {"username": "admin", "password": "secret"}
    }
  ]
}
函数描述示例结果
text纯文本kind is {.kind}kind is List
@当前对象{@}与输入相同
.[]子运算符{.kind}{['kind']}{['name\.type']}List
..递归下降{..name}127.0.0.1 127.0.0.2 myself e2e
*通配符。获取所有对象{.items[*].metadata.name}[127.0.0.1 127.0.0.2]
[start:end:step]下标运算符{.users[0].name}myself
[,]并集运算符{.items[*]['metadata.name', 'status.capacity']}127.0.0.1 127.0.0.2 map[cpu:4] map[cpu:8]
?()过滤{.users[?(@.name=="e2e")].user.password}secret
rangeend迭代列表{range .items[*]}[{.metadata.name}, {.status.capacity}] {end}[127.0.0.1, map[cpu:4]] [127.0.0.2, map[cpu:8]]
''引用解释执行字符串{range .items[*]}{.metadata.name}{'\t'}{end}127.0.0.1 127.0.0.2
\转义终止符{.items[0].metadata.labels.kubernetes\.io/hostname}127.0.0.1

使用 kubectl 和 JSONPath 表达式的示例:

kubectl get pods -o json
kubectl get pods -o=jsonpath='{@}'
kubectl get pods -o=jsonpath='{.items[0]}'
kubectl get pods -o=jsonpath='{.items[0].metadata.name}'
kubectl get pods -o=jsonpath="{.items[*]['metadata.name', 'status.capacity']}"
kubectl get pods -o=jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.startTime}{"\n"}{end}'
kubectl get pods -o=jsonpath='{.items[0].metadata.labels.kubernetes\.io/hostname}'

6.11.5 - 适用于 Docker 用户的 kubectl

你可以使用 Kubernetes 命令行工具 kubectl 与 API 服务器进行交互。如果你熟悉 Docker 命令行工具, 则使用 kubectl 非常简单。但是,Docker 命令和 kubectl 命令之间有一些区别。以下显示了 Docker 子命令, 并描述了等效的 kubectl 命令。

docker run

要运行 nginx 部署并将其暴露,请参见 kubectl create deployment

使用 Docker 命令:

docker run -d --restart=always -e DOMAIN=cluster --name nginx-app -p 80:80 nginx
55c103fa129692154a7652490236fee9be47d70a8dd562281ae7d2f9a339a6db
docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                NAMES
55c103fa1296        nginx               "nginx -g 'daemon of…"   9 seconds ago       Up 9 seconds        0.0.0.0:80->80/tcp   nginx-app

使用 kubectl 命令:

# 启动运行 nginx 的 Pod
kubectl create deployment --image=nginx nginx-app
deployment.apps/nginx-app created
# 添加 env 到 nginx-app
kubectl set env deployment/nginx-app  DOMAIN=cluster
deployment.apps/nginx-app env updated
# 通过服务公开端口
kubectl expose deployment nginx-app --port=80 --name=nginx-http
service "nginx-http" exposed

在 kubectl 命令中,我们创建了一个 Deployment, 这将保证有 N 个运行 nginx 的 Pod(N 代表 spec 中声明的副本数,默认为 1)。 我们还创建了一个 service,其选择算符与容器标签匹配。 查看使用服务访问集群中的应用程序获取更多信息。

默认情况下镜像会在后台运行,与 docker run -d ... 类似,如果你想在前台运行, 使用 kubectl run 在前台运行 Pod:

kubectl run [-i] [--tty] --attach <name> --image=<image>

docker run ... 不同的是,如果指定了 --attach,我们将连接到 stdinstdoutstderr, 而不能控制具体连接到哪个输出流(docker -a ...)。要从容器中退出,可以输入 Ctrl + P,然后按 Ctrl + Q。

因为我们使用 Deployment 启动了容器,如果你终止连接到的进程(例如 ctrl-c),容器将会重启, 这跟 docker run -it 不同。如果想销毁该 Deployment(和它的 Pod), 你需要运行 kubectl delete deployment <name>

docker ps

如何列出哪些正在运行?查看 kubectl get

使用 Docker 命令:

docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS                     PORTS                NAMES
14636241935f        ubuntu:16.04        "echo test"              5 seconds ago        Exited (0) 5 seconds ago                        cocky_fermi
55c103fa1296        nginx               "nginx -g 'daemon of…"   About a minute ago   Up About a minute          0.0.0.0:80->80/tcp   nginx-app

使用 kubectl 命令:

kubectl get po
NAME                        READY     STATUS      RESTARTS   AGE
nginx-app-8df569cb7-4gd89   1/1       Running     0          3m
ubuntu                      0/1       Completed   0          20s

docker attach

如何连接到已经运行在容器中的进程? 查看 kubectl attach

使用 Docker 命令:

docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                NAMES
55c103fa1296        nginx               "nginx -g 'daemon of…"   5 minutes ago       Up 5 minutes        0.0.0.0:80->80/tcp   nginx-app
docker attach 55c103fa1296
...

kubectl:

kubectl get pods
NAME              READY     STATUS    RESTARTS   AGE
nginx-app-5jyvm   1/1       Running   0          10m
kubectl attach -it nginx-app-5jyvm
...

要从容器中分离,可以输入 Ctrl + P,然后按 Ctrl + Q。

docker exec

如何在容器中执行命令?查看 kubectl exec

使用 Docker 命令:

docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                NAMES
55c103fa1296        nginx               "nginx -g 'daemon of…"   6 minutes ago       Up 6 minutes        0.0.0.0:80->80/tcp   nginx-app
docker exec 55c103fa1296 cat /etc/hostname
55c103fa1296

使用 kubectl 命令:

kubectl get po
NAME              READY     STATUS    RESTARTS   AGE
nginx-app-5jyvm   1/1       Running   0          10m
kubectl exec nginx-app-5jyvm -- cat /etc/hostname
nginx-app-5jyvm

执行交互式命令怎么办?

使用 Docker 命令:

docker exec -ti 55c103fa1296 /bin/sh
# exit

kubectl:

kubectl exec -ti nginx-app-5jyvm -- /bin/sh
# exit

更多信息请查看获取运行中容器的 Shell 环境

docker logs

如何查看运行中进程的 stdout/stderr?查看 kubectl logs

使用 Docker 命令:

docker logs -f a9e
192.168.9.1 - - [14/Jul/2015:01:04:02 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.35.0" "-"
192.168.9.1 - - [14/Jul/2015:01:04:03 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.35.0" "-"

使用 kubectl 命令:

kubectl logs -f nginx-app-zibvs
10.240.63.110 - - [14/Jul/2015:01:09:01 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.26.0" "-"
10.240.63.110 - - [14/Jul/2015:01:09:02 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.26.0" "-"

现在是时候提一下 Pod 和容器之间的细微差别了;默认情况下如果 Pod 中的进程退出 Pod 也不会终止, 相反它将会重启该进程。这类似于 docker run 时的 --restart=always 选项,这是主要差别。 在 Docker 中,进程的每个调用的输出都是被连接起来的,但是对于 Kubernetes,每个调用都是分开的。 要查看以前在 Kubernetes 中执行的输出,请执行以下操作:

kubectl logs --previous nginx-app-zibvs
10.240.63.110 - - [14/Jul/2015:01:09:01 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.26.0" "-"
10.240.63.110 - - [14/Jul/2015:01:09:02 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.26.0" "-"

查看日志架构获取更多信息。

docker stop 和 docker rm

如何停止和删除运行中的进程?查看 kubectl delete

使用 Docker 命令:

docker ps
CONTAINER ID        IMAGE               COMMAND                CREATED             STATUS              PORTS                         NAMES
a9ec34d98787        nginx               "nginx -g 'daemon of"  22 hours ago        Up 22 hours         0.0.0.0:80->80/tcp, 443/tcp   nginx-app
docker stop a9ec34d98787
a9ec34d98787
docker rm a9ec34d98787
a9ec34d98787

使用 kubectl 命令:

kubectl get deployment nginx-app
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
nginx-app    1/1     1            1           2m
kubectl get po -l app=nginx-app
NAME                         READY     STATUS    RESTARTS   AGE
nginx-app-2883164633-aklf7   1/1       Running   0          2m
kubectl delete deployment nginx-app
deployment "nginx-app" deleted
kubectl get po -l app=nginx-app
# 什么都没有返回

docker login

在 kubectl 中没有对 docker login 的直接模拟。如果你有兴趣在私有镜像仓库中使用 Kubernetes, 请参阅使用私有镜像仓库

docker version

如何查看客户端和服务端的版本?查看 kubectl version

使用 Docker 命令:

docker version
Client version: 1.7.0
Client API version: 1.19
Go version (client): go1.4.2
Git commit (client): 0baf609
OS/Arch (client): linux/amd64
Server version: 1.7.0
Server API version: 1.19
Go version (server): go1.4.2
Git commit (server): 0baf609
OS/Arch (server): linux/amd64

使用 kubectl 命令:

kubectl version
Client Version: version.Info{Major:"1", Minor:"6", GitVersion:"v1.6.9+a3d1dfa6f4335", GitCommit:"9b77fed11a9843ce3780f70dd251e92901c43072", GitTreeState:"dirty", BuildDate:"2017-08-29T20:32:58Z", OpenPaasKubernetesVersion:"v1.03.02", GoVersion:"go1.7.5", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"6", GitVersion:"v1.6.9+a3d1dfa6f4335", GitCommit:"9b77fed11a9843ce3780f70dd251e92901c43072", GitTreeState:"dirty", BuildDate:"2017-08-29T20:32:58Z", OpenPaasKubernetesVersion:"v1.03.02", GoVersion:"go1.7.5", Compiler:"gc", Platform:"linux/amd64"}

docker info

如何获取有关环境和配置的各种信息?查看 kubectl cluster-info

使用 Docker 命令:

docker info
Containers: 40
Images: 168
Storage Driver: aufs
 Root Dir: /usr/local/google/docker/aufs
 Backing Filesystem: extfs
 Dirs: 248
 Dirperm1 Supported: false
Execution Driver: native-0.2
Logging Driver: json-file
Kernel Version: 3.13.0-53-generic
Operating System: Ubuntu 14.04.2 LTS
CPUs: 12
Total Memory: 31.32 GiB
Name: k8s-is-fun.mtv.corp.google.com
ID: ADUV:GCYR:B3VJ:HMPO:LNPQ:KD5S:YKFQ:76VN:IANZ:7TFV:ZBF4:BYJO
WARNING: No swap limit support

使用 kubectl 命令:

kubectl cluster-info
Kubernetes master is running at https://203.0.113.141
KubeDNS is running at https://203.0.113.141/api/v1/namespaces/kube-system/services/kube-dns/proxy
kubernetes-dashboard is running at https://203.0.113.141/api/v1/namespaces/kube-system/services/kubernetes-dashboard/proxy
Grafana is running at https://203.0.113.141/api/v1/namespaces/kube-system/services/monitoring-grafana/proxy
Heapster is running at https://203.0.113.141/api/v1/namespaces/kube-system/services/monitoring-heapster/proxy
InfluxDB is running at https://203.0.113.141/api/v1/namespaces/kube-system/services/monitoring-influxdb/proxy

6.11.6 - kubectl 的用法约定

kubectl 的推荐用法约定。

在可重用脚本中使用 kubectl

对于脚本中的稳定输出:

  • 请求一个面向机器的输出格式,例如 -o name-o json-o yaml-o go template-o jsonpath
  • 完全限定版本。例如 jobs.v1.batch/myjob。这将确保 kubectl 不会使用其默认版本,该版本会随着时间的推移而更改。
  • 不要依赖上下文、首选项或其他隐式状态。

子资源

  • 你可以将 --subresource Beta 标志用于 kubectl 命令,例如 getpatcheditreplace 来获取和更新所有支持子资源的资源的子资源。目前,仅支持 statusscale 子资源。
    • 对于 kubectl edit,不支持 scale 子资源。如果将 --subresourcekubectl edit 一起使用, 并指定 scale 作为子资源,则命令将会报错。
  • 针对子资源的 API 协定与完整资源相同。在更新 status 子资源为一个新值时,请记住, 子资源可能是潜在的由控制器调和为不同的值。

最佳实践

kubectl run

若希望 kubectl run 满足基础设施即代码的要求:

  • 使用特定版本的标签标记镜像,不要将该标签改为新版本。例如使用 :v1234v1.2.3r03062016-1-4, 而不是 :latest(有关详细信息,请参阅配置的最佳实践)。
  • 使用基于版本控制的脚本来运行包含大量参数的镜像。
  • 对于无法通过 kubectl run 参数来表示的功能特性,使用基于源码控制的配置文件,以记录要使用的功能特性。

你可以使用 --dry-run=client 参数来预览而不真正提交即将下发到集群的对象实例:

kubectl apply

  • 你可以使用 kubectl apply 命令创建或更新资源。有关使用 kubectl apply 更新资源的详细信息,请参阅 Kubectl 文档

6.12 - 调试集群

6.12.1 - 流控

API 优先级和公平性控制着 Kubernetes API 服务器在负载过高的情况下的行为。你可以在 API 优先级和公平性文档中找到更多信息。

问题诊断

对于启用了 APF 的 API 服务器,每个 HTTP 响应都有两个额外的 HTTP 头: X-Kubernetes-PF-FlowSchema-UIDX-Kubernetes-PF-PriorityLevel-UID, 给出与请求匹配的 FlowSchema 和已分配的优先级级别。 如果请求用户没有查看这些对象的权限,则这些 HTTP 头中将不包含 API 对象的名称, 因此在调试时,你可以使用类似如下的命令:

kubectl get flowschemas -o custom-columns="uid:{metadata.uid},name:{metadata.name}"
kubectl get prioritylevelconfigurations -o custom-columns="uid:{metadata.uid},name:{metadata.name}"

来获取 UID 与 FlowSchema 的名称和 PriorityLevelConfiguration 的名称之间的对应关系。

调试端点

启用 APF 特性后,kube-apiserver 会在其 HTTP/HTTPS 端口额外提供以下路径:

你需要确保自己具有访问这些端点的权限。如果你使用管理员身份,则无需进行任何操作。 必要时可以通过设置 nonResourceURLs 来访问 /debug/api_priority_and_fairness/ 参照 RBAC 文档授予权限。

  • /debug/api_priority_and_fairness/dump_priority_levels —— 所有优先级及其当前状态的列表。你可以这样获取:

    kubectl get --raw /debug/api_priority_and_fairness/dump_priority_levels
    

    输出格式为 CSV,类似于:

    PriorityLevelName, ActiveQueues, IsIdle, IsQuiescing, WaitingRequests, ExecutingRequests, DispatchedRequests, RejectedRequests, TimedoutRequests, CancelledRequests
    catch-all,         0,            true,   false,       0,               0,                 1,                  0,                0,                0
    exempt,            0,            true,   false,       0,               0,                 0,                  0,                0,                0
    global-default,    0,            true,   false,       0,               0,                 46,                 0,                0,                0
    leader-election,   0,            true,   false,       0,               0,                 4,                  0,                0,                0
    node-high,         0,            true,   false,       0,               0,                 34,                 0,                0,                0
    system,            0,            true,   false,       0,               0,                 48,                 0,                0,                0
    workload-high,     0,            true,   false,       0,               0,                 500,                0,                0,                0
    workload-low,      0,            true,   false,       0,               0,                 0,                  0,                0,                0
    

    所选列名的解释:

    • IsQuiescing 表示当队列已被腾空时此优先级级别是否将被移除。
  • /debug/api_priority_and_fairness/dump_queues —— 所有队列及其当前状态的列表。 你可以这样获取:

    kubectl get --raw /debug/api_priority_and_fairness/dump_queues
    

    输出格式为 CSV,类似于:

    PriorityLevelName, Index,  PendingRequests, ExecutingRequests, SeatsInUse, NextDispatchR,   InitialSeatsSum, MaxSeatsSum, TotalWorkSum
    workload-low,      14,     27,              0,                 0,          77.64342019ss,   270,             270,         0.81000000ss
    workload-low,      74,     26,              0,                 0,          76.95387841ss,   260,             260,         0.78000000ss
    ...
    leader-election,   0,      0,               0,                 0,          5088.87053833ss, 0,               0,           0.00000000ss
    leader-election,   1,      0,               0,                 0,          0.00000000ss,    0,               0,           0.00000000ss
    ...
    workload-high,     0,      0,               0,                 0,          0.00000000ss,    0,               0,           0.00000000ss
    workload-high,     1,      0,               0,                 0,          1119.44936475ss, 0,               0,           0.00000000ss
    

    所选列名的解释:

    • NextDispatchR:下一个请求将被调度时的 R 进度计读数,单位为 seat-second。
    • InitialSeatsSum:与在某个给定队列中所有请求关联的 InitialSeats 的总和。
    • MaxSeatsSum:与某个给定队列中所有请求关联的 MaxSeats 的总和。
    • TotalWorkSum:在某个给定队列中所有等待中请求的总工作量,单位为 seat-second。

    注意:seat-second(缩写为 ss)是 APF 领域中的工作量单位。

  • /debug/api_priority_and_fairness/dump_requests - 所有请求的列表, 包括队列中正在等待的请求和正在执行的请求。你可以运行以下类似命令获取此列表:

    kubectl get --raw /debug/api_priority_and_fairness/dump_requests
    

    输出格式为 CSV,类似于:

    PriorityLevelName, FlowSchemaName,   QueueIndex, RequestIndexInQueue, FlowDistingsher,                        ArriveTime,                     InitialSeats, FinalSeats, AdditionalLatency, StartTime
    exempt,            exempt,           -1,         -1,                  ,                                       2023-07-15T04:51:25.596404345Z, 1,            0,          0s,                2023-07-15T04:51:25.596404345Z
    workload-low,      service-accounts, 14,         0,                   system:serviceaccount:default:loadtest, 2023-07-18T00:12:51.386556253Z, 10,           0,          0s,                0001-01-01T00:00:00Z
    workload-low,      service-accounts, 14,         1,                   system:serviceaccount:default:loadtest, 2023-07-18T00:12:51.487092539Z, 10,           0,          0s,                0001-01-01T00:00:00Z
    

    你可以使用以下命令获得更详细的清单:

    kubectl get --raw '/debug/api_priority_and_fairness/dump_requests?includeRequestDetails=1'
    

    输出格式为 CSV,类似于:

    PriorityLevelName, FlowSchemaName,   QueueIndex, RequestIndexInQueue, FlowDistingsher,                        ArriveTime,                     InitialSeats, FinalSeats, AdditionalLatency, StartTime,                      UserName,                               Verb,   APIPath,                                   Namespace,   Name,   APIVersion, Resource,   SubResource
    exempt,            exempt,           -1,         -1,                  ,                                       2023-07-15T04:51:25.596404345Z, 1,            0,          0s,                2023-07-15T04:51:25.596404345Z, system:serviceaccount:system:admin,     list,   /api/v1/namespaces/kube-stress/configmaps, kube-stress, ,       v1,         configmaps,
    workload-low,      service-accounts, 14,         0,                   system:serviceaccount:default:loadtest, 2023-07-18T00:13:08.986534842Z, 10,           0,          0s,                0001-01-01T00:00:00Z,           system:serviceaccount:default:loadtest, list,   /api/v1/namespaces/kube-stress/configmaps, kube-stress, ,       v1,         configmaps,
    workload-low,      service-accounts, 14,         1,                   system:serviceaccount:default:loadtest, 2023-07-18T00:13:09.086476021Z, 10,           0,          0s,                0001-01-01T00:00:00Z,           system:serviceaccount:default:loadtest, list,   /api/v1/namespaces/kube-stress/configmaps, kube-stress, ,       v1,         configmaps,
    

    所选列名的解释:

    • QueueIndex:队列的索引。对于没有队列的优先级级别,该值将为 -1。
    • RequestIndexInQueue:某个给定请求在队列中的索引。对于正在执行的请求,该值将为 -1。
    • InitialSeats:在请求的初始(正常)执行阶段占用的席位数。
    • FinalSeats:在请求执行的最终阶段占用的席位数,包括与之关联的 WATCH 通知。
    • AdditionalLatency:在请求执行的最终阶段所耗用的额外时间。 FinalSeats 将在此时间段内被占用。这并不意味着用户会感知到任何延迟。
    • StartTime:请求开始执行的时间。对于排队的请求,该值将为 0001-01-01T00:00:00Z。

调试日志生成行为

-v=3 或更详细的情况下,API 服务器会为在 API 服务日志中为每个请求输出一行 httplog, 其中包括以下属性:

  • apf_fs:请求被分类到的 FlowSchema 的名称。
  • apf_pl:该 FlowSchema 的优先级名称。
  • apf_iseats:为请求执行的初始(正常)阶段确定的席位数量。
  • apf_fseats:为请求的最后执行阶段(考虑关联的 watch 通知)确定的席位数量。
  • apf_additionalLatency:请求执行最后阶段的持续时间。

在更高级别的精细度下,将有日志行揭示 APF 如何处理请求的详细信息,主要用于调试目的。

响应头

APF 将以下两个头添加到每个 HTTP 响应消息中。 这些信息不会出现在审计日志中,但可以从客户端查看。 对于使用 klog 的客户端,使用 -v=8 或更高的详细级别可以查看这些头。

  • X-Kubernetes-PF-FlowSchema-UID 保存相应请求被分类到的 FlowSchema 对象的 UID。
  • X-Kubernetes-PF-PriorityLevel-UID 保存与该 FlowSchema 关联的 PriorityLevelConfiguration 对象的 UID。

接下来

有关 API 优先级和公平性的设计细节的背景信息, 请参阅增强提案

6.13 - 组件工具

6.13.1 - 特性门控

本页详述了管理员可以在不同的 Kubernetes 组件上指定的各种特性门控。

关于特性各个阶段的说明,请参见特性阶段

概述

特性门控是描述 Kubernetes 特性的一组键值对。你可以在 Kubernetes 的各个组件中使用 --feature-gates 标志来启用或禁用这些特性。

每个 Kubernetes 组件都支持启用或禁用与该组件相关的一组特性门控。 使用 -h 参数来查看所有组件支持的完整特性门控。 要为诸如 kubelet 之类的组件设置特性门控,请使用 --feature-gates 参数, 并向其传递一个特性设置键值对列表:

--feature-gates=...,GracefulNodeShutdown=true

下表总结了在不同的 Kubernetes 组件上可以设置的特性门控。

  • 引入特性或更改其发布阶段后,"开始(Since)" 列将包含 Kubernetes 版本。
  • "结束(Until)" 列(如果不为空)包含最后一个 Kubernetes 版本,你仍可以在其中使用特性门控。
  • 如果某个特性处于 Alpha 或 Beta 状态,你可以在 Alpha 和 Beta 特性门控表中找到该特性。
  • 如果某个特性处于稳定状态, 你可以在已毕业和废弃特性门控表中找到该特性的所有阶段。
  • 已毕业和废弃特性门控表还列出了废弃的和已被移除的特性。

Alpha 和 Beta 状态的特性门控

处于 Alpha 或 Beta 状态的特性门控
FeatureDefaultStageSinceUntil
AdmissionWebhookMatchConditionsfalseAlpha1.271.27
AdmissionWebhookMatchConditionstrueBeta1.28
AggregatedDiscoveryEndpointfalseAlpha1.261.26
AggregatedDiscoveryEndpointtrueBeta1.27
AnyVolumeDataSourcefalseAlpha1.181.23
AnyVolumeDataSourcetrueBeta1.24
APIResponseCompressionfalseAlpha1.71.15
APIResponseCompressiontrueBeta1.16
APIServerIdentityfalseAlpha1.201.25
APIServerIdentitytrueBeta1.26
APIServerTracingfalseAlpha1.221.26
APIServerTracingtrueBeta1.27
AppArmortrueBeta1.4
CloudControllerManagerWebhookfalseAlpha1.27
CloudDualStackNodeIPsfalseAlpha1.271.28
CloudDualStackNodeIPstrueBeta1.29
ClusterTrustBundlefalseAlpha1.27
ClusterTrustBundleProjectionfalseAlpha1.29
ComponentSLIsfalseAlpha1.261.26
ComponentSLIstrueBeta1.27
ConsistentListFromCachefalseAlpha1.28
ContainerCheckpointfalseAlpha1.25
ContextualLoggingfalseAlpha1.24
CPUManagerPolicyAlphaOptionsfalseAlpha1.23
CPUManagerPolicyBetaOptionstrueBeta1.23
CPUManagerPolicyOptionsfalseAlpha1.221.22
CPUManagerPolicyOptionstrueBeta1.23
CRDValidationRatchetingfalseAlpha1.28
CronJobsScheduledAnnotationtrueBeta1.28
CrossNamespaceVolumeDataSourcefalseAlpha1.26
CSIMigrationPortworxfalseAlpha1.231.24
CSIMigrationPortworxfalseBeta1.25
CSIVolumeHealthfalseAlpha1.21
CustomCPUCFSQuotaPeriodfalseAlpha1.12
DevicePluginCDIDevicesfalseAlpha1.281.28
DevicePluginCDIDevicestrueBeta1.29
DisableCloudProvidersfalseAlpha1.221.28
DisableCloudProviderstrueBeta1.29
DisableKubeletCloudCredentialProvidersfalseAlpha1.231.28
DisableKubeletCloudCredentialProviderstrueBeta1.29
DisableNodeKubeProxyVersionfalseAlpha1.29
DynamicResourceAllocationfalseAlpha1.26
ElasticIndexedJobtrueBeta1.27
EventedPLEGfalseAlpha1.261.26
EventedPLEGfalseBeta1.27
GracefulNodeShutdownfalseAlpha1.201.20
GracefulNodeShutdowntrueBeta1.21
GracefulNodeShutdownBasedOnPodPriorityfalseAlpha1.231.23
GracefulNodeShutdownBasedOnPodPrioritytrueBeta1.24
HonorPVReclaimPolicyfalseAlpha1.23
HPAContainerMetricsfalseAlpha1.201.26
HPAContainerMetricstrueBeta1.27
HPAScaleToZerofalseAlpha1.16
ImageMaximumGCAgefalseAlpha1.29
InPlacePodVerticalScalingfalseAlpha1.27
InTreePluginAWSUnregisterfalseAlpha1.21
InTreePluginAzureDiskUnregisterfalseAlpha1.21
InTreePluginAzureFileUnregisterfalseAlpha1.21
InTreePluginGCEUnregisterfalseAlpha1.21
InTreePluginOpenStackUnregisterfalseAlpha1.21
InTreePluginPortworxUnregisterfalseAlpha1.23
InTreePluginvSphereUnregisterfalseAlpha1.21
JobBackoffLimitPerIndexfalseAlpha1.281.28
JobBackoffLimitPerIndextrueBeta1.29
JobPodFailurePolicyfalseAlpha1.251.25
JobPodFailurePolicytrueBeta1.26
JobPodReplacementPolicyfalseAlpha1.281.28
JobPodReplacementPolicytrueBeta1.29
KubeletCgroupDriverFromCRIfalseAlpha1.28
KubeletInUserNamespacefalseAlpha1.22
KubeletPodResourcesDynamicResourcesfalseAlpha1.27
KubeletPodResourcesGetfalseAlpha1.27
KubeletSeparateDiskGCfalseAlpha1.29
KubeletTracingfalseAlpha1.251.26
KubeletTracingtrueBeta1.27
KubeProxyDrainingTerminatingNodesfalseAlpha1.28
LegacyServiceAccountTokenCleanUpfalseAlpha1.281.28
LegacyServiceAccountTokenCleanUptrueBeta1.29
LoadBalancerIPModefalseAlpha1.29
LocalStorageCapacityIsolationFSQuotaMonitoringfalseAlpha1.15
LogarithmicScaleDownfalseAlpha1.211.21
LogarithmicScaleDowntrueBeta1.22
LoggingAlphaOptionsfalseAlpha1.24
LoggingBetaOptionstrueBeta1.24
MatchLabelKeysInPodAffinityfalseAlpha1.29
MatchLabelKeysInPodTopologySpreadfalseAlpha1.251.26
MatchLabelKeysInPodTopologySpreadtrueBeta1.27
MaxUnavailableStatefulSetfalseAlpha1.24
MemoryManagerfalseAlpha1.211.21
MemoryManagertrueBeta1.22
MemoryQoSfalseAlpha1.22
MinDomainsInPodTopologySpreadfalseAlpha1.241.24
MinDomainsInPodTopologySpreadfalseBeta1.251.26
MinDomainsInPodTopologySpreadtrueBeta1.27
MultiCIDRServiceAllocatorfalseAlpha1.27
NewVolumeManagerReconstructionfalseBeta1.271.27
NewVolumeManagerReconstructiontrueBeta1.28
NFTablesProxyModefalseAlpha1.29
NodeInclusionPolicyInPodTopologySpreadfalseAlpha1.251.25
NodeInclusionPolicyInPodTopologySpreadtrueBeta1.26
NodeLogQueryfalseAlpha1.27
NodeSwapfalseAlpha1.221.27
NodeSwapfalseBeta1.28
OpenAPIEnumsfalseAlpha1.231.23
OpenAPIEnumstrueBeta1.24
PDBUnhealthyPodEvictionPolicyfalseAlpha1.261.26
PDBUnhealthyPodEvictionPolicytrueBeta1.27
PersistentVolumeLastPhaseTransitionTimefalseAlpha1.281.28
PersistentVolumeLastPhaseTransitionTimetrueBeta1.29
PodAndContainerStatsFromCRIfalseAlpha1.23
PodDeletionCostfalseAlpha1.211.21
PodDeletionCosttrueBeta1.22
PodDisruptionConditionsfalseAlpha1.251.25
PodDisruptionConditionstrueBeta1.26
PodHostIPsfalseAlpha1.281.28
PodHostIPstrueBeta1.29
PodIndexLabeltrueBeta1.28
PodLifecycleSleepActionfalseAlpha1.29
PodReadyToStartContainersConditionfalseAlpha1.281.28
PodReadyToStartContainersConditiontrueBeta1.29
PodSchedulingReadinessfalseAlpha1.261.26
PodSchedulingReadinesstrueBeta1.27
ProcMountTypefalseAlpha1.12
QOSReservedfalseAlpha1.11
RecoverVolumeExpansionFailurefalseAlpha1.23
SchedulerQueueingHintstrueBeta1.281.28
SchedulerQueueingHintsfalseBeta1.29
SecurityContextDenyfalseAlpha1.27
SELinuxMountReadWriteOncePodfalseAlpha1.251.26
SELinuxMountReadWriteOncePodfalseBeta1.271.27
SELinuxMountReadWriteOncePodtrueBeta1.28
SeparateTaintEvictionControllertrueBeta1.29
ServiceAccountTokenJTIfalseAlpha1.29
ServiceAccountTokenNodeBindingfalseAlpha1.29
ServiceAccountTokenNodeBindingValidationfalseAlpha1.29
ServiceAccountTokenPodNodeInfofalseAlpha1.29
SidecarContainersfalseAlpha1.281.28
SidecarContainerstrueBeta1.29
SizeMemoryBackedVolumesfalseAlpha1.201.21
SizeMemoryBackedVolumestrueBeta1.22
StableLoadBalancerNodeSettrueBeta1.27
StatefulSetAutoDeletePVCfalseAlpha1.231.26
StatefulSetAutoDeletePVCtrueBeta1.27
StatefulSetStartOrdinalfalseAlpha1.261.26
StatefulSetStartOrdinaltrueBeta1.27
StorageVersionAPIfalseAlpha1.20
StorageVersionHashfalseAlpha1.141.14
StorageVersionHashtrueBeta1.15
StructuredAuthenticationConfigurationfalseAlpha1.29
StructuredAuthorizationConfigurationfalseAlpha1.29
TopologyAwareHintsfalseAlpha1.211.22
TopologyAwareHintsfalseBeta1.231.23
TopologyAwareHintstrueBeta1.24
TopologyManagerPolicyAlphaOptionsfalseAlpha1.26
TopologyManagerPolicyBetaOptionsfalseBeta1.261.27
TopologyManagerPolicyBetaOptionstrueBeta1.28
TopologyManagerPolicyOptionsfalseAlpha1.261.27
TopologyManagerPolicyOptionstrueBeta1.28
TranslateStreamCloseWebsocketRequestsfalseAlpha1.29
UnauthenticatedHTTP2DOSMitigationfalseBeta1.281.28
UnauthenticatedHTTP2DOSMitigationtrueBeta1.29
UnknownVersionInteroperabilityProxyfalseAlpha1.28
UserNamespacesPodSecurityStandardsfalseAlpha1.29
UserNamespacesSupportfalseAlpha1.28
ValidatingAdmissionPolicyfalseAlpha1.261.27
ValidatingAdmissionPolicyfalseBeta1.28
VolumeAttributesClassfalseAlpha1.29
VolumeCapacityPriorityfalseAlpha1.21
WatchListfalseAlpha1.27
WindowsHostNetworktrueAlpha1.26
WinDSRfalseAlpha1.14
WinOverlayfalseAlpha1.141.19
WinOverlaytrueBeta1.20
ZeroLimitedNominalConcurrencySharesfalseBeta1.29

已毕业和已废弃的特性门控

已毕业或已废弃的特性门控
FeatureDefaultStageSinceUntil
APIListChunkingfalseAlpha1.81.8
APIListChunkingtrueBeta1.91.28
APIListChunkingtrueGA1.29
APIPriorityAndFairnessfalseAlpha1.181.19
APIPriorityAndFairnesstrueBeta1.201.28
APIPriorityAndFairnesstrueGA1.29
APISelfSubjectReviewfalseAlpha1.261.26
APISelfSubjectReviewtrueBeta1.271.27
APISelfSubjectReviewtrueGA1.28
ConsistentHTTPGetHandlerstrueGA1.25
CPUManagerfalseAlpha1.81.9
CPUManagertrueBeta1.101.25
CPUManagertrueGA1.26
CSIMigrationAzureFilefalseAlpha1.151.20
CSIMigrationAzureFilefalseBeta1.211.23
CSIMigrationAzureFiletrueBeta1.241.25
CSIMigrationAzureFiletrueGA1.26
CSIMigrationRBDfalseAlpha1.231.27
CSIMigrationRBDfalseDeprecated1.28
CSINodeExpandSecretfalseAlpha1.251.26
CSINodeExpandSecrettrueBeta1.271.28
CSINodeExpandSecrettrueGA1.29
CustomResourceValidationExpressionsfalseAlpha1.231.24
CustomResourceValidationExpressionstrueBeta1.251.28
CustomResourceValidationExpressionstrueGA1.29
DefaultHostNetworkHostPortsInPodTemplatesfalseDeprecated1.28
EfficientWatchResumptionfalseAlpha1.201.20
EfficientWatchResumptiontrueBeta1.211.23
EfficientWatchResumptiontrueGA1.24
ExecProbeTimeouttrueGA1.20
ExpandedDNSConfigfalseAlpha1.221.25
ExpandedDNSConfigtrueBeta1.261.27
ExpandedDNSConfigtrueGA1.28
ExperimentalHostUserNamespaceDefaultingfalseBeta1.51.27
ExperimentalHostUserNamespaceDefaultingfalseDeprecated1.28
InTreePluginRBDUnregisterfalseAlpha1.231.27
InTreePluginRBDUnregisterfalseDeprecated1.28
IPTablesOwnershipCleanupfalseAlpha1.251.26
IPTablesOwnershipCleanuptrueBeta1.271.27
IPTablesOwnershipCleanuptrueGA1.28
JobReadyPodsfalseAlpha1.231.23
JobReadyPodstrueBeta1.241.28
JobReadyPodstrueGA1.29
KMSv1trueDeprecated1.281.28
KMSv1falseDeprecated1.29
KMSv2falseAlpha1.251.26
KMSv2trueBeta1.271.28
KMSv2trueGA1.29
KMSv2KDFfalseBeta1.281.28
KMSv2KDFtrueGA1.29
KubeletPodResourcesfalseAlpha1.131.14
KubeletPodResourcestrueBeta1.151.27
KubeletPodResourcestrueGA1.28
KubeletPodResourcesGetAllocatablefalseAlpha1.211.22
KubeletPodResourcesGetAllocatabletrueBeta1.231.27
KubeletPodResourcesGetAllocatabletrueGA1.28
LegacyServiceAccountTokenTrackingfalseAlpha1.261.26
LegacyServiceAccountTokenTrackingtrueBeta1.271.27
LegacyServiceAccountTokenTrackingtrueGA1.28
MinimizeIPTablesRestorefalseAlpha1.261.26
MinimizeIPTablesRestoretrueBeta1.271.27
MinimizeIPTablesRestoretrueGA1.28
NodeOutOfServiceVolumeDetachfalseAlpha1.241.25
NodeOutOfServiceVolumeDetachtrueBeta1.261.27
NodeOutOfServiceVolumeDetachtrueGA1.28
ProxyTerminatingEndpointsfalseAlpha1.221.25
ProxyTerminatingEndpointstrueBeta1.261.27
ProxyTerminatingEndpointstrueGA1.28
ReadWriteOncePodfalseAlpha1.221.26
ReadWriteOncePodtrueBeta1.271.28
ReadWriteOncePodtrueGA1.29
RemainingItemCountfalseAlpha1.151.15
RemainingItemCounttrueBeta1.161.28
RemainingItemCounttrueGA1.29
RemoveSelfLinkfalseAlpha1.161.19
RemoveSelfLinktrueBeta1.201.23
RemoveSelfLinktrueGA1.24
ServerSideApplyfalseAlpha1.141.15
ServerSideApplytrueBeta1.161.21
ServerSideApplytrueGA1.22
ServerSideFieldValidationfalseAlpha1.231.24
ServerSideFieldValidationtrueBeta1.251.26
ServerSideFieldValidationtrueGA1.27
ServiceNodePortStaticSubrangefalseAlpha1.271.27
ServiceNodePortStaticSubrangetrueBeta1.281.28
ServiceNodePortStaticSubrangetrueGA1.29
SkipReadOnlyValidationGCEfalseAlpha1.281.28
SkipReadOnlyValidationGCEtrueDeprecated1.29
WatchBookmarkfalseAlpha1.151.15
WatchBookmarktrueBeta1.161.16
WatchBookmarktrueGA1.17

使用特性

特性阶段

处于 AlphaBetaGA 阶段的特性。

Alpha 特性代表:

  • 默认禁用。
  • 可能有错误,启用此特性可能会导致错误。
  • 随时可能删除对此特性的支持,恕不另行通知。
  • 在以后的软件版本中,API 可能会以不兼容的方式更改,恕不另行通知。
  • 建议将其仅用于短期测试中,因为开启特性会增加错误的风险,并且缺乏长期支持。

Beta 特性代表:

  • 通常默认启用。Beta API 组默认是被禁用的
  • 该特性已经经过良好测试。启用该特性是安全的。
  • 尽管详细信息可能会更改,但不会放弃对整体特性的支持。
  • 对象的架构或语义可能会在随后的 Beta 或稳定版本中以不兼容的方式更改。 当发生这种情况时,我们将提供迁移到下一版本的说明。此特性可能需要删除、编辑和重新创建 API 对象。 编辑过程可能需要慎重操作,因为这可能会导致依赖该特性的应用程序停机。
  • 推荐仅用于非关键业务用途,因为在后续版本中可能会发生不兼容的更改。如果你具有多个可以独立升级的,则可以放宽此限制。

General Availability(GA)特性也称为稳定特性,GA 特性代表着:

  • 此特性会一直启用;你不能禁用它。
  • 不再需要相应的特性门控。
  • 对于许多后续版本,特性的稳定版本将出现在发行的软件中。

特性门控列表

每个特性门控均用于启用或禁用某个特定的特性:

  • AdmissionWebhookMatchConditions:

    在变更性质和合法性检查性质的准入 Webhook 上启用匹配状况

  • AggregatedDiscoveryEndpoint:

    启用单个 HTTP 端点 /discovery/<version>, 支持用 ETag 进行原生 HTTP 缓存,包含 API 服务器已知的所有 APIResource。

  • AnyVolumeDataSource:

    允许使用任何自定义的资源来作为 PVC 中的 dataSource

  • APIListChunking:

    允许 API 客户端以块的形式从 API 服务器检索(LISTGET)资源。

  • APIPriorityAndFairness:

    在每个服务器上启用优先级和公平性来管理请求的并发度(由 RequestManagement 重命名而来)。

  • APIResponseCompression:

    压缩 LISTGET 请求的 API 响应。

  • APISelfSubjectReview:

    激活 SelfSubjectReview API,允许用户查看请求主体的身份认证信息。 更多细节请参阅 API 访问客户端的身份认证信息

  • APIServerIdentity:

    使用 Lease(租约)为集群中的每个 API 服务器赋予一个 ID。

  • APIServerTracing:

    在 API 服务器中添加对分布式跟踪的支持。 更多细节参阅针对 Kubernetes 系统组件的追踪

  • AppArmor:

    在 Linux 节点上为 Pod 启用 AppArmor 机制的强制访问控制。 更多细节参阅 AppArmor 教程

  • CloudControllerManagerWebhook:

    启用在云控制器管理器中的 Webhook。

  • CloudDualStackNodeIPs:

    允许在外部云驱动中通过 kubelet --node-ip 设置双协议栈。 有关更多详细信息,请参阅配置 IPv4/IPv6 双栈

  • ClusterTrustBundle:

    启用 ClusterTrustBundle 对象和 kubelet 集成。

  • ClusterTrustBundleProjection:

    clusterTrustBundle 投射卷源

  • ComponentSLIs:

    在 kubelet、kube-scheduler、kube-proxy、kube-controller-manager、cloud-controller-manager 等 Kubernetes 组件上启用 /metrics/slis 端点,从而允许你抓取健康检查指标。

  • ConsistentHTTPGetHandlers:

    使用探测器为生命周期处理程序规范化 HTTP get URL 和标头传递。

  • ConsistentListFromCache:

    允许 API 服务器从缓存中提供一致的 list 操作。

  • ContainerCheckpoint:

    启用 kubelet checkpoint API。 详情见 Kubelet Checkpoint API

  • ContextualLogging:

    当你启用这个特性门控,支持日志上下文记录的 Kubernetes 组件会为日志输出添加额外的详细内容。

  • CPUManager:

    启用容器级别的 CPU 亲和性支持,有关更多详细信息,请参见 CPU 管理策略

  • CPUManagerPolicyAlphaOptions:

    允许对 CPU 管理器策略进行微调,针对试验性的、Alpha 质量级别的选项。 此特性门控用来保护一组质量级别为 Alpha 的 CPU 管理器选项。 此特性门控永远不会被升级为 Beta 或者稳定版本。

  • CPUManagerPolicyBetaOptions:

    允许对 CPU 管理器策略进行微调,针对试验性的、Beta 质量级别的选项。 此特性门控用来保护一组质量级别为 Beta 的 CPU 管理器选项。 此特性门控永远不会被升级为稳定版本。

  • CPUManagerPolicyOptions:

    允许微调 CPU 管理器策略。

  • CRDValidationRatcheting:

    如果资源更新的冲突部分未发生变化,则启用对自定义资源的更新以包含对 OpenAPI 模式的违规条目。 详情参见验证递进

  • CronJobsScheduledAnnotation:

    将调度作业的时间设置为代表 CronJob 创建的作业上的一个 注解

  • CrossNamespaceVolumeDataSource:

    启用跨名字空间卷数据源,以允许你在 PersistentVolumeClaim 的 dataSourceRef 字段中指定一个源名字空间。

  • CSIMigrationAzureFile:

    启用封装和转换逻辑,将卷操作从 AzureFile 内嵌插件路由到 AzureFile CSI 插件。对于禁用了此特性的节点或者没有安装并配置 AzureFile CSI 插件的节点,支持回退到内嵌(in-tree)AzureFile 插件来执行卷挂载操作。 不支持回退到内嵌插件来执行卷制备操作,因为对应的 CSI 插件必须已安装且正确配置。 此特性需要启用 CSIMigration 特性标志。

  • CSIMigrationPortworx:

    启用封装和转换逻辑,将卷操作从 Portworx 内嵌插件路由到 Portworx CSI 插件。需要在集群中安装并配置 Portworx CSI 插件.

  • CSIMigrationRBD:

    启用封装和转换逻辑,将卷操作从 RBD 的内嵌插件路由到 Ceph RBD CSI 插件。 此特性要求 CSIMigration 和 csiMigrationRBD 特性标志均被启用, 且集群中安装并配置了 Ceph CSI 插件。 此标志已被弃用,以鼓励使用 InTreePluginRBDUnregister 特性标志。 后者会禁止注册内嵌的 RBD 插件。

  • CSINodeExpandSecret:

    允许在 NodeExpandVolume CSI 操作期间, 将保密的身份验证数据传递到 CSI 驱动以供后者使用。

  • CSIVolumeHealth:

    启用对节点上的 CSI 卷运行状况监控的支持。

  • CustomCPUCFSQuotaPeriod:

    使节点能够更改 kubelet 配置 中的 cpuCFSQuotaPeriod

  • CustomResourceValidationExpressions:

    启用 CRD 中的表达式语言合法性检查, 基于 x-kubernetes-validations 扩展中所书写的合法性检查规则来验证定制资源。

  • DefaultHostNetworkHostPortsInPodTemplates:

    更改何时设置 PodSpec.containers[*].ports[*].hostPort 的默认值。 默认仅在 Pod 中设置默认值。

    启用此特性意味着即使在嵌套的 PodSpec(例如 Deployment 中)中也会设置默认值, 这是以前的默认行为。

  • DevicePluginCDIDevices:

    启用设备插件 API 对 CDI 设备 ID 的支持。

  • DisableCloudProviders:

    禁用 kube-apiserverkube-controller-managerkubelet 中与 --cloud-provider 组件标志相关的所有功能。

  • DisableKubeletCloudCredentialProviders:

    禁用 kubelet 中为拉取镜像内置的凭据机制, 该凭据用于向云提供商的容器镜像仓库进行身份认证。

  • DisableNodeKubeProxyVersion:

    禁止设置 Node 的 kubeProxyVersion 字段。

  • DynamicResourceAllocation:

    启用对具有自定义参数和独立于 Pod 生命周期的资源的支持。

  • EfficientWatchResumption:

    允许将存储发起的书签(进度通知)事件传递给用户。这仅适用于 watch(监视)操作。

  • ElasticIndexedJob:

    允许通过同时改变 spec.completionsspec.parallelism 使得 spec.completions == spec.parallelism 来对带索引的 Job 执行扩容或缩容。 更多细节请参阅弹性索引 Job 文档。

  • EventedPLEG:

    启用此特性后,kubelet 能够通过 CRI 扩展从容器运行时接收容器生命周期事件。 (PLEG 是 “Pod lifecycle event generator” 的缩写,即 Pod 生命周期事件生成器)。 要使用此特性,你还需要在集群中运行的每个容器运行时中启用对容器生命周期事件的支持。 如果容器运行时未宣布支持容器生命周期事件,即使你已启用了此特性门控, kubelet 也会自动切换到原有的通用 PLEG 机制。

  • ExecProbeTimeout:

    确保 kubelet 会遵从 exec 探针的超时值设置。 此特性门控的主要目的是方便你处理现有的、依赖于已被修复的缺陷的工作负载; 该缺陷导致 Kubernetes 会忽略 exec 探针的超时值设置。 参阅就绪态探针.

  • ExpandedDNSConfig:

    在 kubelet 和 kube-apiserver 上启用后, 允许使用更多的 DNS 搜索域和搜索域列表。此功能特性需要容器运行时 (containerd v1.5.6 或更高,CRI-O v1.22 或更高)的支持。 参阅扩展 DNS 配置.

  • ExperimentalHostUserNamespaceDefaulting:

    启用主机默认的用户名字空间。 这适用于使用其他主机名字空间、主机安装的容器,或具有特权或使用特定的非名字空间功能 (例如 MKNODESYS_MODULE 等)的容器。 如果在 Docker 守护程序中启用了用户名字空间重新映射,则启用此选项。

  • GracefulNodeShutdown:

    在 kubelet 中启用体面地关闭节点的支持。 在系统关闭时,kubelet 会尝试监测该事件并体面地终止节点上运行的 Pod。 参阅体面地关闭节点以了解更多细节。

  • GracefulNodeShutdownBasedOnPodPriority:

    允许 kubelet 在体面终止节点时检查 Pod 的优先级。

  • HonorPVReclaimPolicy:

    无论 PV 和 PVC 的删除顺序如何,当持久卷回收策略为 Delete 时都要遵守该策略。更多细节参阅 PersistentVolume 删除保护终结器文档。

  • HPAContainerMetrics:

    启用 HorizontalPodAutoscaler 基于目标 Pod 中单个容器的指标进行扩缩。

  • HPAScaleToZero:

    使用自定义指标或外部指标时,可将 HorizontalPodAutoscaler 资源的 minReplicas 设置为 0。

  • ImageMaximumGCAge:

    启用 kubelet 配置字段 imageMaximumGCAge,允许管理员指定多久之后镜像将被垃圾收集。

  • InPlacePodVerticalScaling:

    启用就地 Pod 垂直扩缩。

  • InTreePluginAWSUnregister:

    在 kubelet 和卷控制器上关闭注册 aws-ebs 内嵌插件。

  • InTreePluginAzureDiskUnregister:

    在 kubelet 和卷控制器上关闭注册 azuredisk 内嵌插件。

  • InTreePluginAzureFileUnregister:

    在 kubelet 和卷控制器上关闭注册 azurefile 内嵌插件。

  • InTreePluginGCEUnregister:

    在 kubelet 和卷控制器上关闭注册 gce-pd 内嵌插件。

  • InTreePluginOpenStackUnregister:

    在 kubelet 和卷控制器上关闭注册 OpenStack cinder 内嵌插件。

  • InTreePluginPortworxUnregister:

    在 kubelet 和卷控制器上关闭注册 Portworx 内嵌插件。

  • InTreePluginRBDUnregister:

    在 kubelet 和卷控制器上关闭注册 RBD 内嵌插件。

  • InTreePluginvSphereUnregister:

    在 kubelet 和卷控制器上关闭注册 vSphere 内嵌插件。

  • IPTablesOwnershipCleanup:

    这使得 kubelet 不再创建传统的 iptables 规则。

  • JobBackoffLimitPerIndex:

    允许在索引作业中指定每个索引的最大 Pod 重试次数。

  • JobPodFailurePolicy:

    允许用户根据容器退出码和 Pod 状况来指定 Pod 失效的处理方法。

  • JobPodReplacementPolicy:

    允许你在 Job 中为终止的 Pod 指定替代 Pod。

  • JobReadyPods:

    允许跟踪状况Ready 的 Pod 的个数。Ready 的 Pod 记录在 Job 对象的 status 字段中。

  • KMSv1:

    启用 KMS v1 API 以实现静态加密。 详情参见使用 KMS 驱动进行数据加密

  • KMSv2:

    启用 KMS v2 API 以实现静态加密。 详情参见使用 KMS 驱动进行数据加密

  • KMSv2KDF:

    启用 KMS v2 以生成一次性数据加密密钥。 详情参见使用 KMS 驱动进行数据加密。 如果 KMSv2 特性门控在你的集群未被启用,则 KMSv2KDF 特性门控的值不会产生任何影响。

  • KubeletCgroupDriverFromCRI:

    启用检测来自 CRI 的 kubelet cgroup 驱动配置选项。你可以在支持该特性门控的 kubelet 节点上使用此特性门控, 也可以在支持 RuntimeConfig CRI 调用的 CRI 容器运行时所在节点上使用此特性门控。 如果 CRI 和 kubelet 都支持此特性,kubelet 将忽略 cgroupDriver 配置设置(或已弃用的 --cgroup-driver 命令行参数)。 如果你启用此特性门控但容器运行时不支持它,则 kubelet 将回退到使用通过 cgroupDriver 配置设置进行配置的驱动。 详情参见配置 cgroup 驱动

  • KubeletInUserNamespace:

    支持在用户名字空间里运行 kubelet。 请参见以非 root 用户身份运行 Kubernetes 节点组件

  • KubeletPodResources:

    启用 kubelet 上 Pod 资源 gRPC 端点。 详情参见支持设备监控

  • KubeletPodResourcesDynamicResources:

    扩展 kubelet 的 Pod 资源 gRPC 端点以包括通过 DynamicResourceAllocation API 在 ResourceClaims 中分配的资源。 详情参见资源分配报告。 包含有关可分配资源的信息,使客户端能够正确跟踪节点上的可用计算资源。

  • KubeletPodResourcesGet:

    在 kubelet 上为 Pod 资源启用 Get gRPC 端点。 此 API 增强了资源分配报告

  • KubeletPodResourcesGetAllocatable:

    启用 kubelet 的 Pod 资源的 GetAllocatableResources 功能。 此 API 增强了资源分配报告

  • KubeletSeparateDiskGC:

    启用 kubelet,即使在容器镜像和容器位于独立文件系统的情况下,也能进行垃圾回收。

  • KubeletTracing:

    新增在 kubelet 中对分布式追踪的支持。 启用时,kubelet CRI 接口和经身份验证的 http 服务器被插桩以生成 OpenTelemetry 追踪 span。 详情参见追踪 Kubernetes 系统组件

  • KubeProxyDrainingTerminatingNodes:

    externalTrafficPolicy: Cluster 服务实现终止节点的连接排空。

  • LegacyServiceAccountTokenCleanUp:

    当服务账号令牌在指定时间内(默认为一年)未被使用时, 允许基于 Secret 清理服务账号令牌

  • LegacyServiceAccountTokenTracking:

    跟踪基于 Secret 的服务账号令牌的使用情况。

  • LoadBalancerIPMode:

    当 Service 的 typeLoadBalancer 时,可设置该 Service 的 ipMode。 更多细节请参阅指定负载均衡器状态的 IPMode

  • LocalStorageCapacityIsolationFSQuotaMonitoring:

    如果本地临时存储启用了 LocalStorageCapacityIsolation,并且 emptyDir 卷所使用的文件系统支持项目配额, 并且启用了这些配额,将使用项目配额来监视 emptyDir 卷的存储消耗而不是遍历文件系统, 以此获得更好的性能和准确性。

  • LogarithmicScaleDown:

    启用 Pod 的半随机选择,控制器将根据 Pod 时间戳的对数桶按比例缩小来驱逐 Pod。

  • LoggingAlphaOptions:

    允许微调实验性的、Alpha 级别的日志选项。

  • LoggingBetaOptions:

    允许微调实验性的、Beta 级别的日志选项。

  • MatchLabelKeysInPodAffinity:

    Pod(反)亲和性启用 matchLabelKeysmismatchLabelKeys 字段。

  • MatchLabelKeysInPodTopologySpread:

    Pod 拓扑分布约束启用 matchLabelKeys 字段。

  • MaxUnavailableStatefulSet:

    允许为 StatefulSet 的滚动更新策略设置 maxUnavailable 字段。此字段指定更新过程中不可用 Pod 个数的上限。

  • MemoryManager:

    允许基于 NUMA 拓扑为容器设置内存亲和性。

  • MemoryQoS:

    使用 cgroup v2 内存控制器为 Pod 或容器启用内存保护和使用限制。

  • MinDomainsInPodTopologySpread:

    Pod 拓扑分布约束中启用 minDomains

  • MinimizeIPTablesRestore:

    启用 kube-proxy iptables 模式中的性能改进逻辑。

  • MultiCIDRServiceAllocator:

    使用 IPAddress 对象跟踪为 Service 的集群 IP 分配的 IP 地址。

  • NewVolumeManagerReconstruction:

    在 kubelet 启动期间启用改进的挂载卷的发现。由于这段代码已经进行了重大重构, 我们允许用户不采用这一逻辑,以免 kubelet 在启动时被卡住,或者未能为已终止的 Pod 卸载卷。 请注意,此重构是作为 Kubernetes 1.25 中的 SELinuxMountReadWriteOncePod Alpha 特性门控的一部分完成的。

    允许使用 nftables 模式运行 kube-proxy。

  • NodeInclusionPolicyInPodTopologySpread:

    在计算 Pod 拓扑分布偏差时允许在 Pod 拓扑分布约束中使用 nodeAffinityPolicynodeTaintsPolicy

  • NodeLogQuery:

    允许使用 /logs 端点来查询节点服务的日志。

  • NodeOutOfServiceVolumeDetach:

    当使用 node.kubernetes.io/out-of-service 污点将节点标记为无法提供服务时,节点上不能容忍这个污点的 Pod 将被强制删除, 并且针对此节点上被终止的 Pod 将立即执行解除卷挂接操作。 被删除的 Pod 可以很快在不同的节点上恢复。

  • NodeSwap:

    允许 kubelet 为节点上的 Kubernetes 工作负载分配交换内存。 必须将 KubeletConfiguration.failSwapOn 设置为 false 才能使用此能力。 更多细节请参见交换内存

  • OpenAPIEnums:

    允许在从 API 服务器返回的 spec 中填充 OpenAPI 模式的 "enum" 字段。

  • PDBUnhealthyPodEvictionPolicy:

    启用 PodDisruptionBudgetunhealthyPodEvictionPolicy 字段。 此字段指定何时应考虑驱逐不健康的 Pod。 更多细节请参阅不健康 Pod 驱逐策略

  • PersistentVolumeLastPhaseTransitionTime:

    为 PersistentVolume 添加一个新字段,用于保存卷上一次转换阶段的时间戳。

  • PodAndContainerStatsFromCRI:

    将 kubelet 配置为从 CRI 容器运行时收集容器和 Pod 的统计信息,而不是从 cAdvisor 收集统计信息。 从 1.26 版本开始,这还包括从 CRI 收集指标并通过 /metrics/cadvisor 进行发布(而不是直接由 cAdvisor 发布)。

  • PodDeletionCost:

    启用 Pod 删除开销特性, 允许用户影响 ReplicaSet 的缩容顺序。

  • PodDisruptionConditions:

    启用支持追加一个专用的 Pod 状况,以表示 Pod 由于某个干扰正在被删除。

  • PodHostIPs:

    为 Pod 和 downward API 启用 status.hostIPs 字段。此字段允许你将主机 IP 地址暴露给工作负载。

  • PodIndexLabel:

    在创建新的 Pod 时允许 Job 控制器和 StatefulSet 控制器将 Pod 索引添加为标签。 详情参见 Job 完成模式文档StatefulSet Pod 索引标签文档

  • PodLifecycleSleepAction:

    在 Container 生命周期钩子中启用 sleep 操作。

  • PodReadyToStartContainersCondition:

    使得 kubelet 能在 Pod 上标记 PodReadyToStartContainers 状况。

    此特性门控先前称为 PodHasNetworkCondition,关联的状况称为 PodHasNetwork

  • PodSchedulingReadiness:

    允许设置 schedulingGates 字段以控制 Pod 的调度就绪状态

  • ProcMountType:

    允许容器通过设置 SecurityContext 的 procMount 字段来控制对 proc 类型的挂载方式。

  • ProxyTerminatingEndpoints:

    ExternalTrafficPolicy=Local 时,允许 kube-proxy 处理终止过程中的端点。

  • QOSReserved:

    允许在 QoS 层面预留资源,避免低 QoS 级别的 Pod 占用高 QoS 级别所请求的资源(当前只适用于内存)。

  • ReadWriteOncePod:

    允许使用 ReadWriteOncePod 访问模式的 PersistentVolume。

  • RecoverVolumeExpansionFailure:

    允许用户编辑自己的 PVC 来缩容,以便从之前卷扩容引发的失败中恢复。 更多细节可参见处理扩充卷过程中的失败

  • RemainingItemCount:

    允许 API 服务器在分块列表请求 的响应中显示剩余条目的个数。

  • RemoveSelfLink:

    将所有对象和集合的 .metadata.selfLink 字段设置为空(空字符串)。 此字段自 Kubernetes v1.16 版本起已被弃用。 启用此特性后,.metadata.selfLink 字段仍然是 Kubernetes API 的一部分,但始终不设置。

  • SchedulerQueueingHints:

    启用调度器的排队提示增强功能, 有助于减少无效的重新排队。 调度器会在集群中发生可能导致 Pod 被重新调度的变化时,尝试重新进行 Pod 的调度。 排队提示是一些内部信号, 用于帮助调度器基于先前的调度尝试来筛选集群中与未调度的 Pod 相关的变化。

  • SecurityContextDeny:

    此门控表示 SecurityContextDeny 准入控制器已弃用。

  • SELinuxMountReadWriteOncePod:

    通过允许 kubelet 直接用正确的 SELinux 标签为 Pod 挂载卷而不是以递归方式更改这些卷上的每个文件来加速容器启动。 最初的实现侧重 ReadWriteOncePod 卷。

  • SeparateTaintEvictionController:

    允许运行 TaintEvictionController,该控制器可在 NodeLifecycleController 之外执行基于污点的驱逐。 此特性启用时,用户可以在 kube-controller-manager 上设置 --controllers=-taint-eviction-controller 标志, 可选择禁用基于污点的驱逐。

  • ServerSideApply:

    在 API 服务器上启用服务器端应用(SSA)

  • ServerSideFieldValidation:

    启用服务器端字段验证。这意味着资源模式的验证发生在 API 服务器端而不是客户端 (例如,kubectl createkubectl apply 命令行)。

  • ServiceAccountTokenJTI:

    控制是否将 JTI(UUID)嵌入到生成的服务账号令牌中, 以及对于这些令牌未来的请求,是否将这些 JTI 记录到 Kubernetes 审计日志中。

  • ServiceAccountTokenNodeBinding:

    控制 API 服务器是否允许将服务账号令牌绑定到 Node 对象。

  • ServiceAccountTokenNodeBindingValidation:

    控制 API 服务器是否会验证服务账号令牌中的 Node 引用。

  • ServiceAccountTokenPodNodeInfo:

    控制 API 服务器在颁发绑定到 Pod 对象的服务账号令牌时, 是否嵌入关联 Node 的名称和 uid

  • ServiceNodePortStaticSubrange:

    允许对 NodePort Service 使用不同的端口分配策略。 有关更多详细信息, 请参阅保留 NodePort 范围以避免冲突

  • SidecarContainers:

    允许将 Init 容器的 restartPolicy 设置为 Always, 以便该容器成为一个边车容器(可重启的 Init 容器)。 详情参见边车容器和 restartPolicy

  • SizeMemoryBackedVolumes:

    允许 kubelet 检查基于内存制备的卷的尺寸约束(目前主要针对 emptyDir 卷)。

  • SkipReadOnlyValidationGCE:

    跳过对 GCE 的验证,将在下个版本中启用。

  • StableLoadBalancerNodeSet:

    允许服务控制器(KCCM)减少根据节点状态变化来对负载均衡器作重新配置的操作。

  • StatefulSetAutoDeletePVC:

    允许使用可选字段 .spec.persistentVolumeClaimRetentionPolicy, 以便根据 StatefulSet 的生命周期来控制 PVC 的删除。 详情参见 PersistentVolumeClaim 保留

  • StatefulSetStartOrdinal:

    允许在 StatefulSet 中配置起始序号。 详情参见起始序号

  • StorageVersionAPI:

    启用存储版本 API

  • StorageVersionHash:

    允许 API 服务器在版本发现中公开存储版本的哈希值。

  • StructuredAuthenticationConfiguration:

    为 API 服务器启用结构化身份验证配置

  • StructuredAuthorizationConfiguration:

    启用结构化授权配置,以便集群管理员可以在 API 服务器处理程序链中指定多个授权 Webhook

  • TopologyAwareHints:

    在 EndpointSlice 中启用基于拓扑提示的拓扑感知路由。 更多细节参见拓扑感知路由

  • TopologyManagerPolicyAlphaOptions:

    允许微调拓扑管理器策略的实验性的、Alpha 质量的选项。 此特性门控守护一组质量级别为 Alpha 的拓扑管理器选项。 此特性门控绝对不会进阶至 Beta 或稳定版。

  • TopologyManagerPolicyBetaOptions:

    允许微调拓扑管理器策略的实验性的、Beta 质量的选项。 此特性门控守护一组质量级别为 Beta 的拓扑管理器选项。 此特性门控绝对不会进阶至稳定版。

  • TopologyManagerPolicyOptions:

    允许对拓扑管理器策略进行微调。

  • TranslateStreamCloseWebsocketRequests:

    允许从请求 v5 子协议版本的客户端处通过 WebSocket 流式传输远程命令子协议(execcpattach)。

  • UnauthenticatedHTTP2DOSMitigation:

    启用了针对未认证客户端的 HTTP/2 拒绝服务(DoS)防护措施。 Kubernetes v1.28.0 至 v1.28.2 版本并未包括这项特性门控。

  • UnknownVersionInteroperabilityProxy:

    当存在多个不同版本的 kube-apiserver 时,将资源请求代理到正确的对等 kube-apiserver。 更多信息请参见混合版本代理

  • UserNamespacesPodSecurityStandards:

    启用 Pod 安全标准策略的放宽措施,适用于在命名空间中运行的 Pod。 你需要在集群的所有节点上统一设置此特性门控,并且必须启用 UserNamespacesSupport 才能使用此功能。

  • UserNamespacesSupport:

    为 Pod 启用用户命名空间支持。

  • ValidatingAdmissionPolicy:

    在准入控制中启用 ValidatingAdmissionPolicy 以支持 CEL 合法性检查。

  • VolumeAttributesClass:

    启用对 VolumeAttributesClasses 的支持。 更多细节参见卷属性类

  • VolumeCapacityPriority:

    启用基于可用 PV 容量对不同拓扑域下节点进行优先级排序的支持。

  • WatchBookmark:

    启用对监听书签事件的支持。

  • WatchList:

    启用对监听请求中流式传输对象初始状态的支持。

  • WindowsHostNetwork:

    启用对 Windows 容器接入主机网络名字空间的支持。

  • WinDSR:

    允许 kube-proxy 为 Windows 创建 DSR(Direct Server Return,直接服务器返回)负载均衡器。

  • WinOverlay:

    允许 kube-proxy 以覆盖模式在 Windows 上运行。

  • ZeroLimitedNominalConcurrencyShares:

    允许 API 服务器中的优先级和公平性使用 limited 部分的 nominalConcurrencyShares 字段的零值作为优先级。

接下来

  • Kubernetes 的弃用策略介绍了项目针对已移除特性和组件的处理方法。
  • 从 Kubernetes 1.24 开始,默认不启用新的 Beta API。 启用 Beta 功能时,还需要启用所有关联的 API 资源。 例如:要启用一个特定资源,如 storage.k8s.io/v1beta1/csistoragecapacities, 请设置 --runtime-config=storage.k8s.io/v1beta1/csistoragecapacities。 有关命令行标志的更多详细信息,请参阅 API 版本控制

6.13.2 - 特性门控(已移除)

本页包含了已移除的特性门控的列表。本页的信息仅供参考。 已移除的特性门控不同于正式发布(GA)或废弃的特性门控,因为已移除的特性门控将不再被视为有效的特性门控。 然而,正式发布或废弃的特性门控仍然能被对应的 Kubernetes 组件识别,这些特性门控在集群中不会造成任何行为差异。

有关 Kubernetes 组件仍可识别的特性门控,请参阅 Alpha 和 Beta 状态的特性门控已毕业和已废弃的特性门控

已移除的特性门控

在下表中,

  • “开始(From)” 列包含了引入某个特性或其发布状态发生变更时的 Kubernetes 版本。
  • “结束(To)” 列(如果不为空)包含你仍然可以使用某个特性门控的最后一个 Kubernetes 版本。 如果对应特性处于 “废弃” 或 “GA” 状态,则 “结束(To)” 列是该特性被移除时的 Kubernetes 版本。
已移除的特性门控
FeatureDefaultStageFromTo
AcceleratorsfalseAlpha1.61.10
AcceleratorsDeprecated1.111.11
AdvancedAuditingfalseAlpha1.71.7
AdvancedAuditingtrueBeta1.81.11
AdvancedAuditingtrueGA1.121.27
AffinityInAnnotationsfalseAlpha1.61.7
AffinityInAnnotationsDeprecated1.81.8
AllowExtTrafficLocalEndpointsfalseBeta1.41.6
AllowExtTrafficLocalEndpointstrueGA1.71.9
AllowInsecureBackendProxytrueBeta1.171.20
AllowInsecureBackendProxytrueGA1.211.25
AttachVolumeLimitfalseAlpha1.111.11
AttachVolumeLimittrueBeta1.121.16
AttachVolumeLimittrueGA1.171.21
BalanceAttachedNodeVolumesfalseAlpha1.111.21
BalanceAttachedNodeVolumesfalseDeprecated1.221.22
BlockVolumefalseAlpha1.91.12
BlockVolumetrueBeta1.131.17
BlockVolumetrueGA1.181.21
BoundServiceAccountTokenVolumefalseAlpha1.131.20
BoundServiceAccountTokenVolumetrueBeta1.211.21
BoundServiceAccountTokenVolumetrueGA1.221.23
ConfigurableFSGroupPolicyfalseAlpha1.181.19
ConfigurableFSGroupPolicytrueBeta1.201.22
ConfigurableFSGroupPolicytrueGA1.231.25
ControllerManagerLeaderMigrationfalseAlpha1.211.21
ControllerManagerLeaderMigrationtrueBeta1.221.23
ControllerManagerLeaderMigrationtrueGA1.241.26
CRIContainerLogRotationfalseAlpha1.101.10
CRIContainerLogRotationtrueBeta1.111.20
CRIContainerLogRotationtrueGA1.211.22
CronJobControllerV2falseAlpha1.201.20
CronJobControllerV2trueBeta1.211.21
CronJobControllerV2trueGA1.221.23
CronJobTimeZonefalseAlpha1.241.24
CronJobTimeZonetrueBeta1.251.26
CronJobTimeZonetrueGA1.271.28
CSIBlockVolumefalseAlpha1.111.13
CSIBlockVolumetrueBeta1.141.17
CSIBlockVolumetrueGA1.181.21
CSIDriverRegistryfalseAlpha1.121.13
CSIDriverRegistrytrueBeta1.141.17
CSIDriverRegistrytrueGA1.181.21
CSIInlineVolumefalseAlpha1.151.15
CSIInlineVolumetrueBeta1.161.24
CSIInlineVolumetrueGA1.251.26
CSIMigrationfalseAlpha1.141.16
CSIMigrationtrueBeta1.171.24
CSIMigrationtrueGA1.251.26
CSIMigrationAWSfalseAlpha1.141.16
CSIMigrationAWSfalseBeta1.171.22
CSIMigrationAWStrueBeta1.231.24
CSIMigrationAWStrueGA1.251.26
CSIMigrationAWSCompletefalseAlpha1.171.20
CSIMigrationAWSCompleteDeprecated1.211.21
CSIMigrationAzureDiskfalseAlpha1.151.18
CSIMigrationAzureDiskfalseBeta1.191.22
CSIMigrationAzureDisktrueBeta1.231.23
CSIMigrationAzureDisktrueGA1.241.26
CSIMigrationAzureDiskCompletefalseAlpha1.171.20
CSIMigrationAzureDiskCompleteDeprecated1.211.21
CSIMigrationAzureFileCompletefalseAlpha1.171.20
CSIMigrationAzureFileCompleteDeprecated1.211.21
CSIMigrationGCEfalseAlpha1.141.16
CSIMigrationGCEfalseBeta1.171.22
CSIMigrationGCEtrueBeta1.231.24
CSIMigrationGCEtrueGA1.251.27
CSIMigrationGCECompletefalseAlpha1.171.20
CSIMigrationGCECompleteDeprecated1.211.21
CSIMigrationOpenStackfalseAlpha1.141.17
CSIMigrationOpenStacktrueBeta1.181.23
CSIMigrationOpenStacktrueGA1.241.25
CSIMigrationOpenStackCompletefalseAlpha1.171.20
CSIMigrationOpenStackCompleteDeprecated1.211.21
CSIMigrationvSpherefalseAlpha1.181.18
CSIMigrationvSpherefalseBeta1.191.24
CSIMigrationvSpheretrueBeta1.251.25
CSIMigrationvSpheretrueGA1.261.28
CSIMigrationvSphereCompletefalseBeta1.191.21
CSIMigrationvSphereCompleteDeprecated1.221.22
CSINodeInfofalseAlpha1.121.13
CSINodeInfotrueBeta1.141.16
CSINodeInfotrueGA1.171.22
CSIPersistentVolumefalseAlpha1.91.9
CSIPersistentVolumetrueBeta1.101.12
CSIPersistentVolumetrueGA1.131.16
CSIServiceAccountTokenfalseAlpha1.201.20
CSIServiceAccountTokentrueBeta1.211.21
CSIServiceAccountTokentrueGA1.221.24
CSIStorageCapacityfalseAlpha1.191.20
CSIStorageCapacitytrueBeta1.211.23
CSIStorageCapacitytrueGA1.241.27
CSIVolumeFSGroupPolicyfalseAlpha1.191.19
CSIVolumeFSGroupPolicytrueBeta1.201.22
CSIVolumeFSGroupPolicytrueGA1.231.25
CSRDurationtrueBeta1.221.23
CSRDurationtrueGA1.241.25
CustomPodDNSfalseAlpha1.91.9
CustomPodDNStrueBeta1.101.13
CustomPodDNStrueGA1.141.16
CustomResourceDefaultingfalseAlpha1.151.15
CustomResourceDefaultingtrueBeta1.161.16
CustomResourceDefaultingtrueGA1.171.18
CustomResourcePublishOpenAPIfalseAlpha1.141.14
CustomResourcePublishOpenAPItrueBeta1.151.15
CustomResourcePublishOpenAPItrueGA1.161.18
CustomResourceSubresourcesfalseAlpha1.101.10
CustomResourceSubresourcestrueBeta1.111.15
CustomResourceSubresourcestrueGA1.161.18
CustomResourceValidationfalseAlpha1.81.8
CustomResourceValidationtrueBeta1.91.15
CustomResourceValidationtrueGA1.161.18
CustomResourceWebhookConversionfalseAlpha1.131.14
CustomResourceWebhookConversiontrueBeta1.151.15
CustomResourceWebhookConversiontrueGA1.161.18
DaemonSetUpdateSurgefalseAlpha1.211.21
DaemonSetUpdateSurgetrueBeta1.221.24
DaemonSetUpdateSurgetrueGA1.251.28
DefaultPodTopologySpreadfalseAlpha1.191.19
DefaultPodTopologySpreadtrueBeta1.201.23
DefaultPodTopologySpreadtrueGA1.241.25
DelegateFSGroupToCSIDriverfalseAlpha1.221.22
DelegateFSGroupToCSIDrivertrueBeta1.231.25
DelegateFSGroupToCSIDrivertrueGA1.261.27
DevicePluginsfalseAlpha1.81.9
DevicePluginstrueBeta1.101.25
DevicePluginstrueGA1.261.27
DisableAcceleratorUsageMetricsfalseAlpha1.191.19
DisableAcceleratorUsageMetricstrueBeta1.201.24
DisableAcceleratorUsageMetricstrueGA1.251.27
DownwardAPIHugePagesfalseAlpha1.201.20
DownwardAPIHugePagesfalseBeta1.211.21
DownwardAPIHugePagestrueBeta1.221.26
DownwardAPIHugePagestrueGA1.271.28
DryRunfalseAlpha1.121.12
DryRuntrueBeta1.131.18
DryRuntrueGA1.191.27
DynamicAuditingfalseAlpha1.131.18
DynamicAuditingDeprecated1.191.19
DynamicKubeletConfigfalseAlpha1.41.10
DynamicKubeletConfigtrueBeta1.111.21
DynamicKubeletConfigfalseDeprecated1.221.25
DynamicProvisioningSchedulingfalseAlpha1.111.11
DynamicProvisioningSchedulingDeprecated1.12
DynamicVolumeProvisioningtrueAlpha1.31.7
DynamicVolumeProvisioningtrueGA1.81.12
EnableAggregatedDiscoveryTimeouttrueDeprecated1.161.17
EnableEquivalenceClassCachefalseAlpha1.81.12
EnableEquivalenceClassCacheDeprecated1.131.23
EndpointSlicefalseAlpha1.161.16
EndpointSlicefalseBeta1.171.17
EndpointSlicetrueBeta1.181.20
EndpointSlicetrueGA1.211.24
EndpointSliceNodeNamefalseAlpha1.201.20
EndpointSliceNodeNametrueGA1.211.24
EndpointSliceProxyingfalseAlpha1.181.18
EndpointSliceProxyingtrueBeta1.191.21
EndpointSliceProxyingtrueGA1.221.24
EndpointSliceTerminatingConditionfalseAlpha1.201.21
EndpointSliceTerminatingConditiontrueBeta1.221.25
EndpointSliceTerminatingConditiontrueGA1.261.27
EphemeralContainersfalseAlpha1.161.22
EphemeralContainerstrueBeta1.231.24
EphemeralContainerstrueGA1.251.26
EvenPodsSpreadfalseAlpha1.161.17
EvenPodsSpreadtrueBeta1.181.18
EvenPodsSpreadtrueGA1.191.21
ExpandCSIVolumesfalseAlpha1.141.15
ExpandCSIVolumestrueBeta1.161.23
ExpandCSIVolumestrueGA1.241.26
ExpandInUsePersistentVolumesfalseAlpha1.111.14
ExpandInUsePersistentVolumestrueBeta1.151.23
ExpandInUsePersistentVolumestrueGA1.241.26
ExpandPersistentVolumesfalseAlpha1.81.10
ExpandPersistentVolumestrueBeta1.111.23
ExpandPersistentVolumestrueGA1.241.26
ExperimentalCriticalPodAnnotationfalseAlpha1.51.12
ExperimentalCriticalPodAnnotationfalseDeprecated1.131.16
ExternalPolicyForExternalIPtrueGA1.181.22
GCERegionalPersistentDisktrueBeta1.101.12
GCERegionalPersistentDisktrueGA1.131.16
GenericEphemeralVolumefalseAlpha1.191.20
GenericEphemeralVolumetrueBeta1.211.22
GenericEphemeralVolumetrueGA1.231.24
GRPCContainerProbefalseAlpha1.231.23
GRPCContainerProbetrueBeta1.241.26
GRPCContainerProbetrueGA1.271.28
HugePagesfalseAlpha1.81.9
HugePagestrueBeta1.101.13
HugePagestrueGA1.141.16
HugePageStorageMediumSizefalseAlpha1.181.18
HugePageStorageMediumSizetrueBeta1.191.21
HugePageStorageMediumSizetrueGA1.221.24
HyperVContainerfalseAlpha1.101.19
HyperVContainerfalseDeprecated1.201.20
IdentifyPodOSfalseAlpha1.231.23
IdentifyPodOStrueBeta1.241.24
IdentifyPodOStrueGA1.251.26
ImmutableEphemeralVolumesfalseAlpha1.181.18
ImmutableEphemeralVolumestrueBeta1.191.20
ImmutableEphemeralVolumestrueGA1.211.24
IndexedJobfalseAlpha1.211.21
IndexedJobtrueBeta1.221.23
IndexedJobtrueGA1.241.25
IngressClassNamespacedParamsfalseAlpha1.211.21
IngressClassNamespacedParamstrueBeta1.221.22
IngressClassNamespacedParamstrueGA1.231.24
InitializersfalseAlpha1.71.13
InitializersDeprecated1.141.14
IPv6DualStackfalseAlpha1.151.20
IPv6DualStacktrueBeta1.211.22
IPv6DualStacktrueGA1.231.24
JobMutableNodeSchedulingDirectivestrueBeta1.231.26
JobMutableNodeSchedulingDirectivestrueGA1.271.28
JobTrackingWithFinalizersfalseAlpha1.221.22
JobTrackingWithFinalizersfalseBeta1.231.24
JobTrackingWithFinalizerstrueBeta1.251.25
JobTrackingWithFinalizerstrueGA1.261.28
KubeletConfigFilefalseAlpha1.81.9
KubeletConfigFileDeprecated1.101.10
KubeletCredentialProvidersfalseAlpha1.201.23
KubeletCredentialProviderstrueBeta1.241.25
KubeletCredentialProviderstrueGA1.261.28
KubeletPluginsWatcherfalseAlpha1.111.11
KubeletPluginsWatchertrueBeta1.121.12
KubeletPluginsWatchertrueGA1.131.16
LegacyNodeRoleBehaviorfalseAlpha1.161.18
LegacyNodeRoleBehaviortrueBeta1.191.20
LegacyNodeRoleBehaviorfalseGA1.211.22
LegacyServiceAccountTokenNoAutoGenerationtrueBeta1.241.25
LegacyServiceAccountTokenNoAutoGenerationtrueGA1.261.28
LocalStorageCapacityIsolationfalseAlpha1.71.9
LocalStorageCapacityIsolationtrueBeta1.101.24
LocalStorageCapacityIsolationtrueGA1.251.26
MixedProtocolLBServicefalseAlpha1.201.23
MixedProtocolLBServicetrueBeta1.241.25
MixedProtocolLBServicetrueGA1.261.27
MountContainersfalseAlpha1.91.16
MountContainersfalseDeprecated1.171.17
MountPropagationfalseAlpha1.81.9
MountPropagationtrueBeta1.101.11
MountPropagationtrueGA1.121.14
MultiCIDRRangeAllocatorfalseAlpha1.251.28
NamespaceDefaultLabelNametrueBeta1.211.21
NamespaceDefaultLabelNametrueGA1.221.23
NetworkPolicyEndPortfalseAlpha1.211.21
NetworkPolicyEndPorttrueBeta1.221.24
NetworkPolicyEndPorttrueGA1.251.26
NetworkPolicyStatusfalseAlpha1.241.27
NodeDisruptionExclusionfalseAlpha1.161.18
NodeDisruptionExclusiontrueBeta1.191.20
NodeDisruptionExclusiontrueGA1.211.22
NodeLeasefalseAlpha1.121.13
NodeLeasetrueBeta1.141.16
NodeLeasetrueGA1.171.23
NonPreemptingPriorityfalseAlpha1.151.18
NonPreemptingPrioritytrueBeta1.191.23
NonPreemptingPrioritytrueGA1.241.25
OpenAPIV3falseAlpha1.231.23
OpenAPIV3trueBeta1.241.26
OpenAPIV3trueGA1.271.28
PersistentLocalVolumesfalseAlpha1.71.9
PersistentLocalVolumestrueBeta1.101.13
PersistentLocalVolumestrueGA1.141.16
PodAffinityNamespaceSelectorfalseAlpha1.211.21
PodAffinityNamespaceSelectortrueBeta1.221.23
PodAffinityNamespaceSelectortrueGA1.241.25
PodDisruptionBudgetfalseAlpha1.31.4
PodDisruptionBudgettrueBeta1.51.20
PodDisruptionBudgettrueGA1.211.25
PodHasNetworkConditionfalseAlpha1.251.27
PodOverheadfalseAlpha1.161.17
PodOverheadtrueBeta1.181.23
PodOverheadtrueGA1.241.25
PodPriorityfalseAlpha1.81.10
PodPrioritytrueBeta1.111.13
PodPrioritytrueGA1.141.18
PodReadinessGatesfalseAlpha1.111.11
PodReadinessGatestrueBeta1.121.13
PodReadinessGatestrueGA1.141.16
PodSecurityfalseAlpha1.221.22
PodSecuritytrueBeta1.231.24
PodSecuritytrueGA1.251.27
PodShareProcessNamespacefalseAlpha1.101.11
PodShareProcessNamespacetrueBeta1.121.16
PodShareProcessNamespacetrueGA1.171.19
PreferNominatedNodefalseAlpha1.211.21
PreferNominatedNodetrueBeta1.221.23
PreferNominatedNodetrueGA1.241.25
ProbeTerminationGracePeriodfalseAlpha1.211.21
ProbeTerminationGracePeriodfalseBeta1.221.24
ProbeTerminationGracePeriodtrueBeta1.251.27
ProbeTerminationGracePeriodtrueGA1.281.28
PVCProtectionfalseAlpha1.91.9
PVCProtectionDeprecated1.101.10
ReadOnlyAPIDataVolumestrueBeta1.81.9
ReadOnlyAPIDataVolumesGA1.101.10
RequestManagementfalseAlpha1.151.16
RequestManagementDeprecated1.171.17
ResourceLimitsPriorityFunctionfalseAlpha1.91.18
ResourceLimitsPriorityFunctionDeprecated1.191.19
ResourceQuotaScopeSelectorsfalseAlpha1.111.11
ResourceQuotaScopeSelectorstrueBeta1.121.16
ResourceQuotaScopeSelectorstrueGA1.171.18
ScheduleDaemonSetPodsfalseAlpha1.111.11
ScheduleDaemonSetPodstrueBeta1.121.16
ScheduleDaemonSetPodstrueGA1.171.18
SCTPSupportfalseAlpha1.121.18
SCTPSupporttrueBeta1.191.19
SCTPSupporttrueGA1.201.22
SeccompDefaultfalseAlpha1.221.24
SeccompDefaulttrueBeta1.251.26
SeccompDefaulttrueGA1.271.28
SelectorIndexfalseAlpha1.181.18
SelectorIndextrueBeta1.191.19
SelectorIndextrueGA1.201.25
ServiceAccountIssuerDiscoveryfalseAlpha1.181.19
ServiceAccountIssuerDiscoverytrueBeta1.201.20
ServiceAccountIssuerDiscoverytrueGA1.211.23
ServiceAppProtocolfalseAlpha1.181.18
ServiceAppProtocoltrueBeta1.191.19
ServiceAppProtocoltrueGA1.201.22
ServiceInternalTrafficPolicyfalseAlpha1.211.21
ServiceInternalTrafficPolicytrueBeta1.221.25
ServiceInternalTrafficPolicytrueGA1.261.27
ServiceIPStaticSubrangefalseAlpha1.241.24
ServiceIPStaticSubrangetrueBeta1.251.25
ServiceIPStaticSubrangetrueGA1.261.27
ServiceLBNodePortControlfalseAlpha1.201.21
ServiceLBNodePortControltrueBeta1.221.23
ServiceLBNodePortControltrueGA1.241.25
ServiceLoadBalancerClassfalseAlpha1.211.21
ServiceLoadBalancerClasstrueBeta1.221.23
ServiceLoadBalancerClasstrueGA1.241.25
ServiceLoadBalancerFinalizerfalseAlpha1.151.15
ServiceLoadBalancerFinalizertrueBeta1.161.16
ServiceLoadBalancerFinalizertrueGA1.171.20
ServiceNodeExclusionfalseAlpha1.81.18
ServiceNodeExclusiontrueBeta1.191.20
ServiceNodeExclusiontrueGA1.211.22
ServiceTopologyfalseAlpha1.171.19
ServiceTopologyfalseDeprecated1.201.22
SetHostnameAsFQDNfalseAlpha1.191.19
SetHostnameAsFQDNtrueBeta1.201.21
SetHostnameAsFQDNtrueGA1.221.24
StartupProbefalseAlpha1.161.17
StartupProbetrueBeta1.181.19
StartupProbetrueGA1.201.23
StatefulSetMinReadySecondsfalseAlpha1.221.22
StatefulSetMinReadySecondstrueBeta1.231.24
StatefulSetMinReadySecondstrueGA1.251.26
StorageObjectInUseProtectiontrueBeta1.101.10
StorageObjectInUseProtectiontrueGA1.111.24
StreamingProxyRedirectsfalseBeta1.51.5
StreamingProxyRedirectstrueBeta1.61.17
StreamingProxyRedirectstrueDeprecated1.181.21
StreamingProxyRedirectsfalseDeprecated1.221.24
SupportIPVSProxyModefalseAlpha1.81.8
SupportIPVSProxyModefalseBeta1.91.9
SupportIPVSProxyModetrueBeta1.101.10
SupportIPVSProxyModetrueGA1.111.20
SupportNodePidsLimitfalseAlpha1.141.14
SupportNodePidsLimittrueBeta1.151.19
SupportNodePidsLimittrueGA1.201.23
SupportPodPidsLimitfalseAlpha1.101.13
SupportPodPidsLimittrueBeta1.141.19
SupportPodPidsLimittrueGA1.201.23
SuspendJobfalseAlpha1.211.21
SuspendJobtrueBeta1.221.23
SuspendJobtrueGA1.241.25
SysctlstrueBeta1.111.20
SysctlstrueGA1.211.22
TaintBasedEvictionsfalseAlpha1.61.12
TaintBasedEvictionstrueBeta1.131.17
TaintBasedEvictionstrueGA1.181.20
TaintNodesByConditionfalseAlpha1.81.11
TaintNodesByConditiontrueBeta1.121.16
TaintNodesByConditiontrueGA1.171.18
TokenRequestfalseAlpha1.101.11
TokenRequesttrueBeta1.121.19
TokenRequesttrueGA1.201.21
TokenRequestProjectionfalseAlpha1.111.11
TokenRequestProjectiontrueBeta1.121.19
TokenRequestProjectiontrueGA1.201.21
TopologyManagerfalseAlpha1.161.17
TopologyManagertrueBeta1.181.26
TopologyManagertrueGA1.271.28
TTLAfterFinishedfalseAlpha1.121.20
TTLAfterFinishedtrueBeta1.211.22
TTLAfterFinishedtrueGA1.231.24
UserNamespacesStatelessPodsSupportfalseAlpha1.251.27
ValidateProxyRedirectsfalseAlpha1.121.13
ValidateProxyRedirectstrueBeta1.141.21
ValidateProxyRedirectstrueDeprecated1.221.24
VolumePVCDataSourcefalseAlpha1.151.15
VolumePVCDataSourcetrueBeta1.161.17
VolumePVCDataSourcetrueGA1.181.21
VolumeSchedulingfalseAlpha1.91.9
VolumeSchedulingtrueBeta1.101.12
VolumeSchedulingtrueGA1.131.16
VolumeSnapshotDataSourcefalseAlpha1.121.16
VolumeSnapshotDataSourcetrueBeta1.171.19
VolumeSnapshotDataSourcetrueGA1.201.22
VolumeSubpathtrueGA1.101.24
VolumeSubpathEnvExpansionfalseAlpha1.141.14
VolumeSubpathEnvExpansiontrueBeta1.151.16
VolumeSubpathEnvExpansiontrueGA1.171.24
WarningHeaderstrueBeta1.191.21
WarningHeaderstrueGA1.221.24
WindowsEndpointSliceProxyingfalseAlpha1.191.20
WindowsEndpointSliceProxyingtrueBeta1.211.21
WindowsEndpointSliceProxyingtrueGA1.221.24
WindowsGMSAfalseAlpha1.141.15
WindowsGMSAtrueBeta1.161.17
WindowsGMSAtrueGA1.181.20
WindowsHostProcessContainersfalseAlpha1.221.22
WindowsHostProcessContainerstrueBeta1.231.25
WindowsHostProcessContainerstrueGA1.261.27
WindowsRunAsUserNamefalseAlpha1.161.16
WindowsRunAsUserNametrueBeta1.171.17
WindowsRunAsUserNametrueGA1.181.20

已移除的特性门控的说明

  • Accelerators:

    使用 Docker Engine 时启用 Nvidia GPU 支持。这一特性不再提供。 关于替代方案,请参阅设备插件

  • AdvancedAuditing:

    启用高级审计

  • AffinityInAnnotations:

    启用 Pod 亲和性或反亲和性设置。

  • AllowExtTrafficLocalEndpoints:

    允许服务将外部请求路由到节点本地端点。

  • AllowInsecureBackendProxy:

    允许用户在请求 Pod 日志时跳过 kubelet 的 TLS 验证。

  • AttachVolumeLimit:

    启用卷插件用于报告可连接到节点的卷数限制。 更多细节参阅动态卷限制

  • BalanceAttachedNodeVolumes:

    在调度时考虑节点上的卷数量,以实现平衡资源分配。 调度器在决策时会优先考虑 CPU、内存利用率和卷数更近的节点。

  • BlockVolume:

    启用在 Pod 中定义和使用原始块设备。 更多详情请参见原始块卷支持

  • BoundServiceAccountTokenVolume:

    将 ServiceAccount 卷迁移到使用由 ServiceAccountTokenVolumeProjection 组成的投射卷。集群管理员可以使用 serviceaccount_stale_tokens_total 指标来监控依赖于扩展令牌的工作负载。 如果没有这样的工作负载,你可以在启动 kube-apiserver 时添加 --service-account-extend-token-expiration=false 标志来关闭扩展令牌。

    更多详情请参见绑定服务账号令牌

  • ConfigurableFSGroupPolicy:

    在 Pod 中挂载卷时,允许用户为 fsGroup 配置卷访问权限和属主变更策略。 请参见为 Pod 配置卷访问权限和属主变更策略

  • ControllerManagerLeaderMigration:

    kube-controller-managercloud-controller-manager 启用 Leader 迁移,它允许集群管理者在没有停机的高可用集群环境下,实时把 kube-controller-manager 迁移到外部的 controller-manager (例如 cloud-controller-manager) 中。

  • CRIContainerLogRotation:

    为 CRI 容器运行时启用容器日志轮换。日志文件的默认最大大小为 10MB, 缺省情况下,一个容器允许的最大日志文件数为 5。这些值可以在 kubelet 配置中配置。 更多细节请参见日志架构

  • CronJobControllerV2:

    使用 CronJob 控制器的一种替代实现。否则,系统会选择同一控制器的 v1 版本。

  • CronJobTimeZone:

    允许在 CronJobs 中使用 timeZone 可选字段。

  • CSIBlockVolume:

    启用外部 CSI 卷驱动程序用于支持块存储。有关更多详细信息,请参见 csi 原始块卷支持

  • CSIDriverRegistry:

    csi.storage.k8s.io 中启用与 CSIDriver API 对象有关的所有逻辑。

  • CSIInlineVolume:

    为 Pod 启用 CSI 内联卷支持。

  • CSIMigration:

    启用封装和转换逻辑,将卷操作从内嵌插件路由到对应的预安装 CSI 插件。

  • CSIMigrationAWS:

    启用封装和转换逻辑,将卷操作从 AWS-EBS 内嵌插件路由到 EBS CSI 插件。 如果节点禁用了此特性门控或者未安装和配置 EBS CSI 插件,支持回退到内嵌 EBS 插件来执行卷挂载操作。 不支持回退到这些插件来执行卷制备操作,因为需要安装并配置 CSI 插件

  • CSIMigrationAWSComplete:

    停止在 kubelet 和卷控制器中注册 EBS 内嵌插件, 并启用封装和转换逻辑,将卷操作从 AWS-EBS 内嵌插件路由到 EBS CSI 插件。 这需要启用 CSIMigration 和 CSIMigrationAWS 特性标志,并在集群中的所有节点上安装和配置 EBS CSI 插件。该特性标志已被废弃,取而代之的是 InTreePluginAWSUnregister, 后者会阻止注册 EBS 内嵌插件。

  • CSIMigrationAzureDisk:

    启用封装和转换逻辑,将卷操作从 AzureDisk 内嵌插件路由到 Azure 磁盘 CSI 插件。 对于禁用了此特性的节点或者没有安装并配置 AzureDisk CSI 插件的节点, 支持回退到内嵌(in-tree)AzureDisk 插件来执行磁盘挂载操作。 不支持回退到内嵌插件来执行磁盘制备操作,因为对应的 CSI 插件必须已安装且正确配置。 此特性需要启用 CSIMigration 特性标志。

  • CSIMigrationAzureDiskComplete:

    停止在 kubelet 和卷控制器中注册 Azure 磁盘内嵌插件, 并启用封装和转换逻辑,将卷操作从 Azure 磁盘内嵌插件路由到 AzureDisk CSI 插件。 这需要启用 CSIMigration 和 CSIMigrationAzureDisk 特性标志, 并在集群中的所有节点上安装和配置 AzureDisk CSI 插件。该特性标志已被废弃, 取而代之的是能防止注册内嵌 AzureDisk 插件的 InTreePluginAzureDiskUnregister 特性标志。

  • CSIMigrationAzureFileComplete:

    停止在 kubelet 和卷控制器中注册 Azure-File 内嵌插件, 并启用封装和转换逻辑,将卷操作从 Azure-File 内嵌插件路由到 AzureFile CSI 插件。 这需要启用 CSIMigration 和 CSIMigrationAzureFile 特性标志, 并在集群中的所有节点上安装和配置 AzureFile CSI 插件。该特性标志已被废弃, 取而代之的是能防止注册内嵌 AzureDisk 插件的 InTreePluginAzureFileUnregister 特性标志。

  • CSIMigrationGCE:

    启用封装和转换逻辑,将卷操作从 GCE-PD 内嵌插件路由到 PD CSI 插件。 对于禁用了此特性的节点或者没有安装并配置 PD CSI 插件的节点, 支持回退到内嵌GCE 插件来执行卷挂载操作。 不支持回退到内嵌插件来执行卷制备操作,因为对应的 CSI 插件必须已安装且正确配置。 此特性需要启用 CSIMigration 特性标志。

  • CSIMigrationGCEComplete:

    停止在 kubelet 和卷控制器中注册 GCE-PD 内嵌插件, 并启用封装和转换逻辑,将卷操作从 GCE-PD 内嵌插件路由到 PD CSI 插件。 这需要启用 CSIMigration 和 CSIMigrationGCE 特性标志,并在集群中的所有节点上安装和配置 PD CSI 插件。该特性标志已被废弃,取而代之的是能防止注册内嵌 GCE PD 插件的 InTreePluginGCEUnregister 特性标志。

  • CSIMigrationOpenStack:

    启用封装和转换逻辑,将卷操作从 Cinder 内嵌插件路由到 Cinder CSI 插件。 对于禁用了此特性的节点或者没有安装并配置 Cinder CSI 插件的节点, 支持回退到内嵌 Cinder 插件来执行挂载操作。 不支持回退到内嵌插件来执行制备操作,因为对应的 CSI 插件必须已安装且正确配置。 此特性需要启用 CSIMigration 特性标志。

  • CSIMigrationOpenStackComplete:

    停止在 kubelet 和卷控制器中注册 Cinder 内嵌插件, 并启用封装和转换逻辑,将卷操作从 Cinder 内嵌插件路由到 Cinder CSI 插件。 这需要启用 CSIMigration 和 CSIMigrationOpenStack 特性标志, 并在集群中的所有节点上安装和配置 Cinder CSI 插件。 该特性标志已被弃用,取而代之的是能防止注册内嵌 OpenStack Cinder 插件的 InTreePluginOpenStackUnregister 特性标志。

  • CSIMigrationvSphere:

    启用封装和转换逻辑,将卷操作从 vSphere 内嵌插件路由到 vSphere CSI 插件。 如果节点禁用了此特性门控或者未安装和配置 vSphere CSI 插件, 则支持回退到 vSphere 内嵌插件来执行挂载操作。 不支持回退到内嵌插件来执行制备操作,因为对应的 CSI 插件必须已安装且正确配置。 这需要启用 CSIMigration 特性标志。

  • CSIMigrationvSphereComplete:

    停止在 kubelet 和卷控制器中注册 vSphere 内嵌插件, 并启用封装和转换逻辑,将卷操作从 vSphere 内嵌插件路由到 vSphere CSI 插件。 这需要启用 CSIMigration 和 CSIMigrationvSphere 特性标志, 并在集群中的所有节点上安装和配置 vSphere CSI 插件。 该特性标志已被废弃,取而代之的是能防止注册内嵌 vSphere 插件的 InTreePluginvSphereUnregister 特性标志。

  • CSINodeInfo:

    csi.storage.k8s.io 中启用与 CSINodeInfo API 对象有关的所有逻辑。

  • CSIPersistentVolume:

    启用发现和挂载通过 CSI(容器存储接口) 兼容卷插件配置的卷。

  • CSIServiceAccountToken:

    允许 CSI 驱动接收挂载卷目标 Pods 的服务账号令牌。 参阅令牌请求(Token Requests)

  • CSIStorageCapacity:

    启用 CSI 驱动发布存储容量信息, 以及 Kubernetes 调度器在调度 Pods 时使用该信息。 详情见存储容量。 更多详情请参阅 csi 卷类型文档。

  • CSIVolumeFSGroupPolicy:

    允许 CSIDriver 使用 fsGroupPolicy 字段。 该字段能控制由 CSIDriver 创建的卷在挂载这些卷时是否支持卷所有权和权限修改。

  • CSRDuration:

    允许客户端为通过 Kubernetes CSR API 签署的证书申请有效期限。

  • CustomPodDNS:

    允许使用 Pod 的 dnsConfig 属性自定义其 DNS 设置。 更多详细信息,请参见 Pod 的 DNS 配置

  • CustomResourceDefaulting:

    为 CRD 启用在其 OpenAPI v3 验证模式中提供默认值的支持。

  • CustomResourcePublishOpenAPI:

    启用 CustomResourceDefinition OpenAPI 规范的发布。

  • CustomResourceSubresources:

    对于用 CustomResourceDefinition 创建的资源启用其 /status/scale 子资源。

  • CustomResourceValidation:

    对于用 CustomResourceDefinition 创建的资源启用基于模式的验证。

  • CustomResourceWebhookConversion:

    对于用 CustomResourceDefinition 创建的资源启用基于 Webhook 的转换。

  • DaemonSetUpdateSurge:

    使 DaemonSet 工作负载在每个节点的更新期间保持可用性。 参阅对 DaemonSet 执行滚动更新

  • DefaultPodTopologySpread:

    启用 PodTopologySpread 调度插件以执行 默认分布

  • DelegateFSGroupToCSIDriver:

    如果 CSI 驱动程序支持,则通过 NodeStageVolume 和 NodePublishVolume CSI 调用传递 fsGroup, 委托驱动来应用 Pod 的 securityContext 中的 fsGroup

  • DevicePlugins:

    在节点上启用基于设备插件的资源制备。

  • DisableAcceleratorUsageMetrics:

    禁用 kubelet 收集的加速器指标

  • DownwardAPIHugePages:

    允许在下行(Downward)API 中使用巨页信息。

  • DryRun:

    启用在服务器端对请求进行 试运行(Dry Run), 以便在不提交的情况下测试验证、合并和变更。

  • DynamicAuditing:

    在 v1.19 版本前用于启用动态审计。

  • DynamicKubeletConfig:

    启用 kubelet 的动态配置。 除偏差策略场景外,不再支持该功能。 该特性门控在 kubelet 1.24 版本中已被移除。

  • DynamicProvisioningScheduling:

    扩展默认调度器以了解卷拓扑并处理 PV 制备。 此特性已在 v1.12 中完全被 VolumeScheduling 特性取代。

  • DynamicVolumeProvisioning:

    启用持久化卷到 Pod 的动态制备

  • EnableAggregatedDiscoveryTimeout:

    对聚合的发现调用启用五秒钟超时设置。

  • EnableEquivalenceClassCache:

    调度 Pod 时,使调度器缓存节点的等效项。

  • EndpointSlice:

    启用 EndpointSlice 以实现可扩缩性和可扩展性更好的网络端点。 参阅启用 EndpointSlice

  • EndpointSliceNodeName:

    启用 EndpointSlice 的 nodeName 字段。

  • EndpointSliceProxying:

    启用此特性门控时,Linux 上运行的 kube-proxy 会使用 EndpointSlice 而不是 Endpoints 作为其主要数据源, 从而使得可扩缩性和性能提升成为可能。 参阅启用 EndpointSlice

  • EndpointSliceTerminatingCondition:

    启用 EndpointSlice 的 terminatingserving 状况字段。

  • EphemeralContainers:

    启用添加临时容器到正在运行的 Pod 的特性。

  • EvenPodsSpread:

    使 Pod 能够在拓扑域之间平衡调度。请参阅 Pod 拓扑扩展约束

  • ExpandCSIVolumes:

    启用 CSI 卷的扩展。

  • ExpandInUsePersistentVolumes:

    启用扩充使用中的 PVC 的大小。 请查阅调整使用中的 PersistentVolumeClaim 的大小

  • ExpandPersistentVolumes:

    允许扩展持久卷。 请查阅扩展持久卷申领

  • ExperimentalCriticalPodAnnotation:

    启用将特定 Pod 注解为 关键负载(Critical) 的方式, 用于确保其被调度。 从 v1.13 开始已弃用此特性,转而使用 Pod 优先级和抢占功能。

  • ExternalPolicyForExternalIP:

    修复一个缺陷,该缺陷会导致 ExternalTrafficPolicy 不会应用到 Service 的外部 IP。

  • GCERegionalPersistentDisk:

    在 GCE 上启用带地理区域信息的 PD 特性。

  • GenericEphemeralVolume:

    启用支持临时的内联卷,这些卷支持普通卷的所有特性 (可以由第三方存储供应商提供,支持存储容量跟踪、从快照还原等等)。 请参见临时卷

  • GRPCContainerProbe:

    为活跃态、就绪态和启动探针启用 gRPC 探针。 参阅配置活跃态、就绪态和启动探针

  • HugePages:

    启用预分配的巨页资源的分配和使用。

  • HugePageStorageMediumSize:

    启用对多种大小的预分配巨页资源的支持。

  • HyperVContainer:

    为 Windows 容器启用 Hyper-V 隔离

  • IdentifyPodOS:

    允许设置 Pod 的 os 字段。这有助于在 API 服务器准入时确定性地辨识 Pod 的 OS。 在 Kubernetes 1.30 中,pod.spec.os.name 的允许值为 windowslinux

  • ImmutableEphemeralVolumes:

    允许将各个 Secret 和 ConfigMap 标记为不可变更的,以提高安全性和性能。

  • IndexedJob:

    允许 Job 控制器根据完成索引来管理 Pod 完成。

  • IngressClassNamespacedParams:

    允许在 IngressClass 资源中使用名字空间范围的参数引用。 此特性为 IngressClass.spec.parameters 添加了两个字段:scopenamespace

  • Initializers:

    允许使用 Intializer 准入插件来异步协调对象创建操作。

  • IPv6DualStack:

    启用双协议栈以支持 IPv6。

  • JobMutableNodeSchedulingDirectives:

    允许更新在 Job 的 Pod 模板中的节点调度指令。

  • JobTrackingWithFinalizers:

    启用跟踪 Job 完成情况,而非总是从集群剩余 Pod 获取信息来判断完成情况。 Job 控制器使用 Pod 终结器(finalizers)和 Job 状态中的一个字段 来跟踪已完成的 Pod 以计算完成度。

  • KubeletConfigFile:

    允许从使用配置文件指定的文件中加载 kubelet 配置。 详情参见通过配置文件设置 kubelet 参数

  • KubeletCredentialProviders:

    启用 kubelet exec 凭据提供程序以获取镜像拉取凭据。

  • KubeletPluginsWatcher:

    启用基于探针的插件监视程序,使 kubelet 能够发现 CSI 卷驱动程序这类插件。

  • LegacyNodeRoleBehavior:

    禁用此门控时,服务负载均衡器中和节点干扰中的原先行为会忽略 node-role.kubernetes.io/master 标签,将使用 NodeDisruptionExclusionServiceNodeExclusion 对应特性所提供的标签。

  • LegacyServiceAccountTokenNoAutoGeneration:

    停止基于 Secret 自动生成服务账号令牌

  • LocalStorageCapacityIsolation:

    允许使用本地临时存储以及 emptyDir 卷sizeLimit 属性。

  • MixedProtocolLBService:

    允许在同一 LoadBalancer 类型的 Service 实例中使用不同的协议。

  • MountContainers:

    允许使用主机上的工具容器作为卷挂载程序。

  • MountPropagation:

    允许将一个容器上挂载的卷共享到其他容器或 Pod。 更多详细信息,请参见挂载传播

  • MultiCIDRRangeAllocator:

    启用 MultiCIDR 网段分配机制。

  • NamespaceDefaultLabelName:

    配置 API 服务器以在所有名字空间上设置一个不可变的标签 kubernetes.io/metadata.name,取值为名字空间的名称。

  • NetworkPolicyEndPort:

    允许你在 NetworkPolicy 规则中将端口设置为一个端口号范围。

  • NetworkPolicyStatus:

    为 NetworkPolicy 对象启用 status 子资源。

  • NodeDisruptionExclusion:

    启用节点标签 node.kubernetes.io/exclude-disruption, 以防止在可用区发生故障期间驱逐节点。

  • NodeLease:

    启用新的 Lease API 以报告节点心跳,节点心跳可用作节点运行状况信号。

  • NonPreemptingPriority:

    为 PriorityClass 和 Pod 启用 preemptionPolicy 字段。

  • OpenAPIV3:

    允许 API 服务器发布 OpenAPI V3。

  • PersistentLocalVolumes:

    允许在 Pod 中使用 local 卷类型。 如果请求 local 卷,则必须指定 Pod 亲和性属性。

  • PodAffinityNamespaceSelector:

    启用 Pod 亲和性名字空间选择算符CrossNamespacePodAffinity 资源配额特性。

  • PodDisruptionBudget:

    启用 PodDisruptionBudget 特性。

  • PodHasNetworkCondition:

    使得 kubelet 能够对 Pod 标记 PodHasNetwork 状况。 此特性在 1.28 中重命名为 PodReadyToStartContainersCondition

  • PodOverhead:

    启用 PodOverhead 特性以计算 Pod 开销。

  • PodPriority:

    允许根据优先级取消调度和抢占 Pod。

  • PodReadinessGates:

    允许设置 podReadinessGate 字段以扩展 Pod 就绪状态评估。更多细节请参见 Pod 就绪状态判别

  • PodSecurity:

    启用 PodSecurity 准入插件。

  • PodShareProcessNamespace:

    在 Pod 中启用 shareProcessNamespace 的设置, 以便在 Pod 中运行的容器之间共享同一进程名字空间。 更多细节请参见在 Pod 中的容器间共享同一进程名字空间

  • PreferNominatedNode:

    这个标志告诉调度器在循环遍历集群中的所有其他节点之前是否先检查指定的节点。

  • ProbeTerminationGracePeriod:

    在 Pod 上启用设置探针级别 terminationGracePeriodSeconds。 更多细节请参见增强提案

  • PVCProtection:

    启用后,可预防仍然有 Pod 正使用的 PersistentVolumeClaim (PVC) 被删除。

  • ReadOnlyAPIDataVolumes:

    请参阅以只读方式挂载的 configMapsecretdownwardAPIprojected 卷。

    自 Kubernetes v1.10 起,这些卷类型始终是只读的,无法选择其它模式。

  • RequestManagement:

    允许在每个 API 服务器上通过优先级和公平性来管理请求并发性。 自 1.17 以来已被 APIPriorityAndFairness 替代。

  • ResourceLimitsPriorityFunction:

    启用某调度器优先级函数,此函数将最低得分 1 指派给至少满足输入 Pod 的 CPU 和内存限制之一的节点, 目的是打破得分相同的节点之间的关联。

  • ResourceQuotaScopeSelectors:

    启用资源配额范围选择算符。

  • ScheduleDaemonSetPods:

    启用 DaemonSet Pod 由默认调度器而不是 DaemonSet 控制器进行调度。

  • SCTPSupport:

    在 Pod、Service、Endpoints、NetworkPolicy 定义中允许将 SCTP 用作 protocol 值。

  • SeccompDefault:

    启用 RuntimeDefault 作为所有工作负载的默认 seccomp 配置文件。 此 seccomp 配置文件在 Pod 和/或 Container 的 securityContext 中被指定。

  • SelectorIndex:

    允许使用 API 服务器的 watch 缓存中基于标签和字段的索引来加速 list 操作。

  • ServiceAccountIssuerDiscovery:

    在 API 服务器中为服务账号颁发者启用 OIDC 发现端点(颁发者和 JWKS URL)。 详情参见为 Pod 配置服务账号

  • ServiceAppProtocol:

    为 Service 和 Endpoints 启用 appProtocol 字段。

  • ServiceInternalTrafficPolicy:

    为 Service 启用 internalTrafficPolicy 字段。

  • ServiceIPStaticSubrange:

    启用 Service 的 ClusterIP 分配策略,从而细分 ClusterIP 范围。 动态分配的 ClusterIP 地址将优先从较高范围分配, 以允许用户从较低范围分配静态 ClusterIP,进而降低发生冲突的风险。 更多详细信息请参阅避免冲突

  • ServiceLBNodePortControl:

    为 Service 启用 allocateLoadBalancerNodePorts 字段。

  • ServiceLoadBalancerClass:

    为 Service 启用 loadBalancerClass 字段。 有关更多信息,请参见设置负载均衡器实现的类别

  • ServiceLoadBalancerFinalizer:

    为 Service 负载均衡器启用终结器(finalizers)保护。

  • ServiceNodeExclusion:

    允许从云提供商创建的负载均衡器中排除节点。 如果节点标记有 node.kubernetes.io/exclude-from-external-load-balancers 标签, 则可以排除该节点。

  • ServiceTopology:

    允许 Service 基于集群的节点拓扑进行流量路由。

  • SetHostnameAsFQDN:

    启用将全限定域名(FQDN)设置为 Pod 主机名的功能。 请参见 Pod 的 setHostnameAsFQDN 字段

  • StartupProbe:

    在 kubelet 中启用启动探针

  • StatefulSetMinReadySeconds:

    允许 StatefulSet 控制器遵守 minReadySeconds

  • StorageObjectInUseProtection:

    如果仍在使用 PersistentVolume 或 PersistentVolumeClaim 对象, 则将其删除操作推迟。

  • StreamingProxyRedirects:

    指示 API 服务器拦截(并跟踪)后端(kubelet)的重定向以处理流请求。 流请求的例子包括 execattachport-forward 请求。

  • SupportIPVSProxyMode:

    启用使用 IPVS 提供集群内服务负载平衡。 详情参见服务代理

  • SupportNodePidsLimit:

    允许限制 Node 上的 PID 用量。 --system-reserved--kube-reserved 中的参数 pid=<数值> 可以分别用来设定为整个系统所预留的进程 ID 个数, 和为 Kubernetes 系统守护进程预留的进程 ID 个数。

  • SupportPodPidsLimit:

    允许限制 Pod 中的 PID 用量。

  • SuspendJob:

    启用对挂起和恢复 Job 的支持。详情参见 Job 文档

  • Sysctls:

    允许为每个 Pod 设置受命名空间约束的内核参数(sysctl)。详情参见 sysctl

  • TaintBasedEvictions:

    根据节点上的污点和 Pod 上的容忍度启用从节点驱逐 Pod 的特性。 更多细节参见污点和容忍度

  • TaintNodesByCondition:

    根据节点状况启用自动为节点标记污点。

  • TokenRequest:

    在服务账号资源上启用 TokenRequest 端点。

  • TokenRequestProjection:

    启用通过 projected将服务账号令牌注入到 Pod 中的特性。

  • TopologyManager:

    启用一种机制来协调 Kubernetes 不同组件的细粒度硬件资源分配。 详见控制节点上的拓扑管理策略

  • TTLAfterFinished:

    允许 TTL 控制器在资源执行完毕后清理资源。

  • UserNamespacesStatelessPodsSupport:

    为无状态 Pod 启用用户命名空间支持。此特性在 Kubernetes v1.28 版本中被 UserNamespacesSupport 特性取代。

  • ValidateProxyRedirects:

    这个标志控制 API 服务器是否检查重定向仅指向相同主机。 仅在启用 StreamingProxyRedirects 标志时被使用。

  • VolumePVCDataSource:

    启用对将现有 PVC 设置为数据源的支持。

  • VolumeScheduling:

    启用卷拓扑感知调度,并使 PersistentVolumeClaim(PVC) 绑定能够了解调度决策;当与 PersistentLocalVolumes 特性门控一起使用时, 还允许使用 local 卷类型。

  • VolumeSnapshotDataSource:

    启用卷快照数据源支持。

  • VolumeSubpath:

    允许在容器中挂载卷的子路径。

  • VolumeSubpathEnvExpansion:

    启用 subPathExpr 字段用于在 subPath 中展开环境变量。

  • WarningHeaders:

    允许在 API 响应中发送警告头。

  • WindowsEndpointSliceProxying:

    启用后,运行在 Windows 上的 kube-proxy 将使用 EndpointSlice 而不是 Endpoints 作为主要数据源,从而实现可伸缩性和性能改进。 详情请参见启用端点切片

  • WindowsGMSA:

    允许从 Pod 传递 GMSA 凭据规范到容器运行时。

  • WindowsHostProcessContainers:

    启用对 Windows HostProcess 容器的支持。

  • WindowsRunAsUserName:

    启用在 Windows 容器中以非默认用户身份运行应用程序的支持。 详情请参见配置 RunAsUserName

6.13.3 - kubelet

简介

kubelet 是在每个节点上运行的主要 “节点代理”。它可以使用以下方式之一向 API 服务器注册:

  • 主机名(hostname);
  • 覆盖主机名的参数;
  • 特定于某云驱动的逻辑。

kubelet 是基于 PodSpec 来工作的。每个 PodSpec 是一个描述 Pod 的 YAML 或 JSON 对象。 kubelet 接受通过各种机制(主要是通过 apiserver)提供的一组 PodSpec,并确保这些 PodSpec 中描述的容器处于运行状态且运行状况良好。 kubelet 不管理不是由 Kubernetes 创建的容器。

除了来自 API 服务器的 PodSpec 之外,还可以通过以下两种方式将容器清单(manifest)提供给 kubelet。

  • 文件(File):利用命令行参数传递路径。kubelet 周期性地监视此路径下的文件是否有更新。 监视周期默认为 20s,且可通过参数进行配置。
  • HTTP 端点(HTTP endpoint):利用命令行参数指定 HTTP 端点。 此端点的监视周期默认为 20 秒,也可以使用参数进行配置。
kubelet [flags]

选项

--address string     默认值:0.0.0.0
kubelet 用来提供服务的 IP 地址(设置为 0.0.0.0:: 表示监听所有接口和 IP 地址族)。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--allowed-unsafe-sysctls strings
用逗号分隔的字符串序列设置允许使用的非安全的 sysctls 或 sysctl 模式(以 * 结尾)。 使用此参数时风险自担。(已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--anonymous-auth     默认值:true
设置为 true 表示 kubelet 服务器可以接受匿名请求。未被任何认证组件拒绝的请求将被视为匿名请求。 匿名请求的用户名为 system:anonymous,用户组为 system:unauthenticated。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--authentication-token-webhook
使用 TokenReview API 对持有者令牌进行身份认证。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--authentication-token-webhook-cache-ttl duration     默认值:2m0s
对 Webhook 令牌认证组件所返回的响应的缓存时间。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--authorization-mode string     默认值: AlwaysAllow
kubelet 服务器的鉴权模式。可选值包括:AlwaysAllow、Webhook。 Webhook 模式使用 SubjectAccessReview API 鉴权。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--authorization-webhook-cache-authorized-ttl duration     默认值:5m0s
对 Webhook 认证组件所返回的 “Authorized(已授权)” 应答的缓存时间。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--authorization-webhook-cache-unauthorized-ttl duration     默认值:30s
对 Webhook 认证组件所返回的 “Unauthorized(未授权)” 应答的缓存时间。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--bootstrap-kubeconfig string
某 kubeconfig 文件的路径,该文件将用于获取 kubelet 的客户端证书。 如果 --kubeconfig 所指定的文件不存在,则使用引导所用 kubeconfig 从 API 服务器请求客户端证书。成功后,将引用生成的客户端证书和密钥的 kubeconfig 写入 --kubeconfig 所指定的路径。客户端证书和密钥文件将存储在 --cert-dir 所指的目录。
--cert-dir string     默认值:/var/lib/kubelet/pki
TLS 证书所在的目录。如果设置了 --tls-cert-file--tls-private-key-file, 则此标志将被忽略。
--cgroup-driver string     默认值:cgroupfs
kubelet 用来操作本机 cgroup 时使用的驱动程序。支持的选项包括 cgroupfssystemd。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--cgroup-root string     默认值:""
可选的选项,为 Pod 设置根 cgroup。容器运行时会尽可能使用此配置。 默认值 "" 意味着将使用容器运行时的默认设置。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--cgroups-per-qos     默认值:true
启用创建 QoS cgroup 层次结构。此值为 true 时 kubelet 为 QoS 和 Pod 创建顶级的 cgroup。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--client-ca-file string
如果设置了此参数,则使用对应文件中机构之一检查请求中所携带的客户端证书。 若客户端证书通过身份认证,则其对应身份为其证书中所设置的 CommonName。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--cloud-config string
云驱动配置文件的路径。空字符串表示没有配置文件。 已弃用:将在 1.25 或更高版本中移除,以便于从 kubelet 中去除云驱动代码。
--cloud-provider string
云服务的提供者。设置为空字符串表示在没有云驱动的情况下运行, 设置为 'external' 表示使用外部云驱动。 如果设置了此标志,则云驱动负责确定节点的名称(参考云提供商文档以确定是否以及如何使用主机名)。
--cluster-dns strings
DNS 服务器的 IP 地址,以逗号分隔。此标志值用于 Pod 中设置了 “dnsPolicy: ClusterFirst” 时为容器提供 DNS 服务。
注意::列表中出现的所有 DNS 服务器必须包含相同的记录组, 否则集群中的名称解析可能无法正常工作。至于名称解析过程中会牵涉到哪些 DNS 服务器, 这一点无法保证。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--cluster-domain string
集群的域名。如果设置了此值,kubelet 除了将主机的搜索域配置到所有容器之外,还会为其 配置所搜这里指定的域名。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--config string
kubelet 将从此标志所指的文件中加载其初始配置。此路径可以是绝对路径,也可以是相对路径。 相对路径按 kubelet 的当前工作目录起计。省略此参数时 kubelet 会使用内置的默认配置值。 命令行参数会覆盖此文件中的配置。
--config-dir string     Default: ''
用于指定插件的目录路径,允许用户通过指定其他配置来覆盖默认值以及 `--config` 标志中指定的内容。
注意:设置 "KUBELET_CONFIG_DROPIN_DIR_ALPHA" 环境变量以指定目录。
--container-log-max-files int32     默认值:5
【警告:Beta 特性】设置容器的日志文件个数上限。此值必须大于等于 2。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--container-log-max-size string     默认值:10Mi
【警告:Beta 特性】设置容器日志文件在轮换生成新文件时之前的最大值(例如,10Mi)。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--container-runtime string     默认值:remote
要使用的容器运行时。目前支持 dockerremote。 (已弃用:将会在 1.27 版本中移除,因为合法值只有 “remote”)
--container-runtime-endpoint string     Default: "unix:///run/containerd/containerd.sock"
远程运行时服务的端点。目前支持 Linux 系统上的 UNIX 套接字和 Windows 系统上的 npipe 和 TCP 端点。例如: unix:///path/to/runtime.socknpipe:////./pipe/runtime。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--contention-profiling
当启用了性能分析时,启用锁竞争分析。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--cpu-cfs-quota     默认值:true
为设置了 CPU 限制的容器启用 CPU CFS 配额保障。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--cpu-cfs-quota-period duration     默认值:100ms
设置 CPU CFS 配额周期 cpu.cfs_period_us。默认使用 Linux 内核所设置的默认值。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--cpu-manager-policy string     默认值:none
要使用的 CPU 管理器策略。可选值包括:nonestatic。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--cpu-manager-policy-options string
一组用于微调 CPU 管理器策略行为的 key=value 选项。如果未提供,保留默认行为。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--cpu-manager-reconcile-period duration     默认值:10s
【警告:Alpha 特性】设置 CPU 管理器的调和时间。例如:10s 或者 1m。 如果未设置,默认使用节点状态更新频率。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--enable-controller-attach-detach     默认值:true
启用 Attach/Detach 控制器来挂接和摘除调度到该节点的卷,同时禁用 kubelet 执行挂接和摘除操作。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--enable-debugging-handlers     默认值:true
启用服务器上用于日志收集和在本地运行容器和命令的端点。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--enable-server     默认值:true
启用 kubelet 服务器。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--enforce-node-allocatable strings     默认值:pods
用逗号分隔的列表,包含由 kubelet 强制执行的节点可分配资源级别。 可选配置为:nonepodssystem-reservedkube-reserved。 在设置 system-reservedkube-reserved 这两个值时,同时要求设置 --system-reserved-cgroup--kube-reserved-cgroup 这两个参数。 如果设置为 none,则不需要设置其他参数。 有关更多详细信息,请参阅官方文档。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--event-burst int32     默认值:100
事件记录的个数的突发峰值上限,在遵从 --event-qps 阈值约束的前提下 临时允许事件记录达到此数目。该数字必须大于等于 0。如果为 0,则使用默认突发值(100)。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--event-qps int32     默认值:50
QPS 用于限制事件创建的速率。该数字必须大于等于 0。如果为 0,则使用默认 QPS 值(50)。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--eviction-hard strings     默认值:imagefs.available<15%,memory.available<100Mi,nodefs.available<10%
触发 Pod 驱逐操作的一组硬性门限(例如:memory.available<1Gi (内存可用值小于 1G)设置。在 Linux 节点上,默认值还包括 nodefs.inodesFree<5%。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--eviction-max-pod-grace-period int32
响应满足软性驱逐阈值(Soft Eviction Threshold)而终止 Pod 时使用的最长宽限期(以秒为单位)。 如果设置为负数,则遵循 Pod 的指定值。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--eviction-minimum-reclaim strings
当某资源压力过大时,kubelet 将执行 Pod 驱逐操作。 此参数设置软性驱逐操作需要回收的资源的最小数量(例如:imagefs.available=2Gi)。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--eviction-pressure-transition-period duration     默认值:5m0s
kubelet 在驱逐压力状况解除之前的最长等待时间。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--eviction-soft strings
设置一组驱逐阈值(例如:memory.available<1.5Gi)。 如果在相应的宽限期内达到该阈值,则会触发 Pod 驱逐操作。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--eviction-soft-grace-period strings
设置一组驱逐宽限期(例如,memory.available=1m30s),对应于触发软性 Pod 驱逐操作之前软性驱逐阈值所需持续的时间长短。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--exit-on-lock-contention
设置为 true 表示当发生锁文件竞争时 kubelet 可以退出。
--experimental-allocatable-ignore-eviction     默认值:false
设置为 true 表示在计算节点可分配资源数量时忽略硬性逐出阈值设置。 参考 相关文档。 已弃用:将在 1.25 或更高版本中移除。
--experimental-mounter-path string     默认值:mount
[实验性特性] 卷挂载器(mounter)的可执行文件的路径。设置为空表示使用默认挂载器 mount。 已弃用:将在 1.24 或更高版本移除以支持 CSI。
--fail-swap-on     默认值:true
设置为 true 表示如果主机启用了交换分区,kubelet 将直接失败。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--feature-gates <一个由 “key=true/false” 组成的对偶>
用于 alpha 实验性特性的特性开关组,每个开关以 key=value 形式表示。当前可用开关包括:
APIResponseCompression=true|false (BETA - 默认值为 true)
APISelfSubjectReview=true|false (BETA - 默认值为 true)
APIServerIdentity=true|false (BETA - 默认值为 true)
APIServerTracing=true|false (BETA - 默认值为 true)
AdmissionWebhookMatchConditions=true|false (BETA - 默认值为 true)
AggregatedDiscoveryEndpoint=true|false (BETA - 默认值为 true)
AllAlpha=true|false (ALPHA - 默认值为 false)
AllBeta=true|false (BETA - 默认值为 false)
AnyVolumeDataSource=true|false (BETA - 默认值为 true)
AppArmor=true|false (BETA - 默认值为 true)
CPUManagerPolicyAlphaOptions=true|false (ALPHA - 默认值为 false)
CPUManagerPolicyBetaOptions=true|false (BETA - 默认值为 true)
CPUManagerPolicyOptions=true|false (BETA - 默认值为 true)
CRDValidationRatcheting=true|false (ALPHA - 默认值为 false)
CSIMigrationPortworx=true|false (BETA - 默认值为 false)
CSIVolumeHealth=true|false (ALPHA - 默认值为 false)
CloudControllerManagerWebhook=true|false (ALPHA - 默认值为 false)
CloudDualStackNodeIPs=true|false (BETA - 默认值为 true)
ClusterTrustBundle=true|false (ALPHA - 默认值为 false)
ClusterTrustBundleProjection=true|false (ALPHA - 默认值为 false)
ComponentSLIs=true|false (BETA - 默认值为 true)
ConsistentListFromCache=true|false (ALPHA - 默认值为 false)
ContainerCheckpoint=true|false (ALPHA - 默认值为 false)
ContextualLogging=true|false (ALPHA - 默认值为 false)
CronJobsScheduledAnnotation=true|false (BETA - 默认值为 true)
CrossNamespaceVolumeDataSource=true|false (ALPHA - 默认值为 false)
CustomCPUCFSQuotaPeriod=true|false (ALPHA - 默认值为 false)
DevicePluginCDIDevices=true|false (BETA - 默认值为 true)
DisableCloudProviders=true|false (BETA - 默认值为 true)
DisableKubeletCloudCredentialProviders=true|false (BETA - 默认值为 true)
DisableNodeKubeProxyVersion=true|false (ALPHA - 默认值为 false)
DynamicResourceAllocation=true|false (ALPHA - 默认值为 false)
ElasticIndexedJob=true|false (BETA - 默认值为 true)
EventedPLEG=true|false (BETA - 默认值为 false)
GracefulNodeShutdown=true|false (BETA - 默认值为 true)
GracefulNodeShutdownBasedOnPodPriority=true|false (BETA - 默认值为 true)
HPAContainerMetrics=true|false (BETA - 默认值为 true)
HPAScaleToZero=true|false (ALPHA - 默认值为 false)
HonorPVReclaimPolicy=true|false (ALPHA - 默认值为 false)
ImageMaximumGCAge=true|false (ALPHA - 默认值为 false)
InPlacePodVerticalScaling=true|false (ALPHA - 默认值为 false)
InTreePluginAWSUnregister=true|false (ALPHA - 默认值为 false)
InTreePluginAzureDiskUnregister=true|false (ALPHA - 默认值为 false)
InTreePluginAzureFileUnregister=true|false (ALPHA - 默认值为 false)
InTreePluginGCEUnregister=true|false (ALPHA - 默认值为 false)
InTreePluginOpenStackUnregister=true|false (ALPHA - 默认值为 false)
InTreePluginPortworxUnregister=true|false (ALPHA - 默认值为 false)
InTreePluginvSphereUnregister=true|false (ALPHA - 默认值为 false)
JobBackoffLimitPerIndex=true|false (BETA - 默认值为 true)
JobPodFailurePolicy=true|false (BETA - 默认值为 true)
JobPodReplacementPolicy=true|false (BETA - 默认值为 true)
KubeProxyDrainingTerminatingNodes=true|false (ALPHA - 默认值为 false)
KubeletCgroupDriverFromCRI=true|false (ALPHA - 默认值为 false)
KubeletInUserNamespace=true|false (ALPHA - 默认值为 false)
KubeletPodResourcesDynamicResources=true|false (ALPHA - 默认值为 false)
KubeletPodResourcesGet=true|false (ALPHA - 默认值为 false)
KubeletSeparateDiskGC=true|false (ALPHA - 默认值为 false)
KubeletPodResourcesGetAllocatable=true|false (BETA - 默认值为 true)
KubeletTracing=true|false (BETA - 默认值为 true)
LegacyServiceAccountTokenCleanUp=true|false (BETA - 默认值为 true)
LoadBalancerIPMode=true|false (ALPHA - 默认值为 false)
LocalStorageCapacityIsolationFSQuotaMonitoring=true|false (ALPHA - 默认值为 false)
LogarithmicScaleDown=true|false (BETA - 默认值为 true)
LoggingAlphaOptions=true|false (ALPHA - 默认值为 false)
LoggingBetaOptions=true|false (BETA - 默认值为 true)
MatchLabelKeysInPodAffinity=true|false (ALPHA - 默认值为 false)
MatchLabelKeysInPodTopologySpread=true|false (BETA - 默认值为 true)
MaxUnavailableStatefulSet=true|false (ALPHA - 默认值为 false)
MemoryManager=true|false (BETA - 默认值为 true)
MemoryQoS=true|false (ALPHA - 默认值为 false)
MinDomainsInPodTopologySpread=true|false (BETA - 默认值为 true)
MultiCIDRServiceAllocator=true|false (ALPHA - 默认值为 false)
NFTablesProxyMode=true|false (ALPHA - 默认值为 false)
NewVolumeManagerReconstruction=true|false (BETA - 默认值为 true)
NodeInclusionPolicyInPodTopologySpread=true|false (BETA - 默认值为 true)
NodeLogQuery=true|false (ALPHA - 默认值为 false)
NodeSwap=true|false (BETA - 默认值为 false)
OpenAPIEnums=true|false (BETA - 默认值为 true)
PDBUnhealthyPodEvictionPolicy=true|false (BETA - 默认值为 true)
PersistentVolumeLastPhaseTransitionTime=true|false (BETA - 默认值为 true)
PodAndContainerStatsFromCRI=true|false (ALPHA - 默认值为 false)
PodDeletionCost=true|false (BETA - 默认值为 true)
PodDisruptionConditions=true|false (BETA - 默认值为 true)
PodHostIPs=true|false (BETA - 默认值为 true)
PodIndexLabel=true|false (BETA - 默认值为 true)
PodLifecycleSleepAction=true|false (ALPHA - 默认值为 false)
PodReadyToStartContainersCondition=true|false (BETA - 默认值为 true)
PodSchedulingReadiness=true|false (BETA - 默认值为 true)
ProcMountType=true|false (ALPHA - 默认值为 false)
QOSReserved=true|false (ALPHA - 默认值为 false)
RecoverVolumeExpansionFailure=true|false (ALPHA - 默认值为 false)
RotateKubeletServerCertificate=true|false (BETA - 默认值为 true)
RuntimeClassInImageCriApi=true|false (ALPHA - 默认值为 false)
SELinuxMountReadWriteOncePod=true|false (BETA - 默认值为 true)
SchedulerQueueingHints=true|false (BETA - 默认值为 false)
SecurityContextDeny=true|false (ALPHA - 默认值为 false)
SeparateTaintEvictionController=true|false (BETA - 默认值为 true)
ServiceAccountTokenJTI=true|false (ALPHA - 默认值为 false)
ServiceAccountTokenNodeBinding=true|false (ALPHA - 默认值为 false)
ServiceAccountTokenNodeBindingValidation=true|false (ALPHA - 默认值为 false)
ServiceAccountTokenPodNodeInfo=true|false (ALPHA - 默认值为 false)
SidecarContainers=true|false (BETA - default=true)
SizeMemoryBackedVolumes=true|false (BETA - 默认值为 true)
StableLoadBalancerNodeSet=true|false (BETA - 默认值为 true)
StatefulSetAutoDeletePVC=true|false (BETA - 默认值为 true)
StatefulSetStartOrdinal=true|false (BETA - 默认值为 true)
StorageVersionAPI=true|false (ALPHA - 默认值为 false)
StorageVersionHash=true|false (BETA - 默认值为 true)
StructuredAuthenticationConfiguration=true|false (ALPHA - 默认值为 false)
StructuredAuthorizationConfiguration=true|false (ALPHA - 默认值为 false)
TopologyAwareHints=true|false (BETA - 默认值为 true)
TopologyManagerPolicyAlphaOptions=true|false (ALPHA - 默认值为 false)
TopologyManagerPolicyBetaOptions=true|false (BETA - 默认值为 true)
TopologyManagerPolicyOptions=true|false (BETA - 默认值为 true)
TranslateStreamCloseWebsocketRequests=true|false (ALPHA - 默认值为 false)
UnauthenticatedHTTP2DOSMitigation=true|false (BETA - 默认值为 true)
UnknownVersionInteroperabilityProxy=true|false (ALPHA - 默认值为 false)
UserNamespacesPodSecurityStandards=true|false (ALPHA - 默认值为 false)
UserNamespacesSupport=true|false (ALPHA - 默认值为 false)
ValidatingAdmissionPolicy=true|false (BETA - 默认值为 false)
VolumeAttributesClass=true|false (ALPHA - 默认值为 false)
VolumeCapacityPriority=true|false (ALPHA - 默认值为 false)
WatchList=true|false (ALPHA - 默认值为 false)
WinDSR=true|false (ALPHA - 默认值为 false)
WinOverlay=true|false (BETA - 默认值为 true)
WindowsHostNetwork=true|false (ALPHA - 默认值为 true)
ZeroLimitedNominalConcurrencyShares=true|false (BETA - 默认值为 false)
已弃用: 应在 --config 所给的配置文件中进行设置。 (进一步了解
--file-check-frequency duration     默认值:20s
检查配置文件中新数据的时间间隔。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--hairpin-mode string     默认值:promiscuous-bridge
设置 kubelet 执行发夹模式(hairpin)网络地址转译的方式。 该模式允许后端端点对其自身服务的访问能够再次经由负载均衡转发回自身。 可选项包括 promiscuous-bridgehairpin-vethnone。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--healthz-bind-address string     默认值:127.0.0.1
healthz 服务器提供服务所使用的 IP 地址(设置为 0.0.0.0:: 表示监听所有接口和 IP 协议族。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--healthz-port int32     默认值:10248
本地 healthz 端点使用的端口(设置为 0 表示禁用)。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
-h, --help
kubelet 操作的帮助命令
--hostname-override string
如果为非空,将使用此字符串而不是实际的主机名作为节点标识。如果设置了 --cloud-provider,则云驱动将确定节点的名称 (请查阅云服务商文档以确定是否以及如何使用主机名)。
--http-check-frequency duration     默认值:20s
HTTP 服务以获取新数据的时间间隔。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--image-credential-provider-bin-dir string
指向凭据提供组件可执行文件所在目录的路径。
--image-credential-provider-config string
指向凭据提供插件配置文件所在目录的路径。
--image-gc-high-threshold int32     默认值:85
镜像垃圾回收上限。磁盘使用空间达到该百分比时,镜像垃圾回收将持续工作。 值必须在 [0,100] 范围内。要禁用镜像垃圾回收,请设置为 100。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--image-gc-low-threshold int32     默认值:80
镜像垃圾回收下限。磁盘使用空间在达到该百分比之前,镜像垃圾回收操作不会运行。 值必须在 [0,100] 范围内,并且不得大于 --image-gc-high-threshold的值。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--image-service-endpoint string
远程镜像服务的端点。若未设定则默认情况下使用 --container-runtime-endpoint 的值。目前支持的类型包括在 Linux 系统上的 UNIX 套接字端点和 Windows 系统上的 npipe 和 TCP 端点。 例如:unix:///var/run/dockershim.socknpipe:////./pipe/dockershim。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--iptables-masquerade-bit int32     默认值:14
标记数据包将进行 SNAT 的 fwmark 空间位设置。必须在 [0,31] 范围内。 请将此参数与 kube-proxy 中的相应参数匹配。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--keep-terminated-pod-volumes
设置为 true 表示 Pod 终止后仍然保留之前挂载过的卷,常用于调试与卷有关的问题。 已弃用:将未来版本中移除。
--kernel-memcg-notification
若启用,则 kubelet 将与内核中的 memcg 通知机制集成,不再使用轮询的方式来判定 是否 Pod 达到内存驱逐阈值。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--kube-api-burst int32     默认值:100
每秒发送到 API 服务器 的突发请求数量上限。 该数字必须大于或等于 0。如果为 0,则使用默认的突发值(100)。 不包括事件和节点心跳 API,其速率限制由一组不同的标志控制。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--kube-api-content-type string     默认值:application/vnd.kubernetes.protobuf
发送到 apiserver 的请求的内容类型。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--kube-api-qps int32     默认值:50
与 apiserver 通信的每秒查询个数(QPS)。 此值必须 >= 0。如果为 0,则使用默认 QPS(50)。 不包含事件和节点心跳 API,它们的速率限制是由一组不同的标志所控制。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--kube-reserved strings     默认值:<None>
kubernetes 系统预留的资源配置,以一组 <资源名称>=<资源数量> 格式表示。 (例如:cpu=200m,memory=500Mi,ephemeral-storage=1Gi,pid='100')。 当前支持 cpumemory 和用于根文件系统的 ephemeral-storage。 请参阅这里获取更多信息。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--kube-reserved-cgroup string     默认值:""
给出某个顶层 cgroup 绝对名称,该 cgroup 用于管理通过标志 --kube-reserved 为 kubernetes 组件所预留的计算资源。例如:"/kube-reserved"。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--kubeconfig string
kubeconfig 配置文件的路径,指定如何连接到 API 服务器。 提供 --kubeconfig 将启用 API 服务器模式,而省略 --kubeconfig 将启用独立模式。
--kubelet-cgroups string
用于创建和运行 kubelet 的 cgroup 的绝对名称。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--local-storage-capacity-isolation>     Default: true
如果此值为 true,将启用本地临时存储隔离。 否则,本地存储隔离功能特性将被禁用。 (已弃用:这个参数应该通过 kubelet 的 --config 标志指定的配置文件来设置。 有关详细信息,请参阅 kubelet-config-file)。
--lock-file string
【警告:Alpha 特性】kubelet 用作锁文件的文件路径。
--log-flush-frequency duration     默认值:5s
两次日志刷新之间的最大秒数(默认值为 5s)。
--log-json-info-buffer-size string     默认值:'0'
[Alpha 特性]在具有拆分输出流的 JSON 格式中,可以将信息消息缓冲一段时间以提高性能。 零字节的默认值禁用缓冲。大小可以指定为字节数(512)、1000 的倍数(1K)、1024 的倍数(2Ki) 或这些(3M、4G、5Mi、6Gi)的幂。 启用 LoggingAlphaOptions 特性门控来使用此功能。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--log-json-split-stream
[Alpha 特性]以 JSON 格式,将错误消息写入 stderr,将 info 消息写入 stdout。 启用 LoggingAlphaOptions 特性门控来使用此功能。 默认是将单个流写入标准输出。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--logging-format string     默认值:"text"
设置日志格式。允许的格式:json(由 LoggingBetaOptionstext 控制)。 (已弃用:此参数应通过 kubelet 的 --config 标志指定的配置文件设置。 请参阅 kubelet-config-file 了解更多信息。)
--make-iptables-util-chains     默认值:true
设置为 true 表示 kubelet 将确保 iptables 规则在主机上存在。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--manifest-url string
用于访问要运行的其他 Pod 规范的 URL。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--manifest-url-header strings
取值为由 HTTP 头部组成的逗号分隔列表,在访问 --manifest-url 所给出的 URL 时使用。 名称相同的多个头部将按所列的顺序添加。该参数可以多次使用。例如: --manifest-url-header 'a:hello,b:again,c:world' --manifest-url-header 'b:beautiful'。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--master-service-namespace string     默认值:default
kubelet 向 Pod 注入 Kubernetes 主控服务信息时使用的命名空间。 已弃用:此标志将在未来的版本中删除。
--max-open-files int     默认值:1000000
kubelet 进程可以打开的最大文件数量。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--max-pods int32     默认值:110
此 kubelet 能运行的 Pod 最大数量。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--maximum-dead-containers int32     默认值:-1
设置全局可保留的已停止容器实例个数上限。 每个实例会占用一些磁盘空间。要禁用,可设置为负数。 已弃用:改用 --eviction-hard--eviction-soft。 此标志将在未来的版本中删除。
--maximum-dead-containers-per-container int32     默认值:1
每个已停止容器可以保留的的最大实例数量。每个容器占用一些磁盘空间。 已弃用:改用 --eviction-hard--eviction-soft。 此标志将在未来的版本中删除。
--memory-manager-policy string     默认值:None
内存管理器策略使用。可选值:NoneStatic。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--minimum-container-ttl-duration duration
已结束的容器在被垃圾回收清理之前的最少存活时间。 例如:300ms10s 或者 2h45m。 已弃用:请改用 --eviction-hard 或者 --eviction-soft。 此标志将在未来的版本中删除。
--minimum-image-ttl-duration duration     默认值:2m0s
已结束的容器在被垃圾回收清理之前的最少存活时间。 例如:300ms10s 或者 2h45m。 已弃用:这个参数应该通过 kubelet 的 --config 标志指定的配置文件来设置。 (进一步了解
--node-ip string
节点的 IP 地址(或逗号分隔的双栈 IP 地址)。 如果未设置,kubelet 将使用节点的默认 IPv4 地址(如果有)或默认 IPv6 地址(如果它没有 IPv4 地址)。 你可以传值 :: 使其偏向于默认的 IPv6 地址而不是默认的 IPv4 地址。
--node-labels <key=value pairs>
【警告:Alpha 特性】kubelet 在集群中注册本节点时设置的标签。标签以 key=value 的格式表示,多个标签以逗号分隔。名字空间 kubernetes.io 中的标签必须以 kubelet.kubernetes.ionode.kubernetes.io 为前缀, 或者在以下明确允许范围内: beta.kubernetes.io/arch, beta.kubernetes.io/instance-type, beta.kubernetes.io/os, failure-domain.beta.kubernetes.io/region, failure-domain.beta.kubernetes.io/zone, kubernetes.io/arch, kubernetes.io/hostname, kubernetes.io/os, node.kubernetes.io/instance-type, topology.kubernetes.io/region, topology.kubernetes.io/zone
--node-status-max-images int32     默认值:50
node.status.images 中可以报告的最大镜像数量。如果指定为 -1,则不设上限。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--node-status-update-frequency duration     默认值:10s
指定 kubelet 向主控节点汇报节点状态的时间间隔。注意:更改此常量时请务必谨慎, 它必须与节点控制器中的 nodeMonitorGracePeriod 一起使用。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--oom-score-adj int32     默认值:-999
kubelet 进程的 oom-score-adj 参数值。有效范围为 [-1000,1000]。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--pod-cidr string
用于给 Pod 分配 IP 地址的 CIDR 地址池,仅在独立运行模式下使用。 在集群模式下,CIDR 设置是从主服务器获取的。对于 IPv6,分配的 IP 的最大数量为 65536。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--pod-infra-container-image string      默认值: registry.k8s.io/pause:3.9
所指定的镜像不会被镜像垃圾收集器删除。 CRI 实现有自己的配置来设置此镜像。 (已弃用:将在 1.27 中删除,镜像垃圾收集器将从 CRI 获取沙箱镜像信息。)
--pod-manifest-path string
设置包含要运行的静态 Pod 的文件的路径,或单个静态 Pod 文件的路径。以点(.) 开头的文件将被忽略。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--pod-max-pids int     默认值:-1
设置每个 Pod 中的最大进程数目。如果为 -1,则 kubelet 使用节点可分配的 PID 容量作为默认值。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--pods-per-core int32
kubelet 在每个处理器核上可运行的 Pod 数量。此 kubelet 上的 Pod 总数不能超过 --max-pods 标志值。因此,如果此计算结果导致在 kubelet 上允许更多数量的 Pod,则使用 --max-pods 值。值为 0 表示不作限制。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--port int32     默认值:10250
kubelet 服务监听的本机端口号。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--protect-kernel-defaults
设置 kubelet 的默认内核调整行为。如果已设置该参数,当任何内核可调参数与 kubelet 默认值不同时,kubelet 都会出错。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--provider-id string
设置主机数据库(即,云驱动)中用来标识节点的唯一标识。
--qos-reserved string
【警告:Alpha 特性】设置在指定的 QoS 级别预留的 Pod 资源请求,以一组 "资源名称=百分比" 的形式进行设置,例如 memory=50%。 当前仅支持内存(memory)。要求启用 QOSReserved 特性门控。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--read-only-port int32     默认值:10255
kubelet 可以在没有身份验证/鉴权的情况下提供只读服务的端口(设置为 0 表示禁用)。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--register-node     默认值:true
向 API 服务器注册节点,如果未提供 --kubeconfig,此标志无关紧要, 因为 kubelet 没有 API 服务器可注册。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--register-schedulable     默认值:true
注册本节点为可调度的节点。当 --register-node标志为 false 时此设置无效。 已弃用:此参数将在未来的版本中删除。
--register-with-taints string
设置本节点的污点标记,格式为 <key>=<value>:<effect>, 以逗号分隔。当 --register-node 为 false 时此标志无效。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--registry-burst int32     默认值:10
设置突发性镜像拉取的个数上限,在不超过 --registration-qps 设置值的前提下 暂时允许此参数所给的镜像拉取个数。仅在 --registry-qps 大于 0 时使用。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--registry-qps int32     默认值:5
如此值大于 0,可用来限制镜像仓库的 QPS 上限。设置为 0,表示不受限制。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--reserved-cpus string
用逗号分隔的一组 CPU 或 CPU 范围列表,给出为系统和 Kubernetes 保留使用的 CPU。 此列表所给出的设置优先于通过 --system-reserved--kube-reskube-reserved 所保留的 CPU 个数配置。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--reserved-memory string
以逗号分隔的 NUMA 节点内存预留列表。(例如 --reserved-memory 0:memory=1Gi,hugepages-1M=2Gi --reserved-memory 1:memory=2Gi)。 每种内存类型的总和应该等于--kube-reserved--system-reserved--eviction-threshold。 了解更多详细信息。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--resolv-conf string     默认值:/etc/resolv.conf
名字解析服务的配置文件名,用作容器 DNS 解析配置的基础。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--root-dir string     默认值:/var/lib/kubelet
设置用于管理 kubelet 文件的根目录(例如挂载卷的相关文件等)。
--rotate-certificates
设置当客户端证书即将过期时 kubelet 自动从 kube-apiserver 请求新的证书进行轮换。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--rotate-server-certificates
当 kubelet 的服务证书即将过期时自动从 kube-apiserver 请求新的证书进行轮换。 要求启用 RotateKubeletServerCertificate 特性门控,以及对提交的 CertificateSigningRequest 对象进行批复(Approve)操作。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--runonce
设置为 true 表示从本地清单或远程 URL 创建完 Pod 后立即退出 kubelet 进程。 与 --enable-server 标志互斥。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--runtime-cgroups string
设置用于创建和运行容器运行时的 cgroup 的绝对名称。
--runtime-request-timeout duration     默认值:2m0s
设置除了长时间运行的请求(包括 pulllogsexecattach 等操作)之外的其他运行时请求的超时时间。 到达超时时间时,请求会被取消,抛出一个错误并会等待重试。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--seccomp-default
启用 RuntimeDefault 作为所有工作负载的默认 seccomp 配置文件。
--serialize-image-pulls     默认值:true
逐一拉取镜像。建议 *不要* 在 docker 守护进程版本低于 1.9 或启用了 Aufs 存储后端的节点上 更改默认值。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--streaming-connection-idle-timeout duration     默认值:4h0m0s
设置流连接在自动关闭之前可以空闲的最长时间。0 表示没有超时限制。 例如:5m。 注意:与 kubelet 服务器的所有连接最长持续时间为 4 小时。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--sync-frequency duration     默认值:1m0s
在运行中的容器与其配置之间执行同步操作的最长时间间隔。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--system-cgroups string
此标志值为一个 cgroup 的绝对名称,用于所有尚未放置在根目录下某 cgroup 内的非内核进程。 空值表示不指定 cgroup。回滚该参数需要重启机器。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--system-reserved string     默认值:无
系统预留的资源配置,以一组 资源名称=资源数量 的格式表示, (例如:cpu=200m,memory=500Mi,ephemeral-storage=1Gi,pid='100')。 目前仅支持 cpumemory 的设置。 更多细节可参考 相关文档。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--system-reserved-cgroup string     默认值:""
此标志给出一个顶层 cgroup 绝对名称,该 cgroup 用于管理非 kubernetes 组件, 这些组件的计算资源通过 --system-reserved 标志进行预留。 例如 "/system-reserved"。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--tls-cert-file string
包含 x509 证书的文件路径,用于 HTTPS 认证。 如果有中间证书,则中间证书要串接在在服务器证书之后。 如果未提供 --tls-cert-file--tls-private-key-file, kubelet 会为公开地址生成自签名证书和密钥,并将其保存到通过 --cert-dir 指定的目录中。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--tls-cipher-suites string
服务器端加密算法列表,以逗号分隔。如果不设置,则使用 Go 语言加密包的默认算法列表。
首选算法: TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_GCM_SHA256, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_256_GCM_SHA384
不安全算法: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_3DES_EDE_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_RSA_WITH_RC4_128_SHA.
(已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--tls-min-version string
设置支持的最小 TLS 版本号,可选的版本号包括:VersionTLS10VersionTLS11VersionTLS12VersionTLS13。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--tls-private-key-file string
包含与 --tls-cert-file 对应的 x509 私钥文件路径。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--topology-manager-policy string     默认值: 'none'
要使用的拓扑管理器策略,用于微调它们的行为。 可能的取值有:'none''best-effort''restricted''single-numa-node'。 (已弃用:此参数应通过 kubelet 的 --config 标志指定的配置文件设置。请参阅 kubelet-config-file 了解更多信息。)
--topology-manager-policy-options string
设置拓扑管理策略(Topology Manager policy)。可选值包括:nonebest-effortrestrictedsingle-numa-node。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--topology-manager-scope string     默认值:container
拓扑提示信息使用范围。拓扑管理器从提示提供者(Hints Providers)处收集提示信息, 并将其应用到所定义的范围以确保 Pod 准入。 可选值包括:container(默认)、pod。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
-v, --v Level
设置 kubelet 日志级别详细程度的数值。
--version version[=true]
打印 kubelet 版本信息并退出;--version=vX.Y.Z... 设置报告的版本。
--vmodule <一个 “pattern=N” 格式的字符串列表>
以逗号分隔的 pattern=N 设置列表,用于文件过滤的日志记录(仅适用于文本日志格式)。
--volume-plugin-dir string     默认值:/usr/libexec/kubernetes/kubelet-plugins/volume/exec/
用来搜索第三方存储卷插件的目录。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)
--volume-stats-agg-period duration     默认值:1m0s
指定 kubelet 计算和缓存所有 Pod 和卷的磁盘用量总值的时间间隔。要禁用磁盘用量计算, 可设置为负数。 (已弃用:应在 --config 所给的配置文件中进行设置。 请参阅 kubelet-config-file 了解更多信息。)

6.13.4 - kube-apiserver

简介

Kubernetes API 服务器验证并配置 API 对象的数据, 这些对象包括 pods、services、replicationcontrollers 等。 API 服务器为 REST 操作提供服务,并为集群的共享状态提供前端, 所有其他组件都通过该前端进行交互。

kube-apiserver [flags]

选项

--admission-control-config-file string

包含准入控制配置的文件。

--advertise-address string

向集群成员通知 apiserver 消息的 IP 地址。 这个地址必须能够被集群中其他成员访问。 如果 IP 地址为空,将会使用 --bind-address, 如果未指定 --bind-address,将会使用主机的默认接口地址。

--aggregator-reject-forwarding-redirect     默认值:true

聚合器拒绝将重定向响应转发回客户端。

--allow-metric-labels stringToString     默认值:[]

允许使用的指标标签到指标值的映射列表。键的格式为 <MetricName>,<LabelName>. 值的格式为 <allowed_value>,<allowed_value>...。 例如:metric1,label1='v1,v2,v3', metric1,label2='v1,v2,v3' metric2,label1='v1,v2,v3'

--allow-metric-labels-manifest string

包含允许列表映射的清单文件的路径。此文件的格式与 --allow-metric-labels 相同。 请注意,--allow-metric-labels 标志将覆盖清单文件。

--allow-privileged
如果为 true,将允许特权容器。[默认值=false]
--anonymous-auth     默认值:true
启用针对 API 服务器的安全端口的匿名请求。 未被其他身份认证方法拒绝的请求被当做匿名请求。 匿名请求的用户名为 system:anonymous, 用户组名为 system:unauthenticated。
--api-audiences strings
API 的标识符。 服务帐户令牌验证者将验证针对 API 使用的令牌是否已绑定到这些受众中的至少一个。 如果配置了 --service-account-issuer 标志,但未配置此标志, 则此字段默认为包含发布者 URL 的单个元素列表。
--audit-log-batch-buffer-size int     默认值:10000
批处理和写入事件之前用于缓存事件的缓冲区大小。 仅在批处理模式下使用。
--audit-log-batch-max-size int     默认值:1
每个批次的最大大小。仅在批处理模式下使用。
--audit-log-batch-max-wait duration
强制写入尚未达到最大大小的批次之前要等待的时间。 仅在批处理模式下使用。
--audit-log-batch-throttle-burst int
如果之前未使用 ThrottleQPS,则为同时发送的最大请求数。 仅在批处理模式下使用。
--audit-log-batch-throttle-enable
是否启用了批量限制。仅在批处理模式下使用。
--audit-log-batch-throttle-qps float
每秒的最大平均批次数。仅在批处理模式下使用。
--audit-log-compress
若设置了此标志,则被轮换的日志文件会使用 gzip 压缩。
--audit-log-format string     默认值:"json"
所保存的审计格式。 "legacy" 表示每行一个事件的文本格式。"json" 表示结构化的 JSON 格式。 已知格式为 legacy,json。
--audit-log-maxage int
根据文件名中编码的时间戳保留旧审计日志文件的最大天数。
--audit-log-maxbackup int
要保留的旧的审计日志文件个数上限。 将值设置为 0 表示对文件个数没有限制。
--audit-log-maxsize int
轮换之前,审计日志文件的最大大小(以兆字节为单位)。
--audit-log-mode string     默认值:"blocking"
用来发送审计事件的策略。 阻塞(blocking)表示发送事件应阻止服务器响应。 批处理(batch)会导致后端异步缓冲和写入事件。 已知的模式是批处理(batch),阻塞(blocking),严格阻塞(blocking-strict)。
--audit-log-path string
如果设置,则所有到达 API 服务器的请求都将记录到该文件中。 "-" 表示标准输出。
--audit-log-truncate-enabled
是否启用事件和批次截断。
--audit-log-truncate-max-batch-size int     默认值:10485760
发送到下层后端的每批次的最大数据量。 实际的序列化大小可能会增加数百个字节。 如果一个批次超出此限制,则将其分成几个较小的批次。
--audit-log-truncate-max-event-size int     默认值:102400
发送到下层后端的每批次的最大数据量。 如果事件的大小大于此数字,则将删除第一个请求和响应; 如果这样做没有减小足够大的程度,则将丢弃事件。
--audit-log-version string     默认值:"audit.k8s.io/v1"
用于对写入日志的审计事件执行序列化的 API 组和版本。
--audit-policy-file string
定义审计策略配置的文件的路径。
--audit-webhook-batch-buffer-size int     默认值:10000
划分批次和写入之前用于存储事件的缓冲区大小。 仅在批处理模式下使用。
--audit-webhook-batch-max-size int     默认值:400
批次的最大大小。 仅在批处理模式下使用。
--audit-webhook-batch-max-wait duration     默认值:30s
强制写入尚未达到最大大小的批处理之前要等待的时间。 仅在批处理模式下使用。
--audit-webhook-batch-throttle-burst int     默认值:15
如果之前未使用 ThrottleQPS,同时发送的最大请求数。 仅在批处理模式下使用。
--audit-webhook-batch-throttle-enable     默认值:true
是否启用了批量限制。仅在批处理模式下使用。
--audit-webhook-batch-throttle-qps float32     默认值:10
每秒的最大平均批次数。仅在批处理模式下使用。
--audit-webhook-config-file string
定义审计 webhook 配置的 kubeconfig 格式文件的路径。
--audit-webhook-initial-backoff duration     默认值:10s
重试第一个失败的请求之前要等待的时间。
--audit-webhook-mode string     默认值:"batch"
发送审计事件的策略。 阻止(Blocking)表示发送事件应阻止服务器响应。 批处理(Batch)导致后端异步缓冲和写入事件。 已知的模式是批处理(batch),阻塞(blocking),严格阻塞(blocking-strict)。
--audit-webhook-truncate-enabled
是否启用事件和批处理截断。
--audit-webhook-truncate-max-batch-size int     默认值:10485760
发送到下层后端的批次的最大数据量。 实际的序列化大小可能会增加数百个字节。 如果一个批次超出此限制,则将其分成几个较小的批次。
--audit-webhook-truncate-max-event-size int     默认值:102400
发送到下层后端的批次的最大数据量。 如果事件的大小大于此数字,则将删除第一个请求和响应; 如果事件和事件的大小没有减小到一定幅度,则将丢弃事件。
--audit-webhook-version string     默认值:"audit.k8s.io/v1"
用于序列化写入 Webhook 的审计事件的 API 组和版本。
--authentication-config string

用于配置 JWT 令牌验证器的身份验证配置文件。注意:此特性自 v1.29 起处于 Alpha 阶段。 需要设置 --feature-gate=StructuredAuthenticationConfiguration=true 才能启用此特性。 此特性与 oidc-* 标志互斥。

--authentication-token-webhook-cache-ttl duration     2m0s
对来自 Webhook 令牌身份验证器的响应的缓存时间。
--authentication-token-webhook-config-file string
包含 Webhook 配置的 kubeconfig 格式文件,用于进行令牌认证。 API 服务器将查询远程服务,以对持有者令牌进行身份验证。
--authentication-token-webhook-version string     默认值:"v1beta1"
与 Webhook 之间交换 authentication.k8s.io TokenReview 时使用的 API 版本。
--authorization-config string

用于配置鉴权链的鉴权配置文件。注意:此特性自 v1.29 起处于 Alpha 阶段。 需要将 --feature-gate=StructuredAuthorizationConfiguration=true 特性标志设置为 true 才能启用此特性。 此特性与其他 --authorization-mode和--authorization-webhook-* 标志互斥。

--authorization-mode strings     默认值:"AlwaysAllow"
在安全端口上进行鉴权的插件的顺序列表。 逗号分隔的列表:AlwaysAllow、AlwaysDeny、ABAC、Webhook、RBAC、Node。
--authorization-policy-file string
包含鉴权策略的文件,其内容为分行 JSON 格式, 在安全端口上与 --authorization-mode=ABAC 一起使用。
--authorization-webhook-cache-authorized-ttl duration     默认值:5m0s
对来自 Webhook 鉴权组件的 “授权(authorized)” 响应的缓存时间。
--authorization-webhook-cache-unauthorized-ttl duration     默认值:30s
对来自 Webhook 鉴权模块的 “未授权(unauthorized)” 响应的缓存时间。
--authorization-webhook-config-file string
包含 Webhook 配置的文件,其格式为 kubeconfig, 与 --authorization-mode=Webhook 一起使用。 API 服务器将查询远程服务,以对 API 服务器的安全端口的访问执行鉴权。
--authorization-webhook-version string     默认值:"v1beta1"
与 Webhook 之间交换 authorization.k8s.io SubjectAccessReview 时使用的 API 版本。
--azure-container-registry-config string
包含 Azure 容器仓库配置信息的文件的路径。
--bind-address string     默认值:"0.0.0.0"
用来监听 --secure-port 端口的 IP 地址。 集群的其余部分以及 CLI/web 客户端必须可以访问所关联的接口。 如果为空白或未指定地址(0.0.0.0::),则将使用所有接口和 IP 地址簇。
--cert-dir string     默认值:"/var/run/kubernetes"
TLS 证书所在的目录。 如果提供了 --tls-cert-file--tls-private-key-file 标志值,则将忽略此标志。
--client-ca-file string
如果已设置,则使用与客户端证书的 CommonName 对应的标识对任何出示由 client-ca 文件中的授权机构之一签名的客户端证书的请求进行身份验证。
--cloud-provider-gce-l7lb-src-cidrs cidrs     默认值:"130.211.0.0/22,35.191.0.0/16"
在 GCE 防火墙中打开 CIDR,以进行第 7 层负载均衡流量代理和健康状况检查。
--contention-profiling
如果启用了性能分析,则启用阻塞分析。
--cors-allowed-origins strings

CORS 允许的来源清单,以逗号分隔。 允许的来源可以是支持子域匹配的正则表达式。 如果此列表为空,则不会启用 CORS。 请确保每个表达式与整个主机名相匹配,方法是用'^'锚定开始或包括'//'前缀,同时用'$'锚定结束或包括':'端口分隔符后缀。 有效表达式的例子是'//example.com(:|$)'和'^https://example.com(:|$)'。

--debug-socket-path string

使用位于给定路径的、未受保护的(无身份认证或鉴权的)UNIX 域套接字执行性能分析。

--default-not-ready-toleration-seconds int     默认值:300
对污点 NotReady:NoExecute 的容忍时长(以秒计)。 默认情况下这一容忍度会被添加到尚未具有此容忍度的每个 pod 中。
--default-unreachable-toleration-seconds int     默认值:300
对污点 Unreachable:NoExecute 的容忍时长(以秒计) 默认情况下这一容忍度会被添加到尚未具有此容忍度的每个 pod 中。
--delete-collection-workers int     默认值:1
为 DeleteCollection 调用而产生的工作线程数。 这些用于加速名字空间清理。
--disable-admission-plugins strings

尽管位于默认启用的插件列表中,仍须被禁用的准入插件(NamespaceLifecycle、LimitRanger、ServiceAccount、TaintNodesByCondition、PodSecurity、Priority、DefaultTolerationSeconds、DefaultStorageClass、StorageObjectInUseProtection、PersistentVolumeClaimResize、RuntimeClass、CertificateApproval、CertificateSigning、ClusterTrustBundleAttest、CertificateSubjectRestriction、DefaultIngressClass、MutatingAdmissionWebhook、ValidatingAdmissionPolicy、ValidatingAdmissionWebhook、ResourceQuota)。 取值为逗号分隔的准入插件列表:AlwaysAdmit、AlwaysDeny、AlwaysPullImages、CertificateApproval、CertificateSigning、CertificateSubjectRestriction、ClusterTrustBundleAttest、DefaultIngressClass、DefaultStorageClass、DefaultTolerationSeconds、DenyServiceExternalIPs、EventRateLimit、ExtendedResourceToleration、ImagePolicyWebhook、LimitPodHardAntiAffinityTopology、LimitRanger、MutatingAdmissionWebhook、NamespaceAutoProvision、NamespaceExists、NamespaceLifecycle、NodeRestriction、OwnerReferencesPermissionEnforcement、PersistentVolumeClaimResize、PersistentVolumeLabel、PodNodeSelector、PodSecurity、PodTolerationRestriction、Priority、ResourceQuota、RuntimeClass、SecurityContextDeny、ServiceAccount、StorageObjectInUseProtection、TaintNodesByCondition、ValidatingAdmissionPolicy、ValidatingAdmissionWebhook。 该标志中插件的顺序无关紧要。

--disabled-metrics strings
此标志为行为不正确的度量指标提供一种处理方案。 你必须提供完全限定的指标名称才能将其禁止。 声明:禁用度量值的行为优先于显示已隐藏的度量值。
--egress-selector-config-file string
带有 API 服务器出站选择器配置的文件。
--enable-admission-plugins strings

除了默认启用的插件(NamespaceLifecycle、LimitRanger、ServiceAccount、TaintNodesByCondition、PodSecurity、Priority、DefaultTolerationSeconds、DefaultStorageClass、StorageObjectInUseProtection、PersistentVolumeClaimResize、RuntimeClass、CertificateApproval、CertificateSigning、ClusterTrustBundleAttest、CertificateSubjectRestriction、DefaultIngressClass、MutatingAdmissionWebhook、ValidatingAdmissionPolicy、ValidatingAdmissionWebhook、ResourceQuota)之外要启用的准入插件。 取值为逗号分隔的准入插件列表:AlwaysAdmit、AlwaysDeny、AlwaysPullImages、CertificateApproval、CertificateSigning、CertificateSubjectRestriction、ClusterTrustBundleAttest、DefaultIngressClass、DefaultStorageClass、DefaultTolerationSeconds、DenyServiceExternalIPs、EventRateLimit、ExtendedResourceToleration、ImagePolicyWebhook、LimitPodHardAntiAffinityTopology、LimitRanger、MutatingAdmissionWebhook、NamespaceAutoProvision、NamespaceExists、NamespaceLifecycle、NodeRestriction、OwnerReferencesPermissionEnforcement、PersistentVolumeClaimResize、PersistentVolumeLabel、PodNodeSelector、PodSecurity、PodTolerationRestriction、Priority、ResourceQuota、RuntimeClass、SecurityContextDeny、ServiceAccount、StorageObjectInUseProtection、TaintNodesByCondition、ValidatingAdmissionPolicy、ValidatingAdmissionWebhook。该标志中插件的顺序无关紧要。

--enable-aggregator-routing
允许聚合器将请求路由到端点 IP 而非集群 IP。
--enable-bootstrap-token-auth
启用以允许将 "kube-system" 名字空间中类型为 "bootstrap.kubernetes.io/token" 的 Secret 用于 TLS 引导身份验证。
--enable-garbage-collector     默认值:true
启用通用垃圾收集器。必须与 kube-controller-manager 的相应标志同步。
--enable-priority-and-fairness     默认值:true
如果为 true,则使用增强的处理程序替换 max-in-flight 处理程序, 以便根据优先级和公平性完成排队和调度。
--encryption-provider-config string
包含加密提供程序配置信息的文件,用在 etcd 中所存储的 Secret 上。
--encryption-provider-config-automatic-reload

确定由 --encryption-provider-config 设置的文件是否应在磁盘内容更改时自动重新加载。 将此标志设置为 true 将禁用通过 API 服务器 healthz 端点来唯一地标识不同 KMS 插件的能力。

--endpoint-reconciler-type string     默认值:"lease"
使用端点协调器(master-countleasenone)。 master-count 已弃用,并将在未来版本中删除。
--etcd-cafile string
用于保护 etcd 通信的 SSL 证书颁发机构文件。
--etcd-certfile string
用于保护 etcd 通信的 SSL 证书文件。
--etcd-compaction-interval duration     默认值:5m0s
压缩请求的间隔。 如果为0,则禁用来自 API 服务器的压缩请求。
--etcd-count-metric-poll-period duration     默认值:1m0s
针对每种类型的资源数量轮询 etcd 的频率。 0 值表示禁用度量值收集。
--etcd-db-metric-poll-interval duration     默认值:30s
轮询 etcd 和更新度量值的请求间隔。0 值表示禁用度量值收集。
--etcd-healthcheck-timeout duration      检查 etcd 健康状况时使用的超时时长。
--etcd-keyfile string
用于保护 etcd 通信的 SSL 密钥文件。
--etcd-prefix string     默认值:"/registry"
要在 etcd 中所有资源路径之前添加的前缀。
--etcd-readycheck-timeout 时长     默认值: 2s

检查 etcd 是否就绪时使用的超时

--etcd-servers strings
要连接的 etcd 服务器列表(scheme://ip:port),以逗号分隔。
--etcd-servers-overrides strings
etcd 服务器针对每个资源的重载设置,以逗号分隔。 单个替代格式:组/资源#服务器(group/resource#servers), 其中服务器是 URL,以分号分隔。 注意,此选项仅适用于编译进此服务器二进制文件的资源。
--event-ttl duration     默认值:1h0m0s
事件的保留时长。
--external-hostname string
为此主机生成外部化 UR L时要使用的主机名(例如 Swagger API 文档或 OpenID 发现)。
--feature-gates < 逗号分隔的 'key=True|False' 键值对>

一组 key=value 对,用来描述测试性/试验性功能的特性门控。可选项有:
APIResponseCompression=true|false (BETA - 默认值=true)
APIServerIdentity=true|false (BETA - 默认值=true)
APIServerTracing=true|false (BETA - 默认值=true)
AdmissionWebhookMatchConditions=true|false (BETA - 默认值=true)
AggregatedDiscoveryEndpoint=true|false (BETA - 默认值=true)
AllAlpha=true|false (ALPHA - 默认值=false)
AllBeta=true|false (BETA - 默认值=false)
AnyVolumeDataSource=true|false (BETA - 默认值=true)
AppArmor=true|false (BETA - 默认值=true)
CPUManagerPolicyAlphaOptions=true|false (ALPHA - 默认值=false)
CPUManagerPolicyBetaOptions=true|false (BETA - 默认值=true)
CPUManagerPolicyOptions=true|false (BETA - 默认值=true)
CRDValidationRatcheting=true|false (ALPHA - 默认值=false)
CSIMigrationPortworx=true|false (BETA - 默认值=false)
CSIVolumeHealth=true|false (ALPHA - 默认值=false)
CloudControllerManagerWebhook=true|false (ALPHA - 默认值=false)
CloudDualStackNodeIPs=true|false (BETA - 默认值=true)
ClusterTrustBundle=true|false (ALPHA - 默认值=false)
ClusterTrustBundleProjection=true|false (ALPHA - 默认值=false)
ComponentSLIs=true|false (BETA - 默认值=true)
ConsistentListFromCache=true|false (ALPHA - 默认值=false)
ContainerCheckpoint=true|false (ALPHA - 默认值=false)
ContextualLogging=true|false (ALPHA - 默认值=false)
CronJobsScheduledAnnotation=true|false (BETA - 默认值=true)
CrossNamespaceVolumeDataSource=true|false (ALPHA - 默认值=false)
CustomCPUCFSQuotaPeriod=true|false (ALPHA - 默认值=false)
DevicePluginCDIDevices=true|false (BETA - 默认值=true)
DisableCloudProviders=true|false (BETA - 默认值=true)
DisableKubeletCloudCredentialProviders=true|false (BETA - 默认值=true)
DisableNodeKubeProxyVersion=true|false (ALPHA - 默认值=false)
DynamicResourceAllocation=true|false (ALPHA - 默认值=false)
ElasticIndexedJob=true|false (BETA - 默认值=true)
EventedPLEG=true|false (BETA - 默认值=false)
GracefulNodeShutdown=true|false (BETA - 默认值=true)
GracefulNodeShutdownBasedOnPodPriority=true|false (BETA - 默认值=true)
HPAContainerMetrics=true|false (BETA - 默认值=true)
HPAScaleToZero=true|false (ALPHA - 默认值=false)
HonorPVReclaimPolicy=true|false (ALPHA - 默认值=false)
ImageMaximumGCAge=true|false (ALPHA - 默认值=false)
InPlacePodVerticalScaling=true|false (ALPHA - 默认值=false)
InTreePluginAWSUnregister=true|false (ALPHA - 默认值=false)
InTreePluginAzureDiskUnregister=true|false (ALPHA - 默认值=false)
InTreePluginAzureFileUnregister=true|false (ALPHA - 默认值=false)
InTreePluginGCEUnregister=true|false (ALPHA - 默认值=false)
InTreePluginOpenStackUnregister=true|false (ALPHA - 默认值=false)
InTreePluginPortworxUnregister=true|false (ALPHA - 默认值=false)
InTreePluginvSphereUnregister=true|false (ALPHA - 默认值=false)
JobBackoffLimitPerIndex=true|false (BETA - 默认值=true)
JobPodFailurePolicy=true|false (BETA - 默认值=true)
JobPodReplacementPolicy=true|false (BETA - 默认值=true)
KubeProxyDrainingTerminatingNodes=true|false (ALPHA - 默认值=false)
KubeletCgroupDriverFromCRI=true|false (ALPHA - 默认值=false)
KubeletInUserNamespace=true|false (ALPHA - 默认值=false)
KubeletPodResourcesDynamicResources=true|false (ALPHA - 默认值=false)
KubeletPodResourcesGet=true|false (ALPHA - 默认值=false)
KubeletSeparateDiskGC=true|false (ALPHA - 默认值=false)
KubeletTracing=true|false (BETA - 默认值=true)
LegacyServiceAccountTokenCleanUp=true|false (BETA - 默认值=true)
LoadBalancerIPMode=true|false (ALPHA - 默认值=false)
LocalStorageCapacityIsolationFSQuotaMonitoring=true|false (ALPHA - 默认值=false)
LogarithmicScaleDown=true|false (BETA - 默认值=true)
LoggingAlphaOptions=true|false (ALPHA - 默认值=false)
LoggingBetaOptions=true|false (BETA - 默认值=true)
MatchLabelKeysInPodAffinity=true|false (ALPHA - 默认值=false)
MatchLabelKeysInPodTopologySpread=true|false (BETA - 默认值=true)
MaxUnavailableStatefulSet=true|false (ALPHA - 默认值=false)
MemoryManager=true|false (BETA - 默认值=true)
MemoryQoS=true|false (ALPHA - 默认值=false)
MinDomainsInPodTopologySpread=true|false (BETA - 默认值=true)
MultiCIDRServiceAllocator=true|false (ALPHA - 默认值=false)
NFTablesProxyMode=true|false (ALPHA - 默认值=false)
NewVolumeManagerReconstruction=true|false (BETA - 默认值=true)
NodeInclusionPolicyInPodTopologySpread=true|false (BETA - 默认值=true)
NodeLogQuery=true|false (ALPHA - 默认值=false)
NodeSwap=true|false (BETA - 默认值=false)
OpenAPIEnums=true|false (BETA - 默认值=true)
PDBUnhealthyPodEvictionPolicy=true|false (BETA - 默认值=true)
PersistentVolumeLastPhaseTransitionTime=true|false (BETA - 默认值=true)
PodAndContainerStatsFromCRI=true|false (ALPHA - 默认值=false)
PodDeletionCost=true|false (BETA - 默认值=true)
PodDisruptionConditions=true|false (BETA - 默认值=true)
PodHostIPs=true|false (BETA - 默认值=true)
PodIndexLabel=true|false (BETA - 默认值=true)
PodLifecycleSleepAction=true|false (ALPHA - 默认值=false)
PodReadyToStartContainersCondition=true|false (BETA - 默认值=true)
PodSchedulingReadiness=true|false (BETA - 默认值=true)
ProcMountType=true|false (ALPHA - 默认值=false)
QOSReserved=true|false (ALPHA - 默认值=false)
RecoverVolumeExpansionFailure=true|false (ALPHA - 默认值=false)
RotateKubeletServerCertificate=true|false (BETA - 默认值=true)
RuntimeClassInImageCriApi=true|false (ALPHA - 默认值=false)
SELinuxMountReadWriteOncePod=true|false (BETA - 默认值=true)
SchedulerQueueingHints=true|false (BETA - 默认值=false)
SecurityContextDeny=true|false (ALPHA - 默认值=false)
SeparateTaintEvictionController=true|false (BETA - 默认值=true)
ServiceAccountTokenJTI=true|false (ALPHA - 默认值=false)
ServiceAccountTokenNodeBinding=true|false (ALPHA - 默认值=false)
ServiceAccountTokenNodeBindingValidation=true|false (ALPHA - 默认值=false)
ServiceAccountTokenPodNodeInfo=true|false (ALPHA - 默认值=false)
SidecarContainers=true|false (BETA - 默认值=true)
SizeMemoryBackedVolumes=true|false (BETA - 默认值=true)
StableLoadBalancerNodeSet=true|false (BETA - 默认值=true)
StatefulSetAutoDeletePVC=true|false (BETA - 默认值=true)
StatefulSetStartOrdinal=true|false (BETA - 默认值=true)
StorageVersionAPI=true|false (ALPHA - 默认值=false)
StorageVersionHash=true|false (BETA - 默认值=true)
StructuredAuthenticationConfiguration=true|false (ALPHA - 默认值=false)
StructuredAuthorizationConfiguration=true|false (ALPHA - 默认值=false)
TopologyAwareHints=true|false (BETA - 默认值=true)
TopologyManagerPolicyAlphaOptions=true|false (ALPHA - 默认值=false)
TopologyManagerPolicyBetaOptions=true|false (BETA - 默认值=true)
TopologyManagerPolicyOptions=true|false (BETA - 默认值=true)
TranslateStreamCloseWebsocketRequests=true|false (ALPHA - 默认值=false)
UnauthenticatedHTTP2DOSMitigation=true|false (BETA - 默认值=true)
UnknownVersionInteroperabilityProxy=true|false (ALPHA - 默认值=false)
UserNamespacesPodSecurityStandards=true|false (ALPHA - 默认值=false)
UserNamespacesSupport=true|false (ALPHA - 默认值=false)
ValidatingAdmissionPolicy=true|false (BETA - 默认值=false)
VolumeAttributesClass=true|false (ALPHA - 默认值=false)
VolumeCapacityPriority=true|false (ALPHA - 默认值=false)
WatchList=true|false (ALPHA - 默认值=false)
WinDSR=true|false (ALPHA - 默认值=false)
WinOverlay=true|false (BETA - 默认值=true)
WindowsHostNetwork=true|false (ALPHA - 默认值=true)
ZeroLimitedNominalConcurrencyShares=true|false (BETA - 默认值=false)

--goaway-chance float
为防止 HTTP/2 客户端卡在单个 API 服务器上,随机关闭某连接(GOAWAY)。 客户端的其他运行中请求不会受到影响。被关闭的客户端将重新连接, 重新被负载均衡后可能会与其他 API 服务器开始通信。 此参数设置将被发送 GOAWAY 指令的请求的比例。 只有一个 API 服务器或不使用负载均衡器的集群不应启用此特性。 最小值为 0(关闭),最大值为 .02(1/50 请求);建议使用 .001(1/1000)。
-h, --help
kube-apiserver 的帮助命令
--http2-max-streams-per-connection int
服务器为客户端提供的 HTTP/2 连接中最大流数的限制。 零表示使用 GoLang 的默认值。
--kubelet-certificate-authority string
证书颁发机构的证书文件的路径。
--kubelet-client-certificate string
TLS 的客户端证书文件的路径。
--kubelet-client-key string
TLS 客户端密钥文件的路径。
--kubelet-preferred-address-types strings     默认值:Hostname,InternalDNS,InternalIP,ExternalDNS,ExternalIP
用于 kubelet 连接的首选 NodeAddressTypes 列表。
--kubelet-timeout duration     默认值:5s
kubelet 操作超时时间。
--kubernetes-service-node-port int
如果非零,那么 Kubernetes 主服务(由 apiserver 创建/维护)将是 NodePort 类型, 使用此字段值作为端口值。 如果为零,则 Kubernetes 主服务将为 ClusterIP 类型。
--lease-reuse-duration-seconds int     默认值:60
每个租约被重用的时长。 如果此值比较低,可以避免大量对象重用此租约。 注意,如果此值过小,可能导致存储层出现性能问题。
--livez-grace-period duration
此选项代表 API 服务器完成启动序列并生效所需的最长时间。 从 API 服务器的启动时间到这段时间为止, /livez 将假定未完成的启动后钩子将成功完成,因此返回 true。
--log-flush-frequency duration     默认值:5s
两次日志刷新之间的最大秒数。
--logging-format string     默认值:"text"

设置日志格式。允许的格式:"text"。

--master-service-namespace string     默认值:"default"
已废弃:应该从其中将 Kubernetes 主服务注入到 Pod 中的名字空间。
--max-connection-bytes-per-sec int
如果不为零,则将每个用户连接的带宽限制为此数值(字节数/秒)。 当前仅适用于长时间运行的请求。
--max-mutating-requests-inflight int     默认值:200
如果 --enable-priority-and-fairness 为 true,那么此值和 --max-requests-inflight 的和将确定服务器的总并发限制(必须是正数)。 否则,该值限制同时运行的变更类型的请求的个数上限。0 表示无限制。
--max-requests-inflight int     默认值:400
如果 --enable-priority-and-fairness 为 true,那么此值和 --max-mutating-requests-inflight 的和将确定服务器的总并发限制(必须是正数)。 否则,该值限制进行中非变更类型请求的最大个数,零表示无限制。
--min-request-timeout int     默认值:1800
可选字段,表示处理程序在请求超时前,必须保持连接处于打开状态的最小秒数。 当前只对监听(Watch)请求的处理程序有效。 Watch 请求的处理程序会基于这个值选择一个随机数作为连接超时值, 以达到分散负载的目的。
--oidc-ca-file string
如果设置该值,将会使用 oidc-ca-file 中的机构之一对 OpenID 服务的证书进行验证, 否则将会使用主机的根 CA 对其进行验证。
--oidc-client-id string
OpenID 连接客户端的要使用的客户 ID,如果设置了 oidc-issuer-url,则必须设置这个值。
--oidc-groups-claim string
如果提供该值,这个自定义 OpenID 连接声明将被用来设定用户组。 该声明值需要是一个字符串或字符串数组。 此标志为实验性的,请查阅身份认证相关文档进一步了解详细信息。
--oidc-groups-prefix string
如果提供了此值,则所有组都将以该值作为前缀,以防止与其他身份认证策略冲突。
--oidc-issuer-url string
OpenID 颁发者 URL,只接受 HTTPS 方案。 如果设置该值,它将被用于验证 OIDC JSON Web Token(JWT)。
--oidc-required-claim <逗号分隔的 'key=value' 键值对列表>
描述 ID 令牌中必需声明的键值对。 如果设置此值,则会验证 ID 令牌中存在与该声明匹配的值。 重复此标志以指定多个声明。
--oidc-signing-algs strings     默认值:RS256
允许的 JOSE 非对称签名算法的逗号分隔列表。 具有收支持 "alg" 标头值的 JWTs 有:RS256、RS384、RS512、ES256、ES384、ES512、PS256、PS384、PS512。 取值依据 RFC 7518 https://tools.ietf.org/html/rfc7518#section-3.1 定义。
--oidc-username-claim string     默认值:"sub"
要用作用户名的 OpenID 声明。 请注意,除默认声明("sub")以外的其他声明不能保证是唯一且不可变的。 此标志是实验性的,请参阅身份认证文档以获取更多详细信息。
--oidc-username-prefix string
如果提供,则所有用户名都将以该值作为前缀。 如果未提供,则除 "email" 之外的用户名声明都会添加颁发者 URL 作为前缀,以避免冲突。 要略过添加前缀处理,请设置值为 "-"。
--peer-advertise-ip string

如果设置并启用了 UnknownVersionInteroperabilityProxy 特性门控, 当请求由于 kube-apiservers 之间的版本偏差而无法被处理时, 此 IP 将由对等 kube-apiserver 用于代理请求到该 kube-apiserver。 此标志仅被用于配置了多个 kube-apiserver 以实现高可用性的集群中。

--peer-advertise-port string

如果设置并且启用了 UnknownVersionInteroperabilityProxy 特性门控, 当请求由于 kube-apiservers 之间的版本偏差导致对等方无法被处理时, 此端口将由对等 kube-apiserver 用于代理请求到该 kube-apiserver。 此标志仅被用于配置了多个 kube-apiserver 以实现高可用性的集群中。

--peer-ca-file string

如果设置并启用了 UnknownVersionInteroperabilityProxy 特性门控, 此文件将被用于验证对等 kube-apiserver 的服务证书。 此标志仅被用于配置了多个 kube-apiserver 以实现高可用性的集群中。

--permit-address-sharing

若此标志为 true,则使用 SO_REUSEADDR 来绑定端口。 这样设置可以同时绑定到用通配符表示的类似 0.0.0.0 这种 IP 地址, 以及特定的 IP 地址。也可以避免等待内核释放 TIME_WAIT 状态的套接字。[默认值=false]

--permit-port-sharing
如果为 true,则在绑定端口时将使用 SO_REUSEPORT, 这样多个实例可以绑定到同一地址和端口上。[默认值=false]
--profiling     默认值:true
通过 Web 接口 host:port/debug/pprof/ 启用性能分析。
--proxy-client-cert-file string
当必须调用外部程序以处理请求时,用于证明聚合器或者 kube-apiserver 的身份的客户端证书。 包括代理转发到用户 api-server 的请求和调用 Webhook 准入控制插件的请求。 Kubernetes 期望此证书包含来自于 --requestheader-client-ca-file 标志中所给 CA 的签名。 该 CA 在 kube-system 命名空间的 "extension-apiserver-authentication" ConfigMap 中公开。 从 kube-aggregator 收到调用的组件应该使用该 CA 进行各自的双向 TLS 验证。
--proxy-client-key-file string
当必须调用外部程序来处理请求时,用来证明聚合器或者 kube-apiserver 的身份的客户端私钥。 这包括代理转发给用户 api-server 的请求和调用 Webhook 准入控制插件的请求。
--request-timeout duration     默认值:1m0s
可选字段,指示处理程序在超时之前必须保持打开请求的持续时间。 这是请求的默认请求超时,但对于特定类型的请求,可能会被 --min-request-timeout等标志覆盖。
--requestheader-allowed-names strings
此值为客户端证书通用名称(Common Name)的列表;表中所列的表项可以用来提供用户名, 方式是使用 --requestheader-username-headers 所指定的头部。 如果为空,能够通过 --requestheader-client-ca-file 中机构 认证的客户端证书都是被允许的。
--requestheader-client-ca-file string
在信任请求头中以 --requestheader-username-headers 指示的用户名之前, 用于验证接入请求中客户端证书的根证书包。 警告:一般不要假定传入请求已被授权。
--requestheader-extra-headers-prefix strings
用于查验请求头部的前缀列表。建议使用 X-Remote-Extra-
--requestheader-group-headers strings
用于查验用户组的请求头部列表。建议使用 X-Remote-Group
--requestheader-username-headers strings
用于查验用户名的请求头部字段列表。建议使用 X-Remote-User
--runtime-config <逗号分隔的 'key=value' 对列表>
一组启用或禁用内置 API 的键值对。支持的选项包括:
v1=true|false(针对核心 API 组)
<group>/<version>=true|false(针对特定 API 组和版本,例如:apps/v1=true)
api/all=true|false 控制所有 API 版本
api/ga=true|false 控制所有 v[0-9]+ API 版本
api/beta=true|false 控制所有 v[0-9]+beta[0-9]+ API 版本
api/alpha=true|false 控制所有 v[0-9]+alpha[0-9]+ API 版本
api/legacy 已弃用,并将在以后的版本中删除
--secure-port int     默认值:6443
带身份验证和鉴权机制的 HTTPS 服务端口。 不能用 0 关闭。
--service-account-extend-token-expiration     默认值:true
在生成令牌时,启用投射服务帐户到期时间扩展, 这有助于从旧版令牌安全地过渡到绑定的服务帐户令牌功能。 如果启用此标志,则准入插件注入的令牌的过期时间将延长至 1 年,以防止过渡期间发生意外故障, 并忽略 service-account-max-token-expiration 的值。
--service-account-issuer strings
服务帐号令牌颁发者的标识符。 颁发者将在已颁发令牌的 "iss" 声明中检查此标识符。 此值为字符串或 URI。 如果根据 OpenID Discovery 1.0 规范检查此选项不是有效的 URI,则即使特性门控设置为 true, ServiceAccountIssuerDiscovery 功能也将保持禁用状态。 强烈建议该值符合 OpenID 规范: https://openid.net/specs/openid-connect-discovery-1_0.html 。 实践中,这意味着 service-account-issuer 取值必须是 HTTPS URL。 还强烈建议此 URL 能够在 {service-account-issuer}/.well-known/openid-configuration 处提供 OpenID 发现文档。 当此值被多次指定时,第一次的值用于生成令牌,所有的值用于确定接受哪些发行人。
--service-account-jwks-uri string
覆盖 /.well-known/openid-configuration 提供的发现文档中 JSON Web 密钥集的 URI。 如果发现文档和密钥集是通过 API 服务器外部 (而非自动检测到或被外部主机名覆盖)之外的 URL 提供给依赖方的,则此标志很有用。
--service-account-key-file strings
包含 PEM 编码的 x509 RSA 或 ECDSA 私钥或公钥的文件,用于验证 ServiceAccount 令牌。 指定的文件可以包含多个键,并且可以使用不同的文件多次指定标志。 如果未指定,则使用 --tls-private-key-file。 提供 --service-account-signing-key-file 时必须指定。
--service-account-lookup     默认值:true
如果为 true,则在身份认证时验证 etcd 中是否存在 ServiceAccount 令牌。
--service-account-max-token-expiration duration
服务帐户令牌发布者创建的令牌的最长有效期。 如果请求有效期大于此值的有效令牌请求,将使用此值的有效期颁发令牌。
--service-account-signing-key-file string
包含服务帐户令牌颁发者当前私钥的文件的路径。 颁发者将使用此私钥签署所颁发的 ID 令牌。
--service-cluster-ip-range string
CIDR 表示的 IP 范围用来为服务分配集群 IP。 此地址不得与指定给节点或 Pod 的任何 IP 范围重叠。 最多允许两个双栈 CIDR。
--service-node-port-range <形式为 'N1-N2' 的字符串>     默认值:30000-32767

保留给具有 NodePort 可见性的服务的端口范围。 不得与节点上的临时端口范围重叠。 例如:"30000-32767"。范围的两端都包括在内。

--show-hidden-metrics-for-version string
你要显示隐藏指标的先前版本。仅先前的次要版本有意义,不允许其他值。 格式为 <major>.<minor>,例如:"1.16"。 这种格式的目的是确保你有机会注意到下一个版本是否隐藏了其他指标, 而不是在此之后将它们从发行版中永久删除时感到惊讶。
--shutdown-delay-duration duration
延迟终止时间。在此期间,服务器将继续正常处理请求。 端点 /healthz 和 /livez 将返回成功,但是 /readyz 立即返回失败。 在此延迟过去之后,将开始正常终止。 这可用于允许负载均衡器停止向该服务器发送流量。
--shutdown-send-retry-after
值为 true 表示 HTTP 服务器将继续监听直到耗尽所有非长时间运行的请求, 在此期间,所有传入请求将被拒绝,状态码为 429,响应头为 "Retry-After", 此外,设置 "Connection: close" 响应头是为了在空闲时断开 TCP 链接。
--shutdown-watch-termination-grace-period duration

此选项如果被设置了,则表示 API 服务器体面关闭服务器窗口内,等待活跃的监听请求耗尽的最长宽限期。

--storage-backend string
持久化存储后端。选项:"etcd3"(默认)。
--storage-media-type string     默认值:"application/vnd.kubernetes.protobuf"

用于在存储中存储对象的媒体类型。某些资源或存储后端可能仅支持特定的媒体类型,并且将忽略此设置。 支持的媒体类型:[application/json, application/yaml, application/vnd.kubernetes.protobuf]

--strict-transport-security-directives strings

为 HSTS 所设置的指令列表,用逗号分隔。 如果此列表为空,则不会添加 HSTS 指令。 例如:'max-age=31536000,includeSubDomains,preload'

--tls-cert-file string
包含用于 HTTPS 的默认 x509 证书的文件。(CA 证书(如果有)在服务器证书之后并置)。 如果启用了 HTTPS 服务,并且未提供 --tls-cert-file--tls-private-key-file, 为公共地址生成一个自签名证书和密钥,并将其保存到 --cert-dir 指定的目录中。
--tls-cipher-suites strings
服务器的密码套件的列表,以逗号分隔。如果省略,将使用默认的 Go 密码套件。
首选值: TLS_AES_128_GCM_SHA256、TLS_AES_256_GCM_SHA384、TLS_CHACHA20_POLY1305_SHA256、TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA、 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256、TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA、TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384、TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305、TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256、TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA、TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256、TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA、TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384、TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305、TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256、TLS_RSA_WITH_AES_128_CBC_SHA、TLS_RSA_WITH_AES_128_GCM_SHA256、TLS_RSA_WITH_AES_256_CBC_SHA、TLS_RSA_WITH_AES_256_GCM_SHA384。 不安全的值有: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256、TLS_ECDHE_ECDSA_WITH_RC4_128_SHA、TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA、TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256、TLS_ECDHE_RSA_WITH_RC4_128_SHA、TLS_RSA_WITH_3DES_EDE_CBC_SHA、TLS_RSA_WITH_AES_128_CBC_SHA256、TLS_RSA_WITH_RC4_128_SHA。
--tls-min-version string
支持的最低 TLS 版本。可能的值:VersionTLS10,VersionTLS11,VersionTLS12,VersionTLS13
--tls-private-key-file string
包含匹配 --tls-cert-file 的 x509 证书私钥的文件。
--tls-sni-cert-key string     默认值:[]
一对 x509 证书和私钥文件路径,(可选)后缀为全限定域名的域名模式列表,可以使用带有通配符的前缀。 域模式也允许使用 IP 地址,但仅当 apiserver 对客户端请求的IP地址具有可见性时,才应使用 IP。 如果未提供域模式,则提取证书的名称。 非通配符匹配优先于通配符匹配,显式域模式优先于提取出的名称。 对于多个密钥/证书对,请多次使用 --tls-sni-cert-key。 示例:"example.crt,example.key" 或 "foo.crt,foo.key:\*.foo.com,foo.com"。
--token-auth-file string
如果设置该值,这个文件将被用于通过令牌认证来保护 API 服务的安全端口。
--tracing-config-file string
包含 API 服务器跟踪配置的文件。
-v, --v int
日志级别详细程度的数字。
--version version[=true]

--version, --version=raw 打印版本信息并退出; --version=vX.Y.Z... 设置报告的版本

--vmodule pattern=N,...
以逗号分隔的 pattern=N 设置列表,用于文件过滤的日志记录(仅适用于 text 日志格式)。
--watch-cache     默认值:true
在 API 服务器中启用监视缓存。
--watch-cache-sizes strings

某些资源(Pod、Node 等)的监视缓存大小设置,以逗号分隔。 每个资源对应的设置格式:resource[.group]#size,其中 resource 为小写复数(无版本), 对于 apiVersion v1(旧版核心 API)的资源要省略 group, 对其它资源要给出 groupsize 为一个数字。 此选项仅对 API 服务器中的内置资源生效,对 CRD 定义的资源或从外部服务器接入的资源无效。 启用 watch-cache 时仅查询此选项。 这里能生效的 size 设置只有 0,意味着禁用关联资源的 watch-cache。 所有的非零值都等效,意味着不禁用该资源的watch-cache

6.13.5 - kube-controller-manager

简介

Kubernetes 控制器管理器是一个守护进程,内嵌随 Kubernetes 一起发布的核心控制回路。 在机器人和自动化的应用中,控制回路是一个永不休止的循环,用于调节系统状态。 在 Kubernetes 中,每个控制器是一个控制回路,通过 API 服务器监视集群的共享状态, 并尝试进行更改以将当前状态转为期望状态。 目前,Kubernetes 自带的控制器例子包括副本控制器、节点控制器、命名空间控制器和服务账号控制器等。

kube-controller-manager [flags]

选项

--allocate-node-cidrs
基于云驱动来为 Pod 分配和设置子网掩码。
--allow-metric-labels stringToString     默认值:[]

从度量值标签到准许值列表的映射。键名的格式为<MetricName>,<LabelName>。 准许值的格式为<allowed_value>,<allowed_value>...。 例如,metric1,label1='v1,v2,v3', metric1,label2='v1,v2,v3', metric2,label='v1,v2,v3'

--attach-detach-reconcile-sync-period duration     默认值:1m0s
协调器(reconciler)在相邻两次对存储卷进行挂载和解除挂载操作之间的等待时间。 此时长必须长于 1 秒钟。此值设置为大于默认值时,可能导致存储卷无法与 Pod 匹配。
--authentication-kubeconfig string
此标志值为一个 kubeconfig 文件的路径名。该文件中包含与某 Kubernetes “核心” 服务器相关的信息,并支持足够的权限以创建 tokenreviews.authentication.k8s.io。 此选项是可选的。如果设置为空值,则所有令牌请求都会被认作匿名请求, Kubernetes 也不再在集群中查找客户端的 CA 证书信息。
--authentication-skip-lookup
此值为 false 时,通过 authentication-kubeconfig 参数所指定的文件会被用来检索集群中缺失的身份认证配置信息。
--authentication-token-webhook-cache-ttl duration     默认值:10s
对 Webhook 令牌认证设施返回结果的缓存时长。
--authentication-tolerate-lookup-failure
此值为 true 时,即使无法从集群中检索到缺失的身份认证配置信息也无大碍。 需要注意的是,这样设置可能导致所有请求都被视作匿名请求。
--authorization-always-allow-paths strings     默认值:"/healthz,/readyz,/livez"
鉴权过程中会忽略的一个 HTTP 路径列表。 换言之,控制器管理器会对列表中路径的访问进行授权,并且无须征得 Kubernetes “核心” 服务器同意。
--authorization-kubeconfig string
包含 Kubernetes “核心” 服务器信息的 kubeconfig 文件路径, 所包含信息具有创建 subjectaccessreviews.authorization.k8s.io 的足够权限。 此参数是可选的。如果配置为空字符串,未被鉴权模块所忽略的请求都会被禁止。
--authorization-webhook-cache-authorized-ttl duration     默认值:10s
对 Webhook 形式鉴权组件所返回的“已授权(Authorized)”响应的缓存时长。
--authorization-webhook-cache-unauthorized-ttl duration     默认值:10s
对 Webhook 形式鉴权组件所返回的“未授权(Unauthorized)”响应的缓存时长。
--azure-container-registry-config string
指向包含 Azure 容器仓库配置信息的文件的路径名。
--bind-address string     默认值:0.0.0.0
针对 --secure-port 端口上请求执行监听操作的 IP 地址。 所对应的网络接口必须从集群中其它位置可访问(含命令行及 Web 客户端)。 如果此值为空或者设定为非特定地址(0.0.0.0::), 意味着所有网络接口和 IP 地址簇都在监听范围。
--cert-dir string
TLS 证书所在的目录。如果提供了 --tls-cert-file--tls-private-key-file,此标志会被忽略。
--cidr-allocator-type string     默认值:"RangeAllocator"
要使用的 CIDR 分配器类型。
--client-ca-file string
如果设置了此标志,对于所有能够提供客户端证书的请求,若该证书由 --client-ca-file 中所给机构之一签署, 则该请求会被成功认证为客户端证书中 CommonName 所标识的实体。
--cloud-config string
云驱动程序配置文件的路径。空字符串表示没有配置文件。
--cloud-provider string
云服务的提供者。空字符串表示没有对应的提供者(驱动)。
--cluster-cidr string
集群中 Pod 的 CIDR 范围。要求 --allocate-node-cidrs 标志为 true。
--cluster-name string     默认值:"kubernetes"
集群实例的前缀。
--cluster-signing-cert-file string
包含 PEM 编码格式的 X509 CA 证书的文件名。该证书用来发放集群范围的证书。 如果设置了此标志,则不能指定更具体的 --cluster-signing-* 标志。
--cluster-signing-duration duration     默认值:8760h0m0s
所签名证书的有效期限。每个 CSR 可以通过设置 spec.expirationSeconds 来请求更短的证书。
--cluster-signing-key-file string
包含 PEM 编码的 RSA 或 ECDSA 私钥的文件名。该私钥用来对集群范围证书签名。 若指定了此选项,则不可再设置 --cluster-signing-* 参数。
--cluster-signing-kube-apiserver-client-cert-file string
包含 PEM 编码的 X509 CA 证书的文件名, 该证书用于为 kubernetes.io/kube-apiserver-client 签署者颁发证书。 如果指定,则不得设置 --cluster-signing-{cert,key}-file
--cluster-signing-kube-apiserver-client-key-file string
包含 PEM 编码的 RSA 或 ECDSA 私钥的文件名, 该私钥用于为 kubernetes.io/kube-apiserver-client 签署者签名证书。 如果指定,则不得设置 --cluster-signing-{cert,key}-file
--cluster-signing-kubelet-client-cert-file string
包含 PEM 编码的 X509 CA 证书的文件名, 该证书用于为 kubernetes.io/kube-apiserver-client-kubelet 签署者颁发证书。 如果指定,则不得设置 --cluster-signing-{cert,key}-file
--cluster-signing-kubelet-client-key-file string
包含 PEM 编码的 RSA 或 ECDSA 私钥的文件名, 该私钥用于为 kubernetes.io/kube-apiserver-client-kubelet 签署者签名证书。 如果指定,则不得设置 --cluster-signing-{cert,key}-file
--cluster-signing-kubelet-serving-cert-file string
包含 PEM 编码的 X509 CA 证书的文件名, 该证书用于为 kubernetes.io/kubelet-serving 签署者颁发证书。 如果指定,则不得设置 --cluster-signing-{cert,key}-file。
--cluster-signing-kubelet-serving-key-file string
包含 PEM 编码的 RSA或ECDSA 私钥的文件名, 该私钥用于对 kubernetes.io/kubelet-serving 签署者的证书进行签名。 如果指定,则不得设置 --cluster-signing-{cert,key}-file
--cluster-signing-legacy-unknown-cert-file string
包含 PEM 编码的 X509 CA 证书的文件名, 用于为 kubernetes.io/legacy-unknown 签署者颁发证书。 如果指定,则不得设置 --cluster-signing-{cert,key}-file
--cluster-signing-legacy-unknown-key-file string
包含 PEM 编码的 RSA 或 ECDSA 私钥的文件名, 用于为 kubernetes.io/legacy-unknown 签署者签名证书。 如果指定,则不得设置 --cluster-signing-{cert,key}-file
--concurrent-cron-job-syncs int32     默认值:5

可以并发同步的 CronJob 对象个数。数值越大意味着对 CronJob 的响应越及时, 同时也意味着更大的 CPU(和网络带宽)压力。

--concurrent-deployment-syncs int32     默认值:5
可以并发同步的 Deployment 对象个数。数值越大意味着对 Deployment 的响应越及时, 同时也意味着更大的 CPU(和网络带宽)压力。
--concurrent-endpoint-syncs int32     默认值:5
可以并发执行的 Endpoints 同步操作个数。数值越大意味着更快的 Endpoints 更新操作, 同时也意味着更大的 CPU (和网络)压力。
--concurrent-ephemeralvolume-syncs int32     默认值:5
可以并发执行的 EphemeralVolume 同步操作个数。数值越大意味着更快的 EphemeralVolume 更新操作, 同时也意味着更大的 CPU (和网络)压力。
--concurrent-gc-syncs int32     默认值:20
可以并发同步的垃圾收集工作线程个数。
--concurrent-horizontal-pod-autoscaler-syncs int32     默认值:5

允许并发执行的、对水平 Pod 自动扩缩器对象进行同步的数量。 更大的数字 = 响应更快的水平 Pod 自动缩放器对象处理,但需要更高的 CPU(和网络)负载。

--concurrent-job-syncs int32     默认值:5

可以并发同步的 Job 对象个数。较大的数值意味着更快的 Job 终结操作, 不过也意味着更多的 CPU (和网络)占用。

--concurrent-namespace-syncs int32     默认值:10
可以并发同步的 Namespace 对象个数。较大的数值意味着更快的名字空间终结操作, 不过也意味着更多的 CPU (和网络)占用。
--concurrent-rc-syncs int32     默认值:5

可以并发同步的副本控制器对象个数。较大的数值意味着更快的副本管理操作, 不过也意味着更多的 CPU (和网络)占用。

--concurrent-replicaset-syncs int32     默认值:5
可以并发同步的 ReplicaSet 个数。数值越大意味着副本管理的响应速度越快, 同时也意味着更多的 CPU (和网络)占用。
--concurrent-resource-quota-syncs int32     默认值:5
可以并发同步的 ResourceQuota 对象个数。数值越大,配额管理的响应速度越快, 不过对 CPU (和网络)的占用也越高。
--concurrent-service-endpoint-syncs int32     默认值:5
可以并发执行的服务端点同步操作个数。数值越大,端点片段(Endpoint Slice) 的更新速度越快,不过对 CPU (和网络)的占用也越高。默认值为 5。
--concurrent-service-syncs int32     默认值:1
可以并发同步的 Service 对象个数。数值越大,服务管理的响应速度越快, 不过对 CPU (和网络)的占用也越高。
--concurrent-serviceaccount-token-syncs int32     默认值:5
可以并发同步的服务账号令牌对象个数。数值越大,令牌生成的速度越快, 不过对 CPU (和网络)的占用也越高。
--concurrent-statefulset-syncs int32     默认值:5
可以并发同步的 StatefulSet 对象个数。数值越大,StatefulSet 管理的响应速度越快, 不过对 CPU (和网络)的占用也越高。
--concurrent-ttl-after-finished-syncs int32     默认值:5
可以并发同步的 ttl-after-finished-controller 线程个数。
--concurrent-validating-admission-policy-status-syncs int32     默认值:5

可以并发同步的 ValidatingAdmissionPolicyStatusController 线程个数。

--configure-cloud-routes     默认值:true
决定是否由 --allocate-node-cidrs 所分配的 CIDR 要通过云驱动程序来配置。
--contention-profiling
在启用了性能分析(profiling)时,也启用锁竞争情况分析。
--controller-start-interval duration
在两次启动控制器管理器之间的时间间隔。
--controllers strings     默认值:*
要启用的控制器列表。* 表示启用所有默认启用的控制器; foo 启用名为 foo 的控制器; -foo 表示禁用名为 foo 的控制器。
控制器的全集:bootstrap-signer-controller、certificatesigningrequest-approving-controller、 certificatesigningrequest-cleaner-controller、certificatesigningrequest-signing-controller、 cloud-node-lifecycle-controller、clusterrole-aggregation-controller、cronjob-controller、daemonset-controller、 deployment-controller、disruption-controller、endpoints-controller、endpointslice-controller、 endpointslice-mirroring-controller、ephemeral-volume-controller、garbage-collector-controller、 horizontal-pod-autoscaler-controller、job-controller、namespace-controller、node-ipam-controller、 node-lifecycle-controller、node-route-controller、persistentvolume-attach-detach-controller、 persistentvolume-binder-controller、persistentvolume-expander-controller、persistentvolume-protection-controller、 persistentvolumeclaim-protection-controller、pod-garbage-collector-controller、replicaset-controller、 replicationcontroller-controller、resourcequota-controller、root-ca-certificate-publisher-controller、 service-lb-controller、serviceaccount-controller、serviceaccount-token-controller、statefulset-controller、 token-cleaner-controller、ttl-after-finished-controller、ttl-controller
默认禁用的控制器有:bootstrap-signer-controller、token-cleaner-controller。
--disable-attach-detach-reconcile-sync
禁用卷挂接/解挂调节器的同步。禁用此同步可能导致卷存储与 Pod 之间出现错位。 请小心使用。
--disabled-metrics strings

此标志提供对行为异常的度量值的防控措施。你必须提供度量值的完全限定名称才能将其禁用。 声明:禁用度量值的操作比显示隐藏度量值的操作优先级高。

--enable-dynamic-provisioning     默认值:true
在环境允许的情况下启用动态卷制备。
--enable-garbage-collector     默认值:true
启用通用垃圾收集器。必须与 kube-apiserver 中对应的标志一致。
--enable-hostpath-provisioner
在没有云驱动程序的情况下,启用 HostPath 持久卷的制备。 此参数便于对卷供应功能进行开发和测试。HostPath 卷的制备并非受支持的功能特性, 在多节点的集群中也无法工作,因此除了开发和测试环境中不应使用 HostPath 卷的制备。
--enable-leader-migration

此标志决定是否启用控制器领导者迁移。

--endpoint-updates-batch-period duration
端点(Endpoint)批量更新周期时长。对 Pod 变更的处理会被延迟, 以便将其与即将到来的更新操作合并,从而减少端点更新操作次数。 较大的数值意味着端点更新的迟滞时间会增长,也意味着所生成的端点版本个数会变少。
--endpointslice-updates-batch-period duration
端点片段(Endpoint Slice)批量更新周期时长。对 Pod 变更的处理会被延迟, 以便将其与即将到来的更新操作合并,从而减少端点更新操作次数。 较大的数值意味着端点更新的迟滞时间会增长,也意味着所生成的端点版本个数会变少。
--external-cloud-volume-plugin string
当云驱动程序设置为 external 时要使用的插件名称。此字符串可以为空。 只能在云驱动程序为 external 时设置。 目前用来保证 node-ipam-controller、persistentvolume-binder-controller、persistentvolume-expander-controller 和 attach-detach-controller 能够在三种云驱动上正常工作。
--feature-gates <逗号分隔的 'key=True|False' 对列表>

一组 key=value 对,用来描述测试性/试验性功能的特性门控。可选项有:
APIListChunking=true|false (BETA - 默认值为 true)
APIPriorityAndFairness=true|false (BETA - 默认值为 true)
APIResponseCompression=true|false (BETA - 默认值为 true)
APIServerIdentity=true|false (BETA - 默认值为 true)
APIServerTracing=true|false (BETA - 默认值为 true)
AdmissionWebhookMatchConditions=true|false (BETA - 默认值为 true)
AggregatedDiscoveryEndpoint=true|false (BETA - 默认值为 true)
AllAlpha=true|false (ALPHA - 默认值为 false)
AllBeta=true|false (BETA - 默认值为 false)
AnyVolumeDataSource=true|false (BETA - 默认值为 true)
AppArmor=true|false (BETA - 默认值为 true)
CPUManagerPolicyAlphaOptions=true|false (ALPHA - 默认值为 false)
CPUManagerPolicyBetaOptions=true|false (BETA - 默认值为 true)
CPUManagerPolicyOptions=true|false (BETA - 默认值为 true)
CRDValidationRatcheting=true|false (ALPHA - 默认值为 false)
CSIMigrationPortworx=true|false (BETA - 默认值为 false)
CSINodeExpandSecret=true|false (BETA - 默认值为 true)
CSIVolumeHealth=true|false (ALPHA - 默认值为 false)
CloudControllerManagerWebhook=true|false (ALPHA - 默认值为 false)
CloudDualStackNodeIPs=true|false (ALPHA - 默认值为 false)
ClusterTrustBundle=true|false (ALPHA - 默认值为 false)
ComponentSLIs=true|false (BETA - 默认值为 true)
ConsistentListFromCache=true|false (ALPHA - 默认值为 false)
ContainerCheckpoint=true|false (ALPHA - 默认值为 false)
ContextualLogging=true|false (ALPHA - 默认值为 false)
CronJobsScheduledAnnotation=true|false (BETA - 默认值为 true)
CrossNamespaceVolumeDataSource=true|false (ALPHA - 默认值为 false)
CustomCPUCFSQuotaPeriod=true|false (ALPHA - 默认值为 false)
CustomResourceValidationExpressions=true|false (BETA - 默认值为 true)
DevicePluginCDIDevices=true|false (ALPHA - 默认值为 false)
DisableCloudProviders=true|false (ALPHA - 默认值为 false)
DisableKubeletCloudCredentialProviders=true|false (ALPHA - 默认值为 false)
DynamicResourceAllocation=true|false (ALPHA - 默认值为 false)
ElasticIndexedJob=true|false (BETA - 默认值为 true)
EventedPLEG=true|false (BETA - 默认值为 false)
GracefulNodeShutdown=true|false (BETA - 默认值为 true)
GracefulNodeShutdownBasedOnPodPriority=true|false (BETA - 默认值为 true)
HPAContainerMetrics=true|false (BETA - 默认值为 true)
HPAScaleToZero=true|false (ALPHA - 默认值为 false)
HonorPVReclaimPolicy=true|false (ALPHA - 默认值为 false)
InPlacePodVerticalScaling=true|false (ALPHA - 默认值为 false)
InTreePluginAWSUnregister=true|false (ALPHA - 默认值为 false)
InTreePluginAzureDiskUnregister=true|false (ALPHA - 默认值为 false)
InTreePluginAzureFileUnregister=true|false (ALPHA - 默认值为 false)
InTreePluginGCEUnregister=true|false (ALPHA - 默认值为 false)
InTreePluginOpenStackUnregister=true|false (ALPHA - 默认值为 false)
InTreePluginPortworxUnregister=true|false (ALPHA - 默认值为 false)
InTreePluginvSphereUnregister=true|false (ALPHA - 默认值为 false)
JobBackoffLimitPerIndex=true|false (ALPHA - 默认值为 false)
JobPodFailurePolicy=true|false (BETA - 默认值为 true)
JobPodReplacementPolicy=true|false (ALPHA - 默认值为 false)
JobReadyPods=true|false (BETA - 默认值为 true)
KMSv2=true|false (BETA - 默认值为 true)
KMSv2KDF=true|false (BETA - 默认值为 false)
KubeProxyDrainingTerminatingNodes=true|false (ALPHA - 默认值为 false)
KubeletCgroupDriverFromCRI=true|false (ALPHA - 默认值为 false)
KubeletInUserNamespace=true|false (ALPHA - 默认值为 false)
KubeletPodResourcesDynamicResources=true|false (ALPHA - 默认值为 false)
KubeletPodResourcesGet=true|false (ALPHA - 默认值为 false)
KubeletTracing=true|false (BETA - 默认值为 true)
LegacyServiceAccountTokenCleanUp=true|false (ALPHA - 默认值为 false)
LocalStorageCapacityIsolationFSQuotaMonitoring=true|false (ALPHA - 默认值为 false)
LogarithmicScaleDown=true|false (BETA - 默认值为 true)
LoggingAlphaOptions=true|false (ALPHA - 默认值为 false)
LoggingBetaOptions=true|false (BETA - 默认值为 true)
MatchLabelKeysInPodTopologySpread=true|false (BETA - 默认值为 true)
MaxUnavailableStatefulSet=true|false (ALPHA - 默认值为 false)
MemoryManager=true|false (BETA - 默认值为 true)
MemoryQoS=true|false (ALPHA - 默认值为 false)
MinDomainsInPodTopologySpread=true|false (BETA - 默认值为 true)
MultiCIDRRangeAllocator=true|false (ALPHA - 默认值为 false)
MultiCIDRServiceAllocator=true|false (ALPHA - 默认值为 false)
NewVolumeManagerReconstruction=true|false (BETA - 默认值为 true)
NodeInclusionPolicyInPodTopologySpread=true|false (BETA - 默认值为 true)
NodeLogQuery=true|false (ALPHA - 默认值为 false)
NodeSwap=true|false (BETA - 默认值为 false)
OpenAPIEnums=true|false (BETA - 默认值为 true)
PDBUnhealthyPodEvictionPolicy=true|false (BETA - 默认值为 true)
PersistentVolumeLastPhaseTransitionTime=true|false (ALPHA - 默认值为 false)
PodAndContainerStatsFromCRI=true|false (ALPHA - 默认值为 false)
PodDeletionCost=true|false (BETA - 默认值为 true)
PodDisruptionConditions=true|false (BETA - 默认值为 true)
PodHostIPs=true|false (ALPHA - 默认值为 false)
PodIndexLabel=true|false (BETA - 默认值为 true)
PodReadyToStartContainersCondition=true|false (ALPHA - 默认值为 false)
PodSchedulingReadiness=true|false (BETA - 默认值为 true)
ProcMountType=true|false (ALPHA - 默认值为 false)
QOSReserved=true|false (ALPHA - 默认值为 false)
ReadWriteOncePod=true|false (BETA - 默认值为 true)
RecoverVolumeExpansionFailure=true|false (ALPHA - 默认值为 false)
RemainingItemCount=true|false (BETA - 默认值为 true)
RotateKubeletServerCertificate=true|false (BETA - 默认值为 true)
SELinuxMountReadWriteOncePod=true|false (BETA - 默认值为 true)
SchedulerQueueingHints=true|false (BETA - 默认值为 true)
SecurityContextDeny=true|false (ALPHA - 默认值为 false)
ServiceNodePortStaticSubrange=true|false (BETA - 默认值为 true)
SidecarContainers=true|false (ALPHA - 默认值为 false)
SizeMemoryBackedVolumes=true|false (BETA - 默认值为 true)
SkipReadOnlyValidationGCE=true|false (ALPHA - 默认值为 false)
StableLoadBalancerNodeSet=true|false (BETA - 默认值为 true)
StatefulSetAutoDeletePVC=true|false (BETA - 默认值为 true)
StatefulSetStartOrdinal=true|false (BETA - 默认值为 true)
StorageVersionAPI=true|false (ALPHA - 默认值为 false)
StorageVersionHash=true|false (BETA - 默认值为 true)
TopologyAwareHints=true|false (BETA - 默认值为 true)
TopologyManagerPolicyAlphaOptions=true|false (ALPHA - 默认值为 false)
TopologyManagerPolicyBetaOptions=true|false (BETA - 默认值为 true)
TopologyManagerPolicyOptions=true|false (BETA - 默认值为 true)
UnknownVersionInteroperabilityProxy=true|false (ALPHA - 默认值为 false)
UserNamespacesSupport=true|false (ALPHA - 默认值为 false)
ValidatingAdmissionPolicy=true|false (BETA - 默认值为 false)
VolumeCapacityPriority=true|false (ALPHA - 默认值为 false)
WatchList=true|false (ALPHA - 默认值为 false)
WinDSR=true|false (ALPHA - 默认值为 false)
WinOverlay=true|false (BETA - 默认值为 true)
WindowsHostNetwork=true|false (ALPHA - 默认值为 true)

--flex-volume-plugin-dir string     默认值:"/usr/libexec/kubernetes/kubelet-plugins/volume/exec/"
FlexVolume 插件要搜索第三方卷插件的目录路径全名。
-h, --help
kube-controller-manager 的帮助信息。
--horizontal-pod-autoscaler-cpu-initialization-period duration     默认值:5m0s
Pod 启动之后可以忽略 CPU 采样值的时长。
--horizontal-pod-autoscaler-downscale-stabilization duration     默认值:5m0s
自动扩缩程序的回溯时长。 自动扩缩程序不会基于在给定的时长内所建议的规模对负载执行缩容操作。
--horizontal-pod-autoscaler-initial-readiness-delay duration     默认值:30s
Pod 启动之后,在此值所给定的时长内,就绪状态的变化都不会作为初始的就绪状态。
--horizontal-pod-autoscaler-sync-period duration     默认值:15s
水平 Pod 扩缩器对 Pod 数目执行同步操作的周期。
--horizontal-pod-autoscaler-tolerance float     默认值:0.1
此值为目标值与实际值的比值与 1.0 的差值。只有超过此标志所设的阈值时, HPA 才会考虑执行缩放操作。
--http2-max-streams-per-connection int
服务器为客户端所设置的 HTTP/2 连接中流式连接个数上限。 此值为 0 表示采用 Go 语言库所设置的默认值。
--kube-api-burst int32     默认值:30
与 Kubernetes API 服务器通信时突发峰值请求个数上限。
--kube-api-content-type string     默认值:"application/vnd.kubernetes.protobuf"
向 API 服务器发送请求时使用的内容类型(Content-Type)。
--kube-api-qps float     默认值:20
与 API 服务器通信时每秒请求数(QPS)限制。
--kubeconfig string
指向 kubeconfig 文件的路径。该文件中包含主控节点位置以及鉴权凭据信息。
--large-cluster-size-threshold int32     默认值:50
node-lifecycle-controller 在执行 Pod 驱逐操作逻辑时, 基于此标志所设置的节点个数阈值来判断所在集群是否为大规模集群。 当集群规模小于等于此规模时, --secondary-node-eviction-rate 会被隐式重设为 0。
--leader-elect     默认值:true
在执行主循环之前,启动领导选举(Leader Election)客户端,并尝试获得领导者身份。 在运行多副本组件时启用此标志有助于提高可用性。
--leader-elect-lease-duration duration     默认值:15s
对于未获得领导者身份的节点, 在探测到领导者身份需要更迭时需要等待此标志所设置的时长, 才能尝试去获得曾经是领导者但尚未续约的席位。本质上, 这个时长也是现有领导者节点在被其他候选节点替代之前可以停止的最长时长。 只有集群启用了领导者选举机制时,此标志才起作用。
--leader-elect-renew-deadline duration     默认值:10s
当前执行领导者角色的节点在被停止履行领导职责之前可多次尝试续约领导者身份; 此标志给出相邻两次尝试之间的间歇时长。 此值必须小于租期时长(Lease Duration)。 仅在集群启用了领导者选举时有效。
--leader-elect-resource-lock string     默认值:"leases"
在领导者选举期间用于锁定的资源对象的类型。 支持的选项为 leasesendpointsleasesconfigmapsleases
--leader-elect-resource-name string     默认值:"kube-controller-manager"
在领导者选举期间,用来执行锁操作的资源对象名称。
--leader-elect-resource-namespace string     默认值:"kube-system"
在领导者选举期间,用来执行锁操作的资源对象的名字空间。
--leader-elect-retry-period duration     默认值:2s
尝试获得领导者身份时,客户端在相邻两次尝试之间要等待的时长。 此标志仅在启用了领导者选举的集群中起作用。
--leader-migration-config string

控制器领导者迁移所用的配置文件路径。 此值为空意味着使用控制器管理器的默认配置。 配置文件应该是 controllermanager.config.k8s.io 组、 v1alpha1 版本的 LeaderMigrationConfiguration 结构。

--legacy-service-account-token-clean-up-period duration     默认值:8760h0m0s

从最近一次使用某个旧的服务账号令牌计起,到该令牌在可以删除之前的时长。

--log-flush-frequency duration     默认值:5s
将内存中日志数据清除到日志文件中时,相邻两次清除操作之间最大间隔秒数。
--logging-format string     默认值:"text"

设置日志格式。允许的格式:"text"。

--master string
Kubernetes API 服务器的地址。此值会覆盖 kubeconfig 文件中所给的地址。
--max-endpoints-per-slice int32     默认值:100
每个 EndpointSlice 中可以添加的端点个数上限。每个片段中端点个数越多, 得到的片段个数越少,但是片段的规模会变得更大。默认值为 100。
--min-resync-period duration     默认值:12h0m0s
自省程序的重新同步时隔下限。实际时隔长度会在 min-resync-period2 * min-resync-period 之间。
--mirroring-concurrent-service-endpoint-syncs int32     默认值:5
endpointslice-mirroring-controller 将同时执行的服务端点同步操作数。 较大的数量 = 更快的端点切片更新,但 CPU(和网络)负载更多。 默认为 5。
--mirroring-endpointslice-updates-batch-period duration
EndpointSlice 的长度会更新 endpointslice-mirroring-controller 的批处理周期。 EndpointSlice 更改的处理将延迟此持续时间, 以使它们与潜在的即将进行的更新结合在一起,并减少 EndpointSlice 更新的总数。 较大的数量 = 较高的端点编程延迟,但是生成的端点修订版本数量较少
--mirroring-max-endpoints-per-subset int32     默认值:1000
endpointslice-mirroring-controller 可添加到某 EndpointSlice 的端点个数上限。 每个分片的端点越多,端点分片越少,但资源越大。默认为 100。
--namespace-sync-period duration     默认值:5m0s
对名字空间对象进行同步的周期。
--node-cidr-mask-size int32
集群中节点 CIDR 的掩码长度。对 IPv4 而言默认为 24;对 IPv6 而言默认为 64。
--node-cidr-mask-size-ipv4 int32
在双堆栈(同时支持 IPv4 和 IPv6)的集群中,节点 IPV4 CIDR 掩码长度。默认为 24。
--node-cidr-mask-size-ipv6 int32
在双堆栈(同时支持 IPv4 和 IPv6)的集群中,节点 IPv6 CIDR 掩码长度。默认为 64。
--node-eviction-rate float     默认值:0.1
当某区域健康时,在节点故障的情况下每秒删除 Pods 的节点数。 请参阅 --unhealthy-zone-threshold 以了解“健康”的判定标准。 这里的区域(zone)在集群并不跨多个区域时指的是整个集群。
--node-monitor-grace-period duration     默认值:40s
在将一个 Node 标记为不健康之前允许其无响应的时长上限。 必须比 kubelet 的 nodeStatusUpdateFrequency 大 N 倍; 这里 N 指的是 kubelet 发送节点状态的重试次数。
--node-monitor-period duration     默认值:5s
cloud-node-lifecycle-controller 对节点状态进行同步的周期。
--node-startup-grace-period duration     默认值:1m0s
在节点启动期间,节点可以处于无响应状态; 但超出此标志所设置的时长仍然无响应则该节点被标记为不健康。
--permit-address-sharing

如果此标志为 true,则在绑定端口时使用 SO_REUSEADDR。 这就意味着可以同时绑定到 0.0.0.0 和特定的 IP 地址, 并且避免等待内核释放处于 TIME_WAITE 状态的套接字。[默认值=false]。

--permit-port-sharing
如果为 true,则在绑定端口时将使用 SO_REUSEPORT, 这允许多个实例在同一地址和端口上进行绑定。[默认值=false]。
--profiling     默认值:true
通过位于 host:port/debug/pprof/ 的 Web 接口启用性能分析。
--pv-recycler-increment-timeout-nfs int32     默认值:30
NFS 清洗 Pod 在清洗用过的卷时,根据此标志所设置的秒数, 为每清洗 1 GiB 数据增加对应超时时长,作为 activeDeadlineSeconds。
--pv-recycler-minimum-timeout-hostpath int32     默认值:60
对于 HostPath 回收器 Pod,设置其 activeDeadlineSeconds 参数下限。 此参数仅用于开发和测试目的,不适合在多节点集群中使用。
--pv-recycler-minimum-timeout-nfs int32     默认值:300
NFS 回收器 Pod 要使用的 activeDeadlineSeconds 参数下限。
--pv-recycler-pod-template-filepath-hostpath string
对 HostPath 持久卷进行回收利用时,用作模板的 Pod 定义文件所在路径。 此标志仅用于开发和测试目的,不适合多节点集群中使用。
--pv-recycler-pod-template-filepath-nfs string
对 NFS 卷执行回收利用时,用作模板的 Pod 定义文件所在路径。
--pv-recycler-timeout-increment-hostpath int32     默认值:30
HostPath 清洗器 Pod 在清洗对应类型持久卷时,为每 GiB 数据增加此标志所设置的秒数, 作为其 activeDeadlineSeconds 参数。此标志仅用于开发和测试环境,不适合多节点集群环境。
--pvclaimbinder-sync-period duration     默认值:15s
持久卷(PV)和持久卷申领(PVC)对象的同步周期。
--requestheader-allowed-names strings
标志值是客户端证书中的 Common Names 列表。其中所列的名称可以通过 --requestheader-username-headers 所设置的 HTTP 头部来提供用户名。 如果此标志值为空表,则被 --requestheader-client-ca-file 中机构所验证过的所有客户端证书都是允许的。
--requestheader-client-ca-file string
根证书包文件名。在信任通过 --requestheader-username-headers 所指定的任何用户名之前,要使用这里的证书来检查请求中的客户证书。 警告:一般不要依赖对请求所作的鉴权结果。
--requestheader-extra-headers-prefix strings     默认值:"x-remote-extra-"
要插入的请求头部前缀。建议使用 X-Remote-Exra-
--requestheader-group-headers strings     默认值:"x-remote-group"
用来检查用户组名的请求头部名称列表。建议使用 X-Remote-Group
--requestheader-username-headers strings     默认值:"x-remote-user"
用来检查用户名的请求头部名称列表。建议使用 X-Remote-User
--resource-quota-sync-period duration     默认值:5m0s
对系统中配额用量信息进行同步的周期。
--root-ca-file string
如果此标志非空,则在服务账号的令牌 Secret 中会包含此根证书机构。 所指定标志值必须是一个合法的 PEM 编码的 CA 证书包。
--route-reconciliation-period duration     默认值:10s
对云驱动为节点所创建的路由信息进行调解的周期。
--secondary-node-eviction-rate float32     默认值:0.01
当一个区域不健康造成节点失效时,每秒钟从此标志所给的节点上删除 Pod 的节点个数。 参见 --unhealthy-zone-threshold 以了解“健康与否”的判定标准。 在只有一个区域的集群中,区域指的是整个集群。如果集群规模小于 --large-cluster-size-threshold 所设置的节点个数时, 此值被隐式地重设为 0。
--secure-port int     默认值:10257
在此端口上提供 HTTPS 身份认证和鉴权操作。若此标志值为 0,则不提供 HTTPS 服务。
--service-account-private-key-file string
包含 PEM 编码的 RSA 或 ECDSA 私钥数据的文件名,这些私钥用来对服务账号令牌签名。
--service-cluster-ip-range string
集群中 Service 对象的 CIDR 范围。要求 --allocate-node-cidrs 标志为 true。
--show-hidden-metrics-for-version string
你希望展示隐藏度量值的上一个版本。只有上一个次版本号有意义,其他值都是不允许的。 字符串格式为 "<major>.<minor>"。例如:"1.16"。 此格式的目的是确保你能够有机会注意到下一个版本隐藏了一些额外的度量值, 而不是在更新版本中某些度量值被彻底删除时措手不及。
--terminated-pod-gc-threshold int32     默认值:12500
在已终止 Pod 垃圾收集器删除已终止 Pod 之前,可以保留的已终止 Pod 的个数上限。 若此值小于等于 0,则相当于禁止垃圾回收已终止的 Pod。
--tls-cert-file string
包含 HTTPS 所用的默认 X509 证书的文件。如果有 CA 证书,会被串接在服务器证书之后。 若启用了 HTTPS 服务且 --tls-cert-file--tls-private-key-file 标志未设置, 则为节点的公开地址生成自签名的证书和密钥,并保存到 --cert-dir 所给的目录中。
--tls-cipher-suites strings
供服务器使用的加密包的逗号分隔列表。若忽略此标志,则使用 Go 语言默认的加密包。
可选值包括:TLS_AES_128_GCM_SHA256、TLS_AES_256_GCM_SHA384、TLS_CHACHA20_POLY1305_SHA256、TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA、TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256、TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA、TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384、TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305、TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256、TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA、TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256、TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA、TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384、TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305、TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256、TLS_RSA_WITH_AES_128_CBC_SHA、TLS_RSA_WITH_AES_128_GCM_SHA256、TLS_RSA_WITH_AES_256_CBC_SHA、TLS_RSA_WITH_AES_256_GCM_SHA384。
不安全的值: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256、TLS_ECDHE_ECDSA_WITH_RC4_128_SHA、TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA、TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256、TLS_ECDHE_RSA_WITH_RC4_128_SHA、TLS_RSA_WITH_3DES_EDE_CBC_SHA、TLS_RSA_WITH_AES_128_CBC_SHA256、TLS_RSA_WITH_RC4_128_SHA。
--tls-min-version string
可支持的最低 TLS 版本。可选值包括: “VersionTLS10”、“VersionTLS11”、“VersionTLS12”、“VersionTLS13”。
--tls-private-key-file string
包含与 --tls-cert-file 对应的默认 X509 私钥的文件。
--tls-sni-cert-key string
X509 证书和私钥文件路径的耦对。作为可选项,可以添加域名模式的列表, 其中每个域名模式都是可以带通配片段前缀的全限定域名(FQDN)。 域名模式也可以使用 IP 地址字符串, 不过只有 API 服务器在所给 IP 地址上对客户端可见时才可以使用 IP 地址。 在未提供域名模式时,从证书中提取域名。 如果有非通配方式的匹配,则优先于通配方式的匹配;显式的域名模式优先于提取的域名。 当存在多个密钥/证书耦对时,可以多次使用 --tls-sni-cert-key 标志。 例如:example.crt,example.keyfoo.crt,foo.key:\*.foo.com,foo.com
--unhealthy-zone-threshold float32     默认值:0.55
仅当给定区域中处于非就绪状态的节点(最少 3 个)的占比高于此值时, 才将该区域视为不健康。
--use-service-account-credentials
当此标志为 true 时,为每个控制器单独使用服务账号凭据。
-v, --v int
日志级别详细程度取值。
--version version[=true]
--version, --version=raw 打印版本信息之后退出; --version=vX.Y.Z... 设置报告的版本。
--vmodule pattern=N,...
由逗号分隔的列表,每一项都是 pattern=N 格式,用来执行根据文件过滤的日志行为(仅适用于 text 日志格式)。

6.13.6 - kube-proxy

简介

Kubernetes 网络代理在每个节点上运行。网络代理反映了每个节点上 Kubernetes API 中定义的服务,并且可以执行简单的 TCP、UDP 和 SCTP 流转发,或者在一组后端进行 循环 TCP、UDP 和 SCTP 转发。 当前可通过 Docker-links-compatible 环境变量找到服务集群 IP 和端口, 这些环境变量指定了服务代理打开的端口。 有一个可选的插件,可以为这些集群 IP 提供集群 DNS。 用户必须使用 apiserver API 创建服务才能配置代理。

kube-proxy [flags]

选项

--add_dir_header

如果为 true,将文件目录添加到日志消息的头部

--alsologtostderr

设置为 true 表示将日志输出到文件的同时输出到 stderr(当 --logtostderr=true 时不生效)

--bind-address string     默认值:0.0.0.0

重写 kube-proxy 对其节点主要 IP 的理解。请注意,此名称是一个历史遗留字段, 并且 kube-proxy 实际上并没有将任何套接字绑定到此 IP。 如果配置文件由 --config 指定,则忽略此参数。

--bind-address-hard-fail

如果为 true,kube-proxy 会将无法绑定端口的失败操作视为致命错误并退出。

--boot_id_file string     默认值:"/proc/sys/kernel/random/boot_id"

逗号分隔的文件列表,用于检查 boot-id。使用第一个存在的文件。

--cleanup

如果为 true,清理 iptables 和 ipvs 规则并退出。

--cluster-cidr string

集群中 Pod 的 CIDR 范围。对于双协议栈集群,这可以是逗号分隔的双协议栈 CIDR 范围对。 当 --detect-local-mode 设置为 ClusterCIDR 时, kube-proxy 会将源 IP 在此范围内的流量视为本地流量。否则不使用此字段。 如果配置文件由 --config 指定,则忽略此参数。

--config string

配置文件的路径。

--config-sync-period duration     默认值:15m0s

来自 apiserver 的配置的刷新频率。必须大于 0。

--conntrack-max-per-core int32     默认值:32768

每个 CPU 核跟踪的最大 NAT 连接数(0 表示保留当前限制并忽略 conntrack-min 设置)。

--conntrack-min int32     默认值:131072

无论 conntrack-max-per-core 多少,要分配的 conntrack 条目的最小数量(将 conntrack-max-per-core 设置为 0 即可 保持当前的限制)。

--conntrack-tcp-be-liberal

通过将 nf_conntrack_tcp_be_liberal 设置为 1,启用宽松模式以跟踪 TCP 数据包。

--conntrack-tcp-timeout-close-wait duration     默认值:1h0m0s

处于 CLOSE_WAIT 状态的 TCP 连接的 NAT 超时。

--conntrack-tcp-timeout-established duration     默认值:24h0m0s

已建立的 TCP 连接的空闲超时(0 保持当前设置)。

--conntrack-udp-timeout duration

UNREPLIED UDP 连接的空闲超时(0 保持当前设置)。

--conntrack-udp-timeout-stream duration

ASSURED UDP 连接的空闲超时(0 保持当前设置)。

--detect-local-mode LocalMode

用于检测本地流量的模式。 如果配置文件由 --config 指定,则忽略此参数。

--feature-gates <逗号分隔的 'key=True|False' 对>

一组 key=value 对,用来描述测试性/试验性功能的特性门控。可选项有:
APIResponseCompression=true|false (BETA - 默认值=true)
APIServerIdentity=true|false (BETA - 默认值=true)
APIServerTracing=true|false (BETA - 默认值=true)
AdmissionWebhookMatchConditions=true|false (BETA - 默认值=true)
AggregatedDiscoveryEndpoint=true|false (BETA - 默认值=true)
AllAlpha=true|false (ALPHA - 默认值=false)
AllBeta=true|false (BETA - 默认值=false)
AnyVolumeDataSource=true|false (BETA - 默认值=true)
AppArmor=true|false (BETA - 默认值=true)
CPUManagerPolicyAlphaOptions=true|false (ALPHA - 默认值=false)
CPUManagerPolicyBetaOptions=true|false (BETA - 默认值=true)
CPUManagerPolicyOptions=true|false (BETA - 默认值=true)
CRDValidationRatcheting=true|false (ALPHA - 默认值=false)
CSIMigrationPortworx=true|false (BETA - 默认值=false)
CSIVolumeHealth=true|false (ALPHA - 默认值=false)
CloudControllerManagerWebhook=true|false (ALPHA - 默认值=false)
CloudDualStackNodeIPs=true|false (BETA - 默认值=true)
ClusterTrustBundle=true|false (ALPHA - 默认值=false)
ClusterTrustBundleProjection=true|false (ALPHA - 默认值=false)
ComponentSLIs=true|false (BETA - 默认值=true)
ConsistentListFromCache=true|false (ALPHA - 默认值=false)
ContainerCheckpoint=true|false (ALPHA - 默认值=false)
ContextualLogging=true|false (ALPHA - 默认值=false)
CronJobsScheduledAnnotation=true|false (BETA - 默认值=true)
CrossNamespaceVolumeDataSource=true|false (ALPHA - 默认值=false)
CustomCPUCFSQuotaPeriod=true|false (ALPHA - 默认值=false)
DevicePluginCDIDevices=true|false (BETA - 默认值=true)
DisableCloudProviders=true|false (BETA - 默认值=true)
DisableKubeletCloudCredentialProviders=true|false (BETA - 默认值=true)
DisableNodeKubeProxyVersion=true|false (ALPHA - 默认值=false)
DynamicResourceAllocation=true|false (ALPHA - 默认值=false)
ElasticIndexedJob=true|false (BETA - 默认值=true)
EventedPLEG=true|false (BETA - 默认值=false)
GracefulNodeShutdown=true|false (BETA - 默认值=true)
GracefulNodeShutdownBasedOnPodPriority=true|false (BETA - 默认值=true)
HPAContainerMetrics=true|false (BETA - 默认值=true)
HPAScaleToZero=true|false (ALPHA - 默认值=false)
HonorPVReclaimPolicy=true|false (ALPHA - 默认值=false)
ImageMaximumGCAge=true|false (ALPHA - 默认值=false)
InPlacePodVerticalScaling=true|false (ALPHA - 默认值=false)
InTreePluginAWSUnregister=true|false (ALPHA - 默认值=false)
InTreePluginAzureDiskUnregister=true|false (ALPHA - 默认值=false)
InTreePluginAzureFileUnregister=true|false (ALPHA - 默认值=false)
InTreePluginGCEUnregister=true|false (ALPHA - 默认值=false)
InTreePluginOpenStackUnregister=true|false (ALPHA - 默认值=false)
InTreePluginPortworxUnregister=true|false (ALPHA - 默认值=false)
InTreePluginvSphereUnregister=true|false (ALPHA - 默认值=false)
JobBackoffLimitPerIndex=true|false (BETA - 默认值=true)
JobPodFailurePolicy=true|false (BETA - 默认值=true)
JobPodReplacementPolicy=true|false (BETA - 默认值=true)
KubeProxyDrainingTerminatingNodes=true|false (ALPHA - 默认值=false)
KubeletCgroupDriverFromCRI=true|false (ALPHA - 默认值=false)
KubeletInUserNamespace=true|false (ALPHA - 默认值=false)
KubeletPodResourcesDynamicResources=true|false (ALPHA - 默认值=false)
KubeletPodResourcesGet=true|false (ALPHA - 默认值=false)
KubeletSeparateDiskGC=true|false (ALPHA - 默认值=false)
KubeletTracing=true|false (BETA - 默认值=true)
LegacyServiceAccountTokenCleanUp=true|false (BETA - 默认值=true)
LoadBalancerIPMode=true|false (ALPHA - 默认值=false)
LocalStorageCapacityIsolationFSQuotaMonitoring=true|false (ALPHA - 默认值=false)
LogarithmicScaleDown=true|false (BETA - 默认值=true)
LoggingAlphaOptions=true|false (ALPHA - 默认值=false)
LoggingBetaOptions=true|false (BETA - 默认值=true)
MatchLabelKeysInPodAffinity=true|false (ALPHA - 默认值=false)
MatchLabelKeysInPodTopologySpread=true|false (BETA - 默认值=true)
MaxUnavailableStatefulSet=true|false (ALPHA - 默认值=false)
MemoryManager=true|false (BETA - 默认值=true)
MemoryQoS=true|false (ALPHA - 默认值=false)
MinDomainsInPodTopologySpread=true|false (BETA - 默认值=true)
MultiCIDRServiceAllocator=true|false (ALPHA - 默认值=false)
NFTablesProxyMode=true|false (ALPHA - 默认值=false)
NewVolumeManagerReconstruction=true|false (BETA - 默认值=true)
NodeInclusionPolicyInPodTopologySpread=true|false (BETA - 默认值=true)
NodeLogQuery=true|false (ALPHA - 默认值=false)
NodeSwap=true|false (BETA - 默认值=false)
OpenAPIEnums=true|false (BETA - 默认值=true)
PDBUnhealthyPodEvictionPolicy=true|false (BETA - 默认值=true)
PersistentVolumeLastPhaseTransitionTime=true|false (BETA - 默认值=true)
PodAndContainerStatsFromCRI=true|false (ALPHA - 默认值=false)
PodDeletionCost=true|false (BETA - 默认值=true)
PodDisruptionConditions=true|false (BETA - 默认值=true)
PodHostIPs=true|false (BETA - 默认值=true)
PodIndexLabel=true|false (BETA - 默认值=true)
PodLifecycleSleepAction=true|false (ALPHA - 默认值=false)
PodReadyToStartContainersCondition=true|false (BETA - 默认值=true)
PodSchedulingReadiness=true|false (BETA - 默认值=true)
ProcMountType=true|false (ALPHA - 默认值=false)
QOSReserved=true|false (ALPHA - 默认值=false)
RecoverVolumeExpansionFailure=true|false (ALPHA - 默认值=false)
RotateKubeletServerCertificate=true|false (BETA - 默认值=true)
RuntimeClassInImageCriApi=true|false (ALPHA - 默认值=false)
SELinuxMountReadWriteOncePod=true|false (BETA - 默认值=true)
SchedulerQueueingHints=true|false (BETA - 默认值=false)
SecurityContextDeny=true|false (ALPHA - 默认值=false)
SeparateTaintEvictionController=true|false (BETA - 默认值=true)
ServiceAccountTokenJTI=true|false (ALPHA - 默认值=false)
ServiceAccountTokenNodeBinding=true|false (ALPHA - 默认值=false)
ServiceAccountTokenNodeBindingValidation=true|false (ALPHA - 默认值=false)
ServiceAccountTokenPodNodeInfo=true|false (ALPHA - 默认值=false)
SidecarContainers=true|false (BETA - 默认值=true)
SizeMemoryBackedVolumes=true|false (BETA - 默认值=true)
StableLoadBalancerNodeSet=true|false (BETA - 默认值=true)
StatefulSetAutoDeletePVC=true|false (BETA - 默认值=true)
StatefulSetStartOrdinal=true|false (BETA - 默认值=true)
StorageVersionAPI=true|false (ALPHA - 默认值=false)
StorageVersionHash=true|false (BETA - 默认值=true)
StructuredAuthenticationConfiguration=true|false (ALPHA - 默认值=false)
StructuredAuthorizationConfiguration=true|false (ALPHA - 默认值=false)
TopologyAwareHints=true|false (BETA - 默认值=true)
TopologyManagerPolicyAlphaOptions=true|false (ALPHA - 默认值=false)
TopologyManagerPolicyBetaOptions=true|false (BETA - 默认值=true)
TopologyManagerPolicyOptions=true|false (BETA - 默认值=true)
TranslateStreamCloseWebsocketRequests=true|false (ALPHA - 默认值=false)
UnauthenticatedHTTP2DOSMitigation=true|false (BETA - 默认值=true)
UnknownVersionInteroperabilityProxy=true|false (ALPHA - 默认值=false)
UserNamespacesPodSecurityStandards=true|false (ALPHA - 默认值=false)
UserNamespacesSupport=true|false (ALPHA - 默认值=false)
ValidatingAdmissionPolicy=true|false (BETA - 默认值=false)
VolumeAttributesClass=true|false (ALPHA - 默认值=false)
VolumeCapacityPriority=true|false (ALPHA - 默认值=false)
WatchList=true|false (ALPHA - 默认值=false)
WinDSR=true|false (ALPHA - 默认值=false)
WinOverlay=true|false (BETA - 默认值=true)
WindowsHostNetwork=true|false (ALPHA - 默认值=true)
ZeroLimitedNominalConcurrencyShares=true|false (BETA - 默认值=false)
如果配置文件由 --config 指定,则忽略此参数。

--healthz-bind-address 0.0.0.0     默认值:0.0.0.0:10256

服务健康状态检查的 IP 地址和端口。 如果 --bind-address 未设置或设置为 IPv4,则默认为 “0.0.0.0:10256”。 如果 --bind-address 设置为 IPv6,则默认为 “[::]:10256”。 设置为空则禁用。如果配置文件由 --config 指定,则忽略此参数。

-h, --help

kube-proxy 操作的帮助命令。

--hostname-override string

如果非空,将使用此字符串而不是实际的主机名作为标识。 如果不设置,节点名称假定为与节点的主机名相同。

--init-only

如果设置为 true,则执行需要完整 root 权限才能执行的所有初始化步骤,然后退出。 完成此操作后,你可以仅使用 CAP_NET_ADMIN 权能再次运行 kube-proxy。

--iptables-localhost-nodeports     默认值:true

如果设为 false,Kube-proxy 将禁用允许通过本地主机访问 NodePort 服务的传统行为, 这仅适用于 iptables 模式和 ipv4。本地主机的 NodePort 在其他代理模式或 IPv6 下是不允许的。

--iptables-masquerade-bit int32     默认值:14

在使用 iptables 或 ipvs 代理模式时,用来设置 fwmark 空间的 bit,标记需要 SNAT 的数据包。必须在 [0,31] 范围内。

--iptables-min-sync-period duration     默认值:1s

iptables 规则重新同步之间的最小间隔(例如 '5s'、'1m'、'2h22m')。 值为 0 表示每次 Service 或 EndpointSlice 更改都会立即进行 iptables 重新同步。

--iptables-sync-period duration     默认值:30s

表示各种重新同步和清理操作执行频率的时间间隔(例如 '5s'、'1m'、'2h22m')。必须大于 0。

--ipvs-exclude-cidrs strings

逗号分隔的 CIDR 列表,ipvs 代理在清理 IPVS 规则时不会此列表中的地址范围。

--ipvs-min-sync-period duration

ipvs 规则重新同步之间的最小间隔(例如 '5s'、'1m'、'2h22m')。 值为 0 表示每次 Service 或 EndpointSlice 更改都会立即进行 ipvs 重新同步。

--ipvs-scheduler string

代理模式为 ipvs 时所选的 ipvs 调度器类型。

--ipvs-strict-arp

通过将 arp_ignore 设置为 1 并将 arp_announce 设置为 2 启用严格的 ARP。

--ipvs-sync-period duration     默认值:30s

表示各种重新同步和清理操作执行频率的时间间隔(例如 '5s'、'1m'、'2h22m')。必须大于 0。

--ipvs-tcp-timeout duration

空闲 IPVS TCP 连接的超时时间,0 保持连接(例如 '5s'、'1m'、'2h22m')。

--ipvs-tcpfin-timeout duration

收到 FIN 数据包后,IPVS TCP 连接的超时,0 保持当前设置不变。(例如 '5s'、'1m'、'2h22m')。

--ipvs-udp-timeout duration

IPVS UDP 数据包的超时,0 保持当前设置不变。(例如 '5s'、'1m'、'2h22m')。

--kube-api-burst int32     默认值:10

与 kubernetes apiserver 通信的突发数量。

--kube-api-content-type string     默认值:"application/vnd.kubernetes.protobuf"

发送到 apiserver 的请求的内容类型。

--kube-api-qps float32     默认值:5

与 kubernetes apiserver 交互时使用的 QPS。

--kubeconfig string

包含鉴权信息的 kubeconfig 文件的路径(主控节点位置由 master 标志设置)。

--log-flush-frequency duration     默认值:5s

日志清洗之间的最大秒数

--log_backtrace_at <“file:N” 格式的字符串>     默认值:0

当日志命中 file:N,触发一次堆栈追踪

--log_dir string

如果非空,则在此目录中写入日志文件(当 --logtostderr=true 时不生效)

--log_file string

如果非空,使用此日志文件(当 --logtostderr=true 时不生效)

--log_file_max_size uint     默认值:1800

定义日志文件可以增长到的最大大小(当 --logtostderr=true 时不生效)。 单位是兆字节。如果值为 0,则最大文件大小不受限制。

--logging-format string     默认值:"text"

设置日志格式。允许的格式为:"text"。

--logtostderr     默认值:true

日志输出到 stderr 而不是文件。

--machine_id_file string     默认值:"/etc/machine-id,/var/lib/dbus/machine-id"

用来检查 Machine-ID 的文件列表,用逗号分隔。 使用找到的第一个文件。

--masquerade-all

如果使用 iptables 或 ipvs 代理模式,则对通过 Service 集群 IP 发送的所有流量进行 SNAT。 这对某些 CNI 插件可能是必需的。

--master string

Kubernetes API 服务器的地址(覆盖 kubeconfig 中的相关值)。

--metrics-bind-address ipport     默认值:127.0.0.1:10249

metrics 服务器要使用的 IP 地址和端口。 如果 --bind-address 未设置或设置为 IPv4,则默认为 "127.0.0.1:10249"。 如果 --bind-address 设置为 IPv6,则默认为 "[::1]:10249"。 设置为 "0.0.0.0:10249" / "[::]:10249" 可以在所有接口上进行绑定。 设置为空则禁用。如果配置文件由 --config 指定,则忽略此参数。

--nodeport-addresses strings

一个包含有效节点 IP 的 CIDR 范围列表。 如果设置了,到 NodePort 服务的连接只会在节点 IP 在指定范围内时才会被接受。 如果不设置,则 NodePort 连接将在所有本地 IP 上被接受。 如果配置文件由 --config 指定,则忽略此参数。

--one_output

如果为 true,则仅将日志写入其本身的严重性级别 (而不是写入每个较低的严重性级别;当 --logtostderr=true 时不生效)。

--oom-score-adj int32     默认值:-999

kube-proxy 进程中的 oom-score-adj 值,必须在 [-1000,1000] 范围内。 如果配置文件由 --config 指定,则忽略此参数。

--pod-bridge-interface string
一个桥接接口名称。当 --detect-local-mode 设置为 BridgeInterface 时, kube-proxy 会将源自此桥接的流量视为本地流量。
--pod-interface-name-prefix string
一个接口名称前缀。当 --detect-local-mode 设置为 InterfaceNamePrefix 时, kube-proxy 会将源自名称以该前缀开头的所有接口的流量视为本地流量。
--profiling

如果为 true,则通过 Web 接口 /debug/pprof 启用性能分析。 如果配置文件由 --config 指定,则忽略此参数。

--proxy-mode ProxyMode

使用哪种代理模式:在 Linux 上可以是 'iptables'(默认)或 'ipvs'。 在 Windows 上唯一支持的值是 'kernelspace'。 如果配置文件由 --config 指定,则忽略此参数。

--show-hidden-metrics-for-version string

要显示隐藏指标的先前版本。 仅先前的次要版本有意义,不允许其他值。 格式为 <major>.<minor>,例如 '1.16'。 这种格式的目的是确保你有机会注意到下一个发行版是否隐藏了其他指标, 而不是在之后将其永久删除时感到惊讶。 如果配置文件由 --config 指定,则忽略此参数。

--skip_headers

如果为 true,则避免在日志消息中使用头部前缀。

--skip_log_headers

如果为 true,则在打开日志文件时避免使用头部(当 --logtostderr=true 时不生效)

--stderrthreshold int     默认值:2

当写入到文件或 stderr 时设置严重程度达到或超过此阈值的日志输出到 stderr (当 --logtostderr=true--alsologtostderr=true 时不生效)。

-v, --v int

设置日志级别详细程度的数值。

--version version[=true]

--version, --version=raw 打印版本信息并退出; --version=vX.Y.Z... 设置报告的版本。

--vmodule pattern=N,...

以逗号分割的 pattern=N 设置的列表,用于文件过滤日志(仅适用于文本日志格式)

--write-config-to string

如果设置,将默认配置信息写入此文件并退出。

6.13.7 - kube-scheduler

简介

Kubernetes 调度器是一个控制面进程,负责将 Pods 指派到节点上。 调度器基于约束和可用资源为调度队列中每个 Pod 确定其可合法放置的节点。 调度器之后对所有合法的节点进行排序,将 Pod 绑定到一个合适的节点。 在同一个集群中可以使用多个不同的调度器;kube-scheduler 是其参考实现。 参阅调度以获得关于调度和 kube-scheduler 组件的更多信息。

kube-scheduler [flags]

选项

--allow-metric-labels stringToString      默认值:[]
这个键值映射表设置度量标签所允许设置的值。 其中键的格式是 <MetricName>,<LabelName>。 值的格式是 <allowed_value>,<allowed_value>。 例如:metric1,label1='v1,v2,v3', metric1,label2='v1,v2,v3' metric2,label1='v1,v2,v3'。
--authentication-kubeconfig string
指向具有足够权限以创建 tokenaccessreviews.authentication.k8s.io 的 Kubernetes 核心服务器的 kubeconfig 文件。 这是可选的。如果为空,则所有令牌请求均被视为匿名请求,并且不会在集群中查找任何客户端 CA。
--authentication-skip-lookup
如果为 false,则 authentication-kubeconfig 将用于从集群中查找缺少的身份验证配置。
--authentication-token-webhook-cache-ttl duration     默认值:10s
缓存来自 Webhook 令牌身份验证器的响应的持续时间。
--authentication-tolerate-lookup-failure     默认值:true
如果为 true,则无法从集群中查找缺少的身份验证配置是致命的。 请注意,这可能导致身份验证将所有请求视为匿名。
--authorization-always-allow-paths strings     默认值:"/healthz,/readyz,/livez"
在授权过程中跳过的 HTTP 路径列表,即在不联系 “core” kubernetes 服务器的情况下被授权的 HTTP 路径。
--authorization-kubeconfig string
指向具有足够权限以创建 subjectaccessreviews.authorization.k8s.io 的 Kubernetes 核心服务器的 kubeconfig 文件。这是可选的。 如果为空,则所有未被鉴权机制略过的请求都会被禁止。
--authorization-webhook-cache-authorized-ttl duration     默认值:10s
缓存来自 Webhook 授权者的 “authorized” 响应的持续时间。
--authorization-webhook-cache-unauthorized-ttl duration     默认值:10s
缓存来自 Webhook 授权者的 “unauthorized” 响应的持续时间。
--azure-container-registry-config string
包含 Azure 容器仓库配置信息的文件的路径。
--bind-address string     默认值:0.0.0.0
监听 --secure-port 端口的 IP 地址。 集群的其余部分以及 CLI/ Web 客户端必须可以访问关联的接口。 如果为空,将使用所有接口(0.0.0.0 表示使用所有 IPv4 接口,“::” 表示使用所有 IPv6 接口)。 如果为空或未指定地址 (0.0.0.0 或 ::),所有接口和 IP 地址簇将被使用。
--cert-dir string
TLS 证书所在的目录。如果提供了--tls-cert-file 和 --tls private-key-file, 则将忽略此参数。
--client-ca-file string
如果已设置,由 client-ca-file 中的证书机构签名的客户端证书的任何请求都将使用 与客户端证书的 CommonName 对应的身份进行身份验证。
--config string
配置文件的路径。
--contention-profiling     默认值:true
已弃用: 如果启用了性能分析,则启用锁竞争分析。 如果 --config 指定了一个配置文件,那么这个参数将被忽略。
--disabled-metrics strings
这个标志提供了一个规避不良指标的选项。你必须提供完整的指标名称才能禁用它。 免责声明:禁用指标的优先级比显示隐藏的指标更高。
--feature-gates <逗号分隔的 'key=True|False' 对>
一组 key=value 对,描述了 alpha/experimental 特性门控开关。选项包括:
APIListChunking=true|false (BETA - 默认值为 true)
APIPriorityAndFairness=true|false (BETA - 默认值为 true)
APIResponseCompression=true|false (BETA - 默认值为 true)
APIServerIdentity=true|false (BETA - 默认值为 true)
APIServerTracing=true|false (BETA - 默认值为 true)
AdmissionWebhookMatchConditions=true|false (BETA - 默认值为 true)
AggregatedDiscoveryEndpoint=true|false (BETA - 默认值为 true)
AllAlpha=true|false (ALPHA - 默认值为 false)
AllBeta=true|false (BETA - 默认值为 false)
AnyVolumeDataSource=true|false (BETA - 默认值为 true)
AppArmor=true|false (BETA - 默认值为 true)
CPUManagerPolicyAlphaOptions=true|false (ALPHA - 默认值为 false)
CPUManagerPolicyBetaOptions=true|false (BETA - 默认值为 true)
CPUManagerPolicyOptions=true|false (BETA - 默认值为 true)
CRDValidationRatcheting=true|false (ALPHA - 默认值为 false)
CSIMigrationPortworx=true|false (BETA - 默认值为 false)
CSINodeExpandSecret=true|false (BETA - 默认值为 true)
CSIVolumeHealth=true|false (ALPHA - 默认值为 false)
CloudControllerManagerWebhook=true|false (ALPHA - 默认值为 false)
CloudDualStackNodeIPs=true|false (ALPHA - 默认值为 false)
ClusterTrustBundle=true|false (ALPHA - 默认值为 false)
ComponentSLIs=true|false (BETA - 默认值为 true)
ConsistentListFromCache=true|false (ALPHA - 默认值为 false)
ContainerCheckpoint=true|false (ALPHA - 默认值为 false)
ContextualLogging=true|false (ALPHA - 默认值为 false)
CronJobsScheduledAnnotation=true|false (BETA - 默认值为 true)
CrossNamespaceVolumeDataSource=true|false (ALPHA - 默认值为 false)
CustomCPUCFSQuotaPeriod=true|false (ALPHA - 默认值为 false)
CustomResourceValidationExpressions=true|false (BETA - 默认值为 true)
DevicePluginCDIDevices=true|false (ALPHA - 默认值为 false)
DisableCloudProviders=true|false (ALPHA - 默认值为 false)
DisableKubeletCloudCredentialProviders=true|false (ALPHA - 默认值为 false)
DynamicResourceAllocation=true|false (ALPHA - 默认值为 false)
ElasticIndexedJob=true|false (BETA - 默认值为 true)
EventedPLEG=true|false (BETA - 默认值为 false)
GracefulNodeShutdown=true|false (BETA - 默认值为 true)
GracefulNodeShutdownBasedOnPodPriority=true|false (BETA - 默认值为 true)
HPAContainerMetrics=true|false (BETA - 默认值为 true)
HPAScaleToZero=true|false (ALPHA - 默认值为 false)
HonorPVReclaimPolicy=true|false (ALPHA - 默认值为 false)
InPlacePodVerticalScaling=true|false (ALPHA - 默认值为 false)
InTreePluginAWSUnregister=true|false (ALPHA - 默认值为 false)
InTreePluginAzureDiskUnregister=true|false (ALPHA - 默认值为 false)
InTreePluginAzureFileUnregister=true|false (ALPHA - 默认值为 false)
InTreePluginGCEUnregister=true|false (ALPHA - 默认值为 false)
InTreePluginOpenStackUnregister=true|false (ALPHA - 默认值为 false)
InTreePluginPortworxUnregister=true|false (ALPHA - 默认值为 false)
InTreePluginvSphereUnregister=true|false (ALPHA - 默认值为 false)
JobBackoffLimitPerIndex=true|false (ALPHA - 默认值为 false)
JobPodFailurePolicy=true|false (BETA - 默认值为 true)
JobPodReplacementPolicy=true|false (ALPHA - 默认值为 false)
JobReadyPods=true|false (BETA - 默认值为 true)
KMSv2=true|false (BETA - 默认值为 true)
KMSv2KDF=true|false (BETA - 默认值为 false)
KubeProxyDrainingTerminatingNodes=true|false (ALPHA - 默认值为 false)
KubeletCgroupDriverFromCRI=true|false (ALPHA - 默认值为 false)
KubeletInUserNamespace=true|false (ALPHA - 默认值为 false)
KubeletPodResourcesDynamicResources=true|false (ALPHA - 默认值为 false)
KubeletPodResourcesGet=true|false (ALPHA - 默认值为 false)
KubeletTracing=true|false (BETA - 默认值为 true)
LegacyServiceAccountTokenCleanUp=true|false (ALPHA - 默认值为 false)
LocalStorageCapacityIsolationFSQuotaMonitoring=true|false (ALPHA - 默认值为 false)
LogarithmicScaleDown=true|false (BETA - 默认值为 true)
LoggingAlphaOptions=true|false (ALPHA - 默认值为 false)
LoggingBetaOptions=true|false (BETA - 默认值为 true)
MatchLabelKeysInPodTopologySpread=true|false (BETA - 默认值为 true)
MaxUnavailableStatefulSet=true|false (ALPHA - 默认值为 false)
MemoryManager=true|false (BETA - 默认值为 true)
MemoryQoS=true|false (ALPHA - 默认值为 false)
MinDomainsInPodTopologySpread=true|false (BETA - 默认值为 true)
MultiCIDRRangeAllocator=true|false (ALPHA - 默认值为 false)
MultiCIDRServiceAllocator=true|false (ALPHA - 默认值为 false)
NewVolumeManagerReconstruction=true|false (BETA - 默认值为 true)
NodeInclusionPolicyInPodTopologySpread=true|false (BETA - 默认值为 true)
NodeLogQuery=true|false (ALPHA - 默认值为 false)
NodeSwap=true|false (BETA - 默认值为 false)
OpenAPIEnums=true|false (BETA - 默认值为 true)
PDBUnhealthyPodEvictionPolicy=true|false (BETA - 默认值为 true)
PersistentVolumeLastPhaseTransitionTime=true|false (ALPHA - 默认值为 false)
PodAndContainerStatsFromCRI=true|false (ALPHA - 默认值为 false)
PodDeletionCost=true|false (BETA - 默认值为 true)
PodDisruptionConditions=true|false (BETA - 默认值为 true)
PodHostIPs=true|false (ALPHA - 默认值为 false)
PodIndexLabel=true|false (BETA - 默认值为 true)
PodReadyToStartContainersCondition=true|false (ALPHA - 默认值为 false)
PodSchedulingReadiness=true|false (BETA - 默认值为 true)
ProcMountType=true|false (ALPHA - 默认值为 false)
QOSReserved=true|false (ALPHA - 默认值为 false)
ReadWriteOncePod=true|false (BETA - 默认值为 true)
RecoverVolumeExpansionFailure=true|false (ALPHA - 默认值为 false)
RemainingItemCount=true|false (BETA - 默认值为 true)
RotateKubeletServerCertificate=true|false (BETA - 默认值为 true)
SELinuxMountReadWriteOncePod=true|false (BETA - 默认值为 true)
SchedulerQueueingHints=true|false (BETA - 默认值为 true)
SecurityContextDeny=true|false (ALPHA - 默认值为 false)
ServiceNodePortStaticSubrange=true|false (BETA - 默认值为 true)
SidecarContainers=true|false (ALPHA - 默认值为 false)
SizeMemoryBackedVolumes=true|false (BETA - 默认值为 true)
SkipReadOnlyValidationGCE=true|false (ALPHA - 默认值为 false)
StableLoadBalancerNodeSet=true|false (BETA - 默认值为 true)
StatefulSetAutoDeletePVC=true|false (BETA - 默认值为 true)
StatefulSetStartOrdinal=true|false (BETA - 默认值为 true)
StorageVersionAPI=true|false (ALPHA - 默认值为 false)
StorageVersionHash=true|false (BETA - 默认值为 true)
TopologyAwareHints=true|false (BETA - 默认值为 true)
TopologyManagerPolicyAlphaOptions=true|false (ALPHA - 默认值为 false)
TopologyManagerPolicyBetaOptions=true|false (BETA - 默认值为 true)
TopologyManagerPolicyOptions=true|false (BETA - 默认值为 true)
UnknownVersionInteroperabilityProxy=true|false (ALPHA - 默认值为 false)
UserNamespacesSupport=true|false (ALPHA - 默认值为 false)
ValidatingAdmissionPolicy=true|false (BETA - 默认值为 false)
VolumeCapacityPriority=true|false (ALPHA - 默认值为 false)
WatchList=true|false (ALPHA - 默认值为 false)
WinDSR=true|false (ALPHA - 默认值为 false)
WinOverlay=true|false (BETA - 默认值为 true)
WindowsHostNetwork=true|false (ALPHA - 默认值为 true)
-h, --help
kube-scheduler 帮助命令
--http2-max-streams-per-connection int
服务器为客户端提供的 HTTP/2 连接最大限制。零表示使用 Golang 的默认值。
--kube-api-burst int32     默认值:100
已弃用: 与 kubernetes API 通信时使用的突发请求个数限值。 如果 --config 指定了一个配置文件,那么这个参数将被忽略。
--kube-api-content-type string     默认值:"application/vnd.kubernetes.protobuf"
已弃用: 发送到 API 服务器的请求的内容类型。 如果 --config 指定了一个配置文件,那么这个参数将被忽略。
--kube-api-qps float     默认值:50
已弃用: 与 kubernetes apiserver 通信时要使用的 QPS 如果 --config 指定了一个配置文件,那么这个参数将被忽略。
--kubeconfig string
已弃用: 包含鉴权和主节点位置信息的 kubeconfig 文件的路径。 如果 --config 指定了一个配置文件,那么这个参数将被忽略。
--leader-elect     默认值:true
在执行主循环之前,开始领导者选举并选出领导者。 使用多副本来实现高可用性时,可启用此标志。
--leader-elect-lease-duration duration     默认值:15s
非领导者候选人在观察到领导者更新后将等待直到试图获得领导但未更新的领导者职位的等待时间。 这实际上是领导者在被另一位候选人替代之前可以停止的最大持续时间。 该情况仅在启用了领导者选举的情况下才适用。
--leader-elect-renew-deadline duration     默认值:10s
领导者尝试在停止领导之前更新领导职位的间隔时间。该时间必须小于租赁期限。 仅在启用了领导者选举的情况下才适用。
--leader-elect-resource-lock string     默认值:"leases"
在领导者选举期间用于锁定的资源对象的类型。支持的选项有 `leases`、`endpointleases` 和 `configmapsleases`。
--leader-elect-resource-name string     默认值:"kube-scheduler"
在领导者选举期间用于锁定的资源对象的名称。
--leader-elect-resource-namespace string     默认值:"kube-system"
在领导者选举期间用于锁定的资源对象的命名空间。
--leader-elect-retry-period duration     默认值:2s
客户应在尝试获取和更新领导之间等待的时间。仅在启用了领导者选举的情况下才适用。
--log-flush-frequency duration     默认值:5s
两次日志刷新之间的最大秒数。
--logging-format string     默认值:“text”
设置日志格式。可选格式:“text”。
--logtostderr     默认值:true
日志记录到标准错误输出而不是文件。
--master string
Kubernetes API 服务器的地址(覆盖 kubeconfig 中的任何值)。
--permit-address-sharing
如果为 true,在绑定端口时将使用 SO_REUSEADDR。 这将允许同时绑定诸如 0.0.0.0 这类通配符 IP和特定 IP, 并且它避免等待内核释放处于 TIME_WAIT 状态的套接字。 默认值:false
--permit-port-sharing
如果此标志为 true,在绑定端口时会使用 SO_REUSEPORT,从而允许不止一个 实例绑定到同一地址和端口。 默认值:false
--pod-max-in-unschedulable-pods-duration duration     默认值:5m0s
已弃用:Pod 可以在 unschedulablePods 中停留的最长时间。 如果 Pod 在 unschedulablePods 中停留的时间超过此值,则该 pod 将被从 unschedulablePods 移动到 backoffQ 或 activeQ。 此标志已弃用,将在 1.2 中删除。
--profiling     默认值:true
已弃用: 通过 Web 界面主机启用配置文件:host:port/debug/pprof/。 如果 --config 指定了一个配置文件,这个参数将被忽略。
--requestheader-allowed-names stringSlice
客户端证书通用名称列表,允许在 --requestheader-username-headers 指定的头部中提供用户名。如果为空,则允许任何由 --requestheader-client-ca-file 中证书机构验证的客户端证书。
--requestheader-client-ca-file string
在信任 --requestheader-username-headers 指定的头部中的用户名之前 用于验证传入请求上的客户端证书的根证书包。 警告:通常不应假定传入请求已经完成鉴权。
--requestheader-extra-headers-prefix strings      默认值:"x-remote-extra-"
要检查请求头部前缀列表。建议使用 X-Remote-Extra-
--requestheader-group-headers strings      默认值:"x-remote-group"
用于检查组的请求头部列表。建议使用 X-Remote-Group
--requestheader-username-headers strings      默认值:"x-remote-user"
用于检查用户名的请求头部列表。X-Remote-User 很常用。
--secure-port int     默认值:10259
通过身份验证和授权为 HTTPS 服务的端口。如果为 0,则根本不提供 HTTPS。
--show-hidden-metrics-for-version string
你希望显式隐藏指标的老版本号。只有较早的此版本号有意义,其它值都是不允许的。 格式为 <主版本>.<此版本>,例如:'1.16'。 此格式的目的是确保你有机会注意到是否下一个发行版本中隐藏了一些额外的指标, 而不是当某些指标在该版本之后被彻底移除时感到震惊。
--tls-cert-file string
包含默认的 HTTPS x509 证书的文件。(如果有 CA 证书,在服务器证书之后并置)。 如果启用了 HTTPS 服务,并且未提供 --tls-cert-file--tls-private-key-file,则会为公共地址生成一个自签名证书和密钥, 并将其保存到 --cert-dir 指定的目录中。
--tls-cipher-suites strings
服务器的密码套件列表,以逗号分隔。如果省略,将使用默认的 Go 密码套件。 优先考虑的值: TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, TLS_RSA_WITH_3DES_EDE_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_GCM_SHA256, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_256_GCM_SHA384.
不安全的值: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_RSA_WITH_RC4_128_SHA.
--tls-min-version string
支持的最低 TLS 版本。可能的值:VersionTLS10, VersionTLS11, VersionTLS12, VersionTLS13
--tls-private-key-file string
包含与 --tls-cert-file 匹配的默认 x509 私钥的文件。
--tls-sni-cert-key string
一对 x509 证书和私钥文件路径,也可以包含由全限定域名构成的域名模式列表作为后缀, 并可能带有前缀的通配符段。域名匹配还允许是 IP 地址, 但是只有当 apiserver 对客户端请求的 IP 地址可见时,才能使用 IP。 如果未提供域名匹配模式,则提取证书名称。 非通配符匹配优先于通配符匹配,显式域名匹配优先于提取而来的名称。 若有多个密钥/证书对,可多次使用 --tls-sni-cert-key。 例如: "example.crt,example.key" 或者 "foo.crt,foo.key:*.foo.com,foo.com"。
-v, --v int
设置日志级别详细程度的数字
--version version[=true]
--version, --version=raw 打印版本信息并推出; --version=vX.Y.Z... 设置报告的版本。
--vmodule pattern=N,...
以逗号分隔的 “pattern=N” 设置列表,用于文件过滤的日志记录(仅适用于文本日志格式)。
--write-config-to string
如果设置此参数,将配置值写入此文件并退出。

6.14 - 配置 API

6.14.1 - Event Rate Limit Configuration (v1alpha1)

资源类型

Configuration

Configuration 为 EventRateLimit 准入控制器提供配置数据。

字段描述
apiVersion
string
eventratelimit.admission.k8s.io/v1alpha1
kind
string
Configuration
limits [Required]
[]Limit

limits 是为所接收到的事件查询设置的限制。可以针对服务器端接收到的事件设置限制, 按逐个名字空间、逐个用户、或逐个来源+对象组合的方式均可以。 至少需要设置一种限制。

Limit

出现在:

Limit 是为特定限制类型提供的配置数据。

字段描述
type [必需]
LimitType

type 是此配置所适用的限制的类型。

qps [必需]
int32

qps 是针对此类型的限制每秒钟所允许的事件查询次数。qps 和 burst 字段一起用来确定是否特定的事件查询会被接受。qps 确定的是当超出查询数量的 burst 值时可以接受的查询个数。

burst [必需]
int32

burst 是针对此类型限制的突发事件查询数量。qps 和 burst 字段一起使用可用来确定特定的事件查询是否被接受。 burst 字段确定针对特定的事件桶(bucket)可以接受的规模上限。 例如,如果 burst 是 10,qps 是 3,那么准入控制器会在接收 10 个查询之后阻塞所有查询。 每秒钟可以额外允许 3 个查询。如果这一限额未被用尽,则剩余的限额会被顺延到下一秒钟, 直到再次达到 10 个限额的上限。

cacheSize
int32

cacheSize 是此类型限制的 LRU 缓存的规模。如果某个事件桶(bucket)被从缓存中剔除, 该事件桶所对应的限额也会被重置。如果后来再次收到针对某个已被剔除的事件桶的查询, 则该事件桶会重新以干净的状态进入缓存,因而获得全量的突发查询配额。

默认的缓存大小是 4096。

如果 limitType 是 “server”,则 cacheSize 设置会被忽略。

LimitType

string 类型的别名)

出现在:

LimitType 是限制类型(例如:per-namespace)。

6.14.2 - Image Policy API (v1alpha1)

资源类型

ImageReview

ImageReview 检查某个 Pod 中是否可以使用某些镜像。

字段描述
apiVersion
string
imagepolicy.k8s.io/v1alpha1
kind
string
ImageReview
metadata
meta/v1.ObjectMeta

标准的对象元数据。更多信息:https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata

参阅 Kubernetes API 文档了解 metadata 字段的内容。
spec [必需]
ImageReviewSpec

spec 中包含与被评估的 Pod 相关的信息。

status
ImageReviewStatus

status 由后台负责填充,用来标明 Pod 是否会被准入。

ImageReviewContainerSpec

出现在:

ImageReviewContainerSpec 是对 Pod 创建请求中的某容器的描述。

字段描述
image
string

此字段的格式可以是 image:tag 或 image@SHA:012345679abcdef。

ImageReviewSpec

出现在:

ImageReviewSpec 是对 Pod 创建请求的描述。

字段描述
containers
[]ImageReviewContainerSpec

containers 是一个列表,其中包含正被创建的 Pod 中各容器的信息子集。

annotations
map[string]string

annotations 是一个键值对列表,内容抽取自 Pod 的注解(annotations)。 其中仅包含与模式 *.image-policy.k8s.io/* 匹配的键。 每个 Webhook 后端要负责决定如何解释这些注解(如果有的话)。

namespace
string

namespace 是 Pod 创建所针对的名字空间。

ImageReviewStatus

出现在:

ImageReviewStatus 是针对 Pod 创建请求所作的评估结果。

字段描述
allowed [必需]
bool

allowed 表明所有镜像都可以被运行。

reason
string

allowed 不是 false,reason 应该为空。 否则其中应包含出错信息的简短描述。Kubernetes 在向用户展示此信息时可能会截断过长的错误文字。

auditAnnotations
map[string]string

auditAnnotations 会被通过 AddAnnotation 添加到准入控制器的 attributes 对象上。 注解键应该不含前缀,换言之,准入控制器会添加合适的前缀。

6.14.3 - kube 配置 (v1)

资源类型

Config

Config 保存以给定用户身份构建连接到远程 Kubernetes 集群所需的信息

字段描述
apiVersion
string
/v1
kind
string
Config
kind
string

来自 pkg/api/types.go TypeMeta 的遗留字段。

apiVersion
string

来自 pkg/api/types.go TypeMeta 的遗留字段。

preferences[必需]
Preferences

preferences保存用于 CLI 交互的一般信息。

clusters[必需]
[]NamedCluster

clusters 是从可引用名称到集群配置的映射。

users[必需]
[]NamedAuthInfo

users 是一个从可引用名称到用户配置的映射。

contexts[必需]
[]NamedContext

contexts 是从可引用名称到上下文配置的映射。

current-context[必需]
string

current-context 是默认情况下你想使用的上下文的名称。

extensions
[]NamedExtension

extensions 保存额外信息。这对于扩展程序是有用的,目的是使读写操作不会破解未知字段。

AuthInfo

出现在:

AuthInfo 包含描述身份信息的信息。这一信息用来告诉 kubernetes 集群你是谁。

字段描述
client-certificate
string

client-certificate 是 TLS 客户端证书文件的路径。

client-certificate-data
[]byte

client-certificate-data 包含用于 TLS 连接的、来自客户端证书的 PEM 编码的数据。 此字段值会覆盖 client-certificate 内容。

client-key
string

client-key 是用于 TLS 连接的客户端密钥文件的路径。

client-key-data
[]byte

client-key-data 包含用于 TLS 连接的、来自客户端密钥文件的 PEM 编码数据。此数据会覆盖 client-key 的内容。

token
string

token 是用于向 kubernetes 集群进行身份验证的持有者令牌。

tokenFile
string

tokenFile 是一个指针,指向包含有持有者令牌(如上所述)的文件。 如果 tokentokenFile 都存在,token 优先。

as
string

as 是要冒充的用户名。名字与命令行标志相匹配。

as-uid
string

as-uid 是要冒充的 UID。

as-groups
[]string

as-groups 是要冒充的用户组。

as-user-extra
map[string][]string

as-user-extra 包含与要冒充的用户相关的额外信息。

username
string

username 是向 Kubernetes 集群进行基本认证的用户名。

password
string

password 是向 Kubernetes 集群进行基本认证的密码。

auth-provider
AuthProviderConfig

auth-provider 给出用于给定 Kubernetes 集群的自定义身份验证插件。

exec
ExecConfig

exec 指定了针对某 Kubernetes 集群的基于 exec 的自定义身份认证插件。

extensions
[]NamedExtension

extensions 保存一些额外信息。这些信息对于扩展程序是有用的,目的是确保读写操作不会破坏未知字段。

AuthProviderConfig

出现在:

AuthProviderConfig 保存特定于某认证提供机制的配置。

字段描述
name[必需]
string

配置选项名称。

config[必需]
map[string]string

配置选项取值映射。

Cluster

出现在:

Cluster 包含有关如何与 Kubernetes 集群通信的信息。

字段描述
server[必需]
string

server 是 Kubernetes 集群的地址(形式为 https://hostname:port)。

tls-server-name
string

tls-server-name 用于检查服务器证书。如果 tls-server-name 是空的,则使用用于联系服务器的主机名。

insecure-skip-tls-verify
bool

insecure-skip-tls-verify 跳过服务器证书的有效性检查。 这样做将使你的 HTTPS 连接不安全。

certificate-authority
string

certificate-authority 是证书机构的证书文件的路径。

certificate-authority-data
[]byte

certificate-authority-data 包含 PEM 编码的证书机构证书。 覆盖 certificate-authority 的设置值。

proxy-url
string

proxy-url 是代理的 URL,该代理用于此客户端的所有请求。 带有 "http"、"https" 和 "socks5" 的 URL 是被支持的。 如果未提供此配置或为空字符串,客户端尝试使用 http_proxyhttps_proxy 环境变量构建代理配置。 如果这些环境变量也没有设置, 客户端不会尝试代理请求。

socks5 代理当前不支持 SPDY 流式端点 (execattachport-forward)。

disable-compression
bool

disable-compression 允许客户端选择不对发往服务器的所有请求进行响应压缩。 当客户端与服务器之间的网络带宽充足时,这对于加快请求(尤其是 list 操作)非常有用, 能够节省进行(服务器端)压缩和(客户端)解压缩的时间。参见 https://github.com/kubernetes/kubernetes/issues/112296。

extensions
[]NamedExtension

extensions 保存一些额外信息。 这些信息对于扩展程序是有用的,目的是确保读写操作不会破坏未知字段。

Context

出现在:

Context 是一个元组,包含对集群 (我如何与某 Kubernetes 集群通信)、用户 (我如何标识自己) 和名字空间(我想要使用哪些资源子集)的引用。

字段描述
cluster[必需]
string

cluster 是此上下文中的集群名称。

user[必需]
string

user 是此上下文的 authInfo 名称。

namespace
string

namespace 是在请求中未明确指定时使用的默认名字空间。

extensions
[]NamedExtension

extensions 保存一些额外信息。 这些信息对于扩展程序是有用的,目的是确保读写操作不会破坏未知字段。

ExecConfig

出现在:

ExecConfig 指定提供客户端凭证的命令。这个命令被执行(以 exec 方式) 并输出结构化的标准输出(stdout),其中包含了凭据。

查看 client.authentication.k8s.io API 组以获取输入和输出的确切格式规范。

字段描述
command[必需]
string

command 是要执行的命令。

args
[]string

args 是执行命令时要传递的参数。

env
[]ExecEnvVar

env 定义了要暴露给进程的额外的环境变量。这些与主机的环境变量以及 client-go 使用的变量一起,用于传递参数给插件。

apiVersion[必需]
string

ExecInfo 的首选输入版本。返回的 ExecCredentials 必须使用与输入相同的编码版本。

installHint[必需]
string

当似乎找不到可执行文件时,将向用户显示此文本。 例如,对于在 Mac OS 系统上安装 foo-cli 插件而言, brew install foo-cli 这可能是不错的 installHint。

provideClusterInfo[必需]
bool

ProvideClusterInfo 决定是否提供集群信息。 这些信息可能包含非常大的 CA 数据,用来作为 KUBERNETES_EXEC_INFO 环境变量的一部分提供给这个 exec 插件。 默认情况下,它被设置为 falsek8s.io/client-go/tools/auth/exec 包提供了用于读取这个环境变量的辅助方法。

interactiveMode
ExecInteractiveMode

interactiveMode 确定此插件与标准输入之间的关系。有效值为:

  • "Never":这个 exec 插件从不使用标准输入;
  • "IfAvailable":这个 exec 插件希望使用标准输入,如果可用的话;
  • "Always":这个 exec 插件需要标准输入以正常运行。

查看 ExecInteractiveMode 值以了解更多详情。

如果 apiVersionclient.authentication.k8s.io/v1alpha1client.authentication.k8s.io/v1beta1, 则此字段是可选的, 且当未设置时默认为 "IfAvailable"。否则,此字段是必需的。

ExecEnvVar

出现在:

ExecEnvVar 用于在执行基于 exec 的凭据插件时要设置的环境变量。

字段描述
name
[必需] string

环境变量名称。

value
[必需]
string

环境变量取值。

ExecInteractiveMode

string 的别名)

出现在:

ExecInteractiveMode 是一个描述 exec 插件与标准输入间关系的字符串。

NamedAuthInfo

出现在:

NamedAuthInfo 将昵称与身份认证信息关联起来。

字段描述
name[必需] string

name 是该 AuthInfo 的昵称。

user[必需]
AuthInfo

user 保存身份认证信息。

NamedCluster

出现在:

NamedCluster 将昵称与集群信息关联起来。

字段描述
name
[必需]

string

name 是此集群的昵称。

cluster
[必需]
Cluster

cluster 保存集群的信息。

NamedContext

出现在:

NamedContext 将昵称与上下文信息关联起来。

字段描述
name
[必需]

string

name 是此上下文的昵称。

context
[必需] Context

context 保存上下文信息。

NamedExtension

出现在:

NamedExtension 将昵称与扩展信息关联起来。

字段描述
name[必需]
string

name 是此扩展的昵称。

extension[必需]
k8s.io/apimachinery/pkg/runtime.RawExtension

extension 保存扩展信息。

Preferences

出现在:

字段描述
colors
bool

是否采用彩色字符编码。

extensions
[]NamedExtension

extensions 保存一些额外信息。 这些信息对于扩展程序是有用的,目的是确保读写操作不会破坏未知字段。

6.14.4 - kube-apiserver Admission (v1)

资源类型

AdmissionReview

AdmissionReview 描述准入评审请求/响应。

字段描述
apiVersion
string
admission.k8s.io/v1
kind
string
AdmissionReview
request
AdmissionRequest

request 描述准入请求的属性。

response
AdmissionResponse

response 描述准入响应的属性。

AdmissionRequest

出现在:

AdmissionRequest 描述准入请求的 admission.Attributes。

字段描述
uid [必需]
k8s.io/apimachinery/pkg/types.UID

uid 是用于标识单个请求/响应的标识符。它允许我们区分在其他情况下完全相同的请求实例(并行请求、在先前请求未修改时的请求等)。 uid 的目的是跟踪 KAS(Kubernetes Admission Server)和 WebHook 之间的轮询(请求/响应),而不是用户请求。 它适用于在 WebHook 和 API 服务器之间建立日志条目上的关联,从而服务于审计或调试目的。

kind [必需]
meta/v1.GroupVersionKind

kind 是正被提交的对象的全限定类别名称(例如 v1.Pod 或 autoscaling.v1.Scale)。

resource [必需]
meta/v1.GroupVersionResource

resource 是正被请求的资源的全限定名称(例如 v1.pods)。

subResource
string

subResource 是正被请求的子资源——如果存在的话(例如 "status" 或 "scale")。

requestKind
meta/v1.GroupVersionKind

requestKind 是原始 API 请求的完全限定类别名称(例如 v1.Pod 或 autoscaling.v1.Scale)。 如果此字段被指定且不同于 "kind" 中的值,则执行等效的匹配和转换。

例如,如果 Deployment 可以通过 apps/v1 和 apps/v1beta1 进行修改,并且 Webhook 注册了 apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]matchPolicy: Equivalent 的规则,那么指向 apps/v1beta1 Deployment 的 API 请求将被转换并发送到 Webhook, 其中 kind: {group:"apps", version:"v1", kind:"Deployment"} (与 Webhook 注册的规则匹配)并且 requestKind: {group:"apps", version:"v1beta1", kind:"Deployment"}(指示原始 API 请求的类别)。

参阅文档了解 Webhook 配置类型中 "matchPolicy" 字段的更多细节。

requestResource
meta/v1.GroupVersionResource

requestResource 是原始 API 请求的全限定资源名称(例如 v1.pods)。 如果此字段被指定且不同于 "resource" 中的值,则执行等效的匹配和转换。

例如,如果 Deployment 可以通过 apps/v1 和 apps/v1beta1 修改,并且 Webhook 注册了 apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]matchPolicy: Equivalent 的规则,那么指向 apps/v1beta1 Deployment 的 API 请求将被转换并发送到 Webhook, 其中 resource: {group:"apps", version:"v1", resource:"deployments"} (与 Webhook 注册的资源匹配)以及 requestResource: {group:"apps", version:"v1beta1", resource:"deployments"}(指示原始 API 请求的资源)。

参阅文档了解 Webhook 配置类型中 "matchPolicy" 字段的更多细节。

requestSubResource
string

requestSubResource 是可能存在的、原始 API 所请求的子资源(例如 "status" 或 "scale")。 如果此字段被指定且不同于 "subResource" 中的值,则执行等效的匹配和转换。 参阅文档了解 Webhook 配置类型中的 "matchPolicy" 字段。

name
string

name 是出现在请求中的对象的名称。客户端在执行 CREATE 操作时,可以忽略此命令并依赖服务器生成此名称。 如果是这种情况,此字段将包含一个空白字符串。

namespace
string

namespace 是与请求(如果有的话)关联的命名空间。

operation [必需]
Operation

operation 是正在执行的操作。这可能不同于请求的操作, 例如 patch 可以造成 CREATE 或 UPDATE 操作。

userInfo [必需]
authentication/v1.UserInfo

userInfo 是发出请求的用户的相关信息。

object
k8s.io/apimachinery/pkg/runtime.RawExtension

object 是来自传入请求的对象。

oldObject
k8s.io/apimachinery/pkg/runtime.RawExtension

oldObject 是现有的对象。只有 DELETE 和 UPDATE 请求中此字段会有值。

dryRun
bool

dryRun 表示此请求的修改绝对不会被持久化。默认为 false。

options
k8s.io/apimachinery/pkg/runtime.RawExtension

options 是正在执行的操作的操作选项结构。 例如 meta.k8s.io/v1.DeleteOptionsmeta.k8s.io/v1.CreateOptions。 所设置的值可能不同于调用方所提供的选项。例如 patch 请求执行的操作可能是 CREATE,那这种情况下即使调用方提供了 meta.k8s.io/v1.PatchOptionsoptions 也将是 meta.k8s.io/v1.CreateOptions

AdmissionResponse

出现在:

AdmissionResponse 描述准入响应。

字段描述
uid [必需]
k8s.io/apimachinery/pkg/types.UID

uid 是标识单独请求/响应的标识符。 它必须从相应的 AdmissionRequest 复制过来。

allowed [必需]
bool

allowed 表示准入请求是否被允许。

status
meta/v1.Status

status 包含为什么准入请求被拒绝的额外细节。 如果 "Allowed" 的值为 "true",则不会以任何方式使用此字段。

patch
[]byte

patch 操作的主体。目前 Kubernetes 仅支持实现了 RFC 6902 的 "JSONPatch"。

patchType
PatchType

patch 的类型。目前 Kubernetes 仅允许 "JSONPatch"。

auditAnnotations
map[string]string

auditAnnotations 是由远程准入控制器设置的非结构化键值映射(例如 error=image-blacklisted)。 MutatingAdmissionWebhook 和 ValidatingAdmissionWebhook 准入控制器将在键前缀中使用准入 Webhook 名称 (例如 imagepolicy.example.com/error=image-blacklisted)。auditAnnotations 将由准入 Webhook 提供,向此请求的审计日志添加额外的上下文。

warnings
[]string

warnings 是警告消息的列表,返回给发出请求的 API 客户端。 这些警告消息描述客户端在进行 API 请求时应该纠正或注意的问题。 如果可能的话,将 warnings 限制在 120 个字符以内。 如果 warnings 中的消息超过 256 个字符,或 warnings 数量过多,可能会被截断。

Operation

string 的别名)

出现在:

Operation 是正在检查准入控制时资源操作的类型。

PatchType

string 的别名)

出现在:

PatchType 是用于表示所变更对象的补丁类型。

6.14.5 - kube-apiserver Audit 配置 (v1)

资源类型

Event

出现在:

Event 结构包含可出现在 API 审计日志中的所有信息。

字段描述
apiVersion
string
audit.k8s.io/v1
kind
string
Event
level [必需]
Level

生成事件所对应的审计级别。

auditID [必需]
k8s.io/apimachinery/pkg/types.UID

为每个请求所生成的唯一审计 ID。

stage [必需]
Stage

生成此事件时请求的处理阶段。

requestURI [必需]
string

requestURI 是客户端发送到服务器端的请求 URI。

verb [必需]
string

verb 是与请求对应的 Kubernetes 动词。对于非资源请求,此字段为 HTTP 方法的小写形式。

user [必需]
authentication/v1.UserInfo

关于认证用户的信息。

impersonatedUser
authentication/v1.UserInfo

关于所伪装(impersonated)的用户的信息。

sourceIPs
[]string

发起请求和中间代理的源 IP 地址。 源 IP 从以下(按顺序)列出:

  1. X-Forwarded-For 请求标头 IP
  2. X-Real-Ip 标头,如果 X-Forwarded-For 列表中不存在
  3. 连接的远程地址,如果它无法与此处列表中的最后一个 IP(X-Forwarded-For 或 X-Real-Ip)匹配。 注意:除最后一个 IP 外的所有 IP 均可由客户端任意设置。
userAgent
string

userAgent 中记录客户端所报告的用户代理(User Agent)字符串。 注意 userAgent 信息是由客户端提供的,一定不要信任。

objectRef
ObjectReference

此请求所指向的对象引用。对于 List 类型的请求或者非资源请求,此字段可忽略。

responseStatus
meta/v1.Status

响应的状态,当 responseObject 不是 Status 类型时被赋值。 对于成功的请求,此字段仅包含 code 和 statusSuccess。 对于非 Status 类型的错误响应,此字段会被自动赋值为出错信息。

requestObject
k8s.io/apimachinery/pkg/runtime.Unknown

来自请求的 API 对象,以 JSON 格式呈现。requestObject 在请求中按原样记录 (可能会采用 JSON 重新编码),之后会进入版本转换、默认值填充、准入控制以及配置信息合并等阶段。 此对象为外部版本化的对象类型,甚至其自身可能并不是一个合法的对象。对于非资源请求,此字段被忽略。 只有当审计级别为 Request 或更高的时候才会记录。

responseObject
k8s.io/apimachinery/pkg/runtime.Unknown

响应中包含的 API 对象,以 JSON 格式呈现。responseObject 是在被转换为外部类型并序列化为 JSON 格式之后才被记录的。对于非资源请求,此字段会被忽略。 只有审计级别为 Response 时才会记录。

requestReceivedTimestamp
meta/v1.MicroTime

请求到达 API 服务器时的时间。

stageTimestamp
meta/v1.MicroTime

请求到达当前审计阶段时的时间。

annotations
map[string]string

annotations 是一个无结构的键-值映射,其中保存的是一个审计事件。 该事件可以由请求处理链路上的插件来设置,包括身份认证插件、鉴权插件以及准入控制插件等。 注意这些注解是针对审计事件本身的,与所提交的对象中的 metadata.annotations 之间不存在对应关系。 映射中的键名应该唯一性地标识生成该事件的组件,从而避免名字上的冲突 (例如 podsecuritypolicy.admission.k8s.io/policy)。 映射中的键值应该比较简洁。 当审计级别为 Metadata 时会包含 annotations 字段。

EventList

EventList 是审计事件(Event)的列表。

字段描述
apiVersion
string
audit.k8s.io/v1
kind
string
EventList
metadata
meta/v1.ListMeta
列表结构元数据
items [必需]
[]Event
事件对象列表

Policy

出现在:

Policy 定义的是审计日志的配置以及不同类型请求的日志记录规则。

字段描述
apiVersion
string
audit.k8s.io/v1
kind
string
Policy
metadata
meta/v1.ObjectMeta

包含 metadata 字段是为了便于与 API 基础设施之间实现互操作。

参考 Kubernetes API 文档了解 metadata 字段的详细信息。
rules [必需]
[]PolicyRule

字段 rules 设置请求要被记录的审计级别(level)。 每个请求可能会与多条规则相匹配;发生这种状况时遵从第一条匹配规则。 默认的审计级别是 None,不过可以在列表的末尾使用一条全抓(catch-all)规则重载其设置。 列表中的规则(PolicyRule)是严格有序的。

omitStages
[]Stage

字段 omitStages 是一个阶段(Stage)列表,其中包含无须生成事件的阶段。 注意这一选项也可以通过每条规则来设置。 审计组件最终会忽略出现在 omitStages 中阶段,也会忽略规则中的阶段。

omitManagedFields
bool

omitManagedFields 标明将请求和响应主体写入 API 审计日志时,是否省略其托管字段。 此字段值用作全局默认值 - 'true' 值将省略托管字段,否则托管字段将包含在 API 审计日志中。 请注意,也可以按规则指定此值,在这种情况下,规则中指定的值将覆盖全局默认值。

PolicyList

PolicyList 是由审计策略(Policy)组成的列表。

字段描述
apiVersion
string
audit.k8s.io/v1
kind
string
PolicyList
metadata
meta/v1.ListMeta
列表结构元数据。
items [必需]
[]Policy
策略(Policy)对象列表。

GroupResources

出现在:

GroupResources 代表的是某 API 组中的资源类别。

字段描述
group
string
字段 group 给出包含资源的 API 组的名称。 空字符串代表 core API 组。
resources
[]string

resources 是此规则所适用的资源的列表。

例如:

  • pods 匹配 Pod。
  • pods/log 匹配 Pod 的 log 子资源。
  • * 匹配所有资源及其子资源。
  • pods/* 匹配 Pod 的所有子资源。
  • */scale 匹配所有的 scale 子资源。

如果存在通配符,则合法性检查逻辑会确保 resources 中的条目不会彼此重叠。

空的列表意味着规则适用于该 API 组中的所有资源及其子资源。

resourceNames
[]string

字段 resourceNames 是策略将匹配的资源实例名称列表。 使用此字段时,resources 必须指定。 空的 resourceNames 列表意味着资源的所有实例都会匹配到此策略。

Level

string 数据类型的别名。

出现在:

Level 定义的是审计过程中在日志内记录的信息量。

ObjectReference

出现在:

ObjectReference 包含的是用来检查或修改所引用对象时将需要的全部信息。

字段描述
resource
string
资源类别。
namespace
string
资源对象所在名字空间。
name
string
资源对象名称。
uid
k8s.io/apimachinery/pkg/types.UID
资源对象的唯一标识(UID)。
apiGroup
string

字段 apiGroup 给出包含所引用对象的 API 组的名称。 空字符串代表 core API 组。

apiVersion
string

字段 apiVersion 是包含所引用对象的 API 组的版本。

resourceVersion
string
资源对象自身的版本值。
subresource
string
子资源的类别。

PolicyRule

出现在:

PolicyRule 包含一个映射,基于元数据将请求映射到某审计级别。 请求必须与每个字段所定义的规则都匹配(即 rules 的交集)才被视为匹配。

字段描述
level [必需]
Level

与此规则匹配的请求所对应的日志记录级别(Level)。

users
[]string

根据身份认证所确定的用户名的列表,给出此规则所适用的用户。 空列表意味着适用于所有用户。

userGroups
[]string

此规则所适用的用户组的列表。如果用户是所列用户组中任一用户组的成员,则视为匹配。 空列表意味着适用于所有用户组。

verbs
[]string

此规则所适用的动词(verb)列表。 空列表意味着适用于所有动词。

resources
[]GroupResources

此规则所适用的资源类别列表。 空列表意味着适用于 API 组中的所有资源类别。

namespaces
[]string

此规则所适用的名字空间列表。 空字符串("")意味着适用于非名字空间作用域的资源。 空列表意味着适用于所有名字空间。

nonResourceURLs
[]string

nonResourceURLs 是一组需要被审计的 URL 路径。 允许使用 *,但只能作为路径中最后一个完整分段。 例如:

  • "/metrics" - 记录对 API 服务器度量值(metrics)的所有请求;
  • "/healthz*" - 记录所有健康检查。
omitStages
[]Stage

字段 omitStages 是一个阶段(Stage)列表,针对所列的阶段服务器不会生成审计事件。 注意这一选项也可以在策略(Policy)级别指定。服务器审计组件会忽略 omitStages 中给出的阶段,也会忽略策略中给出的阶段。 空列表意味着不对阶段作任何限制。

omitManagedFields
bool

omitManagedFields 决定将请求和响应主体写入 API 审计日志时,是否省略其托管字段。

  • 值为 'true' 将从 API 审计日志中删除托管字段
  • 值为 'false' 表示托管字段应包含在 API 审计日志中 请注意,如果指定此规则中的值将覆盖全局默认值。 如果未指定,则使用 policy.omitManagedFields 中指定的全局默认值。

Stage

string 数据类型的别名。

出现在:

Stage 定义在请求处理过程中可以生成审计事件的阶段。

6.14.6 - kube-apiserver 配置 (v1)

v1 包中包含 API 的 v1 版本。

资源类型

AdmissionConfiguration

AdmissionConfiguration 为准入控制器提供版本化的配置。

字段描述
apiVersion
string
apiserver.config.k8s.io/v1
kind
string
AdmissionConfiguration
plugins
[]AdmissionPluginConfiguration

plugins 字段允许为每个准入控制插件设置配置选项。

AdmissionPluginConfiguration

出现在:

AdmissionPluginConfiguration 为某个插件提供配置信息。

字段描述
name [必需]
string

name 是准入控制器的名称。它必须与所注册的准入插件名称匹配。

path
string

path 是指向包含插件配置信息的配置文件的路径。

configuration
k8s.io/apimachinery/pkg/runtime.Unknown

configuration 是一个内嵌的配置对象,用来保存插件的配置信息。 如果存在,则使用这里的配置信息而不是指向配置文件的路径。

6.14.7 - kube-apiserver 配置 (v1alpha1)

包 v1alpha1 包含 API 的 v1alpha1 版本。

资源类型

TracingConfiguration

出现在:

TracingConfiguration 为 OpenTelemetry 跟踪客户端提供了不同版本的配置。

字段描述
endpoint
string

采集器的端点,此组件将向其报告跟踪信息。 连接不安全,目前不支持 TLS。 推荐不设置,端点为 otlp grpc 默认值 localhost:4317。

samplingRatePerMillion
int32

SamplingRatePerMillion 是每百万 span 中采集的样本数。 推荐不设置。如果不设置,采集器将继承其父级 span 的采样率,否则不进行采样。

AdmissionConfiguration

AdmissionConfiguration 为准入控制器提供版本化的配置信息。

字段描述
apiVersion
string
apiserver.k8s.io/v1alpha1
kind
string
AdmissionConfiguration
plugins
[]AdmissionPluginConfiguration

plugins 允许用户为每个准入控制插件指定设置。

EgressSelectorConfiguration

EgressSelectorConfiguration 为 Egress 选择算符客户端提供版本化的配置选项。

字段描述
apiVersion
string
apiserver.k8s.io/v1alpha1
kind
string
EgressSelectorConfiguration
egressSelections [必需]
[]EgressSelection

connectionServices 包含一组 Egress 选择算符客户端配置选项。

TracingConfiguration

TracingConfiguration 为跟踪客户端提供版本化的配置信息。

字段描述
apiVersion
string
apiserver.k8s.io/v1alpha1
kind
string
TracingConfiguration
TracingConfiguration [必需]
TracingConfiguration
TracingConfiguration 的成员嵌入到这种类型中。)

嵌入组件配置中的跟踪配置结构体。

AdmissionPluginConfiguration

出现在:

AdmissionPluginConfiguration 为某个插件提供配置信息。

字段描述
name [必需]
string

name 是准入控制器的名称。此名称必须与所注册的准入插件名称匹配。

path
string

path 为指向包含插件配置数据的配置文件的路径。

configuration
k8s.io/apimachinery/pkg/runtime.Unknown

configuration 是一个嵌入的配置对象,用作插件的配置数据来源。 如果设置了此字段,则使用此字段而不是指向配置文件的路径。

Connection

出现在:

Connection 提供某个 Egress 选择客户端的配置信息。

字段描述
proxyProtocol [必需]
ProtocolType

proxyProtocol 是客户端连接到 konnectivity 服务器所使用的协议。

transport
Transport

transport 定义的是传输层的配置。我们使用这个配置来联系 konnectivity 服务器。 当 proxyProtocol 是 HTTPConnect 或 GRPC 时需要设置此字段。

EgressSelection

出现在:

EgressSelection 为某个 Egress 选择客户端提供配置信息。

字段描述
name [必需]
string

name 是 Egress 选择器的名称。当前支持的取值有 "controlplane", "master","etcd" 和 "cluster"。 "master" Egress 选择器已被弃用,推荐使用 "controlplane"。

connection [必需]
Connection

connection 是用来配置 Egress 选择器的配置信息。

ProtocolType

string 类型的别名)

出现在:

ProtocolType 是 connection.protocolType 的合法值集合。

TCPTransport

出现在:

TCPTransport 提供使用 TCP 连接 konnectivity 服务器时需要的信息。

字段描述
url [必需]
string

url 是要连接的 konnectivity 服务器的位置。例如 "https://127.0.0.1:8131"。

tlsConfig
TLSConfig

tlsConfig 是使用 TLS 来连接 konnectivity 服务器时需要的信息。

TLSConfig

出现在:

TLSConfig 为连接 konnectivity 服务器提供身份认证信息。仅用于 TCPTransport。

字段描述
caBundle
string

caBundle 是指向用来确定与 konnectivity 服务器间信任欢喜的 CA 证书包的文件位置。 当 tcpTransport.url 前缀为 "http://" 时必须不设置,或者设置为空。 如果 tcpTransport.url 前缀为 "https://" 并且此字段未设置,则默认使用系统的信任根。

clientKey
string

clientKey 是与 konnectivity 服务器进行 mtls 握手时使用的客户端秘钥文件位置。 如果 `tcp.url` 前缀为 http://,必须不指定或者为空; 如果 `tcp.url` 前缀为 https://,必须设置。

clientCert
string

clientCert 是与 konnectivity 服务器进行 mtls 握手时使用的客户端证书文件位置。 如果 `tcp.url` 前缀为 http://,必须不指定或者为空; 如果 `tcp.url` 前缀为 https://,必须设置。

Transport

出现在:

Transport 定义联系 konnectivity 服务器时要使用的传输层配置。

字段描述
tcp
TCPTransport

tcp 包含通过 TCP 与 konnectivity 服务器通信时使用的 TCP 配置。 目前使用 TCP 传输时不支持 GRPC 的 proxyProtocoltcpuds 二者至少设置一个。

uds
UDSTransport

uds 包含通过 UDS 与 konnectivity 服务器通信时使用的 UDS 配置。 tcpuds 二者至少设置一个。

UDSTransport

出现在:

UDSTransport 设置通过 UDS 连接 konnectivity 服务器时需要的信息。

字段描述
udsName [必需]
string

udsName 是与 konnectivity 服务器连接时使用的 UNIX 域套接字名称。 字段取值不要求包含 unix:// 前缀。 (例如:/etc/srv/kubernetes/konnectivity-server/konnectivity-server.socket

6.14.8 - kube-apiserver 配置 (v1beta1)

v1beta1 包是 v1beta1 版本的 API。

资源类型

EgressSelectorConfiguration

EgressSelectorConfiguration 为 Egress 选择算符客户端提供版本化的配置选项。

字段描述
apiVersion
string
apiserver.k8s.io/v1beta1
kind
string
EgressSelectorConfiguration
egressSelections [必需]
[]EgressSelection

connectionServices 包含一组 Egress 选择算符客户端配置选项。

TracingConfiguration

出现在:

TracingConfiguration 为 OpenTelemetry 跟踪客户端提供版本化的配置。

字段描述
endpoint
string

采集器的端点,此组件将向其报告跟踪信息。 连接不安全,目前不支持 TLS。 推荐不设置,端点为 otlp grpc 默认值 localhost:4317。

samplingRatePerMillion
int32

samplingRatePerMillion 是每百万 span 中采集的样本数。 推荐不设置。如果不设置,采集器将继承其父级 span 的采样率,否则不进行采样。

TracingConfiguration

TracingConfiguration 为跟踪客户端提供版本化的配置信息。

字段描述
apiVersion
string
apiserver.k8s.io/v1beta1
kind
string
TracingConfiguration
TracingConfiguration [必需]
TracingConfiguration
TracingConfiguration 的成员嵌入到这种类型中。)

嵌入组件配置中的跟踪配置结构体。

Connection

出现在:

Connection 提供某个 Egress 选择客户端的配置信息。

字段描述
proxyProtocol [必需]
ProtocolType

proxyProtocol 是客户端连接到 konnectivity 服务器所使用的协议。

transport
Transport

transport 定义的是传输层的配置。我们使用这个配置来联系 konnectivity 服务器。 当 proxyProtocol 是 HTTPConnect 或 GRPC 时需要设置此字段。

EgressSelection

出现在:

EgressSelection 为某个 Egress 选择客户端提供配置信息。

字段描述
name [必需]
string

name 是 Egress 选择算符的名称。当前支持的取值有 "controlplane", "master","etcd" 和 "cluster"。 "master" Egress 选择算符已被弃用,推荐使用 "controlplane"。

connection [必需]
Connection

connection 是用来配置 Egress 选择算符的配置信息。

ProtocolType

string 类型的别名)

出现在:

ProtocolType 是 connection.protocolType 的合法值集合。

TCPTransport

出现在:

TCPTransport 提供使用 TCP 连接 konnectivity 服务器时需要的信息。

字段描述
url [必需]
string

url 是要连接的 konnectivity 服务器的位置。例如 "https://127.0.0.1:8131"。

tlsConfig
TLSConfig

tlsConfig 是使用 TLS 来连接 konnectivity 服务器时需要的信息。

TLSConfig

出现在:

TLSConfig 为连接 konnectivity 服务器提供身份认证信息。仅用于 TCPTransport。

字段描述
caBundle
string

caBundle 是指向用来确定与 konnectivity 服务器间信任关系的 CA 证书包的文件位置。 如果 TCPTransport.URL 前缀为 "http://" 时必须不设置,或者设置为空。 如果 TCPTransport.URL 前缀为 "https://" 并且此字段未设置,则默认使用系统的信任根。

clientKey
string

clientKey 是与 konnectivity 服务器进行 mtls 握手时使用的客户端秘钥文件位置。 如果 TCPTransport.URL 前缀为 http://,必须不指定或者为空; 如果 TCPTransport.URL 前缀为 https://,必须设置。

clientCert
string

clientCert 是与 konnectivity 服务器进行 mtls 握手时使用的客户端证书文件位置。 如果 TCPTransport.URL 前缀为 http://,必须不指定或者为空; 如果 TCPTransport.URL 前缀为 https://,必须设置。

Transport

出现在:

Transport 定义联系 konnectivity 服务器时要使用的传输层配置。

字段描述
tcp
TCPTransport

tcp 包含通过 TCP 与 konnectivity 服务器通信时使用的 TCP 配置。 目前使用 TCP 传输时不支持 GRPC 的 proxyProtocol。 tcp 和 uds 二者至少设置一个。

uds
UDSTransport

uds 包含通过 UDS 与 konnectivity 服务器通信时使用的 UDS 配置。 tcp 和 uds 二者至少设置一个。

UDSTransport

出现在:

UDSTransport 设置通过 UDS 连接 konnectivity 服务器时需要的信息。

字段描述
udsName [必需]
string

udsName 是与 konnectivity 服务器连接时使用的 UNIX 域套接字名称。 字段取值不要求包含 unix:// 前缀。 (例如:/etc/srv/kubernetes/konnectivity-server/konnectivity-server.socket)

6.14.9 - kube-controller-manager Configuration (v1alpha1)

资源类型

NodeControllerConfiguration

出现在:

NodeControllerConfiguration 包含描述 NodeController 的元素。

字段描述
ConcurrentNodeSyncs [必需]
int32

ConcurrentNodeSyncs 是并发执行以进行节点同步的工作程序的数量。

ServiceControllerConfiguration

出现在:

ServiceControllerConfiguration 包含描述 ServiceController 的元素。

字段描述
ConcurrentServiceSyncs [必需]
int32

concurrentServiceSyncs 是允许同时同步的服务数。 数量越大表示服务管理响应越快,但 CPU(和网络)负载也越高。

CloudControllerManagerConfiguration

CloudControllerManagerConfiguration 包含描述云控制器管理器的元素。

字段描述
apiVersion
string
cloudcontrollermanager.config.k8s.io/v1alpha1
kind
string
CloudControllerManagerConfiguration
Generic [必需]
GenericControllerManagerConfiguration

Generic 包含通用控制器管理器的配置。

KubeCloudShared [必需]
KubeCloudSharedConfiguration

KubeCloudSharedConfiguration 保存被云控制器管理器和 kube-controller 管理器共享的相关特性的配置。

NodeController [必需]
NodeControllerConfiguration

NodeController 保存与节点控制器相关的特性的配置。

ServiceController [必需]
ServiceControllerConfiguration

ServiceControllerConfiguration 保存 ServiceController 相关的特性的配置。

NodeStatusUpdateFrequency [必需]
meta/v1.Duration

NodeStatusUpdateFrequency 是控制器更新节点状态的频率。

Webhook [必需]
WebhookConfiguration

Webhook 是云控制器管理器托管的 webhook 的配置。

CloudProviderConfiguration

出现在:

CloudProviderConfiguration 包含有关云提供商的一些基本元素。

字段描述
Name [必需]
string

Name 是云服务的提供商。

CloudConfigFile [必需]
string

cloudConfigFile 是云提供程序配置文件的路径。

KubeCloudSharedConfiguration

出现在:

KubeCloudSharedConfiguration 包含 kube-controller 管理器和云控制器管理器共享的元素,但不包含通用配置。

字段描述
CloudProvider [必需]
CloudProviderConfiguration

CloudProviderConfiguration 保存 CloudProvider 相关特性的配置。

ExternalCloudVolumePlugin [必需]
string

当 cloudProvider 为 "external" 时,externalCloudVolumePlugin 用于指定插件。 它目前被仓库内的云驱动用于处理 KCM 中的节点和卷控制。

UseServiceAccountCredentials [必需]
bool

useServiceAccountCredentials 指出控制器是否应使用独立的服务帐户凭据运行。

AllowUntaggedCloud [必需]
bool

使用未标记的云实例运行。

RouteReconciliationPeriod [必需]
meta/v1.Duration

routeReconciliationPeriod 是云驱动商为节点创建的路由的调和周期。

NodeMonitorPeriod [必需]
meta/v1.Duration

nodeMonitorPeriod 是 NodeController 同步 NodeStatus 的周期。

ClusterName [必需]
string

clusterName 是集群的实例前缀。

ClusterCIDR [必需]
string

clusterCIDR 是集群中 Pod CIDR 的范围。

AllocateNodeCIDRs [必需]
bool

AllocateNodeCIDRs 允许为 Pod 分配 CIDR, 如果 ConfigureCloudRoutes 为 true,则允许在对云驱动商设置 CIDR。

CIDRAllocatorType [必需]
string

CIDRAllocatorType 决定使用哪种类型的 Pod CIDR 分配器。

ConfigureCloudRoutes [必需]
bool

configureCloudRoutes 使通过 allocateNodeCIDRs 分配的 CIDR 能够在云提供商上配置。

NodeSyncPeriod [必需]
meta/v1.Duration

nodeSyncPeriod 从云平台同步节点的周期。 周期较长时,调用云平台的次数减少, 但向集群添加新节点可能会延迟。

WebhookConfiguration

出现在:

WebhookConfiguration 包含与云控制器管理器托管的 webhook 相关的配置。

字段描述
Webhooks [必需]
[]string

Webhooks 是要启用或者禁用的 Webhook 的列表。 '*' 表示"所有默认启用的 webhook ", 'foo' 表示"启用 'foo'", '-foo' 表示"禁用 'foo'", 特定名称的首个项有效。

LeaderMigrationConfiguration

出现在:

LeaderMigrationConfiguration 为所有迁移中的领导者锁提供了版本化配置。

字段描述
apiVersion
string
controllermanager.config.k8s.io/v1alpha1
kind
string
LeaderMigrationConfiguration
leaderName [必需]
string

LeaderName 是保护迁移的领导者选举资源的名称,例如:1-20-KCM-to-1-21-CCM。

resourceLock [必需]
string

ResourceLock 表示将被用于加锁的资源对象类型, 应该是 "leases" 或者是 "endpoints"。

controllerLeaders [必需]
[]ControllerLeaderConfiguration

ControllerLeaders 包含迁移领导者锁配置列表。

ControllerLeaderConfiguration

出现在:

ControllerLeaderConfiguration 提供迁移中领导者锁的配置。

字段描述
name [必需]
string

Name 是正被迁移的控制器的名称,例如:service-controller、route-controller、cloud-node-controller 等等

component [必需]
string

Component 是控制器运行所处的组件的名称。 例如,kube-controller-manager、cloud-controller-manager 等。 或者 “*” 表示控制器可以在任何正在参与迁移的组件中运行。

GenericControllerManagerConfiguration

出现在:

GenericControllerManagerConfiguration 保存通用控制器管理器的配置。

字段描述
Port [必需]
int32

port 是控制器管理器运行 HTTP 服务运行的端口。

Address [必需]
string

address 是提供服务所用的 IP 地址(所有接口设置为 0.0.0.0)。

MinResyncPeriod [必需]
meta/v1.Duration

minResyncPeriod 是反射器的重新同步周期;大小是在 minResyncPeriod 和 2*minResyncPeriod 范围内的随机数。

ClientConnection [必需]
ClientConnectionConfiguration

ClientConnection 指定代理服务器在与 API 服务器通信时使用的 kubeconfig 文件和客户端连接设置。

ControllerStartInterval [必需]
meta/v1.Duration

两次启动控制器管理器之间的间隔时间。

LeaderElection [必需]
LeaderElectionConfiguration

leaderElection 定义领导者选举客户端的配置。

Controllers [必需]
[]string

Controllers 是要启用或者禁用的控制器列表。 '*' 表示"所有默认启用的控制器", 'foo' 表示"启用 'foo'", '-foo' 表示"禁用 'foo'", 特定名称的首个项有效。

Debugging [必需]
DebuggingConfiguration

DebuggingConfiguration 保存调试相关特性的配置。

LeaderMigrationEnabled [必需]
bool

LeaderMigrationEnabled 指示是否应为控制器管理器启用领导者迁移(Leader Migration)。

LeaderMigration [必需]
LeaderMigrationConfiguration

LeaderMigration 保存领导者迁移的配置。

KubeControllerManagerConfiguration

KubeControllerManagerConfiguration 包含描述 kube-controller 管理器的元素。

字段描述
apiVersion
string
kubecontrollermanager.config.k8s.io/v1alpha1
kind
string
KubeControllerManagerConfiguration
Generic [必需]
GenericControllerManagerConfiguration

Generic 保存通用控制器管理器的配置。

KubeCloudShared [必需]
KubeCloudSharedConfiguration

KubeCloudSharedConfiguration 保存云控制器管理器和 kube-controller 管理器间共享的相关特性的配置。

AttachDetachController [必需]
AttachDetachControllerConfiguration

AttachDetachControllerConfiguration 包含 AttachDetachController 相关特性的配置。

CSRSigningController [必需]
CSRSigningControllerConfiguration

CSRSigningControllerConfiguration 包含 CSRSigningController 相关特性的配置。

DaemonSetController [必需]
DaemonSetControllerConfiguration

DaemonSetControllerConfiguration 包含 DaemonSetController 相关特性的配置。

DeploymentController [必需]
DeploymentControllerConfiguration

DeploymentControllerConfiguration 包含 DeploymentController 相关特性的配置。

StatefulSetController [必需]
StatefulSetControllerConfiguration

StatefulSetControllerConfiguration 包含 StatefulSetController 相关特性的配置。

DeprecatedController [必需]
DeprecatedControllerConfiguration

DeprecatedControllerConfiguration 包含一些已弃用的特性的配置。

EndpointController [必需]
EndpointControllerConfiguration

EndpointControllerConfiguration 包含 EndpointController 相关特性的配置。

EndpointSliceController [必需]
EndpointSliceControllerConfiguration

EndpointSliceControllerConfiguration 包含 EndpointSliceController 相关特性的配置。

EndpointSliceMirroringController [必需]
EndpointSliceMirroringControllerConfiguration

EndpointSliceMirroringControllerConfiguration 包含 EndpointSliceMirroringController 相关特性的配置。

EphemeralVolumeController [必需]
EphemeralVolumeControllerConfiguration

EphemeralVolumeControllerConfiguration 包含 EphemeralVolumeController 相关特性的配置。

GarbageCollectorController [必需]
GarbageCollectorControllerConfiguration

GarbageCollectorControllerConfiguration 包含 GarbageCollectorController 相关特性的配置。

HPAController [必需]
HPAControllerConfiguration

HPAControllerConfiguration 包含 HPAController 相关特性的配置。

JobController [必需]
JobControllerConfiguration

HPAControllerConfiguration 包含 JobController 相关特性的配置。

CronJobController [必需]
CronJobControllerConfiguration

CronJobControllerConfiguration 包含 CronJobController 相关特性的配置。

LegacySATokenCleaner [必需]
LegacySATokenCleanerConfiguration

LegacySATokenCleanerConfiguration 包含 LegacySATokenCleaner 相关特性的配置。

NamespaceController [必需]
NamespaceControllerConfiguration

NamespaceControllerConfiguration 包含 NamespaceController 相关特性的配置。

NodeIPAMController [必需]
NodeIPAMControllerConfiguration

NodeIPAMControllerConfiguration 包含 NodeIPAMController 相关特性的配置。

NodeLifecycleController [必需]
NodeLifecycleControllerConfiguration

NodeLifecycleControllerConfiguration 包含 NodeLifecycleController 相关特性的配置。

PersistentVolumeBinderController [必需]
PersistentVolumeBinderControllerConfiguration

PersistentVolumeBinderControllerConfiguration 包含 PersistentVolumeBinderController 相关特性的配置。

PodGCController [必需]
PodGCControllerConfiguration

PodGCControllerConfiguration 包含 PodGCController 相关特性的配置。

ReplicaSetController [必需]
ReplicaSetControllerConfiguration

ReplicaSetControllerConfiguration 包含 ReplicaSetController 相关特性的配置。

ReplicationController [必需]
ReplicationControllerConfiguration

ReplicationControllerConfiguration 包含 ReplicationController 相关特性的配置。

ResourceQuotaController [必需]
ResourceQuotaControllerConfiguration

ResourceQuotaControllerConfiguration 包含 ResourceQuotaController 相关特性的配置。

SAController [必需]
SAControllerConfiguration

SAControllerConfiguration 包含 ServiceAccountController 相关特性的配置。

ServiceController [必需]
ServiceControllerConfiguration

ServiceControllerConfiguration 包含 ServiceController 相关特性的配置。

TTLAfterFinishedController [必需]
TTLAfterFinishedControllerConfiguration

TTLAfterFinishedControllerConfiguration 包含 TTLAfterFinishedController 相关特性的配置。

ValidatingAdmissionPolicyStatusController [必需]
ValidatingAdmissionPolicyStatusControllerConfiguration

ValidatingAdmissionPolicyStatusControllerConfiguration 包含 ValidatingAdmissionPolicyStatusController 相关特性的配置。

AttachDetachControllerConfiguration

出现在:

AttachDetachControllerConfiguration 包含描述 AttachDetachController 的元素。

字段描述
DisableAttachDetachReconcilerSync [必需]
bool

Reconciler 运行一个周期性循环,通过触发 attach/detach 操作来协调期望状态与实际状态。 此标志启用或禁用调和操作。默认为 false,即被启用的。

ReconcilerSyncLoopPeriod [必需]
meta/v1.Duration

ReconcilerSyncLoopPeriod 是调和器在连续执行同步状态的循环间,所等待的时间量。 默认为 5 秒。

CSRSigningConfiguration

出现在:

CSRSigningConfiguration 保存有关特定 CSR 签名者的信息。

字段描述
CertFile [必需]
string

certFile 是包含 PEM 编码的 X509 CA 证书的文件名,用于颁发证书。

KeyFile [必需]
string

keyFile 是包含 PEM 编码的 RSA 或 ECDSA 私钥的文件名,用于颁发证书。

CSRSigningControllerConfiguration

出现在:

CSRSigningControllerConfiguration 包含描述 CSRSigningController 的元素。

字段描述
ClusterSigningCertFile [必需]
string

clusterSigningCertFile 是包含 PEM 编码的 X509 CA 证书的文件名,该证书用于颁发集群范围的证书。

ClusterSigningKeyFile [必需]
string

clusterSigningCertFile 是包含 PEM 编码的 RSA 或 ECDSA 私钥的文件名,用于颁发集群范围的证书。

KubeletServingSignerConfiguration [必需]
CSRSigningConfiguration

kubeletServingSignerConfiguration 保存用于为 kubernetes.io/kubelet-serving 签名者颁发证书的证书和密钥。

KubeletClientSignerConfiguration [必需]
CSRSigningConfiguration

kubeletClientSignerConfiguration 保存用于为 kubernetes.io/kube-apiserver-client-kubelet 颁发证书的证书和密钥。

KubeAPIServerClientSignerConfiguration [必需]
CSRSigningConfiguration

kubeAPIServerClientSignerConfiguration 保存用于为 kubernetes.io/kube-apiserver-client 颁发证书的证书和密钥。

LegacyUnknownSignerConfiguration [必需]
CSRSigningConfiguration

legacyUnknownSignerConfiguration 保存用于颁发 kubernetes.io/legacy-unknown 证书的证书和密钥。

ClusterSigningDuration [必需]
meta/v1.Duration

clusterSigningDuration 是签名证书的最长持续时间。 单个 CSRs 可以通过设置 spec.expirationSeconds 来请求有效期更短的证书。

CronJobControllerConfiguration

出现在:

CronJobControllerConfiguration 包含描述 CrongJob2Controller 的元素。

字段描述
ConcurrentCronJobSyncs [必需]
int32

concurrentCronJobSyncs 是允许并发同步的 Job 对象的数量。 数量越大意味着 Job 响应越快,但 CPU(和网络)负载也越高。

DaemonSetControllerConfiguration

出现在:

DaemonSetControllerConfiguration 包含描述 DaemonSetController 的元素。

字段描述
ConcurrentDaemonSetSyncs [必需]
int32

concurrentDaemonSetSyncs 是允许并发同步的 DaemonSet 对象的数量。 数目越大意味着 DaemonSet 响应越快,但 CPU(和网络)负载也越高。

DeploymentControllerConfiguration

Appears in:

DeploymentControllerConfiguration 包含描述 DeploymentController 的元素。

字段描述
ConcurrentDeploymentSyncs [必需]
int32

concurrentDeploymentSyncs 是允许并发同步的 Deployment 对象的数量。 数量越大意味着 Deployment 响应更越快,但 CPU(和网络)负载也越高。

DeprecatedControllerConfiguration

出现在:

DeprecatedControllerConfiguration 包含被弃用的元素。

EndpointControllerConfiguration

出现在:

EndpointControllerConfiguration 包含描述 EndpointController 的元素。

字段描述
ConcurrentEndpointSyncs [必需]
int32

concurrentEndpointSyncs 是将并发执行的 Endpoints 同步操作的数量。 数字越大意味着 Endpoints 更新越快,但 CPU(和网络)负载也越高。

EndpointUpdatesBatchPeriod [必需]
meta/v1.Duration

EndpointUpdatesBatchPeriod 描述批量更新 Endpoints 的周期长度。 Pod 更改的处理将被延迟相同的时长,以便将它们与即将到来的更新连接起来,并减少 Endpoints 更新的总数。

EndpointSliceControllerConfiguration

出现在:

EndpointSliceControllerConfiguration 包含描述 EndpointSliceController 的元素。

字段描述
ConcurrentServiceEndpointSyncs [必需]
int32

concurrentServiceEndpointSyncs 是将并发完成的服务端点同步操作的数量。 数字越大意味着 EndpointSlice 更新越快,但 CPU(和网络)负载也越高。

MaxEndpointsPerSlice [必需]
int32

maxEndpointsPerSlice 是将添加到 EndpointSlice 的最大端点数。 每个切片的端点越多,端点切片就会更少、更大,但资源消耗就会更多。

EndpointUpdatesBatchPeriod [必需]
meta/v1.Duration

EndpointUpdatesBatchPeriod 描述批量更新 Endpoints 的周期长度。 Pod 更改的处理将被延迟相同的时长,以便将它们与即将到来的更新连接起来,并减少 Endpoints 更新的总数。

EndpointSliceMirroringControllerConfiguration

出现在:

EndpointSliceMirroringControllerConfiguration 包含描述 EndpointSliceMirroringController 的元素。

字段描述
MirroringConcurrentServiceEndpointSyncs [必需]
int32

mirroringConcurrentServiceEndpointSyncs 是将并发完成的服务端点同步操作的数量。 数字越大意味着 EndpointSlice 更新越快,但 CPU(和网络)负载也越高。

MirroringMaxEndpointsPerSubset [必需]
int32

mirroringMaxEndpointsPerSubset 是把 EndpointSubset 映射到 EndpointSlice 的端点数上限。

MirroringEndpointUpdatesBatchPeriod [必需]
meta/v1.Duration

mirroringEndpointUpdatesBatchPeriod 可用于批量更新 EndpointSlice。 所有由 EndpointSlice 更改触发的更新可能被延迟,延迟的时间长度上限为 “mirroringEndpointUpdatesBatchPeriod”。 如果同一 Endpoints 资源中的其他地址在此期间发生变化,它们将被合并到同一个 EndpointSlice 更新中以实现批处理。 默认值 0 表示 Endpoints 的每次更新都会触发一次 EndpointSlice 更新。

EphemeralVolumeControllerConfiguration

出现在:

EphemeralVolumeControllerConfiguration 包含描述 EphemeralVolumeController 的元素。

字段描述
ConcurrentEphemeralVolumeSyncs [必需]
int32

ConcurrentEphemeralVolumeSyncseSyncs 是并发执行的临时卷同步操作数量。 数字越大意味着临时卷更新越快,但 CPU(和网络)负载也越高。

GarbageCollectorControllerConfiguration

出现在:

GarbageCollectorControllerConfiguration 包含描述 GarbageCollectorController 的元素。

字段描述
EnableGarbageCollector [必需]
bool

启用通用垃圾收集器。必须与 kube-apiserver 的相应标志同步。 警告:通用垃圾收集器是一个 Alpha 特性。

ConcurrentGCSyncs [必需]
int32

concurrentGCSyncs 是允许垃圾收集器并发同步的工作线程的数量。

GCIgnoredResources [必需]
[]GroupResource

gcIgnoredResources 是垃圾收集应该忽略的 GroupResource 列表。

GroupResource

出现在:

GroupResource 描述组资源。

字段描述
Group [必需]
string

group 是 GroupResource 的 group 部分。

Resource [必需]
string

resource 是 GroupResource 的 resource 部分。

HPAControllerConfiguration

出现在:

HPAControllerConfiguration 包含描述 HPAController 的元素。

字段描述
ConcurrentHorizontalPodAutoscalerSyncs [必需]
int32

ConcurrentHorizontalPodAutoscalerSyncs 是允许并发同步的 HPA 对象的数量。 数字越大意味着 HPA 处理响应越快,但 CPU(和网络)负载也越高。

HorizontalPodAutoscalerSyncPeriod [必需]
meta/v1.Duration

HorizontalPodAutoscalerSyncPeriod 是对 HPA 中 Pod 数量进行同步的周期。

HorizontalPodAutoscalerUpscaleForbiddenWindow [必需]
meta/v1.Duration

HorizontalPodAutoscalerUpscaleForbiddenWindow 是一个时间段,过了这一时间段才允许下一次扩容

HorizontalPodAutoscalerDownscaleStabilizationWindow [必需]
meta/v1.Duration

horizontalpodautoscalerdowncalstabilizationwindow 是一个自动缩放器要回顾的时段长度, 在所给时段内,自动缩放器不会按照建议执行任何缩容操作。

HorizontalPodAutoscalerDownscaleForbiddenWindow [必需]
meta/v1.Duration

HorizontalPodAutoscalerDownscaleForbiddenWindow 是一个时间段长度,过了此时间段才允许执行下一此缩容操作。

HorizontalPodAutoscalerTolerance [必需]
float64

HorizontalPodAutoscalerTolerance 是当资源用量表明需要扩容/缩容时的容忍度。

HorizontalPodAutoscalerCPUInitializationPeriod [必需]
meta/v1.Duration

HorizontalPodAutoscalerCPUInitializationPeriod 是 Pod 启动后可以跳过时间段,这段时间内部执行 CPU 采样。

HorizontalPodAutoscalerInitialReadinessDelay [必需]
meta/v1.Duration

HorizontalPodAutoscalerInitialReadinessDelay 是 Pod 启动后的一段时间, 在此期间,readiness 状态的变更被视为初次设置 readiness 状态。 这样做的唯一影响是,对于在此期间发生 readiness 状态变化但未准备好的 Pod,HPA 将忽略其 CPU 采样值。

JobControllerConfiguration

出现在:

JobControllerConfiguration 包含描述 JobController 的元素。

字段描述
ConcurrentJobSyncs [必需]
int32

concurrentJobSyncs 是允许并发同步的 Job 对象的数量。 数字越大意味着 Job 响应越快,但 CPU(和网络)负载也越高。

LegacySATokenCleanerConfiguration

出现在:

LegacySATokenCleanerConfiguration 包含描述 LegacySATokenCleaner 的元素。

字段描述
CleanUpPeriod [必需]
meta/v1.Duration

CleanUpPeriod 是自动生成的服务帐户令牌上次被使用以来的时长,超出此时长的令牌可被清理。

NamespaceControllerConfiguration

出现在:

NamespaceControllerConfiguration 包含描述 NamespaceController 的元素。

字段描述
NamespaceSyncPeriod [必需]
meta/v1.Duration

namespaceSyncPeriod 是对名字空间生命周期更新进行同步的周期。

ConcurrentNamespaceSyncs [必需]
int32

concurrentNamespaceSyncs 是允许并发同步的 Namespace 对象的数量。

NodeIPAMControllerConfiguration

出现在:

NodeIPAMControllerConfiguration 包含描述 NodeIpamController 的元素。

字段描述
ServiceCIDR [必需]
string

serviceCIDR 为集群中 Service 的 CIDR 范围。

SecondaryServiceCIDR [必需]
string

SecondaryServiceCIDR 为集群中 Service 的 CIDR 范围。此字段用于双栈集群。 SecondaryServiceCIDR 和 ServiceCIDR 的 IP 族不能相同。

NodeCIDRMaskSize [必需]
int32

NodeCIDRMaskSize 为集群中节点 CIDR 的掩码大小。

NodeCIDRMaskSizeIPv4 [必需]
int32

NodeCIDRMaskSizeIPv4 为双栈集群中节点 CIDR 的掩码大小。

NodeCIDRMaskSizeIPv6 [必需]
int32

NodeCIDRMaskSizeIPv6 为双栈集群中节点 CIDR 的掩码大小。

NodeLifecycleControllerConfiguration

出现在:

Nodelifecyclecontrolerconfiguration 包含描述 NodeLifecycleController 的元素。

字段描述
NodeEvictionRate [必需]
float32

nodeEvictionRate 是在区域健康时,如果节点发生故障,每秒删除 Pod 的节点数。

SecondaryNodeEvictionRate [必需]
float32

secondaryNodeEvictionRate 是在区域不健康时,如果节点故障,每秒删除 Pod 的节点数。

NodeStartupGracePeriod [必需]
meta/v1.Duration

nodeStartupGracePeriod 是在将节点标记为不健康之前允许启动节点无响应的时长。

NodeMonitorGracePeriod [必需]
meta/v1.Duration

nodeMontiorGracePeriod 是在将运行中的节点标记为不健康之前允许其无响应的时长。 必须是 kubelet 的 nodeStatusUpdateFrequency 的 N 倍,其中 N 表示允许 kubelet 发布节点状态的重试次数。

PodEvictionTimeout [必需]
meta/v1.Duration

podEvictionTimeout 为删除故障节点上的 Pod 的宽限时间。

LargeClusterSizeThreshold [必需]
int32

对于规模小于或等于 largeClusterSizeThreshold 的集群,secondaryNodeEvictionRate 会被隐式覆盖,取值为 0。

UnhealthyZoneThreshold [必需]
float32

当区域中至少有 unhealthyZoneThreshold(不少于 3 个)的节点处于 NotReady 状态时, nodeEvctionRate 和 secondaryNodeEvictionRate 两个属性的判定逻辑会将区域视为不健康。

PersistentVolumeBinderControllerConfiguration

出现在:

PersistentVolumeBinderControllerConfiguration 包含描述 PersistentVolumeBinderController 的元素。

字段描述
PVClaimBinderSyncPeriod [必需]
meta/v1.Duration

pvClaimBinderSyncPeriod 用于同步 PV 和 PVC 的周期。

VolumeConfiguration [必需]
VolumeConfiguration

volumeConfiguration 包含卷相关特性的配置。

VolumeHostCIDRDenylist [必需]
[]string

已弃用:VolumeHostCIDRDenylist 是一个不能被插件中控制器访问的 CIDR 列表。

VolumeHostAllowLocalLoopback [必需]
bool

已弃用:VolumeHostAllowLocalLoopback 表示是否应该允许插件使用本地回路主机地址(127.0.0.1 等)。

PersistentVolumeRecyclerConfiguration

出现在:

PersistentVolumeRecyclerConfiguration 包含描述持久卷插件的元素。

字段描述
MaximumRetry [必需]
int32

maximumRetry 是当 PV 回收失败时,PV 回收器重试的次数。

MinimumTimeoutNFS [必需]
int32

minimumTimeoutNFS 是用于 NFS 回收器的,用于设置 Pod 的最小 ActiveDeadlineSeconds。

PodTemplateFilePathNFS [必需]
string

podTemplateFilePathNFS 是一个 Pod 定义文件的路径,该文件将被用作 NFS PV 卷回收模板。

IncrementTimeoutNFS [必需]
int32

incrementTimeoutNFS 提供给 NFS 清理器 Pod 的设置值,数据卷每增加 1 GiB, 则需要向 Pod 中的 activeDeadlineSeconds 参数增加这里所给的秒数。

PodTemplateFilePathHostPath [必需]
string

podTemplateFilePathHostPath 是一个 Pod 定义文件的路径,该文件将被作为 HostPath PV 卷回收模板。 此字段仅用于开发和测试场景,在多节点集群中无法正常工作。

MinimumTimeoutHostPath [必需]
int32

minimumTimeoutHostPath 是用于 HostPath 回收器 Pod 的 activeDeadlineSeconds 属性值下限。 此字段仅用于开发和测试场景,在多节点集群中无法正常工作。

IncrementTimeoutHostPath [必需]
int32

incrementTimeoutHostPath 是提供给 HostPath 清理器 Pod 的配置值, HostPath 卷的尺寸每增加 1 GiB,则需要为 Pod 的 activeDeadlineSeconds 属性增加这里所给的秒数。 回收器 Pod 的 activeDeadlineSeconds 属性值下限。

PodGCControllerConfiguration

出现在:

PodGCControllerConfiguration 包含描述 PodGCController 的元素。

字段描述
TerminatedPodGCThreshold [必需]
int32

terminatedPodGCThreshold 是提供给回收已终止 Pod 的垃圾收集器的, 所设置的数字是在垃圾收集器开始删除某 Pod 之前可以存在的、已终止 Pod 的个数。 如果 <= 0,则禁用已终止的 Pod 垃圾收集器。

ReplicaSetControllerConfiguration

出现在:

ReplicaSetControllerConfiguration 包含描述 ReplicaSetController 的元素。

字段描述
ConcurrentRSSyncs [必需]
int32

concurrentRSSyncs 是允许并发同步的 ReplicaSet 的数量。 数量越大意味着副本管理响应越快,但 CPU(和网络)负载也越高。

ReplicationControllerConfiguration

出现在:

ReplicationControllerConfiguration 包含描述 ReplicationController 的元素。

字段描述
ConcurrentRCSyncs [必需]
int32

concurrentRCSyncs 是允许并发同步的 ReplicationController 数量。 数量越大意味着副本管理响应越快,但 CPU(和网络)负载也越高。

ResourceQuotaControllerConfiguration

出现在:

ResourceQuotaControllerConfiguration 包含描述 ResourceQuotaController 的元素。

字段描述
ResourceQuotaSyncPeriod [必需]
meta/v1.Duration

resourceQuotaSyncPeriod 是系统中 Quota 使用状态的同步周期。

ConcurrentResourceQuotaSyncs [必需]
int32

concurrentResourceQuotaSyncs 是允许并发同步的 ResourcQuota 数目。 数量越大意味着配额管理响应越快,但 CPU(和网络)负载也越高。

SAControllerConfiguration

出现在:

SAControllerConfiguration 包含描述 ServiceAccountController 的元素。

字段描述
ServiceAccountKeyFile [必需]
string

serviceAccountKeyFile 是包含 PEM 编码的用于签署服务帐户令牌的 RSA 私钥的文件名。

ConcurrentSATokenSyncs [必需]
int32

concurrentSATokenSyncs 是将并发完成的服务帐户令牌同步操作的数量。

RootCAFile [必需]
string

rootCAFile 是根证书颁发机构将被包含在 ServiceAccount 的令牌 Secret 中。 所提供的数据必须是一个有效的 PEM 编码的 CA 包。

StatefulSetControllerConfiguration

出现在:

StatefulSetControllerConfiguration 包含描述 StatefulSetController 的元素。

字段描述
ConcurrentStatefulSetSyncs [必需]
int32

concurrentStatefulSetSyncs 是允许并发同步的 StatefulSet 对象的数量。 数字越大意味着 StatefulSet 响应越快,但 CPU(和网络)负载也越高。

TTLAfterFinishedControllerConfiguration

出现在:

TTLAfterFinishedControllerConfiguration 包含描述 TTLAfterFinishedController 的元素。

字段描述
ConcurrentTTLSyncs [必需]
int32

concurrentTTLSyncs 是允许并发同步的 TTL-after-finished 收集器工作线程的数量。

ValidatingAdmissionPolicyStatusControllerConfiguration

出现在:

ValidatingAdmissionPolicyStatusControllerConfiguration 包含描述 ValidatingAdmissionPolicyStatusController 的元素。

字段描述
ConcurrentPolicySyncs [必需]
int32

ConcurrentPolicySyncs 是允许并发同步的策略对象的数量。 数字越大意味着类型检查越快,但 CPU(和网络)负载越高。 默认值为 5。

VolumeConfiguration

出现在:

VolumeConfiguration 包含所有用于配置各个卷插件的所有参数。 从这个配置中,控制器管理器可执行文件将创建许多 volume.VolumeConfig 的实例。 每个只包含该插件所需的配置,然后将其传递给相应的插件。 控制器管理器可执行文件是代码中唯一知道支持哪些插件以及每个插件对应哪些标志的部分。

字段描述
EnableHostPathProvisioning [必需]
bool

enableHostPathProvisioning 在没有云驱动的情况下允许制备 HostPath PV。 此特性用来测试和开发 PV 卷制备特性。HostPath 配置完全不受支持,在多节点集群中无法工作, 除了测试或开发之外不应该用于任何其他用途。

EnableDynamicProvisioning [必需]
bool

enableDynamicProvisioning 在支持动态配置的环境中运行时允许制备新卷。默认为 true。

PersistentVolumeRecyclerConfiguration [必需]
PersistentVolumeRecyclerConfiguration

persistentVolumeRecyclerConfiguration 保存持久卷插件的配置。

FlexVolumePluginDir [必需]
string

volumePluginDir 是一个完整路径,FlexVolume 插件在这一目录中搜索额外的第三方卷插件。

6.14.10 - kube-proxy 配置 (v1alpha1)

资源类型

ClientConnectionConfiguration

出现在:

ClientConnectionConfiguration 包含构造客户端所需要的细节信息。

字段描述
kubeconfig [必需]
string

kubeconfig 字段是指向一个 KubeConfig 文件的路径。

acceptContentTypes [必需]
string

acceptContentTypes 字段定义客户端在连接到服务器时所发送的 Accept 头部字段。 此设置值会覆盖默认配置 'application/json'。 此字段会控制某特定客户端与指定服务器的所有链接。

contentType [必需]
string

contentType 字段是从此客户端向服务器发送数据时使用的内容类型(Content Type)。

qps [必需]
float32

qps 字段控制此连接上每秒钟可以发送的查询请求个数。

burst [必需]
int32

burst 字段允许客户端超出其速率限制时可以临时累积的额外查询个数。

DebuggingConfiguration

出现在:

DebuggingConfiguration 包含调试相关功能的配置。

字段描述
enableProfiling [Required]
bool

enableProfiling 字段通过位于 host:port/debug/pprof/ 的 Web 接口启用性能分析。

enableContentionProfiling [Required]
bool

enableContentionProfiling 字段在 enableProfiling 为 true 时启用阻塞分析。

LeaderElectionConfiguration

出现在:

LeaderElectionConfiguration 为能够支持领导者选举的组件定义其领导者选举客户端的配置。

字段描述
leaderElect [必需]
bool

leaderElect 字段允许领导者选举客户端在进入主循环执行之前先获得领导者角色。 运行多副本组件时启用此功能有助于提高可用性。

leaseDuration [必需]
meta/v1.Duration

leaseDuration 字段是非领导角色候选者在观察到需要领导席位更新时要等待的时间; 只有经过所设置时长才可以尝试去获得一个仍处于领导状态但需要被刷新的席位。 这里的设置值本质上意味着某个领导者在被另一个候选者替换掉之前可以停止运行的最长时长。 只有当启用了领导者选举时此字段有意义。

renewDeadline [必需]
meta/v1.Duration

renewDeadline 字段设置的是当前领导者在停止扮演领导角色之前需要刷新领导状态的时间间隔。 此值必须小于或等于租约期限的长度。只有到启用了领导者选举时此字段才有意义。

retryPeriod [必需]
meta/v1.Duration

retryPeriod 字段是客户端在连续两次尝试获得或者刷新领导状态之间需要等待的时长。 只有当启用了领导者选举时此字段才有意义。

resourceLock [必需]
string

resourceLock 字段给出在领导者选举期间要作为锁来使用的资源对象类型。

resourceName [必需]
string

resourceName 字段给出在领导者选举期间要作为锁来使用的资源对象名称。

resourceNamespace [必需]
string

resourceNamespace 字段给出在领导者选举期间要作为锁来使用的资源对象所在名字空间。

KubeProxyConfiguration

KubeProxyConfiguration 包含用来配置 Kubernetes 代理服务器的所有配置信息。

字段描述
apiVersion
string
kubeproxy.config.k8s.io/v1alpha1
kind
string
KubeProxyConfiguration
featureGates [必需]
map[string]bool

featureGates 字段是一个功能特性名称到布尔值的映射表, 用来启用或者禁用测试性质的功能特性。

bindAddress [必需]
string

bindAddress 字段是代理服务器提供服务时所用 IP 地址(设置为 0.0.0.0 时意味着在所有网络接口上提供服务)。

healthzBindAddress [必需]
string

healthzBindAddress 字段是健康状态检查服务器提供服务时所使用的 IP 地址和端口, 默认设置为 '0.0.0.0:10256'。

metricsBindAddress [必需]
string

metricsBindAddress 字段是指标服务器提供服务时所使用的 IP 地址和端口, 默认设置为 '127.0.0.1:10249'(设置为 0.0.0.0 意味着在所有接口上提供服务)。

bindAddressHardFail [必需]
bool

bindAddressHardFail 字段设置为 true 时, kube-proxy 将无法绑定到某端口这类问题视为致命错误并直接退出。

enableProfiling [必需]
bool

enableProfiling 字段通过 '/debug/pprof' 处理程序在 Web 界面上启用性能分析。 性能分析处理程序将由指标服务器执行。

clusterCIDR [必需]
string

clusterCIDR 字段是集群中 Pod 所使用的 CIDR 范围。 这一地址范围用于对来自集群外的请求流量进行桥接。 如果未设置,则 kube-proxy 不会对非集群内部的流量做桥接。

hostnameOverride [必需]
string

hostnameOverride 字段非空时, 所给的字符串(而不是实际的主机名)将被用作 kube-proxy 的标识。

clientConnection [必需]
ClientConnectionConfiguration

clientConnection 字段给出代理服务器与 API 服务器通信时要使用的 kubeconfig 文件和客户端链接设置。

iptables [必需]
KubeProxyIPTablesConfiguration

iptables 字段字段包含与 iptables 相关的配置选项。

ipvs [必需]
KubeProxyIPVSConfiguration

ipvs 字段中包含与 ipvs 相关的配置选项。

oomScoreAdj [必需]
int32

oomScoreAdj 字段是为 kube-proxy 进程所设置的 oom-score-adj 值。 此设置值必须介于 [-1000, 1000] 范围内。

mode [必需]
ProxyMode

mode 字段用来设置将使用的代理模式。

portRange [必需]
string

portRange 字段是主机端口的范围,形式为 ‘beginPort-endPort’(包含边界), 用来设置代理服务所使用的端口。如果未指定(即 ‘0-0’),则代理服务会随机选择端口号。

conntrack [必需]
KubeProxyConntrackConfiguration

conntrack 字段包含与 conntrack 相关的配置选项。

configSyncPeriod [必需]
meta/v1.Duration

configSyncPeriod 字段是从 API 服务器刷新配置的频率。此值必须大于 0。

nodePortAddresses [必需]
[]string

nodePortAddresses 字段是 kube-proxy 进程的 --nodeport-addresses 命令行参数设置。 此值必须是合法的 IP 段。所给的 IP 段会作为参数来选择 NodePort 类型服务所使用的接口。 如果有人希望将本地主机(Localhost)上的服务暴露给本地访问, 同时暴露在某些其他网络接口上以实现某种目标,可以使用 IP 段的列表。 如果此值被设置为 "127.0.0.0/8",则 kube-proxy 将仅为 NodePort 服务选择本地回路(loopback)接口。 如果此值被设置为非零的 IP 段,则 kube-proxy 会对 IP 作过滤,仅使用适用于当前节点的 IP 地址。 空的字符串列表意味着选择所有网络接口。

winkernel [必需]
KubeProxyWinkernelConfiguration

winkernel 字段包含与 winkernel 相关的配置选项。

showHiddenMetricsForVersion [必需]
string

showHiddenMetricsForVersion 字段给出的是一个 Kubernetes 版本号字符串, 用来设置你希望显示隐藏指标的版本。

detectLocalMode [必需]
LocalMode

detectLocalMode 字段用来确定检测本地流量的方式,默认为 LocalModeClusterCIDR。

detectLocal [必需]
DetectLocalConfiguration

detectLocal 字段包含与 DetectLocalMode 相关的可选配置设置。

logging [必需]
LoggingConfiguration

logging 字段指定记录日志的选项。更多细节参阅 Logs Options

DetectLocalConfiguration

出现在:

DetectLocalConfiguration 包含与 DetectLocalMode 选项相关的可选设置。

字段描述
bridgeInterface [必需]
string

bridgeInterface 字段是一个表示单个桥接接口名称的字符串参数。 Kube-proxy 将来自这个给定桥接接口的流量视为本地流量。 如果 DetectLocalMode 设置为 LocalModeBridgeInterface,则应设置该参数。

interfaceNamePrefix [必需]
string

interfaceNamePrefix 字段是一个表示单个接口前缀名称的字符串参数。 Kube-proxy 将来自一个或多个与给定前缀匹配的接口流量视为本地流量。 如果 DetectLocalMode 设置为 LocalModeInterfaceNamePrefix,则应设置该参数。

KubeProxyConntrackConfiguration

出现在:

KubeProxyConntrackConfiguration 包含为 Kubernetes 代理服务器提供的 conntrack 设置。

字段描述
maxPerCore [必需]
int32

maxPerCore 字段是每个 CPU 核所跟踪的 NAT 链接个数上限 (0 意味着保留当前上限限制并忽略 min 字段设置值)。

min [必需]
int32

min 字段给出要分配的链接跟踪记录个数下限。 设置此值时会忽略 maxPerCore 的值(将 maxPerCore 设置为 0 时不会调整上限值)。

tcpEstablishedTimeout [必需]
meta/v1.Duration

tcpEstablishedTimeout 字段给出空闲 TCP 连接的保留时间(例如,'2s')。 此值必须大于 0。

tcpCloseWaitTimeout [必需]
meta/v1.Duration

tcpCloseWaitTimeout 字段用来设置空闲的、处于 CLOSE_WAIT 状态的 conntrack 条目 保留在 conntrack 表中的时间长度(例如,'60s')。 此设置值必须大于 0。

KubeProxyIPTablesConfiguration

出现在:

KubeProxyIPTablesConfiguration 包含用于 Kubernetes 代理服务器的、与 iptables 相关的配置细节。

字段描述
masqueradeBit [必需]
int32

masqueradeBit 字段是 iptables fwmark 空间中的具体一位, 用来在纯 iptables 代理模式下设置 SNAT。此值必须介于 [0, 31](含边界值)。

masqueradeAll [必需]
bool

masqueradeAll 字段用来通知 kube-proxy 在使用纯 iptables 代理模式时对所有流量执行 SNAT 操作。

localhostNodePorts [必需]
bool

localhostNodePorts 告知 kube-proxy 允许通过 localhost 访问服务 NodePorts(仅 iptables 模式)

syncPeriod [必需]
meta/v1.Duration

syncPeriod 字段给出 iptables 规则的刷新周期(例如,'5s'、'1m'、'2h22m')。此值必须大于 0。

minSyncPeriod [必需]
meta/v1.Duration

minSyncPeriod 字段给出 iptables 规则被刷新的最小周期(例如,'5s'、'1m'、'2h22m')。

KubeProxyIPVSConfiguration

出现在:

KubeProxyIPVSConfiguration 包含用于 Kubernetes 代理服务器的、与 ipvs 相关的配置细节。

字段描述
syncPeriod [必需]
meta/v1.Duration

syncPeriod 字段给出 ipvs 规则的刷新周期(例如,'5s'、'1m'、'2h22m')。 此值必须大于 0。

minSyncPeriod [必需]
meta/v1.Duration

minSyncPeriod 字段给出 ipvs 规则被刷新的最小周期(例如,'5s'、'1m'、'2h22m')。

scheduler [必需]
string

IPVS 调度器。

excludeCIDRs [必需]
[]string

excludeCIDRs 字段取值为一个 CIDR 列表,ipvs 代理程序在清理 IPVS 服务时不应触碰这些 IP 地址。

strictARP [必需]
bool

strictARP 字段用来配置 arp_ignore 和 arp_announce,以避免(错误地)响应来自 kube-ipvs0 接口的 ARP 查询请求。

tcpTimeout [必需]
meta/v1.Duration

tcpTimeout 字段是用于设置空闲 IPVS TCP 会话的超时值。 默认值为 0,意味着使用系统上当前的超时值设置。

tcpFinTimeout [必需]
meta/v1.Duration

tcpFinTimeout 字段用来设置 IPVS TCP 会话在收到 FIN 之后的超时值。 默认值为 0,意味着使用系统上当前的超时值设置。

udpTimeout [必需]
meta/v1.Duration

udpTimeout 字段用来设置 IPVS UDP 包的超时值。 默认值为 0,意味着使用系统上当前的超时值设置。

KubeProxyWinkernelConfiguration

出现在:

KubeProxyWinkernelConfiguration 包含 Kubernetes 代理服务器的 Windows/HNS 设置。

字段描述
networkName [必需]
string

networkName 字段是 kube-proxy 用来创建端点和策略的网络名称。

sourceVip [必需]
string

sourceVip 字段是执行负载均衡时进行 NAT 转换所使用的源端 VIP 端点 IP 地址。

enableDSR [必需]
bool

enableDSR 字段通知 kube-proxy 是否使用 DSR 来创建 HNS 策略。

rootHnsEndpointName [必需]
string

rootHnsEndpointName 字段是附加到用于根网络命名空间二层桥接的 hnsendpoint 的名称。

forwardHealthCheckVip [必需]
bool

forwardHealthCheckVip 字段为 Windows 上的健康检查端口转发服务 VIP。

LocalMode

string 类型的别名)

出现在:

LocalMode 代表的是对节点上本地流量进行检测的模式。

ProxyMode

string 类型的别名)

出现在:

ProxyMode 表示的是 Kubernetes 代理服务器所使用的模式。

目前 Linux 平台上有两种可用的代理模式:'iptables' 和 'ipvs'。 在 Windows 平台上可用的一种代理模式是:'kernelspace'。

如果代理模式未被指定,将使用最佳可用的代理模式(目前在 Linux 上是 iptables,在 Windows 上是 kernelspace)。 如果不能使用选定的代理模式(由于缺少内核支持、缺少用户空间组件等),则 kube-proxy 将出错并退出。

6.14.11 - kube-scheduler 配置 (v1)

资源类型

ClientConnectionConfiguration

出现在:

ClientConnectionConfiguration 中包含用来构造客户端所需的细节。

字段描述
kubeconfig [必需]
string

kubeconfig 字段为指向 KubeConfig 文件的路径。

acceptContentTypes [必需]
string

acceptContentTypes 定义的是客户端与服务器建立连接时要发送的 Accept 头部, 这里的设置值会覆盖默认值 "application/json"。此字段会影响某特定客户端与服务器的所有连接。

contentType [必需]
string

contentType 包含的是此客户端向服务器发送数据时使用的内容类型(Content Type)。

qps [必需]
float32

qps 控制此连接允许的每秒查询次数。

burst [必需]
int32

burst 允许在客户端超出其速率限制时可以累积的额外查询个数。

DebuggingConfiguration

出现在:

DebuggingConfiguration 包含与调试功能相关的配置。

字段描述
enableProfiling [必需]
bool

enableProfiling 字段允许通过 Web 接口 host:port/debug/pprof/ 执行性能分析。

enableContentionProfiling [必需]
bool

enableContentionProfiling 字段在 enableProfiling 为 true 时启用阻塞分析。

LeaderElectionConfiguration

出现在:

LeaderElectionConfiguration 为能够支持领导者选举的组件定义其领导者选举客户端的配置。

字段描述
leaderElect [必需]
bool

leaderElect 允许领导者选举客户端在进入主循环执行之前先获得领导者角色。 运行多副本组件时启用此功能有助于提高可用性。

leaseDuration [必需]
meta/v1.Duration

leaseDuration 是非领导角色候选者在观察到需要领导席位更新时要等待的时间; 只有经过所设置时长才可以尝试去获得一个仍处于领导状态但需要被刷新的席位。 这里的设置值本质上意味着某个领导者在被另一个候选者替换掉之前可以停止运行的最长时长。 只有当启用了领导者选举时此字段有意义。

renewDeadline [必需]
meta/v1.Duration

renewDeadline 设置的是当前领导者在停止扮演领导角色之前需要刷新领导状态的时间间隔。 此值必须小于或等于租约期限的长度。只有到启用了领导者选举时此字段才有意义。

retryPeriod [Required[必需]
meta/v1.Duration

retryPeriod 是客户端在连续两次尝试获得或者刷新领导状态之间需要等待的时长。 只有当启用了领导者选举时此字段才有意义。

resourceLock [必需]
string

resourceLock 字段给出在领导者选举期间要作为锁来使用的资源对象类型。

resourceName [必需]
string

resourceName 字段给出在领导者选举期间要作为锁来使用的资源对象名称。

resourceNamespace [必需]
string

resourceNamespace 字段给出在领导者选举期间要作为锁来使用的资源对象所在名字空间。

DefaultPreemptionArgs

DefaultPreemptionArgs 包含用来配置 DefaultPreemption 插件的参数。

字段描述
apiVersion
string
kubescheduler.config.k8s.io/v1
kind
string
DefaultPreemptionArgs
minCandidateNodesPercentage [必需]
int32

minCandidateNodesPercentage 字段为试运行抢占时 shortlist 中候选节点数的下限, 数值为节点数的百分比。字段值必须介于 [0, 100] 之间。未指定时默认值为整个集群规模的 10%。

minCandidateNodesAbsolute [必需]
int32

minCandidateNodesAbsolute 字段设置 shortlist 中候选节点的绝对下限。 用于试运行抢占而列举的候选节点个数近似于通过下面的公式计算的:
候选节点数 = max(节点数 * minCandidateNodesPercentage, minCandidateNodesAbsolute)
之所以说是"近似于"是因为存在一些类似于 PDB 违例这种因素, 会影响到进入 shortlist 中候选节点的个数。 取值至少为 0 节点。若未设置默认为 100 节点。

InterPodAffinityArgs

InterPodAffinityArgs 包含用来配置 InterPodAffinity 插件的参数。

字段描述
apiVersion
string
kubescheduler.config.k8s.io/v1
kind
string
InterPodAffinityArgs
hardPodAffinityWeight [必需]
int32

hardPodAffinityWeight 字段是一个计分权重值。针对新增的 Pod,要对现存的、 带有与新 Pod 匹配的硬性亲和性设置的 Pods 计算亲和性得分。

ignorePreferredTermsOfExistingPods [必需]
bool

ignorePreferredTermsOfExistingPods 配置调度器在为候选节点评分时忽略现有 Pod 的优选亲和性规则, 除非传入的 Pod 具有 Pod 间的亲和性。

KubeSchedulerConfiguration

KubeSchedulerConfiguration 用来配置调度器。

字段描述
apiVersion
string
kubescheduler.config.k8s.io/v1
kind
string
KubeSchedulerConfiguration
parallelism [必需]
int32

parallelism 字段设置为调度 Pod 而执行算法时的并发度。此值必须大于 0。默认值为 16。

leaderElection [必需]
LeaderElectionConfiguration

LeaderElection 字段用来定义领导者选举客户端的配置。

clientConnection [必需]
ClientConnectionConfiguration

clientConnection 字段为与 API 服务器通信时使用的代理服务器设置 kubeconfig 文件和客户端连接配置。

DebuggingConfiguration [必需]
DebuggingConfiguration
DebuggingConfiguration 的成员被内嵌到此类型中)

DebuggingConfiguration 字段设置与调试相关功能特性的配置。 TODO:我们可能想把它做成一个子结构,像调试 component-base/config/v1alpha1.DebuggingConfiguration 一样。

percentageOfNodesToScore [必需]
int32

percentageOfNodesToScore 字段为所有节点的百分比,一旦调度器找到所设置比例的、能够运行 Pod 的节点, 则停止在集群中继续寻找更合适的节点。这一配置有助于提高调度器的性能。 调度器总会尝试寻找至少 "minFeasibleNodesToFind" 个可行节点,无论此字段的取值如何。 例如:当集群规模为 500 个节点,而此字段的取值为 30, 则调度器在找到 150 个合适的节点后会停止继续寻找合适的节点。当此值为 0 时, 调度器会使用默认节点数百分比(基于集群规模确定的值,在 5% 到 50% 之间)来执行打分操作。 它可被配置文件级别的 PercentageofNodesToScore 覆盖。

podInitialBackoffSeconds [必需]
int64

podInitialBackoffSeconds 字段设置不可调度 Pod 的初始回退秒数。 如果设置了此字段,其取值必须大于零。若此值为 null,则使用默认值(1s)。

podMaxBackoffSeconds [必需]
int64

podMaxBackoffSeconds 字段设置不可调度的 Pod 的最大回退秒数。 如果设置了此字段,则其值必须大于 podInitialBackoffSeconds 字段值。 如果此值设置为 null,则使用默认值(10s)。

profiles [必需]
[]KubeSchedulerProfile

profiles 字段为 kube-scheduler 所支持的方案(profiles)。 Pod 可以通过设置其对应的调度器名称来选择使用特定的方案。 未指定调度器名称的 Pod 会使用 "default-scheduler" 方案来调度,如果存在的话。

extenders [必需]
[]Extender

extenders 字段为调度器扩展模块(Extender)的列表,每个元素包含如何与某扩展模块通信的配置信息。 所有调度器方案会共享此扩展模块列表。

delayCacheUntilActive [Required]
bool

DelayCacheUntilActive 指定何时开始缓存。如果字段设置为 true 并且启用了领导者选举, 则调度程序将等待填充通知者缓存,直到它成为领导者,这样做会减慢故障转移速度, 并在等待成为领导者时降低内存开销。 默认为 false。

NodeAffinityArgs

NodeAffinityArgs 中包含配置 NodeAffinity 插件的参数。

字段描述
apiVersion
string
kubescheduler.config.k8s.io/v1
kind
string
NodeAffinityArgs
addedAffinity
core/v1.NodeAffinity

addedAffinity 会作为附加的亲和性属性添加到所有 Pod 的规约中指定的 NodeAffinity 中。 换言之,节点需要同时满足 addedAffinity 和 .spec.nodeAffinity。 默认情况下,addedAffinity 为空(与所有节点匹配)。使用了 addedAffinity 时, 某些带有已经能够与某特定节点匹配的亲和性需求的 Pod (例如 DaemonSet Pod)可能会继续呈现不可调度状态。

NodeResourcesBalancedAllocationArgs

NodeResourcesBalancedAllocationArgs 包含用来配置 NodeResourcesBalancedAllocation 插件的参数。

字段描述
apiVersion
string
kubescheduler.config.k8s.io/v1
kind
string
NodeResourcesBalancedAllocationArgs
resources [必需]
[]ResourceSpec

要管理的资源;如果未设置,则默认值为 "cpu" 和 "memory"。

NodeResourcesFitArgs

NodeResourcesFitArgs 包含用来配置 NodeResourcesFit 插件的参数。

字段描述
apiVersion
string
kubescheduler.config.k8s.io/v1
kind
string
NodeResourcesFitArgs
ignoredResources [必需]
[]string

ignoredResources 字段为 NodeResources 匹配过滤器要忽略的资源列表。此列表不影响节点打分。

ignoredResourceGroups [必需]
[]string

ignoredResourceGroups 字段定义 NodeResources 匹配过滤器要忽略的资源组列表。 例如,如果配置值为 ["example.com"], 则以 "example.com" 开头的资源名 (如"example.com/aaa" 和 "example.com/bbb")都会被忽略。 资源组名称中不可以包含 '/'。此设置不影响节点的打分。

scoringStrategy [必需]
ScoringStrategy

scoringStrategy 用来选择节点资源打分策略。默认的策略为 LeastAllocated, 且 "cpu" 和 "memory" 的权重相同。

PodTopologySpreadArgs

PodTopologySpreadArgs 包含用来配置 PodTopologySpread 插件的参数。

字段描述
apiVersion
string
kubescheduler.config.k8s.io/v1
kind
string
PodTopologySpreadArgs
defaultConstraints
[]core/v1.TopologySpreadConstraint

defaultConstraints 字段针对未定义 .spec.topologySpreadConstraints 的 Pod, 为其提供拓扑分布约束。.defaultConstraints[∗].labelSelectors必须为空, 因为这一信息要从 Pod 所属的 Service、ReplicationController、ReplicaSet 或 StatefulSet 来推导。 此字段不为空时,.defaultingType 必须为 "List"。

defaultingType
PodTopologySpreadConstraintsDefaulting

defaultingType 决定如何推导 .defaultConstraints。 可选值为 "System" 或 "List"。

  • "System":使用 Kubernetes 定义的约束,将 Pod 分布到不同节点和可用区;
  • "List":使用 .defaultConstraints 中定义的约束。

默认值为 "System"。

VolumeBindingArgs

VolumeBindingArgs 包含用来配置 VolumeBinding 插件的参数。

字段描述
apiVersion
string
kubescheduler.config.k8s.io/v1
kind
string
VolumeBindingArgs
bindTimeoutSeconds [必需]
int64

bindTimeoutSeconds 字段设置卷绑定操作的超时秒数。字段值必须是非负数。 取值为 0 意味着不等待。如果此值为 null,则使用默认值(600)。

shape
[]UtilizationShapePoint

shape 用来设置打分函数曲线所使用的计分点, 这些计分点用来基于静态制备的 PV 卷的利用率为节点打分。 卷的利用率是计算得来的, 将 Pod 所请求的总的存储空间大小除以每个节点上可用的总的卷容量。 每个计分点包含利用率(范围从 0 到 100)和其对应的得分(范围从 0 到 10)。 你可以通过为不同的使用率值设置不同的得分来反转优先级:

默认的曲线计分点为:

  1. 利用率为 0 时得分为 0;
  2. 利用率为 100 时得分为 10。

所有计分点必须按利用率值的升序来排序。

Extender

出现在:

Extender 包含与扩展模块(Extender)通信所用的参数。 如果未指定 verb 或者 verb 为空,则假定对应的扩展模块选择不提供该扩展功能。

字段描述
urlPrefix [必需]
string

用来访问扩展模块的 URL 前缀。

filterVerb [必需]
string

filter 调用所使用的动词,如果不支持过滤操作则为空。 此动词会在向扩展模块发送 filter 调用时追加到 urlPrefix 后面。

preemptVerb [必需]
string

preempt 调用所使用的动词,如果不支持 preempt 操作则为空。 此动词会在向扩展模块发送 preempt 调用时追加到 urlPrefix 后面。

prioritizeVerb [必需]
string

prioritize 调用所使用的动词,如果不支持 prioritize 操作则为空。 此动词会在向扩展模块发送 prioritize 调用时追加到 urlPrefix 后面。

weight [必需]
int64

针对 prioritize 调用所生成的节点分数要使用的数值系数。 weight 值必须是正整数。

bindVerb [必需]
string

bind 调用所使用的动词,如果不支持 bind 操作则为空。 此动词会在向扩展模块发送 bind 调用时追加到 urlPrefix 后面。 如果扩展模块实现了此方法,扩展模块要负责将 Pod 绑定到 API 服务器。 只有一个扩展模块可以实现此函数。

enableHTTPS [必需]
bool

enableHTTPS 字段设置是否需要使用 HTTPS 来与扩展模块通信。

tlsConfig [Required]
ExtenderTLSConfig

tlsConfig 字段设置传输层安全性(TLS)配置。

httpTimeout [必需]
meta/v1.Duration

httpTimeout 给出扩展模块功能调用的超时值。filter 操作超时会导致 Pod 无法被调度。 prioritize 操作超时会被忽略, Kubernetes 或者其他扩展模块所给出的优先级值会被用来选择节点。

nodeCacheCapable [必需]
bool

nodeCacheCapable 指示扩展模块可以缓存节点信息,从而调度器应该发送关于可选节点的最少信息, 假定扩展模块已经缓存了集群中所有节点的全部详细信息。

managedResources
[]ExtenderManagedResource

managedResources 是一个由此扩展模块所管理的扩展资源的列表。

  • 如果某 Pod 请求了此列表中的至少一个扩展资源,则 Pod 会在 filter、 prioritize 和 bind (如果扩展模块可以执行绑定操作)阶段被发送到该扩展模块。
  • 如果某资源上设置了 ignoredByScheduler 为 true,则 kube-scheduler 会在断言阶段略过对该资源的检查。
ignorable [必需]
bool

ignorable 用来设置扩展模块是否是可忽略的。 换言之,当扩展模块返回错误或者完全不可达时,调度操作不应失败。

ExtenderManagedResource

出现在:

ExtenderManagedResource 描述某扩展模块所管理的扩展资源的参数。

字段描述
name [必需]
string

扩展资源的名称。

ignoredByScheduler [必需]
bool

ignoredByScheduler 标明 kube-scheduler 是否应在应用断言时忽略此资源。

ExtenderTLSConfig

出现在:

ExtenderTLSConfig 包含启用与扩展模块间 TLS 传输所需的配置参数。

字段描述
insecure [必需]
bool

访问服务器时不需要检查 TLS 证书。此配置仅针对测试用途。

serverName [必需]
string

serverName 会被发送到服务器端,作为 SNI 标志; 客户端会使用此设置来检查服务器证书。 如果 serverName 为空,则会使用联系服务器时所用的主机名。

certFile [必需]
string

服务器端所要求的 TLS 客户端证书认证。

keyFile [必需]
string

服务器端所要求的 TLS 客户端秘钥认证。

caFile [必需]
string

服务器端被信任的根证书。

certData [必需]
[]byte

certData 包含 PEM 编码的字节流(通常从某客户端证书文件读入)。 此字段优先级高于 certFile 字段。

keyData [必需]
[]byte

keyData 包含 PEM 编码的字节流(通常从某客户端证书秘钥文件读入)。 此字段优先级高于 keyFile 字段。

caData [必需]
[]byte

caData 包含 PEM 编码的字节流(通常从某根证书包文件读入)。 此字段优先级高于 caFile 字段。

KubeSchedulerProfile

出现在:

KubeSchedulerProfile 是一个调度方案。

字段描述
schedulerName [必需]
string

schedulerName 是与此调度方案相关联的调度器的名称。 如果 schedulerName 与 Pod 的 spec.schedulerName 匹配, 则该 Pod 会使用此方案来调度。

percentageOfNodesToScore [必需]
int32

percentageOfNodesToScore 是已发现可运行 Pod 的节点与所有节点的百分比, 调度器所发现的可行节点到达此阈值时,将停止在集群中继续搜索可行节点。 这有助于提高调度器的性能。无论此标志的值是多少,调度器总是尝试至少找到 “minFeasibleNodesToFind” 个可行的节点。 例如:如果集群大小为 500 个节点并且此标志的值为 30,则调度器在找到 150 个可行节点后将停止寻找更多可行的节点。 当值为 0 时,默认百分比(根据集群大小为 5% - 50%)的节点将被评分。此设置值将覆盖全局的 PercentageOfNodesToScore 值。 如果为空,将使用全局 PercentageOfNodesToScore。

plugins [必需]
Plugins

plugins 设置一组应该被启用或禁止的插件。 被启用的插件是指除了默认插件之外需要被启用的插件。 被禁止的插件是指需要被禁用的默认插件。

如果针对某个扩展点没有设置被启用或被禁止的插件, 则使用该扩展点的默认插件(如果有的话)。如果设置了 QueueSort 插件, 则同一个 QueueSort 插件和 pluginConfig 要被设置到所有调度方案之上。

pluginConfig [必需]
[]PluginConfig

pluginConfig 是为每个插件提供的一组可选的定制插件参数。 如果忽略了插件的配置参数,则意味着使用该插件的默认配置。

Plugin

出现在:

Plugin 指定插件的名称及其权重(如果适用的话)。权重仅用于评分(Score)插件。

字段描述
name [必需]
string

插件的名称。

weight [必需]
int32

插件的权重;仅适用于评分(Score)插件。

PluginConfig

出现在:

PluginConfig 给出初始化阶段要传递给插件的参数。 在多个扩展点被调用的插件仅会被初始化一次。 参数可以是任意结构。插件负责处理这里所传的参数。

字段描述
name [必需]
string

name 是所配置的插件的名称。

args [必需]
k8s.io/apimachinery/pkg/runtime.RawExtension

args 定义在初始化阶段要传递给插件的参数。参数可以为任意结构。

PluginSet

出现在:

PluginSet 为某扩展点设置要启用或禁用的插件。 如果数组为空,或者取值为 null,则使用该扩展点的默认插件集合。

字段描述
enabled [必需]
[]Plugin

enabled 设置在默认插件之外要启用的插件。 如果在调度器的配置文件中也配置了默认插件,则对应插件的权重会被覆盖。 此处所设置的插件会在默认插件之后被调用,调用顺序与数组中元素顺序相同。

disabled [必需]
[]Plugin

disabled 设置要被禁用的默认插件。 如果需要禁用所有的默认插件,应该提供仅包含一个元素 "∗" 的数组。

Plugins

出现在:

Plugins 结构中包含多个扩展点。当此结构被设置时, 针对特定扩展点所启用的所有插件都在这一列表中。 如果配置中不包含某个扩展点,则使用该扩展点的默认插件集合。 被启用的插件的调用顺序与这里指定的顺序相同,都在默认插件之后调用。 如果它们需要在默认插件之前调用,则需要先行禁止默认插件, 之后在这里按期望的顺序重新启用。

字段描述
preEnqueue [必需]
PluginSet

preEnqueue 是在将 Pod 添加到调度队列之前应调用的插件的列表。

queueSort [必需]
PluginSet

queueSort 是一个在对调度队列中 Pod 排序时要调用的插件列表。

preFilter [必需]
PluginSet

preFilter 是一个在调度框架中 "PreFilter(预过滤)"扩展点上要调用的插件列表。

filter [必需]
PluginSet

filter 是一个在需要过滤掉无法运行 Pod 的节点时被调用的插件列表。

postFilter [必需]
PluginSet

postFilter 是一个在过滤阶段结束后会被调用的插件列表; 这里的插件只有在找不到合适的节点来运行 Pod 时才会被调用。

preScore [必需]
PluginSet

preScore 是一个在打分之前要调用的插件列表。

score [必需]
PluginSet

score 是一个在对已经通过过滤阶段的节点进行排序时调用的插件的列表。

reserve [必需]
PluginSet

reserve 是一组在运行 Pod 的节点已被选定后,需要预留或者释放资源时调用的插件的列表。

permit [必需]
PluginSet

permit 是一个用来控制 Pod 绑定关系的插件列表。 这些插件可以禁止或者延迟 Pod 的绑定。

preBind [必需]
PluginSet

preBind 是一个在 Pod 被绑定到某节点之前要被调用的插件的列表。

bind [必需]
PluginSet

bind 是一个在调度框架中 "Bind(绑定)"扩展点上要调用的插件的列表。 调度器按顺序调用这些插件。只要其中某个插件返回成功,则调度器就略过余下的插件。

postBind [必需]
PluginSet

postBind 是一个在 Pod 已经被成功绑定之后要调用的插件的列表。

multiPoint [必需]
PluginSet

multiPoint 是一个简化的配置段落,用来为所有合法的扩展点启用插件。 通过 multiPoint 启用的插件会自动注册到插件所实现的每个独立的扩展点上。 通过 multiPoint 禁用的插件会禁用对应的操作行为。 通过 multiPoint 所禁止的 "∗" 也是如此,意味着所有默认插件都不会被自动注册。 插件也可以通过各个独立的扩展点来禁用。

就优先序而言,插件配置遵从以下基本层次:

  1. 特定的扩展点;
  2. 显式配置的 multiPoint 插件;
  3. 默认插件的集合,以及 multiPoint 插件。

这意味着优先序较高的插件会先被运行,并且覆盖 multiPoint 中的任何配置。

用户显式配置的插件也会比默认插件优先序高。

在这样的层次结构设计之下,enabled 的优先序高于 disabled。 例如,某插件同时出现在 multiPoint.enabledmultiPoint.disalbed 时, 该插件会被启用。类似的, 同时设置 multiPoint.disabled = '∗'multiPoint.enabled = pluginA 时, 插件 pluginA 仍然会被注册。这一设计与所有其他扩展点的配置行为是相符的。

PodTopologySpreadConstraintsDefaulting

string 类型的别名)

出现在:

PodTopologySpreadConstraintsDefaulting 定义如何为 PodTopologySpread 插件设置默认的约束。

RequestedToCapacityRatioParam

出现在:

RequestedToCapacityRatioParam 结构定义 RequestedToCapacityRatio 的参数。

字段描述
shape [必需]
[]UtilizationShapePoint

shape 是一个定义评分函数曲线的计分点的列表。

ResourceSpec

出现在:

ResourceSpec 用来代表某个资源。

字段描述
name [必需]
string

资源名称。

weight [必需]
int64

资源权重。

ScoringStrategy

出现在:

ScoringStrategy 为节点资源插件定义 ScoringStrategyType。

字段描述
type [必需]
ScoringStrategyType

type 用来选择要运行的策略。

resources [必需]
[]ResourceSpec

resources 设置在评分时要考虑的资源。

默认的资源集合包含 "cpu" 和 "memory",且二者权重相同。

权重的取值范围为 1 到 100。

当权重未设置或者显式设置为 0 时,意味着使用默认值 1。

requestedToCapacityRatio [必需]
RequestedToCapacityRatioParam

特定于 RequestedToCapacityRatio 策略的参数。

ScoringStrategyType

string 数据类型的别名)

出现在:

ScoringStrategyType 是 NodeResourcesFit 插件所使用的的评分策略类型。

UtilizationShapePoint

出现在:

UtilizationShapePoint 代表的是优先级函数曲线中的一个评分点。

字段描述
utilization [必需]
int32

利用率(x 轴)。合法值为 0 到 100。完全被利用的节点映射到 100。

score [必需]
int32

score 分配给指定利用率的分值(y 轴)。合法值为 0 到 10。

6.14.12 - kubeadm 配置 (v1beta3)

概述

v1beta3 包定义 v1beta3 版本的 kubeadm 配置文件格式。 此版本改进了 v1beta2 的格式,修复了一些小问题并添加了一些新的字段。

从 v1beta2 版本以来的变更列表:

  • 已弃用的字段 "ClusterConfiguration.useHyperKubeImage" 现在被移除。 kubeadm 不再支持 hyperkube 镜像。
  • "ClusterConfiguration.dns.type" 字段已经被移除,因为 CoreDNS 是 kubeadm 所支持的唯一 DNS 服务器类型。
  • 保存 Secret 信息的字段现在包含了 "datapolicy" 标记(tag)。 这一标记会导致 API 结构通过 klog 打印输出时,会忽略这些字段的值。
  • 添加了 "InitConfiguration.skipPhases"、"JoinConfiguration.skipPhases", 以允许在执行 kubeadm init/join 命令时略过某些阶段。
  • 添加了 "InitConfiguration.nodeRegistration.imagePullPolicy" 和 "JoinConfiguration.nodeRegistration.imagePullPolicy" 以允许在 kubeadm initkubeadm join 期间指定镜像拉取策略。 这两个字段的值必须是 "Always"、"Never" 或 "IfNotPresent" 之一。 默认值是 "IfNotPresent",也是添加此字段之前的默认行为。
  • 添加了 "InitConfiguration.patches.directory" 和 "JoinConfiguration.patches.directory" 以允许用户配置一个目录, kubeadm 将从该目录中提取组件的补丁包。
  • BootstrapToken* API 和相关的工具被从 "kubeadm" API 组中移出, 放到一个新的 "bootstraptoken" 组中。kubeadm API 版本 v1beta3 不再包含 BootstrapToken* 结构。

从老的 kubeadm 配置版本迁移:

  • kubeadm v1.15.x 及更新的版本可以用来从 v1beta1 迁移到 v1beta2 版本;
  • kubeadm v1.22.x 及更新的版本不再支持 v1beta1 和更老的 API,但可以用来从 v1beta2 迁移到 v1beta3。

基础知识

配置 kubeadm 的推荐方式是使用 --config 选项向其传递一个 YAML 配置文件。 kubeadm 配置文件中定义的某些配置选项也可以作为命令行标志来使用,不过这种方法所支持的都是一些最常见的、最简单的使用场景。

一个 kubeadm 配置文件中可以包含多个配置类型,使用三根横线(---)作为分隔符。

kubeadm 支持以下配置类型:

apiVersion: kubeadm.k8s.io/v1beta3
kind: InitConfiguration

apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration

apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration

apiVersion: kubeadm.k8s.io/v1beta3
kind: JoinConfiguration

要输出 "init" 和 "join" 动作的默认值,可以使用下面的命令:

kubeadm config print init-defaults
kubeadm config print join-defaults

配置文件中必须包含的配置类型列表取决于你在执行的动作(initjoin), 也取决于你要使用的配置选项(默认值或者高级定制)。

如果某些配置类型没有提供,或者仅部分提供,kubeadm 将使用默认值; kubeadm 所提供的默认值在必要时也会保证其在多个组件之间是一致的 (例如控制器管理器上的 --cluster-cidr 参数和 kube-proxy 上的 clusterCIDR)。

用户总是可以重载默认配置值,唯一的例外是一小部分与安全性相关联的配置 (例如在 API 服务器上强制实施 Node 和 RBAC 鉴权模式)。

如果用户所提供的配置类型并非你所执行的操作需要的,kubeadm 会忽略这些配置类型并打印警告信息。

kubeadm init 配置类型

当带有 --config 选项来执行 kubeadm init 命令时,可以使用下面的配置类型: InitConfigurationClusterConfigurationKubeProxyConfigurationKubeletConfiguration, 但 InitConfigurationClusterConfiguration 之间只有一个是必须提供的。

apiVersion: kubeadm.k8s.io/v1beta3
kind: InitConfiguration
bootstrapTokens:
  ...
nodeRegistration:
  ...

类型 InitConfiguration 用来配置运行时设置,就 kubeadm init 命令而言, 包括启动引导令牌以及所有与 kubeadm 所在节点相关的设置,包括:

  • nodeRegistration:其中包含与向集群注册新节点相关的字段; 使用这个类型来定制节点名称、要使用的 CRI 套接字或者其他仅对当前节点起作用的设置 (例如节点 IP 地址)。
  • localAPIEndpoint:代表的是要部署到此节点上的 API 服务器实例的端点; 使用这个类型可以完成定制 API 服务器公告地址这类操作。
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
networking:
  ...
etcd:
  ...
apiServer:
  extraArgs:
    ...
  extraVolumes:
    ...
...

类型 ClusterConfiguration 用来定制集群范围的设置,具体包括以下设置:

  • networking:其中包含集群的网络拓扑配置。使用这一部分可以定制 Pod 的子网或者 Service 的子网。

  • etcd:etcd 数据库的配置。例如使用这个部分可以定制本地 etcd 或者配置 API 服务器使用一个外部的 etcd 集群。

  • kube-apiserverkube-schedulerkube-controller-manager 配置:这些部分可以通过添加定制的设置或者重载 kubeadm 的默认设置来定制控制面组件。

apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
  ...

KubeProxyConfiguration 类型用来更改传递给在集群中部署的 kube-proxy 实例的配置。 如果此对象没有提供,或者仅部分提供,kubeadm 使用默认值。

关于 kube-proxy 的官方文档,可参阅 https://kubernetes.io/zh-cn/docs/reference/command-line-tools-reference/kube-proxy/ 或者 https://pkg.go.dev/k8s.io/kube-proxy/config/v1alpha1#KubeProxyConfiguration。

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
  ...

KubeletConfiguration 类型用来更改传递给在集群中部署的 kubelet 实例的配置。 如果此对象没有提供,或者仅部分提供,kubeadm 使用默认值。

关于 kubelet 的官方文档,可参阅 https://kubernetes.io/zh-cn/docs/reference/command-line-tools-reference/kubelet/ 或者 https://pkg.go.dev/k8s.io/kubelet/config/v1beta1#KubeletConfiguration。

下面是一个为执行 kubeadm init 而提供的、包含多个配置类型的单一 YAML 文件, 其中填充了很多部分。

apiVersion: kubeadm.k8s.io/v1beta3
kind: InitConfiguration
bootstrapTokens:
  - token: "9a08jv.c0izixklcxtmnze7"
    description: "kubeadm bootstrap token"
    ttl: "24h"
  - token: "783bde.3f89s0fje9f38fhf"
    description: "another bootstrap token"
    usages:
      - authentication
      - signing
    groups:
      - system:bootstrappers:kubeadm:default-node-token
nodeRegistration:
  name: "ec2-10-100-0-1"
  criSocket: "/var/run/dockershim.sock"
  taints:
    - key: "kubeadmNode"
      value: "someValue"
      effect: "NoSchedule"
  kubeletExtraArgs:
    v: 4
  ignorePreflightErrors:
    - IsPrivilegedUser
  imagePullPolicy: "IfNotPresent"
localAPIEndpoint:
  advertiseAddress: "10.100.0.1"
  bindPort: 6443
certificateKey: "e6a2eb8581237ab72a4f494f30285ec12a9694d750b9785706a83bfcbbbd2204"
skipPhases:
  - addon/kube-proxy
---
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
etcd:
  # one of local or external
  local:
    imageRepository: "registry.k8s.io"
    imageTag: "3.2.24"
    dataDir: "/var/lib/etcd"
    extraArgs:
      listen-client-urls: "http://10.100.0.1:2379"
    serverCertSANs:
      -  "ec2-10-100-0-1.compute-1.amazonaws.com"
    peerCertSANs:
      - "10.100.0.1"
  # external:
    # endpoints:
    # - "10.100.0.1:2379"
    # - "10.100.0.2:2379"
    # caFile: "/etcd/kubernetes/pki/etcd/etcd-ca.crt"
    # certFile: "/etcd/kubernetes/pki/etcd/etcd.crt"
    # keyFile: "/etcd/kubernetes/pki/etcd/etcd.key"
networking:
  serviceSubnet: "10.96.0.0/16"
  podSubnet: "10.244.0.0/24"
  dnsDomain: "cluster.local"
kubernetesVersion: "v1.21.0"
controlPlaneEndpoint: "10.100.0.1:6443"
apiServer:
  extraArgs:
    authorization-mode: "Node,RBAC"
  extraVolumes:
    - name: "some-volume"
      hostPath: "/etc/some-path"
      mountPath: "/etc/some-pod-path"
      readOnly: false
      pathType: File
  certSANs:
    - "10.100.1.1"
    - "ec2-10-100-0-1.compute-1.amazonaws.com"
  timeoutForControlPlane: 4m0s
controllerManager:
  extraArgs:
    "node-cidr-mask-size": "20"
  extraVolumes:
    - name: "some-volume"
      hostPath: "/etc/some-path"
      mountPath: "/etc/some-pod-path"
      readOnly: false
      pathType: File
scheduler:
  extraArgs:
    bind-address: "10.100.0.1"
  extraVolumes:
    - name: "some-volume"
      hostPath: "/etc/some-path"
      mountPath: "/etc/some-pod-path"
      readOnly: false
      pathType: File
certificatesDir: "/etc/kubernetes/pki"
imageRepository: "registry.k8s.io"
clusterName: "example-cluster"
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
# kubelet specific options here
---
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
# kube-proxy specific options here

kubeadm join 配置类型

当使用 --config 选项执行 kubeadm join 命令时, 需要提供 JoinConfiguration 类型。

apiVersion: kubeadm.k8s.io/v1beta3
kind: JoinConfiguration
  ...

JoinConfiguration 类型用来配置运行时设置,就 kubeadm join 而言包括用来访问集群信息的发现方法,以及所有特定于 kubeadm 执行所在节点的设置,包括:

  • nodeRegistration:其中包含向集群注册新节点相关的配置字段; 使用这个类型可以定制节点名称、用使用的 CRI 套接字和所有其他仅适用于当前节点的设置 (例如节点 IP 地址)。
  • apiEndpoint:用来代表最终要部署到此节点上的 API 服务器实例的端点。

资源类型

BootstrapToken

出现在:

BootstrapToken 描述的是一个启动引导令牌,以 Secret 形式存储在集群中。

字段描述
token [必需]
BootstrapTokenString

token 用来在节点与控制面之间建立双向的信任关系。 在向集群中添加节点时使用。

description
string

description 设置一个对用户友好的消息, 说明为什么此令牌会存在以及其目标用途,这样其他管理员能够知道其目的。

ttl
meta/v1.Duration

ttl 定义此令牌的声明周期。默认为 24hexpiresttl 是互斥的。

expires
meta/v1.Time

expires 设置此令牌过期的时间戳。默认为在运行时基于 ttl 来决定。 expiresttl 是互斥的。

usages
[]string

usages 描述此令牌的可能使用方式。默认情况下, 令牌可用于建立双向的信任关系;不过这里可以改变默认用途。

groups
[]string

groups 设定此令牌被用于身份认证时对应的附加用户组。

BootstrapTokenString

出现在:

BootstrapTokenString 形式为 abcdef.abcdef0123456789 的一个令牌, 用来从加入集群的节点角度验证 API 服务器的身份,或者 "kubeadm join" 在节点启动引导时作为一种身份认证方法。 此令牌的生命期是短暂的,并且应该如此。

字段描述
- [必需]
string
令牌的 ID。
- [必需]
string
令牌的私密数据。

ClusterConfiguration

ClusterConfiguration 包含一个 kubeadm 集群的集群范围配置信息。

字段描述
apiVersion
string
kubeadm.k8s.io/v1beta3
kind
string
ClusterConfiguration
etcd
Etcd

etcd 中包含 etcd 服务的配置。

networking
Networking

networking 字段包含集群的网络拓扑配置。

kubernetesVersion
string

kubernetesVersion 设置控制面的目标版本。

controlPlaneEndpoint
string

controlPlaneEndpoint 为控制面设置一个稳定的 IP 地址或 DNS 名称。 取值可以是一个合法的 IP 地址或者 RFC-1123 形式的 DNS 子域名,二者均可以带一个可选的 TCP 端口号。 如果 controlPlaneEndpoint 未设置,则使用 advertiseAddress + bindPort。 如果设置了 controlPlaneEndpoint,但未指定 TCP 端口号,则使用 bindPort

可能的用法有:

  • 在一个包含不止一个控制面实例的集群中,该字段应该设置为放置在控制面实例前面的外部负载均衡器的地址。
  • 在带有强制性节点回收的环境中,controlPlaneEndpoint 可以用来为控制面设置一个稳定的 DNS。
apiServer
APIServer

apiServer 包含 API 服务器的一些额外配置。

controllerManager
ControlPlaneComponent

controllerManager 中包含控制器管理器的额外配置。

scheduler
ControlPlaneComponent

scheduler 包含调度器的额外配置。

dns
DNS

dns 定义在集群中安装的 DNS 插件的选项。

certificatesDir
string

certificatesDir 设置在何处存放或者查找所需证书。

imageRepository
string

imageRepository 设置用来拉取镜像的容器仓库。 如果此字段为空,默认使用 registry.k8s.io。 当 Kubernetes 用来执行 CI 构建时(Kubernetes 版本以 ci/ 开头), 将默认使用 gcr.io/k8s-staging-ci-images 来拉取控制面组件镜像, 而使用 registry.k8s.io 来拉取所有其他镜像。

featureGates
map[string]bool

featureGates 包含用户所启用的特性门控。

clusterName
string

集群名称。

InitConfiguration

InitConfiguration 包含一组特定于 "kubeadm init" 的运行时元素。 这里的字段仅用于第一次运行 kubeadm init 命令。 之后,此结构中的字段信息不会再被上传到 kubeadm upgrade 所要使用的 kubeadm-config ConfigMap 中。 这些字段必须设置 "omitempty"

字段描述
apiVersion
string
kubeadm.k8s.io/v1beta3
kind
string
InitConfiguration
bootstrapTokens
[]BootstrapToken

bootstrapTokenskubeadm init 执行时会被用到, 其中描述了一组要创建的启动引导令牌(Bootstrap Tokens)。这里的信息不会被上传到 kubeadm 在集群中保存的 ConfigMap 中,部分原因是由于信息本身比较敏感。

nodeRegistration
NodeRegistrationOptions

nodeRegistration 中包含与向集群中注册新的控制面节点相关的字段。

localAPIEndpoint
APIEndpoint

localAPIEndpoint 所代表的是在此控制面节点上要部署的 API 服务器的端点。 在高可用(HA)配置中,此字段与 ClusterConfiguration.controlPlaneEndpoint 的取值不同:后者代表的是整个集群的全局端点,该端点上的请求会被负载均衡到每个 API 服务器。 此配置对象允许你定制本地 API 服务器所公布的、可访问的 IP/DNS 名称和端口。 默认情况下,kubeadm 会尝试自动检测默认接口上的 IP 并使用该地址。 不过,如果这种检测失败,你可以在此字段中直接设置所期望的值。

certificateKey
string

certificateKey 用来设置一个密钥,该密钥将对 uploadcerts init 阶段上传到集群中某 Secret 内的密钥和证书加密。 证书密钥是十六进制编码的字符串,是长度为 32 字节的 AES 密钥。

skipPhases
[]string

skipPhases 是命令执行过程中要略过的阶段(Phases)。 通过执行命令 kubeadm init --help 可以获得阶段的列表。 参数标志 "--skip-phases" 优先于此字段的设置。

patches
Patches

patches 包含与 kubeadm init 阶段 kubeadm 所部署的组件上要应用的补丁相关的信息。

JoinConfiguration

JoinConfiguration 包含描述特定节点的元素。

字段描述
apiVersion
string
kubeadm.k8s.io/v1beta3
kind
string
JoinConfiguration
nodeRegistration
NodeRegistrationOptions

nodeRegistration 包含与向集群注册控制面节点相关的字段。

caCertPath
string

caCertPath 是指向 SSL 证书机构的路径,该证书包用来加密节点与控制面之间的通信。 默认值为 "/etc/kubernetes/pki/ca.crt"。

discovery [必需]
Discovery

discovery 设置 TLS 引导过程中 kubelet 要使用的选项。

controlPlane
JoinControlPlane

controlPlane 定义要在正被加入到集群中的节点上部署的额外控制面实例。 此字段为 null 时,不会在上面部署额外的控制面实例。

skipPhases
[]string

此字段包含在命令执行过程中要略过的阶段。通过 kubeadm join --help 命令可以查看阶段的列表。参数 --skip-phases 优先于此字段。

patches
Patches

此字段包含 kubeadm join 阶段向 kubeadm 所部署的组件打补丁的选项。

APIEndpoint

出现在:

APIEndpoint 结构包含某节点上部署的 API 服务器的配置元素。

字段描述
advertiseAddress
string

advertiseAddress 设置 API 服务器要公布的 IP 地址。

bindPort
int32

bindPort 设置 API 服务器要绑定到的安全端口。默认值为 6443。

APIServer

出现在:

APIServer 包含集群中 API 服务器部署所必需的设置。

字段描述
ControlPlaneComponent [必需]
ControlPlaneComponent
ControlPlaneComponent 结构的字段被嵌入到此类型中。) 无描述。
certSANs
[]string

certSANs 设置 API 服务器签署证书所用的额外主体替代名(Subject Alternative Name,SAN)。

timeoutForControlPlane
meta/v1.Duration

timeoutForControlPlane 用来控制我们等待 API 服务器开始运行的超时时间。

BootstrapTokenDiscovery

出现在:

BootstrapTokenDiscovery 用来设置基于引导令牌的服务发现选项。

字段描述
token [必需]
string

token 用来验证从控制面获得的集群信息。

apiServerEndpoint
string

apiServerEndpoint 为 API 服务器的 IP 地址或者域名,从该端点可以获得集群信息。

caCertHashes
[]string

caCertHashes 设置一组在基于令牌来发现服务时要验证的公钥指纹。 发现过程中获得的根 CA 必须与这里的数值之一匹配。 设置为空集合意味着禁用根 CA 指纹,因而可能是不安全的。 每个哈希值的形式为 <type>:<value>,当前唯一支持的 type 为 "sha256"。 哈希值为主体公钥信息(Subject Public Key Info,SPKI)对象的 SHA-256 哈希值(十六进制编码),形式为 DER 编码的 ASN.1。 例如,这些哈希值可以使用 OpenSSL 来计算。

unsafeSkipCAVerification
bool

unsafeSkipCAVerification 允许在使用基于令牌的服务发现时不使用 caCertHashes 来执行 CA 验证。这会弱化 kubeadm 的安全性, 因为其他节点可以伪装成控制面。

ControlPlaneComponent

出现在:

ControlPlaneComponent 中包含对集群中所有控制面组件都适用的设置。

字段描述
extraArgs
map[string]string

extraArgs 是要传递给控制面组件的一组额外的参数标志。 此映射中的每个键对应命令行上使用的标志名称,只是没有其引导连字符。

extraVolumes
[]HostPathMount

extraVolumes 是一组额外的主机卷,需要挂载到控制面组件中。

DNS

出现在:

DNS 结构定义要在集群中使用的 DNS 插件。

字段描述
ImageMeta [必需]
ImageMeta
ImageMeta 的成员被内嵌到此类型中)。

imageMeta 允许对 DNS 组件所使用的镜像作定制。

Discovery

出现在:

Discovery 设置 TLS 启动引导过程中 kubelet 要使用的配置选项。

字段描述
bootstrapToken
BootstrapTokenDiscovery

bootstrapToken 设置基于启动引导令牌的服务发现选项。 bootstrapTokenfile 是互斥的。

file
FileDiscovery

用来设置一个文件或者 URL 路径,指向一个 kubeconfig 文件;该配置文件中包含集群信息。 bootstrapTokenfile 是互斥的。

tlsBootstrapToken
string

tlsBootstrapToken 是 TLS 启动引导过程中使用的令牌。 如果设置了 bootstrapToken,则此字段默认值为 .bootstrapToken.token,不过可以被重载。 如果设置了 file,此字段必须被设置,以防 kubeconfig 文件中不包含其他身份认证信息。

timeout
meta/v1.Duration

timeout 用来修改发现过程的超时时长。

Etcd

出现在:

Etcd 包含用来描述 etcd 配置的元素。

字段描述
local
LocalEtcd

local 提供配置本地 etcd 实例的选项。localexternal 是互斥的。

external
ExternalEtcd

external 描述如何连接到外部的 etcd 集群。 external 是互斥的。

ExternalEtcd

出现在:

ExternalEtcd 描述外部 etcd 集群。 kubeadm 不清楚证书文件的存放位置,因此必须单独提供证书信息。

字段描述
endpoints [必需]
[]string

endpoints 包含一组 etcd 成员的列表。

caFile [必需]
string

caFile 是一个 SSL 证书机构(CA)文件,用来加密 etcd 通信。 如果使用 TLS 连接,此字段为必需字段。

certFile [必需]
string

certFile 是一个 SSL 证书文件,用来加密 etcd 通信。 如果使用 TLS 连接,此字段为必需字段。

keyFile [必需]
string

keyFile 是一个用来加密 etcd 通信的 SSL 密钥文件。 此字段在使用 TLS 连接时为必填字段。

FileDiscovery

出现在:

FileDiscovery 用来指定一个文件或者 URL 路径,指向一个 kubeconfig 文件; 该配置文件可用来加载集群信息。

字段描述
kubeConfigPath [必需]
string

kubeConfigPath 用来指定一个文件或者 URL 路径,指向一个 kubeconfig 文件; 该配置文件可用来加载集群信息。

HostPathMount

出现在:

HostPathMount 包含从主机节点挂载的卷的信息。

字段描述
name [必需]
string

name 为卷在 Pod 模板中的名称。

hostPath [必需]
string

hostPath 是要在 Pod 中挂载的卷在主机系统上的路径。

mountPath [必需]
string

mountPathhostPath 在 Pod 内挂载的路径。

readOnly
bool

readOnly 控制卷的读写访问模式。

pathType
core/v1.HostPathType

pathTypehostPath 的类型。

ImageMeta

出现在:

ImageMeta 用来配置来源不是 Kubernetes/kubernetes 发布过程的组件所使用的镜像。

字段描述
imageRepository
string

imageRepository 设置镜像拉取所用的容器仓库。 若未设置,则使用 ClusterConfiguration 中的 imageRepository

imageTag
string

imageTag 允许用户设置镜像的标签。 如果设置了此字段,则 kubeadm 不再在集群升级时自动更改组件的版本。

JoinControlPlane

出现在:

JoinControlPlane 包含在正在加入集群的节点上要部署的额外的控制面组件的设置。

字段描述
localAPIEndpoint
APIEndpoint

localAPIEndpoint 代表的是将在此节点上部署的 API 服务器实例的端点。

certificateKey
string

certificateKey 是在添加新的控制面节点时用来解密所下载的 Secret 中的证书的密钥。对应的加密密钥在 InitConfiguration 结构中。 证书密钥是十六进制编码的字符串,是长度为 32 字节的 AES 密钥。

LocalEtcd

出现在:

LocalEtcd 描述的是 kubeadm 要使用的本地 etcd 集群。

字段描述
ImageMeta [必需]
ImageMeta
ImageMeta 结构的字段被嵌入到此类型中。)

ImageMeta 允许用户为 etcd 定制要使用的容器。

dataDir [必需]
string

dataDir 是 etcd 用来存放数据的目录。 默认值为 "/var/lib/etcd"。

extraArgs
map[string]string

extraArgs 是为 etcd 可执行文件提供的额外参数,用于在静态 Pod 中运行 etcd。映射中的每一个键对应命令行上的一个标志参数,只是去掉了前置的连字符。

serverCertSANs
[]string

serverCertSANs 为 etcd 服务器的签名证书设置额外的主体替代名(Subject Alternative Names,SAN)。

peerCertSANs
[]string

peerCertSANs 为 etcd 的对等端签名证书设置额外的主体替代名(Subject Alternative Names,SAN)。

Networking

出现在:

Networking 中包含描述集群网络配置的元素。

字段描述
serviceSubnet
string

serviceSubnet 是 Kubernetes 服务所使用的子网。 默认值为 "10.96.0.0/12"。

podSubnet
string

podSubnet 为 Pod 所使用的子网。

dnsDomain
string

dnsDomain 是 Kubernetes 服务所使用的 DNS 域名。 默认值为 "cluster.local"。

NodeRegistrationOptions

出现在:

NodeRegistrationOptions 包含向集群中注册新的控制面或节点所需要的信息; 节点注册可能通过 "kubeadm init" 或 "kubeadm join" 完成。

字段描述
name
string

name 是 Node API 对象的 .metadata.name 字段值; 该 API 对象会在此 kubeadm initkubeadm join 操作期间创建。 在提交给 API 服务器的 kubelet 客户端证书中,此字段也用作其 CommonName。 如果未指定则默认为节点的主机名。

criSocket
string

criSocket 用来读取容器运行时的信息。 此信息会被以注解的方式添加到 Node API 对象至上,用于后续用途。

taints [必需]
[]core/v1.Taint

taints 设定 Node API 对象被注册时要附带的污点。 若未设置此字段(即字段值为 null),默认为控制面节点添加控制面污点。 如果你不想为控制面节点添加污点,可以将此字段设置为空列表(即 YAML 文件中的 taints: []), 这个字段只用于节点注册。

kubeletExtraArgs
map[string]string

kubeletExtraArgs 用来向 kubelet 传递额外参数。 这里的参数会通过 kubeadm 在运行时写入的、由 kubelet 来读取的环境文件来传递给 kubelet 命令行。 这里的设置会覆盖掉 kubelet-config ConfigMap 中包含的一般性的配置。 命令行标志在解析时优先级更高。这里的设置值仅作用于 kubeadm 运行所在的节点。 映射中的每个键对应命令行中的一个标志参数,只是去掉了前置的连字符。

ignorePreflightErrors
[]string

ignorePreflightErrors 提供一组在当前节点被注册时可以忽略掉的预检错误。 例如:IsPrevilegedUser,Swap。 取值 all 忽略所有检查的错误。

imagePullPolicy
core/v1.PullPolicy

imagePullPolicy 设定 "kubeadm init" 和 "kubeadm join" 操作期间的镜像拉取策略。此字段的取值可以是 "Always"、"IfNotPresent" 或 "Never" 之一。若此字段未设置,则 kubeadm 使用 "IfNotPresent" 作为其默认值, 换言之,当镜像在主机上不存在时才执行拉取操作。

Patches

出现在:

Patches 包含要向 kubeadm 所部署的组件应用的补丁信息。

字段描述
directory
string

directory 是指向某目录的路径,该目录中包含名为 "target[suffix][+patchtype].extension" 的文件。 例如,"kube-apiserver0+merge.yaml" 或者 "etcd.json"。 "target" 可以是 "kube-apiserver"、"kube-controller-manager"、 "kube-scheduler"、"etcd" 之一。 "patchtype" 可以是 "strategic"、"merge" 或者 "json", 其取值对应 kubectl 所支持的补丁形式。 "patchtype" 的默认值是 "strategic"。 "extension" 必须是 "json" 或者 "yaml"。 "suffix" 是一个可选的字符串,用来确定按字母表顺序来应用时,哪个补丁最先被应用。

6.14.13 - kubeadm 配置 (v1beta4)

概述

v1beta4 包定义 v1beta4 版本的 kubeadm 配置文件格式。 此版本改进了 v1beta3 的格式,修复了一些小问题并添加了一些新的字段。

从 v1beta3 版本以来的变更列表:

  • TODO https://github.com/kubernetes/kubeadm/issues/2890
  • 使用 APIServer.ExtraEnvsControllerManager.ExtraEnvsScheduler.ExtraEnvsEtcd.Local.ExtraEnvs。 支持在 ClusterConfiguration 下控制平面组件中的定制环境变量。
  • ResetConfiguration API 类型在 v1beta4 中已得到支持。 用户可以为 kubeadm reset 指定 --config 文件来重置节点。

kubeadm 配置版本迁移

  • kubeadm v1.15.x 及更高版本可用于从 v1beta1 迁移到 v1beta2。
  • kubeadm v1.22.x 及更高版本不再支持 v1beta1 和更早的 API,但可用于从 v1beta2 迁移到 v1beta3。
  • kubeadm v1.27.x 及更高版本不再支持 v1beta2 和更早的 API。
  • TODO: https://github.com/kubernetes/kubeadm/issues/2890 添加可用于转换到 v1beta4 的版本

基础知识

配置 kubeadm 的推荐方式是使用 --config 选项向其传递一个 YAML 配置文件。 kubeadm 配置文件中定义的某些配置选项也可以作为命令行标志来使用, 不过这种方法所支持的都是一些最常见的、最简单的使用场景。

一个 kubeadm 配置文件中可以包含多个配置类型,使用三根横线(---)作为分隔符。

kubeadm 支持以下配置类型:

apiVersion: kubeadm.k8s.io/v1beta4
kind: InitConfiguration

apiVersion: kubeadm.k8s.io/v1beta4
kind: ClusterConfiguration

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration

apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration

apiVersion: kubeadm.k8s.io/v1beta4
kind: JoinConfiguration

要输出 "init" 和 "join" 动作的默认值,可以使用下面的命令:

kubeadm config print init-defaults
kubeadm config print join-defaults

配置文件中必须包含的配置类型列表取决于你所执行的动作(initjoin), 也取决于你要使用的配置选项(默认值或者高级定制)。

如果某些配置类型没有提供,或者仅部分提供,kubeadm 将使用默认值; kubeadm 所提供的默认值在必要时也会保证其在多个组件之间是一致的 (例如控制器管理器上的 --cluster-cidr 参数和 kube-proxy 上的 clusterCIDR)。

用户总是可以覆盖默认配置值,唯一的例外是一小部分与安全性相关联的配置 (例如在 API 服务器上强制实施 Node 和 RBAC 鉴权模式)。

如果用户所提供的配置类型并非你所执行的操作需要的,kubeadm 会忽略这些配置类型并打印警告信息。

kubeadm init 配置类型

当带有 --config 选项来执行 kubeadm init 命令时,可以使用下面的配置类型: InitConfigurationClusterConfigurationKubeProxyConfigurationKubeletConfiguration,但 InitConfigurationClusterConfiguration 二者之间取其一即可。

apiVersion: kubeadm.k8s.io/v1beta4
kind: InitConfiguration
bootstrapTokens:

	...

nodeRegistration:

	...

InitConfiguration 类型用来配置运行时设置,就 kubeadm init 命令而言, 包括启动引导令牌以及所有与 kubeadm 所在节点相关的设置,包括:

  • nodeRegistration:其中包含与向集群注册新节点相关的字段; 使用这个类型来定制节点名称、要使用的 CRI 套接字或者其他仅对当前节点起作用的设置(例如节点 IP 地址)。
  • localAPIEndpoint:代表的是要部署到此节点上的 API 服务器实例的端点; 使用这个类型可以完成定制 API 服务器公告地址这类操作。
apiVersion: kubeadm.k8s.io/v1beta4
kind: ClusterConfiguration
networking:

	...

etcd:

	...

apiServer:

	extraArgs:
	  ...
	extraVolumes:
	  ...

...

类型 ClusterConfiguration 用来定制集群范围的设置,具体包括以下设置:

  • networking:其中包含集群的网络拓扑配置。使用这一部分可以定制 Pod 的子网或者 Service 的子网。

  • etcd:etcd 数据库的配置。例如使用这个部分可以定制本地 etcd 或者配置 API 服务器使用一个外部的 etcd 集群。

  • kube-apiserverkube-schedulerkube-controller-manager 配置:这些部分可以通过添加定制的设置或者重载 kubeadm 的默认设置来定制控制平面组件。

apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration

	...

KubeProxyConfiguration 类型用来更改传递给在集群中部署的 kube-proxy 实例的配置。 如果此对象没有提供,或者仅部分提供,kubeadm 使用默认值。

关于 kube-proxy 的官方文档,可参阅 https://kubernetes.io/zh-cn/docs/reference/command-line-tools-reference/kube-proxy/ 或者 https://pkg.go.dev/k8s.io/kube-proxy/config/v1alpha1#KubeProxyConfiguration。

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration

	...

KubeletConfiguration 类型用来更改传递给在集群中部署的 kubelet 实例的配置。 如果此对象没有提供,或者仅部分提供,kubeadm 使用默认值。

关于 kubelet 的官方文档,可参阅 https://kubernetes.io/zh-cn/docs/reference/command-line-tools-reference/kubelet/ 或者 https://pkg.go.dev/k8s.io/kubelet/config/v1beta1#KubeletConfiguration。

下面是一个为执行 kubeadm init 而提供的、包含多个配置类型的单一 YAML 文件, 其中填充了很多部分。

apiVersion: kubeadm.k8s.io/v1beta4
kind: InitConfiguration
bootstrapTokens:
  - token: "9a08jv.c0izixklcxtmnze7"
    description: "kubeadm bootstrap token"
    ttl: "24h"
  - token: "783bde.3f89s0fje9f38fhf"
    description: "another bootstrap token"
    usages:
  - authentication
  - signing
    groups:
  - system:bootstrappers:kubeadm:default-node-token

nodeRegistration:

	name: "ec2-10-100-0-1"
	criSocket: "unix:///var/run/containerd/containerd.sock"
	taints:
	  - key: "kubeadmNode"
	    value: "someValue"
	    effect: "NoSchedule"
	kubeletExtraArgs:
	  v: 4
	ignorePreflightErrors:
	  - IsPrivilegedUser
	imagePullPolicy: "IfNotPresent"

localAPIEndpoint:

	advertiseAddress: "10.100.0.1"
	bindPort: 6443

certificateKey: "e6a2eb8581237ab72a4f494f30285ec12a9694d750b9785706a83bfcbbbd2204"
skipPhases:
  - addon/kube-proxy

---
apiVersion: kubeadm.k8s.io/v1beta4
kind: ClusterConfiguration
etcd:

	# one of local or external
	local:
	  imageRepository: "registry.k8s.io"
	  imageTag: "3.2.24"
	  dataDir: "/var/lib/etcd"
	  extraArgs:
	    listen-client-urls: "http://10.100.0.1:2379"
	  serverCertSANs:
	    -  "ec2-10-100-0-1.compute-1.amazonaws.com"
	  peerCertSANs:
	    - "10.100.0.1"
	# external:
	  # endpoints:
	  # - "10.100.0.1:2379"
	  # - "10.100.0.2:2379"
	  # caFile: "/etcd/kubernetes/pki/etcd/etcd-ca.crt"
	  # certFile: "/etcd/kubernetes/pki/etcd/etcd.crt"
	  # keyFile: "/etcd/kubernetes/pki/etcd/etcd.key"

networking:

	serviceSubnet: "10.96.0.0/16"
	podSubnet: "10.244.0.0/24"
	dnsDomain: "cluster.local"

kubernetesVersion: "v1.21.0"
controlPlaneEndpoint: "10.100.0.1:6443"
apiServer:

	extraArgs:
	  authorization-mode: "Node,RBAC"
	extraVolumes:
	  - name: "some-volume"
	    hostPath: "/etc/some-path"
	    mountPath: "/etc/some-pod-path"
	    readOnly: false
	    pathType: File
	certSANs:
	  - "10.100.1.1"
	  - "ec2-10-100-0-1.compute-1.amazonaws.com"
	timeoutForControlPlane: 4m0s

controllerManager:

	extraArgs:
	  "node-cidr-mask-size": "20"
	extraVolumes:
	  - name: "some-volume"
	    hostPath: "/etc/some-path"
	    mountPath: "/etc/some-pod-path"
	    readOnly: false
	    pathType: File

scheduler:

	extraArgs:
	  address: "10.100.0.1"
	extraVolumes:
	  - name: "some-volume"
	    hostPath: "/etc/some-path"
	    mountPath: "/etc/some-pod-path"
	    readOnly: false
	    pathType: File

certificatesDir: "/etc/kubernetes/pki"
imageRepository: "registry.k8s.io"
clusterName: "example-cluster"
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
# kubelet specific options here
---
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
# kube-proxy specific options here

kubeadm join 配置类型

当使用 --config 选项执行 kubeadm join 命令时, 需要提供 JoinConfiguration 类型。

apiVersion: kubeadm.k8s.io/v1beta4
kind: JoinConfiguration

	...

JoinConfiguration 类型用来配置运行时设置,就 kubeadm join 而言包括用来访问集群信息的发现方法,以及所有特定于 kubeadm 执行所在节点的设置,包括:

  • nodeRegistration:其中包含向集群注册新节点相关的配置字段; 使用这个类型可以定制节点名称、用使用的 CRI 套接字和所有其他仅适用于当前节点的设置 (例如节点 IP 地址)。

  • apiEndpoint:用来代表最终要部署到此节点上的 API 服务器实例的端点。

资源类型

BootstrapToken

出现在:

BootstrapToken 描述的是一个启动引导令牌,以 Secret 的形式存储在集群中。

字段描述
token [必需]
BootstrapTokenString

token 用来在节点与控制平面之间建立双向的信任关系。 在向集群中添加节点时使用。

description
string

description 设置一个对用户友好的信息, 说明为什么此令牌会存在以及其目标用途,这样其他管理员能够知道其目的。

ttl
meta/v1.Duration

ttl 定义此令牌的生命周期。默认为 24hexpiresttl 是互斥的。

expires
meta/v1.Time

expires 设置此令牌过期的时间戳。默认根据 ttl 值在运行时动态设置。 expiresttl 是互斥的。

usages
[]string

usages 描述此令牌的可能使用方式。默认情况下, 令牌可用于建立双向的信任关系;不过这里可以改变默认用途。

groups
[]string

groups 设定此令牌被用于身份认证时对应的附加用户组。

BootstrapTokenString

出现在:

BootstrapTokenString 形式为 abcdef.abcdef0123456789 的一个令牌, 用来从加入集群的节点角度验证 API 服务器的身份,或者 "kubeadm join" 在节点启动引导时作为一种身份认证方法。 此令牌的生命期是短暂的,并且应该如此。

字段描述
- [必需]
string
令牌的 ID。
- [必需]
string
令牌的私密数据。

ClusterConfiguration

ClusterConfiguration 包含一个 kubeadm 集群的集群范围配置信息。

字段描述
apiVersion
string
kubeadm.k8s.io/v1beta4
kind
string
ClusterConfiguration
etcd
Etcd

etcd 中包含 etcd 服务的配置。

networking
Networking

networking 字段包含集群的网络拓扑配置。

kubernetesVersion
string

kubernetesVersion 设置控制平面的目标版本。

controlPlaneEndpoint
string

controlPlaneEndpoint 为控制平面设置一个稳定的 IP 地址或 DNS 名称。 取值可以是一个合法的 IP 地址或者 RFC-1123 形式的 DNS 子域名,二者均可以带一个可选的 TCP 端口号。 如果 controlPlaneEndpoint 未设置,则使用 advertiseAddress + bindPort。如果设置了 controlPlaneEndpoint,但未指定 TCP 端口号, 则使用 bindPort

可能的用法有:

  • 在一个包含不止一个控制平面实例的集群中,该字段应该设置为放置在控制平面实例前面的外部负载均衡器的地址。
  • 在带有强制性节点回收的环境中,controlPlaneEndpoint 可以用来为控制平面设置一个稳定的 DNS。
apiServer
APIServer

apiServer 包含 API 服务器的一些额外配置。

controllerManager
ControlPlaneComponent

controllerManager 中包含控制器管理器的额外配置。

scheduler
ControlPlaneComponent

scheduler 包含调度器控制平面组件的额外配置。

dns
DNS

dns 定义在集群中安装的 DNS 插件的选项。

certificatesDir
string

certificatesDir 设置在何处存放或者查找所需证书。

imageRepository
string

imageRepository 设置用来拉取镜像的容器仓库。 如果此字段为空,默认使用 registry.k8s.io。 当 Kubernetes 用来执行 CI 构建时(Kubernetes 版本以 ci/ 开头), 将默认使用 gcr.io/k8s-staging-ci-images 来拉取控制平面组件镜像, 而使用 registry.k8s.io 来拉取所有其他镜像。

featureGates
map[string]bool

featureGates 包含用户所启用的特性门控。

clusterName
string

集群名称。

InitConfiguration

InitConfiguration 包含一组特定于 "kubeadm init" 的运行时元素。

字段描述
apiVersion
string
kubeadm.k8s.io/v1beta4
kind
string
InitConfiguration
bootstrapTokens
[]BootstrapToken

bootstrapTokenskubeadm init 执行时会被用到, 其中描述了一组要创建的启动引导令牌(Bootstrap Tokens)。 这里的信息不会被上传到 kubeadm 在集群中保存的 ConfigMap 中,部分原因是由于信息本身比较敏感。

nodeRegistration
NodeRegistrationOptions

nodeRegistration 中包含与向集群中注册新的控制平面节点相关的字段。

localAPIEndpoint
APIEndpoint

localAPIEndpoint 所代表的是在此控制平面节点上要部署的 API 服务器的端点。 在高可用(HA)配置中,此字段与 ClusterConfiguration.controlPlaneEndpoint 的取值不同:后者代表的是整个集群的全局端点,该端点上的请求会被负载均衡到每个 API 服务器。此配置对象允许你定制本地 API 服务器所公布的、可访问的 IP/DNS 名称和端口。 默认情况下,kubeadm 会尝试自动检测默认接口上的 IP 并使用该地址。 不过,如果这种检测失败,你可以在此字段中直接设置所期望的值。

certificateKey
string

certificateKey 用来设置一个秘钥,该秘钥将对 uploadcerts init 阶段上传到集群中某 Secret 内的秘钥和证书加密。

skipPhases
[]string

skipPhases 是命令执行过程中要略过的阶段(Phases)。 通过执行命令 kubeadm init --help 可以获得阶段的列表。 参数标志 "--skip-phases" 优先于此字段的设置。

patches
Patches

patches 包含与 kubeadm init 阶段 kubeadm 所部署的组件上要应用的补丁相关的信息。

JoinConfiguration

JoinConfiguration 包含描述特定节点的元素。

字段描述
apiVersion
string
kubeadm.k8s.io/v1beta4
kind
string
JoinConfiguration
nodeRegistration
NodeRegistrationOptions

nodeRegistration 包含与向集群注册控制平面节点相关的字段。

caCertPath
string

caCertPath 是指向 SSL 证书机构的路径,该证书包用来加密节点与控制平面之间的通信。 默认值为 "/etc/kubernetes/pki/ca.crt"。

discovery [必需]
Discovery

discovery 设置 TLS 引导过程中 kubelet 要使用的选项。

controlPlane
JoinControlPlane

controlPlane 定义要在正被加入到集群中的节点上部署的额外控制平面实例。 此字段为 null 时,不会在上面部署额外的控制平面实例。

skipPhases
[]string

skipPhases 是在命令执行过程中要略过的阶段的列表。 通过 kubeadm join --help 命令可以查看阶段的列表。 参数 --skip-phases 优先于此字段。

patches
Patches

此字段包含 kubeadm join 阶段向 kubeadm 所部署的组件打补丁的选项。

ResetConfiguration

ResetConfiguration 包含一个字段列表,这些字段是特定于 "kubeadm reset" 的运行时元素。

字段描述
apiVersion
string
kubeadm.k8s.io/v1beta4
kind
string
ResetConfiguration
cleanupTmpDir
bool

cleanupTmpDir 指定在重置过程中 "/etc/kubernetes/tmp" 目录是否应被清理。

certificatesDir
string

certificatesDir 指定证书存储的目录。如果被指定,则在重置过程中此目录将被清理。

criSocket
string

criSocket 用于检索容器运行时信息,并用于移除容器。 如果未通过标志或配置文件指定 criSocket,kubeadm 将尝试检测一个有效的 criSocket

dryRun
bool

dryRun 指定是否启用试运行模式。如果启用了试运行模式, 则不应用任何更改,只输出将要执行的操作。

force
bool

force 标志指示 kubeadm 在重置节点时不需要确认提示。

ignorePreflightErrors
[]string

ignorePreflightErrors 提供了一个在重置过程中要忽略的预检错误列表,例如 'IsPrivilegedUser,Swap'。值 'all' 将忽略所有检查的错误。

skipPhases
[]string

skipPhases 是一个在命令执行过程中要略过的阶段的列表。 你可以使用命令 "kubeadm reset phase --help" 获取阶段的列表。

APIEndpoint

出现在:

APIEndpoint 结构包含某节点上部署的 API 服务器实例的元素。

字段描述
advertiseAddress
string

advertiseAddress 设置 API 服务器要公布的 IP 地址。

bindPort
int32

bindPort 设置 API 服务器要绑定到的安全端口。默认值为 6443。

APIServer

出现在:

APIServer 包含集群中 API 服务器部署所必需的设置。

字段描述
ControlPlaneComponent [必需]
ControlPlaneComponent
ControlPlaneComponent 结构的字段被嵌入到此类型中。) 无描述。
certSANs
[]string

certSANs 设置 API 服务器签署证书所用的额外主体替代名(Subject Alternative Name,SAN)。

timeoutForControlPlane
meta/v1.Duration

timeoutForControlPlane 用来控制我们等待 API 服务器开始运行的超时时间。

BootstrapTokenDiscovery

出现在:

BootstrapTokenDiscovery 用来设置基于引导令牌的服务发现选项。

字段描述
token [必需]
string

token 是用来验证从控制平面获得的集群信息的令牌。

apiServerEndpoint
string

apiServerEndpoint 为 API 服务器的 IP 地址或者域名,从该端点可以获得集群信息。

caCertHashes
[]string

caCertHashes 设置一组在基于令牌来发现服务时要验证的公钥指纹。 发现过程中获得的根 CA 必须与这里的数值之一匹配。设置为空集合意味着禁用根 CA 指纹, 因而可能是不安全的。每个哈希值的形式为 <type>:<value>, 当前唯一支持的 type 为 "sha256"。 哈希值为主体公钥信息(Subject Public Key Info,SPKI)对象的 SHA-256 哈希值(十六进制编码),形式为 DER 编码的 ASN.1。 例如,这些哈希值可以使用 OpenSSL 来计算。

unsafeSkipCAVerification
bool

unsafeSkipCAVerification 允许在使用基于令牌的服务发现时不使用 caCertHashes 来执行 CA 验证。这会弱化 kubeadm 的安全性, 因为其他节点可以伪装成控制平面。

ControlPlaneComponent

出现在:

ControlPlaneComponent 中包含对集群中所有控制平面组件都适用的设置。

字段描述
extraArgs
map[string]string

extraArgs 是要传递给控制平面组件的一组额外的参数标志。 此映射中的每个键对应命令行上使用的标志名称,只是没有其引导连字符。 TODO:这只是暂时的,我们希望将所有组件切换为使用 ComponentConfig + ConfigMap。

extraVolumes
[]HostPathMount

extraVolumes 是一组额外的主机卷,需要挂载到控制平面组件中。

extraEnvs
[]core/v1.EnvVar

extraEnvs 是要传递给控制平面组件的额外环境变量集合。 使用 extraEnvs 传递的环境变量将会覆盖所有现有的环境变量或是 kubeadm 默认添加的 *_proxy 环境变量。

DNS

出现在:

DNS 结构定义要在集群中使用的 DNS 插件。

字段描述
ImageMeta [必需]
ImageMeta
ImageMeta 的成员被内嵌到此类型中)。

imageMeta 允许对 DNS 组件所使用的的镜像作定制。

Discovery

出现在:

Discovery 设置 TLS 启动引导过程中 kubelet 要使用的配置选项。

字段描述
bootstrapToken
BootstrapTokenDiscovery

bootstrapToken 设置基于启动引导令牌的服务发现选项。 bootstrapTokenfile 是互斥的。

file
FileDiscovery

file 用来设置一个文件或者 URL 路径,指向一个 kubeconfig 文件; 该配置文件中包含集群信息。 bootstrapTokenfile 是互斥的。

tlsBootstrapToken
string

tlsBootstrapToken 是 TLS 启动引导过程中使用的令牌。 如果设置了 bootstrapToken,则此字段默认值为 .bootstrapToken.token, 不过可以被重载。如果设置了 file,此字段必须被设置,以防 kubeconfig 文件中不包含其他身份认证信息。

timeout
meta/v1.Duration

timeout 用来修改发现过程的超时时长。

Etcd

出现在:

Etcd 包含用来描述 etcd 配置的元素。

字段描述
local
LocalEtcd

local 提供配置本地 etcd 实例的选项。 localexternal 是互斥的。

external
ExternalEtcd

external 描述如何连接到外部的 etcd 集群。 external 是互斥的。

ExternalEtcd

出现在:

ExternalEtcd 描述外部 etcd 集群。 kubeadm 不清楚证书文件的存放位置,因此必须单独提供证书信息。

字段描述
endpoints [必需]
[]string

endpoints 包含一组 etcd 成员的列表。

caFile [必需]
string

caFile 是一个 SSL 证书机构(CA)文件,用来加密 etcd 通信。 如果使用 TLS 连接,此字段为必需字段。

certFile [必需]
string

certFile 是一个 SSL 证书文件,用来加密 etcd 通信。 如果使用 TLS 连接,此字段为必需字段。

keyFile [必需]
string

keyFile 是一个用来加密 etcd 通信的 SSL 秘钥文件。 此字段在使用 TLS 连接时为必填字段。

FileDiscovery

出现在:

FileDiscovery 用来指定一个文件或者 URL 路径,指向一个 kubeconfig 文件; 该配置文件可用来加载集群信息。

字段描述
kubeConfigPath [必需]
string

kubeConfigPath 用来指定一个文件或者 URL 路径,指向一个 kubeconfig 文件; 该配置文件可用来加载集群信息。

HostPathMount

出现在:

HostPathMount 包含从宿主节点挂载的卷的信息。

字段描述
name [必需]
string

name 为卷在 Pod 模板中的名称。

hostPath [必需]
string

hostPath 是要在 Pod 中挂载的卷在宿主系统上的路径。

mountPath [必需]
string

mountPathhostPath 在 Pod 内挂载的路径。

readOnly
bool

readOnly 控制卷的读写访问模式。

pathType
core/v1.HostPathType

pathTypehostPath 的类型。

ImageMeta

出现在:

ImageMeta 用来配置来源不是 Kubernetes/Kubernetes 发布过程的组件所使用的镜像。

字段描述
imageRepository
string

imageRepository 设置镜像拉取所用的容器仓库。 若未设置,则使用 ClusterConfiguration 中的 imageRepository

imageTag
string

imageTag 允许用户设置镜像的标签。 如果设置了此字段,则 kubeadm 不再在集群升级时自动更改组件的版本。

JoinControlPlane

出现在:

JoinControlPlane 包含在正在加入集群的节点上要部署的额外的控制平面组件的设置。

字段描述
localAPIEndpoint
APIEndpoint

localAPIEndpoint 代表的是将在此节点上部署的 API 服务器实例的端点。

certificateKey
string

certificateKey 是在添加新的控制平面节点时用来解密所下载的 Secret 中的证书的秘钥。对应的加密秘钥在 InitConfiguration 结构中。

LocalEtcd

出现在:

LocalEtcd 描述的是 kubeadm 要使用的本地 etcd 集群。

字段描述
ImageMeta [必需]
ImageMeta
ImageMeta 结构的字段被嵌入到此类型中。)

ImageMeta 允许用户为 etcd 定制要使用的容器。

dataDir [必需]
string

dataDir 是 etcd 用来存放数据的目录。 默认值为 "/var/lib/etcd"。

extraArgs
map[string]string

extraArgs 是为 etcd 可执行文件提供的额外参数,用于在静态 Pod 中运行 etcd。映射中的每一个键对应命令行上的一个标志参数,只是去掉了前置的连字符。

extraEnvs
[]core/v1.EnvVar

extraEnvs 是要传递给控制平面组件的额外环境变量集合。 使用 ExtraEnvs 传递的环境变量将会覆盖任何现有的环境变量或是 kubeadm 默认添加的 *_proxy 环境变量。

serverCertSANs
[]string

serverCertSANs 为 etcd 服务器的签名证书设置额外的主体替代名 (Subject Alternative Names,SAN)。

peerCertSANs
[]string

peerCertSANs 为 etcd 的对等端签名证书设置额外的主体替代名 (Subject Alternative Names,SAN)。

Networking

出现在:

Networking 中包含描述集群网络配置的元素。

字段描述
serviceSubnet
string

serviceSubnet 是 Kubernetes 服务所使用的的子网。 默认值为 "10.96.0.0/12"。

podSubnet
string

podSubnet 为 Pod 所使用的子网。

dnsDomain
string

dnsDomain 是 Kubernetes 服务所使用的的 DNS 域名。 默认值为 "cluster.local"。

NodeRegistrationOptions

出现在:

NodeRegistrationOptions 包含向集群中注册新的控制平面或节点所需要的信息; 节点注册可能通过 "kubeadm init" 或 "kubeadm join" 完成。

字段描述
name
string

name 是 Node API 对象的 .metadata.name 字段值; 该 API 对象会在此 kubeadm initkubeadm join 操作期间创建。 在提交给 API 服务器的 kubelet 客户端证书中,此字段也用作其 CommonName。 如果未指定则默认为节点的主机名。

criSocket
string

criSocket 用来读取容器运行时的信息。此信息会被以注解的方式添加到 Node API 对象至上,用于后续用途。

taints [必需]
[]core/v1.Taint

taints 设定 Node API 对象被注册时要附带的污点。 若未设置此字段(即字段值为 null),默认为控制平面节点添加控制平面污点。 如果你不想为控制平面节点添加污点,可以将此字段设置为空列表(即 YAML 文件中的 taints: []), 这个字段只用于节点注册。

kubeletExtraArgs
map[string]string

kubeletExtraArgs 用来向 kubelet 传递额外参数。 这里的参数会通过 kubeadm 在运行时写入的、由 kubelet 来读取的环境文件来传递给 kubelet 命令行。 这里的设置会覆盖掉 kubelet-config ConfigMap 中包含的一般性的配置。 命令行标志在解析时优先级更高。这里的设置值仅作用于 kubeadm 运行所在的节点。 映射中的每个键对应命令行中的一个标志参数,只是去掉了前置的连字符。

ignorePreflightErrors
[]string

ignorePreflightErrors 提供一组在当前节点被注册时可以忽略掉的预检错误。 例如:IsPrevilegedUser,Swap。 取值 all 忽略所有检查的错误。

imagePullPolicy
core/v1.PullPolicy

imagePullPolicy 设定 "kubeadm init" 和 "kubeadm join" 操作期间的镜像拉取策略。此字段的取值可以是 "Always"、"IfNotPresent" 或 "Never" 之一。若此字段未设置,则 kubeadm 使用 "IfNotPresent" 作为其默认值, 换言之,当镜像在主机上不存在时才执行拉取操作。

Patches

出现在:

Patches 包含要向 kubeadm 所部署的组件应用的补丁信息。

字段描述
directory
string

directory 是指向某目录的路径,该目录中包含名为 "target[suffix][+patchtype].extension" 的文件。 例如,"kube-apiserver0+merge.yaml" 或者 "etcd.json"。 "target" 可以是 "kube-apiserver"、"kube-controller-manager"、 "kube-scheduler"、"etcd" 之一。 "patchtype" 可以是 "strategic"、"merge" 或者 "json", 其取值对应 kubectl 所支持的补丁形式。 "patchtype" 的默认值是 "strategic"。 "extension" 必须是 "json" 或者 "yaml"。 "suffix" 是一个可选的字符串,用来确定按字母表顺序来应用时,哪个补丁最先被应用。

6.14.14 - Kubelet CredentialProvider (v1)

资源类型

CredentialProviderRequest

CredentialProviderRequest 包含 kubelet 需要通过身份验证才能访问的镜像。 kubelet 将此请求对象通过 stdin 传递到插件。 通常,插件应优先使用所收到的 apiVersion 作出响应。

字段描述
apiVersion
string
credentialprovider.kubelet.k8s.io/v1
kind
string
CredentialProviderRequest
image [必需]
string

image 是作为凭据提供程序插件请求的一部分所拉取的容器镜像。 这些插件可以选择解析镜像以提取获取凭据所需的任何信息。

CredentialProviderResponse

CredentialProviderResponse 中包含 kubelet 应针对原始请求中所给镜像来使用的凭据。 kubelet 将通过 stdout 读取来自插件的响应。 此响应应被设置为与 CredentialProviderRequest 相同的 apiVersion。

字段描述
apiVersion
string
credentialprovider.kubelet.k8s.io/v1
kind
string
CredentialProviderResponse
cacheKeyType [必需]
PluginCacheKeyType

cacheKeyType 标示了基于请求中提供的镜像要使用的缓存键的类型。 缓存键类型有三个有效值:Image、Registry 和 Global。 如果所指定的值无效,则此响应不会被 kubelet 使用。

cacheDuration
meta/v1.Duration

cacheDuration 标示所提供的凭据可被缓存的持续期。 kubelet 将使用此字段为 AuthConfig 中的凭据设置内存中缓存持续期。 如果为空,kubelet 将使用 CredentialProviderConfig 中提供的 defaultCacheDuration。 如果设置为 0,kubelet 将不再缓存提供的 AuthConfig。

auth
map[string]AuthConfig

auth 是一个映射,包含传递给 kubelet 的身份验证信息。 映射中每个键都是一个匹配镜像字符串(更多内容见下文)。 相应的 authConfig 值应该对匹配此键的所有镜像有效。 如果无法为请求的镜像返回有效凭据,则插件应将此字段设置为空。

映射中的每个主键都可以包含端口和路径。 域名中可以使用 Glob 通配,但不能在端口或路径中使用 Glob。 Glob 支持类似 *.k8s.iok8s.*.io 这类子域以及 k8s.* 这类顶级域。 也支持匹配的部分子域,例如 app*.k8s.io。 每个 Glob 只能匹配一个子域段,因此 *.io*.k8s.io 不匹配。

当满足以下所有条件时,kubelet 将根据主键来匹配镜像:

  • 两者都包含相同数量的域名部分,并且每个部分都匹配。
  • imageMatch 的 URL 路径必须是目标镜像 URL 路径的前缀。
  • 如果 imageMatch 包含端口,则此端口也必须在镜像中匹配。

当返回多个主键时,kubelet 将以相反的顺序遍历所有主键,以便:

  • 较长键出现在具有相同前缀的较短键前面。
  • 非通配符键出现在具有相同前缀的通配符键之前。

对于任一给定的匹配项,kubelet 将尝试用提供的凭据拉取镜像,并在第一次成功通过身份验证的拉取之后停止。

示例键:

  • 123456789.dkr.ecr.us-east-1.amazonaws.com
  • *.azurecr.io
  • gcr.io
  • *.*.registry.io
  • registry.io:8080/path

AuthConfig

出现在:

AuthConfig 包含针对容器镜像仓库的身份验证信息。 目前仅支持基于用户名/密码的身份验证,但未来可能添加更多的身份验证机制。

字段描述
username [必需]
string

username 是对容器镜像仓库身份验证所用的用户名。 空白用户名是有效的。

password [必需]
string

password 是对容器镜像仓库身份验证所用的密码。 空白密码是有效的。

PluginCacheKeyType

string 的别名)

出现在:

6.14.15 - Kubelet 配置 (v1)

资源类型

CredentialProviderConfig

CredentialProviderConfig 包含有关每个 exec 凭据提供程序的配置信息。 Kubelet 从磁盘上读取这些配置信息,并根据 CredentialProvider 类型启用各个提供程序。

字段描述
apiVersion
string
kubelet.config.k8s.io/v1
kind
string
CredentialProviderConfig
providers [必需]
[]CredentialProvider

providers 是一组凭据提供程序插件,这些插件会被 kubelet 启用。 多个提供程序可以匹配到同一镜像上,这时,来自所有提供程序的凭据信息都会返回给 kubelet。 如果针对同一镜像调用了多个提供程序,则结果会被组合起来。如果提供程序返回的认证主键有重复, 列表中先出现的提供程序所返回的值将被使用。

CredentialProvider

出现在:

CredentialProvider 代表的是要被 kubelet 调用的一个 exec 插件。 这一插件只会在所拉取的镜像与该插件所处理的镜像匹配时才会被调用(参见 matchImages)。

字段描述
name [必需]
string

name 是凭据提供程序的名称(必需)。此名称必须与 kubelet 所看到的提供程序可执行文件的名称匹配。可执行文件必须位于 kubelet 的 bin 目录(通过 --image-credential-provider-bin-dir 设置)下。

matchImages [必需]
[]string

matchImages 是一个必须设置的字符串列表,用来匹配镜像以便确定是否要调用此提供程序。 如果字符串之一与 kubelet 所请求的镜像匹配,则此插件会被调用并给予提供凭据的机会。 镜像应该包含镜像库域名和 URL 路径。

matchImages 中的每个条目都是一个模式字符串,其中可以包含端口号和路径。 域名部分可以包含通配符,但端口或路径部分不可以。'*.k8s.io' 或 'k8s.*.io' 等子域名以及 'k8s.*' 这类顶级域名都支持通配符。

对于 'app.k8s.io' 这类部分子域名的匹配也是支持的。 每个通配符只能用来匹配一个子域名段,所以 *.io 不会匹配 *.k8s.io。

镜像与 matchImages 之间存在匹配时,以下条件都要满足:

  • 二者均包含相同个数的域名部分,并且每个域名部分都对应匹配;
  • matchImages 条目中的 URL 路径部分必须是目标镜像的 URL 路径的前缀;
  • 如果 matchImages 条目中包含端口号,则端口号也必须与镜像端口号匹配。

matchImages 的一些示例如下:

  • 123456789.dkr.ecr.us-east-1.amazonaws.com
  • *.azurecr.io
  • gcr.io
  • *.*.registry.io
  • registry.io:8080/path
defaultCacheDuration [必需]
meta/v1.Duration

defaultCacheDuration 是插件在内存中缓存凭据的默认时长, 在插件响应中没有给出缓存时长时,使用这里设置的值。此字段是必需的。

apiVersion [必需]
string

要求 exec 插件 CredentialProviderRequest 请求的输入版本。 所返回的 CredentialProviderResponse 必须使用与输入相同的编码版本。当前支持的值有:

  • credentialprovider.kubelet.k8s.io/v1
args
[]string

在执行插件可执行文件时要传递给命令的参数。

env
[]ExecEnvVar

env 定义要提供给插件进程的额外的环境变量。 这些环境变量会与主机上的其他环境变量以及 client-go 所使用的环境变量组合起来, 一起传递给插件。

ExecEnvVar

出现在:

ExecEnvVar 用来在执行基于 exec 的凭据插件时设置环境变量。

字段描述
name [必需]
string
环境变量名称
value [必需]
string
环境变量取值

6.14.16 - Kubelet 配置 (v1alpha1)

资源类型

CredentialProviderConfig

CredentialProviderConfig 包含有关每个 exec 凭据提供者的配置信息。 Kubelet 从磁盘上读取这些配置信息,并根据 CredentialProvider 类型启用各个提供者。

字段描述
apiVersion
string
kubelet.config.k8s.io/v1alpha1
kind
string
CredentialProviderConfig
providers [必需]
[]CredentialProvider
providers 是一组凭据提供者插件,这些插件会被 kubelet 启用。 多个提供者可以匹配到同一镜像上,这时,来自所有提供者的凭据信息都会返回给 kubelet。 如果针对同一镜像调用了多个提供者,则结果会被组合起来。如果提供者返回的认证主键有重复, 列表中先出现的提供者所返回的值将被使用。

CredentialProvider

出现在:

CredentialProvider 代表的是要被 kubelet 调用的一个 exec 插件。 这一插件只会在所拉取的镜像与该插件所处理的镜像匹配时才会被调用(参见 matchImages)。

字段描述
name [必需]
string
name 是凭据提供者的名称(必需)。此名称必须与 kubelet 所看到的提供者可执行文件的名称匹配。可执行文件必须位于 kubelet 的 bin 目录(通过 --image-credential-provider-bin-dir 设置)下。
matchImages [必需]
[]string

matchImages 是一个必须设置的字符串列表,用来匹配镜像以便确定是否要调用此提供者。 如果字符串之一与 kubelet 所请求的镜像匹配,则此插件会被调用并给予提供凭证的机会。 镜像应该包含镜像库域名和 URL 路径。

matchImages 中的每个条目都是一个模式字符串,其中可以包含端口号和路径。 域名部分可以包含统配符,但端口或路径部分不可以。通配符可以用作子域名,例如 *.k8s.iok8s.*.io,以及顶级域名,如 k8s.*

对类似 app*.k8s.io 这类部分子域名的匹配也是支持的。 每个通配符只能用来匹配一个子域名段,所以 *.io 不会匹配 *.k8s.io

镜像与 matchImages 之间存在匹配时,以下条件都要满足:

  • 二者均包含相同个数的域名部分,并且每个域名部分都对应匹配;
  • matchImages 条目中的 URL 路径部分必须是目标镜像的 URL 路径的前缀;
  • 如果 matchImages 条目中包含端口号,则端口号也必须与镜像端口号匹配。

matchImages 的一些示例如下:

  • 123456789.dkr.ecr.us-east-1.amazonaws.com
  • *.azurecr.io
  • gcr.io
  • *.*.registry.io
  • registry.io:8080/path
defaultCacheDuration [必需]
meta/v1.Duration
defaultCacheDuration 是插件在内存中缓存凭据的默认时长, 在插件响应中没有给出缓存时长时,使用这里设置的值。此字段是必需的。
apiVersion [必需]
string

要求 exec 插件 CredentialProviderRequest 请求的输入版本。 所返回的 CredentialProviderResponse 必须使用与输入相同的编码版本。当前支持的值有:

  • credentialprovider.kubelet.k8s.io/v1alpha1
args
[]string
在执行插件可执行文件时要传递给命令的参数。
env
[]ExecEnvVar
env 定义要提供给插件进程的额外的环境变量。 这些环境变量会与主机上的其他环境变量以及 client-go 所使用的环境变量组合起来, 一起传递给插件。

ExecEnvVar

出现在:

ExecEnvVar 用来在执行基于 exec 的凭据插件时设置环境变量。

字段描述
name [必需]
string
环境变量名称。
value [必需]
string
环境变量取值。

6.14.17 - Kubelet 配置 (v1beta1)

资源类型

FormatOptions

出现在:

FormatOptions 包含为不同日志格式提供的选项。

字段描述
json [必需]
JSONOptions

[Alpha] json 包含 "json" 日志格式的选项。 只有 LoggingAlphaOptions 特性门控被启用时才可用。

JSONOptions

出现在:

JSONOptions 包含为 "json" 日志格式提供的选项。

字段描述
splitStream [必需]
bool

[Alpha] splitStream 将错误信息重定向到标准错误输出(stderr), 而将提示信息重定向到标准输出(stdout),并为二者提供缓存。 默认设置是将二者都写出到标准输出,并且不提供缓存。 只有 LoggingAlphaOptions 特性门控被启用时才可用。

infoBufferSize [必需]
k8s.io/apimachinery/pkg/api/resource.QuantityValue

[Alpha] infoBufferSize 在分离数据流时用来设置 info 数据流的大小。 默认值为 0,相当于禁止缓存。只有 LoggingAlphaOptions 特性门控被启用时才可用。

LogFormatFactory

LogFormatFactory 提供了对某些附加的、非默认的日志格式的支持。

LoggingConfiguration

出现在:

LoggingConfiguration 包含日志选项。

字段描述
format [必需]
string

format 设置日志消息的结构。默认的格式取值为 text

flushFrequency [必需]
TimeOrMetaDuration

日志清洗之间的最大时间间隔。 如果是字符串,则解析为持续时间(例如 "1s")。 如果是整数,则表示为最大纳秒数(例如 1s = 1000000000)。 如果所选的日志后端在写入日志消息时未缓冲,则被忽略。

verbosity [必需]
VerbosityLevel

verbosity 用来确定日志消息记录的详细程度阈值。默认值为 0, 意味着仅记录最重要的消息。数值越大,额外的消息越多。出错消息总是会被记录下来。

vmodule [必需]
VModuleConfiguration

vmodule 会在单个文件层面重载 verbosity 阈值的设置。 这一选项仅支持 "text" 日志格式。

options [必需]
FormatOptions

[Alpha] options 中包含特定于不同日志格式的附加参数。 只有针对所选格式的选项会被使用,但是合法性检查时会查看所有参数。 只有 LoggingAlphaOptions 特性门控被启用时才可用。

LoggingOptions

LoggingOptions 可以与 ValidateAndApplyWithOptions 一起使用,以覆盖某些全局默认值。

字段描述
ErrorStream [必需]
io.Writer

ErrorStream 可用于覆盖默认值 os.Stderr

InfoStream [必需]
io.Writer

InfoStream 可用于覆盖默认值 os.Stdout

TimeOrMetaDuration

出现在:

TimeOrMetaDuration 仅出于向后兼容 flushFrequency 字段而存在, 新字段应使用 metav1.Duration

字段描述
Duration [必需]
meta/v1.Duration

Duration 保存持续时间。

- [必需]
bool

SerializeAsString 控制此值是以字符串还是以整数进行序列化。

TracingConfiguration

出现在:

TracingConfiguration 为 OpenTelemetry 追踪客户端提供版本化的配置信息。

字段描述
endpoint
string

采集器的端点,此组件将向其报告追踪链路。 此连接不安全,目前不支持 TLS。推荐不设置,端点是 otlp grpc 默认值 localhost:4317。

samplingRatePerMillion
int32

samplingRatePerMillion 是每百万 span 要采集的样本数。推荐不设置。 如果不设置,则采样器优先使用其父级 span 的采样率,否则不采样。

VModuleConfiguration

[]k8s.io/component-base/logs/api/v1.VModuleItem 的别名)

出现在:

VModuleConfiguration 是一个集合,其中包含一个个文件名(或文件名模式) 及其对应的详细程度阈值。

VerbosityLevel

uint32 的别名)

出现在:

VerbosityLevel 表示 klog 或 logr 的详细程度(verbosity)阈值。

CredentialProviderConfig

CredentialProviderConfig 包含有关每个 exec 凭据提供者的配置信息。 Kubelet 从磁盘上读取这些配置信息,并根据 CredentialProvider 类型启用各个提供者。

字段描述
apiVersion
string
kubelet.config.k8s.io/v1beta1
kind
string
CredentialProviderConfig
providers [必需]
[]CredentialProvider

providers 是一组凭据提供者插件,这些插件会被 kubelet 启用。 多个提供者可以匹配到同一镜像上,这时,来自所有提供者的凭据信息都会返回给 kubelet。 如果针对同一镜像调用了多个提供者,则结果会被组合起来。如果提供者返回的认证主键有重复, 列表中先出现的提供者所返回的值将被使用。

KubeletConfiguration

KubeletConfiguration 中包含 Kubelet 的配置。

字段描述
apiVersion
string
kubelet.config.k8s.io/v1beta1
kind
string
KubeletConfiguration
enableServer [必需]
bool

enableServer 会启用 kubelet 的安全服务器。

注意:kubelet 的不安全端口由 readOnlyPort 选项控制。

默认值:true

staticPodPath
string

staticPodPath 是指向要运行的本地(静态)Pod 的目录, 或者指向某个静态 Pod 文件的路径。

默认值:""

syncFrequency
meta/v1.Duration

syncFrequency 是对运行中的容器和配置进行同步的最长周期。

默认值:"1m"

fileCheckFrequency
meta/v1.Duration

fileCheckFrequency 是对配置文件中新数据进行检查的时间间隔值。

默认值:"20s"

httpCheckFrequency
meta/v1.Duration

httpCheckFrequency 是对 HTTP 服务器上新数据进行检查的时间间隔值。

默认值:"20s"

staticPodURL
string

staticPodURL 是访问要运行的静态 Pod 的 URL 地址。

默认值:""

staticPodURLHeader
map[string][]string

staticPodURLHeader是一个由字符串组成的映射表,其中包含的 HTTP 头部信息用于访问podURL

默认值:nil

address
string

address 是 kubelet 提供服务所用的 IP 地址(设置为 0.0.0.0 使用所有网络接口提供服务)。

默认值:"0.0.0.0"

port
int32

port 是 kubelet 用来提供服务所使用的端口号。 这一端口号必须介于 1 到 65535 之间,包含 1 和 65535。

默认值:10250

readOnlyPort
int32

readOnlyPort 是 kubelet 用来提供服务所使用的只读端口号。 此端口上的服务不支持身份认证或鉴权。这一端口号必须介于 1 到 65535 之间, 包含 1 和 65535。将此字段设置为 0 会禁用只读服务。

默认值:0(禁用)

tlsCertFile
string

tlsCertFile 是包含 HTTPS 所需要的 x509 证书的文件 (如果有 CA 证书,会串接到服务器证书之后)。如果tlsCertFiletlsPrivateKeyFile 都没有设置,则系统会为节点的公开地址生成自签名的证书和私钥, 并将其保存到 kubelet --cert-dir 参数所指定的目录下。

默认值:""

tlsPrivateKeyFile
string

tlsPrivateKeyFile 是一个包含与 tlsCertFile 证书匹配的 X509 私钥的文件。

默认值:""

tlsCipherSuites
[]string

tlsCipherSuites 是一个字符串列表,其中包含服务器所接受的加密包名称。 请注意,TLS 1.3 密码套件是不可配置的。 列表中的每个值来自于 tls 包中定义的常数(https://pkg.go.dev/crypto/tls#pkg-constants)。

默认值:nil

tlsMinVersion
string

tlsMinVersion 给出所支持的最小 TLS 版本。 字段取值来自于 tls 包中的常数定义(https://pkg.go.dev/crypto/tls#pkg-constants)。

默认值:""

rotateCertificates
bool

rotateCertificates 用来启用客户端证书轮换。kubelet 会调用 certificates.k8s.io API 来请求新的证书。需要有一个批复人批准证书签名请求。

默认值:false

serverTLSBootstrap
bool

serverTLSBootstrap 用来启用服务器证书引导。系统不再使用自签名的服务证书, kubelet 会调用 certificates.k8s.io API 来请求证书。 需要有一个批复人来批准证书签名请求(CSR)。 设置此字段时,RotateKubeletServerCertificate 特性必须被启用。

默认值:false

authentication
KubeletAuthentication

authentication 设置发送给 kubelet 服务器的请求是如何进行身份认证的。

默认值:

anonymous: enabled: false webhook: enabled: true cacheTTL: "2m"

authorization
KubeletAuthorization

authorization 设置发送给 kubelet 服务器的请求是如何进行鉴权的。

默认值:

mode: Webhook webhook: cacheAuthorizedTTL: "5m" cacheUnauthorizedTTL: "30s"

registryPullQPS
int32

registryPullQPS 是每秒钟可以执行的镜像仓库拉取操作限值。 此值必须不能为负数。将其设置为 0 表示没有限值。

默认值:5

registryBurst
int32

registryBurst 是突发性镜像拉取的上限值,允许镜像拉取临时上升到所指定数量, 不过仍然不超过 registryPullQPS 所设置的约束。此值必须是非负值。 只有 registryPullQPS 参数值大于 0 时才会使用此设置。

默认值:10

eventRecordQPS
int32

eventRecordQPS 设置每秒钟可创建的事件个数上限。如果此值为 0, 则表示没有限制。此值不能设置为负数。

默认值:50

eventBurst
int32

eventBurst 是突发性事件创建的上限值,允许事件创建临时上升到所指定数量, 不过仍然不超过 eventRecordQPS所设置的约束。此值必须是非负值, 且只有 eventRecordQPS > 0 时才会使用此设置。

默认值:100

enableDebuggingHandlers
bool

enableDebuggingHandlers 启用服务器上用来访问日志、 在本地运行容器和命令的端点,包括 execattachlogsportforward 等功能。

默认值:true

enableContentionProfiling
bool

enableContentionProfiling 用于启用阻塞性能分析, 仅用于 enableDebuggingHandlerstrue 的场合。

默认值:false

healthzPort
int32

healthzPort 是本地主机上提供 healthz 端点的端口 (设置值为 0 时表示禁止)。合法值介于 1 和 65535 之间。

默认值:10248

healthzBindAddress
string

healthzBindAddresshealthz 服务器用来提供服务的 IP 地址。

默认值:"127.0.0.1"

oomScoreAdj
int32

oomScoreAdj 是为 kubelet 进程设置的 oom-score-adj 值。 所设置的取值要在 [-1000, 1000] 范围之内。

默认值:-999

clusterDomain
string

clusterDomain 是集群的 DNS 域名。如果设置了此字段,kubelet 会配置所有容器,使之在搜索主机的搜索域的同时也搜索这里指定的 DNS 域。

默认值:""

clusterDNS
[]string

clusterDNS 是集群 DNS 服务器的 IP 地址的列表。 如果设置了,kubelet 将会配置所有容器使用这里的 IP 地址而不是宿主系统上的 DNS 服务器来完成 DNS 解析。

默认值:nil

streamingConnectionIdleTimeout
meta/v1.Duration

streamingConnectionIdleTimeout 设置流式连接在被自动关闭之前可以空闲的最长时间。

默认值:"4h"

nodeStatusUpdateFrequency
meta/v1.Duration

nodeStatusUpdateFrequency 是 kubelet 计算节点状态的频率。 如果未启用节点租约特性,这一字段设置的也是 kubelet 向控制面投递节点状态的频率。

注意:如果节点租约特性未被启用,更改此参数设置时要非常小心, 所设置的参数值必须与节点控制器的 nodeMonitorGracePeriod 协同。

默认值:"10s"

nodeStatusReportFrequency
meta/v1.Duration

nodeStatusReportFrequency 是节点状态未发生变化时,kubelet 向控制面更新节点状态的频率。如果节点状态发生变化,则 kubelet 会忽略这一频率设置, 立即更新节点状态。

此字段仅当启用了节点租约特性时才被使用。nodeStatusReportFrequency 的默认值是"5m"。不过,如果 nodeStatusUpdateFrequency 被显式设置了,则 nodeStatusReportFrequency 的默认值会等于 nodeStatusUpdateFrequency 值,这是为了实现向后兼容。

默认值:"5m"

nodeLeaseDurationSeconds
int32

nodeLeaseDurationSeconds 是 kubelet 会在其对应的 Lease 对象上设置的时长值。 NodeLease 让 kubelet 来在 kube-node-lease 名字空间中创建按节点名称命名的租约并定期执行续约操作,并通过这种机制来了解节点健康状况。

如果租约过期,则节点可被视作不健康。根据 KEP-0009 约定,目前的租约每 10 秒钟续约一次。 在将来,租约的续约时间间隔可能会根据租约的时长来设置。

此字段的取值必须大于零。

默认值:40

imageMinimumGCAge
meta/v1.Duration

imageMinimumGCAge 是对未使用镜像进行垃圾收集之前允许其存在的时长。

默认值:"2m"

imageMaximumGCAge
meta/v1.Duration

imageMaximumGCAge 是对未使用镜像进行垃圾收集之前允许其存在的时长。 此字段的默认值为 "0s",表示禁用此字段,这意味着镜像不会因为过长时间不使用而被垃圾收集。 默认值:"0s"(已禁用)

imageGCHighThresholdPercent
int32

imageGCHighThresholdPercent 所给的是镜像的磁盘用量百分数, 一旦镜像用量超过此阈值,则镜像垃圾收集会一直运行。百分比是用这里的值除以 100 得到的,所以此字段取值必须介于 0 和 100 之间,包括 0 和 100。如果设置了此字段, 则取值必须大于 imageGCLowThresholdPercent 取值。

默认值:85

imageGCLowThresholdPercent
int32

imageGCLowThresholdPercent 所给的是镜像的磁盘用量百分数, 镜像用量低于此阈值时不会执行镜像垃圾收集操作。垃圾收集操作也将此作为最低磁盘用量边界。 百分比是用这里的值除以 100 得到的,所以此字段取值必须介于 0 和 100 之间,包括 0 和 100。 如果设置了此字段,则取值必须小于 imageGCHighThresholdPercent 取值。

默认值:80

volumeStatsAggPeriod
meta/v1.Duration

volumeStatsAggPeriod 是计算和缓存所有 Pod 磁盘用量的频率。

默认值:"1m"

kubeletCgroups
string

kubeletCgroups 是用来隔离 kubelet 的控制组(CGroup)的绝对名称。

默认值:""

systemCgroups
string

systemCgroups 是用来放置那些未被容器化的、非内核的进程的控制组 (CGroup)的绝对名称。设置为空字符串表示没有这类容器。回滚此字段设置需要重启节点。 当此字段非空时,必须设置 cgroupRoot 字段。

默认值:""

cgroupRoot
string

cgroupRoot 是用来运行 Pod 的控制组(CGroup)。 容器运行时会尽可能处理此字段的设置值。

cgroupsPerQOS
bool

cgroupsPerQOS 用来启用基于 QoS 的控制组(CGroup)层次结构: 顶层的控制组用于不同 QoS 类,所有 BurstableBestEffort Pod 都会被放置到对应的顶级 QoS 控制组下。

默认值:true

cgroupDriver
string

cgroupDriver 是 kubelet 用来操控宿主系统上控制组(CGroup) 的驱动程序(cgroupfs 或 systemd)。

默认值:"cgroupfs"

cpuManagerPolicy
string

cpuManagerPolicy 是要使用的策略名称。需要启用 CPUManager 特性门控。

默认值:"None"

cpuManagerPolicyOptions
map[string]string

cpuManagerPolicyOptions 是一组 key=value 键值映射, 容许通过额外的选项来精细调整 CPU 管理器策略的行为。需要 CPUManagerCPUManagerPolicyOptions 两个特性门控都被启用。

默认值:nil

cpuManagerReconcilePeriod
meta/v1.Duration

cpuManagerReconcilePeriod 是 CPU 管理器的协调周期时长。 要求启用 CPUManager 特性门控。默认值:"10s"

memoryManagerPolicy
string

memoryManagerPolicy 是内存管理器要使用的策略的名称。 要求启用 MemoryManager 特性门控。

默认值:"none"

topologyManagerPolicy
string

topologyManagerPolicy 是要使用的拓扑管理器策略名称。合法值包括:

  • restricted:kubelet 仅接受在所请求资源上实现最佳 NUMA 对齐的 Pod。
  • best-effort:kubelet 会优选在 CPU 和设备资源上实现 NUMA 对齐的 Pod。
  • none:kubelet 不了解 Pod CPU 和设备资源 NUMA 对齐需求。
  • single-numa-node:kubelet 仅允许在 CPU 和设备资源上对齐到同一 NUMA 节点的 Pod。

默认值:"none"

topologyManagerScope
string

topologyManagerScope 代表的是拓扑提示生成的范围, 拓扑提示信息由提示提供者生成,提供给拓扑管理器。合法值包括:

  • container:拓扑策略是按每个容器来实施的。
  • pod:拓扑策略是按每个 Pod 来实施的。

默认值:"container"

topologyManagerPolicyOptions
map[string]string

TopologyManagerPolicyOptions 是一组 key=value 键值映射,容许设置额外的选项来微调拓扑管理器策略的行为。 需要同时启用 "TopologyManager" 和 "TopologyManagerPolicyOptions" 特性门控。 默认值:nil

qosReserved
map[string]string

qosReserved 是一组从资源名称到百分比值的映射,用来为 Guaranteed QoS 类型的负载预留供其独占使用的资源百分比。目前支持的资源为:"memory"。 需要启用 QOSReserved 特性门控。

默认值:nil

runtimeRequestTimeout
meta/v1.Duration

runtimeRequestTimeout 用来设置除长期运行的请求(pulllogsexecattach)之外所有运行时请求的超时时长。

默认值:"2m"

hairpinMode
string

hairpinMode 设置 kubelet 如何为发夹模式数据包配置容器网桥。 设置此字段可以让 Service 中的端点在尝试访问自身 Service 时将服务请求路由的自身。 可选值有:

  • "promiscuous-bridge":将容器网桥设置为混杂模式。
  • "hairpin-veth":在容器的 veth 接口上设置发夹模式标记。
  • "none":什么也不做。

一般而言,用户必须设置 --hairpin-mode=hairpin-veth 才能实现发夹模式的网络地址转译 (NAT),因为混杂模式的网桥要求存在一个名为 cbr0 的容器网桥。

默认值:"promiscuous-bridge"

maxPods
int32

maxPods 是此 kubelet 上课运行的 Pod 个数上限。此值必须为非负整数。

默认值:110

podCIDR
string

podCIDR 是用来设置 Pod IP 地址的 CIDR 值,仅用于独立部署模式。 运行于集群模式时,这一数值会从控制面获得。

默认值:""

podPidsLimit
int64

podPidsLimit 是每个 Pod 中可使用的 PID 个数上限。

默认值:-1

resolvConf
string

resolvConf 是一个域名解析配置文件,用作容器 DNS 解析配置的基础。

如果此值设置为空字符串,则会覆盖 DNS 解析的默认配置,本质上相当于禁用了 DNS 查询。

默认值:"/etc/resolv.conf"

runOnce
bool

runOnce 字段被设置时,kubelet 会咨询 API 服务器一次并获得 Pod 列表, 运行在静态 Pod 文件中指定的 Pod 及这里所获得的 Pod,然后退出。

默认值:false

cpuCFSQuota
bool

cpuCFSQuota 允许为设置了 CPU 限制的容器实施 CPU CFS 配额约束。

默认值:true

cpuCFSQuotaPeriod
meta/v1.Duration

cpuCFSQuotaPeriod 设置 CPU CFS 配额周期值,cpu.cfs_period_us。 此值需要介于 1 毫秒和 1 秒之间,包含 1 毫秒和 1 秒。 此功能要求启用 CustomCPUCFSQuotaPeriod 特性门控被启用。

默认值:"100ms"

nodeStatusMaxImages
int32

nodeStatusMaxImages 限制 Node.status.images 中报告的镜像数量。 此值必须大于 -2。

注意:如果设置为 -1,则不会对镜像数量做限制;如果设置为 0,则不会返回任何镜像。

默认值:50

maxOpenFiles
int64

maxOpenFiles 是 kubelet 进程可以打开的文件个数。此值必须不能为负数。

默认值:1000000

contentType
string

contentType 是向 API 服务器发送请求时使用的内容类型。

默认值:"application/vnd.kubernetes.protobuf"

kubeAPIQPS
int32

kubeAPIQPS 设置与 Kubernetes API 服务器通信时要使用的 QPS(每秒查询数)。

默认值:50

kubeAPIBurst
int32

kubeAPIBurst 设置与 Kubernetes API 服务器通信时突发的流量级别。 此字段取值不可以是负数。

默认值:100

serializeImagePulls
bool

serializeImagePulls 被启用时会通知 kubelet 每次仅拉取一个镜像。 我们建议不要在所运行的 Docker 守护进程版本低于 1.9、使用 aufs 存储后端的节点上更改默认值。详细信息可参见 Issue #10959。

默认值:true

maxParallelImagePulls
int32

maxParallelImagePulls 设置并行拉取镜像的最大数量。 如果 serializeImagePulls 为 true,则无法设置此字段。 把它设置为 nil 意味着没有限制。

默认值:true

evictionHard
map[string]string

evictionHard 是一个映射,是从信号名称到定义硬性驱逐阈值的映射。 例如:{"memory.available": "300Mi"}。 如果希望显式地禁用,可以在任意资源上将其阈值设置为 0% 或 100%。

默认值:

   memory.available:  "100Mi"
   nodefs.available:  "10%"
   nodefs.inodesFree: "5%"
   imagefs.available: "15%"
  
evictionSoft
map[string]string

evictionSoft 是一个映射,是从信号名称到定义软性驱逐阈值的映射。 例如:{"memory.available": "300Mi"}

默认值:nil

evictionSoftGracePeriod
map[string]string

evictionSoftGracePeriod 是一个映射,是从信号名称到每个软性驱逐信号的宽限期限。 例如:{"memory.available": "30s"}

默认值:nil

evictionPressureTransitionPeriod
meta/v1.Duration

evictionPressureTransitionPeriod 设置 kubelet 离开驱逐压力状况之前必须要等待的时长。

默认值:"5m"

evictionMaxPodGracePeriod
int32

evictionMaxPodGracePeriod 是指达到软性逐出阈值而引起 Pod 终止时, 可以赋予的宽限期限最大值(按秒计)。这个值本质上限制了软性逐出事件发生时, Pod 可以获得的 terminationGracePeriodSeconds

注意:由于 Issue #64530 的原因,系统中存在一个缺陷,即此处所设置的值会在软性逐出时覆盖 Pod 的宽限期设置,从而有可能增加 Pod 上原本设置的宽限期限时长。 这个缺陷会在未来版本中修复。

默认值:0

evictionMinimumReclaim
map[string]string

evictionMinimumReclaim 是一个映射,定义信号名称与最小回收量数值之间的关系。 最小回收量指的是资源压力较大而执行 Pod 驱逐操作时,kubelet 对给定资源的最小回收量。 例如:{"imagefs.available": "2Gi"}

默认值:nil

podsPerCore
int32

podsPerCore 设置的是每个核上 Pod 个数上限。此值不能超过 maxPods。 所设值必须是非负整数。如果设置为 0,则意味着对 Pod 个数没有限制。

默认值:0

enableControllerAttachDetach
bool

enableControllerAttachDetach 用来允许 Attach/Detach 控制器管理调度到本节点的卷的挂接(attachment)和解除挂接(detachement), 并且禁止 kubelet 执行任何 attach/detach 操作。

注意:kubelet 不支持挂接 CSI 卷和解除挂接, 因此对于该用例,此选项必须为 true。

默认值:true

protectKernelDefaults
bool

protectKernelDefaults 设置为 true 时,会令 kubelet 在发现内核参数与预期不符时出错退出。若此字段设置为 false,则 kubelet 会尝试更改内核参数以满足其预期。

默认值:false

makeIPTablesUtilChains
bool

makeIPTablesUtilChains 设置为 true 时,相当于允许 kubelet 在 iptables 中创建 KUBE-IPTABLES-HINT 链,提示其他组件有关系统上 iptables 的配置。

默认值:true

iptablesMasqueradeBit
int32

iptablesMasqueradeBit 以前用于控制 KUBE-MARK-MASQ 链的创建。

已弃用:不再有任何效果。

默认值:14

iptablesDropBit
int32

iptablesDropBit 以前用于控制 KUBE-MARK-DROP 链的创建。

已弃用:不再有任何效果。

默认值:15

featureGates
map[string]bool

featureGates 是一个从功能特性名称到布尔值的映射,用来启用或禁用实验性的功能。 此字段可逐条更改文件 "k8s.io/kubernetes/pkg/features/kube_features.go" 中所给的内置默认值。

默认值:nil

failSwapOn
bool

failSwapOn 通知 kubelet 在节点上启用交换分区时拒绝启动。

默认值:true

memorySwap
MemorySwapConfiguration

memorySwap 配置容器负载可用的交换内存。

containerLogMaxSize
string

containerLogMaxSize 是定义容器日志文件被轮转之前可以到达的最大尺寸。 例如:"5Mi" 或 "256Ki"。

默认值:"10Mi"

containerLogMaxFiles
int32

containerLogMaxFiles 设置每个容器可以存在的日志文件个数上限。

默认值:"5"

configMapAndSecretChangeDetectionStrategy
ResourceChangeDetectionStrategy

configMapAndSecretChangeDetectionStrategy 是 ConfigMap 和 Secret 管理器的运行模式。合法值包括:

  • Get:kubelet 从 API 服务器直接取回必要的对象;
  • Cache:kubelet 使用 TTL 缓存来管理来自 API 服务器的对象;
  • Watch:kubelet 使用 watch 操作来观察所关心的对象的变更。

默认值:"Watch"

systemReserved
map[string]string

systemReserved 是一组资源名称=资源数量对, 用来描述为非 Kubernetes 组件预留的资源(例如:'cpu=200m,memory=150G')。

目前仅支持 CPU 和内存。更多细节可参见 https://kubernetes.io/zh-cn/docs/concepts/configuration/manage-resources-containers/ 。

默认值:Nil

kubeReserved
map[string]string

kubeReserved 是一组资源名称=资源数量对, 用来描述为 Kubernetes 系统组件预留的资源(例如:'cpu=200m,memory=150G')。 目前支持 CPU、内存和根文件系统的本地存储。 更多细节可参见 https://kubernetes.io/zh-cn/docs/concepts/configuration/manage-resources-containers/。

默认值:Nil

reservedSystemCPUs [必需]
string

reservedSystemCPUs 选项设置为宿主级系统线程和 Kubernetes 相关线程所预留的 CPU 列表。此字段提供的是一种“静态”的 CPU 列表,而不是像 systemReservedkubeReserved 所提供的“动态”列表。 此选项不支持 systemReservedCgroupkubeReservedCgroup

showHiddenMetricsForVersion
string

showHiddenMetricsForVersion 是你希望显示隐藏度量值的上一版本。 只有上一个次版本是有意义的,其他值都是不允许的。 字段值的格式为 <major>.<minor>,例如:1.16。 此格式的目的是为了确保在下一个版本中有新的度量值被隐藏时,你有机会注意到这类变化, 而不是当这些度量值在其后的版本中彻底去除时来不及应对。

默认值:""

systemReservedCgroup
string

systemReservedCgroup 帮助 kubelet 识别用来为 OS 系统级守护进程实施 systemReserved 计算资源预留时使用的顶级控制组(CGroup)。 参考 Node Allocatable 以了解详细信息。

默认值:""

kubeReservedCgroup
string

kubeReservedCgroup 帮助 kubelet 识别用来为 Kubernetes 节点系统级守护进程实施 kubeReserved 计算资源预留时使用的顶级控制组(CGroup)。 参阅 Node Allocatable 了解进一步的信息。

默认值:""

enforceNodeAllocatable
[]string

此标志设置 kubelet 需要执行的各类节点可分配资源策略。此字段接受一组选项列表。 可接受的选项有 nonepodssystem-reservedkube-reserved

如果设置了 none,则字段值中不可以包含其他选项。

如果列表中包含 system-reserved,则必须设置 systemReservedCgroup

如果列表中包含 kube-reserved,则必须设置 kubeReservedCgroup

这个字段只有在 cgroupsPerQOS被设置为 true 才被支持。

参阅Node Allocatable 了解进一步的信息。

默认值:["pods"]

allowedUnsafeSysctls
[]string

用逗号分隔的白名单列表,其中包含不安全的 sysctl 或 sysctl 模式(以 * 结尾)。

不安全的 sysctl 组有 kernel.shm*kernel.msg*kernel.semfs.mqueue.*net.*

例如:"kernel.msg*,net.ipv4.route.min\_pmtu"

默认值:[]

volumePluginDir
string

volumePluginDir 是用来搜索其他第三方卷插件的目录的路径。

默认值:"/usr/libexec/kubernetes/kubelet-plugins/volume/exec/"

providerID
string

providerID 字段被设置时,指定的是一个外部提供者(即云驱动)实例的唯一 ID, 该提供者可用来唯一性地标识特定节点。

默认值:""

kernelMemcgNotification
bool

kernelMemcgNotification 字段如果被设置了,会告知 kubelet 集成内核的 memcg 通知机制来确定是否超出内存逐出阈值,而不是使用轮询机制来判定。

默认值:false

logging [必需]
LoggingConfiguration

logging设置日志机制选项。更多的详细信息可参阅 日志选项

默认值:

Format: text
enableSystemLogHandler
bool

enableSystemLogHandler 用来启用通过 Web 接口 host:port/logs/ 访问系统日志的能力。

默认值:true

enableSystemLogQuery
bool

enableSystemLogQuery 启用在 /logs 端点上的节点日志查询功能。 此外,还必须启用 enableSystemLogHandler 才能使此功能起作用。

默认值:false

shutdownGracePeriod
meta/v1.Duration

shutdownGracePeriod 设置节点关闭期间,节点自身需要延迟以及为 Pod 提供的宽限期限的总时长。

默认值:"0s"

shutdownGracePeriodCriticalPods
meta/v1.Duration

shutdownGracePeriodCriticalPods设置节点关闭期间用来终止关键性 Pod 的时长。此时长要短于shutdownGracePeriod。 例如,如果shutdownGracePeriod=30sshutdownGracePeriodCriticalPods=10s, 在节点关闭期间,前 20 秒钟被预留用来体面终止普通 Pod,后 10 秒钟用来终止关键 Pod。

默认值:"0s"

shutdownGracePeriodByPodPriority
[]ShutdownGracePeriodByPodPriority

shutdownGracePeriodByPodPriority 设置基于 Pod 相关的优先级类值而确定的体面关闭时间。当 kubelet 收到关闭请求的时候,kubelet 会针对节点上运行的所有 Pod 发起关闭操作,这些关闭操作会根据 Pod 的优先级确定其宽限期限, 之后 kubelet 等待所有 Pod 退出。

数组中的每个表项代表的是节点关闭时 Pod 的体面终止时间;这里的 Pod 的优先级类介于列表中当前优先级类值和下一个表项的优先级类值之间。

例如,要赋予关键 Pod 10 秒钟时间来关闭,赋予优先级 >=10000 Pod 20 秒钟时间来关闭, 赋予其余的 Pod 30 秒钟来关闭。

shutdownGracePeriodByPodPriority:

  • priority: 2000000000 shutdownGracePeriodSeconds: 10
  • priority: 10000 shutdownGracePeriodSeconds: 20
  • priority: 0 shutdownGracePeriodSeconds: 30

在退出之前,kubelet 要等待的时间上限为节点上所有优先级类的 shutdownGracePeriodSeconds 的最大值。 当所有 Pod 都退出或者到达其宽限期限时,kubelet 会释放关闭防护锁。 此功能要求 GracefulNodeShutdown 特性门控被启用。

shutdownGracePeriodshutdownGracePeriodCriticalPods 被设置时,此配置字段必须为空。

默认值:nil

reservedMemory
[]MemoryReservation

reservedMemory 给出一个逗号分隔的列表,为 NUMA 节点预留内存。

此参数仅在内存管理器功能特性语境下有意义。内存管理器不会为容器负载分配预留内存。 例如,如果你的 NUMA0 节点内存为 10Gi,reservedMemory 设置为在 NUMA0 上预留 1Gi 内存,内存管理器会认为其上只有 9Gi 内存可供分配。

你可以设置不同数量的 NUMA 节点和内存类型。你也可以完全忽略这个字段,不过你要清楚, 所有 NUMA 节点上预留内存的总量要等于通过 node allocatable 设置的内存量。

如果至少有一个节点可分配参数设置值非零,则你需要设置至少一个 NUMA 节点。

此外,避免如下设置:

  1. 在配置值中存在重复项,NUMA 节点和内存类型相同,但配置值不同,这是不允许的。
  2. 为任何内存类型设置限制值为零。
  3. NUMA 节点 ID 在宿主系统上不存在。/li>
  4. memoryhugepages-<size> 之外的内存类型。

默认值:nil

enableProfilingHandler
bool

enableProfilingHandler 启用通过 host:port/debug/pprof/ 接口来执行性能分析。

默认值:true

enableDebugFlagsHandler
bool

enableDebugFlagsHandler 启用通过 host:port/debug/flags/v Web 接口上的标志设置。

默认值:true

seccompDefault
bool

seccompDefault 字段允许针对所有负载将 RuntimeDefault 设置为默认的 seccomp 配置。这一设置要求对应的 SeccompDefault 特性门控被启用。

默认值:false

memoryThrottlingFactor
float64

当设置 cgroupv2 memory.high 以实施 MemoryQoS 特性时, memoryThrottlingFactor 用来作为内存限制或节点可分配内存的系数。

减小此系数会为容器控制组设置较低的 high 限制值,从而增大回收压力;反之, 增大此系数会降低回收压力。更多细节参见 https://kep.k8s.io/2570。

默认值:0.8

registerWithTaints
[]core/v1.Taint

registerWithTaints 是一个由污点组成的数组,包含 kubelet 注册自身时要向节点对象添加的污点。只有 registerNodetrue 时才会起作用,并且仅在节点的最初注册时起作用。

默认值:nil

registerNode
bool

registerNode 启用向 API 服务器的自动注册。

默认值:true

tracing
TracingConfiguration

tracing 为 OpenTelemetry 追踪客户端设置版本化的配置信息。 参阅 https://kep.k8s.io/2832 了解更多细节。

localStorageCapacityIsolation
bool

localStorageCapacityIsolation 启用本地临时存储隔离特性。默认设置为 true。 此特性允许用户为容器的临时存储设置请求/限制,并以类似的方式管理 cpu 和 memory 的请求/限制。 此特性还允许为 emptyDir 卷设置 sizeLimit,如果卷所用的磁盘超过此限制将触发 Pod 驱逐。 此特性取决于准确测定根文件系统磁盘用量的能力。对于 kind rootless 这类系统, 如果不支持此能力,则 LocalStorageCapacityIsolation 特性应被禁用。 一旦禁用,用户不应该为容器的临时存储设置请求/限制,也不应该为 emptyDir 设置 sizeLimit。 默认值:true

containerRuntimeEndpoint [必需]
string

containerRuntimeEndpoint 是容器运行时的端点。 Linux 支持 UNIX 域套接字,而 Windows 支持命名管道和 TCP 端点。 示例:'unix:///path/to/runtime.sock', 'npipe:////./pipe/runtime'。

imageServiceEndpoint
string

imageServiceEndpoint 是容器镜像服务的端点。 Linux 支持 UNIX 域套接字,而 Windows 支持命名管道和 TCP 端点。 示例:'unix:///path/to/runtime.sock'、'npipe:////./pipe/runtime'。 如果未指定,则使用 containerRuntimeEndpoint 中的值。

SerializedNodeConfigSource

SerializedNodeConfigSource 允许对 v1.NodeConfigSource 执行序列化操作。 这一类型供 kubelet 内部使用,以便跟踪动态配置的检查点。 此资源存在于 kubeletconfig API 组是因为它被当做是对 kubelet 的一种版本化输入。

字段描述
apiVersion
string
kubelet.config.k8s.io/v1beta1
kind
string
SerializedNodeConfigSource
source
core/v1.NodeConfigSource

source是我们执行序列化的数据源。

CredentialProvider

出现在:

CredentialProvider 代表的是要被 kubelet 调用的一个 exec 插件。 这一插件只会在所拉取的镜像与该插件所处理的镜像匹配时才会被调用(参见 matchImages)。

字段描述
name [必需]
string

name 是凭据提供者的名称(必需)。此名称必须与 kubelet 所看到的提供者可执行文件的名称匹配。可执行文件必须位于 kubelet 的 bin 目录(通过 --image-credential-provider-bin-dir 设置)下。

matchImages [必需]
[]string

matchImages 是一个必须设置的字符串列表,用来匹配镜像以便确定是否要调用此提供者。 如果字符串之一与 kubelet 所请求的镜像匹配,则此插件会被调用并给予提供凭证的机会。 镜像应该包含镜像库域名和 URL 路径。

matchImages 中的每个条目都是一个模式字符串,其中可以包含端口号和路径。 域名部分可以包含统配符,但端口或路径部分不可以。通配符可以用作子域名,例如 *.k8s.iok8s.*.io,以及顶级域名,如 k8s.*

对类似 app*.k8s.io 这类部分子域名的匹配也是支持的。 每个通配符只能用来匹配一个子域名段,所以 *.io 不会匹配 *.k8s.io

镜像与 matchImages 之间存在匹配时,以下条件都要满足:

  • 二者均包含相同个数的域名部分,并且每个域名部分都对应匹配;
  • matchImages 条目中的 URL 路径部分必须是目标镜像的 URL 路径的前缀;
  • 如果 matchImages 条目中包含端口号,则端口号也必须与镜像端口号匹配。

matchImages 的一些示例如下:

  • 123456789.dkr.ecr.us-east-1.amazonaws.com
  • *.azurecr.io
  • gcr.io
  • *.*.registry.io
  • registry.io:8080/path
defaultCacheDuration [必需]
meta/v1.Duration

defaultCacheDuration 是插件在内存中缓存凭据的默认时长, 在插件响应中没有给出缓存时长时,使用这里设置的值。此字段是必需的。

apiVersion [必需]
string

要求 exec 插件 CredentialProviderRequest 请求的输入版本。 所返回的 CredentialProviderResponse 必须使用与输入相同的编码版本。当前支持的值有:

  • credentialprovider.kubelet.k8s.io/v1beta1
args
[]string

在执行插件可执行文件时要传递给命令的参数。

env
[]ExecEnvVar

env 定义要提供给插件进程的额外的环境变量。 这些环境变量会与主机上的其他环境变量以及 client-go 所使用的环境变量组合起来, 一起传递给插件。

ExecEnvVar

出现在:

ExecEnvVar 用来在执行基于 exec 的凭据插件时设置环境变量。

字段描述
name [必需]
string
环境变量的名称。
value [必需]
string
环境变量的取值。

KubeletAnonymousAuthentication

出现在:

字段描述
enabled
bool

enabled 允许匿名用户向 kubelet 服务器发送请求。 未被其他身份认证方法拒绝的请求都会被当做匿名请求。 匿名请求对应的用户名为 system:anonymous,对应的用户组名为 system:unauthenticated

KubeletAuthentication

出现在:

字段描述
x509
KubeletX509Authentication

x509 包含与 x509 客户端证书认证相关的配置。

webhook
KubeletWebhookAuthentication

webhook 包含与 Webhook 持有者令牌认证相关的配置。

anonymous
KubeletAnonymousAuthentication

anonymous 包含与匿名身份认证相关的配置信息。

KubeletAuthorization

出现在:

字段描述
mode
KubeletAuthorizationMode

mode 是应用到 kubelet 服务器所接收到的请求上的鉴权模式。合法值包括 AlwaysAllowWebhook。 Webhook 模式使用 SubjectAccessReview API 来确定鉴权。

webhook
KubeletWebhookAuthorization

webhook包含与 Webhook 鉴权相关的配置信息。

KubeletAuthorizationMode

string 类型的别名)

出现在:

KubeletWebhookAuthentication

出现在:

字段描述
enabled
bool

enabled 允许使用 tokenreviews.authentication.k8s.io API 来提供持有者令牌身份认证。

cacheTTL
meta/v1.Duration

cacheTTL 启用对身份认证结果的缓存。

KubeletWebhookAuthorization

出现在:

字段描述
cacheAuthorizedTTL
meta/v1.Duration

cacheAuthorizedTTL 设置来自 Webhook 鉴权组件的 'authorized' 响应的缓存时长。

cacheUnauthorizedTTL
meta/v1.Duration

cacheUnauthorizedTTL 设置来自 Webhook 鉴权组件的 'unauthorized' 响应的缓存时长。

KubeletX509Authentication

出现在:

字段描述
clientCAFile
string

clientCAFile 是一个指向 PEM 编码的证书包的路径。 如果设置了此字段,则能够提供由此证书包中机构之一所签名的客户端证书的请求会被成功认证, 并且其用户名对应于客户端证书的 CommonName、组名对应于客户端证书的 Organization

MemoryReservation

出现在:

MemoryReservation 为每个 NUMA 节点设置不同类型的内存预留。

字段描述
numaNode [必需]
int32

NUMA 节点

limits [必需]
core/v1.ResourceList

资源列表

MemorySwapConfiguration

出现在:

字段描述
swapBehavior
string

swapBehavior配置容器负载可以使用的交换内存。可以是:

  • ""、"LimitedSwap":工作负载的内存和交换分区总用量不能超过 Pod 的内存限制;
  • "UnlimitedSwap":工作负载可以无限制地使用交换分区,上限是可分配的约束。

ResourceChangeDetectionStrategy

string 类型的别名)

出现在:

ResourceChangeDetectionStrategy 给出的是内部管理器(Secret、ConfigMap) 用来发现对象变化的模式。

ShutdownGracePeriodByPodPriority

出现在:

ShutdownGracePeriodByPodPriority 基于 Pod 关联的优先级类数值来为其设置关闭宽限时间。

字段描述
priority [必需]
int32

priority 是与关闭宽限期限相关联的优先级值。

shutdownGracePeriodSeconds [必需]
int64

shutdownGracePeriodSeconds 是按秒数给出的关闭宽限期限。

6.14.18 - WebhookAdmission 配置 (v1)

此 API 的版本是 v1。

资源类型

WebhookAdmission

WebhookAdmission 为 Webhook 准入控制器提供配置信息。

字段描述
apiVersion
string
apiserver.config.k8s.io/v1
kind
string
WebhookAdmission
kubeConfigFile [必需]
string

字段 kubeConfigFile 包含指向 kubeconfig 文件的路径。

6.14.19 - 客户端身份认证(Client Authentication) (v1)

资源类型

ExecCredential

ExecCredential 由基于 exec 的插件使用,与 HTTP 传输组件沟通凭据信息。

字段描述
apiVersion
string
client.authentication.k8s.io/v1
kind
string
ExecCredential
spec [必需]
ExecCredentialSpec
字段 spec 包含由 HTTP 传输组件传递给插件的信息。
status
ExecCredentialStatus
字段 status 由插件填充,包含传输组件与 API 服务器连接时需要提供的凭据。

Cluster

出现在:

Cluster 中包含允许 exec 插件与 Kubernetes 集群进行通信身份认证时所需 的信息。

为了确保该结构体包含需要与 Kubernetes 集群进行通信的所有内容(就像通过 Kubeconfig 一样), 除了证书授权之外,该字段应该映射到 "k8s.io/client-go/tools/clientcmd/api/v1".cluster, 由于 CA 数据将始终以字节形式传递给插件。

字段描述
server [必需]
string
字段 server 是 Kubernetes 集群的地址(https://hostname:port)。
tls-server-name
string
tls-server-name 是用来提供给服务器用作 SNI 解析的,客户端以此检查服务器的证书。 如此字段为空,则使用链接服务器时使用的主机名。
insecure-skip-tls-verify
bool
设置此字段之后,会令客户端跳过对服务器端证书的合法性检查。 这会使得你的 HTTPS 链接不再安全。
certificate-authority-data
[]byte
此字段包含 PEM 编码的证书机构(CA)证书。 如果为空,则使用系统的根证书。
proxy-url
string
此字段用来设置向集群发送所有请求时要使用的代理服务器。
disable-compression
bool

disable-compression 允许客户端针对到服务器的所有请求选择取消响应压缩。 当客户端服务器网络带宽充足时,这有助于通过节省压缩(服务器端)和解压缩(客户端)时间来加快请求(特别是列表)的速度: https://github.com/kubernetes/kubernetes/issues/112296。

config
k8s.io/apimachinery/pkg/runtime.RawExtension

在某些环境中,用户配置可能对很多集群而言都完全一样(即调用同一个 exec 插件), 只是针对不同集群会有一些细节上的差异,例如 audience。 此字段使得特定于集群的配置可以直接使用集群信息来设置。 不建议使用此字段来保存 Secret 数据,因为 exec 插件的主要优势之一是不需要在 kubeconfig 中保存 Secret 数据。

ExecCredentialSpec

出现在:

ExecCredentialSpec 保存传输组件所提供的特定于请求和运行时的信息。

字段描述
cluster
Cluster
此字段中包含的信息使得 exec 插件能够与要访问的 Kubernetes 集群通信。 注意,cluster 字段只有在 exec 驱动的配置中 provideClusterInfo (即:ExecConfig.ProvideClusterInfo)被设置为 true 时才不能为空。
interactive [必需]
bool
此字段用来标明标准输出信息是否已传递给 exec 插件。

ExecCredentialStatus

出现在:

ExecCredentialStatus 中包含传输组件要使用的凭据。

字段 token 和 clientKeyData 都是敏感字段。此数据只能在 客户端与 exec 插件进程之间使用内存来传递。exec 插件本身至少 应通过文件访问许可来实施保护。

字段描述
expirationTimestamp
meta/v1.Time
给出所提供的凭据到期的时间。
token [必需]
string
客户端用做请求身份认证的持有者令牌。
clientCertificateData [必需]
string
PEM 编码的客户端 TLS 证书(如果有临时证书,也会包含)。
clientKeyData [必需]
string
与上述证书对应的、PEM 编码的私钥。

6.14.20 - 客户端身份认证(Client Authentication)(v1beta1)

资源类型

ExecCredential

ExecCredential 由基于 exec 的插件使用,与 HTTP 传输组件沟通凭据信息。

字段描述
apiVersion
string
client.authentication.k8s.io/v1beta1
kind
string
ExecCredential
spec [必需]
ExecCredentialSpec
字段 spec 包含由 HTTP 传输组件传递给插件的信息。
status
ExecCredentialStatus
字段 status 由插件填充,包含传输组件与 API 服务器连接时需要提供的凭据。

Cluster

出现在:

Cluster 中包含允许 exec 插件与 Kubernetes 集群进行通信身份认证时所需 的信息。

为了确保该结构体包含需要与 Kubernetes 集群进行通信的所有内容(就像通过 Kubeconfig 一样), 该字段应该映射到 "k8s.io/client-go/tools/clientcmd/api/v1".cluster, 除了证书授权之外,由于 CA 数据将始终以字节形式传递给插件。

字段描述
server [必需]
string
字段 server 是 Kubernetes 集群的地址(https://hostname:port)。
tls-server-name
string
tls-server-name 是用来提供给服务器用作 SNI 解析的,客户端以此检查服务器的证书。 如此字段为空,则使用链接服务器时使用的主机名。
insecure-skip-tls-verify
bool
设置此字段之后,会令客户端跳过对服务器端证书的合法性检查。 这会使得你的 HTTPS 链接不再安全。
certificate-authority-data
[]byte
此字段包含 PEM 编码的证书机构(CA)证书。 如果为空,则使用系统的根证书。
proxy-url
string
此字段用来设置向集群发送所有请求时要使用的代理服务器。
disable-compression
bool

disable-compression 允许客户端针对到服务器的所有请求选择取消响应压缩。 当客户端服务器网络带宽充足时,这有助于通过节省压缩(服务器端)和解压缩(客户端)时间来加快请求(特别是列表)的速度: https://github.com/kubernetes/kubernetes/issues/112296。

config
k8s.io/apimachinery/pkg/runtime.RawExtension

在某些环境中,用户配置可能对很多集群而言都完全一样(即调用同一个 exec 插件), 只是针对不同集群会有一些细节上的差异,例如 audience。 此字段使得特定于集群的配置可以直接使用集群信息来设置。 不建议使用此字段来保存 Secret 数据,因为 exec 插件的主要优势之一是不需要在 kubeconfig 中保存 Secret 数据。

ExecCredentialSpec

出现在:

ExecCredentialSpec 保存传输组件所提供的特定于请求和运行时的信息。

字段描述
cluster
Cluster
此字段中包含的信息使得 exec 插件能够与要访问的 Kubernetes 集群通信。 注意,cluster 字段只有在 exec 驱动的配置中 provideClusterInfo (即:ExecConfig.ProvideClusterInfo)被设置为 true 时才不能为空。

ExecCredentialStatus

出现在:

ExecCredentialStatus 中包含传输组件要使用的凭据。

字段 token 和 clientKeyData 都是敏感字段。 此数据只能在客户端与 exec 插件进程之间使用内存来传递。 exec 插件本身至少应通过文件访问许可来实施保护。

字段描述
expirationTimestamp
meta/v1.Time
给出所提供的凭据到期的时间。
token [必需]
string
客户端用做请求身份认证的持有者令牌。
clientCertificateData [必需]
string
PEM 编码的客户端 TLS 证书(如果有临时证书,也会包含)。
clientKeyData [必需]
string
与上述证书对应的、PEM 编码的私钥。

6.15 - 外部 API

6.15.1 - Kubernetes 外部指标 (v1beta1)

v1beta1 包是 v1beta1 版本的外部指标 API。

资源类型

ExternalMetricValue

出现在:

ExternalMetricValue 是外部指标的一个度量值。 单个度量值由指标名称和一组字符串标签标识。 对于一个指标,可以有多个具有不同标签集的值。

字段描述
apiVersion
string
external.metrics.k8s.io/v1beta1
kind
string
ExternalMetricValue
metricName [必需]
string

指标的名称。

metricLabels [必需]
map[string]string

用于标识指标的单个时间序列的标签集。

timestamp [必需]
meta/v1.Time

标明这些度量值生成的时间。

window [必需]
int64

当返回根据累积度量计算的速率度量值时,此字段标明计算这些度量值的时间窗口 ([Timestamp-Window, Timestamp])(或对于非计算的瞬时度量值为零)。

value [必需]
k8s.io/apimachinery/pkg/api/resource.Quantity

度量值。

ExternalMetricValueList

ExternalMetricValueList 是某个给定指标的某些标签集的数值列表。

字段描述
apiVersion
string
external.metrics.k8s.io/v1beta1
kind
string
ExternalMetricValueList
metadata [必需]
meta/v1.ListMeta
无描述。
items [必需]
[]ExternalMetricValue

与给定标签集匹配的度量值。

6.15.2 - Kubernetes 指标 (v1beta1)

v1beta1 包是 v1beta1 版本的指标 API。

资源类型

NodeMetrics

出现在:

NodeMetrics 设置节点的资源用量指标。

字段描述
apiVersion
string
metrics.k8s.io/v1beta1
kind
string
NodeMetrics
metadata
meta/v1.ObjectMeta

标准的对象元数据。更多信息: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata

参阅 Kubernetes API 文档了解 metadata 字段。
timestamp [必需]
meta/v1.Time

以下字段定义从时间间隔 [Timestamp-Window,Timestamp] 中收集指标的时间间隔。

window [必需]
meta/v1.Duration
无描述。
usage [必需]
core/v1.ResourceList

内存用量是内存工作集。

NodeMetricsList

NodeMetricsList 是 NodeMetrics 的列表。

字段描述
apiVersion
string
metrics.k8s.io/v1beta1
kind
string
NodeMetricsList
metadata [必需]
meta/v1.ListMeta

标准的列表元数据。更多信息: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds

items [必需]
[]NodeMetrics

节点指标的列表。

PodMetrics

出现在:

PodMetrics 设置 Pod 的资源用量指标。

字段描述
apiVersion
string
metrics.k8s.io/v1beta1
kind
string
PodMetrics
metadata
meta/v1.ObjectMeta

标准的对象元数据。更多信息: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata

参阅 Kubernetes API 文档了解 metadata 字段。
timestamp [必需]
meta/v1.Time

以下字段定义了从时间间隔 [Timestamp-Window,Timestamp] 中收集指标的时间间隔。

window [必需]
meta/v1.Duration
无描述。
containers [必需]
[]ContainerMetrics

在相同时间窗口内收集所有容器的指标。

PodMetricsList

PodMetricsList 是 PodMetrics 的列表。

字段描述
apiVersion
string
metrics.k8s.io/v1beta1
kind
string
PodMetricsList
metadata [必需]
meta/v1.ListMeta

标准的列表元数据。更多信息: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds

items [必需]
[]PodMetrics

Pod 指标的列表。

ContainerMetrics

出现在:

ContainerMetrics 设置容器的资源用量指标。

字段描述
name [必需]
string

与 pod.spec.containers 中某个对应的容器名称。

usage [必需]
core/v1.ResourceList

内存用量是内容工作集。

6.15.3 - Kubernetes 自定义指标 (v1beta2)

v1beta2 包是 v1beta2 版本的 custom_metrics API。

资源类型

MetricListOptions

MetricListOptions 用于按其标签选择算符来选择指标。

字段描述
apiVersion
string
custom.metrics.k8s.io/v1beta2
kind
string
MetricListOptions
labelSelector
string

这个选择算符通过标签来限制所返回对象的列表。 默认为任意值。

metricLabelSelector
string

这个选择算符通过标签来限制所返回指标的列表。

MetricValue

出现在:

MetricValue 是某些对象的指标值。

字段描述
apiVersion
string
custom.metrics.k8s.io/v1beta2
kind
string
MetricValue
describedObject [必需]
core/v1.ObjectReference

指向描述对象的引用。

metric [必需]
MetricIdentifier
无描述。
timestamp [必需]
meta/v1.Time

标明度量值生成的时间。

windowSeconds [必需]
int64

当返回根据累积度量计算的速率度量值时,此字段标明计算这些度量值的时间窗口 ([Timestamp-Window, Timestamp])(或对于非计算的瞬时度量值为零)。

value [必需]
k8s.io/apimachinery/pkg/api/resource.Quantity

度量值。

MetricValueList

MetricValueList 是某个给定指标的某些对象集的数值列表。

字段描述
apiVersion
string
custom.metrics.k8s.io/v1beta2
kind
string
MetricValueList
metadata [必需]
meta/v1.ListMeta
无描述。
items [必需]
[]MetricValue

所描述对象的度量值。

MetricIdentifier

出现在:

MetricIdentifier 按名称和可选的选择算符来标识指标。

字段描述
name [必需]
string

name 是给定指标的名称。

selector
meta/v1.LabelSelector

selector 表示可用于选择此指标的标签选择算符,通常就是传递给查询用于获取此指标的选择算符。 当留空时,仅使用指标的 Name 来采集指标。

6.16 - 调度

6.16.1 - 调度器配置

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

你可以通过编写配置文件,并将其路径传给 kube-scheduler 的命令行参数,定制 kube-scheduler 的行为。

调度模板(Profile)允许你配置 kube-scheduler 中的不同调度阶段。每个阶段都暴露于某个扩展点中。插件通过实现一个或多个扩展点来提供调度行为。

你可以通过运行 kube-scheduler --config <filename> 来设置调度模板, 使用 KubeSchedulerConfiguration v1 结构体。

最简单的配置如下:

apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
clientConnection:
  kubeconfig: /etc/srv/kubernetes/kube-scheduler/kubeconfig

配置文件

通过调度配置文件,你可以配置 kube-scheduler 在不同阶段的调度行为。 每个阶段都在一个扩展点中公开。 调度插件通过实现一个或多个扩展点,来提供调度行为。

你可以配置同一 kube-scheduler 实例使用多个配置文件

扩展点

调度行为发生在一系列阶段中,这些阶段是通过以下扩展点公开的:

  1. queueSort:这些插件对调度队列中的悬决的 Pod 排序。 一次只能启用一个队列排序插件。
  1. preFilter:这些插件用于在过滤之前预处理或检查 Pod 或集群的信息。 它们可以将 Pod 标记为不可调度。
  1. filter:这些插件相当于调度策略中的断言(Predicates),用于过滤不能运行 Pod 的节点。 过滤器的调用顺序是可配置的。 如果没有一个节点通过所有过滤器的筛选,Pod 将会被标记为不可调度。
  1. postFilter:当无法为 Pod 找到可用节点时,按照这些插件的配置顺序调用他们。 如果任何 postFilter 插件将 Pod 标记为可调度,则不会调用其余插件。
  1. preScore:这是一个信息扩展点,可用于预打分工作。
  1. score:这些插件给通过筛选阶段的节点打分。调度器会选择得分最高的节点。
  1. reserve:这是一个信息扩展点,当资源已经预留给 Pod 时,会通知插件。 这些插件还实现了 Unreserve 接口,在 Reserve 期间或之后出现故障时调用。
  1. permit:这些插件可以阻止或延迟 Pod 绑定。
  1. preBind:这些插件在 Pod 绑定节点之前执行。
  1. bind:这个插件将 Pod 与节点绑定。bind 插件是按顺序调用的,只要有一个插件完成了绑定,其余插件都会跳过。bind 插件至少需要一个。
  1. postBind:这是一个信息扩展点,在 Pod 绑定了节点之后调用。
  1. multiPoint:这是一个仅配置字段,允许同时为所有适用的扩展点启用或禁用插件。

对每个扩展点,你可以禁用默认插件或者是启用自己的插件,例如:

apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
  - plugins:
      score:
        disabled:
        - name: PodTopologySpread
        enabled:
        - name: MyCustomPluginA
          weight: 2
        - name: MyCustomPluginB
          weight: 1

你可以在 disabled 数组中使用 * 禁用该扩展点的所有默认插件。 如果需要,这个字段也可以用来对插件重新顺序。

调度插件

下面默认启用的插件实现了一个或多个扩展点:

  • ImageLocality:选择已经存在 Pod 运行所需容器镜像的节点。

    实现的扩展点:score

  • TaintToleration:实现了污点和容忍

    实现的扩展点:filterpreScorescore

  • NodeName:检查 Pod 指定的节点名称与当前节点是否匹配。

    实现的扩展点:filter

  • NodePorts:检查 Pod 请求的端口在节点上是否可用。

    实现的扩展点:preFilterfilter

  • PodTopologySpread:实现了 Pod 拓扑分布

    实现的扩展点:preFilterfilterpreScorescore

  • NodeUnschedulable:过滤 .spec.unschedulable 值为 true 的节点。

    实现的扩展点:filter

  • NodeResourcesFit:检查节点是否拥有 Pod 请求的所有资源。 得分可以使用以下三种策略之一:LeastAllocated(默认)、MostAllocatedRequestedToCapacityRatio

    实现的扩展点:preFilterfilterscore

  • NodeResourcesBalancedAllocation:调度 Pod 时,选择资源使用更为均衡的节点。

    实现的扩展点:score

  • VolumeBinding:检查节点是否有请求的卷,或是否可以绑定请求的。 实现的扩展点:preFilterfilterreservepreBindscore

  • VolumeRestrictions:检查挂载到节点上的卷是否满足卷提供程序的限制。

    实现的扩展点:filter

  • VolumeZone:检查请求的卷是否在任何区域都满足。

    实现的扩展点:filter

  • NodeVolumeLimits:检查该节点是否满足 CSI 卷限制。

    实现的扩展点:filter

  • EBSLimits:检查节点是否满足 AWS EBS 卷限制。

    实现的扩展点:filter

  • GCEPDLimits:检查该节点是否满足 GCP-PD 卷限制。

    实现的扩展点:filter

  • AzureDiskLimits:检查该节点是否满足 Azure 卷限制。

    实现的扩展点:filter

  • PrioritySort:提供默认的基于优先级的排序。

    实现的扩展点:queueSort

  • DefaultBinder:提供默认的绑定机制。

    实现的扩展点:bind

  • DefaultPreemption:提供默认的抢占机制。

    实现的扩展点:postFilter

你也可以通过组件配置 API 启用以下插件(默认不启用):

  • CinderLimits:检查是否可以满足节点的 OpenStack Cinder 卷限制。 实现的扩展点:filter

多配置文件

你可以配置 kube-scheduler 运行多个配置文件。 每个配置文件都有一个关联的调度器名称,并且可以在其扩展点中配置一组不同的插件。

使用下面的配置样例,调度器将运行两个配置文件:一个使用默认插件,另一个禁用所有打分插件。

apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
  - schedulerName: default-scheduler
  - schedulerName: no-scoring-scheduler
    plugins:
      preScore:
        disabled:
        - name: '*'
      score:
        disabled:
        - name: '*'

对于那些希望根据特定配置文件来进行调度的 Pod,可以在 .spec.schedulerName 字段指定相应的调度器名称。

默认情况下,将创建一个调度器名为 default-scheduler 的配置文件。 这个配置文件包括上面描述的所有默认插件。 声明多个配置文件时,每个配置文件中调度器名称必须唯一。

如果 Pod 未指定调度器名称,kube-apiserver 将会把调度器名设置为 default-scheduler。 因此,应该存在一个调度器名为 default-scheduler 的配置文件来调度这些 Pod。

应用于多个扩展点的插件

kubescheduler.config.k8s.io/v1beta3 开始,配置文件配置中有一个附加字段 multiPoint,它允许跨多个扩展点轻松启用或禁用插件。 multiPoint 配置的目的是简化用户和管理员在使用自定义配置文件时所需的配置。

考虑一个插件,MyPlugin,它实现了 preScorescorepreFilterfilter 扩展点。 要为其所有可用的扩展点启用 MyPlugin,配置文件配置如下所示:

apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
  - schedulerName: multipoint-scheduler
    plugins:
      multiPoint:
        enabled:
        - name: MyPlugin

这相当于为所有扩展点手动启用 MyPlugin,如下所示:

apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
  - schedulerName: non-multipoint-scheduler
    plugins:
      preScore:
        enabled:
        - name: MyPlugin
      score:
        enabled:
        - name: MyPlugin
      preFilter:
        enabled:
        - name: MyPlugin
      filter:
        enabled:
        - name: MyPlugin

在这里使用 multiPoint 的一个好处是,如果 MyPlugin 将来实现另一个扩展点,multiPoint 配置将自动为新扩展启用它。

可以使用该扩展点的 disabled 字段将特定扩展点从 MultiPoint 扩展中排除。 这适用于禁用默认插件、非默认插件或使用通配符 ('*') 来禁用所有插件。 禁用 ScorePreScore 的一个例子是:

apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
  - schedulerName: non-multipoint-scheduler
    plugins:
      multiPoint:
        enabled:
        - name: 'MyPlugin'
      preScore:
        disabled:
        - name: '*'
      score:
        disabled:
        - name: '*'

kubescheduler.config.k8s.io/v1beta3 开始,所有默认插件都通过 MultiPoint 在内部启用。 但是,仍然可以使用单独的扩展点来灵活地重新配置默认值(例如排序和分数权重)。 例如,考虑两个 Score 插件 DefaultScore1DefaultScore2,每个插件的权重为 1。 它们可以用不同的权重重新排序,如下所示:

apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
  - schedulerName: multipoint-scheduler
    plugins:
      score:
        enabled:
        - name: 'DefaultScore2'
          weight: 5

在这个例子中,没有必要在 MultiPoint 中明确指定插件,因为它们是默认插件。 Score 中指定的唯一插件是 DefaultScore2。 这是因为通过特定扩展点设置的插件将始终优先于 MultiPoint 插件。 因此,此代码段实质上重新排序了这两个插件,而无需同时指定它们。

配置 MultiPoint 插件时优先级的一般层次结构如下:

  1. 特定的扩展点首先运行,它们的设置会覆盖其他地方的设置
  2. 通过 MultiPoint 手动配置的插件及其设置
  3. 默认插件及其默认设置

为了演示上述层次结构,以下示例基于这些插件:

插件扩展点
DefaultQueueSortQueueSort
CustomQueueSortQueueSort
DefaultPlugin1Score, Filter
DefaultPlugin2Score
CustomPlugin1Score, Filter
CustomPlugin2Score, Filter

这些插件的一个有效示例配置是:

apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
  - schedulerName: multipoint-scheduler
    plugins:
      multiPoint:
        enabled:
        - name: 'CustomQueueSort'
        - name: 'CustomPlugin1'
          weight: 3
        - name: 'CustomPlugin2'
        disabled:
        - name: 'DefaultQueueSort'
      filter:
        disabled:
        - name: 'DefaultPlugin1'
      score:
        enabled:
        - name: 'DefaultPlugin2'

请注意,在特定扩展点中重新声明 MultiPoint 插件不会出错。 重新声明被忽略(并记录),因为特定的扩展点优先。

除了将大部分配置保存在一个位置之外,此示例还做了一些事情:

  • 启用自定义 queueSort 插件并禁用默认插件
  • 启用 CustomPlugin1CustomPlugin2,这将首先为它们的所有扩展点运行
  • 禁用 DefaultPlugin1,但仅适用于 filter
  • 重新排序 DefaultPlugin2 以在 score 中首先运行(甚至在自定义插件之前)

v1beta3 之前的配置版本中,没有 multiPoint,上面的代码片段等同于:

apiVersion: kubescheduler.config.k8s.io/v1beta2
kind: KubeSchedulerConfiguration
profiles:
  - schedulerName: multipoint-scheduler
    plugins:

      # 禁用默认的 QueueSort 插件
      queueSort:
        enabled:
        - name: 'CustomQueueSort'
        disabled:
        - name: 'DefaultQueueSort'

      # 启用自定义的 Filter 插件
      filter:
        enabled:
        - name: 'CustomPlugin1'
        - name: 'CustomPlugin2'
        - name: 'DefaultPlugin2'
        disabled:
        - name: 'DefaultPlugin1'

      # 启用并重新排序自定义的打分插件
      score:
        enabled:
        - name: 'DefaultPlugin2'
          weight: 1
        - name: 'DefaultPlugin1'
          weight: 3

虽然这是一个复杂的例子,但它展示了 MultiPoint 配置的灵活性以及它与配置扩展点的现有方法的无缝集成。

调度程序配置迁移

  • 在 v1beta2 配置版本中,你可以为 NodeResourcesFit 插件使用新的 score 扩展。 新的扩展结合了 NodeResourcesLeastAllocatedNodeResourcesMostAllocatedRequestedToCapacityRatio 插件的功能。 例如,如果你之前使用了 NodeResourcesMostAllocated 插件, 则可以改用 NodeResourcesFit(默认启用)并添加一个 pluginConfigscoreStrategy,类似于:

    apiVersion: kubescheduler.config.k8s.io/v1beta2
    kind: KubeSchedulerConfiguration
    profiles:
    - pluginConfig:
      - args:
          scoringStrategy:
            resources:
            - name: cpu
              weight: 1
            type: MostAllocated
        name: NodeResourcesFit
    
  • 调度器插件 NodeLabel 已弃用; 相反,要使用 NodeAffinity 插件(默认启用)来实现类似的行为。
  • 调度程序插件 ServiceAffinity 已弃用; 相反,使用 InterPodAffinity 插件(默认启用)来实现类似的行为。
  • 调度器插件 NodePreferAvoidPods 已弃用; 相反,使用 节点污点 来实现类似的行为。
  • 在 v1beta2 配置文件中启用的插件优先于该插件的默认配置。
  • 调度器的健康检查和审计的绑定地址,所配置的 hostport 无效将导致验证失败。

  • 默认增加三个插件的权重:
    • InterPodAffinity 从 1 到 2
    • NodeAffinity 从 1 到 2
    • TaintToleration 从 1 到 3

  • 调度器插件 SelectorSpread 被移除,改为使用 PodTopologySpread 插件(默认启用)来实现类似的行为。

接下来

6.16.2 - 调度策略

在 Kubernetes v1.23 版本之前,可以使用调度策略来指定 predicatespriorities 进程。 例如,可以通过运行 kube-scheduler --policy-config-file <filename> 或者 kube-scheduler --policy-configmap <ConfigMap> 设置调度策略。

但是从 Kubernetes v1.23 版本开始,不再支持这种调度策略。 同样地也不支持相关的 policy-config-filepolicy-configmappolicy-configmap-namespaceuse-legacy-policy-config 标志。 你可以通过使用调度配置来实现类似的行为。

接下来

6.17 - 其他工具

Kubernetes 包含多种工具来帮助你使用 Kubernetes 系统。

crictl

crictl 是用于检查和调试兼容 CRI 的容器运行时的命令行接口。

仪表盘

Dashboard, 基于 Web 的 Kubernetes 用户界面, 允许你将容器化的应用程序部署到 Kubernetes 集群, 对它们进行故障排查,并管理集群及其资源本身。

Helm

Helm 是一个用于管理预配置 Kubernetes 资源包的工具。这些包被称为“Helm 图表”。

使用 Helm 来:

  • 查找和使用打包为 Kubernetes 图表的流行软件
  • 将你自己的应用程序共享为 Kubernetes 图表
  • 为你的 Kubernetes 应用程序创建可重现的构建
  • 智能管理你的 Kubernetes 清单文件
  • 管理 Helm 包的发布

Kompose

Kompose 是一个帮助 Docker Compose 用户迁移到 Kubernetes 的工具。

使用 Kompose:

  • 将 Docker Compose 文件翻译成 Kubernetes 对象
  • 从本地 Docker 开发转到通过 Kubernetes 管理你的应用程序
  • 转换 Docker Compose v1 或 v2 版本的 yaml 文件或分布式应用程序包

Kui

Kui 是一个接受你标准的 kubectl 命令行请求并以图形响应的 GUI 工具。

Kui 接受标准的 kubectl 命令行工具并以图形响应。 Kui 提供包含可排序表格的 GUI 渲染,而不是 ASCII 表格。

Kui 让你能够:

  • 直接点击长的、自动生成的资源名称,而不是复制和粘贴
  • 输入 kubectl 命令并查看它们的执行,有时甚至比 kubectl 本身更快
  • 查询 Job 并查看其执行渲染为瀑布图
  • 使用选项卡式 UI 在集群中单击资源

Minikube

minikube 是一种在你的工作站上本地运行单节点 Kubernetes 集群的工具,用于开发和测试。

6.17.1 - 从 Docker 命令行映射到 crictl

crictl 是兼容 CRI的容器运行时的一种命令行接口。 你可以使用它来在 Kubernetes 节点上检视和调试容器运行时和应用。 crictl 及其源代码都托管在 cri-tools 仓库中。

本页面提供一份参考资料,用来将 docker 命令行工具的常用命令映射到 crictl 的等价命令。

从 docker 命令行映射到 crictl

映射表格中列举的确切版本是 docker 命令行的 v1.40 版本和 crictl 的 v1.19.0 版本。 这一列表不是完备的。例如,其中并未包含实验性质的 docker 命令。

获得调试信息

docker 命令行与 crictl 的映射 - 获得调试信息
docker CLIcrictl描述不支持的功能
attachattach挂接到某运行中的容器--detach-keys, --sig-proxy
execexec在运行中的容器内执行命令--privileged, --user, --detach-keys
imagesimages列举镜像 
infoinfo显示系统范围的信息 
inspectinspect, inspecti返回容器、镜像或任务的底层信息 
logslogs取回容器的日志数据--details
psps列举容器 
statsstats显示容器资源用量统计的动态数据流列:NET/BLOCK I/O、PIDs
versionversion显示运行时(Docker、ContainerD 或其他)的版本信息

执行变更

docker 命令行与 crictl 的映射 - 执行变更
docker CLIcrictl描述不支持的功能
createcreate创建一个新容器 
killstop (超时值为 0)杀死一个或多个运行中的容器--signal
pullpull从某镜像库拉取镜像或仓库--all-tags, --disable-content-trust
rmrm移除一个或者多个容器 
rmirmi移除一个或者多个镜像 
runrun在一个新的容器中执行命令 
startstart启动一个或多个已停止的容器--detach-keys
stopstop停止一个或多个运行中的容器 
updateupdate更新一个或多个容器的配置--restart--blkio-weight 以 CRI 所不支持的资源约束

仅被 crictl 支持的命令

docker 命令行与 crictl 的映射 - 仅被 crictl 支持的命令
crictl描述
imagefsinfo返回镜像文件系统信息
inspectp显示一个或多个 Pod 的状态
port-forward将本地端口转发到 Pod
pods列举 Pod
runp运行一个新的 Pod
rmp删除一个或多个 Pod
stopp停止一个或多个运行中的 Pod

7 - 为 Kubernetes 出一份力

为 Kubernetes 做出贡献的方法有很多。你可以致力于新特性的设计、记录我们已有的代码、为我们的博客写文章。 还有更多:你可以实现这些新特性或修复 Bug、可以帮助人们加入我们的贡献者社区,或支持现有的贡献者。

通过所有这些不同的方式来改变项目,我们(Kubernetes)制作了一个专门的网站:https://k8s.dev/。 你可以去那里了解有关为 Kubernetes 做出贡献的更多信息。

如果你特别想了解如何为文档做出贡献,请阅读为 Kubernetes 文档做出贡献

7.1 - 为 Kubernetes 文档出一份力

本网站由 Kubernetes SIG Docs(文档特别兴趣小组)维护。 Kubernetes 项目欢迎所有贡献者(无论是新手还是经验丰富的贡献者)提供帮助!

Kubernetes 文档项目的贡献者:

  • 改进现有内容
  • 创建新内容
  • 翻译文档
  • 管理并发布 Kubernetes 周期性发行版的文档

入门

任何人都可以提出文档方面的问题(issue),或贡献一个变更,用拉取请求(PR)的方式提交到 GitHub 上的 kubernetes/website 仓库。 当然你需要熟练使用 gitGitHub 才能在 Kubernetes 社区中有效工作。

如何参与文档编制:

  1. 签署 CNCF 的贡献者许可协议
  2. 熟悉文档仓库和网站的静态站点生成器
  3. 确保理解发起 PR审查变更的基本流程。
flowchart TB subgraph third[发起 PR] direction TB U[ ] -.- Q[改进现有内容] --- N[创建新内容] N --- O[翻译文档] O --- P[管理并发布 K8s
周期性发行版的文档] end subgraph second[评审] direction TB T[ ] -.- D[仔细查看
kubernetes/website
仓库] --- E[下载安装 Hugo
静态站点
生成器] E --- F[了解基本的
GitHub 命令] F --- G[评审待处理的 PR
并遵从变更审查
流程] end subgraph first[注册] direction TB S[ ] -.- B[签署 CNCF
贡献者
许可协议] --- C[加入 sig-docs
Slack 频道] C --- V[加入 kubernetes-sig-docs
邮件列表] V --- M[参加每周的
sig-docs 电话会议
或 slack 会议] end A([fa:fa-user 新的
贡献者]) --> first A --> second A --> third A --> H[提出问题!!!] classDef grey fill:#dddddd,stroke:#ffffff,stroke-width:px,color:#000000, font-size:15px; classDef white fill:#ffffff,stroke:#000,stroke-width:px,color:#000,font-weight:bold classDef spacewhite fill:#ffffff,stroke:#fff,stroke-width:0px,color:#000 class A,B,C,D,E,F,G,H,M,Q,N,O,P,V grey class S,T,U spacewhite class first,second,third white

图 1. 新手入门指示。

图 1 概述了新贡献者的路线图。 你可以遵从“注册”和“评审”所述的某些或全部步骤。 至此,你完成了发起 PR 的准备工作, 可以通过“发起 PR” 列出的事项实现你的贡献目标。 再次重申,欢迎随时提出问题!

有些任务要求 Kubernetes 组织内更高的信任级别和访问权限。 阅读参与 SIG Docs 工作,获取角色和权限的更多细节。

第一次贡献

你可以提前查阅几个步骤,来准备你的第一次贡献。 图 2 概述了后续的步骤和细节。

flowchart LR subgraph second[第一次贡献] direction TB S[ ] -.- G[查阅其他 K8s
成员发起的 PR] --> A[检索 kubernetes/website
问题列表是否有
good first 一类的 PR] --> B[发起一个 PR!!] end subgraph first[建议的准备工作] direction TB T[ ] -.- D[阅读贡献概述] -->E[阅读 K8s 内容
和风格指南] E --> F[了解 Hugo 页面
内容类型
和短代码] end first ----> second classDef grey fill:#dddddd,stroke:#ffffff,stroke-width:px,color:#000000, font-size:15px; classDef white fill:#ffffff,stroke:#000,stroke-width:px,color:#000,font-weight:bold classDef spacewhite fill:#ffffff,stroke:#fff,stroke-width:0px,color:#000 class A,B,D,E,F,G grey class S,T spacewhite class first,second white

图 2. 第一次贡献的准备工作。

贡献时获取帮助

做出第一个贡献可能会让人感觉比较困难。 新贡献者大使 将引导你完成最初的一些贡献。你可以在 Kubernetes Slack 中联系他们,最好是在 #sig-docs 频道中。还有每月第一个星期二举行的 新贡献者见面会, 你可以在此处与新贡献者大使互动并解决你的疑问。

下一步

参与 SIG Docs 工作

SIG Docs 是负责发布、维护 Kubernetes 文档的贡献者团体。 参与 SIG Docs 是 Kubernetes 贡献者(开发者和其他人员)对 Kubernetes 项目产生重大影响力的好方式。

SIG Docs 的几种沟通方式:

其他贡献方式

7.2 - 提出内容改进建议

如果你发现 Kubernetes 文档中存在问题或者你有一个关于新内容的想法, 可以考虑提出一个问题(issue)。你只需要具有 GitHub 账号和 Web 浏览器就可以完成这件事。

在大多数情况下,Kubernetes 文档的新工作都是开始于 GitHub 上的某个问题。 Kubernetes 贡献者会审阅这些问题并根据需要对其分类、打标签。 接下来,你或者别的 Kubernetes 社区成员就可以发起一个带有变更的拉取请求, 以解决这一问题。

创建问题

如果你希望就改进已有内容提出建议或者在文档中发现了错误,请创建一个问题(issue)。

  1. 点击右侧边栏的 提交文档问题 按钮。浏览器会重定向到一个 GitHub 问题页面, 其中包含了一些预先填充的内容。
  2. 请描述遇到的问题或关于改进的建议。尽可能提供细节信息。
  3. 点击 Submit new issue

提交之后,偶尔查看一下你所提交的问题,或者开启 GitHub 通知。 评审人(reviewers)和其他社区成员可能在针对所提问题采取行动之前,问一些问题。

关于新内容的建议

如果你对新内容有想法,但是你不确定这些内容应该放在哪里,你仍可以提出问题。

  • 在预期的节区中选择一个现有页面,点击 提交文档问题
  • 前往 GitHub Issues 页面, 直接记录问题。

如何更好地记录问题

在记录问题时,请注意以下事项:

  • 提供问题的清晰描述,描述具体缺失的内容、过期的内容、错误的内容或者需要改进的文字。
  • 解释该问题对用户的特定影响。
  • 将给定问题的范围限定在一个工作单位范围内。如果问题牵涉的领域较大,可以将其分解为多个小一点的问题。 例如:"Fix the security docs" 是一个过于宽泛的问题,而 "Add details to the 'Restricting network access' topic" 就是一个足够具体的、可操作的问题。
  • 搜索现有问题的列表,查看是否已经有相关的或者类似的问题已被记录。
  • 如果新问题与某其他问题或 PR 有关联,可以使用其完整 URL 或带 # 字符的 PR 编号来引用之。 例如:Introduced by #987654
  • 遵从行为准则。尊重同行贡献者。 例如,"The docs are terrible" 就是无用且无礼的反馈。

7.3 - 贡献新内容

本节包含你在贡献新内容之前需要知晓的信息。

flowchart LR subgraph second[开始之前] direction TB S[ ] -.- A[签署 CNCF CLA] --> B[选择 Git 分支] B --> C[每个 PR 一种语言] C --> F[检查贡献者工具] end subgraph first[基本知识] direction TB T[ ] -.- D[用 markdown 编写文档
并用 Hugo 构建网站] --- E[GitHub 源代码] E --- G['/content/../docs' 文件夹包含
多语言文档] G --- H[评审 Hugo 页面内容
类型和短代码] end first ----> second classDef grey fill:#dddddd,stroke:#ffffff,stroke-width:px,color:#000000, font-size:15px; classDef white fill:#ffffff,stroke:#000,stroke-width:px,color:#000,font-weight:bold classDef spacewhite fill:#ffffff,stroke:#fff,stroke-width:0px,color:#000 class A,B,C,D,E,F,G,H grey class S,T spacewhite class first,second white

插图 - 贡献新内容准备工作

上图描述了你在提交新内容之前需要知晓的信息。 详细信息见下文。

基本知识

  • 使用 Markdown 编写 Kubernetes 文档并使用 Hugo 构建网站。
  • Kubernetes 文档使用 CommonMark 作为 Markdown 的风格。
  • 源代码位于 GitHub 仓库中。 你可以在 /content/zh-cn/docs/ 目录下找到 Kubernetes 文档。 某些参考文档是使用位于 update-imported-docs/ 目录下的脚本自动生成的。
  • 页面内容类型使用 Hugo 描述文档内容的呈现。
  • 你可以使用 Docsy 短代码定制的 Hugo 短代码贡献 Kubernetes 文档。
  • 除了标准的 Hugo 短代码外, 我们还在文档中使用一些定制的 Hugo 短代码来控制内容的呈现。
  • 文档的源代码有多种语言形式,位于 /content/ 目录下。 每种语言都有一个自己的目录,用两个字母表示,这两个字母是基于 ISO 639-1 标准来确定的。 例如,英语文档的源代码位于 /content/en/docs/ 目录下。
  • 关于为多语言文档做贡献以及如何开始新翻译的详细信息, 可参考本地化文档

开始之前

签署 CNCF CLA

所有 Kubernetes 贡献者必须阅读贡献者指南签署贡献者授权同意书 (Contributor License Agreement, CLA)

若贡献者尚未签署 CLA,其发起的 PR 将无法通过自动化测试。 你所提供的姓名和邮件地址必须与 git config 中配置的完全相同, 而且你的 git 用户名和邮件地址必须与用来签署 CNCF CLA 的信息一致。

选择要使用的 Git 分支

在发起 PR 时,你需要预先知道基于哪个分支来开展工作。

场景分支
针对当前发行版本的,对现有英文内容的修改或新的英文内容main
针对功能特性变更的内容分支对应于功能特性变更的主要和次要版本,分支名称采用 dev-<version> 的模式。例如,如果某功能特性在 v1.31 版本发生变化,则对应的文档变化要添加到 dev-1.31 分支。
其他语言的内容(本地化)基于本地化团队的约定。参见本地化分支策略了解更多信息。

如果你仍不能确定要选择哪个分支,请在 Slack 的 #sig-docs 频道上提出问题。

每个 PR 牵涉的语言

请确保每个 PR 仅涉及一种语言。 如果你需要对多种语言下的同一代码示例进行相同的修改,也请为每种语言发起一个独立的 PR。

为贡献者提供的工具

kubernetes/website 仓库的文档贡献者工具目录中包含了一些工具, 有助于使你的贡献过程更为顺畅。

7.3.1 - 发起拉取请求(PR)

要贡献新的内容页面或者改进已有内容页面,请发起拉取请求(PR)。 请确保你满足了开始之前一节中所列举的所有要求。

如果你所提交的变更足够小,或者你对 git 工具不熟悉, 可以阅读使用 GitHub 提交变更以了解如何编辑页面。

如果所提交的变更较大, 请阅读基于本地克隆副本开展工作以学习如何在你本地计算机上进行修改。

使用 GitHub 提交变更

如果你在 git 工作流方面欠缺经验,这里有一种发起拉取请求的更为简单的方法。 图 1 勾勒了后续的步骤和细节。

flowchart LR A([fa:fa-user 新的
贡献者]) --- id1[(kubernetes/website
GitHub)] subgraph tasks[使用 GitHub 提交变更] direction TB 0[ ] -.- 1[1. 编辑此页] --> 2[2. 使用 GitHub markdown
编辑器进行修改] 2 --> 3[3. 填写 Propose file change] end subgraph tasks2[ ] direction TB 4[4. 选择 Propose file change] --> 5[5. 选择 Create pull request] --> 6[6. 填写 Open a pull request] 6 --> 7[7. 选择 Create pull request] end id1 --> tasks --> tasks2 classDef grey fill:#dddddd,stroke:#ffffff,stroke-width:px,color:#000000, font-size:15px; classDef white fill:#ffffff,stroke:#000,stroke-width:px,color:#000,font-weight:bold classDef k8s fill:#326ce5,stroke:#fff,stroke-width:1px,color:#fff; classDef spacewhite fill:#ffffff,stroke:#fff,stroke-width:0px,color:#000 class A,1,2,3,4,5,6,7 grey class 0 spacewhite class tasks,tasks2 white class id1 k8s

图 1. 使用 GitHub 发起一个 PR 的步骤。

  1. 在你发现问题的页面上,选择右侧导航面板中的编辑此页面选项。

  2. 在 GitHub 的 Markdown 编辑器中修改内容。

  3. 在编辑器的下方,填写 Propose file change 表单。 在第一个字段中,为你的提交消息取一个标题。 在第二个字段中,为你的提交写一些描述文字。

  1. 选择 Propose File Change

  2. 选择 Create pull request

  3. 出现 Open a pull request 界面。填写表单:

    • Subject 字段默认为提交的概要信息,你可以根据需要进行修改。
    • Body 字段包含更为详细的提交消息(如果你之前有填写过的话)和一些模板文字。 填写模板所要求的详细信息,之后删除多余的模板文字。
    • 确保 Allow edits from maintainers 复选框被勾选。
  1. 选择 Create pull request

在 GitHub 上处理反馈意见

在合并 PR 之前,Kubernetes 社区成员会评阅并批准它。 k8s-ci-robot 会基于页面中最近提及的属主来建议评阅人(reviewers)。 如果你希望特定某人来评阅,可以留下评论,提及该用户的 GitHub 用户名。

如果某个评阅人请你修改 PR:

  1. 前往 Files changed Tab 页面;
  2. 选择 PR 所修改的任何文件所对应的铅笔(edit)图标;
  3. 根据建议作出修改;
  4. 提交所作修改。

如果你希望等待评阅人的反馈,可以每 7 天左右联系一次。 你也可以在 #sig-docs Slack 频道发送消息。

当评阅过程结束,某个评阅人会合并你的 PR。 几分钟之后,你所做的变更就会上线了。

基于本地克隆副本开展工作

如果你有 git 的使用经验,或者你要提议的修改不仅仅几行,请使用本地克隆副本来开展工作。

首先要确保你在本地计算机上安装了 git。 你也可以使用 git 的带用户界面的应用。

图 2 显示了基于本地克隆副本开展工作的步骤。每个步骤的细节如下。

flowchart LR 1[派生 kubernetes/website
仓库] --> 2[创建本地克隆副本
并指定 upstream 仓库] subgraph changes[你的变更] direction TB S[ ] -.- 3[创建一个分支
例如:my_new_branch] --> 3a[使用文本编辑器
进行修改] --> 4["使用 Hugo 在本地
预览你的变更
(localhost:1313)
或构建容器镜像"] end subgraph changes2[提交 / 推送] direction TB T[ ] -.- 5[提交你的变更] --> 6[将提交推送到
origin/my_new_branch] end 2 --> changes --> changes2 classDef grey fill:#dddddd,stroke:#ffffff,stroke-width:px,color:#000000, font-size:15px; classDef white fill:#ffffff,stroke:#000,stroke-width:px,color:#000,font-weight:bold classDef k8s fill:#326ce5,stroke:#fff,stroke-width:1px,color:#fff; classDef spacewhite fill:#ffffff,stroke:#fff,stroke-width:0px,color:#000 class 1,2,3,3a,4,5,6 grey class S,T spacewhite class changes,changes2 white

图 2. 使用本地克隆副本进行修改。

派生 kubernetes/website 仓库

  1. 前往 kubernetes/website 仓库;
  2. 选择 Fork.

创建一个本地克隆副本并指定 upstream 仓库

  1. 打开终端窗口,克隆你所派生的副本,并更新 Docsy Hugo 主题

    git clone git@github.com:<github_username>/website
    cd website
    git submodule update --init --recursive --depth 1
    
  1. 前往新的 website 目录,将 kubernetes/website 仓库设置为 upstream 远端:

    cd website
    
    git remote add upstream https://github.com/kubernetes/website.git
    
  1. 确认你现在有两个仓库 originupstream

    git remote -v
    

    输出类似于:

    origin	git@github.com:<github_username>/website.git (fetch)
    origin	git@github.com:<github_username>/website.git (push)
    upstream	https://github.com/kubernetes/website.git (fetch)
    upstream	https://github.com/kubernetes/website.git (push)
    
  1. 从你的克隆副本取回 origin/main 分支,从 kubernetes/website 取回 upstream/main

    git fetch origin
    git fetch upstream
    

    这样可以确保你本地的仓库在开始工作前是最新的。

创建一个分支

  1. 决定你要基于哪个分支来开展工作:

    • 针对已有内容的改进,请使用 upstream/main
    • 针对已有功能特性的新文档内容,请使用 upstream/main
    • 对于本地化内容,请基于本地化的约定。 可参考本地化 Kubernetes 文档了解详细信息。
    • 对于在下一个 Kubernetes 版本中新功能特性的文档,使用独立的功能特性分支。 参考为发行版本撰写功能特性文档了解更多信息。
    • 对于很多 SIG Docs 共同参与的,需较长时间才完成的任务,例如内容的重构, 请使用为该任务创建的特性分支。

    如果你在选择分支上需要帮助,请在 #sig-docs Slack 频道提问。

  1. 基于第 1 步中选定的分支,创建新分支。 下面的例子假定基础分支是 upstream/main

    git checkout -b <my_new_branch> upstream/main
    
  1. 使用文本编辑器进行修改。

在任何时候,都可以使用 git status 命令查看你所改变了的文件列表。

提交你的变更

当你准备好发起拉取请求(PR)时,提交你所做的变更。

  1. 在你的本地仓库中,检查你要提交的文件:

    git status
    

    输出类似于:

    On branch <my_new_branch>
    Your branch is up to date with 'origin/<my_new_branch>'.
    
    Changes not staged for commit:
    (use "git add <file>..." to update what will be committed)
    (use "git checkout -- <file>..." to discard changes in working directory)
    
    modified:   content/en/docs/contribute/new-content/contributing-content.md
    
    no changes added to commit (use "git add" and/or "git commit -a")
    
  1. Changes not staged for commit 下列举的文件添加到提交中:

    git add <your_file_name>
    

    针对每个文件重复此操作。

  1. 添加完所有文件之后,创建一个提交(commit):

    git commit -m "Your commit message"
    
  1. 推送你本地分支及其中的新提交到你的远程派生副本库:

    git push origin <my_new_branch>
    

在本地预览你的变更

在推送变更或者发起 PR 之前在本地查看一下预览是个不错的主意。 通过预览你可以发现构建错误或者 Markdown 格式问题。

你可以构建网站的容器镜像或者在本地运行 Hugo。 构建容器镜像的方式比较慢,不过能够显示 Hugo 短代码(shortcodes), 因此对于调试是很有用的。

  1. 在本地构建容器镜像 如果你正在测试对 Hugo 工具本身的更改,则仅需要此步骤

    # 在终端窗口中执行(如果有需要)
    make container-image
    
  1. 在容器中启动 Hugo:

    # 在终端窗口中执行
    make container-serve
    
  1. 启动浏览器,浏览 https://localhost:1313。 Hugo 会监测文件的变更并根据需要重新构建网站。

  2. 要停止本地 Hugo 实例,可返回到终端并输入 Ctrl+C,或者关闭终端窗口。

另一种方式是,在你的本地计算机上安装并使用 hugo 命令:

  1. 安装 website/netlify.toml 文件中指定的 Hugo 版本。

  2. 如果你尚未更新你的网站仓库,则 website/themes/docsy 目录是空的。 如果本地缺少主题的副本,则该站点无法构建。 要更新网站主题,运行以下命令:

git submodule update --init --recursive --depth 1
  1. 启动一个终端窗口,进入 Kubernetes 网站仓库目录,启动 Hugo 服务器:

    cd <path_to_your_repo>/website
    hugo server --buildFuture
    
  1. 在浏览器的地址栏输入: https://localhost:1313。 Hugo 会监测文件的变更并根据需要重新构建网站。

  2. 要停止本地 Hugo 实例,返回到终端窗口并输入 Ctrl+C 或者关闭终端窗口。

从你的克隆副本向 kubernetes/website 发起拉取请求(PR)

图 3 显示了从你的克隆副本向 kubernetes/website 发起 PR 的步骤。 详细信息如下。

请注意,贡献者可以将 kubernetes/website 称为 k/website

flowchart LR subgraph first[ ] direction TB 1[1. 前往 kubernetes/website 仓库] --> 2[2. 选择 New Pull Request] 2 --> 3[3. 选择 compare across forks] 3 --> 4[4. 从 head repository 下拉菜单
选择你的克隆副本] end subgraph second [ ] direction TB 5[5. 从 compare 下拉菜单
选择你的分支] --> 6[6. 选择 Create Pull Request] 6 --> 7[7. 为你的 PR
添加一个描述] 7 --> 8[8. 选择 Create pull request] end first --> second classDef grey fill:#dddddd,stroke:#ffffff,stroke-width:px,color:#000000, font-size:15px; classDef white fill:#ffffff,stroke:#000,stroke-width:px,color:#000,font-weight:bold class 1,2,3,4,5,6,7,8 grey class first,second white

图 3. 从你的克隆副本向 kubernetes/website 发起一个 PR 的步骤。

  1. 在 Web 浏览器中,前往 kubernetes/website 仓库;

  2. 点击 New Pull Request

  3. 选择 compare across forks

  4. head repository 下拉菜单中,选取你的派生仓库;

  5. compare 下拉菜单中,选择你的分支;

  6. 点击 Create Pull Request

  7. 为你的拉取请求添加一个描述:

    • Title (不超过 50 个字符):总结变更的目的;

    • Description:给出变更的详细信息;

      • 如果存在一个相关联的 GitHub Issue,可以在描述中包含 Fixes #12345Closes #12345。GitHub 的自动化设施能够在当前 PR 被合并时自动关闭所提及 的 Issue。如果有其他相关联的 PR,也可以添加对它们的链接。
      • 如果你特别希望获得某方面的建议,可以在描述中包含你希望评阅人思考的问题。
  8. 点击 Create pull request 按钮。

祝贺你!你的拉取请求现在出现在 Pull Requests 列表中了!

在发起 PR 之后,GitHub 会执行一些自动化的测试,并尝试使用 Netlify 部署一个预览版本。

  • 如果 Netlify 构建操作失败,可选择 Details 了解详细信息。
  • 如果 Netlify 构建操作成功,选择 Details 会打开 Kubernetes 的一个预览版本, 其中包含了你所作的变更。评阅人也使用这一功能来检查你的变更。

GitHub 也会自动为 PR 分派一些标签,以帮助评阅人。 如果有需要,你也可以向 PR 添加标签。 欲了解相关详细信息,可以参考 添加和删除 Issue 标签

在本地处理反馈

  1. 在本地完成修改之后,可以修补(amend)你之前的提交:

    git commit -a --amend
    
    • -a:提交所有修改
    • --amend:对前一次提交进行增补,而不是创建新的提交
  1. 如果有必要,更新你的提交消息;

  2. 使用 git push origin <my_new_branch> 来推送你的变更,重新触发 Netlify 测试。

来自评阅人的修改

有时评阅人会向你的 PR 中提交修改。在作出其他修改之前,请先取回这些提交。

  1. 从你的远程派生副本仓库取回提交,让你的工作分支基于所取回的分支:

    git fetch origin
    git rebase origin/<your-branch-name>
    
  1. 变更基线(rebase)操作完成之后,强制推送本地的新改动到你的派生仓库:

    git push --force-with-lease origin <your-branch-name>
    

合并冲突和重设基线

如果另一个贡献者在别的 PR 中提交了对同一文件的修改,这可能会造成合并冲突。 你必须在你的 PR 中解决所有合并冲突。

  1. 更新你的派生副本,重设本地分支的基线:

    git fetch origin
    git rebase origin/<your-branch-name>
    

    之后强制推送修改到你的派生副本仓库:

    git push --force-with-lease origin <your-branch-name>
    
  1. kubernetes/websiteupstream/main 分支取回更改,然后重设本地分支的基线:

    git fetch upstream
    git rebase upstream/main
    
  1. 检查重设基线操作之后的状态:

    git status
    

    你会看到一组存在冲突的文件。

  1. 打开每个存在冲突的文件,查找冲突标记:>>><<<===。 解决完冲突之后删除冲突标记。

  1. 添加文件到变更集合:

    git add <filename>
    
  1. 继续执行基线变更(rebase)操作:

    git rebase --continue
    
  1. 根据需要重复步骤 2 到 5。

    在应用完所有提交之后,git status 命令会显示 rebase 操作完成。

  1. 将分支强制推送到你的派生仓库:

    git push --force-with-lease origin <your-branch-name>
    

    PR 不再显示存在冲突。

压缩(Squashing)提交

如果你的 PR 包含多个提交(commits),你必须将其压缩成一个提交才能被合并。 你可以在 PR 的 Commits Tab 页面查看提交个数,也可以在本地通过 git log 命令查看提交个数。

  1. 启动一个交互式的 rebase 操作:

    git rebase -i HEAD~<number_of_commits_in_branch>
    

    压缩提交的过程也是一种重设基线的过程。 这里的 -i 开关告诉 git 你希望交互式地执行重设基线操作。 HEAD~<number_of_commits_in_branch 表明在 rebase 操作中查看多少个提交。

    输出类似于;

    pick d875112ca Original commit
    pick 4fa167b80 Address feedback 1
    pick 7d54e15ee Address feedback 2
    
    # Rebase 3d18sf680..7d54e15ee onto 3d183f680 (3 commands)
    
    ...
    
    # These lines can be re-ordered; they are executed from top to bottom.
    

    输出的第一部分列举了重设基线操作中的提交。 第二部分给出每个提交的选项。 改变单词 pick 就可以改变重设基线操作之后提交的状态。

    就重设基线操作本身,我们关注 squashpick 选项。

  1. 开始编辑文件。

    修改原来的文本:

    pick d875112ca Original commit
    pick 4fa167b80 Address feedback 1
    pick 7d54e15ee Address feedback 2
    

    使之成为:

    pick d875112ca Original commit
    squash 4fa167b80 Address feedback 1
    squash 7d54e15ee Address feedback 2
    

    以上编辑操作会压缩提交 4fa167b80 Address feedback 17d54e15ee Address feedback 2d875112ca Original commit 中,只留下 d875112ca Original commit 成为时间线中的一部分。

  1. 保存文件并退出编辑器。

  2. 推送压缩后的提交:

    git push --force-with-lease origin <branch_name>
    

贡献到其他仓库

Kubernetes 项目包含大约 50 多个仓库。 这些仓库中很多都有文档:提供给最终用户的帮助文本、错误信息、API 参考或者代码注释等。

如果你发现有些文本需要改进,可以使用 GitHub 来搜索 Kubernetes 组织下的所有仓库。 这样有助于发现要在哪里提交 Issue 或 PR。

每个仓库有其自己的流程和过程。在登记 Issue 或者发起 PR 之前, 记得阅读仓库可能存在的 README.mdCONTRIBUTING.mdcode-of-conduct.md 文件。

大多数仓库都有自己的 Issue 和 PR 模板。 通过查看一些待解决的 Issue 和 PR, 你可以大致了解协作的流程。 在登记 Issue 或提出 PR 时,务必尽量填充所给的模板,多提供详细信息。

接下来

  • 阅读评阅节,学习评阅过程。

7.3.2 - 为发行版本撰写功能特性文档

Kubernetes 的每个主要版本发布都会包含一些需要文档说明的新功能。 新的发行版本也会对已有功能特性和文档(例如将某功能特性从 Alpha 升级为 Beta)进行更新。

通常,负责某功能特性的 SIG 要为功能特性的文档草拟文档,并针对 kubernetes/website 仓库的合适的开发分支发起拉取请求。 SIG Docs 团队会提供文字方面的反馈意见,或者直接编辑文档草稿。 本节讨论两个小组在分支方面和发行期间所遵从的流程方面的约定。

对于文档贡献者

一般而言,文档贡献者不会为某个发行版本从头撰写文档。 相反,他们会与开发该功能特性的 SIG 团队一起,对文档草稿进行润色, 使之符合发布条件。

在你选定了某个功能特性,为其撰写文档(主笔或辅助),请在 #sig-docs Slack 频道、SIG Docs 的每周例会上, 或者在功能特性对应的 PR 上提出咨询。如果继续工作是没有问题的, 你可以使用提交到他人的 PR 所述的某个技巧参与 PR 的编辑工作。

了解即将发布的功能特性

要了解即将发布的功能特性,可以参加每周的 SIG Release 例会 (参考社区页面,了解即将召开的会议), 监视 kubernetes/sig-release 中与发行相关的文档。 每个发行版本在 /sig-release/tree/master/releases/ 下都有一个对应的子目录。 该子目录包含了发行版本的时间计划、发行公告的草稿以及列举发行团队名单的文档。

发行时间计划文件中包含到所有其他文档、会议、会议记录及发行相关的里程碑的链接。 其中也包含关于发行版本的目标列表、时间线,以及当前发行版本中就绪的特殊流程的信息。 文档末尾附近定义了若干与该发行版本有关的术语。

此文档也包含到 功能特性跟踪清单 的链接。 这一清单是了解哪些功能特性计划进入某发行版本的正式途径。

发行团队文档列举了哪些人扮演着各个发行版本的不同角色。 如果不清楚要联系谁来讨论特定的功能特性或者回答你的问题, 你可以参加发行团队的会议,提出你的问题,或者联系发行团队的牵头人, 这样他们就可以帮你找到正确的联系人。

发行说明草稿是用来发现与特定发行版本相关的功能特性、变更、废弃以及其他信息的好来源。 由于在发行周期的后段该文档的内容才会最终定稿,参考其中的信息时请谨慎。

特性跟踪清单

针对给定 Kubernetes 发行版本 特性跟踪清单中列举的是计划包含于该版本中的每个功能特性。 每一行中都包含特性的名称、特性对应的主要 GitHub Issue,其稳定性级别(ALpha、 Beta 或 Stable)、负责实现该特性的 SIG 和个人、是否该特性需要文档、 该特性的发行说明草稿以及该特性是否已经被合并等等。阅读此清单时请注意:

  • Beta 和 Stable 功能特性通常比 Alpha 特性更为需要文档支持。
  • 如果某功能特性尚未被合并,就很难测试或者为其撰写文档。 对于对应的 PR 而言,也很难讲特性是否完全实现。
  • 确定某个功能特性是否需要文档的过程是一个手动的过程。 即使某个功能特性没有标记需要文档,你仍可能需要为其提供文档。

针对开发人员或其他 SIG 成员

本节中的信息是针对为发行版本中新功能特性撰写文档的来自其他 Kubernetes SIG 的成员。

如果你是某个 SIG 的成员,负责为 Kubernetes 开发某一项新的功能特性,你需要与 SIG Docs 一起工作,确保这一新功能在发行之前已经为之撰写文档。 请参考特性跟踪清单或者 Kubernetes Slack 上的 #sig-release 频道,检查时间安排的细节以及截止日期。

提交占位 PR

  1. kubernetes/website 仓库上针对 dev-1.31 分支提交一个draft PR,其中包含较少的、待以后慢慢补齐的提交内容。 要创建一个草案(draft)状态的 PR,可以在 Create Pull Request 下拉菜单中选择 Create Draft Pull Request,然后点击 Draft Pull Request
  2. 编辑拉取请求描述以包括指向 kubernetes/kubernetes PR 和 kubernetes/enhancements 问题的链接。
  3. 在对应的 kubernetes/enhancements issue 上添加评论,附上新 PR 的链接以便管理此发行版本的人员能够得到通知, 了解特性的文档正在被撰写,在新的发行版本中要跟踪其进展。

如果对应的功能特性不需要任何类型的文档变更,请通过在 #sig-release Slack 频道声明这一点以确保 sig-release 团队了解。 如果功能特性确实需要文档,而没有对应的 PR 提交,该功能特性可能会被从里程碑中移除。

PR 准备好评阅

时机成熟时,你可以在你的占位 PR 中完成功能特性文档,并将 PR 的状态从草案状态更改为 Ready for Review。要将一个拉取请求标记为已准备好评阅, 转到页面的 merge 框,点击 Ready for review

尽可能为功能特性提供详尽文档以及使用说明。如果你需要文档组织方面的帮助, 请在 #sig-docs Slack 频道中提问。

当你已经完成内容撰写,指派给你的功能特性的文档贡献者会去评阅文档。 为了确保技术准确性,内容可能还需要相应 SIG 的技术审核。 尽量利用他们所给出的建议,改进文档内容以达到发布就绪状态。

如果你的特性需要文档,而你未提交初版文档,那此特性可能会被从里程碑中移除。

特性门控

如果你在处理的特性处于 Alpha 或 Beta 阶段并由某特性门控控制, 你需要在 content/en/docs/reference/command-line-tools-reference/feature-gates/ 目录中为其创建一个特性门控文件。 此文件的名称应该是特性门控的名称,此名称的式样从 UpperCamelCase 转换为 kebab-case,并以 .md 作为后缀。 你可以参照同一目录中已存在的其他文件,以了解你的文件应该是什么样子的。 通常一段话就够了;若要长篇阐述,请在其他地方添加文档,并为其添加链接。

此外,为了确保你的特性门控出现在 Alpha/Beta 特性门控表格中, 请在 Markdown 描述文件的 Front Matter 中包含以下细节:

stages:
  - stage: <alpha/beta/stable/deprecated>  # 指定特性门控的开发阶段
    defaultValue: <true or false>     # 如果默认启用,则设置为 true,否则为 false
    fromVersion: <Version>            # 特性门控可用的起始版本
    toVersion: <Version>              # (可选)特性门控可用的结束版本

对于全新的特性门控,还需要一个单独的特性门控描述;在 content/en/docs/reference/command-line-tools-reference/feature-gates/ 目录中创建一个新的 Markdown 文件(把其他文件用作模板)。

当你将特性门控从默认禁用更改为默认启用时,你可能还需要更改其他文档(不仅仅是特性门控列表)。 参照这样的话术 “exampleSetting 字段是一个 Beta 字段,默认禁用。 你可以通过启用 ProcessExampleThings 特性门控来启用此字段。”

如果你的特性已经是 GA(正式发布)或已弃用的,请在描述文件的 stages 块中包含一个额外的 stage 条目。 确保 Alpha 和 Beta 阶段保持不变。这一步将特性门控从 Alpha/Beta 特性门控 表格移到已毕业或已弃用的特性门控表格。例如:

stages:
  - stage: alpha 
    defaultValue: false
    fromVersion: "1.12"
    toVersion: "1.12"
  - stage: beta 
    defaultValue: true
    fromVersion: "1.13"
    toVersion: "1.18"    
  # 将 'stable' stage 代码块添加到了 stages 下 
  - stage: stable     
    defaultValue: true
    fromVersion: "1.19"
    toVersion: "1.27"   

最终,Kubernetes 将完全停止包含此特性门控。为了表示某特性门控已被移除, 请在相应描述文件的 Front Matter 中包括 removed: true。 此操作将使特性门控及其描述从已毕业或已弃用的特性门控 部分移到名为特性门控(已移除)的专用页面。

所有 PR 均经过评审且合并就绪

如果你的 PR 在发行截止日期之前尚未合并到 dev-1.31 分支, 请与负责管理该发行版本的文档团队成员一起合作,在截止期限之前将其合并。 如果功能特性需要文档,而文档并未就绪,该特性可能会被从里程碑中去除。

7.3.3 - 提交博客和案例分析

任何人都可以撰写博客并提交评阅。 案例分析则在被批准之前需要更多的评阅。

Kubernetes 博客

Kubernetes 博客用于项目发布新功能特性、 社区报告以及其他一些可能对整个社区很重要的新闻。 其读者包括最终用户和开发人员。 大多数博客的内容是关于核心项目中正在发生的事情, 不过我们也鼓励你提交一些有关生态系统中其他时事的博客。

任何人都可以撰写博客并提交评阅。

提交博文

博文不应该是商业性质的,应该包含广泛适用于 Kubernetes 社区的原创内容。 合适的博客内容包括:

  • Kubernetes 新能力
  • Kubernetes 项目更新信息
  • 来自特别兴趣小组(Special Interest Groups, SIG)的更新信息
  • 教程和演练
  • 有关 Kubernetes 的纲领性理念
  • Kubernetes 合作伙伴 OSS 集成信息
  • 仅限原创内容

不合适的博客内容包括:

  • 供应商产品推介
  • 不含集成信息和客户故事的合作伙伴更新信息
  • 已发表的博文(可刊登博文译稿)

要提交博文,你可以遵从以下步骤:

  1. 如果你还未签署 CLA,请先签署 CLA
  2. 查阅网站仓库中现有博文的 Markdown 格式。
  3. 在你所选的文本编辑器中撰写你的博文。
  4. 在第 2 步的同一链接上,点击 Create new file 按钮。 将你的内容粘贴到编辑器中。为文件命名,使其与提议的博文标题一致, 但不要在文件名中写日期。 博客评阅者将与你一起确定最终的文件名和发表博客的日期。
  5. 保存文件时,GitHub 将引导你完成 PR 流程。
  6. 博客评阅者将评阅你提交的内容,并与你一起处理反馈和最终细节。 当博文被批准后,博客将排期发表。

指导原则和期望

  • 博客内容不可以是销售用语。
    • 文章内容必须是对整个 Kubernetes 社区中很多人都有参考意义。 例如,所提交的文章应该关注上游的 Kubernetes 项目本身,而不是某个厂商特定的配置。 请参阅文档风格指南 以了解哪些内容是 Kubernetes 所允许的。
    • 链接应该主要指向官方的 Kubernetes 文档。 当引用外部信息时,链接应该是多样的。 例如,所提交的博客文章中不可以只包含指向某个公司的博客的链接。
    • 有些时候,这是一个比较棘手的权衡过程。 博客团队的存在目的即是为 Kubernetes 博客提供文章是否合适的指导意见。 所以,需要帮助的时候不要犹豫。
  • 博客内容并非在某特定日期发表。
    • 文章会交由社区自愿者评阅。我们会尽力满足特定的时限要求,只是无法就此作出承诺。
    • Kubernetes 项目的很多核心组件会在发布窗口期内提交博客文章,导致发表时间被推迟。 因此,请考虑在发布周期内较为平静的时间段提交博客文章。
    • 如果你希望就博文发表日期上进行较大范围的协调,请联系 CNCF 推广团队。 这也许是比提交博客文章更合适的一种选择。
    • 有时,博客的评审可能会堆积起来。如果你觉得你的文章没有引起该有的重视,你可以通过 #sig-docs-blog Slack 频道联系博客团队, 以获得实时反馈。
  • 博客内容应该对 Kubernetes 用户有用。
    • 与参与 Kubernetes SIG 活动相关,或者与这类活动的结果相关的主题通常是切题的。 请参考 贡献者沟通(Contributor Comms)团队的工作以获得对此类博文的支持。
    • Kubernetes 的组件都有意设计得模块化,因此使用类似 CNI、CSI 等集成点的工具通常都是切题的。
    • 关于其他 CNCF 项目的博客可能切题也可能不切题。 我们建议你在提交草稿之前与博客团队联系。
      • 很多 CNCF 项目有自己的博客。这些博客通常是更好的选择。 有些时候,某个 CNCF 项目的主要功能特性或者里程碑的变化可能是用户有兴趣在 Kubernetes 博客上阅读的内容。
    • 关于为 Kubernetes 项目做贡献的博客内容应该放在 Kubernetes 贡献者站点上。
  • 博客文章须是原创内容。
    • 官方博客的目的不是将某第三方已发表的内容重新作为新内容发表。
    • 博客的授权协议 的确允许出于商业目的来使用博客内容;但并不是所有可以商用的内容都适合在这里发表。
  • 博客文章的内容应该在一段时间内不过期。
    • 考虑到项目的开发速度,我们希望读者看到的是不必更新就能保持长期准确的内容。
    • 有时候,在官方文档中添加一个教程或者进行内容更新都是比博客更好的选择。
      • 可以考虑在博客文章中将较长技术内容的重点放在鼓励读者自行尝试上, 或者放在问题域本身或者为什么读者应该关注某个话题上。

提交博客的技术考虑

所提交的内容应该是 Markdown 格式的,以便能够被 Hugo 生成器来处理。 关于如何使用相关技术,有很多可用的资源

对于插图、表格或图表,可以使用 figure shortcode。 对于其他图片,我们强烈建议使用 alt 属性;如果一张图片不需要任何 alt 属性, 那么这张图片在文章中就不是必需的。

我们知道这一需求可能给那些对此过程不熟悉的朋友们带来不便, 我们也一直在寻找降低难度的解决方案。 如果你有降低难度的好主意,请自荐帮忙。

SIG Docs 博客子项目负责管博客的评阅过程。 更多信息可参考提交博文

要提交博文,你可以遵从以下指南:

  • 发起一个包含新博文的 PR。 新博文要创建于 content/en/blog/_posts 目录下。

  • 确保你的博文遵从合适的命名规范,并带有下面的引言(元数据)信息:

    • Markdown 文件名必须符合格式 YYYY-MM-DD-Your-Title-Here.md。 例如,2020-02-07-Deploying-External-OpenStack-Cloud-Provider-With-Kubeadm.md

    • 不要在文件名中包含多余的句点。类似 2020-01-01-whats-new-in-1.19.md 这类文件名会导致文件无法正确打开。

    • 引言部分必须包含以下内容:

      ---
      layout: blog
      title: "Your Title Here"
      date: YYYY-MM-DD
      slug: text-for-URL-link-here-no-spaces
      ---
      
    • 第一个或者最初的提交的描述信息中应该包含一个所作工作的简单摘要, 并作为整个博文的一个独立描述。 请注意,对博文的后续修改编辑都会最终合并到此主提交中,所以此提交的描述信息 应该尽量有用。
      • 较好的提交消息(Commit Message)示例:
        • Add blog post on the foo kubernetes feature
        • blog: foobar announcement
      • 较差的提交消息示例:
        • Add blog post
        • .
        • initial commit
        • draft post
    • 博客团队会对 PR 内容进行评阅,为你提供一些评语以便修订。 之后,机器人会将你的博文合并并发表。
    • 如果博文的内容仅包含预期无需更新就能对读者保持精准的内容, 则可以将这篇博文标记为长期有效(evergreen), 且免除添加博文发表一年后内容过期的自动警告。
      • 要将一篇博文标记为长期有效,请在引言部分添加以下标记:

        evergreen: true
        
      • 不应标记为长期有效的内容示例:

        • 仅适用于特定发行版或版本而不是所有未来版本的教程
        • 对非正式发行(Pre-GA)API 或功能特性的引用

制作 Kubernetes 贡献者博客的镜像

要从 Kubernetes 贡献者博客制作某篇博文的镜像,遵循以下指导原则:

  • 保持博客内容不变。如有变更,应该先在原稿上进行更改,然后再更改到镜像的文章上。
  • 镜像博客应该有一个 canonicalUrl,即基本上是原始博客发布后的网址。
  • Kubernetes 贡献者博客在 YAML 头部中提及作者, 而 Kubernetes 博文在博客内容中提及作者。你在镜像内容时应修改这一点。
  • 发布日期与原博客保持一致。

在制作镜像博客时,你也需遵守本文所述的所有其他指导原则和期望。

提交案例分析

案例分析用来概述组织如何使用 Kubernetes 解决现实世界的问题。 Kubernetes 市场化团队和 CNCF 成员会与你一起工作, 撰写所有的案例分析。

请查看现有案例分析的源码。

参考案例分析指南, 根据指南中的注意事项提交你的 PR 请求。

7.4 - 评阅变更

本节描述如何对内容进行评阅。

7.4.1 - 评审 PR

任何人均可评审文档的拉取请求。 访问 Kubernetes 网站仓库的 pull requests 部分, 可以查看所有待处理的拉取请求(PR)。

评审文档 PR 是将你自己介绍给 Kubernetes 社区的一种很好的方式。 它将有助于你学习代码库并与其他贡献者之间建立相互信任关系。

在评审之前,可以考虑:

准备工作

在你开始评审之前:

  • 阅读 CNCF 行为准则。 确保你会始终遵从其中约定。
  • 保持有礼貌、体谅他人,怀助人为乐初心。
  • 评论时若给出修改建议,也要兼顾 PR 的积极方面。
  • 保持同理心,多考虑他人收到评审意见时的可能反应。
  • 假定大家都是好意的,通过问问题澄清意图。
  • 如果你是有经验的贡献者,请考虑和新贡献者一起合作,提高其产出质量。

评审过程

一般而言,应该使用英语来评审 PR 的内容和样式。 图 1 概述了评审流程的各个步骤。 每个步骤的详细信息如下。

flowchart LR subgraph fourth[开始评审] direction TB S[ ] -.- M[添加评论] --> N[评审变更] N --> O[新手应该
选择 Comment] end subgraph third[选择 PR] direction TB T[ ] -.- J[阅读描述
和评论]--> K[通过 Netlify 预览构建
来预览变更] end A[查阅待处理的 PR 清单]--> B[通过标签过滤
待处理的 PR] B --> third --> fourth classDef grey fill:#dddddd,stroke:#ffffff,stroke-width:px,color:#000000, font-size:15px; classDef white fill:#ffffff,stroke:#000,stroke-width:px,color:#000,font-weight:bold classDef spacewhite fill:#ffffff,stroke:#fff,stroke-width:0px,color:#000 class A,B,J,K,M,N,O grey class S,T spacewhite class third,fourth white

图 1. 评审流程步骤。

  1. 前往 https://github.com/kubernetes/website/pulls, 你会看到所有针对 Kubernetes 网站和文档的待处理 PR。

  2. 使用以下标签(组合)对待处理 PR 进行过滤:

    • cncf-cla: yes (建议):由尚未签署 CLA 的贡献者所发起的 PR 不可以合并。 参考签署 CLA 以了解更多信息。
    • language/en (建议):仅查看英语语言的 PR。
    • size/<尺寸>:过滤特定尺寸(规模)的 PR。 如果你刚入门,可以从较小的 PR 开始。

    此外,确保 PR 没有标记为尚未完成(Work in Progress)。 包含 work in progress 的 PR 通常还没准备好被评审。

  1. 选定 PR 评审之后,可以通过以下方式理解所作的变更:

    • 阅读 PR 描述以理解所作变更,并且阅读所有关联的 Issues。
    • 阅读其他评审人给出的评论。
    • 点击 Files changed Tab 页面,查看被改变的文件和代码行。
    • 滚动到 Conversation Tab 页面下端的 PR 构建检查节区, 预览 Netlify 预览构建中的变更。 以下是一个屏幕截图(这显示了 GitHub 的桌面版外观; 如果你在平板电脑或智能手机设备上进行评审, GitHub 的 Web UI 会略有不同):
      GitHub PR 详细信息,包括 Netlify 预览链接
      要打开预览,请点击 deploy/netlify 行的 Details 链接。
  1. 前往 Files changed Tab 页面,开始你的评审工作。

    1. 点击你希望评论的行旁边的 + 号。

    2. 填写你对该行的评论, 之后选择 Add single comment(如果你只有一条评论) 或者 Start a review(如果你还有其他评论要添加)。

    3. 评论结束时,点击页面顶部的 Review changes。 这里你可以添加你的评论结语(记得留下一些正能量的评论!)、 根据需要批准 PR、请求作者进一步修改等等。 新手应该选择 Comment

      • 避免在完成审查后点击 "Request changes(请求修改)"按钮。 如果在完成进一步修改之前你想阻止某 PR 被合并。你可以在评论中留下一个 “/hold”。 同时在评论中说明你为什么要设置 Hold,并且在必要时指定在什么条件下可以由你或其他评审人取消 Hold。
      • 避免在完成审查后直接点击 "Approve(批准)"按钮。 在大多数情况下,建议在评论区留下一个"/approve(批准)"的评论。

评审清单

评审 PR 时可以从下面的条目入手。

语言和语法

  • 是否存在明显的语言或语法错误?对某事的描述有更好的方式?
    • 关注作者正在更改的页面部分的语言和语法。除非作者明确打算更新整个页面,否则他们没有义务修复页面上的所有问题。
    • 当一个 PR 更新现有页面时,你应专注于评审正在更新的页面部分。你应评审所更改内容的技术和编辑的正确性。 如果你发现页面上的一些错误与 PR 作者尝试解决的问题没有直接关系, 则应将其视为一个单独的 Issue(首先检查是否存在与此相关的 Issue)。
    • 要特别注意那些 移动 内容的 PR。如果作者重命名一个页面或合并两个页面, 我们(Kubernetes SIG Docs)通常应避免要求该作者修复可能在所移动的内容中发现的所有语法或拼写错误。
  • 是否存在一些过于复杂晦涩的用词,本可以用简单词汇来代替?
  • 是否有些用词、术语或短语可以用不带歧视性的表达方式代替?
  • 用词和大小写方面是否遵从了样式指南
  • 是否有些句子太长,可以改得更短、更简单?
  • 是否某些段落过长,可以考虑使用列表或者表格来表达?

内容

  • Kubernetes 网站上是否别处已经存在类似的内容?
  • 内容本身是否过度依赖于网站范畴之外、独立供应商或者非开源的文档?

网站

  • PR 是否改变或者删除了某页面的标题、slug/别名或者链接锚点? 如果是这样,PR 是否会导致出现新的失效链接? 是否有其他的办法,比如改变页面标题但不改变其 slug?

  • PR 是否引入新的页面?如果是:

    • 该页面是否使用了正确的页面内容类型 及相关联的 Hugo 短代码(shortcodes)?
    • 该页面能否在对应章节的侧面导航中显示?显示得正确么?
    • 该页面是否应出现在网站主页面的列表中?
  • 变更是否正确出现在 Netlify 预览中了? 要对列表、代码段、表格、注释和图像等元素格外留心。

其他

  • 查阅 Trivial Edits; 如果你看到某个变更在你看来是一个 Trivial Edit,请向作者指明这项政策(如果该变更确实会有所改进,那仍然可以接受)。
  • 鼓励作者们在第一次发 PR 时修复一些空格相关的问题,在随后的 PR 中增加其他更改。 这样更便于合并和评审。尤其要注意在单个 commit 中大量空格清理附带的微小变更(如果你看到,请鼓励作者进行修复)。

作为一名 Reviewer,如果你发现 PR 有一些无关紧要的小问题,例如拼写错误或不正确的空格, 可以在你的评论前面加上 nit:。这样做可以让作者知道该问题不是一个不得了的大问题。

如果你正在考虑批准一个 PR,并且所有剩余的反馈都被标记为 nit,那么你确实可以合并该 PR。 在这种情况下,你需要针对剩余的 nit 发帖登记一个 Issue。 考虑一下是否能把这些新 Issue 标记为 Good First Issue。 如果可以,这就是这类 Issue 的良好来源。

7.4.2 - 评阅人和批准人文档

SIG Docs 评阅人(Reviewers)批准人(Approvers) 在对变更进行评审时需要做一些额外的事情。

每周都有一个特定的文档批准人自愿负责对 PR 进行分类和评阅。 此角色称作该周的“PR 管理者(PR Wrangler)”。 相关信息可参考 PR Wrangler 排班表。 要成为 PR Wangler,需要参加每周的 SIG Docs 例会,并自愿报名。 即使当前这周排班没有轮到你,你仍可以评阅那些尚未被积极评阅的 PRs。

除了上述的轮值安排,后台机器人也会为基于所影响的文件来为 PR 指派评阅人和批准人。

评阅 PR

Kubernetes 文档遵循 Kubernetes 代码评阅流程

评阅 PR 文档中所描述的所有规程都适用, 不过评阅人和批准人还要做以下工作:

  • 根据需要使用 Prow 命令 /assign 指派特定的评阅人。如果某个 PR 需要来自代码贡献者的技术审核时,这一点非常重要。

  • 确保 PR 遵从内容指南样式指南; 如果 PR 没有达到要求,指引作者阅读指南中的相关部分。

  • 适当的时候使用 GitHub Request Changes 选项,建议 PR 作者实施所建议的修改。

  • 当你所提供的建议被采纳后,在 GitHub 中使用 /approve/lgtm Prow 命令,改变评审状态。

提交到他人的 PR

为 PR 留下评语是很有用的,不过有时候你需要向他人的 PR 提交内容。

除非他人明确请求你的帮助或者你希望重启一个被放弃很久的 PR,不要“接手”他人的工作。 尽管短期看来这样做可以提高效率,但是也剥夺了他人提交贡献的机会。

你所要遵循的流程取决于你需要编辑已经在 PR 范畴的文件,还是 PR 尚未触碰的文件。

如果处于下列情况之一,你不可以向别人的 PR 提交内容:

  • 如果 PR 作者是直接将自己的分支提交到 https://github.com/kubernetes/website/ 仓库。只有具有推送权限的评阅人才可以向他人的 PR 提交内容。

  • PR 作者明确地禁止批准人编辑他/她的 PR。

评阅用的 Prow 命令

Prow 是基于 Kubernetes 的 CI/CD 系统,基于拉取请求(PR)的触发运行不同任务。 Prow 使得我们可以使用会话机器人一样的命令跨整个 Kubernetes 组织处理 GitHub 动作,例如添加和删除标签、关闭 Issues 以及指派批准人等等。你可以使用 /<命令名称> 的形式以 GitHub 评论的方式输入 Prow 命令。

评阅人和批准人最常用的 Prow 命令有:

评阅用 Prow 命令
Prow 命令角色限制描述
/lgtm组织成员用来表明你已经完成 PR 的评阅并对其所作变更表示满意
/approve批准人批准某 PR 可以合并
/assign任何人指派某人来评阅或批准某 PR
/close组织成员关闭 Issue 或 PR
/hold任何人添加 do-not-merge/hold 标签,用来表明 PR 不应被自动合并
/hold cancel任何人去掉 do-not-merge/hold 标签

要查看可以在 PR 中使用的命令,请参阅 Prow 命令指南

对 Issue 进行诊断和分类

一般而言,SIG Docs 遵从 Kubernetes issue 判定 流程并使用相同的标签。

此 GitHub Issue 过滤器 可以用来查找需要评判的 Issues。

评判 Issue

  1. 验证 Issue 的合法性
  • 确保 Issue 是关于网站文档的。某些 Issue 可以通过回答问题或者为报告者提供 资源链接来快速关闭。 参考请求支持或代码缺陷报告 节以了解详细信息。
  • 评估该 Issue 是否有价值。
  • 如果 Issue 缺少足够的细节以至于无法采取行动,或者报告者没有通过模版提供 足够信息,可以添加 triage/needs-information 标签。
  • 如果 Issue 同时标注了 lifecycle/staletriage/needs-information 标签,可以直接关闭。
  1. 添加优先级标签( Issue 判定指南中有优先级标签的详细定义)
Issue 标签
标签描述
priority/critical-urgent应马上处理
priority/important-soon应在 3 个月内处理
priority/important-longterm应在 6 个月内处理
priority/backlog可无限期地推迟,可在人手充足时处理
priority/awaiting-more-evidence占位符,标示 Issue 可能是一个不错的 Issue,避免该 Issue 被忽略或遗忘
help or good first issue适合对 Kubernetes 或 SIG Docs 经验较少的贡献者来处理。更多信息可参考需要帮助和入门候选 Issue 标签

基于你自己的判断,你可以选择某 Issue 来处理,为之发起 PR (尤其是那些可以很快处理或与你已经在做的工作相关的 Issue)。

如果你对 Issue 评判有任何问题,可以在 #sig-docs Slack 频道或者 kubernetes-sig-docs 邮件列表 中提问。

添加和删除 Issue 标签

要添加标签,可以用以下形式对 PR 进行评论:

  • /<要添加的标签> (例如, /good-first-issue
  • /<标签类别> <要添加的标签> (例如,/triage needs-information/language ja

要移除某个标签,可以用以下形式对 PR 进行评论:

  • /remove-<要移除的标签> (例如,/remove-help
  • /remove-<标签类别> <要移除的标签> (例如,/remove-triage needs-information

在以上两种情况下,标签都必须合法存在。如果你尝试添加一个尚不存在的标签, 对应的命令会被悄悄忽略。

关于所有标签的完整列表,可以参考 Website 仓库的标签节。 实际上,SIG Docs 并没有使用全部标签。

Issue 生命周期标签

Issues 通常都可以快速创建并关闭。 不过也有些时候,某个 Issue 被创建之后会长期处于非活跃状态。 也有一些时候,即使超过 90 天,某个 Issue 仍应保持打开状态。

Issue 生命周期标签
标签描述
lifecycle/stale过去 90 天内某 Issue 无人问津,会被自动标记为停滞状态。如果 Issue 没有被 /remove-lifecycle stale 命令重置生命期,就会被自动关闭。
lifecycle/frozen对应的 Issue 即使超过 90 天仍无人处理也不会进入停滞状态。用户手动添加此标签给一些需要保持打开状态超过 90 天的 Issue,例如那些带有 priority/important-longterm 标签的 Issue。

处理特殊的 Issue 类型

SIG Docs 常常会遇到以下类型的 Issue,因此对其处理方式描述如下。

重复的 Issue

如果针对同一个问题有不止一个打开的 Issue,可以将其合并为一个 Issue。 你需要决定保留哪个 Issue 为打开状态(或者重新登记一个新的 Issue), 然后将所有相关的信息复制过去并提供对关联 Issues 的链接。 最后,将所有其他描述同一问题的 Issue 标记为 triage/duplicate 并关闭之。 保持只有一个 Issue 待处理有助于减少困惑,避免在同一问题上发生重复劳动。

如果失效链接是关于 API 或者 kubectl 文档的,可以将其标记为 /priority critical-urgent,直到问题原因被弄清楚为止。 对于其他的链接失效问题,可以标记 /priority important-longterm, 因为这些问题都需要手动处理。

博客问题

我们预期 Kubernetes 博客条目随着时间推移都会过期。 因此,我们只维护一年内的博客条目。 如果某个 Issue 是与某个超过一年的博客条目有关的,可以直接关闭 Issue,不必修复。

请求支持或代码缺陷报告

某些文档 Issues 实际上是关于底层代码的 Issue 或者在某方面请求协助的问题, 例如某个教程无法正常工作。 对于与文档无关的 Issues,关闭它并打上标签 kind/support,可以通过评论 告知请求者其他支持渠道(Slack、Stack Overflow)。 如果有相关的其他仓库,可以告诉请求者应该在哪个仓库登记与功能特性相关的 Issues (通常会是 kubernetes/kubernetes)。

下面是对支持请求的回复示例:

This issue sounds more like a request for support and less
like an issue specifically for docs. I encourage you to bring
your question to the `#kubernetes-users` channel in
[Kubernetes slack](https://slack.k8s.io/). You can also search
resources like
[Stack Overflow](https://stackoverflow.com/questions/tagged/kubernetes)
for answers to similar questions.

You can also open issues for Kubernetes functionality in
https://github.com/kubernetes/kubernetes.

If this is a documentation issue, please re-open this issue.

对代码缺陷 Issue 的回复示例:

This sounds more like an issue with the code than an issue with
the documentation. Please open an issue at
https://github.com/kubernetes/kubernetes/issues.

If this is a documentation issue, please re-open this issue.

压缩(Squashing)提交

作为一名 Approver,当你评审 PR 时,可能会遇到以下几种情况:

  • 建议贡献者压缩他们的提交。
  • 协助贡献者压缩提交。
  • 建议贡献者先不要压缩提交。
  • 阻止压缩提交。

建议贡献者压缩提交:新贡献者可能不知道要压缩 PR 中的提交。 如果是这种情况,Approver 要给出压缩提交的建议,并贴附有用的链接, 并在贡献者需要帮助时伸出援手。这里有一些有用的链接:

协助贡献者压缩提交:如果贡献者压缩提交遇到难题或合并 PR 的时间紧迫, 你可以协助贡献者执行压缩提交的操作。

  • kubernetes/website 仓库被配置为允许压缩提交后合并 PR。 你只需选择 Squash commits 按钮。
  • 在 PR 中,如果贡献者允许 Maintainer 们管理 PR,你就可以为他们压缩提交并将其 fork 更新为最新结果。 在你执行压缩提交之后,请建议贡献者将压缩后的提交拉到他们本地的克隆副本。
  • 你可以使用标签让 GitHub 压缩提交,这样 Tide / GitHub 就会对提交执行压缩; 你还可以在合并 PR 时点选 Squash commits 按钮。

建议贡献者避免压缩提交

  • 如果一个提交做了一些破坏性或不明智的修改,那最后一个提交可用于回滚错误,这种情况不要压缩提交。 即使通过 GitHub 上 PR 中的 "Files changed" 页签以及 Netlify 预览看起来都正常, 合并这种 PR 可能会在其他 fork 中造成 rebase 或合并冲突。 你看到这种情况要进行合理的干预,避免对其他贡献者造成麻烦。

千万不要压缩提交

  • 如果你为新版本发起了一次本地化批量作业或为新版发布许多文档,那你要合并到的分支将与用户 fork 的分支不同, 这种情况千万不要压缩提交。之所以不压缩提交,是因为你必须保持这些文件的提交历史记录。

7.5 - 本地化 Kubernetes 文档

此页面描述如何为其他语言的文档提供 本地化版本。

为现有的本地化做出贡献

你可以帮助添加或改进现有本地化的内容。在 Kubernetes Slack 中, 你能找到每个本地化的频道。还有一个通用的 SIG Docs Localizations Slack 频道, 你可以在这里打个招呼。

找到两个字母的语言代码

首先,有关本地化的两个字母的语言代码,请参考 ISO 639-1 标准。 例如,韩语的两个字母代码是 ko

一些语言使用 ISO-3166 定义的国家代码的小写版本及其语言代码。 例如,巴西葡萄牙语代码是 pt-br

派生(fork)并且克隆仓库

首先,为 kubernetes/website 仓库创建你自己的副本

然后,克隆你的 website 仓库副本并通过 cd 命令进入 website 目录:

git clone https://github.com/<username>/website
cd website

网站内容目录包括每种语言的子目录。你想要助力的本地化位于 content/<two-letter-code> 中。

建议更改

根据英文原件创建或更新你选择的本地化页面。 有关更多详细信息,请参阅本地化内容

如果你发现上游(英文)文档存在技术错误或其他问题, 你应该先修复上游文档,然后通过更新你正在处理的本地化来重复等效的修复。

请将 PR 限制为单个语言版本,因为多语言的 PR 内容修改可能难以审查。

按照内容改进建议提出对该本地化的更改。 该过程与提议更改上游(英文)内容非常相似。

开始新的本地化

如果你希望将 Kubernetes 文档本地化为一种新语言,你需要执行以下操作。

因为贡献者不能批准他们自己的拉取请求,你需要至少两个贡献者来开始本地化。

所有本地化团队都必须能够自我维持。 Kubernetes 网站很乐意托管你的作品,但要由你来翻译它并使现有的本地化内容保持最新。

你需要知道你的语言的两个字母的语言代码。 请查阅 ISO 639-1 标准 以查找你的本地化的两字母语言代码。例如,韩语的两字母代码是 ko

如果你开始本地化的语言在不同地方使用,并且变体之间存在显着差异, 则将小写的 ISO-3166 国家/地区代码与语言双字母代码结合起来可能是有意义的。 例如,巴西葡萄牙语被本地化为 pt-br

当你开始新的本地化时,你必须先本地化所有最少要求的内容, Kubernetes 项目才能将你的更改发布到当前网站。

SIG Docs 可以帮助你在单独的分支上工作,以便你可以逐步实现该目标。

找到社区

让 Kubernetes SIG Docs 知道你有兴趣创建本地化! 加入 SIG Docs Slack 频道SIG Docs Localizations Slack 频道。 其他本地化团队很乐意帮助你入门并回答你的问题。

也请考虑参加 SIG Docs 本地化小组的会议。 SIG Docs 本地化小组的任务是与 SIG Docs 本地化团队合作, 共同定义和记录创建本地化贡献指南的流程。 此外,SIG Docs 本地化小组将寻找机会在本地化团队中创建和共享通用工具, 并为 SIG Docs 领导团队确定新要求。如果你对本次会议有任何疑问,请在 SIG Docs Localizations Slack 频道中提问。

你还可以在 kubernetes/community 仓库中为你的本地化创建一个 Slack 频道。 有关添加 Slack 频道的示例, 请参阅为波斯语添加频道的 PR。

加入到 Kubernetes GitHub 组织

提交本地化 PR 后,你可以成为 Kubernetes GitHub 组织的成员。 团队中的每个人都需要在 kubernetes/org 仓库中创建自己的组织成员申请

在 GitHub 中添加你的本地化团队

接下来,将你的 Kubernetes 本地化团队添加到 sig-docs/teams.yaml。 有关添加本地化团队的示例,请参见添加西班牙本地化团队的 PR。

@kubernetes/sig-docs-**-owners 成员可以批准更改对应本地化目录 /content/**/ 中内容的 PR,并仅限这类 PR。 对于每个本地化,@kubernetes/sig-docs-**-reviews 团队被自动分派新 PR 的审阅任务。 @kubernetes/website-maintainers 成员可以创建新的本地化分支来协调翻译工作。 @kubernetes/website-milestone-maintainers 成员可以使用 /milestone Prow 命令为 Issue 或 PR 设定里程碑。

配置工作流程

接下来,在 kubernetes/test-infra 仓库中为你的本地化添加一个 GitHub 标签。 标签可让你过滤 Issue 和针对特定语言的 PR。

有关添加标签的示例,请参见添加意大利语标签的 PR。

修改站点配置

Kubernetes 网站使用 Hugo 作为其 Web 框架。网站的 Hugo 配置位于 hugo.toml 文件中。 为了支持新的本地化,你需要修改 hugo.toml

在现有的 [languages] 下,将新语言的配置添加到 hugo.toml 中。 例如,下面是德语的配置示例:

[languages.de]
title = "Kubernetes"
description = "Produktionsreife Container-Verwaltung"
languageName = "Deutsch (German)"
languageNameLatinScript = "Deutsch"
contentDir = "content/de"
weight = 8

语言选择栏列出了 languageName 的值。 将 languageName 赋值为"本地脚本中的语言名称(拉丁脚本中的语言名称)"。 例如,languageName = "한국어 (Korean)"languageName = "Deutsch (German)"

languageNameLatinScript 可用于访问拉丁脚本中的语言名称并在主题中使用。 将 languageNameLatinScript 赋值为"拉丁脚本中的语言名称"。 例如,languageNameLatinScript ="Korean"languageNameLatinScript = "Deutsch"

weight 参数决定语言选择栏中的语言顺序, 优先显示权重较低的语言。 分配 weight 参数时,检查现有语言块并调整其权重以确保它们相对于所有语言 (包括任何新添加的语言)按排序顺序非常重要。

有关 Hugo 多语言支持的更多信息,请参阅"多语言模式"。

添加一个新的本地化目录

将特定语言的子目录添加到仓库中的 content 文件夹下。 例如,德语的两个字母的代码是 de

mkdir content/de

你还需要在 data/i18n 中为本地化字符串创建一个目录; 以现有的本地化为例。要使用这些新字符串, 你还必须创建从 i18n/<localization>.tomldata/i18n/<localization>/<localization>.toml 中实际字符串配置的符号链接(记得提交符号链接关联)。

例如,对于德语,字符串位于 data/i18n/de/de.toml 中, 而 i18n/de.toml 是指向 data/i18n/de/de.toml 的符号链接。

本地化社区行为准则

cncf/foundation 仓库提交 PR,添加你所用语言版本的行为准则。

设置 OWNERS 文件

要设置每个对本地化做出贡献用户的角色,请在特定于语言的子目录内创建一个 OWNERS 文件,其中:

有关 OWNERS 文件的更多信息,请访问 go.k8s.io/owners

语言代码为 es西班牙语 OWNERS 文件看起来像:

# 参见 OWNERS 文档,位于 https://go.k8s.io/owners

# 这是西班牙语的本地化项目
# 各团队和成员名单位于 https://github.com/orgs/kubernetes/teams

reviewers:
- sig-docs-es-reviews

approvers:
- sig-docs-es-owners

labels:
- language/es

添加了特定语言的 OWNERS 文件之后,使用新的 Kubernetes 本地化团队、 sig-docs-**-ownerssig-docs-**-reviews 列表更新根目录下的 OWNERS_ALIAES 文件。

对于每个团队, 请按字母顺序添加在 GitHub 中添加你的本地化团队中所请求的 GitHub 用户列表。

--- a/OWNERS_ALIASES
+++ b/OWNERS_ALIASES
@@ -48,6 +48,14 @@ aliases:
     - stewart-yu
     - xiangpengzhao
     - zhangxiaoyu-zidif
+  sig-docs-es-owners: # Admins for Spanish content
+    - alexbrand
+    - raelga
+  sig-docs-es-reviews: # PR reviews for Spanish content
+    - alexbrand
+    - electrocucaracha
+    - glo-pena
+    - raelga
   sig-docs-fr-owners: # Admins for French content
     - perriea
     - remyleone

发起拉取请求

接下来,发起拉取请求(PR) 将本地化添加到 kubernetes/website 存储库。 PR 必须包含所有最低要求内容才能获得批准。

有关添加新本地化的示例, 请参阅启用法语文档的 PR。

添加本地化的 README 文件

为了指导其他本地化贡献者,请在 kubernetes/website 的根目录添加一个新的 README-**.md, 其中 ** 是两个字母的语言代码。例如,德语 README 文件为 README-de.md

在本地化的 README-**.md 文件中为本地化贡献者提供指导。包含 README.md 中包含的相同信息,以及:

  • 本地化项目的联系人
  • 任何特定于本地化的信息

创建本地化的 README 文件后,请在英语版文件 README.md 中添加指向该文件的链接, 并给出英文形式的联系信息。你可以提供 GitHub ID、电子邮件地址、 Slack 频道或其他联系方式。你还必须提供指向本地化的社区行为准则的链接。

启动你的新本地化

一旦本地化满足工作流程和最小输出的要求,SIG Docs 将:

本地化文档

本地化所有 Kubernetes 文档是一项艰巨的任务。从小做起,循序渐进。

最低要求内容

所有本地化至少必须包括:

描述网址
主页所有标题和副标题网址
安装所有标题和副标题网址
教程Kubernetes 基础, Hello Minikube
网站字符串所有网站字符串
发行版本所有标题和副标题 URL

翻译后的文档必须保存在自己的 content/**/ 子目录中,否则将遵循与英文源相同的 URL 路径。 例如,要准备将 Kubernetes 基础教程翻译为德语, 请在 content/de/ 文件夹下创建一个子文件夹并复制英文源:

mkdir -p content/de/docs/tutorials
cp content/en/docs/tutorials/kubernetes-basics.md content/de/docs/tutorials/kubernetes-basics.md

翻译工具可以加快翻译过程。例如,某些编辑器提供了用于快速翻译文本的插件。

为了确保语法和含义的准确性,本地化团队的成员应在发布之前仔细检查所有由机器生成的翻译。

本地化 SVG 图片

Kubernetes 项目建议尽可能使用矢量(SVG)图片,因为这些图片对于本地化团队来说更容易编辑。 如果你发现一个光栅图(位图)需要本地化翻译,先将英文版本重新绘制为矢量图片,然后再进行本地化。

在翻译 SVG(可缩放矢量图)图片中的文本时,需要遵循几点指导方针, 以确保准确性并在不同语言版本之间保持一致。 Kubernetes 文档中常用 SVG 图片来说明概念、工作流和图表。

  1. 识别可翻译文本:首先辨别出 SVG 图片中需要翻译的文本元素。 这些元素通常包括标签、标题、注解或任何传达信息的文本。
  1. 编辑 SVG 文件:SVG 文件是基于 XML 的,这意味着可以使用文本编辑器进行编辑。 但请注意 Kubernetes 文档中的大部分图片已经将文本转换为曲线以避免字体兼容性问题。 在这种情况下,建议使用 Inkscape 这类专业的 SVG 编辑软件, 打开 SVG 文件并定位需要翻译的文本元素。
  1. 翻译文本:将原始的文本替换为目标语言的译文。确保翻译的文本准确传达所需的含义, 并适配图片中可用的空间。在处理使用拉丁字母的语言时,应使用 Open Sans 字体系列。 你可以从此处下载 Open Sans 字体: Open Sans Typeface
  1. 文本转换为曲线:如前所述,为解决字体兼容性问题,建议将翻译后的文本转换为曲线或路径。 即使用户的系统没有原始 SVG 中所使用的确切字体,将文本转换为曲线也可确保最终图片能正确显示译文。
  1. 检查和测试:完成必要的翻译并将文本转换为曲线后,保存并检查更新后的 SVG 图片,确保文本正确显示和对齐。 参考在本地预览你的变更

源文件

本地化必须基于本地化团队所针对的特定发行版本中的英文文件。 每个本地化团队可以决定要针对哪个发行版本,在下文中称作 目标版本(target version)

要查找你的目标版本的源文件:

  1. 导航到 Kubernetes website 仓库,网址为 https://github.com/kubernetes/website
  2. 从下面的表格中选择你的目标版本分支:
目标版本分支
最新版本main
上一个版本release-1.29
下一个版本dev-1.31

main 分支中保存的是当前发行版本 v1.30 的内容。 发行团队会在下一个发行版本 v1.31 出现之前创建 release-1.30 分支。

i18n/ 中的网站字符串

本地化必须在新的语言特定文件中包含 data/i18n/en/en.toml 的内容。以德语为例:data/i18n/de/de.toml

将新的本地化文件和目录添加到 data/i18n/。例如德语(de):

mkdir -p data/i18n/de
cp data/i18n/en/en.toml data/i18n/de/de.toml

修改文件顶部的注释以适合你的本地化,然后翻译每个字符串的值。 例如,这是搜索表单的德语占位符文本:

[ui_search_placeholder]
other = "Suchen"

本地化网站字符串允许你自定义网站范围的文本和特性:例如每个页面页脚中的版权声明文本。

特定语言的本地化指南

作为本地化团队,你可以通过创建特定语言的本地化指南来正式确定团队需遵循的最佳实践。 请参见中文本地化指南

特定语言的 Zoom 会议

如果本地化项目需要单独的会议时间, 请联系 SIG Docs 联合主席或技术主管以创建新的重复 Zoom 会议和日历邀请。 仅当团队维持在足够大的规模并需要单独的会议时才需要这样做。

根据 CNCF 政策,本地化团队必须将他们的会议上传到 SIG Docs YouTube 播放列表。 SIG Docs 联合主席或技术主管可以帮助完成该过程,直到 SIG Docs 实现自动化。

分支策略

因为本地化项目是高度协同的工作, 特别是在刚开始本地化并且本地化尚未生效时,我们鼓励团队基于共享的本地化分支工作。

在本地化分支上协作需要:

  1. @kubernetes/website-maintainers 中的团队成员从 https://github.com/kubernetes/website 原有分支新建一个本地化分支。

    当你给 kubernetes/org 仓库添加你的本地化团队时, 你的团队批准人便加入了 @kubernetes/website-maintainers 团队。

    我们推荐以下分支命名方案:

    dev-<source version>-<language code>.<team milestone>

    例如,一个德语本地化团队的批准人基于 Kubernetes v1.12 版本的源分支, 直接新建了 kubernetes/website 仓库的本地化分支 dev-1.12-de.1

  1. 个人贡献者基于本地化分支创建新的特性分支。

    例如,一个德语贡献者新建了一个拉取请求, 并将 username:local-branch-name 更改为 kubernetes:dev-1.12-de.1

  2. 批准人审查功能分支并将其合并到本地化分支中。

  3. 批准人会定期发起并批准新的 PR,将本地化分支合并到其源分支。 在批准 PR 之前,请确保先 squash commits。

根据需要重复步骤 1-4,直到完成本地化工作。例如,随后的德语本地化分支将是: dev-1.12-de.2dev-1.12-de.3 等等。

团队必须将本地化内容合入到发布分支中,该发布分支是内容的来源。例如:

  • 源于 main 分支的本地化分支必须被合并到 main
  • 源于 release-{{ skew "prevMinorVersion" }} 的本地化分支必须被合并到 release-{{ skew "prevMinorVersion" }}

在团队每个里程碑的开始时段,创建一个 issue 来比较先前的本地化分支和当前的本地化分支之间的上游变化很有帮助。 现在有两个脚本用来比较上游的变化。

虽然只有批准人才能创建新的本地化分支并合并 PR, 任何人都可以为新的本地化分支提交一个拉取请求(PR)。不需要特殊权限。

有关基于派生或直接从仓库开展工作的更多信息,请参见"派生和克隆"

上游贡献

Sig Docs 欢迎对英文原文的上游贡献和修正。

7.6 - 参与 SIG Docs

SIG Docs 是 Kubernetes 项目 特别兴趣小组 中的一个,负责编写、更新和维护 Kubernetes 的总体文档。 参见社区 GitHub 仓库中 SIG Docs 以进一步了解该 SIG。

SIG Docs 欢迎所有贡献者提供内容和审阅。任何人可以提交拉取请求(PR)。 欢迎所有人对文档内容创建 Issue 和对正在处理中的 PR 进行评论。

你也可以成为成员(member)评阅人(reviewer) 或者 批准人(approver)。 这些角色拥有更高的权限,且需要承担批准和提交变更的责任。 有关 Kubernetes 社区中的成员如何工作的更多信息,请参见 社区成员身份

本文档的其余部分概述了这些角色在 SIG Docs 中发挥作用的一些独特方式。 SIG Docs 负责维护 Kubernetes 最面向公众的方面之一 —— Kubernetes 网站和文档。

SIG Docs 主席

每个 SIG,包括 SIG Docs,都会选出一位或多位成员作为主席。 主席会成为 SIG Docs 和其他 Kubernetes 组织的联络接口人。 他们需要了解整个 Kubernetes 项目的架构,并明白 SIG Docs 如何在其中运作。 如需查询当前的主席名单,请查阅 领导人员

SIG Docs 团队和自动化

SIG 文档中的自动化服务依赖于两种不同的机制: GitHub 团队和 OWNERS 文件。

GitHub 团队

GitHub 上有两类 SIG Docs 团队:

  • @sig-docs-{language}-owners 包含批准人和牵头人
  • @sig-docs-{language}-reviews 包含评阅人

可以在 GitHub 的评论中使用团队的名称 @name 来与团队成员沟通。

有时候 Prow 所定义的团队和 GitHub 团队有所重叠,并不完全一致。 对于指派 Issue、PR 和批准 PR,自动化工具使用来自 OWNERS 文件的信息。

OWNERS 文件和扉页

Kubernetes 项目使用名为 prow 的自动化工具来自动处理 GitHub issue 和 PR。 Kubernetes website 仓库 使用了两个 prow 插件

  • blunderbuss
  • approve

这两个插件使用位于 kubernetes/website 仓库顶层的 OWNERS 文件和 OWNERS_ALIASES 文件来控制 prow 在仓库范围的工作方式。

OWNERS 文件包含 SIG Docs 评阅人和批准人的列表。 OWNERS 文件也可以存在于子目录中,可以在子目录层级重新设置哪些人可以作为评阅人和 批准人,并将这一设定传递到下层子目录。 关于 OWNERS 的更多信息,请参考 OWNERS 文档。

此外,每个独立的 Markdown 文件都可以在其前言部分列出评阅人和批准人, 每一项可以是 GitHub 用户名,也可以是 GitHub 组名。

结合 OWNERS 文件及 Markdown 文件的前言信息,自动化系统可以给 PR 作者可以就应该 向谁请求技术和文字评阅给出建议。

PR 是怎样被合并的

当某个拉取请求(PR)被合并到用来发布内容的分支,对应的内容就会被发布到 http://kubernetes.io。 为了确保我们所发布的内容的质量足够好,合并 PR 的权限仅限于 SIG Docs 批准人。下面是合并的工作机制:

  • 当某个 PR 同时具有 lgtmapprove 标签,没有 hold 标签且通过所有测试时, 该 PR 会被自动合并。
  • Kubernetes 组织的成员和 SIG Docs 批准人可以添加评论以阻止给定 PR 的自动合并, 即通过 /hold 评论或者收回某个 /lgtm 评论实现这点。
  • 所有 Kubernetes 成员可以通过 /lgtm 评论添加 lgtm 标签。
  • 只有 SIG Docs 批准人可以通过评论 /approve 合并 PR。 某些批准人还会执行一些其他角色,例如 PR 管理者SIG Docs 主席等。

接下来

关于贡献 Kubernetes 文档的更多信息,请参考:

7.6.1 - 角色与责任

任何人都可以为 Kubernetes 作出贡献。随着你对 SIG Docs 的贡献增多,你可以申请 社区内不同级别的成员资格。 这些角色使得你可以在社区中承担更多的责任。 每个角色都需要更多的时间和投入。具体包括:

  • 任何人(Anyone):为 Kubernetes 文档作出贡献的普通贡献者。
  • 成员(Members):可以对 Issue 进行分派和判别,对 PR 提出无约束性的评审意见。
  • 评审人(Reviewers):可以领导对文档 PR 的评审,可以对变更的质量进行判别。
  • 批准人(Approvers):可以领导对文档的评审并合并变更。

任何人(Anyone)

任何拥有 GitHub 账号的人都可以对 Kubernetes 作出贡献。SIG Docs 欢迎所有新的贡献者。

任何人都可以:

签署了 CLA 之后,任何人还可以:

  • 发起拉取请求(PR),改进现有内容、添加新内容、撰写博客或者案例分析
  • 创建示意图、图形资产或者嵌入式的截屏和视频内容

进一步的详细信息,可参见贡献新内容

成员(Members)

成员是指那些对 kubernetes/website 提交很多拉取请求(PR)的人。 成员都要加入 Kubernetes GitHub 组织

成员可以:

  • 执行任何人节区所列举操作

  • 使用 /lgtm 评论添加 LGTM (looks good to me(我觉得可以)) 标签到某个 PR

  • 利用 /hold 评论来阻止某个 PR 被合并

  • 使用 /assign 评论为某个 PR 指定评审人

  • 对 PR 提供非约束性的评审意见

  • 使用自动化机制来对 Issue 进行判别和分类

  • 为新功能特性撰写文档

成为一个成员

在你成功地提交至少 5 个 PR 并满足 相关条件 之后:

  1. 找到两个评审人批准人为你的成员身份提供 担保

    通过 Kubernetes Slack 上的 #sig-docs 频道 或者 SIG Docs 邮件列表 来寻找为你担保的人。

  2. kubernetes/org 仓库 使用 Organization Membership Request Issue 模板登记一个 Issue。

  1. 告知你的担保人你所创建的 Issue,你可以:

    • 在 Issue 中 @<GitHub-username> 提及他们的 GitHub 用户名
    • 通过 Slack 或 email 直接发送给他们 Issue 链接

    担保人会通过 +1 投票来批准你的请求。一旦你的担保人批准了该请求, 某个 Kubernetes GitHub 管理员会将你添加为组织成员。恭喜!

    如果你的成员请求未被接受,你会收到一些反馈。 当处理完反馈意见之后,可以再次发起申请。

  2. 登录你的邮件账户,接受来自 Kubernetes GitHub 组织发出的成员邀请。

评审人(Reviewers)

评审人负责评审悬决的 PR。 与成员所给的反馈不同,身为 PR 作者必须处理评审人的反馈。 评审人是 @kubernetes/sig-docs-{language}-reviews GitHub 团队的成员。

评审人可以:

  • 执行任何人成员节区所列举的操作

  • 评审 PR 并提供具约束性的反馈信息

  • 编辑代码中用户可见的字符串

  • 改进代码注释

你可以是 SIG Docs 的评审人,也可以是某个主题领域的文档的评审人。

为 PR 指派评审人

自动化引擎会为每个 PR 自动指派评审人。 你可以通过为 PR 添加评论 /assign [@_github_handle] 来请求某个特定评审人来评审。

如果所指派的评审人未能及时评审,其他的评审人也可以参与进来。 你可以根据需要指派技术评审人。

使用 /lgtm

LGTM 代表的是 “Looks Good To Me (我觉得可以)”,用来标示某个 PR 在技术上是准确的,可以被合并。 所有 PR 都需要来自某评审人的 /lgtm 评论和来自某批准人的 /approve 评论。

来自评审人的 /lgtm 评论是具有约束性的,会触发自动化引擎添加 lgtm 标签。

成为评审人

当你满足相关条件时, 你可以成为一个 SIG Docs 评审人。 来自其他 SIG 的评审人必须为 SIG Docs 单独申请评审人资格。

申请流程如下:

  1. 发起 PR,将你的 GitHub 用户名添加到 kubernetes/website 仓库中 OWNERS_ALIASES 文件的对应节区。

  2. 将 PR 指派给一个或多个 SIG Docs 批准人(sig-docs-{language}-owners 下列举的用户名)。

申请被批准之后,SIG Docs Leads 之一会将你添加到合适的 GitHub 团队。 一旦添加完成,@k8s-ci-robot 会在处理未来的 PR 时,将 PR 指派给你或者建议你来评审某 PR。

批准人(Approvers)

批准人负责评审和批准 PR 以将其合并。 批准人是 @kubernetes/sig-docs-{language}-owners GitHub 团队的成员。

批准人可以执行以下操作:

  • 执行列举在任何人成员评审人节区的操作
  • 通过使用 /approve 评论来批准、合并 PR,发布贡献者所贡献的内容。
  • 就样式指南给出改进建议
  • 对文档测试给出改进建议
  • 对 Kubernetes 网站或其他工具给出改进建议

如果某个 PR 已有 /lgtm 标签,或者批准人再回复一个 /lgtm,则这个 PR 会自动合并。 SIG Docs 批准人应该只在不需要额外的技术评审的情况下才可以标记 /lgtm

批准 PR

只有批准人和 SIG Docs Leads 可以将 PR 合并到网站仓库。 这意味着以下责任:

  • 批准人可以使用 /approve 命令将 PR 合并到仓库中。

  • 确保所提议的变更满足文档内容指南要求。

    如果有问题或者疑惑,可以根据需要请他人帮助评审。

  • /approve PR 之前,须验证 Netlify 测试是否正常通过。

    批准之前必须通过 Netlify 测试
  • 在批准之前,请访问 Netlify 的页面预览来确保变更内容可正常显示。

  • 参与 PR 管理者轮值排班 执行时长为一周的 PR 管理。SIG Docs 期望所有批准人都参与到此轮值工作中。 更多细节可参见 PR 管理者

成为批准人

当你满足一定条件时,可以成为一个 SIG Docs 批准人。 来自其他 SIG 的批准人也必须在 SIG Docs 独立申请批准人资格。

申请流程如下:

  1. 发起一个 PR,将自己添加到 kubernetes/website 仓库中 OWNERS_ALIASES 文件的对应节区。

  2. 将 PR 指派给一个或多个 SIG Docs 批准人。

请求被批准之后,SIG Docs Leads 之一会将你添加到对应的 GitHub 团队。 一旦添加完成,K8s-ci-robot 会在处理未来的 PR 时,将 PR 指派给你或者建议你来评审某 PR。

接下来

  • 阅读 PR 管理者,了解所有批准人轮值的角色。

7.6.2 - Issue 管理者

除了承担 PR 管理者的职责外, SIG Docs 正式的批准人(Approver)、评审人(Reviewer)和成员(Member) 按周轮流归类仓库的 Issue

职责

在为期一周的轮值期内,Issue 管理者每天负责:

  • 对收到的 Issue 进行日常分类和标记。有关 SIG Docs 如何使用元数据的指导说明, 参阅归类 Issue
  • 密切关注 kubernetes/website 代码仓库中陈旧和过期的 Issue。
  • 维护 Issues 看板

要求

  • 必须是 Kubernetes 组织的活跃成员。
  • 至少为 Kubernetes 做了 15 个非小微的贡献 (其中某些应是直接针对 kubernetes/website 的贡献)。
  • 已经以非正式身份履行该职责。

对管理者有帮助的 Prow 命令

以下是 Issue 管理者的一些常用命令:

# 重新打开 Issue
/reopen

# 将不切合 k/website 的 Issue 转移到其他代码仓库
/transfer[-issue]

# 更改陈旧 Issue 的状态
/remove-lifecycle rotten

# 更改过期 Issue 的状态
/remove-lifecycle stale

# 为 Issue 指派 SIG
/sig <sig_name>

# 添加具体领域
/area <area_name>

# 对新手友好的 Issue
/good-first-issue

# 需要帮助的 Issue
/help wanted

# 将 Issue 标记为某种支持
/kind support

# 接受某个 Issue 的归类
/triage accepted

# 关闭还未处理且未修复的 Issue
/close not-planned

要查找更多 Prow 命令,请参阅命令帮助文档。

何时关闭 Issue

一个开源项目想要成功,良好的 Issue 管理非常关键。 但解决 Issue 也很重要,这样才能维护代码仓库,并与贡献者和用户进行清晰明确的交流。

关闭 Issue 的时机包括:

  • 类似的 Issue 被多次报告。你首先需要将其标记为 /triage duplicate; 将其链接到主要 Issue 然后关闭它。还建议将用户引导至最初的 Issue。
  • 通过所提供的信息很难理解和解决作者提出的 Issue。 但要鼓励用户提供更多细节,或者在以后可以重现 Issue 时重新打开此 Issue 。
  • 相同的功能在其他地方已实现。管理者可以关闭此 Issue 并将用户引导至适当的位置。
  • 报告的 Issue 当前未被计划或不符合项目的目标。
  • 如果 Issue 看起来是垃圾信息并且明显不相关。
  • 如果 Issue 与外部限制或依赖项有关并且超出了本项目的控制范围。

要关闭 Issue,可以在 Issue 中留下一条 /close 的评论。

7.6.3 - PR 管理者

SIG Docs 的批准人(Approver)们每周轮流负责 管理仓库的 PR

本节介绍 PR 管理者的职责。关于如何提供较好的评审意见, 可参阅评审变更

职责

在为期一周的轮值期内,PR 管理者要:

  • 检查悬决的 PR 的质量并确保它们符合样式指南内容指南要求。

    • 首先查看最小的 PR(size/XS),然后逐渐扩展到最大的 PR(size/XXL),尽可能多地评审 PR。
  • 确保贡献者签署 CLA
    • 使用此脚本自动提醒尚未签署 CLA 的贡献者签署 CLA。
  • 针对变更提供反馈,请求其他 SIG 的成员进行技术审核。
    • 为 PR 所建议的内容更改提供就地反馈。
    • 如果你需要验证内容,请在 PR 上发表评论并要求贡献者提供更多细节。
    • 设置相关的 sig/ 标签。
    • 如果需要,根据文件开头的 reviewers: 块来指派评审人。
    • 你也可以通过在 PR 上作出 @kubernetes/<sig>-pr-reviews 的评论以标记需要某个 SIG 来评审。
  • 使用 /approve 评论来批准可以合并的 PR,在 PR 就绪时将其合并。
    • PR 在被合并之前,应该有来自其他成员的 /lgtm 评论。
    • 可以考虑接受那些技术上准确、 但文风上不满足风格指南要求的 PR。 批准变更时,可以登记一个新的 Issue 来解决文档风格问题。 你通常可以将这些风格修复问题标记为 good first issue
    • 将风格修复事项标记为 good first issue 可以很好地确保向新加入的贡献者分派一些比较简单的任务, 这有助于接纳新的贡献者。

对管理者有用的 GitHub 查询

执行管理操作时,以下查询很有用。完成以下这些查询后,剩余的要评审的 PR 列表通常很小。 这些查询都不包含本地化的 PR,并仅包含主分支上的 PR(除了最后一个查询)。

  • 未签署 CLA,不可合并的 PR: 提醒贡献者签署 CLA。如果机器人和评审者都已经提醒他们,请关闭 PR,并提醒他们在签署 CLA 后可以重新提交。

    在作者没有签署 CLA 之前,不要评审他们的 PR!

  • 需要 LGTM: 列举需要来自成员的 LGTM 评论的 PR。 如果需要技术审查,请告知机器人所建议的评审者。 如果 PR 继续改进,就地提供更改建议或反馈。

  • 已有 LGTM标签,需要 Docs 团队批准: 列举需要 /approve 评论来合并的 PR。

  • 快速批阅: 列举针对主分支的、没有明确合并障碍的 PR。 在浏览 PR 时,可以将 "XS" 尺寸标签更改为 "S"、"M"、"L"、"XL"、"XXL"。

  • 非主分支的 PR: 如果 PR 针对 dev- 分支,则表示它适用于即将发布的版本。 请添加带有 /assign @<负责人的 github 账号>,将其指派给 发行版本负责人。 如果 PR 是针对旧分支,请帮助 PR 作者确定是否所针对的是最合适的分支。

对管理者有用的 Prow 命令

# 添加 English 标签
/language en

# 如果 PR 包含多个提交(commits),添加 squash 标签
/label tide/merge-method-squash

# 使用 Prow 来为 PR 重设标题(例如一个正在处理 [WIP] 的 PR 或为 PR 提供更好的细节信息)
/retitle [WIP] <TITLE>

何时关闭 PR

审查和批准是缩短和更新我们的 PR 队列的一种方式;另一种方式是关闭 PR。

当以下条件满足时,可以关闭 PR:

  • 作者两周内未签署 CLA。 PR 作者可以在签署 CLA 后重新打开 PR,因此这是确保未签署 CLA 的 PR 不会被合并的一种风险较低的方法。

  • 作者在两周或更长时间内未回复评论或反馈。

不要害怕关闭 PR。贡献者可以轻松地重新打开并继续工作。 通常,关闭通知会激励作者继续完成其贡献。

要关闭 PR,请在 PR 上输入 /close 评论。

PR 管理者影子计划

2021 下半年,SIG Docs 推出了 PR 管理者影子计划(PR Wrangler Shadow Program)。 该计划旨在帮助新的贡献者们了解 PR 管理流程。

成为一名影子

7.7 - 更新参考文档

本节的主题是描述如何生成 Kubernetes 参考指南。 要生成参考文档,请参考下面的指南:

7.7.1 - 参考文档快速入门

本页讨论如何使用 update-imported-docs.py 脚本来生成 Kubernetes 参考文档。 此脚本将构建的配置过程自动化,并为某个发行版本生成参考文档。

准备开始

需求

  • 你的 PATH 环境变量必须包含所需要的构建工具,例如 Go 程序和 python

  • 你需要知道如何为一个 GitHub 仓库创建拉取请求(PR)。 这牵涉到创建仓库的派生(fork)副本。 有关信息可进一步查看基于本地副本开展工作

获取文档仓库

确保你的 website 派生仓库与 GitHub 上的 kubernetes/website 远程仓库(main 分支)保持同步, 并克隆你的派生仓库。

mkdir github.com
cd github.com
git clone git@github.com:<your_github_username>/website.git

确定你的克隆副本的根目录。例如,如果你按照前面的步骤获取了仓库,你的根目录 会是 github.com/website。接下来的步骤中,<web-base> 用来指代你的根目录。

update-imported-docs 的概述

脚本 update-imported-docs.py 位于 <web-base>/update-imported-docs/ 目录下, 能够生成以下参考文档:

  • Kubernetes 组件和工具的参考页面
  • kubectl 命令参考文档
  • Kubernetes API 参考文档

脚本 update-imported-docs.py 基于 Kubernetes 源代码生成参考文档。 过程中会在你的机器的 /tmp 目录下创建临时目录,克隆所需要的仓库 kubernetes/kuberneteskubernetes-sigs/reference-docs 到此临时目录。 脚本会将 GOPATH 环境变量设置为指向此临时目录。 此外,脚本会设置三个环境变量:

  • K8S_RELEASE
  • K8S_ROOT
  • K8S_WEBROOT

脚本需要两个参数才能成功运行:

  • 一个 YAML 配置文件(reference.yml
  • 一个发行版本字符串,例如:1.17

配置文件中包含 generate-command 字段,其中定义了一系列来自于 kubernetes-sigs/reference-docs/Makefile 的构建指令。 变量 K8S_RELEASE 用来确定所针对的发行版本。

脚本 update-imported-docs.py 执行以下步骤:

  1. 克隆配置文件中所指定的相关仓库。就生成参考文档这一目的而言,要克隆的仓库默认为 kubernetes-sigs/reference-docs
  2. 在所克隆的仓库下运行命令,准备文档生成器,之后生成 HTML 和 Markdown 文件。
  3. 将所生成的 HTML 和 Markdown 文件复制到 <web-base> 本地克隆副本中, 放在配置文件中所指定的目录下。
  4. 更新 kubectl.md 文件中对 kubectl 命令文档的链接,使之指向 kubectl 命令参考中对应的节区。

当所生成的文件已经被放到 <web-base> 目录下,你就可以将其提交到你的派生副本中, 并基于所作提交发起拉取请求(PR)到 kubernetes/website 仓库。

配置文件格式

每个配置文件可以包含多个被导入的仓库。当必要时,你可以通过手工编辑此文件进行定制。 你也可以通过创建新的配置文件来导入其他文档集合。 下面是 YAML 配置文件的一个例子:

repos:
- name: community
  remote: https://github.com/kubernetes/community.git
  branch: master
  files:
  - src: contributors/devel/README.md
    dst: docs/imported/community/devel.md
  - src: contributors/guide/README.md
    dst: docs/imported/community/guide.md

通过工具导入的单页面的 Markdown 文档必须遵从文档样式指南

定制 reference.yml

打开 <web-base>/update-imported-docs/reference.yml 文件进行编辑。 在不了解参考文档构造命令的情况下,不要更改 generate-command 字段的内容。 你一般不需要更新 reference.yml 文件。不过也有时候上游的源代码发生变化, 导致需要对配置文件进行更改(例如:Golang 版本依赖或者第三方库发生变化)。 如果你遇到类似问题,请在 Kubernetes Slack 的 #sig-docs 频道 联系 SIG-Docs 团队。

reference.yml 文件中,files 属性包含了一组 srcdst 字段。 src 字段给出在所克隆的 kubernetes-sigs/reference-docs 构造目录中生成的 Markdown 文件的位置,而 dst 字段则给出了对应文件要复制到的、所克隆的 kubernetes/website 仓库中的位置。例如:

repos:
- name: reference-docs
  remote: https://github.com/kubernetes-sigs/reference-docs.git
  files:
  - src: gen-compdocs/build/kube-apiserver.md
    dst: content/en/docs/reference/command-line-tools-reference/kube-apiserver.md
  ...

注意,如果从同一源目录中有很多文件要复制到目标目录,你可以在为 src 所设置的值中使用通配符。这时,为 dst 所设置的值必须是目录名称。例如:

  files:
  - src: gen-compdocs/build/kubeadm*.md
    dst: content/en/docs/reference/setup-tools/kubeadm/generated/

运行 update-imported-docs 工具

你可以用如下方式运行 update-imported-docs.py 工具:

cd <web-base>/update-imported-docs
./update-imported-docs.py <configuration-file.yml> <release-version>

例如:

./update-imported-docs.py reference.yml 1.17

配置文件 release.yml 中包含用来修复相对链接的指令。 若要修复导入文件中的相对链接,将 gen-absolute-links 属性设置为 true。你可以在 release.yml 文件中找到示例。

添加并提交 kubernetes/website 中的变更

枚举新生成并复制到 <web-base> 的文件:

cd <web-base>
git status

输出显示新生成和已修改的文件。取决于上游源代码的修改多少, 所生成的输出也会不同。

生成的 Kubernetes 组件文档

content/en/docs/reference/command-line-tools-reference/cloud-controller-manager.md
content/en/docs/reference/command-line-tools-reference/kube-apiserver.md
content/en/docs/reference/command-line-tools-reference/kube-controller-manager.md
content/en/docs/reference/command-line-tools-reference/kube-proxy.md
content/en/docs/reference/command-line-tools-reference/kube-scheduler.md
content/en/docs/reference/setup-tools/kubeadm/generated/kubeadm.md
content/en/docs/reference/kubectl/kubectl.md

生成的 kubectl 命令参考文件

static/docs/reference/generated/kubectl/kubectl-commands.html
static/docs/reference/generated/kubectl/navData.js
static/docs/reference/generated/kubectl/scroll.js
static/docs/reference/generated/kubectl/stylesheet.css
static/docs/reference/generated/kubectl/tabvisibility.js
static/docs/reference/generated/kubectl/node_modules/bootstrap/dist/css/bootstrap.min.css
static/docs/reference/generated/kubectl/node_modules/highlight.js/styles/default.css
static/docs/reference/generated/kubectl/node_modules/jquery.scrollto/jquery.scrollTo.min.js
static/docs/reference/generated/kubectl/node_modules/jquery/dist/jquery.min.js
static/docs/reference/generated/kubectl/css/font-awesome.min.css

生成的 Kubernetes API 参考目录与文件

static/docs/reference/generated/kubernetes-api/v1.30/index.html
static/docs/reference/generated/kubernetes-api/v1.30/js/navData.js
static/docs/reference/generated/kubernetes-api/v1.30/js/scroll.js
static/docs/reference/generated/kubernetes-api/v1.30/js/query.scrollTo.min.js
static/docs/reference/generated/kubernetes-api/v1.30/css/font-awesome.min.css
static/docs/reference/generated/kubernetes-api/v1.30/css/bootstrap.min.css
static/docs/reference/generated/kubernetes-api/v1.30/css/stylesheet.css
static/docs/reference/generated/kubernetes-api/v1.30/fonts/FontAwesome.otf
static/docs/reference/generated/kubernetes-api/v1.30/fonts/fontawesome-webfont.eot
static/docs/reference/generated/kubernetes-api/v1.30/fonts/fontawesome-webfont.svg
static/docs/reference/generated/kubernetes-api/v1.30/fonts/fontawesome-webfont.ttf
static/docs/reference/generated/kubernetes-api/v1.30/fonts/fontawesome-webfont.woff
static/docs/reference/generated/kubernetes-api/v1.30/fonts/fontawesome-webfont.woff2

运行 git addgit commit 提交文件。

创建拉取请求

接下来创建一个对 kubernetes/website 仓库的拉取请求(PR)。 监视所创建的 PR,并根据需要对评阅意见给出反馈。 继续监视该 PR 直到其被合并为止。

当你的 PR 被合并几分钟之后,你所做的对参考文档的变更就会出现 发布的文档上。

接下来

要手动设置所需的构造仓库,执行构建目标,以生成各个参考文档,可参考下面的指南:

7.7.2 - 为上游 Kubernetes 代码库做出贡献

此页面描述如何为上游 kubernetes/kubernetes 项目做出贡献,如修复 Kubernetes API 文档或 Kubernetes 组件(例如 kubeadmkube-apiserverkube-controller-manager 等) 中发现的错误。

如果你仅想从上游代码重新生成 Kubernetes API 或 kube-* 组件的参考文档。请参考以下说明:

准备开始

  • 你必须设置 GOPATH 环境变量,并且 etcd 的位置必须在 PATH 环境变量中。

基本说明

Kubernetes API 和 kube-* 组件(例如 kube-apiserverkube-controller-manager)的参考文档 是根据上游 Kubernetes 中的源代码自动生成的。

当你在生成的文档中看到错误时,你可能需要考虑创建一个 PR 用来在上游项目中对其进行修复。

克隆 Kubernetes 代码仓库

如果你还没有 kubernetes/kubernetes 代码仓库,请参照下列命令获取:

mkdir $GOPATH/src
cd $GOPATH/src
go get github.com/kubernetes/kubernetes

确定你的 kubernetes/kubernetes 代码仓库克隆的根目录。 例如,如果按照前面的步骤获取代码仓库,则你的根目录为 $GOPATH/src/github.com/kubernetes/kubernetes。 接下来其余步骤将你的根目录称为 <k8s-base>

确定你的 kubernetes-sigs/reference-docs 代码仓库克隆的根目录。 例如,如果按照前面的步骤获取代码仓库,则你的根目录为 $GOPATH/src/github.com/kubernetes-sigs/reference-docs。 接下来其余步骤将你的根目录称为 <rdocs-base>

编辑 Kubernetes 源代码

Kubernetes API 参考文档是根据 OpenAPI 规范自动生成的,该规范是从 Kubernetes 源代码生成的。 如果要更改 API 参考文档,第一步是更改 Kubernetes 源代码中的一个或多个注释。

kube-* 组件的文档也是从上游源代码生成的。你必须更改与要修复的组件相关的代码,才能修复生成的文档。

更改上游 Kubernetes 源代码

以下在 Kubernetes 源代码中编辑注释的示例。

在你本地的 kubernetes/kubernetes 代码仓库中,检出默认分支,并确保它是最新的:

cd <k8s-base>
git checkout master
git pull https://github.com/kubernetes/kubernetes master

假设默认分支中的下面源文件中包含拼写错误 "atmost":

kubernetes/kubernetes/staging/src/k8s.io/api/apps/v1/types.go

在你的本地环境中,打开 types.go 文件,然后将 "atmost" 更改为 "at most"。

以下命令验证你已经更改了文件:

git status

输出显示你在 master 分支上,types.go 源文件已被修改:

On branch master
...
    modified:   staging/src/k8s.io/api/apps/v1/types.go

提交已编辑的文件

运行 git addgit commit 命令提交到目前为止所做的更改。 在下一步中,你将进行第二次提交,将更改分成两个提交很重要。

生成 OpenAPI 规范和相关文件

进入 <k8s-base> 目录并运行以下脚本:

./hack/update-codegen.sh
./hack/update-openapi-spec.sh

运行 git status 命令查看生成的文件。

On branch master
...
    modified:   api/openapi-spec/swagger.json
    modified:   api/openapi-spec/v3/apis__apps__v1_openapi.json
    modified:   pkg/generated/openapi/zz_generated.openapi.go
    modified:   staging/src/k8s.io/api/apps/v1/generated.proto
    modified:   staging/src/k8s.io/api/apps/v1/types_swagger_doc_generated.go

查看 api/openapi-spec/swagger.json 的内容,以确保拼写错误已经被修正。 例如,你可以运行 git diff -a api/openapi-spec/swagger.json 命令。 这很重要,因为 swagger.json 是文档生成过程中第二阶段的输入。

运行 git addgit commit 命令来提交你的更改。现在你有两个提交(commits): 一种包含编辑的 types.go 文件,另一种包含生成的 OpenAPI 规范和相关文件。 将这两个提交分开独立。也就是说,不要 squash 你的提交。

将你的更改作为 PR 提交到 kubernetes/kubernetes 代码仓库的 master 分支。 关注你的 PR,并根据需要回复 reviewer 的评论。继续关注你的 PR,直到 PR 被合并为止。

PR 57758 是修复 Kubernetes 源代码中的拼写错误的拉取请求的示例。

将你的提交 Cherrypick 到发布分支

在上一节中,你在 master 分支中编辑了一个文件,然后运行了脚本用来生成 OpenAPI 规范和相关文件。 然后用 PR 将你的更改提交到 kubernetes/kubernetes 代码仓库的 master 分支中。 现在,需要将你的更改反向移植到已经发布的分支。 例如,假设 master 分支被用来开发 Kubernetes 1.30 版, 并且你想将更改反向移植到 release-1.29 分支。

回想一下,你的 PR 有两个提交:一个用于编辑 types.go,一个用于由脚本生成的文件。 下一步是将你的第一次提交 cherrypick 到 release-1.29 分支。 这样做的原因是仅 cherrypick 编辑了 types.go 的提交, 而不是具有脚本运行结果的提交。 有关说明,请参见提出 Cherry Pick

当你发起 PR 将你的一个提交 cherry pick 到 release-1.29 分支中时, 下一步是在本地环境的 release-1.29 分支中运行如下脚本。

./hack/update-codegen.sh
./hack/update-openapi-spec.sh

现在将提交添加到你的 Cherry-Pick PR 中,该 PR 中包含最新生成的 OpenAPI 规范和相关文件。 关注你的 PR,直到其合并到 release-1.29 分支中为止。

此时,master 分支和 release-1.29 分支都具有更新的 types.go 文件和一组生成的文件, 这些文件反映了对 types.go 所做的更改。 请注意,生成的 OpenAPI 规范和其他 release-1.29 分支中生成的文件不一定与 master 分支中生成的文件相同。 release-1.29 分支中生成的文件仅包含来自 Kubernetes 1.29 的 API 元素。 master 分支中生成的文件可能包含不在 1.29 中但正在为 1.30 开发的 API 元素。

生成已发布的参考文档

上一节显示了如何编辑源文件然后生成多个文件,包括在 kubernetes/kubernetes 代码仓库中的 api/openapi-spec/swagger.jsonswagger.json 文件是 OpenAPI 定义文件,可用于生成 API 参考文档。

现在,你可以按照 生成 Kubernetes API 的参考文档 指南来生成 已发布的 Kubernetes API 参考文档

接下来

7.7.3 - 为 Kubernetes API 生成参考文档

本页面显示了如何更新 Kubernetes API 参考文档。

Kubernetes API 参考文档是从 Kubernetes OpenAPI 规范构建的, 且使用 kubernetes-sigs/reference-docs 生成代码。

如果你在生成的文档中发现错误,则需要在上游修复

如果你只需要从 OpenAPI 规范中重新生成参考文档,请继续阅读此页。

准备开始

需求

  • 你的 PATH 环境变量必须包含所需要的构建工具,例如 Go 程序和 python

  • 你需要知道如何为一个 GitHub 仓库创建拉取请求(PR)。 这牵涉到创建仓库的派生(fork)副本。 有关信息可进一步查看基于本地副本开展工作

配置本地仓库

创建本地工作区并设置你的 GOPATH

mkdir -p $HOME/<workspace>
export GOPATH=$HOME/<workspace>

获取以下仓库的本地克隆:

go get -u github.com/kubernetes-sigs/reference-docs

go get -u github.com/go-openapi/loads
go get -u github.com/go-openapi/spec

如果你还没有下载过 kubernetes/website 仓库,现在下载:

git clone https://github.com/<your-username>/website $GOPATH/src/github.com/<your-username>/website

克隆 kubernetes/kubernetes 仓库作为 k8s.io/kubernetes:

git clone https://github.com/kubernetes/kubernetes $GOPATH/src/k8s.io/kubernetes
  • kubernetes/kubernetes 仓库克隆后的根目录为 $GOPATH/src/k8s.io/kubernetes。 后续步骤将此目录称为 <k8s-base>
  • kubernetes/website 仓库克隆后的根目录为 $GOPATH/src/github.com/<your username>/website。后续步骤将此目录称为 <web-base>
  • kubernetes-sigs/reference-docs 仓库克隆后的基本目录为 $GOPATH/src/github.com/kubernetes-sigs/reference-docs。 后续步骤将此目录称为 <rdocs-base>

生成 API 参考文档

本节说明如何生成已发布的 Kubernetes API 参考文档

设置构建变量

进入 <rdocs-base> 目录,然后打开 Makefile 文件进行编辑:

  • 设置 K8S_ROOT<k8s-base>
  • 设置 K8S_WEBROOT<web-base>
  • 设置 K8S_RELEASE 为要构建的文档的版本。 例如,如果你想为 Kubernetes 1.17.0 构建文档,请将 K8S_RELEASE 设置为 1.17.0。

例如:

export K8S_WEBROOT=${GOPATH}/src/github.com/<your-username>/website
export K8S_ROOT=${GOPATH}/src/k8s.io/kubernetes
export K8S_RELEASE=1.17.0

创建版本目录并复制 OpenAPI 规范

构建目标 updateapispec 负责创建版本化的构建目录。 目录创建了之后,从 <k8s-base> 仓库取回 OpenAPI 规范文件。 这些步骤确保配置文件的版本和 Kubernetes OpenAPI 规范的版本与发行版本匹配。 版本化目录的名称形式为 v<major>_<minor>

<rdocs-base> 目录中,运行以下命令来构建:

cd <rdocs-base>
make updateapispec

构建 API 参考文档

构建目标 copyapi 会生成 API 参考文档并将所生成文件复制到 <web-base 中的目录下。 在 <rdocs-base> 目录中运行以下命令:

cd <rdocs-base>
make copyapi

验证是否已生成这两个文件:

[ -e "<rdocs-base>/gen-apidocs/build/index.html" ] && echo "index.html built" || echo "no index.html"
[ -e "<rdocs-base>/gen-apidocs/build/navData.js" ] && echo "navData.js built" || echo "no navData.js"

进入本地 <web-base> 目录,检查哪些文件被更改:

cd <web-base>
git status

输出类似于:

static/docs/reference/generated/kubernetes-api/v1.30/css/bootstrap.min.css
static/docs/reference/generated/kubernetes-api/v1.30/css/font-awesome.min.css
static/docs/reference/generated/kubernetes-api/v1.30/css/stylesheet.css
static/docs/reference/generated/kubernetes-api/v1.30/fonts/FontAwesome.otf
static/docs/reference/generated/kubernetes-api/v1.30/fonts/fontawesome-webfont.eot
static/docs/reference/generated/kubernetes-api/v1.30/fonts/fontawesome-webfont.svg
static/docs/reference/generated/kubernetes-api/v1.30/fonts/fontawesome-webfont.ttf
static/docs/reference/generated/kubernetes-api/v1.30/fonts/fontawesome-webfont.woff
static/docs/reference/generated/kubernetes-api/v1.30/fonts/fontawesome-webfont.woff2
static/docs/reference/generated/kubernetes-api/v1.30/index.html
static/docs/reference/generated/kubernetes-api/v1.30/js/jquery.scrollTo.min.js
static/docs/reference/generated/kubernetes-api/v1.30/js/navData.js
static/docs/reference/generated/kubernetes-api/v1.30/js/scroll.js

更新 API 参考索引页面

在为新发行版本生成参考文档时,需要更新下面的文件,使之包含新的版本号: <web-base>/content/en/docs/reference/kubernetes-api/api-index.md

  • 打开并编辑 <web-base>/content/en/docs/reference/kubernetes-api/api-index.md, API 参考的版本号。例如:

    title: v1.17
    [Kubernetes API v1.17](/docs/reference/generated/kubernetes-api/v1.17/)
    
  • 打开编辑 <web-base>/content/en/docs/reference/_index.md,添加指向最新 API 参考的链接,删除最老的 API 版本。 通常保留最近的五个版本的 API 参考的链接。

在本地测试 API 参考

发布 API 参考的本地版本。 检查本地预览

cd <web-base>
git submodule update --init --recursive --depth 1 # 如果尚未完成
make container-serve

提交更改

<web-base> 中运行 git addgit commit 来提交更改。

基于你所生成的更改创建 PR, 提交到 kubernetes/website 仓库。 监视你提交的 PR,并根据需要回复 reviewer 的评论。继续监视你的 PR,直到合并为止。

接下来

7.7.4 - 为 kubectl 命令集生成参考文档

本页面描述了如何生成 kubectl 命令参考。

准备开始

需求

  • 你的 PATH 环境变量必须包含所需要的构建工具,例如 Go 程序和 python

  • 你需要知道如何为一个 GitHub 仓库创建拉取请求(PR)。 这牵涉到创建仓库的派生(fork)副本。 有关信息可进一步查看基于本地副本开展工作

配置本地仓库

创建本地工作区并设置你的 GOPATH

mkdir -p $HOME/<workspace>
export GOPATH=$HOME/<workspace>

获取以下仓库的本地克隆:

go get -u github.com/spf13/pflag
go get -u github.com/spf13/cobra
go get -u gopkg.in/yaml.v2
go get -u kubernetes-incubator/reference-docs

如果你还没有获取过 kubernetes/website 仓库,现在获取之:

git clone https://github.com/<your-username>/website $GOPATH/src/github.com/<your-username>/website

克隆 kubernetes/kubernetes 仓库作为 k8s.io/kubernetes:

git clone https://github.com/kubernetes/kubernetes $GOPATH/src/k8s.io/kubernetes

$GOPATH/src/k8s.io/kubernetes/vendor/github.com 中移除 spf13 软件包:

rm -rf $GOPATH/src/k8s.io/kubernetes/vendor/github.com/spf13

kubernetes/kubernetes 仓库提供对 kubectl 和 kustomize 源代码的访问。

  • 确定 kubernetes/kubernetes 仓库的本地主目录。 例如,如果按照前面的步骤来获取该仓库,则主目录是 $GOPATH/src/k8s.io/kubernetes.。 下文将该目录称为 <k8s-base>
  • 确定 kubernetes/website 仓库的本地主目录。 例如,如果按照前面的步骤来获取该仓库,则主目录是 $GOPATH/src/github.com/<your-username>/website。 下文将该目录称为 <web-base>
  • 确定 kubernetes-sigs/reference-docs 仓库的本地主目录。例如,如果按照前面的步骤来获取该仓库,则主目录是 $GOPATH/src/github.com/kubernetes-sigs/reference-docs。 下文将该目录称为 <rdocs-base>

在本地的 k8s.io/kubernetes 仓库中,检出感兴趣的分支并确保它是最新的。例如, 如果你想要生成 Kubernetes 1.29.0 的文档,可以使用以下命令:

cd <k8s-base>
git checkout v1.29.0
git pull https://github.com/kubernetes/kubernetes 1.29.0

如果不需要编辑 kubectl 源码,请按照说明配置构建变量

编辑 kubectl 源码

kubectl 命令的参考文档是基于 kubectl 源码自动生成的。如果想要修改参考文档,可以从修改 kubectl 源码中的一个或多个注释开始。在本地 kubernetes/kubernetes 仓库中进行修改,然后向 github.com/kubernetes/kubernetes 的 master 分支提交 PR。

PR 56673 是一个对 kubectl 源码中的笔误进行修复的 PR 示例。

跟踪你的 PR,并回应评审人的评论。继续跟踪你的 PR,直到它合入到 kubernetes/kubernetes 仓库的目标分支中。

以 cherry-pick 方式将你的修改合入已发布分支

你的修改已合入 master 分支中,该分支用于开发下一个 Kubernetes 版本。 如果你希望修改部分出现在已发布的 Kubernetes 版本文档中,则需要提议将它们以 cherry-pick 方式合入已发布分支。

例如,假设 master 分支正用于开发 Kubernetes 1.30 版本, 而你希望将修改合入到 release-1.29 版本分支。 相关的操作指南,请参见 提议一个 cherry-pick

跟踪你的 cherry-pick PR,直到它合入到已发布分支中。

设置构建变量

进入 <rdocs-base> 目录, 打开 Makefile 进行编辑:

  • 设置 K8S_ROOT<k8s-base>
  • 设置 K8S_WEBROOT<web-base>
  • 设置 K8S_RELEASE 为要构建文档的版本。 例如,如果你想为 Kubernetes 1.29 构建文档, 请将 K8S_RELEASE 设置为 1.29。

例如:

export K8S_WEBROOT=$(GOPATH)/src/github.com/<your-username>/website
export K8S_ROOT=$(GOPATH)/src/k8s.io/kubernetes
export K8S_RELEASE=1.29

创建版本目录

构建目标 createversiondirs 会生成一个版本目录并将 kubectl 参考配置文件复制到该目录中。 版本目录的名字模式为 v<major>_<minor>

<rdocs-base> 目录下,执行下面的命令:

cd <rdocs-base>
make createversiondirs

从 kubernetes/kubernetes 检出一个分支

在本地 <k8s-base> 仓库中,检出你想要生成文档的、包含 Kubernetes 版本的分支。 例如,如果希望为 Kubernetes 1.29.0 版本生成文档, 请检出 v1.29 标记。 确保本地分支是最新的。

cd <k8s-base>
git checkout v1.29.0
git pull https://github.com/kubernetes/kubernetes v1.29.0

运行文档生成代码

在本地的 <rdocs-base> 目录下,运行 copycli 构建目标。此命令以 root 账号运行:

cd <rdocs-base>
make copycli

copycli 命令将清理暂存目录,生成 kubectl 命令文件,并将整理后的 kubectl 参考 HTML 页面和文件复制到 <web-base>

找到生成的文件

验证是否已生成以下两个文件:

[ -e "<rdocs-base>/gen-kubectldocs/generators/build/index.html" ] && echo "index.html built" || echo "no index.html"
[ -e "<rdocs-base>/gen-kubectldocs/generators/build/navData.js" ] && echo "navData.js built" || echo "no navData.js"

找到复制的文件

确认所有生成的文件都已复制到你的 <web-base>

cd <web-base>
git status

输出应包括修改后的文件:

static/docs/reference/generated/kubectl/kubectl-commands.html
static/docs/reference/generated/kubectl/navData.js

此外,输出可能还包含:

static/docs/reference/generated/kubectl/scroll.js
static/docs/reference/generated/kubectl/stylesheet.css
static/docs/reference/generated/kubectl/tabvisibility.js
static/docs/reference/generated/kubectl/node_modules/bootstrap/dist/css/bootstrap.min.css
static/docs/reference/generated/kubectl/node_modules/highlight.js/styles/default.css
static/docs/reference/generated/kubectl/node_modules/jquery.scrollto/jquery.scrollTo.min.js
static/docs/reference/generated/kubectl/node_modules/jquery/dist/jquery.min.js
static/docs/reference/generated/kubectl/node_modules/font-awesome/css/font-awesome.min.css

在本地测试文档

在本地 <web-base> 中构建 Kubernetes 文档。

cd <web-base>
git submodule update --init --recursive --depth 1 # 如果还没完成
make container-serve

查看本地预览

在 kubernetes/website 中添加和提交更改

运行 git addgit commit 提交修改文件。

创建 PR

kubernetes/website 仓库创建 PR。跟踪你的 PR,并根据需要回应评审人的评论。 继续跟踪你的 PR,直到它被合入。

在 PR 合入的几分钟后,你更新的参考主题将出现在已发布文档中。

接下来

7.7.5 - 为指标生成参考文档

本页演示如何生成指标参考文档。

准备开始

需求

  • 你的 PATH 环境变量必须包含所需要的构建工具,例如 Go 程序和 python

  • 你需要知道如何为一个 GitHub 仓库创建拉取请求(PR)。 这牵涉到创建仓库的派生(fork)副本。 有关信息可进一步查看基于本地副本开展工作

克隆 Kubernetes 仓库

指标的生成发生在 Kubernetes 仓库中。 要克隆此仓库,请将目录更改为你要克隆到的目标位置。

然后,执行以下命令:

git clone https://www.github.com/kubernetes/kubernetes 

这将在当前工作目录中创建一个 kubernetes 文件夹。

生成指标

在克隆的 Kubernetes 仓库中,找到 test/instrumentation/documentation 目录。 指标文档将在此目录中生成。

每次发布都会添加新的指标。你在运行指标文档生成脚本之后, 将指标文档复制到 Kubernetes 网站并发布更新的指标文档。

要生成最新的指标,请确保你位于已克隆的 Kubernetes 仓库的根目录中。 然后,执行以下命令:

./test/instrumentation/update-documentation.sh

要检查变更,执行以下命令:

git status

输出类似于:

./test/instrumentation/documentation/documentation.md
./test/instrumentation/documentation/documentation-list.yaml

将生成的指标文档文件复制到 Kubernetes 网站仓库

  1. 设置 Kubernetes 网站根环境变量

    执行以下命令设置网站根目录:

    export WEBSITE_ROOT=<path to website root>
    
  1. 将生成的指标文件复制到 Kubernetes 网站仓库。

    cp ./test/instrumentation/documentation/documentation.md "${WEBSITE_ROOT}/content/en/docs/reference/instrumentation/metrics.md"
    

创建 PR

要创建 PR,请按照提 PR 中的说明操作。

接下来

7.7.6 - 为 Kubernetes 组件和工具生成参考文档

本页面描述如何构造 Kubernetes 组件和工具的参考文档。

准备开始

阅读参考文档快速入门指南中的准备工作节。

按照参考文档快速入门 指引,生成 Kubernetes 组件和工具的参考文档。

接下来

7.7.7 -

需求

  • 你的 PATH 环境变量必须包含所需要的构建工具,例如 Go 程序和 python

  • 你需要知道如何为一个 GitHub 仓库创建拉取请求(PR)。 这牵涉到创建仓库的派生(fork)副本。 有关信息可进一步查看基于本地副本开展工作

7.8 - 文档样式概述

本节的主题是提供有关写作风格、内容格式和组织以及如何使用 特定于 Kubernetes 文档的 Hugo 定制代码的指导。

7.8.1 - 文档内容指南

本页包含 Kubernetes 文档的一些指南。

如果你不清楚哪些事情是可以做的,请加入到 Kubernetes Slack#sig-docs 频道提问! 你可以在 https://slack.k8s.io 注册到 Kubernetes Slack。

关于为 Kubernetes 文档创建新内容的更多信息, 可参考样式指南

概述

Kubernetes 网站(包括其文档)源代码位于 kubernetes/website 仓库中。

kubernetes/website/content/<语言代码>/docs 目录下, 绝大多数 Kubernetes 文档都是特定于 Kubernetes 项目的。

可以发布的内容

只有当以下条件满足时,Kubernetes 文档才允许第三方项目的内容:

  • 内容所描述的软件在 Kubernetes 项目内
  • 内容所描述的软件不在 Kubernetes 项目内,却是让 Kubernetes 正常工作所必需的
  • 内容是被 kubernetes.io 域名收编的,或者是其他位置的标准典型内容

第三方内容

Kubernetes 文档包含 Kubernetes 项目下的多个项目的应用示例。 这里的 Kubernetes 项目指的是 KubernetesKubernetes SIGs GitHub 组织下的那些项目。

链接到 Kubernetes 项目中活跃的内容是一直允许的。

Kubernetes 需要某些第三方内容才能正常工作。例如容器运行时(containerd、CRI-O、Docker)、 联网策略(CNI 插件)、 Ingress 控制器 以及日志等。

只有对应的第三方开源软件(OSS)是运行 Kubernetes 所必需的, 才可以在文档中包含指向这些 Kubernetes 项目之外的软件的链接。

双重来源的内容

只要有可能,Kubernetes 文档就应该指向标准典型的信息源而不是直接托管双重来源的内容。

双重来源的内容需要双倍(甚至更多)的投入才能维护,而且通常很快就会变得停滞不前。

更多信息

如果你对允许出现的内容有疑问,请加入到 Kubernetes Slack#sig-docs 频道提问!

接下来

7.8.2 - 文档样式指南

本页讨论 Kubernetes 文档的样式指南。 这些仅仅是指南而不是规则。 你可以自行决定,且欢迎使用 PR 来为此文档提供修改意见。

关于为 Kubernetes 文档贡献新内容的更多信息, 可以参考文档内容指南

样式指南的变更是 SIG Docs 团队集体决定。 如要提议更改或新增条目,请先将其添加到下一次 SIG Docs 例会的议程表上,并按时参加会议讨论。

语言

Kubernetes 文档已经被翻译为多个语种 (参见 本地化 README)。

本地化 Kubernetes 文档描述了如何为一种新的语言提供本地化文档。

英语文档使用美国英语的拼写和语法。

文档格式标准

对 API 对象使用大写驼峰式命名法

当你与指定的 API 对象进行交互时, 使用大写驼峰式命名法, 也被称为帕斯卡拼写法(PascalCase)。 你可以在 API 参考中看到不同的大小写形式,例如 "configMap"。 在编写通用文档时,最好使用大写驼峰形式,将之称作 "ConfigMap"。

通常在讨论 API 对象时,使用 句子式大写

下面的例子关注的是大小写问题。关于如何格式化 API 对象名称的更多信息, 可参考相关的代码风格指南。

使用 Pascal 风格大小写来给出 API 对象的约定
可以不可以
该 HorizontalPodAutoscaler 负责...该 Horizontal pod autoscaler 负责...
每个 PodList 是一个 Pod 组成的列表。每个 Pod List 是一个由 Pod 组成的列表。
该 Volume 对象包含一个 hostPath 字段。此卷对象包含一个 hostPath 字段。
每个 ConfigMap 对象都是某个名字空间的一部分。每个 configMap 对象是某个名字空间的一部分。
要管理机密数据,可以考虑使用 Secret API。要管理机密数据,可以考虑使用秘密 API。

在占位符中使用尖括号

用尖括号表示占位符,让读者知道占位符表示的是什么。例如:

显示有关 Pod 的信息:

kubectl describe pod <Pod 名称> -n <名字空间>

如果名字空间被忽略,默认为 default,你可以省略 '-n' 参数。

用粗体字表现用户界面元素

以粗体表示用户界面元素
可以不可以
点击 Fork点击 "Fork"。
选择 Other选择 "Other"。

定义或引入新术语时使用斜体

新术语约定
可以不可以
每个 集群 是一组节点 ...每个“集群”是一组节点 ...
这些组件构成了 控制面这些组件构成了 控制面

使用代码样式表现文件名、目录和路径

文件名、目录和路径约定
可以不可以
打开 envars.yaml 文件打开 envars.yaml 文件
进入到 /docs/tutorials 目录进入到 /docs/tutorials 目录
打开 /_data/concepts.yaml 文件打开 /_data/concepts.yaml 文件

在引号内使用国际标准标点

标点符号约定
可以不可以
事件记录中都包含对应的“stage”。事件记录中都包含对应的“stage。”
此副本称作一个“fork”。此副本称作一个“fork。”

行间代码格式

为行间代码、命令使用代码样式

对于 HTML 文档中的行间代码,使用 <code> 标记。在 Markdown 文档中,使用反引号(`)。 然而,StatefulSet 或 ConfigMap 这些 API 类别是直接书写的(不用反引号);这样允许使用表示所有格的撇号。

行间代码、命令和 API 对象约定
可以不可以
kubectl run 命令会创建一个 Pod。"kubectl run" 命令会创建一个 Pod。
每个节点上的 kubelet 都会获得一个 Lease…每个节点上的 kubelet 都会获得一个 Lease
一个 PersistentVolume 代表持久存储…一个 PersistentVolume 代表持久存储…
CustomResourceDefinition 的 .spec.group 字段…CustomResourceDefinition.spec.group 字段…
在声明式管理中,使用 kubectl apply在声明式管理中,使用 "kubectl apply"。
用三个反引号来(```)标示代码示例用其他语法来标示代码示例。
使用单个反引号来标示行间代码。例如:var example = true使用两个星号(**)或者一个下划线(_)来标示行间代码。例如:var example = true
在多行代码块之前和之后使用三个反引号标示隔离的代码块。使用多行代码块来创建示意图、流程图或者其他表示。
使用符合上下文的有意义的变量名。使用诸如 'foo'、'bar' 和 'baz' 这类无意义且无语境的变量名。
删除代码中行尾空白。在代码中包含行尾空白,因为屏幕抓取工具通常也会抓取空白字符。

为对象字段名和名字空间使用代码风格

对象字段名约定
可以不可以
在配置文件中设置 replicas 字段的值。在配置文件中设置 "replicas" 字段的值。
exec 字段的值是一个 ExecAction 对象。"exec" 字段的值是一个 ExecAction 对象。
kube-system 名字空间中以 DaemonSet 形式运行此进程。在 kube-system 名字空间中以 DaemonSet 形式运行此进程。

用代码样式书写 Kubernetes 命令工具和组件名

Kubernetes 命令工具和组件名
可以不可以
kubelet 维持节点稳定性。kubelet 负责维护节点稳定性。
kubectl 处理 API 服务器的定位和身份认证。kubectl 处理 API 服务器的定位和身份认证。
使用该证书运行进程 kube-apiserver --client-ca-file=FILENAME使用证书运行进程 kube-apiserver --client-ca-file=FILENAME。

用工具或组件名称开始一句话

工具或组件名称使用约定
可以不可以
The kubeadm tool bootstraps and provisions machines in a cluster.kubeadm tool bootstraps and provisions machines in a cluster.
The kube-scheduler is the default scheduler for Kubernetes.kube-scheduler is the default scheduler for Kubernetes.

尽量使用通用描述而不是组件名称

组件名称与通用描述
可以不可以
Kubernetes API 服务器提供 OpenAPI 规范。apiserver 提供 OpenAPI 规范。
聚合 API 是下级 API 服务器。聚合 API 是下级 APIServer。

使用普通样式表达字符串和整数字段值

对于字符串或整数,使用正常样式,不要带引号。

字符串和整数字段值约定
可以不可以
imagePullPolicy 设置为 Always。imagePullPolicy 设置为 "Always"。
image 设置为 nginx:1.16。image 设置为 nginx:1.16
replicas 字段值设置为 2。replicas 字段值设置为 2

然而,在读者可能会将某些值与 API 类别混淆时,请考虑为这些值添加引号。

引用 Kubernetes API 资源

本节讨论我们如何在文档中引用 API 资源。

有关 “资源” 的阐述

Kubernetes 使用单词 resource 一词来指代 API 资源。 例如,URL 路径 /apis/apps/v1/namespaces/default/deployments/my-app 表示 "default" 名字空间中名为 "my-app" 的 Deployment。 在 HTTP 的术语中,名字空间是一个资源, 就像所有 Web URL 都标识一个资源。

Kubernetes 文档在讨论 CPU 和内存请求以及限制也使用“资源(resource)”一词。 将 API 资源称为 "API 资源" 往往是一个好的做法;这有助于避免与 CPU 和内存资源或其他类别的资源混淆。

如果你使用资源名称的小写复数形式,例如 deploymentsconfigmaps, 请提供额外的书面上下文来帮助读者理解你的用意。 如果你使用术语时所处的上下文中使用驼峰编码(UpperCamelCase)的名称也可行,且术语存在歧义的风险, 应该考虑使用 UpperCamelCase 形式的 API 类别。

何时使用 Kubernetes API 术语

不同 Kubernetes API 术语的说明如下:

  • API 类别 :API URL 中使用的名称(如 podsnamespaces)。 API 类别有时也称为 资源类型
  • API 资源 :API 类别的单个实例(如 podsecret
  • 对象 :作为 “意向记录” 的资源。对象是集群特定部分的期望状态, 该状态由 Kubernetes 控制平面负责维护。 Kubernetes API 中的所有对象也都是资源。

为了清晰起见,在 Kubernetes 文档中引用 API 资源时可以使用 "资源" 或 "对象"。 例如:写成 "Secret 对象" 而不是 "Secret"。 如果仅大写就能明确含义,那么无需添加额外的单词。

当修改有助于避免误解时,那就考虑修改表述。 一个常见的情况是当你想要某个句子以 "Secret" 这种 API 类别开头时; 因为英语和其他几种语言会对句首的第一个字母大写,所以读者无法确定你说的是 API 类别还是一般概念。 此时重新构词有助于让句子更清晰。

API 资源名称

始终使用大写驼峰式命名法 (也称为 PascalCase)来表达 API 资源名称。不要使用代码格式书写 API 类别。

不要将 API 对象的名称切分成多个单词。 例如请使用 PodTemplateList 而非 Pod Template List。

有关 PascalCase 和代码格式的更多信息, 请查看对 API 对象使用大写驼峰式命名法针对内嵌代码、命令与 API 对象使用代码样式

有关 Kubernetes API 术语的更多信息, 请查看 Kubernetes API 术语的相关指南。

代码段格式

不要包含命令行提示符

命令行提示符约定
可以不可以
kubectl get pods$ kubectl get pods

将命令和输出分开

例如:

验证 Pod 已经在你所选的节点上运行:

kubectl get pods --output=wide

输出类似于:

NAME     READY     STATUS    RESTARTS   AGE    IP           NODE
nginx    1/1       Running   0          13s    10.200.0.4   worker0

为 Kubernetes 示例给出版本

代码示例或者配置示例如果包含版本信息,应该与对应的文字描述一致。

如果所给的信息是特定于具体版本的,需要在 任务模板教程模板prerequisites 小节定义 Kubernetes 版本。 页面保存之后,prerequisites 小节会显示为开始之前

如果要为任务或教程页面指定 Kubernetes 版本,可以在文件的前言部分包含 min-kubernetes-server-version 信息。

如果示例 YAML 是一个独立文件,找到并审查包含该文件的主题页面。 确认使用该独立 YAML 文件的主题都定义了合适的版本信息。 如果独立的 YAML 文件没有在任何主题中引用,可以考虑删除该文件, 而不是继续更新它。

例如,如果你在编写一个教程,与 Kubernetes 1.8 版本相关。那么你的 Markdown 文件的文件头应该开始起来像这样:

---
title: <教程标题>
min-kubernetes-server-version: v1.8
---

在代码和配置示例中,不要包含其他版本的注释信息。 尤其要小心不要在示例中包含不正确的注释信息,例如:

apiVersion: v1 # 早期版本使用...
kind: Pod
...

Kubernetes.io 术语列表

以下特定于 Kubernetes 的术语和词汇在使用时要保持一致性。

Kubernetes.io 词汇表
术语用法
KubernetesKubernetes 的首字母要保持大写。
DockerDocker 的首字母要保持大写。
SIG DocsSIG Docs 是正确拼写形式,不要用 SIG-DOCS 或其他变体。
On-premisesOn-premises 或 On-prem 而不是 On-premise 或其他变体。

短代码(Shortcodes)

Hugo 短代码(Shortcodes) 有助于创建比较漂亮的展示效果。我们的文档支持三个不同的这类短代码。 注意 {{< note >}}小心 {{< caution >}}警告 {{< warning >}}

  1. 将要突出显示的文字用短代码的开始和结束形式包围。

  2. 使用下面的语法来应用某种样式:

    {{< note >}}
    不需要前缀;短代码会自动添加前缀(注意:、小心:等)
    {{< /note >}}
    

    输出的样子是:

注释(Note)

使用短代码 {{< note >}} 来突出显示某种提示或者有助于读者的信息。

例如:

{{< note >}}
在这类短代码中仍然 _可以_ 使用 Markdown 语法。
{{< /note >}}

输出为:

你可以在列表中使用 {{< note >}}

1. 在列表中使用 note 短代码

1. 带嵌套 note 的第二个条目

   {{< note >}}
   警告、小心和注意短代码可以嵌套在列表中,但是要缩进四个空格。
   参见[常见短代码问题](#common-shortcode-issues)。
   {{< /note >}}

1. 列表中第三个条目

1. 列表中第四个条目

其输出为:

  1. 在列表中使用 note 短代码

  2. 带嵌套 note 的第二个条目

  1. 列表中第三个条目

  2. 列表中第四个条目

小心(Caution)

使用 {{< caution >}} 短代码来引起读者对某段信息的重视,以避免遇到问题。

例如:

{{< caution >}}
此短代码样式仅对标记之上的一行起作用。
{{< /caution >}}

其输出为:

警告(Warning)

使用 {{< warning >}} 来表明危险或者必须要重视的一则信息。

例如:

{{< warning >}}
注意事项
{{< /warning >}}

其输出为:

常见的短代码问题

编号列表

短代码会打乱编号列表的编号,除非你在信息和标志之前都缩进四个空格。

例如:

1. 预热到 350˚F
1. 准备好面糊,倒入烘烤盘
    {{< note >}}给盘子抹上油可以达到最佳效果。{{< /note >}}
1. 烘烤 20 到 25 分钟,或者直到满意为止。

其输出结果为:

  1. 预热到 350˚F
  2. 准备好面糊,倒入烘烤盘
  3. 烘烤 20 到 25 分钟,或者直到满意为止。

Include 语句

如果短代码出现在 include 语境中,会导致网站无法构建。 你必须将他们插入到上级文档中,分别将开始标记和结束标记插入到 include 语句之前和之后。 例如:

{{< note >}}
{{< include "task-tutorial-prereqs.md" >}}
{{< /note >}}

Markdown 元素

换行

使用单一换行符来隔离块级内容,例如标题、列表、图片、代码块以及其他元素。 这里的例外是二级标题,必须有两个换行符。 二级标题紧随一级标题(或标题),中间没有段落或文字。

两行的留白有助于在代码编辑器中查看整个内容的结构组织。

适当时在 Markdown 文档中手动换行。由于 git 工具和 GitHub 网站是逐行生成文件差异的,手动换行可以帮助审阅者轻松找到 PR 中所做的更改并提供反馈。 它还可以帮助下游本地化团队,使其按行跟踪上游更改。例如,换行可以发生在句子或标点符号的末尾。 一个例外是 Markdown 链接或短代码应位于一行中。

大标题和小标题

访问文档的读者可能会使用屏幕抓取程序或者其他辅助技术。 屏幕抓取器是一种线性输出设备, 它们每次输出页面上的一个条目。 如果页面上内容过多,你可以使用标题来为页面组织结构。 页面的良好结构对所有读者都有帮助,使得他们更容易浏览或者过滤感兴趣的内容。

标题约定
可以不可以
更新页面或博客在前言部分中的标题。使用一级标题。因为 Hugo 会自动将页面前言部分的标题转化为一级标题。
使用编号的标题以便内容组织有一个更有意义的结构。使用四级到六级标题,除非非常有必要这样。如果你要编写的内容有非常多细节,可以尝试拆分成多个不同页面。
在非博客内容页面中使用井号(#使用下划线 ---=== 来标记一级标题。
页面正文中的小标题采用正常语句的大小写。例如:Extend kubectl with plugins页面正文中的小标题采用首字母大写的大标题式样。例如:Extend Kubectl With Plugins
头部的页面标题采用大标题的式样。例如:title: Kubernetes API Server Bypass Risks头部的页面标题采用正常语句的大小写。例如不要使用 title: Kubernetes API server bypass risks

段落

段落约定
可以不可以
尝试不要让段落超出 6 句话。用空格来缩进第一段。例如,段落前面的三个空格⋅⋅⋅会将段落缩进。
使用三个连字符(---)来创建水平线。使用水平线来分隔段落内容。例如,在故事中切换场景或者在上下文中切换主题。使用水平线来装饰页面。
链接约定
可以不可以
插入超级链接时给出它们所链接到的目标内容的上下文。例如:你的机器上某些端口处于开放状态。参见检查所需端口了解更详细信息。使用“点击这里”等模糊的词语。例如:你的机器上某些端口处于打开状态。参见这里了解详细信息。
编写 Markdown 风格的链接:[链接文本](URL)。例如:[Hugo 短代码](/zh-cn/docs/contribute/style/hugo-shortcodes/#table-captions),输出是 Hugo 短代码编写 HTML 风格的超级链接:<a href="/media/examples/link-element-example.css" target="_blank">访问我们的教程!</a>,或者创建会打开新 Tab 页签或新窗口的链接。例如:[网站示例](https://example.com){target="_blank"}

列表

将一组相互关联的内容组织到一个列表中,以便表达这些条目彼此之间有先后顺序或者某种相互关联关系。 当屏幕抓取器遇到列表时,无论该列表是否有序,它会告知用户存在一组枚举的条目。 用户可以使用箭头键来上下移动,浏览列表中条目。 网站导航链接也可以标记成列表条目,因为说到底他们也是一组相互关联的链接而已。

  • 如果列表中一个或者多个条目是完整的句子,则在每个条目末尾添加句号。 出于一致性考虑,一般要么所有条目要么没有条目是完整句子。

  • 在编号列表中,使用数字 1(1.)。

  • 对非排序列表,使用加号(+)、星号(*)、或者减号(-)。

  • 在每个列表之后留一个空行。

  • 对于嵌套的列表,相对缩进四个空格(例如,⋅⋅⋅⋅)。

  • 列表条目可能包含多个段落。每个后续段落都要缩进或者四个空格或者一个制表符。

表格

数据表格的语义用途是呈现表格化的数据。 用户可以快速浏览表格,但屏幕抓取器需要逐行地处理数据。 表格标题可以用来给数据表提供一个描述性的标题。 辅助技术使用 HTML 表格标题元素来在页面结构中辨识表格内容。

内容最佳实践

本节包含一些建议的最佳实践,用来开发清晰、明确一致的文档内容。

使用现在时态

使用现在时态
可以不可以
此命令启动代理。此命令将启动一个代理。

例外:如果需要使用过去时或将来时来表达正确含义时,是可以使用的。

使用主动语态

使用主动语态
可以不可以
你可以使用浏览器来浏览 API。API 可以被使用浏览器来浏览。
YAML 文件给出副本个数。副本个数是在 YAML 文件中给出的。

例外:如果主动语态会导致句子很难构造时,可以使用被动语态。

使用简单直接的语言

使用简单直接的语言。避免不必要的短语,例如说“请”。

使用简单直接语言
可以不可以
要创建 ReplicaSet,...如果你想要创建 ReplicaSet,...
参看配置文件。请自行查看配置文件。
查看 Pod。使用下面的命令,我们将会看到 Pod。

将读者称为“你”

将读者称为“你”
可以不可以
你可以通过 ... 创建一个 Deployment。通过...我们将创建一个 Deployment。
在前面的输出中,你可以看到...在前面的输出中,我们可以看到...

避免拉丁短语

尽可能使用英语而不是拉丁语缩写。

避免拉丁语短语
可以不可以
例如,...e.g., ...
也就是说,...i.e., ...

例外:使用 etc. 表示等等。

应避免的模式

避免使用“我们”

在句子中使用“我们”会让人感到困惑,因为读者可能不知道这里的“我们”指的是谁。

要避免的模式
可以不可以
版本 1.4 包含了 ...在 1.4 版本中,我们添加了 ...
Kubernetes 为 ... 提供了一项新功能。我们提供了一项新功能...
本页面教你如何使用 Pod。在本页中,我们将会学到如何使用 Pod。

避免使用俚语或行话

对某些读者而言,英语是其外语。 避免使用一些俚语或行话有助于他们更方便的理解内容。

避免使用俚语或行话
可以不可以
Internally, ...Under the hood, ...
Create a new cluster.Turn up a new cluster.

避免关于将来的陈述

要避免对将来作出承诺或暗示。如果你需要讨论的是 Alpha 功能特性, 可以将相关文字放在一个单独的标题下,标识为 Alpha 版本信息。

此规则的一个例外是对未来版本中计划移除的已废弃功能选项的文档。 此类文档的例子之一是已弃用 API 迁移指南

避免使用很快就会过时的表达

避免使用一些很快就会过时的陈述,例如“目前”、“新的”。 今天而言是新的功能,过了几个月之后就不再是新的了。

避免使用很快过时的表达
可以不可以
在版本 1.4 中,...在当前版本中,...
联邦功能特性提供 ...新的联邦功能特性提供 ...

避免使用隐含用户对某技术有一定理解的词汇

避免使用“只是”、“仅仅”、“简单”、“很容易地”、“很简单”这类词汇。 这些词并没有提升文档的价值。

避免无意义词汇的注意事项
可以不可以
在 ... 中包含一个命令只需要在... 中包含一个命令
运行容器 ...只需运行该容器...
你可以移除...你可以很容易地移除...
这些步骤...这些简单的步骤...

编辑器配置文件

Kubernetes 项目维护一个 EditorConfig 文件,用于设置文本编辑器(例如 VS Code)中的常见样式首选项。 如果你想确保你的贡献与项目的其余部分样式保持一致,则可以使用此文件。 要查看该文件,请参阅项目仓库根目录的 .editorconfig

接下来

7.8.3 - 图表指南

本指南为你展示如何创建、编辑和分享基于 Mermaid JavaScript 库的图表。 Mermaid.js 允许你使用简单的、类似于 Markdown 的语法来在 Markdown 文件中生成图表。 你也可以使用 Mermaid 来创建 .svg.png 图片文件,将其添加到你的文档中。

本指南的目标受众是所有希望了解 Mermaid 的用户,以及那些想了解如何创建图表并将其添加到 Kubernetes 文档中的用户。

图 1 概要介绍的是本节所涉及的话题。

flowchart LR subgraph m[Mermaid.js] direction TB S[ ]-.- C[使用 Markdown 来
构造图表] --> D[在线
编辑器] end A[为什么图表
很有用] --> m m --> N[3 种创建
图表的方法] N --> T[示例] T --> X[样式
与标题] X --> V[提示] classDef box fill:#fff,stroke:#000,stroke-width:1px,color:#000; classDef spacewhite fill:#ffffff,stroke:#fff,stroke-width:0px,color:#000 class A,C,D,N,X,m,T,V box class S spacewhite %% you can hyperlink Mermaid diagram nodes to a URL using click statements click A "https://mermaid-js.github.io/mermaid-live-editor/edit/#eyJjb2RlIjoiZmxvd2NoYXJ0IExSXG4gICAgc3ViZ3JhcGggbVtNZXJtYWlkLmpzXVxuICAgIGRpcmVjdGlvbiBUQlxuICAgICAgICBTWyBdLS4tXG4gICAgICAgIENbYnVpbGQ8YnI-ZGlhZ3JhbXM8YnI-d2l0aCBtYXJrZG93bl0gLS0-XG4gICAgICAgIERbb24tbGluZTxicj5saXZlIGVkaXRvcl1cbiAgICBlbmRcbiAgICBBW1doeSBhcmUgZGlhZ3JhbXM8YnI-dXNlZnVsP10gLS0-IG1cbiAgICBtIC0tPiBOWzMgeCBtZXRob2RzPGJyPmZvciBjcmVhdGluZzxicj5kaWFncmFtc11cbiAgICBOIC0tPiBUW0V4YW1wbGVzXVxuICAgIFQgLS0-IFhbU3R5bGluZzxicj5hbmQ8YnI-Y2FwdGlvbnNdXG4gICAgWCAtLT4gVltUaXBzXVxuICAgIFxuIFxuICAgIGNsYXNzRGVmIGJveCBmaWxsOiNmZmYsc3Ryb2tlOiMwMDAsc3Ryb2tlLXdpZHRoOjFweCxjb2xvcjojMDAwO1xuICAgIGNsYXNzRGVmIHNwYWNld2hpdGUgZmlsbDojZmZmZmZmLHN0cm9rZTojZmZmLHN0cm9rZS13aWR0aDowcHgsY29sb3I6IzAwMFxuICAgIGNsYXNzIEEsQyxELE4sWCxtLFQsViBib3hcbiAgICBjbGFzcyBTIHNwYWNld2hpdGUiLCJtZXJtYWlkIjoie1xuICBcInRoZW1lXCI6IFwiZGVmYXVsdFwiXG59IiwidXBkYXRlRWRpdG9yIjpmYWxzZSwiYXV0b1N5bmMiOnRydWUsInVwZGF0ZURpYWdyYW0iOnRydWV9" _blank click C "https://mermaid-js.github.io/mermaid-live-editor/edit/#eyJjb2RlIjoiZmxvd2NoYXJ0IExSXG4gICAgc3ViZ3JhcGggbVtNZXJtYWlkLmpzXVxuICAgIGRpcmVjdGlvbiBUQlxuICAgICAgICBTWyBdLS4tXG4gICAgICAgIENbYnVpbGQ8YnI-ZGlhZ3JhbXM8YnI-d2l0aCBtYXJrZG93bl0gLS0-XG4gICAgICAgIERbb24tbGluZTxicj5saXZlIGVkaXRvcl1cbiAgICBlbmRcbiAgICBBW1doeSBhcmUgZGlhZ3JhbXM8YnI-dXNlZnVsP10gLS0-IG1cbiAgICBtIC0tPiBOWzMgeCBtZXRob2RzPGJyPmZvciBjcmVhdGluZzxicj5kaWFncmFtc11cbiAgICBOIC0tPiBUW0V4YW1wbGVzXVxuICAgIFQgLS0-IFhbU3R5bGluZzxicj5hbmQ8YnI-Y2FwdGlvbnNdXG4gICAgWCAtLT4gVltUaXBzXVxuICAgIFxuIFxuICAgIGNsYXNzRGVmIGJveCBmaWxsOiNmZmYsc3Ryb2tlOiMwMDAsc3Ryb2tlLXdpZHRoOjFweCxjb2xvcjojMDAwO1xuICAgIGNsYXNzRGVmIHNwYWNld2hpdGUgZmlsbDojZmZmZmZmLHN0cm9rZTojZmZmLHN0cm9rZS13aWR0aDowcHgsY29sb3I6IzAwMFxuICAgIGNsYXNzIEEsQyxELE4sWCxtLFQsViBib3hcbiAgICBjbGFzcyBTIHNwYWNld2hpdGUiLCJtZXJtYWlkIjoie1xuICBcInRoZW1lXCI6IFwiZGVmYXVsdFwiXG59IiwidXBkYXRlRWRpdG9yIjpmYWxzZSwiYXV0b1N5bmMiOnRydWUsInVwZGF0ZURpYWdyYW0iOnRydWV9" _blank click D "https://mermaid-js.github.io/mermaid-live-editor/edit/#eyJjb2RlIjoiZmxvd2NoYXJ0IExSXG4gICAgc3ViZ3JhcGggbVtNZXJtYWlkLmpzXVxuICAgIGRpcmVjdGlvbiBUQlxuICAgICAgICBTWyBdLS4tXG4gICAgICAgIENbYnVpbGQ8YnI-ZGlhZ3JhbXM8YnI-d2l0aCBtYXJrZG93bl0gLS0-XG4gICAgICAgIERbb24tbGluZTxicj5saXZlIGVkaXRvcl1cbiAgICBlbmRcbiAgICBBW1doeSBhcmUgZGlhZ3JhbXM8YnI-dXNlZnVsP10gLS0-IG1cbiAgICBtIC0tPiBOWzMgeCBtZXRob2RzPGJyPmZvciBjcmVhdGluZzxicj5kaWFncmFtc11cbiAgICBOIC0tPiBUW0V4YW1wbGVzXVxuICAgIFQgLS0-IFhbU3R5bGluZzxicj5hbmQ8YnI-Y2FwdGlvbnNdXG4gICAgWCAtLT4gVltUaXBzXVxuICAgIFxuIFxuICAgIGNsYXNzRGVmIGJveCBmaWxsOiNmZmYsc3Ryb2tlOiMwMDAsc3Ryb2tlLXdpZHRoOjFweCxjb2xvcjojMDAwO1xuICAgIGNsYXNzRGVmIHNwYWNld2hpdGUgZmlsbDojZmZmZmZmLHN0cm9rZTojZmZmLHN0cm9rZS13aWR0aDowcHgsY29sb3I6IzAwMFxuICAgIGNsYXNzIEEsQyxELE4sWCxtLFQsViBib3hcbiAgICBjbGFzcyBTIHNwYWNld2hpdGUiLCJtZXJtYWlkIjoie1xuICBcInRoZW1lXCI6IFwiZGVmYXVsdFwiXG59IiwidXBkYXRlRWRpdG9yIjpmYWxzZSwiYXV0b1N5bmMiOnRydWUsInVwZGF0ZURpYWdyYW0iOnRydWV9" _blank click N "https://mermaid-js.github.io/mermaid-live-editor/edit/#eyJjb2RlIjoiZmxvd2NoYXJ0IExSXG4gICAgc3ViZ3JhcGggbVtNZXJtYWlkLmpzXVxuICAgIGRpcmVjdGlvbiBUQlxuICAgICAgICBTWyBdLS4tXG4gICAgICAgIENbYnVpbGQ8YnI-ZGlhZ3JhbXM8YnI-d2l0aCBtYXJrZG93bl0gLS0-XG4gICAgICAgIERbb24tbGluZTxicj5saXZlIGVkaXRvcl1cbiAgICBlbmRcbiAgICBBW1doeSBhcmUgZGlhZ3JhbXM8YnI-dXNlZnVsP10gLS0-IG1cbiAgICBtIC0tPiBOWzMgeCBtZXRob2RzPGJyPmZvciBjcmVhdGluZzxicj5kaWFncmFtc11cbiAgICBOIC0tPiBUW0V4YW1wbGVzXVxuICAgIFQgLS0-IFhbU3R5bGluZzxicj5hbmQ8YnI-Y2FwdGlvbnNdXG4gICAgWCAtLT4gVltUaXBzXVxuICAgIFxuIFxuICAgIGNsYXNzRGVmIGJveCBmaWxsOiNmZmYsc3Ryb2tlOiMwMDAsc3Ryb2tlLXdpZHRoOjFweCxjb2xvcjojMDAwO1xuICAgIGNsYXNzRGVmIHNwYWNld2hpdGUgZmlsbDojZmZmZmZmLHN0cm9rZTojZmZmLHN0cm9rZS13aWR0aDowcHgsY29sb3I6IzAwMFxuICAgIGNsYXNzIEEsQyxELE4sWCxtLFQsViBib3hcbiAgICBjbGFzcyBTIHNwYWNld2hpdGUiLCJtZXJtYWlkIjoie1xuICBcInRoZW1lXCI6IFwiZGVmYXVsdFwiXG59IiwidXBkYXRlRWRpdG9yIjpmYWxzZSwiYXV0b1N5bmMiOnRydWUsInVwZGF0ZURpYWdyYW0iOnRydWV9" _blank click T "https://mermaid-js.github.io/mermaid-live-editor/edit/#eyJjb2RlIjoiZmxvd2NoYXJ0IExSXG4gICAgc3ViZ3JhcGggbVtNZXJtYWlkLmpzXVxuICAgIGRpcmVjdGlvbiBUQlxuICAgICAgICBTWyBdLS4tXG4gICAgICAgIENbYnVpbGQ8YnI-ZGlhZ3JhbXM8YnI-d2l0aCBtYXJrZG93bl0gLS0-XG4gICAgICAgIERbb24tbGluZTxicj5saXZlIGVkaXRvcl1cbiAgICBlbmRcbiAgICBBW1doeSBhcmUgZGlhZ3JhbXM8YnI-dXNlZnVsP10gLS0-IG1cbiAgICBtIC0tPiBOWzMgeCBtZXRob2RzPGJyPmZvciBjcmVhdGluZzxicj5kaWFncmFtc11cbiAgICBOIC0tPiBUW0V4YW1wbGVzXVxuICAgIFQgLS0-IFhbU3R5bGluZzxicj5hbmQ8YnI-Y2FwdGlvbnNdXG4gICAgWCAtLT4gVltUaXBzXVxuICAgIFxuIFxuICAgIGNsYXNzRGVmIGJveCBmaWxsOiNmZmYsc3Ryb2tlOiMwMDAsc3Ryb2tlLXdpZHRoOjFweCxjb2xvcjojMDAwO1xuICAgIGNsYXNzRGVmIHNwYWNld2hpdGUgZmlsbDojZmZmZmZmLHN0cm9rZTojZmZmLHN0cm9rZS13aWR0aDowcHgsY29sb3I6IzAwMFxuICAgIGNsYXNzIEEsQyxELE4sWCxtLFQsViBib3hcbiAgICBjbGFzcyBTIHNwYWNld2hpdGUiLCJtZXJtYWlkIjoie1xuICBcInRoZW1lXCI6IFwiZGVmYXVsdFwiXG59IiwidXBkYXRlRWRpdG9yIjpmYWxzZSwiYXV0b1N5bmMiOnRydWUsInVwZGF0ZURpYWdyYW0iOnRydWV9" _blank click X "https://mermaid-js.github.io/mermaid-live-editor/edit/#eyJjb2RlIjoiZmxvd2NoYXJ0IExSXG4gICAgc3ViZ3JhcGggbVtNZXJtYWlkLmpzXVxuICAgIGRpcmVjdGlvbiBUQlxuICAgICAgICBTWyBdLS4tXG4gICAgICAgIENbYnVpbGQ8YnI-ZGlhZ3JhbXM8YnI-d2l0aCBtYXJrZG93bl0gLS0-XG4gICAgICAgIERbb24tbGluZTxicj5saXZlIGVkaXRvcl1cbiAgICBlbmRcbiAgICBBW1doeSBhcmUgZGlhZ3JhbXM8YnI-dXNlZnVsP10gLS0-IG1cbiAgICBtIC0tPiBOWzMgeCBtZXRob2RzPGJyPmZvciBjcmVhdGluZzxicj5kaWFncmFtc11cbiAgICBOIC0tPiBUW0V4YW1wbGVzXVxuICAgIFQgLS0-IFhbU3R5bGluZzxicj5hbmQ8YnI-Y2FwdGlvbnNdXG4gICAgWCAtLT4gVltUaXBzXVxuICAgIFxuIFxuICAgIGNsYXNzRGVmIGJveCBmaWxsOiNmZmYsc3Ryb2tlOiMwMDAsc3Ryb2tlLXdpZHRoOjFweCxjb2xvcjojMDAwO1xuICAgIGNsYXNzRGVmIHNwYWNld2hpdGUgZmlsbDojZmZmZmZmLHN0cm9rZTojZmZmLHN0cm9rZS13aWR0aDowcHgsY29sb3I6IzAwMFxuICAgIGNsYXNzIEEsQyxELE4sWCxtLFQsViBib3hcbiAgICBjbGFzcyBTIHNwYWNld2hpdGUiLCJtZXJtYWlkIjoie1xuICBcInRoZW1lXCI6IFwiZGVmYXVsdFwiXG59IiwidXBkYXRlRWRpdG9yIjpmYWxzZSwiYXV0b1N5bmMiOnRydWUsInVwZGF0ZURpYWdyYW0iOnRydWV9" _blank click V "https://mermaid-js.github.io/mermaid-live-editor/edit/#eyJjb2RlIjoiZmxvd2NoYXJ0IExSXG4gICAgc3ViZ3JhcGggbVtNZXJtYWlkLmpzXVxuICAgIGRpcmVjdGlvbiBUQlxuICAgICAgICBTWyBdLS4tXG4gICAgICAgIENbYnVpbGQ8YnI-ZGlhZ3JhbXM8YnI-d2l0aCBtYXJrZG93bl0gLS0-XG4gICAgICAgIERbb24tbGluZTxicj5saXZlIGVkaXRvcl1cbiAgICBlbmRcbiAgICBBW1doeSBhcmUgZGlhZ3JhbXM8YnI-dXNlZnVsP10gLS0-IG1cbiAgICBtIC0tPiBOWzMgeCBtZXRob2RzPGJyPmZvciBjcmVhdGluZzxicj5kaWFncmFtc11cbiAgICBOIC0tPiBUW0V4YW1wbGVzXVxuICAgIFQgLS0-IFhbU3R5bGluZzxicj5hbmQ8YnI-Y2FwdGlvbnNdXG4gICAgWCAtLT4gVltUaXBzXVxuICAgIFxuIFxuICAgIGNsYXNzRGVmIGJveCBmaWxsOiNmZmYsc3Ryb2tlOiMwMDAsc3Ryb2tlLXdpZHRoOjFweCxjb2xvcjojMDAwO1xuICAgIGNsYXNzRGVmIHNwYWNld2hpdGUgZmlsbDojZmZmZmZmLHN0cm9rZTojZmZmLHN0cm9rZS13aWR0aDowcHgsY29sb3I6IzAwMFxuICAgIGNsYXNzIEEsQyxELE4sWCxtLFQsViBib3hcbiAgICBjbGFzcyBTIHNwYWNld2hpdGUiLCJtZXJtYWlkIjoie1xuICBcInRoZW1lXCI6IFwiZGVmYXVsdFwiXG59IiwidXBkYXRlRWRpdG9yIjpmYWxzZSwiYXV0b1N5bmMiOnRydWUsInVwZGF0ZURpYWdyYW0iOnRydWV9" _blank

图 1. 本节中涉及的话题。

开始使用 Mermaid 之前,你需要以下准备:

你为什么应该在代码中使用图表

图表可以增进文档的清晰度,便于理解。对于用户和贡献者而言都有好处。

用户获得的好处有:

  • 较为友好的初次体验:非常详尽的、只包含文本的欢迎页面对用户而言是蛮恐怖的, 尤其是初次接触 Kubernetes 的用户。
  • 快速理解概念:图表可以帮助用户理解复杂主题下的要点。 你的图表可以作为一种可视化的学习指南,将用户带入主题的细节。
  • 便于记忆:对某些人而言,图形(图像)要比文字更容易记忆。

对贡献者而言的好处有:

  • 帮助确立所贡献文档的结构和内容。例如, 你可以先提供一个覆盖所有顶层要点的图表,然后再逐步展开细节。
  • 培养用户社区并提升其能力。容易理解的文档,附以图表,能够吸引新的用户, 尤其是那些因为预见到复杂性而不愿参与的用户。

你需要考虑你的目标受众。除了一些有经验的 Kubernetes 用户外,你还会遇到很多刚接触 Kubernetes 的用户。即使一张简单的图表也可以帮助新用户吸收 Kubernetes 概念。 他们会变得更为大胆和自信,进一步地了解 Kubernetes 及其文档。

Mermaid

Mermaid 是一个开源的 JavaScript 库, 可以帮助你创建、编辑并很容易地分享图表。这些图表使用简单的、类似 Markdown 的语法开发,并可内嵌到 Markdown 文件中。

下面是 Mermaid 的一些特性:

  • 简单的编码语法
  • 包含基于 Web 的工具,便于你编制和预览你的图表
  • 支持包括流程图、状态图、时序图在内的多种格式
  • 可以通过共享图表的 URL 来与同事方便地合作
  • 有丰富的形状、线条、主题和样式可供选择

使用 Mermaid 的一些好处如下:

  • 不需要使用另外的、非 Mermaid 的图表工具
  • 与现有的 PR 工作流结合的很好。你可以将 Mermaid 代码视为你的 PR 中所包含的 Markdown 文本
  • 简单的工具生成简单的图表。你不需要精心制作或雕琢过于复杂或详尽的图片。 保持简单就好。

Mermaid 提供一种简单的、开放且透明的方法,便于 SIG 社区为新的或现有的文档添加、 编辑图表并展开协作。

在线编辑器

Mermaid 在线编辑器是一个基于 Web 的工具,允许你创建、编辑和审阅图表。

在线编辑器的功能主要有:

  • 显示 Mermaid 代码和渲染的图表。
  • 为所保存的每个图表生成一个 URL。该 URL 显示在你的浏览器的 URL 字段中。 你可以将 URL 分享给同事,便于他人访问和更改图表。
  • 提供将图表下载为 .svg.png 文件的选项。

创建图表的方法

图 2 给出三种生成和添加图表的方法。

graph TB A[贡献者] B[向 .md 文件

中内嵌
Mermaid 代码] C[Mermaid+SVG

将 Mermaid 所生成的
SVG 文件添加到 .md 文件] D[外部工具

添加外部工具
所生成的 SVG
文件到 .md 文件] A --> B A --> C A --> D classDef box fill:#fff,stroke:#000,stroke-width:1px,color:#000; class A,B,C,D box %% 你可以使用 click 语句为 Mermaid 节点设置指向某 URL 的超链接 click A "https://mermaid-js.github.io/mermaid-live-editor/edit/#eyJjb2RlIjoiZ3JhcGggVEJcbiAgICBBW0NvbnRyaWJ1dG9yXVxuICAgIEJbSW5saW5lPGJyPjxicj5NZXJtYWlkIGNvZGU8YnI-YWRkZWQgdG8gLm1kIGZpbGVdXG4gICAgQ1tNZXJtYWlkK1NWRzxicj48YnI-QWRkIG1lcm1haWQtZ2VuZXJhdGVkPGJyPnN2ZyBmaWxlIHRvIC5tZCBmaWxlXVxuICAgIERbRXh0ZXJuYWwgdG9vbDxicj48YnI-QWRkIGV4dGVybmFsLXRvb2wtPGJyPmdlbmVyYXRlZCBzdmcgZmlsZTxicj50byAubWQgZmlsZV1cblxuICAgIEEgLS0-IEJcbiAgICBBIC0tPiBDXG4gICAgQSAtLT4gRFxuXG4gICAgY2xhc3NEZWYgYm94IGZpbGw6I2ZmZixzdHJva2U6IzAwMCxzdHJva2Utd2lkdGg6MXB4LGNvbG9yOiMwMDA7XG4gICAgY2xhc3MgQSxCLEMsRCBib3giLCJtZXJtYWlkIjoie1xuICBcInRoZW1lXCI6IFwiZGVmYXVsdFwiXG59IiwidXBkYXRlRWRpdG9yIjpmYWxzZSwiYXV0b1N5bmMiOnRydWUsInVwZGF0ZURpYWdyYW0iOmZhbHNlfQ" _blank click B "https://mermaid-js.github.io/mermaid-live-editor/edit/#eyJjb2RlIjoiZ3JhcGggVEJcbiAgICBBW0NvbnRyaWJ1dG9yXVxuICAgIEJbSW5saW5lPGJyPjxicj5NZXJtYWlkIGNvZGU8YnI-YWRkZWQgdG8gLm1kIGZpbGVdXG4gICAgQ1tNZXJtYWlkK1NWRzxicj48YnI-QWRkIG1lcm1haWQtZ2VuZXJhdGVkPGJyPnN2ZyBmaWxlIHRvIC5tZCBmaWxlXVxuICAgIERbRXh0ZXJuYWwgdG9vbDxicj48YnI-QWRkIGV4dGVybmFsLXRvb2wtPGJyPmdlbmVyYXRlZCBzdmcgZmlsZTxicj50byAubWQgZmlsZV1cblxuICAgIEEgLS0-IEJcbiAgICBBIC0tPiBDXG4gICAgQSAtLT4gRFxuXG4gICAgY2xhc3NEZWYgYm94IGZpbGw6I2ZmZixzdHJva2U6IzAwMCxzdHJva2Utd2lkdGg6MXB4LGNvbG9yOiMwMDA7XG4gICAgY2xhc3MgQSxCLEMsRCBib3giLCJtZXJtYWlkIjoie1xuICBcInRoZW1lXCI6IFwiZGVmYXVsdFwiXG59IiwidXBkYXRlRWRpdG9yIjpmYWxzZSwiYXV0b1N5bmMiOnRydWUsInVwZGF0ZURpYWdyYW0iOmZhbHNlfQ" _blank click C "https://mermaid-js.github.io/mermaid-live-editor/edit/#eyJjb2RlIjoiZ3JhcGggVEJcbiAgICBBW0NvbnRyaWJ1dG9yXVxuICAgIEJbSW5saW5lPGJyPjxicj5NZXJtYWlkIGNvZGU8YnI-YWRkZWQgdG8gLm1kIGZpbGVdXG4gICAgQ1tNZXJtYWlkK1NWRzxicj48YnI-QWRkIG1lcm1haWQtZ2VuZXJhdGVkPGJyPnN2ZyBmaWxlIHRvIC5tZCBmaWxlXVxuICAgIERbRXh0ZXJuYWwgdG9vbDxicj48YnI-QWRkIGV4dGVybmFsLXRvb2wtPGJyPmdlbmVyYXRlZCBzdmcgZmlsZTxicj50byAubWQgZmlsZV1cblxuICAgIEEgLS0-IEJcbiAgICBBIC0tPiBDXG4gICAgQSAtLT4gRFxuXG4gICAgY2xhc3NEZWYgYm94IGZpbGw6I2ZmZixzdHJva2U6IzAwMCxzdHJva2Utd2lkdGg6MXB4LGNvbG9yOiMwMDA7XG4gICAgY2xhc3MgQSxCLEMsRCBib3giLCJtZXJtYWlkIjoie1xuICBcInRoZW1lXCI6IFwiZGVmYXVsdFwiXG59IiwidXBkYXRlRWRpdG9yIjpmYWxzZSwiYXV0b1N5bmMiOnRydWUsInVwZGF0ZURpYWdyYW0iOmZhbHNlfQ" _blank click D "https://mermaid-js.github.io/mermaid-live-editor/edit/#eyJjb2RlIjoiZ3JhcGggVEJcbiAgICBBW0NvbnRyaWJ1dG9yXVxuICAgIEJbSW5saW5lPGJyPjxicj5NZXJtYWlkIGNvZGU8YnI-YWRkZWQgdG8gLm1kIGZpbGVdXG4gICAgQ1tNZXJtYWlkK1NWRzxicj48YnI-QWRkIG1lcm1haWQtZ2VuZXJhdGVkPGJyPnN2ZyBmaWxlIHRvIC5tZCBmaWxlXVxuICAgIERbRXh0ZXJuYWwgdG9vbDxicj48YnI-QWRkIGV4dGVybmFsLXRvb2wtPGJyPmdlbmVyYXRlZCBzdmcgZmlsZTxicj50byAubWQgZmlsZV1cblxuICAgIEEgLS0-IEJcbiAgICBBIC0tPiBDXG4gICAgQSAtLT4gRFxuXG4gICAgY2xhc3NEZWYgYm94IGZpbGw6I2ZmZixzdHJva2U6IzAwMCxzdHJva2Utd2lkdGg6MXB4LGNvbG9yOiMwMDA7XG4gICAgY2xhc3MgQSxCLEMsRCBib3giLCJtZXJtYWlkIjoie1xuICBcInRoZW1lXCI6IFwiZGVmYXVsdFwiXG59IiwidXBkYXRlRWRpdG9yIjpmYWxzZSwiYXV0b1N5bmMiOnRydWUsInVwZGF0ZURpYWdyYW0iOmZhbHNlfQ" _blank

图 2. 创建图表的方法

内嵌(Inline)

图 3 给出的是使用内嵌方法来添加图表所遵循的步骤。

graph LR A[1. 使用在线编辑器
来创建或编辑
图表] --> B[2. 将图表的 URL
保存到某处] --> C[3. 将 Mermaid 代码
复制到 markdown 文件中] --> D[4. 添加图表标题] classDef box fill:#fff,stroke:#000,stroke-width:1px,color:#000; class A,B,C,D box %% 你可以使用 click 语句为 Mermaid 节点设置指向某 URL 的超链接 click A "https://mermaid-js.github.io/mermaid-live-editor/edit/#eyJjb2RlIjoiZ3JhcGggTFJcbiAgICBBWzEuIFVzZSBsaXZlIGVkaXRvcjxicj4gdG8gY3JlYXRlL2VkaXQ8YnI-ZGlhZ3JhbV0gLS0-XG4gICAgQlsyLiBTdG9yZSBkaWFncmFtPGJyPlVSTCBzb21ld2hlcmVdIC0tPlxuICAgIENbMy4gQ29weSBNZXJtYWlkIGNvZGU8YnI-dG8gcGFnZSBtYXJrZG93biBmaWxlXSAtLT5cbiAgICBEWzQuIEFkZCBjYXB0aW9uXVxuIFxuXG4gICAgY2xhc3NEZWYgYm94IGZpbGw6I2ZmZixzdHJva2U6IzAwMCxzdHJva2Utd2lkdGg6MXB4LGNvbG9yOiMwMDA7XG4gICAgY2xhc3MgQSxCLEMsRCBib3hcbiAgICAiLCJtZXJtYWlkIjoie1xuICBcInRoZW1lXCI6IFwiZGVmYXVsdFwiXG59IiwidXBkYXRlRWRpdG9yIjpmYWxzZSwiYXV0b1N5bmMiOnRydWUsInVwZGF0ZURpYWdyYW0iOmZhbHNlfQ" _blank click B "https://mermaid-js.github.io/mermaid-live-editor/edit/#eyJjb2RlIjoiZ3JhcGggTFJcbiAgICBBWzEuIFVzZSBsaXZlIGVkaXRvcjxicj4gdG8gY3JlYXRlL2VkaXQ8YnI-ZGlhZ3JhbV0gLS0-XG4gICAgQlsyLiBTdG9yZSBkaWFncmFtPGJyPlVSTCBzb21ld2hlcmVdIC0tPlxuICAgIENbMy4gQ29weSBNZXJtYWlkIGNvZGU8YnI-dG8gcGFnZSBtYXJrZG93biBmaWxlXSAtLT5cbiAgICBEWzQuIEFkZCBjYXB0aW9uXVxuIFxuXG4gICAgY2xhc3NEZWYgYm94IGZpbGw6I2ZmZixzdHJva2U6IzAwMCxzdHJva2Utd2lkdGg6MXB4LGNvbG9yOiMwMDA7XG4gICAgY2xhc3MgQSxCLEMsRCBib3hcbiAgICAiLCJtZXJtYWlkIjoie1xuICBcInRoZW1lXCI6IFwiZGVmYXVsdFwiXG59IiwidXBkYXRlRWRpdG9yIjpmYWxzZSwiYXV0b1N5bmMiOnRydWUsInVwZGF0ZURpYWdyYW0iOmZhbHNlfQ" _blank click C "https://mermaid-js.github.io/mermaid-live-editor/edit/#eyJjb2RlIjoiZ3JhcGggTFJcbiAgICBBWzEuIFVzZSBsaXZlIGVkaXRvcjxicj4gdG8gY3JlYXRlL2VkaXQ8YnI-ZGlhZ3JhbV0gLS0-XG4gICAgQlsyLiBTdG9yZSBkaWFncmFtPGJyPlVSTCBzb21ld2hlcmVdIC0tPlxuICAgIENbMy4gQ29weSBNZXJtYWlkIGNvZGU8YnI-dG8gcGFnZSBtYXJrZG93biBmaWxlXSAtLT5cbiAgICBEWzQuIEFkZCBjYXB0aW9uXVxuIFxuXG4gICAgY2xhc3NEZWYgYm94IGZpbGw6I2ZmZixzdHJva2U6IzAwMCxzdHJva2Utd2lkdGg6MXB4LGNvbG9yOiMwMDA7XG4gICAgY2xhc3MgQSxCLEMsRCBib3hcbiAgICAiLCJtZXJtYWlkIjoie1xuICBcInRoZW1lXCI6IFwiZGVmYXVsdFwiXG59IiwidXBkYXRlRWRpdG9yIjpmYWxzZSwiYXV0b1N5bmMiOnRydWUsInVwZGF0ZURpYWdyYW0iOmZhbHNlfQ" _blank click D "https://mermaid-js.github.io/mermaid-live-editor/edit/#eyJjb2RlIjoiZ3JhcGggTFJcbiAgICBBWzEuIFVzZSBsaXZlIGVkaXRvcjxicj4gdG8gY3JlYXRlL2VkaXQ8YnI-ZGlhZ3JhbV0gLS0-XG4gICAgQlsyLiBTdG9yZSBkaWFncmFtPGJyPlVSTCBzb21ld2hlcmVdIC0tPlxuICAgIENbMy4gQ29weSBNZXJtYWlkIGNvZGU8YnI-dG8gcGFnZSBtYXJrZG93biBmaWxlXSAtLT5cbiAgICBEWzQuIEFkZCBjYXB0aW9uXVxuIFxuXG4gICAgY2xhc3NEZWYgYm94IGZpbGw6I2ZmZixzdHJva2U6IzAwMCxzdHJva2Utd2lkdGg6MXB4LGNvbG9yOiMwMDA7XG4gICAgY2xhc3MgQSxCLEMsRCBib3hcbiAgICAiLCJtZXJtYWlkIjoie1xuICBcInRoZW1lXCI6IFwiZGVmYXVsdFwiXG59IiwidXBkYXRlRWRpdG9yIjpmYWxzZSwiYXV0b1N5bmMiOnRydWUsInVwZGF0ZURpYWdyYW0iOmZhbHNlfQ" _blank

图 3. 内嵌方法的步骤

下面是使用内嵌方法来添加图表时你要执行的步骤:

  1. 使用在线编辑器创建你的图表
  2. 将图表的 URL 保存在某处以便以后访问
  3. 将 Mermaid 代码复制到你的 .md 文件中你希望它出现的位置
  4. 使用 Markdown 文本在图表下方为其添加标题

Hugo 在构造(网站)过程中会运行 Mermaid 代码,将其转换为图表。

下面是一段包含在某 .md 文件中的示例代码片段:

---
title: 我的文档
---
图 17 给出从 A 到 B 的一个简单流程。
这里是其他 markdown 文本
...
{{< mermaid >}} 
    graph TB
    A --> B
{{< /mermaid >}}

图 17. 从 A 到 B

其他文本

有关添加图表标题的细节,参阅如何使用标题

使用内嵌方法的好处有:

  • 可以直接使用在线编辑器工具
  • 很容易在在线编辑器与你的 .md 文件之间来回复制 Mermaid 代码
  • 不需要额外处理 .svg 图片文件
  • 内容文字、图表代码和图表标题都位于同一个 .md 文件中。

你应该使用本地和 Netlify 预览来验证图表是可以正常渲染的。

Mermaid+SVG

图 4 给出的是使用 Mermaid+SVG 方法添加图表所要遵循的步骤:

flowchart LR A[1. 使用在线编辑器
创建或编辑
图表] B[2. 将图表的 URL
保存到别处] C[3. 生成 .svg 文件
并将其下载到
images/ 目录] subgraph w[ ] direction TB D[4. 使用 figure 短代码
来在 .md 文件中
引用 .svg 文件] --> E[5. 添加图表标题] end A --> B B --> C C --> w classDef box fill:#fff,stroke:#000,stroke-width:1px,color:#000; class A,B,C,D,E,w box click A "https://mermaid-js.github.io/mermaid-live-editor/edit/#eyJjb2RlIjoiZmxvd2NoYXJ0IExSXG4gICAgQVsxLiBVc2UgbGl2ZSBlZGl0b3I8YnI-IHRvIGNyZWF0ZS9lZGl0PGJyPmRpYWdyYW1dXG4gICAgQlsyLiBTdG9yZSBkaWFncmFtPGJyPlVSTCBzb21ld2hlcmVdXG4gICAgQ1szLiBHZW5lcmF0ZSAuc3ZnIGZpbGU8YnI-YW5kIGRvd25sb2FkIHRvPGJyPmltYWdlcy8gZm9sZGVyXVxuICAgIHN1YmdyYXBoIHdbIF1cbiAgICBkaXJlY3Rpb24gVEJcbiAgICBEWzQuIFVzZSBmaWd1cmUgc2hvcnRjb2RlPGJyPnRvIHJlZmVyZW5jZSAuc3ZnPGJyPmZpbGUgaW4gcGFnZTxicj4ubWQgZmlsZV0gLS0-XG4gICAgRVs1LiBBZGQgY2FwdGlvbl1cbiAgICBlbmRcbkEgLS0-IEJcbkIgLS0-IENcbkMgLS0-IHdcblxuICAgIGNsYXNzRGVmIGJveCBmaWxsOiNmZmYsc3Ryb2tlOiMwMDAsc3Ryb2tlLXdpZHRoOjFweCxjb2xvcjojMDAwO1xuICAgIGNsYXNzIEEsQixDLEQsRSx3IGJveFxuICAgICIsIm1lcm1haWQiOiJ7XG4gIFwidGhlbWVcIjogXCJkZWZhdWx0XCJcbn0iLCJ1cGRhdGVFZGl0b3IiOmZhbHNlLCJhdXRvU3luYyI6dHJ1ZSwidXBkYXRlRGlhZ3JhbSI6dHJ1ZX0" _blank click B "https://mermaid-js.github.io/mermaid-live-editor/edit/#eyJjb2RlIjoiZmxvd2NoYXJ0IExSXG4gICAgQVsxLiBVc2UgbGl2ZSBlZGl0b3I8YnI-IHRvIGNyZWF0ZS9lZGl0PGJyPmRpYWdyYW1dXG4gICAgQlsyLiBTdG9yZSBkaWFncmFtPGJyPlVSTCBzb21ld2hlcmVdXG4gICAgQ1szLiBHZW5lcmF0ZSAuc3ZnIGZpbGU8YnI-YW5kIGRvd25sb2FkIHRvPGJyPmltYWdlcy8gZm9sZGVyXVxuICAgIHN1YmdyYXBoIHdbIF1cbiAgICBkaXJlY3Rpb24gVEJcbiAgICBEWzQuIFVzZSBmaWd1cmUgc2hvcnRjb2RlPGJyPnRvIHJlZmVyZW5jZSAuc3ZnPGJyPmZpbGUgaW4gcGFnZTxicj4ubWQgZmlsZV0gLS0-XG4gICAgRVs1LiBBZGQgY2FwdGlvbl1cbiAgICBlbmRcbkEgLS0-IEJcbkIgLS0-IENcbkMgLS0-IHdcblxuICAgIGNsYXNzRGVmIGJveCBmaWxsOiNmZmYsc3Ryb2tlOiMwMDAsc3Ryb2tlLXdpZHRoOjFweCxjb2xvcjojMDAwO1xuICAgIGNsYXNzIEEsQixDLEQsRSx3IGJveFxuICAgICIsIm1lcm1haWQiOiJ7XG4gIFwidGhlbWVcIjogXCJkZWZhdWx0XCJcbn0iLCJ1cGRhdGVFZGl0b3IiOmZhbHNlLCJhdXRvU3luYyI6dHJ1ZSwidXBkYXRlRGlhZ3JhbSI6dHJ1ZX0" _blank click C "https://mermaid-js.github.io/mermaid-live-editor/edit/#eyJjb2RlIjoiZmxvd2NoYXJ0IExSXG4gICAgQVsxLiBVc2UgbGl2ZSBlZGl0b3I8YnI-IHRvIGNyZWF0ZS9lZGl0PGJyPmRpYWdyYW1dXG4gICAgQlsyLiBTdG9yZSBkaWFncmFtPGJyPlVSTCBzb21ld2hlcmVdXG4gICAgQ1szLiBHZW5lcmF0ZSAuc3ZnIGZpbGU8YnI-YW5kIGRvd25sb2FkIHRvPGJyPmltYWdlcy8gZm9sZGVyXVxuICAgIHN1YmdyYXBoIHdbIF1cbiAgICBkaXJlY3Rpb24gVEJcbiAgICBEWzQuIFVzZSBmaWd1cmUgc2hvcnRjb2RlPGJyPnRvIHJlZmVyZW5jZSAuc3ZnPGJyPmZpbGUgaW4gcGFnZTxicj4ubWQgZmlsZV0gLS0-XG4gICAgRVs1LiBBZGQgY2FwdGlvbl1cbiAgICBlbmRcbkEgLS0-IEJcbkIgLS0-IENcbkMgLS0-IHdcblxuICAgIGNsYXNzRGVmIGJveCBmaWxsOiNmZmYsc3Ryb2tlOiMwMDAsc3Ryb2tlLXdpZHRoOjFweCxjb2xvcjojMDAwO1xuICAgIGNsYXNzIEEsQixDLEQsRSx3IGJveFxuICAgICIsIm1lcm1haWQiOiJ7XG4gIFwidGhlbWVcIjogXCJkZWZhdWx0XCJcbn0iLCJ1cGRhdGVFZGl0b3IiOmZhbHNlLCJhdXRvU3luYyI6dHJ1ZSwidXBkYXRlRGlhZ3JhbSI6dHJ1ZX0" _blank click D "https://mermaid-js.github.io/mermaid-live-editor/edit/#eyJjb2RlIjoiZmxvd2NoYXJ0IExSXG4gICAgQVsxLiBVc2UgbGl2ZSBlZGl0b3I8YnI-IHRvIGNyZWF0ZS9lZGl0PGJyPmRpYWdyYW1dXG4gICAgQlsyLiBTdG9yZSBkaWFncmFtPGJyPlVSTCBzb21ld2hlcmVdXG4gICAgQ1szLiBHZW5lcmF0ZSAuc3ZnIGZpbGU8YnI-YW5kIGRvd25sb2FkIHRvPGJyPmltYWdlcy8gZm9sZGVyXVxuICAgIHN1YmdyYXBoIHdbIF1cbiAgICBkaXJlY3Rpb24gVEJcbiAgICBEWzQuIFVzZSBmaWd1cmUgc2hvcnRjb2RlPGJyPnRvIHJlZmVyZW5jZSAuc3ZnPGJyPmZpbGUgaW4gcGFnZTxicj4ubWQgZmlsZV0gLS0-XG4gICAgRVs1LiBBZGQgY2FwdGlvbl1cbiAgICBlbmRcbkEgLS0-IEJcbkIgLS0-IENcbkMgLS0-IHdcblxuICAgIGNsYXNzRGVmIGJveCBmaWxsOiNmZmYsc3Ryb2tlOiMwMDAsc3Ryb2tlLXdpZHRoOjFweCxjb2xvcjojMDAwO1xuICAgIGNsYXNzIEEsQixDLEQsRSx3IGJveFxuICAgICIsIm1lcm1haWQiOiJ7XG4gIFwidGhlbWVcIjogXCJkZWZhdWx0XCJcbn0iLCJ1cGRhdGVFZGl0b3IiOmZhbHNlLCJhdXRvU3luYyI6dHJ1ZSwidXBkYXRlRGlhZ3JhbSI6dHJ1ZX0" _blank click E "https://mermaid-js.github.io/mermaid-live-editor/edit/#eyJjb2RlIjoiZmxvd2NoYXJ0IExSXG4gICAgQVsxLiBVc2UgbGl2ZSBlZGl0b3I8YnI-IHRvIGNyZWF0ZS9lZGl0PGJyPmRpYWdyYW1dXG4gICAgQlsyLiBTdG9yZSBkaWFncmFtPGJyPlVSTCBzb21ld2hlcmVdXG4gICAgQ1szLiBHZW5lcmF0ZSAuc3ZnIGZpbGU8YnI-YW5kIGRvd25sb2FkIHRvPGJyPmltYWdlcy8gZm9sZGVyXVxuICAgIHN1YmdyYXBoIHdbIF1cbiAgICBkaXJlY3Rpb24gVEJcbiAgICBEWzQuIFVzZSBmaWd1cmUgc2hvcnRjb2RlPGJyPnRvIHJlZmVyZW5jZSAuc3ZnPGJyPmZpbGUgaW4gcGFnZTxicj4ubWQgZmlsZV0gLS0-XG4gICAgRVs1LiBBZGQgY2FwdGlvbl1cbiAgICBlbmRcbkEgLS0-IEJcbkIgLS0-IENcbkMgLS0-IHdcblxuICAgIGNsYXNzRGVmIGJveCBmaWxsOiNmZmYsc3Ryb2tlOiMwMDAsc3Ryb2tlLXdpZHRoOjFweCxjb2xvcjojMDAwO1xuICAgIGNsYXNzIEEsQixDLEQsRSx3IGJveFxuICAgICIsIm1lcm1haWQiOiJ7XG4gIFwidGhlbWVcIjogXCJkZWZhdWx0XCJcbn0iLCJ1cGRhdGVFZGl0b3IiOmZhbHNlLCJhdXRvU3luYyI6dHJ1ZSwidXBkYXRlRGlhZ3JhbSI6dHJ1ZX0" _blank

图 4. Mermaid+SVG 方法的步骤。

使用 Mermaid+SVG 方法来添加图表时你要遵从的步骤:

  1. 使用在线编辑器创建你的图表
  2. 将图表的 URL 保存到某处以便以后访问
  3. 为你的图表生成 .svg 文件,并将其下载到合适的 images/ 目录下
  4. 使用 {{< figure >}} 短代码在 .md 文件中引用该图表
  5. 使用 {{< figure >}} 短代码的 caption 参数为图表设置标题

例如,使用在线编辑器创建一个名为 boxnet 的图表。 将图表的 URL 保存到别处以便以后访问。生成 boxnet.svg 文件并将其下载到合适的 ../images/ 目录下。

在你的 PR 中的 .md 文件内使用 {{< figure >}} 短代码来引用 .svg 图片文件,并为之添加标题。

{{< figure src="/static/images/boxnet.svg" alt="Boxnet 示意图" class="diagram-large" caption="图 14. Boxnet 标题" >}}

关于图表标题的细节,可参阅如何使用标题

你应该使用文本编辑器以注释块的形式在 .svg 图片文件中添加在线编辑器的 URL。 例如,你应该在 .svg 图片文件的开头部分包含下面的内容:

<!-- 要查看或者编辑 Mermaid 代码,可访问下面的 URL:-->
<!-- https://mermaid-js.github.io/mermaid-live-editor/edit/#eyJjb ... <URL 的其余部分> -->

使用 Mermaid+SVG 方法的好处有:

  • 可以直接使用在线编辑器工具
  • 在线编辑器支持的 Mermaid 特性集合最新
  • 可以利用 kubernetes/website 用来处理 .svg 图片文件的现有方法
  • 工作环境不需要 Mermaid 支持

要使用本地和 Netlify 预览来检查你的图表可以正常渲染。

外部工具

图 5 给出使用外部工具来添加图表时所遵循的步骤。

首先,要使用你的外部工具来创建图表,并将其保存为一个 .svg 文件或 .png 图片文件。 之后,使用 Mermaid+SVG 方法中相同的步骤添加 .svg.png)文件。

flowchart LR A[1. 使用外部工具
来创建或编辑
图表] B[2. 如果可能保存
图表位置供
其他贡献者访问] C[3. 生成 .svg 文件
或 .png 文件
并将其下载到
合适的 images/ 目录] subgraph w[ ] direction TB D[4. 使用 figure 短代码
在你的 .md 文件中
引用该 SVG 或 PNG
文件] --> E[5. 为图表添加标题] end A --> B B --> C C --> w classDef box fill:#fff,stroke:#000,stroke-width:1px,color:#000; class A,B,C,D,E,w box click A "https://mermaid-js.github.io/mermaid-live-editor/edit/#eyJjb2RlIjoiZmxvd2NoYXJ0IExSXG4gICAgQVsxLiBVc2UgZXh0ZXJuYWw8YnI-dG9vbCB0byBjcmVhdGUvZWRpdDxicj5kaWFncmFtXVxuICAgIEJbMi4gSWYgcG9zc2libGUsIHNhdmU8YnI-ZGlhZ3JhbSBjb29yZGluYXRlczxicj5mb3IgY29udHJpYnV0b3I8YnI-YWNjZXNzXVxuICAgIENbMy4gR2VuZXJhdGUgLnN2ZyA8YnI-b3IucG5nIGZpbGU8YnI-YW5kIGRvd25sb2FkIHRvPGJyPmFwcHJvcHJpYXRlPGJyPmltYWdlcy8gZm9sZGVyXVxuICAgIHN1YmdyYXBoIHdbIF1cbiAgICBkaXJlY3Rpb24gVEJcbiAgICBEWzQuIFVzZSBmaWd1cmUgc2hvcnRjb2RlPGJyPnRvIHJlZmVyZW5jZSBzdmcgb3I8YnI-cG5nIGZpbGUgaW48YnI-cGFnZSAubWQgZmlsZV0gLS0-XG4gICAgRVs1LiBBZGQgY2FwdGlvbl1cbiAgICBlbmRcbiAgICBBIC0tPiBCXG4gICAgQiAtLT4gQ1xuICAgIEMgLS0-IHdcbiAgICBjbGFzc0RlZiBib3ggZmlsbDojZmZmLHN0cm9rZTojMDAwLHN0cm9rZS13aWR0aDoxcHgsY29sb3I6IzAwMDtcbiAgICBjbGFzcyBBLEIsQyxELEUsdyBib3hcbiAgICAiLCJtZXJtYWlkIjoie1xuICBcInRoZW1lXCI6IFwiZGVmYXVsdFwiXG59IiwidXBkYXRlRWRpdG9yIjpmYWxzZSwiYXV0b1N5bmMiOnRydWUsInVwZGF0ZURpYWdyYW0iOmZhbHNlfQ" click B "https://mermaid-js.github.io/mermaid-live-editor/edit/#eyJjb2RlIjoiZmxvd2NoYXJ0IExSXG4gICAgQVsxLiBVc2UgZXh0ZXJuYWw8YnI-dG9vbCB0byBjcmVhdGUvZWRpdDxicj5kaWFncmFtXVxuICAgIEJbMi4gSWYgcG9zc2libGUsIHNhdmU8YnI-ZGlhZ3JhbSBjb29yZGluYXRlczxicj5mb3IgY29udHJpYnV0b3I8YnI-YWNjZXNzXVxuICAgIENbMy4gR2VuZXJhdGUgLnN2ZyA8YnI-b3IucG5nIGZpbGU8YnI-YW5kIGRvd25sb2FkIHRvPGJyPmFwcHJvcHJpYXRlPGJyPmltYWdlcy8gZm9sZGVyXVxuICAgIHN1YmdyYXBoIHdbIF1cbiAgICBkaXJlY3Rpb24gVEJcbiAgICBEWzQuIFVzZSBmaWd1cmUgc2hvcnRjb2RlPGJyPnRvIHJlZmVyZW5jZSBzdmcgb3I8YnI-cG5nIGZpbGUgaW48YnI-cGFnZSAubWQgZmlsZV0gLS0-XG4gICAgRVs1LiBBZGQgY2FwdGlvbl1cbiAgICBlbmRcbiAgICBBIC0tPiBCXG4gICAgQiAtLT4gQ1xuICAgIEMgLS0-IHdcbiAgICBjbGFzc0RlZiBib3ggZmlsbDojZmZmLHN0cm9rZTojMDAwLHN0cm9rZS13aWR0aDoxcHgsY29sb3I6IzAwMDtcbiAgICBjbGFzcyBBLEIsQyxELEUsdyBib3hcbiAgICAiLCJtZXJtYWlkIjoie1xuICBcInRoZW1lXCI6IFwiZGVmYXVsdFwiXG59IiwidXBkYXRlRWRpdG9yIjpmYWxzZSwiYXV0b1N5bmMiOnRydWUsInVwZGF0ZURpYWdyYW0iOmZhbHNlfQ" click C "https://mermaid-js.github.io/mermaid-live-editor/edit/#eyJjb2RlIjoiZmxvd2NoYXJ0IExSXG4gICAgQVsxLiBVc2UgZXh0ZXJuYWw8YnI-dG9vbCB0byBjcmVhdGUvZWRpdDxicj5kaWFncmFtXVxuICAgIEJbMi4gSWYgcG9zc2libGUsIHNhdmU8YnI-ZGlhZ3JhbSBjb29yZGluYXRlczxicj5mb3IgY29udHJpYnV0b3I8YnI-YWNjZXNzXVxuICAgIENbMy4gR2VuZXJhdGUgLnN2ZyA8YnI-b3IucG5nIGZpbGU8YnI-YW5kIGRvd25sb2FkIHRvPGJyPmFwcHJvcHJpYXRlPGJyPmltYWdlcy8gZm9sZGVyXVxuICAgIHN1YmdyYXBoIHdbIF1cbiAgICBkaXJlY3Rpb24gVEJcbiAgICBEWzQuIFVzZSBmaWd1cmUgc2hvcnRjb2RlPGJyPnRvIHJlZmVyZW5jZSBzdmcgb3I8YnI-cG5nIGZpbGUgaW48YnI-cGFnZSAubWQgZmlsZV0gLS0-XG4gICAgRVs1LiBBZGQgY2FwdGlvbl1cbiAgICBlbmRcbiAgICBBIC0tPiBCXG4gICAgQiAtLT4gQ1xuICAgIEMgLS0-IHdcbiAgICBjbGFzc0RlZiBib3ggZmlsbDojZmZmLHN0cm9rZTojMDAwLHN0cm9rZS13aWR0aDoxcHgsY29sb3I6IzAwMDtcbiAgICBjbGFzcyBBLEIsQyxELEUsdyBib3hcbiAgICAiLCJtZXJtYWlkIjoie1xuICBcInRoZW1lXCI6IFwiZGVmYXVsdFwiXG59IiwidXBkYXRlRWRpdG9yIjpmYWxzZSwiYXV0b1N5bmMiOnRydWUsInVwZGF0ZURpYWdyYW0iOmZhbHNlfQ" click D "https://mermaid-js.github.io/mermaid-live-editor/edit/#eyJjb2RlIjoiZmxvd2NoYXJ0IExSXG4gICAgQVsxLiBVc2UgZXh0ZXJuYWw8YnI-dG9vbCB0byBjcmVhdGUvZWRpdDxicj5kaWFncmFtXVxuICAgIEJbMi4gSWYgcG9zc2libGUsIHNhdmU8YnI-ZGlhZ3JhbSBjb29yZGluYXRlczxicj5mb3IgY29udHJpYnV0b3I8YnI-YWNjZXNzXVxuICAgIENbMy4gR2VuZXJhdGUgLnN2ZyA8YnI-b3IucG5nIGZpbGU8YnI-YW5kIGRvd25sb2FkIHRvPGJyPmFwcHJvcHJpYXRlPGJyPmltYWdlcy8gZm9sZGVyXVxuICAgIHN1YmdyYXBoIHdbIF1cbiAgICBkaXJlY3Rpb24gVEJcbiAgICBEWzQuIFVzZSBmaWd1cmUgc2hvcnRjb2RlPGJyPnRvIHJlZmVyZW5jZSBzdmcgb3I8YnI-cG5nIGZpbGUgaW48YnI-cGFnZSAubWQgZmlsZV0gLS0-XG4gICAgRVs1LiBBZGQgY2FwdGlvbl1cbiAgICBlbmRcbiAgICBBIC0tPiBCXG4gICAgQiAtLT4gQ1xuICAgIEMgLS0-IHdcbiAgICBjbGFzc0RlZiBib3ggZmlsbDojZmZmLHN0cm9rZTojMDAwLHN0cm9rZS13aWR0aDoxcHgsY29sb3I6IzAwMDtcbiAgICBjbGFzcyBBLEIsQyxELEUsdyBib3hcbiAgICAiLCJtZXJtYWlkIjoie1xuICBcInRoZW1lXCI6IFwiZGVmYXVsdFwiXG59IiwidXBkYXRlRWRpdG9yIjpmYWxzZSwiYXV0b1N5bmMiOnRydWUsInVwZGF0ZURpYWdyYW0iOmZhbHNlfQ" click E "https://mermaid-js.github.io/mermaid-live-editor/edit/#eyJjb2RlIjoiZmxvd2NoYXJ0IExSXG4gICAgQVsxLiBVc2UgZXh0ZXJuYWw8YnI-dG9vbCB0byBjcmVhdGUvZWRpdDxicj5kaWFncmFtXVxuICAgIEJbMi4gSWYgcG9zc2libGUsIHNhdmU8YnI-ZGlhZ3JhbSBjb29yZGluYXRlczxicj5mb3IgY29udHJpYnV0b3I8YnI-YWNjZXNzXVxuICAgIENbMy4gR2VuZXJhdGUgLnN2ZyA8YnI-b3IucG5nIGZpbGU8YnI-YW5kIGRvd25sb2FkIHRvPGJyPmFwcHJvcHJpYXRlPGJyPmltYWdlcy8gZm9sZGVyXVxuICAgIHN1YmdyYXBoIHdbIF1cbiAgICBkaXJlY3Rpb24gVEJcbiAgICBEWzQuIFVzZSBmaWd1cmUgc2hvcnRjb2RlPGJyPnRvIHJlZmVyZW5jZSBzdmcgb3I8YnI-cG5nIGZpbGUgaW48YnI-cGFnZSAubWQgZmlsZV0gLS0-XG4gICAgRVs1LiBBZGQgY2FwdGlvbl1cbiAgICBlbmRcbiAgICBBIC0tPiBCXG4gICAgQiAtLT4gQ1xuICAgIEMgLS0-IHdcbiAgICBjbGFzc0RlZiBib3ggZmlsbDojZmZmLHN0cm9rZTojMDAwLHN0cm9rZS13aWR0aDoxcHgsY29sb3I6IzAwMDtcbiAgICBjbGFzcyBBLEIsQyxELEUsdyBib3hcbiAgICAiLCJtZXJtYWlkIjoie1xuICBcInRoZW1lXCI6IFwiZGVmYXVsdFwiXG59IiwidXBkYXRlRWRpdG9yIjpmYWxzZSwiYXV0b1N5bmMiOnRydWUsInVwZGF0ZURpYWdyYW0iOmZhbHNlfQ"

图 5. 外部工具方法步骤

使用外部工具方法来添加图表时,你要遵从的步骤如下:

  1. 使用你的外部工具来创建图表。
  2. 将图表的位置保存起来供其他贡献者访问。例如,你的工具可能提供一个指向图表的链接, 或者你可以将源码文件(例如一个 .xml 文件)放置到一个公开的仓库, 以便其他贡献者访问。
  3. 生成图表并将其下载为 .svg.png 图片文件,保存到合适的 ../images/ 目录下。
  4. 使用 {{< figure >}} 短代码从 .md 文件中引用该图表。
  5. 使用 {{< figure >}} 短代码的 caption 参数为图表设置标题。

下面是一个用于 images/apple.svg 图表的 {{< figure >}} 短代码:

{{< figure src="/static/images/apple.svg" alt="red-apple-figure" class="diagram-large" caption="图 9. 一个大红苹果" >}} 

如果你的外部绘图工具支持:

  • 你可以将多个 .svg.png 商标、图标或图片整合到你的图表中。 不过,你需要确保你查看了版权并遵守了 Kubernetes 文档关于使用第三方内容的 指南
  • 你应该将图表的源位置保存起来,以便其他贡献者访问。 例如,你的工具可能提供指向图表文件的链接,或者你应该将源代码文件 (例如一个 .xml 文件)放到某处以便其他贡献者访问。

关于 K8s 和 CNCF 商标与图片的详细信息,可参阅 CNCF Artwork

使用外部工具的好处有:

  • 贡献者对外部工具更为熟悉
  • 图表可能需要 Mermaid 所无法提供的细节

不要忘记使用本地和 Netlify 预览来检查你的图表可以正常渲染。

示例

本节给出 Mermaid 的若干样例。

示例 1 - Pod 拓扑分布约束

图 6 展示的是 Pod 拓扑分布约束 页面所出现的图表。

graph TB subgraph "zoneB" n3(Node3) n4(Node4) end subgraph "zoneA" n1(Node1) n2(Node2) end classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000; classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff; classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5; class n1,n2,n3,n4 k8s; class zoneA,zoneB cluster; click n3 "https://mermaid-js.github.io/mermaid-live-editor/edit/#eyJjb2RlIjoiZ3JhcGggVEJcbiAgICBzdWJncmFwaCBcInpvbmVCXCJcbiAgICAgICAgbjMoTm9kZTMpXG4gICAgICAgIG40KE5vZGU0KVxuICAgIGVuZFxuICAgIHN1YmdyYXBoIFwiem9uZUFcIlxuICAgICAgICBuMShOb2RlMSlcbiAgICAgICAgbjIoTm9kZTIpXG4gICAgZW5kXG5cbiAgICBjbGFzc0RlZiBwbGFpbiBmaWxsOiNkZGQsc3Ryb2tlOiNmZmYsc3Ryb2tlLXdpZHRoOjRweCxjb2xvcjojMDAwO1xuICAgIGNsYXNzRGVmIGs4cyBmaWxsOiMzMjZjZTUsc3Ryb2tlOiNmZmYsc3Ryb2tlLXdpZHRoOjRweCxjb2xvcjojZmZmO1xuICAgIGNsYXNzRGVmIGNsdXN0ZXIgZmlsbDojZmZmLHN0cm9rZTojYmJiLHN0cm9rZS13aWR0aDoycHgsY29sb3I6IzMyNmNlNTtcbiAgICBjbGFzcyBuMSxuMixuMyxuNCBrOHM7XG4gICAgY2xhc3Mgem9uZUEsem9uZUIgY2x1c3RlcjtcbiIsIm1lcm1haWQiOiJ7XG4gIFwidGhlbWVcIjogXCJkZWZhdWx0XCJcbn0iLCJ1cGRhdGVFZGl0b3IiOmZhbHNlLCJhdXRvU3luYyI6dHJ1ZSwidXBkYXRlRGlhZ3JhbSI6dHJ1ZX0" _blank click n4 "https://mermaid-js.github.io/mermaid-live-editor/edit/#eyJjb2RlIjoiZ3JhcGggVEJcbiAgICBzdWJncmFwaCBcInpvbmVCXCJcbiAgICAgICAgbjMoTm9kZTMpXG4gICAgICAgIG40KE5vZGU0KVxuICAgIGVuZFxuICAgIHN1YmdyYXBoIFwiem9uZUFcIlxuICAgICAgICBuMShOb2RlMSlcbiAgICAgICAgbjIoTm9kZTIpXG4gICAgZW5kXG5cbiAgICBjbGFzc0RlZiBwbGFpbiBmaWxsOiNkZGQsc3Ryb2tlOiNmZmYsc3Ryb2tlLXdpZHRoOjRweCxjb2xvcjojMDAwO1xuICAgIGNsYXNzRGVmIGs4cyBmaWxsOiMzMjZjZTUsc3Ryb2tlOiNmZmYsc3Ryb2tlLXdpZHRoOjRweCxjb2xvcjojZmZmO1xuICAgIGNsYXNzRGVmIGNsdXN0ZXIgZmlsbDojZmZmLHN0cm9rZTojYmJiLHN0cm9rZS13aWR0aDoycHgsY29sb3I6IzMyNmNlNTtcbiAgICBjbGFzcyBuMSxuMixuMyxuNCBrOHM7XG4gICAgY2xhc3Mgem9uZUEsem9uZUIgY2x1c3RlcjtcbiIsIm1lcm1haWQiOiJ7XG4gIFwidGhlbWVcIjogXCJkZWZhdWx0XCJcbn0iLCJ1cGRhdGVFZGl0b3IiOmZhbHNlLCJhdXRvU3luYyI6dHJ1ZSwidXBkYXRlRGlhZ3JhbSI6dHJ1ZX0" _blank click n1 "https://mermaid-js.github.io/mermaid-live-editor/edit/#eyJjb2RlIjoiZ3JhcGggVEJcbiAgICBzdWJncmFwaCBcInpvbmVCXCJcbiAgICAgICAgbjMoTm9kZTMpXG4gICAgICAgIG40KE5vZGU0KVxuICAgIGVuZFxuICAgIHN1YmdyYXBoIFwiem9uZUFcIlxuICAgICAgICBuMShOb2RlMSlcbiAgICAgICAgbjIoTm9kZTIpXG4gICAgZW5kXG5cbiAgICBjbGFzc0RlZiBwbGFpbiBmaWxsOiNkZGQsc3Ryb2tlOiNmZmYsc3Ryb2tlLXdpZHRoOjRweCxjb2xvcjojMDAwO1xuICAgIGNsYXNzRGVmIGs4cyBmaWxsOiMzMjZjZTUsc3Ryb2tlOiNmZmYsc3Ryb2tlLXdpZHRoOjRweCxjb2xvcjojZmZmO1xuICAgIGNsYXNzRGVmIGNsdXN0ZXIgZmlsbDojZmZmLHN0cm9rZTojYmJiLHN0cm9rZS13aWR0aDoycHgsY29sb3I6IzMyNmNlNTtcbiAgICBjbGFzcyBuMSxuMixuMyxuNCBrOHM7XG4gICAgY2xhc3Mgem9uZUEsem9uZUIgY2x1c3RlcjtcbiIsIm1lcm1haWQiOiJ7XG4gIFwidGhlbWVcIjogXCJkZWZhdWx0XCJcbn0iLCJ1cGRhdGVFZGl0b3IiOmZhbHNlLCJhdXRvU3luYyI6dHJ1ZSwidXBkYXRlRGlhZ3JhbSI6dHJ1ZX0" _blank click n2 "https://mermaid-js.github.io/mermaid-live-editor/edit/#eyJjb2RlIjoiZ3JhcGggVEJcbiAgICBzdWJncmFwaCBcInpvbmVCXCJcbiAgICAgICAgbjMoTm9kZTMpXG4gICAgICAgIG40KE5vZGU0KVxuICAgIGVuZFxuICAgIHN1YmdyYXBoIFwiem9uZUFcIlxuICAgICAgICBuMShOb2RlMSlcbiAgICAgICAgbjIoTm9kZTIpXG4gICAgZW5kXG5cbiAgICBjbGFzc0RlZiBwbGFpbiBmaWxsOiNkZGQsc3Ryb2tlOiNmZmYsc3Ryb2tlLXdpZHRoOjRweCxjb2xvcjojMDAwO1xuICAgIGNsYXNzRGVmIGs4cyBmaWxsOiMzMjZjZTUsc3Ryb2tlOiNmZmYsc3Ryb2tlLXdpZHRoOjRweCxjb2xvcjojZmZmO1xuICAgIGNsYXNzRGVmIGNsdXN0ZXIgZmlsbDojZmZmLHN0cm9rZTojYmJiLHN0cm9rZS13aWR0aDoycHgsY29sb3I6IzMyNmNlNTtcbiAgICBjbGFzcyBuMSxuMixuMyxuNCBrOHM7XG4gICAgY2xhc3Mgem9uZUEsem9uZUIgY2x1c3RlcjtcbiIsIm1lcm1haWQiOiJ7XG4gIFwidGhlbWVcIjogXCJkZWZhdWx0XCJcbn0iLCJ1cGRhdGVFZGl0b3IiOmZhbHNlLCJhdXRvU3luYyI6dHJ1ZSwidXBkYXRlRGlhZ3JhbSI6dHJ1ZX0" _blank

图 6. Pod 拓扑分布约束

代码块:

graph TB
   subgraph "zoneB"
       n3(Node3)
       n4(Node4)
   end
   subgraph "zoneA"
       n1(Node1)
       n2(Node2)
   end
 
   classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000;
   classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff;
   classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5;
   class n1,n2,n3,n4 k8s;
   class zoneA,zoneB cluster;

示例 2 - Ingress

图 7 显示的是 Ingress 是什么 页面所出现的图表。

graph LR; client([客户端])-. Ingress 所管理的
负载均衡器 .->ingress[Ingress]; ingress-->|路由规则|service[服务]; subgraph cluster ingress; service-->pod1[Pod]; service-->pod2[Pod]; end classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000; classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff; classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5; class ingress,service,pod1,pod2 k8s; class client plain; class cluster cluster; click client "https://mermaid-js.github.io/mermaid-live-editor/edit/#eyJjb2RlIjoiZ3JhcGggIExSXG4gIGNsaWVudChbY2xpZW50XSktLiBJbmdyZXNzLW1hbmFnZWQgPGJyPiBsb2FkIGJhbGFuY2VyIC4tPmluZ3Jlc3NbSW5ncmVzc107XG4gIGluZ3Jlc3MtLT58cm91dGluZyBydWxlfHNlcnZpY2VbU2VydmljZV07XG4gIHN1YmdyYXBoIGNsdXN0ZXJcbiAgaW5ncmVzcztcbiAgc2VydmljZS0tPnBvZDFbUG9kXTtcbiAgc2VydmljZS0tPnBvZDJbUG9kXTtcbiAgZW5kXG4gIGNsYXNzRGVmIHBsYWluIGZpbGw6I2RkZCxzdHJva2U6I2ZmZixzdHJva2Utd2lkdGg6NHB4LGNvbG9yOiMwMDA7XG4gIGNsYXNzRGVmIGs4cyBmaWxsOiMzMjZjZTUsc3Ryb2tlOiNmZmYsc3Ryb2tlLXdpZHRoOjRweCxjb2xvcjojZmZmO1xuICBjbGFzc0RlZiBjbHVzdGVyIGZpbGw6I2ZmZixzdHJva2U6I2JiYixzdHJva2Utd2lkdGg6MnB4LGNvbG9yOiMzMjZjZTU7XG4gIGNsYXNzIGluZ3Jlc3Msc2VydmljZSxwb2QxLHBvZDIgazhzO1xuICBjbGFzcyBjbGllbnQgcGxhaW47XG4gIGNsYXNzIGNsdXN0ZXIgY2x1c3RlcjtcbiIsIm1lcm1haWQiOiJ7XG4gIFwidGhlbWVcIjogXCJkZWZhdWx0XCJcbn0iLCJ1cGRhdGVFZGl0b3IiOmZhbHNlLCJhdXRvU3luYyI6dHJ1ZSwidXBkYXRlRGlhZ3JhbSI6ZmFsc2V9" _blank click ingress "https://mermaid-js.github.io/mermaid-live-editor/edit/#eyJjb2RlIjoiZ3JhcGggIExSXG4gIGNsaWVudChbY2xpZW50XSktLiBJbmdyZXNzLW1hbmFnZWQgPGJyPiBsb2FkIGJhbGFuY2VyIC4tPmluZ3Jlc3NbSW5ncmVzc107XG4gIGluZ3Jlc3MtLT58cm91dGluZyBydWxlfHNlcnZpY2VbU2VydmljZV07XG4gIHN1YmdyYXBoIGNsdXN0ZXJcbiAgaW5ncmVzcztcbiAgc2VydmljZS0tPnBvZDFbUG9kXTtcbiAgc2VydmljZS0tPnBvZDJbUG9kXTtcbiAgZW5kXG4gIGNsYXNzRGVmIHBsYWluIGZpbGw6I2RkZCxzdHJva2U6I2ZmZixzdHJva2Utd2lkdGg6NHB4LGNvbG9yOiMwMDA7XG4gIGNsYXNzRGVmIGs4cyBmaWxsOiMzMjZjZTUsc3Ryb2tlOiNmZmYsc3Ryb2tlLXdpZHRoOjRweCxjb2xvcjojZmZmO1xuICBjbGFzc0RlZiBjbHVzdGVyIGZpbGw6I2ZmZixzdHJva2U6I2JiYixzdHJva2Utd2lkdGg6MnB4LGNvbG9yOiMzMjZjZTU7XG4gIGNsYXNzIGluZ3Jlc3Msc2VydmljZSxwb2QxLHBvZDIgazhzO1xuICBjbGFzcyBjbGllbnQgcGxhaW47XG4gIGNsYXNzIGNsdXN0ZXIgY2x1c3RlcjtcbiIsIm1lcm1haWQiOiJ7XG4gIFwidGhlbWVcIjogXCJkZWZhdWx0XCJcbn0iLCJ1cGRhdGVFZGl0b3IiOmZhbHNlLCJhdXRvU3luYyI6dHJ1ZSwidXBkYXRlRGlhZ3JhbSI6ZmFsc2V9" _blank click service "https://mermaid-js.github.io/mermaid-live-editor/edit/#eyJjb2RlIjoiZ3JhcGggIExSXG4gIGNsaWVudChbY2xpZW50XSktLiBJbmdyZXNzLW1hbmFnZWQgPGJyPiBsb2FkIGJhbGFuY2VyIC4tPmluZ3Jlc3NbSW5ncmVzc107XG4gIGluZ3Jlc3MtLT58cm91dGluZyBydWxlfHNlcnZpY2VbU2VydmljZV07XG4gIHN1YmdyYXBoIGNsdXN0ZXJcbiAgaW5ncmVzcztcbiAgc2VydmljZS0tPnBvZDFbUG9kXTtcbiAgc2VydmljZS0tPnBvZDJbUG9kXTtcbiAgZW5kXG4gIGNsYXNzRGVmIHBsYWluIGZpbGw6I2RkZCxzdHJva2U6I2ZmZixzdHJva2Utd2lkdGg6NHB4LGNvbG9yOiMwMDA7XG4gIGNsYXNzRGVmIGs4cyBmaWxsOiMzMjZjZTUsc3Ryb2tlOiNmZmYsc3Ryb2tlLXdpZHRoOjRweCxjb2xvcjojZmZmO1xuICBjbGFzc0RlZiBjbHVzdGVyIGZpbGw6I2ZmZixzdHJva2U6I2JiYixzdHJva2Utd2lkdGg6MnB4LGNvbG9yOiMzMjZjZTU7XG4gIGNsYXNzIGluZ3Jlc3Msc2VydmljZSxwb2QxLHBvZDIgazhzO1xuICBjbGFzcyBjbGllbnQgcGxhaW47XG4gIGNsYXNzIGNsdXN0ZXIgY2x1c3RlcjtcbiIsIm1lcm1haWQiOiJ7XG4gIFwidGhlbWVcIjogXCJkZWZhdWx0XCJcbn0iLCJ1cGRhdGVFZGl0b3IiOmZhbHNlLCJhdXRvU3luYyI6dHJ1ZSwidXBkYXRlRGlhZ3JhbSI6ZmFsc2V9" _blank click pod1 "https://mermaid-js.github.io/mermaid-live-editor/edit/#eyJjb2RlIjoiZ3JhcGggIExSXG4gIGNsaWVudChbY2xpZW50XSktLiBJbmdyZXNzLW1hbmFnZWQgPGJyPiBsb2FkIGJhbGFuY2VyIC4tPmluZ3Jlc3NbSW5ncmVzc107XG4gIGluZ3Jlc3MtLT58cm91dGluZyBydWxlfHNlcnZpY2VbU2VydmljZV07XG4gIHN1YmdyYXBoIGNsdXN0ZXJcbiAgaW5ncmVzcztcbiAgc2VydmljZS0tPnBvZDFbUG9kXTtcbiAgc2VydmljZS0tPnBvZDJbUG9kXTtcbiAgZW5kXG4gIGNsYXNzRGVmIHBsYWluIGZpbGw6I2RkZCxzdHJva2U6I2ZmZixzdHJva2Utd2lkdGg6NHB4LGNvbG9yOiMwMDA7XG4gIGNsYXNzRGVmIGs4cyBmaWxsOiMzMjZjZTUsc3Ryb2tlOiNmZmYsc3Ryb2tlLXdpZHRoOjRweCxjb2xvcjojZmZmO1xuICBjbGFzc0RlZiBjbHVzdGVyIGZpbGw6I2ZmZixzdHJva2U6I2JiYixzdHJva2Utd2lkdGg6MnB4LGNvbG9yOiMzMjZjZTU7XG4gIGNsYXNzIGluZ3Jlc3Msc2VydmljZSxwb2QxLHBvZDIgazhzO1xuICBjbGFzcyBjbGllbnQgcGxhaW47XG4gIGNsYXNzIGNsdXN0ZXIgY2x1c3RlcjtcbiIsIm1lcm1haWQiOiJ7XG4gIFwidGhlbWVcIjogXCJkZWZhdWx0XCJcbn0iLCJ1cGRhdGVFZGl0b3IiOmZhbHNlLCJhdXRvU3luYyI6dHJ1ZSwidXBkYXRlRGlhZ3JhbSI6ZmFsc2V9" _blank click pod2 "https://mermaid-js.github.io/mermaid-live-editor/edit/#eyJjb2RlIjoiZ3JhcGggIExSXG4gIGNsaWVudChbY2xpZW50XSktLiBJbmdyZXNzLW1hbmFnZWQgPGJyPiBsb2FkIGJhbGFuY2VyIC4tPmluZ3Jlc3NbSW5ncmVzc107XG4gIGluZ3Jlc3MtLT58cm91dGluZyBydWxlfHNlcnZpY2VbU2VydmljZV07XG4gIHN1YmdyYXBoIGNsdXN0ZXJcbiAgaW5ncmVzcztcbiAgc2VydmljZS0tPnBvZDFbUG9kXTtcbiAgc2VydmljZS0tPnBvZDJbUG9kXTtcbiAgZW5kXG4gIGNsYXNzRGVmIHBsYWluIGZpbGw6I2RkZCxzdHJva2U6I2ZmZixzdHJva2Utd2lkdGg6NHB4LGNvbG9yOiMwMDA7XG4gIGNsYXNzRGVmIGs4cyBmaWxsOiMzMjZjZTUsc3Ryb2tlOiNmZmYsc3Ryb2tlLXdpZHRoOjRweCxjb2xvcjojZmZmO1xuICBjbGFzc0RlZiBjbHVzdGVyIGZpbGw6I2ZmZixzdHJva2U6I2JiYixzdHJva2Utd2lkdGg6MnB4LGNvbG9yOiMzMjZjZTU7XG4gIGNsYXNzIGluZ3Jlc3Msc2VydmljZSxwb2QxLHBvZDIgazhzO1xuICBjbGFzcyBjbGllbnQgcGxhaW47XG4gIGNsYXNzIGNsdXN0ZXIgY2x1c3RlcjtcbiIsIm1lcm1haWQiOiJ7XG4gIFwidGhlbWVcIjogXCJkZWZhdWx0XCJcbn0iLCJ1cGRhdGVFZGl0b3IiOmZhbHNlLCJhdXRvU3luYyI6dHJ1ZSwidXBkYXRlRGlhZ3JhbSI6ZmFsc2V9" _blank

图 7. Ingress

代码块:

graph LR;
 client([客户端])-. Ingress 所管理的<br>负载均衡器 .->ingress[Ingress];
 ingress-->|路由规则|service[服务];
 subgraph cluster
 ingress;
 service-->pod1[Pod];
 service-->pod2[Pod];
 end
 classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000;
 classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff;
 classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5;
 class ingress,service,pod1,pod2 k8s;
 class client plain;
 class cluster cluster;

示例 3 - K8s 系统流程

图 8 给出的是一个 Mermaid 时序图,展示启动容器时 K8s 组件间的控制流。

K8s system flow diagram

图 8. K8s 系统流程图

代码段:

%%{init:{"theme":"neutral"}}%%
sequenceDiagram
    actor me
    participant apiSrv as 控制面<br><br>api-server
    participant etcd as 控制面<br><br>etcd 数据存储
    participant cntrlMgr as 控制面<br><br>控制器管理器
    participant sched as 控制面<br><br>调度器
    participant kubelet as 节点<br><br>kubelet
    participant container as 节点<br><br>容器运行时
    me->>apiSrv: 1. kubectl create -f pod.yaml
    apiSrv-->>etcd: 2. 保存新状态
    cntrlMgr->>apiSrv: 3. 检查变更
    sched->>apiSrv: 4. 监视未分派的 Pod(s)
    apiSrv->>sched: 5. 通知 nodename=" " 的 Pod
    sched->>apiSrv: 6. 指派 Pod 到节点
    apiSrv-->>etcd: 7. 保存新状态
    kubelet->>apiSrv: 8. 查询新指派的 Pod(s)
    apiSrv->>kubelet: 9. 将 Pod 绑定到节点
    kubelet->>container: 10. 启动容器
    kubelet->>apiSrv: 11. 更新 Pod 状态
    apiSrv-->>etcd: 12. 保存新状态

如何设置图表样式

你可以使用大家都熟悉的 CSS 术语来为一个或多个图表元素设置渲染样式。 你可以在 Mermaid 代码中使用两种类型的语句来完成这一工作:

  • classDef 定义一类样式属性;
  • class 指定 class 所适用的一种或多种元素。

图 7 中,你可以看到这两种示例。

classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff; // 定义 k8s 类的样式
class ingress,service,pod1,pod2 k8s; // k8s 类会应用到 ingress、service、pod1 和 pod2 这些元素之上

你可以在你的图表中包含一个或多个 classDefclass 语句。 你也可以在你的图表中为 k8s 组件使用官方的 K8s #326ce5 十六进制颜色代码。

关于样式设置和类的更多信息,可参阅 Mermaid Styling and classes docs

如何使用标题

标题用来为图表提供简要的描述。标题或短描述都可以作为图表标题。 标题不是用来替换你在文档中要提供的解释性文字。 相反,它们是用来在文字与图表之间建立“语境连接”的。

将一些文字和带标题的图表组合到一起,可以为你所想要向用户传递的信息提供一种更为精确的表达。

没有标题的话,用户就必须在图表前后的文字中来回阅读,从而了解其含义。 这会让用户感觉到很沮丧。

图 9 给出合适的标题所需要具备的三要素:图表、图表标题和图表引用。

flowchart A[图表本身

内嵌 Mermaid 或
SVG 图片文件] B[图表标题

添加图表编号和
标题文字] C[图表引用

在文字中用图表
编号引用图表] classDef box fill:#fff,stroke:#000,stroke-width:1px,color:#000; class A,B,C box click A "https://mermaid-js.github.io/mermaid-live-editor/edit#eyJjb2RlIjoiZmxvd2NoYXJ0XG4gICAgQVtEaWFncmFtPGJyPjxicj5JbmxpbmUgTWVybWFpZCBvcjxicj5TVkcgaW1hZ2UgZmlsZXNdXG4gICAgQltEaWFncmFtIENhcHRpb248YnI-PGJyPkFkZCBGaWd1cmUgTnVtYmVyLiBhbmQ8YnI-Q2FwdGlvbiBUZXh0XVxuICAgIENbRGlhZ3JhbSBSZWZlcnJhbDxicj48YnI-UmVmZXJlbmVuY2UgRmlndXJlIE51bWJlcjxicj5pbiB0ZXh0XVxuXG4gICAgY2xhc3NEZWYgYm94IGZpbGw6I2ZmZixzdHJva2U6IzAwMCxzdHJva2Utd2lkdGg6MXB4LGNvbG9yOiMwMDA7XG4gICAgY2xhc3MgQSxCLEMgYm94IiwibWVybWFpZCI6IntcbiAgXCJ0aGVtZVwiOiBcImRlZmF1bHRcIlxufSIsInVwZGF0ZUVkaXRvciI6ZmFsc2UsImF1dG9TeW5jIjp0cnVlLCJ1cGRhdGVEaWFncmFtIjpmYWxzZX0" _blank click B "https://mermaid-js.github.io/mermaid-live-editor/edit#eyJjb2RlIjoiZmxvd2NoYXJ0XG4gICAgQVtEaWFncmFtPGJyPjxicj5JbmxpbmUgTWVybWFpZCBvcjxicj5TVkcgaW1hZ2UgZmlsZXNdXG4gICAgQltEaWFncmFtIENhcHRpb248YnI-PGJyPkFkZCBGaWd1cmUgTnVtYmVyLiBhbmQ8YnI-Q2FwdGlvbiBUZXh0XVxuICAgIENbRGlhZ3JhbSBSZWZlcnJhbDxicj48YnI-UmVmZXJlbmVuY2UgRmlndXJlIE51bWJlcjxicj5pbiB0ZXh0XVxuXG4gICAgY2xhc3NEZWYgYm94IGZpbGw6I2ZmZixzdHJva2U6IzAwMCxzdHJva2Utd2lkdGg6MXB4LGNvbG9yOiMwMDA7XG4gICAgY2xhc3MgQSxCLEMgYm94IiwibWVybWFpZCI6IntcbiAgXCJ0aGVtZVwiOiBcImRlZmF1bHRcIlxufSIsInVwZGF0ZUVkaXRvciI6ZmFsc2UsImF1dG9TeW5jIjp0cnVlLCJ1cGRhdGVEaWFncmFtIjpmYWxzZX0" _blank click C "https://mermaid-js.github.io/mermaid-live-editor/edit#eyJjb2RlIjoiZmxvd2NoYXJ0XG4gICAgQVtEaWFncmFtPGJyPjxicj5JbmxpbmUgTWVybWFpZCBvcjxicj5TVkcgaW1hZ2UgZmlsZXNdXG4gICAgQltEaWFncmFtIENhcHRpb248YnI-PGJyPkFkZCBGaWd1cmUgTnVtYmVyLiBhbmQ8YnI-Q2FwdGlvbiBUZXh0XVxuICAgIENbRGlhZ3JhbSBSZWZlcnJhbDxicj48YnI-UmVmZXJlbmVuY2UgRmlndXJlIE51bWJlcjxicj5pbiB0ZXh0XVxuXG4gICAgY2xhc3NEZWYgYm94IGZpbGw6I2ZmZixzdHJva2U6IzAwMCxzdHJva2Utd2lkdGg6MXB4LGNvbG9yOiMwMDA7XG4gICAgY2xhc3MgQSxCLEMgYm94IiwibWVybWFpZCI6IntcbiAgXCJ0aGVtZVwiOiBcImRlZmF1bHRcIlxufSIsInVwZGF0ZUVkaXRvciI6ZmFsc2UsImF1dG9TeW5jIjp0cnVlLCJ1cGRhdGVEaWFncmFtIjpmYWxzZX0" _blank

图 9. 标题要素

图表本身

Mermaid+SVG 和外部工具方法都会生成 .svg 图片文件。

下面的 {{< figure >}} 短代码是针对定义在保存于 /images/docs/components-of-kubernetes.svg 中的 .svg 图片文件的。

{{< figure src="/images/docs/components-of-kubernetes.svg" alt="运行于集群中的 Kubernetes Pod" class="diagram-large" caption="图 4. Kubernetes 结构组件" >}}

你需要将 srcaltclasscaption 取值传递给 {{< figure >}} 短代码。 你可以使用 diagram-largediagram-mediumdiagram-small 类来调整图表的尺寸。

关于创建图表的不同方法,可参阅创建图表的方法

图表标题

接下来,添加图表标题。

如果你使用 .svg 图片文件来定义你的图表,你就需要使用 {{< figure >}} 短代码的 caption 参数。

{{< figure src="/images/docs/components-of-kubernetes.svg" alt="运行于集群中的 Kubernetes Pod" class="diagram-large" caption="图 4. Kubernetes 架构组件" >}}

如果你使用内嵌的 Mermaid 代码来定义图表,则你需要使用 Markdown 文本来添加标题。

图 4. Kubernetes 架构组件

添加图表标题时需要考虑的问题:

  • 使用 {{< figure >}} 短代码来为 Mermaid+SVG 和外部工具方法制作的图表添加标题。
  • 对于内嵌方法制作的图表,使用简单的 Markdown 文本来为其添加标题。
  • 在你的图表标题前面添加 图 <编号>.。你必须使用 字样, 并且编号必须对于文档页面中所有图表而言唯一。 在编号之后添加一个英文句号。
  • 将图表标题添加到 图 <编号>. 之后,并且在同一行。 你必须为图表标题添加英文句点作为其结束标志。尽量保持标题文字简短。
  • 图表标题要放在图表之后

图表引用

最后,你可以添加图表引用。图表引用位于你的文档正文中,并且应该出现在图表之前。 这样,用户可以将你的文字与对应的图表关联起来。引用时所给的 图 <编号> 部分要与图表标题中对应部分一致。

你要避免使用空间上的相对引用,例如 ..下面的图片.. 或者 ..下面的图形..

以下是一个参考引用的示例。

图 10 展示的是 Kubernetes 体系结构。其控制面 ...

图表引用是可选的,在有些场合中,添加这类引用是不合适的。 如果你不是很确定,可以在文字中添加图表引用,以判断是否看起来、读起来比较通顺。 如果仍然不确定,可以使用图表引用。

完整全图

图 10 展示的是一个 Kubernetes 架构图表,其中包含了图表本身、图表标题和图表引用。 这里的 {{< figure >}} 短代码负责渲染图表,添加标题并包含可选的 link 参数,以便于你为图表提供超级链接。图表引用也被包含在段落中。

下面是针对此图表的 {{< figure >}} 短代码。

{{< figure src="/images/docs/components-of-kubernetes.svg" alt="运行在集群中的 Kubernetes Pod" class="diagram-large" caption="图 10. Kubernetes 架构" link="https://kubernetes.io/zh-cn/docs/concepts/overview/components/" >}}
运行在集群中的 Kubernetes Pod

图 10. Kubernetes 架构

提示

  • 总是使用在线编辑器来创建和编辑你的图表。

  • 总是使用 Hugo 本地和 Netlify 预览来检查图表在文档中的渲染效果。

  • 提供图表源代码指针,例如 URL、源代码位置或者标明代码时是说明的。

  • 总是提供图表标题。

  • 在问题报告或 PR 中包含 .svg.png 图片与/或 Mermaid 代码会很有帮助。

  • 对于 Mermaid+SVG 方法和外部工具方法而言,尽量使用 .svg 图片文件, 因为这类文件在被放大之后仍能清晰地显示。

  • 对于 .svg 文件的最佳实践是将其加载到一个 SVG 编辑工具中,并使用 “将文字转换为路径”功能完成转换。

  • Mermaid 不支持额外的图表或艺术形式。

  • Hugo Mermaid 短代码在在线编辑器中无法显示。

  • 如果想要在在线编辑器中更改图表,你必须保存它以便为图表生成新的 URL。

  • 点击本节中的图表,你可以查看其源代码及其在在线编辑器中的渲染效果。

  • 查看本页的源代码,diagram-guide.md 文件,可以将其作为示例。

  • 查阅 Mermaid 文档以获得更多的解释和示例。

最重要的一点,保持图表简单。 这样做会节省你和其他贡献者的时间,同时也会方便新的以及有经验的用户阅读。

7.8.4 - 撰写新主题

本页面展示如何为 Kubernetes 文档库创建新主题。

准备开始

发起 PR中所述,创建 Kubernetes 文档库的派生副本。

选择页面类型

当你准备编写一个新的主题时,考虑一下最适合你的内容的页面类型:

选择页面类型的说明
类型描述
概念(Concept)概念页面负责解释 Kubernetes 的某方面。例如,概念页面可以描述 Kubernetes Deployment 对象,并解释当部署、扩展和更新时,它作为应用程序所扮演的角色。一般来说,概念页面不包括步骤序列,而是提供任务或教程的链接。概念主题的示例可参见 节点
任务(Task)任务页面展示如何完成特定任务。其目的是给读者提供一系列的步骤,让他们在阅读时可以实际执行。任务页面可长可短,前提是它始终围绕着某个主题展开。在任务页面中,可以将简短的解释与要执行的步骤混合在一起。如果需要提供较长的解释,则应在概念主题中进行。相关联的任务和概念主题应该相互链接。一个简短的任务页面的实例可参见 配置 Pod 使用卷存储。一个较长的任务页面的实例可参见 配置活跃性和就绪性探针
教程(Tutorial)教程页面展示如何实现某个目标,该目标将若干 Kubernetes 功能特性联系在一起。教程可能提供一些步骤序列,读者可以在阅读页面时实际执行这些步骤。或者它可以提供相关代码片段的解释。例如,教程可以提供代码示例的讲解。教程可以包括对 Kubernetes 几个关联特性的简要解释,但有关更深入的特性解释应该链接到相关概念主题。

创建一个新页面

为每个新页面选择其内容类型。 文档站提供了模板或 Hugo Archetypes 来创建新的内容页面。 要创建新类型的页面,请使用要创建的文件的路径,运行 hugo new 命令。例如:

hugo new docs/concepts/my-first-concept.md

选择标题和文件名

选择一个标题,确保其中包含希望搜索引擎发现的关键字。 确定文件名时请使用标题中的单词,由连字符分隔。 例如,标题为使用 HTTP 代理访问 Kubernetes API 的主题的文件名为 http-proxy-access-api.md。 你不需要在文件名中加上 "kubernetes",因为 "kubernetes" 已经在主题的 URL 中了, 例如:

   /docs/tasks/extend-kubernetes/http-proxy-access-api/

在页面前言中添加主题标题

在你的主题中,在前言(front-matter) 中设置一个 title 字段。 前言是位于页面顶部三条虚线之间的 YAML 块。下面是一个例子:

---
title: 使用 HTTP 代理访问 Kubernetes API
---

选择目录

根据页面类型,将新文件放入其中一个子目录中:

  • /content/en/docs/tasks/
  • /content/en/docs/tutorials/
  • /content/en/docs/concepts/

你可以将文件放在现有的子目录中,也可以创建一个新的子目录。

将主题放在目录中

目录是使用文档源的目录结构动态构建的。 /content/en/docs/ 下的顶层目录用于创建顶层导航条目, 这些目录和它们的子目录在网站目录中都有对应条目。

每个子目录都有一个 _index.md 文件,它表示的是该子目录内容的主页面。 _index.md 文件不需要模板。它可以包含各子目录中主题的概述内容。

默认情况下,目录中的其他文件按字母顺序排序。这一般不是最好的顺序。 要控制子目录中主题的相对排序,请将页面头部的键 weight: 设置为整数值。 通常我们使用 10 的倍数,添加后续主题时 weight 值递增。 例如,weight10 的主题将位于 weight20 的主题之前。

在主题中嵌入代码

如果你想在主题中嵌入一些代码,可以直接使用 Markdown 代码块语法将代码嵌入到文件中。 建议在以下场合(并非详尽列表)使用嵌入代码:

  • 代码显示来自命令的输出,例如 kubectl get deploy mydeployment -o json | jq '.status'
  • 代码不够通用,用户无法验证。例如,你可以嵌入 YAML 文件来创建一个依赖于特定 FlexVolume 实现的 Pod。
  • 该代码是一个不完整的示例,因为其目的是突出展现某个大文件中的部分内容。 例如,在描述 RoleBinding 的方法时,你可以在主题文件中直接提供一个短的代码段。
  • 由于某些其他原因,该代码不适合用户验证。 例如,当使用 kubectl edit 命令描述如何将新属性添加到资源时, 你可以提供一个仅包含要添加的属性的简短示例。

引用来自其他文件的代码

在主题中引用代码的另一种方法是创建一个新的、完整的示例文件(或文件组), 然后在主题中引用这些示例。当示例是通用的和可重用的,并且你希望读者自己验证时, 使用此方法引用示例 YAML 文件。

添加新的独立示例文件(如 YAML 文件)时,将代码放在 <LANG>/examples/ 的某个子目录中, 其中 <LANG> 是该主题的语言。在主题文件中使用 code_sample 短代码:

{{% code_sample file="<RELPATH>/my-example-yaml>" %}}

<RELPATH> 是要引用的文件的路径,相对于 examples 目录。以下 Hugo 短代码引用了位于 /content/en/examples/pods/storage/gce-volume.yaml 的 YAML 文件。

{{% code_sample file="pods/storage/gce-volume.yaml" %}}

显示如何从配置文件创建 API 对象

如果需要演示如何基于配置文件创建 API 对象,请将配置文件放在 <LANG>/examples 下的某个子目录中。

在主题中展示以下命令:

kubectl create -f https://k8s.io/examples/pods/storage/gce-volume.yaml

有关使用此技术的主题的示例, 请参见运行单实例有状态的应用

向主题添加图片

将图片文件放入 /images 目录。首选的图片格式是 SVG。

接下来

7.8.5 - 页面内容类型

Kubernetes 文档包含以下几种页面内容类型:

  • 概念(Concept)
  • 任务(Task)
  • 教程(Tutorial)
  • 参考(Reference)

内容章节

每种页面内容类型都有一些使用 Markdown 注释和 HTML 标题定义的章节。 你可以使用 heading 短代码将内容标题添加到你的页面中。 注释和标题有助于维护对应页面内容类型的结构组织。

定义页面内容章节的 Markdown 注释示例:

<!-- overview -->
<!-- body -->

要在内容页面中创建通用的标题,可以使用 heading 短代码加上标题字符串。

标题字符串示例:

  • whatsnext
  • prerequisites
  • objectives
  • cleanup
  • synopsis
  • seealso
  • options

例如,要创建一个 whatsnext 标题,添加 heading 短代码并指定 "whatsnext" 字符串:

## {{% heading "whatsnext" %}}

你可以像下面这样声明一个 prerequisites 标题:

## {{% heading "prerequisites" %}}

短代码 heading 需要一个字符串参数。 该字符串参数要与 i18n/<语言>.toml 文件中以其为前缀的某个变量匹配。 例如:

i18n/en.toml:

[whatsnext_heading]
other = "What's next"

i18n/ko.toml:

[whatsnext_heading]
other = "다음 내용"

内容类型

每种内容类型都非正式地定义了期望的页面结构组织。 请按照所建议的页面章节来创建内容页面。

概念

概念页面用来解释 Kubernetes 的某些方面。例如,概念页面可以用来描述 Kubernetes 中的 Deployment 对象,解释其作为应用的角色如何部署、扩缩和更新。 通常,概念页面不需要包含步骤序列,但包含指向任务或教程的链接。

要编写一个新的概念页面,在 /content/en/docs/concepts 目录下面的子目录中新建一个 Markdown 文件。 该文件具有以下特点。

概念页面分为三个章节:

页面章节
overview(概述)
body(正文)
whatsnext(接下来)

其中的 overviewbody 章节在概念页面中显示为注释。 你可以使用 heading 短代码向页面添加 wahtsnext 节。

在为每个章节撰写内容时,遵从一些规定:

  • 使用二级和三级标题(H2、H3)来组织内容;
  • overview 节中,使用一段文字概括当前主题的上下文;
  • body 节中,详细解释对应概念;
  • 对于 whatsnext 节,提供一个项目符号列表(最多 5 个),帮助读者进一步学习掌握概念。

注解页面是一个已经上线的概念页面的例子。

任务(Task)

任务页面讲解如何完成某项工作,通常包含由为数不多的几个步骤组成的序列。 任务页面的讲解文字很少,不过通常会包含指向概念主题的链接,以便读者能够了解相关的背景和知识。

编写新的任务页面时,在 /content/en/docs/tasks 目录下的子目录中创建一个新的 Markdown 文件。 该文件特点如下。

页面章节
overview(概述)
prerequisites(准备工作)
steps(步骤)
discussion(讨论)
whatsnext(接下来)

其中的 overviewstepsdiscussion 节在任务页面中显示为注释。 你可以使用 heading 短代码添加 prerequisiteswhatsnext 小节。

在每个小节内撰写内容时注意以下规定:

  • 最低使用二级标题(H2,标题行前带两个 # 字符)。每个小节都会由模板自动给出标题。
  • overview 节中,用一个段落为整个任务主题设定语境;
  • prerequisites 节中,尽可能使用项目符号列表。 额外的环境准备条件要加在 include 短代码之后。 默认的环境准备条件是拥有一个在运行的 Kubernetes 集群。
  • steps 节中,使用编号符号列表。
  • discussion 节中,使用正常文字内容来对 steps 节中内容展开叙述。
  • whatsnext 节中,使用项目符号列表(不超过 5 项),列举读者可能接下来有兴趣阅读的主题。

已上线的任务主题示例之一是使用 HTTP 代理访问 Kubernetes API

教程(Tutorial)

教程页面描述如果完成一个比单一任务规模更大的目标。通常教程页面会有多个小节, 每个小节由一系列步骤组成。例如,每个教程可能提供对代码示例的讲解, 便于用户了解 Kubernetes 的某个功能特性。教程可以包含表面层面的概念解释, 对于更深层面的概念主题应该使用链接。

撰写新的教程页面时,在 /content/en/docs/tutorials 目录下面的子目录中创建新的 Markdown 文件。该文件有以下特点。

页面节区
overview(概述)
prerequisites(环境准备)
objectives(目标)
lessoncontent(教程内容)
cleanup(清理工作)
whatsnext(接下来)

教程页面的 overviewobjectiveslessoncontent 小节显示为注释形式。 你可以使用 heading 短代码根据需要添加 prerequisitescleanupwhatsnext 小节。

在每个小节中编写内容时,请注意以下规定:

  • 最低使用二级标题(H2,标题前面有两个 # 字符)。模板会自动为每个小节设置标题。
  • overview 节中,用一个段落为整个主题设定语境;
  • prerequisites 节中,尽可能使用项目符号列表。 额外的环境准备条件要加在已包含的条件之后。
  • objectives 节中,使用项目符号列表。
  • lessoncontent 节中,结合使用编号符号列表和叙述性文字。
  • cleanup 节中,使用编号符号列表来描述任务结束后清理集群状态所需要的步骤。
  • whatsnext 节中,使用项目符号列表(不超过 5 项),列举读者可能接下来有兴趣阅读的主题。

已发布的教程主题的一个例子是 使用 Deployment 运行无状态应用.

参考(Reference)

组件工具的参考页面给出的是某个 Kubernetes 组件工具的描述和参数选项输出。 每个页面都是使用组件工具命令基于脚本生成的。

每个工具参考页面可能包含以下小节:

页面小节
synopsis(用法)
options(选项)
options from parent commands(从父命令集成的选项)
examples(示例)
seealso(参考)

已发布的工具参考页面示例包括:

接下来

7.8.6 - 内容组织

本网站使用了 Hugo。在 Hugo 中,内容组织是一个核心概念。

页面列表

页面顺序

文档侧方菜单、文档页面浏览器等以 Hugo 的默认排序顺序列出。Hugo 会按照权重(从 1 开始)、 日期(最新的排最前面)排序,最后按链接标题排序。

有鉴于此,如果你想将一个页面或一个章节前移,请在页面头部设置一个较高的权重:

title: My Page
weight: 10

文档主菜单

文档主菜单是从 docs/ 下面的章节构建的。 这些章节在其章节内容文件 _index.md 的头部设置了 main_menu 标志:

main_menu: true

注意,链接标题来自页面的 linkTitle 字段,因此如果希望它与页面标题不同,请在内容文件中更改它:

main_menu: true
title: Page Title
linkTitle: Title used in links

文档侧方菜单

文档侧方菜单是基于 docs/ 下面的当前章节的内容树构建的。

菜单默认显示所有的章节和它们的页面。

如果你不想列出某个章节或页面,请在页面头部将 toc_hide 标志设置为 true

toc_hide: true

当导航到具有内容的章节时,网站将显示出指定的章节或页面(例如 _index.md)。 否则,将显示该章节里的第一个页面。

文档浏览器

文档主页上的页面浏览器是基于 docs section 下一层的所有章节和页面构建的。

如果你不想列出某个章节或页面,请在页面头部将 toc_hide 标志设置为 true

toc_hide: true

主菜单

右上菜单中的网站链接(也出现在页脚中)是通过页面查找构建的。 这是为了确保页面实际存在。因此,如果案例分析章节在网站(或者其本地化版本)中不存在, 则不会出现对应的链接。

页面包

除了独立的内容页面(Markdown 文件), Hugo 还支持页面包

一个例子是定制的 Hugo 短代码(shortcodes)。 它被认为是 leaf bundle(叶子包)。 目录下的所有内容,包括 index.md,都是包的一部分。此外还包括页面间相对链接、可被处理的图像等:

zh-cn/docs/home/contribute/includes
├── example1.md
├── example2.md
├── index.md
└── podtemplate.json

另一个广泛使用的例子是 includes 包。 这类包在页面头部设置 headless: true,意味着它没有得到自己的 URL。它只用于其他页面。

zh-cn/includes
├── default-storage-class-prereqs.md
├── index.md
├── partner-script.js
├── partner-style.css
├── task-tutorial-prereqs.md
├── user-guide-content-moved.md
└── user-guide-migration-notice.md

有关包中文件的一些重要说明:

  • 已翻译的包会从上面的语言继承所有缺失的、非内容文件。这一设计可以避免重复。
  • 包中的所有文件都是 Hugo 所指的 Resources,即使某个语言不支持头部设置(YAML 文件等), 你也可以为每个语言提供诸如参数和标题等元数据。 参见页面资源元数据
  • Resource.RelPermalink 中获得的值是相对于当前页面的。 参见 Permalinks

样式

本网站的样式表的 SASS 源文件存放在 src/sass 下面, 并通过 Hugo 自动构建。

接下来

7.8.7 - 定制 Hugo 短代码

本页面将介绍 Hugo 自定义短代码,可以用于 Kubernetes Markdown 文档书写。

关于短代码的更多信息可参见 Hugo 文档

功能状态

在本站的 Markdown 页面(.md 文件)中,你可以加入短代码来展示所描述的功能特性的版本和状态。

功能状态示例

下面是一个功能状态代码段的演示,表明这个功能已经在最新版 Kubernetes 中稳定了。

{{< feature-state state="stable" >}}

会转换为:

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

state 的可选值如下:

  • alpha
  • beta
  • deprecated
  • stable

功能状态代码

所显示的 Kubernetes 默认为该页或站点版本。 修改 for_k8s_version 短代码参数可以调整要显示的版本。例如:

{{< feature-state for_k8s_version="v1.10" state="beta" >}}

会转换为:

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

从描述文件中检索特征状态

要动态确定特性的状态,请使用 feature_gate_name 短代码参数,此参数将从 content/en/docs/reference/command-line-tools-reference/feature-gates/ 中相应的特性门控描述文件中提取特性的详细状态信息。

例如:

{{< feature-state feature_gate_name="NodeSwap" >}}

会转换为:

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

特性门控介绍

在此站点上的 Markdown 页面(.md 文件)中,你可以添加短代码来显示短代码的描述。

特性门控 Demo 演示

下面是特性状态片段的 Demo,它显示该特性在最新的 Kubernetes 版本中处于稳定状态。

{{< feature-gate-description name="DryRun" >}}

渲染到:

DryRun:

启用在服务器端对请求进行 试运行(Dry Run), 以便在不提交的情况下测试验证、合并和变更。

词汇

有两种词汇表提示:glossary_tooltipglossary_definition

你可以通过加入术语词汇的短代码,来自动更新和替换相应链接中的内容 (我们的词汇库) 在浏览在线文档时,术语会显示为超链接的样式,当鼠标移到术语上时,其解释就会显示在提示框中。

除了包含工具提示外,你还可以重用页面内容中词汇表中的定义。

词汇术语的原始数据保存在词汇目录, 每个内容文件对应相应的术语解释。

词汇演示

例如下面的代码在 Markdown 中将会转换为 cluster,然后在提示框中显示。

{{< glossary_tooltip text="cluster" term_id="cluster" >}}

这是一个简短的词汇表定义:

{{< glossary_definition prepend="A cluster is" term_id="cluster" length="short" >}}

呈现为:

A cluster is 一组工作机器,称为节点, 会运行容器化应用程序。每个集群至少有一个工作节点。

你也可以包括完整的定义:

{{< glossary_definition term_id="cluster" length="all" >}}

呈现为:

一组工作机器,称为节点, 会运行容器化应用程序。每个集群至少有一个工作节点。

工作节点会托管 Pod,而 Pod 就是作为应用负载的组件。 控制平面管理集群中的工作节点和 Pod。 在生产环境中,控制平面通常跨多台计算机运行, 一个集群通常运行多个节点,提供容错性和高可用性。

你可以使用 api-reference 短代码链接到 Kubernetes API 参考页面,例如: Pod Pod 参考文件:

{{< api-reference page="workload-resources/pod-v1" >}}

本语句中 page 参数的内容是 API 参考页面的 URL 后缀。

你可以通过指定 anchor 参数链接到页面中的特定位置,例如到 PodSpec 参考,或页面的 environment-variables 部分:

{{< api-reference page="workload-resources/pod-v1" anchor="PodSpec" >}}
{{< api-reference page="workload-resources/pod-v1" anchor="environment-variables" >}}

你可以通过指定 text 参数来更改链接的文本, 例如通过链接到页面的 环境变量部分:

{{< api-reference page="workload-resources/pod-v1" anchor="environment-variables" text="环境变量" >}}

表格标题

通过添加表格标题,你可以让表格能够被屏幕阅读器读取。 要向表格添加标题(Caption), 可用 table 短代码包围表格定义,并使用 caption 参数给出表格标题。

下面是一个例子:

{{< table caption="配置参数" >}}
参数      | 描述        | 默认值
:---------|:------------|:-------
`timeout` | 请求的超时时长 | `30s`
`logLevel` | 日志输出的级别 | `INFO`
{{< /table >}}

所渲染的表格如下:

配置参数
参数描述默认值
timeout请求的超时时长30s
logLevel日志输出的级别INFO

如果你查看表格的 HTML 输出结果,你会看到 <table> 元素后面紧接着下面的元素:

<caption style="display: none;">配置参数</caption>

标签页

在本站的 Markdown 页面(.md 文件)中,你可以加入一个标签页集来显示 某解决方案的不同形式。

标签页的短代码包含以下参数:

  • name: 标签页上显示的名字。
  • codelang: 如果要在 tab 短代码中加入内部内容,需要告知 Hugo 使用的是什么代码语言,方便代码高亮。
  • include: 标签页中所要包含的文件。如果标签页是在 Hugo 的 叶子包中定义, Hugo 会在包内查找文件(可以是 Hugo 所支持的任何 MIME 类型文件)。 否则,Hugo 会在当前路径的相对路径下查找所要包含的内容页面。 注意,在 include 页面中不能包含短代码内容,必须要使用自结束(self-closing)语法。 例如 {{< tab name="Content File #1" include="example1" />}}。 如果没有在 codelang 进行声明的话,Hugo 会根据文件名推测所用的语言。 默认情况下,非内容文件将会被代码高亮。
  • 如果内部内容是 Markdown,你必须要使用 % 分隔符来包装标签页。 例如,{{% tab name="Tab 1" %}}This is **markdown**{{% /tab %}}
  • 可以在标签页集中混合使用上面的各种变形。

下面是标签页短代码的示例。

标签页演示:代码高亮

{{< tabs name="tab_with_code" >}}
{{< tab name="Tab 1" codelang="bash" >}}
echo "This is tab 1."
{{< /tab >}}
{{< tab name="Tab 2" codelang="go" >}}
println "This is tab 2."
{{< /tab >}}
{{< /tabs >}}

会转换为:


echo "This is tab 1."


println "This is tab 2."

标签页演示:内联 Markdown 和 HTML

{{< tabs name="tab_with_md" >}}
{{% tab name="Markdown" %}}
这是**一些 markdown。**
{{< note >}}
它甚至可以包含短代码。
{{< /note >}}
{{% /tab %}}
{{< tab name="HTML" >}}
<div>
	<h3>纯 HTML</h3>
	<p>这是一些 <i>纯</i> HTML。</p>
</div>
{{< /tab >}}
{{< /tabs >}}

会转换为:

这是一些 markdown。

纯 HTML

这是一些 HTML。

标签页演示:文件嵌套

{{< tabs name="tab_with_file_include" >}}
{{< tab name="Content File #1" include="example1" />}}
{{< tab name="Content File #2" include="example2" />}}
{{< tab name="JSON File" include="podtemplate" />}}
{{< /tabs >}}

会转换为:

这是一个内容文件示例,位于一个includes叶子包中。

这是另一个内容文件示例,位于一个includes叶子包中。

  {
    "apiVersion": "v1",
    "kind": "PodTemplate",
    "metadata": {
      "name": "nginx"
    },
    "template": {
      "metadata": {
        "labels": {
          "name": "nginx"
        },
        "generateName": "nginx-"
      },
      "spec": {
         "containers": [{
           "name": "nginx",
           "image": "dockerfile/nginx",
           "ports": [{"containerPort": 80}]
         }]
      }
    }
  }

源代码文件

你可以使用 {{% code_sample %}} 短代码将文件内容嵌入代码块中, 以允许用户下载或复制其内容到他们的剪贴板。 当示例文件的内容是通用的、可复用的,并且希望用户自己尝试使用示例文件时, 可以使用此短代码。

这个短代码有两个命名参数:languagefile, 必选参数 file 用于指定要显示的文件的路径, 可选参数 language 用于指定文件的编程语言。 如果未提供 language 参数,短代码将尝试根据文件扩展名推测编程语言。

{{% code_sample language="yaml" file="application/deployment-scale.yaml" %}}

输出是:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 4 # 将副本数从 2 更新为 4
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.16.1
        ports:
        - containerPort: 80

添加新的示例文件(例如 YAML 文件)时,在 <LANG>/examples/ 子目录之一中创建该文件,其中 <LANG> 是页面的语言。 在你的页面的 Markdown 文本中,使用 code 短代码:

{{% code_sample file="<RELATIVE-PATH>/example-yaml>" %}}

其中 <RELATIVE-PATH> 是要包含的示例文件的路径,相对于 examples 目录。 以下短代码引用位于 /content/en/examples/configmap/configmaps.yaml 的 YAML 文件。

{{% code_sample file="configmap/configmaps.yaml" %}}

传统的 {{% codenew %}} 短代码将被替换为 {{% code_sample %}}。 在新文档中使用 {{% code_sample %}}

第三方内容标记

运行 Kubernetes 需要第三方软件。例如:你通常需要将 DNS 服务器 添加到集群中,以便名称解析工作。

当我们链接到第三方软件或以其他方式提及它时,我们会遵循内容指南 并标记这些第三方项目。

使用这些短代码会向使用它们的任何文档页面添加免责声明。

列表

对于有关几个第三方项目的列表,请添加:

{{% thirdparty-content %}}

在包含所有项目的段落标题正下方。

项目

如果你有一个列表,其中大多数项目引用项目内软件(例如:Kubernetes 本身,以及单独的 Descheduler 组件),那么可以使用不同的形式。

在项目之前,或在特定项目的段落下方添加此短代码:

{{% thirdparty-content single="true" %}}

版本号信息

要在文档中生成版本号信息,可以从以下几种短代码中选择。每个短代码可以基于站点配置文件 hugo.toml 中的版本参数生成一个版本号取值。最常用的参数为 latestversion

{{< param "version" >}}

{{< param "version" >}} 短代码可以基于站点参数 version 生成 Kubernetes 文档的当前版本号取值。短代码 param 允许传入一个站点参数名称,在这里是 version

转换为:

v1.30

{{< latest-version >}}

{{< latest-version >}} 返回站点参数 latest 的取值。每当新版本文档发布时,该参数均会被更新。 因此,参数 latestversion 并不总是相同。

转换为:

v1.30

{{< latest-semver >}}

{{< latest-semver >}} 短代码可以生成站点参数 latest 不含前缀 v 的版本号取值。

转换为:

1.30

{{< version-check >}}

{{< version-check >}} 会检查是否设置了页面参数 min-kubernetes-server-version 并将其与 version 进行比较。

转换为:

要获知版本信息,请输入 kubectl version.

{{< latest-release-notes >}}

{{< latest-release-notes >}} 短代码基于站点参数 latest 生成不含前缀 v 的版本号取值,并输出该版本更新日志的超链接地址。

转换为:

https://git.k8s.io/kubernetes/CHANGELOG/CHANGELOG-1.30.md

接下来

7.9 - 进阶贡献

如果你已经了解如何贡献新内容评阅他人工作,并准备了解更多贡献的途径, 请阅读此文。你需要使用 Git 命令行工具和其他工具做这些工作。

提出改进建议

SIG Docs 的成员可以提出改进建议。

在对 Kubernetes 文档贡献了一段时间后,你可能会对样式指南内容指南、用于构建文档的工具链、网站样式、 评审和合并 PR 的流程或者文档的其他方面产生改进的想法。 为了尽可能透明化,这些提议都需要在 SIG Docs 会议或 kubernetes-sig-docs 邮件列表上讨论。 此外,在提出全面的改进之前,这些讨论能帮助我们了解有关“当前工作如何运作”和“以往的决定是为何做出”的背景。 想了解文档的当前运作方式,最快的途径是咨询 kubernetes.slack.com 中的 #sig-docs 聊天群组。

在进行了讨论并且 SIG 就期望的结果达成一致之后,你就能以最合理的方式处理改进建议了。 例如,样式指南或网站功能的更新可能涉及 PR 的新增,而与文档测试相关的更改可能涉及 sig-testing。

为 Kubernetes 版本发布协调文档工作

SIG Docs 的批准人(Approver) 可以为 Kubernetes 版本发布协调文档工作。

每一个 Kubernetes 版本都是由参与 sig-release 的 SIG(特别兴趣小组)的一个团队协调的。 指定版本的发布团队中还包括总体发布牵头人,以及来自 sig-testing 的代表等。 要了解更多关于 Kubernetes 版本发布的流程,请参考 https://github.com/kubernetes/sig-release

SIG Docs 团队的代表需要为一个指定的版本协调以下工作:

  • 通过特性跟踪表来监视新功能特性或现有功能特性的修改。 如果版本的某个功能特性的文档没有为发布做好准备,那么该功能特性不允许进入发布版本。
  • 定期参加 sig-release 会议并汇报文档的发布状态。
  • 评审和修改由负责实现某功能特性的 SIG 起草的功能特性文档。
  • 合入版本发布相关的 PR,并为对应发布版本维护 Git 特性分支。
  • 指导那些想学习并有意愿担当该角色的 SIG Docs 贡献者。这就是我们常说的“实习”。
  • 发布版本的组件发布时,相关的文档更新也需要发布。

协调一个版本发布通常需要 3-4 个月的时间投入,该任务由 SIG Docs 批准人轮流承担。

担任新的贡献者大使

SIG Docs 批准人(Approver) 可以担任新的贡献者大使。

新的贡献者大使欢迎 SIG-Docs 的新贡献者,对新贡献者的 PR 提出建议, 以及在前几份 PR 提交中指导新贡献者。

新的贡献者大使的职责包括:

  • 监听 Kubernetes #sig-docs 频道 上新贡献者的 Issue。
  • 与 PR 管理者合作为新参与者寻找合适的第一个 issue
  • 通过前几个 PR 指导新贡献者为文档存储库作贡献。
  • 帮助新的贡献者创建成为 Kubernetes 成员所需的更复杂的 PR。
  • 为贡献者提供保荐,使其成为 Kubernetes 成员。
  • 每月召开一次会议,帮助和指导新的贡献者。

当前新贡献者大使将在每次 SIG 文档会议上以及 Kubernetes #sig-docs 频道中宣布。

SIG Docs 的评审人(Reviewer) 可以为新的贡献者提供保荐。

新的贡献者针对一个或多个 Kubernetes 项目仓库成功提交了 5 个实质性 PR 之后, 就有资格申请 Kubernetes 组织的成员身份。 贡献者的成员资格需要同时得到两位评审人的保荐。

新的文档贡献者可以通过咨询 Kubernetes Slack 实例 上的 #sig-docs 频道或者 SIG Docs 邮件列表 来请求评审者保荐。如果你对申请人的工作充满信心,你自愿保荐他们。 当他们提交成员资格申请时,回复 “+1” 并详细说明为什么你认为申请人适合加入 Kubernetes 组织。

SIG Docs 成员(Member) 可以担任 SIG Docs 的联合主席。

前提条件

Kubernetes 成员必须满足以下要求才能成为联合主席:

职责范围

联合主席的角色提供以下服务:

  • 拓展贡献者规模
  • 处理流程和政策
  • 安排时间和召开会议
  • 安排 PR 管理员
  • 在 Kubernetes 社区中提出文档倡议
  • 确保文档在 Kubernetes 发布周期中符合预期
  • 让 SIG Docs 专注于有效的优先事项

职责范围包括:

  • 保持 SIG Docs 专注于通过出色的文档最大限度地提高开发人员的满意度
  • 以身作则,践行社区行为准则, 并要求 SIG 成员对自身行为负责
  • 通过更新贡献指南,为 SIG 学习并设置最佳实践
  • 安排和举行 SIG 会议:每周状态更新,每季度回顾/计划会议以及其他需要的会议
  • 在 KubeCon 活动和其他会议上安排和负责文档工作
  • CNCF 及其尊贵合作伙伴 (包括 Google、Oracle、Azure、IBM 和华为)一起以 SIG Docs 的身份招募和宣传
  • 负责 SIG 正常运行

召开高效的会议

为了安排和召开高效的会议,这些指南说明了如何做、怎样做以及原因。

坚持社区行为准则

  • 相互尊重地、包容地进行讨论。

设定明确的议程

  • 设定清晰的主题议程
  • 提前发布议程

对于每周一次的会议,请将前一周的笔记复制并粘贴到笔记的“过去的会议”部分中

通过协作,完成准确的记录

  • 记录会议讨论
  • 考虑委派笔记记录员的角色

清晰准确地分配执行项目

  • 记录操作项,分配给它的人员以及预期的完成日期

根据需要来进行协调

  • 如果讨论偏离议程,请让参与者重新关注当前主题
  • 为不同的讨论风格留出空间,同时保持讨论重点并尊重人们的时间

尊重大家的时间:

按时开始和结束会议

有效利用 Zoom

声明 Zoom 主持人角色

录制 Zoom 会议

准备开始录制时,请单击“录制到云”。

准备停止录制时,请单击“停止”。

视频会自动上传到 YouTube。

SIG 联合主席 (Emeritus) 离职

参见 k/community/sig-docs/offboarding.md

7.10 - 查看站点分析

此页面包含有关 kubernetes.io 分析仪表板的信息。

查看仪表板

此仪表板使用 Google Looker Studio 构建,并显示自 2022 年 8 月以来使用 Google Analytics 4 在 kubernetes.io 上收集的信息。

使用仪表板

默认情况下,仪表板显示过去 30 天收集的所有分析。 使用日期选择器查看来自不同日期范围的数据。 其他过滤选项允许你根据用户位置、用于访问站点的设备、所用文档的翻译等查看数据。

如果你发现此仪表板存在问题,或者想要请求任何改进, 请开启一个问题

7.11 - 中文本地化样式指南

本节详述文档中文本地化过程中须注意的事项。 这里列举的内容包含了中文本地化小组早期给出的指导性建议和后续实践过程中积累的经验。 在阅读、贡献、评阅中文本地化文档的过程中,如果对本文的指南有任何改进建议, 都请直接提出 PR。我们欢迎任何形式的补充和更正!

一般规定

本节列举一些译文中常见问题和约定。

英文原文的保留

为便于译文审查和变更追踪,所有中文本地化 Markdown 文件中都应使用 HTML 注释 <!----> 将英文原文逐段注释起来,后跟对应中文译文。例如:

<!--
This is English text ... 
-->
中文译文对应 ...

不建议采用下面的方式注释英文段落,除非英文段落非常非常短:

<!-- This is English text ...  -->
中文译文对应 ...

无论英文原文或者中文译文中,都不要保留过多的、不必要的空白行。

段落划分

请避免大段大段地注释和翻译。一般而言,每段翻译可对应两三个自然段。 段落过长会导致译文很难评阅。但也不必每个段落都单独翻译。例如:

<!--
## Overview

### Concept

First paragraph, not very long.
-->
## 概述 {#overview}

### 概念 {#concept}

第一段落,不太长。

以下风格是不必要的:

<!--
## Overview
-->
## 概述 {#overview}

<!--
### Concept
-->
### 概念 {#concept}

<!--
First paragraph, not very long.
-->
第一段落,不太长。

编号列表的处理

编号列表需要编号的连续性,处理不好的话可能导致输出结果错误。 由于有些列表可能很长,一次性等将整个列表注释掉再翻译也不现实。 推荐采用下面的方式。

假定英文为:

1. Prepare something
1. Followed by a long step with code snippets and notes ...
   this is a really long item
1. Another long item ...
   .. continues here
1. Almost done ...

本地化处理:

<!--
1. Prepare something
-->
1. 准备工作,...
   这里每行缩进 3 个空格

<!--
1. Followed by a long step with code snippets and notes ...
   this is a really long item
-->
2. 这里是第二个编号,但需要显式给出数字,不能沿用英文编号。
   缩进内容同上,3 个空格。
   即使有三个反引号的代码段或者短代码,都按 3 个空格缩进。

<!--
1. Another long item ...
   .. continues here
1. Almost done ...
-->
3. 继续列表。

   如果条目有多个段落,也要
   保持缩进对齐以确保排版正确。

4. 列表终于结束

Frontmatter 的处理

页面中的 Frontmatter 指的是文件头的两个 --- 中间的部分。 对这一部分,解析器有特殊处理,因此不能将英文部分放在前面,中文跟在后面。 需要将二者顺序颠倒。如下所示:

---
title: 译文标题
type: concept
weight: 30
---

<!--
title: English title
type: concept
reviewers:
  - john
  - doe
weight: 30
-->

这里要注意的是:

  • titledescription 的内容要翻译,其他字段一般不必(甚至不可)翻译。
  • reviewers 部分要删除,不然中文译文会转给英文作者来审阅。

短代码(shortcode)处理

通过 HTML 注释的短代码仍会被运行,因此需要额外小心。建议处理方式:

{{< note >}}
<!--
English text
-->
中文译文
{{< /note >}}

博客译者署名

翻译一篇博客需要花费大量的时间和精力,添加署名是对译者工作的认可, 也有利于激励贡献者同步英文博客,提升博客质量。 如要添加译者署名,可在作者下面一行添加译者相关内容。例如:

<!--
**Author**: Alice (Google)
-->
**作者** :Alice (Google)

**译者** :李明 (百度)  

译与不译

资源名称或字段不译

根据英文原文写作风格约定【也在持续修订改进】,对 Kubernetes 中的 API 资源均按其规范中所给的大小写形式书写,例如:英文中会使用 Deployment 而不是 deployment 来表示名为 "Deployment" 的 API 资源类型和对象实例。

对这类词语,一般不应翻译。

代码中的注释

一般而言,代码中的注释需要翻译,包括存放在 content/zh-cn/examples/ 目录下的清单文件中的注释。

如果超级链接的目标是 Kubernetes 网站之外的纯英文网页,链接中的内容可以不翻译。 例如:

<!--
Please check [installation caveats](https://acme.com/docs/v1/caveats) ...
-->
请参阅 [installation caveats](https://acme.com/docs/v1/caveats) ...

标点符号

  1. 译文中标点符号要使用全角字符,除非以下两种情况:

    • 标点符号是英文命令的一部分;
    • 标点符号是 Markdown 语法的一部分。
  2. 英文排比句式中采用的逗号,在译文中要使用顿号代替,以便符合中文书写习惯。

更新译文

由于整个文档站点会随着 Kubernetes 项目的开发进展而演化,英文版本的网站内容会不断更新。 鉴于中文站点的基本翻译工作在 1.19 版本已完成, 从 1.20 版本开始本地化的工作会集中在追踪英文内容变化上。

为确保准确跟踪中文化版本与英文版本之间的差异,中文内容的 PR 所包含的每个页面都必须是“最新的”。 这里的“最新”指的是对应的英文页面中的更改已全部同步到中文页面。 如果某中文 PR 中包含对 content/zh-cn/docs/foo/bar.md 的更改,且文件 bar.md 的上次更改日期是 2020-10-01 01:02:03 UTC,对应 GIT 标签 abcd1234, 则 bar.md 应包含自 abcd1234 以来 content/en/docs/foo/bar.md 的所有变更, 否则视此 PR 为不完整 PR,会破坏我们对上游变更的跟踪。

这一要求适用于所有更改,包括拼写错误、格式更正、链接修订等等。要查看文件 bar.md 上次提交以来发生的所有变更,可使用:

./scripts/lsync.sh content/zh-cn/docs/foo/bar.md

链接锚点

英文 Markdown 中的各级标题会自动生成锚点,以便从其他页面中链接。 在译为中文后,相应的链接必然会失效。为防止这类问题, 建议在翻译各级标题时,使用英文方式显式给出链接锚点。例如:

<!--
### Create a Pod
-->
### 创建 Pod   {#create-a-pod}

此类问题对于概念部分的页面最为突出,需要格外注意。

由于大部分页面已经完成中文本地化,这意味着很多链接可以使用中文版本作为目标。 例如:

<!--
For more information, please check [volumes](/docs/concepts/storage/)
...
-->
更多的信息可参考[卷](/zh-cn/docs/concepts/storage/)页面。

如果对应目标页面尚未本地化,建议登记一个 Issue。

排版格式

以下为译文 Markdown 排版格式要求:

  • 中英文之间留一个空格

    • 这里的“英文”包括以英文呈现的超级链接
    • 这里的中文、英文都不包括标点符号
  • 译文 Markdown 中不要使用长行,应适当断行。

    • 可根据需要在 80-120 列断行
    • 最好结合句子的边界断行,即一句话在一行,不必留几个字转到下一行
    • 不要在两个中文字符中间断行,因为这样会造成中文字符中间显示一个多余空格, 如果句子太长,可以从中文与非中文符号之间断行
    • 超级链接文字一般较长,可独立成行
  • 英文原文中可能通过 _text_ 或者 *text* 的形式用斜体突出部分字句。 考虑到中文斜体字非常不美观,在译文中应改为 **译文** 形式, 即用双引号语法生成加粗字句,实现突出显示效果。

特殊词汇

英文中 "you" 翻译成 "你",不必翻译为 "您" 以表现尊敬或谦卑。

术语拼写

按中文译文习惯,尽量不要在中文译文中使用首字母小写的拼写。例如:

列举所有 pods,查看其创建时间 ...       [No]
列举所有 Pod,查看其创建时间 ...        [Yes]

第一次使用首字母缩写时,应标注其全称和中文译文。例如:

你可以创建一个 Pod 干扰预算(Pod Disruption Budget,PDB)来解决这一问题。
所谓 PDB 实际上是 ...

对于某些特定于 Kubernetes 语境的术语,也应在第一次出现在页面中时给出其英文原文, 以便读者对照阅读。例如:

镜像策略(Image Policy)用来控制集群可拉取的镜像仓库(Image Registry)源。

术语对照

本节列举常见术语的统一译法。除极个别情况,对于专业术语应使用本节所列举的译法:

  • API Server,API 服务器
  • GA (general availability),正式发布
  • addons,插件
  • admission controller,准入控制器
  • affinity,亲和性
  • annotation,注解
  • anti-affinity,反亲和性
  • attach,挂接
  • authenticate,身份验证
  • authorize,授权或鉴权
    • 将权限授予某主体时,译为“授权”
    • 根据某些参数/条件来确定具有哪些权限时,译为“鉴权”
  • authorizer,鉴权器
  • autoscale,自动扩缩容
  • bearer token,持有者令牌
  • capabilities
    • 当泛指某主体执行某操作的能力时,可直译为“能力”
    • 当特指 Linux 操作系统上的权限控制机制时,译为“权能字”
  • certificate authority,证书机构
  • certificate,证书
  • claim,申领
  • cloud provider
    • 当用来指代下层云服务的提供厂商时,译为“云服务供应商”
    • 当特指 Kubernetes 中对不同云平台的支持时,可酌情译为“云驱动”
  • cluster,集群
  • condition
    • 大多数上下文中,可译为“条件”
    • 在讨论 Kubernetes 资源的 condition 时,应译为“状况”
  • control loop,控制回路
  • control plane,控制平面,或控制面
  • controller,控制器
  • controller manager,控制器管理器
  • credential,登录凭据,凭据
  • custom,定制,或自定义
  • daemon,守护进程
  • dashboard,仪表板
  • dependent,附属或附属者
  • deprecated,已弃用的
  • deprecation,弃用
  • desired,预期的
  • desired state,预期状态
  • detach,解除挂接
  • distribution,发行版本
  • disruption,干扰(请勿译为“中断”)
  • drain,腾空
  • endpoint,端点
  • egress,出站
  • evict,驱逐
  • eviction,驱逐
  • feature gate,特性门控
  • federation,联邦
  • flags,命令行参数,参数
  • grace period,宽限期限
  • graceful termination,体面终止
  • hairpin,发夹
  • hash,哈希
  • headless service,无头服务
  • healthcheck,健康检查
  • hook,回调
  • host,主机,宿主机
  • hosting,托管
  • idempotent,幂等的
  • image,镜像
  • image registry,镜像仓库
  • ingress,入站
  • init container,Init 容器
  • key
    • 在加密解密、安全认证上下文中,译为密钥
    • 在配置文件、数据结构上下文中,译为主键,或键
  • label,标签
  • label selector,标签选择算符
  • lifecycle,生命周期
  • limit,限制,限值
  • liveness probe,存活态探针
  • load balance,负载均衡
  • load balancer,负载均衡器
  • log flush,清刷日志数据
  • loopback,本地回路
  • manifest,清单,清单文件
  • master node,主控节点
  • metric
    • 用来指代被测量的数据源时,译为指标
    • 用来指代测量观测结果时,译为度量值
  • mount,挂载
  • namespace,名字空间,命名空间
  • orphans,孤立或孤立的
  • override,覆写
  • owner,所有者,属主
  • pending,悬决的
  • persistent volume,持久卷
  • persistent volume claim,持久卷申领
  • pipeline,流水线
  • prerequisites,依赖,前提条件(根据上下文判断)
  • priority class,优先级类
  • probe,探针
  • provision,供应
  • pull,拉取
  • push,推送
  • quota,配额
  • readiness probe,就绪态探针
  • replica,副本
  • repo,仓库
  • repository,仓库
  • revision,修订版本
  • role,角色
  • role binding,角色绑定
  • rolling update,滚动更新
  • rollout,上线
  • rotate,轮换
  • round robin,轮转
  • runtime,运行时
  • scale in/out,横向缩容/扩容
  • scale up/down,纵向扩容/缩容
  • scale
    • 做动词用时,译为“扩缩”,或者“改变...的规模”
    • 做名词用时,译为“规模”
  • scheduler,调度器
  • service,服务
  • service account,服务账号
  • service account token,服务账号令牌
  • service discovery,服务发现
  • service mesh,服务网格
  • session,会话
  • sidecar,边车
  • skew,偏移
  • spec,规约
  • specification,规约
  • startup probe,启动探针
  • stateless,无状态的
  • static pod,静态 Pod
  • stderr,标准错误输出
  • stdin,标准输入
  • stdout,标准输出
  • storage class,存储类
  • taint,污点
  • threshold,阈值
  • toleration,容忍度
  • topology,拓扑
  • topology spread constraint,拓扑分布约束
  • traffic,流量
    • 在某些上下文中,可以根据情况译为“服务请求”,“服务响应”
  • unmount,卸载
  • use case,用例,使用场景
  • volume,卷
  • worker node,工作节点
  • workload,工作负载

8 - 测试页面(中文版)

本页面服务于两个目的:

  • 展示 Kubernetes 中文版文档中应如何使用 Markdown
  • 提供一个测试用文档,用来测试可能影响所有文档的 HTML、CSS 和模板变更

标题级别

上面的标题是 H2 级别。页面标题(Title)会渲染为 H1。以下各节分别展示 H3-H6 的渲染结果。

H3

此处为 H3 节内容。

H4

此处为 H4 节内容。

H5

此处为 H5 节内容。

H6

此处为 H6 节内容。

内联元素(Inline elements)

内联元素显示在段落文字、列表条目、提醒信息或者块级别元素之内。

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

内联文本风格

  • 粗体字
  • 斜体字
  • 粗斜体字
  • 删除线
  • 下划线
  • 带下划线的斜体
  • 带下划线的粗斜体
  • monospace text <- 等宽字体
  • monospace bold <- 粗等宽字体

列表

Markdown 在如何处理列表方面没有严格的规则。在我们从 Jekyll 迁移到 Hugo 时, 我们遇到了一些问题。为了处理这些问题,请注意以下几点:

  • 确保你将子列表的条目缩进两个空格

  • 要结束一个列表并开始一个新的列表,你需要在两个列表之间添加加一个 HTML 注释块, 并将其置于独立的一行,左边顶边对齐。否则前一个列表不会结束,无论你在它与第二个列表之间放多少个空行。

项目符号列表

  • 此为列表条目。
  • 此为另一列表条目,位于同一列表中。
  • 你可以将 -* 混合使用。
    • 要开始子列表,缩进两个空格。
      • 这是另一个子子条目。进一步多缩进两个空格。
    • 另一个子条目。
  • 这是一个新的列表。使用 Hugo 时,你需要用一行 HTML 注释将两个紧挨着的列表分开。 这里的 HTML 注释需要按左侧顶边对齐。

  • 项目符号列表可以中包含文字段落或块元素。

    段落内容与第一行文字左侧对齐。 此段文字和下面的代码段都与前一行中的“项”字对齐。

    ls -l
    
    • 在块级内容之后还可以有子列表内容。
  • 项目符号列表条目中还可以包含编号列表。

    1. 编号子列表条目一
    2. 编号子列表条目二
  • 项目符号列表条目中包含编号列表的另一种形式(推荐形式)。 让子列表的编号数字与项目符号列表文字左对齐。

    1. 编号子列表条目一,左侧编号与前一行的“项”字左对齐。
    2. 编号子列表条目二,条目文字与数字之间多了一个空格。

编号列表

  1. 此为列表条目
  2. 此为列表中的第二个条目。在 Markdown 源码中所给的编号数字与最终输出的数字 可能不同。建议在紧凑列表中编号都使用 1。如果条目之间有其他内容(比如注释 掉的英文)存在,则需要显式给出编号。
  1. 这是一个新的列表。 使用 Hugo 时,你需要用 HTML 注释将两个紧挨着的列表分开。 HTML 注释需要按左边顶边对齐。

  2. 编号列表条目中也可以包含额外的段落或者块元素。

    后续段落应该按编号列表文字的第一行左侧对齐。 此段落及下面的代码段都与本条目中的第一个字“编”对齐。

    ls -l
    
    • 编号列表条目中可以在块级内容之后有子列表。子列表的符号项要与上层列表条目文字左侧对齐。

中文译文的编号列表格式 1

  1. 译文条目一
  1. 译文条目二,由于前述原因,条目 2 与 1 之间存在注释行,如果此条目不显式给出 起始编号,会被 Hugo 当做两个独立的列表。

中文译文的编号列表格式 2

  1. 译文条目一

    中文译文段落。

    带注释的代码段(注意以上英文注释 <!----> 的缩进空格数)。

    # 列举服务
    kubectl get svc
    
  1. 译文条目二,由于前述原因,条目 2 与 1 之间存在注释行,如果此条目不显式给出起始编号, 会被 Hugo 当做两个独立的列表。

标签列表

标签列表可以用来有条件地显式内容,例如,当有多种选项可供选择时, 每个选项可能需要完全不同的指令或者上下文。

请选择一个选项。

标签页中也可以包含嵌套的排版风格,其中的英文注释处理也同正文中 的处理基本一致。

  1. 编号列表
  2. (或者没有编号的)
  3. 列表
echo '标签页里面也可以有代码段!'

在标签页中的子标题

标签页中也可以包含嵌套的子标题。

检查项列表 (Checklists)

检查项列表本质上也是一种项目符号列表,只是这里的项目符号部分被 CSS 压制了。

  • 此为第一个检查项
  • 此为被选中的检查项

代码段

你可以用两种方式来创建代码块。一种方式是将在代码块之前和之后分别加上包含三个反引号的独立行。 反引号应该仅用于代码段。 用这种方式标记代码段时,你还可以指定所包含的代码的编程语言,从而启用语法加亮。 这种方式也比使用空格缩进的方式可预测性更好。

这是用反引号创建的代码段

反引号标记代码段的方式有以下优点:

  • 这种方式几乎总是能正确工作
  • 在查看源代码时,内容相对紧凑
  • 允许你指定代码块的编程语言,以便启用语法加亮
  • 代码段的结束位置有明确标记。有时候,采用缩进空格的方式会使得一些对空格很敏感的语言(如 Python、YAML)很难处理。

要为代码段指定编程语言,可以在第一组反引号之后加上编程语言名称:

ls -l

Kubernetes 文档中代码块常用语言包括:

  • bash / shell(二者几乎完全相同)
  • go
  • json
  • yaml
  • xml
  • none(禁止对代码块执行语法加亮)

包含 Hugo 短代码的代码块

如果要像上面的例子一样显示 Hugo 短代码(Shortcode),不希望 Hugo 将其当做短代码来处理, 可以在 <> 之间使用 C 语言风格的注释。 下面的示例展示如何实现这点(查看本页的 Markdown 源码):

{{< alert color="warning" >}}This is a warning.{{< /alert >}}

要格式化链接,将链接显示文本放在方括号中,后接用圆括号括起来的链接目标。

  • [指向 Kubernetes.io 的链接](https://kubernetes.io/)
  • [到 Kubernetes.io 的相对链接](/)

你也可以使用 HTML,但这种方式不是推荐的方式。 例如,<a href="https://kubernetes.io/">到 Kubernetes.io 的链接</a>

中文链接

中文版本文档中的链接要注意以下两点:

  • 指向 Kubernetes 文档的站内链接,需要在英文链接之前添加前缀 /zh-cn。 例如,原链接目标为 /docs/foo/bar 时,译文中的链接目标应为 /zh-cn/docs/foo/bar。例如:

  • 英文页面子标题会生成对应锚点(Anchor),例如子标题 ## Using object 会生成对应标签 #using-objects。在翻译为中文之后,对应锚点可能会失效。对此,有两种方法处理。 假定译文中存在以下子标题:

    <!--
    ## Clean up
    
    You can do this ...
    -->
    ## 清理现场
    
    你可以这样 ...
    

    并且在本页或其他页面有指向 #clean-up 的链接如下:

    ..., please refer to the [clean up](#clean-up) section.
    

    第一种处理方法是将链接改为中文锚点,即将引用该子标题的文字全部改为中文锚点。 例如:

    ..., 请参考[清理工作](#清理现场)一节。
    

    第二种方式(也是推荐的方式)是将原来可能生成的锚点(尽管在英文原文中未明确给出) 显式标记在译文的子标题上。

    <!--
    ## Clean up
    
    You can do this ...
    -->
    ## 清理现场  {#clean-up}
    
    你可以这样 ...
    

    之所以优选第二种方式是因为可以避免文档站点中其他引用此子标题的链接失效。

图片

要显示图片,可以使用与链接类似的语法([links](#links)),不过要在整个链接之前添加一个感叹号(!)。 方括号中给出的是图片的替代文本。 请坚持为图片设定替代文本,这样使用屏幕阅读器的人也能够了解图片中包含的是什么。

pencil icon

要设置扩展的属性,例如 width、title、caption 等等,可以使用 figure 短代码,而不是使用 HTML 的 <img> 标签。 此外,如果你需要让图片本身变成超链接,可以使用短代码的 link 属性, 而不是将整个图片放到 Markdown 的链接语法之内。下面是一个例子:

用来展示 figure 短代码的图片

铅笔图标

用来展示 figure 短代码的图片

即使你不想使用 figure 短代码,图片也可以展示为链接。这里,铅笔图标指向 Kubernetes 网站。外层的方括号将整个 image 标签封装起来,链接目标在末尾的圆括号之间给出。

铅笔图标

你也可以使用 HTML 来嵌入图片,不过这种方式是不推荐的。

铅笔图标

表格

简单的表格可能每行只有一个独立的数据行,各个列之间用 | 隔开。 表格的标题行与表格内容之间用独立的一行隔开,在这一行中每个单元格的内容只有 - 字符,且至少三个。出于方便维护考虑,请尝试将各个单元格间的分割线对齐, 尽管这样意味着你需要多输入几个空格。

标题单元格 1标题单元格 2
内容单元格 1内容单元格 2

标题行是可选的。所有用 | 隔开的内容都会被渲染成表格。

Markdown 表格在处理块级元素方面还很笨拙。例如在单元格中嵌入列表条目、代码段、 或者在其中划分多个段落方面的能力都比较差。对于复杂的或者很宽的表格,可以使用 HTML。

标题单元格 1标题单元格 2
内容单元格 1内容单元格 2

使用 Mermaid 来可视化

你可以使用 Mermaid JS 来进行可视化展示。 Mermaid JS 版本在 /layouts/partials/head.html 中设置。

{{< mermaid >}}
graph TD;
  甲-->乙;
  甲-->丙;
  乙-->丁;
  丙-->丁;
{{</ mermaid >}}

会产生:

graph TD; 甲-->乙; 甲-->丙; 乙-->丁; 丙-->丁;
{{< mermaid >}}
sequenceDiagram
    张三 ->> 李四: 李四,锄禾日当午?
    李四-->>王五: 王五,锄禾日当午?
    李四--x 张三: 汗滴禾下土!
    李四-x 王五: 汗滴禾下土!
    Note right of 王五: 李四想啊想啊<br/>一直想啊想,太阳<br/>都下山了,他还没想出来<br/>,文本框都放不下了。

    李四-->张三: 跑去问王五...
    张三->王五: 好吧... 王五,白日依山尽?
{{</ mermaid >}}

会产生:

sequenceDiagram 张三 ->> 李四: 李四,锄禾日当午? 李四-->>王五: 王五,锄禾日当午? 李四--x 张三: 汗滴禾下土! 李四-x 王五: 汗滴禾下土! Note right of 王五: 李四想啊想啊一直想,
想到太阳都下山了,
他还没想出来,
文本框都放不下了。 李四-->张三: 跑去问王五... 张三->王五: 好吧... 王五,白日依山尽?

你可以查阅官方网站上的更多示例

侧边栏和提醒框可以为文本提供直观的重要性强调效果,可以偶尔一用。

侧边栏可以将文字横向平移,只是其显示效果可能不像提醒那么明显。

此为侧边栏。

你可以在侧边栏内排版段落和块级元素。

你甚至可以在其中包含代码块。

sudo dmesg

提醒框

提醒框(说明、警告等等)都是用 Hugo 短代码的形式展现。

注意,在较老的 Hugo 版本中,直接将 notewarningcaution 短代码括入 HTML 注释当中是有问题的。这些短代码仍然会起作用。目前,在 0.70.0 以上版本中似乎已经修复了这一问题。

包含其他页面

要包含其他页面,可使用短代码。

嵌入的 Katacoda 环境