Linux系統診斷小技巧(14):啟停問題之如何修復initrd損壞
題外話
我們先講什麼是 initrd 和需要的工具。
initrd的二意性
ofollow,noindex" target="_blank">initrd 在Linux核心社群有兩種意義。一是核心的一種啟動機制。二是指實現這種機制的一種方法。作為方法的initrd,已經逐步被initramfs 取代 。
三大利器
關於工具的重要性我們就不絮叨了。解決啟停問題,一定要有三大利器的輔助:快照、VNC和錄屏工具。
關於這些工具,請參考[
Linux系統診斷小技巧(13):啟停問題之如何修復grub損壞]( https://yq.aliyun.com/articles/642902) 。
bind掛載
修復啟停時經常需要做chroot操作,即我們要在一個新的根檔案系統內操作。但是,只執行chroot是不夠的。我們還需要掛載/dev、/proc和/sys目錄。這樣才能使用在 chroot 後繼續使用系統資料。
關於bind掛載,同樣請參考[
Linux系統診斷小技巧(13):啟停問題之如何修復grub損壞]( https://yq.aliyun.com/articles/642902) 有關章節。
回到話題
initrd 丟失、損壞或者沒有正常工作,是導致系統啟動失敗比較常見的原因。
如何解決這類問題呢?重新做 initrd 即可。那麼,具體怎麼操作呢?我們討論下。這裡我們主要是圍繞阿里雲的例項問題展開。
現場復現
initrd 丟失相對容易模擬。重啟、及時VNC登入,在 grub 提供可用核心清單介面,按字母 e 就能修改啟動項配置。我們修改 initrd 檔名稱即可。
修改檔名稱
按照終端提示,啟動系統,得到結果如下(但是這個介面很快會被核心堆疊資訊覆蓋,靈活使用錄屏工具是需要的)

initrd缺少驅動
我們可以分別測試缺少主機板驅動、磁碟驅動、顯示卡驅動等多種情形。簡單計,這裡我們只測試 initrd 只包括主機板驅動的情形。
建立這樣的 initramfs ,可以使用下面的命令:
kernel_verion_str=$(uname -r) # chroot情形的話,需要手工設定 dracut -d virtio_pci /boot/initramfs-${kernel_version_str}-issues.img ${kernel_version_str}
如果不是chroot環境,可以直接使用下面的命令
dracut -d virtio_pci /boot/initramfs-$(uname -r)-issues.img $(uname -r)
命令執行示例如下:

驗證下我們作出了 initrd 檔案:

復現現場,現在:

磨刀不誤砍柴工
常見的Linux系統都有救援模式,而且(大多)都是有多個核心可用。所以,一個核心的 initrd 出現問題,並不妨礙我們啟動系統,拿到 shell 。但是,的確存在需要在其他系統上重新做 initrd 的情形。比如,我們運氣不好,碰到的例項,其上的系統只有一個核心。這樣現有系統是啟動不起來了。系統啟動不起來,則拿不到 shell ,拿不到 shell ,則讀寫不了系統盤。
所以,在運氣不好的情況下,我們的問題就變成了兩個:
- 通過什麼方式來處理系統盤。
- 怎麼重新生成 initrd 。
解決第一個問題的方法比較多,這裡我們提供一個使用快照的方式。
如何使用快照?
請參考 Linux系統診斷小技巧(13):啟停問題之如何修復grub損壞 有關章節。
重新制作initrd的其他準備步驟
在正常例項上重新制作 initrd 需要
# 1. 把新建立磁碟作為資料盤掛載到正常例項上。這需要在控制檯操作。 # 2. 掛載目標根檔案系統。 mount /dev/vdb1 /mnt # 3. 把/dev、/proc和/sys掛載到新掛載檔案系統。這樣我們就能夠使用核心匯出的系統資料了。 for d in dev proc sys;do mount --bind /$d /mnt/$d 4. chroot到目標根檔案系統,使之成為當前跟檔案系統。 chroot /mnt
有關上面步驟的執行示例,請參考 Linux系統診斷小技巧(13):啟停問題之如何修復grub損壞 有關章節。
重新制作initrd
各個主流發行版都提供了製作 initrd 檔案的易用工具,我們不贅述。這裡我們以CentOS 7系統為例。重要的是要把需要啟動系統所需的驅動不要忘記了。
對於阿里雲當前的例項,主要是主機板驅動( virtio_pci )和系統盤驅動(virtio_blk):
# 1. 如果要製作initrd檔案的版本號不同於當前執行的核心(比如chroot條件下), # 則需要手工設定核心版本號;否則,直接使用uname -r的結果即可 kernel_verion_str=... # 2. 備份老的檔案 cp -p /boot/initramfs-${kernel_version_str}.img /boot/initramfs-${kernel_version_str}.img.origin # 3. 驗證備份完畢 ls /boot/initramfs-${kernel_version_str}.img* # 4. 刪除老的initrd檔案 rm -f /boot/initramfs-${kernel_version_str}.img # 5. 重新生成initrd dracut --add-drivers "virio_pci virtio_blk" /boot/initramfs-${kernel_version_str}.img ${kernel_version_str}
執行示例如下
這樣我們就重新制作了 initrd 檔案。
終焉
祝探索愉快。