大家好,我是张晋涛。
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 中运行,我们来看一个例子:
---
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.plugin
,self
和 oldSelf
代表了变化前后的资源对象, 一旦定义了 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 只支持 exec
和 httpGet
这两种,这个 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 的有效时间,尽可能的减少攻击面。
代码语言: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 中主要值得关注的内容了。下次再见!