Ansible脚本实现GitLab Runner升级前等待无运行作业的问题
解决Ansible中GitLab Runner运行中作业检查的循环等待问题
问题场景
编写Ansible Playbook升级本地GitLab Runner,流程为:暂停虚拟机上的Runner→确认无运行中作业(通过GitLab API)→补丁/升级→恢复Runner。暂停和恢复步骤已实现,但作业检查环节触发报错。
原代码及报错
尝试用以下代码循环检查Runner的运行中作业:
- name: Wait for No Running Jobs uri: url: "{{ api_base_url }}/runners/{{ item.id }}/jobs?status=running" headers: PRIVATE-TOKEN: "{{ PRIVATE_TOKEN }}" register: job_list with_items: "{{ host_runner_ids.json }}" until: job_list.results | json_query('[*].json[*]') | first | length == 0
持续触发错误:
The error was: Unexpected templating type error occurred on ({% if job_list.results | json_query('[].json[]') | first | length <= 20 %} True {% else %} False {% endif %}): 'NoneType' object is not iterable.
移除until条件后,通过debug任务可正常获取每个Runner的运行作业数:
- name: debug job list debug: msg: "{{ job_list.results | json_query('[*].json[*]') | first | length }}" with_items: "{{ host_runner_ids.json }}"
错误原因
- 首次执行时变量未初始化:任务第一次运行时,
job_list还未被注册,job_list.results为None,此时用json_query处理会触发NoneType不可迭代的错误。 - 逻辑覆盖不全:原
until条件仅检查第一个Runner的作业数,未验证所有Runner均无运行作业,不符合升级前的安全要求。
解决方案
方案1:逐个等待每个Runner无作业(推荐,逻辑清晰)
针对每个Runner单独发起API请求并等待,避免批量注册变量的初始化问题:
- name: Wait for each Runner to finish all running jobs uri: url: "{{ api_base_url }}/runners/{{ item }}/jobs?status=running" headers: PRIVATE-TOKEN: "{{ PRIVATE_TOKEN }}" register: single_runner_jobs until: single_runner_jobs.json | length == 0 retries: 30 # 根据作业平均时长调整重试次数 delay: 60 # 每次检查间隔60秒 loop: "{{ host_runner_ids.json | map(attribute='id') | list }}"
- 用
loop替代with_items(Ansible官方推荐),先提取所有Runner的ID列表 - 每个循环单独注册
single_runner_jobs,首次执行时即可正常获取API返回结果 - 直接检查当前Runner的作业列表长度,直到为0再进入下一个Runner的检查
方案2:批量检查所有Runner(处理初始化问题)
如果需要批量获取所有Runner状态,需先判断变量是否存在,再验证全部Runner无作业:
- name: Wait for all Runners to have no running jobs uri: url: "{{ api_base_url }}/runners/{{ item.id }}/jobs?status=running" headers: PRIVATE-TOKEN: "{{ PRIVATE_TOKEN }}" register: job_list loop: "{{ host_runner_ids.json }}" until: > job_list is defined and job_list.results is defined and job_list.results | json_query('[*].json | length(@)') | max == 0 retries: 30 delay: 60
- 先通过
job_list is defined和job_list.results is defined避免首次执行的None错误 - 用
json_query('[*].json | length(@)')提取每个Runner的作业数量列表,再取最大值,当最大值为0时说明所有Runner均无运行作业
注意事项
- 根据实际环境中作业的平均运行时长,调整
retries和delay参数,确保等待时间充足 - 如果GitLab API返回的作业列表嵌套在
json.jobs字段下,需修改json_query路径为[*].json.jobs | length(@)
内容的提问来源于stack exchange,提问作者JScott
相关产品推荐
相关产品推荐

