CoreDNS Plugin’leri: forward, cache, log ve health Kullanımı
Kubernetes cluster’larımda CoreDNS ile ilk ciddi tanışmam, production ortamında yaşanan garip bir DNS gecikme problemiyle oldu. Servisler bazen 2-3 saniye gecikmeli yanıt veriyordu, bazen hiç yanıt vermiyordu. O güne kadar CoreDNS’e “zaten çalışıyor, dokunma” gözüyle bakıyordum. Ama o geceden sonra her bir plugin’i tek tek incelemek zorunda kaldım. Bugün sizinle bu süreçte en çok işime yarayan dört plugin’i paylaşacağım: forward, cache, log ve health. Bunların her biri kendi başına bir dünya, ama birlikte doğru kullanıldığında CoreDNS’i gerçekten güçlü bir araca dönüştürüyorlar.
CoreDNS’in Mimarisi: Plugin Zinciri
CoreDNS, monolitik bir DNS sunucusu değil. Her şey plugin üzerine kurulu. Bir DNS isteği geldiğinde, Corefile’da tanımlı plugin’ler sırayla devreye giriyor. Bu zinciri anlamadan plugin’leri düzgün yapılandıramazsınız.
Corefile’ın temel yapısı şöyle:
# /etc/coredns/Corefile
.:53 {
log
health :8080
cache 300
forward . 8.8.8.8 8.8.4.4
errors
}
Burada sıralama önemli. log plugin’i isteği önce görür, sonra health kontrolü yapılır, ardından cache‘e bakılır, cache’de yoksa forward upstream’e gider. Bu akışı kafanızda oturtun, çünkü ileride yapılandırma sorunlarının büyük çoğunluğu bu sıralamadan kaynaklanıyor.
forward Plugin’i: Upstream DNS Yönetimi
forward plugin’i, CoreDNS’in upstream DNS sunucularıyla konuşmasını sağlar. Basit görünüyor ama içinde ciddi özellikler var.
En temel kullanım:
.:53 {
forward . 8.8.8.8 8.8.4.4 1.1.1.1
}
Bu yapılandırmada CoreDNS, gelen tüm sorguları sırayla 8.8.8.8, 8.8.4.4 ve 1.1.1.1’e iletir. Ama burada dikkat edilmesi gereken bir nokta var: varsayılan policy random‘dur. Yani her sorguda upstream sunuculardan biri rastgele seçilir. Bu, bazı durumlarda tutarsız sonuçlara yol açabilir.
Policy seçenekleri:
- random: Her sorguda rastgele upstream seçer (varsayılan)
- round_robin: Sırayla upstream’lere gider
- sequential: İlk upstream çalışmıyorsa bir sonrakine geçer
Production’da genellikle sequential veya round_robin kullanmak daha öngörülebilir sonuçlar verir:
.:53 {
forward . 8.8.8.8 8.8.4.4 {
policy sequential
max_fails 3
expire 10s
health_check 5s
}
}
Split-Horizon DNS Yapılandırması
Şirketteki en yaygın senaryolardan biri split-horizon DNS. İç domain’ler için şirket DNS sunucusuna, dış domain’ler için public DNS’e yönlendirme:
.:53 {
# İç domain'ler için şirket DNS'i
forward sirket.local 192.168.1.10 192.168.1.11 {
policy round_robin
health_check 10s
}
# dev ortamı için ayrı DNS
forward dev.internal 10.0.0.53 {
expire 5s
}
# Geri kalan her şey için public DNS
forward . 1.1.1.1 8.8.8.8 {
tls_servername cloudflare-dns.com
policy sequential
}
cache 300
log
errors
}
DNS-over-TLS ile Güvenli Forward
Özellikle public Wi-Fi ağlarında veya güvenlik politikası katı olan ortamlarda DNS-over-TLS kullanmak isteyebilirsiniz:
.:53 {
forward . tls://1.1.1.1 tls://1.0.0.1 {
tls_servername cloudflare-dns.com
health_check 5s
max_fails 2
expire 10s
}
cache 300
errors
}
tls_servername parametresi önemli. TLS bağlantısında SNI için kullanılıyor ve sertifika doğrulaması bu isimle yapılıyor.
forward Parametreleri
- policy: Upstream seçim stratejisi (random/round_robin/sequential)
- max_fails: Bir upstream’in başarısız kabul edilmesi için gereken hata sayısı
- expire: Başarısız upstream’in ne kadar süre sonra tekrar deneneceği
- health_check: Upstream sağlık kontrolü aralığı
- tls_servername: DNS-over-TLS için sunucu adı
- except: Belirtilen domain’leri bu forward bloğundan hariç tutar
- force_tcp: UDP yerine TCP kullanmaya zorlar
cache Plugin’i: DNS Önbellekleme
DNS önbellekleme, hem performans hem de upstream yükü açısından kritik. Ama yanlış yapılandırıldığında ciddi sorunlara da yol açabiliyor. O geceki production problemimin yarısı da buradaydı aslında: çok agresif cache ayarları yüzünden güncellenen bir IP adresi saatlerce eski değerle cache’de kalmıştı.
Temel cache yapılandırması:
.:53 {
cache {
success 9984 3600 # başarılı yanıtlar: max 9984 kayıt, 3600 saniye TTL
denial 9984 300 # NXDOMAIN yanıtları: 300 saniye
prefetch 10 1m 10% # prefetch ayarları
}
forward . 8.8.8.8
}
Parametreleri açıklayalım:
- success [capacity] [ttl]: Pozitif yanıtlar için kapasite ve maksimum TTL
- denial [capacity] [ttl]: Negatif yanıtlar (NXDOMAIN, SERVFAIL) için
- prefetch [amount] [duration] [percentage]: Cache dolmadan önce yenileme
Prefetch: Sessiz Kahraman
prefetch özelliği çok az konuşulan ama çok işe yarayan bir özellik. Nasıl çalışıyor? Diyelim ki bir cache kaydının TTL’i dolmak üzere, ve bu sorgu son 1 dakika içinde en az 10 kez sorgulanmış. CoreDNS bu durumda TTL dolmadan önce sessizce upstream’den yenileme yapıyor. Kullanıcı asla stale cache durumunu görmüyor.
.:53 {
cache {
success 9984 1800
denial 1000 60
prefetch 10 60s 10%
# Son 60 saniye içinde %10 TTL kaldığında,
# en az 10 kez sorgulanmış kayıtları prefetch et
}
forward . 1.1.1.1
errors
}
Kubernetes Ortamında Cache Stratejisi
Kubernetes’te CoreDNS cache’ini yapılandırırken dikkatli olmak gerekiyor. Pod’lar sürekli oluşturuluyor ve siliniyor, servis IP’leri değişiyor. Çok uzun TTL değerleri stale DNS kayıtlarına yol açar:
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
cache {
success 9984 30 # Kubernetes servisleri için düşük TTL
denial 1000 5
prefetch 5 10s 10%
}
forward . /etc/resolv.conf {
max_concurrent 1000
}
loop
reload
loadbalance
}
Burada Kubernetes servisleri için cache TTL’ini 30 saniyeye düşürdüm. Bu, servis değişikliklerinin hızlı yayılmasını sağlıyor. Tabii upstream yükü artıyor, ama Kubernetes iç DNS’i zaten cluster içinde çalışıyor, bu yüzden overhead kabul edilebilir.
Cache’i Devre Dışı Bırakmak
Belirli domain’ler için cache’i devre dışı bırakmak isteyebilirsiniz. Örneğin, sık değişen dinamik kayıtlar için:
.:53 {
cache {
success 9984 300
denial 1000 30
disable success dynamic.example.com
disable denial dynamic.example.com
}
forward . 8.8.8.8
}
log Plugin’i: DNS Trafiğini Anlamak
log plugin’i olmadan CoreDNS’i yönetmek kör uçmak gibi. Özellikle sorun giderme anında bu plugin hayat kurtarıyor.
Temel kullanım son derece basit:
.:53 {
log
forward . 8.8.8.8
}
Bu haliyle her DNS sorgusunu stdout’a yazar. Ama production’da bu çok gürültülü olabilir. Filtreleme şart:
.:53 {
log . {
class success
class denial
}
forward . 8.8.8.8
}
Log Class’ları
- all: Her şeyi logla (varsayılan)
- success: Başarılı yanıtları logla
- denial: NXDOMAIN ve NODATA yanıtlarını logla
- error: Hataları logla (SERVFAIL vb.)
- stdout: stdout’a yaz (varsayılan)
Log Formatını Özelleştirme
Varsayılan log formatı işlevsel ama bazen daha fazlasına ihtiyaç duyuyorsunuz. Özellikle ELK stack veya Grafana Loki’ye göndereceğiniz JSON logları için:
.:53 {
log . {
format combined
class all
}
forward . 8.8.8.8
}
CoreDNS log formatı için birkaç seçenek:
- {type}: Sorgu tipi (A, AAAA, MX vb.)
- {name}: Sorgulanan domain adı
- {class}: DNS class’ı (genellikle IN)
- {proto}: Protokol (udp/tcp)
- {remote}: İstemci IP adresi
- {size}: İstek boyutu
- {rcode}: Yanıt kodu
- {duration}: İşlem süresi
Özel format örneği:
.:53 {
log . {
format "{remote} - {type} {name} {rcode} {duration}"
}
forward . 8.8.8.8
cache 300
}
Üretim Ortamında Log Stratejisi
Her DNS sorgusunu loglamak production’da ciddi I/O yüküne yol açar. Kubernetes cluster’ında dakikada binlerce DNS sorgusu olabilir. Benim tercih ettiğim yaklaşım: sadece hataları ve denial’ları loglamak, başarılı sorguları örneklemek.
Bir Kubernetes ConfigMap örneği:
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
Corefile: |
.:53 {
errors
log . {
class denial error
}
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
cache {
success 9984 300
denial 1000 30
}
forward . 1.1.1.1 8.8.8.8 {
max_concurrent 1000
}
reload
loadbalance
}
Bu yapılandırmada sadece başarısız sorgular loglanıyor. Bir servis birbirine bağlanamıyorsa veya NXDOMAIN alıyorsa, log’larda hemen görünüyor.
health Plugin’i: CoreDNS’i İzleme
health plugin’i, CoreDNS’in sağlık durumunu HTTP endpoint üzerinden sunuyor. Kubernetes liveness/readiness probe’ları için vazgeçilmez, ama bağımsız deployment’larda da çok işe yarıyor.
Temel yapılandırma:
.:53 {
health :8080
forward . 8.8.8.8
cache 300
log
}
Bu haliyle http://localhost:8080/health endpoint’i CoreDNS çalışıyorsa OK döner.
health ve ready Farkı
health plugin’i ile ready plugin’ini karıştırmamak önemli. İkisi farklı amaçlar için:
- health: CoreDNS process’inin ayakta olup olmadığını kontrol eder. Kubernetes liveness probe için kullanılır.
- ready: Tüm plugin’lerin hazır olup olmadığını kontrol eder. Kubernetes readiness probe için kullanılır.
.:53 {
health {
lameduck 5s
}
ready :8181
forward . 8.8.8.8
cache 300
errors
}
lameduck parametresi önemli: CoreDNS kapanmadan önce bu süre kadar bekler ve bu sürede sağlık endpoint’i 503 döner. Bu, Kubernetes’in pod’u servis rotasyonundan çıkarmasına zaman tanır. Özellikle rolling update sırasında DNS hataları yaşıyorsanız bu değeri artırmayı deneyin.
Kubernetes Probe Yapılandırması
apiVersion: apps/v1
kind: Deployment
metadata:
name: coredns
namespace: kube-system
spec:
template:
spec:
containers:
- name: coredns
image: coredns/coredns:1.11.1
ports:
- containerPort: 53
protocol: UDP
- containerPort: 53
protocol: TCP
- containerPort: 8080
name: health
- containerPort: 8181
name: ready
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 60
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /ready
port: 8181
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 3
Prometheus Metrikleri ile İzleme
health endpoint’i temel bir kontrol sağlıyor ama gerçek izleme için prometheus plugin’ini de eklemenizi öneririm. Bu plugin, CoreDNS metriklerini Prometheus formatında sunar:
.:53 {
errors
health {
lameduck 5s
}
ready :8181
prometheus :9153
log . {
class denial error
}
cache {
success 9984 300
denial 1000 30
prefetch 10 60s 10%
}
forward . 1.1.1.1 8.8.8.8 {
max_concurrent 1000
health_check 5s
}
reload
}
http://localhost:9153/metrics endpoint’inden şu metriklere ulaşabilirsiniz:
- coredns_dns_requests_total: Toplam DNS istek sayısı
- coredns_dns_responses_total: Yanıt kodlarına göre toplam yanıt sayısı
- coredns_dns_request_duration_seconds: İstek gecikme histogramı
- coredns_cache_hits_total: Cache hit sayısı
- coredns_cache_misses_total: Cache miss sayısı
- coredns_forward_requests_total: Forward edilen istek sayısı
- coredns_forward_request_duration_seconds: Forward gecikme süresi
Gerçek Dünya Senaryosu: Kurumsal DNS Altyapısı
Tüm bu plugin’leri bir araya getirerek kurumsal bir ortam için örnek bir Corefile oluşturalım. Senaryomuz: On-premise Kubernetes cluster’ı, şirket içi Active Directory DNS, ve internet erişimi olan bir ortam.
# Şirket içi domain'ler
sirket.local:53 {
errors
log . {
class denial error
}
cache {
success 1000 60
denial 100 10
}
forward . 192.168.10.10 192.168.10.11 {
policy round_robin
health_check 10s
max_fails 3
expire 30s
}
}
# Kubernetes internal DNS
cluster.local:53 {
errors
log . {
class denial error
}
kubernetes cluster.local {
pods insecure
ttl 30
}
cache {
success 9984 30
denial 1000 5
}
}
# Genel internet trafiği
.:53 {
errors
health {
lameduck 5s
}
ready :8181
prometheus :9153
log . {
class denial error
}
cache {
success 9984 3600
denial 1000 300
prefetch 10 300s 10%
}
forward . tls://1.1.1.1 tls://1.0.0.1 {
tls_servername cloudflare-dns.com
policy sequential
health_check 10s
max_fails 2
expire 15s
max_concurrent 1000
}
reload 10s
}
Bu yapılandırmada birkaç önemli nokta var. Şirket içi domain’ler için ayrı bir blok tanımladım ve bu blokta cache TTL’ini düşük tutdum çünkü AD ortamlarında DNS kayıtları sık değişebilir. Kubernetes internal DNS’i için de ayrı bir blok var ve orada da TTL 30 saniye. Genel internet trafiği için DNS-over-TLS kullanıyorum ve prefetch ile birlikte 3600 saniyelik cache süresi ayarladım.
Sorun Giderme İpuçları
CoreDNS plugin’leriyle çalışırken karşılaşılan yaygın sorunlar ve çözümleri:
Cache poisoning şüphesi varsa cache’i temizleyin:
# CoreDNS pod'unu yeniden başlatarak cache'i temizleyebilirsiniz
kubectl rollout restart deployment/coredns -n kube-system
# Veya metrics endpoint'inden cache durumunu kontrol edin
curl -s http://localhost:9153/metrics | grep coredns_cache
Forward upstream sağlık durumunu kontrol edin:
# Upstream DNS'e direkt sorgu atın
dig @8.8.8.8 google.com +short
# CoreDNS üzerinden sorgu atın ve karşılaştırın
dig @localhost google.com +short
# Gecikme farkını ölçün
time dig @localhost google.com > /dev/null
time dig @8.8.8.8 google.com > /dev/null
Log seviyesini geçici olarak artırın:
# Corefile'ı geçici olarak güncelle
# reload plugin varsa otomatik yeniden yüklenecek
# Yoksa SIGHUP gönderin
kill -SIGHUP $(pidof coredns)
Sonuç
forward, cache, log ve health plugin’leri CoreDNS’in temel taşları. Bunları doğru yapılandırmadan production’a çıkmak, motorsuz araba sürmek gibi. Her birinin kendi nüansları var: forward‘da policy seçimi ve DNS-over-TLS, cache‘de TTL dengesi ve prefetch, log‘da gürültüyü azaltırken görünürlüğü korumak, health‘de lameduck süresi ve readiness ayrımı.
Yazının başında bahsettiğim o production problemini ne çözdü biliyor musunuz? Cache TTL değerlerini düzelttim, prefetch aktif ettim, log‘a denial class’ı ekledim ve health probe timeout değerlerini artırdım. Basit değişiklikler, ama bunları bilmek için o geceyi yaşamak gerekti. Umarım bu yazı size o geceyi yaşatmaz.
Her ortamın ihtiyacı farklı. Buradaki örnekleri kendi altyapınıza göre uyarlayın, Prometheus metriklerinizi izleyin ve zamanla kendi optimal değerlerinizi bulun. DNS sıkıcı bir konu gibi görünür ama her şeyin temeli odur. Temeli sağlam atarsanız üstüne ne kurarsanız kurun, rahat edersiniz.
