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

Jinja2模板中使用列表型Ansible Fact报错,求更优实现方案

问题:Ansible Fact列表在Jinja2模板中直接引用报错

场景重现

我用以下任务定义了一个Ansible Fact:

- name: Set server ips fact
  ansible.builtin.set_fact:
    k3s_server_ips: "{{ k3s_server_hosts | map('extract', hostvars, ['ansible_default_ipv4', 'address']) }}"
  when: ansible_host in k3s_server_hosts

生成的k3s_server_ips是列表类型,执行结果如下:

ok: [apollo] => changed=false
  ansible_facts:
    k3s_server_ips:
    - 192.168.4.2
    - 192.168.4.3
    - 192.168.4.4

但在Jinja2模板中直接引用这个Fact时:

kubeProxy:
  endpoints: '{{ k3s_server_ips }}'

出现了YAML语法错误:

Traceback (most recent call last):
  File "/usr/local/Cellar/ansible/9.4.0/libexec/lib/python3.12/site-packages/ansible/executor/task_executor.py", line 526, in _execute
    self._task.post_validate(templar=templar)
  File "/usr/local/Cellar/ansible/9.4.0/libexec/lib/python3.12/site-packages/ansible/playbook/task.py", line 290, in post_validate
    super(Task, self).post_validate(templar)
  File "/usr/local/Cellar/ansible/9.4.0/libexec/lib/python3.12/site-packages/ansible/playbook/base.py", line 543, in post_validate
    value = method(attribute, getattr(self, name), templar)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/Cellar/ansible/9.4.0/libexec/lib/python3.12/site-packages/ansible/playbook/task.py", line 298, in _post_validate_args
    args = templar.template(value)
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/Cellar/ansible/9.4.0/libexec/lib/python3.12/site-packages/ansible/template/__init__.py", line 791, in template
    d[k] = self.template(
           ^^^^^^^^^^^^^^
  File "/usr/local/Cellar/ansible/9.4.0/libexec/lib/python3.12/site-packages/ansible/template/__init__.py", line 764, in template
    result = self.do_template(
             ^^^^^^^^^^^^^^^^^
  File "/usr/local/Cellar/ansible/9.4.0/libexec/lib/python3.12/site-packages/ansible/template/__init__.py", line 1010, in do_template
    res = myenv.concat(rf)
          ^^^^^^^^^^^^^^^^
  File "/usr/local/Cellar/ansible/9.4.0/libexec/lib/python3.12/site-packages/ansible/template/native_helpers.py", line 43, in ansible_eval_concat
    head = list(islice(nodes, 2))
           ^^^^^^^^^^^^^^^^^^^^^^
  File "<template>", line 18, in root
  File "/usr/local/Cellar/ansible/9.4.0/libexec/lib/python3.12/site-packages/ansible/template/__init__.py", line 295, in wrapper
    ret = func(*args, **kwargs)
          ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/Cellar/ansible/9.4.0/libexec/lib/python3.12/site-packages/ansible/plugins/filter/core.py", line 221, in from_yaml
    return yaml_load(text_type(to_text(data, errors='surrogate_or_strict')))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/Cellar/ansible/9.4.0/libexec/lib/python3.12/site-packages/yaml/__init__.py", line 81, in load
    return loader.get_single_data()
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/Cellar/ansible/9.4.0/libexec/lib/python3.12/site-packages/yaml/constructor.py", line 49, in get_single_data
    node = self.get_single_node()
           ^^^^^^^^^^^^^^^^^^^^^^
  File "yaml/_yaml.pyx", line 673, in yaml._yaml.CParser.get_single_node
  File "yaml/_yaml.pyx", line 687, in yaml._yaml.CParser._compose_document
  File "yaml/_yaml.pyx", line 731, in yaml._yaml.CParser._compose_node
  File "yaml/_yaml.pyx", line 845, in yaml._yaml.CParser._compose_mapping_node
  File "yaml/_yaml.pyx", line 731, in yaml._yaml.CParser._compose_node
  File "yaml/_yaml.pyx", line 847, in yaml._yaml.CParser._compose_mapping_node
  File "yaml/_yaml.pyx", line 860, in yaml._yaml.CParser._parse_next_event
yaml.scanner.ScannerError: mapping values are not allowed in this context
  in "<unicode string>", line 26, column 68
fatal: [apollo]: FAILED! => changed=false

目前临时解决方案是用循环遍历生成列表:

kubeProxy:
  endpoints:
{% for ip in k3s_server_ips %}
    - {{ ip }}
{% endfor %}

想知道有没有更优的实现方式。

错误原因

直接用单引号包裹列表变量时,Jinja2会把列表渲染成Python风格的字符串(比如['192.168.4.2', '192.168.4.3', '192.168.4.4']),而YAML解析器无法识别这种格式作为合法的列表值,从而抛出语法错误。

更优解决方案

方案1:使用Jinja2的to_yaml过滤器

直接将列表变量转换为合法的YAML格式,无需手动循环:

kubeProxy:
  endpoints: {{ k3s_server_ips | to_yaml }}

注意这里不要加单引号,因为to_yaml会直接输出符合YAML规范的列表格式。

方案2:直接引用变量(无引号)

在YAML模板中,如果变量本身就是列表类型,可以直接引用,不需要包裹引号:

kubeProxy:
  endpoints: {{ k3s_server_ips }}

Ansible的模板引擎会自动将列表渲染为正确的YAML列表格式。

方案3:提前转换为YAML字符串并存为Fact

如果需要在多个模板中复用,可以在任务阶段就把列表转换为YAML格式的字符串:

- name: Set server ips as YAML fact
  ansible.builtin.set_fact:
    k3s_server_ips_yaml: "{{ k3s_server_ips | to_yaml }}"

然后在模板中直接引用:

kubeProxy:
  endpoints: {{ k3s_server_ips_yaml }}

验证效果

使用上述方案后,模板会正确渲染出:

kubeProxy:
  endpoints:
  - 192.168.4.2
  - 192.168.4.3
  - 192.168.4.4

符合YAML语法规范,不会再触发解析错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 14:57:04