재해 복구 및 RTO/RPO 가이드
이 문서는 EKB AWS/EKS 인프라의 재해 복구 (DR) 전략, 복구 시간 목표 (RTO), 복구 지점 목표 (RPO)를 설명합니다. 내장된 HA 기능, 액티브 백업 시스템, 복구 절차 및 알려진_gap을 다룹니다.
배포 지침은 Terragrunt 배포 가이드를 참조하세요.
아키텍처 및 고가용성 요약
인프라 구성 요소
| 구성 요소 | 구현 | HA 모델 |
|---|---|---|
| 컴퓨팅 | EKS (Kubernetes 1.33) + Karpenter | 멀티 AZ 노드, Spot + 온디맨드 |
| Pod 자동 스케일링 | KEDA | CPU/메모리 기반, 최소 2개 복제본 |
| 인그레스 / SSL | ALB + ACM | 멀티 AZ, 헬스 체크 라우팅 |
| 캐시 | ElastiCache Redis (조건부) | 자동 페일오버가 포함된 멀티 AZ |
| 메시지 큐 | Amazon MQ / RabbitMQ (조건부) | 단일 인스턴스 또는 액티브/스탠바이 |
| 데이터베이스 (셀프호스팅) | CloudNativePG HA 클러스터 + PgBouncer | 프라이머리 + 복제본, 자동 페일오버 |
| 데이터베이스 (클라우드) | Supabase Cloud | 매니지드, 제공자에 의한 크로스 리전 |
| 관측 가능성 | SigNoz + k8s-infra (조건부) | 클러스터 내, 단일 장애 지점 없음 |
| IaC 상태 | S3 (버전 관리) + DynamoDB 잠금 | 암호화, 버전 관리 |
| 퍼시스턴트 볼륨 | EBS (EBS CSI Driver 경유) | AWS Data Lifecycle Manager를 통한 스냅샷 |
현재 HA 기능
| 기능 | 복구 시간 | 데이터 손실 | 상태 |
|---|---|---|---|
| AZ 장애 (Pod) | 0–5분 | 없음 | 활성 |
| Pod 장애 / 크래시 | 0–2분 | 없음 | 활성 |
| ALB 헴스 체크 재라우팅 | 0–1분 | 없음 | 활성 |
| Redis Multi-AZ 페일오버 | < 1분 | 없음 | 활성 (활성화 시) |
| Karpenter 노드 교체 | 0–10분 | 없음 | 활성 |
| EBS 스냅샷 복원 | 15–30분 | 최대 24시간 | 활성 |
| CloudNativePG PITR 복원 | 15–60분 | WAL 지연만큼 | 활성 (활성화 시) |
| 전체 인프라 재생성 | 30–60분 | 없음 (IaC) | Terragrunt를 통해 |
RTO / RPO 목표
| 시나리오 | RTO 목표 | RPO 목표 | 현재 기능 |
|---|---|---|---|
| 단일 Pod 장애 | < 2분 | 0 | 충족 (KEDA 최소 복제본) |
| 단일 노드 장애 | < 10분 | 0 | 충족 (Karpenter 교체) |
| AZ 장애 | < 5분 | 0 | 충족 (멀티 AZ 분산) |
| Redis 장애 | < 1분 | 0 | 충족 (멀티 AZ 페일오버) |
| Postgres 프라이머리 장애 | < 5분 | 0–초 단위 WAL 지연 | 충족 (CNPG 자동 페일오버) |
| EBS 볼륨 손실 | 15–30분 | < 24시간 | 부분적 — DLM 스냅샷 일간 |
| 전체 리전 장애 | > 60분 | 백업 빈도에 따라 | 현재 자동화되지 않음 |
백업 시스템
1. Terraform / Terragrunt 상태 — S3
백업 대상: 모든 인프라 상태 (EKS, 네트워킹, IAM, Helm 릴리스).
구현:
- 환경별 S3 버킷:
ekb-terraform-state-<env-name>(terragrunt/environments/<env-name>/state/를 통해 부트스트랩) - 버전 관리 활성화 — 이전 상태 버전을 모두 복원 가능
- 서버 측 암호화 (AES-256)
- 상태 잠금을 위한 DynamoDB 테이블
복구: S3에서 이전 상태 버전으로 롤백한 후 terragrunt apply를 다시 실행합니다.
RPO: 모든 terragrunt apply 커밋 — 지속적.
2. EBS 퍼시스턴트 볼륨 — AWS Data Lifecycle Manager
백업 대상: Pod에 연결된 EBS 볼륨 (Automator PostgreSQL, Supabase MinIO, 모든 상태형 워크로드).
구현: 환경 태그를 대상으로 하는 AWS Data Lifecycle Manager (DLM) 정책.
# Tag EBS volumes with your environment name, then create a DLM policy:
# - Resource: EBS volumes
# - Target tag: Environment=<your-env-name>
# - Schedule: Daily at 02:00 UTC
# - Retention: 7 snapshots
복구:
# 1. Identify the snapshot to restore
aws ec2 describe-snapshots --filters "Name=tag:Environment,Values=<your-env-name>"
# 2. Create a volume from the snapshot
aws ec2 create-volume \
--snapshot-id snap-xxxxxxxxxxxxxxxxx \
--availability-zone <your-az> \
--volume-type gp3
# 3. Update the PersistentVolume in Kubernetes to reference the new volume ID
kubectl patch pv <pv-name> -p '{"spec":{"awsElasticBlockStore":{"volumeID":"<new-vol-id>"}}}'
RPO: 최대 24시간 (일간 스냅샷). DLM 스케줄 빈도를 늘려 줄일 수 있습니다.
3. CloudNativePG (HA Supabase DB) — Barman Cloud 백업
적용 대상: ENABLE_CNPG=true 및 ENABLE_HA_SUPABASE_DB=true인 환경.
백업 대상: S3 또는 MinIO로의 지속적인 WAL (Write-Ahead Log) 스트리밍과 CNPG ScheduledBackup CRD를 통한 예약된 전체 기본 백업을 포함하는 CloudNativePG Postgres 클러스터 (ha-supabase-db).
구현 (values/ha-supabase-db.yaml에서 구성):
postgres:
backup:
enabled: true # toggle barman-cloud backups
# barmanObjectStore points to S3 bucket or MinIO endpoint
# IRSA / service account must have s3:PutObject, s3:GetObject, s3:ListBucket
retentionPolicy: "30d" # keep backups for 30 days
compression: gzip
백업 상태 확인:
# List all backups for the cluster
kubectl get backup -n ha-supabase-db
# Check a specific backup
kubectl describe backup <backup-name> -n ha-supabase-db
# Trigger an on-demand backup
kubectl cnpg backup ha-supabase-db -n ha-supabase-db
지점 복구 (PITR):
# Create a new CNPG Cluster restoring from a backup
# (in a new namespace or after removing the original)
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: ha-supabase-db-restored
namespace: ha-supabase-db-restore
spec:
instances: 3
bootstrap:
recovery:
source: ha-supabase-db
recoveryTarget:
targetTime: "2026-03-19T03:00:00.000000+00:00" # target point in time
externalClusters:
- name: ha-supabase-db
barmanObjectStore:
# same S3/MinIO config as the source cluster
serverName: ha-supabase-db
RPO: WAL가 활성화된 클러스터의 경우 거의 제로 (WAL 업로드 간격으로 제한되는 초 단위 지연).
4. ElastiCache Redis — Multi-AZ 복제
적용 대상: ENABLE_AWS_SERVICES=true인 환경.
Redis는 기본 데이터 저장소가 아니며, 일시적인 캐시 및 세션 데이터를 보유합니다. DR 초점은 백업/복원보다는 빠른 페일오버에 있습니다.
구현:
- 자동 페일오버가 포함된 Multi-AZ 활성화 -静态 저장소 및 전송 중 암호화
- 프라이머리 노드 장애 시 복제본이 자동 승격 (< 1분)
RPO: Redis 데이터는 설계적으로 ephemeral합니다. 페일오버 후 캐시 미스는 예상되며, 애플리케이션이 데이터베이스에서 다시 채웁니다.
5. Amazon MQ (RabbitMQ) — 메시지 큐
적용 대상: ENABLE_AWS_SERVICES=true인 환경.
구현:
- 단일 인스턴스 (기본값) 또는 액티브/스탠바이 배포
- 브로커 재시작 중 전송 중인 메시지가 손실될 수 있으므로, 소비자를 멱등성 있게 설계
- 관리 UI는 포트 15671 (SSL)에서 사용 가능
RPO: 단일 인스턴스 — 장애 시점에 전송 중인 메시지가 손실될 수 있습니다. 이 위험을 줄이려면 액티브/스탠바이 배포 모드를 사용하세요.
자동 복구 메커니즘
Karpenter — 노드 프로비저닝 및 복구
- 통합:
WhenEmptyOrUnderutilized— 유휴 노드가 자동으로 종료됨 - Spot 중단 처리: SQS 중단 이벤트를 수신; 종료 전에 Spot 노드를 드레인하고 교체
- 노드 드리프트: 오래된 AMI 또는 구성을 사용하는 노드가
enable_drift = true일 때 자동으로 교체됨 - 복구 시간: 새 노드가 0–10분 내에 프로비저닝됨
KEDA — Pod 자동 스케일링
- 최소 복제본: 모든 서비스 (Web, API, Celery, Automator)에 2개 — 단일 장애 지점 방지
- CPU 임계값: 60–70%에서 스케일 아웃 트리거
- 메모리 임계값: 80%에서 스케일 아웃 트리거
- 스케일 다운 안정화: 30초 — 플래핑 방지
- 복구 시간: 장애 Pod가 0–2분 내에 다시 스케줄링됨
Kubernetes Pod 안티어피니티
모든 상태 비저장 서비스는 Pod를 노드와 AZ에 분산하기 위해 kubernetes.io/hostname에 대한 preferredDuringSchedulingIgnoredDuringExecution 안티어피니티를 사용합니다.
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values: ["web", "fastapi-backend", "celery-worker", "automator"]
topologyKey: kubernetes.io/hostname
복구 절차
시나리오 1: AZ 장애
예상 동작: Karpenter가 나머지 AZ에 교체 노드를 프로비저닝하고; KEDA가 Pod를 다시 스케일링하고; ALB가 건강하지 않은 대상으로의 라우팅을 중지합니다.
검증:
kubectl get nodes -o wide # confirm nodes are in healthy AZs
kubectl get pods -A -o wide # confirm pods are running
kubectl get ingress # confirm ALB is routing correctly
일반적인 상황에서는 수동 개입이 필요하지 않습니다.
시나리오 2: Postgres 프라이머리 노드 장애 (CloudNativePG)
CloudNativePG가 자동으로 복제본을 프라이머리로 승격합니다.
# Monitor failover
kubectl get cluster ha-supabase-db -n ha-supabase-db -w
# Confirm new primary
kubectl cnpg status ha-supabase-db -n ha-supabase-db
# Check PgBouncer pooler is pointing to new primary
kubectl get svc -n ha-supabase-db | grep pooler
애플리케이션이 PgBouncer를 통해 재연결됩니다 — 연결 문자열 변경이 필요하지 않습니다.
시나리오 3: 스냅샷에서 EBS 볼륨 복원
# 1. Find snapshots for the environment
aws ec2 describe-snapshots \
--filters "Name=tag:Environment,Values=<your-env-name>" \
--query 'Snapshots[*].[SnapshotId,StartTime,VolumeSize]' \
--output table
# 2. Create a new volume from the chosen snapshot
aws ec2 create-volume \
--snapshot-id snap-xxxxxxxxxxxxxxxxx \
--availability-zone <target-az> \
--volume-type gp3 \
--tag-specifications 'ResourceType=volume,Tags=[{Key=Environment,Value=<your-env-name>}]'
# 3. Scale down the affected workload
kubectl scale deployment <workload> --replicas=0
# 4. Update the PV to reference the new EBS volume, then scale back up
kubectl patch pv <pv-name> -p '{"spec":{"csi":{"volumeHandle":"<new-volume-id>"}}}'
kubectl scale deployment <workload> --replicas=2
시나리오 4: 전체 인프라 재생성
치명적 장애 후 또는 리전을 재구축할 때 사용합니다.
# 1. Bootstrap state bucket (if it doesn't exist)
cd terragrunt/environments/<your-env-name>/state
terragrunt apply
# 2. Recreate the EKS cluster and core infrastructure
cd terragrunt/environments/<your-env-name>
terragrunt apply
# 3. Deploy services in order (see Terragrunt Deployment Guide § Phase 6)
ENABLE_CNPG=true terragrunt apply --target='helm_release.local["cloudnative-pg"]'
ENABLE_HA_SUPABASE_DB=true terragrunt apply --target='helm_release.local["ha-supabase-db"]'
ENABLE_SUPABASE=true terragrunt apply --target='helm_release.local["supabase"]'
예상 RTO: 전체 클러스터의 경우 30–60분; EBS/CNPG 데이터를 복원해야 하는 경우 60–120분.
시나리오 5: Terragrunt 상태 롤백
# List state versions in S3
aws s3api list-object-versions \
--bucket ekb-terraform-state-<your-env-name> \
--prefix <state-key>
# Restore a specific version
aws s3api get-object \
--bucket ekb-terraform-state-<your-env-name> \
--key <state-key> \
--version-id <version-id> \
terraform.tfstate
# Re-import or apply with the restored state
관측 가능성 및 알림 (SigNoz)
ENABLE_SIGNOZ=true일 때, SigNoz가 monitoring 네임스페이스에 배포되어 다음을 제공합니다:
- 모든 EKB 서비스에 대한 분산 추적
- k8s-infra DaemonSet 에이전트를 통한 클러스터 메트릭 (CPU, 메모리, Pod 상태)
- 모든 Pod에서의 로그 집계
- 알림 — SigNoz에서 알림 규칙을 구성하여 Pod 크래시 루프, 높은 오류율 또는 노드 압력을 통보
DR 목적으로, SigNoz 대시보드는 사고 후 진단 및 복구 확인을 위한 주요 도구입니다. SigNoz 데이터 자체는 EBS PV에 저장되며 EBS 스냅샷 정책의 적용을 받습니다.
##_gap 및 권장 사항
| gap | 위험 | 권장 사항 |
|---|---|---|
| 크로스 리전 복제 없음 | 전체 리전 장애 = 연장된 RTO | CNPG 백업에 대한 RDS 크로스 리전 읽기 복제본 또는 S3 크로스 리전 복제를 고려하세요 |
| EBS 스냅샷이 일간 | EBS 기반 워크로드의 최대 24시간 데이터 손실 | 중요 볼륨의 DLM 빈도를 시간당으로 증가하세요 |
| RabbitMQ 단일 인스턴스 (기본값) | 브로커 장애 시 전송 중 메시지 손실 | 프로덕션에서는 ACTIVE_STANDBY_MULTI_AZ 배포 모드로 전환하세요 |
| 자동화된 DR 훈련 없음 | 복구 절차가 필요할 때까지 테스트되지 않음 | 분기별 복구 훈련을 예약하세요; 스테이징 환경에서 CNPG PITR 복원을 테스트하세요 |
| 동일 클러스터의 SigNoz | 클러스터 장애 시 관측 가능성 손실 | 별도의 경량 모니터링 엔드포인트 또는 CloudWatch 폴백 알림을 고려하세요 |