Alertmanager 고가용성
Alertmanager 고가용성 (High Availability)
Alertmanager는 고가용성(HA, High Availability)을 위해 클러스터를 구성하는 기능을 지원해요. 이 문서는 HA 메커니즘이 어떻게 동작하는지, 설계 목표는 무엇인지, 운영 시 고려할 점은 무엇인지 설명합니다.
핵심은 "fail open" 원칙이에요. 네트워크 파티션 상황에서도 중요한 알림을 놓치기보다는 중복 알림을 보내는 쪽을 선택합니다. 그래서 Alertmanager 클러스터는 최소 한 번 이상(at-least-once) 알림 전달을 보장해요.
출처: 문서
본문
Alertmanager는 고가용성을 위한 클러스터를 구성하는 설정을 지원해요. 이 문서는 HA 메커니즘이 어떻게 동작하는지, 그 설계 목표, 그리고 운영 고려 사항을 설명합니다.
설계 목표 (Design Goals)
Alertmanager HA 구현은 세 가지 핵심 원칙을 중심으로 설계되었습니다:
- 통합 뷰 및 관리 (Single pane view and management) — 사일런스와 알림을 클러스터의 어느 멤버에서든 보고 관리할 수 있어, 통일된 운영 경험을 제공합니다.
- "fail open"으로 분할-뇌(split-brain) 생존 — 네트워크 파티션 동안 Alertmanager는 중요한 알림을 놓치기보다 중복 알림을 보내는 것을 선호합니다.
- 최소 한 번 이상 전달 (At-least-once delivery) — 시스템은 알림이 적어도 한 번 이상 전달됨을 보장하며, 이는 fail-open 철학과 일치합니다.
이 목표들은 엄격한 정확히-한 번(exactly-once) 의미론보다 운영 신뢰성과 알림 전달을 우선시합니다.
아키텍처 개요 (Architecture Overview)
Alertmanager 클러스터는 gossip 프로토콜로 통신하는 여러 Alertmanager 인스턴스로 구성됩니다. 각 인스턴스는:
- Prometheus 서버로부터 알림을 독립적으로 수신합니다.
- 피어투피어(peer-to-peer) gossip 메시에 참여합니다.
- 상태(사일런스와 알림 로그)를 다른 클러스터 멤버에게 복제합니다.
- 알림을 독립적으로 처리하고 전송합니다.
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Prometheus 1 │ │ Prometheus 2 │ │ Prometheus N │
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
│ │ │
│ alerts │ alerts │ alerts
│ │ │
▼ ▼ ▼
┌────────────────────────────────────────────┐
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ AM-1 │ │ AM-2 │ │ AM-3 │ │
│ │ (pos: 0) ├──┤ (pos: 1) ├──┤ (pos: 2) │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ Gossip Protocol (Memberlist) │
└────────────────────────────────────────────┘
│ │ │
▼ ▼ ▼
Receivers Receivers Receivers
Gossip 프로토콜
Alertmanager는 Hashicorp의 Memberlist 라이브러리를 사용해 gossip 기반 통신을 구현합니다. gossip 프로토콜은 다음을 처리합니다:
멤버십 관리 (Membership Management)
- 자동 피어 발견 — 인스턴스는 알려진 피어 목록으로 구성될 수 있으며 다른 클러스터 멤버를 자동으로 발견합니다.
- 헬스 체크 — 정기적인 프로브가 실패한 멤버를 감지합니다(기본값: 매 1초).
- 장애 감지 — 실패한 멤버는 표시되고 다시 합류(rejoin)를 시도할 수 있습니다.
상태 복제 (State Replication)
gossip 계층은 세 가지 유형의 상태를 복제합니다:
- 사일런스 — 생성, 업데이트, 삭제 작업이 모든 피어에게 브로드캐스트됩니다.
- 알림 로그 (Notification log) — 중복을 방지하기 위해 어떤 알림이 전송되었는지의 기록.
- 멤버십 변경 — join, leave, failure 이벤트.
상태는 최종적으로 일관됩니다(eventually consistent) — 충분한 시간과 네트워크 연결이 주어지면 모든 클러스터 멤버는 같은 상태로 수렴합니다.
Gossip 정착 (Gossip Settling)
Alertmanager가 시작하거나 클러스터에 다시 합류할 때, 알림을 처리하기 전에 gossip이 "정착(settle)"할 때까지 기다립니다. 이는 불완전한 상태에 기반해 알림을 보내는 것을 방지합니다.
정착 알고리즘은 다음을 기다립니다:
- 피어 수가 3번의 연속 검사 동안 안정적으로 유지될 때까지(기본 간격: push-pull 간격).
- 또는 타임아웃이 발생할 때까지(context로 구성 가능).
이 시간 동안 인스턴스는 알림을 이미 수신하고 저장하지만 알림 처리는 연기합니다.
HA 모드의 알림 파이프라인
알림 파이프라인은 클러스터 환경에서 중복 제거를 보장하면서 최소 한 번 이상 전달을 유지하도록 다르게 동작합니다:
┌────────────────────────────────────────────────┐
│ DISPATCHER STAGE │
├────────────────────────────────────────────────┤
│ 1. Find matching route(s) │
│ 2. Find/create aggregation group within route │
│ 3. Throttle by group wait or group interval │
└───────────────────┬────────────────────────────┘
│
▼
┌────────────────────────────────────────────────┐
│ NOTIFIER STAGE │
├────────────────────────────────────────────────┤
│ 1. Wait for HA gossip to settle │◄─── Ensures complete state
│ 2. Filter inhibited alerts │
│ 3. Filter non-time-active alerts │
│ 4. Filter time-muted alerts │
│ 5. Filter silenced alerts │◄─── Uses replicated silences
│ 6. Wait according to HA cluster peer index │◄─── Staggered notifications
│ 7. Dedupe by repeat interval/HA state │◄─── Uses notification log
│ 8. Notify & retry intermittent failures │
│ 9. Update notification log │◄─── Replicated to peers
└────────────────────────────────────────────────┘
HA 특화 단계
1. Gossip 정착 대기
그룹에서 첫 알림을 보내기 전에, 인스턴스는 gossip이 정착하기를 기다립니다. 이는 다음을 보장합니다:
- 사일런스가 완전히 복제됨.
- 알림 로그에 다른 인스턴스의 최근 전송 기록이 포함됨.
- 클러스터 멤버십이 안정적임.
구현: peer.WaitReady(ctx)
2. 피어 위치 기반 대기
모든 클러스터 멤버가 동시에 알림을 보내지 않도록, 각 인스턴스는 정렬된 피어 목록에서 자신의 위치에 따라 기다립니다:
wait_time = peer_position × peer_timeout
예를 들어 3개 인스턴스와 15초 피어 타임아웃의 경우:
- 인스턴스
am-1(위치 0): 0초 대기 - 인스턴스
am-2(위치 1): 15초 대기 - 인스턴스
am-3(위치 2): 30초 대기
이 지그재그 스태거링 타이밍은 다음을 허용합니다:
- 첫 번째 인스턴스가 알림을 보냄.
- 후속 인스턴스가 알림 로그 항목을 봄.
- 중복 제거로 중복 전송을 방지함.
구현: clusterWait() in cmd/alertmanager/main.go:594
위치는 모든 피어 이름을 알파벳순으로 정렬해 결정됩니다:
func (p *Peer) Position() int {
all := p.mlist.Members()
sort.Slice(all, func(i, j int) bool {
return all[i].Name
...
- 발생 중인(firing) 알림이 변경됐나요? 그렇다면 알림 보내기
- 해결된(resolved) 알림이 변경됐나요? 그렇고
send_resolved: true이면 알림 보내기 - 반복 간격(repeat interval)이 경과했나요? 그렇다면 알림 보내기
- 그렇지 않으면: 알림 건너뛰기(중복 제거됨)
알림 로그는 gossip을 통해 복제되므로 모든 클러스터 멤버가 같은 전송 기록을 공유합니다.
분할-뇌 처리 (Fail Open)
네트워크 파티션 동안 클러스터는 통신할 수 없는 여러 그룹으로 나뉠 수 있습니다. Alertmanager의 "fail open" 설계는 알림이 여전히 전달되도록 보장합니다:
시나리오: 네트워크 파티션
Before partition:
┌────────┬────────┬────────┐
│ AM-1 │ AM-2 │ AM-3 │
└────────┴────────┴────────┘
Unified cluster
After partition:
┌────────┐ │ ┌────────┬────────┐
│ AM-1 │ │ │ AM-2 │ AM-3 │
└────────┘ │ └────────┴────────┘
Partition A │ Partition B
파티션 중 동작
Partition A (AM-1 단독):
- AM-1은 자신을 위치 0으로 봄.
- 0 × timeout = 0초 대기.
- 알림을 보냄(AM-2/AM-3에서 온 중복 제거 없음).
Partition B (AM-2, AM-3):
- AM-2는 위치 0, AM-3은 위치 1.
- AM-2는 0초 대기, 알림 전송.
- AM-3는 AM-2의 알림 로그 항목을 보고 중복 제거.
결과: 중복 알림이 전송됨(Partition A에서 하나, Partition B에서 하나).
이것은 의도적입니다 — Alertmanager는 놓친 알림보다 중복 알림을 선호합니다.
파티션 복구 후
네트워크 파티션이 치유되면:
- gossip 프로토콜이 모든 피어를 다시 감지합니다.
- 알림 로그가 병합됩니다(타임스탬프가 있는 CRDT 유사 병합).
- 이후 알림은 모든 인스턴스에서 올바르게 중복 제거됩니다.
- 어느 파티션에서든 생성된 사일런스가 모든 피어에게 복제됩니다.
HA에서의 사일런스 관리
사일런스는 클러스터에서 일급 복제 상태(first-class replicated state)입니다.
사일런스 생성 및 업데이트
사일런스가 어느 인스턴스에서 생성 또는 업데이트되면:
- 로컬 저장 — 사일런스가 로컬 상태 맵에 저장됩니다.
- 브로드캐스트 — 사일런스가 직렬화(protobuf)되어 gossip으로 브로드캐스트됩니다.
- 수신 시 병합 — 다른 인스턴스가 사일런스를 수신해 병합합니다:
// Merge logic: last-write-wins based on UpdatedAt timestamp
if !exists || incoming.UpdatedAt > existing.UpdatedAt {
accept_update()
}
- 인덱싱 — 빠른 알림 매칭을 위해 사일런스 매처 캐시가 업데이트됩니다.
사일런스 만료
사일런스는 다음을 가집니다:
StartsAt,EndsAt— 활성 시간 범위.ExpiresAt— 가비지 컬렉션 시점(EndsAt + 보존 기간).UpdatedAt— 병합 중 충돌 해결용.
각 인스턴스는 독립적으로:
- 현재 시간에 기반해 사일런스 상태(pending/active/expired)를 평가합니다.
- 보존 기간이 지난 만료 사일런스를 가비지 컬렉팅합니다.
- 모든 인스턴스가 같은 결정으로 수렴하므로 GC는 로컬 전용입니다(gossip 없음).
통합 관리 뷰 (Single Pane of Glass)
사용자는 클러스터의 어떤 Alertmanager 인스턴스와도 상호작용할 수 있습니다:
- 사일런스 보기 — 모든 인스턴스가 같은 사일런스 상태를 가짐(최종적 일관성).
- 사일런스 생성/업데이트 — 어느 인스턴스에서든 변경이 모든 피어에게 전파됩니다.
- 사일런스 삭제 — "즉시 만료" + gossip으로 구현됨.
이것은 어떤 인스턴스에 접근하든 통일된 운영 경험을 제공합니다.
운영 고려 사항 (Operational Considerations)
구성
클러스터를 구성하려면 각 Alertmanager 인스턴스가 필요합니다:
# alertmanager.yml
global:
# ... other config ...
# No cluster config in YAML - use CLI flags
커맨드라인 플래그:
alertmanager \
--cluster.listen-address=0.0.0.0:9094 \
--cluster.peer=am-1.example.com:9094 \
--cluster.peer=am-2.example.com:9094 \
--cluster.peer=am-3.example.com:9094 \
--cluster.advertise-address=192.0.2.1:9094 \
--cluster.peer-timeout=15s \
--cluster.gossip-interval=200ms \
--cluster.pushpull-interval=60s
주요 플래그:
--cluster.listen-address— 클러스터 통신용 바인드 주소(기본값:0.0.0.0:9094).--cluster.peer— 피어 주소 목록(반복 가능).--cluster.advertise-address— 피어에게 알리는ip:port형식의 IP 주소(호스트명 아님)(생략 시 자동 감지).--cluster.peer-timeout— 중복 제거를 위한 피어 위치당 대기 시간(기본값:15s).--cluster.gossip-interval— gossip 주기(기본값:200ms).--cluster.pushpull-interval— 전체 상태 동기화 간격(기본값:60s).--cluster.probe-interval— 피어 헬스 체크 간격(기본값:1s).--cluster.settle-timeout— gossip 정착을 기다리는 최대 시간(기본값: context 타임아웃).
Prometheus 구성
중요: Prometheus가 로드 밸런서가 아닌 모든 Alertmanager 인스턴스로 알림을 보내도록 구성하세요.
# prometheus.yml
alerting:
alertmanagers:
- static_configs:
- targets:
- am-1.example.com:9093
- am-2.example.com:9093
- am-3.example.com:9093
이것은 다음을 보장합니다:
- 중복성 — 하나의 Alertmanager가 다운되어도 다른 인스턴스가 여전히 알림을 받습니다.
- 독립적 처리 — 각 인스턴스가 라우팅, 그룹화, 중복 제거를 독립적으로 평가합니다.
- 단일 장애 지점 없음 — 로드 밸런서는 단일 장애 지점을 도입합니다.
클러스터 크기 고려 사항
Alertmanager는 쿼럼이나 투표 없이 gossip을 사용하므로, 어떤 N 인스턴스든 최대 N-1 실패를 견딥니다 — 하나의 인스턴스만 살아 있으면 알림이 전송됩니다.
하지만 클러스터 크기는 트레이드오프를 수반합니다:
인스턴스가 더 많을 때의 이점:
- 동시 실패(하드웨어, 네트워크, 데이터센터 장애)에 대한 더 큰 탄력성.
- 유지보수 창 동안에도 계속 운영됨.
인스턴스가 더 많을 때의 비용:
- 파티션 시 중복 알림이 증가함.
- 더 많은 gossip 트래픽.
일반적인 배포:
- 2-3개 인스턴스 — 단일 데이터센터 운영 배포에 흔함.
- 4-5개 인스턴스 — 멀티 데이터센터 또는 매우 중요한 환경.
참고: 합의 기반 시스템(etcd, Raft)과 달리 홀수/짝수 클러스터 크기는 차이가 없습니다 — 투표나 쿼럼이 없기 때문입니다.
클러스터 헬스 모니터링
모니터링할 주요 메트릭:
# Cluster size
alertmanager_cluster_members
# Peer health
alertmanager_cluster_peer_info
# Peer position (affects notification timing)
alertmanager_peer_position
# Failed peers
alertmanager_cluster_failed_peers
# State replication
alertmanager_nflog_gossip_messages_propagated_total
alertmanager_silences_gossip_messages_propagated_total
보안
기본적으로 클러스터 통신은 암호화되지 않습니다. 운영 배포, 특히 WAN을 가로지르는 경우 상호 TLS(mTLS)를 사용하세요:
alertmanager \
--cluster.tls-config=/etc/alertmanager/cluster-tls.yml
상호 TLS 구성은 Gossip Traffic 섹션을 참고하세요.
영속성 (Persistence)
각 Alertmanager 인스턴스는 다음을 영속화합니다:
- 사일런스 — 스냅샷 파일에 저장됨(기본값:
data/silences). - 알림 로그 — 스냅샷 파일에 저장됨(기본값:
data/nflog).
재시작 시:
- 인스턴스가 디스크에서 사일런스와 알림 로그를 로드합니다.
- 클러스터에 합류하고 피어와 gossip합니다.
- 피어에서 받은 상태를 병합합니다(최신 타임스탬프가 승리).
- gossip 정착 후 알림 처리를 시작합니다.
참고: 알림 자체는 영속화되지 않습니다 — Prometheus가 발생 중인 알림을 정기적으로 재전송합니다.
흔한 함정 (Common Pitfalls)
-
Prometheus → Alertmanager 로드 밸런싱
- ❌ 로드 밸런서를 사용하지 마세요.
- ✅ Prometheus에서 모든 인스턴스를 구성하세요.
-
gossip 정착을 기다리지 않음
- 시작 시 사일런스 누락이나 중복 알림으로 이어질 수 있음.
--cluster.settle-timeout플래그가 이를 제어합니다.
-
클러스터 포트를 차단하는 네트워크 ACL
- 모든 인스턴스 간에 포트 9094(또는
--cluster.listen-address포트)가 열려 있는지 확인. - 기본적으로 TCP와 UDP 둘 다 사용됨(TLS 전송 사용 시 TCP만).
- 모든 인스턴스 간에 포트 9094(또는
-
라우팅 불가능한 advertise 주소
--cluster.advertise-address가 설정되지 않으면 Alertmanager가 자동 감지를 시도.- 값은
ip:port형식의 IP 주소여야 함 — 호스트명은 지원되지 않음. 호스트명을 사용하면 경고가 기록되고, advertise 주소가 설정되지 않거나 잘못된 채로 시작이 계속되어 클러스터 통신 실패가 발생함. - 클라우드/NAT 환경에서는 라우팅 가능한 IP를 명시적으로 설정하세요(예:
--cluster.advertise-address=192.0.2.1:9094).
-
불일치하는 클러스터 구성
- 모든 인스턴스가 같은
--cluster.peer-timeout과 gossip 설정을 가져야 함. - 불일치는 불필요한 중복이나 놓친 알림을 일으킬 수 있음.
- 모든 인스턴스가 같은
작동 방식: 엔드투엔드 예시
시나리오: 3개 인스턴스 클러스터, 새 알림 그룹
- Prometheus에서 알림이 3개 인스턴스 모두에 도착합니다.
- 디스패처가 집계 그룹을 만들고
group_wait(예: 30s) 동안 기다립니다. - group_wait 후:
- 각 인스턴스가 알림할 준비를 합니다.
- Notifier 단계:
- 모든 인스턴스가 gossip 정착을 기다립니다(막 시작했다면).
- AM-1(위치 0): 0s 대기, 알림 로그 확인(비어 있음), 알림 전송, nflog에 기록.
- AM-2(위치 1): 15s 대기, 알림 로그 확인(AM-1의 항목 확인), 알림 건너뜀.
- AM-3(위치 2): 30s 대기, 알림 로그 확인(AM-1의 항목 확인), 알림 건너뜀.
- 결과: 정확히 하나의 알림이 전송됨(AM-1이).
시나리오: AM-1 실패
- 알림이 AM-2와 AM-3에만 도착합니다.
- 디스패처가 그룹을 만들고
group_wait를 기다립니다. - Notifier 단계:
- AM-1은 클러스터에 없음(프로브 실패).
- AM-2는 이제 위치 0: 0s 대기, 알림 전송.
- AM-3는 이제 위치 1: 15s 대기, AM-2의 항목 확인, 건너뜀.
- 결과: 알림이 여전히 전송됨(fail-open).
시나리오: 알림 중 네트워크 파티션
- 알림이 모든 인스턴스에 도착합니다.
- 네트워크 파티션이 AM-1을 AM-2/AM-3과 분리합니다.
- Partition A(AM-1)에서:
- 위치 0, 0s 대기, 알림 전송.
- Partition B(AM-2, AM-3)에서:
- AM-2는 위치 0, 0s 대기, 알림 전송.
- AM-3는 위치 1, 15s 대기, 중복 제거.
- 결과: 두 개의 알림이 전송됨(파티션당 하나) — fail-open 동작.
문제 해결 (Troubleshooting)
클러스터 상태 확인
# View cluster members via API
curl http://am-1:9093/api/v2/status
# Check metrics
curl http://am-1:9093/metrics | grep cluster
분할-뇌 진단
분할-뇌가 의심되면:
- 각 인스턴스에서
alertmanager_cluster_members를 확인.- 총 클러스터 크기와 일치해야 함.
alertmanager_cluster_peer_info{state="alive"}를 확인.- 모든 피어가 alive로 표시되어야 함.
- 인스턴스 간 네트워크 연결을 검토.
중복 알림 디버그
중복 알림은 다음으로 인해 발생할 수 있습니다:
- 네트워크 파티션(예상됨, fail-open).
- gossip 미정착 —
--cluster.settle-timeout확인. - 클록 스큐(clock skew) — 모든 인스턴스에서 NTP가 구성되어 있는지 확인.
- 알림 로그 미복제 — gossip 메트릭 확인.
디버그 로깅 활성화:
alertmanager --log.level=debug
다음을 찾으세요:
"Waiting for gossip to settle...""gossip settled; proceeding"- 알림 파이프라인의 중복 제거 결정.
더 읽을거리 (Further Reading)
더 알아보기 (Learn more)
- Alertmanager 설정 문서 — 클러스터 관련 플래그와 라우팅 설정
- HTTPS 및 보안 — 클러스터 gossip 트래픽 상호 TLS 구성
- Alertmanager 개념 — 그룹화, 인히비션, 사일런스 구조 이해하기
- Alertmanager 저장소 · 고가용성 — 공식 HA 안내