Let's Talk

Feel free to reach out. I'll get back to you as soon as possible.

Study14 min

AWS Project3 - Smart Vault

AWS+Terraform로 구축한 EC2 자동 백업·복구 시스템에 대해 설명합니다.

AWS Project3 - Smart Vault

Project 3: Smart Vault — EC2 자동 백업·복구 시스템 (EBS + Lambda + EventBridge)

Terraform으로 구축한 EC2 EBS 자동 스냅샷 시스템입니다. EC2 인스턴스에 backup:true 태그 하나만 붙이면 EventBridge가 매시간 Lambda를 트리거하여 EBS 스냅샷을 자동 생성하고, 7일이 지난 스냅샷은 매일 새벽 2시 자동으로 정리됩니다. 장애 발생 시에는 REST API 한 번으로 지정 스냅샷에서 새 EBS 볼륨을 복구할 수 있습니다.

삭제 로그는 S3 Archive 버킷(서울)에 JSON으로 쌓이며, 크로스 리전 복제를 통해 싱가포르 DR 버킷으로 자동 동기화됩니다. 서울 리전 전체 장애 상황에서도 감사 기록이 보존됩니다. Lambda 3개, EventBridge 3개, API Gateway 1개로 구성된 완전 서버리스 구조입니다.


아키텍처

[흐름 ① 자동 백업]
EventBridge (매시간)


Lambda (Backup)
 · backup:true 태그 EC2 탐색
 · EBS 볼륨 스냅샷 생성
 · 메타데이터 태그 부착 (RetainUntil = +7일, ManagedBy = smart-vault)

       ├──▶ EBS Snapshot 생성
       └──▶ SNS ──── 백업 완료 이메일 알림


[흐름 ② 자동 정리]
EventBridge (매일 새벽 2시 KST)


Lambda (Cleanup)
 · ManagedBy=smart-vault 스냅샷 전체 조회
 · RetainUntil < 오늘 → 삭제 대상 판정
 · DRY_RUN=true 이면 목록만 출력 (실제 삭제 없음)

       ├──▶ 만료 스냅샷 삭제
       └──▶ S3 Archive (서울) ──── 삭제 JSON 로그 저장

                  ▼ (크로스 리전 복제)
             S3 DR (싱가포르)


[흐름 ③ 수동 복구]
curl POST /restore
(x-api-key 헤더 필수)


API Gateway


Lambda (Restore)
 · snapshot_id 기반 새 EBS 볼륨 생성

       ├──▶ 새 EBS 볼륨 (EC2에 수동 연결 후 복구 완료)
       └──▶ SNS ──── 복구 완료 이메일 알림

주요 구성 요소

리소스 설명 비용
Lambda (Backup) backup:true 태그 EC2 탐색 → EBS 스냅샷 생성 → SNS 알림 프리 티어: 월 1,000,000 요청
Lambda (Cleanup) 만료 스냅샷 조회·삭제 → S3 감사 로그 저장. DRY_RUN 모드 지원 프리 티어: 월 1,000,000 요청
Lambda (Restore) POST 요청 수신 → 스냅샷에서 새 EBS 볼륨 생성 → SNS 알림 프리 티어: 월 1,000,000 요청
EventBridge (매시간 백업) 매 정시 Backup Lambda 트리거 프리 티어: 월 1,000,000 이벤트
EventBridge (매일 09시 백업) 매일 오전 9시(KST) Backup Lambda 트리거 프리 티어: 월 1,000,000 이벤트
EventBridge (매일 02시 정리) 매일 새벽 2시(KST) Cleanup Lambda 트리거 프리 티어: 월 1,000,000 이벤트
S3 Archive (서울) Cleanup Lambda가 삭제 JSON 로그를 저장하는 감사 버킷. 1년치 기록 보관 프리 티어: 5GB / 월
S3 DR (싱가포르) 서울 버킷에서 크로스 리전 복제로 자동 동기화되는 재해 복구 버킷 크로스 리전 전송 ~$0.02/GB (로그 파일은 수 KB 수준이라 실질 비용 미미)
API Gateway Restore Lambda 앞단. API 키 인증(x-api-key 헤더) 필수 프리 티어: 월 1,000,000 호출 (12개월)
SNS 백업 완료·복구 완료 시 등록 이메일로 알림 발송 프리 티어: 월 1,000건 이메일
CloudWatch Lambda 실행 로그, 에러율, 대시보드 제공 프리 티어: 5GB 로그 수집

EventBridge 스케줄 참고: EventBridge 규칙은 UTC 기준입니다.

  • KST 02:00 = cron(0 17 * * ? *) (UTC 전일 17:00)
  • KST 09:00 = cron(0 0 * * ? *) (UTC 00:00)

사전 준비

IAM 사용자 생성

AWS CLI와 Terraform이 AWS 리소스를 생성할 수 있도록 프로그래밍 방식 액세스 권한을 가진 IAM 사용자가 필요합니다.

1단계: IAM 사용자 생성

AWS 콘솔에서 IAM → 사용자 → 사용자 생성으로 이동합니다. 사용자 이름(예: terraform-admin)을 입력하고, AWS Management Console 액세스는 체크하지 않습니다 (CLI 전용 사용자이므로 콘솔 로그인 권한 불필요).

2단계: AdministratorAccess 권한 부여

권한 설정 단계에서 직접 정책 연결을 선택하고 AdministratorAccess 정책을 검색하여 체크합니다.

⚠️ AdministratorAccess는 실습/테스트용 편의 설정입니다. 실제 운영 환경에서는 최소 권한 원칙에 따라 필요한 서비스만 허용하는 커스텀 정책을 사용하세요.

3단계: 액세스 키(Access Key) 발급 및 저장

사용자 생성 후 해당 사용자 → 보안 자격 증명 → 액세스 키 만들기로 이동합니다. 사용 사례로 **Command Line Interface(CLI)**를 선택합니다. 발급된 액세스 키 ID시크릿 액세스 키는 이 화면에서만 확인 가능하므로, CSV를 다운로드하거나 별도로 메모해 두세요. 화면을 닫으면 시크릿 키는 다시 볼 수 없습니다.


AWS CLI v2 설치

# Linux / macOS의 경우
brew install awscli
aws --version
# Windows의 경우
winget install Amazon.AWSCLI
aws --version

Terraform 설치

# Linux / macOS의 경우

# 1. HashiCorp 저장소 추가
brew tap hashicorp/tap

# 2. Terraform 설치
brew install hashicorp/tap/terraform
terraform --version
# Windows의 경우
winget install Hashicorp.Terraform
terraform --version

설치 후 주의사항

  • Windows winget 약관 동의: winget 명령어를 처음 사용하면 중간에 ‘소스 약관에 동의하십니까?’ 라는 질문(Y/N)이 뜰 수 있습니다. Y를 입력하고 엔터를 누르면 계속 진행됩니다.

  • 버전 확인 에러 발생 시: 설치가 완료된 직후 aws --version이나 terraform --version을 입력했을 때 *‘명령어를 찾을 수 없다’*고 나온다면, 터미널(또는 PowerShell) 창을 완전히 닫고 새로 열어 다시 입력해 보세요. 환경 변수가 새로고침되어 정상 동작합니다.

  • VSCode 사용 시: 터미널 탭만 닫는 것이 아니라 VSCode 창 전체를 완전히 껐다가 다시 켜야 합니다.


배포 가이드

0. AWS CLI 자격증명 등록

aws configure
# AWS Access Key ID:     <발급받은 Access Key ID>
# AWS Secret Access Key: <발급받은 Secret Access Key>
# Default region name:   ap-northeast-2
# Default output format: json

연결 확인:

aws sts get-caller-identity

1. 변수 설정 (variables.tf)

project3-smart-vault/variables.tf 파일을 열어 아래 두 변수를 본인 값으로 수정합니다.

변수 설명 예시
suffix S3 버킷 이름 뒤에 붙는 고유 식별자. 전 세계에서 유일해야 하므로 이름+날짜 조합 권장 myname-20260613
alert_email 백업·복구 완료 알림을 받을 이메일 주소 your@email.com

2. Terraform 배포

프로젝트 폴더로 이동 후 순서대로 실행합니다.

cd project3-smart-vault
terraform init    # Provider 플러그인 다운로드 (최초 1회)
terraform plan    # 생성될 리소스 미리보기
terraform apply   # 실제 배포, 확인 프롬프트에 'yes' 입력

배포가 완료되면 터미널에 Outputs이 출력됩니다. restore_api_endpoint, restore_api_key_value, dashboard_url 등의 값을 메모해 두면 이후 테스트에 편리합니다.

3. SNS 구독 이메일 확인

variables.tf에 입력한 이메일 주소로 AWS Notification - Subscription Confirmation 메일이 발송됩니다. 메일 내 Confirm subscription 링크를 클릭해야 이후 백업·복구 알림이 정상적으로 수신됩니다. 링크를 클릭하지 않으면 알림이 발송되지 않습니다.

p3-Sub-cofirm-email

p3-Sub-cofirm-success-email

4. 백업 대상 EC2에 태그 추가

Backup Lambda는 backup:true 태그가 붙은 EC2 인스턴스만 스냅샷 대상으로 인식합니다. 아래 두 가지 방법 중 하나를 선택합니다.

방법 A — main.tf에 EC2 리소스 블록 추가 (Terraform 관리)

project3-smart-vault/main.tf에 아래 블록을 추가하고 terraform apply를 재실행합니다.

########################################################
# 테스트용 EC2 인스턴스 (백업 대상)
########################################################

data "aws_vpc" "default" {
  default = true
}

data "aws_subnets" "default" {
  filter {
    name   = "vpc-id"
    values = [data.aws_vpc.default.id]
  }
}

data "aws_ami" "amazon_linux" {
  most_recent = true
  owners      = ["amazon"]
  filter {
    name   = "name"
    values = ["al2023-ami-*-x86_64"]
  }
}

resource "aws_instance" "backup_target" {
  ami           = data.aws_ami.amazon_linux.id
  instance_type = "t3.micro"

  tags = {
    Name        = "smart-vault-test-server"
    backup      = "true"   # ← 이 태그 하나로 백업 대상 자동 등록
    Environment = "test"
    ManagedBy   = "terraform"
  }
}

방법 B — AWS CLI로 기존 인스턴스에 태그 추가

이미 실행 중인 인스턴스에 태그만 추가하려면 아래 CLI 명령어를 사용합니다.

aws ec2 create-tags `
  --resources <instance-id> `
  --tags Key=backup,Value=true

태그가 정상적으로 붙었는지 EC2 콘솔 → 인스턴스 → 태그 탭에서 확인합니다.

p3-ec2-AZ


테스트 시나리오

① Backup Lambda 수동 실행 — 스냅샷 생성 확인

Lambda 콘솔에서 p3-smart-vault-backup 함수를 열고 테스트 버튼을 클릭하여 수동 실행합니다.

테스트 이벤트 페이로드는 빈 JSON ({})을 사용합니다.

실행 후 EC2 콘솔 → Elastic Block Store → 스냅샷으로 이동하여 ManagedBy=smart-vault 태그가 붙은 스냅샷이 생성되었는지 확인합니다.

스냅샷 태그 예시:
  Name:           backup-smart-vault-test-server-20260613-0900
  SourceInstance: <instance-id>
  BackupDate:     2026-06-13
  RetainUntil:    2026-06-20   ← 7일 후 날짜
  ManagedBy:      smart-vault

p3-snapshot-generate-check

p3-snapshot-generate-verification

Backup Lambda 실행 후 등록된 이메일로 백업 완료 리포트가 도착합니다.

p3-backup-success-verification-result-email


② Cleanup Lambda DRY_RUN 테스트

실제 스냅샷을 삭제하기 전에 DRY_RUN 모드로 동작을 먼저 확인하는 것을 권장합니다.

1단계: variables.tf에서 cleanup_dry_run = true로 설정합니다 (기본값).

variable "cleanup_dry_run" {
  default = true   # true: 목록만 출력, false: 실제 삭제
}

2단계: Lambda 콘솔에서 p3-smart-vault-cleanup 함수를 수동 실행합니다.

3단계: CloudWatch Logs (/aws/lambda/p3-smart-vault-cleanup)에서 로그를 확인합니다. DRY_RUN 모드에서는 아래처럼 삭제 예정 목록만 출력되고 실제 삭제는 일어나지 않습니다.

[DRY_RUN] 삭제 예정: snap-xxxxxxxxxxxxxxxxx (RetainUntil: 2026-06-06, 만료 7일 경과)
[DRY_RUN] 총 1개 스냅샷 삭제 예정 — 실제 삭제는 수행하지 않음

4단계: 목록을 확인한 후 실제 삭제를 원하면 cleanup_dry_run = false로 변경하고 terraform apply를 재실행합니다.


③ Restore API 호출 — EBS 볼륨 복구 확인

Step 1 — 복구할 스냅샷 ID 조회

aws ec2 describe-snapshots `
  --owner-ids self `
  --filters "Name=tag:ManagedBy,Values=smart-vault" `
  --region ap-northeast-2 `
  --query "Snapshots[*].{ID:SnapshotId,State:State}" `
  --output table

Step 2 — Restore API에 POST 요청

⚠️ Windows PowerShell에서 curl.exe에 멀티라인 -d 인자를 사용하면 JSON 파싱 오류가 발생합니다. 반드시 Invoke-RestMethod를 사용하세요.

cd project3-smart-vault
$endpoint = (terraform output -raw restore_api_endpoint).Trim()
$apiKey   = (terraform output -raw restore_api_key_value).Trim()

$headers = @{
  "x-api-key" = $apiKey
}

$body = @{
  snapshot_id       = "<snap-id>"
  volume_type       = "gp3"
  availability_zone = "ap-northeast-2a"
} | ConvertTo-Json

Invoke-RestMethod -Method POST `
  -Uri $endpoint `
  -Headers $headers `
  -ContentType "application/json" `
  -Body $body

정상 응답:

{
  "message": "복구 볼륨 생성 완료",
  "volume_id": "vol-xxxxxxxxxxxxxxxxx",
  "source_snapshot_id": "<snap-id>",
  "volume_type": "gp3",
  "availability_zone": "ap-northeast-2a"
}

EC2 콘솔 → Elastic Block Store → 볼륨에서 restored-<snap-id> 이름의 새 볼륨이 생성된 것을 확인합니다. 이 볼륨을 기존 EC2에 연결하면 데이터 복구가 완료됩니다.

p3-restore-log

복구 완료 후 등록 이메일로 알림이 도착합니다.

p3-restore-success-email


④ CloudWatch 대시보드 확인

terraform output dashboard_url

출력된 URL을 브라우저에서 열면 Lambda 실행 횟수, 에러 수, S3 버킷 크기를 한 화면에서 확인할 수 있습니다.


⚠️ 비용 관리

서비스별 프리 티어 범위

서비스 프리 티어 한도 초과 시 단가
Lambda × 3 (Backup/Cleanup/Restore) 월 1,000,000 요청 / 400,000 GB-초 $0.20 / 백만 요청
EventBridge × 3 월 1,000,000 이벤트 $1.00 / 백만 이벤트
S3 Archive (서울) 5GB 스토리지 / 월 20,000 GET, 2,000 PUT $0.023 / GB
S3 DR (싱가포르) 5GB 스토리지 크로스 리전 전송 ~$0.02/GB (로그 파일은 수 KB 수준)
API Gateway 월 1,000,000 호출 (12개월) $1.00 / 백만 호출
SNS 월 1,000건 이메일 $2.00 / 100,000건
CloudWatch 5GB 로그 수집 / 3개 대시보드 $0.50 / GB
EBS 스냅샷 없음 (프리 티어 해당 없음) $0.05 / GB-월 (증분 방식)

EBS 스냅샷은 terraform destroy로 삭제되지 않습니다.

Lambda가 생성한 EBS 스냅샷은 Terraform이 직접 관리하는 리소스가 아니기 때문에 terraform destroy를 실행해도 스냅샷은 남아 있고 비용이 계속 발생합니다. 테스트 종료 후에는 반드시 아래 “인프라 삭제” 절차에 따라 스냅샷을 수동으로 삭제한 뒤 terraform destroy를 실행하세요.

크로스 리전 S3 복제 전송 비용: ~$0.02/GB이지만, 삭제 로그 파일은 수 KB 수준이라 실질적인 추가 비용은 거의 없습니다.


트러블슈팅 노트

1. Restore API 400 오류 — snapshot_id 누락

증상: curl.exe로 POST 요청 시 400 응답이 돌아옴.

원인: PowerShell에서 curl.exe에 멀티라인 -d 인자를 백틱으로 이어 붙이면 리터럴 줄바꿈과 공백이 삽입되어 Lambda가 json.loads()를 실패하고 snapshot_id를 찾지 못합니다.

해결: Invoke-RestMethod를 사용하여 PowerShell 오브젝트를 ConvertTo-Json으로 직렬화한 뒤 전달합니다.

# ❌ 문제가 생기는 방식
curl.exe -X POST $endpoint `
  -H "x-api-key: $apiKey" `
  -d '{
    "snapshot_id": "<snap-id>"
  }'

# ✅ 올바른 방식
$body = @{ snapshot_id = "<snap-id>"; volume_type = "gp3"; availability_zone = "ap-northeast-2a" } | ConvertTo-Json
Invoke-RestMethod -Method POST -Uri $endpoint `
  -Headers @{ "x-api-key" = $apiKey } `
  -ContentType "application/json" `
  -Body $body

