CoreDNS Corefile Yapılandırması ve Plugin Sistemi
Kubernetes ortamında DNS sorunlarıyla boğuşmaya başladığınızda, bir noktada CoreDNS’in Corefile’ına bakmak zorunda kalırsınız. İlk bakışta sade görünen bu yapılandırma dosyası, aslında oldukça güçlü bir plugin mimarisinin üzerine kurulu. Ben de bu yazıda CoreDNS’i salt teorik anlatmak yerine, gerçekte karşılaşılan senaryolar üzerinden anlatmaya çalışacağım.
Corefile’ın Anatomisi
CoreDNS’in kalbi Corefile’dır. Nginx’in nginx.conf‘u ne ise CoreDNS için Corefile odur. Yapı şöyle özetlenebilir: her blok bir sunucu tanımıdır ve bu sunucu hangi port/protokol üzerinde hangi zone’ları dinleyeceğini belirtir.
<zone> [<port>] {
<plugin> [<seçenekler>]
<plugin> [<seçenekler>]
}
Basit bir örnek:
. {
forward . 8.8.8.8 8.8.4.4
cache 30
log
errors
}
Bu yapılandırma şunu söylüyor: “Nokta (.) yani tüm domainler için gelen sorguları 8.8.8.8 ve 8.8.4.4’e yönlendir, 30 saniyelik önbellek kullan, logla ve hataları göster.” Gayet okunabilir.
Ama işler genellikle bu kadar sade kalmaz.
Plugin Sistemi Nasıl Çalışır?
CoreDNS’in en önemli özelliği plugin tabanlı mimarisinin olması. Bind9 gibi monolitik bir yapı yerine, her işlev ayrı bir plugin olarak tanımlanmış. Bu mimari beraberinde hem esneklik hem de dikkat edilmesi gereken bazı önemli detaylar getiriyor.
Plugin zinciri ve sıralama: Corefile’daki plugin sırası değil, CoreDNS’in derleme zamanında belirlenen plugin sırası önemlidir. Yani siz Corefile’da cache‘i forward‘dan önce yazarsanız bu bir hata üretmez ama davranış sizi şaşırtabilir. Plugin’ler her zaman önceden tanımlı sırayla çalışır.
Resmi plugin sırasına göz atmak için:
coredns -plugins
Bu komut mevcut binary’ye derlenmiş tüm plugin’leri listeler. Production ortamında hangi plugin’lerin mevcut olduğunu bilmek, “neden çalışmıyor” sorularının yarısını yanıtlar.
Kubernetes’te Varsayılan Corefile
Kubernetes’te kube-system namespace’inde çalışan CoreDNS’in varsayılan Corefile’ına bakalım:
kubectl get configmap coredns -n kube-system -o yaml
Çıktı genellikle şuna benzer:
.: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
}
Her satırın ne işe yaradığını tek tek geçelim:
- errors: DNS sorgularındaki hataları stderr’e yazar
- health:
/healthendpoint’i üzerinden liveness probe’a yanıt verir,lameduck 5sise pod kapanırken 5 saniye boyunca sağlıklı olmadığını bildirerek graceful shutdown sağlar - ready: Tüm plugin’ler hazır olana kadar
/readyendpoint’i 503 döner - kubernetes: Kubernetes servis ve pod DNS’ini yönetir,
pods insecureile pod IP’lerinden reverse DNS yapılabilir - prometheus:
:9153portunda metrics endpoint açar - forward: Cluster dışı sorgular için upstream DNS belirtir
- cache: TTL bazlı önbellek, burada 30 saniye
- loop: Sonsuz döngü tespiti, varsa CoreDNS crash’ler (kasıtlı)
- reload: Corefile değişikliklerini otomatik algılar
- loadbalance: Round-robin DNS load balancing
Gerçek Senaryo 1: Şirket İçi Domain Yönlendirmesi
Diyelim ki şirketinizde internal.sirket.com.tr domaini için ayrı bir DNS sunucunuz var ve bu sunucu 192.168.1.10 adresinde çalışıyor. Kubernetes pod’larının bu domain’e erişebilmesi lazım ama geri kalan trafiğin normal forward’a gitmesini istiyorsunuz.
.:53 {
errors
health
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
forward internal.sirket.com.tr 192.168.1.10 {
policy sequential
health_check 5s
}
forward . /etc/resolv.conf {
max_concurrent 1000
}
cache 30
loop
reload
loadbalance
}
Burada dikkat edilmesi gereken nokta: iki forward direktifi var. CoreDNS en spesifik zone’u önce eşleştirir. internal.sirket.com.tr için gelen sorgular 192.168.1.10’a giderken geri kalanlar sistem DNS’ine gider.
Bunu ConfigMap olarak uygulamak için:
kubectl edit configmap coredns -n kube-system
Değişiklikler reload plugin’i sayesinde otomatik algılanır, pod’ları yeniden başlatmanız gerekmez. Ama değişiklik sonrası log’ları izlemek yine de iyi bir alışkanlık:
kubectl logs -n kube-system -l k8s-app=kube-dns -f
Gerçek Senaryo 2: Hosts Plugin ile Statik Kayıt
Bazı durumlarda DNS sunucusuna kayıt eklemek mümkün olmayabilir veya geçici bir çözüm gerekebilir. hosts plugin’i tam bu noktada devreye giriyor.
.:53 {
errors
health
ready
hosts /etc/coredns/customhosts {
192.168.10.50 eski-sistem.internal
192.168.10.51 migration-db.internal
fallthrough
}
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
forward . /etc/resolv.conf
cache 30
loop
reload
loadbalance
}
fallthrough direktifi burada kritik. Eğer hosts dosyasında eşleşme bulunamazsa sorgu bir sonraki plugin’e geçsin istiyorsanız fallthrough şart. Yoksa NXDOMAIN döner.
Hosts dosyasını ConfigMap’e eklemek için:
kubectl create configmap custom-hosts
--from-literal=customhosts="192.168.10.50 eski-sistem.internaln192.168.10.51 migration-db.internal"
-n kube-system
Ardından CoreDNS deployment’ına bu configmap’i volume olarak bağlarsınız.
Gerçek Senaryo 3: Rewrite Plugin ile DNS Manipülasyonu
Migration süreçlerinde ya da test ortamlarında rewrite plugin’i hayat kurtarır. Örneğin api.eskisite.com sorgularını api.yenisite.com‘a yönlendirmek istiyorsunuz:
.:53 {
errors
rewrite name api.eskisite.com api.yenisite.com
rewrite name regex (.*).test.internal {1}.staging.cluster.local answer auto
forward . /etc/resolv.conf
cache 30
loop
reload
loadbalance
}
İkinci rewrite kuralına dikkat: regex ile pattern matching yapıyoruz. servis1.test.internal sorgusu servis1.staging.cluster.local‘e rewrite ediliyor. answer auto ise cevap paketindeki ismi de otomatik olarak orijinal sorguya dönüştürür, aksi halde client kafası karışabilir.
Rewrite kurallarını test etmek için:
kubectl run -it --rm dns-test --image=busybox --restart=Never -- nslookup api.eskisite.com
Cache Plugin’in İncelikleri
Cache plugin göründüğünden daha fazla seçenek sunar. Şöyle bir yapılandırma düşünün:
.:53 {
errors
cache {
success 9984 3600
denial 9984 5
prefetch 10 1m 10%
serve_stale 1h
}
forward . 8.8.8.8 8.8.4.4
log
}
- success 9984 3600: Başarılı yanıtlar için 9984 entry kapasiteli, max 3600 saniyelik cache
- denial 9984 5: NXDOMAIN ve SERVFAIL yanıtları için sadece 5 saniye cache (negative caching’i kısa tutmak iyi pratik)
- prefetch 10 1m 10%: Son 1 dakikada 10’dan fazla sorgulan ve TTL’nin %10’una gelen kayıtlar proaktif olarak yenilenir
- serve_stale 1h: Upstream cevap veremezse 1 saate kadar eski cache verisi döndürülür
serve_stale özellikle upstream DNS’in unstable olduğu ortamlarda çok değerli. Ziraat Bankası DNS’ini mi forward ettiniz ve o an arızalı? En azından önbelleğinizdeki verilerle ayakta kalırsınız.
Log Plugin ile Detaylı İzleme
Üretim ortamında DNS sorgularını loglamak başlangıçta masum görünür ama yüksek trafikli ortamlarda log hacmi sizi boğabilir. Seçici loglama şöyle yapılır:
.:53 {
errors
log . {
class denial error
}
log internal.sirket.com.tr
forward . /etc/resolv.conf
cache 30
}
Birinci log direktifi sadece NXDOMAIN ve hata yanıtlarını loglar. İkincisi ise sadece belirli zone için tüm sorguları loglar. Bu şekilde gürültüyü azaltıp önemli olana odaklanabilirsiniz.
Log formatını özelleştirmek de mümkün:
.:53 {
log . {
format combined
class all
}
forward . /etc/resolv.conf
cache 30
}
Health Check ve Metrics
CoreDNS’i production’da izlemek için iki endpoint kritik:
# Liveness probe
curl http://localhost:8080/health
# Readiness probe
curl http://localhost:8181/ready
# Prometheus metrics
curl http://localhost:9153/metrics
Prometheus plugin ile toplanan önemli metrikler:
- coredns_dns_requests_total: Toplam sorgu sayısı, tip ve protokole göre
- coredns_dns_responses_total: Yanıt tiplerine göre (NOERROR, NXDOMAIN, SERVFAIL vb.)
- coredns_cache_hits_total / coredns_cache_misses_total: Cache etkinliği
- coredns_forward_healthcheck_failures_total: Upstream DNS sağlık sorunları
- coredns_dns_request_duration_seconds: Sorgu gecikme histogramı
Grafana’da CoreDNS dashboard’u kurmak için dashboard ID 14981’i kullanabilirsiniz, Kubernetes ortamları için oldukça kapsamlı.
Çoklu Server Block Kullanımı
Bazen farklı portlarda veya farklı zone’lar için ayrı sunucu blokları tanımlamak gerekir:
cluster.local:53 {
kubernetes cluster.local {
pods insecure
}
cache 30
errors
log
}
.:53 {
forward . 8.8.8.8 {
except cluster.local
}
cache 60
errors
}
.:5353 {
# Internal test sorguları için ayrı port
hosts {
192.168.99.100 test.local
fallthrough
}
forward . /etc/resolv.conf
}
Her blok bağımsız bir listener oluşturur. Bu yapı özellikle split-horizon DNS gereksinimleri olan ortamlarda işe yarar. Hem internal hem external sorguların farklı şekilde yanıtlanması gerektiğinde bu yaklaşım oldukça temiz bir çözüm sunar.
Corefile Doğrulama ve Test
Değişiklik yapmadan önce Corefile’ı doğrulamak için:
# Binary ile doğrulama
coredns -conf /etc/coredns/Corefile -validate
# Kubernetes'te ConfigMap değişikliği sonrası CoreDNS pod'larını izle
kubectl rollout status deployment/coredns -n kube-system
# DNS sorgu testi
kubectl run dnsutils --image=gcr.io/kubernetes-e2e-test-images/dnsutils:1.3
--restart=Never -it --rm -- dig @10.96.0.10 kubernetes.default.svc.cluster.local
dig çıktısında ANSWER SECTION kısmında kayıt görünüyorsa her şey yolunda demektir. AUTHORITY SECTION boşsa ve sadece ADDITIONAL SECTION doluysa yönlendirme doğru çalışıyor ama yanıt yetkili değil demektir ki bu çoğu senaryoda normaldir.
Daha kapsamlı test için:
# SERVFAIL kontrolü
dig +short @10.96.0.10 olmayan-servis.cluster.local
# Boş veya NXDOMAIN beklenir
# Upstream çalışıyor mu?
dig +short @10.96.0.10 google.com
# IP adresi görünmeli
Sık Karşılaşılan Sorunlar
Loop detection crash: CoreDNS’in loop plugin’i döngü tespit edince kendini öldürür. Bu genellikle node’un /etc/resolv.conf dosyasının CoreDNS’in kendi IP’sine işaret ettiğinde olur. systemd-resolved kullanan Ubuntu sistemlerde 127.0.0.53’e yönlendirme bu sorunu tetikleyebilir.
Çözüm için:
forward . 8.8.8.8 8.8.4.4 {
# /etc/resolv.conf yerine explicit IP
}
NXDOMAIN fırtınası: Uygulamalar var olmayan servisleri ısrarla sorguladığında negative caching devreye girmezse CoreDNS ve upstream DNS ezilir. Cache denial süresini artırmak ve uygulama tarafında retry backoff eklemek bu sorunu çözer.
Yüksek bellek kullanımı: Cache büyüklüğünü gereksiz yere artırmak bellek sorununa yol açar. 9984 entry varsayılan değeri çoğu cluster için yeterlidir. Gerçekten yüksek sorgu hacmi varsa horizontal scaling tercih edilmeli.
Sonuç
CoreDNS’in Corefile yapılandırması, ilk bakışta sade görünse de altında ciddi bir esneklik yatıyor. Plugin sistemi sayesinde neredeyse her DNS senaryosunu karşılayabiliyorsunuz: static host kaydından Kubernetes servis discovery’sine, DNS rewrite’tan split-horizon yapılandırmasına kadar.
Benim önerim şu: önce varsayılan Corefile’ı iyi anlayın, her direktifin ne işe yaradığını bilin. Sonra ihtiyaca göre ekleme yapın. Gereksiz plugin eklemek hem performansı etkiler hem de debug süreçlerini zorlaştırır. CoreDNS log’larını ve metric’lerini düzenli izleyin, özellikle coredns_forward_healthcheck_failures_total arttığında upstream DNS sorunlarını hemen fark edersiniz.
DNS sorunları genellikle gecenin köründe ortaya çıkar ve baskı altında debug etmek zorunda kalırsınız. Corefile’ı önceden iyi yapılandırıp test etmek, o anlar için kendinize yapabileceğiniz en iyi iyiliktir.
