☰
Ubuntu 20.04 换阿里源:apt 加速与报错排查
2026/10/1 18:00:23 网站建设 项目流程

装完 Ubuntu 20.04 之后如果不做换源,第一条apt install命令大概率会让你盯着终端发呆——进度条卡在 0%,几十秒后蹦出一个Failed to fetch。我见过不少人到这一步就开始怀疑网络,重启网卡、改 DNS、重装系统,最后发现问题压根不在网络,而在/etc/apt/sources.list里那几个指向海外服务器的域名。Ubuntu 20.04 换源到阿里源,本质上是把软件包的下载地址从官方源换成国内镜像站,让 apt 不用绕远路去拉包。

这篇内容写给三类人:刚装完系统、对 Linux 还半生不熟的新手;在 VMware 虚拟机里搭开发环境、准备长期折腾的同学;以及要给团队做装机模板、想把换源这件事一次性做干净的运维。我会把"为什么要换、换成什么、怎么换、换完出问题怎么查"整条链路讲透,包括几个我自己踩过的、网上教程基本不提的坑。

1. 换源到底改了什么,值不值得折腾

1.1 Ubuntu 20.04 默认源在国内的真实表现

先看一个默认的 20.04 系统里sources.list长什么样。官方镜像给的内容大致是deb http://archive.ubuntu.com/ubuntu/ focal main restricted这样的形式,security那一组则指向security.ubuntu.com。这两个域名都在海外,apt update的时候你要去拉InRelease、Packages.gz这些索引文件,apt install的时候还要去拉几百 KB 到几百 MB 的 deb 包。

索引文件的体积其实不大,focal主仓库的InRelease大概两百多 KB,四个仓库加起来也就几兆。真正让人难受的是小文件多、往返次数多,每次都要重新建连,TCP 握手和 TLS 握手的开销被放大了几十倍。我实测过同一个虚拟机,默认源apt update要跑三到五分钟,中间还会因为某个索引超时中断一次,重跑才行;换成阿里源之后,通常十几秒内结束。

更隐蔽的问题是 deb 包。装个build-essential要拉十几个依赖,装ubuntu-desktop相关的组件动辄上百兆。默认源下这些包的速度经常掉到几十 KB/s,装一次环境能刷掉半小时。换源之后带宽基本能跑满你本地的出口。

1.2 阿里源、清华源、中科大源怎么选

国内主流镜像站就那么几个,功能上没有本质区别,都是官方源的完整同步,签名也是 Ubuntu 原版签名。真正的差别在同步频率、覆盖范围和你的网络路径。

镜像站地址特点适合场景
阿里云mirrors.aliyun.com节点多、覆盖面广,除 Ubuntu 外还镜像 pypi、anaconda、npm 等生态一站式换源,尤其适合同时要用 pip、conda 的人
清华 TUNAmirrors.tuna.tsinghua.edu.cn教育网内速度极好,同步脚本开源、状态页透明校园网、教育网环境
中科大 USTCmirrors.ustc.edu.cn老牌镜像,华东地区表现不错教育网、安徽周边
网易mirrors.163.com同步较早,部分仓库覆盖不如前几家一般备选

我自己的习惯是:如果只换 apt,选哪家都行,挑一个你ping延迟最低的;如果打算把 pip、conda、npm 一起换掉,直接统一用阿里源,省得记四套域名。混合用也不是不行,但同一份sources.list里千万别混两家镜像站——不同站点的同步时间差会导致同一个包的索引和 deb 哈希对不上,报出Hash Sum mismatch,排查起来很烦。

1.3 动手前必须确认的三个信息

很多人换源翻车,不是命令写错了,是信息没核对。有三件事必须在敲命令之前确认清楚。

第一是系统版本代号。Ubuntu 20.04 的代号是focal(Focal Fossa),在源文件里它出现在每一行的中间位置,紧跟在 URL 后面。如果这台机器其实是 22.04(jammy)或者 18.04(bionic),你按 20.04 的模板抄一遍,apt update会直接给你一片 404。查代号用:

lsb_release -cs # 输出应该是:focal

