CoreDNS ile Kubernetes Service Discovery Nasıl Yapılır?

Kubernetes cluster’ında bir pod başka bir servise ulaşmaya çalıştığında arka planda neler döndüğünü hiç merak ettiniz mi? “Sadece servis adını yazıyorum, çalışıyor” diyip geçiyoruz çoğu zaman. Ama bu büyünün arkasında CoreDNS var ve prodüksiyon ortamında bir şeyler ters gittiğinde “sadece çalışıyor” demek yetmiyor. Bu yazıda CoreDNS’i gerçekten anlamak, doğru yapılandırmak ve sorunları çözmek üzerine konuşacağız.

CoreDNS Nedir ve Neden Önemlidir

Kubernetes 1.13’ten itibaren CoreDNS, kube-dns’in yerini alarak cluster içi DNS çözümlemenin standart bileşeni haline geldi. Go ile yazılmış, plugin tabanlı bir DNS sunucusu. Ama asıl önemli olan şu: Kubernetes’teki her servis keşfi, her pod-to-pod iletişimi, her dış kaynak erişimi bu bileşenden geçiyor. CoreDNS çöktüğünde veya yanlış yapılandırıldığında tüm cluster iletişimi durma noktasına geliyor.

CoreDNS’in plugin mimarisi sayesinde davranışını son derece esnek biçimde şekillendirebiliyorsunuz. Corefile adı verilen yapılandırma dosyasıyla hem hangi plugin’lerin çalışacağını hem de her birinin nasıl davranacağını kontrol ediyorsunuz.

Kubernetes’te CoreDNS’in Çalışma Mekanizması

Bir pod içinde /etc/resolv.conf dosyasına baktığınızda şöyle bir şey görürsünüz:

nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

Bu nameserver adresi CoreDNS servisinin ClusterIP’sidir. search satırları ise kısa adlarla (örneğin sadece my-service) tam nitelikli domainlere (FQDN) ulaşmanızı sağlayan arama alanlarıdır.

ndots:5 ayarı çok kritik bir detay. Bir hostname içinde 5’ten az nokta varsa, DNS istemcisi önce arama listesindeki domainleri deniyor, başarısız olursa mutlak domain olarak sorguluyor. Bu ayar yanlış anlaşıldığında gereksiz DNS sorgularına ve latency artışlarına yol açıyor. Bunu ilerleyen bölümlerde daha ayrıntılı ele alacağız.

Servis keşfi için DNS kayıt formatları şu şekilde işliyor:

  • Normal servisler: ..svc.cluster.local
  • Headless servisler: ...svc.cluster.local
  • Pod’lar (doğrudan): ..pod.cluster.local

CoreDNS Kurulumu ve Mevcut Durumu Kontrol Etmek

Eğer kubeadm ile cluster kurduysanız CoreDNS büyük ihtimalle zaten çalışıyordur. Durumu kontrol edelim:

kubectl get pods -n kube-system -l k8s-app=kube-dns
kubectl get deployment coredns -n kube-system
kubectl get configmap coredns -n kube-system -o yaml

CoreDNS’in hangi versiyonunu çalıştırdığınızı ve mevcut Corefile yapılandırmanızı görmek için:

kubectl describe configmap coredns -n kube-system

Eğer CoreDNS yoksa veya sıfırdan kurmak istiyorsanız, kubectl ile manifest uygulayabilirsiniz. Ancak gerçek hayatta çoğunlukla mevcut yapılandırmayı düzenlemek veya sorun gidermek söz konusu oluyor.

Corefile Yapılandırması: Gerçekten Anlamak

CoreDNS’in kalbi Corefile’dır. Varsayılan Corefile şuna benzer:

.: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
}

Her plugin ne yapıyor, tek tek bakalım:

  • errors: Hataları stdout’a loglar
  • health: /health endpoint’i açar, liveness probe için kullanılır
  • ready: /ready endpoint’i açar, readiness probe için kullanılır
  • kubernetes: Kubernetes servis ve pod kayıtlarını çözümler
  • prometheus: Metrikleri :9153 portunda açar
  • forward: Cluster dışı sorgular için upstream DNS’e yönlendirir
  • cache: DNS yanıtlarını önbelleğe alır (TTL saniye cinsinden)
  • loop: DNS döngülerini tespit eder
  • reload: Corefile değişikliklerini otomatik algılar
  • loadbalance: Birden fazla A kaydı varsa round-robin uygular

