大家好,我是张晋涛。
Kubernetes v1.28 是 2023 年的第二个大版本更新,包含了 46 项主要的更新。 而今年发布的第一个版本 v1.27 有近 60 项,所以可以看出来,在发布节奏调整后,每个 Kubernetes 版本中都会包含很多新的变化。
其中 20 个增强功能正在进入 Alpha 阶段,14 个将升级到 Beta 阶段,而另外 12 个则将升级到稳定版。
可以看出来很多都是新特性。
我之前每期的 「k8s生态周报」都有一个叫上游进展的部分,会发一些值得关注的内容。 不过最近太忙,停更了几期,正好趁这篇把这些值得关注的内容聊一下。
Kubernetes 集群可以真正年度升级了
我们知道 Kubernetes 集群的架构中 control plane 和 Node 分离的,虽然用户在部署集群的时候,通常会选择将 control plane 和 Node 部署为相同版本。 但是如果要进行集群版本升级的时候,就会发现 control plane 和 Node 在升级过程中是无法保持版本一致的。
举例来说:
- 我部署了一套 v1.23 版本的 Kubernetes 集群,control plane 和 Node 目前都是 v1.23;
- 升级 control plane 到 v1.24;
- 这个时候 Node 还是 v1.23;
- 最后将 Node 也升级到 v1.24;
在这个升级过程中,control plane 和 Node 有一段时间版本是不一致的,这会产生问题吗?
通常来说,对于大多数软件,如果是这种分离式架构,版本不一致是有可能存在问题的(这里我就不说某些系统/软件了)。 但上述过程很明显是一定存在的,所以 Kubernetes 在这方面专门做了处理和兼容 。我们把这个策略叫作 Kubernetes 的版本偏差策略。
Kubernetes 中允许 control plane 和 Node 之间的版本存在 n-2 的偏差。比如:
- control plane 版本为 v1.27
- Node 版本可以为 v1.27, v1.26 和 v1.25
不过从 v1.28 开始,这个版本偏差策略扩展成了 n-3,比如:
- control plane 版本为 v1.28
- Node 版本可以为 v1.28, v1.27, v1.26 和 v1.25
解释完这个版本偏差策略后,我们来看下这个事情到底有多么重要。
Kubernetes 当前的发布和支持策略,是用户可以在一年内持续升级到最新的补丁版本以获取安全修复,并且只要进行 3 个连续的版本升级,就可以追上最新支持的版本了。
然而,由于 control plane 和 Node 之间测试/支持偏差目前仅限于 n-2 个版本,因此每年的 3 个版本升级将不得不进行两次 Node 的升级,才能保持在受支持范围内。例如,从 v1.24 升级到 v1.27 ,这个场景正好是从 2022 年的第一个版本升级到 2023 年的第一个版本。
- control plane 和 Node 都是 v1.24;
- control plane 升级 v1.24 - v1.25 - v1.26;
- Node 升级 v1.24 - v1.26;
- control plane 升级 v1.26 - v1.27
- Node 升级 v1.26 - v1.27
从这里可以看到 Node 要升级两次,事实上在 Kubernetes 集群的维护过程中,Node 版本的升级带来的影响是很大的, 比如原先运行在这些 Node 上的 Pod 需要重新调度/重建等,虽然理想状况下对于业务应该没什么影响(但实际情况下...) 此外,随着集群规模的增加,这个 Node 升级的工作量也是很大的。
如果使用现在新的 n-3 偏差策略,我们来看看效果,还是从 v1.24 到 v1.27 (此处只是用做举例,从 v1.28 才生效):
- control plane 和 Node 都是 v1.24;
- control plane 升级 v1.24 - v1.25 - v1.26 - v1.27;
- Node 升级 v1.24 - v1.27;
这样的话,Node 只需要升级一次,很明显 节省了很多工作量,并且也可以减少对业务的影响 ,这样也真正实现了 Kubernetes 的年度支持策略。
注意:首个可用 n-3 偏差策略的是 v1.28 ,它允许 control plane 是 v1.28 而 Node 最旧为 v1.25。
关于 Kubernetes 的版本偏差策略的详细说明可以参考 https://kubernetes.io/releases/version-skew-policy/
基于 CEL 的 Admission Control 达到 Beta
对 Kubernetes 中的 Adminssion Control 感兴趣的小伙伴可以看看我之前的文章:理清 Kubernetes 中的准入控制(Admission Controller) | MoeLove
- KEP-3488: Implement secondary authz for ValidatingAdmissionPolicy by jpbetz · Pull Request #116054 · kubernetes/kubernetes
- KEP-3488: Implement Enforcement Actions and Audit Annotations by jpbetz · Pull Request #115973 · kubernetes/kubernetes
Kubernetes 在 v1.26 增加了一项很重要的特性,那就是允许使用 CEL 进行 Admission Control,具体内容可以参考我之前写的文章:Kubernetes v1.26 新特性一览 | MoeLove
其中引入了一个新的资源 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"
但是在当时也只是实现了 KEP-3488 的一部分。
在这个 PR 中是在实现 Authz 的部分,这允许用 CEL 表达式编写复杂的 admission control 规则, 放置在声明式资源中,而不是构建和部署 webhook。
虽然 admission webhook 一直是我们灵活的与第三方工具集成的基石,但是对于新用户来说,它们有很多复杂性,新的 CEL 系统有望接管仅需要对默认规则进行小修改的简单独立案例。
以前,表达式上下文会公开有关当前请求和目标资源的信息,现在通过这个 PR 可以在授权层中动态检查 RBAC 权限。 一些有用的地方可能是使用 RBAC 进行每个字段的更新权限,允许 RBAC 检查特定对象而不使用 resourceNames 系统, 或基于请求者身份限制对程序敏感字段(例如 finalizers )的访问而无需生成复杂的 RBAC 策略。
例如:
authorizer.group('').resource('pods').namespace('default').check('create').allowed() authorizer.path('/healthz').check('GET').allowed() authorizer.requestResource.check('my-custom-verb').allowed()
在 v1.27 还加入了#115973,该功能允许在失败时作为主要操作发出审计日志事件,或者如果需要更多数据,可以编写一个或多个CEL表达式,以提供详细的值, 这些值将发送到审计子系统。
这既可以在开发新策略时提供强大的调试选项,也可以进行运行时分析。 其他 CEL admission 特性包括各种检查,以防止您使用所有这些新功能意外拒绝服务自己的 kube-apiserver,以及改进的类型检查。
本次升级到 Beta 阶段,API version 也就随之升级到了:
admissionregistration.k8s.io/v1beta1
此外,还引入了包括 ValidatingAdmissionPolicy: support namespace access by cici37 · Pull Request #118267 · kubernetes/kubernetes 等在内的特性,进一步将 webhook 推入到一个有限的用例空间中(防止滥用)。
CRD 使用 CEL 进行 Validate 的特性再次达到 Beta
我在 2021 年的 K8S 生态周报| Kubernetes v1.23.0 正式发布,新特性一览 | MoeLove 文章中,曾介绍过 Kubernetes 中引入了 Common Expression Language (CEL) 进行 CRD Validation。该特性计划在 v1.25 达到 Beta,这次是它第二次宣布达到 Beta 了。
例如,某个 CRDs 的内容如下,其中定义了 minReplicas
小于 replicas
并且 replicas
小于 maxReplicas
。
...
openAPIV3Schema:
type: object
properties:
spec:
type: object
x-kubernetes-validations:
- 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.
这次还增加了 [KEP-2876]Add reason and fieldPath into CRD validation rules by cici37 · Pull Request #118041 · kubernetes/kubernetes 这个 PR, 新增了 fieldPath
和 reason
字段的配置,可以更加方便使用。
其中 reason
字段可以传递给调用方的 HTTP status code,目前支持 FieldValueInvalid
, FieldValueForbidden
,FieldValueRequired
, FieldValueDuplicate
。如果未设置,默认使用 FieldValueInvalid
。
与之相关的,还有一个新增的 Alpha 特性 CRD Validation Ratcheting · Issue #4008 · kubernetes/enhancements
非优雅的节点关闭特性达到 GA
当一个节点被关闭但没有被 Kubelet 的 Node Shutdown Manager 检测到时,StatefulSet 的 Pod 将会停留在正在终止状态,并且不能移动到新运行的节点上。
这是因为关闭节点上的 Kubelet 不可用来删除 Pod ,所以 StatefulSet 无法创建具有相同名称的新 Pod 。如果 Pod 使用卷,则 VolumeAttachments 将不会从原始关闭节点中删除,因此这些 Pod 使用的卷将无法附加到新运行的节点上。
结果是,在 StatefulSet 上运行应用程序将无法正常工作。如果原始关闭节重新启动,则 Pods 将被 Kubelet 删除,并在不同运行中创建新 Pods 。如果原始关闭节没有重新启动,则这些 Pods 将永远停留在正在终止状态下。
所谓的非优雅关闭节点的特性就是一种处理未被 Kubelet 的 Node Shutdown Manager 检测到的情况下进行 node 关闭的方法。 在此情况下强制删除 pods ,触发 VolumeAttachments 的删除,并在健康 Node 中创建 pod ,以便应用程序可以继续正常工作。 类似地,在遇到硬件故障或破损操作系统等不可恢复状态时也可以采取此方法。
这个特性的 feature gate 是 NodeOutOfServiceVolumeDetach,在旧版本的 kube-controller-manager 也可以自行开启。
目前它还需要人工操作,可以执行如下操作:
kubectl taint nodes <node-name> node.kubernetes.io/out-of-service=nodeshutdown:NoExecute
这样非正常关闭的节点上的有状态应用的 Pod 只要没有匹配的容忍度,此污点将触发强制删除该节点上的 Pod。附加到关闭节点的持久卷将被分离,并且新的 Pod 将成功创建在另一个运行中的节点上。
我觉得这个特性还是很有用的,不过我早年也处理过很多类似的异常。
其他
- in-tree 的 Ceph RBD, Ceph FS 的插件都废弃了,并且没有迁移 CSI 的计划;
- Sidecar container 的支持达到 alpha 级别;
- PodResources API 达到 GA;
- Node swap 特性达到 Beta, 这是从 v1.22 引入的,有兴趣可以看看我之前的文章;
这就是我觉得 Kubernetes v1.28 中主要值得关注的内容了。 下次再见!
TheMoeLove