第二是CPU 架构。x86_64 走mirrors.aliyun.com/ubuntu/,ARM64(树莓派、部分云主机、Apple Silicon 上的虚拟机)走的是mirrors.aliyun.com/ubuntu-ports/,路径不一样。查架构用:

dpkg --print-architecture # 常见输出:amd64 / arm64 / armhf

第三是源文件的位置和格式。Ubuntu 20.04 用的是传统的单行格式,全部写在/etc/apt/sources.list这一个文件里。到了 24.04 之后,Ubuntu 改用了 deb822 格式,主文件变成/etc/apt/sources.list.d/ubuntu.sources,是分段的 key-value 写法。网上很多教程把这两种混着讲,导致新手在 20.04 上照着 24.04 的模板改,改完发现根本不生效。

提示:/etc/apt/sources.list.d/目录下的第三方源(比如 Docker、NodeSource、各类 PPA)不在本次换源范围内,它们各自有独立的下载地址,不需要动。

2. 备份和读懂原文件,这五分钟别省

2.1 focal 四个仓库组件分别装什么

focal后面的main restricted universe multiverse是仓库组件,理解它们能帮你判断"某个包找不到"是不是因为源没开全。

  • main:官方支持的自由软件,Ubuntu 团队直接维护,安全更新有保证。
  • restricted:官方支持的专有驱动,显卡驱动、部分无线网卡固件在这里。
  • universe:社区维护的自由软件,包量最大,绝大多数开发工具、Python 生态工具都在这一层。
  • multiverse:有版权或法律限制的软件,编解码器、部分字体在这。

默认的sources.list里,main和restricted是开着的,universe和multiverse在focal和focal-updates行里通常也开着,但focal-backports那一行默认是被注释掉的。这个细节很关键:后面用 sed 批量替换 URL 的时候,注释行里的地址也会被改,但它依然是注释状态,backports 不会因此被打开。

另外,deb-src开头的行默认全部注释。如果你后面要编译内核模块、自己打包,需要把deb-src打开,这个和 VMware 内核模块编译失败的问题是有因果关系的,第 7 节会细说。

2.2 备份的两种做法与差别

换源之前备份,是唯一一个"你不做也可能没事,但出事后会救命"的步骤。我推荐用cp -a而不是cp:

sudo cp -a /etc/apt/sources.list /etc/apt/sources.list.bak sudo ls -l /etc/apt/sources.list*

-a会保留权限、属主和时间戳。sources.list是root:root 644,用普通cp复制出来的文件权限受 umask 影响,虽然大概率也是 644,但没必要赌。备份文件名我习惯带日期,比如sources.list.20240115.bak,这样一台机器上改过好几次也能区分。

还有一种更保险的做法,是把原始文件复制到/root/下:

sudo cp -a /etc/apt/sources.list /root/sources.list.orig

好处是即使有人把/etc/apt/整个目录搞乱了,你手上还有一份干净的原件。缺点是分散在两处,容易忘。选一种坚持用就行。

2.3 检查 sources.list.d 里有没有第三方源

这一步很多人跳过,但它决定了你后面排查问题时的思路。先看一眼:

ls -l /etc/apt/sources.list.d/ grep -rhv '^\s*#' /etc/apt/sources.list.d/ 2>/dev/null

如果里面已经有 Docker、NodeSource、MongoDB 之类的第三方源,那换完主源之后,如果apt update只报某一个特定域名的错,基本可以锁定是那些第三方源的问题,跟阿里源无关。我遇到过一次,apt update一片红字,最后发现是某个早已下线的 PPA 还挂在sources.list.d里,把整个更新流程拖慢了十几秒。

3. 两种替换方式,按机器用途选

3.1 sed 一行替换:快,但有前提

如果这台机器就是标准的 20.04 官方镜像,sources.list里清一色是archive.ubuntu.com和security.ubuntu.com,那 sed 是最省事的:

sudo cp -a /etc/apt/sources.list /etc/apt/sources.list.bak sudo sed -i \ -e 's@//.*archive\.ubuntu\.com@//mirrors.aliyun.com@g' \ -e 's@//security\.ubuntu\.com@//mirrors.aliyun.com@g' \ /etc/apt/sources.list grep -v '^\s*#' /etc/apt/sources.list | grep -v '^\s*$'

