CoreDNS ile kube-dns’den Geçiş Rehberi

Kubernetes ortamınızda bir gün DNS çözümleme sorunuyla karşılaştınızda, servislerinizin birbirini bulamadığını görürsünüz ve o an her şeyi bırakıp DNS katmanına bakarsınız. Yıllar içinde gördüğüm kadarıyla, kube-dns’den CoreDNS’e geçiş tam da böyle bir kriz anında zorunluluk haline geliyor. Ama bu geçişi planlı yaparsanız, hem performans kazanırsınız hem de gece yarısı uyanmanıza neden olan o belirsiz DNS hatalarından kurtulursunuz.

Bu yazıda, kube-dns’i çalıştıran mevcut bir cluster’dan CoreDNS’e geçişi adım adım anlatacağım. Sadece “şunu çalıştır” demeyeceğim; neyi neden yaptığımızı da açıklayacağım.

kube-dns ve CoreDNS: Temel Farklar

kube-dns, Kubernetes’in eski DNS çözümüdür. Üç ayrı container’dan oluşur: kubedns, dnsmasq ve sidecar. Bu mimari hem kaynak tüketimi hem de hata ayıklama açısından zahmetlidir. Bir şeyler ters gittiğinde hangi container’a bakacağınızı bulmak başlı başına bir iş olur.

CoreDNS ise Go ile yazılmış, plugin tabanlı tek bir binary’dir. Kubernetes 1.11’den itibaren alpha, 1.13’ten itibaren de default DNS çözümü olarak geldi. Eğer hâlâ kube-dns kullanıyorsanız, muhtemelen ya çok eski bir cluster’ı devraldınız ya da bir şeyler yanlış gitti ve geri döndünüz.

CoreDNS’in öne çıkan avantajları:

  • Plugin mimarisi: DNS davranışını Corefile üzerinden tamamen özelleştirebilirsiniz
  • Tek process: Hata ayıklamak çok daha kolay, log okumak sade
  • Forward ve cache yönetimi: Upstream DNS yönetimi çok daha esnek
  • Health endpoint: /health ve /ready endpoint’leri ile sağlık kontrolü yerleşik geliyor
  • Prometheus metrikleri: Kutusundan çıktığı gibi Prometheus ile entegre
  • Daha az kaynak tüketimi: Aynı yük altında kube-dns’e kıyasla belirgin şekilde daha az memory kullanıyor

Geçiş Öncesi Hazırlık

Geçiş yapmadan önce mevcut durumu belgelemeniz şart. Sonradan “önceki hâl nasıldı?” diye bakarken elinizde hiçbir şey olmadığını fark edersiniz.

# Mevcut DNS pod'larını kontrol et
kubectl get pods -n kube-system -l k8s-app=kube-dns

# Mevcut kube-dns ConfigMap'ini yedekle
kubectl get configmap kube-dns -n kube-system -o yaml > kube-dns-backup.yaml

# Cluster DNS IP adresini öğren
kubectl get svc kube-dns -n kube-system

# Mevcut DNS çözümlemesini test et
kubectl run test-dns --image=busybox:1.28 --rm -it --restart=Never -- nslookup kubernetes.default

Bu adımı atlamayın. Özellikle kube-dns ConfigMap’indeki stub zone tanımlarını veya özel upstream ayarlarını CoreDNS Corefile’ına taşımanız gerekiyor. Yedek almadan geçiş yapanların yarısı bu nedenle geri dönmek zorunda kalıyor.

Cluster Versiyonu ve Uyumluluk

# Kubernetes versiyonunu kontrol et
kubectl version --short

# Node'lardaki kubelet DNS ayarlarını kontrol et
kubectl get nodes -o jsonpath='{.items[*].spec.dnsConfig}' | python3 -m json.tool

Kubernetes 1.10 ve üzeri için CoreDNS sorunsuz çalışır. Daha eski bir sürümdeyseniz önce cluster upgrade’ini düşünün; zaten o kadar eski bir cluster’da başka sorunlarınız da vardır muhtemelen.

CoreDNS Kurulumu

Eğer kubeadm kullanıyorsanız ve cluster’ı CoreDNS ile yeniden başlatma şansınız yoksa, manuel kurulum yapacaksınız. Bu senaryo özellikle production’da devralınan cluster’lar için geçerli.

