添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

1.变量的基础介绍和应用

在ansible中使用变量,能让我们的工作变得更加灵活,在ansible中,变量的使用方式有很多种,我们慢慢聊。

先说说怎样定义变量, 变量名应该由字母、数字、下划线组成,变量名需要以字母开头,ansible内置的关键字不能作为变量名。

1.playbook中变量的定义和引用方法

如果我们想要在某个play中定义变量,可以借助vars关键字,示例语法1如下

- hosts: test70 vars : testvar1: testfile remote_user: root tasks: - name: task1 file : path: /testdir/ {{ testvar1 }} state: touch

上例中,先 使用vars关键字,表示在 当前play 中进行变量的相关设置。

vars关键字的下一级定义了一个变量,变量名为testvar1,变量值为testfile

当我们需要使用testvar1的变量值时,则需要引用这个变量,如你所见, 使用"{{变量名}}"可以引用对应的变量。

多变量定义语法格式

vars:
  testvar1: testfile
  testvar2: testfile2

格式2(块序列语法)

vars:
  - testvar1: testfile
  - testvar2: testfile2
- hosts: test70 remote_user: root vars: nginx: conf80: /etc/nginx/conf.d/ 80 .conf conf8080: /etc/nginx/conf.d/ 8080 .conf tasks: - name: task1 file : path: " {{nginx.conf80}} " state: touch - name: task2 file : path: " {{nginx.conf8080}} " state: touch

上述格式3,用类似"属性"的方式定义变量,上述格式3定义了两个变量,两个变量的值对应两个nginx配置文件路径

格式3定义变量,引用语法如下

"{{nginx.conf80}}"
"{{nginx['conf8080']}}"

这里需要注意一点就是, 当我们引用变量的的时候,如果变量被引用时,处于冒号之后(:),我们引用变量时必须使用双引号引起被引用的变量,否则会报语法错误 。如下所示:

path: "{{nginx.conf80}}"

2.文件中变量的定义和引用

除了能够在playbook中直接定义变量,我们还可以在某个文件中定义变量,然后再在playbook中引入对应的文件,引入文件后,playbook即可使用文件中定义的变量,当想要让别人阅读你的playbook,却不想让别人看到某些值,可以使用这种办法,因为别人在阅读playbook时,只能看到引入的变量名,但是看不到变量对应的值, 这种将变量分离到某个文件中的做法叫做"变量文件分离" "变量文件分离"除了能够隐藏某些值,还能够让你将不同类的信息放在不同的文件中,并且让这些信息与剧本主体分开

首先,我们来定义一个专门用来存放nginx相关变量的文件(文件名为nginx_vars.yml), 在文件中定义变量时,不要使用vars关键字,直接定义变量即可,定义变量的语法与在playbook中定义变量的几种语法相同

语法一示例:
  testvar1: testfile
  testvar2: testfile2
语法二示例:
  - testvar1: testfile
  - testvar2: testfile2
语法三示例:
nginx:
  conf80: /etc/nginx/conf.d/80.conf
  conf8080: /etc/nginx/conf.d/8080.conf
# cat nginx_vars.yml
nginx:
  conf80: /etc/nginx/conf.d/80.conf
  conf8080: /etc/nginx/conf.d/8080.conf

在playbook引用变量文件中的变量语法格式如下 ,在playbook中引入包含变量的文件时,需要使用"vars_files"关键字,被引入的文件需要以"- "开头,以YAML中块序列的语法引入,

- hosts: testB remote_user: root vars_files: - /testdir/ansible/ nginx_vars.yml tasks: - name: task1 file : path = {{nginx.conf80}} state = touch - name: task2 file : path ={{nginx[ ' conf8080 ' ]}} state = touch

上例中"vars_files"关键字只引入了一个变量文件,也可以 引入多个变量文件 每个被引入的文件都需要以"- "开头,示例如下

  vars_files:
  - /testdir/ansible/nginx_vars.yml
  - /testdir/ansible/other_vars.yml

"vars"关键字和"vars_files"关键字可以同时使用 ,如下

  vars:
  - conf90: /etc/nginx/conf.d/90.conf
  vars_files:
  - /testdir/ansible/nginx_vars.yml

3.引用变量常用模块简介

setup模块(引用主机内配置信息变量)

我们在前面运行一个playbook时,会发现默认都会运行一个名为"[Gathering Facts]"的任务,前文中已经大致的介绍过这个默认的任务,ansible其实是通过自动 调用了setup模块从而执行了"[Gathering Facts]"这个默认任务收集远程主机的相关信息(例如远程主机的IP地址,主机名,系统版本,硬件配置等信息) ,其实,这些被收集到的远程主机信息会保存在对应的变量中,当我们想要使用这些信息时,我们可以获取对应的变量,从而使用这些信息。

我们可以通过手动执行setup模块查看"[Gathering Facts]"任务收集到的信息,示例如下

ansible test70 -m setup

上述ad-hoc命令表示收集test70主机的相关信息,执行上述命令后,远程主机test70的相关信息将会输出到ansible主机的控制台上,返回的信息的格式是为了方便阅读格式化后的json格式。返回信息中,

"ansible_all_ipv4_addresses"表示远程主机中的所有ipv4地址,从其对应的值可以看出,test70主机上一共有4个ipv4地址。

"ansible_distribution"表示远程主机的系统发行版,从其对应的值可以看出test70主机的系统发行版为centos

"ansible_distribution_version"表示远程主机的系统版本号,从其对应的值与  "ansible_distribution" 的值可以看出test70主机的系统版本为centos7.4

"ansible_ens35"表示远程主机ens35网卡的相关信息,细心如你一定也发现了,

"ansible_memory_mb"表示远程主机的内存配置信息。

返回的信息的确很多,很全面,但是,并不是每一次我们都需要看这么多信息, 如果你只是想查看某一类信息,你可以通过关键字对信息进行过滤 ,这里的 关键字就相当于变量名(可以在playbook中直接引用) ,比如,我只是想要 查看远程主机的内存配置信息,那么我可以使用如下命令

ansible test70 -m setup -a 'filter=ansible_memory_mb'

上述命令表示通过"ansible_memory_mb"关键字对返回信息进行过滤,如你所见, 通过setup模块的filter参数可以指定需要过滤的关键字 ,这样ansible就只会将"ansible_memory_mb"的相关信息返回

我们可以使用通配符,对关键字进行相对模糊的过滤 ,示例如下

ansible test70 -m setup -a "filter=*mb*"

上述命令表示 返回所有包含mb的关键字对应的信息

除了这些系统自带信息以外,我们还能够在远程主机中写入一些自定义的信息 ,这些自定义信息也可以被setup模块收集到。

ansible默认会去目标主机的/etc/ansible/facts.d目录下查找主机中的自定义信息,并且规定,自定义信息需要写在以".fact"为后缀的文件中,同时,这些以".fact"为后缀的文件中的内容需要是INI格式或者是json格式的。

自定义信息示例如下,第一个是INI风格的,第二个是json风格的。

