Ansible - 在 hosts.yml 和 group_vars 之间拆分 YAML 格式的变量列表

Ansible - splitting YAML-formatted list of vars across hosts.yml and group_vars

我的目录结构如下:

├── ansible.cfg
├── hosts.yml
├── playbook.yml
├── group_vars
|   ├── all.yml
│   └── vm_dns.yml
└── roles
    └── pihole
        ├── handlers
        │   └── main.yml
        └── tasks
            └── main.yml

ansible.cfg 我只是有:

[defaults]
inventory = ./hosts.yml

group_vars/all.yml 我有一些通用设置:

---
aptcachetime: 3600
locale: "en_GB.UTF-8"
timezone: "Europe/Paris"

然后在 hosts.yml 中我设置了我的 PiHole 虚拟机:

---
all:
  vars:
    ansible_python_interpreter: /usr/bin/python3
vm_dns:
  vars:
    dns_server: true
  hosts:
    vmb-dns:
      pihole:
        dns: 
          - "185.228.168.10"
          - "185.228.169.11"
        network:
          ipv4: "192.168.2.4/24"
          interface: eth0
    vmk-dns: 
      pihole:
        dns: 
          - "185.228.168.10"
          - "185.228.169.11"
        network:
          ipv4: "192.168.3.4/24"
          interface: eth0

此时,我没有尝试将任何变量移动到 group_vars,一切正常。
现在,我觉得可以通过将 all vm_dns 主机相同的设置分解为 group_vars 文件来使主机文件更具可读性。所以我从 hosts.yml 中删除了所有 dnsinterface 行,并将它们放在 group_vars/vm_dns.yml 文件,像这样:

---
pihole:
  dns: 
    - "185.228.168.10"
    - "185.228.169.11"
  network:
    interface: eth0

此时,hosts.yml因此包含:

---
all:
  vars:
    ansible_python_interpreter: /usr/bin/python3
vm_dns:
  vars:
    dns_server: true
  hosts:
    vmb-dns:
      pihole:
        network:
          ipv4: "192.168.2.4/24"
    vmk-dns: 
      pihole:
        network:
          ipv4: "192.168.3.4/24"

但是当我现在 运行 剧本时,一旦它尝试执行使用从 hosts.yml 移动到 group_vars/vm_dns.yml 的变量之一的任务,Ansible 失败并显示 AnsibleUndefinedVariable: dict object has no attribute ....

我不太确定我是否只是误解了“Ansible 方式”,或者我正在尝试做什么(本质上是将同一列表的不同部分分成 hostsgroup_vars,我想)不仅仅是可行的。我认为 "flattening" that Ansible does 应该可以处理这个问题,但似乎 Ansible 根本没有包含 group_vars/vm_dns.yml 中定义的变量。

我已阅读 the subject, and found some 上的文档,但发现 none 演示了跨主机使用的 YAML 格式列表,并且 group_vars 以这种方式使用。

编辑:与此问题实际相关的其他 SO 或 Github 问题

https://github.com/ansible/ansible/issues/58120
https://docs.ansible.com/ansible/latest/reference_appendices/config.html#default-hash-behaviour

由于您在主机级别的清单中保留了 pihole var 的定义,因此默认情况下这个变量会赢得游戏并取代之前在组级别的定义。参见variable precedence documentation。因此,如果您稍后尝试访问例如pihole.dnspihole.network.interface,映射不再存在,ansible 会触发上述错误。

这是 ansible 中的默认行为:按优先顺序用最新的变量替换先前的变量。但是您可以通过在 ansible.cfg.

中设置 hash_behaviour=merge 来更改字典的这种行为

我个人对此设置的实验并不十分令人满意:它在我自己的 playbooks/roles 中表现正确,但在包含第三方贡献(剧本片段,角色、自定义模块....)。所以我绝对不推荐它。此外,此配置已 deprecated in ansible 2.10 因此将在 ansible 2.14 中删除。如果你仍然想使用它,你应该将设置的范围限制得尽可能小,当然不要在全局级别设置它(即肯定不会在/etc/ansible/ansible.cfg

我现在全球使用的解决这类问题的方法:

  1. 为每个只包含特定信息的 host/group/whatever 定义变量。在你的情况下为你主持
    ---
    pihole_host:
      network:
        ipv4: "192.168.2.4/24"
    
  2. 在某处定义这些设置的默认值。在您的情况下,您的团队。
    ---
    pihole_defaults:
      dns: 
        - "185.228.168.10"
        - "185.228.169.11"
      network:
        interface: eth0
    
    (请注意,您可以利用上述变量的优先顺序在不同级别定义这些默认值)
  3. 在全局级别(我通常把它放在 group_vars/all.yml 中),定义你的 var,它将是 default 和 specific 的组合,确保它总是默认为空
    ---
    # Calculate pihole from group defaults and host specific
    pihole: >-
      {{
        (pihole_defaults | default({}))
        | combine((pihole_host | default({}), recursive=true)
      }}