浅谈DNS解析

当你访问windsings.com时,这个世界都发生了什么?

最普通的DNS解析

根据计算机网络课本,访问某个域名,这个域名怎么跳转到具体的IP上,是使用了DNS解析。如下:

主机向本地域名服务器解析的过程采用递归,而本地域名服务器向其它域名服务器解析可以使用递归和迭代两种方式。

迭代的方式下,本地域名服务器向一个域名服务器解析请求解析之后,结果返回到本地域名服务器,然后本地域名服务器继续向其它域名服务器请求解析;而递归地方式下,结果不是直接返回的,而是继续向前请求解析,最后的结果才会返回。

设置域名解析

当我从某个域名商那里买了某个域名,要对域名的解析进行设置。阿里云中,有如下的设置方式:

对于一个独立的,带有公网IP的VPS,可以使用A解析方式。

在linux下,通过dig命令,可以查看主机的信息。如果带有+trace命令,则会显示查询的过程。

比如我的一个朋友的域名keyanjie.net,有如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
intel@intel-pc:~$ dig +trace keyanjie.net

; <<>> DiG 9.10.3-P4-Ubuntu <<>> +trace keyanjie.net
;; global options: +cmd
. 1023 IN NS g.root-servers.net.
. 1023 IN NS h.root-servers.net.
. 1023 IN NS l.root-servers.net.
. 1023 IN NS j.root-servers.net.
. 1023 IN NS k.root-servers.net.
. 1023 IN NS b.root-servers.net.
. 1023 IN NS a.root-servers.net.
. 1023 IN NS f.root-servers.net.
. 1023 IN NS d.root-servers.net.
. 1023 IN NS c.root-servers.net.
. 1023 IN NS e.root-servers.net.
. 1023 IN NS i.root-servers.net.
. 1023 IN NS m.root-servers.net.
;; Received 239 bytes from 127.0.1.1#53(127.0.1.1) in 5 ms

net. 172800 IN NS l.gtld-servers.net.
net. 172800 IN NS b.gtld-servers.net.
net. 172800 IN NS c.gtld-servers.net.
net. 172800 IN NS d.gtld-servers.net.
net. 172800 IN NS e.gtld-servers.net.
net. 172800 IN NS f.gtld-servers.net.
net. 172800 IN NS g.gtld-servers.net.
net. 172800 IN NS a.gtld-servers.net.
net. 172800 IN NS h.gtld-servers.net.
net. 172800 IN NS i.gtld-servers.net.
net. 172800 IN NS j.gtld-servers.net.
net. 172800 IN NS k.gtld-servers.net.
net. 172800 IN NS m.gtld-servers.net.
net. 86400 IN DS 35886 8 2 7862B27F5F516EBE19680444D4CE5E762981931842C465F00236401D 8BD973EE
net. 86400 IN RRSIG DS 8 1 86400 20200806170000 20200724160000 46594 . Mn3fm45mYwBzvw8EuVABTXC269FVccFND9a6jaX/CfWye4E6YONViIkh Zs0fEBH3g2i6QKsTXliK+nYa3PfZAYO9VqheNxIvkdJO760Q29sd2BKa 6DooGoz3vqaC4aqSSKO1NHmNWSaEii++pVLNiggssxOJapdcv8scpo1R ebtASpz0fnOu6rO8MhYkz5aDLBWKEgWYyjfFK+ItwNoyc1+MYNtrMX6H QJRUtDY4b21ICLuDpp7nMNFIwnoScFf4e50qM413WP0ihVyCPuDD/aMx p/DcWdJUCpM9CTs5o0wIZnM9udZwXPJ2tAD6SyVpLmkgR7Sxwqy0SwCm E4+oMw==
;; Received 1169 bytes from 192.203.230.10#53(e.root-servers.net) in 206 ms

