Prometheus absent()로 알람 걸 때, unless도 같이 봐야 하는 이유

오늘 알게 된 건데, 이거 모르고 쓰는 분이 꽤 많더라. absent() 함수. Prometheus에서 "메트릭이 사라졌을 때" 알람 거는 그 함수 말이다.
뭐가 문제냐면
우리 팀이 최근에 배치 job 상태를 감시하려고 이런 룰을 넣었다.
- alert: JobMetricMissing
expr: absent(batch_job_last_success_timestamp)
for: 5m
문제는 이거다. 배치가 서버 2대에 떠 있고, 한쪽 서버만 죽었을 때 이 알람이 안 울린다. absent()는 "메트릭 전체가 사라졌을 때"만 1을 반환한다. 나머지 한 서버가 살아있으면 시리즈는 여전히 존재하니까 조용하다. 근데 우리는 인스턴스 단위로 놓치고 싶지 않았다.
unless가 답인 경우
이럴 땐 unless를 쓴다.
- alert: JobInstanceDown
expr: |
up{job="batch"} == 1
unless
batch_job_last_success_timestamp
for: 10m
이건 이렇게 읽으면 된다. "up이 1인 인스턴스 중에서, batch_job_last_success_timestamp가 없는 라벨 셋만 남겨라." 라벨이 매칭되는 우변이 있으면 좌변을 뺀다. 인스턴스별로 각각 판별되니까 한쪽만 죽어도 잡힌다.
여기서 up == 1 조건을 왜 붙였냐. 서버 자체가 다운돼서 scrape 실패한 인스턴스는 다른 알람(TargetDown 같은)이 이미 잡고 있을 거다. 이중으로 페이지 받기 싫어서 걸러낸다.
그럼 absent()는 언제 쓰나
메트릭이 정말 하나도 없는 상황을 잡고 싶을 때. 예를 들어 신규 배포 후 exporter가 아예 안 뜨는 경우, 라벨 조합이 완전히 사라진 경우. 이럴 땐 라벨을 명시해서 쓴다.
expr: absent(kube_deployment_status_replicas_available{deployment="checkout-api"})
이렇게 하면 알람이 발동될 때 라벨 정보도 살아있어서 어떤 deployment가 문제인지 바로 보인다. 인자로 넘긴 라벨 매처가 그대로 결과에 붙기 때문이다. 이게 은근 편하다.
정리하면
absent()는 "이 시리즈가 통째로 없어졌냐"고, unless는 "각 라벨 셋 중 매칭 안 되는 게 있냐"다. 착각하기 쉬우니 알람 만들 때 한 번 더 확인하자. 우리 팀도 이거 잘못 걸어놓고 반년 동안 조용히 배치 하나가 죽어있었다. 아직도 그때 생각하면 좀 억울하다.
혹시 다른 방식으로 missing metric 알람 거시는 분 있으면 어떻게 하는지 궁금하다.