本検証では、Velero(以前の名称はHeptio Ark)の動作検証を行います。2019/12時点ではVMware社のOpen Source Projectsのひとつとして開発が進められています。Veleroは、DR(災害復旧: Disaster Recovery)・マイグレーション・データ保護のユースケースをターゲットに、Kubernetesのリソースと永続ボリューム(Persistent Volume)のバックアップ・リストアを行うツールです。Veleroにより、取得されたバックアップデータは、オブジェクトストレージに保存されます。Persistent VolumeのデータもVolume Snapshot(※1)としてオブジェクトストレージに保存されます。
また、VeleroはWebアプリケーションのようなステートレスアプリケーションだけでなく、Persistent Volumeのバックアップも可能なため、データベースのようなステートフルアプリケーションにも対応しています。ただし、以下のような制限/注意事項がありますので注意してください。
- プロバイダ(クラウドプロバイダ)毎に単一のクレデンシャルのみサポートです
- プロバイダによりVolume Snapshotは、取得できる場所に制限があります。KubernetesのPersistent Volumeと異なるリージョンにVolume Snapshotのバックアップを保存しようとするとバックアップが失敗します
- 各Veleroのバックアップ先は、1つのBackupStorageLocation(アプリケーションなどのリソースの保存先)とVolumeSnapshotLocation(Persistent Volumeのデータの保存先)のみの指定となります
- クロスプロバイダーのVolume Snapshotは非サポートです
- バックアップ作成時にユーザが指定したBackupStorageLocationのBucket(オブジェクトストレージの格納先)のprefix/subdirectory配下にResticデータ(※2)が格納されます
(※1) Veleroでは、Persistent VolumeのバックアップデータのことをVolume Snapshotという用語を使っています。データの差分を取得するストレージのスナップショット技術(Kubernetes Concepts/ Volume snapshots,Kubernetes: Volume Snapshotの検証)とは異なりますので、注意してください。
(※2) Veleroではオープンソースのバックアップツールresticを利用しています。Resticデータとは、resticにより取得されたバックアップデータのことです。resticではいくつかの制限事項があるため、注意してください(hostPahtは非サポートだがLocal Volumeはサポートなど)。
検証環境
以下に、本検証の環境を示します。
- GKE (Kubernetes v1.15.4-gke.22)
- Velero v1.2.0
- Client: macOS 10.14.6
GKEを操作するCLI(gcloud,gsutilなど)はあらかじめセットアップされているものとします。
動作検証
Veleroはスケジュールで定期的にバックアップを取得する方法とCLIでオンデマンドに取得する2種類の実行方法があります。本検証ではCLIでオンデマンドに取得する実行方法を検証します。
セットアップ
まず、veleroコマンドをClientにインストールします。
$ brew install velero
次に、バックアップデータを保存するオブジェクトストレージ(Google Cloud Storage bucket)を作成します。
$ BUCKET=velero-bucket $ gsutil mb gs://$BUCKET/
作成したGoogle Cloud Storage bucketを確認します。
$ gsutil list gs://velero-bucket/
Service Account を作成します。
$ PROJECT_ID=$(gcloud config get-value project) $ gcloud iam service-accounts create velero \ --display-name "Velero service account"
作成したService Accountにパーミッションを付与します。
$ SERVICE_ACCOUNT_EMAIL=$(gcloud iam service-accounts list \ --filter="displayName:Velero service account" \ --format 'value(email)') $ ROLE_PERMISSIONS=( compute.disks.get compute.disks.create compute.disks.createSnapshot compute.snapshots.get compute.snapshots.create compute.snapshots.useReadOnly compute.snapshots.delete compute.zones.get ) $ gcloud iam roles create velero.server \ --project $PROJECT_ID \ --title "Velero Server" \ --permissions "$(IFS=","; echo "${ROLE_PERMISSIONS[*]}")" $ gcloud projects add-iam-policy-binding $PROJECT_ID \ --member serviceAccount:$SERVICE_ACCOUNT_EMAIL \ --role projects/$PROJECT_ID/roles/velero.server $ gsutil iam ch serviceAccount:$SERVICE_ACCOUNT_EMAIL:objectAdmin gs://${BUCKET}
作成したService Accountのキーをファイルに保存します。
$ gcloud iam service-accounts keys create credentials-velero \ --iam-account $SERVICE_ACCOUNT_EMAIL $ ls credentials-velero
velero install
コマンドを使ってKubernetesへVeleroをデプロイします。
$ velero install \ --provider gcp \ --plugins velero/velero-plugin-for-gcp:v1.0.0 \ --bucket $BUCKET \ --secret-file ./credentials-velero CustomResourceDefinition/backups.velero.io: attempting to create resource CustomResourceDefinition/backups.velero.io: created CustomResourceDefinition/backupstoragelocations.velero.io: attempting to create resource CustomResourceDefinition/backupstoragelocations.velero.io: created CustomResourceDefinition/deletebackuprequests.velero.io: attempting to create resource CustomResourceDefinition/deletebackuprequests.velero.io: created CustomResourceDefinition/downloadrequests.velero.io: attempting to create resource CustomResourceDefinition/downloadrequests.velero.io: created CustomResourceDefinition/podvolumebackups.velero.io: attempting to create resource CustomResourceDefinition/podvolumebackups.velero.io: created CustomResourceDefinition/podvolumerestores.velero.io: attempting to create resource CustomResourceDefinition/podvolumerestores.velero.io: created CustomResourceDefinition/resticrepositories.velero.io: attempting to create resource CustomResourceDefinition/resticrepositories.velero.io: created CustomResourceDefinition/restores.velero.io: attempting to create resource CustomResourceDefinition/restores.velero.io: created CustomResourceDefinition/schedules.velero.io: attempting to create resource CustomResourceDefinition/schedules.velero.io: created CustomResourceDefinition/serverstatusrequests.velero.io: attempting to create resource CustomResourceDefinition/serverstatusrequests.velero.io: created CustomResourceDefinition/volumesnapshotlocations.velero.io: attempting to create resource CustomResourceDefinition/volumesnapshotlocations.velero.io: created Waiting for resources to be ready in cluster... Namespace/velero: attempting to create resource Namespace/velero: created ClusterRoleBinding/velero: attempting to create resource ClusterRoleBinding/velero: created ServiceAccount/velero: attempting to create resource ServiceAccount/velero: created Secret/cloud-credentials: attempting to create resource Secret/cloud-credentials: created BackupStorageLocation/default: attempting to create resource BackupStorageLocation/default: created VolumeSnapshotLocation/default: attempting to create resource VolumeSnapshotLocation/default: created Deployment/velero: attempting to create resource Deployment/velero: created Velero is installed! ⛵ Use 'kubectl logs deployment/velero -n velero' to view the status.
これでVeleroのセットアップは完了です。
バックアップ&リストア1 (PersistentVolumeなし)
最初の検証では、データを保存するPersistentVolumeを使わないステートレスなアプリケーションのバックアップ&リストアを試します。
まず、検証用にnginxをデプロイします。
デプロイに使うManifest(nginx.yaml
)を以下に示します。nginx.yaml
apiVersion: v1 kind: Namespace metadata: name: hoge labels: app: nginx --- apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment namespace: hoge spec: replicas: 2 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - image: nginx:1.7.9 name: nginx ports: - containerPort: 80
Manifest(nginx.yaml
)をデプロイします。
Namespace(hoge
)が作成され、その配下にnginxのPodがデプロイされています。
$ kubectl apply -f nginx.yaml namespace/hoge created deployment.apps/nginx-deployment created $ kubectl get deploy,pod -n hoge NAME READY UP-TO-DATE AVAILABLE AGE deployment.extensions/nginx-deployment 2/2 2 2 92s NAME READY STATUS RESTARTS AGE pod/nginx-deployment-5754944d6c-c6jbr 1/1 Running 0 92s pod/nginx-deployment-5754944d6c-dz5dx 1/1 Running 0 92s
次に、veleroコマンドを使ってバックアップを取得します。
$ velero backup create nginx-backup --include-namespaces hoge
取得したバックアップを確認します。
$ velero backup describe nginx-backup Name: nginx-backup Namespace: velero Labels: velero.io/storage-location=default Annotations: <none> Phase: Completed Namespaces: Included: hoge Excluded: <none> Resources: Included: * Excluded: <none> Cluster-scoped: auto Label selector: <none> Storage Location: default Snapshot PVs: auto TTL: 720h0m0s Hooks: <none> Backup Format Version: 1 Started: 2019-12-14 17:43:23 +0900 JST Completed: 2019-12-14 17:43:24 +0900 JST Expiration: 2020-01-13 17:43:23 +0900 JST Persistent Volumes: <none included>
Google Cloud Storage bucketにバックアップファイルが保存されていることを確認します。
$ gsutil dir -R gs://velero-bucket/ gs://velero-bucket/backups/: gs://velero-bucket/backups/nginx-backup/: gs://velero-bucket/backups/nginx-backup/nginx-backup-logs.gz gs://velero-bucket/backups/nginx-backup/nginx-backup-podvolumebackups.json.gz gs://velero-bucket/backups/nginx-backup/nginx-backup-resource-list.json.gz gs://velero-bucket/backups/nginx-backup/nginx-backup-volumesnapshots.json.gz gs://velero-bucket/backups/nginx-backup/nginx-backup.tar.gz gs://velero-bucket/backups/nginx-backup/velero-backup.json
次に、リストアを検証します。
リストアの前に、障害を想定しNamaspace(hoge
)を削除します。
$ kubectl delete ns hoge namespace "hoge" deleted $ kubectl get pod -n hoge No resources found in hoge namespace.
Namespace(hoge
)および配下のリソースが全て削除されました。
続いて、veleroコマンドを使いリストアします。
$ velero restore create --from-backup nginx-backup
リストアされたNamespace(hoge
)と配下のリソースを確認します。
$ kubectl get deploy,pod -n hoge NAME READY UP-TO-DATE AVAILABLE AGE deployment.extensions/nginx-deployment 2/2 2 2 61s NAME READY STATUS RESTARTS AGE pod/nginx-deployment-5754944d6c-c6jbr 1/1 Running 0 61s pod/nginx-deployment-5754944d6c-dz5dx 1/1 Running 0 61s
無事、Namespace(hoge
)と配下のリソースがリストアされました。
バックアップ&リストア2 (PersistentVolumeあり)
次の検証では、データを保存するPersistentVolumeを使ったステートフルなアプリケーションのバックアップ&リストアを試します。
検証用にログをPersistentVolumeに保存するnginxをデプロイします。
デプロイに使うManifest(nginx-pv.yaml
)を以下に示します。nginx-pv.yaml
apiVersion: v1 kind: Namespace metadata: name: boo labels: app: nginx --- apiVersion: apps/v1 kind: StatefulSet metadata: name: nginx-sts namespace: boo spec: serviceName: nginx-sts replicas: 1 selector: matchLabels: app: nginx template: metadata: labels: app: nginx annotations: pre.hook.backup.velero.io/container: fsfreeze pre.hook.backup.velero.io/command: '["/sbin/fsfreeze", "--freeze", "/var/log/nginx"]' post.hook.backup.velero.io/container: fsfreeze post.hook.backup.velero.io/command: '["/sbin/fsfreeze", "--unfreeze", "/var/log/nginx"]' spec: containers: - name: nginx image: nginx:1.7.9 volumeMounts: - name: nginx-logs mountPath: /var/log/nginx - name: fsfreeze image: ubuntu:bionic securityContext: privileged: true volumeMounts: - name: nginx-logs mountPath: /var/log/nginx command: - "/bin/bash" - "-c" - "sleep infinity" volumeClaimTemplates: - metadata: name: nginx-logs spec: storageClassName: standard accessModes: [ "ReadWriteOnce" ] resources: requests: storage: 1Gi
なお、Veleroでは pre.hook.backup.velero.io/container
, pre.hook.backup.velero.io/command
, post.hook.backup.velero.io/container
, post.hook.backup.velero.io/command
のアノテーションにて指定されたコンテナのコマンドをバックアップの取得前と取得後に呼び出すことが出来ます。
上記では、Sidecar(fsfreeze
)を用意し、Sidecarにてコマンドを実行します。
これにより、アプリケーションの静止化を行うことができます。
静止化とは、アプリケーションのデータの書き込みを一時的に止める処理のことを言います。
バックアップの実行に書き込みがあった場合、バックアップのデータが壊れることがあります。
特にRDBなどでは静止化を行い、ステーブルになったデータをストレージに保存しないと、リストアできない場合がありますので注意してください。
今回は、ファイルシステムの静止化を行うコマンド(fsfreeze
)を利用します。
Manifest(nginx-pv.yaml
)をデプロイします。
Namespace(boo
)が作成され、その配下にnginxのPodとPersistentVolumeがデプロイされています。
$ kubectl apply -f nginx-pv.yaml $ kubectl get sts,pod,pvc,pv -n boo NAME READY AGE statefulset.apps/nginx-sts 1/1 13m NAME READY STATUS RESTARTS AGE pod/nginx-sts-0 2/2 Running 0 13m NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE persistentvolumeclaim/nginx-logs-nginx-sts-0 Bound pvc-5af42324-887f-4758-98da-c1dc6da8539c 1Gi RWO standard 13m NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE persistentvolume/pvc-5af42324-887f-4758-98da-c1dc6da8539c 1Gi RWO Delete Bound boo/nginx-logs-nginx-sts-0 standard 13m
次に、nginxのアクセスログ(/var/log/nginx/access.log
)に足跡を残すために、ワンタイムのPodをデプロイしcurlでniginxにアクセスします。
$ kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-sts-0 2/2 Running 0 15m 10.24.0.9 gke-velero-test-default-pool-9ec9e72c-460l <none> <none> $ kubectl run -ti --image ubuntu:bionic access-test --restart=Never --rm /bin/sh ... # apt-get update ... # apt-get install curl ... # curl 10.24.0.9 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> ... # exit
nginxのアクセスログ(/var/log/nginx/access.log
)に足跡が記録されました。
$ kubectl exec nginx-sts-0 -n boo -- cat /var/log/nginx/access.log ... 10.24.2.9 - - [14/Dec/2019:10:37:04 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.58.0" "-"
続いて、veleroコマンドを使ってバックアップを取得します。
$ velero backup create nginx-backup-with-pv --include-namespaces boo
バックアップを確認します。
$ velero backup describe nginx-backup-with-pv Name: nginx-backup-with-pv Namespace: velero Labels: velero.io/backup=nginx-backup-with-pv velero.io/pv=pvc-5af42324-887f-4758-98da-c1dc6da8539c velero.io/storage-location=default Annotations: <none> Phase: Completed Namespaces: Included: boo Excluded: <none> Resources: Included: * Excluded: <none> Cluster-scoped: auto Label selector: <none> Storage Location: default Snapshot PVs: auto TTL: 720h0m0s Hooks: <none> Backup Format Version: 1 Started: 2019-12-14 19:48:32 +0900 JST Completed: 2019-12-14 19:48:35 +0900 JST Expiration: 2020-01-13 19:48:32 +0900 JST Persistent Volumes: 1 of 1 snapshots completed successfully (specify --details for more information)
Google Cloud Storage bucketにバックアップファイルが保存されていることを確認します。
$ gsutil dir -R gs://velero-bucket/ gs://velero-bucket/backups/: ... gs://velero-bucket/backups/nginx-backup-with-pv/: gs://velero-bucket/backups/nginx-backup-with-pv/nginx-backup-with-pv-logs.gz gs://velero-bucket/backups/nginx-backup-with-pv/nginx-backup-with-pv-podvolumebackups.json.gz gs://velero-bucket/backups/nginx-backup-with-pv/nginx-backup-with-pv-resource-list.json.gz gs://velero-bucket/backups/nginx-backup-with-pv/nginx-backup-with-pv-volumesnapshots.json.gz gs://velero-bucket/backups/nginx-backup-with-pv/nginx-backup-with-pv.tar.gz gs://velero-bucket/backups/nginx-backup-with-pv/velero-backup.json ...
先ほどと同様に、障害を想定しNamespace(boo
)を削除します。
$ kubectl delete ns boo namespace "boo" deleted
Namespace(boo
)と配下のリソースが全て削除されているのを確認します。
PersistentVolumeについても、Reclaim PolicyがDelete
と設定されているため、PersistentVolumeClaimが削除されると共に削除されています。
$ kubectl get sts,pod,pvc,pv -n boo No resources found in boo namespace.
次に、リストアを実行します。
$ velero restore create --from-backup nginx-backup-with-pv
リストアされたリソースを確認します。
$ kubectl get sts,pod,pvc,pv -n boo NAME READY AGE statefulset.apps/nginx-sts 1/1 38s NAME READY STATUS RESTARTS AGE pod/nginx-sts-0 2/2 Running 0 38s NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE persistentvolumeclaim/nginx-logs-nginx-sts-0 Bound pvc-5af42324-887f-4758-98da-c1dc6da8539c 1Gi RWO standard 38s NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE persistentvolume/pvc-5af42324-887f-4758-98da-c1dc6da8539c 1Gi RWO Delete Bound boo/nginx-logs-nginx-sts-0 standard 38s
続いて、nginxのアクセスログ(/var/log/nginx/access.log
)を確認します。
$ kubectl exec nginx-sts-0 -n boo -- cat /var/log/nginx/access.log ... 10.24.2.9 - - [14/Dec/2019:10:37:04 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.58.0" "-"
バックアップ前に、アクセスした足跡がアクセスログ(/var/log/nginx/access.log
)に残っています。
無事、PersistentVolumeのデータもリストアすることが出来ました。
感想
本検証では、Veleroを試しKubernetes上のリソースとPersistent Volumeのバックアップ・リストアを検証しました。Veleroは幾つか制限事項があるものKubernetes上のリソースとPersistent Volumeをコマンド一つで一括にバックアップ・リストアできました。
Veleroの良い点としては、簡単に一括でバックアップ・リストアができる以外にも、バックアップを行う事前と事後にHooksにより、任意のコマンドを実行できる点です。2019年時点では、KubernetesのSIG-Storageでも同様にVolume Snapshotの事前と事後に任意のコマンドが挟めるようHookの開発が進められていますが、リリースはまだまだ先となっています。ストレージ・データベース業界にとっては、アプリケーションの静止化をおこなってバックアップを取得するのが安全にバックアップを取得する定石のひとつとなっています。これら定石を実行できる口をVeleroはいち早くサポートしているのが良い点のひとつではないでしょうか
地震や火事などの災害では、データセンタ単位で壊れます。このような場合においても、迅速にサービスを復旧するために、日頃からバックアップを取得し備えることは、防災対策のひとつです。ITは水や電気などと同じく生活の基盤としてなくてはならないインフラです。防災対策としても、定期的にバックアップを取得し、いざと言う時に備えるのは重要ではないでしょうか。