CoreDNS ile Servis Mesh DNS Entegrasyonu
Kubernetes ortamında servis keşfi üzerine çalışırken en çok baş ağrıtan konulardan biri DNS katmanının doğru yapılandırılmasıdır. Özellikle Istio veya Linkerd gibi bir servis mesh kurduğunuzda, CoreDNS’in bu ekosistemle nasıl konuştuğunu anlamadan ciddi sorunlarla karşılaşabilirsiniz. Ben de geçen yıl büyük bir finansal teknoloji şirketinin Kubernetes geçiş projesinde tam olarak bu problemle boğuştum. O deneyimden edindiğim bilgileri burada paylaşmak istedim.
CoreDNS ve Servis Mesh: Neden Birlikte Düşünmek Gerekir
CoreDNS, Kubernetes 1.13’ten itibaren varsayılan DNS çözümleyici olarak kube-dns’in yerini aldı. Plugin tabanlı mimarisi sayesinde son derece esnek bir yapı sunuyor. Ancak Istio gibi bir servis mesh devreye girdiğinde, DNS katmanı artık sadece servis isimlerini IP’lere çevirmekle kalmıyor; trafik yönetimi, güvenlik politikaları ve gözlemlenebilirlik gibi katmanlarla iç içe geçiyor.
Servis mesh’in DNS’e dokunduğu en kritik nokta, sidecar proxy’lerin trafik yönlendirmesi sırasında ortaya çıkıyor. Envoy proxy (Istio’nun kullandığı), gelen DNS sorgularını kendi yönlendirme tablosuna göre işliyor ve bu süreçte CoreDNS ile uyumlu çalışması şart. Aksi durumda, servisler birbirini DNS üzerinden bulamıyor ya da yanlış endpoint’lere ulaşıyor.
Şimdi adım adım nasıl bir yapı kurulabileceğine bakalım.
Temel CoreDNS Yapılandırması
Öncelikle mevcut CoreDNS ConfigMap’inizi kontrol etmeniz gerekiyor:
kubectl get configmap coredns -n kube-system -o yaml
Tipik bir çıktı şöyle görünür:
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
}
Bu yapılandırma yeterli gibi görünse de servis mesh senaryolarında birkaç kritik eksiklik içeriyor. Özellikle multicluster veya federated yapılarda external DNS sorgularının nasıl işleneceği tanımlanmamış.
Istio ile CoreDNS Entegrasyonu
Istio kurulumu sonrasında istio-system namespace’inde kendi DNS bileşenleri çalışmaya başlar. Ancak asıl entegrasyon, ServiceEntry ve VirtualService kaynaklarının DNS katmanıyla buluştuğu noktada gerçekleşir.
Şu senaryoyu ele alalım: Uygulamamız payment-service.production.svc.cluster.local adresine istek gönderiyor, ancak bu servis başka bir cluster’da çalışıyor. Istio’nun bu durumu çözmesi için CoreDNS’e doğru yönlendirmeyi yapmamız gerekiyor.
İlk adımda özel bir DNS stub zone yapılandırması ekliyoruz:
kubectl edit configmap coredns -n kube-system
Corefile’a şunu ekliyoruz:
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
}
remote.cluster.local:53 {
errors
cache 30
forward . 10.96.0.11 {
force_tcp
}
}
Buradaki 10.96.0.11, remote cluster’daki CoreDNS servisinin cluster IP’si. Bu konfigürasyon, remote.cluster.local domain’ine gelen sorguları remote cluster’ın DNS sunucusuna yönlendiriyor.
ServiceEntry ile DNS Kaydı Yönetimi
Istio ekosisteminde dış servisleri cluster içinden erişilebilir kılmak için ServiceEntry kullanıyoruz. Ancak bu yeterli değil; CoreDNS’in bu girişleri tanıması gerekiyor.
Örnek bir ServiceEntry:
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: external-payment-api
namespace: production
spec:
hosts:
- payment.external-provider.com
ports:
- number: 443
name: https
protocol: HTTPS
location: MESH_EXTERNAL
resolution: DNS
endpoints:
- address: payment.external-provider.com
Bu tanımın ardından CoreDNS’e özel bir forward kuralı ekliyoruz:
payment.external-provider.com:53 {
errors
cache 10
forward . 8.8.8.8 8.8.4.4 {
prefer_udp
health_check 5s
}
}
Bu şekilde payment.external-provider.com‘a gelen DNS sorguları Google’ın public DNS’ine yönlendirilirken, cluster içi sorgular kube-dns üzerinden çözümleniyor.
DNS Cache Optimizasyonu ve TTL Ayarları
Servis mesh ortamlarında DNS cache yönetimi kritik bir performans faktörü. Yanlış TTL değerleri hem gereksiz DNS trafiğine hem de stale (eski) kayıtlar nedeniyle bağlantı hatalarına yol açabilir.
Bir üretim ortamında yaşadığım somut sorunu paylaşayım: Deployment güncellemelerinde pod IP’leri değişiyor ama bazı servisler eski IP’lere bağlanmaya devam ediyordu. Sorun, CoreDNS cache’inin 30 saniyelik TTL ile çalışmasına karşın Istio sidecar proxy’nin kendi cache’inin 300 saniye olarak ayarlanmış olmasıydı.
Bu durumu düzeltmek için önce mevcut cache davranışını inceliyoruz:
# CoreDNS metrics'ini kontrol et
kubectl port-forward -n kube-system svc/kube-dns 9153:9153
curl http://localhost:9153/metrics | grep coredns_cache
Ardından cache plugin’ini daha ayrıntılı yapılandırıyoruz:
cache 30 {
success 9984 30
denial 9984 5
servfail 0
prefetch 10 1m 10%
serve_stale 5s
}
Bu yapılandırmada:
- success 9984 30: Başarılı yanıtlar için maksimum 9984 kayıt, 30 saniye TTL
- denial 9984 5: NXDOMAIN yanıtları için 5 saniye TTL (kısa tutuyoruz, yeni servisler hızlı bulunabilsin)
- prefetch 10 1m 10%: Sık sorgulanan kayıtlar TTL dolmadan yenilenir
- serve_stale 5s: Upstream erişilemez olduğunda 5 saniye eski yanıt servis edilir
Linkerd İçin Özel Yapılandırma
Linkerd, Istio’ya kıyasla daha az DNS müdahalesine ihtiyaç duyuyor ancak kendi DNS proxy’si olan linkerd-proxy‘nin CoreDNS ile doğru konuşması gerekiyor. Özellikle multicluster kurulumda ServiceMirror bileşeni, remote cluster servislerini yerel cluster’da mirror’lar ve bu mirror’ların DNS’e yansıtılması kritik önem taşır.
Linkerd multicluster için CoreDNS yapılandırması:
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
}
kubernetes east.cluster.local {
pods insecure
ttl 30
namespaces production staging
}
prometheus :9153
forward . /etc/resolv.conf {
max_concurrent 1000
}
cache 30
loop
reload
loadbalance
}
Burada east.cluster.local zone’u, Linkerd’in mirror ettiği servislerin DNS üzerinden erişilebilmesi için tanımlanıyor. namespaces production staging direktifi ile sadece belirli namespace’lerdeki servisleri bu zone üzerinden yayınlıyoruz, güvenlik açısından önemli bir kısıtlama.
Hata Ayıklama: Gerçek Hayattan Senaryolar
DNS sorunları genellikle en belirsiz hata mesajlarıyla kendini gösteriyor. Şu araçları her zaman yanınızda bulundurun.
DNS sorgularını canlı olarak izlemek için:
# CoreDNS log plugin'ini geçici olarak aktif et
kubectl edit configmap coredns -n kube-system
# Corefile'a log ekle:
.:53 {
log
errors
...
}
# Pod'ları restart et
kubectl rollout restart deployment/coredns -n kube-system
# Logları takip et
kubectl logs -f -n kube-system -l k8s-app=kube-dns
Belirli bir servise DNS çözümlemesini test etmek için:
# Geçici debug pod oluştur
kubectl run dns-debug --image=busybox:1.35 --rm -it --restart=Never -- /bin/sh
# Pod içinden DNS sorgusu
nslookup payment-service.production.svc.cluster.local
dig +short payment-service.production.svc.cluster.local @10.96.0.10
# Timeout ve retry davranışını test et
dig payment-service.production.svc.cluster.local +time=2 +retry=3
Istio ile birlikte DNS sorunlarını araştırırken sidecar proxy’nin DNS önbelleklemesini de kontrol etmek gerekiyor:
# Envoy proxy admin API'yi kullanarak DNS cache'ini sorgula
kubectl exec -it payment-pod -n production -c istio-proxy --
curl -s localhost:15000/clusters | grep payment-service | head -20
# Istio proxy konfigürasyonunu doğrula
istioctl proxy-config listeners payment-pod.production --port 53
CoreDNS Yüksek Erişilebilirlik Yapılandırması
Üretim ortamlarında CoreDNS’in tek nokta arızası (SPOF) oluşturmaması için dikkat edilmesi gereken birkaç kritik nokta var.
Pod Disruption Budget tanımı:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: coredns-pdb
namespace: kube-system
spec:
minAvailable: 1
selector:
matchLabels:
k8s-app: kube-dns
Anti-affinity kuralları ile CoreDNS pod’larının farklı node’lara dağılmasını sağlamak:
kubectl patch deployment coredns -n kube-system --type='json' -p='[
{
"op": "add",
"path": "/spec/template/spec/affinity",
"value": {
"podAntiAffinity": {
"preferredDuringSchedulingIgnoredDuringExecution": [
{
"weight": 100,
"podAffinityTerm": {
"labelSelector": {
"matchExpressions": [
{
"key": "k8s-app",
"operator": "In",
"values": ["kube-dns"]
}
]
},
"topologyKey": "kubernetes.io/hostname"
}
}
]
}
}
}
]'
Replica sayısını artırmak ise şöyle yapılıyor:
kubectl scale deployment coredns --replicas=3 -n kube-system
Ancak sadece replica artırmak yetmez. CoreDNS’e ayrılan kaynak limitlerini de üretim yüküne göre ayarlamak gerekiyor:
kubectl set resources deployment coredns
--requests=cpu=100m,memory=70Mi
--limits=cpu=500m,memory=170Mi
-n kube-system
DNS Policy ve Pod Yapılandırması
Servis mesh içindeki pod’ların DNS davranışını kontrol etmek için dnsPolicy ve dnsConfig alanlarını kullanıyoruz. Bu özellikle sidecarsız çalışan (ambient mesh modunda) bazı bileşenler için önem taşıyor.
apiVersion: v1
kind: Pod
metadata:
name: payment-processor
namespace: production
spec:
dnsPolicy: "None"
dnsConfig:
nameservers:
- 10.96.0.10
searches:
- production.svc.cluster.local
- svc.cluster.local
- cluster.local
options:
- name: ndots
value: "2"
- name: timeout
value: "2"
- name: attempts
value: "3"
containers:
- name: payment-processor
image: payment-processor:v2.1.0
Burada ndots: 2 değeri önemli. Varsayılan ndots: 5 değeriyle, payment-service gibi kısa bir ad için Kubernetes önce payment-service.production.svc.cluster.local, payment-service.svc.cluster.local, payment-service.cluster.local gibi tam nitelendirmeleri deneyecek, başarısız olursa payment-service‘i external DNS’de arayacak. Bu gereksiz DNS sorgularına yol açıyor. ndots: 2 ile bu arama zincirini kısaltmış oluyoruz.
Monitoring ve Alerting
CoreDNS’i izlemek için Prometheus ve Grafana entegrasyonu neredeyse zorunlu. CoreDNS zaten /metrics endpoint’i sunuyor, tek yapmanız gereken bunu scrape etmek.
ServiceMonitor tanımı (Prometheus Operator kullanıyorsanız):
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: coredns
namespace: monitoring
labels:
app: coredns
spec:
jobLabel: k8s-app
selector:
matchLabels:
k8s-app: kube-dns
namespaceSelector:
matchNames:
- kube-system
endpoints:
- port: metrics
interval: 15s
path: /metrics
İzlenmesi gereken kritik metrikler:
- coredns_dns_requests_total: Toplam DNS istek sayısı (rate olarak izleyin)
- coredns_dns_responses_total: Yanıt tiplerine göre dağılım (NXDOMAIN oranı yüksekse sorun var)
- coredns_forward_request_duration_seconds: Upstream DNS yanıt süreleri
- coredns_cache_hits_total: Cache hit oranı (düşükse TTL veya cache boyutunu gözden geçirin)
- coredns_forward_healthcheck_failures_total: Upstream sağlık kontrolü başarısızlıkları
Yaygın Hatalar ve Çözümleri
Servis mesh ve CoreDNS entegrasyonunda tekrar tekrar karşılaşılan birkaç problem var.
NXDOMAIN Fırtınası: Yeni bir namespace oluşturup servis deploy ettiğinizde, henüz DNS kaydı oluşmadan gelen sorgular NXDOMAIN dönüyor ve bunlar cache’e yazılıyor. denial TTL‘ini düşük tutmak (5-10 saniye) bu sorunu minimize eder.
ndots Cehennemi: Özellikle Python veya Node.js tabanlı uygulamalar bazen FQDN (nokta ile biten tam nitelendirilmiş domain adı) yerine kısa isim kullanıyor. payment-service. yerine payment-service kullanıldığında ndots değerine göre gereksiz lookup’lar oluyor. Uygulamayı değiştirme şansınız yoksa pod’a özel dnsConfig ile ndots değerini düşürün.
Istio’nun DNS Intercept Etmesi: Istio 1.8’den itibaren dns_proxy_addr özelliği ile DNS sorgularını intercept ediyor. Bu bazı durumlarda CoreDNS ile çakışıyor. Özellikle non-Kubernetes DNS sorguları (external API’ler için) sorun çıkarabilir. meshConfig.defaultConfig.proxyMetadata altında ISTIO_META_DNS_CAPTURE değerini kontrol edin.
CoreDNS Loop Detection: loop plugin’i, DNS lookup döngülerini tespit edip CoreDNS’i durdurabilir. Özellikle on-premise Kubernetes kurulumlarında, cluster’ın kendi nameserver’ını upstream olarak kullanması bu problemi tetikliyor. /etc/resolv.conf yerine explicit IP adresleri kullanın.
Sonuç
CoreDNS ile servis mesh entegrasyonu, göründüğünden çok daha derin bir konu. Basit bir Corefile değişikliğiyle başlıyor ama multicluster, external servisler, performance tuning ve yüksek erişilebilirlik gereksinimleri devreye girince ciddi bir mimari düşünme süreci gerekiyor.
Benim önerim şu: önce mevcut ortamınızın DNS trafik desenini ölçün. CoreDNS metriklerini bir hafta izleyin, hangi sorgular çok tekrarlanıyor, NXDOMAIN oranı nedir, upstream yanıt süreleri nasıl görünüyor? Bunları bilmeden yapılacak her optimizasyon kör bir atış olur.
Servis mesh katmanı karmaşıklık getiriyor, bunu kabul etmek gerekiyor. Ama DNS katmanı düzgün kurulduğunda, bu karmaşıklığı yönetmek çok daha kolay hale geliyor. Ağ sorunlarının önemli bir kısmı aslında DNS sorunlarıdır ve bu katmanı sağlam tutmak, gece 3’te karanlık bir terminale bakarak hata ayıklamak zorunda kalma riskini ciddi ölçüde azaltır.
