devops's docs

1.5.4: vars host在不同group中,变量覆盖问题


0. 问题背景

在ansible中,inventory的组织方式可如下

somedomain

[cluster01]
domain01

[cluster02]
domain02

[tomcat:children]
cluster01
cluster02

这样我们可以使用子分组来区分不同分组主机的变量,但是生产环境中,很有可能cluster01和02在同一台主机上
此时,在切换子分组的时候,每个子分组的变量会互相干涉吗?

注意:
文中的转义符"\",仅为了语法不冲突,无实际意义,实际操作时,需去掉它


1. 以下所有实验的目录结构

tree .
.
├── group_vars
│   ├── group
│   ├── group01
│   └── group02
├── hosts
└── main.yml

2. group_vars同一层级之间会竞争,无法共存

1) 场景1、子分组变量会覆盖父分组变量

# inventory文件
****************************
[group:children]
group01

[group01]
192.168.33.102
****************************

# 父group变量文件:group_vars/group
****************************
---
test: test_group
****************************

# 子group变量文件:group_vars/group
****************************
---
test: test_group_01
****************************

# 测试yaml文件:main.yml
****************************
---
- hosts: "\{\{ host }}"
  remote_user: root

  tasks:
   - debug: msg="\{\{ test }}"
****************************

# 测试结果
ansible-playbook -i hosts main.yml -e '{"host":"group"}'

PLAY [group] *******************************************************************

TASK [setup] *******************************************************************
ok: [192.168.33.102]

TASK [debug] *******************************************************************
ok: [192.168.33.102] => {
    "msg": "test_group_01"
}

PLAY RECAP *********************************************************************
192.168.33.102               : ok=2    changed=0    unreachable=0    failed=0  

# 结论
子分组的变量会覆盖父分组

2) 场景2、host变量会覆盖组变量

# 与场景1唯一的改变是在inventory文件中增加host变量
****************************
[group:children]
group01

[group01]
192.168.33.102 test=test_host
****************************

# 测试结果
ansible-playbook -i hosts main.yml -e '{"host":"group"}'

PLAY [group] *******************************************************************

TASK [setup] *******************************************************************
ok: [192.168.33.102]

TASK [debug] *******************************************************************
ok: [192.168.33.102] => {
    "msg": "test_host"
}

PLAY RECAP *********************************************************************
192.168.33.102               : ok=2    changed=0    unreachable=0    failed=0   

# 结论
host变量会覆盖组变量

3) 场景3、同一层级的组变量,不确定哪一方会胜出

# 与场景1(注意是场景1)的区别1是修改了inventory内容
****************************
[group:children]
group01
group02

[group01]
192.168.33.102

[group02]
192.168.33.102
****************************

# 与场景1(注意是场景1)的区别2是增加了group_vars/group02的内容
****************************
---
test: test_group_02
****************************

# 测试结果
ansible-playbook -i hosts main.yml -e '{"host":"group"}'

PLAY [group] *******************************************************************

TASK [setup] *******************************************************************
ok: [192.168.33.102]

TASK [debug] *******************************************************************
ok: [192.168.33.102] => {
    "msg": "test_group_01"
}

PLAY RECAP *********************************************************************
192.168.33.102               : ok=2    changed=0    unreachable=0    failed=0

# 结论
当同层级(group01和group02)同时存在针对同一host的组变量时,不确定哪一个会生效

当然,实验3也是主要的问题所在,因为假设在同一台host上我们部署了两个tomcat,一主一备, 当我们希望去分别指定两个tomcat时,同一变量的情况下,我们无法准确的知道该变量会指定谁。


3. 如何解决主机同时处在不同分组时变量传递的问题

从上面的实验中,我们得出一个结论,ansible对于变量的层级分为三种,all、group、host(暂不考虑role)

  • 不同类别中变量优先级为host>group>all
  • 同类别中,子层级>父层级 也就是说,对于主机的指定越精确的变量,优先级越大。

这里思考解决办法:

  • 办法1,可以通过使用不同的role,来达到隔离分组变量互相干扰的问题(个人不是太喜欢这种方法,未测试)
  • 办法2,上面的变量覆盖和冲突,根源在于ansible对同一变量名的优先级逻辑,那我们可以在变量名称逻辑上下功夫

1) 使用变量list解决

# inventory内容
****************************
[group:children]
group01
group02

[group01]
192.168.33.102

[group02]
192.168.33.102
****************************

