はじめに
本検証では、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ではいくつかの制限事項があるため、注意してください(hostPathは非サポートだが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
)を以下に示します。
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
)を以下に示します。
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は水や電気などと同じく生活の基盤としてなくてはならないインフラです。防災対策としても、定期的にバックアップを取得し、いざと言う時に備えるのは重要ではないでしょうか。