Docker, Mesos, Marathon以及寵物模式的終結
【編者的話:國外廣為流傳的一個比喻是:在傳統服務模式下,可以想象服務器就是IT的寵物(Pets),給他們取名字,精心撫養長大,當他們生病 了,你得修復他們;在新形態的應用服務模型中,虛擬機被看做是農場中的公牛(Cattle),名字通常都是編號,當他們生病了,你就殺掉他,用一頭新牛代 替。 未來的應用架構應該像對待農場中的公牛一樣:對基礎架構的“保養”、保護基礎架構的各種功能,比起云計算型應用模式可能會逐漸變得越來越不那么重要。最后 作者用比較簡潔抽象的方式構建了基于Marathon、Mesos的奶牛模式使用場景】
標題 ##關于寵物和奶牛模式
寵物和奶牛并不是一個新比喻,對素食主義者先說一聲抱歉,這種比喻并不是我,而是熊們(bears)總是重復這個比喻。
一般來說,通過ssh進入一臺設備,配置她,然后給她一個可愛的或者博學的名字----這是寵物模式。如果不幸她死了,病了,或者閃亮紅色外表變黑了你會很傷心;
奶牛模式,代表著另外一種批量方式,例如你可以給許多機柜中的某一臺設備命名cow_with_big_ram_and_ssd_00003,如果某一個服務死機了,一個替代會自動部署好并且啟動。
這種類比可以存在于在云或者公司數據中心內。
現在,許多人們傾向認為奶牛模式更加適合需求。但是事實上,跟大多數人認為相左或者很多人不愿意承認,許多有數據中心或者云平臺的商家,里面都是寵物,這是真的,或許他們是虛擬化的,他們仍然是寵物模式的。
標題 ##深入“非常重要的”服務內部
正如我剛才宣稱幾乎每個人仍然運行許多寵物,這是因為sysops(譯者注:系統管理員)認為某些服務“非常重要”或者“太核心”。在某些技術超 前的商家,這些服務限于bind,dhcp,haproxy,zoopkeeper,etcd;略帶諷刺色彩的,在像openstack、hadoop namenodes等復雜控制框架中,chef/puppet服務也被這樣認為。如此種種。。。
在Factual,我得承認,我們也有很多類似的情況,這也是為什么我會花很多業余時間來調查我們的傾向,從我們的sysops和工程師團隊找到 某些原因。從根源上,這種誘惑,或者說陷阱,來自于認為:這些服務如此“核心”, 以至于不能去相信不成熟的帶有爭議的抽象軟件層;同時這些服務如此“重要”,以至于不能與其它耗用RAM,CPU或者IO資源的服務放在同一臺設備中。其 結局就是變成很多寵物,每個寵物都是一個單點故障,而且系統變得很復雜:最佳情況是記不得每臺機器是干什么的;最壞情況是一團混亂。
我們要做的第一件事情就是打破這種復雜性,我們需要為“非寵物化的核心服務”(pet-free core services)設置一些基本規則,他們是:
? 僅把絕對需要的放入“核心服務”
? 自動部署(相對于需要ssh,敲命令部署模式)
? 在很多“輕量級設備(beefier machines)”中運行更多服務
? 跟主機名和物理機無關
? 跟每種服務RAM和CPU限制隔離
? 核心服務必須是冗余的
標題 ##LXC容器,資源管理器和容器管理戰爭
關于server和devops正在進行一場解構性的競賽,從這點上來說,我認為,目前說“Docker是容器的最佳選擇”是安全的。不管是否同 意以上說法,大多數人已經開始嘗試Docker了,至少目前Docker很火,在大多數場景下適用,實用,獨特;因此把服務Docker化會感到安全,而 CoreOS Kerfuffles看起來短期內不會改變這種狀況。
資源管理的競爭更復雜一些。Hadoop生態環境都已經轉移到Yarn上。Yarn不太適合非hadoop化的環境(non-hadoop- things),而對于傳統hadoop環境(2U設備帶12塊硬盤)非常有效,盡管不是專用目的,其有效率可以達到將近100%。盡管場景不太一樣,但 是hadoop基本會將Mesos或者Kubernetes運行在Yarn上以便更好利用設備資源。
仍然有很多其它服務不需要運行在hadoop環境上,我喜歡選擇Mesos來管理他們。關于這點,我持開放的態度,等待各種批評。但是為什么我要 選在Mesos?因為Mesos使用了一種直接的,基于成熟高可用組件zookeeper的安裝方式,它有大量的用戶,成熟的文檔,而且我喜歡 amplab---盡管這并不是最重要的原因,我后面會解釋。
容器管理領域似乎還是硝煙彌漫,Kubernetes有(a Clintonesque inevitability to it),考慮到現狀,尤其是Mesosphere提供了另外一種Mesos框架,我們可以合理推斷最終會采用Kubernetes。現在,我會演示并且詳 細記錄基于Marathon的實現方式。
這里我表示再次開放接受批評。。。。
但是,事實確實是這樣的,如果你對比一下配置文件,大家看起來幾乎是一樣的-----json文件中配置用什么
Docker image,多少實例,多少CPU,內存,什么端口,服務,以及一些通用“行話”。如我在資源管理器中所說,這些都無關緊要。。。(開放接受批評。。。) 好吧,這里我可能過度簡化了,但是我會強調Kubernetes配置、Marathon配置和其他類似配置,我覺得如果我可以了解不同,我就可以挑出最好 的一個。如果這樣,與其等著其中某一個統治這七個王國,我可以主動挑一個,而且如果一旦發現有問題也可以毫無問題的重選。實際上,這種抽象的美妙之處在于 他們可以共存而且你可以隨時轉向一個更好的。
我的論點是:Docker是許多真實配置和提交代碼最終運行的地方,沒有其它強有力的競爭者。即使突然出現一個,肯定需要方便辦法從Docker移植過去。同時,容器管理領域還需要繼續演進,這可以接受,我不會等著,而是會主動先去嘗試各種選擇,來體會帶來的好處。
標題 ##Docker寵物
在此觀點被廣泛接受前,我還想指出關于Docker的一件事情。
使用dockerfiles來編譯Docker images是一種目前唯一可行的方法。如果你連接或者ssh到一個細心雕琢的image,最后用‘docker push’來創建她,這是一只寵物,一個Docker寵物,在某些地方,他們還會吃狗(原文:In some countries, they still eat dog),如果文化上可以接受,那就會存在(原文:if that's culturally acceptable, so be it)。手動創建images?不是吧?但是如果你不能回退20條命令前的配置,或者只能通過改變代碼回到基礎OS,你又在讓Docker等同于一只寵物 了。
標題 ##意識流的終結(END OF MY STREAM OF CONSCIOUSNESS OPINION-FEST)
好吧,前面都是讓我做下面這些事情的推動力。接下來,我要做的就是讓所有的核心服務都運行在Docker中,而與機器本身配置沒有任何關系。一旦我們有了最小一組核心服務,我們可以用一種易于部署和擴展的方式讓所有其他服務運轉起來,提供一種完全使用設備資源的能力。
標題 ##開始搭建集群(Finally Building The Cluster)
我們要搭建的集群包括兩部分:核心節點和一系列Mesos從服務節點。為了簡化,我盡量在示例配置中避免一些問題,但是如果你用于生產,則不能忽略這些。
另外,一些機器配置命令也用命令行方式直接寫出來,在實際中,這些命令需要用PXE boot和ansible/chef/puppet等方式。
## 標題 ## ##在所有節點上
假設你已經安裝好了vanilla Ubuntu或者其他你愿意的系統。。。。。
## ## 標題 ## ## ##激活swap和內存記賬(accouting)
sudo sed -i 's/^GRUB_CMDLINE_LINUX=""/GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1"/' /etc/default/grub sudo update-grub
## ## 標題 ## ## ##添加docker repo和最新安裝包
echo deb https://get.docker.com/ubuntu docker main > /etc/apt/sources.list.d/docker.list apt-get update apt-get install lxc-docker
標題 ##在核心節點上
為了安裝核心節點,我們選擇三臺相對比較強勁的硬件設備。需要安裝的典型服務包括bind,dhcp,zookeeper,mesos和 marathon。每個服務都要運行在容器中,每個容器都帶有合理容量的內存和CPU,關鍵點在于每種服務都有各自的image和唯一的配置文件(包含主 機名,IP,同種服務通過動態環境變量傳遞),這種方式給核心服務以冗余性,隔離性,可移植性等重要特性。
在配置過程中,bind和dhcp服務都采用無狀態主服務器模式,而不是基于failover策略的主從模式;另外,每個實例都從nginx服務(從一個git repo上同步)上獲得自己的配置,
## 標題 ## ##zookeeper
## ## 標題 ## ## ##配置數據卷(data volumes)
在每臺節點上,運行如下命令:
mkdir -p /disk/ssd/data/zookeeper mkdir -p /disk/ssd/log/zookeeper docker run -d -v /disk/ssd/data/zookeeper/data:/data --name zookeeper-data boritzio/docker-base true docker run -d -v /disk/ssd/log/zookeeper:/data-log --name zookeeper-data-log boritzio/docker-base true
## ## 標題 ## ## ##每個節點上啟動zookeeper
this just assigns the zookeeper node id based on our numbering scheme with is machine1x0 -> x+1 MACHINE_NUMBER= hostname | perl -nle '$_ =~ /(\d+)$/; print (($1+10-100)/10)'
docker run -e ZK_SERVER_ID=$MACHINE_NUMBER --restart=on-failure:10 --name zookeeper -p 2181:2181 -p 2888:2888 -p 3888:3888 -e HOSTNAME= hostname
-e HOSTS=ops100,ops110,ops120 -m 2g --volumes-from zookeeper-data --volumes-from zookeeper-data-log boritzio/docker-zookeeper
## 標題 ## ##mesos master
## ## 標題 ## ## ##配置數據卷(data volumes)
mkdir -p /disk/ssd/data/mesos/workdir docker run -d -v /disk/ssd/data/mesos/workdir:/workdir --name mesos-workdir boritzio/docker-base true
## ## 標題 ## ## ##啟動mesos master
docker run --restart=on-failure:10 --name mesos-master -p 5050:5050 -m 1g -e MESOS_ZK=zk://ops100:2181,ops110:2181,ops120:2181/mesos -e MESOS_CLUSTER=factual-mesosphere -e MESOS_WORK_DIR=/workdir -e MESOS_LOG_DIR=/var/log/mesos/ -e MESOS_QUORUM=2 -e HOSTNAME= hostname
-e IP= hostname -i
--volumes-from mesos-workdir boritzio/docker-mesos-master
標題 ##marathon
## 標題 ## ##啟動marathon
use host network for now... docker run --restart=on-failure:10 --net host --name marathon -m 1g -e MARATHON_MASTER=zk://ops100:2181,ops110:2181,ops120:2181/mesos -e MARATHON_ZK=zk://ops100:2181,ops110:2181,ops120:2181/marathon -e HOSTNAME= hostname
boritzio/docker-marathon
標題 ##從(slave)節點上
這是我們的“奶牛”設備。實際工作中,需要對他們分配合適的機柜和規則。
## 標題 ## ##添加mesosphere repo和最新安裝包
echo "deb http://repos.mesosphere.io/ubuntu/ trusty main" > /etc/apt/sources.list.d/mesosphere.list apt-key adv --keyserver keyserver.ubuntu.com --recv E56151BF apt-get update && apt-get -y install mesos
## 標題 ## ##set up slave
確保zookeeper和mesos master在slave設備上沒有運行
sudo stop zookeeper echo manual | sudo tee /etc/init/zookeeper.override sudo stop mesos-master echo manual | sudo tee /etc/init/mesos-master.override
拷貝 zookeeper config 和設置本地IP地址
echo "zk://ops100:2181,ops110:2181,ops120:2181/mesos" > /etc/mesos/zk HOST_IP= hostname -i
echo $HOST_IP > /etc/mesos-slave/ip #add docker to the list of containerizers echo 'docker,mesos' > /etc/mesos-slave/containerizers #this gives it time to pull a large container echo '5mins' > /etc/mesos-slave/executor_registration_timeout #this gives the mesos slave access to the main storage device (/disk/ssd/) mkdir -p /disk/ssd/mesos-slave-workdir echo '/disk/ssd/mesos-slave-workdir' > /etc/mesos-slave/work_dir #ok, now we start start mesos-slave
標題 ##HAPROXY代理
為了使得系統對下層節點真正透明,需要運行一臺負載均服務。Marathon中內置一個簡單腳本,它會從marathon api中拉(pull)下服務狀況,更新HAproxy配置,這點很棒,但是因為marathon的工作模式,每個可訪問服務都被分配一個對外的“端口號 (service port)”,這些端口號都是HAproxy配置對外發布的。那么問題是,所有服務都想通過80端口發布自己的服務,因此另外一個proxy需要放置在 marathon配置的HAproxy之前。
標題 ##為服務和主節點設置簡稱(ALIASES )
Mesos Master,Marathon和Chronos都有webUI和RESTFul API接口,但是他們運行的多主機方案有些許不同。某些情況下,像Marathon和Chronos,和任何slave主機通訊將被proxy到主服務 器;而對于Mesos主服務器,slave服務器上的web應用將會被重定向到主服務器。
期望用戶去了解哪臺服務器是主服務器,這肯定是不好的用戶體驗。因此,我們會提供一個可負載均衡的代理服務器,提供簡稱服務,例如:mesos.mydomain.com,marathon.mydomain.com,或者chronnos.mydomain.com
標題 ##haproxy for mesos master
首先我們把mesos.mydomain.cn的DNS指向到haproxy節點,然后把所有mesos-master節點加到最后。現在有一個 技巧,我們想haproxy只代理發到制定主節點上的請求,為了做到這一點,所有非master節點將會對健康檢查請求返回失敗。這個方式保證對新選舉出 來的主服務器,一樣可以提供一致性,保證指向最新的master服務器。
這里的技巧是,當查看/master/state.json,主服務器可能有不同的響應,我們查詢字符串‘elected time’,這個只從主服務器上返回。
backend mesos mode http balance roundrobin option httpclose option forwardfor option httpchk /master/state.json http-check expect string elected_time timeout check 10s server ops100 10.20.6.123:5050 check inter 10s fall 1 rise 3 server ops110 10.20.40.203:5050 check inter 10s fall 1 rise 3 server ops120 10.20.51.2:5050 check inter 10s fall 1 rise 3
以上結果返回兩個失敗節點和一個健康節點。
標題 ##Our First Services
在保證我們有足夠的基礎平臺基礎上,可以運行剩下的服務。現在主服務起來后,剩下的服務都可以通過現在的框架進行管理。說明一下這點,我們實際上會再Marathon之上運行基于Docker上的Chronos。不僅僅是因為這是說明這點的正確方式,也是正確的運行方式。
## 標題 ## ##LAUNCH SCRIPT
可以通過marathon gem或者只是做一個簡單的腳本,例如:
!/bin/bash if [ "$#" -ne 1 ]; then echo "script takes json file as an argument" exit 1; fi curl -X POST -H "Content-Type: application/json" marathon.mycompany.com/v2/apps -d@"$@"
啟動若干服務器后,marathon UI看起來是這樣的:
## 標題 ## ##CHRONOS
Chronos本質上是cron-on-mesos,這是一個用來運行基于容器定時任務的Mesos框架,我們將會提供基于Marathon的幾個實例。
我們可以用json配置來描述服務。
{ "id": "chronos", "container": { "type": "DOCKER", "docker": { "image": "boritzio/docker-chronos", "network": "HOST", "parameters": [ { "key": "env", "value": "MESOS_ZK=zk://ops100:2181,ops110:2181,ops120:2181/mesos" }, { "key": "env", "value": "CHRONOS_ZK=ops100:2181,ops110:2181,ops120:2181" } ] } }, "instances": 3, "cpus": 1, "mem": 1024, "ports": [4400] }
然后我們把json配置貼到marathon API服務器上,
~/launch.sh chronos.json
顯示如下:
標題 ##Launch Away!
結果就是這樣了。以上方法給我們一個好的思路,針對“太核心”服務,不需要太多妥協,幫助我們設置一個相對“非寵物式環境(pet-free environment)”。當你熟悉這種方法后,有時候盡管臨時服務會比較慢,但是從全局看,對所有服務都提供一種更好的環境,特別是當硬盤損壞時。
Gratuitous Promotion-y Stuff
如果你讀了所有內容,而且對你看到的有很強的想法,并且想幫助我們提供更加自動和優化devops,可以看看如下鏈接:包括 : http://www.factual.com/jobs/ow ... neer.
我很少使用tweet,但是大家可以聯系我@shimanovsky.
標題 ##所有代碼
在以下連接中我包括了所有自動創建的Dockerfiles和marathon服務配置,希望能夠幫助大家了解這篇文章中省略的內容
標題 ##CORE SERVICES
Zookeeper
https://github.com/bfs/docker-zookeeper
Mesos Master
https://github.com/bfs/docker-mesos-master
Marathon
https://github.com/bfs/docker-marathon
標題 ##RUN THESE ON MARATHON
Chronos
https://github.com/bfs/docker-chronos
標題 ##一些使用marathon的應用配置
Ruby Web App (Rails or Sinatra) From Github
以下是配置步驟,用來從Github獲取代碼部署為ruby web應用。我們用它來啟動幾個內部Sinatra應用。
Docker: https://github.com/bfs/docker-github-rubyapp
Marathon Config: https://gist.github.com/bfs/b34b7e09b0a2360e60e1
Postgresql with Postgis
以下是帶Postgisde Postgresql服務,可以用Marathon啟動 Docker: https://github.com/bfs/docker-postgis
Marathon Config: https://gist.github.com/bfs/be77416a19bec481b584
–Boris Shimanovsky, VP of Engineering
Discuss this post on Hacker News.
Tweet
Categories: Data, Engineering and Product