文章目录
- Ansible剧本与变量判断循环
- YAML语言
- Palybook核心组件
- Playbook中使用变量
- Playbook逻辑控制语句
- template模板
- 环境变量
- changed_when/failed_when
-
- [关闭 changed 状态](#关闭 changed 状态)
- [利用 changed_when/failed_when 检查task返回结果](#利用 changed_when/failed_when 检查task返回结果)
- 滚动执行
- Playbook调试
- Playbook的handlers与notify
- 写playbook的原则
Ansible剧本与变量判断循环
-
playbook 剧本是由一个或多个"play"组成的列表
-
play的主要功能在于将预定义的一组主机,装扮成事先通过ansible中的task定义好的角色。Task实际是调用ansible的一个module,将多个play组织在一个playbook中,即可以让它们联合起来,按
-
事先编排的机制执行预定义的动作
-
Playbook 文件是采用YAML语言编写的
YAML语言
YAML官方网站:YAML 官方网站
Ansible官方文档:Ansible官方文档YAML语法
YAML:YAML Ain't Markup Language,即YAML不是标记语言。不过,在开发的这种语言时,YAML的
意思其实是:"Yet Another Markup Language"(仍是一种标记语言)YAML是一个可读性高的用来表达资料序列的格式。YAML参考了其他多种语言,包括:XML、C语言、Python、Perl以及电子邮件格式RFC2822等。Clark Evans在2001年在首次发表了这种语言,另外Ingy döt Net与Oren Ben-Kiki也是这语言的共同设计者,目前很多最新的软件比较流行采用此格式的文件存放配置信息,如:ubuntu,anisble,docker,kubernetes等
YAML语法简介
-
在单一文件第一行,用连续三个连字号"-" 开始,还有选择性的连续三个点号( ... )用来表示文件的结尾
-
次行开始正常写Playbook的内容,一般建议写明该Playbook的功能
-
使用#号注释代码
-
缩进必须是统一的,不能空格和tab混用
-
缩进的级别也必须是一致的,同样的缩进代表同样的级别,程序判别配置的级别是通过缩进结合换行来实现的
-
YAML文件内容是区别大小写的,key/value的值均需大小写敏感
-
多个key/value可同行写也可换行写,同行使用,分隔
-
key后面冒号要加一个空格 比如: key: value
-
value可是个字符串,也可是另一个列表
-
YAML文件扩展名通常为yml或yaml
支持的数据类型
YAML 支持以下常用几种数据类型:
-
标量:单个的、不可再分的值
-
对象:键值对的集合,又称为映射(mapping)/ 哈希(hashes) / 字典(dictionary)
-
数组:一组按次序排列的值,又称为序列(sequence) / 列表(list)
scalar
key对应value
shell
name: csq
age: 18
使用缩进的方式
shell
name:
csq
age:
18
标量是最基本的,不可再分的值,包括:
-
字符串
-
布尔值
-
整数
-
浮点数
-
Null
-
时间
-
日期
字典
字典由多个key与value构成,key和value之间用 :分隔, 并且 : 后面有一个空格,所有k/v可以放在一行,或者每个 k/v 分别放在不同行
shell
account: { name: csq, age: 18 }
使用缩进方式
shell
account:
name: csq
age: 18
范例
shell
#不同行
name: Example Developer
job: Developer
skill: Elite(社会精英)
#同一行,也可以将key:value放置于{}中进行表示,用,分隔多个key:value
{name: "Example Developer", job: "Developer", skill: "Elite"}
List列表
列表由多个元素组成,每个元素放在不同行,且元素前均使用"-"打头,并且 - 后有一个空格, 或者将所有元素用 [ ] 括起来放在同一行
格式
shell
course: [ linux , golang , python ]
也可与写成以-开头的多行
shell
course:
- linux
- golang
- python
数据里面也可以包含字典
shell
course:
- linux: csq
- golang: buhui
- python: buhui
范例
shell
#不同行,行以-开头,后面有一个空格
- Apple
- Orange
- Strawberry
- Mango
#同一行
[Apple,Orange,Strawberry,Mango]
yaml文件中的"|"和">" 符号
| 控制符
| 这个控制符的作用是保留文本每一行尾部的换行符。
|会保证整段文本最后有且只有一个换行符;使用|+可以保留整段文本最后的所有换行符;使用|-可以删除整段文本最后的所有换行符。
yaml
# --------"|"示例begin--------
# YAML格式
key: |
a
b
c
nextKey: ...
# 实际效果
"key": "a\nb\nc\n"
# ---------"|"示例end---------
# --------"|+"示例begin--------
# YAML格式
key: |+
a
b
c
nextKey: ...
# 实际效果
"key": "a\nb\nc\n\n\n"
# ---------"|+"示例end---------
# --------"|-"示例begin--------
# YAML格式
key: |-
a
b
c
nextKey: ...
# 实际效果
"key": "a\nb\nc"
# ---------"|-"示例end---------
>控制符
>这个控制符的作用是将每一行尾部的换行符替换为空格,也就是将多行文本视为一行。
>会保证文本最后有且只有一个换行符。使用>+可以保留文本最后的所有换行符,使用>-可以删除文本最后的所有换行符。
shell
# --------">"示例begin--------
# YAML格式
key: >
a
b
c
nextKey: ...
# 实际效果
"key": "a b c\n"
# ---------">"示例end---------
# --------">+"示例begin--------
# YAML格式
key: >+
a
b
c
nextKey: ...
# 实际效果
"key": "a b c\n\n\n"
# ---------">+"示例end---------
# --------">-"示例begin--------
# YAML格式
key: >-
a
b
c
nextKey: ...
# 实际效果
"key": "a\nb\nc"
# ---------">-"示例end---------
三种常见的数据格式
yaml转json:https://www.bejson.com/json/json2yaml/
-
XML:Extensible Markup Language,可扩展标记语言,可用于数据交换和配置
-
JSON:JavaScript Object Notation, JavaScript 对象表记法,主要用来数据交换或配置,不支持注释
-
YAML:YAML Ain't Markup Language YAML 不是一种标记语言, 主要用来配置,大小写敏感,不支持tab

Palybook核心组件
Playbook核心组件官方文档:https://docs.ansible.com/projects/ansible/latest/reference_appendices/playbooks_keywords.html
一个playbook 中由多个组件组成,其中所用到的常见组件类型如下:
-
Hosts 执行的远程主机列表
-
Tasks 任务集,由多个task的元素组成的列表实现,每个task是一个字典,一个完整的代码块功能需最少元素需包括 name 和 task,一个name只能包括一个task
-
Variables 内置变量或自定义变量在playbook中调用
-
Templates 模板,可替换模板文件中的变量并实现一些简单逻辑的文件
-
Handlers 和 notify 结合使用,由特定条件触发的操作,满足条件方才执行,否则不执行
-
tags 标签 指定某条任务执行,用于选择运行playbook中的部分代码。ansible具有幂等性,因此会自动跳过没有变化的部分,即便如此,有些代码为测试其确实没有发生变化的时间依然会非常地长。此时,如果确信其没有变化,就可以通过tags跳过此些代码片断
| 核心组件 | 说明 |
|---|---|
| tasks | Playbook 中要执行的具体任务列表(比如安装软件、修改配置等),是 Playbook 的核心内容。 |
| vars | 存储变量的集合(键值对形式),任务中可通过"{{ 变量名 }}"调用这些变量。 |
| vars_files | 需要导入的外部变量文件列表(将变量单独存放在文件中,便于管理和复用)。 |
| roles | 要引入的 "角色" 列表(角色是预定义的任务、变量、模板等的集合,用于简化复杂场景)。 |
| remote_user | 登录目标主机时使用的用户名(通过连接插件,如 SSH,登录并执行任务)。 |
| name | 用于标识 Playbook 或任务的名称,便于文档说明和执行结果中区分不同部分。 |
| hosts | 目标主机或主机组的列表(指定 Playbook 要在哪些主机上执行,支持组名、主机名或模式)。 |
| gather_facts | 布尔值(true/false),控制是否自动收集目标主机的事实信息(如主机属性、系统信息等)。 |
| become | 布尔值(true/false),控制执行任务时是否需要权限提升(如切换到 root 用户执行)。 |
| ignore_errors | 利用 ignore_errors: yes 可以忽略此task的错误,继续向下执行playbook其它task |
| handlers | 包含被视为处理程序的任务的部分,这些任务不会正常执行,只有在任务的每个部分完成后收到通知时才会执行。(搭配notify使用) |
| notify | 当任务返回"changed=True"状态时要通知的处理程序列表(搭配handlers使用) |
| tags | 应用于任务或包含任务的标签,这允许从命令行中选择任务的子集 |
利用playbook部署tomcat
shell
#部署tomcat
- hosts: web
tasks:
- name: 安装jdk11
yum:
name: java-11-openjdk
state: present
- name: 分发tomcat
unarchive:
src: /download/apache-tomcat-9.0.112.tar.gz
dest: /usr/local/
- name: 设置软链接简化路径
file:
src: /usr/local/apache-tomcat-9.0.112
path: /usr/local/tomcat
state: link
- name: 分发systemctl文件
copy:
src: /lib/systemd/system/tomcat.service
dest: /lib/systemd/system/tomcat.service
- name: 启动tomcat服务
systemd:
name: tomcat
state: started
daemon_reload: true
enabled: true
利用playbook部署nginx
shell
- hosts: web
tasks:
- name: 配置yum源
yum_repository:
name: nginx-last-packages
description: nginx-last-packages
baseurl: http://nginx.org/packages/centos/8/$basearch/
enabled: true
gpgcheck: false
- name: 清除缓存
shell: yum clean packages
- name: 安装nginx
yum:
name: nginx
state: latest
- name: 配置nginx用户组
group:
name: ansible-www
gid: 3999
- name: 配置nginx用户
user:
name: ansible-www
uid: 3999
group: ansible-www
create_home: true
shell: /sbin/nologin
- name: 修改nginx主配置文件
lineinfile:
path: /etc/nginx/nginx.conf
regexp: '^user'
line: "user ansible-www;"
- name: 配置nginx站点目录配置文件
copy:
src: ./bird.conf
dest: /etc/nginx/conf.d/bird.conf
- name: 创建nginx站点目录
file:
path: /app/code/bird/
state: directory
- name: 复制站点压缩包到远程主机/app/code/并解压
unarchive:
src: ./bird.tar.gz
dest: /app/code/bird/
remote_src: false
- name: 启动nginx
systemd:
name: nginx
state: started
利用playbook部署rsync
shell
#部署rsync
- hosts: nfs01
tasks:
- name: 安装rsync
yum:
name: rsync
state: present
- name: 修改配置文件
copy:
src: ./rsyncd.conf
dest: /etc/rsyncd.conf
backup: true
- name: 添加rsync用户
user:
name: rsync
create_home: false
shell: /sbin/nologin
system: true
- name: 创建密码文件,并写入密码
lineinfile:
path: /etc/rsync.password
line: rsync_backup:Abc@1234
create: true
mode: 0600
- name: 创建共享目录
file:
path: /backup
state: directory
owner: rsync
group: rsync
- name: 启动rsync服务
systemd:
name: rsyncd
state: restarted
enabled: true
利用playbook修改用户密码
shell
- hosts: web
tasks:
- name: "开始修改用户密码"
user:
name: csq
password: "{{ 'Abc@1234csq' | password_hash('sha512','hehedahehda') }}"
state: present
Playbook中使用变量
| ansible定义变量方法 | 说明 |
|---|---|
| palybook中定义 | 在剧本中创建与使用,仅限于当前play部分使用.vars |
| 独立文件中定义(变量文件) | 把写入变量到文件中,通过vars_files指定调取.play中指定变量文件 |
| 分组变量 | 推荐,根据分组自动调用.all组创建与使用.最方便(group_vars) |
| Facts变量(远程主机的系统变量) | 剧本运行的时默认的任务,收集信息,根据信息创建变量 如果不用,建议关闭功能.加速 |
| register变量 | 类似于shell中````功能,先执行命令结果保留下来 |
| 主机清单变量 | 批量修改主机名。批量修改密码(每台机器都不同) |
| 命令行传递参数 | 使用ansible-playbook -e "hostname=csq user=root"传递参数 |
| ansible魔法变量 | 不用加载Facts变量就可使用 hostvars:每个主机详细信息 groups:显示当前主机所属的组 group_names:列出当前主机所在的主机组名称 inventory_hostname:显示当前主机的主机名 |
palybook中定义vars
shell
- hosts: web
vars: #通过vars定义变量
- username: csq
- password: Abc@1234csq
tasks:
- name: "打印修改参数"
debug:
msg: "用户名: {{ username }} 密码: {{ password }}"
- name: "开始修改用户密码"
user:
name: csq
password: "{{ 'password' | password_hash('sha512','hehedahehda') }}"
state: present
独立文件中定义(变量文件)
shell
[root@m01 /server/ansible]# cat vars.yaml
username: ansible-www
uid_gid: 3999
nginx_dir: /app/code/bird/
nginx_conf: /etc/nginx/conf.d/
[root@m01 /server/ansible]# cat 04-modify-nginx-install.yaml
#部署nginx
- hosts: web
vars_files: ./vars.yaml #在此处定义变量文件
tasks:
- name: 配置yum源
yum_repository:
name: nginx-last-packages
description: nginx-last-packages
baseurl: http://nginx.org/packages/centos/8/$basearch/
enabled: true
gpgcheck: false
- name: 清除缓存
shell: yum clean packages
- name: 安装nginx
yum:
name: nginx
state: latest
- name: 配置nginx用户组
group:
name: "{{ username }}"
gid: "{{ uid_gid }}"
- name: 配置nginx用户
user:
name: "{{ username }}"
uid: "{{ uid_gid }}"
group: "{{ username }}"
create_home: true
shell: /sbin/nologin
- name: 修改nginx主配置文件
lineinfile:
path: /etc/nginx/nginx.conf
regexp: '^user'
line: "user {{ username }};"
- name: 配置nginx站点目录配置文件
copy:
src: ./bird.conf
dest: "{{ nginx_conf }}/bird.conf"
- name: 创建nginx站点目录
file:
path: "{{ nginx_dir }}"
state: directory
- name: 复制站点压缩包到远程主机/app/code/并解压
unarchive:
src: ./bird.tar.gz
dest: "{{ nginx_dir }}"
remote_src: false
- name: 启动nginx
systemd:
name: nginx
state: restarted
group_vars分组变量
shell
[root@m01 /server/ansible]# tree group_vars/
group_vars/
└── all
└── vars.yaml
[root@m01 /server/ansible]# cat 04-modify-nginx-install.yaml
#部署nginx
- hosts: web #不用添加变量文件,自动识别
tasks:
- name: 配置yum源
yum_repository:
name: nginx-last-packages
description: nginx-last-packages
baseurl: http://nginx.org/packages/centos/8/$basearch/
enabled: true
gpgcheck: false
- name: 清除缓存
shell: yum clean packages
- name: 安装nginx
yum:
name: nginx
state: latest
- name: 配置nginx用户组
group:
name: "{{ username }}"
gid: "{{ uid_gid }}"
- name: 配置nginx用户
user:
name: "{{ username }}"
uid: "{{ uid_gid }}"
group: "{{ username }}"
create_home: true
shell: /sbin/nologin
- name: 修改nginx主配置文件
lineinfile:
path: /etc/nginx/nginx.conf
regexp: '^user'
line: "user {{ username }};"
- name: 配置nginx站点目录配置文件
copy:
src: ./bird.conf
dest: "{{ nginx_conf }}/bird.conf"
- name: 创建nginx站点目录
file:
path: "{{ nginx_dir }}"
state: directory
- name: 复制站点压缩包到远程主机/app/code/并解压
unarchive:
src: ./bird.tar.gz
dest: "{{ nginx_dir }}"
remote_src: false
- name: 启动nginx
systemd:
name: nginx
state: restarted
Facts变量(远程主机的系统变量)
facts变量,运行剧本的时候,默认所有节点的fact变量的任务
- 如果需要直接使用,主机名字,发行版本
- 不需要则关闭,建议全局是关闭,使用的时候再去开启
shell
#在ansible.cfg配置文件中[defaults]添加
gathering=explicit
#常见的fact变量使用
主机名: "{{ ansible_hostname }}"
所有有ip: "{{ ansible_all_ipv4_addresses }}"
内存总大小: "{{ ansible_memtotal_mb }}"
系统发行版本: "{{ ansible_distribution }}"
系统版本: "{{ ansible_distribution_major_version }}"
cpu架构: "{{ ansible_architecture }}"
系统版本昵称: "{{ ansible_distribution_release }}"
第1块网卡的ip地址: "{{ ansible_default_ipv4.address }}"
范例:打印Facts变量(通过setup模块)
shell
[root@m01 /server/ansible]# ansible nfs01 -m setup
10.0.0.102 | SUCCESS => {
"ansible_facts": {
"ansible_all_ipv4_addresses": [
"172.16.1.102",
"10.0.0.102"
],
"ansible_all_ipv6_addresses": [
"fe80::5af3:9a3d:a6fd:ddff",
"fe80::84d:ff1a:2696:841d",
"fe80::f51d:e68f:f840:24af",
"fe80::eb8b:46e3:add2:d0d7",
"fe80::4454:49bf:ea82:21e1",
"fe80::a39d:5c8f:f395:7339"
],
"ansible_apparmor": {
"status": "disabled"
},
"ansible_architecture": "x86_64",
"ansible_bios_date": "11/12/2020",
"ansible_bios_vendor": "Phoenix Technologies LTD",
"ansible_bios_version": "6.00",
"ansible_board_asset_tag": "NA",
"ansible_board_name": "440BX Desktop Reference Platform",
"ansible_board_serial": "None",
"ansible_board_vendor": "Intel Corporation",
"ansible_board_version": "None",
"ansible_chassis_asset_tag": "No Asset Tag",
"ansible_chassis_serial": "None",
"ansible_chassis_vendor": "No Enclosure",
"ansible_chassis_version": "N/A",
"ansible_cmdline": {
"BOOT_IMAGE": "/vmlinuz-4.19.90-52.22.v2207.ky10.x86_64",
"audit": "0",
"quiet": true,
"rd.lvm.lv": "klas/swap",
"resume": "/dev/mapper/klas-swap",
"rhgb": true,
"ro": true,
"root": "/dev/mapper/klas-root"
},
"ansible_date_time": {
"date": "2025-11-12",
"day": "12",
"epoch": "1762937751",
"hour": "16",
"iso8601": "2025-11-12T08:55:51Z",
"iso8601_basic": "20251112T165551888691",
"iso8601_basic_short": "20251112T165551",
"iso8601_micro": "2025-11-12T08:55:51.888691Z",
"minute": "55",
"month": "11",
"second": "51",
"time": "16:55:51",
"tz": "CST",
"tz_dst": "CST",
"tz_offset": "+0800",
"weekday": "星期三",
"weekday_number": "3",
"weeknumber": "45",
"year": "2025"
},
"ansible_default_ipv4": {
"address": "10.0.0.102",
"alias": "ens33",
"broadcast": "10.0.0.255",
"gateway": "10.0.0.2",
"interface": "ens33",
"macaddress": "00:50:56:2d:c8:b7",
"mtu": 1500,
"netmask": "255.255.255.0",
"network": "10.0.0.0",
"type": "ether"
},
.....
.....
.....
template中使用Facts变量
在palybook中调用Facts变量
shell
#使用jinja2模板中的变量分发motd
[root@m01 /server/ansible]# cat motd.j2
主机名: {{ ansible_hostname }}
所有ip: {{ ansible_all_ipv4_addresses }}
内存总大小: {{ansible_memtotal_mb}}
系统发行版本: {{ansible_distribution}}
系统版本: {{ansible_distribution_major_version }}
cpu架构: {{ ansible_architecture }}
系统版本昵称: {{ ansible_distribution_release }}
第1块网卡的ip地址: {{ ansible_default_ipv4.address }}
#playbook
---
- hosts: all
gather_facts: true
tasks:
- name: test
debug:
msg: |
主机名: {{ ansible_hostname }}
所有ip: {{ ansible_all_ipv4_addresses }}
内存总大小: {{ansible_memtotal_mb}}
系统发行版本: {{ansible_distribution}}
系统版本: {{ansible_distribution_major_version }}
cpu架构: {{ ansible_architecture }}
系统版本昵称: {{ ansible_distribution_release }}
第1块网卡的ip地址: {{ ansible_default_ipv4.address }}
系统版本: {{ ansible_os_family }}
- name: 分发motd
template:
src: motd.j2
dest: /etc/motd
把运行结果当作变量使用(register变量)
在playbook中可以使用register将捕获命令的输出保存在临时变量中,然后使用debug模块进行显示输
出
范例:register变量输出形式
shell
#playbook
- hosts: nfs01
tasks:
- name: get hostname
shell: hostname
register: name
- name: print hostname
debug:
msg: "{{name}}"
#执行playbook
[root@m01 /server/ansible]# ansible-playbook -i /etc/ansible/hosts test.yaml
PLAY [nfs01] ************************************************************************************************************************************************************************
TASK [get hostname] *****************************************************************************************************************************************************************
changed: [10.0.0.102]
TASK [print hostname] ***************************************************************************************************************************************************************
ok: [10.0.0.102] => {
"msg": {
"changed": true,
"cmd": "hostname",
"delta": "0:00:00.001587",
"end": "2025-11-13 09:00:27.204286",
"failed": false,
"msg": "",
"rc": 0,
"start": "2025-11-13 09:00:27.202699",
"stderr": "",
"stderr_lines": [],
"stdout": "nfs1",
"stdout_lines": [
"nfs1"
]
}
}
PLAY RECAP **************************************************************************************************************************************************************************
10.0.0.102 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
范例:利用debug模块输出变量
shell
- hosts: nfs01
tasks:
- name: get hostname
shell: hostname
register: name
- name: print hostname
debug:
msg: |
输出register注册的name变量的全部信息: "{{ name }}"
显示命令的输出结果为字符串形式: "{{ name.stdout }}"
显示命令: "{{ name.cmd }}"
显示命令成功与否: "{{ name.rc }}"
显示命令的输出结果为列表形式: "{{ name.stdout_lines }}"
显示命令的输出结果的列表中的第一个元素: "{{ name.stdout_lines[0] }}"
显示命令的执行结果为列表形式: "{{ name['stdout_lines'] }}"
范例:使用register创建IP命名的目录
shell
#类似于shell中 ip=`hostname -I` 反引号功能
#创建以远程主机IP命名的目录
- hosts: all
tasks:
- name: 创建IP变量
shell: hostname -I | awk '{print $1}'
register: name
- name: 创建以IP命名的目录
file:
path: "/tmp/{{name.stdout}}"
state: directory
主机清单变量
shell
#主机清单文件
[root@m01 /server/ansible]# cat /etc/ansible/hosts
[nfs01]
10.0.0.102 hostname=nfs01
[by01]
10.0.0.110
[web]
10.0.0.10[3:4]
10.0.0.10[8:9]
[db01]
10.0.0.105
#编写playbook
---
- hosts: nfs01
tasks:
- name: 修改主机名
hostname:
name: "{{ hostname }}"
use: systemd
- name: 打印主机名
debug:
msg: "主机名为: {{hostname}}"
使用变量部署服务
利用playbook部署mysql
shell
#1.创建分组变量目录db01
mkdir -p group_vars/db01
#2.使用group_vars分组变量
cat > group_vars/db01/vars.yaml <<EOF
mysql_tools: ["ncurses-devel","libaio-devel","openssl-devel","wget"]
mysql_user: mysql
mysql_password: Abc@1234
EOF
#3.编写palybook
[root@m01 /server/ansible]# cat 07-installmysql.yaml
#mysql部署
- hosts: db01
tasks:
- name: "二进制安装{{ mysql_user }}8.4依赖"
yum:
name: "{{ item }}"
state: present
loop: "{{ mysql_tools }}"
- name: "分发{{ mysql_user }}二进制8.4压缩包"
unarchive:
src: ./mysql-8.4.6-linux-glibc2.28-x86_64.tar.xz
dest: /usr/local/
- name: "设置{{ mysql_user }}虚拟用户"
user:
name: "{{ mysql_user }}"
shell: /sbin/nologin
create_home: false
system: true
- name: "设置{{ mysql_user }}根目录软链接"
file:
src: /usr/local/mysql-8.4.6-linux-glibc2.28-x86_64
path: /usr/local/mysql
state: link
- name: "设置{{ mysql_user }}根目录权限"
file:
path: /usr/local/mysql/
state: directory
owner: "{{ mysql_user }}"
group: "{{ mysql_user }}"
recurse: true
- name: "分发{{ mysql_user }}配置文件"
copy:
src: my.cnf
dest: /etc/my.cnf
owner: "{{ mysql_user }}"
group: "{{ mysql_user }}"
- name: "创建数据目录"
file:
name: /usr/local/data/3306
state: directory
owner: "{{ mysql_user }}"
group: "{{ mysql_user }}"
- name: "配置PATH环境变量"
lineinfile:
path: /etc/profile
line: 'export PATH=${PATH}:/usr/local/mysql/bin'
- name: "初始化数据库"
shell: /usr/local/mysql/bin/mysqld --initialize-insecure --user=mysql --basedir=/usr/local/mysql/ --datadir=/usr/local/data/3306/
- name: "分发mysql8.4启动服务文件"
copy:
src: ./mysqld.service
dest: /lib/systemd/system/mysqld.service
- name: "启动mysql8.4"
systemd:
name: mysqld
enabled: true
state: restarted
- name: "设置mysql密码"
shell: /usr/local/mysql/bin/mysqladmin -uroot password '{{ mysql_password }}'
变量优先级
变量优先级官方文档:Ansible变量优先级
Playbook逻辑控制语句
条件判断语句when
when语句可以实现条件测试。如果需要根据变量、facts或此前任务的执行结果来做为某task执行与否的前提时要用到条件测试,通过在task后添加when子句即可使用条件测试
when的比较运算符
| 运算符 | 说明 | 示例(假设 num=5) |
|---|---|---|
== |
等于 | when: num == 5 |
!= |
不等于 | when: num != 3 |
> |
大于 | when: num > 2 |
< |
小于 | when: num < 10 |
>= |
大于等于 | when: num >= 5 |
<= |
小于等于 | when: num <= 5 |
when的逻辑运算符
or:逻辑或,当左右和右边两个表达式任意一个为真,则返回真and:逻辑与,当左边和右边两个表达式同时为真,则返回真not:逻辑否,对表达式取反():当一组表达式组合在一起,形成一个更大的表达式,组合内的所有表达式都是逻辑与的关系
条件判断
is exists: 用于路径存在时返回真is not exists: 用于路径不存在时返回真- 也可以在整个条件表达式的前面使用
not来取反
正则过滤器
yaml
变量 is match('正则表达式模式')
判断执行结果
succeeded:任务执行成功则返回truefailed:任务执行失败则返回truechanged:任务执行状态为changed则返回trueskipped:任务跳过则返回true
判断变量
defined:判断变量是否已定义,已定义则返回真undefined:判断变量是否未定义,未定义则返回真none:判断变量的值是否为空,如果变量已定义且值为空,则返回真
判断是否在集合
in: 用于检查一个元素是否在指定列表或集合中
范例1:使用when判断操作系统,不同操作系统安装不同软件
shell
---
- hosts: all
gather_facts: true
tasks:
- name: "kylin系统安装软件"
yum:
name: tree,telnet,nmap,nc
state: latest
when: ansible_distribution is match("Kylin")
- name: "ubuntu系统安装软件"
apt:
name: lolcat,lrzsz,cmatrix
state: latest
when: ansible_distribution is match("Ubuntu")
范例2:failed_when配合检查功能,检查nginx配置文件语法问题,有问题直接报错,没问题继续往下执行
shell
#部署nginx
- hosts: web
tasks:
- name: 配置yum源
yum_repository:
name: nginx-last-packages
description: nginx-last-packages
baseurl: http://nginx.org/packages/centos/8/$basearch/
enabled: true
gpgcheck: false
- name: 清除缓存
shell: yum clean packages
- name: 安装nginx
yum:
name: nginx
state: latest
- name: 配置nginx用户组
group:
name: ansible-www
gid: 3999
- name: 配置nginx用户
user:
name: ansible-www
uid: 3999
group: ansible-www
create_home: true
shell: /sbin/nologin
- name: 修改nginx主配置文件
lineinfile:
path: /etc/nginx/nginx.conf
regexp: '^user'
line: "user ansible-www;"
- name: 配置nginx站点目录配置文件
copy:
src: ./bird.conf
dest: /etc/nginx/conf.d/bird.conf
- name: 创建nginx站点目录
file:
path: /app/code/bird/
state: directory
- name: 复制站点压缩包到远程主机/app/code/并解压
unarchive:
src: ./bird.tar.gz
dest: /app/code/bird/
remote_src: false
- name: check nginx
shell: nginx -t
register: check_nginx_conf
failed_when: check_nginx_conf.rc != 0 #如果返回值为非0就不往下执行任务
- name: 启动nginx
systemd:
name: nginx
state: started
loop循环
对循环ansible有固定内置变量名为"item"
要在task中使用loop(with_items)给定要迭代的元素列表
列表元素格式:
-
字符串
-
字典
批量添加用户设置密码,批量创建目录
shell
- hosts: web
tasks:
- name: "批量创建目录"
file:
path: "{{ item }}"
state: directory
loop:
- '/tmp/csq' #字符串元素
- '/tmp/zhw' #字符串元素
- '/tmp/hehe' #字符串元素
- '/tmp/heheda' #字符串元素
- name: "批量创建用户设置密码"#字符串元素
user:
name: "{{ item.username }}"
uid: "{{ item.uid }}"
password: "{{ item.password | password_hash('sha512','cfcfcfhehehddd') }}"
loop:
- { username: "csq", uid: "3091",password: "Abc@1234csq" } #字典元素
- { username: "zhw", uid: "3092",password: "Abc@1234zhw" } #字典元素
- { username: "xbzz", uid: "3093",password: "Abc@1234xbzz" } #字典元素
- { username: "byd", uid: "3094",password: "Abc@1234byd" } #字典元素
Block块
当想在满足一个条件下,执行多个任务时,就需要分组了。而不再每个任务都是用when
shell
- hosts: test2
remote_user: root
tasks:
- block:
- name: install httpd memcached
yum: name={{ item }} state=installed
with_items:
- httpd
- memcached
- template: src=templates/httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf
- service: name=httpd state=started enabled=true
when: ansible_distribution is match ("Kylin") # 当系统为CentOS才会执行上面的block
处理异常也会很方便
rescue关键字在任务执行过程中捕获错误,并执行指定的任务
always关键字定义的任务始终会被执行,不管前面的任务执行成功还是失败
shell
- hosts: test2
remote_user: root
tasks:
- block:
- debug: msg="i execute normally" # 失败不会执行
- command: /bin/false
- debug: msg="i never execute,cause ERROR"
rescue:
- debug: msg='I caught an error' # 捕获错误但是最后还会执行
- command: /bin/false
- debug: msg="I also never execute"
always:
- debug: msg="this always executes" # 不管前面的任务执行成功还是失败都会执行
template模板
模板是一个文本文件,可以做为生成文件的模版,并且模板文件中还可嵌套jinja2语法
jinja2语言
Jinja 是一款快速、富有表现力且可扩展的模板引擎。特别 模板中的占位符允许编写与 Python 相似的代码 语法。然后通过模板数据来渲染最终文档
官方文档:https://jinja.palletsprojects.com/en/stable/
jinja2 语言支持多种数据类型和操作
-
字面量,如: 字符串:使用单引号或双引号,数字:整数,浮点数
-
列表:[item1, item2, ...]
-
元组:(item1, item2, ...)
-
字典:{key1:value1, key2:value2, ...}
-
布尔型:true/false
-
算术运算:+, -, *, /, //, %, **
-
比较操作:==, !=, >, >=, <, <=
-
逻辑运算:and,or,not
-
流表达式:For,If,When
算术运算
-
+:把两个对象加到一起。通常对象是素质,但是如果两者是字符串或列表,你可以用这 种方式来衔接它们。无论如何这不是首选的连接字符串的方式!连接字符串见 ~ 运算符。 {{ 1 + 1 }} 等于 2
-
-:用第一个数减去第二个数。 {{ 3 - 2 }} 等于 1
-
/:对两个数做除法。返回值会是一个浮点数。 {{ 1 / 2 }} 等于 0.5
-
//:对两个数做除法,返回整数商。 {{ 20 // 7 }} 等于 2
-
%:计算整数除法的余数。 {{ 11 % 7 }} 等于 4
-
*:用右边的数乘左边的操作数。 {{ 2 * 2 }} 会返回 4 。也可以用于重 复一个字符串多次。 {{ '=' * 80 }} 会打印 80 个等号的横条\
-
**:取左操作数的右操作数次幂。 {{ 2**3 }} 会返回 8
比较操作符
-
== 比较两个对象是否相等
-
!= 比较两个对象是否不等
-
> 如果左边大于右边,返回 true
-
>= 如果左边大于等于右边,返回 true
-
< 如果左边小于右边,返回 true
-
<= 如果左边小于等于右边,返回 true
逻辑运算符
-
对于 if 语句,在 for 过滤或 if 表达式中,它可以用于联合多个表达式
-
and 如果左操作数和右操作数同为真,返回 true
-
or 如果左操作数和右操作数有一个为真,返回 true
-
not 对一个表达式取反
-
(expr)表达式组
-
true / false true 永远是 true ,而 false 始终是 false
template中使用流程控制for和if
template中也可以使用流程控制 for 循环和 if 条件判断,实现动态生成文件功能
for循环
格式
shell
{% for i in EXPR %}
...
{% endfor %}
示例:
{% for i in range(1,10) %}
server_name web{{i}};
{% endfor %}
if条件判断
在模版文件中还可以使用 if条件判断,决定是否生成相关的配置信息
shell
{% if 条件表达式 %}
# 条件为 true 时生成的内容
{% elif 另一个条件表达式 %}
# 第一个条件为 false,第二个条件为 true 时生成的内容
{% else %}
# 所有条件都为 false 时生成的内容
{% endif %}
template
template功能:可以根据和参考模块文件,动态生成相类似的配置文件
template文件必须存放于templates目录下,且命名为 .j2 结尾
yaml/yml 文件需和templates目录平级
jinja2-判断-分发keepalived的配置文件
jinja2-判断-分发keepalived的配置文件
方法1:分别准备2个文件,分发到lb01,lb02
方法2:分发1个文件,配置文件中使用jinja2的判断
shell
global_defs {
router_id lb01 #lb01或lb02 判断
}
vrrp_instance vir_3 {
state MASTER #MASTER BACKUP
priority 100 #100 50
interface ens33
virtual_router_id 51
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
10.0.0.3 dev ens33 label ens33:0
}
}
方法2
shell
#keepalived.conf.j2
! Configuration File for keepalived
global_defs {
router_id {{ansible_hostname}}
}
vrrp_instance lb_vip_3 {
{% if ansible_hostname is match('lb01') %}
state MASTER
priority 100
{% elif ansible_hostname is match('lb02') %}
state BACKUP
priority 50
{% endif %}
interface ens33
virtual_router_id 51
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
10.0.0.3/24 dev ens33 label ens33:1
}
}
#playbook
- hosts: web
gather_facts: true
tasks:
- name: "分发配置文件"
template:
src: keepalived.conf.j2
dest: /tmp/keepalived.conf
#结果
ansible lb -m shell -a "cat /tmp/keepalived.conf
#lb02
10.0.0.107 | CHANGED | rc=0 >>
! Configuration File for keepalived
global_defs {
router_id lb02
}
vrrp_instance lb_vip_3 {
state BACKUP
priority 50
interface ens33
virtual_router_id 51
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
10.0.0.3/24 dev ens33 label ens33:1
}
}
#lb01
10.0.0.106 | CHANGED | rc=0 >>
! Configuration File for keepalived
global_defs {
router_id lb01
}
vrrp_instance lb_vip_3 {
state MASTER
priority 100
interface ens33
virtual_router_id 51
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
10.0.0.3/24 dev ens33 label ens33:1
}
}
循环
使用jinja2循环nfs配置文件
shell
#1.生成目标
/nfsdata 172.16.1.0/24(rw,all_squash,anonuid=2999,anongid=2999)
/share 172.16.1.0/24(rw,all_squash,anonuid=2999,anongid=2999)
/private 172.16.1.0/24(rw,all_squash,anonuid=2999,anongid=2999)
#2.变量在group_vars/all/vars.yaml文件中定义列表变量
nfs_dirs:
- /nfsdata
- /share
- /private
#3.生成jinja2模板exports.j2
{% for dir in nfs_dirs %}
#nfs服务端目录 {{ dir }}
{{dir}} 172.16.1.0/24(rw,all_squash,anonuid=2999,anongid=2999)
{% endfor %}
#4.编写playbook
- hosts: nfs01
tasks:
- name: "jinja2循环生成内容"
template:
src: exports.j2
dest: /tmp/exports
#5.生成文件内容
ansible nfs01 -m shell -a "cat /tmp/exports"
10.0.0.102 | CHANGED | rc=0 >>
#nfs服务端目录 /nfsdata
/nfsdata 172.16.1.0/24(rw,all_squash,anonuid=2999,anongid=2999)
#nfs服务端目录 /share
/share 172.16.1.0/24(rw,all_squash,anonuid=2999,anongid=2999)
#nfs服务端目录 /private
/private 172.16.1.0/24(rw,all_squash,anonuid=2999,anongid=2999)
使用jinja2模板循环生成nginx配置文件(静态)
shell
#1.生成目标
server {
listen 80;
server_name bird.chenshiquan.xyz;
access_log /var/log/nginx/bird-access.log main;
error_log /var/log/nginx/bird-error.log notice;
root /app/code/bird/;
location / {
index index.html
}
}
server {
listen 80;
server_name test.chenshiquan.cn;
access_log /var/log/nginx/bird-access.log main;
error_log /var/log/nginx/bird-error.log notice;
root /app/code/bird/;
location / {
index index.html
}
}
server {
listen 80;
server_name china.chenshiquan.cn;
access_log /var/log/nginx/bird-access.log main;
error_log /var/log/nginx/bird-error.log notice;
root /app/code/bird/;
location / {
index index.html
}
}
#2.变量在group_vars/all/vars.yaml文件中定义列表变量
sites:
- bird
- test
- china
#3.生成jinja2模板nginx.conf.j2
server {
listen 80;
server_name {{item}}.chenshiquan.cn;
access_log /var/log/nginx/{{item}}-access.log main;
error_log /var/log/nginx/{{item}}-error.log notice;
root /app/code/{{item}}/;
location / {
index index.html
}
}
#4.编写playbook
---
- hosts:
tasks:
- name: "jinja2模板循环生成nginx配置文件"
template:
src: nginx.conf.j2
dest: /tmp/{{item}}.chenshiquan.xyz.conf
loop: "{{sites}}"
...
#5.生成结果
ansible web -m shell -a "cat /tmp/*.chenshiquan.xyz.conf"
10.0.0.104 | CHANGED | rc=0 >>
server {
listen 80;
server_name bird.chenshiquan.cn;
access_log /var/log/nginx/bird-access.log main;
error_log /var/log/nginx/bird-error.log notice;
root /app/code/bird/;
location / {
index index.html
}
}
server {
listen 80;
server_name china.chenshiquan.cn;
access_log /var/log/nginx/china-access.log main;
error_log /var/log/nginx/china-error.log notice;
root /app/code/china/;
location / {
index index.html
}
}
server {
listen 80;
server_name test.chenshiquan.cn;
access_log /var/log/nginx/test-access.log main;
error_log /var/log/nginx/test-error.log notice;
root /app/code/test/;
location / {
index index.html
}
}
10.0.0.103 | CHANGED | rc=0 >>
server {
listen 80;
server_name bird.chenshiquan.cn;
access_log /var/log/nginx/bird-access.log main;
error_log /var/log/nginx/bird-error.log notice;
root /app/code/bird/;
location / {
index index.html
}
}
server {
listen 80;
server_name china.chenshiquan.cn;
access_log /var/log/nginx/china-access.log main;
error_log /var/log/nginx/china-error.log notice;
root /app/code/china/;
location / {
index index.html
}
}
server {
listen 80;
server_name test.chenshiquan.cn;
access_log /var/log/nginx/test-access.log main;
error_log /var/log/nginx/test-error.log notice;
root /app/code/test/;
location / {
index index.html
}
}
使用jinja2模板循环配置rsync文件
shell
#1.生成目标
....
....
[data]
comment = date by chenshiquan 14:18 2025-11-12
path = /data
[backup]
comment = backup by chenshiquan 14:18 2025-11-12
path = /backup
[blog]
comment = blog by chenshiquan 14:18 2025-11-12
path = /nfs/backup/blog
#2.变量在group_vars/all/vars.yaml文件中定义列表变量
rsync_dirs:
- {name: "date",dir: "/data"}
- {name: "backup",dir: "/backup"}
- {name: "blog",dir: "/nfs/backup/blog"}
#3.生成jinja2模板rsyncd.conf.j2
{% for dir in rsync_dirs %}
[{{dir.name}}]
comment = {{dir.name}} by chenshiquan 14:18 2025-11-12
path = {{dir.dir}}
{% endfor %}
#4.编写playbook
---
- hosts: backup
tasks:
- name: "jinja2模板循环生成rsync配置文件"
template:
src: rsyncd.conf.j2
dest: /tmp/rsyncd.conf
loop: "{{rsync_dirs}}"
...
#5.生成结果
[date]
comment = date by chenshiquan 14:18 2025-11-12
path = /data
[backup]
comment = backup by chenshiquan 14:18 2025-11-12
path = /backup
[blog]
comment = blog by chenshiquan 14:18 2025-11-12
path = /nfs/backup/blog
环境变量
临时修改环境变量
shell
#playbook
- hosts: localhost
gather_facts: true
tasks:
- shell: echo $PATH
environment:
PATH: /usr/local/app/bin:{{ ansible_env.PATH }}
register: path_result
- debug:
msg: "{{path_result}}"
#输出详解
[root@m01 /server/ansible]# ansible-playbook -i /etc/ansible/hosts test.yaml
......
......
TASK [debug] ************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": {
"changed": true,
"cmd": "echo $PATH",
"delta": "0:00:00.001392",
"end": "2025-11-13 08:37:17.431355",
"failed": false,
"msg": "",
"rc": 0,
"start": "2025-11-13 08:37:17.429963",
"stderr": "",
"stderr_lines": [],
"stdout": "/usr/local/app/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root/bin",
"stdout_lines": [
"/usr/local/app/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root/bin"
]
}
}
......
......
changed_when/failed_when
关闭 changed 状态
当确定某个task不会对被控制端做修改时但执行结果却显示是黄色的changed状态,可以通过changed_when: false 关闭changed状态
shell
---
- hosts: web
tasks:
- name: "check sshd service"
shell: ps aux | grep sshd
changed_when: false
利用 changed_when/failed_when 检查task返回结果
changed_when /failed_when检查task返回结果,决定是否继续向下执行
changed_when
shell
---
- hosts: web
tasks:
- name: install nginx
yum:
name: nginx
- name: move template
template:
src: "nginx.conf.j2"
dest: /etc/nginx/nginx.conf
notify:
- restart nginx
- name:
shell: /usr/sbin/nginx -t
register: check_nginx_conf
changed_when:
- check_nginx_conf.stdout is match('successful') #如果返回值正则匹配不是successful就不执行下面内容
- name: start nginx
systemd:
name: nginx
state: restarted
enabled: true
- handlers:
- name: restart nginx
systemd:
name: nginx
state: restarted
failed_when
shell
---
- hosts: web
tasks:
- name: install nginx
yum:
name: nginx
- name: move template
template:
src: "nginx.conf.j2"
dest: /etc/nginx/nginx.conf
notify:
- restart nginx
- name:
shell: /usr/sbin/nginx -t
register: check_nginx_conf
failed_when:
- check_nginx_conf.rc != 0 #如果返回值为非0就不执行下面内容
- name: start nginx
systemd:
name: nginx
state: restarted
enabled: true
- handlers:
- name: restart nginx
systemd:
name: nginx
state: restarted
滚动执行
默认情况下,Ansible将尝试并行管理playbook中所有的机器。对于滚动更新用例,可以使用serial 关键字定义Ansible一次应管理多少主机,还可以将serial关键字指定为百分比,表示每次并行执行的主机数占总数的比例
范例1:每次只同时处理2个主机,将所有task执行完成后,再选下2个主机再执行所有task,直至所有主机
shell
---
- hosts: all
serial: 2
tasks:
- name: task one
command: hostname
- name: task two
command: hostname
范例2:每次只同时处理20%的主机
shell
---
- hosts: all
serial: "20%"
tasks:
- name: task one
command: hostname
- name: task two
command: hostname
Playbook调试
命令调试功能
shell
--syntax-check #对 playbook 执行语法检查,但不执行它
-C #不要进行任何更改
-v 或 -vv 或 -vvv #显示过程
--step #一步一步:在运行前确认每个任务
--limit #只针对主机列表中的特定主机执行
指定报错的机器运行Playbook
手动/自动指定错误机器
playbook执行任务,有部分主机执行失败,下次执行时怎么指定只在失败的主机上执行任务?
ansible retry重试
手动指定错误机器不依赖retry
shell
#使用--limit host1,host2...
ansible-playbook test.yaml --limit nfs01,backup
自动指定错误机器
shell
#需要修改ansible.cfg配置文件在[defaults]下修改
retry_files_enabled = True
retry_files_save_path = /ansible_retry/
#执行剧本制造失败
[root@m01 /server/ansible]# cat /ansible_retry/test.retry
10.0.0.101
10.0.0.102
10.0.0.103
#下次修改错误后,指定错误机器即可
ansible-playbook test.yaml --limit @/ansible_retry/test.retry
委派至错误主机执行playbook
利用委托技术,可以在非当前被控主机的其它主机上执行指定操作
shell
#playbook,在10.0.0.102上执行hostname -I 而非主机localhost
- hosts: localhost
tasks:
- name: show ip address
command: hostname -I
delegate_to: 10.0.0.102
TAGS标签
Ansible 标签用于在大型 playbook 中精准执行或跳过特定任务,核心流程分两步
-
将标签添加到任务中,可以单独添加,也可以从block、playbook、role或导入继承标签,实现对目标任务的标记。
-
运行 playbook 时选择或跳过标记
-
--tags指定要运行的标签(仅执行标记的任务) -
--skip-tags指定要跳过的标签 -
--tags tagged指定要执行标记了标签的任务 -
--tags untagged指定要执行没有标记标签的任务 -
--tags all执行所有任务 -
如果把标签的名字定义为
always,那么always标签所对应的任务就始终会被执行
-
标签基本用法
shell
#部署nginx的playbook,在其中指定标签
- hosts: web
gather_facts: true
vars:
- ubuntu: nginx=1.28.0-1~jammy
- kylin: nginx-1:1.28.0
tasks:
#ubuntu安装nginx
- block:
- name: ubuntu安装源依赖
apt:
name: curl,gnupg2,ca-certificates,lsb-release,ubuntu-keyring
state: latest
update_cache: true
- name: ubuntu配置源密钥
apt_key:
url: https://nginx.org/keys/nginx_signing.key
keyring: /usr/share/keyrings/nginx-archive-keyring.gpg
state: present
- name: ubntu配置源仓库
apt_repository:
repo: "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] http://nginx.org/packages/ubuntu jammy nginx"
filename: nginx
state: present
update_cache: true
- name: ubuntu安装nginx
apt:
name: "{{ubuntu}}"
state: present
update_cache: true
tags:
- install_ubuntu_nginx
when: ansible_distribution is match("Ubuntu")
#kylin安装nginx
- block:
- name: 配置kylin的yum源
yum_repository:
name: nginx-last-packages
description: nginx-last-packages
baseurl: http://nginx.org/packages/centos/8/$basearch/
enabled: true
gpgcheck: false
- name: 清除缓存
shell: yum clean packages
- name: kylin安装nginx
yum:
name: "{{kylin}}"
state: present
tags:
- install_kylin_nginx
when: ansible_distribution is match("Kylin")
- name: 配置nginx用户组
group:
name: ansible-www
gid: 3999
tags:
- add_group
- nginx_config
- name: 配置nginx用户
user:
name: ansible-www
uid: 3999
group: ansible-www
create_home: true
shell: /sbin/nologin
tags:
- add_user
- nginx_config
#1.执行的时候不带任何tag参数,那么会执行所有标签对应的任务
ansible-playbook 19testnginx.yaml
#2.如果指定执行部分安装的任务,则可利用关键字tags指定需要执行的任务
ansible-playbook 19testnginx.yaml --tags install_ubuntu_nginx
#3.如果指定不执行tag packages对应的任务,则可利用关键字skip-tags
ansible-playbook 19testnginx.yaml --skip-tags install_ubuntu_nginx
Playbook的handlers与notify
Handlers本质是任务列表,类似MySQL触发器的触发行为,其包含的任务与一般task无本质区别,用于关注的资源发生变化时执行操作;而Notify对应的action会在每个play最后触发,可避免多次变化时重复执行操作,仅在所有变化完成后一次性执行,且Notify中列出的操作即调用Handlers中定义的操作
- 如果多个task通知了相同的handlers, 此handlers仅会在所有tasks结束后运行一 次。
- 只有notify对应的task发生改变了才会通知handlers, 没有改变则不会触发handlers
- handlers 是在所有前面的tasks都成功执行才会执行,如果前面任何一个task失败,会导致handler跳过执行,可以使用force_handlers: yes 强制执行handler
shell
#playbook
---
- hosts: web
tasks:
- name: "jinja2模板循环生成nginx配置文件"
template:
src: nginx.conf.j2
dest: /tmp/{{item}}.chenshiquan.xyz.conf
loop: "{{sites}}"
notify:
- restart nginx
handlers:
- name: "restart nginx"
systemd:
name: nginx
state: restarted
...
#template模块会对比本地模板文件(nginx.conf.j2)与远程主机目标文件
#(/tmp/{{item}}.chenshiquan.xyz.conf)的内容
#若两者内容相同,template任务不会对目标文件做任何修改(任务状态为ok)
#此时notify不会触发,handlers中定义的restart nginx操作不会执行
#若两者内容不同,template任务会更新目标文件(任务状态为changed),此时notify会标记对应的handler
#在整个 play 执行结束后,触发handlers中的restart nginx操作,重启 nginx 服务
写playbook的原则
- 尽量使用include和role避免重复代码
- 尽量把大文件分成小的文件
- 重要的一点:多练习(操千曲而后晓声,观千剑而后识器)