天天看點

初探 openEuler 容器鏡像剪裁

系統基礎鏡像提供了一個輕量級的 Linux 作業系統使用者空間,并且作為各應用鏡像的建構基礎,在擁有最基礎的軟體包能力同時,體積應盡量做到最小。為提高容器應用的啟動效率,為後續雲原生相關應用打下基礎,Cloud Native SIG 進行了一系列的探索,包含本文提及的基礎鏡像裁剪以及後續計劃實施的軟體包優化等。同時我們也了解到,我們的夥伴目前已經在公司内部的“磐舟”容器平台部分使用 openEuler 基礎鏡像作為應用建構的基礎,在規模容器化部署的過程中,鏡像的存儲、分發占據了較多的啟動時間及實體存儲空間。在裁剪分析之前,我們先給出主流系統發行版在 x86_64 架構下基礎鏡像的軟體包數量及其大小:

版本 軟體包數量 基礎鏡像大小
openeuler-20.03-lts 352 469MB
openeuler-20.03-lts-sp1 358 512MB
openeuler-20.09 346 531MB
ubi 186 205MB
ubi-init 187 220MB
ubi-minimal 105 103MB
centos8 172 209MB
fedora 145 175MB
ubuntu 92 72.9MB

因為

軟體包依賴關系較為複雜、軟體包合并提供能力等問題,大量的非核心軟體包被引入到 openEuler 基礎鏡像,使得基礎鏡像體積過大。下面,我們一步步分析 openEuler 基礎鏡像“大”起來的原因。

2. openEuler 基礎鏡像裁剪前分析

目前 openEuler 基礎鏡像使用

openeuler-os-build

[1]工程進行制作,使用

kiwi

[2]工具進行裁剪。其中 kiwi 工具使用配置檔案 config.xml 用于配置基礎鏡像安裝的軟體包清單,在裁剪定制之前安裝的軟體包有:basesystem、bash、coreutils、yum、filesystem、iproute、NetworkManager、net-tools、openEuler-release、procps-ng、rootfiles、systemd、vim-minimal、which,這麼多的包真的有必要都放入基礎鏡像中嗎?欲善事、先利器,簡單實作

鏡像依賴分析工具 idl

[3],用于根據基礎鏡像内軟體包清單及相應的軟體源擷取鏡像内全部軟體包的依賴關系字典及依賴圖,idl 代碼較少還有很大的優化空間,同學們可以自己去實作更好的版本。

使用工具分析後發現,使得目前 openEuler 基礎鏡像體積過大的主要原因為以下兩點,組内的同學

woqidaideshi

[4]已經提出相應的 issue 并且已經得到解決:

  • [src-openeuler/libreport 分離 libreport-filesystem 軟體包](<

    https://gitee.com/src-openeuler/libreport/issues/I1SY6B

    [5]>) openEuler master,21.03 分支合入

  • [src-openeuler/dnf dnf 移除依賴 deltarpm](<

    https://gitee.com/src-openeuler/dnf/issues/I1SY58

    [6]>) openEuler 全分支合入

下面我們

總結一下這兩點原因。在 libreport 倉分離 libreport-filesystem 軟體包前,因為使用 libreport 包來提供 libreport-filesystem 包的能力,使得 dnf 包管理器直接依賴 libreport 包(yum 包管理器在 openEuler 就是 dnf 包管理器,這點留給讀者自己檢視),最終導緻 systemd 等一系列依賴包的引入,而我們隻需要使用 libreport-filesystem 軟體包提供 dnf 包管理器相關的目錄結構。由于 dnf 移除 deltarpm 包依賴已經在 openEuler 全分支合入,是以在分析過程中,基礎鏡像實際并未引入 deltarpm 相關的子依賴包,但 openEuler 20.03 LTS 版本系統中依舊是未拆分依賴的 dnf 版本,使用該版本本地源手動建構出的基礎鏡像依舊會存在該依賴問題。

僅包含 yum 的基礎鏡像

對于基礎鏡像而言,包含 yum 包管理器的軟體包棧是最基礎的,我們嘗試制作僅包含 yum 軟體包棧的基礎鏡像,結果大小如下:

初探 openEuler 容器鏡像剪裁

使用已經

拆分 libreport-filesystem 軟體包的 openEuler21.03 版本,在 x86_64 架構下,相應的僅包含 yum 軟體棧的基礎鏡像包含 124 個軟體包,大小為 199M。

但是作為 openEuler 系統發行的釋出件之一,基礎鏡像僅包含 yum 軟體包棧不甚合理,我們需要提供常用的軟體包供開發人員在基礎鏡像容器中使用。

下面我們分析主流系統基礎鏡像,然後決定選取哪些軟體包到 openEuler 基礎鏡像中。

通用基礎鏡像入口軟體包分析

這裡我們分析

Universal Base Image

[7]、CentOS、Fedora 基礎鏡像入口軟體包,同樣使用

[8]。下面以 CentOS 為例進行分析:首先,我們擷取到鏡像内軟體包清單并将其寫入到檔案中,如 centos8.list。

rpm -qa --qf '%{NAME}\n' | sort > centos8.list
           

