Ansible-playbook 运维笔记

之前详细介绍了Ansible的安装, 配置, 以及Ansible常用模块的使用. 下面对Ansible的playbook用法做一小结。

为什么引入playbook?
一般运维人员完成一个任务, 比如安装部署一个httpd服务会需要多个模块(一个模块也可以称之为task)提供功能来完成。而playbook就是组织多个task的容器,它的实质就是一个文件,有着特定的组织格式,它采用的语法格式是YAML(Yet Another Markup Language)。YAML语法能够简单的表示散列表,字典等数据结构。简单来说, playbook是由一个或多个模块组成的,使用多个不同的模块,完成一件事情。 

Ansible核心功能
-  pyYAML用于ansible编写剧本所使用的语言格式(saltstack---python);
-  rsync-ini语法, sersync-xml语法, nsible-pyYAML语法;
-  paramiko远程连接与数据传输;
-  Jinja2用于编写ansible的模板信息;

YAML三板斧
缩进: YAML使用一个固定的缩进风格表示层级结构,每个缩进由两个空格组成, 不能使用tabs;
冒号: 以冒号结尾的除外,其他所有冒号后面所有必须有空格;
短横线: 表示列表项,使用一个短横杠加一个空格。多个项使用同样的缩进级别作为同一列表;

YAML基本语法
Ansible-playbook采用YAML语法编写。连续的项目(即列表)用 -减号来表示,key/value(字典)用冒号:分隔。

列表:每一个列表成员前面都要有一个短横线和一个空格

1

2

3

4

5

6

7

8

fruits:

    - Apple

    - Orange

    - Strawberry

    - Mango

 

或者:

fruits: ['Apple''Orange''Strawberry''Mango']

字典:每一个成员由键值对组成,注意冒号后面要有空格

1

2

3

4

5

6

martin:

    name: Martin D'vloper

    job: Developer

    skill: Elite

或者

martin: {name: Martin D'vloper, job: Developer, skill: Elite}

列表和字典可以混合使用

1

2

3

4

5

6

7

8

9

10

11

12

13

14

-  martin:

    name: Martin D'vloper

    job: Developer

    skills:

      - python

      - perl

      - pascal

-  tabitha:

    name: Tabitha Bitumen

    job: Developer

    skills:

      - lisp

      - fortran

      - erlang

示例如下:
[root@localhost ~]# cat httpd.yaml

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

---

- hosts: control-node    #将要执行任务的主机,已经在hosts文件中定义好了,可是单个主机或主机组

  remote_user: root      #在目标主机上执行任务时的用户身份

  vars:

    - pkg: httpd

  tasks:

    - name: "install httpd package."

      yum: name={{ pkg }}  state=installed

    - name: "copy httpd configure file to remote host."

      copy: src=/root/conf/httpd.conf dest=/etc/httpd/conf/httpd.conf

      notify: restart httpd     #当这个任务执行状态发生改变时,触发handlers执行.

    - name: "boot httpd service."

      service: name=httpd state=started

  handlers:                     #handlers与tasks是同一级别

    - name: restart httpd

      service: name=httpd state=restarted

playbook语法特性
1.  以 --- (三个减号)开始,必须顶行写;
2. 次行开始写Playbook的内容,但是一般要求写明该playbook的功能;
3. 严格缩进,并且不能用Tab键缩进;
4. 缩进级别必须是一致的,同样的缩进代表同样的级别,程序判别配置的级别是通过缩进结合换行来实现的;
5. K/V的值可同行写,也可换行写。同行使用 :分隔,换行写需要以 - 分隔;

playbook基础组件
Hosts:运行执行任务(task)的目标主机
remote_user:在远程主机上执行任务的用户
tasks:任务列表
handlers:任务,与tasks不同的是只有在接受到通知时才会被触发
templates:使用模板语言的文本文件,使用jinja2语法。
variables:变量,变量替换{{ variable_name }}

整个playbook是以task为中心,表明要执行的任务。hosts和remote_user表明在远程主机以何种身份执行,其他组件让其能够更加灵活。下面介绍插件:

1.  variable
变量定义在资产 (inventory) 中, 默认就是/etc/ansible/hosts文件中

1

2

3

4

5

6

7

8

9

10

11

12

主机变量:

192.168.200.136 http_port=808 maxRequestsPerChild=808

192.168.200.137 http_port=8080 maxRequestsPerChild=909

 

主机组变量:

[websers]

192.168.200.136

192.168.200.137

 

[websers:vars] 

ntp_server=ntp.exampl.com

proxy=proxy.exampl.com

变量定义在playbook中

1

2

3

- hosts: webservers

  vars:

    http_port: 80

使用facts变量

1

2

3

4

facts变量是由setup模块获取远程主机的信息。

 

用法:

# ansible 192.168.200.136 -m setup

在roles中定义变量, 这个后面会介绍到.

ansible-playbook 命令中传入参数

1

2

使用 -e选项传入参数

# ansible-playbook 192.168.200.136 -e "httpd_port=808" httpd04.yml

变量的引用

1

{{ var_name }}

2.  templates
它是一个模块功能,与copy不同的是他的文本文件采用了jinga2语法,jinga2基本语法如下:

1

2

3

4

5

6

7

8

9

10

11

12

字面量:

  字符串:使用单引号或双引号

  数字:整型,浮点数

  列表:{item1,item2,...}

  字典:{key1:value1,key2:value2,...}

  布尔型:true/false

算术运算:

  +,-,*,/,//,%,**

比较运算:

  ==,!=,>,>=,<,<=

逻辑运算:

  and,or,not

注意:template只能在palybook中使用。

3.  tasks
执行的模块命令

1

2

3

4

5

6

7

8

9

10

11

12

13

格式:

  action:模块参数(此种方式只在较新的版本中出现)

  module:参数(已键值对的形式出现)

 

每一个task都有一个名称,用于标记此任务。任务示例:

  name: install httpd

  yum: name=httpd state=present

 

注意:shell和command没有参数,可在后面直接跟命令

  shell: ss -tnl | grep :80

 

1)某任务的运行状态为changed后,可通过相应的notify通知相应的handlers

2)任务可以通过tags打标签,然后通过palybook命令-t选项调用.

playbook命令及调用方式

用法:
ansible-playbook  <filename.yml> ... [options]

<filename.yml>: yaml格式的playbook文件路径,必须指明
[options]: 选项

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

Options:

--ask-vault-pass    

             #ask for vault password

             #加密playbook文件时提示输入密码

  -C, --check         

             #don't make any changes; instead, try to predict some of the changes that may occur

             #模拟执行,不会真正在机器上执行(查看执行会产生什么变化)。即并不在远程主机上执行,只是测试。

  -D, --diff          

             #when changing (small) files and templates, show the differences in those files; works great with --check

             #当更新的文件数及内容较少时,该选项可显示这些文件不同的地方,该选项结合-C用会有较好的效果

  -e EXTRA_VARS, --extra-vars=EXTRA_VARS

             #set additional variables as key=value or YAML/JSON

             #在Playbook中引入外部参数变量

  --flush-cache       

             #clear the fact cache

             #清理fact缓存,将fact清除到的远程主机缓存

  --force-handlers    

             #run handlers even if a task fails

             #强制运行handlers的任务,即使在任务失败的情况下

  -f FORKS, --forks=FORKS

             #specify number of parallel processes to use(default=5)

             #并行任务数。FORKS被指定为一个整数,默认是5

  -h, --help          

             #show this help message and exit

             #打开帮助文档API

  -i INVENTORY, --inventory-file=INVENTORY

             #specify inventory host path (default=/etc/ansible/hosts) or comma separated host list.

             #指定要读取的Inventory清单文件

  -l SUBSET, --limit=SUBSET

             #further limit selected hosts to an additional pattern

             #限定执行的主机范围

  --list-hosts        

             #outputs a list of matching hosts; does not execute anything else

             #列出执行匹配到的主机,但并不会执行任何动作。

  --list-tags         

             #list all available tags

             #列出所有可用的tags

  --list-tasks        

             #list all tasks that would be executed

             #列出所有即将被执行的任务

  -M MODULE_PATH, --module-path=MODULE_PATH

             #specify path(s) to module library (default=None)

             #要执行的模块的路径

  --new-vault-password-file=NEW_VAULT_PASSWORD_FILE

             #new vault password file for rekey

             #

  --output=OUTPUT_FILE

             #output file name for encrypt or decrypt; use - for stdout

             #

  --skip-tags=SKIP_TAGS

             #only run plays and tasks whose tags do not match these values

             #跳过指定的tags任务

  --start-at-task=START_AT_TASK

             #start the playbook at the task matching this name

             #从第几条任务(START_AT_TASK)开始执行

  --step              

             #one-step-at-a-time: confirm each task before running

             #逐步执行Playbook定义的任务,并经人工确认后继续执行下一步任务

  --syntax-check      

             #perform a syntax check on the playbook, but do not execute it

             #检查Playbook中的语法书写,并不实际执行

  -t TAGS, --tags=TAGS

             #only run plays and tasks tagged with these values

             #指定执行该tags的任务

  --vault-password-file=VAULT_PASSWORD_FILE

             #vault password file

             #

  -v, --verbose       

             #verbose mode (-vvv for more, -vvvv to enable connection debugging)

             #执行详细输出

  --version           

             #show program's version number and exit

             #显示版本

  

  ############Connection Options,即下面时连接权限############

    control as whom and how to connect to hosts

  

    -k, --ask-pass    

             #ask for connection password

             #

    --private-key=PRIVATE_KEY_FILE, --key-file=PRIVATE_KEY_FILE

             #use this file to authenticate the connection

             #

    -u REMOTE_USER, --user=REMOTE_USER

             #connect as this user (default=None)

             #指定远程主机以USERNAME运行命令

    -c CONNECTION, --connection=CONNECTION

             #connection type to use (default=smart)

             #指定连接方式,可用选项paramiko (SSH)、ssh、local,local方式常用于crontab和kickstarts

    -T TIMEOUT, --timeout=TIMEOUT

             #override the connection timeout in seconds(default=10)

             #SSH连接超时时间设定,默认10s

    --ssh-common-args=SSH_COMMON_ARGS

             #specify common arguments to pass to sftp/scp/ssh (e.g.ProxyCommand)

             #

    --sftp-extra-args=SFTP_EXTRA_ARGS

             #specify extra arguments to pass to sftp only (e.g. -f, -l)

             #

    --scp-extra-args=SCP_EXTRA_ARGS

             #specify extra arguments to pass to scp only (e.g. -l)

             #

    --ssh-extra-args=SSH_EXTRA_ARGS

             #specify extra arguments to pass to ssh only (e.g. -R)

             #

  

  ############Privilege Escalation Options, 即下面时权限提升权限############

    control how and which user you become as on target hosts

  

    -s, --sudo        

             #run operations with sudo (nopasswd) (deprecated, use become)

             #相当于Linux系统下的sudo命令

    -U SUDO_USER, --sudo-user=SUDO_USER

             #desired sudo user (default=root) (deprecated, use become)

             #使用sudo,相当于Linux下的sudo命令

    -S, --su          

             #run operations with su (deprecated, use become)

             #

    -R SU_USER, --su-user=SU_USER

             #run operations with su as this user (default=root)(deprecated, use become)

    -b, --become      

             #run operations with become (does not imply password prompting)

             #

    --become-method=BECOME_METHOD

             #privilege escalation method to use (default=sudo),valid choices: [ sudo | su | pbrun | pfexec | doas |dzdo | ksu | runas ]

             #

    --become-user=BECOME_USER

             #run operations as this user (default=root)

             #

    --ask-sudo-pass   

             #ask for sudo password (deprecated, use become)

             #传递sudo密码到远程主机,来保证sudo命令的正常运行

    --ask-su-pass     

             #ask for su password (deprecated, use become)

             #

    -K, --ask-become-pass

             #ask for privilege escalation password

             #

ansible-playbook需要注意的两个命令
1)检查语法,只检查是否是yaml语法格式。并不做逻辑校验。(记住这个要经常使用, 它是判断语法是否正确!!!)
# ansible-playbook --syntax-check kevin.yml
2)模拟执行(不是真的执行)
# ansible-playbook -C kevin.yml

关闭Facts
如果不需要使用主机的任何fact数据,可以选择关闭fact数据的获取,这样有利于增强Ansible面对大量系统的push模块。
在playbook中关闭Facts方法(gather_facts: no):

1

2

3

---

- hosts: webserver

  gather_facts: no

palybook书写格式

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

---                                   # 也可以不使用这一行。可以省略。

- hosts: 172.16.60.211                #处理指定服务器.   - (空格)hosts:(空格)172.16.20.211

  task:                               #剧本所要干的事情; (空格)(空格)task:

  - name:                             #(两个空格)-(空格)name。

    commandecho hello clsn linux    #(四个空格)command:(空格)

 

需要注意:

Task任务里的name可以省略不写,将-(空格)放到下一行模块墙面。例如:

---                                  

- hosts: 172.16.60.211              

  task:                            

  commandecho hello clsn linux

 

小示例:

[root@localhost ansible]# cat haha.yaml

---

- hosts: test_host

  remote_user: root

  gather_facts: no

  tasks:

     file: path=/opt/task1.txt state=touch

palybook格式示例

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

[root@ansible-server ~]# vim /etc/ansible/test.yaml

- hosts: 172.16.60.213

  tasks:

    - name: Install Rsync

      yum: name=rsync state=installed

 

playbook检查方法

[root@ansible-server ~]# ansible-playbook --syntax-check /etc/ansible/test.yaml

 

playbook: /etc/ansible/test.yaml

