Kubernetes之备份与还原
LiuSw Lv6

一.k8s模型

Kubernetes 集群能支撑庞大而又复杂的应用系统,许多用户和团队共享集群,难免会相互影响或冲突。为了避免用户之间的冲突,Kubernetes 引入命名空间概念,在同一个命名空间下各种资源的不能重名,在不同的命名空间下允许重名,实现资源安全隔离。用户在分配给他的命名空间下操作,不用担心影响到别人,也不用担心受别人影响,因为每个用户或者每个团队都有独立的命名空间。

Kubernetes 拥有和保留系统级的命名空间 Kube-system 和 kube-public,未经授权不允许普通用户使用系统命名空间。默认命名空间 default 是公共的,如果没有指定命名空间,用户新建的资源都将建立在 default 命名空间。

Kubernetes 的资源类型包括:

  • 服务 service

  • 部署 deploy

  • 配置 configmap

  • 加密配置 secret

  • 任务 job

  • 定时任务 cronjob

  • 副本集 replicaset

  • 驻留任务集 daemonset

  • 有状态集 statefulset

此处不一一列举。

Kubernetes 的资源配置告诉 Kubernetes 系统,部署(deploy)哪些应用,对外提供哪些服务(service),应用配置参数(configmap)存在哪儿,敏感参数(secret)需要加密吗, 运行一次就结束的任务(job)如何调度,像闹钟一样的定时任务(cronjob)怎么安排,一个节点运行一个且只运行一个的驻留任务(daemonset)支持吗,运行过后希望保留数据及状态的任务(statefulset)。
资源配置赋予 Kubernetes 丰富的资源创建能力,能适应复杂多变的应用环境。

同一个资源类型下,可以配置多个不同的资源实例,而同一个资源实例允许运行多个完全相同的副本,拓展了系统服务能力。例如:供客户使用的 nginx/http 服务和供内部运营人员使用 nginx/http 服务,可以分别配置、分别部署。供客户使用的 nginx/http 服务可以多副本运行,并且按应用负载自适应扩展或者缩减副本数量。

二.k8s集群层级模型

如上所述,Kubernetes 系统可以归纳为多层级的分层模型,从上到下分别是:

  • Kubernete 平台

  • 命名空间 namespace

  • 资源类型 resource type

  • 资源实例 resource instance。

上一级和下一级之间是一对多的关系,而下一级从属于唯一的上级。例如,资源类型为deploy 的应用实例 nginx-web 从属于类型 deploy,而 deploy 从属于某个命名空间 ns-cmft。命名空间可以容纳 deploy、service 和 job 等多种类型的资源。资源实例的副本(实例),是运行时的概念,可以动态创建、动态销毁。

Kubernetes 资源配置构成容器编排的身体骨架,而血肉在运行时填充进去并塑造形状。或者说,资源配置就像是一个具体 Kubernetes 运行时实例的 DNA、基因组。如果所有资源配置确定了,系统的内部结构也基本确定了。这个 Kubernetes 基因组的另一部分是容器镜像,以及容器运行时产生的数据。

三.数据恢复模型

数据恢复是从已备份的数据副本恢复到正在运行的 Kubernetes 系统。

这里的数据副本是指此前备份导出来的 yaml 格式文本文件。Yaml 文件是人类肉眼可读的,也是可以修改的。如果管理员想恢复某个历史时点备份的资源文件,且修改某个错误值,那么从备份的 yaml 文件恢复是比较好的选择。

数据恢复可以是全量恢复,或者部分恢复。全量恢复是从某个时刻的全量备份数据恢复至 Kubernetes 系统。部分恢复只恢复部分数据。

相比数据备份的豪气、大方,数据恢复则要小心谨慎得多。数据恢复一般以部分恢复为主,只对发生故障,而且确认备份副本数据正确有效时才会恢复。在系统发生不可逆转的全面崩溃时,会优先考虑从物理备份恢复,只有在物理备份不可用时,此时才考虑逻辑备份(本文所说的 yaml 备份)。

有时物理备份与逻辑备份配合使用恢复系统也是不错的选项。

部分恢复应当把经过仔细审查通过的 yaml 文件复制到专门的恢复目录(restore),以便于按顺序批量执行,恢复系统数据。

数据恢复应当记录详细的日志,以便事后查询、审计。

四.数据备份与恢复

脚本各个目录位置
备份路径/opt/k8s-backup/
脚本放置路径/opt/k8s-backup/bin
名称空间备份路径/opt/k8s-backup/日期/namespace
备份日志路径/opt/k8s-backup/k8s-backup.log

1.手动备份

备份脚本默认是全量备份,也就是备份 K8s 集群下的所有命名空间下的系统资源数据。有些生产系统,命名空间比较多,运行的服务、容器和 Pod 也多,系统资源的数据量比较大, 备份时间也会比较长。
优先备份是指选择一部分优先级高的重要资源数据、或者经常变化的资源数据,并提供频率更高的备份。

同一用户下的资源数据经常配置在相同的命名空间,按命名空间来识别高优先级的资源数据是一条简便途径。本文的方法能用脚本传入参数,只备份指定命名空间的资源数据,而不会备份其他命名空间下的资源数据,可以用作优先备份。

在命令行下输入备份脚本,默认开始全量备份:

1
2
cd /opt/k8s-backup/bin
./k8s_backup.sh

在命令行输入带参数的备份脚本,开始部分备份(优先备份):

1
2
cd /opt/k8s-backup/bin
./k8s_backup.sh test test2

2.自动备份

备份工作最好加入定时任务,定时由系统自动触发备份。将备份脚本加入到 Linux/Unix 的 crontab 定时任务:crontab -e
输入定时任务计划,每天凌晨 1:05 触发自动备份运行一次。

1
5 1 * * * sh -x /opt/k8s-backup/k8s_backup.sh

3.备份注意

备份机可以是 K8s 集群内的任意一台主机,也可以是集群外的主机。只要网络互通,备份机能访问到 K8s 集群的 Kube-apiserver,备份机可以部署在任何地方。备份机需要持有 K8s 集群的数字证书,也就是 kubeconfig 文件,否则无法访问 K8s 集群,也就不能执行任何备份操作。

备份的备份
备份数据需不需要备份,要不要考虑备份数据的安全性。备份数据的备份是管理员要考虑的事项之一。如果要提高备份数据的安全性,可以拷贝一份备份数据,存放到安全的地方。

4.数据恢复

数据恢复会对 K8s 集群正常运行产生影响,需要谨慎执行。数据恢复没有采用全面恢复的策略,而是设置专用的数据恢复目录,只有恢复目录下的 yaml 文件才会被恢复。
从备份目录复制 yaml 文件到恢复目录,文件名应替换为待恢复的文件。

1
2
3
cd  /opt/k8s-backup/data
rm -rf restore
cp -r 202xXXyy restore

名称空间创建(存在则无需创建)
!!!不要恢复kube-system名称空间下的资源!!!

1
2
cd /opt/k8s-backup/202xXXyy/namespace
kubectl create -f 名称空间.yaml

执行恢复脚本
数据恢复以批处理方式进行,对数据恢复专用目录下的所有 yaml 文件逐个执行,先删除旧资源条目,再创建新的资源条目。数据恢复的次序是按照 yaml 文件名升序先后排列。

1
2
cd /opt/k8s-backup/bin
./k8s_restore.sh

恢复检测
数据恢复以后,最好再查询一次已恢复的资源项,验证资源项是否正确恢复了。检测的 方式可以是重新做一次备份,导出 yaml 文件,再做数据比对。命令 diff 比较两个文本文件内容,并列出两个文件的差异行。

如果不想导出太多 yaml 文件,也可以导出单个资源项的 yaml 文件,然后与之前的文件做比对。例如:

1
kubectl -n wisecloud-controller get cm test-config -o yaml

5.脚本源文件

