CoreDNS Nedir: Cloud Native DNS Sunucusuna Giriş

Kubernetes cluster’ı ilk kurduğumda DNS olayını tam anlayamamıştım. kube-dns çalışıyor, pod’lar birbirini buluyor, herşey güzel. Ama bir gün production’da DNS sorguları aniden yavaşlamaya başladı, bazı sorgular zaman aşımına uğruyordu. O günden sonra CoreDNS’i ciddiye almaya başladım ve altında ne döndüğünü gerçekten anlamak istedim. Bu yazıda size CoreDNS’in ne olduğunu, neden bu kadar önem kazandığını ve nasıl çalıştığını anlatacağım.

CoreDNS Nedir ve Nereden Geldi

CoreDNS, Go diliyle yazılmış, plugin tabanlı bir DNS sunucusudur. 2016 yılında Miek Gieben tarafından başlatılan proje, 2017’de Cloud Native Computing Foundation (CNCF) bünyesine girdi ve 2019’da “graduated” statüsüne yükseldi. Bu statü, projenin production-ready olduğunun resmi onayı sayılır.

Kubernetes 1.11 sürümüyle birlikte CoreDNS, kube-dns’in yerini alarak varsayılan DNS çözümleyici haline geldi. Ama CoreDNS sadece Kubernetes için değil, bağımsız bir DNS sunucusu olarak da kullanılabilen genel amaçlı bir çözüm.

Peki neden yeni bir DNS sunucusuna ihtiyaç duyuldu? BIND yıllardır var, dnsmasq var, Unbound var. Bunlar yetmiyor muydu?

Sorun şu ki geleneksel DNS sunucuları monolitik yapıda. Bir özelliği değiştirmek istediğinizde kaynak koduna giriyorsunuz, derliyorsunuz, test ediyorsunuz. Cloud native dünyasının dinamik yapısına bu yaklaşım tam olarak uymuyordu. CoreDNS’in getirdiği temel yenilik şu: her şey bir plugin.

Plugin Mimarisi: CoreDNS’i Farklı Kılan Şey

CoreDNS’in kalbi Caddy web sunucusundan alınan plugin zincirine dayanıyor. Bir DNS sorgusu geldiğinde, bu sorgu sırayla tanımlanmış plugin’lerden geçiyor. Her plugin sorguya bir şey yapabilir, yapamayabilir ya da bir sonraki plugin’e bırakabilir.

Bir sorgunun yolculuğunu şöyle düşünebilirsiniz:

DNS Sorgusu -> cache plugin -> forward plugin -> upstream DNS
                    |
               Cache'de varsa
               direkt cevapla

Bu yaklaşım inanılmaz bir esneklik sağlıyor. Örneğin şu anda Kubernetes’te CoreDNS’in tipik bir Corefile’ı şöyle görünür:

.: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 dosyayı ilk gördüğünüzde kafa karıştırıcı görünebilir ama aslında çok okunabilir bir yapı. errors, health, ready, kubernetes, prometheus… her biri bir plugin ve her biri belirli bir işlevi yerine getiriyor.

Corefile Sözdizimi

CoreDNS’in konfigürasyon dosyası Corefile adını alır. Temel sözdizimi şöyle:

ZONE:PORT {
    plugin1 [options]
    plugin2 [options]
    ...
}

Zone tanımı hangi DNS sorgularının bu blok tarafından ele alınacağını belirliyor. Birden fazla zone tanımlayabilirsiniz:

example.com:53 {
    file /etc/coredns/zones/example.com.db
    log
    errors
}

.:53 {
    forward . 8.8.8.8 8.8.4.4
    cache 300
    log
    errors
}

Burada example.com için yerel zone dosyası kullanılıyor, diğer tüm sorgular ise Google’ın DNS sunucularına iletiliyor. . notasyonu “her şey” anlamına geliyor.

Temel Plugin’lere Yakından Bakış

errors ve log Plugin’leri

.:53 {
    errors
    log
    forward . 1.1.1.1
}

errors plugin’i hata mesajlarını standart çıktıya yazar. log ise her DNS sorgusunu loglar. Production ortamında log plugin’ini dikkatli kullanın, yüksek trafikteki ortamlarda ciddi disk I/O oluşturabilir.

cache Plugin’i

Cache, CoreDNS’in en değerli özelliklerinden biri. TTL değerlerine göre yanıtları bellekte tutuyor:

.:53 {
    cache {
        success 9984 3600
        denial 9984 300
        prefetch 10 1m 10%
    }
    forward . 1.1.1.1
}
  • success 9984 3600: Başarılı yanıtlar için 9984 kayıt kapasitesi, maksimum 3600 saniye TTL
  • denial 9984 300: NXDOMAIN gibi olumsuz yanıtlar için 9984 kayıt, 300 saniye TTL
  • prefetch 10 1m 10%: Son 1 dakikada 10’dan fazla sorgulanmış kayıtları TTL dolmadan yenile

Production’da bu yazının başında anlattığım sorunu, cache konfigürasyonunu optimize ederek çözmüştük.

forward Plugin’i

