Velero备份实战 - 基于Ceph的CSI快照
背景知识
卷快照(Volume Snapshot)
继CSI之后,卷快照在Kubernetes v1.12发布了alpha版,引入了以下几个关键的API对象:
VolumeSnapshot
与PVC类似,当用户想创建或删除一个卷快照时,可以选择创建或删除一个VolumeSnapshot。
VolumeSnapshotContent
同PV类似,VolumeSnapshotContent代表集群中被分配的一个快照资源。当一个VolumeSnapshot被创建时,CSI驱动会创建一个VolumeSnapshotContent对象。VolumeSnapshotContent同VolumeSnapshot互相引用,是1对1的关系。
VolumeSnapshotContent建立了同底层存储的快照的映射关系,VolumeSnapshotContent中的SnapshotHandle是一个指向存储快照的UID。
VolumeSnapshotClass
VolumeSnapshotClass同StorageClass类似,指定了卷快照的一些参数,如驱动的信息,DeletionPolicy,访问快照的Secret等。
值得注意的是,上面3个API对象并不是Kubernetes的核心API对象,而是CRD。一般来说,CSI驱动会自动安装所需的CRD。
CSI Snapshotter
CSI Snapshotter是Kubernetes CSI实现的一部分,主要包括以下几个部分:
其中,卷快照控制器和CSI Snapshotter sidecar是核心部分。卷快照控制器监听VolumeSnapshot和VolumeSnapshotContent的创建、更新、删除事件。而CSI Snapshotter sidecar只监听VolumeSnapshotContent的创建、更新、删除。
例如,在动态预配(dynamic provision)情况下,当一个VolumeSnapshot被创建后,卷快照控制器监听到这个事件,在条件满足的情况下会创建一个VolumeSnapshotContent。之后,CSI Snapshotter sidecar监听到VolumeSnapshotContent创建的事件,在条件满足的情况下会调用CSI驱动的API去创建底层存储的快照。
Ceph RBD
RBD是Ceph的块设备接口实现,是Ceph最常用的存储类型。RBD块设备类似磁盘可以被挂载,一个实例化的RBD设备被称为RBD镜像(image)。 RBD块设备具有快照、多副本、克隆和一致性等特性,数据以条带化的方式存储在Ceph集群的多个OSD中。
在Ceph CSI驱动的设计文档中,可以看到对一个PVC做一个快照大概分为如下几步:
创建一个临时的快照
从上面的快照克隆一个镜像,参数为--rbd-default-clone-format 2 --image-feature layering,deep-flatten
删除临时创建的快照
由此可见,当一个CSI的卷快照被创建时,Ceph实际创建的是一个可读可写的克隆。
小结
上面简单介绍了一下卷快照的基本组成以及Ceph的RBD快照概要,下面这张图简单描述了这几个组件之间的关系:
图1 卷快照概念
环境准备
K8s 1.19安装
这里略过,请参考使用kubeadm安装kubernetes_v1.19.x。
Rook-Ceph 1.4安装
获取rook的代码仓库,并切换到release-1.4:
git clone https://github.com/rook/rook.git
git checkout release-1.4
进入ceph的example目录,编辑cluster.yaml
:
cd rook/cluster/examples/kubernetes/ceph
vi cluster.yaml
在resources:
部分,按照机器的配置来指定Ceph的CPU和内存资源,以下是一个参考:
resources:
mgr:
limits:
cpu: "500m"
memory: "1024Mi"
requests:
cpu: "500m"
memory: "1024Mi"
mon:
limits:
cpu: "500m"
memory: "1024Mi"
mds:
limits:
cpu: "500m"
memory: "1024Mi"
crashcollector:
limits:
cpu: "500m"
memory: "1024Mi"
osd:
limits:
cpu: "1"
memory: "2048Mi"
requests:
cpu: "1"
memory: "2048Mi"
在storage:
部分,根据集群节点以及挂载的块设备来填写这部分信息,以下是一个单节点集群,单个块设备的例子:
storage: # cluster level storage configuration and selection
useAllNodes: false
useAllDevices: false
nodes:
- name: "dev-host1"
devices: # specific devices to use for storage can be specified for each node
- name: "sdb"
config:
osdsPerDevice: "2"
storType: bluestore
Ceph安装,依次执行下面的命令:
kubectl create -f common.yaml
kubectl create -f operator.yaml
kubectl create -f cluster.yaml
这里安装过程大概会有15分钟左右,取决于集群节点的数量以及提供给Ceph的块设备的数量。如果需要rook的toolbox,可以等ceph安装完成后执行:
kubectl create -f toolbox.yaml
如果出现问题,请参考文章尾部的Ceph teardown部分,尝试卸载,调整参数之后,重新安装。
创建ceph storageclass, snapshot class
cd csi/rbd
注意:如果是单节点的集群,要将storageclass里的failureDomain
去掉,并且把replicated: size
改成2
执行:
kubectl create -f storageclass.yaml
注意:snapshotclass的deletionPolicy
必须是Retain
,并且加上label:
labels:
velero.io/csi-volumesnapshot-class: "true"
执行:
kubectl create -f snapshotclass.yaml
Volumesnapshot CRD
获取external-snapshotter的代码仓库:
git clone https://github.com/kubernetes-csi/external-snapshotter.git
进入external-snapshotter
目录,切换到release-2.0
分支
git checkout release-2.0
执行以下命令来创建CRD:
kubectl create -f config/crd
执行以下命令来创建snapshot controller:
kubectl create -f deploy/kubernetes/snapshot-controller/
minio安装
minio是一个开源的对象存储,并且与AWS的S3兼容,它的特点是容易安装上手。minio有多种安装模式,具体可以参考minio quickstart guide。本文用一个最简便的安装方式,就在velero的下载包中,有一个example/minio/
的目录,进去执行:
kubectl create -f 00-minio-deployment.yaml
这个yaml文件里,有几个地方需要注意:
spec:
volumes:
- name: storage
emptyDir: {}
...
env:
- name: MINIO_ACCESS_KEY
value: "minio"
- name: MINIO_SECRET_KEY
value: "minio123"
---
apiVersion: v1
kind: Service
spec:
type: ClusterIP
ports:
- port: 9000
targetPort: 9000
protocol: TCP
这里的env实际上指定了minio的key/secret,用这个可以访问minio的GUI。
下面Service的port可以根据自己的实际需要改成其他的端口号,比如31605。service是ClusterIP,可以根据需要改成NodePort。
另外,这里的volume是emptydir形式,作为学习问题不大,如果需要用其他形式的存储,可以参考例如minio NFS backend。
最后,登陆minio的GUI并创建一个bucket,比如test1
,为velero的安装做准备。
Velero 1.4.3安装
下载velero v1.4.3的安装包,解压。
准备好credential file:
[root~ ]# cat credentials-velero
[default]
aws_access_key_id=minio
aws_secret_access_key=minio123
在velero的下载目录执行:
velero install --provider aws --bucket test1 --secret-file credentials-velero --use-volume-snapshots=true --use-restic --backup-location-config region=minio,s3ForcePathStyle="true",s3Url=http://ip:port,publicUrl=http://ip:port --plugins=velero/velero-plugin-for-aws:v1.1.0,velero/velero-plugin-for-csi:v0.1.1 --namespace=velero --snapshot-location-config region=default --features=EnableCSI
上面命令的ip就是minio安装所在的机器ip,port是minio开放的port。
执行完后,可以用kubectl logs -n velero velero-xxxx-xxx
命令来查看velero的log来观察velero是否安装成功:
time="2021-04-22T07:10:21Z" level=info msg="setting log-level to INFO" logSource="pkg/cmd/server/server.go:177"
time="2021-04-22T07:10:21Z" level=info msg="Starting Velero server v1.4.3 (4fc1c700282d6ece5b3ffc03cf296c435ccc903e)" logSource="pkg/cmd/server/server.go:179"
time="2021-04-22T07:10:21Z" level=info msg="1 feature flags enabled [EnableCSI]" logSource="pkg/cmd/server/server.go:181"
Wordpress + MySQL
最后用Ceph的storageclass来为MySQL和Wordpress创建PVC,具体可以参考rook wordpress example和rook mysql example。安装完Wordpress后,可以发布一篇文章,作为后面恢复的验证。
<img src="https://gitee.com/jibutech/tech-docs/raw/master/images/wp文章1.png" style="zoom:50%;" />
同时可以进入MySQL的Pod查看wordpress
的database,表wp_posts
:
| 4 | 1 | 2021-04-20 13:47:35 | 2021-04-20 05:47:35 | | bakcup1 | | publish | open | open | | bakcup1 | | | 2021-04-20 13:47:35 | 2021-04-20 05:47:35 | | 0 | https://foo.bar.com:30165/?p=4 | 0 | post |
备份
- 执行备份命令
[root~ ]# velero backup create wp-backup --include-namespaces wordpress
Backup request "wp-backup" submitted successfully.
Run `velero backup describe wp-backup` or `velero backup logs wp-backup` for more details.
- 查看备份状态
[root~ ]# kubectl get backups.velero.io -n v elero
NAME AGE
wp-backup 32s
[root~ ]# kubectl get backups.velero.io -n velero wp-backup -o yaml
...
...
spec:
hooks: {}
includedNamespaces:
- wordpress
storageLocation: default
ttl: 720h0m0s
volumeSnapshotLocations:
- default
status:
expiration: "2021-05-22T12:15:11Z"
formatVersion: 1.1.0
phase: InProgress
progress:
totalItems: 19
startTimestamp: "2021-04-22T12:15:11Z"
version: 1
- 查看volumesnapshot状态
[root~ ]# kubectl get volumesnapshot -n wordpress
NAME AGE
velero-mysql-pv-claim-kkxwx 3m2s
velero-wp-pv-claim-587dp 3m47s
[root~ ]# k get volumesnapshotcontent
NAME AGE
snapcontent-549679d6-a5cf-41fb-aaa4-fc11fe09e9ab 2m52s
snapcontent-984839a0-55a8-41de-8bd6-5d0bfaba6e2c 3m37s
这里如果去查看snapcontent-549679d6-a5cf-41fb-aaa4-fc11fe09e9ab
的信息,可以看到:
Spec:
Deletion Policy: Retain
Driver: rook-ceph.rbd.csi.ceph.com
Source:
Volume Handle: 0001-0009-rook-ceph-0000000000000002-05b2f2d6-a19b-11eb-baf1-7a576c7e3674
Volume Snapshot Class Name: csi-rbdplugin-snapclass
Volume Snapshot Ref:
API Version: snapshot.storage.k8s.io/v1beta1
Kind: VolumeSnapshot
Name: velero-mysql-pv-claim-kkxwx
Namespace: wordpress
Resource Version: 43496419
UID: 549679d6-a5cf-41fb-aaa4-fc11fe09e9ab
Status:
Creation Time: 1619093815974957075
Ready To Use: true
Restore Size: 10737418240
Snapshot Handle: 0001-0009-rook-ceph-0000000000000002-a0a1af05-a364-11eb-baf1-7a576c7e3674
Events: <none>
其中,Deletion Policy
被我们之前设置成了Retain
,这样的话,即使刚创建的Volumesnapshot
的CR被删掉,VolumesnapShotContent
也不会被删掉,VolumeSnapshotContent
指向的Ceph的快照也不会被删掉。
- 查看RBD快照
查看并进入Ceph的tools Pod:
rook-ceph-tools-5949d6759-2kwzm 1/1 Running 2 33d
[root]# kubectl exec -it -n rook-ceph rook-ceph-tools-5949d6759-2kwzm -- bash
[root@rook-ceph-tools-5949d6759-2kwzm /]#
用上面VolumesnapShotContent
的Snapshot Handle
的后半部分a0a1af05-a364-11eb-baf1-7a576c7e3674
来找到rbd的快照:
[root@rook-ceph-tools-5949d6759-2kwzm /]# rbd ls replicapool | grep a0a1af05-a364-11eb-baf1-7a576c7e3674
csi-snap-a0a1af05-a364-11eb-baf1-7a576c7e3674
上面replicapool
其实是安装Ceph时的默认存储池的名字,然后可以用rbd info
命令去看这个快照的属性:
[root@rook-ceph-tools-5949d6759-2kwzm /]# rbd info replicapool/csi-snap-a0a1af05-a364-11eb-baf1-7a576c7e3674
rbd image 'csi-snap-a0a1af05-a364-11eb-baf1-7a576c7e3674':
size 10 GiB in 2560 objects
order 22 (4 MiB objects)
snapshot_count: 1
id: 455cd100fcb2d
block_name_prefix: rbd_data.455cd100fcb2d
format: 2
features: layering, deep-flatten, operations
op_features: clone-child
flags:
create_timestamp: Thu Apr 22 12:16:55 2021
access_timestamp: Fri Apr 23 06:36:18 2021
modify_timestamp: Thu Apr 22 12:16:55 2021
parent: replicapool/csi-vol-05b2f2d6-a19b-11eb-baf1-7a576c7e3674@7ad163d8-0c3c-49ef-a58e-43b95423beee
overlap: 10 GiB
可以看到这个rbd image的属性其实跟前面讲Ceph CSI驱动创建快照时候的参数是一致的,format: 2
,layering, deep-flatten
应该表示了这个image是一个rbd克隆。
- 备份完成
status:
completionTimestamp: "2021-04-22T12:16:58Z"
expiration: "2021-05-22T12:15:11Z"
formatVersion: 1.1.0
phase: Completed
progress:
itemsBackedUp: 26
totalItems: 26
startTimestamp: "2021-04-22T12:15:11Z"
version: 1
这里还是对前面backups.velero.io
的查看,这里状态已经变成了Completed
,表示成功完成。
不同场景的恢复
删除PVC
删除PVC之前,可以新写一篇文章,用来表示当前应用的最新版本的数据,然后再执行PVC的删除:
[root~ ]# kubectl delete pvc -n wordpress mysql-pv-claim
persistentvolumeclaim "mysql-pv-claim" deleted
[root~ ]# kubectl delete pvc -n wordpress wp-pv-claim
persistentvolumeclaim "wp-pv-claim" deleted
[root~ ]#
恢复:
[root~ ]# velero create restore --from-backup wp-backup
Restore request "wp-backup-20210422204956" submitted successfully.
Run `velero restore describe wp-backup-20210422204956` or `velero restore logs wp-backup-20210422204956` for more details.
这时候如果去看restores.velero.io
的状态,会发现很快就恢复完成了:
[root~ ]# kubectl describe restores.velero.io -n velero wp-backup-20210422204956
...
Spec:
Backup Name: wp-backup
Excluded Resources:
nodes
events
events.events.k8s.io
backups.velero.io
restores.velero.io
resticrepositories.velero.io
Included Namespaces:
*
Status:
Phase: Completed
Warnings: 6
Events: <none>
查看PV,PVC:
[root~ ]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-179efdd1-4d91-4b4f-a2dd-2ecfccedfb28 10Gi RWO Delete Bound wordpress/wp-pv-claim rook-ceph-block 76s
pvc-a444c477-d2f9-481e-8db6-85b9d893ecad 10Gi RWO Delete Released wordpress/mysql-pv-claim rook-ceph-block 2d7h
pvc-ad1c99d3-1f7f-486f-9924-0c997ad503cb 10Gi RWO Delete Bound wordpress/mysql-pv-claim rook-ceph-block 76s
pvc-bc45621b-50a3-4056-8f1b-43038b6e0174 10Gi RWO Delete Released wordpress/wp-pv-claim rook-ceph-block 2d7h
[root~ ]# kubectl get pvc -n wordpress
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mysql-pv-claim Bound pvc-ad1c99d3-1f7f-486f-9924-0c997ad503cb 10Gi RWO rook-ceph-block 79s
wp-pv-claim Bound pvc-179efdd1-4d91-4b4f-a2dd-2ecfccedfb28 10Gi RWO rook-ceph-block 79s
这里可以看到前面删除的两个PV处于Released
的状态,恢复回来的PV是另外的名字。
恢复完成之后,需要注意一点,要把MySQL的Pod删掉来造成Pod重启一下(deployment会自动创建新的Pod)。这个重启十分重要,会让Pod把PV重新挂载一遍,否则的话,Pod里面的数据还是最新的,并不是想要恢复的那个版本。
再去看Wordpress的文章,没有问题,这里读者可以自行验证,这里就不展示了。
损坏PV的数据
这里可以直接把Wordpress里面的文章删掉,或者修改当前文章,然后再尝试恢复。这里跟上面场景主要的区别在于数据是应用随着时间正常更新了(比如修改或新增文章),还是恶意被破坏了(比如删掉文章)。但是从步骤来说,是一样的,记得恢复完要把MySQL的Pod重启一下。
删除namespace
[root~ ]# kubectl delete ns wordpress
namespace "wordpress" deleted
[root~ ]#
再执行恢复,也很快成功了,再去看MySQL的数据:
| 4 | 1 | 2021-04-20 13:47:35 | 2021-04-20 05:47:35 | | bakcup1 | | publish | open | open | | bakcup1 | | | 2021-04-20 13:47:35 | 2021-04-20 05:47:35 | | 0 | https://foo.bar.com:30165/?p=4 | 0 | post |
没有问题,数据恢复成功。
总结
以上介绍了怎么用velero通过Ceph的快照方式来备份恢复有状态的应用,可以看到,不论是删掉PV,删掉命名空间,还是损坏原始PV的数据,都可以通过velero把原始数据恢复回来。但是,这里需要注意的是,velero备份的只是VolumeSnapshot和VolumeSnapshotContent的CR,并没有把PV的数据进行导出,一旦Ceph集群出现问题,比如集群遇到错误起不来,那备份到对象存储的资源是没法恢复丢失的数据的。
参考
rook ceph quick start
ceph teardown
volume snapshots
external-snapshotter
rbd snapshot
Venky Shankar - Red Hat - Ceph RBD Deep Dive
design-rbd-snap-clone