部落格

Linux 內核錯誤 - 無法同步:致命異常

Kernel Panic - Not Syncing: Fatal Exception

當 Linux 內核發生錯誤時,作為作業系統提供者的我們也幾乎會跟著恐慌。這會自動觸發團隊放下手頭上的一切,立刻投入解決問題。如果修復沒有明顯改善,災難就會發生。

當作業系統無法啟動時,剩下的選擇是有限的。因為沒有運行中的系統可以執行指令或運行診斷工具,也沒有可操作的控制台來查看日誌,更不用說能夠進行遠端存取的網路連接了。這時候,舊式的串口控制台就顯得非常有用。在高端伺服器中,通常會有 BMC/IPMI,作為帶外通道,讓你通過串口連接控制系統並與目標機器互動。如果沒有這樣的設備,網路可接入的 IP-VGA 或 DIY 類型的控制台伺服器(帶有多個 RS232 介面)也能達到相同的目的。萬不得已的情況下,你還是得親自到現場,坐在 KVM 顯示器前,希望客戶不會站在旁邊盯著你。

在我遇到的大多數內核錯誤案例中,問題通常與驅動程式有關,尤其是與檔案系統的驅動程式有關。Linux 的根目錄(/)及其子目錄和文件都存放在檔案系統中,這通常是磁碟驅動器中已格式化的分區。當按下電源按鈕時,BIOS/UEFI 被啟動,並根據啟動順序嘗試加載並執行作業系統。為了識別檔案系統,運行中的系統需要加載所需的驅動程式。運行中的系統通常是 initramfs,它足夠小,可以加載到記憶體中,並作為臨時的作業系統運行。

當 initramfs 可以成功執行時,情況就不會那麼糟糕。至少我們仍然可以透過附加或修改 Linux 內核參數來進入各種啟動階段。這個方法類似於 Linux 的運行級別,使我們能夠調查哪一步出現了問題。以下是我用來檢查的幾個步驟:

  1. 驅動程式是否缺失?
  2. 路徑是否正確?
  3. 驅動程式是否已加載?
  4. 硬碟是否被偵測到?
  5. 檔案系統是否被識別?
  6. 資料夾是否已掛載並包含內容?
  7. 環境變數是否有正確的值?
  8. switchroot 是否成功?

我們可以在其他時候詳細探討具體範例,但今天我處理的是一個上述方法無效的情況。


img-01圖片來源:Bigstack CubeCOS
img-02圖片來源:Bigstack CubeCOS

在 initramfs 解壓後不久,內核掛了。它列出了與我在 initramfs 中集成的驅動程式相符的驅動,但並未告訴我為什麼會發生內核錯誤。至少我無法理解其中的原因。

我調整了一些 Linux 啟動參數,例如 initramfs_size、acpi 等,但都沒有成功。接著,我排除了記憶體、硬碟和主機板的問題,因為現有的作業系統仍然能正常運行。幸運的是,透過我們的 CI/CD 管道,我們可以確保最後釋出的固件仍然有效。因此,我檢查了從最後一次已知的穩定版本到今天的變更集差異,但仍然找不到任何明顯的問題。最終,通過打開打包的固件並進行一些比較,我發現內核版本略有不同:kernel-4.18.0-348 與 kernel-4.18.0-373。在構建過程中,我們依賴 rpm 套件管理器,根據提供的軟體庫解決依賴關係並安裝套件。這樣做有多重優點,可以讓我們保持最新的套件,並節省了源代碼庫的空間與維護自己的 rpm 軟體庫的精力。相反,當需要在客戶端站點的已部署系統上應用補丁、熱修復或修正包時,這也讓我們的工作變得更加困難。每一次新的構建都有可能有所不同,在這個案例中,正是這個微小的版本差異導致了在固件安裝過程中發生了內核錯誤。

發現罪魁禍首後,我決定鎖定內核套件,這些對系統至關重要,不能失控。

    sh-4.4# dnf install -y 'dnf-command(versionlock)'
sh-4.4# dnf -y install kernel-core-4.18.0-348.el8.x86_64 kernel-modules-4.18.0-348.el8.x86_64 kernel-headers-4.18.0-348.el8.x86_64 kernel-4.18.0-348.el8.x86_64 kernel-modules-extra-4.18.0-348.el8.x86_64

sh-4.4# dnf list installed | grep kernel | tr -s ' '
kernel.x86_64 4.18.0-348.el8 @baseos
kernel-core.x86_64 4.18.0-348.el8 @baseos
kernel-headers.x86_64 4.18.0-348.el8 @baseos
kernel-modules.x86_64 4.18.0-348.el8 @baseos
kernel-modules-extra.x86_64 4.18.0-348.el8 @baseos

將內核套件加入鎖定清單,使用命令 "dnf versionlock add"

    sh-4.4# dnf versionlock list
Last metadata expiration check: 0:28:02 ago on Thu 21 Apr 2022 06:06:00 PM CEST.
kernel-0:4.18.0-348.el8.*
kernel-core-0:4.18.0-348.el8.*
kernel-modules-0:4.18.0-348.el8.*
kernel-modules-extra-0:4.18.0-348.el8.*
kernel-headers-0:4.18.0-348.el8.*

ship$ sudo dd if=CUBE_2.2.4_20220421-1616_55765df7.img of=/dev/sda bs=4M
1459+1 records in
1459+1 records out
6122962944 bytes (6.1 GB, 5.7 GiB) copied, 505.694 s, 12.1 MB/s

接著是這兩個主要的套件,提供我們守護程序、rados-ng/rados-kv 以及配置文件。

    sh-4.4# dnf -y install nfs-ganesha-ceph.x86_64 nfs-ganesha-rados-grace

現在插入包含我們新構建固件的 USB,並對 NUC 進行重新啟動以從 USB 硬碟啟動,我們成功越過了恐慌點,並順利完成了啟動過程。

img-03

圖片來源:Bigstack CubeCOS