Kubernetes v1.29 新特性一览

2023年 12月 12日 41.8k 0

大家好,我是张晋涛。

Kubernetes v1.29 是 2023 年的第三个大版本更新,也是今年的最后一个大版本,包含了 49 项主要的更新。 而今年发布的第一个版本 v1.27 有近 60 项,第二个版本 v1.28 有 46 项。尽管 Kubernetes 已经发布快 10 年了,Kubernetes 的生命力仍然很旺盛!

这个版本中有 19 个增强功能正在进入 Alpha 阶段,19 个将升级到 Beta 阶段,而另外 11 个则将升级到稳定版。

可以看出来还是有很多新特性在逐步引入。

我维护的 「k8s生态周报」每篇都有一个叫上游进展的部分,会发一些值得关注的内容。
不过正如我上篇提到的那样,最近发生了很多事情,断更了一段时间,这篇恰好都补上,感谢大家的支持。

KEP-2876: 基于 CEL 的 CRD 规则校验正式达到 GA

这个特性对于所有在 Kubernetes 上进行开发的小伙伴来说应该都是非常重要的。
因为大多数情况下,我们都是通过 CRD 来实现 Kubernetes 中的功能扩展。

在通过 CRD 实现功能扩展的时候,为了能提供更好的用户体验,和更可靠的输入校验,就需要支持校验了。

CRD 目前原生支持两类校验能力:

  • 基于 CRD 结构定义的校验
  • OpenAPIv3 的校验规则

例如:

---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  annotations:
    controller-gen.kubebuilder.io/version: v0.13.0
  name: kongplugins.configuration.konghq.com
spec:
  group: configuration.konghq.com
  names:
    categories:
    - kong-ingress-controller
    kind: KongPlugin
    listKind: KongPluginList
    plural: kongplugins
    shortNames:
    - kp
    singular: kongplugin
  scope: Namespaced
  versions:
    name: v1
    schema:
      openAPIV3Schema:
        description: KongPlugin is the Schema for the kongplugins API.
        properties:
          apiVersion:
            description: 'APIVersion defines the versioned schema of this representation
              of an object. Servers should convert recognized schemas to the latest
              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
            type: string
          protocols:
            description: Protocols configures plugin to run on requests received on
              specific protocols.
            items:
              description: KongProtocol is a valid Kong protocol. This alias is necessary
                to deal with https://github.com/kubernetes-sigs/controller-tools/issues/342
              enum:
              - http
              - https
              - grpc
              - grpcs
              - tcp
              - tls
              - udp
              type: string
            type: array
        type: object
        ...
        x-kubernetes-validations:
        - message: Using both config and configFrom fields is not allowed.
          rule: '!(has(self.config) && has(self.configFrom))'
        - message: Using both configFrom and configPatches fields is not allowed.
          rule: '!(has(self.configFrom) && has(self.configPatches))'
        - message: The plugin field is immutable
          rule: self.plugin == oldSelf.plugin

以上示例中定义了一个名为 KongPlugin 的自定义资源,其中使用 openAPIV3Schema 定义了 OpenAPI schema 的校验规则。

但这些内置规则能达到的效果相对有限,如果想要实现更加丰富的校验规则/特性,则可以使用:

  • Admission Webhook: 关于 Adminssion webhook 可以参考我之前的文章 理清 Kubernetes 中的准入控制(Admission Controller) | MoeLove
  • 使用自定义验证器:例如基于 Open Policy Agent(OPA) 的 Gatekeeper,关于 OPA 可以参考 Open Policy Agent (OPA) 入门实践 | MoeLove

不过 无论是 Admission webhook 还是自定义验证器,它们与 CRD 自身都是分离的,并且这也会导致开发 CRD 的难度和后续的维护成本增加。

为了能解决这些问题,Kubernetes 社区为 CRD 引入了一种基于 CEL(Common Expression Language)的校验规则,这个规则是可以直接
在 CRD 的声明文件中编写的,无需使用任何 Admission webhook 或者自定义验证器,大大简化了 CRD 的开发和维护成本。

我从社区刚开始想要引入这个功能起就一直在文章中宣传该功能了,持续关注的小伙伴对这个应该不算陌生了。
我大概查看了以下文章中都有介绍 Kubernetes 中 CEL 相关特性的演进过程,感兴趣的小伙伴可以逐篇阅读:

  • 两年前的 K8S 生态周报| Kubernetes v1.23.0 正式发布,新特性一览 | MoeLove
  • 一年前的 Kubernetes v1.26 新特性一览 | MoeLove
  • K8S 生态周报| Kubernetes 基于 CEL 的控制系统再添新特性 | MoeLove
  • Kubernetes v1.27 新特性一览 | MoeLove

在 Kubernetes v1.29 版本中基于 CEL 的 CRD 校验能力达到 GA,只需要使用 x-kubernetes-validations 定义校验规则即可,
它足够轻量且安全,可以直接在 kube-apiserver 中运行,我们来看一个例子:

---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  annotations:
    controller-gen.kubebuilder.io/version: v0.13.0
  name: kongplugins.configuration.konghq.com
spec:
  group: configuration.konghq.com
  scope: Namespaced
  versions:
    name: v1
    schema:
      openAPIV3Schema:
        description: KongPlugin is the Schema for the kongplugins API.
        properties:
          plugin:
        ...
        x-kubernetes-validations:
        - message: Using both config and configFrom fields is not allowed.
          rule: '!(has(self.config) && has(self.configFrom))'
        - message: The plugin field is immutable
          rule: self.plugin == oldSelf.plugin

例如其中的这句 self.plugin == oldSelf.pluginselfoldSelf 代表了变化前后的资源对象,
一旦定义了 plugin 字段的内容,则不允许修改它。

此外,CEL 还有非常丰富的特性,可以通过在线的 Playground 来体验它 playcel.undistro.io/

正如我前面提到的,这个特性引入的时间已经两年多了,在 Kubernetes v1.25 中达到 Beta 并默认开启,如今终于达到了 GA。

额外一说,Kubernetes Gateway API 项目正在将它的所有 Admission webhook 删掉,全部使用基于 CEL 的规则进行校验,
这大概是目前社区中最大的一个用例了。

KEP-3668: 为动态和静态分配预留 NodePort 端口范围达到 stable

众所周知 Kubernetes 原生的 Service 对象有一种 NodePort 的类型,用于将集群内的服务暴露到集群外。

在当前情况下,如果用户想要新建一个 NodePort,并为它固定的指定一个 Port,其实是存在一定的风险的。
有可能指定的 Port 已经被分配出去了,这样就会冲突,导致 Service 创建失败。

这个 KEP 提出了为 NodePort 类型的 Services 动、静态地预留一段可选区间,并且支持两种方式进行 Port 配置:

  • 自动化地随机生成(由 Kubernetes 按照规则自动生成)
  • 手工设置(由用户根据需求自行设定)。

举个例子:

kube-apiserver 可以通过 --service-node-port-range 控制 NodePort 可以使用的端口范围,默认是 30000-32767。在这个 KEP 中引入的计算公式是:min(max($min, node-range-size/$step), $max) ,也就是说:

  • Service Node Port 范围: 30000-32767
  • 范围大小: 32767 - 30000 = 2767
  • Band Offset: min(max(16,2767/32),128) = min(86,128) = 86
  • Static band start: 30000
  • Static band ends: 30086

按照这种方式计算,也就是 30000-30086 作为 static 段,之后的作为 dynamic 段。

 ┌─────────────┬─────────────────────────────────────────────┐
 │   static    │                    dynamic                  │
 └─────────────┴─────────────────────────────────────────────┘

 ◄────────────► ◄────────────────────────────────────────────►
30000        30086                                          32767

如果用户想要自己指定一个 NodePort 的端口,若在 static 范围内,则相对来说不容易出现冲突。

不过,总体来说这个特性实际上是一个 Kubernetes 内部的特性,多数情况下只要记住一个原则:“手动指定 NodePort 时尽量选择在 static 范围(前段)内即可”。如果用户指定的 Port 在 dynamic 范围内,如果该 Port 尚未被分配,则也可以创建成功。

这种计算方式实际上是来自于 KEP-3070,用于给 Service 分配 ClusterIP 使用的。

KEP-753:SidecarContainers 特性达到 Beta 并默认启用

Sidecars 是一种辅助容器,它们能够给主容器添加额外功能。尽管这种模式在 Service Mesh 场景用例更多,但很多用户也会在非 Service Mesh 场景下使用,比如像日志记录、监控等场景。

