Velero(https://velero.io)(可以提供備份和還原 Kubernetes 集羣資源和持久卷的能力,你可以在公有云或本地搭建的私有云環境安裝 Velero,可以爲你提供以下能力:

  • 備份集羣數據,並在集羣故障的情況下進行還原

  • 將集羣資源遷移到其他集羣

  • 將生產集羣複製到開發和測試集羣

Velero 包含一個在集羣上運行的服務器端和在本地運行的命令行客戶端。

原理

每個 Velero 的操作(比如按需備份、計劃備份、還原)都是 CRD 自定義資源,Velero 可以備份或還原集羣中的所有對象,也可以按類型、namespace 或標籤過濾對象。Velero 是 Kubernetes 用來災難恢復的理想選擇,也可以在集羣上執行系統操作(比如升級)之前對應用程序狀態進行快照的理想選擇。

按需備份

按需備份操作可以將複製的 Kubernetes 對象的壓縮文件上傳到雲對象存儲中,也可以調用雲環境提供的 API 來創建持久化卷的磁盤快照。我們可以選擇指定在備份期間執行的備份 hook,比如你可能需要在拍攝快照之前告訴數據庫將其內存中的緩衝區刷新到磁盤。

需要注意的是集羣備份並不是嚴格的原子備份,如果在備份時創建或編輯 Kubernetes 對象,則它們可能不會被包含在備份中,是可能出現這種狀況的。

定時備份

通過定時操作,我們可以定期備份數據,第一次創建日程表時將執行第一次備份,隨後的備份將按日程表指定的間隔進行備份,這些間隔由 Cron 表達式指定。

定時備份保存的名稱爲 <SCHEDULE NAME>-<TIMESTAMP>,其中 <TIMESTAMP> 格式爲 YYYYMMDDhhmmss

備份還原

通過還原操作,我們可以從以前創建的備份中還原所有對象和持久卷,此外我們還可以僅還原對象和持久卷的子集,Velero 支持多個命名空間重新映射。例如在一次還原操作中,可以在命名空間 def 下重新創建命名空間 abc 中的對象,或在 456 之下重新創建名稱空間 123 中的對象。

還原的默認名稱爲 <BACKUP NAME>-<TIMESTAMP><TIMESTAMP> 格式爲 YYYYMMDDhhmmss,還可以指定自定義名稱,恢復的對象還包括帶有鍵 velero.io/restore-name 和值的標籤 <RESTORE NAME>

默認情況下,備份存儲位置以讀寫模式創建,但是,在還原期間,可以將備份存儲位置配置爲只讀模式,這將禁用該存儲位置的備份創建和刪除,這對於確保在還原方案期間不會無意間創建或刪除任何備份非常有用。此外我們還可以選擇指定在還原期間或還原資源後執行的還原 hook,例如可能需要在數據庫應用程序容器啓動之前執行自定義數據庫還原操作。

備份流程

執行命令 velero backup create test-backup 的時候,會執行下面的操作:

  • Velero 客戶端調用 Kubernetes APIServer 創建 Backup 這個 CRD 對象

  • Backup 控制器 watch 到新的 Backup 對象被創建並執行驗證

  • Backup 控制器開始執行備份,通過查詢 APIServer 來獲取資源收集數據進行備份

  • Backup 控制器調用對象存儲服務,比如 S3 上傳備份文件

默認情況下 velero backup create 支持任何持久卷的磁盤快照,可以通過指定其他參數來調整快照,可以使用 --snapshot-volumes=false 選項禁用快照。

設置備份過期時間

創建備份時,可以通過添加標誌 --ttl 來指定 TTL,如果未指定,則將默認的 TTL 值爲 30 天,如果 Velero 檢測到有備份資源已過期,它將刪除以下相應備份數據:

  • 備份資源

  • 來自雲對象存儲的備份文件

  • 所有PersistentVolume 快照

  • 所有關聯的還原

同步對象存儲

Velero 將對象存儲視爲資源的來源,它不斷檢查以確保始終存在正確的備份資源,如果存儲桶中有格式正確的備份文件,但 Kubernetes APIServer 中沒有相應的備份資源,則 Velero 會將信息從對象存儲同步到 Kubernetes,這使還原功能可以在集羣遷移方案中工作,在該方案中,新集羣中不存在原始的備份對象。同樣,如果備份對象存在於 Kubernetes 中,但不存在於對象存儲中,則由於備份壓縮包不再存在,它將從 Kubernetes 中刪除。

備份存儲位置和卷快照位置

Velero 有兩個自定義資源 BackupStorageLocation 和 VolumeSnapshotLocation,用於配置 Velero 備份及其關聯的持久卷快照的存儲位置。

  • BackupStorageLocation:定義爲存儲區,存儲所有 Velero 數據的存儲區中的前綴以及一組其他特定於提供程序的字段, 後面部分會詳細介紹該部分所包含的字段。

  • VolumeSnapshotLocation:完全由提供程序提供的特定的字段(例如 AWS 區域,Azure 資源組,Portworx 快照類型等)定義。

用戶可以預先配置一個或多個可能的 BackupStorageLocations 對象,也可以預先配置一個或多個 VolumeSnapshotLocations 對象,並且可以在創建備份時選擇應該存儲備份和相關快照的位置。

此配置設計支持許多不同的用法,包括:

  • 在單個 Velero 備份中創建不止一種持久卷的快照。例如,在同時具有 EBS 卷和 Portworx 卷的集羣中

  • 在不同地區將數據備份到不同的存儲中

  • 對於支持它的卷提供程序(例如 Portworx),可以將一些快照存儲在本地集羣中,而將其他快照存儲在雲中

安裝客戶端

wget https://github.com/vmware-tanzu/velero/releases/download/v1.9.3/velero-v1.9.3-linux-amd64.tar.gz
tar zxvf velero-v1.9.3-linux-amd64.tar.gz

cd velero-v1.9.3-linux-amd64/
cp velero /usr/local/bin && chmod +x /usr/local/bin/velero
$ velero version
Client:
Version: v1.9.3
Git commit: 9a617fe5af9c997d69f8f37a5b40d0462edd9b28
<error getting server version: no matches for kind "ServerStatusRequest" in version "velero.io/v1">

安裝 MINIO

這裏我們可以使用 minio 來代替雲環境的對象存儲,在上面解壓的壓縮包中包含一個 examples/minio/00-minio-deployment.yaml 的資源清單文件,爲了測試方便可以將其中的 Service 更改爲 NodePort 類型,我們可以配置一個 console-address 來提供一個 console 頁面的訪問入口,完整的資源清單文件如下所示:

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"

 

然後直接部署在 Kubernetes 集羣中即可:

➜  ~ kubectl apply -f examples/minio/00-minio-deployment.yaml
namespace/velero created
deployment.apps/minio created
service/minio created
job.batch/minio-setup created
➜  ~ kubectl get pods -n velero
NAME                     READY   STATUS      RESTARTS   AGE
minio-5b96ffddf8-x8s7p   1/1     Running     0          2m48s
minio-setup-rhc4d        0/1     Completed   1          2m48s
➜  ~ kubectl get svc -n velero
NAME    TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)                         AGE
minio   NodePort   10.103.132.34   <none>        9000:32036/TCP,9001:32262/TCP   3m56s

