Skip to content

feat: [복구 분기 B] 로컬 볼륨 비정상 시 새 볼륨과 S3 백업으로 복원하는 워크플로우 구현 #88

Open

Description

@Hexeong

어떤 기능인가요?

#68 복구 워크플로우의 **분기 B(로컬 볼륨 비정상)**를 구현합니다. 새 DB EC2와 같은 스펙의 새 데이터 EBS를 만들고, S3 dump와 binlog로 데이터를 복원합니다.

볼륨을 마운트할 수 없거나, MySQL이 기동하지 못하거나, 정합성 확인에서 이상이 발견된 경우를 다룹니다. 로컬 binlog를 쓸 수 없으므로 S3에 업로드된 마지막 binlog 시점까지만 복구할 수 있고, RPO는 최대 5분입니다.

분기 A(#87)에서 만든 공통 기반(승인 게이트, Terraform 교체, SSH 접속, systemd-run, host key 갱신, 백업 재설치)을 재사용하고, 볼륨 교체와 S3 복원을 더합니다.

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를 번호 순으로 재생합니다.
  • binlog 재생은 파일로 한 번 떨어뜨린 뒤 적용합니다. 여러 파일을 나열할 때 --start-position은 첫 파일에만 적용된다는 점에 주의합니다([논의] Prod DB 데이터 논리적 손상 시 시점 복구 절차 #78 3.2).
  • 복원 작업은 공통 기반의 systemd-run과 상태 폴링으로 실행합니다.

검증과 재개

  • MySQL 상태, 주요 테이블, 마지막으로 재생한 binlog 시점, API 서버의 DB 연결을 검증합니다.
  • 백업 체계를 재설치해 새 serverUuid 기준으로 binlog 체인과 첫 dump가 생성되는지 확인합니다.

사전 확인

  • 새 DB EC2의 IAM 권한으로 백업 버킷을 읽을 수 있는지(GetObject, ListBucket) 확인합니다.
  • 복원에 쓸 도구(mysql-recovery-tools 이미지 또는 mysqlbinlog)가 코드의 DB AMI에 들어 있는지 확인합니다.

완료 조건

  • 승인된 사용자만 워크플로우를 실행할 수 있습니다.
  • 새 볼륨에 S3 백업을 복원해 마지막으로 업로드된 binlog 시점까지 복구할 수 있습니다.
  • 기존 볼륨은 삭제되지 않고 보존됩니다.
  • 복구 후 백업 체계가 새 기준점으로 다시 동작하고, API 서버 설정을 바꾸지 않아도 연결됩니다.
  • 리허설로 소요 시간을 측정해 기록합니다.

참고할만한 자료(선택)

Activity

  1. self-assigned this
    on Oct 10, 2026
  2. Hexeong commented on Oct 10, 2026

    @Hexeong
    ContributorAuthor

    계획 검토 피드백

    코드(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 도 막혀 있습니다.

    따라서 워크플로우 실행 중에 처리하는 것을 제안합니다.

    1. terraform state rm 'module.prod_stack.aws_ebs_volume.db_data[0]'
    2. -replace=module.prod_stack.aws_instance.db_server[0] + -target(volume, attachment, instance)으로 apply
      • attachment 는 volume_id 변경으로 자동 replace
      • 인스턴스는 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 를 실행해도 동작하는지 확인이 필요합니다.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions