CoreDNS Prometheus ile Metrik İzleme

Kubernetes ortamında DNS sorunlarını debug etmeye çalışırken “acaba CoreDNS ne yapıyor?” diye düşündüğünüz oldu mu? Ben bu soruyu kendime ayda en az birkaç kez soruyordum, ta ki Prometheus ile düzgün bir metrik izleme kurulumu yapana kadar. Artık bir şey patlamadan önce neler döndüğünü görebiliyorum ve bu yazıda bunu nasıl yaptığımı paylaşacağım.

CoreDNS Metrik Sistemi Nasıl Çalışır?

CoreDNS, Prometheus formatında metrik sunmak için yerleşik bir plugin’e sahip. Bu plugin prometheus adını taşıyor ve Corefile’a tek satır ekleyerek aktive ediliyor. Ama işin sadeliğine aldanmayın; bu metrikleri doğru okumak ve anlamlandırmak başlı başına bir iş.

CoreDNS’in sunduğu metrikler birkaç ana kategoriye ayrılıyor:

  • coredns_dns_requests_total: Toplam DNS istek sayısı, protocol/family/server/zone/type etiketleriyle
  • coredns_dns_responses_total: Dönülen yanıtlar ve rcode’lar
  • coredns_dns_request_duration_seconds: İstek işleme süresi histogramı
  • coredns_dns_request_size_bytes: İstek boyutları
  • coredns_dns_response_size_bytes: Yanıt boyutları
  • coredns_cache_hits_total: Cache isabeti sayısı
  • coredns_cache_misses_total: Cache ıskalama sayısı
  • coredns_forward_requests_total: Upstream’e yönlendirilen istekler
  • coredns_forward_healthcheck_failures_total: Upstream sağlık kontrol başarısızlıkları

Bu metrikleri okumak için önce CoreDNS’i düzgün yapılandırmak gerekiyor.

CoreDNS Prometheus Plugin Yapılandırması

Temel yapılandırma oldukça basit. Kubernetes’te CoreDNS genellikle kube-system namespace’inde bir ConfigMap ile yönetiliyor.

kubectl get configmap coredns -n kube-system -o yaml

Tipik bir Corefile şöyle görünür:

cat <<EOF > corefile-with-metrics.conf
.:53 {
    errors
    health {
        lameduck 5s
    }
    ready
    prometheus :9153
    kubernetes cluster.local in-addr.arpa ip6.arpa {
        pods insecure
        fallthrough in-addr.arpa ip6.arpa
        ttl 30
    }
    forward . /etc/resolv.conf {
        max_concurrent 1000
    }
    cache 30
    loop
    reload
    loadbalance
}
EOF

Buradaki prometheus :9153 satırı, CoreDNS’in 9153 portunda metrik sunmasını sağlıyor. Eğer bu satır yoksa hiçbir metrik göremezsiniz, bu arada. Kubernetes ortamında ConfigMap’i güncellemek için:

kubectl edit configmap coredns -n kube-system

Değişikliği yaptıktan sonra CoreDNS pod’larının reload plugin’i sayesinde otomatik yeniden yükleme yapması gerekiyor. Bunu doğrulamak için pod loglarına bakabilirsiniz:

kubectl logs -n kube-system -l k8s-app=kube-dns --tail=20

Prometheus ServiceMonitor Kurulumu

Kubernetes üzerinde Prometheus Operator kullanıyorsanız (ve kullanıyor olmalısınız, elle scrape config yazmak artık 2015’te kaldı), ServiceMonitor kaynağı oluşturmanız gerekiyor.

Önce CoreDNS için bir Service tanımı var mı kontrol edin:

kubectl get svc -n kube-system | grep dns

Genellikle kube-dns adında bir servis zaten bulunuyor ama bu servis sadece 53 portunu expose ediyor. Metrikler için 9153 portunu da eklemek ya da ayrı bir servis oluşturmak gerekiyor.

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
  name: coredns-metrics
  namespace: kube-system
  labels:
    app: coredns
    k8s-app: kube-dns
spec:
  selector:
    k8s-app: kube-dns
  ports:
  - name: metrics
    port: 9153
    targetPort: 9153
    protocol: TCP
  clusterIP: None