然後我們可以通過 http://<nodeip>:32262 訪問 minio 的 console 頁面,使用 minio 與 minio123 進行登錄即可:

建立一組accesskey及SecretKey

安裝 velero 服務端

我們可以使用 velero 客戶端來安裝服務端,也可以使用 Helm Chart 來進行安裝,比如這裏我們用客戶端來安裝,velero 命令默認讀取 kubectl 配置的集羣上下文,所以前提是 velero 客戶端所在的節點有可訪問集羣的 kubeconfig 配置。

首先準備密鑰文件,在當前目錄建立一個空白文本文件,內容如下所示:

[root@node-204 velero]# cat credentials-velero
[default]
aws_access_key_id=uAow4ncmdXNr4BFF
aws_secret_access_key=FFHUujgBlkkxh7yubX4a6mSR7vOS3fsC

替換爲之前步驟中 minio 的對應 access key id 和 secret access key 如果 minio 安裝在 kubernetes 集羣內時按照如下命令安裝 velero 服務端:

[root@dev-rancher velero]#velero install \
--provider aws \
--bucket velero \
--image velero/velero:v1.6.3 \
--plugins velero/velero-plugin-for-aws:v1.2.1  \
--namespace velero  \
--secret-file ./credentials-velero  \
--use-volume-snapshots=false \
--use-restic \
--backup-location-config region=minio,s3ForcePathStyle="true",s3Url=http://minio.velero.svc.cluster.local:9000


