1. 程式人生 > >PouchContainer 容器技術演進助力阿里雲原生升級

PouchContainer 容器技術演進助力阿里雲原生升級

點選下載《不一樣的 雙11 技術:阿里巴巴經濟體雲原生實踐》

作者 | 楊育兵(沈陵) 阿里巴巴高階技術專家

我們從 2016 年開始在集團推廣全面的映象化容器化,今年是集團全面映象化容器化後的第 4 個 雙11,PouchContainer 容器技術已經成為集團所有線上應用執行的執行時底座和運維載體,每年 雙11 都有超過百萬的 PouchContainer 容器同時線上,提供電商和所有相關的線上應用平穩執行的載體,保障大促購物體驗的順滑。

我們通過 PouchContainer 容器執行時這一層標準構建了應用開發和基礎設施團隊的標準介面,每年應用都有新的需求、新的變化,同時基礎設施也有上雲/混部/神龍/儲存計算分離/網路變革這些升級,兩邊平行演進,互不干擾。技術設施和 PouchContainer 自身都做了很大的架構演進,這些很多的架構和技術演進對應用開發者都是無感知的。

在容器技術加持的雲原生形成趨勢的今天,PouchContainer 容器技術支援的業務方也不再只有集團電商業務和線上業務了,我們通過標準化的演進,把所有定製功能做了外掛化,適配了不同場景的需要。除了集團線上應用,還有執行在離線排程器上面的離線 job 類任務、跑在搜尋排程器上面的搜尋廣告應用、跑在 SAE/CSE 上面的 Serverless 應用、專有云產品及公有云(ACK+CDN)等場景,都使用了 PouchContainer 提供的能力。

執行時的演進

2015 年之前,我們用的執行時是 LXC,PouchContainer 為了在映象化後能夠平滑接管原來的 T4 容器,在 LXC 中支援新的映象組裝方式,並支援互動式的 exec 和內建的網路模式。

隨著雲原生的程序,我們在使用者無感知的情況下對執行時做了 containerd+runc 的支援,用標準化的方式加內部功能外掛,實現了內部功能特性的支援和被各種標準化運維繫統無縫整合的目標。

無論是 LXC 還是 runc 都是讓所有容器共享 Linux 核心,利用 cgroup 和 namespace 來做隔離,對於強安全場景和強隔離場景是不適用的。為了容器這種開發和運維友好的交付形式能給更多場景帶來收益,我們很早就開始探索這方面的技術,和集團 os 創新團隊以及螞蟻 os 虛擬化團隊合作共建了 kata 安全容器和 gvisor 安全容器技術,在容器生態嫁接,磁碟、網路和系統呼叫效能優化等方面都做了很多的優化。在相容性要求高的場景我們優先推廣 kata 安全容器,已經支援了 SAE 和 ACK 安全容器場景。在語言和運維習慣確定的場景,我們也在 618 大促時上線了一些合適的電商使用了 gvisor 的執行時隔離技術,穩定性和效能都得到了驗證。

為了一部分專有云場景的實施,我們今年還首次支援了 Windows 容器執行時,在容器依賴相關的部署、運維方面做了一些探索,幫助敏捷版專有云拿下了一些客戶。

除了安全性和隔離性,我們的執行時演進還保證了標準性,今年最新版本的 PouchContainer 把 diskquota、lxcfs、dragonfly、DADI 這些特性都做成了可插拔的外掛,不需要這些功能的場景可以完全不受這些功能程式碼的影響。甚至我們還對一些場景做了 containerd 發行版,支援純粹的標準 CRI 介面和豐富的執行時。

映象技術的演進

映象化以後必然會引入映象分發的效率方面的困難,一個是速度另一個是穩定性,讓釋出擴容流程不增加太多時間的情況下,還要保證中心節點不被壓垮。
PouchContainer 在一開始就支援了使用 Dragonfly 來做 P2P 的映象分發,就是為了應對這種問題,這是我們的第一代映象分發方案。在研發域我們也對映象分層的最佳實踐做了推廣,這樣能最大程度的保證基礎環境不變時每次下載的映象層最小。映象加速要解決的問題有:build 效率、push 效率、pull 效率、解壓效率以及組裝效率。第一代映象加速方案,結合 Dockerfile 的最佳實踐解決了 build 效率和 pull 效率和中心壓力。

第一代映象分發的缺點是無論使用者啟動過程中用了多少映象資料,在啟動容器之前就需要把所有的映象檔案都拉到本地,在很多場景下都是浪費的,特別影響的是擴容場景。所以第二代的映象加速方案,我們調研了阿里雲的盤古,盤古的打快照、mount、再打快照這種使用方式完美匹配打映象和分發的流程;能做到秒級映象 pull,因為 pull 映象時只需要鑑權,下載映象 manifest,然後 mount 盤古,也能做到映象內容按需讀取。

