本文結(jié)合域名請(qǐng)求慢的問(wèn)題弹谁,從虛擬網(wǎng)絡(luò)定位到域名解析,根據(jù) coredns 添加域名后綴的機(jī)制芝加,定位 coredns 解析慢的根因备畦。
1.問(wèn)題現(xiàn)象
背景:項(xiàng)目是微服務(wù) + flink,其中 flink 采用 k8s session standalone 的部署模式仁期。
問(wèn)題:微服務(wù)通過(guò) flink restful api 啟動(dòng)作業(yè)的平均時(shí)長(zhǎng)超過(guò) 40s桑驱,導(dǎo)致客戶(hù)端超時(shí)和作業(yè)失聯(lián)。
#為了排除 flink 內(nèi)部啟動(dòng)作業(yè)耗時(shí)因素跛蛋,使用 /jobs/overview 測(cè)試微服務(wù)到 flink 的網(wǎng)絡(luò)耗時(shí)
$time curl http://flink-jobmanager.default.svc.cluster.local:8091/jobs/overview
{"jobs":[{"jid":"0bf232afb7febbb8b070221833ddb99c","name":"TopSpeedWindowing","state":"RUNNING","start-time":1631687894914,"end-time":-1,"duration":701488855,"last-modification":1631687895895,"tasks":{"total":3,"created":0,"scheduled":0,"deploying":0,"running":3,"finished":0,"canceling":0,"canceled":0,"failed":0,"reconciling":0}}]}
real 0m10.528s
user 0m0.004s
sys 0m0.005s
2.根因定位
定位思路:理解 Service 【K8s 精選】Kubernetes Service 介紹熬的。在使用 service name 即域名的場(chǎng)景下,Client Pod3 首先拿著域名去 coredns 解析成 ClusterIP赊级,接著去請(qǐng)求 ClusterIP押框,最后通過(guò) Kube-Proxy 把請(qǐng)求轉(zhuǎn)發(fā)到目標(biāo)后端 Pod。
2.1 虛擬化網(wǎng)絡(luò)分析
2.1.1 curl 命令詳解
參考curl命令詳解理逊,例如
curl --header "Content-Type: application/json" -X POST --data '{"text":"germany"}' https://labs.tib.eu/falcon/api?mode=short
2.1.2 curl 獲取 http 各階段的響應(yīng)時(shí)間
參考通過(guò)curl得到http各階段的響應(yīng)時(shí)間
① time_namelookup:DNS解析時(shí)間
② time_connect:連接時(shí)間橡伞,從請(qǐng)求開(kāi)始到 DNS 解析完畢所用時(shí)間。單純的連接時(shí)間=time_connect - time_namelookup
③ time_appconnect:建立完成時(shí)間挡鞍,例如 SSL/SSH 等建立連接或者完成三次握手的時(shí)間骑歹。
④ time_redirect:重定向時(shí)間,包括最后一次傳輸前的幾次重定向的 DNS 解析墨微、連接道媚、預(yù)傳輸、傳輸時(shí)間。
⑤ time_pretransfer: 從開(kāi)始到準(zhǔn)備傳輸?shù)臅r(shí)間最域。
⑥ time_starttransfer:開(kāi)始傳輸時(shí)間谴分。在 client 發(fā)出請(qǐng)求后,服務(wù)端返回?cái)?shù)據(jù)的第一個(gè)字節(jié)所用的時(shí)間镀脂。
進(jìn)入業(yè)務(wù)容器牺蹄,編輯完獲取數(shù)據(jù)的格式后,執(zhí)行 curl 命令薄翅。
/dev/null 表示空設(shè)備沙兰,即丟棄一切寫(xiě)入的數(shù)據(jù),但顯示寫(xiě)入操作成功翘魄。
$vim curl-format.txt
time_namelookup: %{time_namelookup}\n
time_connect: %{time_connect}\n
time_appconnect: %{time_appconnect}\n
time_redirect: %{time_redirect}\n
time_pretransfer: %{time_pretransfer}\n
time_starttransfer: %{time_starttransfer}\n
----------\n
time_total: %{time_total}\n
$kubectl get svc |grep flink
flink-jobmanager ClusterIP 10.96.0.123 <none> 8123/TCP,8124/TCP,8091/TCP 4d22h
# 首先使用 ClusterIP 測(cè)試接口調(diào)用時(shí)長(zhǎng)
$curl -w "@curl-format.txt" -o /dev/null -l "http://10.96.0.123:8091/jobs/overview"
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 660 100 660 0 0 81865 0 --:--:-- --:--:-- --:--:-- 94285
time_namelookup: 0.004
time_connect: 0.005
time_appconnect: 0.000
time_redirect: 0.000
time_pretransfer: 0.005
time_starttransfer: 0.008
----------
time_total: 0.008
# 然后使用 service name 即域名測(cè)試接口調(diào)用時(shí)長(zhǎng)
$curl -w "@curl-format.txt" -o /dev/null -l "http://flink-jobmanager.default.svc.cluster.local:8091/jobs/overview"
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 660 100 660 0 0 62 0 0:00:10 0:00:10 --:--:-- 164
time_melookup: 10.516
time_connect: 10.517
time_appconnect: 0.000
time_redirect: 0.000
time_pretransfer: 10.517
time_starttransfer: 10.520
----------
time_total: 10.520
對(duì)比 ClusterIP 和 service name 的接口調(diào)用時(shí)長(zhǎng)鼎天,由 time_namelookup 可知 DNS 解析時(shí)間長(zhǎng)。
2.2 域名解析分析
2.2.1 外部分析 - coredns 解析域名
$kubectl logs coredns-66509f5cf2-km1q4 -nkube-system
2021-09-23T01:54:04.590Z [ERROR] plugin/errors: 2 flink-jobmanager.default.svc.cluster.local.openstacklocal. A: read udp 10.244.0.18:32960->100.79.1.250:53: i/o timeout
2021-09-23T01:54:09.592Z [ERROR] plugin/errors: 2 flink-jobmanager.default.svc.cluster.local.openstacklocal. A: read udp 10.244.0.18:59978->100.79.1.250:53: i/o timeout
2021-09-23T01:56:00.609Z [ERROR] plugin/errors: 2 flink-jobmanager.default.svc.cluster.local.openstacklocal. AAAA: read udp 10.244.2.19:41797->100.79.1.250:53: i/o timeout
2021-09-23T01:56:02.610Z [ERROR] plugin/errors: 2 flink-jobmanager.default.svc.cluster.local.openstacklocal. AAAA: read udp 10.244.2.19:48375->100.79.1.250:53: i/o timeout
由 coredns 后臺(tái)關(guān)鍵日志 A: read udp xxx->xxx: i/o timeout可知 IPV4 解析超時(shí)暑竟,AAAA: read udp xxx->xxx: i/o timeout 可知 IPV6解析也超時(shí)斋射。
IPV4 和 IPV6 耗時(shí)對(duì)比
# IPV4 請(qǐng)求耗時(shí)
$curl -4 -w "@curl-format.txt" -o /dev/null -l "http://flink-jobmanager.default.svc.cluster.local:8091/jobs/overview"
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- 0:00:03 --:--:-- 0
time_melookup: 0.000
time_connect: 0.000
time_appconnect: 0.000
time_redirect: 0.000
time_pretransfer: 0.000
time_starttransfer: 0.000
----------
time_total: 3.510
# IPV6 請(qǐng)求耗時(shí)
$curl -6 -w "@curl-format.txt" -o /dev/null -l "http://flink-jobmanager.default.svc.cluster.local:8091/jobs/overview"
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 660 100 660 0 0 146 0 0:00:04 0:00:04 --:--:-- 146
time_melookup: 4.511
time_connect: 4.512
time_appconnect: 0.000
time_redirect: 0.000
time_pretransfer: 4.512
time_starttransfer: 4.515
----------
time_total: 4.515
結(jié)論:IPV6 解析比 IPV4 多耗時(shí)約 20%,說(shuō)明 IPV6 對(duì)域名解析有一定的影響但荤,建議 coredns 關(guān)閉 IPV6 解析罗岖。然而直接用 IPV4 解析也耗時(shí) 3s+,需要進(jìn)一步對(duì)容器內(nèi)部進(jìn)行抓包分析腹躁。
建議:如果 IPV6 模式?jīng)]有使用桑包,可以關(guān)閉。
2.2.2 內(nèi)部分析 - 容器內(nèi)部解析域名
通過(guò)宿主機(jī)抓起 Pod 網(wǎng)絡(luò)數(shù)據(jù)報(bào)
nsenter 可以進(jìn)入 Pod 容器 net 命名空間纺非,同時(shí)提供一個(gè)快速進(jìn)入 Pod 容器 net 命名空間腳本捡多,可以參考在容器環(huán)境使用 tcpdump 抓包
# 步驟1 獲取容器的 pid
$ docker ps |grep flink-jobmanager-7b58565dc8-msgpp
1386ce6244ae 192.168.31.37:5000/flink "bash -cx /opt/flink/s…" 3 weeks ago Up 3 weeks k8s_flink-jobmanager-7b58565dc8-msgpp_4b6d15d8-7b54-41fb-bf46-ffa91aa33963_0
$ docker inspect 1386ce6244ae| grep Pid
"Pid": 63046,
"PidMode": "",
"PidsLimit": 0,
# 步驟2 根據(jù) pid 執(zhí)行命令
# 53 是 corends 域名解析端口
$ nsenter -t 63046 -n
$ tcpdump 'src host 10.244.3.81 and src port 8091'
# 步驟3 打開(kāi)新窗口,進(jìn)入容器并執(zhí)行上述的 curl 命令
$curl -w "@curl-format.txt" -o /dev/null -l "http://flink-jobmanager.default.svc.cluster.local:8091/jobs/overview"
下載 tcpdump 的抓包數(shù)據(jù) xxx.pcap铐炫, 利用 wireshark 分析 tcpdump 報(bào)文,其結(jié)果如下:
① flink-jobmanager.default.svc.cluster.local 域名解析成 ip 的時(shí)間約 10s
② 在域名解析的過(guò)程中蒜焊,在 flink-jobmanager.default.svc.cluster.local 的基礎(chǔ)上倒信,其后綴依次添加default.svc.cluster.local、svc.cluster.local泳梆、cluster.local鳖悠、openstacklocal
查看業(yè)務(wù)容器中的配置 /etc/resolv.conf,發(fā)現(xiàn)上述的后綴恰好是search 內(nèi)容优妙。
$ cat /etc/resolv.conf
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local openstacklocal
options ndots:5 single-request-reopen
3 容器 /etc/resolv.conf 配置分析
容器 /etc/resolv.conf 配置如下:
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local openstacklocal
options ndots:5 single-request-reopen
① nameserver
resolv.conf 文件的第一行是 nameserver乘综,內(nèi)容是 coredns 的 ClusterIP
$kubectl get svc -nkube-system |grep dns
kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP 167d
② search
resolv.conf 文件的第一行是 search。當(dāng)域名解析的時(shí)候套硼,將域名依次添加后綴卡辰,例如:
flink-jobmanager.default.svc.cluster.local.default.svc.cluster.local
flink-jobmanager.default.svc.cluster.local.svc.cluster.local
flink-jobmanager.default.svc.cluster.local.cluster.local
flink-jobmanager.default.svc.cluster.local.openstacklocal
③ options
resolv.conf 文件的第一行是 options 其它項(xiàng),常見(jiàn)配置是 ndots。ndots: 5 表示如果域名包含的 "." 少于5個(gè)九妈,則先添加 search 后綴反砌,再使用絕對(duì)域名;如果域名包含的 "." 大于等于5個(gè)萌朱,則先使用絕對(duì)域名宴树,再添加 search 后綴。
#樣例1 域名a.b.c.d.e晶疼,則域名的順序如下
a.b.c.d.e.default.svc.cluster.local
a.b.c.d.e.svc.cluster.local
a.b.c.d.e.cluster.local
a.b.c.d.e.openstacklocal
a.b.c.d.e
#樣例2 域名a.b.c.d.e.f酒贬,則域名的順序如下
a.b.c.d.e.f
a.b.c.d.e.f.default.svc.cluster.local
a.b.c.d.e.f.svc.cluster.local
a.b.c.d.e.f.cluster.local
a.b.c.d.e.f.openstacklocal
結(jié)合 flink-jobmanager.default.svc.cluster.local 域名解析慢的問(wèn)題,由于該域名包含 "." 等于 4翠霍,即少于5锭吨,所以會(huì)首先依次添加后綴default.svc.cluster.local、svc.cluster.local壶运、cluster.local openstacklocal耐齐。因此,解決方案有 ①flink-jobmanager.default.svc.cluster.local 修改為 flink-jobmanager蒋情;②ndots: 5 修改為 ndots: 4埠况。
4 解決方案
4.1 使用簡(jiǎn)潔域名
將訪問(wèn)的域名從flink-jobmanager.default.svc.cluster.local 修改為 flink-jobmanager,效果如下:
curl -w "@curl-format.txt" -o /dev/null -l "http://flink-jobmanager:8091/jobs/overview"
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 660 100 660 0 0 82038 0 --:--:-- --:--:-- --:--:-- 94285
time_melookup: 0.004
time_connect: 0.005
time_appconnect: 0.000
time_redirect: 0.000
time_pretransfer: 0.005
time_starttransfer: 0.008
----------
time_total: 0.008
4.2 resolv.conf 配置更改
將 /etc/resolv.conf 的 options 配置 ndots: 5 修改為 ndots: 4棵癣,或者修改 deployment 的 yaml 配置辕翰,效果如下:
# 修改 deployment 的 yaml 配置
# spec.spec.dnsConfig.options[].ndots
dnsConfig:
options:
- name: ndots
value: 4
- name: single-request-reopen
curl -w "@curl-format.txt" -o /dev/null -l "http://flink-jobmanager.default.svc.cluster.local:8091/jobs/overview"
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 660 100 660 0 0 82038 0 --:--:-- --:--:-- --:--:-- 94285
time_melookup: 0.005
time_connect: 0.006
time_appconnect: 0.000
time_redirect: 0.000
time_pretransfer: 0.005
time_starttransfer: 0.008
----------
time_total: 0.008