原生于 Kubernetes 的备份工具 Kanister 的验证
首先
在本验证中,我们将进行Kubernetes Native备份工具Kanister的操作验证。
Kanister是基于Veeam公司的Kasten(K10)开发的开源软件。Kanister可以将在Kubernetes上运行的应用程序的数据备份到对象存储中。
Kanister的特点之一是以应用程序为中心,因此为每个应用程序准备了模板(Blueprint)。模板中包含了每个应用程序备份的前期准备和后期处理等操作,因此可以为应用程序提供友好的备份服务。
截至2020年12月,已经为以下应用程序准备了模板。
-
- Cassandra
-
- Couchbase
-
- Elasticsearch
-
- FoundationDB
-
- MongoDB
-
- MySQL on OpenShift using DeploymentConfig
-
- MySQL
-
- PostgreSQL with Point In Time Recovery (PITR)
-
- ETCD
- PostgreSQL
校验环境
-
- minikube
Kubernetes v1.19.4
MinIO RELEASE.2020-12-10T01-54-29Z
MySQL 5.7
Kanister 0.44.0
事前筹备
作为事前准备,我们将准备用于存储备份数据的对象存储(MinIO)和备份目标 MySQL。
MinIO的安装。
首先,创建并切换到MinIO的名称空间(minio)。
$ kubectl create ns minio
$ kubens minio
下面将使用Manifest(minio.yaml)来设置MinIO。
- minio.yaml
apiVersion: v1
kind: Service
metadata:
name: minio
spec:
ports:
- port: 9000
targetPort: 9000
selector:
app: minio
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: minio-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: minio
spec:
serviceName: "minio"
replicas: 1
selector:
matchLabels:
app: minio
template:
metadata:
labels:
app: minio
spec:
volumes:
- name: data
persistentVolumeClaim:
claimName: minio-pvc
containers:
- name: minio
volumeMounts:
- name: data
mountPath: "/data"
image: minio/minio:RELEASE.2020-12-10T01-54-29Z
args:
- server
- /data
env:
- name: MINIO_ACCESS_KEY
value: "minio"
- name: MINIO_SECRET_KEY
value: "minio123"
ports:
- containerPort: 9000
hostPort: 9000
部署minio.yaml,并确认MinIO的启动。
$ kubectl apply -f minio.yaml
$ kubectl get svc,pod,pvc,pv
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/minio ClusterIP 10.106.197.201 <none> 9000/TCP 2m57s
NAME READY STATUS RESTARTS AGE
pod/minio-0 1/1 Running 0 2m57s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/minio-pvc Bound pvc-f1016d2e-2fc5-48a0-befb-8a7304281b16 1Gi RWO standard 2m57s
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/pvc-f1016d2e-2fc5-48a0-befb-8a7304281b16 1Gi RWO Delete Bound minio/minio-pvc standard 2m57s
完成Portward之后,尝试访问MinIO的WebUI。
$ kubectl port-forward -n minio svc/minio 9000:9000 &
使用浏览器访问URL(http://localhost:9000)。
能够使用AccessKey(minio)和Secret Key(mino123)访问。

我会准备一个用于此验证的Bucket。
点击WebUI右下角的”+”,选择创建Bucket,然后在Bucket名称中输入”kanister”来创建Bucket。

以上是MinIO准备完成的部分。
MySQL的安装设置
为了进行Kanister操作的验证,我们将创建一个单一实例的MySQL以用作动作验证。MySQL将被设置在命名空间(默认)中。
$ kubens default
接下来,我们将使用Manifest(mysql.yaml)来设置MySQL。
- mysql.yaml
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
ports:
- port: 3306
selector:
app: mysql
clusterIP: None
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
labels:
app.kubernetes.io/name: mysql
app.kubernetes.io/instance: mysql
spec:
serviceName: "mysql"
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- image: mysql:5.7
name: mysql
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql
key: mysql-root-password
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: data
mountPath: /var/lib/mysql
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
创建MySQL的root密码为Secret(mysql)之后,部署Manifest(mysql.yaml)以确认MySQL的启动。
在本次验证中使用的Kanister的Blueprint(mysql-blueprint),以Label app.kubernetes.io/instance: 的值作为Secret名称使用(原因不明),因此需要进行设置。
$ kubectl create secret generic mysql \
--from-literal=mysql-root-password='password'
$ kubectl apply -f mysql.yaml
$ kubectl get svc,pod,pvc,pv
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 67m
service/mysql ClusterIP None <none> 3306/TCP 2m26s
NAME READY STATUS RESTARTS AGE
pod/mysql-0 1/1 Running 0 2m26s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/data-mysql-0 Bound pvc-cbbb914b-6911-477c-b77a-1adc3c849d1a 1Gi RWO standard 2m26s
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/pvc-cbbb914b-6911-477c-b77a-1adc3c849d1a 1Gi RWO Delete Bound default/data-mysql-0 standard 2m26s
persistentvolume/pvc-f1016d2e-2fc5-48a0-befb-8a7304281b16 1Gi RWO Delete Bound minio/minio-pvc standard 24m
我将尝试访问MySQL。
由于在mysql.yaml中设置了MySQL的root密码为password,我将使用它执行SELECT 1以确认能够成功访问。
$ kubectl run mysql-client --image=mysql:5.7 -it --rm --restart=Never -- mysql -h mysql -uroot -ppassword -e 'SELECT 1'
mysql: [Warning] Using a password on the command line interface can be insecure.
+---+
| 1 |
+---+
| 1 |
+---+
pod "mysql-client" deleted
接下来,为了进行本次验证,需要创建一个适当的表并添加记录。
$ kubectl exec -ti mysql-0 -- bash
root@mysql-0:/# mysql -p
Enter password: # MySQLのrootのパスワードを入力
...
mysql> CREATE DATABASE test;
mysql> USE test;
mysql> CREATE TABLE pets (name VARCHAR(20), species VARCHAR(20));
mysql> INSERT INTO pets VALUES ('flare', 'dog');
mysql> INSERT INTO pets VALUES ('reno', 'dog');
mysql> SELECT * FROM pets;
+-------+---------+
| name | species |
+-------+---------+
| flare | dog |
| reno | dog |
+-------+---------+
2 rows in set (0.00 sec)
mysql> exit
root@mysql-0:/# exit
以上,准备工作已经完成。
行动验证
Kanister的安装
準備工作花费了很长时间,但是现在我们终于要开始安装主题中的Kanister了。
首先,我们创建并切换到Kanister的命名空间(kanister)。
$ kubectl create ns kanister
$ kubens kanister
接下来,我们将使用Helm来安装Kanister Operator。
$ helm repo add kanister http://charts.kanister.io
$ helm install myrelease --namespace kanister kanister/kanister-operator --set image.tag=0.44.0
确认启动kanister operator。
$ $ kubectl get deploy,pod
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/myrelease-kanister-operator 1/1 1 1 53s
NAME READY STATUS RESTARTS AGE
pod/myrelease-kanister-operator-6f97fd58f6-dqt8g 1/1 Running 0 53s
以上で、Kanister Operatorのセットアップが終了しました。
Kanisterでは以下の3つのCRDも展開されています。
$ kubectl get crd |grep kanister
actionsets.cr.kanister.io 2020-12-12T05:52:57Z
blueprints.cr.kanister.io 2020-12-12T05:52:57Z
profiles.cr.kanister.io 2020-12-12T05:52:57Z
以下是对各 CR 进行简要说明的内容。
-
- Actionset
バックアップやリストアの実行するタスク(アクション)を示すリソース
Blueprint
バックアップやリストアなどの処理を定義するテンプレートのリソース
Profile
バックアップ先となるオブジェクトストレージのロケーション情報などを定義するリソース
在Kanister中,使用上述的三个CR执行备份/恢复操作。以下是Kanister的工作流程。

创建个人资料
首先,使用之前准备好的MinIO信息作为备份存储位置,创建一个Profile。
以下是Profile的清单(profile.yaml)。
- profile.yaml
apiVersion: cr.kanister.io/v1alpha1
kind: Profile
metadata:
name: s3-profile
namespace: kanister
location:
type: s3Compliant
bucket: kanister
endpoint: http://minio.minio.svc:9000
credential:
type: keyPair
keyPair:
idField: minio_access_key_id
secretField: minio_secret_access_key
secret:
apiVersion: v1
kind: Secret
name: minio-secret
namespace: kanister
skipSSLVerify: true
因此,在这次创建的MinIO中没有设置TLS,因此将skipSSLVerify设为true。
我们将部署profile.yaml文件。
$ kubectl apply -f profile.yaml
接下来,我们将创建一个名为minio-secret的密钥(Secret),其中包含MinIO的AccessKey和Secret Key。
下一步,我们将创建一个名为minio-secret的密钥(Secret),其中包含MinIO的AccessKey和Secret Key。
$ kubectl create secret -n kanister generic minio-secret \
--from-literal=minio_access_key_id='minio' \
--from-literal=minio_secret_access_key='minio123'
准备MySQL蓝图。
接下来,部署MySQL备份恢复处理的蓝图清单(mysql-blueprint.yaml),其中包含了相关操作的记录。
$ git clone git@github.com:kanisterio/kanister.git
$ kubectl apply -f kanister/examples/stable/mysql/mysql-blueprint.yaml
$ kubectl get blueprint
NAME AGE
mysql-blueprint 11m
执行备份
终于要执行备份了。
在Kanister中,通过创建Actionsets资源来执行备份和恢复操作。
下面是备份Actionset的清单(backup.yaml)。
apiVersion: cr.kanister.io/v1alpha1
kind: ActionSet
metadata:
name: backup
namespace: kanister
spec:
actions:
- blueprint: mysql-blueprint
name: backup
object:
kind: statefulset
name: mysql
namespace: default
profile:
name: s3-profile
namespace: kanister
在备份完成之前,请稍等一会。
可以通过查看Actionset(备份)的事件来确认备份的状态。
$ kubectl describe actionset -n kanister backup
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Started Action 3m18s Kanister Controller Executing action backup
Normal Started Phase 3m18s Kanister Controller Executing phase dumpToObjectStore
Normal Ended Phase 35s Kanister Controller Completed phase dumpToObjectStore
Normal Update Complete 35s Kanister Controller Updated ActionSet 'backup' Status->complete
状态已更改为完成,备份已完成。
让我们从MinIO的WebUI中确认备份的数据。

在用于验证的Buket(坚尼斯特)下面,通过命名空间名称、StatefulSet名称和日期创建了层级,并在其下面存储了名为dump.sql.gz的备份数据。
执行还原操作
为了验证恢复操作,接下来我们将删除MySQL。
$ kubens default
$ kubectl delete -f mysql.yaml
$ kubectl delete pvc data-mysql-0
再次部署MySQL的Manifest(mysql.yaml)。
$ kubectl apply -f mysql.yaml
$ kubectl get svc,pod,pvc,pv
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4h3m
service/mysql ClusterIP None <none> 3306/TCP 68s
NAME READY STATUS RESTARTS AGE
pod/mysql-0 1/1 Running 0 68s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/data-mysql-0 Bound pvc-cc959139-bf18-465d-a17c-e1727b4c1ac2 1Gi RWO standard 68s
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/pvc-cc959139-bf18-465d-a17c-e1727b4c1ac2 1Gi RWO Delete Bound default/data-mysql-0 standard 68s
persistentvolume/pvc-f1016d2e-2fc5-48a0-befb-8a7304281b16 1Gi RWO Delete Bound minio/minio-pvc standard 3h21m
使用Kanister将备份数据恢复到初始状态的MySQL。
然后,我们将开始恢复备份数据。
我们将创建并执行一个用于恢复的Actionset。
为了创建恢复用的Actionset清单,我们需要获取信息(artifacts.mysqlCloudDump.keyValue.s3path)。
$ kubectl get actionsets backup -o yaml |grep -A 3 artifacts
- artifacts:
mysqlCloudDump:
keyValue:
s3path: /mysql-backups/default/mysql/2020-12-12T08-26-08/dump.sql.gz
以下是使用上述值创建的用于列表还原的Actionset的清单(restore.yaml)。
- restore.yaml
apiVersion: cr.kanister.io/v1alpha1
kind: ActionSet
metadata:
name: restore
namespace: kanister
spec:
actions:
- name: restore
artifacts:
mysqlCloudDump:
keyValue:
s3path: /mysql-backups/default/mysql/2020-12-12T08-26-08/dump.sql.gz
blueprint: mysql-blueprint
object:
kind: statefulset
name: mysql
namespace: default
profile:
name: s3-profile
namespace: kanister
部署并执行恢复操作 (restore.yaml)。
请稍等,直到恢复操作完成。
可通过查看备份操作的事件 (Actionset backup) 来确认恢复操作的状态。
$ kubectl describe actionset -n kanister restore
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Started Action 85s Kanister Controller Executing action restore
Normal Started Phase 85s Kanister Controller Executing phase restoreFromBlobStore
Normal Ended Phase 83s Kanister Controller Completed phase restoreFromBlobStore
Normal Update Complete 83s Kanister Controller Updated ActionSet 'restore' Status->complete
状态已设置为完成,并已完成还原。
连接到MySQL并确认还原是否正确。
$ kubens default
$ kubectl exec -ti mysql-0 -- bash
root@mysql-0:/# mysql -p
Enter password:
...
mysql> SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
| test |
+--------------------+
5 rows in set (0.00 sec)
mysql> USE test
...
Database changed
mysql> SHOW TABLES;
+----------------+
| Tables_in_test |
+----------------+
| pets |
+----------------+
1 row in set (0.00 sec)
mysql> SELECT * FROM pets;
+-------+---------+
| name | species |
+-------+---------+
| flare | dog |
| reno | dog |
+-------+---------+
2 rows in set (0.00 sec)
mysql> exit
Bye
root@mysql-0:/# exit
我确认可以正确进行恢复操作。
补充:使用kanctl命令快速部署Actionset。
你可以在以下位置安装kanctl命令。
$ curl https://raw.githubusercontent.com/kanisterio/kanister/master/scripts/get.sh | bash
例如,即使不创建一个Actionset的Mainfest,仍然可以通过以下的kanctl命令执行上述的验证恢复。
$ kanctl --namespace kanister create actionset --action restore --from backup
触动内心感受
本次我们对Kanister进行了Kubernetes原生备份/还原的测试验证。Kanister是一个工具,通过为每个应用程序提供名为Blueprint的模板来实现按照应用程序的方式进行备份/还原。只要Blueprint已经准备好了,就可以轻松使用。但是请注意,虽然Blueprint已经准备好,但过于依赖它可能是危险的。在使用之前,请务必检查Blueprint的内容,并确认其中正在执行的操作。例如,本次使用的Blueprint(mysql-blueprint)只是通过mysqldump命令将备份文件复制到对象存储中,非常简单。遗憾的是,它没有包含像在数据库备份中常见的处理,如在备份期间锁定写入以防止记录被修改。如果需要,您需要自行添加到Blueprint中。从GitHub页面来看,Kanister被描述为一个用于Kubernetes中数据管理的框架。换句话说,它是用于进行应用程序中心的备份/还原的框架。可能这是与提供备份/还原软件Kasten(K10)的差异化之处。
另外,還有一個類似的Kubernetes Native的備份/還原工具叫做Velero。(Velero的驗證報告在這裡)現在可能有人對Velero和Kanister的區別感到好奇。簡單說,Velero非常適合備份/還原在Kubernetes上部署的資源(例如Pod、PVC、PV等)。而Kanister則專注於以適合應用程序的方式備份/還原應用程序的數據。根據使用場景,可以根據是要備份整個Kubernetes資源還是只備份特定應用程序的數據等進行選擇,這是值得推薦的。
请提供参考信息。
-
- GitHub kanisterio/kanister
- Kanister docs