Upstream DNS sunucularına yönlendirme için kullanılır:

.:53 {
    forward . 1.1.1.1 1.0.0.1 8.8.8.8 {
        max_concurrent 1000
        health_check 5s
        policy random
    }
}
  • max_concurrent 1000: Aynı anda maksimum 1000 eş zamanlı sorgu
  • health_check 5s: Upstream sunucuların sağlığını 5 saniyede bir kontrol et
  • policy random: Upstream sunucular arasında rastgele seçim yap (round_robin ve sequential alternatifleri de var)

kubernetes Plugin’i

Kubernetes ortamlarında servis keşfi için:

kubernetes cluster.local in-addr.arpa ip6.arpa {
    pods verified
    upstream
    fallthrough in-addr.arpa ip6.arpa
    ttl 30
    noendpoints
}
  • pods verified: Pod IP’lerini doğrulayarak çöz
  • fallthrough: Bu zone’da bulunamazsa bir sonraki plugin’e geç
  • ttl 30: Kubernetes kayıtları için 30 saniye TTL
  • noendpoints: Endpoint kayıtlarını yayımlama

CoreDNS Kurulumu

Bağımsız Kurulum (Binary)

# GitHub releases'tan en son versiyonu indir
wget https://github.com/coredns/coredns/releases/download/v1.11.1/coredns_1.11.1_linux_amd64.tgz

# Arşivi aç
tar -xzf coredns_1.11.1_linux_amd64.tgz

# Binary'yi uygun yere taşı
sudo mv coredns /usr/local/bin/

# Çalıştırma izni ver
sudo chmod +x /usr/local/bin/coredns

# Versiyon kontrolü
coredns --version

Systemd Servis Dosyası

sudo tee /etc/systemd/system/coredns.service > /dev/null <<EOF
[Unit]
Description=CoreDNS DNS server
Documentation=https://coredns.io
After=network.target

[Service]
PermissionsStartOnly=true
LimitNOFILE=1048576
LimitNPROC=512
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE
NoNewPrivileges=true
User=coredns
WorkingDirectory=/etc/coredns
ExecStart=/usr/local/bin/coredns -conf /etc/coredns/Corefile
ExecReload=/bin/kill -SIGUSR1 $MAINPID
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target
EOF

# Kullanıcı oluştur
sudo useradd -r -s /sbin/nologin coredns

# Konfigürasyon dizinini oluştur
sudo mkdir -p /etc/coredns

# Servisi aktif et
sudo systemctl daemon-reload
sudo systemctl enable coredns
sudo systemctl start coredns

Docker ile Hızlı Test

# Basit bir Corefile oluştur
cat > /tmp/Corefile << 'EOF'
.:53 {
    forward . 1.1.1.1 8.8.8.8
    cache 300
    log
    errors
    health :8080
}
EOF

# Docker container başlat
docker run -d 
  --name coredns-test 
  -p 53:53/udp 
  -p 53:53/tcp 
  -p 8080:8080 
  -v /tmp/Corefile:/Corefile 
  coredns/coredns:1.11.1 
  -conf /Corefile

# Test sorgusu
dig @localhost google.com

# Health check
curl http://localhost:8080/health

Gerçek Dünya Senaryosu: Split-Horizon DNS

Split-horizon DNS, aynı domain adını iç ve dış ağlarda farklı IP’lerle çözümleme ihtiyacında kullanılır. Şirkette sıkça karşılaşılan bir senaryo:

# /etc/coredns/Corefile

# İç ağ için company.local zone'u
company.local:53 {
    file /etc/coredns/zones/company.local.db
    log
    errors
}

# Dış DNS'e ihtiyaç duyan internal domainler için
aws.internal:53 {
    forward . 169.254.169.253
    cache 60
}

# Genel internet trafiği
.:53 {
    forward . 1.1.1.1 1.0.0.1 {
        policy round_robin
        health_check 10s
    }
    cache {
        success 10000 3600
        denial 1000 300
    }
    errors
    log . {
        class error
    }
}

Zone dosyası da şöyle olabilir:

# /etc/coredns/zones/company.local.db
$ORIGIN company.local.
$TTL 300

@       IN SOA  ns1.company.local. admin.company.local. (
                2024010101 ; serial
                3600       ; refresh
                900        ; retry
                604800     ; expire
                300 )      ; minimum TTL

        IN NS   ns1.company.local.

ns1     IN A    192.168.1.10
gitlab  IN A    192.168.1.50
nexus   IN A    192.168.1.51
jenkins IN A    192.168.1.52
*.apps  IN A    192.168.1.100

Kubernetes’te CoreDNS Troubleshooting

Kubernetes’te DNS sorunları yaşıyorsanız, ilk bakmanız gereken yer CoreDNS logları:

# CoreDNS pod'larını bul
kubectl get pods -n kube-system -l k8s-app=kube-dns

# Log'lara bak
kubectl logs -n kube-system -l k8s-app=kube-dns --tail=100

# CoreDNS ConfigMap'ini incele
kubectl get configmap coredns -n kube-system -o yaml

