主要内容:
YAML语法格式,层级关系、Ansible Playbook文件及语法格式、Ansible变量(定义变量方法、优先级顺序、setup和debug查看变量)
补充:Ansible ad-hoc 可以通过命令行形式远程管理其他主机,适合执行一些临时性简单任务。另外还有一种远程管理的方式叫 Playbook,Ansible Playbook中文名称叫剧本,它将经常需要执行的任务写入一个文件,这个文件就叫剧本;
一、Playbook概述
1、Ansible playbook
中文名称为剧本,将经常需要执行的任务写入一个文件(剧本);剧本中可以包含多个任务;剧本写后可随时根据剧本执行相关的任务命令;playbook剧本要求按照YAML格式编写;适合执行周期性经常执行的复杂任务;
Ansible 提供了两种主要的方式来执行自动化任务:ad-hoc 命令和 Playbooks。这两种方式各有特点,适用于不同的场景。
1)Ansible ad-hoc 命令
Ad-hoc 命令是一种快速执行单个任务的方式,适合于临时性的操作。Ad-hoc 命令通常用于一次性任务,不需要编写复杂的脚本或 Playbooks。
ansible <host-pattern> -m <module> -a <arguments>
<host-pattern>
:指定目标主机或主机组。-m <module>
:指定要使用的模块。-a <arguments>
:传递给模块的参数。
示例:
# 检查所有主机的 uptime
ansible all -m command -a "uptime"# 在 webservers 组中的所有主机上安装 Apache
ansible webservers -m yum -a "name=httpd state=present" --become# 重启 webservers 组中的所有主机的 Apache 服务
ansible webservers -m service -a "name=httpd state=restarted" --become
2)Ansible Playbooks
Playbooks 是 Ansible 的配置、部署和编排语言。它们使用 YAML 格式编写,定义了一系列任务,这些任务可以按顺序执行,以实现自动化目标。Playbooks 适合于复杂的、可重复的自动化任务。
- name: Playbook 名称hosts: 目标主机或主机组become: yes/no # 是否提升权限tasks:- name: 任务名称module: 模块参数
示例:
# playbook.yml
- name: Install and configure Apachehosts: webserversbecome: yestasks:- name: Ensure Apache is installedyum:name: httpdstate: present- name: Ensure Apache is running and enabledservice:name: httpdstate: startedenabled: yes- name: Configure Apachetemplate:src: templates/httpd.conf.j2dest: /etc/httpd/conf/httpd.conf
运行 Playbook
ansible-playbook -i inventory.ini playbook.yml
ad-hoc 命令和 Playbooks 比较:
特性 | Ad-hoc 命令 | Playbooks |
---|---|---|
适用场景 | 临时性、一次性任务 | 复杂、可重复的自动化任务 |
语法 | 简单,命令行格式 | 复杂,YAML 格式 |
可读性 | 较差,适合简单操作 | 较好,适合复杂操作 |
可重用性 | 低,不适合重复使用 | 高,适合编写可重用的脚本 |
扩展性 | 低,不适合复杂逻辑 | 高,支持变量、条件、循环等 |
适用性 | 适合快速检查或执行简单任务 | 适合配置管理、应用部署等复杂任务 |
2、YAML简介(YAML Ain’t a Markup Language)
YAML是一个可读性高,用来表达数据序列的格式语言,YAML以数据为中心,重点描述数据的关系和结构;
YAML的格式要求如下:
① "#" 代表注释;一般第一行为三个横杠【---】;
② 键值对(key/value)使用【:】表示,数组使用【-】表示;
注意:key和value之间使用":"分隔,且":" 后面必须有空格
注意:数组使用”-“表示,且"-" 后面必须有空格
③ 一般缩进由两个或以上空格组成,相同层级的缩进必须对齐,缩进代表层级关系
注意:全文不可以使用tab键,YAML不识别
④ 区分大小写
⑤ 扩展名为 yml 或者 yaml
⑥ 跨行数据需要使用【>】或者【|】,其中|会保留换行符
YAML格式,示例1:
- YAML格式的键值对数据;
- key和value之间使用“:”分隔;(“:”后面必须有空格)
- 缩进代表层级关系
YAML格式,示例2:
- YAML格式的数组数据
- 使用短横杠和空格表示,一行表示数据格式[值,值,值...]
YAML格式,示例3:
YAML格式,示例4:(注意空格)
YAML格式,示例5:(“|”会保留换行符)
YAML格式,示例6:(注意“-”和“:”后面必须有空格)
补充:一个【-】为数组,可以存放多个值,需注意层级关系
二、Playbook剧本
1)Playbook 语法格式
- playbook采用YAML格式编写;
- playbook文件中由一个或多个play组成(使用【-】区分);
- 每个play中关键词包含:hosts(主机)、tasks(任务)、vars(变量)等元素组成;(必须要有hosts和tasks)
- 使用ansible-playbook命令运行playbook剧本;
- 编写剧本时,必须要在~/ansible目录,否则无法调用配置文件;
测试Playbook语法格式
1)编写第一个Playbook(剧本)
- hosts、tasks、name是关键词(不可修改),ping是模块,调用不同模块完成不同任务。
[root@control ansible]# vim ~/ansible/test.yml
---
- hosts: all //hosts定义要远程的主机(可一个或多个)tasks: //tasks定义远程后要执行的任务有哪些- name: This is my first playbook //任务描述,后面的具体内容可以任意ping: //第一个任务调用ping模块,该模块没有参数
[root@control ansible]# ansible-playbook ~/ansible/test.yml
补充:name任务描述可忽略,name后面的具体内容可以任意;(用来自定义描述)
2)定义多个主机和任务的剧本
- hosts由一个或多个组或主机组成,主机之间用逗号【,】分隔;
- tasks由一个或多个任务组成,多个任务按顺序执行
- 执行ansible-playbook命令可以使用[ -f ]选项自定义并发量。
[root@control ansible]# vim ~/ansible/test.yml
---
- hosts: test,webserver //hosts定义需要远程哪些被管理主机tasks: //tasks定义需要执行哪些任务- name: This is my first playbookping:- name: Run a shell commandshell: touch ~/shell.txt //第二个任务调用shell模块在被管理主机创建一个空文件~/shell.txt
[root@control ansible]# ansible-playbook ~/ansible/test.yml
[root@control ansible]# ansible test -a "ls ~/shell.txt"
node1 | CHANGED | rc=0 >>
/root/shell.txt
3)多个 play的Playbook文件
[root@control ansible]# vim ~/ansible/test.yml
---
- hosts: test //第一个play剧目tasks:- name: This is my first playbookping:
- hosts: webserver //第二个play剧目tasks:- name: This is my second playbookping:
[root@control ansible]# ansible-playbook ~/ansible/test.yml
常见报错:”- name”中,”-”后面没有空格
1、Playbook 应用案例
案例1:用户管理,创建系统账户、账户属性、设置密码(ansible-doc user)
1)创建用户并设置UID、基本组、Password
[root@control ansible]# vim ~/ansible/test_tom.yml
---
- hosts: webservertasks:- name: Add the user "tom" //任务描述信息user:name: tom //name参数uid: 1040group: daemonpassword: "{{ '123' | password_hash('sha512') }}"
[root@control ansible]# ansible-playbook ~/ansible/test_tom.yml
[root@control ansible]# ansible webserver -a "id tom" //验证
node4 | CHANGED | rc=0 >>
uid=1040(tom) gid=2(daemon) groups=2(daemon)
node3 | CHANGED | rc=0 >>
uid=1040(tom) gid=2(daemon) groups=2(daemon)
解释说明:
# hosts定义需要远程的对象是webserver组,hosts是关键词
# tasks定义需要执行的任务,tasks是关键词
# name是第一个任务的描述信息,描述信息可以任意
# user是第一个任务需要调用的模块,user下面的缩进内容是给user模块的参数
# name是需要创建的用户名,uid是用户ID号,group是用户属于哪个基本组
# password是用户的密码,密码是123,密码经过sha512算法加密
补充:tasks的name为任务的描述信息,方便观察(可忽略),user模块下的name为参数,用来指定需要创建的用户名;
2)创建用户并设置登录解释器、附加组、Password
[root@control ansible]# vim ~/ansible/test_jerry.yml
---
- hosts: webservertasks:- name: Add "jerry" with a bash shelluser:name: jerryshell: /bin/bashgroups: bin,admpassword: "{{ '123' | password_hash('sha512') }}"
[root@control ansible]# ansible-playbook ~/ansible/test_jerry.yml
[root@control ansible]# ansible webserver -a "id jerry" //验证
node4 | CHANGED | rc=0 >>
uid=1041(jerry) gid=1041(jerry) groups=1041(jerry),1(bin),4(adm)
node3 | CHANGED | rc=0 >>
uid=1041(jerry) gid=1041(jerry) groups=1041(jerry),1(bin),4(adm)
解释说明:
# shell指定用户属于哪个解释器
# groups指定用户属于哪些附加组
注意:passowrd “{{}}”,花括号为固定格式,外面必须要有双引号;
3)删除用户及家目录、邮箱
[root@control ansible]# vim ~/ansible/test_jerry.yml
---
- hosts: webservertasks:- name: Remove the user "jerry"user:name: jerrystate: absentremove: yes //删除用户家目录,邮件
[root@control ansible]# ansible-playbook test_jerry.yml
[root@node3 home]# id jerry //验证,查看用户是否被删除
id: 'jerry': no such user
[root@node3 home]# ls /var/mail/ //验证。查看用户邮箱是否被删除;
tom
解释:# state的值设置为absent是删除用户
案例2:使用playbook管理逻辑卷(ansible-doc parted,ansible-doc lvg,ansible-doc lvol)
- 准备工作:给node2主机再添加一块磁盘并安装软件包lvm2
[root@node2 ~]# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sr0 11:0 1 1024M 0 rom
vda 253:0 0 30G 0 disk
`-vda1 253:1 0 20G 0 part /
vdb 253:16 0 20G 0 disk
[root@node2 ~]# yum -y install lvm2 //安装软件包才可使用vgcreate、lvcreate[root@control ansible]# vim ~/ansible/lvm.yml
---
- hosts: node2tasks:- name: Create a new Primary partition with a size of 1GiBparted: //调用parted模块进行分区device: /dev/vdb //对/dev/vdb磁盘进行分区label: gpt //分区表类型为gpt,或msdosnumber: 1 //分区编号(创建第几个分区)state: present //present是创建分区,absent是删除分区part_start: 1MiB //分区的开始位置(默认从最开始位置分区)part_end: 1GiB //分区的结束位置(不写就分到磁盘最后位置)- name: Create a volume group "my_vg"lvg: //调用lvg模块,创建VG卷组vg: my_vg //创建的卷组名称pvs: /dev/vdb1 //使用哪个分区创建PV- name: Create a logical volume of 512mlvol: //调用lvol模块创建LVlv: my_lv //需要创建的LV名称vg: my_vg //使用哪个VG创建LVsize: 512M //需要创建的LV大小,可以不指定单位,默认单位m
[root@control ansible]# ansible-playbook lvm.yml
[root@node2 ~]# lsblk //验证
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sr0 11:0 1 1024M 0 rom
vda 253:0 0 30G 0 disk
`-vda1 253:1 0 20G 0 part /
vdb 253:16 0 20G 0 disk
`-vdb1 253:17 0 1023M 0 part
[root@node2 ~]# vgs //验证VG #PV #LV #SN Attr VSize VFree my_vg 1 1 0 wz--n- 1020.00m 508.00m
[root@node2 ~]# lvs //验证LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convertmy_lv my_vg -wi-a----- 512.00m
注意:使用parted、lvg、lvol有幂等性,所以创建分区后下次不会再创建;
注意:使用parted分区,按照ansible-doc parted帮助,需要加state: present;
补充:part_start可忽略,默认从最开始位置分区;part_end不写默认分配磁盘最后位置
补充:format模块,用来进行格式化
常见报错:在分区进行的开始和结束位置时,单位(GiB、MiB)必须补全,否则报错;
案例3:使用playbook管理软件(ansible-doc yum)
补充:RHEL或CentOS系统中的软件有组包的概念,使用yum grouplist或者dnf grouplist可以查看组包的名称。(RHEL8中还有module模块概念,与group组包一样)
补充:有关yum针对组包(group)的命令
- yum groupinfo //查看组包信息(类似yum info)
- yum groupinstall //下载组包(类似yum install)
- yum grouplist //列出组包(类似yum grouplist)
- yum groupremove //删除组包(类似yum remove)
- yum groupupdate //升级组包(类似yum update)
例如:
[root@control ansible]# yum grouplist //列出软件包组
Available Environment Groups: //可用的环境组包Server with GUIServerWorkstationVirtualization Host
...
例如:
[root@control ansible]# vim ~/ansible/package.yml
---
- hosts: webservertasks:- name: Install a list of packages //任务1描述,下载一些软件包yum: //调用yum模块安装软件name: //安装软件的名字,它的值有多个,使用数组-- httpd- mariadb- mariadb-server- name: Intall the 'RPM Development Tools' packages group //任务2描述,下载组包yum: //调用yum模块安装软件组包name: "@RPM Development Tools" //安装哪个组包,@是关键词,代表组包- name: update software //任务3描述,升级软件yum: //调用yum模块升级软件name: '*' //需要升级哪些软件,【*】代表所有state: latest //latest代表升级软件
[root@control ansible]# ansible-playbook package.yml[root@node3 ~]# rpm -qa httpd mariadb mariadb-server //验证软件包
mariadb-server-10.3.17-1.module+el8.1.0+3974+90eded84.x86_64
mariadb-10.3.17-1.module+el8.1.0+3974+90eded84.x86_64
httpd-2.4.37-21.module+el8.2.0+5008+cca404a3.x86_64
[root@node3 ~]# yum grouplist | grep 'RPM Development Tools' //验证组包RPM Development Tools
或者
[root@node3 ~]# yum grouplist
Installed Groups: //已安装的组包RPM Development Tools...
解释:state的值可以是(present安装 | absent卸载 | latest升级)
遇到问题:验证组包时,出现“Failed to set locale, defaulting to C.UTF-8”报错,表示没有设置正确的语言环境;
解决办法:#
echo "export LC_ALL=en_US.UTF-8" >> /etc/profile
source /etc/profile
2、Ansible变量应用案例
案例要求:熟悉setup与debug模块、熟悉各种常见的变量定义方式
2.1 Ansible特殊模块
1)setup模块
- setup模块可显示ansible_facts变量;
- ansible_facts用于采集被管理设备的系统信息,所有收集的信息都被保存在变量中,每次执行playbook默认第一个任务就是Gathering Facts,使用setup模块可以查看收集到的facts信息;
例如:假设只编写1个任务,默认还有Gathering Facts任务,收集被控制端系统信息
[root@control ansible]# vim ping.yml
---
- hosts: testtasks:- name: ping testping:
结果:显示完成任务OK为2个,其中一个为Facts任务,另一个为Play任务;
例如:使用setup模块查看收集到的facts信息(类似shell中的env查看环境变量)
[root@control ansible]# ansible test -m setup //收集到的fasts信息即变量
node1 | SUCCESS => {"ansible_facts": {"ansible_all_ipv4_addresses": ["192.168.4.11"],
...
实验:找出以下facts变量(可使用ansible test -m setup | less //可翻页和【/】搜索查看)
- ansible_all_ipv4_addresses #IP地址
- ansible_bios_version #主板BIOS版本
- ansible_memtotal_mb #总内存
- ansible_hostname #主机名
- ansible_fqdn #主机的域名
- ansible_devices.vda.partitions.vda1.size #某磁盘分区大小(有父子关系使用【.】分隔)
2)debug模块
- debug模块可显示变量的值,可辅助排错
- 通过msg参数可以显示变量的值,变量必须要使用【{{}}】扩起来(类似shell中引用变量的$)
- 格式:msg: “{{需要显示的变量}}”
例如:通过debug模块+ msg参数显示变量的值
[root@control ansible]# vim ~/ansible/debug.yml
---
- hosts: testtasks:- debug:msg: "Hostname is :{{ ansible_hostname }}" //显示被控制端主机名- debug:msg: "Mem is :{{ ansible_memtotal-mb }}" //显示被控制端内存
[root@control ansible]# ansible-playbook debug.yml
2.2 定义变量的方法
- Ansible支持十几种定义变量的方式,这里我们仅介绍其中一部分变量。
下面是根据优先级排序的定义方式:(从小到大排序)
① Inventory变量 ↓
② Host Facts变量 ↓
③ Playbook变量 ↓
④ 变量文件
定义变量优先级顺序:变量文件> Playbook变量> Host Facts变量> Inventory变量
① Inventory变量(在主机清单配置文件中定义变量)
[root@control ansible]# vim ~/ansible/inventory
[test]
node1 iname="nb" //在node1主机后面给该主机添加变量iname,值为nb
[proxy]
node2
[webserver]
node[3:4]
[database]
node5
[cluster:children]
webserver
database
[webserver:vars] //给webserver组定义变量,vars是关键词不可以改变,webserver是上面定义的组
iname="dachui" //给这个组定义变量iname="dachui"
补充:定义的变量iname虽然变量名相同,但在不同主机/主机组所以不会覆盖;
下面编写剧本调用刚才的变量:(在剧本中需要调用变量是要使用{{}})
[root@control ansible]# vim ~/ansible/inventory_var.yml
---
- hosts: test,webservertasks:- name: create a user with var.user: //调用user模块,创建用户name: "{{ iname }}" //创建的用户名是iname这个变量
[root@control ansible]# ansible-playbook inventory_var.yml[root@node1 ~]# id nb //验证
uid=1000(nb) gid=1000(nb) groups=1000(nb)
[root@node3 ~]# id dachui //验证
uid=1041(dachui) gid=1041(dachui) groups=1041(dachui)
注意事项:
- ① 在ansible剧本中当调用变量时,开始位置就调用变量,需要在{{}}外面加双引号【“”】
- ② 如果是在后面或者中间位置调用变量{{}},外面可以不加双引号
"{{ iname }}"
nihao {{ iname }}
② Host Facts变量(可以直接调用ansible收集的系统信息,“ansible_facts变量”)
[root@control ansible]# vim ~/ansible/facts_var.yml
---
- hosts: testtasks:- name: create user.user: //调用user模块,创建用户name: "{{ansible_hostname}}" //创建用户名为变量ansible_hostname的主机,“ansible_hostname”是一个ansible_facts变量。
[root@control ansible]# ansible-playbook facts_var.yml
[root@node1 ~]# id node1 //验证查看node1主机是否有与主机名同名的用户
uid=1001(node1) gid=1001(node1) groups=1001(node1)
③ Playbook变量(使用vars关键词可以在playbook内定义变量)
[root@control ansible]# vim ~/ansible/playbook_var.yml
---
- hosts: testvars: //vars是关键词,用来定义变量用的iname: heal //定义变量名iname,值是healipass: '123456' //定义变量名ipass,值是123456tasks:- name: Use variables create user. //任务描述信息,使用变量创建用户user: //调用user模块创建用户name: "{{iname}}" //创建的用户名是vars定义的变量inamepassword: "{{ipass | password_hash('sha512')}}" //创建的密码是vars定义的变量ipass,管道给password_hash把密码进行sha512加密
[root@control ansible]# ansible-playbook playbook_var.yml[root@node1 ~]# id heal //验证
uid=1002(heal) gid=1002(heal) groups=1002(heal)
注意:密码必须是字符串,需要引号;
注意:使用vars关键词,需要在tasks任务前面定义,否则执行任务无法调用定义的变量
定义变量优先级顺序:变量文件> Playbook变量> Host Facts变量> Inventory变量
④ 单独定义个变量文件,在playbook中用vars_files模块调用该文件
- 当变量比较多时,可以专门定义一个文件用来存变量,然后在通过playbook调用文件;
[root@control ansible]# vim ~/ansible/variables.yml //定义变量文件(文件名随意)
---
iname: cloud
ipass: '123456'[root@control ansible]# vim ~/ansible/file_var.yml
---
- hosts: testvars_files: variables.yml //调用变量文件tasks:- name: Use variables.yml create user.user:name: "{{iname}}" //调用user模块创建用户password: "{{ipass | password_hash('sha512')}}"
[root@control ansible]# ansible-playbook file_var.yml
[root@node1 ~]# id cloud //验证
uid=1003(cloud) gid=1003(cloud) groups=1003(cloud)
解释:用户名是变量文件variables.yml中定义的变量iname,密码是变量文件中定义的ipass变量;
思维导图:
小结:
本篇章节为【第二阶段】AUTOMATION-DAY5 的学习笔记,这篇笔记可以初步了解到 YAML语法格式,层级关系、Ansible Playbook文件及语法格式、Ansible变量。
Tip:毕竟两个人的智慧大于一个人的智慧,如果你不理解本章节的内容或需要相关笔记、视频,可私信小安,请不要害羞和回避,可以向他人请教,花点时间直到你真正的理解