Kubernetes v1.26 新特性一览

大家好,我是张晋涛。

Kubernetes v1.26 是 2022 年的最后一个大版本更新,包含了 37 项主要的更新。

8 月底正式发布的 v1.25 中包含 40 项,单从这个角度来看,Kubernetes 每个版本节奏的把控也是挺不错的。

我每期的 「k8s生态周报」都有一个叫上游进展的部分,所以很多值得关注的内容在之前的文章中已经发过了。 这篇中我会再额外介绍一些之前未涵盖的,和之前介绍过的值得关注的内容。

在 v1.26 中包含了很多直接影响用户与 Kubernetes 交互相关的功能,比如以下内容中会介绍的允许跨 namespace 持久化卷快照引用; 允许用户无需额外的 exporter 即可自定义 Service Level Indicator(SLI); kubectl events 达到 Beta;支持 Pod 所需资源就绪后再进行调度; 支持使用 OpenAPI v3 等。

以及针对高性能负载运行在哪个物理 CPU 的场景支持。

我们一起来具体看看吧!

#3294 允许跨 namespace 持久化卷快照引用

在 Kubernetes 中,用户可以通过使用 VolumeSnapshot 特性,从卷快照创建新的持久化卷。这是非常有用的,比如说 DBA 可以在进行数据库大的变更/迁移操作前,对持久化卷做一次快照,如果出现异常,则可以直接从该快照恢复数据库之前的状态。

例如通过以下方式可以动态的创建一个快照:

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
  name: test-snapclass
driver: testdriver.csi.k8s.io
deletionPolicy: Delete
parameters:
  csi.storage.k8s.io/snapshotter-secret-name: mysecret
  csi.storage.k8s.io/snapshotter-secret-namespace: mysecretnamespace
---
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
  name: test-snapshot
  namespace: ns1
spec:
  volumeSnapshotClassName: test-snapclass
  source:
    persistentVolumeClaimName: test-pvc

当然,这里也有些需要注意的点,该功能只能用于 CSI 存储驱动,并且该功能是自 Kubernetes v1.17 达到 beta,v1.20 正式 GA 的。

在 v1.26 中增加的特性是 CrossNamespaceVolumeDataSource 当前是 Alpha 阶段,允许创建持久化卷的时候,跨 namespace 引用卷快照。 它的主要价值在于,比如想要做应用跨 namespace 迁移的时候,直接从另一个 namespace 对持久化卷做快照,然后使用快照创建 PVC 即可很轻松的完成迁移。 例如:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: testvolumeclaim
  namespace: nstest1
spec:
  storageClassName: mystorageclass
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 2Gi
  dataSourceRef:
    apiGroup: snapshot.storage.k8s.io
    kind: VolumeSnapshot
    name: testsnapshot
    namespace: moelove
  volumeMode: Filesystem

当然如果考虑授权相关问题的话,这里使用 Gateway API 的 ReferenceGrant 来控制授权。

apiVersion: gateway.networking.k8s.io/v1alpha2
kind: ReferenceGrant
metadata:
  name: test
  namespace: moelove
spec:
  from:
  - group: ""
    kind: PersistentVolumeClaim
    namespace: nstest1
  to:
  - group: snapshot.storage.k8s.io
    kind: VolumeSnapshot
    name: testsnapshot

我认为这是个很实用的功能。

#3488 允许使用 CEL 进行 Admission Control

我在去年的 K8S 生态周报| Kubernetes v1.23.0 正式发布,新特性一览 | MoeLove 文章中,曾介绍过 Kubernetes 中引入了 Common Expression Language (CEL) 进行 CRD Validation。该特性在 v1.25 达到 Beta。

例如,某个 CRDs 的内容如下,其中定义了 minReplicas 小于 replicas 并且 replicas 小于 maxReplicas

...
openAPIV3Schema:
  type: object
  properties:
    spec:
      type: object
      x-kubernetes-validation-rules:
        - 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 

