参考文档:

  1. mesos:

  2. mesosphere社区版:

  3. mesosphere仓库:

  4. marathon:

  5. chronos:

  6. consul:

  7. docker安装:

一、系统说明

mesos 集群资源管理框架(以集群的形式出现,主决定资源的分配,从负责执行executor),分配资源,提供offer给framework,不负责调度资源,千言万语都在这里了

marathon 是mesos的framework,容器编排系统,保证长时间的运行任务,类似后台执行或者supervisor。

zooKeeper是一个的,开放源码的协调服务,是的Chubby一个的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置管理维护、域名服务、分布式同步、组服务、健康检查,HA等。

Chronos是一个运行在Mesos之上的具有分布式容错特性的作业调度器。在Airbnb公司,它是cron的替代品。与cron相比,Chronos在很多方面具备优势。比如,它支持ISO8601标准,允许更灵活地定义调度时间;Chronos也支持任务依赖,即一个作业的开始依赖于一些任务的完成,和marathon一样都是听从mesos的指令,给mesos搬砖的。

consul经常拿来和etcd、 zookeeper来对比,他有和etcd一样的服务发现、服务注册功能,也有和etcd、zookeeper一样的配置存储功能,详细的对比在此不再列出。

Registrator是一个独立于服务注册表的自动服务注册/注销组件,一般以容器的方式进行部署。会自动侦测它所在的宿主机上的所有 容器状态(启用/销毁),并根据容器状态到对应的服务注册列表注册/注销服务。去除了需要手动管理Consul服务条目的复杂性,它监视容器的启动和停止,根据容器暴露的端口和环境变量自动注册服务。

事实上, 通过读取同一台宿主机的其他容器 的环境变量进行服务注册、健康检查定义等操作,支持可插拔式的服务注册表配置。

架构图看起来像这样:

mesos架构:

架构图可以看出mesos主要两部分主和从 ,主集群依靠zookeeper

架构图展示了Mesos的重要组成部分,Mesos由一个master进程管理运行着每个客户端节点的slave进程和跑任务的Mesos计算框架。

Mesos进程通过计算框架可以很细致的管理cpu和内存等,从而提供资源。每个资源提供都包含了一个清单(slave ID,resource1:amount1,resource2,amount2,…)master会根据现有的资源决定提供每个计算框架多少资源。例如公平分享或者根据优先级分享。

为了支持不同种的政策,master通过插件机制新增额一个allocation模块使之分配资源更简单方便。

一个计算框架运行在两个组建之上,一个是Scheduler,他是master提供资源的注册中心,另一个是Executor程序,用来发起在slave节点上运行计算框架的任务。master决定给每个计算框架提供多少计算资源,计算框架的调度去选择使用哪种资源。当一个计算框架接受了提供的资源,他会通过Mesos的任务描述运行程序。Mesos也会在相应的slave上发起任务。

Mesos是Apache下的开源分布式资源管理框架,它被称为分布式系统的内核。Mesos最初是由加州大学伯克利分校的AMPLab开发,后在Twitter得到广泛使用。

Mesos-Master:主要负责管理各个framework和slave,并将slave上的资源分配给各个framework。

Mesos-Slave:负责管理本节点上的各个mesos-task,比如:为各个executor分配资源。

Framework:计算框架,如:Hadoop、Spark、Kafaka、ElasticSerach等,通过MesosSchedulerDiver接入Mesos

Executor:执行器,就是安装到每个机器节点的软件,这里就是利用docker的容器来担任执行器的角色。具有启动销毁快,隔离性高,环境一致等特点。

Mesos-Master是整个系统的核心,负责管理接入Mesos的各个framework(由frameworks_manager管理)和slave(由slaves_manager管理),并将slave上的资源按照某种策略分配给framework(由独立插拔模块Allocator管理)。

Mesos-Slave负责接受并执行来自Mesos-master的命令、管理节点上的mesos-task,并为各个task分配资源。Mesos-slave将自己的资源量发送给mesos-master,由mesos-master中的Allocator模块决定将资源分配给哪个framework,当前考虑的资源有CPU和内存两种,也就是说,Mesos-slave会将CPU个数的内存量发送给mesos-master,而用户提交作业时,需要指定每个任务需要的CPU个数和内存。这样:当任务运行时,mesos-slave会将任务放导包含固定资源Linux container中运行,以达到资源隔离的效果。很明显,master存在单点故障问题,为此:Mesos采用了Zookeeper解决该问题。

Framework是指外部的计算框架,如果Hadoop、Mesos等,这些计算框架可通过注册的方式接入Mesos,以便Mesos进行统一管理和资源分配。Mesos要求可接入的框架必须有一个调度模块,该调度器负责框架内部的任务调度。当一个framework想要接入Mesos时,需要修改自己的调度器,以便向Mesos注册,并获取Mesos分配给自己的资源,这样再由自己的调度器将这些资源分配给框架中的任务,也就是说,整个Mesos系统采用了双层调度框架:第一层,由Mesos将资源分配给框架。第二层,框架自己的调度器将资源分配给自己内部的任务。当前Mesos支持三中语言编写的调度器,分别是C++、Java、Python。为了向各种调度器提供统一的接入方式,Mesos内部采用C++实现了一个MesosSchedulerDriver(调度驱动器),framework的调度器可调用该driver中的接口与Mesos-master交互,完成一系列功能(如注册,资源分配等。)

Executor主要用于启动框架内部的task。由于不同的框架,启动task的接口或者方式不同,当一个新的框架要接入mesos时,需要编写一个Executor,告诉Mesos如何启动该框架中的task。为了向各种框架提供统一的执行器编写方式,Mesos内部采用C++实现了一个MesosExecutorDiver(执行器驱动器),framework可通过该驱动器的相关接口告诉Mesos启动task的方式。

mesos运行流程:

流程步骤:

1、slave1报告给master他拥有4核cpu和4G剩余内存,Marathon调用allocation政策模块,告诉slave1计算框架1应该被提供可用的资源。

2、master给计算框架1发送一个在slave上可用的资源描述。

3、计算框架的调度器回复给master运行在slave上两个任务相关信息,任务1需要使用2个CPU,内存1G,任务2需使用1个CPU,2G内存。

4、最后,master发送任务给slave,分配适当的给计算框架执行器,继续发起两个任务(图1.1-2虚线处),因为任有1个CPU和1G内存未分配,allocation模块现在或许提供剩下的资源给计算框架2。

除此之外,当任务完成,新的资源成为空闲时,这个资源提供程序将会重复

二、环境

说明:

主机名

IP

OS

安装服务

mesos-master1

192.168.8.131

CentOS-7.5

mesos-master, marathon,zookeeper, consul-server, chronos, consul-template

mesos-master2

192.168.8.132

CentOS-7.5

mesos-master, marathon,zookeeper, consul-server, chronos

mesos-master3

192.168.8.133

CentOS-7.5

mesos-master, marathon,zookeeper, consul-server, chronos

mesos-slave1

192.168.8.134

CentOS-7.5

mesos-slave, docker, registrator

mesos-slave2

192.168.8.135

CentOS-7.5

mesos-slave, docker, registrator

软件版本:

服务

版本

作用

zookeeper

3.4.12

保持各master之间的通信,选举leader、HA

mesos-master

1.6.0

管理接入mesos的各个framework & slave,并将slave上的资源按照相应策略分配给framework

mesos-slave

1.6.0

任务执行节点

marathon

1.6.352

调度器,用于下发任务,可保持长应用

docker

1.13.1

具体执行docker下发任务

chronos

3.0+

cron-on-mesos,用来运行基于容器的定时任务的Mesos框架

consul/ registrator

1.1.0/ latest

提供服务自动注册、发现功能

consul-template

0.19.4

Consul template 搭配consul使用,支持多种负载均衡接入层,如Nginx、Haproxy,LVS

三、Master节点部署

以节点zk-node1为例,zk-node2/3根据情况调整。

1、准备工作

# systemctl stop firewalld

# setenforce 0

# systemctl disable firewalld

# cat /etc/hosts

127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4

