为什么引入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
|
变量的引用
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。
command: echo hello clsn linux #(四个空格)command:(空格)
需要注意:
Task任务里的name可以省略不写,将-(空格)放到下一行模块墙面。例如:
---
- hosts: 172.16.60.211
task:
- command: echo 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, 名称
command: cat log.log ###执行一行 command , 和 shell 类似, 但是不支持 >> 等操作符
register: result ###执行的结果, 设到 result 这个变量中, 后面可以使用
- name: set hostname
shell: cat {{result.stdout}} >> host.text
- name: task1
command: echo '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
sudo: yes
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)