阅读视图
我没法访问 dl.google.com —— 记一次 TUN 下的网络 debug
如果大家对目前中国大陆境内的网络环境足够了解,应该就会知道 dl.google.com 在很多情况下是可以直连访问的。比如,你可以通过 google.cn/chrome/?standalone=1 这个 URL 直接在境内的网络环境下下载 Chrome 的离线安装包,最终的下载域名就是 dl.google.com。
我平常的使用习惯是 24 小时开启代理工具的 TUN,让所有流量先经过一张虚拟网卡,再根据分流规则自动判断要不要走代理。这个习惯大部分时候都挺省心的——直到最近我用 yay 滚 Arch 的时候,突然遇到了 dl.google.com 的 SSL 连接建立失败。

而且不止是 yay,我的浏览器也返回了相同的结果:

当时我的第一反应是:是不是我那套分流规则又抽风了?(毕竟不是我自己写的,出事先甩锅很合理。)
规则确实是直连,这锅甩不掉
我特意去查了分流规则,针对 SNI 为 dl.google.com 的流量是直连访问的。

这就很奇怪了。按理说:
dl.google.com本身在国内网络环境里经常是能直连的- 规则也明确写了 DIRECT
说实话,这个问题我之前也遇到过,但那时手上有优先级更高的事,就直接关掉代理工具绕过了它完成更新。好在我现在刚处理完手头事情,正处于无事可做的状态,于是决定认真把这个坑填了。
解析到海外 IP 了
我先把代理工具的 fake-ip 关掉,换成真实 IP 解析(避免再引入额外变量),然后用 curl -vv 去访问 dl.google.com 的下载链接,看看它到底要连到哪里去。

现在回头看我能很笃定地说:这里解析出来的这个 IP 来自 Google 的海外 CDN,而不是国内机房/国内可达的那一类。

如果大家不清楚的话:dl.google.com 针对国内访客的 DNS 解析结果,很多时候会返回国内可达的 IP(否则你也没法在境内直连下载)。而这里返回的这个海外 IP 在我这条网络上是不可达的;再加上我在喵喵工具里给 dl.google.com 配的是直连,于是就变成了:
DNS 给了一个「海外 IP」
- 规则要求 DIRECT = 直连到一个我连不上的地方 = TLS 握手失败
所以这并不是「直连规则没生效」,而更像是:规则生效得非常彻底,但 DNS 把我带沟里了。
谁在负责回答 dl.google.com?
Mihomo 内核目前的 DNS 配置项主要是下面四个:
nameserver: 默认解析服务器(大部分域名都走这里)direct-nameserver: 直连域名的解析服务器(较新版本才有)proxy-server-nameserver: 节点域名解析(跟这次没啥关系)default-nameserver: 用来解析 DNS 配置里「域名形式」的 nameserver(也先不展开)
dl.google.com 被规则指定为直连域名,所以 Mihomo 理论上应该优先参考 direct-nameserver;如果没设置,就回落到 nameserver。
而我当时的 nameserver 配置是:
https://dns.alidns.com/dns-query
我当时的直觉很简单:既然解析结果像是从海外 CDN 池里出来的,那就先验证一下是不是这条阿里 DNS(DoH)返回的就是海外 IP。
直接查阿里 DoH,确实回了海外池
阿里 DoH 提供了一个 JSON 查询接口,所以我直接用 curl 去请求:
curl -s 'https://dns.alidns.com/resolve?name=dl.google.com&type=A'

返回的 IP 就是我之前遇到的那个海外 IP。到这一步我基本可以确认:至少在我当前这条网络出口下,阿里 DNS 对 dl.google.com 的解析结果就是“那一类”我访问不到的 IP。
这个问题需要两个条件同时成立(缺一不可)
写到这里必须强调一下:这事并不是「阿里 DNS 永远解析错」这么简单,我后来做了一圈对照,发现它其实很“苛刻”:
**只有在「移动宽带」+「阿里 DNS(包括 223.5.5.5 或 alidns 的 DoH)」这两个条件同时成立时,问题才可能稳定复现。**两个条件缺一不可。
更具体一点就是:
- 换成电信/联通的宽带:用同样的阿里 DNS,
dl.google.com的解析结果通常就正常 - 还是移动宽带,但不用阿里 DNS:解析结果也通常正常
- 移动宽带 + 阿里 DNS:高概率拿到海外池,然后直连就炸
我也用 itdog 做了下全国解析测试,移动网络下的复现比例确实更高。

为什么会这样?老实说我没有能力给一个“全网唯一真相”的解释,我只能说现象非常一致,而且足够让我下结论:问题不是 TUN 本身,而是 TUN 下我的 DNS 选择把 dl.google.com 导向了一个在移动网络里不可达的地址池。
我最后怎么解决的?
既然问题出在「移动宽带 + 阿里 DNS」这个组合上,那解决方式也就很朴素了:别让 dl.google.com 继续走阿里 DNS 解析。
可以配置 direct-nameserver 或者 nameserver-policy,可以配置 119.29.29.29 等其他公共 DNS,或者干脆把 DNS 解析交给家里的路由器。
direct-nameserver:
- 192.168.8.1
nameserver-policy:
"dl.google.com": [119.29.29.29]
这么搞完之后,yay 更新恢复正常,浏览器也能直连访问 dl.google.com。
参见
GorillaServers–2025黑色星期五促销,168T磁盘 E5-2690v4*2 128G内存 1G不限流 159 美元/月
你的域名后缀拖慢你的网站速度了嘛?——再谈 DNS 冷启动
在上一篇博客中,我提到过一个核心观点——对于流量少、访客的地理位置不集中的小型站点,DNS 冷启动不是偶发的“意外”,而是一种被动的“常态”。
对于大多数站长而言,自己的站点流量不是一时半刻就能提上去的,因此我们的访客大概率都要走完一遍完整的 DNS 解析过程。上一篇博客中我提到过更改为距离访客物理位置更近的权威 DNS 服务器来提升速度,但 TLD(域名后缀)的 Nameservers 是我们无法改变的,也就是下图中红色背景的那一段解析过程。
sequenceDiagram
autonumber
participant User as 用户/浏览器
participant Local as 本地DNS<br>递归解析器
participant Root as 根域名服务器
participant TLD as 顶级域服务器<br>(TLD Server)
participant Auth as 权威DNS服务器
Note over User,Auth: DNS 递归查询完整流程
User->>Local: 查询域名 www.example.com
Note over Local: 检查缓存 (MISS)
Local->>Root: 查询 .com 的 TLD 服务器
Root-->>Local: 返回 .com TLD 服务器地址
%% --- 重点高亮区域开始 ---
rect rgb(255, 235, 235)
Note right of Local: ⚠️ 本文核心讨论区域 <br> (TLD 解析时延)
Local->>TLD: 查询 example.com 的权威服务器
Note left of TLD: 这里的物理距离与 Anycast 能力<br>决定了是否存在数百毫秒的延迟
TLD-->>Local: 返回 example.com 的权威服务器地址
end
%% --- 重点高亮区域结束 ---
Local->>Auth: 查询 www.example.com 的 A 记录
Auth-->>Local: 返回 IP 地址 (e.g., 1.1.1.1)
Note over Local: 缓存结果 (TTL)
Local-->>User: 返回最终 IP 地址
User->>Auth: 建立 TCP 连接 / HTTP 请求
所以,如果你还没有购买域名,但想要像个 geeker 一样追求极致的首屏加载(哪怕你并没有多少访客),你该选择哪个 TLD 呢?
简单测试
一个简单的方法是,直接去 ping TLD 的 nameserver,看看访客所请求的公共 DNS 服务器在这一段解析中所花费的时常。
以我的域名 zhul.in 为例,在 Linux 下,可以通过 dig 命令拿到 in 这个 TLD 的 Nameserver
dig NS in.

随后可以挑选任何一个 Nameserver(公共 DNS 服务器其实有一套基于历史性能的选择策略),直接去 ping 这个域名

我这里的网络环境是杭州移动,如果我在我的局域网开一台 DNS 递归服务器,这个结果就是在上面那张时序图中红色部分所需要时长的最小值(DNS 服务器还需要额外的时长去处理请求)。
借助一些网站提供的多个地点 ping 延迟测试,我们可以推测这个 TLD 在全球哪些国家或地区部署了 Anycast(泛播)节点,下图为 iplark.com 提供的结果。

可以推测,in 的 TLD Nameserver 起码在日本、香港、美国、加拿大、欧洲、澳大利亚、巴西、印度、南非等多地部署了 Anycast 节点,而在中国大陆境内的延迟较高。
作为对比,我们可以通过同样的方法再看看 cn 域名的 TLD Nameserver 的 Anycast 节点。

