容器持久化存储实战:使用Marathon本地卷创建有状态应用

Marathon是Mesos的一款核心框架,能够支持运行长服务。Marathon在版本1.0之后增加了持久化存储功能,本文教你快速上手使用Marathon本地持久化卷创建有状态应用。

注意: Marathon版本1.0的持久化存储功能为beta版,使用时可能存在潜在风险。建议使用Marathon版本1.1及以上。

我们知道,当Marathon应用终止或重新登录时会丢失状态。但在某些场景下,比如使用MySQL,应用状态必须得保存。这时候,我们就可以采用本地持久化卷来支持有状态应用。本地持久化卷之所以能支持有状态任务,是因为任务重启时数据不会丢失。

当配置本地持久化卷时,任务以及相关数据将直接pin到它们首个登陆的节点,如果终止后重新登录,会依然登陆到该节点。应用所需资源也将被保存。除了所配置的sandbox disk大小作为应用定义的一部分,Marathon还暗中保留一个合适大小的磁盘空间(通过persistent.size在卷中声明)。

使用Marathon本地持久化卷有哪些好处?

•运行有状态服务的任务时,所需全部资源将动态保存,从而确保任务能在同一节点重新登录并在必要时使用同一个卷。
•任务不必强制pin到某个agent里,即使数据都在其上。
•指定分布式逻辑关系时,依然可以采用限制条件。
•对于不再使用的持久化卷,Marathon找到它后立即销毁,释放资源。

如何使用Marathon本地持久化卷创建一个有状态应用?

1 前提

要使用Marathon本地持久化卷来创建有状态应用,需要设定2个命令行标记,从而让Marathon保存/释放资源,以及创建/销毁卷。

•–mesos_authentication_principal:你可以按需选择,但如果已经在Mesos master上设置了ACLs,那么这一条principal就是通过验证和授权的。
•–mesos_role: 这个role比较特别,会在Marathon中暗自使用,也就是说,不必通过—roles来配置Mesos master。

2 配置选项

可使用如下选项来配置一个持久化卷:

容器持久化存储实战:使用Marathon本地卷创建有状态应用_第1张图片

•containerPath: 应用读写数据的路径。它必须是关于容器的单层路径,不能包含一个前置的斜杠(/)。(即:应该是”data”, 而不是”/data”, “/var/data”或”var/data”)。
•mode:卷的登陆模式。
•persistent.type:mesos磁盘资源使用类型。有效的选项是root, path, 和mount。
•persistent.size: 持久化卷的大小,在MiBs量级。
•persistent.maxSize: 对于root mesos磁盘资源来说,这是可考虑的专有增加卷的最大尺寸
•persistent.constraints:创建新持久化卷的约束条件。

注意 还需要设置一个residency节点来告知Marathon建立有状态应用。

3 有状态应用的扩展和收缩

当应用收缩时,与终止实例相关的卷已经分离,但所有资源依然保留。这时候,可通过Marathon REST API来删除任务,释放所保留的资源,并销毁持久化卷。

卷分离后应用所需的全部资源依然被保留。一方面,用户希望彻底销毁这些分离卷,将资源释放给其它应用和框架使用;另一方面,用户希望这些卷就处于分离状态,当应用再次扩展时,卷上的数据依然在那儿。

注意
如果应用已经被删除,任何相关的卷和保留的资源也将被删除。

4 升级/重启有状态应用

一个有状态应用的默认升级策略UpgradeStrategy是: minimumHealthCapacity 0.5
maximumOverCapacity 0

如果不按默认值来,定义也必须低于上述默认值以确保通过验证。

更深层次来讲,之所以UpgradeStrategy必须低于默认值,是因为Marathon必须在启动新任务之前杀死旧任务,如此一来,新任务就能接管之前的保留资源和卷,Marathon也无法再创建额外任务,从而阻止了额外卷的创建。

注意
对于有状态应用,Marathon从来不会启动超出UpgradeStrategy里配置的实例,而且在升级或重启期间,Marathon会优先杀掉旧实例,再创建新实例。

在Hood之下

Marathon主要为Mesos提供了三个功能来支持有状态应用的运行:动态预留,预留标签和持续化卷。

不同于静态预留,动态预留是针对一个给定的role在runtime时创建,并通过预留标签将资源与frameworkId、taskId结合在一起。这使得Marathon重启一个先前因某些原因终止过的有状态任务时,对于未配置给该role的框架将无法使用相关资源。

持久化卷可以hold住应用的有状态数据,这是因为持久化卷是局部于一个agent的,使用了相关数据的有状态任务将直接pin到初始登陆的agent上,并且在需要时依然重新登录同一个节点。用户无需设置任何约束条件,当Marathon启动一个任务时,它将接受一个匹配的Mesos请求,为任务动态预留所需资源,创建持续化卷,并确保任务每次重启都使用这些预留资源,从而访问现有数据。

一旦某个使用持久化卷的任务终止,它的元数据将被保存。如有需要,可通过该元数据启动一个替代的任务。

例如,如果把5个实例缩减为3个,那么有2个任务会处于Waiting状态,并且这2个任务所使用的持久化卷以及它们所在的agent节点等相关信息也都在。Marathon不会不保留这些资源,也不会销毁卷。当用户需要再次扩展时,Marathon只要一接收到包含有标记资源的Mesos请求,就马上试图启动那些使用现有预留资源和卷的任务。

Marathon只在下列情况下不保留资源/销毁卷:

•应用删除(这种情况下,所有任务的卷已被销毁,所有预留资源都已删除)。
•明确使用wipe=true标志删除一个或多个挂起的任务。

如果预留资源或创建持久化卷失败,已创建任务将在task_reservation_timeout(默认: 20秒)后超时失效,Marathon随即开始新的资源预留动作。如果任务是LOST(该任务的agent断开链接或崩溃),那么预留资源和卷将不会超时失效,这时需要手动删除清理该任务,从而让Marathon启动一个新的任务。

潜在陷阱

利用动态预留和持久化卷在Marathon里运行有状态应用,请注意下列问题和局限性。

1 静态预留

动态预留的创建只针对非预留资源,如果使用Mesos –resources 或 –default_role标志将一个agent的资源指定预留给一个role,那么这些资源是无法用于动态预留的。

此外,如果Marathon以–default_accepted_resource_roles标志开始,而且指定的值不含 * ,那么应用定义应该明确指定”acceptedResourceRoles”: [“*”],这样非预留集群资源才被允许使用和预留。

2 资源请求

目前,有状态应用的资源请求不能被更改。比如初始卷大小,CPU使用情况,内存要求等,一旦应用定义发出,这些资源请求就无法更改。

3 复制和备份

由于持久化卷是pin到节点上的,一旦节点从集群断开链接,比如网络分区或agent崩溃,这时持久化卷也一并“失联”。如果有状态服务无法自我数据复制,就需用户手动建立一套复制和备份策略来应对网络分区或agent崩溃造成的数据丢失。

如果agent重新登记到集群并提供资源,Marathon最终能重新登录任务到该节点。如果节点没有重新登记到集群,Marathon将永远处于等待接收期望资源的状态,因为当下它的目标就是重新使用现有数据。如果不希望该agent重新回来,可添加一条wipe=true标志将相关任务删除,Marathon最终会在另一个agent上用新卷启动一个新任务。

4 磁盘消耗

对于Mesos 0.28,销毁持久化卷不能清理或销毁数据。Mesos正在考虑删除卷的元数据,但数据依然保留在磁盘。为了阻止磁盘消耗,用户应手动一处不再需要的数据。

5 不唯一的role

Mesos的静态预留和动态预留均绑定到role,而不是绑定到框架或框架实例。前文已经提及,Marathon添加标签来声明预留资源与frameworkId和taskId结合在一起。然而,这种标签不能防止预留资源被其它框架或旧的Marathon实例(1.0以前)误用。每一个已经登记到给定role的Mesos框架最终接收的offer会包含已经预留给该role的资源。

然而,如果另一个框架无视预留标签的存在以及标签预期的用意,而径直使用资源,Marathon也无法收回这些已背离最初用途的资源。不同的框架最好不要使用同一role,如果框架中有谁使用动态预留的话。不过HA模式下的Marathon实例不需要特定的role,因为设计之初就是使用同一role。

6 Mesos sandbox

临时的Mesos sandbox依然是stdout和stderr日志的作用目标。

实践举例

1 通过Marathon UI创建一个有状态应用

a. 在web界面创建一个新的Marathon应用;
b. 点击卷目录
c. 选择需要使用的卷以及卷大小。所选卷大小需适合应用需求;卷大小在登陆应用后无法再修改。
d. 定制容器路径,应用将从这里读写数据。容器路径必须非嵌套,也不能包含斜杠,比如,应该是data,而不能是../../../etc/opt或/user/data/。
e. 点击创建。

2 在一个专有磁盘上用Marathon来运行有状态的PostgreSQL

Marathon上一个PostgreSQL的应用定义模板如下所示。注意,模板里将postgres数据文件夹设置到pgdata,与Mesos sandbox相关(包含在$MESOS_SANDBOX变量中),这样就能用pgdata的容器路径来建立持久化卷。该路径非嵌套,而且与要求的sandbox相关。

容器持久化存储实战:使用Marathon本地卷创建有状态应用_第2张图片

注意
上述情况中一个Mesos挂载磁盘要求在512mb到1gb之间,这样Postgres就有了专门访问资源的路径,从而提升应用的IO吞吐。之所以指定最大尺寸为1gb,因为太大的专有卷不宜分配给应用。而且,依照惯例,如果SSD和机械盘可以通过挂载路径来确认,那么就可以用限制条件来挑选磁盘类型。

3 在Marathon上运行有状态的MySQL

默认的MySQL docker镜像不允许更改数据文件夹。既然无法用明显嵌套的containerPath,比如/var/lib/mysql来定义一个持久化卷,我们必须配置一个工作区来建立一个从主机路径mysql(与Mesos sandbox相关)到/var/lib/mysql(MySQL试图读/写的路径)的docker挂载。

容器持久化存储实战:使用Marathon本地卷创建有状态应用_第3张图片

此外,我们用一个containerPath mysql来配置持久化卷,这样就能将本地持久化卷以mysql挂载到docker容器。

容器持久化存储实战:使用Marathon本地卷创建有状态应用_第4张图片

完成版的JSON应用定义读如下:

容器持久化存储实战:使用Marathon本地卷创建有状态应用_第5张图片

4 检查并删除悬挂的有状态任务

要销毁和清理持久化卷并释放任务相关的预留资源,执行以下2步:

a. 定位包含有该持久化卷的agent并移除里面的数据。
b. 发送一个包含wipe=true标志的HTTP DELETE请求给Marathon。

要定位agent,请检查Marathon UI,并在卷目录里找到分离卷。或者,询问/v2/apps终点,它会提供有关host和Mesos slaveId的信息。

容器持久化存储实战:使用Marathon本地卷创建有状态应用_第6张图片

注意 一个运行的任务将显示stagedAt, startedAt和version,以及上述提及的信息。

当然,你还可以:
a. 用ssh’ing将磁盘数据移到agent里,运行rm -rf /*命令。
b. 用wipe=true删除任务,它可以将任务信息从Marathon内部仓库里清除,并最终销毁卷,不再提前预留与任务有关的资源:http DELETE

5 查看持久化本地卷上的应用状态

创建Marathon应用后,查看应用细节的卷目录可以获取应用实例和相关卷的详细信息。
“Status”(状态)栏显示了应用实例是否链接到卷。如果应用收缩,那么应用实例将显示为“detached”(分离)。
点击一个卷可展开卷的细节页面,有关用户个人的卷信息都可看到。

本文翻译自:Marathon:Stateful Applications Using Local Persistent Volumes

你可能感兴趣的:(容器持久化存储实战:使用Marathon本地卷创建有状态应用)