🔥 백업과 PITR: 시점 복구의 범위

2259자
26분

처음 RDS 콘솔을 열었을 때 Backups 탭과 Snapshots 탭이 따로 있는 구성이 한참 혼란스러웠다. 같은 데이터를 같은 storage에서 같은 방식으로 떠내는 것 같은데 왜 두 탭으로 나누어 둔 건지 이유를 알 수 없었다. 어느 날 운영하던 인스턴스 하나에서 누가 잘못 친 DELETE FROM users 쿼리를 두 시간 전 시점으로 돌려놔야 했고, 그제야 두 탭이 가리키는 게 같은 백업이 아니라는 사실을 알게 됐다. 콘솔에서 Restore to point in time 버튼을 누르고 시각을 1초 단위로 골랐을 때, 이 시각을 어떻게 1초 단위로 짚을 수 있는지가 다음으로 혼란스러운 대목이었다.

RDS란 무엇인가: 관리형 DB의 의미에서 본 자동화 7갈래 중 하나가 backup이었다. 자동화한다고 해서 운영자가 손을 떼면 안 되는 게 백업의 결과물이다. 백업이 어떻게 떠지고, 어디까지 복구할 수 있고, 무엇은 복구되지 않는지를 모르면 정작 사고가 나는 순간에 손을 쓰지 못한다. 백업과 복구는 RDS의 자동이 끝나는 곳이자 사용자의 책임이 시작되는 곳이다. 자동 백업과 수동 Snapshot이라는 두 백업 모델, 그 위에 올라간 Point-in-Time Recovery의 기제, 그리고 시점 복구로 무엇이 복구되고 무엇이 복구되지 않는지를 따라가자.

cover — 백업과 PITR이라는 두 단어가 가리키는 시간축 위의 한 점

자동 백업과 Snapshot: 같은 단어 다른 두 가지

RDS는 백업을 두 갈래로 나눈다. 자동 백업(Automated Backup)과 수동 Snapshot(Manual DB Snapshot)이다. RDS는 같은 EBS volume에서 같은 메커니즘으로 두 가지를 떠내지만, 라이프사이클과 청구가 다른 별개의 객체로 관리한다.

RDS는 자동 백업을 사용자가 지정한 backup retention period 동안만 보존한다. 이 retention 값은 콘솔에서 default 7일, API·CLI에서 default 1일이고 0~35일 범위라는 사실을 RDS란 무엇인가: 관리형 DB의 의미에서 짚었다. 결정적인 차이는 인스턴스를 삭제할 때 나타난다. AWS 공식 문서가 명시한다.

If you don't choose Retain automated backups when you delete a DB instance, all automated backups are deleted with the DB instance.

인스턴스를 지우면 RDS가 자동 백업도 같이 지운다는 뜻이다. 운영자가 별도로 Retain automated backups 옵션을 명시하지 않는 한.

수동 Snapshot은 사용자가 명시적으로 만든다. AWS 콘솔의 Take snapshot 버튼이나 aws rds create-db-snapshot CLI 명령으로 한 번 떠 두면, AWS는 retention 정책과 무관하게 사용자가 직접 삭제할 때까지 그 Snapshot을 무한히 들고 있는다. 같은 문서가 이어서 명시한다.

Manual snapshots are not deleted when an instance is deleted.

인스턴스가 사라져도 수동 Snapshot은 그곳에 남는다. 한 region당 계정이 가질 수 있는 수동 Snapshot 수에는 100개라는 soft quota가 있고, 이 한도는 quota 증가 신청으로 늘릴 수 있다.

두 백업의 라이프사이클이 다른 결정적인 의미는 감사 요건과 폐기 시점에서 나타난다. 자동 백업은 35일이라는 상한이 있어 그 너머는 보장하지 못한다. 35일을 넘기는 audit 요건(분기 결산이 끝나야만 폐기 가능한 청구 데이터, 1년 보관이 의무인 PCI DSS 트랜잭션 로그 같은 경우)은 수동 Snapshot으로 떠 두거나 AWS Backup의 vault에 넣어야 한다. 자동 백업의 35일은 운영적인 회복용이지 법적인 보존용이 아니다.

