详解kubernetes备份恢复利器 Velero | 深入了解Carina系列第三期

语言: CN / TW / HK

详解kubernetes备份恢复利器 Velero | 深入了解Carina系列第三期

前言

Carina 是由博云主导并发起的云原生本地存储项目(GitHub 地址为:https://github.com/carina-io/carina),目前已经进入 CNCF 全景图。Carina 可以为云原生环境中的有状态应用提供高性能免运维本地存储解决方案,具有存储卷生命周期管理、LVM/RAW 盘供应、智能调度、RAID 管理、自动分层等能力,旨在为云原生有状态服务提供极低延迟、免运维、懂数据库的数据存储系统。Carina 作为博云容器云平台的组件之一,已经在多个金融机构的生产环境中稳定运行多年。

传统的数据备份方案主要有两种, 一种是利用存储数据的服务端实现基于快照的备份,另一种是在每台目标服务器上部署专有备份 agent 并指定备份数据目录,定期把数据复制到外部存储上。这两种方式的备份机制相对固化,在云原生时代无法适应容器化后的弹性、池化等部署场景。

以云原生存储插件 Carina 为例,数据库等数据敏感场景中每个数据库集群包括多个计算实例,实例可能在集群内任意漂移并实现自动故障恢复。传统数据备份方式在数据库集群快速扩缩容、跨节点漂移等场景下无法自动跟随计算实例迁移从而导致数据备份失效,因此一款贴合 k8s 容器场景的备份工具就十分重要。

Kubernetes 备份恢复利器:velero

Velero 是一款云原生时代的灾难恢复和迁移工具,采用 Go 语言编写,并在 github 上进行了开源,开源地址为:https://github.com/vmware-tanzu/velero。Velero 源于西班牙语,意思为帆船,非常符合 Kubernetes 社区的命名风格。

利用 velero 用户可以安全的备份、恢复和迁移 Kubernetes 集群资源和持久卷。它的基本原理就是将集群的数据,例如集群资源和持久化数据卷备份到对象存储中,在恢复的时候将数据从对象存储中拉取下来。除了灾备之外它还能做资源移转,支持把容器应用从一个集群迁移到另一个集群,这也是 velero 一个非常成功的使用场景。

Velero 主要包括连个核心组件,分别为服务端和客户端。服务端运行在具体的 Kubernetes 集群中,客户端是运行在本地的命令行工具,只要配置好 kubectl 及 kubeconfig 即可使用,非常简单。

Velero 基于其实现的 kubernetes 资源备份能力,可以轻松实现 Kubernetes 集群的数据备份和恢复、复制 kubernetes 集群资源到其他 kubernetes 集群或者快速复制生产环境到测试环境等功能。

在资源备份方面,velero 支持将数据备份到众多的云存储中,例如 AWS S3 或 S3 兼容的存储系统、Azure Blob、Google Cloud 存储、Aliyun OSS 等。与备份整个 kubernetes 的数据存储引擎 etcd 相比,velero 的控制更加细化,可以对 Kubernetes 集群内对象级别进行备份,还可以通过对 Type、Namespace、Label 等对象进行分类备份或者恢复。

Velero 工作流程

以核心的数据备份为例,当执行 velero backup create my-backup 时:

  • Velero 客户端首先调用 Kubernetes API 服务器以创建 Backup 对象;

  • BackupController 将收到通知有新的 Backup 对象被创建并执行验证;

  • BackupController 开始备份过程,它通过查询 API 服务器以获取资源来收集数据以进行备份;

  • BackupController 将调用对象存储服务,例如,AWS S3 -上传备份文件。默认情况下,velero backup create 支持任何持久卷的磁盘快照,可以通过指定其他标志来调整快照,运行 velero backup create --help 可以查看可用的标志,也可以使用--snapshot-volumes=false 选项禁用快照。

关于备份存储位置和卷快照,Velero 有两个自定义资源 BackupStorageLocation 和 VolumeSnapshotLocation,用于配置 Velero 备份及其关联的持久卷快照的存储位置。

  • BackupStorageLocation 主要支持的后端存储是 S3 兼容的存储,存储所有 Velero 数据的存储区中的前缀以及一组其他特定于提供程序的字段。比如:Minio 和阿里云 OSS 等 ;

  • VolumeSnapshotLocation(pv 数据),主要用来给 PV 做快照,需要云提供商提供插件,完全由提供程序提供的特定的字段(例如 AWS 区域,Azure 资源组,Portworx 快照类型等)定义。以对数据一致性最为敏感的数据库和中间件为例,开源存储插件 Carina 也即将提供数据库感知的 velero 卷快照功能,可以实现中间件数据的快速备份及恢复。

Velero 安装和使用

安装 velero 客户端

$ wget https://mirror.ghproxy.com/https://github.com/vmware-tanzu/velero/releases/download/v1.6.3/velero-v1.6.3-darwin-amd64.tar.gz $ tar -zxvf velero-v1.6.3-darwin-amd64.tar.gz && cd velero-v1.6.3-darwin-amd64 $ mv velero /usr/local/bin && chmod +x /usr/local/bin/velero $ velero version

复制代码

安装 minio 作为数据备份后端

Minio 安装 Yaml 文件如下:

apiVersion: v1 kind: Namespace metadata:   name: velero --- apiVersion: apps/v1 kind: Deployment metadata:   namespace: velero   name: minio   labels:     component: minio spec:   strategy:     type: Recreate   selector:     matchLabels:       component: minio   template:     metadata:       labels:         component: minio     spec:       volumes:       - name: storage         emptyDir: {}       - name: config         emptyDir: {}       containers:       - name: minio         image: minio/minio:latest         imagePullPolicy: IfNotPresent         args:         - server         - /storage         - --config-dir=/config         - --console-address=:9001         env:         - name: MINIO_ACCESS_KEY           value: "minio"         - name: MINIO_SECRET_KEY           value: "minio123"         ports:         - containerPort: 9000         - containerPort: 9001         volumeMounts:         - name: storage           mountPath: "/storage"         - name: config           mountPath: "/config" --- apiVersion: v1 kind: Service metadata:   namespace: velero   name: minio   labels:     component: minio spec:   type: NodePort   ports:     - name: api       port: 9000       targetPort: 9000     - name: console       port: 9001       targetPort: 9001   selector:     component: minio --- apiVersion: batch/v1 kind: Job metadata:   namespace: velero   name: minio-setup   labels:     component: minio spec:   template:     metadata:       name: minio-setup     spec:       restartPolicy: OnFailure       volumes:       - name: config         emptyDir: {}       containers:       - name: mc         image: minio/mc:latest         imagePullPolicy: IfNotPresent         command:         - /bin/sh         - -c         - "mc --config-dir=/config config host add velero http://minio:9000 minio minio123 && mc --config-dir=/config mb -p velero/velero"         volumeMounts:         - name: config           mountPath: "/config"

复制代码

安装 Mini,并检查资源创建情况。

$ kubectl apply -f ./00-minio-deployment.yaml $ kubectl get pods -n velero NAME                     READY   STATUS              RESTARTS   AGEminio-58dc5cf789-z2777   0/1     ContainerCreating   0          14sminio-setup-dz4jb        0/1     ContainerCreating   0          6s$ kubectl get svc  -n velero NAME    TYPE       CLUSTER-IP    EXTERNAL-IP   PORT(S)                         AGEminio   NodePort   10.96.13.35   <none>        9000:30693/TCP,9001:32351/TCP   17s

复制代码

待服务都已经启动完毕,可以登录 minio 查看 velero/velero 的 bucket 是否创建成功。

安装 velero 服务端 ,使用 s3 作为存储

  • 创建 minio 凭证

    $ cat > credentials-velero <<EOF[default]aws_access_key_id = minioaws_secret_access_key = minio123EOF# 安装velero$ cp velero /usr/bin/# 启用快速补全$ velero completion bash

复制代码

  • 使用官方提供的 restic 组件备份 pv

    $ velero install \ --image velero/velero:v1.6.3 \ --plugins velero/velero-plugin-for-aws:v1.0.0 \ --provider aws \ --bucket velero \ --namespace velero \ --secret-file ./credentials-velero \ --velero-pod-cpu-request 200m \ --velero-pod-mem-request 200Mi \ --velero-pod-cpu-limit 1000m \ --velero-pod-mem-limit 1000Mi \ --use-volume-snapshots=false \ --use-restic \ --restic-pod-cpu-request 200m \ --restic-pod-mem-request 200Mi \ --restic-pod-cpu-limit 1000m \ --restic-pod-mem-limit 1000Mi \ --backup-location-config region=minio,s3ForcePathStyle="true",s3Url=http://minio.velero.svc:9000

复制代码

其中,几个重要的参数及其说明如下:

--provider:声明使用的 Velero 插件类型。--plugins:使用 S3 API 兼容插件 “velero-plugin-for-aws ”。--bucket:在腾讯云 COS 创建的存储桶名。--secret-file:访问 COS 的访问凭证文件,见上面创建的 “credentials-velero”凭证文件。--use-restic:使用开源免费备份工具 restic 备份和还原持久卷数据。--default-volumes-to-restic:使用 restic 来备份所有Pod卷,前提是需要开启 --use-restic 参数。--backup-location-config:备份存储桶访问相关配置。--region:兼容 S3 API 的 COS 存储桶地区,例如创建地区是广州的话,region 参数值为“ap-guangzhou”。--s3ForcePathStyle:使用 S3 文件路径格式。--s3Url:COS 兼容的 S3 API 访问地址--use-volume-snapshots=false 来关闭存储卷数据快照备份。

复制代码

安装命令执行完成后,等待 Velero 和 restic 工作负载就绪后,查看配置的存储位置是否可用。

$ velero backup-location get 
apiVersion: velero.io/v1 kind: BackupStorageLocation metadata:   name: default   namespace: velero spec: # 只有 aws gcp azure   provider: aws   objectStorage:     bucket: myBucket     prefix: backup   config:     region: us-west-2      profile: "default"     s3ForcePathStyle: "false"     s3Url: http://minio:9000

复制代码

至此 velero 就已经全部部署完成。

velero 功能介绍

创建备份

velero 支持备份所有对象,也可以按类型,名称空间和/或标签过滤对象

$ velero create backup $NAME [flags]$ velero backup create pvc-backup-1  --snapshot-volumes --include-namespaces nginx-example --default-volumes-to-restic --volume-snapshot-locations default

复制代码

其中:

--include-namespaces:备份该命名空间下的所有资源,不包括集群资源

--include-resources:要备份的资源类型

--include-cluster-resources:是否备份集群资源 此选项可以具有三个可能的值: true:包括所有集群范围的资源; false:不包括集群范围内的资源; nil (“自动”或不提供)

--selector:通过标签选择匹配的资源备份

--exclude-namespaces:备份时该命名空间下的资源不进行备份

--exclude-resources:备份时该类型的资源不进行备份

http://--velero.io/exclude-from-backup=true:当标签选择器匹配到该资源时,若该资源带有此标签,也不进行备份

同时,也可以通过使用 –ordered-resources 参数,按特定顺序备份特定种类的资源,需要指定资源名称和该资源的对象名称列表,资源对象名称以逗号分隔,其名称格式为“命名空间/资源名称”,对于集群范围资源,只需使用资源名称。映射中的键值对以分号分隔,资源类型是复数形式。

$ velero backup create backupName --include-cluster-resources=true --ordered-resources 'pods=ns1/pod1,ns1/pod2;persistentvolumes=pv4,pv8' --include-namespaces=ns1
$ velero backup create backupName --ordered-resources 'statefulsets=ns1/sts1,ns1/sts0' --include-namespaces=n

复制代码

定时备份:

$ velero schedule create <SCHEDULE NAME> --schedule "0 7 * * *"
$ velero create schedule NAME --schedule="@every 6h"
$ velero create schedule NAME --schedule="@every 24h" --include-namespaces web
$ velero create schedule NAME --schedule="@every 168h" --ttl 2160h0m0s

复制代码

备份高级用法举例

  • 在单个 Velero 备份中创建不止一种持久卷的快照

    $ velero snapshot-location create ebs-us-east-1 \ --provider aws \ --config region=us-east-1 $ velero snapshot-location create portworx-cloud \ --provider portworx \ --config type=cloud $ velero backup create full-cluster-backup \ --volume-snapshot-locations ebs-us-east-1,portworx-cloud

复制代码

  • 在不同的地区将备份存储到不同的对象存储桶中

    $ velero backup-location create default \ --provider aws \ --bucket velero-backups \ --config region=us-east-1 $ velero backup-location create s3-alt-region \ --provider aws \ --bucket velero-backups-alt \ --config region=us-west-1 $ velero backup create full-cluster-alternate-location-backup \ --storage-location s3-alt-region

复制代码

  • 对于公有云提供的存储卷,将一部分快照存储在本地,一部分存储在公有云

    $ velero snapshot-location create portworx-local \ --provider portworx \ --config type=local $ velero snapshot-location create portworx-cloud \ --provider portworx \ --config type=cloud $ velero backup create cloud-snapshot-backup \ --volume-snapshot-locations portworx-cloud

复制代码

  • 使用存储位置

    $ velero backup-location create default \ --provider aws \ --bucket velero-backups \ --config region=us-west-1 $ velero snapshot-location create ebs-us-west-1 \ --provider aws \ --config region=us-west-1 $ velero backup create full-cluster-backup

复制代码

查看备份任务。

当备份任务状态是 “Completed” ,且错误数为 0 ,说明备份任务完成且没发生任何错误,可以通过以下命令查询:

 $ velero backup get

复制代码

通过先临时将备份存储位置更新为只读模式,可以防止在还原过程中在备份存储位置中创建或删除备份对象。

$ kubectl patch backupstoragelocation default --namespace velero \    --type merge \    --patch '{"spec":{"accessMode":"ReadOnly"}}'    velero backup-location getNAME      PROVIDER   BUCKET/PREFIX   PHASE     LAST VALIDATED   ACCESS MODE   DEFAULTdefault   aws        velero          Unknown   Unknown          ReadWrite     true

复制代码

恢复备份数据

$ velero restore create --from-backup <backup-name>$ velero  restore create --from-backup pvc-backup-1 --restore-volumes

复制代码

查看恢复任务。

$ velero restore get

复制代码

还原完成后,不要忘记把备份存储位置恢复为读写模式,以便下次备份任务使用:

$ kubectl patch backupstoragelocation default --namespace velero \   --type merge \   --patch '{"spec":{"accessMode":"ReadWrite"}}'

复制代码

备份 hooks 介绍

Velero 支持在备份任务执行之前和执行后在容器中执行一些预先设定好的命令,这种方式对数据一致性等非常有效。velero 支持两种方法指定钩子,一种是 pod 本身的注释声明,另一种是在定义 Backup 任务时的 Spec 中声明。

  • Pre hooks

    pre.hook.backup.velero.io/container:将要执行命令的容器,默认为pod中的第一个容器,可选的。 pre.hook.backup.velero.io/command:要执行的命令,如果需要多个参数,请将该命令指定为JSON数组。例如:["/usr/bin/uname", "-a"] pre.hook.backup.velero.io/on-error:如果命令返回非零退出代码如何处理。默认为“Fail”,有效值为“Fail”和“Continue”,可选的。 pre.hook.backup.velero.io/timeout:等待命令执行的时间,如果命令超过超时,则认为该挂钩失败的。默认为30秒,可选的。

复制代码

  • Post hooks

    post.hook.backup.velero.io/container:将要执行命令的容器,默认为pod中的第一个容器,可选的。 post.hook.backup.velero.io/command:要执行的命令,如果需要多个参数,请将该命令指定为JSON数组。例如:["/usr/bin/uname", "-a"] post.hook.backup.velero.io/on-error:如果命令返回非零退出代码如何处理。默认为“Fail”,有效值为“Fail”和“Continue”,可选的。 post.hook.backup.velero.io/timeout:等待命令执行的时间,如果命令超过超时,则认为该挂钩失败的。默认为30秒,可选的

复制代码

还原 hooks 介绍

Velero 支持还原 hooks,可以在还原任务执行前或还原过程之后执行的自定义操作。有以下两种定义形式:

  • InitContainer Restore Hooks:这些将在待还原的 Pod 的应用程序容器启动之前将 init 容器添加到还原的 pod 中,以执行任何必要的设置。

    init.hook.restore.velero.io/container-image:要添加的init容器的容器镜像 init.hook.restore.velero.io/container-name:要添加的init容器的名称 init.hook.restore.velero.io/command:将要在初始化容器中执行的任务或命令

复制代码

如进行备份之前,请使用以下命令将注释添加到 Pod:

kubectl annotate pod -n <POD_NAMESPACE> <POD_NAME> \    init.hook.restore.velero.io/container-name=restore-hook \    init.hook.restore.velero.io/container-image=alpine:latest \    init.hook.restore.velero.io/command='["/bin/ash", "-c", "date"]'

复制代码

  • Exec Restore Hooks:可用于在已还原的 Kubernetes pod 的容器中执行自定义命令或脚本。

    post.hook.restore.velero.io/container:;执行hook的容器名称,默认为第一个容器,可选 post.hook.restore.velero.io/command:将在容器中执行的命令,必填 post.hook.restore.velero.io/on-error:如何处理执行失败,有效值为Fail和Continue,默认为Continue,使用Continue模式,仅记录执行失败;使用Fail模式时,将不会在自行其他的hook,还原的状态将为PartiallyFailed,可选 post.hook.restore.velero.io/exec-timeout:开始执行后要等待多长时间,默认为30秒,可选 post.hook.restore.velero.io/wait-timeout:等待容器准备就绪的时间,该时间应足够长,以使容器能够启动,并

复制代码

如进行备份之前,请使用以下命令将注释添加到 Pod

kubectl annotate pod -n <POD_NAMESPACE> <POD_NAME> \    post.hook.restore.velero.io/container=postgres \    post.hook.restore.velero.io/command='["/bin/bash", "-c", "psql < /backup/backup.sql"]' \    post.hook.restore.velero.io/wait-timeout=5m \    post.hook.restore.velero.io/exec-timeout=45s \    post.hook.restore.velero.io/on-error=Continue

复制代码

Velero 部分关键问题解析

Velero 可以将资源还原到与其备份来源不同的命名空间中吗?

是的,可以使用--namespace-mappings 参数来指定:

velero restore create RESTORE_NAME \  --from-backup BACKUP_NAME \  --namespace-mappings old-ns-1:new-ns-1,old-ns-2:new-ns-2

复制代码

执行还原操作后,已有的 NodePort 类型的 service 如何处理?

Velero 有一个参数,可让用户决定保留原来的 nodePorts。

velero restore create 子命令具有 --preserve-nodeports 标志保护服务 nodePorts。此标志用于从备份中保留原始的 nodePorts,可用作--preserve-nodeports 或--preserve-nodeports=true 如果给定此标志,则 Velero 在还原 Service 时不会删除 nodePorts,而是尝试使用备份时写入的 nodePorts。

velero 怎么实现不影响业务前提下得一致性备份策略,并将备份数据上传到对象存储中?

如果是基于 velero 实现数据库的一致性,需要用 velero 的 hook,在备份前对数据库进行 quiesce 操作,备份完 unquiesce。对于备份本身,可以使用 restic 来 copy 数据(但不用快照),或者使用快照的方式。