MySQL İzleme: Prometheus ve Grafana Entegrasyonu

Veritabanı izleme konusu birçok sysadmin’in ertelediği ama sonradan pişman olduğu bir alan. “Her şey çalışıyor, neden izleyeyim?” diye düşünürsün, ta ki üretim veritabanın gece 2’de yanıt vermeyi bırakana kadar. Prometheus ve Grafana kombinasyonu bu iş için gerçekten iyi bir çözüm sunuyor: birincisi veriyi toplar, ikincisi anlamlı hale getirir. Bu yazıda sıfırdan kurulumdan gerçek dünyada kullanılan alert kurallarına kadar her şeyi ele alacağız.

Neden Prometheus + Grafana?

Piyasada MySQL izleme için onlarca araç var. Percona Monitoring and Management (PMM) hazır gelir, Datadog ücretlidir, Zabbix kurulumu karmaşıktır. Ama Prometheus ve Grafana kombinasyonu özellikle şu sebeplerle öne çıkıyor:

  • Açık kaynak ve ücretsiz: Lisans maliyeti yok, topluluk desteği güçlü
  • Esnek mimari: Kendi metrik toplayıcılarını yazabilirsin
  • Grafana ekosistemi: Hazır dashboard’lar mevcut, özelleştirme kolay
  • Uzun vadeli depolama: PromQL ile güçlü sorgular yazabilirsin
  • Alert yönetimi: Alertmanager ile gelişmiş bildirim sistemi

Küçük bir startup’tan büyük ölçekli bir altyapıya kadar uyarlanabiliyor. Biz bu yazıda Ubuntu 22.04 üzerinde çalışacağız ama CentOS/RHEL için de adımlar büyük ölçüde benzer.

Mimariyi Anlamak

Kuruluma geçmeden önce kafanızda bir resim oluşturalım. Sistem şu şekilde çalışıyor:

  • mysqld_exporter: MySQL sunucusuna bağlanır, metrikleri HTTP üzerinden expose eder
  • Prometheus: Belirli aralıklarla mysqld_exporter’dan metrikleri çeker (scrape)
  • Grafana: Prometheus’u data source olarak kullanır, dashboard’lar oluşturur
  • Alertmanager: Prometheus’tan gelen alert’leri işler, bildirimleri iletir

Veri akışı tek yönlü: MySQL -> mysqld_exporter -> Prometheus -> Grafana. Bu mimari sayesinde bileşenler birbirinden bağımsız. Grafana çökse Prometheus veri toplamaya devam eder.

Ön Hazırlık

Ortamımızı netleştirelim:

  • MySQL sunucusu: 192.168.1.10
  • Prometheus + Grafana sunucusu: 192.168.1.20
  • İşletim sistemi: Ubuntu 22.04 LTS

Gerçek dünyada mysqld_exporter’ı MySQL sunucusunun üzerine kurmanı öneririm. Ağ gecikmesi azalır ve güvenlik tarafında MySQL’in local socket’ini kullanabilirsin.

MySQL’de İzleme Kullanıcısı Oluşturma

mysqld_exporter’ın MySQL’e bağlanabilmesi için özel bir kullanıcı oluşturmak gerekiyor. Bu kullanıcıya sadece gereken izinleri ver, asla root kullanma.

mysql -u root -p
CREATE USER 'exporter'@'localhost' IDENTIFIED BY 'GucluBirSifre123!' WITH MAX_USER_CONNECTIONS 3;

GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'exporter'@'localhost';

GRANT SELECT ON performance_schema.* TO 'exporter'@'localhost';

FLUSH PRIVILEGES;

EXIT;

WITH MAX_USER_CONNECTIONS 3 kısmı önemli. Exporter’ın aşırı bağlantı açmasını engeller. REPLICATION CLIENT izni slave durumunu izlemek için gerekiyor.

mysqld_exporter Kurulumu

MySQL sunucusunda (192.168.1.10) çalışıyoruz:

# En son versiyonu kontrol et
wget https://github.com/prometheus/mysqld_exporter/releases/download/v0.15.1/mysqld_exporter-0.15.1.linux-amd64.tar.gz

tar xzf mysqld_exporter-0.15.1.linux-amd64.tar.gz

sudo mv mysqld_exporter-0.15.1.linux-amd64/mysqld_exporter /usr/local/bin/

sudo chmod +x /usr/local/bin/mysqld_exporter

# Versiyon kontrolü
mysqld_exporter --version

Şimdi kimlik bilgileri dosyasını oluşturalım. Şifreyi komut satırına yazmak güvenli değil:

sudo mkdir -p /etc/mysqld_exporter

sudo tee /etc/mysqld_exporter/.my.cnf << 'EOF'
[client]
user=exporter
password=GucluBirSifre123!
host=localhost
socket=/var/run/mysqld/mysqld.sock
EOF

sudo chmod 600 /etc/mysqld_exporter/.my.cnf
sudo chown root:root /etc/mysqld_exporter/.my.cnf

Systemd servis dosyası oluşturalım:

sudo tee /etc/systemd/system/mysqld_exporter.service << 'EOF'
[Unit]
Description=MySQL Exporter for Prometheus
After=network.target mysql.service

[Service]
Type=simple
User=root
ExecStart=/usr/local/bin/mysqld_exporter 
  --config.my-cnf=/etc/mysqld_exporter/.my.cnf 
  --collect.global_status 
  --collect.global_variables 
  --collect.slave_status 
  --collect.info_schema.innodb_metrics 
  --collect.info_schema.processlist 
  --collect.info_schema.tables 
  --collect.perf_schema.eventswaits 
  --collect.perf_schema.file_events 
  --collect.perf_schema.tablelocks 
  --web.listen-address=:9104

Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable mysqld_exporter
sudo systemctl start mysqld_exporter
sudo systemctl status mysqld_exporter

Çalışıp çalışmadığını test edelim:

curl -s http://localhost:9104/metrics | head -50

Ekranda mysql_global_status_connections gibi metrikler göreceksin. Eğer görüyorsan kurulum başarılı.

Firewall Ayarı

Prometheus sunucusunun bu porta erişebilmesi için:

sudo ufw allow from 192.168.1.20 to any port 9104

Tüm ağa açma. Sadece Prometheus sunucusuna izin ver.

Prometheus Kurulumu

Prometheus sunucusunda (192.168.1.20) çalışıyoruz:

wget https://github.com/prometheus/prometheus/releases/download/v2.48.0/prometheus-2.48.0.linux-amd64.tar.gz

tar xzf prometheus-2.48.0.linux-amd64.tar.gz

sudo useradd --no-create-home --shell /bin/false prometheus

sudo mkdir -p /etc/prometheus /var/lib/prometheus

sudo mv prometheus-2.48.0.linux-amd64/prometheus /usr/local/bin/
sudo mv prometheus-2.48.0.linux-amd64/promtool /usr/local/bin/
sudo mv prometheus-2.48.0.linux-amd64/consoles /etc/prometheus/
sudo mv prometheus-2.48.0.linux-amd64/console_libraries /etc/prometheus/

sudo chown -R prometheus:prometheus /etc/prometheus /var/lib/prometheus

Prometheus konfigürasyonunu yazalım:

sudo tee /etc/prometheus/prometheus.yml << 'EOF'
global:
  scrape_interval: 15s
  evaluation_interval: 15s
  scrape_timeout: 10s

alerting:
  alertmanagers:
    - static_configs:
        - targets:
          - localhost:9093

rule_files:
  - "/etc/prometheus/rules/mysql_alerts.yml"

scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  - job_name: 'mysql_production'
    static_configs:
      - targets: ['192.168.1.10:9104']
    labels:
      environment: production
      team: backend

  - job_name: 'mysql_staging'
    static_configs:
      - targets: ['192.168.1.11:9104']
    labels:
      environment: staging
      team: backend
EOF

Birden fazla MySQL sunucusu izliyorsan her biri için ayrı job tanımlamak yerine file_sd_configs kullanabilirsin. Dinamik ortamlarda çok kullanışlı oluyor.

Alert kuralları dizinini oluşturalım:

sudo mkdir -p /etc/prometheus/rules
sudo chown prometheus:prometheus /etc/prometheus/rules

Systemd servis dosyası:

sudo tee /etc/systemd/system/prometheus.service << 'EOF'
[Unit]
Description=Prometheus Monitoring
After=network.target

[Service]
User=prometheus
Group=prometheus
Type=simple
ExecStart=/usr/local/bin/prometheus 
  --config.file=/etc/prometheus/prometheus.yml 
  --storage.tsdb.path=/var/lib/prometheus 
  --storage.tsdb.retention.time=30d 
  --web.enable-lifecycle 
  --web.listen-address=0.0.0.0:9090

Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable prometheus
sudo systemctl start prometheus

--storage.tsdb.retention.time=30d ile 30 günlük veri saklıyoruz. Disk alanına göre bunu ayarlayabilirsin. Genellikle yoğun bir MySQL sunucusu için 30 günlük veri yaklaşık 2-5 GB yer tutar.

Grafana Kurulumu

sudo apt-get install -y apt-transport-https software-properties-common

wget -q -O - https://packages.grafana.com/gpg.key | sudo apt-key add -

echo "deb https://packages.grafana.com/oss/deb stable main" | sudo tee /etc/apt/sources.list.d/grafana.list

sudo apt-get update
sudo apt-get install -y grafana

sudo systemctl enable grafana-server
sudo systemctl start grafana-server

Grafana varsayılan olarak 3000 portunda çalışır. Tarayıcıdan http://192.168.1.20:3000 adresine git. Varsayılan kullanıcı adı ve şifre admin/admin. İlk girişte şifreyi değiştirmeni isteyecek, değiştir.

Prometheus’u Data Source Olarak Ekleme

Grafana arayüzünde:

  • Sol menüden Configuration > Data Sources yolunu izle
  • Add data source tıkla
  • Prometheus seç
  • URL olarak http://localhost:9090 gir
  • Save & Test tıkla

Yeşil “Data source is working” mesajı görmelisin.

Hazır Dashboard Yükleme

Sıfırdan dashboard yazmak yerine topluluk tarafından hazırlanan dashboard’ları kullanabilirsin. Grafana.com üzerinde MySQL için popüler olan ID’ler:

  • 7362: MySQL Overview (mysqld_exporter için optimize edilmiş)
  • 14057: MySQL Exporter Quickstart

Dashboard import etmek için:

  • Sol menüden Dashboards > Import
  • Dashboard ID’sini gir (örn: 7362)
  • Load tıkla
  • Prometheus data source seç
  • Import tıkla

Anında onlarca metriği gösteren bir dashboard karşına çıkacak.

MySQL Alert Kuralları

Grafana güzel görünüm sağlar ama asıl kritik olan erken uyarı sistemi. Şimdi production ortamında gerçekten işe yarayan alert kuralları yazalım:

sudo tee /etc/prometheus/rules/mysql_alerts.yml << 'EOF'
groups:
  - name: mysql_alerts
    interval: 30s
    rules:
      # MySQL down oldu
      - alert: MySQLDown
        expr: mysql_up == 0
        for: 30s
        labels:
          severity: critical
          team: dba
        annotations:
          summary: "MySQL {{ $labels.instance }} erişilemiyor"
          description: "MySQL sunucusu 30 saniyeden fazladır yanıt vermiyor."

      # Çok fazla aktif bağlantı
      - alert: MySQLTooManyConnections
        expr: (mysql_global_status_threads_connected / mysql_global_variables_max_connections) * 100 > 80
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: "MySQL bağlantıları kritik seviyede"
          description: "Bağlantı kullanımı %{{ $value | printf "%.1f" }}"

      # Yavaş query oranı yüksek
      - alert: MySQLSlowQueriesHigh
        expr: rate(mysql_global_status_slow_queries[5m]) > 5
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "MySQL yavaş query oranı yüksek"
          description: "Son 5 dakikada saniyede {{ $value | printf "%.2f" }} yavaş query"

      # InnoDB buffer pool doluluk oranı
      - alert: MySQLInnoDBBufferPoolFull
        expr: (1 - (mysql_global_status_innodb_buffer_pool_pages_free / mysql_global_status_innodb_buffer_pool_pages_total)) * 100 > 95
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "InnoDB Buffer Pool neredeyse dolu"
          description: "Buffer pool doluluk oranı %{{ $value | printf "%.1f" }}"

      # Replication lag
      - alert: MySQLReplicationLag
        expr: mysql_slave_status_seconds_behind_master > 30
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "MySQL replikasyon gecikmesi yüksek"
          description: "Slave {{ $labels.instance }} masterin {{ $value }}s gerisinde"

      # Disk I/O beklemeleri
      - alert: MySQLHighDiskIOWait
        expr: rate(mysql_global_status_innodb_data_reads[5m]) > 1000
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "MySQL disk okuma yoğunluğu yüksek"
          description: "Saniyede {{ $value | printf "%.0f" }} disk okuma işlemi"
EOF

sudo chown prometheus:prometheus /etc/prometheus/rules/mysql_alerts.yml

Kural dosyasını doğrulayalım:

promtool check rules /etc/prometheus/rules/mysql_alerts.yml

Prometheus’u yeniden yükleyelim:

sudo systemctl reload prometheus

Ya da API üzerinden:

curl -X POST http://localhost:9090/-/reload

Alertmanager Kurulumu

Alert kuralları yazdık ama bunları e-posta veya Slack’e göndermek için Alertmanager gerekiyor:

wget https://github.com/prometheus/alertmanager/releases/download/v0.26.0/alertmanager-0.26.0.linux-amd64.tar.gz

tar xzf alertmanager-0.26.0.linux-amd64.tar.gz

sudo mv alertmanager-0.26.0.linux-amd64/alertmanager /usr/local/bin/
sudo mv alertmanager-0.26.0.linux-amd64/amtool /usr/local/bin/

sudo mkdir -p /etc/alertmanager /var/lib/alertmanager

Alertmanager konfigürasyonu. Slack ve e-posta birlikte kullanıyoruz:

sudo tee /etc/alertmanager/alertmanager.yml << 'EOF'
global:
  resolve_timeout: 5m
  smtp_smarthost: 'smtp.gmail.com:587'
  smtp_from: '[email protected]'
  smtp_auth_username: '[email protected]'
  smtp_auth_password: 'uygulama-sifresi'
  slack_api_url: 'https://hooks.slack.com/services/XXXXX/YYYYY/ZZZZZ'

route:
  group_by: ['alertname', 'environment']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
  receiver: 'default'
  routes:
    - match:
        severity: critical
      receiver: 'critical-alerts'
      repeat_interval: 1h
    - match:
        severity: warning
      receiver: 'warning-alerts'

receivers:
  - name: 'default'
    slack_configs:
      - channel: '#db-alerts'
        title: 'Prometheus Alert'
        text: '{{ range .Alerts }}{{ .Annotations.description }}{{ end }}'

  - name: 'critical-alerts'
    slack_configs:
      - channel: '#db-critical'
        title: 'KRITIK: {{ .CommonAnnotations.summary }}'
        text: '{{ range .Alerts }}{{ .Annotations.description }}{{ end }}'
    email_configs:
      - to: '[email protected]'
        subject: '[KRITIK] {{ .CommonAnnotations.summary }}'

  - name: 'warning-alerts'
    slack_configs:
      - channel: '#db-alerts'
        title: 'UYARI: {{ .CommonAnnotations.summary }}'
        text: '{{ range .Alerts }}{{ .Annotations.description }}{{ end }}'

inhibit_rules:
  - source_match:
      severity: 'critical'
    target_match:
      severity: 'warning'
    equal: ['instance']
EOF

inhibit_rules kısmı önemli bir detay. Bir instance için critical alert tetiklendiğinde aynı instance’ın warning alert’lerini susturur. Yoksa onlarca alert mesajı alırsın.

Gerçek Dünya Senaryosu: Yavaş Query Tespiti

Diyelim ki monitoring sistemin kurulu ve gece 3’te Slow Queries alert’i geldi. Sabah geldiğinde Grafana’dan baktın, belirli bir saatte yavaş query sayısının patladığını gördün. Şimdi ne yapacaksın?

Performance Schema aktifleştir ve detaylı analiz yap:

mysql -u root -p << 'EOF'
-- Performance schema açık mı?
SELECT @@performance_schema;

-- En yavaş queryler
SELECT
  DIGEST_TEXT,
  COUNT_STAR as calisma_sayisi,
  ROUND(AVG_TIMER_WAIT/1000000000, 2) as ort_sure_sn,
  ROUND(MAX_TIMER_WAIT/1000000000, 2) as max_sure_sn
