由Docker引领的容器技术最近一年在生产环境叫嚣的比较厉害,由于Docker本身拥有的一些特性,使得越来越多的人愿意并且想尝试在生产环境构建Docker,有关docker相关的介绍可以看我去年发布的文章( )。然而随着业务的规模不断扩大,对docker的管理和维护也对运维人员有一些挑战,使用一些开源的框架和服务满足互联网公司的基本需求是一种常见而高效的方式,本篇文章就简单介绍一下使用Mesos+Marathon来对docker集群进行管理和维护。

 

一、Mesos简介

Mesos是Apache下的开源分布式资源管理框架,它被称为是分布式系统的内核,提供了有效的、跨分布式应用或框架的资源隔离和共享,可以运行Hadoop、MPI、Hypertable、Spark、docker等。

docker 集群 skywalking docker 集群化管理_zookeeper


上图是mesos的管理服务的架构流程图,主要进程包括一个 主控 守护进程,用来管理子节点的 被控 守护进程,也即Mesos-master和Mesos-slave。

可以看到整个mesos架构会有以下几个角色:

Mesos master,主要负责管理各个framework和slave,并将slave上的资源分配给各个framework
Mesos slave,负责管理本节点上的各个mesos-task,比如:为各个executor分配资源
Framwork,计算框架,如:Hadoop,Spark,Docker等,通过MesosSchedulerDiver接入Mesos
Excutor,执行器,安装到mesos-slave上,用于启动计算框架中的task。

        主控(Mesos-master)负责决定给每个应用分配多少资源,而应用框架(部署在mesos-slave上的服务)的调度器才会真正选择使用哪些被分配到的资源。当应用框架接收了分配的资源,它会向Mesos发送一个它希望运行任务的描述信息。然后,Mesos会负责在相应的被控节点上启动任务。

 

资源调度流程:

docker 集群 skywalking docker 集群化管理_docker_02


详细mesos教程请查看官方文档:http://mesos.mydoc.io/

二、Marathon基础知识

        Marathon(马拉松)是一个全新的框架,它将Mesos变成一个更有活力的工具,进而可以在单一的集群上运行不同的应用程序。

它的设计宗旨就是让用户在同一组服务器之上,更智能地运行多种应用程序和服务——Hadoop、Storm,甚至一个标准的Web应用。Marathon出自于一家初创公司 Mesosphere之手,这家公司主要就是想构建一个数据中心操作系统,不过这个系统是运行在Apache Mesos集群管理软件之上,这也是 Twitter基础设施的重要组成部分。

        Mesos仅仅是适用于集群的管理,这意味着它可以隔离不同的任务负载。但是仍然需要额外的工具来帮助工程师查看不同系统上运行的工作负载。不然的话,如果某些工作负载消耗了所有资源,那么重要的工作负载可能就难以及时地获得资源。

        想要理解mesos和marathon之间的关系,简单粗暴的可以理解为mesos就是集群的内核,负责资源调度,而marathon则是集群的进程管理器(init.d/systemd),用来管理应用的状态信息。

 

三、Zookeeper基础知识

        由于mesos组件之间的调度需要使用zk来共享配置信息,因此这里讲对zookeeper进行简单的介绍。

ZooKeeper是用来给集群服务维护配置信息,域名服务,提供分布式同步和提供组服务。所有这些类型的服务都使用某种形式的分布式应用程序。是一个分布式的,开放源码的协调服务,是的Chubby一个的实现,是Hadoop和Hbase的重要组件。

Zookeeper分为以下几个角色:

领导者(leader):领导者负责投票发起和决议,更新系统状态
跟随者(follwoer):follower用于接收客户请求并向客户端返回结果,在选主过程中参与投票
观察者:ObServer可以接受客户端连接,将写请求转发给leader节点,但ObServer不参加投票过程,只同步leader的状态,ObServer的目的是为了拓展系统,提高读取速度。
客户端:请求发起方

Zookeeper工作原理:

        Zookeeper的核心是原子广播,这个机制保证了各个Server之间的同步。实现这个机制的协议叫做Zab协议。Zab协议有两种模式,它们分别是恢复模式(选主)和广播模式(同步)。当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数Server完成了和leader的状态同步以后,恢复模式就结束了。状态同步保证了leader和Server具有相同的系统状态。

        为了保证事务的顺序一致性,zookeeper采用了递增的事务id号(zxid)来标识事务。所有的提议(proposal)都在被提出的时候加上了zxid。实现中zxid是一个64位的数字,它高32位是epoch用来标识leader关系是否改变,每次一个leader被选出来,它都会有一个新的epoch,标识当前属于那个leader的统治时期。低32位用于递增计数。
每个Server在工作过程中有三种状态:

  • LOOKING:当前Server不知道leader是谁,正在搜寻
  • LEADING:当前Server即为选举出来的leader
  • FOLLOWING:leader已经选举出来,当前Server与之同步

