Skip to content

DNS Debugging và Performance Tuning

Tại Sao DNS Debug Khó

DNS failure thường không hiển thị rõ ràng. Application thấy "connection timeout" hay "connection refused", không thấy "DNS lookup failed". Hệ quả là developer đi debug network connectivity trong khi vấn đề thực sự ở DNS layer.

Thêm vào đó, DNS failures trong Kubernetes có nhiều lớp và nguồn gốc:

  • Pod layer: /etc/resolv.conf sai, dnsPolicy sai
  • Cluster DNS layer: CoreDNS overloaded, pod crash, configuration error
  • Node layer: conntrack exhaustion, network namespace issues
  • Cloud DNS layer: zone sync delay, peering misconfiguration
  • Upstream layer: external DNS unreachable, firewall blocking

Debug hiệu quả cần hiểu layers và test từng layer theo thứ tự — không nhảy vào giả định nguyên nhân.

Debug Workflow — Từ Pod Ra Ngoài

Layer 1: Verify /etc/resolv.conf

Bước đầu tiên luôn là kiểm tra cấu hình DNS của Pod:

bash
# Xem resolv.conf
kubectl exec -it <pod-name> -- cat /etc/resolv.conf

# Expected output với NodeLocal DNSCache:
# nameserver 169.254.20.10
# search default.svc.cluster.local svc.cluster.local cluster.local
# options ndots:5

# Expected output với kube-dns (không có NodeLocal DNSCache):
# nameserver 10.96.0.10
# search default.svc.cluster.local svc.cluster.local cluster.local
# options ndots:5

# Expected output với Cloud DNS (không có NodeLocal DNSCache):
# nameserver 169.254.169.254
# ...

Nếu nameserver trỏ sai (ví dụ: 0.0.0.0 hoặc empty), dnsPolicy bị misconfigure hoặc kubelet có vấn đề.

Layer 2: Test Basic DNS Resolution

bash
# Chạy busybox trong cluster để test DNS
kubectl run dns-debug --image=busybox:1.36 \
    --rm -it --restart=Never -- sh

# Trong busybox shell:
# Test cluster-internal DNS
nslookup kubernetes.default.svc.cluster.local

# Test external DNS
nslookup google.com

# Test cross-namespace (phải specify namespace)
nslookup payment-service.backend.svc.cluster.local

# Test với FQDN (trailing dot - bypass search domains)
nslookup payment-service.backend.svc.cluster.local.

Nếu kubernetes.default.svc.cluster.local resolve được nhưng external domain không, forward chain bị broken. Nếu cả hai fail, CoreDNS/Cloud DNS unreachable.

Layer 3: Trace Toàn Bộ Query Path

bash
# dig cho phép xem chi tiết hơn nslookup
# -x: verbose, +search: show expanded search domains
kubectl exec -it <pod> -- dig +search payment-service

# Xem tất cả DNS queries bao gồm failed attempts
kubectl exec -it <pod> -- dig -debug payment-service 2>&1

# Test time DNS lookup mất bao lâu
kubectl exec -it <pod> -- time nslookup payment-service.backend.svc.cluster.local

# Verify ndots behavior
# Tên có ít dấu chấm sẽ trigger search domain expansion
kubectl exec -it <pod> -- dig api.example.com +stats 2>&1 | grep "Query time"
kubectl exec -it <pod> -- dig api.example.com. +stats 2>&1 | grep "Query time"
# Cái có trailing dot (FQDN) sẽ nhanh hơn đáng kể

Layer 4: Kiểm Tra CoreDNS Pods

bash
# Verify CoreDNS pods running
kubectl get pods -n kube-system -l k8s-app=kube-dns

# Với Cloud DNS for GKE, check controller
kubectl get pods -n kube-system | grep dns

# Xem logs của CoreDNS
kubectl logs -n kube-system -l k8s-app=kube-dns --tail=100

# Filter errors
kubectl logs -n kube-system -l k8s-app=kube-dns | grep -E "REFUSED|SERVFAIL|ERROR"

# Xem NodeLocal DNSCache pods
kubectl get pods -n kube-system -l k8s-app=node-local-dns
kubectl logs -n kube-system -l k8s-app=node-local-dns --tail=100

Layer 5: CoreDNS Metrics

CoreDNS expose Prometheus metrics ở port 9153. Quan trọng nhất:

bash
# Port-forward đến CoreDNS pod
kubectl port-forward -n kube-system deployment/coredns 9153:9153

# Xem metrics
curl -s http://localhost:9153/metrics | grep -E "coredns_(dns_requests_total|dns_responses_total|forward_requests_total|cache_hits_total|dns_request_duration)"

Metrics quan trọng:

MetricÝ nghĩaAlert khi
coredns_dns_requests_totalTổng queries nhận đượcTăng đột biến
coredns_dns_responses_total{rcode="NXDOMAIN"}NXDOMAIN responses>20% total requests
coredns_forward_requests_totalQueries forward đến upstreamTăng cao bất thường
coredns_dns_request_duration_seconds_p99P99 latency>100ms
coredns_cache_hits_totalCache hitsHit rate thấp (<80%)
coredns_forward_requests_duration_seconds_p99Upstream latency>200ms

Layer 6: Conntrack Table Status (Node Level)

Đây là layer thường bị bỏ qua nhưng là nguyên nhân của DNS drops:

bash
# SSH vào node (hoặc dùng node debugging pod)
kubectl debug node/<node-name> -it --image=ubuntu

# Kiểm tra conntrack table size và usage
cat /proc/sys/net/netfilter/nf_conntrack_max     # Max entries
cat /proc/sys/net/netfilter/nf_conntrack_count   # Current entries

# Nếu count gần max → conntrack exhaustion đang xảy ra
# Xem conntrack entries related to DNS (port 53)
conntrack -L | grep "dport=53" | wc -l

# Xem conntrack full stats
conntrack -S

Nếu nf_conntrack_count gần nf_conntrack_max, đây là nguyên nhân DNS drops. Solution: enable NodeLocal DNSCache, hoặc increase table size (short-term fix).

Playbook Chẩn Đoán Theo Symptom

Symptom: "DNS lookup timeout" sau 5 giây

Nguyên nhân phổ biến nhất: Conntrack table exhaustion hoặc CoreDNS pods overloaded.

bash
# Step 1: Check conntrack (trên node chạy pod bị ảnh hưởng)
kubectl debug node/<node> -it --image=busybox -- cat /proc/sys/net/netfilter/nf_conntrack_count
kubectl debug node/<node> -it --image=busybox -- cat /proc/sys/net/netfilter/nf_conntrack_max

# Step 2: Check CoreDNS latency metrics
kubectl port-forward -n kube-system deployment/coredns 9153:9153
curl http://localhost:9153/metrics | grep duration

# Step 3: Check CoreDNS pod resource usage
kubectl top pods -n kube-system -l k8s-app=kube-dns

# Solution path:
# - Conntrack full → Enable NodeLocal DNSCache
# - CoreDNS overloaded → Scale CoreDNS Deployment, check NXDOMAIN rate

Symptom: High NXDOMAIN Rate — ndots:5 Lookup Storm

bash
# Verify NXDOMAIN rate
kubectl port-forward -n kube-system deployment/coredns 9153:9153
curl -s http://localhost:9153/metrics | grep 'coredns_dns_responses_total{rcode="NXDOMAIN"}'

# Xác định pod nào đang generate nhiều NXDOMAIN
# CoreDNS logs với plugin errors:
kubectl logs -n kube-system -l k8s-app=kube-dns | grep NXDOMAIN | awk '{print $4}' | sort | uniq -c | sort -rn | head -20

# Verify ndots trong pod bị suspect
kubectl exec -it <suspect-pod> -- cat /etc/resolv.conf | grep ndots

# Solution: Override ndots cho pods với nhiều external calls

Sau khi identify pods với high external DNS traffic, override ndots:

yaml
spec:
  dnsConfig:
    options:
      - name: ndots
        value: "2"  # Giảm từ 5 xuống 2

Với ndots:2, api.example.com (2 dấu chấm) được query như FQDN trực tiếp — không có search domain expansion.

Symptom: Cross-Namespace Service Không Resolve

bash
# Test từ pod nguồn
kubectl exec -it <pod-in-namespace-a> -- \
    nslookup service-name.namespace-b.svc.cluster.local

# Common mistake: thiếu namespace trong service name
kubectl exec -it <pod> -- nslookup service-name  # Fail vì search domains không cover namespace-b
kubectl exec -it <pod> -- nslookup service-name.namespace-b  # Đúng
kubectl exec -it <pod> -- nslookup service-name.namespace-b.svc.cluster.local  # FQDN đầy đủ

Symptom: DNS Resolution Chậm Cho External Hostnames

bash
# Đo latency với và không có trailing dot
kubectl exec -it <pod> -- sh -c 'time nslookup api.external.com'
kubectl exec -it <pod> -- sh -c 'time nslookup api.external.com.'

# Nếu có trailing dot nhanh hơn đáng kể → ndots:5 lookup storm
# Kiểm tra số queries thực tế với strace
kubectl exec -it <pod> -- strace -e trace=sendto,recvfrom \
    nslookup api.external.com 2>&1 | grep "53" | wc -l

Performance Tuning

Override ndots Per-Pod/Per-Deployment

Cách tốt nhất để reduce NXDOMAIN storm cho services với nhiều external calls:

yaml
# Deployment với nhiều external HTTP calls
apiVersion: apps/v1
kind: Deployment
spec:
  template:
    spec:
      dnsConfig:
        options:
          - name: ndots
            value: "1"  # Hoặc "2" tùy pattern
      containers:
      - name: app
        ...

