首先我們的問題是:產品包含了大量的服務,并且服務之間存在復雜的依賴關系,以拓撲的形式運行并相互協作,部署的時候需要手動解決整體的依賴,配制通信的協議和地址,重新部署新環境復雜度非常高。因此,我們希望有一種容器技術可以讓我們構建產品所需要的所有的服務能夠迅速快捷的重新部署,并且可以根據需求橫向的擴展,且保證高可用性,在出現問題的時候可以自動重啟或者啟動備份服務。
目前有多種解決方案,考慮我們有私有云,亞馬遜云以及物理機的幾種部署方式,所以Docker作為解決方案的基礎,在其之上選擇合適的容器拓撲管理工具就成了主要任務,常見的解決方案有:
- Fleet,最早CoreOS集成的解決方案,基于Systemd管理操作系統上的容器,通過Systemd描述文件來表達容器之間啟動的依賴關系,并且擴展支持Global, Single等類型的啟動方式,跟系統契合的非常好,使用簡單,處于比較高的應用級別,由于現在CoreOS跟Docker目前處于比較尷尬的關系,各自都想獨立形成自己完整的生態系統,對其發展前景不是非常樂觀;
- Kubernetes,Google根據自身很多年的容器部署經驗所開源的容器管理解決方案,具有服務、節點、Pod等一系列不同層級的概念劃分,通過自身的DNS服務發現可以實現很復雜的服務編排部署,對Docker兼容不錯,不過略復雜些。
- Mesos+Marathon,Mesos是一個通用的資源管理調度框架,目前比較成熟的應用可以外接各種常用的計算框架,比如Hadoop MR, Storm, Spark等, Docker container作為一種long running的Framework也可以很自然的被Mesos所托管,Marathon作為Mesos之上的一種Scheduler比較擅長管理Docker這種長時間運行的“計算框架”,因此經常與Mesos綁定在一起作為Docker服務生產環境部署的解決方案討論。
- Machine+Swarm+Compose, Docker官方提供的工具組合,特點就是與Docker engine的結合非常好,可以說能夠無縫的替換原來Docker engine所使用的地方,最終所形成的容器集合可以作為單一的Docker容器來使用,從不同層面上實現了使用方法和調用的統一,缺點就是還不夠成熟。
多種解決方案中我們優先選擇官方提供的工具,一般來說官方提供的工具跟自己的原生服務結合的更好,也具有更長遠的規劃,在官方工具確實不足的情況下輔助以第三方的工具,因此初步我們決定采用Docker原生的工具Machine+Swarm+Compose輔助以Mesos來實現整個工程的部署,其中Swarm負責某一功能模塊小規模的容器分配調度,Mesos負責整個集群最外層大規模容器資源調度,可以說以Mesos為主,Swarm為輔助,因為Mesos是比較成熟的資源管理框架,也有非常適合的調度引擎,Swarm還相對初步隨著時間演進,也許會接管更多的調度。
簡單介紹下Docker官方原生的工具:
- Machine,在一臺機器上管理多個Docker 環境變量配置,方便快捷的啟動本地或者遠程的Docker容器,切換環境變量后,Docker command可以像在本地執行一樣操作遠程的Docker容器,依賴于其多種多樣的Driver實現,降低了ssh登陸到各個機器去執行各種命令的復雜度;
- Swarm,將多個Docker host集成在一起作為一個虛擬的Docker host來使用,使用方式跟普通的Docker命令相同,通常可以用來實現小規模容器的負載均衡以及高可用的服務備份,其架構特點比較直接,就是在多臺機器上安裝Swarm agent與manager溝通,服務發現可以采用etcd/zookeeper/consul進行服務注冊。這樣確實實現了容器的分布式部署,不過只應對于相對簡單的情況,調度算法也比較簡單,適合于實現相對結構比較簡單的Web應用,對于大規模的拓撲或者服務編排可能不夠成熟;
- Compose,通過編寫yaml格式的服務組織編排文件,我們可以通過一個命令來啟動多個容器并連接在一起作為一個完整的服務來使用,通常來說面向的是開發和測試,默認多個容器都是在一個host啟動的,不能作為生產環境使用,如果希望compose的服務可以在生產環境中使用,可以獨立于測試的配置文件新建一個production.yaml, 結合machine和swarm來管理多個遠程host并組織成一個虛擬的host來使用,這樣一個compose包含的服務會部署到實際不同的機器,滿足生產的需要。Compose通過link將不同容器的端口連接在一起,如果服務運行于不同的host,需要給這個Swarm集群設置共同的overlay網絡,否則默認的bridge網絡只能在單個host聯通。
關于Docker網絡解決方案的爭論比較多了,CoreOS和Kubernetes都有自己的解決方案,前兩者都是比較通用的PAAS工具,作為通用性的服務編排工具容器的具體實現可以是多種,Docker只是其中之一,而Docker libnetwork的解決方案過于底層,不適合作為通用的插件集成到Kubernetes或者CoreOS中,因此這兩家都有自己CNI類型的解決方案,對于使用者來說我們不那么關心到底這個工具支持多少種容器,只需要知道Docker這種容器能夠滿足當前產品部署的需求就好,因此我們仍然以Docker的工具為主,盡管不那么通用,但是能夠解決我們目前服務編排的問題。
官方的工具看起來很美好,解決方案也足夠優雅和簡潔,問題就是成熟程度,compose和swarm的結合仍然是在試驗階段,對于處于不同host的container,進行link仍然需要手動對整個Swarm集群設置網絡,對于大規模或者復雜拓撲的部署工作量不小,因此我們借助于Mesos來做第一級的資源或者容器管理,其中第二級或者說小規模容器部署是可以在swarm中實現。
Mesos作為資深的資源管理平臺,在Docker出現之前就已經被廣泛利用了,基本上所有的主從類型的分布式計算框架都支持使用Mesos來做基本的資源分配調度,比如hadoop, storm,spark等等,同時Mesos的設計也可允許長時間運行的application, 不管是batch job, stream job還是普通的應用服務都可以接入Mesos來申請資源啟動自身的容器。早期Mesos只支持LXC形式的資源限制,在Docker崛起之后Mesos也開始支持直接使用Docker容器來運行具體的計算框架,可以說二者既有競爭又相輔相成。說競爭是因為目前Docker自己的工具已經慢慢的可以替代一部分Mesos的應用場景了,只要機器上安裝了Docker engine就可以無差別的管理所有主機,比如Swarm就可以組建簡單的服務集群,管理容器在集群中的運行,同時也能夠利用Machine來進行遠程管理,說相輔是因為Swarm的設計是可以替換具體的調度后端的,默認情況使用自己的調度器在服務發現的基礎上選擇一個host來啟動容器,通過配置可以選擇Mesos作為其調度后端,將Swarm 作為跟Spark同等的Compute Framework來運行,這樣Swarm就能夠使用Mesos更加成熟和靈活的調度機制來管理容器,在此之上Compose就可以把編排好的服務運行在Mesos集群,可見Mesos和Docker結合的生態系統在當前階段是比較和諧的。
這樣,最終我們的解決方案就基本確定了,Mesos作為最基礎的集群資源管理或者調度工具運行在所有的服務器上,Spark等計算框架不再獨立部署,而是使用Mesos最初的LXC容器來運行,Swarm使用Docker容器通過Mesos來調度,Compose文件用來啟動結合比較緊密服務堆棧,比如Tachyon集群,我們自己所開發的應用服務以及ACO集群也作為一個Docker服務堆棧在Swarm上運行。所以我們的Mesos集群上目前運行兩種計算框架,Spark和Swarm,負責我們的應用和分布式計算的部署,具體的應用和服務編排都是在Compose中完成,個別復雜的應用需要手動去處理關聯關系,依然是以Docker的形式運行在Mesos中。
Mesos可以把我們的機器聚合在一起作為一個機器來使用,不管是我們的應用還是分布式計算的任務,都直接提交給Mesos來進行調度,減少了對服務器的垂直劃分,不存在Spark的集群, Hadoop的集群等概念,Spark或者Hadoop的job直接在Mesos的slave中分配資源并運行各自job相關的Executor, 運行結束后釋放資源,就像Spark沒有存在過一樣, 因此從更高的角度看Mesos的Framework其實就是一個調度器加一個運行時的處理流程,不用再需要Spark或者Storm等框架的Standalone 模式自己來處理調度,只需要使用Mesos的API,實現自己的scheduler和具體啟動停止運行過程的Executor就好, 而對于我們自己的應用如果要作為Framework存在也需要實現對應的Scheduler和Executor, 不過可以利用現成的比較好的調度器比如Marathon來托管我們的應用,減少開發Framework的工作量。使用Mesos這樣的好處是資源的利用率更高, 因此我們在也不需要除了Mesos之外的long running 集群, 即使有Long running的服務,也是在Mesos分配好的容器內運行。
Framework = Scheduler + Executor
Mesos的安裝過程稍微有點服務,雖然Docker鏡像可以減少Mesos的部署復雜度,但是這樣就存在了兩層容器, Mesos在Docker 容器中運行,而Mesos里邊的任務也是在自己的Docker容器里運行,如果有些長時間運行的任務需要暴露出端口跟外界交互,就需要先Expose port到Mesos slave級別的容器, 然后再Expose到最外層的物理機,復雜度增加且對性能有損耗,因此我們最終還是傾向于在物理機上部署Mesos, 只保留一層Docker容器不推薦嵌套,而且有了Mesosphere的DCOS系統,在AWS上部署Mesos就比較簡單了。
Mesos看起來很完美,那我們為什么還需要Docker容器呢, 直接使用LXC標準Linux kernel支持的容器不就可以了,在這個解決方案中我們期望所有運行的應用或者分布式計算框架的任務的Executor都是在Docker容器中運行,也是因為Docker殺手級的功能,一個Docker容器就像一個集裝箱,里邊包含了需要運行一個服務或者任務的所有的依賴條件或者配置,都可以根據需求自身靈活的修改,并且一次裝箱隨處運行,不用關心外在環境,舉個例子假如直接使用LXC來運行Spark的某個任務的Executor,需要提供Spark jar包的地址,相關的配置集成到ExecutorInfo中才能運行,而如果使用Docker container就簡單很多,Spark executor運行需要的信息都在某個Docker image中,Mesos slave只要調用Docker client啟動某個鏡像就足以運行一個Framework的某個任務,任務的執行在Docker 容器中。對于我們自己開發的各種服務同理也是組織成鏡像最終在Docker容器中運行, Scheduler依賴Marathon就可以。