2018 年 雙11,我們小規模上線了盤古遠端映象,也驗證了我們的設計思路,這一代的映象加速方案結合新的 overlay2 技術在第一代的基礎上又解決了PouchContainer 效率/pull 效率/解壓效率和組裝效率。

但是也存在一些問題。首先映象資料沒有儲存在中心映象倉庫中,只有 manifest 資訊,這樣映象的分發範圍就受限,在哪個盤古叢集做的映象,就必須在那個盤古叢集所在的阿里雲集群中使用這個映象;其次沒有 P2P 的能力,在大規模使用時對盤古後端的壓力會很大,特別是離線場景下由於記憶體壓力導致很多程序的可執行檔案的 page cache 被清理,然後需要重新 load 這種場景,會給盤古後端帶來更大的壓力。基於這兩個原因,我們和 ContainerFS 團隊合作共建了第三代映象分發方案:DADI(基於塊裝置的按需 P2P 載入技術,後面也有計劃開源這個映象技術)。

DADI 在構建階段保留了映象的多層結構,保證了映象在多次構建過程中的可重用性,並索引了每個檔案在每層的offset 和 length,推送階段還是把映象推送到中心映象倉庫中,保證在每個機房都能拉取到這個映象。在每個機房都設定了超級節點做快取,每一塊內容在特定的時間段內,都只從映象倉庫下載一次。如果有時間做映象預熱,像 雙11 這種場景,預熱階段就是從中心倉庫中把映象預熱到本地機房的超級節點,後面的同機房的資料傳輸會非常快。映象 pull 階段只需要下載映象的 manifest 檔案(通常只有幾 K大小),速度非常快,啟動階段 DADI 會給每個容器生成一個塊裝置,這個塊裝置的 chunk 讀取是按需從超級節點或臨近節點 P2P 讀取的內容,這樣就保證了容器啟動階段節點上只讀取了需要的部分內容。為了防止容器執行過程中出現 iohang,我們在容器啟動後會在後臺把整個映象的內容全部拉到 node 節點,享受超快速啟動的同時最大程度地避免後續可能出現的 iohang。

使用 DADI 映象技術後的今年 雙11 高峰期,每次有人在群裡面說有擴容任務,我們值班的同學去看工單時,基本都已經擴容完成了,擴容體驗做到了秒級。

網路技術演進

PouchContainer 一開始的網路功能是揉合在 PouchContainer 自己的程式碼中的,用整合程式碼的方式支援了集團各個時期的網路架構,為了向標準化和雲原生轉型,在應用無感知的情況下,我們在 Sigma-2.0 時代使用 libnetwork 把集團現存的各種網路機架構都統一做了 CNM 標準的網路外掛,沉澱了集團和專有云都在用的阿里巴巴自己的網路外掛。在線上排程系統推廣期間,CNM 的網路外掛已經不再適用,為了不需要把所有的網路外掛再重新實現一遍,我們對原來的網路外掛做了包裝,沉澱了 CNI 的網路外掛,把 CNM 的介面轉換為 CNI 的介面標準。

內部的網路外掛支援的主流單機網路拓撲演進過程如下圖所示:

從單機拓撲能看出來使用神龍 eni 網路模式可以避免容器再做網橋轉接,但是用神龍的彈性網絡卡和CNI網路外掛時也有坑需要避免,特別是 eni 彈性網絡卡是擴容容器時才熱插上來的情況時。建立 eni 網絡卡時,udevd 服務會分配一個唯一的 id N,比如 ethN,然後容器 N 啟動時會把 ethN 移動到容器 N 的 netns,並從裡面改名為 eth0。容器 N 停止時,eth0 會改名為 ethN 並從容器 N 的 netns 中移動到宿主機的 netns 中。

這個過程中,如果容器 N 沒有停止時又分配了一個容器和 eni 到這臺宿主機上,udevd 由於看不到 ethN 了,它有可能會分配這個新的 eni 的名字為 ethN。容器 N 停止時,把 eth0 改名為 ethN 這一步能成功,但是移動到宿主機根 netns 中這一步由於名字衝突會失敗,導致 eni 網絡卡洩漏,下一次容器 N 啟動時找不到它的 eni 了。可以通過修改 udevd 的網絡卡名字生成規則來避免這個坑。

運維能力演進

PouchContainer 容器技術支援了百萬級的線上容器同時執行,經常會有一些問題需要我們排查,很多都是已知的問題,為了解決這個困擾,我還寫了 PouchContainer 的一千個細節以備使用者查詢,或者重複問題問過來時直接交給使用者。但是 PouchContainer 和相關鏈路本身穩定性和運維能力的提升才是最優的方法。今年我們建設了 container-debugger 和 NodeOps 中心繫統,把一些容器被使用者問的問題做自動檢測和修復,任何修復都做了灰度篩選和灰度部署能力,把一些經常需要答疑的問題做了使用者友好的提示和修復,也減輕了我們自身的運維壓力。

  1. 內部的中心化日誌採集和即時分析
  2. 自帶各模組的健康和保活邏輯
  3. 所有模組提供 Prometheus 介面,暴露介面成功率和耗時
  4. 提供常見問題自動巡檢修復的工具
  5. 運維經驗積累,對使用者問題提供修復建議
  6. 提供灰度工具,任何變更通過金絲雀逐步灰度
  7. 剖析工具,流程中插入程式碼的能力
  8. Pouch 具備一鍵釋出能力,快速修復

