本文由 網易雲 發佈。node
做者:劉超 來自:網易雲 基礎服務算法
不管是在社區,仍是在同客戶交流的過程當中,總會被問到到底何時該用 Docker?何時用虛擬機?若是使用容器,應該使用哪一個容器平臺?
顯而易見,我不會直接給你們一個答案,而是但願從技術角度進行分析具體的場景。例如客戶是大公司仍是小公司,將部署小集羣仍是大集羣,傾向於私有云仍是公有云,已經採購了 IaaS 仍是沒有 IaaS,IT 運維能力強仍是弱,是否須要物理機、虛擬機、容器的混合部署,是通常的併發系統仍是高併發,這裏面所應該作的技術選型都不同。舉個例子,若是你是一個初創型的主營業務非 IT 的小公司,天然不該該花大力氣在數據中內心面本身搭建一套大規模、高併發、高性能的容器平臺。
接下來,首先,咱們來談下什麼狀況下應該使用 Docker 的問題。
如上圖所示,左面是咱們常常掛在嘴邊的所謂容器的優點,可是虛擬機都能一一懟回去。
若是部署的是一個傳統的應用,這個應用啓動速度慢,進程數量少,基本不更新,那麼虛擬機徹底可以知足需求。
○ 應用啓動慢:應用啓動 15 分鐘,容器自己秒級,虛擬機不少平臺能優化到十幾秒,二者幾乎看不出差異;
○ 內存佔用大:動不動 32G,64G 內存,一臺機器跑不了幾個;
○ 基本不更新:半年更新一次,虛擬機鏡像照樣可以升級和回滾;
○ 應用有狀態:停機會丟數據,若是不知道丟了什麼,就算秒級啓動也沒有用,照樣恢復不了,並且還有可能由於丟數據,在沒有修復的狀況下,盲目重啓帶來數據混亂;
○ 進程數量少:兩三個進程相互配置一下,不用服務發現,配置不麻煩
若是是一個傳統應用,根本沒有必要花費精力去容器化,由於白花了力氣,享受不到好處。
那麼什麼狀況下,才應該考慮作一些改變呢:
傳統業務忽然被互聯網業務衝擊了,應用總是變,三天兩頭要更新,並且流量增大了,原來支付系統是取錢刷卡的,如今要互聯網支付了,流量擴大了 N 倍。
這種狀況下就只能:拆。
拆開了,每一個子模塊獨自變化,相互影響變少。
拆開了,原來一個進程扛流量,如今多個進程一塊兒扛。
這被稱爲微服務。
微服務場景下,進程多,更新快,因而出現 100 個進程,天天一個鏡像。
容器樂了,每一個容器鏡像小,沒什麼問題,虛擬機哭了,由於虛擬機每一個鏡像太大了。
因此微服務場景下,能夠開始考慮用容器了。
這時虛擬機又怒了,我不用容器了,微服務拆分以後,用 Ansible 自動部署是同樣的。
這從技術角度來說沒有任何問題,問題是從組織角度出現的。通常的公司,開發會比運維多得多,開發寫完代碼就不用管了,環境的部署徹底是運維負責,運維爲了自動化,寫 Ansible 腳原本解決問題。
然而這麼多進程,又拆又合併的,更新這麼快,配置老是變,Ansible 腳本也要常改,天天都上線,不得累死運維。
因此在如此大的工做量狀況下,運維很容易出錯,哪怕經過自動化腳本。這時,容器就能夠做爲一個很是好的工具運用起來。
除了容器從技術角度,可以使得大部分的內部配置能夠放在鏡像裏面以外,更重要的是從流程角度,將環境配置這件事情,往前推了,推到了開發這裏,要求開發完畢以後,就須要考慮環境部署的問題,而不能當甩手掌櫃。
這樣作的好處就是,雖然進程多,配置變化多,更新頻繁,可是對於某個模塊的開發團隊來說,這個量是很小的,由於 5-10 我的專門維護這個模塊的配置和更新,不容易出錯。
若是這些工做量全交給少數的運維團隊,不但信息傳遞會使得環境配置不一致,部署量也會大很是多。
容器是一個很是好的工具,就是讓每一個開發僅僅多作 5% 的工做,就可以節約運維 200% 的工做量,而且不容易出錯。
然而原來運維該作的事情開發作了,開發的老大願意麼?開發的老大會投訴運維的老大麼?
這就不是技術問題了,其實這就是 DevOps,DevOps 不是不區分開發和運維,而是公司從組織到流程可以打通,看如何合做,邊界如何劃分,對系統的穩定性更有好處。
因此微服務、DevOps、容器是相輔相成,不可分割的。不是微服務,根本不須要容器,虛擬機就能搞定,不須要 DevOps,一年部署一次,開發和運維溝通再慢都能搞定。
因此,容器的本質是基於鏡像的跨環境遷移。
鏡像是容器的根本性發明,是封裝和運行的標準,其它什麼 namespace,cgroup,早就有了,這是技術方面。
在流程方面,鏡像是 DevOps 的良好工具。
容器是爲了跨環境遷移的,第一種遷移的場景是開發、測試、生產環境之間的遷移。若是不須要遷移,或者遷移不頻繁,虛擬機鏡像也行,但老是要遷移,帶着幾百 G 的虛擬機鏡像,太大了。
第二種遷移的場景是跨雲遷移,跨公有云,跨 Region,跨兩個 OpenStack 的虛擬機遷移都是很是麻煩,甚至不可能的,由於公有云不提供虛擬機鏡像的下載和上傳功能,並且虛擬機鏡像太大了,一傳傳一天。
因此跨雲場景下,混合雲場景下,容器也是很好的使用場景。這也同時解決了僅僅私有云資源不足,扛不住流量的問題。
因此這是我認爲的容器的本質,是最終應該使用容器的正確姿式,固然一開始你不必定徹底按照這個來。
docker
適合場景:初創公司,無信息安全擔心
若是您是一家初創公司,人員少,IT 運維能力不足,要部署的系統不多,可以花在 IT 系統上的資金有限,固然應該選擇公有云的虛擬機部署,它可以解決您的以下問題:
○ 基層 IT 資源的管理交給公有云平臺,公司自身運維人員僅須要基本的 Linux 能力;
○ 少許的部署系統,例如 10 臺如下的虛擬機,每每替換一個 war,重啓 Tomcat 就能解決,若是稍微虛擬機多一點 10 到 20 臺,Ansible 腳本能夠很好地解決這個問題;
○ 公有云按量按時收費,能夠在花費不多的狀況下啓動,而且在業務飛速擴展的時候,迅速申請大量虛擬機;
這裏所說的信息安全擔心,真的僅僅是心理的擔心,公有云每每有大量的安全機制來保證每一個租戶的安全隔離,只要用好了這些機制,公有云的安全性絕對大於通常公司本身搭建的數據中心,當客戶在說要安全的時候,客戶在想什麼? 這篇文章講到了絕對的端到端解決方案。
這裏貼張圖說明公有云的安全性:
數據庫
○ 多線 BGP,外網線路冗餘
○ 高吞吐量的 DDoS 外網防禦
○ 更完善的防火牆,入侵檢測,WAF
○ 更完善的流量清洗規則
apache
數據庫:
○ 高可用:主備切換數據零丟失
○ 高可靠:同城雙活,異地備份
○ 安全性:訪問控制,IP 白名單
對象存儲:
○ 高可靠:超大容量,三份備份,異地同步
○ 安全性:訪問控制,防盜鏈
編程
完善的監控系統,保障大促期間系統故障的快速定位和排障
保障大促可以極大的提高和訓練一支有經驗的運維團隊
大促的業務層面的數據對運維也是機密的,須要流程保障api
○ 愈來愈強的 DDoS 防禦
○ 愈來愈完善的防火牆規則
○ 最新的雲平臺安全功能和機制
○ 不斷更新的虛擬機和容器鏡像建設漏洞
○ 不斷更新的病毒庫
緩存
適用場景:初創公司無 IaaS,有信息安全擔心
可是即使如此,仍是有初創公司或者初創項目,也許由於心理方面,也許由於合規方面,很是擔憂信息安全問題,仍是但願採起部署在本身機房的方式。
但因爲是初創公司,在機房裏面通常是不能部署 IaaS,由於 IaaS 平臺的運維難度,優化難度更大,沒有一個 50 人的團隊根本玩不起來,因此通常在使用容器以前,採用的是物理機部署的方式,當物理機數目很是小,好比部署 5 到 10 個應用的時候手動部署或者簡單腳本部署就能夠,可是一旦到了 20 個應用,手動部署和簡單腳本就很是麻煩了:
○ 運維人員比例低,而應用相對較多
○ 部署在同一個物理機上的應用多,配置衝突,端口衝突,互相鏈接,運維須要一個 excel 去管理,還容易出錯
○ 物理機容器被腳本和 Ansible 改的亂七八糟,難以保證環境一致性,重裝物理機更加麻煩
○ 不一樣的應用依賴不一樣的操做系統和底層包,千差萬別
這個時候,能夠試一下裸用容器,即在原來的腳本,或者 Ansible 裏面,將啓動進程,改成使用 Docker run,能夠有如下的做用:
○ 配置,端口隔離,衝突減小
○ 基於容器部署,使得環境一致性,安裝和刪除乾乾淨淨
○ 不一樣的操做系統和底層包,均可以用容器鏡像搞定
在這個階段,最簡單的方式就是把容器當作虛擬機來使用,也即先啓動容器,而後在裏面下載 war 包等,固然也能夠更進一步,將 war 包和配置直接打在容器鏡像裏面,這樣須要一個持續集成的流程了,不只僅是運維的事情,開發也要參與其中。
在這個階段,網絡的模式可使用橋接打平的方式。
這種方式好處是訪問 Docker 和訪問物理機同樣,可很方便地實現 Docker 裏面和物理機裏面的互通,兼容原來部署在物理機上的應用。
固然 Bridge 的性能通常,若是性能要求比較高,可以使用 SR-IOV 網卡嵌入容器內。
安全
適用場景:創新項目,引入 DevOps 流程
有一些公司規模大一些,已經採購了 IaaS,只不過有一些創新的項目須要部署,這種狀態下,基本虛擬機已經可以知足需求,並且因爲可以運維 IaaS,IT 能力比較強,通常也採用了 Ansible 等部署工具。
這種狀況下,使用容器的動力相對比較少,然而容器也是可以帶來必定好處的,就是 DevOps。
創新項目迭代速度比較快,若是有比較多的創新項目,對運維的壓力也是很是大的,這裏的裸用容器和模式二的裸用容器不一樣的是,不是拿容器當作虛擬機來用,而是將容器當作交付物來用。
雖然容器化對於運維的整個過程來說改進有限,可是關鍵就是要開發寫一個 Dockerfile,這一點很是重要,意味着運行環境的配置提早到開發,而非直接交到運維,也即上面說的,開發 5% 的工做量增長減小大量運維工做,容器環境原子性升級回滾使得停服時間變短,能夠保持開發、測試、運維環境的一致性。
網絡
適用場景:發展中公司,中等規模集羣
當集羣規模超過 50 臺時,裸用容器已經很是難受了,由於網絡、存儲、編排、服務發現等所有要靠本身的腳本或 Ansible 來搞定,是時候引入容器平臺了。
當容器平臺規模不是很大時,Docker Swarm Mode 仍是比較好用的:
○ 集羣的維護不須要 Zookeeper,不須要 Etcd,本身內置
○ 命令行和 Docker 是同樣的,用起來順手
○ 服務發現和 DNS 是內置的
○ Docker Overlay 網絡是內置的
總之 docker 幫你料理好了一切,你不用太關心細節,很容易就可以將集羣運行起來。
並且能夠經過 docker 命令,像在一臺機器上使用容器同樣使用集羣上的容器,能夠隨時將容器當虛擬機來使用,這樣對於中等規模集羣,以及運維人員仍是比較友好的。
固然內置的太多了也有缺點,就是很差定製化,很差 Debug,很差干預。當你發現有一部分性能不行時,你須要改整個代碼,所有從新編譯,當社區更新了,合併分支是很頭疼的事情。當出現問題時,因爲 Manager 大包大攬幹了不少活,不知道哪一步出錯了,反正就是沒有返回,停在那裏,若是重啓整個 Manager,影響面又很大。
使用場景:萬節點集羣,多定製
當集羣規模大一些,幾百個節點時,不少人就不肯意使用 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 模塊劃分得更細,模塊比較多,比起裸 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 的解決。
使用場景:萬節點集羣,IT 能力強
隨着 Kubernetes 使用規模的愈來愈大,大型的公司能夠對 Kubernetes 進行必定的定製化,從而能夠實現萬節點甚至更大規模的支撐,固然須要 IT 能力比較強,網易在這方面有不少的實踐。
隨着集羣規模的擴大,apiserver 的壓力愈來愈大。
由於全部的其餘組件,例如 Controller、Scheduler、客戶端、Kubelet 等都須要監聽apiserver,來查看 etcd 裏面的變化,從而執行必定的操做。
不少人都將容器和微服務聯繫起來,從 Kubernetes 的設計能夠看出,Kubernetes 的模塊設計時很是的微服務化,每一個進程都僅僅幹本身的事情,而經過 apiserver 的鬆耦合關聯起來。
而 apiserver 則很像微服務中的 api 網關,是一個無狀態的服務,能夠很好地彈性伸縮。
爲了應對 listwatch,apiserver 用了 watchcache 來緩解壓力,然而最終的瓶頸仍是在 etcd 上。
最初用的是 etcd2,這時候 listwatch 每次只能接受一個事件,因此壓力很大。爲了繼續使用 etcd2,則須要使用多個 etcd2 的集羣來解決這個問題,經過不一樣的租戶分配到不一樣的 etcd2 集羣來分擔壓力。
未來會遷移到 etcd3 有了事件的批量推送,可是從 etcd2 到 etcd3 須要必定的遷移工做。
大的資源池的調度也是一個很大的問題,由於一樣一個資源只能被一個任務使用,若是並行調度,則存在兩個並行的調度器同時認爲某個資源空閒,因而同時將兩個任務調度到同一臺機器,結果出現競爭的狀況。
爲了租戶隔離,不一樣的租戶是不共享虛擬機的,這樣不一樣的租戶是能夠參考 Mesos 機制進行並行調度的。由於不一樣的租戶即使進行並行調度,也不會出現衝突的現象,每一個租戶不是在幾萬個節點中進行調度,而僅僅在屬於這個租戶的有限的節點中進行調度,大大提升了調度策略。
而且經過預過濾無空閒資源的 Node,調整 predicate 算法進行預過濾,進一步減小調度規模。
經過優化 Controller 加快新任務的調度速度
Kubernetes 採用的是微服務常使用的基於事件的編程模型。
當有增量事件產生時,則 controller 根據事件進行添加、刪除、更新等操做。
但基於事件模型的一個缺點是,老是經過 delta 進行事件觸發,過了一段時間,就不知道是否同步了,於是須要週期性地 Resync 一下,保證全量的同步以後,而後再進行增量的事件處理。
然而問題來了,當 Resync 時,正好遇到一個新容器的建立,則全部的事件在一個隊列裏面,拖慢了新建立容器的速度。
經過保持多個隊列,而且隊列的優先級 ADD 優於 Update 優於 Delete 優於 Sync,保證相應的實時性。
使用場景:萬節點集羣,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,纔敢在大規模集羣中使用。
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://www.163yun.com/
新用戶大禮包:https://www.163yun.com/gift
網易雲社區:https://sq.163yun.com/