Ansible 开发实战:多个TaskQueueManager并行运行时本地临时目录丢失的问题

(本文基于Ansible 2.7)
TaskQueueManager是Ansible调度play的基本执行单元,如果有多个TaskQueueManager并行运行,并且按照ansible官方给出的API示例清理本地的临时目录:

shutil.rmtree(C.DEFAULT_LOCAL_TMP, True)

可能会造成FileNotFoundError

FileNotFoundError: [Errno 2]No such file or dirctory:
‘/xxx/.ansible/tmp/ansible-local-xxxxx/tmpxxxx’

这是因为Ansible本地临时目录的生成是在lib/ansible/constants.py里做的。constants会调用ConfigManager来读取本地配置:

# POPULATE SETTINGS FROM CONFIG ###
config = ConfigManager()

# Generate constants from config
for setting in config.data.get_settings():

    value = setting.value
    if setting.origin == 'default' and \
       isinstance(setting.value, string_types) and \
       (setting.value.startswith('{{') and setting.value.endswith('}}')):
        try:
            t = Template(setting.value)
            value = t.render(vars())
            try:
                value = literal_eval(value)
            except ValueError:
                pass  # not a python data structure
        except Exception:
            pass  # not templatable

        value = ensure_type(value, setting.type)

    set_constant(setting.name, value)

value = ensure_type(value, setting.type)

创建了本地临时目录。下面是ensure_type方法中与本地临时目录创建相关的分支代码块:

        elif value_type in ('tmp', 'temppath', 'tmppath'):
            value = resolve_path(value, basedir=basedir)
            if not os.path.exists(value):
                makedirs_safe(value, 0o700)
            prefix = 'ansible-local-%s' % os.getpid()
            value = tempfile.mkdtemp(prefix=prefix, dir=value)

可以看到,ansible用“ansible-local-” 和当前进程的pid拼成一个前缀,并用这个前缀创建了临时目录,并赋值给constants.DEFAULT_LOCAL_TMP。那么问题来了,一旦constants在主进程被引用,本地临时目录就会在主进程被创建,后续无论是启动子进程还是子线程来实现TaskQueueManager并行,这个值都会被所有的TaskQueueManager继承。每个TaskQueueManager执行时间不定,先执行完毕的在做本地临时目录清理的时候是直接rmtree的,也就是说可能把其他TaskQueueManager产生的临时文件也删除掉了,这样就报出了上面的那个FileNotFoundError。

这个问题我觉得是因为Ansible其实主要还是在考虑将其作为一款工具来使用,而从作为远程作业调度库使用的角度来说考虑较少导致的,从设计阶段就没有考虑以TaskQueueManager的粒度来做临时文件的清理,即使不算是设计缺陷应该说也是不太完善。

从这个角度来说,较好的方案应该是本地临时目录由TaskQueueManager来创建,临时目录的绝对路径由TaskQueueManager来维护,在TaskQueueManager.cleanup方法中由TaskQueueManager来负责清理。临时目录的命名避免使用pid这种与运行环境相关的数据,而考虑使用UUID更好。这样TaskQueueManager之间互不干扰,可以杜绝误删。

但以2.7版本的源码为例,其中对DEFAULT_LOCAL_TMP项的引用超过10处(不含测试代码),在保留这个配置项的前提下,要做到这个设计首先需要从ensure_type中去掉拼接前缀和创建临时目录的步骤,改到TaskQueueManager里,这个修改的动静就比较大了,需要大量的测试。

如果稍退一步,可以从自身应用的编写上想办法
首先,采用多进程结构,确保每个进程只有一个TaskQueueManager对象被创建。
第二,保证constants在TaskQueueManager子进程创建之前不被引用。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值