CoreDNS Yüksek Erişilebilirlik ve Ölçeklendirme
Kubernetes ortamında DNS çözümlemesi bozulduğunda ne olduğunu bir kere yaşayanlar bilir: her şey aniden durur, servisler birbirini bulamaz, hata mesajları anlamsız gelmeye başlar ve ekip panik moduna girer. CoreDNS bu senaryonun tam ortasında oturuyor ve çoğu zaman gereken ilgiyi almıyor. Yüksek erişilebilirlik ve ölçeklendirme konusunda ise durum daha da vahim: birçok kurulum varsayılan ayarlarla bırakılıyor, tekil hata noktaları görmezden geliniyor. Bu yazıda CoreDNS’i gerçekten üretime hazır hale getirmek için ne yapılması gerektiğini, neyin çalışıp neyin çalışmadığını doğrudan paylaşacağım.
CoreDNS Mimari Temelleri ve HA Gereklilikleri
CoreDNS, plugin tabanlı mimarisiyle son derece esnek bir DNS sunucusu. Kubernetes’te kube-dns’in yerini almasının temel sebebi de bu esneklik. Ama esneklik beraberinde karmaşıklık getiriyor ve yanlış yapılandırılmış bir CoreDNS kümesi, yüksek erişilebilirlik yanılsaması yaratabilir.
Temel HA gereklilikleri şunlar:
- Birden fazla replica: Tek pod çalışan CoreDNS production’da kabul edilemez
- Anti-affinity kuralları: Tüm pod’ların aynı node’da çalışmaması
- Resource limitleri: Bellek sızıntısı veya CPU spike’larının kontrol altında tutulması
- Health check mekanizmaları: Sağlıksız instance’ların trafikten çıkarılması
- Graceful shutdown: Mevcut sorguların tamamlanması için yeterli süre
Şimdi bunları tek tek nasıl uygulayacağımıza bakalım.
Replica Sayısı ve Pod Anti-Affinity
Kubernetes’te CoreDNS deployment’ı varsayılan olarak 2 replica ile gelir. Küçük kümeler için bu yeterli görünebilir, ama node sayısı arttıkça ve sorgu yükü büyüdükçe bu sayı yetersiz kalır.
Genel kural olarak her 1000 node için minimum 2 CoreDNS pod’u düşünülebilir, ama bunu gerçek metriklerle doğrulamak şart. Şu deployment yapılandırmasıyla başlayalım:
apiVersion: apps/v1
kind: Deployment
metadata:
name: coredns
namespace: kube-system
spec:
replicas: 4
selector:
matchLabels:
k8s-app: kube-dns
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1
template:
metadata:
labels:
k8s-app: kube-dns
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: k8s-app
operator: In
values:
- kube-dns
topologyKey: kubernetes.io/hostname
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: k8s-app
operator: In
values:
- kube-dns
topologyKey: topology.kubernetes.io/zone
priorityClassName: system-cluster-critical
tolerations:
- key: "CriticalAddonsOnly"
operator: "Exists"
- key: "node-role.kubernetes.io/control-plane"
effect: NoSchedule
Burada requiredDuringSchedulingIgnoredDuringExecution ile pod’ların farklı node’lara dağıtılmasını zorunlu kılıyoruz. preferredDuringScheduling kısmıyla ise farklı availability zone’lara yayılmasını tercih ediyoruz. “Required” ile “preferred” arasındaki fark kritik: required kural sağlanamazsa pod schedule edilmez, preferred ise en iyi çabayı ifade eder.
Resource Limitleri ve HPA Yapılandırması
Resource limitlerini doğru ayarlamak hem HA hem de güvenlik açısından önemli. Çok düşük limit koyarsanız OOMKill yaşarsınız, çok yüksek bırakırsanız node kaynakları israf edilir.
resources:
requests:
cpu: 100m
memory: 70Mi
limits:
cpu: 200m
memory: 170Mi
Bu değerler küçük-orta ölçekli kümeler için başlangıç noktası. Prometheus metrikleriyle gerçek kullanımı izleyip buna göre ayarlamalısınız. Ben genellikle ilk iki hafta limitlieri geniş tutup sonra metrikler ışığında daraltıyorum.
Yük arttıkça otomatik ölçeklendirme için HPA kullanabilirsiniz:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: coredns
namespace: kube-system
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: coredns
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
behavior:
scaleUp:
stabilizationWindowSeconds: 30
policies:
- type: Pods
value: 2
periodSeconds: 60
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Pods
value: 1
periodSeconds: 120
stabilizationWindowSeconds değerlerine dikkat edin. Scale-up için 30 saniye yeterli olabilir, ama scale-down için 300 saniye koyuyoruz. DNS sorgu trafiği ani iniş çıkışlar gösterebilir ve erken scale-down sonra hızlı scale-up ihtiyacı doğurabilir, bu da geçici latency artışına yol açar.
Corefile Optimizasyonu
Yüksek trafik altında CoreDNS davranışını belirleyen şey büyük ölçüde Corefile yapılandırması. Varsayılan yapılandırma production için yeterince optimize edilmemiş.
.: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
prefer_udp
health_check 5s
policy round_robin
}
cache {
success 9984 30 0
denial 9984 5 0
prefetch 10 1m 10%
serve_stale 30s
}
loop
reload
loadbalance
}
Önemli parametreler:
- lameduck 5s: Shutdown sırasında 5 saniye boyunca yeni bağlantı kabul etmeyi durdurur ama mevcut sorgular tamamlanır
- max_concurrent 1000: Upstream’e eş zamanlı maksimum sorgu sayısı, varsayılan değer çok düşük
- prefetch 10 1m 10%: Son 1 dakikada 10 kez sorgulanmış ve TTL’inin %10’u kalan kayıtları önceden yeniler
- serve_stale 30s: Upstream erişilemezse 30 saniye boyunca eski cache yanıtı dönmeye devam eder
serve_stale özelliği kritik. Upstream DNS sunucunuz geçici olarak erişilemez olduğunda, süresi dolmuş cache yanıtıyla bile hizmet devam edebilir. Bu özellik olmadan tüm DNS çözümlemesi durur.
NodeLocal DNSCache ile Latency Azaltma
Büyük kümelerde en etkili ölçeklendirme yöntemlerinden biri NodeLocal DNSCache. Her node üzerinde bir DNS cache agent çalıştırarak CoreDNS’e giden merkezi trafik yükünü dramatik biçimde azaltıyor.
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-local-dns
namespace: kube-system
spec:
selector:
matchLabels:
k8s-app: node-local-dns
template:
metadata:
labels:
k8s-app: node-local-dns
spec:
hostNetwork: true
dnsPolicy: Default
tolerations:
- operator: Exists
containers:
- name: node-cache
image: registry.k8s.io/dns/k8s-dns-node-cache:1.22.28
resources:
requests:
cpu: 25m
memory: 5Mi
limits:
cpu: 100m
memory: 30Mi
args:
- -localip
- "169.254.20.10,10.96.0.10"
- -conf
- /etc/Corefile
- -upstreamsvc
- kube-dns-upstream
- -health-port
- "8080"
ports:
- containerPort: 53
name: dns
protocol: UDP
- containerPort: 53
name: dns-tcp
protocol: TCP
volumeMounts:
- mountPath: /run/xtables.lock
name: xtables-lock
readOnly: false
- mountPath: /etc/coredns
name: config-volume
volumes:
- name: xtables-lock
hostPath:
path: /run/xtables.lock
type: FileOrCreate
- name: config-volume
configMap:
name: node-local-dns
NodeLocal DNSCache, link-local IP adresi (169.254.20.10) üzerinden çalışır. Bu IP’ye gelen sorgular önce yerel cache’e bakılır, cache’de yoksa CoreDNS’e iletilir. Büyük kümelerde CoreDNS üzerindeki yük %70-80 oranında azalabilir.
PodDisruptionBudget ile Bakım Koruması
Node bakımı veya cluster upgrade sırasında CoreDNS pod’larının tamamının aynı anda durmaması için PodDisruptionBudget şart:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: coredns-pdb
namespace: kube-system
spec:
minAvailable: 2
selector:
matchLabels:
k8s-app: kube-dns
minAvailable: 2 diyerek her zaman en az 2 CoreDNS pod’unun çalışır durumda olmasını garanti ediyoruz. kubectl drain komutu çalıştırıldığında bu kural dikkate alınır ve minAvailable şartı sağlanmadan pod evict edilmez.
Dikkat: maxUnavailable: 1 yazmak ile minAvailable: 2 yazmak farklı şeyler ifade eder. Toplam replica sayısı değişebileceğinden minAvailable ile mutlak sayı vermek daha güvenli.
Prometheus ile İzleme ve Alerting
CoreDNS’in sağlığını izlemeden HA kurulumun hiçbir anlamı yok. CoreDNS varsayılan olarak :9153 portunda Prometheus metrikleri sunuyor.
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: coredns
namespace: kube-system
labels:
release: prometheus
spec:
endpoints:
- bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
interval: 15s
port: metrics
scheme: https
tlsConfig:
insecureSkipVerify: true
jobLabel: jobLabel
namespaceSelector:
matchNames:
- kube-system
selector:
matchLabels:
k8s-app: kube-dns
Alert kuralları için şunlar kritik:
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: coredns-alerts
namespace: kube-system
spec:
groups:
- name: coredns
rules:
- alert: CoreDNSDown
expr: absent(up{job="coredns"} == 1)
for: 2m
labels:
severity: critical
annotations:
summary: "CoreDNS tamamen erişilemez"
description: "CoreDNS 2 dakikadır hiçbir instance cevap vermiyor"
- alert: CoreDNSHighErrorRate
expr: |
(rate(coredns_dns_responses_total{rcode="SERVFAIL"}[5m]) /
rate(coredns_dns_responses_total[5m])) > 0.05
for: 5m
labels:
severity: warning
annotations:
summary: "CoreDNS SERVFAIL oranı yüksek"
description: "Son 5 dakikada SERVFAIL oranı %5 üzerinde"
- alert: CoreDNSLatencyHigh
expr: |
histogram_quantile(0.99,
rate(coredns_dns_request_duration_seconds_bucket[5m])
) > 0.5
for: 10m
labels:
severity: warning
annotations:
summary: "CoreDNS p99 latency yüksek"
description: "p99 DNS sorgu süresi 500ms üzerinde"
Özellikle CoreDNSHighErrorRate alert’i pratikte çok değerli. Tek bir upstream DNS sunucusunun problem yaşaması SERVFAIL oranını ani şekilde artırabilir ve bu alert sayesinde müdahale edebilirsiniz.
Gerçek Dünya Senaryosu: Split-Brain DNS ve External DNS Entegrasyonu
Kurumsal ortamlarda sık karşılaşılan bir senaryo: iç servisler için Kubernetes DNS kullanılırken dış domain’ler için şirket içi DNS sunucuları devrede. Bu yapıyı CoreDNS ile yönetmek hem HA’yı hem de doğru yönlendirmeyi gerektiriyor.
.: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
cache {
success 9984 30 0
denial 9984 5 0
prefetch 10 1m 10%
serve_stale 30s
}
forward . 8.8.8.8 8.8.4.4 {
max_concurrent 1000
health_check 5s
policy sequential
}
loop
reload
loadbalance
}
# Şirket içi domain için özel yönlendirme
sirket.internal:53 {
errors
cache 30
forward . 10.0.1.53 10.0.1.54 {
max_concurrent 500
health_check 10s
policy round_robin
}
}
# Prod namespace için özel TTL ve logging
prod.cluster.local:53 {
errors
log
kubernetes cluster.local {
namespaces prod
ttl 5
}
cache 10
}
Bu yapıda sirket.internal sorgular şirket içi DNS’e yönlendirilirken genel internet sorguları Google DNS’e gidiyor. İkisi için ayrı cache ve health_check parametreleri var. Şirket içi DNS sunucusu erişilemez olduğunda bile serve_stale sayesinde kısa süreli kesinti atlatılabiliyor.
Upstream DNS Havuzu ve Failover
CoreDNS’in forward plugin’i birden fazla upstream destekliyor ama yük dağılımı ve failover davranışı policy seçimine bağlı:
- sequential: Listedeki ilk sunucu çalıştığı sürece hep ona yönlendirir, hata olursa sıradakine geçer
- round_robin: Her sorgu sırayla farklı sunucuya gönderilir
- random: Rastgele seçim yapılır
Çoğu production senaryosunda sequential kullanıyorum. Birincil DNS sunucusu sağlıklı olduğu sürece tüm trafik oraya gitsin, devre dışı kalınca ikinciye geçsin. round_robin teorik olarak daha iyi dağılım sağlıyor ama sorunlu bir upstream’in %50 trafik alması durumunda gecikme önemli ölçüde artıyor.
Health check aralığı da kritik. health_check 5s ile 5 saniyede bir kontrol yapılıyor. Çok sık kontrol upstream’e gereksiz yük bindirir, çok seyrek kontrol ise arızalı sunucunun uzun süre trafikte kalmasına yol açar.
Cluster Dışı CoreDNS Deployment’ı
Kubernetes dışı ortamlarda, örneğin bare metal sunucularda veya VM tabanlı altyapılarda da CoreDNS HA kurulumu yapabilirsiniz. Burada Keepalived ile VIP yaklaşımı kullanıyorum:
# Her CoreDNS sunucusuna kurulum
sudo apt-get install -y keepalived
# /etc/keepalived/keepalived.conf (MASTER sunucu)
cat << 'EOF' > /etc/keepalived/keepalived.conf
vrrp_script chk_coredns {
script "/bin/systemctl is-active coredns"
interval 2
weight -20
fall 3
rise 2
}
vrrp_instance DNS_VIP {
state MASTER
interface ens3
virtual_router_id 51
priority 110
advert_int 1
authentication {
auth_type PASS
auth_pass gizli_parola_buraya
}
virtual_ipaddress {
10.0.0.100/24
}
track_script {
chk_coredns
}
}
EOF
systemctl enable --now keepalived
# CoreDNS systemd service
cat << 'EOF' > /etc/systemd/system/coredns.service
[Unit]
Description=CoreDNS DNS server
Documentation=https://coredns.io
After=network.target
Wants=network-online.target
[Service]
Type=simple
User=coredns
ExecStart=/usr/local/bin/coredns -conf /etc/coredns/Corefile
Restart=on-failure
RestartSec=5s
LimitNOFILE=65536
LimitNPROC=512
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now coredns
Bu kurulumda 10.0.0.100 VIP’i active sunucuya yönlenir. CoreDNS servis hata verirse Keepalived health check’i yakalar ve VIP backup sunucuya taşınır. Failover süresi genellikle 2-4 saniye, bu süre içinde UDP sorguları zaten retry yapar.
Performans Testleri ve Kapasite Planlaması
HA kurulumunuzu test etmeden canlıya almak büyük risk. Basit bir stres testi için:
# dnsperf ile yük testi
# Önce test dosyası oluştur
cat << 'EOF' > /tmp/dns-queries.txt
kubernetes.default.svc.cluster.local A
google.com A
github.com A
kube-dns.kube-system.svc.cluster.local A
EOF
# 60 saniye boyunca 1000 QPS ile test
dnsperf -s 10.96.0.10
-d /tmp/dns-queries.txt
-t 60
-Q 1000
-c 10
# Sonuç: Queries sent, completed, lost ve latency istatistikleri
# CoreDNS metriklerini anlık izleme
kubectl exec -n kube-system
$(kubectl get pods -n kube-system -l k8s-app=kube-dns -o name | head -1)
-- sh -c 'while true; do
curl -s localhost:9153/metrics | grep -E "coredns_dns_requests_total|coredns_cache_hits_total" | grep -v "^#"
sleep 5
done'
Cache hit oranı %80 altındaysa ya çok fazla unique sorgu var ya da TTL değerleri çok düşük. Her iki durumda da CoreDNS üzerindeki yük artıyor.
Sonuç
CoreDNS HA ve ölçeklendirme konusunu özetlersek şu adımlar şart:
- Birden fazla replica ve güçlü anti-affinity kuralları olmadan HA kurulum olmaz, yanılsamadan ibaret
- NodeLocal DNSCache büyük kümelerde oyunun kurallarını tamamen değiştiriyor, mutlaka değerlendirin
- Corefile’daki
serve_stale,prefetchvemax_concurrentparametreleri küçük değişikliklerle büyük etki yaratıyor - PodDisruptionBudget olmadan en iyi hazırlıklarınız cluster upgrade sırasında çöpe gidebilir
- Alerting ve izleme kurulmadan bir HA kurulumu kör uçuştan farksız
DNS, altyapının kan dolaşımı. Sağlam çalışırken kimse fark etmez, bozulunca herkes sen neredeydin diye sorar. Bu yüzden CoreDNS’e hak ettiği ilgiyi göstermek ve HA yapılandırmasını ciddiye almak gerekiyor. Yukarıdaki yapılandırmaları birebir kopyalamak yerine kendi ortamınızın özelliklerine göre uyarlamanızı ve her değişikliği test ortamında doğrulamanızı şiddetle tavsiye ederim.