备份脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#!/usr/bin/bash
## show usage
echo "usage: $0 [namespace]"
## define variable
BACKUP_PATH=/opt/k8s-backup
BACKUP_PATH_BIN=$BACKUP_PATH/bin
BACKUP_PATH_DATA=$BACKUP_PATH/data/`date +%Y%m%d%H%M%S`
BACKUP_PATH_LOG=$BACKUP_PATH/log
BACKUP_PATH_NS=$BACKUP_PATH/data/`date +%Y%m%d%H%M%S`/namespace
BACKUP_LOG_FILE=$BACKUP_PATH_LOG/k8s-backup.log
## set K8s type
CONFIG_TYPE="service deploy configmap secret job cronjob replicaset daemonset statefulset"
## make dir
mkdir -p $BACKUP_PATH_BIN
mkdir -p $BACKUP_PATH_DATA
mkdir -p $BACKUP_PATH_LOG
mkdir -p $BACKUP_PATH_NS
cd $BACKUP_PATH_DATA
## set namespace list
ns_list=`kubectl get ns | awk '{print $1}' | grep -v NAME|grep -v kube-system`
if [ $# -ge 1 ]; then
ns_list="$@"
fi
## define counters
COUNT0=0
COUNT1=0
COUNT2=0
COUNT3=0
## print hint
echo "`date` Backup kubernetes config in namespaces ["
echo "${ns_list}"
echo '] now.'
echo "`date` Backup kubernetes config for [type: ${CONFIG_TYPE}]."
echo "`date` If you want to read the record of backup, please input command ' tail -100f ${BACKUP_LOG_FILE} '"
## ask and answer
message="This will backup resources of kubernetes cluster to yaml files."
echo ${message} 2>&1 >> $BACKUP_LOG_FILE
echo ${message}
read -n 1 -p "Do you want to continue? [yes/no] " input_char && printf "\n"
if [ "${input_char}" != 'y' ]; then
message="`date` Exit by user's selection."
echo $message 2>&1 >> $BACKUP_LOG_FILE
echo $message
exit 1
fi
## loop for namespaces
for ns in $ns_list; do
COUNT0=`expr $COUNT0 + 1`
echo "`date` Backup No.${COUNT0} namespace [namespace: ${ns}]." 2>&1 >>$BACKUP_LOG_FILE
kubectl get ns ${ns} -o yaml >>$BACKUP_PATH_DATA/namespace/${ns}_namespace.yaml
COUNT2=0
## loop for types
for type in $CONFIG_TYPE; do
echo "`date` Backup type [namespace: ${ns}, type: ${type}]." 2>&1 >>$BACKUP_LOG_FILE
item_list=`kubectl -n $ns get $type | awk '{print $1}' | grep -v NAME | grep -v "No "`
COUNT1=0
## loop for items
for item in $item_list; do file_name=$BACKUP_PATH_DATA/${ns}_${type}_${item}.yaml
echo "`date` Backup kubernetes config yaml [namespace: ${ns}, type: ${type}, item: ${item}] to file: ${file_name}" 2>&1 >> $BACKUP_LOG_FILE
kubectl -n $ns get $type $item -o yaml > $file_name
COUNT1=`expr $COUNT1 + 1`
COUNT2=`expr $COUNT2 + 1`
COUNT3=`expr $COUNT3 + 1`
echo "`date` Backup No.$COUNT3 file done." 2>&1 >> $BACKUP_LOG_FILE
done;
done;
echo "`date` Backup ${COUNT2} files done in [namespace: ${ns}]." 2>&1 >>$BACKUP_LOG_FILE
done;
## show stats
message="`date` Backup ${COUNT3} yaml files in all."
echo ${message}
echo ${message} 2>&1 >> $BACKUP_LOG_FILE
echo "`date` kubernetes Backup completed, all done." 2>&1 >>$BACKUP_LOG_FILE
exit


还原脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#!/usr/bin/bash

## define variable
BACKUP_PATH=/opt/k8s-backup
BACKUP_PATH_BIN=$BACKUP_PATH/bin
BACKUP_PATH_DATA=$BACKUP_PATH/data/restore
BACKUP_PATH_LOG=$BACKUP_PATH/log
BACKUP_PATH_NS=$BACKUP_PATH_DATA/namespace
RESTORE_LOG_FILE=$BACKUP_PATH_LOG/k8s-restore.log

## make dir
mkdir -p $BACKUP_PATH_BIN
mkdir -p $BACKUP_PATH_DATA
mkdir -p $BACKUP_PATH_LOG
cd $BACKUP_PATH_DATA

## print hint
message="`date` Kubernetes Restore start now. All yaml files which located in path ${BACKUP_PATH_DATA} will be applied."
echo ${message} 2>&1 >> $RESTORE_LOG_FILE
echo ${message}
echo "`date` If you want to read the record of restore, please input command ' tail -100f ${RESTORE_LOG_FILE} '"

## list yaml files
file_list=`ls -n ${BACKUP_PATH_DATA}/*.yaml | awk '{print $9}'`
file_count=`echo ${file_list} | wc -w`

## ask and answer
message="WARNING!!! This will create ${file_count} yaml files to kubernetes cluster. While same name resources will be deleted. Please consider it carefully!"
echo ${message} 2>&1 >> $RESTORE_LOG_FILE
echo ${message}
read -n 1 -p "Do you want to continue? [yes/no/show] " input_char && printf "\n"
if [ "${input_char}" == 's' ]; then
message="`date` Show yaml files list."
echo $message 2>&1 >> $RESTORE_LOG_FILE
echo $message
ls -n ${BACKUP_PATH_NS}/*.yaml
ls -n ${BACKUP_PATH_DATA}/*.yaml
exit 1
elif [ "${input_char}" != 'y' ]; then
message="`date` Exit by user's selection."
echo $message 2>&1 >> $RESTORE_LOG_FILE
echo $message
exit 2
fi

## create namespace
for ns in `ls ${BACKUP_PATH_NS}`; do
names=`echo ${ns}|awk -F'_' '{print$1}'`
echo "kubectl delete -f ${ns}" 2>&1 >> $RESTORE_LOG_FILE
cmd_delete_ns="kubectl delete -f ${BACKUP_PATH_NS}/${ns}"
cmd_create_ns="kubectl create -f ${BACKUP_PATH_NS}/${ns}"
## run delete
#echo "`date` Run shell: ${cmd_delete_ns}." 2>&1 >> $RESTORE_LOG_FILE
#if [ ${ns}'x' != 'kube-systemx' ];then
# ${cmd_delete_ns}
#fi
#result="failed"
#if [ $? -eq 0 ]; then
# result="ok"
#fi

## run create
echo "`date` Run shell: ${cmd_create_ns}." 2>&1 >> $RESTORE_LOG_FILE
${cmd_create_ns}
result="failed"
if [ $? -eq 0 ]; then
result="ok"
fi
echo "`date` Create namespace ${result}." 2>&1 >> $RESTORE_LOG_FILE
done

## loop for file list
COUNT=0
for file_yaml in $file_list; do
COUNT=`expr $COUNT + 1`
echo "`date` Restore No.${COUNT} yaml file: ${file_yaml}..." 2>&1 >> $RESTORE_LOG_FILE
cmd_delete="kubectl delete -f ${file_yaml}"
cmd_create="kubectl create -f ${file_yaml}"

## run delete
echo "`date` Run shell: ${cmd_delete}." 2>&1 >> $RESTORE_LOG_FILE
${cmd_delete}
result="failed"
if [ $? -eq 0 ]; then
result="ok"
fi
echo "`date` Delete resource ${result}." 2>&1 >> $RESTORE_LOG_FILE

## run create
echo "`date` Run shell: ${cmd_create}." 2>&1 >> $RESTORE_LOG_FILE
${cmd_create}
result="failed"
if [ $? -eq 0 ]; then
result="ok"

fi
echo "`date` Create resource ${result}." 2>&1 >> $RESTORE_LOG_FILE
done;

## show stats
message="`date` Restore ${COUNT} yaml files in all."
echo ${message}
echo ${message} 2>&1 >> $RESTORE_LOG_FILE
echo "`date` Kubernetes Restore completed, all done." 2>&1 >> $RESTORE_LOG_FILE
exit 0;

End

详细内容请到CNCF
此文脚本增加了名称空间备份yaml

 评论