You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

高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
问题原因分析
  1. run_once的批次执行特性:run_once: true的任务会在每个serial批次结束时执行一次。当serial=500,800台主机会被分成两个批次(500台+300台),第一个批次执行完后,create footer html任务就会写入HTML闭合标签,导致第二个批次的主机条目被追加到</html>之后,浏览器无法解析这部分内容,看起来像是丢失了后续主机。
  2. 并发写文件的竞态条件:多个并行执行的主机同时通过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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 03:21:04