)
1. 先把源這件事說(shuō)透apt 與 sources.list 到底在干什么很多人第一次碰 Ubuntu 的 apt 源配置都是被逼的。要么是apt update卡在Connecting to archive.ubuntu.com轉(zhuǎn)到天荒地老要么是裝個(gè)nvidia-driver-535卡在下載 500MB 的包上進(jìn)度條十分鐘不動(dòng)一格。改完源之后速度從幾十 KB/s 跳到十幾 MB/s那種感覺(jué)跟換了臺(tái)機(jī)器一樣。但真正把源配置搞明白的人不多大部分人是從網(wǎng)上抄一段現(xiàn)成的配置粘進(jìn)去能跑就不管了。問(wèn)題是 Ubuntu 從 24.04 開(kāi)始主推 deb822 新格式抄來(lái)的老格式配置放進(jìn)去要么不生效要么和新文件打架apt update直接報(bào)重復(fù)定義。所以我打算從一次 apt install 到底發(fā)生了什么講起把每個(gè)配置項(xiàng)拆開(kāi)揉碎最后落到能直接抄的實(shí)操步驟上。這篇內(nèi)容適合三類人剛裝完 Ubuntu 想換源但不知道每個(gè)字段什么意思的新手、維護(hù)內(nèi)網(wǎng)幾十臺(tái)機(jī)器需要統(tǒng)一源配置的運(yùn)維、以及遇到 GPG 報(bào)錯(cuò)或 404 之后只能靠重裝系統(tǒng)解決的老用戶。我會(huì)盡量用生活化的類比解釋機(jī)制同時(shí)在關(guān)鍵的地方給出準(zhǔn)確的字段語(yǔ)義和可復(fù)現(xiàn)的命令讀完你應(yīng)該能做到看一行源配置就知道它指向哪個(gè)倉(cāng)庫(kù)、包含哪些組件、用哪把密鑰校驗(yàn)而不是只會(huì)復(fù)制粘貼。1.1 一次 apt install 背后發(fā)生的完整鏈路先建立一個(gè)心智模型。你把a(bǔ)pt想象成一個(gè)采購(gòu)員/etc/apt/sources.list和/etc/apt/sources.list.d/里的文件就是它的供應(yīng)商名錄而/var/lib/apt/lists/是它手上的庫(kù)存清單。當(dāng)你敲下sudo apt install nginx時(shí)真正發(fā)生的事情分幾步走。第一步apt 讀取所有源配置文件把每個(gè)倉(cāng)庫(kù)的地址、發(fā)行版代號(hào)、組件列表拼成實(shí)際的 URL。第二步它去每個(gè)倉(cāng)庫(kù)下載Packages索引文件——這個(gè)文件是純文本的里面記錄了每個(gè)包的名稱、版本、依賴關(guān)系、文件大小和 SHA256 校驗(yàn)值但不含包體本身。第三步把這些索引解壓后存到/var/lib/apt/lists/。第四步apt 在本地索引里做依賴求解算出裝 nginx 需要哪幾個(gè)包。第五步按依賴順序從倉(cāng)庫(kù)拉取.deb包體下載到/var/cache/apt/archives/。第六步用dpkg逐個(gè)解包安裝。理解這個(gè)鏈路的好處是你會(huì)知道apt update和apt upgrade是兩件完全不同的事。前者只刷新索引清單后者才真正動(dòng)包。所以磁盤空間緊張的時(shí)候/var/lib/apt/lists/占幾百 MB 是正常的它不是緩存垃圾刪了下次 update 還得重新下。而/var/cache/apt/archives/里的.deb才是可以放心清的這也是apt clean干的事情。還有一個(gè)細(xì)節(jié)值得說(shuō)apt update是按源逐個(gè)請(qǐng)求的任何一個(gè)超時(shí)都會(huì)拖慢整體速度默認(rèn)超時(shí)相對(duì)寬松。所以當(dāng)你的源列表里有幾個(gè)不可達(dá)的條目時(shí)整個(gè) update 會(huì)顯得特別慢而不是干脆失敗。這也是為什么排查 apt 慢的第一步永遠(yuǎn)是看有沒(méi)有失效的源。1.2 軟件源、倉(cāng)庫(kù)、組件與架構(gòu)之間的關(guān)系這里有幾個(gè)詞經(jīng)常被混用理清楚之后看配置就不迷糊了。軟件源repository / mirror指的是一個(gè) HTTP 或 FTP 站點(diǎn)比如官方的archive.ubuntu.com或者各高校和企業(yè)提供的鏡像站點(diǎn)。鏡像站做的事很簡(jiǎn)單定期從上游同步一份完整拷貝讓地理位置近的用戶就近下載。所以你在鏡像站上看到的內(nèi)容和官方是一模一樣的只是域名不同。發(fā)行版代號(hào)suite / codename是 Ubuntu 的版本代號(hào)比如 20.04 是focal22.04 是jammy24.04 是noble。每個(gè)代號(hào)下面還會(huì)派生幾個(gè)口袋noble是發(fā)布時(shí)的凍結(jié)快照noble-updates是發(fā)布后的常規(guī)更新noble-security是安全補(bǔ)丁noble-backports是從新版本回遷的軟件noble-proposed是待驗(yàn)證的預(yù)發(fā)布更新。新手最容易犯的錯(cuò)是把這幾種混在一個(gè)組件列表里或者漏掉 security 導(dǎo)致系統(tǒng)長(zhǎng)期不打安全補(bǔ)丁。組件component是 Ubuntu 對(duì)軟件包做的分類一共四個(gè)main是官方支持的自由軟件restricted是官方支持的專有驅(qū)動(dòng)顯卡驅(qū)動(dòng)就在這里universe是社區(qū)維護(hù)的自由軟件multiverse是有版權(quán)或法律限制的軟件。默認(rèn)的四組件全開(kāi)是有道理的——你裝nvidia-driver-535需要restricted裝很多開(kāi)發(fā)工具需要universe。架構(gòu)architecture則是 CPU 類型現(xiàn)在絕大多數(shù)是amd64ARM 服務(wù)器是arm64樹(shù)莓派早期是armhf。只有你需要在一臺(tái)機(jī)器上裝另一種架構(gòu)的包比如給 ARM 設(shè)備做交叉編譯準(zhǔn)備時(shí)才需要在源配置里顯式聲明架構(gòu)否則 apt 會(huì)自動(dòng)按當(dāng)前系統(tǒng)架構(gòu)去請(qǐng)求。把這四個(gè)維度想成快遞地址 課本版本 章節(jié)范圍 語(yǔ)言版本源配置那一行字符串的含義就清楚了。1.3 為什么國(guó)內(nèi)機(jī)器必須換源這不是玄學(xué)。官方源archive.ubuntu.com的服務(wù)器在境外物理距離帶來(lái)的延遲是客觀存在的幾十毫秒到幾百毫秒不等。更關(guān)鍵的是單個(gè)連接的帶寬會(huì)被限制而且跨國(guó)鏈路在晚高峰時(shí)抖動(dòng)明顯表現(xiàn)為下載速度忽高忽低甚至斷流。換到地理位置近的鏡像站好處不只是快。鏡像站通常有更大的出口帶寬apt update時(shí)并發(fā)拉取索引的體驗(yàn)明顯更順同時(shí)因?yàn)殒溌范坛霈F(xiàn)連接超時(shí)的概率低很多。實(shí)測(cè)下來(lái)同樣是noble的完整組件索引換源前后 update 耗時(shí)的差距常常在五到十倍之間。注意換源本身不改變?nèi)魏诬浖?nèi)容鏡像站只是搬運(yùn)工。但鏡像站有同步延遲通常幾小時(shí)以內(nèi)所以極個(gè)別情況下你需要的包版本在鏡像站上還沒(méi)同步過(guò)來(lái)。這種時(shí)候臨時(shí)切回官方源即可不必懷疑是配置寫錯(cuò)了。2. sources.list 配置項(xiàng)逐字段拆解搞清楚機(jī)制之后就可以正面拆那行看似天書的配置了。老格式也叫 one-line style長(zhǎng)這樣deb http://archive.ubuntu.com/ubuntu/ noble main restricted universe multiverse這一行只有五個(gè)字段但每個(gè)字段都有講究。我按順序把它們講透。2.1 單行格式的五個(gè)字段順序不能錯(cuò)第一個(gè)字段是歸檔類型只有兩個(gè)合法值deb表示這個(gè)源提供二進(jìn)制包編譯好的.debdeb-src表示提供源碼包Sources索引加.dsc/.tar.xz等。日常使用只需要deb。第二個(gè)字段是倉(cāng)庫(kù)基地址注意結(jié)尾的斜杠。這個(gè)斜杠不是可有可無(wú)的裝飾apt 會(huì)把后面的路徑直接拼上去比如最終請(qǐng)求的是http://archive.ubuntu.com/ubuntu/dists/noble/main/binary-amd64/Packages.xz。少寫斜杠有時(shí)候也能跑但不同版本的 apt 行為不完全一致建議嚴(yán)格保留。第三個(gè)字段是發(fā)行版代號(hào)可以是noble也可以是noble-updates、noble-backports這類派生名。一個(gè)容易忽略的點(diǎn)是同一個(gè)代號(hào)下可以并列寫多個(gè)用空格分隔apt 會(huì)把多個(gè)口袋的索引合并看。但更清晰的做法是每個(gè)口袋寫一行報(bào)錯(cuò)時(shí)能一眼定位是哪個(gè)源出的問(wèn)題。第四個(gè)及之后的所有字段都是組件列表順序無(wú)所謂重復(fù)也沒(méi)關(guān)系。main restricted universe multiverse四件套是官方推薦的完整組合。第五個(gè)位置其實(shí)沒(méi)有第五個(gè)字段了組件一直寫到行尾。如果你看到有人在組件后面又寫了東西那多半是寫錯(cuò)了。2.2 deb 和 deb-src 到底什么時(shí)候才需要很多教程會(huì)讓你把deb-src也一起加上理由是以后可能要編譯源碼。我的建議是除非你明確要做 Debian 打包、內(nèi)核編譯或者需要apt source拉某個(gè)包的源碼否則不要加。原因是deb-src會(huì)讓apt update額外下載每個(gè)組件的Sources索引體積和耗時(shí)都不小而且這些索引 99% 的時(shí)間是躺在那里吃磁盤。Ubuntu 官方從某個(gè)版本開(kāi)始默認(rèn)就注釋掉了deb-src行就是這個(gè)思路。需要的時(shí)候再臨時(shí)打開(kāi)改完跑一次apt update用完注釋掉這是更務(wù)實(shí)的做法。注意源碼包通常在universe和main里都有做打包工作的話四個(gè)組件的 src 都要開(kāi)。2.3 發(fā)行版代號(hào)寫錯(cuò)會(huì)發(fā)生什么代號(hào)寫錯(cuò)是新手最常踩的坑而且報(bào)錯(cuò)信息很有迷惑性。如果你在 22.04jammy上寫了focalapt update會(huì)正常完成——因?yàn)閒ocal確實(shí)是個(gè)合法的目錄倉(cāng)庫(kù)里真有這些文件。但接下來(lái)你裝包時(shí)會(huì)發(fā)現(xiàn)版本對(duì)不上或者裝了個(gè)老版本的庫(kù)把系統(tǒng)依賴搞亂。更糟的是把代號(hào)寫成stable或testing這類 Debian 風(fēng)格的別名。Ubuntu 倉(cāng)庫(kù)里也有stable這個(gè)目錄但它指向的東西和你想的完全不是一回事可能會(huì)拉進(jìn)一堆不匹配的包。確認(rèn)自己代號(hào)的方法lsb_release -cs # 或者 . /etc/os-release echo $VERSION_CODENAME第二個(gè)命令更可靠因?yàn)閘sb_release在某些精簡(jiǎn)系統(tǒng)上沒(méi)裝。拿到代號(hào)之后照著寫一個(gè)字母都別改。2.4 方括號(hào)里的選項(xiàng)字段arch、signed-by、lang在deb和 URL 之間還可以插入一個(gè)方括號(hào)包裹的選項(xiàng)塊多個(gè)選項(xiàng)用空格分隔。這塊是很多人看到但從來(lái)沒(méi)搞懂的部分。[archamd64]用于限制這個(gè)源只對(duì)指定架構(gòu)生效。典型場(chǎng)景是你在 x86 上通過(guò)dpkg --add-architecture arm64加了 ARM 架構(gòu)支持但只想讓某個(gè)特定源提供 ARM 包其他源保持 amd64這時(shí)候就需要按源聲明 arch否則 apt 會(huì)去找所有源的 arm64 索引找不到就是一堆 404。[signed-by/usr/share/keyrings/xxx.gpg]指定這個(gè)源用哪把 GPG 公鑰校驗(yàn)簽名。這是第三方源的標(biāo)準(zhǔn)姿勢(shì)后面講 PPA 和 Docker 源時(shí)會(huì)重點(diǎn)說(shuō)。它的作用是防止公鑰全局信任帶來(lái)的風(fēng)險(xiǎn)——以前的做法是把密鑰apt-key add進(jìn)全局信任環(huán)意味著任何源都能用這把密鑰簽名安全性差。[langzh_CN]這種是針對(duì)Translation索引的控制下載哪些語(yǔ)言的翻譯文件。默認(rèn)情況下 apt 會(huì)下載所有語(yǔ)言的翻譯索引中文用戶可以把語(yǔ)言限制到zh_CN和en能省下不少 update 時(shí)間和流量。這個(gè)選項(xiàng)不常用但倉(cāng)庫(kù)大的時(shí)候效果明顯。# 只下載中英文翻譯索引的寫法 deb [langzh_CN,en] http://mirrors.example.edu.cn/ubuntu/ noble main restricted universe multiverse還有一個(gè)[trustedyes]字面意思是跳過(guò)簽名校驗(yàn)。這玩意兒只應(yīng)該在完全可控的內(nèi)網(wǎng)離線源上使用公網(wǎng)源用它是自找麻煩。我見(jiàn)過(guò)有人為了解決 GPG 報(bào)錯(cuò)直接加trustedyes等于把整條供應(yīng)鏈的校驗(yàn)環(huán)節(jié)拆掉了。3. deb822 新格式Ubuntu 24.04 之后繞不開(kāi)的寫法如果你裝的是 Ubuntu 24.04 或更新版本打開(kāi)/etc/apt/sources.list大概率會(huì)發(fā)現(xiàn)里面只有一行注釋寫著這個(gè)文件已經(jīng)被/etc/apt/sources.list.d/ubuntu.sources取代。這不是 bug是 apt 2.4 之后引入的 deb822 格式成為默認(rèn)。3.1 .sources 文件的結(jié)構(gòu)與字段含義新格式長(zhǎng)這樣Types: deb URIs: http://archive.ubuntu.com/ubuntu/ Suites: noble noble-updates noble-backports Components: main restricted universe multiverse Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg對(duì)比老格式字段名的映射關(guān)系是deb對(duì)應(yīng)TypesURL 對(duì)應(yīng)URIs代號(hào)對(duì)應(yīng)Suites組件對(duì)應(yīng)Components。新增了Signed-By作為必填項(xiàng)這是最大的變化——官方源現(xiàn)在也顯式聲明用哪把密鑰了。字段名不區(qū)分大小寫Types和types都行但社區(qū)慣例是首字母大寫。值里的多個(gè)條目用空格分隔這點(diǎn)和老格式一致??s進(jìn)無(wú)關(guān)緊要但同一段落內(nèi)不能有空行空行意味著一個(gè)新的源塊開(kāi)始。一個(gè).sources文件里可以寫多個(gè)塊用空行分隔。這就是為什么新版把三個(gè)口袋塞進(jìn)一個(gè)塊里寫Suites: noble noble-updates noble-backports而不是像老格式那樣寫三行。簡(jiǎn)潔是簡(jiǎn)潔了但出問(wèn)題時(shí)定位稍微麻煩一點(diǎn)因?yàn)橐粋€(gè)塊里的任何一項(xiàng)出錯(cuò)都會(huì)讓整個(gè)塊失效。3.2 Signed-By 與密鑰路徑的正確姿勢(shì)Signed-By的值可以是一個(gè)文件路徑也可以直接內(nèi)嵌公鑰內(nèi)容還可以是多個(gè)路徑用空格分隔。實(shí)踐中都是寫路徑。這里有個(gè)容易搞混的點(diǎn)Ubuntu 系統(tǒng)自帶的密鑰在/usr/share/keyrings/下比如ubuntu-archive-keyring.gpg管官方源ubuntu-pro-*-keyring.gpg管 Pro 相關(guān)服務(wù)。而你自己用curl下載的第三方公鑰慣例是放到/etc/apt/keyrings/目錄下并且要用gpg --dearmor轉(zhuǎn)成二進(jìn)制格式因?yàn)橹苯酉螺d的往往是 ASCII armor 格式文本可見(jiàn)的-----BEGIN PGP PUBLIC KEY BLOCK-----apt 要的是.gpg二進(jìn)制。sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://example.com/repo-key.asc | sudo gpg --dearmor -o /etc/apt/keyrings/example-archive-keyring.gpg sudo chmod ar /etc/apt/keyrings/example-archive-keyring.gpgchmod ar這一步經(jīng)常被忽略但很關(guān)鍵——apt 以_apt用戶身份做下載校驗(yàn)如果文件權(quán)限是 600 只有 root 能讀apt 會(huì)報(bào)權(quán)限錯(cuò)誤而報(bào)錯(cuò)信息通常只說(shuō)無(wú)法讀取密鑰不會(huì)告訴你具體是權(quán)限問(wèn)題。3.3 兩種格式的共存規(guī)則與遷移方法apt 同時(shí)支持兩種格式掃描順序是/etc/apt/sources.list里的老格式行、/etc/apt/sources.list.d/下的.list文件、以及.sources文件。它們不是互斥的可以共存。但這里有個(gè)大坑如果同一個(gè)倉(cāng)庫(kù)同時(shí)出現(xiàn)在老格式和新格式里apt 不會(huì)去重而是會(huì)把索引下載兩遍apt update輸出里你能看到重復(fù)的Get行。更嚴(yán)重的情況是版本沖突比如兩個(gè)塊聲明了同一組件的不同 URLapt 會(huì)優(yōu)先使用列出來(lái)的第一個(gè)匹配倉(cāng)庫(kù)但下載索引時(shí)兩個(gè)都下浪費(fèi)時(shí)間還可能觸發(fā)哈希校驗(yàn)困惑。遷移的建議是二選一不要混著來(lái)。新裝 24.04 及以上就用.sources把/etc/apt/sources.list里剩余的有效行全部注釋掉實(shí)體文件放在/etc/apt/sources.list.d/下一個(gè)來(lái)源一個(gè)文件文件名用有意義的名字比如ubuntu.sources、docker.sources、nodesource.sources。這樣以后要禁用某個(gè)源直接重命名加.disabled后綴就行比在長(zhǎng)文件里注釋行干凈得多。4. 動(dòng)手實(shí)操?gòu)膫浞莸津?yàn)證的完整流程理論講完來(lái)一遍完整操作。我按 Ubuntu 24.04 的環(huán)境走22.04 和 20.04 的同學(xué)把代號(hào)換成jammy/focal其余步驟類似。4.1 備份與代號(hào)確認(rèn)永遠(yuǎn)先備份。改壞了還能回滾這是唯一能讓你放心折騰的保障。sudo cp -a /etc/apt/sources.list /etc/apt/sources.list.bak.$(date %F) sudo cp -a /etc/apt/sources.list.d /etc/apt/sources.list.d.bak.$(date %F)cp -a保留權(quán)限和時(shí)間戳比cp -r更適合備份系統(tǒng)文件。日期后綴方便你以后有多份備份時(shí)區(qū)分。然后確認(rèn)代號(hào)和架構(gòu). /etc/os-release echo 代號(hào): $VERSION_CODENAME dpkg --print-architecture輸出應(yīng)該是noble和amd64或你的實(shí)際平臺(tái)。4.2 寫一份可用的源配置以 Ubuntu 24.04 為例創(chuàng)建/etc/apt/sources.list.d/ubuntu.sourcesTypes: deb URIs: http://mirrors.example.edu.cn/ubuntu/ Suites: noble noble-updates noble-security Components: main restricted universe multiverse Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg把mirrors.example.edu.cn替換成你實(shí)際要用的鏡像站域名。選擇鏡像站有幾個(gè)考量一是看它是否同步了你要用的代號(hào)老版本系統(tǒng)尤其要確認(rèn)鏡像站還保留著舊代號(hào)的目錄二是看它的更新頻率公告里會(huì)寫同步周期三是看你所在網(wǎng)絡(luò)到它的鏈路質(zhì)量這個(gè)只能實(shí)測(cè)。同時(shí)把老文件清干凈避免重復(fù)sudo sed -i s/^deb /#deb / /etc/apt/sources.list或者干脆清空內(nèi)容只留一行注釋說(shuō)明去哪看配置。我個(gè)人習(xí)慣是清空文件內(nèi)容加一行注釋指向新文件這樣以后別人接手也看得懂。4.3 更新緩存并驗(yàn)證生效sudo rm -rf /var/lib/apt/lists/* sudo apt update先刪索引再 update是為了避免殘留的舊索引干擾判斷。命令輸出里每個(gè)Get行都會(huì)顯示實(shí)際請(qǐng)求的 URL 和下載速度確認(rèn)域名是你配置的鏡像站速度相比之前有明顯提升就說(shuō)明生效了。如果輸出里還有archive.ubuntu.com的請(qǐng)求說(shuō)明某個(gè)地方還留著生效的官方源回去用grep -r archive.ubuntu.com /etc/apt/找出來(lái)。最后做一個(gè)實(shí)際安裝驗(yàn)證sudo apt install -y --reinstall htop--reinstall對(duì)它已經(jīng)裝過(guò)的包也能跑借它驗(yàn)證下載鏈路通暢。裝完之后可以看看下載速度apt-get download tree ls -lh tree_*.deb rm tree_*.deb4.4 第三方源的添加規(guī)范拿 Docker 官方源舉例因?yàn)樗膶懛ê艿湫蛃udo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod ar /etc/apt/keyrings/docker.gpg然后寫/etc/apt/sources.list.d/docker.sourcesTypes: deb URIs: https://download.docker.com/linux/ubuntu Suites: noble Components: stable Architectures: amd64 Signed-By: /etc/apt/keyrings/docker.gpg注意這里Signed-By指向的是你自己下載的密鑰而Architectures限定了只對(duì) amd64 生效。第三方源通常只提供部分架構(gòu)聲明清楚能避免一堆 404。PPA 的處理方式稍有不同它走的是add-apt-repository命令底層會(huì)幫你下載密鑰并生成配置文件。PPA 在 24.04 上生成的也是 deb822 格式。用 PPA 的前提是你已經(jīng)裝了software-properties-common這是個(gè)常見(jiàn)的依賴缺失點(diǎn)。提示每加一個(gè)第三方源之前先想清楚為什么需要它。系統(tǒng)自帶倉(cāng)庫(kù)里的包優(yōu)先用系統(tǒng)倉(cāng)庫(kù)的第三方源越少依賴沖突和更新失敗的概率越低。我維護(hù)的機(jī)器上第三方源通常不超過(guò)三個(gè)。5. 那些年踩過(guò)的坑常見(jiàn)報(bào)錯(cuò)與排查配置改多了報(bào)錯(cuò)就一定遇到。這一節(jié)把高頻問(wèn)題整理清楚。5.1 NO_PUBKEY 與 GPG 簽名校驗(yàn)失敗報(bào)錯(cuò)長(zhǎng)這樣W: GPG error: https://example.com/repo stable InRelease: The following signatures couldnt be verified because the public key is not available: NO_PUBKEY ABCD1234EF567890含義很直白倉(cāng)庫(kù)的InRelease文件帶簽名但 apt 手里沒(méi)有對(duì)應(yīng)的公鑰沒(méi)法驗(yàn)證。解決就是把公鑰裝上。# 從密鑰服務(wù)器拉取需要能訪問(wèn) keyserver sudo gpg --keyserver keyserver.ubuntu.com --recv-keys ABCD1234EF567890 sudo gpg --export ABCD1234EF567890 | sudo gpg --dearmor -o /etc/apt/keyrings/example.gpg # 或者從倉(cāng)庫(kù)站點(diǎn)直接下載 .asc 文件 curl -fsSL https://example.com/repo/key.asc | sudo gpg --dearmor -o /etc/apt/keyrings/example.gpg然后在源的Signed-By里指向這個(gè)文件。順序很重要先裝密鑰再 update否則還是報(bào)錯(cuò)。另一個(gè)相關(guān)報(bào)錯(cuò)是EXPKEYSIG或KEYEXPIRED意思是公鑰過(guò)期了。同樣是重新拉取但注意某些倉(cāng)庫(kù)換了密鑰需要先刪舊密鑰否則新舊兩份都在會(huì)導(dǎo)致簽名匹配混亂。5.2 404 Not Found 與 Hash Sum mismatch404通常意味著 URL 拼出來(lái)指向了不存在的文件原因無(wú)非幾種代號(hào)寫錯(cuò)了比如把noble寫成了nobles、組件名拼錯(cuò)了、鏡像站還沒(méi)同步這個(gè)代號(hào)、或者第三方源不支持你這個(gè)架構(gòu)。排查方法是從報(bào)錯(cuò)里把完整 URL 摳出來(lái)用瀏覽器或curl -I直接訪問(wèn)curl -I http://mirrors.example.edu.cn/ubuntu/dists/noble/InRelease能返回 200 說(shuō)明路徑?jīng)]問(wèn)題問(wèn)題在別處返回 404 就順著往上試試到哪一級(jí)開(kāi)始 404就知道是哪一段寫錯(cuò)了。Hash Sum mismatch更煩人意思是下載下來(lái)的索引文件內(nèi)容和倉(cāng)庫(kù)聲明的校驗(yàn)值不一致。常見(jiàn)原因是中間有緩存服務(wù)器返回了過(guò)期內(nèi)容或者本地/var/lib/apt/lists/里殘留了半截文件。處理辦法sudo rm -rf /var/lib/apt/lists/* sudo apt update如果還報(bào)檢查是否有 HTTP 代理或者換一個(gè)鏡像站試試。極少數(shù)情況是鏡像站同步到一半索引和包體版本不匹配等幾個(gè)小時(shí)同步完就好了。5.3 依賴沖突與版本鎖定當(dāng)你從多個(gè)來(lái)源裝了同一個(gè)包的不同版本apt install會(huì)開(kāi)始報(bào)一堆held broken packages。這時(shí)候需要apt-cache policy查清楚各來(lái)源的版本apt-cache policy libfoo-dev輸出會(huì)列出每個(gè)源提供的版本和當(dāng)前的優(yōu)先級(jí)默認(rèn) 500。要壓制某個(gè)源用 pinning# /etc/apt/preferences.d/no-example Package: * Pin: origin example.com Pin-Priority: 100優(yōu)先級(jí)低于 500 意味著只有當(dāng)沒(méi)有更高優(yōu)先級(jí)的候選時(shí)才用這個(gè)源設(shè)成負(fù)數(shù)等于禁用設(shè)成 1001 則是強(qiáng)制降級(jí)安裝。這個(gè)機(jī)制在需要固定某個(gè)包版本時(shí)很有用比如生產(chǎn)環(huán)境要卡住某個(gè)庫(kù)不讓它升級(jí)。5.4 常見(jiàn)問(wèn)題速查表報(bào)錯(cuò)關(guān)鍵詞大概率原因處理方式NO_PUBKEY缺少倉(cāng)庫(kù)公鑰下載公鑰到/etc/apt/keyrings/并在Signed-By引用EXPKEYSIG公鑰已過(guò)期重新拉取并替換密鑰文件404 Not Found代號(hào)/組件/路徑寫錯(cuò)或鏡像未同步curl -I逐級(jí)驗(yàn)證 URLHash Sum mismatch本地索引殘留或中間緩存過(guò)期清空/var/lib/apt/lists/后重試Release file expired鏡像站同步滯后換鏡像站或臨時(shí)切回官方源Could not resolveDNS 或網(wǎng)絡(luò)問(wèn)題檢查/etc/resolv.conf和連通性Conflicting values新舊格式重復(fù)定義同一倉(cāng)庫(kù)清理重復(fù)的源文件Unable to locate package組件未開(kāi)啟檢查是否包含universe組件6. 組合場(chǎng)景局域網(wǎng)緩存、離線源與自動(dòng)化單機(jī)換源很簡(jiǎn)單但在真實(shí)環(huán)境里你面對(duì)的往往是幾十臺(tái)機(jī)器、無(wú)法直連外網(wǎng)的隔離網(wǎng)絡(luò)、或者需要批量重建的 CI 環(huán)境。這一節(jié)講三種實(shí)際用得上的組合方案。6.1 局域網(wǎng)統(tǒng)一緩存一臺(tái)機(jī)器當(dāng)二道販子思路是找一臺(tái)能上網(wǎng)的機(jī)器在上面跑一個(gè)緩存服務(wù)其他機(jī)器把源指向它。這樣多個(gè)機(jī)器重復(fù)下載同一個(gè)包時(shí)實(shí)際只從上游拉一次。安裝很簡(jiǎn)單sudo apt install -y apt-cacher-ng默認(rèn)監(jiān)聽(tīng) 3142 端口。其他機(jī)器的源配置改成指向這臺(tái)機(jī)器的 IPTypes: deb URIs: http://192.168.1.10:3142/mirrors.example.edu.cn/ubuntu/ Suites: noble noble-updates noble-security Components: main restricted universe multiverse Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg注意 URL 的構(gòu)造方式http://緩存機(jī)IP:3142/后面直接接原始倉(cāng)庫(kù)域名和路徑中間的http://要去掉。這個(gè)寫法第一次看到容易寫錯(cuò)多試兩次就記住了。緩存會(huì)積累在/var/cache/apt-cacher-ng/長(zhǎng)期跑下來(lái)可能占幾十 GB需要配個(gè)清理策略比如在/etc/apt-cacher-ng/acng.conf里調(diào)整ExThreshold和ExTreshold相關(guān)參數(shù)或者定期手工清理。注意緩存機(jī)對(duì)上游是明文 HTTP內(nèi)網(wǎng)環(huán)境安全性可控但如果內(nèi)網(wǎng)本身不可信應(yīng)該考慮搭配簽名校驗(yàn)——好在 apt 本身就會(huì)校驗(yàn)包簽名緩存機(jī)即使被篡改也無(wú)法偽造通過(guò)校驗(yàn)的包這一層是松不了的。這也是 apt 這個(gè)設(shè)計(jì)比單純的文件服務(wù)器更穩(wěn)的地方。6.2 內(nèi)網(wǎng)隔離環(huán)境的離線源完全不能出網(wǎng)的機(jī)器做法是找一臺(tái)同版本、能出網(wǎng)的機(jī)器把包下全了搬進(jìn)去。# 在能出網(wǎng)的機(jī)器上下載指定包的完整依賴樹(shù) apt-get install --download-only -y nginx # 或者下載某個(gè)包的完整閉包 apt-get download $(apt-rdepends nginx | grep -v ^ | sed s/debconf-2.0/debconf/g)--download-only把.deb放在/var/cache/apt/archives/里然后拷貝到目標(biāo)機(jī)器對(duì)應(yīng)的目錄apt install時(shí)它會(huì)優(yōu)先用本地已有的包。如果目標(biāo)機(jī)器數(shù)量多更規(guī)范的做法是用dpkg-scanpackages建一個(gè)本地倉(cāng)庫(kù)cd /srv/offline-repo dpkg-scanpackages . /dev/null | gzip -9c Packages.gz然后在客戶端源里指向本地路徑Types: deb URIs: file:/srv/offline-repo Suites: ./ Components: Signed-By: /etc/apt/keyrings/local.gpg Trusted: yes離線源通常自己簽不出來(lái)所以會(huì)用到Trusted: yes。這在內(nèi)網(wǎng)物理隔離的場(chǎng)景下是可接受的但一定要清楚這是拿掉了校驗(yàn)環(huán)節(jié)倉(cāng)庫(kù)目錄的寫權(quán)限必須嚴(yán)格限制。6.3 把源配置交給自動(dòng)化如果你經(jīng)常重建虛擬機(jī)手工改源太慢把它寫進(jìn) cloud-init 的bootcmd或者 Ansible 的 task 里更省事。cloud-init 的寫法#cloud-config apt: primary: - arches: [amd64, arm64] uri: http://mirrors.example.edu.cn/ubuntu/ security: - arches: [amd64, arm64] uri: http://mirrors.example.edu.cn/ubuntu/ sources_list: | Types: deb URIs: http://mirrors.example.edu.cn/ubuntu/ Suites: $RELEASE $RELEASE-updates $RELEASE-security Components: main restricted universe multiverse Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpgcloud-init 內(nèi)部會(huì)處理$RELEASE變量替換比你在模板里手寫代號(hào)更通用。Ansible 那邊用ansible.builtin.deb822_repository模塊參數(shù)化的程度更高還能順手處理密鑰下載和校驗(yàn)。有一個(gè)踩過(guò)的坑值得分享自動(dòng)化腳本里apt update之后一定要檢查返回碼。有時(shí)候鏡像站臨時(shí)不可達(dá)apt update會(huì)返回非零但在某些 shell 配置下被忽略后續(xù)安裝就莫名其妙失敗。用set -e或者顯式判斷|| exit 1都能避免這個(gè)隱蔽問(wèn)題。另外批量環(huán)境里鏡像站的選擇要考慮容量。所有人都指向同一個(gè)鏡像站時(shí)如果鏡像站的帶寬有限反而會(huì)互相擠。有條件的話在局域網(wǎng)部署 6.1 說(shuō)的緩存層讓所有機(jī)器走本地緩存對(duì)外只保持一個(gè)出口這是最穩(wěn)的結(jié)構(gòu)。我個(gè)人的習(xí)慣是每臺(tái)新機(jī)器裝完之后第一件事就是確認(rèn)源的代號(hào)和四組件是否齊全第二件事是跑一次apt update看輸出里有沒(méi)有異常源。這兩步花不了一分鐘但能省掉后面很多莫名其妙的排查時(shí)間。踩過(guò)幾次代號(hào)寫成舊版本導(dǎo)致裝了老庫(kù)然后整個(gè)編譯環(huán)境崩掉的坑之后我寧可每次多看一眼。