1、ansible概述

Ansible是用python写的一款自动化运维的工具,可以实现批量管理、批量分发、批量执行、维护等功能

批量管理工具说明
ansilbe无客户端,基于ssh进行管理与维护
saltstack需要安全客户端,基于ssh进行管理
terraformtf批量管理基础设施(比如:批量创建100台公有云)
…..

2、ansible管理架构

ansible组成:

Inventory:主机清单,被管理主机的ip列表,也包含分类
ad-hoc模式:命令行批量管理(使用ans模块),临时任务
playbook 剧本模式:类似于把操作写出脚本,可以重复运行这个脚本

3、环境准备

在管理端部署ansible。管理端:ip:192.168.10.61、172.16.1.61,主机名:m01

在使用ansible之前,必需做好密钥认证。

1)安装ansible

[root@m01 ~]#yum install -y ansible

2)配置

ansible的配置文件:/etc/ansible/ansible.cfg

关闭host_key_checking,把它设为host_key_checking=false

开启日志功能:log_path = /var/log/ansible.log

[root@m01 ~]#vim /etc/ansible/ansible.cfg
[defaults]
host_key_checking=False
log_path = /var/log/ansible.log
[inventory]
[privilege_escalation]
[paramiko_connection]
[ssh_connection]
[persistent_connection]
[accelerate]
[selinux]
[colors]
[diff]

配置文件大多数是注释:

root@m01 ~]#grep -Evn '^$|#' /etc/ansible/ansible.cfg 
10:[defaults]
327:[inventory]
340:[privilege_escalation]
346:[paramiko_connection]
370:[ssh_connection]
431:[persistent_connection]
445:[accelerate]
460:[selinux]
469:[colors]
485:[diff]
[root@m01 ~]#

ansible不是服务,所以修改配置文件后,没有重启ansible服务的说法。

4、ans-inventory主机清单

主机清单:就是让ansible管理的节点的列表

ansible默认读取在/etc/ansible/hosts文件,并非/etc/hosts

可以把主机清单放在指定的目录中,运行ansible的时候通过-i选项指定主机清单文件即可。

环境准备分类主机名ip地址
ansible管理端m01192.168.10.61、172.16.1.61
被管理端lblb01192.168.1.5、172.16.1.5
被管理端webweb01192.168.10.7、172.16.1.7
被管理端webweb02192.168.10.8、172.16.1.8
被管理端nfsnfs01192.168.10.31、172.16.1.31
被管理端backupbackup192.168.10.41、172.16.1.41
被管理端dbdb01192.168.10.51、172.16.1.51

ansible默认读取的主机文件:/etc/ansible/hosts

如何修改ansible默认的主机文件:

修改配置文件(/etc/ansible/ansible.cfg)的inventory参数,比如,修改为:
inventory = ./hosts
然后修改环境变量,执行命令:export  ANSIBLE_INVENTORY=./hosts
永久修改环境变量:echo "export  ANSIBLE_INVENTORY=./hosts" >> /etc/profile

1)分组

按照层次、功能、业务等将主机进行分组

配置主机清单:

[root@m01 ~]#vim /etc/ansible/hosts
[web] #分组的名称,组名:web
172.16.1.7  #组内的主机
#172.16.1.8

[backup]    #backup组
172.16.1.41

[nfs]
172.16.1.31

ansible命令使用:

ansible  ip或主机名或分组或all  -m 模块名称  -a 模块的参数或命令等

例如:

[root@m01 ~]#ansible all -m command -a "hostname"
172.16.1.7 | CHANGED | rc=0 >>
web01
172.16.1.41 | CHANGED | rc=0 >>
backup
172.16.1.31 | CHANGED | rc=0 >>
nfs01
[root@m01 ~]#

command:命令模块,只能使用简单的命令,不能使用管道,不能包含特殊符号。command是ansible默认的模块,可以不写。比如:

[root@m01 ~]#ansible all -a "hostname"
172.16.1.31 | CHANGED | rc=0 >>
nfs01
172.16.1.41 | CHANGED | rc=0 >>
backup
172.16.1.7 | CHANGED | rc=0 >>
web01
[root@m01 ~]#

all:表示所有主机(所有分组)

管理某一组:

[root@m01 ~]#ansible web -m command -a "hostname"
172.16.1.7 | CHANGED | rc=0 >>
web01
[root@m01 ~]#

2)子组

比如,对backuo、nfs这2个分组再创建个叫data的分组

新创建的分组data包含了已有的分组backuo和nfs,此时backuo和nfs叫做data的子组。使用children标识,如下:

[root@m01 ~]#cat /etc/ansible/hosts
[web]
172.16.1.7
#172.16.1.8
[backup]
172.16.1.41
[nfs]
172.16.1.31
[data:children]
backup
nfs

管理子组:

[root@m01 ~]#ansible data -a "hostname"
#注意:是data,不是data:children

5、指定用户、密码

没有做密钥认证的话,就必须指定用户和密码,该方法不推荐。

没有做密钥认证的主机清单书写格式,比如

[nfs]
172.16.1.31
192.168.10.10 ansible_user=root ansible_password=12345 ansible_port=22

显然,用户名和密码都是明文,不安全,所以推荐做密钥认证

6、ansible的模块

ansible模块概述:

ansible中的模块就类似于Linux中的命令,Linux命令管理系统,我们通过ansible模块实现批量管理
ansible中的模块一般相当于Linux中的一些命令,如:yum模块、file模块、user模块
ansible中的模块拥有不同的选项,这些选项一般都是一些单词,拥有自己的格式与要求

常用模块分类:

模块分类模块
命令和脚本模块command模块:ansible默认的模块,执行简单命令,不支持管道、特殊符号。
shell模块:执行命令,支持特殊符号、管道。
script模块:分发脚本并执行
文件file模块:创建目录、文件、软连接等。
copy模块:远程分发文件,修改权限、所有者、备份
服务systemd模块:服务管理 ,systemctl命令
servece模块:服务管理
软件包yum源:yum_repository
yum模块/apt模块:yum命令/apt命令
get_url:下载软件,wget命令
系统管理mount模块:挂载 ,mount命令
cron模块:定时任务。
crontab命令
用户管理group:管理用户组
user:管理用户
其他模块unarchive:压缩解压
rsync:synchronize mysql_db、mysql_user:数据库模块
ansible管理docker、k8s、zabbix、granfana….
用于调试模块ping模块:ansible与其他节点连通性
debug模块:用于检查、显示变量

0)查看模块帮助

查看模块帮助:ansible-doc  -s  模块名

模块的官方文档:https://docs.ansible.com/ansible/latest/collections/index.html

1)shell、script模块

1.1 shell模块

主要用于执行shell命令,例如:

[root@m01 ~]#ansible all -m shell -a "hostname -I | awk '{print \$2}'"
1.2 script模块

执行流程:分发脚本(传输脚本),在被管理端执行脚本

#创建一个脚本
[root@m01 ~]#vim /server/script/ansible-scripts.sh
#!/bin/bash
hostname
hostname
sleep 10
ip a s eth0 | awk -F'[ /]+' 'NR==3{print $3}'

ansible执行:

[root@m01 ~]#ansible all -m script -a '/server/script/ansible-scripts.sh'

然后在被管理端查看一下进程:

可以看到被管理端发生了什么。可以看到,管理端把脚本传输到被管理端的一个临时目录后,被管理端再执行这个脚本,执行完成后,再清理这个脚本文件。

2)file、copy模块

2.1 file模块

file模块不但可以管理文件,还可以管理目录、软连接

file模块相当于把touch命令、rm命令、ln -s命令结合在一起

file模块参数模块说明
path路径(目录,文件),必须要写
srcsource:源,源文件,一般用于link(创建软连接模式),用于指定源文件
state状态(模式),具体要做什么,比如创建/删除,操作文件/目录 state=directory 创建目录 staet=file(默认) 更新文件,如果文件不存在也不创建 state=link 创建软连接 state=touch 创建文件 state=absent 删除(注意,如果是目录,则递归删除目录)
mode创建并修改权限,比如,mode=755
owner用户,比如,onwre=root
group用户组,比如,group=root

案例01:创建/opt/1.txt文件

[root@m01 ~]#ansible all -m file -a 'path=/opt/1.txt state=touch'

案例02:创建目录/app/a/b/c/

