1 - Kubelet Checkpoint API

特性状态: Kubernetes v1.30 [beta] (enabled by default: true)

为容器生成检查点这个功能可以为一个正在运行的容器创建有状态的拷贝。 一旦容器有一个有状态的拷贝,你就可以将其移动到其他计算机进行调试或类似用途。

如果你将通过检查点操作生成的容器数据移动到能够恢复该容器的一台计算机, 所恢复的容器将从之前检查点操作执行的时间点继续运行。 你也可以检视所保存的数据,前提是你拥有这类操作的合适工具。

创建容器的检查点可能会产生安全隐患。 通常,一个检查点包含执行检查点操作时容器中所有进程的所有内存页。 这意味着以前存在于内存中的一切内容现在都在本地磁盘上获得。 这里的内容包括一切私密数据和可能用于加密的密钥。 底层 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(参阅错误消息了解更多细节))

2 - Linux 内核版本要求

许多特性依赖于特定的内核功能,并且有最低的内核版本要求。 然而,单纯依赖内核版本号可能不足以满足某些操作系统发行版, 因为像 RHEL、Ubuntu 和 SUSE 等发行版的维护者们通常会将选定的特性反向移植到较旧的内核版本(保留较旧的内核版本)。

Pod sysctls

在 Linux 中,sysctl() 系统调用在运行时配置内核参数。 你可以使用名为 sysctl 的命令行工具来配置这些参数,许多参数通过 proc 文件系统暴露。

某些 sysctl 仅可用于足够新的内核上。

以下 sysctl 具有最低的内核版本要求, 并在安全集中得到了支持:

  • net.ipv4.ip_local_reserved_ports(自 Kubernetes 1.27 起,需要内核 3.16+);
  • net.ipv4.tcp_keepalive_time(自 Kubernetes 1.29 起,需要内核 4.5+);
  • net.ipv4.tcp_fin_timeout(自 Kubernetes 1.29 起,需要内核 4.6+);
  • net.ipv4.tcp_keepalive_intvl(自 Kubernetes 1.29 起,需要内核 4.5+);
  • net.ipv4.tcp_keepalive_probes(自 Kubernetes 1.29 起,需要内核 4.5+);
  • net.ipv4.tcp_syncookies(自内核 4.6+ 添加了命名空间作用域)。
  • net.ipv4.vs.conn_reuse_mode(用于 ipvs 代理模式,需要内核 4.1+);

kube proxy nftables 代理模式

对于 Kubernetes 1.31,kube-proxy 的 nftables 模式要求 nft 命令行工具为 v1.0.1 或更高版本,要求内核为 v5.13 或更高版本。

出于测试/开发目的,你可以使用较旧的内核,如果你在 kube-proxy 配置中设置 nftables.skipKernelVersionCheck 选项, 最老可以回溯到 v5.4。但在生产环境中不推荐这样做,因为这可能会导致系统上其他 nftables 用户出现问题。

v2 控制组

Kubernetes 对 cgroup v1 的支持从 v1.31 开始处于维护模式;推荐使用 cgroup v2。 在 Linux 5.8 中,为了方便使用,系统层面的 cpu.stat 文件被添加到根 cgroup。

在 runc 文档中,不推荐使用低于 5.2 的内核,因为其缺少冻结特性。

其他内核要求

某些特性可能依赖于新的内核功能并具有特定的内核要求:

  1. 递归只读挂载: 这是通过应用 MOUNT_ATTR_RDONLY 属性和 AT_RECURSIVE 标志来实现的,使用的是在 Linux 内核 v5.12 中添加的 mount_setattr(2)。
  2. Pod 用户命名空间支持需要最低内核版本 6.5+,参阅 KEP-127
  3. 对于节点系统交换, 直到内核 6.3 才支持将 tmpfs 设置为 noswap

Linux 内核长期维护

你可以在 kernel.org 找到活动的内核版本。

通常会提供多个 长期维护 内核版本,用于将 Bug 修复反向移植到较旧的内核树。 特别是对于较旧的树,只有重要的 Bug 修复才会被应用到此类内核,这些内核通常不会频繁发布新版本。 请参阅 Linux 内核网站,了解 Longterm 类别中的发布列表

接下来

3 - 关于 dockershim 移除和使用兼容 CRI 运行时的文章

这是关于 Kubernetes 弃用和移除 dockershim 或使用兼容 CRI 的容器运行时相关的文章和其他页面的列表。

