Kubernetes v1.29 新特性一览

2023-12-14 10:10:49 浏览数 (2)

大家好,我是张晋涛。

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 的校验规则

例如:

代码语言:javascript复制
---
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 中运行,我们来看一个例子:

代码语言:javascript复制
---
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 来体验它 https://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),

  • 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 段。

代码语言:javascript复制
 ┌─────────────┬─────────────────────────────────────────────┐
 │   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 的一部分,并且指定其具有“始终重启”策略。

代码语言:javascript复制
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 操作,例如:

代码语言:javascript复制
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 的有效时间,尽可能的减少攻击面。

代码语言:javascript复制
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

以下是一个示例的输出:

代码语言:javascript复制
# 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 已经实际上废弃了。

代码语言:javascript复制
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。(关于它们之间的关系有兴趣的小伙伴可以搜索下相关内容)

以下是一个示例的输出:

代码语言:javascript复制
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 中主要值得关注的内容了。下次再见!

0 人点赞