一、什么是playbooks
playbooks是ansible的脚本、如同shell脚本一样,它是控制远程主机的一系列命令的集合,通过YAML语言编写。执行一些简单的任务,我们可以使用ad-hoc命令就可以解决,对于一些较复杂的任务,ad-hoc就不能胜任了,这时候playbooks就派上用场了,在playbooks中可以编排有序的执行过程,甚至可以在多组机器间来回有序的执行特定的步骤,并且可以同步或异步发起任务。
二、YAML语法
1、文件开始符
1
|
---
|
2、数组
1
2
3
|
- name
- hosts
- user
|
3、字典
1
2
|
name: restart apache
service: name=httpd state=restarted
|
字典与字典的嵌套:
1
2
3
|
vars:
http_port: 80
max_clients: 200
|
字典与数组的嵌套:
1
2
3
4
5
6
7
8
9
|
tasks:
- name: ensure apache is at the latest version
yum: pkg=httpd state=latest
- name: write the apache config
file
template: src=
/srv/httpd
.j2 dest=
/etc/httpd
.conf
notify:
- restart apache
- name: ensure apache is running
service: name=httpd state=started
|
此外,Ansible 使用 "{{ var }}"来引用变量.,如果一个值以 "{" 开头, YAML 将认为它是一个字典, 所以我们必须引用它, 像这样:foo: "{{ variable }}"
三、playbooks的基本用法
最基本的playbook分为四部分:
定义主机和用户
1
2
|
hosts
users
|
定义 playbook 执行需要的变量
1
|
variable
|
定义任务
1
|
tasks
|
定义响应事件
1
|
handlers
|
简单示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
---
- name: Install apache
hosts: webservers
user: root
gather_facts:
false
vars:
http_port: 80
max_clients: 200
tasks:
- name: ensure apache is at the latest version
yum: pkg=httpd state=latest
- name: write the apache config
file
template: src=
/srv/httpd
.j2 dest=
/etc/httpd
.conf
notify:
- restart apache
- name: ensure apache is running
service: name=httpd state=started
handlers:
- name: restart apache
service: name=httpd state=restarted
|
说明:
name参数对此playbook实现的功能的概述,执行时会输出name变量值。
hosts参数指定了在哪些主机上执行playbook
user参数指定在远程主机上使用什么用户执行任务
gather_facts参数指定了下面任务执行前,是否先执行setup模块获取远程主机相关信息,这些信息在task中可以作为变量使用
vars参数指定了变量
task参数指定了任务,这里有3个任务。name参数是对具体任务的描述,在执行过程中会输出
handlers参数指定一个响应事件,当template: src=/srv/httpd.j2 dest=/etc/httpd.conf这个任务执行状态是changed时才会触发。
1、主机和用户
key | 含 义 |
hosts | 为主机的IP,或者主机组名,或者关键字all |
user | 在远程以哪个用户身份执行。 |
become | 切换成其它用户身份执行,值为yes或者no |
become_method | 与became一起用,指可以为‘sudo’/’su’/’pbrun’/’pfexec’/’doas’ |
become_user | 与bacome一起用,可以是root或者其它用户名 |
一般用法:
1
2
3
|
---
- hosts: webserver, [all]
user: root
|
还可以在每个 task 中,定义远程执行用户
1
2
3
4
5
6
7
|
---
- hosts: webserver
user: root
tasks:
- name:
test
connection
ping
:
remote_user: root
|
也支持 sudo 方法,在 task中同样支持,在sudo需要密码时,可以加上选项 –ask-sudo-pass
1
2
3
4
5
6
7
8
|
---
- hosts: all
user:
test
sudo
:
yes
task:
- service: name=nginx state=started
sudo
:
yes
sudo_user: root
|
2、任务列表
tasks 是从上到下顺序执行,如果中间发生错误,整个 playbook 便会中断。
每一个 task 是对module的一次调用,通常会带有特定参数,参数可以使用变量。
每一个 task 有一个 name 属性,name 值会在命令行中输出,以提示用户,如果没有定义,aciton 的值会作为输出信息来标记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
|
tasks:
- name:
make
sure apache is running
service: name=httpd state=running
//
如果参数过长,可以使用空格或者缩进分隔为多行
tasks:
- name: copy ansible inventory
file
to client
copy: src=
/etc/ansible/hosts
dest=
/etc/ansible/hosts
owner=root group=root mode=0644
//
或者使用 yaml 的字典作为参数
tasks:
- name: copy ansible inventory
file
to client
copy:
src:
/etc/ansible/hosts
dest:
/etc/ansible/hosts
owner: root
group: root
mode: 0644
//
大部分的模块都是使用 `key-value` 这种格式的,其中有两个比较特殊,
command
和 shell 模块。
tasks:
- name: disable selinux
command
:
/sbin/setenforce
0
tasks:
- name: run this
command
and ignore the result
shell:
/usr/bin/command
||
/bin/true
tasks:
- name: run some
command
and ignore the reslut
shell:
/usr/bin/somecommadn
ignore_error: True
|
执行状态:
task中每个action会调用一个module,在module中会去检查当前系统状态是否需要重新执行。
如果本次执行了,那么action会得到返回值changed
如果不需要执行,那么action得到返回值ok
3、响应事件
每个主流的编程语言都会有event机制,那么handler就是playbook的event。
Handlers里面的每一个handler,也是对module的一次调用。而handlers与tasks不同,tasks会默认的按定义顺序执行每一个task,handlers则不会,它需要在tasks中被调用,才有可能被执行。Tasks中的任务都是有状态的,changed或者ok。 在Ansible中,只在task的执行状态为changed的时候,才会执行该task调用的handler,这也是handler与普通的event机制不同的地方。
什么情况下使用handlers呢?
如果你在tasks中修改了apache的配置文件。需要重起apache。此外还安装了apache的插件。那么还需要重起apache。像这样的应该场景中,重启apache就可以设计成一个handler。
特性:
一个handler最多只执行一次
在所有的任务里表执行之后执行,如果有多个task notify同一个handler,那么只执行一次。
在下面的例子里apache只执行一次
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
---
- hosts: lb
remote_user: root
vars:
random_number1:
"{{ 10000 | random }}"
random_number2:
"{{ 10000000000 | random }}"
tasks:
- name: Copy the
/etc/hosts
to
/tmp/hosts
.{{ random_number1 }}
copy: src=
/etc/hosts
dest=
/tmp/hosts
.{{ random_number1 }}
notify:
- call
in
every action
- name: Copy the
/etc/hosts
to
/tmp/hosts
.{{ random_number2 }}
copy: src=
/etc/hosts
dest=
/tmp/hosts
.{{ random_number2 }}
notify:
- call
in
every action
handlers:
- name: call
in
every action
debug: msg=
"call in every action, but execute only one time"
|
action是Changed ,才会执行handler
只有当TASKS种的action的执行状态是changed时,才会触发notify handler的执行。
下面的脚本执行两次,执行结果是不同的:
第一次执行是,tasks的状态都是changed,会触发两个handler
第二次执行是,
第一个task的状态是OK,那么不会触发handlers"call by /tmp/hosts",
第二个task的状态是changed,触发了handler"call by /tmp/hosts.random_number"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
//
示例代码
---
- hosts: lb
remote_user: root
vars:
random_number:
"{{ 10000 | random }}"
tasks:
- name: Copy the
/etc/hosts
to
/tmp/hosts
copy: src=
/etc/hosts
dest=
/tmp/hosts
notify:
- call by
/tmp/hosts
- name: Copy the
/etc/hosts
to
/tmp/hosts
.{{ random_number }}
copy: src=
/etc/hosts
dest=
/tmp/hosts
.{{ random_number }}
notify:
- call by
/tmp/hosts
.random_number
handlers:
- name: call by
/tmp/hosts
debug: msg=
"call first time"
- name: call by
/tmp/hosts
.random_number
debug: msg=
"call by /tmp/hosts.random_number"
|
按Handler的定义顺序执行
andlers是按照在handlers中定义个顺序执行的,而不是安装notify的顺序执行的。
下面的例子定义的顺序是1>2>3,notify的顺序是3>2>1,实际执行顺序:1>2>3
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
|
---
- hosts: lb
remote_user: root
gather_facts: no
vars:
random_number1:
"{{ 10000 | random }}"
random_number2:
"{{ 10000000000 | random }}"
tasks:
- name: Copy the
/etc/hosts
to
/tmp/hosts
.{{ random_number1 }}
copy: src=
/etc/hosts
dest=
/tmp/hosts
.{{ random_number1 }}
notify:
- define the 3nd handler
- name: Copy the
/etc/hosts
to
/tmp/hosts
.{{ random_number2 }}
copy: src=
/etc/hosts
dest=
/tmp/hosts
.{{ random_number2 }}
notify:
- define the 2nd handler
- define the 1nd handler
handlers:
- name: define the 1nd handler
debug: msg=
"define the 1nd handler"
- name: define the 2nd handler
debug: msg=
"define the 2nd handler"
- name: define the 3nd handler
debug: msg=
"define the 3nd handler"
|
4、变量
playbook中常用变量的几种情况:
用户自定义的变量
用户可以在Playbook中,通过vars关键字自定义变量,使用时用{{ }}引用以来即可。
示例:
1
2
3
4
5
6
7
8
|
---
- hosts: web
vars:
http_port: 80
remote_user: root
tasks:
- name: insert firewalld rule
for
httpd
firewalld: port={{ http_port }}
/tcp
permanent=
true
state=enabled immediate=
yes
|
把变量放在单独的文件中
当变量比较多的时候,或者变量需要在多个playbook中重用的时候,可以把变量放到一个单独的文件中。通过关键字var_files把文件中定义的变量引入playbook中,使用变量的方法和在本文件中定义的变量相同。
1
2
3
4
5
6
7
8
9
10
|
- hosts: web
remote_user: root
vars_files:
- vars
/server_vars
.yml
tasks:
- name: insert firewalld rule
for
httpd
firewalld: port={{ http_port }}
/tcp
permanent=
true
state=enabled immediate=
yes
#cat vars/server_vars.yml
http_port: 80
|
定义复杂的变量
当变量的值不是简单的字符串或者数字,而是一个字典,语法如下
1
2
3
|
foo:
field1: one
field2: two
|
访问复杂变量中的子属性,可以利用中括号或者点号:
1
2
3
|
foo[
'field1'
]
或
foo.field1
|
系统变量(facts)
ansible会通过module setup来收集主机的系统信息,这些收集到的系统信息叫做facts,这些facts信息可以直接以变量的形式使用。
有哪些facts变量可以引用呢?在命令行上通过调用setup module命令可以查看:
1
|
$ ansible all -m setup -u root
|
系统变量在playbook中可以直接使用:
1
2
3
4
5
6
7
8
9
10
11
12
|
---
- hosts: all
user: root
tasks:
- name:
echo
system
shell:
echo
{{ ansible_os_family }}
- name
install
ntp on Debian linux
apt: name=git state=installed
when: ansible_os_family ==
"Debian"
- name
install
ntp on redhat linux
yum: name=git state=present
when: ansible_os_family ==
"RedHat"
|
对于复杂的、多层级的facts变量,可以通过下面的两种方式的任意一种访问复杂的变量中的子属性:
中括号:
1
|
{{ ansible_ens3[
"ipv4"
][
"address"
] }}
|
点号:
1
|
{{ ansible_ens3.ipv4.address }}
|
关系facts变量,一旦关闭之后就不用调用了。关闭方法:
1
2
|
- hosts: webserver
gather_facts: no
|
注册变量
把task的执行结果也可以作为一个变量值。这个时候就需要用到“注册变量”,将执行结果注册到一个变量中,待后面的action使用。
1
2
3
4
5
6
7
8
9
|
---
- hosts: web
tasks:
- shell:
ls
register: result
ignore_errors: True
- shell:
echo
"{{ result.stdout }}"
when: result.rc == 5
- debug: msg=
"{{ result.stdout }}"
|
注册变量经常和debug module一起使用,这样可以得到更多action的输出信息,帮助用户调试。
命令行中传递的变量
为了使Playbook更灵活、通用性更强,允许用户在执行的时候传入变量的值,这个时候就需要用到“额外变量”。
定义命令行变量
在test.yml文件里,hosts和user都定义为变量,需要从命令行传递变量值。
1
2
3
|