Kubernetesディザスタリカバリのアプローチ:GitOps vs ストレージスナップショット vs バックアップオペレータ
ステートレスアプリケーションの復旧は容易ですが、ステートフルワークロードはそうではありません。不意のkubectl delete namespaceの実行、移行作業の失敗、あるいはetcdの破損が発生した場合、単にマニフェストを再適用するだけではボリュームが空になり、依存関係が壊れた状態に陥ってしまいます。
本番環境を運用するチームは通常、以下の3つの主要な復旧パターンを利用します。
- GitOps & Infrastructure as Code (IaC): ArgoCDやFluxなどのツールは、Gitからマニフェストを直接同期します。ステートレスなDeployment、Service、CRDを数秒で再構築できます。しかし、GitOpsは実際のランタイム状態を認識できません。PostgreSQLのテーブルやKafkaのトピックを復旧することは不可能です。
- クラウドストレージのボリュームスナップショット: 定期的なブロックスナップショット(AWS EBS、GCP PD、Ceph RBDなど)を取得することで、生バイトデータを保護できます。しかし、これらのスナップショットはKubernetesの外部で動作します。書き込み処理を一時停止したり、ディスクをアクティブなPod定義と紐付け直したりしなければ、ネームスペースをまたいだリストアはエラーが発生しやすいスクリプト作業の負担となります。
- Kubernetesネイティブなバックアップオペレータ(Velero): Veleroはクラスタ内で直接動作し、APIサーバに問い合わせてS3互換ストレージへのバックアップをオーケストレーションします。Kubernetes APIリソースとPersistentVolumeデータ(CSIボリュームスナップショットまたはKopiaなどのファイルレベルのnode-agent経由)の両面をまとめて保護します。
バックアップアプローチの比較:メリットとデメリット
適切なリカバリアーキテクチャの選定は、目標復旧時点(RPO)と運用の複雑さのバランスによって決まります。
GitOpsリポジトリ
- メリット: バージョン管理による監査ログ、プルリクエストによる承認フロー、自動ドリフト検出・修正。
- デメリット: 状態(ステート)の保護が一切できない点。オペレータによって動的に生成されるSecretや実際のディスクコンテンツのバックアップには別のツールが必要です。
ストレージプロバイダのネイティブスナップショット
- メリット: 高速なブロックレベルのレプリケーション(500GB以上のボリュームでも1分未満のRPOを実現可能)、ワーカーノードのCPU負荷がゼロ。
- デメリット: クラウドベンダーのロックイン、分散ノード間での調整の難しさ、クラスタ間移行の複雑さ。
VeleroとS3オブジェクトストレージ
- メリット: ベアメタルとパブリッククラウド間での完全なクラスタポータビリティ、ラベルやネームスペースによるきめ細かなフィルタリング、組み込みのKopia node-agentサポート。
- デメリット: ファイルシステムレベルのバックアップ(Kopia)実行時にノードのCPU/メモリを消費する点、転送速度がオブジェクトストレージへのネットワーク帯域に直接依存する点。
推奨アーキテクチャ:VeleroとMinIOの組み合わせ
ハイブリッドクラウド、ベアメタルクラスタ、またはインターネットから隔離されたエアギャップ環境では、Veleroと専用のMinIOクラスタを組み合わせることで、速度と自律性を兼ね備えた理想的な構成が実現できます。
VeleroはAPIへの問い合わせやバックアップフックを制御します。一方MinIOは、ローカルNVMeドライブ間で毎秒数ギガバイトのスループットを実現する軽量なS3互換オブジェクトストレージを提供し、クラウドアウトバウンド通信料(Egress費用)も一切かかりません。
Veleroはクラスタ内でコントローラのDeploymentとして動作します。バックアップ実行時には、対象のAPIマニフェストを圧縮されたtarballにパッケージ化し、MinIOにプッシュします。PersistentVolumeに対しては、Veleroのnode-agent DaemonSetがKopiaを使用して同じバケットにファイルデータをストリーミング転送します。深夜に予期せぬコントロールプレーンの障害やリリースの破損が発生しても、リストアコマンドを1つ実行するだけでアプリケーションスタック全体を数分で復旧できます。
ステップバイステップ導入ガイド
以下のチュートリアルでは、MinIOのセットアップ、ファイルレベルバックアップを有効にしたVeleroのデプロイ、そして一連のバックアップと復旧サイクルの検証を行います。
1. MinIOオブジェクトストレージのデプロイ
外部のMinIO環境がまだない場合は、テスト用に以下のスタンドアロンインスタンスを起動します。
# MinIO用ネームスペースを作成
kubectl create namespace minio
# 永続ストレージを備えたスタンドアロンMinIOをデプロイ
kubectl apply -n minio -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: minio
spec:
replicas: 1
selector:
matchLabels:
app: minio
template:
metadata:
labels:
app: minio
spec:
containers:
- name: minio
image: minio/minio:RELEASE.2024-05-10T01-41-38Z
args:
- server
- /data
- --console-address
- :9001
env:
- name: MINIO_ROOT_USER
value: "admin-velero"
- name: MINIO_ROOT_PASSWORD
value: "StrongSecretKey123"
ports:
- containerPort: 9000
- containerPort: 9001
volumeMounts:
- name: storage
mountPath: /data
volumes:
- name: storage
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: minio
spec:
ports:
- port: 9000
targetPort: 9000
name: api
selector:
app: minio
EOF
次に、ポート9001でMinIOコンソールにログインするか、MinIOクライアント(mc)を使用してvelero-backupsという名前のバケットを作成します。
2. Velero CLIのインストールと認証情報の設定
ご使用のOSに対応したVelero CLI(v1.13以上)を取得し、MinIOの認証情報をローカルのINI形式ファイルに保存します。
# credentials-veleroファイルを作成
cat <<EOF > credentials-velero
[default]
aws_access_key_id = admin-velero
aws_secret_access_key = StrongSecretKey123
EOF
3. Node-Agentを有効にしてVelero Serverをインストール
クラスタにVeleroをデプロイします。クラスタ内のMinIOサービスエンドポイントを指定し、クラウドのスナップショットプラグインなしでファイルレベルのボリュームバックアップをサポートするために--use-node-agentを指定します。
velero install \
--provider aws \
--plugins velero/velero-plugin-for-aws:v1.9.0 \
--bucket velero-backups \
--secret-file ./credentials-velero \
--use-node-agent \
--backup-location-config region=minio,s3ForcePathStyle="true",s3Url=http://minio.minio.svc.cluster.local:9000
デプロイとnode-agent DaemonSetが正常に稼働していることを確認します。
kubectl get pods -n velero
すべてのコントローラおよびnode-agentのPodは、30〜60秒以内にRunningステータスに移行するはずです。
4. サンプルのステートフルワークロードをデプロイ
1GiのPersistentVolumeClaim(PVC)をマウントしたNginx Webサーバを含むテスト用ネームスペースを作成します。
kubectl create namespace production-app
kubectl apply -n production-app -f - <<EOF
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: web-data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-server
spec:
replicas: 1
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:alpine
volumeMounts:
- mountPath: /usr/share/nginx/html
name: storage
volumes:
- name: storage
persistentVolumeClaim
claimName: web-data
EOF
復旧後にデータが保持されているか検証するために、ボリュームにサンプルデータを書き込みます。
POD_NAME=$(kubectl get pod -n production-app -l app=web -o jsonpath="{.items[0].metadata.name}")
kubectl exec -n production-app $POD_NAME -- sh -c 'echo "クラスタのディザスタリカバリが成功しました!" > /usr/share/nginx/html/index.html'
5. ネームスペース全体のバックアップを実行
Veleroに指示して、PVCデータを含めたproduction-appネームスペースをバックアップします。
velero backup create production-backup \
--include-namespaces production-app \
--default-volumes-to-fs-backup \
--wait
コマンドの実行完了後、バックアップのステータスを確認します。
velero backup describe production-backup --details
エラー数が0でフェーズがCompletedとなっており、スナップショットリストにKopiaによるボリュームバックアップが記録されていることが確認できます。
6. ディザスタリカバリの検証
本番ネームスペースを削除して、偶発的なデータ消失をシミュレートします。
kubectl delete namespace production-app
kubectl get namespace production-app
Kubernetesによってネームスペースが完全に削除されたら、MinIOからアプリケーションとボリュームをリストアします。
velero restore create --from-backup production-backup --wait
velero restore get
Podが実行中であることを確認し、復元されたファイルの内容を検査します。
RESTORED_POD=$(kubectl get pod -n production-app -l app=web -o jsonpath="{.items[0].metadata.name}")
kubectl exec -n production-app $RESTORED_POD -- cat /usr/share/nginx/html/index.html
シェルにクラスタのディザスタリカバリが成功しました!と出力されます。マニフェスト、PVC、ファイルデータが完全に復元されました。

