CoreDNS ile Servis Mesh DNS Entegrasyonu

Kubernetes ortamında servis keşfi üzerine çalışırken en çok baş ağrıtan konulardan biri DNS katmanının doğru yapılandırılmasıdır. Özellikle Istio veya Linkerd gibi bir servis mesh kurduğunuzda, CoreDNS’in bu ekosistemle nasıl konuştuğunu anlamadan ciddi sorunlarla karşılaşabilirsiniz. Ben de geçen yıl büyük bir finansal teknoloji şirketinin Kubernetes geçiş projesinde tam olarak bu problemle boğuştum. O deneyimden edindiğim bilgileri burada paylaşmak istedim.

CoreDNS ve Servis Mesh: Neden Birlikte Düşünmek Gerekir

CoreDNS, Kubernetes 1.13’ten itibaren varsayılan DNS çözümleyici olarak kube-dns’in yerini aldı. Plugin tabanlı mimarisi sayesinde son derece esnek bir yapı sunuyor. Ancak Istio gibi bir servis mesh devreye girdiğinde, DNS katmanı artık sadece servis isimlerini IP’lere çevirmekle kalmıyor; trafik yönetimi, güvenlik politikaları ve gözlemlenebilirlik gibi katmanlarla iç içe geçiyor.

Servis mesh’in DNS’e dokunduğu en kritik nokta, sidecar proxy’lerin trafik yönlendirmesi sırasında ortaya çıkıyor. Envoy proxy (Istio’nun kullandığı), gelen DNS sorgularını kendi yönlendirme tablosuna göre işliyor ve bu süreçte CoreDNS ile uyumlu çalışması şart. Aksi durumda, servisler birbirini DNS üzerinden bulamıyor ya da yanlış endpoint’lere ulaşıyor.

Şimdi adım adım nasıl bir yapı kurulabileceğine bakalım.

Temel CoreDNS Yapılandırması

Öncelikle mevcut CoreDNS ConfigMap’inizi kontrol etmeniz gerekiyor:

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

Tipik bir çıktı şöyle görünür:

apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns
  namespace: kube-system
data:
  Corefile: |
    .:53 {
        errors
        health {
           lameduck 5s
        }
        ready
        kubernetes cluster.local in-addr.arpa ip6.arpa {
           pods insecure
           fallthrough in-addr.arpa ip6.arpa
           ttl 30
        }
        prometheus :9153
        forward . /etc/resolv.conf {
           max_concurrent 1000
        }
        cache 30
        loop
        reload
        loadbalance
    }

Bu yapılandırma yeterli gibi görünse de servis mesh senaryolarında birkaç kritik eksiklik içeriyor. Özellikle multicluster veya federated yapılarda external DNS sorgularının nasıl işleneceği tanımlanmamış.

Istio ile CoreDNS Entegrasyonu

Istio kurulumu sonrasında istio-system namespace’inde kendi DNS bileşenleri çalışmaya başlar. Ancak asıl entegrasyon, ServiceEntry ve VirtualService kaynaklarının DNS katmanıyla buluştuğu noktada gerçekleşir.

Şu senaryoyu ele alalım: Uygulamamız payment-service.production.svc.cluster.local adresine istek gönderiyor, ancak bu servis başka bir cluster’da çalışıyor. Istio’nun bu durumu çözmesi için CoreDNS’e doğru yönlendirmeyi yapmamız gerekiyor.

İlk adımda özel bir DNS stub zone yapılandırması ekliyoruz:

kubectl edit configmap coredns -n kube-system

Corefile’a şunu ekliyoruz:

apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns
  namespace: kube-system
data:
  Corefile: |
    .:53 {
        errors
        health {
           lameduck 5s
        }
        ready
        kubernetes cluster.local in-addr.arpa ip6.arpa {
           pods insecure
           fallthrough in-addr.arpa ip6.arpa
           ttl 30
        }
        prometheus :9153
        forward . /etc/resolv.conf {
           max_concurrent 1000
        }
        cache 30
        loop
        reload
        loadbalance
    }
    
    remote.cluster.local:53 {
        errors
        cache 30
        forward . 10.96.0.11 {
            force_tcp
        }
    }

Buradaki 10.96.0.11, remote cluster’daki CoreDNS servisinin cluster IP’si. Bu konfigürasyon, remote.cluster.local domain’ine gelen sorguları remote cluster’ın DNS sunucusuna yönlendiriyor.

