概述
杏仁的容器化平台基于Mesos和Marathon构建,使用Calico提供容器的多主机网络,并通过Consul进行服务的注册和发现。
之所以选择这套方案,是考虑到成本和可控性。由于使用的组件都是开源项目,几乎不需要二次开发,另外,每项功能都由不同组件提供,可以进行很细粒度的控制。
总体架构参见下图:
资源分配
Docker本身提供了资源使用的管控,启动容器时可以指定CPU和内存的限制,但在大规模多主机的环境中,需要一种更加高效、集成的资源管理方式。而分布式资源管理框架Mesos,经过了Facebook、Twitter这些大型公司的万台主机验证,无疑是一个很好的选择。
Mesos实现了两级调度架构,可以管理多种类型的应用程序,支持Docker。第一级调度是Master的守护进程,管理Mesos集群中所有节点上运行的Slave守护进程。集群由物理服务器或虚拟服务器组成,用于运行应用程序的任务,比如Hadoop和MPI作业。第二级调度由被称作Framework的“组件”组成。Framework包括调度器(Scheduler)和执行器(Executor)进程,其中每个节点上都会运行执行器。Mesos能和不同类型的Framework通信,每种Framework由相应的应用集群管理。图中只展示了Hadoop和MPI两种类型,其它类型的应用程序也有相应的Framework。
Mesos Master协调全部的Slave,并确定每个节点的可用资源,然后向注册到Master的Framework(作为Master的客户端)发出resource offer。Framework可以根据应用程序的需求,选择接受或拒绝来自master的resource offer。一旦接受邀约,Master会协调Framework和Slave去运行应用,因此,Mesos可以将Hadoop、Cassandra等多个任务同时运行在同一个节点。
通过下面的实例可以进一步理解Mesos的资源分配:
- 各个Slave收集本机可用的CPU、内存、磁盘资源,汇集到Master.
- Master将各个Slave收集的资源作为resource offer提供给Framework(Marathon).
- Marathon会选择一个resource offer, 并将一个需要2核cpu、512M内存和5GB磁盘的任务提交给Master.
- Master将该任务分配给slave,后者通过Executor运行任务.
- Master继续将可用的资源作为resource offer提供给其它Framework,重复上面的步骤.
调度
在容器调度方面,我们选择了Marathon。Marathon 是可以和 Mesos 一起协作的一个 framework,用来运行持久性的应用,比如docker。用户可以通过WEB UI和API将应用发布到mesos集群中。
Marathon主要有以下特性:
- 高可用
- 支持多种容器技术
Mesos containers 和 Docker
- 资源控制,控制容器运行在特定机器上
- 应用扩展和故障恢复
比如我们启动三个服务,分别是一个节点的,三个节点的和五个节点的。我们想拓展这些服务的话,可以调用Marathon的API进行扩展,并且是秒级的。如果有一台主机宕掉了, Marathon会均匀地把服务迁移到其他的机器上,选择资源有空余的机器进行迁移。这样能就保证服务是动态的调度,保证服务的高可用,并且这些都是它内部自行处理的,不需要手动干预。
- 提供健康检查
支持Http,Tcp和自定义脚本测活
- 提供Web UI
- 提供API
界面如下:
网络
在测试环境中docker一般通过映射主机端口来提供网络功能,但在生产环境,考虑到性能和兼容性问题(有的应用需要独享IP),1容器1 IP的方案必不可少。
目前Docker的网络方案主要分为隧道和路由两大类,通过不同的实现方式。
隧道
通过隧道,或者说Overlay Networking的方式:
- Weave:UDP广播,本机建立新的BR,通过PCAP互通。
- Open vSwitch(OVS):基于VxLAN和GRE协议,但是性能方面损失比较严重。
- Flannel:UDP广播,VxLan。
隧道方案在IaaS层的网络中应用也比较多,大家共识是随着节点规模的增长复杂度会提升,而且出了网络问题跟踪起来比较麻烦,大规模集群情况下这是需要考虑的一个点。
路由
比较典型的代表有:
- Calico:基于BGP协议的路由方案,支持很细致的ACL控制,对混合云亲和度比较高。
- Macvlan:从逻辑和Kernel层来看隔离性和性能最优的方案,基于二层隔离,所以需要二层路由器支持,大多数云服务商不支持,所以混合云上比较难以实现。
最终,考虑到性能和集成成本,我们选择了Calico。
服务发现和负载
Marathon支持多种服务发现和负载方式:
- Mesos-DNS 通过Dns实现服务的发现
- Marathon-lb 使用Haproxy提供基于端口的服务发现
- haproxy-marathon-bridge (不推荐) 通过脚本将marathon中的服务信息更新到Haproxy。
在测试环境,考虑到易用性,我们使用Marathon-lb进行服务注册和负载,前端的nginx将请求二次转发到haproxy的固定端口。
在生产环境中,为减少二次转发的开销并兼容服务化使用的consul注册方式,我们使用Registrator将应用注册到Consul,再通过consul-template将应用的访问地址动态写入到nginx,并由nginx提供负载功能。
遇到的问题
- Registrator无法注册
Registrator的latest版本镜像无法注册(很久没更新了),需要使用master版本
- Marathon和Consul的测活间隔问题
Marathon和Consul都有自己的测活配置,如果Consul的测活间隔大于Marathon的测活间隔,会导致应用发布期间可能出现服务不可用。Marathon发布期间会保证整个过程灰度,但Registrator只要检测到容器启动就会注册到Consul,此时Consul中的应用会同时存在可用和不可用的服务实例(不可用的实例不会加入nginx负载),但当Marathon完成应用测活后会立即杀掉老的应用,这时Consul中不可用的服务器还没有完成测活,但可用的服务已经被杀掉,整个服务就处于不可用状态,reload nginx时因为没有可用的upstream server会报错。所以一定要保证Consul的测活间隔小于Marathon的测活间隔,目前是1比5的测活间隔。
- 更改Mesos slave的配置后,mesos slave无法启动
Mesos slave的配置变更后,需要清理掉mesos slave的元数据,才能启动。具体的命令可以参考slave的log。
参考资料
mesos : http://mesos.apache.org/documentation/latest/
marathon : https://mesosphere.github.io/marathon/docs/
Calico : http://docs.projectcalico.org/v1.6/getting-started/
Registrator : http://gliderlabs.com/registrator/latest/user/quickstart/
Consul : https://www.consul.io/intro/
Consul template : https://github.com/hashicorp/consul-template