做者 | Liu,Bo 來源|阿里巴巴雲原生公衆號html
前言
常言道,startup 有 startup 的好,大廠有大廠的好,那麼大廠究竟好在哪呢?拿硅谷老牌大廠們 FLG 來講,若是要問最使人懷念的是什麼?Free food 和基礎設施(Infrastructure)必定是會上榜的,二者均極大提高了廣大應用開發者的幸福指數。那麼能不能「讓天下沒有難作的應用」呢?請你們把目光投向正在興起的雲原生生態。mysql
在雲原生生態中,容器服務包括了鏡像和容器引擎兩個部分。其中容器鏡像做爲核心的雲原生應用製品,打包了完整的操做系統和應用運行環境,應用的迭代也由於使用了這種不可變架構而變得更簡單,更頻繁。git
本文將圍繞着容器鏡像這一核心,分享它的相關知識和業界的思考與實踐。github
容器鏡像的概念
1)容器鏡像
容器鏡像有一個官方的類比,"生活中常見的集裝箱",雖然擁有不一樣的規格,但箱子自己是不可變的(Immutable),只是其中裝的內容不一樣。sql
對於鏡像來講,不變的部分包含了運行一個應用軟件(如 mysql)所須要的全部元素。開發者可使用一些工具(如 Dockerfile)構建出本身的容器鏡像,簽名並上傳到互聯網上,而後須要運行這些軟件的人能夠經過指定名稱(如 example.com/my-app)下載、驗證和運行這些容器。docker
2)OCI 標準鏡像規範
在 OCI 標準鏡像規範出臺以前,其實有兩套普遍使用的鏡像規範,分別是 appc 和 docker v2.2,但「合久必分,分久必合」,有意思的是二者的內容已經在各自的發展中逐步同化了,因此 OCI 組織順水推舟地在 docker v2.2 的基礎上推出了 oci image format spec,規定了對於符合規範的鏡像,容許開發者只要對容器打包和簽名一次,就能夠在全部的容器引擎上運行該容器。緩存
這份規範給出了 OCI image 的定義:安全
This specification defines an OCI Image, consisting of a manifest, an image index (optional), a set of filesystem layers, and a configuration.
3)容器的工做流程
一個典型的容器工做流程是從由 developers 製做容器鏡像開始的(build),而後上傳到鏡像存儲中心(ship),最後部署在集羣中(run)。網絡
容器鏡像技術發展中遇到的問題
不得不說,容器鏡像的設計是很出彩的,首先它蘊含了「完整的操做系統就是一個包」的優秀思想,帶着你們跳出了安裝包的思路,又提出了諸如 dockerfile 這樣的提高開發者體驗的 killer features,還能利用分層結構來節約時間空間。架構
不過,"金無足赤,人無完人",優秀的設計並不等於優秀的實踐,下面來聊一聊問題具體出在哪。
1. 容器鏡像使用者
1)問題一:啓動容器慢
容器啓動慢的狀況廣泛發生在當用戶啓動一個很大 size 的容器鏡像時,因爲在容器準備階段須要三步(以 overlayfs 爲例):
-
download 鏡像。
-
unpack 鏡像。
-
使用 overlayfs 將容器可寫層和鏡像中的只讀層聚合起來提供容器運行環境。
其中,download 鏡像時須要 download 整個鏡像,不能實現文件數據按需加載。再加上 download 鏡像自己受限於網絡帶寬的影響,當容器鏡像 size 在到幾個 G 時,下載時間會較長,破壞了容器本來優秀的用戶體驗。
2)問題二:較高的本地存儲成本
不一樣鏡像之間能夠共享的最小單位是鏡像中的層,它的缺點之一是在 deduplication 上的效率是較低的,緣由是:
-
首先,層內部存在重複的數據。
-
其次,層與層之間可能存在大量重複的數據,但即便有微小的差異,也會被做爲不一樣的層。
-
再次,根據 OCI image spec 對刪除文件和 hardlink 的設計,一個鏡像內部可能存在已經被上層刪除的文件仍然存在於下層中,幷包含在鏡像中。
因此,當不一樣鏡像的容器被調度到同一臺機器上運行時,鏡像自己在本地文件系統中所佔的存儲空間是一筆不可忽視的成本開銷。
2. 鏡像提供者側
這裏的提供者主要指容器服務的鏡像中心。
1)問題一:巨大的存儲浪費
-
存在大量類似鏡像 形成這種狀況有兩個緣由:
-
首先,上面提到的層的缺點,在容器鏡像中心會產生許多類似鏡像。
-
其次,OCI image 使用了 tar+gzip 格式來表達鏡像中的層,而 tar 格式並不區分 tar archive entries ordering,這帶來一個問題,即若是用戶在不一樣機器上 build 去同一個鏡像,最終可能會由於使用了不一樣的文件系統而獲得不一樣的鏡像,而後用戶上傳以後,鏡像中心中會存在若干不一樣鏡像的實質內容是徹底相同的狀況。
-
-
鏡像去重效率低:雖然鏡像中心有垃圾回收來實現去重功能,但其仍然以層爲單位,因此只能在有徹底相同 hash value 的層之間去重。
2)問題二:雲原生軟件供應鏈帶來的新需求
隨着時間推移,和軟件供應鏈一塊兒發展的還有對軟件供應鏈環節的多樣性攻擊手段。安全防禦是軟件供應鏈中很是重要的組成,不光體如今對軟件自己的安全加強,也體如今對供應鏈自己的安全加強。而由於應用運行環境被前置到了容器鏡像中,因此對容器鏡像的安全,包括對鏡像的漏洞掃描和簽名成爲了容器服務提供者的必要能力。
對容器鏡像的思考和討論
1. 業界的嘗試
針對前面所述的問題,業界大小廠也是集思廣益,各顯神通,下面提幾個典型的項目:
1)CernVM-FS
使用了 fuse 按需從遠程加載須要的數據。
2)Slacker
經過設計一個鏡像的 benchmark 貢獻了幾個有意思的理論基礎:
-
事實上,容器啓動時間很長。
-
啓動時數據讀寫放大係數很大(啓動時中只使用 6% 的數據)。
-
分析了 57 個 docker image 的 layer 數量,發現一半以上的 image 的 layer 數量大於 9。
Slacker 最終使用了按需加載和減小鏡像層數將啓動速度提升了 5-20 倍。
3)SquashFs
Oracle 使用 Linux SquashFS 來替代 targz 存儲容器 image layer 的內容,去掉了 unpack tar 的環節。
2. OCI 社區中的討論
自 2019 年開始,對於鏡像自己的吐槽慢慢多了起來,發酵了一年多,OCI 社區以爲時機成熟了,從 2020 年 6 月開始,花了一個多月時間密集討論了當前 OCI 鏡像規範的缺陷,以及 OCIv2 鏡像格式(*)須要知足哪些要求。
(*)OCIv2 在這裏只是一個宣傳命名,實際上 OCIv2 是當前 OCI 鏡像規範的改進,而不會是一個全新的鏡像規範。
1)OCI 鏡像規範的缺陷
通過討論得出目前的缺陷主要有兩點:
-
tar 格式標準
- tar 格式並不區分 tar archive entries ordering,這帶來一個問題,即若是用戶在不一樣機器上去 build 同一個鏡像,最終可能會由於使用了不一樣的文件系統而獲得不一樣的鏡像,好比在文件系統 A 上的 order 是 foo 在 bar 以前進入 tar,在文件系統 B 上的 order 是 bar 在 foo 以前進入 tar,那麼這兩個鏡像是不一樣的。
- 當 tar 被 gzip 壓縮過以後不支持 seek,致使 run container 以前必須先下載並解壓 targz 的 image layers,而不能實現文件數據按需加載。
-
以層爲鏡像的基本單位
- 內容冗餘:不一樣層之間相同信息在傳輸和存儲時都是冗餘內容,在不讀取內容的時候沒法判斷到這些冗餘的存在。
- 沒法並行:單一層是一個總體,對同一個層既沒法並行傳輸,也不能並行提取。
- 沒法進行小塊數據的校驗,只有完整的層下載完成以後,才能對整個層的數據作完整性校驗。
- 其餘一些問題:好比,跨層數據刪除難以完美處理。
2)下一代鏡像格式的要求
此次鏡像格式大討論從一個郵件和一份共享文檔開始,並促成了屢次在線的 OCI 社區討論會議。最後的結論也很鼓舞人心,在這份共享文檔中能夠找到對 OCIv2 鏡像格式須要知足的要求的詳細描述。咱們能夠將這些要求分類爲:
(*): 諸如 file timestamp 等只在一個特定機器上有意義的 metadata 是沒有必要存在於鏡像中的。
能夠看出,上面這些要求明確了容器鏡像的下一步重點在易用、效率、安全三個方面,達到在 "build - ship - run" 這三個階段協同優化的目的。
3. 阿里雲在容器鏡像上的思考
阿里雲一直積極地推進和發展雲原生生態,提供了基礎設施「阿里雲容器鏡像服務(ACR)」做爲用戶雲原生容器化的第一站,負責提供容器鏡像、Helm Chart 等 OCI Artifacts 管理和分發服務。同時咱們也在結合容器業務現狀深化對容器鏡像格式的理解,不斷地總結什麼是知足發展需求的容器鏡像格式,這裏能夠歸納爲如下幾點,新的鏡像格式須要:
- 知足容器 "build once, run anywhere" 的理念。
- 實如今鏡像中心和容器運行結點上存儲資源上的高效使用。
- 在容器鏡像的全鏈路上(build, ship, run)比現有的 OCI 鏡像格式速度更快。
- 可以擴展在安全上的能力。
- 最大程度兼容已有基礎設施,普惠大多數用戶。
阿里雲沙箱容器的鏡像加速
相比於社區的討論重點放在了新的鏡像格式的設計上,阿里雲更關心如何設計出一套優化全鏈路的鏡像方案,爲客戶帶來可以應用在生產中的價值。
在明確以上在技術發展過程當中產生的需求以後,咱們爲阿里雲沙箱容器設計了新的鏡像格式 Rafs,併爲 CNCF 下的 Dragonfly 項目引入了容器鏡像服務,它可以極大縮短鏡像下載時間,並提供端到端的鏡像數據一致性校驗,從而讓用戶可以更安全快捷地管理容器應用。
1. Rafs: 鏡像格式
Rafs 把一個容器鏡像只分紅元數據和數據兩層。其中:
- 元數據層:元數據層是一顆自校驗的哈希樹。每一個文件和目錄都是哈希樹中的一個附帶哈希值的節點。一個文件節點的哈希值是由文件的數據肯定,一個目錄節點的哈希值則是由該目錄下全部文件和目錄的哈希值肯定。
- 數據層:每一個文件的數據被按照固定大小切片並保存到數據層中。數據切片能夠在不一樣文件以及不一樣鏡像中的不一樣文件共享。
2. Nydus: Dragonfly 的容器鏡像服務
除了使用上面的鏡像格式 Rafs,Nydus 還包含一個負責解析容器鏡像的 FUSE 用戶態文件系統進程。
nydus 可以解析 FUSE 或者 virtiofs 協議來支持傳統的 runC 容器或者阿里雲沙箱容器。容器倉庫、OSS 對象存儲、NAS、以及 Dragonfly 的超級節點和 peer 節點均可以做爲 nydus 的鏡像數據源。同時,nydus 還能夠配置一個本地緩存,從而避免每次啓動都從遠端數據源拉取數據。
基於這個設計架構,nydus 分別在 build, ship, run 和兼容性方面提供下面這些優化:
3. 爲何選擇基於文件的設計
在設計之初,Nydus 選擇了基於文件的設計而不是基於塊的設計,爲何這樣作呢?
主要的緣由是,咱們想在鏡像加速的基礎上作基於容器特色的附加能力,這一切都創建在可以獲取到鏡像中的文件元數據;而基於塊的設計只使用 disk LBA,自然的沒法獲取其上層(即文件系統)中的信息。
有了文件元數據以後,咱們輕鬆地實現瞭如下幾個增值功能:
- 鏡像優化建議:在 build container 環節,提示用戶有哪些文件是根本沒有訪問過的,能夠考慮藉此來優化鏡像。
- 預讀:在 run container 環節預加載,猜到用戶要讀文件,那就預先在讀操做發生以前送過去,從而優化訪問速度。
- 安全審計:在 run container 環節,若是一個容器訪問鏡像內容的模式和其餘容器產生了明顯差別,那麼,這有多是一個安全性風險。
- 變動風險發現:在 run container 環節,若是一個鏡像升級以後,發現它訪問內容的模式和以前發生了明顯差別,那麼,要麼是程序本身有意變了,要麼就多是引入 bug 了,這時能夠考慮提醒開發者這個變化。
總結
OCI image 分層鏡像機制雖然極大地方便了開發,但在大規模集羣運行時,也有頗多不足,對此,OCI 鏡像社區也在尋求着如何利用鏡像內容可感知性,讓它更加快速、節省資源,也更加安全。阿里雲在這個基礎上本着爲客戶帶來價值的原則,提出了公有云上對鏡像的穩定性、預讀等需求,併爲阿里雲沙箱容器研發出了相應的的鏡像加速方案,實現 "build-ship-run" 整個鏡像鏈路上的統一優化,讓用戶不光聽着熱鬧,也能用着開心,切實獲得雲原生基礎設施發展的紅利。
點擊參與「容器鏡像使用調查問卷」填寫,將有 10 位隨機獲贈阿里雲容器鏡像服務(企業版)ACR EE 50 元優惠券哦~