[root@ansible-server ~]# ansible-playbook -C /etc/ansible/test.yaml

 

PLAY [172.16.60.213] *******************************************************************************************************************

 

TASK [Gathering Facts] *****************************************************************************************************************

ok: [172.16.60.213]

 

TASK [Install Rsync] *******************************************************************************************************************

ok: [172.16.60.213]

 

PLAY RECAP *****************************************************************************************************************************

172.16.60.213              : ok=2    changed=0    unreachable=0    failed=0 

 

上面两个检查命令, 第一个是进行playbook剧本配置信息语法检查; 第二个是模拟playbook剧本执行(彩排)

palybook剧本文件示例

ansible-playbook编写内容扩展剧本任务编写多个任务

1

2

3

4

5

6

- hosts: all

  tasks:

    - name: restart-network

      cron: name='restart network' minute=00 hour=00 job='/usr/sbin/ntpdate time.nist.gov >/dev/null 2>&1'

    - name: sync time

      cron: name='sync time' minute=*/5 job="/usr/sbin/ntpdate pool.ntp.com >/dev/null 2>&1"

剧本编写内容扩展:剧本任务编写多个主机

1

2

3

4

5

6

7

8

9

10

11

- hosts: 172.16.60.7

  tasks:

    - name: restart-network

      cron: name='restart network' minute=00 hour=00 job='/usr/sbin/ntpdate time.nist.gov >/dev/null 2>&1'

    - name: sync time

      cron: name='sync time' minute=*/5 job="/usr/sbin/ntpdate pool.ntp.com >/dev/null 2>&1"

 

- hosts: 172.16.60.31

  tasks:

    - name: show ip addr to file

      shell: echo $(hostname -i) >> /tmp/ip.txt

playbook剧本编写方式
-  多主机单任务编写方式
-  多主机多任务编写方式
-  不同主机多任务编写方式

来看一个比较完整的ansible的yml文件写法:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

---

- host: webservers       ###要管理的远程服务器组名称, 服务器地址维护在/etc/ansible/hosts 里, 也可以直接写地址

  vars:

    port: 8081           ###定义了一个变量 端口号

  remote_user: root      ###远程登录后用什么用户执行

  

  pre_tasks:             ###执行正式 task 之前执行的任务

  - name: pre task       ###任务名称

    shell: echo 'execute pre task'      ###执行一行 shell 命令, 支持 >> 等符号

  

  roles:            ###引入 roles, 可以理解为引用了一个其他项目 ansible 包, 引用的 roles 可以是另一个完整的 ansible 脚本

  - role: my_role   ###要引用的 role 名称

    when: "ansible_os_family == 'RedHat'"   ###判断条件, ansible_os_family 是一个内置变量, 可直接使用

  

  tasks:           ###按顺序执行以下 task

  - include: my_tasks/some_task.yml       ###可以引入其他 yml 文件

  - name: get hostname        ###这是一个 task, 名称

    commandcat log.log      ###执行一行 command , 和 shell 类似, 但是不支持 >> 等操作符

    register: result          ###执行的结果, 设到 result 这个变量中, 后面可以使用

  - name: set hostname

    shell: cat {{result.stdout}} >> host.text

  - name: task1

    commandecho 'execute task1'

  - name: task2 start apache

    service:             ###启动 httpd 服务器, service 是一个 ansible 内置模块, 读者可以自行查看更多模块, 包括下载复制等等

      name: httpd

      state: started

      tags:

        - apache        ###这是一个标签, 可以用 ansible-playbook main.yml --tags "apache" 指定只执行这个任务

  - name: copy and set value of index.html

    template:           ###这是一个复制方法, 也叫模块, 并且.j2文件中可以使用{{}}来设置需要替换的变量

      src: templates/index.html.j2

      dest: /etc/httpd/index.html

    notify:             ###唤醒执行后面的 handlers 中名字叫 restart apache 的任务

    - restart apache

  

  post_tasks:           ###最后需要执行的任务

  - name: posy task

    shell: echo 'execute post task'

  

  handlers:

  - name: restart apache

    debug:              ###这是一个打印模块

      msg: start restart apche

palybook剧本中的方法

1.  handlers 任务触发
在需要被监控的任务(tasks)中定义一个notify,只有当这个任务被执行时,才会触发notify对应的handlers去执行相应操作。例如配置文件被修改后,有可能需要重启程序,此时我们可以配置一个handlers,类似触发器。注意:handlers下的name名称必须要和它对应的notify名称相同!否则不会执行!!

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

[root@localhost ~]# cat httpd.yaml

---

- hosts: control-node

  remote_user: root

  vars:

    - pkg: httpd

  tasks:

    - name: "install httpd package."

      yum: name={{ pkg }}  state=installed

    - name: "copy httpd configure file to remote host."

      copy: src=/root/conf/httpd.conf dest=/etc/httpd/conf/httpd.conf

      notify: restart httpd

    - name: "boot httpd service."

      service: name=httpd state=started

  handlers:

    - name: restart httpd

      service: name=httpd state=restarted

########  在使用handlers的过程中,需要注意下面几点  ########
1. handlers只有在其所在的任务被执行完时,它才会被运行;如果一个任务中定义了notify调用Handlers,但由于条件判断等原因,该任务未被执行,则Handlers同样不会被执行。
2. handlers只会在Play的末尾运行一次;如果想在一个Playbook的中间运行handlers,则需要使用meta模块来实现,例如:-meta: flush_handlers。
3. 可以直接在Handlers中使用notify选项,实现Handlers调用Handlers。
4. 可以使用listen关键字,在一个tasks任务中一次性notify多个handler。即将多个handler分为"一组",使用相同的"组名"即可,当notify对应的值为"组名"时,"组"内的所有handler都会被notify。
5. 如果一个Play在运行到调用handlers的语句之前失败了,那么这个handlers将不会被执行。但是可以使用mega模块的--force-handlers选项来强制执行handlers,即使在handlers所在Play中途运行失败也能执行。需要注意:--force-handlers参数主要针对即使playbook执行失败,也要执行代码块成功了的handlers(即执行成功的task任务), 如果代码块本身执行失败(即执行失败的task任务),那么它所对应的handlers应当不会被执行!

handlers可以理解成另一种tasks,handlers是另一种"任务列表",可以理解handlers和tasks是"平级关系",所以他们的缩进相同。handlers的任务会被tasks中的任务进行"调用",但是,被"调用"并不意味着一定会执行,只有当tasks中的任务"真正执行"以后,handlers中被调用的任务才会执行,如果tasks中的任务并没有做出任何实际的操作,那么handlers中的任务即使被"调用",也并不会执行。handlers中可以有多个任务,被tasks中不同的任务notify。

场景1:headlers在所有tasks任务被执行完时才执行。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

[root@localhost ansible]# cat haha.yaml

---

- hosts: test_host

  remote_user: root

  become: yes

  become_method: sudo

  tasks:

    - name: make file task1

      file: path=/opt/task1.txt state=touch

      notify: task1

    - name: make file task2

      file: path=/opt/task2.txt state=touch

      notify: task2

  handlers:

    - name: task1

      file: path=/opt/task1.txt mode=777 owner=root group=root

    - name: task2

      file: path=/opt/task2.txt src=/opt/task2.txt dest=/opt/heihei state=link force=yes

 

执行结果:

[root@localhost ansible]# ansible-playbook haha.yaml

 

PLAY [test_host] *********************************************************************************************************************************

 

TASK [Gathering Facts] ***************************************************************************************************************************

ok: [172.16.60.233]

ok: [172.16.60.234]

 

TASK [make file task1] ***************************************************************************************************************************

changed: [172.16.60.234]

changed: [172.16.60.233]

 

TASK [make file task2] ***************************************************************************************************************************

changed: [172.16.60.233]

changed: [172.16.60.234]

 

RUNNING HANDLER [task1] **************************************************************************************************************************

changed: [172.16.60.234]

changed: [172.16.60.233]

 

RUNNING HANDLER [task2] **************************************************************************************************************************

changed: [172.16.60.233]

changed: [172.16.60.234]

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.233              : ok=5    changed=4    unreachable=0    failed=0  

172.16.60.234              : ok=5    changed=4    unreachable=0    failed=0

从上面运行结果看出,Handlers执行的顺序与Handlers在playbook中定义的顺序是相同的,与"handler"被notify的顺序无关。

场景2:使用meta模块,headlers会在它所对应的task任务执行完后立即被触发并执行,即在playbook的中间环节运行。
默认情况下,所有的task执行完毕后,才会执行各个handles,并不是执行完某个task后,立即执行相应的handler,如果想要在执行完某些task以后立即执行对应的handlre,那么需要使用meta模块。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

[root@localhost ansible]# cat haha.yaml

---

- hosts: test_host

  remote_user: root

  become: yes

  become_method: sudo

  tasks:

    - name: make file task1

      file: path=/opt/task1.txt state=touch

      notify: task1

    - meta: flush_handlers

    - name: make file task2

      file: path=/opt/task2.txt state=touch

      notify: task2

  handlers:

    - name: task1

      file: path=/opt/task1.txt mode=777 owner=root group=root

    - name: task2

      file: path=/opt/task2.txt src=/opt/task2.txt dest=/opt/heihei state=link force=yes

 

执行结果:

[root@localhost ansible]# ansible-playbook haha.yaml

 

PLAY [test_host] *********************************************************************************************************************************

 

TASK [Gathering Facts] ***************************************************************************************************************************

ok: [172.16.60.234]

ok: [172.16.60.233]

 

TASK [make file task1] ***************************************************************************************************************************

changed: [172.16.60.234]

changed: [172.16.60.233]

 

RUNNING HANDLER [task1] **************************************************************************************************************************

changed: [172.16.60.234]

changed: [172.16.60.233]

 

TASK [make file task2] ***************************************************************************************************************************

changed: [172.16.60.234]

changed: [172.16.60.233]

 

RUNNING HANDLER [task2] **************************************************************************************************************************

changed: [172.16.60.234]

changed: [172.16.60.233]

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.233              : ok=5    changed=4    unreachable=0    failed=0  

172.16.60.234              : ok=5    changed=4    unreachable=0    failed=0

上面使用了meta模块后,注意它的执行顺序于场景1做下对比!

场景3:Handlers调用Handlers
若实现Handlers调用Handlers,则直接在Handlers中使用notify选项即可以。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

[root@localhost ansible]# cat haha.yaml

---

- hosts: test_host

  remote_user: root

  become: yes

  become_method: sudo

  tasks:

    - name: make file task1

      file: path=/opt/task1.txt state=touch

      notify: task1

    - name: make file task2

      file: path=/opt/task2.txt state=touch

        

  handlers:

    - name: task1

      file: path=/opt/task1.txt mode=777 owner=root group=root

      notify: task2

    - name: task2

      file: path=/opt/task2.txt src=/opt/task2.txt dest=/opt/heihei state=link force=yes

  

执行结果:

[root@localhost ansible]# ansible-playbook haha.yaml            

  

PLAY [test_host] *********************************************************************************************************************************

  

TASK [Gathering Facts] ***************************************************************************************************************************

ok: [172.16.60.234]

ok: [172.16.60.233]

  

TASK [make file task1] ***************************************************************************************************************************

changed: [172.16.60.233]

changed: [172.16.60.234]

  

TASK [make file task2] ***************************************************************************************************************************

changed: [172.16.60.234]

changed: [172.16.60.233]

  

RUNNING HANDLER [task1] **************************************************************************************************************************

changed: [172.16.60.234]

changed: [172.16.60.233]

  

RUNNING HANDLER [task2] **************************************************************************************************************************

changed: [172.16.60.234]

changed: [172.16.60.233]

  

PLAY RECAP ***************************************************************************************************************************************

172.16.60.233              : ok=5    changed=4    unreachable=0    failed=0 

172.16.60.234              : ok=5    changed=4    unreachable=0    failed=0

 

 

注意:上面执行的顺序是:make file task1 > make file task2 > task1 > task2

 

====================================================================

也可以改成下面的方式:实现Handlers调用Handlers

 

[root@localhost ansible]# cat haha.yaml

---

- hosts: test_host

  remote_user: root

  become: yes

  become_method: sudo

  tasks:

    - name: make file task1

      file: path=/opt/task1.txt state=touch

      notify: task1

 

  handlers:

    - name: task1

      file: path=/opt/task1.txt mode=777 owner=root group=root

      notify: task2

    - name: task2

      file: path=/opt/task2.txt state=touch

      notify: task3

    - name: task3

      file: path=/opt/task2.txt src=/opt/task2.txt dest=/opt/heihei state=link force=yes

 

执行结果:

[root@localhost ansible]# ansible-playbook haha.yaml

 

PLAY [test_host] *********************************************************************************************************************************

 

TASK [Gathering Facts] ***************************************************************************************************************************

ok: [172.16.60.233]

ok: [172.16.60.234]

 

TASK [make file task1] ***************************************************************************************************************************

changed: [172.16.60.233]

changed: [172.16.60.234]

 

RUNNING HANDLER [task1] **************************************************************************************************************************

changed: [172.16.60.233]

changed: [172.16.60.234]

 

RUNNING HANDLER [task2] **************************************************************************************************************************

changed: [172.16.60.234]

changed: [172.16.60.233]

 

RUNNING HANDLER [task3] **************************************************************************************************************************

changed: [172.16.60.234]

changed: [172.16.60.233]

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.233              : ok=5    changed=4    unreachable=0    failed=0  

172.16.60.234              : ok=5    changed=4    unreachable=0    failed=0 

 

注意:上面的执行顺序是:make file task1 > task1 > task2 > task3

