Aller au contenu

Bonnes pratiques

Haute disponibilité

Minimum 2 serveurs par zone

Un service DNS unique constitue un SPOF (Single Point of Failure). Toute panne DNS paralyse l'ensemble de l'infrastructure.

Configuration Disponibilité Commentaire
1 serveur ~99.5% Inacceptable pour un service critique
2 serveurs (actif/actif) ~99.95% Minimum requis
3 serveurs (multi-AZ) ~99.99% Recommande pour la production

Architecture HA avec load balancer

graph TD
    LB["Load Balancer<br/>(HAProxy / MetalLB)<br/>VIP: 10.30.2.1"] --> C1["coredns-1<br/>AZ-a<br/>10.30.2.10"]
    LB --> C2["coredns-2<br/>AZ-b<br/>10.30.2.11"]
    LB --> C3["coredns-3<br/>AZ-c<br/>10.30.2.12"]

Health check pour le load balancer

# HAProxy configuration
frontend dns_frontend
    bind *:53
    mode tcp
    default_backend dns_servers

backend dns_servers
    mode tcp
    balance roundrobin
    option tcp-check
    server coredns-1 10.30.2.10:53 check inter 5s fall 3 rise 2
    server coredns-2 10.30.2.11:53 check inter 5s fall 3 rise 2
    server coredns-3 10.30.2.12:53 check inter 5s fall 3 rise 2

Synchronisation des zones

Pour que tous les serveurs autoritatifs servent les mêmes enregistrements :

Méthode Avantage Inconvénient
Backend partage (etcd) Cohérence forte, temps reel Necessite etcd en HA
Fichiers de zone + Git + CI/CD Auditabilite, rollback Latence de propagation
Transfert de zone (AXFR/IXFR) Standard DNS, compatible BIND Configuration supplémentaire

Recommandation

Pour CoreDNS, le backend etcd offre la meilleure cohérence. Si etcd n'est pas disponible, gérer les fichiers de zone dans Git et les déployer via CI/CD sur tous les serveurs.

Caching

TTL adaptés par type de service

; Services stables (changent rarement)
$TTL 3600
ns1         IN  A   10.30.2.10
ns2         IN  A   10.30.2.11

; Services avec failover (changent lors des basculements)
$TTL 120
db-master   IN  A   10.30.2.50
db-replica  IN  A   10.30.2.51

; Services dynamiques (pods Kubernetes, auto-scaling)
$TTL 30
worker      IN  A   10.30.3.10
worker      IN  A   10.30.3.11
worker      IN  A   10.30.3.12

Configuration du cache CoreDNS

# Corefile — cache avec tuning
production.internal.company.io {
    file /etc/coredns/zones/db.production.internal
    cache {
        success 9984 3600   # Cache positif : max 9984 entrees, 3600s max
        denial 9984 300     # Cache negatif : max 9984 entrees, 300s max
        prefetch 10 1h 20%  # Prefetch si >10 requetes et TTL restant <20%
    }
    log
    errors
}

Metriques de cache

Metrique Seuil sain Action si anormal
Cache hit ratio > 70% Augmenter TTL ou taille du cache
Cache miss ratio < 30% Normal si beaucoup de noms uniques
Cache evictions Stable Augmenter la taille du cache
Prefetch count > 0 Confirme que le prefetch fonctionne

Monitoring

Metriques Prometheus essentielles

CoreDNS expose nativement des metriques Prometheus sur le port 9153 :

Metrique Description Alerte si
coredns_dns_requests_total Nombre total de requêtes Chute brutale (panne client)
coredns_dns_responses_total Nombre total de réponses Divergence avec les requêtes
coredns_dns_request_duration_seconds Latence de résolution p99 > 100 ms
coredns_dns_responses_total{rcode="SERVFAIL"} Erreurs serveur > 1% du trafic
coredns_dns_responses_total{rcode="NXDOMAIN"} Noms inexistants Augmentation soudaine
coredns_cache_hits_total Hits de cache Cache hit ratio < 50%
coredns_cache_misses_total Misses de cache Augmentation soudaine
coredns_forward_healthcheck_failures_total Echecs de health check upstream > 0 (upstream en panne)

