Ansible playbook详解

Playbook简介

Playbooks与Ad-Hoc相比,是一种完全不同的运用Ansible的方式,而且是非常之强大的;也是系统ansible命令的集合,其利用yaml语法编写,运行过程,ansbile-playbook命令根据自上而下的顺序依次执行任务。playbook 由一个或多个 ‘plays’ 组成.它的内容是一个以 ‘plays’为元素的列表,在 play 之中,一组机器被映射为定义好的角色.在 ansible 中,play 的内容,被称为 tasks,即任务.在基本层次的应用中,一个任务是一个对 ansible模块的调用。当第一个任务依次在所有主机上执行完毕后,开始执行第二个任务。如果某个主机执行时发生错误,则所有操作将会回滚。

Playbook基础组件

  • hosts:运行执行任务(task)的目标主机
  • remote_user:在远程主机上执行任务的用户
  • tasks:任务列表
  • handlers:任务,与tasks不同的是只有在接受到通知时才会被触发
  • templates:使用模板语言的文本文件,使用jinja2语法。
  • variables:变量,变量替换
  • tag:标签,为某tasks指定标签,运行该标签可以即运行特定的tasks,定义为always的tag总会执行
  • when: 条件判断,当条件成立则执行tasks,不成立不执行表达式 判断表达式如:not or and != =
  • with_items:循环迭代需要重复执行的任务列表,用{item}}引用列表值

例如一个简单的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
---
- hosts: test # 指定运行任务的主机组
remote_user: root # 指定远程执行任务的用户
vars: # 指定变量
- bsh: b.sh
- httprpm: httpd
task: # 任务的开始
- name: install httpd # 一个安装httpd的任务
yum: name={{ httprpm }} state=present # ansible的yum模块
tags: install_httpd # 为该任务打一个install_httpd的标签
- name: copy b.sh # 又一个复制b.sh脚本的任务
copy: src=/root/{{ bsh }} dest=/root/ owner=ala group=ala mode=0644
notify: # 如果copy的文件内容发生改变就会触发
- reload httpd # 指定通知的哪个handlers
when: ansible_distribution == "CentOS" # 通过变量判断系统为CentOS时才执行该copy任务
- name: copy b.sh
copy: src=/root/{{ bsh }} dest=/opt/ owner=ala group=ala mode=0644
notify:
- reload httpd
when: ansible_distribution == "Ubuntu" # 通过变量判断系统为Ubuntu时才执行该copy任务
- name: start httpd # 又一个启动httpd服务的任务
service: name=httpd state=started enabled=yes
handlers: # 满足触发条件则执行的任务
- name: reload httpd # 满足触发条件的任务名
service: name=httpd state=reloaded # 该任务为重新加载一下httpd服务

其中的ansible_distribution是ansible收集的facts变量。

playbook定义变量

ansible 常见定义变量有以下 6 种

  • /etc/ansible/hosts文件主机中定义
  • /etc/ansible/hosts/文件主机组中定义
  • playbook的yaml文件中通过vars定义
  • 获取系统变量,也称facts变量
  • 分文件定义主机和主机组的变量
  • 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
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# /etc/ansible/hosts文件主机中定义 主机变量
192.168.200.136 http_port=808 maxRequestsPerChild=808
192.168.200.137 http_port=8080 maxRequestsPerChild=909

# /etc/ansible/hosts文件主机中定义 主机组变量
[websers]
192.168.200.136
192.168.200.137

[websers:vars]
ntp_server=ntp.exampl.com
proxy=proxy.exampl.com # ntp_server和proxy变量可为websers组中主机使用

# playbook的yaml文件中通过vars定义
---
- hosts: test
remote_user: root
vars: # 定义bsh和httprpm的两个变量
- bsh: b.sh
- httprpm: httpd

# 获取系统facts变量
ansible 192.168.200.136 -m setup # 可以获取主机所有的facts变量,通过{{variable_name}}引用

# 分文件定义主机和主机组的变量
/etc/ansible/group_vars/websers # 定义主机组名为websers的变量文件
/etc/ansible/host_vars/hostpc # 定义主机名为hostpc的变量文件

$ cat /etc/ansible/host_vars/hostpc # 变量文件内容格式如下
---
ntp_server: acme.example.org
database_server: storage.example.org

# role中定义 (目录结构下文讲述)
$ cat /etc/ansible/roles/nginx/vars/main.yml
---
nginx_port: 80
nginx_domain: www.abc.com
nginx_user: nginx

playbook roles目录结构

Roles简介

Ansible为了层次化、结构化地组织Playbook,使用了角色(roles)。Roles能够根据层次型结构自动装载变量文件、task及handlers等。简单来讲,roles就是通过分别将变量、文件、任务、模块及处理器放置于单独的目录中,并可以便捷地include它们,roles一般用于基于主机构建服务的场景中,但也可以用于构建守护进程等场景中。

创建Roles

创建roles时一般需要以下步骤:首先创建以roles命名的目录。然后在roles目标下分别创建以这个角色名称命令的目录,如websevers等,然后在每个角色命令的目录中分别创建files、handlers、tasks、templates、meta、defaults和vars目录,用不到的目录可以创建为空目录。最后在Playbook文件中调用各角色进行使用。

可以使用命令快捷创建

1
$ ansible-galaxy init websevers

roles内各目录含义解释

  • files:用来存放由copy模块或script模块调用的文件。
  • templates:用来存放jinjia2模板,template模块会自动在此目录中寻找jinjia2模板文件。
  • tasks:此目录应当包含一个main.yml文件,用于定义此角色的任务列表,此文件可以使用include包含其它的位于此目录的task文件。
  • handlers:此目录应当包含一个main.yml文件,用于定义此角色中触发条件时执行的动作。
  • vars:此目录应当包含一个main.yml文件,用于定义此角色用到的变量。
  • defaults:此目录应当包含一个main.yml文件,用于为当前角色设定默认变量。
  • meta:此目录应当包含一个main.yml文件,用于定义此角色的特殊设定及其依赖关系。

如下定义了一个nginx服务的playbook目录结构树。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@k8s-master1 roles]# tree /etc/ansible/roles/nginx
/etc/ansible/roles/nginx
├── defaults
│   └── main.yml
├── files
│   └── nginx.tar.gz
├── handlers
│   └── main.yml
├── meta
│   └── main.yml
├── tasks
│   └── main.yml
├── templates
│   └── nginx.conf.j2
│   └── default.conf.j2
└── vars
└── main.yml

创建和使用角色

需求

  • 1、创建名为 apache 的角色

  • 2、httpd软件包已安装,设为在系统启动时启用

  • 3、防火墙已启用并正在运行,并使用允许访问 Web 服务器的规则

  • 4、模板文件 index.html.j2 已存在,用于创建具有以下输出的文件 /var/www/html/index.html:

    • Welcome to HOSTNAME on IPADDRESS
    • 其中,HOSTNAME 是受管节点的完全限定域名,IPADDRESS 则是受管节点的 IP 地址。
  • 5、按照下方所述,创建一个使用此角色的 playbook /home/ansible/ansible/newrole.yml

    • 该 playbook 在 webservers 主机组中的主机上运行

实例环境

主机名 IP 系统 角色
ansible 192.168.20.210 rhel8.6 ansible控制器
servera 192.168.20.211 rhel8.6 web服务器1
serverb 192.168.20.212 rhel8.6 web服务器2

环境准备

每台web服务器创建用户,并设置sudo提权

1
2
3
$ useradd devops
$ echo redhat | passwd --stdin devops
$ echo 'devops ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/devops

控制器创建普通用户并设置免密登录

1
2
3
4
5
6
7
$ useradd ansible
$ echo redhat | passwd --stdin ansible

$ ssh-keygen

$ ssh-copy-id devops@servera
$ ssh-copy-id devops@serverb

配置ansible环境

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ su - ansible
$ mkdir -p ansible/role
$ cd ansible

# 定义主机清单
$ vim inventory
[webserver]
servera
serverb

$ cp /etc/ansible/ansible.cfg .
$ vim ansible.cfg
# 修改如下内容
[defaults]
inventory = /home/ansible/ansible/inventory # 定义主机清单文件
remote_user = devops # 定义远程用户
roles_path = /home/ansible/ansible/roles # 定义roles路径
[privilege_escalation] # 这是一个标签,表示以下配置属于权限提升设置。
become=True # 表示允许在执行任务时进行权限提升。
become_method=sudo # 表示使用sudo命令进行权限提升。
become_user=root # 表示在进行权限提升后,用户身份将切换为root。
become_ask_pass=False # 表示在执行权限提升操作时,不需要输入root用户的密码。

创建角色

1
$ ansible-galaxy init apache

定义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
$ vim apache/tasks/main.yml

---
# tasks file for apache
- name: install http # 第一个任务名称
yum: # 模块名称
name: httpd # 要安装的软件名称
state: present # 模块选项
- name: config system service # 第二个任务名称
service: # 模块名称
name: "{{ item }}" # 要开机自启的软件名称
state: started # 模块选项,表示启动
enabled: yes # 模块选项,表示启用开机自启
loop: # 表示一个循环。它用于遍历一个序列
- httpd
- firewalld
- name: firewalld service # 第三个任务名称
firewalld: # 模块名称
zone: public # 开放区域
service: http # 开放service
permanent: yes # 是否永久生效
immediate: yes # 是否立即生效
state: enabled # 启动并运行
- name: use templates # 第四个任务名称
template: # 模块名称
src: index.html.j2 # 源文件
dest: /var/www/html/index.html # 目标文件

定义模版文件

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
$ vim apache/templates/index.html.j2
# ansible_fqdn和ansible_default_ipv4.address都来源于ansible内置变量,可以通过setup模块获取
Welcome to {{ ansible_fqdn }} on {{ ansible_default_ipv4.address }}

# setup示例
$ ansible node01 -m setup -a "filter=ansible_default_ipv4"
node01 | SUCCESS => {
"ansible_facts": {
"ansible_default_ipv4": {
"address": "10.0.1.204",
"alias": "ens192",
"broadcast": "10.255.255.255",
"gateway": "10.0.1.1",
"interface": "ens192",
"macaddress": "00:50:56:84:f7:a4",
"mtu": 1500,
"netmask": "255.0.0.0",
"network": "10.0.0.0",
"prefix": "8",
"type": "ether"
},
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false
}

定义role入口文件

1
2
3
4
5
6
$ vim newrole.yml

- name: use apache role
hosts: webservers
roles:
- apache

playbook语法检测

1
2
$ ansible-playbook --syntax-check newrole.yaml
playbook: newrole.yml

运行playbook

1
$ ansible-playbook newrole.yaml

验证安装结果

1
2
3
4
5
$ curl serverc
Welcome to servera.lab.example.com on 192.168.20.211

$ curl serverd
Welcome to serverb.lab.example.com on 192.168.20.212

playbook条件判断

在 Ansible Playbook 中,条件判断的基本语法是使用 when 关键字来控制任务的执行。例如:

1
2
3
4
5
- name: install software
yum:
name: software
state: present
when: ansible_os_type == 'redhat'

在这个例子中,when 关键字后面的表达式 ansible_os_type == 'redhat' 表示只有当受管节点的操作系统类型为 Red Hat 时,才会执行 install software 任务。

在 Ansible Playbook 中,可以使用变量来控制条件判断。例如:

1
2
3
4
5
6
7
- name: install software
yum:
name: software
state: present
when: ansible_os_type == 'redhat'
vars:
software_version: '7.0'

在这个例子中,software_version 变量被定义在 vars 关键字后面,表示安装的软件版本为 7.0。在 when 关键字后面的表达式中,可以使用 software_version 变量来控制任务的执行。

同样也可以使用ansible内置变量来控制条件判断。比如:

1
2
3
4
5
6
- name: create lv 600m
lvol:
vg: research
lv: data
size: 600m
when: "'research' in ansible_lvm.vgs"

在这个例子中,当researchvg存在时,则执行创建lv步骤。

playbook任务控制

在 Ansible Playbook 中,blockrescuealways 是三个常用的关键字,用于控制任务的执行流程。

block 关键字用于定义一个任务块,包含多个任务。在任务块中,可以使用 rescue 关键字来定义一个救援块,用于处理任务执行过程中可能出现的错误。always 关键字用于定义一个总是在任务块中执行的任务。

例如,下面是一个使用 blockrescuealways 的 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
---
- hosts: all
vars:
software_version: '7.0'
tasks:
- name: install software
yum:
name: software
state: present
when: ansible_os_type == 'redhat' and software_version == '7.0'
block:
- name: install additional software
yum:
name: additional_software
state: present
when: ansible_os_type == 'redhat' and software_version == '7.0'
rescue:
- AnsibleError
msg: "Error installing software"
always:
- name: restart services
service:
name: httpd
state: restarted


在这个例子中,install software 任务只有在受管节点的操作系统类型为 Red Hat,并且软件版本为 7.0 时才会执行。在任务块中,install additional software 任务只有在 install software 任务成功执行后才会执行。如果 install additional software 任务执行过程中出现错误,可以使用 rescue 关键字来定义一个救援块,处理错误。always 关键字用于定义一个总是在任务块中执行的任务,例如在这个例子中,restart services 任务总是在任务块中执行,无论前面的任务是否成功执行。

blockrescuealways 是 Ansible Playbook 中常用的关键字,用于控制任务的执行流程,提高 Playbook 的可读性和可维护性。

本文介绍了 Ansible Playbook 的基本概念、组件、目录结构和创建使用方法。通过使用 Playbook,可以更方便地管理和维护多个服务器。同时,Playbook 也提供了更高级的特性,如条件判断、循环迭代等,能够满足更复杂的需求。


Ansible playbook详解
https://www.zhoumx.net/Ansible-playbook详解.html
作者
阿星
发布于
2020年2月16日
许可协议