场景4:使用listen关键字,在一个tasks任务中一次性notify多个handler
怎么才能一次性notify多个handler呢?如果尝试将多个handler使用相同的name呢?其实这样并不可行!因为当多个handler的name相同时,只有一个handler会被执行。要想实现一次notify多个handler,需要借助一个关键字,它就是"listen",可以把listen理解成"组名",可以把多个handler分成"组",当需要一次性notify多个handler时,只要将多个handler分为"一组",使用相同的"组名"即可,当notify对应的值为"组名"时,"组"内的所有handler都会被notify。需要注意:listen的名称要和notify名称保持一致!

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

[root@localhost ansible]# cat haha.yaml

---

- hosts: test_host

  remote_user: root

  become: yes

  become_method: sudo

  tasks:

    - name: make file task1

      file: path=/opt/task1.txt state=touch

      notify: group1_handler

  handlers:

    - name: task1

      listen: group1_handler

      file: path=/opt/task1.txt mode=777 owner=root group=root

    - name: task2

      listen: group1_handler

      file: path=/opt/task1.txt src=/opt/task1.txt dest=/opt/heihei state=link force=yes

    - name: task3

      listen: group1_handler

      shell: echo "this is test,haha...." >> /opt/task1.txt

 

执行结果:

[root@localhost ansible]# ansible-playbook haha.yaml

 

PLAY [test_host] *********************************************************************************************************************************

 

TASK [Gathering Facts] ***************************************************************************************************************************

ok: [172.16.60.233]

ok: [172.16.60.234]

 

TASK [make file task1] ***************************************************************************************************************************

changed: [172.16.60.234]

changed: [172.16.60.233]

 

RUNNING HANDLER [task1] **************************************************************************************************************************

changed: [172.16.60.234]

changed: [172.16.60.233]

 

RUNNING HANDLER [task2] **************************************************************************************************************************

changed: [172.16.60.234]

changed: [172.16.60.233]

 

RUNNING HANDLER [task3] **************************************************************************************************************************

changed: [172.16.60.234]

changed: [172.16.60.233]

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.233              : ok=5    changed=4    unreachable=0    failed=0  

172.16.60.234              : ok=5    changed=4    unreachable=0    failed=0

场景5:使用--force-handlers选项来强制执行handlers
当playbook剧本执行失败以后,handlers可能并没有被触发,也就不会执行了!如果想不管task任务是否成功执行,都强制执行handlers。在这个时候,可以在执行playbook的时候,添加--force-handlers来强制执行handlers!但是必须要注意的是:--force-handlers参数主要针对即使playbook执行失败,也要执行代码块成功了的handlers(即执行成功的task任务), 如果代码块本身执行失败(即执行失败的task任务),那么它所对应的handlers应当不会被执行!

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

[root@localhost ansible]# cat haha.yaml

---

- hosts: test_host

  remote_user: root

  become: yes

  become_method: sudo

  tasks:

    - name: make file task1

      file: path=/opt/task1.txt state=touch

      notify: task1

    - name: make file task2

      file: path=/opt/kevin/task2.txt state=touch

      notify: task2

  handlers:

    - name: task1

      file: path=/opt/task1.txt mode=777 owner=root group=root

    - name: task2

      shell: ln -s /opt/task1.txt /opt/task2.txt

 

执行结果:

[root@localhost ansible]# ansible-playbook haha.yaml

 

PLAY [test_host] *********************************************************************************************************************************

 

TASK [Gathering Facts] ***************************************************************************************************************************

ok: [172.16.60.234]

ok: [172.16.60.233]

 

TASK [make file task1] ***************************************************************************************************************************

changed: [172.16.60.233]

changed: [172.16.60.234]

 

TASK [make file task2] ***************************************************************************************************************************

fatal: [172.16.60.234]: FAILED! => {"changed"false"module_stderr""""module_stdout""Traceback (most recent call last):\r\n  File \"/tmp/ansible_iNMDpU/ansible_module_file.py\", line 474, in <module>\r\n    main()\r\n  File \"/tmp/ansible_iNMDpU/ansible_module_file.py\", line 448, in main\r\n    open(b_path, 'wb').close()\r\nIOError: [Errno 2] No such file or directory: '/opt/kevin/task2.txt'\r\n""msg""MODULE FAILURE""rc": 0}

fatal: [172.16.60.233]: FAILED! => {"changed"false"module_stderr""""module_stdout""Traceback (most recent call last):\r\n  File \"/tmp/ansible_OvTacW/ansible_module_file.py\", line 474, in <module>\r\n    main()\r\n  File \"/tmp/ansible_OvTacW/ansible_module_file.py\", line 448, in main\r\n    open(b_path, 'wb').close()\r\nIOError: [Errno 2] No such file or directory: '/opt/kevin/task2.txt'\r\n""msg""MODULE FAILURE""rc": 0}

 

RUNNING HANDLER [task1] **************************************************************************************************************************

        to retry, use: --limit @/etc/ansible/haha.retry

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.233              : ok=2    changed=1    unreachable=0    failed=1  

172.16.60.234              : ok=2    changed=1    unreachable=0    failed=1

 

如上执行结果,由于/opt/kevin目录不存在,导致task的第二个任务执行失败,这个时候handler根本没有被触发,也就不会执行。

即使第一个任务执行成功,但是它对应的第一个handler也不会被执行!!

 

###################################################################################

接下来使用--force-handlers选项来强制执行handlers(强制执行的是:成功执行的task对应的handler)

[root@localhost ansible]# ansible-playbook haha.yaml --force-handlers

 

PLAY [test_host] *********************************************************************************************************************************

 

TASK [Gathering Facts] ***************************************************************************************************************************

ok: [172.16.60.234]

ok: [172.16.60.233]

 

TASK [make file task1] ***************************************************************************************************************************

changed: [172.16.60.234]

changed: [172.16.60.233]

 

TASK [make file task2] ***************************************************************************************************************************

fatal: [172.16.60.233]: FAILED! => {"changed"false"module_stderr""""module_stdout""Traceback (most recent call last):\r\n  File \"/tmp/ansible_rEJQHm/ansible_module_file.py\", line 474, in <module>\r\n    main()\r\n  File \"/tmp/ansible_rEJQHm/ansible_module_file.py\", line 448, in main\r\n    open(b_path, 'wb').close()\r\nIOError: [Errno 2] No such file or directory: '/opt/kevin/task2.txt'\r\n""msg""MODULE FAILURE""rc": 0}

fatal: [172.16.60.234]: FAILED! => {"changed"false"module_stderr""""module_stdout""Traceback (most recent call last):\r\n  File \"/tmp/ansible_7CDxpp/ansible_module_file.py\", line 474, in <module>\r\n    main()\r\n  File \"/tmp/ansible_7CDxpp/ansible_module_file.py\", line 448, in main\r\n    open(b_path, 'wb').close()\r\nIOError: [Errno 2] No such file or directory: '/opt/kevin/task2.txt'\r\n""msg""MODULE FAILURE""rc": 0}

 

RUNNING HANDLER [task1] **************************************************************************************************************************

changed: [172.16.60.234]

changed: [172.16.60.233]

        to retry, use: --limit @/etc/ansible/haha.retry

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.233              : ok=3    changed=2    unreachable=0    failed=1  

172.16.60.234              : ok=3    changed=2    unreachable=0    failed=1

 

如上执行结果,即使playbook执行中有task任务执行失败,但是执行成功的task任务所调用的handler依然会被强制触发并执行!但是执行失败的task任务所调用的handler依然不会被执行。

即handlers中的task1会被执行,task2不会被执行!

2.  tags任务标签
tags用于让用户选择运行playbook中的部分代码。ansible具有幂等性,因此会自动跳过没有变化的部分,即便如此,有些代码为测试其确实没有发生变化的时间依然会非常地长。此时如果确信其没有变化,就可以通过tags跳过此些代码片断。tags可以看作是ansible的任务控制!

ansible的标签(Tags)功能可以给角色(Roles)、文件、单独的任务,甚至整个Playbook打上标签,然后利用这些标签来指定要运行Playbook中的个别任务,或不执行指定的任务。如果有一个很大的playbook剧本,而只想运行playbook其中的某个或部分task任务,而不是运行playbook中所有的任务,这个时候tags是你的最佳选择。

2.1  ansible支持"tags:"属性,执行playbook时,可以通过两种方式根据"tags"过滤任务:
1. 在命令行上,使用或选项"--tags或 --skip-tags",后面使用空格或"="都可以。
2. 在ansible配置设置中,使用和选项"TAGS_RUN或TAGS_SKIP";
3. 可以使用"--list-tags"查看playbook中有哪些tags会被执行;

2.2  ansible系统中内置的特殊tags(目前有5个特殊的tags)
到ansible 2.5版本以后,目前系统内置的tags有以下几个:
always: 除非--skip-tags指定这个标签,否则该标记为always的task一直都会执行。"--tags always"只执行标记了always的tasks;
never: 除非--tags指定了这个标签,否则该标记为never的task一直都不会执行。"--tags never"执行标记了always和never的tasks;
tagged: --tags tagged表示执行所有有tags标签的tasks任务,但不包括tags标签是never的tasks任务;--skip-tags tagged表示所有有tags标签的tasks任务都跳过,即不会执行。
untagged: --tags untagged表示执行所有没有tags标签的tasks任务和tags标签为always的tasks任务;--skip-tags untagged效果相反!
all:--tags all表示执行所有的tags标签为非never的task,包括有tags标签和无tags标签的tasks。

执行ansible-playbook命令时,使用下面两个参数的含义(自定义的tags可以是单个,也可以是多个,多个之间使用逗号隔开):
"--tags 自定义的tag" 表示执行tags为指定的标签名的tasks和tags为always的tasks。如果执行命令ansible-playbook site.yml 时不指定tags,则会执行所有tags为非never的tasks
"--skip-tags 自定义tag" 表示执行所有非指定tag和非never的tasks

2.3  tags标签配置语法有下面三种:

1

2

3

4

5

6

7

8

9

语法一:

tags:

  - tag_test

  

语法二:

tags: tag_test

  

语法三:

tags: ['tag_test']

2.3  ansible的tags使用
1)最常见的使用形式。一个task任务添加一个tags标签。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

官方示例如下:

[root@localhost ansible]# vim example.yml

---

- hosts: all

  remote_user: root

  gather_facts: no

  tasks:

    - yum: name={{ item }} state=installed

      with_items:

         - httpd

         - memcached

      tags:

         - packages

    - template: src=templates/src.j2 dest=/etc/foo.conf

      tags:

         - configuration

此时如果希望只run其中的某个task,则run的时候指定tags即可。可以运行多个tags,中间使用逗号隔开;也可以运行单个tags。

1

2

3

4

5

6

7

[root@localhost ansible]# ansible-playbook example.yml --tags "configuration,packages"  

[root@localhost ansible]# ansible-playbook example.yml --tags configuration  

[root@localhost ansible]# ansible-playbook example.yml --tags packages

或者

[root@localhost ansible]# ansible-playbook example.yml --tags="configuration,packages"  

[root@localhost ansible]# ansible-playbook example.yml --tags=configuration  

[root@localhost ansible]# ansible-playbook example.yml --tags=packages

相反,也可以使用--skip-tags跳过某个task任务。

1

2

3

[root@localhost ansible]# ansible-playbook example.yml --skip-tags configuration

或者

[root@localhost ansible]# ansible-playbook example.yml --skip-tags=configuration

具体看下面示例(tags三种语法都用上):

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

[root@localhost ansible]# cat haha.yaml

---

- hosts: test_host

  remote_user: root

  gather_facts: no

  tasks:

    - name: task1

      file: path=/opt/task1.txt state=touch

      tags: make_task1

    - name: task2

      file: path=/opt/task2.txt state=touch

      tags:

         - make_task2

    - name: task3

      file: path=/opt/task2.txt src=/opt/task2.txt dest=/opt/heihei state=link force=yes

      tags: ['link_task3']

 

只运行make_task1标签的task任务

[root@localhost ansible]# ansible-playbook haha.yaml --tags make_task1

 

PLAY [test_host] *********************************************************************************************************************************

 

TASK [task1] *************************************************************************************************************************************

changed: [172.16.60.234]

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.234              : ok=1    changed=1    unreachable=0    failed=0  

 

运行多个tags

[root@localhost ansible]# ansible-playbook haha.yaml --tags make_task1,make_task2

 

PLAY [test_host] *********************************************************************************************************************************

 

TASK [task1] *************************************************************************************************************************************

changed: [172.16.60.234]

 

TASK [task2] *************************************************************************************************************************************

changed: [172.16.60.234]

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.234              : ok=2    changed=2    unreachable=0    failed=0

 

跳过make_task2标签的任务,其他任务正常执行

[root@localhost ansible]# ansible-playbook haha.yaml --skip-tags make_task2

 

PLAY [test_host] *********************************************************************************************************************************

 

TASK [task1] *************************************************************************************************************************************

changed: [172.16.60.234]

 

TASK [task3] *************************************************************************************************************************************

changed: [172.16.60.234]

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.234              : ok=2    changed=2    unreachable=0    failed=0

2)一个task任务添加多个tags标签。
上面是一个task任务添加一个tags标签,其实一个task任务可以添加多个标签,而且不同的task任务可以使用相同的tags标签。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

一个任务添加多个tags标签的语法仍然也有三种:

语法1:

tags:

  - tag1

  - tag2

 

语法2:

tags: tag1,tag2

 

语法3:

tags: ['tag1,tag2']

 

========================================================

具体示例如下:

[root@localhost ansible]# vim https.yml

