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

Ansible因API触发客户端重启丢失连接的解决方案咨询

Ansible 非Play触发服务器重启导致Playbook执行失败的解决方案

问题背景

  • 编写了用于新服务器部署的Ansible Role,Play执行时会触发Python脚本setup.py,该脚本会将服务器信息上报至内部API,最终由API侧触发任务对目标服务器执行重启操作
  • 按逻辑Play需要等待Python脚本执行完成后才会结束,但重启操作并非由Play自身发起,重启过程中Ansible会丢失连接,最终导致Playbook执行失败
  • 手动在远程主机直接执行setup.py运行完全正常,不存在脚本本身逻辑问题;后续所有部署步骤必须在该脚本执行完成(即服务器重启完成)后才能运行
  • 当前使用版本为ansible-core 2.12,需要可行的连接保持/恢复方案,尽可能减少无效等待,提升执行效率

已尝试的无效方案

  1. 异步执行加固定轮询方式运行脚本
    配置如下:

    - name: Run setup.py
      command: "{{ run_setup_py }} --username {{ username }} --password {{ password }} --ip {{ ansible_host }} --hostname {{ host_name }}"
      async: 1800
      poll: 60
    

    问题:async超时后直接报错,Ansible无法识别脚本已完成的状态,执行失败。

  2. fire-and-forget模式异步执行脚本,通过async_status轮询任务状态
    配置如下:

    - name: Run setup.py
      command: "{{ run_setup_py }} --username {{ username }} --password {{ password }} --ip {{ ansible_host }} --hostname {{ host_name }}"
      async: 600
      poll: 0
      register: run_setup
    - name: check on async task
      async_status:
        jid: "{{ run_setup.ansible_job_id }}"
      register: job_result
      until: job_result.finished
      retries: 1000
      delay: 450
    

    问题:同样执行失败。

  3. 通过wait_for_connection模块等待远程主机恢复连接,分别测试了委托到localhost执行和直接在目标主机执行两种写法
    委托本地执行配置:

    - name: Wait until remote system is reachable
      wait_for_connection:
        delay: 180
        sleep: 15
      delegate_to: localhost
    

    目标主机直接执行配置:

    - name: Wait until remote system is reachable
      wait_for_connection:
        delay: 180
        sleep: 15
    

    问题:在Play层级配置wait_for_connection会直接跳过Python脚本的执行步骤,导致后续Play运行失败。

  4. Playbook层级添加ignore_unreachable: yes配置
    问题:Ansible会在服务器断开后立即尝试重连,此时服务器仍处于POST自检阶段,重连必然失败。


可行落地方案

核心失败原因

之前所有方案失效的根本原因是:无论异步执行还是async_status轮询,所有任务状态查询都依赖和目标主机的SSH连接,异步任务的状态临时文件也存储在目标主机本地。当API侧触发重启后,SSH连接立刻断开,目标主机重启过程中临时文件也会被清理,Ansible既连不上主机,也读不到任务状态,自然会判定执行失败。

解决思路

把整个流程拆成三个完全独立的执行阶段,彻底规避重启过程中对目标主机连接的依赖:

  1. 启动阶段:在目标主机上以脱离SSH会话的方式后台启动setup.py,确保SSH断开后脚本不会被SIGHUP信号终止,命令发送成功后立刻结束当前Play,不等待脚本返回
  2. 探测阶段:从控制节点本地轮询检测目标主机SSH端口状态,先预留固定等待时间避开服务器POST自检阶段,减少无效重连
  3. 恢复阶段:确认SSH端口开放后,再等待Ansible执行连接完全恢复,之后运行后续所有部署步骤

具体配置

第一部分:独立Play负责启动setup.py

- name: 触发setup.py上报流程
  hosts: target_servers
  gather_facts: no
  tasks:
    - name: 脱离SSH会话后台运行setup.py,避免断连导致脚本被终止
      shell: |
        nohup {{ run_setup_py }} --username {{ username }} --password {{ password }} --ip {{ ansible_host }} --hostname {{ host_name }} > /tmp/setup_run.log 2>&1 &
      async: 10
      poll: 0
      register: setup_launch
      ignore_unreachable: yes

    - name: 等待2秒确认脚本启动指令下发成功
      pause:
        seconds: 2

这里用nohup加后台运行的方式,让脚本完全脱离Ansible的SSH会话生命周期,就算SSH立刻断开,脚本也会继续跑完上报流程,不会被系统杀掉。async设置为10秒、poll为0,让Ansible只负责把启动命令发出去,完全不等待脚本执行结果,避免卡在这里等重启断连报错。setup.py的运行日志会存在目标主机/tmp/setup_run.log,排查问题时可直接查看。

第二部分:独立Play从控制节点探测SSH端口

- name: 等待服务器完成重启流程
  hosts: localhost
  gather_facts: no
  tasks:
    - name: 固定等待3分钟,避开服务器POST自检阶段,避免无效重连
      pause:
        seconds: 180

    - name: 轮询检测目标主机SSH端口是否开放
      wait_for:
        host: "{{ hostvars[item].ansible_host }}"
        port: "{{ hostvars[item].ansible_port | default(22) }}"
        state: started
        timeout: 1800
        sleep: 10
      loop: "{{ groups['target_servers'] }}"

这一步完全在控制节点本地执行,不需要连接目标主机,wait_for每隔10秒检测一次目标的SSH端口,直到端口通或者超时(默认设置30分钟,可根据物理机/虚拟机实际重启时长调整),比直接用wait_for_connection发起早连的效率高很多。

第三部分:独立Play执行后续部署

- name: 服务器重启完成后执行部署任务
  hosts: target_servers
  gather_facts: yes
  tasks:
    - name: 等待Ansible可正常在目标主机执行操作
      wait_for_connection:
        delay: 10
        sleep: 5
        timeout: 300

    # 原有所有后续部署任务写在此处即可

方案优势

  • 完全规避重启过程中Ansible连接断开导致的任务失败问题,不需要依赖目标主机上的异步任务状态
  • 先固定等待时间避开POST自检阶段,再用低频率轮询检测端口,比持续重连的方案减少90%以上的无效请求
  • 脚本启动、端口探测、后续部署三个阶段完全解耦,问题排查路径清晰
  • 完全兼容ansible-core 2.12版本,不需要额外安装第三方集合或模块

内容的提问来源于stack exchange,提问作者connor_h

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.22 16:12:26