전체 비유: 환자 모니터 경보 시스템#

Alerting Rules를 중환자실 환자 모니터 경보 시스템에 비유하면 이해하기 쉽습니다:

환자 모니터 비유Alerting Rules역할
맥박 비정상 조건expr (조건)알림 발동 조건 정의
5분간 지속 시 알림for (지속 시간)일시적 변동 무시
긴급/주의 분류severity 라벨심각도 구분
담당 의료진 호출알림 라우팅적절한 담당자에게 전달
알림 상세 정보annotations문제 설명, 조치 가이드
경보 피로 방지적절한 임계값의미 있는 알림만
연쇄 경보 억제inhibition중복 알림 방지
대응 매뉴얼 링크runbook_url문제 해결 가이드

이처럼 환자 모니터가 이상 징후를 감지하여 의료진에게 알리듯, Alerting Rules로 시스템 문제를 감지합니다.


대상 독자: 모니터링 알림을 설정하려는 개발자, SRE 선수 지식: Recording Rules 소요 시간: 약 25-30분 이 문서를 읽으면: 오탐을 줄이고 실제 문제만 알림받는 규칙을 작성할 수 있습니다

TL;DR
  • for: 지정 시간 동안 조건 만족 시 발동 (오탐 방지)
  • labels: 심각도, 팀 등 메타데이터 추가
  • annotations: 알림 메시지, 런북 URL 포함
  • Recording Rules 결과를 활용하여 간결하게 작성

왜 Alerting Rules가 필요한가?#

대시보드를 24시간 쳐다보고 있을 수는 없습니다. 새벽 3시에 에러율이 치솟아도 아무도 대시보드를 보지 않으면 장애는 방치됩니다. Alerting Rules는 “조건이 일정 시간 지속되면 자동으로 알린다"는 규칙을 선언적으로 정의하여, 사람이 모니터링하지 않는 시간에도 시스템이 스스로 문제를 감지하고 담당자에게 알릴 수 있게 합니다.

기본 문법#

Alerting Rule 구조#

groups:
  - name: <그룹명>
    rules:
      - alert: <알림명>
        expr: <PromQL 조건>
        for: <지속 시간>
        labels:
          <라벨명>: <값>
        annotations:
          <어노테이션명>: <값>

기본 예제#

groups:
  - name: availability
    rules:
      - alert: ServiceDown
        expr: up == 0
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "{{ $labels.instance }} is down"
          description: "{{ $labels.job }} has been down for more than 5 minutes."
          runbook_url: "https://wiki.example.com/runbook/service-down"

핵심 구성요소#

expr (조건)#

알림이 발동하는 조건입니다.

# 타겟 다운
expr: up == 0

# 에러율 5% 초과
expr: |
  sum by (service) (rate(http_requests_total{status=~"5.."}[5m]))
  / sum by (service) (rate(http_requests_total[5m]))
  > 0.05

# P99 응답시간 500ms 초과
expr: |
  histogram_quantile(0.99,
    sum by (service, le) (rate(http_request_duration_seconds_bucket[5m]))
  ) > 0.5

# Recording Rule 결과 사용 (권장)
expr: service:http_requests_errors:ratio_rate5m > 0.05

for (지속 시간)#

조건이 지속되어야 알림이 발동합니다. 일시적 스파이크로 인한 오탐을 방지합니다.

# 5분간 지속되어야 발동
for: 5m

# 즉시 발동 (for 없음)
# 주의: 오탐 위험
graph LR
    subgraph "for: 5m"
        P["Pending<br>(조건 만족)"]
        F["Firing<br>(5분 경과)"]
    end

    P --> |"5분 지속"| F
    P --> |"조건 해제"| R["해제<br>(알림 취소)"]

for 설정에 따라 조건 만족 시 Pending 상태에서 5분 지속되면 Firing으로, 중간에 해제되면 취소되는 흐름입니다.

권장 값:

상황for 값이유
서비스 다운1-5m빠른 감지 필요
에러율 증가5-10m일시적 스파이크 필터링
리소스 부족10-15m자동 복구 대기
디스크 부족30m-1h천천히 증가

labels (라벨)#

알림에 메타데이터를 추가합니다.

labels:
  severity: critical          # 심각도
  team: platform              # 담당 팀
  service: "{{ $labels.service }}"  # 동적 라벨

심각도 레벨:

레벨설명대응
critical서비스 중단즉시 대응 (호출)
warning성능 저하업무 시간 내 대응
info참고 사항기록만

annotations (어노테이션)#

알림 내용을 상세히 설명합니다.

annotations:
  summary: "High error rate on {{ $labels.service }}"
  description: |
    Error rate is {{ $value | humanizePercentage }}.
    Current threshold: 5%
  runbook_url: "https://wiki.example.com/runbook/high-error-rate"
  dashboard_url: "https://grafana.example.com/d/abc/errors?var-service={{ $labels.service }}"

템플릿 변수:

변수설명
{{ $labels }}알림의 모든 라벨
{{ $labels.name }}특정 라벨 값
{{ $value }}표현식 결과 값
`{{ $valuehumanize }}`
`{{ $valuehumanizePercentage }}`
`{{ $valuehumanizeDuration }}`

실전 알림 규칙#

가용성 알림#

groups:
  - name: availability
    rules:
      # 타겟 다운
      - alert: TargetDown
        expr: up == 0
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "Target {{ $labels.instance }} is down"
          description: "{{ $labels.job }} target {{ $labels.instance }} has been down for more than 5 minutes."

      # 서비스 가용성 저하
      - alert: ServiceAvailabilityLow
        expr: |
          sum by (service) (rate(http_requests_total{status!~"5.."}[5m]))
          / sum by (service) (rate(http_requests_total[5m]))
          < 0.99
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "{{ $labels.service }} availability below 99%"
          description: "Current availability: {{ $value | humanizePercentage }}"