四、Mesos+Marathon+Docker集群部署

(一)系统环境

#uname -r 
3.10.0-327.4.5.el7.x86_64
#cat /etc/redhat-release 
CentOS Linux release 7.2.1511 (Core)

 

(二)架构

两台主机:

10.13.18.23 mesos-master,mesos-slave,marathon,docker,zk伪分布式
10.13.18.22 mesos-slave,docker

注意,两台主机必须可以相互解析到。因此需要在/etc/hosts下进行配置

#cat /etc/hosts
docker1 10.13.18.23
docker2 10.13.18.22
#hostnamectl set-hostname docker1
#hostnamectl set-hostname docker2

(三)组件部署

1.安装docker

#yum install docker 

#sysctmctl start docker

#docker --version 查看docker的当前版本

centos7默认自带的是1.10.3

注意:docker需要libcgroup支持

#docker pull nginx #先下载一个最新版本的nginx镜像

#docker images #查看docker当前镜像文件

#docker run -itd -h nginx --name nginx -p 80:80 docker.io/nginx

#curl localhost:80 #测试nginx容器是否正常

2.mesos+mathron+zookeeper集群搭建

2.1 zookeeper搭建:

由于zk是进行配置发现的,因此也需要配置成集群模式,在这里我将使用docker1主机进行搭建zk伪分布式

#wget http://mirrors.cnnic.cn/apache/zookeeper/stable/zookeeper-3.4.8.tar.gz -P /export/zookeeper
也可以使用cdh的源
#rpm -Uvh http://archive.cloudera.com/cdh4/one-click-install/redhat/6/x86_64/cloudera-cdh-4-0.x86_64.rpm
当然了运行zk是需要java环境的
#tar zxf zookeeper-3.4.8.tar.gz

编辑伪分布式zk配置模板,如下:

[root@docker1 conf]# cat /export/zookeeper/zookeeper-3.4.8/conf/zoo.cfg 
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/export/zookeeper/zk1
clientPort=2181
server.1=10.13.18.23:3181:4181
server.2=10.13.18.23:3182:4182
server.3=10.13.18.23:3183:4183
[root@docker1 conf]# cp zoo.cfg zk1.cfg
[root@docker1 conf]# cp zoo.cfg zk2.cfg
[root@docker1 conf]# cp zoo.cfg zk3.cfg
[root@docker1 conf]# sed -i 's/2181/2182/' zk2.cfg 
[root@docker1 conf]# sed -i 's/2181/2183/' zk3.cfg 
[root@docker1 conf]# sed -i 's/zk1/zk3/' zk3.cfg 
[root@docker1 conf]# sed -i 's/zk1/zk2/' zk2.cfg 
[root@docker1 conf]# for i in 1 2 3;do mkdir -p /export/zookeeper/zk$i;done 
[root@docker1 conf]# for i in 1 2 3;do echo $i >  /export/zookeeper/zk$i/myid;done 
启动zk:
[root@docker1 conf]# /export/zookeeper/zookeeper-3.4.8/bin/zkServer.sh start /export/zookeeper/zookeeper-3.4.8/conf/zk1.cfg 
[root@docker1 conf]# /export/zookeeper/zookeeper-3.4.8/bin/zkServer.sh start /export/zookeeper/zookeeper-3.4.8/conf/zk2.cfg 
[root@docker1 conf]# /export/zookeeper/zookeeper-3.4.8/bin/zkServer.sh start /export/zookeeper/zookeeper-3.4.8/conf/zk3.cfg 
启动完成后查看下状态,一般zk集群个数为3个会比较好些,因此有一些节点是Mode: leader ;Mode: follower
[root@docker1 conf]# /export/zookeeper/zookeeper-3.4.8/bin/zkServer.sh status /export/zookeeper/zookeeper-3.4.8/conf/zk3.cfg

zk启动异常

查看日志文件:
zookeeper.out
2016-07-10 14:58:24,367 [myid:1] - INFO  [QuorumPeer[myid=1]/0:0:0:0:0:0:0:0:2181:FastLeaderElection@818] - New election. My id =  1, proposed zxid=0x0
2016-07-10 14:58:24,368 [myid:1] - INFO  [WorkerReceiver[myid=1]:FastLeaderElection@600] - Notification: 1 (message format version), 1 (n.leader), 0x0 (n.zxid), 0x1 (n.round), LOOKING (n.state), 1 (n.sid), 0x0 (n.peerEpoch) LOOKING (my state)
2016-07-10 14:58:24,373 [myid:1] - WARN  [WorkerSender[myid=1]:QuorumCnxManager@400] - Cannot open channel to 2 at election address /10.81.152.47:4182
java.net.ConnectException: Connection refused
        at java.net.PlainSocketImpl.socketConnect(Native Method)
        at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
        at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
        at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
        at java.net.Socket.connect(Socket.java:589)
        at org.apache.zookeeper.server.quorum.QuorumCnxManager.connectOne(QuorumCnxManager.java:381)
        at org.apache.zookeeper.server.quorum.QuorumCnxManager.toSend(QuorumCnxManager.java:354)
        at org.apache.zookeeper.server.quorum.FastLeaderElection$Messenger$WorkerSender.process(FastLeaderElection.java:452)
        at org.apache.zookeeper.server.quorum.FastLeaderElection$Messenger$WorkerSender.run(FastLeaderElection.java:433)
        at java.lang.Thread.run(Thread.java:745)

 