keyanjie.net. 172800 IN NS dns11.hichina.com.
keyanjie.net. 172800 IN NS dns12.hichina.com.
A1RT98BS5QGC9NFI51S9HCI47ULJG6JH.net. 86400 IN NSEC3 1 1 0 - A1RUUFFJKCT2Q54P78F8EJGJ8JBK7I8B NS SOA RRSIG DNSKEY NSEC3PARAM
A1RT98BS5QGC9NFI51S9HCI47ULJG6JH.net. 86400 IN RRSIG NSEC3 8 2 86400 20200728064417 20200721053417 56519 net. B4N1ZLUcMCAkfgGNrmJfXylkUAbPnqxO31ZQ5ZwU0AecyChOkGGTelH/ 87eM7RtGM7mqrM6zU9CnGwsgheqg2HCbpX7n5Fvl1AclnJZBuFlF0hve jSO99rrLrLRaTWQtLyJwVQJWkMfgztCO5Mh2ngJfK6ZB1PLSxOFqVz0j 5MTYYp9QwdL1PvLIaBAw0kTgcm3o476wB0glMo6yzDbK2A==
DMUULDLK9AHE90T3TVLM9UO0BL3FSD6H.net. 86400 IN NSEC3 1 1 0 - DMV0V678LC7UEC158TTU3M81UCSIPP21 NS DS RRSIG
DMUULDLK9AHE90T3TVLM9UO0BL3FSD6H.net. 86400 IN RRSIG NSEC3 8 2 86400 20200730063155 20200723052155 56519 net. Nnwi6w5n3/C/sNwr5mMCP2i7Uv7Ki3KAI4dkigOMjFwpgLLN2egqyCqL QjLb/Dlk0fe4BjpzbPLMe67vsX8uJVcPXddgSh3EHa1LxhQROegMAFxJ QS2x/2gEI7a5lkLN2Jp7/F8c7q29LqERK8kFCw5sUC5/HHa+c/Pjk96r pfvFNfgj7doPCn7cZ0iXv3eGRxorZFIwemOVzrJfZ/IEiw==
;; Received 641 bytes from 192.33.14.30#53(b.gtld-servers.net) in 157 ms

keyanjie.net. 600 IN A 101.132.113.74
;; Received 57 bytes from 106.11.141.115#53(dns11.hichina.com) in 45 ms

按前文所述,应该是本机向本地域名服务器,以递归的方式申请解析,127.0.1.1是什么东西?

我的机器是ubuntu16.04,ubuntu下有个本地DNS服务,叫dnsmasq,它是由NetworkManager控制的。

它监听的本地地址,--listen-address=127.0.1.1 (ubuntu12.04及之前的版本 是 127.0.0.1), 这个地址是一个本地回环地址。 而真实的dns服务器地址,是被这个服务管理维护着的。

127.0.1.1是用于端口映射(port forwarding)的,比如ISP分配的DNS server是123.123.123.123。则dnsmasq会将客户端向127.0.1.1:53发送的DNS请求,转发到123.123.123.123:53。

在阿里云的解析图示中,有一个NS记录类型,标记着将域名指定其他DNS服务器进行解析。这里的NS,就是dig +trace应答记录中的NS。

它返回13个根域名的地址。根据应答的类型NS,交由列出来的.net的权威域名服务器(在每个域中,会有一台或者多台服务器来保存这个域名空间的所有信息,并且响应关于该域名空间的所有请求,这种服务器就叫这个域的权威域名服务器,多个权威域名服务器间,只能有一台是主域名服务器,这台主域名服务器向其他辅域名服务器分发域名空间的更新信息)进行解析。

同样,b.gtld-servers.net这个DNS服务器返回NS类型的解析,交由dns11.hichina.com或dns12.hichina.com进行解析。

选择了dns11.hichina.com,返回keyanjie.net的解析记录:A类型,到IP:101.132.113.74。

这时,访问101.132.113.74就可以访问到他的博客。

一个IP下多个网站

这是我另外一个同学的博客:cjh.zone,当dig cjh.zone的时候,有如下结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
intel@intel-pc:~$ dig cjh.zone

; <<>> DiG 9.10.3-P4-Ubuntu <<>> cjh.zone
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 47702
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;cjh.zone. IN A

;; ANSWER SECTION:
cjh.zone. 300 IN A 185.199.111.153

;; Query time: 64 msec
;; SERVER: 127.0.1.1#53(127.0.1.1)
;; WHEN: Fri Jul 24 18:09:24 CST 2020
;; MSG SIZE rcvd: 42

当访问这个IP:185.199.111.153 的时候,出现了github的404界面:

原因是这个主机下,有多个网站。

那当我访问cjh.zone的时候,这个主机是如何正确显示网站的呢?

是通过主机头,

在知乎的这个回答中:

在一个服务器上面怎么放两个网站呀? - 牛岱的回答 - 知乎中,有如下:

理解了HTTP 协议你就知道,一个服务器完全可以 host 很多网站,原因就在于 HTTP request 里面包含了 HOST 这个信息头。

虽然两个域名都解析到同一个 IP,但是当用户用不同的两个域名向你的服务器发出请求的时候,HTTP Request 里面的 Host 头是不一样的。

当你在浏览器里输 http://developer.mozilla.org的时候,server 收到的 HTTP 报文头长这样:

1
2
3
GET / HTTP/1.1
Host: developer.mozilla.org
...

当你换一个域名的时候,比如 http://developer.google.org,报文就是这样:

1
2
3
4

GET / HTTP/1.1
Host: developer.google.org
...

