Ubuntu 24.04 的 sources.list 是个空壳:一次 275 秒初始化超时的复盘

Ubuntu 24.04 的 /etc/apt/sources.list 是个空壳。

这句话解释了我们上周一次初始化卡 275 秒的原因。容器开了、IP 有了、22 端口通了,sudo 却没装上,前端一直转圈,最后报「初始化超时」。查到最后,不是 PVE 的问题,不是网络的问题,是软件源的问题——我们换源的那行脚本,改的是一个已经不生效的文件。

现象:机器是活的,工具是缺的

那次交付的是一台 Ubuntu 24.04(noble)。按流程走到「初始化系统」阶段,等了四分半钟,状态卡住。到 PVE 里看:

  • status=running ✅
  • /interfaces 返回 10.x 地址 ✅
  • TCP:22 能连 ✅
  • 但 curl、git、sudo 一个都没有 ❌

按当时的判定逻辑,工具不全就判失败。但机器明明是好的,用户也能 SSH 进去——只是什么也干不了。这比彻底失败更尴尬:订单停在「初始化中」,用户以为我们在骗他。

第一层根因:换源改错了文件

Ubuntu 24.04 起默认使用 deb822 格式,源定义搬到了 /etc/apt/sources.list.d/ubuntu.sources:


Types: deb
URIs: http://archive.ubuntu.com/ubuntu
Suites: noble noble-updates noble-security
Components: main restricted universe multiverse
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg

而 /etc/apt/sources.list 里通常只剩注释。我们旧脚本是这么换源的:


cat > /etc/apt/sources.list <<EOF
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ $CN main restricted universe multiverse
...
EOF

看起来很合理,实际上等于往一个没人读的文件里写配置。ubuntu.sources 原封不动,archive.ubuntu.com 依然是活跃源。国内容器访问海外 archive,apt-get update 在那边逐个重试超时,几百个包索引一个也下不来——这就是 275 秒的来源。

教训一句话:Debian/Ubuntu 现在有两套源文件格式并存,只改一套等于没改。

第二层根因:拿日志字样判成败

旧逻辑是抓初始化脚本的输出,看到「成功」字样就算过。问题是 apt 的失败大多是静默的:apt-get install -y -qq 返回非零,脚本里一句 || true 吞掉,日志看起来干干净净。于是「跑完了」和「装上了」被混为一谈。

改成实测探针,一个工具一个检查函数:


tool_ready() {
  case "$1" in
    openssh-server) [ -x /usr/sbin/sshd ] || command -v sshd >/dev/null 2>&1 ;;
    ca-certificates) [ -f /etc/ssl/certs/ca-certificates.crt ] ;;
    sudo) command -v sudo >/dev/null 2>&1 ;;
    *) command -v "$1" >/dev/null 2>&1 ;;
  esac
}

for t in $CORE_TOOLS; do tool_ready "$t" || MISSING="$MISSING $t"; done

判定标准变成:lihua 能登录 + 核心工具齐全。不看日志写了什么,只看文件在不在、命令跑不跑得起来。

三层修法

层面 旧做法 新做法 效果
源文件 只写 sources.list 同时识别 sources.list 与 sources.list.d/*.sources Ubuntu 24.04 / Debian 12 都能换到清华镜像
发行版识别 不区分 读 /etc/os-release 的 ID + VERSION_CODENAME Debian 用 bookworm,Ubuntu 用 noble,不会串
非 Debian 系 继续跑,白等 检测不到 apt-get 立即 die 5 秒失败,不耗 300 秒
成败判定 抓日志字样 实测探针 + 二次补装 「跑完了」≠「装上了」
备份 无 sources.list.lihua-bak + *.sources.lihua-bak 出问题能回滚

换源主体现在长这样,四条分支全铺开:


case "$OS_ID" in
  debian)
    if ls /etc/apt/sources.list.d/*.sources >/dev/null 2>&1; then
      for f in /etc/apt/sources.list.d/*.sources; do
        sed -i -E "s#URIs:.*#URIs: https://mirrors.tuna.tsinghua.edu.cn/debian#g" "$f"
      done
    else
      printf 'deb https://mirrors.tuna.tsinghua.edu.cn/debian/ %s main ...\n' "$CN" \
        > /etc/apt/sources.list
    fi
    ;;
  ubuntu)  # 同上,套件名换成 noble/jammy
    ;;
esac

另外补了一个「二次补装」:第一次 apt-get install 之后重新探针,缺什么再单独装一遍,还是缺就直接红字退出并指明缺哪个。宁可 60 秒报错,也不要 275 秒后含糊其词。

复盘:慢失败比快失败贵

这次最贵的不是代码,是误判。系统报「初始化超时」,第一反应是 PVE 慢、网络差、模板有问题,来回查了两小时。真正的线索只有一条:机器活着但工具缺失——这排除了资源层,直指软件源。

所以我们后来加了两条硬规则:

1. 快速失败优先。不知道发行版就说不知道,然后立刻停。不许「试试看能不能跑」。

2. 判定靠观测,不靠自述。脚本说自己成功了没有意义,端口通不通、命令在不在才有意义。

同一套逻辑后来也用在容器自动上报上:不信上报脚本的返回码,服务端自己核对字段完整性。


狸花云 v7.20.5,2026-10-30

💬 留下你的爪印