본문 바로가기
서버 인프라 실무

Prometheus + Alertmanager로 서비스 헬스 모니터링: SLO·경보 규칙·소음 차단(Blackbox/Node/Process Exporter)

by yamoojin83 2025. 10. 29.

Prometheus + Alertmanager로 서비스 헬스 모니터링: SLO·경보 규칙·소음 차단(Blackbox/Node/Process Exporter)

로그(LoKI)로 원인 파악은 좋아도, 문제가 생길 조짐을 먼저 울려주는 건 메트릭입니다. 여기서는 Ubuntu 24.04 기준으로 Prometheus 수집기 + Alertmanager 경보 라우팅을 붙이고, node_exporter·blackbox_exporter·process_exporter를 조합해 SLO 기반 경보, 노이즈 억제(inhibit/group/silence), 운영 체크리스트까지 한 번에 세팅합니다.


1) 설치 구성(단일 노드 시작)

1-1) 디렉터리/사용자

sudo useradd --no-create-home --system --shell /usr/sbin/nologin prometheus
sudo mkdir -p /etc/prometheus /var/lib/prometheus
sudo chown -R prometheus:prometheus /etc/prometheus /var/lib/prometheus

1-2) 바이너리 배치(버전은 환경에 맞게 교체)

sudo mv prometheus promtool /usr/local/bin/
sudo chmod +x /usr/local/bin/prometheus /usr/local/bin/promtool

1-3) 기본 설정

# /etc/prometheus/prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s
  external_labels:
    env: prod
    site: dc1

rule_files:
  - "rules/*.yml"

scrape_configs:
  - job_name: prometheus
    static_configs:
      - targets: ["127.0.0.1:9090"]

  - job_name: node
    static_configs:
      - targets: ["127.0.0.1:9100"]   # node_exporter

  - job_name: blackbox_http
    metrics_path: /probe
    params:
      module: [http_2xx]
    static_configs:
      - targets:
          - https://app.example.com/health
          - https://api.example.com/actuator/health
    relabel_configs:
      - source_labels: [__address__]
        target_label: __param_target
      - source_labels: [__param_target]
        target_label: instance
      - target_label: __address__
        replacement: 127.0.0.1:9115      # blackbox_exporter

  - job_name: process
    static_configs:
      - targets: ["127.0.0.1:9256"]   # process_exporter

1-4) 서비스 등록

# /etc/systemd/system/prometheus.service
[Unit]
Description=Prometheus Server
After=network-online.target
Wants=network-online.target

[Service]
User=prometheus
ExecStart=/usr/local/bin/prometheus \
  --config.file=/etc/prometheus/prometheus.yml \
  --storage.tsdb.path=/var/lib/prometheus \
  --web.enable-lifecycle
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

sudo systemctl daemon-reload
sudo systemctl enable --now prometheus

2) Exporter 3총사: 노드/서비스/외부헬스

2-1) node_exporter

sudo useradd --system --no-create-home --shell /usr/sbin/nologin nodeexp
sudo mv node_exporter /usr/local/bin/ && sudo chmod +x /usr/local/bin/node_exporter
sudo tee /etc/systemd/system/node_exporter.service >/dev/null <<'S'
[Unit]
Description=Node Exporter
After=network.target
[Service]
User=nodeexp
ExecStart=/usr/local/bin/node_exporter
Restart=always
[Install]
WantedBy=multi-user.target
S
sudo systemctl enable --now node_exporter

2-2) blackbox_exporter(HTTP/TCP/ICMP)

sudo mv blackbox_exporter /usr/local/bin/
sudo mkdir -p /etc/blackbox && sudo chown -R prometheus:prometheus /etc/blackbox
sudo tee /etc/blackbox/blackbox.yml >/dev/null <<'B'
modules:
  http_2xx:
    prober: http
    timeout: 5s
    http:
      method: GET
      headers:
        User-Agent: "probe"
      preferred_ip_protocol: "ip4"
B

