Ansible. 重写单个字典键
我正在使用ansible来管理生产环境的配置,还有vagrant箱子。
我有一个包含默认值的文件:group_vars/all。
---
env: prod
wwwuser: www-data
db:
root_pwd: root_pwd
pdo_driver: pdo_mysql
host: localhost
name: test
user: test
pwd: test
charset: utf8
domain: somedomain
projectdir: /var/www/application
webrootdir: "{{ projectdir }}/web"
在host_vars/vagrantbox中,我想要有类似这样的内容:
db:
root_pwd: super_easy_password
但是这样做会完全覆盖掉数据库的字典,而我只想覆盖其中的一个键。
我该怎么做才能实现这个目标呢?
更新 1
我刚刚检查了ansible.cfg:
[defaults]
host_key_checking=false
hash_behaviour=merge
groups_vars/all
db:
root_pwd: some_strong_pwd
pdo_driver: pdo_mysql
host: localhost
name: dbname
user: dbuser
pwd: some password
charset: utf8
host_vars/vagrantbox
db:
root_pwd: root
我遇到了以下错误:
One or more undefined variables: 'dict object' has no attribute 'name'
我哪里做错了呢?
4 个回答
我刚刚用 ansible 1.9.3 试了一下,运行得很好。不太确定,但看起来你在 group_vars
这个文件夹的名字上写错了(应该是 group_vars
,而不是 groups_vars
)。
我发现最好的方法是把变量当作字典项的值,然后覆盖它。这样做可以让变量的优先级变得简单而强大,特别是在处理ansible的变量顺序时。
角色/父级/默认/main.yml
---
root_pw_value: ParentPassword
parent_dict:
- root_pw: "{{ root_pw_value }}"
角色/子级/默认/main.yml
注意:角色/子级/meta/main.yml
包含 dependencies: - { role: parent }
---
root_pw_value: ChildPassword
play-me.yml
---
- hosts: all
roles:
- child
角色/父级/任务/main.yml & 角色/子级/任务/main.yml
- debug: var=parent_dict
运行 ansible -i localhost, --connection="local" play-me.yml
,你会看到以下输出:
PLAY [all] ********************************************************************
GATHERING FACTS ***************************************************************
ok: [localhost]
TASK: [parent | debug var=parent_dict] ****************************************
ok: [localhost] => {
"var": {
"parent_dict": [
{
"root_pw": "ParentPassword"
}
]
}
}
TASK: [child | debug var=parent_dict] *****************************************
ok: [localhost] => {
"var": {
"parent_dict": [
{
"root_pw": "ChildPassword"
}
]
}
}
PLAY RECAP ********************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0
这些都是默认值。如果你在更具体的优先级层级上指定 root_pw_value
,比如在清单组/主机变量、角色变量、命令行的额外变量,或者任何来自优先级顺序的东西,你就会得到那些值。
从Ansible 2.0开始,你可以使用Jinja2的combine
过滤器来合并YAML的哈希表或字典,而不需要在你的ansible.cfg
文件中全局设置hash_behavior=merge
。
默认情况下,Ansible会在第一级覆盖变量。如果你想要合并字典(就是把两个字典的内容合在一起),你需要修改你的 ansible.cfg
文件,并设置:
hash_behaviour=merge
(默认值是 replace
)。
需要注意的是,Ansible团队 并不推荐这样做。我觉得这在用户之间是个很大的分歧。这是一个一劳永逸的决定:一旦你开始使用这个功能,就不能再回头了,而且你可能也无法和那些使用 replace
的人共享你的剧本。
不过,你仍然可以从网上找到的剧本中受益(我觉得剧本并不会把 replace
行为当成“功能”来使用)。这就像是AB型血的人,能接受任何血型……但因为魔法通常发生在变量解析的过程中,而不是在任务或模板内部,我认为通常可以在不做任何修改的情况下共享你的角色。
如果你需要覆盖某个特定角色的参数中的单个键,你就得用一些复杂的方法来传递参数。
例如,要在一个 php5
字典中覆盖 post_max_size
和 upload_max_size
这两个键,你需要这样做:
- { role: php5-fpm, php5: { post_max_size: 40M,
upload_max_filesize: 20M }}
话虽如此,我从一开始就使用 merge
行为,并且对此非常满意。这样可以很好地保持变量的组织性。