카테고리 : MySQL/기술노트

재해복구 리허설: 문서화된 runbook과 역할 분담

MySQL 재해복구 리허설에서 복구 경계, 담당자, 중단 조건, 쓰기 전환 승인과 검증 증거를 연결하는 runbook 설계 방법을 정리한다.

저자: MySQL 기술 노트 작성: 2026.10.07 약 11분 6,478자
다운로드

백업을 복원할 수 있다는 사실과 장애 중에 서비스를 복구할 수 있다는 사실은 다르다. 복구 담당자가 백업 위치를 알고 있더라도 암호화 키 접근 승인이 지연될 수 있고, 데이터베이스가 기동되어도 애플리케이션의 연결 풀은 이전 서버를 계속 사용할 수 있다. 새 서버에서 쓰기를 받기 시작한 뒤 이전 서버가 다시 살아나면 기술적으로 성공한 복원이 이중 쓰기 사고로 바뀐다.

재해복구 리허설은 복원 명령의 속도만 측정하는 행사가 아니다. 누가 어떤 증거를 확인한 뒤 다음 단계로 넘어갈 수 있는지를 실제로 시험하는 절차다. 이 글은 MySQL 8.0 이상을 기준으로, Community MySQL과 Aurora MySQL에 공통으로 적용할 수 있는 runbook의 구조와 역할 분담을 설명한다.

1. 복구를 명령 목록이 아니라 상태 전이로 설계한다

runbook이 “백업 다운로드 → 복원 → 재시작”으로 끝나면 데이터베이스 바깥의 실패를 다루지 못한다. 서비스 복구는 다음 상태들을 순서대로 통과해야 한다.

flowchart TD
    A[장애 선언과 복구 목표 확정] --> B[이전 쓰기 경로 격리 및 증거 보존]
    B --> C[격리 환경에서 백업 복원과 로그 적용]
    C --> D[데이터 및 애플리케이션 검증]
    D --> E{필수 증거와 전환 승인 충족}
    E -->|아니오| F[중단 상태 유지 및 원인 보완]
    F --> D
    E -->|예| G[새 쓰기 경로 개방과 연결 전환]
    G --> H[관찰 기간 및 업무 대사]
    H --> I[복구 종료 선언과 새 백업 기준선 확보]

각 화살표에는 진입 조건과 완료 조건이 필요하다. 특히 복원 완료, 검증 완료, 쓰기 개방 승인, 서비스 복구 완료는 서로 다른 상태다. 작업자가 마지막 명령의 종료 코드만 보고 여러 상태를 한 번에 건너뛰어서는 안 된다.

InnoDB가 redo를 적용하고 미커밋 변경을 되돌려 일관된 상태로 기동하는 것은 엔진 수준의 복구다. 그 상태가 원하는 업무 시점인지, 잘못된 삭제가 제외되었는지, 결제 요청의 중복 실행이 방지되는지는 별도의 검증 대상이다. 논리 복원은 객체 정의·데이터·권한의 범위가 덤프 옵션과 대상 구성에 따라 달라지므로 서버가 열렸다는 사실만으로 복구 범위를 추정해서도 안 된다.

리허설의 세 가지 수준

수준 실제로 시험하는 것 확인했다고 말할 수 없는 것
문서 기반 모의훈련 연락 체계, 역할 인계, 승인 및 중단 판단 백업 복원 가능성, 실제 복구 소요 시간
격리 환경 복구훈련 백업·키 접근, 복원, 로그 경계, 데이터 검증 운영 트래픽 전환, 실제 사용자의 체감 복구
통제된 서비스 전환훈련 연결 전환, 이전 writer 차단, 업무 확인, 복귀 절차 시험하지 않은 리전 장애나 대규모 부하 조건

처음부터 운영 전환을 수행할 필요는 없다. 다만 어떤 수준에서 무엇을 검증했는지를 보고서에 분리해야 한다. 격리된 소형 서버의 성공을 운영 규모의 RTO 충족으로 확대하면 리허설의 가치가 사라진다.

2. 시나리오와 완료 기준을 먼저 고정한다

같은 runbook으로 모든 장애를 처리하려 하면 복구 분기가 숨겨진다. 다음 항목을 훈련 시작 전에 고정한다.

  • 장애 가정: 서버 손실, 스토리지 손실, 논리적 삭제, 리전 접근 불가 중 무엇인가.
  • 보존된 자원: 원본 서버, replica, 객체 저장소, 암호화 키, 계정 체계 중 무엇을 사용할 수 있는가.
  • 복구 목표: 최신 상태 복구인지, 사고 트랜잭션 직전 복구인지, 손실을 승인한 replica 승격인지.
  • 업무 범위: 주문 조회만 우선 개방할지, 결제·정산·배치까지 포함할지.
  • 성공 조건: 허용 데이터 손실, 필수 객체와 불변식, 오류율·지연 시간 기준, 관찰 기간.
  • 안전 경계: 운영 endpoint 접속 금지, 외부 결제·메일 발송 차단, 원본 백업 변경 금지.

RTO의 시계는 복원 명령 실행 시각부터 재는 것이 아니다. 조직이 합의한 서비스 중단 기준 시각부터, 정해진 업무와 품질 기준으로 서비스를 다시 제공한 시각까지를 기록한다. 장애 감지, 선언, 권한 확보, 대상 인프라 준비, 복원, 검증, 연결 전환과 관찰을 각각 남겨야 병목을 구분할 수 있다. 병렬 작업의 경과 시간은 단순 합산하지 않고, 완료를 지연시킨 의존 경로를 분석한다.

RPO는 “가장 최근 파일의 생성 시각”으로 확인하지 않는다. 해당 복구본에 포함된 마지막 정상 업무 변경과 사고 시점 사이의 손실 구간을 확인해야 한다. GTID 집합은 트랜잭션 이력을 비교하는 근거지만 그 자체가 시각이거나 데이터 동일성의 증명은 아니다. 복제 필터, 빈 GTID 스킵, 별도 입력이 있었다면 이력과 데이터가 어긋날 수 있다. 주문 원장, 애플리케이션 요청 식별자 등 업무 증거와 함께 판단한다.

논리적 삭제 사고에서는 가장 최신 복구 시점이 오히려 잘못된 목표다. 사고 직전으로 되돌리면 사고 이후의 정상 변경도 제외될 수 있으므로, 이를 별도로 추출·대사할 담당자와 허용 시간을 정해야 한다.

3. 역할은 직함보다 결정권과 인계 조건으로 나눈다

한 명에게 “DB 복구 담당”이라는 역할만 부여하면 기술 실행과 위험 승인이 섞인다. 소규모 조직에서 한 사람이 여러 역할을 겸할 수는 있지만, 쓰기 개방이나 데이터 손실 수용과 같은 결정은 별도 확인자를 두는 편이 안전하다.

역할 책임과 결정 범위 다음 담당자에게 넘길 증거
사고 지휘자 장애 선언, 시나리오 선택, 우선순위, 중단·재개 결정 승인된 복구 목표, 영향 범위, 의사결정 기록
DBA 실행자 백업 선택, 복원, 로그 적용, MySQL 상태 확인 백업 식별자, 적용 경계, 오류 로그, 대상 서버 식별 정보
인프라·보안 담당자 네트워크 격리, 권한·키 접근, 이전 쓰기 경로 차단 차단 정책과 실제 접속 시험, 대상 네트워크·계정 확인
애플리케이션 담당자 연결 풀 전환, 배치·큐 제어, 대표 업무 시험 새 대상 연결 증거, 쓰기·재조회 결과, 재시도·중복 처리 결과
데이터·업무 확인자 데이터 손실 평가, 원장 대사, 서비스 범위 수용 불변식 검사, 누락 목록, 허용 손실 승인
기록·관찰 담당자 공통 시각 기록, 단계별 소요 시간, 훈련 중 문제 수집 증거 묶음, 인계 지연, 후속 개선 항목

각 역할에는 주 담당자와 대체 담당자를 지정한다. 연락처와 실제 계정 식별자는 접근 통제된 운영 문서에서 관리하고 공개 매뉴얼이나 SQL 결과에 넣지 않는다. 승인자가 자리에 없을 때 자동으로 다음 단계로 넘어가는 대신, 대체 승인자에게 인계하거나 중단 상태를 유지하도록 정한다.

리허설 중에는 평소 문서를 작성한 DBA가 직접 모든 명령을 실행하지 않도록 해보는 것이 유익하다. 대체 담당자가 설명 없이 절차를 따라갈 수 있어야 숨겨진 전제와 개인 기억에 대한 의존을 찾을 수 있다.

4. 실행 가능한 runbook의 최소 단위

단계마다 다음 형식의 작업 카드를 둔다. 이것은 환경에 맞게 채워야 하는 문서 틀이며 실행 명령이나 실제 훈련 결과가 아니다.

단계 식별자: 전환-승인
목적: 이전 쓰기 경로를 차단한 상태에서 새 대상의 쓰기 개방 여부 결정
실행자 / 확인자 / 대체자: 사전 지정
진입 조건: 복원 완료, 복구 경계 확정, 격리 환경 검증 완료
대상: 복구 작업 ID와 연결된 서버 또는 클러스터 식별자
입력 증거: 이전 writer 차단 시험, 데이터 대사, 대표 업무 시험
예상 결과: 모든 필수 증거가 현재 복구 대상에 대해 유효함
중단 조건: 필수 증거 누락, 대상 불일치, 미해결 데이터 차이, 승인 부재
완료 조건: 확인자가 증거와 대상 ID를 대조하고 전환 승인 기록
재실행 조건: 기존 승인 무효화 후 바뀐 대상과 경계에 대해 재검증
후속 단계: 연결 전환과 제한된 쓰기 개방, 관찰 시작

실제 명령을 붙일 때는 대상 검증 절차를 먼저 둔다. 같은 프롬프트에서 원본과 복구본을 번갈아 조작하는 방식은 피하고, 연결 프로파일·세션 표식·승인된 대상 목록으로 구분한다. 복원 대상 데이터 디렉터리 교체, 로그 재적용, endpoint 변경은 재실행해도 안전한 작업인지 별도로 표시한다. 도중에 접속이 끊겼다고 무조건 처음부터 다시 실행하면 이미 적용된 변경을 중복 반영하거나 정상 복구본을 지울 수 있다.

MySQL의 역할 상태는 확인하되 격리 증거와 혼동하지 않는다

다음 쿼리는 연결한 MySQL의 버전, 읽기 전용 상태와 binary log 활성 여부를 확인한다. 운영에서는 승인된 서버 식별 정보도 별도로 대조한다. 쿼리는 읽기만 수행하며 전환이나 차단을 실행하지 않는다.

SELECT VERSION() AS mysql_version,
       @@global.read_only AS read_only,
       @@global.super_read_only AS super_read_only,
       @@global.log_bin AS log_bin;

실행 결과(MySQL 8.0.x):

mysql> SELECT VERSION() AS mysql_version,
    ->        @@global.read_only AS read_only,
    ->        @@global.super_read_only AS super_read_only,
    ->        @@global.log_bin AS log_bin;

+---------------+-----------+-----------------+---------+
| mysql_version | read_only | super_read_only | log_bin |
+---------------+-----------+-----------------+---------+
| 8.0.46        |         0 |               0 |       0 |
+---------------+-----------+-----------------+---------+
1 row in set (0.00 sec)

실행 결과의 값은 폐기용 MySQL 검증 서버의 설정이다. read_only=0이나 log_bin=0을 복구 대상의 권장 설정으로 해석해서는 안 된다. 특히 로그가 꺼진 새 writer를 그대로 사용하면 이후 PITR이나 복제 설계에 필요한 이력이 생성되지 않을 수 있다.

Community MySQL에서 read_only는 일반 클라이언트 쓰기를 제한하지만 권한 있는 사용자의 예외가 있고, super_read_only는 클라이언트 변경을 더 강하게 제한한다. 그렇더라도 복제 적용과 같은 예외가 있으며, 권한 있는 운영자는 설정 자체를 바꿀 수 있다. 두 변수 조회만으로 이전 writer의 완전한 차단을 증명할 수 없다. 네트워크·프록시·계정 경로와 직접 접속 경로를 함께 통제하고, 실제 애플리케이션 경로에서 쓰기가 차단되는지 확인한다. 원본이 접근 불가능한 상황이라면 원본 SQL 조회에 의존하지 않는 인프라 수준의 차단 증거가 필요하다.

증거가 없으면 실패하는 승인 조건을 만든다

“실패 행이 없으면 통과”하는 검사에는 함정이 있다. 수집되지 않은 검사 결과도 실패 행으로 나타나지 않기 때문이다. 아래 SQL은 가상의 승인 증거로 판정 로직만 검증하는 축소 예제다. 실제 복원·네트워크 차단·업무 시험을 수행한 결과가 아니다.

필수 항목 목록을 기준으로 LEFT JOIN하여, 실패뿐 아니라 누락도 차단한다. all_pass는 모두 통과, fence_failed는 이전 쓰기 경로 차단 실패, approval_missing은 업무 승인 누락을 나타낸다. 예제의 업무 승인은 쓰기 전환 직전의 데이터 손실·서비스 범위 승인이다.

WITH
required_checks AS (
    SELECT 'old_writer_fenced' AS check_name
    UNION ALL SELECT 'data_validated'
    UNION ALL SELECT 'app_validated'
    UNION ALL SELECT 'business_approved'
),
scenarios AS (
    SELECT 'all_pass' AS scenario
    UNION ALL SELECT 'fence_failed'
    UNION ALL SELECT 'approval_missing'
),
evidence AS (
    SELECT s.scenario, r.check_name,
           CASE WHEN s.scenario = 'fence_failed'
                     AND r.check_name = 'old_writer_fenced'
                THEN 'FAIL' ELSE 'PASS' END AS result
    FROM scenarios s CROSS JOIN required_checks r
    WHERE NOT (s.scenario = 'approval_missing'
               AND r.check_name = 'business_approved')
)
SELECT s.scenario,
       COUNT(*) AS required_count,
       COUNT(e.check_name) AS observed_count,
       SUM(CASE WHEN e.result = 'PASS' THEN 1 ELSE 0 END) AS passed_count,
       CASE WHEN SUM(CASE WHEN e.result = 'PASS' THEN 1 ELSE 0 END)
                      = COUNT(*)
            THEN 'READY' ELSE 'BLOCKED' END AS decision
FROM scenarios s
CROSS JOIN required_checks r
LEFT JOIN evidence e
  ON e.scenario = s.scenario AND e.check_name = r.check_name
GROUP BY s.scenario
ORDER BY s.scenario;

실행 결과(MySQL 8.0.x):

+------------------+----------------+----------------+--------------+----------+
| scenario         | required_count | observed_count | passed_count | decision |
+------------------+----------------+----------------+--------------+----------+
| all_pass         |              4 |              4 |            4 | READY    |
| approval_missing |              4 |              3 |            3 | BLOCKED  |
| fence_failed     |              4 |              4 |            3 | BLOCKED  |
+------------------+----------------+----------------+--------------+----------+

긴 CTE 원문 반복은 제외하고 실제 결과 표만 발췌했다. 모두 통과한 경우에만 READY이며, 실패 증거가 있거나 필수 증거가 빠진 경우에는 BLOCKED다. READY는 정의한 전제 조건이 충족되었다는 판정이며, SQL 자체가 트래픽을 전환하거나 쓰기를 허용하지는 않는다.