sudo tee /etc/systemd/system/blackbox_exporter.service >/dev/null <<'S'
[Unit]
Description=Blackbox Exporter
After=network-online.target
[Service]
User=prometheus
ExecStart=/usr/local/bin/blackbox_exporter --config.file=/etc/blackbox/blackbox.yml --web.listen-address=:9115
Restart=always
[Install]
WantedBy=multi-user.target
S
sudo systemctl enable --now blackbox_exporter

2-3) process_exporter(특정 프로세스 헬스)

sudo mv process-exporter /usr/local/bin/process-exporter
sudo tee /etc/process-exporter.yml >/dev/null <<'P'
process_names:
  - name: "java"
    cmdline:
      - '.*/java.*-jar.*'
  - name: "nginx"
    cmdline:
      - '.*/nginx: master process.*'
P
sudo tee /etc/systemd/system/process-exporter.service >/dev/null <<'S'
[Unit]
Description=Process Exporter
After=network.target
[Service]
ExecStart=/usr/local/bin/process-exporter --config.path=/etc/process-exporter.yml --web.listen-address=:9256
Restart=always
[Install]
WantedBy=multi-user.target
S
sudo systemctl enable --now process-exporter

3) SLI/SLO를 먼저 정한다(그래야 경보가 유의미)

  • 가용성 SLI: blackbox_http로 /health 200 비율.
  • 지연 SLI: 백엔드 http_server_requests_seconds_bucket p95.
  • 오류율 SLI: 5xx/전체 요청 비율.

SLO 예: “가용성 99.9% 월간”. 에러버짓은 43.2분. 경보는 이 버짓을 초과할 추세에서 울려야 합니다.


4) 룰 파일: 가용성/지연/리소스 경보

# /etc/prometheus/rules/slo-alerts.yml
groups:
- name: slo.apdex
  rules:
  - record: sli:probe_success:ratio_5m
    expr: sum(rate(probe_success[5m])) by (instance) / sum(rate(probe_success[5m])) by (instance)

  - alert: SLOAvailabilityFastBurn
    expr: (1 - sli:probe_success:ratio_5m) > 0.05
    for: 10m
    labels:
      severity: critical
      scope: fast-burn
    annotations:
      summary: "가용성 급격 저하 ({{ $labels.instance }})"
      description: "5분 오류율 > 5%. 즉시 조치 필요"

- name: backend.latency
  rules:
  - record: app:request:p95_5m
    expr: histogram_quantile(0.95, sum by (le) (rate(http_server_requests_seconds_bucket{job="myapp"}[5m])))
  - alert: BackendLatencyHigh
    expr: app:request:p95_5m > 0.8
    for: 10m
    labels:
      severity: warning
    annotations:
      summary: "백엔드 p95 지연 상승"
      description: "최근 10분 p95 > 800ms"

- name: node.resources
  rules:
  - alert: NodeDiskAlmostFull
    expr: (node_filesystem_avail_bytes{fstype=~"ext4|xfs"} / node_filesystem_size_bytes{fstype=~"ext4|xfs"}) < 0.1
    for: 15m
    labels:
      severity: warning
    annotations:
      summary: "디스크 여유 < 10%"
      description: "파티션 {{ $labels.mountpoint }} 용량 부족"

  - alert: NodeHighLoad
    expr: (node_load1 / count without (cpu,mode) (node_cpu_seconds_total{mode="idle"})) > 1.5
    for: 15m
    labels:
      severity: warning
    annotations:
      summary: "시스템 Load 평균 과다"
      description: "1분 Load가 vCPU 대비 1.5배 초과"

룰 추가 후:

sudo mkdir -p /etc/prometheus/rules
sudo chown -R prometheus:prometheus /etc/prometheus
promtool check rules /etc/prometheus/rules/slo-alerts.yml
curl -X POST http://127.0.0.1:9090/-/reload

5) Alertmanager: 그룹핑/라우팅/억제(inhibit)/침묵(silence)

# /etc/alertmanager/alertmanager.yml
global:
  resolve_timeout: 5m

route:
  receiver: slack-default
  group_by: ['alertname','instance']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 3h
  routes:
    - matchers:
        - severity="critical"
      receiver: slack-critical
      group_by: ['alertname']
      repeat_interval: 1h

