Ansible剧本(playbook)编写

Playbook简介

playbook是ansible用于配置,部署,和管理被控节点的剧本。
通过playbook的详细描述,执行其中的一系列tasks,可以让远端主机达到预期的状态。playbook就像Ansible控制器给被控节点列出的的一系列to-do-list,而被控节点必须要完成。
也可以这么理解,playbook 字面意思,即剧本,现实中由演员按照剧本表演,在Ansible中,这次由计算机进行表演,由计算机安装,部署应用,提供对外服务,以及组织计算机处理各种各样的事情。

Playbook的使用优势

  1. 功能比ansible命令更强大
  2. 能很好的控制先后执行顺序, 以及依赖关系
  3. 语法展现更加的直观
  4. ansible命令无法持久使用, playbook 可以持久使用

剧本的书写格式要求

语法格式

ansible剧本格式:遵循yaml语法格式(类似python脚本编写格式)
rsync配置文件格式:ini语法格式
sersync配置文件格式:xml语法格式(标签格式)

注意缩进

合理的信息缩进,两个空格表示一个缩进关系
一定不要使用tab

1
2
3
标题一
_ _ 标题二
_ _ _ _ 标题三

冒号

所有冒号后面都要加上空格

1
2
3
- hosts: 10.1.1.20
tasks:
yum: name=rsync state=installed

短横线 - 列表功能

使用短横线构成列表信息,短横线后面需要有空格

1
2
3
4
- boysec
man
- 爱好
台球

剧本书写

文件名格式

剧本文件拓展名为xxx.yaml

  1. 方便识别文件是一个剧本文件
  2. 文件编写时会有颜色提示

练习: 写一个剧本,使用yum/copy/service模块安装部署启动rsync服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
---
- hosts: rsync # 指定主机组或主机
remote_user: root # 远程用户
gather_facts: no # 是否先执行setup模块获取主机相关信息。
tasks: # 固定格式
- name: "01-add-user" # 指定名称
user: name=rsync create_home=no shell=/sbin/nologin uid=2021 # user模块
- name: "02-installed-rsync"
yum: name=rsync state=installed
- name: "03-copy rsync.conf"
copy: src=/playbook/rsync/rsyncd.conf dest=/etc/
- name: "04-create passwd conf"
copy: content="rsync_backup:123456" dest=/etc/rsync.passwd mode=0600
- name: "05-create rsync dir"
file: path=/backup state=directory owner=rsync group=rsync
- name: "06-started rsync service"
service: name=rsyncd state=started
- name: "07-enabled rsync service"
service: name=rsyncd enabled=yes

剧本检查模拟

1
2
3
4
5
6
7
8
## 检查
ansible-playbook --syntax-check rsync.yaml

## 模拟执行
ansible-playbook -C rsync.yaml

## 执行
ansible-playbook rsync.yaml

nfs服务剧本

服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- hosts: nfs_server
tasks:
- name: 01-add user
user:
name: www
shell: /sbin/nologin
create_home: no
state: present
uid: 1010
- name: 02-installed nfs server
yum: name=nfs-utils state=latest
- name: 03-copy nfs exports
copy: src=/playbook/exports dest=/etc/
- name: 04-create data dir
file: path=/nfs-data state=directory owner=www group=www
- name: 05-start rpcbind
service: name=rpcbind state=started enabled=yes
- name: 06-start nfs
service: name=nfs state=started enabled=yes

客户端

1
2
3
4
5
6
7
8
9
10
- hosts: nfs_client
tasks:
- name: 01-add user
user: name=www create_home=no shell=/sbin/nologin uid=1010
- name: 02-installed nfs-client
yum: name=nfs-utils state=latest
- name: 03-create mount data
file: path=/mnt/data state=directory owner=www
- name: 04-mount data
mount: path=/mnt/data src=10.1.1.60:/nfs-data fstype=nfs opts=defaults state=mounted

剧本高级特性

我们已经体验了使用剧本来安装服务,但是上述的简单ansible剧本存在一定的局限性

  1. 全部写成一行虽然看起来整洁,但是有一些特性没办法使用
  2. 比如同时需要创建多个目录,启动多个服务,需要重复写多条语句
  3. 参数不直观,不好修改
  4. 剧本里写的是启动服务,如果配置文件发生变化,重复执行不会重启服务

不过没有关系,等学习了下面的高级特性,然后我们可以换一种写法

循环(loop)

官方文档:https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html

使用情景:

  1. 需要创建多个目录
  2. 需要启动多个服务

具体实现:
同时创建2个目录/data和/backup

1
2
3
4
5
6
7
8
9
10
11
- hosts: 10.1.1.20
tasks:
- name: 01-create dir data and backuo
file:
path: "{{ item }}"
state: directory
owner: www
group: www
loop:
- /data
- /backup

同时创建多个用户(字典类型)

1
2
3
4
5
6
7
8
9
10
11
---
- name: create user
hosts: 10.1.1.20
tasks:
- name: create user and group
user:
name: "{{ item.name }}"
group: "{{ item.groups }}"
loop:
- {name: 'user01', groups: 'wheel'}
- {name: 'user02', groups: 'root'}

变量(vars)

官方文档:https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html
使用情景:

  1. 自定义某个名称,在任务中会多次引用
  2. 从主机收集的系统信息中提取某个变量并引用,例如网卡信息

具体实现:

自定义一个文件名变量,创建文件时引用:

1
2
3
4
5
6
7
8
9
10
- hosts: 10.1.1.20
vars:
file_name: boysec
tasks:
- name: 01-create vars dir
file:
path: "/data/{{ file_name }}"
state: directory
owner: www
group: www

使用变量获取主机的eth1地址:

1
2
3
4
- hosts: 10.1.1.20
tasks:
- name: 01-get ip address
shell: "echo {{ ansible_facts.eth0.ipv4.address }}" >/root/ip.txt

在主机hosts中指定变量:

1
2
3
4
5
6
tail -5 /etc/ansible/hosts
[backup]
10.1.1.30

[backup:vars]
file_name="boysec"

软件安装:

1
2
3
4
5
6
7
8
9
10
11
12
- hosts 10.1.1.20
vars:
- web_packages: nginx-1.15.2
- ftp_packages: vsftp

tasks:
- name: Installed {{ web_packages }} and {{ ftp_packages }}
yum:
name:
- "{{ web_packages }}"
- "{{ ftp_packages }}"
state: present

注册变量(register)

使用情景:将配置文件的状态注册成一个变量,方便其他任务引用
具体实现:
1.将配置文件的状态注册成一个服务变量并打印出来

1
2
3
4
5
6
7
8
- hosts: 10.1.1.20
tasks:
- name: 01-register nginx status
shell: netstat -lnpt|grep nginx
register: nginx_port
- name: 02-out nginx status
debug:
msg: "{{ nginx_port.stdout_lines }}"

2.打印多个信息:

1
2
3
4
5
6
7
8
9
10
11
12
- hosts: rsync
tasks:
- name: 01-echo hostname
shell: echo $(hostname)
register: rsync_hostname

- name: debug rsync_hostname
debug:
msg: "{{ item }}"
loop:
- "{{ rsync_hostname.stdout }}"
- "{{ rsync_hostname.cmd }}"

服务管理(service)

官方文档:https://docs.ansible.com/ansible/latest/user_guide/playbooks_intro.html?highlight=handlers
使用情景:如果配置文件发生了变化,就重启服务,否则什么都不操作
具体实现:

1
2
3
4
5
6
7
8
9
10
11
12
- hosts: 10.1.1.80
tasks:
- name: 01-if nfs conf changed,then restart nfs service
copy:
src: /playbook/exports
dest: /etc/
notify: Restart_nfs
handlers:
- name: Restart_nfs
service:
name: nfs
state: restarted

标签(tags)

