Veleroを使ったAKSのクラスタバックアップ

## はじめに
本記事ではVeleroを使ったAKSクラスタのバックアップについて紹介します。
[Velero](https://velero.io)はKubernetesクラスタのバックアップを行うためのオープンソースツールです。元々はHeptio Arkという名前でしたが、2018年11月に発表されたVMwareによるHeptio
の買収により名称もVeleroへ変更となりました(*1)。（そのためveleroで検索してもヒット数はまだ多くありません）

*1 - https://www.publickey1.jp/blog/18/vmwarekubernetesheptiokubernetes.html

## Kubernetesの運用管理
kubernetesを導入するメリットとして、コンテナのデプロイ、コンテナ基盤のオーケストレーション、クラスタの運用管理の自動化などがあげられます。従来運用組織が行っていたような環境配備と管理タスクも、yamlフォーマットのマニフェストに記載して自動的に実行させることが可能です。

一方で、kubernetes基盤のクラスタ全体または各テナントに対する運用管理タスクとして以下のような項目が必要となります。

- バックアップ
- 運用監視
- バージョンアップ
- ログ管理
- 基盤のメンテナンス（ノードに対するパッチ適用等）

ここでは、上記のうち特にバックアップに焦点を当てて、kubernetes環境におけるバックアップについて考えてみます。

## kubernetesでのバックアップの必要性
kubernetesクラスタでは、リソースはすべてyamlフォーマットのマニフェストで表現されるため、展開するリソースの元になるマニフェストさえあればバックアップは不要、という考え方もあります。ただし、運用者の操作ミスなどでクラスタごと削除してしまうなどの事故もありえるため、その際にもすみやかに確実に環境を元に戻せるための方策としてバックアップは有用です。(*2)

*2 - https://www.publickey1.jp/blog/19/spotifykubernetes.html

そもそも、手動でkubectlコマンドを実行するCLIベースの運用は避けるべきという考えから、環境を構成するマニフェストファイルはすべてGitで管理し、これを"Single Source of Truth"としてCI/CDパイプラインを使って環境デプロイを行おうという発想として、WeaveWorks社による"GitOps"などが提唱されてもいます。

一方で、最初から完璧なGitOps環境を導入して使いこなしている利用者が全てではありませんし、細かい点で言うとパスワードなどの情報(Secret)をGitに置けないなど課題もあります。

バックアップの必要性はkubernetesの利用形態やSLAに応じて変わるものの、多くの利用者では必要とされる運用管理タスクであると考えられます。

## 従来のkubernetesバックアップの方法
kubernetesではこうするべきといった決まったバックアップ取得方法はありません。必要に応じて必要な箇所のバックアップを取得しようとすると、たとえば以下のような対象が考えられます。

- etcdデータのバックアップ
- リソースのyamlエクスポート
- Volumeのストレージのバックアップ

kubernetesでは状態を管理するのはetcdなので、とりいそぎetcdクラスタのバックアップを取得する方法が考えられますが、一般的にetcdのバックアップ取得とリストアは難易度が高く、かつ、マネージドサービスのk8sの場合、etcd自体にアクセスができないケースがほとんどです。
また、リソースやVolumeのバックアップについてもクラスタ全体で一貫性のある形でバックアップをとる仕組みがないと、正常に復元することはできません。たとえば、PVとPVCの紐づけを保ったままバックアップができなければ、リストア時にPVCからPVが参照できない、といった問題が起きる可能性があります。

## Veleroを使ったkubernetesバックアップ
上記のような課題をもとに、旧Heptioが作ったバックアップツールがVeleroです。Heptio自体がkubernetesを開発したエンジニアが中心になって立ち上げた会社だけあって、前述したようなkubernetes固有の課題を理解した仕組みがVeleroにはあります。

- etcdクラスタをバックアップするのではなく、kubernetes APIへのアクセスのみを用いてリソース情報を抽出して、これをバックアップする
- リソースデータのバックアップは別途設定したオブジェクトストレージに保存する
- Volumeストレージはバックエンドストレージ固有のプラグインによりスナップショットなど適切な方法でバックアップを取得する

これらを機能面も含めてまとめると以下のように整理できます。

### Veleroの機能と特徴
- クラスタ全体、Namespace単位、ラベル指定単位でのバックアップとリストアが可能
- リソースとPVともにバックアップ対象とすることができる
- スケジュール管理されたバックアップ取得およびバックアップデータ保持が可能
- バックアップ前後のhook処理が可能
- S3、GCS、Azure Blob Storage、Minioなどの複数のオブジェクトストレージに対応
- バックエンドのブロックおよびオブジェクトストレージの操作はバックエンドごとに固有のplugin（プロバイダと呼ばれる）で行うアーキテクチャを採用


## Veleroを使ったAKSのバックアップ
以降では、AKSの環境をVeleroでバックアップするためのセットアップ手順およびバックアップ・リストア手順を解説します。

## 環境前提

| product | version |
|---------|---------|
| AKS (japaneast region) | 1.16.6 |
| azure-cli | 2.1.0 |
| kubectl | 1.16.0 |
| velero | 1.2.0 |
| Velero plugins for Microsoft Azure | 1.0.0 |

## 導入手順概要
手順の概要は以下の通りです。

1. AKSのセットアップ
2. バックアップ先となるAzureストレージアカウントとBLOBコンテナの作成
3. veleroがAzureリソース管理を行うためのservice principalの作成
4. velero CLIのインストール
5. veleroのインストールと起動
6. サンプルアプリケーションのデプロイ
7. バックアップスケジュールの作成
8. リストアの確認

## 導入手順

#### 1. AKSのセットアップ
AKSクラスタをセットアップする手順の例を以下にまとめます。
リソースグループ名やクラスタ名、サブネットのCIDRなど、必要に応じて変更してください。
また、リージョンは東日本を指定していますが、AKSはリージョンごとに利用できるk8sのバージョンが異なる場合がありますので、ご注意ください。(az aks get-versions --location japaneastなどとすると確認できます）

```shell
## ログイン
$ az login

## subscriptionとテナントが自分のものであることを確認
$ AZURE_SUBSCRIPTION_ID=`az account list --query '[?isDefault].id' -o tsv`
$ AZURE_TENANT_ID=`az account list --query '[?isDefault].tenantId' -o tsv`
$ echo $AZURE_SUBSCRIPTION_ID
$ echo $AZURE_TENANT_ID

## リソースグループの作成
$ AZURE_AKS_RESOURCE_GROUP=akstest01rg
$ az group create --name $AZURE_AKS_RESOURCE_GROUP --location japaneast

## 仮想ネットワークとサブネットの作成
$ az network vnet create \
--name aksVNet \
--resource-group $AZURE_AKS_RESOURCE_GROUP \
--address-prefixes 10.0.0.0/8 \
--subnet-name aksSubNet \
--subnet-prefixes 10.1.0.0/16 \
--location japaneast

## AKS を所属させるサブネットリソースID の取得
$ VNET_SUBNET_ID=$(az network vnet subnet list \
--resource-group $AZURE_AKS_RESOURCE_GROUP \
--vnet-name aksVNet \
--query [].id --output tsv)

## AKSクラスタの作成
$ AKS_NAME=aks01
$ az aks create \
--resource-group $AZURE_AKS_RESOURCE_GROUP \
--name $AKS_NAME \
--network-plugin azure \
--vnet-subnet-id ${VNET_SUBNET_ID} \
--docker-bridge-address 172.17.0.1/16 \
--dns-service-ip 10.0.0.10 \
--service-cidr 10.0.0.0/16 \
--node-count 3 \
--kubernetes-version 1.16.4 \
--generate-ssh-keys

## クレデンシャル情報の登録
$ az aks get-credentials --resource-group $AZURE_AKS_RESOURCE_GROUP --name $AKS_NAME
Merged "aks01" as current context in /home/USER/.kube/config

## ノード確認
$ kubectl get node
NAME                                STATUS   ROLES   AGE   VERSION
aks-nodepool1-18086731-vmss000000   Ready    agent   21h   v1.16.4
aks-nodepool1-18086731-vmss000001   Ready    agent   21h   v1.16.4
aks-nodepool1-18086731-vmss000002   Ready    agent   21h   v1.16.4
```

#### 2. バックアップ先となるAzureストレージアカウントとBLOBコンテナの作成
Veleroではバックアップデータを指定したオブジェクトストレージ上に保存します。AzureではBLOBコンテナが対応しているため、事前にストレージアカウントとコンテナをCLIコマンドで作成しておきます。
ストレージアカウント名はグローバルで一意である必要がありますが、下記以外の一意な命名をしても問題ありません。
ここでは暗号化(encryption at rest)を行うとともにDRも想定してStandard_GRS（地理的冗長ストレージ）を構成しています。skuや暗号化、access-tierなどは要件に応じて設定してください。

```shell
$ AZURE_BACKUP_RESOURCE_GROUP=Velero_Backups
$ az group create -n $AZURE_BACKUP_RESOURCE_GROUP --location japaneast

$ AZURE_STORAGE_ACCOUNT_ID="velero$RANDOM"
$ az storage account create \
    --name $AZURE_STORAGE_ACCOUNT_ID \
    --resource-group $AZURE_BACKUP_RESOURCE_GROUP \
    --sku Standard_GRS \
    --encryption-services blob \
    --https-only true \
    --kind BlobStorage \
    --access-tier Hot

$ BLOB_CONTAINER=velero
$ az storage container create -n $BLOB_CONTAINER --public-access off --account-name $AZURE_STORAGE_ACCOUNT_ID
```

#### 3. veleroがAzureリソース管理を行うためのservice principalの作成
veleroではユーザに代わりAzureリソースの作成・管理を行う必要があります。このため、リソース管理を移譲する機能であるservice principalを作成しておきます。
なお、作成したservice principalは、Azure Portal上からはAzure Active Directoryの「アプリの登録」メニューから確認することが可能です。下記コマンド実行後に"velero"という名前のservice principalが作成されていることを確認しておいても構いません。

![aad.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51832/35968cdb-30a6-a6cd-408a-990235673dbf.png)


AZURE_RESOURCE_GROUPという変数にはAKSクラスタのリソースが管理されているリソースグループ名を設定します。このリソースグループ名は1.のAKSクラスタ作成時に命名規則により決められます。
最後にveleroのインストールに必要なクレデンシャルファイルを作成しておきます。このファイルの全ての行で"key=value"のように値がセットされていることをcatコマンドなどで確認してください。もし"key="のように値が欠けているようであれば環境変数の設定で失敗していますので、手順をスキップした箇所がないかを再確認してください。

```shell
$ AZURE_CLIENT_SECRET=`az ad sp create-for-rbac --name "velero" --role "Contributor" --query 'password' -o tsv --scopes  /subscriptions/$AZURE_SUBSCRIPTION_ID`
$ AZURE_CLIENT_ID=`az ad sp list --display-name "velero" --query '[0].appId' -o tsv`
$ echo $AZURE_CLIENT_ID
$ echo $AZURE_CLIENT_SECRET

## AZURE_RESOURCE_GROUPはAKSMC_XXXのやつ。以下で確認する。
$ az group list --query '[].{ ResourceGroup: name, Location:location }'
$ AZURE_RESOURCE_GROUP=MC_${AZURE_AKS_RESOURCE_GROUP}_${AKS_NAME}_japaneast
$ echo $AZURE_RESOURCE_GROUP

$ cat << EOF  > ./credentials-velero
AZURE_SUBSCRIPTION_ID=${AZURE_SUBSCRIPTION_ID}
AZURE_TENANT_ID=${AZURE_TENANT_ID}
AZURE_CLIENT_ID=${AZURE_CLIENT_ID}
AZURE_CLIENT_SECRET=${AZURE_CLIENT_SECRET}
AZURE_RESOURCE_GROUP=${AZURE_RESOURCE_GROUP}
AZURE_CLOUD_NAME=AzurePublicCloud
EOF
$ echo ./credential-velero
```

#### 4. velero CLIのインストール
velero CLIはgithubサイトからダウンロードして使います。本稿ではバージョン1.2.0のLinux用CLIをダウンロードしています。
最後にvelero versionコマンドを実行してPATHが正しいかを確認します。clientバージョンは表示されますが、serverバージョンは表示されない点に注意してください。まだこの時点ではveleroのインストール前であるため、veleroサーバ環境にはアクセスできないためこれで問題ありません。

```shell
$ wget https://github.com/vmware-tanzu/velero/releases/download/v1.2.0/velero-v1.2.0-linux-amd64.tar.gz
$ tar zxvf velero-v1.2.0-linux-amd64.tar.gz
$ export PATH=$PATH:~/velero-v1.2.0-linux-amd64
$ velero version
Client:
        Version: v1.2.0
        Git commit: 5d008491bbf681658d3e372da1a9d3a21ca4c03c
<error getting server version: the server could not find the requested resource (post serverstatusrequests.velero.io)>
```

#### 5. veleroのインストールと起動
いよいよveleroをインストールします。オプション項目が多いですが、provider、pluginsはazure固有の設定、Azure環境へのアクセス情報は手順3.の最後で作成したファイルを指定します。bucketはバックアップ保管先のBLOBコンテナ名、backup-location-configはバックアップ保管先の細かい設定を記述できますが、ここではリソースグループ名とストレージアカウントIDのみを指定しています。同様にVolumeスナップショットに関する設定はsnapshot-location-configで記載します。ここでは例としてapiTimeoutパラメータのみを指定しています。それぞれで指定できるパラメータの詳細については各ドキュメントを参照してください。( *3)( *4)

*3 - https://github.com/vmware-tanzu/velero-plugin-for-microsoft-azure/blob/master/backupstoragelocation.md

*4 - https://github.com/vmware-tanzu/velero-plugin-for-microsoft-azure/blob/master/volumesnapshotlocation.md

```shell
$ echo $BLOB_CONTAINER
$ echo $AZURE_BACKUP_RESOURCE_GROUP
$ echo $AZURE_STORAGE_ACCOUNT_ID

### veleroインストール(dryrunとしてyamlファイルの出力のみ)
$ velero install \
    --provider azure \
    --plugins velero/velero-plugin-for-microsoft-azure:v1.0.0 \
    --secret-file ./credentials-velero \
    --bucket $BLOB_CONTAINER \
    --backup-location-config resourceGroup=$AZURE_BACKUP_RESOURCE_GROUP,storageAccount=$AZURE_STORAGE_ACCOUNT_ID \
    --snapshot-location-config apiTimeout=5m \
    --dry-run -o yaml > dryrun.yaml

### veleroインストール(dryrunで問題がなければ実際にインストールを実行)
$ velero install \
    --provider azure \
    --plugins velero/velero-plugin-for-microsoft-azure:v1.0.0 \
    --secret-file ./credentials-velero \
    --bucket $BLOB_CONTAINER \
    --backup-location-config resourceGroup=$AZURE_BACKUP_RESOURCE_GROUP,storageAccount=$AZURE_STORAGE_ACCOUNT_ID \
    --snapshot-location-config apiTimeout=5m 
```

インストールを実行すると、veleroというnamespace（設定で変更も可能）上にPod(リソースとしてはDeployment)がデプロイされます。このPodのログを確認して致命的なエラーが出ていないことをチェックしておきます。

```shell
### veleroデプロイ状況の確認
$ kubectl get all -n velero
$ kubectl logs pod/velero-987df5cbc-kf5pv -n velero

...
time="2020-02-24T06:33:21Z" level=info msg="Checking for expired DeleteBackupRequests" controller=backup-deletion logSource="pkg/controller/backup_deletion_controller.go:456"
time="2020-02-24T06:33:21Z" level=info msg="Done checking for expired DeleteBackupRequests" controller=backup-deletion logSource="pkg/controller/backup_deletion_controller.go:484"
time="2020-02-24T06:33:21Z" level=info msg="Caches are synced" controller=restore logSource="pkg/controller/generic_controller.go:83"
time="2020-02-24T06:33:21Z" level=info msg="Caches are synced" controller=backup-sync logSource="pkg/controller/generic_controller.go:83"
time="2020-02-24T06:33:21Z" level=info msg="Caches are synced" controller=downloadrequest logSource="pkg/controller/generic_controller.go:83"
time="2020-02-24T06:33:21Z" level=info msg="Caches are synced" controller=restic-repository logSource="pkg/controller/generic_controller.go:83"
time="2020-02-24T06:33:21Z" level=info msg="Caches are synced" controller=schedule logSource="pkg/controller/generic_controller.go:83"
time="2020-02-24T06:33:21Z" level=info msg="Caches are synced" controller=serverstatusrequest logSource="pkg/controller/generic_controller.go:83"
time="2020-02-24T06:33:21Z" level=info msg="Caches are synced" controller=backup logSource="pkg/controller/generic_controller.go:83"
```

#### 6. サンプルアプリケーションのデプロイ
AKSのサンプルアプリであるVotingアプリをここでは一部改変してデプロイします(*5)。

*5 - https://docs.microsoft.com/ja-jp/azure/aks/servicemesh-istio-scenario-routing?pivots=client-operating-system-linux

元のサンプルアプリではIstioを使い、EnvoyとIngressGateway経由でアクセスを制御するのですが、シンプルな構成にするためにIstioは使用せず、アプリへのアクセスには"type:LoadBalancer"タイプのServiceリソースを追加でデプロイしています。

```shell
$ git clone https://github.com/Azure-Samples/aks-voting-app.git
$ cd aks-voting-app/scenarios/intelligent-routing-with-istio/kubernetes

$ kubectl create namespace voting
$ for i in voting-analytics-deployment-2.0.yaml voting-analytics-service.yaml voting-app-deployment-2.0.yaml voting-app-service.yaml voting-storage-deployment-2.0.yaml voting-storage-secret.yaml voting-storage-service-add-mysql.yaml
> do
> kubectl apply -f $i -n voting
> done

$ cat <<EOF >~/voting-app-svc.yaml
# voting-app-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: voting-app
  labels:
    app: voting-app
spec:
  type: LoadBalancer
  ports:
  - port: 8080
    name: http
  selector:
    app: voting-app
EOF

$ kubectl apply -f ~/voting-app-svc.yaml -n voting

$ kubectl get svc -n voting
NAME               TYPE           CLUSTER-IP     EXTERNAL-IP    PORT(S)             AGE
voting-analytics   ClusterIP      10.0.106.6     <none>         8080/TCP            13m
voting-app         LoadBalancer   10.0.90.107    20.43.92.146   8080:31094/TCP      13m
voting-storage     ClusterIP      10.0.243.201   <none>         6379/TCP,3306/TCP   13m
```

type:LoadBalancerのServiceリソースを配備すると、しばらくして外部からアクセス可能なIPアドレスが"EXTERNAL-IP"列に表示されますので、ブラウザでこのIPアドレス（Portは8080）にアクセスすると、サンプルアプリケーションの画面が表示されます。

これはCatsかDogsかに投票(vote)するというあプケーションです。バックエンドではMySQLが動作しており、永続データはPV(Persistent Volume)に格納しています。

後ほど、バックアップとリストアの動作を試す際に、このアプリ上で何回か投票をした状態からリストアを実行してみる予定です。

![votingapp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51832/b90e6d93-9e58-883d-0a33-3a2ed21ba1c1.png)


#### 7. バックアップスケジュールの作成
veleroではバックアップ取得にいくつかのオプションを指定できます。
クラスタ全体を対象とすることもできますが、テナントレベルで使う場合には範囲を絞るこれらのオプションを利用すると便利です。

- バックアップ対象を指定するオプション
  - 特定のnamespaceを含める、または、除外する
  - 特定のリソースタイプを含める、または、除外する
  - 特定のラベルをセレクタで指定する

- バックアップの挙動を指定するオプション
  - バックアップ取得スケジュール(cronフォーマットで指定)
  - バックアップデータの保持期間(TTL)（デフォルト:720時間）
  - PVのスナップショットを取得するか(true/false)

以下の例では、サンプルアプリケーションが動作するvoting namespaceを対象に、PVスナップショットも取得するようにして、5分間隔で自動バックアップを設定しています。

```shell
$ velero schedule create voting-backup --include-namespaces=voting --snapshot-volumes=true --volume-snapshot-locations default --schedule="*/5 * * * *"
$ velero schedule get
NAME            STATUS    CREATED                         SCHEDULE      BACKUP TTL   LAST BACKUP   SELECTOR
voting-backup   Enabled   2020-02-29 12:08:34 +0000 UTC   */5 * * * *   720h0m0s     6s ago        <none>
```

しばらくすると自動で取得されたバックアップを確認することができます。

```shell
$ velero backup get
NAME                           STATUS      CREATED                         EXPIRES   STORAGE LOCATION   SELECTOR
voting-backup-20200229121201   Completed   2020-02-29 12:12:01 +0000 UTC   29d       default            <none>
```


#### 8. リストアの確認
実際に正しくリストアできるかどうかを確認してみます。
サンプルアプリケーションにアクセスして、DogsやCatsに何回か投票をしてみます。

![votingapp2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51832/6e6f6152-dd49-8740-f54f-3acce86160f5.png)

いまバックエンドのMySQLにもデータが投入されています。この状態からリストアを実行してみましょう。
一度voting namespaceごと全部削除し、その後で最初に取得されたバックアップデータを指定してリストアを実行します。

```shell
$ kubectl delete ns/voting
namespace "voting" deleted

$ velero restore create --from-backup voting-backup-20200229121201
velero restore create --from-backup voting-backup-20200229121201
Restore request "voting-backup-20200229121201-20200229121613" submitted successfully.
Run `velero restore describe voting-backup-20200229121201-20200229121613` or `velero restore logs voting-backup-20200229121201-20200229121613` for more details.

$ velero restore get
NAME                                          BACKUP                         STATUS       WARNINGS   ERRORS   CREATED                         SELECTOR
voting-backup-20200229121201-20200229121613   voting-backup-20200229121201   InProgress   0          0        2020-02-29 12:16:14 +0000 UTC   <none>
$ velero restore get
NAME                                          BACKUP                         STATUS      WARNINGS   ERRORS   CREATED                         SELECTOR
voting-backup-20200229121201-20200229121613   voting-backup-20200229121201   Completed   0          0        2020-02-29 12:16:14 +0000 UTC   <none>
```

ボリュームも小さいため、比較的リストアはすぐ完了します。STATUS列がCompletedになれば完了です。
念のため、ログも確認しておきます。

```shell
$ velero restore logs voting-backup-20200229121201-20200229121613
...
time="2020-02-29T12:16:26Z" level=info msg="Attempting to restore Service: voting-app" logSource="pkg/restore/restore.go:1016" restore=velero/voting-backup-20200229121201-20200229121613
time="2020-02-29T12:16:26Z" level=info msg="Executing item action for services" logSource="pkg/restore/restore.go:910" restore=velero/voting-backup-20200229121201-20200229121613
time="2020-02-29T12:16:26Z" level=info msg="Attempting to restore Service: voting-storage" logSource="pkg/restore/restore.go:1016" restore=velero/voting-backup-20200229121201-20200229121613
time="2020-02-29T12:16:26Z" level=info msg="restore completed" logSource="pkg/controller/restore_controller.go:473" restore=velero/voting-backup-20200229121201-20200229121613
```

"restore completed"となっておりエラーも出ていません。
アプリケーションも無事復旧したことを確認しましょう。

```shell
$ kubectl get all -n voting
NAME                                        READY   STATUS    RESTARTS   AGE
pod/voting-analytics-2-0-69864fd7bb-57sqg   1/1     Running   0          102s
pod/voting-app-2-0-777d6d9b98-b8gxj         1/1     Running   0          102s
pod/voting-app-2-0-777d6d9b98-jjd89         1/1     Running   0          102s
pod/voting-app-2-0-777d6d9b98-zj987         1/1     Running   0          102s
pod/voting-storage-2-0-b74b79c68-x8v4z      1/1     Running   0          102s

NAME                       TYPE           CLUSTER-IP     EXTERNAL-IP    PORT(S)             AGE
service/voting-analytics   ClusterIP      10.0.183.101   <none>         8080/TCP            102s
service/voting-app         LoadBalancer   10.0.219.228   20.43.93.216   8080:32667/TCP      102s
service/voting-storage     ClusterIP      10.0.138.128   <none>         6379/TCP,3306/TCP   102s

NAME                                   READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/voting-analytics-2-0   1/1     1            1           102s
deployment.apps/voting-app-2-0         3/3     3            3           102s
deployment.apps/voting-storage-2-0     1/1     1            1           102s

NAME                                              DESIRED   CURRENT   READY   AGE
replicaset.apps/voting-analytics-2-0-69864fd7bb   1         1         1       102s
replicaset.apps/voting-app-2-0-777d6d9b98         3         3         3       102s
replicaset.apps/voting-storage-2-0-b74b79c68      1         1         1       102s
```

問題ないようです。ブラウザでアプリケーションにアクセスすると、投票する前に状態にリストアされていることも確認できます。

最後に、veleroのスケジュールバックアップを止めておかないとずっと取得を続けてしまうため、スケジュールの削除操作を行います。

```shell
$ velero schedule get
NAME            STATUS    CREATED                         SCHEDULE      BACKUP TTL   LAST BACKUP   SELECTOR
voting-backup   Enabled   2020-02-29 12:12:01 +0000 UTC   */5 * * * *   720h0m0s     3m ago        <none>

$ velero schedule delete voting-backup
Are you sure you want to continue (Y/N)? y
Schedule deleted: voting-backup
```


## veleroの制限事項について
現バージョンのveleroではいくつかの制限事項もあります。

- 各バックアップにおいて、BackupStorageLocation（リソース保存先）とVolumeSnapshotLocation(PVのスナップショット保存先）をそれぞれ１つずつのみ指定できます
- PV(Persistent Volume)のスナップショットはPVと同じリージョンにする必要があります
- クラウドプロバイダをまたいでスナップショットを取得することはできません

## その他
Veleroはリソースのバックアップを取得する際にバックエンドでResticというオープンソースツールを利用しています。ResticはGo言語で書かれたバックエンドクライアントツールですが、Restic自体の制限もあるため、詳細はドキュメントも参照してください。(*6)

*6 - https://velero.io/docs/master/restic/


## 各種リソース（リポジトリ、ドキュメント）
Veleroの公式ドキュメント
https://velero.io/docs/v1.2.0/

Velero plugins for Microsoft Azureのドキュメント
https://github.com/vmware-tanzu/velero-plugin-for-microsoft-azure/blob/master/README.md

qiitaで@ysakashitaさんによるGKEのVeleroバックアップの記事（大変参考になります）
https://qiita.com/ysakashita/items/3a0b2ad9ac37e2ce315a


## まとめ
ここではVeleroを使ったAKSクラスタのバックアップについて紹介しました。IaC(Infrastructure as Code)が当たり前になり、コードだけで環境を簡単に作れるようになる一方で、誤操作でクラスタごとロストする障害も常に起きるリスクがあるため、はじめからスケジュールバックアップを取得しておけば安心して環境を管理できます。

また、kubernetesはバージョンアップのスピードが早く、環境を動かしたままのアップグレード(in-place upgrade)には困難も伴います。veleroによるバックアップを使えば、現環境の隣に新環境を構築し、veleroでリストアするという手段も現実的と言えます。
これらの点からもveleroは今後より注目されるのではないかと考えています。