CustomResourceDefinition/backups.velero.io: attempting to create resource
CustomResourceDefinition/backups.velero.io: attempting to create resource client
CustomResourceDefinition/backups.velero.io: created
CustomResourceDefinition/backupstoragelocations.velero.io: attempting to create resource
CustomResourceDefinition/backupstoragelocations.velero.io: attempting to create resource client
CustomResourceDefinition/backupstoragelocations.velero.io: created
CustomResourceDefinition/deletebackuprequests.velero.io: attempting to create resource
CustomResourceDefinition/deletebackuprequests.velero.io: attempting to create resource client
CustomResourceDefinition/deletebackuprequests.velero.io: created
CustomResourceDefinition/downloadrequests.velero.io: attempting to create resource
CustomResourceDefinition/downloadrequests.velero.io: attempting to create resource client
CustomResourceDefinition/downloadrequests.velero.io: created
CustomResourceDefinition/podvolumebackups.velero.io: attempting to create resource
CustomResourceDefinition/podvolumebackups.velero.io: attempting to create resource client
CustomResourceDefinition/podvolumebackups.velero.io: created
CustomResourceDefinition/podvolumerestores.velero.io: attempting to create resource
CustomResourceDefinition/podvolumerestores.velero.io: attempting to create resource client
CustomResourceDefinition/podvolumerestores.velero.io: created
CustomResourceDefinition/resticrepositories.velero.io: attempting to create resource
CustomResourceDefinition/resticrepositories.velero.io: attempting to create resource client
CustomResourceDefinition/resticrepositories.velero.io: created
CustomResourceDefinition/restores.velero.io: attempting to create resource
CustomResourceDefinition/restores.velero.io: attempting to create resource client
CustomResourceDefinition/restores.velero.io: created
CustomResourceDefinition/schedules.velero.io: attempting to create resource
CustomResourceDefinition/schedules.velero.io: attempting to create resource client
CustomResourceDefinition/schedules.velero.io: created
CustomResourceDefinition/serverstatusrequests.velero.io: attempting to create resource
CustomResourceDefinition/serverstatusrequests.velero.io: attempting to create resource client
CustomResourceDefinition/serverstatusrequests.velero.io: created
CustomResourceDefinition/volumesnapshotlocations.velero.io: attempting to create resource
CustomResourceDefinition/volumesnapshotlocations.velero.io: attempting to create resource client
CustomResourceDefinition/volumesnapshotlocations.velero.io: created
Waiting for resources to be ready in cluster...
Namespace/velero: attempting to create resource
Namespace/velero: attempting to create resource client
Namespace/velero: already exists, proceeding
Namespace/velero: created
ClusterRoleBinding/velero: attempting to create resource
ClusterRoleBinding/velero: attempting to create resource client
ClusterRoleBinding/velero: created
ServiceAccount/velero: attempting to create resource
ServiceAccount/velero: attempting to create resource client
ServiceAccount/velero: created
Secret/cloud-credentials: attempting to create resource
Secret/cloud-credentials: attempting to create resource client
Secret/cloud-credentials: created
BackupStorageLocation/default: attempting to create resource
BackupStorageLocation/default: attempting to create resource client
BackupStorageLocation/default: created
Deployment/velero: attempting to create resource
Deployment/velero: attempting to create resource client
Deployment/velero: created
DaemonSet/restic: attempting to create resource
DaemonSet/restic: attempting to create resource client
DaemonSet/restic: created
Velero is installed! ⛵ Use 'kubectl logs deployment/velero -n velero' to view the status.

由於我們這裏準備使用 minio 來作爲對象存儲,minio 是兼容 S3 的,所以這裏我們配置的 provider(聲明使用的 Velero 插件類型)是 aws--secret-file 用來提供訪問 minio 的密鑰,--use-restic 表示使用開源免費備份工具 restic 備份和還原持久卷數據,啓用該參數後會部署一個名爲 restic 的 DaemonSet 對象,--plugins 使用的 velero 插件,我們使用 AWS S3 兼容插件。

安裝完成後 velero 的服務端就部署成功了。

[root@node-204 velero]# kubectl get pods -n velero
NAME READY STATUS RESTARTS AGE
minio-6db8cd4c55-wbxwm 1/1 Running 0 4m6s
minio-setup-dq9k6 0/1 Completed 2 4m6s
restic-8dcht 1/1 Running 0 65s
restic-8k8hv 1/1 Running 0 65s
restic-dtlmw 1/1 Running 0 65s
velero-667f844cf9-wjjhs 1/1 Running 0 66s

測試

比如現在我們部署一個 mysql 應用,資源清單文件如下所示:

# mysql-deployment.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pv-claim
  labels:
    app: mysql
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
  labels:
    app: mysql
spec:
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - image: mysql:5.6
        name: mysql
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-pass
              key: password
        livenessProbe:
          tcpSocket:
            port: 3306
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: mysql-persistent-storage
          mountPath: /var/lib/mysql
      volumes:
      - name: mysql-persistent-storage
        persistentVolumeClaim:
          claimName: mysql-pv-claim

直接部署上面的應用:

kubectl create namespace kube-demo
kubectl create secret generic mysql-pass --from-literal=password=password321 -n kube-demo
kubectl apply -f mysql-deployment.yaml -n kube-demo
kubectl get pods -n kube-demo

