Ansible并发执行playbook更新filex.dat文件锁报错处理
问题根因
并发执行playbook时多个rsync进程同时操作目标节点上的同一个filex.dat,rsync写入完成后执行重命名操作时会持有文件的排他锁,后到达的操作拿不到锁就会抛出rename failed错误。你最初写的wait_for方案完全不生效,原因有三个:
- wait_for默认仅检查文件是否存在、是否可访问,
filex.dat本身是长期存在的文件,检查会直接通过,根本不会等待锁释放 - 你加了
delegate_to: localhost,等于在Ansible控制机本地检查路径,完全感知不到远端ENVX节点上的文件锁状态 - 示例代码的YAML缩进、play结构存在语法错误,根本无法正常运行
可行解决方案
核心思路是在操作目标文件前加互斥锁,保证同一时间只有一个任务能执行文件更新操作,其余任务自动排队等待锁释放,不需要靠固定时长sleep傻等。
方案1:目标节点flock锁(最通用,无控制节点限制)
Linux系统默认自带flock工具,可以直接在存放filex.dat的ENVX节点上做内核级文件互斥,配合Ansible重试机制兼容所有并发场景,不管你从哪套环境发起playbook都能生效:
- hosts: xpto gather_facts: no tasks: - name: 加锁更新filex.dat,冲突自动重试 block: - name: 申请目标文件操作排他锁 command: flock -x -w 30 /tmp/filex.dat.lock -c "echo get_lock_ok" register: lock_res changed_when: false - name: 同步更新filex.dat ansible.posix.synchronize: src: "some/path1/v" dest: "some/path2/filex.dat" rescue: - name: 等待1秒后重试 pause: seconds: 1 retries: 10 until: lock_res is success
逻辑说明:
flock -x -w 30表示对独立的锁文件/tmp/filex.dat.lock加排他锁,最多等待30秒拿锁,拿不到直接返回错误状态- 只有成功拿到锁才会执行后续的synchronize同步操作,从根本上避免多进程同时操作目标文件
- 锁会在flock命令执行结束后自动释放,哪怕同步任务报错也不会出现死锁
- 拿锁失败后会等待1秒重试,最多重试10次,总等待时长可以根据实际并发量调整
方案2:控制节点本地锁(适合所有playbook从同一控制节点发起的场景)
如果所有环境的playbook都在同一台/同一组控制节点上执行,可以直接用Ansible自带的lock模块在控制层面做互斥,写法更简单:
- hosts: xpto gather_facts: no tasks: - name: 申请全局文件更新锁 ansible.builtin.lock: lockfile: /tmp/ansible_filex_update.lock timeout: 300 - name: 更新filex.dat ansible.posix.synchronize: src: "some/path1/v" dest: "some/path2/filex.dat"
lock模块会在控制节点上维护锁文件,同一时间只会放行一个更新操作,其余操作会自动阻塞等待,任务执行完成后自动释放锁。如果playbook是从不同的控制节点发起的,这个方案不生效,必须用方案1。
注意事项
- 不要直接对业务文件
filex.dat加锁,单独使用独立的.lock后缀锁文件,避免损坏业务数据 - 不要用固定时长的sleep等待,无法准确预估等待时间,效率极低;flock是内核级的锁唤醒机制,锁释放后等待的进程会立刻被调度,没有额外轮询开销
- 锁等待超时、重试次数可以根据单文件同步的平均耗时调整,一般单文件rsync耗时在秒级,设置5分钟以内的总等待时间足够覆盖极端并发场景
内容的提问来源于stack exchange,提问作者fr0zt
相关产品推荐
相关产品推荐