실제 운영에서는 복구 작업 ID, 대상 ID, 복구 경계, runbook 리비전, 확인자, 관측 시각, 유효기간과 증거 위치를 저장한다. 다른 복구 작업에서 얻은 오래된 성공 결과를 재사용하지 않는다. 검사별로 유일한 결과를 대응시키고, 중복이나 알 수 없는 상태도 거부한다. 위 SQL은 승인 시스템의 완성 구현이 아니라 미관측 상태를 성공으로 취급하지 않는 최소 판정 예제다.

5. 전환과 복귀에는 서로 다른 안전 조건이 필요하다

새 쓰기를 받기 전

복원 서버를 외부 배치와 연동하지 않은 채 데이터와 대표 업무를 확인한다. 시험 쓰기는 격리된 시험 계정과 데이터에 한정하고, 이메일·결제·메시지 발행 같은 외부 부작용은 차단한다. 단순 SELECT 1이나 테이블 행 수 비교는 서비스 검증을 대신하지 못한다. 중요한 테이블의 값, 참조 관계, 객체 정의, 권한과 실제 업무의 쓰기 후 재조회까지 확인해야 한다.

이 단계에서 검증이 실패하면 격리 상태를 유지하며 재복원할 수 있다. 다만 이미 원본을 차단했다면 원본 서비스 재개 역시 별도 승인 대상이다. 복구본을 버리는 작업과 원본을 다시 writer로 만드는 작업은 동일하지 않다.

새 쓰기를 받은 뒤

새 writer에 업무 커밋이 생기는 순간 단순 endpoint 원복은 안전한 되돌리기가 아니다. 이전 서버에는 새 변경이 없기 때문이다. 두 서버에 쓰기를 허용하면 동일 키 충돌뿐 아니라 정상 SQL에 의한 조용한 덮어쓰기도 발생할 수 있다.

새 대상에서 장애가 나면 쓰기를 다시 통제하고, 어느 서버의 어떤 업무 변경을 보존할지 판단해야 한다. 이전 서버로 복귀하려면 새 변경의 이관·대사와 재검증 또는 승인된 손실 처리 계획이 필요하다. runbook에는 첫 업무 쓰기 이전의 취소와 첫 업무 쓰기 이후의 복귀를 다른 절차로 둔다.

연결 전환도 DNS 레코드 수정 한 번으로 끝나지 않는다. 애플리케이션과 드라이버의 DNS 캐시, 기존 연결 풀, 장기 트랜잭션, 프록시 세션, 별도 배치의 직접 접속을 확인한다. 새 연결이 새 대상에 도달하는지와 오래된 연결에서 더 이상 쓰기가 발생하지 않는지를 각각 시험한다. 재시도 가능한 오류와 커밋 여부를 알 수 없는 오류를 구분하고, 멱등 키나 업무 대사로 중복 처리 위험을 관리한다.

6. Aurora MySQL에서는 관리형 복원과 서비스 전환을 분리한다

Aurora의 PITR은 원본을 그 자리에서 되감는 작업이 아니라 새 DB 클러스터를 생성하는 복원이다. 기존 클러스터의 failover와 새 클러스터로의 복구는 다른 시나리오이므로 같은 절차와 시간 기준으로 묶지 않는다.

공식 문서에 따르면 CLI/API로 PITR을 수행할 때는 복원 클러스터의 primary DB instance를 별도로 생성해야 한다. 또한 복원 시 기본 파라미터 그룹이 연결될 수 있으므로 필요한 사용자 정의 그룹을 명시하고 실제 적용 상태를 확인한다. 콘솔과 CLI/API의 절차 차이 자체를 리허설 대상으로 삼아야 한다.

