Frame DB 복구
AI 요청 프롬프트
https://docs.axelabs.ai/ops/runbook/db-recovery 따라 frame DB 복구 진행해줘.
진행:
1. 현재 사고 진단 — 사용자 보고 증상으로 페이지 "시나리오 분류" 표의 5 시나리오 중 분기 식별 (migration / 사용자 실수 / macmini 손실 / silent corruption / postgres crash)
2. 즉시 대응 — frame container stop (사용자 추가 쓰기 차단) + restic 최신 snapshot 확인
3. 시나리오별 복구 절차 실행, 매 step 결과 받고 다음. destructive (drop database / restore from backup) 직전 사용자 확인
4. 함정 발생 시 페이지 본문 따라 우회 (Phase 5 stub `axe restore` 부재 → restic 직접 / silent corruption 의 부분 복원 범위 / migration rollback)
5. 정합성 검사 통과 (`integrity-check`) + frame 재기동 + (선택) /ops/known-gaps 사고 원인 한 줄본인 AI session = Claude Code / Cursor / ChatGPT 데스크탑 / Claude.app / 기타.
페이지 본문 = 사람이 직접 read 도 가능, AI 도 참고. AI 가 본 페이지 fetch 후 위 진행 순서대로 사용자와 step-by-step interactive 풀어나감.
frame 의 PostgreSQL 데이터가 손상되었을 때 복구 절차.
시나리오 분류
| 사건 | 즉시 대응 | 복구 경로 |
|---|---|---|
| Migration 사고 (alembic 적용 후 정합성 깨짐) | 모든 frame container stop | local backup (Tier A) |
| 사용자 실수 (잘못된 분개 대량 등록) | 해당 entity 만 freeze | reverse_journal × 다수 OR 백업 |
| macmini 전체 손실 (도난·화재) | 새 macmini 셋업 | ring (Tier B) 또는 cold SSD (Tier C) |
| Silent corruption (검증 실패) | 정합성 검사 결과 분석 | 부분 복원 |
| Postgres 자체 crash | docker restart frame-postgres | 거의 자동 복구 |
시나리오 1 — Migration 사고
새 migration 적용 후 정합성 검사 실패:
ssh axe-macmini "docker exec frame-mcp-blue python -m frame.cli integrity-check --entity axec"
# → FAIL: balance mismatch in journal X즉시
# 1. 모든 frame container stop (사용자 추가 쓰기 차단)
ssh axe-macmini "cd /Users/axe/frame && docker compose stop frame-mcp-blue frame-mcp-green"
# 2. Backup 최근 snapshot 확인
ssh axe-macmini "
restic -r /Users/axe/.axe/backups/local snapshots \
--password-file <(security find-generic-password -w -s axe.backup.restic.local)
"
# 어제 03:00 KST snapshot 찾기복구
# 3. 어제 backup 으로 frame-postgres 복원
# `axe restore` 는 Phase 5 stub 상태 → 현재는 restic 직접 호출:
ssh axe-macmini "
restic -r /Users/axe/.axe/backups/local \
--password-file <(security find-generic-password -w -s axe.backup.restic.local -a axe-cli) \
restore <snapshot_id> --target /tmp/restore --include 'frame-postgres/*'
"
# 그 후 docker exec frame-postgres pg_restore 또는 dump replay
# 정식 `axe restore` 자동화는 Phase 5 (D) 작업 항목
# 4. frame container restart (이전 코드 버전으로!)
ssh axe-macmini "cd /Users/axe/frame && git checkout <previous-good-commit>"
ssh axe-macmini "
cd /Users/axe/frame &&
set -a && source .env.local && set +a &&
docker compose build frame-mcp-blue &&
docker compose up -d --force-recreate frame-mcp-blue frame-mcp-green
"
# 5. 정합성 재확인
ssh axe-macmini "docker exec frame-mcp-blue python -m frame.cli integrity-check --entity axec"
# → OK손실 분석
복원 후 손실 = 백업 시점 (어제 03:00) ~ container stop 시점 사이의 모든 쓰기.
# 손실된 audit_log 추출 (다른 백업 또는 in-memory)
ssh axe-macmini "docker exec frame-postgres psql -U frame -d frame -c \"
SELECT * FROM axec.audit_log
WHERE ts > '2026-05-20 03:00:00+09:00'
ORDER BY ts;
\""사용자에게 통지 + 수동 재입력 또는 잘못 적용된 migration 의 의도된 효과 분석.
시나리오 2 — 사용자 실수
회계사가 실수로 잘못된 분개 50건 등록:
우선 — reverse_journal 시도
# Claude Code 에서
Frame:list_journals entity_id=axec, start_date=2026-05-20, status=posted
# 잘못된 journal_id 목록 확인 후
for jid in [...]:
Frame:reverse_journal entity_id=axec, journal_id=jid, memo="실수 역분"reverse_journal 은 새 journal 생성 (append-only). audit_log 에 모두 기록.
대량 (50건+) — 백업 복원
50건 넘으면 reverse 절차도 부담. 백업 복원이 더 깔끔:
# 1. 사용자의 다른 활동 멈추기 (claude.ai connector 비활성 요청)
# 2. 가장 가까운 valid backup 복원 — table-level restore 자동화 TBD
# 현재는 dump 전체 복원 후 손상 table 만 selective merge
# (정식 도구화 = Phase 5 D 항목)table-level 복원이 가능한지 확인 필요 — restic 자체는 file-level 만 → pg_dump 의 selective restore 필요. 향후 도구화 검토.
시나리오 3 — macmini 전체 손실
가장 심각. 새 macmini 에 처음부터 셋업.
절차
# 1. 새 macmini 준비
# - macOS 클린 install
# - Tailscale 가입 + 운영자 key 등록
# - Docker Desktop install
# - axe CLI install: bash <(curl -sSL https://docs.axelabs.ai/install/axe-cli.sh)
# 2. customers.yaml 의 entry 그대로 사용
# (운영자 머신에서 push)
# 3. 가장 신선한 백업 복원 (restic 직접 호출)
# ring peer (realchoice macmini) 측에서 pull:
ssh axe-macmini "
restic -r sftp:[email protected]:/Users/realchoice/peer-backups/axe \
--password-file <(security find-generic-password -w -s axe.backup.restic.local -a axe-cli) \
restore latest --target /tmp/restore
"
# 또는 cold SSD 마운트 후
ssh axe-macmini "
restic -r /Volumes/axe-cold-1/restic-repo \
--password-file <(종이메모) \
restore latest --target /tmp/restore
"
# 4. frame onboard (axe CLI 의 `--skip-azure` 플래그 는 향후 추가 예정;
# 현재는 onboard 가 Azure 측 변경 자체를 하지 않으므로 일반 onboard 그대로)
axe onboard axe --apply
# 5. Microsoft 측 — redirect_uri 변경 불필요 (axe.axelabs.ai 도메인 그대로)
# 6. 검증
axe health axe
ssh axe-macmini "docker exec frame-mcp-blue python -m frame.cli integrity-check --entity axec"
ssh axe-macmini "docker exec frame-mcp-blue python -m frame.cli integrity-check --entity axev"
# 7. 직원 통지
"frame 시스템 복구 완료. claude.ai 의 connector 는 그대로 작동합니다."시나리오 4 — Silent Corruption
매일 자동 integrity-check 가 잡아냄. 발견 즉시:
- 어느 entity, 어느 검사가 실패했는지 확인
- 가장 가까운 valid snapshot 찾기 (전날 또는 그 이전)
- table-level 또는 entity-level 복원
- 사용자에게 통지 (해당 기간 데이터 재입력 필요할 수도)
Restore Drill (분기별)
매 분기 (Jan/Apr/Jul/Oct 15) 자동 drill 이 실행:
- 임시 staging 컨테이너 (
frame-mcp-drill) 에 어제 backup 복원 integrity-check4개 모두 통과 여부- 결과 → 운영자 Slack
drill 실패 = backup 자체 문제 (압축 손상, password 오류 등). 즉시 원인 분석.
함정
| 함정 | 결과 | 회피 |
|---|---|---|
| 손상 발견 후 frame 미정지 → 추가 쓰기 누적 | 손실 확대 | 즉시 docker compose stop |
| ring peer 의 backup 도 손상되었을 가능성 | 양쪽 손상 | cold SSD 도 보유 |
| 복원 후 정합성 검사 안 함 | 잠재 손상 미발견 | integrity-check 필수 |
| pg_dump 의 schema-per-entity 옵션 누락 | 복원 시 entity schema 사라짐 | restic 의 raw file 복원 (PGDATA volume) |
⚠️ 현재 도구 상태
axe restore --customer ... --tier ... --target ... --table ... --apply 식의 통합 CLI 는 Phase 5 (D) 작업 항목 — 현재 stub 상태. 본 runbook 의 실제 복구는 위에서처럼 restic 직접 호출.
자동화 완성 후 본 runbook 의 명령어 갱신 예정 — 그때까지 운영자는 restic CLI 직접 + docker exec 조합 사용.