[root@test70 facts.d]# cat testinfo.fact
[testmsg]
msg1=This is the first custom test message
msg2=This is the second custom test message
" testmsg " :{ " msg1 " : " This is the first custom test message " , " msg2 " : " This is the second custom test message "

通过上述方式,我们可以在目标主机的本地自定义信息, 这些在远程主机本地自定义的信息被称为"local facts",当我们运行setup模块时,远程主机的"local facts"信息也会被收集,我们可以通过"ansible_local"关键字过滤远程主机的"local facts"信息 ,示例命令如下

ansible test70 -m setup -a "filter=ansible_local"

之前说过,当setup收集远程主机的"local facts"时,默认会查找远程主机的/etc/ansible/facts.d目录, 如果你把"local facts"信息文件放在了其他自定义路径,在使用setup模块时,需要使用"fact_path"参数指定对应的路径,假设,我把".fact"文件放在了目标主机的"/testdir"目录下,示例命令如下

ansible test70 -m setup -a 'fact_path=/testdir'

debug模块的playbook小示例,如下

[root@linux-test-no data]# cat test.yml
- hosts: testB
  remote_user: root
  tasks:
  - name: touch testfile
    file:
      path: /testdir/testfile
      state: touch
  - name: debug demo
    debug:
      msg: this is debug info,The test file has been touched

上例中,我们先在testB主机上touch了对应的文件,然后,利用debug模块在控制台中输出了我们想要显示的信息,如你所见, debug模块的msg参数可以指定我们想要输出的信息 ,上述playbook表示touch完对应的文件以后,在ansible控制台中输出我们指定的信息,那么我们运行一下这个测试剧本,看一下效果,如下

debug模块除了能够使用msg参数输出自定义的信息,还能够直接输出变量中的信息,通过debug模块直接输出变量信息需要使用var参数 ,示例如下

[root@linux-test-no data]# cat test2.yml
- hosts: testB
  remote_user: root
  vars:
    testvar: value of test variable
  tasks:
  - name: debug demo
    debug:
      var: testvar

上例虽然连接到了testB远程主机,但是并没有对testB做任何操作,只是在playbook中定义了一个变量,并且通过debug的var参数输出了这个变量的内容,只是为了单纯的演示debug模块的var参数的使用方法,上述playbook的执行效果如下

变量的名称以及变量的值都输出到了屏幕上, 这个功能可以帮助我们调试playbook中变量,让我们了解变量的值是否符合我们的要求。

使用debug的msg参数时也可以引用变量的值 ,这样我们自定义的信息就更加灵活了,示例如下。

[root@linux-test-no data]# cat test3.yml
- hosts: testB
  remote_user: root
  vars:
    testvar: test3
  tasks:
  - name: debug demo
    debug:
      msg: "value of testvar is : {{testvar}}"

上例中的msg自定义信息中引用了testvar变量的值,执行结果如下图

4.注册变量(将模块的返回值写入到变量)

ansible的模块在运行之后,其实都会返回一些"返回值",只是默认情况下,这些"返回值"并不会显示而已,我们可以把这些返回值写入到某个变量中,这样我们就能够通过引用对应的变量从而获取到这些返回值了,这种 将模块的返回值写入到变量中的方法被称为"注册变量" ,那么怎样将返回值注册到变量中呢?我们来看一个playbook示例

- hosts: testB remote_user: root tasks: - name: test shell shell: " echo test > /testdir/testshellfile " register: testvar - name: shell module return values debug: var : testvar

上例中共有两个任务,第一个任务使用shell模块在test70主机中创建了一个测试文件 /var/testshellfile,将字符"test"输入到了测试文件中,然后 使用"register"关键字将当前shell任务的返回值写入了名为"testvar"的变量中 第二个任务使用debug模块输出了第一个任务中的注册变量的值 ,没错,注册变量就是这么简单,使用register关键字指定对应的变量名即可。

上述playbook执行后,可以在控制台中看到名为"[shell module return values]"的任务中已经显示了第一个任务的返回值的信息,返回信息如下

从上述返回信息可以看出, 返回值是json格式的,上述返回值中包含一些键值对,比如 "changed": true 或 "cmd": "echo test > /testdir/testshellfile"等, 如果你只是想要获取到返回值中的某一项特定值,只需要指定键值对中的key即可 ,假设,我只是想要获取到上述返回信息中cmd的值,则可以使用如下两种语法(前文中已经对如下两种语法进行过示例,此处不再赘述)。

- name: shell module return values debug: msg: " {{testvar.cmd}} " - name: shell module return values debug: msg: " {{testvar['cmd']}} "

上述示例的返回信息为shell模块的返回值,如果你想要了解返回值中每一项的含义,则可以查看官方手册

https://docs.ansible.com/ansible/2.9/modules/shell_module.html#return-values

不同的模块,返回值也不尽相同,ansible官网对一些常见的返回值进行了总结,链接如下

https://docs.ansible.com/ansible/2.9/reference_appendices/common_return_values.html

5.提示用户输入信息并写入变量

在运行某些脚本时,有时候脚本会提示用户输入一些信息,脚本需要根据用户输入的信息决定下一步的动作,这种"交互"有时候是必须的,那么,在playbook中该怎样实现这种交互呢?我们可以这样做, 提示用户输入信息,然后将用户输入的信息存入到指定的变量中 ,当我们需要使用这些"输入的信息"时,只要引用对应的变量即可。

示例如下:

[root@linux-test-no data]# cat test.yml
- hosts: testB
  remote_user: root
  vars_prompt:
    - name: "your_name"
      prompt: "What is your name"
    - name: "your_age"
      prompt: "How old are you"
  tasks:
   - name: output vars
     debug:
      msg: Your name is {{your_name}},You are {{your_age}} years old.

如上例所示,我们 使用"vars_prompt"关键字创建了两个变量,这两个变量的名称分别为"your_name" 和 "your_age",当运行上例playbook时,会出现 "What is your name"的提示信息,然后用户输入的信息会存入到"your_name"变量中,之后,会出现 "How old are you"的提示信息,用户输入的信息会存入到"your_age"变量中,上例中的"output vars"任务会输出一句话,这句话中包含了上述两个变量的值 ,我们来看一下上例的执行效果。

如你所见,当你使用这种方式提示用户时,默认情况下 不会显示用户输入的信息 ,这种方式比较适合用户输入密码时的场景,如果你 想要 显示用户输入 的信息,可以使用如下示例中的方法。

  vars_prompt:
    - name: "your_name"
      prompt: "What is your name"
      private: no
    - name: "your_age"
      prompt: "How old are you"
      private: no

如上例所示,我们在定义" vars_prompt"中的变量时,使用private关键字,将变量的private属性设置为no即可, "private: no"表示变量值为非私有的,可见的,默认情况下 private值为yes,表示不可见。

我们还能为提示信息 设置默认值,即如果用户不输入任何信息,则将默认值赋予变量 ,示例playbook如下。

[root@linux-test-no data]# cat test2.yml
- hosts: testB
  remote_user: root
  vars_prompt:
    - name: "solution"
      prompt: "Choose the solution you want \n
      A: solutionA\n
      B: solutionB\n
      C: solutionC\n"
      private: no
      default: A
  tasks:
   - name: output vars
     debug:
      msg: The final solution is {{solution}}.

如上例所示,我们使用了 default关键字设置 了"solution" 变量的默认值 ,如果用户没有输入任何值(直接回车),则将"solution"变量的值设置为A,如果用户输入了值,则"solution"变量值为用户输入的值。

注册变量的应用(利用注册变量,创建用户和设置密码,示例如下)

[root@linux-test-no data]# cat test3.yml
- hosts: testB
  remote_user: root
  vars_prompt:
    - name: "user_name"
      prompt: "Enter user name"
      private: no
    - name: "user_password"
      prompt: "Enter user password"
      encrypt: "sha512_crypt"
      confirm: yes
  tasks:
   - name: create user
     user:
      name: "{{user_name}}"
      password: "{{user_password}}"

上述示例中 encrypt关键字表示对用户输入的信息进行哈希 ,encrypt: "sha512_crypt"表示使用sha512算法对用户输入的信息进行哈希,哈希后的字符串会存入到上例中的"user_password"变量中,这里这样做的原因是user模块创建用户时,输入的用户密码需要经过哈希。这里在执行这个playbook之前我们 需要先安装好passlib库,已支持encrypt关键字所指定的算法 。不然执行会报错,具体安装过程下面介绍。

上面"confirm"关键字实现类似确认密码的功能 ,我们在为用户设置密码时,通常需要输入两次完全相同的密码,才能够设置成功,通过"confirm"关键字就能实现类似的效果

passlin库安装过程:
#wget --no-check-certific ate https://pypi.python.org/packages/source/p/pip/pip-10.0.1.tar.gz >>/dev/null
# tar -xvf pip-10.0.1.tar.gz
# cd pip-10.0.1/
# python setup.py install
# pip install passlib

6.通过命令行传入变量

- hosts: testB remote_user: root tasks: - name: " Passing Variables On The Command Line " debug: msg: " {{pass_var}} " ansible -playbook cmdvar.yml --extra-vars " pass_var=cmdline pass var "

上例中的playbook中,并没有定义pass_var变量,而是直接引用了pass_var变量,我们可以在调用上述playbook时直接从命令行通过 --extra-vars选项传入 pass_var 变量

"--extra-vars" 选项可以传递对应的变量与变量值, "--extra-vars" 是长选项,对应的短选项是"-e",我们也可以一次性传入多个变量,变量之间用空格隔开 ,如下是传入变量的两种格式,相关一样,上面是键值对格式,下面是json格式。

ansible-playbook cmdvar.yml -e 'pass_var="test" pass_var1="test1"'
ansible-playbook cmdvar.yml -e '{"pass_var":"test","pass_var1":"test1"}'

这里如果playbook中定义了变量,然后命令行在传入变量的话,已传入的变量值为准。 命令行传入的变量的优先级要高于playbook中的变量

不仅ansible-playbook命令可以使用"-e"传递变量,ansible命令也同样可以 ,所以在执行ad-hoc命令时也可以使用同样的方法传入变量,如下

ansible testB -e "testvar=test" -m shell -a "echo {{testvar}}"

通过json格式传入稍微复杂一点的变量(下面表示一个变量传多个值)

ansible-playbook cmdvar.yml -e '{"countlist":["one","two","three","four"]}'

在剧本中引用上述命令传入的countlist变量时,如果想要获取到值"one",则可以使用如下两种语法引用变量

{{countlist[0]}} 或者 {{countlist.0}}

命令行不仅能够传入变量,还能传入变量文件 ,变量文件中的变量都会一并被传入,变量文件可以是json格式的,也可以是YAML格式的,命令如下

ansible-playbook cmdvar.yml -e "@/testdir/ansible/testvar"

如上述命令所示, 使用"@"符号加上变量文件的路径,即可在命令行中传入对应的变量文件 ,变量文件中的所有变量都可以在playbook中引用

7.在主机清单中配置变量

在清单中配置远程主机时,可以同时为主机配置对应的变量,当操作这个主机时,即可直接使用对应的变量。其他主机并不能引用到这个变量, 主机变量的生效范围只限于对应的主机 。示例如下,下面两种格式效果相同。

INI格式:
test70 ansible_host=10.1.1.70 testhostvar=test70_host_var testhostvar1=test70_host_var1

YAML格式

all:
hosts:
test70:
ansible_host: 10.1.1.70
ansible_port: 22
testhostvar: test70_host_var
testhostvar1: test70_host_var1

如果同时在主机组和主机中指定了相同变量,那边优先级为: 主机变量优先级 > 主机组变量 。然后就是 主机组变量优先级 > all特殊组

8.通过set_fact定义变量(类似于目标主机的全局变量)

set_fact是一个模块,我们可以通过set_fact模块在 tasks中定义变量,也可以将一个变量的值赋予另一个变量 ,示例如下:

[root@linux-test-no data]# cat test.yml
- hosts: testB
  remote_user: root
  tasks:
  - set_fact:
      testvar1: test1_string
  - shell: "echo test2_string"
    register: shellreturn
  - set_fact:
      testsf1: "{{testvar1}}"
      testsf2: "{{shellreturn.stdout}}"
  - debug:
      msg: "{{testsf1}} {{testsf2}}"

上例中,我们先定义了一个变量testvar1,又使用register将shell模块的返回值注册到了变量shellreturn中,

之后,使用set_fact模块将testvar1变量的值赋予了变量testsf1,将shellreturn变量中的stdout信息赋值给了testsf2变量,

最后,使用debug模块输出了testsf1与testsf2的值。

其实,通过set_fact模块创建的变量还有一个特殊性, 通过set_fact创建的变量就像主机上的facts信息一样,可以在之后的play中被引用(有点类似于全局变量), 注意此特性针对同一台主机 ,不能跨主机。什么意思呢?我们慢慢聊。

前文中已经总结过,默认情况下,每个play执行之前都会执行一个名为"[Gathering Facts]"的默认任务,这个任务会收集对应主机的相关信息,我们可以称这些信息为facts信息,我们已经总结过怎样通过变量引用这些facts信息,此处不再赘述,而通过set_fact模块创建的变量可以在之后play中被引用,就好像主机的facts信息可以在play中引用一样,

- hosts: testB remote_user: root vars: testvar1: tv1 tasks: - set_fact: testvar2: tv2 - debug: msg: " {{testvar1}} ----- {{testvar2}} " - hosts: testB remote_user: root tasks: - name: other play get testvar2 debug: msg: " {{testvar2}} " - name: other play get testvar1 debug: msg: " {{testvar1}} "

上例中一共有两个play, 第一个play中,我们通过两种方式创建了两个变量,第一个变量testvar1使用vas关键字创建,第二个变量使用set_fact创建

如果 执行上例的playbook,可以发现,这两个变量在第一个play中都可以正常的输出。但是在第二个play中,testvar2可以被正常输出了,testvar1却不能被正常输出,会出现未定义testvar1的错误 ,因为在第一个play中针对test70主机进行操作时,testvar1是通过vars关键字创建的,而testvar2是通过set_fact创建的,所以testvar2就好像test70的facts信息一样,可以在第二个play中引用到,而创建testvar1变量的方式则不能达到这种效果,虽然testvar2就像facts信息一样能被之后的play引用,但是在facts信息中并不能找到testvar2,只是"效果上"与facts信息相同罢了。

注册变量也可以在之后的play操作同一主机时被调用到 ,和上面讲的set_fact特性一样。注册变量中的信息是模块的返回值,这并不是我们自定义的信息,所以,如果想要在tasks中给变量自定义信息,并且在之后的play操作同一个主机时能够使用到之前在tasks中定义的变量时,则可以使用set_facts定义对应的变量。

9.常见内置变量详解

ansible中还有一些内置变量可供我们使用,当然,这些内置变量的变量名是被ansible保留的,我们定义变量时不能使用这些变量名。

内置变量ansible_version

通过内置变量ansible_version 获取到ansible的版本号 ,示例命令如下

ansible test70 -m debug -a "msg={{ansible_version}}"

内置变量hostvars

除了ansible_version,还有一些非常有用的内置变量。比如内置变量hostvars

hostvars可以帮助我们在操作当前主机时获取到其他主机中的信息。

假设,我想要在操作test70主机时获取到test71主机中的 facts信息 ,我该怎么办呢?示例如下

- name: " play 1: Gather facts of test71 " hosts: test71 remote_user: root - name: " play 2: Get facts of test71 when operating on test70 " hosts: test70 remote_user: root tasks: - debug: msg: " {{ hostvars['test71'] .ansible_ens35.ipv4}} "

上例中的第一个play中并没有任何的task,为什么还需要第一个play呢?如果你将上例的第一个play删除,只保留第二个play,运行时则会报错,这是因为,虽然第一个play中没有任何task,但是当第一个play执行时,默认会调用"[Gathering Facts]"任务,也就是说,默认会收集test71主机的facts信息, 只有被收集过的facts信息才能被后面的play引用到 ,如果压根没有收集对应主机的facts信息,即使使用hostvars内置变量,也无法获取到对应主机的facts信息

其实,除了facts信息,我们还能够利用 hostvars内置变量从别的主机中获取到其他类型的一些变量信息,比如,其他主机的注册变量、主机变量、组变量等信息 ,我们先来看一个获取其他主机的 注册变量的小示例 ,如下:
下面中的 gather_facts:no 的作用是执行这个play的时候不去获取主机的facts信息 ,也就是讲不去调用默认的"[Gathering Facts]"任务

- hosts: test71 remote_user: root gather_facts: no tasks: - shell: " echo register_var_in_play1 " register: shellreturn - hosts: test70 remote_user: root gather_facts: no tasks: - debug: msg: " {{hostvars.test71.shellreturn.stdout}} "

通过hostvars和set_fact进行跨主机自定义变量引用

- hosts: test71 remote_user: root gather_facts: no tasks: - set_fact: testvar: " testvar_in_71 " - debug: msg: " {{testvar}} " - hosts: test70 remote_user: root gather_facts: no tasks: - debug: msg: " {{hostvars.test71.testvar}} "

内置变量inventory_hostname

通过inventory_hostname变量可以获取到被操作的当前主机的主机名称,这里所说的主机名称并不是linux系统的主机名,而是对应主机在清单中配置的名称

可以看到,此play每操作一个主机,都会将当前play操作的所有主机的主机名列表返回。

没错,inventory_hostname和play_hosts都是返回主机名,只不过, inventory_hostname只返回 当前被操作 的主机的主机名,而play_hosts则返回当前play中 所有被操作主机 的主机名列表。

内置变量groups

通过groups内置变量可以 获取到清单中" 所有分组"的"分组信息 "

内置变量group_names

通过内置变量group_names获取到 当前主机所在分组的组名

内置变量inventory_dir

我们可以通过inventory_dir变量 获取到ansible主机中清单文件的存放路径 ,我使用的是默认的清单文件/etc/ansible/hosts,所以,inventory_dir变量对应的值为/etc/ansible,如下例所示