[root@m01 ~]#ansible all -m file -a 'path=/app/a/b/c/  state=directory'

案例03:创建/etc/hosts软连接到/tmp/目录

[root@m01 ~]#ansible all -m file -a 'src=/etc/hosts path=/tmp/hosts.link  state=link'
#注意:必须指定软连接的名称,否则会报错,如下:则会报错
ansible all -m file -a 'src=/etc/hosts path=/tmp/  state=link'

案例04:创建/ans-backup/目录,所有者是test,用户组是test,权限700

[root@m01 ~]#ansible all -m file -a 'path=/ans-backup/ owner=test group=test mode=700 state=directory'

案例05:删除/ans-backup/目录

[root@m01 ~]#ansible all -m file -a 'path=/ans-backup/ state=absent'
2.2 copy模块

批量分发:scp1个节点(管理节点)发送文件或压缩包到被管理端,注意:copy是单向传输

类似与scp命令

copy模块参数模块说明
srcsource,源文件,即管理端的某个文件
destdestination,目标,被管理端的目录/文件
backupbackup=yes,则会在覆盖前备份,文件内容要有变化/区别
mode修改权限
owner修改为指定用户
group修改为指定用户组

案例01:分发/etc/hosts文件,如果文件存在则备份

[root@m01 ~]#ansible all -m copy -a 'src=/etc/hosts dest=/etc/hosts backup=yes'

额外扩展:

2.3 lineinfile模块

修改文件策略:

sed修改
cat批量写入
echo写入
管理机准备好配置文件,然后copy分发
lineinfile模块

用来修改配置文件,类似于sed -i和sed ‘cai’

lineinfile模块参数模块说明
path指定文件
regexp指定正则,通过正则过滤出要修改的行
line这一行修改后的内容

案例:分发写好的/etc/selinux/conf(开启)文件到/tmp/目录,然后通过lineinfile修改为关闭

[root@m01 ~]#ansible all -m copy -a 'src=/etc/selinux/config  dest=/tmp/'
#修改
[root@m01 ~]#ansible all -m lineinfile -a "path=/tmp/config regexp='^SELINUX=' line='SELINUX=disabled'"

查看修改的结果:

[root@m01 ~]#ansible all -m shell -a "grep -E '^SELINUX=' /tmp/config"
172.16.1.41 | CHANGED | rc=0 >>
SELINUX=disabled
172.16.1.31 | CHANGED | rc=0 >>
SELINUX=disabled
172.16.1.7 | CHANGED | rc=0 >>
SELINUX=disabled
[root@m01 ~]#

3)systemd-服务管理模块

systemd模块相当于是Linux的systemctl命令

用于启动、关闭、重启服务

systemd参数模块说明
name用于指定服务名称(1次只能写1个服务)
enabledyes:开启启动,no关闭开启启动
state表示服务状态:开、关、重启
state=started 开启
state=stopped 关闭
state=reloaded 重读配置文件(如果服务支持的话) state=restarted 重启
daemon-reloadyes是否重新加载对应的服务的管理配置文件

案例01:开启crond服务并设置开机启动

[root@m01 ~]#ansible all -m systemd -a 'name=crond enabled=yes state=started'

案例02:关闭firewalld服务,并取消开机启动

[root@m01 ~]#ansible all -m systemd -a 'name=firewalld enabled=no state=stopped'

额外扩展:

systemd模块适用于当前大部分的Linux系统
service模块适用于管理就的Linux系统

4)软件管理模块

yum模块:安装卸载软件等

get_url模块:wget命令

yum_repository模块:yum源配置模块,未来可以通过copy模块

4.1 yum模块

yum命令管理软件,包含了yum/apt命令

yum模块参数说明
name指定软件包名字,可以指定多个,用逗号(,)分隔
stateinstalled:安装(也可写成present)(默认)
removed:删除(也可以写成absent)
lastest:安装或更新
update_cache可以设为no,加速,表示不更新本地yum缓存,实际应用建议开启(yes)

案例:安装htop、tree、sshpass、lrasz

[root@m01 ~]#ansible all -m yum -a "name=htop,tree,lrzsz,sshpass"
4.2 get_url模块

相当于是wget命令

推荐在管理节点下好,再使用copy分发使用

get_url模块参数说明
url指定要下载的地址
dest下载到哪个目录

案例:下载zabbix-agent软件包到/app/tools/目录中

#创建/app/tools/目录
[root@m01 ~]#ansible all -m file -a "path=/app/tools/ state=directory"
#下载
[root@m01 ~]#ansible all -m get_url -a "url=https://mirrors.aliyun.com/zabbix/zabbix/6.0/rhel/7/x86_64/zabbix-agent-6.0.13-release1.el7.x86_64.rpm dest=/app/tools/"

后续可以使用yum模块安装本地软件,如:name=/app/tools/xxx.rpm,例如:

[root@m01 ~]#ansible all -m yum -a "name=/app/tools/zabbix-agent-6.0.13-release1.el7.x86_64.rpm  state=installed"
4.3 yum_repository模块

未来写好yum配置文件(xxx.repo),然后copy分发(推荐)

yum源模块yum_repository说明
nameyum源中的名字,如:[epel]
descriptionyum源的注释说明,对应的是name的内容
baseurlyum源中的baseurl下载地址
enabled是否开启这个源(yes、no)
gpgcheck是否开启gpgcheck功能(yes、no)
file指定yum源的文件,自动添加.repo,默认与模块名字一致

使用yum_repository如下:

[root@m01 ~]#ansible all -m yum_repository \
-a 'name=epel description="Extra Packages for Enterprise Linux 7 - $basearch" baseurl="http://mirrors.aliyun.com/epel/7/$basearch"
enabled=yes 
gpgcheck=no'

对比如下:

yum配置文件内容yum源模块yum_repository的选项
[epel]name=epel #默认yum源文件的名字与这个一致
name=XXXXdescription=XXX
baseurl=xxxbaseurl=xxx
enabled=1enabled=yes
gpgcheck=0gpgcheck=no

5)user、group模块

用户管理:useradd、userdel

用户组管理:groupadd

5.1)user模块

user模块:用户管理

user模块参数说明
name用户名
uid指定uid
group指定用户组,一般用于事先创建好了的用户组,通过该选项指定一下
shell指定命令解释器,默认是:/bin/bash
create_home是否创建家目录(yes、no),默认是创建
state添加:present
absent:删除
password加密的密码(不是明文密码)

案例01:创建uid为2000的虚拟用户www-ans

[root@m01 ~]#ansible all -m user -a 'name=www-ans uid=2000 shell=/sbin/nologin create_home=no state=present'

案例02:批量更新密码

[root@m01 ~]#ansible all -i localhost, -m debug -a "msg={{ '1' | password_hash('sha512','test')}}"
localhost | SUCCESS => {
    "msg": "$6$test$zNd9/8gSf8OR7HjGSlRSiHlhddlIUzDLSAmUyHAjJd2oKgabN3A3EvJcw96OojKce8/DO0h3tHn4cbQKWUuqO0"
}
[root@m01 ~]#
#下面命令可以更新密码,假设密码为1
[root@m01 ~]#ansible all -m user -a "name=test password={{ '12345' | password_hash('sha512','test123')}} state=present"
或者:
[root@m01 ~]#ansible all -m shell -a "echo 12345 | passwd --stdin test"

关于{{}}相关解释

{{ ‘12345’ | password_hash(‘sha512′,’test123’)}}

表示12345是密码,经过管道传输给了password_hash()插件,sha512算法加密,test123是随机字符,用于生成随机加密后的密码

5.2)group模块

group模块:用户组管理模块

group模块参数说明
name指定用户组名称
gid指定组的id
statepresent:添加
absent:删除

6)cron模块

用于管理系统的定时任务,替代crontab -e功能

cron模块参数说明
name定时任务名称(一定要加上),对应下面注释的内容
minute分钟,如:minute=”*/2″
hour小时
day日期
month月份
week周几
job指定命令或脚本(定向到空),如:job=”/sbin/ntpdate ntp1.aliyun.com &>/dev/null”
statepresent:添加定时任务(默认)
absent:删除

案例01:每3分钟同步时间

正常使用crontab -e如下:

[root@m01 ~]#crontab -e
#1、sync time  #注释 即cron模块的name的内容
*/3 * * * * /sbin/ntpdate ntp1.aliyun.com &>/dev/null