几条要注意的地方。sed -i的替换分隔符我用了@而不是默认的/,因为地址里全是斜杠,用/得写一堆转义,容易出错。.*archive里的.*是为了兼容cn.archive.ubuntu.com、us.archive.ubuntu.com这类带国家前缀的写法——不少云厂商的系统镜像默认就是这个格式。替换完一定要grep出来看一眼,确认没有残留的旧域名:

grep -n 'ubuntu.com' /etc/apt/sources.list

如果这条命令还有输出(注释行里的除外),说明有漏网的。

sed 方案的局限也很明显:它不改仓库组件,universe没开就是没开;它不打开deb-src;它也不会帮你补上focal-backports。所以我只在临时机器、一次性环境里用 sed,长期用的机器一律走下一节的方式。

3.2 整文件重写:可控,适合长期维护

正式环境的做法是直接把sources.list覆盖成一份写好的完整内容。用tee配合 heredoc 比vim更不容易出错,也更容易写进脚本:

sudo cp -a /etc/apt/sources.list /etc/apt/sources.list.bak sudo tee /etc/apt/sources.list > /dev/null <<'EOF' # Ubuntu 20.04 LTS (focal) - Aliyun Mirror deb https://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse deb https://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse deb https://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse deb https://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse # Uncomment the following if you need source packages # deb-src https://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse # deb-src https://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse # deb-src https://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse EOF

几个细节。heredoc 的结束标记用了<<'EOF'(带单引号),这样里面的$不会被 shell 展开,写进什么就是什么。tee后面加> /dev/null是为了不把文件内容又回显到终端上,只保留sudo的密码提示。四行顺序建议按focal、focal-updates、focal-backports、focal-security排,跟官方保持一致的阅读习惯。

这里用的是https://。Ubuntu 20.04 自带的 apt 是 2.x,原生支持 HTTPS,不需要额外装apt-transport-https(那个包在 apt 1.6 之后已经变成空壳)。如果你的环境里有老旧的防火墙对 HTTPS 做拦截,换成http://也能用,功能和包内容完全一致,只是少了传输层加密。我自己在所有能出网的机器上都用 HTTPS,图个干净。

3.3 更新索引并读懂 apt update 的输出

改完文件不等于生效,索引必须重建:

sudo apt clean sudo apt update

apt clean清掉/var/cache/apt/archives/里的 deb 缓存,apt update重新拉索引到/var/lib/apt/lists/。这两步合起来,能规避掉大部分因为镜像站切换导致的缓存不一致。

接着看输出。正常的阿里源输出应该长这样:

Get:1 https://mirrors.aliyun.com/ubuntu focal InRelease [265 kB] Get:2 https://mirrors.aliyun.com/ubuntu focal-updates InRelease [114 kB] Get:3 https://mirrors.aliyun.com/ubuntu focal-backports InRelease [108 kB] Get:4 https://mirrors.aliyun.com/ubuntu focal-security InRelease [114 kB] Get:5 https://mirrors.aliyun.com/ubuntu focal/main amd64 Packages [970 kB] ... Reading package lists... Done

判断是否真的换成阿里源,看两点:一是Get:/Hit:后面的域名是mirrors.aliyun.com;二是所有行前面都是Get或Hit,没有Err。Hit表示本地索引还是新的、没重新下载,Get表示重新拉了,两个都正常。

想看有多少包可以升级:

apt list --upgradable

如果这台机器是刚从官方源切过来的,这个列表通常会很长,几百个包不奇怪——因为官方源下你可能有半年没成功更新过了。别急着apt upgrade,至少先确认一下当前内核版本和你要跑的业务是否兼容,第 7 节有具体案例。

顺手测一下实测速度,方便和换源前做对比:

curl -o /dev/null -s -w 'time_total: %{time_total}s speed: %{speed_download} B/s\n' \ https://mirrors.aliyun.com/ubuntu/dists/focal/Release

这个命令只下一个几百字节的Release文件,speed_download受文件太小影响不太准,但time_total很能说明问题。换源前这个值经常是 2 秒以上,换源后在 0.1 到 0.3 秒之间是常态。

