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
{{ message }}
Repository navigation
feat: [복구 분기 B] 로컬 볼륨 비정상 시 새 볼륨과 S3 백업으로 복원하는 워크플로우 구현 #88
1. 기존 데이터 EBS 를 Terraform state 에서 분리해 원인 분석용으로 보존
2. 기존 스펙의 새 데이터 EBS 와 새 DB EC2 생성
3. user_data 가 새 볼륨을 포맷하고 빈 MySQL 기동
4. S3 에서 dump 선택 → 체크섬 검증 → 복원
5. dump manifest 의 sourceLogFile / sourceLogPosition 부터
같은 serverUuid 의 S3 binlog 를 번호 순으로 재생
6. MySQL 상태와 데이터 검증
7. 새 SSH host key 확인 및 갱신
8. 백업 체계 재설치 (상태가 비어 있으므로 첫 dump 가 자동 생성되어 새 기준점이 됨)
private IP가 고정(#86)되어 있으므로 Parameter Store 갱신과 API 서버 재배포는 필요 없습니다.
작업 상세 내용
볼륨 교체
prevent_destroy가 걸린 기존 데이터 EBS를 삭제하지 않고 Terraform state에서 분리해 보존하는 방식을 정하고 구현합니다(removed 블록 또는 state rm).
보존한 기존 볼륨의 정리 기준(원인 분석 후 수동 삭제 등)을 정합니다.
새 볼륨과 새 인스턴스가 같은 AZ에 생성되고, user_data가 새 볼륨을 포맷해 빈 MySQL을 기동하는지 확인합니다.
S3 복원
입력값으로 dump S3 key를 선택적으로 받고, 생략하면 manifest.json이 있는 최신 dump를 사용합니다.
dump 체크섬과 manifest를 검증하고, binlog 번호의 연속성을 사전 검사합니다.
dump를 복원한 뒤 manifest의 sourceLogFile/sourceLogPosition부터 같은 serverUuid의 S3 binlog를 번호 순으로 재생합니다.
코드(modules/app_stack/db_ec2.tf, scripts/mysql_backup/bin/mysql-backup-dump, mysql-backup-binlog, terraform-apply.yml)와 대조해 본 결과입니다.
🔴 필수
1. 복원 후 앱 계정 재생성 단계 추가
mysql-backup-dump 는 mysqldump ... "$MYSQL_DATABASE" 로 단일 DB 만 덤프하므로 mysql.user 가 포함되지 않습니다. 새 MySQL 에는 user_data 가 만든 root 계정만 있고 additional_db_users(앱 계정·권한)는 없어, 완료 조건인 "API 서버 설정 변경 없이 재연결"이 되지 않습니다.
검증을 마친 뒤 SSM 터널을 열고 -target=module.prod_stack.mysql_user.users, -target=module.prod_stack.mysql_grant.user_grants 로 apply 해 계정을 재생성하는 단계를 두면 좋겠습니다(refresh 시 계정 부재가 감지되어 생성 계획이 잡힙니다). 이 단계를 검증 이후에 두면, 복원 중인 DB 에 API 가 붙어 쓰기나 마이그레이션을 실행하는 것을 막는 게이트 역할도 합니다.
2. 볼륨 분리 방식은 state rm 으로 결정
removed 블록은 리소스 선언이 코드에서 빠질 때 쓰는 방식이라, aws_ebs_volume.db_data 선언을 유지한 채 새 볼륨을 만들어야 하는 이 경우에는 맞지 않습니다. 또 main 에 머지되면 terraform-apply.yml 이 자동 apply 합니다. prevent_destroy 로 -replace 도 막혀 있습니다.
따라서 워크플로우 실행 중에 처리하는 것을 제안합니다.
terraform state rm 'module.prod_stack.aws_ebs_volume.db_data[0]'
인스턴스는 ignore_changes 때문에 -replace 가 있어야 새 볼륨 ID 가 담긴 user_data 로 생성됨
plan 변경 집합 단언도 분기 B 기준(aws_ebs_volume create, 기존 볼륨 delete 없음)으로 맞춰야 합니다. 복구 role 에는 state 쓰기와 ec2:CreateVolume 권한이 필요합니다.
3. 복원 내용이 새 binlog 에 남지 않도록 처리
기본 설정이면 dump import 와 binlog 재생 내용이 모두 새 서버의 binlog 에 기록됩니다. 백업 재설치 후 binlog 스크립트는 state 가 없으므로 S3 에서 새 uuid 체인을 찾고, 없으면 닫힌 binlog 를 모두 업로드해 복원 분량 전체가 Object Lock 버킷에 14일간 남습니다.
dump import: SET sql_log_bin=0 상태로 실행
binlog 재생: mysqlbinlog --disable-log-bin
4. binlog 재생 범위(DB 필터) 결정
dump 에는 단일 DB 만 있지만 binlog 에는 다른 스키마와 mysql.* 이벤트도 섞여 있습니다. 그대로 재생하면 없는 스키마를 참조해 실패하거나 의도하지 않은 변경이 적용될 수 있습니다. --database=$MYSQL_DATABASE 필터를 쓸지 정하고, ROW 포맷에서 필터가 어떻게 동작하는지 리허설에서 확인하면 좋겠습니다.
🟡 보완
기존 볼륨 분리와 보존 처리
볼륨이 impaired 상태면 detach 가 오래 걸릴 수 있어 aws ec2 detach-volume --force 단계와 타임아웃이 필요합니다.
보존하는 기존 볼륨은 새 볼륨과 Name 태그가 같아지므로 -retired-<run_id> 같은 태그로 바꿔 혼동을 막는 것을 제안합니다. "정리 기준" 항목과 함께 정하면 좋겠습니다.
실제 RPO 출력: "최대 5분"은 binlog 업로드가 정상이었을 때 기준입니다. 장애 전 업로드가 밀려 있었다면 더 길어지므로, 마지막으로 재생한 binlog 의 시각을 워크플로우 결과로 출력하면 좋겠습니다.
dump 자동 선택 조건: "manifest 가 있는 최신 dump"에 더해, 그 dump 의 serverUuid 로 sourceLogFile 부터 S3 binlog 가 끊김 없이 이어지는지까지 확인한 dump 를 선택해야 합니다. 이전에 분기 B 복구를 한 적이 있으면 uuid 가 섞여 있습니다.
스테이징 용량 사전 검사: 새 볼륨이 기존과 같은 크기이므로 압축 dump, binlog, 복원된 DB 가 동시에 들어갈 수 있는지 미리 확인합니다(require_dump_staging_space 와 같은 방식).
A 실패 후 B 진입: 분기 A 로 인스턴스를 이미 교체했지만 MySQL 이 기동하지 못한 상태에서 B 를 실행해도 동작하는지 확인이 필요합니다.
Activity
Hexeong commentedon Oct 10, 2026
계획 검토 피드백
코드(
modules/app_stack/db_ec2.tf,scripts/mysql_backup/bin/mysql-backup-dump,mysql-backup-binlog,terraform-apply.yml)와 대조해 본 결과입니다.🔴 필수
1. 복원 후 앱 계정 재생성 단계 추가
mysql-backup-dump는mysqldump ... "$MYSQL_DATABASE"로 단일 DB 만 덤프하므로mysql.user가 포함되지 않습니다. 새 MySQL 에는 user_data 가 만든 root 계정만 있고additional_db_users(앱 계정·권한)는 없어, 완료 조건인 "API 서버 설정 변경 없이 재연결"이 되지 않습니다.검증을 마친 뒤 SSM 터널을 열고
-target=module.prod_stack.mysql_user.users,-target=module.prod_stack.mysql_grant.user_grants로 apply 해 계정을 재생성하는 단계를 두면 좋겠습니다(refresh 시 계정 부재가 감지되어 생성 계획이 잡힙니다). 이 단계를 검증 이후에 두면, 복원 중인 DB 에 API 가 붙어 쓰기나 마이그레이션을 실행하는 것을 막는 게이트 역할도 합니다.2. 볼륨 분리 방식은
state rm으로 결정removed블록은 리소스 선언이 코드에서 빠질 때 쓰는 방식이라,aws_ebs_volume.db_data선언을 유지한 채 새 볼륨을 만들어야 하는 이 경우에는 맞지 않습니다. 또 main 에 머지되면terraform-apply.yml이 자동 apply 합니다.prevent_destroy로-replace도 막혀 있습니다.따라서 워크플로우 실행 중에 처리하는 것을 제안합니다.
terraform state rm 'module.prod_stack.aws_ebs_volume.db_data[0]'-replace=module.prod_stack.aws_instance.db_server[0]+-target(volume, attachment, instance)으로 applyvolume_id변경으로 자동 replaceignore_changes때문에-replace가 있어야 새 볼륨 ID 가 담긴 user_data 로 생성됨plan 변경 집합 단언도 분기 B 기준(
aws_ebs_volumecreate, 기존 볼륨 delete 없음)으로 맞춰야 합니다. 복구 role 에는 state 쓰기와ec2:CreateVolume권한이 필요합니다.3. 복원 내용이 새 binlog 에 남지 않도록 처리
기본 설정이면 dump import 와 binlog 재생 내용이 모두 새 서버의 binlog 에 기록됩니다. 백업 재설치 후 binlog 스크립트는 state 가 없으므로 S3 에서 새 uuid 체인을 찾고, 없으면 닫힌 binlog 를 모두 업로드해 복원 분량 전체가 Object Lock 버킷에 14일간 남습니다.
SET sql_log_bin=0상태로 실행mysqlbinlog --disable-log-bin4. binlog 재생 범위(DB 필터) 결정
dump 에는 단일 DB 만 있지만 binlog 에는 다른 스키마와
mysql.*이벤트도 섞여 있습니다. 그대로 재생하면 없는 스키마를 참조해 실패하거나 의도하지 않은 변경이 적용될 수 있습니다.--database=$MYSQL_DATABASE필터를 쓸지 정하고, ROW 포맷에서 필터가 어떻게 동작하는지 리허설에서 확인하면 좋겠습니다.🟡 보완
aws ec2 detach-volume --force단계와 타임아웃이 필요합니다.Name태그가 같아지므로-retired-<run_id>같은 태그로 바꿔 혼동을 막는 것을 제안합니다. "정리 기준" 항목과 함께 정하면 좋겠습니다.serverUuid로sourceLogFile부터 S3 binlog 가 끊김 없이 이어지는지까지 확인한 dump 를 선택해야 합니다. 이전에 분기 B 복구를 한 적이 있으면 uuid 가 섞여 있습니다.require_dump_staging_space와 같은 방식).