Kubernetesクラスタの移行にVeleroを使った話
この記事はKubernetes Advent Calendar 2020 その2の20日目の記事です。
背景
半年ほど前の話ですが、私の所属している企業ではEC2インスタンスの上にkopsを使って建てたKubernetesクラスタを利用していました。しかし、EKSなどのマネージドなKubernetesクラスタと比較するとクラスタの管理やバージョンアップが容易ではなく、クラスタ自身も2年以上稼働し続けているものだったため、EKSへの移行を計画していました。このへんの話はよくある話だと思います。
幸運にもKubernetesクラスタ自体を新しく建てること自体はそこまで難しくありませんが、クラスタ上のアプリケーション等を古いクラスタから新しいクラスタに載せ替える作業にはかなり手間がかかります。
yamlで宣言的に書かれているんだから、HelmなりKustomizeなりでぱっと移行できそうな気もしますが、新しくOperatorなどを入れるのと違って、数年間運用されているアプリーケーションが動いているという状態は。Kubernetesクラスタ自体がステートを持っているのに等しい状態なので、Helm等でinstallしても何かしらの依存関係でPodが立ち上がらなかったりします。
DevのクラスタはHelmで移行してみましたが、前述の問題もあってかなり手間が発生したり、Dev環境が正常に動かない時間が発生していました。Prodのクラスタの移行は深夜に行いましたが、長時間クラスタが動かない状態は避けなければならなかったため、クラスタのリソースの移行にVeleroというツールを用いました。
What`s Velero
VeleroはVMwareからOSSとして公開されているKubernetesクラスタのバックアップ・リストア・マイグレーション等を行うためのツールです。ここで取られるバックアップに含まれるのはDeploymentやConfigMapなどのリソースで、kube-api-serverの起動に利用された引数などは含まれません。リソースの状態をまるっと保存しているとイメージするとわかりやすいかもしれません。
Veleroを選択した理由
Veleroを選択した主な理由は以下です。
- 他企業でクラスタ移行に使われていた実績があった
- apiVersionの更新に対応している
- シンプルな設計
この中でもapiVersionの更新に対応しているというのが非常にありがたかったです。
旧クラスタのDeploymentはほとんどのものがextensions/v1beta1
を使っていましたが、あたらしいクラスタではそのApiVersionは使えないため、Helm等でApplyするとエラーになってしまいます。
Veleroではそのあたりのバージョンの差分をリストアする段階で吸収してくれて適切なApiVersion(Deploymentならapps/v1
)に変更してくれました。
Veleroの主な使い方
次に簡単にVeleroの使い方を紹介します。
CLIのインストール
$ brew install velero
or
$ tar -xvf <RELEASE-TARBALL-NAME>.tar.gz
check velero version
$ velero version
Veleroのクラスタへのインストール
Veleroにはpluginの機構があり、EKSやGKEなどマネージドKubernetesに対して、簡単にS3などのバックアップを設定することができます。
ここではバケット名やリージョン、
$ CLUSTER_NAME=sample-cluster
$ VELERO_BUCKET=eks-stdm-dev
$ AWS_REGION=ap-northeast-1
$ kubectl config use-context sample-cluster
$ velero install \
--provider aws \
--plugins velero/velero-plugin-for-aws:v1.0.1 \
--bucket $VELERO_BUCKET \
--backup-location-config region=$AWS_REGION \
--snapshot-location-config region=$AWS_REGION \
--secret-file ~/.aws/credentials
---------------------------------
$ kubectl get all -n velero
NAME READY STATUS RESTARTS AGE
pod/velero-649db948f4-qcj5q 0/1 PodInitializing 0 12s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/velero 0/1 1 0 13s
NAME DESIRED CURRENT READY AGE
replicaset.apps/velero-649db948f4 1 1 0 13s
バックアップ&リストをやってみる
実際にバックアップを取った後、リストアをしてリソースがもとに戻ることを確認して見ます。
用意
でテストアプリと、type LBを作成. nginxをhelmでインストールしてみる
$ helm repo add nginx-stable https://helm.nginx.com/stable
$ helm repo add nginx-stable https://helm.nginx.com/stable
$ helm repo update
$ helm install -n staging my-release nginx-stable/nginx-ingress
$ helm list -n staging
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
my-release staging 1 2020-09-29 19:00:15.409846 +0900 JST deployed nginx-ingress-0.6.1 1.8.1
$ > kubectl get all -n staging
NAME READY STATUS RESTARTS AGE
pod/my-release-nginx-ingress-54fc64c4c5-zctr8 1/1 Running 0 51s
pod/wordpress-578744754c-kzlv5 1/1 Running 0 79m
pod/wordpress-mysql-5b697dbbfc-9chw6 1/1 Running 0 79m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/my-release-nginx-ingress LoadBalancer 10.100.191.241 a9a607b886c61463c9ab34a2b1cf294f-1084391947.ap-northeast-1.elb.amazonaws.com 80:32222/TCP,443:30894/TCP 51s
service/wordpress LoadBalancer 10.100.163.187 ae3784ca27723485c83b86836f87090e-809501219.ap-northeast-1.elb.amazonaws.com 80:32688/TCP 79m
service/wordpress-mysql ClusterIP None <none> 3306/TCP 79m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/my-release-nginx-ingress 1/1 1 1 51s
deployment.apps/wordpress 1/1 1 1 79m
deployment.apps/wordpress-mysql 1/1 1 1 79m
NAME DESIRED CURRENT READY AGE
replicaset.apps/my-release-nginx-ingress-54fc64c4c5 1 1 1 51s
replicaset.apps/wordpress-578744754c 1 1 1 79m
replicaset.apps/wordpress-mysql-5b697dbbfc 1 1 1 79m
ここまででデプロイ終わり
バックアップの作成
VeleroのバックアップはCLIから行います。オンデマンドで好きなタイミングでバックアップを取る方法と、時間を指定して定期的にバックアップをとる方法があります。今回は単発でバックアップをとります。
$ velero backup create staging-backup --include-namespaces staging
$ velero backup get
staging-backup Completed 0 0 2020-xx-xx 19:02:00 +0900 JST 29d default <none>
リソースの削除
ここで先ほどDeployしたリソースをnamespaceごと削除します。
$ kubectl delete namespace staging
$ kubectl get all -n staging
No resources found in staging namespace.
リストア
バックアップと同様にリストアもCLIから行います。--from-backup
オプションからリストアする対象のバックアップを指定します
$ velero restore create --from-backup staging-backup
$ kubectl get all -n staging
NAME READY STATUS RESTARTS AGE
pod/my-release-nginx-ingress-54fc64c4c5-zctr8 1/1 Running 0 8s
...
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/my-release-nginx-ingress LoadBalancer 10.100.245.48 ae075cd90574b4c0f90fc8811b49956c-957111671.ap-northeast-1.elb.amazonaws.com 80:30882/TCP,443:30129/TCP 8s
...
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/my-release-nginx-ingress 1/1 1 1 8s
...
NAME DESIRED CURRENT READY AGE
replicaset.apps/my-release-nginx-ingress-54fc64c4c5 1 1 1 8s
...
$ helm3 list -n staging
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
my-release staging 1 2020-09-29 19:00:15.409846 +0900 JST deployed nginx-ingress-0.6.1 1.8.1
Veleroを利用することでこのようにクラスタのリソースのバックアップ・リストアが簡単に行えます。
ここでは紹介だけにしますが、ほかにもDeploymentだけのリストア・バックアップができたり、リソース名でフィルタリングをかけられたりといった機能があります。詳細はドキュメントを参照してください。
また先ほどちらっと書きましたが、plugin形式でバックアップ先やバックアップ・リストア時の挙動を拡張することも可能です
クラスタ間のマイグレーション
クラスタ間でのバックアップ・リストアも基本的な流れは同じです。
前の手順と同じようにバックアップを行う旧クラスタでVeleroをインストールしておきます。
クラスタ間のリストア時の認証はあるSecretの値を共有することで認証します。
Secretは旧クラスタ側のvelero namespaceにあるcloud-credentials
というSecretの値を使うので、ファイルに保存しておきます。
- secretの取得
$ kubectl get secrets/cloud-credentials -n velero --template={{.data.cloud}} | base64 -D >> secret.txt
そしてそのSecretを使って、新クラスタ側のインストールを行います。
$ velero install \
--secret-file=secret.txt \
--provider aws \
--backup-location-config region=AWS_REGION \
--snapshot-location-config region=AWS_REGION \
--plugins=velero/velero-plugin-for-aws:v1.0.1 \
--bucket VELERO_BUCKET
両方のクラスタでインストールが完了したら、旧クラスタの方でバックアップを作成します。ここではstaging
namespaceのみのバックアップを作成しています。
$ velero backup create cluster-migration-staging --include-namespaces staging
あとは普通のリストアと同様にバックアップする対象を選択することでクラスタ間のマイグレーションが行えます・
- バックアップのリストア
$ velero restore create --from-backup cluster-migration-staging
$ kubectl get all -n staging
NAME READY STATUS RESTARTS AGE
pod/wordpress-578744754c-gj7h5 0/1 ContainerCreating 0 31s
...
Veleroを使った移行で困った点
最後にVeleroを使ってマイグレーションした際に困った点をいくつか紹介して終わります・
Jobの扱い
私が使ったVeleroのバージョン(v1.5.1)ではJobをリストアした際に、JobはCompleteならJobリソース自体存在しない状態になるのですが、PodがCompleteではない場合、Jobが作り出したPodもPodリソースとしてリストアされてしまいます。
その状態で、PodリソースとJobリソースをリストアするとリストア先のクラスタでそのJobに紐づくPodが2つ(旧クラスタのJobが作ったPod + 新クラスタのJobが作ったPod)作られてしまいました。
Helmのアノテーション問題
このクラスタ移行時に同時にHelm2系からHelm3系のアップデートをしていました。Helm2系とHelm3系ではHelmで管理されているリソースについているアノテーションが違ったために、Helmが正しくリソースを認識できない問題が発生していました。
Helmが認識するようにアノテーションをOverwriteするスクリプトを用意することで正しくHelmが認識するように対処しました。
Secret情報の扱い
通常だと、クラスタのバックアップを取った時にSecretの中身も見える状態なので注意が必要です。
バックアップ先のS3などのACLで権限を絞るか、リストアの対象からSecretを外す必要があります。
最後に
Veleroかなりおすすめです
Discussion