6화

헬스체크

정하늘 시점

비 오는 밤 술집에서 동료들이 지켜보는 가운데 하늘의 잔에 사이다를 먼저 따르는 지안

1

정하늘이 입사 첫날 받은 것은 노트북과 사원증과 포스트모템 문서 네 개였다.

“네 개 읽고 와요.” 서지안 팀장은 그렇게만 말했다. “읽고 나서 질문 있으면 하고, 없으면 없다고 하고.”

하늘은 4월 12일 오후를 그 네 개로 보냈다. 9월 결제 장애, 작성자 서지안. 11월 쿠폰 장애, 작성자 서지안, 액션 아이템 담당 유도현. 1월 정산 장애, 작성자 강민석. 3월 플래그 장애, 작성자 아틀라스 팀장, 마지막에 강민석의 한 줄. 입사 안내에서 본 이름들이 다른 순서로 나왔다. 팀장 이름은 버그를 만든 사람 칸에도 있었고, 본부장 이름은 확인 없이 결론부터 보고한 사람 칸에 있었다.

질문은 하나였다.

“이 회사 장애는 다 사람이 나오네요.”

“그게 질문이에요?”

“장애에 사람이 안 나오는 경우도 있어요?”

지안은 잠깐 생각했다.

“아직 못 봤어요.”

2

7월 22일 목요일 오후 세 시 사십 분, 장마 사흘째였다. 하늘이 온콜을 맡는 세 번째 주였다. 지안은 대표 보고로 오후 내내 자리를 비웠다.

문서 폴더에는 이제 다섯 개가 있었다. 다섯 번째는 5월 단말기 장애, 작성자 정하늘. 두 번째 온콜 주에 인증서를 갱신하고 쓴 문서였다. 만료를 확인한 뒤 교체까지 걸린 22분을 적자 도현이 통화 기록을 보내왔고, 지안은 수동 발급 이력을, 민석은 담당자 없는 스프레드시트를 붙였다. 승인자 교대표와 대체 연락 절차는 그 뒤에 생겼다.

[P1] AZ kr-1a storage latency p99 4.2s · auto-failover triggered → kr-1b · 15:40:35 complete

하늘은 알림을 읽고 대시보드를 열었다. 클라우드 가용 영역 a의 스토리지가 느려졌고, 4월 초에 지안이 만든 자동 페일오버가 30초 만에 트래픽 전부를 b로 옮겼다. 결제 API 5xx는 15:40:05부터 15:40:40까지 35초 동안 튀었다가 0으로 돌아왔다. 클라우드 상태 페이지에도 “kr-1a 일부 노드 장애 조사 중”이 떴다. SEV 선언 기준의 지속 시간에는 못 미쳤다. 하늘은 인시던트 채널에 상황을 적었다.

런북을 열었다. “AZ 장애 시” 항목은 세 줄이었다. 1) 페일오버 상태 확인. 2) 자동 페일오버가 안 됐으면 수동 페일오버. 3) 클라우드 상태 페이지 모니터링. 하늘은 1번을 확인하고, 확인 차원에서 2번 명령도 쳤다.

$ failover --to kr-1b
already active: kr-1b (since 15:40:35). no-op.

아무 일도 하지 않는 명령이었고, 하늘은 그걸 채널에 그대로 붙였다. 15:43이었다.

15:52, 클라우드 상태 페이지가 “kr-1a 복구 중”으로 바뀌었다.

상태 패널에서 a의 HTTP 응답은 초록색이었다. 아래의 디스크 지연 그래프는 아직 높았다. 하늘은 두 패널의 마지막 갱신 시각을 확인했다. 둘 다 방금이었다. b가 받고 있으니 기다려도 되겠다고 생각하고 런북의 3번 옆에 시간을 적었다.

15:53:10, 결제 API p99가 다시 뛰었다. 이번엔 35초로 끝나지 않았다.

3

“인시던트 커맨더 제가 잡을게요.” SRE 부팀장 최은서가 16:05에 브리지 콜에 들어오면서 말했다. 지안은 대표실에 있었다. “하늘 씨, 지금 트래픽 어디 가고 있어요?”

“확인 중이에요. 페일오버는 b로 돼 있는데.”