---

- hosts: test_host

  remote_user: root

  tasks:

     - name: install httpd package

       tags: 

         - httpd

         - package

       yum:

         name=httpd

         state=latest

  

     - name: start up httpd service

       tags: httpd,service

       service:

         name: httpd

         state: started

 

 

上面例子中每个任务都有多个标签,而且上例中两个任务都有一个共同的标签,就是httpd标签。

所以当执行"ansible-playbook httpd.yml --tags=httpd"时,上面两个task任务都会被执行。

 

由于上面例子中的所有任务都有共同的httpd标签,所以像这种情况,可以把httpd标签提取出来并写在play剧本中,示例如下:

[root@localhost ansible]# vim https.yml

---

- hosts: test_host

  remote_user: root

  tags:httpd

  tasks:

     - name: install httpd package

       tags: 

          - package

       yum:

         name=httpd

         state=latest

  

     - name: start up httpd service

       tags: ['service']

       service:

         name: httpd

         state: started

需要注意:当tags写在play剧本中而非写在task任务中时,play中的所有task任务都会继续当前paly中的tags,就像上例中,两个任务都会继承httpds的tag标签,同时还拥有自己的tag标签。

3)内置的特殊tags的用法
上面已经介绍了5个内置的特殊的tags,每个都有其自身的用意。如下以always关键字的tags为例:如果把任务的tags值指定为always时,那么这个任务就总是被执行,除非使用"--skip-tags"选项明确指定不执行对应任务的tags标签。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

[root@localhost ansible]# cat haha.yaml

---

- hosts: test_host

  remote_user: root

  gather_facts: no

  tasks:

    - name: task1

      file: path=/opt/task1.txt state=touch

      tags: make_task1

    - name: task2

      file: path=/opt/task2.txt state=touch

      tags:

         - always

    - name: task3

      file: path=/opt/task2.txt src=/opt/task2.txt dest=/opt/heihei state=link force=yes

      tags: ['link_task3']

 

执行1:如下,虽然tags指定了执行标签为make_task1的任务,但是由于任务2的标签有关键字always,所以任务2也会被执行,这就是always的作用!

[root@localhost ansible]# ansible-playbook haha.yaml --tags=make_task1

 

PLAY [test_host] *********************************************************************************************************************************

 

TASK [task1] *************************************************************************************************************************************

changed: [172.16.60.234]

 

TASK [task2] *************************************************************************************************************************************

changed: [172.16.60.234]

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.234              : ok=2    changed=2    unreachable=0    failed=0 

 

执行2: 只执行标签为always的任务

[root@localhost ansible]# ansible-playbook haha.yaml --tags always

或者

[root@localhost ansible]# ansible-playbook haha.yaml --tags=always

 

PLAY [test_host] *********************************************************************************************************************************

 

TASK [task2] *************************************************************************************************************************************

changed: [172.16.60.234]

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.234              : ok=1    changed=1    unreachable=0    failed=0

 

执行3: 跳过标签为always关键字的任务,这里明确指出跳过执行always标签。

[root@localhost ansible]# ansible-playbook haha.yaml --skip-tags always   

 

PLAY [test_host] *********************************************************************************************************************************

 

TASK [task1] *************************************************************************************************************************************

changed: [172.16.60.234]

 

TASK [task3] *************************************************************************************************************************************

ok: [172.16.60.234]

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.234              : ok=2    changed=1    unreachable=0    failed=0  

 

其他四个特殊的tags标签在这里就不做示例说明了。特殊tags标签可以在ansible-playbook命令执行时直接使用。

4)tags标签可以和role 结合使用

1

2

3

4

[root@localhost ansible]# cat test.yml

---

roles:

  - { role: webserver, port: 5000, tags: [ 'web''foo' ] }

5)tags和include结合使用。

1

2

3

[root@localhost ansible]# cat test.yml

---

- include: kevin.yml tags=web,foo

如上,对一个include任务打了两个tags标签,直接执行"ansible_playbook test.yml" 或 "ansible_playbook test.yml --tags=web" 或 "ansible_playbook test.yml --tags=foo" 命令则会将kevin.yml文件中所有task任务都执行。

再来看看一个include结合tags的示例:通过指定标签(tags),来说明是安装tomcat7还是tomcat8

tomcat.yml文件

1

2

3

4

5

---

- include: install_tomcat7.yml

  tags: tomcat7

- include: install_tomcat8.yml

  tags: tomcat8

install_tomcat7.yml文件

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

---

- name: "复制文件到远程主机"

  copy:

    src={{ item.src }}

    dest={{ item.dest }}

  with_items:

    - src: jdk-7u79-linux-x64.rpm

      dest: /usr/local/src/

    - src: java17.sh

      dest: /etc/profile.d/

- name: "安装jdk"

  yum:

    name: /usr/local/src/jdk-7u79-linux-x64.rpm

    state: present

- name: "重新加载环境变量"

  shell: "source /etc/profile.d/java17.sh"

- name: "复制tomcat文件到远程服务器并解压"

  unarchive:

    src=apache-tomcat-7.0.64.zip

    dest=/data/

    copy=yes

    owner=staplesapp

    group=admin

- name: "对解压后的文件重命名"

  shell: mv /data/apache-tomcat-7.0.64 /data/tomcat7

- name: "对tomcat进行相关配置"

  shell: find /data/tomcat7/bin -name "*.sh" xargs chmod +x

- name: "启动tomcat"

  shell: 'nohup /data/tomcat7/bin/startup.sh &'

install_tomcat8.yml文件

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

---

- name: "复制文件到远程主机"

  copy:

    src={{ item.src }}

    dest={{ item.dest }}

  with_items:

    - src: jdk-8u111-linux-x64.rpm

      dest: /usr/local/src/

    - src: java18.sh

      dest: /etc/profile.d/

- name: "安装jdk"

  yum:

    name: /usr/local/src/jdk-8u111-linux-x64.rpm

    state: present

- name: "配置java环境变量"

  shell: "source /etc/profile.d/java18.sh"

- name: "安装tomcat"

  unarchive:

      src=apache-tomcat-8.0.30.tar.gz

      dest=/data/

      copy=yes

      owner=staplesapp

      group=admin

- name: "对解压后的文件重命名"

  shell: mv /data/apache-tomcat-8.0.30 /data/tomcat8

- name: "启动tomcat"

  shell: 'nohup /data/tomcat8/bin/startup.sh &'

下面开始执行命令:

1

2

3

4

5

安装tomcat7:

[root@localhost ansible]# ansible-playbook tomcat.yml --tags tomcat7

 

安装tomcat8:

[root@localhost ansible]# ansible-playbook tomcat.yml --tags tomcat8

这里需要特别注意:
在之前ansible版本中使用include 整合多个roles至统一入口结合tags标签来管理roles剧本,但在ansible2.8版本之后将会删除include语法,更改为import_playbook。如果还使用include语法也可以,只不过ansible-playbook执行结果中会有告警信息:"DEPRECATION WARNING]:'include' for playbook includes. You should use 'import_playbook' instead. This feature will be removed in version 2.8. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg."。所以,最好将上面tomcat.yml文件中的include语法改成import_playbook,如下:

1

2

3

4

5

6

[root@localhost ansible]# cat tomcat.yml

---

- import_playbook: install_tomcat7.yml

  tags: tomcat7

- import_playbook: install_tomcat8.yml

  tags: tomcat8

3.  include用法
如果想在playbook中重复使用任务列表,则可以使用include文件来执行此操作。 使用include的任务列表是定义系统将要实现的角色的好方法。主要清楚:ansible2.8版本之后include语法变成了import_playbook。如果还是使用include,则不会影响执行结果,只不过是有告警信息。ansible也可以将变量传递给include。示例如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

示例1: 通过Include,可以在playbook中引用另一个playbook或者tasks

==============================================================

[root@localhost ansible]# cat install_MysqlAndPhp.yml

- yum:

    name: mysql

    state: present

- yum:

    name: php-fpm

    state: present

 

[root@localhost ansible]# cat lamp.yml

---

- hosts: test_host

  remote_user: root

  gather_facts: no

  tasks:

  - include: install_MysqlAndPhp.yml

  - yum:

      name: httpd

      state: present

  

[root@localhost ansible]# cat lnmp.yml

---

- hosts: test_host

  remote_user: root

  gather_facts: no

  tasks:

    - include: install_MysqlAndPhp.yml

    - yum:

        name: nginx

        state: present

 

 

示例2: 可以在handler中引用include

==============================================================

[root@localhost ansible]# cat test_include.yml

---

- hosts: test_host

  remote_user: root

  gather_facts: no

  tasks:

    file:

        path: /opt/ttt

        state: touch

      notify: test include handlers

  

  handlers:

    - name: test include handlers

      include: include_handler.yml

  

[root@localhost ansible]# cat include_handler.yml

- debug:

    msg: "task1 of handlers"

- debug:

    msg: "task2 of handlers"

- debug:

    msg: "task3 of handlers"

 

示例3: when在include中使用

==============================================================

[root@localhost ansible]# cat /etc/ansible/hosts

[db]

192.168.24.10

[app]

192.168.24.11

 

[root@localhost ansible]# cat install_client.yml

---

- hosts: '` hosts `'

  user: ansible

  sudoyes

  sudo_user:root

  roles:

    - install_client

 

[root@localhost ansible]# cat roles/install_client/tasks/main.yml 

---

- include: db.yml

  when: "hosts == 'db'"

- include: app.yml

  when: "hosts == 'app'"

 

[root@localhost ansible]# cat roles/install_client/tasks/db.yml

---

  - name: Touchdb file

    shell: touch /tmp/db.txt

 

[root@localhost ansible]# cat roles/install_client/tasks/app.yml

---

  - name: Touchdb file

    shell: touch /tmp/db.txt

 

执行命令:

[root@localhost ansible]# ansible-playbook -i hosts install_client.yml --extra-vars "hosts=db"

[root@localhost ansible]# ansible-playbook -i hosts install_client.yml --extra-vars "hosts=app"

 

示例4: 可以在include中使用tags标签,这个在上面已经介绍过了

==============================================================

4.  role用法
角色(roles)是ansible自1.2版本开始引入的新特性,用于层次性,结构化地组织playbook。roles能够根据层次型结构自动装载变量文件、tasks以及handlers等。要使用roles只需要在playbook中使用include指令即可。简单的说,roles就是通过分别将变量、文件、任务、模块及处理器放置于单独的目录中、并可以便捷地include他们的一种机制。角色一般用于基于主机构建服务的场景中、但也可以是用于构建守护进程等场景中。role主要作用是重用playbook,例如无论安装什么软件都会安装时间同步服务,那么每个playbook都要编写ntp task,可以将ntp task写好,等到用的时候再调用就行了。ansible中将其组织成role,它有着固定的组织格式,以便playbook调用。

4.1  role层级目录结构
role以特定的层级目录结构进行组织的tasks、variables、handlers、templates、files等;相当于函数的调用把各个功能切割成片段来执行。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

roles/

    role_name/:定义的role的名字

    file/:     用于存放copy或script等模块调用的函数

    tasks/:     用于定义各种task,此目录一定要有main.yml;其他文件需要main.yml包含调用

    handlers/: 用于定义各种handlers,此目录一定要有main.yml;其他文件需要main.yml包含调用

    vars/:      用于定义variables,此目录一定要有main.yml;其他文件需要main.yml包含调用

    templates/:存储由template模块调用的模板文本;

    meta/:     定义当前角色的特殊设定及其依赖关系,此目录中至少应该有一个名为main.yml的文件;其它的文件需要由main.yml进行"包含"调用;

    default/:  此目录中至少应该有一个名为main.yml的文件,用于设定默认变量;

 

[root@localhost ansible]# ll roles/

total 40

drwkebor-kebor-kebo 8 root root 4096 Jul 29 22:13 web_Deploy

drwkebor-kebor-kebo 8 root root 4096 May  7  2019 web_Deploy_af

 

[root@localhost ansible]# ll roles/web_Deploy                  

total 25

-rw-r--r-- 1 root root   45 May  7  2019 web_Deploy.yml

drwkebor-kebor-kebo 2 root root 4096 Jul 10 19:09 defaults

drwkebor-kebor-kebo 2 root root 4096 May  7  2019 handlers

drwkebor-kebor-kebo 2 root root 4096 May  7  2019 meta

drwkebor-kebor-kebo 2 root root 4096 Dec 26 19:42 tasks

drwkebor-kebor-kebo 2 root root 4096 May  7  2019 templates

drwkebor-kebor-kebo 2 root root 4096 May  7  2019 vars

 

[root@localhost ansible]# ll roles/web_Deploy/tasks/

total 35

-rwkebor-kebor-kebo 1 root root 1542 Jun 24  2019 Auth.yml

-rwkebor-kebor-kebo 1 root root 1482 Oct 11 16:13 StartService.yml

-rwkebor-kebor-kebo 1 root root  963 Jun 18  2019 main.yml

-rwkebor-kebor-kebo 1 root root 1415 May  7  2019 StopService.yml

 

[root@localhost ansible]# cat roles/web_Deploy/tasks/main.yml

---

- include_tasks: Auth.yml

  tags: userauth

   

- include_tasks: StopService.yml

  tags: stopservice

- include_tasks: StartService.yml

  tags: startservice

 

[root@localhost ansible]# cat roles/web_Deploy/web_Deploy.yml

---

- hosts: all

  roles:

    - web_Deploy

 

===================================================================================

再如下一个项目的role目录结构:

site.yml

webservers.yml

fooservers.yml

