[Observability] Prometheus의 Alert (Alert Rule & AlertManager)
Prometheus 공식 문서에 따르면 Prometheus에서 Alert는 크게 2가지 컴포넌트로 구성되어있다.
- Alert Rule : PromQL을 기반으로 정의하는 알람이 발생할 규칙
- AlertManager : 수신받은 Alert를 다양한 서비스에 notify를 수행
오늘은 Prometheus의 Alert 아키텍쳐를 알아보도록 한다.
Alerting Rule
Prometheus 쿼리문(PromQL)을 바탕으로 정의하는 알람 조건, 조건에 맞아 알람이 발생하면 연결된 외부 서비스로 알람을 전달하게 된다.
Prometheus에서 Alert Rule을 선언하는 방법으로 Prometheus 웹 GUI 상에서 선언해주는 방법과 설정파일을 작성하는 방법이 존재한다.
groups:
- name: example
rules:
- alert: HighRequestLatency
expr: job:request_latency_seconds:mean5m{job="myjob"} > 0.5 # Alert가 트리거될 쿼리문
for: 10m # 트리거된 Alert가 얼마만큼 지속되어야 Firing state가 될 지
labels:
severity: page
annotations:
summary: High request latency
Alert Rule 설정에서 가장 중요한 부분은 expr과 for 구문으로 볼 수 있다.
expr은 알람 조건식으로, 해당 조건식이 참이되는 경우 Alert가 트리거된다. cloudwatch의 Metric Query와 유사한 설정값으로 볼 수 있다.
그리고 for 구문은 트리거된 Alarm이 Firing State로 넘어가기 위해 체크할 지속시간이다.
Alarm State에 대한 설명은 바로 아래를 참고하자.
Alarm State
Prometheus에서 Alarm은 크게 3가지 상태로 이루어져 있다.
- Inactive : Alert Rule에 맞는 alarm이 없는 상태로 정상 상태로 본다.
- Pending : Alert Rule에 해당하는 alarm이 발생한 상태로, 해당 Alert Rule의 'for' 기간 동안 Pending이 유지되면 Firing단계로 넘어가게 된다.
- Firing : 설정한 'for'기간 동안 Pending이 유지되어 전이된 상태, Firing 상태가 되어야 Prometheus가 AlertManager에게 alarm을 전송하게 된다.
Pending과 Firing은 Alert Rule에 해당하는 alarm이 더 이상 존재하지 않는 경우(문제가 해결되어 alarm이 해소), 다시 Inactive 상태로 전이된다.
각 State에 따른 상태 전이 다이어그램은 아래와 같다.
AlertManager
Prometheus의 Alert Rule로부터 전달된 Alert를 Notify할 수 있도록 Receiver에 뿌려주는 컴포넌트이다. Prometheus와는 별개의 프로세스로 동작한다.
Prometheus server가 가져온 이벤트(PULL)를 기반으로 Alarm이 발생하는 경우, 이를 다양한 형태의 클라이언트에 뿌려주는(Push alert) 이벤트를 실행한다.
AlertManager 설정
AlertManager도 yaml 설정파일을 작성함으로써 구축할 수 있다. 주된 setting 구간은 아래와 같다.
- global : AlertManager의 global한 설정구간, 여기에서 이메일을 사용할 경우 SMTP나 notification 전달을 위한 API의 URL 등을 설정할 수 있다.
- receiver : AlertManager가 alert를 전송할 대상(receiver)을 지정하는 구간, Slack의 경우 수신받을 slack 채널, 메시지 템플릿/내용 등을 지정할 수 있다.
- route : 전달된 alert를 어떤 receiver에 보낼지 Routing하는 설정을 지정하는 구간, 주로 alert의 라벨의 값에 따라 alert를 분류하고 group_by 설정을 통해 alert들을 그룹핑한다.
- templates : recevier에 보낼 alert 메시지의 포맷을 설정할 수 있는 템플릿 함수가 정의된 파일의 경로 (Go Template 형식을 사용)
alertmanager의 설정 yaml과 관련된 공식 Docs는 아래와 같다.
AlertManager 구축 및 Receiver 연동
이번에는 직접 Alert Rule과 AlertManager를 구축하고 Recevier로 Slack에 연동해보도록 한다.
간단하게 Prometheus와 AlertManager가 위치할 인스턴스 1개와 모니터링할 대상인 인스턴스 1개를 구축하고 연결하도록한다.
Prometheus-AlertManager 설치
docker-compose를 이용해 Prometheus와 AlertManager를 설치해보도록 한다.
서비스를 위한 docker-compose 디렉토리 구조와 yml 내용은 아래와 같다.
.
├── alertmanager
│ └── alertmanager.yml # AlertManager 설정파일
├── docker-compose.yml
└── prometheus
├── alert_rules.yml # Alert Rules 설정파일
└── prometheus.yml # Prometheus 설정파일
version: '3'
services:
prometheus:
image: prom/prometheus
container_name: prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus:/etc/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
networks:
- monitoring
alertmanager:
image: prom/alertmanager
container_name: alertmanager
ports:
- "9093:9093"
volumes:
- ./alertmanager:/etc/alertmanager
command:
- '--config.file=/etc/alertmanager/alertmanager.yml'
networks:
- monitoring
networks:
monitoring:
driver: bridge
모니터링 대상 인스턴스 구축
모니터링 대상 인스턴스는 간단하게 docker를 이용해 Nginx와 node-exporter를 올려준다.
docker pull nginx
docker pull prom/node-exporter
docker run -d -p 80:80 --name demonginx nginx
docker run -d -p 9100:9100 --name node-exporter prom/node-exporter
Alert Rule 작성
Alarm 발송의 시작점이 되는 Alert Rule을 설정한다.
# prometheus/alert.rules.yml
groups:
- name: node-exporter.rules
rules:
- alert: InstanceDown
expr: up{job="node-exporter"} == 0 # Alert Rule 조건식(PromQL)
for: 1m # 1분 동안 alerting rule이 발생하면 Firing
labels:
severity: "critical"
annotations:
summary: "Instance {{ $labels.instance }} down"
description: "Instance {{ $labels.instance }} is down."
prometheus.yml 작성
Prometheus 설정파일을 작성한다. 모니터링 대상 인스턴스의 node-exporter 정보도 까먹지 말고 입력해준다.
Alerting Rule을 별개의 파일에 작성한 뒤 rule_files block을 이용해 호출하는 방식을 이용한다.
# prometheus/prometheus.yml
global:
scrape_interval: 15s # Prometheus가 정보를 긁어오는 주기
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'node-exporter'
static_configs:
- targets: ['(모니터링 인스턴스의 node-exporter 접속정보)']
rule_files:
- 'alert_rules.yml' # Alert Rule이 작성된 파일 경로를 기입
alerting:
alertmanagers:
- static_configs:
- targets: ['alertmanager:9093']
Slack Webhook 설정
알람 수신의 대상 채널에 incoming-webhook 앱을 추가 후, 웹 훅 URL을 확인한다.
Slack 채널의 통합/앱의 앱 추가 → Incoming Webhooks → 웹 훅 URL 확인
alertmanager.yml
alert 메시지를 전달할 대상의 정보 등을 작성해줄 AlertManager 설정파일을 작성한다.
global:
slack_api_url: "(slack webhook URL)"
route:
receiver: 'slack-notification'
group_by: ['alertname', 'severity']
receivers:
- name: 'slack-notification'
slack_configs:
- channel: '(slack webhook channel)'
username: "Prometheus-alertmanager"
title: "{{ .CommonAnnotations.summary }}"
text: "{{ .CommonAnnotations.description }}"
- global : Slack의 웹 훅 URL을 지정
- route : 전달된 alert를 어떤 receiver에 보낼지 Routing하는 설정을 지정하는 구간,
- receivers : AlertManager가 alert를 전송할 대상(receiver)을 지정하는 구간, Slack의 경우 수신받을 slack 채널, 메시지 템플릿/내용 등을 지정할 수 있다.
작성한 예시 이외에도 메시지 재전송, Resolve된 경우 메시지 전송, 자동 Resolve 처리하는 시간과 같은 옵션도 제공하니 공식 Docs의 옵션들을 참고하여 작성할 수 있다.
테스트
앞서 작성한 Alert Rule을 활성화시키기 위해 연결시킨 node-exporter 서비스를 중지시켜본다.
docker stop node-exporter
시간이 흐름에 따라 이 Alert Rule의 상태가 Pending에서 Firing까지 변화하는 것을 확인할 수 있다.
Firing State에 도달하면 AlertManager에 설정한 대로 Slack으로 알람이 발송되는 것도 확인할 수 있다.
이후 node-exporter를 재시작하게되면 Alert State는 다시 Inactive로 돌아가게된다.
이렇게 Alert Rule과 AlertManager를 통해 알람 메시지가 클라이언트 Application으로 전송되도록하는 아키텍쳐를 간단하게 구축해보았다.
AlertManager에는 예시에서 사용한 것 이외에도 Grouping, Inhibition, Silences와 같은 기능들도 지원하니 Alarm과 수신 대상이 다양해지는 경우, 알람 아키텍쳐를 고도화할 수 있을 것이다.