회사 테크

[monitoring] "마지막 갱신 시각"으로 감시하면 오탐 나는 경우 해결법

정상 동작 중에는 변하지 않는 값을 감시 지표로 쓰면 시간이 지날수록 반드시 울립니다. 오탐을 만든 지표와, 그것을 고치다 정탐을 놓친 이야기입니다.

삽질하는개발자

정상일 때 변하지 않는 지표와 일이 될 때 변하는 지표를 좌우로 대비시켜 보여주는 표지 이미지

경보가 왔습니다.

WARNING · 로그인 갱신 28.3시간째 없음

문자 발송 에이전트가 외부 시스템에 로그인한 지 28시간이 넘었다는 뜻이었습니다. 세션이 끊긴 것으로 보였습니다.

실제로 확인해보니 전부 정상이었습니다.

확인 항목
미발송 큐 0건
어제 발송량 51건 → 96건 (45건 추가 발송)
최근 발송 몇 분 전
에이전트 상태 정상, systemd active

발송은 계속되고 있었습니다. 경보가 틀렸습니다.

그 값은 원래 안 변합니다

원인은 제가 고른 지표에 있었습니다.

에이전트가 상태 파일에 LoginTime 을 남기는데, 이건 세션을 “처음 맺은” 시각입니다. 세션이 유지되는 동안에는 갱신되지 않습니다. 하루 전에 로그인해서 지금까지 잘 쓰고 있다면 그 값은 하루 전 그대로인 것이 정상입니다.

거기에 26시간 임계를 걸어뒀습니다. 그러니 세션이 정상적으로 오래 유지될수록 반드시 울리는 구조였습니다. 정상 상태를 이상으로 판정하는 지표를 만들어 둔 셈입니다.

이런 값들이 생각보다 많습니다.

  • 세션·연결을 수립한 시각
  • 프로세스 시작 시각
  • 설정을 마지막으로 변경한 시각
  • 인증서를 발급받은 시각

전부 “최근에 무언가 있었는지”를 알려줄 것 같지만, 정상 동작 중에는 변하지 않습니다. 값이 오래됐다는 것은 오히려 아무 일도 없이 잘 돌아갔다는 뜻일 수 있습니다.

지표는 “정상일 때 변하는가”로 고릅니다

경보를 설계할 때 던져야 할 질문은 하나입니다.

이 값은 시스템이 제 일을 하고 있을 때 변하는가?

변하지 않는다면 그 값의 나이는 건강 지표가 아닙니다. 이 에이전트에서 실제로 계속 변하는 값은 따로 있었습니다. 마지막으로 문자를 보낸 시각입니다. 발송이 멈추면 그 값이 늙습니다. 그게 진짜 신호입니다.

지표를 바꿨습니다.

항목 이전 이후
세션 수립 시각 26시간 넘으면 경보 경보 제거, 정보로만 표시
마지막 발송 시각 없음 48시간 넘으면 경보
미발송 큐 없음 정체 시 경보 (핵심 지표)
프로세스·런타임 상태 있음 유지

핵심 지표를 미발송 큐로 옮긴 것이 가장 큰 변화입니다. 문자 발송 시스템에서 “일이 되고 있는가”를 가장 직접적으로 말해주는 숫자이기 때문입니다. 큐가 쌓이면 원인이 무엇이든 문제이고, 큐가 비어 있으면 대체로 괜찮습니다.

이런 접근은 다른 곳에서도 통합니다. 디스크가 차는 문제를 감시할 때도 사용률이 아니라 차오르게 만드는 원인을 직접 재는 편이 훨씬 이르게 잡힙니다. 그 사례는 안 줄어드는 pg_wal, 복제 슬롯부터 확인하기에 적어뒀습니다.

정상 동작 중에 변하지 않는 지표와 변하는 지표를 좌우로 나누어 예시와 함께 비교한 도표

오탐을 고치다 정탐을 놓쳤습니다

여기서 끝났으면 좋았을 텐데, 비슷한 시기에 반대 방향의 실수를 했습니다.

다른 점검 스크립트에서 정지된 서버까지 “접속 실패”로 경보가 나가고 있었습니다. 서버를 정리하는 중이라 정지 상태가 늘어나 매번 울렸습니다. 그래서 필터를 넣었습니다. 가상화 API로 실행 중인 게스트 목록을 받아와, 목록에 없으면 점검에서 제외하도록 했습니다.

논리는 맞았는데 이름 매칭이 어긋났습니다.

  • 물리 서버 한 대는 애초에 가상화 목록에 없습니다. 그래서 “정지 중”으로 잘못 제외됐습니다
  • DB 노드 두 대는 표시명과 가상화 시스템의 실제 게스트명이 달랐습니다. 역시 제외됐습니다

그 결과 DB 클러스터 세 대가 통째로 점검에서 빠졌습니다. 그런데 그때 그 클러스터에는 실제 문제가 있었습니다. 필터가 대상을 삼키면서 CRITICAL이 조용히 사라지고 점검 결과가 OK로 바뀌었습니다.

경보가 안 오니 아무 일도 없어 보였습니다. 이게 감시에서 가장 위험한 실패 형태입니다. 오탐은 시끄러워서 금방 고치게 되지만, 필터가 삼킨 정탐은 아무 소리도 내지 않습니다.

수정은 목록 구조를 바꾸는 것이었습니다. 표시명과 실제 게스트명을 따로 들고, 가상화 게스트명이 없으면 물리 서버로 간주해 항상 점검하도록 했습니다.

필터를 넣을 때는 걸러진 것을 출력하세요

이 두 사건에서 남은 규칙은 간단합니다.

제외 조건을 넣었으면, 무엇이 제외됐는지 로그로 남기고 한 번은 눈으로 확인합니다. “몇 건을 점검했다”가 아니라 “무엇을 점검하지 않았다”를 보는 것입니다. 이번에도 제외 목록을 출력해보고서야 물리 서버와 DB 노드가 빠져 있는 것을 알았습니다.

그리고 감시를 새로 넣거나 고쳤다면 한 번은 일부러 울려봐야 합니다. 임계치를 잠깐 낮춰서 경보가 실제로 발생하는지 확인하고 되돌립니다. 그래야 그다음의 “0건”이 정상이라는 뜻인지, 아니면 점검이 조용히 실패하고 있는 것인지 구분됩니다.

정리

  1. 정상 동작 중에 변하지 않는 값의 나이는 건강 지표가 아닙니다. 세션 수립 시각, 프로세스 시작 시각이 대표적입니다
  2. “일이 되고 있는가”를 가장 직접적으로 말해주는 숫자를 핵심 지표로 둡니다. 큐 길이, 마지막 처리 시각 같은 것들입니다
  3. 제외 필터는 걸러낸 대상을 출력합니다. 필터가 삼킨 경보는 아무 소리도 내지 않습니다
  4. 새 감시는 한 번 일부러 울려보고 되돌립니다

경보가 자주 울리면 사람들은 그것을 무시하기 시작합니다. 오탐을 줄이는 일은 정확도의 문제가 아니라 경보 전체의 신뢰도를 지키는 일입니다. 다만 그 과정에서 조건을 좁히다 보면 진짜 신호까지 함께 잘려나가기 쉽습니다. 두 실수는 반대 방향으로 보이지만 뿌리는 같습니다. 지표가 무엇을 의미하는지 확인하지 않은 것입니다.