清理已有的定时任务:

[root@m01 ~]#ansible all -a "sed -i '/ntpdate/d' /var/spool/cron/root"

创建定时任务:

[root@m01 ~]#ansible all -m cron -a 'name="1、sync time" minute="*/3" job="/sbin/ntpdate ntp1.aliyun.com &>/dev/null" state=present'

删除定时任务:

[root@m01 ~]#ansible all -m cron -a 'name="1、sync time" state=absent'

7)mount模块

mount模块:实现mount命令进行挂载,可以修改/etc/fstab文件实现永久挂载

mount模块参数说明
fstypefilesystem type,指定文件系统,如:nfs、xfs、ext4、iso9660
src源地址(nfs服务端地址,如:172.16.1.31:/data/
path注意不是目标地址(dest),而是挂载点(即要把源挂载到哪里)
stateabsent:卸载并修改/etc/fstab文件
unmounted:卸载并修改/etc/fstab文件
present:仅修改/etc/fstab文件但不挂载
mounted:挂载并修改/etc/fstab文件
remounted:重新挂载

案例:通过ansible管理在web01上挂载nfs:/data/挂载到web01的/ans-upload/

nfs服务端:172.16.1.31,共享目录:/ans-data/

nfs服务端(已添加到ansible清单的nfs组):
1、安装nfs(nfs-utils、rpcbind)
2、配置nfs(/etc/exports)
3、创建共享目录并修改属主属组为nfsnobody
4、启动nfs服务

客户端:
1、安装nfs(nfs-utils)
2、创建挂载目录,不需要修改属主属组
3、挂载

nfs服务端配置:

#1、安装nfs
[root@m01 ~]#ansible nfs -m yum -a 'name="nfs-utils,rpcbind" state=present'

#2、修改nfs配置文件(/etc/exports)
[root@m01 ~]#ansible nfs -m lineinfile -a 'path=/etc/exports line="/ans-data/  172.16.1.0/24(rw)"'

#3、创建共享目录/ans-data/ 
[root@m01 ~]#ansible nfs -m file -a "path=/ans-data/ owner=nfsnobody group=nfsnobody state=directory"

#4、重启rpc,nfs服务
[root@m01 ~]#ansible nfs -m systemd -a "name=rpcbind enabled=yes state=restarted"
[root@m01 ~]#ansible nfs -m systemd -a "name=nfs enabled=yes state=restarted"

web01的ip:172.16.1.7,已纳入web主机清单

#1、在web服务器上安装nfs
[root@m01 ~]#ansible web -m yum -a 'name=nfs-utils state=present'

#2、创建挂载点/ans-upload/
[root@m01 ~]#ansible web -m file -a 'path=/ans-upload/ state=directory'

#3、挂载nfs
[root@m01 ~]#ansible web -m mount -a 'src=172.16.1.31:/ans-data/ path=/ans-upload/ fstype=nfs state=mounted'

#4、检查
[root@m01 ~]#ansible web -m shell  -a 'df -Th | grep nfs'
172.16.1.7 | CHANGED | rc=0 >>
172.16.1.31:/ans-data                      nfs4       47G  2.3G   45G   5% /ans-upload
[root@m01 ~]#ansible web -m shell  -a 'grep upload /etc/fstab'
172.16.1.7 | CHANGED | rc=0 >>
172.16.1.31:/ans-data/ /ans-upload/ nfs defaults 0 0
[root@m01 ~]#

#5、卸载
[root@m01 ~]#ansible web -m mount -a 'path=/ans-upload/ state=absent'

8)template模块

9)unarchive模块

解压

7、playbook剧本

1、概述

playbook:剧本,用于长久保存并且实现批量管理、维护、部署的文件,类似于脚本存放命令和变量。剧本中存放的是模块。

剧本格式为yaml,yaml格式的文件:空格,冒号,不能使用Tab键

对比点ans剧本ans ad-hoc(命令行)
共同点批量管理,使用模块批量管理,使用模块
区别重复调用不是很方面,不容易重复使用
应用场景部署服务,多个步骤的任务测试模块,临时性任务

2、剧本格式

剧本的格式,书写要对齐,后缀名:yaml、yml,例如:

---           #3个-,表示剧本的开始,也可以省略不写
- hosts: all  #角色play,指定角色,即指定要批量执行指令的机器,all表示所有机器
  tasks:                #任务列表
    - name: 01 打开冰箱门     #具体的步骤,name:步骤名称
       shell: echo 01 >> /tmp/bingxiang.log   #shell:shell模块
    - name: 02 把大象装进冰箱
       shell: echo 02 >> /tmp/bingxiang.log
    - name: 03 关上冰箱门
       shell: echo 03 >> >> /tmp/bingxiang.log

角色play:指定你要管理的主机(主机清单里面的)

任务tasks:具体要执行的模块(根根据你的步骤转换)

模块(根据你的操作流程、步骤选择模块)

windows与Linux的回车换行的区别:

windows回车换行:\r\n
Linux回车换行:\n

因此,不建议在在Windows写好脚本,再传到Linux,因为两者的回车换行不一样,可能会报错。

如果在Windows写的脚本传到Linux后,可以使用”dos2unix 脚本“命令进行格式转换

案例:写一个剧本(放在/server/playbook/目录)

要注意格式对齐

[root@m01 ~]#mkdir /server/playbook
[root@m01 ~]#cd /server/playbooks/
[root@m01 /server/playbooks]#vim 01.test.yaml
---
-  hosts: all
   tasks:
     - name: 01.打开冰箱门
       shell: echo 01.open >> /tmp/ele.txt
     - name: 02.把大象放进冰箱
       shell: echo 02.put >> /tmp/ele.txt
     - name: 03.关闭冰箱门
       shell: echo 03.close >> /tmp/ele.txt

执行剧本:

#把主机清单文件和剧本放在同一个目录,因此复制主机清单到/server/playbooks/目录
[root@m01 /server/playbooks]#cp /etc/ansible/hosts .
[root@m01 /server/playbooks]#ls
01.test.yaml  hosts
[root@m01 /server/playbooks]#
#执行剧本
[root@m01 /server/playbooks]#ansible-playbook -i hosts 01.test.yaml 

PLAY [all] ******************************

TASK [Gathering Facts] ***************************
ok: [172.16.1.7]
ok: [172.16.1.41]
ok: [172.16.1.31]

TASK [01.打开冰箱门] **********************
changed: [172.16.1.31]
changed: [172.16.1.7]
changed: [172.16.1.41]

TASK [02.把大象放进冰箱] ************************
changed: [172.16.1.41]
changed: [172.16.1.7]
changed: [172.16.1.31]

TASK [03.关闭冰箱门] ********************
changed: [172.16.1.7]
changed: [172.16.1.41]
changed: [172.16.1.31]

PLAY RECAP ****************
172.16.1.31                : ok=4    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
172.16.1.41                : ok=4    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
172.16.1.7                 : ok=4    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@m01 /server/playbooks]#
#查看结果
[root@m01 /server/playbooks]#ansible all -a 'cat /tmp/ele.txt'
#或者
[root@m01 /server/playbooks]#ansible -i hosts all -a 'cat /tmp/ele.txt'
# -i:指定主机清单文件,all:表示所有机子

执行脚本命令格式:

ansible-playbook -i 主机清单文件  剧本文件
#如果不指定主机清单文件,默认执行/etc/ansible/hosts主机清单文件

检查剧本:

ansible-play -C 剧本

3、书写剧本的注意事项

注意事项:

1、剧本中对齐的空格必须是2的倍数:2、4、6...
2、不能使用Tab键
3、有多个参数的话,每个参数一行

例如:

4、模块转换剧本案例

安装cowsay

[root@m01 /server/playbooks]#yum install -y cowsay

案例01:创建目录并分发文件

1、创建目录/server/files/
2、/etc/hosts文件发送到/server/files/

中间转换步骤:任务的步骤—>模块(命令行)

1、创建目录/server/files/
ansible all -m file -a 'path=/server/files/ state=directory'
2、分发
ansible all -m copy -a 'src=/etc/hosts dest=/server/files'

书写成剧本:

[root@m01 /server/playbooks]#vim 02.dir_file.yml
---
- hosts: all
  tasks:
    - name: 01 创建目录
      file: path=/server/files/ state=directory
    - name: 02 分发文件
      copy: src=/etc/hosts dest=/server/files/

专业格式的剧本:

参数有多个,每个一行

---
- hosts: all
  tasks:
    - name: 01 创建目录
      file: 
        path: /server/files/
        state: directory
    - name: 02 分发文件
      copy:
        src: /etc/hosts
        dest: /server/files/

如图较为直观:

安装cowsay后,执行剧本效果如下:

[root@m01 /server/playbooks]#ansible-playbook 02.dir_file.yml 
 ____________
< PLAY [all] >
 ------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
案例02:nfsf服务

nfs服务端:ip:192.168.10.31、172.16.1.31,主机名:nfs01

nfs客户端:ip:192.168.10.7、172.16.1.7,主机名:web01

需求:

1、在nfs服务端nfs上部署nfs服务,共享/backup-nfs/目录,all_squash,匿名用户:nfsnobody
2、在nfs客户端web01挂载/ans-upload/目录挂载nfs服务端共享的/backup-nfs/(永久挂载)

服务端部署流程:

1、安装nfs-utils、rpcbind
2、修改/etc/exports配置文件
3、创建共享目录/backup-nfs/,修改用户和用户组
4、先启动rpcbind服务,再启动nfs服务

客户端部署流程

1、安装nfs-utils
2、挂载与永久挂载

剧本书写:

[root@m01 /server/playbooks]#cat 03.nfs_all_in_one.yml
---
- hosts: nfs
  tasks:
    - name: 01 安装nfs、rpcbind
      yum:
         name: nfs-utils,rpcbind
         state: present
    - name: 02 配置nfs /etc/exports
      lineinfile:
         path: /etc/exports
         line: "/backup-nfs/ 172.16.1.0/24(rw,all_squash)"
    - name: 03 创建共享目录并修改所有者
      file:
        path: /backup-nfs/
        owner: nfsnobody
        group: nfsnobody
        state: directory
    - name: 04 启动(重启)rpc服务
      systemd:
        name: rpcbind
        enabled: yes
        state: restarted
    - name: 05 启动(重启)nfs服务
      systemd:
        name: nfs
        enabled: yes
        state: restarted
- hosts: web
  tasks:
    - name: 01 安装nfs
      yum:
        name: nfs-utils
        state: present
    - name: 02 创建挂载点
      file:
        path: /ans-upload/
        state: directory
    - name: 03 挂载与永久挂载
      mount:
        src: 172.16.1.31:/backup-nfs/
        path: /ans-upload/
        fstype: nfs
        state: mounted
[root@m01 /server/playbooks]#

执行剧本并查看结果:

[root@m01 /server/playbooks]#ansible-playbook 03.nfs_all_in_one.yml
#查看结果:
[root@m01 /server/playbooks]#ansible web -m shell -a "df -Th | grep nfs"
172.16.1.7 | CHANGED | rc=0 >>
172.16.1.31:/backup-nfs                    nfs4       47G  2.3G   45G   5% /ans-upload
[root@m01 /server/playbooks]#

OK,挂载成功。再查看/etc/fstab是否永久挂载:

[root@m01 /server/playbooks]#ansible web -m shell -a "grep nfs /etc/fstab"
172.16.1.7 | CHANGED | rc=0 >>
172.16.1.31:/backup-nfs/ /ans-upload/ nfs defaults 0 0
[root@m01 /server/playbooks]#

8、ansible变量

可以定义变量的地方说明
在剧本文件中定义比较常用,仅仅限于当前的play使用(即:1个剧本只有1个hosts)
register变量(注册变量)ip=hostname -I 实现脚本中反引号的功能,可以获取命令结果
变量文件,group_vars自动读取group_vars/all/vars.yml内容。未来用于大型剧本
变量文件,剧本中通过vars_files调用同1个剧本中多个play使用
inbentory主机清单中定义变量未来可以用于批量修改主机使用,其他场景很少用
命令行中几乎不用
facts变量ansible内置变量。一般用于获取主机基本信息,如:ip、主机名、系统版本 如果不需要,可以关闭,用于加速剧本的执行
变量定义

1、剧本中使用变量

例如:

[root@m01 ~]#vim /server/playbooks/04.vars.yml
---
- hosts: all
  vars:
    dir: /oldboy/lidao/upload/
    file: 1.txt
  tasks:
    - name: 1. mkdir
      file:
        path: "{{ dir }}"
        state: directory
    - name: 2. touch file
      file:
        path: "{{ dir }}{{ file }}" 
        state: touch

说明:

本剧本使用变量创建/oldboy/lidao/upload/目录
变量的定义格式:
   vars: 
      变量名称:  变量的值(变量的内容)
变量的引用(使用): "{{ 变量名称 }}"

温馨提示:

使用变量的时候,如果变量是某个选项的开头,则变量引用的时候要加上“”双引号。例如:
file:
  path: "{{ dir }}" #选项以变量开头,变量需要使用""双引号
file:
  path: "{{ dir }}{{ file }}"   # 注意:不能写成:"{{ dir }}"{{ file }}
file:
  path: /oldboy-new/{{ dir }}  #这种不是以变量开头,可以不使用“”双引号

在剧本play定义变量的应用:

1、仅仅在当前play中生效
2、一般用来存放路径、用户名、ip地址等,类似于之前使用的脚本
3、注意引号的使用

Anaible选项:

anbile-play   -i  主机清单  : -i,用于指定主机清单文件
anbile-play   -C  剧本     : -C,测试剧本,模拟运行剧本,并不真的运行

2、共用变量-vars_files

变量文件:把变量放在一个文件中

例如:

#1、创一个存放变量的文件,如:vars.yml
[root@m01 /server/playbooks]#vim vars.yml
dir: /tmp/
file: 1.txt
user: test
#2、在剧本中使用变量文件
[root@m01 /server/playbooks]#vim 05.vars.yml
---
- hosts: all
  vars_files: ./vars.yml   #当前目录下的vars.yml文件
  tasks:
    - name: "touch file"
      file:
        path: "{{ dir }}{{ user }}-{{ file }}"
        state: touch

变量文件的引用格式:

vars_files: 变量文件

3、共用变量-group_vars

根据主机组使用变量,主机不同,则使用的变量不同

group_vars:根据主机清单的分组去匹配

把变量文件放在:group_vars/主机分组/变量文件

主机组创建变量文件

#1、创建/server/playbooks/group_vars/all/目录
#存放all主机(即所有主机)的变量文件
[root@m01 /server/playbooks]#pwd
/server/playbooks
[root@m01 /server/playbooks]#mkdir -p group_vars/all/

#2、创建针对all的vars_files.yml文件
[root@m01 /server/playbooks]#vim group_vars/all/vars_files.yml
dir: /tmp/
file: 1.txt
user: test

#3、创建剧本
[root@m01 /server/playbooks]#vim 06.ans_group_vars.yml
---
- hosts: all
  tasks:
    - name: "touch file"
      file:
        path: "{{ dir }}{{ user }}-{{ file }}"
        state: touch

#4、执行剧本
[root@m01 /server/playbooks]#ansible-playbook 06.ans_group_vars.yml
#执行的时候,会根据hosts的all,去找到group_vars/目录下与all同名的目录,再找到变量文件
#如果hosts定义的是web组,则去group_vars/目录下找到web目录,再找到变量文件;如果没有web对应的变量文件,则也会匹配all的变量文件
#这就是根据不同的主机分组去匹配对应的变量文件,进行变量引用

4、facts变量

1、ansible内置变量,ansible运行剧本的时候会有一个默认的task(Gathering Facts),这个背后在收集每个主机的基本信息(比如:ip、主机名、磁盘等)

这些基本信息就称为facts变量。

2、facts变量setup模块获取

setup模块查看facts变量:

[root@m01 /server/playbooks]#ansible 172.16.1.31 -m setup
172.16.1.31 | SUCCESS => {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "192.168.10.31", 
            "172.16.1.31"
        ], 
        "ansible_all_ipv6_addresses": [
            "fe80::b6ba:9eee:233d:247c", 
            "fe80::c4:d3b6:909c:b3c3", 
            "fe80::a9b4:ac3b:9de1:669", 
            "fe80::c0e5:5bd4:f432:480c", 
            "fe80::5eff:ab98:b0b4:8989", 
            "fe80::a333:6b2e:5151:5a96"
        ], 
        "ansible_apparmor": {
            "status": "disabled"
        }, 
        "ansible_architecture": "x86_64", 
        "ansible_bios_date": "07/29/2019", 
        "ansible_bios_version": "6.00", 
        "ansible_cmdline": {
            "BOOT_IMAGE": "/vmlinuz-3.10.0-1160.el7.x86_64",
.......

结果以JSON格式显示

常用facts变量:

ansible_hostname       #主机名
ansible_memtotal_mb    #内存大小(总计),单位:mb
ansible_processor_vcpus  #cpu数量
ansible_default_ipv4.address  #默认的网卡ip
ansible_distribution    #系统发行版本名字,如,CentOS、Ubuntu等
ansible_distribution_version  #系统的版本
ansible_date_time.date      #时间

案例01:取出变量

[root@m01 /server/playbooks]#vim 07.facts_vars.yml
- hosts: all
  tasks:
    - name: 调试变量
      debug:
         msg: |
              "主机名:{{ ansible_hostname }}"
              "内存大小:{{ ansible_memtotal_mb }}"
    - name: 保存到文件
      lineinfile:
        path: /tmp/info.txt
        create: yes
        line: |
              "主机名:{{ ansible_hostname }}"
              "内存大小:{{ ansible_memtotal_mb }}"

案例02:多层变量取出

例如:

 "ansible_default_ipv4": {
            "address": "192.168.10.31", 
            "alias": "eth0", 
            "broadcast": "192.168.10.255", 
            "gateway": "192.168.10.2", 
            "interface": "eth0", 
            "macaddress": "00:0c:29:32:73:56", 
            "mtu": 1500, 
            "netmask": "255.255.255.0", 
            "network": "192.168.10.0", 
            "type": "ether"
        }, 

ansible_default_ipv4的变量内容用{}分层了好多行(多层),怎么取出某一层的内容,比如:取出address。

[root@m01 /server/playbooks]#vim 08.facts_vars_duo_level.yml
- hosts: all
  tasks:
    - name: 调试变量
      debug:
         msg: |
              "ip地址:{{ ansible_default_ipv4.address }}"

上述方法,只适用于{},对于[]不使用。

案例03:批量修改/etc/motd文件。登录系统的时候输出系统的基本信息(主机名、内存大小、ip、发行版本、cpu数、核心数)

文件中有ansible的变量,则文件后缀名为.j2,否则在剧本中使用template模块将无法识别

#1、创建包含变量的模版文件(motd.j2)
[root@m01 /server/playbooks]#vim motd.j2
#############################
Welcome to xzm elastic linux system
操作需谨慎,删根弹指间
主机名:{{ ansible_hostname }}
ip地址: {{ ansible_default_ipv4.address }}
内存大小: {{ ansible_memtotal_mb }}
CPU数量: {{ ansible_processor_vcpus }}
核心总数: {{ ansible_processor_cores }}
发行版本: {{ ansible_distribution }}

#2、发送模版文件替代/etc/motd文件即可
[root@m01 /server/playbooks]#vim 09.change_motd.yml
- hosts: all
  tasks:
    - name: copy分发motd.j2文件
      copy:
        src: ./motd.j2
        dest: /tmp/cp-motd
        backup: yes
    - name: template分发motd.j2文件
      template:
        src: ./motd.j2
        dest: /opt/tm-motd
        backup: yes

执行剧本后,查看copy模块和template分发文件的区别:

[root@nfs01 ~]#cat /tmp/cp-motd 
#############################
Welcome to xzm elastic linux system
操作需谨慎,删根弹指间
主机名:{{ ansible_hostname }}
ip地址: {{ ansible_default_ipv4.address }}
内存大小: {{ ansible_memtotal_mb }}
CPU数量: {{ ansible_processor_vcpus }}
核心总数: {{ ansible_processor_cores }}
发行版本: {{ ansible_distribution }}

[root@nfs01 ~]#cat /opt/tm-motd 
#############################
Welcome to xzm elastic linux system
操作需谨慎,删根弹指间
主机名:nfs01
ip地址: 192.168.10.31
内存大小: 1980
CPU数量: 1
核心总数: 1
发行版本: CentOS
[root@nfs01 ~]#

区别:

copy模块分发文件,文件中的变量不会被解析,保持不变
template模块分发文件(文件后缀名:.j2),文件中的变量被解析

关闭facts变量:

在剧本中的hosts和tasks之间添加“gather_facts: no”即可,比如:

---
- hosts: all
  gather_facts: no   #也可以写成false
  tasks:
     - name: 01.打开冰箱门
       shell: echo 01.open >> /tmp/ele.txt
     - name: 02.把大象放进冰箱
       shell: echo 02.put >> /tmp/ele.txt
     - name: 03.关闭冰箱门
       shell: echo 03.close >> /tmp/ele.txt

5、register变量注册

本质上就是用来实现脚本中的反引号功能,比如:ip=hostname -I

用户通过命令获取的内容都存放到Register变量中

例如:

[root@m01 /server/playbooks]#vim 10.ans_register.yml
- hosts: all
  gather_facts: no
  tasks:
    - name: 执行命令
      shell: date +%F
      register: result  #把命令执行的结果,保存在register变量(result)中
    - name: 输出register变量
      debug:
        msg: "{{ result }}"

执行剧本:

[root@m01 /server/playbooks]#ansible-playbook  10.ans_register.yml 
.....
TASK [输出register变量] *******************
ok: [172.16.1.7] => {
    "msg": {
        "ansible_facts": {
            "discovered_interpreter_python": "/usr/bin/python"
        }, 
        "changed": true, 
        "cmd": "date +%F", 
        "delta": "0:00:00.008533", 
        "end": "2023-06-16 23:53:34.861539", 
        "failed": false, 
        "rc": 0, 
        "start": "2023-06-16 23:53:34.853006", 
        "stderr": "", 
        "stderr_lines": [], 
        "stdout": "2023-06-16", 
        "stdout_lines": [
            "2023-06-16"
        ]
    }
}

从输出结果,可以看到result内容为:

{
        "ansible_facts": {
            "discovered_interpreter_python": "/usr/bin/python"
        }, 
        "changed": true, 
        "cmd": "date +%F", 
        "delta": "0:00:00.008533", 
        "end": "2023-06-16 23:53:34.861539", 
        "failed": false, 
        "rc": 0, 
        "start": "2023-06-16 23:53:34.853006", 
        "stderr": "", 
        "stderr_lines": [], 
        "stdout": "2023-06-16", 
        "stdout_lines": [
            "2023-06-16"
        ]
    }

但是,剧本中,result变量的内容是执行date +%F命令的结果,根据结果,应该是stdout的内容。那怎么办?

result.stdout即可,因此,剧本改成:

- hosts: all
  gather_facts: no
  tasks:
    - name: 执行命令
      shell: date +%F
      register: result  #把命令执行的结果,保存在register变量(result)中
    - name: 输出register变量
      debug:
        msg:  "{{ result.stdout }}"

再执行即可。

register注册变量:

变量名.stdout  即可获取变量的内容

stdout:standard  output,标准输出
stderr:标准错误输出

json数据格式:

key:value  即:键:值(变量:变量值)

符号说明:

msg中的|表示下面的内容是多行的,|也可以用于其他模块中

9、流程控制

涉及判断、循环流程

循环:loop、with_items

判断:when

触发器:handler

9.1)循环

循环分2类:with_*系列:with_items和loop

with_*系列:with_items、with_list、with_together….

批量创建文件、添加用户、批量启动或重启服务的时候,需要通过循环实现

案例01:批量启动服务rpcbind,然后启动nfs服务

使用with_items:

- hosts: nfs
  gather_facts: false
  tasks:
    - name: 重启服务
      systemd:
        name: "{{ item }}"  #变量名,一般就是item
        state: restarted
      with_items:
        - rpcbind
        - nfs

使用loop:

- hosts: nfs
  gather_facts: false
  tasks:
    - name: 重启服务
      systemd:
        name: "{{ item }}"  #变量名,一般就是item
        state: restarted
      loop:
        - rpcbind
        - nfs

loop与with_items几乎一样

案例02:多变量与循环

批量添加用户

- hosts: all
  gather_facts: no
  tasks:
    - name: add user
      user:
        name: "{{ item.name }}"
        uid:  "{{ item.uid }}"
        state: present
      loop:
          # key:value
        - { name: 'test01',uid: 2020 }
        - { name: 'xzm',uid: 2021 }