修改内核配置:

net.ipv4.ip_local_port_range = 1024 65535

 

2.2 mesos+marthon搭建:

配置mesosphere源:

因为docker节点是需要装mesos-slave的,因此在所有的docker节点都应该安装
[root@docker1 zookeeper]# rpm -Uvh http://repos.mesosphere.com/el/7/noarch/RPMS/mesosphere-el-repo-7-1.noarch.rpm
[root@docker2 ~]#  rpm -Uvh http://repos.mesosphere.com/el/7/noarch/RPMS/mesosphere-el-repo-7-1.noarch.rpm
 
安装mesos和marthon:
[root@docker1 conf]# yum -y install mesos marathon
[root@docker2 conf]# yum -y install mesos marathon
配置mesos:
[root@docker1 zookeeper]# cat /etc/mesos/zk 
zk://10.13.18.23:2181,10.13.18.23:2182,10.13.18.23:2183/mesos
启动mesos:
[root@docker1 zookeeper]# systemctl start mesos-master
[root@docker1 zookeeper]# systemctl start mesos-slave
[root@docker1 zookeeper]# systemctl start marathon

docker2只是一个应用节点,因此启动slave就行:

需要注意的是因为其实marathon就类似init.d,因此针对mesos来说,他也是一个应用,所以需要用marathon去管理mesos-slave

[root@docker2 ~]# systemctl start mesos-slave
[root@docker2 zookeeper]# systemctl start marathon

Mesos-master启动之后会默认开启5050一个web端口,用来进行资源协调。(linux kernel)

marathon 启动会默认开启一个8080端口,进行任务调度(init.d)

访问http://10.13.18.23:5050 可以查看mesos相关信息:

docker 集群 skywalking docker 集群化管理_json_03


此时点击slave会发现当前mesos集群管理着两个资源节点,即docker1和docker2主机。

docker 集群 skywalking docker 集群化管理_json_04


访问http://10.13.18.23:8080 可以查看marathon的相关信息:

docker 集群 skywalking docker 集群化管理_zookeeper_05

看到mesos和marathon的相关信息之后,我就可以任务mesos+marathon架构已经基本搭建完成。

(四)mesos+marathon架构的简单应用

4.1 创建tst_task任务

默认的mesos管控任务里其实没有任务进程的,可以使用以下的命令简单创建任务:

#MASTER=$(mesos-resolve `cat /etc/mesos/zk`)
#mesos-execute --master=$MASTER --name="cluster-test" --command="sleep 60"

此时就会发现在mesos的web界面上出现一个新的任务:

docker 集群 skywalking docker 集群化管理_json_06


4.2 使用marathon创建nginx的docker容器,使用mesos进行调度。

注意:marathon启动的时候会根据mesos的信息链接zk。
 
配置Mesos运行Docker容器:
再所有mesos-slave上增加配置参数,并重启
#echo 'docker,mesos' | tee /etc/mesos-slave/containerizers
#systemctl restart mesos-slave
首先,我们创建一个json文件,用来通过marathon的api进行创建容器:
#vim nginx.json
{
  "id":"nginx",
  "cpus":0.2,
  "mem":20.0,
 "instances": 1,
 "constraints": [["hostname", "UNIQUE",""]],
 "container": {
    "type":"DOCKER",
   "docker": {
     "image": "nginx",
     "network": "BRIDGE",
     "portMappings": [
        {"containerPort": 80, "hostPort": 0,"servicePort": 0, "protocol": "tcp" }
      ]
    }
  }
}

创建容器,成功创建后会返回json串

[root@docker1 ~]# curl -X POST http://10.81.152.47:8080/v2/apps -d @nginx.json -H "Content-type: application/json"
 
[root@docker1 ~]# docker ps -a 
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS                            NAMES
0d5c10207f04        nginx               "nginx -g 'daemon off"   About a minute ago   Up About a minute   443/tcp, 0.0.0.0:31975->80/tcp   mesos-ce041d30-c4bc-437d-829c-4fb09b0ce682-S0.b99a86b5-3b6f-4c58-b3db-e6ff0d2c6bcf
此时就可以看到使用marathon的api创建的nginx容器了,可以访问本地的31975端口来测

试nginx的正常与否。

成功的使用marathon创建了一个docker容器!

 

docker 集群 skywalking docker 集群化管理_zookeeper_07