# group变量文件:group_vars/group
****************************
---
test:
  - '\{\{ test_01 | d({}) }}'
  - '\{\{ test_02 | d({}) }}'
****************************
# 含义为当test_01变量不存在时,返回{}
# test_02亦同

# group01变量文件:group_vars/group01
****************************
---
test_01: test_group_01
****************************

# group02变量文件:group_vars/group02
****************************
---
test_02: test_group_02
****************************

# 测试yaml文件内容
****************************
---
- hosts: "\{\{ host }}"
  remote_user: root

  tasks:
   - debug: msg="\{\{ test }}"
****************************

# 测试结果
ansible-playbook -i hosts main.yml -e '{"host":"group"}'

PLAY [group] *******************************************************************

TASK [setup] *******************************************************************
ok: [192.168.33.102]

TASK [debug] *******************************************************************
ok: [192.168.33.102] => {
    "msg": [
        "test_group_01",
        "test_group_02"
    ]
}

PLAY RECAP *********************************************************************
192.168.33.102               : ok=2    changed=0    unreachable=0    failed=0  

# 此时我们发现返回的结果是一个list,包含了两个变量的值。但我们希望它返回两次,而不是一个list
# 因为若返回结果是list,我们在很多模块中调用它会因为类型问题报错

# 我们仅对yaml文件进行修改
****************************
---
- hosts: "\{\{ host }}"
  remote_user: root

  tasks:
   - debug: msg="\{\{ item }}"
     with_items: "\{\{ test }}"
****************************

# 再次测试
ansible-playbook -i hosts main.yml -e '{"host":"group"}'

PLAY [group] *******************************************************************

TASK [setup] *******************************************************************
ok: [192.168.33.102]

TASK [debug] *******************************************************************
ok: [192.168.33.102] => (item=test_group_01) => {
    "item": "test_group_01",
    "msg": "test_group_01"
}
ok: [192.168.33.102] => (item=test_group_02) => {
    "item": "test_group_02",
    "msg": "test_group_02"
}

PLAY RECAP *********************************************************************
192.168.33.102               : ok=2    changed=0    unreachable=0    failed=0   
# 此时结果完全符合我们的预期

2) 若不希望得到列表呢? 使用主机别名解决

有时候我们并不希望执行时ansible去循环这个变量,而是希望指定一个子分组,ansible就采用我们在子分组中配置的那个变量怎么办?

此时我们可以使用外部传参或inventory中指定别名的方式来隔离分组变量 其中外部传参的方式完全规避了group vars的影响,暴力直接,但是也有其局限性,那就是在inventory中主机特别多时,需要整理的参数很多

这里我们采用inventory别名的方法来测试
inventory文件内容修改

[group:children]
group01
group02

[group01]
host1 ansible_host=192.168.33.102

[group02]
host2 ansible_host=192.168.33.102

变量文件修改

# group01变量文件
---
test_01: test_group_01
test_separate: this is 01

# group02变量文件
---
test_02: test_group_02
test_separate: this is 02
---

这样我们就在group01和group02中拥有了两个直接冲突的变量

yaml测试文件修改

---
- hosts: "\{\{ host }}"
  remote_user: root

  tasks:
   - debug: msg="\{\{ test }}"
   - debug: msg="\{\{ test_separate }}"

增加一个debug信息,让我们来看看区别

测试结果

# 先来指定group01
ansible-playbook -i hosts main.yml -e "host=group01"

PLAY [group01] *****************************************************************

TASK [setup] *******************************************************************
ok: [host1]

TASK [debug] *******************************************************************
ok: [host1] => {
    "msg": [
        "test_group_01",
        {}
    ]
}

TASK [debug] *******************************************************************
ok: [host1] => {
    "msg": "this is 01"
}

PLAY RECAP *********************************************************************
host1                      : ok=3    changed=0    unreachable=0    failed=0

# 再来指定group02
ansible-playbook -i hosts main.yml -e "host=group02"

PLAY [group02] *****************************************************************

TASK [setup] *******************************************************************
ok: [host2]

TASK [debug] *******************************************************************
ok: [host2] => {
    "msg": [
        {},
        "test_group_02"
    ]
}

TASK [debug] *******************************************************************
ok: [host2] => {
    "msg": "this is 02"
}

PLAY RECAP *********************************************************************
host2                      : ok=3    changed=0    unreachable=0    failed=0

我们可以看出,当我们指定的不是group而是其分组时,第一个debug信息输出的是一个包含{}的列表 而第二个debug,则由于我们在inventory中的别名配置,隔离了分组之间的变量冲突