:跳過重啟實現(xiàn)秒切Linux內(nèi)核的完整指南)
1. 從“重啟”到“秒切內(nèi)核”kexec到底解決了什么痛點我在生產(chǎn)環(huán)境折騰內(nèi)核不是一天兩天了。早些年每次升級內(nèi)核或者調(diào)試一個新的內(nèi)核特性最讓人頭疼的就是“重啟”這兩個字。一臺承載著業(yè)務(wù)的服務(wù)機動不動就要等上幾分鐘甚至十幾分鐘的BIOS自檢、硬件初始化、引導加載程序掃描然后才是內(nèi)核解壓、驅(qū)動加載、服務(wù)拉起。如果是一臺需要快速切換內(nèi)核的測試機倒還好但如果是在線上環(huán)境每多等一秒都意味著業(yè)務(wù)中斷時間被拉長。有沒有一種辦法能跳過那堆冗長的硬件自檢和引導流程直接從當前正在運行的Linux內(nèi)核“跳”到一個新內(nèi)核答案就是題目里這個命令kexec。kexec是Linux內(nèi)核提供的一個機制它的全稱是“Kernel EXecution”直白地說就是讓當前正在運行的內(nèi)核直接加載并切換到一個新內(nèi)核不需要經(jīng)過傳統(tǒng)的重啟流程。底層對應(yīng)一個系統(tǒng)調(diào)用用戶態(tài)配套的工具也叫kexec。它的工作方式相當于你在辦公室里正開著會突然要換一間會議室但你不是走出大樓再從大門進來而是直接推開隔壁會議室的門坐下——省掉了所有重復(fù)的入場流程。這個能力在不少場景下都非常值錢。比如線上內(nèi)核升級后需要驗證新內(nèi)核是否穩(wěn)定你不想經(jīng)歷漫長的停機窗口再比如你在調(diào)試一個會導致系統(tǒng)崩潰的內(nèi)核模塊希望崩潰時能自動跳到一個保留的“急救內(nèi)核”去抓取現(xiàn)場信息這就是后面要聊的kdump機制的基礎(chǔ)。作為Linux運維、內(nèi)核開發(fā)者或者底層系統(tǒng)愛好者掌握kexec幾乎是必修課。這篇內(nèi)容我盡量用實際操作的角度把kexec的原理、用法、坑點和配套場景都講透確保你不僅能看懂還能直接拿去用。2. kexec的兩種核心工作模式與系統(tǒng)級架構(gòu)2.1 加載式的兩階段操作kexec -l 與 kexec -ekexec這個工具的使用邏輯非常清晰一共就兩大步先加載再執(zhí)行。第一步用kexec -l把新內(nèi)核和它的initrd初始根文件系統(tǒng)加載到當前內(nèi)核已經(jīng)預(yù)留好的內(nèi)存區(qū)域中同時把啟動參數(shù)一并準備好。執(zhí)行完這一步新內(nèi)核其實是“待命”狀態(tài)當前系統(tǒng)一切照常運行沒有任何切換發(fā)生。第二步在你準備好的時機執(zhí)行kexec -e當前內(nèi)核會立即停止一切正?;顒又苯犹D(zhuǎn)到之前加載好的新內(nèi)核入口地址。整個切換過程非??焱ǔ>褪且粌擅氲氖赂緵]有傳統(tǒng)重啟那樣的硬件重置和引導流程。這兩個步驟分開設(shè)計是刻意為之。因為加載內(nèi)核是一個相對耗時的過程需要讀取內(nèi)核文件、解壓縮、把各段放入內(nèi)存、組裝啟動參數(shù)而執(zhí)行切換是瞬間動作。把加載和執(zhí)行解耦意味著你可以提前很多時間把新內(nèi)核準備好然后在業(yè)務(wù)低峰期一鍵切換。我在實際使用中經(jīng)常提前幾分鐘把內(nèi)核加載好反復(fù)檢查啟動參數(shù)無誤最后執(zhí)行真正的切換動作。需要注意kexec -l參數(shù)后面可以直接指定內(nèi)核鏡像文件路徑比如/boot/vmlinuz-xxx。同時幾乎都必須配合--initrd指定initrd文件否則新內(nèi)核可能因為缺少必要的驅(qū)動模塊而無法掛載根文件系統(tǒng)。至于啟動參數(shù)可以用--command-line手動指定但如果懶得一個個敲直接用--reuse-cmdline復(fù)用當前內(nèi)核的啟動參數(shù)也是一種省事且穩(wěn)妥的做法。2.2 立即執(zhí)行的kexec系統(tǒng)調(diào)用與一次性的頂層封裝上面說的兩階段是針對kexec工具的使用流程。在系統(tǒng)調(diào)用層面其實還有一個更加猛烈的封裝kexec_load系統(tǒng)調(diào)用可以直接完成從加載到執(zhí)行的整個過程因為加載和執(zhí)行本質(zhì)上是一個連續(xù)的動作。只是內(nèi)核出于安全考慮通常會要求調(diào)用者具備CAP_SYS_BOOT權(quán)限而且大多數(shù)發(fā)行版默認不會把這種“自殺式”調(diào)用暴露給普通用戶。實際日常操作中我們使用kexec工具就足夠了。kexec工具在加載階段會把內(nèi)核鏡像、initrd等數(shù)據(jù)通過kexec_load系統(tǒng)調(diào)用交給內(nèi)核但并不會觸發(fā)立即切換。而kexec -e執(zhí)行的其實是另一個系統(tǒng)調(diào)用reboot(LINUX_REBOOT_CMD_KEXEC)相當于告訴內(nèi)核“我已經(jīng)準備好了你現(xiàn)在可以跳轉(zhuǎn)過去了”。此外如果你的系統(tǒng)使用systemd作為init系統(tǒng)還有一個更省事的入口systemctl kexec。這個命令會讀取systemd預(yù)設(shè)的kexec加載配置通常是/run/systemd/system/kexec.conf或者由systemd-kexec服務(wù)在啟動時配置然后自動完成加載并執(zhí)行切換。對很多場景來說直接用systemctl kexec比手工敲kexec -l加kexec -e要省心很多但前提是你得先把加載信息提前配置好。3. 為什么kexec能跳過重啟深入內(nèi)核的切換機制3.1 跳過固件與引導加載程序的關(guān)鍵設(shè)計傳統(tǒng)啟動的路徑是電源上電 - BIOS/UEFI自檢 - 引導加載程序如GRUB加載內(nèi)核到內(nèi)存 - 內(nèi)核初始化硬件 - 掛載根文件系統(tǒng) - 啟動init進程。這個過程中BIOS/UEFI自檢和引導加載程序的掃描往往是耗時的大頭很多時候不需要每次都完整執(zhí)行。kexec的核心思想是當前內(nèi)核已經(jīng)完成過硬件初始化這些硬件狀態(tài)其實都還活著不需要重新初始化一遍。新內(nèi)核只需要接管這些已經(jīng)被初始化好的硬件即可。所以kexec在加載新內(nèi)核的時候會把新內(nèi)核鏡像放入一段特定的內(nèi)存區(qū)域然后在新內(nèi)核入口點執(zhí)行時它并不會像傳統(tǒng)引導那樣重新掃描總線、重新枚舉設(shè)備而是盡可能繼承當前內(nèi)核已經(jīng)建立的硬件上下文。這就像你換了個新的大腦來接管一個已經(jīng)正常運行的身體而不是讓整個身體推到手術(shù)室重新組裝一遍。當然實際操作中新內(nèi)核還是會做大量的軟初始化工作比如重新初始化內(nèi)存管理、進程調(diào)度、中斷等核心子系統(tǒng)但硬件層面的重復(fù)枚舉確實被大幅簡化了。3.2 內(nèi)存布局與加載地址的指定邏輯kexec加載內(nèi)核時有一個很重要的概念是加載地址。使用kexec -l加載時系統(tǒng)默認會選擇一段“安全”的內(nèi)存區(qū)域來放新內(nèi)核。這個安全區(qū)域通常是當前內(nèi)核未使用的物理內(nèi)存段由內(nèi)核在啟動時預(yù)留或者動態(tài)分配。在某些特殊場景下你可能需要手動指定加載地址比如當默認加載區(qū)域被某些固件或驅(qū)動占用時會報出kexec: Cannot allocate memory或者類似的內(nèi)存沖突錯誤。這時可以通過--load參數(shù)配合地址選項手動指定。但絕大多數(shù)情況下內(nèi)核自帶的這個內(nèi)存分配邏輯已經(jīng)足夠智能會自動避開正在使用的內(nèi)存區(qū)域。這里要特別提醒一個點kexec加載新內(nèi)核時必須保證新內(nèi)核鏡像的完整性并且新內(nèi)核運行時需要的物理內(nèi)存區(qū)域覆蓋范圍不能和當前內(nèi)核正在使用的關(guān)鍵數(shù)據(jù)結(jié)構(gòu)沖突。否則切換后可能出現(xiàn)無法解釋的隨機崩潰。我在調(diào)試時遇到過好幾次類似問題最后都發(fā)現(xiàn)是加載地址選擇不當造成的。3.3 內(nèi)核配置選項與kexec的依賴關(guān)系要在你的Linux系統(tǒng)上使用kexec首先內(nèi)核必須開啟相應(yīng)的配置項最常見的有三個CONFIG_KEXEC、CONFIG_KEXEC_FILE和CONFIG_CRASH_DUMP。CONFIG_KEXEC是支持傳統(tǒng)kexec加載機制的開關(guān)CONFIG_KEXEC_FILE是指基于文件描述符的加載方式也就是用kexec -s參數(shù)時走的那條路徑。基于文件的加載方式在安全性上要更好一些因為內(nèi)核會對鏡像簽名做校驗尤其在啟用了Secure Boot的機器上這種方式幾乎成了唯一選擇。如果你的內(nèi)核編譯時沒開這兩個選項即使你裝了kexec工具執(zhí)行時也會提示不支持。所以在折騰kexec之前先確認一下你的內(nèi)核配置zgrep KEXEC /proc/config.gz或者如果你的發(fā)行版沒有把config導出到/proc/config.gz那就檢查一下/boot/config-$(uname -r)文件。確認有CONFIG_KEXECy或CONFIG_KEXEC_FILEy之后再進行下一步。4. 實操篇kexec的安裝、加載與切換全流程4.1 安裝kexec-tools不同發(fā)行版的差異kexec的用戶態(tài)工具一般叫kexec-tools幾乎所有主流Linux發(fā)行版都提供了對應(yīng)的軟件包。在Debian/Ubuntu系列上安裝命令是sudo apt install kexec-tools安裝過程中會詢問你是否允許系統(tǒng)在重啟時自動使用kexec快速啟動如果沒有特殊需求可以先選擇否手動控制更穩(wěn)。在RHEL/CentOS/Fedora系列上安裝命令是sudo yum install kexec-tools或者在新版本上用sudo dnf install kexec-tools在openSUSE上則是sudo zypper install kexec-tools安裝完成后先用kexec --version驗證一下工具是否正??捎?。版本號不同參數(shù)解析上會有細微差異但核心操作邏輯基本一致。4.2 加載新內(nèi)核到內(nèi)存一個完整的kexec -l示例下面我以一個實際的升級場景來演示。假設(shè)當前系統(tǒng)跑的是5.15內(nèi)核你要切換到剛裝好的6.1內(nèi)核但暫時不想執(zhí)行真正的重啟動作。首先確認新內(nèi)核和initrd文件已經(jīng)放在了/boot目錄下ls /boot/vmlinuz-6.1* /boot/initrd.img-6.1*確認無誤之后執(zhí)行加載命令sudo kexec -l /boot/vmlinuz-6.1.0-rc3-generic \ --initrd/boot/initrd.img-6.1.0-rc3-generic \ --reuse-cmdline這里--reuse-cmdline會自動復(fù)用當前內(nèi)核的啟動參數(shù)在很多場景下是最省事的選擇。但如果你的新內(nèi)核需要額外的啟動參數(shù)比如要預(yù)留內(nèi)存給kdump那就需要手動指定sudo kexec -l /boot/vmlinuz-6.1.0-rc3-generic \ --initrd/boot/initrd.img-6.1.0-rc3-generic \ --command-lineroot/dev/mapper/vg-root ro crashkernel256M quiet這里有一個小提醒手動指定啟動參數(shù)時root一定要寫對否則新內(nèi)核起來后會因為找不到根文件系統(tǒng)而直接進入initramfs的緊急模式甚至直接panic。如果你不確定當前根文件系統(tǒng)的設(shè)備路徑可以先執(zhí)行findmnt /查看。4.3 執(zhí)行切換kexec -e與systemctl kexec加載完成之后你可以通過kexec -e立即執(zhí)行內(nèi)核切換。但執(zhí)行前務(wù)必確認幾件事所有重要的數(shù)據(jù)已保存當前服務(wù)的狀態(tài)可以接受中斷。因為kexec -e執(zhí)行后當前內(nèi)核幾乎不會有任何優(yōu)雅的退出流程所有用戶態(tài)進程會直接消失未落盤的數(shù)據(jù)可能會丟失。如果你用的是systemd更優(yōu)雅的替代方案是sudo systemctl kexecsystemctl kexec會觸發(fā)systemd先執(zhí)行一些基本的服務(wù)停止操作然后再調(diào)用kexec切換。注意它并不是完整地執(zhí)行關(guān)機流程而是會盡量在短時間內(nèi)停掉部分服務(wù)、卸載部分文件系統(tǒng)然后快速切換。優(yōu)點是比kexec -e稍微溫和一些缺點是沒有完整關(guān)機那么干凈。從我個人的經(jīng)驗來看如果是測試機或者你能扛得住服務(wù)直接中斷的場景直接kexec -e反而更簡單直接因為加載階段你已經(jīng)準備好了一切。systemctl kexec多執(zhí)行的那些步驟有時候反而會引入額外的變量。4.4 用腳本一鍵完成“加載-切換”的自動化實踐如果你經(jīng)常需要切換不同的內(nèi)核寫一個簡單的腳本可以省去每次手動輸入?yún)?shù)的麻煩。下面是一個我常用的shell腳本模板#!/bin/bash KERNEL$1 INITRD$2 EXTRA_CMDLINE${3:-} if [ -z $KERNEL ] || [ -z $INITRD ]; then echo Usage: $0 kernel-image initrd-image [extra-cmdline] exit 1 fi echo Loading kernel: $KERNEL echo Initrd: $INITRD CMD if [ -n $EXTRA_CMDLINE ]; then CMD--command-line$EXTRA_CMDLINE else CMD--reuse-cmdline fi sudo kexec -l $KERNEL --initrd$INITRD $CMD echo Kernel loaded. Run sudo kexec -e to switch.使用時就執(zhí)行./kexec-switch.sh /boot/vmlinuz-6.1.0-rc3-generic /boot/initrd.img-6.1.0-rc3-generic這個腳本只負責加載切換動作留給使用者手動觸發(fā)避免腳本誤觸發(fā)導致意外重啟。5. kdump場景kexec的另一半江山5.1 crashkernel預(yù)留內(nèi)存與kdump機制講到kexec不可能不提kdump。kdump是Linux內(nèi)核崩潰轉(zhuǎn)儲機制的核心它依賴的正是kexec技術(shù)。在系統(tǒng)啟動時通過內(nèi)核參數(shù)crashkernel256M預(yù)留一塊物理內(nèi)存這塊內(nèi)存專門用于在系統(tǒng)崩潰時加載和維護一個“捕獲內(nèi)核”。平時這個捕獲內(nèi)核并不會運行它只是被kexec預(yù)先加載到預(yù)留內(nèi)存中處于待命狀態(tài)。當主內(nèi)核發(fā)生panic時它會直接跳轉(zhuǎn)到這個捕獲內(nèi)核捕獲內(nèi)核啟動后會盡可能保存崩潰現(xiàn)場的內(nèi)存映像也就是生成一個vmcore文件供后續(xù)離線分析。這背后的流程是系統(tǒng)正常啟動時用kexec -p加載捕獲內(nèi)核。這里-p參數(shù)的含義是panic內(nèi)核加載目標是一段獨立的預(yù)留內(nèi)存。等到真正崩潰時主內(nèi)核無法繼續(xù)執(zhí)行但它會跳轉(zhuǎn)到這段預(yù)留內(nèi)存中運行捕獲內(nèi)核。捕獲內(nèi)核不依賴被崩潰破壞的根文件系統(tǒng)而是從預(yù)先加載好的initrd啟動然后把主內(nèi)核的內(nèi)存轉(zhuǎn)儲到磁盤上。5.2 配置kdump的完整步驟與參數(shù)說明配置一套可用的kdump環(huán)境其實很簡單。首先在GRUB的內(nèi)核啟動參數(shù)中加入crashkernel256M這個大小取決于你的系統(tǒng)內(nèi)存總量和需要轉(zhuǎn)儲的內(nèi)存大小。一般建議至少預(yù)留系統(tǒng)內(nèi)存的1/16比如系統(tǒng)有8G內(nèi)存預(yù)留512M會比較穩(wěn)。修改/etc/default/grub中的GRUB_CMDLINE_LINUX加上參數(shù)然后執(zhí)行sudo update-grub重啟后確認預(yù)留內(nèi)存已生效cat /proc/iomem | grep Crash kernel這一步如果沒有任何輸出說明crashkernel參數(shù)沒有生效需要檢查是不是GRUB配置沒寫對。接著安裝kdump工具sudo apt install kdump-tools安裝完成后kdump-tools服務(wù)會自動用kexec -p把當前內(nèi)核的捕獲內(nèi)核加載到預(yù)留內(nèi)存中。驗證是否加載成功可以看kdump-config show正常情況下會顯示ready to kdump狀態(tài)。最后測試一下崩潰轉(zhuǎn)儲是否生效sudo sysctl -w kernel.panic_on_oops1 sudo echo c /proc/sysrq-trigger這條命令會強制觸發(fā)內(nèi)核panic然后系統(tǒng)應(yīng)該會快速跳轉(zhuǎn)到捕獲內(nèi)核并生成vmcore。注意執(zhí)行這條命令后系統(tǒng)會短暫不可用生產(chǎn)環(huán)境別亂試。測試成功后重啟回到正常內(nèi)核檢查/var/crash目錄下是否有vmcore文件生成。5.3 捕獲內(nèi)核與主內(nèi)核的資源隔離問題kdump有一個容易被忽略的問題捕獲內(nèi)核和主內(nèi)核共享物理硬件但預(yù)留內(nèi)存是獨立的。如果捕獲內(nèi)核加載了和主內(nèi)核沖突的驅(qū)動或者在轉(zhuǎn)儲時訪問了被主內(nèi)核破壞的硬件寄存器可能導致轉(zhuǎn)儲失敗。為了減少這種概率很多發(fā)行版會為捕獲內(nèi)核準備一個專門的initrd里面只包含最基礎(chǔ)的驅(qū)動模塊。實際操作中如果發(fā)現(xiàn)kdump經(jīng)常轉(zhuǎn)儲失敗大概率是initrd里包含了太多不必要或者有副作用的驅(qū)動模塊。這時候可以考慮精簡initrd只保留磁盤控制器、網(wǎng)卡等必要驅(qū)動。不過需要注意的是精簡initrd是個技術(shù)活搞不好反而會讓捕獲內(nèi)核根本起不來。我在這里的建議是先用發(fā)行版默認的initrd跑一遍測試確認基本可用后再考慮優(yōu)化。6. 我踩過的坑kexec使用中的常見問題與排查技巧6.1 kexec -l 報錯內(nèi)核鏡像格式或權(quán)限問題最常見的問題之一是在執(zhí)行kexec -l時直接報kexec: Loading kernel failed或者Invalid file format。這類錯誤通常有兩個原因。第一是權(quán)限不夠。kexec加載內(nèi)核需要root權(quán)限如果你用的是普通用戶記得加上sudo。第二是內(nèi)核鏡像文件格式不對。部分發(fā)行版在/boot目錄下的vmlinuz文件不是標準的Linux內(nèi)核鏡像而是帶EFI stub的混合鏡像某些舊版本的kexec-tools識別不了。解決辦法是使用kexec -s走基于文件描述符的加載方式它會用內(nèi)核內(nèi)部的kexec_file_load接口來處理兼容性通常更好。另外還有一個小概率問題內(nèi)核鏡像文件損壞。檢查一下文件的校驗值是否和發(fā)行版的發(fā)布值一致。如果文件不完整加載時內(nèi)核在解壓階段就會失敗。6.2 切換后新內(nèi)核起不來initrd問題與模塊缺失kexec -e執(zhí)行后如果新內(nèi)核一直黑屏或者反復(fù)重啟大概率是initrd有問題。initrd的作用是在真正的根文件系統(tǒng)掛載前提供必要的驅(qū)動和工具讓內(nèi)核能找到根設(shè)備。如果你沒有指定initrd或者initrd和內(nèi)核版本不匹配新內(nèi)核會因為沒有磁盤驅(qū)動而無法掛載根文件系統(tǒng)。遇到這種情況我的排查思路是這樣在kexec -l之前先檢查一下initrd里是否包含了對應(yīng)的磁盤控制器驅(qū)動??梢杂靡韵旅畈榭磇nitrd內(nèi)容lsinitramfs /boot/initrd.img-6.1.0-rc3-generic如果發(fā)現(xiàn)缺少必要模塊可以用update-initramfs -c -k 6.1.0-rc3-generic重新生成initrd。另外一個容易被忽略的點是部分驅(qū)動模塊依賴固件文件而固件文件在initrd中缺失導致驅(qū)動加載失敗。這種問題通常會在系統(tǒng)日志中留下firmware: failed to load之類的記錄。排查時如果黑屏看不到任何輸出可以考慮在內(nèi)核啟動參數(shù)里加上noreplace-paravirt earlyprintk這類調(diào)試選項讓啟動早期信息輸出到串口或VGA上。6.3 Secure Boot場景下的簽名校驗問題現(xiàn)在不少服務(wù)器默認開啟Secure Boot這種情況下直接加載未簽名的內(nèi)核鏡像會被固件攔截。使用傳統(tǒng)kexec -l通常會直接失敗或者雖然在加載階段通過了但在執(zhí)行切換時被固件中止。正確的做法是使用帶簽名的內(nèi)核鏡像并且使用kexec -s基于文件描述符的加載方式讓內(nèi)核自己去做簽名校驗。如果你需要簽名的工具鏈可以在發(fā)行版?zhèn)}庫里找sbsigntool用自己的Machine Owner KeyMOK對內(nèi)核鏡像簽名。簽完名后加載時會比傳統(tǒng)方式稍微復(fù)雜一點但整體動作還是那幾步。如果你只是為了在本地測試不想折騰簽名最簡單的方案是在BIOS設(shè)置里臨時關(guān)閉Secure Boot。但要注意生產(chǎn)環(huán)境不建議為了圖省事關(guān)閉安全啟動這會大大降低系統(tǒng)安全性。6.4 切換后網(wǎng)卡名變化導致網(wǎng)絡(luò)失聯(lián)的應(yīng)對這是一個非常實際的問題。當你從一個內(nèi)核切換到另一個內(nèi)核時如果新內(nèi)核使用的網(wǎng)絡(luò)驅(qū)動命名規(guī)則不同比如從eth0變成了enp0s3你的網(wǎng)絡(luò)配置可能全部失效遠程連接的機器會直接斷線。解決辦法是在新內(nèi)核的啟動參數(shù)中固定網(wǎng)卡命名規(guī)則或者提前在initrd和系統(tǒng)配置中預(yù)置好新環(huán)境下的網(wǎng)絡(luò)配置。最簡單粗暴的辦法是在切換前確保新內(nèi)核的驅(qū)動模塊和udev規(guī)則和舊內(nèi)核一致。如果你用的是現(xiàn)代發(fā)行版通常系統(tǒng)的網(wǎng)絡(luò)管理服務(wù)會自動處理網(wǎng)卡重命名但還是建議在切換前把網(wǎng)卡的配置文件檢查一遍。我這里還有一個實用的小技巧如果你必須遠程操作一臺機器做kexec切換最好準備一個IPMI或者帶外管理口。萬一切換后網(wǎng)絡(luò)起不來你還能通過帶外方式恢復(fù)。這個教訓我是付出過代價的在沒有任何帶外管理的情況下遠程切換內(nèi)核結(jié)果只能跑去機房物理操作。6.5 內(nèi)存不足導致的加載失敗當系統(tǒng)可用內(nèi)存非常緊張時kexec -l可能會報錯Cannot allocate memory。這是因為kexec需要一段連續(xù)的物理內(nèi)存來存放新內(nèi)核和initrd。系統(tǒng)內(nèi)存碎片化嚴重時即使總可用內(nèi)存還有不少也找不到足夠大的連續(xù)區(qū)域。這時有兩個常用手段。一是嘗試用--load配合指定的加載地址嘗試不同的內(nèi)存段。這種情況通常需要根據(jù)/proc/iomem里的內(nèi)存布局來選擇合適的區(qū)域。二是先用echo 3 /proc/sys/vm/drop_caches釋放頁緩存減少內(nèi)存碎片。如果這兩個辦法都不行那基本只能等一些內(nèi)存占用大戶退出后再嘗試。6.6 常見問題速查表為方便快速定位問題我把上面提到的典型問題整理成一張速查表現(xiàn)象可能原因排查方向kexec -l 報 Invalid file format內(nèi)核鏡像格式不被識別嘗試 kexec -s 走文件描述符方式加載時報 Cannot allocate memory內(nèi)存碎片化或預(yù)留區(qū)域沖突釋放緩存或手動指定加載地址kexec -e 后黑屏initrd缺失或驅(qū)動模塊不完整檢查initrd內(nèi)容并重新生成Secure Boot攔截切換內(nèi)核鏡像未簽名使用簽名內(nèi)核或臨時關(guān)閉Secure Boot切換后網(wǎng)絡(luò)失聯(lián)網(wǎng)卡命名變化或驅(qū)動缺失提前確認新內(nèi)核網(wǎng)絡(luò)配置切換后系統(tǒng)反復(fù)重啟根文件系統(tǒng)參數(shù)不對檢查 command-line 里的 root 配置kdump轉(zhuǎn)儲失敗預(yù)留內(nèi)存不夠或模塊沖突調(diào)大 crashkernel 或精簡initrd這張表不能覆蓋所有場景但常見的坑基本都在這了。7. kexec的進一步擴展與GRUB、systemd和容器的協(xié)同使用7.1 和GRUB引導流程的對比何時用kexec何時用傳統(tǒng)重啟有人可能會問既然kexec這么方便為什么不把所有重啟都改成kexec答案在于kexec的適用范圍是有限的。傳統(tǒng)重啟的好處是徹底。硬件被完全重置、固件重新初始化能夠清掉很多潛在的不穩(wěn)定狀態(tài)。如果系統(tǒng)里某個硬件設(shè)備狀態(tài)異常傳統(tǒng)重啟很容易解決但kexec切換時硬件狀態(tài)是繼承的如果某個設(shè)備已經(jīng)處于不穩(wěn)定狀態(tài)新內(nèi)核也會跟著遭殃。所以我的經(jīng)驗法則是日常內(nèi)核升級和版本切換用kexec能顯著縮短停機時間但如果系統(tǒng)出現(xiàn)莫名其妙的硬件異常、驅(qū)動僵死、或者你要清理固件級別的異常狀態(tài)那就老老實實走完整重啟。不要迷信kexec它只是工具不是銀彈。7.2 systemd-kexec服務(wù)與內(nèi)核升級自動化systemd提供了一套和kexec協(xié)同的機制你可以通過配置/etc/systemd/kexec.conf來預(yù)設(shè)kexec加載信息。每當系統(tǒng)執(zhí)行systemctl kexec時systemd會根據(jù)配置完成加載并切換。同時在內(nèi)核升級自動化方面有些發(fā)行版會在安裝新內(nèi)核包后自動執(zhí)行kexec -l預(yù)加載新內(nèi)核。這樣用戶之后只需要執(zhí)行一條命令就能完成切換。Debian系的部分環(huán)境在安裝kexec-tools時會提示你配置這個行為。實際操作時我不太建議完全自動化畢竟內(nèi)核切換是一件影響面很大的動作提前預(yù)加載可以但真正的切換動作還是應(yīng)該由人來控制至少得留一個確認環(huán)節(jié)。7.3 kexec在容器和虛擬機快速迭代中的妙用在容器場景下kexec本身并不直接作用于容器但如果你是搞內(nèi)核開發(fā)的經(jīng)常需要測試新內(nèi)核在容器運行時環(huán)境下的表現(xiàn)kexec就能幫上大忙。比如某公司在物理機上跑著一堆容器需要快速驗證新內(nèi)核與容器運行時是否兼容。如果走傳統(tǒng)重啟可能要等很多容器重新調(diào)度。用kexec提前加載好新內(nèi)核在維護窗口一鍵切換整體服務(wù)中斷時間能壓縮到一分鐘以內(nèi)。虛擬化場景下的思路也類似。某些虛擬化平臺支持guest內(nèi)部用kexec快速切換內(nèi)核宿主機層面無感知對業(yè)務(wù)影響極小。但需要注意虛擬化平臺的某些特性比如動態(tài)內(nèi)存氣球、設(shè)備透傳在kexec切換后可能無法保持原有狀態(tài)測試時一定要驗證這些高級特性是否仍然正常工作。8. 實操記錄一次線上內(nèi)核升級的完整復(fù)盤最后分享一次我實際操作的案例。環(huán)境是一臺某公司的測試服務(wù)器跑著一個對網(wǎng)絡(luò)性能要求很高的應(yīng)用內(nèi)核版本從5.10升到5.15但那次升級不能走正規(guī)的重啟流程因為業(yè)務(wù)要求盡量減少中斷時間而完整的硬件自檢要花掉將近十分鐘。我的操作流程是這樣先把新內(nèi)核和initrd準備好檢查/boot目錄下的文件無誤然后執(zhí)行kexec -l預(yù)加載加載時手動指定了root設(shè)備和幾個必要的啟動參數(shù)沒有用--reuse-cmdline因為舊內(nèi)核的某些參數(shù)比如舊驅(qū)動專用的配置在新內(nèi)核上已經(jīng)不適用。加載完成后我用kexec -e執(zhí)行切換整個切換過程只花了大概兩秒鐘系統(tǒng)直接進入新內(nèi)核。新內(nèi)核啟動后我第一時間檢查了/var/log/kern.log確認沒有明顯的驅(qū)動報錯然后又檢查了網(wǎng)絡(luò)狀態(tài)和業(yè)務(wù)進程的運行情況一切正常。整個過程從發(fā)出切換指令到業(yè)務(wù)完全恢復(fù)用了不到三十秒。如果走傳統(tǒng)重啟這個數(shù)字至少要膨脹到十分鐘。再分享一個反面案例。另一次我圖省事沒有手動指定啟動參數(shù)直接用了--reuse-cmdline結(jié)果新內(nèi)核繼承了舊內(nèi)核里一個已經(jīng)廢棄的驅(qū)動參數(shù)導致驅(qū)動加載失敗網(wǎng)絡(luò)接口起不來最后只能跑機房處理。所以現(xiàn)在我在kexec -l這一步從來不敢偷懶啟動參數(shù)寧可花兩分鐘仔細核對也不要切換后花半小時收拾爛攤子。這里給你一個操作清單可以當作checklist用確認新內(nèi)核和initrd文件存在且完整檢查當前內(nèi)核啟動參數(shù)決定復(fù)用還是手動指定手動指定參數(shù)時確認root設(shè)備路徑正確確認預(yù)留內(nèi)存區(qū)域kdump場景沒有被新參數(shù)覆蓋切換前保存所有重要數(shù)據(jù)通知業(yè)務(wù)方執(zhí)行kexec -l后用kexec --status檢查加載狀態(tài)一切就緒后執(zhí)行kexec -e或systemctl kexec切換完成后立刻檢查內(nèi)核日志和關(guān)鍵服務(wù)狀態(tài)9. 個人經(jīng)驗與建議kexec雖好用但別忽視細節(jié)我用kexec已經(jīng)有比較長的時間了從最開始單純?yōu)榱思涌熘貑⑺俣鹊胶髞斫Y(jié)合kdump做崩潰分析再到線上內(nèi)核版本的低中斷升級這個工具確實幫了大忙。但我也想說kexec不是一個可以完全無腦使用的黑魔法它要求使用者對內(nèi)核啟動機制、內(nèi)存布局和硬件狀態(tài)都有足夠的理解。給剛接觸kexec的朋友幾個建議。第一先在虛擬機或者測試機上反復(fù)練習整個加載、切換、回退流程不要一上來就在生產(chǎn)環(huán)境上操作。第二切換前務(wù)必備份當前內(nèi)核的啟動參數(shù)和根文件系統(tǒng)配置一旦新內(nèi)核出了問題至少還能用傳統(tǒng)方式引導回舊內(nèi)核。第三如果條件允許配合串口控制臺使用。串口能讓你在屏幕黑掉的情況下也能看到內(nèi)核啟動日志這對排錯來說簡直是無價之寶。最后再分享一個小技巧如果你需要頻繁在不同的內(nèi)核版本之間切換測試建議在磁盤上保留至少兩個可引導的內(nèi)核版本并分別用GRUB菜單項固定好。這樣一來即使kexec切換失敗你重啟后還能通過GRUB選擇器回到原來的內(nèi)核。生產(chǎn)環(huán)境里的安全網(wǎng)越多越好這個習慣能幫你避免很多低級事故。