但是之前 Sidecars 在这些场景中使用存在一些问题,比如当 Pod 删除的时候,由于 Sidecar 容器的生命周期管理的缺失,会导致这些服务和主容器的生命周期不同步,进而影响服务的可靠性。

这个 KEP 在 Pod 规范中将 Sidecar 容器定义为 init containers 的一部分,并且指定其具有“始终重启”策略。

apiVersion: v1
kind: Pod
metadata:
  name: moelove-pod
spec:
  initContainers:
  - name: log
    image: moelove/fluentbit
    restartPolicy: Always
    ...

在 Kubernetes v1.29 版本中该特性将默认启用,同时 Sidecar containers 的停止顺序将按照与它们启动时候相反的顺序来进行,这样一方面可以确保是主容器先停止的,另一方面也便于控制所有组件的生命周期。

KEP-3960: PreStop Hook 引入 Sleep 动作(Alpha)

这个 KEP 也比较有意思,主要是为了简化一个最常见的需求。

很多应用 Pod 在关闭的时候,需要断开连接,以免影响用户流量。所以很多时候会在关闭前设置 PreStop 来进行一些相关的处理或者等待。

但是当前的 PreStop Hook 只支持 exechttpGet 这两种,这个 KEP 是想要实现一种原生的 sleep 操作,例如:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  template:
    spec:
      containers:
      - name: nginx
        image: nginx:1.25.3
        lifecycle:
          preStop:
            sleep:
              seconds: 5
        readinessProbe:
          httpGet:
            path: /
            port: 80

这样就可以很简单了。在引入这个 KEP 之前,需要设置为类似 exec sh -c "sleep 5" 这样,这需要容器内还包含 sleep 这个命令才行。

不过这个特性目前处于 Alpha,能否最终 GA 还需要看看社区的反馈。

KEP-4193:改善 service account token 绑定机制(Alpha)

在 Kubernetes 中,service account token 是保障安全性不可或缺的一环。它们用于验证集群中各个工作负载,并且可以防止未经授权访问。

Kubernetes v1.29 中进一步加强了对这些令牌的安全保护:现在每个 service account 仅能与特定 Pod 实例相关联,在外泄后也无法被滥用。这种方式有效地将 service account 和 Pod 生命周期捆绑在一起,大大减少了攻击者利用盗取 token 进行攻击的可能。

在 v1.29 中 kube-apiserver 有如下相关的 feature gates 可以控制相关的特性,当然除了 KEP-4193 外,这个版本中也推进了 KEP-2799,减少了基于 secret 的 service account token。这有助于缩短 Token 的有效时间,尽可能的减少攻击面。

LegacyServiceAccountTokenCleanUp=true|false (BETA - default=true)
ServiceAccountTokenJTI=true|false (ALPHA - default=false)
ServiceAccountTokenNodeBinding=true|false (ALPHA - default=false)
ServiceAccountTokenNodeBindingValidation=true|false (ALPHA - default=false)
ServiceAccountTokenPodNodeInfo=true|false (ALPHA - default=false)

KEP-727:Kubelet Resource Metrics 达到 GA

这是一个有很长历史的 KEP 了,从开始提出到现在 GA 用了 5 年的时间,中间也发生了很多有趣的事情。这次主要涉及的部分是如下 metrics:

  • container_cpu_usage_seconds_total
  • container_memory_working_set_bytes
  • container_start_time_seconds
  • node_cpu_usage_seconds_total
  • node_memory_working_set_bytes
  • pod_cpu_usage_seconds_total
  • pod_memory_working_set_bytes
  • resource_scrape_error

以下是一个示例的输出:

# HELP container_cpu_usage_seconds_total [STABLE] Cumulative cpu time consumed by the container in core-seconds
# TYPE container_cpu_usage_seconds_total counter
container_cpu_usage_seconds_total{container="coredns",namespace="kube-system",pod="coredns-55968cc89d-bhhbx"} 0.195744 1691361886865
# HELP container_memory_working_set_bytes [STABLE] Current working set of the container in bytes
# TYPE container_memory_working_set_bytes gauge
container_memory_working_set_bytes{container="coredns",namespace="kube-system",pod="coredns-55968cc89d-bhhbx"} 1.675264e+07 1691361886865
# HELP container_start_time_seconds [STABLE] Start time of the container since unix epoch in seconds
# TYPE container_start_time_seconds gauge
container_start_time_seconds{container="coredns",namespace="kube-system",pod="coredns-55968cc89d-bhhbx"} 1.6913618235901163e+09 1691361823590
# HELP node_cpu_usage_seconds_total [STABLE] Cumulative cpu time consumed by the node in core-seconds
# TYPE node_cpu_usage_seconds_total counter
node_cpu_usage_seconds_total 514578.636 1691361887931
# HELP node_memory_working_set_bytes [STABLE] Current working set of the node in bytes
# TYPE node_memory_working_set_bytes gauge
node_memory_working_set_bytes 1.9501084672e+10 1691361887931
# HELP pod_cpu_usage_seconds_total [STABLE] Cumulative cpu time consumed by the pod in core-seconds
# TYPE pod_cpu_usage_seconds_total counter
pod_cpu_usage_seconds_total{namespace="kube-system",pod="coredns-55968cc89d-bhhbx"} 1.30598 1691361880003
# HELP pod_memory_working_set_bytes [STABLE] Current working set of the pod in bytes
# TYPE pod_memory_working_set_bytes gauge
pod_memory_working_set_bytes{namespace="kube-system",pod="coredns-55968cc89d-bhhbx"} 1.6715776e+07 1691361880003
# HELP resource_scrape_error [STABLE] 1 if there was an error while getting container metrics, 0 otherwise
# TYPE resource_scrape_error gauge
resource_scrape_error 0
# HELP scrape_error [ALPHA] 1 if there was an error while getting container metrics, 0 otherwise
# TYPE scrape_error gauge
scrape_error 0

KEP-3466: Kubernetes Component Health SLIs 达到 GA

这个 KEP 的主要目的是为了让每个组件暴露它们自己的健康状态,以便于可以根据其健康状态的 SLI 计算集群的 SLO。

很久之前 Kubernetes 中存在一个 ComponentStatus,可以用于查看集群中组件的状态。但正如下方所示,在 v1.19 已经实际上废弃了。

moelove@k8s-test:~$ kubectl  get cs
Warning: v1 ComponentStatus is deprecated in v1.19+
NAME                 STATUS    MESSAGE   ERROR
scheduler            Healthy   ok        
etcd-0               Healthy   ok        
controller-manager   Healthy   ok 

我们期望能通过这个 KEP 使用各个组件的健康状态作为 SLI,采集聚合后,计算出集群的 SLO。进而可以给出 SLA。(关于它们之间的关系有兴趣的小伙伴可以搜索下相关内容)

以下是一个示例的输出:

tao@moelove:/$ kubectl get --raw "/metrics/slis"
# HELP kubernetes_healthcheck [STABLE] This metric records the result of a single healthcheck.
# TYPE kubernetes_healthcheck gauge
kubernetes_healthcheck{name="etcd",type="healthz"} 1
kubernetes_healthcheck{name="etcd",type="livez"} 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="ping",type="healthz"} 1
kubernetes_healthcheck{name="ping",type="livez"} 1
kubernetes_healthcheck{name="ping",type="readyz"} 1

已知问题

EventedPLEG 这个特性在 v1.27 中升级到了 Beta,但是在新版本的测试中发现了比较多的问题,所以现在将它默认禁用了,待社区修复后会再打开。建议在 v1.29 中先关闭此特性

其他

  • KEP-2495: PV/PVC ReadWriteOncePod 达到 GA
  • NodeExpandSecret 特性达到 GA
  • kube-proxy 有了个 nftables 的新后端

这就是我觉得 Kubernetes v1.29 中主要值得关注的内容了。
下次再见!

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

TheMoeLove

相关文章

KubeSphere 部署向量数据库 Milvus 实战指南
探索 Kubernetes 持久化存储之 Longhorn 初窥门径
征服 Docker 镜像访问限制!KubeSphere v3.4.1 成功部署全攻略
那些年在 Terraform 上吃到的糖和踩过的坑
无需 Kubernetes 测试 Kubernetes 网络实现
Kubernetes v1.31 中的移除和主要变更

发布评论