“페일오버 말고 실제 트래픽이요. 라우팅 대시보드.”

하늘은 라우팅 대시보드를 열었다. 15:53:10부터 트래픽의 절반이 a로 가고 있었다.

“a로 50% 가고 있어요. 15:53부터요.”

“페일백이네.” 은서가 말했다. “a가 살아난 걸로 판정돼서 자동으로 돌아간 거예요. 근데 a가 안 살아났어. 하늘 씨, 수동으로 b 100% 고정하고 페일백 끄세요. 승인 검토는 저하고 도현 님이에요. 제가 부를게요.”

도현은 자리에 있었다. 은서와 도현이 대상 영역과 고정 설정을 확인했다. 두 사람의 승인은 16:14에 모였고, 하늘이 명령을 쳤고, 16:16에 트래픽이 전부 b로 돌아갔다. p99는 16:20에 정상이 됐다.

장애 시간 27분. 15:40의 35초는 따로였다.

브리지 콜이 끝나고 하늘은 자리에 앉아 있었다. 15:43에 자기가 친 명령이 계속 걸렸다. no-op이었다. 아무 일도 안 했다고 명령이 스스로 말했다. 그런데 10분 뒤에 페일백이 돌았다. 하늘은 1월 문서의 2027-00을 떠올렸다. 한 줄만 보면 몰랐다. 요청과 그 뒤에 벌어진 일을 맞춰봐야 했다.

하늘은 로그를 끝까지 읽기로 했다.

4

첫 번째는 자기 명령이었다. 페일오버 컨트롤러 로그에 15:43:22 failover --to kr-1b 요청과 no-op 응답이 있었다. 상태 변경 없음. 페일백 타이머 리셋 없음. 정말로 아무 일도 안 한 것이었다. 하늘은 그걸 세 번 확인했다. 확인할수록 안심이 됐고, 안심이 되는 게 이상했다.

두 번째는 페일백이었다. 컨트롤러는 원래 AZ가 “5분 연속 정상”이면 트래픽을 절반씩 되돌린다. a의 헬스체크 이력을 열었다. 15:40:05부터 15:48:09까지 실패. 15:48:10부터 정상. 5분 뒤인 15:53:10에 페일백. 정확했다. 컨트롤러는 설계대로 움직였다.

그러니까 문제는 헬스체크가 15:48:10부터 a를 정상이라고 본 것이었다. 15:48부터 16:16까지 a의 스토리지 지연은 p99 3.8초였다. 정상이 아니었다. 그런데 헬스체크는 정상이라고 했다.

헬스체크 정의를 열었다. GET /healthz 응답 200이면 정상. 그게 다였다. 노드는 살아 있었다. HTTP는 응답했다. 디스크가 50배 느린 것은 HTTP 200에 나오지 않았다.

하늘은 4월에 지안이 처음 만든 헬스체크 정의를 깃 이력에서 찾았다. 두 개였다. GET /healthz 200, 그리고 disk-probe: 4KB 쓰기 지연 50ms 이하. 두 번째가 지금은 없었다.

git log -p로 그 줄이 사라진 커밋을 찾았다.

#3120 · health: disk-probe 제거 · 2027-04-14 · author: 정하늘 · reviewer: 서지안 “disk-probe가 하루 두세 번 false positive로 실패해서 알림이 옴. flaky하여 제거.” 리뷰 코멘트: “OK. 첫 PR 축하 🎉”

하늘은 화면을 한참 봤다. 입사 사흘째에 올린 PR이었다. 온보딩 중에 지켜보던 채널에 새벽 알림이 두 번 남아 있었고, 사용자 오류는 없었다. 알림 피로가 온콜을 망친다는 얘기를 듣고, 없애도 되는 알림을 찾았다고 생각했다.

당시 첨부한 측정값을 다시 열었다. 62ms, 71ms. 프로브는 임계값 50ms를 넘었다고 정확히 말하고 있었다. 그 정도의 순간 지연을 장애로 볼지는 조정할 수 있었다. 하늘은 조정하는 대신 검사 자체를 뺐다. 그리고 지안이 축하했다.

