背景
Kubernetes 遵循着 N-2 支持原则,即只有最新的三个主要版本才能获得安全和错误修复更新。此外,大约每 15 周发布一个新版本。在这样的前提下,从 Kubernetes 1.19 开始,每个版本可以获得长达 14 个月的技术支持。您可以从下图中观察到最近版本的支持周期。
让我们来看看 Ubuntu 这个传统项目的技术支持周期。对于 LTS 版本来说,可以获得将近 10 年的技术支持。因此,在使用 K8s 时,必须明白每年至少需要进行一次升级是不可避免的。否则,当遇到安全或功能问题时,将无法获得更新并进行安装使用
说实话,这样的速度有点累人 🥲 尤其是K8s的版本更新也不仅仅是K8s本身的更新,最近几个版本更新涉及的东西也不少。一开始我很好奇其他公司真的能够跟上至少一年一次的更新吗?于是我打开了AKS、GKE和EKS的支持版本页面,发现GKE在1.19版本支持到2022年6月,而EKS目前还支持1.18版本。所以答案是业界实际上跟不太上这一年一次的更新速度 😂 但是相对频繁的更新还是无法避免的,所以我想通过这篇文章记录下自己在更新K8s时的一些步骤和经验。
总的来说
在进行升级之前,有哪些前期准备工作需要完成呢?
🛠️ 仔细阅读更新日志
首先,您需要仔细阅读 Kubernetes 官方社区发布的每个版本的"更新文件"。例如,如果您想将 K8s 1.21 升级到 1.23,您应该查看 1.22 和 1.23 的 CHANGELOG,详细研究其中的变更内容,找出对您所使用的 K8s 集群有哪些影响。
🛠️ 沟通与日程安排
由于管理和使用K8s集群的人员可能不是同一群人,例如SRE负责搭建和管理K8s集群,而后端/前端工程师负责在其上部署应用服务,因此在确定受影响范围后,如果需要其他团队或部门进行相应的修改,就需要开始沟通如何安排人力资源和制定时间表
🛠️ 生产和非生产 K8s 集群
接下来,我们需要至少有两个/群K8s集群,分别是生产环境和非生产环境。因为在升级时,我们必须先在非生产环境中运行一段时间,才能有信心在生产环境中进行升级。即使我们已经仔细查看了CHANGELOG,并且已经修改了需要修改的内容,但只有通过实际尝试,我们才能知道升级后可能会遇到什么问题。如果直接在生产环境中进行升级,可能会对线上服务造成影响。接下来,我们将更深入地讨论可能在K8s升级中遇到的一些问题,例如..
升级时如何妥善处理已弃用的 API?
👉 如何应对即将在 K8s 1.24 版本中被移除的 Dockershim?
cgroups v2 真的已经可以使用了吗?
废弃的API
大家在管理K8s Manifest的时候应该都有发现有一个参数叫做 apiVersion
,因为K8s的API一直都在不断地更新,所以旧的API就会慢慢地被弃用,最后被删除掉。以K8s 1.22为例,就有一批API要被删除掉(参考资料)。那么,当遇到这种情况时,我们应该如何升级呢?
🛠️ 存在 K8s 资源
根据官方文件和问题,我们可以发现在K8s内存在许多资源不需要为了升级而进行修改,因为升级后,K8s会自动进行向下兼容。例如,我之前使用的extensions/v1beta1或networking.k8s.io/v1beta1 apiVersion创建的Ingress,在升级到K8s 1.22后仍然能够正常运行。
🛠️ K8s 资源管理器
然而,一旦升级完成,协助建立K8s资源的角色如果想要对该K8s资源进行新增、修改或删除操作,就会遇到问题。接下来,我们以Ingress为例进行讨论:
👉 Kubernetes清单文件
假设当初是用静态的 K8s Manifest 去将此 Ingress 建立起来的,例如使用 Helm Chart,当想要使用同样的 Helm Chart 去对 Ingress 做新增/修改/删除时就可能会遇到问题,解决办法就是将 Helm Chart 内使用到的 apiVersion 修改成新的版本 networking.k8s.io/v1,或是使用下面这种可以根据不同的 K8s 版本去自动决定 apiVersion 的方式 (参考)
arduino
{{/* Get Ingress API Version */}}
{{- define "kube-prometheus-stack.ingress.apiVersion" -}}
{{- if and (.Capabilities.APIVersions.Has "networking.k8s.io/v1") (semverCompare ">= 1.19-0" (include "kube-prometheus-stack.kubeVersion" .)) -}}
{{- print "networking.k8s.io/v1" -}}
{{- else if .Capabilities.APIVersions.Has "networking.k8s.io/v1beta1" -}}
{{- print "networking.k8s.io/v1beta1" -}}
{{- else -}}
{{- print "extensions/v1beta1" -}}
{{- end -}}
{{- end -}}
👉 控制器或操作员
假设 Ingress 是由 Controller 或是 Operator 负责管理的话,那就要确保他们已经支持新版的 apiVersion,然后去研究是否需要升级该 Controller 或是 Operator。当使用静态的 K8s Manifest 来建立 K8s 资源,在遇到不支持的 apiVersion 的情况下,可以很直观地立即发现。但当使用 Controller 或是 Operator 来管理 K8s 资源时,就需要查看日志或进行测试,以确认是否正常运作
🛠️ 如何找到已弃用的 API
通常部署在K8s集群里面的东西有一大堆,有些时候升级K8s版本会有API即将要Deprecated有些版本却没有,要怎么很快速地知道目前在K8s集群内的资源在升级到某个K8s版本后,有使用到即将Deprecated的API版本呢?这时候要推荐一个开源工具Kube No Trouble(kubent)给大家,安装完之后,只要在命令提示列输入kubent,就可以从输出画面中得知哪一些K8s资源需要特别去关照,不会像是在大海捞针一般
markdown
$./kubent
6:25PM INF >>> Kube No Trouble `kubent` <<<
6:25PM INF Initializing collectors and retrieving data
6:25PM INF Retrieved 103 resources from collector name=Cluster
6:25PM INF Retrieved 132 resources from collector name="Helm v2"
6:25PM INF Retrieved 0 resources from collector name="Helm v3"
6:25PM INF Loaded ruleset name=deprecated-1-16.rego
6:25PM INF Loaded ruleset name=deprecated-1-20.rego
__________________________________________________________________________________________
>>> 1.16 Deprecated APIs <<<
------------------------------------------------------------------------------------------
KIND NAMESPACE NAME API_VERSION
Deployment default nginx-deployment-old apps/v1beta1
Deployment kube-system event-exporter-v0.2.5 apps/v1beta1
Deployment kube-system k8s-snapshots extensions/v1beta1
Deployment kube-system kube-dns extensions/v1beta1
__________________________________________________________________________________________
>>> 1.20 Deprecated APIs <<<
------------------------------------------------------------------------------------------
KIND NAMESPACE NAME API_VERSION
Ingress default test-ingress extensions/v1beta1
移除Dockershim
大家应该或多少有听说过 K8s 1.24 即将完全移除 Dockershim。而在即将推出的 1.24 版本中,官方特地发布了一篇文章来提醒大家这个事情。我自己在一两年前稍微研究过,得出的结论是...如果不是负责管理 Self-Hosted K8s Cluster 的人,应该不太需要关注这个事情。但是,如果组织内需要自己搭建 K8s Cluster,那么在升级到 K8s 1.24 之前就要小心谨慎(参考资料)。
🛠️ 当前容器运行时
首先要做的当然是先确定目前使用的是哪一种容器运行时。如果查询结果是 Dockershim,那就要在升级到 1.24 之前将其更换成其他的容器运行时。
csharp
~$ kubectl get nodes -o wide# For dockershim
NAME STATUS VERSION CONTAINER-RUNTIME
node-1 Ready v1.16.15 docker://19.3.1
...# For containerd
NAME STATUS VERSION CONTAINER-RUNTIME
node-1 Ready v1.19.6 containerd://1.4.1
...
🛠️ 其他容器运行时
如果你想直接放弃使用Docker Engine,可以停用或直接移除K8s节点上的Docker Engine,然后安装containerd、CRI-O等其他容器运行时(参考)
👉 或者可以使用 cri-dockerd 替代 dockershim,并继续使用 Docker Engine(参考)
目前我会先停留在1.23版本,继续使用Dockershim一段时间,用时间换取空间来研究containerd + gVisor的可行性和稳定性。等到研究完成后,再升级到1.24版本并告别Dockershim。届时,我会将心得写成另一篇文章分享出来。
cgroups v1 到 v2
我们提到了cgroups v2的许多优点,那么在K8s的哪个版本可以开始使用cgroups v2呢?理论上是K8s 1.19(参考),但实际上我建议可以再等一等。我也提到了遇到的一些小问题,后来在深入研究之后发现问题出在cAdvisor身上。
cAdvisor目前嵌入在kubelet中,它是用来让用户了解容器资源使用情况的工具。从它的发布页面可以看到有很多关于cgroups v2的错误修复。目前最新的K8s 1.23.5使用的cAdvisor版本是0.43,而cAdvisor 0.43与cgroups v2之间还存在一些问题。看来要等到K8s 1.24才会更新到cAdvisor 0.44。考虑到这些问题还没有得到很好的解决并且没有经过更多人的测试,我建议先稍微等一下,等几个月后K8s 1.24被更多使用cgroups v2的人测试过没有问题再考虑切换cgroups v1到v2也不迟。