Skip to content

17 集群升级与数据备份

你好,我是雪飞。

刚开始使用 K8s 的时候,我们团队搭建了 V1.18 稳定版本的 K8s 集群,在上面部署了各种业务应用,这套环境长时间没有进行集群升级。随着业务迭代加快和系统安全需求提升,我们发现老版的 K8s 集群无法兼容一些新组件,因此必须要对集群升级。预期升级后的版本是 V1.26 版本,然而,由于版本跨度过大,升级时遇到了许多问题,花费了大量时间。因此,及时升级集群非常重要,避免留下技术债。

集群升级能够带来很多好处,比如确保集群能利用最新版本的新功能、性能提升和安全补丁,可以增强集群的稳定性和可靠性,同时提升兼容性和扩展性,满足不断变化的技术与业务需求。接下来,我们将深入了解 K8s 的升级过程。

K8s 升级说明

在 K8s 升级过程中,首先需要备份集群数据。由于所有 K8s 集群相关数据都存储在 etcd 数据库中,所以我们需要先对 etcd 中的数据进行整体备份。如果集群升级过程中出现意外,可以利用 etcd 的快照数据进行回退恢复。

接下来,按照顺序先升级管理节点,即 Master 节点,然后再升级工作节点,也就是其他Worker 节点。为避免业务服务中断,通常采用滚动升级方式,逐个或分批升级工作节点,确保部分节点仍能正常运行,从而保障节点上的 Pod 能持续对外提供服务。

在升级单个节点时,需要先清空节点上的 Pod 并设置节点为不可调度状态,待升级完成后再取消这一设置,确保整个集群的平稳过渡。

注意: 在整个升级过程中,需要更新 kubeadm、kubelet 和 kubectl 等工具和组件,K8s 建议这三个组件的版本在升级时保持一致。

备份 etcd 数据

在集群升级前,备份 etcd 数据至关重要,实际上,日常运维中也应该定期备份 etcd 数据,从而应对灾难恢复场景(如管理节点全部故障)。etcd 的数据备份文件包含所有 K8s 资源对象、配置参数、状态信息和其他数据,我们可以通过 etcdctl 工具导出数据备份,并将其加密存储在安全的服务器备份目录中,以确保在集群故障时能够进行集群恢复。下面我们来动手备份和恢复 etcd 数据。首先安装 etcdctl 工具。

安装 etcdctl 命令工具

由于 etcd 组件是部署在管理节点上,所以我们备份和恢复操作也是在管理节点上进行。在管理节点上执行安装 etcd 的命令,会自动安装 etcdctl 的命令工具。

[root@k8s-master ~]# yum install -y etcd
[root@k8s-master ~]# etcd --version   # 查看安装的etcd版本
etcd Version: 3.3.11
Git SHA: 2cf9e51
Go Version: go1.10.3
Go OS/Arch: linux/amd64

备份 etcd 数据

安装好 etcdctl 工具,执行 “etcdctl snapshot save” 命令,对 etcd 数据进行备份。

[root@k8s-master ~]# ETCDCTL_API=3 etcdctl snapshot save snap.db --endpoints=https://127.0.0.1:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key
Snapshot saved at snap.db

备份命令参数有点多,我来解释一下:

  • ETCDCTL_API=3:设置环境变量,指定 etcdctl 工具使用的 API 版本为 3。
  • etcdctl snapshot save snap.db: 执行全部数据的快照(快照就是一次数据备份)保存操作,快照文件命名为 snap.db。
  • –endpoints:指定集群 etcd 组件的 IP 和端口。由于备份命令就在管理节点执行,所以etcd 组件的 IP 就是本地 IP,端口号为 2379 端口,使用 HTTPS 协议。
  • –cacert:指定集群 etcd 组件的 CA 证书路径,用于验证身份。
  • –cert:指定集群 etcd 组件的证书路径。
  • –key:指定集群 etcd 组件的私钥路径。

部署 K8s 集群时,会自动安装 etcd 组件的证书,默认文件路径为:“/etc/kubernetes/pki/etcd/ca.crt”、“/etc/kubernetes/pki/etcd/server.crt”、“/etc/kubernetes/pki/etcd/server.key”。我们可以在管理节点中看到这三个文件。