::1      localhost localhost.localdomain localhost6 localhost6.localdomain6

192.168.8.131  mesos-master1

192.168.8.132  mesos-master2

192.168.8.133  mesos-master3

192.168.8.134  mesos-slave1

192.168.8.135  mesos-slave2

# yum groupinstall -y "Development Tools"

# yum install -y tar wget git lrzsz lsof

 

2、zookeeper安装及HA

ZooKeeper运行在java环境下,文档中建议安装jdk 1.7以上版本(含)。

# yum -y install java java-devel

# java -version

openjdk version "1.8.0_171"

OpenJDK Runtime Environment (build 1.8.0_171-b10)

OpenJDK 64-Bit Server VM (build 25.171-b10, mixed mode)

# tar xf zookeeper-3.4.12.tar.gz -C /usr/local

# chown -R root.root /usr/local/zookeeper-3.4.12

# ln -s /usr/local/zookeeper-3.4.12/ /usr/local/zk

# cp /usr/local/zk/conf/zoo_sample.cfg /usr/local/zk/conf/zoo.cfg

zookeeper配置文件详解

下面就是zoo.cfg配置文件的修改了,那么我们首先要熟悉下zookeeper配置文件。

# cat /usr/local/zk/conf/zoo.cfg
dataDir:数据目录
dataLogDir:日志目录
clientPort:客户端连接端口
tickTime:Zookeeper 服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是每隔tickTime 时间就会发送一个心跳。
initLimit:Zookeeper的Leader 接受客户端(Follower)初始化连接时最长能忍受多少个心跳时间间隔数。当已经超过 5个心跳的时间(也就是tickTime)长度后 Zookeeper 服务器还没有收到客户端的返回信息,那么表明这个客户端连接失败。总的时间长度就是 5*2000=10 秒
syncLimit:表示 Leader 与 Follower 之间发送消息时请求和应答时间长度,最长不能超过多少个tickTime 的时间长度,总的时间长度就是 2*2000=4 秒。
server.A=B:C:D:
A 是一个数字,表示这个是第几号服务器;
B 是这个服务器的 ip 地址;
C 表示的是这个服务器与集群中的 Leader 服务器交换信息的端口;
D 表示的是万一集群中的 Leader 服务器挂了,需要一个端口来重新进行选举,选出一个新的 Leader,而这个端口就是用来执行选举时服务器相互通信的端口。

# grep -Ev "#|^$" /usr/local/zk/conf/zoo.cfg

tickTime=2000

initLimit=10

syncLimit=5

dataDir=/usr/local/zk/data/zk1

dataLogDir=/usr/local/zk/logs

clientPort=2181

maxClientCnxns=60

server.1=192.168.8.131:2888:3888

server.2=192.168.8.132:2888:3888

server.3=192.168.8.133:2888:3888

# mkdir /usr/local/zk/data/zk1 -p

# mkdir /usr/local/zk/logs -p

# echo "1" > /usr/local/zk/data/zk1/myid

# scp -p /usr/local/zk/conf/zoo.cfg root@192.168.8.132:/usr/local/zk/conf/zoo.cfg

# scp -p /usr/local/zk/conf/zoo.cfg root@192.168.8.133:/usr/local/zk/conf/zoo.cfg

# mesos-master2的zk配置,同样需要创建dataDir,dataLogDir目录和myid文件,文件内容2

# sed -i 's/zk1/zk2/g' /usr/local/zk/conf/zoo.cfg

# mkdir /usr/local/zk/data/zk2 -p

# mkdir /usr/local/zk/logs -p

# echo "2" > /usr/local/zk/data/zk2/myid

# grep -Ev "#|^$" /usr/local/zk/conf/zoo.cfg

tickTime=2000

initLimit=10

syncLimit=5

dataDir=/usr/local/zk/data/zk2

dataLogDir=/usr/local/zk/logs

clientPort=2181

maxClientCnxns=60

server.1=192.168.8.131:2888:3888

server.2=192.168.8.132:2888:3888

server.3=192.168.8.133:2888:3888

# mesos-master3的zk配置,同样需要创建dataDir,dataLogDir目录和myid文件,文件内容3

# sed -i 's/zk1/zk3/g' /usr/local/zk/conf/zoo.cfg

# mkdir /usr/local/zk/data/zk3 -p

# mkdir /usr/local/zk/logs -p

# echo "3" > /usr/local/zk/data/zk3/myid

# grep -Ev "#|^$" /usr/local/zk/conf/zoo.cfg

tickTime=2000

initLimit=10

syncLimit=5

dataDir=/usr/local/zk/data/zk3

dataLogDir=/usr/local/zk/logs

clientPort=2181

maxClientCnxns=60

server.1=192.168.8.131:2888:3888

server.2=192.168.8.132:2888:3888

server.3=192.168.8.133:2888:3888

启动zk服务,查看状态:

# /usr/local/zk/bin/zkServer.sh start

# /usr/local/zk/bin/zkServer.sh status

zk集群各节点状态:

zk各节点端口:

由此可见,集群已经正常运行。

客户端可以通过nc或telnet连接ZooKeeper Server提交指令。

ZooKeeper Client 简单操作:

9个基本操作指令:

3、安装mesos-master&marathon

Mesos 集群部署

Mesos集群有MesosMaster和Mesos Slave两个角色。

mesosphere仓库

需要在Mesos Master和MesosSlave节点均安装。

# 添加mesosphere repository,根据github mesosphere社区版获取最新repository

# rpm -Uvh http://repos.mesosphere.io/el/7/noarch/RPMS/mesosphere-el-repo-7-2.noarch.rpm

# hostnamectl --static set-hostname mesos-master1 //修改主机名

# yum -y install mesos marathon

4、mesos-master&marathon配置

mesos-master增加zookeeper配置,选主并增加HA:

# vi /etc/mesos/zk

zk://192.168.8.131:2181,192.168.8.132:2181,192.168.8.133:2181/mesos

# scp /etc/mesos/zk root@192.168.8.132:/etc/mesos/zk

# scp /etc/mesos/zk root@192.168.8.133:/etc/mesos/zk

设置文件/etc/master-/quorum内容为一个大于(master节点数除以2)的整数。即采用四舍五入,比如这里有3master节点,那么3/2=1.5,四舍五入为2

# echo "2" >/etc/mesos-master/quorum

# cat /etc/mesos-master/quorum

2

# scp /etc/mesos-master/quorum root@192.168.8.132:/etc/mesos-master/quorum

# scp /etc/mesos-master/quorum root@192.168.8.133:/etc/mesos-master/quorum

#主机名和ip要在hosts中写入,最好不要使用localhost,否则会出现slave不能识别,以及marathon任务下发不正常等现象。

# 默认marathon无相关配置目录/文件

# mkdir -p /etc/marathon/conf

mesos-master1:

# echo "192.168.8.131" >/etc/mesos-master/hostname

# echo "192.168.8.131" >/etc/mesos-master/ip

# echo "192.168.8.131" >/etc/marathon/conf/hostname

# echo "192.168.8.131" >/etc/marathon/conf/ip

mesos-master2:

# echo "192.168.8.132" >/etc/mesos-master/hostname

# echo "192.168.8.132" >/etc/mesos-master/ip

# echo "192.168.8.132" >/etc/marathon/conf/hostname

# echo "192.168.8.132" >/etc/marathon/conf/ip

mesos-master3:

# echo "192.168.8.133" >/etc/mesos-master/hostname

# echo "192.168.8.133" >/etc/mesos-master/ip

# echo "192.168.8.133" >/etc/marathon/conf/hostname

# echo "192.168.8.133" >/etc/marathon/conf/ip

marathon连接mesos-masterHA

# cp /etc/mesos/zk /etc/marathon/conf/master

# cp /etc/mesos/zk /etc/marathon/conf/zk

# sed -i 's|mesos|marathon|g' /etc/marathon/conf/zk

# cat /etc/marathon/conf/master

zk://192.168.8.131:2181,192.168.8.132:2181,192.168.8.133:2181/mesos

# cat /etc/marathon/conf/zk

