Stan 기술블로그

담당자가 없어도 1차 대응이 되게 — 운영 런북 네 개를 같은 모양으로

사내 인프라 몇 가지를 혼자 맡고 있다. 내가 자리에 없으면 아무도 손을 못 대는 구조, 말하자면 사람이 단일 장애점(SPOF)인 상태다. 이걸 깨려고 서비스 네 개(Sentry, Keycloak, Nginx Proxy Manager, TabbyML)의 운영 런북을 같은 모양으로 썼다. 쓰다 보니 권한이 없어 채우지 못한 칸이 계속 나왔고, 그 빈칸을 한데 모으자 성격이 바뀌었다.

(사내 인프라 특성상 서버 주소·사람 이름은 일반화했다.)


문서가 있어도 급할 때 못 쓰는 이유

지식이 한 사람 머릿속에만 있으면, 그 사람이 아프거나 휴가 중일 때 서비스가 멈춘다. 문서로 풀어 보려는 시도는 흔한데, 막상 급할 때 못 쓰는 문서가 많다. 이유를 세 가지로 봤다.

  • 서비스마다 모양이 다르다. 급한 순간에 “이 문서는 재시작이 어디 있지”를 찾는 데 시간이 든다.
  • 급할 때 볼 것과 깊이 이해할 것이 섞여 있다. 장애 원인을 분석한 긴 이야기 사이에서 재시작 명령을 찾게 된다.
  • 해당 없는 칸까지 다 채워 둔다. “해당 없음”이 줄줄이 있으면, 정말 비어 있어서 문제인 칸이 그 사이에 묻힌다.

네 런북, 같은 모양

네 런북 모두 이 뼈대를 따른다.

급할 때
  ├ 살아 있는지 확인     ← 한 줄 명령 + 무엇이 나와야 정상인지
  ├ 재시작               ← 부작용·다운타임이 있으면 같이
  ├ 증상별로 어디를 보나  ← 자주 나는 증상 → 트러블슈팅 문서 링크
  └ 내 선에서 안 되면     ← 죽으면 무엇이 막히나, 권한 밖이면 누구에게
평상시 운영
  ├ 어디에 무엇이 있나
  ├ 백업과 복구          ← 마지막으로 실제 복구해 본 날짜
  └ 바꿀 때 지킬 것
배경 지식
  ├ 구조와 의존 관계
  └ 관련 문서
아직 못 채운 것

지킨 규칙은 네 가지다.

“급할 때” 네 칸은 순서를 바꾸지 않는다

어느 서비스를 열어도 같은 자리에 같은 게 있어야 한다. 사람이 바뀌어도 같은 순서로 대응할 수 있게 하는 게 이 문서의 목적이고, 그러려면 내용보다 먼저 자리가 예측 가능해야 한다. 그래서 장애가 의심되면 “급할 때” 블록만 봐도 1차 대응이 되게 썼다.

마지막 칸 “내 선에서 안 되면”이 실제로는 가장 중요하다. 이 서비스가 죽으면 무엇이 같이 멈추는지, 내 권한 밖이면 누구에게 넘기는지가 여기 있다.

해당 없는 칸은 통째로 뺀다

TabbyML 런북에는 다른 런북 같은 백업·복구 절차가 없다. 인덱스는 다시 만들면 되고 계정과 설정은 소량이라, “따로 절차를 두지 않는다”는 이유 한 줄과 서버 백업에 넣을 파일 두 개만 적었다. “백업: 해당 없음”으로 칸을 채우지 않는다. 그래야 다른 런북의 “복구해 본 적 없음” 같은 진짜 빈칸이 눈에 띈다.

사고 이야기는 쓰지 않는다

원인을 어떻게 찾았는지 같은 이야기는 트러블슈팅 문서에 두고, 런북에서는 링크만 건다. 런북은 “지금 무엇을 하면 되는가”만 답한다.

모르면 모른다고 적는다

이게 이 작업에서 가장 중요한 규칙이 됐다. 런북을 쓰다 보면 “헬스체크 주소는 아마 이거겠지”, “재시작은 이렇게 하겠지” 같은 칸이 계속 나온다. 그걸 추측으로 채우는 순간, 그 문서는 틀린 답을 맞는 답처럼 담게 된다. 급할 때 그 추측을 믿고 움직이면 더 위험하다.