[root@k8s-master ~]# cd /etc/kubernetes/pki/etcd
[root@k8s-master etcd]# ls
ca.crt  ca.key  healthcheck-client.crt  healthcheck-client.key  peer.crt  peer.key  server.crt  server.key

备份命令执行成功后,就可以在当前目录下看到名为 “snap.db” 的备份文件。

[root@k8s-master ~]# ls
snap.db

恢复 etcd 数据

如果集群管理节点出现故障,修复后可能导致 etcd 中存放的数据丢失,这时我们可以使用备份数据来恢复 etcd 数据,从而回退到备份时的集群工作状态。K8s 官方建议,在执行恢复数据操作前,应先停止所有系统核心组件,如 Scheduler 和 Controller Manager 等,以确保它们不会依赖一些过时数据。以下是恢复 etcd 数据的具体步骤。

第一步:暂停系统组件

由于集群系统组件的部署文件都存放在 “/etc/kubernetes/manifests/” 目录中,并且以静态 Pod 的形式运行,因此我们只需要更改该目录的名称,让 K8s 找不到组件部署的 YAML 文件,从而会自动暂停这些系统组件运行。 注意: 修改目录之后,kubectl 命令也将无法使用。

[root@k8s-master ~]# mv /etc/kubernetes/manifests /etc/kubernetes/manifests.bak

第二步:备份当前etcd 的数据目录

etcd 数据文件位于 “/var/lib/etcd/”。在恢复数据前,需要将当前数据目录修改为另一目录,避免数据被直接覆盖。

[root@k8s-master ~]# mv /var/lib/etcd/ /var/lib/etcd.bak

第三步:执行恢复命令

在刚才备份文件 snap.db 所在目录执行 “etcdctl snapshot restore” 恢复数据命令,指定恢复的数据存放至 “/var/lib/etcd” 目录中。