에러율 알림#

groups:
  - name: errors
    rules:
      # 높은 에러율
      - alert: HighErrorRate
        expr: |
          sum by (service) (rate(http_requests_total{status=~"5.."}[5m]))
          / sum by (service) (rate(http_requests_total[5m]))
          > 0.05
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "High error rate on {{ $labels.service }}"
          description: "Error rate is {{ $value | humanizePercentage }}"

      # 심각한 에러율
      - alert: CriticalErrorRate
        expr: |
          sum by (service) (rate(http_requests_total{status=~"5.."}[5m]))
          / sum by (service) (rate(http_requests_total[5m]))
          > 0.10
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "Critical error rate on {{ $labels.service }}"
          description: "Error rate is {{ $value | humanizePercentage }}. Immediate action required."

지연시간 알림#

groups:
  - name: latency
    rules:
      # P99 지연시간 높음
      - alert: HighP99Latency
        expr: |
          histogram_quantile(0.99,
            sum by (service, le) (rate(http_request_duration_seconds_bucket[5m]))
          ) > 0.5
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "High P99 latency on {{ $labels.service }}"
          description: "P99 latency is {{ $value | humanizeDuration }}"

      # Recording Rule 사용
      - alert: HighP99LatencyFromRule
        expr: service:http_request_duration_seconds:p99 > 0.5
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "High P99 latency on {{ $labels.service }}"

리소스 알림#

groups:
  - name: resources
    rules:
      # CPU 높음
      - alert: HighCPUUsage
        expr: |
          100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "High CPU usage on {{ $labels.instance }}"
          description: "CPU usage is {{ $value | humanize }}%"

      # 메모리 부족
      - alert: LowMemory
        expr: |
          (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) < 0.1
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Low memory on {{ $labels.instance }}"
          description: "Available memory is {{ $value | humanizePercentage }}"

      # 디스크 부족
      - alert: DiskSpaceLow
        expr: |
          (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) < 0.1
        for: 30m
        labels:
          severity: warning
        annotations:
          summary: "Low disk space on {{ $labels.instance }}"
          description: "Available disk space is {{ $value | humanizePercentage }}"

Kafka 알림#

groups:
  - name: kafka
    rules:
      # Consumer Lag 높음
      - alert: KafkaConsumerLagHigh
        expr: |
          sum by (consumer_group, topic) (kafka_consumer_group_lag) > 10000
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "High consumer lag for {{ $labels.consumer_group }}"
          description: "Lag is {{ $value | humanize }} messages on topic {{ $labels.topic }}"

      # Under-replicated 파티션
      - alert: KafkaUnderReplicatedPartitions
        expr: kafka_server_replicamanager_underreplicatedpartitions > 0
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "Kafka under-replicated partitions detected"

알림 피로 방지#

1. 적절한 임계값#

# ❌ 너무 민감
expr: error_rate > 0.001  # 0.1%

# ✅ 의미 있는 임계값
expr: error_rate > 0.01   # 1%

2. 충분한 for 시간#

# ❌ 일시적 스파이크에도 발동
for: 30s

# ✅ 지속적인 문제만 감지
for: 5m

3. 계층적 알림#

# Warning: 5% 에러, 5분 지속
- alert: HighErrorRate
  expr: error_rate > 0.05
  for: 5m
  labels:
    severity: warning

# Critical: 10% 에러, 2분 지속 (더 빠르게)
- alert: CriticalErrorRate
  expr: error_rate > 0.10
  for: 2m
  labels:
    severity: critical

4. Alertmanager에서 그룹화#

# alertmanager.yml
route:
  group_by: ['alertname', 'service']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h

규칙 검증#

문법 검사#

promtool check rules alerts/*.yml

단위 테스트#

# tests/alert_tests.yml
rule_files:
  - ../alerts/errors.yml

evaluation_interval: 1m

tests:
  - interval: 1m
    input_series:
      - series: 'http_requests_total{service="api", status="500"}'
        values: '0+10x10'
      - series: 'http_requests_total{service="api", status="200"}'
        values: '0+100x10'

    alert_rule_test:
      - eval_time: 10m
        alertname: HighErrorRate
        exp_alerts:
          - exp_labels:
              service: api
              severity: warning
            exp_annotations:
              summary: "High error rate on api"
promtool test rules tests/alert_tests.yml

알림 상태#

stateDiagram-v2
    [*] --> Inactive: 조건 불만족
    Inactive --> Pending: 조건 만족
    Pending --> Inactive: 조건 해제
    Pending --> Firing: for 시간 경과
    Firing --> Inactive: 조건 해제 (Resolved)

알림의 Inactive → Pending → Firing 상태 전환과 각 상태 간 조건을 보여주는 상태 다이어그램입니다.

상태설명
Inactive조건 불만족
Pending조건 만족, for 대기 중
Firing알림 발동됨

핵심 정리#

구성요소역할예시
expr발동 조건error_rate > 0.05
for오탐 방지5m
labels메타데이터severity: critical
annotations알림 내용summary, runbook_url

좋은 알림의 조건:

  1. 즉각적인 조치가 필요한 상황만
  2. 명확한 심각도 분류
  3. 상세한 컨텍스트 (runbook, dashboard)
  4. 적절한 for 시간으로 오탐 방지

다음 단계#

추천 순서문서배우는 것
1SRE 황금 신호알림 대상 지표 선정
2알림 후 액션 가이드알림 수신 후 대응