roles/

   common/

     files/

     templates/

     tasks/

     handlers/

     vars/

     defaults/

     meta/

   webservers/

     files/

     templates/

     tasks/

     handlers/

     vars/

     defaults/

     meta/

 

 

再看下目录解释:

yml文件:用于定义此角色用到的各handler:在handler中使用include包含的其他的handler文件也应该位于此目录中;

files目录:存放由copy或script等模块调用的文件;

templates目录:templates模块会自动在此目录中寻找Jinja2模板文件;

tasks目录:至少应该包含一个名为main.yml的文件,其定义了此角色的任务列表;此文件可以使用include包含其他的位于此目录中的task文件;

handlers目录:此目录中应当包含一个main;

vars目录:应当包含一个main.yml文件,用于定义此角色用到的变量;

meta目录:应当包含一个main.yml文件,用于定义此角色的特殊设定及其依赖关系;ansible 1.3及其以后的版本才支持

default目录:为当前角色设定默认变量时使用此目录;应当包含一个main.yml文件;

 

 

那么一个playbook就可以这样写:

---

 - hosts: webservers

  roles:

     - common

     - webservers

 

这个playbook为一个角色"kebo"指定了如下的行为:

如果 roles/kebo/tasks/main.yml 存在, 其中列出的tasks将被添加到play中

如果roles/kebo/handlers/main.yml 存在, 其中列出的handlers将被添加到play中

如果roles/kebo/vars/main.yml 存在, 其中列出的variables将被添加到play中

如果roles/kebo/meta/main.yml 存在, 其中列出的 "角色依赖"将被添加到roles列表中 (1.3 andlater)

所有 copy tasks 可以引用 roles/kebo/files/ 中的文件,不需要指明文件的路径。

所有 scripttasks 可以引用 roles/kebo/files/ 中的脚本,不需要指明文件的路径。

所有 template tasks 可以引用roles/kebo/templates/ 中的文件,不需要指明文件的路径。

所有 include tasks 可以引用roles/kebo/tasks/ 中的文件,不需要指明文件的路径。

 

如果roles目录下有文件不存在,这些文件将被忽略。比如 roles目录下面缺少了"vars/"目录,这也没关系。

 

需要注意:

仍然可以在playbook中松散地列出tasks,vars_files 以及 handlers,这种方式仍然可用,但是roles是一种很好的具有组织性的功能特性,强烈建议使用它。

如果在playbook中同时使用roles和tasks,vars_files 或者 handlers,roles 将优先执行。

 

而且也可以使用参数化的roles,这种方式通过添加变量来实现,比如:

--

- hosts: webservers

  roles:

    - common

    - { role: foo_app_instance, dir'/opt/a',  port: 5000 }

    - { role: foo_app_instance, dir'/opt/b',  port: 5001 }

 

当一些事情不需要频繁去做时,也可以为 roles 设置触发条件,像这样:

---

- hosts: webservers

  roles:

    - { role: some_role, when: "ansible_os_family == 'RedHat'" }

它的工作方式是:将条件子句应用到 role 中的每一个 task 上。

 

也可以给role分配指定的标签,比如:

---

- hosts: webservers

  roles:

    - { role: foo, tags: ["bar""baz"] }

 

如果play仍然包含有 "tasks" section,这些 tasks 将在所有 roles 应用完成之后才被执行。也可定义一些tasks,让它们在roles之前以及之后执行,可以这样做:

---

- hosts: webservers

  pre_tasks:

    - shell: echo 'hello'

  roles:

    - { role: some_role }

  tasks:

    - shell: echo 'still busy'

  post_tasks:

    - shell: echo 'goodbye'

 

注意:

pre_tasks: 执行正式 task 之前执行的任务

post_tasks:最后需要执行的任务

4.2  在playbook中调用role

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

role存放的路径在配置文件/etc/ansible/ansible.cfg中定义。如下,发现ansible的roles目录定义到/root/app/script/ansible/roles路径下了!!

[root@localhost ansible]# cat /etc/ansible/ansible.cfg |grep roles_path

roles_path    = /etc/ansible/roles:/root/app/script/ansible/roles

 

在playbook中调用role的方式有三种,如下:

第一种:

- hosts: HOSTS

  remote_user: root

  roles:

    - ROLE_NAME1

    - ROLE_NAME2

 

第二种:除了字典第一个元素指明调用的role,后面是传递给role的变量

- hosts: HOSTS

  remote_user: root

  roles:

  - { role: ROLE_NAME1, VARIABLE1: VALUE1, ... }

 

第三种:when指明role调用的条件

- hosts: HOSTS

  remote_user: root

  roles:

  - { role: ROLE_NAME1, when: CONDITIONS }

4.3  调用role示例

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

0) 先来看看role的路径定义

[root@localhost ansible]# cat /etc/ansible/ansible.cfg|grep roles_path

roles_path    = /etc/ansible/roles:/etc/ansible/roles

 

1)目录结构

[root@localhost ansible]# tree /etc/ansible/roles

/etc/ansible/roles

└── httpd                   #palybook调用时role的名称

    ├── defaults

    ├── files

    └── handlers

    │   └── main.yml       #所有的目录文件,并不一定要有,用时才创建

    └── mata

    └── tasks

    │   └── main.yml

    └── tamplates

        └── httpd.conf.c6.j2  #centos6,centos7的配置文件

        └── httpd.conf.c7.j2

 

2)tasks文件

[root@localhost ansible]# cat /etc/ansible/roles/httpd/tasks/main.yml

- name: install httpd package

  yum: name=httpd state=present

- name: install configure file

  template: src=httpd.conf.c{{ ansible_distribution_major_version }}.j2 dest=/etc/httpd/conf/httpd.conf

  tags: instconf

  notify: restart httpd service

- name: start httpd service

  service: name=httpd state=started enabled=true

 

3) handlers文件

[root@localhost ansible]# cat /etc/ansible/roles/httpd/handlers/main.yml

- name: restart httpd service

  service: name=httpd state=restarted

 

4) 模板文件

[root@localhost ansible]# grep ^Listen /etc/ansible/roles/httpd/templates/httpd.conf.c6.j2

Lister {{ httpd_port }}

 

5) 变量

[root@localhost ansible]# cat /etc/ansible/roles/httpd/vars/main.yml

httpd_port: 8088

 

6) playbook文件

[root@localhost ansible]# cat /etc/ansible/httpd_conf.yml

---

- hosts: webservers

  remote_user: root

  roles:

  - { role: httpd }

 

7) 执行playbook文件,并查看httpd端口

[root@localhost ansible]# ansible-playbook -i /etc/ansible/hosts /etc/ansible/httpd_conf.yml

[root@localhost ansible]# ansible -i /etc/ansible/hosts webservers -m shell -a "ss -tnlp|grep :80"

 

========================================================================================================

再来看一例:

1.group: 创建用户组nginx

2.user: 创建用户nginx

3.yum: 安装nginx

4.template: 配置文件更新nginx.conf

5.service: 启动nginx

 

[root@localhost ~]# cat /etc/ansible/ansible.cfg|grep roles_path

roles_path    = /etc/ansible/roles:/root/ansible/roles

 

[root@localhost ~]# cd /root/ansible/roles/nginx

[root@localhost nginx]# mkdir tasks templates

[root@localhost nginx]# cd tasks

 

[root@localhost tasks]# vim group.yml

- name: create group nginx

    group: name=nginx gid=80

 

[root@localhost tasks]# vim user.yml

-name: create user nginx

    user: name=nginx uid=80 group=nginx system=yes shell=/sbi/nologin

 

[root@localhost tasks]# vim install.yml

- name: install package

    yum: name=nginx

    

[root@localhost tasks]# vim start.yml

- name: start service

    service: name=nginx state=started enabled=yes

     

[root@localhost tasks]# vim restart.yml

- name: restart service

    service: name=nginx state=restarted

     

[root@localhost tasks]# vim templ.yml

- name: copy conf

    template: src=nginx.conf.j2 dest=/etc/nginx/conf/nginx.conf

     

[root@localhost tasks]# vim main.yml

- include: group.yml

- include: user.yml

- include: install.yml

- include: templ.yml

- include: start.yml

       

[root@localhost tasks]# cd ../templates  && ls

nginx.conf.j2

 

[root@localhost tasks]# cd /root/ansible

[root@localhost ansible]# vim nginx_role.yml

- hosts: websrvs

  remote_user: root

  roles:

    - role: nginx

 

执行命令:

[root@localhost ansible]# ansible-playbook nginx_role.yml

5. loop列表循环用法
在ansible 2.5版本之前,大多数人习惯使用"with_X"风格的关键字操作循环,从ansible 2.6版本开始,官方开始推荐使用"loop"关键字代替"with_X"风格关键字。下面通过一些小示例来说明使用loop关键字进行的列表循环操作。[loop、with_items、with_list 三者等同,效果是一样的!]。ansible的循环使用,可以参考下面"循环变量"以及参考这里

playbook中的变量设置

########   变量的优先级   ########
1.  extra vars变量(在命令行中使用 -e);优先级最高
2.  在inventory中定义的连接变量(比如ansible_ssh_user);优先级第二
3.  大多数的其他变量(命令行转换,play中的变量,include的变量,role的变量等);优先级第三
4.  在inventory定义的其他变量;优先级第四
5.  有系统发现的facts;优先级第五
6.  "role默认变量",这个是最默认的值,很容易丧失优先权。优先级最小。

另外:在inventory清单列表里定义的变量:单个主机定义的变量优先级高于主机组定义的变量
经过实验,ansible使用inventory定义变量的优先级顺序从高到低为:
1. host_vars下定义变量
2. inventory中单个主机定义变量
3. group_vars下定义变量
4. inventory中组定义变量

playbook中定义变量,如下:

1

2

3

- hosts: webservers

  vars:

    http_port: 80

YAML陷阱
YAML语法要求如果值以{{ foo }}开头的话,那么就需要将整行用双引号包起来,这是为了确认你不是想声明一个YAML字典。
如下面配置是不行的!!!

1

2

3

4

---

- hosts: app_servers

  vars:

    app_path: {{ base_path }}/data/web

应该改成下面这样:

1

2

3

4

---

- hosts: app_servers

  vars:

    app_path: "{{ base_path }}/data/web"

########   Ansbile-playbook变量配置方法   ########

1.  在inventory主机清单文件中定义变量
可以直接定义在主机清单文件/etc/ansible/hosts中,表明该变量只对对应的主机或者组有效,对其余的主机和组无效。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

有针对单个主机定义变量和组定义变量两种方式。

1)向不同的单个主机传递不同的变量;

IP/HOSTNAME  var1=value1 var2=value2

 

2)向组中的主机传递相同的变量;

[groupname:vars]

var1=value1

var2=value2

 

但是注意:

组定义变量的作用范围是组下的所有主机。

当两种定义方式同时存在时,ansible会优先采用单个主机定义的变量值!

 

[root@ss-server ansible]# pwd

/etc/ansible

[root@ss-server ansible]# cat hosts|egrep -v "^#|^$"

[kevin]

172.16.60.20 key=20180101

172.16.60.22 ansible_ssh_port=22288 key="niubility"

  

[root@ss-server ansible]# cat ansi.yml

---

- hosts: kevin

  gather_facts: False

  tasks:

    - name: haha

      debug: msg="the {{ inventory_hostname }} value is {{ key }}"

  

执行结果(注意inventory_hostname代表inventory列表列表里被控节点的主机名):

[root@ss-server ansible]# ansible-playbook ansi.yml

  

PLAY [kevin] *************************************************************************************************************************************

  

TASK [haha] **************************************************************************************************************************************

ok: [172.16.60.20] => {

    "msg""the 172.16.60.20 value is 20180101"

}

ok: [172.16.60.22] => {

    "msg""the 172.16.60.22 value is niubility"

}

  

PLAY RECAP ***************************************************************************************************************************************

172.16.60.20             : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

172.16.60.22             : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

 

再看下面一示例

[root@ss-server ansible]# tail -n 10 /etc/ansible/hosts

[webserver]

172.16.60.51 dir=/root/node2

172.16.60.80 dir=/root/node1

 

[node1]

172.16.60.80

 

[webserver:vars]

file=hostname.txt

 

[root@ss-server ansible]# cat playbook.yml

---

- hosts: webserver

  remote_user: root

  tasks:

    - name: Create New Folder

      file: name={dir }} state=directory

    - name: Create New Folder

      shell: echo `hostname` > {{dir}}/{file }}

 

执行结果:

[root@ss-server ansible]# ansible-playbook playbook.yml   

 

PLAY [webserver] ************************************************************************

 

TASK [Gathering Facts] ***************************************************************

ok: [172.16.60.80]

ok: [172.16.60.51]

 

TASK [Create New Folder] *************************************************************

changed: [172.16.60.51]

changed: [172.16.60.80]

 

TASK [Create New Folder] *************************************************************

changed: [172.16.60.51]

changed: [172.16.60.80]

 

PLAY RECAP ***************************************************************************

172.16.60.51              : ok=3    changed=2    unreachable=0    failed=0  

172.16.60.80              : ok=3    changed=2    unreachable=0    failed=0

此外:ansible还内置了一些固定的主机变量名,在inventory中定义其值, 在另一篇文章中介绍过。

2.  通过host_vars和group_vars目录来定义变量
/etc/ansible/目录是linux系统上ansible默认的配置文件目录(Mac系统上的话,其默认配置目录是在/usr/local/etc/ansible/),在该目录下创建host_vars和group_vars两个目录用来存放定义变量的文件。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

1)针对单个主机的变量 

[root@ss-server ansible]# pwd

/etc/ansible

[root@ss-server ansible]# cat host_vars/172.16.60.20

