管理变量

Ansible变量简介

在Ansible中支持设置主机变量、组变量,变量支持嵌套使用,定义好了的变量可以在playbook中引用。由于Ansible是在每个主机上单独运行命令,所以不同的主机去调用同样的变量,也可以取到不同的值得,这样进行一些配置就更灵活合理

命名变量

变量的名称必须以字母开头,并且只能包含字母、数字和下划线。

无效和有效的Ansible变量名称示例

无效的变量名称

有效的变量名称

web server

web_server

remote.file

remote_file

1st file

file_1 file1

remoteserver$1

remote_server_1 remote_server1

[root@localhost ~]# docker_user_fto_ll      //有效的变量 
[root@localhost ~]# docker user fto ll      //无效的变量

定义变量

可以在Ansible项目中的多个位置定义变量。不过,这些变量大致可简化为三个范围级别:

  • 全局范围:从命令行或Ansible配置设置的变量
  • Play范围:在play和相关结构中设置的变量
  • 主机范围:由清单、事实收集或注册的任务,在主机组和个别主机上设置的变量

如果在多个xeklh定义了相同名称的变量,则采用优先级别最高的变量。窄范围优先于更广泛的范围:由清单定义的变量将被playbook定义的变量覆盖,后者将被命令行中定义的变量覆盖。

playbook中的变量

变量在Ansible Playbook中发挥着重要作用,因为它们可以简化playbook中变量数据的管理。

在playbook中定义变量

编写playbook时,可以定义自己的变量,然后在任务中调用这些值。例如,名为web_package的变量可以使用值httpd来定义。然后,任务可以使用yum模块调用该变量来安装httpd软件包。

Playbook变量可以通过多种方式定义。一种常见的方式是将变量放在playbook开头的vars块中

[root@localhost ansible]# cat aaa/aaa.yml 
---
- name: c
  hosts: 192.168.100.42
  vars:          //变量
    IP: 192.168.100.43
    NAME: aaa2
  tasks:
    - name: test
      lineinfile:
        path: /etc/hosts 
        line: "{{IP}} {{NAME}}"     //插入变量(使用引号)
        state: present   
[root@localhost ansible]# 
[root@localhost ansible]# ansible-playbook aaa/aaa.yml    //执行

PLAY [c] **************************************************************************

TASK [Gathering Facts] ************************************************************
ok: [192.168.100.42]

TASK [test] ***********************************************************************
changed: [192.168.100.42]

PLAY RECAP ************************************************************************
192.168.100.42             : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@localhost ansible]#

[root@localhost ~]#  cat /etc/hosts          //在目标主机上查看结果
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.100.43 aaa2
[root@localhost ~]# 
[root@localhost ansible]# cat aaa/aaa.yml 
---
- name: c
  hosts: 192.168.100.42
  vars:
    IP: 192.168.100.43
    NAME: aaa2
  tasks:
    - name: test
      lineinfile:
        path: /etc/hosts
        line: aaa{{IP}} {{NAME}}      //把去掉引号,往前面加一个名字
        state: present
[root@localhost ansible]#
[root@localhost ~]#  cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.100.43 aaa2
aaa192.168.100.43 aaa2
[root@localhost ~]#

也可以在外部文件中定义playbook变量。此时不使用playbook中的vars块,可以改为使用vars_files指令,后面跟上相对于playbook位置的外部变量文件名称列表

[root@localhost playbook]# tree /etc/ansible/aaa
/etc/ansible/aaa
├── aaa.yml
└── bbb
    └── bbb.yml

1 directory, 2 files 
[root@localhost ansible]# cat aaa/bbb/bbb.yml   //我们在外边同级别下创建一个文件,并且写入相应变量的值(必须在同一个级别下)
IP: 192.168.100.43
NAME: bbb
[root@localhost ansible]# cat aaa/aaa.yml 
---
- name: c
  hosts: 192.168.100.42
  vars_files:
    - bbb/bbb.yml  //指定路径
  tasks:
    - name: test
      lineinfile:
        path: /etc/hosts
        line: aaa{{IP}} {{NAME}}
        state: present
[root@localhost ansible]# 
[root@localhost ansible]# ansible-playbook aaa/aaa.yml      //执行

PLAY [c] **************************************************************************

TASK [Gathering Facts] ************************************************************
ok: [192.168.100.42]

TASK [test] ***********************************************************************
changed: [192.168.100.42]

PLAY RECAP ************************************************************************
192.168.100.42             : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@localhost ansible]#

[root@localhost ~]#  cat /etc/hosts       //在目标主机上查看
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.100.43 aaa2
aaa192.168.100.43 aaa2
aaa192.168.100.43 bbb   
[root@localhost ~]#

在playbook中使用变量

声明了变量后,可以在任务中使用这些变量。若要引用变量,可以将变量名放在双大括号内。在任务执行时,Ansible会将变量替换为其值。

[root@localhost ansible]# cat aaa/aaa.yml 
---
- name: c
  hosts: 192.168.100.42
  vars_files:
    - bbb/bbb.yml
  tasks:
    - name: test
      lineinfile:
        path: /etc/hosts
        line: aaa{{IP}} {{NAME}}
        state: present
[root@localhost ansible]#

注意:当变量用作开始一个值的第一元素时,必须使用引号。这可以防止Ansible将变量引用视为YAML字典的开头。

主机变量和组变量

直接应用于主机的清单变量分为两在类

主机变量,应用于特定主机
组管理,应用于一个主机组或一组主机组中的所有主机
主机变量优先于组变量,但playbook中定义的变量的优先级比这两者更高。

若要定义主机变量和组变量,一种方法是直接在清单文件中定义。这是较旧的做法,不建议采用,但你可能会在未来的工作当中遇到。

定义192.168.100.42的ansible_user主机变量

[webservers]
[apache]
192.168.100.42   ansible_user=root   ansible_password=1

定义webserver主机组的user组变量

[webservers]
[apache]
192.168.100.42

[webserver:vars]
ansible_user=root
ansible_password=1
[root@node1 ansible]#

定义servers组的user组变量,该组由两个主机组成,每个主机组有两个服务器:

[servers1]
node1.example.com
node2.example.com

[servers2]
node3.example.com
node4.example.com

[servers:children]
servers1
servers2

[servers:vars]
user=joe

使用目录填充主机和组变量

定义主机和主机组的变量的首选做法是在与清单文件或目录相同的工作目录中,创建group_vars和host_vars两个目录。这两个目录分别包含用于定义组变量和主机变量的文件。

建议的做法是使用host_vars和group_vars目录定义清单变量,而不直接在清单文件中定义它们。

为了定义用于servers组的组变量,需要创建名为group_vars/servers的YAML文件,然后该文件的内容将使用与playbook相同的语法将变量设置为值

[root@localhost ansible]# mkdir group_vars
[root@localhost ansible]# mkdir host_vars
[root@localhost ansible]# ls
   aaa            group_vars         host_vars  
   playbook     ansible.cfg         inventory   
[root@localhost ansible]# vim host_vars/192.168.100.42
[root@localhost ansible]# cat host_vars/192.168.100.42
ansible_user: root
ansible_password: 1
[root@localhost ansible]# cat inventory 
[webservers]
192.168.100.42  
 
[root@localhost ansible]# 
[root@localhost ansible]# ansible all -m ping
192.168.100.42 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}
[root@localhost ansible]#

类似的,为了定义用于特定主机的主机变量,需要在host_vars目录中创建名称与主机匹配的文件来存放主机变量。

[root@localhost playbook]# tree /etc/ansible
/etc/ansible
├── aaa
│   ├── aaa.yml
│   └── bbb
│       └── bbb.yml
├── ansible.cfg
├── group_vars
├── hosts
├── host_vars
│   └── 192.168.100.42
├── inventory
├── playbook
│   └── user.yml
└── test
    └── test.yml

6 directories, 8 files
[root@localhost playbook]#

从命令行覆盖变量

清单变量可被playbook中设置的变量覆盖,这两种变量又可通过在命令行中传递参数到ansible或ansible-playbook命令来覆盖。在命令行上设置的变量称为额外变量。

当需要覆盖一次性运行的playbook的变量的已定义值时,额外变量非常有用

[root@node1 node]# vim host_vars/192.168.100.42
[root@node1 node]# cat host_vars/192.168.100.42     //我们主机变量里面是写了一个IP地址和名字的
ansible_user: root
ansible_password: 1
ip: 100.1.1.1
name: node3
[root@node1 node]# 

[root@node1 node]# vim aaaa.yml 
[root@node1 node]# cat aaa.yml      //playbook中的优先级最大
---
- hosts: 192.168.100.42
  vars:
    ip: 100.1.1.1
    name: node3
  tasks: 
    - name: aaa
      lineinfile:
        path: /etc/hosts
        line: aaa {{IP}} {{NAME}}
        state: present
[root@node1 node]#

使用数组作为变量

除了将同一元素相关的配置数据(软件包列表、服务列表和用户列表等)分配到多个变量外,也可以使用数组。这种做法的一个好处在于,数组是可以浏览的

[root@localhost ansible]# cat host_vars/192.168.100.42 
ansible_user: root
ansible_password: 1


info:
  ccc:
    ip: 100.2.2.2
    domain: abc.example.com 
  ddd:   
    ip: 100.3.3.3
    domain: web.example.com
[root@localhost ansible]# cat aaa/aaa.yml 
---
- name: c
  hosts: 192.168.100.42
  vars_files:
    - bbb/bbb.yml
  tasks:
    - name: test
      lineinfile:
        path: /etc/hosts
        line: "{{info.ccc.ip}} {{info.ddd.domain}}"      //更改变量
        state: present
[root@localhost ansible]# 
[root@localhost ansible]# ansible-playbook aaa/aaa.yml 

PLAY [c] **************************************************************************

TASK [Gathering Facts] ************************************************************
ok: [192.168.100.42]

TASK [test] ***********************************************************************
changed: [192.168.100.42]

PLAY RECAP ************************************************************************
192.168.100.42             : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@localhost ansible]#
[root@localhost ~]#  cat /etc/hosts      //在目标主机上查看
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.100.43 aaa2
aaa"192.168.100.43 aaa2
aaa192.168.100.43 bbb
100.2.2.2 web.example.com
[root@localhost ~]#

由此这出,变量它是比较灵活的。而且有很多比较复杂,也有很多比较简单

如果键名与python方法或属性的名称(如discard、copy和add)相同,点表示法可能会造成问题。使用中括号表示法有助于避免冲突和错误。

但要声明的是,上面介绍的两种语法都有效,但为了方便故障排除,建议在任何给定Ansible项目的所有文件中一致地采用一种语法,不要混用。

使用已注册变量捕获命令输出

可以使用register语句捕获命令输出。输出保存在一个临时变量中,然后在playbook中可用于调试用途或者达成其他目的,例如基于命令输出的特定配置

[root@localhost ansible]# mkdir ccc
[root@localhost ansible]# vim ccc/ccc.yml
[root@localhost ansible]# cat ccc/ccc.yml 
---
- name: abc
  hosts: 192.168.100.42
  tasks:
    - name: ccc
      yum:
        name: vsftpd
        state: present
      register: install_result

    - debug: var=install_result
[root@localhost ansible]#

运行该playbook时,debug模块用于将install_result注册变量的值转储到终端。

[root@localhost ansible]# ansible-playbook ccc/ccc.yml 

PLAY [abc] ************************************************************************

TASK [Gathering Facts] ************************************************************
ok: [192.168.100.42]

TASK [ccc] ************************************************************************
changed: [192.168.100.42]

TASK [debug] **********************************************************************
ok: [192.168.100.42] => {
    "install_result": {
        "changed": true,
        "failed": false,
        "msg": "",
        "rc": 0,
        "results": [
            "Installed: vsftpd-3.0.3-34.el8.x86_64"
        ]
    }
}

PLAY RECAP ************************************************************************
192.168.100.42             : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@localhost ansible]#

管理机密

Ansible Vault

Ansible可能需要访问密码或API密钥等敏感数据,以便能配置受管主机。通常,此信息可能以纯文本形式存储在清单变量或其他Ansible文件中。但若如此,任何有权访问Ansible文件的用户或存储这些Ansible文件的版本控制系统都能够访问此敏感数据。这显示存在安全风险。

Ansible提供的Ansible Vault可以加密和解密任何由Ansible使用的结构化数据文件。若要使用Ansible Vault,可通过一个名为ansible-vault的命令行工具创建、编辑、加密、解密和查看文件。Ansible Vault可以加密任何由Ansible使用的结构化数据文件。这可能包括清单变量、playbook中含有的变量文件、在执行playbook时作为参数传递的变量文件,或者Ansible角色中定义的变量。

创建加密文件

要创建新的加密文件,可使用ansible-vault create filename命令。该命令将提示输入新的vault密码,然后利用默认编辑器vi打开文件。我们可以设置和导出EDITOR环境变量,通过设置和导出指定其他默认编辑器。例如,若要将默认编辑器设为nano,可设置为export EDITOR=nano

[root@localhost test]# ls
test.yml
[root@localhost test]# ansible-vault create ddd/ddd.yml 
New Vault password:        //设置密码
Confirm New Vault password:        //再次输入密码
[WARNING]: ddd does not exist, creating...      //创建
[root@localhost test]# ls
ddd  test.yml
[root@localhost test]# cat ddd/ddd.yml      //因为已经加密所以看不懂类容
$ANSIBLE_VAULT;1.1;AES256
64363833336335343763643661303762333433323132383766336665386337323039643762376133
3464373365333263633266313039653263356433366632300a313634653830663264303937633238
64653631616366303762333062646461663539386462323433656536373761303632633563326635
3934633061333266390a643762363837613336393164653530363035646162373333646361653162
3334
[root@localhost test]#

我们还可以用vault密码文件来存储vault密码,而不是通过标准输入途径输入vault密码。这样做需要使用文件权限和其他方式来严密保护该文件。

ansible-vault create --vault-password-file=vault-pass secret.yml

查看加密文件

可以使用ansible-vault view filename命令查看Ansible Vault加密的文件,而不必打开它进行编辑。

[root@localhost test]# ansible-vault view ddd/ddd.yml 
Vault password:      //输入密码后才能查看
[root@localhost test]#

编辑现有加密的文件

要编辑现有的加密文件,Ansible Vault提供了ansible-vault edit filename命令。此命令将文件解密为一个临时文件,并允许编辑。保存时,它将复制其内容并删除临时文件。

[root@localhost test]# ansible-vault edit ddd/ddd.yml 
Vault password:       //输入密码进行编写
[root@localhost test]# ansible-vault view ddd/ddd.yml 
Vault password: 
abcdefg      //这是更改编写的类容
[root@localhost test]#

注意:编辑时需要输入加密文件的加密密码

edit子命令始终重写文件,因此只应在进行更改时使用它。要查看文件的内容而不进行更改时,应使用view子命令。

加密现有文件

要加密已存在的文件,请使用ansible-vault encrypt filename命令。此命令可取多个欲加密文件的名称作为参数。

[root@localhost test]# ls
ddd  test.yml
[root@localhost test]# ansible-vault encrypt test.yml 
New Vault password:        //设置密码
Confirm New Vault password:       //再次输入密码
Encryption successful        //加密成功
[root@localhost test]#

更改加密文件的密码

使用ansible-vault rekey filename命令更改加密文件的密码。此命令可一次性更新多个数据文件的密钥。它将提示提供原始密码和新密码。

[root@localhost test]# ansible-vault rekey test.yml 
Vault password:     //输入旧密码
New Vault password:     //输入新密码
Confirm New Vault password:     //再次输入新密码
Rekey successful
[root@localhost test]#

解密现有文件

现有的加密文件可以通过ansible-vault decrypt filename命令永久解密。在解密单个文件时,可使用**–output**选项以其他名称保存解密的文件

[root@localhost test]# ansible-vault decrypt test.yml   
Vault password: 
Decryption successful
[root@localhost test]# cat test.yml 
---
- name: aaa
  hosts: 192.168.100.42
  tasks:
    - name: Add SELinux = disabled
      lineinfile:
        path: /etc/selinux/config
        line: |
                SELinux = permissive
                SELinux = disabled
                SELinux = enabled    
[root@localhost test]#

事实

Ansible事实简介

事实是Ansible在受管主机上自动检测到的变量。事实中包含有与主机相关的信息,可以像play中的常规变量、条件、循环或依赖于从受管主机收集的值的任何其他语句那样使用。为受管主机收集的一些事实可能包括:

  • IP地址
  • 主机名称
  • 网络接口
  • CPU数量
  • 内核版本
  • 可用磁盘空间
  • 各种环境变量
  • 操作系统版本
  • 提供的或可用的内存内核版本

**借助事实,可以方便地检索受管主机的状态,并根据该状态确定要执行的操作。**例如:

可以根据含有受管主机当前内核版本的事实运行条件任务,以此来重启服务器
可以根据通过事实报告的可用内存来自定义MySQL配置文件
可以根据事实的值设置配置文件中使用的IPv4地址
通常,每个play在执行第一个任务之前会先自动运行setup模块来收集事实。查看为受管主机收集的事实的一种方式是,运行一个收集事实并使用debug模块显示ansible_facts变量值的简短playbook。

[root@localhost ansible]# cat test/test.yml 
---
- name: test
  hosts: "*"
  tasks:
    - name: print all facts
      debug:
        var: ansible_facts
[root@localhost ansible]#

playbook 以 JSON 格式显示 ansible_facts 变量的内容。

运行该playbook时,事实将显示在输出:

[root@localhost ansible]# ansible-playbook test/test.yml 

PLAY [test] ***********************************************************************

TASK [Gathering Facts] ************************************************************
ok: [192.168.100.42]

TASK [print all facts] ************************************************************
ok: [192.168.100.42] => {
    "ansible_facts": {
        "all_ipv4_addresses": [
            "192.168.100.42",
            "192.168.122.1"
        ],
......

下面显示了可能从受管节点收集的并可在playbook中使用的一些事实:

事实

变量

短主机名

ansible_facts[‘hostname’]

完全限定域名

ansible_facts[‘fqdn’]

IPv4地址

ansible_facts[‘default_ipv4’][‘address’]

所有网络接口的名称列表

ansible_facts[‘interfaces’]

/dev/vda1磁盘分区的大小

ansible_facts[‘devices’][‘vda’][‘partitions’][‘vda1’][‘size’

DNS服务器列表

ansible_facts[‘dns’][‘nameservers’]

当前运行的内核版本

ansible_facts[‘kernel’]

[root@localhost ansible]# cat test/test.yml 
---
- name: test
  hosts: "*"
  tasks:
    - name: print all facts
      debug:
        var: ansible_facts['hostname']     //获取事实的短主机名
[root@localhost ansible]#
[root@localhost ansible]# ansible-playbook test/test.yml 

PLAY [test] ***********************************************************************

TASK [Gathering Facts] ************************************************************
ok: [192.168.100.42]

TASK [print all facts] ************************************************************
ok: [192.168.100.42] => {
    "ansible_facts['hostname']": "localhost"
}

PLAY RECAP ************************************************************************
192.168.100.42             : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@localhost ansible]#
[root@localhost ansible]# cat test/test.yml 
---
- name: test
  hosts: "*"
  tasks:
    - name: print all facts
      debug:
        var: ansible_facts['fqdn']    //获取事实的完全限定域名
[root@localhost ansible]# ansible-playbook test/test.yml 

PLAY [test] ***********************************************************************

TASK [Gathering Facts] ************************************************************
ok: [192.168.100.42]

TASK [print all facts] ************************************************************
ok: [192.168.100.42] => {
    "ansible_facts['fqdn']": "localhost.localdomain"
}

PLAY RECAP ************************************************************************
192.168.100.42             : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@localhost ansible]#

在playbook中使用事实时,Ansible将事实的变量名动态替换为对应的值:

[root@localhost ansible]# cat test/test.yml 
---
- name: test
  hosts: "*"
  tasks:
    - name: print all facts
      debug:
        msg: > 
          The default ipv4 address of {{ ansible_facts['default_ipv4']['address'] }} 
          is {{ ansible_facts['dns']['nameservers'] }}
                                    
[root@localhost ansible]# ansible-playbook test/test.yml 

PLAY [test] ***********************************************************************

TASK [Gathering Facts] ************************************************************
ok: [192.168.100.42]

TASK [print all facts] ************************************************************
ok: [192.168.100.42] => {
    "msg": "The default ipv4 address of 192.168.100.42  is ['114.114.114.114']\n                          \n"
}

PLAY RECAP ************************************************************************
192.168.100.42             : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@localhost ansible]#

将事实作为变量注入

在Ansible2.5之前,事实是作为前缀为字符串ansible_的单个变量注入,而不是作为ansible_facts变量的一部分注入。例如,ansible_facts[‘distribution’]事实会被称为ansible_distribution。

许多较旧的playbook仍然使用作为变量注入的事实,而不是在ansible_facts变量下创建命名空间的新语法。我们可以使用临时命令来运行setup模块,以此形式显示所有事实的值。

[root@localhost ansible]# ansible 192.168.100.42 -m setup

选定的Ansible事实名称比较

ansible_facts形式

旧事实变量形式

ansible_facts[‘hostname’]

ansible_hostname

ansible_facts[‘fqdn’]

ansible_fqdn

ansible_facts[‘default_ipv4’][‘address’]

ansible_default_ipv4[‘address’]

ansible_facts[‘interfaces’]

ansible_interfaces

ansible_facts[‘devices’][‘vda’][‘partitions’][‘vda1’][‘size’]

ansible_devices[‘vda’][‘partitions’][‘vda1’][‘size’]

ansible_facts[‘dns’][‘nameservers’]

ansible_dns[‘nameservers’]

ansible_facts[‘kernel’]

ansible_kernel

目前,Ansible同时识别新的事实命名系统(使用ansible_facts)和旧的2.5前“作为单独变量注入的事实”命名系统。

将Ansible配置文件的**[default]部分中inject_facts_as_vars参数设置为False**,可关闭旧命名系统。默认设置目前为True

[root@localhost ansible]# vim ansible.cfg 
65 # ansible_facts.
66 inject_facts_as_vars = false       //默认为True,改为false,去掉行首的'#'就不支持旧命名系统了

---
 - name: test
   hosts: "*"
   tasks:
     - name: print facts
       debug:
         var: ansible_hostname     //旧的事实变量形式 
[root@localhost ansible]# ansible-playbook setup       //执行
.....
TASK [print facts] ************************************************************************************
ok: [192.168.100.42] => {
    "ansible_hostname": "VARIABLE IS NOT DEFINED!"   //变量未定义 运行失败
}

inject_facts_as_vars的默认值在Ansible的未来版本中可能会更改为False。如果设置为False,则只能使用新的**ansible_facts.***命名系统引用Ansible事实。所以建议大家一开始就要适应这种方式。

关闭事实收集

有时我们不想为play收集事实。这样做的原因可能有:

  • 希望加快play速度
  • 不准备使用任何事实
  • 需要安装一些必备软件后再收集事实
  • 希望减小play在受管主机上造成的负载
  • 受管主机因为某种原因无法运行setup模块

以上种种原因导致我们可能想要永久或暂时关闭事实收集的功能,要为play禁用事实收集功能,可将gather_facts关键字设置为no:

---
 - name: test
   hosts: "*"
   gather_facts: on      //设置为on,关闭事实收集功能
   tasks:

即使play设置了gather_facts: no,也可以随时通过运行使用setup模块的任务来手动收集事实:

---- name: gather_facts  hosts: 172.16.100.100  gather_facts: no  tasks:    - name: get gather_facts      setup:              //通过运行使用setup模块的任务来手动收集事实    - name: use debug      debug:        var: ansible_facts

创建自定义事实

除了使用系统捕获的事实外,我们还可以自定义事实,并将其本地存储在每个受管主机上。这些事实整合到setup模块在受管主机上运行时收集的标准事实列表中。它们让受管主机能够向Ansible提供任意变量,以用于调整play的行为。

自定义事实可以在静态文件中定义,格式可为INI文件或采用JSON。它们也可以是生成JSON输出的可执行脚本,如同动态清单脚本一样。

有了自定义事实,我们可以为受管主机定义特定的值,供play用于填充配置文件或有条件地运行任务。动态自定义事实允许在play运行时以编程方式确定这些事实的值,甚至还可以确定提供哪些事实。

默认情况下,setup模块从各受管主机的/etc/ansible/facts.d目录下的文件和脚本中加载自定义事实。各个文件或脚本的名称必须以**.fact**结尾才能被使用。动态自定义事实脚本必须输出JSON格式的事实,而且必须是可执行文件。

以下是采用INI格式编写的静态自定义事实文件。INI格式的自定义事实文件包含由一个部分定义的顶层值,后跟用于待定义的事实的键值对:

[root@localhost ~]# mkdir /etc/ansible -p     //在受管主机创建/etc/ansible[root@localhost ~]# cd /etc/ansible/[root@localhost ansible]# mkdir facts.d[root@localhost ansible]# cd facts.d/[root@localhost facts.d]# vi facts.fact[packages]svr_package = apachess_package = php[users]user1 = tomuser2 = harry[root@localhost facts.d]# tree /etc/ansible/ /etc/ansible/`-- facts.d    `-- facts.fact        [root@localhost ansible]# ansible all -m setup |less    //在ansible控制机上执行..... "ansible_local": {            "facts": {                "packages": {                    "ss_package": "php",         //创建的自定义事实                    "svr_package": "apache"                },                "users": {                    "user1": "tom",                    "user2": "harry".....

同样的事实可能以JSON格式提供。以下JSON事实等同于以上示例中INI格式指定的事实。JSON数据可以存储在静态文本文件中,或者通过可执行脚本输出到标准输出:

ansible_local": {    "facts": {      "packages": {           "ss_package": "php",           "svr_package": "apache"                },        "users": {           "user1": "tom",           "user2": "harry"        }     }  }

自定义事实文件不能采用playbook那样的YAML格式。JSON格式是最为接近的等效格式。

自定义事实由setup模块存储在ansible_facts.ansible_local变量中。
事实按照定义它们的文件的名称来整理。例如,假设前面的自定义事实由受管主机上保存为**/etc/ansible/facts.d/custom.fact**的文件生成。在这种情况下,ansible_facts.ansible_local[‘custom’][‘users’][‘user1’]的值为joe。

自定义事实的使用方式与playbook中的默认事实相同:

---- hosts: all  tasks:  - name: Prints various Ansible facts    debug:      msg: >        The package to install on {{ ansible_facts['fqdn'] }}        is {{ ansible_facts['ansible_local']['cutstom']['packages']['web_package'] }}

使用魔法变量

一些变量并非事实或通过setup模块配置,但也由Ansible自动设置。这些魔法变量也可用于获取与特定受管主机相关的信息。

最常用的有四个:

魔法变量

说明

hostvars

包含受管主机的变量,可以用于获取另一台受管主机的变量的值。 如果还没有为受管主机收集事实,则它不会包含该主机的事实。

group_names

列出当前受管主机所属的所有组

groups

列出清单中的所有组和主机

inventory_hostname

包含清单中配置的当前受管主机的主机名称。 因为各种原因有可能与事实报告的主机名称不同