You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
결정이 필요한 것 5가지를 2장에 정리했고, 3장은 그에 대한 제 제안입니다. 특히 3.4 의 대응 체계는 팀 합의가 필요한 부분이라 의견 주시면 좋겠습니다.
1. 논의 배경
#66 백업 파이프라인이 prod 에 적용되어 dump 와 binlog 가 S3 에 쌓이기 시작했습니다. 이제 실제로 복구할 수 있는지를 절차로 확정해야 합니다.
절차를 쓰다 보니 범위가 계속 넓어져서, 백업 체계를 만든 이유를 다시 확인했습니다.
데이터 EBS 를 분리했기 때문에 EC2 장애는 기존 EBS 를 새 EC2 에 연결해 복구할 수 있지만, 잘못된 DELETE/UPDATE, 스키마 변경과 같은 데이터 장애는 별도 백업 없이는 복구할 수 없습니다. — #64
잘못된 쿼리가 발생한 경우, EBS 스냅샷은 스냅샷 생성 시점까지만 돌아갈 수 있지만 binlog 는 문제가 된 쿼리 직전까지 복구할 수 있습니다. — Discussion #65, 3안 선택 근거
즉 이 백업 체계의 목적은 "데이터의 논리적 손상을 특정 시점으로 되돌리는 것" 하나입니다. 잘못된 DELETE/UPDATE, 배포·마이그레이션 사고처럼 볼륨과 파일시스템은 정상인데 데이터 내용이 잘못된 경우를 말합니다.
그래서 이 논의도 거기에 맞춰 좁혔습니다. EBS 손상이나 인스턴스 장애는 다른 수단이 담당하므로 범위에서 뺐습니다(3.6 참고).
지금 확보된 것
항목
값
dump
매일 03:00 KST, 압축 647KB (원본 4.8MB / 35테이블), 약 18초
binlog
5분 주기, 회전부터 S3 업로드 완료까지 약 5초
보관
S3 14일 / 호스트 로컬 binlog 30일
복구 기준점
dump manifest 에 sourceLogFile + sourceLogPosition 기록
복구 도구
DB EC2 에 mysql-recovery-tools:8.4.8 적재 (private subnet 대비)
데이터 복원 자체는 매우 빠릅니다. dump 복원 수십 초, binlog 재생 수 초 수준입니다(하루 증가량 59KB). 즉 복구 시간의 대부분은 판단과 검증입니다. 절차서와 리허설이 실제 관건이라고 봅니다.
2. 논의 사안
다음 다섯 가지를 정하고 싶습니다.
#
결정할 것
선택지
1
복구 시 어느 binlog 를 쓸지
로컬(30일) 우선 / S3(14일) 정본 → RPO 가 갈립니다
2
binlog 재생 범위
문제 쿼리만 제외 / 문제 시점에서 절단
3
긴급 차단(쓰기 중단) 권한
1명 즉시 / 2명 합의
4
검증할 "주요 테이블" 목록
도메인 기준으로 확정
5
리허설 환경·데이터·주기
stage dump + 8.4.8 컨테이너 / 분기 1회
준비가 필요한 항목: -replace 전용 워크플로우입니다. 인스턴스 교체를 승인 절차로 실행할 경로가 아직 없어서, 리허설(인스턴스 장애 경로)의 전제가 됩니다. 언제 만들지 함께 정하면 좋겠습니다.
2.1 어느 binlog 를 쓸지 — RPO 가 갈립니다
백업 스크립트는 닫힌 binlog 만 업로드합니다. 5분마다 회전시키고 방금 닫힌 파일을 올리므로, 진행 중인 활성 binlog 는 S3 에 없습니다. 반면 로컬에는 sync_binlog=1 로 커밋 시점에 기록된 최신 상태까지 있습니다.
이 차이가 RPO 를 가릅니다.
14:00 binlog 회전 → S3 업로드 ← S3 에 있는 것은 여기까지
14:03 잘못된 DELETE 실행
14:10 발견
목표가 "14:03 직전"일 때,
경로
재생 가능 범위
결과
S3 만
14:00 까지
14:00~14:03 의 정상 트랜잭션 최대 5분치 유실
로컬 사용
14:10 까지 (활성 binlog 포함)
14:03 직전까지 정확히 재생 → 유실 0
dump 의 기준점 position 부터 로컬 binlog 를 순차 적용하면 커밋된 모든 트랜잭션을 복원할 수 있습니다.
dump manifest: sourceLogFile = binlog.000042, sourceLogPosition = 12345
↓ dump 복원 = 그 position 시점의 일관 스냅샷
binlog.000042(12345부터) → 000043 → ... → 활성 파일
↓
커밋 기준 RPO 0
보관 기간도 이 구조를 뒷받침합니다. 로컬 binlog(30일)가 S3 dump(14일)보다 길어서, 어떤 dump 를 골라도 그 기준점부터의 로컬 binlog 가 남아 있습니다.
안
장점
단점
A. 로컬 binlog 우선, S3 는 백업 경로
커밋 기준 RPO 0
볼륨의 파일을 신뢰해야 함
B. 항상 S3 정본
볼륨 상태와 무관하게 절차가 하나
재생 범위가 마지막 업로드까지로 제한 → 최대 5분 유실
A안을 제안합니다. 논리적 손상은 대체로 "방금" 발생하므로 목표 시점이 마지막 업로드 이후일 가능성이 높고, 그럴 때 S3 만으로는 그 구간을 재생할 수 없습니다.
A안의 단점은 검증으로 줄일 수 있습니다. binlog manifest 에 sha256 이 있으니 S3 와 겹치는 구간의 로컬 파일을 대조하면 되고, 그 구간이 전부 일치하면 아직 업로드되지 않은 활성 binlog 도 신뢰할 근거가 됩니다. 애초에 이 문서가 다루는 상황은 정의상 볼륨과 파일시스템이 정상인 경우입니다.
그래서 RPO 는 두 층입니다
상황
RPO
논리적 데이터 손상 (볼륨 정상)
커밋 기준 0 — 로컬 binlog 사용
볼륨을 쓸 수 없는 경우
최대 5분 — S3 binlog 만
즉 RPO 5분은 최악의 경우 보장치이고, 이 문서가 다루는 시나리오에서는 0 에 가깝습니다. 다만 아래쪽 5분은 백업이 정상 동작하고 있다는 전제입니다(3.6 참고).
다만 RPO 0 은 재생할 데이터가 다 손에 있다는 뜻이고, 그 범위 안에서 어디를 잘라낼지는 다음 사안입니다.
그럼 S3 백업은 왜 필요한가
로컬 binlog 를 1차 경로로 쓰더라도 S3 백업 없이는 복구가 성립하지 않습니다. dump 는 업로드 후 로컬에서 삭제되므로(rm -rf "$JOB_DIR"), 재생을 시작할 기준점이 S3 에만 있습니다.
S3 의 역할
내용
dump = 재생 기준점
로컬에 남지 않습니다. 이것만으로도 필수
binlog 이중화
볼륨을 못 쓰게 되면 로컬 binlog 도 함께 사라집니다
Object Lock (GOVERNANCE 14일)
로컬 binlog 는 지우면 없어지지만 S3 는 지울 수 없습니다. 운영 실수·침해 대비
리전 단위 복제
AZ 장애 시에도 남습니다
특히 Object Lock 이 중요합니다. 논리적 손상은 사람 실수로 생기는데 그 사람이 binlog 도 지울 수 있습니다. 로컬만 믿으면 백업이 아닙니다.
2.2 binlog 재생 범위
문제 쿼리가 실행된 뒤에도 정상 트랜잭션이 쌓였을 수 있습니다.
문제 쿼리만 제외하고 이후는 살린다 — 데이터를 더 살리지만 binlog 를 구간별로 나눠 재생해야 하고 정합성 판단이 어렵습니다
문제 시점에서 잘라낸다 — 단순하지만 그 이후 정상 작업이 유실됩니다
2.3 긴급 차단 권한
복구 개시는 최소 2명 합의가 어떨지 3.4 에 적었습니다. 여기서 따로 여쭤보고 싶은 건, 손상 확산을 막는 조치까지 그 합의를 기다려야 하는가입니다.
잘못된 UPDATE 가 계속 돌고 있으면 시간이 지날수록 복구 범위가 넓어집니다. 쓰기 차단과 백업 타이머 정지는 되돌릴 수 있는 조치라서, 저는 1명 판단으로 즉시 하는 편이 낫다고 봅니다.
다만 쓰기 차단도 서비스 영향이 있어 혼자 판단하기 부담스러울 수 있습니다. 의견 부탁드립니다.
2.4 검증할 주요 테이블
복구 후 무엇을 확인해야 복구가 성공했다고 볼지 기준이 필요합니다. 도메인 기준으로 목록을 만들어두면 복구할 때마다 판단하지 않아도 됩니다.
2.5 리허설 환경과 주기
prod dump 를 쓰면 개인정보를 복사해 두는 셈입니다. 두 환경을 비교해보니 대안이 있었습니다.
항목
prod
stage
MySQL
8.4.8
8.0.45
테이블 / 크기
35개 / 4.8MB
36개 / 4.7MB
log_bin / binlog_format / gtid_mode
1 / ROW / OFF
동일
규모와 binlog 설정이 사실상 같고 버전만 다릅니다. 그래서 prod 와 같은 mysql:8.4.8 컨테이너를 띄우고 stage dump 를 복원하는 조합을 제안합니다. 개인정보 문제가 없고, 규모가 같아 시간 측정이 유효하며, 환경은 prod 와 같은 버전입니다.
주기는 Discussion #65 에서 "최소 분기 1회, 가능하면 월 1회"가 제안됐는데, 현재 인력과 규모를 고려하면 분기 1회가 현실적이라고 봅니다.
3. 제안 정리
아래는 제가 백업 체계를 만들면서 생각한 방향입니다. 확정된 절차가 아니라 초안이고, 특히 3.4 의 대응 체계는 제 판단만으로 정할 수 없는 부분이라 의견이 필요합니다.
이 판단을 절차 맨 앞에 둔 이유는, 백업이 필요 없는 상황에서 백업 복구를 시작하면 오히려 데이터를 잃기 때문입니다. 볼륨이 정상인데 dump 를 복원하면 그 이후 데이터가 사라집니다. 급할 때 가장 저지르기 쉬운 실수라고 생각해서 앞에 뒀습니다.
3.2 복구 절차는 이렇게 생각했습니다
1. 쓰기 차단, 백업 타이머 정지
2. 복구 전 상태 보존 (현재 dump + 일회성 EBS 스냅샷)
3. 문제 시점 특정
4. 그 시점 이전의 최신 dump 선택 — **S3 에서** (manifest 있는 prefix)
5. 체크섬 검증, binlog 번호 연속성 확인, **S3 와 겹치는 구간의 로컬 binlog sha256 대조**
6. 별도 MySQL 컨테이너에 dump 복원
7. manifest 기준점부터 문제 시점 직전까지 binlog 재생 — **로컬 binlog 사용** (2.1)
8. 복원 결과 검증
9. 검증한 데이터를 운영에 반영
10. 백업 타이머 재개 + 즉시 dump 1회
11. 쓰기 재개
이 순서를 만들 때 지킨 원칙 세 가지
① 운영 DB 를 바로 덮지 않았으면 합니다 (6번)
별도 컨테이너에 복원해 검증한 뒤 반영하면, 복구가 실패해도 현재 상태를 잃지 않습니다. 데이터가 4.8MB 라 임시 복원 비용이 거의 없습니다.
② 손대기 전에 현재 상태를 남기고 싶습니다 (2번)
손상된 상태라도 복구 판단이 틀렸을 때 돌아올 지점이 필요합니다. 일회성 EBS 스냅샷은 Discussion #65 의 "상시 스냅샷 미사용" 결정과 충돌하지 않는다고 봅니다 — 그때 문제는 정기 스냅샷의 누적 고정비였고, 여기서는 장애 대응 시 한 번이니까요. 검증 후 삭제하면 됩니다.
③ 복구 중에는 백업 타이머를 멈춰야 한다고 봅니다 (1번, 10번)
멈추지 않으면 손상된 상태가 dump 로 올라가 나중에 정상 백업으로 착각할 수 있습니다. 알림도 반복되고, 복구 작업과 binlog 작업이 같은 볼륨에서 충돌합니다. 재개 시 Persistent=true 가 놓친 발화를 따라잡고, 복구된 상태를 새 기준점으로 만들기 위해 dump 를 즉시 한 번 생성합니다.
제가 가장 위험하게 보는 단계는 9번입니다
방식
적용 상황
위험
테이블 단위 반영
영향이 특정 테이블에 국한
낮음
스키마 전체 교체
광범위 손상
높음 — 사고 이후 정상 데이터가 사라짐
가능하면 테이블 단위로 반영하는 쪽을 제안합니다. 잘못된 DELETE 는 삭제된 행만 다시 넣고, 잘못된 UPDATE 는 영향받은 행만 되돌립니다. 그러면 사고 이후의 정상 작업이 살아남습니다. 데이터가 4.8MB 규모라 복구된 상태와 현재 상태를 나란히 비교하는 비용이 크지 않습니다.
시점 특정과 binlog 재생 주의점
목표 시각을 정하는 것이 절차에서 가장 애매합니다.
애플리케이션 로그나 배포 시각으로 구간을 좁힙니다
mysqlbinlog 로 해당 구간을 사람이 읽을 수 있는 형태로 덤프해 문제 쿼리의 position 을 찾습니다
--stop-position 으로 그 직전까지 재생합니다
재생은 파일로 한 번 떨어뜨린 뒤 적용하는 방식을 제안합니다.
mysqlbinlog --start-position=<manifest 의 position> --stop-position=<문제 직전> \
binlog.000003 binlog.000004 > /mnt/mysql-data/recovery.sql
# 내용 확인 후
mysql -D solid_connection < /mnt/mysql-data/recovery.sql
mysqlbinlog | mysql 로 바로 흘리면 무엇이 적용되는지 보지 못한 채 실행됩니다.
위치 옵션의 적용 범위에 주의가 필요합니다.
-j, --start-position=# Applies to the first binlog passed on the command line.
--stop-position=# Applies to the last binlog passed on the command line.
여러 파일을 나열하면 첫 파일과 마지막 파일에만 적용되고 중간 파일은 전체가 재생됩니다.
같은 인스턴스의 다른 스키마에 복원하는 방식은 쓰지 않는 게 좋겠습니다.mysqlbinlog --rewrite-db 의 설명이 "Rewrite the row event" 인데, binlog_format=ROW 여도 DDL 은 statement 로 기록됩니다. 즉 재생 구간에 ALTER TABLE 이 있으면 원래 스키마(운영 데이터)에 적용됩니다. 스키마 사고 복구에서 특히 위험합니다.
manifest.json 이 있는 prefix 만 완결된 백업입니다. dump → 체크섬 → manifest 순으로 올리므로 manifest 가 없으면 미완성입니다
날짜 prefix 는 UTC 기준입니다. 8월 26일 03:00 KST 백업은 dump/2026/08/25/ 에 있습니다
키에 server_uuid 가 들어가는 이유는 데이터를 재생성하면 binlog 번호가 000001 로 리셋되기 때문입니다. 복구 시 manifest 의 serverUuid 와 일치하는 것만 골라야 합니다
3.3 복구 후 처리도 함께 정해야 한다고 봅니다
DB 만 되돌리면 끝이 아닙니다. 여기서 놓치면 복구는 성공했는데 서비스가 이상해집니다.
대상
처리
Redis 조회 캐시
비워야 합니다 — 과거로 되돌린 DB 에 미래 데이터를 반환
로그아웃 토큰 블랙리스트
비우면 안 됩니다 — 지우면 로그아웃된 토큰이 다시 유효
flyway_schema_history
복구 시점으로 돌아갑니다. 기동 전에 버전을 대조해야 합니다
그래서 Redis 전체 비우기는 하지 말아야 한다고 봅니다. 캐시를 비우려다 블랙리스트까지 지우면 로그아웃 처리가 무효화되는 보안 문제가 됩니다. 급할 때 가장 손이 갈 만한 명령이라 미리 못 박아두고 싶습니다.
Flyway 는 복구 시점이 최신 마이그레이션 이전이면 애플리케이션 기동 시 누락분을 다시 적용합니다. 스키마는 맞춰지지만 데이터 변환이 포함된 마이그레이션은 재실행되므로 예상 못한 변경으로 보일 수 있습니다. 현재 마이그레이션은 58개입니다.
검증 항목
구분
확인
데이터
주요 테이블 row count, 최신 레코드 생성 시각, 문제 데이터의 실제 복구 여부
스키마
flyway_schema_history 최신 버전이 예상과 일치
Redis
캐시 비움 확인, 블랙리스트 잔존 확인
애플리케이션
/actuator/health UP, 주요 조회 API 가 복구된 데이터와 일치
백업
타이머 재개, 즉시 생성한 dump 의 manifest 존재
중단·롤백 기준
중단: dump 체크섬 불일치, binlog 재생 중 오류, 검증에서 데이터 불일치
롤백: 별도 컨테이너에 복원했다면 운영 데이터는 그대로이므로 되돌릴 것이 없습니다. 제가 항상 임시 복원 후 반영 방식을 제안하는 이유입니다
보존: 손상된 상태의 dump·스냅샷·임시 환경은 검증이 끝날 때까지 남겨두면 좋겠습니다
3.4 대응 체계는 이렇게 하면 어떨까 합니다
이 절은 제 제안이 가장 약한 부분입니다. 절차는 코드로 검증할 수 있지만 이건 팀이 합의해야 성립하는 내용이라, 아래는 출발점으로만 봐주시면 좋겠습니다.
복구 개시에는 최소 2명의 합의가 있으면 좋겠습니다. 문제를 발견한 서버 개발자와 다른 1명입니다. 서버 리드가 암묵적으로 있지만 그 한 사람이 매번 대응할 수는 없다고 생각해서, 특정 개인에게 의존하지 않는 기준을 두고 싶었습니다
두 번째 사람의 역할은 단순 동의가 아니라 교차 확인이라고 생각합니다. 목표 시점, 적용할 SQL 의 범위, 운영 반영 방식을 함께 확인하는 것까지를 뜻합니다
새벽에 발견한 경우에는 즉시 사람을 모으지 않아도 된다고 봅니다. 상황을 파악한 사람이 오전 10시 전까지 원인을 파악하고, 파악한 내용과 함께 팀에 알린 뒤 대처하는 정도면 충분하지 않을까 합니다
복구 중에 뜨는 백업 실패 알림은 무시해도 된다고 봅니다. 대신 작업 시작을 팀에 공유해 혼선을 줄이면 좋겠습니다
특히 두 가지를 여쭤보고 싶습니다. 2명이 적절한 인원인지, 그리고 새벽 사고에서 오전 10시라는 기준이 너무 늦지 않은지입니다.
3.5 리허설은 한 번 해봐야 한다고 생각합니다
절차서만 있고 해보지 않으면 실제로 막히는 지점을 알 수 없습니다. 환경과 데이터는 2.5 에 적었습니다.
측정할 것
각 단계 소요 시간 → RTO 목표값의 근거
절차서에서 막히거나 애매했던 지점
새 AMI 로 기동한 인스턴스의 정상 동작 여부 (코드 AMI 는 아직 미검증)
host key 갱신 후 백업 재설치 성공 여부
AMI·인스턴스 타입 변경, 백업 스크립트 로직 변경, MySQL 버전 상향 시에는 주기와 무관하게 다시 검증해야 한다고 생각합니다. 이 측정값이 나와야 RTO 목표를 숫자로 말할 수 있다고 봅니다.
3.6 이 논의에서 다루지 않는 것
항목
어디서
백업 중단 감지
#74 외부 freshness 모니터링. 주로 RTO 문제라고 봅니다 — 백업이 멈춘 것을 사고가 난 뒤에 알게 되면, 그것을 발견하고 우회하는 시간이 복구 시간에 그대로 붙습니다. dump 가 30일 넘게 멈추면 로컬 재생의 기준점이 없어져 복구 자체가 불가능해집니다. 지금은 감지 수단이 없습니다
RTO 목표값 확정
내부 논의. 리허설 측정 후 정하는 것이 순서라고 봅니다. 위 freshness 가 없으면 발생 → 발견 구간을 한정할 수 없어, 목표는 "발견부터" 로 두는 것을 제안합니다
EBS 볼륨 손상
AWS 관리 영역이고 확률이 낮습니다. 아래 참고
인스턴스·시스템 장애
볼륨 재연결로 해결됩니다. 아래 참고
데이터 유실 시 사용자 공지
채널(Instagram / 웹 공지)과 방식에 기획·프론트 작업이 필요해 백엔드 단독으로 정할 수 없습니다. 필요성만 확인하고 팀 전체 논의로 넘기는 것을 제안합니다
복구 워크플로우 자동화
#68. 리허설에서 "사람이 판단해야 하는 단계"가 확정된 뒤 범위를 정하는 것이 순서입니다
EBS 손상을 범위에서 뺀 이유
EBS 자체의 내구성 관리는 AWS 영역입니다. 볼륨은 AZ 내 여러 물리 디바이스에 자동 복제되므로 단일 하드웨어 고장으로는 데이터를 잃지 않습니다. 인스턴스가 죽어도 볼륨은 네트워크 스토리지로 별도 존재하므로 다른 호스트에 재연결하면 됩니다.
볼륨을 실제로 쓸 수 없게 되려면 다음 중 하나가 필요합니다.
원인
현재 상황
AZ 레벨 장애 (전원·냉각)
수년 단위. 발생하면 서비스 전체가 멈춥니다
파일시스템 손상 (디스크 공간 고갈 등)
데이터 볼륨은 12GiB 중 252MB(3%)만 사용 중입니다. 하루 증가량이 수십 KB 수준이라 공간 고갈은 매우 먼 미래입니다
gp3 볼륨 자체 실패
연간 실패율 0.2% (500개 중 1개)
파일시스템 손상은 대체로 스스로 복구됩니다. ext4 는 저널링이라 비정상 종료 후 저널로 복구하고, InnoDB 도 crash recovery 가 있습니다. 우리는 sync_binlog=1 과 innodb_flush_log_at_trx_commit=1 이라 커밋된 데이터는 디스크에 기록되어 있습니다.
즉 이 복구 매뉴얼은 EBS 손상이 아니라 데이터의 논리적 손상을 대비하기 위한 체계입니다. 볼륨을 쓸 수 없게 되는 상황이 생기더라도 복구 절차는 3.2 와 같고, 로컬 binlog 를 쓸 수 없어 S3 만 사용하는 차이만 있습니다. 별도 절차를 만들 필요는 없다고 봅니다.
참고로 우리 리소스는 DB EC2·데이터 볼륨·API EC2 가 모두 ap-northeast-2d 단일 AZ 에 있습니다. AZ 장애 시 서비스와 알림 경로가 함께 멈추므로, 그 경우 복구는 "다른 AZ 에 신규 구성 + S3 복원"이 유일한 경로입니다. 다중 AZ 는 비용이 늘어나 지금 규모에서 할 일은 아니지만 기록해둡니다.
인스턴스·시스템 장애 경로 (백업을 쓰지 않음)
데이터가 아니라 compute 가 문제인 경우입니다. 볼륨이 정상이면 백업을 쓰지 않습니다.
1. 인스턴스 상태 확인 — 재시작으로 해결되는지
2. 해결되지 않으면 인스턴스 교체
- attachment 가 인스턴스를 참조하므로 기존 볼륨이 자동 재연결됨
- blkid 가 기존 파일시스템을 인식해 포맷하지 않음 (데이터 보존)
- user_data 가 마운트·권한·컨테이너 기동까지 처리
3. MySQL 기동 확인, 데이터 정합성 확인
4. 새 host key fingerprint 확인 후 Secret 갱신 ← 놓치기 쉬움
5. 백업 파이프라인 재설치 (install 워크플로우)
커밋된 트랜잭션 기준 RPO 는 0 입니다. S3 복원으로 넘어가는 조건은 파일시스템이 마운트되지 않거나, MySQL 이 기동하지 못하거나, 정합성 확인에서 이상이 발견되는 경우뿐입니다.
준비가 필요한 것 두 가지
첫째, -replace 를 실행할 경로가 없습니다. plan 으로 확인한 영향 범위는 이렇습니다.
module.prod_stack.aws_instance.db_server[0] will be replaced, as requested
module.prod_stack.aws_volume_attachment.db_data[0] must be replaced
Plan: 2 to add, 0 to change, 2 to destroy.
데이터 EBS 는 destroy 목록에 없습니다. 기대한 대로 동작합니다. 문제는 terraform-apply.yml 이 push: main 만 트리거하고 -replace 를 지정할 입력이 없다는 점입니다. MySQL Backup Deploy 워크플로우와 같은 구조(workflow_dispatch + prod-db 승인 게이트 + SSM 터널)로 만들면 됩니다. plan 을 먼저 확인하고 승인 후 apply 하는 순서로 두고, 대상은 선택 목록으로 제한합니다.
둘째, 4번을 빼먹으면 복구는 끝났는데 백업이 안 돕니다. 인스턴스를 재생성하면 SSH host key 가 바뀌고, 배포 워크플로우가 PROD_DB_SSH_HOST_KEY_ED25519 와 fingerprint 를 대조하기 때문입니다.
ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub
재생성 시 코드의 AMI 가 적용되는데 현재 코드 AMI 는 실제 운영 인스턴스와 다르고 아직 검증되지 않았습니다. 리허설에서 함께 확인해야 합니다.
별도 이슈로 올릴 것
작업 중 확인한 것인데 DB EC2 의 루트 볼륨이 89% (여유 757MB) 입니다. 데이터는 별도 볼륨(사용률 3%)에 있어 안전하지만, 루트가 꽉 차면 컨테이너 기동 실패나 로그 기록 불가가 생깁니다. 도커 이미지 두 개가 1.4GB 를 차지하고 로그도 쌓입니다.
복구 매뉴얼과 성격이 다른 예방 조치라 별도 이슈로 올리겠습니다. journald 크기 제한, 도커 이미지 정리, 사용률 알림, 루트 볼륨 상향이 후보입니다.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
결정이 필요한 것 5가지를 2장에 정리했고, 3장은 그에 대한 제 제안입니다. 특히 3.4 의 대응 체계는 팀 합의가 필요한 부분이라 의견 주시면 좋겠습니다.
1. 논의 배경
#66 백업 파이프라인이 prod 에 적용되어 dump 와 binlog 가 S3 에 쌓이기 시작했습니다. 이제 실제로 복구할 수 있는지를 절차로 확정해야 합니다.
절차를 쓰다 보니 범위가 계속 넓어져서, 백업 체계를 만든 이유를 다시 확인했습니다.
즉 이 백업 체계의 목적은 "데이터의 논리적 손상을 특정 시점으로 되돌리는 것" 하나입니다. 잘못된
DELETE/UPDATE, 배포·마이그레이션 사고처럼 볼륨과 파일시스템은 정상인데 데이터 내용이 잘못된 경우를 말합니다.그래서 이 논의도 거기에 맞춰 좁혔습니다. EBS 손상이나 인스턴스 장애는 다른 수단이 담당하므로 범위에서 뺐습니다(3.6 참고).
지금 확보된 것
sourceLogFile+sourceLogPosition기록mysql-recovery-tools:8.4.8적재 (private subnet 대비)데이터 복원 자체는 매우 빠릅니다. dump 복원 수십 초, binlog 재생 수 초 수준입니다(하루 증가량 59KB). 즉 복구 시간의 대부분은 판단과 검증입니다. 절차서와 리허설이 실제 관건이라고 봅니다.
2. 논의 사안
다음 다섯 가지를 정하고 싶습니다.
준비가 필요한 항목:
-replace전용 워크플로우입니다. 인스턴스 교체를 승인 절차로 실행할 경로가 아직 없어서, 리허설(인스턴스 장애 경로)의 전제가 됩니다. 언제 만들지 함께 정하면 좋겠습니다.2.1 어느 binlog 를 쓸지 — RPO 가 갈립니다
백업 스크립트는 닫힌 binlog 만 업로드합니다. 5분마다 회전시키고 방금 닫힌 파일을 올리므로, 진행 중인 활성 binlog 는 S3 에 없습니다. 반면 로컬에는
sync_binlog=1로 커밋 시점에 기록된 최신 상태까지 있습니다.이 차이가 RPO 를 가릅니다.
목표가 "14:03 직전"일 때,
dump 의 기준점 position 부터 로컬 binlog 를 순차 적용하면 커밋된 모든 트랜잭션을 복원할 수 있습니다.
보관 기간도 이 구조를 뒷받침합니다. 로컬 binlog(30일)가 S3 dump(14일)보다 길어서, 어떤 dump 를 골라도 그 기준점부터의 로컬 binlog 가 남아 있습니다.
A안을 제안합니다. 논리적 손상은 대체로 "방금" 발생하므로 목표 시점이 마지막 업로드 이후일 가능성이 높고, 그럴 때 S3 만으로는 그 구간을 재생할 수 없습니다.
A안의 단점은 검증으로 줄일 수 있습니다. binlog manifest 에
sha256이 있으니 S3 와 겹치는 구간의 로컬 파일을 대조하면 되고, 그 구간이 전부 일치하면 아직 업로드되지 않은 활성 binlog 도 신뢰할 근거가 됩니다. 애초에 이 문서가 다루는 상황은 정의상 볼륨과 파일시스템이 정상인 경우입니다.그래서 RPO 는 두 층입니다
즉 RPO 5분은 최악의 경우 보장치이고, 이 문서가 다루는 시나리오에서는 0 에 가깝습니다. 다만 아래쪽 5분은 백업이 정상 동작하고 있다는 전제입니다(3.6 참고).
다만 RPO 0 은 재생할 데이터가 다 손에 있다는 뜻이고, 그 범위 안에서 어디를 잘라낼지는 다음 사안입니다.
그럼 S3 백업은 왜 필요한가
로컬 binlog 를 1차 경로로 쓰더라도 S3 백업 없이는 복구가 성립하지 않습니다. dump 는 업로드 후 로컬에서 삭제되므로(
rm -rf "$JOB_DIR"), 재생을 시작할 기준점이 S3 에만 있습니다.특히 Object Lock 이 중요합니다. 논리적 손상은 사람 실수로 생기는데 그 사람이 binlog 도 지울 수 있습니다. 로컬만 믿으면 백업이 아닙니다.
2.2 binlog 재생 범위
문제 쿼리가 실행된 뒤에도 정상 트랜잭션이 쌓였을 수 있습니다.
2.3 긴급 차단 권한
복구 개시는 최소 2명 합의가 어떨지 3.4 에 적었습니다. 여기서 따로 여쭤보고 싶은 건, 손상 확산을 막는 조치까지 그 합의를 기다려야 하는가입니다.
잘못된
UPDATE가 계속 돌고 있으면 시간이 지날수록 복구 범위가 넓어집니다. 쓰기 차단과 백업 타이머 정지는 되돌릴 수 있는 조치라서, 저는 1명 판단으로 즉시 하는 편이 낫다고 봅니다.다만 쓰기 차단도 서비스 영향이 있어 혼자 판단하기 부담스러울 수 있습니다. 의견 부탁드립니다.
2.4 검증할 주요 테이블
복구 후 무엇을 확인해야 복구가 성공했다고 볼지 기준이 필요합니다. 도메인 기준으로 목록을 만들어두면 복구할 때마다 판단하지 않아도 됩니다.
2.5 리허설 환경과 주기
prod dump 를 쓰면 개인정보를 복사해 두는 셈입니다. 두 환경을 비교해보니 대안이 있었습니다.
log_bin/binlog_format/gtid_mode규모와 binlog 설정이 사실상 같고 버전만 다릅니다. 그래서 prod 와 같은
mysql:8.4.8컨테이너를 띄우고 stage dump 를 복원하는 조합을 제안합니다. 개인정보 문제가 없고, 규모가 같아 시간 측정이 유효하며, 환경은 prod 와 같은 버전입니다.주기는 Discussion #65 에서 "최소 분기 1회, 가능하면 월 1회"가 제안됐는데, 현재 인력과 규모를 고려하면 분기 1회가 현실적이라고 봅니다.
3. 제안 정리
아래는 제가 백업 체계를 만들면서 생각한 방향입니다. 확정된 절차가 아니라 초안이고, 특히 3.4 의 대응 체계는 제 판단만으로 정할 수 없는 부분이라 의견이 필요합니다.
3.1 먼저 백업을 쓸 상황인지 가르면 좋겠습니다
이 판단을 절차 맨 앞에 둔 이유는, 백업이 필요 없는 상황에서 백업 복구를 시작하면 오히려 데이터를 잃기 때문입니다. 볼륨이 정상인데 dump 를 복원하면 그 이후 데이터가 사라집니다. 급할 때 가장 저지르기 쉬운 실수라고 생각해서 앞에 뒀습니다.
3.2 복구 절차는 이렇게 생각했습니다
이 순서를 만들 때 지킨 원칙 세 가지
① 운영 DB 를 바로 덮지 않았으면 합니다 (6번)
별도 컨테이너에 복원해 검증한 뒤 반영하면, 복구가 실패해도 현재 상태를 잃지 않습니다. 데이터가 4.8MB 라 임시 복원 비용이 거의 없습니다.
② 손대기 전에 현재 상태를 남기고 싶습니다 (2번)
손상된 상태라도 복구 판단이 틀렸을 때 돌아올 지점이 필요합니다. 일회성 EBS 스냅샷은 Discussion #65 의 "상시 스냅샷 미사용" 결정과 충돌하지 않는다고 봅니다 — 그때 문제는 정기 스냅샷의 누적 고정비였고, 여기서는 장애 대응 시 한 번이니까요. 검증 후 삭제하면 됩니다.
③ 복구 중에는 백업 타이머를 멈춰야 한다고 봅니다 (1번, 10번)
멈추지 않으면 손상된 상태가 dump 로 올라가 나중에 정상 백업으로 착각할 수 있습니다. 알림도 반복되고, 복구 작업과 binlog 작업이 같은 볼륨에서 충돌합니다. 재개 시
Persistent=true가 놓친 발화를 따라잡고, 복구된 상태를 새 기준점으로 만들기 위해 dump 를 즉시 한 번 생성합니다.제가 가장 위험하게 보는 단계는 9번입니다
가능하면 테이블 단위로 반영하는 쪽을 제안합니다. 잘못된
DELETE는 삭제된 행만 다시 넣고, 잘못된UPDATE는 영향받은 행만 되돌립니다. 그러면 사고 이후의 정상 작업이 살아남습니다. 데이터가 4.8MB 규모라 복구된 상태와 현재 상태를 나란히 비교하는 비용이 크지 않습니다.시점 특정과 binlog 재생 주의점
목표 시각을 정하는 것이 절차에서 가장 애매합니다.
mysqlbinlog로 해당 구간을 사람이 읽을 수 있는 형태로 덤프해 문제 쿼리의 position 을 찾습니다--stop-position으로 그 직전까지 재생합니다재생은 파일로 한 번 떨어뜨린 뒤 적용하는 방식을 제안합니다.
mysqlbinlog | mysql로 바로 흘리면 무엇이 적용되는지 보지 못한 채 실행됩니다.위치 옵션의 적용 범위에 주의가 필요합니다.
여러 파일을 나열하면 첫 파일과 마지막 파일에만 적용되고 중간 파일은 전체가 재생됩니다.
같은 인스턴스의 다른 스키마에 복원하는 방식은 쓰지 않는 게 좋겠습니다.
mysqlbinlog --rewrite-db의 설명이 "Rewrite the row event" 인데,binlog_format=ROW여도 DDL 은 statement 로 기록됩니다. 즉 재생 구간에ALTER TABLE이 있으면 원래 스키마(운영 데이터)에 적용됩니다. 스키마 사고 복구에서 특히 위험합니다.S3 키 구조와 주의점
manifest.json이 있는 prefix 만 완결된 백업입니다. dump → 체크섬 → manifest 순으로 올리므로 manifest 가 없으면 미완성입니다dump/2026/08/25/에 있습니다server_uuid가 들어가는 이유는 데이터를 재생성하면 binlog 번호가000001로 리셋되기 때문입니다. 복구 시 manifest 의serverUuid와 일치하는 것만 골라야 합니다3.3 복구 후 처리도 함께 정해야 한다고 봅니다
DB 만 되돌리면 끝이 아닙니다. 여기서 놓치면 복구는 성공했는데 서비스가 이상해집니다.
flyway_schema_history그래서 Redis 전체 비우기는 하지 말아야 한다고 봅니다. 캐시를 비우려다 블랙리스트까지 지우면 로그아웃 처리가 무효화되는 보안 문제가 됩니다. 급할 때 가장 손이 갈 만한 명령이라 미리 못 박아두고 싶습니다.
Flyway 는 복구 시점이 최신 마이그레이션 이전이면 애플리케이션 기동 시 누락분을 다시 적용합니다. 스키마는 맞춰지지만 데이터 변환이 포함된 마이그레이션은 재실행되므로 예상 못한 변경으로 보일 수 있습니다. 현재 마이그레이션은 58개입니다.
검증 항목
flyway_schema_history최신 버전이 예상과 일치/actuator/healthUP, 주요 조회 API 가 복구된 데이터와 일치중단·롤백 기준
3.4 대응 체계는 이렇게 하면 어떨까 합니다
이 절은 제 제안이 가장 약한 부분입니다. 절차는 코드로 검증할 수 있지만 이건 팀이 합의해야 성립하는 내용이라, 아래는 출발점으로만 봐주시면 좋겠습니다.
특히 두 가지를 여쭤보고 싶습니다. 2명이 적절한 인원인지, 그리고 새벽 사고에서 오전 10시라는 기준이 너무 늦지 않은지입니다.
3.5 리허설은 한 번 해봐야 한다고 생각합니다
절차서만 있고 해보지 않으면 실제로 막히는 지점을 알 수 없습니다. 환경과 데이터는 2.5 에 적었습니다.
측정할 것
AMI·인스턴스 타입 변경, 백업 스크립트 로직 변경, MySQL 버전 상향 시에는 주기와 무관하게 다시 검증해야 한다고 생각합니다. 이 측정값이 나와야 RTO 목표를 숫자로 말할 수 있다고 봅니다.
3.6 이 논의에서 다루지 않는 것
발생 → 발견구간을 한정할 수 없어, 목표는 "발견부터" 로 두는 것을 제안합니다EBS 손상을 범위에서 뺀 이유
EBS 자체의 내구성 관리는 AWS 영역입니다. 볼륨은 AZ 내 여러 물리 디바이스에 자동 복제되므로 단일 하드웨어 고장으로는 데이터를 잃지 않습니다. 인스턴스가 죽어도 볼륨은 네트워크 스토리지로 별도 존재하므로 다른 호스트에 재연결하면 됩니다.
볼륨을 실제로 쓸 수 없게 되려면 다음 중 하나가 필요합니다.
파일시스템 손상은 대체로 스스로 복구됩니다. ext4 는 저널링이라 비정상 종료 후 저널로 복구하고, InnoDB 도 crash recovery 가 있습니다. 우리는
sync_binlog=1과innodb_flush_log_at_trx_commit=1이라 커밋된 데이터는 디스크에 기록되어 있습니다.즉 이 복구 매뉴얼은 EBS 손상이 아니라 데이터의 논리적 손상을 대비하기 위한 체계입니다. 볼륨을 쓸 수 없게 되는 상황이 생기더라도 복구 절차는 3.2 와 같고, 로컬 binlog 를 쓸 수 없어 S3 만 사용하는 차이만 있습니다. 별도 절차를 만들 필요는 없다고 봅니다.
참고로 우리 리소스는 DB EC2·데이터 볼륨·API EC2 가 모두
ap-northeast-2d단일 AZ 에 있습니다. AZ 장애 시 서비스와 알림 경로가 함께 멈추므로, 그 경우 복구는 "다른 AZ 에 신규 구성 + S3 복원"이 유일한 경로입니다. 다중 AZ 는 비용이 늘어나 지금 규모에서 할 일은 아니지만 기록해둡니다.인스턴스·시스템 장애 경로 (백업을 쓰지 않음)
데이터가 아니라 compute 가 문제인 경우입니다. 볼륨이 정상이면 백업을 쓰지 않습니다.
커밋된 트랜잭션 기준 RPO 는 0 입니다. S3 복원으로 넘어가는 조건은 파일시스템이 마운트되지 않거나, MySQL 이 기동하지 못하거나, 정합성 확인에서 이상이 발견되는 경우뿐입니다.
준비가 필요한 것 두 가지
첫째,
-replace를 실행할 경로가 없습니다. plan 으로 확인한 영향 범위는 이렇습니다.데이터 EBS 는 destroy 목록에 없습니다. 기대한 대로 동작합니다. 문제는
terraform-apply.yml이push: main만 트리거하고-replace를 지정할 입력이 없다는 점입니다.MySQL Backup Deploy워크플로우와 같은 구조(workflow_dispatch+prod-db승인 게이트 + SSM 터널)로 만들면 됩니다.plan을 먼저 확인하고 승인 후apply하는 순서로 두고, 대상은 선택 목록으로 제한합니다.둘째, 4번을 빼먹으면 복구는 끝났는데 백업이 안 돕니다. 인스턴스를 재생성하면 SSH host key 가 바뀌고, 배포 워크플로우가
PROD_DB_SSH_HOST_KEY_ED25519와 fingerprint 를 대조하기 때문입니다.재생성 시 코드의 AMI 가 적용되는데 현재 코드 AMI 는 실제 운영 인스턴스와 다르고 아직 검증되지 않았습니다. 리허설에서 함께 확인해야 합니다.
별도 이슈로 올릴 것
작업 중 확인한 것인데 DB EC2 의 루트 볼륨이 89% (여유 757MB) 입니다. 데이터는 별도 볼륨(사용률 3%)에 있어 안전하지만, 루트가 꽉 차면 컨테이너 기동 실패나 로그 기록 불가가 생깁니다. 도커 이미지 두 개가 1.4GB 를 차지하고 로그도 쌓입니다.
복구 매뉴얼과 성격이 다른 예방 조치라 별도 이슈로 올리겠습니다. journald 크기 제한, 도커 이미지 정리, 사용률 알림, 루트 볼륨 상향이 후보입니다.
All reactions