zk://192.168.8.131:2181,192.168.8.132:2181,192.168.8.133:2181/marathon

# scp /etc/marathon/conf/master root@192.168.8.132:/etc/marathon/conf/

# scp /etc/marathon/conf/master root@192.168.8.133:/etc/marathon/conf/

# scp /etc/marathon/conf/zk root@192.168.8.132:/etc/marathon/conf/

# scp /etc/marathon/conf/zk root@192.168.8.133:/etc/marathon/conf/

5、启动mesos,marathon

# systemctl enable mesos-master marathon

# systemctl start mesos-master marathon

# systemctl disable mesos-slave

# systemctl status mesos-master marathon

# systemctl status marathon #marathon启动报错

● marathon.service - Scheduler for Apache Mesos

Loaded: loaded (/usr/lib/systemd/system/marathon.service; enabled; vendor preset: disabled)

Active: activating (auto-restart) (Result: exit-code) since Mon 2018-05-14 21:13:32 CST; 37s ago

Process: 25368 ExecStart=/usr/share/marathon/bin/marathon (code=exited, status=1/FAILURE)

……

Main PID: 25368 (code=exited, status=1/FAILURE)

May 14 21:13:32 mesos-master1 systemd[1]: marathon.service: main process exited, code=exited, status=1...LURE

……

Hint: Some lines were ellipsized, use -l to show in full.

用以下命令 [journalctl -o verbose _PID=24962] 查看marathon进程的详细信息。

# journalctl -o verbose _PID=25368

-- Logs begin at Sun 2018-05-13 03:47:48 CST, end at Mon 2018-05-14 21:08:12 CST. --

Mon 2018-05-14 21:07:04.849490 CST [s=2f8e0393e2dc4d639f4d5bf76a4e9b6f;i=ec5;b=4a9ee33c3e074eb987230b0202c47a

PRIORITY=6

_SYSTEMD_SLICE=system.slice

_BOOT_ID=4a9ee33c3e074eb987230b0202c47a39

_MACHINE_ID=74bb4614536844d798b123d0cc927d4e

SYSLOG_FACILITY=3

_TRANSPORT=stdout

_SELINUX_CONTEXT=system_u:system_r:init_t:s0

_EXE=/usr/bin/bash

_CAP_EFFECTIVE=0

_COMM=bash

_HOSTNAME=mesos-master1

SYSLOG_IDENTIFIER=marathon

MESSAGE=No start hook file found ($HOOK_MARATHON_START). Proceeding with the start script.

_UID=998

_GID=996

_CMDLINE=bash /usr/share/marathon/bin/marathon

_SYSTEMD_CGROUP=/system.slice/marathon.service

_SYSTEMD_UNIT=marathon.service

_STREAM_ID=bd3ee42018a94236baeab1e5f653171f

_PID=24962

Mon 2018-05-14 21:07:08.496043 CST [s=2f8e0393e2dc4d639f4d5bf76a4e9b6f;i=ec6;b=4a9ee33c3e074eb987230b0202c47a

PRIORITY=6

_SYSTEMD_SLICE=system.slice

_BOOT_ID=4a9ee33c3e074eb987230b0202c47a39

_MACHINE_ID=74bb4614536844d798b123d0cc927d4e

SYSLOG_FACILITY=3

_TRANSPORT=stdout

_SELINUX_CONTEXT=system_u:system_r:init_t:s0

_CAP_EFFECTIVE=0

_HOSTNAME=mesos-master1

SYSLOG_IDENTIFIER=marathon

_UID=998

_GID=996

_SYSTEMD_CGROUP=/system.slice/marathon.service

_SYSTEMD_UNIT=marathon.service

MESSAGE=[scallop] Error: Required option 'master' not found

_COMM=java

_EXE=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.171-7.b10.el7.x86_64/jre/bin/java

_CMDLINE=java -cp /usr/share/marathon/lib/mesosphere.marathon.marathon-1.6.352.jar:/usr/share/marathon/li

_STREAM_ID=bd3ee42018a94236baeab1e5f653171f

_PID=24962

通过上述日志可以看出,是找不到master导致启动失败。检查了下以上关于marathon的所有配置,确认没有问题。可以根据如下办法直接带master参数启动marathon,最终成功!

# which marathon

/usr/bin/marathon

# nohup marathon --master zk://192.168.8.131:2181,192.168.8.132:2181,192.168.8.133:2181/mesos --zk zk://192.168.8.131:2181,192.168.8.132:2181,192.168.8.133:2181/marathon & >>nohup.out

# ss -tunlp|grep 8080 #8080端口为marathon的进程监听端口

tcp LISTEN 0 50 :::8080 :::* users:(("java",pid=17946,fd=179))

6、查看mesos application的信息

查看application的metric

# curl http://192.168.8.131:8080/metrics | python -m json.tool | less

查看运行的app

# curl http://192.168.8.131:8080/v2/apps | python -m json.tool

查看ID为nginx-test的app

# curl http://192.168.8.131:8080/v2/apps/nginx-test | python -m json.tool

删除ID为nginx-test的app

# curl -X DELETE http://192.168.8.131:8080/v2/apps/nginx-test | python -m json.tool

四、Slave节点部署

以slave-node1为例,slave-node2配置根据环境微调。

1、安装mesos-slave

# yum install mesos docker –y

2、mesos-slave关联zookeeper

# vi /etc/mesos/zk

zk://192.168.8.131:2181,192.168.8.132:2181,192.168.8.133:2181/mesos

3、配置marathon调用mesos运行docker

# echo "docker,mesos" >/etc/mesos-slave/containerizers

4、mesos-slave配置ip&hostname

# echo "192.168.8.134">/etc/mesos-slave/ip

# echo "192.168.8.134" >/etc/mesos-slave/hostname

5、启动服务并增加开机启动

# systemctl stop mesos-master

# systemctl disable mesos-master

# systemctl enable mesos-slave docker

# systemctl start mesos-slave docker

# systemctl status docker

报错:Unit dockergetenforce.service could not be found.

解决方法:

重启服务

# systemctl status mesos-slave

五、验证

登陆mesos web:http://192.168.8.131:5050/#/

1、mesos-homepage

1. 从首页可以看到mesos-agent:activated状态的agent有2个;

2. mesos-master管理的2个agent的资源已汇总。

2、mesos-Frameworks

1. 在mesos框架中,marathon框架已经被识别,marathon的leader是:mesos-master2;

2. 虽然mesos-master与marathon的leader相同,但两者的leader是zookeeper独立选举的,并没有直接关系,是可以不同的。

3、mesos-Agents

1.在Agnets中能看到2个agent;

2.同时能看到每个agent的资源;

点击上面2个slave中的任意一个,也能看出它的master是mesos-master2

访问marathon的管理页面,

这里的master_ip就是在上面访问mesos页面Frameworks中识别出的marathon,即192.168.8.132:8080

或者直接点击mesos访问页面Frameworks中识别出的marathon也可以。

4、mesos state

浏览器访问:

5、通过Mesos调度,使用marathon来创建容器

比如创建一个nginx镜像的Docker容器,Marathon启动时会读取/etc/mesos/zk配置文件,Marathon通过Zookeeper来找到Mesos Master。

1)Marathon有自己的REST API,我们通过API的方式来创建一个 tomcat 的Docker容器

首先创建一个json文件(这个要在master节点机器上创建,任意一台master节点机上都可以):

# docker pull nginx

# docker pull tomcat

nginx-example:

{

"id":"nginx", #容器名,只在marathon生效

"cpus":0.2, #cpu用量

"mem":32.0, #mem用量

"instances": 1, #容器数量

"constraints": [["hostname", "UNIQUE",""]], #限制

"container": {

"type":"DOCKER", #应用类型

"docker": { #docker具体配置

"image": "docker.io/nginx", #采用的image

"network": "BRIDGE", #网络模式

"portMappings": [

{"containerPort": 80, "hostPort": 0,"servicePort": 0, "protocol": "tcp" }

] #端口映射,”0”表示任意端口,"servicePort"服务端口

}

}

}

# vi tomcat.json # tomcat的docker镜像要提前创建或下载

{

"id":"tomcat-1",

"cpus":0.5,

"mem":64,

"instances": 1,

"constraints": [["hostname", "CLUSTER",""]],

"container": {

"type":"DOCKER",

"docker": {

"image": "docker.io/tomcat",

"network": "BRIDGE",

"portMappings": [

{"containerPort": 8080, "hostPort": 0,"servicePort": 0, "protocol": "tcp" }

]

}

}

}

注意:json文件中的每一行内容的开头和结尾一定不能有空格,但可以有空行,否则手动通过curl的方式创建容器实例,会有各种各样的报错!!!

接着使用curl的方式调用,注意上面的tomcat.json文件是在/root路径下的(注意下面命令中json文件路径)。

# curl -X POST http://192.168.8.131:8080/v2/apps -d @/root/tomcat.json -H "Content-type: application/json"

{"id":"/tomcat-1","backoffFactor":1.15,"backoffSeconds":1,"constraints":[["hostname","CLUSTER",""]],"container":{"type":"DOCKER","docker":{"forcePullImage":false,"image":"docker.io/tomcat","parameters":[],"privileged":false},"volumes":[],"portMappings":[{"containerPort":8080,"hostPort":0,"labels":{},"protocol":"tcp","servicePort":0}]},"cpus":0.5,"disk":0,"executor":"","instances":1,"labels":{},"maxLaunchDelaySeconds":3600,"mem":64,"gpus":0,"networks":[{"mode":"container/bridge"}],"requirePorts":false,"upgradeStrategy":{"maximumOverCapacity":1,"minimumHealthCapacity":1},"version":"2018-05-20T09:20:37.589Z","killSelection":"YOUNGEST_FIRST","unreachableStrategy":{"inactiveAfterSeconds":0,"expungeAfterSeconds":0},"tasksStaged":0,"tasksRunning":0,"tasksHealthy":0,"tasksUnhealthy":0,"deployments":[{"id":"8035fe3f-44a7-4c8d-9f8f-22ecf66ea24f"}],"tasks":[]}

也可以将上面的tomcat.json文件内容直接复制到marathon创建应用容器的"JSON Mode"模式里,然后直接点击创建。

登陆marathon界面查看是在哪一台slave机器上创建的docker容器实例(这个是随机的),点击"running"。(如果容器创建失败,即非"running"状态,可以尝试重启slave节点的docker服务)

然后登陆mesos-slave1机器,发现在服务器上,这个容器只是被关闭了(docker ps -a),可以选择删除。如果再次在机器上启动这个nginx容器,那么在marathon上是不会显示的。

注意:在节点机器上手动创建的docker容器,这些容器信息是不会在marathon和mesos里展示的。

在marathon界面里"Destory"删除对应的application后,在mesos界面的"Active Tasks"里的对应任务也会删除。

另外要注意:在marathon界面里通过调用mesos创建docker容器,只能创建应用容器(Application),如nginx、tomcat、mysql、redis等,需要映射端口,这里是映射的是宿主机的随机端口。不能创建基本centos,ubuntu的系统容器!

2)nginx实例扩容