Khi nào dùng ndots gì:

  • ndots:5 (mặc định): Phù hợp cho services chủ yếu gọi cluster-internal names
  • ndots:2: Phù hợp cho services mix giữa internal và external (short internal names + external với ≥2 dots)
  • ndots:1: Phù hợp cho services gần như chỉ gọi external services

Lưu ý: Nếu giảm ndots quá thấp, short internal service names có thể không resolve được. Test kỹ trước khi rollout.

CoreDNS Cache Tuning

CoreDNS cache plugin trong Corefile:

cache {
    success 9984   # Max entries cho successful responses (mặc định: unbounded)
    denial 9984    # Max entries cho NXDOMAIN/NODATA responses
    ttl 30         # Max TTL cho cached responses
    servfail 5     # TTL cho SERVFAIL responses
}

Tăng successdenial cache size → ít queries đến upstream hơn → better performance. Trade-off: memory usage tăng.

Với 9984 entries mỗi loại, memory usage ~30-40MB per CoreDNS pod. Với 100,000 entries: ~300MB.

Scale CoreDNS Horizontally

Mặc định GKE tự scale CoreDNS nhưng có thể override:

bash
# Xem CoreDNS Deployment
kubectl get deployment coredns -n kube-system

# Không nên manual scale trong GKE — GKE tự manage
# Nhưng có thể check autoscaling config
kubectl get configmap coredns-autoscaler -n kube-system -o yaml

GKE dùng DNS Autoscaling dựa trên số nodes. Cấu hình trong ConfigMap coredns-autoscaler.

NodeLocal DNSCache Cache Sizing

bash
# Xem config của NodeLocal DNS
kubectl get configmap -n kube-system node-local-dns -o yaml

# Corefile trong ConfigMap có cache settings
# Mặc định: cache 30 (30 giây max TTL, unbounded entries)
# Để giới hạn entries:
#   cache 30 {
#       success 9984
#       denial 9984
#   }

Connection Pooling Cho Upstream

Forward plugin max_concurrent ảnh hưởng đến throughput upstream:

forward . 169.254.169.254 {
    max_concurrent 2000  # Tăng từ 1000 mặc định
    expire 10s           # Expire idle connections sau 10s
    health_check 5s      # Health check upstream interval
}

max_concurrent cao hơn cho phép nhiều concurrent upstream queries nhưng tốn memory và FDs hơn.

CoreDNS Logs — Enable Query Logging

Mặc định CoreDNS không log từng DNS query (quá nhiều). Khi debug, có thể bật tạm thời:

bash
# Edit ConfigMap để thêm log plugin
kubectl edit configmap coredns -n kube-system

# Thêm plugin 'log' vào server block:
# .:53 {
#     log  # Log tất cả queries
#     errors
#     ...
# }

# CẢNH BÁO: log plugin rất verbose trong production cluster
# Chỉ bật tạm thời để debug, disable ngay sau khi xong

Log output mẫu:

[INFO] 10.0.1.5:47832 - 12345 "A IN payment-service.backend.svc.cluster.local. udp 62 false 512" NOERROR qr,aa,rd 106 0.000234s
[INFO] 10.0.1.5:47833 - 12346 "A IN api.external.com. udp 45 false 512" NOERROR qr,rd,ra 89 0.002341s

Format: [client-ip] - [query-id] [type] [name] [transport] [RCODE] [flags] [response-size] [latency]

Checklist Verification Sau DNS Config Changes

Sau mọi thay đổi DNS configuration (thêm stub domain, thay đổi upstream, update Cloud DNS scope), verify theo thứ tự:

bash
#!/bin/bash
# DNS health check script

echo "1. CoreDNS pods running?"
kubectl get pods -n kube-system -l k8s-app=kube-dns

echo "2. NodeLocal DNS pods running?"
kubectl get pods -n kube-system -l k8s-app=node-local-dns

echo "3. Cluster-internal DNS working?"
kubectl run dns-check --image=busybox:1.36 --rm -it --restart=Never -- \
    nslookup kubernetes.default.svc.cluster.local

echo "4. External DNS working?"
kubectl run dns-check --image=busybox:1.36 --rm -it --restart=Never -- \
    nslookup google.com

echo "5. NXDOMAIN rate check (< 5% threshold)?"
kubectl port-forward -n kube-system deployment/coredns 9153:9153 &
sleep 2
TOTAL=$(curl -s http://localhost:9153/metrics | grep 'coredns_dns_requests_total' | awk '{sum+=$2} END {print sum}')
NXDOMAIN=$(curl -s http://localhost:9153/metrics | grep 'NXDOMAIN' | awk '{sum+=$2} END {print sum}')
echo "NXDOMAIN rate: $(echo "scale=2; $NXDOMAIN * 100 / $TOTAL" | bc)%"
kill %1  # Kill port-forward

References