Apache Mesos(7)-Marathon的高级应用

目录
  1. 程序的健康检查
  2. 创建应用程序组
    1. 理解应用程序组
    2. 实际部署应用程序组
    3. 部署Docker应用程序组

作者:杨冬 欢迎转载,也请保留这段声明。谢谢!
出处:https://andyyoung01.github.io/http://andyyoung01.16mb.com/

前面我们使用Marathon部署了一个简单的命令行程序和一个基于Docker容器的应用,现在我们来探索一下Marathon提供的其它功能。这些功能包括:程序的健康检查,程序组的应用等。

程序的健康检查

对于任何的Marathon应用程序,都可以实现健康检查的功能。对于一个程序的实例,有三种方法来实现:

  • HTTP-发送一个第七层的HTTP请求到特定的端口和路径。
  • TCP-尝试打开一个到特定端口的TCP socket连接。
  • COMMAND-运行任意一个命令来判断健康状态(目前与运行在容器中的任务不兼容)。

另外,您可以指定intervalSeconds,gracePeriodSeconds,timeoutSeconds,maxConsecutiveFailures等参数来配置健康状态监控。

健康检查参数选项可以参考http://mesosphere.github.io/marathon/docs/health-checks.html

我们来看一个实际的例子。在上一篇时,我们部署了一个基于docker的web服务器,我们继续使用这个配置,在原来的配置基础上加上健康检查的配置,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{
"id": "basic-3",
"cmd": "python3 -m http.server 8080",
"cpus": 0.5,
"mem": 32,
"disk": 0,
"instances": 1,
"container": {
"type": "DOCKER",
"docker": {
"image": "python:3",
"network": "BRIDGE",
"portMappings": [
{
"containerPort": 8080,
"hostPort": 0
}
]
}
},
"healthChecks": [
{
"path": "/",
"portIndex": 0,
"protocol": "HTTP",
"gracePeriodSeconds": 300,
"intervalSeconds": 60,
"timeoutSeconds": 20,
"maxConsecutiveFailures": 3,
"ignoreHttp1xx": false
}
]
}

这个例子指定了使用HTTP协议(代码清单第25行)来查询指定的路径(代码清单第23行)。关于端口的指定,使用了portIndex项(代码清单第24行)而取代重复在程序中的端口定义(代码清单第15、16行),默认情况下此项的值为0,指定的是portMappings数组的第一个值。gracePeriodSeconds(代码清单第26行)指定了在程序刚刚启动多长时间内,忽略健康检查的结果。intervalSeconds(代码清单第27行)指定两次检查的间隔时间。timeoutSeconds(代码清单第28行)指定健康检查的超时时间。maxConsecutiveFailures(代码清单第29行)指定了健康检查连续失败几次后,应该结束此不健康的程序。

通过Marathon框架部署程序,程序相关端口的配置的详细信息请参考http://mesosphere.github.io/marathon/docs/ports.html

创建应用程序组

理解应用程序组

在现实中,应用程序通常是由多个服务或Docker程序组成的。前面我们已经通过Web或JSON部署了单个的Marathon应用程序,现在我们来看看怎样通过程序组来定义应用程序及其依赖的服务。
应用程序组可以包括应用程序或者其它的程序组。而且,这些程序或程序组可以依赖组中的其它程序或程序组。
通过程序组,可以使用一个JSON文档来描述一个非常复杂的应用,这个应用内部包含各种依赖关系。通过它可以非常容易得定义依赖,部署更新,缩放单个应用程序实例或者整个程序组实例。

实际部署应用程序组

下面我们实际部署一个示例程序来看看程序组的使用。这个程序是一个基于Ruby的web程序(使用Sinatra框架),它允许用户操作Redis数据库中的键/值对。这个程序组运行了web app的三个实例和Redis数据库的一个实例,并且使用marathon-lb进行服务发现,处理服务间的通信。如下图:
“Keys and Values程序部署概览”
在实际部署前,需要配置好marathon-lb的服务发现机制(参考)。在168的机器上执行:

docker run -e PORTS=9090  -d --net=host --name internal-marathon-lb mesosphere/marathon-lb sse -m http://192.168.71.167:8080 -m http://192.168.71.168:8080 -m http://192.168.71.169:8080 --health-check --group internal 

这样此节点上部署了一个内部的负载均衡器,它可以用来进行端口映射,将动态部署到slave上未知端口的应用映射到固定节点的固定端口上。
另外需要注意的是,此程序并没有做成Docker镜像。为了使程序在slave节点上成功运行,每个slave节点必须安装了Ruby(版本1.9.3+)和Bundler。从这里就可以看出,Docker使程序及依赖库的部署更加简单,一个镜像就可以包括程序及其依赖,不用系统管理员手动进行应用程序环境的配置。
下面的代码清单将keys-and-values的程序组建立了起来:

keys-and-values.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
{
"id": "keys-and-values",
"apps": [
{
"id": "app",
"instances": 3,
"cpus": 0.1,
"mem": 128.0,
"uris": [
"https://github.com/andyyoung01/keys-values-app/archive/master.zip"
],
"cmd": "cd keys-values-app-master && bundle install --retry 3 && bundle exec ruby app.rb",
"env": {
"REDIS_HOST": "192.168.71.168",
"REDIS_PORT": "9000",
"PORT": "11111"
},
"dependencies": [
"/keys-and-values/db"
]
},
{
"id": "db",
"instances": 1,
"cpus": 0.1,
"mem": 128.0,
"disk": 0.0,
"container": {
"type": "DOCKER",
"docker": {
"image": "redis:3.0.3",
"network": "BRIDGE",
"portMappings": [
{
"containerPort": 6379,
"hostPort": 0,
"servicePort": 9000,
"protocol": "tcp"
}
]
}
},
"healthChecks": [
{
"protocol": "TCP",
"portIndex": 0,
"maxConsecutiveFailures": 3
}
],
"labels": {
"HAPROXY_GROUP":"internal"
}
}
]
}

使用如下命令进行应用组的部署:

curl -H 'Content-Type: application/json' -d @keys-and-values.json http://192.168.71.167:8080/v2/groups

上面的代码清单,我们看几个关键的地方:
第9-11行指定了程序的下载地址,下载完成后Marathon自动在其Sandbox目录中解压。
第12行是怎样运行该程序的命令。
第13-17行指定了程序运行需要的相关的环境变量。由于我们使用了Marathon-lb的服务发现机制,便可以指定固定节点的固定端口来连接Redis的服务。PORT环境变量是web服务器监听的端口,默认为8080。我们的实验环境master、slave以及Marathon都部署到同一机器上,8080已经被Marathon占用,所以我们指定PORT到一个未占用的端口。
第18-20行指定app程序依赖于db程序。
第33-40行指定了将容器暴露的6379端口映射到该节点主机上的任意端口(hostPort:0)。服务端口便是通过Marathon-lb的服务发现机制暴露出的端口,其它程序和服务可以连接到Marathon-lb所在机器的该端口来使用这个服务。
第50-52行也比较关键,只有指定了”HAPROXY_GROUP”:”internal”标签,才能将此服务映射到主机的动态的端口,绑定到上面指定的服务端口9000上。
服务部署完成后,可以通过slave节点的11111端口来访问web页面。

Marathon的端口映射的更多信息可以参考官方文档

部署Docker应用程序组

上面的例子部署了一个由命令行的程序和Docker容器组成的程序组。我们再来部署一个全部都是由Docker容器组成的程序组。我们先来添加一个外部的负载均衡器,在167的机器上执行:

docker run -e PORTS=9090  -d --privileged --net=host --name external-marathon-lb mesosphere/marathon-lb sse -m http://192.168.71.167:8080 -m http://192.168.71.168:8080 -m http://192.168.71.169:8080 --health-check --group external

内部负载均衡器还用上面那个例子的(在机器168上),部署的json代码清单如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
{
"id": "wordpress",
"apps": [
{
"id": "web-app",
"instances": 3,
"cpus": 0.1,
"mem": 128.0,
"container": {
"type": "DOCKER",
"docker": {
"image": "wordpress",
"network": "BRIDGE",
"parameters": [
{ "key": "env", "value": "WORDPRESS_DB_PASSWORD=my-secret-pw" },
{ "key": "env", "value": "WORDPRESS_DB_HOST=192.168.71.168:10000" }
],
"portMappings": [
{
"containerPort": 80,
"hostPort": 0,
"servicePort": 11111,
"protocol": "tcp"
}
]
}
},
"labels": {
"HAPROXY_GROUP":"external",
"HAPROXY_0_VHOST":"test-worldpress.example.com"
},
"dependencies": [
"/wordpress/mysql-db"
]
},
{
"id": "mysql-db",
"instances": 1,
"cpus": 0.1,
"mem": 128.0,
"disk": 0.0,
"container": {
"type": "DOCKER",
"docker": {
"image": "mysql",
"network": "BRIDGE",
"parameters": [
{ "key": "env", "value": "MYSQL_ROOT_PASSWORD=my-secret-pw" }
],
"portMappings": [
{
"containerPort": 3306,
"hostPort": 0,
"servicePort": 10000,
"protocol": "tcp"
}
]
}
},
"healthChecks": [
{
"protocol": "TCP",
"portIndex": 0,
"maxConsecutiveFailures": 3
}
],
"labels": {
"HAPROXY_GROUP":"internal"
}
}
]
}

上面的代码我们不再详细解释,要注意的一点是在第30行,启用了外部负载均衡器的虚拟主机配置。这样便可以通过在hosts表中加入一行记录192.168.71.167 test-worldpress.example.com,便可以通过域名访问虚拟主机,并且Marathon-lb通过HAProxy对web-app的三个实例进行了负载均衡。

Marathon框架还有一些其它功能,例如:可以指定约束条件让程序部署到特定的节点上,使用授权和访问控制,以及使用Volumes进行持久化(该功能处于beta阶段),可以参考官方文档