Mesos持久化存储初探

如何将 Mysql, Mongodb 等存储型服务或stateful service融到 Mesos 里会是大家在使用 Mesos 时需要思考的问题。我们认为只有让 MySql 集群,MongoDB 集群,RabbitMQ 集群等等所有这些都使用 Mesos 的资源池,Mesos 才能称之为一个真正的分布式操作系统,才能提高整个分布式环境的资源利用率。然而,在当前的 mesos 0.22,社区并没有给出一个 generic 的解决方案,有的只是某些团队针对个例开源的项目,譬如,twitter 开源的 mysos,一个运行 Mysql 实例的 mesos framework。抛开 mysos 等的可用性不谈,我认为 mesos 需要的是一个统一的,公用的解决方案。

最近 mesos社区在其邮件列表里公布了Mesos 0.23 发布计划概要 (Proposal), 其提到了3个重要的feature:SSL, Persistent Volumes 和 Dynamic Reservations, 后两者都是跟存储相关的功能。 这些功能是 mesos 社区解决持久化问题的雏形,这里我会分享下 mesos 解决此问题的大致思路。另外,由于本篇文章写作时,mesos 0.23 还没有 release,所以可能跟最终版本有些出入。而且,从 Mesos 的原始 Paper 可以看到,Mesos 是为 short-term 或者stateless的计算型任务而生的,所以说使用 Mesos 来管理 long-term 的服务,其稳定性和可行性,还有待实践验证。

当前我们可以怎么解决持久化问题

将stateful service放到 Mesos 集群之外

显然这有违我们使用 Mesos 的初衷,我们无法利用 Mesos 来提高集群外节点的资源利用率。但是在很多应用场景下,譬如stateful service的资源使用在大部分时间内趋于常量,利用现有的成熟方案快速搭建稳定的redis,rabbitmq集群等,仍然是effort最小的解决方案。

使用 Mesos 节点上的本地文件系统

Mesos 通过限制 role 可以将task发布到指定的slave上,这样我们就可以让 stateful service 的task持久化数据到该slave的数据盘上,同时不要将该数据盘作为集群的resource,这样才能避免持久化的数据被Mesos回收掉,新的task才能recover以前的数据。以MySQL数据库为例,通过marathon将 MySQL(Dockerized) 发布到 role=MySQL 的唯一一台slave上,同时map slave上的数据卷 /data 到 docker container 的目录 /var/lib/mysql, 这样MySQL的task就会把数据持久化到slave上的/data目录,而且将来被分发到这个slave上的MySQL task仍然可以restore /data 里的数据。

这个方案会存在许多问题:

  • 第一,为了保证MySQL task 运行时所需的CPU,Memory等,我们需要static reserve 该 slave 上的资源。这样无论MySQL task有没有在使用这些资源,集群中的其它 task 都不能使用它。这个问题可以通过下面我们提到的分布式文件系统部分解决;

  • 第二,目录 /data 里的资源无法被集群释放掉,尽管 /data 里的数据是持久化的,我们仍然可能会在未来的某个时刻删除过时的数据以回收资源,在当前的架构下,只能通过集群之外的手段去完成;

  • 第三, 可能会出现 data conflict,多个MySQL task使用同一个 host 目录 /data,不仅数据冲突难以避免,而且data size也无法限制。在当前的情况下,可以通过限制一个 slave 只运行一个 MySQL task 来临时解决;第四,对 MySQL 集群无能为力,这样也就无法保证服务高可用。

为 Mesos 集群配置分布式文件系统

为了保证 stateful service 能在任何 slave 上访问持久化的数据,我们可以为集群配置 DFS,这就解决了 static reserve 资源的问题。 但随之而来的是数据传输的网络延迟,像MySQL或PG数据库对网络延迟的容忍有待验证。同时,在这个架构下,Mesos无法管理DFS节点间的网络传输,这又相应增加了集群的复杂度。一个显而易见的解决办法是,牺牲网络的资源利用率,在Mesos集群之外再为DFS配置一套高性能的网络环境,避免DFS占用集群的网络资源。

本地文件系统+stateful service build-in 数据分片功能

许多 stateful service 先天支持数据分片功能,譬如Cassandra,MariaDB Galera,MongoDB等,这些是当前最适合接入 Mesos 集群的 stateful service。其中Mesosphere已经实现了cassandra-on-mesos-一个Cassandra的mesos framework, 另外,我还在网络上发现了关于 MariaDB Galera on mesos的博客-利用Marathon发布MariaDB Galera。

以Cassandra为例,限制 Cassandra Framework 使用指定的多个 slave 节点资源,Cassandra 可以在这些节点上的本地文件系统进行数据分片的持久化。显然, 这种情况下没有网络延迟问题,同时,由于 Cassandra 的 task 受 Mesos 集群管理,所以网络传输也是集群可控的,不会增加集群的复杂度。

综上所述,在当前版本下,为了进行数据持久化, 除了集群资源的static partition,我们还需要在 Mesos 集群之外提供磁盘,甚至网络资源。显然,这大大降低了集群的资源利用率。

disk isolation and monitoring(磁盘隔离和监测)

在探讨 persistent volumes 与 dynamic reservations 之前,我们有必要了解下 Mesos 的磁盘隔离和监测机制。尽管磁盘是廉价的,但为了保证 Mesos slave 上其它 task 的正常运行,Mesos 仍然需要限定 slave 上 task 的磁盘配额。

首先,这里所提到的 task 磁盘是一个generic的概念,它有可能是一个通过物理磁盘或LVM卷创建的独享的文件系统,在这种情况下,task如果尝试写超出它所申请的数据量到磁盘中,它会收到信号ENOSPC,即No Space left on Device,(由于笔者没有研究具体的实现细节,mesos 也有可能是通过kernel事件回调来处理data size超出的情况),显然task将会被中断,这种设置比较适合生产环境,又称为 hard enforcement;另外一种是对应的 soft enforcement,task 的磁盘是与其它运行在该 slave 上的 task 共享的文件系统下的一个目录,在这种情况下,Mesos 是通过周期性的运行 du 命令来监测 task 磁盘的使用状况的,所以 task 写入到磁盘的数据量可能会超出它所申请的磁盘大小。

其次,对于上述共享的文件系统,Mesos 实现了 shared filesystem 隔离,当然这个只支持Mesos build-in 容器的隔离,它通过命令 mount -n --bind 来将容器的path映射到slave的path,并且task完成后,kernel会自动执行mount的cleanup工作;对于docker容器的隔离,Mesos使用docker自身的volumn映射机制。

再次,为了避免重度磁盘IO的task阻塞另一个task的磁盘IO操作,Mesos 社区正在讨论利用Cgroups blkio controller 进行磁盘IO隔离。

最后,在 task 写入到磁盘的数据量超出它所申请的磁盘大小时, Mesos 是否应该中断task呢? 对于上面提到的第一种 task 磁盘来说,task 显然会因异常而被终止,进而数据被 Mesos 垃圾回收掉;对于第二种 task 磁盘来说,由于当前 Mesos 不支持持久化磁盘,被中断的 task 将无法 recover 先前的数据,所以 Mesos 默认是不会中断 task 的,Mesos 将这种情况交给 Framework 处理。

更多详细信息, 请参考 issue enforce disk quota in MesosContainerizer

Persistent Volumes(持久化卷)

在即将 release 的 Mesos-0.23 中,Mesos 可以为 task 提供持久化卷,在 task 运行结束后, 持久化卷里的数据不会被 Mesos 回收。同时,新的 task 可以通过该持久化卷读取前一个已完成task存储的数据。与前面提到的在 Mesos 集群外提供磁盘资源不同,这里的持久化卷是Mesos集群内的资源,受Mesos的管理。另外,即使 slave 被重启,或 slave info/id 改变,持久化卷里的数据依然不会丢失。

基于持久化卷,Mesos 会向 Framework offer 两种资源:regular resource 和 persistent resource。其中 regular resource 就是 0.22 版本中的 resource,regular resource 适合 stateless service,task完成后,CPU,RAM和磁盘资源都会被 Mesos 回收;persistent resource 是0.23版本中的一个新概念,它 除了包括持久化卷标,还包含了CPU和RAM等资源,这可能与我们的直观感觉不一样。原因很简单,集群需要避免这种状况,即 slave 可以为 task 提供持久化卷但是 CPU/RAM 已经被占用,所以 Mesos 需要将部分或全部CPU/RAM和持久化卷打包在一起offer给framework。对于提供 stateful service 的Framework来说,它会在 persistent resource 上执行 task,并且 task 完成后,持久化卷里的数据会被保留,这与 google Borg 的工作方式类似。下面是带有persistent resources 的 offer 例子:

{“id” : { “value” : “offerid-123456789”},
 “framework_id” : “MYID”,
 “slave_id” : “slaveid-123456789”,
 “hostname” : “hostname1.prod.twttr.net”
 “resources” : [
   // Regular resources.
   { “name” : “cpu”, “type” : SCALAR, “scalar” : 10 }
   { “name” : “mem”, “type” : SCALAR, “scalar” : 12GB }
   { “name” : “disk”, “type” : SCALAR, “scalar” : 90GB }
   // Persistent resources.
   { “name” : “cpu”, “type” : SCALAR, “scalar” : 2,
     “persistence” : { “framework_id” : “MYID”, “handle” : “uuid-123456789” } }
   { “name” : “mem”, “type” : SCALAR, “scalar” : 2GB,
     “persistence” : { “framework_id” : “MYID”, “handle” : “uuid-123456789” } }
   { “name” : “disk”, “type” : SCALAR, “scalar” : 10GB,
     “persistence” : { “framework_id” : “MYID”, “handle” : “uuid-123456789” } }
 ]
 ...
}

这里社区给出了一个 framework 使用持久化卷的例子an example framework for testing persistent volumes

另外,由于 task 之间可能需要共享相同的持久化数据,Mesos 未来还会支持资源共享。譬如,MySQL 的 framework 可能会运行一个 mysqld server 的 task 和另一个周期备份数据库的 task,并且,这两个 task 都需要访问同一个持久化数据文件。

更多详细信息,请参考issue persistent resources support for storage-like services

Dynamic Reservations(动态保留)

前面已经提过,对于 stateful service framework 来说,framework 需要在特定的一个或几个 slave 上运行新的 task 以能够读取已完成 task 存储在持久化卷里的数据。在 0.22 版本,为了保证任何时刻在这些特定的 slave 上都能执行上述操作,slave 在启动时会静态的为相应的role保留资源,即每一个slave需要为每一个支持 stateful service 的role进行配置,资源无法被其它 framework 使用,灵活性特别差。这种状况在0.23实现了 Dynamic Reservations 之后得到了改善,相对于static role,集群引入了新的 reserved role。

  • 当启动一个task时, framework 可以设置 “reservedrole” 去声明动态保留resource(无论是regular resource 还是 persistent resource),其中这里的 “reservedrole” 是framework注册到Mesos的role;
  • 当task完成后,设置了“reserved_role” 的 resource 会重新 offer 给相应的 framework;
  • 当一个framework收到了带有 “reserved_role” 的resource,它会知道这个资源是动态保留的,如果framework不再需要这些动态保留的资源了,它可以通过新的 API “release" 来释放掉这些资源。但是,framework 无法释放 静态分配 的资源给其它framework使用。

    更多详细信息,请参考issue[Dynamic Reservation] (https://issues.apache.org/jira/browse/MESOS-2018) 展望

基于 Mesos 0.23,我们可以怎样让 stateful service 更好更快的使用 Mesos 集群资源呢?从有状态服务与 Mesos 集成的工作量大小角度来看,有状态服务可以分为两大类:

  • 无中心节点的 stateful service。譬如Cassandra,由于各节点的功能平等,我们可以利用docker包装Cassandra程序成excutor,并通过已有schedule 如 Marathon 发布到特定的已进行了持久化配置的 slave 节点上来提供服务。
  • master-slave 或者 leader-follower 模式的 stateful service。例如HDFS,MongoDB,MySQL等,这种类型服务的节点功能是不一样的,有name node, config node,data node 等等。我们需要为各个服务的各种节点dockerize excutor, 并开发schedule,同时解决容错,备份等问题以达到production ready。

综上, Mesos 0.23 的 release 将极大的改进stateful service使用Mesos集群的方式,这也让企业的整个环境集成到 Mesos 成为可能。

作者简介:

周伟涛,数人科技云平台负责人。曾在Red Hat工作。Pythoner,对NLP,CI,Mesos和docker有一定的实践经验。

Reference:

https://docs.google.com/presentation/d/1ku7YnAzUai0A0i79tYycq-lg5EhewhXoTomYeRPLTBw/edit#slide=id.g39b2d4eb6_00

https://docs.google.com/document/d/1Az4pYN9LEOsCqTgL0MXn7YyId6IU2HZKa0_hIaxu0uE/edit

https://docs.google.com/document/d/1GwI0gk6oZHL3tJbdZRFstnUftR0C4udRY6a-l9XRhgI/edit#heading=h.fm07s55ubih4

http://cloudarchitectmusings.com/2015/03/31/dealing-with-persistent-storage-and-fault-tolerance-in-apache-mesos/

https://docs.google.com/document/d/1Az4pYN9LEOsCqTgL0MXn7YyId6IU2HZKa0_hIaxu0uE/edit