如何在Ansible的group_vars中合并字典变量且不使用hash_behaviour=merge?
hash_behaviour=merge) 我明白你现在的困扰——之前依赖的hash_behaviour = merge要被移除,直接在group_vars里用combine又触发递归循环,确实头疼。下面给你几个实用的方案,都是不需要在group_vars里搞复杂模板,还能实现多组字典自动合并的方法:
方案1:拆分变量+显式合并(适合组数量少的场景)
这个方法思路很简单:给每个组的端口片段起独立的变量名,然后在playbook里统一合并成最终的字典。
分别在各group_vars文件里定义独立的端口变量:
- group_vars/ftp_servers.yml:
important_ports_ftp: {ftp: 21} - group_vars/web_servers.yml:
important_ports_web: {http: 80} - 要是有通用端口,比如SSH,可以放在group_vars/all.yml:
important_ports_base: {ssh: 22}
- group_vars/ftp_servers.yml:
在playbook里用
set_fact一次性合并所有相关变量:- name: 合并所有组的重要端口配置 ansible.builtin.set_fact: important_ports: "{{ important_ports_base | combine(important_ports_ftp | default({})) | combine(important_ports_web | default({})) }}"这里用
default({})是为了避免主机不属于某个组时变量不存在报错,空字典不影响合并结果。
方案2:动态遍历组名合并(适合组数量多的场景)
如果你的主机组很多,一个个写combine太麻烦,可以利用group_names变量(存储当前主机所属的所有组名)来动态合并:
保持每个组的变量命名规则统一,比如
important_ports_<组名>:- group_vars/ftp_servers.yml:
important_ports_ftp: {ftp: 21} - group_vars/web_servers.yml:
important_ports_web: {http: 80} - group_vars/db_servers.yml:
important_ports_db: {mysql: 3306}
- group_vars/ftp_servers.yml:
在playbook里通过循环遍历所有组,合并对应的变量:
- name: 动态合并所有组的重要端口 ansible.builtin.set_fact: important_ports: "{{ important_ports | combine(lookup('vars', 'important_ports_' + item, default={})) }}" loop: "{{ group_names }}" vars: important_ports: {} # 初始化空字典作为合并起点这个方法会自动识别当前主机所属的所有组,把对应的端口片段全部合并进去,扩展性很强,新增组只需要按规则加变量就行。
为什么之前的方法会报错?
你在group_vars里写important_ports: "{{ important_ports | combine({http: 80}) }}"时,Ansible在渲染这个变量的过程中,需要先获取important_ports的值,但这个变量本身就是你当前正在定义的,相当于自己引用自己,直接触发了递归循环,所以不管有没有初始化空字典都会报错。这是Ansible模板渲染的机制问题,没法在group_vars层面直接解决,必须通过拆分变量+playbook合并的方式绕过。
内容的提问来源于stack exchange,提问作者EM0