9.2)判断(when)

when判断:用于给ans运行的tasks(模块)设置条件,满足或不满足条件再运行对应的模块

应用建议:when进行判断,一般与变量一起使用

when条件一般与facts变量或register变量一起使用

需求:

只想在web01(172.16.1.7)上执行安装软件的操作。但是web主机分组如下:
web
172.16.1.7
172.16.1.8

剧本如下:

- hosts: all
  tasks:
    - name: 只输出web01信息
      debug:
        msg: "这是web01,正在安装软件...."
      when:
        ansible_hostname == "web01"   #只是演示when,并没有写真正安装软件的代码

when中使用符号

when判断中使用的符号说明
==等于
!=不等于
is match(“正则”)例如:ansible_hostname is match(“web|backup”) 类似于grep,正则
is not match(“正则”)例如:ansible_hostname is not match(“web|backup”) 取反、排除
>大于
>=大于等于
<小于
<=小于等于
when符号

案例01:如果系统是centos,则安装sl、cowsay;如果是Ubuntu,则安装cmatrix

[root@m01 /server/playbooks]#vim 16.when-sys.yml
- hosts: all
  tasks:
    - name: CentOS 安装 sl,cowsay
      yum:
        name: sl,cowsay
        state: present
      when: ansible_distribution == 'CentOS'
    - name: Ubuntu 安装 cmatrix
      apt:
        name: cmatrix
        state: present
      when: ansible_distribution == 'Ubuntu'
    - name: Kylin 安装 tree
      yum:
        name: tree
        state: present
      when: ansible_distribution is match('Kylin')

执行剧本,Kylin系统提示没有安装python2-dnf

fatal: [192.168.10.130]: FAILED! => {"changed": false, "msg": "`python2-dnf` is not installed, but it is requiredfor the Ansible dnf module.", "results": []}

python2-dnf无法通过yum安装,需配置[extras]源:

[extras]
name=CentOS-$releasever - Extras - mirrors.aliyun.com
failovermethod=priority
baseurl=http://mirrors.aliyun.com/centos/7/extras/$basearch/
        http://mirrors.aliyuncs.com/centos/7/extras/$basearch/
        http://mirrors.cloud.aliyuncs.com/centos/7/extras/$basearch/
gpgcheck=1
gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7

这里用阿里云的centos7的源,因此,$releasever要改成7,不改的话,会获取kylin系统的版本而kylin版本10,所以会报错。由于kylin系统已经安装了python3-dnf,所以不能再安装python2-dnf

温馨提示:Ubuntu默认禁止root远程登录

9.3)handler触发器

需求:分发nfs服务配置文件,然后重启nfs

应用场景:一般用于分发配置文件的时候

如果配置文件发生变化,则重启服务;如果配置文件没有变化,则不重启,此时就需要使用触发器了。

案例01:没有使用触发器

[root@m01 /server/playbooks]#vim 14.fenfa_nfs.yml
- hosts: nfs
  gather_facts: false
  tasks:
    - name: 1.分发配置文件
      copy:
        src: ./exports
        dest: /etc/exports
        backup: yes
    - name: 2.重启nfs服务
      systemd:
        name: nfs
        state: restarted

使用handler触发器(notify-handlers):

- hosts: nfs
  gather_facts: false
  tasks:
    - name: 1.分发配置文件
      copy:
        src: ./exports
        dest: /etc/exports
        backup: yes
      notify:
        - 2.重启nfs服务
  handlers:
    - name: 2.重启nfs服务
      systemd:
        name: nfs
        state: restarted

10、剧本调试

1)检查语法与单步执行

-C:--check,模拟运行,不做出改变,一些变量可能会提示错误,因为没有真正运行剧本
--syntax-check:只做语法检查,不运行
高级的调试:
  --step:单步运行,y:执行这个tasks,n:忽略这个tasks, c:自动运行

例如:

–syntax-check:

[root@m01 /server/playbooks]#ansible-playbook --syntax-check 14.fenfa_nfs.yml 

–step:

[root@m01 /server/playbooks]#ansible-playbook --step 14.fenfa_nfs.yml
Perform task: TASK: 1.分发配置文件 (N)o/(y)es/(c)ontinue:   #输入y、n或c

2)tag标签

给ansible中的tasks标记标签,运行剧本的时候可以运行指定的tag标签

运行剧本的时候:

-t 运行的标签,如果有多个,通过逗号(,)分隔
--skip-tags 排除指定的tags,如果有多个,通过逗号(,)分隔
--start-at-task:从指定的任务开始运行到剧本结尾

显示剧本的标签:

--list-tags:显示剧本中所有的tags标签

例如:

[root@m01 /server/playbooks]#vim 18.nfs_all_in_one_tags.yml
---
- hosts: nfs
  tasks:
    - name: 01 安装nfs、rpcbind
      yum:
         name: nfs-utils,rpcbind
         state: present
      tags:                         #定义标签
        - install_nfs               #标签名为:install_nfs
    - name: 02 配置nfs /etc/exports
      lineinfile:
         path: /etc/exports
         line: "/backup-nfs/ 172.16.1.0/24(rw,all_squash)"
      tags:
        - config_nfs_server        #同一个tasks中,可以定义多个标签
        - nfs_server_config_file
    - name: 03 创建共享目录并修改所有者
      file:
        path: /backup-nfs/
        owner: nfsnobody
        group: nfsnobody
        state: directory
      tags:
        - config_nfs_server
        - nfs_server_dir
    - name: 04 启动(重启)rpc服务
      systemd:
        name: rpcbind
        enabled: yes
        state: restarted
      tags:
        - restart_rpc
    - name: 05 启动(重启)nfs服务
      systemd:
        name: nfs
        enabled: yes
        state: restarted
- hosts: web
  tasks:
    - name: 01 安装nfs
      yum:
        name: nfs-utils
        state: present
    - name: 02 创建挂载点
      file:
        path: /ans-upload/
        state: directory
    - name: 03 挂载与永久挂载
      mount:
        src: 172.16.1.31:/backup-nfs/
        path: /ans-upload/
        fstype: nfs
        state: mounted

查看标签(–list-tags):

[root@m01 /server/playbooks]#ansible-playbook --list-tags -C 18.nfs_all_in_one_tags.yml 

playbook: 18.nfs_all_in_one_tags.yml

  play #1 (nfs): nfs	TAGS: []
      TASK TAGS: [config_nfs_server, install_nfs, nfs_server_config_file, nfs_server_dir, restart_rpc]

  play #2 (web): web	TAGS: []
      TASK TAGS: []
[root@m01 /server/playbooks]#

执行某个标签(-t):

[root@m01 /server/playbooks]#ansible-playbook -t config_nfs_server,install_nfs  -C 18.nfs_all_in_one_tags.yml 

–start-at-task:

–start-at-task 任务:从指定的任务开始执行,注意,不是标签

[root@m01 /server/playbooks]#ansible-playbook --start-at-task "02 创建挂载点"  -C 18.nfs_all_in_one_tags.yml

3)忽略错误

运行剧本的时候,由于重复运行导致的报错,并未真的错误,比如:目录已存在、用户已存在

这种情况下,可以通过ignore_errors忽略错误,让剧本继续运行

例如:

[root@m01 /server/playbooks]#vim 20.useradd_ignore.yml
- hosts: nfs
  gather_facts: no
  tasks:
   - name: useradd
     user:
      name: test
      state: present
     ignore_errors: true     #true(yes)、false(no)

11、Jinja2模版

template模版与xxx.j2文件

应用场景:

1、进行分发配置文件或文件的时候,需要在文件中使用变量,需要使用jinja2文件(.j2),就需要使用template模块进行分发
2、进行判断
3、进行循环

1)基本使用

案例:分发motd文件,文件中包含ans变量,目标文件是/tmp/motd

[root@m01 /server/playbooks]#cat 09.change_motd.yml 
- hosts: all
  tasks:
    - name: copy分发motd.j2文件
      copy:
        src: ./motd.j2
        dest: /tmp/cp-motd
        backup: yes
    - name: template分发motd.j2文件
      template:
        src: ./motd.j2
        dest: /opt/tm-motd
        backup: yes
[root@m01 /server/playbooks]#

测试用的motd.j2文件:

[root@m01 /server/playbooks]#cat motd.j2 
#############################
Welcome to xzm elastic linux system
操作需谨慎,删根弹指间
主机名:{{ ansible_hostname }}
ip地址: {{ ansible_default_ipv4.address }}
内存大小: {{ ansible_memtotal_mb }}
CPU数量: {{ ansible_processor_vcpus }}
核心总数: {{ ansible_processor_cores }}
发行版本: {{ ansible_distribution }}

[root@m01 /server/playbooks]#

2)判断使用

背景:未来某一个服务运行在主、备两台机器上,虽然配置文件大部分是一样的(主:master,备:backup),但是怎么根据主、备的不同,生成不同的配置文件?

应用场景:根据主机名或ip或其他条件,生成不同的配置文件

例如:有个配置文件叫keepalived.conf,根据不同的主机有所区别

/tmp/keepalived.conf
wb01的配置文件内容:
state  MASTER
backup的配置文件内容:
state  BACKUP

剧本实现:

#剧本
[root@m01 /server/playbooks]#vim 17.ans_jinja2_if.yml
- hosts: all
  tasks:
    - name: 分发keepalived配置文件
      template:
        src: ./keepalived.conf.j2
        dest: /tmp/keepalived.conf

#模版文件:keepalived.conf.j2
[root@m01 /server/playbooks]#vim keepalived.conf.j2
# keepalived.conf
{%  if ansible_hostname == "web01" %}
state MASTER

{% elif ansible_hostname == "backup"%}
state BACKUP
{% endif %}

执行剧本:

[root@m01 /server/playbooks]#ansible-playbook 17.ans_jinja2_if.yml

查看:

[root@m01 /server/playbooks]#ansible all -a "cat /tmp/keepalived.conf"
172.16.1.7 | CHANGED | rc=0 >>  #文件有内容
# keepalived.conf
state MASTER
172.16.1.31 | CHANGED | rc=0 >> 
# keepalived.conf
172.16.1.41 | CHANGED | rc=0 >>  #文件有内容
# keepalived.conf
state BACKUP
192.168.10.131 | CHANGED | rc=0 >>
192.168.10.130 | CHANGED | rc=0 >>
# keepalived.conf
[root@m01 /server/playbooks]#

结果显示,只有web01、backup主机的文件才有内容

3)循环

循环格式举例:

格式1:
{%  for ip in [1,3,5,7,9] %}
   192.168.10.{{ ip }}
{% endfor %}

格式2:
#range(2,11),生成序列,从2开始到10,不包括11
{%  for ip in range(2,11) %}
   192.168.10.{{ ip }}
{% endfor %}

格式3:
{%  for ip in ["web01","backup"] %}
   192.168.10.{{ ip }}
{% endfor %}

剧本:

#1、创建将j2文件
[root@m01 /server/playbooks]#vim server.conf.j2
{%  for ip in range(2,11) %}
   192.168.10.{{ ip }}
{% endfor %}

#2、剧本
[root@m01 /server/playbooks]#vim 17.ans_jinja2_for.yml
- hosts: all
  tasks:
    - name: fenfa server.conf
      template:
        src: ./server.conf.j2
        dest: /tmp/server.conf
#3、执行剧本
[root@m01 /server/playbooks]#ansible-playbook 17.ans_jinja2_for.yml

#4、查看结果:
[root@m01 /server/playbooks]#ansible all -a "cat /tmp/server.conf"
172.16.1.7 | CHANGED | rc=0 >>
   192.168.10.2
   192.168.10.3
   192.168.10.4
   192.168.10.5
   192.168.10.6
   192.168.10.7
   192.168.10.8
   192.168.10.9
   192.168.10.10

12、ansible-进阶

1)Roles

随着书写的剧本越来越复杂,我们发现,剧本使用的文件、配置文件、j2文件等等都放在与剧本本身同一层目录中,这样会有点乱,不方便后期维护。

roles:本质让我们书写剧本的时候有个目录规范
把完整的剧本拆分成若干部分,有的专门存放tasks部分(剧本)、存放变量、存放普通文件、存放j2文件、handler触发器...

举例:创建/server/roles/目录

[root@m01 /server/playbooks]#mkdir -p /server/roles

#复制/etc/ansible/hosts到/server/roles目录
[root@m01 /server/playbooks]#cp /etc/ansible/hosts /server/roles/

#进入/server/roles/目录
[root@m01 /server/playbooks]#cd /server/roles/

创建符合roles规则的目录:

#命令格式:ansible-galaxy init  目录名
[root@m01 /server/roles]#ansible-galaxy init nfs-server
- Role nfs-server was created successfully

#相关目录解释
[root@m01 /server/roles]#tree -F
.
├── hosts
|---group_vars/all/vars.yml  #这个需要自行创建,默认不存在
└── nfs-server/
    ├── defaults/
    │   └── main.yml
    ├── files/            #存放普通文件、压缩包、配置文件等,通过copy模块读取
    ├── handlers/
    │   └── main.yml      #剧本中handler部分
    ├── meta/
    │   └── main.yml
    ├── README.md
    ├── tasks/             
    │   └── main.yml     #存放剧本中的tasks部分
    ├── templates/       #存放.j2文件,通过template调用
    ├── tests/
    │   ├── inventory
    │   └── test.yml
    └── vars/
        └── main.yml

9 directories, 9 files
[root@m01 /server/roles]#

也可以通过mkdir手工创建相关目录

2)实战

剧本文件如下:

---
- hosts: nfs
  tasks:
    - name: 01 安装nfs、rpcbind
      yum:
         name: nfs-utils,rpcbind
         state: present
      tags:
        - install_nfs
    - name: 02 配置nfs /etc/exports
      lineinfile:
         path: /etc/exports
         line: "/backup-nfs/ 172.16.1.0/24(rw,all_squash)"
      notify:
        - "05 启动(重启)nfs服务"
      tags:
        - config_nfs_server
        - nfs_server_config_file
    - name: 03 创建共享目录并修改所有者
      file:
        path: /backup-nfs/
        owner: nfsnobody
        group: nfsnobody
        state: directory
      tags:
        - config_nfs_server
        - nfs_server_dir
    - name: 04 启动(重启)rpc服务
      systemd:
        name: rpcbind
        enabled: yes
        state: restarted
      tags:
        - restart_rpc
  handlers:
    - name: 05 启动(重启)nfs服务
      systemd:
        name: nfs
        enabled: yes
        state: restarted
- hosts: web
  tasks:
    - name: 01 安装nfs
      yum:
        name: nfs-utils
        state: present
    - name: 02 创建挂载点
      file:
        path: /ans-upload/
        state: directory
    - name: 03 挂载与永久挂载
      mount:
        src: 172.16.1.31:/backup-nfs/
        path: /ans-upload/
        fstype: nfs
        state: mounted

使用roles进行拆分:这里演示nfs-server部分

第1步:tasks部分

[root@m01 /server/roles]#vim nfs-server/tasks/main.yml
---
# tasks file for nfs-server
    - name: 01 安装nfs、rpcbind
      yum:
         name: nfs-utils,rpcbind
         state: present
      tags:
        - install_nfs
    - name: 02 配置nfs /etc/exports
      template:                          #这里使用template
         src: exports.j2
         backup: yes
         dest: /etc/exports
      tags:
        - config_nfs_server
        - nfs_server_config_file
    - name: 03 创建共享目录并修改所有者
      file:
        path: /backup-nfs/
        owner: nfsnobody
        group: nfsnobody
        state: directory
      tags:
        - config_nfs_server
        - nfs_server_dir
    - name: 04 启动(重启)rpc服务
      systemd:
        name: rpcbind
        enabled: yes
        state: restarted
      tags:
        - restart_rpc
    - name: 05 启动(重启)nfs服务
      systemd:
        name: nfs
        enabled: yes
        state: restarted

创建/server/roles/nfs-server/templates/exports.j2文件

[root@m01 /server/roles]#vim nfs-server/templates/exports.j2
/ans-data/   172.16.1.0/24(rw)

第2步:handlers部分

[root@m01 /server/roles]#vim nfs-server/handlers/main.yml
---
# handlers file for nfs-server
    - name: 05 启动(重启)nfs服务
      systemd:
        name: nfs
        enabled: yes
        state: restarted

第3步:group_vars,如果不使用的话,不用创建

创建group_vars