Dashboard Grafana

Les metriques cles a afficher :

graph TD
    subgraph Dashboard["DNS Dashboard"]
        subgraph Row1[" "]
            Req["Requetes/s<br/>1,234"]
            Lat50["Latence p50<br/>2.3 ms"]
            CacheHit["Cache Hit Ratio<br/>82.4%"]
        end
        subgraph Row2[" "]
            Fail["SERVFAIL/s<br/>0.1"]
            Lat99["Latence p99<br/>12.1 ms"]
            Health["Upstream Health<br/>3/3 healthy"]
        end
        Rcode["Requetes par rcode (time series)<br/>NOERROR | NXDOMAIN | SERVFAIL"]
        Heatmap["Latence par zone (heatmap)<br/>production.internal 2.1ms<br/>enterprise.internal 1.8ms<br/>build.internal 1.5ms<br/>external (forward) 15.2ms"]
    end

Regles d'alerte

# Prometheus alerting rules
groups:
  - name: coredns
    rules:
      - alert: CoreDNSDown
        expr: up{job="coredns"} == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "CoreDNS {{ $labels.instance }} est down"
          runbook: "https://wiki.internal/runbooks/dns-down"

      - alert: CoreDNSHighLatency
        expr: histogram_quantile(0.99, rate(coredns_dns_request_duration_seconds_bucket[5m])) > 0.1
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Latence DNS p99 > 100ms sur {{ $labels.instance }}"

      - alert: CoreDNSHighSERVFAIL
        expr: >
          rate(coredns_dns_responses_total{rcode="SERVFAIL"}[5m])
          / rate(coredns_dns_responses_total[5m]) > 0.01
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Taux de SERVFAIL > 1% sur {{ $labels.instance }}"

      - alert: CoreDNSForwardUnhealthy
        expr: coredns_forward_healthcheck_failures_total > 0
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "Upstream DNS en echec sur {{ $labels.instance }}"

Troubleshooting

Outils essentiels

dig — requête DNS détaillée

# Requete A simple
dig @10.30.2.10 app-api.production.internal.company.io A

# Requete avec details (flags, TTL, autorite)
dig @10.30.2.10 app-api.production.internal.company.io A +noall +answer +authority

# Requete SRV
dig @10.30.2.10 _ldaps._tcp.enterprise.internal.company.io SRV +short

# Tracer la resolution complete
dig @10.30.2.10 app-api.production.internal.company.io A +trace

# Requete en TCP (si UDP pose probleme)
dig @10.30.2.10 app-api.production.internal.company.io A +tcp

# Resolution inverse
dig @10.30.2.10 -x 10.30.1.10

nslookup — vérification rapide

# Resolution simple
nslookup app-api.production.internal.company.io 10.30.2.10

# Requete specifique (SRV)
nslookup -type=SRV _ldaps._tcp.enterprise.internal.company.io 10.30.2.10

tcpdump — capture du trafic DNS

# Capturer le trafic DNS sur l'interface reseau
tcpdump -i eth0 port 53 -nn -v

# Capturer et sauvegarder pour analyse dans Wireshark
tcpdump -i eth0 port 53 -w /tmp/dns-capture.pcap

# Filtrer par IP source
tcpdump -i eth0 port 53 and src host 10.30.1.42 -nn

CoreDNS debug logging

# Corefile — activer le debug logging
. {
    debug             # Active le debug sur toutes les zones
    log . {
        class all     # Journaliser toutes les classes de requetes
    }
    errors
    forward . 1.1.1.1
}

Problèmes courants

Symptome Cause probable Diagnostic Solution
SERVFAIL Zone mal configuree ou upstream injoignable dig +trace, logs CoreDNS Vérifier le fichier de zone, tester l'upstream
NXDOMAIN inattendu Enregistrement manquant ou search domain incorrect dig FQDN. (avec point final) Ajouter l'enregistrement ou corriger le search domain
Latence elevee Cache froid ou upstream lent Metriques Prometheus, dig +stats Vérifier le cache hit ratio, tester l'upstream
Résolution intermittente Un des serveurs en panne dig @serveur1, dig @serveur2 Vérifier le health du serveur defaillant
Pods K8s ne résolvent pas CoreDNS kube-system down kubectl get pods -n kube-system Redemarrer les pods CoreDNS

