集群自动化维护-必备工具-Ansible

一、概述

参考思维导图

自动化运维:批量管理,批量分发,批量执行,维护……

Ansible 是python写的

批量管理工具 说明
Ansible 无客户端,基于ssh进行管理与维护
Saltstack 需要安装客户端,基于ssh进行管理,与ansible
terraform tf批量管理基础设施(主要用于创建公有云)

二、Ansible管理架构

Inventory 主机清单:被管理主机的ip列表,分类

ad-hoc 模式:命令行批量管理(使用ansible模块),临时任务

playbook 剧本模式:类似把操作写成脚本,可以重复运行这个脚本

image

三、部署与配置

1.部署

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

2.配置

[root@m01 ~]# egrep -vn "^#|^$" /etc/ansible/ansible.cfg 
10:[defaults]
#修改配置文件关机主机验证(就是远程连接时的yes/no):host_key_checking
71:host_key_checking = False     #解开注释即可
#修改配置文件开启日志功能
111:log_path = /var/log/ansible.log  #解开注释即可
327:[inventory]
340:[privilege_escalation]
346:[paramiko_connection]
370:[ssh_connection]
431:[persistent_connection]
445:[accelerate]
460:[selinux]
469:[colors]
485:[diff]

四、Ans-inventory 主机清单

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

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

实际使用中一般我们会把主机清单文件存放在指定的目录中,运行ansible的时候,通过-i来指定主机清单文件

1.主机清单必会格式

主机清单格式:
[分类或分组的名字]      #注意分类要体现出服务器的作用  分组:一般按照层次,功能,业务进行分组
ip地址或主机名或域名    #注意主机名要能解析才行
#对主机分组并进行连接测试
[root@m01 ~]# cat /etc/ansible/hosts 
[web]
172.16.1.7

[nfs]
172.16.1.31

[backup]
172.16.1.41


[root@m01 ~]# ansible all -m ping
172.16.1.41 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}
172.16.1.7 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}
172.16.1.31 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}

ansible命令格式:

ansible 主机ip或分组或all -m 指定使用的模块名字

这里的ping模块用于检查被管理端是否可以访问

2.子组

#创建新的分组包含已有的分组backup和nfs
[root@m01 ~]# cat /etc/ansible/hosts
[web]
172.16.1.7

[nfs]
172.16.1.31

[backup]
172.16.1.41

[data:children]
nfs
backup

[root@m01 ~]# ansible data -m ping
172.16.1.41 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}
172.16.1.31 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}

子组使用children关键词创建

格式:

[data:children] #子组名字:children

3.指定用户,密码

不推荐,推荐先配置密钥认证,然后进行管理

#没有设置密钥认证,主机清单如何书写
[root@m01 ~]# ssh 172.16.1.7 hostname
root@172.16.1.7's password: 
#172.16.1.7已经没有密钥认证

[root@m01 ~]# cat /etc/ansible/hosts
[web]
172.16.1.7 ansible_user=root  ansible_password=1 ansible_port=22   #书写格式

[nfs]
172.16.1.31

[backup]
172.16.1.41

[data:children]
nfs
backup
[root@m01 ~]# ansible web -m ping
172.16.1.7 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}

五、Ansible必知必会模块

1. ansible模块概述

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

参考官方文档:https://docs.ansible.com/ansible/latest/collections/ansible/builtin/index.html#plugins-in-ansible-builtin

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

2. 命令格式

ansible
ansible 主机清单(all/web/172.16.1.7) -m 模块名 -a 模块中的选项
-i 指定主机清单文件 -m 指定模块 -a 指定模块中的选项

温馨提示:

-a 选项的内容中如果有 ‘ ‘ ,那么-a 的 ‘ ‘ 要变成 ” ” ,或者内容中的变成 ” “

3. 命令与脚本类模块

3.1 command模块

ansible默认的模块,适用于执行简单的命令,不支持特殊符号

#批量获取所有主机的主机名
[root@m01 ~]# ansible web -m command -a 'hostname'
172.16.1.7 | CHANGED | rc=0 >>
web01
[root@m01 ~]# ansible web  -a 'hostname'
172.16.1.7 | CHANGED | rc=0 >>
web01

3.2 shell模块

与command模块类似,但是shell支持特殊符号

#批量删除/tmp/下面所有内容
[root@m01 ~]# ansible web -m shell -a 'rm -rf /tmp/*'
[WARNING]: Consider using the file module with state=absent rather than running 'rm'.  If you need to use command because file is insufficient you can
add 'warn: false' to this command task or set 'command_warnings=False' in ansible.cfg to get rid of this message.
172.16.1.7 | CHANGED | rc=0 >>

注意事项:

-a 后边的命令不能用别名,如果有特殊符号要使用撬棍

温馨提示:

shell 模块不推荐执行较为复杂的指令,如果需要执行放在脚本中执行

避免因为特殊符号和引号导致的问题

3.3 script模块

执行流程:

分发脚本(传输脚本),在被管理端运行脚本

[root@m01 ~]# cat /server/scripts/ansible_script.sh 
#!/bin/bash
#author: wh
#desc: 系统巡检脚本

hostname 
hostname -I 
ip a  s eth0 |awk -F'[ /]+'   'NR==3{print $3}'
uptime 
whoami 
date  +%F
sleep 5

#执行脚本
[root@m01 ~]# ansible web -m script -a '/server/scripts/ansible_script.sh'
172.16.1.7 | CHANGED => {
    "changed": true, 
    "rc": 0, 
    "stderr": "Shared connection to 172.16.1.7 closed.\r\n", 
    "stderr_lines": [
        "Shared connection to 172.16.1.7 closed."
    ], 
    "stdout": "web01\r\n10.0.0.7 172.16.1.7 \r\n10.0.0.7\r\n 17:30:35 up 14:57,  3 users,  load average: 0.06, 0.15, 0.14\r\nroot\r\n2023-02-02\r\n", 
    "stdout_lines": [
        "web01", 
        "10.0.0.7 172.16.1.7 ", 
        "10.0.0.7", 
        " 17:30:35 up 14:57,  3 users,  load average: 0.06, 0.15, 0.14", 
        "root", 
        "2023-02-02"
    ]
}


#执行脚本被控端流程
#把脚本先放在临时目录,执行完在删除
[root@web01 ~]# ps -ef|grep ansible
root      10621  10513  0 09:18 pts/2    00:00:00 /bin/sh -c  /root/.ansible/tmp/ansible-tmp-1675300696.78-5875-246300159085630/ansible_script.sh && sleep 0
root      10632  10621  0 09:18 pts/2    00:00:00 /bin/bash /root/.ansible/tmp/ansible-tmp-1675300696.78-5875-246300159085630/ansible_script.sh
root      10642  10203  0 09:18 pts/1    00:00:00 grep --color=auto ansible
[root@web01 ~]# cd /root/.ansible/tmp
[root@web01 ~/.ansible/tmp]# ll
total 0

4.文件相关模块

4.1 file模块

file模块不仅可以管理文件,还可以管理目录,管理软链接

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

模块选项 说明
path 路径(目录,文件)
src source源,源文件一般用于link(创建软链接),用于指定源文件
state 状态:具体要做什么,创建/删除,操作文件/操作目录
state=directory 创建目录
state=file (默认)更新文件,如果文件不存在也不创建
state=link 创建软链接
state=touch 创建文件
state=absent 删除(如果是目录,递归删除目录)
mode 创建并修改权限
owner 设置属主
group 设置属组
#创建/opt/test.txt
[root@m01 ~]# ansible web -m file -a 'path=/opt/test.txt state=touch'
172.16.1.7 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "dest": "/opt/test.txt", 
    "gid": 0, 
    "group": "root", 
    "mode": "0644", 
    "owner": "root", 
    "size": 0, 
    "state": "file", 
    "uid": 0
}
[root@m01 ~]# ansible web -a 'ls /opt/'
172.16.1.7 | CHANGED | rc=0 >>
test.txt

#创建目录/test/a/b/c/
[root@m01 ~]# ansible web -m file -a 'path=/test/a/b/c/ state=directory'
172.16.1.7 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "gid": 0, 
    "group": "root", 
    "mode": "0755", 
    "owner": "root", 
    "path": "/test/a/b/c/", 
    "size": 6, 
    "state": "directory", 
    "uid": 0
}
[root@m01 ~]# ansible web -a 'tree /test/'
172.16.1.7 | CHANGED | rc=0 >>
/test/
└── a
    └── b
        └── c

3 directories, 0 files

#创建软连接 /etc/hosts创建软连接到/opt/下
[root@m01 ~]# ansible web -m file -a 'src=/etc/hosts path=/opt/hosts state=link'
172.16.1.7 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "dest": "/opt/hosts", 
    "gid": 0, 
    "group": "root", 
    "mode": "0777", 
    "owner": "root", 
    "size": 10, 
    "src": "/etc/hosts", 
    "state": "link", 
    "uid": 0
}
[root@m01 ~]# ansible web -a 'ls -l /opt/'
172.16.1.7 | CHANGED | rc=0 >>
total 0
lrwxrwxrwx 1 root root 10 Feb  2 17:47 hosts -> /etc/hosts
-rw-r--r-- 1 root root  0 Feb  2 17:43 test.txt

#创建/ans-backup/目录 所有者是test,权限是700
[root@m01 ~]# ansible web -m file -a 'path=/ans-backup mode=700 owner=test state=directory'
172.16.1.7 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "gid": 0, 
    "group": "root", 
    "mode": "0700", 
    "owner": "test", 
    "path": "/ans-backup", 
    "size": 6, 
    "state": "directory", 
    "uid": 2001
}
[root@m01 ~]# ansible web -a 'ls -ld /ans-backup'
172.16.1.7 | CHANGED | rc=0 >>
drwx------ 2 test root 6 Feb  2 17:49 /ans-backup

#删除/test/目录
[root@m01 ~]# ansible web -m file -a 'path=/test/ state=absent'
172.16.1.7 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "path": "/test/", 
    "state": "absent"
}
[root@m01 ~]# ansible web -a 'ls /test/'
172.16.1.7 | FAILED | rc=2 >>
ls: cannot access /test/: No such file or directorynon-zero return code

温馨提示:

查看模块帮助,可以通过 ansible-doc -s 模块名 来查看。

[root@m01 ~]# ansible-doc -s file

4.2 copy模块

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

模块选项 说明
src source 源文件,管理端的某个文件
dest destination 目标,被管理端的目录/文件
backup backup=yes 会在覆盖前进行备份,文件内容要有变化与区别
mode 修改权限
owner 修改为指定的所有者
group 修改为指定的用户组
#分发书写好的/etc/hosts文件,如果文件存在则备份下
[root@m01 ~]# ansible web -m copy -a 'src=/etc/hosts dest=/etc/hosts backup=yes'
172.16.1.7 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "backup_file": "/etc/hosts.25211.2023-02-02@17:56:27~", 
    "changed": true, 
    "checksum": "665a4fa576811b11d78028074633acaddc8c292e", 
    "dest": "/etc/hosts", 
    "gid": 0, 
    "group": "root", 
    "md5sum": "b304c1bff962a2f1aaa79a4b5c618126", 
    "mode": "0644", 
    "owner": "root", 
    "size": 334, 
    "src": "/root/.ansible/tmp/ansible-tmp-1675331786.28-9824-177488215351037/source", 
    "state": "file", 
    "uid": 0
}

[root@web01 ~]# ll -d /etc/hosts*
-rw-r--r--  1 root root 334 Feb  2 17:56 /etc/hosts
-rw-r--r--  1 root root 330 Feb  2 10:38 /etc/hosts.25211.2023-02-02@17:56:27~  #备份文件

补充:

copy是推送,批量推送

fetch是拉取,批量拉取,使用较少

4.3 lineinfile

类似于sed ‘cai’.

模块选项 说明
path 要修改的文件,指定文件
regexp 修改正则匹配的内容,如果匹配多行,修改最后一行
line 写入的内容
create yes 如果文件不存在,进行创建

4.4 replace

类似于sed -i ‘sg’

4.5 unarchive

模块 说明
creates 一个文件名,当它已经存在时,这个步骤将不会被运行
copy 默认为yes,拷贝的文件从ansible主机复制到远程主机,no在远程主机上寻找src源文件解压
src tar源路径,可以是ansible主机上的路径,也可以是远程主机上的路径,如果是远程主机上的路径,则需设置copy=no
dest 远程主机上的目标绝对路径
mode 设置解压缩后的文件权限
exec 列出需要排除的目录和文件
remote_src 设置remote_src=yes为解包目标上已经存在的档案。对于Windows目标,改用win_unzip模块。
owner 解压后文件或目录的属主
group 解压后的目录或文件的属组

5.服务管理模块

systemd相当于Linux systemctl命令

systemd模块 说明
name 用于指定服务名称
enabled yes开机自启动(yes/no)
state 表示服务开,关,重启…
state=started 开启
state=stopped 关闭
state=reloaded 重读配置文件
state=restarted 重启(关闭再开启)
daemon-reload yes是否重新加载对应的服务的管理配置文件
#开启firewalld服务并设置开机自启动
[root@m01 ~]# ansible web -m systemd -a 'name=firewalld enabled=yes state=started'
[root@m01 ~]# ansible web -a 'systemctl status firewalld'
172.16.1.7 | CHANGED | rc=0 >>
● firewalld.service - firewalld - dynamic firewall daemon
   Loaded: loaded (/usr/lib/systemd/system/firewalld.service; enabled; vendor preset: enabled)
   Active: active (running) since Thu 2023-02-02 19:02:37 CST; 49s ago
     Docs: man:firewalld(1)
 Main PID: 26620 (firewalld)
   CGroup: /system.slice/firewalld.service
           └─26620 /usr/bin/python2 -Es /usr/sbin/firewalld --nofork --nopid

Feb 02 19:02:37 web01 systemd[1]: Starting firewalld - dynamic firewall daemon...
Feb 02 19:02:37 web01 systemd[1]: Started firewalld - dynamic firewall daemon.
Feb 02 19:02:37 web01 firewalld[26620]: WARNING: AllowZoneDrifting is enabled. This is considered an insecure configuration option. It will be removed in a future release. Please consider disabling it now.

#关闭firewalld服务并不让开机自启动
[root@m01 ~]# ansible web -m systemd -a 'name=firewalld enabled=no state=stopped'
[root@m01 ~]# ansible web -a 'systemctl status firewalld'
172.16.1.7 | FAILED | rc=3 >>
● firewalld.service - firewalld - dynamic firewall daemon
   Loaded: loaded (/usr/lib/systemd/system/firewalld.service; disabled; vendor preset: enabled)
   Active: inactive (dead)
     Docs: man:firewalld(1)

Feb 02 10:59:31 web01 systemd[1]: Starting firewalld - dynamic firewall daemon...
Feb 02 10:59:33 web01 systemd[1]: Started firewalld - dynamic firewall daemon.
Feb 02 10:59:34 web01 firewalld[13868]: WARNING: AllowZoneDrifting is enabled. This is considered an insecure configuration option. It will be removed in a future release. Please consider disabling it now.
Feb 02 10:59:48 web01 systemd[1]: Stopping firewalld - dynamic firewall daemon...
Feb 02 10:59:48 web01 systemd[1]: Stopped firewalld - dynamic firewall daemon.
Feb 02 19:02:37 web01 systemd[1]: Starting firewalld - dynamic firewall daemon...
Feb 02 19:02:37 web01 systemd[1]: Started firewalld - dynamic firewall daemon.
Feb 02 19:02:37 web01 firewalld[26620]: WARNING: AllowZoneDrifting is enabled. This is considered an insecure configuration option. It will be removed in a future release. Please consider disabling it now.
Feb 02 19:03:54 web01 systemd[1]: Stopping firewalld - dynamic firewall daemon...
Feb 02 19:03:54 web01 systemd[1]: Stopped firewalld - dynamic firewall daemon.non-zero return code

补充:

systemd模块适用于目前大部分的Linux系统

service模块适用于管理旧的Linux系统

6.软件管理模块

6.1 yum模块

yum模块并不只是yum命令,包含了yum/apt命令

模块选项 说明
name 指定的软件包名字,可以指定多个,通过”,”分隔
state installed 安装(也可以写为present)默认
removed 删除 (也可以写为absent)
lastest 安装或更新
update_cache 可以设置为no,表示不更新本地yum缓存,这样下载快。实际应用建议开启
#安装常用的软件htop,tree,lrzsz,sshpass
[root@m01 ~]# ansible web -m yum -a 'name=tree,lrzsz'
172.16.1.7 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "msg": "", 
    "rc": 0, 
    "results": [
        "tree-1.6.0-10.el7.x86_64 providing tree is already installed", 
        "lrzsz-0.12.20-36.el7.x86_64 providing lrzsz is already installed"
    ]
}

6.2 get_url模块

相当于wegt命令,所有主机能访问网络才行

推荐在管理节点下载号,使用copy仅分发即可

模块选项 说明
url 指定要下载的地址
dest 下载到哪个目录
#下载zabbix-agent的软包到/app/tools下面
zabbix-agent地址为:
https://mir	rors.aliyun.com/zabbix/zabbix/6.0/rhel/7/x86_64/zabbix-agent-6.0.13-release1.el7.x86_64.rpm
[root@m01 ~]# ansible web -m file -a 'path=/app/tools state=directory'
[root@m01 ~]# ansible web -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 '
172.16.1.7 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "checksum_dest": null, 
    "checksum_src": "19d3cab2ee9d0f69d0d8752165def185615c9607", 
    "dest": "/app/tools/zabbix-agent-6.0.13-release1.el7.x86_64.rpm", 
    "elapsed": 1, 
    "gid": 0, 
    "group": "root", 
    "md5sum": "ee1fb29f984b12a1bb381e881293e154", 
    "mode": "0644", 
    "msg": "OK (543376 bytes)", 
    "owner": "root", 
    "size": 543376, 
    "src": "/root/.ansible/tmp/ansible-tmp-1675337078.76-10471-19543799047354/tmpIEKEMp", 
    "state": "file", 
    "status_code": 200, 
    "uid": 0, 
    "url": "https://mirrors.aliyun.com/zabbix/zabbix/6.0/rhel/7/x86_64/zabbix-agent-6.0.13-release1.el7.x86_64.rpm"
}


如果要安装,可以调用yum模块安装本地的软件
name=/app/tools/zabbix-agent-6.0.13-release1.el7.x86_64.rpm

6.3 yum_repository模块

一般都是写好yum配置文件,copy分发过去,很少用这个模块

模块选项 说明
name yum源中的名字[epel]
description yum源的注释说明,对应的是name的内容
baseurl yum源中的baseurl下载地址
enabled 是否启动这个源yes/no
gpgcheck 是否启动gpgcheck功能 no
file 指定yum源的文件 自动添加.repo 默认与模块的名字一样
[root@web01 ~]# cat /etc/yum.repos.d/epel.repo 
[epel]
name=Extra Packages for Enterprise Linux 7 - $basearch
baseurl=http://mirrors.aliyun.com/epel/7/$basearch
failovermethod=priority
enabled=1
gpgcheck=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7
yum配置文件内容 yum源的模块的选项
[epel] name=epel #默认yum源文件的名字和这个一致
name=Extra Packages xxxxx description=”Extra Packages xxxxx”
baseurl=http://mirrors.aliyun.com/epel/7/$basearch baseurl=”http://mirrors.aliyun.com/epel/7/$basearch
enabled=1 enabled=yes
gpgcheck=0 gpgcheck=no
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

[root@m01 ~]# ansible web -m yum_repository -a 'name=nginx description="nginx repo" baseurl="http://nginx.org/packages/centos/$releasever/$basearch/" gpgcheck=no enabled=yes'

172.16.1.7 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "repo": "nginx", 
    "state": "present"
}
[root@m01 ~]# ansible web -a 'cat /etc/yum.repos.d/nginx.repo'
172.16.1.7 | CHANGED | rc=0 >>
[nginx]
baseurl = http://nginx.org/packages/centos/$releasever/$basearch/
enabled = 1
gpgcheck = 0
name = nginx repo

温馨提示:

nginx yum源的地址:http://nginx.org/en/linux_packages.html#RHEL

7.用户管理模块

7.1 user模块

相当于用户管理的useradd,userdel

模块选项 说明
name www 用户名
uid 指定uid
group 指定用户组,一般用于事项创建好了用户组,通过选项指定
shell 指定命令解释器:默认是/bin/bash
create_home 是否创建家目录(yes/no)
state present 添加
absent 删除
password 加密的密码
#创建www-ans用户uid 2000虚拟用户
[root@m01 ~]# ansible web -m user -a 'name=www-ans uid=2000 shell=/sbin/nologin create_home=no state=present'
172.16.1.7 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "comment": "", 
    "create_home": false, 
    "group": 2000, 
    "home": "/home/www-ans", 
    "name": "www-ans", 
    "shell": "/sbin/nologin", 
    "state": "present", 
    "system": false, 
    "uid": 2000
}
[root@m01 ~]# ansible web -a 'id www-ans'
172.16.1.7 | CHANGED | rc=0 >>
uid=2000(www-ans) gid=2000(www-ans) groups=2000(www-ans)
#批量更改密码
[root@m01 ~]# ansible all -i localhost, -m debug -a "msg={{ '1' |password_hash('sha512','mima')}}"
localhost | SUCCESS => {
    "msg": "$6$mima$/JbXK6m0czPNRNkpNqMoAI.5kk6XZBreAKaTpV1OFDd7YGXbUe2ySHgky2KaFnUKw7ghVY2G0zer5jhLWp3DT0"
}

#更新密码。密码为1
[root@m01 ~]# ansible web -m user -a "name=test password={{ '1' |password_hash('sha512','mima')}} state=present"
172.16.1.7 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "append": false, 
    "changed": true, 
    "comment": "", 
    "group": 2001, 
    "home": "/home/test", 
    "move_home": false, 
    "name": "test", 
    "password": "NOT_LOGGING_PASSWORD", 
    "shell": "/bin/bash", 
    "state": "present", 
    "uid": 2001
}

补充:

关于{{ }}的解释

{{ ‘1’ |password_hash(‘sha512′,’mima’)}}

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

7.2 group模块

模块选项 说明
name 指定用户组的名字
gid 指定组的gid
state present 添加
absent 删除

8. mount 模块

实现mount命令进行挂载可以修改/etc/fstab实现永久挂载

模块选项 说明
fstype filesystem type 指定文件系统,xfs ,ext4, iso9660,nfs
src 源地址,nfs地址
path 注意这里不是dest,挂载点(要把源挂载到哪里)
state 下表
mount模块的state参数可使用的值
absent 卸载并修改fstab
umounted 卸载不修改/etc/fstab
present 仅修改/etc/fstab 不挂载
mounted 挂载并修改/etc/fstab
remounted 重新挂载
# 通过ans管理在web01上挂载nfs:/data挂载到web01的/ans-upload/
#在web服务器上安装nfs
[root@m01 ~]# ansible web -m yum -a 'name=nfs-utils state=present'
#创建挂载点
[root@m01 ~]# ansible web -m file -a 'path=/ans-upload state=directory'
#挂载nfs
[root@m01 ~]# ansible web -m mount -a 'src=172.16.1.31:/data/ path=/ans-upload fstype=nfs state=mounted'
172.16.1.7 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "dump": "0", 
    "fstab": "/etc/fstab", 
    "fstype": "nfs", 
    "name": "/ans-upload", 
    "opts": "defaults", 
    "passno": "0", 
    "src": "172.16.1.31:/data/"
}
#检查
[root@m01 ~]# ansible web -a 'df -h'
172.16.1.7 | CHANGED | rc=0 >>
Filesystem               Size  Used Avail Use% Mounted on
devtmpfs                 475M     0  475M   0% /dev
tmpfs                    487M     0  487M   0% /dev/shm
tmpfs                    487M   26M  461M   6% /run
tmpfs                    487M     0  487M   0% /sys/fs/cgroup
/dev/mapper/centos-root   17G  2.3G   15G  14% /
/dev/sda1               1014M  138M  877M  14% /boot
tmpfs                     98M     0   98M   0% /run/user/0
172.16.1.31:/data         17G  2.0G   16G  12% /ans-upload
[root@m01 ~]# ansible web -a 'grep upload /etc/fstab'
172.16.1.7 | CHANGED | rc=0 >>
172.16.1.31:/data/ /ans-upload nfs defaults 0 0


#卸载
[root@m01 ~]# ansible web -m mount -a 'path=/ans-upload state=absent'
172.16.1.7 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "dump": "0", 
    "fstab": "/etc/fstab", 
    "name": "/ans-upload", 
    "opts": "defaults", 
    "passno": "0"
}

9.cron模块

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

模块选项 说明
name 定时任务名字,一定要加上,对用注释
minute 分钟 minute=”*/2″
hour 小时
day 日期
month 月份
week 周几
job 指定命令或脚本(定向到空)
state present 添加定时任务(默认)
absent 删除
# 每3分钟同步时间
[root@m01 ~]# ansible web -m cron -a 'name="3.ansible rsync time" minute="*/3" job="/sbin/ntpdate ntp1.aliyun.com  &>/dev/null" state=present'
172.16.1.7 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "envs": [], 
    "jobs": [
        "3.ansible rsync time"
    ]
}
[root@m01 ~]# ansible web -a 'crontab -l'
172.16.1.7 | CHANGED | rc=0 >>
#1.配置时间同步
*/2 * * * * /sbin/ntpdate ntp1.aliyun.com  &>/dev/null
#2.定时备份etc和定时任务
* * * * * sh /server/scripts/conf_backup.sh &>/dev/null
#Ansible: 3.ansible rsync time
*/3 * * * * /sbin/ntpdate ntp1.aliyun.com  &>/dev/null



#删除定时任务
[root@m01 ~]# ansible web -m cron -a 'name="3.ansible rsync time" state=absent'
172.16.1.7 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "envs": [], 
    "jobs": []
}
[root@m01 ~]# ansible web -a 'crontab -l'
172.16.1.7 | CHANGED | rc=0 >>
#1.配置时间同步
*/2 * * * * /sbin/ntpdate ntp1.aliyun.com  &>/dev/null
#2.定时备份etc和定时任务
* * * * * sh /server/scripts/conf_backup.sh &>/dev/null


#清理已有的定时任务,不是ansible创建的
ansible all -a "sed -i '/ntpdate/d' /var/spool/cron/root"

10. debug

用于调试

模块选项 说明
msg 输出提示

六、剧本

1.概述

剧本:

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

剧本yaml格式,yaml格式的文件:空格,冒号

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

2.剧本书写格式:

[root@m01 /server/scripts/playbook]# cat 1.show.yml 
---														#可以写可以不写
- hosts: all											#角色:play 指定你要管理的主机(主机清单里面的)
  tasks:												#任务:tasks 具体要执行的模块
    - name: 01 打开冰箱门                                 
      shell: echo 01 >>/tmp/bingxiang.log				#模块: 根据你的操作流程,步骤选择模块
      
    - name: 02 把大象放入冰箱
      shell: echo 02 >>/tmp/bingxiang.log
      
    - name: 03 关上冰箱的门
      shell: echo 03 >>/tmp/bingxiang.log
      
      
#书写剧本,注意以.yml或.yaml结尾
#执行剧本:
[root@m01 /server/scripts/playbook]# cat hosts
[web]
172.16.1.7

#[nfs]
#172.16.1.31

#[backup]
#172.16.1.41

#[data:children]
#nfs
#backup
[root@m01 /server/scripts/playbook]# ansible-playbook -i hosts 1.show.yml 

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

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

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

TASK [02 把大象放入冰箱] **************************
changed: [172.16.1.7]

TASK [03 关上冰箱的门] ***************************
changed: [172.16.1.7]

PLAY RECAP *********************************
172.16.1.7                 : ok=4    changed=3    unreachable=0  ailed=0    skipped=0    rescued=0    ignored=0

补充:

执行的时候显示奶牛

可以删除软件或修改ansible.cfg配置进行关闭

nocows=1 去掉注释即可

书写Ans playbook注意事项:

  • 同一个层级的内容对齐
  • 不同层级的通过2个空格对齐
  • 不能使用tab键

3.剧本案例

一般步骤:

  1. 列出流程
  2. 找出每个步骤的命令
  3. 把上面的步骤与命令—>找出对应的ans模块
  4. 书写剧本
  5. 书写剧本前拍摄快照,边书写剧本边测试,最后测试完成,恢复快照,重新跑一次(分步测试,联合测试)

3.1 创建目录并分发文件

1. 创建目录/server/files/
2. /etc/hosts文件发送过去/server/files/
[root@m01 /server/scripts/playbook]# cat 2.fenfa.yml 
---
  - hosts: all
    tasks:
      - name: 1.创建目录
        file:
          path: /server/files/
          state: directory

      - name: 2.分发文件
        copy:
          src: /etc/hosts
          dest: /server/files

3.2 分发软件包,安装软件包,启动服务

1.zabbix-agent软件包(下载)
2.安装软件包
3.配置(略)
4.启动开机自启动

[root@m01 /server/scripts/playbook]# cat 3.zabbix.yml 
---
  - hosts: all
    tasks:
      - name: 1.下载软件包
        get_url:
          url: https://mirrors.tuna.tsinghua.edu.cn/zabbix/zabbix/6.0/rhel/7/x86_64/zabbix-agent-6.0.7-1.el7.x86_64.rpm
          dest: /tmp/
          validate_certs: no
      - name: 2.安装软件包
        yum:
          name: /tmp/zabbix-agent-6.0.7-1.el7.x86_64.rpm
          state: present
      - name: 3.配置软件包
        debug:
          msg: "进行配置zabbix-agent"
      - name: 4.启动软件包
        systemd:
          name: zabbix-agent
          enabled: yes
          state: started

3.3 nfs 服务

nfs服务端:在backup上部署nfs服务,共享/backup-nfs目录,all_squash,匿名用户:nfsnobody

nfs客户端:web挂载 /ans-upload目录挂载nfs服务端共享的/backup-nfs(永久挂载)

服务端流程:
1. 部署nfs-utils,rpcbind
2. 修改配置文件
3. 创建共享目录并改所有者
4. 启动服务rpcbind,nfs(注意顺序)
客户端流程:
1. 安装nfs-utils
2. 挂载与永久挂载

[root@m01 /server/scripts/playbook]# cat 4.nfs-playbook.yml
- hosts: backup
  tasks:
    - name: 部署nfs-utils,rpcbind
      yum:
        name: nfs-utils,rpcbind
        state: present
    - name: 修改配置文件
      lineinfile:
        path: /etc/exports
        line: "/backup-nfs 172.16.1.0/24(rw,all_squash)"
    - name: 创建共享目录并改所有者
      file:
        path: /backup-nfs
        state: directory
        owner: nfsnobody
        group: nfsnobody  
    - name: 启动服务rpcbind
      systemd:
        name: rpcbind
        enabled: yes
        state: started
    - name: 启动服务nfs
      systemd:
        name: nfs
        enabled: yes
        state: started   

- hosts: web
  tasks:
    - name: 安装nfs-utils
      yum: 
        name: nfs-utils
        state: present
    - name: 挂载与永久挂载
      mount:
        src: 172.16.1.41:/backup-nfs
        path: /ans-upload
        fstype: nfs
        state: mounted

七、Ansible中的变量

变量无处不在,在ans中大部分的地方都可以定义变量

常用的创建变量的地方:剧本中,类似与 “ 功能,共用的变量文件

可以定义变量的地方 说明
在剧本文件中的定义 比较常用,仅限于当前的play使用
register变量(注册变量) 实现脚本中反引号的功能,可以获取命令结果
变量文件,根据主机清单分组进行定义变量 如果多个剧本,使用相同的变量
inventory主机清单中定义变量 用于批量修改主机使用,
命令行中 几乎不用
facts变量 一般用于获取主机基本信息:ip,主机名,系统

1. 剧本中使用变量

#批量创建/test/a/b/c/d/
[root@m01 /server/scripts/playbook]# cat 5.vars.yml 
- hosts: all
  vars:
    dir: /test/a/b/c/d/
  tasks:
    - name: 创建目录
      file:
        path: "{{ dir }}"
        state: directory

注:vars: variable 变量的内容,变量
dir就是一个变量,变量的内容就是右边的内容

温馨提示:

使用变量的时候如果变量是某个选项的开头,则变量引用的时候需要加上双引号

file:

path: “{{ dir }}” #这种要添加,变量是开头.

file:

path: /test/{{ dir }} #这种可以不加引号,变量不 是开头.

注意事项:

在剧本play中定义变量:

  1. 仅仅在当前play生效
  2. 一般用来存放路径,用户名,ip地址
  3. 注意引号的使用

2. 共用变量-变量文件

[root@m01 /server/scripts/playbook]# cat vars_file.yml 
user: www
dir: /dest
[root@m01 /server/scripts/playbook]# cat 6.vars_file.yml 
- hosts: all
  vars_files: ./vars_file.yml
  tasks:
    - name: 测试
      debug:
        msg: "测试vars: {{ user }} {{dir}}"                            
[root@m01 /server/scripts/playbook]# ansible-playbook -i hosts 6.vars_file.yml 

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

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

TASK [测试] *****************************
ok: [172.16.1.7] => {
    "msg": "测试vars: www /dest"
}
ok: [172.16.1.41] => {
    "msg": "测试vars: www /dest"
}

PLAY RECAP ****************************
172.16.1.41                : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
172.16.1.7                 : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

3. 共用变量- 根据主机组使用变量

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

变量文件

主机组创建变量文件

xxxx-check.yml
group_vars/
   lb/vars.yml  #存放lb组的变量
   web/vars.yml #存放web组的变量
   data/vars.yml #存放xxx组的变量
   all/vars.yml  #所有主机共用的变量
   未来一般使用all分组即可,把所有变量存放在一起,供剧本使用.
[root@m01 /server/scripts/playbook]# mkdir -p group_vars/all
[root@m01 /server/scripts/playbook]# cat group_vars/all/vars.yml 
user: www
dir: /test

[root@m01 /server/scripts/playbook]# cat 7.ans_group_vars.yml 
- hosts: all
  tasks:
    - name: 测试所有主机
      debug:
        msg: "测试所有主机: {{ user }} {{ dir }}"

- hosts: web
  tasks:
    - name: 测试web主机
      debug:
        msg: "测试web主机: {{ user }}{{ dir }}"

[root@m01 /server/scripts/playbook]# ansible-playbook -i hosts 7.ans_group_vars.yml 

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

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

TASK [测试所有主机] *************************
ok: [172.16.1.7] => {
    "msg": "测试所有主机: www /test"
}
ok: [172.16.1.41] => {
    "msg": "测试所有主机: www /test"
}

PLAY [web] ****************************

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

TASK [测试web主机] ************************
ok: [172.16.1.7] => {
    "msg": "测试web主机: www/test"
}

PLAY RECAP ****************************
172.16.1.41                : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  

温馨提示:

group_vars应用提示:

-般使用group_vars中all分组

4. register 变量注册变量

本质上就是用来实现脚本的反引号功能

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

某个register变量的信息
 "msg": {
        "changed": true, 
        "cmd": "date +%F", 
        "delta": "0:00:00.004257", 
        "end": "2023-02-04 15:17:24.290526", 
        "failed": false, 
        "rc": 0, 
        "start": "2023-02-04 15:17:24.286269", 
        "stderr": "", 
        "stderr_lines": [], 
        "stdout": "2023-02-04", 
        "stdout_lines": [
            "2023-02-04"
        ]
    }

[root@m01 /server/scripts/playbook]# cat 8.reg.yml 
- hosts: web
  tasks:
    - name: get date
      shell: date +%F
      register: result

    - name: 打印result内容
      debug:
        msg: "{{ result.stdout }}"

[root@m01 /server/scripts/playbook]# ansible-playbook -i hosts 8.reg.yml 

PLAY [web] *********************************

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

TASK [get date] ****************************
changed: [172.16.1.7]

TASK [打印result内容] **************************
ok: [172.16.1.7] => {
    "msg": "2023-02-04"
}

PLAY RECAP *********************************
172.16.1.7                 : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

补充:

register注册变量

变量名.stdout获取输出

json形式数据.

key: value

键: 值 变量:

内容 date +%F

stdout部分是我们想要的内容.

register变量result.

result.stdout #std standard output 标准输出

5. facts变量

  • ans内置变量,ans运行剧本的时候会有一个默认的task(Gathering Facts)
  • 运行剧本的时候ans会收集每个主机的基本信息,这些信息形成的变量叫做fact变量
  • fatcs变量setup模块获取
#查看fact变量
[root@m01 /server/scripts/playbook]# ansible web -m setup  
[root@m01 /server/scripts/playbook]# cat 9.facts.yml 
- hosts: web
  tasks:
    - name: 测试
      debug:
        msg: |
             系统ip是{{ ansible_default_ipv4.address }}
             系统主机名是{{ ansible_hostname }}
             系统是{{ ansible_distribution }}
             系统版本是{{ ansible_distribution_version }}
[root@m01 /server/scripts/playbook]# ansible-playbook -i hosts 9.facts.yml 

PLAY [web] *********************************

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

TASK [测试] **********************************
ok: [172.16.1.7] => {
    "msg": "系统ip是10.0.0.7\n系统主机名是web01\n系统是CentOS\n系统版本是7.9\n"
}

PLAY RECAP *********************************
172.16.1.7                 : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 


常用fact变量
ansible_hostname   #主机名
ansible_memtotal_mb #内存大小(总计) 单位mb
ansible_processor_vcpus        #cpu数量
ansible_default_ipv4.address   #默认的网卡ip eth0
ansible_distribution           #系统发行版本名字
ansible_processor_vcpus
ansible_processor_cores
ansible_date_time.date  

应用建议:

  1. 通过facts变量获取系统的基本信息
  2. 通过facts变量获取信息并进行判断
  3. 如果不需要可以进行关闭,加速剧本的运行( gather_facts: no)

八、流程控制

1. handlers触发器

应用场景:

一般用于分发配置文件的时候,如果文件发生变化则重启服务,如果没有变化则不重启

[root@m01 /server/scripts/playbook]# cat hosts 
[web]
172.16.1.7

[nfs]
172.16.1.31

#[backup]
#172.16.1.41

[ubuntu]
10.0.0.100

#[data:children]
#nfs
#backup
#
#[rsync_client:children]
#web
#nfs


[root@m01 /server/scripts/playbook]# cat 10.handler.yml 
- hosts: nfs
  gather_facts: no
  tasks:
    - name: 分发文件
      copy:
        src: exports
        dest: /etc/exports
        backup: yes
      notify:
        - 重启服务
  handlers:
    - name: 重启服务
      systemd:
        name: nfs
        state: reloaded
 #修改文件之后执行结果       
[root@m01 /server/scripts/playbook]# ansible-playbook -i hosts 10.handler.yml 

PLAY [nfs] *********************************

TASK [分发文件] ********************************
changed: [172.16.1.31]

RUNNING HANDLER [重启服务] *********************
changed: [172.16.1.31]

PLAY RECAP *********************************
172.16.1.31                : ok=2    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
#没有修改文件结果
[root@m01 /server/scripts/playbook]# ansible-playbook -i hosts 10.handler.yml 

PLAY [nfs] *********************************

TASK [分发文件] ********************************
ok: [172.16.1.31]

PLAY RECAP *********************************
172.16.1.31                : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

补充:

格式:

埋雷:设置绊雷(notify: 写个名字) , handlers设置名字和模块

注意事项

  1. handlers放在剧本的最后,否则都会被识别为handlers
  2. notify的名字要和handlers的名字一样

2. when判断

用于给ans运行的task模块设置条件,满足或不满足条件在运行对应的模块(不满足条件的主机提示skip)

应用建议

when进行判断,一般facts变量或register变量一起使用

#CentOS安装sl,tree    Ubuntu安装 cmatrix,lolcat
[root@m01 /server/scripts/playbook]# cat 11.when.yml 
- hosts: all
  tasks:
    - name: CentOS安装sl,tree
      yum:
        name: sl,tree
        state: present
      when: ansible_distribution == "CentOS"

    - name: Ubuntu安装 cmatrix,lolcat
      apt:
        name: cmatrix,lolcat
        state: present
      when: ansible_distribution == "Ubuntu"
      
[root@m01 /server/scripts/playbook]# ansible-playbook -i hosts 11.when.yml 

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

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

TASK [CentOS安装sl,tree] *********************
skipping: [10.0.0.100]
ok: [172.16.1.7]
ok: [172.16.1.31]

TASK [Ubuntu安装 cmatrix,lolcat] *********************
skipping: [172.16.1.7]
skipping: [172.16.1.31]
ok: [10.0.0.100]

PLAY RECAP *********************
10.0.0.100                 : ok=2    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   
172.16.1.31                : ok=2    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   
172.16.1.7                 : ok=2    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0  
when中使用的符号
==等于
!=不等于
is match(web)
ansible_hostname is match("web|backup") #类似于grep,正则.
ansible_hostname is not match("web|backup") #取反,排除.

3.循环

https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_loops.html

  • with_* :with_item
  • loop
#批量启动nfs,crond
[root@m01 /server/scripts/playbook]# cat 12.loop.yml 
- hosts: web
  tasks:
    - name: 重启nfs,crond
      systemd:
        name: "{{ item }}"
        state: restarted
      #with_items:
      loop:
        - nfs
        - crond

#循环与多个变量
[root@m01 /server/scripts/playbook]# cat 13.loop_vars.yml 
- hosts: web
  gather_facts: no
  tasks:
    - name: add user
      user:
        name: "{{ item.name }}"
        uid: "{{ item.uid }}"
        shell: /bin/bash
        state: present
      loop:
        - { name: "test1",uid: 2011}
        - { name: "test2",uid: 2012}

九、剧本调试

1. 检查语法与单步执行

-C –check 模拟运行,不做出改变。一些变量可能会提示报错。因为-C没有真正运行剧本。有些变量获取不到

–syntax-check 只做语法检查,不运行

–step 单步运行,y执行这个task,n忽略这个task,c自动运行

[root@m01 /server/scripts/playbook]# cat 2.fenfa.yml 
---
  - hosts: web
    tasks:
      - name: 1.创建目录
        file:
          path: /server/files/
          state: directory

      - name: 2.分发文件
        copy:
          src: /etc/hosts
          dest: /server/files

#-C
[root@m01 /server/scripts/playbook]# ansible-playbook -i hosts -C 2.fenfa.yml 

PLAY [web] *********************

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

TASK [1.创建目录] *********************
ok: [172.16.1.7]

TASK [2.分发文件] *********************
ok: [172.16.1.7]

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

[root@m01 /server/scripts/playbook]# ansible-playbook -i hosts --syntax-check 2.fenfa.yml 

playbook: 2.fenfa.yml

#--step
[root@m01 /server/scripts/playbook]# ansible-playbook -i hosts --step 2.fenfa.yml 

PLAY [web] *********************
Perform task: TASK: Gathering Facts (N)o/(y)es/(c)ontinue: y

Perform task: TASK: Gathering Facts (N)o/(y)es/(c)ontinue: *********************

TASK [Gathering Facts] *********************************************************
ok: [172.16.1.7]
Perform task: TASK: 1.创建目录 (N)o/(y)es/(c)ontinue: c

Perform task: TASK: 1.创建目录 (N)o/(y)es/(c)ontinue: ******************************

TASK [1.创建目录] ******************************************************************
ok: [172.16.1.7]

TASK [2.分发文件] ******************************************************************
ok: [172.16.1.7]

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

2. tag标签

tag标签类似于超市物品的分类,只不过tag标签是给ansible中的task进行分类,加上标记

运行剧本的时候运行指定的tag标签或排除某些tag

[root@m01 /server/scripts/playbook]# cat 14.tags.yml 
- hosts: nfs
  tasks:
    - name: 01. 部署nfs-utils,rpcbind
      yum:
        name: nfs-utils,rpcbind
        state: present
      tags:
        - 01.install
    - name: 02. 修改配置文件
      lineinfile:
        path: /etc/exports
        line: "/backup-nfs 172.16.1.0/24(rw,all_squash)"
        create: true
      tags:
        - 02.conf
    - name: 03. 创建共享目录并改所有者
      file:
        path: /backup-nfs
        owner: nfsnobody
        group: nfsnobody
        state:  directory
      tags:
        - 03.dir
    - name: 04. 启动服务rpcbind,nfs(注意顺序)
      systemd:
        name: "{{ item }}"
        enabled: yes
        state: started
      loop:
        - rpcbind
        - nfs
      tags:
        - 04.start_srv
        
#list-tags显示剧本中所有的tags标签
[root@m01 /server/scripts/playbook]# ansible-playbook -i hosts --list-tags 14.tags.yml 

playbook: 14.tags.yml

  play #1 (nfs): nfs	TAGS: []
      TASK TAGS: [01.install, 02.conf, 03.dir, 04.start_srv]

#运行剧本的时候
#-t 运行的标签,如果多个标签通过","分割
#skip-tags 排除指定的tags,如果多个标签通过","分割

[root@m01 /server/scripts/playbook]# ansible-playbook -i hosts -t 04.start_srv 14.tags.yml 

PLAY [nfs] *********************

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

TASK [04. 启动服务rpcbind,nfs(注意顺序)] *********************
ok: [172.16.1.31] => (item=rpcbind)
ok: [172.16.1.31] => (item=nfs)

PLAY RECAP *********************
172.16.1.31                : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

3.忽略错误

运行剧本的时候,因为重复运行导致的错误提示,并发是真的错误.

比如:目录已经存在,用户已经存在.

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

ignore_errors: true  
true/false
yes/no

十、 Jinja2模板

应用场景:

进行分发配置文件或文件的时候,需要在文件中使用变量,需要使用jinja2文件,需要使用template分发

1.基本使用

#分发motd文件,motd文件中包含ans变量(目标文件是/etc/motd).
[root@m01 /server/scripts/playbook]# cat 15.jinja2.yml 
- hosts: web
  tasks:
    - name: 分发motd
      template:
        src: motd.j2
        dest: /etc/motd
        backup: yes
[root@m01 /server/scripts/playbook]# cat motd.j2 

操作需谨慎,删根弹指间.
主机名: {{ ansible_hostname }}
ip地址: {{ ansible_default_ipv4.address }}
内存大小: {{ ansible_memtotal_mb  }}
CPU数量:  {{ ansible_processor_vcpus  }}
核心总数: {{ ansible_processor_cores }}
发行版本: {{ ansible_distribution }}

[root@m01 /server/scripts/playbook]# ansible-playbook -i hosts 15.jinja2.yml 

PLAY [web] *****************************************

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

TASK [分发motd] **************************************
ok: [172.16.1.7]

PLAY RECAP *****************************************
172.16.1.7                 : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

[root@m01 /server/scripts/playbook]# ssh 172.16.1.7
Last login: Mon Feb  6 17:15:03 2023 from 172.16.1.61

操作需谨慎,删根弹指间.
主机名: web01
ip地址: 10.0.0.7
内存大小: 972
CPU数量:  1
核心总数: 1
发行版本: CentOS

[root@web01 ~]# cat /etc/motd

操作需谨慎,删根弹指间.
主机名: web01
ip地址: 10.0.0.7
内存大小: 972
CPU数量:  1
核心总数: 1
发行版本: CentOS

2.判断使用

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

/tmp/keepalived.conf
lb01
lb02
配置文件内容
/tmp/keepalived.conf
web01 配置文件内容
state MASTER
/tmp/keepalived.conf
backup 配置文件内容
state BACKUP


[root@m01 /server/scripts/playbook]# cat 15.jinja2-
if.yml
- hosts: all
 tasks:
    - name: 分发配置文件
     template:
       src: templates/keepalived.conf.j2
       dest: /tmp/keepalived.conf
[root@m01 /server/scripts/playbook]# tree templates/
templates/
├── keepalived.conf.j2
└── motd.j2
0 directories, 2 files
[root@m01 /server/scripts/playbook]# cat
templates/keepalived.conf.j2
#this is keepalived.conf
{% if ansible_hostname  "web01" %}
   state 主节点
{% elif ansible_hostname  "backup" %}
   state 备节点
{% endif %}
[root@m01 /server/scripts/playbook]# 

3.循环

配置文件 server.conf web服务器的/tmp/目录下
10.0.0.5
10.0.0.6
10.0.0.7
10.0.0.8
10.0.0.9
10.0.0.10
{% for ip in [1,2,3,4,5,6] %}
  10.0.0.{{ ip }}
{% endfor %}
{% for ip in range(2,11) %}
  10.0.0.{{ ip }}
{%endfor%}
{% for ip in ["lidao","oldwang"] %}
  10.0.0.{{ ip }}
{%endfor%}

十一、include文件包含

在我们书写剧本的时候,会涉及到多个步骤,还会涉及到服务端和客户端.

发现剧本越来越大,不容易进行分析与阅读.

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

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

应用场景:

1个ansible剧本内容过多,涉及到多个play,可读性变弱,不方便测试

于是把单个大的剧本拆分成多个,小的剧本

多个小的剧本可以通过include功能合并使用

[root@m01 /server/scripts/playbook]# cat 16.deploy_nfs_server.yml
- name: 部署nfs-utils,rpcbind
  yum:
    name: nfs-utils,rpcbind
    state: present
- name: 修改配置文件
  lineinfile:
    path: /etc/exports
    line: "/backup-nfs 172.16.1.0/24(rw,all_squash)"
- name: 创建共享目录并改所有者
  file:
    path: /backup-nfs
    state: directory
    owner: nfsnobody
    group: nfsnobody  
