Alibaba Cloud Linux 2 LTS 快速啓動優化實踐

做者:阿里雲操做系統:張世樂html

概述/Overviewnode

Alibaba Cloud Linux 2(Aliyun Linux 2,簡稱Alinux 2)是阿里雲操做系統團隊基於開源Linux內核4.19 LTS版本打造的一款針對雲應用場景的下一代Linux OS發行,不只提供Linux社區的最新加強功能,在提供雲上最佳用戶體驗的同時,也針對阿里雲基礎設施作了深度的優化。今日Alinux 2 LTS 正式發佈,是Alinux 2的一個重要里程碑。這標誌着阿里雲操做系統團隊將爲Alinux 2提供長期技術支持、穩定的更新、更好服務,爲Alinux 2的用戶提供更多保障。Alinux 2 LTS 版本不只增長了更多社區新功能的支持,對系統啓動時間、運行時性能及穩定性都作了許多優化。更詳細的更新優化可參考發佈記錄,推薦直接上手試用體驗。Alinux 2在快速啓動優化上取得一些不錯的效果,同時推出「Alinux 2 qboot快速啓動版鏡像(公測中),內核部分啓動性能提高40%:linux

 

 

這裏分享一下Alinux 2 LTS在系統快速啓動優化上的所作的一些實踐。算法

 

Linux系統啓動流程/Boot Process

 