容器使用方式演進

提供容器平臺給應用使用,在容器啟動之前必然有很多平臺相關的邏輯需要處理,這也是我們以前用富容器的原因。

  1. 安全相關:安全路由生成、安全指令碼配置
  2. cpushare 化相關配置:tsar 和 nginx 配置
  3. 運維agent 更新相關:運維agent 更新相對頻繁,基礎映象更新特別慢,不能依賴於基礎映象更新來更新運維agent
  4. 配置相關邏輯:同步頁頭頁尾,隔離環境支援, 強弱依賴外掛部署
  5. SN 相關:模擬寫 SN 到/dev/mem,保證 dmidecode 能讀到正確的 SN
  6. 運維相關的的 agent 拉起,很多運維繫統都依賴於在節點上面有一個 agent,不管這個節點是容器/ecs 還是物理機
  7. 隔離相關的配置:比如 nproc 這個限制是在使用者上面的,用統一個映象的容器不能使用統一 uid 不然無法隔離 nproc
    現在隨著基於 K8s 編排排程系統的推廣,我們有了 Pod 能力,可以把一些預置邏輯放到前置 hook 中去執行,當然富容器可以瘦下來,還要依賴於運維 agent 可以從主容器中拆出來,那些只依賴於 volume 共享就能跑起來的 agent 可以先移動到 sidecar 裡面去,這樣就可以把運維容器和業務主容器分到不同的容器裡面去,一個 Pod 多個容器在資源隔離上面分開,主容器是 Guaranteed 的 QOS,運維容器是 Burstable 的 QOS。同時在 kubelet 上支援 Pod 級別的資源管控,保證這個 Pod 整體是 Guaranteed 的同時,限制了整個 pod 的資源使用量不超過應用單例項的申請資源。

還有一些 agent 不是隻做 volume 共享就可以放到 sidecar 的運維容器中的,比如 arthas 需要能 attach 到主容器的程序上去,還要能 load 主容器中非 volume 路徑上面的 jar 檔案才能正常工作。對於這種場景 PouchContainer 容器也提供了能讓同 Pod 多容器做一些 ns 共享的能力,同時配合 ns 穿越來讓這些 agent 可以在部署方式和資源隔離上是和主容器分離的,但是在執行過程中還可以做它原來可以做的事情。

容器技術繼續演進的方向

可插拔的外掛化的架構和更精簡的呼叫鏈路在容器生態裡面還是主流方向,kubelet 可以直接去呼叫 pouch-containerd 的 CRI 介面,可以減少中間一個元件的遠端呼叫,不過 CRI 介面現在還不夠完善,很多運維相關的指令都沒有,logs 介面也要依賴於 container API 來實現,還有執行環境和構建環境分離,這樣使用者就不需要到宿主機上面執行 build。所有的運維繫統也不再依賴於 container API。在這些約束下我們可以做到減少對一箇中間元件的系統呼叫,直接用 kubelet 去呼叫 pouch-containerd 的 CRI 介面。

現在每個應用都有很多的 Dockerifle,怎麼讓 Dockerfile 更有表達能力,減少 Dockerfile 數量。構建的時候併發構建也是一個優化方向,buildkit 在這方面是可選的方案,Dockerfile 表達能力的欠缺也需要新的解決方案,buildkit 中間態的 LLB 是 go 程式碼,是不是可以用 go 程式碼來代替 Dockerfile,定義更強表達能力的 Dockerfile 替代品。

容器化是雲原生的關鍵路徑,容器技術在執行時和映象技術逐漸趨於穩定的情況下,熱點和開發者的目光開始向上層轉移,K8s 和基於其上的生態成為容器技術未來能產生更多創新的領域,PouchContainer 技術也在向著更雲原生、更好適配 K8s 生態的方向發展,網路、diskquota、試圖隔離等 PouchContainer 的外掛,在 K8s 生態系統中適配和優化也我們後面的方向之一。

本書亮點

  • 雙11 超大規模 K8s 叢集實踐中,遇到的問題及解決方法詳述
  • 雲原生化最佳組合:Kubernetes+容器+神龍,實現核心系統 100% 上雲的技術細節
  • 雙 11 Service Mesh 超大規模落地解決方案

“阿里巴巴雲原生關注微服務、Serverless、容器、Service Mesh 等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,做最懂雲原生開發者的技術圈。”