Aurora용 runbook에서는 다음 증거를 명시한다.

  • 요청한 복구 시점과 EarliestRestorableTime·LatestRestorableTime의 범위 대조.
  • 대상 클러스터와 writer instance의 준비 상태, 새 endpoint와 애플리케이션 연결 확인.
  • DB cluster/instance parameter group, 네트워크·보안 그룹, 암호화 키와 접근 권한 확인.
  • 데이터베이스 계정·권한, 외부 자격증명, 프록시·배치 연결 대상의 일치 여부.
  • 원본 클러스터에 대한 쓰기 경로 차단과 새 클러스터의 업무 검증.

Aurora의 내부 스토리지 복구와 로그 처리는 서비스가 관리한다. Community MySQL의 데이터 디렉터리 복사나 서버 변수 변경 절차를 그대로 대입하지 않는다. 반대로 관리형이라는 이유로 외부 시스템 대사, 연결 전환, 데이터 손실 승인까지 AWS가 대신한다고 가정해서도 안 된다. 리전 단위 복구훈련이라면 백업뿐 아니라 대상 리전의 키·권한·네트워크·서비스 할당량과 애플리케이션 의존성도 사용할 수 있어야 한다.

이 글의 SQL은 로컬 MySQL 8.0 검증 서버에서 확인한 것으로, Aurora 복원이나 리전 장애를 실험한 결과는 아니다.

7. 실패를 주입하고 중단되는지도 검증한다

리허설을 매번 정해진 성공 경로로만 수행하면 승인 체계가 실제로 작동하는지 알 수 없다. 운영 자원을 훼손하지 않는 격리 환경에서 다음 조건을 하나씩 주입한다.

주입 조건 기대하는 대응 확인할 결함
백업은 있으나 복호화 권한이 없음 복원 시작 전 중단하고 대체 승인 경로로 인계 담당자만 아는 키 접근 절차
로그 연속성 또는 목표 경계가 불명확함 RPO 충족으로 선언하지 않고 경계 재확인 파일 존재만으로 적용 가능하다고 판단
필수 객체·업무 행이 누락됨 검증 실패 기록, 쓰기 개방 보류 서버 기동만으로 완료 처리
이전 writer 차단 증거가 없음 새 writer 개방 금지 접근 불가를 차단 완료로 오해
승인자 부재 또는 증거 유효기간 만료 대체 승인자 인계 또는 재검증 빈 결과나 오래된 PASS를 성공으로 처리
새 연결과 오래된 연결의 대상이 다름 단계별 연결 정리와 대상 확인 DNS 변경만으로 전환 종료

사후 보고에는 “성공” 한 단어 대신 각 단계의 시작·종료 시각, 실제 담당자, 승인 대기, 재시도, 데이터 손실 범위, 미검증 항목을 남긴다. 비밀정보를 지운 로그와 검사 결과는 장애로 사라질 수 있는 복구 대상 DB와 별도의 저장소에 보관한다. 훈련에서만 쓴 임시 권한과 자원은 정리하되 재현에 필요한 증거와 실패 원인은 보존한다.

발견된 문제는 담당자·기한·재시험 조건이 있는 개선 작업으로 전환한다. 문서를 고쳤다는 사실만으로 해결 처리하지 않는다. 수정된 runbook으로 대체 담당자가 같은 실패 지점을 다시 통과하거나 안전하게 중단하는지 확인해야 한다.

8. 실행 전후 점검표

훈련 시작 전

쓰기 개방과 종료 전

결론

재해복구 능력은 복원 명령을 아는 사람의 수가 아니라, 검증 가능한 복구 경계와 역할 인계가 문서 밖에서도 작동하는지에 달려 있다. runbook은 실행 방법과 함께 중단할 조건을 기록하고, 리허설은 정상 복구뿐 아니라 불완전한 증거를 안전하게 거부하는지도 시험해야 한다.

백업·PITR·데이터 검증을 이 절차에 연결하면 개별 기술이 실제 서비스 복구 능력이 된다. 이후 스키마 변경과 운영 자동화에서도 같은 원칙을 적용할 수 있다. 실행 전 대상과 경계를 고정하고, 성공 증거를 확인하며, 되돌릴 수 있는 시점을 명확히 구분하는 것이다.

참고 문서