EOF

Şimdi ServiceMonitor:

cat <<EOF | kubectl apply -f -
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: coredns
  namespace: monitoring
  labels:
    release: prometheus
spec:
  namespaceSelector:
    matchNames:
    - kube-system
  selector:
    matchLabels:
      k8s-app: kube-dns
  endpoints:
  - port: metrics
    interval: 15s
    path: /metrics
    bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
    tlsConfig:
      insecureSkipVerify: true
EOF

Burada dikkat edilmesi gereken bir nokta: release: prometheus label’ı, Prometheus Operator’ın hangi ServiceMonitor’ları scrape edeceğini belirlemek için kullandığı selector ile eşleşmeli. Kendi kurulumunuzda bu label farklı olabilir, şöyle kontrol edin:

kubectl get prometheus -n monitoring -o jsonpath='{.items[0].spec.serviceMonitorSelector}'

Prometheus Olmayan Ortamlar için Statik Scrape Yapılandırması

Kubernetes dışında, örneğin bare metal veya VM üzerinde CoreDNS çalıştırıyorsanız, Prometheus’un prometheus.yml dosyasına statik hedef eklemek daha pratik:

cat <<EOF >> /etc/prometheus/prometheus.yml
scrape_configs:
  - job_name: 'coredns'
    static_configs:
      - targets: ['coredns-server-1:9153', 'coredns-server-2:9153']
    relabel_configs:
      - source_labels: [__address__]
        target_label: instance
      - target_label: job
        replacement: coredns
    scrape_interval: 15s
    scrape_timeout: 10s
EOF

Prometheus’u yeniden yüklemeden önce yapılandırmayı doğrulayın:

promtool check config /etc/prometheus/prometheus.yml

Ardından reload sinyali gönderin:

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

Grafana Dashboard Kurulumu

Grafana’da CoreDNS için hazır bir dashboard var, ID’si 5926. Bunu import etmek için Grafana UI’dan “Import” seçeneğini kullanabilirsiniz. Ama ben genellikle bu hazır dashboard’ları temel alıp kendi ortamıma göre özelleştiriyorum, çünkü hazırların bir kısmı eski metrik isimleriyle yazılmış oluyor.

Grafana API üzerinden dashboard import etmek için:

curl -s -X POST 
  -H "Content-Type: application/json" 
  -H "Authorization: Bearer YOUR_API_KEY" 
  http://grafana:3000/api/dashboards/import 
  -d '{
    "dashboard": {"id": null},
    "overwrite": true,
    "inputs": [{"name": "DS_PROMETHEUS", "type": "datasource", "pluginId": "prometheus", "value": "Prometheus"}],
    "folderId": 0,
    "path": "https://grafana.com/api/dashboards/5926/revisions/latest/download"
  }'

Kritik Alertler ve PromQL Sorguları

İşte gerçekten değerli kısım burası. Metrik toplamak güzel, ama ne zaman alarm üreteceğinizi bilmek daha önemli.

Günlük operasyonda kullandığım temel PromQL sorgularından bazıları:

DNS hata oranı:

# SERVFAIL oranı yüksek mi?
sum(rate(coredns_dns_responses_total{rcode="SERVFAIL"}[5m])) 
  / 
sum(rate(coredns_dns_responses_total[5m])) * 100

Cache hit oranı:

sum(rate(coredns_cache_hits_total[5m])) 
  / 
(sum(rate(coredns_cache_hits_total[5m])) + sum(rate(coredns_cache_misses_total[5m]))) * 100

95. persentil yanıt süresi:

histogram_quantile(0.95, 
  sum(rate(coredns_dns_request_duration_seconds_bucket[5m])) by (le, server)
)

Upstream forward hataları:

rate(coredns_forward_healthcheck_failures_total[5m]) > 0

Bu sorguları Prometheus alert rule’larına dönüştürmek için:

cat <<EOF > /etc/prometheus/rules/coredns.yml
groups:
- name: coredns
  interval: 30s
  rules:
  - alert: CoreDNSHighErrorRate
    expr: |
      sum(rate(coredns_dns_responses_total{rcode=~"SERVFAIL|REFUSED"}[5m])) 
        / sum(rate(coredns_dns_responses_total[5m])) * 100 > 5
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "CoreDNS hata oranı yüksek"
      description: "Son 5 dakikada DNS hata oranı {{ $value | printf "%.2f" }}% olarak ölçüldü."

  - alert: CoreDNSLatencyHigh
    expr: |
      histogram_quantile(0.99, 
        sum(rate(coredns_dns_request_duration_seconds_bucket[5m])) by (le)
      ) > 0.5
    for: 10m
    labels:
      severity: warning
    annotations:
      summary: "CoreDNS yanıt süresi yüksek"
      description: "P99 yanıt süresi {{ $value | printf "%.3f" }} saniyenin üzerinde."

  - alert: CoreDNSUpstreamFailure
    expr: |
      increase(coredns_forward_healthcheck_failures_total[5m]) > 3
    for: 2m
    labels:
      severity: critical
    annotations:
      summary: "CoreDNS upstream DNS erişilemiyor"
      description: "{{ $labels.to }} upstream'ine sağlık kontrolleri başarısız oluyor."

  - alert: CoreDNSCacheHitRateLow
    expr: |
      sum(rate(coredns_cache_hits_total[10m])) 
        / (sum(rate(coredns_cache_hits_total[10m])) + sum(rate(coredns_cache_misses_total[10m]))) * 100 < 60
    for: 15m
    labels:
      severity: info
    annotations:
      summary: "CoreDNS cache hit oranı düşük"
      description: "Cache hit oranı %{{ $value | printf "%.1f" }} seviyesinde, cache boyutunu artırmayı değerlendirin."
EOF

Gerçek Dünya Senaryosu: Gece Yarısı DNS Krizi

Geçen yıl yaşanan bir olayı anlatayım. Bir Kubernetes cluster’ında servisler arasında intermittent bağlantı kopmaları yaşanmaya başladı. Uygulama ekibi “bazen çalışıyor bazen çalışmıyor” diyordu, ki bu her sysadmin’in korkulu rüyasıdır.

Grafana’yı açtığımda coredns_forward_healthcheck_failures_total metriğinin gece 02:00-04:00 arasında spike yaptığını gördüm. Aynı saatlerde coredns_dns_request_duration_seconds P99 değeri 500ms’nin üzerine çıkıyordu.

Prometheus’ta daha derine indim:

# Hangi zone'lara yapılan istekler yavaş?
histogram_quantile(0.95, 
  sum(rate(coredns_dns_request_duration_seconds_bucket[5m])) by (le, zone)
)

Sonuç çok netti: Cluster dışı DNS sorguları yavaşlıyordu, internal sorgular değil. CoreDNS’in forward ettiği upstream DNS sunucusu (şirket içi bir DNS resolver) gece yarısı bakım penceresi nedeniyle yavaşlıyormuş. Hiç kimse bunu CoreDNS ekibine söylememiş tabii.

Çözüm: Corefile’a ikincil bir upstream ekledik ve health_check interval’ını düşürdük:

forward . 10.0.0.53 8.8.8.8 {
    max_concurrent 1000
    health_check 5s
    policy sequential
}

Metriklerin olmadığı dönemde bu sorunu bulmak günler sürerdi. Metriklerle birlikte 20 dakikada çözdük.

CoreDNS Metrik Derinlikleri: Labels ile Analiz

CoreDNS metrikleri üzerindeki label’ları iyi anlamak, granüler analiz yapabilmenin anahtarı. Özellikle büyük cluster’larda hangi namespace’in ne kadar DNS sorgusu ürettiğini görmek isteyebilirsiniz.

Kubernetes’te CoreDNS’e gelen sorgularda kubernetes plugin’i zone bilgisini ekliyor. Bunu kullanarak:

# Zone bazında istek dağılımı
sum by (zone) (rate(coredns_dns_requests_total[5m]))

# Protokol bazında (UDP vs TCP)
sum by (proto) (rate(coredns_dns_requests_total[5m]))

# Sorgu tipi bazında (A, AAAA, SRV, PTR...)
sum by (type) (rate(coredns_dns_requests_total[5m]))