其次,配置相應基礎鏡像使用的軟體源,如 centos8-x86_64.conf。最後再使用如下指令擷取 centos8 基礎鏡像入口軟體包。

python3 idl.py conf/centos8-x86_64.conf -e image_package_list/centos8.list
           

類似地,我們擷取 UBI 系列以及 Fedora 基礎鏡像的入口軟體包清單。結果如下:

鏡像 入口包清單
binutils, hostname, kexec-tools, langpacks-en, less, libdb-utils, rootfiles, tar, vim-minimal, yum
fedora33 alternatives, fedora-repos-modular, gpg-pubkey, rootfiles, sssd-client, sudo, tar, util-linux, vim-minimal, yum
audit-libs, chkconfig, gpg-pubkey, langpacks-en, libcap-ng, libdb-utils, microdnf, rootfiles
crypto-policies-scripts, findutils, gdb-gdbserver, gpg-pubkey, langpacks-en, libdb-utils, rootfiles, subscription-manager, tar, vim-minimal, yum
crypto-policies-scripts, findutils, gdb-gdbserver, gpg-pubkey, langpacks-en, libdb-utils, procps-ng, rootfiles, subscription-manager, tar, vim-minimal, yum

相應的結果圖如下:

初探 openEuler 容器鏡像剪裁

注:

  • centos8 findutils 包通過 kexec-tools -> dracut -> findutils 路徑引入
  • centos8 procps-ng 包通過 kexec-tools -> dracut -> procps-ng 路徑引入
  • centos8、ubi、ubi-init 均包含 systemd 軟體包全棧, centos8 systemd 可通過 kexec-tools -> systemd 路徑引入,ubi、ubi-init 可通過 subscription-manager -> systemd 引入

我們可以發現,yum 軟體包棧以及 libdb-utils、tar、vim-minimal 等基礎軟體包作為最常用的系統軟體被包含于基礎鏡像中。此外,centos8 提供額外的 kexec-tools 工具用于直接加載核心以及 bintuils 工具用于分析二進制程式等。而 ubi-minimal 則包含使用 C 語言實作的 dnf 包管理器輕量化子集版本 microdnf 以及其他的基礎包用于提供更小的基礎鏡像。

4. openEuler 基礎鏡像軟體包政策及結果

引入以下圖中紅框選中的公共基礎軟體包,包含 yum 軟體包棧以及相關的基礎工具包共同作為 openEuler 基礎鏡像的基礎軟體集。

軟體包選取的思路是:盡可能提供系統中常用的指令,如 ps 指令(procps-ng)、find 指令(findutils)等;因為 Fedora 及 UBI 基礎鏡像的選取思路而沒有選擇引入 hostname、binutils 等 CentOS 獨有的軟體包,留給開發者自行下載下傳;選擇 gdb-gdbserver 包是希望 openEuler 基礎鏡像能提供簡單的調試環境。

初探 openEuler 容器鏡像剪裁

openEuler 對比其他的系統鏡像,相關包引入的差別:

  • glibc-all-langpacks 由 glibc-common 提供能力
  • libdb-utils 由 libdb 提供能力
  • findutils 通過 libsolv 引入

而 glibc-common、libdb-utils、libsolv 均存在于 yum 軟體包棧的依賴圖中,于是相應的

[9]倉用于制作基礎鏡像的配置檔案 config.xml 如下:

初探 openEuler 容器鏡像剪裁

Packages type 為 bootstrap 表示在鏡像準備(kiwi prepare)階段安裝 filesystem 包;Packages type 為 image 表示在鏡像制作(kiwi create)階段安裝:yum、procps-ng、gdb-gdbserver、rootfiles、tar、vim-minimal、openEuler-release 包(filesystem 為 linux 基礎目錄結構軟體包,在 openEuler 中可以通過 yum-> dnf -> bash -> filesystem 路徑依賴引入,由于使用 kiwi 工具裁剪,必須提供類型為 bootstrap 的軟體包,filesystem 一般作為此類型的軟體包優先安裝)。

openEuler 基礎鏡像最終結果

在 4.1 小節政策下,分别在 x86_64 以及 aarch64 架構下制作出的 openEuler21.03 創新版本基礎鏡像如下表所示:

架構
openeuler-21.03 129 x86_64 203MB
aarch64 230MB

使用如下指令來擷取最新的 openEuler-21.03 創新版基礎鏡像,并嘗試使用起來吧。

docker pull hub.oepkgs.net/openeuler/openeuler:21.03
           

5. 還能更進一步嗎

我們注意到 x86_64 架構下,openEuler 21.03 版本基礎鏡像大小為 203MB,包含 129 個軟體包;而 CentOS8 基礎鏡像為 209MB,包含 172 個軟體包。大小幾乎相同,但是軟體包的數量卻有較大的差異,openEuler 基礎鏡像“大”在了哪裡?我們進入鏡像裡面深入的看一下。對比運作基礎鏡像容器:

docker run -it --rm hub.oepkgs.net/openeuler/openeuler:21.03 bash
docker run -it --rm centos bash
           

進入容器中檢視各目錄占用空間的大小:

du -h -d 1
           

