一.故障背景:
最近在进行IDC的虚拟化环境转k8s的工作,目前已经有二十几个应用已经转到了k8s环境,在最近的一次操作中,在删除测试命名空间时,误删除线上的命名空间了,导致整个命令空间下的十几个应用的资源,其中包括deployment、confmigmap、secret、statefulSet、service、cronjob被删除掉。当回车键敲出的那一瞬间,脑子处于一片空白,我是谁?我在哪里?我在干什么?我为什么要这样操作?当我执行命令:
kubectl get deploy -n ts-app
kubectl get service -n ts-app
kubectl get pod -n ts-app
kubectl get cm -n ts-app
结果都是显示:
No resources found in ts-app namespace
的那一刻,我已经感觉到自己犯下了一个非常致命且愚蠢的错误。后悔、懊恼、紧张、心慌在这一瞬间充满我的脑袋。
二.方案应对:
在经过短暂的心里挣扎过后,告诉自己需要冷静应对,需要快速想出一个解决方案:
首先,想到的是回滚,可是突然明白,deployment滚动更新是可以回滚,可删除命名空间是没有回滚的。oh,my god!,刺激!!
其次,是想到用之前上线k8s环境时的yaml配置文件,可是这批配置文件虽然都有,但是可能存在和线上配置不一致、而且分散在不同的应用和路径下,想快速恢复比较困难。
最后,我想到了备份,对,就是备份,在搭建好k8s的时候,我已经做了两个备份,一个是针对etcd的备份,一个是针对yaml配置文件的备份配置了一个cronjob。接下来就是需要选择用何种方法去恢复备份了。
原本我是打算采用etcd的备份进行恢复,当时当我通过以下命令逐个命名空间再次确认时:
kubectl get node
kubectl get ns
kubectl get deploy,svc,cm,secret,pod -n xxx
确认了影响的就只是我删除的命名空间:ts-app。该命名空间下有十几个应用,几十个service,deployment,有上百个pod。就在这时候,我再次找了故障告警群来确认看看影响的范围,是否有大量的故障信息报出来,平时我们的每个应用都会有URL检测告警,每隔三分钟检测一次,超时30s,重试三次以后就告警出来。这时、发现我们的告警居然还没告出来,此时我是疑惑的,为什么还没告出来,难道服务没有问题?可当我用域名在浏览器访问的时候,脸又被扇了一次,网站无法打开。这时,我再一次提醒自己,不要抱有幻想,赶紧恢复备份,勇敢的面对这一切,以最快的速度减少故障时间。
如果采用etcd的恢复,需要先停掉api-server,停掉etcd,恢复etcd的镜像,再启动etcd和api-server,可能还需要继续重启一下相关的kube-controller-manager,kube-scheduler,kubelet服务,这样折腾一轮下来,肯定至少20-30分钟了,而且其他正常的命名空间下的服务也会受影响。经过思考,还是采用yaml配置文件的备份。
三.恢复过程:
这里说明一下yaml的备份情况,yaml配置过程是先获取命名空间列表,然后再根据命名空间,获取该命名空间下的deploy,service,cronjob,ing,cm,secret,serviceAccount,sts,pvc,hpa 资源进行备份。然后根据命名空间、资源类型、资源名称的目录结构进行备份,每小时备份一次,并将备份目录根据时间进行压缩、上传到对象存储 和 NAS存储各存一份,备份脚本参考:
Root="/data/yaml"
clusterName="yt-pcauto"
nameSpaces=$(kubectl get ns|grep -v "NAME"|awk '{print $1}'|xargs)
types="deploy service cronjob ing cm secret serviceAccount sts pvc hpa"
for ns in ${nameSpaces[@]}
do
for tps in ${types[@]}
do
mkdir $Root/$clusterName/$ns/$tps -p
for name in $(kubectl get $tps -n $ns|awk '{print $1}'|grep -v NAME)
do
kubectl get $tps $name -n $ns -o yaml > $Root/$clusterName/$ns/$tps/${name}.yaml
done
done
done
当我决定采用yaml配置进行恢复时,首先就是下载备份,可是由于当时心里比较紧张、一时间却突然忘记存放到哪个存储里面了。最终找了好几次才下载到备份文件。当下载了备份以后,解压,并提取出对应命名空间ts-app下的路径以后,先通过命令创建命名空间,然后再apply yaml配置文件,参见如下命令:
kubectl create ns ts-app
find * -type f|while read ff;do kubectl apply -f $ff;done
循环apply所有的yaml配置文件。等命令执行完,正当我松一口气去检查pod的时候,发现pod都没有启动,状态是:ImagePullBackOff。这是才明白新创建的命名空间拉取镜像是需要进行全校校验的。由于我们的镜像是放在阿里云,需要进行权限校验以后才能拉取镜像,在命名空间删除的时候,将对应的serviceAccount和secret给删除了。所以,有一些资源在创建以后,会提示拉取镜像失败,在执行apply这条命令以后,后边的很少资源是有正常拉起的,很多的pod都挂起。如果需要快速将pod 拉起的话,需要删除挂起的pod,让其重新拉取镜像进行启动。随后执行了一条批量删除pod的命令:
get pod -n ts-app|grep "ImagePullBackOff"|awk '{print $2}'|while read pp;do kubectl delete pod $pp -n ts-app;done
该命令是将ImagePullBackOff的pod进行删除。在执行完该命令以后,pod也比较快的启动了。当所有的pod都正常Running的时候,心里悬着的心才有了着落。接下来就是检查服务是否正常运行,将每个应用的域名都访问一遍以后,才真正确认到所有的服务已恢复正常。
此时,再去看URL的告警群,有7、8个告警信息,主要体现在四五个应用上,平时出现服务有问题时,一般都会持续不断的发出告警信息。而这次的告警这么少,主要有几个原因:
- 从删除到pod完全拉起,整个过程大概花了9分钟的样子。
- 大部份被删除的应用,刚好过了检测周期,再加上超时时间和重试次数的关系,才没有出现大批量的告警信息。
当服务完全恢复,确认都没问题时,感觉自己像是从鬼门关去游了一圈回来。从发现问题到恢复问题这10分钟的时间,没有用户或相关其他运维、开发人员留意到此次故障,这也是我们后续的流程中需要完善的。
四.经验总结:
经过这么一次故障处理,自己也总结了一些经验:
1、备份:作为运维人员,首要的任务就是对现有环境的配置、数据等做一个定期的全量和增量的备份,关键时刻是真能救你一命。
2、权限:权限的控制是特别有必要的,针对一些能威胁生产环境的用户、操作、都需要进行比较严格的权限控制,平时的操作就用普通用户、或者只读的权限。但对于修改、删除这一类比较危险的事情,只需要掌握在少数人手中即可。多一个人掌控,就多一分危险。
3、删除:运维在操作删除这种毁灭性的操作时,一定要慎重、再慎重、确认再确认。删除前最好是确认是否有备份、是否能恢复。然后再确认是否删错、是否真的需要删除。要不然可没有后悔药吃。
4、告警:通过这次故障,也对我们的故障告警进行了一次测试,也就是说当故障发生时,告警机制比较滞后,这对于服务要求特别高的企业或团队,是绝对不能接受的。接下来得去优化一下告警的机制了。
5、上报机制:对于领导来说,如果发生了故障,领导是需要你第一时间进行上报的。针对出现比较重大的故障的时候,如果不能在最短的时间内进行恢复的话,第一时间上报领导,否则会被叼死,当然出现这么大故障是自己导致的话,也会被叼死。好像横竖都是死,那这种情况下只有深呼吸,放松心态,快速的相处应对办法才是正道。
6、缩短恢复流程:针对一些比较负载的备份恢复过程,可以通过写一些自动化的脚本来处理,当故障发生时,只需要执行对应的脚本就能快速的恢复。
7、定期的故障演练:对于一些非常重要的应用,可以安排模拟一些模拟故障演练、通过演练能熟悉整个故障的恢复过程,对于一些命令、流程等能够更熟悉,这样在真正出现故障的时候,能够较快的缩短故障时间。
8、鸡蛋别放一个篮子:这件事件中,有个问题就是单一命名空间下放了太多的资源,所以建议时按照应用划分到不同的命名空间。另外也需要在上线时考虑一下应用的垮集群部署架构、