如何将set_fact模块设置的变量传递给Jinja2模板?
Ansible部署NATS集群:主节点IP传递问题
问题背景
我用Ansible角色部署NATS集群,通过host_vars定义主节点:
is_master: true
在setup-nats.yml任务中尝试提取主节点IP并传给Jinja2模板,但模板提示variable 'master_ip' is undefined。任务代码如下:
- name: Set master IP set_fact: set_master_ip: "{{ ansible_facts['default_ipv4']['address'] }}" cacheable: yes when: is_master - name: debug debug: msg: "{{ set_master_ip }}" run_once: true - name: generate nats-server.conf for the slave nodes template: src: nats-server-slave.conf.j2 dest: /etc/nats-server.conf owner: nats group: nats mode: 0644 when: - is_master == false vars: master_ip: "{{ set_master_ip }}" notify: nats-server
Jinja2模板中的变量使用:
routes = [ nats-route://ruser:{{ nats_server_password }}@{{ master_ip }}:6222 ] }
用Molecule测试时,debug任务能正常显示主节点IP,但模板任务报错:
TASK [nats : Set master IP] **************************************************** ok: [target1] skipping: [target2] skipping: [target3] TASK [nats : debug] ************************************************************ ok: [target1] => msg: 10.0.2.15 TASK [nats : generate nats-server.conf for the slave nodes] ******************** skipping: [target1] An exception occurred during task execution. To see the full traceback, use -vvv. The error was: ansible.errors.AnsibleUndefinedVariable: {{ set_master_ip }}: 'set_master_ip' is undefined fatal: [target2]: FAILED! => changed=false msg: 'AnsibleUndefinedVariable: {{ set_master_ip }}: ''set_master_ip'' is undefined' An exception occurred during task execution. To see the full traceback, use -vvv. The error was: ansible.errors.AnsibleUndefinedVariable: {{ set_master_ip }}: 'set_master_ip' is undefined fatal: [target3]: FAILED! => changed=false msg: 'AnsibleUndefinedVariable: {{ set_master_ip }}: ''set_master_ip'' is undefined'
我怀疑问题和变量的主机上下文作用域有关,但不知道怎么解决,想请教两个问题:
- 上述实现是否符合Ansible最佳实践?
- 如何正确将主节点IP传递给Jinja2模板?
问题解答
1. 现有实现是否符合最佳实践?
不符合。核心问题是对set_fact的作用域理解有误:
set_fact默认仅在当前执行任务的主机上下文内创建变量,即便添加cacheable: yes,也只是让变量可在后续play中复用,其他主机无法直接访问该变量。run_once: true的debug任务能显示IP,是因为它在主节点上执行,自然能读取主节点的变量;但从节点(target2、target3)从未执行过Set master IP任务,其上下文里没有set_master_ip变量,导致模板任务报错。- 额外问题:变量命名
set_master_ip不够直观,建议改用master_node_ip这类清晰名称。
2. 正确传递主节点IP的方法
有两种可靠的常用方案:
方案一:通过groups和hostvars跨主机引用变量
直接在从节点任务中,通过主机组筛选主节点并引用其IP:
- name: generate nats-server.conf for the slave nodes template: src: nats-server-slave.conf.j2 dest: /etc/nats-server.conf owner: nats group: nats mode: 0644 when: not is_master vars: master_ip: "{{ hostvars[groups['nats'] | selectattr('is_master', 'equalto', true) | first]['ansible_default_ipv4']['address'] }}" notify: nats-server
说明:假设NATS集群主机组名为
nats,通过selectattr筛选出标记为is_master: true的主节点,再通过hostvars获取其IP。
方案二:先收集主节点IP,再全局共享变量
通过delegate_to强制在主节点执行变量设置,确保所有主机都能访问:
- name: Collect master node IP set_fact: master_node_ip: "{{ ansible_default_ipv4.address }}" delegate_to: "{{ groups['nats'] | selectattr('is_master', 'equalto', true) | first }}" run_once: true cacheable: yes - name: generate nats-server.conf for the slave nodes template: src: nats-server-slave.conf.j2 dest: /etc/nats-server.conf owner: nats group: nats mode: 0644 when: not is_master vars: master_ip: "{{ master_node_ip }}" notify: nats-server
说明:
delegate_to指定任务在主节点执行,run_once: true确保只执行一次,cacheable: yes让变量在所有主机上下文内可用。
额外优化建议
- 条件判断用
not is_master替代is_master == false,更符合Ansible语法习惯。 - 删除模板末尾多余的
},避免NATS配置文件语法错误。
内容的提问来源于stack exchange,提问作者Sinux
相关产品推荐
相关产品推荐