Migration de BIND vers CoreDNS

Stratégie progressive

La migration s'effectue zone par zone, sans interruption de service :

graph TD
    subgraph P1["Phase 1 — Coexistence"]
        C1["Clients"] --> CD1["CoreDNS"] -->|forward| BIND1["BIND (autoritatif)"]
    end
    subgraph P2["Phase 2 — Migration progressive"]
        C2["Clients"] --> CD2["CoreDNS"]
        CD2 -->|file| Zone["zone production (local)"]
        CD2 -->|forward| BIND2["BIND (zones restantes)"]
    end
    subgraph P3["Phase 3 — CoreDNS seul"]
        C3["Clients"] --> CD3["CoreDNS"] -->|file| All["toutes les zones"]
    end
    P1 --> P2 --> P3

Étapes détaillées

Phase 1 : Déployer CoreDNS en forward

# Corefile initial — tout forwarder vers BIND
. {
    forward . 10.30.2.50 10.30.2.51 {
        # BIND existant
        health_check 10s
    }
    cache 300
    prometheus :9153
    log
    errors
}

Phase 2 : Migrer une zone

# Exporter la zone depuis BIND
dig @10.30.2.50 production.internal.company.io AXFR > db.production.internal

# Convertir au format CoreDNS file (identique au format BIND)
# Le fichier de zone BIND est directement utilisable par CoreDNS
cp db.production.internal /opt/coredns/zones/
# Corefile — zone production en local, reste en forward
production.internal.company.io {
    file /etc/coredns/zones/db.production.internal
    cache 300
    log
    errors
}

. {
    forward . 10.30.2.50 10.30.2.51
    cache 300
    log
    errors
}

Phase 3 : Decommissionner BIND

Une fois toutes les zones migrees et validees (minimum 2 semaines de fonctionnement stable) :

  1. Retirer le forward vers BIND dans le Corefile
  2. Garder BIND en lecture seule pendant 1 semaine (sécurité)
  3. Eteindre BIND
  4. Supprimer les références a BIND dans l'infrastructure

Points d'attention pour la migration

  • Tester chaque zone migree avec dig depuis plusieurs clients
  • Comparer les réponses CoreDNS vs BIND pour chaque enregistrement
  • Surveiller les metriques SERVFAIL et NXDOMAIN apres chaque migration
  • Prevoir un rollback rapide (re-activer le forward vers BIND)
  • Prevenir les équipes de la migration et du calendrier

Script de validation post-migration

#!/bin/bash
# validate-dns-migration.sh
# Compare les reponses entre l'ancien BIND et le nouveau CoreDNS

BIND_SERVER="10.30.2.50"
COREDNS_SERVER="10.30.2.10"
ZONE="production.internal.company.io"
RECORDS=(
  "app-api"
  "db-master"
  "db-replica"
  "cache-redis"
  "mq-rabbit"
)

echo "=== Validation migration DNS : $ZONE ==="
ERRORS=0

for record in "${RECORDS[@]}"; do
  FQDN="${record}.${ZONE}"
  BIND_RESULT=$(dig @${BIND_SERVER} ${FQDN} A +short | sort)
  COREDNS_RESULT=$(dig @${COREDNS_SERVER} ${FQDN} A +short | sort)

  if [ "$BIND_RESULT" = "$COREDNS_RESULT" ]; then
    echo "[OK] ${FQDN} : ${COREDNS_RESULT}"
  else
    echo "[ERREUR] ${FQDN}"
    echo "  BIND    : ${BIND_RESULT}"
    echo "  CoreDNS : ${COREDNS_RESULT}"
    ERRORS=$((ERRORS + 1))
  fi
done

echo ""
if [ $ERRORS -eq 0 ]; then
  echo "Validation reussie : ${#RECORDS[@]} enregistrements OK"
else
  echo "ECHEC : ${ERRORS} divergences detectees"
  exit 1
fi