CoreDNS trong GKE — Plugin Chain và Corefile
Tại Sao Cần Hiểu CoreDNS
CoreDNS là DNS server mặc định trong Kubernetes từ phiên bản 1.13, thay thế kube-dns. Trong GKE Standard clusters, CoreDNS chạy như một Deployment trong kube-system namespace, được Google quản lý — bạn không cần (và thường không nên) can thiệp vào cách nó chạy. Tuy nhiên, hiểu cơ chế bên trong CoreDNS là cần thiết khi:
- Debug tại sao một DNS query không được resolve đúng
- Cần cấu hình stub domains hoặc custom upstream resolvers
- Phân tích tại sao CoreDNS đang bị overload (metrics spike)
- Hiểu tại sao NodeLocal DNSCache và Cloud DNS for GKE thay đổi behavior
Internal Model — Plugin Chain Architecture
CoreDNS Là Gì Về Mặt Kỹ Thuật
CoreDNS không phải DNS server truyền thống với cơ sở dữ liệu DNS cố định. Nó là một plugin pipeline processor: mỗi DNS query đi qua một chuỗi plugins được cấu hình theo thứ tự, mỗi plugin có thể xử lý query, bổ sung thông tin, forward đến nơi khác, hoặc pass sang plugin tiếp theo.
Kiến trúc này giống middleware chain trong web framework: Request → Plugin1 → Plugin2 → Plugin3 → Response. Plugin có thể:
- Trả về response ngay (cache hit, kubernetes lookup thành công)
- Forward query đến upstream DNS server (forward plugin)
- Sửa đổi query trước khi pass tiếp (rewrite plugin)
- Pass sang plugin tiếp theo (không xử lý được)
Corefile — Ngôn Ngữ Cấu Hình
CoreDNS đọc cấu hình từ file gọi là Corefile, được lưu trong ConfigMap coredns ở namespace kube-system. Mỗi server block trong Corefile xác định một tập hợp zones mà CoreDNS lắng nghe và danh sách plugins xử lý.
<zone> [<port>] {
<plugin-1> [args...]
<plugin-2> [args...]
...
}CoreDNS xử lý query theo thứ tự: tìm server block khớp với zone của query, rồi chạy từng plugin theo thứ tự trong block.
Corefile Mặc Định Trong GKE Standard
Trên GKE Standard cluster, ConfigMap coredns chứa Corefile tương tự:
.: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
}Phân tích từng plugin theo thứ tự chạy:
errors: Log DNS errors ra stderr. Không block query, chỉ log.
health: Cung cấp HTTP endpoint /health trên port 8080. GKE dùng endpoint này cho liveness probe của CoreDNS pods. lameduck 5s cho phép CoreDNS draining connections 5 giây trước khi unhealthy — tránh in-flight queries bị drop khi pod restart.
ready: Cung cấp HTTP endpoint /ready trên port 8181. Plugin này chỉ trả về 200 OK khi tất cả plugins đã sẵn sàng (bao gồm kubernetes plugin đã sync xong với API server). Quan trọng: CoreDNS pods không được đưa vào LB pool cho đến khi readiness probe pass.
kubernetes: Plugin quan trọng nhất — xử lý DNS cho cluster DNS zone (cluster.local). Plugin này:
- Connect đến Kubernetes API server để watch Services, Endpoints, Pods
- Build in-memory cache của cluster DNS records
- Trả lời queries về
*.cluster.localtừ cache này pods insecure: generate A records cho Pod IPs (dạngpod-ip.namespace.pod.cluster.local)fallthrough in-addr.arpa ip6.arpa: nếu không resolve được PTR records, pass sang plugin tiếp theo
prometheus: Export metrics cho Prometheus scraping trên port 9153. Các metrics quan trọng: coredns_dns_requests_total, coredns_dns_responses_total, coredns_forward_requests_total, coredns_cache_hits_total.
forward . /etc/resolv.conf: Forward các queries không thuộc cluster.local đến upstream DNS servers (đọc từ /etc/resolv.conf của CoreDNS pod — chính là DNS của node). Đây là điểm exit cho external DNS queries. max_concurrent 1000 giới hạn số concurrent upstream queries.
cache 30: Cache DNS responses với TTL tối đa 30 giây. Giảm số queries đến upstream và Kubernetes API. Lưu ý: cache plugin trong Corefile là cache sau khi kubernetes plugin xử lý — cache cluster DNS responses.
loop: Phát hiện DNS forwarding loops. Nếu CoreDNS nhận lại query mà nó đã forward đi, nó crash intentionally thay vì loop vô hạn.
reload: Tự động reload Corefile khi ConfigMap thay đổi. Không cần restart pod.
loadbalance: Round-robin A, AAAA, MX records trong response. Đảm bảo client không luôn kết nối đến cùng một endpoint.
Query Processing Flow
Khi một Pod query payment-service.payment.svc.cluster.local:
- errors plugin: Đăng ký error handler, pass tiếp
- health plugin: Không can thiệp, pass tiếp
- ready plugin: Không can thiệp, pass tiếp
- kubernetes plugin: Query khớp với
cluster.localzone → plugin tìm trong cache → tìm thấy Servicepayment-serviceở namespacepayment→ trả về ClusterIP - Response đi ngược lại, không qua
forwardhaycacheplugin (response đã có từ kubernetes plugin)
Khi Pod query api.external-company.com:
1-3. Giống trên. 4. kubernetes plugin: Query không khớp với cluster.local → fallthrough nếu là PTR, hoặc pass sang plugin tiếp theo 5. forward plugin: Query được forward đến upstream DNS (resolver của node, thường là Cloud DNS metadata server 169.254.169.254:53) 6. cache plugin: Response từ upstream được cache lại với TTL do response chỉ định (tối đa 30 giây)
Plugin Ordering — Tại Sao Thứ Tự Quan Trọng
CoreDNS chạy plugins theo thứ tự trong Corefile, không phải theo dependency. Nếu cache plugin được đặt trước kubernetes plugin:
# SAI - cache sẽ được check trước kubernetes
cache 30
kubernetes cluster.local ...Cache check sẽ xảy ra trước kubernetes lookup. Nếu cache miss, query vẫn được xử lý bởi kubernetes (cache được build sau). Tuy nhiên thứ tự này không tối ưu và có thể gây behavior không mong đợi với cache invalidation.
Thứ tự đúng trong GKE: kubernetes plugin (resolution) trước, sau đó cache (caching), sau đó forward (upstream).
Kubernetes Plugin — State Sync Mechanism
Watch-Based Sync
kubernetes plugin không poll API server — nó dùng watch mechanism (tương tự controller-manager). Plugin giữ một in-memory index của tất cả Services và Endpoints trong cluster.
Khi DNS query đến, plugin không cần gọi API server — nó lookup từ local cache. Điều này làm cho DNS resolution trong CoreDNS cực kỳ nhanh (thường < 1ms) cho cluster-internal queries.
TTL Behavior
Với cấu hình ttl 30 trong kubernetes plugin, mọi DNS response về cluster resources có TTL là 30 giây. Đây là giá trị cứng — nếu một Service bị xóa, client có thể tiếp tục dùng cached IP tối đa 30 giây.
Trong thực tế, khi Service bị xóa, ClusterIP của nó cũng bị giải phóng ngay lập tức. Nếu client cố gọi IP đó trong 30 giây TTL còn lại, sẽ nhận được connection refused (vì không có endpoint nào lắng nghe IP đó nữa). Đây là behavior được thiết kế chủ đích — TTL 30 giây là đủ thấp để cân bằng giữa performance (cache hits) và freshness.
Pods Directive
kubernetes cluster.local ... {
pods insecure
}pods insecure cho phép CoreDNS tạo A records cho mọi Pod IP, không chỉ Pods thuộc headless Services. Record dạng <pod-ip-with-dashes>.<namespace>.pod.cluster.local. Ví dụ: Pod có IP 10.0.1.5 ở namespace default → 10-0-1-5.default.pod.cluster.local.
Gọi là "insecure" vì bất kỳ ai biết IP của Pod đều có thể resolve hostname của nó — không cần authentication. Trên GKE, điều này được chấp nhận vì network access đến Pod IP đã được kiểm soát bởi NetworkPolicy.
Custom DNS Configuration — Khi Nào Cần Sửa Corefile
GKE cho phép sửa ConfigMap coredns để thêm stub domains hoặc upstream resolvers. Tuy nhiên, GKE có thể overwrite ConfigMap khi cluster upgrade hoặc khi control plane reconcile. Nên sử dụng mechanism chính thức của GKE cho custom DNS.
Thêm Stub Domain (qua kube-dns ConfigMap)
GKE đọc stub domain config từ ConfigMap kube-dns (backward compatibility):
apiVersion: v1
kind: ConfigMap
metadata:
name: kube-dns
namespace: kube-system
data:
stubDomains: |
{"acme.local": ["1.2.3.4"]}
upstreamNameservers: |
["8.8.8.8", "8.8.4.4"]CoreDNS tự động sync config này vào Corefile qua reload plugin. Không cần restart pods.
Với Cloud DNS for GKE
Khi GKE cluster sử dụng Cloud DNS (thay vì kube-dns), Corefile thay đổi đáng kể:
kubernetesplugin được thay bằng integration với Cloud DNS- External queries được routed qua metadata server thay vì forward trực tiếp
- Custom stub domains được cấu hình qua Cloud DNS forwarding zones
Constraints và Scale Limits
CoreDNS QPS Limits
Mặc định, CoreDNS Deployment trong GKE có 2 replicas cho small clusters, tự động scale hơn cho large clusters. Mỗi CoreDNS pod có thể handle khoảng 10,000-20,000 queries/giây tùy theo query complexity.
Giới hạn thực tế đến từ:
forwardpluginmax_concurrent: Mặc định 1000 concurrent upstream queries. Vớindots:5lookup storm, con số này có thể bị saturate.- Kubernetes API watch latency: Khi cluster có nhiều Service/Endpoint changes, kubernetes plugin phải process nhiều watch events, có thể tăng lookup latency.
- Memory: In-memory cache tăng theo số Services và Endpoints. Với 10,000 Services, memory mỗi pod tăng đáng kể.
Không Có HA Trong Một Pod
CoreDNS là stateless — không có leader election, không có shared state giữa pods. Mỗi pod có cache riêng. Điều này là tốt cho horizontally scalability nhưng nghĩa là sau khi pod restart, cache bị cold — tất cả queries phải đến kubernetes plugin hoặc upstream cho đến khi cache được build lại.
CoreDNS Và conntrack Exhaustion
Đây là issue quan trọng nhất và thường bị bỏ qua. Khi một Pod gửi DNS query đến ClusterIP của kube-dns:
- Query gửi đến
10.96.0.10:53qua UDP - kube-proxy (iptables) DNAT gói tin đến một trong các CoreDNS pod endpoints
- Kernel tạo conntrack entry để track state của UDP "connection"
- UDP conntrack entry timeout mặc định là 30 giây
Với cluster lớn và nhiều DNS queries:
- Mỗi query = 1 conntrack entry = ~350 bytes memory
- 10,000 queries/giây = 10,000 entries × 30s = 300,000 entries ở bất kỳ thời điểm nào
- Default conntrack table size: 65,536 entries (có thể tune nhưng có ceiling)
- Khi table full: gói tin bị DROP, không có error message rõ ràng
Symptom: DNS queries mất đột ngột, "connection timed out" sau 30s (không phải "connection refused"). Đây là lý do chính NodeLocal DNSCache được thiết kế — nó loại bỏ hoàn toàn conntrack overhead cho DNS.