2. InvalidSubnetID.NotFound 오류 — 기본 VPC 없음

증상: terraform apply 중 EC2 인스턴스 생성 시 InvalidSubnetID.NotFound 오류 발생.

원인: 일부 AWS 계정은 기본 VPC(Default VPC)가 삭제되어 있어 data "aws_vpc" "default" 조회가 실패합니다.

해결 방법 1 — AWS 콘솔에서 기본 VPC 재생성: VPC 콘솔 → 작업 → 기본 VPC 생성을 클릭하면 기본 VPC와 서브넷이 자동으로 재생성됩니다.

해결 방법 2 — 특정 서브넷 ID 직접 지정: 기본 VPC 재생성 없이 진행하려면, 계정에 존재하는 서브넷 ID를 main.tfaws_instance 리소스에 subnet_id로 직접 지정합니다.


3. Cleanup Lambda가 실제로 삭제하지 않음

증상: Cleanup Lambda를 실행했는데 만료된 스냅샷이 삭제되지 않음.

원인: variables.tfcleanup_dry_run 값이 true로 설정되어 있으면 삭제 목록만 출력하고 실제 삭제는 수행하지 않습니다.

해결: variables.tf에서 cleanup_dry_run = false로 변경하고 terraform apply를 재실행합니다.

# variables.tf
variable "cleanup_dry_run" {
  default = false   # true → 목록만 출력 / false → 실제 삭제
}

인프라 삭제

중요: EBS 스냅샷은 terraform destroy로 자동 삭제되지 않습니다. 먼저 스냅샷을 수동으로 삭제한 뒤 terraform destroy를 실행해야 비용이 완전히 멈춥니다.

1단계 — ManagedBy=smart-vault 스냅샷 조회 및 삭제

# smart-vault가 생성한 스냅샷 목록 확인
aws ec2 describe-snapshots `
  --owner-ids self `
  --filters "Name=tag:ManagedBy,Values=smart-vault" `
  --region ap-northeast-2 `
  --query "Snapshots[*].SnapshotId" `
  --output text

# 조회된 스냅샷 ID를 하나씩 삭제 (예시)
aws ec2 delete-snapshot --snapshot-id <snap-id> --region ap-northeast-2
# 스냅샷이 여러 개인 경우 — 일괄 삭제
$snapIds = (aws ec2 describe-snapshots --owner-ids self `
  --filters "Name=tag:ManagedBy,Values=smart-vault" `
  --region ap-northeast-2 `
  --query "Snapshots[*].SnapshotId" --output text).Split()

foreach ($id in $snapIds) {
    aws ec2 delete-snapshot --snapshot-id $id --region ap-northeast-2
    Write-Host "삭제됨: $id"
}

2단계 — S3 아카이브 버킷 비우기

# S3 아카이브 버킷 비우기
aws s3 rm s3://p3-smart-vault-archive-<suffix> --recursive

3단계 — Terraform으로 인프라 삭제

cd project3-smart-vault
terraform destroy

Lambda 함수 3개, EventBridge 규칙 3개, API Gateway, SNS 토픽, S3 버킷 2개, CloudWatch 대시보드 등 Terraform이 생성한 모든 리소스가 삭제됩니다.


기술 스택

Terraform · AWS Lambda · AWS EventBridge · AWS EBS Snapshot · AWS S3 · AWS API Gateway · AWS SNS · AWS CloudWatch · AWS IAM · Python