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 中心系統,把一些容器被用戶問的問題做自動檢測和修復,任何修復都做了灰度篩選和灰度部署能力,把一些經常需要答疑的問題做了用戶友好的提示和修復,也減輕了我們自身的運維壓力。
容器使用方式演進
提供容器平臺給應用使用,在容器啟動之前必然有很多平臺相關的邏輯需要處理,這也是我們以前用富容器的原因。
現在隨著基于 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 等技術領域、聚焦云原生流行技術趨勢、云原生大規模的落地實踐,做最懂云原生開發者的技術圈。”
總結
以上是生活随笔為你收集整理的PouchContainer 容器技术演进助力阿里云原生升级的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 云原生应用万节点分钟级分发协同实践
- 下一篇: Go 开发关键技术指南 | Go 面向失