[root@k8s-master ~]# ETCDCTL_API=3 etcdctl snapshot restore snap.db --data-dir=/var/lib/etcd
2024-07-03 09:35:12.892662 I | mvcc: restore compact to 3393178
2024-07-03 09:35:12.901833 I | etcdserver/membership: added member 8e9e05c52164694d [http://localhost:2380] to cluster cdf818194e3a8c32

第四步:重启系统组件

备份数据恢复后,将集群系统组件的部署文件目录改回为 “/etc/kubernetes/manifests/”,这样所有系统组件会自动重新运行。最后重启所有节点上的 kubelet。

[root@k8s-master ~]# mv /etc/kubernetes/manifests.bak /etc/kubernetes/manifests
[root@k8s-master ~]# systemctl restart kubelet  # 所有节点重启 kubelet

可以看到,所有集群系统组件 Pod 开始重新启动,稍等片刻,所有 Pod 加载正常。这时,集群状态恢复到 etcd 备份数据时的状态。

[root@k8s-master ~]# kubectl get pod -n kube-system
NAME                                 READY   STATUS    RESTARTS      AGE
coredns-66f779496c-pqwpr             1/1     Running   0             15d
coredns-66f779496c-x7xhf             1/1     Running   0             15d
etcd-k8s-master                      1/1     Running   0             17d
kube-apiserver-k8s-master            1/1     Running   0             10d
kube-controller-manager-k8s-master   1/1     Running   0             10d
kube-proxy-84jp5                     1/1     Running   1 (10d ago)   10d
kube-proxy-r2mwc                     1/1     Running   0             10d
kube-proxy-vchhh                     1/1     Running   0             10d
kube-scheduler-k8s-master            1/1     Running   0             10d
metrics-server-7f676b86dc-9lpsb      1/1     Running   0             11d

升级控制面主节点

我们掌握了 ectd 的数据备份,就可以在集群升级前做好数据备份,然后开始升级集群节点。

先从管理节点开始升级,确保升级版本不要跨度太大。通过 “kubectl get node” 命令查看当前我的集群节点版本为 V1.28.0,本次将升级到 V1.28.2 版本。

[root@k8s-master ~]# kubectl get node
NAME          STATUS   ROLES           AGE   VERSION
k8s-master    Ready    control-plane   17d   v1.28.0
k8s-worker1   Ready    <none>          15d   v1.28.0
k8s-worker2   Ready    <none>          15d   v1.28.0

先升级 k8s-master 节点。在 k8s-master 节点上依次执行命令步骤如下:

# 1、查找最新版本号,目前能使用的最新版本是 1.28.2
[root@k8s-master ~]# yum list --showduplicates kubeadm
Loaded plugins: fastestmirror, langpacks
Loading mirror speeds from cached hostfile
Installed Packages
kubeadm.x86_64              1.28.0-0                   @kubernetes
Available Packages
.......
kubeadm.x86_64              1.28.2-0                   kubernetes

# 2、升级kubeadm
[root@k8s-master ~]# yum install -y kubeadm-1.28.2-0

# 3、驱逐节点上的Pod,且设置节点不可调度
# --ignore-daemonsets 表示忽略 Daemonset 类型的 Pod
[root@k8s-master ~]# kubectl drain k8s-master --ignore-daemonsets
node/k8s-master cordoned
Warning: ignoring DaemonSet-managed Pods: calico-system/calico-node-qd57g, calico-system/csi-node-driver-fd2ws, kube-system/kube-proxy-84jp5
node/k8s-master drained

# 4、检查集群是否可以升级,并获取可以升级的版本
[root@k8s-master ~]# kubeadm upgrade plan
[upgrade/config] Making sure the configuration is correct:
[upgrade/config] Reading configuration from the cluster...
......
You can now apply the upgrade by executing the following command:
        kubeadm upgrade apply v1.28.11
......

# 5、执行升级
[root@k8s-master ~]# kubeadm upgrade apply v1.28.2
[upgrade/config] Making sure the configuration is correct:
[upgrade/config] Reading configuration from the cluster...
.......

# 6、升级 kubelet 和 kubectl
[root@k8s-master ~]# yum install -y kubelet-1.28.2-0 kubectl-1.28.2-0
Loaded plugins: fastestmirror, langpacks
Loading mirror speeds from cached hostfile
......

# 7、重启kubelet
[root@k8s-master ~]# systemctl daemon-reload
[root@k8s-master ~]# systemctl restart kubelet

# 8、取消设置节点不可调度,节点重新上线
[root@k8s-master ~]# kubectl uncordon k8s-master
node/k8s-master uncordoned

注意: 在升级节点前,需要先清空节点上的所有 Pod 并设置为不可调度。升级完成后,再将节点重新设置为可调度状态。

主节点升级成功后,可以通过 “kubectl get node” 命令查看升级后的版本。我们看到主节点已经变成 1.28.2 版本。

[root@k8s-master ~]# kubectl get node
NAME          STATUS   ROLES           AGE   VERSION
k8s-master    Ready    control-plane   17d   v1.28.2
k8s-worker1   Ready    <none>          15d   v1.28.0
k8s-worker2   Ready    <none>          15d   v1.28.0

升级工作节点

下一步,我们开始升级工作节点。为了确保集群不会完全停止运行,通常会逐个依次升级工作节点,这样在升级过程中仍有正常的工作节点可以维持应用 Pod 的运行。

我们先升级 k8s-worker1 节点。升级成功后,再升级 k8s-worker2 节点。工作节点的升级步骤完全相同,只需要在不同节点上执行一系列命令。

注意: 驱逐节点和设置节点可调度时需要替换为每个节点自己的名称,而且这两步操作都在管理节点上通过 kubectl 命令操作。步骤如下:

# 1、升级 kubeadm
[root@k8s-worker1 ~]# yum install -y kubeadm-1.28.2-0
......
Updated:
  kubeadm.x86_64 0:1.28.2-0
Complete!

# 2、驱逐节点上的Pod,且设置节点不可调度
# 注意:在管理节点上执行
# --ignore-daemonsets 表示忽略 Daemonset 类型的 Pod
# --delete-emptydir-data 表示删除 Pod 用到的本地数据
[root@k8s-master ~]# kubectl drain k8s-worker1 --ignore-daemonsets --delete-emptydir-data --force
node/k8s-worker1 cordoned
Warning: ignoring DaemonSet-managed Pods: calico-system/calico-node-8g7nn, calico-system/csi-node-driver-hr6ng, kube-system/kube-proxy-vchhh; deleting Pods that declare no controller: default/my-nginx-resources-limit

# 3、升级kubelet配置
[root@k8s-worker1 ~]# kubeadm upgrade node
[upgrade] Reading configuration from the cluster...
[upgrade] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
[preflight] Running pre-flight checks
[preflight] Skipping prepull. Not a control plane node.
[upgrade] Skipping phase. Not a control plane node.
[upgrade] Backing up kubelet config file to /etc/kubernetes/tmp/kubeadm-kubelet-config2378199440/config.yaml
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[upgrade] The configuration for this node was successfully updated!
[upgrade] Now you should go ahead and upgrade the kubelet package using your package manager.

# 4、升级kubelet和kubectl
[root@k8s-worker1 ~]# yum install -y kubelet-1.28.2-0 kubectl-1.28.2-0
......
Updated:
  kubectl.x86_64 0:1.28.2-0            kubelet.x86_64 0:1.28.2-0

Complete!

# 5、重启kubelet
[root@k8s-worker1 ~]# systemctl daemon-reload
[root@k8s-worker1 ~]# systemctl restart kubelet

# 6、取消设置节点不可调度,节点重新上线。
# 注意:在管理节点上执行
[root@k8s-master ~]# kubectl uncordon k8s-worker1
node/k8s-worker1 uncordoned

所有节点升级完成后,我们再查看节点信息,确认所有节点已更新至 1.28.2 版本。

[root@k8s-master ~]# kubectl get node
NAME          STATUS   ROLES           AGE   VERSION
k8s-master    Ready    control-plane   18d   v1.28.2
k8s-worker1   Ready    <none>          16d   v1.28.2
k8s-worker2   Ready    <none>          16d   v1.28.2

故障回滚

如果 kubeadm upgrade 在执行过程中失败,且没有自动回滚,比如因为节点意外关闭,这时,你可以直接再次运行 kubeadm upgrade 命令。这个命令具有幂等性,可以确保最终的状态与你的预期保持一致。

在升级过程中,kubeadm 会将数据备份到 “/etc/kubernetes/tmp” 目录下的以下文件夹:

  • kubeadm-backup-etcd--
  • kubeadm-backup-manifests--

kubeadm-backup-etcd 包含集群 etcd 组件数据的备份。如果 etcd 升级失败且自动回滚也无法修复,可以将此文件夹中的内容复制到 “/var/lib/etcd” 目录中进行手动恢复。

kubeadm-backup-manifests 包含集群组件的静态 Pod 的 YAML 文件的备份版本。如果升级失败且无法自动回滚,则可以将此文件夹中的内容复制到 “/etc/kubernetes/manifests” 目录中以实现手动恢复。

小结

今天,我给你介绍了 K8s 集群升级的重要性和具体步骤。集群升级可以保持集群技术的先进性和安全性,从而利用最新功能、性能改进和安全补丁,增强集群的稳定性、可靠性、兼容性和扩展性。

升级前需要备份 etcd 数据,先升级管理节点(Master Node),再升级工作节点(Worker Node),工作节点采用滚动升级的方式,逐个或分批升级,避免业务中断。

然后我带你动手完成了 etcd 组件的数据备份和数据恢复,需要先安装 etcdctl 命令工具,然后使用 “etcdctl snapshot save” 和 “etcdctl snapshot restore” 这两个命令进行数据备份和恢复。在数据恢复前需要先停止所有系统核心组件,确保它们不会依赖一些过时数据,等数据恢复完成再重启组件。

最后,我给你列出了管理节点和工作节点的升级步骤,按照操作步骤执行命令就可以完成集群节点的版本升级,需要注意升级过程中需要先驱逐节点上的 Pod,同时设置节点不可调度,升级完成后再取消设置。

思考题

这就是今天的全部内容,在 CKA 考试中会考到 etcd 备份和恢复命令相关的知识点。我给你留一道练习题。

写一个脚本,每天定时备份 etcd 数据。

相信经过动手实践,会让你对知识的理解更加深刻。