Kubernetes’te CoreDNS Kurulumu ve Yapılandırması
Kubernetes cluster’ı ilk kurduğumda DNS konusu beni en çok zorlayan şeylerden biriydi. Pod’lar birbirini bulamıyor, servisler resolve edilemiyor, her şey bir muamma gibi görünüyordu. Sonra CoreDNS’i gerçekten anlamaya başlayınca hem sorunları çözmek hem de yapılandırmayı özelleştirmek çok daha kolay hale geldi. Bu yazıda Kubernetes ortamında CoreDNS’i sıfırdan kurmaktan başlayarak production’da karşılaşacağınız gerçek senaryolara kadar kapsamlı şekilde ele alacağım.
CoreDNS Nedir ve Neden Kubernetes’in Standart DNS Çözümü?
CoreDNS, Go ile yazılmış, plugin tabanlı bir DNS sunucusudur. Kubernetes 1.13 sürümünden itibaren kube-dns’in yerini alarak cluster’ların varsayılan DNS çözümü haline geldi. Plugin mimarisi sayesinde son derece esnek bir yapıya sahip; istediğiniz özelliği açıp kapatabiliyorsunuz.
Kubernetes’teki rolünü kısaca özetlemek gerekirse: her Pod, DNS sorguları için cluster’ın DNS servis IP’sine yönlendirilir. Bu IP, CoreDNS servisine aittir ve CoreDNS bu sorguları karşılayarak Pod’ların birbirini isim üzerinden bulmasını sağlar. my-service.my-namespace.svc.cluster.local gibi bir FQDN çözümlemek istediğinizde işi yapan CoreDNS’tir.
Kurulum Öncesi Gereksinimler
Mevcut bir cluster’da CoreDNS’in kurulu olup olmadığını kontrol etmekle başlayın:
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
Eğer cluster’ınızda eski kube-dns çalışıyorsa ve CoreDNS’e geçiş yapacaksanız, önce mevcut yapılandırmayı yedekleyin:
kubectl get configmap kube-dns -n kube-system -o yaml > kube-dns-backup.yaml
kubectl get deployment kube-dns -n kube-system -o yaml > kube-dns-deployment-backup.yaml
Kubeadm ile kurulmuş bir cluster’da CoreDNS zaten gelir ama bazen versiyon eski kalıyor. Mevcut versiyonu görmek için:
kubectl describe deployment coredns -n kube-system | grep Image
Temel CoreDNS Kurulumu
Kubeadm kullanıyorsanız CoreDNS otomatik olarak kurulur. Ancak bare-metal ya da özel bir ortamda manuel kurulum yapmanız gerekebilir. Aşağıdaki manifest dosyaları ile CoreDNS’i cluster’ınıza ekleyebilirsiniz.
Önce ServiceAccount ve ClusterRole tanımları:
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: coredns
namespace: kube-system
---
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:
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. Tüm davranışı bu dosya belirler. Kubernetes’te bu dosya bir ConfigMap olarak saklanır:
cat <<EOF | kubectl apply -f -
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 Corefile’daki her satırın ne anlama geldiğini bilmek önemli. Gerçek ortamlarda hata ayıklarken bu detayları bilmek hayat kurtarıyor:
- errors: Hataları stdout’a yazar, log izleme için kritik
- health:
/healthendpoint’i açar, liveness probe için kullanılır - lameduck 5s: Graceful shutdown sırasında 5 saniye bekler, pod yeniden başlarken DNS kesintisini önler
- ready:
/readyendpoint’i açar, readiness probe için - kubernetes: Kubernetes servis ve pod isimlerini çözer
- pods insecure: Pod IP’lerini reverse DNS için kayıt etmeden de çözebilir
- fallthrough: Eşleşme olmayan sorguları bir sonraki plugin’e iletir
- ttl 30: DNS kayıtlarının geçerlilik süresi
- prometheus: Metrik endpoint’i açar
- forward: Cluster dışı sorgular için upstream DNS
- max_concurrent 1000: Maksimum eşzamanlı sorgu sayısı
- cache 30: 30 saniyelik cache
- loop: DNS döngülerini tespit eder
- reload: Corefile değişikliklerini otomatik uygular
- loadbalance: DNS yanıtlarında round-robin uygular
CoreDNS Deployment ve Servis Tanımı
cat <<EOF | kubectl apply -f -
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: registry.k8s.io/coredns/coredns:v1.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
livenessProbe:
httpGet:
path: /health
port: 8080
scheme: HTTP
initialDelaySeconds: 60
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 5
readinessProbe:
httpGet:
path: /ready
port: 8181
scheme: HTTP
initialDelaySeconds: 30
timeoutSeconds: 5
securityContext:
allowPrivilegeEscalation: false
capabilities:
add:
- NET_BIND_SERVICE
drop:
- all
readOnlyRootFilesystem: true
volumes:
- name: config-volume
configMap:
name: coredns
items:
- key: Corefile
path: Corefile
---
apiVersion: v1
kind: Service
metadata:
name: kube-dns
namespace: kube-system
labels:
k8s-app: kube-dns
kubernetes.io/cluster-service: "true"
kubernetes.io/name: "CoreDNS"
annotations:
prometheus.io/port: "9153"
prometheus.io/scrape: "true"
spec:
selector:
k8s-app: kube-dns
clusterIP: 10.96.0.10
ports:
- name: dns
port: 53
protocol: UDP
- name: dns-tcp
port: 53
protocol: TCP
- name: metrics
port: 9153
protocol: TCP
EOF
Burada dikkat etmenizi istediğim birkaç nokta var. clusterIP: 10.96.0.10 değeri cluster’ınızın servis CIDR’ına göre değişmeli. Kubeadm kurulumlarında bu genellikle 10.96.0.10 olur. podAntiAffinity kuralı ise iki CoreDNS pod’unun aynı node’a düşmesini önlüyor; tek node’un gitmesiyle DNS’in çökmemesi için kritik.
Gerçek Dünya Senaryosu: Özel DNS Zone Ekleme
Diyelim ki şirket içi bir DNS sunucunuz var ve bazı domain’leri ona yönlendirmek istiyorsunuz. Örneğin internal.sirket.com adreslerini 192.168.1.53 adresindeki iç DNS’e sormak istiyorsunuz:
kubectl edit configmap coredns -n kube-system
Corefile’a şunu ekleyin:
internal.sirket.com:53 {
errors
cache 30
forward . 192.168.1.53 192.168.1.54
}
Tam Corefile şöyle görünecek:
kubectl patch configmap coredns -n kube-system --type merge -p '
{
"data": {
"Corefile": ".:53 {n errorsn health {n lameduck 5sn }n readyn kubernetes cluster.local in-addr.arpa ip6.arpa {n pods insecuren fallthrough in-addr.arpa ip6.arpan ttl 30n }n prometheus :9153n forward . /etc/resolv.conf {n max_concurrent 1000n }n cache 30n loopn reloadn loadbalancen}ninternal.sirket.com:53 {n errorsn cache 30n forward . 192.168.1.53 192.168.1.54n}n"
}
}'
reload plugin’i aktifse CoreDNS değişikliği otomatik algılar, pod restart’a gerek yoktur. Yine de emin olmak için kontrol edin:
kubectl rollout restart deployment/coredns -n kube-system
kubectl rollout status deployment/coredns -n kube-system
Gerçek Dünya Senaryosu: Stub Zone ile Split-Horizon DNS
Hybrid cloud ortamlarında çok karşılaşılan bir durum: on-premise sistemler ile cloud’daki Kubernetes’in aynı domain isimlerini farklı IP’lere çözmesi gerekiyor. Bu durumda stub zone yapılandırması işe yarıyor:
cat <<EOF | kubectl apply -f -
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
policy sequential
}
cache 30
loop
reload
loadbalance
}
prod.sirket.com:53 {
errors
cache 30
forward . 10.0.0.53 {
force_tcp
}
}
staging.sirket.com:53 {
errors
cache 30
forward . 10.0.1.53
}
EOF
force_tcp opsiyonu özellikle büyük DNS yanıtları için önemli. UDP ile 512 byte’ın üzerindeki yanıtlarda truncation yaşanabiliyor ve bu bazen garip sorunlara yol açıyor.
Hata Ayıklama: DNS Sorunlarını Tespit Etmek
Production’da “pod’lar birbirini bulamıyor” şikayeti aldığınızda nereye bakacağınızı bilmek çok değerli. İlk önce CoreDNS pod’larının sağlığını kontrol edin:
kubectl get pods -n kube-system -l k8s-app=kube-dns -o wide
kubectl logs -n kube-system -l k8s-app=kube-dns --tail=50
Sonra test pod’u oluşturup DNS sorgusu yapın:
kubectl run dns-test --image=busybox:1.35 --rm -it --restart=Never -- sh
# Pod içinde:
nslookup kubernetes.default
nslookup kubernetes.default.svc.cluster.local
nslookup google.com
cat /etc/resolv.conf
Daha detaylı debug için dnsutils imajını kullanın:
kubectl run dnsutils --image=registry.k8s.io/e2e-test-images/jessie-dnsutils:1.3
--rm -it --restart=Never -- bash
# Pod içinde:
dig @10.96.0.10 kubernetes.default.svc.cluster.local
dig @10.96.0.10 google.com
nslookup -type=SRV _https._tcp.kubernetes.default.svc.cluster.local
CoreDNS loglarında hata görüyorsanız log seviyesini artırabilirsiniz. Corefile’a log plugin’ini ekleyin:
kubectl edit configmap coredns -n kube-system
# errors satırının altına ekleyin:
# log
Bu, tüm sorguları loglar. Yüksek trafikli ortamda log miktarı çok artacağından sadece debug sırasında açın.
CoreDNS Metriklerini İzlemek
prometheus plugin’i aktifse CoreDNS metrikleri 9153 portunda yayınlanır. Prometheus + Grafana kuruluysa hemen dashboard ekleyebilirsiniz. Metrik endpoint’ini hızlıca test etmek için:
kubectl port-forward -n kube-system service/kube-dns 9153:9153 &
curl http://localhost:9153/metrics | grep coredns_dns_requests_total
İzlenmesi gereken temel metrikler:
- coredns_dns_requests_total: Toplam sorgu sayısı, ani artışlar loop’a işaret edebilir
- coredns_dns_responses_total: Yanıt tiplerine göre dağılım, SERVFAIL artışı yapılandırma sorununa işaret eder
- coredns_forward_requests_total: Upstream’e iletilen sorgular
- coredns_cache_hits_total: Cache hit oranı, düşükse TTL değerini gözden geçirin
- coredns_panics_total: Bu sıfır olmalı, artıyorsa ciddi bir sorun var
NodeLocal DNSCache ile Performansı Artırmak
Büyük cluster’larda CoreDNS pod’larına gelen yük çok artabiliyor. NodeLocal DNSCache, her node’da bir DNS cache daemon’ı çalıştırarak hem latency’yi düşürür hem de CoreDNS üzerindeki yükü azaltır.
# NodeLocal DNSCache manifest'ini indirin
curl https://raw.githubusercontent.com/kubernetes/kubernetes/master/cluster/addons/dns/nodelocaldns/nodelocaldns.yaml -O
# Değişkenleri ayarlayın
KUBEDNS_SERVICE_IP=$(kubectl get svc kube-dns -n kube-system -o jsonpath='{.spec.clusterIP}')
LOCAL_DNS_IP="169.254.20.10"
sed -i "s/__PILLAR__DNS__SERVER__/${KUBEDNS_SERVICE_IP}/g" nodelocaldns.yaml
sed -i "s/__PILLAR__LOCAL__DNS__/${LOCAL_DNS_IP}/g" nodelocaldns.yaml
sed -i "s/__PILLAR__DNS__DOMAIN__/cluster.local/g" nodelocaldns.yaml
kubectl apply -f nodelocaldns.yaml
NodeLocal DNSCache devreye girdiğinde pod’ların /etc/resolv.conf dosyasında nameserver olarak 169.254.20.10 görünmeli:
kubectl run test-pod --image=busybox --rm -it --restart=Never -- cat /etc/resolv.conf
Sık Karşılaşılan Sorunlar ve Çözümleri
5 saniye DNS timeout sorunu: Bu klasik bir Kubernetes DNS problemidir. Linux’ta conntrack tablosu, UDP sorgularında race condition’a yol açabilir. Çözüm olarak CoreDNS’in forward plugin’inde prefer_udp yerine TCP kullanmak ya da pod spec’ine dnsConfig eklemek işe yarar:
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: dns-config-test
spec:
dnsConfig:
options:
- name: ndots
value: "2"
- name: edns0
- name: single-request-reopen
containers:
- name: test
image: busybox
command: ["sleep", "3600"]
EOF
ndots:2 değeri özellikle önemli. Varsayılan ndots:5 ile her DNS sorgusunda önce 5 farklı arama yapılıyor (servis.namespace.svc.cluster.local, servis.namespace.svc, vs.). Bu gereksiz yük yaratıyor. ndots:2‘ye düşürmek latency’yi belirgin şekilde azaltıyor.
CoreDNS loop tespiti: loop plugin’i bir döngü tespit ettiğinde pod crash’e giriyor. Logda Loop ... detected for zone "." görüyorsanız upstream DNS olarak cluster’ın kendi DNS IP’sini gösteriyorsunuzdur. /etc/resolv.conf‘u kontrol edin:
cat /etc/resolv.conf
# Eğer burada cluster DNS IP'si varsa loop oluşur
# forward . /etc/resolv.conf yerine direkt IP kullanın
Düzeltme olarak Corefile’da upstream’i açık şekilde belirtin:
forward . 8.8.8.8 1.1.1.1
Sonuç
CoreDNS, Kubernetes ekosisteminin en kritik bileşenlerinden biri ama çoğu zaman “varsayılan kurulum yeterli” diye üzerine düşülmüyor. Oysa performans sorunlarının, intermittent bağlantı hatalarının ve servis keşif problemlerinin büyük kısmının arkasında DNS yapılandırma eksiklikleri var.
Anlatılanları özetlemek gerekirse: kurulum tek başına yetmiyor, Corefile’ı gerçekten anlamak gerekiyor. ndots değerini ortamınıza göre ayarlayın, NodeLocal DNSCache’i ciddi iş yüklerinde mutlaka değerlendirin, Prometheus metrikleriyle DNS sağlığını aktif olarak izleyin. Özellikle stub zone ve forward yapılandırmalarını doğru kurmak, hybrid ortamlarda saatlerce harcanan hata ayıklama süresini önlüyor.
DNS konusunda “çalışıyor, dokunma” refleksiyle yaklaşmak, ileride en kötü zamanda sizi yakalayan sorunlara zemin hazırlıyor. Şimdi beş dakika ayırıp cluster’ınızdaki CoreDNS yapılandırmasını gözden geçirmenizi öneririm.