type bazında analiz yaparken PTR sorgularının beklenmedik biçimde yüksek olduğunu görmek çok yaygın bir bulgu. Bu genellikle uygulamaların IP adreslerini sürekli reverse lookup yaptığı anlamına geliyor ve çoğunlukla gereksiz bir yük. Bunu tespit edip uygulama ekiplerine bildirmek CoreDNS yükünü ciddi oranda düşürebiliyor.

Performans Baselining

Bir ortama yeni geldiğinizde veya büyük değişiklikler öncesinde baseline almak kritik. Şu recording rule’ları kullanıyorum:

cat <<EOF >> /etc/prometheus/rules/coredns-recording.yml
groups:
- name: coredns-recording
  interval: 1m
  rules:
  - record: job:coredns_dns_requests:rate5m
    expr: sum(rate(coredns_dns_requests_total[5m])) by (server, zone)

  - record: job:coredns_error_rate:ratio5m
    expr: |
      sum(rate(coredns_dns_responses_total{rcode!="NOERROR"}[5m])) by (server)
        /
      sum(rate(coredns_dns_responses_total[5m])) by (server)

  - record: job:coredns_cache_hit_ratio:rate5m
    expr: |
      sum(rate(coredns_cache_hits_total[5m]))
        /
      (sum(rate(coredns_cache_hits_total[5m])) + sum(rate(coredns_cache_misses_total[5m])))
EOF

Recording rule’lar karmaşık sorguları önceden hesaplayıp saklamak için kullanılır. Özellikle Grafana dashboard’larınızda sık sorgulanan metrikler için performans açısından çok fark yaratıyor, büyük Prometheus deployment’larında bunu mutlaka yapın.

Metrik Erişimini Doğrulama

Her şeyi kurduğunuzda gerçekten çalışıp çalışmadığını test etmek için:

# CoreDNS metrik endpoint'ine doğrudan erişim
kubectl exec -it -n kube-system $(kubectl get pods -n kube-system -l k8s-app=kube-dns -o name | head -1) -- wget -qO- http://localhost:9153/metrics | grep coredns_dns_requests_total | head -5

# Prometheus'un hedefi scrape edip etmediğini kontrol etme
curl -s http://prometheus:9090/api/v1/targets | python3 -m json.tool | grep -A 5 coredns

# Kısa süreli yük testi ile metriklerin değişip değişmediğini görmek
for i in $(seq 1 100); do
  kubectl exec -it -n default test-pod -- nslookup kubernetes.default.svc.cluster.local > /dev/null 2>&1
done

Metriklerin gerçekten güncellendiğini Prometheus query browser’dan da doğrulayabilirsiniz. coredns_dns_requests_total için increase fonksiyonu kullanarak son birkaç dakikadaki artışı görebilirsiniz.

Sonuç

CoreDNS metrik izlemesi, Kubernetes ortamlarında genellikle ihmal edilen ama kritik bir alan. DNS sorunları çoğunlukla “aralıklı bağlantı hatası” ya da “servis bazen erişilemiyor” gibi belirsiz şikayetlerle yüzeye çıkar ve metriklerin olmadığı ortamlarda saatlerce debug sürecine dönüşür.

Öncelik sıralaması yapmam gerekirse: önce prometheus plugin’ini aktive edin, sonra SERVFAIL oranı ve upstream sağlık alertlerini kurun. Dashboard güzel ama alertler hayat kurtarır. Cache hit oranı da sürekli gözlemlenmesi gereken bir metrik; düşük cache hit oranı genellikle yanlış yapılandırılmış TTL değerlerinin ya da aşırı dinamik DNS sorgularının işareti.

Recording rule’ları eklemek, uzun vadeli trend analizi için gerekli. Özellikle cluster büyüdükçe DNS yükünün nasıl arttığını görmek, kapasite planlaması için değerli bir girdi sağlıyor.

Son olarak şunu söyleyeyim: bu metrikleri kurmak bir saatinizi alır. Ama ilk DNS krizinde sizi saatlik debug maratonundan kurtardığında, o bir saatin ne kadar iyi yatırım olduğunu anlayacaksınız.

Bir yanıt yazın

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