举例说明如何使用不同的可解变量

时间:2020-01-09 10:34:04  来源:igfitidea点击:

Ansible并不是一种成熟的编程语言,但它确实具有几种编程语言特性,其中最重要的特性之一就是变量替换。

Ansible中提供了不同类型的变量(我们可以单击单独的链接,该链接将带我们从docs.ansible.org:

  • 特殊变量

  • 清单(Inventory)变量

  • 主机变量

  • 组变量

  • 事实与地方事实

  • 连接变量

  • 以此类推。。

创建有效的变量名

  • 在开始使用变量之前,了解什么是有效的变量名是很重要的。

  • 变量名只能包含字母、下划线和数字,不允许使用空格。

  • 变量的名称只能以字母开头,它们可以包含数字,但不能以数字开头。

例如,以下是好的变量名:

http_port
server_hostname_db1

但以下示例均无效,不能使用:

dbserver-east-zone
app server ip
web.server.port
01dbserver

内置变量

Ansible定义了在剧本中始终可用的几个变量

参数说明
hostvars;一个字典,其键是可转换的主机名和值,是将变量名映射到值的字典
inventory_hostname;Ansible已知的当前主机的名称
group当前主机是的成员的所有组的列表
groups一个字典,其键是可翻译的组名和值,是组成员的主机名列表。包括所有组和未分组组:{“all”:[…],“web”:[…],“ungroup”:[…]}
play_hosts;当前播放中活动的资源清册主机名列表
ansible_version;具有可翻译版本信息的字典:{string':'2.9.13','full':'2.9.13','main':2'次要':9'修订:13}

定义清单(Inventory)中的变量

我们可以在列表中定义两种类型的变量,即主机变量和组变量。让我们逐一了解他们:

主机变量

我们将使用/etc/ansible/hosts中的默认清单(Inventory),其中包含以下条目

server1
server2
server3

在这个列表中,我将用以下格式为server2定义两个变量

server1
server2 http_port=8080 pkg=httpd
server3

所以其中我定义了两个自定义变量,它们只适用于"server2"。现在我们将这个变量与ansible playbook一起使用。我其中写了一个小剧本_变量yml它将使用清单(Inventory)中的这些变量,以便我们知道这些变量其中工作:

--
 - name: Verifying host variable
   hosts: server2
   tasks:
   - name: Testing first variable (http_port)
     debug:
       msg: "The HTTP PORT is {{ http_port }}"
   - name: Testing second variable (pkg)
     debug:
       msg: "The package name is {{ pkg }}"

让我们来执行这个剧本:

[ansible@controller ~]$ ansible-playbook host_vars.yml
PLAY [Verifying host variable] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** *****
TASK [Gathering Facts] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** *****
ok: [server2]
TASK [Testing first variable (http_port)] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **
ok: [server2] => {
    "msg": "The HTTP PORT is 8080"
}
TASK [Testing second variable (pkg)] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ***
ok: [server2] => {
    "msg": "The package name is httpd"
}
PLAY RECAP ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** *****
server2                    : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

在TASK的输出中,我们可以看到变量"http_port"被替换为"8080",而"pkg"被替换为"httpd"。

组变量

我已经向你解释了清单(Inventory)中的组。因此,可以将组变量分配给一个完整的组,并且所有主机都是该组的一部分,而不是单个主机。为了演示如何修改列表文件并将主机列表划分为组

[app]
server1
server3
[db]
server2
[db:vars]
http_port=8080
pkg=httpd

其中:我将托管节点分为两个组,并使用"db:vars"将变量分配给"db"组。我将对playbook文件进行一些更改,即将"server2"替换为组名"db",并对文本进行一些小的更改:

--
 - name: Verifying group variable
   hosts: db
   tasks:
   - name: Testing first variable (http_port)
     debug:
       msg: "The HTTP PORT is {{ http_port }}"
   - name: Testing second variable (pkg)
     debug:
       msg: "The package name is {{ pkg }}"

让我们执行 group_vars.yml 剧本并验证"组变量":

[ansible@controller ~]$ ansible-playbook group_vars.yml
PLAY [Verifying group variable] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ****
TASK [Gathering Facts] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** *****
ok: [server2]
TASK [Testing first variable (http_port)] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **
ok: [server2] => {
    "msg": "The HTTP PORT is 8080"
}
TASK [Testing second variable (pkg)] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ***
ok: [server2] => {
    "msg": "The package name is httpd"
}
PLAY RECAP ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** *****
server2                    : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

在项目中定义变量

如果我们在某些项目下使用ansible playbook,则不建议修改列表文件。我们有一个更干净的解决方案来定义项目中的主机和组变量。

要定义主机变量,我们可以创建一个子目录"host_vars",类似地,要定义组变量,我们可以在主项目目录中创建一个子目录"group_vars"。

例如,我将创建一个"lab1"项目并复制ansible.cfg文件从默认目录/etc/ansible/

[ansible@controller ~]$ mkdir lab1
[ansible@controller ~]$ cd lab1/

复制/etc/ansible/ansible.cfg文件到项目目录

[ansible@controller lab1]$ cp /etc/ansible/ansible.cfg .

创建一个列表文件,我将只在列表文件中添加"server2",因为这足以其中演示示例:

[ansible@controller lab1]$ cat inventory
server2

现在我们将在lab1中创建"host_vars"目录`

[ansible@controller lab1]$ mkdir host_vars

在"host_vars"中,我们将创建一个与服务器同名的新文件,即"server2",并定义变量:

[ansible@controller lab1]$ cat host_vars/server2
http_port: 8080
pkg: httpd

现在我们复制现有的主机_变量yml`到此项目的主文件夹:

[ansible@controller lab1]$ cp ~/host_vars.yml .

然后执行剧本:

[ansible@controller lab1]$ ansible-playbook host_vars.yml
PLAY [Verifying host variable] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** *****
TASK [Gathering Facts] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** *****
ok: [server2]
TASK [Testing first variable (http_port)] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **
ok: [server2] => {
    "msg": "The HTTP PORT is 8080"
}
TASK [Testing second variable (pkg)] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ***
ok: [server2] => {
    "msg": "The package name is httpd"
}
PLAY RECAP ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** *****
server2                    : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

因此ansible能够从"host_vars"目录中的"server2"文件中获取变量。

类似地,我们可以在lab1下创建groupvars目录,并使用组名创建一个包含变量列表的新文件。接下来在playbook文件中用组名而不是服务器名修改"hosts:"条目。

在playbook中定义变量

定义变量的最简单方法是在playbook中放置一个"vars"部分,其中包含变量的名称和值。

在本例中playbook-变量yml我在"vars"下定义了两个自定义变量,并将它们与"debug"模块一起使用。此外,我还使用内置变量

--
 - hosts: server2
   vars:
     port_no: 80
     pkg_name: httpd
   gather_facts: no
   tasks:
   - debug:
       msg:
        - "The value of port is {{ port_no }}"
        - "The value of pkg is {{ pkg_name }}"
        - "The value of hostname is {{ inventory_hostname }}"
        - "Ansible version is {{ ansible_version }}"

让我们来执行这个剧本:

[ansible@controller ~]$ ansible-playbook playbook-vars.yml
PLAY [server2] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** *****
TASK [debug] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ***
ok: [server2] => {
    "msg": [
        "The value of port is 80",
        "The value of pkg is httpd",
        "The value of hostname is server2",
        "Ansible version is {'string': '2.9.13', 'full': '2.9.13', 'major': 2, 'minor': 9, 'revision': 13}"
    ]
}
PLAY RECAP ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** *****
server2                    : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

所以ansible已经成功地获取了变量的值。

使用命令行定义变量

通过将带有"var=value"的"-e"或者"-extra vars"传递给ansible playbook而设置的变量具有最高的优先级,这意味着我们可以使用它来重写已经定义的变量

这里有一个示例playbookvariable with cmd-行.yml其中,我定义了一个变量"username",其值为"hynman"。然后我使用debug模块打印'username'变量的值

--
 - hosts: localhost
   vars:
     username: hynman
   tasks:
   - debug:
      msg: "USERNAME={{ username }}"

另外,由于本节是关于使用"command line variables"的,我们将使用"username"定义相同的变量,并指定不同的值:

[ansible@controller ~]$ ansible-playbook variable-with-cmd-line.yml -e username=theitroad
PLAY [localhost] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ***
TASK [Gathering Facts] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** *****
ok: [localhost]
TASK [debug] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ***
ok: [localhost] => {
    "msg": "USERNAME=theitroad"
}
PLAY RECAP ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** *****
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

从"TASK"部分的输出中可以看到,我们用"-e"给出的命令行变量值优先于playbook中由"vars"给出的值。

访问变量

可以使用不同的方法访问变量的内容。在上面的例子中,我们使用了双花括号来访问所有变量。这个花括号语法是Jinja2模板的一部分。

现在在这些花括号中,我们只定义了变量的名称,映射值被打印在控制台上。但有时变量可以是复杂的结构,例如"收集事实"的输出

我们可以使用setup模块访问系统的事实,setup模块提供了一个很长的输出列表。提供的一些事实,如网络信息,可以作为嵌套的数据结构使用。要访问它们,一个简单的{foo}}是不够的,但仍然很容易做到。下面是获取IP地址的方法:

在本行动手册中access-变量.yml我结合了多种方法,我们可以使用Ansible Facts捕获eth0的IPv4地址

--
 - name: Collect IPv4 address of eth0
   hosts: localhost
   tasks:
   - debug:
       msg:
         - "Method-1 {{ ansible_eth0.ipv4.address }}"
         - "Method-2 {{ ansible_eth0['ipv4']['address'] }}"
         - "Method-3 {{ ansible_eth0['ipv4'].address }}"
         - "Method-4 {{ ansible_eth0.ipv4['address'] }}"

正如我们在debug module部分中所看到的,我们可以使用两种方法从facts中收集IPv4地址。让我们来执行这个剧本:

[ansible@controller ~]$ ansible-playbook register_variable.yml
PLAY [Capture output of command] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ***
TASK [Gathering Facts] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** *****
ok: [localhost]
TASK [debug] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ***
ok: [localhost] => {
    "msg": [
        "Method-1 172.31.7.253",
        "Method-2 172.31.7.253",
        "Method-3 172.31.7.253",
        "Method-4 172.31.7.253",
    ]
}
PLAY RECAP ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** *****
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

所以我们用所有的方法得到相同的值。

访问变量中的字典键

当我们执行setup模块时,它会给我们一长串带有系统信息的输出。默认情况下,此信息存储在"ansible_facts"变量中。我们还可以单独搜索存储系统不同值的其他变量,以从安装模块中获取变量列表,我们可以执行:

[ansible@controller ~]$ ansible localhost -m setup | grep ansible
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
        "ansible_all_ipv6_addresses": [
        "ansible_apparmor": {
        "ansible_architecture": "x86_64",
        "ansible_bios_date": "08/24/2006",
        "ansible_bios_version": "4.2.amazon",
        "ansible_cmdline": {
        "ansible_date_time": {
        "ansible_default_ipv4": {
        "ansible_default_ipv6": {},
        "ansible_device_links": {
        "ansible_devices": {
        "ansible_distribution": "CentOS",
        "ansible_distribution_file_parsed": true,
        "ansible_distribution_file_path": "/etc/redhat-release",
        "ansible_distribution_file_variety": "RedHat",
        "ansible_distribution_major_version": "8",
        "ansible_distribution_release": "Core",
        "ansible_distribution_version": "8.2",
        ...
        "ansible_user_shell": "/bin/bash",
        "ansible_user_uid": 1001,
        "ansible_userspace_architecture": "x86_64",
        "ansible_userspace_bits": "64",
        "ansible_virtualization_role": "guest",
        "ansible_virtualization_type": "xen",

所以我们可以使用这些变量中的任何一个来收集系统信息。

如果变量包含字典,则可以使用点(.)或者下标([])访问字典的键。这是安装模块的修剪输出示例,其中包含接口和IP地址信息,我们使用这些信息来获取上一个示例中的数据:

"ansible_eth0": {
            "active": true,
            "device": "eth0",
            "features": {
                "esp_hw_offload": "off [fixed]",
                 ...
                "tls_hw_rx_offload": "off [fixed]",
                "tls_hw_tx_offload": "off [fixed]",
            },
            "hw_timestamp_filters": [],
            "ipv4": {
                "address": "172.31.7.253",
                "broadcast": "172.31.15.255",
                "netmask": "255.255.240.0",
                "network": "172.31.0.0"
            },

这里的输出是字典格式的,我们需要访问这个字典的键。因此,我们首先取变量名"ansible_eth0",后跟键,这样使用"dot"表示法,变量就变成了"ansible_eth0.ipv4.addess"

现在,我们还可以使用下标"[]",并将变量修改为"ansible_eth0['ipv4']['address']"

最后,要访问ansible playbook中的变量,我们使用Jinja2模板,使用双花括号{{}}

使用寄存器模块存储任何命令的输出

注册变量与事实相似,但有一些关键区别。与事实一样,注册变量也是主机级变量。但是,注册变量只存储在内存中。(Ansible事实由我们配置的任何缓存插件支持。)注册变量仅在主机上对当前playbook运行的其余部分有效。最后,注册变量和事实具有不同的优先级。

我有一个剧本文件示例_变量.yml包括以下内容:

--
 - name: Capture output of command
   hosts: localhost
   gather_facts: false
   tasks:
   - name: Collect ip address
     shell: "ifconfig eth0"

此剧本将在"localhost"上执行"ifconfig eth0"。

[ansible@controller ~]$ ansible-playbook register_variable.yml
PLAY [Capture output of command] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ***
TASK [Collect ip address] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **
changed: [localhost]
PLAY RECAP ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** *****
localhost                  : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

但这里我们的目的是"获取eth1接口使用的IP地址",因此必须捕获"ifconfig eth1"的输出

为了捕获输出,我们使用'register'模块。使用"register"子句的变量集的值始终是字典,但字典的特定键不同,具体取决于调用的模块。

因此,我们将更新playbook以使用register模块,并将"ifconfig eth0"的输出存储到"ip_addr"变量中

--
 - name: Capture output of command
   hosts: localhost
   gather_facts: false
   tasks:
   - name: Collect ip address
     shell: "ifconfig eth0"
     register: ip_addr
   - debug: var=ip_addr

接下来,我们将执行我们的剧本,其中包含一长串的输出:

[ansible@controller ~]$ ansible-playbook register_variable.yml
PLAY [Capture output of command] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ***
TASK [Collect ip address] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **
changed: [localhost]
TASK [debug] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ***
ok: [localhost] => {
    "ip_addr": {
        "changed": true,
        "cmd": "ifconfig eth0",
        "delta": "0:00:00.004912",
        "end": "2017-09-23 08:16:01.349273",
        "failed": false,
        "rc": 0,
        "start": "2017-09-23 08:16:01.344361",
        "stderr": "",
        "stderr_lines": [],
        "stdout": "eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 9001\n        inet 172.31.7.253  netmask 255.255.240.0  broadcast 172.31.15.255\n        inet6 fe80::4c:ff:feb4:e3c  prefixlen 64  scopeid 0x20\n        ether 02:4c:00:b4:0e:3c  txqueuelen 1000  (Ethernet)\n        RX packets 12128  bytes 967043 (944.3 KiB)\n        RX errors 0  dropped 0  overruns 0  frame 0\n        TX packets 9008  bytes 1183882 (1.1 MiB)\n        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0",
        "stdout_lines": [
            "eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 9001",
            "        inet 172.31.7.253  netmask 255.255.240.0  broadcast 172.31.15.255",
            "        inet6 fe80::4c:ff:feb4:e3c  prefixlen 64  scopeid 0x20",
            "        ether 02:4c:00:b4:0e:3c  txqueuelen 1000  (Ethernet)",
            "        RX packets 12128  bytes 967043 (944.3 KiB)",
            "        RX errors 0  dropped 0  overruns 0  frame 0",
            "        TX packets 9008  bytes 1183882 (1.1 MiB)",
            "        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0"
        ]
    }
}
PLAY RECAP ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** *****
localhost                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

在这个输出中,我们只对"stdout"键值感兴趣,所以要访问没有变量的"stdout",我们将使用ip_addr标准

我们将用以下条目更新我们的行动手册:

- debug: var=ip_addr.stdout

重新运行剧本:

TASK [debug] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ***
ok: [localhost] => {
    "ip_addr.stdout": "eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 9001\n        inet 172.31.7.253  netmask 255.255.240.0  broadcast 172.31.15.255\n        inet6 fe80::4c:ff:feb4:e3c  prefixlen 64  scopeid 0x20\n        ether 02:4c:00:b4:0e:3c  txqueuelen 1000  (Ethernet)\n        RX packets 12297  bytes 978553 (955.6 KiB)\n        RX errors 0  dropped 0  overruns 0  frame 0\n        TX packets 9111  bytes 1199660 (1.1 MiB)\n        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0"
}

现在我们只得到'stdout'的内容。但这需要格式化,我们将执行一些额外的strip来获得我们想要的值,即eth0的IP地址。我将用\n(新行)拆分内容并获得第一个索引值,即第二行

- debug: var=ip_addr.stdout.split("\n")[1]

让我们重新运行剧本:

TASK [debug] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ***
ok: [localhost] => {
    "ip_addr.stdout.split(\"\n\")[1]": "        inet 172.31.7.253  netmask 255.255.240.0  broadcast 172.31.15.255"
}

现在我们有了更干净的输出,但是我们仍然需要更多的格式来获得eth0的IP地址。因此,我们将添加另一个基于空格字符的拆分,然后打印第一个索引值,即第二行:

- debug: var=ip_addr.stdout.split("\n")[1].split()[1]

让我们检查输出:

TASK [debug] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ***
ok: [localhost] => {
    "ip_addr.stdout.split(\"\n\")[1].split()[1]": "172.31.7.253"
}

现在我们得到了eth0的IP地址。

使用set\事实模块创建新变量

如何将这些拆分操作得到的IP地址分配给一个新变量?

vars只能在playbook中静态定义变量时使用,但对于运行时收集的动态值,可以使用set\ _fact

Ansible还允许我们使用"set_fact"模块在任务中设置事实(实际上与定义新变量相同)。因此,我们将使用"set_fact"模块,在playbook中定义一个新变量"ip_addr",它将执行所有拆分操作,然后将最终值存储在"ip_addr"中

我已经更新了我的剧本以使用"设置事实"模块:

--
 - name: Capture output of command
   hosts: localhost
   gather_facts: false
   tasks:
   - name: Collect ip address
     shell: "ifconfig eth0"
     register: ip_addr
   - set_fact:
       ip_addr: "{{ ip_addr.stdout.split('\n')[1].split()[1] }}"
   - debug: var=ip_addr

其中:我们执行相同的拆分操作,但不是使用debug module执行,而是使用"set_fact"执行此操作,以便将输出存储到"ip_addr"变量中。现在,我们可以在需要时在playbook中使用这个变量

让我们来执行这个剧本:

[ansible@controller ~]$ ansible-playbook register_variable.yml
PLAY [Capture output of command] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ***
TASK [Collect ip address] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **
changed: [localhost]
TASK [set_fact] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ****
ok: [localhost]
TASK [debug] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ***
ok: [localhost] => {
    "ip_addr": "172.31.7.253"
}
PLAY RECAP ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** *****
localhost                  : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

所以我们正确地得到了eth0接口的IP地址值。

用vars\u提示符提示用户输入

运行剧本时,可能需要在运行时收集一些信息。这可能是敏感信息,如密码。在其他情况下,这些信息只能由最终用户在运行时提供,例如在引导系统时用于根用户的密码。

我们可以通过在playbook中指定"vars_prompt"部分从最终用户处收集此信息。当我们运行playbook时,它将询问我们指定的问题并记录答案,准备用作变量。

我们再写一本剧本-变量.yml它将使用"vars_prompt"模块提示用户输入用户名和密码,然后使用"debug"模块在控制台上打印这些值。"user_name"的提示设置为"private:no",这样操作员可以看到文本,而"password"设置为"private:yes",这样操作员在键入密码时不会看到任何内容。

--
 - name: Prompt for user input
   hosts: localhost
   gather_facts: no
   vars_prompt:
     - name: user_name
       prompt: Enter your user name
       private: no
     - name: password
       prompt: Enter your password
       private: yes
   tasks:
     - debug:
         msg: "The username is {{ user_name}} and password is {{ password }}"

让我们执行这个剧本并观察输出:

[ansible@controller ~]$ ansible-playbook prompt-variable.yml
Enter your user name: hynman
Enter your password:
PLAY [Prompt for user input] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ***
TASK [debug] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ***
ok: [localhost] => {
    "msg": "The username is hynman and password is abcd"
}
PLAY RECAP ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** *****
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

从单独的YAML或者JSON文件读取和访问变量

在上面的所有示例中,我们都在inventory、playbook或者"host_vars"/"group_vars"文件夹中定义了变量。现在在本节中,我们将创建一个单独的YAML或者JSON文件,它只包含一堆变量,并使用playbook引用这个YAML或者JSON文件来读取和访问变量值。

这可以在处理多个变量时缩短剧本的长度:

使用YAML文件

我们将创建一个单独的YAML文件变量.yml包括以下内容:

[ansible@controller ~]$ cat variables.yml
NAME: hynman
COURSE: Ansible
CHAPTER: Variables

其中:我定义了3个变量,我们将使用我们的playbook"read from yml"访问这些变量-文件.yml`

--
 - name: Collects variable from variables.yml file
   hosts: localhost
   vars_files: variables.yml
   gather_facts: false
   tasks:
   - debug:
       msg:
         - "My name is {{ NAME }}"
         - "This is {{ COURSE }} tutorial"
         - "We are learning about {{ CHAPTER }}"

这里我们定义了变量.yml带有"vars_files"模块的文件。由于该文件存在于我当前的工作目录中,我没有给出任何路径,或者我们必须给出变量文件的完整绝对路径。

此剧本的输出:

[ansible@controller ~]$ ansible-playbook read-from-yml-file.yml
PLAY [Collects variable from variables.yml file] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ***
TASK [debug] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ***
ok: [localhost] => {
    "msg": [
        "My name is hynman",
        "This is Ansible tutorial",
        "We are learning about Variables"
    ]
}
PLAY RECAP ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** *****
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

因此,playbook已经成功地从我们的YAML文件中收集了值。

使用JSON文件

同样,我们也可以创建一个变量文件"custom_vars.json"文件使用JSON格式

[ansible@controller ~]$ cat custom_vars.json
{
  "a": 50,
  "b": [1,2,3,4,5],
  "c": { "one":1, "two":2 }
}

接下来我们将更新playbook文件以引用这两个变量文件。我们可以使用单独的行放置路径和变量文件名,并提及多个文件。

--
 - name: Collects variable from variables.yml file
   hosts: localhost
   vars_files:
     - ~/variables.yml
     - ~/custom_vars.json
   gather_facts: false
   tasks:
   - debug:
       msg:
         - "My name is {{ NAME }}"
         - "This is {{ COURSE }} tutorial"
         - "We are learning about {{ CHAPTER }}"
         - "The value of b is: {{ b }}"
         - "Second index value of b is: {{ b[1] }}"
         - "The value of c is {{ c }}"

让我们执行这个脚本:

[ansible@controller ~]$ ansible-playbook read-from-yml-file.yml
PLAY [Collects variable from variables.yml file] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ***
TASK [debug] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ***
ok: [localhost] => {
    "msg": [
        "My name is hynman",
        "This is Ansible tutorial",
        "We are learning about Variables",
        "The value of b is: [1, 2, 3, 4, 5]",
        "Second index value of b is: 2",
        "The value of c is {'one': 1, 'two': 2}"
    ]
}
PLAY RECAP ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** *****
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

所以我们的剧本可以读取两个变量文件。

优先

我们已经介绍了几种定义变量的方法,我们可以使用不同的值为主机多次定义同一个变量。尽量避免这种情况,但如果不行,请记住Ansible的优先规则。当同一个变量以多种方式定义时,优先规则决定哪个值获胜。

基本优先规则如下:

  • (最高优先级)ansible playbook-e var=值

  • 任务变量

  • 块变量

  • 角色和包含变量

  • set_fact

  • 注册变量

  • vars_files

  • vars_prompt

  • Play变量

  • Host facts

  • 在剧本上设置 host_vars

  • 在Playbook设置的group_vars

  • 在inventory设置的host_vars

  • 在inventory设置的group_vars

  • 清单(Inventory)变量

  • defaults/main.yml定义的角色