---

user: root

pass: root@123

 

2)针对test组的变量

[root@ss-server ansible]# cat group_vars/kevin

---

user: work

pass: work@123

 

############ 在inventory清单列表文件里,单个主机定义的变量优先级高于主机组定义的变量 ############

############ 经过实验,ansible使用变量的优先级顺序从高到低为 #############

1. host_vars下定义变量

2. inventory中单个主机定义变量

3. group_vars下定义变量

4. inventory中组定义变量

 

示例如下:

[root@ss-server ~]# cat /root/ansible/hosts

[kevin]

172.16.60.20 ansible_ssh_port=22222

172.16.60.21 ansible_ssh_port=22288

 

[root@ss-server ~]# cat /root/ansible/group_vars/kevin

key=20191010

 

[root@ss-server ~]# cat /root/ansible/bo.yml

---

- hosts: kevin

  remote_user: root

  tasks:

    - debug: msg='The key is {{key}} '

 

[root@ss-server ~]# ansible-playbook /root/ansible/bo.yml

  

PLAY [kevin] *************************************************************************************************************************************

  

TASK [haha] **************************************************************************************************************************************

ok: [172.16.60.20] => {

    "msg""The key is 20191010"

}

ok: [172.16.60.22] => {

    "msg""The key is 20191010"

}

  

PLAY RECAP ***************************************************************************************************************************************

172.16.60.20             : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

172.16.60.22             : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

3.  通过var_files定义变量

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

[root@ss-server ansible]# cat laoji.yml

---

key: jiayou

 

[root@ss-server ansible]# cat bo.yml

---

- hosts: kevin

  gather_facts: False

  vars_files:

      - laoji.yml

  tasks:

    - name: display

      debug: msg="the {{ inventory_hostname }} valus is {{ key }}"

  

执行结果:

[root@ss-server ansible]# ansible-playbook bo.yml

PALY [kevin] ****************************************************

  

TASK [display] ****************************************************

ok: [172.16.60.20] => {

    "changed"false

    "msg":"the 172.16.60.20 value is jiayou"

}

  

PLAY RECAP ****************************************************

172.16.60.20         : ok=1  changed=0  unreachable=0  failed=0

4.  通过vars_prompt交互式传入变量
在playbook中定义vars_prompt的变量名和交互式提示信息,就可以实现在运行playbook时,通过交互的传入变量值。
private字段:用来定义交互时是否回显输入的值,默认private为yes;
default字段:用来定义变量的默认值。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

[root@ss-server ansible]#  cat prom.yml

---

- hosts: kevin

  remote_user: root

  vars_prompt:

      - name: "var1"

        prompt: "please input you name"

        private: no

      - name: "var2"

        prompt: "please input you age"

        private: yes

        default: 'kevin var'

  tasks:

      - name: display var1

        debug: msg="your name of var1 is {{ var1 }}"

      - name: display var2

        debug: msg="you age of var2 is {{ var2 }}"

  

执行结果:

[root@ss-server ansible]# ansible-playbook prom.yml

your name of var1: wangzhaojun       #把输入的内容传递给变量var1。输入的值显示出来!!

you age of var2 [kevin var]:         #把输入的内容传递给默认变量kevin var。但是输入的值不显示出来!! 比如这里输入的30

  

PALY [kevin] ****************************************************

  

TASK [Gathering Facts] ****************************************************

OK: [172.16.60.20]

  

TASK [display var1] ****************************************************

ok: [172.16.60.20] =>{

    "msg""youn name of var1 is wangzhaojun"

}

  

TASK [display var2] ****************************************************

ok: [172.16.60.20] =>{

    "msg""youn name of var2 is 30"

}

  

PLAY RECAP ****************************************************

172.16.60.20         : ok=3  changed=0  unreachable=0  failed=0

  

接下来再来看一个"ansible 中prompt 交互变量的使用"的示例

[root@ss-server ansible]# cat haha.yml

---

- hosts: kevin

  remote_user: root

  vars_prompt:

     - name: "your_name"

       prompt: "what is your name"

       private: no

     - name: "your_age"

       prompt: "how old are you"

       private: no

     - name: "solution"

       prompt: "Choose the solution you want \n

       A: solutionA\n

       B: solutionB\n

       C: solutionC\n"

       private: no

       default: A

     - name: "user_name"

       prompt: "Enter user name"

       private: no

     - name: "user_password"

       prompt: "Enter user password"

       private: no

       encrypt: "sha512_crypt"

       confirm: yes

  tasks:

     - name: "output vars"

       debug: msg="your name is {{ your_name }},you are {{ your_age }} years old"

     - name: "output solution"

       debug: msg="the final solution is {{solution}}"

     - name: "create_user"

       user:

         name: "{{user_name}}"

         password: "{{user_password}}"

     - name: "debug_create user"

       debug: msg="create user {{user_name}} in"

  

执行结果为:

[root@ss-server ansible]# ansible-playbook haha.yml

what is your name: wangshibo

how old are you: 29

Choose the solution you want

 A: solutionA

 B: solutionB

 C: solutionC

 [A]: C

Enter user name: bobo

Enter user password: bobo123

confirm Enter user password: bobo123

  

PLAY [kevin] *************************************************************************************************************************************

  

TASK [Gathering Facts] ***************************************************************************************************************************

ok: [172.16.60.20]

  

TASK [output vars] *******************************************************************************************************************************

ok: [172.16.60.20] => {

    "msg""your name is wangshibo,you are 29 years old"

}

  

TASK [output solution] ***************************************************************************************************************************

ok: [172.16.60.20] => {

    "msg""the final solution is C"

}

  

TASK [create_user] *******************************************************************************************************************************

changed: [172.16.60.20]

  

TASK [debug_create user] *************************************************************************************************************************

ok: [172.16.60.20] => {

    "msg""create user bobo in"

}

  

PLAY RECAP ***************************************************************************************************************************************

172.16.60.20                  : ok=5    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

5.  通过ansible-playbook命令行定义变量!即参数传入变量
除了"vars_prompt"和"vars_files",也可以通过Ansible命令行发送变量。如果想要编写一个通用的发布playbook时则特别有用!你可以传递应用的版本以便部署。例如下面命令(注意: --extra-vars 相等于 -e)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

[root@ss-server ~]# ansible-playbook release.yml --extra-vars "version=1.78.34 other_variable=fos"

  

其他场景中,通过参数传入变量也是很有用的。比如为playbook设置主机群组或用户。如下:

[root@ss-server ~]# vim exap.yml

---

- hosts: '{{hosts}}'

  remote_user: '{{user}}'

  tasks:

    - name: "一个测试"

      debug: msg="your hosts is {{hosts}}, user is {{user}}"

  

执行的时候,通过参数传入变量(-e)。变量{{hosts}}可以是主机群组名称,也可以是主机ip。

[root@ss-server ansible]# ansible-playbook exap.yml -e "hosts=kevin user=root"  

[WARNING]: Found variable using reserved name: hosts

  

  

PLAY [kevin] *************************************************************************************************************************************

  

TASK [Gathering Facts] ***************************************************************************************************************************

ok: [172.16.60.20]

  

TASK [一个测试] **************************************************************************************************************************************

ok: [172.16.60.20] => {

    "msg""your hosts is kevin, user is root"

}

  

PLAY RECAP ***************************************************************************************************************************************

172.16.60.20                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

  

在命令行里面传值的方法:

[root@ss-server ansible]# ansible-playbook amp.yml --extra-vars "hosts=webserver user=root"

  

还可以使用json格式传递参数:

[root@ss-server ansible]# ansible-playbook amp.yml --extra-vars "{'hosts':'webserver', 'user':'root'}"

  

还可以将参数放在文件里面进行传递(注意命令行里要是用"@文件名"):

[root@ss-server ansible]# ansible-playbook amp.yml --extra-vars "@anhui.yml"

[root@ss-server ansible]# cat anhui.yml

---

hosts: webserver

user: root

 

例如下面这个添加用户的plakbook剧本配置,用传递了json文件:

[root@ss-server ansible]# cat useradd.yml

---

- hosts: "{{ host }}"

    gather_facts: no

    remote_user: root

    vars:

      UserName: "{{ user }}"

      UserPassword: "{{ pass }}"

    tasks:

      - name: create new user {{ UserName }}

        user: name={{ UserName }} shell=/bin/bash  password={{ UserPassword |password_hash('sha512') }} update_password=always append=yes

      - name: Config /etc/sudoers

        lineinfile: dest=/etc/sudoers state=present  line='{{ item }}' validate='visudo -cf %s'

        with_items:

          "{{ user }} ALL=(ALL) NOPASSWD:ALL,!/bin/rm,!/bin/su,!/usr/bin/passwd,!/usr/sbin/visudo,!/sbin/shutdown,!/sbin/reboot,!/sbin/halt"

          "Defaults: {{ user }}  !requiretty"

 

[root@ss-server ansible]# ansible-playbook useradd.yml -e "host=172.16.60.20 user=kevin_bo pass=kevin@bo123"

 

在上例中,变量pass是密码,如果变量pass里有特殊的字符,或者变量pass是一串数组的话,它将被转义。若不想被转义,可以使用如下方法:

[root@ss-server ansible]# cat user.json

host: webserver                     #ansible里面定义的主机组或者直接配置主机ip,如172.16.60.20

user: kevin_bo                      #添加的用户名

pass: 'Fxx6unM$R%I$Jna&'            #添加的用户的密码,可以用百度上随机密码生成器生成

 

指定user,json文件执行剧本 (使用JSON格式的文件即可,-e 传递变量,优先级最高)

[root@ss-server ansible]# ansible-playbook useradd.yml -e "@user.json"       

 

删除web组中所有用户

[root@ss-server ansible]# ansible webserver -m user -a 'name=zhangsan state=absent remove=yes'

6.  在playbook剧本中定义变量

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

在playbook中定义变量需要用到Ansible的vars模块,可以将所有需要用到的变量统一在vars模块下定义,定义格式需要遵循YAML语言格式:

vars:

  - var1: value1

  - var2: value2

  - var3: value3

  - ....: .....

 

示例如下:

[root@ss-server ansible]# cat playbook.yml

---

- hosts: kevin

  remote_user: root

  vars:

    - dir1: /root/Ansible

    - dir2: /root/Ansible/test1

    - dir3: /root/Ansible/test2

  tasks:

    - name: Create New Folder

      file: name={{ dir1 }} state=directory

    - name: Create New Folder

      file: name={{ dir2 }} state=directory

    - name: Create New Folder

      file: name={{ dir3 }} state=directory

 

[root@ss-server ansible]# ansible-playbook playbook.yml   

 

PLAY [kevin] *************************************************************************

 

TASK [Gathering Facts] ***************************************************************

ok: [172.16.60.20]

 

TASK [Create New Folder] *************************************************************

changed: [172.16.60.20]

 

TASK [Create New Folder] *************************************************************

changed: [172.16.60.20]

 

TASK [Create New Folder] *************************************************************

changed: [172.16.60.20]

 

PLAY RECAP ***************************************************************************

[172.16.60.20]              : ok=4    changed=3    unreachable=0    failed=0

7.  通过roles角色定义变量

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

在Ansible的roles中定义变量,需要将变量及值的键值对形式写到roles的vars目录下的main.yml文件中,同样适用YAML语言格式,格式如下:

var1: value1

var2: value2

var3: value3

 

但是请注意:

通过Roles定义的变量只适用于当前roles。如下是roles文件组织结构:

[root@ss-server roles]# tree test/                 

test/

├── files

├── handlers

├── playbook.retry

├── playbook.yml

├── tasks

│   └── main.yml

├── templates

└── vars

    └── main.yml

5 directories, 4 files

 

[root@ss-server roles]# cat test/tasks/main.yml         #任务文件

- name: Get IP Address

  shell: echo `{{ cmd }}` >> {dir }}/{file }}

 

[root@ss-server roles]# cat test/vars/main.yml          #定义变量cmd

cmd: hostname -I

 

[root@ss-server roles]# cat test/playbook.yml

---

- hosts: webserver

  remote_user: root

  roles:

    test

 

hosts清单列表里定义的变量,适用于所有roles。

[root@ss-server roles]# cat /etc/ansible/hosts

[webserver]

172.16.60.51 dir=/root/node2

172.16.60.80 dir=/root/node1

 

[node1]

172.16.60.80

 

[webserver:vars]

file=hostname.txt

 

playbook调用Roles,执行结果为:

[root@Centos7T test]#ansible-playbook playbook.yml   

 

PLAY [websvr] ************************************************************************

 

TASK [Gathering Facts] ***************************************************************

ok: [172.16.60.80]

ok: [172.16.60.51]

 

TASK [test : Get IP Address] *********************************************************

changed: [172.16.60.51]

changed: [172.16.60.80]

 

PLAY RECAP ***************************************************************************

172.16.60.51              : ok=2    changed=1    unreachable=0    failed=0  

172.16.60.80              : ok=2    changed=1    unreachable=0    failed=0

8.  使用Facts获取的信息
还有其它地方可以获取变量, 这些变量是自动发现的,而不是用户自己设置的。Facts通过访问远程系统获取相应的信息,一个很好的例子就是远程主机的IP地址或者操作系统是什么。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

使用以下命令可以查看哪些信息是可用的(kevin是上面在/etc/ansible/hosts列表文件中配置的主机群组):

[root@ss-server ansible]# ansible kevin -m setup

  

[root@ss-server ansible]# ansible kevin -m setup|grep "ansible_python_version"

        "ansible_python_version""2.7.5",

