拿到一台 Ubuntu 20.04 服务器,第一件事是什么?绝大多数人都会先跑apt update,然后被默认源折磨得没脾气。官方源在国内的连接质量并不稳定,依赖安装经常半路超时;好不容易把源换好,继续安装某个包时又冒出一句No LSB modules are available.。这句话看着吓人,实际上就是一个可选模块缺失,属于换源之后顺手能修掉的小问题。
结合我日常处理 Linux 服务器时的习惯,我把 Ubuntu 20.04 修改 apt 源的关键操作整理成可以直接照抄的步骤,每条命令都解释清楚为什么这么做,再花篇幅讲明白No LSB modules报错到底怎么回事、该装哪个包,以及改源之后最容易踩的四个坑。适合刚接触 Linux 的初学者,也适合那些想弄清楚“改源”背后原理的运维朋友。
1. 一条命令敲下去之前,先搞清 sources.list 到底在管什么
1.1 默认源的链路与“慢”的本质
Ubuntu 的 apt 默认从官方源下载软件包,20.04 版本默认写入的是archive.ubuntu.com和security.ubuntu.com两个域名。官方 CDN 在国外的响应速度,受国际出口带宽、运营商路由策略、机房地理位置多重影响。很多人遇到的“卡在 1% 不动”“连接超时”“error 101”基本都是这个链路里某个环节引起的。
改源的目的不是让系统变得“更高级”,而是把软件包下载终点换成国内同步速度更快的镜像地址。镜像站在绝大多数情况下和官方源保持同步,路径结构也一致,只是服务器放在国内,走国内网络更快。所以改源的实质,就是替换/etc/apt/sources.list文件里的域名和路径前缀。
1.2 sources.list 一行一行拆开看
先看一个最典型的 20.04 源写法:
deb http://archive.ubuntu.com/ubuntu/ focal main restricted universe multiverse这行看起来长,其实拆开只有四个部分:
| 字段 | 含义 | 常见取值 |
|---|---|---|
| deb | 表示二进制软件包仓库 | deb;如果写 deb-src 则是源码包仓库 |
| URL | 软件包仓库的实际地址 | 官方或镜像站的 ubuntu 目录 |
| 发行版代号 | 哪个系统版本使用的仓库 | focal、focal-updates、focal-security |
| 组件 | 仓库里的软件分类 | main、restricted、universe、multiverse |
focal是 Ubuntu 20.04 的代号,这个不能写错。写成bionic就是 18.04 的仓库,系统会直接去下载不匹配的软件包元数据,轻则报错重则装坏依赖。focal-updates是持续更新的软件包,focal-security是安全补丁,focal-backports是向后移植的新版本。
组件部分,main是官方维护的自由软件,restricted是官方维护的非自由软件,universe是社区维护的自由软件,multiverse是社区维护的非自由软件。20.04 的官方源默认会把这四个组件全部启用,但我在很多精简过的教程里见过只写main restricted的写法,会导致装一些常用软件时报“无法定位软件包”。后面会专门处理这个问题。
1.3 为什么改源之前先备份是最低消费
修改任何系统配置文件,第一条原则永远是备份。/etc/apt/sources.list这个文件特别容易手滑,比如多敲一个空格、LOCATION 写错、把focal打成focal/,apt 的行为会变得很不可控。备份不是为了仪式感,是为了给自己留一条 10 秒内回到原点的路。
备份建议用带时间戳的方式:
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak.$(date +%F)这样不仅保留了原始内容,还能看出是什么时候备份的。恢复老配置只需要把备份文件覆盖回去:
sudo cp /etc/apt/sources.list.bak.$(date +%F) /etc/apt/sources.list很多高手还会额外备份/etc/apt/sources.list.d/目录,因为这个目录里的.list文件同样会被 apt 读取。如果你装过某些第三方软件仓库,改源时很容易漏掉这个目录,导致新旧源混在一起。
2. 把源换成国内可用的镜像地址——两种落地方案
2.1 方案A:用 sed 对官方域名做全局替换
如果你希望保留源文件里原有的结构,只换域名,最稳的方法是sed全局替换。先备份,再执行替换:
sudo sed -i 's#archive.ubuntu.com#mirrors.region.example.com#g' /etc/apt/sources.list sudo sed -i 's#security.ubuntu.com#mirrors.region.example.com#g' /etc/apt/sources.list我用#作为分隔符,而不是常见的/,因为 URL 里本身就带斜杠。如果还用/分隔,整个命令会变得像转义地狱,读起来非常痛苦。-i表示原地修改,g表示替换这一行里所有匹配项,不只是第一个。
这里你看到的mirrors.region.example.com是关键占位符。实际操作时,把它换成你本地网络访问速度最好的镜像地址。挑选标准后面我会说,这里先记住路径结构即可。替换完成后用下面命令检查效果:
grep -E "^(deb|deb-src)" /etc/apt/sources.list正常输出里应该看不到任何archive.ubuntu.com或security.ubuntu.com,全部变成你的镜像域名。sed的优点是快、可重复执行,缺点是如果原始文件里本来就有奇怪的路径或多余组件,替换完还是带着问题。
2.2 方案B:直接重写 sources.list 并补齐四个组件
对于希望从头控制源文件内容的人,我更喜欢直接重写整个文件。先把旧文件备份,然后用编辑器打开:
sudo vim /etc/apt/sources.list把内容清空后,粘贴以下模板:
deb http://mirrors.region.example.com/ubuntu/ focal main restricted universe multiverse deb http://mirrors.region.example.com/ubuntu/ focal-updates main restricted universe multiverse deb http://mirrors.region.example.com/ubuntu/ focal-backports main restricted universe multiverse deb http://mirrors.region.example.com/ubuntu/ focal-security main restricted universe multiverse # deb-src http://mirrors.region.example.com/ubuntu/ focal main restricted universe multiverse # deb-src http://mirrors.region.example.com/ubuntu/ focal-updates main restricted universe multiverse四行分别对应基础仓库、更新仓库、向后移植仓库、安全更新仓库。focal-security我刻意没有省略,因为安全补丁非常重要,很多精简教程只顾主仓库,导致系统长期收不到安全更新。
deb-src行我全部注释掉,这是源码包仓库,普通使用场景用不到,留着反而会让apt update多拉一份源码索引。如果你搞编译开发,再取消注释也不迟。
这里必须强调一件事:mirrors.region.example.com是示意地址,不要真的填进去。国内可用镜像一般分两类,一类是云厂商提供的开源镜像站,一类是高校维护的软件镜像。挑选时我不看名气,只看三个硬指标:
| 指标 | 原因 |
|---|---|
| 是否支持 HTTPS | 明文 HTTP 在传输过程容易被篡改,软件包校验虽能发现异常,但没必要冒险 |
| 同步频率 | 越频繁越好,否则你会遇到软件包索引里有、但下载时报 404 |
| 本地可访问速度 | 用curl -I猜一下响应时间,或者直接跑一次apt update感受延迟 |
另外注意路径细节。有的镜像站在 URL 后面要求写/ubuntu/,有的要求写/ubuntu,有的还区分ubuntu和ubuntu-security两个路径。实际使用时最好打开镜像站首页,找到“ubuntu 源使用帮助”页面,看它给出的 20.04 写法长什么样,直接复制比手敲可靠得多。
2.3 更新索引并确认改源生效的实证标志
无论用哪种方案,写完文件之后都必须刷新软件包索引:
sudo apt updateapt update不是把软件装上,而是拉取每个仓库的Packages索引文件。这些索引记录了仓库里有哪些软件包、什么版本、依赖什么,后面apt install就是根据这个索引去定位软件包。如果源写错,这一步就会直接暴露问题。
改源成功、网络通畅的情况下,apt update的输出应该是几条Get和Hit交替出现,最后以Reading package lists... Done结束无报错。看到下面这些特征基本可以放心:
Get:1 http://mirrors.region.example.com/ubuntu focal InReleaseGet:2 http://mirrors.region.example.com/ubuntu focal-updates InReleaseHit表示文件没变化,Get表示实际下载了新数据
如果出现大段Err或W: ... 404 Not Found,先检查文件里的路径末尾有没有该加的斜杠,再检查代号是不是focal,不要急着重装系统。
3. 遇到 “No LSB modules are available” 时,到底少了什么
3.1 触发场景与报错来源的完整关系
很多人在跑service --status-all查看系统服务时,输出开头多出一行No LSB modules are available.,之后才是一堆服务状态。也有人是在跑lsb_release -a时看到同样的话。
产生原因要从 LSB 说起。LSB 是 Linux Standard Base 的缩写,目的是让不同发行版之间在初始化脚本、发行版信息、命令行为上保持某种一致性。Ubuntu 的service命令在处理 SysV 初始化脚本时,会尝试读取 LSB 信息来判断脚本应该如何启动、停止。如果系统里缺少提供 LSB 运行时支持的软件包,service就找不到完整的模块信息,于是打印出这么一句提示。
最小化安装的 Ubuntu 20.04、Docker 容器里裁剪过的镜像、被人手工删过/etc/lsb-release的系统,最容易触发这个报错。它本身不会让系统崩溃,但会污染脚本输出,还可能让依赖lsb_release命令的第三方安装脚本直接失败。
3.2 lsb-release、lsb-base、lsb-core 三兄弟的区别
修复之前,先把三个看起来很像的包理清楚:
| 软件包 | 作用 | 是否解决 No LSB modules |
|---|---|---|
| lsb-release | 提供/usr/bin/lsb_release命令和/etc/lsb-release文件 | 能解决大部分报错 |
| lsb-base | 提供 LSB 初始化脚本基础支持,比如/lib/lsb/init-functions | 有它更完整 |
| lsb-core | 一组 LSB 核心运行时集合,包含版本检测等更多内容 | 报错严重时可装 |
其实lsb-release是最常见的缺口。它提供的那条lsb_release命令,是很多初始化框架检测系统发行版信息的关键工具。缺了它,脚本拿不到 Codename、Release、Description,只能提示模块不可用。
lsb-base是运行基础,它提供/etc/init.d/lsb脚本的支持文件,某些 SysV 服务在启动时依赖这些函数。如果一个系统连lsb-base都没有,光装lsb-release也可能不够。lsb-core更像是集成包,会把 LSB 相关的核心模块一起装好,一般用于做到“以后别再报错”的目的。
3.3 安装 LSB 模块的完整步骤与验证
先用更新后的源刷新索引,这一步尤其重要——如果你拿的是刚改完源还没来得及apt update的终端,直接安装大概率会失败:
sudo apt update sudo apt install lsb-release装完后重新执行lsb_release -a,输出应该不再是那行单句报错,而是完整的发行版信息:
Distributor ID: Ubuntu Description: Ubuntu 20.04.6 LTS Release: 20.04 Codename: focal如果重新执行service --status-all仍然出现No LSB modules,那就再装lsb-base和lsb-core,顺序不重要,一次装齐更省事:
sudo apt install lsb-base lsb-core装完再次验证。这一步贵在让完整的 LSB 环境落地,而不是只装一个名字看起来像的那个包。
这个修复为什么在标题里被标成“可选”?因为service命令本身对系统服务的管理并不完全依赖 LSB 模块,SysV 脚本的start/stop/status逻辑照样能跑。报错只是少了发行版识别层面的信息,不会直接导致系统停机。但对于写脚本自动化运维的人,多一行脏输出就多一分误判风险,所以我还是建议顺手装掉。
4. 改源之后常见的四个报错,定位思路一次讲清
4.1 NO_PUBKEY:公钥过期或未导入时的解决方案
改源后跑apt update,最常听到的报错是:
W: GPG error: http://mirrors.region.example.com/ubuntu focal InRelease: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY XXXXXXXXXXXXXXXX出现这个问题的原因有两种:一是新换的镜像仓库发行了一套不同的签名密钥,而本地没有导入;二是系统时间不对,导致密钥验证过期。前者更常见。
处理思路是找到报错里NO_PUBKEY后面的那串十六进制密钥 ID,把公钥手动导入:
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys XXXXXXXXXXXXXXXXkeyserver.ubuntu.com是官方公钥服务器,导入之后再跑一次sudo apt update,W 警告应该消失。需要提醒一句,apt-key在 Ubuntu 20.04 上还能正常用,但如果后续升级到新版本,建议改用/etc/apt/keyrings/目录管理密钥,这是后话。
4.2 404 Not Found:抱错 focal 与 focal-security 的最小复现
另一个高频问题是:
Err:5 http://mirrors.region.example.com/ubuntu focal-security/main amd64 Packages 404 Not Found [IP: 127.0.0.1]404 说明这个 URL 在镜像服务器上不存在。常见原因是镜像站没有同步某个仓库,或者路径写法不对。比如有的镜像站把安全更新放在单独的/ubuntu-security/路径下,和安全源本身是两回事。
排查步骤按顺序来:
- 检查发行版代号确实是
focal,不是jammy或bionic - 打开浏览器直接访问报错的 URL,看到目录列表说明路径没问题
- 去镜像站帮助页确认它的 20.04 仓库路径是否跟官方一致
- 如果该镜像没有同步
proposed这类特殊仓库,就删掉对应行别惦记
一个小技巧:临时用apt update -o Acquire::http::Timeout=5加快报错反馈,能快速试出到底哪行源有问题。
4.3 组件缺失:universe/multiverse 被注释后依赖消失
很多时候“无法定位软件包”跟源地址没关系,纯粹是组件写少了。比如你想装htop,它属于universe组件,但你的sources.list只留了main restricted,apt 就会告诉你找不到。
解决方式很直接:把四行源的组件部分统一补成main restricted universe multiverse,再更新索引。不需要一个个追查软件包属于哪个组件,四个组件全开基本能覆盖绝大多数软件需求。
universe覆盖海量社区软件,multiverse包含一些非自由软件和特殊授权包。从安全角度讲,组件并不是开得越多越危险,apt 的软件包签名和安装确认机制依然有效,只是搜索范围变大而已。担心软件来源的话,重点看你到底从哪个仓库拉包,而不是担心组件数量。
4.4 改坏了也没关系:一条命令回滚到官方源
手滑把源写坏,最忌讳的是慌着删文件。只要备份还在,一条命令就能回到官方默认状态:
sudo cp /etc/apt/sources.list.bak.$(date +%F) /etc/apt/sources.list如果你完全没备份,但还记得官方源的格式,也可以直接写回上面第一步里的官方写法。回滚之后立刻执行sudo apt update确认系统恢复正常,再重新走一遍改源流程,速度反而最快。
一个额外的排查方向是/etc/apt/sources.list.d/目录。有时候你改了主文件,但 apt 仍然去连接旧地址,多半是目录下某个.list或.sources文件在捣乱。处理办法是把可疑文件临时移走:
sudo mv /etc/apt/sources.list.d/custom.list /etc/apt/sources.list.d/custom.list.disabled移动后再跑apt update,如果报错消失,那就是这个文件的问题。这个目录容易被忽略,但实际排障时比想象中更常遇到。
5. 一点超出标题的经验:容器与多版本场景下的源管理
5.1 用 codename 变量写出一套可复制的源模板
20.04 用focal,22.04 用jammy,如果每换一台机器就去查一遍代号,效率实在太低。我自己的习惯是直接复用系统变量:
source /etc/os-release echo "当前版本代号: ${UBUNTU_CODENAME:-$VERSION_CODENAME}"拿到代号后,写源模板时可以把它硬编码进去,也可以写成带变量的形式。带变量的版本适合拿来写自动化脚本,举例:
CODENAME=$(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") sudo sed -i "s/focal/${CODENAME}/g" /etc/apt/sources.list这个思路本质是把“系统版本”和“源写法”解耦。无论当前是 20.04 还是 22.04,脚本都能生成匹配的源,不会因为手动填错代号而翻车。
5.2 升级到新版本后源失效的复盘
很多人在do-release-upgrade升级系统版本以后,会遇到源全部 404 的情况。原因很简单:升级过程会把/etc/apt/sources.list里的版本代号整体替换成新系统代号,但国内镜像不一定立刻同步新版本的仓库目录。
这时候不要急着把源改回老版本,否则升级后的软件包管理会非常混乱。我的建议是:
- 先临时使用官方源跑一次
apt update,确认新系统基本可更新 - 等待镜像站公告完成新版本同步
- 再把源切回镜像站
如果升级前能主动把镜像源恢复成官方源,升级过程其实更顺,升级完成后再切换回来,这是目前我验证过最稳的顺序。
5.3 源管理这件事的日常维护习惯
改源不是一次性的活儿,它需要偶尔维护。我给自己定了几条规矩:
- 每次改源后至少跑一次
apt update,确认没有 W 或 Err - 新装第三方软件仓库前,先看
.list文件里是否夹杂旧源 - 定期检查系统里有没有失效的源,用
apt update的输出当警报器 - 重要机器保留一个带日期的源备份,而不是让备份文件被下一次备份覆盖
容器场景更特殊。Docker 镜像里如果裁剪过 apt 列表,apt update后可能连基础工具都装不全。这种时候我习惯先补ca-certificates和gnupg,再把源模板改掉,否则后面拉密钥、验证书都会出问题。lsb-release在这个环境里尤其容易被忽略,但它对很多安装脚本的判断逻辑又很重要,所以标题里那句“可选安装”其实是有经验判断在里面的。
改源这件事情,真正考验人的不是会不会敲命令,而是遇到报错时能不能快速定位到文件结构、组件、密钥、路径这几个核心变量上。我处理过不少“源有问题”的工单,最后大多数都落在 404 和公钥这两个点上。把上面这些检查流程走一遍,八成问题都能自己解决。