ansbile实战应用系列教程6:管理ansible变量
Ansible Variables
Ansible支持用于存储可在整个Ansible项目中在整个文件中重用的值的变量。这可以帮助简化项目的创建和维护,并减少错误的发生。
变量为Ansible项目中给定环境的动态值管理提供了一种方便的方法。一些变量可能包含的值的例子包括
-
用户创建
-
要安装的软件包
-
重启服务
-
要删除的文件
-
从因特网上检索的档案
Naming Variables
变量的名称由一个必须以字母开头且只能包含字母、数字和下划线的字符串组成。
考虑下表,它显示了无效和有效变量名的区别:
Defining Variables
在一个ansible的项目中,变量可以在多个地方定义,不同级别的变量具有不同的有效范围。然而,这可以简化为三个基本范围级别:
-
Global scope:命令行中或者Ansible配置文件中定义,类似于shell里面的环境变量
-
Play scope:play中定义,只在自己的play中生效,当前playbook中的其他play不可以使用当前play定义的变量,只针对自己的play生效。
-
Host scope:由inventory、fact gathering或registered tasks在主机组和单个主机上设置的变量。
如果在多个级别定义相同的变量名,则较高的将胜出。因此,由inventory定义的变量会被playbook定义的变量覆盖,而playbook会被命令行定义的变量覆盖。
优先级: Global->Play->Host。
在playbook中定义变量
Defining Variables in Playbooks
当编写playbook时,管理员可以使用他们自己的变量并在任务中调用它们。例如,变量web_package可以定义为httpd值,并由yum模块调用,以便安装httpd包。
Playbook变量可以用多种方式定义。最简单的一种方法是将它放在playbook开头的vars块中。
---
- name: test vars command in play
hosts: servera
remote_user: root
vars:
user: joe
home: /home/joe
也可以在外部文件中定义playbook变量。在这种情况下,代替使用vars,可以使用var_files指令,后面是一个外部变量文件的列表:
---
- name: test vars command in play
hosts: servera
remote_user: root
vars_files:
- vars/user1.yml
然后在vars/user1.yaml或YAML格式的文件中定义playbook变量:
user: joe
home: /home/joe
在playbooks中使用Variables
一旦声明了变量,管理员就可以在任务中使用变量。通过将变量名放在双花括号中来引用变量。Ansible在执行任务时用变量的值替换变量。
---
- name: test vars command in play
hosts: servera
remote_user: root
vars:
user: joe
home: /home/joe
tasks:
- name: add user "{{user }}"
user:
name: "{{ user }}" #取变量的值
home: "{{ home}}"
---
- name: test vars command inplay
hosts: servera
remote_user: root
vars_files:
- vars/user1.yml
tasks:
- name: add user {{user1_name }}
user:
name: "{{user1_name }}"
home: "{{user1_home }}"
# cat vars/user1.yml
--- #可省略
user1_name: user1
user1_home: /home/user1
Host Variables and Group Variables
直接应用于主机的inventory variables分为两大类:应用于特定主机的主机变量和应用于主机组或主机组中的所有主机的组变量。host主机变量优先于group组变量,但是由playbook定义的变量优先于两者。
定义主机变量和组变量的一种方法是直接在inventory file中执行。这是一种较旧的方法,不是首选,但用户可能会遇到:
Host主机变量:为主机demo.example.com,定义一个ansible_user的变量。
[servers]
demo.example.com ansible_user=joe
group主机组变量:为servers的组定义一个user的变量
[servers]
demo1.example.com
demo2.example.com
[servers:vars]
user=joe
在本例中,为组服务器定义了组变量user,组服务器由两个主机组组成,每个主机组有两个服务器。
[servers1]
demo1.example.com
demo2.example.com
[servers2]
demo3.example.com
demo4.example.com
[servers:children]
servers1
servers2
[servers:vars]
user=joe
这种方法的缺点之一是,它使库存文件更加难以处理,将关于主机和变量的信息混合在同一个文件中,并且使用了过时的语法。主机变量优先级高于主机组变量
使用group_vars和host_vars目录
首选的方法是在与目录文件或目录相同的工作目录中创建两个目录,group_vars和host_vars。这些目录包含分别定义组变量和主机变量的文件。
要为servers组定义组变量,需要创建一个名为group_vars/servers的YAML文件,然后该文件的内容将使用与piaybook相同的语法将变量设置为值:
user: joe
同样,要为特定主机定义主机变量,需要在host_vars中创建一个名称与主机匹配的文件来包含主机变量。
下面的示例更详细地说明了这种方法。考虑以下场景,其中有两个数据中心需要管理,在~/project/inventory中有以下inventory文件:
[admin@station project]$ cat /project/inventory
[datacenterl]
demol.example.com
demo2.example.com
[datacenter2]
demo3.example.com
demo4.example.com
[datacenters:children]
datacenterl
datacenter2
如果需要为两个数据中心的所有服务器定义一个通用值,可以为datacenters设置一个组变量:
[admin@station project]$ cat ~/project/group_vars/datacenters
package : httpd
如果要定义的值因每个数据中心而异,则可以为每个数据中心设置组变量
[admin@station project]$ cat ~/project/group_vars/datacenterl
package: httpd
[admin@station project]$ cat ~/project/group_vars/datacenter2
package: apache
如果要定义的值对于每个数据中心的每个主机都不同,建议使用主机变量:
[admin@station project ]$ cat ~/project/host_vars/demol.example.com
package: httpd
[admin@station project ]$ cat ~/project/host_vars/demo2.example.com
package: apache
[admin@station project ]$ cat ~/project/host_vars/demo3.example.com
package: mariadb-server
[adniin@station project]$ cat ~/project/host_vars/demo4.example.com
package: mysql-server
Project的目录结构,如果它包含上面所有的示例文件,可能看起来像这样:

命令行定义全局变量
inventory变量会被playbook中设置的变量覆盖,但是这两种变量都可以通过在命令行中传递给ansible或ansible playbook命令的参数来覆盖。这在需要为单个主机一次运行剧本而重写变量的定义值的情况下非常有用。例如:
[user@demo ~]$ansible-playbook main.yml --limit=demo2.example.com - e "package=apache
[student@taijitao-server ~]$ ansible localhost -m debug -a 'var=package'-e "package=111"
[student@taijitao-server ~]$ vim ansible.cfg
[defaults]
inventory=inventory
[student@taijitao-server ~]$ vim inventory
server[a:d]
[student@taijitao-server ~]$ ansible all --list-hosts
hosts (4):
servera
serverb
serverc
serverd
[student@taijitao-server ~]$ ansible localhost -m debug -a 'var=package'-e "package=111" #-e 参数传递变量时,一次只能传递一个变量
localhost | SUCCESS => {
"package":"111"
}
Variables and Arrays
管理员可以使用数组arrays,而不是将与相同元素(包列表、服务列表、用户列表等)相关的一段配置数据分配给多个变量。这样做的一个有趣的结果是可以浏览数组。
例如,变量其他方式考虑以下代码片段:
user1_first_name: Bob
user1_last_name: Jones
user1_home_dir: /users/bjones
user2_first_name: Anne
user2_last_name: Cook
user3_home_dir: /usr/acook
这可以重写为一个数组称为users:
users:
bjones:
first_name: Bob
last_name: Jones
home_dir: /users/bjones
acook:
first_name: Anne
last_name: Cook
home_dir: /users/acook
然后可以使用以下变量访问用户
# Returns 'Bob'
users.bjones.first_name
# Returns '/users/acook'
users.acook.home_dir
因为该变量被定义为Python字典,所以可以使用另一种语法。
# Returns 'Bob'
users['bjones']['first_name']
# Returns '/users/acook'
users['acook']['home_dir']
.分隔符引用的关键字与python的功能函数同名,例如discard*、copy、add,那么就会出现问题。使用方法二**[‘’**]*引用方式可以避免这种错误。
如果键名与Python方法或属性的名称相同,例如discard、copy、add等,点表示法可能会导致问题。使用方括号可以帮助避免错误。
这两种语法都是有效的,但为了使故障排除更容易,建议在任何给定的Ansible项目的所有文件中一致使用一种语法。
register变量
管理员可以使用register语句捕获命令的输出。输出被保存到一个变量中,这个变量稍后可以用于调试目的,也可以用于实现其他目的,比如基于命令输出的特定配置。
下面的剧本演示了如何捕获用于调试的命令输出:
---
- name: Install a package and print the result
hosts: servera
remote_user: root
tasks:
- name: install the package
yum:
name: httpd
state: installed
register: install_result #保存了这个task的执行结果。
- name: print the result
debug:
var: install_result
当playbook运行时,debug模块用于将install_result注册变量的值转储到终端。验证结果;
[student@taijitao-server ~]$ansible-playbook var3.yaml
PLAY [Install a package and print theresult] **********
TASK [Gathering Facts]***************
ok: [servera]
TASK [install the package]****************
ok: [servera]
TASK [print the result]********************
ok: [servera] => {
"install_result": {
"changed": false,
"msg": "",
"rc": 0, #相当于echo $?返回的结果
"results": [
"httpd-2.4.6-45.el7.x86_64providing httpd is already installed"
]
}
}
PLAY RECAP ******************
servera : ok=3 changed=0 unreachable=0 failed=0
实战一:
- 从taijitao-server,作为student用户,切换到~/dev-vars-playbook目录。
[student@taijitao-server~]$ cd dev-vars-playbook/
[student@taijitao-serverdev-vars-playbook]$ ll
total 8
-rw-r--r--. 1student student 99 Feb 7 16:45ansible.cfg
-rw-r--r--. 1student student 37 Feb 7 16:45 inventory
[student@taijitao-serverdev-vars-playbook]$ cat inventory
[webserver]
servera.lab.example.com
- 在接下来的几个步骤中,您将创建一个playbook,用于安装Apache web服务器并为服务打开可访问的端口。playbook查询web服务器,以确保它是启动和运行。
首先,创建playbook.yml并在vars部分定义以下变量:web_pkg,它定义了要为web服务器安装的httpd;firewall_pkg,它定义firewalld;Web_ service为要管理的web服务的httpd;和firewall_service为要管理的防火墙服务的名称。添加python_pkg变量,为uri模块安装所需的包;和rule,该规则定义要打开的服务。
---
- name:Deploy and start apache httpd service
hosts: webserver
vars:
web_pkg: httpd
firewall_pkg: firewalld
web_service: httpd
firewall_service: firewalld
python_pkg: python-httplib2
rule: http
3.创建任务块并创建第一个任务,该任务应该使用yum模块来确保安装了所需软件包的最新版本。
tasks:
- name: required package are installed and up to date
yum:
name:
- "{{ web_pkg }}"
- "{{ firewall_pkg }}"
- "{{ python_pkg }}"
state: latest
如果你使用ansible-docyum来查看yum模块的语法,你会看到它的name指令将接受模块应该使用的包的列表,所以你不需要单独的任务来确保每个包都是最新的。
- 创建两个任务,以确保httpd和防火墙服务已启动和启用。
- name: the "{{firewall_service }}" service is started and enabled
service:
name: "{{ firewall_service }}"
enabled: true
state: started
- name: the "{{ web_service }}" service is started and enabled
service:
name: "{{ web_service }}"
enabled: true
state: started
根据ansible-doc service的文档,service模块的工作方式与yum模块不同。它的name指令只接受要使用的一个服务的名称。
可以通过使用with_items指令来编写一个任务,以确保这两个服务都已启动和启用,我们将在本课程后面介绍该指令。
- 添加一个任务,以确保某些内容在/var/www/htnil/index.html中。
- name: web content is in plane
copy:
content: "example web content"
dest: /var/www/html/index.html
- 添加一个任务,该任务将使用防火墙模块来确保防火墙端口对rule变量中命名的防火墙服务是开放的。
- name: the firewall port for {{ rule }} is open
firewalld:
service: "{{ rule }}"
permanent: true
immediate: true
state: enabled
- 创建一个新的play,该play将查询web服务,以确保所有内容都已正确配置。它应该在localhost上运行。因为这个事实,Ansible不需要改变身份,所以将become模块设置为false。uri模块可以用来检查URL。对于此任务,检查状态代码200以确认服务器正在运行并正确配置。
- name: verify the apache service
hosts: localhost
become: false
tasks:
- name: ensure the webserver is reachable
uri:
url: http://servera.lab.example.com
status_code: 200
- 完成后,playbooks应该如下所示。回顾play,确认两个plays都是正确的。
---
- name: Deploy and start apache httpd service
hosts: webserver
vars:
web_pkg: httpd
firewall_pkg: firewalld
web_service: httpd
firewall_service: firewalld
python_pkg: python-httplib2
rule: http
tasks:
- name: required package are installed and up to date
yum:
name:
- "{{ web_pkg }}"
- "{{ firewall_pkg }}"
- "{{ python_pkg }}"
state: latest
- name: the "{{firewall_service }}" service is started and enabled
service:
name: "{{ firewall_service }}"
enabled: true
state: started
- name: the "{{ web_service }}" service is started and enabled
service:
name: "{{ web_service }}"
enabled: true
state: started
- name: web content is in plane
copy:
content: "example web content"
dest: /var/www/html/index.html
- name: the firewall port for {{ rule }} is open
firewalld:
service: "{{ rule }}"
permanent: true
immediate: true
state: enabled
- name: verify the apache service
hosts: localhost
become: false
tasks:
- name: ensure the webserver is reachable
uri:
url: http://servera.lab.example.com
status_code: 200
- 在运行playbook之前,请执行ansible-playbook --syntax-check命令验证其语法是否正确。如果它报告任何错误,请在进行下一步之前纠正它们。您应该会看到类似如下的输出:
[student@taijitao-server dev-vars-playbook]$ ansible-playbook playbook.yaml --syntax-check
playbook: playbook.yaml
- 可以使用ansible-playbook命令运行playbook。当Ansible开始安装包,启动和启用服务,并确保web服务器可达时,观察输出。
[student@taijitao-server dev-vars-playbook]$ ansible-playbook playbook.yaml
实战二:
- 从taijitao-server,作为student用户,切换到~/demo_variables-playbook目录。
[student@taijitao-server ~]$ mkdir ~/demo_variables-playbook
[student@taijitao-server ~]$ cd demo_variables-playbook/
- 查看库存inventory文件。注意,有两个主机组,webservers和dbservers,它们是较大主机组servers的子机组。servers主机组的变量直接在目录文件中以旧的方式设置,包括将package设置为httpd的变量。
[webservers]
servera.lab.example.com
[dbservers]
servera.lab.example.com
[servers:children]
webservers
dbservers
[servers:vars]
ansible_user=devops
ansible_become=yes
package=httpd
3.为所有主机创建一个新的剧本playbook.yml。使用yum模块,安装由package变量指定的包。
---
- name: test
hosts: all
tasks:
- name: install the "{{ package }}" package
yum:
name: "{{ package }}"
state: latest
- 使用ansible-playbook命令运行playbook。观察Ansible安装httpd包时的输出。
[student@taijitao-serverdemo_variables-playbook]$ ansible-playbook playbook.yaml
PLAY [test] **********************************
TASK [Gathering Facts] *******************************************
ok: [servera.lab.example.com]
TASK [install the "httpd" package] *******************************
ok: [servera.lab.example.com]
PLAY RECAP *******************************************************
servera.lab.example.com : ok=2 changed=0 unreachable=0 failed=0
- 执行ad hoc命令确认httpd包已经成功安装。
[student@taijitao-serverdemo_variables-playbook]$ ansible servers -a 'yum list installed httpd'
[WARNING]: Consider using yum module ratherthan running yum
servera.lab.example.com | SUCCESS | rc=0>>
Loaded plugins: langpacks,search-disabled-repos
Installed Package
httpd.x86_64 2.4.6-45.el7 @rhel_dvd
- 创建group_vars目录和一个新文件group_vars/dbservers,以推荐的方式将dbservers主机组的变量包设置为mariadb-server。
[student@taijitao-serverdemo_variables-playbook]$ mkdir group_vars
[student@taijitao-server demo_variables-playbook]$cat group_vars/dbservers
package: mariadb-server
- 使用ansible-playbook命令重新运行playbook。观看Ansible安装mariadb-server包。
特定主机组dbservers的包变量优先于其父主机组服务器的包变量。
[student@taijitao-serverdemo_variables-playbook]$ ansible-playbook playbook.yaml
PLAY [test]***************************
TASK [Gathering Facts]************************
ok: [servera.lab.example.com]
TASK [install the "mariadb-server"package] ***************
changed: [servera.lab.example.com]
PLAY RECAP****************************
servera.lab.example.com : ok=2 changed=1 unreachable=0 failed=0
- 运行ad hoc命令确认mariadb- server安装成功。
[student@taijitao-serverdemo_variables-playbook]$ ansible servers -a 'yum list installedmariadb-server'
[WARNING]: Consider using yum module ratherthan running yum
servera.lab.example.com| SUCCESS | rc=0 >>
Loadedplugins: langpacks, search-disabled-repos
InstalledPackages
mariadb-server.x86_64 1:5.5.52-1.el7 @rhel_dvd
验证得出,group_vars主机组的变量优先级要高于inventory文件中全局主机组的变量。
- 对于servera.lab.example.com,将变量包设置为screen。为此,可以创建一个新目录host_vars和一个文件host_vars/servera。以推荐的方式设置变量。
[student@taijitao-serverdemo_variables-playbook]$ mkdir host_vars
[student@taijitao-serverdemo_variables-playbook]$ cat host_vars/servera.lab.example.com
package: screen
- 再运行一遍playbook。观察Ansible安装screeb包时的输出。
包变量的特定于主机的值将覆盖主机的主机组设置的任何值。
[student@taijitao-serverdemo_variables-playbook]$ ansible-playbook playbook.yaml
PLAY [test]***************************
TASK [Gathering Facts]************************
ok: [servera.lab.example.com]
TASK [install the "screen"package] *******************
changed: [servera.lab.example.com]
PLAY RECAP ****************************
servera.lab.example.com : ok=2 changed=1 unreachable=0 failed=0
\11. 运行ad hoc命令确认screen包已成功安装。
[student@taijitao-serverdemo_variables-playbook]$ ansible servers -a 'yum list installed screen'
[WARNING]: Consider using yum module ratherthan running yum
servera.lab.example.com | SUCCESS | rc=0>>
Loaded plugins: langpacks,search-disabled-repos
Installed Packages
screen.x86_64 4.1.0-0.23.20120314git3c2946.el7_2 @rhel_dvd
验证得出,host_vars主机的变量优先级要高于group_vars主机组的变量。
- 再次运行ansible-playbook命令,这次使用-e选项覆盖package变量。
[student@taijitao-serverdemo_variables-playbook]$ ansible-playbook playbook.yaml -e 'package=mutt'
PLAY [test] ***************************
TASK [Gathering Facts]************************
ok: [servera.lab.example.com]
TASK [install the "mutt" package]*********************
changed: [servera.lab.example.com]
PLAY RECAP****************************
servera.lab.example.com : ok=2 changed=1 unreachable=0 failed=0
- 运行ad hoc命令确认已安装mutt包。
[student@taijitao-serverdemo_variables-playbook]$ ansible servers -a 'yum list installed mutt'
[WARNING]: Consider using yum module ratherthan running yum
servera.lab.example.com | SUCCESS | rc=0>>
Loaded plugins: langpacks,search-disabled-repos
Installed Packages
mutt.x86_64 5:1.5.21-26.el7 @rhel_dvd
t] ***************************
TASK [Gathering Facts]************************
ok: [servera.lab.example.com]
TASK [install the “mutt” package]*********************
changed: [servera.lab.example.com]
PLAY RECAP****************************
servera.lab.example.com : ok=2 changed=1 unreachable=0 failed=0
13. 运行ad hoc命令确认已安装mutt包。
```sh
[student@taijitao-serverdemo_variables-playbook]$ ansible servers -a 'yum list installed mutt'
[WARNING]: Consider using yum module ratherthan running yum
servera.lab.example.com | SUCCESS | rc=0>>
Loaded plugins: langpacks,search-disabled-repos
Installed Packages
mutt.x86_64 5:1.5.21-26.el7 @rhel_dvd
验证得出,ad hoc中-e传递的变量优先级要高于host_vars主机的变量。
文章详细介绍了Ansible中变量的使用,包括命名规则、定义位置(全局、play、主机)、优先级,以及通过group_vars和host_vars目录管理主机和组变量。此外,还讨论了命令行定义变量、数组的使用以及注册变量来捕获任务输出。文中通过实战例子展示了如何在playbook中安装和管理Apache服务。
5385

被折叠的 条评论
为什么被折叠?