세 번째는 런북이었다. “AZ 장애 시” 항목에 페일백이라는 단어가 없었다. 4월에 자동화를 붙이면서 페일오버 확인 절차는 고쳤지만, 돌아오는 조건은 설계 문서에만 적었다. 하늘은 아직 그 문서를 읽은 적이 없었다. 은서는 4월 도입 작업에 참여한 사람이었다. 런북에서 찾을 필요가 없었다.

네 번째는 클라우드였다. 15:48부터 a의 HTTP는 다시 응답했지만 디스크는 여전히 느렸다. 노드가 왜 그렇게 됐는지는 자기들 로그에 없었다. 클라우드 쪽 포스트모템은 다음 주에나 나올 것이었다.

하늘은 조사한 순서대로 초안을 썼다. 자기 명령은 상태를 바꾸지 않았다. 컨트롤러는 정상 판정 뒤 5분 만에 트래픽을 되돌렸다. 그 판정에서 디스크 검사를 뺀 건 자기 PR이었다. 런북에는 되돌아오는 동작이 빠져 있었다. 클라우드 장애 원인 칸에는 ‘조사 결과 대기’라고 썼다.

5

17:30, 하늘은 초안을 인시던트 채널의 입력창에 붙여넣었다. PR 링크도 붙였다. 마지막 줄은 “제 PR이고, 리뷰어는 팀장님입니다”였다.

보내기 전에 PR 링크를 빼고 싶었다. 팀장에게 먼저 보여드릴까. 커서를 링크 앞으로 옮겼다가 멈췄다. 9월 문서에는 2주가 적혀 있었다. 11월 문서에는 주말이 적혀 있었다. 하늘은 시계를 봤다. 두 번째 장애가 시작된 지 아직 2시간이 안 됐다. 링크를 둔 채 전송을 눌렀다.

답은 3분 안에 셋이 왔다.

17:32 유도현 PR 확인했어요. 오늘 지연 기록 붙여서 프로브 복구안 같이 만들죠. 17:33 서지안 리뷰어 맞아요. 임계값은 제가 잡았어요. 조정 검토 없이 제거를 승인한 경위도 붙일게요. 17:33 강민석 이 초안으로 내일 회의 잡겠습니다. 미확인 항목은 결과 나오는 대로 보완합시다.

그리고 조금 뒤에 하나 더.

17:41 서지안 하늘 씨. 입사 첫날 질문 기억나요? 사람이 안 나오는 장애. 17:41 정하늘 네. 17:42 서지안 클라우드 원인 칸은 지금처럼 둬요. 우리 PR은 확인했지만 그쪽에서 무슨 일이 있었는지는 아직 몰라요.

하늘은 그 메시지를 저장했다.

6

목요일 밤 열한 시 이십오 분, 지안에게서 메시지가 왔다.

열한 시 반. 회사 뒷골목. 온콜이면 사이다.

하늘은 온콜이었다. 비가 오고 있었다. 우산을 쓰고 골목을 찾아갔다. 5월 문서에 술집 이름은 없었지만 골목은 있었다.

유리문 안에 넷이 있었다. 테이블 두 개를 붙여놓고 있었다. 지안, 도현, 민석, 그리고 하늘이 얼굴만 아는 사람. 스위치보드에서 온 유리 씨였다. 3월 문서에는 이름이 없었지만 도현이 언젠가 말해줬다.

문을 열었다. 넷이 동시에 하늘을 봤다. 사장님이 사이다 한 병과 잔을 가지고 왔다. 하늘이 앉기 전이었다.

“온콜이죠.” 지안이 말했다.

“네.”

지안이 사이다 병을 집었다. 그리고 하늘의 잔을 먼저 채웠다. 그다음 소주병을 집어 도현, 민석, 유리, 자기 잔 순서로 채웠다.

도현이 그 순서를 봤다.

“순서 바뀌었네요.”

“포스트모템 쓴 사람 먼저.” 지안이 말했다.

하늘은 사이다를 마셨다. TV에서는 야구 대신 “우천 취소”라는 자막이 떠 있었다. 사장님은 그래도 TV를 끄지 않았다.

“클라우드 보고서가 오면요.” 하늘이 물었다. “거기에도 누가 있을까요.”

지안은 잔을 비우고 나서 대답했다.

“읽어봐야 알겠죠.”

하늘은 고개를 끄덕였다. 사이다가 아직 반 잔 남아 있었다.