首先定義Linux系統啓動,這裏咱們定義爲從系統上電(虛擬機開啓),到用戶可以登錄(ssh login)爲系統啓動。通用Linux系統啓動大體分爲三個階段:引導階段(phase#1),內核啓動階段(phase#2)及用戶態啓動階段(phase#3)服務器

 

 

其中,網絡

·     對物理機產品,開機後運行固件中的BIOS程序,完成基本硬件初始化及上電自檢(POST),經過後跳轉至系統磁盤引導扇區;多線程

·     對虛擬機(Qemu + KVM)產品,Qemu運行後模擬BIOS,加載系統鏡像文件虛擬出系統盤,跳轉至系統盤引導扇區;
下面來看看各階段大體的啓動流程。
dom

 

Boot Loader

Bootloader是位於系統引導扇區的一段獨立的系統程序,用於系統啓動初期的硬件初始化,系統分區識別,系統內核加載及跳轉執行。目前應用比較普遍的bootloader是用於通用系統的grub2和嵌入式系統的ubootGrub2是多重引導器(multiboot),提供交互界面,默認配置下有5s交互超時,啓動耗時較長。ssh

Kernel

Bootloader加載Linux內核(通常爲壓縮內核vmlinuz)到內存,並運行內核自解壓縮程序,解壓後跳轉至start_kernel,開始內核初始化流程:異步

 

User Space

 

Linux內核完成一系列初始化動做以後,開始運行init程序,建立PID1的用戶態進程,將系統控制權從內核態跳轉到用戶態。init進程會繼續進行用戶態啓動流程,開啓各類必要的,或是預先配置的系統服務,最後啓動登錄服務,完成整個系統的啓動。

 

 

InitrdSwitch Root

init是用戶態程序,存放在系統根文件系統(rootfs)裏。內核須要先掛載rootfs,才能運行init程序。通用Linux發行須要支持多種磁盤設備,多種文件系統,所以內核必須可以識別不一樣的磁盤設備,不一樣的文件系統。這須要內核預加載多種可能的磁盤設備驅動以及多種文件系統相關用戶態工具軟件才能正確識別rootfs。而這些驅動及用戶態工具通常都存放在rootfs中,造成一個循環依賴。
爲解決這個問題,initrd應運而生,將掛載rootfs必要的驅動,用戶態工具以及其餘須要預加載的代碼從rootfs總抽取出來,並依照rootfs的文件結構,打包成一個小的rootfs,作成一個內存盤(ram disk)。內核在掛載最終的rootfs以前,先從內存中掛載initrd,加載必要的驅動後,先運行initrd中的init程序,掛載最終的rootfs。而後執行switch root動做,切換至最終的rootfsAlinux 2系統採用systemd來管理用戶空間啓動流程,systemd就是init程序,initrd使用壓縮格式的initramfs文件。所以在加載initrd以前,內核須要先解壓縮initramfs

 

Cloud Init

Cloud init是雲環境中的虛擬實例初始化配置工具,實例啓動階段能從多種數據源讀取相關數據並據此對虛擬機進行配置,如用戶密碼,主機名,網絡,用戶數據等等一些配置。

啓動耗時畫像/Profile

優化系統啓動時間,天然須要先對系統啓動畫像,瞭解啓動時間分佈狀況,找出系統啓動耗時熱點。

啓動時間測量

Linux系統有以下常見的啓動時間測量統計方法:

·     systemd-analyzesystemd自帶的啓動分析工具,可以給出總的啓動時間消耗,已經用戶態服務啓動耗時統計。

dmesgdmesg輸出內核啓動日誌,時間戳可以幫助分析內核初始化各階段耗時狀況。配合-d選項計算出日誌間的時間差,方便快速定位內核啓動過程當中耗時熱點。

·     initcall_debug
內核啓動參數,開啓後會統計內核各初始化函數的耗時狀況,相比dmesg -d更加精確。

·     printk/trace_printk
要分析一些啓動熱點的細化耗時狀況時,手動增長一些printk/trace_printk探針可以幫助獲取時間統計信息。

·     ftrace
必要時也可開啓內核早期ftrace功能,幫助分析熱點耗時。不過須要注意開啓ftrace後可能會致使函數延時增長,所以不宜參考ftrace得出函數絕對耗時,能夠參照trace結果幫助分析熱點函數的耗時邏輯。

 

還有其餘一些時間測試方法,以及圖形化畫像工具,這裏不一一介紹。

 

啓動耗時熱點分析

 

Alinux 2系統啓動畫像後,按耗時排序,獲得以下耗時熱點:(這裏以2C8G虛擬機爲例,內核耗時1000ms,整體耗時5000ms)

image.png

 

可見:

 

·     整體啓動耗時中,一半以上的時間消耗在用戶態cloud-init進程上;

·     內核啓動階段,鼠標探測耗時佔比較高。

 

快速啓動優化/Boot Optimize

 

啓動優化方法

 

經常使用的啓動優化方法大體以下:

 

·     瘦身

o   移除沒必要要的代碼,如模塊,服務等,縮減啓動初始化步驟;

o   移除沒必要要的測試,調式及打印

o   精簡共享庫

·     異步、並行

o   將耗時動做從關鍵路徑移除,延後執行

o   將順序動做並行化執行

·     原地執行(XIP)

o   多用於嵌入式系統

·     定製化

o   將通用初始化程序定製化

·     算法優化

o   改進算法,加速初始化時間

 

initrd

 

從前面的啓動耗時熱點分析結果能夠看出,initrd解壓縮及initrd systemd耗時佔Alinux 2啓動較大比率。

 

 

Alinux 2系統主要面向雲環境虛擬實例,系統盤設備基本固定爲virtio-blk設備,根文件系統格式基本固定爲ext4文件系統,應該不須要經過initrd來加載rootfs,能夠去掉initrd,直接掛載系統磁盤,即對內核啓動瘦身。

 

理論上會優化掉initramfs unpack(270) + initrd systemd(560) ~ 800ms的啓動耗時。去掉initrd測試結果以下:

 

 

可見initrd systemd時間確實優化掉了,但總的啓動時間並無理論優化收益。緣由是內核啓動耗時增長了約400ms。進一步分析發現,啓動耗時熱點之一的mouse probe(600ms),去initrd以前是與initrd systemd並行執行的。

 

 

去掉initrd後,這部分時間就直接計入內核啓動時間了。抵去優化掉的initramfs unpacking200ms,內核實際增長了400ms左右。

 

所以,要最大化去initrd的優化收益,必須同時解決mouse probe的耗時。

 

延遲probe

 

通用Linux系統須要支持多種IO設備,而鼠標鍵盤是比較經常使用的輸入設備,特別是鼠標,產品繁多,接口多樣。系統啓動過程當中加載鼠標驅動後,須要掃描多種IO總線來探測鼠標設備,這一過程很是耗時。
依據前面提到的優化方法,咱們有兩種方案:

 

1.   對雲環境定製鼠標驅動,固定探測virtio設備;

2.   將鼠標探測從啓動關鍵路徑剝離,延遲探測,與後面系統啓動服務並行;

 

第一種方案須要重構相關代碼,成本較高;並且定製化限制較多,沒法與開源社區協做。所以須要思考第二種方法:延遲探測。一種簡單可行的方法是將本來內置(built-in)的設備驅動從新編譯爲內核模塊(kernel module),因內核模塊存放在根文件系統,因此加載時機被動推遲到根文件系統掛載以後,此時內核已經啓動完成,天然與用戶態初始化進程並行執行。
測試結果以下:(注意這是優化後的內核本地測,cloud-init被禁用)
initrd啓動:

 

 

不帶initrd啓動:

 

 

可見,內核啓動時間縮減約200ms,優化掉initrd systemd時間;鼠標設備探測延後至userspace初始化階段,致使userspace啓動時間略有增長。得到預期的啓動時間優化。

 

內存初始化優化

 

內存初始化也是內核啓動熱點之一,特別是在大規格實例上,內存初始化耗時佔比較高。圖中爲750GB實例內存初始化耗時:meminit耗時近2s

 

 

buddy init耗時1.8s

 

 

內存初始化動做是在內核啓動的關鍵路徑上,優化思路是並行初始化。因內存初始化時機較早,系統多CPU還未初始化完成,因此須要將內存初始化延後至CPU初始化完成以後,採用多線程並行執行內存初始化。這部分工做社區已經完成,經過內核配置CONFIG_DEFERRED_STRUCT_PAGE_INIT來開啓。
開啓後,內存初始化延後,按NUMA node並行執行:
前半部耗時約0.2s

 

 

後半部耗時約1.3s

 

 

free initmem修復

Alinux 2 內核啓動優化前有一個機率性的啓動熱點,free initmembuddy系統時,會大機率(超過70%)出現200ms以上延時,dmesg日誌顯示以下,耗時超過200ms

[    0.687494] rtc_cmos 00:00: setting system clock to 2020-03-03 15:09:38 UTC (1583248178)

[    0.915315] Freeing unused kernel image memory: 1836K

 

經分析,發現是社區已知問題,並在新內核已經修復 因而backportAlinux 2 LTS內核,修復後耗時約5ms,基本消除這部分延時:

[    0.482477] rtc_cmos 00:00: setting system clock to 2020-03-03 15:01:41 UTC (1583247701)

[    0.487438] Freeing unused kernel image memory: 1856K

 

ORC unwind初始化

內核中有一些靜態表,須要在內核初始化階段排序,有些表體積較大,初始化耗時佔比也不容小覷。如ORC unwind表格初始化排序,耗時約90ms

[    0.087330] clocksource: refined-jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 1910969940391419 ns

[    0.179718] random: get_random_bytes called from start_kernel+0x8b/0x563 with crng_init=0

 

這些靜態表格是在內核構建階段生成,所以能夠將排序動做從內核初始化階段移除,放到內核構建階段完成,以節省內核初始化時間。經調查發現社區已經有相似的優化方案,異常處理表(exception table)排序移植到了內核構建階段完成。因而對異常處理表改進,增長了ORC unwind表格構建階段排序優化,系列patch已經合入主線。
優化後基本削減了這部分耗時:

[    0.037253] clocksource: refined-jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 1910969940391419 ns

[    0.040714] random: get_random_bytes called from start_kernel+0x8b/0x563 with crng_init=0

 

Cloud-init優化

 

Alinux 2採用systemd啓動用戶態系統服務進程,以實現最大化並行啓動。在到達啓動完成的ssh登錄狀態前,依賴一系列必要的系統服務,其中cloud-init是關鍵鏈路中一個耗時熱點,啓動畫像中能夠看出cloud-init幾乎佔整個系統啓動時間的一半,所以優化cloud-init可以得到較大的啓動性能收益。
cloud-init服務中,一個耗時的配置任務是用戶密碼配置,須要從metadata服務器獲取帳號密碼,完成配置。Alinux 2 LTS 內核開啓了
Qemu firmware configuration 功能,可以經過qemu透傳一些諸如帳號密碼的配置到虛擬機內部,使得cloud-init可以本地讀取配置信息,加快cloud-init配置動做。感謝阿里雲鏡像團隊跟阿里雲虛擬化團隊共同努力,即將推出InnerPasswd功能,加速Alinux 2 LTS 實例的cloud-init配置,提高實例啓動時間,敬請期待!

 

其它優化

 

另外,啓動階段的console輸出也是一個相對耗時的動做,由於串行口的波特率是固定的,大量是輸出會造成阻塞致使console enabled延時較大。例如:
開啓console outputconsole耗時2.6s:

 

 

配置內核參數quiet,關閉console output:

 

下一步工做/The Next

 

雖然Alinux 2 LTS在啓動優化已經取得了不錯的效果,啓動性能獲得進一步提高,但仍然還有進一步挖掘的空間。特別是內存初始化這塊,仍然是大規格實例啓動熱點。即使已經開啓的deferred page init特性,但內存初始仍然限於node間並行,而node內並行初始化值得進一步挖掘,特別對當前ECS實例大都爲單node實例(NUMA關閉)的場景下,理論上有更大的收益。 據瞭解社區已經有貢獻者在着手進行相關工做,值得期待。

相關文章
相關標籤/搜索