点击marathon首页正在运行的nginx实例最后面的"…",选择"Scale",在弹出来的实例扩容对话框中,输入所要扩容的实例个数,确定后进行扩容。很快会看到原来正在运行的nginx单实例由一个变为多个。Marathon的实例扩容同时也有相应的API,也可以在bash下通过api进行扩容。

访问所创建的nginx容器。(marathon ui界面里创建的docker容器映射到宿主机的访问端口默认都是随机分配的(BRIDGE模式))、可以自己制作应用的docker镜像,或者自定义构建容器并提交为新镜像(自己设定应用容器结构),然后根据自己制作的镜像在Marathon上创建应用。

如以上截图中可知,这个nginx容器是在mesos-slave1节点机(192.168.8.134)上创建的

注意:如果mesos-slave1节点机宕机或docker服务重启,那么这个nginx容器就会自动漂移到其他的slave节点机上;另外,通过上面方式创建好的容器,在单个slave节点机上删除后,容器也会自动转移到其他slave节点机器上,这样就实现了在slave节点出现故障时容器自动转移的高可用功能。

可以登陆mesos-slave1机器查看所创建的容器,如上可知:访问Docker随机启动的端口是31725。

接着访问mesos页面,可以看到"Active Tasks"有刚才创建的nginx任务了。(注意:只有当mesos访问界面"Active Tasks"里有容器创建任务时,才说明此容器真正创建成功了)

3)图形化创建并运行容器

然后填写创建容器的配置信息,如下图,可以点击"New Application"创建页面右上角的"JSON Mode"模式,将上面创建nginx容器的json文件复制到这里。

剩下以上红框中的配置选项暂时先不配置,待以后测试。

4)删除marathon创建的docker实例。如下图,点击"Destory"即可删除

Marathon还可以对App应用实现手动扩缩的功能,选择"Scale Application"进行快速扩容。如下图,对上面已创建的tomcat应用容器进行扩展到2个Task(注意:这里有2个slave节点,那么扩展的Task实例最好是2个,即2个Instances;如果扩展多个Task,会发现多余的创建失败,这时候可以点击"Configuration"修改,修改成2个)。

点击下面的日志"stderr"和"stdout"就会下载到本地。

也可以到mesos页面查看或下载。点击下面mesos页面对应容器任务后面的"Sandbox"

具体配置可以参考marathon官方文档:里面有关于json文件的配置

6、marathon创建应用使用volumes

在marathon界面里创建应用,可以使用volumes,即映射容器目录到宿主机上,JSON文件内容如下:

{

"id": "nginx-1",

"cmd": null,

"cpus": 0.5,

"mem": 32,

"disk": 2048,

"instances": 1,

"container": {

"docker": {

"image": "docker.io/nginx",

"network": "BRIDGE",

"portMappings": [

{

"containerPort": 80,

"protocol": "tcp",

"name": null,

"labels": null

}

]

},

"type": "DOCKER",

"volumes": [

{

"containerPath": "/usr/share/nginx/html",

"hostPath": "/opt/web/www",

"mode": "RW"

}

]

}

}

# mkdir -p /opt/web/www //如上图在192.168.8.135(mesos-slave2)节点上创建使用的卷目录(volumes,即映射的宿主机目录)

# echo "test" > /opt/web/www/index.html //映射的宿主机目录下创建测试文件

注意事项:

 

1)映射到宿主机的目录/opt/web/www要在每个slave节点机器上都要创建,并且里面的文件要在每个slave节点机上都有,因为容器重启后会在各个slave节点之间随机漂移。

2)上面映射的是nginx容器应用的站点目录,默认创建后,/usr/share/nginx/html是空的(/opt/web/www目录也是空的),所以容器默认访问会出现403报错。只需在slave节点的/opt/web/www目录下编写html文件(如index.html)即可访问。

3)marathon里创建好的容器应用,你可以在对应的slave机器上登陆容器内修改,但是这个容器应用一旦在marathon界面里restart,那么你之前的修改就没有了。因为重启应用,就是再次使用初始镜像进行构建了。

4)可以自己制作应用镜像,在镜像里设定好应用的配置文件;或者将自己创建的容器提交为新的镜像。然后在marathon界面里根据自己定义的镜像创建应用。

7、marathon创建应用指定访问端口

默认情况下,marathon创建的应用访问端口是随机分配的,因为hostPort默认配置的是0,具体看下面说明:

marathon创建应用后,会涉及到三个端口:containerPort:、hostPort、servicePort,其中:

1)containerPort:用来指定容器内部的一个端口。当使用BRIDGE或USER网络模式连接Docker容器时,必须将这个属性设置为port mapping的一部分。

2)hostPort:用来指定绑定到主机上的一个端口。当使用BRIDGE或USER网络模式,你可以指定一个port mapping将一个主机端口映射到容器端口。在HOST网络模式下,默认的请求端口就是主机的端口。

请注意,主机端口只可以通过环境变量提供给一个任务。

3)servicePort:当您在Marathon上(不管是通过REST API或界面)创建一个新的应用程序,你可以指定一个或多个服务端口给它。