经过 itdog.cn 的测试,推测 cn 域名的 TLD Nameserver 可能仅在北京有节点。
更进一步的的实验方案
上面的测试方法只是一个简易的判断方法,在现实中会有很多的外部因素影响 DNS 冷启动的解析时长:
- 公共 DNS 服务器和 TLD Nameserver 之间存在 peer,他们的通信非常快
- TLD Nameserver 的性能差,需要额外的几十 ms 去处理你的请求
- TLD 的几个 Nameserver 有快慢之分,而你选用的公共 DNS 服务器能根据历史数据选择较快的那个
- ...
所以,我们需要有一个基于真实的 DNS 解析请求的测试方案
对于 DNS 冷启动相关的测试一直以来存在一个困境——公共 DNS 服务器不归我们管,我们无法登陆上去手动清除它的缓存,因此所有的测试都只有第一次结果才可能有效,后续的请求会直接打到缓存上。但这一次我们测试的是公共 DNS 服务器到 TLD Nameserver 这一段的延迟,在 Gemini 的提醒下,我意识到可以在不同地区测试公共 DNS 对随机的、不存在的域名的解析时长,这能够反应不同 TLD 之间的差异。
所以,测试代码在下面,你可以使用常见的 Linux 使用 bash 执行这段代码,需要确保装有 dig 和 shasum 命令,并且推荐使用 screen / tmux 等工具挂在后台,因为整个测试过程可能会持续十几分钟。如果你所采用的网络环境在中国大陆境内,我建议你把代码中的公共 DNS 服务器换成 223.5.5.5 / 119.29.29.29 ,应该会更符合境内访客的使用环境。
#!/bin/bash
# ================= 配置区域 =================
# CSV 文件名
OUTPUT_FILE="dns_benchmark_results.csv"
# DNS 服务器
DNS_SERVER="8.8.8.8"
# 待测试的 TLD 列表
# 包含:全球通用(com), 国别(cn, de), 热门技术(io, xyz), 以及可能较慢的后缀
TLDS_TO_TEST=("com" "net" "org" "cn" "in" "de" "cc" "site" "ai" "io" "xyz" "top")
# 每个 TLD 测试次数
SAMPLES=1000
# 每次查询间隔 (秒),防止被 DNS 服务器判定为攻击
# 1000次 * 0.1s = 100秒/TLD,总耗时约 15-20 分钟
SLEEP_INTERVAL=0.1
# ===========================================
# 初始化 CSV 文件头
echo "TLD,Domain,QueryTime_ms,Status,Timestamp" > "$OUTPUT_FILE"
echo "============================================="
echo " DNS TLD Latency Benchmark Tool"
echo " Target DNS: $DNS_SERVER"
echo " Samples per TLD: $SAMPLES"
echo " Output File: $OUTPUT_FILE"
echo "============================================="
echo ""
# 定义进度条函数
function show_progress {
# 参数: $1=当前进度, $2=总数, $3=当前TLD, $4=当前平均耗时
let _progress=(${1}*100/${2})
let _done=(${_progress}*4)/10
let _left=40-$_done
# 构建填充字符串
_fill=$(printf "%${_done}s")
_empty=$(printf "%${_left}s")
# \r 让光标回到行首,实现刷新效果
printf "\rProgress [${_fill// /#}${_empty// /-}] ${_progress}%% - Testing .${3} (Avg: ${4}ms) "
}
# 主循环
for tld in "${TLDS_TO_TEST[@]}"; do
# 统计变量初始化
total_time_accum=0
valid_count=0
for (( i=1; i<=${SAMPLES}; i++ )); do
# 1. 生成随机域名 (防止缓存命中)
# 使用 date +%N (纳秒) 确保足够随机,兼容 Linux/macOS
RAND_PART=$(date +%s%N | shasum | head -c 10)
DOMAIN="test-${RAND_PART}.${tld}"
TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S")
# 2. 执行查询
# +tries=1 +time=2: 尝试1次,超时2秒,避免脚本卡死
result=$(dig @${DNS_SERVER} ${DOMAIN} A +noall +stats +time=2 +tries=1)
# 提取时间 (Query time: 12 msec)
query_time=$(echo "$result" | grep "Query time" | awk '{print $4}')
# 提取状态 (status: NXDOMAIN, NOERROR, etc.)
status=$(echo "$result" | grep "status:" | awk '{print $6}' | tr -d ',')
# 3. 数据清洗与记录
if [[ -n "$query_time" && "$query_time" =~ ^[0-9]+$ ]]; then
# 写入 CSV
echo "${tld},${DOMAIN},${query_time},${status},${TIMESTAMP}" >> "$OUTPUT_FILE"
# 更新统计
total_time_accum=$((total_time_accum + query_time))
valid_count=$((valid_count + 1))
current_avg=$((total_time_accum / valid_count))
else
# 记录失败/超时
echo "${tld},${DOMAIN},-1,TIMEOUT,${TIMESTAMP}" >> "$OUTPUT_FILE"
current_avg="N/A"
fi
# 4. 显示进度条
show_progress $i $SAMPLES $tld $current_avg
sleep $SLEEP_INTERVAL
done
# 每个 TLD 完成后换行
echo ""
echo "✅ Completed .${tld} | Final Avg: ${current_avg} ms"
echo "---------------------------------------------"
done
echo "🎉 All Done! Results saved to $OUTPUT_FILE"
测试结果
免责声明:以下测试结果仅供参考,不构成任何购买推荐,且仅代表测试当日(2025.11.24)的网络情况,后续不会进行跟进。DNS 冷启动对于大型站点几乎没有影响,仅小站需要关注。本次测试中,所有境内检测点使用 223.5.5.5 作为 DNS 服务器,境外检测点使用 8.8.8.8。
| 测试点/延迟(ms) | .com | .net | .org | .cn | .in | .de | .cc | .site | .ai | .io | .xyz | .top |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 🇨🇳 上海腾讯云 | 438 | 429 | 470 | 30 | 535 | 353 | 476 | 454 | 367 | 485 | 444 | 43 |
| 🇨🇳 北京腾讯云 | 425 | 443 | 469 | 17 | 350 | 420 | 466 | 647 | 582 | 461 | 559 | 9 |
| 🇭🇰 香港 Yxvm | 75 | 75 | 363 | 227 | 6 | 11 | 61 | 6 | 33 | 126 | 5 | 7 |
| 🇨🇳 彰化(台湾) Hinet | 90 | 87 | 128 | 213 | 59 | 38 | 76 | 37 | 73 | 94 | 36 | 47 |
| 🇯🇵 大阪 Vmiss | 20 | 19 | 244 | 309 | 15 | 24 | 17 | 35 | 19 | 65 | 37 | 90 |
| 🇸🇬 新加坡 Wap | 6 | 9 | 139 | 398 | 6 | 10 | 7 | 17 | 7 | 110 | 17 | 66 |
| 🇺🇸 洛杉矶 ColoCrossing | 7 | 7 | 307 | 137 | 4 | 64 | 5 | 62 | 5 | 49 | 47 | 231 |
| 🇩🇪 杜塞尔多夫 WIIT AG | 16 | 17 | 288 | 82 | 75 | 15 | 14 | 24 | 66 | 73 | 24 | 306 |
| 🇦🇺 悉尼 Oracle | 33 | 31 | 12 | 338 | 7 | 13 | 121 | 7 | 10 | 9 | 7 | 191 |
通过上面的数据,我们可以看到 .cn 和 .top 是所有测试的域名后缀中在中国大陆境内解析速度最快的,但选择 .cn 和 .top 意味着你需要牺牲其他地区访客的解析速度。而像 .com、.net、.org 这些通用的域名后缀在全球绝大部分地区表现良好,而在中国大陆境内的解析速度则相对较慢,因为他们没有在大陆境内部署 Anycast 节点。在 DNS 冷启动的场景下(如果你的站点访客少,那几乎每次访问都是冷启动),首屏加载时间会因此增加 500ms 甚至更多。
经 v2ex 的网友 Showfom 提醒,GoDaddy 作为注册局掌握的部分 TLD 的 Nameserver 同样在中国大路境内拥有 Anycast 节点,比如 .one、.tv、.moe 等。另, Amazon Registry Services 旗下的 .you 域名经我测试也有境内的 Anycast 节点。其他域名后缀可自行测试。
你可以点击这里下载完整的测试结果 CSV 文件进行进一步的分析。
内网域名管理+DNS加速+DNS去广告+魔法上网的终极系统
最近使用openWRT实现了一套几乎终极效果的内网域名管理+DNS加速+DNS去广告+魔法上网的系统,极致的复杂配置之后,就是最简单的无感使用方式。本文将讲述其构架和实现细节,现在任何人都可以无需任何配置就可以直接域名访问 nas 中部署的内网各种服务,加访问 google openai 等服务。
TIPS: 本文不是写给小白的,而是给懂技术的用户的。可能不会说的很细,但能看懂的自然能看懂,看不懂的,写得再细致看了还是白看。

什么是DNS
首先让我们简单了解什么是 DNS,为什么要配置 DNS
DNS 解析器(也称解析器)将域名转换为互联网上的 IP 地址。
每次您的计算机使用域名(例如example.com)连接到网站时,它都需要知道该网站的 IP 地址,即一组唯一的数字。因此,它会联系 DNS 解析器并获取网站的当前 IP 地址。
一般来说,DNS解析器是去中心化DNS系统的一部分。当你向解析器发送请求时,它会联系其他DNS服务器来获取地址。

您计算机使用的 DNS 解析器通常由您的互联网服务提供商 (ISP) 选择。如果您想为您的网络使用其他 DNS 解析器,您可以自行配置网络以启用它。您可以在操作系统的网络设置或家用路由器的管理界面中更改此配置。
DNSMasq + DNSCrypt Proxy
开胃小菜的配置

