Prometheus absent 알람, 이거 모르고 쓰면 새벽에 안 울린다
오늘 알게 된 건데, 아니 정확히는 어제 새벽 4시쯤 깨달은 건데, absent() 알람을 그냥 쓰면 staleness 때문에 정말 중요한 순간에 침묵할 수 있다. 이거 모르는 분 꽤 많더라. 우리 팀도 6개월째 이 룰을 쓰고 있다가 한 번 데였다.
무슨 일이 있었나
배치 잡 하나가 죽었다. 정확히는 메트릭을 push하는 사이드카가 OOM으로 재시작되면서 job_last_success_timestamp 시리즈가 사라졌다. 알람 룰은 이렇게 생겼었다.
- alert: BatchJobMissing
expr: absent(job_last_success_timestamp{job="nightly-etl"})
for: 10m
근데 안 울렸다. 왜냐, Prometheus 3.x부터(사실 2.x 후반부터지만) staleness 처리가 바뀌면서 absent()는 마지막 샘플이 stale로 마킹된 시점부터 5분 정도까지는 시리즈가 "존재한다"고 본다. 그러다가 stale marker가 사라지면 그제서야 absent가 1이 된다. 문제는 우리 룰의 for: 10m은 그 시점부터 다시 카운트를 시작한다는 거다. 결과적으로 알람이 울리는 데 15분 넘게 걸렸고, 그 사이 ETL은 죽은 채 다음 다운스트림이 도미노로 무너졌다.
해결: absent_over_time 써라
absent_over_time()은 staleness 마커를 무시하고 지정한 윈도우 안에 샘플이 있었는지만 본다. 즉, 이렇게 바꾸니까 깔끔해졌다.
- alert: BatchJobMissing
expr: absent_over_time(job_last_success_timestamp{job="nightly-etl"}[10m])
for: 2m
[10m] 윈도우 안에 샘플이 하나도 없으면 즉시 1이 되고, for: 2m은 단순한 스크레이프 흔들림 방지용. 시리즈가 stale 처리되든 말든 window 안에 진짜 데이터가 있었느냐만 본다. 사실 내부적으로 absent_over_time은 range vector를 받기 때문에 staleness 핸들링 로직 자체가 다르다.
한 가지 잊지 말 것. for 절은 반드시 넣어야 한다. Prometheus가 막 시작했을 때는 아무 메트릭도 없으니 모든 absent 알람이 일제히 터진다. for: 2m 정도면 prom 재시작 직후 스크레이프 한 번 돌 시간은 확보된다.
정리하면
absent()는 "현재 시리즈가 없다"를 판단하는데 staleness 처리에 묶여서 반응이 느릴 수 있다. 배치/크론처럼 메트릭이 듬성듬성 들어오는 워크로드에는 거의 항상 absent_over_time()이 정답이다. 우리 팀 룰은 이제 전부 후자로 갈아엎는 중이다. 혹시 더 좋은 패턴 쓰시는 분 있으면 댓글로 알려주세요.