例如,如果你是一個初創型的主營業務非IT的小公司,自然不應該花大力氣在數據中心里面自己搭建一套大規模、高並發、高性能的容器平台。
什么情況下使用IaaS就夠了?
如圖,左面是經常掛在嘴邊的所謂容器的優勢,但是虛擬機都能一一懟回去。
如果部署的是一個傳統的應用,這個應用啟動速度慢,進程數量少,基本不更新,那么虛擬機完全能夠滿足需求。
-
應用啟動慢:應用啟動15分鍾,容器本身秒級,虛擬機很多平台能優化到十幾秒,兩者幾乎看不出差別
-
內存占用大:動不動32G,64G內存,一台機器跑不了幾個
-
基本不更新:半年更新一次,虛擬機鏡像照樣能夠升級和回滾
-
應用有狀態:停機會丟數據,如果不知道丟了啥,就算秒級啟動有啥用,照樣恢復不了,而且還有可能因為丟數據,在沒有修復的情況下,盲目重啟帶來數據混亂。
-
進程數量少:兩三個進程相互配置一下,不用服務發現,配置不麻煩
如果是一個傳統應用,根本沒有必要花費精去容器化,因為白花了力氣,享受不到好處。
什么情況下,應該考慮做一些改變?
傳統業務突然被互聯網業務沖擊了,應用老是變,三天兩頭要更新,而且流量增大了,原來支付系統是取錢刷卡的,現在要互聯網支付了,流量擴大了N倍。
沒辦法,一個字:拆!
拆開了,每個子模塊獨自變化,少相互影響。
拆開了,原來一個進程扛流量,現在多個進程一起扛。
所以稱為微服務。
微服務場景下,進程多,更新快,於是出現100個進程,每天一個鏡像。
容器樂了,每個容器鏡像小,沒啥問題,虛擬機哭了,因為虛擬機每個鏡像太大了。
所以微服務場景下,可以開始考慮用容器了。
虛擬機怒了,老子不用容器了,微服務拆分之后,用Ansible自動部署是一樣的。
這樣從技術角度來講沒有任何問題。問題是從組織角度出現的。
什么情況下應該使用Docker?
一般的公司,開發會比運維多得多,開發寫完代碼就不用管了,環境的部署完全是運維負責,運維為了自動化,寫Ansible腳本來解決問題。
然而這么多進程,又拆又合並的,更新這么快,配置總是變,Ansible腳本也要常改,每天都上線,不得累死運維。
所以在這如此大的工作量情況下,運維很容易出錯,哪怕通過自動化腳本。這時,容器就可以作為一個非常好的工具運用起來。
除了容器從技術角度,能夠使得大部分的內部配置可以放在鏡像里面之外,更重要的是從流程角度,將環境配置這件事情,往前推了,推到了開發這里,要求開發完畢之后,就需要考慮環境部署的問題,而不能當甩手掌櫃。
這樣做的好處就是,雖然進程多,配置變化多,更新頻繁,但是對於某個模塊的開發團隊來講,這個量是很小的,因為5-10個人專門維護這個模塊的配置和更新,不容易出錯。
如果這些工作量全交給少數的運維團隊,不但信息傳遞會使得環境配置不一致,部署量會大非常多。
容器是一個非常好的工具,就是讓每個開發僅僅多做5%的工作,就能夠節約運維200%的工作,並且不容易出錯。
然而原來運維該做的事情開發做了,開發的老大願意么?開發的老大會投訴運維的老大么?
這就不是技術問題了,其實這就是DevOps,DevOps不是不區分開發和運維,而是公司從組織到流程能夠打通,看如何合作,邊界如何划分,對系統的穩定性更有好處。
所以微服務、DevOps、容器是相輔相成,不可分割的。
不是微服務,根本不需要容器,虛擬機就能搞定,不需要DevOps,一年部署一次,開發和運維溝通再慢都能搞定。
所以,容器的本質是基於鏡像的跨環境遷移。
鏡像是容器的根本性發明,是封裝和運行的標准,其它什么namespace,cgroups,早就有了。這是技術方面。
在流程方面,鏡像是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的架構相對松耦合,有很多可以定制化的地方,從而運維人員可以根據自己的需要開發自己的模塊。
這也是很多優秀的公司使用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之上添加了很多的組件,如圖所示,現在已經非常豐富,例如DC/OS的客戶端(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有兩種方式,粗粒度和細粒度。
o 粗粒度模式(Coarse-grained Mode):應用程序的各個任務正式運行之前,需要將運行環境中的資源全部申請好,且運行過程中要一直占用這些資源,即使不用,最后程序運行結束后,回收這些資源。組粒度的方式浪費資源。
o 細粒度模式(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可以嘗試集成一下。