1. 三者到底是什么关系:先用人话讲清楚
IP、域名、DNS,这三个词只要搞过网络就绕不开。我做了十来年运维,被问得最多的问题往往不是那些复杂的路由协议,而是这三个基础概念:IP到底是不是“网址”?DNS是不是“域名服务器”?域名和IP哪个更重要?其实把它们的定位想清楚了,后面看抓包、配路由、排查网站打不开都会轻松很多。
先打个比方。IP是每台设备在网络里的“门牌号”,比如192.168.1.10,就是你当前设备在小区里的具体位置。域名是给人记的“名字”,比如example.com,你不必记住那串数字,只需要记住名字。DNS则是那本“小区通讯录”,当你喊出example.com,通讯录会帮你查出它对应的门牌号,也就是93.184.216.34。网络通信最终靠的还是IP地址,域名只是入口,DNS是入口背后的翻译机制。
这里要注意一点:题目里说“域名解析DNS”,容易让人理解成“DNS等于域名”。其实DNS全称是Domain Name System,是一套分布式数据库,不是某一台单独的主机。所谓“域名解析”,就是通过这套系统把域名翻译成IP地址。你输入的域名会先经过解析,拿到目标IP,再去走TCP/IP协议里的连接流程。
理解这个关系对谁最有用呢?我觉得三类人最需要:一是刚接触网络的新手,容易被一堆概念绕晕;二是前后端开发,部署环境和调试回调域名时经常要跟DNS打交道;三是运维,排查“域名解析失败”“IP冲突”这类问题,靠的就是理清三者的边界。下面的内容我会按实际排查思路来写,尽量不堆术语。
2. 一次完整访问的历程:DNS 解析细节拆解
2.1 第一步不是找 DNS 服务器,而是查本地缓存
我在培训新人的时候,喜欢问一个问题:你在浏览器里输入一个域名,第一件事是不是去问互联网上的DNS服务器?答案是否定的。浏览器会先查自己的DNS缓存,然后查操作系统的DNS缓存,再然后查hosts文件。只有这三层都没有结果,才会上网查询。
hosts文件是一份“本地手写通讯录”,位置在Windows的C:\Windows\System32\drivers\etc\hosts,Linux和macOS下是/etc/hosts。里面可以写域名和IP的对应关系,比如:
127.0.0.1 localhost 192.168.56.10 dev.example.com很多开发者在本地调试时,根本不需要注册域名或改公网DNS,只要把虚拟机的IP和自定义域名写进hosts,浏览器就把这个域名当成真实域名来访问。这也是为什么后面我会单独讲“本地加虚拟机多站点配置”,因为hosts就是最轻量级的本地DNS。
要注意,浏览器缓存和系统缓存是可以清掉的。浏览器可以用隐身模式或者开发者工具里的“清空缓存并进行硬性重新加载”,系统层在Windows下用ipconfig /flushdns,Linux下用sudo resolvectl flush-caches。但hosts文件是写死的,优先级极高,清缓存也清不掉,排查时第一件事就是看它。
2.2 本地没有缓存:递归 DNS 开始分布式查询
如果本地三层缓存都没有命中,系统会把域名解析请求交给网络配置里指定的DNS解析器。这个解析器可能是路由器下发的,可能是公共DNS,也可能是公司内网DNS。它并不是全知全能的,而是替你跑腿。
以www.example.com为例,解析器大致会这样工作:先问根DNS服务器“.com这个顶级域归谁管”,根服务器告诉它去问.com顶级域服务器;它再问.com顶级域服务器“example.com归谁管”,对方返回example.com的权威DNS服务器地址;最后它去问example.com的权威DNS服务器,拿到www这条主机记录对应的IP地址。
这个过程里,本地解析器一直在递归地替你查询,而每层DNS服务器只是迭代地回答一句话。查询结果会在沿途各级缓存一段时间,也就是TTL。TTL到期前,你再去访问同一个域名,可能直接从本地DNS缓存中拿到结果,速度会快很多。
这里想强调一个容易混淆的点:DNS并不是一条“域名->IP”的简单关系表,而是一个分级授权的分布式系统。根、顶级域、权威域各管一段。理解了这一点,你就能明白为什么有些域名解析很慢,为什么改了DNS记录不能立刻全球生效。
2.3 常见解析记录:查IP、做别名、收邮件
很多人以为DNS里只有A记录,其实不只。实际用到比较多的有下面这几种:
| 记录类型 | 作用 | 常见场景 |
|---|---|---|
| A | 把域名解析到IPv4地址 | 网站、服务器的IPv4指向 |
| AAAA | 把域名解析到IPv6地址 | 支持IPv6的站点 |
| CNAME | 把别名指向另一个域名 | 让www指向主域名 |
| MX | 指定邮件服务器 | 让别人知道你的邮件交给谁收 |
| NS | 指定某域名的权威DNS服务器 | 域名的DNS托管设置 |
| PTR | 从IP反查域名 | 反垃圾邮件、IP归属校验 |
后面要讲的“域名查询IP”,本质上就是查A记录或AAAA记录。比如在命令行执行dig example.com A +short,出来的就是服务器IPv4地址。如果你看到一条记录是CNAME,说明它的最终解析还要继续追下去,直到追到一个A或AAAA记录为止。
3. 配置与实操:从修改 DNS 到解析结果验证
3.1 Ubuntu 22.04 修改 DNS 的正确姿势(为什么不能直接改 resolv.conf)
网上很多教程会让你直接改/etc/resolv.conf,但Ubuntu 22.04上这绝对是个坑。这个文件通常是个软链接,指向/run/systemd/resolve/stub-resolv.conf,由systemd-resolved动态生成。你手动改完,重启网络或者重启系统,内容又会被覆盖回去。我早期踩过一次,后来再也不用这种临时改法了。
如果是临时改一下当前生效的DNS,可以用resolvectl命令。假设接口名是eth0,想把DNS改成223.5.5.5:
sudo resolvectl dns eth0 223.5.5.5 sudo resolvectl status这种方式适合临时测试,重启后会失效。如果要持久化,就应该改netplan配置。Ubuntu 22.04的netplan文件一般在/etc/netplan/目录下,文件名可能是00-installer-config.yaml或01-network-manager-all.yaml,内容大致如下:
network: version: 2 ethernets: eth0: dhcp4: true dhcp4-overrides: use-dns: false nameservers: addresses: [223.5.5.5, 119.29.29.29]写完保存后执行sudo netplan apply。如果你希望同时从DHCP拿IP但不拿DNS,那段dhcp4-overrides很关键,否则DHCP下发的DNS可能会把你手工填的nameservers顶掉。
如果是桌面版Ubuntu,通常由NetworkManager接管网络,你可以在图形界面里改,也可以直接用nmcli:
nmcli con mod "Wired connection 1" ipv4.dns "223.5.5.5,119.29.29.29" nmcli con up "Wired connection 1"改完以后不要急着看结果,先确认一下当前生效配置:cat /etc/resolv.conf看看解析器是不是systemd-resolved,再用resolvectl status看看接口实际用的DNS。很多情况下你觉得改了没用,其实是改的地方不对。
3.2 Linux 改完 DNS 重启就被还原?问题不在命令
我见过最多的问题就是:用命令改完DNS当时是能用的,但重启系统或者重启网络服务后,DNS又变回原来的值,甚至变成空。问题几乎都出在持久化上面。临时命令只作用于当前运行状态,没有写进任何配置文件,DHCP重新下发网络参数后,原来的配置自然被覆盖。
如果你在用systemd-resolved,还可以遇到一个更隐蔽的问题:明明netplan里写了新的DNS,但解析结果还是旧的。这种情况我一般先做两件事:
sudo resolvectl flush-caches cat /etc/resolv.conf刷新缓存很重要,systemd-resolved会把之前的解析结果缓存一段时间,即使上游DNS已经切换了,缓存里还是旧内容,看起来就像没改成功。
还有个细节:不要在一个网卡上同时设置多个来源的DNS,否则顺序会影响最终解析。比如你可能在netplan里写了一个,NetworkManager又接管的同一个网卡,最终生效的是谁,取决于哪个服务在管理这个接口。排查时用resolvectl status看清楚接口的DNS来源,比自己瞎猜高效得多。
3.3 telnet IP 端口怎么看通不通:三条命令足够
“telnet ip 端口 命令怎么看通不通”这个问题,几乎是每个运维新人都要问的。最传统的判断方法就是:
telnet 192.168.1.10 80如果能出现Connected to 192.168.1.10或者切换到一个空的黑窗口,说明IP能到,端口上也有服务在监听。如果很快提示Connection refused,说明网络通,但这个端口没人监听。如果一直卡着不动直到超时,那多半是中间有防火墙把包丢了。
不过现在的Ubuntu默认不带telnet客户端,而且telnet本身也没有超时参数,卡住时很烦。我更推荐用nc:
nc -zv -w 3 192.168.1.10 80-z表示只测试端口不发送数据,-v显示详细信息,-w 3是超时3秒。还有一种不需要安装任何工具的办法,用bash自带的/dev/tcp:
timeout 3 bash -c 'echo >/dev/tcp/192.168.1.10/80' && echo "port open" || echo "port closed"这段命令的意思是:向目标IP的80端口建立一个TCP连接,如果成功就输出open,失败就输出closed。它依赖bash特性,换成sh执行可能不支持。这里的关键是分清refused和timeout:前者是服务器明确拒绝,后者是包被丢了,两者对应的排查方向完全不同。
3.4 域名查询 IP 和反向查询
日常查域名对应的IP,我一般优先用dig,而不是ping。ping走的是ICMP协议,有些服务器禁Ping,域名解析出来也可能不给你回应;而dig直接问DNS服务器,和网站本身通不通无关,更适合定位问题。
dig +short example.com dig www.example.com A +short如果想绕过本地DNS,直接指定用某个公共DNS查询:
dig @223.5.5.5 www.example.com A +shortWindows上没有dig怎么办?用nslookup,习惯用哪个都行:
nslookup -type=A www.example.com反向查询则是从IP查域名,靠的是PTR记录:
dig -x 93.184.216.34这里要提醒一句:IP归属地查询和DNS反查是两回事。反查只能得到这条IP有没有配置对应的PTR记录,而“这个IP属于哪个城市、哪个运营商”是另一套IP地理库在做的事,不要把两者混在一起。
4. 局域网实战:IP 冲突、静态 IP 和本地域名
4.1 IP 冲突排查
局域网里出现“IP地址冲突”的时候,最典型的症状是设备一会儿能上网一会儿不能,哪怕你什么都没动。原因很简单:网络里有两个设备用了同一个IP,路由器或者交换机的ARP表会来回漂移,一会把包送给A,一会送给B。
排查时我会用最直接的命令。Windows下先看ARP表:
arp -aLinux下用:
ip neigh如果看到同一个IP对应多个MAC地址,而且这些MAC还在交替更新,基本可以确定冲突。接着进路由器后台看在线设备列表,找出这个IP对应的实际设备,再手动把其中一台改成别的IP。长期解决方案是在DHCP服务器里做静态绑定,让每个MAC地址固定拿到唯一的IP。
这里要注意,虚拟机、手机、各种智能硬件都容易造成冲突,特别是从虚拟机镜像克隆出来的机器,网卡MAC没变,但系统之前已经缓存了旧IP,重启后很可能抢IP。遇到陌生的MAC地址,先不要急着封禁,先查清楚是哪个设备再用。
4.2 修改虚拟机 IP 地址
修改虚拟机IP是开发环境里经常要做的操作,也是容易翻车的一步。Linux虚拟机改IP,我建议用netplan而不是直接ifconfig eth0 192.168.x.x。先ip a看清楚网卡名,然后编辑netplan配置文件,把dhcp4改成false并设置静态地址:
network: version: 2 ethernets: ens33: dhcp4: false addresses: - 192.168.56.10/24 gateway4: 192.168.56.1 nameservers: addresses: [223.5.5.5]执行sudo netplan apply后,再用ip addr show ens33确认地址是否生效。Windows虚拟机就简单多了,在“网络和共享中心”里的IPv4属性里直接改,记得填子网掩码和默认网关。改完如果上不了网,先ping网关,再ping外部IP,能快速判断是IP问题还是路由问题。
还有一个容易忽视的点:虚拟机的网络模式不同,IP规则也不同。NAT模式下,虚拟机和宿主机通常不在同一网段,靠虚拟网卡转发;Host-only模式下,它和宿主机应该是同一网段,但出不了外网。你要把自定义域名解析到虚拟机IP时,就得保证宿主机能直接访问那个IP,否则hosts写了也访问不了。
4.3 本地+虚拟机多端口 Nginx 多站点自定义域名
开发环境里做多站点,不一定非要买域名。比如虚拟机IP是192.168.56.10,我想在浏览器里用dev.test和admin.test访问两个站点,可以在宿主机hosts里写:
192.168.56.10 dev.test 192.168.56.10 admin.test然后在虚拟机的Nginx里配置虚拟主机:
server { listen 80; server_name dev.test; root /var/www/dev; } server { listen 80; server_name admin.test; root /var/www/admin; } server { listen 8080; server_name dev.test; root /var/www/dev-mobile; }这样http://dev.test会命中第一个站点,http://admin.test命中第二个站点,http://dev.test:8080命中第三个站点。要注意hosts文件里的域名最好带一个不存在的顶级域或后缀,比如.test、.local,避免和真实域名撞车。
有人会问,那网页授权回调域名呢?像OAuth授权登录这种场景,第三方平台会从公网DNS去解析你的回调域名,它不会读你本地hosts。所以你在本地用dev.test调试没问题,但真实回调场景必须在域名服务商那边把回调域名指向服务器公网IP,同时做好Nginx反向代理,否则平台请求到你服务器上的时候还是会失败。
5. DNS 的坑和安全:要不要自己搭 DNS 服务器
5.1 为什么解析结果会不对:缓存、TTL 和解析链路
当你改了域名解析记录,却发现过了一个小时还是访问到旧IP,不用急着怀疑自己操作有问题。每条DNS记录都有一个TTL值,比如300秒或3600秒,TTL没到期前,本地DNS服务器和客户端缓存都会把旧结果留着。你本地清缓存只是第一步,中间的公共DNS或运营商DNS可能还缓存着旧记录,只能等它自然过期。
这种情况下,我一般直接指定一个公共DNS来查“真实答案”:
dig @223.5.5.5 example.com A +short如果指定DNS查到的是新IP,而普通解析查到的是旧IP,说明本地或中间链路有缓存。如果两种方式都查到旧IP,才需要考虑域名服务商那边的解析记录是否真的改了,或者记录是不是写错了。
这里也延伸出一个安全问题:DNS传统上使用UDP 53端口明文传输,中间链路理论上可以篡改响应。现在新的做法是DoT和DoH,把DNS请求加密起来,但兼容性也需要自己验证。尤其是公司内网环境,自建DNS往往要做一些内部域名解析,这些安全性问题就要提前想清楚。
5.2 内网自建 DNS:为什么我建议先用 dnsmasq
如果只是为了内网解析、局域网内自定义域名、缓存加速,我建议先用dnsmasq,而不是一上来就搭Bind9。dnsmasq配置简单、资源占用低,非常适合几十台机器的小环境。
一个典型配置大概是这样的:
port=53 listen-address=127.0.0.1,192.168.1.1 cache-size=1000 server=223.5.5.5 address=/dev.local/192.168.1.20address=/dev.local/192.168.1.20表示把所有以dev.local结尾的域名都解析到192.168.1.20。这样内网机器只要把DNS指到这台服务器,就能直接使用自定义域名。前面说到的“本地+虚拟机多站点”场景,如果机器数量多,靠改hosts太累,就可以用dnsmasq统一管理。
自建DNS有一点要特别留神:服务器上如果已经跑了systemd-resolved,它会默认占用53端口。你需要先把systemd-resolved的stub监听停掉或者改端口,否则dnsmasq启动时会提示端口被占用。另外,如果这台DNS服务器要对外提供递归查询,建议做访问控制和限速,否则容易成为被利用的“公共递归”。普通家庭或小团队,我更推荐直接用公共DNS,省心很多。
5.3 DNS 问题排查速查表
| 现象 | 大概率原因 | 处理方向 |
|---|---|---|
nslookup显示无法解析 | 本地DNS配置不对或上游DNS不可达 | 换公共DNS,检查网络链路 |
| Linux重启后DNS还原 | 修改未持久化 | 用netplan或nmcli做持久配置 |
| 解析结果一直是旧IP | 缓存TTL未过期 | 清缓存、等待TTL、指定公共DNS查询 |
telnet端口报Connection refused | 目标端口没有服务监听 | 检查服务进程、端口绑定、监听地址 |
telnet端口一直超时 | 防火墙丢包或路由不通 | 依次ping网关、目标IP,清理防火墙规则 |
| 本地自定义域名访问不了虚拟机站点 | 虚拟机IP变了或者hosts写错 | 重新确认IP,检查Nginx server_name |
这几类问题看起来五花八门,但本质上都在IP、域名、DNS三角关系里打转:域名靠DNS解析成IP,IP靠路由到达目标,到达以后还要看端口和服务是否正常。能分清是哪一层的锅,排查速度会快很多。
最后分享一个我一直保留的习惯:遇到“网站打不开”,不要先猜,先用dig +short确认域名解析结果,再ping IP确认网络通不通,最后telnet IP 端口确认服务在不在。三步下来基本能把DNS、网络、服务三层问题分开。我自己在Ubuntu上踩得最深的坑就是/etc/resolv.conf被系统覆盖,所以现在不管临时测试还是长期使用,都下意识选择resolvectl或netplan。希望这篇能帮你把三者的关系理顺,下次再遇到解析问题,心里能有一条明确的排查路线。