Blog
prometheusalertingalertmanagerpromqlobservabilitypromtool

Prometheus Alert Rule 작성 방법 — expr, for, labels, annotations부터 테스트까지

Prometheus 알림 규칙을 제대로 작성하는 법을 정리합니다. 규칙 파일 구조와 alert/expr/for/labels/annotations 5요소, pending→firing 상태, 템플릿(annotation) 작성, 좋은 expr 패턴, InstanceDown·에러율·지연·디스크 예측 같은 실전 예제, recording rule, 그리고 promtool 검증·유닛 테스트와 운영 팁까지 다룹니다.

Data Dynamics2026年6月12日14 min read
This post is not yet translated. The original Korean version is shown below.

메트릭을 수집하는 목적의 절반은 "문제가 생겼을 때 알림을 받기" 위해서입니다. Prometheus 에서 그 알림의 조건 을 정의하는 것이 Alert Rule(알림 규칙) 입니다. 규칙을 잘 쓰면 진짜 장애만 깔끔하게 울리고, 못 쓰면 새벽마다 무의미한 알림에 시달립니다.

이 글에서 다루는 내용:

  • 알림 규칙의 평가 흐름과 파일 구조
  • 규칙 한 개의 5요소: alert / expr / for / labels / annotations
  • pending → firing 상태와 flapping 방지
  • annotation 템플릿, 좋은 expr 작성법
  • InstanceDown·에러율·지연·디스크 예측 등 실전 예제
  • recording rule, promtool 검증·유닛 테스트, 운영 팁

1. Alert Rule이란

Prometheus 는 설정한 주기(evaluation_interval)마다 각 규칙의 expr 을 평가합니다. 결과가 나오면(=조건 충족) 알림 후보가 되고, 지정한 for 시간만큼 조건이 유지되면 firing 상태가 되어 Alertmanager 로 전송됩니다. 알림의 그룹핑·중복제거·라우팅·발송(Slack·이메일 등)은 Alertmanager 의 몫입니다.

[메트릭] → Prometheus가 expr 평가(주기적)
        → 조건 충족 + for 경과 → firing
        → Alertmanager로 전송 → 그룹핑/라우팅 → Slack·Email·PagerDuty

참고로 규칙에는 두 종류가 있습니다. Alerting rule(알림 조건)과 Recording rule(자주 쓰는 식을 미리 계산해 새 시계열로 저장). 이 글의 주제는 전자이며, 후자는 9장에서 함께 다룹니다.

2. 규칙 파일 구조

규칙은 별도 YAML 파일에 groups 로 묶어 작성하고, prometheus.ymlrule_files 로 불러옵니다.

# prometheus.yml
global:
  evaluation_interval: 15s        # 규칙을 얼마나 자주 평가할지
 
rule_files:
  - "rules/*.yml"
 
alerting:
  alertmanagers:
    - static_configs:
        - targets: ["localhost:9093"]
# rules/app-alerts.yml
groups:
  - name: app-availability         # 그룹 이름(같은 그룹은 순차 평가)
    interval: 30s                  # (선택) 이 그룹만의 평가 주기
    rules:
      - alert: InstanceDown
        expr: up == 0
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "인스턴스 다운: {{ $labels.instance }}"
          description: "{{ $labels.job }} / {{ $labels.instance }} 가 5분 이상 응답하지 않습니다."

같은 그룹 내 규칙은 위에서 아래로 순차 평가되므로, recording rule 의 결과를 같은 그룹의 다른 규칙이 참조할 수 있습니다.

3. Alert Rule 한 개의 구조 (5요소)

필드역할필수
alert알림 이름(=alertname 라벨)Y
expr알림 조건이 되는 PromQL. 결과가 하나라도 나오면 그 시계열마다 알림 후보Y
for조건이 이 시간 동안 연속 유지돼야 firingN
labels알림에 덧붙는 라벨(라우팅·심각도). expr 결과 라벨과 합쳐짐N
annotations사람이 읽는 설명(템플릿 가능). 라우팅에는 안 쓰임N

핵심은 expr 이 반환한 시계열 하나하나가 개별 알림 인스턴스라는 점입니다. up == 0 이 인스턴스 3개에서 참이면, 알림 3건이 각각 instance 라벨을 달고 만들어집니다.

4. for와 알림 상태(state)

알림은 세 가지 상태를 오갑니다.

  • inactiveexpr 결과 없음(조건 미충족)
  • pending — 조건은 충족됐지만 for 시간이 아직 안 지남
  • firingfor 동안 조건이 연속 유지됨 → Alertmanager 로 발송
inactive ──(조건 충족)──▶ pending ──(for 경과)──▶ firing
   ▲                          │                      │
   └────(조건 해제)───────────┴──────────────────────┘