那么,当有如下的自定义资源创建时,Kubernetes 将会拒绝其请求。

apiVersion: "stable.example.com/v1"
kind: CustomDeployment
metadata:
  name: my-new-deploy-object
spec:
  minReplicas: 0
  replicas: 20
  maxReplicas: 10

并且返回如下错误:

The CustomDeployment "my-new-deploy-object" is invalid:
* spec: Invalid value: map[string]interface {}{"maxReplicas":10, "minReplicas":0, "replicas":20}: replicas should be smaller than or equal to maxReplicas.

这个过程看起来和 Admission controller 做的事情很像。当然,我之前也写了一篇文章 《理清 Kubernetes 中的准入控制(Admission Controller) | MoeLove》,有兴趣的小伙伴可以再翻看下。

回到我们聊到这个特性中,可以认为这是一种对 Admission webhook 的替代,用一种更加原生,直观,并且声明式的方式来进行管理。

这里引入了一个新的资源 ValidatingAdmissionPolicy ,使用起来如下:

 apiVersion: admissionregistration.k8s.io/v1alpha1
 kind: ValidatingAdmissionPolicy
 metadata:
   name: "demo-policy.moelove.info"
 Spec:
   failurePolicy: Fail
   matchConstraints:
     resourceRules:
     - apiGroups:   ["apps"]
       apiVersions: ["v1"]
       operations:  ["CREATE", "UPDATE"]
       resources:   ["deployments"]
   validations:
     - expression: "object.spec.replicas <= 2"

上述配置将会拒绝 replicas 不大于 2 的 Deployment。

这个特性的优势就在于无需额外的其他组件,除了需要学习 CEL 外,还是比较方便的。该特性目前是 alpha 级别。

这里我还想为大家推荐我之前写的两篇和策略相关的文章: 《云原生策略引擎 Kyverno (上) | MoeLove》 和 《Open Policy Agent (OPA) 入门实践 | MoeLove

#3545 改善 NUMA 中的拓扑管理

我在 2021 年 4 月份的文章 K8S 生态周报| Kubernetes v1.21 发布, 带来新的内存管理器 | MoeLove 中介绍过,Kubernetes v1.21 中在 kubelet 中新增了一个内存管理器。

主要是为了应对多 NUMA 结构下的效率。多 NUMA 结构下,为了保证效率,所以会按内存和 CPU 的相对距离来按 node 定义是否为 local memory 或者说本地内存,同时由于实际位置不同,所以就可能会产生内存分配不均匀的情况了。比如,我们可以使用 numactl 管理工具查看下当前机器上的情况:

[[email protected] ~]# numactl -H
available: 2 nodes (0-1)
node 0 cpus: 0 1 2 3 4 5 6 7 8 9 20 21 22 23 24 25 26 27 28 29
node 0 size: 65186 MB
node 0 free: 9769 MB
node 1 cpus: 10 11 12 13 14 15 16 17 18 19 30 31 32 33 34 35 36 37 38 39
node 1 size: 65536 MB
node 1 free: 15206 MB
node distances:
node   0   1 
  0:  10  21 
  1:  21  10 

可以看到在我当前的这台机器上就存在着比较明显的内存分配不均的情况。所以当某个进程达到了其 local memory 的上限,那自然就会影响到它的性能。我最早折腾 NUMA 相关问题是在前些年大量使用 MongoDB 的时候,在这上面也花了些时间,不过这也不影响我对 MongoDB 的喜爱~

这次在 kubelet 中增加的内存管理器便可以很好的解决这个问题,可以在启动 kubelet 的时候通过 --reserved-memory 以及配合 --memory-manager-policy 等参数来一起设置。例如:

--memory-manager-policy static --reserved-memory 0:memory=1Gi,hugepages-1M=2Gi --reserved-memory 1:memory=2Gi

注意:memory-manager-policy 必须设置为 static,如果不设置则默认为 none,即不采取任何行为。

