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
相关产品推荐
相关产品推荐