Longhorn上面也有建立mysql的volume

比如現在我們創建一個新的數據庫 velero:

現在我們來執行一個備份:

[root@node-204 velero]# velero backup create mysql-backup --include-namespaces kube-demo --default-volumes-to-restic
Backup request "mysql-backup" submitted successfully.
Run `velero backup describe mysql-backup` or `velero backup logs mysql-backup` for more details.
[root@node-204 velero]# velero backup get
NAME           STATUS      ERRORS   WARNINGS   CREATED                         EXPIRES   STORAGE LOCATION   SELECTOR
mysql-backup   Completed   0        0          2022-11-28 01:11:25 -0500 EST   29d       default            <none>
[root@node-204 velero]# velero backup describe mysql-backup
Name:         mysql-backup
Namespace:    velero
Labels:       velero.io/storage-location=default
Annotations:  velero.io/source-cluster-k8s-gitversion=v1.23.6
              velero.io/source-cluster-k8s-major-version=1
              velero.io/source-cluster-k8s-minor-version=23

Phase:  Completed

Errors:    0
Warnings:  0

Namespaces:
  Included:  kube-demo
  Excluded:  <none>

Resources:
  Included:        *
  Excluded:        <none>
  Cluster-scoped:  auto

Label selector:  <none>

Storage Location:  default

Velero-Native Snapshot PVs:  auto

TTL:  720h0m0s

Hooks:  <none>

Backup Format Version:  1.1.0

Started:    2022-11-28 01:11:25 -0500 EST
Completed:  2022-11-28 01:11:54 -0500 EST

Expiration:  2022-12-28 01:11:25 -0500 EST

Total items to be backed up:  22
Items backed up:              22

Velero-Native Snapshots: <none included>

Restic Backups (specify --details for more information):
  Completed:  1

其中我們指定的 --default-volumes-to-restic 參數表示使用 restic 備份持久捲到 minio,--include-namespaces 用來備份該命名空間下的所有資源,不包括集羣資源,此外還可以使用 --include-resources 指定要備份的資源類型 ,--include-cluster-resources 指定是否備份集羣資源。

該命令請求創建一個對項目(命名空間)的備份,備份請求發送之後可以用命令查看備份狀態,等到 STATUS 列變爲 Completed 表示備份完成。

備份完成後可以去 minio 的 bucket 上查看是否有對應的備份數據:

現在我們刪除應用所在的命名空間來模擬生產環境發生災難或運維錯誤導致應用失敗的場景:

➜  ~ kubectl delete namespace kube-demo

這個時候我們肯定訪問不了我們的 MySQL 數據庫了,這時候我們可以用一條命令,使用 velero 從 minio 中來恢復應用和數據:

同樣我們可以使用 velero restore get 來查看還原的進度,等到 STATUS 列變爲 Completed 表示還原完成:

[root@node-204 velero]# velero restore create --from-backup mysql-backup
Restore request "mysql-backup-20221128011941" submitted successfully.
Run `velero restore describe mysql-backup-20221128011941` or `velero restore logs mysql-backup-20221128011941` for more details.
[root@node-204 velero]# k9s
[root@node-204 velero]# velero restore get
NAME                          BACKUP         STATUS      STARTED                         COMPLETED                       ERRORS   WARNINGS   CREATED                         SELECTOR
mysql-backup-20221128011941   mysql-backup   Completed   2022-11-28 01:19:41 -0500 EST   2022-11-28 01:20:06 -0500 EST   0        0          2022-11-28 01:19:41 -0500 EST   <none>
[root@node-204 velero]# kubectl get pods -n kube-demo
NAME                    READY   STATUS    RESTARTS   AGE
mysql-cfd859697-p6495   1/1     Running   0          66s
[root@node-204 velero]# kubectl exec -it -n kube-demo mysql-cfd859697-p6495 -- /bin/bash

還原完成後我們再去查看之前我們的 kube-demo 命名空間下面的應用數據是否正確:

可以看到我們的創建的 velero 數據庫依然存在,證明已經完全恢復了。

只要我們將每個 velero 實例指向相同的對象存儲,velero 就能將資源從一個羣集遷移到另一個羣集。此外還支持定時備份,觸發備份 Hooks 等操作,更多資料請查閱官方文檔:https://velero.io/docs/

 

參考原文:https://mp.weixin.qq.com/s/VC6kVfcBCUQfG6RwM6F1QA

By tony

自由軟體愛好者~喜歡不斷的思考各種問題,有新的事物都會想去學習嘗試 做實驗並熱衷研究 沒有所謂頂天的技術 只有謙虛及不斷的學習 精進專業,本站主要以分享系統及網路相關知識、資源而建立。 Github http://stnet253.github.io

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料