Ubuntu 24.04 升級 26.04 失敗如何修復
升級從 Ubuntu 24.04 到 26.04 中途停止?本文說明如何重新連線 screen、修復 dpkg、處理切換一半的套件來源,並判斷何時應還原快照。
Ubuntu 升級中途失敗:先判斷症狀
從 24.04 升級 Ubuntu 版本至 26.04 失敗後,伺服器會處於四種狀態之一,而每種狀態都有不同的修復方式。升級程式可能仍在你已失去連線的 screen 工作階段中執行。dpkg 可能在套件尚未完成設定時遭到終止,因此 apt 現在拒絕所有命令。apt 來源可能已指定 26.04,但已安裝的套件仍是 24.04。伺服器也可能完全無法開機。在輸入任何內容前,先確認你遇到的是哪一種狀態,因為某一種狀態的修復方式,可能讓另一種狀態更加嚴重。
以下規則適用於這四種狀態。在確認 dpkg 所處的狀態前,不要重新開機。在套件置換過程中重新開機,可能會讓原本可修復的 dpkg 中斷,變成本指南後段所述的無法開機情況。此外,若第一個 apt 或 dpkg 程序可能仍在執行,也不要啟動第二個 apt 或 dpkg 程序,因為兩個程序同時寫入套件資料庫,可能造成資料庫損毀。
本指南假設你已遵循24.04 至 26.04 升級指南,並在開始前建立快照。若沒有建立快照,請閱讀開機區段時將此情況納入考量,因為快照是最嚴重情況的復原方式。
升級程序是否仍在執行?
許多顯示為失敗的升級其實仍在執行。SSH 工作階段中斷、終端機畫面變空白,但升級程式仍在沒有你的情況下繼續執行。
do-release-upgrade 就是為此設計的。以文字介面執行時,也就是伺服器通常使用的模式,do-release-upgrade 會在 GNU screen 工作階段中執行,因此即使啟動升級的終端機遺失,升級仍可繼續。另一方面,當它偵測到自己是從 SSH 工作階段啟動時,會提供在另一個連接埠上啟動第二個 sshd 的選項,預設為 1022。如此一來,即使主要 SSH daemon 在交換套件期間故障,你仍可登入。這兩點現在都很重要。
重新透過 SSH 連線,並尋找 screen 工作階段。升級程式是在 sudo 下執行,因此它的 screen 工作階段屬於 root;以自己的使用者執行一般的 screen -ls 不會列出該工作階段。
sudo screen -lsscreen -ls 會標示每個工作階段目前是已連結或已分離。若列出一個工作階段,請重新連結到該工作階段。如果它仍標示為已連結,表示中斷的 SSH 連線尚未釋放該工作階段,此時 -d 會先分離這個過期的連結。
sudo screen -d -r如果列出多個工作階段,請將 screen -ls 中的工作階段名稱放在 -r 後面。再次執行 sudo do-release-upgrade 也可以:升級程式會檢查自己既有的 screen 工作階段,並重新連結,而不是開始新的升級。無論採用哪種方式,你都會回到正在執行的升級程序,通常會停在變更的設定檔或服務重新啟動提示上。回答提示,讓升級完成。
如果你依照升級指南的建議,在 tmux 中啟動升級,請先使用 tmux attach 重新連結 tmux。screen 工作階段是在該 tmux 窗格中執行,因此你會直接看到升級程序。如果該窗格只有 shell 提示字元,表示升級程式已不再於其中執行,下一步應檢查 sudo screen -ls。
如果主要 SSH 連接埠拒絕連線,請嘗試備援連接埠:ssh -p 1022 user@host。該 daemon 只會在升級期間存在,因此如果兩個連接埠都無法登入,請改用供應商的主控台。在主控台中,sudo ss -ltnp 會顯示 sshd 程序正在監聽哪些連接埠,而 sudo ufw status 可確認防火牆是否允許備援連接埠的流量通過。
如果不存在 screen 工作階段,主控台上也沒有任何等待中的提示,表示升級確實已停止。在處理套件資料庫前,先確認沒有程序仍在使用它:
ps -eo pid,etime,cmd | grep -iE '[u]pgrade|[d]pkg|[a]pt'結果為空表示 dpkg 處於閒置狀態,你可以繼續修復程序。如果有 dpkg 或 apt 程序已經執行很久,且沒有可重新連結的 screen 工作階段,表示它已停止回應。先等待幾分鐘,檢查主控台是否有無人回答的 debconf 問題,之後才終止該程序。當程序仍持有 /var/lib/dpkg/ 或 /var/lib/apt/lists/ 下的鎖定檔時,絕對不要刪除這些檔案。鎖定是防止兩個寫入程序損毀套件資料庫的唯一機制。
dpkg 中斷,apt 拒絕執行
當 dpkg 在解開套件與執行其設定指令碼之間被終止時,會將未完成的狀態記錄在 /var/lib/dpkg/status。之後的每個 apt 指令都會讀取這個狀態並停止,因為 apt 不會在含有未完成工作的資料庫上繼續執行。不論 apt 拒絕執行時顯示什麼訊息,第一步都相同。
sudo dpkg --configure -a這會完成所有已解開但尚未設定之套件的設定步驟。它會依相依性順序執行 maintainer scripts。在升級到一半的系統上,這可能需要很長時間,因此請等待它完成。若它在某個套件上停止,會列印套件名稱與失敗的指令碼。請記下該名稱。這就是導致升級停止的套件,下一節會說明如何讀取其錯誤。
接著讓 apt 修復中斷留下的未滿足相依性,因為部分套件已升級,但其相依套件尚未升級。
sudo apt --fix-broken install接著完成升級程式原本正在執行的升級:
sudo apt update
sudo apt full-upgrade請使用 full-upgrade,不要使用 upgrade,因為版本升級會移除套件,而一般的 upgrade 拒絕移除任何套件。確認前請先閱讀 apt 列印的摘要。移除少量套件屬於正常情況。但如果清單會移除 ubuntu-server、systemd、openssh-server 或 kernel package,則不正常。此時請回答否,先查明 apt 為何要移除這些套件,再繼續操作。
執行其他操作前,先檢查結果:
sudo dpkg --audit
sudo apt-get checkdpkg --audit 會列出所有仍處於損壞狀態的套件,apt-get check 會回報未滿足的相依性。兩者都應不輸出任何內容。確認沒有輸出後,執行 sudo apt autoremove,移除不再有任何套件相依的 24.04 套件;接著使用 cat /etc/os-release 確認版本,再執行 sudo update-initramfs -u -k all 與 sudo update-grub,最後才重新啟動。
如何讀取 /var/log/dist-upgrade,找出導致升級停止的套件
升級程式會將所有內容寫入 /var/log/dist-upgrade/。如果執行過不只一次,它會將先前嘗試的日誌移至以時間戳記命名的子目錄,因此請先檢查 ls -la /var/log/dist-upgrade/,再讀取與失敗執行相符的目錄。
main.log 是升級程式自己的記錄檔。它會記錄執行所處的階段,以及它對套件來源所做的判斷。如果升級程式本身當機,這裡也會包含 Python traceback。請從檔案結尾開始閱讀:最後幾行會指出它停止時所處的階段;如果那裡有 traceback,表示工具本身失敗,而不是套件失敗。
apt.log 包含相依性解析器的判斷過程。內容較詳細;當 apt 根本拒絕計算升級時,這份檔案很重要,因為這表示在任何套件被處理前就已經失敗。如果升級已進行到安裝套件的階段,通常可以略過它。
apt-term.log 是套件失敗時應查看的檔案。它會擷取升級期間 dpkg 的終端輸出,也就是原本會快速捲過畫面的相同內容。檔案結尾前最後提到的套件,就是升級停止時正在處理的套件。如果 maintainer script 失敗,dpkg 對該錯誤的抱怨會出現在這裡,而指令碼自己的錯誤則位於其上方。
sudo tail -n 60 /var/log/dist-upgrade/apt-term.log
sudo grep -in 'error' /var/log/dist-upgrade/apt-term.log | tail請與 /var/log/dpkg.log 交叉比對。該檔案會以時間戳記記錄 dpkg 所做的每項狀態變更。tail -n 30 /var/log/dpkg.log 會顯示 dpkg 最後處理的套件,以及當時對它執行的動作;這是從第二個來源取得的相同答案。
在伺服器上,這個階段的大多數失敗都源自少數幾種原因。套件在 postinst 中重新啟動服務,但服務因為你自訂的設定檔而無法啟動;針對該服務執行 systemctl status 與 journalctl -xeu,即可找出它不接受的設定行。磁碟空間已用盡,最常見的是 /boot 存放舊 kernel,或 /var 存放 apt 的套件快取;df -h / /boot /var 會顯示此情況。即使 apt 卡住,sudo apt clean 仍可清空快取;而 du 顯示未滿,但磁碟回報已滿 則有另外的原因。升級程式停用的第三方套件庫中,可能有套件相依於 26.04 已不再提供的函式庫。被保留的套件(apt-mark showhold)可能阻止相依套件升級。修正原因後,再次執行 sudo dpkg --configure -a。它會從停止的位置繼續。
如果無論如何都無法設定某個套件,而且沒有重要套件相依於它,請在升級完成後移除並重新安裝:
sudo dpkg --remove --force-remove-reinstreq package-name
sudo dpkg --configure -a請只對你能明確指出並說明用途的套件使用此方法。不要對函式庫或 ubuntu-server 相依性鏈中的任何項目使用,因為強制移除會略過原本可告知你哪些項目會損壞的檢查。
來源已切換,但套件尚未切換
升級工具會在下載任何套件前,先改寫 apt 來源。若程序在這之後中斷,來源會標示 26.04,但已安裝的套件仍是混合狀態。這種混合狀態會使工具判斷錯誤,也因此 do-release-upgrade 現在可能會堅稱沒有新版本可用。
請比較兩個用來描述目前版本的位置。/etc/apt/sources.list.d/ubuntu.sources 是 24.04 引入的 deb822 來源檔案,其中的 Suites: 行會記載版本代號。/etc/os-release 由 base-files 套件寫入,用來表示實際安裝的版本。
grep -E '^(Suites|Components):' /etc/apt/sources.list.d/ubuntu.sources
grep -E '^(VERSION_ID|VERSION_CODENAME)=' /etc/os-release
apt-cache policy base-files有 3 種組合需要注意。來源仍標示 24.04(版本代號為 noble),而 os-release 也仍表示 24.04:升級尚未通過檢查。讀取 main.log 了解停止原因後,即可再次執行 sudo do-release-upgrade。來源標示 26.04 的版本代號,而 os-release 仍表示 24.04:套件替換已開始但遭到中斷。上一節所述、最後以 apt full-upgrade 結束的 dpkg 修復程序,就是完成升級的方法。來源標示 26.04,且 os-release 也表示 26.04:base-files 是已成功完成安裝的套件之一。系統現在會將自身標示為 26.04,即使大部分套件仍未完成升級。
最後一種組合最容易造成誤判。do-release-upgrade 會根據與 os-release 相同的資訊判斷目前版本。若該資訊已表示 26.04,工具就會尋找比 26.04 更新的版本。由於找不到更高版本,便會回報沒有新版本可用。工具回答的是關於 os-release 的問題,而 os-release 的內容是錯的。請不要再執行升級工具,改用 apt 完成升級:先執行 sudo apt update,再執行 sudo apt full-upgrade,將所有仍處於 24.04 版本的套件升級,最後執行 sudo apt autoremove。如果 os-release 仍表示 24.04,則仍應排除 do-release-upgrade 回報沒有新版本的其他原因,例如 LTS 提示正在等待第一個 point release。
升級工具也會停用 /etc/apt/sources.list.d/ 下的第三方來源,並為每個變更過的檔案保留備份副本,在原始檔名後加入副檔名。執行 ls -la /etc/apt/sources.list.d/ 和 diff,分別比較每個原始檔案與其備份,以確切查看工具進行了哪些變更。在 Ubuntu 套件狀態一致前,請維持第三方來源停用。之後只有在確認供應商已發布適用於 26.04 的套件後,才逐一重新啟用。如果 apt update 抱怨某個來源被設定多次,表示舊的 sources.list 項目與新的 ubuntu.sources 項目描述相同的套件套件庫;deb822 重複來源錯誤會說明應移除哪一個。
升級後伺服器無法開機
重新開機時,未完成的升級可能會造成問題。在 VPS 上,常見原因包括安裝 kernel 時未建立 initramfs、從未重新產生 GRUB 設定、某個開機時需要的 unit 尚未完成套件設定,或磁碟在 dpkg 寫入資料時已填滿。
先開啟供應商提供的主控台,再進行其他操作。主控台會顯示開機停止的位置:GRUB 選單、kernel panic、等待回應的檔案系統檢查,或要求輸入 root 密碼的 systemd emergency shell。這項觀察結果會決定下一步。
如果出現 GRUB,請從進階選項子選單啟動先前的 24.04 kernel。舊 kernel 通常會一直保留,直到執行 autoremove 為止。系統使用舊 kernel 啟動後,執行 sudo dpkg --configure -a,並依照上一節完成其餘修復;接著執行 sudo update-initramfs -u -k all 和 sudo update-grub,再重新嘗試啟動新 kernel。復原更新 kernel 後無法開機的 VPS 詳細說明了 GRUB 和 initramfs 相關處理方式。
如果進入 emergency shell,root 檔案系統通常會以唯讀模式掛載。重新掛載後,執行相同的修復程序:
mount -o remount,rw /
dpkg --configure -a如果系統完全未進入 shell,請啟動供應商提供的 rescue image,掛載 VPS 磁碟,然後從 chroot 中進行修復。請使用 lsblk 找出 root 分割區,不要自行猜測名稱。
lsblk
mount /dev/<root-partition> /mnt
for d in dev proc sys run; do mount --bind /$d /mnt/$d; done
chroot /mnt /bin/bash
dpkg --configure -a
apt --fix-broken install
update-initramfs -u -k all
update-grub
exit
reboot在 chroot 中花費一小時之前,請先評估是否應改用 snapshot。你在開始前已建立 snapshot,而多數供應商只需幾分鐘即可還原。接著重新執行升級;在 VPS 上通常不到一小時,且這次你已知道要先修復哪個套件。符合以下任一情況時,還原通常是較快的方式:無法取得主控台或 rescue image、無法判斷是哪個套件中斷升級、卡住的套件不只一個,或伺服器上執行著使用者正在等待的服務。只有在你確定是哪一項設定或元件損壞,且修復只需一個指令時,手動修補才會比較快。
還原前,請先將 /var/log/dist-upgrade/ 複製到伺服器外部;如果只能透過 rescue image 存取,就從 rescue image 執行。還原會刪除這些日誌;如果你沒有找出第一次失敗的原因,第二次嘗試很可能會完全以相同方式失敗。snapshot 能還原及不能還原的內容 值得在依賴 snapshot 前閱讀。snapshot 會將整個磁碟回復到建立當時的狀態,包括建立後寫入的所有資料。這在升級進行期間通常沒有問題,但如果已經過了一週,就可能造成資料遺失。
改為重建系統的 3 個跡象
有些升級問題不值得挽救。還原 snapshot 並重新執行升級的成本很低;但如果失敗原因來自伺服器本身的狀態,第二次執行也會失敗。此時,正確的處理方式是使用全新的 26.04 image,並從 backup 還原資料。出現以下 3 個跡象時,表示你已經遇到這種情況。
第一,dpkg 自己的資料庫已損壞。如果 dpkg --audit 或 apt-get check 完全無法讀取 /var/lib/dpkg/status,而不是回報其中包含損壞的套件,表示已安裝套件的記錄已遺失。Ubuntu 會在 /var/backups/(ls -la /var/backups/dpkg.status*)下保留每日副本,將最新的正常副本換回原位有時可以解決問題。但如果該副本與磁碟上的實際內容不一致,你就只能猜測;之後每次執行 apt 都會建立在這個猜測上。
第二,修復系統所需的工具本身已損壞。如果 apt 或 dpkg 因為 shared library 被移除或只完成部分替換而無法啟動,或 systemd 因為自己的套件只完成部分設定而無法啟動 units,系統就沒有可運作的 package manager 可以修復 package manager。ldd /usr/bin/apt 可用來確認 apt 所需的 libraries 是否完整。你有時可以從 rescue image 的 chroot 環境中 bootstrap 修復系統,但通常這需要的時間比重建系統更久。
第三,損壞套件的清單沒有減少。如果你已經在迴圈中執行 dpkg --configure -a 和 apt --fix-broken install 超過 1 小時,而每次執行都發現新的套件,卻無法清除上一個問題,表示這些問題是在升級前就已存在,並非升級造成。例如,/usr 下的檔案曾由人工修改、套件被 pinned 或 held、第三方 repository 替換了核心 libraries,或先前的升級本身就未完成。全新的 image 不會包含這些問題。將資料還原到新系統所需的時間,也比找出這些問題更短。
只有在資料存放於伺服器以外的位置時,重建系統的成本才低。這就是 snapshot 與 backup 的差異,也是升級指南要求同時準備兩者的原因。
FAQ
do-release-upgrade 中斷後,可以直接再次執行嗎?
可以,這是最適合先採取的措施。如果升級程式仍在其 screen 工作階段中執行,再次執行會重新連接到該工作階段。如果升級程式已停止,請先執行 sudo dpkg --configure -a 和 sudo apt --fix-broken install,再重新啟動升級程式。它會重新讀取目前狀態並繼續升級。唯一無法透過此方式處理的情況,是 /etc/os-release 已顯示 26.04,因為升級程式會認為升級已完成;此時請改用 sudo apt full-upgrade 完成作業。
升級失敗後,為什麼 do-release-upgrade 會顯示沒有新的版本?
因為 base-files 是寫入 /etc/os-release 的套件,而它在中斷前已完成升級。工具現在會讀取該檔案,判定系統已是 26.04,因此找不到可提供的新版本。請比較 grep VERSION_ID /etc/os-release 與 grep Suites /etc/apt/sources.list.d/ubuntu.sources,再使用 sudo apt update && sudo apt full-upgrade 完成作業。
Ubuntu server 升級到一半時重新開機是否安全?
在 sudo dpkg --audit 沒有任何輸出前,不安全。核心已解包但尚未設定,或 GRUB 尚未重新產生時重新開機,最常會讓原本 10 分鐘內可修復的升級問題變成需要使用救援映像檔處理的工作。請完成 dpkg 修復與 apt full-upgrade,執行 update-initramfs -u -k all 和 update-grub,之後才能重新開機。
如何找出中止升級的套件?
查看 /var/log/dist-upgrade/apt-term.log 的結尾,其中包含 dpkg 的終端輸出。日誌結束前最後列出的套件,就是當時正在處理的套件;如果維護者指令碼失敗,其錯誤會顯示在 dpkg 錯誤訊息的正上方。tail -n 30 /var/log/dpkg.log 可從第二個來源確認這項判斷。如果 main.log 最後顯示的是 Python traceback,代表升級程式本身當機,並非套件造成問題。
應該還原 snapshot,還是繼續修復?
在無法確認失敗套件、超過一個套件卡住、沒有主控台存取權,或伺服器必須儘快恢復服務時,請還原 snapshot。只有在確定唯一故障點,且修復只需執行一個指令時,才應繼續修復。還原前請將 /var/log/dist-upgrade/ 複製到伺服器外部,否則第二次嘗試會以相同方式失敗。