主要内容:
Ansible进阶(firewalld模块、template模块、语法:error、handlers、when条件判断、block任务块、loop循环)、Ansible Role基本概念、Roles标准规范、管理Anisble Role
一、ansible应用案例(进阶)
案例要求:
- 1)熟悉firewalld和template模块(识别变量)的使用
- 2)熟悉error处理机制(ignore_error)
- 3)熟悉handlers任务(notify通知)
- 4)熟悉when条件判断
- 5)熟悉block任务块(block、rescue、always)
- 6)熟悉loop循环的使用方法
1、firewalld模块
- 使用firewalld模块可以配置防火墙策略
帮助:ansible-doc firewalld
[root@control ansible]# vim ~/ansible/firewall.yml
---
- hosts: test
tasks:
- name: install firewalld. //任务1描述,安装firewalld
yum: //调用yum模块安装软件
name: firewalld //需安装的软件名称为firewalld
state: present //state: present代表安装软件
- name: run firewalld. //任务2描述,运行firewalld
service: //调用service模块启动服务
name: firewalld //启动的服务名称为firewalld
state: started //state: started代表启动服务
enabled: yes //enabled: yes是设置服务为开机自启
#### 以上任务已安装firewalld,已运行firewalld可忽略 ####
- name: set firewalld rule. //任务描述,设置防火墙规则
firewalld: //调用firewalld模块设置防火墙规则
port: 80/tcp //在防火墙规则中添加一个放行tcp,80端口的规则
permanent: yes //permanent 是设置永久规则
immediate: yes //immediate 是让规则立刻生效
state: enabled //state等于enabled是添加防火墙规则,相反disabled
[root@control ansible]# ansible-playbook firewall.yml
[root@node1 ~]# firewall-cmd --list-all //验证
public (active)
ports: 80/tcp
...
补充:临时规则,可不加 permanent
补充:不指定被控制端的防火墙区域,默认修改的为当前区域
2、template模块
1)copy模块
可以将一个文件拷贝给远程主机,但用户希望每个拷贝的文件内容都不一样(涉及变量),例如给所有web主机拷贝index.html内容是各自的IP地址,则需要用到template模块。
- copy模块与template模块语法与作用一样,区别是copy模块拷贝文件中的变量是不识别的,template模块拷贝的变量会识别;
- 补充:Ansible可以利用Jinja2模板引擎读取变量,之前在playbook中调用变量也是Jinja2的功能,Jinja2模块的表达式包含在分隔符"{{ }}"内。
2)template模块
参数:src指定需要拷贝的源文件,dest指定需要拷贝的目标位置
帮助:ansible-doc template
例如:给webserver主机拷贝index.html文件并放到/tmp/目录,要求每个文件内容不同(facts变量)
[root@control ansible]# vim ~/ansible/index.html //准备测试的源文件
Welcome to {{ansible_hostname}} on {{ ansible_eth0.ipv4.address }}.
[root@control ansible]# vim ~/ansible/template.yml
---
- hosts: webserver
tasks:
- name: use template copy index.html to webserver. //任务描述,需要调用template模块
template:
src: ~/ansible/index.html //相对或绝对路径都可以,保证能找到文件
dest: /tmp/index.html
[root@control ansible]# ansible-playbook template.yml
[root@node3 ~]# cat /tmp/index.html //验证
Welcome to node3 on 192.168.4.13.
[root@node4 ~]# cat /tmp/index.html //验证
Welcome to node4 on 192.168.4.14.
解释说明:
# {{ansible_hostname}}和{{ ansible_eth0.ipv4.address }}是ansible自动的facts变量
# src参数: 指定源,~/ansible/index.html是在控制端创建的测试源文件,文件包含变量
# dest参数: 指定目标,将源文件拷贝到被控制端目标主机放在/tmp目录下
3、Ansible高级语法应用
应用案例:error、handles、when、block、loop
3.1 error 错误处理
默认ansible在遇到error会立刻停止playbook;假设一个剧本里有20个任务,执行到第3个时失败,则不再往下执行任务;(使用ignore_errors可忽略错误任务,继续后续的任务);
参数:ignore_errors关键词,针对某个任务或全局任务忽略错误(取决于关键词位置)
示例1:
① 测试Playbook执行一个错误任务,查看执行的意外中断效果
[root@control ansible]# vim ~/ansible/error.yml
---
- hosts: test
tasks:
- name: start a service that does not exit.
service:
name: hehe //启动一个不存在的服务hehe,测试error报错
state: started
- name: touch a file.
file:
path: /tmp/service.txt
state: touch
[root@control ansible]# ansible-playbook error.yml
结果:执行成功任务1次,错误任务1次,执行到错误任务立刻停止playbooky,所以中断了后续的touch任务;
② 测试Playbook,执行时因为忽略了错误(针对某一个任务),不会被中断。
[root@control ansible]# vim ~/ansible/error.yml
---
- hosts: test
tasks:
- name: start a service that does not exit.
service:
name: hehe //启动一个不存在的服务
state: started
ignore_errors: true //ignore_errors是关键词,针对某一个任务忽略错误
- name: touch a file.
file:
path: /tmp/service.txt
state: touch
[root@control ansible]# ansible-playbook error.yml
结果:执行成功任务3次(包括默认任务、改变任务、忽略任务),错误任务0次,执行到被针对的错误任务后被忽略,并执行后续的touch任务;
注意:ignore_errors 需要与 service任务为同一层级,才能实现忽略service任务错误效果
示例2:
测试Playbook,执行时因为忽略了错误(针对所有任务),不会被中断
[root@control ansible]# vim ~/ansible/error.yml
---
- hosts: test
ignore_errors: true //针对playbook全局忽略错误(位置不同,实现效果不同)
tasks:
- name: start a service that does not exit.
service:
name: hehe
state: started
- name: touch a file.
file:
path: /tmp/service.txt
state: touch
[root@control ansible]# ansible-playbook error.yml
结果:执行成功任务3次(包括默认任务、改变任务、忽略任务),错误任务0次,针对全局的所有错误任务都会被忽略,并执行后续的touch任务;
注意:ignore_errors需要与tasks为同一层级,且要在tasks任务前,才能实现忽略全局任务错误效果
3.2 handlers
在剧本中 tasks用来定义任务(一定会执行),handlers也可以定义任务(不一定执行),handlers任务要想执行必须要被notify通知触发才能执行。
- 可以通过 handlers定义一组任务,仅当某个任务触发 (notify)handlers时,才执行相应的任务,如果有多个notify触发执行 handlers任务,也仅执行一次。
- handlers 任务在所有其他 tasks任务都执行完成后才执行。
- 仅当任务的执行状态为 changed时,handlers任务才执行。
[root@control ansible]# vim ~/ansible/handlers.yml
---
- hosts: test
tasks:
- name: create directory.
file: //调用file模块创建目录
path: /tmp/parents/subdir/ //指定创建的具体目录名称
state: directory //state=directory代表创建目录
notify: touch file //notify后面名称必须和handlers中的任务name名称一致
handlers: //通过handlers再定义一组任务
- name: touch file //任务描述,内容需要与notify对应
file:
path: /tmp/parents/subdir/new.txt
state: touch
[root@control ansible]# ansible-playbook handlers.yml
注意:handlers与tasks需要在同一层级,notify与file任务为同一个层级;
注意:notity通知后面的名称,必须和handlers下面name描述定义的任务名称一致(名称可以任意)
补充:第一次执行playbook成功后任务状态是changed,下次执行playbook时,根据幂等性,任务状态将不再是changed;由于notify实现条件必须是仅当file模块执行成功并且状态为changed时才会通过notify触发执行handlers下面的任务,所以多次执行该剧本时,handlers任务不会被重复执行。
常见报错:当handlers下面定义的任务名称与notify通知的名称不一致,则报错
3.3 when条件判断
- when可以定义判断条件,条件为“真”时,才执行某个任务(类似shell中的while)
- 常见条件操作符:==、!=、>、>=、
- 多个条件可以使用and(并且)或or(或者)分割;
- 注意:when表达式中调用变量不要使用{{ }}
例如1:远程node1主机,当系统剩余内存不足700M则关闭httpd.service服务
[root@node1 ~]# free -h //查看node1当前内存剩余空间(570M<700M)
total used free shared buff/cache available
Mem: 979Mi 192Mi 570Mi 6.0Mi 216Mi 641Mi
Swap: 0B 0B 0B
[root@control ansible]# vim ~/ansible/when_1.yml
---
- hosts: test
tasks:
- name: check memory size.
service:
name: httpd.service
state: stopped
when: ansible_memfree_mb < 700 //注意:调用变量不要使用{{}}
[root@control ansible]# ansible-playbook when_1.yml
注意:when与service任务需要是同一个层级
解释说明:
# ansible_memfree_mb 是 ansible自带的 facts变量,代表剩余内存的容量。
补充:设置内存ansible_memfree_mb 当when条件不满足时,则跳过任务(skipping)
例如2:远程node1主机,判断操作系统是RedHat8则创建测试文件(使用and连接多个条件)
[root@control ansible]# ansible test -m setup | less //查看facts变量
[root@control ansible]# vim ~/ansible/when_2.yml
---
- hosts: test
tasks:
- name: touch a file
file:
path: /tmp/when.txt
state: touch
when: > //YAML的语法格式中>支持多行输入
ansible_distribution == "RedHat" //判断是否是红帽操作系统RedHat
and
ansible_distribution_major_version == "8" //判断是否是8版本
[root@control ansible]# ansible-playbook when_2.yml
[root@node1 ~]# ls /tmp/when.txt //验证
/tmp/when.txt
解释说明:
# YAML的语法格式中>支持多行输入,但不保留换行符(计算机会认为实际是一行内容)
# ansible_distribution和ansible_distribution_major_version都是自带的facts变量
# 当when的两个条件(“and且”)都成立时,则执行对应的file任务;
3.4 block任务块
当用户满足条件并需要执行N个任务时,可给N个任务后面都加when判断(但很麻烦),此时可使用block定义一个任务块,当条件满足时执行整个任务块(任务组);
- block任务块,将把多个任务合并为一个任务组(对于block任务块,可使用rescue语句定义在block任务执行失败时要执行的其他任务,还可使用always语句定义无论block任务是否成功,都要执行的任务)
- rescue关键字,字面意思为"救援",表示当block中的任务执行失败时,会执行rescue中的任务进行补救;
- always关键字,无论block中的任务执行成功还是失败,always中的任务都会被执行;
例如1:判断远程的目标主机的Linux发行版本是否是RedHat,当满足条件再执行任务组中的任务
[root@control ansible]# vim ~/ansible/block_1.yml
---
- hosts: test
tasks:
- name: define a group of tasks.
block: //block是关键词,定义任务组
- name: install httpd. //任务组中的第一个任务
yum: //调用yum模块安装httpd软件包
name: httpd
state: present
- name: start httpd. //任务组中的第二个任务
service: //调用service模块启动httpd服务
name: httpd
state: started
when: ansible_distribution == "RedHat"
[root@control ansible]# ansible-playbook block_1.yml
注意:when和block是在同一个层级,当条件满足时执行的是block任务组(不是某一个任务)
补充:当when条件满足时,假设执行的其中一个任务失败,也会执行另一个任务
例如2:包含block任务块、rescue、always执行任务的判断条件示例
- 情况1:当block默认任务成功时,就会在/tmp/目录下创建test1.txt,所以不执行rescue;
- 情况2:当block默认任务失败时,就执行rescue任务,创建test2.txt;
- 情况3:不管block任务是否成功都会执行always任务,创建test3.txt;
[root@control ansible]# vim ~/ansible/block_2.yml
---
- hosts: test
tasks:
- block:
- name: touch a file test1.txt
file:
name: /tmp/test1.txt
state: touch
rescue:
- name: touch a file test2.txt
file:
name: /tmp/test2.txt
state: touch
always:
- name: touch a file test3.txt
file:
name: /tmp/test3.txt
state: touch
[root@control ansible]# ansible-playbook block_2.yml
[root@node1 ~]# ls /tmp/test?.txt //验证
/tmp/test1.txt /tmp/test3.txt
结果:block的任务执行成功,rescue的任务不执行,always不管block是否成功都执行
注意:block、rescue、always在同一个层级
例如3:当block中有两个任务,其中一个任务失败,一个任务成功,block依旧会以任务失败报错,执行rescue任务;若在失败任务加上ignore_errors,rescue不会执行;
[root@control ansible]# vim block_2.yml
---
- hosts: test
tasks:
- block:
- name: touch a file failed test.txt
file:
name: /tmp/1/test.txt //假设修改为/tmp/1/test.txt测试无法创建成功
state: touch
- name: touch a file test1.txt
file:
name: /tmp/test1.txt
state: touch
rescue:
- name: touch a file test2.txt
file:
name: /tmp/test2.txt
state: touch
always:
- name: touch a file test3.txt
file:
name: /tmp/test3.txt
state: touch
结果:block的其中一个任务失败,则不执行,rescue的任务执行,always的任务执行;
[root@control ansible]# vim block_2.yml
---
- hosts: test
tasks:
- block:
- name: touch a file failed test.txt
file:
name: /tmp/1/test.txt
state: touch
ignore_errors: yes //忽略错误任务
- name: touch a file test1.txt
file:
name: /tmp/test1.txt
state: touch
rescue:
- name: touch a file test2.txt
file:
name: /tmp/test2.txt
state: touch
always:
- name: touch a file test3.txt
file:
name: /tmp/test3.txt
state: touch
结果:block的其中一个任务失败并被忽略,不影响另一个任务执行,rescue的任务不执行,always的任务执行;
3.5 loop循环
相同模块需要反复被执行,使用loop循环可以避免重复添加模块(类似shell中的for)
- 【-】表示一个数组,loop循环定义的值为数组;
- item是关键字,调用loop循环的值;
- loop是关键词,定义循环的值
例如1:编写Playbook,循环创建目录
[root@control ansible]# vim ~/ansible/simple_loop.yml
---
- hosts: test
tasks:
- name: mkdir multi directory.
file:
path: /tmp/{{item}} //item是关键字,调用loop循环的值
state: directory
loop: // loop是关键词,定义循环的值,下面是具体的值(值有多少则循环几次)
- School
- Legend
- Life
[root@control ansible]# ansible-playbook simple_loop.yml
注意:loop循环需要与哪一个模块在同一层级,就能对模块实现循环;
[root@node1 ~]# ls /tmp/ //验证,/tmp目录下创建3个子目录,file模块被循环执行3次
Legend
Life
School
解释说明:
# item是关键字,调用loop循环的值;
# loop是关键词,定义循环的值
例如2:编写Playbook,循环创建用户并设置密码
[root@control ansible]# vim ~/ansible/complex_loop.yml
---
- hosts: test
tasks:
- name: create multi user.
user:
name: "{{item.iname}}"
password: "{{item.ipass | password_hash('sha512')}}"
loop:
- { iname: 'tom' , ipass: '123456' } //定义一个数组,执行一次循环
- { iname: 'jerry' , ipass: '123321' }
[root@control ansible]# ansible-playbook complex_loop.yml
[root@node1 ~]# id tom //验证
uid=1004(tom) gid=1004(tom) groups=1004(tom)
[root@node1 ~]# id jerry
uid=1005(jerry) gid=1005(jerry) groups=1005(jerry)
[root@node1 ~]# tail -2 /etc/shadow //验证
tom:$6$BpLPPtmYV5sZWb6/$bLSmhlZEyqqteLiCv85UeGV4597lmPBAwUzD.FrelOLN7aS.zyTcIjK5AZ1HxPqn0o7SvAl80.B2yz.iwyw2Z.:18743:0:99999:7:::
jerry:$6$et8wbhTs.UkzfcWV$R/HI453zOXwpK.4XGVE.q7Ubiqv57HyfchugLnv7tqhMzJGsQth1hUfYcVAA0b ZOd.K18q6rtgcwLZu5XUJM3.:18743:0:99999:7:::
解释说明:
loop循环第一次调用user模块创建用户,创建用户时会读取loop里面的第一个值。
- loop第一个值(数组)里面有两个子值,iname和ipass
- 创建用户item.iname就是loop第一个值里面的iname=tom
- 修改密码item.ipass就是loop第一个值里面的ipass=123456
loop循环第二次调用user模块创建用户,创建用户时会读取loop里面的第二个值。
- loop第二个值(数组)里面有两个子值,iname和ipass
- 创建用户item.iname就是loop第二个值里面的iname=jerry
- 修改密码item.ipass就是loop第二个值里面的ipass=123321
二、Ansible Roles
安装包:rhel-system-roles //提供了默认的roles,通过README文件还可查看Playbook案例
案例要求:
- 1)自定义Ansible Role
- 2)编写playbook调用role
- 3)使用ansible-galaxy管理Roles
Ansible Roles应用场景:在实际生产环境中,为了实现不同的功能,用户会编写大量的playbook文件,而且每个playbook还可能会调用其他文件(如变量文件),对于海量的、无规律的文件管理起来非常困难。Ansible从1.2版本开始支持Role(角色)
Role(角色)是管理ansible文件的一种规范(目录结构),Role(角色)会按照标准的规范,自动到特定的目录和文件中读取数据。
创建了一个名称为user.example的Role(角色),则其标准的目录结构如下图所示:
Roles规范的目录结构及主要文件/目录的作用:
- - defualts/main.yml:定义变量的缺省值,优先级较低
- - files目录:存储静态文件的目录,如tar包、音乐、视频等
- - handlers/main.yml:定义handlers
- - meta/main.yml:写作者、版本等描述信息
- - README.md:整个角色(role)的描述信息
- - tasks/main.yml:定义任务的地方
- - templates目录:存放动态数据文件的地方(文件中包含了变量的模板文件)
- - vars/main.yml:定义变量,优先级高
Role应用案例1:
1)创建Roles
案例目的:编写一个包含变量的模板文件,编写任务调用template模块,将模板文件拷贝给被管理端主机的/etc/issue文件。
命令:ansible-galaxy //可以创建、管理自己的roles;
选项:init 初始化
[root@control ansible]# mkdir ~/ansible/roles //创建一个存放“角色”的目录
[root@control ansible]# ansible-galaxy init ~/ansible/roles/issue //创建一个名称为issue的Role(角色)即目录
- /root/ansible/roles/issue was created successfully
[root@control ansible]# ls ~/ansible/roles/issue/
README.md defaults files handlers meta tasks templates tests vars
[root@control ansible]# yum -y install tree //tree命令实现,需安装tree软件包
[root@control ansible]# tree ~/ansible/roles/issue //查看issue下的目录结构
/root/ansible/roles/issue
|-- README.md
|-- defaults
| `-- main.yml
|-- files
|-- handlers
| `-- main.yml
|-- meta
| `-- main.yml
|-- tasks
| `-- main.yml
|-- templates
|-- tests
| |-- inventory
| `-- test.yml
`-- vars
`-- main.yml
2)修改Role文件
定义名称为myfile.txt的模板文件(该文件包含变量,因此必须放置templates目录)
[root@control ansible]# vim ~/ansible/roles/issue/templates/myfile.txt
This is the system {{ansible_hostname}}
Today's date is:{{ansible_date_time.date}}
Contact to {{admin}} //自定义的变量
自定义变量文件(前面调用了admin这个变量,需要在vars/main.yml定义admin变量并赋值)
[root@control ansible]# vim ~/ansible/roles/issue/vars/main.yml
---
# vars file for /root/ansible/roles/issue
admin: anj@tedu.cn //变量名为admin,变量的值为anj@tedu.cn
补充:当文件准备好以后,计算机不会自动将文件拷贝给被管理端主机,需要编写任务调用模块实现拷贝的功能。
修改任务文件(任务文件中不需要tasks关键词,Role的各文件之间相互调用不需要写文件的路径)
[root@control ansible]# vim ~/ansible/roles/issue/tasks/main.yml
---
# tasks file for /root/ansible/roles/issue
- name: delever issue file.
template:
src: myfile.txt
dest: /etc/issue
解释:编写任务,调用template模块将myfile.txt文件拷贝给被管理端主机。
3)在Playbook中调用Role
Role创建好,role不会自己运行,需要编写一个剧本调用上面的role。
- 编写playbook剧本文件,通过roles关键词调用role。
[root@control ansible]# vim ~/ansible/issue.yml
---
- hosts: test
roles:
- issue //调用role(补充:- role2支持加载多个role)
[root@control ansible]# ansible-playbook issue.yml
验证:
[root@node1 ~]# cat /etc/issue
This is the system node1
Today's date is:2021-04-26
Contact to anj@tedu.cn
4)修改ansible.cfg配置文件,定义roles目录(了解)
结合mkdir ~/ansible/roles/,因配置文件已指定./roles目录,所以调用role时,会在该目录下找创建的role(默认就在./roles目录)
[root@control ansible]# vim ~/ansible/ansible.cfg
[defaults]
inventory = ./inventory
roles_path = ./roles //指定到哪个目录下找role
remote_user = alice
[privilege_escalation]
become=True
become_method=sudo
become_user=root
become_ask_pass=False
galaxy命令行下载(了解内容)
公共Roles仓库管理:https://galaxy.ansible.com
1)使用ansible-galaxy install 可以直接下载Role;
[root@control ansible]# ansible-galaxy search 'httpd' //联网搜索roles
[root@control ansible]# ansible-galaxy info acandid.httpd //查看roles基本信息
[root@control ansible]# ansible-galaxy install acandid.httpd -p ~/ansible/roles/ //下载roles到特定的目录
补充:[-p]选项,可以指定下载到哪个目录;install可不指定网址,默认为官网;“acandid.httpd”为acandid用户的httpd角色
2)编写requirements.yml文件下载Role
[root@control ansible]# vim ~/ansible/roles/requirements.yml
## 格式一:可以直接从Ansible Galaxy官网下载
- src: acandid.httpd
## 格式二:可以从某个git服务器下载
- src: http://gitlab.com/xxx/xxx.git
scm: git
version: 56e00a54
name: nginx-acme
## 格式三:可以指定位置下载tar包,支持http、https、file
- src: http://example.com/myrole.tar
name: myrole
[root@control ansible]# ansible-galaxy install -r ~/ansible/roles/requirements.yml -p roles
# -r后面跟文件名,该文件中包含了需要下载哪些role以及他们的链接位置;
# -p 指定将下载的role保存到哪个目录;
思维导图:
小结:
本篇章节为【第二阶段】AUTOMATION-DAY6 的学习笔记,这篇笔记可以初步了解到 YAML语法格式,层级关系、Ansible Playbook文件及语法格式、Ansible变量。
Tip:毕竟两个人的智慧大于一个人的智慧,如果你不理解本章节的内容或需要相关笔记、视频,可私信小安,请不要害羞和回避,可以向他人请教,花点时间直到你真正的理解