はじめに
AWS EKSの運用をしていると、標準サポートが切れるので定期的にEKSのバージョンアップをする必要があります。
弊社では新しいEKSクラスターを作成してBlue/Greenデプロイで切り替えるのですが、現行EKSクラスターで利用中のアプリケーションの永続データ(EBSを用いたPV/PVC)を移行するのが結構大変です。
何か良いツールないかなと思って探してVeleroを知ったので、実際にバックアップとリストアを試してみることにしました。
バックアップ取得
Veleroのインストール
今回はローカルから手動でバックアップとリストアをするので、brew install veleroでVeleroをインストールします。
IAM/IRSA/S3/EBSの権限設定
terraformで必要なリソースを作成します。
resource "aws_s3_bucket" "velero" {
bucket = "dev-velero"
tags = {
Name = "dev-velero"
Environment = "dev"
}
}
resource "aws_s3_bucket_policy" "velero" {
bucket = aws_s3_bucket.velero.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
AWS = aws_iam_role.velero.arn
}
Action = [
"s3:GetObject",
"s3:DeleteObject",
"s3:PutObject",
"s3:AbortMultipartUpload",
"s3:ListMultipartUploadParts",
"s3:ListBucket"
]
Resource = [
"${aws_s3_bucket.velero.arn}",
"${aws_s3_bucket.velero.arn}/*"
]
}
]
})
}
resource "aws_iam_policy" "velero" {
name = "VeleroAccessPolicy"
description = "Policy for Velero backup and restore"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"s3:GetObject",
"s3:DeleteObject",
"s3:PutObject",
"s3:AbortMultipartUpload",
"s3:ListMultipartUploadParts",
"s3:ListBucket"
]
Resource = [
"${aws_s3_bucket.velero.arn}",
"${aws_s3_bucket.velero.arn}/*"
]
},
{
Effect : "Allow",
Action : [
"ec2:DescribeVolumes",
"ec2:DescribeSnapshots",
"ec2:CreateTags",
"ec2:CreateVolume",
"ec2:CreateSnapshot",
"ec2:DeleteSnapshot"
],
Resource : "*"
}
]
})
}
resource "aws_iam_role" "velero" {
name = "eks-velero-recovery"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Federated = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:oidc-provider/${var.current_eks_oidc_provider}"
}
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
StringEquals = {
"${var.current_eks_oidc_provider}:sub" = "system:serviceaccount:velero:velero-server"
}
}
},
{
Effect = "Allow"
Principal = {
Federated = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:oidc-provider/${var.new_eks_oidc_provider}"
}
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
StringEquals = {
"${var.new_eks_oidc_provider}:sub" = "system:serviceaccount:velero:velero-server"
}
}
}
]
})
}
resource "aws_iam_role_policy_attachment" "velero" {
role = aws_iam_role.velero.name
policy_arn = aws_iam_policy.velero.arn
}
HelmでVeleroをデプロイ
新旧のEKSクラスターに対してveleroのnamespaceを作成し、Helmでveleroをインストールします。
defaultのvalueから以下の点を変更してます。
# ...(省略)...
kubectl:
image:
repository: docker.io/bitnamilegacy/kubectl
# Bitnamiイメージのセキュリティ対応により、bitnamilegacy のリポジトリで使えるのはlatestタグのイメージのみとなる。
# https://github.com/bitnami/containers/issues/83267
+ tag: latest
# ...(省略)...
configuration:
backupStorageLocation:
- name:
+ provider: aws
+ bucket: dev-velero
# ...(省略)...
volumeSnapshotLocation:
- name:
+ provider: aws
credential:
name:
key:
config:
+ region: ap-northeast-1
# ...(省略)...
serviceAccount:
server:
create: true
name:
annotations:
# terraformで作成したIAM roleを使ってveleroのServiceAccountを作成
+ eks.amazonaws.com/role-arn: "arn:aws:iam::123456789012:role/eks-velero-recovery"
apiVersion: v1
kind: Namespace
metadata:
name: velero
BackupStorageLocationの確認
veleroのデプロイが出来たら、kubectlやveleroのコマンドでBackupStorageLocationが作成されているか確認します。
$ kubectl -n velero get backupstoragelocation
NAME PHASE LAST VALIDATED AGE DEFAULT
default Available 51s 24h true
$ velero backup-location get
NAME PROVIDER BUCKET/PREFIX PHASE LAST VALIDATED ACCESS MODE DEFAULT
default aws dev-velero Available 2025-12-23 18:06:05 +0900 JST ReadWrite true
バックアップ作成
kubectlコンテキストを現行クラスターに切り替え、velero backup create を使ってpersistentvolumeのバックアップを取得していきます。
$ velero backup create <バックアップ名> --include-namespaces <バックアップ対象のnamespace> --include-resources persistentvolumes,persistentvolumeclaims
Backup request "<バックアップ名>" submitted successfully.
Run `velero backup describe <バックアップ名>` or `velero backup logs <バックアップ名>` for more details.
バックアップの状態確認
エラーなくPhase: Completedになっていることを確認します。
$ velero backup describe <バックアップ名>
Name: <バックアップ名>
Namespace: velero
Labels: velero.io/storage-location=default
Annotations: velero.io/resource-timeout=10m0s
velero.io/source-cluster-k8s-gitversion=v1.31.13-eks-3025e55
velero.io/source-cluster-k8s-major-version=1
velero.io/source-cluster-k8s-minor-version=31
Phase: Completed
Namespaces:
Included: <バックアップ対象のnamespace>
Excluded: <none>
Resources:
Included: persistentvolumes, persistentvolumeclaims
Excluded: volumesnapshots.snapshot.storage.k8s.io, volumesnapshotcontents.snapshot.storage.k8s.io
Cluster-scoped: auto
Label selector: <none>
Or label selector: <none>
Storage Location: default
Velero-Native Snapshot PVs: auto
File System Backup (Default): false
Snapshot Move Data: false
Data Mover: velero
TTL: 720h0m0s
CSISnapshotTimeout: 10m0s
ItemOperationTimeout: 4h0m0s
Hooks: <none>
Backup Format Version: 1.1.0
Started: 2025-12-23 18:46:36 +0900 JST
Completed: 2025-12-23 18:46:39 +0900 JST
Expiration: 2026-01-22 18:46:36 +0900 JST
Total items to be backed up: 4
Items backed up: 4
Backup Volumes:
Velero-Native Snapshots:
pvc-43704220-5e87-433a-a299-a6c953211281: specify --details for more information
pvc-f077283e-4306-459f-8a91-de2c71d05bc1: specify --details for more information
CSI Snapshots: <none included>
Pod Volume Backups: <none included>
HooksAttempted: 0
HooksFailed: 0
backup先に指定したs3にデータが取得できていることも確認します。
$ aws s3 ls s3://dev-velero/backups/<バックアップ名>/
2025-12-23 18:46:39 29 <バックアップ名>-csi-volumesnapshotclasses.json.gz
2025-12-23 18:46:39 27 <バックアップ名>-itemoperations.json.gz
2025-12-23 18:46:39 5582 <バックアップ名>-logs.gz
2025-12-23 18:46:39 29 <バックアップ名>-podvolumebackups.json.gz
2025-12-23 18:46:39 169 <バックアップ名>-resource-list.json.gz
2025-12-23 18:46:39 49 <バックアップ名>-results.gz
2025-12-23 18:46:39 368 <バックアップ名>-volumeinfo.json.gz
2025-12-23 18:46:39 331 <バックアップ名>-volumesnapshots.json.gz
2025-12-23 18:46:39 2154 <バックアップ名>.tar.gz
2025-12-23 18:46:40 3673 velero-backup.json
リストア
kubectlコンテキストを新クラスターに切り替え、velero restore create を使って取得したバックアップからリストアを実行します。
$ velero restore create --from-backup <バックアップ名> --include-resources persistentvolumes,persistentvolumeclaims
Restore request "<バックアップ名>-20251223220846" submitted successfully.
Run `velero restore describe <バックアップ名>-20251223220846` or `velero restore logs <バックアップ名>-20251223220846` for more details.
velero restore describeで結果を確認します。
エラーなくPhase: Completedが表示されていたら問題なしです。
$ velero restore describe <バックアップ名>-20251223220846
Name: <バックアップ名>-20251223220846
Namespace: velero
Labels: <none>
Annotations: <none>
Phase: Completed
Total items to be restored: 4
Items restored: 4
Started: 2025-12-23 22:08:47 +0900 JST
Completed: 2025-12-23 22:08:51 +0900 JST
Backup: <バックアップ名>
Namespaces:
Included: all namespaces found in the backup
Excluded: <none>
Resources:
Included: persistentvolumes, persistentvolumeclaims
Excluded: nodes, events, events.events.k8s.io, backups.velero.io, restores.velero.io, resticrepositories.velero.io, csinodes.storage.k8s.io, volumeattachments.storage.k8s.io, backuprepositories.velero.io
Cluster-scoped: auto
Namespace mappings: <none>
Label selector: <none>
Or label selector: <none>
Restore PVs: auto
CSI Snapshot Restores: <none included>
Existing Resource Policy: <none>
ItemOperationTimeout: 4h0m0s
Preserve Service NodePorts: auto
Uploader config:
HooksAttempted: 0
HooksFailed: 0
リストア後、新クラスターの対象アプリのデータが現行クラスターと同じになっていることを確認します。
$ kubectl get pvc -n <バックアップ対象のnamespace>
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
pvcxxxxxx-xxxxxxxxxx1 Bound pvc-43704220-5e87-433a-a299-a6c95321128a 10Gi RWO gp3 <unset> 97s
pvcxxxxxx-xxxxxx2 Bound pvc-f077283e-4306-459f-8a91-de2c71d05bc7 8Gi RWO gp3 <unset> 96s
注意点
新クラスターで同名のPVがある場合はリストア時に already exists エラーとなるので、事前削除が必要です。現行クラスターのリソースをコピーして新クラスターを作成する時は対応が必要です。
まとめ
Veleroを使って、EKSの永続データのバックアップ・リストアが出来ることを確認しました。
参考