本文根據美團基礎架構部/容器研發中心技術總監歐陽堅在2018 QCon(全球軟件開發大會)上的演講內容整理而成。git
美團的容器集羣管理平臺叫作HULK。漫威動畫裏的HULK在發怒時會變成「綠巨人」,它的這個特性和容器的「彈性伸縮」很像,因此咱們給這個平臺起名爲HULK。貌似有一些公司的容器平臺也叫這個名字,純屬巧合。github
2016年,美團開始使用容器,當時美團已經具有必定的規模,在使用容器以前就已經存在的各類系統,包括CMDB、服務治理、監控告警、發佈平臺等等。咱們在探索容器技術時,很難放棄原有的資產。因此容器化的第一步,就是打通容器的生命週期和這些平臺的交互,例如容器的申請/建立、刪除/釋放、發佈、遷移等等。而後咱們又驗證了容器的可行性,證明容器能夠做爲線上核心業務的運行環境。算法
2018年,通過兩年的運營和實踐探索,咱們對容器平臺進行了一次升級,這就是容器集羣管理平臺HULK 2.0。docker
美團的容器使用情況是:目前線上業務已經超過3000個服務,容器實例數超過30000個,不少大併發、低延時要求的核心鏈路服務,已經穩定地運行在HULK之上。本文主要介紹咱們在容器技術上的一些實踐,屬於基礎系統優化和打磨。編程
首先介紹一下美團容器平臺的基礎架構,相信各家的容器平臺架構大致都差很少。緩存
首先,容器平臺對外對接服務治理、發佈平臺、CMDB、監控告警等等系統。經過和這些系統打通,容器實現了和虛擬機基本一致的使用體驗。研發人員在使用容器時,能夠和使用VM同樣,不須要改變原來的使用習慣。安全
此外,容器提供彈性擴容能力,能根據必定的彈性策略動態增長和減小服務的容器節點數,從而動態地調整服務處理能力。這裏還有個特殊的模塊——「服務畫像」,它的主要功能是經過對服務容器實例運行指標的蒐集和統計,更好的完成調度容器、優化資源分配。好比能夠根據某服務的容器實例的CPU、內存、IO等使用狀況,來分辨這個服務屬於計算密集型仍是IO密集型服務,在調度時儘可能把互補的容器放在一塊兒。再好比,咱們能夠知道某個服務的每一個容器實例在運行時會有大概500個進程,咱們就會在建立容器時,給該容器加上一個合理的進程數限制(好比最大1000個進程),從而避免容器在出現問題時,佔用過多的系統資源。若是這個服務的容器在運行時,忽然申請建立20000個進程,咱們有理由相信是業務容器遇到了Bug,經過以前的資源約束對容器進行限制,併發出告警,通知業務及時進行處理。性能優化
往下一層是「容器編排」和「鏡像管理」。容器編排解決容器動態實例的問題,包括容器什麼時候被建立、建立到哪一個位置、什麼時候被刪除等等。鏡像管理解決容器靜態實例的問題,包括容器鏡像應該如何構建、如何分發、分發的位置等等。服務器
最下層是咱們的容器運行時,美團使用主流的Linux+Docker容器方案,HULK Agent是咱們在服務器上的管理代理程序。網絡
把前面的「容器運行時」具體展開,能夠看到這張架構圖,按照從下到上的順序介紹:
美團主要使用了CentOS系列的開源組件,由於咱們認爲Red Hat有很強的開源技術實力,比起直接使用開源社區的版本,咱們但願Red Hat的開源版本可以幫助解決大部分的系統問題。咱們也發現,即便部署了CentOS的開源組件,仍然有可能會碰到社區和Red Hat沒有解決的問題。從某種程度上也說明,國內大型互聯公司在技術應用的場景、規模、複雜度層面已經達到了世界領先的水平,因此纔會先於社區、先於Red Hat的客戶遇到這些問題。
在容器技術自己,咱們主要遇到了4個問題:隔離、穩定性、性能和推廣。
容器本質上是把系統中爲同一個業務目標服務的相關進程合成一組,放在一個叫作namespace的空間中,同一個namespace中的進程可以互相通訊,但看不見其餘namespace中的進程。每一個namespace能夠擁有本身獨立的主機名、進程ID系統、IPC、網絡、文件系統、用戶等等資源。在某種程度上,實現了一個簡單的虛擬:讓一個主機上能夠同時運行多個互不感知的系統。
此外,爲了限制namespace對物理資源的使用,對進程能使用的CPU、內存等資源須要作必定的限制。這就是Cgroup技術,Cgroup是Control group的意思。好比咱們常說的4c4g的容器,其實是限制這個容器namespace中所用的進程,最多可以使用4核的計算資源和4GB的內存。
簡而言之,Linux內核提供namespace完成隔離,Cgroup完成資源限制。namespace+Cgroup構成了容器的底層技術(rootfs是容器文件系統層技術)。
以前一直和虛擬機打交道,但直到用上容器,才發如今容器裏面看到的CPU、Memory的信息都是服務器主機的信息,而不是容器自身的配置信息。直到如今,社區版的容器仍是這樣,好比一個4c4g的容器,在容器內部能夠看到有40顆CPU、196GB內存的資源,這些資源實際上是容器所在宿主機的信息。這給人的感受,就像是容器的「自我膨脹」,以爲本身能力很強,但實際上並無,還會帶來不少問題。
上圖是一個內存信息隔離的例子。獲取系統內存信息時,社區Linux不管在主機上仍是在容器中,內核都是統一返回主機的內存信息,若是容器內的應用,按照它發現的宿主機內存來進行配置的話,實際資源是遠遠不夠的,致使的結果就是:系統很快會發生OOM異常。
咱們作的隔離工做,是在容器中獲取內存信息時,內核根據容器的Cgroup信息,返回容器的內存信息(相似LXCFS的工做)。
CPU信息隔離的實現和內存的相似,再也不贅述,這裏舉一個CPU數目影響應用性能例子。
你們都知道,JVM GC(垃圾對象回收)對Java程序執行性能有必定的影響。默認的JVM使用公式「ParallelGCThreads = (ncpus <= 8) ? ncpus : 3 + ((ncpus * 5) / 8)」 來計算作並行GC的線程數,其中ncpus是JVM發現的系統CPU個數。一旦容器中JVM發現了宿主機的CPU個數(一般比容器實際CPU限制多不少),這就會致使JVM啓動過多的GC線程,直接的結果就致使GC性能降低。Java服務的感覺就是延時增長,TP監控曲線突刺增長,吞吐量降低。針對這個問題有各類解法:
有一段時間,咱們的容器是使用root權限進行運行,實現的方法是在docker run的時候加入‘privileged=true’參數。這種粗放的使用方式,使容器可以看到所在服務器上全部容器的磁盤,致使了安全問題和性能問題。安全問題很好理解,爲何會致使性能問題呢?能夠試想一下,每一個容器都作一次磁盤狀態掃描的場景。固然,權限過大的問題還體如今能夠隨意進行mount操做,能夠隨意的修改NTP時間等等。
在新版本中,咱們去掉了容器的root權限,發現有一些反作用,好比致使一些系統調用失敗。咱們默認給容器額外增長了sys_ptrace和sys_admin兩個權限,讓容器能夠運行GDB和更改主機名。若是有特例容器須要更多的權限,能夠在咱們的平臺上按服務粒度進行配置。
Linux有兩種IO:Direct IO和Buffered IO。Direct IO直接寫磁盤,Buffered IO會先寫到緩存再寫磁盤,大部分場景下都是Buffered IO。
咱們使用的Linux內核3.X,社區版本中全部容器Buffer IO共享一個內核緩存,而且緩存不隔離,沒有速率限制,致使高IO容器很容易影響同主機上的其餘容器。Buffer IO緩存隔離和限速在Linux 4.X裏經過Cgroup V2實現,有了明顯的改進,咱們還借鑑了Cgroup V2的思想,在咱們的Linux 3.10內核實現了相同的功能:每一個容器根據本身的內存配置有對應比例的IO Cache,Cache的數據寫到磁盤的速率受容器Cgroup IO配置的限制。
Docker自己支持較多對容器的Cgroup資源限制,可是K8s調用Docker時能夠傳遞的參數較少,爲了下降容器間的互相影響,咱們基於服務畫像的資源分配,對不一樣服務的容器設定不一樣的資源限制,除了常見的CPU、內存外,還有IO的限制、ulimit限制、PID限制等等。因此咱們擴展了K8s來完成這些工做。
業務在使用容器的過程當中產生core dump文件是常見的事,好比C/C++程序內存訪問越界,或者系統OOM的時候,系統選擇佔用內存多的進程殺死,默認都會生成一個core dump文件。
社區容器系統默認的core dump文件會生成在宿主機上,因爲一些core dump文件比較大,好比JVM的core dump一般是幾個GB,或者有些存在Bug的程序,其頻發的core dump很容易快速寫滿宿主機的存儲,而且會致使高磁盤IO,也會影響到其餘容器。還有一個問題是:業務容器的使用者沒有權限訪問宿主機,從而拿不到dump文件進行下一步的分析。
爲此,咱們對core dump的流程進行了修改,讓dump文件寫到容器自身的文件系統中,而且使用容器本身的Cgroup IO吞吐限制。
咱們在實踐中發現,影響系統穩定性的主要是Linux Kernel和Docker。雖然它們自己是很可靠的系統軟件,可是在大規模、高強度的場景中,仍是會存在一些Bug。這也從側面說明,咱們國內互聯網公司在應用規模和應用複雜度層面也屬於全球領先。
在內核方面,美團發現了Kernel 4.x Buffer IO限制的實現問題,獲得了社區的確認和修復。咱們還跟進了一系列CentOS的Ext4補丁,解決了一段時間內進程頻繁卡死的問題。
咱們碰到了兩個比較關鍵的Red Hat版Docker穩定性問題:
在Docker服務重啓之後,Docker exec沒法進入容器,這個問題比較複雜。在解決以前咱們用nsenter來代替Docker exec並積極反饋給RedHat。後來Red Hat在今年初的一個更新解決了這個問題。access.redhat.com/errata/RHBA…
是在特定條件下Docker Daemon會Panic,致使容器沒法刪除。通過咱們本身Debug,並對比最新的代碼,發現問題已經在Docker upstream中獲得解決,反饋給Red Hat也很快獲得瞭解決。github.com/projectatom…
面對系統內核、Docker、K8s這些開源社區的系統軟件,存在一種觀點是:咱們不須要本身分析問題,只須要拿社區的最新更新就好了。可是咱們並不認同,咱們認爲技術團隊自身的能力很重要,主要是以下緣由:
美團在解決開源系統問題時,通常會經歷五個階段:本身深挖、研發解決、關注社區、和社區交互,最後貢獻給社區。
容器平臺性能,主要包括兩個方面性能:
上圖是咱們CPU分配的一個例子,咱們採用的主流服務器是兩路24核服務器,包含兩個Node,每一個12核,算上超線程共48顆邏輯CPU。屬於典型的NUMA(非一致訪存)架構:系統中每一個Node有本身的內存,Node內的CPU訪問本身的內存的速度,比訪問另外一個Node內存的速度快不少(差一倍左右)。
過去咱們曾經遇到過網絡中斷集中到CPU0上的問題,在大流量下可能致使網絡延時增長甚至丟包。爲了保證網絡處理能力,咱們從Node0上劃出了8顆邏輯CPU用來專門處理網絡中斷和宿主機系統上的任務,例如鏡像解壓這類高CPU的工做,這8顆邏輯CPU不運行任何容器的Workload。
在容器調度方面,咱們的容器CPU分配儘可能不跨Node,實踐證實跨Node訪問內存對應用性能的影響比較大。在一些計算密集型的場景下,容器分配在Node內部會提高30%以上的吞吐量。按Node的分配方案也存在必定的弊端:會致使CPU的碎片增長,爲了更高效地利用CPU資源。在實際系統中,咱們會根據服務畫像的信息,分配一些對CPU不敏感的服務容器跨Node使用CPU資源。
上圖是一個真實的服務在CPU分配優化先後,響應延時的TP指標線對比。能夠看到TP999線降低了一個數量級,全部的指標都更加平穩。
針對文件系統的性能優化,第一步是選型,根據統計到的應用讀寫特徵,咱們選擇了Ext4文件系統(超過85%的文件讀寫是對小於1M文件的操做)。
Ext4文件系統有三種日誌模式:
咱們選擇了Writeback模式(默認是oderded),它在幾種掛載模式中速度最快,缺點是:發生故障時數據很差恢復。咱們大部分容器處於無狀態,故障時在別的機器上再拉起一臺便可。所以咱們在性能和穩定性中,選擇了性能。容器內部給應用提供可選的基於內存的文件系統tmpfs,能夠提高有大量臨時文件讀寫的服務性能。
如上圖所示,在美團內部建立一個虛擬機至少經歷三步,平均時間超過300秒。使用鏡像建立容器平均時間23秒。容器的靈活、快速獲得了顯著的體現。
容器擴容23秒的平均時間包含了各個部分的優化,如擴容鏈路優化、鏡像分發優化、初始化和業務拉起優化等等。接下來,本文主要介紹一下咱們作的鏡像分發和解壓相關的優化。
上圖是美團容器鏡像管理的整體架構,其特色以下:
鏡像分發是影響容器擴容時長的一個重要環節。
從上圖能夠看出,隨着分發服務器數目的增長,原有分發時間也快速增長,而P2P鏡像分發時間基本上保持穩定。
Docker的鏡像拉取是一個並行下載,串行解壓的過程,爲了提高解壓的速度,咱們美團也作了一些優化工做。
對於單個層的解壓,咱們使用並行解壓算法替換Docker默認的串行解壓算法,實現上是使用pgzip替換gzip。
Docker的鏡像具備分層結構,對鏡像層的合併是一個「解壓一層合併一層,再解壓一層,再合併一層」的串行操做。實際上只有合併是須要串行的,解壓能夠並行起來。咱們把多層的解壓改爲並行,解壓出的數據先放在臨時存儲空間,最後根據層之間的依賴進行串行合併。前面的改動(並行解壓全部的層到臨時空間)致使磁盤IO的次數增長了近一倍,也會致使解壓過程不夠快。因而,咱們使用基於內存的Ramdisk來存儲解壓出來的臨時文件,減輕了額外文件寫帶來的開銷。作了上面這些工做之後,咱們又發現,容器的分層也會影響下載加解壓的時間。上圖是咱們簡單測試的結果:不管對於怎麼分層的鏡像並行解壓,都能大幅提高解壓時間,對於層數多的鏡像提高更加明顯。
推廣容器的第一步是能說出容器的優點,咱們認爲容器有以下優點:
這三個特性的組合,能夠給業務帶來更大的靈活度和更低的計算成本。
由於容器平臺自己是一個技術產品,它的客戶是各個業務的RD團隊,所以咱們須要考慮下面一些因素:
Docker容器加Kubernetes編排是當前容器雲的主流實踐之一,美團容器集羣管理平臺HULK也採用了這樣的方案。本文主要分享了美團在容器技術上作的一些探索和實踐。內容主要涵蓋美團容器雲在Linux Kernel、Docker和Kubernetes層面作的一些優化工做,以及美團內部推進容器化進程的一些思考,歡迎你們跟咱們交流、探討。
歐陽堅,2006年畢業於清華大學計算機系,擁有12年數據中心開發管理經驗。曾任VMware中國Staff Engineer,無雙科技CTO,中科睿光首席架構師。現任美團基礎架構部/容器研發中心技術總監,負責美團容器化的相關工做。
美團點評基礎架構團隊誠招Java高級、資深技術專家,Base北京、上海。咱們是集團致力於研發公司級、業界領先基礎架構組件的核心團隊,涵蓋分佈式監控、服務治理、高性能通訊、消息中間件、基礎存儲、容器化、集羣調度等技術領域。歡迎有興趣的同窗投送簡歷到 liuxing14@meituan.com。