for 가 없으면 한 번만 조건을 만족해도 즉시 firing 되어, 순간 스파이크에도 알림이 울렸다 꺼졌다(flapping) 합니다. 대부분의 규칙에 for: 5m~for: 15m 정도를 두는 것이 노이즈를 크게 줄입니다. pending 중에 조건이 한 번이라도 풀리면 타이머는 0으로 리셋됩니다.

활성 알림은 ALERTS{alertname=..., alertstate="pending|firing", ...} 메트릭으로도 조회할 수 있어, 알림 자체를 모니터링할 수 있습니다.

5. labels — 라우팅과 심각도

labels 는 알림에 의미를 부여하고, Alertmanager 가 이 라벨로 라우팅·그룹핑·억제(inhibition) 를 결정합니다. 가장 흔한 것이 severity 입니다.

labels:
  severity: critical      # critical / warning / info 등 일관된 체계로
  team: platform          # 어느 팀으로 보낼지

expr 결과에 이미 붙어 있던 라벨(instance, job 등)은 그대로 알림에 유지되고, 여기에 위 라벨이 더해집니다. 그래서 Alertmanager 라우트에서 severity="critical" 은 PagerDuty 로, warning 은 Slack 으로 보내는 식의 분기가 가능합니다.

팁: severity 값은 조직 전체에서 동일한 어휘로 통일하세요. 누구는 crit, 누구는 critical 을 쓰면 라우팅 규칙이 새기 시작합니다.

6. annotations와 템플릿

annotations 는 알림 메시지 본문입니다. Go 템플릿으로 라벨·값을 끼워 넣어 무엇이, 얼마나 문제인지 구체적으로 적습니다.

annotations:
  summary: "{{ $labels.job }} 5xx 에러율 높음"
  description: "에러율 {{ $value | humanizePercentage }} (임계 5%)가 10분 이상 지속."
  runbook_url: "https://runbooks.example.com/HighErrorRate"
  dashboard: "https://grafana.example.com/d/abc/app"

자주 쓰는 템플릿 요소:

  • {{ $labels.<name> }} — 해당 알림 시계열의 라벨 값 ({{ $labels.instance }})
  • {{ $value }}expr 의 현재 값
  • {{ $value | humanize }} — 1234567 → 1.235M 식으로 사람이 읽기 좋게
  • {{ $value | humanizePercentage }} — 0.0523 → 5.23%
  • {{ $value | humanizeDuration }} — 초 단위를 2h 5m 식으로
  • {{ printf "%.2f" $value }} — 소수 자리 고정

좋은 annotation 은 현재 값·임계값·영향 범위·런북 링크를 담아, 알림만 보고도 바로 행동할 수 있게 합니다.

7. 좋은 expr 작성법

규칙의 품질은 expr 에서 갈립니다.

  • counter 는 rate() 로. 누적값을 그대로 비교하지 말고 초당 증가율로.
  • 의미 있는 라벨만 남겨 집계. sum by (job, instance) (...) 처럼, 알림을 구분할 라벨은 유지하고 나머지는 합칩니다.
  • 비율은 0 나눗셈에 주의. 분모가 0이면 결과가 사라져(no data) 알림이 안 뜨는데, 보통은 이게 안전한 동작입니다.
  • 누락 감지는 absent(). 메트릭이 아예 사라지는 상황(타깃 소멸)은 임계 비교로는 못 잡습니다.
  • 원인이 아니라 증상에 알림. "CPU 90%" 단독보다 "에러율 상승·지연 증가"처럼 사용자 영향에 거는 편이 노이즈가 적습니다.
# 에러율(5xx 비율) — 분모 0이면 자연히 no data
sum by (job) (rate(http_requests_total{code=~"5.."}[5m]))
  / sum by (job) (rate(http_requests_total[5m])) > 0.05
 
# 특정 job 의 메트릭이 통째로 사라졌는지
absent(up{job="payment-api"})

8. 자주 쓰는 패턴 예제

groups:
  - name: app-symptoms
    rules:
      # 인스턴스 다운
      - alert: InstanceDown
        expr: up == 0
        for: 5m
        labels: { severity: critical }
        annotations:
          summary: "인스턴스 다운: {{ $labels.instance }}"
 
      # 5xx 에러율 5% 초과
      - alert: HighErrorRate
        expr: |
          sum by (job) (rate(http_requests_total{code=~"5.."}[5m]))
            / sum by (job) (rate(http_requests_total[5m])) > 0.05
        for: 10m
        labels: { severity: warning }
        annotations:
          summary: "{{ $labels.job }} 에러율 높음"
          description: "에러율 {{ $value | humanizePercentage }}, 10분 지속."
 
      # p95 지연 1초 초과 (histogram)
      - alert: HighRequestLatency
        expr: |
          histogram_quantile(0.95,
            sum by (job, le) (rate(http_request_duration_seconds_bucket[5m]))
          ) > 1
        for: 10m
        labels: { severity: warning }
        annotations:
          summary: "{{ $labels.job }} p95 지연 높음"
          description: "p95 = {{ $value | humanizeDuration }} (임계 1s)."
 
      # 디스크가 4시간 내 가득 찰 추세 (예측)
      - alert: DiskWillFillSoon
        expr: |
          predict_linear(node_filesystem_avail_bytes{fstype!~"tmpfs|overlay"}[1h], 4*3600) < 0
        for: 30m
        labels: { severity: warning }
        annotations:
          summary: "{{ $labels.instance }} 디스크 4시간 내 소진 예상"
 
      # 메모리 포화 90% 초과
      - alert: HighMemoryUsage
        expr: |
          (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) > 0.9
        for: 15m
        labels: { severity: warning }
        annotations:
          summary: "{{ $labels.instance }} 메모리 사용률 90% 초과"