ServiceEntry ile DNS Kaydı Yönetimi

Istio ekosisteminde dış servisleri cluster içinden erişilebilir kılmak için ServiceEntry kullanıyoruz. Ancak bu yeterli değil; CoreDNS’in bu girişleri tanıması gerekiyor.

Örnek bir ServiceEntry:

apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
  name: external-payment-api
  namespace: production
spec:
  hosts:
  - payment.external-provider.com
  ports:
  - number: 443
    name: https
    protocol: HTTPS
  location: MESH_EXTERNAL
  resolution: DNS
  endpoints:
  - address: payment.external-provider.com

Bu tanımın ardından CoreDNS’e özel bir forward kuralı ekliyoruz:

payment.external-provider.com:53 {
    errors
    cache 10
    forward . 8.8.8.8 8.8.4.4 {
        prefer_udp
        health_check 5s
    }
}

Bu şekilde payment.external-provider.com‘a gelen DNS sorguları Google’ın public DNS’ine yönlendirilirken, cluster içi sorgular kube-dns üzerinden çözümleniyor.

DNS Cache Optimizasyonu ve TTL Ayarları

Servis mesh ortamlarında DNS cache yönetimi kritik bir performans faktörü. Yanlış TTL değerleri hem gereksiz DNS trafiğine hem de stale (eski) kayıtlar nedeniyle bağlantı hatalarına yol açabilir.

Bir üretim ortamında yaşadığım somut sorunu paylaşayım: Deployment güncellemelerinde pod IP’leri değişiyor ama bazı servisler eski IP’lere bağlanmaya devam ediyordu. Sorun, CoreDNS cache’inin 30 saniyelik TTL ile çalışmasına karşın Istio sidecar proxy’nin kendi cache’inin 300 saniye olarak ayarlanmış olmasıydı.

Bu durumu düzeltmek için önce mevcut cache davranışını inceliyoruz:

# CoreDNS metrics'ini kontrol et
kubectl port-forward -n kube-system svc/kube-dns 9153:9153
curl http://localhost:9153/metrics | grep coredns_cache

Ardından cache plugin’ini daha ayrıntılı yapılandırıyoruz:

cache 30 {
    success 9984 30
    denial 9984 5
    servfail 0
    prefetch 10 1m 10%
    serve_stale 5s
}

Bu yapılandırmada:

  • success 9984 30: Başarılı yanıtlar için maksimum 9984 kayıt, 30 saniye TTL
  • denial 9984 5: NXDOMAIN yanıtları için 5 saniye TTL (kısa tutuyoruz, yeni servisler hızlı bulunabilsin)
  • prefetch 10 1m 10%: Sık sorgulanan kayıtlar TTL dolmadan yenilenir
  • serve_stale 5s: Upstream erişilemez olduğunda 5 saniye eski yanıt servis edilir

Linkerd İçin Özel Yapılandırma

Linkerd, Istio’ya kıyasla daha az DNS müdahalesine ihtiyaç duyuyor ancak kendi DNS proxy’si olan linkerd-proxy‘nin CoreDNS ile doğru konuşması gerekiyor. Özellikle multicluster kurulumda ServiceMirror bileşeni, remote cluster servislerini yerel cluster’da mirror’lar ve bu mirror’ların DNS’e yansıtılması kritik önem taşır.

Linkerd multicluster için CoreDNS yapılandırması:

apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns
  namespace: kube-system
data:
  Corefile: |
    .:53 {
        errors
        health {
           lameduck 5s
        }
        ready
        kubernetes cluster.local in-addr.arpa ip6.arpa {
           pods insecure
           fallthrough in-addr.arpa ip6.arpa
           ttl 30
        }
        kubernetes east.cluster.local {
           pods insecure
           ttl 30
           namespaces production staging
        }
        prometheus :9153
        forward . /etc/resolv.conf {
           max_concurrent 1000
        }
        cache 30
        loop
        reload
        loadbalance
    }

Burada east.cluster.local zone’u, Linkerd’in mirror ettiği servislerin DNS üzerinden erişilebilmesi için tanımlanıyor. namespaces production staging direktifi ile sadece belirli namespace’lerdeki servisleri bu zone üzerinden yayınlıyoruz, güvenlik açısından önemli bir kısıtlama.