可以在playbook中这样引用上面被控制主机的python版本: {{ ansible_python_version }}

  

[root@ss-server ansible]# ansible kevin -m setup|grep "ansible_nodename"

        "ansible_nodename""ss-server",

可以在playbook中这样引用上面被控制主机的主机名: {{ ansible_nodename }}

  

[root@ss-server ansible]# ansible kevin -m setup|grep "ansible_hostname"

        "ansible_hostname""ss-server",

被控制主机的主机名变量还可以是: {{ ansible_hostname }}

 

访问复杂变量数据

有些提供的facts, 比如网络信息等, 是一个嵌套的数据结构。访问它们使用简单的 {{ foo }} 语法并不够用,

但是也仍然很容易.如下所示:

{{ ansible_eth0["ipv4"]["address"] }}

 

或者这样写:

{{ ansible_eth0.ipv4.address }}

  

############ 如果关闭Facts,可以大大提高ansible的执行速度 ###########

关闭方法如下:

[root@ss-server ansible]# cat anhui.yml

---

- hosts: kevin

  gather_facts: no

9.  register注册变量
变量的另一个主要用途是在运行命令时,把命令结果存储到一个变量中,不同模块的执行结果是不同的。运行playbook时使用-v选项可以看到可能的结果值,ansible执行任务的结果值可以保存在变量中,以便稍后使用它。register方式主要用于在task之间传递变量。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

示例如下:

[root@ss-server ansible]# cat /etc/ansible/hosts

[kevin]

172.16.60.237

172.16.60.238

  

[root@ss-server ansible]# cat /root/register.yml

---

- hosts: kevin

  remote_user: root

  tasks:

      - name: register bo_test

        shell: hostname -I

        register: info

      - name: display info

        debug: msg="this host ip is {{ info }}"

  

[root@ss-server ansible]# ansible-playbook /root/register.yml

  

PLAY [kevin] *************************************************************************************************************************************

  

TASK [Gathering Facts] ***************************************************************************************************************************

ok: [172.16.60.238]

ok: [172.16.60.237]

  

TASK [register bo_test] **************************************************************************************************************************

changed: [172.16.60.238]

changed: [172.16.60.237]

  

TASK [display info] ******************************************************************************************************************************

ok: [172.16.60.237] => {

    "msg""this host ip is {'stderr_lines': [], u'changed': True, u'end': u'2019-12-15 22:07:18.431549', 'failed': False, u'stdout': u'172.16.60.237 ', u'cmd': u'hostname -I', u'rc': 0, u'start': u'2019-12-15 22:07:18.408235', u'stderr': u'', u'delta': u'0:00:00.023314', 'stdout_lines': [u'172.16.60.237 ']}"

}

ok: [172.16.60.238] => {

    "msg""this host ip is {'stderr_lines': [], u'changed': True, u'end': u'2019-12-15 22:07:18.430108', 'failed': False, u'stdout': u'172.16.60.238 ', u'cmd': u'hostname -I', u'rc': 0, u'start': u'2019-12-15 22:07:18.407184', u'stderr': u'', u'delta': u'0:00:00.022924', 'stdout_lines': [u'172.16.60.238 ']}"

}

  

PLAY RECAP ***************************************************************************************************************************************

172.16.60.237              : ok=3    changed=1    unreachable=0    failed=0 

172.16.60.238              : ok=3    changed=1    unreachable=0    failed=0

  

这里注意下:

register定义的info变量在第二个task中用来查看前一个task中执行的hostname命令的结果。

可以看到playbook运行后的结果中,info返回的是一段python字典数据,如果只想看到stdout部分的信息的话,可以通过info[‘stdout’]来引用。

  

[root@ss-server ansible]# cat /root/register.yml

---

- hosts: kevin

  remote_user: root

  tasks:

      - name: register bo_test

        shell: hostname -I

        register: info

      - name: display info

        debug: msg="this host ip is {{ info['stdout'] }}"

  

[root@ss-server ansible]# ansible-playbook /root/register.yml

  

PLAY [kevin] *************************************************************************************************************************************

  

TASK [Gathering Facts] ***************************************************************************************************************************

ok: [172.16.60.238]

ok: [172.16.60.237]

  

TASK [register bo_test] **************************************************************************************************************************

changed: [172.16.60.237]

changed: [172.16.60.238]

  

TASK [display info] ******************************************************************************************************************************

ok: [172.16.60.237] => {

    "msg""this host ip is 172.16.60.237 "

}

ok: [172.16.60.238] => {

    "msg""this host ip is 172.16.60.238 "

}

  

PLAY RECAP ***************************************************************************************************************************************

172.16.60.237              : ok=3    changed=1    unreachable=0    failed=0 

172.16.60.238              : ok=3    changed=1    unreachable=0    failed=0

10.  hostvars 变量
该变量用于引用其他主机上收集的facts中的数据,或者引用其他主机的主机变量、主机组变量。即从一台远程主机获取另一台远程主机的变量。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

如下示例:

[root@ss-server ansible]# cat /etc/ansible/hosts

[kevin]

172.16.60.237 addr=beijing

172.16.60.238 user=shibo age=39

 

[root@ss-server ansible]# cat test.yml

---

- hosts: kevin

  remote_user: root

  gather_facts: False

  tasks:

    - name: this is test1

      debug: msg="She is come from {{ hostvars['172.16.60.237']['addr'] }}"

    - name: this is test2

      debug: msg="I am {{ hostvars['172.16.60.238']['user'] }}, and age is {{ hostvars['172.16.60.238']['age'] }}"

 

或者将test.yml文件配置如下,即由 "[变量]"" 改为 ".变量"

两种配置的执行结果都是一样的!

[root@ss-server ansible]# cat test.yml

---

- hosts: kevin

  remote_user: root

  gather_facts: False

  tasks:

    - name: this is test1

      debug: msg="She is come from {{ hostvars['172.16.60.237'].addr }}"

    - name: this is test2

      debug: msg="I am {{ hostvars['172.16.60.238'].user }}, and age is {{ hostvars['172.16.60.238'].age }}"

 

执行结果为:

[root@ss-server ansible]# ansible-playbook test.yml            

 

PLAY [kevin] *************************************************************************************************************************************

 

TASK [this is test1] *****************************************************************************************************************************

ok: [172.16.60.237] => {

    "msg""She is come from beijing"

}

ok: [172.16.60.238] => {

    "msg""She is come from beijing"

}

 

TASK [this is test2] *****************************************************************************************************************************

ok: [172.16.60.237] => {

    "msg""I am shibo, and age is 39"

}

ok: [172.16.60.238] => {

    "msg""I am shibo, and age is 39"

}

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.237              : ok=2    changed=0    unreachable=0    failed=0  

172.16.60.238              : ok=2    changed=0    unreachable=0    failed=0 

 

 

############################# 这里需要注意下 ###########################

除了上面的配置,还可以如下配置,但是请注意:

1)debug后面必须使用"var"字段,即hostvar变量传递给var字段的变量。

2)var=hostvars[....],等于两边不能有空格!

 

[root@ss-server ansible]# cat test.yml

---

- hosts: kevin

  remote_user: root

  gather_facts: False

  tasks:

    - name: this is test1

      debug: var=hostvars['172.16.60.237']['addr']

    - name: this is test2

      debug: var=hostvars['172.16.60.238']['user']

 

或者将test.yml文件配置如下,这两个配置方法的执行结果是一样的!

[root@ss-server ansible]# cat test.yml

---

- hosts: kevin

  remote_user: root

  gather_facts: False

  tasks:

    - name: this is test1

      debug: var=hostvars['172.16.60.237'].addr

    - name: this is test2

      debug: var=hostvars['172.16.60.238'].user

 

执行结果为:

[root@ss-server ansible]# ansible-playbook test.yml

 

PLAY [kevin] *************************************************************************************************************************************

 

TASK [this is test1] *****************************************************************************************************************************

ok: [172.16.60.237] => {

    "hostvars['172.16.60.237']['addr']""beijing"

}

ok: [172.16.60.238] => {

    "hostvars['172.16.60.237']['addr']""beijing"

}

 

TASK [this is test2] *****************************************************************************************************************************

ok: [172.16.60.237] => {

    "hostvars['172.16.60.238']['user']""shibo"

}

ok: [172.16.60.238] => {

    "hostvars['172.16.60.238']['user']""shibo"

}

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.237              : ok=2    changed=0    unreachable=0    failed=0  

172.16.60.238              : ok=2    changed=0    unreachable=0    failed=0

 

注意:如果同一个name里配置了多个debug,则默认执行最后一个debug内容! (多个task的话,也是执行最后一个task)

[root@ss-server ansible]# cat test.yml

---

- hosts: kevin

  remote_user: root

  gather_facts: False

  tasks:

    - name: this is test1

      debug: var=hostvars['172.16.60.237']['addr']

    - name: this is test2

      debug: var=hostvars['172.16.60.238']['user']

      debug: var=hostvars['172.16.60.238']['age']

 

执行结果:

[root@ss-server ansible]# ansible-playbook test.yml

 [WARNING]: While constructing a mapping from /etc/ansible/test.yml, line 8, column 7, found a duplicate dict key (debug). Using last defined

value only.

 

 

PLAY [kevin] *************************************************************************************************************************************

 

TASK [this is test1] *****************************************************************************************************************************

ok: [172.16.60.237] => {

    "hostvars['172.16.60.237']['addr']""beijing"

}

ok: [172.16.60.238] => {

    "hostvars['172.16.60.237']['addr']""beijing"

}

 

TASK [this is test2] *****************************************************************************************************************************

ok: [172.16.60.237] => {

    "hostvars['172.16.60.238']['age']""39"

}

ok: [172.16.60.238] => {

    "hostvars['172.16.60.238']['age']""39"

}

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.237              : ok=2    changed=0    unreachable=0    failed=0  

172.16.60.238              : ok=2    changed=0    unreachable=0    failed=0

11. 列表变量、循环变量、字典变量

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

1)ansible的变量不仅可以是单个的值,也可以为列表,即ansible传列表作为变量。如下示例:

#################################################################################################################

[root@ss-server ansible]# vim test.yml

---

- hosts: test_host

  gather_facts: no

  vars:

    - list: [1,2,3]

  tasks:

    - name: echo

      debug: msg="{{ list }}"

 

执行结果:

[root@ss-server ansible]# ansible-palybook test.yml

TASK [echo] ********************************************************************

ok: [172.16.60.220] => {

    "msg": [

        1,

        2,

        3

    ]

}

 

再来看一例:

linux创建用户,需要获取的变量有用户名,用户密码,用户组,有时候需要创建多个用户,那么传递给ansible的用户肯定是列表,但每一组又有用户名、密码、组这些变量值。

做法如下:

[root@ss-server ~]# cat /etc/ansible/test.yml

---

- hosts: test_host

  gather_facts: no

  tasks:

   - name: create or update account

     user: name={{ item.user }} password={{ item.password }} groups={{ item.group }} system=no

     with_items:

       -  '{{ user_list }}'

 

执行结果:

[root@ss-server ansible]# ansible-playbook /etc/ansible/test.yml -e '{"user_list":[{"user":"user1","password":"*******","group":"user1"}]}'

 

#################################################################################################################

2)循环列表

结合循环,这个特性就变得很有用;以参数传递列表给playbook,不用在playbook中固定循环的次数与内容。如下示例:

[root@ss-server ansible]# vim test.yml

---

- hosts: test_host

  gather_facts: no

  vars:

    - list: [1,2,3]

  tasks:

    - name: this is loop

      debug: msg="{{ item }}"

      with_items: '{{list}}'

 

执行结果:

[root@ss-server ansible]# ansible-palybook test.yml

TASK [this is loop] ********************************************************************

ok: [172.16.60.220] => (item=1) => {

    "item": 1,

    "msg": 1

}

ok: [localhost] => (item=2) => {

    "item": 2,

    "msg": 2

}

ok: [localhost] => (item=3) => {

    "item": 3,

    "msg": 3

}

 

接着看下面一例:

loop 关键字表示循环,去读循环体里的变量固定使用{{item}},item是个字典对象item.key=value。

需要注意:下面test.yml文件中的"loop"关键字 改为 "with_items"关键字,效果是一样的!

ansible的循环用法:在ansible 2.5版本之前,大多数人习惯使用"with_X"风格的关键字操作循环,

从ansible 2.6版本开始,官方开始推荐使用"loop"关键字代替"with_X"风格关键字。

[root@ss-server ~]# cat /etc/ansible/test.yml

---

- name: this is test

  hosts: test_host

  connection: local

  gather_facts: no

 

  tasks:

    - name: debug loop

      debug:

        msg: "{{item.A1}}"

      loop:                      #这里将"loop"关键字 改为 "with_items"关键字,效果是一样的!

        - A: a

          A1: a1

          A2: a2

        - B: b

          A1: b1

          A2: b2

        - C: c

          A1: c1

          A2: c2

        - D: d

          A1: d1

          A2: d2

 

执行结果:以上loop下的四个变量分别称为一块,即一个item,符号"-"为循环体块的标志,{{item.A1}}的值,即分别为a1,b1,c1,d1

[root@ss-server ~]# ansible-playbook /etc/ansible/test.yml

 

PLAY [this is test] *********************************************************************************************************************************

 

TASK [debug loop] ********************************************************************************************************************************

ok: [172.16.60.20] => (item={u'A': u'a', u'A1': u'a1', u'A2': u'a2'}) => {

    "msg""a1"

}

ok: [172.16.60.20] => (item={u'A1': u'b1', u'B': u'b', u'A2': u'b2'}) => {

    "msg""b1"

}