predict_linear(...[1h], 4*3600) 은 최근 1시간 추세로 4시간 뒤 값을 외삽합니다. < 0 이면 "지금 추세면 4시간 내 0(=가득 참)" 이라는 뜻이라, 임계치에 닿기 전에 미리 알림을 줍니다.

9. Recording Rule 함께 쓰기

복잡하거나 자주 쓰는 식은 recording rule 로 미리 계산해 새 시계열로 저장하면, 알림 규칙과 대시보드가 단순해지고 평가 비용도 줄어듭니다.

groups:
  - name: app-recordings
    rules:
      - record: job:http_requests:rate5m
        expr: sum by (job) (rate(http_requests_total[5m]))
 
      - record: job:http_errors:ratio5m
        expr: |
          sum by (job) (rate(http_requests_total{code=~"5.."}[5m]))
            / sum by (job) (rate(http_requests_total[5m]))

이름은 관례적으로 level:metric:operations 형식(job:http_errors:ratio5m)을 씁니다. 그러면 알림은 이렇게 짧아집니다.

- alert: HighErrorRate
  expr: job:http_errors:ratio5m > 0.05
  for: 10m
  labels: { severity: warning }

10. 규칙 검증·테스트 (promtool)

규칙을 프로덕션에 넣기 전에 promtool 로 문법과 동작을 검증합니다.

# 문법·구조 검증
promtool check rules rules/*.yml

더 나아가 유닛 테스트로 "이런 입력이면 이 알림이 떠야 한다"를 못 박을 수 있습니다.

# tests/instance-down.test.yml
rule_files:
  - ../rules/app-alerts.yml
evaluation_interval: 1m
 
tests:
  - interval: 1m
    input_series:
      - series: 'up{job="api", instance="a:8080"}'
        values: "1 1 0 0 0 0 0 0"      # 3분차부터 다운
    alert_rule_test:
      - eval_time: 7m
        alertname: InstanceDown
        exp_alerts:
          - exp_labels:
              severity: critical
              job: api
              instance: a:8080
            exp_annotations:
              summary: "인스턴스 다운: a:8080"
promtool test rules tests/instance-down.test.yml

CI 에 promtool check rulespromtool test rules 를 걸어두면, 잘못된 규칙이 머지되는 것을 사전에 막을 수 있습니다.

11. 운영 팁 / 안티패턴

  • 거의 모든 규칙에 for 를 두세요. 순간 스파이크 알림이 사라집니다.
  • 증상에 알림. 사용자 영향(에러율·지연·가용성)에 걸고, 원인 지표(CPU·메모리)는 보조로.
  • annotation 을 행동 가능하게. 현재 값·임계값·런북 링크 필수.
  • severity 체계를 통일. 라우팅이 여기에 의존합니다.
  • 알림 피로(alert fatigue) 경계. 끄지 않는 알림은 결국 모두가 무시합니다. "이 알림이 뜨면 사람이 무엇을 할지" 가 없으면 알림이 아니라 대시보드 항목입니다.
  • 규칙을 바꾼 뒤에는 Prometheus 를 리로드합니다.
# --web.enable-lifecycle 로 떠 있을 때
curl -X POST http://localhost:9090/-/reload

Alertmanager 연결은 2장의 alerting: 블록이면 충분하고, 실제 발송 채널·라우팅·중복제거는 Alertmanager 의 alertmanager.yml 에서 따로 구성합니다(별도 주제).

마무리

Alert Rule 은 결국 "의미 있는 expr + 적절한 for + 행동 가능한 annotation" 의 조합입니다. expr 로 증상을 정확히 포착하고, for 로 노이즈를 걸러내고, labels 로 올바른 사람에게 보내고, annotations 로 바로 조치할 수 있게 만드는 것 — 이 네 가지가 좋은 알림의 전부입니다. 마지막으로 promtool 유닛 테스트까지 붙이면, 규칙이 "기대대로 운다"는 것을 코드처럼 보장할 수 있습니다.

다음 단계로는 여기서 만든 알림을 Alertmanager 라우트(심각도별 채널·근무시간 분기·inhibition)와 연결하고, ALERTS 메트릭으로 "알림 자체"를 대시보드화해 알림 위생을 관리해 보길 권합니다.