高serial值并行执行Ansible Playbook时HTML报告丢失主机条目
问题描述
在约800台主机上运行Ansible Playbook生成包含主机名的HTML报告,期望全主机并行执行:
- 当
serial设为500或更高值时,HTML报告中的主机条目数量少于预期(如仅显示500台) - 当
serial设为1时,报告能包含所有主机,但串行执行效率过低,不符合需求
简化后的Playbook如下:
- name: Tail Logs and Generate Report hosts: all gather_facts: no ignore_unreachable: yes serial: 500 # 高值时出现问题 tasks: - name: create header html lineinfile: path: /reports/daily_report.html line: "<!DOCTYPE html> <html> <head> <style>table{border-collapse:collapse;width:100%}td,th{border:1px solid #000;padding:8px;text-align:left}</style><title>Daily Report Details</title> </head> <body> <h1>Daily Report Details</h1> <table> <thead> <tr> <th>PG Host Name</th> </tr> </thead> <tbody>" create: yes delegate_to: localhost run_once: true ignore_errors: yes - name: Write content in report lineinfile: path: /reports/daily_report.html line: "<tr> <td> {{ inventory_hostname }}</td> </tr>" insertafter: EOF delegate_to: localhost ignore_errors: yes no_log: true - name: create footer html lineinfile: path: /reports/daily_report.html line: "</tbody> </table> </body> </html>" delegate_to: localhost ignore_errors: yes run_once: true
问题原因分析
run_once的批次执行特性:run_once: true的任务会在每个serial批次结束时执行一次。当serial=500,800台主机会被分成两个批次(500台+300台),第一个批次执行完后,create footer html任务就会写入HTML闭合标签,导致第二个批次的主机条目被追加到</html>之后,浏览器无法解析这部分内容,看起来像是丢失了后续主机。- 并发写文件的竞态条件:多个并行执行的主机同时通过
lineinfile向同一个文件追加内容时,lineinfile并非原子操作,多个进程同时读写会导致部分写入操作被覆盖或丢失,进一步加剧条目缺失的问题。
解决方案
改用先收集所有主机数据,再一次性生成完整报告的方式,彻底避免并发写文件和批次执行的问题:
步骤1:编写Playbook收集主机信息
- name: Collect Host Data for Report hosts: all gather_facts: no ignore_unreachable: yes serial: 800 # 支持全量并行 vars: report_host_list: [] tasks: - name: Add reachable host to report list set_fact: report_host_list: "{{ report_host_list + [inventory_hostname] }}" delegate_to: localhost run_once: false - name: Generate Final HTML Report hosts: localhost gather_facts: no tasks: - name: Render HTML from template template: src: report_template.j2 dest: /reports/daily_report.html mode: 0644
步骤2:编写Jinja2模板(report_template.j2)
<!DOCTYPE html> <html> <head> <style> table{border-collapse:collapse;width:100%} td,th{border:1px solid #000;padding:8px;text-align:left} </style> <title>Daily Report Details</title> </head> <body> <h1>Daily Report Details</h1> <table> <thead> <tr> <th>PG Host Name</th> </tr> </thead> <tbody> {% for host in hostvars['all'].report_host_list %} <tr> <td>{{ host }}</td> </tr> {% endfor %} </tbody> </table> </body> </html>
高
serial值使用最佳实践 - 避免并发写同一文件:不要让并行任务直接写入本地共享文件,优先采用「收集数据+批量生成」的模式,从根源避免竞态条件。
- 明确
run_once的行为:run_once: true会在每个serial批次中执行一次,而非整个Playbook仅执行一次,设计任务时要考虑批次划分的影响。 - 优先用模板生成静态文件:Jinja2模板能一次性生成完整的HTML结构,既保证格式正确,又避免多次文件IO的问题。
- 测试批次逻辑:在正式环境前,用小批量主机测试高
serial值下的任务执行顺序,验证批次划分是否符合预期。
内容的提问来源于stack exchange,提问作者Glitch07
相关产品推荐
相关产品推荐