- name: 启动服务rpcbind
  systemd:
    name: rpcbind
    enabled: yes
    state: started
- name: 启动服务nfs
  systemd:
    name: nfs
    enabled: yes
    state: started  
    
[root@m01 /server/scripts/playbook]# cat 16.deploy_nfs_client.yml 
- name: 安装nfs-utils
  yum: 
    name: nfs-utils
    state: present
- name: 挂载与永久挂载
  mount:
    src: 172.16.1.41:/backup-nfs
    path: /ans-upload
    fstype: nfs
    state: mounted

[root@m01 /server/scripts/playbook]# cat 16.deploy_nfs_all.yml 
- hosts: backup
  tasks:
    - include_tasks: 16.deploy_nfs_server.yml

- hosts: web
  tasks:
    - include_tasks: 16.deploy_nfs_client.yml

十二、roles

roles官网

通过使用include_tasks功能,大型剧本,缩小体积,变的更加模块化

但是有新问题,handlers,变量文件,发送配置文件(j2文件)存放比较混乱

于是人们发明了一套规范,这个规范是一套剧本目录结构的要求与标准,让书写剧本的时候,把剧本的内容和需要的文件,按照目录的要求,分类别存储

这套规则,叫roles规则,roles的本质是规定了1套目录结构,用于书写剧本

roles/目录
	top.yml             	#主剧本/入口剧本
	hosts					#主机清单 -i hosts
	nfs-server/				#功能
		files/ 				#files目录存放配置文件,剧本中使用的文件的目录,默认在files下面
		templates/ 			#存放.j2文件,template
		tasks/				#剧本中tasks内容存放在这个目录中
			main.yml		
		handlers/			#存放触发器内容
			main.yml		
	group_vars				#存放变量
		all	
			main.yml	

十三、 Vault

加密指定的文件,ansible-vault 用于加密敏感信息

hosts 文件

变量文件

这两个文件一般加密

#加密文件
[root@m01 /server/scripts/playbook]# ansible-vault encrypt hosts 
New Vault password: 
Confirm New Vault password: 
Encryption successful
[root@m01 /server/scripts/playbook]# cat hosts 
$ANSIBLE_VAULT;1.1;AES256
64616234616463323761363337373636663866623163376564366464383638383462363661383531
6435356439356538616465316435386539323930626136300a383462323035313731333730656132
61363835623439323036386362376665333934356431623337363766383230313332633533656630
3466623338363035310a623665306132313666343031306536303636616163613839376530306636
31623666383063613362633132626164616439353230343134656564663934383033666138633335
35643636313736393431386338643434653431303536386132633837386237363235613639346363
34366631366231326134333636633834646365383337363132626430376435366336653639313834
64393664333330396363386430653934326633643061336130346332653130643566336635613362
39323366326332343638396135663466616437303663313561633130363461623732343432343866
34643637316463633233366234616533663434376630366130313639633332313362343162343964
33336335613761356366316437353934653935666362636135663835343437353338313565313464
65663161666132336630
[root@m01 /server/scripts/playbook]# ansible -i hosts web -m ping
[WARNING]:  * Failed to parse /server/scripts/playbook/hosts with yaml plugin: Attempting to decrypt but no vault secrets found
[WARNING]:  * Failed to parse /server/scripts/playbook/hosts with ini plugin: Attempting to decrypt but no vault secrets found
[WARNING]: Unable to parse /server/scripts/playbook/hosts as an inventory source
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
[WARNING]: Could not match supplied host pattern, ignoring: web
#使用时加--ask-vault-pass
[root@m01 /server/scripts/playbook]# ansible -i hosts  web --ask-vault-pass -m ping
Vault password: 
172.16.1.7 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}
#解密文件
[root@m01 /server/scripts/playbook]# ansible-vault decrypt hosts
Vault password: 
Decryption successful
[root@m01 /server/scripts/playbook]# cat hosts
[web]
172.16.1.7

[nfs]
172.16.1.31

[backup]
172.16.1.41

[ubuntu]
10.0.0.100

#[data:children]
#nfs
#backup
#
#[rsync_client:children]
#web
#nfs

十四、 Galaxy

官网

#官网人家写好的nginx的roles
ansible-galaxy collection install nginxinc.nginx_core

十五、优化

性能:

  • ssh连接速度优化

    UseDNS no #相当于网络命令的-n选项.
    GSSAPIAuthentication no #关闭GSS认证.

  • 不要让ansible运行交互式命令

  • 需要使用ansible,yum安装软件,可以自建本地yum仓库,然后ansible安装(自建yum源,自己制作的rpm包)

  • 调整ansible并发数量,实际调整根据负载情况进行

    -f 调整并发数量,默认是5

    /etc/ansible/ansible.cfg 中 forks= 5

  • 给ansible配置缓存(redis),队列,缓存facts

  • 给主机进行分组操作与管理

  • 关闭 gather_facts,如果不用facts变量可以关闭

    剧本中: gather_facts: false

    配置文件中:gathering = explicit

  • 关闭host,key,check 一般使用密码认证的时候需要关闭,如果不关闭

    配置文件中:host_key_checking = False

安全:

  • ​ 配置sudo用户

    先在被管理端新增ansible用户,ans ALL=(ALL) NOPASSWD: ALL

  • 配合vpm,jms一起使用

  • 用户—>vpn—>jms—>ansible

  • 用户密码,进行加密(hash,ansible-vault)

