自动化工具-Ansible
自动化工具-Ansible
1. Ansible能做什么
Ansible 是一个基于 Python 开发的配置管理和应用部署工具,现在也在自动化管理领域大放异彩。它融合了众多老牌运维工具的优点,Pubbet 和 Saltstack 能实现的功能,Ansible 基本上都可以实现。
Ansible 能批量配置、部署、管理一大堆的主机。比如以前需要切换到每个主机上执行的一或多个操作,使用 Ansible 只需在固定的一台 Ansible 控制节点上去完成所有主机的操作。
其实,固定在一台主机上去控制其它主机,通过 ssh 工具或一些基于 ssh 二次开发的简单工具也能实现,但 Ansible 吸引人的地方在于它提供的 playbook 能批量整合不同主机上执行的不同任务,同时还提供一些额外的机制让用户可以去协调这些任务的执行策略。
Ansible 其中一个比较鲜明的特性是 Agentless,即无 Agent 的存在,它就像普通命令一样,并非 C/S 软件,也只需在某个作为控制节点的主机上安装一次 Ansible 即可,通常它基于 ssh 连接来控制远程主机,远程主机上不需要安装 Ansible 或其它额外的服务。
1.1 Ansible特点
- 部署简单,只需在主控端部署Ansible环境,被控端无需做任何操作;
- 默认使用SSH协议对设备进行管理;
- 有大量常规运维操作模块,可实现日常绝大部分操作。
- 配置简单、功能强大、扩展性强;
- 支持API及自定义模块,可通过Python轻松扩展;
- 通过Playbooks来定制强大的配置、状态管理;
- 轻量级,无需在客户端安装agent,更新时,只需在操作机上进行一次更新即可;
- 提供一个功能强大、操作性强的Web管理界面和REST API接口——AWX平台。
1.2 Ansible架构
上图为Ansible的基本架构,从上图可以了解到其由以下部分组成:
- 核心:Ansible核心程序
- 核心模块(Core Modules):这些都是Ansible自带的模块
- 扩展模块(Custom Modules):如果核心模块不足以完成某种功能,可以添加扩展模块
- 插件(Plugins):完成模块功能的补充
- 剧本(Playbooks):Ansible的任务配置文件,将多个任务定义在剧本中,由Ansible自动执行(YAML格式文件)
- 连接插件(Connaction Plugins):Ansible基于连接插件连接到各个主机上,虽然Ansible是使用ssh连接到各个主机的,但是它还支持其他的连接方法,所以需要有连接插件
- 主机群(Host Inventory):定义Ansible管理的主机
1.3 Ansible重要组件说明
- 模块:Ansible由多种功能模块组成(具体看以上架构说明)
- playbook:Ansible剧本,使用yml语法调用不同功能模块完成特定的功能
- roles:Ansbiel角色,可以使用ansible-galaxy命令下载第3方的roles角色
- ansible-vault:文件加密工具
- ansilbe-console:基于console与用户进行交互
- ansible-doc:帮助文档,-l所有模块,-s简要帮助
1.4 Ansible任务执行模式
Ansible系统由控制主机对被管节点的操作方式可分为两类,即ad-hoc和playbook:
ad-hoc模式:使用单个模块,支持批量执行单条命令。 ad-hoc 命令是一种可以快速输入的命令,而且不需要保存起来的命令。就相当于bash中的一句话shell。
playbook模式:是Ansible主要管理方式,也是Ansible功能强大的关键所在。playbook通过多个task集合完成一类功能,如Web服务的安装部署、数据库服务器的批量备份等。可以简单地把playbook理解为通过组合多条ad-hoc操作作的配置文件。
1.5 Ansible命令执行过程
- 加载自己的配置文件 默认/etc/ansible/ansible.cfg
- 查找对应的主机配置文件,找到要执行的主机或者组
- 加载自己对应的模块文件,如command
- 通过ansible将模块或命令生成对应的临时py文件,并将该文件传输至远程服务器的
- 对应执行用户的家目录的.ansible/tmp/xxx/xxx.py文件
- 给文件+x执行
- 执行并返回结果
- 删除临时py文件,sleep 0退出
1.6 Ansible执行状态说明
绿色:表示正常
黄色:表示执行正常,状态变化
红色:表示错误,输出错误信息
紫色:表示警告,建议
1.7 Ansible程序结构
安装目录
-
配置文件目录:/etc/ansible/
-
执行文件目录:/usr/bin/
-
Lib库依赖目录:/usr/lib/pythonX.X/site-packages/ansible/
-
Help文档目录:/usr/share/doc/ansible-X.X.X/
-
Man文档目录:/usr/share/man/man1/
Ansible配置文件的查找顺序
Ansible 支持 4 种方式指定配置文件,它们的解析顺序从上到下:
-
ANSIBLE_CFG:环境变量指定的配置文件
-
ansible.cfg:当前目录下的 ansible.cfg
-
~/.ansible.cfg:家目录下的 ansible.cfg
-
/etc/ansible/ansible.cfg:默认的全局配置文件
1.8 Ansible配置文件
vim /etc/ansible/ansible.cfg
#设置ansible.cfg配置参数,ansible有许多参数,下面列出常用的参数:
#1. 这个参数表示资源清单inventory文件的位置,资源清单就是一些Ansible需要连接管理的主机列表。这个参数的配置实例如下:
inventory=/etc/ansible/hosts
#2. Ansible的操作动作,无论是本地或远程,都使用一小段代码来执行,这小段代码称为模块,这个library参数就是指向存放Ansible模块的目录。#Ansible支持多个目录方式,只要用冒号(:)隔开就可以,同时也会检查当前执行playbook位置下的./library目录。配置实例如下:
library=/usr/share/ansible
#3. 设置默认情况下Ansible最多能有多少个进程同时工作, 从Ansible 1.3开始,fork数量默认自动设置为主机数量或者潜在的主机数量,默认设置最多5个进程并行处理。具体需要设置多少个,可以根据控制主机的性能和被管节点的数量来确定,可能是 50或100。默认值5是非常保守的值,配置实例如下:
forks=5
#4. 这是设置默认执行命令的用户,也可以在playbook中重新设置这个参数。配置实例如下:
sudo_user = root
#5. 这是指定连接被管节点的管理端口,默认是22。除非设置了特殊的SSH端口,不然这个参数一般是不需要修改的(如果需要修改,则还需修改ssh的配置文件,更改端口)。配置实例如下:
remote_port=22
#6. 这是设置是否检查SSH主机的密钥。可以设置为True或False,关闭后第一次连接没有提示(就是第一次连接不在提示yes或者no)配置实例:
host_key_checking=False
#7. 这是设置SSH连接的超时间隔,单位是秒。配置实例如下:
timeout=60
#8. Ansible系统默认是不记录日志的,如果想把Ansible系统的输出记录到日志文件中,需要置log_path来指定一个存储Ansible日志的文件。配置实例如下:
log_path=/var/log/ansible.log
#另外需要注意,执行Ansible的用户需要有写入日志的权限,模块将会调用被管节点的syslog来记录
1.9 Ansible常用命令
/usr/bin/ansible Ansibe AD-Hoc 临时命令执行工具,常用于临时命令的执行
/usr/bin/ansible-doc Ansible 模块功能查看工具
/usr/bin/ansible-galaxy 下载/上传优秀代码或Roles模块的官网平台,基于网络的
/usr/bin/ansible-playbook Ansible 定制自动化的任务集编排工具
/usr/bin/ansible-pull Ansible远程执行命令的工具,拉取配置而非推送配置(使用较少,海量机器时使用,对运维的架构能力要求较高)
/usr/bin/ansible-vault Ansible 文件加密工具
/usr/bin/ansible-console Ansible基于Linux Consoble界面可与用户交互的命令执行工具
2. 使用Ansible前提
Ansible 的作用是批量控制其它远程主机,并指挥远程主机节点做一些操作、完成一些任务。所以在这个结构中,分为控制节点和被控制节点。Ansible 是 Agentless 的软件,只需在控制节点安装 Ansible,被控制节点一般不需额外安装任何程序,就像一个普通的命令一样,随装随用。
Ansible 的模块是用 Python 来执行的,且默认远程连接的方式是 ssh,所以控制端和被控制端都需要有 Python 环境,并且被控制端需要启动 sshd 服务,但通常这两个条件在安装 Linux 系统时就已经具备了。所以使用 Ansible 的安装过程只有一个:在控制端安装 Ansible。
2.1 环境准备
Ansible环境 | 主机名和IP |
---|---|
Ansible控制端 | ms01 10.0.0.61 |
被控制端 | web01 10.0.0.7 |
被控制端 | backup 10.0.0.41 |
被控制端 | db01 10.0.0.51 |
#在控制端安装Ansible
[root@ms01 ~]# yum install ansible -y
#Ansible 参数补全功能
#从 Ansible 2.9 版本开始,它支持命令的选项补全功能,它依赖于 python 的 argcomplete 插件。
[root@ms01 ~]# yum install python-argcomplete -y
# 要求bash版本大于等于4.2
[root@ms01 ~]# activate-global-python-argcomplete
#最后,退出当前 Shell 重新进入,exec $SHELL
exec $SHELL
2.2 配置免密登陆(密钥认证)
因为 Ansible 默认是基于 ssh 连接的,所以要控制其它节点首先需要建立好 ssh 连接,而建立 ssh 连接要么需要提供密码,要么需要配置好认证方式。所以需要提前测试好控制端与被控制端之间的互信。
为了避免配置主机互信过程中的交互式询问,这里使用 ssh-keyscan 工具添加主机认证信息以及 sshpass 工具(安装 Ansible 时会自动安装 sshpass)直接指定 ssh 连接密码。
2.2.1 编写脚本实现执行分发
前提要先有sshpass,具体脚本内容如下
#脚本内容
[root@ms01 ~]# cat /server/scripts/fenfa_pub.sh
#!/bin/bash
#desc:一键自动化创建和分发密钥
ip_list="10.0.0.41 10.0.0.51 10.0.0.7" #要分发的主机IP
ssh_root_pass="123456" #主机密码
echo '-----------------------------------'
echo '1.创建key'
echo '-----------------------------------'
ssh-keygen -f ~/.ssh/id_rsa -P '' #创建私钥和公钥
echo '-----------------------------------'
echo '2.分发pub key'
for ip in $ip_list #循环IP_list变量中的IP进行分发公钥 “-o StrictHostKeyChecking=no”为取消首次连接的host检查警告
do
sshpass -p$ssh_root_pass ssh-copy-id -i ~/.ssh/id_rsa.pub -o StrictHostKeyChecking=no root@$ip
done
echo '-----------------------------------'
echo '3.已完成分发!'
#脚本执行
[root@ms01 ~]# sh /server/scripts/fenfa_pub.sh
-----------------------------------
1.创建key
-----------------------------------
Generating public/private rsa key pair.
/root/.ssh/id_rsa already exists.
Overwrite (y/n)? y
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:HG/Kl3QomHr6+c5aA6+lzojNz3DoBq9lcv1rJ4b9538 root@ms01
The key's randomart image is:
+---[RSA 2048]----+
| |
| |
| . |
| + o . |
| + S = . |
| . + + = o |
| .oB +oB o |
| XoO.O=o. . E|
| oo*+@**=.o.... |
+----[SHA256]-----+
-----------------------------------
2.分发pub key
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/root/.ssh/id_rsa.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
Number of key(s) added: 1
Now try logging into the machine, with: "ssh -o 'StrictHostKeyChecking=no' 'root@10.0.0.41'"
and check to make sure that only the key(s) you wanted were added.
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/root/.ssh/id_rsa.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
Number of key(s) added: 1
Now try logging into the machine, with: "ssh -o 'StrictHostKeyChecking=no' 'root@10.0.0.51'"
and check to make sure that only the key(s) you wanted were added.
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/root/.ssh/id_rsa.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
Number of key(s) added: 1
Now try logging into the machine, with: "ssh -o 'StrictHostKeyChecking=no' 'root@10.0.0.7'"
and check to make sure that only the key(s) you wanted were added.
-----------------------------------
3.已完成分发!
2.2.2 编写脚本实现一键检查
[root@ms01 ~]# cat /server/scripts/check_ssh.sh
#!/bin/bash
#desc 批量检查脚本
ip_list="10.0.0.41 10.0.0.51 10.0.0.7"
echo '--------------------------------------------'
echo '执行检查'
echo '--------------------------------------------'
for ip in $ip_list
do
ssh root@$ip hostname
done
2.2.3 检查是否免密互通
#执行检查脚本,已ok
[root@ms01 ~]# sh /server/scripts/check_ssh.sh
backup
db01
web01
2.2.4 关闭检查SSH主机的密钥
#这是设置是否检查SSH主机的密钥。可以设置为True或False,关闭后第一次连接没有提示(就是第一次连接不在提示yes或者no)
#在/etc/ansible/ansible.cfg中71行去掉注释即可
[root@ms01 ~]# cat /etc/ansible/ansible.cfg | grep -n host_key_checking
71:host_key_checking = False
3. 初探Ansible
3.1 初步配置主机清单(inventory)
[root@ms01 ~]# cat /etc/ansible/hosts
[yinjay]
172.16.1.41
172.16.1.51
172.16.1.7
#也可以为下列分成不同的组,但目前实验暂时是以上形式一个组
[root@ms01 ~]# cat /etc/ansible/hosts
[backup]
172.16.1.41
[db]
172.16.1.51
[web]
172.16.1.7
3.2 与Ansible的第一次接触
对主机清单中yinjay组,进行ping操作。(-m 为指定模块 ping为模块名)
[root@ms01 ~]# ansible yinjay -m ping
查看某一台主机的hostname,直接使用IP
3.3 执行简单的命令(command模块)
对主机清单中yinjay组,执行简单的命令hostname,command模块不支持管道命令
[root@ms01 ~]# ansible yinjay -m command -a 'hostname'
3.4 指定用户名、密码、端口
通常来讲,可能会涉及到多服务器用户名密码等不相同的情况,那么就要指定好这些信息,可以通过配置主机清单来实现这样子的功能。(需要配置什么就在IP后面加上如下信息)
[root@ms01 ~]# cat /etc/ansible/hosts
[yinjay]
172.16.1.41 ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh_pass='123456'
172.16.1.51
172.16.1.7
3.5 实现子组管理
例如有些主机在主机清单里面都分配到不同的组,但它们需要执行相同的操作时,可以通过配置子组来实现这样子的功能。
[root@ms01 ~]# cat /etc/ansible/hosts
[backup]
172.16.1.41
[db]
172.16.1.51
[web]
172.16.1.7
#data:children表示data是创建的子组,组里面包含db,backup 2个组
[data:children]
db
backup
4. Ansible核心模块
ad-hoc执行模式,命令行调用ansible模块
模块分类 | |
---|---|
命令和脚本模块 | command模块,默认的模块执行简单命令,不支持特殊符号。 |
shell模块执行命令,支持特殊符号 | |
script模块分发脚本并执行 | |
文件 | file模块,创建目录,文件,软连接。 |
copy模块,远程分发文件,修改权限,所有者,备份。 | |
服务 | systemd模块,服务管理 |
service模块,服务管理(了解) | |
软件包 | yum_repository模块,可以管理yum源 |
yum模块,命令 | |
get_url模块,下载软件 | |
系统管理 | mount模块,挂载 |
cron模块,定时任务 | |
用户管理 | group模块,管理用户组 |
user模块,管理用户 | |
用于调试模块 | ping模块,检查 ansible与其他节点连通性 |
debug模块,用于检查/显示变量 | |
其他 | 压缩解压(unarchive) ,rsync模块(synchronize),数据库模块(mysql_db,mysql_user)... |
ansible管理docker k8s zabbix grafana ... |
4.1 命令与脚本模块
4.1.1 command模块
仅支持简单命令,不支持特殊符号、管道。这个模块是默认模块,ansible不加上模块,默认就使用这个模块。
[root@ms01 ~]# ansible all -m command -a '命令'
[root@ms01 ~]# ansible all -a 'hostname' #相当于省略 -m command
4.1.2 shell模块
与command模块类似,shell模块支持特殊符号,执行脚本....
[root@ms01 ~]# ansible all -m shell -a 'ip addr show eth0 | sed -n 3p'
4.1.3 script模块
传输脚本到被管理端并执行脚本,下面通过安装ipvsadm来看看
[root@ms01 ~]# cat /server/scripts/yum.sh
yum install -y ipvsadm
[root@ms01 ~]# ansible all -m script -a '/server/scripts/yum.sh'
4.2 文件与目录管理模块
管理文件或目录,软连接
file模块中的选项 | |
---|---|
path | 路径(目录,文件)必须要写 |
src(source源) | 源文件一般用于link(创建软连接模式)用于指定源文件 |
state | 状态(模式) state=directory 创建目录 state=file (默认) 更新文件,如果文件不存在也不创建。 state=link 创建软链接 state=touch 创建文件 state=absent 删除 |
mode | 修改权限 例子:mode=755 |
owner | 修改为指定所有者 |
group | 修改为指定用户组 |
4.2.1 创建/yinjay目录
[root@ms01 ~]# ansible all -m file -a 'path=/yinjay state=directory'
4.2.2 创建/yinjay/test.sh文件
[root@ms01 ~]# ansible all -m file -a 'path=/yinjay/test.sh state=touch'
4.2.3 创建软链接
创建软链接 /yinjay/test.sh到/root/test.sh.soft
[root@ms01 ~]# ansible all -m file -a 'src=/yinjay/test.sh path=/root/test.sh.soft state=link'
4.2.4 删除文件/目录/软链接
[root@ms01 ~]# ansible all -m file -a 'path=/yinjay/test.sh state=absent' #删除文件
[root@ms01 ~]# ansible all -m file -a 'path=/yinjay state=absent' #删除目录
[root@ms01 ~]# ansible all -m file -a 'path=/root/test.sh.soft state=absent' #删除软链接
4.2.5 创建文件并设置用户、组,权限
在/root下创建755.txt这个文件,并设置用户为yinjay,用户组为yinjay,权限为755
[root@ms01 ~]# ansible all -m file -a 'path=/root/755.txt owner=yinjay group=yinjay mode=755 state=touch'
4.3 远程传输模块
copy模块 | |
---|---|
src | source 源文件 |
dest | destination 目标 |
backup | backup=yes 则会在覆盖前进行备份 |
mode | 修改权限 例子:mode=755 |
owner | 修改为指定所有者 |
group | 修改为指定用户组 |
4.3.2 传输文件
将本地/etc/hosts传输到目标主机的/etc/hosts,直接覆盖
[root@ms01 ~]# ansible all -m copy -a 'src=/etc/hosts dest=/etc/hosts
4.3.2 传输文件并备份
将本地/etc/hosts传输到目标主机的/etc/hosts,如果内容不一样将进行备份再覆盖
[root@ms01 ~]# ansible all -m copy -a 'src=/etc/hosts dest=/etc/hosts backup=yes'
4.4 服务管理模块
Systemd模块 | |
---|---|
name | 用于指定服务名称 |
enabled | 控制服务的开机自启动 enabled=yes /enabled=no |
state | 表示服务开,关,重启等等 state=started 开启 state=stopped 关闭 state=reloaded 重读配置文件(服务支持) sshd,nfs state=restarted 重启(关闭再开启) |
daemon-reload | daemon-reload=yes 重新加载对应的服务的管理配置文件 |
4.4.1 关闭firewalld服务
关闭firewalld服务同时设置开机不启动
[root@ms01 ~]# ansible all -m systemd -a 'name=firewalld enabled=no state=stopped'
4.4.2 重启rsync服务
重启backup这台机器上面的rsync服务
[root@ms01 ~]# ansible backup -m systemd -a 'name=rsyncd enabled=yes state=restarted'
4.5 软件包管理模块
4.5.1 yum模块
yum模块 | |
---|---|
name | 指定软件包名字 |
state | installed 安装(present) removed 删除(absent) latest 安装或更新 |
4.5.1.1 安装lrzsz
需安装多个服务时,name=softname,softname,softname ...
[root@ms01 ~]# ansible all -m yum -a 'name=lrzsz state=installed'
4.5.1.2 卸载lrzsz
[root@ms01 ~]# ansible all -m yum -a 'name=lrzsz state=absent'
4.5.2 get_url模块
get_url模块 | |
---|---|
url | 指定要下载的地址 |
dest | 下载到哪个目录 |
下载tengine源码包到/server/tools/(不存在)目录下
#先创建目录
[root@ms01 ~]# ansible web -m file -a 'path=/server/tools/ state=directory'
#再进行下载
[root@ms01 ~]# ansible web -m get_url -a 'url=https://tengine.taobao.org/download/tengine-2.3.3.tar.gz dest=/server/tools/'
4.6 系统管理模块
4.6.1 mount模块
mount模块 | |
---|---|
fstype | 指定文件系统(nfs) |
src | 源地址(nfs服务端地址 172.16.1.41/data ) |
path | 挂载点(要把源挂载到哪里) |
state | absent 卸载(umount)并修改fstab(清理配置) unmounted 卸载不修改/etc/fstab present 仅修改/etc/fstab 不挂载 mounted 挂载(用mount命令)并修改/etc/fstab(永久挂载) remounted 重新挂载 |
挂载案例
在backup主机上部署了nfs服务,现在通过Ansible让web主机进行挂载nfs目录
#创建/test_nfs目录
[root@ms01 ~]# ansible web -m file -a 'path=/test_nfs state=directory'
#挂载
[root@ms01 ~]# ansible web -m mount -a 'fstype=nfs src="10.0.0.41:/nfsdata" path=/test_nfs state=mounted'
查看web主机的挂载情况和/etc/fstab文件的情况
4.6.2 cron定时任务模块
cron模块 | 定时任务配置中的内容 | |
---|---|---|
name | #及后面的内容 | 定时任务名字(一定要加上), 对应下面注释的内容 |
minute | */2 | 分钟 minute="*/2",如果没有用到不用填写即可 |
hour | 小时 | |
day | 日期 | |
month | 月份 | |
week | 周几 | |
job | 命令/脚本 | 指定命令或脚本 job="/sbin/ntpdate ntp1.aliyun.com &>/dev/null" |
state | present 默认是添加 absent 删除 |
下面通过一个NTP同步时间的案例来看看
[root@ms01 ~]# ansible web -m cron -a 'name="sysnc time by YinJay" minute="*/2" job="/sbin/ntpdate ntp1.aliyun.com &>/dev/null" state=present'
web主机上查看cat /var/spool/cron/root (或者crontab -e也可以)
[root@web01 ~]# cat /var/spool/cron/root
#Ansible: sysnc time by YinJay
*/2 * * * * /sbin/ntpdate ntp1.aliyun.com &>/dev/null
4.7 用户管理模块
user模块 | |
---|---|
name | 用户名 |
uid | 指定uid |
group | 指定用户组 |
shell | 指定命令解释器 |
create_home | 是否创建家目录 create_home=yes/no |
state | present 添加 absent 删除 |
4.7.1 创建用户chen
[root@ms01 ~]# ansible web -m user -a 'name=chen'
4.7.2 创建虚拟用户,指定uid
命令解释器:/sbin/nologin,同时不创建家目录。构成命令useradd -u 10086 -s /sbin/nologin -M tengine
[root@ms01 ~]# ansible web -m user -a 'name=tengine uid=10086 shell=/sbin/nologin create_home=no state=present'
5. Ansible剧本
5.1 什么是剧本(playbook)?
ansible 命令每次只能执行一个任务,这种运行方式称为 Ad-hoc (点对点模式),Ansible 真正强大的是 playbook。每一个剧本(playbook)中都包含了一系列的任务,这每个任务在ansible中又被称为“戏剧”(play),一个剧本中包含多出戏剧。
ansible有两种执行方式ad-hoc和ansible-playbook,ad-hoc主要用于临时命令的执行,而playbook我们可以理解为ad-hoc的集合,有点类似shell脚本,ad-hoc就相当于shell脚本里的某条任务语句,playbook就相当于整个shell脚本。
playbook是由一个或多个“play”组成的列表,play的主要功能在于将预定义的一组主机,装扮成事先通过ansible中的task定义好的角色。task实际是调用ansible的一个模块,将多个play组织在一个playbook中,即可以让他们联合起来,按事先编排的机制执行预定义的动作。
5.2 playbook、play 和 task 的关系
-
playbook 中可以定义一个或多个 play
-
每个 play 中可以定义一个或多个 task
-
其中还可以定义两类特殊的 task:pre_tasks 和 post_tasks
-
pre_tasks 表示执行执行普通任务之前执行的任务列表
-
post_tasks 表示普通任务执行完之后执行的任务列表
-
-
每个 play 都需要通过 hosts 指令指定要执行该 play 的目标主机
-
每个 play 都可以设置一些该 play 的环境控制行为,比如定义 play 级别的变量
5.3 playbook的语法yaml
ansible 的 playbook 采用 yaml 语法,YAML 文件后缀通常为.yaml 或.yml。
YAML 的基本语法规则如下:
-
使用缩进表示层级关系
-
缩进时不允许使用tab键,只允许使用空格
-
缩进的空格数目不重要,只要相同层级的元素左对齐即可
-
yaml文件以“---”作为文档的开始,以表明这是一个yaml文件
- 即使没有使用---开头,也不会有什么影响
-
表示注释,从这个字符一直到行尾,都会被解释器忽略
-
字符串不用加引号,但在可能产生起义时,需要加引号(单双引号皆可)
-
布尔值非常灵活,不区分大小写的true/false、yes/no、on/off、y/n、0/1都允许
---
- name: play 1
hosts: nginx #hosts用于指定在哪些主机执行
tasks: #tasks用于对于这些主机,运行什么模块及选项
- name: task1 in play1 #name 任务的名称
debug:
msg: "output task1 in play1"
- name: task2 in play1
debug:
msg: "output task2 in play1"
- name: play 2
hosts: apache
tasks:
- name: task1 in play2
debug:
msg: "output task1 in play2"
- name: task2 in play2
debug:
msg: "output task2 in play2"
5.4 初探playbook编写
5.4.1 在yinjay组下主机的/tmp下面创建yinjay.txt
那首先就是要用到shell或者command这个模块。这里选用shell示例,编写yaml文件,如下:
---
- hosts: yinjay
tasks:
- name: No.1 test touch file
shell: touch /tmp/yinjay.txt
[root@ms01 playbook]# tree
.
├── 01-touch.yaml
└── hosts
0 directories, 2 files
[root@ms01 playbook]# ansible-playbook -i hosts 01-touch.yaml
再用Ad-hoc的方式查一下是否成功,没问题的。
5.4.2 添加定时同步时间的定时任务
可以用下列命令先查看定时任务列表都有啥
ansible yinjay -m command -a 'crontab -l'
下面编写剧本02-sync_time.yml,简单粗暴版本
---
- host: yinjay
tasks:
- name: add crond sync time
cron: name="sync time" minute="*/2" job="/sbin/ntpdate ntp1.aliyun.com &>/dev/null" state=present
优化版如下
---
- hosts: yinjay
tasks:
- name: add crond sync time
cron:
name: sync time
minute: "*/2"
job: /sbin/ntpdate ntp1.aliyun.com &>/dev/null
state: present
5.4.3 批量下载安装zabbix-agent2-6.0客户端并启动
分析步骤,首先需要有这个软件包,要考虑从epel仓库yum下载安装,还是从官网下载rpm包再进行安装。如果要下载就用到get_url这个模块,安装用到yum模块,最后就是通过systemd这个模块来进行启动。
---
- hosts: yinjay
tasks:
- name: 1.download zabbix agent rpm soft
get_url:
url: https://mirrors.tuna.tsinghua.edu.cn/zabbix/zabbix/6.0/rhel/7/x86_64/zabbix-agent2-6.0.0-1.el7.x86_64.rpm
dest: /tmp/
validate_certs: no
- name: 2.install zabbix agent rpm
yum:
name: /tmp/zabbix-agent2-6.0.0-1.el7.x86_64.rpm
state: installed
- name: 3.start zabbix agent service
systemd:
name: zabbix-agent2
enabled: yes
state: started
5.4.4 部署rsync服务端
分析部署rsync服务端的先后步骤以及所需要的模块,进行编写yml
编写剧本
---
- hosts: yinjay
tasks:
- name: 1.部署nfs服务端软件
yum:
name: nfs-utils
state: installed
- name: 2.修改配置文件
lineinfile:
path: /etc/exports
line: "/data 172.16.1.0/24(rw)"
state: present
backup: yes
- name: 3.创建对应的目录、权限
file:
path: /data
owner: nfsnobody
group: nfsnobody
state: directory
- name: 4.启动rpc服务
systemd:
name: rpcbind
enabled: yes
state: started
- name: 5.启动nfs服务
systemd:
name: nfs
enabled: yes
state: started
运行过程
进行测试,在ms01主机上创建三个文件夹进行挂载,没问题
5.5 了解变量
5.5.1 变量分类
变量具体有下面几种类型,意思就是以后在playbook中可以去使用变量,使用不同类型的变量很实现怎样的功能或者说是便捷性,请看下面的使用案例。
变量 | 含义与特点 | 应用场景 |
---|---|---|
命令行 | 临时使用,较少用 | |
剧本文件中vars定义 | 仅当前这个剧本生效 | |
变量文件 vars_files | 在剧本中通过vars_files: 变量文件路径(./vars.yml) | 每次使用需要手动在剧本中加载. vars_files: ./vars.yml |
主机组共用的变量文件 group_vars | 应用广泛,根据主机清单里面的分组创建目录,存放vars.yml | 根据主机所属的主机组,自动读取group_vars/组名/vars.yml文件 group_vars/all/vars.yml |
ansible 内置变量(facts变量) | 收集主机的基本信息、IP地址、主机名、系统及版本等等信息 | 如果想提升ans执行速度可以关闭 gather_facts: no |
register变量 | 实现命令行$() 或 ``功能 | 寄存器变量(注册,临时变量):把命令,模块结果存放在register变量中。 |
5.5.2 使用vars
在/ansible/playbook/var下创建一个01-vars_test.yml文件,内容如下,使用变量时需要用两对花括号,中间变量名。同时变量申明的vars和hosts、tasks是同一级,接着再书写变量。
---
- hosts: yinj
vars:
dir_name: /yinjay-01
file_name: /yinjay.txt
tasks:
- name: 01. mkdir
file:
path: "{{ dir_name }}"
state: directory
- name: 02. touch
file:
path: "{{ dir_name }}/{{ file_name }}"
state: touch
此时我在该yml文件下也写一个新的hosts,方便测试。同时执行一下
测试的结果,也是没问题的。这里注意的是我用的是Ad-hoc的方式,使用的yinjay组是默认的/etc/ansible/hosts的这个文件,所以两个hosts内容要一致。
5.5.3 使用vars_files
当需要使用比较多变量的时候,可以把变量存放到一个文件中。下面创建一个02-vars.yml存放变量
dir_name: /yinjay-vars_files
file_name: /vars.txt
再创建一个02-vars_files_dir.yml
---
- hosts: yinjay
vars_files: ./02-vars.yml
tasks:
- name: 01. mkdir
file:
path: "{{ dir_name }}"
state: directory
- name: 02. touch
file:
path: "{{ dir_name }}/{{ file_name }}"
state: touch
执行测试一下,没问题
5.5.4 使用group_vars
创建一个变量文件,给某个组共用。那么首先需要创建一个group_vars目录,目录下面创建以主机组命名的目录,存放变量文件vars.yml,具体目录如下:
[root@ms01 var]# tree group_vars/
group_vars/
├── all #存放所有组的共用变量
│ └── vars.yml
├── backup #存放backup组的变量
│ └── vars.yml
├── db #存放db组的变量
│ └── vars.yml
└── web #存放web组的变量
└── vars.yml
4 directories, 4 files
当在group_vars的上一级目录执行剧本时,如果yml定义了某个组,那就会自动加载在group_vars目录下相同组名的目录的vars.yml
5.5.5 facts变量
facts变量是ansible的内置变量,执行剧本,有个默认任务(task),收集每个主机的基本信息。可以通过以下命令去查看都有哪些信息ansible 172.16.1.7 -m setup
,记得替换IP。
下面写一个例子,03-facts.yml获取所有机器的基础信息保存到/tmp/主机名命名文件中
---
- hosts: yinjay
tasks:
- name: 创建文件并写入系统基本信息
lineinfile:
path: /tmp/{{ ansible_hostname }}
create: yes
line: "主机名:{{ ansible_hostname }}\n
网卡:{{ ansible_default_ipv4.alias }}\n
ip地址:{{ ansible_default_ipv4.address }}\n
内存总计:{{ ansible_memtotal_mb }}"
5.5.6 register变量
registers可以用来捕捉一个task的输出作为一个变量。这种变量包含了这个任务的返回值。当我们使用不同的模块时, 会遇到的常见的返回值有:backup_file, changed, failed, invovation, msg, rc, results, skipped, stderr, stderr_lines, stdout. stdout_lines等。每一个注册过的变量在ansible任务执行的host上在执行接来下的任务时都是可用的。
下面来写一个例子,比如我想获取某些主机上有无安装nfs,如果安装了,是安装了哪个版本。创建一个04-register.yml,内容如下:
---
- hosts: yinjay
tasks:
- name: Ansible register variable test
shell: "rpm -qa nfs*"
register: relust
- name: out info #使用debug模块的 msg功能来输出register变量中的数据
debug:
msg: "{{ relust }}"
查看输出信息,有安装和没安装的主机在输出信息中是不同的,这里只演示register的使用,后续可以利用此功能与其他功能配合。
常用方法:
stdout 正常输出信息
rc 取出返回值
stderr 取出错误信息
5.5.7 tags标签
一般用于调试剧本,给剧本的每个task设置个标签,运行剧本的时候可以运行指定标签,也可以排除某些标签。
下面通过部署nfs服务端的剧本来看一下
---
- hosts: yinjay
tasks:
- name: 1.部署nfs服务端软件
yum:
name: nfs-utils
state: installed
tags:
- install
- name: 2.修改配置文件
lineinfile:
path: /etc/exports
line: "/data 172.16.1.0/24(rw)"
state: present
backup: yes
tags:
- conf
- conf_file
- name: 3.创建对应的目录、权限
file:
path: /data
owner: nfsnobody
group: nfsnobody
state: directory
tags:
- conf
- conf_dir
- name: 4.启动rpc服务
systemd:
name: rpcbind
enabled: yes
state: started
trgs:
- start_srv
- name: 5.启动nfs服务
systemd:
name: nfs
enabled: yes
state: started
trgs:
- start_srv
比如我们所有主机都安装nfs了,那么就可以跳过第一步
ansible-playbook -i hosts --skip-tags install 04-nfs.yml
如果是文件夹换了,配置文件内容改了,可以这样子(yml中conf是两个地方的标签哦)
ansible-playbook -i hosts --tags conf,start_srv 04-nfs.yml
5.5.8 忽略错误
用于运行剧本的时候,强制让某个任务(模块)运行即使出错了,也不要中断我们的剧本。
ignore_errors: yes #与 -name 和模块名同级
5.6 进阶应用
5.6.1 include功能
include文件包含,把一个任务分成多个剧本来实现。书写个总剧本文件,通过include_tasks: 引用子剧本文件。子剧本文件中只需要些模块部分(task部分即可)
以下在同一个目录有三个文件
bushu_nfs_all.yml
---
- hosts: nfs
tasks:
- include_task: bushu_nfs_server.yml
- hosts: web
tasks:
- include_tasks: bushu_nfs_client.yml
bushu_nfs_server.yml
- name:
模块:
- name:
模块:
bushu_nfs_client.yml
- name:
模块:
- name:
模块:
5.6.2 handlers触发器
handler 触发器(条件),满足条件后再做什么事情,比如可以做检查配置文件有变化了,再重启服务。(handlers与hosts对齐)
---
- hosts: db
tasks:
- name: 01. 分发配置文件
copy:
src: ./exports
dest: /etc/exports
backup: yes
notify:
- restart nfs #这里要跟handlers定义的name一样
handlers:
- name: restart nfs #也就是这里
systemd:
name: nfs
enabled: yes
state: restarted
Tips:Ansible 在执行完某个任务之后并不会立即去执行对应的 handler,而是在当前 play 中所有普通任务都执行完后再去执行 handler,这样的好处是可以多次触发 notify,但最后只执行一次对应的 handler,从而避免多次重启。
5.6.3 when判断
when可以实现对于某个模块在满足或不满足某条件下再执行模块,比如我下面的例子,是判断某个服务是否有开启,如果没有开启再进行systemd这个模块来启动服务。
[root@ms01 playbook]# cat 05-check_service.yml
---
- hosts: yinjay
tasks:
- name: check service status
service_facts:
register: services_state #通过service_facts这个模块获取所有服务的信息
- name: Debug services result
debug:
msg: "{{ services_state.ansible_facts.services['zabbix-agent2.service']['state']}}"
register: zabbix_result #获取zabbix-agent2的状态信息存进这个注册变量中
- name: if service state
systemd:
name: zabbix-agent2
state: started
when: zabbix_result != 'running' #首先会先通过when判断上面注册变量中存的服务状态信息,然后再确定是否执行systemd模块
Tips:通常可以用于判断是什么Linux版本类型来执行不同版本类型的脚本,其他方面的判断也相似。上面只是应用一个简单的例子!
5.6.4 with_items循环
下面以一个例子来写,比如想要重启nfs整一套服务,那nfs需要去rpc服务注册端口,那么rpc服务就得优先启动,再重启nfs。
[root@ms01 playbook]# cat 06-with_items.yml
---
- hosts: yinjay
tasks:
- name: restart services
systemd:
name: "{{ item }}"
state: restarted
with_items:
- rpcbind
- nfs
再来一个案例就是循环添加用户并指定uid
[root@ms01 playbook]# cat 07-add_users.yml
---
- hosts: yinjay
tasks:
- name: add users
user:
name: "{{ item.name }}"
uid: "{{ item.uid }}"
state: present
with_items:
- {name: "chen01",uid: "2001"}
- {name: "chen02",uid: "2002"}
5.6.5 Jinja2模板
Jinja2模板主要用于需要传输文件,同时需要解析变量的场景。copy模块就不能解析变量,使用Jinja2模板再使用template模块就可以实现,下面演示一个简单的例子。(文件名需改成j2格式)
[root@ms01 playbook]# cat exports.j2 #j2文件
# {{ ansible_hostname }}
/data 172.16.1.0/24(rw)
[root@ms01 playbook]# cat 08-j2_test.yml
---
- hosts: yinjay
tasks:
- name: 分发nfs配置文件
template:
src: ./exports.j2
dest: /etc/exports
主机上查看,已经解析了变量并在文件中生效
Tips:详细的Jinja2使用可以参考骏马金龙博主的Jinja2文章
5.6.6 Roles文件组织形式
各种 yml 文件多了,特别是多个 playbook 任务混在一起时,很容易混乱。所以Roles定义了一种更为规范的文件组织形式。下面是常用的Roles文件结构
下面以一个部署nfs服务端的案例进行拆分剧本
- 原部署nfs服务端playbook剧本
---
- hosts: nfs
tasks:
- name: 01. 部署nfs服务端软件
yum:
name: nfs-utils
state: installed
- name: 02. 修改配置文件
template:
src: ./exports.j2
dest: /etc/exports
backup: yes
notify:
- 04. 启动服务
- name: 03. 创建对应的目录,权限
file:
path: /data/
owner: nfsnobody
group: nfsnobody
state: directory
handlers:
- name: 04. 启动服务
systemd:
name: "{{ item }}"
enabled: yes
state: restarted
with_items:
- rpcbind
- nfs
- 将剧本转换成roles格式
#1. tasks编写在tasks/目录下的main.yml
[root@ms01 roles]# cat nfs-server/tasks/main.yml
- name: 01. 部署nfs服务端软件
yum:
name: nfs-utils
state: installed
- name: 02. 修改配置文件
template:
src: ./exports.j2
dest: /etc/exports
backup: yes
notify:
- 04. 启动服务
- name: 03. 创建对应的目录,权限
file:
path: /data/
owner: nfsnobody
group: nfsnobody
state: directory
#2. 在templates目录编辑新文件或者拷贝即可,存放j2文件
[root@ms01 roles]# cp ../exports.j2 nfs-server/templates/
[root@ms01 roles]# tree nfs-server/templates/
nfs-server/templates/
└── exports.j2
#内容如下:
[root@ms01 roles]# cat nfs-server/templates/exports.j2
# {{ ansible_hostname }}
/data 172.16.1.0/24(rw)
#3. handlers部分编写在handlers/目录下的main.yml
[root@ms01 roles]# cat nfs-server/handlers/main.yml
- name: 04. 启动服务
systemd:
name: "{{ item }}"
enabled: yes
state: restarted
with_items:
- rpcbind
- nfs
#4. 书写入口剧本top.yml,与nfs-server同目录下
[root@ms01 roles]# cat top.yml
---
- hosts: nfs
roles:
- role: nfs-server #此处与任务role目录同名,有多个role可以再下面添加
#5. 整体目录结构查看
[root@ms01 roles]# tree /ansible/roles/
/ansible/roles/
├── nfs-server
│ ├── files
│ ├── handlers
│ │ └── main.yml
│ ├── tasks
│ │ └── main.yml
│ └── templates
│ └── exports.j2
├── rsync
│ ├── files
│ ├── handlers
│ │ └── main.yml
│ ├── tasks
│ │ └── main.yml
│ └── templates
└── top.yml
#6. 在同目录下编写hosts文件
[root@ms01 roles]# cat hosts
[nfs]
172.16.1.31
- 运行剧本并测试挂载
Tips:以上部分内容解释来自骏马金龙、Linux-1874博主,感谢提供知识干货。