Ubuntu upgrade lỗi giữa chừng: cách khôi phục
Upgrade Ubuntu 24.04 lên 26.04 bị dừng? Bạn sẽ biết cách nối lại screen, sửa dpkg, xử lý apt source lệch phiên bản và lúc nào nên restore snapshot.
Nâng cấp Ubuntu thất bại giữa chừng: đọc triệu chứng trước
Một lần nâng cấp bản phát hành Ubuntu từ 24.04 lên 26.04 bị lỗi sẽ để server ở một trong bốn trạng thái, và mỗi trạng thái có cách xử lý khác nhau. Trình nâng cấp có thể vẫn đang chạy trong một screen session mà bạn đã mất kết nối. dpkg có thể đã bị kill khi một package đang ở trạng thái cấu hình dở, khiến apt từ chối mọi lệnh. Các apt source có thể đã ghi 26.04 trong khi những package đã cài vẫn là 24.04. Hoặc server có thể không boot được nữa. Hãy xác định bạn đang gặp trạng thái nào trước khi nhập bất kỳ lệnh nào, vì cách xử lý một trạng thái có thể khiến trạng thái khác nghiêm trọng hơn.
Một quy tắc áp dụng cho cả bốn trạng thái: không reboot cho đến khi biết dpkg đang ở trạng thái nào. Reboot giữa lúc package đang được thay thế có thể biến một lần gián đoạn dpkg còn xử lý được thành trường hợp không boot được ở gần cuối hướng dẫn này. Đồng thời, không khởi động process apt hoặc dpkg thứ hai khi process đầu tiên có thể vẫn còn chạy, vì hai process cùng ghi vào package database có thể làm database bị corrupt.
Hướng dẫn này giả định bạn đã làm theo hướng dẫn nâng cấp từ 24.04 lên 26.04 và đã tạo snapshot trước khi bắt đầu. Nếu chưa tạo snapshot, hãy đọc phần boot với lưu ý đó, vì snapshot là cách xử lý trường hợp xấu nhất.
Bản nâng cấp vẫn đang chạy?
Nhiều bản nâng cấp được báo là thất bại thực ra vẫn đang chạy. Phiên SSH bị ngắt, terminal trống, nhưng trình nâng cấp vẫn tiếp tục mà không cần bạn.
do-release-upgrade được thiết kế cho trường hợp này. Khi chạy với giao diện văn bản, như trên server, nó tự chạy bên trong một phiên GNU screen. Vì vậy, bản nâng cấp vẫn tiếp tục dù terminal khởi chạy nó bị mất. Ngoài ra, khi phát hiện được khởi chạy từ phiên SSH, nó đề nghị mở một sshd thứ hai trên cổng khác (mặc định là 1022). Nhờ đó, bạn vẫn có thể đăng nhập nếu SSH daemon chính bị lỗi trong lúc thay thế package. Cả hai thông tin này đều quan trọng lúc này.
Kết nối lại qua SSH và tìm phiên screen. Trình nâng cấp chạy dưới sudo, nên phiên screen của nó thuộc về root. Lệnh screen -ls chạy bằng user hiện tại sẽ không liệt kê được phiên đó.
sudo screen -lsscreen -ls đánh dấu mỗi phiên là attached hoặc detached. Nếu có một phiên được liệt kê, hãy attach lại vào phiên đó. Nếu phiên vẫn được đánh dấu là attached vì kết nối SSH đã mất chưa giải phóng phiên, -d sẽ detach attachment cũ đó trước.
sudo screen -d -rNếu có nhiều hơn một phiên được liệt kê, đặt tên phiên từ screen -ls sau -r. Chạy lại sudo do-release-upgrade cũng được: trình nâng cấp sẽ kiểm tra phiên screen hiện có của chính nó và attach lại thay vì bắt đầu một lần chạy mới. Dù dùng cách nào, bạn sẽ quay lại bản nâng cấp đang chạy. Thông thường, nó đang chờ bạn trả lời prompt về file cấu hình đã thay đổi hoặc việc restart service. Hãy trả lời và để quá trình hoàn tất.
Nếu bạn bắt đầu nâng cấp bên trong tmux, như hướng dẫn nâng cấp khuyến nghị, hãy attach lại tmux trước bằng tmux attach. Phiên screen đang chạy bên trong pane tmux đó, nên bạn sẽ thấy trực tiếp bản nâng cấp. Nếu pane chỉ còn prompt shell, trình nâng cấp không còn chạy ở đó và sudo screen -ls là bước kiểm tra tiếp theo.
Nếu cổng SSH chính từ chối kết nối, hãy thử cổng dự phòng: ssh -p 1022 user@host. Daemon đó chỉ tồn tại trong thời gian nâng cấp. Vì vậy, nếu không thể đăng nhập qua cả hai cổng, hãy dùng console của nhà cung cấp. Từ console, sudo ss -ltnp cho biết các cổng mà một tiến trình sshd đang listen, còn sudo ufw status cho biết firewall có cho phép cổng dự phòng đi qua hay không.
Khi không có phiên screen và console cũng không hiển thị prompt đang chờ, bản nâng cấp thực sự đã dừng. Hãy xác nhận không có tiến trình nào còn đang thao tác với package database trước khi can thiệp:
ps -eo pid,etime,cmd | grep -iE '[u]pgrade|[d]pkg|[a]pt'Kết quả trống nghĩa là dpkg đang idle và bạn có thể chuyển sang sửa lỗi. Nếu có tiến trình dpkg hoặc apt chạy trong thời gian dài nhưng không có phiên screen để attach lại, tiến trình đó đã bị treo. Hãy chờ vài phút, kiểm tra console xem có câu hỏi debconf nào chưa được trả lời không, rồi chỉ kill tiến trình sau đó. Không bao giờ xóa các lock file bên dưới /var/lib/dpkg/ hoặc /var/lib/apt/lists/ khi vẫn có tiến trình đang giữ chúng. Lock là cơ chế duy nhất ngăn 2 tiến trình ghi đồng thời làm hỏng package database.
dpkg bị gián đoạn và apt từ chối chạy
Khi dpkg bị kill giữa lúc giải nén một package và chạy configuration script của package đó, nó ghi lại trạng thái chưa hoàn tất trong /var/lib/dpkg/status. Mọi lệnh apt chạy sau đó đều đọc trạng thái này rồi dừng, vì apt không thể tiếp tục trên database còn công việc chưa hoàn tất. Dù apt hiển thị thông báo từ chối nào, bước đầu tiên vẫn giống nhau.
sudo dpkg --configure -aLệnh này hoàn tất bước cấu hình cho mọi package đã được giải nén nhưng chưa được cấu hình. Nó chạy maintainer script theo thứ tự dependency. Trên một system đang nâng cấp dở, lệnh có thể mất nhiều thời gian, vì vậy hãy để nó chạy đến khi kết thúc. Nếu lệnh dừng ở một package, nó sẽ in tên package và script bị lỗi. Hãy ghi lại tên đó. Đây là package đã làm quá trình nâng cấp dừng lại. Phần tiếp theo hướng dẫn cách đọc lỗi của package này.
Sau đó để apt sửa các dependency còn thiếu do lần gián đoạn để lại, vì một số package đã được nâng cấp nhưng các package mà chúng phụ thuộc vào thì chưa.
sudo apt --fix-broken installTiếp theo, hoàn tất quá trình nâng cấp mà upgrader đang thực hiện dở:
sudo apt update
sudo apt full-upgradeDùng full-upgrade thay cho upgrade, vì release upgrade có thể gỡ package còn upgrade thông thường từ chối gỡ bất kỳ package nào. Đọc phần tóm tắt mà apt in ra trước khi xác nhận. Một danh sách ngắn các package sẽ bị gỡ là bình thường. Nếu danh sách đó gỡ ubuntu-server, systemd, openssh-server hoặc kernel package của bạn thì không bình thường. Hãy chọn no và tìm hiểu lý do apt muốn gỡ chúng trước khi tiếp tục.
Kiểm tra kết quả trước khi làm bất kỳ việc gì khác:
sudo dpkg --audit
sudo apt-get checkdpkg --audit liệt kê mọi package vẫn ở trạng thái broken, còn apt-get check báo cáo các dependency chưa được đáp ứng. Cả hai lệnh đều phải không in gì. Nếu đúng như vậy, chạy sudo apt autoremove để gỡ các package 24.04 không còn package nào phụ thuộc, sau đó xác nhận release bằng cat /etc/os-release, rồi chạy sudo update-initramfs -u -k all và sudo update-grub, chỉ sau đó mới reboot.
Cách đọc /var/log/dist-upgrade để tìm package đã làm quá trình nâng cấp dừng lại
Upgrader ghi toàn bộ thông tin vào /var/log/dist-upgrade/. Nếu bạn đã chạy nó nhiều hơn một lần, nó chuyển log của lần chạy trước vào một subdirectory có tên là timestamp. Vì vậy, trước tiên hãy kiểm tra ls -la /var/log/dist-upgrade/ và đọc directory tương ứng với lần chạy bị lỗi.
main.log là nhật ký riêng của upgrader. Nó ghi lại run đang ở phase nào và upgrader đã quyết định xử lý các source ra sao. Nếu chính upgrader bị crash, Python traceback cũng nằm ở đây. Hãy đọc từ cuối file: các dòng cuối cho biết nó đang ở phase nào khi dừng. Nếu có traceback ở đó, lỗi nằm ở tool chứ không phải package.
apt.log chứa phần giải thích của dependency resolver. File này rất dài và quan trọng khi apt từ chối tính toán toàn bộ quá trình nâng cấp, tức là lỗi xảy ra trước khi bất kỳ package nào được xử lý. Nếu quá trình nâng cấp đã đi đến bước cài package, bạn thường có thể bỏ qua file này.
apt-term.log là file cần xem khi một package bị lỗi. Nó ghi lại output trên terminal của dpkg trong quá trình nâng cấp, tức là cùng nội dung đã chạy qua màn hình. Package cuối cùng được nhắc đến trước phần kết thúc là package đang được xử lý khi quá trình nâng cấp dừng lại. Nếu maintainer script bị lỗi, thông báo lỗi của dpkg nằm ngay tại đó, cùng với lỗi riêng của script ở các dòng ngay phía trên.
sudo tail -n 60 /var/log/dist-upgrade/apt-term.log
sudo grep -in 'error' /var/log/dist-upgrade/apt-term.log | tailĐối chiếu với /var/log/dpkg.log. File này ghi lại mọi thay đổi trạng thái do dpkg thực hiện, kèm timestamp. tail -n 30 /var/log/dpkg.log cho biết package cuối cùng dpkg xử lý và dpkg đang làm gì với package đó. Đây là cùng một kết quả nhưng từ nguồn thứ hai.
Phần lớn lỗi ở giai đoạn này trên server xuất phát từ một vài nguyên nhân. Một service mà package restart trong postinst không khởi động được vì bạn đã tùy chỉnh file cấu hình. systemctl status cùng với journalctl -xeu trên service đó sẽ chỉ ra dòng cấu hình mà service không chấp nhận. Disk có thể đã đầy, thường là /boot do các kernel cũ hoặc /var do apt package cache. df -h / /boot /var sẽ hiển thị tình trạng này. sudo apt clean xóa cache ngay cả khi apt đang bị kẹt. Trường hợp disk báo đầy nhưng du cho biết vẫn còn chỗ trống có cách xử lý riêng. Một package từ third-party repository mà upgrader đã disable có thể phụ thuộc vào một library không còn được release 26.04 cung cấp. Một package bị hold (apt-mark showhold) có thể đã ngăn dependency được nâng cấp. Hãy sửa nguyên nhân, rồi chạy lại sudo dpkg --configure -a. Lệnh này sẽ tiếp tục từ vị trí đã dừng.
Nếu một package vẫn không thể configure dù bạn đã thử mọi cách, và không có thành phần quan trọng nào phụ thuộc vào nó, hãy gỡ package đó rồi cài lại sau khi upgrade hoàn tất:
sudo dpkg --remove --force-remove-reinstreq package-name
sudo dpkg --configure -aChỉ dùng lệnh này với package mà bạn xác định và giải thích được lý do cần gỡ. Không dùng với library hoặc bất kỳ package nào trong chuỗi dependency của ubuntu-server, vì forced removal bỏ qua các bước kiểm tra vốn cho biết những thành phần nào khác sẽ bị hỏng.
Các source đã chuyển sang release mới nhưng package thì chưa
Trình nâng cấp sửa apt source ngay từ đầu, trước khi tải package. Nếu quá trình bị gián đoạn sau bước đó, source sẽ ghi 26.04 trong khi các package đã cài chỉ là một tập hợp lẫn lộn. Trạng thái này khiến các công cụ bị nhầm, và đó là lý do do-release-upgrade có thể hiện yêu cầu rằng không có release mới.
Hãy so sánh 2 nơi mô tả release hiện tại của hệ thống. /etc/apt/sources.list.d/ubuntu.sources là file source dạng deb822 được giới thiệu từ 24.04, trong đó các dòng Suites: chứa codename của release. /etc/os-release được package base-files ghi và cho biết release nào thực sự đang được cài.
grep -E '^(Suites|Components):' /etc/apt/sources.list.d/ubuntu.sources
grep -E '^(VERSION_ID|VERSION_CODENAME)=' /etc/os-release
apt-cache policy base-filesCó 3 tổ hợp cần chú ý. Source vẫn ghi 24.04 (codename noble) và os-release cũng vẫn ghi 24.04: quá trình nâng cấp chưa vượt qua bước kiểm tra, bạn có thể chạy lại sudo do-release-upgrade sau khi đọc main.log để biết lý do nó dừng. Source ghi codename của 26.04 nhưng os-release vẫn ghi 24.04: quá trình thay thế package đã bắt đầu nhưng bị gián đoạn, và bước sửa dpkg trong phần trước, kết thúc bằng apt full-upgrade, là cách để hoàn tất. Source ghi 26.04 và os-release cũng ghi 26.04: base-files là một trong các package đã được nâng cấp, nên hệ thống hiện tự nhận là 26.04 dù phần lớn package vẫn chưa được nâng cấp.
Tổ hợp cuối cùng là trường hợp dễ mắc bẫy. do-release-upgrade xác định release hiện tại từ cùng thông tin mà os-release chứa. Nếu thông tin đó đã ghi 26.04, công cụ sẽ tìm release mới hơn 26.04, không tìm thấy gì và báo rằng không có release mới. Công cụ đang trả lời câu hỏi dựa trên os-release, nhưng os-release lại sai. Đừng tiếp tục dùng trình nâng cấp; hãy hoàn tất bằng apt: sudo apt update, sau đó chạy sudo apt full-upgrade để nâng cấp mọi package vẫn ở phiên bản 24.04, rồi chạy sudo apt autoremove. Các lý do khác khiến do-release-upgrade báo không có release mới, chẳng hạn prompt LTS chờ bản point release đầu tiên, cũng cần được loại trừ nếu os-release vẫn ghi 24.04.
Trình nâng cấp cũng vô hiệu hóa các source bên thứ ba trong /etc/apt/sources.list.d/ và giữ một bản backup của từng file đã sửa, bằng cách thêm suffix vào tên gốc. Chạy ls -la /etc/apt/sources.list.d/ và diff cho từng file gốc và bản backup tương ứng để xem chính xác nó đã thay đổi gì. Giữ các entry bên thứ ba ở trạng thái vô hiệu hóa cho đến khi các package Ubuntu nhất quán, sau đó chỉ bật lại từng entry khi đã xác nhận vendor phát hành package cho 26.04. Nếu apt update báo một source được cấu hình nhiều lần, một entry sources.list cũ và entry ubuntu.sources mới đang mô tả cùng một suite; lỗi source trùng trong deb822 giải thích cần xóa entry nào.
Máy chủ không khởi động sau khi upgrade
Reboot là lúc một lần upgrade chưa hoàn tất bắt đầu gây hậu quả. Trên VPS, các nguyên nhân thường gặp là kernel được cài nhưng không có initramfs, cấu hình GRUB chưa được tạo lại, một package bị bỏ dở ở trạng thái cấu hình chưa hoàn tất khiến một boot-time unit phụ thuộc vào nó, hoặc disk bị đầy khi dpkg đang ghi dữ liệu.
Mở console của provider trước khi làm bất kỳ việc gì khác. Console cho biết quá trình boot dừng ở đâu: menu GRUB, kernel panic, filesystem check đang chờ câu trả lời, hoặc systemd emergency shell yêu cầu root password. Chỉ một thông tin này cũng quyết định bước tiếp theo.
Nếu GRUB xuất hiện, hãy boot kernel 24.04 trước đó từ submenu advanced options. Kernel cũ thường vẫn được giữ lại cho đến khi autoremove chạy. Sau khi hệ thống đã lên bằng kernel cũ, chạy sudo dpkg --configure -a và thực hiện phần repair còn lại trong section ở trên, rồi chạy sudo update-initramfs -u -k all và sudo update-grub trước khi thử lại kernel mới. Khôi phục VPS không boot được sau khi update kernel trình bày chi tiết phần GRUB và initramfs.
Nếu bạn vào emergency shell, root filesystem thường được mount ở chế độ read-only. Remount filesystem rồi chạy quy trình repair tương tự:
mount -o remount,rw /
dpkg --configure -aNếu hệ thống hoàn toàn không vào được shell, hãy boot rescue image của provider, mount disk của VPS và repair từ một chroot. Tìm root partition bằng lsblk thay vì đoán tên partition.
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
rebootTrước khi dành cả giờ trong chroot đó, hãy cân nhắc snapshot. Bạn đã tạo snapshot trước khi bắt đầu, và việc restore thường chỉ mất vài phút trên hầu hết provider. Sau đó bạn chạy lại upgrade, thường mất chưa đến một giờ trên VPS, và lần này đã biết trước cần sửa package nào. Restore là phương án nhanh hơn khi có bất kỳ điều nào sau đây: bạn không truy cập được console hoặc rescue image, bạn không xác định được package khiến upgrade dừng lại, có nhiều hơn một package bị kẹt, hoặc máy chủ đang chạy dịch vụ mà người khác đang chờ. Tự patch là phương án nhanh hơn chỉ khi bạn biết chính xác một lỗi đã xảy ra và cách sửa chỉ cần một command.
Copy /var/log/dist-upgrade/ ra khỏi máy chủ trước khi restore, dùng rescue image nếu đó là cách duy nhất để truy cập. Restore sẽ xóa các log đó, và lần thử thứ hai sẽ thất bại đúng theo cách cũ nếu bạn chưa biết nguyên nhân lần đầu. Snapshot có thể và không thể khôi phục những gì đáng đọc trước khi bạn dựa vào snapshot. Snapshot đưa toàn bộ disk về trạng thái trước đó, bao gồm mọi dữ liệu được ghi kể từ lúc tạo snapshot. Điều này phù hợp khi đang upgrade nhưng không phù hợp nếu đã qua một tuần.
Ba dấu hiệu cho thấy nên rebuild hệ thống
Một số lần upgrade không đáng để cứu. Khôi phục snapshot rồi chạy lại thường không tốn nhiều công sức. Nhưng nếu lỗi bắt nguồn từ trạng thái của chính máy chủ, lần chạy thứ hai cũng sẽ fail. Khi đó, cách xử lý đúng là dùng image 26.04 mới và khôi phục dữ liệu từ backup. Có ba dấu hiệu cho thấy bạn đang ở tình huống này.
Thứ nhất, database của dpkg đã bị hỏng. Nếu dpkg --audit hoặc apt-get check không thể đọc /var/lib/dpkg/status, thay vì chỉ báo các package bị hỏng bên trong database, thì record về những gì đã được cài đặt đã mất. Ubuntu lưu các bản sao hằng ngày trong /var/backups/ (ls -la /var/backups/dpkg.status*), và đôi khi việc thay bản sao mới nhất còn tốt vào đúng vị trí có thể khắc phục được. Nhưng khi bản sao đó không còn khớp với những gì thực sự có trên disk, bạn đang phải đoán. Mọi lần chạy apt sau đó đều tiếp tục dựa trên phỏng đoán này.
Thứ hai, chính các tool cần để sửa hệ thống cũng đã bị hỏng. Khi apt hoặc dpkg không thể start vì một shared library đã bị xóa hoặc bị thay thế dở dang, hoặc systemd không thể start các unit vì package của chính nó đang được configure dở dang, hệ thống không còn package manager hoạt động để sửa package manager nữa. ldd /usr/bin/apt cho biết các library của apt có còn đầy đủ hay không. Đôi khi bạn có thể bootstrap hệ thống từ chroot trong rescue image. Nhưng cách này thường tốn nhiều thời gian hơn rebuild.
Thứ ba, danh sách package bị hỏng không giảm xuống. Nếu bạn đã chạy dpkg --configure -a và apt --fix-broken install theo vòng lặp hơn một giờ, nhưng mỗi lần chạy lại phát hiện thêm một package mới thay vì xử lý package trước đó, thì hệ thống đã mang các vấn đề vào quá trình upgrade và các vấn đề đó không phải do upgrade tạo ra: các file dưới /usr bị chỉnh sửa thủ công, package bị pin hoặc hold, một third-party repository đã thay thế các core library, hoặc một lần upgrade trước đó chưa bao giờ hoàn tất. Image mới không có các vấn đề này. Khôi phục dữ liệu lên image mới cũng tốn ít thời gian hơn việc truy tìm nguyên nhân.
Rebuild chỉ nhanh và đơn giản khi dữ liệu nằm ở nơi khác, không phải trên máy chủ. Đó là điểm khác nhau giữa snapshot và backup, cũng là lý do hướng dẫn upgrade yêu cầu bạn chuẩn bị cả hai.
FAQ
Tôi có thể chỉ chạy lại do-release-upgrade sau khi tiến trình bị gián đoạn không?
Có. Đây là bước đầu tiên nên thử. Nếu upgrader vẫn đang chạy trong screen session, chạy lại lệnh sẽ kết nối lại vào session đó. Nếu không, trước tiên chạy sudo dpkg --configure -a và sudo apt --fix-broken install, rồi khởi động lại upgrader. Công cụ sẽ đọc lại trạng thái hiện tại và tiếp tục. Trường hợp duy nhất không thể xử lý là khi /etc/os-release đã hiển thị 26.04, vì lúc đó công cụ cho rằng quá trình upgrade đã hoàn tất; hãy hoàn tất bằng sudo apt full-upgrade.
Vì sao do-release-upgrade báo không có release mới sau khi quá trình upgrade thất bại?
Vì base-files, package ghi vào /etc/os-release, là một trong các package đã được upgrade trước khi bị gián đoạn. Công cụ hiện đọc file đó, kết luận rằng bạn đang chạy 26.04 và không tìm thấy release mới hơn để cung cấp. So sánh grep VERSION_ID /etc/os-release với grep Suites /etc/apt/sources.list.d/ubuntu.sources, rồi hoàn tất bằng sudo apt update && sudo apt full-upgrade.
Có an toàn khi reboot Ubuntu server đang upgrade dở không?
Chưa, cho đến khi sudo dpkg --audit không trả về gì. Reboot khi kernel đã được unpack nhưng chưa được configure, hoặc khi GRUB chưa được regenerate, là nguyên nhân phổ biến nhất khiến một quá trình upgrade có thể sửa trong 10 phút trở thành công việc phải dùng rescue image. Hoàn tất việc sửa lỗi dpkg và apt full-upgrade, chạy update-initramfs -u -k all và update-grub, rồi mới reboot.
Làm cách nào để tìm package đã làm quá trình upgrade dừng lại?
Đọc phần cuối của /var/log/dist-upgrade/apt-term.log. File này chứa terminal output của dpkg. Package cuối cùng được nêu tên trước khi log kết thúc là package đang được xử lý. Nếu maintainer script thất bại, lỗi của script sẽ xuất hiện ngay phía trên thông báo lỗi của dpkg. tail -n 30 /var/log/dpkg.log xác nhận điều này từ nguồn thứ hai. Nếu thay vào đó main.log kết thúc bằng Python traceback, chính upgrader đã bị crash và không có package nào gây lỗi.
Tôi nên restore snapshot hay tiếp tục sửa lỗi?
Hãy restore khi bạn không xác định được package gây lỗi, có hơn một package bị kẹt, không có quyền truy cập console hoặc cần đưa server hoạt động trở lại nhanh. Chỉ tiếp tục sửa khi bạn biết chính xác một lỗi duy nhất và cách sửa chỉ là chạy một command. Copy /var/log/dist-upgrade/ ra khỏi máy trước khi restore, nếu không lần thử thứ hai sẽ thất bại theo đúng cách cũ.