Kubernetes Plugin’ini Derinlemesine İncelemek

pods insecure ayarı pod IP’lerini doğrulama yapmadan DNS’e kaydeder. pods verified daha güvenli ama daha yavaş. Prodüksiyonda güvenlik politikalarınıza göre bu ayarı değerlendirmenizi öneririm.

fallthrough direktifi çok önemli: Kubernetes plugin’i bir kaydı bulamazsa sorguyu bir sonraki plugin’e iletir. in-addr.arpa ve ip6.arpa için bu gerekli çünkü reverse DNS sorguları bazen cluster dışına çıkması gerekiyor.

Özel DNS Yapılandırma Senaryoları

Şirket İçi Domain’leri CoreDNS’e Eklemek

Sahadan bir örnek: Şirketinizin iç DNS sunucusunda internal.company.com domain’i var ve cluster içindeki pod’ların bu adreslere ulaşması gerekiyor. Bunun için stub zone yapılandırması kullanıyorsunuz:

kubectl edit configmap coredns -n kube-system

Corefile’a şu bloğu ekleyebilirsiniz:

internal.company.com:53 {
    errors
    cache 30
    forward . 192.168.1.53 192.168.1.54
}

Bu yapılandırmayla internal.company.com altındaki tüm sorgular şirket DNS sunucularınıza iletilir. ConfigMap’i düzenledikten sonra reload plugin’i değişikliği otomatik algılar, CoreDNS’i restart etmenize gerek kalmaz. Ama emin olmak için pod’ları gözlemleyin:

kubectl logs -f -l k8s-app=kube-dns -n kube-system

Split-Horizon DNS Yapılandırması

Bazı şirketlerin hem iç hem dış DNS’te aynı domain adları var ama farklı IP’lere işaret ediyor. Bu durumda şöyle bir Corefile yapısı kuruyorsunuz:

example.com:53 {
    errors
    cache 30
    forward . 10.0.0.53 {
        prefer_udp
    }
}

.:53 {
    errors
    health
    ready
    kubernetes cluster.local in-addr.arpa ip6.arpa {
        pods insecure
        fallthrough in-addr.arpa ip6.arpa
        ttl 30
    }
    prometheus :9153
    forward . 8.8.8.8 8.8.4.4
    cache 30
    loop
    reload
    loadbalance
}

DNS Performans Sorunlarını Çözmek

ndots Sorunu ve Çözümü

Bahsettiğim ndots:5 ayarı prodüksiyonda ciddi latency artışına yol açabilir. Diyelim ki pod’unuz api.external.com‘a istek atıyor. DNS istemcisi sırayla şunları deniyor:

  1. api.external.com.default.svc.cluster.local
  2. api.external.com.svc.cluster.local
  3. api.external.com.cluster.local
  4. api.external.com. (mutlak)

İlk üç sorgu başarısız olacak ve 3 gereksiz DNS roundtrip yapacaksınız. Bunu çözmek için pod spec’inizde DNS yapılandırmasını override edebilirsiniz:

apiVersion: v1
kind: Pod
metadata:
  name: my-app
spec:
  dnsConfig:
    options:
      - name: ndots
        value: "2"
  containers:
    - name: my-app
      image: my-app:latest

ndots:2 ile dış domain’lere yapılan sorgular çok daha hızlı çözülecek. Ama dikkat: Eğer uygulamanız cluster içi servisleri kısa adıyla çağırıyorsa (örneğin sadece my-service), bu ayarla sorun yaşayabilirsiniz. Cluster içi çağrılar için her zaman tam FQDN kullanmak en sağlıklı yaklaşım.

CoreDNS Cache Ayarlarını Optimize Etmek

Yoğun DNS trafiği olan ortamlarda cache süresini artırmak sorgu sayısını drastik biçimde azaltır:

.:53 {
    errors
    health
    ready
    kubernetes cluster.local in-addr.arpa ip6.arpa {
        pods insecure
        fallthrough in-addr.arpa ip6.arpa
        ttl 30
    }
    cache {
        success 9984 300
        denial 9984 5
        prefetch 10
    }
    forward . /etc/resolv.conf
    loop
    reload
    loadbalance
}
  • success 9984 300: Başarılı yanıtları 300 saniye önbelleğe al, maksimum 9984 kayıt
  • denial 9984 5: NXDOMAIN yanıtlarını 5 saniye önbelleğe al
  • prefetch 10: 10 kez sorgulanmış kayıtları TTL dolmadan önce yenile

denial cache’i düşük tutmak önemli. Bir servis henüz hazır değilken gelen NXDOMAIN yanıtı uzun süre cache’de kalırsa, servis hazır olduğunda bile pod’lar onu bulamaz.

CoreDNS’i Scale Etmek

Büyük cluster’larda tek CoreDNS deployment’ı yeterli gelmiyor. Önce mevcut replica sayısını kontrol edelim:

kubectl get deployment coredns -n kube-system

Replica sayısını artırmak için:

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

Ama bunu otomatik hale getirmek daha iyi. HorizontalPodAutoscaler kullanabilirsiniz, ama DNS gibi kritik bir bileşen için cluster-proportional-autoscaler daha yaygın tercih:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: coredns-autoscaler
  namespace: kube-system
spec:
  replicas: 1
  selector:
    matchLabels:
      k8s-app: coredns-autoscaler
  template:
    metadata:
      labels:
        k8s-app: coredns-autoscaler
    spec:
      containers:
        - name: autoscaler
          image: registry.k8s.io/cpa/cluster-proportional-autoscaler:1.8.4
          command:
            - /cluster-proportional-autoscaler
            - --namespace=kube-system
            - --configmap=coredns-autoscaler
            - --target=deployment/coredns
            - --default-params={"linear":{"coresPerReplica":256,"nodesPerReplica":16,"min":2}}
            - --logtostderr=true

Bu yapılandırmayla CoreDNS replika sayısı node ve CPU çekirdeği sayısına göre otomatik ayarlanır.

DNS Sorun Giderme: Sahadan Notlar

Prodüksiyonda sık karşılaştığım senaryolar ve çözümleri:

Pod DNS’i Çözemiyor

İlk kontrol: Pod içinden DNS çözümlemesini test et.

kubectl run dns-test --image=busybox:1.28 --rm -it --restart=Never -- nslookup kubernetes.default

Eğer bu çalışmıyorsa CoreDNS pod’larının durumuna bakın:

kubectl get pods -n kube-system -l k8s-app=kube-dns -o wide
kubectl logs <coredns-pod-name> -n kube-system

Upstream DNS Sorunlarını Debug Etmek

CoreDNS’in upstream DNS sunucularına ulaşıp ulaşamadığını test etmek için:

kubectl run dns-debug --image=infoblox/dnstools --rm -it --restart=Never -- /bin/bash

Pod içinde:

dig @10.96.0.10 google.com
dig @10.96.0.10 kubernetes.default.svc.cluster.local

Cluster DNS’i çalışıyor ama dış adresler çözümlenemiyorsa sorun forward plugin’inde veya node’ların upstream DNS’e erişiminde.

CoreDNS Log Seviyesini Artırmak

Geçici debug için log plugin’ini Corefile’a ekleyebilirsiniz. Ama dikkat: Prodüksiyonda bu ciddi log hacmi üretiyor ve performansı etkiliyor. Kısa süreli tutun:

kubectl edit configmap coredns -n kube-system

errors satırının altına log ekleyin:

.:53 {
    errors
    log
    health
    ...
}

Sonra CoreDNS log’larını izleyin:

kubectl logs -f -l k8s-app=kube-dns -n kube-system --max-log-requests 10

Sorunu tespit ettikten sonra log direktifini kaldırmayı unutmayın.

Metrics ile CoreDNS’i İzlemek

