CoreDNS 深度拆解:ndots、search domain 与那些坑人的 DNS
CoreDNS 深度æè§£ï¼ndotsãsearch domain ä¸é£äºå人ç DNS
ltl 2026-08-20 13 é 读19åéåæåå¸äº quant67.comï¼è½¬è½½è¯·ä¿çåºå¤ã
ä½ çå¾®æå¡è°ç¨å¤é¨ APIï¼å¹³åå»¶è¿ 200msãä½ ä¼åäºè¿æ¥æ± ãè°äºè¶ æ¶åæ°ãæ¢äºæ´å¿«çåºåååºï¼å»¶è¿çº¹ä¸ä¸å¨ãæåä½ æäºä¸ªå ï¼åç°æ¯æ¬¡ HTTP 请æ±ä¹åï¼é½æåæ¡ DNS æ¥è¯¢å¨æéââå ¶ä¸ä¸æ¡æ³¨å®å¤±è´¥ï¼åªææå䏿¡è½æ¿å°ç»æã
罪é祸é¦ï¼ndots:5ã
è¿ä¸ªä¸èµ·ç¼ç /etc/resolv.conf é
ç½®é¡¹ï¼æ¯ Kubernetes é群éæå¸¸è§çæ§è½æåä¹ä¸ã宿䏿¡ç®åçå¤é¨ååè§£æï¼è¨èæäºåå°äºæ¬¡ DNS æ¥è¯¢ãèç»å¤§å¤æ°å¼åè
çè³ä¸ç¥éè¿ä¸ªé
ç½®çåå¨ã
æ¬ææè§£ K8s DNS ç宿´é¾è·¯ï¼
- CoreDNS çæä»¶é¾æ¶æä¸ kubernetes æä»¶çå®ç°åç
/etc/resolv.confä¸ndotsãsearch domainãsingle-request-reopenççæ£å«ä¹- ç¨ tcpdump 宿µ ndots:5 çæ¥è¯¢æ¾å¤§æåº
- NodeLocal DNSCache ä¸ autopath æä»¶çä¼åæ¹æ¡
- ä» nslookup å° tcpdump ç DNS ææ¥å ¨å¥è·¯
å®éªç¯å¢ï¼Kubernetes 1.29ï¼CoreDNS 1.11.1ï¼Ubuntu 22.04 èç¹ã CNIï¼Calicoï¼é群åååç¼ï¼cluster.localã
ä¸ãK8s DNS è§èï¼æ¯ä¸ªèµæºç DNS è®°å½é¿ä»ä¹æ ·
å¨ææ¥ DNS é®é¢ä¹åï¼ä½ å¾å ç¥é K8s å°åºæ³¨åäºåªäº DNS è®°å½ãè¿ä¸æ¯ CoreDNS çåæï¼èæ¯ Kubernetes ç DNS è§èï¼DNS-Based Service Discoveryï¼å®ä¹çã
ClusterIP Service
ä¸ä¸ªæ®éç ClusterIP Service ä¼äº§ç两æ¡è®°å½ï¼
# A è®°å½ï¼Service åç§° â ClusterIP
my-svc.default.svc.cluster.local. IN A 10.96.100.23
# SRV è®°å½ï¼ç«¯å£åç°
_http._tcp.my-svc.default.svc.cluster.local. IN SRV 0 100 80 my-svc.default.svc.cluster.local.
宿´ç FQDN æ ¼å¼æ¯ <service>.<namespace>.svc.<cluster-domain>.ï¼å
¶ä¸ cluster-domain é»è®¤æ¯ cluster.localã
Headless Service
Headless Serviceï¼clusterIP: Noneï¼çè¡ä¸ºå®å
¨ä¸åãå®ä¸åé
ClusterIPï¼A è®°å½ç´æ¥æåå端 Pod ç IPï¼
# A è®°å½ï¼è¿åææå°±ç»ª Pod ç IPï¼å¤æ¡ï¼
my-headless.default.svc.cluster.local. IN A 10.244.1.5
my-headless.default.svc.cluster.local. IN A 10.244.2.8
my-headless.default.svc.cluster.local. IN A 10.244.3.12
# æ¯ä¸ª Pod è¿æç¬ç«ç A è®°å½
10-244-1-5.my-headless.default.svc.cluster.local. IN A 10.244.1.5
StatefulSet é å Headless Service æ¶ï¼Pod ç DNS åç§°æ¯ç¨³å®çï¼
# StatefulSet Pod ç A è®°å½
web-0.my-headless.default.svc.cluster.local. IN A 10.244.1.5
web-1.my-headless.default.svc.cluster.local. IN A 10.244.2.8
è¿å°±æ¯ä¸ºä»ä¹æ°æ®åºé群ï¼MySQLãRedis Clusterï¼å¿ é¡»ç¨ Headless Serviceââ客æ·ç«¯éè¦ç¥éæ¯ä¸ªå®ä¾çç¬ç«å°åã
Pod DNS
Pod æ¬èº«ä¹æ DNS è®°å½ï¼ä½æ ¼å¼æ¯è¾ç¹æ®ââç¨ç ´æå·æ¿ä»£ IP ä¸çç¹ï¼
# Pod A è®°å½ï¼åºäº IP çæï¼
10-244-1-5.default.pod.cluster.local. IN A 10.244.1.5
å¦æå¨ Pod spec ä¸è®¾ç½®äº hostname å subdomainï¼å¹¶ä¸åå¨å¯¹åºç Headless Serviceï¼Pod ä¼è·å¾æ´å好ç DNS åç§°ï¼
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
hostname: my-host
subdomain: my-headless # å¿
é¡»ä¸ Headless Service åå
containers:
- name: app
image: nginx
my-host.my-headless.default.svc.cluster.local. IN A 10.244.1.5
ExternalName Service
ExternalName Service è¿å CNAME èé A è®°å½ï¼
ext-svc.default.svc.cluster.local. IN CNAME api.external-provider.com.
CoreDNS ä¸ä¼éå½è§£æè¿ä¸ª CNAMEââç´æ¥è¿åç»å®¢æ·ç«¯ï¼ç±å®¢æ·ç«¯ç resolver ç»§ç»è§£æã
äºãCoreDNS æ¶æï¼Corefile 䏿件é¾
CoreDNS æ¯ K8s ä» 1.13 å¼å§çé»è®¤é群 DNSãæ ¸å¿è®¾è®¡ææ³æ¯ä¸åçæä»¶ï¼æ¯ä¸ªåè½ï¼ç¼åãæ¥å¿ãå¥åº·æ£æ¥ãKubernetes éæï¼é½æ¯ç¬ç«çæä»¶ï¼éè¿ Corefile é 置串èæå¤çé¾ã
Corefile è§£æ
æ¥çé群ä¸ç CoreDNS é ç½®ï¼
kubectl -n kube-system get configmap coredns -o yaml
ä¸ä¸ªå ¸åç Corefileï¼
.: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
}
æä»¶çæ§è¡é¡ºåºä¸ç± Corefile ä¸ç书å顺åºå³å®ï¼èæ¯ç± CoreDNS æºç ä¸ç硬ç¼ç 顺åºå³å®ãè¿ä¸ªé¡ºåºå®ä¹å¨ plugin.cfg æä»¶ä¸ï¼
# æä»¶æ§è¡é¡ºåºï¼ç®åï¼
cache # å
æ¥ç¼å
autopath # search domain æå¡ç«¯å±å¼
kubernetes # å¤ç cluster.local å
forward # éé群åå转åå°ä¸æ¸¸
loop
å
³é®è®¤ç¥ï¼cache å¨ kubernetes ä¹åæ§è¡ãè¿æå³çé群å
DNS æ¥è¯¢ä¹ä¼è¢«ç¼åï¼é夿¥è¯¢ä¸ä¼æ¯æ¬¡é½èµ° API Serverã
kubernetes æä»¶çå®ç°åç
kubernetes æä»¶æ¯ CoreDNS ä¸ K8s API Server çæ¡¥æ¢ãå®ç工使¹å¼ï¼
- å¯å¨æ¶å»ºç« Watchï¼éè¿ K8s Informer æºå¶ï¼Watch Service å Endpointsï¼æ EndpointSliceï¼èµæº
- æå»ºå åç´¢å¼ï¼æ Service/Endpoints æ°æ®æå»ºææååç´¢å¼çå åæ°æ®ç»æ
- æ¥è¯¢æ¶ç´æ¥æ¥å åï¼æ¶å° DNS æ¥è¯¢æ¶ä¸åè°ç¨ API Serverï¼ç´æ¥æ¥å åç´¢å¼
- å¢éæ´æ°ï¼Watch äºä»¶è§¦åç´¢å¼çå¢éæ´æ°
API Server CoreDNS kubernetes æä»¶
| |
|--- Watch Services ---------->| å»ºç« Watch è¿æ¥
|--- Watch EndpointSlices ---->|
| |
| (Service å建/æ´æ°/å é¤) |
|--- Event: ADDED ------------>| æ´æ°å
åç´¢å¼
|--- Event: MODIFIED --------->| æ´æ°å
åç´¢å¼
|--- Event: DELETED ---------->| ä»ç´¢å¼ä¸å é¤
| |
| DNS Query: my-svc.default.svc.cluster.local
| |---> æ¥å
åç´¢å¼ï¼ç´æ¥è¿å A è®°å½
è¿ä¸ªè®¾è®¡çä¼ç¹æ¯æ¥è¯¢å»¶è¿æä½ï¼å¾®ç§çº§å 忥æ¾ï¼ï¼ç¼ºç¹æ¯ CoreDNS éè¦æ¶èå åå卿´ä¸ªé群ç Service/Endpoints æ°æ®ãå¨å¤§è§æ¨¡é群ä¸ï¼ä¸çº§ Serviceï¼ï¼CoreDNS çå åå ç¨å¯ä»¥è¾¾å°æ°ç¾ MBã
pods insecure é项å
许åºäº Pod IP ååæé A è®°å½ï¼æ IP ä¸çç¹æ¿æ¢æç ´æå·ï¼ï¼ä½ä¸éªè¯ Pod æ¯å¦åå¨ãpods verified ä¼éªè¯ï¼ä½å¢å API Server è´è½½ã
fallthrough æºå¶
fallthrough æå®äºåªäºæ¥è¯¢ç±»åå¨ kubernetes æä»¶æ¾ä¸å°è®°å½æ¶ï¼åºè¯¥ç»§ç»ä¼ éç»ä¸ä¸ä¸ªæä»¶ï¼è䏿¯ç´æ¥è¿å NXDOMAINï¼ï¼
kubernetes cluster.local in-addr.arpa ip6.arpa {
fallthrough in-addr.arpa ip6.arpa
}
ä¸é¢çé
置表示ï¼å¯¹äº cluster.local åï¼å¦æ kubernetes æä»¶æ²¡ææ¾å°è®°å½ï¼ç´æ¥è¿å NXDOMAINãä½å¯¹äºååè§£æï¼in-addr.arpaãip6.arpaï¼ï¼å¦ææ²¡æ¾å°ï¼ç»§ç»ä¼ éç» forward æä»¶ââå 为ååè§£æå¯è½éè¦ä¸æ¸¸ DNS æ¥å¤çã
ä¸ã/etc/resolv.conf çç§å¯
ç°å¨è¿å
¥æ¬æçæ ¸å¿ï¼Pod éé£ä¸ªä¸èµ·ç¼ç /etc/resolv.confã
kubelet å¦ä½çæ resolv.conf
å¨ Pod 䏿§è¡ cat /etc/resolv.confï¼
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
è¿ä¸ªæä»¶ä¸æ¯éåèªå¸¦çï¼èæ¯ kubelet å¨å建 Pod æ¶æ³¨å
¥çãçæé»è¾å¨ kubelet æºç pkg/kubelet/network/dns/dns.go ä¸ï¼kubelet æ ¹æ® Pod ç dnsPolicy å³å®çæä»ä¹å
容ï¼
| dnsPolicy | nameserver | search domain |
|---|---|---|
| ClusterFirstï¼é»è®¤ï¼ | kube-dns ClusterIP | <ns>.svc.cluster.local svc.cluster.local cluster.local |
| Default | ç»§æ¿èç¹ç resolv.conf | ç»§æ¿èç¹ç search domain |
| None | Pod spec ä¸èªå®ä¹ | Pod spec ä¸èªå®ä¹ |
| ClusterFirstWithHostNet | kube-dns ClusterIP | å ClusterFirst |
ndots:5 为ä»ä¹æ¯é»è®¤å¼
ndots çå«ä¹ï¼å¦æä¸ä¸ªååä¸å
å«çç¹çæ°éå°äº ndots å¼ï¼glibc resolver ä¼å
å°è¯æå®å½ä½ç¸å¯¹åç§°ï¼ä¾æ¬¡è¿½å search domain æ¥æ¥è¯¢ãåªæå½ææ search domain é½è¿å NXDOMAIN ä¹åï¼æä¼æåå§åç§°ä½ä¸º FQDN æ¥æ¥è¯¢ã
为ä»ä¹ K8s éäº 5ï¼å 为éç¾¤å æé¿ç DNS åç§°å å« 4 个ç¹ï¼
my-svc.my-namespace.svc.cluster.local
^ ^ ^ ^
1 2 3 4 ï¼4 个ç¹ï¼
SRV è®°å½æ´é¿ï¼
_http._tcp.my-svc.my-namespace.svc.cluster.local
^ ^ ^ ^ ^ ^
1 2 3 4 5 6 ï¼å®é
ä¸ SRV æä¸å线åç¼ï¼
ndots:5 ç¡®ä¿é群å
çä»»ä½ DNS åç§°é½ä¼èµ° search domain å±å¼è·¯å¾ï¼ä»èæ£ç¡®è§£æãä½ä»£ä»·æ¯ââææå¤é¨ååä¹èµ°è¿æ¡è·¯å¾ã
search domain å±å¼é¡ºåºçæ§è½é·é±
å½ Pod éçåºç¨æ¥è¯¢ api.github.com æ¶ï¼2 个ç¹ï¼å°äº ndots:5ï¼ï¼glibc ä¼æé¡ºåºå°è¯ï¼
1. api.github.com.default.svc.cluster.local. â NXDOMAIN (CoreDNS)
2. api.github.com.svc.cluster.local. â NXDOMAIN (CoreDNS)
3. api.github.com.cluster.local. â NXDOMAIN (CoreDNS)
4. api.github.com. â SUCCESS (䏿¸¸ DNS)
ï¼ç¤ºæå¾è§åæï¼DNS æ¥è¯¢æµç¨ï¼search domain å±å¼ä¸ ndots:5 çæ¥è¯¢æ¾å¤§ï¼
忬¡æ¥è¯¢ï¼ä¸æ¬¡æµªè´¹ã èä¸å 为 A å AAAA æ¯å¹¶è¡åçï¼å®é 䏿¯ å «ä¸ª DNS å ã妿䏿¸¸ DNS å»¶è¿ 50msï¼ä½ çåºç¨å¨æ¯æ¬¡æ°è¿æ¥æ¶é½è¦é¢å¤çå¾ 150ms 以ä¸ã
æ´ç³ç³çæ¯ï¼å³ä½¿ç®æ æ¯å个ç¹çååï¼æ¯å¦ api.us-west-2.amazonaws.comï¼ï¼åªè¦ç¹æ°å°äº 5ï¼åæ ·ä¼è§¦åå±å¼ãå¨ AWS/GCP ç¯å¢ä¸ï¼å
鍿å¡çååç»å¸¸æä¸å个ç¹ï¼å
¨é½ä¸æã
为ä»ä¹ä¸ç´æ¥æ¹æ ndots:1
æ ndots æ¹å°å¯ä»¥åå°å¤é¨ååçæ¥è¯¢æ¬¡æ°ï¼ä½ä¼ç ´åé群å ççåç§°è§£æï¼
# ndots:1 æ¶ï¼æ¥è¯¢ "my-svc"ï¼0 个ç¹ï¼< 1ï¼ä»ç¶ä¼å±å¼ search domain
# 使¥è¯¢ "my-svc.my-namespace"ï¼1 个ç¹ï¼>= 1ï¼ä¼ç´æ¥ä½ä¸º FQDN æ¥è¯¢
# ç»æï¼NXDOMAINï¼å 为 "my-svc.my-namespace" 䏿¯åæ³çå
¬ç½åå
æä»¥ ndots çè°æ´å¿
须谨æ
ãå¦æä½ çåºç¨åªç¨ Service çåç§°ï¼my-svcï¼æ FQDNï¼æ«å°¾å ç¹ my-svc.default.svc.cluster.local.ï¼ï¼å¯ä»¥å®å
¨å°æ ndots éå° 2ã
options single-request-reopen æ¯å¨ä¿®ä»ä¹ bug
å¨æäº Linux å æ ¸çæ¬ä¸ï¼glibc ä¼å¨åä¸ä¸ª UDP socket ä¸å¹¶è¡åé A å AAAA æ¥è¯¢ãå½ conntrack 模åå¤çè¿ä¸¤ä¸ªæ¥è¯¢æ¶ï¼å¯è½å ä¸ºç«ææ¡ä»¶ï¼race conditionï¼å¯¼è´å ¶ä¸ä¸ä¸ªæ¥è¯¢ç conntrack entry 被é误å°ä¸¢å¼ï¼è¡¨ç°ä¸º DNS æ¥è¯¢è¶ æ¶ï¼é常 5 ç§ï¼ã
# ç«ææ¡ä»¶ï¼ä¸¤ä¸ª UDP å
å ä¹åæ¶åéï¼ä½¿ç¨ç¸åæºç«¯å£
Pod â conntrack â CoreDNS
|--- A query (src port 12345) --â conntrack å建 entry
|--- AAAA query (src port 12345) --â ä¸å·²æ entry å²çªï¼å
è¢«ä¸¢å¼ â 5 ç§è¶
æ¶
single-request-reopen 让 glibc 为 A å AAAA æ¥è¯¢ä½¿ç¨ä¸åç socketï¼ä¸åçæºç«¯å£ï¼ï¼ä»èé¿å
conntrack å²çªãå¦ä¸ä¸ªç¸å
³éé¡¹æ¯ use-vcï¼å®å¼ºå¶ä½¿ç¨ TCPï¼å®å
¨ç»è¿ conntrack ç UDP ç«æé®é¢ï¼ä½å¼éæ´å¤§ï¼
apiVersion: v1
kind: Pod
spec:
dnsConfig:
options:
- name: single-request-reopen
åãå®éªï¼tcpdump æå ndots:5 çæ¥è¯¢æ¾å¤§æåº
说äºè¿ä¹å¤ï¼æ¥å®é æå éªè¯ã
å®éªç¯å¢æå»º
kubectl run dns-debug --image=nicolaka/netshoot --restart=Never -- sleep 3600
kubectl wait --for=condition=Ready pod/dns-debug --timeout=60s
æå DNS æ¥è¯¢
å¨èç¹ä¸ç¨ tcpdump æå DNS æµéï¼ç¶åå¨ Pod ä¸è§¦åæ¥è¯¢ï¼
# èç¹ä¸ï¼æå 53 端å£ç UDP æµé
tcpdump -i any -nn port 53 -l 2>/dev/null | grep -i "api.github.com"
# Pod ä¸ï¼è§¦å DNS æ¥è¯¢
kubectl exec dns-debug -- nslookup api.github.com
tcpdump çè¾åºï¼ç®åï¼ï¼
10:23:01.001 10.244.1.5.43210 > 10.96.0.10.53: A? api.github.com.default.svc.cluster.local.
10:23:01.001 10.244.1.5.43210 > 10.96.0.10.53: AAAA? api.github.com.default.svc.cluster.local.
10:23:01.002 10.96.0.10.53 > 10.244.1.5.43210: NXDomain 0/1/0
10:23:01.002 10.96.0.10.53 > 10.244.1.5.43210: NXDomain 0/1/0
10:23:01.003 10.244.1.5.43211 > 10.96.0.10.53: A? api.github.com.svc.cluster.local.
10:23:01.003 10.244.1.5.43211 > 10.96.0.10.53: AAAA? api.github.com.svc.cluster.local.
10:23:01.004 10.96.0.10.53 > 10.244.1.5.43211: NXDomain 0/1/0
10:23:01.004 10.96.0.10.53 > 10.244.1.5.43211: NXDomain 0/1/0
10:23:01.005 10.244.1.5.43212 > 10.96.0.10.53: A? api.github.com.cluster.local.
10:23:01.005 10.244.1.5.43212 > 10.96.0.10.53: AAAA? api.github.com.cluster.local.
10:23:01.006 10.96.0.10.53 > 10.244.1.5.43212: NXDomain 0/1/0
10:23:01.006 10.96.0.10.53 > 10.244.1.5.43212: NXDomain 0/1/0
10:23:01.007 10.244.1.5.43213 > 10.96.0.10.53: A? api.github.com.
10:23:01.007 10.244.1.5.43213 > 10.96.0.10.53: AAAA? api.github.com.
10:23:01.058 10.96.0.10.53 > 10.244.1.5.43213: 1/0/0 A 140.82.121.6
10:23:01.060 10.96.0.10.53 > 10.244.1.5.43213: 0/1/0
å «ä¸ª DNS å ï¼4 ç» A+AAAA æ¥è¯¢ï¼ï¼åªææåä¸ç»æ¿å°äºç»æã åä¸ç»å ¨é¨æµªè´¹ã
对æ¯ï¼æ«å°¾å ç¹ç FQDN æ¥è¯¢
kubectl exec dns-debug -- nslookup api.github.com.
10:25:01.001 10.244.1.5.43220 > 10.96.0.10.53: A? api.github.com.
10:25:01.001 10.244.1.5.43220 > 10.96.0.10.53: AAAA? api.github.com.
10:25:01.052 10.96.0.10.53 > 10.244.1.5.43220: 1/0/0 A 140.82.121.6
10:25:01.054 10.96.0.10.53 > 10.244.1.5.43220: 0/1/0
åªæä¸¤ä¸ªå ã æ«å°¾å ç¹åè¯ glibcï¼è¿æ¯ç»å¯¹ååï¼ä¸è¦å±å¼ search domainã
ç¨ dig 精确æµé
# æ¥ç宿´çæ¥è¯¢è¿ç¨
kubectl exec dns-debug -- dig +search +showsearch api.github.com
# å¯¹æ¯ FQDN æ¥è¯¢
kubectl exec dns-debug -- dig api.github.com.
# æ¥ç Pod ç resolv.conf
kubectl exec dns-debug -- cat /etc/resolv.conf
äºãDNS ç¼åå±ï¼CoreDNS cache ä¸ NodeLocal DNSCache
æ¥è¯¢æ¾å¤§çé®é¢å¦æä¸è½ä»æ ¹æºæ¶é¤ï¼è³å°å¯ä»¥éè¿ç¼åæ¥ç¼è§£ã
CoreDNS cache æä»¶
Corefile ä¸ç cache 30 表示ç¼åæåååº 30 ç§ãæ´ç²¾ç»çé
ç½®ï¼
cache {
success 9984 30 # ç¼åæåååºï¼æå¤ 9984 æ¡ï¼TTL 30 ç§
denial 9984 5 # ç¼å NXDOMAIN/NODATAï¼æå¤ 9984 æ¡ï¼TTL 5 ç§
prefetch 10 60s 10% # å½ç¼åæ¡ç®è¢«è®¿é® 10 æ¬¡ä»¥ä¸æ¶ï¼å¨è¿æå 60 ç§å
æåå·æ°
}
denial ç¼å对 ndots:5 çæ¥è¯¢æ¾å¤§ç¹å«éè¦ââé£äº NXDOMAIN ååºä¼è¢«ç¼åï¼ç¬¬äºæ¬¡æ¥è¯¢åä¸ä¸ªå¤é¨ååæ¶ï¼å䏿¬¡ search domain å±å¼å¯ä»¥ç´æ¥ä»ç¼åè¿åï¼ä¸éè¦åæ¥ API Server æä¸æ¸¸ DNSã
ä½é®é¢æ¯ï¼ææ Pod ç DNS æ¥è¯¢é½è¦ç»è¿ CoreDNS Podï¼ç½ç»è·¯å¾æ¯ Pod â CNI ç½ç» â CoreDNS Podãå¨å¤§è§æ¨¡é群ä¸ï¼CoreDNS å¯è½æä¸ºç¶é¢ã
NodeLocal DNSCache
NodeLocal DNSCache 卿¯ä¸ªèç¹ä¸è¿è¡ä¸ä¸ª DNS ç¼å DaemonSetï¼æç¼å屿¨å°èç¹æ¬å°ï¼
Pod â æ¬å°ç¼åï¼DaemonSetï¼â CoreDNS â 䏿¸¸ DNS
169.254.20.10 10.96.0.10
å®è£ æ¹å¼ï¼
# 使ç¨å®æ¹ manifest
kubectl apply -f https://raw.githubusercontent.com/kubernetes/kubernetes/master/cluster/addons/dns/nodelocaldns/nodelocaldns.yaml
NodeLocal DNSCache ç Corefileï¼
cluster.local:53 {
errors
cache {
success 9984 30
denial 9984 5
}
reload
loop
bind 169.254.20.10
forward . __PILLAR__CLUSTER__DNS__ {
force_tcp
}
prometheus :9253
}
.:53 {
errors
cache 30
reload
loop
bind 169.254.20.10
forward . __PILLAR__UPSTREAM__SERVERS__
prometheus :9253
}
å®çå·¥ä½åçï¼
- éè¿ DaemonSet 卿¯ä¸ªèç¹ä¸è¿è¡ä¸ä¸ª DNS ç¼åè¿ç¨
- çå¬
169.254.20.10ï¼link-local å°åï¼ç 53 ç«¯å£ - kubelet ä¿®æ¹ Pod ç
/etc/resolv.confï¼æ nameserver æå169.254.20.10 - ç¼åå½ä¸æ¶ç´æ¥è¿åï¼ä¸ç»è¿ CNI ç½ç»
- ç¼åæªå½ä¸æ¶ï¼éè¿ TCP 转åç» CoreDNSï¼TCP é¿å äº conntrack é®é¢ï¼
æ§è½æ¶çï¼
| ææ | æ NodeLocal DNSCache | æ NodeLocal DNSCache |
|---|---|---|
| DNS æ¥è¯¢å»¶è¿ï¼ç¼åå½ä¸ï¼ | 1-5msï¼ç»è¿ CNI ç½ç»ï¼ | <0.5msï¼æ¬å° loopbackï¼ |
| CoreDNS è´è½½ | æææ¥è¯¢ | ä» ç¼åæªå½ä¸ |
| conntrack ç«æ | åå¨é£é© | TCP 转åï¼æ é£é© |
| èç¹æ éå½±å | CoreDNS Pod æå¨èç¹æ é影忿ä¾èµè | ä» å½±åæ¬èç¹ |
注æï¼NodeLocal DNSCache ä½¿ç¨ force_tcp è¿æ¥ CoreDNSï¼é¿å
äº conntrack ç UDP ç«æé®é¢ï¼ä½å¨æå¤§è§æ¨¡é群ä¸éè¦å
³æ³¨ CoreDNS ç TCP è¿æ¥æ°éå¶ã
å ãautopath æä»¶ï¼æ ndots:5 çæ§è½é®é¢éå° ndots:2 æ°´å¹³
NodeLocal DNSCache éè¿ç¼ååå°äºé夿¥è¯¢çå¼éï¼ä½ç¬¬ä¸æ¬¡æ¥è¯¢å¤é¨ååæ¶ï¼search domain å±å¼ä»ç¶è¦èµ°åè½®ãautopath æä»¶ä»æ ¹æ¬ä¸è§£å³è¿ä¸ªé®é¢ã
autopath çå·¥ä½åç
autopath æ search domain å±å¼ä»å®¢æ·ç«¯ï¼glibcï¼æ¬å°äºæå¡ç«¯ï¼CoreDNSï¼ï¼
- Pod åé第ä¸ä¸ªæ¥è¯¢
api.github.com.default.svc.cluster.local. - CoreDNS ç autopath æä»¶è¯å«åºè¿æ¯ search domain å±å¼çç¬¬ä¸æ¥
- autopath 卿å¡ç«¯ä¾æ¬¡å°è¯ææ search domainï¼åç°é½æ¯ NXDOMAIN
- æç»è§£æ
api.github.com.æ¿å°ç»æ - æç»ææå æä¸ä¸ª CNAME é¾è¿åç»å®¢æ·ç«¯
# 客æ·ç«¯åé 1 个æ¥è¯¢
Query: api.github.com.default.svc.cluster.local. A
# CoreDNS è¿å CNAME é¾ + æç»ç»æ
Answer:
api.github.com.default.svc.cluster.local. CNAME api.github.com.svc.cluster.local.
api.github.com.svc.cluster.local. CNAME api.github.com.cluster.local.
api.github.com.cluster.local. CNAME api.github.com.
api.github.com. A 140.82.121.6
客æ·ç«¯ç glibc resolver æ¶å°ç¬¬ä¸ä¸ªæ¥è¯¢çååºåï¼åç°æç»ç A è®°å½å·²ç»å¨éé¢äºï¼å°±ä¸ä¼ååéåç»ç search domain æ¥è¯¢ãåè½®ç½ç»å¾è¿åæäºä¸è½®ã
é ç½® autopath
.:53 {
errors
health
ready
autopath @kubernetes # å¯ç¨ autopathï¼ä½¿ç¨ kubernetes æä»¶è·å Pod ç search domain
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods verified # autopath éè¦ pods verified æ pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
forward . /etc/resolv.conf
cache 30
loop
reload
loadbalance
}
@kubernetes åè¯ autopath ä» kubernetes æä»¶è·ååèµ·æ¥è¯¢ç Pod ç namespace ä¿¡æ¯ï¼ä»èç¥é该 Pod ç search domain å表ã
autopath çå±éæ§
-
éè¦
pods verifiedæpods insecureï¼autopath éè¦æ ¹æ®æº IP æ¥æ¾ Pod ç namespaceï¼è¿è¦æ± kubernetes æä»¶è½è§£æ Pod IPãpods verifiedä¼å¢å API Server æ¥è¯¢ï¼pods insecureä¸éªè¯ä½æ§è½æ´å¥½ã -
CNAME é¾å¯è½ä¸æäºå®¢æ·ç«¯ä¸å ¼å®¹ï¼å°æ° DNS 客æ·ç«¯ä¸è½æ£ç¡®å¤ç CNAME é¾ãå¨å®é ç产ä¸è¿ç§æ åµå¾å°è§ï¼ä½å¦æéå°å¥æªçè§£æå¤±è´¥ï¼éè¦ææ¥è¿ä¸ç¹ã
-
ä¸è½ä¸ NodeLocal DNSCache ç´æ¥é åï¼NodeLocal DNSCache æ¶å°æ¥è¯¢æ¶ï¼æº IP æ¯ Pod IPãä½å½å®è½¬åç» CoreDNS æ¶ï¼æº IP åæäº NodeLocal DNSCache ç IPãautopath æ¿å°çæ¯ NodeLocal DNSCache ç IPï¼æ æ³ç¡®å®åå§ Pod ç namespaceãéè¦é¢å¤é ç½®æ¥è§£å³ã
autopath ä¸ ndots è°æ´ç对æ¯
| æ¹æ¡ | å¤é¨ååæ¥è¯¢æ¬¡æ° | é群å çåç§° | é ç½®å¤æåº¦ | 客æ·ç«¯å ¼å®¹æ§ |
|---|---|---|---|---|
| é»è®¤ ndots:5 | 4-5 次 | æ£å¸¸ | æ éé ç½® | æå¥½ |
| ndots:2 | 1-2 次 | svc.ns å½¢å¼éè¦æ¹å | Pod 级å«é ç½® | æå¥½ |
| æ«å°¾å ç¹ | 1 次 | éè¦å ¨é¨æ¹å | åºç¨ä»£ç ä¿®æ¹ | æå¥½ |
| autopath | 1 æ¬¡ï¼æå¡ç«¯å±å¼ï¼ | æ£å¸¸ | CoreDNS é ç½® | æå°æ°å®¢æ·ç«¯ä¸å ¼å®¹ |
ä¸ãExternalDNSï¼Service å°å ¬ç½ DNS è®°å½çèªå¨åæ¥
åé¢è®¨è®ºç齿¯é群å DNSãå¦æä½ éè¦æ K8s Service æ Ingress æ´é²å°å ¬ç½ DNSï¼æ¯å¦ Route53ãCloudFlareï¼ï¼æå¨ç»´æ¤ DNS è®°å½æ¯ä¸å¯æ¥åçãExternalDNS è§£å³è¿ä¸ªé®é¢ã
å·¥ä½åç
ExternalDNS 以 Pod å½¢å¼è¿è¡å¨é群ä¸ï¼Watch K8s èµæºï¼ServiceãIngressãGatewayï¼ï¼å½æ£æµå°èµæºååæ¶ï¼èªå¨å¨å¤é¨ DNS æä¾åï¼Route53ãCloudFlare çï¼åå»ºãæ´æ°æå é¤å¯¹åºç DNS è®°å½ã
é¨ç½²ç¤ºä¾
apiVersion: apps/v1
kind: Deployment
metadata:
name: external-dns
namespace: kube-system
spec:
replicas: 1
selector:
matchLabels:
app: external-dns
template:
spec:
containers:
- name: external-dns
image: registry.k8s.io/external-dns/external-dns:v0.14.0
args:
- --source=service
- --source=ingress
- --domain-filter=example.com
- --provider=aws
- --policy=upsert-only # åªå建/æ´æ°ï¼ä¸å é¤
- --registry=txt
- --txt-owner-id=my-cluster
ä½¿ç¨æ¶ï¼åªéè¦å¨ Service æ Ingress 䏿·»å annotationï¼
apiVersion: v1
kind: Service
metadata:
name: my-app
annotations:
external-dns.alpha.kubernetes.io/hostname: app.example.com
external-dns.alpha.kubernetes.io/ttl: "300"
spec:
type: LoadBalancer
ports:
- port: 80
selector:
app: my-app
ExternalDNS ä¼èªå¨å建 app.example.com ç A è®°å½ï¼æå LoadBalancer çå¤é¨ IPã
æææä¸å®å ¨
ExternalDNS ä½¿ç¨ TXT è®°å½æ è®°èªå·±å建ç DNS è®°å½ï¼é²æ¢å¤ä¸ªé群ä¹é´çå²çªã--policy=upsert-only æ¯ç产ç¯å¢çæ¨èé
ç½®ââExternalDNS åªåå»ºåæ´æ°è®°å½ï¼ä¸ä¼å é¤ï¼é¿å
误å 导è´ççäº§äºæ
ã
å «ãDNS ææ¥å¥è·¯
DNS é®é¢çææ¥æä¸å¥åºå®çå级路å¾ã仿ç®åçå·¥å ·å¼å§ï¼éæ¥æ·±å ¥ã
ç¬¬ä¸æ¥ï¼nslookup / dig åºæ¬éªè¯
# ä» Pod å
鍿µè¯
kubectl exec -it dns-debug -- nslookup kubernetes.default
kubectl exec -it dns-debug -- nslookup my-svc.my-namespace
# ç¨ dig è·åæ´è¯¦ç»çä¿¡æ¯
kubectl exec -it dns-debug -- dig kubernetes.default.svc.cluster.local A +short
kubectl exec -it dns-debug -- dig @10.96.0.10 my-svc.default.svc.cluster.local A
# æµè¯å¤é¨åå
kubectl exec -it dns-debug -- dig api.github.com +search +showsearch
常è§è¾åºåæï¼
# æ£å¸¸
;; ANSWER SECTION:
kubernetes.default.svc.cluster.local. 30 IN A 10.96.0.1
# NXDOMAINï¼Service ä¸å卿 namespace é误
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN
# SERVFAILï¼CoreDNS å
é¨é误
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL
# è¶
æ¶ï¼ç½ç»ä¸éæ CoreDNS 没æè¿è¡
;; connection timed out; no servers could be reached
ç¬¬äºæ¥ï¼æ£æ¥ CoreDNS ç¶æ
# CoreDNS Pod æ¯å¦æ£å¸¸è¿è¡
kubectl -n kube-system get pods -l k8s-app=kube-dns
# æ¥ç CoreDNS æ¥å¿
kubectl -n kube-system logs -l k8s-app=kube-dns --tail=100
# æ£æ¥ CoreDNS Service ç Endpoints
kubectl -n kube-system get endpoints kube-dns
妿éè¦æ´è¯¦ç»çæ¥å¿ï¼å¨ Corefile 䏿·»å log æä»¶ï¼
.:53 {
log # è®°å½ææ DNS æ¥è¯¢
errors
# ...
}
ä¿®æ¹å CoreDNS ä¼èªå¨éæ°å è½½ï¼é»è®¤ 30 ç§ï¼ï¼ææå¨éå¯ï¼
kubectl -n kube-system rollout restart deployment coredns
ç¬¬ä¸æ¥ï¼dig +trace 追踪解æé¾è·¯
# ä» CoreDNS å¼å§è¿½è¸ª
kubectl exec dns-debug -- dig +trace api.github.com
# ç´æ¥æ¥ä¸æ¸¸ DNSï¼è·³è¿ CoreDNSï¼
kubectl exec dns-debug -- dig @8.8.8.8 api.github.com
å¦æç´æ¥æ¥ä¸æ¸¸ DNS æåä½éè¿ CoreDNS 失败ï¼é®é¢åºå¨ CoreDNS ç forward é
ç½®ã
ç¬¬åæ¥ï¼tcpdump æå
# å¨ CoreDNS Pod æå¨èç¹ä¸æå
tcpdump -i any -nn port 53 -w dns-capture.pcap
# åªçæ¥èªç¹å® Pod çæ¥è¯¢
tcpdump -i any -nn port 53 and host 10.244.1.5
# 宿¶è§£ææ¾ç¤º
tcpdump -i any -nn port 53 -l 2>/dev/null
ç¬¬äºæ¥ï¼æ£æ¥ CoreDNS ææ
CoreDNS æ´é² Prometheus ææ å¨ :9153/metricsï¼
kubectl -n kube-system port-forward svc/kube-dns 9153:9153
curl -s localhost:9153/metrics | grep -E "coredns_(dns_request|cache|forward|panics)"
å
³æ³¨è¿å ä¸ªææ ï¼coredns_dns_requests_total çªç¶æ´æ¶¨å¯è½æ¯æ¥è¯¢æ¾å¤§ï¼coredns_dns_responses_total{rcode="NXDOMAIN"} 大éåºç°æ¯ ndots å±å¼çç¹å¾ï¼coredns_cache_hits_total å½ä¸çè¿ä½éè¦è°æ´ç¼åï¼coredns_panics_total ä»»ä½éé¶å¼é½éè¦ç«å³ææ¥ã
常è§é®é¢éæ¥è¡¨
| çç¶ | å¯è½åå | ææ¥æ¹å |
|---|---|---|
| DNS è¶ æ¶ 5 ç§ | conntrack ç«æ | æ·»å single-request-reopen æä½¿ç¨ NodeLocal DNSCache |
| é´ææ§ SERVFAIL | CoreDNS æ æ³è¿æ¥ä¸æ¸¸ DNS | æ£æ¥èç¹ç /etc/resolv.conf å䏿¸¸ DNS å¯è¾¾æ§ |
| æ°å建ç Service è§£æå¤±è´¥ | CoreDNS ç Watch å»¶è¿ | çå¾ å ç§åéè¯ï¼æ£æ¥ CoreDNS ä¸ API Server çè¿æ¥ |
| å¤é¨ååè§£ææ ¢ | ndots:5 æ¥è¯¢æ¾å¤§ | tcpdump éªè¯ï¼èè autopath æè°æ´ ndots |
| Pod éå¯å DNS çæå¤±è´¥ | DNS ç¼åä¸çæ§è®°å½ | æ£æ¥ TTL 设置ï¼ç¡®ä¿ç¼åè¿ææ¶é´åç |
ä¹ãé«çº§è¯é¢ï¼dnsPolicy ä¸é群æ©å±
dnsPolicy ç宿´è¯ä¹
dnsPolicy: None + dnsConfig ç»äºä½ æå¤§ççµæ´»æ§ï¼
apiVersion: v1
kind: Pod
spec:
dnsPolicy: "None"
dnsConfig:
nameservers:
- 10.96.0.10
- 8.8.8.8
searches:
- default.svc.cluster.local
options:
- name: ndots
value: "2"
- name: single-request-reopen
- name: timeout
value: "2"
è·¨ namespace è®¿é® Service éè¦å¸¦ namespaceï¼my-svc.other-namespaceãå¨é
ç½®æä»¶ä¸ï¼å»ºè®®ä½¿ç¨ FQDNï¼å¸¦æ«å°¾ç¹ my-svc.other-namespace.svc.cluster.local.ï¼ï¼è¿æ ·æ 论 ndots é
ç½®å¦ä½ååï¼è§£æè¡ä¸ºé½æ¯ç¡®å®çã
CoreDNS æ°´å¹³æ©å±
é»è®¤ç CoreDNS Deployment éå¸¸åªæ 2 ä¸ªå¯æ¬ãå¨å¤§è§æ¨¡é群ä¸å¯ä»¥æå¨æ©å±æä½¿ç¨ cluster-proportional-autoscaler æ ¹æ®èç¹æ°èªå¨è°æ´ï¼
# æå¨æ©å±
kubectl -n kube-system scale deployment coredns --replicas=5
cluster-proportional-autoscaler é»è®¤å
¬å¼å¤§çº¦æ¯æ¯ 16 个èç¹å¯¹åº 1 个 CoreDNS 坿¬ï¼å¯ä»¥èªå®ä¹ã
åãæ»ç»ï¼ä¸å¼ å¾å顾 K8s DNS å ¨é¾è·¯
ä» Pod åèµ·ä¸æ¬¡ DNS æ¥è¯¢å°æ¿å°ç»æï¼å®æ´é¾è·¯æ¯ï¼
åºç¨ä»£ç getaddrinfo()
â
glibc resolverï¼è¯» /etc/resolv.confï¼
â
ndots 夿ï¼ç¹æ° < ndots â search domain å±å¼
â
便¬¡å°è¯å search domainï¼A + AAAA å¹¶è¡ï¼
â
NodeLocal DNSCacheï¼169.254.20.10ï¼
â cache miss
CoreDNSï¼10.96.0.10ï¼
â
æä»¶é¾ï¼cache â autopath â kubernetes â forward
â
cluster.local â æ¥å
åç´¢å¼ å¤é¨åå â forward å°ä¸æ¸¸ DNS
â â
è¿å A/AAAA/SRV/CNAME éå½è§£æåè¿å
å ³é®è¦ç¹ï¼
-
ndots:5 æ¯æ§è½ææï¼å¤é¨ååæ¯æ¬¡è§£æé½ä¼è§¦å 4-5 åæ¥è¯¢æ¾å¤§ãå¨é«é¢è°ç¨å¤é¨ API çåºæ¯ä¸ï¼DNS å¼éå¯è½å 请æ±å»¶è¿ç 30% 以ä¸ã
-
ä¼åæ¹æ¡æå±æ¬¡ï¼
- æç®åï¼
dnsConfigä¸éä½ ndots å° 2ï¼é ååºç¨ä»£ç ä¸ä½¿ç¨ FQDN - ä¸çï¼é¨ç½² NodeLocal DNSCacheï¼å©ç¨æ¬å°ç¼ååå°å»¶è¿å CoreDNS è´è½½
- æå½»åºï¼å¯ç¨ autopath æä»¶ï¼ä»æå¡ç«¯æ¶é¤æ¥è¯¢æ¾å¤§
- æç®åï¼
-
conntrack ç«ææ¯çå®åå¨ç bugï¼å¦æçå°é´ææ§ 5 ç§ DNS è¶ æ¶ï¼ç¬¬ä¸ååºåºè¯¥æ¯æ£æ¥
single-request-reopené ç½®æå¯ç¨ NodeLocal DNSCacheã -
ææ¥è·¯å¾æ¯åºå®çï¼nslookup â dig â CoreDNS log â tcpdump â Prometheus ææ ãæè¿ä¸ªé¡ºåºèµ°ï¼å¤§å¤æ° DNS é®é¢é½è½å®ä½ã
-
ExternalDNS éç¯äºå å¤ DNSï¼é群å é CoreDNS èªå¨æ³¨åï¼é群å¤é ExternalDNS èªå¨åæ¥å°å ¬ç½ DNS æä¾åãService å建åï¼å å¤ DNS è®°å½é½èªå¨å°±ä½ã
éå½ Aï¼DNS ææ¥å½ä»¤éæ¥
# --- åºæ¬éªè¯ ---
kubectl exec <pod> -- cat /etc/resolv.conf
kubectl exec <pod> -- nslookup kubernetes.default
kubectl exec <pod> -- dig +short kubernetes.default.svc.cluster.local
kubectl exec <pod> -- dig +search +showsearch api.github.com
# --- CoreDNS ç¶æ ---
kubectl -n kube-system get pods -l k8s-app=kube-dns -o wide
kubectl -n kube-system logs -l k8s-app=kube-dns --tail=50
kubectl -n kube-system get configmap coredns -o jsonpath='{.data.Corefile}'
# --- æ·±å
¥ææ¥ ---
tcpdump -i any -nn port 53 -l 2>/dev/null
kubectl exec <pod> -- dig +trace example.com
kubectl exec <pod> -- dig @8.8.8.8 example.com # ç»è¿ CoreDNS
# --- æ§è½åæ ---
kubectl -n kube-system port-forward svc/kube-dns 9153:9153
curl -s localhost:9153/metrics | grep -E "coredns_(dns_request|cache|forward)"
éå½ Bï¼æ¨èç Corefile é 置模æ¿
# ç产ç¯å¢æ¨èé
ç½®
.:53 {
errors
health {
lameduck 5s
}
ready
# å¯ç¨ autopath åå°æ¥è¯¢æ¾å¤§
autopath @kubernetes
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
policy sequential
}
# ç²¾ç»ç¼åé
ç½®
cache {
success 9984 30
denial 9984 5
prefetch 10 60s 10%
}
loop
reload 10s
loadbalance round_robin
}
éå½ Cï¼dnsConfig ä¼å模æ¿
# 主è¦è°ç¨å¤é¨ API çå·¥ä½è´è½½
apiVersion: v1
kind: Pod
spec:
dnsPolicy: ClusterFirst
dnsConfig:
options:
- name: ndots
value: "2"
- name: single-request-reopen
# å®å
¨ä¸éè¦é群 DNS çå·¥ä½è´è½½ï¼å¦æ¹å¤çä»»å¡ï¼
apiVersion: v1
kind: Pod
spec:
dnsPolicy: "None"
dnsConfig:
nameservers:
- 8.8.8.8
- 8.8.4.4
options:
- name: ndots
value: "1"
åèèµæ
- Kubernetes DNS Specification
- CoreDNS Manual: Plugins
- CoreDNS autopath Plugin
- NodeLocal DNSCache
- ExternalDNS GitHub Repository
- RFC 1035: "Domain Names - Implementation and Specification"
- RFC 6762: "Multicast DNS"
- 5 -- Why 5? Kubernetes DNS ndots Explained
- Racy conntrack and DNS Lookup Timeouts
- Kubernetes ç½ç»æ¨¡å
- Service ä¸ kube-proxy
ç³»å导èª
- ä¸ä¸ç¯ï¼CNI éåå³ç
- è¿åç®å½ï¼Kubernetes ç½ç»æ·±åº¦ç³»å
- ä¸ä¸ç¯ï¼Service è¿é¶
Aitishiku.com