不过这个特性还在较早期的阶段,目前只为 Guaranteed QoS 类的 Pod 提供支持。另外,如果正确启用了此特性,则在机器的 /var/lib/kubelet/memory_manager_state 可以看到其详细信息。

内存管理器的设计 - https://moelove.info

最终将会影响到拓扑管理器。

然后在 Kubernetes v1.22 则继续在 kubelet 中增加了另一个 --cpu-manager-policy-options 选项,对应为 CPU 管理器。

在 v1.26 中增加的则是 --topology-manager-policy-options 来控制整体的拓扑管理。现在仅支持一个 prefer-closest-numa-nodes=true 配置,当开启此配置的时候,拓扑管理器将尽量在单个 NUMA 或者尽可能少的 NUMA 节点中对齐资源。

这对于高性能负载是非常重要的一个优化。建议关注。

#113274 为 Pod 调度增加调度就绪的机制

这是 KEP 3521 的第一部分。

首先我们来简单的回顾一下 Pod 的创建过程。

当 client 通过 kube-apiserver 创建成功 Pod 资源后,kube-scheduler 会去检查尚未被调度的 Pod,然后为其进行调度,分配 Node。之后 Node 获取到调度到该 Node 上的 Pod 然后进行创建。 这里省略了很多细节,但其他的部分与我们此处要介绍的内容关系不太大,就不展开了。

根据上述的过程,我们可以发现,在 Pod 创建成功后,其实就默认该 Pod 是可以被调度了,kube-scheduler 就应该开始工作了。

但在实际的场景中,Pod 通常还会需要一些其他的资源,最典型的比如存储。在一些环境中,这些资源是需要预先进行创建的, 尤其是在一些云厂商的场景中,还需要检查用户账户中是否还有余额可以用于创建云盘等

一但前置的依赖无法满足,假如 kube-scheduler 已经完成了 Pod 的调度,那么 kubelet 侧就会处于尝试创建 Pod ,但失败的情况。

这个 KEP 的出现就可以很好的解决这个问题,增加了一个 Pod 是否准备好被调度的机制。 如果前置依赖不满足,那么 Pod 就无需被调度,这也不会消耗资源。kube-scheduler 和 kubelet 都无需进行处理。 待条件满足,Pod 再被调度和创建即可。

这个机制我个人感觉还是挺好的,甚至我可以有更灵活的策略来控制应用的副本。 比如在大流量,需要动态扩容的场景下,我可以利用此机制预先创建一些 Pod 资源以及准备好它的依赖, 甚至可以在 Node 上准备好镜像等。当需要增加副本时,直接标记 Pod 可被调度, 这样就可以更快的在 Node 上拉起 Pod 了。

#110618 移除了 CRI v1alpha2

在移除了 dockershim 的支持后, 现在终于删掉了 CRI v1alpha2 的代码,这意味着之后所有实现了 CRI 的运行时,都必须实现 CRI v1 的接口。

目前 containerd 的 CRI v1 接口已经实现了,所以在 Kubernetes v1.26 发布后,也可以放心的使用 containerd 作为其运行时。

不过,你是否还记得 cri-dockerd 呢?用于将 Docker 作为 Kubernetes 运行时的组件,现在由 Mirantis 进行维护, 此项目目前的维护者比较少,目前还只支持 CRI v1alpha2 ,所以,如果你在使用此项目让 Docker 作为运行时,并且想要升级到 Kubernetes v1.26 的话,这是不行的。

#111333 Auth API 允许获取 User 属性

这是 KEP-3325: Self subject attributes review API 实现的一部分。

大家想必都知道,Kubernetes 中并没有 User(用户)的资源,但是 Kubernetes 中有权限校验的方式,比如我们常用到的利用 x509 证书进行用户权限相关的校验,或者 通过外部的 OIDC 和 webhook 等进行校验。

此功能实际上是为了添加一个新的接口,以便于用户身份通过校验后,获取其所具有的属性。这样就可以简单的通过增加一个 kubectl auth whoami 的命令,来了解当前用户的相关信息了。 这功能比较类似于我们做 OAuth 的时候,可能会做个 UserInfo 之类的接口,用来查看用户相关的属性。

该功能是通过在 authentication.k8s.io Group 下添加了 SelfSubjectReview 资源来实现的,具体如下:

// SelfSubjectReview contains the user information that the kube-apiserver has about the user making this request.
// When using impersonation, users will receive the user info of the user being impersonated.
type SelfSubjectReview struct {
	metav1.TypeMeta `json:",inline"`
	// Standard object's metadata.
	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
	// +optional
	metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
	// Status is filled in by the server with the user attributes.
	Status SelfSubjectReviewStatus `json:"status,omitempty" protobuf:"bytes,2,opt,name=status"`
}

// SelfSubjectReviewStatus is filled by the kube-apiserver and sent back to a user.
type SelfSubjectReviewStatus struct {
	// User attributes of the user making this request.
	// +optional
	UserInfo v1.UserInfo `json:"userInfo,omitempty" protobuf:"bytes,1,opt,name=userInfo"`
}

此功能在 v1.26 版本开始引入,并作为 Alpha 特性提供,可通过 APISelfSubjectReview feature gate 控制是否启用。

同时,本次也在 kubectl 中添加了 kubectl alpha auth whoami 子命令,可直接查看当前用户的相关属性信息。 当执行此命令后,会得到如下输出:

apiVersion: authentication.k8s.io/v1alpha1
kind: SelfSubjectReview
status:
  userInfo:
    username: tao.moelove.info
    uid: a79aaf30-0c6a-66ed-881d-0888ac1266683
    groups:
    - admins
    - speakers
    - system:authenticated
    extra:
      skills:
      - reading
      - learning
      subjects:
      - kubernetes
      - containers

#3386 减少 Kubelet 在 PLEG 中的 CPU 消耗

这个主要是为了减少 Kubelet 在跟踪 Pod 状态 PLEG 时的 CPU 消耗。

它将减少 Kubelet 的一些定期轮询,而是尽可能的依赖于 CRI 的通知。

经过这个调整,也许在生产环境中遇到的 PLEG 问题也能少一点吧。

其他

kubeadm 新增 cleanup-tmp-dir 配置项,在 kubeadm reset 时,可以通过传递此参数将 kubeadm 产生的临时文件给清理干净。

从 Kubernetes v1.25 开始,/etc/resolv.conf 中的 search . 会被传递到容器内。

这会导致任意基于 musl libc 系统的 DNS 查询失败,比如任何基于 Alpinelinux 的镜像都会失败。

要验证自己的集群是否会受该问题影响,只需要查看系统的 /etc/resolv.conf 文件的内容即可。比如:

nameserver 127.0.0.53
options edns0 trust-ad
search .

如果是上述的情况,一般说明是被 systemd resove 接管了,并且会受到此处描述的问题所影响。

在执行 kubectl rollout history 的时候可以传递 --revision=3 拿到指定版本的信息。但是如果指定了 output format ,则 --revision 可能会失效。 通过此 PR 便可解决此问题,但要到 v1.26 才会携带。

调度程序可以 dump 出更多信息。

由于 OpenShift 团队发现在使用它的时候存在一些问题, 将 LocalStorageCapacityIsolationFSQuotaMonitoring 重新回退到 Alpha 阶段。

在之前的 「K8S 生态周报」中我曾介绍过,在 v1.25 中将树内的 GlusterFS plugin 标记为废弃,并建议迁移至使用 CSI , 如今这些插件已经被彻底删除了。

当前 kubelet 创建 Pod 的时候,如果 /etc/resolv.conf 中存在多行 options 配置,则只取最后一行。

例如 /etc/resolv.conf 包含的配置如下:

options timeout:1 
options attempts:3

则 Pod 中的 /etc/resolv.conf 只包含如下内容

