容器平臺選型的十大模式:Docker、DC/OS、K8S誰與當先?
首先我們來談什麼情況下應該使用Docker的問題
如圖,左面是經常掛在嘴邊的所謂容器的優勢,但是虛擬機器都能一一懟回去。
如果部署的是一個傳統的應用,這個應用啟動速度慢,程序數量少,基本不更新,那麼虛擬機器完全能夠滿足需求。
-
應用啟動慢:應用啟動15分鐘,容器本身秒級,虛擬機器很多平臺能優化到十幾秒,兩者幾乎看不出差別
-
記憶體佔用大:動不動32G,64G記憶體,一臺機器跑不了幾個
-
基本不更新:半年更新一次,虛擬機器映象照樣能夠升級和回滾
-
應用有狀態:停機會丟資料,如果不知道丟了啥,就算秒級啟動有啥用,照樣恢復不了,而且還有可能因為丟資料,在沒有修復的情況下,盲目重啟帶來資料混亂。
-
程序數量少:兩三個程序相互配置一下,不用服務發現,配置不麻煩
如果是一個傳統應用,根本沒有必要花費精去容器化,因為白花了力氣,享受不到好處。
什麼情況下,才應該考慮做一些改變呢?
傳統業務突然被網際網路業務衝擊了,應用老是變,三天兩頭要更新,而且流量增大了,原來支付系統是取錢刷卡的,現在要網際網路支付了,流量擴大了N倍。
沒辦法,一個字:拆!
拆開了,每個子模組獨自變化,少相互影響。
拆開了,原來一個程序扛流量,現在多個程序一起扛。
所以稱為微服務。
微服務場景下,程序多,更新快,於是出現100個程序,每天一個映象。
容器樂了,每個容器映象小,沒啥問題,虛擬機器哭了,因為虛擬機器每個映象太大了。
所以微服務場景下,可以開始考慮用容器了。
虛擬機器怒了,老子不用容器了,微服務拆分之後,用Ansible自動部署是一樣的。
這樣從技術角度來講沒有任何問題。問題是從組織角度出現的。
一般的公司,開發會比運維多得多,開發寫完程式碼就不用管了,環境的部署完全是運維負責,運維為了自動化,寫Ansible指令碼來解決問題。
然而這麼多程序,又拆又合併的,更新這麼快,配置總是變,Ansible指令碼也要常改,每天都上線,不得累死運維。
所以在這如此大的工作量情況下,運維很容易出錯,哪怕通過自動化指令碼。這時,容器就可以作為一個非常好的工具運用起來。
除了容器從技術角度,能夠使得大部分的內部配置可以放在映象裡面之外,更重要的是從流程角度,將環境配置這件事情,往前推了,推到了開發這裡,要求開發完畢之後,就需要考慮環境部署的問題,而不能當甩手掌櫃。
這樣做的好處就是,雖然程序多,配置變化多,更新頻繁,但是對於某個模組的開發團隊來講,這個量是很小的,因為5-10個人專門維護這個模組的配置和更新,不容易出錯。
如果這些工作量全交給少數的運維團隊,不但資訊傳遞會使得環境配置不一致,部署量會大非常多。
容器是一個非常好的工具,就是讓每個開發僅僅多做5%的工作,就能夠節約運維200%的工作,並且不容易出錯。
然而原來運維該做的事情開發做了,開發的老大願意麼?開發的老大會投訴運維的老大麼?
這就不是技術問題了,其實這就是DevOps,DevOps不是不區分開發和運維,而是公司從組織到流程能夠打通,看如何合作,邊界如何劃分,對系統的穩定性更有好處。
所以微服務、DevOps、容器是相輔相成,不可分割的。
不是微服務,根本不需要容器,虛擬機器就能搞定,不需要DevOps,一年部署一次,開發和運維溝通再慢都能搞定。
所以,容器的本質是基於映象的跨環境遷移。
映象是容器的根本性發明,是封裝和執行的標準,其它什麼namespace,cgroup,早就有了。這是技術方面。
在流程方面,映象是DevOps的良好工具。
容器是為了跨環境遷移的,第一種遷移的場景是開發、測試、生產環境之間的遷移。如果不需要遷移,或者遷移不頻繁,虛擬機器映象也行,但總是要遷移,帶著幾百G的虛擬機器映象,太大了。
第二種遷移的場景是跨雲遷移,跨公有云,跨Region,跨兩個OpenStack的虛擬機器遷移都是非常麻煩,甚至不可能的,因為公有云不提供虛擬機器映象的下載和上傳功能,而且虛擬機器映象太大了,一傳傳一天。
所以跨雲場景下,混合雲場景下,容器也是很好的使用場景。這也同時解決了僅僅私有云資源不足,扛不住流量的問題。
所以這是我認為的容器的本質,是最終應該使用容器的正確姿勢,當然一開始你不一定完全按照這個來。
模式一:公有云虛擬機器
適合場景:初創公司,無資訊保安擔憂
如果您是一家初創公司,人員少,IT運維能力不足,要部署的系統很少,能夠花在IT系統上的資金有限,當然應該選擇公有云的虛擬機器部署,它能夠解決您的如下問題:
-
基層IT資源的管理交給公有云平臺,公司自身運維人員僅需要基本的Linux能力
-
少量的部署系統,例如10臺以下的虛擬機器,往往替換一個war,重啟Tomcat就能解決,如果稍微虛擬機器多一點10到20臺,Ansible指令碼可以很好的解決這個問題
-
公有云按量按時收費,可以在花費很少的情況下啟動,並且在業務飛速擴充套件的時候,迅速申請大量虛擬機器
這裡所說的資訊保安擔憂,真的僅僅是心理的擔憂,公有云往往有大量的安全機制來保證每個租戶的安全隔離,只要用好了這些機制,公有云的安全性絕對大於一般的公司自己搭建的資料中心,為什麼呢?
大家有興趣可以閱讀《當客戶在說要安全的時候,客戶在想什麼?》,絕對的端到端解決方案。
這裡貼張圖說明公有云的安全性。
公有云為支撐自身高併發業務積累了更強的安全防護能力和更多的安全防護經驗:
-
多線BGP,外網線路冗餘
-
高吞吐量的DDoS外網防護
-
更完善的防火牆,入侵檢測,WAF
-
更完善的流量清洗規則
公有云為支撐自身高併發業務推出了更安全、更高可靠、更高可用的PaaS服務:
資料庫:
-
高可用:主備切換資料零丟失
-
高可靠:同城雙活,異地備份
-
安全性:訪問控制,IP白名單
物件儲存:
-
高可靠:超大容量,三份備份,異地同步
-
安全性:訪問控制,防盜鏈
公有云為支撐自身高併發業務推出更完善的監控運維的系統,流程,經驗:
-
完善的監控系統,保障大促期間系統故障的快速定位和排障
-
保障大促能夠極大的提升和訓練一支有經驗的運維團隊
-
大促的業務層面的資料對運維也是機密的,需要流程保障
道高一尺魔高一丈,公有云為保證自身業務的安全性對雲平臺不斷升級:
-
越來越強的DDoS防護
-
越來越完善的防火牆規則
-
最新的雲平臺安全功能和機制
-
不斷更新的虛擬機器和容器映象建設漏洞
-
不斷更新的病毒庫
這不是今天的重點,這幾張圖大家自行參考。
模式二:無IaaS,裸用容器
適用場景:初創公司無IaaS,有資訊保安擔憂
但是即便如此,還是有初創公司或者初創專案,也許因為心理方面,也許因為合規方面,非常擔心資訊保安問題,還是希望採取部署在自己機房的方式。
但由於是初創公司,在機房裡面一般是不能部署IaaS,因為IaaS平臺的運維難度,優化難度更大,沒有一個50人的團隊根本玩不起來,所以一般在使用容器之前,採用的是物理機部署的方式,當物理機數目非常小,比如部署5到10個應用的時候手動部署或者簡單指令碼部署就可以,但是一旦到了20個應用,手動部署和簡單指令碼就非常麻煩了:
-
運維人員比例低,而應用相對較多
-
部署在同一個物理機上的應用多,配置衝突,埠衝突,互相連線,運維需要一個excel去管理,還容易出錯
-
物理機容器被指令碼和Ansible改的亂七八糟,難以保證環境一致性,重灌物理機更加麻煩
-
不同的應用依賴不同的作業系統和底層包,千差萬別
這個時候,可以試一下裸用容器,即在原來的指令碼,或者Ansible裡面,將啟動程序,改為使用Docker run,可以有以下的作用:
-
配置,埠隔離,衝突減少
-
基於容器部署,使得環境一致性,安裝和刪除乾乾淨淨
-
不同的作業系統和底層包,都可以用容器映象搞定
在這個階段,最簡單的方式就是把容器當做虛擬機器來使用,也即先啟動容器,然後在裡面下載war包等,當然也可以更進一步,將war包和配置直接打在容器映象裡面,這樣需要一個持續整合的流程了,不僅僅是運維的事情,開發也要參與其中。
在這個階段,網路的模式可以使用橋接打平的方式。
這種方式好處是訪問Docker和訪問物理機一樣,可很方便地實現Docker裡面和物理機裡面的互通,相容原來部署在物理機上的應用。
當然Bridge的效能一般,如果效能要求比較高,可使用SR-IOV網絡卡嵌入容器內。
模式三:有IaaS,裸用容器
適用場景:創新專案,引入DevOps流程
有一些公司規模大一些,已經採購了IaaS,只不過有一些創新的專案需要部署,這種狀態下,基本虛擬機器已經能夠滿足需求,而且由於能夠運維IaaS,IT能力比較強,一般也採用了Ansible等部署工具。
這種情況下,使用容器的動力相對比較少,然而容器也是能夠帶來一定好處的,就是DevOps。
創新專案迭代速度比較快,如果有比較多的創新專案,對運維的壓力也是非常大的,這裡的裸用容器和模式二的裸用容器不同的是,不是拿容器當做虛擬機器來用,而是將容器當做交付物來用。雖然容器化對於運維的整個過程來講改進有限,但是關鍵就是要開發寫一個Dockerfile,這一點非常重要,意味著執行環境的配置提前到開發,而非直接交到運維,也即上面說的,開發5%的工作量增加減少大量運維工作,容器環境原子性升級回滾使得停服時間變短,可以保持開發、測試、運維環境的一致性。
模式四:使用Docker Swarm Mode
適用場景:發展中公司,中等規模叢集
當叢集規模超過50臺時,裸用容器已經非常難受了,因為網路、儲存、編排
服務發現等全部要靠自己的指令碼或Ansible來搞定,是時候引入容器平臺了。
當容器平臺規模不是很大時,Docker Swarm Mode還是比較好用的:
-
叢集的維護不需要Zookeeper,不需要Etcd,自己內建
-
命令列和Docker一樣的,用起來順手
-
服務發現和DNS是內建的
-
Docker Overlay網路是內建的
總之Docker幫你料理好了一切,你不用太關心細節,很容易就能夠將叢集執行起來。
而且可以通過docker命令,像在一臺機器上使用容器一樣使用叢集上的容器,可以隨時將容器當虛擬機器來使用,這樣對於中等規模叢集,以及運維人員還是比較友好的。
當然內建的太多了也有缺點,就是不好定製化,不好Debug,不好干預。當你發現有一部分效能不行時,你需要改整個程式碼,全部重新編譯,當社群更新了,合併分支是很頭疼的事情。當出現問題時,由於Manager大包大攬幹了很多活,不知道哪一步出錯了,反正就是沒有返回,停在那裡,如果重啟整個Manager,影響面又很大。
模式五:使用Marathon和Mesos
使用場景:萬節點叢集,多定製
當叢集規模大一些,幾百個節點時,很多人就不願意使用Docker Swarm Mode了,很多的選擇是既沒有用DC/OS,也沒有用Kubernetes,而是僅僅用了Marathon和Mesos。
因為Mesos是一個非常優秀的排程器,它的雙層排程機制可以使得叢集規模大很多。
Mesos的排程過程如圖所示:
Mesos有Framework、Master、Agent、Executor、Task幾部分組成。這裡面有兩層的Scheduler,一層在Master裡面,allocator會將資源公平的分給每一個Framework,二層在Framework裡面,Framework的scheduler將資源按規則分配給Task。
其它框架的排程器是直接面對整個叢集,Mesos的優勢在於,第一層排程先將整個Node分配給一個Framework,然後Framework的排程器面對的叢集規模小很多,然後在裡面進行二次排程,而且如果有多個Framework,例如有多個Marathon,則可以並行排程不衝突。
詳細的排程機制非常複雜,可以看《號稱瞭解mesos雙層排程的你,先來回答下面這五個問題!》這篇文章。
而且Mesos的架構相對鬆耦合,有很多可以定製化的地方,從而運維人員可以根據自己的需要開發自己的模組。詳細的定製方式看文章《定製化Mesos任務執行的幾種方法》。
這也是很多優秀的公司使用Marathon和Mesos的原因。
例如愛奇藝、去哪兒、攜程、噹噹等都選擇了使用Mesos,需要提一下的是,大家如果參加社群,能發現裸用Marathon和Mesos的很多,但是整個DC/OS都用得比較少,而用Marathon和Mesos往往不能解決一些問題,因而這些IT能力非常強的網際網路公司做了大量的自己的定製化,增加了Marathon和Mesos的外圍模組。
模式六:使用開源Kubernetes
使用場景:千節點叢集,少定製
Kubernetes模組劃分得更細,模組比較多,比起裸Marathon和Mesos來講功能豐富,而且模組之間完全的鬆耦合,可以非常方便地進行定製化。
而且Kubernetes的資料結構的設計層次比較細,非常符合微服務的設計思想。例如從容器->Pods->Deployment->Service,本來簡單執行一個容器,被封裝為這麼多的層次,每次層有自己的作用,每一層都可以拆分和組合,這樣帶來一個很大的缺點,就是學習門檻高,為了簡單執行一個容器,需要先學習一大堆的概念和編排規則。
但是當需要部署的業務越來越複雜時,場景越來越多時,你會發現Kubernetes這種細粒度設計的優雅,使得你能夠根據自己的需要靈活的組合,而不會因為某個元件被封裝好了,從而導致很難定製。例如對於Service來講,除了提供內部服務之間的發現和相互訪問外,還靈活設計了headless service,這使得很多遊戲需要有狀態的保持長連線有了很好的方式,另外訪問外部服務時,例如資料庫、快取、headless service相當於一個DNS,使得配置外部服務簡單很多。很多配置複雜的大型應用,更復雜的不在於服務之間的相互配置,可以有Spring Cloud或者Dubbo去解決,複雜的反而是外部服務的配置,不同的環境依賴不同的外部應用,External Name這個提供和很好的機制。
包括統一的監控cadvisor,統一的配置confgMap,都是構建一個微服務所必須的。
然而Kubernetes當前也有一個瓶頸——叢集規模還不是多麼大,官方說法是幾千個節點,所以超大規模的叢集,還是需要有很強的IT能力進行定製化,這個在模式七中會說一下我們在網易雲上做的事情。但是對於中等規模的叢集也足夠了。
而且Kubernetes社群的熱度,可以使得使用開源Kubernetes的公司能夠很快地找到幫助,等待到新功能的開發和Bug的解決。
模式七:深入掌握使用Kubernetes
使用場景:萬節點叢集,IT能力強
隨著Kubernetes使用規模的越來越大,大型的公司可以對Kubernetes進行一定的定製化,從而可以實現萬節點甚至更大規模的支撐,當然需要IT能力比較強,網易在這方面有很多的實踐。
從APIServer看叢集的規模問題
隨著叢集規模的擴大,apiserver的壓力越來越大。
因為所有的其他元件,例如Controller、Scheduler、客戶端、Kubelet等都需要監聽apiserver,來檢視etcd裡面的變化,從而執行一定的操作。
很多人都將容器和微服務聯絡起來,從Kubernetes的設計可以看出,Kubernetes的模組設計時非常的微服務化,每個程序都僅僅幹自己的事情,而通過apiserver的鬆耦合關聯起來。
而apiserver則很像微服務中的api閘道器,是一個無狀態的服務,可以很好地彈性伸縮。
為了應對listwatch,apiserver用了watchcache來緩解壓力,然而最終的瓶頸還是在etcd上。
最初用的是etcd2,這時候listwatch每次只能接受一個事件,所以壓力很大。為了繼續使用etcd2,則需要使用多個etcd2的叢集來解決這個問題,通過不同的租戶分配到不同的etcd2叢集來分擔壓力。
將來會遷移到etcd3有了事件的批量推送,但是從etcd2到etcd3需要一定的遷移工作。
通過優化Scheduler解決並行排程的問題
大的資源池的排程也是一個很大的問題,因為同樣一個資源只能被一個任務使用,如果並行排程,則存在兩個並行的排程器同時認為某個資源空閒,於是同時將兩個任務排程到同一臺機器,結果出現競爭的情況。
為了租戶隔離,不同的租戶是不共享虛擬機器的,這樣不同的租戶是可以參考Mesos機制進行並行排程的。因為不同的租戶即便進行並行排程,也不會出現衝突的現象,每個租戶不是在幾萬個節點中進行排程,而僅僅在屬於這個租戶的有限的節點中進行排程,大大提高了排程策略。
並且通過預過濾無空閒資源的Node,調整predicate演算法進行預過濾,進一步減少排程規模。
通過優化Controller加快新任務的排程速度
Kubernetes採用的是微服務常使用的基於事件的程式設計模型。
當有增量事件產生時,則controller根據事件進行新增、刪除、更新等操作。
但基於事件模型的一個缺點是,總是通過delta進行事件觸發,過了一段時間,就不知道是否同步了,因而需要週期性地Resync一下,保證全量的同步之後,然後再進行增量的事件處理。
然而問題來了,當Resync時,正好遇到一個新容器的建立,則所有的事件在一個佇列裡面,拖慢了新建立容器的速度。
通過保持多個佇列,並且佇列的優先順序ADD優於Update優於Delete優於Sync,保證相應的實時性。
模式八:深入掌握使用DC/OS
使用場景:萬節點叢集,IT能力強
前面說過Mesos由於本身獨特的排程機制,從而支撐的叢集規模比較大,但是大多數使用Mesos的公司都沒有使用DC/OS,而是裸使用Marathon和Mesos外加自己定製開發的一些元件。
Mesos可以支援當叢集規模非常大,單個Marathon的效能不足以支撐時,可以使用自己的Framework機制,使得不同的租戶使用單獨的Marathon來解決問題。
後來DC/OS在最基礎的Marathon和Mesos之上添加了很多的元件,如圖所示,現在已經非常豐富,例如DCOS的客戶端(kubectl)、API閘道器admin router(類似apiserver)、服務發現minuteman(類似kube-proxy)、Pod的支援、CNI外掛的支援、儲存外掛的支援等,和Kubernetes已經非常像了。
很多公司裸用Marathon和Mesos而沒有進一步使用DC/OS,可能是因為和核心元件Mesos已經經過大規模生產性支撐不同,這些外圍的元件也是新的,對其穩定性也是有一定的顧慮,所以需要比較長的學習曲線,並且對於這些新的元件有非常好的把控,才敢上生產。
所以從這個角度來講,雖然Mesos的穩定性和大規模無容置疑,但就整個DC/OS來講,和Kubernetes從功能和穩定性來講,在伯仲之間,都需要使用者有強大的IT能力,對於開源軟體的各個模組非常熟悉,甚至能夠做一定的程式碼修改和Bug fix,才敢在大規模叢集中使用。
模式九:部署大資料,Kubernetes vs. Mesos
Mesos還有一個優勢,就是Mesos可以通過開發Framework,構建大資料平臺,例如Spark就有基於Mesos的部署方式。
基於Mesos的Spark有兩種方式,粗粒度和細粒度。
-
粗粒度模式(Coarse-grained Mode):應用程式的各個任務正式執行之前,需要將執行環境中的資源全部申請好,且執行過程中要一直佔用這些資源,即使不用,最後程式執行結束後,回收這些資源。組粒度的方式浪費資源。
-
細粒度模式(Fine-grained Mode):按需分配,應用程式啟動時,先會啟動executor,但每個executor佔用資源僅僅是自己執行所需的資源,不需要考慮將來要執行的任務,之後,mesos會為每個executor動態分配資源,每分配一些,便可以執行一個新任務,單個Task執行完之後可以馬上釋放對應的資源。細粒度的缺點是效能有問題。
其實細粒度模式才是真正能夠發揮Mesos動態資源排程最有效的方式,但是考慮到有大幅度的效能降低,https://issues.apache.org/jira/browse/SPARK-11857,很可惜這種方式在Spark 2.0.0被deprecated掉了。
如果使用kubernetes部署大資料,其實和部署一個普通的應用思路差不多,和Mesos不同,kubernetes不會干預到大資料執行的上下文中,Kubernetes啟動的容器僅僅作為資源預留方式存在,容器內的資源分配則大資料平臺自己解決。這樣的利用率就降低了,相當於粗粒度模式。
基於容器部署大資料平臺,也是建議部署計算部分,例如Map-Reduce,或者Spark,對於資料部分HDFS,應當另行部署。
模式十:容器和虛擬化混合部署
使用場景:大型公司,逐步容器化
對於很多大公司但是非網際網路公司,使用容器還是需要小心對待的,因而需要逐步容器化,所以存在有IaaS平臺,並且虛擬機器和容器混合使用的狀態,這種狀態可能會持續相當長的時間。
在這種情況下,建議容器套在虛擬機器裡面使用。
使用Flannel和Calico都僅僅適用於裸機容器,而且僅僅用於容器之間的互通。
一旦有IaaS層,就會存在網路二次虛擬化的問題。
虛擬機器之間的互聯是需要通過一個虛擬網路的,例如vxlan的實現,而使用Flannel或者Calico相當於在虛擬機器網路虛擬化的上面再做一次虛擬化,使得網路效能大幅度降低。
而且如果使用Flannel或者Calico,那容器內的應用和虛擬機器上的應用相互通訊時,則需要出容器平臺,多使用node port,通過NAT的方式訪問,或者通過外部負載均衡器的方式進行訪問。在現實應用中,不可能一下子將所有的應用全部容器化,只是部分應用容器化,部分應用部署在虛擬機器裡面是常有的現象。然而通過NAT或者外部負載均衡器的方式,對應用的相互呼叫有侵入,使得應用不能像原來一樣相互呼叫,尤其是當應用之間使用Dubbo或者SpringCloud這種服務發現機制時,尤其如此。
網易雲開發了自己的NeteaseController,在監聽到有新的Pod建立時,呼叫IaaS的API建立IaaS層的虛擬網絡卡,然後在虛擬機器內部,通過呼叫Netease CNI外掛將虛擬網絡卡新增到容器裡面。新增技術用的就是上一節提到的setns命令。
通過這個圖我們可以看出,容器的網絡卡是直接連線到虛擬私有網路的OVS上的,和虛擬機器是一個平的二層網路,在OVS來看,容器和虛擬機器是在同一個網路裡面的。
這樣一方面沒有了二次虛擬化,只有OVS一層虛擬化。另外容器和虛擬機器網路打平的好處是,當部分應用部署容器、虛擬機器時,對應用沒有侵入,應用原來如何相互訪問,現在還是如何訪問,有利於應用逐步容器化。
OpenStack裡面有一個專案Kuryr可以很好地去做這件事情,完全使用開源的OpenStack和Kubernetes可以嘗試整合一下。
https://mp.weixin.qq.com/s/JFtZAJabrmp4wlcLrrU6EA