介绍一个不太小的工具——Stash
AppCode Stash 是一个构建在 Restic 基础之上的工具,用于备份 Kubernetes 上运行的有状态应用,它使用一个有趣的 Sidecar,能够方便的通过共享卷的方式来对 RWO 模式的存储卷进行备份。提供了批量备份、备份模板、可扩展的应用(主要是数据库)备份等功能。备份目标包括 Kubernetes 卷、S3 等常见设施。主要功能包括:
- 备份和恢复工作负载数据:包括 Deploy、DaemonSet、StatefulSet 等等。
- 备份和恢复独立存储卷。
- 备份和恢复数据库:PostgreSQL、MySQL、MongoDB、ElasticSearch。
- 卷快照:支持 CSI 卷快照功能的集群。
- 计划备份。
- 自动备份:使用注解和模板进行备份。
- 多种备份存储目标:支持 S3、Azure、GCP 以及存储卷等多种设施。
- 可监控:支持 Prometheus 监控。
- 提供 kubectl 插件。
- 可扩展开发。
- 支持 hook 和 Webhook。
安装
使用 Helm 3 安装:
$ helm repo add appscode https://charts.appscode.com/stable/
"appscode" has been added to your repositories
$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "appscode" chart repository
...
Update Complete. ⎈ Happy Helming!⎈
$ helm search repo appscode/stash --version v0.9.0-rc.4
NAME CHART VERSION APP VERSION DESCRIPTION
appscode/stash v0.9.0-rc.4 v0.9.0-rc.4 Stash by AppsCode - Backup your Kubernetes Volumes
$ helm install stash-operator appscode/stash \
--version v0.9.0-rc.4 \
--namespace kube-system
完成之后,可以使用如下命令进行校验:
$ kubectl get pods --all-namespaces -l app=stash --watch
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system stash-operator-859d6bdb56-m9br5 2/2 Running 2 5s
$ kubectl get crd -l app=stash
NAME AGE
recoveries.stash.appscode.com 5s
repositories.stash.appscode.com 5s
restics.stash.appscode.com 5s
安装完毕之后,就可以尝试第一次备份操作了。
创建备份后端
这里简单使用一个 PVC 来做为备份存储的后端。
Restic 的备份过程需要指定一个密码,这里使用一个 Secret 来保存密码:
$ echo -n 'changeit' > RESTIC_PASSWORD
$ kubectl create secret generic backup-password --from-file=./RESTIC_PASSWORD
secret/local-secret created
接下来创建一个 PVC,作为备份文件的存储目标:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: backend-pvc
spec:
resources:
requests:
storage: 8Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
在 Stash 中新建一个 Repository,用刚才的 PVC 作为存储:
apiVersion: stash.appscode.com/v1alpha1
kind: Repository
metadata:
name: backend-repository
spec:
backend:
local:
mountPath: /storage/data
persistentVolumeClaim:
claimName: backend-pvc
storageSecretName: backup-password
此处的 local
段,指明了在本地加载一个卷作为备份文件的存储目标。官方文档中介绍了各种存储后端的用法。
storageSecretName
除了刚才提到的备份密码之外,还用于存储后端的认证凭据,例如使用 S3 后端就需要这样的 Secret:
$ echo -n 'changeit' > RESTIC_PASSWORD
$ echo -n '<your-azure-storage-account-name>' > AZURE_ACCOUNT_NAME
$ echo -n '<your-azure-storage-account-key>' > AZURE_ACCOUNT_KEY
$ kubectl create secret generic -n demo azure-secret \
--from-file=./RESTIC_PASSWORD \
--from-file=./AZURE_ACCOUNT_NAME \
--from-file=./AZURE_ACCOUNT_KEY
secret/azure-secret created
创建工作负载
接下来随便运行一个应用,挂载 PVC 来模拟业务应用来进行备份。
apiVersion: apps/v1
kind: Deployment
metadata:
name: sleep
spec:
replicas: 1
selector:
matchLabels:
app: sleep
template:
metadata:
labels:
app: sleep
version: v1
spec:
containers:
- name: sleep
image: dustise/sleep
imagePullPolicy: IfNotPresent
resources:
limits:
cpu: 100m
memory: 100M
requests:
cpu: 100m
memory: 100M
volumeMounts:
- name: storage
mountPath: /data
volumes:
- name: storage
persistentVolumeClaim:
claimName: workload-pvc
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: workload-pvc
spec:
resources:
requests:
storage: 1Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
Pod 正常运行后,生成一个文件:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
sleep-76b447c854-7xmgf 1/1 Running 0 9m17s
$ kubectl exec -it sleep-76b447c854-7xmgf -c sleep -- fallocate -l 5M /data/file-5m.txt
$ kubectl exec -it sleep-76b447c854-7xmgf -c sleep ls /data
file-5m.txt lost+found
备份
BackupConfiguration
是一个 CRD,用于连接工作负载和备份存储目标。
apiVersion: stash.appscode.com/v1beta1
kind: BackupConfiguration
metadata:
name: backupconfig-sleep
spec:
repository:
name: backend-repository
# 每五分钟一次备份
schedule: "*/5 * * * *"
target:
ref:
apiVersion: apps/v1
kind: Deployment
name: sleep
# 加载工作负载中的卷
volumeMounts:
- name: storage
mountPath: /data
paths:
- /data
retentionPolicy:
name: 'keep-last-5'
keepLast: 5
prune: true
创建之后,会发现业务 Pod 被注入了 Sidecar,正在重启。
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
sleep-569b786766-mwmrb 0/2 ContainerCreating 0 3s
sleep-76b447c854-7bvtz 1/1 Running 0 10m
并且系统中出现了新的 CronJob 和 BackupSession 对象:
$ kubectl get cronjob
NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE
stash-backup-backupconfig-sleep */5 * * * * False 0 4m31s 36m
$ kubectl get backupsession
NAME INVOKER-TYPE INVOKER-NAME PHASE AGE
backupconfig-sleep-1580535011 BackupConfiguration backupconfig-sleep Succeeded 39s
上面看到,这个 BackupSession 已经成功了一次,就可以看看 Repository 有没有什么变化了:
$ kubectl get repository
NAME INTEGRITY SIZE SNAPSHOT-COUNT LAST-SUCCESSFUL-BACKUP AGE
backend-repository true 5 4m58s 61m
Describe 一下这个对象,会看到其中包含的快照数量,以及文件尺寸。
恢复
备份之后,我们新建一个 Deployment,作为恢复的目标:
apiVersion: apps/v1
kind: Deployment
metadata:
name: sleep-restore
spec:
replicas: 1
selector:
matchLabels:
app: sleep-restore
template:
metadata:
labels:
app: sleep-restore
version: v1
spec:
containers:
- name: sleep-restore
image: dustise/sleep
imagePullPolicy: IfNotPresent
resources:
limits:
cpu: 100m
memory: 100M
requests:
cpu: 100m
memory: 100M
volumeMounts:
- name: storage
mountPath: /data
volumes:
- name: storage
persistentVolumeClaim:
claimName: restore-pvc
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: restore-pvc
spec:
resources:
requests:
storage: 1Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
然后创建一个 RestoreSession 对象:
apiVersion: stash.appscode.com/v1beta1
kind: RestoreSession
metadata:
name: deployment-restore
spec:
repository:
name: backend-repository
rules:
- paths:
- /data
target:
ref:
apiVersion: apps/v1
kind: Deployment
name: sleep-restore
volumeMounts:
- name: storage
mountPath: /data
使用 kubectl 提交对象,启动还原过程。
通过 kubectl get po
的观察,可以看到新建的 Pod 也重启了,这次加入的不是 Sidecar,而是一个 init-container。容器重建之后,可以进入 Pod 查看,例如:
$ kubectl exec -it sleep-restore-76ff947f9b-s52px ls /data
file-5m.txt lost+found
其它
除了 Deployment 之外,Stash 还支持 Statefulset、Daemonset 的备份。另外能通过 AppBind 和 Task 等对象完成针对特定数据库的备份和恢复;通过 BackupBatch 来实现一个应用多种数据的备份;最后更可以使用 BackupPrint 对象根据 Annotation 进行自动备份。
缺点也是有的:
- 目前只是 RC 版本。
- 后面提到的几种功能复杂性都比本文演示的 Deployment 备份要复杂得多。
- 文档非常好,但是不够完善,并且略有瑕疵。
- 注入过程会引发业务中断。
- …
用在生产环境还是颇为冒险的,好在备份恢复从来就不是个容易的事情,这东西至少有很好的参考价值。