近期开发的一个功能,需要在k8s集群容器环境中调用公司内部api,api提供了内网域名,解析内网域名异常导致请求超时,因此梳理了下DNS的知识点。
可以先看到下面这段配置,修改了k8s内置插件服务 CoreDNS 的configMap配置后就能正确解析内网域名了:
xxx.com:53 {
errors
cache
forward .9.xxx.xxx.xxx
}
DNS (Domain Name System 的缩写),作用非常简单:根据域名查出IP地址。
DNS 中保存了一张域名(domain name)和与之相对应的 IP 地址(IP address)的表,以查询域名IP。
Dig的返回可能包含或不包含以下部分:
; <<>> DiG 9.10.6 <<>> yun.tencent.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 61953
;; flags: qr rd ra; QUERY: 1, ANSWER: 5, AUTHORITY: 1, ADDITIONAL: 7
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;yun.tencent.com. IN A
上面结果表示,查询的域名yun.tencent.com
的A记录,A是address的缩写。
注意 OPT PSEUDOSECTION
和EDNS: xxxx
是伪片段,可参考文档Extension Mechanisms for DNS
),他们是ADDITIONAL 额外信息的一部分,这部分信息是否携带和dig软件版本有关,我在腾讯云cvm是没有这段信息的。
第一张是腾讯云cvm的dig qq.com
截图:
第二张是我的开发机dig qq.com
截图:
这里为了弄清楚这段信息为何会携带,我特地在2个地方抓了包:
软件请求没有订阅 addtionnal 信息
软件请求有订阅 addtionnal 信息
;; ANSWER SECTION:
yun.tencent.com. 600 IN CNAME yun.tencent.com.dsa.dnsv1.com.
yun.tencent.com.dsa.dnsv1.com. 600 IN CNAME ik9nnxq4.sched.d0-dk.tdnsdp1.cn.
ik9nnxq4.sched.d0-dk.tdnsdp1.cn. 600 IN A 183.61.169.128
ik9nnxq4.sched.d0-dk.tdnsdp1.cn. 600 IN A 183.60.155.69
ik9nnxq4.sched.d0-dk.tdnsdp1.cn. 600 IN A 14.152.75.203
上面结果显示,yun.tencent.com.
有一个CNAME记录指向yun.tencent.com.dsa.dnsv1.com.
, yun.tencent.com.dsa.dnsv1.com.
又指向了ik9nnxq4.sched.d0-dk.tdnsdp1.cn.
, 它有3个A
记录的IP地址, 第二列 600
是TLL值(Time to live),表示缓存时间。
;; AUTHORITY SECTION:
d0-dk.tdnsdp1.cn. 1 IN NS ns1.dp1.tdnsdp1.cn.
这段提供了ns1.dp1.tdnsdp1.cn
服务器的IP列表
;; ADDITIONAL SECTION:
ns1.dp1.tdnsdp1.cn. 247 IN A 157.148.54.78
ns1.dp1.tdnsdp1.cn. 247 IN A 175.27.41.131
ns1.dp1.tdnsdp1.cn. 247 IN A 36.155.208.102
ns1.dp1.tdnsdp1.cn. 247 IN A 60.29.254.64
ns1.dp1.tdnsdp1.cn. 247 IN A 112.53.42.223
ns1.dp1.tdnsdp1.cn. 247 IN A 123.150.77.68
;; Query time: 215 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Tue Jan 09 15:38:06 CST 2024
;; MSG SIZE rcvd: 295
最后是DNS服务器的传输信息
DNS的服务器是127.0.0.1
,查询端口是53(DNS的默认端口),回应长度是295字节。(注意我的wifi DNS 是127.0.0.1
应该是被内部软件劫持修改了)。
域名对应的 IP 地址,都保存在 DNS 服务器。
以浏览器为例:我们输入域名,浏览器就会在后台,自动向 DNS 服务器发出请求,获取对应的 IP 地址。这就是 DNS 查询。
域名对应的IP保存在DNS服务器,我们要查询到域名对应的IP,首先本机要知道DNS服务器的IP地址
可以查看/etc/resolv.conf
修改该文件nameserver就可以指定DNS服务器
难道一台DNS服务器保存了世界上所有的域名?当然不是,这就需要了解下域名的分层结构。 域名是一个树状结构,最顶层的域名是根域名(root)用点”.”表示。
这种树状结构的意义在于,只有上级域名,才知道下一级域名的 IP 地址,需要逐级查询。
层级
|
说明 |
---|---|
根域名 | 所有域名的起点都是根域名,它写作一个点.,放在域名的结尾。因为这部分对于所有域名都是相同的,所以就省略不写了,比如zhuermu.com等同于zhuermu.com.(结尾多一个点)。 |
顶级域名 | 根域名的下一级是顶级域名。它分成两种:通用顶级域名(gTLD,比如 .com 和 .net)和国别顶级域名(ccTLD,比如 .cn 和 .us)。 |
一级域名 | 一级域名就是你在某个顶级域名下面,自己注册的域名。比如,zhuermu.com就是我在顶级域名.com下面注册的。 |
二级域名 | 二级域名是一级域名的子域名 blog.zhuermu.com,是域名拥有者自行设置的,不用得到许可。 |
使用 dig +trace yun.tencent.com
可以显示DNS的分级查询过程:
(base) ➜ d2l-zh dig +trace yun.tencent.com
; <<>> DiG 9.10.6 <<>> +trace yun.tencent.com
;; global options: +cmd
. 148204 IN NS e.root-servers.net.
. 148204 IN NS f.root-servers.net.
. 148204 IN NS g.root-servers.net.
. 148204 IN NS h.root-servers.net.
. 148204 IN NS i.root-servers.net.
. 148204 IN NS j.root-servers.net.
. 148204 IN NS k.root-servers.net.
. 148204 IN NS l.root-servers.net.
. 148204 IN NS m.root-servers.net.
. 148204 IN NS a.root-servers.net.
. 148204 IN NS b.root-servers.net.
. 148204 IN NS c.root-servers.net.
. 148204 IN NS d.root-servers.net.
;; Received 239 bytes from 127.0.0.1#53(127.0.0.1) in 2040 ms
com. 172800 IN NS a.gtld-servers.net.
com. 172800 IN NS b.gtld-servers.net.
com. 172800 IN NS c.gtld-servers.net.
com. 172800 IN NS d.gtld-servers.net.
com. 172800 IN NS e.gtld-servers.net.
com. 172800 IN NS f.gtld-servers.net.
com. 172800 IN NS g.gtld-servers.net.
com. 172800 IN NS h.gtld-servers.net.
com. 172800 IN NS i.gtld-servers.net.
com. 172800 IN NS j.gtld-servers.net.
com. 172800 IN NS k.gtld-servers.net.
com. 172800 IN NS l.gtld-servers.net.
com. 172800 IN NS m.gtld-servers.net.
com. 86400 IN DS 19718 13 2 8ACBB0CD28F41250A80A491389424D341522D946B0DA0C0291F2D3D7 71D7805A
com. 86400 IN RRSIG DS 8 1 86400 20240122050000 20240109040000 30903 . QikZcFwi549WTNwMZUMZ69E30Ld08zjFRsP7Z3QGbMOGiw+FldkvrXAy 6/w2g3qhbEwy9WSk4dSC0CkRDh0GRG/Tfv9gNTuVm9d4MP5/ok3BwQ/8 F6gYzKZT2O+Nre7mAQv5vXpSSdiONuGWuwdXtS0M1fWsxxQ9nprF04eg CNUeN41lYo7ksL2AVfcnqPO3i496g6R4az4eq07fxJv8/ptd469d542o mjqxFZd3NHrYLf/tx1SqK1nwGEQ+jWoP8W50OjyfetswFBsX+Tgrs9L5 rd5OQo28Ur6sPSjlu0ijZaCp/Oz9vdCjAEAIUZChKMBCfV8zcUiM5Ays 7PRUrQ==
;; Received 1175 bytes from 170.247.170.2#53(b.root-servers.net) in 144 ms
tencent.com. 172800 IN NS ns1.qq.com.
tencent.com. 172800 IN NS ns2.qq.com.
tencent.com. 172800 IN NS ns3.qq.com.
tencent.com. 172800 IN NS ns4.qq.com.
CK0POJMG874LJREF7EFN8430QVIT8BSM.com. 86400 IN NSEC3 1 1 0 - CK0Q2D6NI4I7EQH8NA30NS61O48UL8G5 NS SOA RRSIG DNSKEY NSEC3PARAM
CK0POJMG874LJREF7EFN8430QVIT8BSM.com. 86400 IN RRSIG NSEC3 13 2 86400 20240113052604 20240106041604 46171 com. Y7hvVJcR41A29NyS3rywRjJJtjlYznc1cRPs/t797eolfWOO4nEQnMNu VKC3xr1PPkDt1qsmXkxvg5UNhWbPiA==
VF2UQI8TAH3IC5FJMVG2G1M0N8J7404P.com. 86400 IN NSEC3 1 1 0 - VF2UTHOP0RH8T5MV2F5D6VIBH626EP7Q NS DS RRSIG
VF2UQI8TAH3IC5FJMVG2G1M0N8J7404P.com. 86400 IN RRSIG NSEC3 13 2 86400 20240116065112 20240109054112 46171 com. w6aJYVt7pUF9vKa+v4Vo6IYqDnbqwLcSOwRNQ6xVJ7RDOSzePA4C5cg9 SpIxGcDnAAYiy9olLi5xV0Dq8N/I8A==
;; Received 772 bytes from 192.12.94.30#53(e.gtld-servers.net) in 216 ms
yun.tencent.com. 86400 IN NS ns-cmn1.qq.com.
yun.tencent.com. 86400 IN NS ns-tel1.qq.com.
yun.tencent.com. 86400 IN NS ns-cnc1.qq.com.
yun.tencent.com. 86400 IN NS ns-os1.qq.com.
;; Received 454 bytes from 101.227.218.144#53(ns1.qq.com) in 35 ms
上面显示yun.tencent.com
有4条NS记录,用于描述目标域名到负责解析该域名的 DNS 的映射关系。
ns-os1.qq.com
得到结果yun.tencent.com. 600 IN CNAME yun.tencent.com.dsa.dnsv1.com.
;; Received 84 bytes from 203.205.236.198#53(ns-os1.qq.com) in 255 ms
上面显示一条CNAME记录yun.tencent.com.dsa.dnsv1.com
,对应该域名的IP。
细心的你可能发现了,dig 加上参数+trace
后查询的完成时间变长了很多,没有+trace
的时候总共才200+ms,而加上后光查询根服务器列表就花费了2000+ms,这是为什么呢?
这就要理解了解下DNS查询的2种方式:递归查询和迭代查询。
当使用了+trace
参数后,为了追踪域名的分层信息,因此递归查询变成了迭代查询,我们本地要去和域名个层级的DNS服务器通信,这样整个查询都是本地和个层级DNS去交互迭代查询, 而递归查询的时候,大概率DNS服务器已经缓存域名或者DNS服务器网络延迟比你直接通信短,因此会快很多。
了解DNS的基本原理之后,我们再来看看docker容器的DNS怎么配置。
/etc/resolv.conf
,可以修改宿主机的配置,或者在容器构建时自定义resolv.conf;--dns
指定DNS服务器容器启动时,默认情况下时复制一份宿主机的/etc/resolv.conf ,在宿主机修改之后不会马上生效,需要重启容器服务。
我们首先回顾一下kubernetes的架构,集群由两大部分组成:
DNS 策略可以逐个 Pod 来设定。
目前 Kubernetes 支持以下特定 Pod 的 DNS 策略。 这些策略可以在 Pod 规约中的 dnsPolicy 字段设置:
Default
: Pod 从运行所在的节点继承名称解析配置。
ClusterFirst
: 与配置的集群域后缀不匹配的任何 DNS 查询(例如 “www.kubernetes.io”) 都会由 DNS 服务器转发到上游名称服务器。
ClusterFirstWithHostNet
: 对于以 hostNetwork
方式运行的 Pod,应将其 DNS 策略显式设置为 ClusterFirstWithHostNet
。否则,以 hostNetwork 方式和 ClusterFirst
策略运行的 Pod 将会做出回退至 Default
策略的行为。
“None”: 此设置允许 Pod 忽略 Kubernetes 环境中的 DNS 设置。Pod 会使用其 dnsConfig 字段所提供的 DNS 设置。
注意:hostNetwork 是指 暴露 pod 所在节点 IP 给终端用户,详情可查阅资料Networking using the host network
当 Pod 的 dnsPolicy 设置为 “None” 时,必须指定 dnsConfig 字段。
dnsConfig 字段中属性:
nameservers
:将用作于 Pod 的 DNS 服务器的 IP 地址列表。 最多可以指定 3 个 IP 地址。例如 coredns的Cluster IP。
searches
:用于在 Pod 中查找主机名的 DNS 搜索域的列表。此属性是可选的。
options
:可选的对象列表,其中每个对象可能具有 name 属性(必需)和 value 属性(可选)。
apiVersion: v1
kind: Pod
metadata:
namespace: default
name: dns-example
spec:
containers:
- name: test
image: nginx
dnsPolicy: "None"
dnsConfig:
nameservers:
- 1.2.3.4
searches:
- ns1.svc.cluster-domain.example
- my.dns.search.suffix
options:
- name: ndots
value: "2"
- name: edns0
> kubectl exec -it dns-example -- cat /etc/resolv.conf
nameserver 1.2.3.4
search ns1.svc.cluster-domain.example my.dns.search.suffix
options ndots:2 edns0
如果Pod的dnsPolicy策略配置的是ClusterFirst或ClusterFirstWithHostNet,那么pod的dns解析就会查询k8s集群的DNS服务器,而k8s内置插件提供了该服务—CoreDNS,我们一起来看看。
CoreDNS 是使用插件管理器集群插件自动启动的 kubernetes内置服务。使用deployment的方式来运行CoreDNS,会创建一个名为kube-dns的service,并用ClusterIP(默认为10.96.0.10)来作为集群内的pod的nameserver。
--cluster-dns=
传递DNS服务器传递给容器。我们再来看下k8s的CoreDNS 配置文件:
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
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
cache 30
loop
reload
loadbalance
}
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
Corefile: |
xxx.com:53 {
errors
cache
forward 9.xxx.xxx.xxx
}
.: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
cache 30
loop
reload
loadbalance
}
其中xxx.com
表示遇到该域名解析时需要forward 到9.xxx.xxx.xxx
这台DNS服务器进行解析。
/etc/resolv.conf
,可以修改宿主机的配置,或者在容器构建时自定义;--dns 8.8.8.8
指定DNS服务器Default
和第1种方式一样ClusterFirst
和ClusterFirstWithHostNet
都是集群优先,这个时候就要确认集群的DNS服务器(一般考CoreDNS的ConfigMap配置,在这里修改DNS服务器)None
此设置允许 Pod 忽略 Kubernetes 环境中的 DNS 设置。Pod 会使用其 dnsConfig
字段所提供的 DNS 设置,这个时候在各自的Pod config.yaml 中修改dnsConfig
对应的DNS服务器。