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:
/healthve/readyendpoint’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/healthadresinde sağlık kontrolü - ready:
http://localhost:8181/readyadresinde hazırlık kontrolü - kubernetes: Cluster içi DNS çözümlemesi;
pods insecureseçeneği pod IP’lerinden hostname çözümlemesine izin verir - prometheus:
:9153portunda Prometheus metrikleri - forward: Cluster dışı sorgular için upstream DNS;
/etc/resolv.confnode’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_totalvecoredns_cache_misses_total: Cache etkinliğicoredns_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.