官方文档:https://docs.ansible.com/ansible/latest/user_guide/playbooks_tags.html
使用情景:从我们指定的任务开始执行,而不是从头到尾执行一遍

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- hosts: rsync
tasks:
- name: "01-add-user"
user: name=rsync create_home=no shell=/sbin/nologin uid=2021
tags: 01-add-user
- name: "02-installed-rsync"
yum: name=rsync state=installed
tags: 02-install-rsync
- name: "03-copy rsync.conf"
copy: src=/playbook/rsync/rsyncd.conf dest=/etc/
tags: 03-copy-rsync.conf
- name: "04-create passwd conf"
copy: content="rsync_backup:123456" dest=/etc/rsync.passwd mode=0600
tags: 04-create passwd conf
- name: "05-create rsync dir"
file: path=/backup state=directory owner=rsync group=rsync
tags: 05-create dir
- name: "06-started rsync service"
service: name=rsyncd state=started enabled=yes
tags: 06-start rsync

调用标签:
1.打印出playbook里要执行的所有标签

1
ansible-playbook --list-tags tags.yaml

2.指定运行某个标签

1
ansible-playbook -t "04-create passwd conf" tags.yaml

3.指定运行多个标签,使用逗号隔开

1
ansible-playbook -t "04-create passwd conf","06-start rsync" tags.yaml

4.指定不运行某个标签

1
ansible-playbook --skip-tags="04-create passwd conf" tags.yaml

5.指定不运行多个标签

1
ansible-playbook --skip-tags="04-create passwd conf","06-start rsync" tags.yaml

触发器(handlers)

官方文档:https://docs.ansible.com/ansible/latest/user_guide/playbooks_handlers.html

handlers(触发器): 定义一些task列表,与之前剧本中task没有关系,只有资源发送变化才会采取一定的操作;

**notify(告警器)**:notify中调用handler中定义的操作;

例如:
我们来试试,此处我们使用httpd作为示例,虽然httpd可以使用systemctl restart httpd命令重载配置,但是此处的示例中并不会使用这个命令,而是用httpd类比那些需要重启生效的应用。

假设我们想要将httpd的端口从80改成8088,并且在修改配置以后重启httpd,那么我们可以编写如下剧本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
---
- hosts: webserver
remote_user: root
tasks:
- name: Modif the configuration
lineinfile:
path=/etc/httpd/conf/httpd.conf
regexp="^Listen 80"
line="Linsten 8080"
backrefs=yes
backup=yes
notify:
- restart httpd
handlers:
- name: restart httpd
service:
name=httpd
state=restarted

注意:无论多少个task通知相同的handlers,handlers仅会在tasks结束后运行一次。

条件判断(when)

官方文档:https://docs.ansible.com/ansible/latest/user_guide/playbooks_conditionals.html#basic-conditionals-with-when
通常,您希望根据事实执行或跳过任务。事实是单个主机的属性,包括 IP 地址、操作系统、文件系统的状态等等。基于事实的条件:

  • 只有在操作系统是特定版本时,才能安装特定包。
  • 您可以跳过在具有内部 IP 地址的主机上配置防火墙。
  • 只有在文件系统已满时,才能执行清理任务。

实例:
1.只关闭关闭Debian系统

1
2
3
4
tasks:
- name: Shut down Debian flavored systems
ansible.builtin.command: /sbin/shutdown -t now
when: ansible_facts['os_family'] == "Debian"

2.关闭系统为CentOS系统版本为6的

1
2
3
4
5
6
tasks:
- name: Shut down CentOS 6 systems
ansible.builtin.command: /sbin/shutdown -t now
when:
- ansible_facts['distribution'] == "CentOS"
- ansible_facts['distribution_major_version'] == "6"

回顾运行检查规范与补充

00.检查剧本拼写规范

1
ansible-playbook --syntax-check check.yaml

01.检查这个任务执行的主机对象

1
ansible-playbook --list-host check.yaml

02.检查这个剧本需要执行哪些任务

1
ansible-playbook --list-tasks check.yaml 

03.检查这个剧本执行哪些tag

1
ansible-playbook --list-tags check.yaml

04.模拟执行剧本

1
ansible-playbook -C check.yaml

05.针对某台主机执行剧本

1
ansible-playbook --limit 10.1.1.60 check.yaml

template 模板

模板是一个文本文件,可以做为生成文件的模版,并且模板文件中还可嵌套jinja语法

jinja2语言

网站:https://jinja.palletsprojects.com/en/2.11.x/

jinja2 语言使用字面量,有下面形式:

1
2
3
4
5
6
7
8
9
10
字符串:使用单引号或双引号
数字:整数,浮点数
列表:[item1, item2, …]
元组:(item1, item2, …)
字典:{key1:value1, key2:value2, …}
布尔型:true/false
算术运算:+, -, *, /, //, %, **
比较操作:==, !=, >, >=, <, <=
逻辑运算:and,or,not
流表达式:For,If,When

字面量:

表达式最简单的形式就是字面量。字面量表示诸如字符串和数值的 Python 对象。如“Hello World”
双引号或单引号中间的一切都是字符串。无论何时你需要在模板中使用一个字符串(比如函数调用、过滤器或只是包含或继承一个模板的参数),如42,42.23
数值可以为整数和浮点数。如果有小数点,则为浮点数,否则为整数。在 Python 里, 42 和 42.0 是不一样的

算术运算:

Jinja 允许用计算值。支持下面的运算符

1
2
3
4
5
6
7
+:把两个对象加到一起。通常对象是素质,但是如果两者是字符串或列表,你可以用这 种方式来衔接它们。无论如何这不是首选的连接字符串的方式!连接字符串见 ~ 运算符。 {{ 1 + 1 }} 等于 2
-:用第一个数减去第二个数。 {{ 3 – 2 }} 等于 1
/:对两个数做除法。返回值会是一个浮点数。 {{ 1 / 2 }} 等于 {{ 0.5 }}
//:对两个数做除法,返回整数商。 {{ 20 // 7 }} 等于 2
%:计算整数除法的余数。 {{ 11 % 7 }} 等于 4
*:用右边的数乘左边的操作数。 {{ 2* 2 }} 会返回 4 。也可以用于重 复一个字符串多次。 {{ ‘=’ *80 }} 会打印 80 个等号的横条\
**:取左操作数的右操作数次幂。 {{ 2**3 }} 会返回 8

比较操作符

1
2
3
4
5
6
7
== 比较两个对象是否相等
!= 比较两个对象是否不等

如果左边大于右边,返回 true
= 如果左边大于等于右边,返回 true
< 如果左边小于右边,返回 true
<= 如果左边小于等于右边,返回 true

逻辑运算符

1
2
3
4
5
6
对于 if 语句,在 for 过滤或 if 表达式中,它可以用于联合多个表达式
and 如果左操作数和右操作数同为真,返回 true
or 如果左操作数和右操作数有一个为真,返回 true
not 对一个表达式取反
(expr)表达式组
true / false true 永远是 true ,而 false 始终是 false

template

template功能:可以根据和参考模块文件,动态生成相类似的配置文件
template文件必须存放于templates目录下,且命名为 .j2 结尾
yaml/yml 文件需和templates目录平级,目录结构如下示例:

1
2
3
4
./
├── temnginx.yml
└── templates
└── nginx.conf.j2

范例:利用template 同步nginx配置文件

1
2
3
4
5
6
7
8
9
10
11
#准备templates/nginx.conf.j2文件
vim temnginx.yml
---
- hosts: websrvs
remote_user: root

tasks:
- name: template config to remote hosts
template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf

ansible-playbook temnginx.yml

template变更替换

范例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#修改文件nginx.conf.j2 
mkdir templates
vim templates/nginx.conf.j2
worker_processes {{ ansible_processor_vcpus }};

vim temnginx2.yml
---
- hosts: websrvs
remote_user: root

tasks:
- name: install nginx
yum: name=nginx
- name: template config to remote hosts
template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
- name: start service
service: name=nginx state=started enable=yes

ansible-playbook temnginx2.yml

template算术运算

范例:

1
2
3
vim nginx.conf.j2 
worker_processes {{ ansible_processor_vcpus**2 }};
worker_processes {{ ansible_processor_vcpus+2 }};

template中使用流程控制for和if

for单一值范例:

1
2
3
4
5
6
vim nginx.conf2.j2
{% for vhost in nginx_vhosts %}
server {
listen {{ vhost }}
}
{% endfor %}

for键值对范例:

1
2
3
4
5
6
vim nginx.conf3.j2
{% for vhost in nginx_vhosts %}
server {
listen {{ vhost.listen }}
}
{% endfor %}