跳到主要内容

云雀通与阿里云:解决 Linux 与 Docker DNS 无法联网

部分阿里云 ECS 在安装 Tailscale,或使用内核 TUN 模式接入云雀通后,可能出现“服务器突然无法联网”的现象。常见表现并不是公网连接完全中断,而是:

  • 公网 IP 仍然可以访问,域名却无法解析。
  • ping 一个 IP 可能成功,但 aptyumcurl 或业务程序访问域名失败。
  • /etc/resolv.conf 中仍有 DNS 地址,但请求没有走阿里云原来的网关。

云雀通已经分别为 Linux 一键安装版Docker 内核 TUN 版加入底层 DNS 路由保护,降低这个问题对阿里云服务器的影响。

先确认是否真的是 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.136100.100.2.138 的请求可能被导向 tailscale0 等虚拟接口,而不是阿里云原来的默认网关。

最终就会出现:服务器看起来还有网络,但域名解析、软件更新和依赖域名的业务请求失败。

云雀通做了哪些优化

版本自动保护方式适用范围
云雀通 Linux 一键安装版安装 preserve-underlay-routes.sh,从系统 DNS 配置中查找 CGNAT DNS,并在 maintable 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

脚本会:

  1. /etc/resolv.conf/run/systemd/resolve/resolv.confresolvectlsystemd-resolve 获取 DNS 地址。
  2. 只自动选择落在 100.64.0.0/10 内的 IPv4 DNS。
  3. 获取宿主机原来的默认网关与网卡。
  4. 在 Linux maintable 52 中为每个 DNS 写入更具体的 /32 路由。
  5. 在 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.136100.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,或者 maintable 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=falsenetwork_mode: host,并且容器拥有 NET_ADMIN
  • 不要只永久改写 /etc/resolv.conf:systemd-resolved 或 DHCP 可能重新生成该文件,而且仅更换 DNS 并没有解决底层路由冲突。

相关文档