可以指定所有有效的端口号为服务端口,也可以用0表示Marathon应该自动分配的可用服务端口给应用程序使用。如果你选择自己的服务端口,你必须自己确保,这个端口在所有应用程序中是唯一的。

portMapping:在Docker BRIDGE模式下,在容器外部可被访问到的端口都需要做端口映射。端口映射是一个包含host port, container port, service port和协议的元组。可以为Marathon应用指定多个端口映射; 未指定hostPort,则其默认值为0(意味着Marathon将随机分配一个)。在Docker USER模式下,hostPort的语义为稍有点变化:USER模式不需要指定hostPort,如果未指定Marathon不会自动分配一个随机的。这允许在USER网络模式下部署容器,包括containerPort和发现信息,但不暴露主机网络上的这些端口(意味着将不消耗主机端口资源)。

marathon创建应用的网络模式介绍:

1)BRIDGE网络模式:指定Docker应用程序使用BRIDGE网络模式。在这种模式下,容器端口(容器内部的端口)被映射到主机端口(宿主机上的端口)。在这种模式下,应用程序被绑定到容器内的指定端口,容器的端口被绑定到宿主机上的指定端口。

2)USER网络模式:指定Docker应用程序使用USER网络模式。在这种模式下,容器端口(容器内部的端口)被映射到主机端口(宿主机上的端口)。在这种模式下,应用程序被绑定到容器内的指定端口,容器的端口被绑定到宿主机上的指定端口。在与“用户自定义”Docker网络集成时,USER网络模式将会非常有用。在Mesos世界,这种网络通常是通过使用与Mesos CNI网络隔离的 CNI 插件访问。

3)HOST网络模式:该种模式在Marathon应用为非容器化而其它应用为容器化的情况下使用。在这种模式下,应用程序直接绑定到宿主机上的一个或多个端口。

如下JSON文件内容,注意一下:如果hostport端口指定了,那么serviceport端口也要指定(最好使用大端口),否则会导致应用容器创建失败。

{

"id":"nginx-2",

"cpus":0.2,

"mem":32.0,

"instances": 1,

"constraints": [["hostname", "CLUSTER",""]],

"container": {

"type":"DOCKER",

"docker": {

"image": "docker.io/nginx",

"network": "BRIDGE",

"portMappings": [

{"containerPort": 80, "hostPort": 31030,"servicePort": 33180, "protocol": "tcp" }

]

}

}

}

在marathon界面里创建应用,将上面的JSON文件内容复制到"JSON Mode"模式下。构建成功后,就会发现该应用容器的访问端口就是上面自己定义的31030端口了(如下)

8、其他

在marathon中,应用是一个完整的概念。每个应用是一个典型的长运行的服务,这个服务有很多实例,并且是运行在多个slave节点机上。下面通过一个小示例说明下:

一个内嵌的shell脚步:

如下通过内嵌的shell脚步,编写一个简单的app,即:
打印Hello world到slave节点的/mnt/test文件中,然后sleep 5秒,周而复始。可以使用下面的应用定义文件(json格式)来描述应用(注意:cmd是要执行的命令。它的值会以/bin/sh -c ${cmd}的方式执行。):

 

{

    "id": "basic-0",

    "cmd": "while [ true ] ; do echo 'Hello world' >> /mnt/test ; sleep 5 ; done",

    "cpus": 0.1,

    "mem": 10.0,

    "instances": 1

}

在marathon界面里添加应用,采用"JSON Mode "模式,如下:

不采用"JSON Mode"模式,即将上面的json文件内容粘贴进去后,去掉右上方的"JSON Mode"模式,也就是只配置"General"选向,其他选项都不配置。注意:marathon里的应用是一个长运行服务,所以shell脚本里要配置长运行动作。

然后到192.168.8.135这台slave节点机上检查,发现每隔5秒钟,就会输出"hello world"到/mnt/test文件中。如果这台节点机出现故障,就会输出到其他节点机上。

# tail -f /mnt/test

Hello world

Hello world

Hello world

Hello world

***************当你发现自己的才华撑不起野心时,就请安静下来学习吧!**************

六、分布式作业调度chronos

下图是chronos任务调度系统的基本结构图:

整体上来看,该系统由业务队列、业务调度器、Chronos和Mesos组成。每个组成部分的说明如下:

  • 业务队列:实际存放任务的队列,队列中的任务可以是按照优先级排序,也可以是FIFO. 多个业务队列表示不同的业务,或者同一业务的不同队列。

  • 业务调度器:根据一定的调度算法从多个业务队列中选择任务来调度。业务调度器接受Chronos的资源汇报,然后给这些资源分配任务。

  • Chronos:向业务调度器汇报剩余资源,接受业务调度器提交的任务然后在Mesos集群上面运行。

  • Mesos:运行各个Docker容器的载体。每个任务都在Docker容器中执行。

有一点需要解释的是,在这个任务调度系统中,存在多个调度器:

  • Mesos中的调度器:一种通用的集群资源分配算法,也就是DRF

  • Chronos中的调度器:在我们这个场景中,不需要定时调度任务。我们只需使用Chronos的Rest API提交一个Docker任务就可以了,这个Docker任务然后由Chronos调度到Mesos集群上面运行。为什么不绕过Chronos直接提交任务到Mesos呢?Mesos本身是个两级调度架构,如果绕过Chronos,那么业务调度器就需要实现Mesos的上级调度接口,增加了复杂性。

  • 业务调度器:这就是和业务联系最紧密的地方了,此处必须考虑各种业务特性,比如各个业务队列的任务总量、到达速率、优先级等等。

系统的核心在于业务调度器。经过调研各种调度算法,觉得YARN资源管理系统中的容量调度(Capacity Scheduler)适合多业务多队列的场景。

以两级队列为例来说明容量调度算法。下图是Chronos资源的容量配置示例:

在该示意图中,Chronos资源容量的20%分配给了业务1,80%分配给了业务2. 接着,20%容量中的40%分配给了业务队列11,60%分配给了业务队列12. 业务1和业务2是业务层次的队列,业务队列11、业务队列12和业务队列21是具体业务下的子队列。

1、master节点上安装配置chronos

拉取docker官方chronos镜像

# docker pull mesosphere/chronos

# docker images

REPOSITORY TAG IMAGE ID CREATED SIZE

docker.io/mesosphere/chronos latest ec8accd8eb53 15 months ago 511 MB

# docker run -itd --net=host -e PORT0=18080 -e PORT1=8081 docker.io/mesosphere/chronos:latest --zk_hosts zk://192.168.8.131:2181,192.168.8.132:2181,192.168.8.133:2181 --master zk://192.168.8.131:2181,192.168.8.132:2181,192.168.8.133:2181/mesos

# docker ps -a

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

a6f9ecd25d38 docker.io/mesosphere/chronos:latest "/chronos/bin/star..." 2 minutes ago Up 2 minutes gallant_wescoff

2、登录chronos的web管理页面

3、在Chronos页面,点击“ADD JOB”创建任务

注意:时间是UTC时间

Name: 作业名称

Command: 实际需要执行的命令

Schedule: 作业的调度规则,遵循ISO8601规范。由三部分组成,通过”/“符号分割。例如”R10/2012-10-01T05:52:00Z/PT2S”的三部分内容如下:

1> 重复执行作业务次数,如果只有一个R,则代表无限次执行。

2> 开始启动作业的时间,如果为空,则表示立即执行,其遵循ISO8601规范。

3> 执行频率(即间隔多长时间执行一次),其定义方式如下:

P10M=10 months

PT10M=10 minutes

P1Y12M12D=1 years plus 12 months plus 12 days

P12DT12M=12 days plus 12 minutes

P1Y2M3DT4H5M6S=P(eriod) 1Y(ear) 2M(onth) 3D(ay) T(ime) 4H(our) 5M(inute) 6S(econd)

其中,P是必选字段,T是可选字段,用来区分M(inute)和M(onth)。

ScheduleTimeZone: 用来设置作业调度的时区。

Epsilon: 指因某些原因Chronos丢失了运行作业的下一次时间时,采用的固定运行周期。

