모니터링 도구를 도입하면 대부분 알림부터 설정합니다. CPU 80% 초과, 디스크 90% 초과, 5xx 에러 발생 — 하나씩 추가하다 보면 하루에 수십 건의 알림이 울립니다. 팀원들은 알림을 무시하기 시작하고, 정작 실제 장애 알림도 놓치게 됩니다. 이 글에서는 알림 피로(Alert Fatigue)를 줄이고, 사람이 대응해야 하는 상황에만 알림이 울리도록 설계하는 원칙을 정리합니다.
핵심 요약
- 알림은 "사람이 즉시 대응해야 하는 상황"에만 설정합니다. 정보성 알림은 대시보드로 대체합니다.
- 원인(Cause)이 아닌 증상(Symptom)에 알림을 걸어야 커버리지가 높아집니다.
- 심각도(Severity)를 명확히 분류하고, 각 심각도에 맞는 알림 채널과 대응 시간을 정의합니다.
for절(지속 시간)을 설정하여 일시적 스파이크에 의한 오탐(False Positive)을 줄입니다.- 알림은 한 번 설정하고 끝이 아닙니다. 정기적으로 리뷰하여 노이즈를 제거하고 임계값을 조정합니다.
1. 알림 피로란 무엇인가
On-call 엔지니어의 슬랙 채널에 하루 50건의 알림이 쌓입니다. 그 중 실제로 대응이 필요한 것은 2~3건입니다. 나머지는 자동 복구되거나, 정보성이거나, 이미 알고 있는 상태입니다.
이 상황이 반복되면:
| 단계 | 현상 | 결과 |
|---|---|---|
| 1단계 | 알림이 많아짐 | 모든 알림을 하나씩 확인하느라 시간 소모 |
| 2단계 | 대부분 대응 불필요 확인 | "또 이거구나" → 알림 확인을 미루기 시작 |
| 3단계 | 알림 무시 습관화 | 슬랙 채널 음소거, 이메일 필터 설정 |
| 4단계 | 실제 장애 알림 놓침 | MTTR(평균 복구 시간) 증가, SLA 위반 |
Google의 SRE 팀은 이를 "알림은 사람이 즉시 행동을 취해야 하는 상황에만 발생해야 한다"라는 원칙으로 정리합니다. 행동이 필요 없는 알림은 알림이 아니라 로그 또는 대시보드입니다.
알림 피로의 주요 원인
| 원인 | 예시 | 해결 방향 |
|---|---|---|
| 낮은 임계값 | CPU > 50%에 알림 설정 | 서비스 영향 기준으로 임계값 재설정 |
for 절 없음 |
순간 스파이크에도 알림 발생 | 5분 이상 지속 조건 추가 |
| 원인 기반 알림 | "디스크 80%" → 실제 서비스 영향 없음 | 증상 기반으로 전환 |
| 중복 알림 | 같은 이슈에 여러 알림 동시 발생 | 그룹핑과 억제(Inhibition) 적용 |
| 자동 복구 알림 | Auto Scaling으로 해결되는 상황에도 알림 | 자동 복구 후에도 지속될 때만 알림 |
2. 증상 기반 vs 원인 기반 알림
Google SRE Book에서 강조하는 핵심 원칙 중 하나는 "증상(Symptom)에 알림을 걸고, 원인(Cause)은 진단 정보로 활용하라"입니다.
원인 기반 알림의 문제
원인 기반 알림은 시스템 내부 상태에 직접 걸리는 알림입니다.
# 원인 기반 알림 (비권장)
- alert: HighCPUUsage
expr: node_cpu_seconds_total{mode="idle"} < 0.2
for: 5m
labels:
severity: warning
annotations:
summary: "CPU 사용률이 80%를 초과했습니다"
이 알림의 문제:
- CPU가 80%여도 서비스 응답 시간이 정상이면 대응할 필요가 없습니다.
- 반대로 CPU가 40%인데 GC(Garbage Collection)로 응답이 느려질 수 있습니다.
- CPU 알림은 "뭔가 느릴 수 있다"는 원인 후보일 뿐, 사용자 영향을 직접 보여주지 않습니다.
증상 기반 알림의 장점
증상 기반 알림은 사용자가 체감하는 문제에 걸리는 알림입니다.
# 증상 기반 알림 (권장)
- alert: HighErrorRate
expr: |
sum(rate(http_requests_total{status=~"5.."}[5m]))
/
sum(rate(http_requests_total[5m])) > 0.05
for: 5m
labels:
severity: critical
annotations:
summary: "5xx 에러율이 5%를 초과했습니다 (5분 지속)"
dashboard: "https://grafana.example.com/d/service-overview"
증상 기반 알림의 장점:
| 측면 | 원인 기반 | 증상 기반 |
|---|---|---|
| 커버리지 | 특정 원인 1개만 감지 | 여러 원인이 같은 증상을 유발하면 모두 감지 |
| 오탐률 | 높음 (영향 없는 상태에도 발생) | 낮음 (사용자 영향이 있을 때만 발생) |
| 대응 필요성 | 불확실 ("이게 실제로 문제인가?") | 명확 ("서비스가 영향을 받고 있다") |
| 유지보수 | 인프라 변경마다 알림 수정 필요 | 서비스 동작 기준이므로 안정적 |
실무 설계 원칙
- 알림: 증상에 걸립니다 (에러율, 응답 시간, 가용성)
- 대시보드: 원인을 확인합니다 (CPU, 메모리, 디스크, 큐 깊이)
- Runbook: 증상 → 원인 후보 → 확인 방법 → 조치를 문서화합니다
증상 알림이 발생하면, 대시보드에서 원인을 추적하는 흐름입니다. 원인 자체에 알림을 걸면 "문제가 아닌 것"에 시간을 낭비하게 됩니다.
원인 기반 알림이 유용한 예외도 있습니다. 디스크 용량이 90%를 넘어 곧 서비스가 중단될 예측 가능한 상황, 인증서 만료 임박 등 "지금은 괜찮지만 곧 증상이 나타날 것이 확실한" 경우에는 원인 기반 알림이 적합합니다.
3. 심각도 분류와 알림 채널 설계
모든 알림을 같은 채널로 보내면 중요한 알림이 묻힙니다. 심각도를 명확히 분류하고, 각 심각도에 맞는 채널과 기대 대응 시간을 정의해야 합니다.
심각도 레벨 정의
| 심각도 | 정의 | 기대 대응 시간 | 알림 채널 |
|---|---|---|---|
| Critical (P1) | 서비스 전체 또는 핵심 기능 중단 | 5분 이내 확인 | PagerDuty/전화 + Slack |
| High (P2) | 일부 사용자 영향, 성능 심각 저하 | 15분 이내 확인 | PagerDuty + Slack |
| Warning (P3) | 잠재적 문제, 방치 시 악화 가능 | 업무 시간 내 확인 | Slack 채널 |
| Info (P4) | 참고 정보, 대응 불필요 | 대응 불필요 | 대시보드만 |
심각도 판단 기준
심각도를 결정할 때 고려할 질문:
- 사용자 영향 범위: 전체 사용자인가, 일부인가, 내부 시스템만인가?
- 비즈니스 영향: 매출에 직접 영향을 주는가? (결제, 주문 vs 내부 리포트)
- 자동 복구 가능성: Auto Scaling, 재시도 등으로 자동 해결되는가?
- 시간 민감도: 지금 당장 조치하지 않으면 악화되는가?
실무 시나리오: 이커머스 서비스 심각도 설계
| 알림 조건 | 심각도 | 근거 |
|---|---|---|
| 결제 API 에러율 > 5% (5분 지속) | Critical | 매출 직접 영향, 사용자 이탈 |
| 상품 검색 응답 p99 > 3초 (10분 지속) | High | 사용자 경험 저하, 이탈률 증가 |
| 주문 처리 큐 깊이 > 1000 (15분 지속) | Warning | 지연 발생 중이나 즉시 중단은 아님 |
| 배치 리포트 생성 실패 | Warning | 내부 시스템, 다음 업무일에 재처리 가능 |
| 디스크 사용량 > 85% | Warning | 당장 서비스 영향 없으나 방치 시 문제 |
| CPU 사용률 > 70% | Info | Auto Scaling 동작 중, 대시보드 확인만 |
Alertmanager 라우팅 설정 예시
# alertmanager.yml
route:
receiver: 'slack-info'
group_by: ['alertname', 'service']
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
routes:
- match:
severity: critical
receiver: 'pagerduty-critical'
repeat_interval: 5m
- match:
severity: high
receiver: 'pagerduty-high'
repeat_interval: 15m
- match:
severity: warning
receiver: 'slack-warning'
repeat_interval: 4h
receivers:
- name: 'pagerduty-critical'
pagerduty_configs:
- service_key: '<P1_SERVICE_KEY>'
severity: critical
- name: 'pagerduty-high'
pagerduty_configs:
- service_key: '<P2_SERVICE_KEY>'
severity: high
- name: 'slack-warning'
slack_configs:
- channel: '#alerts-warning'
title: '{{ .GroupLabels.alertname }}'
text: '{{ .CommonAnnotations.summary }}'
- name: 'slack-info'
slack_configs:
- channel: '#alerts-info'
title: '{{ .GroupLabels.alertname }}'
text: '{{ .CommonAnnotations.summary }}'
4. 알림 규칙 설계 원칙
원칙 1: for 절로 일시적 스파이크 필터링
# 나쁜 예: for 절 없음 — 1초라도 넘으면 알림
- alert: HighLatency
expr: histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m])) > 1
# 좋은 예: 5분 이상 지속될 때만 알림
- alert: HighLatency
expr: histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m])) > 1
for: 5m
for 절은 조건이 연속으로 충족되는 시간을 의미합니다. 배포 직후 잠깐 응답이 느려지는 것은 정상입니다. 5분 이상 지속되면 실제 문제일 가능성이 높습니다.
권장 for 값:
| 심각도 | for 값 |
근거 |
|---|---|---|
| Critical | 2~5분 | 빠른 감지 필요하지만 최소 확인 시간 확보 |
| High | 5~10분 | 일시적 스파이크 필터링 |
| Warning | 10~30분 | 추세 확인, 자동 복구 대기 |
원칙 2: 비율(Rate) 기반 알림 사용
# 나쁜 예: 절대값 기반 — 트래픽 적은 시간에 오탐 발생
- alert: TooManyErrors
expr: sum(increase(http_requests_total{status=~"5.."}[5m])) > 10
# 좋은 예: 비율 기반 — 트래픽 양에 관계없이 일관된 기준
- alert: HighErrorRate
expr: |
sum(rate(http_requests_total{status=~"5.."}[5m]))
/
sum(rate(http_requests_total[5m])) > 0.01
for: 5m
절대값(에러 10건)은 트래픽이 적은 새벽에는 심각하지만, 피크 시간(초당 1만 건)에는 무의미할 수 있습니다. 비율로 설정하면 트래픽 양에 관계없이 일관된 기준을 적용할 수 있습니다.
원칙 3: 모든 알림에 Runbook 링크 포함
- alert: DatabaseConnectionPoolExhausted
expr: |
hikaricp_connections_active / hikaricp_connections_max > 0.9
for: 5m
labels:
severity: high
team: backend
annotations:
summary: "DB 커넥션 풀이 90% 이상 사용 중입니다"
runbook: "https://wiki.example.com/runbooks/db-connection-pool"
dashboard: "https://grafana.example.com/d/db-connections"
알림이 울렸을 때 "이게 뭐지? 뭘 해야 하지?"라는 질문에 바로 답을 줘야 합니다. Runbook에는 다음을 포함합니다:
- 이 알림이 의미하는 것
- 확인해야 할 대시보드/로그
- 단계별 대응 절차
- 에스컬레이션 기준
원칙 4: 알림에 행동이 연결되어야 함
알림을 설정하기 전에 물어야 할 질문:
"이 알림이 울렸을 때, 누군가가 구체적으로 무엇을 해야 하는가?"
답이 없으면 알림이 아니라 대시보드에 표시해야 합니다.
| 질문 | "예"이면 | "아니오"이면 |
|---|---|---|
| 즉시 행동이 필요한가? | 알림(Critical/High) | 대시보드 또는 Warning |
| 사람이 개입해야 해결되는가? | 알림 | 자동화 또는 대시보드 |
| 이 정보 없이 장애를 놓칠 수 있는가? | 알림 | 제거 |
5. 그룹핑, 억제, 음소거
Alertmanager는 알림 노이즈를 줄이기 위한 세 가지 메커니즘을 제공합니다.
그룹핑 (Grouping)
같은 종류의 알림을 하나로 묶어서 전달합니다.
서비스 A의 인스턴스가 10개이고 모두 같은 에러를 내면, 10건의 개별 알림 대신 1건의 그룹 알림을 받습니다.
route:
group_by: ['alertname', 'service', 'namespace']
group_wait: 30s # 첫 알림 후 30초 대기 (같은 그룹 알림 수집)
group_interval: 5m # 그룹에 새 알림 추가 시 5분 간격으로 알림
repeat_interval: 4h # 해결 안 되면 4시간마다 반복
group_wait: 첫 알림 발생 후 추가 알림을 모으는 대기 시간group_interval: 기존 그룹에 새 알림이 추가되었을 때 재알림 간격repeat_interval: 변경 없이 지속될 때 반복 알림 간격
억제 (Inhibition)
상위 알림이 발생하면 하위 알림을 자동으로 숨깁니다.
inhibit_rules:
# 클러스터 장애 시 개별 Pod 알림 억제
- source_match:
alertname: ClusterDown
target_match:
severity: warning
equal: ['cluster']
# 노드 장애 시 해당 노드의 Pod 알림 억제
- source_match:
alertname: NodeDown
target_match_re:
alertname: 'Pod.*'
equal: ['node']
노드가 죽으면 그 위의 모든 Pod이 비정상 상태가 됩니다. 각 Pod에 대한 알림을 개별적으로 받으면 노이즈만 증가합니다. 노드 장애 알림만 받으면 충분합니다.
음소거 (Silence)
특정 시간 동안 알림을 일시 중단합니다. 계획된 유지보수 시 사용합니다.
# Alertmanager API로 Silence 생성
# 유지보수 기간: 2026-06-07 02:00 ~ 04:00 UTC
amtool silence add alertname=~".*" namespace="production" \
--comment="Planned maintenance window" \
--author="ops-team" \
--duration="2h"
음소거는 임시 조치입니다. 영구적으로 알림을 끄려면 알림 규칙 자체를 재설계해야 합니다.
6. 에스컬레이션 정책 설계
에스컬레이션 정책은 "알림에 응답하지 않으면 누구에게 전달하는가"를 정의합니다.
기본 에스컬레이션 구조
| 단계 | 대상 | 시간 조건 | 알림 방식 |
|---|---|---|---|
| Level 1 | Primary On-call | 즉시 | Push 알림 + 전화 |
| Level 2 | Secondary On-call | 5분 무응답 시 | Push 알림 + 전화 |
| Level 3 | 팀 리드/매니저 | 15분 무응답 시 | Push 알림 + Slack DM |
| Level 4 | 전체 팀 | 30분 무응답 시 | Slack 채널 @here |
실무 시나리오: 주문 서비스 에스컬레이션
새벽 2시에 "결제 API 에러율 5% 초과 (Critical)" 알림이 발생합니다.
- 0분: Primary On-call(김 엔지니어)에게 PagerDuty 전화
- 5분: 응답 없음 → Secondary On-call(박 엔지니어)에게 전화
- 15분: 여전히 미응답 → 팀 리드에게 Slack DM + 전화
- 30분: 에스컬레이션 Level 4 → #incident 채널에 @here 호출
에스컬레이션 설계 시 고려사항
- 시간대 인식: 글로벌 팀이면 해당 시간대의 On-call에게 먼저 라우팅
- 서비스 소유권: 결제 알림은 결제팀에게, 인프라 알림은 플랫폼팀에게
- 번아웃 방지: On-call 로테이션 주기를 1~2주로 설정, 연속 배정 금지
- 응답 시간 현실성: "5분 이내 확인"이 새벽에도 가능한지 검토
7. 실무 알림 설계 예시
예시 1: 웹 서비스 기본 알림 세트
groups:
- name: web-service-alerts
rules:
# 증상: 에러율 급증 (사용자 영향)
- alert: HighErrorRate
expr: |
sum(rate(http_requests_total{status=~"5.."}[5m])) by (service)
/
sum(rate(http_requests_total[5m])) by (service) > 0.05
for: 5m
labels:
severity: critical
annotations:
summary: "{{ $labels.service }} 에러율이 5%를 초과했습니다"
runbook: "https://wiki.example.com/runbooks/high-error-rate"
# 증상: 응답 시간 저하 (사용자 경험)
- alert: HighLatencyP99
expr: |
histogram_quantile(0.99,
sum(rate(http_request_duration_seconds_bucket[5m])) by (le, service)
) > 2
for: 10m
labels:
severity: high
annotations:
summary: "{{ $labels.service }} p99 응답시간이 2초를 초과했습니다 (10분 지속)"
runbook: "https://wiki.example.com/runbooks/high-latency"
# 예측: 디스크 풀 예상 (원인 기반이지만 예측 가능)
- alert: DiskWillFillIn24h
expr: |
predict_linear(node_filesystem_avail_bytes{mountpoint="/"}[6h], 24*3600) < 0
for: 30m
labels:
severity: warning
annotations:
summary: "{{ $labels.instance }} 디스크가 24시간 내 가득 찰 것으로 예측됩니다"
runbook: "https://wiki.example.com/runbooks/disk-full"
# 가용성: 서비스 프로브 실패
- alert: ServiceDown
expr: up{job="web-service"} == 0
for: 2m
labels:
severity: critical
annotations:
summary: "{{ $labels.instance }}가 응답하지 않습니다"
runbook: "https://wiki.example.com/runbooks/service-down"
예시 2: SLO 기반 알림 (Burn Rate)
단순 임계값 대신 SLO(Service Level Objective) 기반으로 알림을 설계하면 "얼마나 빠르게 에러 예산을 소진하고 있는가"를 기준으로 판단합니다.
SLO가 "월간 가용성 99.9%"라면, 월 에러 예산은 약 43분입니다.
# Burn Rate 알림: 현재 속도로 에러 예산을 얼마나 빨리 소진하는가
groups:
- name: slo-burn-rate
rules:
# 14.4x burn rate (1시간 내 에러 예산 소진 속도)
- alert: SLOBurnRateCritical
expr: |
(
sum(rate(http_requests_total{status=~"5.."}[5m]))
/
sum(rate(http_requests_total[5m]))
) > (14.4 * 0.001)
for: 2m
labels:
severity: critical
annotations:
summary: "에러 예산이 1시간 내 소진될 속도로 에러 발생 중"
# 6x burn rate (6시간 내 에러 예산 소진 속도)
- alert: SLOBurnRateHigh
expr: |
(
sum(rate(http_requests_total{status=~"5.."}[30m]))
/
sum(rate(http_requests_total[30m]))
) > (6 * 0.001)
for: 15m
labels:
severity: high
annotations:
summary: "에러 예산이 6시간 내 소진될 속도로 에러 발생 중"
# 1x burn rate (30일 내 에러 예산 소진 — 서서히 악화)
- alert: SLOBurnRateWarning
expr: |
(
sum(rate(http_requests_total{status=~"5.."}[6h]))
/
sum(rate(http_requests_total[6h]))
) > (1 * 0.001)
for: 3h
labels:
severity: warning
annotations:
summary: "에러 예산이 정상 속도로 소진 중 — 추세 확인 필요"
Burn Rate 기반 알림의 장점:
- 짧은 시간에 많은 에러 → Critical (빠른 대응)
- 긴 시간에 걸쳐 서서히 악화 → Warning (추세 모니터링)
- 에러 예산이 충분하면 → 알림 없음 (불필요한 노이즈 제거)
8. 알림 리뷰 프로세스
알림은 한 번 설정하고 방치하면 시간이 지남에 따라 노이즈가 쌓입니다. 정기적인 리뷰가 필요합니다.
주간 알림 리뷰
매주 On-call 핸드오프 시 다음을 확인합니다:
| 확인 항목 | 질문 | 조치 |
|---|---|---|
| 총 알림 건수 | 이번 주 알림이 몇 건이었는가? | 50건 초과 시 노이즈 분석 |
| 대응 비율 | 실제 조치가 필요했던 알림 비율은? | 50% 미만이면 규칙 재설계 |
| 반복 알림 | 같은 알림이 반복 발생하는가? | 근본 원인 해결 또는 임계값 조정 |
| 미응답 알림 | 무시된 알림이 있는가? | 제거하거나 심각도 변경 |
| 놓친 장애 | 알림 없이 발견된 장애가 있는가? | 새 알림 규칙 추가 |
월간 알림 건전성 지표
Signal-to-Noise Ratio = (대응 필요 알림 수) / (전체 알림 수) × 100%
목표: 80% 이상
위험: 50% 미만이면 팀이 알림을 무시하기 시작할 가능성이 높음
알림 생애주기 관리
| 단계 | 활동 |
|---|---|
| 생성 | 알림 규칙 작성 + Runbook 작성 + 담당 팀 지정 |
| 운영 | 발생 빈도, 대응률, MTTR 추적 |
| 튜닝 | 임계값 조정, for 값 변경, 그룹핑 개선 |
| 폐기 | 대응률 0%, 서비스 변경으로 무의미해진 알림 제거 |
알림을 제거할 때는 "이 알림 없이도 문제를 감지할 수 있는가"를 반드시 확인합니다. 노이즈라고 무조건 삭제하면 감시 사각지대가 생길 수 있습니다. 증상 기반 알림으로 대체 가능한지 먼저 확인한 후 제거합니다.
9. 도구별 알림 설정 비교
| 기능 | Prometheus + Alertmanager | CloudWatch Alarms | Datadog Monitors |
|---|---|---|---|
| 알림 조건 | PromQL (강력한 표현력) | 단순 임계값, Math Expression | 메트릭 쿼리 + 복합 조건 |
| 그룹핑 | group_by 지원 | 미지원 (개별 알람) | 멀티 알림 그룹 |
| 억제 | inhibit_rules | 미지원 | 다운타임 스케줄 |
| 라우팅 | 라벨 기반 계층적 라우팅 | SNS Topic | Team/Service 기반 |
| Burn Rate | 직접 구현 (PromQL) | 미지원 | SLO 모니터링 기능 내장 |
| 비용 | 오픈소스 (인프라 비용만) | 알람 수 × 평가 비용 | 라이선스 비용 |
| 에스컬레이션 | 외부 연동 필요 (PagerDuty) | 미지원 | 에스컬레이션 체인 |
CloudWatch Alarm 설정 예시
{
"AlarmName": "HighErrorRate-OrderService",
"MetricName": "5XXError",
"Namespace": "AWS/ApplicationELB",
"Statistic": "Sum",
"Period": 300,
"EvaluationPeriods": 3,
"Threshold": 50,
"ComparisonOperator": "GreaterThanThreshold",
"AlarmActions": ["arn:aws:sns:ap-northeast-2:123456789:critical-alerts"],
"TreatMissingData": "notBreaching"
}
CloudWatch는 EvaluationPeriods로 for 절과 유사한 효과를 줍니다. 위 설정은 "5분 간격으로 3회 연속(15분) 임계값을 넘으면 알림"입니다.
10. 보안 고려사항
알림 시스템은 인프라 상태 정보를 외부 채널(Slack, PagerDuty, 이메일)로 전송합니다. 알림 메시지에 민감한 인프라 정보가 포함되지 않도록 주의해야 합니다.
| 위험 | 예시 | 대응 |
|---|---|---|
| 알림에 내부 IP/호스트명 노출 | "10.0.1.45에서 DB 연결 실패" | 서비스명으로 대체 |
| 알림에 사용자 정보 포함 | "user_id: 12345의 결제 실패" | 집계 수치만 알림, 상세는 로그 |
| Webhook URL 유출 | Slack Webhook URL이 코드에 노출 | Secret Manager로 관리 |
| 알림 채널 접근 제어 | 모든 직원이 Critical 알림 채널 접근 | 역할 기반 채널 분리 |
| Alertmanager API 미인증 | 외부에서 Silence 생성 가능 | 네트워크 격리 + 인증 적용 |
11. 비용/운영 고려사항
| 항목 | 비용 발생 요인 | 최적화 방향 |
|---|---|---|
| CloudWatch Alarm | 알람 수 × 평가 빈도 | 불필요한 알람 정리, 복합 알람 사용 |
| PagerDuty | 사용자 수 기반 라이선스 | On-call 인원만 라이선스 부여 |
| Datadog Monitors | 모니터 수 무제한이나 알림 채널 비용 | 중복 모니터 통합 |
| 팀 비용 (가장 큼) | 오탐 대응에 소요되는 엔지니어 시간 | Signal-to-Noise Ratio 80% 이상 유지 |
알림 비용에서 가장 큰 부분은 도구 비용이 아니라 엔지니어의 시간입니다. 오탐 1건을 확인하는 데 5분이 걸린다면, 하루 30건의 오탐은 2.5시간의 생산성 손실입니다.
12. 자주 하는 실수
1. 모든 메트릭에 알림을 설정하는 것
"일단 알림을 걸어놓자"는 접근은 노이즈를 만듭니다. 알림을 추가하기 전에 "이 알림이 울리면 내가 뭘 할 것인가"를 먼저 정의합니다.
2. 임계값을 감으로 설정하는 것
"CPU 80%면 위험하지 않을까?"가 아니라, 과거 데이터를 분석하여 "서비스 영향이 시작되는 지점"을 찾아야 합니다. p99 응답 시간이 SLO를 초과하기 시작하는 CPU 수준이 실제 임계값입니다.
3. 알림을 끄는 것으로 문제를 해결하는 것
"이 알림 시끄러우니까 꺼버리자"는 근본 원인을 외면하는 것입니다. 알림이 시끄러우면 두 가지 중 하나입니다: - 임계값이 부적절하다 → 조정 - 실제 문제가 반복된다 → 근본 원인을 해결
4. 알림 Runbook을 작성하지 않는 것
알림만 있고 Runbook이 없으면 새로운 On-call 엔지니어가 "이게 뭔지" 파악하는 데 시간을 낭비합니다. 모든 알림에는 "이 알림이 울리면 이것을 확인하고 이렇게 조치하라"는 문서가 필요합니다.
5. 사후 알림만 설정하는 것
"디스크 100% 도달" 알림은 이미 서비스가 중단된 후입니다. predict_linear()이나 추세 기반 알림으로 문제가 발생하기 전에 감지해야 합니다.
13. 정리
- 알림 피로는 "알림이 많아서" 생기는 것이 아니라 "의미 없는 알림이 많아서" 생깁니다. 모든 알림에 구체적인 행동이 연결되어야 합니다.
- 증상 기반 알림이 원인 기반 알림보다 커버리지가 높고 오탐이 적습니다. 원인은 대시보드와 Runbook에서 확인합니다.
- 심각도를 명확히 분류하고, 각 레벨에 맞는 알림 채널과 에스컬레이션 정책을 설계합니다. Critical은 전화, Warning은 Slack이 적합합니다.
for절, 비율 기반 임계값, 그룹핑, 억제를 활용하여 노이즈를 구조적으로 줄입니다.- 주간/월간 알림 리뷰를 통해 Signal-to-Noise Ratio를 80% 이상으로 유지합니다. 알림은 설정 후 방치하면 반드시 노이즈가 쌓입니다.
참고 문서
'Observability' 카테고리의 다른 글
| OpenTelemetry란 무엇인가: 분산 트레이싱 기본 개념 (0) | 2026.06.08 |
|---|---|
| Prometheus + Grafana로 Kubernetes 모니터링 구성하기 (0) | 2026.06.06 |
| CloudWatch vs Datadog vs Grafana: 모니터링 도구 선택 기준 (0) | 2026.06.01 |
| Observability란 무엇인가: Monitoring, Logging, Tracing의 차이 (0) | 2026.05.31 |