# CoreDNS namespace ve ServiceAccount oluştur
kubectl apply -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
  name: coredns
  namespace: kube-system
EOF

ClusterRole ve ClusterRoleBinding tanımları:

kubectl apply -f - <<EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
  name: system:coredns
rules:
  - apiGroups:
    - ""
    resources:
    - endpoints
    - services
    - pods
    - namespaces
    verbs:
    - list
    - watch
  - apiGroups:
    - discovery.k8s.io
    resources:
    - endpointslices
    verbs:
    - list
    - watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
  name: system:coredns
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:coredns
subjects:
- kind: ServiceAccount
  name: coredns
  namespace: kube-system
EOF

Corefile Yapılandırması

CoreDNS’in kalbi Corefile’dır. kube-dns’den geçişte en kritik adım budur. Temel bir Corefile şöyle görünür:

kubectl apply -f - <<EOF
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
    }
EOF

Bu yapılandırmayı anlamak önemli:

  • errors: Hataları stdout’a yazar, log izleme için şart
  • health: http://localhost:8080/health adresinde sağlık kontrolü
  • ready: http://localhost:8181/ready adresinde hazırlık kontrolü
  • kubernetes: Cluster içi DNS çözümlemesi; pods insecure seçeneği pod IP’lerinden hostname çözümlemesine izin verir
  • prometheus: :9153 portunda Prometheus metrikleri
  • forward: Cluster dışı sorgular için upstream DNS; /etc/resolv.conf node’un kendi DNS ayarlarını kullanır
  • cache: 30 saniyelik TTL ile önbellek
  • loop: Döngüsel DNS sorguyu tespit eder ve durur
  • reload: Corefile değişikliklerini otomatik uygular
  • loadbalance: Round-robin yük dağılımı

Özel Upstream DNS Tanımı

Eğer kube-dns’de stub zone kullanıyordunuz, örneğin şirket içi bir DNS sunucunuza yönlendirme yapıyordunuz, bunu CoreDNS’e taşımanız gerekiyor:

kubectl apply -f - <<EOF
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 . 8.8.8.8 8.8.4.4 {
           max_concurrent 1000
        }
        cache 30
        loop
        reload
        loadbalance
    }

    # Şirket içi domain için özel yönlendirme
    sirket.internal:53 {
        errors
        cache 30
        forward . 10.0.0.53 10.0.0.54
    }

    # Başka bir iç domain
    dev.local:53 {
        errors
        cache 30
        forward . 192.168.1.10
    }
EOF

Bu yapıyı kube-dns’deki stub zone karşılığıyla kıyaslayın. Eğer yedeklediğiniz kube-dns-backup.yaml dosyasında şuna benzer bir bölüm varsa:

stubDomains:
  sirket.internal:
  - 10.0.0.53
  - 10.0.0.54

Bu tanımı yukarıdaki CoreDNS bloğuna dönüştürmeniz yeterli.

CoreDNS Deployment’ı

Şimdi CoreDNS pod’larını oluşturalım:

kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: coredns
  namespace: kube-system
  labels:
    k8s-app: kube-dns
    kubernetes.io/name: CoreDNS
spec:
  replicas: 2
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
  selector:
    matchLabels:
      k8s-app: kube-dns
  template:
    metadata:
      labels:
        k8s-app: kube-dns
    spec:
      priorityClassName: system-cluster-critical
      serviceAccountName: coredns
      tolerations:
        - key: "CriticalAddonsOnly"
          operator: "Exists"
      nodeSelector:
        kubernetes.io/os: linux
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                  - key: k8s-app
                    operator: In
                    values: ["kube-dns"]
              topologyKey: kubernetes.io/hostname
      containers:
      - name: coredns
        image: coredns/coredns:1.11.1
        imagePullPolicy: IfNotPresent
        resources:
          limits:
            memory: 170Mi
          requests:
            cpu: 100m
            memory: 70Mi
        args: [ "-conf", "/etc/coredns/Corefile" ]
        volumeMounts:
        - name: config-volume
          mountPath: /etc/coredns
          readOnly: true
        ports:
        - containerPort: 53
          name: dns
          protocol: UDP
        - containerPort: 53
          name: dns-tcp
          protocol: TCP
        - containerPort: 9153
          name: metrics
          protocol: TCP
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            add:
            - NET_BIND_SERVICE
            drop:
            - all
          readOnlyRootFilesystem: true
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
            scheme: HTTP
          initialDelaySeconds: 60
          timeoutSeconds: 5
          successThreshold: 1
          failureThreshold: 5
        readinessProbe:
          httpGet:
            path: /ready
            port: 8181
            scheme: HTTP
      dnsPolicy: Default
      volumes:
        - name: config-volume
          configMap:
            name: coredns
            items:
            - key: Corefile
              path: Corefile
EOF

Burada dikkat etmeniz gereken birkaç nokta var. podAntiAffinity tanımı, CoreDNS pod’larının farklı node’lara dağılmasını sağlar. DNS single point of failure olmamalı; aynı node çöktüğünde hem DNS hem de uygulamalarınız birlikte gitmesin. priorityClassName: system-cluster-critical ise kaynak sıkışmasında CoreDNS’in tahliye edilmemesini garanti eder.

Geçiş Stratejisi: Kesintisiz Geçiş

Burada dikkatli olmak gerekiyor. Yanlış sırayla yaparsanız cluster DNS’i kesilir ve tüm servisler birbirini bulamaz hale gelir.

Adım 1: CoreDNS’i ölçeklendirip kube-dns’i sıfıra çekme stratejisi yerine, her ikisini birlikte çalıştırarak test edin.

# CoreDNS pod'larının ayağa kalktığını doğrula
kubectl get pods -n kube-system -l k8s-app=kube-dns

# CoreDNS pod loglarını izle
kubectl logs -n kube-system -l k8s-app=kube-dns --follow

Adım 2: DNS servisinin selector’ını kontrol edin. CoreDNS Deployment’ını k8s-app: kube-dns label’ı ile oluşturduysanız, mevcut kube-dns Service otomatik olarak CoreDNS pod’larını da kapsar.

# Service'in hangi pod'ları kapsadığını kontrol et
kubectl get endpoints kube-dns -n kube-system

Adım 3: Test pod’u ile DNS çözümlemesini doğrulayın.

kubectl run dns-test --image=busybox:1.28 --rm -it --restart=Never -- sh -c "
  echo '=== Kubernetes Service DNS ===' &&
  nslookup kubernetes.default &&
  echo '=== Namespace bazlı DNS ===' &&
  nslookup kube-dns.kube-system.svc.cluster.local &&
  echo '=== Dış DNS ===' &&
  nslookup google.com
"

Adım 4: Her şey yolundaysa kube-dns Deployment’ını sıfıra çekin:

# Önce mevcut kube-dns deployment adını bul
kubectl get deployments -n kube-system

# kube-dns'i scale down yap
kubectl scale deployment kube-dns -n kube-system --replicas=0

# Birkaç dakika izle
kubectl get pods -n kube-system -w

Adım 5: Uygulamalarınızdan DNS sorgusu gelip gelmediğini test edin. En az 10-15 dakika bekleyin; bazı uygulamalar DNS cache tuttuğu için hataları gecikmeli görürsünüz.

Sorun Giderme

Geçiş sonrası en sık karşılaşılan sorunlar ve çözümleri:

DNS Loop Sorunları

CoreDNS pod’ları loop plugin’i nedeniyle CrashLoopBackOff durumuna girebilir. Bu genellikle node’un /etc/resolv.conf dosyasında 127.0.0.1 veya 127.0.0.53 (systemd-resolved) gibi loopback adresler bulunduğunda olur.

# Node'un resolv.conf'unu kontrol et
cat /etc/resolv.conf

# Eğer 127.0.0.53 varsa (Ubuntu systemd-resolved durumu)
# Corefile'daki forward satırını güncelle
kubectl edit configmap coredns -n kube-system

Corefile’da şu değişikliği yapın:

forward . 8.8.8.8 8.8.4.4

Ya da loop plugin’ini geçici olarak kaldırıp gerçek upstream DNS ekleyin. Ama loop plugin’ini kalıcı olarak kaldırmak önerilmez; tehlikeli bir DNS döngüsünü tespit edemezsiniz.

