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

作者:阿里雲操作系統:張世樂

 

概述/Overview

 

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%:


1588215854496-baf18923-7f2e-40b9-b2a6-2f2344d3f01d.png


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

 

Linux系統啓動流程/Boot Process

 

首先定義Linux系統啓動,這裏我們定義爲從系統上電(虛擬機開啓),到用戶能夠登陸(ssh login)爲系統啓動。通用Linux系統啓動大致分爲三個階段:引導階段(phase#1),內核啓動階段(phase#2)及用戶態啓動階段(phase#3):


1588215854777-f0191140-77aa-4ccd-a610-6121bb100145.png


其中,

 

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

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

 

Boot Loader

 

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

 

Kernel

 

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


1588215855012-4bef4e4e-c1e9-4c3e-920f-39d41ee1456c.png

User Space

 

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


1588215855210-0c8cf1a0-eb50-487a-9a8f-38779bb1e7e0.png


Initrd與Switch Root

 

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

 

Cloud Init

 

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

 

啓動耗時畫像/Profile

 

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

 

啓動時間測量

 

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

 

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


1588215855477-a21c196a-ea7d-411f-bd98-6d6fe374780b.png


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


1588215855807-bf2ca7ca-3dfd-412b-bdb5-9750689361c2.png


·     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啓動較大比率。


1588215856067-441ae08d-6e42-4a55-b6f1-453e36489be2.png


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

 

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


1588215856318-b2133d42-96c9-4109-94bd-c3f07f4db59d.png


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


1588215856577-313415e4-b909-47cc-acf4-916cac0766c2.png


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

 

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

 

延遲probe

 

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

 

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

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

 

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


1588215856872-01263f44-8a03-4f5c-a54a-0fe0f284ecf0.png


不帶initrd啓動:


1588215857220-adf5bb6b-6d1f-4fac-a356-fd9d5fa6f3de.png


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

 

內存初始化優化

 

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


1588215857487-4bd4a92f-2443-496e-99ba-d56270348063.png


buddy init耗時1.8s


1588215857735-1ee7cc72-b1d6-4197-b20f-c34a605e2cd0.png

 

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


1588215858131-3992bb21-85fb-4827-94a3-6bfcee5a1e6e.png


後半部耗時約1.3s


1588215858644-69099df1-ce5c-4c1c-a36f-6052855b2e4b.png

 

free initmem修復

Alinux 2 內核啓動優化前有一個概率性的啓動熱點,free initmem到buddy系統時,會大概率(超過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

 

經分析,發現是社區已知問題,並在新內核已經修復 於是backport回Alinux 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 output,console耗時2.6s!:


1588215859015-62a2ef4c-25ba-4ebf-999a-f4dd187f432b.png


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


1588215859230-9c4ed67b-aa70-4b9e-878e-9211e7a228c4.png

下一步工作/The Next

 

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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章