在k8s中运行 netshoot 进行抓包
busybox 虽然很小,但是命令不全,可以使用 netshoot 来抓包,或者 praqma/network-multitool
补充一张 netshoot 的图:
启动一个临时容器,可以调试整个容器网络
kubectl run --generator=run-pod/v1 tmp-shell --rm -i --tty --image nicolaka/netshoot -- /bin/bash
再开一个 shell: kubectl exec -it tmp-shell -- /bin/bash
或者启动一个临时容器,调试主机网络
kubectl run tmp-shell --generator=run-pod/v1 --rm -i --tty --overrides='{"spec": {"hostNetwork": true}}' --image nicolaka/netshoot -- /bin/bash
执行 tcpdump -nn -i any,再另外一个 shell 中,ping一个可解析的地址,抓包结果如下
bash-5.1# tcpdump -nn -i any
tcpdump: data link type LINUX_SLL2
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
05:52:58.482205 eth0 Out IP 10.244.21.244.60478 > 172.16.0.10.53: 32600+ A? eos.h3c.com.default.svc.cluster.local. (55)
05:52:58.482390 eth0 Out IP 10.244.21.244.60478 > 172.16.0.10.53: 32818+ AAAA? eos.h3c.com.default.svc.cluster.local. (55)
05:52:58.483641 eth0 In IP 172.16.0.10.53 > 10.244.21.244.60478: 32600 NXDomain*- 0/1/0 (148)
05:52:58.483657 eth0 In IP 172.16.0.10.53 > 10.244.21.244.60478: 32818 NXDomain*- 0/1/0 (148)
05:52:58.483696 eth0 Out IP 10.244.21.244.48575 > 172.16.0.10.53: 28447+ A? eos.h3c.com.svc.cluster.local. (47)
05:52:58.483797 eth0 Out IP 10.244.21.244.48575 > 172.16.0.10.53: 28617+ AAAA? eos.h3c.com.svc.cluster.local. (47)
05:52:58.485032 eth0 In IP 172.16.0.10.53 > 10.244.21.244.48575: 28447 NXDomain*- 0/1/0 (140)
05:52:58.485044 eth0 In IP 172.16.0.10.53 > 10.244.21.244.48575: 28617 NXDomain*- 0/1/0 (140)
05:52:58.485078 eth0 Out IP 10.244.21.244.45182 > 172.16.0.10.53: 42126+ A? eos.h3c.com.cluster.local. (43)
05:52:58.485144 eth0 Out IP 10.244.21.244.45182 > 172.16.0.10.53: 42288+ AAAA? eos.h3c.com.cluster.local. (43)
05:52:58.485946 eth0 In IP 172.16.0.10.53 > 10.244.21.244.45182: 42288 NXDomain*- 0/1/0 (136)
05:52:58.486019 eth0 In IP 172.16.0.10.53 > 10.244.21.244.45182: 42126 NXDomain*- 0/1/0 (136)
05:52:58.486055 eth0 Out IP 10.244.21.244.58772 > 172.16.0.10.53: 34146+ A? eos.h3c.com. (29)
05:52:58.486119 eth0 Out IP 10.244.21.244.58772 > 172.16.0.10.53: 34350+ AAAA? eos.h3c.com. (29)
05:52:58.488728 eth0 In IP 172.16.0.10.53 > 10.244.21.244.58772: 34146* 1/0/0 A 10.64.252.13 (56)
05:52:58.488744 eth0 In IP 172.16.0.10.53 > 10.244.21.244.58772: 34350* 0/1/0 (99)
05:52:58.489020 eth0 Out IP 10.244.21.244 > 10.64.252.13: ICMP echo request, id 38727, seq 1, length 64
05:52:58.490880 eth0 In IP 10.64.252.13 > 10.244.21.244: ICMP echo reply, id 38727, seq 1, length 64
05:52:58.490951 eth0 Out IP 10.244.21.244.51005 > 172.16.0.10.53: 2157+ PTR? 13.252.64.10.in-addr.arpa. (43)
05:52:58.493904 eth0 In IP 172.16.0.10.53 > 10.244.21.244.51005: 2157 NXDomain* 0/1/0 (121)
05:53:03.487789 eth0 In ARP, Request who-has 10.244.21.244 tell 10.244.21.1, length 28
05:53:03.487792 eth0 Out ARP, Request who-has 10.244.21.1 tell 10.244.21.244, length 28
05:53:03.487797 eth0 Out ARP, Reply 10.244.21.244 is-at 82:ba:03:94:44:9b, length 28
05:53:03.487811 eth0 In ARP, Reply 10.244.21.1 is-at b2:b1:f0:a6:cd:fb, length 28
首先,系统尝试通过DNS解析eos.h3c.com的IP地址。它向DNS服务器(IP地址为172.16.0.10,端口53)发送了一系列的DNS查询请求,尝试解析eos.h3c.com在不同的Kubernetes域中的地址。这些查询包括:
eos.h3c.com.default.svc.cluster.local
eos.h3c.com.svc.cluster.local
eos.h3c.com.cluster.local
eos.h3c.com
对于每个查询,系统都发送了两个请求:一个是A记录(IPv4地址),另一个是AAAA记录(IPv6地址)。
DNS服务器回复了这些查询,大部分查询返回了NXDomain(表示域名不存在)。但是,对于eos.h3c.com的查询,DNS服务器返回了一个A记录,IP地址为10.64.252.13。
接下来,系统使用ICMP协议向IP地址10.64.252.13发送了一个echo请求(ping请求)。这是一个用于测试网络连通性的标准操作。
IP地址10.64.252.13回复了一个ICMP echo回复(ping回复),表示网络连通性良好。
然后,系统尝试通过反向DNS解析(PTR记录)查询IP地址10.64.252.13的主机名。DNS服务器返回了NXDomain,表示没有找到与该IP地址关联的主机名。
最后,系统与网关(IP地址为10.244.21.1)进行了ARP通信,以获取网关的MAC地址。这是一个正常的网络操作,用于在本地网络中找到目标设备的物理地址。
补充:
1 中测试了很多域名地址,这和搜索域配置有关系。
5 可能不是很理解,RTP是一种通过ip反向查询主机名的解析,我们平时执行很多网络分析命令,可以选择是否显示主机名,就是 RTP 通过反查记录的。
6 最后 ARP获取MAC地址
5,6不一定是必须的,看你网络设置
通过容器 Network Namespace 抓包
查看指定 pod 运行在哪个宿主机上:
kubctl describe pod <pod> -n mservice
获得容器的 pid:
docker inspect -f {{.State.Pid}} <container>
进入该容器的 network namespace:
nsenter --target <PID> -n
使用宿主机的
tcpdump
抓包, 指定 eth0 网卡:tcpdump -i eth0 tcp and port 80 -vvv
或者直接抓包并导出到文件:
tcpdump -i eth0 -w /tmp/out.cap
从远程
scp
到本地:scp ipaddr:/tmp/out.cap ./
之后在 Wireshark 中可以打开文件非常直观的查看过滤抓到的数据
k8s DNS 解析过多导致请求慢问题
发现应用程序,如果请求集群外的地址,出现非常慢的情况,就是要等一个2,3秒,很多使用域名做rpc地址的服务都遇到了这个问题,改成svc内部地址就好了,难道是网关太慢?2,3秒可不是慢的问题了,今天通过抓包来详细分析这个问题
这个是dns resolve.conf 配置的问题,pod 可以设置 dns 策略,一般是 dnsPolicy: ClusterFirst
找一个容器,以下是pod内的 resolve.conf 配置:
root@/# cat /etc/resolv.conf
nameserver 172.16.0.10
search eos-system.svc.cluster.local svc.cluster.local cluster.local search h3c.com huawei-3com.com
options ndots:5
nameserver 指定dns解析服务器(nameserver表示解析域名时使用该地址指定的主机为域名服务器。其中域名服务器是按照文件中出现的顺序来查询的,且只有当第一个nameserver没有反应时才查询下面的nameserver,一般不要指定超过3个服务器)
search 是搜索域
option 是配置项,这里配置了 ndots 5
因为k8s默认 ndots 5,所以会生成3个搜索域 eos-system.svc.cluster.local svc.cluster.local cluster.local,然后从主机拿2个 h3c.com huawei-3com.com
此时主机的 cat /etc/resolv.conf 配置为:
search h3c.com huawei-3com.com h3c.huawei-3com.com srv.huawei-3com.com ipa-h3c.com
nameserver 10.72.66.37
nameserver 10.72.66.36
如果我们注释了search:
#search h3c.com huawei-3com.com h3c.huawei-3com.com srv.huawei-3com.com ipa-h3c.com
nameserver 10.72.66.37
nameserver 10.72.66.36
那么容器内部为:
nameserver 172.16.0.10
search eos-system.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
回到正题,现在搜索域如下:
search eos-mid-public.svc.cluster.local svc.cluster.local cluster.local search h3c.com huawei-3com.com
在这个配置下,如果我们请求非k8s内部地址,就会拼接这样的地址去解析dns地址
eos.h3c.com.eos-system.svc.cluster.local
eos.h3c.com.svc.cluster.local
eos.h3c.com.cluster.local
eos.h3c.com.h3c.com
eos.h3c.com.huawei-3com.com
eos.h3c.com
到第六次,终于拿到解析地址了,大家可以抓包验证一下
例子:
给业务容器,加一个 netshoot,进入到 netshoot中执行抓包: kubectl -n eos-mid-public exec -it data-ease-v1-6b68dc7599-9rwm5 -c netshoot -- sh
Search Line limits were exceeded 错误事件
Search Line limits were exceeded, some search paths have been omitted, the applied search line is: eos-system.svc.cluster.local svc.cluster.local cluster.local search h3c.com huawei-3com.com
你会在pod事件在看到这个异常,主要还是搜索域配置问题,下面是比较官方一点的解答:
在/etc/resolv.conf
文件中,search
关键字用于指定一系列的搜索域,这些域会被用于DNS解析没有指定完全限定域名(FQDN)的主机名。如果文件中有多个search
行,通常只有最后一个search
行会被使用。
在这个例子中,有两个search
关键字,每个关键字后面都跟着一些域名。这种写法在语法上是合法的,但实际上,只有最后一个search
行(即search h3c.com huawei-3com.com h3c.huawei-3com.com srv.huawei-3com.com ipa-h3c.com
)会被使用。
然而,看到的警告信息表明,搜索路径的长度超过了限制,因此一些搜索路径被省略了。这个限制是由Linux系统的DNS解析器强制的,搜索路径的总长度(包括所有的域名和空格)不能超过256个字符,搜索路径中的域名数量不能超过6个。
在这个例子中,搜索路径的长度可能超过了这个限制,因此Kubernetes只保留了前几个域名,并显示了这个警告信息。被省略的域名将不会被用于DNS解析。
如果需要使用被省略的域名进行DNS解析,可能需要调整搜索路径,使其长度不超过限制。例如,可以移除一些不需要的域名,或者将一些域名缩短。请注意,这可能需要修改Kubernetes的DNS配置,或者在创建Pod时指定自定义的DNS策略。
也就是说搜索域太长了,主要还是主机的配置加上k8s自身的配置导致的。另外通过抓包,发现2个search也是有作用的:
tcpdump -nn -i any host 172.16.0.10
tcpdump: data link type LINUX_SLL2
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
07:27:48.006823 eth0 Out IP 10.244.38.246.52863 > 172.16.0.10.53: 39793+ A? eos.h3c.com.eos-mid-public.svc.cluster.local. (62)
07:27:48.007042 eth0 Out IP 10.244.38.246.52863 > 172.16.0.10.53: 40184+ AAAA? eos.h3c.com.eos-mid-public.svc.cluster.local. (62)
07:27:48.008818 eth0 In IP 172.16.0.10.53 > 10.244.38.246.52863: 40184 NXDomain*- 0/1/0 (155)
07:27:48.008830 eth0 In IP 172.16.0.10.53 > 10.244.38.246.52863: 39793 NXDomain*- 0/1/0 (155)
07:27:48.008867 eth0 Out IP 10.244.38.246.38655 > 172.16.0.10.53: 5074+ A? eos.h3c.com.svc.cluster.local. (47)
07:27:48.009010 eth0 Out IP 10.244.38.246.38655 > 172.16.0.10.53: 5321+ AAAA? eos.h3c.com.svc.cluster.local. (47)
07:27:48.009886 eth0 In IP 172.16.0.10.53 > 10.244.38.246.38655: 5321 NXDomain*- 0/1/0 (140)
07:27:48.009892 eth0 In IP 172.16.0.10.53 > 10.244.38.246.38655: 5074 NXDomain*- 0/1/0 (140)
07:27:48.009929 eth0 Out IP 10.244.38.246.49129 > 172.16.0.10.53: 19272+ A? eos.h3c.com.cluster.local. (43)
07:27:48.009994 eth0 Out IP 10.244.38.246.49129 > 172.16.0.10.53: 19552+ AAAA? eos.h3c.com.cluster.local. (43)
07:27:48.010661 eth0 In IP 172.16.0.10.53 > 10.244.38.246.49129: 19552 NXDomain*- 0/1/0 (136)
07:27:48.010688 eth0 In IP 172.16.0.10.53 > 10.244.38.246.49129: 19272 NXDomain*- 0/1/0 (136)
07:27:48.010719 eth0 Out IP 10.244.38.246.58992 > 172.16.0.10.53: 25097+ A? eos.h3c.com.search. (36)
07:27:48.010780 eth0 Out IP 10.244.38.246.58992 > 172.16.0.10.53: 25300+ AAAA? eos.h3c.com.search. (36)
07:27:50.014100 eth0 In IP 172.16.0.10.53 > 10.244.38.246.58992: 25300 NXDomain 0/1/0 (143)
07:27:50.014143 eth0 In IP 172.16.0.10.53 > 10.244.38.246.58992: 25097 NXDomain 0/1/0 (143)
07:27:50.014200 eth0 Out IP 10.244.38.246.33191 > 172.16.0.10.53: 7012+ A? eos.h3c.com.h3c.com. (37)
07:27:50.014425 eth0 Out IP 10.244.38.246.33191 > 172.16.0.10.53: 7264+ AAAA? eos.h3c.com.h3c.com. (37)
07:27:50.018603 eth0 In IP 172.16.0.10.53 > 10.244.38.246.33191: 7012 NXDomain* 0/1/0 (107)
07:27:50.018619 eth0 In IP 172.16.0.10.53 > 10.244.38.246.33191: 7264 NXDomain* 0/1/0 (107)
07:27:50.018666 eth0 Out IP 10.244.38.246.58819 > 172.16.0.10.53: 34729+ A? eos.h3c.com.huawei-3com.com. (45)
07:27:50.018755 eth0 Out IP 10.244.38.246.58819 > 172.16.0.10.53: 34926+ AAAA? eos.h3c.com.huawei-3com.com. (45)
07:27:50.020462 eth0 In IP 172.16.0.10.53 > 10.244.38.246.58819: 34729 NXDomain* 0/1/0 (147)
07:27:50.021529 eth0 In IP 172.16.0.10.53 > 10.244.38.246.58819: 34926 NXDomain* 0/1/0 (147)
07:27:50.021571 eth0 Out IP 10.244.38.246.52231 > 172.16.0.10.53: 60607+ A? eos.h3c.com. (29)
07:27:50.021644 eth0 Out IP 10.244.38.246.52231 > 172.16.0.10.53: 60831+ AAAA? eos.h3c.com. (29)
07:27:50.023140 eth0 In IP 172.16.0.10.53 > 10.244.38.246.52231: 60831* 0/1/0 (99)
07:27:50.024305 eth0 In IP 172.16.0.10.53 > 10.244.38.246.52231: 60607* 1/0/0 A 10.64.252.13 (56)
其实这个影响特别大,在多次ping 测试中,至少要等待2到3秒以后,才开始响应 ICMP 请求,时间都花在解析域名上了
把刚才那个pod,启动2个实例,然后另外一个实例,注释了主机的搜索域,现在的搜索域配置为
search eos-mid-public.svc.cluster.local svc.cluster.local cluster.local
再次抓包,并且测试ping,发现很快就响应ICMP了,因为少了 h3c.com huawei-3com.com
,这2个不是集群内部的,之前的2到3秒的耗时就是因为拼接解析地址,然后耗时在这2个上
~ # tcpdump -nn -i any host 172.16.0.10
tcpdump: data link type LINUX_SLL2
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
07:32:45.904074 eth0 Out IP 10.244.9.4.37992 > 172.16.0.10.53: 61923+ A? eos.h3c.com.eos-mid-public.svc.cluster.local. (62)
07:32:45.904448 eth0 Out IP 10.244.9.4.37992 > 172.16.0.10.53: 62647+ AAAA? eos.h3c.com.eos-mid-public.svc.cluster.local. (62)
07:32:45.906207 eth0 In IP 172.16.0.10.53 > 10.244.9.4.37992: 62647 NXDomain*- 0/1/0 (155)
07:32:45.906213 eth0 In IP 172.16.0.10.53 > 10.244.9.4.37992: 61923 NXDomain*- 0/1/0 (155)
07:32:45.906318 eth0 Out IP 10.244.9.4.44893 > 172.16.0.10.53: 491+ A? eos.h3c.com.svc.cluster.local. (47)
07:32:45.906475 eth0 Out IP 10.244.9.4.44893 > 172.16.0.10.53: 854+ AAAA? eos.h3c.com.svc.cluster.local. (47)
07:32:45.908170 eth0 In IP 172.16.0.10.53 > 10.244.9.4.44893: 854 NXDomain*- 0/1/0 (140)
07:32:45.908177 eth0 In IP 172.16.0.10.53 > 10.244.9.4.44893: 491 NXDomain*- 0/1/0 (140)
07:32:45.908284 eth0 Out IP 10.244.9.4.33141 > 172.16.0.10.53: 64130+ A? eos.h3c.com.cluster.local. (43)
07:32:45.908483 eth0 Out IP 10.244.9.4.33141 > 172.16.0.10.53: 64592+ AAAA? eos.h3c.com.cluster.local. (43)
07:32:45.909628 eth0 In IP 172.16.0.10.53 > 10.244.9.4.33141: 64592 NXDomain*- 0/1/0 (136)
07:32:45.909634 eth0 In IP 172.16.0.10.53 > 10.244.9.4.33141: 64130 NXDomain*- 0/1/0 (136)
07:32:45.909717 eth0 Out IP 10.244.9.4.58102 > 172.16.0.10.53: 62819+ A? eos.h3c.com. (29)
07:32:45.909894 eth0 Out IP 10.244.9.4.58102 > 172.16.0.10.53: 63062+ AAAA? eos.h3c.com. (29)
07:32:45.913220 eth0 In IP 172.16.0.10.53 > 10.244.9.4.58102: 63062* 0/1/0 (99)
07:32:45.913460 eth0 In IP 172.16.0.10.53 > 10.244.9.4.58102: 62819* 1/0/0 A 10.64.252.13 (56)
如果是请求集群内部地址呢?ping 一个 eos-project-blue.eos-system
~ # tcpdump -nn -i any host 172.16.0.10
tcpdump: data link type LINUX_SLL2
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
07:40:13.472584 eth0 Out IP 10.244.9.4.59303 > 172.16.0.10.53: 23490+ A? eos-project-blue.eos-system.eos-mid-public.svc.cluster.local. (78)
07:40:13.472962 eth0 Out IP 10.244.9.4.59303 > 172.16.0.10.53: 24048+ AAAA? eos-project-blue.eos-system.eos-mid-public.svc.cluster.local. (78)
07:40:13.474561 eth0 In IP 172.16.0.10.53 > 10.244.9.4.59303: 23490 NXDomain*- 0/1/0 (171)
07:40:13.474614 eth0 In IP 172.16.0.10.53 > 10.244.9.4.59303: 24048 NXDomain*- 0/1/0 (171)
07:40:13.474666 eth0 Out IP 10.244.9.4.46662 > 172.16.0.10.53: 35395+ A? eos-project-blue.eos-system.svc.cluster.local. (63)
07:40:13.475014 eth0 Out IP 10.244.9.4.46662 > 172.16.0.10.53: 35807+ AAAA? eos-project-blue.eos-system.svc.cluster.local. (63)
07:40:13.476326 eth0 In IP 172.16.0.10.53 > 10.244.9.4.46662: 35807*- 0/1/0 (156)
07:40:13.476331 eth0 In IP 172.16.0.10.53 > 10.244.9.4.46662: 35395*- 1/0/0 A 172.16.162.110 (124)
由于svc不响应ICMP协议,但是不影响我们测试域名解析,抓包中看到第二次就解析到了
如何解决?
我们搞懂了这个问题的原理,知道是因为主机配置了搜索域,那么根据自己集群网络情况来,这里我们用一个比较简单的办法,调整搜索次数,在yaml中,指定dns配置
spec:
dnsConfig:
options:
- name: ndots
value: "2"
发布后,新的容器已经启动,配置也变化了,虽然搜索域还是5个,但是 ndots:2 调整了
~ # cat /etc/resolv.conf
nameserver 172.16.0.10
search eos-mid-public.svc.cluster.local svc.cluster.local cluster.local search h3c.com huawei-3com.com
options ndots:2
再次请求抓包,测试了 ping 集群外,和集群内
~ # tcpdump -nn -i any host 172.16.0.10
tcpdump: data link type LINUX_SLL2
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
08:04:19.628042 eth0 Out IP 10.244.38.247.38433 > 172.16.0.10.53: 49804+ A? eos.h3c.com. (29)
08:04:19.628242 eth0 Out IP 10.244.38.247.38433 > 172.16.0.10.53: 50094+ AAAA? eos.h3c.com. (29)
08:04:19.629431 eth0 In IP 172.16.0.10.53 > 10.244.38.247.38433: 49804* 1/0/0 A 10.64.252.13 (56)
08:04:19.629443 eth0 In IP 172.16.0.10.53 > 10.244.38.247.38433: 50094* 0/1/0 (99)
08:04:19.631592 eth0 Out IP 10.244.38.247.54515 > 172.16.0.10.53: 3396+ PTR? 13.252.64.10.in-addr.arpa. (43)
08:04:19.634487 eth0 In IP 172.16.0.10.53 > 10.244.38.247.54515: 3396 NXDomain* 0/1/0 (121)
08:04:43.561728 eth0 Out IP 10.244.38.247.33567 > 172.16.0.10.53: 60025+ A? eos-project-blue.eos-system.eos-mid-public.svc.cluster.local. (78)
08:04:43.561911 eth0 Out IP 10.244.38.247.33567 > 172.16.0.10.53: 60442+ AAAA? eos-project-blue.eos-system.eos-mid-public.svc.cluster.local. (78)
08:04:43.564030 eth0 In IP 172.16.0.10.53 > 10.244.38.247.33567: 60442 NXDomain*- 0/1/0 (171)
08:04:43.564045 eth0 In IP 172.16.0.10.53 > 10.244.38.247.33567: 60025 NXDomain*- 0/1/0 (171)
08:04:43.564096 eth0 Out IP 10.244.38.247.50751 > 172.16.0.10.53: 16478+ A? eos-project-blue.eos-system.svc.cluster.local. (63)
08:04:43.564181 eth0 Out IP 10.244.38.247.50751 > 172.16.0.10.53: 16886+ AAAA? eos-project-blue.eos-system.svc.cluster.local. (63)
08:04:43.564904 eth0 In IP 172.16.0.10.53 > 10.244.38.247.50751: 16886*- 0/1/0 (156)
08:04:43.564913 eth0 In IP 172.16.0.10.53 > 10.244.38.247.50751: 16478*- 1/0/0 A 172.16.162.110 (124)
发现集群外的,直接返回了,这里要特别注意ndots:2 不是说在搜索域中拼接2次的意思。
在进行域名的 DNS 解析时,操作系统先判断其是否是一个 FQDN(Fully qualified domain name,即完整域名,指以 . 结尾的),如果是,则会直接查询 DNS 服务器;如果不是,则就要根据 search 和 ndots 的设置进行 FQDN 的拼接再将其发到 DNS 服务器进行解析。
ndots 表示的是完整域名中必须出现的 . 的个数,如果域名中的 . 的个数不小于 ndots,则该域名会被认为是一个 FQDN,操作系统会直接将其发给 DNS 服务器进行查询;否则,操作系统会在 search 搜索域中依次查询。
例如上面的例子,ndots 为 5,查询的域名 eos.h3c.com 不以 . 结尾,且 . 的个数少于 5,因此操作系统会依次在 default.svc.cluster.local svc.cluster.local cluster.local 三个域中进行搜索,这 3 个搜索域都是由 Kubernetes 注入的。
所以情况就是:
eos-project-blue.eos-system < ndots:2 在搜索域中找,第二个找到
eos.h3c.com > ndots:2,直接请求 eos.h3c.com,不再去搜索域中拼接地址
Kubernetes 为什么要使用搜索域?
目的是为了使 Pod 可以解析内部域名,比如通过 Service name 访问 Service。
例如 default namespace 下的 Pod a 需要访问同 namespace 中的 Service service-b 时,直接使用 service-b 就可以访问了,这就是通过在 default.svc.cluster.local 搜索域中搜索完成的。同理 svc.cluster.local 支持了对不同 namespace 下的 Service 访问,如 service-c.sre(意为 sre namespace 下的 service-c)。
Kubernetes 默认配置下 ndots 的值是 5,其原因官方在 issue 33554 中做了解释:
Kubernetes 需要一个标志来识别集群内的域名,因此 svc 是必须包含在域名内的。
有些人需要配置集群 $zone 的后缀以支持多集群,所以一个完整的 Service 的域名为 $service.$namespace.svc.$zone,为了不在代码中配置完整的域名,就需要设置 ndots 和 search。
同 namespace 下的 Service 的请求是最常用的,因此需要解析 $service,此时需 ndots >= 1,且 search 列表中第一个应为 $namespace.D.$zone。
跨 namespace 的 Service 也经常会被请求,因此需要解析 $service.$namespace,此时需 ndots >= 2,且 search 列表中第二个应为 svc.$zone。
为了解析 $service.$namespace.svc,此时需 ndots >= 3,且 search 列表包含 $zone。
在 Kubernetes 1.4 之前,StatefulSet 为 PetSet,当每个 Pet 被创建时,它会获得一个匹配的 DNS 子域,域格式为 $petname.$service.$namespace.svc.$zone,此时需 ndots >= 4。
Kubernetes 还支持 SRV 记录,因此 _$port.$proto.$service.$namespace.svc.$zone 需要可以解析,此时 ndots = 5。
这就是为什么 ndots 为 5。总结来说是为了支持更复杂的 Pod 内的域名解析,但通常情况下群脉只会用到同 namespace 下的 Service(形如 service-b)和跨 namespace 下 Service(形如 service-c.sre)的访问,因此 ndots 的默认值设为 2 便可满足业务需求并避免大量无效的解析请求。
或者调整配置,之前我们是因为2个不在集群内的地址,解析耗时,可以快速失败,或者修改主机搜索域(一般业务都用不到这个功能,可以改主机搜索域)
options 其他选项的补充说明
timeout:设置等待 DNS 服务器返回的超时时间,默认为 5,单位为秒。这个有点高了,为了 fail fast,我们设为了 2。
attempt:解析失败时的重试次数,默认为 2。为了提高成功率,我们设为了 3。
single-request:默认情况下会并行执行 IPv4 and IPv6 的解析,但某些 DNS 服务器可能无法正确处理这些查询使请求超时。该选项可以禁用此行为,并依次执行 IPv6 and IPv4 的解析。
rotate:采用轮询方式访问 nameserver。
no-check-names:禁止对传入的主机名和邮件地址进行无效字符检查。
use-vc:强制使用 TCP 进行 DNS 解析。
Kubernetes DNS 策略
None
表示空的DNS设置
这种方式一般用于想要自定义 DNS 配置的场景,而且,往往需要和 dnsConfig 配合一起使用达到自定义 DNS 的目的。
Default
有人说 Default 的方式,是使用宿主机的方式,这种说法并不准确。
这种方式,其实是,让 kubelet 来决定使用何种 DNS 策略。而 kubelet 默认的方式,就是使用宿主机的 /etc/resolv.conf(可能这就是有人说使用宿主机的DNS策略的方式吧),但是,kubelet 是可以灵活来配置使用什么文件来进行DNS策略的,我们完全可以使用 kubelet 的参数:–resolv-conf=/etc/resolv.conf 来决定你的DNS解析文件地址。
ClusterFirst
这种方式,表示 POD 内的 DNS 使用集群中配置的 DNS 服务,简单来说,就是使用 Kubernetes 中 kubedns 或 coredns 服务进行域名解析。如果解析不成功,才会使用宿主机的 DNS 配置进行解析。
ClusterFirstWithHostNet
在某些场景下,我们的 POD 是用 HOST 模式启动的(HOST模式,是共享宿主机网络的),一旦用 HOST 模式,表示这个 POD 中的所有容器,都要使用宿主机的 /etc/resolv.conf 配置进行DNS查询,但如果你想使用了 HOST 模式,还继续使用 Kubernetes 的DNS服务,那就将 dnsPolicy 设置为 ClusterFirstWithHostNet。
参考资料:
Kubernetes Pod DNS 解析优化 - 知乎 (zhihu.com)
coredns 原理
域名服务器,本质就是一个key-value记录,在上面的分析中,外部地址在k8s内访问是能解析的,从抓包来看,都是请求到了 coredns。
k8s默认pod的dns策略是 ClusterFirst,也就是指向 coredns 的 svc 地址,全部的域名解析请求都交给 coredns,而 coredns的dns策略是 Default,意思是从所在Node继承DNS服务器,对于无法解析的外部域名,kube-dns会继续向集群外部的dns进行查询,kube-dns只能解析集群内部地址,而集群外部地址应该发给外部DNS服务器进行解析。
所以k8s能否解析外网,一般看coredns主机的配置,或者我们修改一个指定配置,另外在 coredns 的configmap中,还可以添加自定义解析记录,可以根据实际情况,灵活配置
kubectl 插件 sniff 抓包
sniff 是一个集成tcpdump等工具的抓包插件,安装它,首先要先安装krew,一个类似brew的包管理工具
(
set -x; cd "$(mktemp -d)" &&
OS="$(uname | tr '[:upper:]' '[:lower:]')" &&
ARCH="$(uname -m | sed -e 's/x86_64/amd64/' -e 's/\(arm\)\(64\)\?.*/\1\2/' -e 's/aarch64$/arm64/')" &&
KREW="krew-${OS}_${ARCH}" &&
curl -fsSLO "http://github.com/kubernetes-sigs/krew/releases/latest/download/${KREW}.tar.gz" &&
tar zxvf "${KREW}.tar.gz" &&
./"${KREW}" install krew
)
根据提示再配置一下环境变量,然后按照 sniff
kubectl krew install sniff
参考资料:
eldadru/ksniff: Kubectl plugin to ease sniffing on kubernetes pods using tcpdump and wireshark (github.com)
一文读懂网络报文分析神器Tshark: 100+张图、100+个示例轻松掌握-腾讯云开发者社区-腾讯云 (tencent.com)
重定向Kubernetes pod中的tcpdump输出 - charlieroro - 博客园 (cnblogs.com)
评论区