Hata Ayıklama: Gerçek Hayattan Senaryolar

DNS sorunları genellikle en belirsiz hata mesajlarıyla kendini gösteriyor. Şu araçları her zaman yanınızda bulundurun.

DNS sorgularını canlı olarak izlemek için:

# CoreDNS log plugin'ini geçici olarak aktif et
kubectl edit configmap coredns -n kube-system

# Corefile'a log ekle:
.:53 {
    log
    errors
    ...
}

# Pod'ları restart et
kubectl rollout restart deployment/coredns -n kube-system

# Logları takip et
kubectl logs -f -n kube-system -l k8s-app=kube-dns

Belirli bir servise DNS çözümlemesini test etmek için:

# Geçici debug pod oluştur
kubectl run dns-debug --image=busybox:1.35 --rm -it --restart=Never -- /bin/sh

# Pod içinden DNS sorgusu
nslookup payment-service.production.svc.cluster.local
dig +short payment-service.production.svc.cluster.local @10.96.0.10

# Timeout ve retry davranışını test et
dig payment-service.production.svc.cluster.local +time=2 +retry=3

Istio ile birlikte DNS sorunlarını araştırırken sidecar proxy’nin DNS önbelleklemesini de kontrol etmek gerekiyor:

# Envoy proxy admin API'yi kullanarak DNS cache'ini sorgula
kubectl exec -it payment-pod -n production -c istio-proxy -- 
  curl -s localhost:15000/clusters | grep payment-service | head -20

# Istio proxy konfigürasyonunu doğrula
istioctl proxy-config listeners payment-pod.production --port 53

CoreDNS Yüksek Erişilebilirlik Yapılandırması

Üretim ortamlarında CoreDNS’in tek nokta arızası (SPOF) oluşturmaması için dikkat edilmesi gereken birkaç kritik nokta var.

Pod Disruption Budget tanımı:

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: coredns-pdb
  namespace: kube-system
spec:
  minAvailable: 1
  selector:
    matchLabels:
      k8s-app: kube-dns

Anti-affinity kuralları ile CoreDNS pod’larının farklı node’lara dağılmasını sağlamak:

kubectl patch deployment coredns -n kube-system --type='json' -p='[
  {
    "op": "add",
    "path": "/spec/template/spec/affinity",
    "value": {
      "podAntiAffinity": {
        "preferredDuringSchedulingIgnoredDuringExecution": [
          {
            "weight": 100,
            "podAffinityTerm": {
              "labelSelector": {
                "matchExpressions": [
                  {
                    "key": "k8s-app",
                    "operator": "In",
                    "values": ["kube-dns"]
                  }
                ]
              },
              "topologyKey": "kubernetes.io/hostname"
            }
          }
        ]
      }
    }
  }
]'

Replica sayısını artırmak ise şöyle yapılıyor:

kubectl scale deployment coredns --replicas=3 -n kube-system

Ancak sadece replica artırmak yetmez. CoreDNS’e ayrılan kaynak limitlerini de üretim yüküne göre ayarlamak gerekiyor:

kubectl set resources deployment coredns 
  --requests=cpu=100m,memory=70Mi 
  --limits=cpu=500m,memory=170Mi 
  -n kube-system

DNS Policy ve Pod Yapılandırması

Servis mesh içindeki pod’ların DNS davranışını kontrol etmek için dnsPolicy ve dnsConfig alanlarını kullanıyoruz. Bu özellikle sidecarsız çalışan (ambient mesh modunda) bazı bileşenler için önem taşıyor.

apiVersion: v1
kind: Pod
metadata:
  name: payment-processor
  namespace: production
spec:
  dnsPolicy: "None"
  dnsConfig:
    nameservers:
    - 10.96.0.10
    searches:
    - production.svc.cluster.local
    - svc.cluster.local
    - cluster.local
    options:
    - name: ndots
      value: "2"
    - name: timeout
      value: "2"
    - name: attempts
      value: "3"
  containers:
  - name: payment-processor
    image: payment-processor:v2.1.0

Burada ndots: 2 değeri önemli. Varsayılan ndots: 5 değeriyle, payment-service gibi kısa bir ad için Kubernetes önce payment-service.production.svc.cluster.local, payment-service.svc.cluster.local, payment-service.cluster.local gibi tam nitelendirmeleri deneyecek, başarısız olursa payment-service‘i external DNS’de arayacak. Bu gereksiz DNS sorgularına yol açıyor. ndots: 2 ile bu arama zincirini kısaltmış oluyoruz.

