2.5 Ansible 变量和循环
变量
通过 Inventory 文件定义主机以及主机组变量
Ansible 默认的 Inventory 文件是 ini 格式,分别对每台主机设置一个变量名称叫做key,接着使用 debug 模块来查看变量的值,最后通过 nginx 组定义一个变量同样使用 debug 模块查看。如下所示
172.18.12.51 key=51
172.18.12.52 key=52
172.18.12.53 key=53
[nginx]
172.18.12.5[1:3]
编写一个 Playbook 文件来验证变量的引用是否正确
---
- hosts: all
tasks:
- name: display Host variale from hostfile
debug: msg="the {{ inventory_hostname }} value is {{ key }}"
运行这个 Playbook 发现每天主机都引用了自己定义的变量,接下来我们注释每台主机的变量定义,直接给 nginx 组定义一个变量,变量名称还是 key ,值为 nginx,如下所示,
#172.18.12.51 key=51
#172.18.12.52 key=52
#172.18.12.53 key=53
[nginx]
172.18.12.5[1:3]
[nginx:vars]
key=nginx
执行 验证的 Playbook 可以看到,改组定义的变量针对组内所有主机都生效。如果 nginx 组定义了变量,然后每台主机也定义了变量,只要定义的变量 key 名称不同,我们都可以直接引用这些变量。
通过命令行传参传入
上边介绍了日常过程中常用的一种变量定义方式,接下来介绍一个通过 ansible-playbook 命令行传参的方式定义变量,但是默认传进去的变量都是全局变量。如下:
$ ansible-playbook variable.yml -e "key=value"
当然也支持同时传递多个变量,
$ ansible-playbook variable.yml -e 'pass_var="test" pass_var1="test1"'
而且命令行传入的变量的优先级要高于 Playbook 中设置的变量,这样可以更灵活的指定变量的值。
通过文件传入变量
ansible-playbook 支持指定文件的方式传入变量,变量文件支持 YAML 和 JSON 两种格式,如下所示
$ cat var.yml
---
key: value
$ cat var.json
{"key": "value"}
指定 var.json文件传入变量
$ ansible-playbook variable.yml -e "@var.json"
指定 var.yml文件传入变量
$ ansible-playbook variable.yml -e "@var.yml"
在 Playbook 文件中使用 vars
我们还可以在 Playbook 文件内通过 vars 字段定义变量,比如上边的 variable.yml 文件内容如下:
---
- hosts: all
vars:
key: ansible
tasks:
- name: display Host variale from hostfile
debug: msg="the {{ inventory_hostname }} value is {{ key }}"
在 Playbook 文件内使用 vars_files
我们还可以在 Playbook 文件内通过 vars_files 字段引用变量,首先吧所有变量定义到某个文件内,然后在 Playbook 文件内使用 vars_files 参数引用这个变量文件,
---
- hosts: all
vars_files:
- var.yml
tasks:
- name: display Host variale from hostfile
debug: msg="the {{ inventory_hostname }} value is {{ key }}"
var.yml 文件就是变量定义存放的文件,这个时候我们就可以直接运行 variable.yml 。
使用 register 内的变量
Ansible Playbook 内 task 之间还可以互相传递数据,比如我们有两个以上的 tasks ,其中第 2 个 task 是否执行是需要判断第一个 task 运行后的结果,这个时候我们就的在 task 之间传递数据,需要把第 1 个 task 执行的结果传递给第 2 个 task。 Ansible task 之间传递数据使用 register 方式,看一个简单的例子。
---
- hosts: all
tasks:
- name: register variable
shell: hostname
register: info
- name: display Host variale from hostfile
debug: msg="the variable is {{ info['stdout'] }}"
这里把第 1 个 task 执行的 hostname 的结果 register 给 info 这个变量,然后在第 2 个 task 把这个结果使用 debug 模块打印出来。
stdout 是一个标准的 Python 语言在字典中取值的用法。
使用 var_prompt 传入
Ansible 还支持在运行 Playbook 的时候通过交互式的方式给定义好的参数传入变量值,只需要在 Playbook 中定义 var_prompt 的变量名和交互式提交内容即可。
而且 Ansible 还支持对输入的变量值进行加密处理,比如采用 SHA512 和 MD5 算法加密,但是需要安装 passlib python 库。 下面来看一个例子。
---
- hosts: all
vars_prompt:
- name: "one"
prompt: "please input one value"
private: no
- name: "two"
prompt: "please input two value"
default: 'good'
private: yes
tasks:
- name: display one value
debug: msg="one value is {{ one }}"
- name: display two value
debug: msg="one value is {{ two }}"
在例子中通过 vars_prompt 参数进行交互输入两个变量的值,变量名分别为 one 和 two ,one 定义为非私有变量,two 变量定义为私有变量且还提供一个默认值。如果不给变量 two 输入值的话,two 的变量的值会变成默认值。
循环
有时候写 Playbook 的时候发现写了很多的 task 都在重复引用某个模块,比如一次传输10个文件,就要写 10个 task。接下来介绍使用 loops 的方式去编写 Playbook 以减少重复使用某个模块。
标准 loops
标准 loops 是我们在编写 Playbook 过程中使用最多的一种 loops ,他能直接减少编写 task 的次数,示例如下:
---
- hosts: all
tasks:
- name: display loops
debug: msg="name is {{ item }}"
with_items:
- one
- two
- aaa
with_items 的值是 python list 数据结果,可以理解为每个 task 会循环读取 list 李明的值,然后 key 的名称是 item,当然 list 里面也支持 python 字典。
---
- hosts: all
tasks:
- name: display loops
debug: msg="name is {{ item.key }} ,vaule is {{ item.value }}"
with_items:
- {key: "one", vaule: "1"}
- {key: "two", vaule: "2"}
- {key: "AAA", vaule: "a"}
loops 除了标准的支持以外,还支持嵌套和散列
文件匹配 loops
文件匹配 loops 是我们编写 Playbook 的时候需要针对文件进行操作中最常用的一些循环,比如我们需要针对一个目录下指定哪个个事的文件进行处理,这个时候直接在引用的时候 用 with_fileglob 循环去匹配我们需要处理的 文件即可。 看一个例子。
---
- hosts: all
tasks:
- name: display loops
debug: msg="file is {{ item }}"
with_fileglob:
- /root/*.yml
with_fileglob 会匹配 root 目录下所有以 yml 结尾的文件,当中 变量 item
随机 loops
随机选择一个作为变量。
---
- hosts: all
tasks:
- name: display loops
debug: msg="file is {{ item }}"
with_random_choice:
- "A1"
- "A2"
- "A3"
with_random_choice 会在传入的 list 中随机选择一个,与 python random 实现原理一样。
条件判断 loops
有时候执行一个 task 以后,我们需要检查这个 task 的结果是否达到了预想状态,如果没有达到我们需要的状态是,就需要退出整个 Playbook 执行过程。这个时候我们就需要对某个task 结果一直循环检测了,示例如下
---
- hosts: all
tasks:
- name: display loops
shell: cat /root/ansible
register: host
until: host.stdout.startswith("Master")
retries: 5
delay: 5
5 秒执行一次 cat /root/ansible 将结果 register 给 host 然后判断 host.stdout.startswith 的内容是否是 Master 字符串开头,如果条件成立,此 task 完成,如果条件不成立,5 秒后重试,5次后不成立,此 task 运行失败。