-
和nginx 配合使用。 简单的可以直接修改
/etc/hosts。 -
DNSCrypt Proxy: 作为DNS前端访问DOH的DNS
-
DNSMasq: 作为DNS后端,连接到DNSCrypt Proxy,并配置本地域名。还可以添加DNS去广告功能,浏览器插件去广告非常消耗CPU和内存,但是在DNS前端去广告,资源消耗低,并一次性解决所有的访问终端(pc,手机,平板)广告问题。
配置一个去广告,本地域名管理工具。
TIPS: 使用时需要手动设置本机dns指向nas 的 DNS
dnsmasq + smartDNS
直接接管路由器下游所有设备的DNS,直接无感访问私有域名!
最推荐的使用方式!用户不需要任何配置,任何软件的安装,真正的无感使用!

整体架构图
graph LR
subgraph 用户网络访问
User[用户] -->|域名访问| Router[OpenWrt路由器]
end
subgraph OpenWrt路由器
Router --> |DNS请求| DNSMASQ[dnsmasq]
DNSMASQ -->|上游查询| SmartDNS[smartdns<br>加速/去广告]
Router -->|内网访问| NAS(域名管理)
SmartDNS -->|国外域名<br>DOH/DOT<br>流量代理| DNS
SmartDNS -->|国外域名<br>DOH/DOT<br>流量代理| DNS...
SmartDNS --> |国内域名| 国内DNS
SmartDNS --> |国内域名| 国内DNS...
passwall[passwall<br>透明代理] --> xray
OpenWrt_Script -->|更新配置| DNSMASQ
OpenWrt_Script -->|更新配置| SmartDNS
end
subgraph NAS服务器
NAS --> |内网域名| Nginx[Nginx<br>反向代理]
Nginx --> Emby[emby服务]
Nginx --> Iyuu[iyuu服务]
Nginx --> Other[其他服务]
Script[配置生成脚本<br>内网域名:IPv4/Ipv6] --> HTTP[HTTP配置服务]
HTTP -->|提供配置| OpenWrt_Script[OpenWrt更新脚本<br>内网域名]
end
subgraph 外网域名
Router -->|外网域名<br>代理访问| PROXYDOMAIN[被墙域名<br>Google openAI etc]
Router -->|外网域名<br>直接访问| DOMAIN[外网域名]
end
关键组件说明
- OpenWrt路由器:
dnsmasq:本地 DNS 服务器,处理客户端 DNS 请求smartdns:智能 DNS 解析服务,作为 dnsmasq 的上游- 定期执行配置更新脚本
- NAS服务器:
Nginx:反向代理所有服务(emby/iyuu等)- 配置生成脚本:自动获取本机 IP 并生成配置文件
- HTTP 服务:提供配置文件下载
- 配置更新流程:
- NAS 脚本定期生成配置文件
- OpenWrt 脚本定期拉取新配置
- 动态更新 DNS 服务配置
Nas 中 DNS 配置更新流程图
sequenceDiagram
participant NAS_Script as NAS 配置脚本
participant HTTP_Server as HTTP 服务
participant OpenWrt_Script as OpenWrt 更新脚本
participant DNSMASQ
participant SmartDNS
loop 定期执行
NAS_Script->>NAS_Script: 获取本机 IPv4/IPv6
NAS_Script->>NAS_Script: 生成配置文件
Note right of NAS_Script: dnsmasq.conf<br>smartdns.conf
NAS_Script->>HTTP_Server: 上传配置文件
end
loop 每小时执行
OpenWrt_Script->>HTTP_Server: 请求最新配置
HTTP_Server-->>OpenWrt_Script: 返回配置文件
OpenWrt_Script->>DNSMASQ: 应用新配置
OpenWrt_Script->>SmartDNS: 应用新配置
OpenWrt_Script->>DNSMASQ: 重启服务
OpenWrt_Script->>SmartDNS: 重启服务
end
配置文件生成逻辑
flowchart TD
Start[开始] --> GetIP{获取IP}
GetIP --> |命令| IPv4[ip -4 addr show]
GetIP --> |命令| IPv6[ip -6 addr show]
IPv4 --> Parse[解析有效IP]
IPv6 --> Parse
Parse --> GenConfig[生成配置文件]
subgraph 配置文件内容
GenConfig --> DnsmasqConf[address=/mydomain.com/NAS_IP]
GenConfig --> SmartDNSConf[domain-rules /mydomain.com/ -c NAS_IP]
end
GenConfig --> Save[保存到HTTP目录]
Save --> End[结束]
服务关系说明
| 组件 | 功能描述 |
|---|---|
| dnsmasq | 本地DNS缓存,将特定域名直接解析为NAS内网IP,绕过公网解析 |
| smartdns | 智能DNS解析,过滤广告/恶意域名,作为dnsmasq的上游提供纯净DNS结果 |
| Nginx | 反向代理服务,通过不同子路径转发到NAS上的不同服务(emby:8096, iyuu:8787等) |
| 更新脚本 | 双端协同工作保持DNS配置与NAS实际IP同步,解决动态IP变化导致的服务中断问题 |
优势特点
- IP动态适配:自动检测NAS的IPv4/IPv6变化,无需手动维护
- DNS分层处理:
pie
title DNS解析分层
"dnsmasq本地解析" : 40
"smartdns智能过滤/加速" : 30
"上游DNS服务" : 30
-
服务高可用:
- 域名访问不受NAS IP变化影响
- 内置服务自动重启机制
-
安全隔离:
- 所有服务通过Nginx暴露
- 内部服务不直接暴露公网
提示:建议将OpenWrt更新脚本设置为开机启动运行一次 + 每小时(或者时间更短)执行,NAS配置脚本在系统启动和网络变化时触发,实现无缝切换。
smartDNS 配置
这个可以很简单,也可以很复杂。
个人使用了一套集成了几十个DNS分线路解析的配置,配置说明很麻烦,写起来很长。
这部分内容暂略,后期再补充。各位用户可以先简单配置,后期看我这里的更新。
后话
考虑在不触碰某条线的情况下,怎么编写本文。有兴趣的过段时间再查看本文。
参考&致谢
系列教程
图书、音乐、视频多媒体锦集
- 视频图书和音乐完全自动化管理框架图解
- 如何建立自己的私人电子图书馆–出版书籍,网络小说,漫画一网打尽!
- Zlibrary 图书资源下载与 Calibre 图书管理打造结构化电子图书馆高阶教程
- 如何使用media Go,MusicBrainz,Mp3tag工具刮削音乐 整理音乐资料库
- 私人在线音乐服务器搭建与使用介绍
- 如何使用tinyMediaManager刮削电影和电视剧,动画,并自动下载字幕
- 使用jeckett,sonarr,iyuu,qt,emby打造全自动追剧流程
- hexo博客博文撰写篇之完美笔记大攻略终极完全版
Nas系列
- 从零开始玩PT-入门到精通
- 如何建立自己的私人电子图书馆–出版书籍,网络小说,漫画一网打尽!
- Zlibrary 图书资源下载与 Calibre 图书管理打造结构化电子图书馆高阶教程
- 音视频图书和音乐自动化管理框架图解
- 使用jeckett,sonarr,iyuu,qt,emby打造全自动追剧流程
- 如何使用tinyMediaManager刮削电影和电视剧,动画,并自动下载字幕
- Potplayer终极优化教程实现PC视频播放最强画质
- Transmission 使用及其配置
- Qbittorrent 参数详细设置教程
- IPFS从零开始快速入门教程
- 家庭网络优化指南:提升NAT类型,降低游戏延迟、提高下载速度
- 内网域名管理+DNS加速+DNS去广告+魔法上网的终极系统
- 如何创建属于自己的私人资料库与私人搜索引擎
- PT 工具集,Linux硬链接助手
- QNAP 修改应用启动顺序
- qnap硬盘移动位置
- qnap IO 错误消除
Docker系列
DNS 冷启动:小型站点的“西西弗斯之石”
当我们谈论网站性能时,我们通常关注前端渲染、资源懒加载、服务器响应时间(TTFB)等。然而,在用户浏览器真正开始请求内容之前,有一个至关重要却鲜少在性能优化方面被提及的部分—— DNS 解析。对于默默无闻的小型站点而言,“DNS Cache Miss”(缓存未命中)或我称之为“DNS 冷启动”,会成为绕不过去的性能瓶颈,也就是本文标题所提到的“西西弗斯之石”。
神话的隐喻:DNS 解析的漫长旅程
要理解这块“石头”的重量,我们必须重温 DNS 解析的完整路径。这并非一次简单的查找,而是一场跨越全球的接力赛:
- 起点:公共 DNS 服务器 — 用户发出请求,公共 DNS 服务器尝试在缓存中寻找答案。
- 首次“推石”:根服务器 — 缓存缺失(Cache Miss),公共 DNS 服务器被引向全球 13 组根服务器。
- 第二程:TLD 服务器 — 根服务器指向特定后缀(如
.com)的顶级域名服务器。 - 第三程:权威服务器 — TLD 服务器指向网站域名最终的“管家”——权威 DNS 服务器。
- 终点: 权威服务器返回最终的 IP 地址,再由公共 DNS 服务器返回给用户。
sequenceDiagram
participant User as 用户/浏览器
participant Local as 本地DNS<br>递归解析器
participant Root as 根域名服务器
participant TLD as 顶级域服务器<br>(.com, .org等)
participant Auth as 权威DNS服务器
Note over User,Auth: DNS递归查询完整流程
User->>Local: 1. 查询域名<br>www.example.com
Note over Local: 检查缓存<br>未找到记录
Local->>Root: 2. 查询 .com 的TLD服务器
Root-->>Local: 3. 返回 .com TLD服务器地址
Local->>TLD: 4. 查询 example.com 的权威服务器
TLD-->>Local: 5. 返回 example.com 的权威服务器地址
Local->>Auth: 6. 查询 www.example.com 的A记录
Auth-->>Local: 7. 返回 IP地址 (e.g., 1.1.1.1)
Note over Local: 缓存结果<br>(根据TTL设置)
Local-->>User: 8. 返回最终IP地址
Note over User,Auth: 后续流程
User->>Auth: 9. 使用IP地址建立TCP连接<br>开始HTTP请求
对于首次或长时间未访问的请求,这个过程意味着至少 4 次网络往返(RTT),而在涉及到 CNAME 等情况时则会更多。对于那些拥有完美缓存的大型网站来说,这块石头可能已被别人推到了山顶;但对小型站点,它总是在山脚等待它的西西弗斯。
多重世界:Anycast 的镜像迷宫
“既然 DNS 冷启动的代价如此之高,那我能否使用脚本定时访问自己的网站,提前让公共 DNS 缓存预热起来呢?”——这是我曾经设想的解题思路。
然而,这一思路在现代互联网的 Anycast(泛播)架构下,往往徒劳无功。
Anycast 的核心理念是:同一个 IP 地址在全球多个节点同时存在,用户请求会被路由到“距离最近”或“网络路径最优”的节点。
这意味着,Google DNS (8.8.8.8) 、Cloudflare DNS (1.1.1.1)、阿里 DNS (223.5.5.5)、腾讯 DNS (119.29.29.29) 等公共 DNS 服务器背后并不是一台中心化的服务器,而是一组分布在世界各地、动态路由的节点集群。
于是问题出现了:
- 我在上海运行的预热脚本,也许命中了 223.5.5.5 的上海节点;
- 但来自北京的访问者,却会被路由到 223.5.5.5 的北京节点;
- 这两个节点的缓存,彼此独立、互不共享。
从站长的视角来看,DNS 缓存不再是一个可预测的实体,而是分裂成一片片地理隔离、随时可变的“镜像迷宫”。
每个访客都在不同的山脚下推着自己的那块石头,仿佛世界上有成千上万个西西弗斯,孤独地在各自的路径上前行。
不可控的缓存与「冷启动的常态化」
这也解释了为什么即便一个小型网站有规律地被脚本访问,仍可能在真实访客那里出现明显的 DNS 延迟。因为「预热」只是局部生效 —— 它温暖的是某一个任播节点的缓存,而不是整个网络的全貌。而当 TTL 到期或缓存被公共 DNS 服务器采用 LRU 等算法清理时,这份温度也会悄然散去。
从宏观上看,这让“小流量站点”陷入了某种宿命循环:
- 因访问量低,缓存不易命中;
- 因缓存不命中,解析耗时高;
- 因解析耗时高,首屏性能差,用户更少访问;
- 因用户更少访问,缓存更难命中。
冷启动不再是偶发的“意外”,而是一种被动的“常态”。
我们能否让石头变轻?—— 减缓冷启动影响的策略
西西弗斯的困境看似无解,但我们并非完全无能为力。虽然无法彻底消除 DNS 冷启动,但通过一系列策略,我们可以显著减轻这块石头的重量,缩短它每次滚落后被推上山顶的时间。
权衡的艺术:调整 DNS TTL (Time-To-Live)
TTL(生存时间)是 DNS 记录中的一个关键值,它告知递归解析器(如公共 DNS、本地缓存)可以将一条解析记录缓存多久,尽管他们可能会被 LRU 算法淘汰。
拉长 TTL 可以有效提高缓存的命中率,减少 DNS 冷启动的情况,尽可能让西西弗斯之石保留在山顶上。
但拉长 TTL 是以牺牲灵活性作为代价的:如果你因为某些原因需要更换域名做对应的 IP 地址,过长的 TTL 可能会导致访客在很长一段时间内取得的都是已经失效的 IP 地址。
选择更快的“信使”:使用合适的权威 DNS 服务器
DNS 解析的最后一公里——从公共 DNS 服务器到你的权威 DNS 服务器——的耗时同样至关重要。如果你的域名所采用的 Nameserver 服务响应缓慢、全球节点稀少、又或者距离访客所请求的公共 DNS 服务器距离太远,那么即使用户的公共 DNS 节点就在身边,整个解析链条依然会被这最后一环拖慢。
如果我正在写的是一篇英文博客,那么我只需要说把 Nameserver 换成 Cloudflare、Google 等一线大厂就完事了。这些大厂提供免费的权威 DNS 托管业务,且在全球各地拥有大量节点,在这方面是非常专业且值得信赖的。
但我现在正在使用简体中文,根据我的博客统计数据,我的读者大多来自中国大陆,他们的站点访客大多也来自中国大陆,他们请求的公共 DNS 服务器大概率也都部署在中国大陆,而 Cloudflare/Google Cloud DNS 完全没有权威 DNS 服务器的中国大陆节点,这会拖慢速度。所以如果你的访客主要来自中国大陆境内,或许可以试试阿里云或者 Dnspod,他们主要的权威 DNS 服务器节点都在中国大陆境内,这在理论上可以减少公共 DNS 服务器与 权威 DNS 服务器之间的通信时长。
结语:推石头的人
DNS 冷启动的问题,从未有完美的解决方案。它像是互联网架构中注定存在的一段“延迟的诗意”——每个访问者都从自己的网络拓扑出发,沿着看不见的路径,一步步推着那块属于自己的石头,直到抵达你的服务器山顶,换得屏幕上第一个像素的亮起。
对小型站点而言,这或许是命运的重量;但理解它、优化它、监测它,便是我们在这条漫长上坡路上,为石头磨出更光滑的棱角。
参见
mDNS:Homelab 网络的良药
前
在 Homelab 中折腾了一段时间后,我发现自己陷入了一个困境:随着设备数量的增加,管理 IP 地址变得越来越痛苦。
典型的场景是这样的:
– SSH 到某台服务器:ssh user@192.168.1.101,等等,101 是哪台机器来着?
– 访问某个服务:`http://192.168.2.88:8080`,这个 88 又是什么?
– 更糟糕的是,DHCP 租约过期后 IP 变了,所有的配置都要改一遍
我尝试过给每台设备绑定静态 IP,但这种方案有几个问题:
- IP 地址难以记住:20+ 台设备,每个都要记住对应的 IP
- 静态绑定不优雅:在路由器上一个个配置 DHCP 静态绑定很繁琐
- 缺乏灵活性:设备迁移到其他网络时需要重新配置
后来我发现了 mDNS(多播域名系统),它完美地解决了这些问题。配置完成后,我可以直接使用 server3.local、pfsense.local 这样的域名来访问设备,再也不用记 IP 地址了。
这篇文章记录了我在 OpenWrt + pfSense 双层路由环境下配置 mDNS 的完整过程,包括踩过的坑和解决方案。
我的网络环境
我的网络是两层路由结构:
– 一级路由:OpenWrt,家庭主路由器,网段 192.168.1.0/24
– 二级路由:pfSense,虚拟机网络路由器,网段 192.168.2.0/24
这样设置的目的是将虚拟机网络与主网络解耦,让 VM 可以独立运行并组成一个独立的集群。但这也带来了跨网段访问的问题,mDNS 的 reflector 功能正好可以解决这个问题。
路由配置篇
我的一级路由用的是 OpenWrt,二级路由用的是 pfSense。虽然系统不同,但配置思路是类似的。
核心组件是 Avahi,一个开源的 mDNS/DNS-SD 实现。OpenWrt 和 pfSense 都可以直接安装。
OpenWrt 安装配置
安装 Avahi
opkg update
opkg install avahi-daemon-service-ssh avahi-daemon-service-http
修改配置文件
编辑 /etc/avahi/avahi-daemon.conf:
[server]
use-ipv4=yes
use-ipv6=yes
check-response-ttl=no
use-iff-running=no
allow-interfaces=br-lan # 指定监听的网络接口
[publish]
publish-addresses=yes
publish-hinfo=yes
publish-workstation=no
publish-domain=yes
[reflector]
enable-reflector=yes # 关键:启用 reflector 功能,用于跨网段转发
reflect-ipv=no
[rlimits]
rlimit-core=0
rlimit-data=4194304
rlimit-fsize=0
rlimit-nofile=30
rlimit-stack=4194304
rlimit-nproc=3
关键配置说明
– allow-interfaces=br-lan:指定 Avahi 监听的网络接口
– enable-reflector=yes:启用 reflector 功能,这是实现跨网段 mDNS 的核心
参考:
- https://openwrt.org/docs/guide-developer/mdns
-
https://openwrt.org/docs/guide-user/network/zeroconfig/zeroconf
pfSense 安装配置
安装插件
在 pfSense 的 Package Manager 中搜索并安装 avahi 插件。
关键问题:WAN 接口配置
这里我踩了个坑。pfSense 默认不允许在 WAN 接口启用 mDNS,这是出于安全考虑(如果 WAN 是公网,确实不应该暴露 mDNS)。
但在我的两层路由场景下,二级路由的 WAN 接口连接的是一级路由的 LAN,需要在 WAN 接口启用 mDNS 才能实现跨网段通信。
解决方法:修改插件源码
编辑 /usr/local/www/avahi_settings.php,找到并注释掉 WAN 过滤代码:
// vi /usr/local/www/avahi_settings.php
// 找到两处 wan 的过滤代码,注释掉:
// unset($available_interfaces['wan']);
修改后,在 Web 控制台就可以看到 WAN 选项了,同时选中 WAN 和 LAN 启用。
生成的配置文件
修改后 pfSense 会自动生成配置文件(位于 /usr/local/etc/avahi/avahi-daemon.conf):
这里需要注意:不推荐直接手动修改这个配置文件,因为它会被 GUI 覆盖。建议通过修改 PHP 源码让 GUI 支持 WAN 配置。
参考配置内容
# /usr/local/etc/avahi/avahi-daemon.conf
[server]
allow-interfaces=em0,em1 # WAN 和 LAN 接口
use-ipv4=yes
use-ipv6=no
[publish]
publish-addresses=yes
publish-domain=yes
[reflector]
enable-reflector=yes # 启用跨网段转发
可选:启用 D-Bus
某些情况下可能需要启用 D-Bus:
mkdir -p /var/run/dbus/
dbus-daemon --system
至此 pfSense 的配置就完成了。
防火墙配置
重要:mDNS 使用 UDP 5353 端口,需要在防火墙中开放此端口。
确保允许以下流量:
– 源:LAN 主机
– 目标:路由器本机(224.0.0.251,mDNS 组播地址)
– 端口:UDP 5353
具体配置方法因路由器而异,在 OpenWrt 或 pfSense 的防火墙规则中添加即可。
主机配置
路由器配置完成后,还需要配置各个主机才能使用 mDNS。我这里以 Ubuntu Server 为例。
设置 Hostname
首先给主机设置一个有意义的 hostname:
sudo hostnamectl set-hostname server3
之后就可以通过 server3.local 访问这台主机了。
配置网络为 DHCP
mDNS 的一大优势是不需要静态 IP,所以把网络配置改为 DHCP:
# 编辑 netplan 配置
vim /etc/netplan/00-installer-config.yaml
network:
ethernets:
ens34:
dhcp4: true
version: 2
# 应用配置
netplan apply
启用 systemd-resolved 的 mDNS 功能
Ubuntu 新版本使用 systemd-resolved 管理 DNS,需要在这里启用 mDNS:
# 编辑配置文件
vim /etc/systemd/resolved.conf
[Resolve]
MulticastDNS=yes
LLMNR=yes
或者用命令一键修改:
sed -i "s|#MulticastDNS=no|MulticastDNS=yes|g" /etc/systemd/resolved.conf
sed -i "s|#LLMNR=no|LLMNR=yes|g" /etc/systemd/resolved.conf
systemctl restart systemd-resolved
踩坑:Netplan 不支持 mDNS 配置
这里有个大坑。即使修改了 resolved.conf,mDNS 在网络接口上仍然是关闭的:
# 检查接口的 mDNS 状态
resolvectl mdns ens34
# 输出:Link 2 (ens34): no
# 手动启用
resolvectl mdns ens34 yes
但这个设置重启后会失效,因为 Netplan 不支持 mDNS 配置(相关 Bug,2019 年提出至今未修复)。
Netplan 会在
/run目录下生成配置文件,优先级高于/etc,导致手动修改无效。
解决方案:创建 systemd 服务
既然是开机自启动的问题,那就用 systemd 来解决:
创建 systemd 服务文件:
cat << EOF > /etc/systemd/system/user-set-mdns@.service
[Unit]
Description=Enable MulticastDNS on network interface
After=systemd-resolved.service
[Service]
ExecStart=resolvectl mdns %i yes
[Install]
WantedBy=multi-user.target
EOF
启用服务(替换 ens34 为你的网卡名称):
sudo systemctl enable user-set-mdns@ens34
sudo systemctl start user-set-mdns@ens34
sudo systemctl status user-set-mdns@ens34
至此,主机的 mDNS 配置就完成了,重启后也会自动生效。
方案二:使用 Avahi(适用于老系统)
如果你的系统没有使用 systemd-resolved(比如老版本的 Ubuntu 或 Debian),可以直接安装 Avahi:
sudo apt-get install avahi-daemon libnss-mdns libnss-mymachines
安装后 Avahi 会自动启动,无需额外配置。
验收测试
配置完成后,让我们测试一下效果。
基本测试
现在可以抛弃 IP 地址,直接使用 .local 域名访问设备:
ping server3.local
ping code-env.local
ping vm-proxy.local
ping pfsense.local
ping openwrt.local
实际使用场景
SSH 连接
# 以前
ssh user@192.168.2.101
# 现在
ssh user@server3.local
访问 Web 服务
# 以前
http://192.168.2.88:8080
# 现在
http://vm-proxy.local:8080
容器配置
# docker-compose.yml
services:
app:
environment:
- DATABASE_URL=postgresql://postgres@db-server.local:5432/mydb
跨网段测试
最重要的是测试跨网段访问,确认 reflector 功能正常工作:
# 从一级网络(192.168.1.x)访问二级网络设备
ping vm-server.local # 这台设备在 192.168.2.x 网段
# 从二级网络访问一级网络设备
ping openwrt.local # 这台设备在 192.168.1.x 网段
如果能 ping 通,说明 mDNS reflector 配置成功!
补充说明
二级路由的 IP 配置注意事项
后续重新部署时发现一个问题:如果 pfSense 二级路由使用静态 IP,会导致一级路由无法获取二级的 mDNS 记录。
解决方法
– 在一级路由(OpenWrt)上通过 DHCP 静态绑定给二级路由分配 IP
– 不要在二级路由上直接配置静态 IP
– 配置好后重启二级路由
OpenWrt 防火墙规则(命令行方式)
如果你习惯用命令行配置 OpenWrt 防火墙,可以使用以下命令:
uci -q delete firewall.mdns
uci set firewall.mdns="rule"
uci set firewall.mdns.name="Allow-mDNS"
uci set firewall.mdns.src="*"
uci set firewall.mdns.src_port="5353"
uci set firewall.mdns.dest_ip="224.0.0.251"
uci set firewall.mdns.dest_port="5353"
uci set firewall.mdns.proto="udp"
uci set firewall.mdns.target="ACCEPT"
uci commit firewall
/etc/init.d/firewall restart
后
配置完 mDNS 已经几个月了,现在回想起来,这是我在 Homelab 中做的最有价值的优化之一。
带来的改变
管理效率提升
– 不再需要维护一个 IP 地址清单
– 设备迁移或重启后不用担心 IP 变化
– 写配置文件时直接用域名,可读性大大提升
实际案例
最近我重装了一台服务器,以前的流程是:
1. 安装系统
2. 登录路由器配置静态 IP 绑定
3. 更新所有相关配置文件中的 IP
4. 重启依赖这台服务器的其他服务
现在的流程:
1. 安装系统
2. 设置 hostname
3. 完事
其他服务根本不需要改配置,因为它们用的是 server3.local 这样的域名,自动就能找到新的 IP。
一些建议
命名规范很重要
建议给设备起一个有意义的 hostname:
– nas.local 而不是 server1.local
– pve-node1.local 而不是 vm1.local
– k8s-master.local 而不是 ubuntu-001.local
好的命名可以让你半年后还记得这台设备是干什么的。
文档还是要有的
虽然不用记 IP 了,但建议维护一个简单的设备清单:
– 设备名称和 hostname
– 主要用途和运行的服务
– 重要配置文件位置
安全性考虑
mDNS 只适合内网使用,不要在公网暴露:
– 路由器 WAN 口不要启用 mDNS
– 如果有公网 IP,确保防火墙规则正确
– mDNS 流量不应该离开你的局域网
总结
mDNS 确实是 Homelab 的良药。它解决了 IP 地址管理的痛点,让网络配置更加灵活和优雅。虽然配置过程有一些坑(特别是 Netplan 的问题),但一旦配置好,体验提升是显著的。
如果你也在运营 Homelab,强烈建议试试 mDNS。配置时间不会超过一个下午,但带来的便利是长期的。
希望这篇文章能够帮助你顺利配置 mDNS,少走一些弯路。
参考链接
- https://forum.netgate.com/topic/134339/new-avahi-package/11
- https://forum.netgate.com/topic/142916/avahi-failed-to-create-client-object-daemon-not-running/2
- https://openwrt.org/docs/guide-developer/mdns
- https://www.linksysinfo.org/index.php?threads/avahi-tutorial-configuring-a-reflector-aka-mdns-repeater.75706/
- https://0e39bf7b.blog/posts/mdns-on-ubuntu-server/
- https://bugs.launchpad.net/netplan/+bug/1830507
Linux 桌面系统故障排查指南(五) - 网络
AI 创作声明:本系列文章由笔者借助 ChatGPT, Kimi K2, 豆包和 Cursor 等 AI 工具创作,有很大篇幅的内容完全由 AI 在我的指导下生成。如有错误,还请指正。
前言
网络连接是现代桌面的基础功能,涉及硬件驱动、固件加载、网络管理和 DNS 解析等多个环节。
本文将从网卡驱动开始,经过内核网络栈,到达应用层,了解 Linux 网络系统的完整架构,包括如何配置网络连接,如何设置防火墙规则,以及如何诊断各种网络问题。
网络连接与管理
网络连接是现代桌面的基础功能,涉及硬件驱动、固件加载、网络管理和 DNS 解析等多个环节。网络故障是最常见的桌面问题之一,理解其工作原理有助于快速定位和解决连接问题。
1.1 网络架构概览
现代 Linux 桌面大多使用 systemd-networkd 配合 iwd 进行网络管理,形成完整的网络解决方案。
虽然目前仍有部分系统默认使用 NetworkManager 管理网络,用 wpa_supplicant 管理 WiFi, 但这已经不够「现代」了(逃
网络协议栈:
- 硬件层:网卡驱动和固件
- 链路层:MAC 地址管理和链路检测
- 网络层:IP 地址配置和路由管理
- 传输层:TCP / UDP 连接管理
- 应用层:DNS 解析和服务发现
主要组件:
- systemd-networkd:网络接口管理,处理 DHCP 和静态配置
- iwd:无线网络管理,支持 WPA2 / WPA3
- systemd-resolved:DNS 解析和缓存
1.2 网络连接流程
有线网络:
- 内核加载网卡驱动
- 检测链路状态(网线连接)
- systemd-networkd 通过 DHCP 获取 IP 配置
- 配置路由和 DNS
无线网络:
- 加载无线网卡驱动和固件
- iwd 扫描可用网络
- 选择网络并进行认证(WPA2 / WPA3)
- 建立连接后通过 DHCP 获取 IP
网络管理命令:
# 查看接口状态
ip link show
ip addr show
# 无线网络管理(iwd)
iwctl station wlan0 scan
iwctl station wlan0 connect "SSID"
# 网络服务状态
systemctl status systemd-networkd iwd
# DNS 解析测试
resolvectl query example.com
resolvectl status
1.3 IPv4 / IPv6 双栈配置
现代网络正在往 IPv6 迁移的过程中,目前仍有许多站点都只支持 IPv6,因此 IPv4+IPv6 双栈成为一个过渡方案,systemd-networkd 提供完整的双栈支持。
双栈特点:
- IPv4:通过 DHCP 获取配置,32 位地址
- IPv6:通过 Router Advertisement 获取,128 位地址
- 并行工作:两个协议栈同时运行
- IPv6 优先:通常有 IPv6 的会优先走 IPv6 网络,没有才走 IPv4.
- Linux 中通过 glibc 的
getaddrinfo()来实现该逻辑,可通过/etc/gai.conf调整该函数的地址排序算法。因为 APP 通常直接使用第一条记录发起连接,所以/etc/gai.conf通常能直接决定系统中是 IPv6 优先还是 IPv4 优先。
- Linux 中通过 glibc 的
双栈验证:
# 查看 IPv4 配置
ip -4 addr show
ip -4 route
# 查看 IPv6 配置
ip -6 addr show
ping -6 2001:4860:4860::8888
# DNS 双栈测试
nslookup -type=A google.com
nslookup -type=AAAA google.com
1.4 网络故障排查
连接问题诊断流程:
- 硬件层面:
# 检查接口存在
ip link show
# 查看驱动加载
dmesg | grep -i firmware
lspci | grep -i network
- 链路层面:
# 有线:检查链路状态
ethtool eth0
# 无线:扫描网络
iw dev wlan0 scan | grep SSID
- 网络配置:
# DHCP 状态
journalctl -u systemd-networkd
# IP 配置检查
ip addr show dev eth0
# 路由表
ip route
- DNS 解析:
# DNS 配置
resolvectl status
cat /etc/resolv.conf
# 解析测试
dig @8.8.8.8 example.com
nslookup example.com
常见问题与解决:
- 无法获取 IP:检查 DHCP 服务、网线连接、无线密码
- DNS 解析失败:验证 DNS 服务器配置、检查 systemd-resolved 状态
- IPv6 无连接:确认路由器支持 IPv6、检查
IPv6AcceptRA配置 - 连接不稳定:查看信号强度、检查驱动兼容性
防火墙与网络安全
2.1 nftables 防火墙配置
nftables 是现代 Linux 的防火墙解决方案,它提供比 iptables 更简洁的语法和更好的性能。
基本概念:
- 表(Table):包含链和规则的容器
- 链(Chain):规则的有序列表
- 规则(Rule):匹配条件和动作
- 集合(Set):用于批量匹配的地址或端口列表
nftables 的四表五链、规则等概念跟 iptables 是完全一致的,这一部分可以参考我之前的文章iptables 及 docker 容器网络分析, 这里不再赘述。
NixOS 配置示例:
# configuration.nix
networking.nftables = {
enable = true;
ruleset = ''
# 定义表
table inet filter {
# 定义链
chain input {
type filter hook input priority 0; policy drop;
# 允许回环接口
if lo accept
# 允许已建立的连接
ct state established,related accept
# 允许 SSH
tcp dport 22 accept
# 允许 HTTP/HTTPS
tcp dport {80, 443} accept
# 允许 DNS
udp dport 53 accept
tcp dport 53 accept
# 允许 DHCP
udp dport 67 accept
udp dport 68 accept
# 允许 ICMP
icmp type {echo-request, echo-reply, destination-unreachable} accept
ip6 nexthdr icmpv6 icmpv6 type {echo-request, echo-reply, destination-unreachable} accept
}
chain forward {
type filter hook forward priority 0; policy drop;
}
chain output {
type filter hook output priority 0; policy accept;
}
}
'';
};
常用 nftables 命令:
# 查看当前规则
nft list ruleset
# 查看特定表
nft list table inet filter
# 临时添加规则
nft add rule inet filter input tcp dport 8080 accept
# 删除规则
nft delete rule inet filter input handle <handle>
# 清空表
nft flush table inet filter
2.2 网络地址转换(NAT)
端口转发配置:
networking.nftables.ruleset = ''
table inet nat {
chain prerouting {
type nat hook prerouting priority 0;
# 端口转发:将外部 8080 端口转发到内网 192.168.1.100:80
tcp dport 8080 dnat to 192.168.1.100:80
}
chain postrouting {
type nat hook postrouting priority 100;
# 源地址转换(SNAT)
oifname "eth0" masquerade
}
}
'';
虚拟网络技术
3.1 VPN 连接管理
WireGuard 配置:
# configuration.nix
networking.wireguard.interfaces = {
wg0 = {
ips = [ "10.0.0.2/24" ];
privateKeyFile = "/etc/wireguard/private.key";
peers = [
{
publicKey = "peer-public-key";
allowedIPs = [ "0.0.0.0/0" ];
endpoint = "vpn.example.com:51820";
persistentKeepalive = 25;
}
];
};
};
3.2 虚拟网络接口
TUN/TAP 接口:
# 创建 TUN 接口
ip tuntap add dev tun0 mode tun
ip addr add 10.0.0.1/24 dev tun0
ip link set tun0 up
# 创建 TAP 接口
ip tuntap add dev tap0 mode tap
ip addr add 192.168.100.1/24 dev tap0
ip link set tap0 up
桥接网络:
# 创建网桥
ip link add name br0 type bridge
ip link set dev br0 up
# 添加接口到网桥
ip link set dev eth1 master br0
ip link set dev tap0 master br0
# 配置网桥 IP
ip addr add 192.168.1.1/24 dev br0
3.3 容器网络
Docker 网络管理:
# 查看网络
docker network ls
# 创建自定义网络
docker network create --driver bridge --subnet=172.20.0.0/16 mynetwork
# 连接容器到网络
docker network connect mynetwork container_name
# 查看网络详情
docker network inspect mynetwork
Podman 网络配置:
# 创建网络
podman network create mynet
# 运行容器
podman run --network mynet -d nginx
# 查看网络
podman network ls
网络性能优化
4.1 网络参数调优
内核网络参数:
# configuration.nix
boot.kernel.sysctl = {
# TCP 缓冲区大小
"net.core.rmem_max" = 134217728;
"net.core.wmem_max" = 134217728;
"net.ipv4.tcp_rmem" = "4096 87380 134217728";
"net.ipv4.tcp_wmem" = "4096 65536 134217728";
# TCP 拥塞控制
"net.ipv4.tcp_congestion_control" = "bbr";
# 连接跟踪
"net.netfilter.nf_conntrack_max" = 1048576;
"net.netfilter.nf_conntrack_tcp_timeout_established" = 3600;
# 网络队列
"net.core.netdev_max_backlog" = 5000;
"net.core.netdev_budget" = 600;
};
网络参数调优:
TCP 缓冲区优化:
net.core.rmem_max = 134217728 设置 TCP 接收缓冲区的最大值为 128MB。更大的接收缓冲区可以处理突发的高流量,减少丢包,提高网络吞吐量,特别适合高带宽网络环境,适用于高带宽、高延迟网络,如光纤网络、VPN 连接。
net.core.wmem_max = 134217728 设置 TCP 发送缓冲区的最大值为 128MB。更大的发送缓冲区可以缓存更多待发送数据,提高发送效率,减少发送阻塞,提高网络传输效率,适用于大文件传输、流媒体上传、高并发网络应用。
net.ipv4.tcp_rmem = "4096 87380 134217728" 设置 TCP 接收缓冲区的初始值、默认值和最大值。参数说明:初始值 4KB,默认值 87KB,最大值 128MB。动态调整接收缓冲区大小,根据网络条件自动优化,在低延迟和高吞吐量之间自动平衡。
net.ipv4.tcp_wmem = "4096 65536 134217728" 设置 TCP 发送缓冲区的初始值、默认值和最大值。参数说明:初始值 4KB,默认值 64KB,最大值 128MB。动态调整发送缓冲区大小,适应不同的网络负载,在内存使用和网络性能之间找到最佳平衡点。
TCP 拥塞控制优化:
net.ipv4.tcp_congestion_control = "bbr" 使用 BBR(Bottleneck Bandwidth and RTT)拥塞控制算法。BBR 是 Google 开发的现代拥塞控制算法,基于带宽和延迟测量,在高带宽、高延迟网络环境下性能更好,减少延迟和丢包,适用于现代网络环境,特别是高带宽网络和长距离连接。
连接跟踪优化:
net.netfilter.nf_conntrack_max = 1048576 增加连接跟踪表大小到 100 万条记录。支持更多并发网络连接,避免连接跟踪表溢出,支持高并发网络应用,如 P2P 下载、多用户服务,适用于服务器环境、高并发网络应用。
net.netfilter.nf_conntrack_tcp_timeout_established = 3600 设置已建立连接的超时时间为 1
小时。延长连接跟踪时间,减少连接重建的频率,减少连接重建开销,提高长连接应用的性能,适用于长连接应用,如数据库连接、WebSocket 连接。
网络队列优化:
net.core.netdev_max_backlog = 5000 增加网络设备接收队列大小到 5000 个数据包。更大的接收队列可以处理突发流量,减少丢包,提高网络处理能力,减少因队列满而导致的丢包,适用于高流量网络环境,如服务器、网络设备。
net.core.netdev_budget = 600 增加每次网络处理的数据包数量到 600 个。提高网络处理效率,减少处理开销,提高网络吞吐量,减少 CPU 使用率,适用于高负载网络环境,需要优化网络处理性能。
优化效果评估:通过缓冲区优化,网络吞吐量可提升 20-50%;BBR 拥塞控制算法可显著减少网络延迟;连接跟踪优化支持更多并发连接;队列优化减少丢包,提高网络稳定性。
4.2 网络监控与分析
网络流量监控:
# 实时流量监控
iftop -i eth0
# 网络连接监控
netstat -tuln
ss -tuln
# 网络统计
cat /proc/net/dev
cat /proc/net/snmp
# 带宽测试
iperf3 -s # 服务器端
iperf3 -c server_ip # 客户端
网络延迟分析:
# ping 测试
ping -c 10 8.8.8.8
# 路由跟踪
traceroute 8.8.8.8
mtr 8.8.8.8
# 网络质量测试
qperf server_ip tcp_bw tcp_lat
4.3 网络故障诊断
连接问题排查:
# 检查网络接口状态
ip link show
ip addr show
# 检查路由表
ip route show
ip route get 8.8.8.8
# 检查 ARP 表
ip neigh show
# 检查网络统计
cat /proc/net/dev
cat /proc/net/snmp
DNS 问题排查:
# 测试 DNS 解析
dig @8.8.8.8 example.com
nslookup example.com
# 检查 DNS 配置
resolvectl status
cat /etc/resolv.conf
# 测试 DNS 性能
dig @8.8.8.8 example.com +stats
防火墙问题排查:
# 检查防火墙规则
nft list ruleset
iptables -L -v -n
# 测试端口连通性
telnet server_ip port
nc -zv server_ip port
# 检查连接跟踪
cat /proc/net/nf_conntrack
高级网络配置
5.1 多网卡绑定
网卡绑定配置:
# configuration.nix
networking.bonds = {
bond0 = {
interfaces = [ "eth0" "eth1" ];
driverOptions = {
mode = "802.3ad";
lacp_rate = "fast";
xmit_hash_policy = "layer3+4";
};
};
};
networking.interfaces.bond0.ipv4.addresses = [{
address = "192.168.1.100";
prefixLength = 24;
}];
5.2 VLAN 配置
VLAN 网络配置:
# configuration.nix
networking.vlans = {
vlan100 = { id = 100; interface = "eth0"; };
vlan200 = { id = 200; interface = "eth0"; };
};
networking.interfaces.vlan100.ipv4.addresses = [{
address = "192.168.100.1";
prefixLength = 24;
}];
networking.interfaces.vlan200.ipv4.addresses = [{
address = "192.168.200.1";
prefixLength = 24;
}];
5.3 网络命名空间
创建网络命名空间:
# 创建命名空间
ip netns add ns1
ip netns add ns2
# 创建 veth 对
ip link add veth1 type veth peer name veth2
# 将接口移到命名空间
ip link set veth1 netns ns1
ip link set veth2 netns ns2
# 配置命名空间内的网络
ip netns exec ns1 ip addr add 10.0.1.1/24 dev veth1
ip netns exec ns1 ip link set veth1 up
ip netns exec ns2 ip addr add 10.0.1.2/24 dev veth2
ip netns exec ns2 ip link set veth2 up
# 测试连通性
ip netns exec ns1 ping 10.0.1.2
总结
网络是计算机科学中最复杂的技术之一,数据在互联网中的流动造就了现代信息社会,现代 AI 的发展也与现代网络中产生的超大规模数据密不可分。
本文只是对 Linux 网络的一个简单介绍,下一篇文章我们会聊聊系统关机和故障排查,看看系统是如何优雅地关机的,以及遇到问题时该如何处理。
快速参考
常用网络管理命令
# 网络接口管理
ip link show # 查看网络接口
ip addr show # 查看 IP 地址
ip route show # 查看路由表
ip neigh show # 查看 ARP 表
# 网络连接管理
ss -tuln # 查看网络连接
netstat -tuln # 传统网络连接查看
lsof -i # 查看端口占用
# 网络测试
ping -c 4 8.8.8.8 # ping 测试
traceroute 8.8.8.8 # 路由跟踪
mtr 8.8.8.8 # 网络质量测试
常用防火墙命令
# nftables 管理
nft list ruleset # 查看所有规则
nft list table inet filter # 查看特定表
nft add rule inet filter input tcp dport 8080 accept # 添加规则
nft delete rule inet filter input handle <handle> # 删除规则
# iptables 管理(传统)
iptables -L -v -n # 查看规则
iptables -A INPUT -p tcp --dport 22 -j ACCEPT # 添加规则
iptables -D INPUT -p tcp --dport 22 -j ACCEPT # 删除规则
常用网络诊断命令
# DNS 解析测试
dig @8.8.8.8 example.com # DNS 查询
nslookup example.com # 传统 DNS 查询
resolvectl query example.com # systemd-resolved 查询
# 网络监控
iftop -i eth0 # 实时流量监控
tcpdump -i eth0 # 网络包捕获
wireshark # 图形化网络分析
# 带宽测试
iperf3 -s # 启动 iperf3 服务器
iperf3 -c server_ip # 客户端测试
重要配置文件位置
# 网络配置
/etc/systemd/network/ # systemd-networkd 配置
/etc/nftables.conf # nftables 配置
/etc/resolv.conf # DNS 配置
# 网络服务
/etc/systemd/system/ # systemd 服务配置
/etc/wireguard/ # WireGuard 配置
/etc/openvpn/ # OpenVPN 配置
# 网络状态
/proc/net/dev # 网络接口统计
/proc/net/snmp # 网络协议统计
/proc/net/nf_conntrack # 连接跟踪表
DNS 解析延迟毁了我的图床优化
去年夏天,我花了不少时间搭建博客图床,核心目标是分地区解析 DNS,让国内外访客都能快速加载图片。技术方案看起来完美无缺,直到最近群友反馈首次访问时图片加载很慢,我才发现问题所在。

955 毫秒的 DNS 解析时长! 这个数字让我大吃一惊。访客点开博客后,光是确定图片服务器位置就要等将近一秒,这完全抵消了 CDN 优化的效果。
为什么之前没发现?
主要是 DNS 缓存的"功劳"。它会为后续访问记住解析结果,让我的本地测试和复访测试看起来都很正常。直到用户反馈,结合最近准备秋招复习的 DNS 解析流程(递归查询、权威查询、根域名、顶级域名等),我才定位到问题:首次访问时的 DNS 解析延迟。
DNS 解析流程分析
让我们看看访客访问 static.031130.xyz 时,DNS 是如何工作的:
sequenceDiagram
participant User as 访客浏览器
participant Local as 本地 DNS
participant CF as Cloudflare<br/>(国外权威)
participant DP as DNSPod<br/>(国内权威)
participant CDN as CDN 节点
User->>Local: 请求 static.031130.xyz
Local->>CF: 查询 031130.xyz 权威
Note over Local,CF: 跨国查询,延迟高
CF->>Local: CNAME: cdn-cname.zhul.in
Local->>DP: 查询 zhul.in 权威
DP->>Local: CNAME: small-storage-cdn.b0.aicdn.com
Local->>DP: 查询 aicdn.com
DP->>Local: CNAME: nm.aicdn.com
Local->>DP: 查询最终 IP
DP->>Local: 返回 CDN IP
Local->>User: 返回解析结果
User->>CDN: 连接并下载图片
问题就在这里:前两步查询指向了国外的 Cloudflare 权威服务器。对于国内用户,虽然最终解析到的 CDN 节点是国内的,但跨国 DNS 查询就足以拖垮首次访问体验。那 955ms 的延迟,基本都耗在与国外 DNS 服务器的通信上了。
优化方案
针对这个问题,我采取了三个措施:
1. DNS 预取
在博客 HTML 的 <head> 中添加:
<link rel="dns-prefetch" href="//static.031130.xyz">
这样浏览器在渲染页面时就会提前解析图床域名,等真正需要加载图片时,DNS 结果可能已经准备好了。
2. 延长 TTL
将 static.031130.xyz 的 CNAME 记录 TTL 值调大(从几分钟延长到几小时甚至一天)。这样本地 DNS 服务器会缓存更久,后续用户可以直接使用缓存结果,省掉权威查询。
3. 迁移权威 DNS(核心)
将 031130.xyz 域名的权威 DNS 服务器从 Cloudflare 迁移到国内的 DNSPod:
graph TB
subgraph "优化前"
A1[访客] --> B1[本地 DNS]
B1 --> C1[Cloudflare 权威<br/>国外]
C1 --> D1[DNSPod 权威<br/>国内]
D1 --> E1[CDN 节点]
style C1 fill:#ffcccc
end
graph TB
subgraph "优化后"
A2[访客] --> B2[本地 DNS]
B2 --> D2[DNSPod 权威<br/>国内]
D2 --> E2[CDN 节点]
style D2 fill:#ccffcc
end
迁移后的好处:
- 递归 DNS 查询
031130.xyz时,直接找到国内的 DNSPod,响应快 - DNSPod 直接返回
static.031130.xyz->small-storage-cdn.b0.aicdn.com,无需中间跳转 - 整个 DNS 解析链路在国内完成,首次访问延迟大幅降低
优化效果
虽然 DNS 缓存给测试带来了困难,但迁移权威 DNS + 调整 TTL + 添加预取后,首次访问的 DNS 解析时间降到了可接受的范围。
经验教训
-
DNS 位置很重要:涉及多地优化时,权威 DNS 的地理位置对首次访问延迟影响很大。优先使用国内权威服务器。
-
首次访问是关键:虽然缓存能帮助后续访问,但首次访问体验直接影响用户印象。善用
dns-prefetch和合理的 TTL 设置。 -
监控和反馈重要:本地测试环境往往有缓存加持,真实的首次访问体验需要通过监控和用户反馈来发现。
重要提醒:警惕 CNAME 拉平
如果你需要分地区解析来让访客连接到最近的 CDN 节点,务必避开 CNAME Flattening(CNAME 拉平)。
什么是 CNAME 拉平?
权威 DNS 服务器(如 Cloudflare)看到 CNAME 记录后,会主动查询目标域名的最终 IP 地址,然后直接返回 IP 而不是 CNAME。
为什么会出问题?
分地区解析(GeoDNS)在权威 DNS 服务器层面实现。当权威服务器执行 CNAME 拉平时,它会在自己的位置查询目标域名的 IP。如果权威 DNS 在美国,它获取的 IP 就是美国最优节点,然后把这个 IP 返回给所有地区的查询者,包括中国用户。这样,你为中国用户配置的国内 CDN IP 策略就完全失效了。
graph LR
subgraph "启用 CNAME 拉平的问题"
A[中国用户] --> B[Cloudflare 权威<br/>美国节点]
B --> C[查询目标 CNAME]
C --> D[返回美国 CDN IP]
D --> A
style D fill:#ffcccc
end
正确做法
老实使用 CNAME 指向另一个支持 GeoDNS 的域名(如 static.031130.xyz -> cdn-cname.zhul.in,后者在 DNSPod 上做分地区解析),才能保证分流策略正确执行。
如果需要分地区解析功能,不要在相关域名上启用 CNAME Flattening(或 ALIAS、ANAME 等类似功能)。
把博客站点交给了 Cloudflare 托管
因为博客域名是在阿里云购买的,先前一直顺理成章地用着阿里云的 DNS 解析。阿里云的 DNS 解析在各方面的体验都很不错,例如修改配置后就能很快更新、配置平台访问速度快、站点不会被国内的运营商污染等等,这些优点反过来可是说尽是 Cloudflare 的缺点。
但由于 Cloudflare 为网站提供的各种免费服务十分诱人,加之我想利用 Cloudflare 的 CDN 搭建博客图床,终究是把站点交给了 Cloudflare 管理。本文记录了从阿里云迁移站点的过程和一些必要的 Nginx 配置。
Cloudflare 注册站点
打开 Cloudflare 官网,注册帐号后选择添加站点,输入域名后点击继续。
按需选择计划,对于普通的小站点来说,Free 计划足矣。点击继续后,Cloudflare 会检测站点目前已有的部分 DNS 记录,其余未检测出的记录日后再手动添加,最关键的是检查域名指向服务器 IP 地址的 A 记录是否正确。
在「代理状态」一列可以选择该 DNS 记录是否使用 Cloudflare 的 CDN,激活后图标显示一朵黄色的云。Cloudflare 的 CDN 在国内速度很慢,一直被称为减速 CDN,所以我都选择「仅 DNS」。此前我也担心 Cloudflare 的 DNS 解析会不会也像其 CDN 一样龟速,幸好解析速度并不慢,我的担心是多虑了。
提交 DNS 记录后,Cloudflare 会提示删除阿里云的 DNS 服务器,以 Cloudflare 的 DNS 服务器代替之,接着就转到阿里云的控制中心操作。
更换 DNS 服务器
登录阿里云,进入控制台。在云解析 DNS - 域名解析下找到迁移的域名,在解析设置中保存了站点的 DNS 记录。将记录备份,后续要将所有记录导入 Cloudflare。站点交由 Cloudflare 解析后,阿里云中的解析设置也会失效,所以也在解析设置中将所有解析都停用。
在阿里云控制台中来到域名控制台 - 域名列表,选择域名的管理 - DNS 管理 - DNS 修改 - 修改 DNS 服务器,将 Cloudflare 提供的两个 DNS 服务器地址填入其中。
修改 DNS 服务器一般需要 24-48 h 生效,生效后 Cloudflare 会发送邮件通知。如果迟迟没有收到邮件,也可以到 Cloudflare 手动验证网站。验证成功后 Cloudflare 会指引是否开启 Brotli 压缩等功能,按需选择即可。至此,站点已经交由 Cloudflare 托管。如果站点是由 Nginx 搭建的,那么就还需要考虑 Nginx 的 SSL 设置是否与 Cloudflare 兼容。
Nginx 中的 SSL 相关配置
在 Cloudflare 的 SSL/TLS 设置界面可以看到,用户访问由 Cloudflare 托管的站点的过程中有 3 个实体,根据实体间通信安全等级的不同可以分为 4 种模式:
- 关闭:浏览器-Cloudflare 间和 Cloudflare-服务器间都使用 HTTP;
- 灵活:浏览器-Cloudflare 间使用 HTTPS,Cloudflare-服务器间使用 HTTP;
- 完全:浏览器-Cloudflare 间和 Cloudflare-服务器间都使用 HTTPS,需要 SSL 证书;
- 完全(严格):浏览器-Cloudflare 间和 Cloudflare-服务器间都使用 HTTPS,需要非自签名 SSL 证书。
现在的站点一般都使用了 HTTPS,还在使用 HTTP 的站长快去申请个 SSL 证书吧,同时通过 Nginx 将访问 80 端口的 HTTP 流量强制重定向到 HTTPS 入口。若使用这样的 Nginx 配置又开启的「灵活」模式,用户发起访问请求后,Cloudflare 使用 HTTP 交由 Nginx,Nginx 告知用户重定向为 HTTPS,但Cloudflare 仍使用 HTTP 与 Nginx 通信,该过程无限循环,出现 301 重定向次数过多。
为了保证站点的安全性和避免以上问题,推荐配置好站点的 HTTPS 后,在 Cloudflare 的 SSL/TLS 中
最后附上我的 Nginx 配置供参考:
server {
listen 443 ssl http2;
server_name leonis.cc;
root /home/Leo/web/blog;
# SSL 配置
ssl_certificate /etc/nginx/cert/leonis.cc.cer;
ssl_certificate_key /etc/nginx/cert/leonis.cc.key;
ssl_session_timeout 5m;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
location / {
index index.html;
}
}
server {
listen 80;
server_name leonis.cc
# 重定向至 HTTPS,开启 Cloudflare 完全模式后不会访问 80 端口,也不会用上此处的重定向
rewrite ^/(.*)$ https://leonis.cc:443/$1 permanent;
}
后记
Cloudflare 总体来说还是很好用的,提供了很多有意思的功能,很便利地就能体验,免去了自己动手配置的烦恼。Cloudflare 的不足仅在于在国内有时访问不畅,添加 DNS 记录后也要等比较长的时间才会更新到国内网络上,若能接受这两点,Cloudflare 的可玩性还是比其他平台更高的。
修改DNS去广告
以前也尝试过在DNS上做手脚拦截广告,但因为误杀太多以及漏网之鱼太多,觉得还不如不折腾呢。
最近偶然发现了一组列表,又尝试了一番,发现效果挺不错的,手机开屏广告绝大多数不见,浏览网页时Google Ads也基本没有了,真是让我喜出望外。所以以前效果不好的原因只是因为使用的黑名单列表不好而已。
我是在路由器上的CoreDNS使用ads插件设置拦截的,所有接入的设备都可以享受到这个效果。用到3组黑名单源,分别是Anti-ad,AdGuard和EasyList,然后自己写了点代码将列表规整了一下,转成hosts文件的格式后合并为一个文件,ads插件可以直接通过http协议加载hosts格式的文件。CoreDNS的配置文件增加ads就行了:
1
2
3
4
5
#blocklist domains.txt
ads {
blacklist https://cdn.jsdelivr.net/gh/missdeer/blocklist@master/convert/alldomains.txt
nxdomain
}
Dnsmasq为不同的设备单独设置不同的DNS服务器
Dnsmasq是一个轻量级的DNS服务器和DHCP服务器软件。它通常用于小型局域网内的网络设备(如路由器、交换机等)。D…