CoreDNS Pod’larının Ready Olmaması

# Pod event'lerini kontrol et
kubectl describe pod -n kube-system -l k8s-app=kube-dns

# Corefile syntax hatası var mı?
kubectl logs -n kube-system -l k8s-app=kube-dns | grep -i error

Yüksek Gecikme Sorunları

ndots ayarı DNS sorgularında ciddi gecikmeye yol açabilir. Varsayılan ndots:5 değeriyle her kısa hostname için 5 farklı arama denemesi yapılır.

# Mevcut ndots değerini test pod'unda kontrol et
kubectl run ndots-test --image=busybox:1.28 --rm -it --restart=Never -- cat /etc/resolv.conf

Eğer uygulamalarınız çok fazla DNS sorgusu yapıyorsa ve gecikme yaşıyorsanız, pod’lara özel dnsConfig ekleyin:

kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: app-with-custom-dns
spec:
  dnsConfig:
    options:
      - name: ndots
        value: "2"
      - name: single-request-reopen
  containers:
  - name: app
    image: nginx:latest
EOF

Monitoring ve Alerting

CoreDNS’i deploy ettikten sonra monitoring kurmadan bırakmayın. Prometheus ile birlikte şu metrikleri takip edin:

  • coredns_dns_requests_total: Toplam istek sayısı
  • coredns_dns_responses_total: Cevap kodlarına göre dağılım (NOERROR, NXDOMAIN, SERVFAIL)
  • coredns_cache_hits_total ve coredns_cache_misses_total: Cache etkinliği
  • coredns_dns_request_duration_seconds: Sorgu gecikme histogramı

Prometheus ServiceMonitor varsa:

kubectl apply -f - <<EOF
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
EOF

Basit bir alert kuralı:

kubectl apply -f - <<EOF
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: coredns-alerts
  namespace: kube-system
spec:
  groups:
  - name: coredns
    rules:
    - alert: CoreDNSHighErrorRate
      expr: |
        rate(coredns_dns_responses_total{rcode="SERVFAIL"}[5m]) > 0.1
      for: 5m
      labels:
        severity: warning
      annotations:
        summary: "CoreDNS yüksek hata oranı"
        description: "Son 5 dakikada SERVFAIL oranı 0.1'in üzerinde"
    - alert: CoreDNSDown
      expr: absent(up{job="coredns"} == 1)
      for: 1m
      labels:
        severity: critical
      annotations:
        summary: "CoreDNS çalışmıyor"
EOF

Geri Dönüş Planı

Her geçişte bir geri dönüş planı olmalı. CoreDNS’e geçtikten sonra bir şeyler ters giderse:

# kube-dns'i tekrar scale up yap
kubectl scale deployment kube-dns -n kube-system --replicas=2

# CoreDNS'i sıfıra çek
kubectl scale deployment coredns -n kube-system --replicas=0

# Eski kube-dns ConfigMap'ini geri yükle
kubectl apply -f kube-dns-backup.yaml

Bu komutları çalıştırmadan önce test edin. Geri dönüş planı test edilmemiş bir plan değildir.

Sonuç

kube-dns’den CoreDNS’e geçiş, doğru yapıldığında cluster DNS altyapınızı hem daha güvenilir hem de çok daha görünür hale getirir. Yıllar içinde onlarca cluster’da bu geçişi yaptım; en çok sorun çıkaran kısım teknik kurulum değil, eksik belgelenmiş stub zone tanımları ve test edilmemiş geri dönüş prosedürleri oldu.

Geçiş öncesinde mevcut yapıyı belgeleyin, test ortamında bir tur geçin, production’da kesintisiz geçiş stratejisini uygulayın ve monitoring’i günden birden devreye alın. CoreDNS’in Prometheus metrikleri sayesinde DNS davranışını gerçek zamanlı görmek, sorunları çok daha hızlı çözmenizi sağlar. Özellikle yüksek trafikli cluster’larda cache hit oranını takip etmek, hem upstream DNS yükünü azaltır hem de uygulamalarınızın gecikme profilini iyileştirir.

En önemlisi: DNS’e dokunmak her zaman biraz risk taşır. Sabah saatlerinde, düşük trafikte, hazırlıklı bir ekiple yapın.

Bir yanıt yazın

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