← 개발 로그 목록

website: 알람 오면 뭘 해야 하는지 찾아 헤매던 문서를 러너북으로 정리하기

/ 5분 분량 / 개발 로그

인프라 알람이 울렸을 때 뭘 해야 하는지가 CLAUDE.md, db-access.md, observability.md 여기저기 흩어져 있었다. 디스크 알람이 오면 이 문서, 백업 알람이 오면 저 문서를 뒤져야 했고, 심지어 어떤 절차는 실제로 실행해본 적 없는 "이론상 맞을 것 같은 명령"으로만 남아 있었다. 알람이 새벽에 오든 낮에 오든 매번 "이게...

그래서 알람별로 경고 → 영향 → 원인 후보 → 복구 순서를 고정해서 하나의 문서(RUNBOOK.md)로 몰아넣기로 했다. 디스크 80%, 메모리 85%, 백업 부재(prod/stage), git 드리프트, UptimeRobot DOWN까지 다섯 가지 알람 전부를 이 형식으로 정리했는데, 여기서 원칙을 하나 세웠다. 문서에 적는 명령은 전부 실제로 서버(ssh likelion-oci)에 접속해서 한 번씩 돌려보고 결과까지 확인한 것만 넣는다는 것이다. 이 과정에서 docker image prune -a를 무심코 실행하려다가 시스템이 막아준 적이 있었는데, -a는 실행 중이지 않은 이미지를 전부 지우는 옵션이라 수동 롤백에 필요한 이전 태그 이미지까지 날아갈 뻔했다. 그래서 러너북에는 -a 없이 dangling 이미지만 지우는 명령으로 못박아뒀다.

문서를 쓰다 보니 "알람 대응"과 다른 성격의 내용이 같이 섞여 있는 걸 발견했다. 이 역할의 마인드셋, 뭘 지표로 볼지, 인수인계 체크리스트, 계정 인벤토리 같은 것들은 알람이 왔을 때 즉시 참고할 절차가 아니라 이 역할을 처음 맡거나 넘길 때 필요한 오리엔테이션 성격이었다. 이걸 같은 문서에 계속 두면 "지금 뭘 해야 하는지"를 찾는 사람도, "이 역할이 뭔지" 알고 싶은 사람도 둘 다 불편해질 것 같아서 handoff.md로 분리했다. ONS 토픽 알림이 결국 개인 메일 구독 하나로 수렴한다는 것, OCI 콘솔 권한과 서버 SSH 권한이 완전히 별개라는 것처럼 인수인계할 때마다 매번 헷갈릴 만한 지점들을 계정 인벤토리 표로 따로 정리해뒀다.

DB 복원 절차를 쓰는 과정에서는 예상 못 한 구멍을 하나 발견했다. 백업 스크립트는 업로드만 하지 다운로드 기능이 없었다. 복원 절차를 문서로 남기려면 실제로 복원까지 한 번 돌려봐야 하는데 그럴 수단 자체가 없었던 것이다. 그래서 backup_manager.py(기존 backup_upload.py에서 이름을 바꾸며)에 list(백업 목록 조회)와 get(다운로드) 명령을 추가했다. 이걸로 실제 복원까지 재현해보다가 더 큰 문제를 찾았는데, 2026-07-24 이전 백업을 복원하면 앱이 아예 기동을 못 한다는 것이었다. 원인은 Flyway 도입 시점(2026-07-23)의 baseline 처리 방식에 있었다. 도입 당시 스키마를 "V1까지는 이미 적용된 걸로" 치고 건너뛰게 해뒀는데, 이건 지금 DB가 실제로 그 V1 모양과 같다는 걸 그냥 전제한 것이었다. 도입 이전 백업(2026-07-17 stage)으로 실제 복원을 재현해보니 V2 마이그레이션이 no such column: failed_login_attempts 에러로 죽으면서 기동 자체가 실패했다. 반대로 07-24 이후 백업은 정상 기동까지 확인했다. 그래서 러너북에 "복원할 백업은 항상 2026-07-24 이후로만 고를 것"이라는 경고를 명시적으로 박아뒀다.

복원 절차 자체도 두 버전으로 나눴다. 기본 절차는 서비스를 잠깐 멈추고 DB 파일을 바꿔치기하는 방식이라 재기동까지 20~30초 다운타임이 생긴다. 트래픽이 있는 시간대에 이 끊김도 피하고 싶은 경우를 위해, 기존 컨테이너는 그대로 켜둔 채 복원된 데이터를 담은 컨테이너를 옆에 하나 더 띄우고 헬스체크가 통과하면 nginx만 그쪽으로 reload하는 무중단 절차를 따로 만들었다. 이건 override 컴포즈 파일로 임시 서비스를 얹는 방식인데, 2026-07-27에 실제 stage 트래픽으로 전환 전후 UptimeRobot 로그가 끊김 없이 200을 받는 것까지 확인했다. 다만 이 절차는 nginx 설정을 전환할 때 한 번, 되돌릴 때 한 번 총 두 번 고치고 매번 reload해야 해서 단순 복원보다 손이 두 배로 간다는 걸 문서에 그대로 적어뒀다 — 급한 마음에 되돌리기 단계를 건너뛰고 바로 정리로 넘어가면, 그 시점엔 이미 옛 컨테이너를 내린 뒤라 되돌릴 대상 자체가 없어지기 때문이다.

문서를 여러 개 새로 만들다 보니 infra/ 루트가 지저분해지는 것도 눈에 띄었다. Claude Code가 디렉터리별로 자동 로드하는 CLAUDE.md/AGENTS.md만 루트에 남기고, 나머지 문서는 infra/docs/로, 실행되는 스크립트는 infra/scripts/로 옮겨서 스크립트와 문서를 분리했다. 이 김에 CI-CD.md도 새로 만들어서 cd.yml이 실제로 어떤 순서로 도는지(빌드 → 배포 → 헬스체크 → 스모크 테스트 → 확정/롤백) 다이어그램으로 남겼다. 특히 이전 태그를 지우는 시점이 헬스체크 통과 직후가 아니라 스모크 테스트까지 통과한 뒤여야 하는 이유는 실제로 겪은 사고(#133 승격 중 마커를 너무 일찍 지워서 롤백이 스킵된 것)에 근거한 것이라 그 이유도 같이 적어뒀다.