# 用 k8up 把 PVC 备份到 S3

[k8up](https://k8up.io/) 是一个基于 [Restic](https://github.com/restic/restic) 的备份工具，可以一次性的或者周期性的把指定的 PVC 备份到 S3 协议的对象存储上去，备份内容还可以使用 Restic 恢复到 S3 或者 PVC 上。除了 PVC，后续还可以用命令的方式，例如 `mysqldump`，把数据库等内容备份出来。

## 安装和初始化

Helm 安装即可：

~~~shell
helm repo add appuio https://charts.appuio.ch
helm repo update
helm install appuio/k8up
~~~

安装过程会生成一系列的 CRD，会在后续步骤中使用。

后续过程中需要两个 Secret，分别用来加密备份和访问 S3：

~~~shell
$ kubectl create secret generic s3secret --from-literal token=[hidden] \
    --from-literal key=[hidden]
secret/s3secret created
$ kubectl create secret generic backup --from-literal password=PassW0rd
secret/backup created
~~~

## 备份 PVC

k8up 会选择命名空间中注解为 `appuio.ch/backup: "true"` 的 PVC 进行备份，我们用下文的工作负载生成两个 PVC，运行起来之后，两个 PVC 分别挂载到容器的 `/data1` 和 `/data2` 目录中，可以登录到 Pod，在其中生成文件：

~~~shell
$ kubectl exec -it debugger-7b8f654484-hrcg9 bash
bash-4.4# echo "Hello world" > /data/data.txt
~~~

创建一次性任务：

~~~yaml
apiVersion: backup.appuio.ch/v1alpha1
kind: Backup
metadata:
  name: backup-now
spec:
  keepJobs: 4
  backend:
    repoPasswordSecretRef:
      name: backup
      key: password
    s3:
      endpoint: https://s3.amazonaws.com
      bucket: dustise
      accessKeyIDSecretRef:
        name: s3secret
        key: token
      secretAccessKeySecretRef:
        name: s3secret
        key: key
~~~

查看 Pod 日志：

~~~shell
$ kubectl logs -f k8up-1578112449-84d7d4d6cc-q6qsh
2020/01/04 14:30:10 [INFO] New backup job received backup-now in namespace default
2020/01/04 14:30:10 [INFO] Listing all PVCs with annotation appuio.ch/backup in namespace default
2020/01/04 14:30:10 [INFO] Adding data to list
...
2020/01/04 14:30:29 [INFO] default/backupjob-1578148210 is running
2020/01/04 14:30:37 [INFO] default/backupjob-1578148210 finished successfully
~~~

两个卷的备份均已完成，查看 S3 的情况：

~~~shell
$ s3cmd la
                       DIR   s3://dustise/data/
                       DIR   s3://dustise/index/
                       DIR   s3://dustise/keys/
                       DIR   s3://dustise/snapshots/
2020-01-04 14:30       155   s3://dustise/config
~~~

发现已经初始化了一个备份结构。

## 还原

备份成功之后，我们希望还原一下，看看备份的内容。

新建一个 PVC 用作还原目标：

~~~yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: restore
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
~~~

创建一个还原命令：

~~~yaml
apiVersion: backup.appuio.ch/v1alpha1
kind: Restore
metadata:
  name: restore2pvc
  namespace: default
spec:
  backend:
    repoPasswordSecretRef:
      key: password
      name: backup
    s3:
      accessKeyIDSecretRef:
        key: token
        name: s3secret
      bucket: dustise
      endpoint: https://s3.amazonaws.com
      secretAccessKeySecretRef:
        key: key
        name: s3secret
  restoreMethod:
    folder:
      claimName: restore
~~~

查看运行日志：

~~~shell
$ kubectl logs -f k8up-1578112449-84d7d4d6cc-q6qsh
2020/01/04 14:43:45 [INFO] Received restore job restore-now in namespace default
2020/01/04 14:43:45 [INFO] default/restorejob-1578149025 is running
2020/01/04 14:43:45 [INFO] default/restorejob-1578149025 is running
2020/01/04 14:43:59 [INFO] default/restorejob-1578149025 is running
2020/01/04 14:44:15 [INFO] default/restorejob-1578149025 finished successfully
~~~

查看该卷内容，会发现其中有一个 `data/data` 目录，包含了我们的备份内容。

## 附录

### 连接

- `https://k8up.io/`

- `https://github.com/restic/restic`

### 源码

~~~yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: debugger
  name: debugger
spec:
  replicas: 1
  selector:
    matchLabels:
      app: debugger
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: debugger
    spec:
      containers:
      - image: dustise/sleep
        name: sleep
        resources: {}
        volumeMounts:
        - name: data
          mountPath: /data
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: data
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: data
  annotations:
    appuio.ch/backup: "true"
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
~~~
