举例说明如何使用不同的可解变量
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
定义的角色