本地存储卷
前面示例已经启动了redis镜像,然后为了验证marathon的资源隔离(主要是内存)特性,做了以下尝试:
- 将redis容器的内存限制为32M;
- 通过脚本循环向redis写入长字符串;
- 发现当任务资源监控超过16M的时候,内存占用又会重新清零。
这个过程我们可以知道,将数据写入redis之后,任务监控到redis实例的内存占用在不断增加。然后超过内存限制的一半之后,任务被kill,几秒钟之后marathon重新启动了任务,本地redis客户端重新可用。这里内存超过一半是因为默认redis开启了rdb文件保存(bgsave),redis需要新fork出进程进行rdb文件写入,导致此时虚拟内存翻倍,容器占用的内存超过了cgroup限制,被内核oom kill。我们可以通过内核日志发现一次oom kill的日志。最后marathon虽然重新启动了任务,但是由于docker容器的隔离性,之前写入硬盘的内容(rdb)文件都已经消失,导致内存清零,之前写入的大字符串消失。
如果我们希望redis任务重新启动之后能够保留数据(虽然对于测试场景无效,因为内存不足,如果数据恢复,进程也会立即被内核kill),就需要将容器中的redis工作目录持久化保存。
marathon文档中描述了两种持久化的方式,对于我们来说,最简单的就是使用本地卷进行持久化。本地卷的大致方式是向mesos master申请指定大小的卷,并将该卷和任务容器关联起来(对于docker容器使用volume-from参数关联即可)。
启动redis任务,其中和卷相关的配置为:
"volumes": [
{
"containerPath": "/data",
"hostPath": "redis",
"mode": "RW"
},
{
"containerPath": "redis",
"mode": "RW",
"persistent": {
"size": 1024
}
}
]
该配置表示在slave机器上申请名为redis的卷,大小为1024MB,权限为读写。同时设置将主机的redis卷挂载到容器的/data目录中,挂载权限为读写。
提交任务之后,发现使用了本地卷之后的任务一直在unsheduled状态,slave上没有任务分派的数据。查了半天文档也没有好的解释。于是重新按照marathon文档进行了设置,特别关注其中的Prerequisites章节。其中描述了marathon启动参数中必须设置mesos_authentication_principal
和mesos_role
两个参数。但是其中没有太明确描述需要设置什么值。
由于这两个参数主要作为在mesos master执行操作时后的标识,首先先在marathon机器上的/etc/marathon/conf/目录中分别创建mesos_authentication_principal和mesos_role两个文件:
echo 'marathon' > /etc/marathon/conf/mesos_authentication_principal
echo 'marathon' > /etc/marathon/conf/mesos_role
重启marathon服务之后,在任务详情中就可以看见新增的volume标签页。但情况并没有好转,任务长时间处于waiting状态,卷的状态一直为detached。最终在mesos master(leader那台)上找到了一些错误日志:
Dropping RESERVE offer operation from framework ... A reserve operation was attempted with no principal, but there is a reserved resource in the request with principal 'marathon' set in
ReservationInfo
紧接着一条错误:
Dropping CREATE offer operation from framework...Invalid CREATE Operation: Insufficient disk resources
查询了文档之后大概知道,由于principal设置的问题,mesos master没有认可marathon发起的RESERVE指令(既让slave预留一部分资源,这里是硬盘空间),随后由于没有预留足够的硬盘空间,发送的创建指令由于没有剩余空间而失败。
从错误日志基本可以断定是由于principal设置问题导致的,从marathon任务相关文档中,终于了解了mesos master acl相关的配置,,以及这些配置对frameworks的影响。直接抄文档中的acls内容会有问题,因为其中只是指定了run_tasks和register_frameworks两个操作的acl,通过mesos文档,我们可以查阅到mesos master支持的所有acl项。对于我们的需求,需要授权marathon支持reserve_resources操作,最终将acls参数内容更新为:
{
"run_tasks": [
{
"principals": {
"type": "ANY"
},
"users": {
"type": "ANY"
}
}
],
"register_frameworks": [
{
"principals": {
"type": "ANY"
},
"roles": {
"type": "ANY"
}
}
],
"reserve_resources": [
{
"principals": {
"type": "ANY"
},
"roles": {
"type": "ANY"
},
"resources": {
"type": "ANY"
}
}
],
"unreserve_resources": [
{
"principals": {
"type": "ANY"
},
"roles": {
"type": "ANY"
},
"reserver_principals": {
"type": "ANY"
}
}
],
"create_volumes": [
{
"principals": {
"type": "ANY"
},
"roles": {
"type": "ANY"
}
}
],
"destroy_volumes": [
{
"principals": {
"type": "ANY"
},
"roles": {
"type": "ANY"
},
"creator_principals": {
"type": "ANY"
}
}
]
}
这里只是为了能够正常分配,没有做任何访问控制。将上述内容保存到文件(/etc/mesos-acls)然后添加到mesos-master的参数:
echo 'file:///etc/mesos-acls' > /etc/mesos-master/acls
重启所有mesos master,之前一直在waiting状态的任务,已经可以正常申请卷,并启动容器。进入分配到的slave服务器,我们可以看见多了一个挂载点:
/dev/vda1 on /var/lib/mesos/slaves/fb9d0623-c0bf-402e-9288-06206b30ea54-S4/frameworks/487f9562-5998-4cfb-bc92-bbc3a441c69c-0000/executors/redis_single.bd21b767-41b8-11e6-9d7a-024259ae7178/runs/8c9304a1-b960-4aa0-a169-f368a4d77ed9/redis type xfs (rw,relatime,attr2,inode64,noquota)
同时通过docker inspect命令查看redis容器的参数,可以发现其中卷挂载有如下配置:
"Mounts": [
{
"Source": "/var/lib/mesos/slaves/fb9d0623-c0bf-402e-9288-06206b30ea54-S4/frameworks/487f9562-5998-4cfb-bc92-bbc3a441c69c-0000/executors/redis_single.bd21b767-41b8-11e6-9d7a-024259ae7178/runs/8c9304a1-b960-4aa0-a169-f368a4d77ed9/redis",
"Destination": "/data",
"Mode": "rw",
"RW": true,
"Propagation": "rprivate"
},
{
"Source": "/var/lib/mesos/slaves/fb9d0623-c0bf-402e-9288-06206b30ea54-S4/frameworks/487f9562-5998-4cfb-bc92-bbc3a441c69c-0000/executors/redis_single.bd21b767-41b8-11e6-9d7a-024259ae7178/runs/8c9304a1-b960-4aa0-a169-f368a4d77ed9",
"Destination": "/mnt/mesos/sandbox",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
]
既创建的挂载点已经被挂到容器的指定路径了。
然后我们进行验证:
首先通过redis客户端连接到redis上,然后设置工作路径为我们挂载的路径:
redis-cli -h 1.1.1.1 CONFIG SET dir /data
然后写入数据:
redis-cli -h 1.1.1.1 set a b
为了确保能够正常持久化,执行:
redis-cli -h 1.1.1.1 bgsave
查看挂载卷路径,可以发现已经有dump.rdb文件产生。此时kill掉redis任务,等待marathon重新启动。再次启动之后,通过redis-cli连接进入redis之后,可以看见之前设置的key仍然存在:
> redis-cli -h 1.1.1.1 keys *
1) "a"
然后获取这个key的值:
> redis-cli -h 1.1.1.1 get a
1) "b"
从这个实验可以看出,marathon的本地存储卷可以确保服务重新启动之后原来的数据仍然存在,对于存储类的应用(如redis、mysql等)非常有用。
你好!我按照你这样设置 marathon 还是waiting状态,在slave,已经看到创建了这个路径
/var/lib/mesos/volumes/roles/marathon/myadd#data#4c841c60-862f-11e6-8d97-005056a13037
找不到任何LOG, 求助, QQ515678087
尝试看下mesos master上有什么分配日志么?平时不上qq,有其他联系方式么?
你好。
如果有多个从节点,如果redis容器重启了,漂移到另外一个节点上运行。
那挂载的本地存储卷,在新的节点上并不存在。
这种情况是怎么处理?
我感觉本地卷就是实验性的,真的要能支持漂移,还是需要其他挂载方式,或者是无状态的服务才行。
如果有其他的挂载方式,感谢能分享下。
我正在研究简单的挂载方式,比如用启动一台NFS server 服务器,然后三台节点挂载到自己分区/data/docker,mesos-master挂存储卷的时候,选择/data/docker 。由于每个节点都挂载了/data/docker目录,解决容器漂移的问题。正在验证可行性。
我之前搭建过glusterfs,不过只是用来做内部文件共享,没有尝试过做mesos挂载。但是glusterfs只是双写,也支持通过nfs挂载,可靠性应该会好点。你可以试试看,不过我现在工作原因已经不涉及这块内容了,最近没有弄过类似的东西了。