# Velero
Velero 是 VMWare 开源的 k8s 集群备份、迁移工具。可以帮助我们完成 k8s 的例行备份工作，以便在出现上面问题的时候可以快速进行恢复。同时也提供了集群迁移功能，可以将 k8s 资源迁移到其他 k8s 集群的功能。Velero 将集群资源保存在对象存储中，默认情况下可以使用 AWS、Azure、GCP 的对象存储，同时也给出了插件功能用来拓展其他平台的存储，这里我们用到的就是阿里云的对象存储 OSS，阿里云也提供了 Velero 的插件，用于将备份存储到 OSS 中。下面我就介绍一下如何在阿里云容器服务 ACK 使用 Velero 完成备份和迁移。

>Velero 地址：https://github.com/vmware-tanzu/velero

>ACK 插件地址：https://github.com/AliyunContainerService/velero-plugin

# 下载 Velero 客户端

Velero 由客户端和服务端组成，服务器部署在目标 k8s 集群上，而客户端则是运行在本地的命令行工具。

- 前往 [Velero 的 Release](https://github.com/vmware-tanzu/velero/releases) 页面 下载客户端，直接在 GitHub 上下载即可
- 解压 release 包
- 将 release 包中的二进制文件 velero 移动到 $PATH 中的某个目录下
- 执行 velero -h 测试

# 创建 OSS bucket

创建一个 OSS bucket 用于存储备份文件，这里也可以用已有的 bucket，之后会在 bucket 中创建 backups、metadata、restores三个目录，这里建议在已有的 bucket 中创建一个子目录用于存储备份文件。

创建 OSS 的时候一定要选对区域，要和 ACK 集群在同一个区域，存储类型和读写权限选择标准存储和私有：

![](http://images.joker.irybd.com/images/20201103180542_4Owuf6_Screenshot.jpeg)

# 创建阿里云 RAM 用户
这里需要创建一个阿里云 RAM 的用户，用于操作 OSS 以及 ACK 资源。

- 新建权限策略
![](http://images.joker.irybd.com/images/20201103181002_vWU3Ra_Screenshot.jpeg)

策略内容如下：
```bash
    {
        "Version": "1",
        "Statement": [
            {
                "Action": [
                    "ecs:DescribeSnapshots",
                    "ecs:CreateSnapshot",
                    "ecs:DeleteSnapshot",
                    "ecs:DescribeDisks",
                    "ecs:CreateDisk",
                    "ecs:Addtags",
                    "oss:PutObject",
                    "oss:GetObject",
                    "oss:DeleteObject",
                    "oss:GetBucket",
                    "oss:ListObjects",
                    "oss:ListBuckets"
                ],
                "Resource": [
                    "*"
                ],
                "Effect": "Allow"
            }
        ]
    }
```

- 新建用户
在新建用户的时候要选择 编程访问，来获取 AccessKeyID 和 AccessKeySecret，这里请创建一个新用于用于备份，不要使用老用户的 AK 和 AS。另外新建用户之后添加权限绑定上面自定义的权限策略。
![创建用户](http://images.joker.irybd.com/images/20201103181201_slfO0t_Screenshot.jpeg)
![](http://images.joker.irybd.com/images/20201103181244_Z3C7On_Screenshot.jpeg)
![](http://images.joker.irybd.com/images/20201103181322_vYCX12_Screenshot.jpeg)


# 部署服务端
- 拉取 Velero 插件 到本地
```git clone git@git.zhiweidata.top:root/velero-plugin.git```

- 配置修改


**1.修改 install/credentials-velero 文件**

将新建用户中获得的 AccessKeyID 和 AccessKeySecret 填入，这里的 OSS EndPoint 为之前 OSS 的访问域名（注：这里需要选择外网访问的 EndPoint。）：

![](http://images.joker.irybd.com/images/20201103181416_1kT5Ew_Screenshot.jpeg)

```
ALIBABA_CLOUD_ACCESS_KEY_ID=<ALIBABA_CLOUD_ACCESS_KEY_ID>
ALIBABA_CLOUD_ACCESS_KEY_SECRET=<ALIBABA_CLOUD_ACCESS_KEY_SECRET>
ALIBABA_CLOUD_OSS_ENDPOINT=<ALIBABA_CLOUD_OSS_ENDPOINT>
```

**2.修改 `install/01-velero.yaml`**

将 OSS 配置填入：
```yaml
---
apiVersion: velero.io/v1
kind: BackupStorageLocation
metadata:
  labels:
    component: velero
  name: default
  namespace: velero
spec:
  config:
    region: <REGION>    # 地域，如果是华东2（上海），则为 cn-shanghai
  objectStorage:
    bucket: <ALIBABA_CLOUD_OSS_BUCKET>  # OSS bucket 名称
    prefix: <OSS_PREFIX>    # bucket 子目录
  provider: alibabacloud

```

**3.kubernetes 部署 velero 服务**
```shell
# 新建 namespace
$ kubectl create namespace velero

# 部署 credentials-velero 的 secret
$ kubectl create secret generic cloud-credentials --namespace velero --from-file cloud=install/credentials-velero

# 部署 CRD
$ kubectl apply -f install/00-crds.yaml

# 部署 Velero
$ kubectl apply -f install/01-velero.yaml
```

**4.测试 Velero 状态**
```shell
[root@node47 velero-plugin]# velero version
Client:
	Version: v1.2.0
	Git commit: 5d008491bbf681658d3e372da1a9d3a21ca4c03c
Server:
	Version: v1.2.0
```

**5.服务端清理**
```shell
$ kubectl delete namespace/velero clusterrolebinding/velero
$ kubectl delete crds -l component=velero
```

**6.设置 velero 命令自动补齐**
```shell
$ source <(velero completion bash)
$ echo 'source <(velero completion bash)' >>~/.bashrc
$ kubectl completion bash >/etc/bash_completion.d/velero
```

# 备份测试
velero-plugin 项目中已经给出 example 用于测试备份。

- 部署测试服务
```kubectl apply -f examples/base.yaml```
- 对 nginx-example 所在的 namespace 进行备份
```velero backup create nginx-backup --include-namespaces nginx-example --wait```
- 模拟 namespace 被误删
```kubectl delete namespaces nginx-example```
- 使用 Velero 进行恢复
```velero restore create --from-backup nginx-backup --wait```

# 集群迁移

迁移方法同备份，在备份后切换集群，在新集群恢复备份即可。

注意要在两个集群中部署同样的服务端，要使用同一个存储源，如果使用阿里云 OSS，要保持 bucket、prefix、region 以及认证信息一致，然后在 A 集群中备份你需要迁移的对象：
```shell
$ velero create backup move-backup --include-namespaces default
Backup request "move-backup" submitted successfully.
Run `velero backup describe move-backup` or `velero backup logs move-backup` for more details.
```
查看阿里云 OSS 对象存储,已将资源 default 名称空间中的所有资源对象都保存到 OSS 中。
![](http://images.joker.irybd.com/images/20201104154047_B7Vnnj_Screenshot.jpeg)

在集群 B 中恢复 default 名称空间的资源对象：
```shell
# 首先查看集群 B 中 default 名称空间中并没有资源对象
[root@node24 ~]# kubectl get pod  | wc -l
No resources found in default namespace.
0

# 开始恢复
$ velero restore create --from-backup move-backup --wait
Restore request "move-backup-20201104160412" submitted successfully.
Waiting for restore to complete. You may safely press ctrl-c to stop waiting - your restore will continue in the background.
.......................
Restore completed with status: PartiallyFailed. You may check for more information using the commands `velero restore describe move-backup-20201104160412` and `velero restore logs move-backup-20201104160412`

# 恢复后查看 default 名称空间的资源对象
[root@node24 ~]# kubectl get pod  | wc -l
48
```


# 高级用法

- 定时备份

  对集群资源进行定时备份，则可在发生意外的情况下，进行恢复（默认情况下，备份保留 30 天）。
```shell
# 每日1点进行备份
$ velero create schedule <SCHEDULE NAME> --schedule="0 1 * * *"

# 每日1点进行备份，备份保留48小时
$ velero create schedule <SCHEDULE NAME> --schedule="0 1 * * *" --ttl 48h

# 每6小时进行一次备份
$ velero create schedule <SCHEDULE NAME> --schedule="@every 6h"

# 每日对 web namespace 进行一次备份
$ velero create schedule <SCHEDULE NAME> --schedule="@every 24h" --include-namespaces web
```
>定时备份的名称为：`<SCHEDULE NAME>-<TIMESTAMP>`，恢复命令为：`velero restore create --from-backup <SCHEDULE NAME>-<TIMESTAMP>`。

- 备份删除

  直接执行命令进行删除

  `velero delete backups <BACKUP_NAME>`

- 备份资源查看

  备份查看

  `velero backup get`

- 查看定时备份
  `velero schedule get`

- 查看可恢复备份 `  velero restore get`

- 备份排除项目 
  可为资源添加指定标签，添加标签的资源在备份的时候被排除。
  ```shell
  # 添加标签
  $ kubectl label -n <ITEM_NAMESPACE> <RESOURCE>/<NAME> velero.io/exclude-from-backup=true
  
  # 为 default namespace 添加标签
  $ kubectl label -n default namespace/default velero.io/exclude-from-backup=true
  ```

# 问题汇总
## 时区问题

进行定时备份时，发现备份使用的事 UTC 时间，并不是本地时间，经过排查后发现是 velero 镜像的时区问题，在调整后就会正常定时备份了，这里我重新调整了时区，直接调整镜像就好，修改 install/01-velero.yaml 文件，将镜像替换为 registry-vpc.cn-shanghai.aliyuncs.com/keking/velero:latest。

```yaml
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: velero
  namespace: velero
spec:
  replicas: 1
  selector:
    matchLabels:
      deploy: velero
  template:
    metadata:
      annotations:
        prometheus.io/path: /metrics
        prometheus.io/port: "8085"
        prometheus.io/scrape: "true"
      labels:
        component: velero
        deploy: velero
    spec:
      serviceAccountName: velero
      containers:
      - name: velero
        # sync from velero/velero:v1.5.2
        image: registry.cn-hangzhou.aliyuncs.com/irybd-package/velero:v1.5.2   # 修复时区后的镜像
        imagePullPolicy: IfNotPresent
        command:
          - /velero
        args:
          - server
          - --default-volume-snapshot-locations=alibabacloud:default
        env:
          - name: VELERO_SCRATCH_DIR
            value: /scratch
          - name: ALIBABA_CLOUD_CREDENTIALS_FILE
            value: /credentials/cloud
        volumeMounts:
          - mountPath: /plugins
            name: plugins
          - mountPath: /scratch
            name: scratch
          - mountPath: /credentials
            name: cloud-credentials
      initContainers:
      - image: registry.cn-hangzhou.aliyuncs.com/acs/velero-plugin-alibabacloud:v1.2
        imagePullPolicy: IfNotPresent
        name: velero-plugin-alibabacloud
        volumeMounts:
        - mountPath: /target
          name: plugins
      volumes:
        - emptyDir: {}
          name: plugins
        - emptyDir: {}
          name: scratch
        - name: cloud-credentials
          secret:
            secretName: cloud-credentials
```