Alertmanager ile Uyarı Yönetimi: Prometheus Alarmlarını Yapılandırma
Prometheus kurulumunu tamamladın, Grafana dashboard’larını ayarladın, metrikler akıyor. Peki ya gece 3’te disk dolmaya başladığında veya production veritabanın bağlantıları tükendiğinde seni kim uyaracak? İşte burada Alertmanager devreye giriyor. Prometheus’un uyarı motoru olan Alertmanager, ham metriklerden anlamlı bildirimler üretmenin merkezinde yer alıyor. Bu yazıda sıfırdan bir Alertmanager kurulumu yapacağız, gerçek dünya senaryolarıyla uyarı kuralları yazacağız ve en önemlisi “uyarı gürültüsünü” nasıl azaltacağımızı konuşacağız.
Alertmanager Nedir ve Nasıl Çalışır
Prometheus, metrik toplar ve bu metriklere karşı tanımladığın kurallara göre “firing” durumuna geçen uyarılar üretir. Ama bu uyarıları kime, nasıl, ne zaman göndereceğini Prometheus değil Alertmanager kararlaştırır.
Temel akış şu şekilde işliyor:
- Prometheus, PromQL kurallarını değerlendirir
- Kural eşiği aşıldığında Alertmanager’a HTTP POST gönderir
- Alertmanager uyarıları gruplar, filtreler ve yönlendirir
- Slack, e-posta, PagerDuty veya webhook üzerinden bildirim gider
Bu ayrımı anlamak çok önemli. Prometheus “ne oldu” sorusunu cevaplar, Alertmanager “bu konuda ne yapalım” sorusunu. İkisini ayrı tutmanın güzelliği şu: Alertmanager’ı yeniden başlatırken Prometheus metrik toplamaya devam eder, uyarı kurallarını değiştirirken bildirim altyapısına dokunmazsın.
Alertmanager Kurulumu
Binary ile Kurulum
# Güncel sürümü indir (2024 itibarıyla 0.27.x stabil)
wget https://github.com/prometheus/alertmanager/releases/download/v0.27.0/alertmanager-0.27.0.linux-amd64.tar.gz
tar xvf alertmanager-0.27.0.linux-amd64.tar.gz
cd alertmanager-0.27.0.linux-amd64
# Binary'leri sisteme kopyala
sudo cp alertmanager /usr/local/bin/
sudo cp amtool /usr/local/bin/
# Dizin yapısını oluştur
sudo mkdir -p /etc/alertmanager
sudo mkdir -p /var/lib/alertmanager
# Kullanıcı oluştur
sudo useradd --no-create-home --shell /bin/false alertmanager
sudo chown alertmanager:alertmanager /usr/local/bin/alertmanager
sudo chown alertmanager:alertmanager /usr/local/bin/amtool
sudo chown -R alertmanager:alertmanager /etc/alertmanager
sudo chown -R alertmanager:alertmanager /var/lib/alertmanager
Systemd Servis Dosyası
sudo tee /etc/systemd/system/alertmanager.service > /dev/null <<EOF
[Unit]
Description=Alertmanager
Wants=network-online.target
After=network-online.target
[Service]
User=alertmanager
Group=alertmanager
Type=simple
ExecStart=/usr/local/bin/alertmanager
--config.file=/etc/alertmanager/alertmanager.yml
--storage.path=/var/lib/alertmanager
--web.listen-address=0.0.0.0:9093
--cluster.listen-address=""
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable alertmanager
sudo systemctl start alertmanager
--cluster.listen-address="" parametresini tek node kurulumda boş bırakmak önemli. Cluster modunu kapatmazsan Alertmanager gereksiz yere peer aramaya çalışır ve loglar dolup taşar.
Temel Yapılandırma Dosyası
Alertmanager’ın kalbi /etc/alertmanager/alertmanager.yml dosyasıdır. Minimal ama işlevsel bir yapılandırmayla başlayalım:
global:
resolve_timeout: 5m
smtp_smarthost: 'smtp.gmail.com:587'
smtp_from: '[email protected]'
smtp_auth_username: '[email protected]'
smtp_auth_password: 'uygulama-sifresi-buraya'
smtp_require_tls: true
route:
group_by: ['alertname', 'cluster', 'service']
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
receiver: 'team-ops-email'
routes:
- match:
severity: critical
receiver: 'team-ops-slack'
continue: true
- match:
severity: critical
receiver: 'pagerduty-oncall'
- match:
team: database
receiver: 'team-dba-slack'
receivers:
- name: 'team-ops-email'
email_configs:
- to: '[email protected]'
send_resolved: true
- name: 'team-ops-slack'
slack_configs:
- api_url: 'https://hooks.slack.com/services/TXXXXXXXX/BXXXXXXXX/xxxxxxxx'
channel: '#alerts-ops'
title: '{{ template "slack.default.title" . }}'
text: '{{ template "slack.default.text" . }}'
send_resolved: true
- name: 'pagerduty-oncall'
pagerduty_configs:
- routing_key: 'pagerduty-integration-key'
severity: '{{ if eq .GroupLabels.severity "critical" }}critical{{ else }}warning{{ end }}'
- name: 'team-dba-slack'
slack_configs:
- api_url: 'https://hooks.slack.com/services/TXXXXXXXX/BYYYYY/yyyyyyyy'
channel: '#alerts-database'
send_resolved: true
inhibit_rules:
- source_match:
severity: 'critical'
target_match:
severity: 'warning'
equal: ['alertname', 'cluster', 'service']
Buradaki inhibit_rules kısmına dikkat et. Aynı servis için hem critical hem warning uyarısı geldiğinde sadece critical olanı iletir. Bu sayede gece 3’te telefonun 10 uyarıyla dolmaz, tek bir kritik bildirim alırsın.
Prometheus Tarafında Uyarı Kuralları Yazmak
Alertmanager tek başına çalışmaz, Prometheus’un ona uyarı göndermesi lazım. Önce Prometheus yapılandırmasına Alertmanager adresini ekleyelim:
# /etc/prometheus/prometheus.yml
alerting:
alertmanagers:
- static_configs:
- targets:
- localhost:9093
rule_files:
- "/etc/prometheus/rules/*.yml"
Şimdi gerçek dünya senaryolarına göre uyarı kuralları yazalım. Bir e-ticaret platformu yönettiğini düşün:
# /etc/prometheus/rules/infrastructure.yml
groups:
- name: infrastructure
interval: 30s
rules:
# Disk kullanımı %80 üzerine çıkarsa warning, %90 üzerine çıkarsa critical
- alert: DiskSpaceWarning
expr: (node_filesystem_avail_bytes{mountpoint!~"^/boot.*"} / node_filesystem_size_bytes) * 100 < 20
for: 5m
labels:
severity: warning
team: ops
annotations:
summary: "{{ $labels.instance }} diskinde yer azalıyor"
description: "{{ $labels.mountpoint }} mount noktasında %{{ printf "%.1f" $value }} boş alan kaldı."
runbook_url: "https://wiki.sirketin.com/runbooks/disk-full"
- alert: DiskSpaceCritical
expr: (node_filesystem_avail_bytes{mountpoint!~"^/boot.*"} / node_filesystem_size_bytes) * 100 < 10
for: 2m
labels:
severity: critical
team: ops
annotations:
summary: "KRITIK: {{ $labels.instance }} diski dolmak üzere"
description: "{{ $labels.mountpoint }} mount noktasında sadece %{{ printf "%.1f" $value }} boş alan var. Hemen müdahale gerekiyor."
# CPU yüksek kullanım
- alert: HighCPUUsage
expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 85
for: 10m
labels:
severity: warning
team: ops
annotations:
summary: "{{ $labels.instance }} CPU kullanımı yüksek"
description: "Son 10 dakikadır CPU kullanımı %{{ printf "%.1f" $value }} seviyesinde."
# Servis down
- alert: ServiceDown
expr: up == 0
for: 1m
labels:
severity: critical
team: ops
annotations:
summary: "{{ $labels.job }} servisi erişilemiyor"
description: "{{ $labels.instance }} adresindeki {{ $labels.job }} servisi 1 dakikadır yanıt vermiyor."
for parametresine özellikle dikkat et. Bu parametre, uyarının kaç dakika boyunca tetiklendikten sonra Alertmanager’a iletileceğini belirler. CPU spike’ları için 10 dakika mantıklı; anlık bir yük olabilir. Ama servis down için 1 dakika yeterli, çünkü production servisinin 1 dakika bile down olması ciddi bir problem.
Gelişmiş Routing Senaryoları
Gerçek dünyada tek bir ekip ve tek bir bildirim kanalı yetmez. Büyüyen altyapılarda routing kritik hale gelir.
route:
group_by: ['alertname', 'datacenter', 'app']
group_wait: 45s
group_interval: 10m
repeat_interval: 3h
receiver: 'default-receiver'
routes:
# İş saatleri dışında kritik uyarılar PagerDuty'ye gitsin
- match:
severity: critical
receiver: 'pagerduty-oncall'
routes:
# Ama database uyarıları DBA ekibine gitsin
- match:
team: database
receiver: 'dba-pagerduty'
# Staging ortamı uyarıları sadece Slack'e gitsin, PagerDuty'yi rahatsız etmesin
- match:
environment: staging
receiver: 'slack-staging'
# Bu match durdurucu, alt route'lara geçmesin
# Belirli saatlerde bazı uyarıları sustur (time_intervals ile)
- match:
alertname: BackupJobRunning
receiver: 'null-receiver'
active_time_intervals:
- business-hours
time_intervals:
- name: business-hours
time_intervals:
- weekdays: ['monday:friday']
times:
- start_time: '09:00'
end_time: '18:00'
receivers:
- name: 'null-receiver'
- name: 'slack-staging'
slack_configs:
- api_url: 'https://hooks.slack.com/services/...'
channel: '#staging-alerts'
color: 'warning'
send_resolved: true
null-receiver hilesi çok işe yarar. BackupJobRunning gibi beklenen uyarıları mesai saatlerinde /dev/null‘a göndermek için kullanılır. Gece backup çalışırken uyarı oluşmasını biliyorsun ama bunun için uyandırılmak istemiyorsun.
Silences: Planlı Bakım Penceresi Yönetimi
Silences, belirli bir zaman aralığında belirli uyarıları susturmak için kullanılır. amtool CLI aracıyla komut satırından yönetebilirsin:
# Yarın sabah 02:00-04:00 arası disk uyarılarını sustur
amtool silence add
--alertmanager.url=http://localhost:9093
--duration=2h
--start="2024-01-15T02:00:00"
--comment="Disk genişletme bakımı - Ayse"
alertname="DiskSpaceCritical" instance="prod-db-01.sirketin.com"
# Aktif silence'ları listele
amtool silence query --alertmanager.url=http://localhost:9093
# Bir silence'ı sona erdir
amtool silence expire --alertmanager.url=http://localhost:9093 <silence-id>
# Mevcut aktif uyarıları kontrol et
amtool alert query --alertmanager.url=http://localhost:9093
Bakım penceresi açmayı alışkanlık haline getirmek ekip disiplini açısından çok önemli. Planlı bakım sırasında uyarı görmezden gelindiğinde, gerçek bir problem yaşandığında fark edilmiyor. Silence koyarsan, bakım bittiğinde otomatik olarak uyarılar yeniden aktif hale gelir.
Slack Bildirim Şablonlarını Özelleştirme
Varsayılan Slack bildirimleri oldukça kuru. İşte daha bilgi dolu bir şablon:
# /etc/alertmanager/templates/slack.tmpl
{{ define "slack.ops.title" }}
{{ if eq .Status "firing" }}
:red_circle: [{{ .GroupLabels.severity | toUpper }}] {{ .GroupLabels.alertname }}
{{ else }}
:large_green_circle: [ÇÖZÜLDÜ] {{ .GroupLabels.alertname }}
{{ end }}
{{ end }}
{{ define "slack.ops.text" }}
*Etkilenen Sunucular:*
{{ range .Alerts }}
• *{{ .Labels.instance }}* - {{ .Annotations.description }}
Başlangıç: {{ .StartsAt | since }}
{{ end }}
{{ if gt (len .Alerts.Firing) 0 }}
*Aktif Uyarılar:* {{ len .Alerts.Firing }}
{{ end }}
{{ if .CommonAnnotations.runbook_url }}
:book: <{{ .CommonAnnotations.runbook_url }}|Runbook>
{{ end }}
{{ end }}
Bu şablonu Alertmanager yapılandırmasına dahil et:
# alertmanager.yml içine ekle
templates:
- '/etc/alertmanager/templates/*.tmpl'
Şablonu kullandığın receiver’da referans ver:
receivers:
- name: 'team-ops-slack'
slack_configs:
- api_url: 'https://hooks.slack.com/services/...'
channel: '#alerts-ops'
title: '{{ template "slack.ops.title" . }}'
text: '{{ template "slack.ops.text" . }}'
send_resolved: true
Yapılandırmayı Test Etmek
Yapılandırma dosyasını canlıya almadan önce mutlaka doğrula:
# Alertmanager konfigürasyonunu kontrol et
amtool check-config /etc/alertmanager/alertmanager.yml
# Belirli bir label setinin hangi receiver'a gideceğini test et
amtool config routes test
--config.file=/etc/alertmanager/alertmanager.yml
severity=critical team=database environment=production
# Şablonları test et
amtool template render
--config.file=/etc/alertmanager/alertmanager.yml
--template.text='{{ template "slack.ops.title" . }}'
--labels='alertname=DiskSpaceCritical,severity=critical'
Bu komutları CI/CD pipeline’ına eklemek altın değerinde. Her PR’da alertmanager config değişikliği varsa otomatik doğrulansın, yanlış yazılmış bir YAML gece production’a gitmesin.
Gerçek Dünya Problemi: Uyarı Yorgunluğu
Bir müşterimizin sisteminde Alertmanager kurulu ama ekip artık uyarılara bakmıyor diye bir durum gördüm. Sorun şuydu: Her gece otomatik job başladığında CPU spike oluyordu ve bu 30-40 uyarı üretiyordu. Ekip uyarılara “zaten yanlış” diye bakıp geçiştirmeye başlamıştı.
Çözüm birkaç adımda geldi:
Birinci adım – Uyarı eşiklerini gerçekçi yap. CPU %70’i geçerse uyarı vermek yerine 10 dakika boyunca %85 üzerinde kalırsa uyar.
İkinci adım – Group ve inhibit kurallarını doğru yapılandır. Aynı host’ta 10 farklı uyarı varsa bunları tek bir bildirimde topla.
Üçüncü adım – repeat_interval değerini artır. Her 30 dakikada bir aynı uyarıyı görmek yerine, resolve olmayan uyarılar için 4 saatte bir hatırlatma yeterli.
Dördüncü adım – Beklenen uyarıları silence veya null-receiver ile sustur.
# Gruplama ile uyarı sayısını azaltma
route:
group_by: ['alertname', 'instance']
group_wait: 60s # 60 saniye bekle, aynı grup başka uyarı gelirse topla
group_interval: 10m # Aynı grup için 10 dakikada bir bildirim
repeat_interval: 6h # Çözülmemiş uyarıları 6 saatte bir tekrarla
High Availability için Cluster Kurulumu
Production ortamında tek Alertmanager noktası olmaz. İki node’lu cluster kurulumu:
# Node 1 başlatma
/usr/local/bin/alertmanager
--config.file=/etc/alertmanager/alertmanager.yml
--storage.path=/var/lib/alertmanager
--cluster.listen-address="0.0.0.0:9094"
--cluster.peer="alertmanager-02.sirketin.com:9094"
--web.listen-address="0.0.0.0:9093"
# Node 2 başlatma
/usr/local/bin/alertmanager
--config.file=/etc/alertmanager/alertmanager.yml
--storage.path=/var/lib/alertmanager
--cluster.listen-address="0.0.0.0:9094"
--cluster.peer="alertmanager-01.sirketin.com:9094"
--web.listen-address="0.0.0.0:9093"
Prometheus tarafında her iki Alertmanager’a da gönderim yap:
# prometheus.yml
alerting:
alertmanagers:
- static_configs:
- targets:
- alertmanager-01.sirketin.com:9093
- alertmanager-02.sirketin.com:9093
Cluster modunda Alertmanager node’ları birbirleriyle gossip protokolüyle haberleşir ve aynı uyarıyı iki kez göndermezler. Silence bilgileri de cluster genelinde paylaşılır.
Web Arayüzü ve API Kullanımı
Alertmanager’ın web arayüzüne http://sunucu-ip:9093 adresinden erişebilirsin. Basit ama işlevsel. Aktif uyarıları görebilir, silence ekleyebilir, silence’ları yönetebilirsin.
API üzerinden de her şeyi yapabilirsin:
# Aktif uyarıları API ile çek
curl -s http://localhost:9093/api/v2/alerts | jq '.[] | {alertname: .labels.alertname, status: .status.state}'
# Yeni silence ekle (JSON ile)
curl -X POST http://localhost:9093/api/v2/silences
-H "Content-Type: application/json"
-d '{
"matchers": [
{"name": "alertname", "value": "DiskSpaceWarning", "isRegex": false},
{"name": "instance", "value": "prod-web-01", "isRegex": false}
],
"startsAt": "2024-01-15T22:00:00Z",
"endsAt": "2024-01-16T02:00:00Z",
"createdBy": "ali.veli",
"comment": "Planlı disk bakımı"
}'
Bu API’yi deployment pipeline’larına entegre etmek çok pratik. Yeni bir deployment başladığında otomatik silence aç, deployment bitince kapat. Böylece deployment sırasında oluşan geçici uyarılar ekibi rahatsız etmez.
Güvenlik Önlemleri
Alertmanager varsayılan olarak authentication gerektirmiyor. Production ortamında bunu değiştirmek şart:
# Nginx ile basic auth ekle
sudo apt install apache2-utils
htpasswd -c /etc/nginx/.alertmanager-htpasswd alertmanager-admin
# Nginx konfigürasyonu
sudo tee /etc/nginx/sites-available/alertmanager > /dev/null <<EOF
server {
listen 443 ssl;
server_name alertmanager.sirketin.com;
ssl_certificate /etc/letsencrypt/live/alertmanager.sirketin.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/alertmanager.sirketin.com/privkey.pem;
auth_basic "Alertmanager";
auth_basic_user_file /etc/nginx/.alertmanager-htpasswd;
location / {
proxy_pass http://localhost:9093;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
EOF
SMTP şifresi ve webhook URL’lerini config dosyasında plain text tutma. Environment variable ile inject et:
# Systemd servis dosyasına ekle
[Service]
EnvironmentFile=/etc/alertmanager/alertmanager.env
# /etc/alertmanager/alertmanager.env
SMTP_PASSWORD=gizli-sifre
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/...
# alertmanager.yml içinde referans
global:
smtp_auth_password: '${SMTP_PASSWORD}'
Sonuç
Alertmanager, düzgün yapılandırıldığında gerçekten hayat kurtarıcı. Ama kötü yapılandırılmış bir Alertmanager, uyarı yorgunluğu yaratarak tam tersi etki yapabilir ve ekibin kritik uyarıları da görmezden gelmesine yol açabilir.
Şu noktalara dikkat edersen başarılı bir kurulum yaparsın: Uyarı eşiklerini gerçekçi tut ve for parametresini doğru ayarla. Gruplamayı iyi kullan, çünkü 50 ayrı bildirim yerine 1 anlamlı bildirim çok daha değerli. Inhibit kurallarıyla kritik uyarıların gürültüye boğulmasını engelle. Planlı bakımlar için mutlaka silence kullan. Şablonları özelleştir ki bildirimler eylem alınabilir bilgi içersin, sadece “bir şey bozuldu” demesin.
Bu altyapı bir kez kurulduktan sonra bakımı nispeten kolay. Yeni bir servis eklediğinde sadece uyarı kuralı yazıp uygun team label’ı koyman yeterli. Routing otomatik olarak doğru ekibe iletecek. Grafana tarafıyla entegrasyon için de bir sonraki yazıda Grafana alerting ve Alertmanager entegrasyonunu ele alacağız.