청구 모델도 다르다. AWS는 자동 백업에서 DB 크기를 넘는 부분만 GB-월로 청구한다는 사실을 같은 RDS란 무엇인가: 관리형 DB의 의미 글의 청구 5겹에서 정리했다. AWS는 수동 Snapshot에는 첫 GB부터 청구한다. 그래서 100 GB 인스턴스 하나에 자동 백업만 두면 GB-월 청구가 0에서 시작해 데이터가 변경될수록 천천히 늘지만, 같은 인스턴스를 매주 수동 Snapshot으로 떠 두면 한 달이 지나면 AWS가 4개 × 약 100 GB의 storage GB-월을 그대로 청구한다. 서울 region 기준 GB-월 단가가 약 $0.095라면(정확한 시점 단가는 RDS pricing 페이지에서 확인) 4 × 100 × 0.095 = 약 $38이 한 달에 추가로 붙는다.

자동 백업의 메커니즘: daily snapshot과 5분 단위 transaction log

자동 백업이 매일 한 번 RDS가 떠 두는 storage volume snapshot 한 장이라고 알고 있는 운영자가 적지 않다. 실제로는 RDS가 두 부품으로 나눠 둔다. 하루 한 번 RDS가 떠 두는 daily snapshot, 그리고 그 위에 계속 쌓아 두는 transaction log다.

RDS는 daily snapshot을 사용자가 지정한 backup window 동안 한 번 떠 둔다. backup window는 사용자가 시각을 명시하지 않으면 AWS가 region별 8시간 블록 안에서 무작위 30분을 골라 잡는다. 30분이라는 길이는 backup이 그 안에 시작된다는 보장이지 그 안에 끝난다는 보장이 아니다. 큰 인스턴스는 backup이 backup window를 넘겨 진행될 수 있고, 그 동안 인스턴스는 정상 동작한다.

RDS는 Transaction log를 매 5분마다 S3에 업로드한다. AWS 공식 문서가 정확한 문장으로 명시한다.

RDS uploads transaction logs for DB instances to Amazon S3 every five minutes.

이 5분이라는 숫자가 PITR 복구 가능 시각의 정확도와 LatestRestorableTime의 lag을 함께 결정한다. 운영적으로 짚자. 지금이 14:03이라면 마지막 transaction log 업로드는 14:00에 일어났을 가능성이 높고, LatestRestorableTime은 14:00 근처를 가리킨다. 14:01에 일어난 사고를 14:01 시점으로 복구하려면 14:05 이후 다음 log 업로드가 끝나기를 기다려야 한다.

이 두 부품의 협업이 시점 복구를 가능하게 만드는 기제다. Daily snapshot은 storage 전체의 baseline을 잡고, transaction log는 그 baseline 이후 일어난 모든 변경을 시간 순서로 기록한다. 복구 요청이 들어오면 RDS는 가장 가까운 daily snapshot을 새 EBS volume에 복원하고, 그 위에 요청 시각까지의 transaction log를 차례로 replay한다. 이 단계가 끝나면 새 DB instance가 그 시점의 상태로 살아난다.

이 흐름에서 사용자에게 잘 안 띄는 비용이 한 갈래 있다. 5분 간격의 transaction log shipping은 인스턴스의 I/O를 사용한다. write가 무거운 워크로드에서는 backup window와 transaction log shipping이 함께 IOPS budget을 갉아먹어 application latency가 평소보다 길어지는 시간대가 생긴다. backup window를 트래픽이 한산한 시각으로 옮기는 운영적인 결정이 의미를 가지는 까닭이 이 IOPS 경합 때문이다.

자동 백업의 두 부품 — 매일 한 번의 snapshot baseline 위에 5분 간격으로 쌓이는 transaction log가 시점 복구의 입력이 된다

Point-in-Time Recovery: 시점 복구가 만들어지는 순간

PITR을 호출하는 명령은 짧다.

bash
aws rds restore-db-instance-to-point-in-time \
    --source-db-instance-identifier mydb-prod \
    --target-db-instance-identifier mydb-restored-1402 \
    --restore-time 2026-05-01T14:02:00.000Z
bash
aws rds restore-db-instance-to-point-in-time \
    --source-db-instance-identifier mydb-prod \
    --target-db-instance-identifier mydb-restored-1402 \
    --restore-time 2026-05-01T14:02:00.000Z

이 명령이 실제로 만드는 결과물에 운영자가 한 번씩 놀라는 대목이 있다. PITR은 원본 인스턴스를 돌려놓지 않는다. 새 DB instance를 별도로 만든다. AWS 공식 문서가 정확히 그렇게 적는다.

You can restore a DB instance to a specific point in time, creating a new DB instance without modifying the source DB instance.

명령 인자에 --target-db-instance-identifier가 강제되는 까닭이 새 인스턴스를 별도로 만든다는 이 동작 때문이다. 새 endpoint가 새 호스트네임으로 발급되고, application connection string은 운영자가 직접 갈아 끼워야 한다.