Monitoring ve Alerting

CoreDNS’i izlemek için Prometheus ve Grafana entegrasyonu neredeyse zorunlu. CoreDNS zaten /metrics endpoint’i sunuyor, tek yapmanız gereken bunu scrape etmek.

ServiceMonitor tanımı (Prometheus Operator kullanıyorsanız):

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: coredns
  namespace: monitoring
  labels:
    app: coredns
spec:
  jobLabel: k8s-app
  selector:
    matchLabels:
      k8s-app: kube-dns
  namespaceSelector:
    matchNames:
    - kube-system
  endpoints:
  - port: metrics
    interval: 15s
    path: /metrics

İzlenmesi gereken kritik metrikler:

  • coredns_dns_requests_total: Toplam DNS istek sayısı (rate olarak izleyin)
  • coredns_dns_responses_total: Yanıt tiplerine göre dağılım (NXDOMAIN oranı yüksekse sorun var)
  • coredns_forward_request_duration_seconds: Upstream DNS yanıt süreleri
  • coredns_cache_hits_total: Cache hit oranı (düşükse TTL veya cache boyutunu gözden geçirin)
  • coredns_forward_healthcheck_failures_total: Upstream sağlık kontrolü başarısızlıkları

Yaygın Hatalar ve Çözümleri

Servis mesh ve CoreDNS entegrasyonunda tekrar tekrar karşılaşılan birkaç problem var.

NXDOMAIN Fırtınası: Yeni bir namespace oluşturup servis deploy ettiğinizde, henüz DNS kaydı oluşmadan gelen sorgular NXDOMAIN dönüyor ve bunlar cache’e yazılıyor. denial TTL‘ini düşük tutmak (5-10 saniye) bu sorunu minimize eder.

ndots Cehennemi: Özellikle Python veya Node.js tabanlı uygulamalar bazen FQDN (nokta ile biten tam nitelendirilmiş domain adı) yerine kısa isim kullanıyor. payment-service. yerine payment-service kullanıldığında ndots değerine göre gereksiz lookup’lar oluyor. Uygulamayı değiştirme şansınız yoksa pod’a özel dnsConfig ile ndots değerini düşürün.

Istio’nun DNS Intercept Etmesi: Istio 1.8’den itibaren dns_proxy_addr özelliği ile DNS sorgularını intercept ediyor. Bu bazı durumlarda CoreDNS ile çakışıyor. Özellikle non-Kubernetes DNS sorguları (external API’ler için) sorun çıkarabilir. meshConfig.defaultConfig.proxyMetadata altında ISTIO_META_DNS_CAPTURE değerini kontrol edin.

CoreDNS Loop Detection: loop plugin’i, DNS lookup döngülerini tespit edip CoreDNS’i durdurabilir. Özellikle on-premise Kubernetes kurulumlarında, cluster’ın kendi nameserver’ını upstream olarak kullanması bu problemi tetikliyor. /etc/resolv.conf yerine explicit IP adresleri kullanın.

Sonuç

CoreDNS ile servis mesh entegrasyonu, göründüğünden çok daha derin bir konu. Basit bir Corefile değişikliğiyle başlıyor ama multicluster, external servisler, performance tuning ve yüksek erişilebilirlik gereksinimleri devreye girince ciddi bir mimari düşünme süreci gerekiyor.

Benim önerim şu: önce mevcut ortamınızın DNS trafik desenini ölçün. CoreDNS metriklerini bir hafta izleyin, hangi sorgular çok tekrarlanıyor, NXDOMAIN oranı nedir, upstream yanıt süreleri nasıl görünüyor? Bunları bilmeden yapılacak her optimizasyon kör bir atış olur.

Servis mesh katmanı karmaşıklık getiriyor, bunu kabul etmek gerekiyor. Ama DNS katmanı düzgün kurulduğunda, bu karmaşıklığı yönetmek çok daha kolay hale geliyor. Ağ sorunlarının önemli bir kısmı aslında DNS sorunlarıdır ve bu katmanı sağlam tutmak, gece 3’te karanlık bir terminale bakarak hata ayıklamak zorunda kalma riskini ciddi ölçüde azaltır.

Bir yanıt yazın

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