Ansible playbook中lineinfile模块写入日志内容丢失问题
问题产生原因
- Ansible默认启用多主机并行执行(默认并发数为5),你的两个受控主机会同时执行delegate到localhost的lineinfile写文件任务,触发竞态条件。
- lineinfile模块的工作逻辑是:先将目标文件完整读取到内存中,完成内容修改后再整体覆盖写入原文件。当多个进程同时操作同一个文件时,进程A读取文件后,进程B写入了新内容,进程A后续写入自己的修改结果时会直接覆盖进程B的写入内容,最终表现为随机丢失条目、写入结果不稳定。
解决方案
提供三种可选方案,按推荐优先级排序:
方案1:统一收集结果后单次写入(最优,无并发问题、性能最好)
先在所有受控主机上采集命令结果,最后仅在控制端执行一次写入任务,一次性把所有主机的结果写入日志文件,完全避免并发冲突:
--- - name: 采集主机信息 hosts: all tasks: - name: 采集date命令结果 command: date register: date_output - name: 采集hostname命令结果 command: hostname register: hostname_output - name: 统一写入日志到控制端 hosts: localhost tasks: - name: 生成所有主机的日志内容 set_fact: log_content: | {% for host in groups['all'] %} {{ hostvars[host].inventory_hostname }} date ====================== {{ hostvars[host].date_output.stdout }} {{ hostvars[host].inventory_hostname }} hostname ====================== {{ hostvars[host].hostname_output.stdout }} {% endfor %} - name: 写入到日志文件 copy: content: "{{ log_content }}" path: /home/ubuntu/list.log create: yes
方案2:调整play并发数为1(最简单,改动最小)
在play配置中添加serial: 1参数,强制同一时间只有一个主机执行任务,避免并发写冲突:
--- - name: 采集主机信息并写入日志 hosts: all serial: 1 # 核心改动,关闭并行执行 tasks: - command: date register: date_output - command: hostname register: hostname_output - lineinfile: line: "{{inventory_hostname}} {{ item.cmd | join(' ') }}\n======================\n{{ item.stdout }}" path: /home/ubuntu/list.log create: yes with_items: - "{{ date_output }}" - "{{ hostname_output }}" delegate_to: localhost
额外优化:添加item.cmd | join(' ')可以把命令列表转为字符串,避免输出['date']这种格式。
方案3:分临时文件写入后合并
如果需要保持多主机并行执行性能,可以先让每个主机写入自己的临时文件,所有任务完成后再合并临时文件到最终日志:
--- - name: 采集主机信息并写入临时文件 hosts: all tasks: - command: date register: date_output - command: hostname register: hostname_output - lineinfile: line: "{{inventory_hostname}} {{ item.cmd | join(' ') }}\n======================\n{{ item.stdout }}" path: "/tmp/{{ inventory_hostname }}_tmp.log" create: yes with_items: - "{{ date_output }}" - "{{ hostname_output }}" delegate_to: localhost - name: 合并临时文件到最终日志 hosts: localhost tasks: - name: 合并所有临时日志 shell: cat /tmp/*_tmp.log > /home/ubuntu/list.log && rm -f /tmp/*_tmp.log
内容的提问来源于stack exchange,提问作者Bindu G
相关产品推荐
相关产品推荐

