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