Prometheus entegrasyonu varsa CoreDNS metrikleri :9153/metrics endpoint’inden geliyor. Kritik metrikler:

  • coredns_dns_requests_total: Toplam istek sayısı
  • coredns_dns_responses_total: Yanıt kodlarına göre toplam yanıt
  • coredns_forward_requests_total: Upstream’e iletilen istekler
  • coredns_cache_hits_total / coredns_cache_misses_total: Cache performansı

Grafana’da izlemek için ServiceMonitor ekleyebilirsiniz:

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

Cache hit oranı %80’in altına düşüyorsa cache boyutunu veya TTL süresini artırmayı düşünün. coredns_dns_responses_total metriğinde SERVFAIL artışı varsa upstream DNS sorununu araştırın.

NodeLocal DNSCache: Bir Sonraki Seviye

Büyük ve yoğun cluster’lar için NodeLocal DNSCache çok etkili bir çözüm. Her node üzerinde bir DNS cache daemon çalıştırarak pod’ların CoreDNS pod’larına değil, kendi node’larındaki local cache’e gitmesini sağlıyor. Bu hem latency’yi düşürüyor hem de CoreDNS üzerindeki yükü azaltıyor.

NodeLocal DNSCache’i etkinleştirmek için DaemonSet deploy etmeniz gerekiyor. Cluster’ınızın kubeadm ile kurulduğunu varsayarak:

wget https://raw.githubusercontent.com/kubernetes/kubernetes/master/cluster/addons/dns/nodelocaldns/nodelocaldns.yaml

Dosyada __PILLAR__DNS__SERVER__, __PILLAR__LOCAL__DNS__ ve __PILLAR__DNS__DOMAIN__ değerlerini cluster’ınıza göre değiştirin. CoreDNS ClusterIP’yi almak için:

kubectl get service kube-dns -n kube-system -o jsonpath='{.spec.clusterIP}'

Bu değerleri YAML dosyasında güncelledikten sonra:

kubectl apply -f nodelocaldns.yaml

NodeLocal DNSCache aktif olduğunda pod’ların resolv.conf‘undaki nameserver 169.254.20.10 (link-local adres) olacak şekilde güncellenir.

Güvenlik Açısından CoreDNS

DNS, güvenlik açısından sıklıkla göz ardı edilen bir bileşen. Birkaç kritik nokta:

Pod’ların CoreDNS’e erişimini NetworkPolicy ile kontrol edebilirsiniz. Aşağıdaki policy yalnızca belirli namespace’lerin DNS sorgularına izin verir:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns-access
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Egress
  egress:
    - ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53

Ayrıca CoreDNS’in kendisini güncel tutmak kritik. DNS amplification saldırıları ve cache poisoning saldırılarına karşı güncel versiyon kullanın. loop plugin’i DNS döngülerini tespit ederek CoreDNS’in kendi kendine sorgu gönderip askıda kalmasını önlüyor, varsayılan Corefile’da bu plugin mutlaka olsun.

Sonuç

CoreDNS, Kubernetes’in sessiz kahramanlarından biri. Düzgün çalıştığında kimse fark etmiyor, bozulduğunda ise tüm cluster ayağa kalkıyor. Bu yazıda ele aldığımız konuları özetlemek gerekirse: Corefile yapılandırmasını gerçekten anlamak, stub zone ve split-horizon senaryolarını bilmek, ndots sorununu çözmek, cache’i doğru optimize etmek ve metriklerle izlemek prodüksiyonda karşınıza çıkacak sorunların büyük çoğunluğunu bertaraf ediyor.

NodeLocal DNSCache’i henüz kullanmıyorsanız ve yoğun DNS trafiğiniz varsa, bunu denemek için iyi bir dönem. CoreDNS metriklerini Prometheus ve Grafana’ya bağlamak da kör noktaları ortadan kaldırıyor, sorunları reaktif değil proaktif yakalayabiliyorsunuz.

Son bir not: Corefile değişikliklerini her zaman önce staging ortamında test edin. reload plugin’i harika ama yanlış bir yapılandırma tüm DNS çözümlemesini durdurabilir. Özellikle stub zone eklerken veya forward direktifini değiştirirken dikkatli olun.

Bir yanıt yazın

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