options attempts:3

但事实上即使在 /etc/resolv.conf 中配置多行 options ,DNS 也是可以正常工作的。所以当前的 kubelet 行为就不一致了。

通过这个 PR, 会将主机的 /etc/resolv.conf 中的多行配置合并为一行。如果还是上面的例子,则 Pod 内看到的就是如下内容了.

options timeout:1 attempts:3

这里有个需要注意的点就是,Kubernetes 中的默认 DNS 策略并非 default,这个修改只是针对 default 策略的。

我们可以为 Pod 增加 postStartpreStop 的 Hook,并且其分别可以设置为 ExecHTTP 模式。这个 PR 主要是在修改 HTTP 模式下的行为。

具体来说,HTTP 模式下的代码与 readiness/liveness 下使用的并不一样,而且也不支持一些常见的功能,比如:

  • 设置自定义 HTTP header
  • 使用 HTTPS 连接

但经过此 PR ,终于将它们的行为进行了统一,但这毕竟是一个比较大的调整,如果你不希望应用此变更, 可以通过 -feature-gates=ConsistentHTTPGetHandlers=false 进行全局禁用,避免这个功能影响到你的环境。

这个 PR 中对一些特殊字符进行了转义。这其实是一个比较有意思的内容,我来稍微介绍一下。

你可以在自己的终端中尝试执行如下命令:

kubectl create -f - <<EOF
{
    "apiVersion": "v1",
    "involvedObject": {
        "kind": "Node",
        "name": "node01",
        "uid": "node01"
    },
    "kind": "Event",
    "message": "\u001b[2J\u001b[3J\u001b[1;1H\u001b[m spoofed \u001b[60C\u001b[1;0m. Done",
    "metadata": {
        "name": "spoofEvent",
        "namespace": "default"
    },
    "source": {
        "component": "kubelet",
        "host": "node01"
    },
    "type": "Normal"
}
EOF

这将会创建一条 event 记录,但是一但你执行 kubectl get events 你就会发现一些有趣的事情,屏幕上的记录被清空了。

这是由于 kubectl 在输出内容的时候并不会进行任何转义,比如上述示例中包含了一些特殊字符:

  • \u001b[2J:清屏
  • \u001b[60C: 将光标向前移动 60

当然还有一些其他的,我印象中我早年刚开始接触 Linux 不久后还花费了一些时间来学习这些东西,因为这些东西可以配置到终端里面,实现一些比如变换颜色之类的事情。

我翻了下我之前的文章,曾经还有将 bash 做过如下设置:

function git-branch-name {
  git symbolic-ref HEAD 2>/dev/null | cut -d"/" -f 3
  #git rev-parse --abbrev-ref HEAD
}

function git-branch-prompt {
  local branch=`git-branch-name`
  if [ $branch ]; then printf " [%s]" $branch; fi
}

PS1="\[email protected]\h \[\033[0;36m\]\W\[\033[0m\]\[\033[0;32m\]\$(git-branch-prompt)\[\033[0m\] \$ "

对此内容感兴趣的小伙伴可以试试看把上述内容贴到自己的 .bashrc 文件中,然后 source 一下看看。

回到这个 PR 中,之前这个问题还给分配成了 CVE-2021-25743, 修复的办法也比较简单,但是比较耗时耗力,就是对一些输出的位置做了转义操作。

不过可能还有一些遗漏的,比如 kubectl logs 之类的,可能也会受到影响,有兴趣的小伙伴可以去验证一下。

certificates.k8s.io/v1beta1 自 Kubernetes v1.19 已被废弃,通过此 PR 移除,将在 v1.26 版本中生效。

以后使用 certificates.k8s.io/v1 即可。这个主要是影响 kubectl certificate 命令。

以上就是 Kubernetes v1.26 版本中我认为主要值得关注的部分。下次再见!


欢迎订阅我的文章公众号【MoeLove】

TheMoeLove

加载评论