等待要有明确终点
变更之后常常需要“等它真的好了再继续”:服务进程起来了但端口还没 listen、数据库刚开始恢复、证书文件还没生成。Playbook 里等待有两条路:一是用专用等待模块让目标机自己轮询条件;二是对任务加重复尝试与间隔,由控制节点在每次尝试后重新判断。两者都要有超时与失败判定,否则等待就变成无限挂起。
基本信息
| 专用等待模块 | 等待端口、路径、连接可用等条件,在目标机侧轮询 |
|---|---|
| 重复尝试 | 任务级 retries 与 delay 控制尝试次数与间隔,until 定义成功条件 |
| 超时 | 等待类模块自带超时参数,需按业务实际耗时设置,而不是默认值 |
| 连接就绪 | 重启类操作后需要先等连接恢复,再继续后续任务 |
| 判定依据 | 用状态字段而非文本片段判定成功,例如端口可连或返回码为 0 |
| 幂等要求 | 轮询类任务不应产生变更,需显式声明无变更 |
典型使用场景
- 重启服务后等端口就绪,再执行健康检查,而不是立刻探测
- 重启主机后等连接恢复,避免后续任务因不可达而失败
- 数据库初始化后等数据目录出现标志文件再继续
- 集群成员逐个就绪:等每个节点上线后再推进下一个
- 等待外部依赖(对象存储、注册中心)可用后继续部署
- 等待长时间任务的产物出现,配合产物校验一起判定成功
两种等待写法
两种等待写法
- name: 重启应用
ansible.builtin.service:
name: "{{ svc_name }}"
state: restarted
notify: 等待端口就绪
# 写法一:等待类模块,在目标机侧轮询条件
- name: 等端口可连再继续
ansible.builtin.wait_for:
host: "{{ ansible_host | default('127.0.0.1') }}"
port: "{{ app_port }}"
delay: 3 # 先给进程一点启动时间
timeout: 120 # 总超时,按业务实际耗时设定
state: started
# 写法二:重复尝试,由控制节点判断成功条件
- name: 轮询健康检查直到通过
ansible.builtin.uri:
url: "http://127.0.0.1:{{ app_port }}/healthz"
status_code: 200
register: health
until: health.status == 200
retries: 12
delay: 5
changed_when: false # 只读探测,不产生变更
- name: 重启主机后等连接恢复
ansible.builtin.wait_for_connection:
delay: 10
timeout: 300等待参数怎么定
| 参数 | 作用 | 取值思路 |
|---|---|---|
| timeout | 总等待上限 | 按最慢一次变更的实测耗时留出余量,不要用很大的固定值 |
| delay | 首次检查前的等待 | 给进程或服务留出启动缓冲,避免立刻失败 |
| retries 与 delay | 尝试次数与间隔 | 两者相乘即为最长等待,需与超时口径一致 |
| 判定条件 | 什么算成功 | 用状态字段与返回码,避免匹配易变的文本 |
| 无变更声明 | 轮询不算变更 | 只读探测类任务显式声明 changed_when: false |
等端口不如等业务就绪:端口能连不代表服务能提供服务(连接池未建好、迁移未完成)。有健康检查接口时,优先探测它并校验返回内容,而不是只探测端口。
把超时设得过大,会让真正的故障以“等待超时失败”的形式晚很多才被发现;设得过小,则会在正常但较慢的环境里误判失败。参数应当来自实测数据,并允许按环境用变量覆盖。
失败后怎么办
等待失败意味着“预期状态没有出现”,此时应当直接中止后续变更,而不是继续往下推。把等待放在需要它的那一步之前,并让其失败消息说明探测目标与已等待时长,接警人才能第一时间判断是启动慢还是根本没起来。