Owner: 作业责任人的邮件地址。

Async: 作业是否在后台运行。

chronos任务如果正常调度并执行的话,在mesos的web页面上的“Active Tasks”下会有任务执行相关的信息,如下图:

也可以在Chronos的任务页面,点击「Run」强制执行。

4、测试通过Chronos执行Docker任务

启动Docker,抓取nginx镜像,启动chronos-nginx容器

A、创建json文件

# vi chronos-test.json

{

"container": {

"type": "DOCKER",

"image": "docker.io/nginx",

"network": "HOST"

},

"schedule": "R/2018-05-30T14:00:00.000Z/PT24H",

"name": "chronos-nginx",

"cpus": "0.3",

"mem": "32",

"uris": [],

"command": "/usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf"

}

network": "BRIDGE",因为Docker默认的网络模式是桥接,不指定默认也是BRIDGE,此处指定为“HOST”。

B、通过BASH调用chronos的RestfulAPI调度接口运行并启动chronos-nginx容器

# curl -L -H 'Content-Type: application/json' -X POST -d @/root/chronos-test.json

在chronos的web管理界面上查看chronos-nginx容器运行的状态如下图:

在Mesos页面确认任务的详细信息,如下图:

在节点上确认容器启动,如下图:

# docker ps -a

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

db3165da2284 docker.io/nginx "/bin/sh -c '/usr/..." 9 minutes ago Up 9 minutes 80/tcp mesos-97cce9bb-a884-454e-aed7-dea10d68737e

5、确认nginx网页可以访问

七、服务自动注册与发现consul

consul的架构图如下所示:

启动consul后默认会监听5个端口:

8300: replication、leader farwarding的端口
8301: lan gossip的端口
8302: wan gossip的端口
8500: web ui界面的端口
8600: 使用dns协议查看节点信息的端口

集群角色:

上图是官方的consul cluster的架构,Consul Cluster有Server和Client两种角色。不管是Server还是Client,统称为Agent,Consul Client是相对无状态的,只负责转发RPC到Server,资源开销很少。Server是一个有一组扩展功能的代理,这些功能包括参与Raft选举,维护集群状态,响应RPC查询,与其他数据中心交互WAN gossip和转发查询给leader或者远程数据中心。

在每个数据中心,client和server是混合的。一般建议有3-5台server。这是基于有故障情况下的可用性和性能之间的权衡结果,因为越多的机器加入达成共识越慢,Server之间会选举出一个leader。然而,并不限制client的数量,它们可以很容易的扩展到数千或者数万台。

consul更像一个“全栈”解决方案,内置了服务注册与发现,具有健康检查、Key/Value存储、多数据中心的功能。个人比较偏爱他有三点:

1、开箱即用,方便运维:安装包仅包含一个可执行文件,方便部署,无需其他依赖,与Docker等轻量级容器可无缝配合;

2、自带ui界面,可以通过web界面直接看到注册的服务,更新K/V;

3、采用GOSSIP协议进行集群内成员的管理和消息的传播,使用和etcd一样的raft协议保证数据的一致性。

Consul提供的四个关键特性:

1、服务发现:提供HTTP和DNS两种发现方式。

2、健康监测: 支持多种方式,HTTP、TCP、Docker、Shell脚本定制化监控。

3、K/V存储: Key、Value的存储方式。

4、多数据中心:Consul支持多数据中心。

当然Consul还有很多锦上添花的特性,比如:可视化Web界面配置模板“consul-template”等。

通过registrator的服务自动注册,配合consul服务,在docker容器中能够自动识别服务的域名,以便在mesos+marathon中部署docker实例能够通过域名互通,也就是consul在docker容器中的自动发现。如果是线上consul server最少3台做集群,consul client是每个mesos-slave上跑一个,mesos-master也最少3台集群,marathon和zookeeper都应该是集群的模式。

consul经常拿来和etcd、zookeeper来对比,他有和etcd一样的服务发现、服务注册功能,也有和etcd、zookeeper一样的配置存储功能,详细的比对这里就不描述了。下面是consul集群的服务注册,服务发现,健康检查,和模板使用。

registrator服务注册         | | consul服务端                     | |nginx代理                  | |应用服务

做健康检查                      | |接收客户端发送的健康         | | 如果nginx的模板       | |

确定本地服务是否正常     | |检查信息做服务的注册         | |更改,则                    | |

通报给consul服务端        | |如果客户端有变动,则         | |nginx reload              | |

                                       | |更新代理的配置                   | |                                  | |

1、consul集群服务安装配置

# unzip consul_1.1.0_linux_amd64.zip -d /usr/local

# ln -s /usr/local/consul /usr/local/bin/consul

# mkdir -p /etc/consul.d //在每个consul节点上配置

command方式创建consul集群:

# consul agent -server -bootstrap-expect=3 -data-dir=/tmp/consul -node=192.168.8.131 -config-dir=/etc/consul.d -client 0.0.0.0

==> Multiple private IPv4 addresses found. Please configure one with 'bind' and/or 'advertise'.

报错:Multiple private IPv4 addresses found. Please configure one with 'bind' and/or 'advertise'.

意思是:找到多个私有IPv4地址。请用“bind”绑定配置一个,就可以解决,根据提示.

[root@mesos-master1 ~]# nohup consul agent -server -bootstrap-expect=3 -data-dir=/tmp/consul -node=mesos-master1 -config-dir=/etc/consul.d -bind=192.168.8.131 -client 0.0.0.0 -ui &

bootstrap_expect > 0: expecting 3 servers

==> Starting Consul agent...

==> Consul agent running!

Version: 'v1.1.0'

Node ID: 'cc8eebd0-6f84-6eee-f8cc-92ae7bf44494'

Node name: 'mesos-master1'

。。。。。。

2018/06/03 15:00:21 [INFO] agent: Started DNS server 0.0.0.0:8600 (tcp)

2018/06/03 15:00:21 [INFO] agent: Started HTTP server on [::]:8500 (tcp)

2018/06/03 15:00:21 [INFO] agent: started state syncer

2018/06/03 15:00:28 [ERR] agent: failed to sync remote state: No cluster leader

2018/06/03 15:00:30 [WARN] raft: no known peers, aborting election

2018/06/03 15:00:51 [ERR] agent: Coordinate update error: No cluster leader

2018/06/03 15:00:54 [ERR] agent: failed to sync remote state: No cluster leader

报错:No cluster leader,是因为其它两台consul的server端还没有启动!

启动参数说明:

agent: 运行一个consul代理。
-server: 切换代理到服务器模式。
-bootstrap: 当consulserver-node1节点启动之后,等待另外两个节点的加入,3个节点聚齐后,之后才开始选举leader。
-ui: 启用内置的静态web UI服务器。
-data-dir: 路径到数据目录存储代理状态。
-bind: 设置集群通信的绑定地址。
-client: 设置用于绑定客户端访问的地址。这包括RPC、DNS、HTTP和HTTPS(如果配置)。
-node: 此节点的名称。 在集群中必须是唯一的,如果你运行第2台consul,可以写server02、server03等。

-advertise:如果要让节点在WAN网络中被发现,就要配置这个参数,暴露出外网ip

[root@mesos-master2 ~]# nohup consul agent -server -bootstrap-expect=3 -data-dir=/tmp/consul -node=mesos-master2 -config-dir=/etc/consul.d -bind=192.168.8.132 -client 0.0.0.0 -join 192.168.8.131 & //注意:此处的-join启动选项,后面跟mesos-master1IP地址,或者也可以在mesos-master1上执行consul join 192.168.8.132/133,否则同样还是会报以上错误!

[root@mesos-master3 ~]# nohup consul agent -server -bootstrap-expect=3 -data-dir=/tmp/consul -node=mesos-master3 -config-dir=/etc/consul.d -bind=192.168.8.133 -client 0.0.0.0 -join 192.168.8.131 &

很×××台mesos-master上的consul都会打印:

consul: New leader elected: mesos-master1

证明此时leader已经选出,集群可以正常工作。

[root@mesos-slave1 ~]# nohup consul agent -client 192.168.8.134 -data-dir=/tmp/consul -config-dir=/etc/consul.d -node=mesos-slave1 -bind=192.168.8.134 -join 192.168.8.131 &

[root@mesos-slave2 ~]# nohup consul agent -client 192.168.8.135 -data-dir=/tmp/consul -config-dir=/etc/consul.d -node=mesos-slave2 -bind=192.168.8.135 -join 192.168.8.131 &

[root@mesos-master1 ~]# consul info consul

集群参数put/get测试:

[root@mesos-slave1 ~]# consul kv put key value

Success! Data written to: key

[root@mesos-master2 ~]# consul kv get key

value

5台机器获取key的值均为value,如此可知key的值已经在集群中同步。

[root@mesos-master1 ~]# consul members

Node Address Status Type Build Protocol DC Segment

mesos-master1 192.168.8.131:8301 alive server 1.1.0 2 dc <all>

mesos-master2 192.168.8.132:8301 alive server 1.1.0 2 dc <all>

mesos-master3 192.168.8.133:8301 alive server 1.1.0 2 dc <all>

mesos-slave1 192.168.8.134:8301 alive client 1.1.0 2 dc <default>

mesos-slave2 192.168.8.135:8301 alive client 1.1.0 2 dc <default>

[root@mesos-master1 ~]# consul operator raft list-peers

Node ID Address State Voter RaftProtocol

mesos-master1 f0522384-3554-61e7-57ce-607a583a179f 192.168.8.131:8300 leader true 3

mesos-master2 b5ffff40-f993-0f9e-7877-4e0bffdabb3d 192.168.8.132:8300 follower true 3

mesos-master3 fc42ff3f-617f-9524-090b-b8c5584b3cac 192.168.8.133:8300 follower true 3

可以看出集群中mesos-master1是leader,mesos-master2和mesos-master3都是follower

[root@mesos-master1 ~]# consul info |grep leader

leader = false

leader_addr = 192.168.8.132:8300

[root@mesos-master1 ~]# consul catalog services

consul

mesos

配置文件方式创建consul集群:

在其中一个mesos master节点上配置bootstrap配置,该节点就是consul集群中的bootstrap节点(192.168.8.131)。

# cat > /etc/consul.d/bootstrap/config.json <<EOF

{

"bootstrap": true,

"server": true,

"datacenter": "dc",

"data_dir": "/var/consul",

"log_level": "INFO",

"enable_syslog": true,

"disable_update_check": true,

"ui": true

}

EOF

在其他的mesos master 节点上配置server配置,这些节点就是consul集群中的server节点(192.168.8.132/192.168.8.133)。

# cat > /etc/consul.d/server/config.json <<EOF

{

"bootstrap": false,

"server": true,

"datacenter": "dc",

"data_dir": "/var/consul",

"log_level": "INFO",

"enable_syslog": true,

"disable_update_check": true,

"start_join": ["192.168.8.131", "192.168.8.133"],

"ui": true

}

EOF

注意: start_join: 记录了其他consul server节点的IP。

在所有的mesos agent 节点上配置client配置,这些节点也就是consul集群中的client节点

# cat > /etc/consul.d/client/config.json <<EOF

{

"server": false,

"datacenter": "dc",

"data_dir": "/var/consul",

"log_level": "INFO",

"enable_syslog": true,

"disable_update_check": true,

"start_join": ["192.168.8.131", "192.168.8.132", "192.168.8.133"],

"ui": true

}

EOF

注意:start_join: 记录了所有的consul server的IP。

启动Consul集群
在consul bootstrap节点启动:

# consul agent -config-dir /etc/consul.d/bootstrap/ -bind=192.168.8.131 -client=0.0.0.0 >> /var/log/consul.log 2>&1 &

在所有consul server节点启动:

# consul agent -config-dir /etc/consul.d/server/ -bind=192.168.8.132 -client=0.0.0.0 >> /var/log/consul.log 2>&1 &

在所有consul client节点启动:

consul agent -config-dir /etc/consul.d/client/ -bind=192.168.8.134 -client=0.0.0.0 >> /var/log/consul.log 2>&1 &

注意: self_ip是本机的IP。

检查consul集群状态:
在consul集群中任意一个节点执行

2、consul的DNS服务发现

consul支持dns的服务注册:

# dig @127.0.0.1 8600 mesos-master1.node.consul +short
192.168.8.131
# dig @127.0.0.1 8600 mesos-master2.node.consul +short
192.168.8.132
# dig @127.0.0.1 8600 mesos-master3.node.consul +short
192.168.8.133
3、consul的常用http api

对consul来说一个重要的接口就是RESTful HTTP API,http api可用于操作nodes, services, checks, configuration等等的CRUD(create, read, update and delete)详见,下面是几个例子?说明

http api可以通过链接请求:

查看当前consul节点的服务

# curl mesos-master1:8500/v1/agent/checks |python -m json.tool

查看当前consul集群的leader

# curl mesos-master1:8500/v1/status/leader

查看当前节点的信息

# curl mesos-master1:8500/v1/operator/raft/configuration |python -m json.tool

查看mesos-slave1节点的健康检查信息

# curl mesos-master1:8500/v1/health/node/mesos-slave1 |python -m json.tool

查看webserver服务的信息

# curl -s http://mesos-master1:8500/v1/catalog/service/webserver | python -m json.tool

# 集群server成员

#curl 127.0.0.1:8500/v1/status/peers

# 集群Raft leader

#curl 127.0.0.1:8500/v1/status/leader

# 注册的所有服务

#curl 127.0.0.1:8500/v1/catalog/services

# 服务信息

#curl 127.0.0.1:8500/v1/catalog/services/nginx

# 集群节点详细信息

#curl 127.0.0.1:8500/v1/catalog/nodes

4、registrator容器实现服务自动注册

服务发现与注册

1. 具体流程

服务注册中心:作为整个架构中的核心,要支持分布式持久化存储注册信息变动实时通知消费者。

服务提供者:服务以容器化方式部署(实现服务端口动态生成),可以通过 的方式来管理。通过 检测到 进程信息以完成服务的自动注册

服务消费者:要使用服务提供者提供的服务,和服务提供者往往是动态相互转位置的。

一个较为完整的服务注册与发现流程如下:

注册服务:服务提供者到注册中心注册

订阅服务:服务消费者到注册中心订阅服务信息,对其进行监听

缓存服务列表:本地缓存服务列表,减少与注册中心的网络通信;

调用服务:查找本地缓存,找不到再去注册中心拉取服务地址,然后发送服务请求;

变更通知:服务节点变动时 (新增删除等),注册中心将通知监听节点,更新服务信息。

2. 相关组件

一个服务发现系统主要由三部分组成:

注册器(registrator):根据服务运行状态,注册/注销服务。主要要解决的问题是,何时发起注册/注销动作。

注册表(registry):存储服务信息。常见的解决方案有zookeeper、etcd、cousul等。

发现机制(discovery):从注册表读取服务信息,给用户封装访问接口。

通过Registrator收集需要注册到Consul作为Nginx后端服务器信息然后注册到Consul key/value.Consul-template去Consul key/value中读取信息,然后自动修改Nginx配置文件并平滑重启Nginx,不需要修改nginx.conf。

分别在mesos-slave1 和mesos-slave2 上都创建:

利用chronos任务调度下载registrator

利用chronos任务调度在所有的mesos-slave上运行registrator

Command: docker run -d --name registrator --network=host -v /var/run/docker.sock:/tmp/docker.sock --restart=always gliderlabs/registrator:latest --ip 192.168.8.134 consul://192.168.8.131:8500

参数说明:

--network:把运行的docker容器设定为host网络模式;
-v /var/run/docker.sock:把宿主机的Docker守护进程(Docker daemon)默认监听的Unix域套接字挂载到容器中;
--ip : 刚才把network指定了host模式,所以我们指定下IP为宿主机的IP;
consul: 最后这个选项是配置consul服务器的IP和端口。

服务注册前:

通过marathon启动tomcat服务:

[root@mesos-master1 ~]# curl -X POST http://192.168.8.131:8080/v2/apps -d @/root/tomcat.json -H "Content-type: application/json"