FROM performance_schema.events_statements_summary_by_digest
WHERE SCHEMA_NAME IS NOT NULL
ORDER BY AVG_TIMER_WAIT DESC
LIMIT 10;
EOF

Grafana’da özel bir panel oluşturmak için şu PromQL sorgusunu kullanabilirsin:

# Son 1 saatte toplam yavaş query sayısı
increase(mysql_global_status_slow_queries[1h])

# Saniyedeki yavaş query oranı (5 dakika penceresi)
rate(mysql_global_status_slow_queries[5m])

# Buffer pool hit ratio - %98 üzerinde olmalı
(mysql_global_status_innodb_buffer_pool_read_requests /
  (mysql_global_status_innodb_buffer_pool_read_requests +
   mysql_global_status_innodb_buffer_pool_reads)) * 100

Buffer pool hit ratio düştüyse genellikle innodb_buffer_pool_size‘ı artırmak gerekir. Bu metriği Grafana’da takip etmek, sunucuya ne zaman RAM eklenmesi gerektiğini planlamamı sağlıyor.

İzlenmesi Gereken Kritik Metrikler

Grafana dashboard’unda mutlaka olması gereken paneller ve onların PromQL sorguları:

Aktif bağlantı sayısı: mysql_global_status_threads_connected

Saniyede işlem sayısı (TPS): rate(mysql_global_status_commands_total{command="commit"}[5m])

Query önbellek hit oranı (MySQL 5.7 için): rate(mysql_global_status_qcache_hits[5m]) / (rate(mysql_global_status_qcache_hits[5m]) + rate(mysql_global_status_qcache_inserts[5m]))

InnoDB row lock beklemeleri: rate(mysql_global_status_innodb_row_lock_waits[5m])

Replikasyon durumu: mysql_slave_status_slave_sql_running

Row lock beklemeleri aniden yükseldiyse deadlock problemi yaşanıyor olabilir. Bu durumda:

mysql -u root -p -e "SHOW ENGINE INNODB STATUSG" | grep -A 30 "LATEST DETECTED DEADLOCK"

Ek İpuçları

Metric cardinality dikkat: mysqld_exporter’ın --collect.info_schema.tables seçeneği çok sayıda tablo olan veritabanlarında Prometheus’u yavaşlatabilir. Yüzlerce tablo varsa bu toplama özelliğini ya kapat ya da tabloları filtrele.

Retention vs. depolama dengesi: 30 günlük veri tutmak yerine Thanos veya VictoriaMetrics kullanarak uzun vadeli depolama yapabilirsin. Özellikle kapasite planlaması için aylık trendlere bakmak gerekiyor.

Güvenlik: Prometheus ve Grafana portlarını internete açma. Nginx reverse proxy arkasına al ve HTTPS kullan. mysqld_exporter için TLS de yapılandırabilirsin.

Test ortamı uyarısı: Alert eşiklerini test ortamında ayarlarken production’dan farklı değerler kullan. Test ortamında bağlantı sayısı 5’i geçince alert alırsan birkaç haftada bildirimlerden bıkarsın.

Sonuç

Bu kurulumu tamamladıktan sonra MySQL sunucunun ne durumda olduğunu artık tahmin etmek zorunda değilsin. Prometheus verisi topluyor, Grafana görselleştiriyor, Alertmanager seni uyarıyor. Gece 3’teki o “veritabanı neden yavaş?” sorusunun cevabı artık Grafana dashboard’unda seni bekliyor olacak.

Kurulumun ilk haftasında alert eşiklerini mutlaka gözden geçir. Çok hassas eşikler yorgunluk yaratır, çok gevşek eşikler ise asıl sorunu gözden kaçırır. Bir ay boyunca metrikleri gözlemleyip kendi ortamına özgü “normal” değerleri bulduktan sonra alert kurallarını buna göre fine-tune et. Her ortam farklı: bir e-ticaret sitesinin trafiği gün içinde dalgalanır, bir raporlama sisteminin yükü gece artar. Bu farklılıklara göre dinamik eşikler yazmak da mümkün ama bu konuyu başka bir yazıya bırakalım.

Yorum yapın