Ansible 中的 Puppet hiera 等价物
Puppet hiera equivalent in Ansible
hiera.yaml
---
:hierarchy:
- node/%{host_fqdn}
- site_config/%{host_site_name}
- site_config/perf_%{host_performance_class}
- site_config/%{host_type}_v%{host_type_version}
- site/%{host_site_name}
- environments/%{site_environment}
- types/%{host_type}_v%{host_type_version}
- hosts
- sites
- users
- common
# options are native, deep, deeper
:merge_behavior: deeper
我们目前有这个 hiera 配置。因此,配置按以下顺序合并 common.yaml > users.yaml > sites.yaml > hosts.yaml > types/xxx_vxxx.yaml > 等。对于可变顶层层次结构,它仅当该文件存在时才会被覆盖。
例如:
common.yaml
server:
instance_type: m3.medium
site_config/mysite.yaml
server:
instance_type: m4.large
因此对于所有其他站点,实例类型将为 m3.medium,但仅对于 mysite 为 m4.large。
如何在 Ansible 中实现相同的功能?
我对 Puppet 不熟悉,所以这可能不是直接映射。但我理解你的问题是 "how do I use values in one shared location but override their definitions for different servers?"。在 Ansible 中,您可以使用 variables.
你可以define variables directly in your inventory. You can define variables in host- and group-specific files. You can define variables at a playbook level. You can define variables at a role level. Heck, you can even define variables with command-line switches.
在所有这些地方之间,您应该能够定义覆盖以适合您的情况。您可能需要查看 the documentation section on how to decide where to define a variable 了解更多信息。
我认为@Xiong 是正确的,你应该在 Ansible 中采用变量方式。
您可以使用从一般到特定的变量优先级设置灵活的清单。
但如果有帮助,您可以试试这个片段:
---
- hosts: loc-test
tasks:
- include_vars: hiera/{{ item }}
with_items:
- common.yml
- "node/{{ ansible_fqdn }}/users.yml"
- "node/{{ ansible_fqdn }}/sites.yml"
- "node/{{ ansible_fqdn }}/types/{{ host_type }}_v{{ host_type_version }}.yml"
failed_when: false
- debug: var=server
这将尝试从结构与您的问题相似的文件中加载变量。
不存在的文件将被忽略(因为 failed_when: false
)。
文件按此列表的顺序加载(从上到下),覆盖以前的值。
陷阱:
您在列表中使用的所有变量都必须定义(例如本例中的 host_type
不能在 common.yml
中定义),因为要迭代的项目列表在执行整个循环之前进行模板化(请参阅解决方法的更新)。
Ansible 默认覆盖(替换)指令,我猜你的用例需要合并行为。这可以通过 hash_behavior 设置来实现——但这对于 Ansible 剧本来说是不常见的。
P.S. 您可以通过将 with_items
更改为 with_first_found
并反转列表来改变从上到下的合并行为(从具体到一般)。在这种情况下,Ansible 将从找到的第一个文件加载变量。
更新: 使用先前包含在文件路径中的变量。
您可以将循环拆分为多个任务,这样 Ansible 将在模板化下一个文件的包含路径之前评估每个任务的结果。
制作 hiera_inc.yml
:
- include_vars: hiera/common.yml
failed_when: false
- include_vars: hiera/node/{{ ansible_fqdn }}/users.yml
failed_when: false
- include_vars: hiera/node/{{ ansible_fqdn }}/sites.yml
failed_when: false
- include_vars: hiera/node/{{ ansible_fqdn }}/types/{{ host_type | default('none') }}_v{{ host_type_version | default('none') }}.yml
failed_when: false
在你的主要剧本中:
- include: hiera_inc.yml
这看起来有点笨拙,但这样您就可以在 common.yaml
中定义 host_type
,它将在下一个任务的路径模板中得到尊重。
使用 Ansible 2.2 可以将 include_vars
转换为命名变量(不是全局主机 space),因此您可以将 include_vars 转换为 hiera_facts
并使用 combine
过滤以在不改变全局哈希行为的情况下合并它们。
它看起来比 Hiera 更基础一些,但有人已经创建了一个具有类似语法的基本 ansible 查找插件
https://github.com/sailthru/ansible-oss/tree/master/tools/echelon
hiera.yaml
---
:hierarchy:
- node/%{host_fqdn}
- site_config/%{host_site_name}
- site_config/perf_%{host_performance_class}
- site_config/%{host_type}_v%{host_type_version}
- site/%{host_site_name}
- environments/%{site_environment}
- types/%{host_type}_v%{host_type_version}
- hosts
- sites
- users
- common
# options are native, deep, deeper
:merge_behavior: deeper
我们目前有这个 hiera 配置。因此,配置按以下顺序合并 common.yaml > users.yaml > sites.yaml > hosts.yaml > types/xxx_vxxx.yaml > 等。对于可变顶层层次结构,它仅当该文件存在时才会被覆盖。
例如: common.yaml
server:
instance_type: m3.medium
site_config/mysite.yaml
server:
instance_type: m4.large
因此对于所有其他站点,实例类型将为 m3.medium,但仅对于 mysite 为 m4.large。
如何在 Ansible 中实现相同的功能?
我对 Puppet 不熟悉,所以这可能不是直接映射。但我理解你的问题是 "how do I use values in one shared location but override their definitions for different servers?"。在 Ansible 中,您可以使用 variables.
你可以define variables directly in your inventory. You can define variables in host- and group-specific files. You can define variables at a playbook level. You can define variables at a role level. Heck, you can even define variables with command-line switches.
在所有这些地方之间,您应该能够定义覆盖以适合您的情况。您可能需要查看 the documentation section on how to decide where to define a variable 了解更多信息。
我认为@Xiong 是正确的,你应该在 Ansible 中采用变量方式。
您可以使用从一般到特定的变量优先级设置灵活的清单。
但如果有帮助,您可以试试这个片段:
---
- hosts: loc-test
tasks:
- include_vars: hiera/{{ item }}
with_items:
- common.yml
- "node/{{ ansible_fqdn }}/users.yml"
- "node/{{ ansible_fqdn }}/sites.yml"
- "node/{{ ansible_fqdn }}/types/{{ host_type }}_v{{ host_type_version }}.yml"
failed_when: false
- debug: var=server
这将尝试从结构与您的问题相似的文件中加载变量。
不存在的文件将被忽略(因为 failed_when: false
)。
文件按此列表的顺序加载(从上到下),覆盖以前的值。
陷阱:
您在列表中使用的所有变量都必须定义(例如本例中的
host_type
不能在common.yml
中定义),因为要迭代的项目列表在执行整个循环之前进行模板化(请参阅解决方法的更新)。Ansible 默认覆盖(替换)指令,我猜你的用例需要合并行为。这可以通过 hash_behavior 设置来实现——但这对于 Ansible 剧本来说是不常见的。
P.S. 您可以通过将 with_items
更改为 with_first_found
并反转列表来改变从上到下的合并行为(从具体到一般)。在这种情况下,Ansible 将从找到的第一个文件加载变量。
更新: 使用先前包含在文件路径中的变量。
您可以将循环拆分为多个任务,这样 Ansible 将在模板化下一个文件的包含路径之前评估每个任务的结果。
制作 hiera_inc.yml
:
- include_vars: hiera/common.yml
failed_when: false
- include_vars: hiera/node/{{ ansible_fqdn }}/users.yml
failed_when: false
- include_vars: hiera/node/{{ ansible_fqdn }}/sites.yml
failed_when: false
- include_vars: hiera/node/{{ ansible_fqdn }}/types/{{ host_type | default('none') }}_v{{ host_type_version | default('none') }}.yml
failed_when: false
在你的主要剧本中:
- include: hiera_inc.yml
这看起来有点笨拙,但这样您就可以在 common.yaml
中定义 host_type
,它将在下一个任务的路径模板中得到尊重。
使用 Ansible 2.2 可以将 include_vars
转换为命名变量(不是全局主机 space),因此您可以将 include_vars 转换为 hiera_facts
并使用 combine
过滤以在不改变全局哈希行为的情况下合并它们。
它看起来比 Hiera 更基础一些,但有人已经创建了一个具有类似语法的基本 ansible 查找插件
https://github.com/sailthru/ansible-oss/tree/master/tools/echelon