很显然,服务器可以根据不同的host 来把请求转发到相应的服务。 一般的实践做法的话,推荐使用 nginx 作为 Gate,在配置文件里这样配置即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
http {  
...

server {
listen 80;
server_name developer.mozilla.org;
root /var/www/html/mozilla;
...

}
server {
listen 80;
server_name developer.google.com;
root /var/www/html/google;
...

}
}

这样配置的话,用户用域名http://developer.mozilla.org请求的,就会从 /mozilla 这个文件夹 serve 静态资源,从 http://google.org请求的,就会从 /google 文件夹 serve 静态资源。 一般情况本地的两个网站还要有 serve 动态请求的应用,假如一个启动后监听 3000端口,一个监听 8000 端口,那再加上如下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
http {  
...
...

server {
listen 80;
server_name developer.mozilla.org;
root /var/www/html/mozilla;
location /api {
proxy_pass localhost:3000
}

}
server {
listen 80;
server_name developer.google.com;
root /var/www/html/google;
location /api {
proxy_pass localhost:8000
}
...

}
}

这样的话,形如http://developer.mozilla.org/api/... 的请求就会转发到 localhost:3000,然后 nginx 再把 localhost:3000 返回的再返回给浏览器,当然如果应用不是在和 nginx 一个主机上的话,可能这个localhost:3000 就是私网的其它主机ip,或者也可以是公网 ip,或者域名,8000 的也同理。

这样配置的话,你的服务器就可以 host 两个网站了,同理还可能是很多个。

nginx 的话,用作这种 Gate 转发请求并静态文件 host 是蛮方便的一种选择,只需要几行简单的 json 配置,就能稳定正常运行,而且基于异步 I/O,并发性能很好。

总之一句话,Host 头可以让过滤转发请求。

一个域名对应多个IP

当你dig +trace windsings.com,也就是对我的网站进行分析时,会发现DNS服务器返回三个IP,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
intel@intel-pc:~$ dig +trace windsings.com

; <<>> DiG 9.10.3-P4-Ubuntu <<>> +trace windsings.com
;; global options: +cmd
. 2983 IN NS a.root-servers.net.
. 2983 IN NS b.root-servers.net.
. 2983 IN NS c.root-servers.net.
. 2983 IN NS d.root-servers.net.
. 2983 IN NS e.root-servers.net.
. 2983 IN NS f.root-servers.net.
. 2983 IN NS g.root-servers.net.
. 2983 IN NS h.root-servers.net.
. 2983 IN NS i.root-servers.net.
. 2983 IN NS j.root-servers.net.
. 2983 IN NS k.root-servers.net.
. 2983 IN NS l.root-servers.net.
. 2983 IN NS m.root-servers.net.
;; Received 239 bytes from 127.0.1.1#53(127.0.1.1) in 7 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 30909 8 2 E2D3C916F6DEEAC73294E8268FB5885044A833FC5459588F4A9184CF C41A5766
com. 86400 IN RRSIG DS 8 1 86400 20200806210000 20200724200000 46594 . iFLnO7of/tl4M23HkDFQ0sMsm5ONcS+Qg6l6QPKHo1xV0zslYX1M8WQ4 TU7kbX1eks3f44hXrtjJwNESdakmQd0XgKapTIiXsOMknfFjpj5lLprm B5x5k6JI5PKZsZ5OVtGF888BsUXYH1dAT7bfny6xw0kRJvk/ZJHbKC+g uczBRdilPuYfqEyVHEdCaWd3TL6clVIS85ZpaB3zzYZAHldD2ykBzWRR 46vmMFfk0EIpatpoOf9p6LQzmq0CqR7FcPo5EEOUnvpXkaMUjEenGIdw q8lm4/a43otwwq9g15CFabxN2o2mQlhlzuVBMmLIyUCKU4QzevhVnh7g AM6nyA==
;; Received 1173 bytes from 198.41.0.4#53(a.root-servers.net) in 162 ms

windsings.com. 172800 IN NS ns1.smartgslb.com.
windsings.com. 172800 IN NS ns2.smartgslb.com.
CK0POJMG874LJREF7EFN8430QVIT8BSM.com. 86400 IN NSEC3 1 1 0 - CK0Q1GIN43N1ARRC9OSM6QPQR81H5M9A NS SOA RRSIG DNSKEY NSEC3PARAM
CK0POJMG874LJREF7EFN8430QVIT8BSM.com. 86400 IN RRSIG NSEC3 8 2 86400 20200728044213 20200721033213 24966 com. qoAss2VVxsBWG9+MAQ3giAmCnzf4dz7GppZsQfkt3b7cFIVgO3TiWPWk usb3BEbpsma2Q+x/TBLeeHYm6l1HWXMIl7zcYLsAXY0ZzoWaqNPTPH6Y NDAreZYP21UekBL7g710cndRk4oaJUNz5t8sGi3JaOJF046QcUz6gGg7 NLMvyGlJWzmftGbxgp9ovdOgwmirddESGOj33kuCfSJvWA==
8HTEEKSHQDE7BN29IUEH9GCUG0BJA05S.com. 86400 IN NSEC3 1 1 0 - 8HTEUV6CJU94CB57CLPPJ7DS5EJ8OTUB NS DS RRSIG
8HTEEKSHQDE7BN29IUEH9GCUG0BJA05S.com. 86400 IN RRSIG NSEC3 8 2 86400 20200731051909 20200724040909 24966 com. PqoClMMWAVeFX2+5EHMtxxS6MgOwOWIBrWYrnhdgy8SecgUvrpv5PhSH TKGR5F+dTpeYpCe8E6HPHEwxHtXaPhuepEzxWNAyREnAy5LMssix1Jnh WrjKiRFjK4tWWqfhDHBaGcgjm9r3SFl3OWneA3VG11RvlEkUTb6vHQRm zH/0ZPKSkpxGK1joaA/GDOIlNrNbKGzicXKDjBaJGFDdQw==
;; Received 817 bytes from 192.54.112.30#53(h.gtld-servers.net) in 483 ms

windsings.com. 60 IN A 154.209.235.56
windsings.com. 60 IN A 45.137.10.76
windsings.com. 60 IN A 119.28.89.140
;; Received 90 bytes from 172.104.121.39#53(ns1.smartgslb.com) in 65 ms

当记录值有多个IP地址时,域名是如何解析的?

这里有一个参考华为云:当记录值有多个IP地址时,域名是如何解析的?

当为域名添加A类型或者AAAA类型解析记录时,参数“值”支持填写多个IP地址,将域名解析到多个IP地址。

当解析记录的“值”包含多个IP地址时,域名解析会返回所有的IP地址,但返回IP地址的顺序是随机的,浏览器默认取第一个返回的IP地址作为解析结果。

其解析流程如下:

  1. 网站访问者通过浏览器向Local DNS发送解析请求。
  2. Local DNS将解析请求逐级转发至权威DNS。
  3. 权威DNS在收到解析请求后,将所有IP地址以随机顺序全部返回Local DNS。
  4. Local DNS将所有IP地址返回浏览器。
  5. 网站访问者的浏览器随机访问其中一个IP地址,通常选取返回的第一个IP地址。

根据大量测试数据显示,解析到各IP地址的比例接近相等。

例如,某网站的域名为“example.com”,部署了3台服务器,对应的IP地址分别为:192.168.1.1、192.168.1.2、192.168.1.3。

为域名“example.com”配置一条A类型记录集,将记录集值设置为3个IP地址。

不同用户访问网站时,返回的解析结果如以下所示。

用户 返回结果 解析结果
用户A 192.168.1.1 192.168.1.2 192.168.1.3 192.168.1.1
用户B 192.168.1.2 192.168.1.1 192.168.1.3 192.168.1.2
用户C 192.168.1.3 192.168.1.1 192.168.1.2 192.168.1.3

这里有个程序可以打印解析windsings.com 的结果(在Linux下)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>

int main()
{
struct hostent *host;
struct in_addr h_addr;

if ((host = gethostbyname("windsings.com")) != NULL) {
h_addr.s_addr = *((unsigned long *)host->h_addr);
printf("%s\n", inet_ntoa(h_addr));
}
return 0;
}

结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
(base) intel@intel-pc:~/Documents/CppCode$ gcc code1.cpp
(base) intel@intel-pc:~/Documents/CppCode$ ls
a.out code1.cpp
(base) intel@intel-pc:~/Documents/CppCode$ ./a.out
119.28.89.140
(base) intel@intel-pc:~/Documents/CppCode$ ./a.out
45.137.10.76
(base) intel@intel-pc:~/Documents/CppCode$ ./a.out
119.28.89.140
(base) intel@intel-pc:~/Documents/CppCode$ ./a.out
154.209.235.56
(base) intel@intel-pc:~/Documents/CppCode$

可见,3个IP都被打印出来了。

在追踪的解析记录中,有一个DNS服务器的域名是smartgslb,自然想到了基于DNS解析的全局负载均衡GSLB,我厚着脸皮发工单问客服,询问到实际上也就是这个东西。

这个背后是一个叫智能解析的东西,当我测试的时候,我的IP地址在亚洲,因此智能DNS解析返回的是亚洲的IP。这三个IP由我的电脑自行选择一个进行访问(一般就是访问第一个),关于智能解析,我另写一篇文章来简述。

我的网站使用了CDN,所以这三个IP其实是CDN服务器的地址,关于CDN,我另写一篇文章来简述。