對比後,我們發現鏡像/usr 目錄占用其 90%以上的空間,是以我們再去檢視/usr 目錄下的目錄空間占用,各子目錄大小如下圖所示:

初探 openEuler 容器鏡像剪裁

可以發現,雖然 openEuler 比 CentOS 基礎鏡像多 43 個軟體包,但是/usr/lib64 庫檔案目錄大小卻幾乎相同。經過繁瑣但并不枯燥的軟體包查詢,能夠逐漸發現 openEuler 基礎鏡像"大"的原因,主要表現為以下三點:

  1. 軟體包的不必要依賴較多
  2. 軟體包庫檔案的多版本共存
  3. 沒有考慮軟體包的精簡版本實作

以下舉例說明以上問題:

  1. openEuler libcurl、libssh 依賴 e2fsprogs 提供的 libcom_err.so,但實際上并不需要除 libcom_err.so 之外其他 e2fsprogs 軟體包提供的檔案。
  2. openEuler 存在多個版本的 libncurses 相關的動态連結庫檔案。
  3. CentOS8 基礎鏡像包含 libcurl-minimal、coreutils-single 軟體包以實作提供相應軟體包基礎能力的精簡版本,openEuler 尚未實作。

雖然存在這些使得 openEuler 基礎鏡像變“大”的問題,但實際上 openEuler 在軟體包建構上已經在不斷優化改進了。舉個 openEuler Embedded SIG 的例子,由于嵌入式環境下使用容器對于鏡像存儲空間大小更加敏感,為了提供更加精簡的基礎鏡像用于嵌入式環境友善業務部署及業務切換,openEuler Embedded SIG 對 openEuler 軟體包進行了拆包優化,

這裡

[10]可以查詢到拆包優化的過軟體包,經過拆分後相應的軟體包依賴關系變得更輕量,做出來的基礎鏡像更小(

可以達到 90 多 MB)。合入 openEuler 主幹的事宜已經在過程中,感興趣的同學可以深入的了解一下。

Back To Square One

為什麼要做系統基礎鏡像?為了給應用提供可運作的、輕量的、一緻的、安全的容器運作環境;為了給開發者提供輕量的,高效的、穩定的、可調試的容器開發環境。

那麼對于應用來說,其實我并不需要我所依賴之外的檔案,對于完全打包的靜态二進制程式甚至我們可以直接使用

FROM scratch來包裝我們的可執行檔案,

distroless

[11]便提供這樣的基礎鏡像最小集适用于應用的運作環境。而對于開發者來說,其更加關注的是開發調試環境的便利統一性,基礎鏡像中包含開發調試過程中的所用到的所有工具。

其實對于普通應用來說,目前似乎也能直接使用開發者環境下的基礎鏡像來打包應用,這是安全性、便利性、存儲空間等諸多因素的權衡。對于這個問題,開源軟體供應鍊點亮計劃 - 暑期 2021 有兩個相關的題目供感興趣的同學選擇,分别是

No.63 openEuler 基礎鏡像優化

[12],

No.66 openEuler 雲原生基礎鏡像集

[13],感興趣的同學可以選擇這些題目與導師一起讨論,一起學習。

總結

使用精簡過後的 openEuler 基礎鏡像,“磐舟”容器平台在生産環境下首次拉取、推送 Base 鏡像以及冷啟動應用的時間平均縮短了 5 秒,同時降低了存儲需求,提高了業務彈性響應速度,為後續 GitOps、FaaS 等新型應用場景的落地奠定了基礎。Cloud Native SIG 也将持續聚焦該方面的工作,為 openEuler 雲原生設施打下更好的基礎。最後,歡迎加入我們

Cloud Native SIG

[14]一起讨論、共建雲原生生态。

參考資料

[1]

openeuler-os-build: https://gitee.com/openeuler/openeuler-os-build

[2]

kiwi: https://github.com/OSInside/kiwi

[3]

鏡像依賴分析工具 idl: https://gitee.com/meilier/idl

[4]

woqidaideshi: https://gitee.com/woqidaideshi

[5]

src-openeuler/libreport 分離 libreport-filesystem 軟體包: https://gitee.com/src-openeuler/libreport/issues/I1SY6B

[6]

src-openeuler/dnf dnf 移除依賴 deltarpm: https://gitee.com/src-openeuler/dnf/issues/I1SY58

[7]

Universal Base Image: https://www.redhat.com/en/blog/introducing-red-hat-universal-base-image

[8]

[9]

openeuler-os-build: https://gitee.com/openeuler/openeuler-os-build/blob/master/script/config/docker_image/config.xml

[10]

這裡: https://gitee.com/organizations/openeuler-embedded/projects

[11]

distroless: https://github.com/GoogleContainerTools/distroless

[12]

No.63 openEuler 基礎鏡像優化: https://gitee.com/openeuler-competition/summer-2021/issues/I3EM49?from=project-issue

[13]

No.66 openEuler 雲原生基礎鏡像集: https://gitee.com/openeuler-competition/summer-2021/issues/I3EN2R?from=project-issue

[14]

Cloud Native SIG: https://gitee.com/openeuler/cloudnative

繼續閱讀