inhibit_rules:
  - source_matchers: [ 'alertname="SLOAvailabilityFastBurn"' ]
    target_matchers: [ 'alertname="BackendLatencyHigh"' ]
    equal: ['instance']

receivers:
  - name: slack-default
    slack_configs:
      - api_url: https://hooks.slack.com/services/XXX/YYY/ZZZ
        channel: "#ops"
        title: '[{{ .Status | toUpper }}] {{ .CommonAnnotations.summary }}'
        text: "{{ range .Alerts }}• {{ .Annotations.description }} ({{ .Labels.instance }}){{ end }}"
  - name: slack-critical
    slack_configs:
      - api_url: https://hooks.slack.com/services/XXX/YYY/ZZZ
        channel: "#ops-critical"
        title: '[CRIT] {{ .CommonAnnotations.summary }}'
        text: "{{ range .Alerts }}• {{ .Annotations.description }}{{ end }}"

5-1) Alertmanager 서비스

sudo useradd --system --no-create-home --shell /usr/sbin/nologin alertman
sudo mv alertmanager amtool /usr/local/bin/
sudo mkdir -p /etc/alertmanager && sudo chown -R alertman:alertman /etc/alertmanager
sudo tee /etc/systemd/system/alertmanager.service >/dev/null <<'S'
[Unit]
Description=Alertmanager
After=network-online.target
[Service]
User=alertman
ExecStart=/usr/local/bin/alertmanager --config.file=/etc/alertmanager/alertmanager.yml --storage.path=/var/lib/alertmanager
Restart=always
[Install]
WantedBy=multi-user.target
S
sudo systemctl daemon-reload
sudo systemctl enable --now alertmanager

5-2) Prometheus에 Alertmanager 연결

# /etc/prometheus/prometheus.yml (하단에 추가)
alerting:
  alertmanagers:
    - static_configs:
        - targets: ["127.0.0.1:9093"]

curl -X POST http://127.0.0.1:9090/-/reload

6) 소음 차단 전략(현업 팁)

  • inhibit: 근본 경보(가용성 붕괴)가 떴을 때 파생 경보(지연/에러율)를 자동 억제.
  • group_by: 동일 인스턴스/알럿명을 묶어 한 번에 묶음 알림.
  • repeat_interval: 해결 전 반복 주기(critical은 짧게, warning은 길게).
  • Silence: 점검창에는 라벨 기반 침묵(서비스=api, env=staging, 2h 등).

7) 대시보드/런북 안내 붙이기

Grafana 대시보드 URL과 Runbook(조치 가이드)를 알림에 포함하세요.

# Alert annotations 예시
annotations:
  summary: "백엔드 p95 지연 상승"
  description: "최근 10분 p95 > 800ms"
  runbook: "https://grafana.example.com/d/abc123/backend-latency"

8) 다중 서버 확장(풀링 구조)

  • 각 서버에 node_exporter/process_exporter 설치, Prometheus 서버에서 풀링.
  • 원격 리전은 리전별 Prometheus + Thanos/Cortex 도입을 고려(장기 보관/집계).

9) 트러블슈팅 빠른 분기

  • 메트릭이 비어 있음/targets 페이지에서 스크레이프 에러 확인.
  • 경보가 안 울림/rules 상태/평가 주기 확인, Alertmanager /#/status 점검.
  • 알림 폭주 → group/inhibit/silence 재점검, 임계치에 for:(지속 시간) 추가.
  • 블랙박스 실패 → 방화벽/리스폰스 코드/리다이렉트 체인 점검, 타임아웃 상향.

10) 체크리스트(복붙용)

  • SLI/SLO 문서화(가용성·지연·오류율), 에러버짓 계산
  • 룰에 for: 지정(일시적 스파이크 무시)
  • Alertmanager group/inhibit/silence 설계
  • 대시보드/Runbook 링크를 알림에 포함
  • 점검창 스케줄은 사전 Silence

이 구성을 적용하면 “장애가 난 뒤”가 아니라 “버짓을 갉아먹기 시작한 순간”에 알림이 울립니다. 로그(LoKI)와 메트릭(Prometheus), 알림(Alertmanager)을 연결해 탐지→진단→조치 루프를 짧게 만드세요.