ok: [172.16.60.20] => (item={u'A1': u'c1', u'C': u'c', u'A2': u'c2'}) => {

    "msg""c1"

}

ok: [172.16.60.20] => (item={u'A1': u'd1', u'A2': u'd2', u'D': u'd'}) => {

    "msg""d1"

}

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.20                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

 

再来看一例:

[root@ss-server ~]# cat /etc/ansible/test.yml

---

- name: this is test

  hosts: test_host

  connection: local

  gather_facts: no

  vars:

    my_list:

      - a

      - b

      - c

      - 1

 

  tasks:

    - name: debug loop output

      debug:

        msg: "The {{index}} one is {{item}}"

      loop: "{{my_list}}"               # 或者使用with_items: "{{my_list}}"  或者 with_list: "{{my_list}}"

      loop_control:

        index_var: index

 

执行结果:

[root@ss-server ~]# ansible-playbook /etc/ansible/test.yml

 

PLAY [this is test] ******************************************************************************************************************************

 

TASK [debug loop output] *************************************************************************************************************************

ok: [172.16.60.20] => (item=a) => {

    "msg""The 0 one is a"

}

ok: [172.16.60.20] => (item=b) => {

    "msg""The 1 one is b"

}

ok: [172.16.60.20] => (item=c) => {

    "msg""The 2 one is c"

}

ok: [172.16.60.20] => (item=1) => {

    "msg""The 3 one is 1"

}

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.20                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

 

#################################################################################################################

3)字典变量

变量也可以为字典。如下示例, 用到了ansible的循环,循环关键字是"with_X", 改为loop关键字,效果是一样的!

注意:在变量中使用循环时,vars下必须要有"-",符号"-"为循环体块的标志!!

[root@ss-server ansible]# vim test.yml

---

- hosts: test_host

  gather_facts: no

  vars:

    - lists:

        list1: [1,2,3]

        list2: [4,5]

  tasks:

    - name: loop

      debug: msg="{{ item }}"

      with_items: '{{lists["list1"]}}'        #替换为 loop: '{{lists["list1"]}}', 或者 with_list: '{{lists["list1"]}}' 效果是一样的

 

执行结果:

[root@ss-server ansible]# ansible-palybook test.yml

TASK [loop] ********************************************************************

ok: [172.16.60.220] => (item=1) => {

    "item": 1,

    "msg": 1

}

ok: [localhost] => (item=2) => {

    "item": 2,

    "msg": 2

}

ok: [localhost] => (item=3) => {

    "item": 3,

    "msg": 3

}

 

接着看下面一例:

[root@ss-server ~]# cat /etc/ansible/test.yml

---

- hosts: test_host

  gather_facts: no

  vars:

    users:

        name: [xiaoming]

        address: [anhui]

        age: [28]

  tasks:

    - name: this is loop

      debug: msg="{{ item }}"

      loop: '{{users["name"]}}'       

[root@ss-server ~]# ansible-playbook /etc/ansible/test.yml

 

PLAY [test_host] *************************************************************************************************************************************

 

TASK [this is loop] **************************************************************************************************************************************

ok: [172.16.60.20] => (item=xiaoming) => {

    "msg""xiaoming"

}

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.20                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

 

上面的示例,如果要想打印多个字典变量,修改如下:

[root@ss-server ~]# cat /etc/ansible/test.yml

---

- hosts: test_host

  gather_facts: no

  vars:

    users:

        name: [xiaoming]

        address: [anhui]

        age: [28]

  tasks:

    - name: this is loop

      debug: msg="{{ item }}"

      loop:                         #这里用loop,with_items,with_list都可以

         '{{users["name"]}}'   

         '{{users["address"]}}'

         '{{users["age"]}}'

 

执行结果:

[root@ss-server ~]# ansible-playbook /etc/ansible/test.yml

 

PLAY [test_host] *********************************************************************************************************************************

 

TASK [this is loop] ******************************************************************************************************************************

ok: [172.16.60.20] => (item=[u'xiaoming']) => {

    "msg": [

        "xiaoming"

    ]

}

ok: [172.16.60.20] => (item=[u'anhui']) => {

    "msg": [

        "anhui"

    ]

}

ok: [172.16.60.20] => (item=[28]) => {

    "msg": [

        28

    ]

}

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.20                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

######   可以在一个yaml文件里放置多个hosts,将多个tasks任务一起执行   ########

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

[root@ss-server ansible]# cat /etc/ansible/hosts

172.16.60.237

[kevin]

172.16.60.238

[grace]

172.16.60.236

 

[root@ss-server ansible]# cat bo.yml           

---

- hosts: kevin

  remote_user: root

  gather_facts: False

  tasks:

    - name: this is test1

      shell: hostname -I

 

- hosts: 172.16.60.237

  remote_user: root

  tasks:

    - name: this is test2

      debug: msg="this server is {{ info }} "

 

- hosts: 172.16.60.238

  remote_user: root

  tasks:

    - name: this is test3

      debug: msg="this key is {{ haha }} "

 

执行结果:

[root@ss-server ansible]# ansible-playbook bo.yml -e "{'info':'my server','haha':'123123213123'}" 

 

PLAY [kevin] *************************************************************************************************************************************

 

TASK [this is test1] *****************************************************************************************************************************

changed: [172.16.60.238]

 

PLAY [172.16.60.237] *****************************************************************************************************************************

 

TASK [Gathering Facts] ***************************************************************************************************************************

ok: [172.16.60.237]

 

TASK [this is test2] *****************************************************************************************************************************

ok: [172.16.60.237] => {

    "msg""this server is my server "

}

 

PLAY [172.16.60.238] *****************************************************************************************************************************

 

TASK [Gathering Facts] ***************************************************************************************************************************

ok: [172.16.60.238]

 

TASK [this is test3] *****************************************************************************************************************************

ok: [172.16.60.238] => {

    "msg""this key is 123123213123 "

}

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.237              : ok=2    changed=0    unreachable=0    failed=0  

172.16.60.238              : ok=3    changed=1    unreachable=0    failed=0

 

 

############# 再来看下面一个测试环境用过的部署脚本配置 #########################

[root@localhost ansible]# cat /opt/ansible_cfg/deploy.yml

---

- hosts: webservers

  tasks:

    - name: Installed Httpd Server

      yum: name=httpd state=present

 

    - name: Start Httpd Server

      systemd: name=httpd state=started enabled=yes

 

    - name: Start Firewalld Server

      systemd: name=firewalld state=started enabled=yes

 

    - name: Configure Firewalld Server

      firewalld: service=http immediate=yes permanent=yes state=enabled

 

- hosts: web01

  tasks:

    - name: Configure web01 Website

      copy: content='This is Web01' dest=/var/www/html/index.html

 

- hosts: web02

  tasks:

    - name: Cofnigure webi-2 weisite

      copy: content='This is Web02' dest=/var/www/html/index.html

 

- hosts: nfs01

  tasks:

    - name: Install NFS-utils Server

      yum: name=nfs-utils state=present

 

    - name: Configure Nfs-utils Server

      copy: src=./exports.j2 dest=/etc/exports owner=root group=root mode=0644

 

    - name: Create NFS Group

      group: name=www gid=666

 

    - name: Create NFS User

      user: name=www uid=666 group=www create_home=no shell=/sbin/nologin

 

    - name: Create Data Directory

      file: path=/data state=directory owner=www group=www mode=0755 recurse=yes

 

    - name: Start NFS Server

      systemd: name=nfs state=started enabled=yes

 

- hosts: nfs01

  tasks:

    - name: Mount NFS Server

      mount: path=/opt src=172.16.60.23:/data fstype=nfs opts=defaults state=mounted

######   Ansible-playbook如何正确获取ip   ######

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

[root@ansible ~]# vim /etc/ansible/hosts_all.yaml

---

- hosts: all

  tasks:      

    - name: 将原有的hosts文件备份        

      shell: mv /etc/hosts /etc/hosts_bak      

    - name: 将ansible端的hosts复制到各自机器上        

      copy: src=/root/hosts dest=/etc/ owner=root group=root mode=0644      

    - name: 在新的hosts文件后面追加各自机器内网ip和hostname        

      lineinfile: dest=/etc/hosts line="`ansible_all_ipv4_addresses`  `ansible_hostname`"

  

但是执行完ansible-playbook之后,ansible客户机器上的/etc/hosts文件里ip和主机名对应关系如下(cat /etc/hosts):

  

127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4

::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

[u'172.16.60.210'] redis-fun01.kevin.cn

  

实际想要的结果是(cat /etc/hosts):

127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4

::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

172.16.60.210 redis-fun01.kevin.cn

  

解决办法:

调整ansible服务端的hosts_all.yaml文件中获取ip的变量

变量用 IP: "{{ ansible_eth0['ipv4']['address'] }}",而不是`ansible_all_ipv4_addresses`

  

修改后的yaml文件配置如下:

[root@ansible ~]# vim /etc/ansible/hosts_all.yaml

---

- hosts: all

  vars:      

    IP: "{{ ansible_eth0['ipv4']['address'] }}"

  tasks:      

    - name: 将原有的hosts文件备份        

      shell: mv /etc/hosts /etc/hosts_bak      

    - name: 将ansible端的hosts复制到各自机器上        

      copy: src=/root/hosts dest=/etc/ owner=root group=root mode=0644      

    - name: 在新的hosts文件后面追加各自机器内网ip和hostname        

      lineinfile: dest=/etc/hosts line="`IP`  `ansible_hostname`"

########  获取ansible清单列表里对应组的ip、指定机器执行操作 [ --list-hosts、--limit ]  ########

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

注意下面两个参数的使用:

1)获取hosts清单文件里指定组内的ip,使用"--list-hosts"

2)指定hosts清单文件里的ip执行操作,使用"--limit"

  

如下是ansible的一个清单文件:

[root@localhost ~]# cat /opt/kevin-bo.cfg

[kevin-bo_F]

172.16.60.65

  

[kevin-bo_A]

172.16.60.140

172.16.60.141

  

[kevin-bo:children]

kevin-bo_D

kevin-bo_F

kevin-bo_A

  

[kevin-bo:vars]

deploy_path=/opt/web/kevin-bo/

start_time_out=90

stop_time_out=60

module=kevin-bo

  

[kevin-bo_D]

172.16.60.195

  

现在需要获取"kevin-bo_A" 和 "kevin-bo_D" 组下的ip,--list-hosts后面加不加引号都可以。

[root@localhost ~]# ansible -i /opt/kevin-bo.cfg --list-hosts kevin-bo_A

  hosts (2):

    172.16.60.140

    172.16.60.141

[root@localhost ~]# ansible -i /opt/kevin-bo.cfg --list-hosts "kevin-bo_A"|grep -v "hosts"

    172.16.60.140

    172.16.60.141

[root@localhost ~]# ansible -i /opt/kevin-bo.cfg --list-hosts kevin-bo_D|grep -v "hosts"

    172.16.60.195

 

获取多个机器组内的ip,中间使用逗号隔开

[root@localhost ~]# ansible -i /opt/kevin-bo.cfg --list-hosts "kevin-bo_A,kevin-bo_D"

  hosts (3):

    172.16.60.140

    172.16.60.141

    172.16.60.195

 

[root@localhost ~]# ansible -i /opt/kevin-bo.cfg --list-hosts "kevin-bo_A,kevin-bo_D"|grep -v "hosts"

    172.16.60.140

    172.16.60.141

    172.16.60.195

  

在执行ansible-playbook时,可以指定单个ip或group,也可以指定多个ip或group,也可以一起指定ip和group,多个中间使用逗号隔开!

--limit后面加不加引号都可以。

# ansible-playbook -i 清单文件 yml文件 --limit ip

# ansible-playbook -i 清单文件 yml文件 --limit ip1,iP2,ipn

# ansible-playbook -i 清单文件 yml文件 --limit group

# ansible-playbook -i 清单文件 yml文件 --limit group1,group2,groupn

# ansible-playbook -i 清单文件 yml文件 --limit ip1,ip2,group1,groupn

还可以将制定的ip或group放在一个文件里,如ip.txt,然后--limit后面跟"@ip.txt"

# ansible-playbook -i 列表文件 yml文件 --limit @ip.txt  

# cat ip.txt

172.16.60.140

172.16.60.141

kevin-bo_F

  

如上,需要对清单文件"kevin-bo_A"组下的机器进行发布操作,由于发布过程中涉及服务启停,为了保证发布中整体服务不中断,需要一台一台的执行,不能所有机器一起执行。如下:

[root@localhost ~]# vim deploy.sh

#!/bin/bash

#设置变量,传参等

BRANCH=$1

MODULE_NAME=$2

PRODUCT_PATH=$3

USER=$4

APP_NANE=${Deploy_App}

.....

  

for Next_Deploy_IP in $(ansible -i /opt/kevin-bo.cfg --list-hosts kevin-bo_A|grep -v "hosts")

do

   ansible-playbook -i /opt/kevin-bo.cfg /root/ansible/web_deploy.yml --limit "${Next_Deploy_IP}" -e "user=${USER} app_name=${APP_NANE} package_name=... ..."

   if [ $? -eq 0 ];then

      echo "[`date +%Y%m%d-%H%M%S`]++++++++++++++++++++++++${Next_Deploy_IP}部署成功!++++++++++++++++++++++++++++++"

      sleep 10

   else

      echo "[`date +%Y%m%d-%H%M%S`]++++++++++++++++++++++++${Next_Deploy_IP}部署失败!++++++++++++++++++++++++++++++"

      exit 1

   fi

done

*************** 当你发现自己的才华撑不起野心时,就请安静下来学习吧!***************

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值