DNS
什么是 DNS
在解释 DNS 之前,我们可以先想象一个场景,就是当你想打电话给某个人的时候,除非你对这个人的电话号码很熟悉,那么你可以一次性地输入这个人的号码并呼叫他,但是,更多的时候,我们是从电话本中找到这个人的名字,然后再选择这个人的某个电话进行呼叫。
那么对应于互联网来说,也是类似的,对于接入互联网的每个设备(这里是指直接接入,而不包含路由器的情况)都有唯一的标识,这个标识以前都是 IPv4,但是,近 10 年来,开始普及 Ipv6 了,那么,当我们想访问某个设备的时候(可能是某个大公司的服务器),那么直接输入 ip 理论上不太行(不是绝对不行),但是这不仅让我们的记忆麻烦,而且对于企业品牌来说也不友好,所以这里就引入了一个给 IP 命名的方式,你可以理解成它就是 DNS 了。这是狭义的理解,如果我们要严格来说,DNS 不仅仅是做 IP 解析,还包含很多的东西,比如邮箱服务,TXT 记录等,但是,这里我想描述的主要就是 IP 解析。
域名的分类
现在的国际域名主要可以分成四类,这里的专业名词叫做 zone(注意了,后续有用到):
- 根域(Root Zone)
- 根域名服务器负责解析
顶级域名
,给出顶级域名的 DNS 服务器地址; - 世界上仅有十三个根域名服务器,这些服务器的 ip 地址是不会随便更改的;
- 它的域名是空字符串。而全限定域名(FQDN)是
.
,因为 FQDN 总是以.
结尾;
- 根域名服务器负责解析
- 顶级域(Top Level Domains, TLD):
.com
.cn
等国际、国家级的域名- 顶级域名服务器负责解析
次级域名
,给出次级域名的 DNS 服务器地址。 - 每个顶级域名都对应各自的服务器,它们之间是完全独立的。
.cn
的域名解析仅由.cn
顶级域名服务器提供。 - 目前国际 DNS 系统中已有上千个 TLD,包括中文「.我爱你」甚至藏文域名,详细列表参见 IANA TLD 数据库
- 除了国际可用的 TLD,还有一类类似「内网 IP 地址」的“私有 TLD”,最常见的比如 xxx.local xxx.lan,被广泛用在集群通信中。后面详细介绍
- 顶级域名服务器负责解析
- 次级域(Second Level Domains):这个是个人/企业能够买到的域名,比如
google.com
- 每个次级域名都有一到多个权威 DNS 服务器,这些 DNS 服务器会以 NS 记录的形式保存在对应的顶级域名(TLD)服务器中。
- 权威域名服务器则负责给出最终的解析结果:ip 地址(A 记录 ),另一个域名(CNAME 记录)、另一个 DNS 服务器(NS 记录)等。
- 子域(Sub Domians):
*.google.com
统统都是google.com
的子域。- 每一个子域都可以有自己独立的权威 DNS 服务器,这通过在子域中添加 NS 记录实现。
域名解析协议
在学习网络知识的时候,我们可能已经被灌输了 DNS 使用的 UDP 协议,这在以前是正确的,但是,随着网络的发展,传统的 UDP DNS 解析协议因为不安全的缘故(国内的同学应该有所体会,比如访问一个正规的网站但是发现这个网站有很多莫名其妙的广告),所以出现了支持 TLS 的 DNS 协议,还有以 https 或者 grpc 作为底层协议的基于 TCP 的 DNS 协议。
配置文件
在 Linux 中,关于 DNS 的解析,有 3 个 /etc
目录下的文件我们需要关注,分别是:
- /etc/hosts 配置域名与IP对应关系
- /etc/resolv.conf 配置DNS服务器
- /etc/nsswitch.conf 配置解析顺序,是先解析hosts文件,还是先解析DNS服务器
- /etc/host.conf:已被放弃
nsswitch.conf
nsswitch.conf(Name Service Switch configuration file) 是系统用来对 Name 服务控制使用的,它可以控制系统解析的元数据,例如对于 DNS 解析:
[root@liqiang.io]# cat /etc/nsswitch.conf| grep hosts
hosts: files dns
从这里的配置可以看出,我们的配置是先进行 files 的解析,如果解析不成功了,再尝试 dns 解析。
hosts 文件
[root@liqiang.io]# cat /etc/hosts
cat /etc/hosts
127.0.0.1 localhost
127.0.1.1 work-pc
127.0.0.1 google.com
resolv.conf文件
配置 Linux 系统 DNS 服务器的配置文件:
[root@liqiang.io]# cat /etc/resolv.conf
# Generated by NetworkManager
nameserver 8.8.8.8
nameserver 1.1.1.1
nameserver 192.168.121.1
这里只是设置了 3 个 DNS 服务器,没有其他的特别设置,那么在这种默认的情况下,当系统需要进行 DNS 解析的时候,系统永远都会尝试用第一个 DNS 服务器进行解析,只有在第一个 DNS 服务器解析不成功的时候才会继续尝试第二个,否则直接就用第一个的结果。
那么如果我们不希望永远只使用第一个,我们希望能够轮询地使用这三个那么应该怎么办呢?这个时候我们可以配置一个参数 options
:
[root@liqiang.io]# cat /etc/resolv.conf
# Generated by NetworkManager
nameserver 8.8.8.8
nameserver 1.1.1.1
nameserver 192.168.121.1
options rotate timeout:1 ndots:5
这里我配置了几个 options:
- rotate:这个配置就是让查询 DNS 的时候可以轮询地尝试这些 DNS 服务器,而不是每次都只使用第一个,但是需要注意的是,这个轮询不是系统级别的,而是进程级别的,也就是说如果你用 ping 或者 dig 命令之类的,你会发现每次用的都是第一个;但是如果你一个进程有多个 DNS 解析,那么它会轮询地使用这些 DNS 服务器;
- timeout:单次查询的超时时间,单位是秒;
- ndots:这个在 k8s 中会非常有用,我们知道 k8s 会不断地添加后缀来解析正确的 ip,这个配置可以限制解析的后缀长度;
我们验证一下就会发现,它会轮询地使用各种 DNS 服务器:
[root@liqiang.io]# cat test.py
import requests
for domain in [
"https://cloudflare.com",
"https://google.com",
]:
requests.request("GET", domain)
[root@liqiang.io]# tcpdump -n -t port 53
IP 192.168.121.42.47385 > 8.8.8.8.domain: 34152+ A? cloudflare.com. (32)
IP 8.8.8.8.domain > 192.168.121.42.47385: 34152 2/0/0 A 104.16.133.229, A 104.16.132.229 (64)
IP 192.168.121.42.42714 > 1.1.1.1.domain: 35683+ A? www.cloudflare.com. (36)
IP 1.1.1.1.domain > 192.168.121.42.42714: 35683 2/0/0 A 104.16.124.96, A 104.16.123.96 (68)
IP 192.168.121.42.55753 > 192.168.121.1.domain: 7003+ A? google.com. (28)
IP 192.168.121.1.domain > 192.168.121.42.55753: 7003 6/0/0 A 172.253.118.139, A 172.253.118.102, A 172.253.118.100, A 172.253.118.101, A 172.253.118.113, A 172.253.118.138 (124)
IP 192.168.121.42.41275 > 8.8.8.8.domain: 32237+ A? www.google.com. (32)
IP 8.8.8.8.domain > 192.168.121.42.41275: 32237 6/0/0 A 64.233.170.106, A 64.233.170.104, A 64.233.170.105, A 64.233.170.103, A 64.233.170.99, A 64.233.170.147 (128)
host.conf 文件
这个现在基本已经不用了,都改为 nsswitch.conf了。
配置文件关系
我们在上面有几个配置文件,那么在真正进行 DNS 解析的时候,他们之间的关系是怎么样的呢?
- 首先看 nsswitch 文件,里面会指定是先进行 file 解析,还是 dns 解析;
- 如果是先进行 file 解析,那么就先查看
/etc/hosts
文件中的配置,存在了就返回,不存在的话就继续使用 dns 解析; - 如果是进行 dns 解析,就会根据
/etc/resolv.conf
文件中的配置,向指定的 DNS 服务器进行解析,解析成功则返回,解析失败那么将返回 unkowns hosts