그래서 사고 직후의 대응은 두 단계로 나뉜다. 1단계로 사고 시점 직전(예를 들어 사고가 14:02:30에 일어났다면 14:02:00)으로 PITR을 돌려 새 인스턴스를 복원한다. 2단계로 그 새 인스턴스에서 정확히 무엇이 손상됐는지 확인하고, 필요한 row만 추출해 원본 인스턴스에 다시 적용하거나 application의 endpoint를 새 인스턴스로 잘라 붙인다. PITR은 자동으로 사고를 되돌리는 도구가 아니라, 사고 직전 상태의 복제본을 만들어 비교와 추출을 가능하게 해 주는 도구다.

복구 시각의 정확도는 transaction log의 5분 업로드 주기에 매여 있다. 다만 SQL Server에 한해서는 같은 인스턴스 안에서 RDS가 각 데이터베이스를 1초 이내 일관성으로 복구한다는 별도의 보장이 있다.

each database within that instance is restored to a point in time within 1 second of each other database within the instance.

다른 엔진의 복구 시각 정확도는 5분 한도 안에서 1초 단위 명시가 가능하지만, RDS는 실제 도달 가능한 시점을 가장 가까운 transaction log 단위 시각으로 맞춘다.

LatestRestorableTime은 이 모든 메커니즘이 합쳐진 운영용 지표다.

bash
aws rds describe-db-instances \
    --query "DBInstances[].{Id:DBInstanceIdentifier,LatestRestorableTime:LatestRestorableTime}"
bash
aws rds describe-db-instances \
    --query "DBInstances[].{Id:DBInstanceIdentifier,LatestRestorableTime:LatestRestorableTime}"

이 값을 모니터링 대시보드에 띄워 두면, 사고가 났을 때 몇 시 몇 분까지 복구 가능한가를 즉시 알 수 있다. 보통 현재 시각보다 5분 정도 뒤를 가리키는 게 정상이고, 이 값이 10분, 30분 뒤로 밀리기 시작하면 RDS가 transaction log shipping을 멈췄다는 신호다. backup retention을 0으로 잘못 잡아 두었거나, 인스턴스가 storage full에 다가갔거나, IAM이 S3 업로드 권한을 잃었거나 하는 곳에서 이 신호가 나타난다.

PITR 복구 메커니즘 — 가장 가까운 daily snapshot에 transaction log를 차례로 replay해 새 DB 인스턴스를 만든다

시점 복구의 범위: 무엇이 복구되고 무엇이 복구되지 않는가

PITR이 시점을 복구한다고 할 때 그 시점에 RDS가 복원하는 것은 DB의 데이터다. 인스턴스를 둘러싼 운영 설정(parameter group, option group, security group, IAM 인증, 콘솔 tag, custom endpoint)의 다수는 자동으로 따라오지 않거나 RDS가 default 값으로 새로 잡는다.

Parameter group과 option group은 default 값을 받는다. AWS 공식 문서가 그렇게 적는다.

Restored DB instances are automatically associated with the default DB parameter and option groups. However, you can apply a custom parameter group and option group by specifying them during a restore.

운영자가 --db-parameter-group-name--option-group-name을 명시하지 않으면 RDS는 복원한 인스턴스에 default parameter group을 적용한다. 운영 중에 max_connectionsslow_query_log_file 같은 값을 custom parameter group에서 조정해 두었다면, PITR로 복원한 인스턴스에서 그 설정이 자동으로 따라오지 않는다.

Security group 역시 마찬가지다. 같은 문서가 적는다.

When you restore a DB instance to a point in time, you can choose the default virtual private cloud (VPC) security group. Or you can apply a custom VPC security group to your DB instance.

명시하지 않으면 default VPC security group이 붙고, 그 default가 inbound 3306 또는 5432를 허용하지 않는다면 복원된 인스턴스에 application이 연결되지 않는다.

Tag는 절반만 따라온다.

If tags are not provided in the request and if the source DB instance is in-region active and has tags, RDS adds the latest tags from the source DB instance to the restored DB instance.

같은 region 안에서 active 상태인 source의 tag는 자동으로 옮겨지지만, 인스턴스가 이미 사라진 상태에서 자동 백업으로부터 복구하는 경우(즉 Retain automated backups로 살려 둔 자동 백업으로 복원하는 경우) tag는 따라오지 않는다.