{"id":"/tomcat-1","backoffFactor":1.15,"backoffSeconds":1,"constraints":[["hostname","CLUSTER",""]],"container":{"type":"DOCKER","docker":{"forcePullImage":false,"image":"docker.io/tomcat","parameters":[],"privileged":false},"volumes":[],"portMappings":[{"containerPort":8080,"hostPort":0,"labels":{},"protocol":"tcp","servicePort":0}]},"cpus":0.5,"disk":0,"executor":"","instances":1,"labels":{},"maxLaunchDelaySeconds":3600,"mem":64,"gpus":0,"networks":[{"mode":"container/bridge"}],"requirePorts":false,"upgradeStrategy":{"maximumOverCapacity":1,"minimumHealthCapacity":1},"version":"2018-06-10T08:34:47.600Z","killSelection":"YOUNGEST_FIRST","unreachableStrategy":{"inactiveAfterSeconds":0,"expungeAfterSeconds":0},"tasksStaged":0,"tasksRunning":0,"tasksHealthy":0,"tasksUnhealthy":0,"deployments":[{"id":"346444ac-978b-42d6-8975-6b01fd6869ab"}],"tasks":[]}

服务注册后:

从上图可以看出,刚才创建的marathon应用tomcat容器服务已经注册到了 consul中。

[root@mesos-master1 ~]# curl 127.0.0.1:8500/v1/catalog/services

{"consul":[],"mesos":["master","leader"],"tomcat":[]}

八、负载均衡

服务被调整后,负载均衡要想动态重新分配负载,就需要修改相应的配置文件,consul-template就是解决这个问题的应用,通过监听consul的注册信息,来自动完成负载均衡相应的配置更新。

安装nginx,此处略过。。。

安装配置consul-template

安装consul-template非常简单,下载二进制包即可使用。

1、下载consul-template

下载地址:

# wget https://releases.hashicorp.com/consul-template/0.19.4/consul-template_0.19.4_linux_amd64.zip

2、解压并安装到/usr/bin目录

# unzip consul-template_0.19.4_linux_amd64.zip

# mv consul-template /usr/bin/

# consul-template -v

consul-template v0.19.4 (68b1da2)

3、创建nginx模板

# cd /usr/local/nginx

# mkdir consul

# cd consul/

# vi nginx.ctmpl

upstream http_backend {

{

{range service "nginx"}}

server {

{ .Address }}:{
{ .Port }} max_fails=3 fail_timeout=90;

{

{ end }}

}

server {

listen 8085;

server_name localhost;

location / {

proxy_pass http://http_backend;

}

}

4、修改nginx.conf

# grep "consul" nginx.conf

include /usr/local/nginx/consul/*.conf; //添加这一行在http模块

# /usr/local/nginx/sbin/nginx //启动nginx服务

5、启动consul-template

# ln -s /usr/local/nginx/sbin/nginx /usr/bin/nginx

# cd /usr/local/nginx/consul/

# consul-template --consul-addr 192.168.8.131:8500 --template /usr/local/nginx/consul/nginx.ctmpl:/usr/local/nginx/consul/vhost.conf:"nginx -s reload" --log-level=info
2018/06/18 13:15:45.719503 [INFO] consul-template v0.19.4 (68b1da2)
2018/06/18 13:15:45.720675 [INFO] (runner) creating new runner (dry: false, once: false)
2018/06/18 13:15:45.721967 [INFO] (runner) creating watcher
2018/06/18 13:15:45.723542 [INFO] (runner) starting
2018/06/18 13:15:45.724051 [INFO] (runner) initiating run
2018/06/18 13:15:45.728395 [INFO] (runner) initiating run
2018/06/18 13:15:45.731084 [INFO] (runner) rendered "/usr/local/nginx/consul/nginx.ctmpl" => "/usr/local/nginx/consul/vhost.conf"
2018/06/18 13:15:45.732215 [INFO] (runner) executing command "nginx -s reload" from "/usr/local/nginx/consul/nginx.ctmpl" => "/usr/local/nginx/consul/vhost.conf"
2018/06/18 13:15:45.733669 [INFO] (child) spawning: nginx -s reload
 
--consul-addr:指定consul服务的ip和端口;./nginx.ctmpl:这是用nginx.ctmpl这个模板来启动进程,这是写的相对路径,也可以写绝对路径;vhost.conf:nginx.ctmpl模板生成后的文件名,这也可以写绝对路径,如果不写绝对路径,这个文件就在当前目录生成(/usr/local/nginx/consul/)
 
由于consul-template在前台运行,所以我们在打开一个终端验证。

[root@mesos-master1 ~]# ps -ef | grep consul-template

root 41543 16705 0 19:33 pts/2 00:00:00 consul-template --consul-addr 192.168.8.131:8500 --template ./nginx.ctmpl:vhost.conf --log-level=info

root 41758 38089 0 19:35 pts/1 00:00:00 grep --color=auto consul-template

[root@mesos-master1 ~]# ll /usr/local/nginx/consul/

total 8

-rw-r--r-- 1 root root 333 Jun 10 18:26 nginx.ctmpl

-rw-r--r-- 1 root root 255 Jun 10 19:33 vhost.conf

在consul目录下,是不是发现多了一个文件vhost.conf,就是刚才启consul-template时生成的。查看下vhost.conf的内容,目前upstraem的server配置为空,还没有docker主机加入进来:

[root@mesos-master1 ~]# cat /usr/local/nginx/consul/vhost.conf

upstream http_backend {

server {

{ .Address }}:{
{ .Port }} max_fails=3 fail_timeout=90;

}

server {

listen 8085;

server_name localhost;

location / {

proxy_pass http://http_backend;

}

}

通过marathon创建nginx容器实例:

[root@mesos-master1 ~]# curl -X POST http://192.168.8.132:8080/v2/apps -d @/root/nginx.json -H "Content-type: application/json"

{"id":"/nginx-1","backoffFactor":1.15,"backoffSeconds":1,"container":{"type":"DOCKER","docker":{"forcePullImage":false,"image":"docker.io/nginx","parameters":[],"privileged":false},"volumes":[{"containerPath":"/usr/share/nginx/html","hostPath":"/opt/web/www","mode":"RW"}],"portMappings":[{"containerPort":80,"hostPort":0,"labels":{},"protocol":"tcp","servicePort":0}]},"cpus":0.5,"disk":2048,"executor":"","instances":1,"labels":{},"maxLaunchDelaySeconds":3600,"mem":32,"gpus":0,"networks":[{"mode":"container/bridge"}],"requirePorts":false,"upgradeStrategy":{"maximumOverCapacity":1,"minimumHealthCapacity":1},"version":"2018-06-18T13:12:27.037Z","killSelection":"YOUNGEST_FIRST","unreachableStrategy":{"inactiveAfterSeconds":0,"expungeAfterSeconds":0},"tasksStaged":0,"tasksRunning":0,"tasksHealthy":0,"tasksUnhealthy":0,"deployments":[{"id":"0b2969ac-a65d-4ff9-b7b9-f2c5cc29312f"}],"tasks":[]}

[root@mesos-master1 ~]# cat /usr/local/nginx/consul/vhost.conf

upstream http_backend {

server 192.168.8.134:31581 max_fails=3 fail_timeout=90;

server 192.168.8.134:31818 max_fails=3 fail_timeout=90;

server 192.168.8.135:31814 max_fails=3 fail_timeout=90;

}

server {

listen 8085;

server_name localhost;

location / {

proxy_pass http://http_backend;

}

}

[root@mesos-slave1 ~]# echo "mesos-slave1" >/opt/web/www/index.html

[root@mesos-slave2 ~]# echo "mesos-slave2" >/opt/web/www/index.html

可以看到,在通过marathon创建了3个nginx的容器实例后,nginx的虚拟主机配置文件已经被consul-template动态修改。然后访问一下nginx服务器的IP地址,http://192.168.8.131:8085,从下图可以看出是可以访问的:

可以看到nginx的8085负载均衡是可以正常轮询的!!!