
EKS를 몇 년째 운영하다 보면 한 번쯤은 이런 순간이 온다. 누군가 kubectl delete ns prod-something을 잘못 쳤거나, GitOps 동기화가 꼬여서 ConfigMap이 통째로 날아갔거나. 우리 팀에서는 작년에 한 번 비슷한 사고가 났다. ArgoCD가 잘못된 브랜치를 source로 잡아서 네임스페이스 하나를 prune 해버린 거다. 다행히 ETCD 덤프가 있었지만, 복구하는 데 반나절을 썼다.
그 사건 이후로 Velero를 정식으로 도입했다. 이번 글은 EKS 환경에서 Velero를 0에서부터 세팅하고 운영하는 실전 가이드다. 공식 문서에 나오는 기본 설치 말고, 실제 운영 들어갔을 때 부딪히는 부분 위주로 적었다.
왜 Velero인가
EKS 백업 옵션은 몇 가지가 있다. AWS Backup이 EKS를 정식 지원하기 시작했고(2024년 GA), Kasten K10 같은 상용 제품도 있다. 그럼에도 Velero를 쓰는 이유는 단순하다. CRD까지 포함한 전체 리소스를 yaml 단위로 백업/복원할 수 있고, 다른 클러스터로 그대로 옮길 수 있어서다. AWS Backup은 EBS 스냅샷에 강한 반면, 리소스 정의 복원은 약하다.
CSI 스냅샷 지원도 이제 안정적이다. EBS CSI Driver와 Velero CSI 플러그인을 같이 쓰면 PV까지 같이 백업된다.
1. IAM 권한 설정 (IRSA 또는 Pod Identity)
Velero가 S3에 백업을 올리고 EBS 스냅샷을 만들려면 IAM 권한이 필요하다. 우리 팀은 Pod Identity로 갈아탔다. IRSA보다 신뢰 정책 관리가 단순해서다.
먼저 정책을 만든다:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:DescribeVolumes",
"ec2:DescribeSnapshots",
"ec2:CreateTags",
"ec2:CreateVolume",
"ec2:CreateSnapshot",
"ec2:DeleteSnapshot"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:DeleteObject",
"s3:PutObject",
"s3:AbortMultipartUpload",
"s3:ListMultipartUploadParts"
],
"Resource": "arn:aws:s3:::velero-backup-prod/*"
},
{
"Effect": "Allow",
"Action": ["s3:ListBucket"],
"Resource": "arn:aws:s3:::velero-backup-prod"
}
]
}
S3 버킷은 별도 계정(백업 전용)으로 분리하는 걸 권장한다. prod 계정이 통째로 털리는 시나리오를 대비해서. 우리는 백업 계정에 S3 Object Lock을 걸어둔다. ransomware 대응용이다.
2. Velero 설치
Helm 차트가 깔끔하다. 기본값 그대로 쓰면 안 되는 옵션 몇 개만 짚는다:
# velero-values.yaml
configuration:
backupStorageLocation:
- name: default
provider: aws
bucket: velero-backup-prod
config:
region: ap-northeast-2
s3ForcePathStyle: false
volumeSnapshotLocation:
- name: default
provider: aws
config:
region: ap-northeast-2
features: EnableCSI
initContainers:
- name: velero-plugin-for-aws
image: velero/velero-plugin-for-aws:v1.10.0
volumeMounts:
- mountPath: /target
name: plugins
serviceAccount:
server:
annotations:
# Pod Identity 쓰는 경우 annotation 불필요. IRSA 쓸 때만:
# eks.amazonaws.com/role-arn: arn:aws:iam::...
# 메트릭은 무조건 켜둔다
metrics:
enabled: true
serviceMonitor:
enabled: true
# 백업 실패 시 알람 받으려면 필수
upgradeCRDs: true
features: EnableCSI가 핵심이다. 이걸 안 켜면 EBS 스냅샷이 CSI 스냅샷이 아닌 레거시 방식으로 만들어진다. EKS 1.24부터는 CSI 스냅샷이 권장 방식이다.
3. 첫 백업 — 그냥 한번 찍어보기
설치가 끝났으면 즉시 백업 한번 떠보자.
velero backup create test-backup-001 --include-namespaces default
velero backup describe test-backup-001 --details
상태가 Completed로 떨어지면 일단 동작은 한다. S3 버킷에 들어가서 실제 파일이 올라갔는지 눈으로 확인하는 것도 잊지 말자. 가끔 Completed인데 실제론 빈 백업인 경우가 있다(권한이 일부만 부여된 경우).
4. 스케줄 설정
운영 환경에서는 Schedule 리소스로 자동화한다. 우리 팀은 이렇게 쓴다:
apiVersion: velero.io/v1
kind: Schedule
metadata:
name: daily-full-backup
namespace: velero
spec:
schedule: "0 18 * * *" # KST 03:00 (UTC 18:00)
template:
ttl: 720h0m0s # 30일 보존
includedNamespaces:
- "*"
excludedNamespaces:
- kube-system
- kube-public
- velero
snapshotVolumes: true
defaultVolumesToFsBackup: false
storageLocation: default
volumeSnapshotLocations:
- default
새벽 3시에 도는 이유는 단순하다. 트래픽이 가장 적은 시간대라 EBS 스냅샷 I/O 영향이 적기 때문이다. snapshotVolumes는 PV가 있는 워크로드만 true로 의미가 있고, 가벼운 stateless 서비스는 굳이 PV 스냅샷을 안 떠도 된다.
defaultVolumesToFsBackup: false로 둔 건 의도적이다. fs-backup(예전 Restic, 지금은 Kopia)은 데이터가 크면 느리고 비용도 더 든다. 우리는 CSI 스냅샷만 쓴다.
5. 복구는 실제로 해봐야 안다
여기가 제일 중요하다. 백업은 만들어두기만 하고 한 번도 안 써본 게 가장 위험하다.
월 1회 정기 복구 훈련을 한다. 별도 네임스페이스로 복원해보고, 리소스가 정상적으로 올라오는지 확인한다.
# test-restore 네임스페이스로만 복원
velero restore create restore-drill-202605 \
--from-backup daily-full-backup-20260516030012 \
--namespace-mappings prod-api:test-restore \
--include-namespaces prod-api
복원 후 체크리스트:
- Pod이 Running 상태로 올라오는가
- Service의 ClusterIP가 새로 발급되었는데 의존하는 다른 리소스가 깨지지 않았는가
- PVC가 정상 바인딩되는가 (CSI 스냅샷에서 복원된 PV가 실제 데이터를 담고 있는가)
- ConfigMap, Secret이 올라왔는가
특히 Secret 복원할 때 주의할 점이 있다. ESO(External Secrets Operator)로 만든 Secret은 복원 후에도 ESO가 다시 sync를 돌리면 덮어쓴다. 그래서 Velero로는 ESO 자체와 ExternalSecret 리소스만 백업하면 충분하다. 평문 Secret을 백업에 굳이 넣을 필요 없다.
6. 메트릭과 알람
Velero는 Prometheus 메트릭을 제공한다. 가장 중요한 건 백업 실패 알람이다.
# PrometheusRule
groups:
- name: velero
rules:
- alert: VeleroBackupPartialFailure
expr: |
increase(velero_backup_partial_failure_total[1h]) > 0
for: 5m
annotations:
summary: "Velero backup partial failure detected"
- alert: VeleroBackupFailure
expr: |
increase(velero_backup_failure_total[1h]) > 0
for: 5m
annotations:
summary: "Velero backup failed"
- alert: VeleroBackupNotRun
expr: |
time() - velero_backup_last_successful_timestamp > 86400 * 2
annotations:
summary: "Velero backup not run in 2 days"
backup_partial_failure_total이 핵심이다. 전체 실패가 아니라 일부 리소스만 백업 실패하는 경우가 의외로 많다. 보통 CRD 권한 문제, 또는 새로 추가된 리소스 타입 때문이다.
운영하면서 만난 함정들
1. CRD를 빼먹은 백업 — 처음에 excludedResources에 CRD를 잘못 넣어서, 복원할 때 CRD가 없어서 워크로드가 못 올라온 적이 있다. 기본값을 건드릴 때는 신중해야 한다.
2. immutable S3 lock과 충돌 — Object Lock을 너무 강하게 걸면 Velero의 garbage collection이 동작 못 한다. Compliance 모드 말고 Governance 모드로 두는 게 운영상 편하다.
3. 클러스터 버전 차이 — 1.27에서 백업한 걸 1.30으로 복원하려면 deprecated API 때문에 깨질 수 있다. velero install 시점의 Kubernetes 버전과 복원 대상 버전이 너무 멀리 떨어지지 않도록 관리한다.
4. 플러그인 버전 핀 — velero-plugin-for-aws 버전을 latest로 두면 어느 날 갑자기 백업이 깨진다. 무조건 버전 핀 박는다.
마치며
EKS 운영하면서 가장 마음 편한 건 "어제 백업 잘 돌았다"는 알람을 매일 아침 보는 거다. 백업 자체보다 복원 훈련이 더 중요하다는 걸 사고 한 번 겪고 나서야 알았다.
다음에는 cross-region DR 시나리오와 Karmada 같은 multi-cluster 환경에서의 Velero 운영 패턴도 정리해보려고 한다.
'IT > Kubernets' 카테고리의 다른 글
| Pod 시작이 느릴 때, fsGroupChangePolicy 한 줄만 바꿔보자 (1) | 2026.05.19 |
|---|---|
| etcd defrag, CronJob에 그냥 박아놓으면 안 된다 (0) | 2026.05.18 |
| kubectl debug --target 플래그, 이거 모르는 분 꽤 많더라 (0) | 2026.05.16 |
| Cilium vs Calico — 6개월 검토하고 우리가 내린 결론 (0) | 2026.05.16 |
| HPA behavior 필드 잘못 만지다가 P99 튀어버린 이야기 (0) | 2026.05.16 |