Kubernetes 项目

你可以通过 GitHub 问题 Dockershim 移除反馈和问题 提供反馈。 (k/kubernetes/#106917)

外部来源

4 - 由 kubelet 填充的节点标签

Kubernetes 节点预先填充了一组标准 标签

你还可以通过 kubelet 配置或使用 Kubernetes API 在节点上设置自己的标签。

预设标签

Kubernetes 在节点上设置的预设标签有:

接下来

5 - kubelet 配置目录合并

当使用 kubelet 的 --config-dir 标志来指定存放配置的目录时,不同类型的配置会有一些特定的行为。

以下是在配置合并过程中不同数据类型的一些行为示例:

结构字段

在 YAML 结构中有两种结构字段:独立(标量类型)和嵌入式(此结构包含标量类型)。 配置合并过程将处理独立构造字段和嵌入式构造字段的重载,以创建最终的 kubelet 配置。

例如,你可能想要为所有节点设置一个基准 kubelet 配置,但希望自定义 addressauthorization 字段。 这种情况下,你可以按以下方式完成:

kubelet 主配置文件内容:

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
port: 20250
authorization:
  mode: Webhook
  webhook:
    cacheAuthorizedTTL: "5m"
    cacheUnauthorizedTTL: "30s"
serializeImagePulls: false
address: "192.168.0.1"

--config-dir 目录中文件的内容:

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
authorization:
  mode: AlwaysAllow
  webhook:
    cacheAuthorizedTTL: "8m"
    cacheUnauthorizedTTL: "45s"
address: "192.168.0.8"

生成的配置如下所示:

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
port: 20250
serializeImagePulls: false
authorization:
  mode: AlwaysAllow
  webhook:
    cacheAuthorizedTTL: "8m"
    cacheUnauthorizedTTL: "45s"
address: "192.168.0.8"

列表

你可以重载 kubelet 配置的切片/列表值。 但在合并过程中整个列表将被重载。 例如,你可以按以下方式重载 clusterDNS 列表:

kubelet 主配置文件的内容:

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
port: 20250
serializeImagePulls: false
clusterDNS:
  - "192.168.0.9"
  - "192.168.0.8"

--config-dir 目录中文件的内容:

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
clusterDNS:
  - "192.168.0.2"
  - "192.168.0.3"
  - "192.168.0.5"

生成的配置如下所示:

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
port: 20250
serializeImagePulls: false
clusterDNS:
  - "192.168.0.2"
  - "192.168.0.3"
  - "192.168.0.5"

含嵌套结构的映射

映射中的各个字段(无论其值类型是布尔值、字符串等)都可以被选择性地重载。 但对于 map[string][]string 类型来说,与特定字段关联的整个列表都将被重载。 让我们通过一个例子更好地理解这一点,特别是 featureGatesstaticPodURLHeader 这类字段:

kubelet 主配置文件的内容:

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
port: 20250
serializeImagePulls: false
featureGates:
  AllAlpha: false
  MemoryQoS: true
staticPodURLHeader:
  kubelet-api-support:
  - "Authorization: 234APSDFA"
  - "X-Custom-Header: 123"
  custom-static-pod:
  - "Authorization: 223EWRWER"
  - "X-Custom-Header: 456"

--config-dir 目录中文件的内容:

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
featureGates:
  MemoryQoS: false
  KubeletTracing: true
  DynamicResourceAllocation: true
staticPodURLHeader:
  custom-static-pod:
  - "Authorization: 223EWRWER"
  - "X-Custom-Header: 345"

生成的配置如下所示:

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
port: 20250
serializeImagePulls: false
featureGates:
  AllAlpha: false
  MemoryQoS: false
  KubeletTracing: true
  DynamicResourceAllocation: true
staticPodURLHeader:
  kubelet-api-support:
  - "Authorization: 234APSDFA"
  - "X-Custom-Header: 123"
  custom-static-pod:
  - "Authorization: 223EWRWER"
  - "X-Custom-Header: 345"

6 - 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)都能工作。

7 - Seccomp 和 Kubernetes

Seccomp 表示安全计算(Secure Computing)模式,自 2.6.12 版本以来,一直是 Linux 内核的一个特性。 它可以用来沙箱化进程的权限,限制进程从用户态到内核态的调用。 Kubernetes 能使你自动将加载到节点上的 seccomp 配置文件应用到你的 Pod 和容器。