#配置sudo
[root@m01 /server/scripts/playbook]# egrep -v '^$|#'
/etc/ansible/ansible.cfg
[defaults]
sudo_user      = ans 被管理端上具有sudo权限的用户
nopasswd: ALL
remote_user    = ans 被管理端使用的用户,不指定默认是当
前用户/root
remote_port    = 22  被管理端ssh端口号
host_key_checking = False
log_path = /var/log/ansible.log
[inventory]
[privilege_escalation]
become=True          开启sudo功能
become_method=sudo   使用sudo命令
become_user=root     普通用户切换为root
[paramiko_connection]
[ssh_connection]
[persistent_connection]
[accelerate]
[selinux]
[colors]
[diff]
被管理端:
ans   ALL=(ALL) NOPASSWD: ALL` 密码是1,ssh端口是 22
重新分发密钥给ans普通用户.
ssh-copy-id ans@10.0.0.7
测试
ansible -i hosts web   -m ping
#新增用户
[root@m01 /ansible/roles]# cat add_ans.yml 
- hosts: all
  tasks:
    - name: "新增ans用户"
      user:
        name: ans
        shell: /bin/bash
        state: present

    - name: "设置密码"
      shell: "echo 2|passwd --stdin ans"
#分发秘钥      
[root@m01 /ansible/roles]# cat fenfa_ans.sh 
#!/bin/bash
#author: wh
#version: v2 
#desc: 一键创建秘钥对 分发秘钥对 

#1.vars
user=ans
pass=2
ips="172.16.1.7 172.16.1.31 172.16.1.41"
. /etc/init.d/functions

#1.4 判断是否联网或是否可以使用yum
#1.5 加入判断sshpass命令是否存在,如果不存在则安装

#2.创建秘钥对
if [ -f ~/.ssh/id_rsa ] ;then
   echo "已经创建过秘钥对"
else
   echo "正在创建秘钥对...."
   ssh-keygen -t rsa  -f  ~/.ssh/id_rsa   -P ''  &>/dev/null
   if [ $? -eq 0 ];then
       action "密钥创建成功" /bin/true
   else
       action "密钥创建失败" /bin/false
   fi
fi

#3.通过循环发送公钥
for ip  in  $ips 
do 
   sshpass -p${pass} ssh-copy-id -i ~/.ssh/id_rsa.pub -oStrictHostKeyChecking=no ${user}@$ip &>/dev/null
   if [ $? -eq 0 ];then
       action "$ip 公钥分发 成功" /bin/true
   else
       action "$ip 公钥分发 失败" /bin/false
   fi
done
[root@m01 /ansible/roles]# sh fenfa_ans.sh 
已经创建过秘钥对
172.16.1.7 公钥分发 成功                                   [  OK  ]
172.16.1.31 公钥分发 成功                                  [  OK  ]
172.16.1.41 公钥分发 成功                                  [  OK  ]

#被控制端
[root@nfs ~]# visudo
ans     ALL=(ALL)       NOPASSWD: ALL   #第100行新增
#修改为root不允许登录
[root@web01 ~]# grep RootLogin /etc/ssh/sshd_config
PermitRootLogin no
#重启远程
[root@web01 ~]# systemctl restart ssh


#这个时候提示权限不足
[root@m01 /ansible/roles]# ansible -i hosts web -m ping
172.16.1.7 | UNREACHABLE! => {
    "changed": false, 
    "msg": "Failed to connect to the host via ssh: Permission denied (publickey,password).", 
    "unreachable": true
}


#管理端修改位置
[root@m01 ~]# egrep -v '^$|^#' /etc/ansible/ansible.cfg 
[defaults]
sudo_user      = ans    #取消注释,修改用户名
remote_user    = ans	#新增这一行,就不要用下边的了
remote_port    = 22		#取消注释
host_key_checking = False
log_path = /var/log/ansible.log
nocows = 1
[inventory]
[privilege_escalation]
become=True				#取消注释
become_method=sudo		#取消注释
become_user=root		#取消注释
[paramiko_connection]
[ssh_connection]
[persistent_connection]
[accelerate]
[selinux]
[colors]
[diff]



[root@m01 /ansible/roles]# ansible -i hosts web -m ping
172.16.1.7 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}


案例:

书写nfs部署剧本:服务端,客户端
书写rsync部署剧本:服务端,客户端(定时任务 + 脚本)
书写sersync部署剧本(存储服务器上).


nfs服务端:nfs
安装rpcbind,nfs-utils
创建共享目录,修改属主属组 /data
修改配置文件
启动rpcbind
启动nfs

nfs 客户端:web
安装nfs-utils
挂载目录 /upload


rsync服务端:backup
安装rsync
配置rsync
创建用户
创建共享目录,修改属组属主 /backup
创建密码文件并写入密码修改权限  rsync.password
启动rsync


rsync客户端:nfs,web
安装rsync
分发备份脚本  back-conf.sh
分发密码文件,修改权限为600 
创建定时任务

sersync服务端: nfs
创建软件目录
解压软件包
移动bin目录
拷贝配置文件
创建快捷方式
启动服务

sersync客户端: backup
创建备份目录,修改属主属组 /nfsbackup rsync

[root@m01 /server/scripts/playbook]# cat group_vars/all/vars.yml 
user: www
group: www
nfs_dir: /data
web_nfs_dir: /upload
rsync_dir: /backup
sersync_dir: /nfsbackup

[root@m01 /server/scripts/playbook/file]# ll
total 728
-rw-r--r-- 1 root root    780 Feb  4 16:43 back-conf.sh
-rwxr-xr-x 1 root root   2219 Feb  4 15:24 confxml.xml
-rw------- 1 root root      2 Feb  4 15:40 rsync.client
-rw-r--r-- 1 root root    561 Feb  4 15:32 rsyncd.conf
-rw-r--r-- 1 root root 727290 Feb  4 15:23 sersync2.5.4_64bit_binary_stable_final.tar.gz

[root@m01 ~]# cat /server/scripts/playbook/9.ansible_nfs_rsync_sersync.yml 
#nfs服务端
- hosts: nfs
  tasks:
    - name: 安装rpcbind,nfs-utils
      yum: 
        name: rpcbind,nfs-utils
        state: present
    - name: 创建共享目录,修改属主属组
      file:
        path: "{{ nfs_dir }}"
        state: directory
        owner: nfsnobody
        group: nfsnobody
    - name: 修改配置文件
      lineinfile:
        path: /etc/exports
        line: "/data/ 172.16.1.0/24(rw)"
        create: yes
    - name: 启动rpcbind
      systemd:
        name: rpcbind
        enabled: yes
        state: started
    - name: 启动nfs
      systemd:
        name: nfs
        enabled: yes
        state: started

#nfs客户端
- hosts: web
  tasks:
    - name: 安装nfs-utils
      yum:
        name: nfs-utils
        state: present
    - name: 挂载目录
      mount:
        src: 172.16.1.31:/data
        path: "{{ web_nfs_dir }}"
        fstype: nfs
        state: mounted

#rsync服务端   
- hosts: backup
  tasks:
    - name: 安装rsync
      yum:
        name: rsync
        state: present
    - name: 配置rsync
      copy:
        src: ./file/rsyncd.conf
        dest: /etc/rsyncd.conf
    - name: 创建用户
      user:
        name: rsync
        create_home: no
        shell: /sbin/nologin  
    - name: 创建共享目录,修改属组属主 /backup
      file:
        path: "{{ rsync_dir }}"
        state: directory
        owner: rsync
        group: rsync
    - name: 创建密码文件并写入密码修改权限
      lineinfile:
        path: /etc/rsync.password
        line: rsync_backup:1
        create: yes
        mode: 600
    - name: 启动rsync
      systemd:
        name: rsyncd
        enabled: yes
        state: started

#rsync客户端
- hosts: rsync_client
  tasks:
    - name: 安装rsync
      yum: 
        name: rsync
        state: present
    - name: 分发备份脚本
      copy:
        src: ./file/back-conf.sh
        dest: /server/scripts/back-conf.sh
    - name: 分发密码文件
      copy:
        src: ./file/rsync.client
        dest: /etc/rsync.client
        mode: 600
    - name: 创建定时任务
      cron:
        name: backup conf
        minute: "*/2"
        job: sh /server/scripts/back-conf.sh &>/dev/null
        state: present

#sersync服务端
- hosts: nfs
  tasks:
    - name: 创建bin目录
      file:
        path: /app/tools/sersync/bin/
        state: directory
    - name: 创建conf目录
      file:
        path: /app/tools/sersync/conf/
        state: directory
    - name: 解压sersync2.5.4_64bit_binary_stable_final.tar.gz
      unarchive:
        src: ./file/sersync2.5.4_64bit_binary_stable_final.tar.gz
        dest: /root/
    - name: 移动目录
      shell: "mv GNU-Linux-x86/sersync2 /app/tools/sersync/bin"           
    - name: 拷贝配置文件
      copy:
        src: ./file/confxml.xml
        dest: /app/tools/sersync/conf/confxml.xml        
    #- name: 创建快捷方式
    #  shell: "ln -s /app/tools/sersync/bin/sersync2 /bin/"
    - name: 创建快捷方式
      file:
        path: /bin/sersync2
        src: /app/tools/sersync/bin/sersync2
        state: link
    - name: 启动sersync 
      shell: "sersync2 -rdo /app/tools/sersync/conf/confxml.xml"

#sersync客户端
- hosts: backup
  tasks:
    - name: 创建同步目录
      file:
        path: "{{ sersync_dir }}"
        owner: rsync
        group: rsync
        state: directory 

故障

1.ansible 提示’provided hosts list is empty’

[root@m01 ~]# ansible all -m ping
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

原因:主机清单是空的

2.ansible 提示’Please add this host’s fingerprint to your known_hosts file to manage this host’

[root@m01 ~]# ansible web -m ping
172.16.1.7 | FAILED! => {
    "msg": "Using a SSH password instead of a key is not possible because Host Key checking is enabled and sshpass does not support this.  Please add this host's fingerprint to your known_hosts file to manage this host."
}


#原因:
~/.ssh/known_hosts 文件被删除了
#解决方法:
1.执行下ssh远程命令,输入下yes/no
2.关闭主机认证
取消/etc/ansible/ansible.cfg 配置文件中第71行:host_key_checking = False这一行的注释

3.ansible playbook下载软件包的时候提示”<urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:618)>”

[root@m01 /server/scripts/playbook]# ansible-playbook -i hosts 3.zabbix.yml 

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

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

TASK [1.下载软件包] *****************************
fatal: [172.16.1.7]: FAILED! => {"changed": false, "dest": "/tmp/", "elapsed": 0, "gid": 0, "group": "root", "mode": "01777", "msg": "Request failed: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:618)>", "owner": "root", "size": 4096, "state": "directory", "uid": 0, "url": "https://mirrors.tuna.tsinghua.edu.cn/zabbix/zabbix/6.0/rhel/7/x86_64/zabbix-agent-6.0.7-1.el7.x86_64.rpm"}

PLAY RECAP *********************************
172.16.1.7                 : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0 


#原因:get_url下载https,会进行验证
#解决:
get_url:
url: https://mirrors.tuna.tsinghua.edu.cn/zabbix/zabbix/6.0/rhel/7/x86_64/zabbix-agent-6.0.71.el7.x86_64.rpm
dest: /tmp/
validate_certs: no    #加这个选项
版权声明:本文为缘之世界原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/world-of-yuan/p/17103254.html