그래서 확실히 아는 것만 적고, 모르는 건 “아직 확인하지 못했습니다”라고 쓴 뒤 맨 아래 “아직 못 채운 것”에도 올렸다.

빈칸을 모았더니

런북 네 개를 다 쓰고 “아직 못 채운 것”만 모아 보니 열두 개였다. 그리고 거의 전부 서버 접근 권한이 있어야 채울 수 있는 칸이었다. 공통 패턴으로 묶으면 이렇다.

빈칸 해당 채우려면
백업을 실제로 복구해 본 적이 없다 Sentry, Keycloak, NPM 담당자와 30분. 스냅샷을 격리된 곳에 띄워 로그인되는지 한 번 확인
운영 서버가 어디서 어떻게 뜨는지 모른다 Keycloak 백엔드, 운영 NPM 호스트 이름·컨테이너 이름·재시작 방법을 공유받기
헬스체크가 정말 정상 응답을 주는지 모른다 Sentry, TabbyML 내부망에서 한 번 호출해 응답 코드 확인
크리덴셜이 어디 있는지 정리가 안 됐다 전체 파일마다 어느 서비스용인지 라벨만 붙이기 (값은 열 필요 없음)

하나씩 보면 그냥 “미확인 항목”이다. 그런데 한자리에 모으니 “이 서비스들의 운영 지식에서 어디가 비어 있는가”를 보여 주는 지도가 됐다. 그리고 그 지도의 대부분이 서버 권한으로 풀린다. “권한을 주세요”가 아니라 “이 칸들을 채우려면 이 권한이 필요하다”고 말할 수 있는 근거가 생긴 것이다. 처음부터 노린 결과는 아니었다. 추측으로 칸을 메웠다면 빈칸은 애초에 보이지 않았을 것이다.

같은 이유로 이 목록은 안정성 지표의 출발점도 된다. “마지막으로 복구를 실제로 해 본 날짜”는 지금 세 서비스 모두 없음이다. 출발점이 0이라는 걸 적어 둔 것 자체가, 앞으로 날짜를 채워 나갈 칸이 된다.

그 뒤

이름을 쉬운 말로 바꿨다. 처음에는 “🚨 긴급 카드 — 헬스체크 / 재시작 / 증상맵 / 에스컬레이션”, “§ 갭 / TODO”, “[확인필요]” 같은 이름을 썼다. 나중에 위 뼈대처럼 “급할 때 — 살아 있는지 확인 / 재시작 / 증상별로 어디를 보나 / 내 선에서 안 되면”, “아직 못 채운 것”으로 바꿨다. § 기호는 보기 불편하다는 이유로 아예 쓰지 않기로 했다(9월 29일).

맨 위에 범위를 적었다. 9월 말부터 절차 문서는 첫머리에 범위: 줄을 둔다. 예를 들어 Sentry 런북은 “상태 확인과 1차 대응. 서버 접속은 사전 승인 후, 재시작·설정 변경은 변경 전후를 보여 주고 승인 후”로 시작한다. 이 문서로 어디까지 해도 되는지가 맨 먼저 보인다.

빈칸 하나는 장애가 닫았다. Sentry의 헬스체크(/_health/)는 9월 28일 장애를 조사하면서 내부망에서 실제로 200이 나오는 걸 확인해 지웠다. TabbyML 쪽은 아직 남아 있다.

런북이 낡으면 알림이 온다. 매주 월요일 문서 점검 스크립트가 런북의 “마지막 확인” 날짜를 보고, 30일이 넘은 런북을 목록에 올린다.

마치며

진짜로 배운 것

  1. 대체 가능성은 내용보다 자리에서 나온다. 모든 런북에서 같은 칸이 같은 자리에 있다는 규칙 하나가, 서비스별 지식 몇 줄보다 더 크게 사람 의존을 줄인다.
  2. 모르는 건 비워 둔다. 추측으로 매끈하게 채운 문서는 완성돼 보이지만, 급할 때는 다시 검증해야 하는 지뢰밭이다. 정직하게 남긴 빈칸만이 나중에 지도가 된다.
  3. 빈칸은 모으면 성격이 바뀐다. 문서마다 흩어진 TODO가 한자리에 모이면, 권한을 요청할 근거이자 다음 작업 목록이자 안정성 지표의 출발점이 된다.
  4. “해당 없음”을 적느니 칸을 뺀다. 없는 칸을 채우면 정말 비어 있는 칸이 묻힌다.