4. 换完仍然报错,按这个顺序查

4.1 第一步:看报错里的域名是谁

这条经验能省掉 80% 的无效排查时间。apt update报错的时候,先看报错信息里的域名。

  • 如果域名是mirrors.aliyun.com,问题是换源本身或者本地状态。
  • 如果域名是archive.ubuntu.com之类的官方域名,说明sources.list里还有没替换干净的行,或者/etc/apt/sources.list.d/里有别的文件在指向官方源。
  • 如果域名是某个你根本不认识的地址,那是第三方源的锅,跟换源无关。

查漏的命令很简单:

grep -rn 'ubuntu\.com' /etc/apt/sources.list /etc/apt/sources.list.d/ 2>/dev/null

4.2 NO_PUBKEY 和 GPG 报错:绝大多数是误判

网上流传着一堆"换阿里源之后要导入阿里云 GPG key"的教程,命令是这样的:

sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys XXXXXXXX

这里必须说清楚:阿里云镜像站镜像的是 Ubuntu 的原始包,签名用的就是 Ubuntu 官方的签名密钥,不存在"阿里云的 key"这回事。如果要导入,导入的也是 Ubuntu Archive Automatic Signing Key。所以正常情况下,换完源不该出现任何公钥问题。

那什么时候会真的报NO_PUBKEY?两种情况。一种是系统的ubuntu-keyring包被误删或者装的是精简版镜像,keyring 文件缺失:

dpkg -l | grep ubuntu-keyring sudo apt install --reinstall ubuntu-keyring

另一种是你自己加了第三方源,那个源的签名公钥没装。这时候的正确做法是找到该源的官方文档,按它给的方式把公钥放到/etc/apt/trusted.gpg.d/下:

curl -fsSL https://example.com/repo-key.asc \ | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/example.gpg

注意apt-key这个命令从 Ubuntu 20.04 起就已经被标记为废弃了,虽然在 20.04 上还能用,但会打印一堆警告。新写的脚本一律走trusted.gpg.d目录,别再用apt-key add。

4.3 Hash Sum mismatch、404 和时间异常的成因

这几类报错的成因差别很大,我做了个对照表,遇到的时候直接对号入座:

报错关键字常见原因处理方式
404 Not Found版本代号写错;在 22.04 上写了focallsb_release -cs核对,改回正确代号
404 Not Found(仅 security)ARM 平台错误使用了ubuntu/而非ubuntu-ports/换路径前缀
Hash Sum mismatch同一文件里混用了多个镜像站,同步进度不一致;或本地 lists 缓存脏sudo rm -rf /var/lib/apt/lists/*后重跑 update
Release file ... is not valid yet虚拟机时间落后于镜像索引的时间戳用timedatectl set-ntp true校时后重试
Temporary failure resolvingDNS 解析失败检查/etc/resolv.conf,或重启 systemd-resolved
Could not connect/Connection timed outIPv6 优先但不可达;或本地出口对 443 有限制试http://;或在 apt 配置里关闭 IPv6 优先

关于Hash Sum mismatch我要多讲一句。这个错的字面意思容易让人以为是"镜像站的文件坏了",实际上绝大多数情况是本地缓存问题。apt 会先下载Packages索引,再按索引里的哈希去校验 deb 包。如果你在两次apt update之间改了源,或者中途换了镜像站,本地索引里记的哈希和实际下到的包就对不上。清掉/var/lib/apt/lists/是最直接的办法:

sudo rm -rf /var/lib/apt/lists/* sudo apt clean sudo apt update

关于Release file is not valid yet,这个错在新装的虚拟机里特别常见。VMware 或者 VirtualBox 里刚装好的系统,如果用的是挂起/恢复而不是正常关机,系统时钟可能停在几天前甚至几年前。apt 校验索引文件时发现文件的时间戳"来自未来",就会拒绝。校时命令:

sudo timedatectl set-ntp true timedatectl status

4.4 卡在 Waiting for headers 或 0% 不动

这个现象和前面几种报错不一样,它不报错,就是干等,一分钟两分钟都没动静。我遇到过的成因有三类。

第一类是镜像站在做同步。阿里源的focal-updates每天要同步多次,同步窗口内某个文件可能短暂不可用。判断方法是在浏览器或者另一台机器上直接访问对应的 Release 文件地址,如果也慢,那就是站点侧的问题,隔十分钟再试。

第二类是MTU 和分片问题。虚拟机如果用了一些特殊网络模式,大包会被丢弃,表现就是小文件能下、大文件卡死。诊断方式是用ping发大包:

ping -M do -s 1472 mirrors.aliyun.com

如果提示Message too long或者丢包,说明路径 MTU 小于 1500。这种情况比较少见,一般出现在嵌套虚拟化或者某些企业网络里。

第三类是IPv6 优先但不可达。系统如果同时拿到 IPv4 和 IPv6 地址,apt 会优先走 IPv6,而部分网络的 IPv6 只是配了地址、实际没有出口,就会一直重试直到超时。临时验证的办法是用curl强制 IPv4 和 IPv6 分别测:

curl -4 -o /dev/null -s -w 'v4: %{time_total}s\n' https://mirrors.aliyun.com/ubuntu/dists/focal/Release curl -6 -o /dev/null -s -w 'v6: %{time_total}s\n' https://mirrors.aliyun.com/ubuntu/dists/focal/Release

如果 v6 明显超时而 v4 正常,那就定位到了。这种情况可以给 apt 单独加一条 IPv4 优先的配置,或者干脆在系统层调整 IPv6 的优先级。

5. 镜像站不止 apt,生态工具一起换掉

5.1 pip 的配置文件位置与优先级

apt 换完了,pip install还是几十 KB/s,这是另一套源在起作用。pip 的配置有三个层级,优先级从低到高:

  1. 全局:/etc/pip.conf(部分发行版是/etc/xdg/pip/pip.conf)
  2. 用户:~/.config/pip/pip.conf(老版本在~/.pip/pip.conf)
  3. 虚拟环境:$VIRTUAL_ENV/pip.conf

个人开发机上改用户级就够了:

mkdir -p ~/.config/pip cat > ~/.config/pip/pip.conf <<'EOF' [global] index-url = https://mirrors.aliyun.com/pypi/simple/ trusted-host = mirrors.aliyun.com EOF pip config list

trusted-host这一项在 HTTPS 下理论上不需要,但保留着能避开一些证书链不完整的环境问题,代价几乎为零。想临时用一次别的源,用-i参数覆盖:

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple requests

这里有个必须提醒的点:不要把trusted-host设置成通配或者设一堆域名。它本质上是在告诉 pip"这个主机的证书我不校验",加得越多风险越大,只加你实际在用的镜像站域名就行。

5.2 conda 的 .condarc 与索引重排

conda 的换源方式和 pip 完全不同,它读的是~/.condarc。阿里云提供了 anaconda 的完整镜像:

cat > ~/.condarc <<'EOF' channels: - defaults show_channel_urls: true default_channels: - https://mirrors.aliyun.com/anaconda/pkgs/main - https://mirrors.aliyun.com/anaconda/pkgs/r - https://mirrors.aliyun.com/anaconda/pkgs/msys2 custom_channels: conda-forge: https://mirrors.aliyun.com/anaconda/cloud pytorch: https://mirrors.aliyun.com/anaconda/cloud EOF conda clean -i conda config --show channels

注意channels下面只保留defaults,不要在channels里直接写 URL。defaults是一个别名,它的真实地址由default_channels决定,这样写的好处是后面想换镜像站只改default_channels就行,不用动所有环境。conda clean -i是清索引缓存,改完源必须跑一次,否则 conda 还在用旧索引,你会觉得"改了没用"。

5.3 npm 和 yarn 的 registry

前端生态对应的是 npm。阿里旗下的 npmmirror(就是原来的淘宝 npm 镜像)目前是事实标准:

npm config set registry https://registry.npmmirror.com/ npm config get registry # yarn yarn config set registry https://registry.npmmirror.com/

npm config set写进的是~/.npmrc,可以直接cat ~/.npmrc看。团队项目里我建议把这个文件纳入版本控制之外的初始化脚本,而不是手动敲——新人入职最常问的问题之一就是"为什么我npm i这么慢"。

顺带提一下pnpm,它读的是.npmrc里的registry字段,用pnpm config set registry https://registry.npmmirror.com/设置,规则和 npm 一致。

6. WSL、容器、ARM 板子上的差异

6.1 WSL2 里的 20.04 换源

WSL2 里的 Ubuntu 20.04 走的是宿主机的网络,出口和 Windows 主机一致。如果你在 Windows 上用浏览器下载速度正常,那 WSL 里也正常;如果 Windows 上访问某些资源也慢,WSL 里换源能改善的是"绕路"这部分,改不了出口本身的带宽。

WSL 的换源命令和普通 20.04 完全一样,/etc/apt/sources.list在同一个位置。唯一需要注意的是 WSL 里systemd默认不开(20.04 的 WSL 尤其如此),所以timedatectl这类依赖 systemd 的命令不一定能用。如果 WSL 里出现时间戳相关的报错,直接手动同步时间,或者干脆重启 WSL 实例:

wsl --shutdown

额外一句,WSL 里最值得投资的是终端配置和字体,而不是反复折腾源——源换一次就够了。

6.2 Dockerfile 里换源要合并到同一个 RUN

容器里的换源逻辑要写在 Dockerfile 里。这里有个高频错误:把换源、apt update、apt install拆成三个RUN指令。

# 反例:三个 RUN,中间产物全留在镜像层里 RUN sed -i 's@//.*archive.ubuntu.com@//mirrors.aliyun.com@g' /etc/apt/sources.list RUN apt-get update RUN apt-get install -y build-essential

问题在于:apt-get update拉下来的索引写在/var/lib/apt/lists/,如果不清理,这一层的体积可能就是几十兆;更要命的是 Docker 的层缓存机制会把每一层的状态都固化下来,sed改源那一层一旦被缓存,后面你想换镜像站必须改那一行才可能失效。

正确写法是合成一条:

RUN sed -i \ -e 's@//.*archive\.ubuntu\.com@//mirrors.aliyun.com@g' \ -e 's@//security\.ubuntu\.com@//mirrors.aliyun.com@g' \ /etc/apt/sources.list \ && apt-get update \ && apt-get install -y --no-install-recommends build-essential \ && rm -rf /var/lib/apt/lists/*

--no-install-recommends也很关键。默认 apt 会把Recommends列的弱依赖一起装上,镜像能膨胀几百兆,而这些包在容器里基本用不上。

6.3 树莓派和 ARM 平台要换 ubuntu-ports

ARM64 平台(树莓派 3/4/5、部分 ARM 云主机)的 Ubuntu 官方源不是archive.ubuntu.com,而是ports.ubuntu.com/ubuntu-ports。所以换源时路径前缀也得跟着变:

sudo sed -i \ -e 's@//ports\.ubuntu\.com/ubuntu-ports@//mirrors.aliyun.com/ubuntu-ports@g' \ /etc/apt/sources.list

判断标准很简单:dpkg --print-architecture输出arm64或armhf,就需要ubuntu-ports这个路径;输出amd64或i386,用ubuntu。搞错了就是一片 404,而且报错信息里不会明说"你架构选错了",得自己看出来。

7. 我踩过的坑和长期维护建议

7.1 换源后大升级,VMware 内核模块编译失败

这是我印象最深的一次。一台用了很久的 Ubuntu 20.04 虚拟机,之前一直没换源,apt update老是超时,于是抱着"一次性解决"的心态换成了阿里源,然后顺手apt upgrade了两百多个包,包含了内核更新。

重启之后 VMware Tools 的共享文件夹和分辨率自适应全废了,查日志发现是vmw_vmci、vmwgfx这几个内核模块编译失败。原因是内核头文件和open-vm-tools的 DKMS 模块需要匹配新内核重新编译,而编译过程需要linux-headers-$(uname -r)和对应的deb-src源——而我的sources.list里deb-src是注释掉的。

教训有两条:一是换源后不要无脑apt upgrade,尤其是有内核更新的场景,先apt list --upgradable | grep linux看一眼会升几个内核;二是如果你需要编译内核模块,deb-src那几行得提前打开,或者至少知道怎么临时打开:

# 查看当前内核版本 uname -r # 安装匹配的内核头文件 sudo apt install linux-headers-$(uname -r) # 重装 DKMS 模块触发重新编译 sudo dpkg-reconfigure open-vm-tools-dkms

7.2 做大版本升级前必须把源改回去

do-release-upgrade这个命令在换过源的机器上有一个非常隐蔽的坑。它升级系统版本的时候,会去读sources.list,把里面的版本代号从focal替换成下一个版本(jammy),然后按新代号去拉包。

问题在于:如果你的源已经换成阿里源,升级过程会全程走阿里源。这本身不是坏事,阿里源也有jammy。但如果某个时刻镜像站正在同步jammy的索引,体积巨大的升级过程就会在中途失败,而且失败之后系统的状态是半升级——部分包是 22.04 的、部分是 20.04 的,apt 的依赖关系一塌糊涂。这种情况下恢复非常痛苦。

我的做法是:做大版本升级之前,先把sources.list换回官方源,升完再换回阿里源。听起来多此一举,但升级本身一年也就一次,多花五分钟换一个可控的过程,很值。真要出问题,官方源的一致性保证也更好。

# 升级前:恢复官方源备份 sudo cp -a /etc/apt/sources.list.bak /etc/apt/sources.list # 升级完成后:再换回来 sudo cp -a /etc/apt/sources.list.aliyun /etc/apt/sources.list

注意备份文件的语义要区分开:sources.list.bak是"换源之前的官方源",sources.list.aliyun是"换好之后的阿里源"。我第一次做的时候两个文件都叫.bak,覆盖掉了官方源,后来只能从/root/sources.list.orig里找回来。

7.3 把换源固化进装机脚本和快照模板

单台机器手动换源没问题,但如果要给团队开十台虚拟机,手动操作一定会漏。我现在的做法是写一个setup-mirror.sh,放在每个新虚拟机的/root/下,内容就是这一整套:

#!/usr/bin/env bash set -euo pipefail CODENAME="$(lsb_release -cs)" ARCH="$(dpkg --print-architecture)" if [ "$ARCH" = "arm64" ] || [ "$ARCH" = "armhf" ]; then MIRROR_PATH="ubuntu-ports" else MIRROR_PATH="ubuntu" fi cat > /etc/apt/sources.list <<EOF deb https://mirrors.aliyun.com/${MIRROR_PATH}/ ${CODENAME} main restricted universe multiverse deb https://mirrors.aliyun.com/${MIRROR_PATH}/ ${CODENAME}-updates main restricted universe multiverse deb https://mirrors.aliyun.com/${MIRROR_PATH}/ ${CODENAME}-backports main restricted universe multiverse deb https://mirrors.aliyun.com/${MIRROR_PATH}/ ${CODENAME}-security main restricted universe multiverse EOF apt-get clean apt-get update

把版本代号和架构都做成变量,脚本就能直接复用到 22.04、24.04 上,不用每次改。set -euo pipefail让脚本在任何一步失败时立刻退出,避免"改了源但 update 失败"这种半成品状态被带进快照。

然后是关键的一步:换好源、清理干净、apt update通过之后再打虚拟机快照。这样克隆出来的所有机器天生就是阿里源,不需要再操作一遍。反过来,如果先打快照再换源,每克隆一台就得手动来一次,早晚会忘。

我个人的习惯是在模板机里额外做两件事:一是删掉/var/lib/apt/lists/*里克隆后会自动继承的索引,让新机器第一次apt update拿到的是干净的列表;二是把sources.list的阿里源版本同时备份成/etc/apt/sources.list.aliyun,这样后面要临时切回官方源测试的时候,两份文件都在,切回来只要一条cp。

最后分享一个判断"这台机器到底换没换源"的小技巧,比cat sources.list更快:

apt-cache policy | grep -m1 -B1 'release' || true grep -rhoP '(?<=https?://)[^/]+' /etc/apt/sources.list | sort -u

第二条命令会把源文件里所有域名去重列出来。如果输出只有mirrors.aliyun.com一个,说明换干净了;如果混着官方域名,那就是还有漏改的行。这个命令在排查别人的机器时特别顺手,一眼就能看出问题出在哪。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询