암호화(KMS key)는 region scope 안에 머문다. Snapshot을 다른 region으로 copy하면 destination region의 KMS key를 새로 지정해야 한다. AWS 공식 문서가 두 곳에서 같은 문장으로 명시한다.

KMS keys are specific to the AWS Region that they are created in, and you can't use encryption keys from one AWS Region in another AWS Region.

DR 절차서에 destination region용 KMS key alias가 미리 준비돼 있지 않으면 cross-region snapshot copy가 시작도 되지 않는다.

자동 복구가 안 되는 항목들의 일관된 구성을 보면 기준이 또렷하다. DB의 데이터와 schema는 PITR이 맡고, 그 외 인스턴스를 운영하는 설정은 사용자의 IaC나 운영 절차서가 맡는다. Terraform이나 CloudFormation으로 인스턴스 설정을 관리하고 있다면 복구 절차에서 그 코드를 다시 적용하기만 하면 되고, 콘솔에서 손으로 조정한 설정은 복구 시점에 따라오지 않는다는 점을 받아들여야 한다.

PITR이 자동으로 복원하는 것과 복원하지 않는 것 — 데이터·schema는 따라오고 parameter/security/KMS는 별도 지정

복구 시나리오: PITR이 답인 곳과 답이 아닌 곳

복구 시나리오를 네 갈래로 두면 PITR의 적용 범위를 또렷이 짚을 수 있다.

첫째는 accidental data 손상이다. 운영자가 잘못 친 DELETE FROM users WHERE created_at < '2026-01-01' 같은 쿼리, 또는 application 버그가 row를 잘못 update한 사고가 여기에 속한다. 사고 시각이 retention window 안에 있고, 사고 시점을 1분 단위로 짚을 수 있다면 PITR이 정확히 답이 되는 경우다. backup retention 35일 안에서, 사고 직전 시점으로 새 인스턴스를 만들어 손상된 row만 추출해 원본에 다시 적용한다.

둘째는 region 단위 장애다. 한 region 전체가 장애가 나면 같은 region의 자동 백업과 수동 Snapshot도 함께 접근 불가가 된다. 이 상황은 PITR이 답이 아니라 cross-region snapshot copy가 답이다. snapshot을 다른 region에 미리 복제해 두는 운영(aws rds copy-db-snapshot --source-region ap-northeast-2 --target-region ap-northeast-1 같은 정기 실행)이 region 장애에 대한 보호가 된다. cross-region copy는 destination region당 계정 20개 동시 in-progress 요청 한도가 있고, copy 자체에 AWS는 cross-region 데이터 전송 비용을 GB당 청구한다(정확한 GB당 단가는 RDS pricing 페이지의 region 페어 표 기준). 100 GB DB 한 개의 snapshot을 매주 cross-region 복사하면 GB당 단가가 $0.02 수준일 때 한 달에 약 4 × 100 × 0.02 = 약 $8의 cross-region DTO가 더 붙는다(스토리지 보관 비용은 별도).

셋째는 KMS key 손실이다. KMS key가 의도치 않게 disable되거나 schedule for deletion으로 들어간 경우다. 암호화된 자동 백업과 Snapshot은 그 KMS key 없이는 복호화할 수 없고, key가 영구 삭제되면 복구가 불가능하다. PITR은 이 경우에 답이 되지 않는다. KMS key의 lifecycle 관리(disable 전 의무 대기 기간, deletion 30일 grace, multi-region key 활용)와 별도로 정책을 두어야 한다.

넷째는 application 단의 logical 손상이다. application 코드가 며칠 동안 잘못된 schema migration을 적용하면서 데이터가 천천히 망가진 경우, PITR이 시점을 정확히 짚지 못한다. 언제부터 망가졌는지가 분 단위로 명확하지 않고, 복구한다 해도 그 사이의 정상 데이터까지 잃을 수 있다. 이 종류의 사고는 PITR이 아니라 application 단의 audit log와 정정 절차로 풀어야 한다.

35일이라는 retention 상한도 PITR의 답이 아닌 영역을 추가로 만든다. SOC 2나 PCI DSS의 1년 트랜잭션 보존 요건은 자동 백업으로 충당할 수 없고, 별도로 수동 Snapshot을 정기적으로 만들거나 AWS Backup의 vault에 넘겨야 한다. AWS Backup은 RDS를 비롯해 EBS·EFS·DynamoDB 등 여러 서비스의 백업 정책을 한 곳에서 관리하고 cross-account · cross-region 보관 규칙을 일관되게 적용하는 backup orchestration 서비스로, AWS의 공식 launch post 기준 2019년 1월에 GA되어 RDS 백업 통합을 함께 출시했다. 단일 RDS 인스턴스 한 대를 운영하는 사이드 프로젝트에는 과한 도구지만, 여러 region·여러 계정의 RDS를 함께 운영하는 환경에서는 RDS-native 백업 위에 한 단을 더 두는 게 맞다.

