云雀通与阿里云:解决 Linux 与 Docker DNS 无法联网
部分阿里云 ECS 在安装 Tailscale,或使用内核 TUN 模式接入云雀通后,可能出现“服务器突然无法联网”的现象。常见表现并不是公网连接完全中断,而是:
- 公网 IP 仍然可以访问,域名却无法解析。
ping一个 IP 可能成功,但apt、yum、curl或业务程序访问域名失败。/etc/resolv.conf中仍有 DNS 地址,但请求没有走阿里云原来的网关。
云雀通已经分别为 Linux 一键安装版和 Docker 内核 TUN 版加入底层 DNS 路由保护,降低这个问题对阿里云服务器的影响。
本页处理的是“IP 基本可达,但域名解析失败”的问题。如果 IP 和域名都无法访问,请先检查阿里云安全组、VPC 路由、NAT 网关、宿主机默认路由和防火墙。
为什么阿里云上会出现这个问题
阿里云 ECS 常见的内网 DNS 地址包括:
100.100.2.136
100.100.2.138
这两个地址位于 100.64.0.0/10 CGNAT 网段。Tailscale 与云雀通的私有 IPv4 地址也使用这个网段。
在 Linux 内核 TUN 模式下,客户端会通过 policy routing 和 Linux table 52 写入云雀通网络路由。如果没有更具体的底层 DNS 路由,发往 100.100.2.136 或 100.100.2.138 的请求可能被导向 tailscale0 等虚拟接口,而不是阿里云原来的默认网关。
最终就会出现:服务器看起来还有网络,但域名解析、软件更新和依赖域名的业务请求失败。
云雀通做了哪些优化
| 版本 | 自动保护方式 | 适用范围 |
|---|---|---|
| 云雀通 Linux 一键安装版 | 安装 preserve-underlay-routes.sh,从系统 DNS 配置中查找 CGNAT DNS,并在 main 和 table 52 写入 /32 路由 | systemd、OpenRC 和 SysV init Linux 主机 |
云雀通 Docker latest | 内核 TUN 模式默认启用底层路由保护,自动读取 DNS 配置、识别阿里云 ECS,并周期性重新检查 | TS_USERSPACE=false 且使用宿主机网络的容器 |
两种实现都会让底层 DNS 地址通过服务器原来的默认网关访问,而不是进入云雀通虚拟接口。
Linux 版保护机制
当前 Linux 安装脚本会安装:
/usr/lib/larktun/preserve-underlay-routes.sh
脚本会:
- 从
/etc/resolv.conf、/run/systemd/resolve/resolv.conf、resolvectl或systemd-resolve获取 DNS 地址。 - 只自动选择落在
100.64.0.0/10内的 IPv4 DNS。 - 获取宿主机原来的默认网关与网卡。
- 在 Linux
main和table 52中为每个 DNS 写入更具体的/32路由。 - 在 systemd、OpenRC 或 SysV 服务启动时再次执行保护脚本。
Linux 安装流程还默认建议使用 --accept-routes=false --accept-dns=false,避免服务器在首次接入时直接采用其他节点的路由或 DNS 设置。
Docker 版保护机制
Docker 镜像在 TS_USERSPACE=false 时默认启用:
TS_PRESERVE_UNDERLAY_ROUTES: "true"
它会读取容器可见的 resolver 配置,并通过 DMI 信息识别阿里云 ECS。识别到阿里云后,即使当前 resolver 文件只显示本地 stub 地址,也会把常见的 100.100.2.136 和 100.100.2.138 纳入保护。
保护程序会在容器启动后的多个时间点快速检查,之后每 5 分钟再次检查,避免启动顺序或 DNS 配置变化导致路由丢失。需要手动指定底层 DNS 时,可以设置:
TS_PRESERVE_UNDERLAY_DNS: "100.100.2.136,100.100.2.138"
用户态模式 TS_USERSPACE=true 不会向宿主机内核写入云雀通路由,因此不需要这项内核 TUN 路由保护。
第 1 步:确认故障特征
在阿里云服务器上执行:
ping -c 3 223.5.5.5
getent hosts www.baidu.com
cat /etc/resolv.conf
cat /run/systemd/resolve/resolv.conf 2>/dev/null || true
resolvectl dns 2>/dev/null || systemd-resolve --status 2>/dev/null || true
如果 IP 测试基本正常,但 getent hosts 没有返回地址,再继续检查路由:
ip -4 route show default
ip -4 route get 100.100.2.136
ip -4 route show table main | grep -E '100\.100\.2\.(136|138)' || true
ip -4 route show table 52 | grep -E '100\.100\.2\.(136|138)' || true
如果 DNS 路由指向 tailscale0,或者 main、table 52 中缺少 DNS 的 /32 路由,就符合本页描述的问题。
第 2 步:修复云雀通 Linux 版
先检查保护脚本是否已经安装:
sudo test -x /usr/lib/larktun/preserve-underlay-routes.sh \
&& echo "underlay route helper is installed"
如果脚本存在,执行:
sudo /usr/lib/larktun/preserve-underlay-routes.sh
sudo larktun set --accept-routes=false --accept-dns=false
如果脚本不存在,说明安装版本较旧。可以先暂时断开云雀通,让原有网络恢复,再按在 Linux 系统上安装并登入云雀通重新执行当前安装脚本:
sudo larktun down
curl -fsSL https://download2.larktun.com/install.sh | sh
然后重新接入:
sudo larktun up --accept-routes=false --accept-dns=false
如果这是第一次登入,还需要使用云雀通控制台创建的 Auth Key 和 https://hs.larktun.com;完整命令请参考上面的 Linux 安装教程。
第 3 步:修复云雀通 Docker 版
这个问题主要影响内核 TUN 模式。先确保使用当前推荐镜像,并在 Docker Compose 中保留以下配置:
services:
larktun:
image: registry.larktun.com/larktun/larktun:latest
network_mode: host
cap_add:
- NET_ADMIN
- NET_RAW
devices:
- /dev/net/tun:/dev/net/tun
environment:
TS_USERSPACE: "false"
TS_PRESERVE_UNDERLAY_ROUTES: "true"
TS_PRESERVE_UNDERLAY_DNS: "100.100.2.136,100.100.2.138"
拉取最新镜像并重新创建容器:
docker compose pull
docker compose up -d --force-recreate
查看保护日志:
docker compose logs --tail=100 larktun \
| grep -E 'Preserved underlay routes|failed to preserve underlay' || true
成功识别并保护 DNS 时,会看到类似 Preserved underlay routes for 2 CGNAT DNS server(s) 的日志。完整部署示例请参考使用 Docker 登入云雀通。
第 4 步:验证修复结果
重新检查两张路由表:
ip -4 route show table main | grep -E '100\.100\.2\.(136|138)'
ip -4 route show table 52 | grep -E '100\.100\.2\.(136|138)'
预期两个 DNS 都通过阿里云原来的默认网关和物理网卡,而不是 tailscale0:
100.100.2.136 via <宿主机默认网关> dev <宿主机网卡>
100.100.2.138 via <宿主机默认网关> dev <宿主机网卡>
最后验证域名解析和 HTTPS:
getent hosts www.baidu.com
curl -I https://larktun.com
域名能够返回 IP,且 HTTPS 请求能够收到响应,说明 DNS 访问已经恢复。
仍然无法联网怎么办
- 实际 DNS 不是
100.100.2.136/138:以resolvectl dns或真实 resolver 文件为准。Docker 可以通过TS_PRESERVE_UNDERLAY_DNS填写实际地址。 - IP 和域名都失败:这通常不是 DNS 路由冲突,请检查安全组、VPC、NAT、默认路由和宿主机防火墙。
- Linux 路由仍缺失:确认系统安装了
iproute2,然后重新执行/usr/lib/larktun/preserve-underlay-routes.sh。 - Docker 没有保护日志:确认使用
latest镜像、TS_USERSPACE=false、network_mode: host,并且容器拥有NET_ADMIN。 - 不要只永久改写
/etc/resolv.conf:systemd-resolved 或 DHCP 可能重新生成该文件,而且仅更换 DNS 并没有解决底层路由冲突。