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ımrepeat_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.

Benzer Konular

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir