Mesos持久化存储初探

持久化是Mesos下一个版本的一项重点工作,也是提高Mesos分布式环境资源利用率必须解决的问题。本文系作者根据自己在Mesos Meetup第二期的演讲内容整理,讲解Mesos 解决持久化存储问题的思路,介绍了即将发布的Mesos 0.23的两个相关特性:Persistent Volumes 和 Dynamic Reservations。

如何将 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](https://github.com/mesosphere/cassandra-mesos)-一个Cassandra的mesos framework, 另外,我还在网络上发现了关于 [MariaDB Galera on mesos](http://sttts.github.io/galera/mesos/2015/03/04/galera-on-mesos.html)的博客-利用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](https://issues.apache.org/jira/browse/MESOS-1588) 。

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](https://reviews.apache.org/r/32984/diff/2/)

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

更多详细信息,请参考issue [persistent resources support for storage-like services(https://issues.apache.org/jira/browse/MESOS-1554)

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 可以设置 “reserved_role” 去声明动态保留resource(无论是regular resource 还是 persistent resource),其中这里的 “reserved_role” 是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 成为可能。

http://www.csdn.net/article/2015-07-02/2825111

相关推荐