kubekey-etcd备份脚本bug分析及踩雷应对
意外发现
3月8日,我在google搜索etcd相关的信息,一个网页标题吸引了我的注意力,按照通常对这种标题党内容的认知,大概率里面不会有什么有用的信息,但是还是忍不住好奇心点了进去一探究竟。
结果没想到这个贴子里提到的bug恰好也存在于我司使用的kubekey版本中。
bug分析
简单概括这个bug的引发原因,也是一句看起来标题党的内容:一个;引发的血案(震惊:代码里一个错误的;竟导致系统全部被删)。
详细拆解该bug的细节如下:
-
kubekey集成了一个备份etcd小服务,在部署完k8s后,会部署一个基于systemd的timer,定时运行一个脚本,从而备份etcd的数据。
-
引发bug的就是该脚本
#!/bin/bash ETCDCTL_PATH='/usr/local/bin/etcdctl' ENDPOINTS='{{ .Etcdendpoint }}' ETCD_DATA_DIR="/var/lib/etcd" BACKUP_DIR="{{ .Backupdir }}/etcd-$(date +%Y-%m-%d-%H-%M-%S)" KEEPBACKUPNUMBER='{{ .KeepbackupNumber }}' ETCDBACKUPSCIPT='{{ .EtcdBackupScriptDir }}' ETCDCTL_CERT="/etc/ssl/etcd/ssl/admin-{{ .Hostname }}.pem" ETCDCTL_KEY="/etc/ssl/etcd/ssl/admin-{{ .Hostname }}-key.pem" ETCDCTL_CA_FILE="/etc/ssl/etcd/ssl/ca.pem" [ ! -d $BACKUP_DIR ] && mkdir -p $BACKUP_DIR export ETCDCTL_API=2;$ETCDCTL_PATH backup --data-dir $ETCD_DATA_DIR --backup-dir $BACKUP_DIR sleep 3 { export ETCDCTL_API=3;$ETCDCTL_PATH --endpoints="$ENDPOINTS" snapshot save $BACKUP_DIR/snapshot.db \ --cacert="$ETCDCTL_CA_FILE" \ --cert="$ETCDCTL_CERT" \ --key="$ETCDCTL_KEY" } > /dev/null sleep 3 cd $BACKUP_DIR/../;ls -lt |awk '{if(NR > '$KEEPBACKUPNUMBER'){print "rm -rf "$9}}'|sh
-
这个脚本比较有意思也是引发bug的是最后一行:在磁盘未满时,脚本能够按照预期运行,最后一行会检测当前备份目录里的备份数量,如果超过了限制,会自动删除掉超出数量的备份。比如设置只保留4个备份,那么当备份数量大于4后,就会删除最老的备份,只保留最新的4个备份。
-
但是一旦磁盘满后,最后这行代码的运行状况就会大大超出预期:磁盘满后,$BACKUP_DIR变量代表的目录并不存在,但是后面代码仍然会继续运行,会在当前目录开始删文件。那么当前目录是什么呢?从bug的结果来看,当前目录就是/!!!
-
虽然看起来这是一个;导致的bug,但是本质上是脚本缺了一些常见的意识,比如用set避免脚本出错继续执行等
为什么是/

灾后重建
在发现该问题后,我立即上报了该问题,并开始着手对环境中的该脚本进行修复。然而还是有一个环境漏掉了,真实触发了该bug。
蛋疼的是,一开始这个三节点的集群master1发现磁盘满了且/bin等目录丢失后,我没有第一时间回忆起这个bug。导致错失了修复的良机。因为此时etcd3节点还存在两个,数据还可以修复。等到过了一段时间master2又出现同样的状况后,我才突然回想起这个问题。这时由于etcd只有一个节点了,只能重新部署集群,然后用etcd的数据备份进行恢复。
-
部署集群后,参考Etcd备份数据如何做到完美恢复Kubernetes中误删数据呢 - KubeSphere 开发者社区完成etcd数据的恢复。然后观察集群里的pod状况,修复不正常的pod。
-
如果集群重装了,证书改变,部分pod需要删掉重建才能正常running;同样node上的一些状态也需要重启node才能恢复正常。因此最好在数据恢复后进行各类状态的重建,比如node重启、pod重建等。
-
tip:按照层级结构,将上层pod先scale到0,分批次重建。避免node压力过大
参考
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。