[root@m01 /server/roles]#mkdir -p group_vars/all/
[root@m01 /server/roles]#vim group_vars/all/vars.ym

第4步:创建运行剧本的入口文件

该文件要与 nfs-server目录在同一层目录中

[root@m01 /server/roles]#ls
group_vars  hosts  nfs-server
[root@m01 /server/roles]#vim top.yml
- hosts: nfs
  roles:
    - role: nfs-server  #目录的名称

第5步:执行剧本

[root@m01 /server/roles]#ansible-playbook -C top.yml  #只是测试

温馨提示:

top.yml文件可以写多剧本,例如:

- hosts: nfs
  roles:
    - role: nfs-server  #nfs的服务端
- hosts: web
  roles:
    - role: nfs-client  #nfs的客户端

最终使用到的文件及目录结构如下:

[root@m01 /server/roles]#tree -F
.
├── nfs-server/
│   ├── handlers/
│   │   └── main.yml
│   ├── tasks/
│   │   └── main.yml
│   ├── templates/
│   │   └── exports.j2
└── top.yml      #剧本的入口文件,跟nfs-server目录在同一层目录中

13、include

我们在写剧本的时候会涉及多个步骤,还会涉及到服务端和客户端,发现剧本越写越大,不容易进行分析与阅读。

把剧本拆分开,分成服务端和客户端2个文件。

这时候通过include_tasks的功能把多个剧本文件合并在一起,让剧本变成多个方便阅读与维护。

include_tasks使用格式:

- include_tasks:  xx.yml

1)roles使用include

以12.2的实战为例,把nfs-server/tasks/main.yml文件进行拆分成3部分(安装、配置、启动)

#1、安装nfs服务
[root@m01 /server/roles]#vim nfs-server/tasks/install.yml
- name: 1.安装nfs
  yum:
    name: nfs-utils,rpcbind
    state: present
  tags:
    - install_nfs_server
    
#2、配置
[root@m01 /server/roles]#vim  nfs-server/tasks/config.yml
- name: 02.配置nfs /etc/exports
  template:
     src: exports.j2
     dest: /etc/exports
  tags:
    - config_nfs_server
    - nfs_server_config_file
- name: 03.创建共享目录并修改所有者
  file:
    path: /backup-nfs/
    owner: nfsnobody
    group: nfsnobody
    state: directory
  tags:
    - config_nfs_server
    - nfs_server_dir
    
#3、启动
[root@m01 /server/roles]#vim  nfs-server/tasks/start.yml
- name: 04.启动rpcbind、nfs服务
  systemd:
    name: "{{ item }}"
    enabled: yes
    state: started
  loop:
    - rpcbind
    - nfs
    
#4、main.yaml文件修改
[root@m01 /server/roles]#mv nfs-server/tasks/main.yml{,.bak}
[root@m01 /server/roles]#vim nfs-server/tasks/main.yml
- include_tasks: install.yml
- include_tasks: config.yml
- include_tasks: start.yml

#5、剧本执行的入口文件
[root@m01 /server/roles]#vim top.yml
- hosts: nfs
  roles:
    - role: nfs-server

剧本执行:

[root@m01 /server/roles]#ansible-playbook top.yml

2)剧本使用include

include_tasks除了在roles使用,也可以直接在剧本中使用,示例:

---
- hosts: nfs
  tasks:
    - include_tasks: install.yml
    - include_tasks: config.yml
    - include_tasks: start.yml

详细的install.yml、config.yml、start.yml文件内容参照前面

include功能小结:

防止剧本过大,对剧本进行拆分

14、vault安全

加密指定的文件:ansible-vault,用于加密敏感信息。比如:加密hosts文件、变量文件。

格式:

#进行加密
ansible-vault encrypt 文件名
#使用已加密文件
ansible或ansible-play  --ask-vault-pass   文件
#彻底解密
ansible-vault decrypt 文件名

示例如下:

#1、进行加密
[root@m01 /server/roles]#ansible-vault encrypt hosts 
New Vault password:           #输入密码
Confirm New Vault password:   #输入密码
Encryption successful
[root@m01 /server/roles]#

#2、使用加密的文件
root@m01 /server/roles]#ansible -i hosts all --ask-vault-pass -m ping
Vault password:     #输入密码即可

#3、彻底解密
[root@m01 /server/roles]#ansible-vault decrypt hosts
Vault password: 
Decryption successful
[root@m01 /server/roles]#

15、Galaxy

别人的roles

官网:galaxy.ansible.com

例如:

#下载别人的剧本ginxinc.nginx_core
[root@m01 /server/roles]#ansible-galaxy collection install nginxinc.nginx_core

如图:

16、优化

1)性能

1、ssh连接速度优化,配置文件(sshd_config)关闭UseDNS、GSSAPIAuthcation
2、不要让ansible运行交互式的命令
3、需要使用ans、yum安装软件,可以自建本地yum仓库(自建yum源,自己制作rpm包)
4、调整ansible并发数量(-f,默认是5,ansible.cfg中forks=5,根据实际修改)
5、按anshible配置缓存(Redis),队列缓存facts
6、给主机进行分组操作与管理
7、如果不使用facts变量,则可以关闭(剧本中:gather_facts:false,配置文件中:gathering=explicit)
8、关闭host_key_check,一般使用密码认证的时候需要关闭,anshible配置文件:host_key_checking=False

2)安全

1、由于root权限太大,存在安全隐患,因此需要配置sudo
2、配置sudo用户:ans  ALL=(ALL) NOPASSWD:ALL,假设密码是:12345,ssh端口:22
3、配合vpn,jms使用
4、用户-->vpn-->jms(跳板机)-->ansible
5、用户密码进行加密(hash,ansible-vault)

3)sudo配置

ansible的sudo配置:

1、所有被管理节点上添加ans用户,密码统一
2、所有被管理节点配置ans用户的sudo权限:ans   ALL=(ALL)  NOPASSWD:ALL
3、分发密钥到ans用户
4、配置管理端ansible.cfg配置

1、配置ans用户sudo权限:

[root@m01 ~]#ansible all -m shell -a 'echo "ans ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers'

NOPASSWD:ALL:表示执行命令不需要输入密码

2、创建ans用户(密码:12345):

[root@m01 ~]#ansible all -m user -a "name=ans password={{ '12345' | password_hash('sha512','a123') }} state=present"

注意:如果是Ubuntu系统的话,要指定:shell=/bin/bash,不指定默认的话,是/bin/sh,centos默认的是/bin/bahs。

Ubuntu中,useradd添加的是虚拟用户。adduser添加的是普通用户

3、给ans用户批量免密认证:

[root@m01 ~]#vim  fenfa_pub_ans.sh
#!/bin/bash

#1.vars
#给这3台机子:172.16.1.31、172.16.1.41、172.16.1.7,密码均为:12345
ip1="31 41 7"
ip2="130 131" #192.168.10.130 192.168.10.131
user=ans
passwd=12345

#2、分发公钥
for ip in $ip1
do
   echo "ip addr:172.16.1.$ip"
   sshpass -p$passwd ssh-copy-id -o  StrictHostKeyChecking=no  $user@172.16.1.$ip
done
for ip in $ip2
do
   echo "ip addr:192.168.10.$ip"
   sshpass -p$passwd ssh-copy-id -o  StrictHostKeyChecking=no  $user@192.168.10.$ip
done
[root@m01 ~]#sh  fenfa_pub_ans.sh

4、管理端配置:

修改/etc/ansible/ansible.cfg 文件

#1、管理端
[root@m01 ~]#grep -Ev '^$|#' /etc/ansible/ansible.cfg 
[defaults]
sudo_user = ans ## 被管理端上有sudo权限的用户,nopasswd:ALL
remote_user = ans ##被管理端使用的用户,不指定的话,默认是root
remote_port = 22  ##被管理端ssh端口:22
host_key_checking = False  ##是否检查主机密钥
log_path = /var/log/ansible.log
[inventory
[privilege_escalation]
become=True             ##开启sudo功能
become_method = sudo    ###使用sudo命令
become_user=root        ##普通用户切换为root,
                        ##设置为具有所需特权的用户-您想要成为的用户,
                        ##而不是您登录时使用的用户,默认root
[paramiko_connection]
[ssh_connection]
[persistent_connection]
[accelerate]
[selinux]
[colors]
[diff]
[root@m01 ~]#

配置好之后即可以ans用户运行ansible