Seccomp 字段

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

有四种方式可以为 Pod 指定 seccomp 配置文件:

apiVersion: v1
kind: Pod
metadata:
  name: pod
spec:
  securityContext:
    seccompProfile:
      type: Unconfined
  ephemeralContainers:
  - name: ephemeral-container
    image: debian
    securityContext:
      seccompProfile:
        type: RuntimeDefault
  initContainers:
  - name: init-container
    image: debian
    securityContext:
      seccompProfile:
        type: RuntimeDefault
  containers:
  - name: container
    image: docker.io/library/debian:stable
    securityContext:
      seccompProfile:
        type: Localhost
        localhostProfile: my-profile.json

上面的示例中的 Pod 以 Unconfined 运行,而 ephemeral-containerinit-container 独立设置了 RuntimeDefault。 如果临时容器或 Init 容器没有明确设置 securityContext.seccompProfile 字段, 则此值将从 Pod 继承。同样的机制也适用于运行 Localhost 配置文件 my-profile.json 的容器。

一般来说,(临时)容器的字段优先级高于 Pod 层级的值,而未设置 seccomp 字段的容器则从 Pod 继承配置。

对于 seccompProfile.type,可以使用以下值:

Unconfined
工作负载在没有任何 seccomp 限制的情况下运行。
RuntimeDefault
容器运行时定义的默认 seccomp 配置文件被应用。这个默认的配置文件旨在提供一套强大的安全默认值,同时保持工作负载的功能不受影响。 不同的容器运行时及其版本之间的默认配置文件可能会有所不同, 例如在比较 CRI-Ocontainerd 的默认配置文件时就会发现不同。
Localhost
localhostProfile 将被应用,这一配置必须位于节点磁盘上(在 Linux 上是 /var/lib/kubelet/seccomp)。 在创建容器时,容器运行时会验证 seccomp 配置文件的可用性。如果此配置文件不存在,则容器创建将失败,并报错 CreateContainerError

Localhost 配置文件

Seccomp 配置文件是遵循 OCI 运行时规范定义的 JSON 文件。配置文件主要根据所匹配的系统调用来定义操作,但也允许将特定值作为参数传递给系统调用。例如:

{
  "defaultAction": "SCMP_ACT_ERRNO",
  "defaultErrnoRet": 38,
  "syscalls": [
    {
      "names": [
        "adjtimex",
        "alarm",
        "bind",
        "waitid",
        "waitpid",
        "write",
        "writev"
      ],
      "action": "SCMP_ACT_ALLOW"
    }
  ]
}

上述配置文件中的 defaultAction 被定义为 SCMP_ACT_ERRNO,并可回退至 syscalls 中所定义的操作。 此错误通过 defaultErrnoRet 字段被定义为代码 38

通常可以使用以下操作:

SCMP_ACT_ERRNO
返回指定的错误码。
SCMP_ACT_ALLOW
允许执行系统调用。
SCMP_ACT_KILL_PROCESS
杀死进程。
SCMP_ACT_KILL_THREADSCMP_ACT_KILL
仅杀死线程。
SCMP_ACT_TRAP
发送 SIGSYS 信号。
SCMP_ACT_NOTIFYSECCOMP_RET_USER_NOTIF
通知用户空间。
SCMP_ACT_TRACE
使用指定的值通知跟踪进程。
SCMP_ACT_LOG
在将操作记录到 syslog 或 auditd 之后,允许执行系统调用。

SCMP_ACT_NOTIFYSECCOMP_RET_USER_NOTIF 这类操作可能不被支持, 具体取决于所使用的容器运行时、OCI 运行时或 Linux 内核版本。也可能存在其他限制, 例如 SCMP_ACT_NOTIFY 不能用作 defaultAction 或用于某些系统调用(如 write)。 所有这些限制由 OCI 运行时 (runccrun) 或 libseccomp 所定义。

syscalls JSON 数组包含对象列表,每个对象通过系统调用的 names 引用系统调用。 例如,SCMP_ACT_ALLOW 操作可用于创建包含如上例所示的系统调用的白名单。 也可以使用 SCMP_ACT_ERRNO 操作定义另一个列表,但会有不同的返回值(errnoRet)。

你还可以指定传递给某些系统调用的参数(args)。有关这些高级用例的细节,请参见 OCI 运行时规范Seccomp Linux 内核文档

进一步阅读

8 - 节点状态

在 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 秒钟。