# DNS test pod'u başlat
kubectl run dns-test --image=busybox:1.35 --rm -it --restart=Never -- nslookup kubernetes.default

# CoreDNS metriklerini kontrol et
kubectl port-forward -n kube-system svc/kube-dns 9153:9153
curl http://localhost:9153/metrics | grep coredns_dns_requests_total

Sık karşılaşılan bir sorun: pod’lar dış adresleri çözemiyorsa ama cluster içi servisler çalışıyorsa, genellikle forward plugin’i veya node’daki /etc/resolv.conf konfigürasyonuyla ilgili bir problem vardır.

Prometheus ile Monitoring

CoreDNS, prometheus plugin’i sayesinde zengin metrikler sunuyor:

.:53 {
    prometheus :9153
    # diğer plugin'ler...
}

Dikkat etmeniz gereken önemli metrikler:

  • coredns_dns_requests_total: Toplam sorgu sayısı (type ve rcode’a göre ayrılmış)
  • coredns_dns_responses_total: Toplam yanıt sayısı
  • coredns_cache_hits_total: Cache isabet sayısı
  • coredns_cache_misses_total: Cache ıskalamalar
  • coredns_forward_requests_total: Upstream’e iletilen sorgular
  • coredns_forward_request_duration_seconds: Upstream yanıt süreleri
  • coredns_panics_total: Bu metriğin sıfır olmasını istiyorsunuz, sıfırdan büyükse ciddi bir sorun var

Cache hit ratio şu şekilde hesaplanabilir: cache_hits / (cache_hits + cache_misses). Genel kural olarak bu oranın yüzde 80’in üzerinde olması istenir, altındaysa ya TTL değerlerinizi artırmanız ya da cache boyutunu büyütmenüz gerekiyor.

Performans Tuning

Yüksek yüklü ortamlarda CoreDNS’i optimize etmek için:

.:53 {
    cache {
        success 65536 3600
        denial 8192 300
        prefetch 10 60s 10%
    }
    
    forward . 1.1.1.1 1.0.0.1 {
        max_concurrent 2000
        health_check 5s
        expire 10s
    }
    
    # UDP buffer boyutunu artır
    bufsize 4096
    
    errors
}

Ayrıca sistem tarafında da birkaç ayar yapmanızı öneririm:

# UDP buffer boyutlarını artır
sysctl -w net.core.rmem_max=134217728
sysctl -w net.core.wmem_max=134217728

# Kalıcı hale getir
cat >> /etc/sysctl.conf << EOF
net.core.rmem_max=134217728
net.core.wmem_max=134217728
EOF

CoreDNS vs BIND vs Unbound: Hangisini Seçmeli

Bu karşılaştırmayı tablo yerine somut durumlarla anlatmak daha doğru olur.

BIND seçin eğer:

  • Çok karmaşık zone yönetimine ihtiyacınız varsa
  • DNSSEC’i derinlemesine kullanmanız gerekiyorsa
  • Ekibinizde BIND’a hakim kişiler varsa ve mevcut altyapınız BIND üzerine kurulu ise

Unbound seçin eğer:

  • Sadece recursive resolver ihtiyacınız varsa
  • Yüksek performanslı caching önceliğinizse
  • Minimal footprint istiyorsanız

CoreDNS seçin eğer:

  • Kubernetes veya container ortamında çalışıyorsanız, burada tercih değil zorunluluk
  • Özel plugin geliştirme ihtiyacınız varsa
  • Dynamic konfigürasyon değişikliklerine ihtiyaç duyuyorsanız
  • Modern cloud native stack kuruyorsanız

Kendi tecrübelerimden söylüyorum: on-premise ortamlarda hala BIND kullanıyorum, ama Kubernetes cluster’larım için CoreDNS’ten başka bir seçenek düşünmüyorum bile.

Sonuç

CoreDNS, “DNS sunucusu” kavramını yeniden tanımlayan bir proje. Plugin mimarisi sayesinde ihtiyacınıza göre şekillendirilebiliyor, Kubernetes ile entegrasyonu birinci sınıf, monitoring desteği eksiksiz.

Ama şunu da söylemek gerekir: CoreDNS her ortam için doğru seçim değil. Karmaşık zone yönetimi gerektiren enterprise ortamlarda BIND hala daha yetenekli. CoreDNS’in gücü cloud native ekosistemde, dynamic service discovery’de ve Go dilinin getirdiği performans ile düşük bellek tüketiminde gizli.

Eğer Kubernetes kullanıyorsanız, CoreDNS zaten sisteminizde çalışıyor. Onu anlamak ve doğru konfigüre etmek, cluster’ınızın güvenilirliği için kritik. Cache ayarlarını optimize edin, Prometheus ile izleyin, sorun çıkmadan önce logları takip edin. DNS sorunları çoğunlukla başka bir şeyin belirtisi gibi görünür ama kaynağa inince DNS konfigürasyon hataları çıkar.

Bir sonraki yazıda CoreDNS plugin geliştirmeye bakacağız; kendi custom plugin’inizi Go ile nasıl yazarsınız, onu ele alacağız.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir