DEV Community

Michael
Michael

Posted on • Edited on

DNS到底怎么运作的

背景

上文讲到Ubuntu中热点遇到的一些问题,涉及到了Ubuntu环境中网络管理的各个组件,总是很模糊。本文主要通过梳理DNS管理来整理Ubuntu中网络管理组件分工,以及DNS管理细节,本文不会涉及DNS协议细节。

环境

system:Ubuntu24.04
libc: glibc2.39
Enter fullscreen mode Exit fullscreen mode

Ubuntu24.04网络管理

Linux环境中有多个网络管理组件,比如Network Manager, systemd-networkd等,不同组件有不同的上下文概念和配置方式,这对于网络管理和配置来说很有挑战性。后面推出了netplan,作为网络管理抽象层,使用YAML文件配置,使用上面列到的组建作为渲染器,一份配置文件适配多种渲染器,目前支持两种渲染器:Network Managersystemd-networkd

Ubuntu上面默认使用前者作为网络管理工具,可以查看man 5 NetworkManager.conf查看相关配置,配置文件位于/etc/NetworkManager/NetworkManager.conf

网络有很多部分,比如无线,有线,DNS, 蓝牙等,NetworkManager使用不同的软件管理这些组件。无线部分使用wpa_supplicant(见wifi.backend), dhcp client端也有多种选择,默认使用internal。

其中DNS配置很有意思,根据manpage文档,DNS管理使用systemd-resolved(/etc/resolv.conf文件是软连接至/run/systemd/resolve/stub-resolv.conf), 也可以设置使用dnsmasq

Ubuntu24.04 DNS管理

DNS基础知识 Cloudflare

DNS基础知识 ruanyifeng

resolvectl作为systemd-resolved的命令行管理工具管理包括DNS等。

systemd-resolved会在本地53端口起个DNS服务,并且将nameserver 127.0.0.53写入/etc/resolv.conf文件,那其他软件DNS查找这个文件,都是访问本地53端口服务(这个成为stub resolver),但是真正的DNS服务server会藏在systemd-resolved的相关配置文件中,可以使用如下命令查看:

resolvectl status
Enter fullscreen mode Exit fullscreen mode

它会列出所有网络设备的resolve信息,比如无线网卡的信息

    Link 3 (wlp4s0)
        Current Scopes: DNS
             Protocols: +DefaultRoute -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
    Current DNS Server: 192.168.0.1
           DNS Servers: 192.168.0.1
            DNS Domain: lan
Enter fullscreen mode Exit fullscreen mode

上述的192.168.0.1作为DNS server一般是DHCP分配的,局域网内部的DNS server, 会去请求ISP的DNS server(DNS的迭代模式(Iterate)), 最后会进入递归模式,不断从ROOT(.), TLD nameserver(Top Level Domain)(.com), Authoritative nameserver(baidu.com)获取DNS信息,返回信息有各种所谓的Record(上述是全流程,当然可能存在缓存就不用走这些流程了,不然每次请求都消耗很多时间,另外服务器负载也太大了), 下面是常见的Record

    A - IPV4
    AAAA - IPV6
    NS - Authoritative Name Server
    CNAME - 别名,比如www.baidu.com -> www.shifen.com
    MX - Mail
    TXT - Human readable document
    PTR - reverse DNS lookup, 由ip查域名
Enter fullscreen mode Exit fullscreen mode

查看systemd-resolved的DNS缓存

使用sudo resolvectl statistic可以查看缓存情况,比如缓存个数,命中个数等。如果想查看缓存内容,使用sudo pkill -USR1 systemd-resolve, 然后在syslog中查看 sudo journalctl -u systemd-resolved > ~/resolved.txt注意上面打印的数据是unique的数据,记录中可能有多个相同域名指向多个IP

清空systemd-resolved的DNS缓存

清空所有cache

sudo pkill -USR2 systemd-resolve
# OR
sudo resolvectl flush-caches
sudo resolvectl statistic
Enter fullscreen mode Exit fullscreen mode

glibc的DNS服务nsswitch和ncsd

为什么提到了glibc的DNS服务呢?因为在程序中使用比如函数getaddrinfo进行域名解析时候就使用了glibc提供的域名解析服务。下面梳理glibc的DNS相关流程。

glibc提供了各类名称解析服务,比如用户名->USER ID, 组名->GROUP ID, 域名->IP等,其中使用的模块是NSS(Name Service Switch), 这个不要跟Firefox的NSS(Network Security Services)模块弄混淆了

NSSwitch

可以使用getent database key从数据库中获取NSS支持类型的存储数据, 比如

getent ahosts # get A record host
getent group # get group resolve record
man 5 nss
man 5 nsswitch.conf
Enter fullscreen mode Exit fullscreen mode

getent ahost先使用缓存没有就使用getaddrinfo开始网络请求,详情见man getent

nsswitch的配置文件见/etc/nsswitch.conf,我们关心DNS相关的是hosts

hosts:          files mdns4_minimal [NOTFOUND=return] dns
Enter fullscreen mode Exit fullscreen mode

其中hosts值规定使用DNS查询的顺序,files代表/etc/hosts; mdns4\_minimal使用系统提供的multicast DNS解析服务,如果有这个服务,调用成功但是没有我们要的结果,就返回,如果没有这个服务,就使用系统提供的DNS服务查询(/etc/resolv.conf)。
使用nsswitch查询DNS整个过程,可以通过strace查看到上述描述细节

sudo strace -e trace=open,openat,connect -f ping -c1 www.baidu.com
     openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
     openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libcap.so.2", O_RDONLY|O_CLOEXEC) = 3
     openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libidn2.so.0", O_RDONLY|O_CLOEXEC) = 3
     openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
     openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libunistring.so.5", O_RDONLY|O_CLOEXEC) = 3
     openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
     openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache", O_RDONLY|O_CLOEXEC) = 5
# nscd缓存
     connect(5, {sa_family=AF_UNIX, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (没有那个文件或目录)
     connect(5, {sa_family=AF_UNIX, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (没有那个文件或目录)
# 使用了getaddrinfo, 所以会调用nsswitch查询dns
     openat(AT_FDCWD, "/etc/nsswitch.conf", O_RDONLY|O_CLOEXEC) = 5
# hosts中配置files第一顺序
     openat(AT_FDCWD, "/etc/host.conf", O_RDONLY|O_CLOEXEC) = 5
     openat(AT_FDCWD, "/etc/resolv.conf", O_RDONLY|O_CLOEXEC) = 5
     openat(AT_FDCWD, "/etc/hosts", O_RDONLY|O_CLOEXEC) = 5
     openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 5
# 使用multicast DNS本地局域网所有example.local机器咨询DNS, 没有这个机器服务,下一个dns系统查询
     openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libnss_mdns4_minimal.so.2", O_RDONLY|O_CLOEXEC) = 5
# hosts配置中的dns, 使用系统提供的dns服务, 即systemd-resolved
     connect(5, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("127.0.0.53")}, 16) = 0
     openat(AT_FDCWD, "/etc/gai.conf", O_RDONLY|O_CLOEXEC) = 5
     ...
     connect(5, {sa_family=AF_INET, sin_port=htons(1025), sin_addr=inet_addr("183.2.172.42")}, 16) = 0
     PING www.a.shifen.com (183.2.172.42) 56(84) bytes of data.
     openat(AT_FDCWD, "/etc/hosts", O_RDONLY|O_CLOEXEC) = 5
     connect(5, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("127.0.0.53")}, 16) = 0
     64 bytes from 183.2.172.42: icmp_seq=1 ttl=51 time=25.3 ms

     --- www.a.shifen.com ping statistics ---
     1 packets transmitted, 1 received, 0% packet loss, time 0ms
     rtt min/avg/max/mdev = 25.305/25.305/25.305/0.000 ms
     +++ exited with 0 +++
Enter fullscreen mode Exit fullscreen mode

其中可以修改/etc/nsswitch.conf中的hosts: file dns就不会有multicast DNS请求了, 修改是自动刷新的

NOTICE:上述strace监听openat系统调用,open可能已经没有用了

ncsd

其中nscd(Name Service Cache Daemon)是全局各种解析服务的缓存,数据库中存储了各种解析类型映射的缓存。nscd manpage


网上看到这篇文章总结的不错。
注意,/etc/nscd.conf文件注释必须是首个字符为#,不然会服务可能启动失败。
主要提下调试,有两种方式,一种是使用日志文件,一种是在终端打印日志查看

  1. 使用日志文件 修改/etc/nscd.conf中日志相关项, logfile(/var/log/nscd.log)debug-file(0-5, 可以选择5), 然后重启nscd, 就可以查看(tail -f /var/log/nscd.log), 当ping或者getent hosts就会更新日志。
  2. 终端查看 先暂停sudo systemctl stop nscd, 然后sudo nscd -g, 就会在终端打印日志了,但是也要设置日志等级。

总结

使用getent, ping这些命令都会走glibc提供的DNS服务,底层使用getaddrinfo函数; 其他命令如host(man 1 host), dig, nslookup走的是全局DNS解析服务,但是也可以自己配置DNS服务器直接网络请求。查看manpage, 会发现都是BIND 9提供的命令,在找BIND 9你就会发现他们为什么不走glibc的了 :)

编程语言DNS服务

目前有很多库提供DNS服务,比如libevent, c-ares

很多语言如果底层使用使用glibc的函数也是走NSS那套,比如python,java等,但是也有的语言提供独立的DNS服务,比如自举后的golang。

NodeJS底层使用c-ares提供DNS服务。
libcurl可以使用自己提供DNS server,但是需要编译时候添加c-ares库; 默认走glibc那套。

Nodejs

Nodejs文档真心不错,最后说明了两种方式的特点和注意点。
默认情况(使用dns.lookup()),使用getaddrinfo走系统那套查询方式; 也可以使用 dns.resolve()dns.resolve*(), and dns.reverse(),底层使用c-ares,直接网络请求DNS服务器获取解析结果。

小结

又整理了一篇无用知识,发现strace真是个好工具,解决问题都要想到她。

Image of Datadog

How to Diagram Your Cloud Architecture

Cloud architecture diagrams provide critical visibility into the resources in your environment and how they’re connected. In our latest eBook, AWS Solution Architects Jason Mimick and James Wenzel walk through best practices on how to build effective and professional diagrams.

Download the Free eBook

Top comments (0)

Heroku

Build apps, not infrastructure.

Dealing with servers, hardware, and infrastructure can take up your valuable time. Discover the benefits of Heroku, the PaaS of choice for developers since 2007.

Visit Site