복구 시나리오 4갈래 — accidental 손상은 PITR, region 장애는 cross-region copy, KMS 손실은 별도 정책, logical 손상은 application 단으로 답이 갈린다

백업 storage의 숨은 비용: 변경량이 청구를 만든다

AWS가 자동 백업을 DB 크기 초과분만 GB-월로 청구한다는 사실은 RDS란 무엇인가: 관리형 DB의 의미에서 정리했다. 그 한 줄 안에 운영적으로 한 번씩 놀라는 부분이 있다. 청구되는 초과분은 데이터의 변경량과 비례한다.

RDS는 자동 백업을 daily snapshot의 incremental 방식으로 보존한다. 데이터가 정적이면 snapshot 간 변경된 block이 작아 storage 추가 청구가 0에 가깝다. 같은 100 GB 인스턴스에서 매일 50 GB의 row를 update하는 워크로드라면(35일 retention 기준) 자동 백업 storage가 35 × 50 = 1,750 GB 가까이 누적되고, GB-월 청구가 약 1,650 GB(DB 100 GB 초과분) × $0.095 = 약 $157이 한 달에 추가로 붙는다. 같은 인스턴스에서 매일 5 GB만 변경되면 추가 청구는 약 $14다. 변경량의 11배 차이가 청구의 약 11배 차이로 그대로 따라온다.

수동 Snapshot의 청구는 더 단순하지만 누적량이 크다. 한 번 떠 둔 Snapshot은 그 시점의 전체 데이터를 그대로 보관한다(같은 region에서는 RDS가 incremental 최적화를 적용하지만 AWS는 첫 GB부터 청구한다는 점은 같다). 매주 수동 Snapshot을 자동화로 만들어 두는 운영은 retention 정책을 같이 두지 않으면 한 단씩 그대로 늘어난다. 오래된 Snapshot 자동 삭제 기능은 RDS-native에 없고, AWS Backup의 lifecycle policy나 EventBridge schedule + Lambda로 직접 만들어야 한다.

운영적인 결론은 둘로 나뉜다. 자동 백업의 retention은 복구 가능 시간과 백업 storage 청구를 함께 잡는 라디오 그룹이고, 수동 Snapshot은 법적 보존과 DR 분기점을 위한 별도의 도구다. 두 백업의 청구를 함께 추적하지 않으면 한 달 청구서에서 RDS 라인의 Backup Storage가 인스턴스 시간보다 큰 항목으로 어느 순간 튀어나온다.

백업 storage 청구 그래프 — 변경량이 누적되는 자동 백업과 시점별 전체 보존인 수동 Snapshot의 비용 비교

다음으로

백업이라는 한 단을 마무리하면 RDS의 운영 라디오 그룹 중 한 갈래를 정리한 셈이다. 가용성은 Multi-AZ와 장애 조치: 가용성의 기제에서, 읽기 부하 분산은 Read Replica: 읽기 부하 분산에서, 복구는 여기서 짚었다. 다음으로 인스턴스의 동작을 결정하는 parameter group과 option group이라는 추상이 어떻게 인스턴스의 동작을 바꾸는지를 짚는다. 여기서 PITR이 default parameter group을 받는다는 사실을 짚었는데, 그 default가 정확히 무엇인지, 그리고 custom parameter group이 인스턴스의 어떤 동작을 바꿀 수 있는지가 다음 단계의 답이다.

YouTube 영상

채널 보기
트라이(Trie)에서 단어를 삭제하는 방법 | Trie 자료구조 이야기
우리가 매일 쓰는 맞춤법 검사기와 라우터 속에 숨겨진 알고리즘은? | Trie 자료구조 이야기
벡터의 정의와 덧셈 연산 | 선형대수학
Trie 자료구조 파이썬 구현: Search와 Starts With 연산 | Trie 자료구조 이야기
트라이(Trie)를 이용한 자동 완성 알고리즘 | Trie 자료구조 이야기
투영과 예측, 그리고 선형 결합 | 선형대수학
마지막편, 트라이 노드를 50% 이상 줄이는 방법? 압축 트라이 성능 분석 | Trie 자료구조 이야기
내적의 기하학적 의미와 코사인 유사도 원리 | 선형대수학