一文读懂Ansible

内容 隐藏
3 Ansible入门
3.5 Ansible常用模块
4 Playbook

Ansible

Ansible的概述

Ansible的功能

  • 批量执行远程命令,可以对远程的多台主机同时进行命令的执行
  • 批量安装和配置软件服务,可以对远程的多台主机进行自动化的方式配置和管理各种服务
  • 编排高级的企业级复杂的IT架构任务, Ansible的Playbook和role可以轻松实现大型的IT复杂架构
  • 提供自动化运维工具的开发API, 有很多运维工具,如jumpserver就是基于 ansible 实现自动化管理功能

Ansible的特性

  • 模块化:调用特定的模块完成特定任务,支持自定义模块,可使用任何编程语言写模块
  • Paramiko(python对ssh的实现),PyYAML,Jinja2(模板语言)三个关键模块
  • 基于Python语言实现
  • 部署简单,基于python和SSH(默认已安装),agentless,无需代理不依赖PKI(无需ssl)
  • 安全,基于OpenSSH
  • 幂等性:一个任务执行1遍和执行n遍效果一样,不因重复执行带来意外情况,此特性非绝对
  • 支持playbook编排任务,YAML格式,编排任务,支持丰富的数据结构
  • 较强大的多层解决方案 role

Ansible的架构

架构图

架构说明

  • 可以大致分为三层结构: 用户、主控端、被控端
  • 用户要通过ansible(主控端)来控制其他机器(被控端)

主要组成部分

  • inventory : ansible管理主机的清单/etc/ansible/hosts
  • ansible playbook : 任务剧本(任务集),编排定义ansible任务集的配置文件,由ansible顺序依次执行,通常是json格式的YML文件
  • modules : ansible执行命令的功能模块,多数为内置核心模块,也可以自定义
  • plugins : 模块功能的补充,如连接类型插件,循环插件,变量插件,锅炉插件等,该功能不常使用
  • API : 供第三方程序调用的应用程序编程接口
  • ansible : 组合inventor/API/modules/plugins,可以理解为是ansible命令工具,其为核心执行工具

连接方式

在管理员节点安装Ansible,编写脚本。在管理节点执行命令或者脚本时,通过SSH连接被管理的主机被管理的远程节点不需要进行特殊安装软件

默认是基于key验证的ssh连接方式控制被控端的。

支持的主机类型

Ansible可以同时管理Redhat系的Linux,Debian系的Linux,以及Windows主机。管理节点只在执行脚本时与远程主机连接,没有特别的同步机制,所以发生断电等异常一般不会影响ansbile。

Ansible入门

Ansible安装

管理员的电脑

安装Ansible

#centos的Ansible目前放在epel源中,所以需要先下载epel源
[root@master ~]# yum install -y epel-release.noarch 
[root@master ~]# yum install -y ansible
#验证是否安装成功
[root@master ~]# ansible --version
ansible 2.9.27
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python2.7/site-packages/ansible
  executable location = /usr/bin/ansible
  python version = 2.7.5 (default, Oct 14 2020, 14:45:30) [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)]

注意:Ansible不是一个长期运行的服务,它只会在需要进行连接的时候通过ssh连接,管理其它设备。管理完成就会断开连接,所以没有专门的Service让我们进行开启

配置文件

  • /etc/ansible/ansible.cfg 主配置文件,配置ansible工作特性,也可以在项目的目录中创建此文件,当前目录下如果也有ansible.cfg,则此文件优先生效,建议每个项目目录下,创建独有的ansible.cfg文件
  • /etc/ansible/hosts 主机清单
  • /etc/ansible/roles/ 存放角色的目录

主配置文件

Ansible 的配置文件可以放在多个不同地方,优先级从高到低顺序如下

ANSIBLE_CONFIG              #环境变量,注意此项用 ansible --version 看不到,但可以生效
./ansible.cfg               #当前目录下的ansible.cfg
~/.ansible.cfg              #当前用户家目录下的.ansible.cfg
/etc/ansible/ansible.cfg    #系统默认配置文件

Ansible 的默认配置文件 /etc/ansible/ansible.cfg ,其中大部分的配置内容无需进行修改

[defaults]
#inventory   = /etc/ansible/hosts              #主机列表配置文件
#library = /usr/share/my_modules/              #库文件存放目录
#remote_tmp = $HOME/.ansible/tmp               #临时py命令文件存放在远程主机目录
#local_tmp   = $HOME/.ansible/tmp              #本机的临时命令执行目录
#forks       = 5                               #默认并发数
#sudo_user   = root                            #默认sudo 用户
#ask_sudo_pass = True                          #每次执行ansible命令是否询问ssh密码
#ask_pass   = True 
#remote_port  = 22
#host_key_checking = False #检查对应服务器的host_key,建议取消此行注释,实现第一次连接自动信任目标主机
#log_path=/var/log/ansible.log                     #日志文件,建议启用
#module_name = command                         #默认模块,可以修改为shell模块
[privilege_escalation]                          #普通用户提权配置
#become=True
#become_method=sudo
#become_user=root
#become_ask_pass=False

Inventory文件

说明

ansible的主要功用在于批量主机操作,为了便捷地使用其中的部分主机,可以在inventory 主机清单文件中将其分组组织

默认的inventory file为 /etc/ansible/hosts

inventory file可以有多个,且也可以通过Dynamic Inventory来动态生成

注意: 生产建议在每个项目目录下创建项目独立的hosts文件

官方文档

https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html

主机清单配置方式

  • 单机配置:将单台主机的可解析主机名或ip配置到主机清单中
  • 分组配置:将几台运行代码相似的主机名或者主机IP放到相同组中,并为该组命名

主机清单文件格式

  • inventory文件遵循INI文件风格,中括号中的字符为组名。可以将同一个主机同时归并到多个不同的组中
  • 此外,当如若目标主机使用了非默认的SSH端口,还可以在主机名称之后使用冒号加端口号来标明
  • 如果主机名称遵循相似的命名模式,还可以使用列表的方式标识各主机

Inventory 参数说明

ansible_ssh_host    #将要连接的远程主机名.与你想要设定的主机的别名不同的话,可通过此变量设置.
ansible_ssh_port    #ssh端口号.如果不是默认的端口号,通过此变量设置.也可以使用 ip:端口192.168.1.100:2222
ansible_ssh_user    #默认的 ssh 用户名
ansible_ssh_pass    #ssh 密码(这种方式并不安全,我们强烈建议使用 --ask-pass 或 SSH 密钥)
ansible_sudo_pass   #sudo 密码(这种方式并不安全,我们强烈建议使用 --ask-sudo-pass)
ansible_sudo_exe (new in version 1.8)   #sudo 命令路径(适用于1.8及以上版本)
ansible_connection  #与主机的连接类型.比如:local, ssh 或者 paramiko. Ansible 1.2 以前默认使用 paramiko.1.2 以后默认使用 'smart','smart' 方式会根据是否支持 ControlPersist, 来判断'ssh' 方式是否可行.
ansible_ssh_private_key_file    #ssh 使用的私钥文件.适用于有多个密钥,而你不想使用 SSH 代理的情况.
ansible_shell_type              #目标系统的shell类型.默认情况下,命令的执行使用 'sh' 语法,可设置为'csh' 或 'fish'.
ansible_python_interpreter      #目标主机的 python 路径.适用于的情况: 系统中有多个 Python,或者命令路径不是"/usr/bin/python",比如 \*BSD, 或者 /usr/bin/python 不是 2.X 版本的Python.之所以不使用 "/usr/bin/env" 机制,因为这要求远程用户的路径设置正确,且要求 "python"可执行程序名不可为 python以外的名字(实际有可能名为python26).与ansible_python_interpreter 的工作方式相同,可设定如 ruby 或 perl 的路径....

常用书写方式

[root@master ansible]# cat hosts
#单机配置
192.168.239.10
192.168.239.20
192.168.239.30

#分组配置
[test1]
192.168.239.20

[test2]
192.168.239.30

#指定ssh端口号
[test3]
192.168.239.20:2222
192.168.239.30

#表示区间
[test4]
web[1:3]                    #web1,web2,web3

#定义test组中包括两个其它分组,实现组嵌套
[test:children]
test1
test2

连接准备

信任问题

通过ssh连接,首次连接需要信任对方,获取对方的/etc/ssh/ssh_host*key.pub文件中的公钥到本地,此时需要输入yes。就一台设备还好,设备多或者调用组的时候就会出现问题

解决方案

方案一:修改ssh配置文件

[root@master ~]# vim /etc/ssh/ssh_config
将
#StrictHostKeyChecking ask 
修改为
StrictHostKeyChecking no

方案二:修改Ansible配置文件

[root@master ansible]# vim ansible.cfg 
将
#host_key_checking = False
修改为
host_key_checking = False

方案三:通过命令

[root@master ~]# sshpass -p sx ssh-copy-id -o StrictHostKeyChecking=no 
在做基于key的验证时加上StrictHostKeyChecking=no选项,之后的连接就无须输入yes了

基于密码连接

ansible支持ssh通过密码的方式连接(-k选项),但是会存在问题,因为只允许输入一次密码。如果一次操作多台设备,设备的密码不一样那这个时候只有密码正确的设备才能正常的连接操作,密码错误的设备就无法正常访问了。

#此时test组中的两台设备密码不同
[root@master ansible]# ansible test -m ping -k
SSH password:                                       #输入密码
192.168.239.30 | UNREACHABLE! => {                  #失败
    "changed": false, 
    "msg": "Invalid/incorrect password: Could not create directory '/root/.ssh'.\r\nWarning: Permanently added '192.168.239.30' (ECDSA) to the list of known hosts.\r\nPermission denied, please try again.", 
    "unreachable": true
}
192.168.239.20 | SUCCESS => {                       #成功
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}
所以还是建议用户使用基于key的方式进行连接

基于key连接

直接运行脚本即可

#!/bin/bash
PASS=sx
#设置网段最后的地址,4-255之间,越小扫描越快
END=254
DEVICE=ens33

IP=`ip a s $DEVICE | awk -F'[ /]+' 'NR==3{print $3}'`
NET=${IP%.*}.

rm -f /root/.ssh/id_rsa
[ -e /root/SCANIP.log ] && rm -f /root/SCANIP.log
for((i=3;i<="$END";i++));do
ping -c 1 -w 1  ${NET}$i &> /dev/null  && echo "${NET}$i" >> /root/SCANIP.log &
done
wait

ssh-keygen -P "" -f /root/.ssh/id_rsa
rpm -q sshpass &> /dev/null || yum install -y epel-release.noarch && yum -y install sshpass &> /dev/null

#单向基于key的ssh
AliveIP=(`cat /root/SCANIP.log`)
for n in ${AliveIP[*]};do
{
sshpass -p $PASS ssh-copy-id -o StrictHostKeyChecking=no root@${n} &> /dev/null
}&
done

#双向基于key通信
#sshpass -p $PASS ssh-copy-id -o StrictHostKeyChecking=no $IP 
#
#AliveIP=(`cat SCANIP.log`)
#for n in ${AliveIP[*]};do
#sshpass -p $PASS scp -o StrictHostKeyChecking=no -r /root/.ssh root@${n}:
#done
#
##把.ssh/known_hosts拷贝到所有主机,使它们第一次互相访问时不需要输入回车
#for n in ${AliveIP[*]};do
#scp /root/.ssh/known_hosts ${n}:.ssh/
#done
#运行脚本
[root@master ~]# bash ssh.sh 
Generating public/private rsa key pair.
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:KaFzxb0jrmwKWvB0i96duQh/NMKzVEUM3t6cs+98DU8 root@master
The key's randomart image is:
+---[RSA 2048]----+
|      .+.        |
|     . oo.       |
|      o.+ .      |
|     ..+ + o     |
|. ..+.o S B      |
| + o=+oo . +  . E|
|  *..= .. .    = |
| + =.+o+   o  . o|
|. . =+B.   .+.   |
+----[SHA256]-----+

#无需输入密码
[root@master ansible]# ansible test -m ping
192.168.239.30 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}
192.168.239.20 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}

Ansible相关工具

  • /usr/bin/ansible 主程序,临时命令执行工具
  • /usr/bin/ansible-doc 查看配置文档,模块功能查看工具,相当于man
  • /usr/bin/ansible-playbook 定制自动化任务,编排剧本工具,相当于脚本
  • /usr/bin/ansible-pull 远程执行命令的工具
  • /usr/bin/ansible-vault 文件加密工具
  • /usr/bin/ansible-console 基于Console界面与用户交互的执行工具
  • /usr/bin/ansible-galaxy 下载/上传优秀代码或Roles模块的官网平台

Ansible主要的管理方式

  • Ansible Ad-Hoc 即利用ansible命令,主要用于临时命令使用场景
  • Ansible playbook 主要用于长期规划好的,大型项目的场景,需要有前期的规划过程

ansible-doc

作用:此工具用来显示模块帮助,相当于man

格式:

ansible-doc [options] [module...]
-l, --list              #列出可用模块
-s, --snippet           #显示指定模块的playbook片段
-t                      #指定插件
#列出所有的模块
[root@master ansible]# ansible-doc -l

#查看指定模块的帮助用法(详细)
[root@master ansible]# ansible-doc ping

#查看指定模块的playbook片段(精简)
[root@master ansible]# ansible-doc -s ping

#查看指定的插件
[root@master ansible]# ansible-doc -t lookup -l

ansible

作用:Ansible Ad-Hoc 的执行方式的主要工具就是 ansible

格式:

ansible <host-pattern> [-m module_name] [-a args]

选项:

--version                           #显示版本
-m module                           #指定模块,默认为command
-v                                  #详细过程 -vv -vvv更详细
--list-hosts                        #显示主机列表,可简写 --list
-C, --check                         #检查,并不执行
-T, --timeout=TIMEOUT               #执行命令的超时时间,默认10s
-k, --ask-pass                      #提示输入ssh连接密码,默认Key验证
-u, --user=REMOTE_USER              #执行远程执行的用户,默认root
-b, --become                        #代替旧版的sudo 切换
--become-user=USERNAME              #指定sudo的用户,默认为root
-K, --ask-become-pass               #提示输入sudo时的口令
-f FORKS, --forks FORKS             #指定并发同时执行ansible任务的主机数

授权

我们正常来说直接通过root帐号去管控别的设备,所以可以省去很多的麻烦,但是不排除有些公司对于安全方面管控严格这就涉及到一个授权的问题了

#先在被控制端sudo授权
[root@slave1 ~]# grep zhangsan /etc/sudoers
zhangsan   ALL=(ALL) NOPASSWD: ALL      #NOPASSWD表示无需输入当前用户密码直接切换权限

#以zhangsan的用户连接用户,并利用sudo代表lisi执行whoami命令
[root@master ansible]# ansible test1 -m shell -a 'whoami' -u zhangsan -k -b --become-user=lisi      #如果不加--become-user=lisi默认切换为root用户权限
SSH password:                   #输入远程主机zhangsan用户ssh连接密码
192.168.239.20 | CHANGED | rc=0 >>
lisi

Host-pattern

作用:用于匹配被控制的主机列表

常用:

all                         #表示所有的Inventory中的所有主机
localhost                   #表示本地
通配符(*)                  #表示所有
或者(:)                   #取并集
与(:&)                   #取交集
非(:!)                   #取未重复的值
正则表达式
#test组与test2组的
[root@master ansible]# ansible "test:&test2" --list-hosts
  hosts (1):
    192.168.239.30

#test组中有test2组中没有的,注意这里要用单引号
[root@master ansible]# ansible "test:!test2" --list-hosts
-bash: !test2": event not found
[root@master ansible]# ansible 'test:!test2' --list-hosts
  hosts (1):
    192.168.239.20

#test1组或test2组中的地址
[root@master ansible]# ansible "test1:test2" --list-hosts
  hosts (2):
    192.168.239.20
    192.168.239.30

并发执行控制

[root@master ansible]# ansible test -a 'sleep 5' -f5
#多台设备同时执行,延时五秒

[root@master ansible]# ansible test -a 'sleep 5' -f1
#多台设备依次执行,每台设备延时五秒

ansible命令执行过程

  1. 加载自己的配置文件,默认/etc/ansible/ansible.cfg
  2. 加载自己对应的模块文件,如:command
  3. 通过ansible将模块或命令生成对应的临时py文件,并将该文件传输至远程服务器的对应执行用户
    $HOME/.ansible/tmp/ansible-tmp-数字/XXX.PY文件
  4. 给文件+x执行
  5. 执行并返回结果
  6. 删除临时py文件,退出
#主控端执行以下命令
[root@master ansible]# ansible test -m shell -a 'sleep 100'

#test组下所有用户都会在用户家目录下的隐藏目录ansible下的tmp目录下,创建一个临时的py文件
[root@slave1 ~]# cd .ansible/

[root@slave1 .ansible]# ll
total 0
drwx------. 3 root root 59 Oct 31 16:44 tmp

[root@slave1 .ansible]# tree tmp/
tmp/
└── ansible-tmp-1667206619.95-1664-197441276064029
    └── AnsiballZ_command.py            #py文件

ansible执行状态

[root@master ansible]# grep -A 14 "\[colors\]" ansible.cfg 
[colors]
#highlight = white
#verbose = blue
#warn = bright purple
#error = red
#debug = dark gray
#deprecate = purple
#skip = cyan
#unreachable = red
#ok = green
#changed = yellow
#diff_add = green
#diff_remove = red
#diff_lines = cyan
  • 绿色:表示执行成功并且不需要做改变的操作
  • 黄色:表示执行成功并且对目标主机做变更
  • 红色:表示执行失败

ansible-console

作用:ansible-console可以交互执行命令,支持tab。ansible 2.0+新增

提示符格式:

执行用户@当前操作的主机组 (当前组的主机数量)[f:并发数]$

常用子命令:

  • 设置并发数: forks n 例如: forks 10
  • 切换组: cd 主机组 例如: cd web
  • 列出当前组主机列表: list
  • 列出所有的内置命令: ?或help
[root@master ~]# ansible-console
Welcome to the ansible console.
Type help or ? to list commands.

root@all (3)[f:5]$ ping             #默认主机列表为所有主机,线程数为5
192.168.239.20 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}
192.168.239.30 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}

192.168.239.10 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}

root@all (3)[f:5]$ cd test1             #切换主机列表
root@test1 (1)[f:5]$ list               #查看主机列表中的主机
192.168.239.20
root@test1 (1)[f:5]$ forks 10           #修改并发数为10
root@test1 (1)[f:10]$ 

ansible-playbook

作用:此工具用于执行编写好的playbook任务,所以这个功能的核心是编写playbook,会在后续章节中详细介绍

说明:创建的playbook的后缀为yaml或yml

[root@test-master ansible]# ansible-playbook debug.yaml 

PLAY [test] ********************************************************************************************************************

TASK [Gathering Facts] *********************************************************************************************************
ok: [192.168.239.30]
ok: [192.168.239.20]

TASK [output variables] ********************************************************************************************************
ok: [192.168.239.20] => {
    "msg": "Host \"slave1\" IP \"192.168.239.20\""
}
ok: [192.168.239.30] => {
    "msg": "Host \"slave2\" IP \"192.168.239.30\""
}

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

ansible-vault

作用:此工具可以用于加密和解密yml文件,因为有些yml文件中存放有敏感信息,不希望直接被别人查看到,所以可以将yml文件加密

格式:

ansible-vault [create|decrypt|edit|encrypt|rekey|view]
ansible-vault encrypt hello.yml         #加密
ansible-vault decrypt hello.yml         #解密
ansible-vault view hello.yml            #查看
ansible-vault edit hello.yml            #编辑加密文件
ansible-vault rekey hello.yml           #修改口令
ansible-vault create new.yml            #创建新文件

ansible-galaxy

介绍

Galaxy 是一个免费网站, 类似于github网站, 网站上发布了很多的共享的roles角色。
Ansible 提供了ansible-galaxy命令行工具连接 https://galaxy.ansible.com 网站下载相应的roles, 进行init(初始化、search( 查拘、install(安装、 remove(移除)等操作。

使用

搜索项目
[root@test-master ~]# ansible-galaxy search firewall

Found 839 roles matching your search:

 Name                                             Description
 ----                                             -----------
 0utsider.ansible_zabbix_agent                    Installing and maintaining zabbix-agent for RedHat/Debian/Ubuntu.
 1mr.firewall                                     install and configure firewall
 75629fce.ufw                                     High-level, service-based interface for configuring UFW
 aaditya2801.ansible_eks                          your role description
 aatrays.ansible_role_firewall                    Techincal Consultant
 abaez.sudo                                       Sudo user permission structure based on archlinux sudo wiki.
 abelfodil.ansible_role_wireguard                 Installs Wireguard incl. systemd integration
 ableton.jenkins_jcasc                            Installs the Jenkins CI service with JCasC
 acandid.apache                                   Install and configuring the Apache SSL on RHEL/CentOS, Debian 9,10 and Ubuntu 
 acandid.apache_failover                          Create web service Failover. Install and configuring the Apache, keepalived an
 acandid.apache_tomcat                            Install and Configure apache tomcat9 Redhat/CentOS 7 and Debian 10
 acandid.bind                                     Install and Configure Bind Redhat/CentOS 7.
 acandid.docker                                   Install Docker Redhat/CentOS 7 and Debian 10
 acandid.haproxy                                  Install and Configure haproxy Redhat/CentOS 7 and Debian 10 from repository
 acandid.httpd                                    Install httpd and ssl.
 acandid.jenkins                                  Install jenkins Redhat/CentOS 7 and Debian 10
 acandid.kubernetes                               Install and configuring the Kubernetes Cluster on RHEL/CentOS and Ubuntu 20.04
 acandid.mariadb                                  Install and Configure MariaDB 10.4
:

下载项目
[root@test-master roles]# ansible-galaxy role install robertdebock.httpd
- downloading role 'httpd', owned by robertdebock
- downloading role from https://github.com/robertdebock/ansible-role-httpd/archive/7.3.3.tar.gz
- extracting robertdebock.httpd to /root/.ansible/roles/robertdebock.httpd
- robertdebock.httpd (7.3.3) was installed successfully
列出已安装
[root@test-master roles]# ansible-galaxy list
# /root/.ansible/roles
- robertdebock.httpd, 7.3.3
# /usr/share/ansible/roles
# /etc/ansible/roles
删除项目
[root@test-master roles]# ansible-galaxy remove robertdebock.httpd
- successfully removed robertdebock.httpd

Ansible常用模块

目前ansible的模块数量趋于稳定,由2015年的270+个模块,到2016年的540+模块,到2018年的1370+个模块,再到2019年的2080+个模块,最后在2020年达到趋于稳定的3387个模块。

虽然模块众多,但是常用到的可能只有二三十个,主要针对一些常用的业务。

#模块数量
[root@master ~]# ansible-doc -l | wc -l
3387

#查看模块帮助
[root@master ~]# ansible-doc shell
> SHELL    (/usr/lib/python2.7/site-packages/ansible/modules/commands/shell.py)

        The `shell' module takes the command name followed by a list of space-delimited arguments. Either a free form
        command or `cmd' parameter is required, see the examples. It is almost exactly like the [command] module but runs
        the command through a shell (`/bin/sh') on the remote node. For Windows targets, use the [win_shell] module
        instead.

  * This module is maintained by The Ansible Core Team
  * note: This module has a corresponding action plugin.

OPTIONS (= is mandatory):

- chdir
        Change into this directory before running the command.
        [Default: (null)]
        type: path
        version_added: 0.6

- cmd
        The command to run followed by optional arguments.
        [Default: (null)]
        type: str

- creates
        A filename, when it already exists, this step will *not* be run.
        [Default: (null)]
        type: path

- executable
        Change the shell used to execute the command.
        This expects an absolute path to the executable.

模块帮助文档

https://docs.ansible.com/ansible/2.9/modules/modules_by_category.html
https://docs.ansible.com/ansible/2.9/modules/list_of_all_modules.html

Command模块

功能

在远程主机执行命令,此为默认模块,可忽略 -m 选项(可修改)

缺点

  • 此命令不支持 $VARNAME < > | ; & 等,可能用shell模块实现
  • 此模块不具有幂等性

使用

#打印字符
[root@master ~]# ansible test -a "echo haha"
192.168.239.30 | CHANGED | rc=0 >>
haha
192.168.239.20 | CHANGED | rc=0 >>
haha

#查看是否存在文件,如果不存在则执行后面的语句
[root@master ~]# ansible test -a "creates=/data mkdir /data"
[WARNING]: Consider using the file module with state=directory rather than running 'mkdir'.  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.
192.168.239.30 | CHANGED | rc=0 >>

192.168.239.20 | CHANGED | rc=0 >>

#查看文件(data被成功创建)
[root@master ~]# ansible test -a "ls -al /data"
192.168.239.20 | CHANGED | rc=0 >>
total 0
drwxr-xr-x.  2 root root   6 Nov  2 01:50 .
dr-xr-xr-x. 18 root root 236 Nov  2 01:50 ..
192.168.239.30 | CHANGED | rc=0 >>
total 0
drwxr-xr-x.  2 root root   6 Nov  3 17:09 .
dr-xr-xr-x. 18 root root 236 Nov  3 17:09 ..

#再次执行查看文件是否存在,此时存在则不执行后面的内容
[root@master ~]# ansible test -a "creates=/data mkdir /data"
192.168.239.30 | SUCCESS | rc=0 >>
skipped, since /data exists
192.168.239.20 | SUCCESS | rc=0 >>
skipped, since /data exists

#切换到指定目录下执行后续命令
[root@master ~]# ansible test -a "chdir=/data touch test.txt"
[WARNING]: Consider using the file module with state=touch rather than running 'touch'.  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.
192.168.239.30 | CHANGED | rc=0 >>
192.168.239.20 | CHANGED | rc=0 >>

#查看是否创建文件
[root@master ~]# ansible test -a "ls -l /data"
192.168.239.30 | CHANGED | rc=0 >>
total 0
-rw-r--r--. 1 root root 0 Nov  3 17:12 test.txt
192.168.239.20 | CHANGED | rc=0 >>
total 0
-rw-r--r--. 1 root root 0 Nov  2 01:53 test.txt

#输出123,写入到test.txt中
[root@master ~]# ansible test -a "echo 123 > /data/test.txt"
192.168.239.20 | CHANGED | rc=0 >>
123 > /data/test.txt
192.168.239.30 | CHANGED | rc=0 >>
123 > /data/test.txt

#查看test.txt中是否写入内容(发现内容为空,所以没有成功写入)
[root@master ~]# ansible test -a "cat /data/test.txt"
192.168.239.20 | CHANGED | rc=0 >>

192.168.239.30 | CHANGED | rc=0 >>


#输出变量的值
[root@master ~]# ansible test -a 'echo $HOSTNAME'   #在被控端上执行输出了字符串$HOSTNAME
192.168.239.20 | CHANGED | rc=0 >>
$HOSTNAME
192.168.239.30 | CHANGED | rc=0 >>
$HOSTNAME

[root@master ~]# ansible test -a "echo $HOSTNAME"   #在被控端上输出主控端的主机名
192.168.239.20 | CHANGED | rc=0 >>
master
192.168.239.30 | CHANGED | rc=0 >>
master


Shell模块

功能

和command相似,用shell执行命令,支持各种符号,比如:*,$, >

缺点

  • 此模块不具有幂等性

使用

#使用变量
[root@master ~]# ansible test -m shell -a "echo $HOSTNAME"  #在被控端上输出主控端的主机名
192.168.239.20 | CHANGED | rc=0 >>
master
192.168.239.30 | CHANGED | rc=0 >>
master

[root@master ~]# ansible test -m shell -a 'echo $HOSTNAME'  #在被控端上执行输出了被控端自己的主机名(可以执行变量)
192.168.239.20 | CHANGED | rc=0 >>
slave1
192.168.239.30 | CHANGED | rc=0 >>
slave2


#使用管道符
[root@master ~]# ansible test -m shell -a 'cat /etc/passwd | grep root'
192.168.239.20 | CHANGED | rc=0 >>
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
192.168.239.30 | CHANGED | rc=0 >>
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin


#使用重定向
[root@master ~]# ansible test -m shell -a 'echo 123 > /data/haha.txt'
192.168.239.20 | CHANGED | rc=0 >>

192.168.239.30 | CHANGED | rc=0 >>

[root@master ~]# ansible test -m shell -a 'cat /data/haha.txt'
192.168.239.30 | CHANGED | rc=0 >>
123                                             #成功写入内容到文件中
192.168.239.20 | CHANGED | rc=0 >>
123


说明

调用bash执行命令 类似cat /etc/passwd | awk -F ‘:’ ‘{print $1,$2}’ &> /data/lala.txt 这些复杂命令,即使使用shell也可能会失败,解决办法:写到脚本时,copy到远程,执行,再把需要的结果拉回执行命令的机器

#直接通过shell执行
[root@master ~]# ansible test -m shell -a "cat /etc/passwd | awk -F ':' '{print $1,$2}' &> /data/lala.txt "
192.168.239.20 | FAILED | rc=1 >>
non-zero return code                                                #报错了
192.168.239.30 | FAILED | rc=1 >>
non-zero return code
#本地创建sh脚本
[root@master ~]# vim test.sh


#复制本地sh脚本文件到被控端
[root@master ~]# ansible test -m copy -a "src=/root/test.sh dest=/data/test.sh"
192.168.239.30 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "checksum": "0bddb06995c8d3526c15331a3afaf0c9ffe7ba3c", 
    "dest": "/data/test.sh", 
    "gid": 0, 
    "group": "root", 
    "md5sum": "24f3359cc2c0bf01db623b813c4e4403", 
    "mode": "0644", 
    "owner": "root", 
    "secontext": "system_u:object_r:default_t:s0", 
    "size": 63, 
    "src": "/root/.ansible/tmp/ansible-tmp-1667578662.82-5161-55917467770947/source", 
    "state": "file", 
    "uid": 0
}
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "checksum": "0bddb06995c8d3526c15331a3afaf0c9ffe7ba3c", 
    "dest": "/data/test.sh", 
    "gid": 0, 
    "group": "root", 
    "md5sum": "24f3359cc2c0bf01db623b813c4e4403", 
    "mode": "0644", 
    "owner": "root", 
    "secontext": "system_u:object_r:default_t:s0", 
    "size": 63, 
    "src": "/root/.ansible/tmp/ansible-tmp-1667578662.86-5159-244943643090721/source", 
    "state": "file", 
    "uid": 0
}

#在被控端执行脚本文件
[root@master ~]# ansible test -m shell -a "bash /data/test.sh"
192.168.239.20 | CHANGED | rc=0 >>

192.168.239.30 | CHANGED | rc=0 >>


#查看执行后生成的文件
[root@master ~]# ansible test -m shell -a "cat /data/lala.txt | tail -3"
192.168.239.30 | CHANGED | rc=0 >>
lisi x
papa x
huahua x
192.168.239.20 | CHANGED | rc=0 >>
lisi x
papa x
huahua x
#让被控端,直接执行,控制端本地的sh脚本
[root@master ~]# ansible test -m script -a /root/test.sh
192.168.239.30 | CHANGED => {
    "changed": true, 
    "rc": 0, 
    "stderr": "Shared connection to 192.168.239.30 closed.\r\n", 
    "stderr_lines": [
        "Shared connection to 192.168.239.30 closed."
    ], 
    "stdout": "", 
    "stdout_lines": []
}
192.168.239.20 | CHANGED => {
    "changed": true, 
    "rc": 0, 
    "stderr": "Shared connection to 192.168.239.20 closed.\r\n", 
    "stderr_lines": [
        "Shared connection to 192.168.239.20 closed."
    ], 
    "stdout": "", 
    "stdout_lines": []
}

Script模块

功能

在远程主机上运行ansible服务器上的脚本(无需执行权限)

缺点

  • 此模块不具有幂等性

使用

#让被控端,直接执行,控制端本地的sh脚本
[root@master ~]# ansible test -m script -a /root/test.sh
192.168.239.30 | CHANGED => {
    "changed": true, 
    "rc": 0, 
    "stderr": "Shared connection to 192.168.239.30 closed.\r\n", 
    "stderr_lines": [
        "Shared connection to 192.168.239.30 closed."
    ], 
    "stdout": "", 
    "stdout_lines": []
}
192.168.239.20 | CHANGED => {
    "changed": true, 
    "rc": 0, 
    "stderr": "Shared connection to 192.168.239.20 closed.\r\n", 
    "stderr_lines": [
        "Shared connection to 192.168.239.20 closed."
    ], 
    "stdout": "", 
    "stdout_lines": []
}

Copy模块

功能

从ansible服务器主控端复制文件到远程主机

说明

src=file 如果是没指明路径,则为当前目录或当前目录下的files目录下的file文件

使用

#复制文件到被控端,默认是直接覆盖,但是可以指定先备份(此处指定先备份)
[root@master ~]# ansible test -m copy -a "src=test.sh dest=/data/test.sh owner=zhangsan mode=600 backup=yes"
192.168.239.30 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false,                   #未发生变化(没有同名文件,或者有同名文件,内容相同)
    "checksum": "0bddb06995c8d3526c15331a3afaf0c9ffe7ba3c", 
    "dest": "/data/test.sh", 
    "gid": 0, 
    "group": "root", 
    "mode": "0600", 
    "owner": "zhangsan", 
    "path": "/data/test.sh", 
    "secontext": "system_u:object_r:default_t:s0", 
    "size": 63, 
    "state": "file", 
    "uid": 1002
}
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "backup_file": "/data/test.sh.3604.2022-11-05@00:43:18~", 
    "changed": true,                    #发生变换(有同名文件,内容不同,将原文件进行备份)
    "checksum": "0bddb06995c8d3526c15331a3afaf0c9ffe7ba3c", 
    "dest": "/data/test.sh", 
    "gid": 0, 
    "group": "root", 
    "md5sum": "24f3359cc2c0bf01db623b813c4e4403", 
    "mode": "0600", 
    "owner": "zhangsan", 
    "secontext": "system_u:object_r:default_t:s0", 
    "size": 63, 
    "src": "/root/.ansible/tmp/ansible-tmp-1667580197.65-5611-74516127536276/source", 
    "state": "file", 
    "uid": 1002
}

#
[root@master ~]# ansible test -m shell -a "ls /data/ | grep test*"
192.168.239.30 | CHANGED | rc=0 >>
test.sh
192.168.239.20 | CHANGED | rc=0 >>
test.sh
test.sh.3604.2022-11-05@00:43:18~                   #有一个备份文件
#指定内容,直接生成目标文件
[root@master ~]# ansible test -m copy -a "content='zhangsan\n lisi\n wangwu\n' dest=/data/name.txt"
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "checksum": "52d3542d777158ee5aefeaf77dead8d1f2f9873d", 
    "dest": "/data/name.txt", 
    "gid": 0, 
    "group": "root", 
    "md5sum": "759eaba8d67a15c79023ce0302363b66", 
    "mode": "0644", 
    "owner": "root", 
    "secontext": "system_u:object_r:default_t:s0", 
    "size": 23, 
    "src": "/root/.ansible/tmp/ansible-tmp-1667580709.93-5932-156722120071847/source", 
    "state": "file", 
    "uid": 0
}
192.168.239.30 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "checksum": "52d3542d777158ee5aefeaf77dead8d1f2f9873d", 
    "dest": "/data/name.txt", 
    "gid": 0, 
    "group": "root", 
    "md5sum": "759eaba8d67a15c79023ce0302363b66", 
    "mode": "0644", 
    "owner": "root", 
    "secontext": "system_u:object_r:default_t:s0", 
    "size": 23, 
    "src": "/root/.ansible/tmp/ansible-tmp-1667580709.9-5934-64319262109722/source", 
    "state": "file", 
    "uid": 0
}
[root@master ~]# ansible test -a "cat /data/name.txt"
192.168.239.30 | CHANGED | rc=0 >>
zhangsan
 lisi
 wangwu
192.168.239.20 | CHANGED | rc=0 >>
zhangsan
 lisi
 wangwu

[root@master ~]# touch /root/zhangsan/{1..5}.txt
[root@master ~]# ls zhangsan/
1.txt  2.txt  3.txt  4.txt  5.txt

#复制目录,及其子文件(目录名后面不加/)
[root@master ~]# ansible test -m copy -a "src=/root/zhangsan dest=/data/test1"
192.168.239.20 | CHANGED => {
    "changed": true, 
    "dest": "/data/test1/", 
    "src": "/root/zhangsan"
}
192.168.239.30 | CHANGED => {
    "changed": true, 
    "dest": "/data/test1/", 
    "src": "/root/zhangsan"
}

[root@master ~]# ansible test -a "ls /data/test1"
192.168.239.30 | CHANGED | rc=0 >>
zhangsan
192.168.239.20 | CHANGED | rc=0 >>
zhangsan


#不复制目录只复制其子文件
[root@master ~]# ansible test -m copy -a "src=/root/zhangsan/ dest=/data/test2"
192.168.239.20 | CHANGED => {
    "changed": true, 
    "dest": "/data/test2/", 
    "src": "/root/zhangsan/"
}
192.168.239.30 | CHANGED => {
    "changed": true, 
    "dest": "/data/test2/", 
    "src": "/root/zhangsan/"
}

[root@master ~]# ansible test -a "ls /data/test2"
192.168.239.20 | CHANGED | rc=0 >>
1.txt
2.txt
3.txt
4.txt
5.txt
192.168.239.30 | CHANGED | rc=0 >>
1.txt
2.txt
3.txt
4.txt
5.txt

Get_url模块

功能

用于将文件从http、https或ftp下载到被管理机节点上

常用参数

url: 下载文件的URL,支持HTTP,HTTPS或FTP协议
dest: 下载到目标路径(绝对路径),如果目标是一个目录,就用服务器上面文件的名称,如果目标设置了名称就用目标设置的名称
owner:指定属主
group:指定属组
mode:指定权限
force: 如果yes,dest不是目录,将每次下载文件,如果内容改变,替换文件。如果否,则只有在目标不存在时才会下载该文件
checksum: 对目标文件在下载后计算摘要,以确保其完整性
     示例: checksum="sha256:D98291AC[...]B6DC7B97",
       checksum="sha256:http://example.com/path/sha256sum.txt"
url_username: 用于HTTP基本认证的用户名。 对于允许空密码的站点,此参数可以不使用`url_password'
url_password: 用于HTTP基本认证的密码。 如果未指定`url_username'参数,则不会使用`url_password'参数
validate_certs:如果“no”,SSL证书将不会被验证。 适用于自签名证书在私有网站上使用
timeout: URL请求的超时时间,秒为单位

使用

#创建目录
[root@master ~]# ansible test -a "mkdir -pv /data/nginx"
[WARNING]: Consider using the file module with state=directory rather than running 'mkdir'.  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.
192.168.239.30 | CHANGED | rc=0 >>
mkdir: created directory ‘/data/nginx’
192.168.239.20 | CHANGED | rc=0 >>
mkdir: created directory ‘/data/nginx’


#从官网下载nginx到指定目录,验证指纹
[root@master ~]# ansible test -m get_url -a 'url=http://nginx.org/download/nginx-1.18.0.tar.gz dest=/data/nginx/nginx.tar.gz checksum="md5:b2d33d24d89b8b1f87ff5d251aa27eb8"'
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "checksum_dest": null, 
    "checksum_src": "47b2c5ccd12e2a7088b03d629ff6b9ab18215180", 
    "dest": "/data/nginx/nginx.tar.gz", 
    "elapsed": 3, 
    "gid": 0, 
    "group": "root", 
    "md5sum": "b2d33d24d89b8b1f87ff5d251aa27eb8", 
    "mode": "0644", 
    "msg": "OK (1039530 bytes)", 
    "owner": "root", 
    "secontext": "system_u:object_r:default_t:s0", 
    "size": 1039530, 
    "src": "/root/.ansible/tmp/ansible-tmp-1667809715.12-1607-187080199384392/tmpIXc1UQ", 
    "state": "file", 
    "status_code": 200, 
    "uid": 0, 
    "url": "http://nginx.org/download/nginx-1.18.0.tar.gz"
}
192.168.239.30 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "checksum_dest": null, 
    "checksum_src": "47b2c5ccd12e2a7088b03d629ff6b9ab18215180", 
    "dest": "/data/nginx/nginx.tar.gz", 
    "elapsed": 14, 
    "gid": 0, 
    "group": "root", 
    "md5sum": "b2d33d24d89b8b1f87ff5d251aa27eb8", 
    "mode": "0644", 
    "msg": "OK (1039530 bytes)", 
    "owner": "root", 
    "secontext": "system_u:object_r:default_t:s0", 
    "size": 1039530, 
    "src": "/root/.ansible/tmp/ansible-tmp-1667809715.13-1609-14551219325281/tmpex5M7a", 
    "state": "file", 
    "status_code": 200, 
    "uid": 0, 
    "url": "http://nginx.org/download/nginx-1.18.0.tar.gz"
}

#检查是否成功下载
[root@master ~]# ansible test -a "ls /data/nginx"
192.168.239.30 | CHANGED | rc=0 >>
nginx.tar.gz
192.168.239.20 | CHANGED | rc=0 >>
nginx.tar.gz

Fetch模块

功能

从远程主机提取文件至ansible的主控端,与copy相反,目前不支持目录

使用

#将被控端的nginx文件复制到控制端
[root@master ~]# ansible test -m fetch -a "src=/data/nginx/nginx.tar.gz dest=/root"
192.168.239.20 | CHANGED => {
    "changed": true, 
    "checksum": "47b2c5ccd12e2a7088b03d629ff6b9ab18215180", 
    "dest": "/root/192.168.239.20/data/nginx/nginx.tar.gz", 
    "md5sum": "b2d33d24d89b8b1f87ff5d251aa27eb8", 
    "remote_checksum": "47b2c5ccd12e2a7088b03d629ff6b9ab18215180", 
    "remote_md5sum": null
}
192.168.239.30 | CHANGED => {
    "changed": true, 
    "checksum": "47b2c5ccd12e2a7088b03d629ff6b9ab18215180", 
    "dest": "/root/192.168.239.30/data/nginx/nginx.tar.gz", 
    "md5sum": "b2d33d24d89b8b1f87ff5d251aa27eb8", 
    "remote_checksum": "47b2c5ccd12e2a7088b03d629ff6b9ab18215180", 
    "remote_md5sum": null
}

#每个被控端都会上传,生成一个自己ip对应的目录
[root@master ~]# tree 192.168.239.*
192.168.239.20
├── data
│   └── nginx
│       └── nginx.tar.gz
└── etc
    └── redhat-release
192.168.239.30
├── data
│   └── nginx
│       └── nginx.tar.gz
└── etc
    └── redhat-release

6 directories, 4 files

File模块

功能

设置文件属性,创建软链接等

选项

path            #被管理文件的路径
state           #指定状态
        touch           #创建文件
        directory       #创建目录
        absent          #删除文件
        link            #创建软链接
owner           #指定属主
mode            #指定权限
recurse         #递归地在目录内容上设置指定的文件属性。这只适用于' state'被设置为“directory”

使用

#创建文件——原始方法
[root@master ~]# ansible test -a "touch /data/a.txt"
[WARNING]: Consider using the file module with state=touch rather than running 'touch'.  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.       #每次使用会有一个粉色的提示信息,说明有专用的模块来实现此功能
192.168.239.30 | CHANGED | rc=0 >>

192.168.239.20 | CHANGED | rc=0 >>

#创建文件——file模块
[root@master ~]# ansible test -m file -a "path=/data/b.txt state=touch"
192.168.239.30 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "dest": "/data/b.txt", 
    "gid": 0, 
    "group": "root", 
    "mode": "0644", 
    "owner": "root", 
    "secontext": "unconfined_u:object_r:default_t:s0", 
    "size": 0, 
    "state": "file", 
    "uid": 0
}
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "dest": "/data/b.txt", 
    "gid": 0, 
    "group": "root", 
    "mode": "0644", 
    "owner": "root", 
    "secontext": "unconfined_u:object_r:default_t:s0", 
    "size": 0, 
    "state": "file", 
    "uid": 0
}

#创建目录
[root@master ~]# ansible test -m file -a "path=/data/test state=directory"
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "gid": 0, 
    "group": "root", 
    "mode": "0755", 
    "owner": "root", 
    "path": "/data/test", 
    "secontext": "unconfined_u:object_r:default_t:s0", 
    "size": 6, 
    "state": "directory", 
    "uid": 0
}
192.168.239.30 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "gid": 0, 
    "group": "root", 
    "mode": "0755", 
    "owner": "root", 
    "path": "/data/test", 
    "secontext": "unconfined_u:object_r:default_t:s0", 
    "size": 6, 
    "state": "directory", 
    "uid": 0
}


#删除文件
[root@master ~]# ansible test -m file -a "path=/data/b.txt state=absent"
192.168.239.30 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "path": "/data/b.txt", 
    "state": "absent"
}
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "path": "/data/b.txt", 
    "state": "absent"
}
#指定属主,数组,文件权限
[root@master ~]# ansible test -m file -a "path=/data/heihei.txt owner=zhangsan group=test mode=666 state=touch"
192.168.239.30 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "dest": "/data/heihei.txt", 
    "gid": 10000, 
    "group": "test", 
    "mode": "0666", 
    "owner": "zhangsan", 
    "secontext": "unconfined_u:object_r:default_t:s0", 
    "size": 0, 
    "state": "file", 
    "uid": 1002
}
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "dest": "/data/heihei.txt", 
    "gid": 10000, 
    "group": "test", 
    "mode": "0666", 
    "owner": "zhangsan", 
    "secontext": "unconfined_u:object_r:default_t:s0", 
    "size": 0, 
    "state": "file", 
    "uid": 1002
}

[root@master ~]# ansible test -a "ls -l /data/heihei.txt"
192.168.239.20 | CHANGED | rc=0 >>
-rw-rw-rw-. 1 zhangsan test 0 Nov  9 16:31 /data/heihei.txt
192.168.239.30 | CHANGED | rc=0 >>
-rw-rw-rw-. 1 zhangsan test 0 Nov  9 16:31 /data/heihei.txt

#创建软链接(创建haha.txt的软链接haha-link)
[root@master ~]# ansible test -m file -a "src=/data/haha.txt path=/data/haha-link state=link"
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "dest": "/data/haha-link", 
    "gid": 0, 
    "group": "root", 
    "mode": "0777", 
    "owner": "root", 
    "secontext": "unconfined_u:object_r:default_t:s0", 
    "size": 14, 
    "src": "/data/haha.txt", 
    "state": "link", 
    "uid": 0
}
192.168.239.30 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "dest": "/data/haha-link", 
    "gid": 0, 
    "group": "root", 
    "mode": "0777", 
    "owner": "root", 
    "secontext": "unconfined_u:object_r:default_t:s0", 
    "size": 14, 
    "src": "/data/haha.txt", 
    "state": "link", 
    "uid": 0
}


[root@master ~]# ansible test -m shell -a "ls -l /data/haha-link"
192.168.239.20 | CHANGED | rc=0 >>
lrwxrwxrwx. 1 root root 14 Nov  9 16:40 /data/haha-link -> /data/haha.txt
192.168.239.30 | CHANGED | rc=0 >>
lrwxrwxrwx. 1 root root 14 Nov  9 16:40 /data/haha-link -> /data/haha.txt

#递归修改目录属性,但不递归至子目录
[root@master ~]# ansible test -m file -a "path=/data state=directory owner=zhangsan"
192.168.239.30 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "gid": 0, 
    "group": "root", 
    "mode": "0755", 
    "owner": "zhangsan", 
    "path": "/data", 
    "secontext": "unconfined_u:object_r:default_t:s0", 
    "size": 169, 
    "state": "directory", 
    "uid": 1002
}
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "gid": 0, 
    "group": "root", 
    "mode": "0755", 
    "owner": "zhangsan", 
    "path": "/data", 
    "secontext": "unconfined_u:object_r:default_t:s0", 
    "size": 169, 
    "state": "directory", 
    "uid": 1002
}

#修改目录属主信息
[root@master ~]# ansible test -m shell -a "ls -l / | grep data"
192.168.239.30 | CHANGED | rc=0 >>
drwxr-xr-x.   4 zhangsan root  169 Nov  9 16:40 data
192.168.239.20 | CHANGED | rc=0 >>
drwxr-xr-x.   4 zhangsan root  169 Nov  9 16:40 data

#查看子目录属主
[root@master ~]# ansible test -m shell -a "ls -l /data"
192.168.239.20 | CHANGED | rc=0 >>
total 20
-rw-r--r--. 1 root     root   4 Nov  7 16:52 a.txt
lrwxrwxrwx. 1 root     root  14 Nov  9 16:40 haha-link -> /data/haha.txt
-rw-r--r--. 1 root     root   4 Nov  4 19:41 haha.txt
-rw-rw-rw-. 1 zhangsan test   0 Nov  9 16:31 heihei.txt
-rw-r--r--. 1 root     root  63 Nov  5 00:40 lala.txt
-rw-r--r--. 1 root     root 200 Nov  5 00:25 lala.txt.3237.2022-11-05@00:40:00~
-rw-r--r--. 1 root     root  23 Nov  5 00:51 name.txt
drwxr-xr-x. 2 root     root  26 Nov  7 16:28 nginx
drwxr-xr-x. 2 root     root   6 Nov  9 16:35 test
192.168.239.30 | CHANGED | rc=0 >>
total 20
-rw-r--r--. 1 root     root   4 Nov  7 16:52 a.txt
lrwxrwxrwx. 1 root     root  14 Nov  9 16:40 haha-link -> /data/haha.txt
-rw-r--r--. 1 root     root   4 Nov  4 19:41 haha.txt
-rw-rw-rw-. 1 zhangsan test   0 Nov  9 16:31 heihei.txt
-rw-r--r--. 1 root     root  63 Nov  5 00:40 lala.txt
-rw-r--r--. 1 root     root 200 Nov  5 00:25 lala.txt.3239.2022-11-05@00:40:05~
-rw-r--r--. 1 root     root  23 Nov  5 00:51 name.txt
drwxr-xr-x. 2 root     root  26 Nov  7 16:28 nginx
drwxr-xr-x. 2 root     root   6 Nov  9 16:35 test



#递归修改目录及子目录的属性
[root@master ~]# ansible test -m file -a "path=/data state=directory owner=zhangsan recurse=yes"
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "gid": 0, 
    "group": "root", 
    "mode": "0755", 
    "owner": "zhangsan", 
    "path": "/data", 
    "secontext": "unconfined_u:object_r:default_t:s0", 
    "size": 169, 
    "state": "directory", 
    "uid": 1002
}
192.168.239.30 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "gid": 0, 
    "group": "root", 
    "mode": "0755", 
    "owner": "zhangsan", 
    "path": "/data", 
    "secontext": "unconfined_u:object_r:default_t:s0", 
    "size": 169, 
    "state": "directory", 
    "uid": 1002
}

#查看子目录的属主
[root@master ~]# ansible test -m shell -a "ls -l /data"
192.168.239.30 | CHANGED | rc=0 >>
total 20
-rw-r--r--. 1 zhangsan root   4 Nov  7 16:52 a.txt
lrwxrwxrwx. 1 zhangsan root  14 Nov  9 16:40 haha-link -> /data/haha.txt
-rw-r--r--. 1 zhangsan root   4 Nov  4 19:41 haha.txt
-rw-rw-rw-. 1 zhangsan test   0 Nov  9 16:31 heihei.txt
-rw-r--r--. 1 zhangsan root  63 Nov  5 00:40 lala.txt
-rw-r--r--. 1 zhangsan root 200 Nov  5 00:25 lala.txt.3239.2022-11-05@00:40:05~
-rw-r--r--. 1 zhangsan root  23 Nov  5 00:51 name.txt
drwxr-xr-x. 2 zhangsan root  26 Nov  7 16:28 nginx
drwxr-xr-x. 2 zhangsan root   6 Nov  9 16:35 test
192.168.239.20 | CHANGED | rc=0 >>
total 20
-rw-r--r--. 1 zhangsan root   4 Nov  7 16:52 a.txt
lrwxrwxrwx. 1 zhangsan root  14 Nov  9 16:40 haha-link -> /data/haha.txt
-rw-r--r--. 1 zhangsan root   4 Nov  4 19:41 haha.txt
-rw-rw-rw-. 1 zhangsan test   0 Nov  9 16:31 heihei.txt
-rw-r--r--. 1 zhangsan root  63 Nov  5 00:40 lala.txt
-rw-r--r--. 1 zhangsan root 200 Nov  5 00:25 lala.txt.3237.2022-11-05@00:40:00~
-rw-r--r--. 1 zhangsan root  23 Nov  5 00:51 name.txt
drwxr-xr-x. 2 zhangsan root  26 Nov  7 16:28 nginx
drwxr-xr-x. 2 zhangsan root   6 Nov  9 16:35 test

[root@master ~]# ansible test -m shell -a "ls -l /data/nginx"
192.168.239.20 | CHANGED | rc=0 >>
total 1016
-rw-r--r--. 1 zhangsan root 1039530 Nov  7 16:28 nginx.tar.gz
192.168.239.30 | CHANGED | rc=0 >>
total 1016
-rw-r--r--. 1 zhangsan root 1039530 Nov  7 16:28 nginx.tar.gz

Stat模块

功能

检查文件或文件系统的状态

说明

对于Windows目标,请改用win_stat模块

选项

path:文件/对象的完整路径(必须)

使用

#查看文件状态
[root@master ~]# ansible test -m stat -a "path=/data/heihei.txt"
192.168.239.30 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "stat": {
        "atime": 1667982695.3030083, 
        "attr_flags": "", 
        "attributes": [], 
        "block_size": 4096, 
        "blocks": 0, 
        "charset": "binary", 
        "checksum": "da39a3ee5e6b4b0d3255bfef95601890afd80709", 
        "ctime": 1667982695.3030083, 
        "dev": 64768, 
        "device_type": 0, 
        "executable": false, 
        "exists": true, 
        "gid": 10000, 
        "gr_name": "test", 
        "inode": 34541698, 
        "isblk": false, 
        "ischr": false, 
        "isdir": false, 
        "isfifo": false, 
        "isgid": false, 
        "islnk": false, 
        "isreg": true, 
        "issock": false, 
        "isuid": false, 
        "mimetype": "inode/x-empty", 
        "mode": "0666", 
        "mtime": 1667982695.3030083, 
        "nlink": 1, 
        "path": "/data/heihei.txt", 
        "pw_name": "zhangsan", 
        "readable": true, 
        "rgrp": true, 
        "roth": true, 
        "rusr": true, 
        "size": 0, 
        "uid": 1002, 
        "version": "18446744073050838260", 
        "wgrp": true, 
        "woth": true, 
        "writeable": true, 
        "wusr": true, 
        "xgrp": false, 
        "xoth": false, 
        "xusr": false
    }
}
192.168.239.20 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "stat": {
        "atime": 1667982695.7960083, 
        "attr_flags": "", 
        "attributes": [], 
        "block_size": 4096, 
        "blocks": 0, 
        "charset": "binary", 
        "checksum": "da39a3ee5e6b4b0d3255bfef95601890afd80709", 
        "ctime": 1667982695.7960083, 
        "dev": 64768, 
        "device_type": 0, 
        "executable": false, 
        "exists": true, 
        "gid": 10000, 
        "gr_name": "test", 
        "inode": 34541699, 
        "isblk": false, 
        "ischr": false, 
        "isdir": false, 
        "isfifo": false, 
        "isgid": false, 
        "islnk": false, 
        "isreg": true, 
        "issock": false, 
        "isuid": false, 
        "mimetype": "inode/x-empty", 
        "mode": "0666", 
        "mtime": 1667982695.7960083, 
        "nlink": 1, 
        "path": "/data/heihei.txt", 
        "pw_name": "zhangsan", 
        "readable": true, 
        "rgrp": true, 
        "roth": true, 
        "rusr": true, 
        "size": 0, 
        "uid": 1002, 
        "version": "257385908", 
        "wgrp": true, 
        "woth": true, 
        "writeable": true, 
        "wusr": true, 
        "xgrp": false, 
        "xoth": false, 
        "xusr": false
    }
}
常用返回值
exists:             #判断是否存在
isuid:              #调用用户的ID与所有者ID是否匹配

Unarchive模块

功能

解包解压缩

实现方法

  • 将ansible主机上的压缩包传到远程主机后解压缩至特定目录,设置copy=yes,此为默认值,可省略
  • 将远程主机上的某个压缩包解压缩到指定路径下,设置copy=no

选项

copy:默认为yes,当copy=yes,拷贝的文件是从ansible主机复制到远程主机上,如果设置为copy=no,会在远程主机上寻找src源文件

remote_src:和copy功能一样且互斥,yes表示在远程主机,不在ansible主机,no表示文件在ansible主机上

src:源路径,可以是ansible主机上的路径,也可以是远程主机(被管理端或者第三方主机)上的路径,如果是远程主机上的路径,则需要设置copy=no

dest:远程主机上的目标路径

mode:设置解压缩后的文件权限

使用

copy=yes
#创建目录
[root@master ~]# ansible test -m file -a "path=/data/http state=directory"
[WARNING]: Consider using the file module with state=directory rather than running 'mkdir'.  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.
192.168.239.20 | CHANGED | rc=0 >>
mkdir: created directory ‘/data/http’
192.168.239.30 | CHANGED | rc=0 >>
mkdir: created directory ‘/data/http’


#将控制端的压缩文件,复制到被控端进行解压
[root@master ~]# ansible test -m unarchive -a "src=/root/httpd-2.4.54.tar.gz dest=/data/http owner=root group=root"
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "dest": "/data/http", 
    "extract_results": {
        "cmd": [
            "/usr/bin/gtar", 
            "--extract", 
            "-C", 
            "/data/http", 
            "-z", 
            "--owner=root", 
            "--group=root", 
            "-f", 
            "/root/.ansible/tmp/ansible-tmp-1668080693.01-1583-110028244980064/source"
        ], 
        "err": "", 
        "out": "", 
        "rc": 0
    }, 
    "gid": 0, 
    "group": "root", 
    "handler": "TgzArchive", 
    "mode": "0755", 
    "owner": "root", 
    "secontext": "unconfined_u:object_r:default_t:s0", 
    "size": 26, 
    "src": "/root/.ansible/tmp/ansible-tmp-1668080693.01-1583-110028244980064/source", 
    "state": "directory", 
    "uid": 0
}
192.168.239.30 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "dest": "/data/http", 
    "extract_results": {
        "cmd": [
            "/usr/bin/gtar", 
            "--extract", 
            "-C", 
            "/data/http", 
            "-z", 
            "--owner=root", 
            "--group=root", 
            "-f", 
            "/root/.ansible/tmp/ansible-tmp-1668080693.01-1585-225195410928325/source"
        ], 
        "err": "", 
        "out": "", 
        "rc": 0
    }, 
    "gid": 0, 
    "group": "root", 
    "handler": "TgzArchive", 
    "mode": "0755", 
    "owner": "root", 
    "secontext": "unconfined_u:object_r:default_t:s0", 
    "size": 26, 
    "src": "/root/.ansible/tmp/ansible-tmp-1668080693.01-1585-225195410928325/source", 
    "state": "directory", 
    "uid": 0
}
[root@master ~]# ansible test -m shell -a "ls -l /data/http"
192.168.239.30 | CHANGED | rc=0 >>
total 4
drwxr-xr-x. 12 root root 4096 Jun  6 22:22 httpd-2.4.54
192.168.239.20 | CHANGED | rc=0 >>
total 4
drwxr-xr-x. 12 root root 4096 Jun  6 22:22 httpd-2.4.54

copy=no
#将被控端本地的压缩文件,在被控端本地进行解压
[root@master ~]# ansible test -m unarchive -a "src=/data/nginx/nginx.tar.gz copy=no dest=/data/nginx"
192.168.239.30 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "dest": "/data/nginx", 
    "extract_results": {
        "cmd": [
            "/usr/bin/gtar", 
            "--extract", 
            "-C", 
            "/data/nginx", 
            "-z", 
            "-f", 
            "/data/nginx/nginx.tar.gz"
        ], 
        "err": "", 
        "out": "", 
        "rc": 0
    }, 
    "gid": 0, 
    "group": "root", 
    "handler": "TgzArchive", 
    "mode": "0755", 
    "owner": "zhangsan", 
    "secontext": "unconfined_u:object_r:default_t:s0", 
    "size": 46, 
    "src": "/data/nginx/nginx.tar.gz", 
    "state": "directory", 
    "uid": 1002
}
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "dest": "/data/nginx", 
    "extract_results": {
        "cmd": [
            "/usr/bin/gtar", 
            "--extract", 
            "-C", 
            "/data/nginx", 
            "-z", 
            "-f", 
            "/data/nginx/nginx.tar.gz"
        ], 
        "err": "", 
        "out": "", 
        "rc": 0
    }, 
    "gid": 0, 
    "group": "root", 
    "handler": "TgzArchive", 
    "mode": "0755", 
    "owner": "zhangsan", 
    "secontext": "unconfined_u:object_r:default_t:s0", 
    "size": 46, 
    "src": "/data/nginx/nginx.tar.gz", 
    "state": "directory", 
    "uid": 1002
}


[root@master ~]# ansible test -m shell -a "ls -l /data/nginx"
192.168.239.30 | CHANGED | rc=0 >>
total 1016
drwxr-xr-x. 8     1001 1001     158 Apr 21  2020 nginx-1.18.0
-rw-r--r--. 1 zhangsan root 1039530 Nov  7 16:28 nginx.tar.gz
192.168.239.20 | CHANGED | rc=0 >>
total 1016
drwxr-xr-x. 8     1001 1001     158 Apr 21  2020 nginx-1.18.0
-rw-r--r--. 1 zhangsan root 1039530 Nov  7 16:28 nginx.tar.gz

remote_src=yes
#被控端从网上下载压缩包,然后在被控端本地进行解压
[root@master ~]# ansible test -m unarchive -a "src=https://golang.google.cn/dl/go1.18.8.src.tar.gz dest=/data/ mode=777 remote_src=yes"
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "dest": "/data/", 
    "extract_results": {
        "cmd": [
            "/usr/bin/gtar", 
            "--extract", 
            "-C", 
            "/data/", 
            "-z", 
            "-f", 
            "/root/.ansible/tmp/ansible-tmp-1668418428.43-1714-140178954477710/go1.18.8.src.tarJIYoId.gz"
        ], 
        "err": "", 
        "out": "", 
        "rc": 0
    }, 
    "gid": 0, 
    "group": "root", 
    "handler": "TgzArchive", 
    "mode": "0755", 
    "owner": "zhangsan", 
    "secontext": "unconfined_u:object_r:default_t:s0", 
    "size": 191, 
    "src": "/root/.ansible/tmp/ansible-tmp-1668418428.43-1714-140178954477710/go1.18.8.src.tarJIYoId.gz", 
    "state": "directory", 
    "uid": 1002
}
192.168.239.30 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "dest": "/data/", 
    "extract_results": {
        "cmd": [
            "/usr/bin/gtar", 
            "--extract", 
            "-C", 
            "/data/", 
            "-z", 
            "-f", 
            "/root/.ansible/tmp/ansible-tmp-1668418428.43-1716-100689500958007/go1.18.8.src.tarlfoR8i.gz"
        ], 
        "err": "", 
        "out": "", 
        "rc": 0
    }, 
    "gid": 0, 
    "group": "root", 
    "handler": "TgzArchive", 
    "mode": "0755", 
    "owner": "zhangsan", 
    "secontext": "unconfined_u:object_r:default_t:s0", 
    "size": 191, 
    "src": "/root/.ansible/tmp/ansible-tmp-1668418428.43-1716-100689500958007/go1.18.8.src.tarlfoR8i.gz", 
    "state": "directory", 
    "uid": 1002
}

[root@master ~]# ansible test -m shell -a "ls -l /data/ | grep go"
192.168.239.30 | CHANGED | rc=0 >>
drwxrwxrwx. 8 root     root 235 Nov  1 04:17 go
192.168.239.20 | CHANGED | rc=0 >>
drwxrwxrwx. 8 root     root 235 Nov  1 04:17 go

Archive模块

功能

打包压缩保存在被管理节点

选项

path                #指定源路径,被控端本地
dest                #指定压缩后的路径,被控端本地
owner               #指定属主
mode                #指定权限
format              #指定要使用的压缩类型

使用

#将被控端本地的文件进行压缩,压缩后存放在被控端本地
[root@master ~]# ansible test -m archive -a "path=/var/log/ dest=/data/log.tar.gz owner=zhangsan mode=0600"
192.168.239.30 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "archived": [
        "/var/log/tallylog", 
        "/var/log/grubby_prune_debug", 
        "/var/log/lastlog", 
        "/var/log/boot.log", 
        "/var/log/firewalld", 
        "/var/log/dmesg.old", 
        "/var/log/yum.log", 
        "/var/log/cron-20220929", 
        "/var/log/maillog-20220929", 
        "/var/log/messages-20220929", 
        "/var/log/secure-20220929", 
        "/var/log/spooler-20220929", 
        "/var/log/cron-20221013", 
        "/var/log/maillog-20221013", 
        "/var/log/messages-20221013", 
        "/var/log/secure-20221013", 
        "/var/log/spooler-20221013", 
        "/var/log/boot.log-20221017", 
        "/var/log/boot.log-20221031", 
        "/var/log/cron-20221031", 
        "/var/log/maillog-20221031", 
        "/var/log/messages-20221031", 
        "/var/log/secure-20221031", 
        "/var/log/spooler-20221031", 
        "/var/log/boot.log-20221103", 
        "/var/log/btmp-20221103", 
        "/var/log/btmp", 
        "/var/log/boot.log-20221104", 
        "/var/log/boot.log-20221107", 
        "/var/log/cron-20221107", 
        "/var/log/cron", 
        "/var/log/maillog-20221107", 
        "/var/log/maillog", 
        "/var/log/messages-20221107", 
        "/var/log/messages", 
        "/var/log/secure-20221107", 
        "/var/log/secure", 
        "/var/log/spooler-20221107", 
        "/var/log/spooler", 
        "/var/log/wtmp-20221107", 
        "/var/log/wtmp", 
        "/var/log/boot.log-20221109", 
        "/var/log/boot.log-20221110", 
        "/var/log/dmesg", 
        "/var/log/tuned/tuned.log", 
        "/var/log/audit/audit.log.2", 
        "/var/log/audit/audit.log.1", 
        "/var/log/audit/audit.log", 
        "/var/log/anaconda/anaconda.log", 
        "/var/log/anaconda/syslog", 
        "/var/log/anaconda/X.log", 
        "/var/log/anaconda/program.log", 
        "/var/log/anaconda/packaging.log", 
        "/var/log/anaconda/storage.log", 
        "/var/log/anaconda/ifcfg.log", 
        "/var/log/anaconda/ks-script-ZEjYtM.log", 
        "/var/log/anaconda/ks-script-ryxzsP.log", 
        "/var/log/anaconda/journal.log", 
        "/var/log/httpd/access_log", 
        "/var/log/httpd/error_log-20221013", 
        "/var/log/httpd/error_log"
    ], 
    "arcroot": "/var/log/", 
    "changed": true, 
    "dest": "/data/log.tar.gz", 
    "expanded_exclude_paths": [], 
    "expanded_paths": [
        "/var/log/"
    ], 
    "gid": 0, 
    "group": "root", 
    "missing": [], 
    "mode": "0600", 
    "owner": "zhangsan", 
    "secontext": "unconfined_u:object_r:default_t:s0", 
    "size": 1285287, 
    "state": "file", 
    "uid": 1002
}
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "archived": [
        "/var/log/tallylog", 
        "/var/log/grubby_prune_debug", 
        "/var/log/lastlog", 
        "/var/log/boot.log", 
        "/var/log/firewalld", 
        "/var/log/dmesg.old", 
        "/var/log/yum.log", 
        "/var/log/cron-20220929", 
        "/var/log/maillog-20220929", 
        "/var/log/messages-20220929", 
        "/var/log/secure-20220929", 
        "/var/log/spooler-20220929", 
        "/var/log/cron-20221013", 
        "/var/log/maillog-20221013", 
        "/var/log/messages-20221013", 
        "/var/log/secure-20221013", 
        "/var/log/spooler-20221013", 
        "/var/log/boot.log-20221014", 
        "/var/log/boot.log-20221017", 
        "/var/log/boot.log-20221031", 
        "/var/log/cron-20221031", 
        "/var/log/maillog-20221031", 
        "/var/log/messages-20221031", 
        "/var/log/secure-20221031", 
        "/var/log/spooler-20221031", 
        "/var/log/boot.log-20221104", 
        "/var/log/btmp-20221104", 
        "/var/log/btmp", 
        "/var/log/boot.log-20221107", 
        "/var/log/cron-20221107", 
        "/var/log/cron", 
        "/var/log/maillog-20221107", 
        "/var/log/maillog", 
        "/var/log/messages-20221107", 
        "/var/log/messages", 
        "/var/log/secure-20221107", 
        "/var/log/secure", 
        "/var/log/spooler-20221107", 
        "/var/log/spooler", 
        "/var/log/wtmp-20221107", 
        "/var/log/wtmp", 
        "/var/log/boot.log-20221109", 
        "/var/log/boot.log-20221110", 
        "/var/log/dmesg", 
        "/var/log/tuned/tuned.log", 
        "/var/log/audit/audit.log.2", 
        "/var/log/audit/audit.log.1", 
        "/var/log/audit/audit.log", 
        "/var/log/anaconda/anaconda.log", 
        "/var/log/anaconda/syslog", 
        "/var/log/anaconda/X.log", 
        "/var/log/anaconda/program.log", 
        "/var/log/anaconda/packaging.log", 
        "/var/log/anaconda/storage.log", 
        "/var/log/anaconda/ifcfg.log", 
        "/var/log/anaconda/ks-script-ZEjYtM.log", 
        "/var/log/anaconda/ks-script-ryxzsP.log", 
        "/var/log/anaconda/journal.log", 
        "/var/log/httpd/access_log", 
        "/var/log/httpd/error_log-20221013", 
        "/var/log/httpd/error_log"
    ], 
    "arcroot": "/var/log/", 
    "changed": true, 
    "dest": "/data/log.tar.gz", 
    "expanded_exclude_paths": [], 
    "expanded_paths": [
        "/var/log/"
    ], 
    "gid": 0, 
    "group": "root", 
    "missing": [], 
    "mode": "0600", 
    "owner": "zhangsan", 
    "secontext": "unconfined_u:object_r:default_t:s0", 
    "size": 1290948, 
    "state": "file", 
    "uid": 1002
}


[root@master ~]# ansible test -m shell -a "ls -l /data/ | grep log"
192.168.239.30 | CHANGED | rc=0 >>
-rw-------. 1 zhangsan root 1285287 Nov 14 17:50 log.tar.gz
192.168.239.20 | CHANGED | rc=0 >>
-rw-------. 1 zhangsan root 1290948 Nov 14 17:50 log.tar.gz

Cron模块

功能

计划任务

选项

minute              #设置分钟
hour                #设置小时
day                 #设置日
month               #设置月
weekday             #设置周
上述选项,如不人为设定默认为“*”,表示每一。如果什么都不设置表示每分钟执行一次。
    minute=15       #表示每小时的15分,执行一次
    minute=*/3      #表示每三分钟执行一次


special_time        #指定周期性质的时间。使用上述选项,表示每;还可以使用reboot表示重启后执行
    special_time=hour       #表示每小时
    special_time=reboot     #表示重启后

user                #设置当前计划任务属于哪个用户,如果不使用此参数,默认为管理员

job                 #指定计划任务中需要执行的命令或者脚本,需要用引号引起来

name                #设置计划任务的名称

state               #根据名称修改或删除对应的任务,当删除任务是state=absent

disable             #根据名称使对应的任务”失效”(注释掉对应的任务)。注意,使用此参数时,除了需要指定任务的名称,还需要同时指定任务的job 以及任务的时间设定,而且任务的时间设定必须和对应任务完全相同,否则在注释任务的同时,任务的时间设定会被修改,除非你确定这样做。

backup              #如果此参数的值设置为 yes,那么当修改或者删除对应的计划任务时,会先对计划任务进行备份,然后再对计划任务进行修改或者删除,cron 模块会在远程主机的 /tmp 目录下创建备份文件,以 crontab 开头并且随机加入一些字符,具体的备份文件名称会在返回信息的backup_file字段中看到,推荐将此此参数设置为 yes。

使用

#创建任务
[root@master ~]# ansible test -m cron -a "minute=*/1 job='/usr/sbin/ntpdate ntp2.aliyun.com' name=sysnctime"
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "envs": [], 
    "jobs": [
        "sysnctime"
    ]
}
192.168.239.30 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "envs": [], 
    "jobs": [
        "sysnctime"
    ]
}

[root@master ~]# ansible test -m shell -a "date"
192.168.239.30 | CHANGED | rc=0 >>
Mon Nov 14 10:47:37 CST 2022
192.168.239.20 | CHANGED | rc=0 >>
Mon Nov 14 10:47:37 CST 2022

#删除计划任务
[root@master ~]# ansible test -m cron -a "name=sysnctime state=absent"
192.168.239.30 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "envs": [], 
    "jobs": []
}
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "envs": [], 
    "jobs": []
}

#创建重启后执行的计划任务
[root@master ~]# ansible test -m cron -a "name=echo special_time=reboot job='echo hahaha'"
192.168.239.30 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "envs": [], 
    "jobs": [
        "echo"
    ]
}
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "envs": [], 
    "jobs": [
        "echo"
    ]
}

#重启设备
[root@master ~]# ansible test -m shell -a "reboot"
192.168.239.20 | UNREACHABLE! => {
    "changed": false, 
    "msg": "Failed to connect to the host via ssh: Shared connection to 192.168.239.20 closed.", 
    "unreachable": true
}
192.168.239.30 | UNREACHABLE! => {
    "changed": false, 
    "msg": "Failed to connect to the host via ssh: Shared connection to 192.168.239.30 closed.", 
    "unreachable": true
}

#查看计划任务
[root@master ~]# ansible test -m shell -a "crontab -u root -l"
192.168.239.30 | CHANGED | rc=0 >>
#Ansible: echo
@reboot echo hahaha
192.168.239.20 | CHANGED | rc=0 >>
#Ansible: echo
@reboot echo hahaha

#查看执行日志
[root@master ~]# ansible test -m shell -a "tail -2 /var/spool/mail/root"
192.168.239.30 | CHANGED | rc=0 >>
hahaha
192.168.239.20 | CHANGED | rc=0 >>
hahaha

Hostname模块

功能

管理主机名

选项

name                #指定修改后的主机名

使用

[root@master ~]# ansible test1 -m shell -a "hostname"
192.168.239.20 | CHANGED | rc=0 >>
slave1


#修改主机列表test1中主机的主机名
[root@master ~]# ansible test1 -m hostname -a "name=test1"
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "ansible_domain": "", 
        "ansible_fqdn": "test1", 
        "ansible_hostname": "test1", 
        "ansible_nodename": "test1", 
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "name": "test1"
}


[root@master ~]# ansible test1 -m shell -a "hostname"
192.168.239.20 | CHANGED | rc=0 >>
test1

Yum与Apt模块

功能

  • yum 管理软件包,只支持RHEL,CentOS,fedora,不支持Ubuntu其它版本
  • apt 模块管理 Debian 相关版本的软件包

选项

name                #指定需要安转的软件名
state               #是安装(present、latest),还是删除(absent)
        latest      #用于更新
        如果不指定state默认为present
enablerepo          #指定安装软件包时临时启用的yum源
exclude             #当state=present或latest时要排除的包名

使用

基本用法
#安装httpd
[root@master ~]# ansible test1 -m yum -a "name=httpd state=present"
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "changes": {
        "installed": [
            "httpd"
        ]
    }, 
    "msg": "", 
    "rc": 0, 
    "results": [
        "Loaded plugins: fastestmirror\nLoading mirror speeds from cached hostfile\n * base: mirrors.aliyun.com\n * extras: mirrors.aliyun.com\n * updates: mirrors.aliyun.com\nResolving Dependencies\n--> Running transaction check\n---> Package httpd.x86_64 0:2.4.6-97.el7.centos.5 will be installed\n--> Finished Dependency Resolution\n\nDependencies Resolved\n\n================================================================================\n Package      Arch          Version                        Repository      Size\n================================================================================\nInstalling:\n httpd        x86_64        2.4.6-97.el7.centos.5          updates        2.7 M\n\nTransaction Summary\n================================================================================\nInstall  1 Package\n\nTotal download size: 2.7 M\nInstalled size: 9.4 M\nDownloading packages:\nRunning transaction check\nRunning transaction test\nTransaction test succeeded\nRunning transaction\n  Installing : httpd-2.4.6-97.el7.centos.5.x86_64                           1/1 \n  Verifying  : httpd-2.4.6-97.el7.centos.5.x86_64                           1/1 \n\nInstalled:\n  httpd.x86_64 0:2.4.6-97.el7.centos.5                                          \n\nComplete!\n"
    ]
}


#卸载httpd
[root@master ~]# ansible test1 -m yum -a "name=httpd state=absent"
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "changes": {
        "removed": [
            "httpd"
        ]
    }, 
    "msg": "", 
    "rc": 0, 
    "results": [
        "Loaded plugins: fastestmirror\nResolving Dependencies\n--> Running transaction check\n---> Package httpd.x86_64 0:2.4.6-97.el7.centos.5 will be erased\n--> Finished Dependency Resolution\n\nDependencies Resolved\n\n================================================================================\n Package      Arch          Version                       Repository       Size\n================================================================================\nRemoving:\n httpd        x86_64        2.4.6-97.el7.centos.5         @updates        9.4 M\n\nTransaction Summary\n================================================================================\nRemove  1 Package\n\nInstalled size: 9.4 M\nDownloading packages:\nRunning transaction check\nRunning transaction test\nTransaction test succeeded\nRunning transaction\n  Erasing    : httpd-2.4.6-97.el7.centos.5.x86_64                           1/1 \n  Verifying  : httpd-2.4.6-97.el7.centos.5.x86_64                           1/1 \n\nRemoved:\n  httpd.x86_64 0:2.4.6-97.el7.centos.5                                          \n\nComplete!\n"
    ]
}

启用epel源
[root@master ~]# ansible test1 -m yum -a "name=nginx state=present enablerepo=epel"
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "changes": {
        "installed": [
            "nginx"
        ]
    }, 
    "msg": "warning: /var/cache/yum/x86_64/7/epel/packages/openssl11-libs-1.1.1k-4.el7.x86_64.rpm: Header V4 RSA/SHA256 Signature, key ID 352c64e5: NOKEY\nImporting GPG key 0x352C64E5:\n Userid     : \"Fedora EPEL (7) <epel@fedoraproject.org>\"\n Fingerprint: 91e9 7d7c 4a5e 96f1 7f3e 888f 6a2f aea2 352c 64e5\n Package    : epel-release-7-11.noarch (@extras)\n From       : /etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7\n", 
    "rc": 0, 
    "results": [
        "Loaded plugins: fastestmirror\nLoading mirror speeds from cached hostfile\n * base: mirrors.aliyun.com\n * epel: epel.mirror.angkasa.id\n * extras: mirrors.aliyun.com\n * updates: mirrors.aliyun.com\nResolving Dependencies\n--> Running transaction check\n---> Package nginx.x86_64 1:1.20.1-9.el7 will be installed\n--> Processing Dependency: nginx-filesystem = 1:1.20.1-9.el7 for package: 1:nginx-1.20.1-9.el7.x86_64\n--> Processing Dependency: libcrypto.so.1.1(OPENSSL_1_1_0)(64bit) for package: 1:nginx-1.20.1-9.el7.x86_64\n--> Processing Dependency: libssl.so.1.1(OPENSSL_1_1_0)(64bit) for package: 1:nginx-1.20.1-9.el7.x86_64\n--> Processing Dependency: libssl.so.1.1(OPENSSL_1_1_1)(64bit) for package: 1:nginx-1.20.1-9.el7.x86_64\n--> Processing Dependency: nginx-filesystem for package: 1:nginx-1.20.1-9.el7.x86_64\n--> Processing Dependency: redhat-indexhtml for package: 1:nginx-1.20.1-9.el7.x86_64\n--> Processing Dependency: libcrypto.so.1.1()(64bit) for package: 1:nginx-1.20.1-9.el7.x86_64\n--> Processing Dependency: libprofiler.so.0()(64bit) for package: 1:nginx-1.20.1-9.el7.x86_64\n--> Processing Dependency: libssl.so.1.1()(64bit) for package: 1:nginx-1.20.1-9.el7.x86_64\n--> Running transaction check\n---> Package centos-indexhtml.noarch 0:7-9.el7.centos will be installed\n---> Package gperftools-libs.x86_64 0:2.6.1-1.el7 will be installed\n---> Package nginx-filesystem.noarch 1:1.20.1-9.el7 will be installed\n---> Package openssl11-libs.x86_64 1:1.1.1k-4.el7 will be installed\n--> Finished Dependency Resolution\n\nDependencies Resolved\n\n================================================================================\n Package                 Arch          Version                Repository   Size\n================================================================================\nInstalling:\n nginx                   x86_64        1:1.20.1-9.el7         epel        587 k\nInstalling for dependencies:\n centos-indexhtml        noarch        7-9.el7.centos         base         92 k\n gperftools-libs         x86_64        2.6.1-1.el7            base        272 k\n nginx-filesystem        noarch        1:1.20.1-9.el7         epel         24 k\n openssl11-libs          x86_64        1:1.1.1k-4.el7         epel        1.5 M\n\nTransaction Summary\n================================================================================\nInstall  1 Package (+4 Dependent packages)\n\nTotal size: 2.4 M\nInstalled size: 6.7 M\nDownloading packages:\nRetrieving key from file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7\nRunning transaction check\nRunning transaction test\nTransaction test succeeded\nRunning transaction\n  Installing : gperftools-libs-2.6.1-1.el7.x86_64                           1/5 \n  Installing : 1:nginx-filesystem-1.20.1-9.el7.noarch                       2/5 \n  Installing : centos-indexhtml-7-9.el7.centos.noarch                       3/5 \n  Installing : 1:openssl11-libs-1.1.1k-4.el7.x86_64                         4/5 \n  Installing : 1:nginx-1.20.1-9.el7.x86_64                                  5/5 \n  Verifying  : 1:openssl11-libs-1.1.1k-4.el7.x86_64                         1/5 \n  Verifying  : centos-indexhtml-7-9.el7.centos.noarch                       2/5 \n  Verifying  : 1:nginx-filesystem-1.20.1-9.el7.noarch                       3/5 \n  Verifying  : gperftools-libs-2.6.1-1.el7.x86_64                           4/5 \n  Verifying  : 1:nginx-1.20.1-9.el7.x86_64                                  5/5 \n\nInstalled:\n  nginx.x86_64 1:1.20.1-9.el7                                                   \n\nDependency Installed:\n  centos-indexhtml.noarch 0:7-9.el7.centos gperftools-libs.x86_64 0:2.6.1-1.el7\n  nginx-filesystem.noarch 1:1.20.1-9.el7   openssl11-libs.x86_64 1:1.1.1k-4.el7\n\nComplete!\n"
    ]
}

Yum_repository模块

作用

帮助我们管理远程主机上的yum仓库,一般都是通过yaml文件来编写一个剧本,因为正常的yum仓库的配置文件内容不是一行两行而且每一行都比较长,所以习惯写在文件中。

选项

name        #用于指定要操作的唯一的仓库ID,也就是”.repo”配置文件中每个仓库对应的”中括号”内的仓库ID
            #注意:这里说是ID,但是并不是数字,比如我们的[base],这个仓库的ID就是base
baseurl     #设置yum仓库的baseurl
description #设置仓库的注释信息,也就是”.repo”配置文件中每个仓库对应的”name字段”对应的内容

file        #设置仓库的配置文件名称,即设置”.repo”配置文件的文件名前缀,在不使用此参数的情况下,默认以name参数的仓库ID作为”.repo”配置文件的文件名前缀,同一个”.repo” 配置文件中可以存在多个 yum 源。

enabled     #设置是否激活对应的yum源,此参数默认值为yes,表示启用对应的yum源,设置为no表示不启用对应的 yum源。

gpgcheck    #设置是否开启rpm包验证功能,默认值为no,表示不启用包验证,设置为yes表示开启包验证功能。

gpgcakey    #当gpgcheck设置为yes时,需要使用此参数指定验证包所需的公钥。

state       #默认值为present,当值设置为absent时,表示删除对应的yum源。

使用

#创建一个yum仓库配置文件
[root@test-master ~]# ansible test1 -m yum_repository -a "name=alyepel description=aliyun_epel baseurl=https://mirrors.aliyun.com/epel/$releasever\Server/$basearch/ file=test"
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "repo": "alyepel", 
    "state": "present"
}

#查看确实已经生成一个配置文件
[root@test1 yum.repos.d]# cat test.repo 
[alyepel]
baseurl = https://mirrors.aliyun.com/epel/\Server//
name = aliyun_epel

Service模块

功能

管理服务,替换shell模块下使用systemctl命令

选项

name            #指定服务名
state           #指定服务的状态(started开启,stopped关闭,restarted重启,reloaded重新加载)
enable          #服务是否应该在引导时启动

使用

#启动服务
[root@master ~]# ansible test1 -m service -a "name=nginx state=started"
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "name": "nginx", 
    "state": "started", 
    "status": {
        "ActiveEnterTimestampMonotonic": "0", 
        "ActiveExitTimestampMonotonic": "0", 
        "ActiveState": "inactive", 
        "After": "system.slice nss-lookup.target basic.target remote-fs.target tmp.mount network-online.target systemd-journald.socket -.mount", 
        "AllowIsolate": "no", 
        "AmbientCapabilities": "0", 
        "AssertResult": "no", 
        "AssertTimestampMonotonic": "0", 
        "Before": "shutdown.target", 
        "BlockIOAccounting": "no", 
        "BlockIOWeight": "18446744073709551615", 
        "CPUAccounting": "no", 
        "CPUQuotaPerSecUSec": "infinity", 
        "CPUSchedulingPolicy": "0", 
        "CPUSchedulingPriority": "0", 
        "CPUSchedulingResetOnFork": "no", 
        "CPUShares": "18446744073709551615", 
        "CanIsolate": "no", 
        "CanReload": "yes", 
        "CanStart": "yes", 
        "CanStop": "yes", 
        "CapabilityBoundingSet": "18446744073709551615", 
        "CollectMode": "inactive", 
        "ConditionResult": "no", 
        "ConditionTimestampMonotonic": "0", 
        "Conflicts": "shutdown.target", 
        "ControlPID": "0", 
        "DefaultDependencies": "yes", 
        "Delegate": "no", 
        "Description": "The nginx HTTP and reverse proxy server", 
        "DevicePolicy": "auto", 
        "ExecMainCode": "0", 
        "ExecMainExitTimestampMonotonic": "0", 
        "ExecMainPID": "0", 
        "ExecMainStartTimestampMonotonic": "0", 
        "ExecMainStatus": "0", 
        "ExecReload": "{ path=/usr/sbin/nginx ; argv[]=/usr/sbin/nginx -s reload ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 }", 
        "ExecStart": "{ path=/usr/sbin/nginx ; argv[]=/usr/sbin/nginx ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 }", 
        "ExecStartPre": "{ path=/usr/sbin/nginx ; argv[]=/usr/sbin/nginx -t ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 }", 
        "FailureAction": "none", 
        "FileDescriptorStoreMax": "0", 
        "FragmentPath": "/usr/lib/systemd/system/nginx.service", 
        "GuessMainPID": "yes", 
        "IOScheduling": "0", 
        "Id": "nginx.service", 
        "IgnoreOnIsolate": "no", 
        "IgnoreOnSnapshot": "no", 
        "IgnoreSIGPIPE": "yes", 
        "InactiveEnterTimestampMonotonic": "0", 
        "InactiveExitTimestampMonotonic": "0", 
        "JobTimeoutAction": "none", 
        "JobTimeoutUSec": "0", 
        "KillMode": "process", 
        "KillSignal": "3", 
        "LimitAS": "18446744073709551615", 
        "LimitCORE": "18446744073709551615", 
        "LimitCPU": "18446744073709551615", 
        "LimitDATA": "18446744073709551615", 
        "LimitFSIZE": "18446744073709551615", 
        "LimitLOCKS": "18446744073709551615", 
        "LimitMEMLOCK": "65536", 
        "LimitMSGQUEUE": "819200", 
        "LimitNICE": "0", 
        "LimitNOFILE": "4096", 
        "LimitNPROC": "7183", 
        "LimitRSS": "18446744073709551615", 
        "LimitRTPRIO": "0", 
        "LimitRTTIME": "18446744073709551615", 
        "LimitSIGPENDING": "7183", 
        "LimitSTACK": "18446744073709551615", 
        "LoadState": "loaded", 
        "MainPID": "0", 
        "MemoryAccounting": "no", 
        "MemoryCurrent": "18446744073709551615", 
        "MemoryLimit": "18446744073709551615", 
        "MountFlags": "0", 
        "Names": "nginx.service", 
        "NeedDaemonReload": "no", 
        "Nice": "0", 
        "NoNewPrivileges": "no", 
        "NonBlocking": "no", 
        "NotifyAccess": "none", 
        "OOMScoreAdjust": "0", 
        "OnFailureJobMode": "replace", 
        "PIDFile": "/run/nginx.pid", 
        "PermissionsStartOnly": "no", 
        "PrivateDevices": "no", 
        "PrivateNetwork": "no", 
        "PrivateTmp": "yes", 
        "ProtectHome": "no", 
        "ProtectSystem": "no", 
        "RefuseManualStart": "no", 
        "RefuseManualStop": "no", 
        "RemainAfterExit": "no", 
        "Requires": "basic.target system.slice -.mount", 
        "RequiresMountsFor": "/var/tmp", 
        "Restart": "no", 
        "RestartUSec": "100ms", 
        "Result": "success", 
        "RootDirectoryStartOnly": "no", 
        "RuntimeDirectoryMode": "0755", 
        "SameProcessGroup": "no", 
        "SecureBits": "0", 
        "SendSIGHUP": "no", 
        "SendSIGKILL": "yes", 
        "Slice": "system.slice", 
        "StandardError": "inherit", 
        "StandardInput": "null", 
        "StandardOutput": "journal", 
        "StartLimitAction": "none", 
        "StartLimitBurst": "5", 
        "StartLimitInterval": "10000000", 
        "StartupBlockIOWeight": "18446744073709551615", 
        "StartupCPUShares": "18446744073709551615", 
        "StatusErrno": "0", 
        "StopWhenUnneeded": "no", 
        "SubState": "dead", 
        "SyslogLevelPrefix": "yes", 
        "SyslogPriority": "30", 
        "SystemCallErrorNumber": "0", 
        "TTYReset": "no", 
        "TTYVHangup": "no", 
        "TTYVTDisallocate": "no", 
        "TasksAccounting": "no", 
        "TasksCurrent": "18446744073709551615", 
        "TasksMax": "18446744073709551615", 
        "TimeoutStartUSec": "1min 30s", 
        "TimeoutStopUSec": "5s", 
        "TimerSlackNSec": "50000", 
        "Transient": "no", 
        "Type": "forking", 
        "UMask": "0022", 
        "UnitFilePreset": "disabled", 
        "UnitFileState": "disabled", 
        "Wants": "network-online.target", 
        "WatchdogTimestampMonotonic": "0", 
        "WatchdogUSec": "0"
    }
}
[root@master ~]# ansible test1 -m shell -a "ss -ntl | grep 80"
192.168.239.20 | CHANGED | rc=0 >>
LISTEN     0      128          *:80                       *:*                  
LISTEN     0      128       [::]:80                    [::]:*  
#停止服务
[root@master ~]# ansible test1 -m service -a "name=nginx state=stopped"
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "name": "nginx", 
    "state": "stopped", 
    "status": {
        "ActiveEnterTimestamp": "Mon 2022-11-14 23:54:03 CST", 
        "ActiveEnterTimestampMonotonic": "16899392841", 
        "ActiveExitTimestampMonotonic": "0", 
        "ActiveState": "active", 
        "After": "nss-lookup.target remote-fs.target systemd-journald.socket basic.target system.slice -.mount tmp.mount network-online.target", 
        "AllowIsolate": "no", 
        "AmbientCapabilities": "0", 
        "AssertResult": "yes", 
        "AssertTimestamp": "Mon 2022-11-14 23:54:03 CST", 
        "AssertTimestampMonotonic": "16899363254", 
        "Before": "shutdown.target", 
        "BlockIOAccounting": "no", 
        "BlockIOWeight": "18446744073709551615", 
        "CPUAccounting": "no", 
        "CPUQuotaPerSecUSec": "infinity", 
        "CPUSchedulingPolicy": "0", 
        "CPUSchedulingPriority": "0", 
        "CPUSchedulingResetOnFork": "no", 
        "CPUShares": "18446744073709551615", 
        "CanIsolate": "no", 
        "CanReload": "yes", 
        "CanStart": "yes", 
        "CanStop": "yes", 
        "CapabilityBoundingSet": "18446744073709551615", 
        "CollectMode": "inactive", 
        "ConditionResult": "yes", 
        "ConditionTimestamp": "Mon 2022-11-14 23:54:03 CST", 
        "ConditionTimestampMonotonic": "16899363254", 
        "Conflicts": "shutdown.target", 
        "ControlGroup": "/system.slice/nginx.service", 
        "ControlPID": "0", 
        "DefaultDependencies": "yes", 
        "Delegate": "no", 
        "Description": "The nginx HTTP and reverse proxy server", 
        "DevicePolicy": "auto", 
        "ExecMainCode": "0", 
        "ExecMainExitTimestampMonotonic": "0", 
        "ExecMainPID": "4822", 
        "ExecMainStartTimestamp": "Mon 2022-11-14 23:54:03 CST", 
        "ExecMainStartTimestampMonotonic": "16899392808", 
        "ExecMainStatus": "0", 
        "ExecReload": "{ path=/usr/sbin/nginx ; argv[]=/usr/sbin/nginx -s reload ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 }", 
        "ExecStart": "{ path=/usr/sbin/nginx ; argv[]=/usr/sbin/nginx ; ignore_errors=no ; start_time=[Mon 2022-11-14 23:54:03 CST] ; stop_time=[Mon 2022-11-14 23:54:03 CST] ; pid=4820 ; code=exited ; status=0 }", 
        "ExecStartPre": "{ path=/usr/sbin/nginx ; argv[]=/usr/sbin/nginx -t ; ignore_errors=no ; start_time=[Mon 2022-11-14 23:54:03 CST] ; stop_time=[Mon 2022-11-14 23:54:03 CST] ; pid=4817 ; code=exited ; status=0 }", 
        "FailureAction": "none", 
        "FileDescriptorStoreMax": "0", 
        "FragmentPath": "/usr/lib/systemd/system/nginx.service", 
        "GuessMainPID": "yes", 
        "IOScheduling": "0", 
        "Id": "nginx.service", 
        "IgnoreOnIsolate": "no", 
        "IgnoreOnSnapshot": "no", 
        "IgnoreSIGPIPE": "yes", 
        "InactiveEnterTimestampMonotonic": "0", 
        "InactiveExitTimestamp": "Mon 2022-11-14 23:54:03 CST", 
        "InactiveExitTimestampMonotonic": "16899364190", 
        "JobTimeoutAction": "none", 
        "JobTimeoutUSec": "0", 
        "KillMode": "process", 
        "KillSignal": "3", 
        "LimitAS": "18446744073709551615", 
        "LimitCORE": "18446744073709551615", 
        "LimitCPU": "18446744073709551615", 
        "LimitDATA": "18446744073709551615", 
        "LimitFSIZE": "18446744073709551615", 
        "LimitLOCKS": "18446744073709551615", 
        "LimitMEMLOCK": "65536", 
        "LimitMSGQUEUE": "819200", 
        "LimitNICE": "0", 
        "LimitNOFILE": "4096", 
        "LimitNPROC": "7183", 
        "LimitRSS": "18446744073709551615", 
        "LimitRTPRIO": "0", 
        "LimitRTTIME": "18446744073709551615", 
        "LimitSIGPENDING": "7183", 
        "LimitSTACK": "18446744073709551615", 
        "LoadState": "loaded", 
        "MainPID": "4822", 
        "MemoryAccounting": "no", 
        "MemoryCurrent": "18446744073709551615", 
        "MemoryLimit": "18446744073709551615", 
        "MountFlags": "0", 
        "Names": "nginx.service", 
        "NeedDaemonReload": "no", 
        "Nice": "0", 
        "NoNewPrivileges": "no", 
        "NonBlocking": "no", 
        "NotifyAccess": "none", 
        "OOMScoreAdjust": "0", 
        "OnFailureJobMode": "replace", 
        "PIDFile": "/run/nginx.pid", 
        "PermissionsStartOnly": "no", 
        "PrivateDevices": "no", 
        "PrivateNetwork": "no", 
        "PrivateTmp": "yes", 
        "ProtectHome": "no", 
        "ProtectSystem": "no", 
        "RefuseManualStart": "no", 
        "RefuseManualStop": "no", 
        "RemainAfterExit": "no", 
        "Requires": "basic.target system.slice -.mount", 
        "RequiresMountsFor": "/var/tmp", 
        "Restart": "no", 
        "RestartUSec": "100ms", 
        "Result": "success", 
        "RootDirectoryStartOnly": "no", 
        "RuntimeDirectoryMode": "0755", 
        "SameProcessGroup": "no", 
        "SecureBits": "0", 
        "SendSIGHUP": "no", 
        "SendSIGKILL": "yes", 
        "Slice": "system.slice", 
        "StandardError": "inherit", 
        "StandardInput": "null", 
        "StandardOutput": "journal", 
        "StartLimitAction": "none", 
        "StartLimitBurst": "5", 
        "StartLimitInterval": "10000000", 
        "StartupBlockIOWeight": "18446744073709551615", 
        "StartupCPUShares": "18446744073709551615", 
        "StatusErrno": "0", 
        "StopWhenUnneeded": "no", 
        "SubState": "running", 
        "SyslogLevelPrefix": "yes", 
        "SyslogPriority": "30", 
        "SystemCallErrorNumber": "0", 
        "TTYReset": "no", 
        "TTYVHangup": "no", 
        "TTYVTDisallocate": "no", 
        "TasksAccounting": "no", 
        "TasksCurrent": "18446744073709551615", 
        "TasksMax": "18446744073709551615", 
        "TimeoutStartUSec": "1min 30s", 
        "TimeoutStopUSec": "5s", 
        "TimerSlackNSec": "50000", 
        "Transient": "no", 
        "Type": "forking", 
        "UMask": "0022", 
        "UnitFilePreset": "disabled", 
        "UnitFileState": "disabled", 
        "Wants": "network-online.target", 
        "WatchdogTimestamp": "Mon 2022-11-14 23:54:03 CST", 
        "WatchdogTimestampMonotonic": "16899392822", 
        "WatchdogUSec": "0"
    }
}
[root@master ~]# ansible test1 -m shell -a "ss -ntl | grep 80"
192.168.239.20 | FAILED | rc=1 >>
non-zero return code

#启动服务,并设为开机自启动
[root@master ~]# ansible test1 -m service -a "name=httpd state=started enabled=yes"
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "enabled": true, 
    "name": "httpd", 
    "state": "started", 
    "status": {
        "ActiveEnterTimestampMonotonic": "0", 
        "ActiveExitTimestampMonotonic": "0", 
        "ActiveState": "inactive", 
        "After": "nss-lookup.target -.mount remote-fs.target network.target systemd-journald.socket system.slice tmp.mount basic.target", 
        "AllowIsolate": "no", 
        "AmbientCapabilities": "0", 
        "AssertResult": "no", 
        "AssertTimestampMonotonic": "0", 
        "Before": "shutdown.target", 
        "BlockIOAccounting": "no", 
        "BlockIOWeight": "18446744073709551615", 
        "CPUAccounting": "no", 
        "CPUQuotaPerSecUSec": "infinity", 
        "CPUSchedulingPolicy": "0", 
        "CPUSchedulingPriority": "0", 
        "CPUSchedulingResetOnFork": "no", 
        "CPUShares": "18446744073709551615", 
        "CanIsolate": "no", 
        "CanReload": "yes", 
        "CanStart": "yes", 
        "CanStop": "yes", 
        "CapabilityBoundingSet": "18446744073709551615", 
        "CollectMode": "inactive", 
        "ConditionResult": "no", 
        "ConditionTimestampMonotonic": "0", 
        "Conflicts": "shutdown.target", 
        "ControlPID": "0", 
        "DefaultDependencies": "yes", 
        "Delegate": "no", 
        "Description": "The Apache HTTP Server", 
        "DevicePolicy": "auto", 
        "Documentation": "man:httpd(8) man:apachectl(8)", 
        "EnvironmentFile": "/etc/sysconfig/httpd (ignore_errors=no)", 
        "ExecMainCode": "0", 
        "ExecMainExitTimestampMonotonic": "0", 
        "ExecMainPID": "0", 
        "ExecMainStartTimestampMonotonic": "0", 
        "ExecMainStatus": "0", 
        "ExecReload": "{ path=/usr/sbin/httpd ; argv[]=/usr/sbin/httpd $OPTIONS -k graceful ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 }", 
        "ExecStart": "{ path=/usr/sbin/httpd ; argv[]=/usr/sbin/httpd $OPTIONS -DFOREGROUND ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 }", 
        "ExecStop": "{ path=/bin/kill ; argv[]=/bin/kill -WINCH ${MAINPID} ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 }", 
        "FailureAction": "none", 
        "FileDescriptorStoreMax": "0", 
        "FragmentPath": "/usr/lib/systemd/system/httpd.service", 
        "GuessMainPID": "yes", 
        "IOScheduling": "0", 
        "Id": "httpd.service", 
        "IgnoreOnIsolate": "no", 
        "IgnoreOnSnapshot": "no", 
        "IgnoreSIGPIPE": "yes", 
        "InactiveEnterTimestampMonotonic": "0", 
        "InactiveExitTimestampMonotonic": "0", 
        "JobTimeoutAction": "none", 
        "JobTimeoutUSec": "0", 
        "KillMode": "control-group", 
        "KillSignal": "18", 
        "LimitAS": "18446744073709551615", 
        "LimitCORE": "18446744073709551615", 
        "LimitCPU": "18446744073709551615", 
        "LimitDATA": "18446744073709551615", 
        "LimitFSIZE": "18446744073709551615", 
        "LimitLOCKS": "18446744073709551615", 
        "LimitMEMLOCK": "65536", 
        "LimitMSGQUEUE": "819200", 
        "LimitNICE": "0", 
        "LimitNOFILE": "4096", 
        "LimitNPROC": "7183", 
        "LimitRSS": "18446744073709551615", 
        "LimitRTPRIO": "0", 
        "LimitRTTIME": "18446744073709551615", 
        "LimitSIGPENDING": "7183", 
        "LimitSTACK": "18446744073709551615", 
        "LoadState": "loaded", 
        "MainPID": "0", 
        "MemoryAccounting": "no", 
        "MemoryCurrent": "18446744073709551615", 
        "MemoryLimit": "18446744073709551615", 
        "MountFlags": "0", 
        "Names": "httpd.service", 
        "NeedDaemonReload": "no", 
        "Nice": "0", 
        "NoNewPrivileges": "no", 
        "NonBlocking": "no", 
        "NotifyAccess": "main", 
        "OOMScoreAdjust": "0", 
        "OnFailureJobMode": "replace", 
        "PermissionsStartOnly": "no", 
        "PrivateDevices": "no", 
        "PrivateNetwork": "no", 
        "PrivateTmp": "yes", 
        "ProtectHome": "no", 
        "ProtectSystem": "no", 
        "RefuseManualStart": "no", 
        "RefuseManualStop": "no", 
        "RemainAfterExit": "no", 
        "Requires": "basic.target system.slice -.mount", 
        "RequiresMountsFor": "/var/tmp", 
        "Restart": "no", 
        "RestartUSec": "100ms", 
        "Result": "success", 
        "RootDirectoryStartOnly": "no", 
        "RuntimeDirectoryMode": "0755", 
        "SameProcessGroup": "no", 
        "SecureBits": "0", 
        "SendSIGHUP": "no", 
        "SendSIGKILL": "yes", 
        "Slice": "system.slice", 
        "StandardError": "inherit", 
        "StandardInput": "null", 
        "StandardOutput": "journal", 
        "StartLimitAction": "none", 
        "StartLimitBurst": "5", 
        "StartLimitInterval": "10000000", 
        "StartupBlockIOWeight": "18446744073709551615", 
        "StartupCPUShares": "18446744073709551615", 
        "StatusErrno": "0", 
        "StopWhenUnneeded": "no", 
        "SubState": "dead", 
        "SyslogLevelPrefix": "yes", 
        "SyslogPriority": "30", 
        "SystemCallErrorNumber": "0", 
        "TTYReset": "no", 
        "TTYVHangup": "no", 
        "TTYVTDisallocate": "no", 
        "TasksAccounting": "no", 
        "TasksCurrent": "18446744073709551615", 
        "TasksMax": "18446744073709551615", 
        "TimeoutStartUSec": "1min 30s", 
        "TimeoutStopUSec": "1min 30s", 
        "TimerSlackNSec": "50000", 
        "Transient": "no", 
        "Type": "notify", 
        "UMask": "0022", 
        "UnitFilePreset": "disabled", 
        "UnitFileState": "disabled", 
        "WatchdogTimestampMonotonic": "0", 
        "WatchdogUSec": "0"
    }
}
[root@master ~]# ansible test1 -m shell -a "systemctl status httpd.service"
192.168.239.20 | CHANGED | rc=0 >>
● httpd.service - The Apache HTTP Server
   Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled; vendor preset: disabled)
   Active: active (running) since Mon 2022-11-14 23:57:50 CST; 26s ago
     Docs: man:httpd(8)
           man:apachectl(8)
 Main PID: 5333 (httpd)
   Status: "Total requests: 0; Current requests/sec: 0; Current traffic:   0 B/sec"
   CGroup: /system.slice/httpd.service
           ├─5333 /usr/sbin/httpd -DFOREGROUND
           ├─5334 /usr/sbin/httpd -DFOREGROUND
           ├─5335 /usr/sbin/httpd -DFOREGROUND
           ├─5336 /usr/sbin/httpd -DFOREGROUND
           ├─5337 /usr/sbin/httpd -DFOREGROUND
           └─5338 /usr/sbin/httpd -DFOREGROUND

Nov 14 23:57:29 test1 systemd[1]: Starting The Apache HTTP Server...
Nov 14 23:57:49 test1 httpd[5333]: AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using fe80::250:56ff:fe26:feb7. Set the 'ServerName' directive globally to suppress this message
Nov 14 23:57:50 test1 systemd[1]: Started The Apache HTTP Server.

User模块

功能

管理用户,替换shell模块中的useradd命令

选项

name            #指定用户名
uid             #指定uid
home            #指定用户的家目录
group           #设置用户的主组
gropus          #用户将被添加到的组列表
shell           #设置用户的shell,为/sbin/nologin时就无法正常
system          #当创建帐户时,将此设置为yes使用户成为系统帐户。不能在现有用户上更改此设置。
create_home     #除非设置为“no”,否则将在创建帐户时为用户创建一个主目录。设置为no后必须指定用户的主目。
comment         #用户的描述信息

password        #指定用户的密码(必须先对密码进行加密)

state           #创建用户时无需指定,删除用户时state=absent
remove          #等于yes表示删除用户及家目录等数据,默认为no

generate_ssh_key    #指定是否生成ssh密钥
ssh_key_bits        #指定密钥的长度
ssh_key_file        #指定ssh密钥保存的路径

使用

#创建用户,设置密码为Huawie@123
[root@test-master ~]# ansible test -m user -a 'name=papa uid=13145 password="Huawei@123"'
[WARNING]: The input password appears not to have been hashed. The 'password' argument must be encrypted for this module to work properly.      #出现提示信息,用户可以创建,但是密码并不是我们输入的密码
192.168.239.30 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "append": false, 
    "changed": true, 
    "comment": "", 
    "group": 9999, 
    "home": "/home/papa", 
    "move_home": false, 
    "name": "papa", 
    "password": "NOT_LOGGING_PASSWORD", 
    "shell": "/bin/bash", 
    "state": "present", 
    "uid": 13145
}
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "append": false, 
    "changed": true, 
    "comment": "", 
    "group": 9999, 
    "home": "/home/papa", 
    "move_home": false, 
    "name": "papa", 
    "password": "NOT_LOGGING_PASSWORD", 
    "shell": "/bin/bash", 
    "state": "present", 
    "uid": 13145
}

#生成Huawei@123加密的密码
[root@test-master ~]# ansible localhost -m debug -a "msg={{ 'Huawei@123' | password_hash('sha512','salt')}}"
localhost | SUCCESS => {
    "msg": "$6$salt$6Y.AAYwyU2hhZbnoWHpewnwfmizEKW0vQIt79XtB6i34y20WGq7/DAHkhNnlFDfvzE0v0J0CWZlsnzOck3Uiw0"
}

#使用生成的加密密码,作为用户的密码,创建成功可以使用我们指定的密码进行登录
[root@test-master ~]# ansible test -m user -a 'name=heihei uid=10000 password="$6$salt$6Y.AAYwyU2hhZbnoWHpewnwfmizEKW0vQIt79XtB6i34y20WGq7/DAHkhNnlFDfvzE0v0J0CWZlsnzOck3Uiw0"'
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "comment": "", 
    "create_home": true, 
    "group": 10001, 
    "home": "/home/heihei", 
    "name": "heihei", 
    "password": "NOT_LOGGING_PASSWORD", 
    "shell": "/bin/bash", 
    "state": "present", 
    "system": false, 
    "uid": 10000
}
192.168.239.30 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "comment": "", 
    "create_home": true, 
    "group": 10001, 
    "home": "/home/heihei", 
    "name": "heihei", 
    "password": "NOT_LOGGING_PASSWORD", 
    "shell": "/bin/bash", 
    "state": "present", 
    "system": false, 
    "uid": 10000
}

#删除用户及其家目录
[root@test-master ~]# ansible test -m user -a "name=heihei state=absent remove=yes"
192.168.239.30 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "force": false, 
    "name": "heihei", 
    "remove": true, 
    "state": "absent"
}
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "force": false, 
    "name": "heihei", 
    "remove": true, 
    "state": "absent"
}

Group模块

功能

管理组

使用

#创建组
[root@test-master ~]# ansible test -m group -a "name=mysql gid=66 system=yes"
192.168.239.30 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "gid": 66, 
    "name": "mysql", 
    "state": "present", 
    "system": true
}
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "gid": 66, 
    "name": "mysql", 
    "state": "present", 
    "system": true
}


#删除组
[root@test-master ~]# ansible test -m group -a "name=mysql state=absent"
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "name": "mysql", 
    "state": "absent"
}
192.168.239.30 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "name": "mysql", 
    "state": "absent"
}

Lineinfile模块

ansible在使用sed进行替换时,经常会遇到需要转义的问题,而且ansible在遇到特殊符号进行替换时,存在问题,无法正常进行替换 。其实在ansible自身提供了两个模块:lineinfile模块和replace模块,可以方便的进行替换

一般在ansible当中去修改某个文件的单行进行替换的时候需要使用lineinfile模块

选项

path            #指定要操作的文件
line            #使用此参数指定文本内容(可能是文件尾添加的内容,可能是替换后的内容)
regexp          #使用正则表达式匹配对应的行,当替换文本时,如果有多行文本都能被匹配,则只有最后面被匹配到的那行文本才会被替换,当删除文本时,如果有多行文本都能被匹配,这么这些行都会被删除。

state           #当想要删除对应的文本时,需要将state参数的值设置为absent,state的默认值为present

backrefs        #当根据正则替换文本时,即使regexp参数中的正则存在分组,在line参数中也不能对正则中的分组进行引用,除非将backrefs参数的值设置为yes。backrefs=yes表示开启后向引用,这样,line参数中就能对regexp参数中的分组进行后向引用了。
                #backrefs=yes除了能够开启后向引用功能,还有另一个作用,默认情况下,当使用正则表达式替换对应行时,如果正则没有匹配到任何的行,那么line对应的内容会被插入到文本的末尾,但是使用了backrefs=yes,情况就不一样了,当正则没有匹配到任何的行时,不会对文件进行任何操作,相当于保持原文件不变。

insertafter     #可以将文本插入到“指定的行”之后,insertafter参数的值可以设置为EOF或者正则表达式,EOF为End Of File之意,表示插入到文档的末尾,默认情况下insertafter的值为EOF,如果将insertafter的值设置为正则表达式,表示将文本插入到匹配到正则的行之后,如果正则没有匹配到任何行,则插入到文件末尾,当使用backrefs参数时,此参数会被忽略。

insertbefore    #可以将文本插入到“指定的行”之前,insertbefore参数的值可以设置为BOF或者正则表达式,BOF为Begin Of File之意,表示插入到文档的开头,如果将insertbefore的值设置为正则表达式,表示将文本插入到匹配到正则的行之前,如果正则没有匹配到任何行,则插入到文件末尾,当使用backrefs参数时,此参数会被忽略。

backup          #是否在修改文件之前对文件进行备份。

create          #当要操作的文件并不存在时,是否创建对应的文件。

使用

#练习文件
[root@test-master ~]# cat test
Hello zhangsan,HiZS
#cool
lisi is so cool!
#pretty
lisi is so pretty!
#cool
wangwu is so cool!

正则匹配——替换
#匹配以He开头的行,然后替换为"Hello zhangsan,HiZS!"
[root@test-master ~]# ansible test -m lineinfile -a 'path=/data/test regexp="^He" line="Hello zhangsan,HiZS!"'
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "backup": "", 
    "changed": true, 
    "msg": "line replaced"                  #提示被替换
}
192.168.239.30 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "backup": "", 
    "changed": true, 
    "msg": "line replaced"
}

#成功替换
[root@test-master ~]# ansible test -m shell -a "cat /data/test"
192.168.239.30 | CHANGED | rc=0 >>
Hello zhangsan,HiZS!
#cool
lisi is so cool!
#pretty
lisi is so pretty!
#cool
wangwu is so cool!
192.168.239.20 | CHANGED | rc=0 >>
Hello zhangsan,HiZS!
#cool
lisi is so cool!
#pretty
lisi is so pretty!
#cool
wangwu is so cool!
正则未匹配——添加
#匹配以zhaoliu开头的行,然后替换为"zhaoliu is so cool!",如果没有匹配的内容则在文件尾部添加替换的内容
[root@test-master ~]# ansible test -m lineinfile -a 'path=/data/test regexp="^zhaoliu" line="zhaoliu is so cool!"'
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "backup": "", 
    "changed": true, 
    "msg": "line added"                     #提示添加
}
192.168.239.30 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "backup": "", 
    "changed": true, 
    "msg": "line added"
}
[root@test-master ~]# ansible test -m shell -a "cat /data/test"
192.168.239.30 | CHANGED | rc=0 >>
Hello zhangsan,HiZS!
#cool
lisi is so cool!
#pretty
lisi is so pretty!
#cool
wangwu is so cool!
zhaoliu is so cool!                 #将要替换的内容插入到文件尾
192.168.239.20 | CHANGED | rc=0 >>
Hello zhangsan,HiZS!
#cool
lisi is so cool!
#pretty
lisi is so pretty!
#cool
wangwu is so cool!
zhaoliu is so cool!
[root@test-master ~]# 

正则多行匹配——最后匹配上的替换
#将以lisi开头的行替换为指定内容,同时有多行匹配,只有最后一行才会替换
[root@test-master ~]# ansible test -m lineinfile -a 'path=/data/test regexp="^lisi" line="lisi is so cutey!"'
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "backup": "", 
    "changed": true, 
    "msg": "line replaced"                  #提示替换
}
192.168.239.30 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "backup": "", 
    "changed": true, 
    "msg": "line replaced"
}

[root@test-master ~]# ansible test -m shell -a "cat /data/test"
192.168.239.20 | CHANGED | rc=0 >>
Hello zhangsan,HiZS!
#cool
lisi is so cool!                            #匹配,未修改
#pretty
lisi is so cutey!                           #匹配,修改
#cool
wangwu is so cool!
zhaoliu is so cool!
192.168.239.30 | CHANGED | rc=0 >>
Hello zhangsan,HiZS!
#cool
lisi is so cool!
#pretty
lisi is so cutey!
#cool
wangwu is so cool!
zhaoliu is so cool!

正则多行匹配——所有匹配上的删除
#删除以#开头的行,所有匹配的行都被删除
[root@test-master ~]# ansible test -m lineinfile -a 'path=/data/test regexp="^#" state=absent'
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "backup": "", 
    "changed": true, 
    "found": 3, 
    "msg": "3 line(s) removed"              #提示删除3行
}
192.168.239.30 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "backup": "", 
    "changed": true, 
    "found": 3, 
    "msg": "3 line(s) removed"
}

[root@test-master ~]# ansible test -m shell -a "cat /data/test"
192.168.239.20 | CHANGED | rc=0 >>
Hello zhangsan,HiZS!
lisi is so cool!
lisi is so cutey!
wangwu is so cool!
zhaoliu is so cool!
192.168.239.30 | CHANGED | rc=0 >>
Hello zhangsan,HiZS!
lisi is so cool!
lisi is so cutey!
wangwu is so cool!
zhaoliu is so cool!

backrefs——不添加
#使用backrefs选项,使得正则没有匹配到任何的行,也不会将line对应的内容会被插入到文本的末尾
[root@test-master ~]# ansible test -m lineinfile -a 'path=/data/test regexp="^heihei" line="heihei is ok!" backrefs=yes'
192.168.239.20 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "backup": "", 
    "changed": false,                   #没有改变
    "msg": ""
}
192.168.239.30 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "backup": "", 
    "changed": false, 
    "msg": ""
}

backrefs——向后引用
#backrefs选项的向后引用功能
#未加选项backrefs=yes,将匹配的语句直接替换为\2
[root@test-master ~]# ansible test -m lineinfile -a 'path=/data/test regexp="(H.{4}).*(H.{4})" line="\2" '
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "backup": "", 
    "changed": true, 
    "msg": "line replaced"
}
192.168.239.30 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "backup": "", 
    "changed": true, 
    "msg": "line replaced"
}
[root@test-master ~]# ansible test -m shell -a "cat /data/test"
192.168.239.30 | CHANGED | rc=0 >>
\2
lisi is so cool!
lisi is so cutey!
wangwu is so cool!
zhaoliu is so cool!
192.168.239.20 | CHANGED | rc=0 >>
\2
lisi is so cool!
lisi is so cutey!
wangwu is so cool!
zhaoliu is so cool!


#加了backrefs选项,将匹配的语句直接替换为第二个分组的内容即(HiZS!)
[root@test-master ~]# ansible test -m lineinfile -a 'path=/data/test regexp="(H.{4}).*(H.{4})" line="\2" backrefs=yes'
192.168.239.30 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "backup": "", 
    "changed": true, 
    "msg": "line replaced"
}
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "backup": "", 
    "changed": true, 
    "msg": "line replaced"
}
[root@test-master ~]# ansible test -m shell -a "cat /data/test"
192.168.239.30 | CHANGED | rc=0 >>
HiZS!
lisi is so cool!
lisi is so cutey!
wangwu is so cool!
zhaoliu is so cool!
192.168.239.20 | CHANGED | rc=0 >>
HiZS!
lisi is so cool!
lisi is so cutey!
wangwu is so cool!
zhaoliu is so cool!

Replace模块

功能

该模块有点类似于sed命令,主要也是基于正则进行匹配和替换

选项

path                #指定要操作的文件
regex               #指定一个正则表达式,文件中与正则匹配的字符串将会被替换。
replace             #指定最终要替换成的字符串。
backup              #是否在修改文件之前对文件进行备份,最好设置为yes。

使用

继续使用上面的练习文件

#将lisi替换为LiSi
[root@test-master ~]# ansible test -m replace -a 'path=/data/test regexp="lisi" replace="LiSi"'
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "msg": "2 replacements made"    
}
192.168.239.30 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "msg": "2 replacements made"
}
[root@test-master ~]# ansible test -m shell -a "cat /data/test"
192.168.239.20 | CHANGED | rc=0 >>
HiZS!
LiSi is so cool!
LiSi is so cutey!
wangwu is so cool!
zhaoliu is so cool!
192.168.239.30 | CHANGED | rc=0 >>
HiZS!
LiSi is so cool!
LiSi is so cutey!
wangwu is so cool!
zhaoliu is so cool!
#在替换前对文件进行备份
[root@test-master ~]# ansible test -m shell -a "cat /data/test"
192.168.239.20 | CHANGED | rc=0 >>
HiZS!
LiSi is so cool!
LiSi is so cutey!
wangwu is so cool!
zhaoliu is so cool!
192.168.239.30 | CHANGED | rc=0 >>
HiZS!
LiSi is so cool!
LiSi is so cutey!
wangwu is so cool!
zhaoliu is so cool!
[root@test-master ~]# ansible test -m replace -a 'path=/data/test regexp="wangwu" replace="WangWu" backup=yes'
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "backup_file": "/data/test.6154.2022-11-16@18:54:54~",      #备份文件名
    "changed": true, 
    "msg": "1 replacements made"                                #有一行发生了修改
}
192.168.239.30 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "backup_file": "/data/test.4910.2022-11-16@18:54:55~", 
    "changed": true, 
    "msg": "1 replacements made"
}

SElinux模块

功能

该模块管理SElinux策略

使用

#关闭SElinux
[root@test-master ~]# ansible test -m selinux -a 'state=disabled'
[WARNING]: SELinux state temporarily changed from 'enforcing' to 'permissive'. State change will take effect next reboot.       #提示需要重启才会生效
192.168.239.30 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "configfile": "/etc/selinux/config", 
    "msg": "Config SELinux state changed from 'enforcing' to 'disabled'", 
    "policy": "targeted", 
    "reboot_required": true, 
    "state": "disabled"
}
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "configfile": "/etc/selinux/config", 
    "msg": "Config SELinux state changed from 'enforcing' to 'disabled'", 
    "policy": "targeted", 
    "reboot_required": true, 
    "state": "disabled"
}


[root@test-master ~]# ansible test -m shell -a " grep -v '#' /etc/selinux/config "
192.168.239.20 | CHANGED | rc=0 >>

SELINUX=disabled
SELINUXTYPE=targeted 
192.168.239.30 | CHANGED | rc=0 >>

SELINUX=disabled
SELINUXTYPE=targeted 


#查看当前是否开启了Selinux
[root@test-master ~]# ansible test -m shell -a "getenforce"
192.168.239.20 | CHANGED | rc=0 >>
Permissive                          #当前还是开启的,需要重启后才能生效
192.168.239.30 | CHANGED | rc=0 >>
Permissive



#重启之后再次查看
[root@test-master ~]# ansible test -m shell -a "reboot"
192.168.239.20 | UNREACHABLE! => {
    "changed": false, 
    "msg": "Failed to connect to the host via ssh: Shared connection to 192.168.239.20 closed.", 
    "unreachable": true
}
192.168.239.30 | UNREACHABLE! => {
    "changed": false, 
    "msg": "Failed to connect to the host via ssh: Shared connection to 192.168.239.30 closed.", 
    "unreachable": true
}
[root@test-master ~]# ansible test -m shell -a "getenforce"
192.168.239.30 | CHANGED | rc=0 >>
Disabled
192.168.239.20 | CHANGED | rc=0 >>
Disabled

Reboot模块

功能

重启被控端,之前都是通过shell模块使用命令reboot来执行,现在可以直接使用reboot模块

使用

[root@test-master ~]# ansible test1 -m reboot
192.168.239.20 | CHANGED => {
    "changed": true, 
    "elapsed": 26, 
    "rebooted": true
}

Mount模块

功能

挂载和卸载文件系统

选项

src             #本地或远程设备的磁盘路径
path            #设备挂载至本地的路径
fstype          #挂载的文件系统类型,xfs、ext4等
opts            #挂载的参数
state           #挂载的zhuan

使用

临时挂载
[root@test-master ~]# ansible test1 -m mount -a "src=/dev/sdb1 path=/data/python state=present fstype=xfs"
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "dump": "0", 
    "fstab": "/etc/fstab", 
    "fstype": "xfs", 
    "name": "/data/python", 
    "opts": "defaults", 
    "passno": "0", 
    "src": "/dev/sdb1"
}
取消临时挂载
[root@test-master ~]# ansible test1 -m mount -a "path=/data/python state=unmounted fstype=xfs"
192.168.239.20 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "dump": "0", 
    "fstab": "/etc/fstab", 
    "fstype": "xfs", 
    "name": "/data/python", 
    "opts": "defaults", 
    "passno": "0"
}
永久挂载
[root@test-master ~]# ansible test1 -m mount -a "src=/dev/sdb1 path=/data/python state=mounted fstype=xfs"
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "dump": "0", 
    "fstab": "/etc/fstab", 
    "fstype": "xfs", 
    "name": "/data/python", 
    "opts": "defaults", 
    "passno": "0", 
    "src": "/dev/sdb1"
}

#查看挂载情况
[root@slave1 ~]# df -h
Filesystem               Size  Used Avail Use% Mounted on
devtmpfs                 898M     0  898M   0% /dev
tmpfs                    910M     0  910M   0% /dev/shm
tmpfs                    910M  9.5M  901M   2% /run
tmpfs                    910M     0  910M   0% /sys/fs/cgroup
/dev/mapper/centos-root   17G  2.9G   15G  17% /
/dev/sda1               1014M  150M  865M  15% /boot
tmpfs                    182M     0  182M   0% /run/user/0
/dev/sdb1                950M   33M  918M   4% /data/python


取消永久挂载
[root@test-master ~]# ansible test1 -m mount -a "src=/dev/sdb1 path=/data/python state=absent fstype=xfs"
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "dump": "0", 
    "fstab": "/etc/fstab", 
    "fstype": "xfs", 
    "name": "/data/python", 
    "opts": "defaults", 
    "passno": "0", 
    "src": "/dev/sdb1"
}


#查看挂载情况
[root@slave1 ~]# df -h
Filesystem               Size  Used Avail Use% Mounted on
devtmpfs                 898M     0  898M   0% /dev
tmpfs                    910M     0  910M   0% /dev/shm
tmpfs                    910M  9.5M  901M   2% /run
tmpfs                    910M     0  910M   0% /sys/fs/cgroup
/dev/mapper/centos-root   17G  2.9G   15G  17% /
/dev/sda1               1014M  150M  865M  15% /boot
tmpfs                    182M     0  182M   0% /run/user/0

Setup模块

功能

setup 模块来收集主机的系统信息,这些 facts 信息可以直接以变量的形式使用,但是如果主机较多,会影响执行速度

补充

可以使用 gather_facts: no 来禁止 Ansible 收集 facts 信息,在playbook中,如果没用特殊需求建议禁止信息收集,这样可以提高执行的效率

使用

未过滤
[root@test-master ~]# ansible test1 -m setup
192.168.239.20 | SUCCESS => {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "192.168.239.20"
        ], 
        "ansible_all_ipv6_addresses": [
            "fe80::250:56ff:fe26:feb7"
        ], 
        "ansible_apparmor": {
            "status": "disabled"
        }, 
        "ansible_architecture": "x86_64", 
        "ansible_bios_date": "07/22/2020", 
        "ansible_bios_version": "6.00", 
        "ansible_cmdline": {
            "BOOT_IMAGE": "/vmlinuz-3.10.0-1160.el7.x86_64", 
            "LANG": "en_US.UTF-8", 
            "crashkernel": "auto", 
            "quiet": true, 
            "rd.lvm.lv": "centos/swap", 
            "rhgb": true, 
            "ro": true, 
            "root": "/dev/mapper/centos-root"
        }, 
        "ansible_date_time": {
            "date": "2022-11-16", 
            "day": "16", 
            "epoch": "1668606414", 
            "hour": "21", 
            "iso8601": "2022-11-16T13:46:54Z", 
            "iso8601_basic": "20221116T214654266000", 
            "iso8601_basic_short": "20221116T214654", 
            "iso8601_micro": "2022-11-16T13:46:54.266000Z", 
            "minute": "46", 
            "month": "11", 
            "second": "54", 
            "time": "21:46:54", 
            "tz": "CST", 
            "tz_offset": "+0800", 
            "weekday": "Wednesday", 
            "weekday_number": "3", 
            "weeknumber": "46", 
            "year": "2022"
        }, 
        "ansible_default_ipv4": {
            "address": "192.168.239.20", 
            "alias": "ens33", 
            "broadcast": "192.168.239.255", 
            "gateway": "192.168.239.2", 
            "interface": "ens33", 
            "macaddress": "00:50:56:26:fe:b7", 
            "mtu": 1500, 
            "netmask": "255.255.255.0", 
            "network": "192.168.239.0", 
            "type": "ether"
        }, 
        "ansible_default_ipv6": {}, 
        "ansible_device_links": {
            "ids": {
                "dm-0": [
                    "dm-name-centos-root", 
                    "dm-uuid-LVM-guxEsfuGdy87fJ4wPG6A3hBZ2NzPI5gHWSJXb5y4AwcO8LF72SCmwu9Zdi0U7nSI"
                ], 
                "dm-1": [
                    "dm-name-centos-swap", 
                    "dm-uuid-LVM-guxEsfuGdy87fJ4wPG6A3hBZ2NzPI5gHH1ljYfwhdUgcj8v3Ei51hiT7Djs6vtCM"
                ], 
                "sda2": [
                    "lvm-pv-uuid-8Uh5fD-f1YU-Mk5a-3X3i-naVn-31hU-n8HQdF"
                ], 
                "sr0": [
                    "ata-VMware_Virtual_IDE_CDROM_Drive_10000000000000000001"
                ]
            }, 
            "labels": {
                "sr0": [
                    "CentOS\\x207\\x20x86_64"
                ]
            }, 
            "masters": {
                "sda2": [
                    "dm-0", 
                    "dm-1"
                ]
            }, 
            "uuids": {
                "dm-0": [
                    "0b8e09e1-47ec-4a90-9963-82d2d96fd74a"
                ], 
                "dm-1": [
                    "0f2d75ff-ce1d-442f-b43d-3747518879c0"
                ], 
                "sda1": [
                    "77285161-e6b0-4742-83a7-1c9e67021a61"
                ], 
                "sr0": [
                    "2020-11-03-14-55-29-00"
                ]
            }
        }, 
        "ansible_devices": {
            "dm-0": {
                "holders": [], 
                "host": "", 
                "links": {
                    "ids": [
                        "dm-name-centos-root", 
                        "dm-uuid-LVM-guxEsfuGdy87fJ4wPG6A3hBZ2NzPI5gHWSJXb5y4AwcO8LF72SCmwu9Zdi0U7nSI"
                    ], 
                    "labels": [], 
                    "masters": [], 
                    "uuids": [
                        "0b8e09e1-47ec-4a90-9963-82d2d96fd74a"
                    ]
                }, 
                "model": null, 
                "partitions": {}, 
                "removable": "0", 
                "rotational": "1", 
                "sas_address": null, 
                "sas_device_handle": null, 
                "scheduler_mode": "", 
                "sectors": "35643392", 
                "sectorsize": "512", 
                "size": "17.00 GB", 
                "support_discard": "0", 
                "vendor": null, 
                "virtual": 1
            }, 
            "dm-1": {
                "holders": [], 
                "host": "", 
                "links": {
                    "ids": [
                        "dm-name-centos-swap", 
                        "dm-uuid-LVM-guxEsfuGdy87fJ4wPG6A3hBZ2NzPI5gHH1ljYfwhdUgcj8v3Ei51hiT7Djs6vtCM"
                    ], 
                    "labels": [], 
                    "masters": [], 
                    "uuids": [
                        "0f2d75ff-ce1d-442f-b43d-3747518879c0"
                    ]
                }, 
                "model": null, 
                "partitions": {}, 
                "removable": "0", 
                "rotational": "1", 
                "sas_address": null, 
                "sas_device_handle": null, 
                "scheduler_mode": "", 
                "sectors": "4194304", 
                "sectorsize": "512", 
                "size": "2.00 GB", 
                "support_discard": "0", 
                "vendor": null, 
                "virtual": 1
            }, 
            "sda": {
                "holders": [], 
                "host": "", 
                "links": {
                    "ids": [], 
                    "labels": [], 
                    "masters": [], 
                    "uuids": []
                }, 
                "model": "VMware Virtual S", 
                "partitions": {
                    "sda1": {
                        "holders": [], 
                        "links": {
                            "ids": [], 
                            "labels": [], 
                            "masters": [], 
                            "uuids": [
                                "77285161-e6b0-4742-83a7-1c9e67021a61"
                            ]
                        }, 
                        "sectors": "2097152", 
                        "sectorsize": 512, 
                        "size": "1.00 GB", 
                        "start": "2048", 
                        "uuid": "77285161-e6b0-4742-83a7-1c9e67021a61"
                    }, 
                    "sda2": {
                        "holders": [
                            "centos-root", 
                            "centos-swap"
                        ], 
                        "links": {
                            "ids": [
                                "lvm-pv-uuid-8Uh5fD-f1YU-Mk5a-3X3i-naVn-31hU-n8HQdF"
                            ], 
                            "labels": [], 
                            "masters": [
                                "dm-0", 
                                "dm-1"
                            ], 
                            "uuids": []
                        }, 
                        "sectors": "39843840", 
                        "sectorsize": 512, 
                        "size": "19.00 GB", 
                        "start": "2099200", 
                        "uuid": null
                    }
                }, 
                "removable": "0", 
                "rotational": "1", 
                "sas_address": null, 
                "sas_device_handle": null, 
                "scheduler_mode": "deadline", 
                "sectors": "41943040", 
                "sectorsize": "512", 
                "size": "20.00 GB", 
                "support_discard": "0", 
                "vendor": "VMware,", 
                "virtual": 1
            }, 
            "sr0": {
                "holders": [], 
                "host": "", 
                "links": {
                    "ids": [
                        "ata-VMware_Virtual_IDE_CDROM_Drive_10000000000000000001"
                    ], 
                    "labels": [
                        "CentOS\\x207\\x20x86_64"
                    ], 
                    "masters": [], 
                    "uuids": [
                        "2020-11-03-14-55-29-00"
                    ]
                }, 
                "model": "VMware IDE CDR10", 
                "partitions": {}, 
                "removable": "1", 
                "rotational": "1", 
                "sas_address": null, 
                "sas_device_handle": null, 
                "scheduler_mode": "deadline", 
                "sectors": "1992704", 
                "sectorsize": "2048", 
                "size": "973.00 MB", 
                "support_discard": "0", 
                "vendor": "NECVMWar", 
                "virtual": 1
            }
        }, 
        "ansible_distribution": "CentOS", 
        "ansible_distribution_file_parsed": true, 
        "ansible_distribution_file_path": "/etc/redhat-release", 
        "ansible_distribution_file_variety": "RedHat", 
        "ansible_distribution_major_version": "7", 
        "ansible_distribution_release": "Core", 
        "ansible_distribution_version": "7.9", 
        "ansible_dns": {
            "nameservers": [
                "8.8.8.8"
            ]
        }, 
        "ansible_domain": "", 
        "ansible_effective_group_id": 0, 
        "ansible_effective_user_id": 0, 
        "ansible_ens33": {
            "active": true, 
            "device": "ens33", 
            "features": {
                "busy_poll": "off [fixed]", 
                "fcoe_mtu": "off [fixed]", 
                "generic_receive_offload": "on", 
                "generic_segmentation_offload": "on", 
                "highdma": "off [fixed]", 
                "hw_tc_offload": "off [fixed]", 
                "l2_fwd_offload": "off [fixed]", 
                "large_receive_offload": "off [fixed]", 
                "loopback": "off [fixed]", 
                "netns_local": "off [fixed]", 
                "ntuple_filters": "off [fixed]", 
                "receive_hashing": "off [fixed]", 
                "rx_all": "off", 
                "rx_checksumming": "off", 
                "rx_fcs": "off", 
                "rx_gro_hw": "off [fixed]", 
                "rx_udp_tunnel_port_offload": "off [fixed]", 
                "rx_vlan_filter": "on [fixed]", 
                "rx_vlan_offload": "on", 
                "rx_vlan_stag_filter": "off [fixed]", 
                "rx_vlan_stag_hw_parse": "off [fixed]", 
                "scatter_gather": "on", 
                "tcp_segmentation_offload": "on", 
                "tx_checksum_fcoe_crc": "off [fixed]", 
                "tx_checksum_ip_generic": "on", 
                "tx_checksum_ipv4": "off [fixed]", 
                "tx_checksum_ipv6": "off [fixed]", 
                "tx_checksum_sctp": "off [fixed]", 
                "tx_checksumming": "on", 
                "tx_fcoe_segmentation": "off [fixed]", 
                "tx_gre_csum_segmentation": "off [fixed]", 
                "tx_gre_segmentation": "off [fixed]", 
                "tx_gso_partial": "off [fixed]", 
                "tx_gso_robust": "off [fixed]", 
                "tx_ipip_segmentation": "off [fixed]", 
                "tx_lockless": "off [fixed]", 
                "tx_nocache_copy": "off", 
                "tx_scatter_gather": "on", 
                "tx_scatter_gather_fraglist": "off [fixed]", 
                "tx_sctp_segmentation": "off [fixed]", 
                "tx_sit_segmentation": "off [fixed]", 
                "tx_tcp6_segmentation": "off [fixed]", 
                "tx_tcp_ecn_segmentation": "off [fixed]", 
                "tx_tcp_mangleid_segmentation": "off", 
                "tx_tcp_segmentation": "on", 
                "tx_udp_tnl_csum_segmentation": "off [fixed]", 
                "tx_udp_tnl_segmentation": "off [fixed]", 
                "tx_vlan_offload": "on [fixed]", 
                "tx_vlan_stag_hw_insert": "off [fixed]", 
                "udp_fragmentation_offload": "off [fixed]", 
                "vlan_challenged": "off [fixed]"
            }, 
            "hw_timestamp_filters": [], 
            "ipv4": {
                "address": "192.168.239.20", 
                "broadcast": "192.168.239.255", 
                "netmask": "255.255.255.0", 
                "network": "192.168.239.0"
            }, 
            "ipv6": [
                {
                    "address": "fe80::250:56ff:fe26:feb7", 
                    "prefix": "64", 
                    "scope": "link"
                }
            ], 
            "macaddress": "00:50:56:26:fe:b7", 
            "module": "e1000", 
            "mtu": 1500, 
            "pciid": "0000:02:01.0", 
            "promisc": false, 
            "speed": 1000, 
            "timestamping": [
                "tx_software", 
                "rx_software", 
                "software"
            ], 
            "type": "ether"
        }, 
        "ansible_env": {
            "HOME": "/root", 
            "LANG": "C", 
            "LC_ALL": "C", 
            "LC_NUMERIC": "C", 
            "LESSOPEN": "||/usr/bin/lesspipe.sh %s", 
            "LOGNAME": "root", 
            "LS_COLORS": "rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36:", 
            "MAIL": "/var/mail/root", 
            "PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin", 
            "PWD": "/root", 
            "SHELL": "/bin/bash", 
            "SHLVL": "2", 
            "SSH_CLIENT": "192.168.239.10 41760 22", 
            "SSH_CONNECTION": "192.168.239.10 41760 192.168.239.20 22", 
            "SSH_TTY": "/dev/pts/1", 
            "TERM": "xterm", 
            "USER": "root", 
            "XDG_RUNTIME_DIR": "/run/user/0", 
            "XDG_SESSION_ID": "7", 
            "_": "/usr/bin/python"
        }, 
        "ansible_fibre_channel_wwn": [], 
        "ansible_fips": false, 
        "ansible_form_factor": "Other", 
        "ansible_fqdn": "test1", 
        "ansible_hostname": "test1", 
        "ansible_hostnqn": "", 
        "ansible_interfaces": [
            "lo", 
            "ens33"
        ], 
        "ansible_is_chroot": false, 
        "ansible_iscsi_iqn": "", 
        "ansible_kernel": "3.10.0-1160.el7.x86_64", 
        "ansible_kernel_version": "#1 SMP Mon Oct 19 16:18:59 UTC 2020", 
        "ansible_lo": {
            "active": true, 
            "device": "lo", 
            "features": {
                "busy_poll": "off [fixed]", 
                "fcoe_mtu": "off [fixed]", 
                "generic_receive_offload": "on", 
                "generic_segmentation_offload": "on", 
                "highdma": "on [fixed]", 
                "hw_tc_offload": "off [fixed]", 
                "l2_fwd_offload": "off [fixed]", 
                "large_receive_offload": "off [fixed]", 
                "loopback": "on [fixed]", 
                "netns_local": "on [fixed]", 
                "ntuple_filters": "off [fixed]", 
                "receive_hashing": "off [fixed]", 
                "rx_all": "off [fixed]", 
                "rx_checksumming": "on [fixed]", 
                "rx_fcs": "off [fixed]", 
                "rx_gro_hw": "off [fixed]", 
                "rx_udp_tunnel_port_offload": "off [fixed]", 
                "rx_vlan_filter": "off [fixed]", 
                "rx_vlan_offload": "off [fixed]", 
                "rx_vlan_stag_filter": "off [fixed]", 
                "rx_vlan_stag_hw_parse": "off [fixed]", 
                "scatter_gather": "on", 
                "tcp_segmentation_offload": "on", 
                "tx_checksum_fcoe_crc": "off [fixed]", 
                "tx_checksum_ip_generic": "on [fixed]", 
                "tx_checksum_ipv4": "off [fixed]", 
                "tx_checksum_ipv6": "off [fixed]", 
                "tx_checksum_sctp": "on [fixed]", 
                "tx_checksumming": "on", 
                "tx_fcoe_segmentation": "off [fixed]", 
                "tx_gre_csum_segmentation": "off [fixed]", 
                "tx_gre_segmentation": "off [fixed]", 
                "tx_gso_partial": "off [fixed]", 
                "tx_gso_robust": "off [fixed]", 
                "tx_ipip_segmentation": "off [fixed]", 
                "tx_lockless": "on [fixed]", 
                "tx_nocache_copy": "off [fixed]", 
                "tx_scatter_gather": "on [fixed]", 
                "tx_scatter_gather_fraglist": "on [fixed]", 
                "tx_sctp_segmentation": "on", 
                "tx_sit_segmentation": "off [fixed]", 
                "tx_tcp6_segmentation": "on", 
                "tx_tcp_ecn_segmentation": "on", 
                "tx_tcp_mangleid_segmentation": "on", 
                "tx_tcp_segmentation": "on", 
                "tx_udp_tnl_csum_segmentation": "off [fixed]", 
                "tx_udp_tnl_segmentation": "off [fixed]", 
                "tx_vlan_offload": "off [fixed]", 
                "tx_vlan_stag_hw_insert": "off [fixed]", 
                "udp_fragmentation_offload": "on", 
                "vlan_challenged": "on [fixed]"
            }, 
            "hw_timestamp_filters": [], 
            "ipv4": {
                "address": "127.0.0.1", 
                "broadcast": "", 
                "netmask": "255.0.0.0", 
                "network": "127.0.0.0"
            }, 
            "ipv6": [
                {
                    "address": "::1", 
                    "prefix": "128", 
                    "scope": "host"
                }
            ], 
            "mtu": 65536, 
            "promisc": false, 
            "timestamping": [
                "rx_software", 
                "software"
            ], 
            "type": "loopback"
        }, 
        "ansible_local": {}, 
        "ansible_lsb": {}, 
        "ansible_lvm": {
            "lvs": {
                "root": {
                    "size_g": "17.00", 
                    "vg": "centos"
                }, 
                "swap": {
                    "size_g": "2.00", 
                    "vg": "centos"
                }
            }, 
            "pvs": {
                "/dev/sda2": {
                    "free_g": "0", 
                    "size_g": "19.00", 
                    "vg": "centos"
                }
            }, 
            "vgs": {
                "centos": {
                    "free_g": "0", 
                    "num_lvs": "2", 
                    "num_pvs": "1", 
                    "size_g": "19.00"
                }
            }
        }, 
        "ansible_machine": "x86_64", 
        "ansible_machine_id": "3d359e11883a43d09ef53eb2b4085ad5", 
        "ansible_memfree_mb": 1491, 
        "ansible_memory_mb": {
            "nocache": {
                "free": 1593, 
                "used": 226
            }, 
            "real": {
                "free": 1491, 
                "total": 1819, 
                "used": 328
            }, 
            "swap": {
                "cached": 0, 
                "free": 2047, 
                "total": 2047, 
                "used": 0
            }
        }, 
        "ansible_memtotal_mb": 1819, 
        "ansible_mounts": [
            {
                "block_available": 221192, 
                "block_size": 4096, 
                "block_total": 259584, 
                "block_used": 38392, 
                "device": "/dev/sda1", 
                "fstype": "xfs", 
                "inode_available": 523961, 
                "inode_total": 524288, 
                "inode_used": 327, 
                "mount": "/boot", 
                "options": "rw,relatime,attr2,inode64,noquota", 
                "size_available": 906002432, 
                "size_total": 1063256064, 
                "uuid": "77285161-e6b0-4742-83a7-1c9e67021a61"
            }, 
            {
                "block_available": 3615504, 
                "block_size": 4096, 
                "block_total": 4452864, 
                "block_used": 837360, 
                "device": "/dev/mapper/centos-root", 
                "fstype": "xfs", 
                "inode_available": 8797182, 
                "inode_total": 8910848, 
                "inode_used": 113666, 
                "mount": "/", 
                "options": "rw,relatime,attr2,inode64,noquota", 
                "size_available": 14809104384, 
                "size_total": 18238930944, 
                "uuid": "0b8e09e1-47ec-4a90-9963-82d2d96fd74a"
            }
        ], 
        "ansible_nodename": "test1", 
        "ansible_os_family": "RedHat", 
        "ansible_pkg_mgr": "yum", 
        "ansible_proc_cmdline": {
            "BOOT_IMAGE": "/vmlinuz-3.10.0-1160.el7.x86_64", 
            "LANG": "en_US.UTF-8", 
            "crashkernel": "auto", 
            "quiet": true, 
            "rd.lvm.lv": [
                "centos/root", 
                "centos/swap"
            ], 
            "rhgb": true, 
            "ro": true, 
            "root": "/dev/mapper/centos-root"
        }, 
        "ansible_processor": [
            "0", 
            "GenuineIntel", 
            "11th Gen Intel(R) Core(TM) i5-1135G7 @ 2.40GHz", 
            "1", 
            "GenuineIntel", 
            "11th Gen Intel(R) Core(TM) i5-1135G7 @ 2.40GHz"
        ], 
        "ansible_processor_cores": 2, 
        "ansible_processor_count": 1, 
        "ansible_processor_threads_per_core": 1, 
        "ansible_processor_vcpus": 2, 
        "ansible_product_name": "VMware Virtual Platform", 
        "ansible_product_serial": "VMware-56 4d 76 43 43 02 57 2f-c1 1c d4 22 de 7d 2e 85", 
        "ansible_product_uuid": "43764D56-0243-2F57-C11C-D422DE7D2E85", 
        "ansible_product_version": "None", 
        "ansible_python": {
            "executable": "/usr/bin/python", 
            "has_sslcontext": true, 
            "type": "CPython", 
            "version": {
                "major": 2, 
                "micro": 5, 
                "minor": 7, 
                "releaselevel": "final", 
                "serial": 0
            }, 
            "version_info": [
                2, 
                7, 
                5, 
                "final", 
                0
            ]
        }, 
        "ansible_python_version": "2.7.5", 
        "ansible_real_group_id": 0, 
        "ansible_real_user_id": 0, 
        "ansible_selinux": {
            "status": "disabled"
        }, 
        "ansible_selinux_python_present": true, 
        "ansible_service_mgr": "systemd", 
        "ansible_ssh_host_key_ecdsa_public": "AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBL3uDNc/N7Tk/TyBEQ24GSltDQoTQsgQz7TFi7zT7Cbs4rO0RkSOIGKkIIhE6DVUCKykQWFYDd0RNU4B7mW9ock=", 
        "ansible_ssh_host_key_ed25519_public": "AAAAC3NzaC1lZDI1NTE5AAAAIFrnH4spBNzwM7erURSGYFk73O1Xj9wp4fozWvwKxwq9", 
        "ansible_ssh_host_key_rsa_public": "AAAAB3NzaC1yc2EAAAADAQABAAABAQDjsL5mxLRgS0nHu53jhn0kfhA8luDeGTgTTrqF4u1Gqgs/ghW/wez1JuezmtmRU5b3wu0bBneHNwzc0ZUsWzkbeFKh7pMhTbCuQ8yBkSaAfGSJ9n6/QqlkvkUw8rCuPqLaU2WN2C0y90LqWyrJ1LQiq5wBFxpNS/Jc8C+sKKtTbHlqMjZKgpjDyvMPFR7I2d8/u/yujR6UZoYWwFTJ9DOww5NJ4x/7fytDtn4pDXrYCWZPoc1UvM47cpXt8mCHbQlFkqzj8XnpoFRbyCNZfq1AzLszdeq4RG6flNirEMTGjHmWVAH2aYWFwX0oShjAB8/0rvH51EKKTyDWXc8VjxwV", 
        "ansible_swapfree_mb": 2047, 
        "ansible_swaptotal_mb": 2047, 
        "ansible_system": "Linux", 
        "ansible_system_capabilities": [
            "cap_chown", 
            "cap_dac_override", 
            "cap_dac_read_search", 
            "cap_fowner", 
            "cap_fsetid", 
            "cap_kill", 
            "cap_setgid", 
            "cap_setuid", 
            "cap_setpcap", 
            "cap_linux_immutable", 
            "cap_net_bind_service", 
            "cap_net_broadcast", 
            "cap_net_admin", 
            "cap_net_raw", 
            "cap_ipc_lock", 
            "cap_ipc_owner", 
            "cap_sys_module", 
            "cap_sys_rawio", 
            "cap_sys_chroot", 
            "cap_sys_ptrace", 
            "cap_sys_pacct", 
            "cap_sys_admin", 
            "cap_sys_boot", 
            "cap_sys_nice", 
            "cap_sys_resource", 
            "cap_sys_time", 
            "cap_sys_tty_config", 
            "cap_mknod", 
            "cap_lease", 
            "cap_audit_write", 
            "cap_audit_control", 
            "cap_setfcap", 
            "cap_mac_override", 
            "cap_mac_admin", 
            "cap_syslog", 
            "35", 
            "36+ep"
        ], 
        "ansible_system_capabilities_enforced": "True", 
        "ansible_system_vendor": "VMware, Inc.", 
        "ansible_uptime_seconds": 8869, 
        "ansible_user_dir": "/root", 
        "ansible_user_gecos": "root", 
        "ansible_user_gid": 0, 
        "ansible_user_id": "root", 
        "ansible_user_shell": "/bin/bash", 
        "ansible_user_uid": 0, 
        "ansible_userspace_architecture": "x86_64", 
        "ansible_userspace_bits": "64", 
        "ansible_virtualization_role": "guest", 
        "ansible_virtualization_type": "VMware", 
        "discovered_interpreter_python": "/usr/bin/python", 
        "gather_subset": [
            "all"
        ], 
        "module_setup": true
    }, 
    "changed": false
}
过滤

一般不会直接使用,因为信息太多了,正常是通过过滤器来筛选我们需要的内容,通常使用以下的语句

ansible test1 -m setup -a "filter=ansible_nodename"
ansible test1 -m setup -a "filter=ansible_hostname"
ansible test1 -m setup -a "filter=ansible_domain"
ansible test1 -m setup -a "filter=ansible_memtotal_mb"
ansible test1 -m setup -a "filter=ansible_memory_mb"
ansible test1 -m setup -a "filter=ansible_memfree_mb"
ansible test1 -m setup -a "filter=ansible_os_family"
ansible test1 -m setup -a "filter=ansible_distribution_major_version"
ansible test1 -m setup -a "filter=ansible_distribution_version"
ansible test1 -m setup -a "filter=ansible_processor_vcpus"
ansible test1 -m setup -a "filter=ansible_all_ipv4_addresses"   #获取所有ip地址
ansible test1 -m setup -a "filter=ansible_default_ipv4"         #获取默认ip地址
ansible test1 -m setup -a "filter=ansible_architecture"
ansible test1 -m setup -a "filter=ansible_uptime_seconds"
ansible test1 -m setup -a "filter=ansible_processor*"
ansible test1 -m setup -a 'filter=ansible_env'

Debug 模块

功能

此模块可以用于输出信息,并且通过 msg 定制输出的信息内容

注意

msg后面的变量有时需要加 ” ” 引起来

说明

此模块一般不直接在ansible下直接使用,通常是通过ansible-playbook来使用

使用

#创建一个debug.yaml,用于获取主机名和ip地址
[root@test-master ansible]# cat debug.yaml 
---
- hosts: test
  tasks:
    - name: output variables
      debug:
        msg: Host "{{ ansible_nodename }}" IP "{{ ansible_default_ipv4.address }}"
#执行剧本
[root@test-master ansible]# ansible-playbook debug.yaml 

PLAY [test] ********************************************************************************************************************

TASK [Gathering Facts] *********************************************************************************************************
ok: [192.168.239.30]
ok: [192.168.239.20]

TASK [output variables] ********************************************************************************************************
ok: [192.168.239.20] => {
    "msg": "Host \"test1\" IP \"192.168.239.20\""       #获取到了主机名和ip地址
}
ok: [192.168.239.30] => {
    "msg": "Host \"slave2\" IP \"192.168.239.30\""
}

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

Playbook

playbook简介

  • playbook 剧本是由一个或多个”play”组成的列表
  • play的主要功能在于将预定义的一组主机,装扮成事先通过ansible中的task定义好的角色。Task实际是调用ansible的一个module,将多个play组织在一个playbook中,即可以让它们联合起来,按事先编排的机制执行预定义的动作
  • Playbook 文件是采用YAML语言编写的

为什么需要使用playbook,这就需要做个类比了,在linux中我们可以一条一条的执行我们的系统命令,但是有时候为了偷懒我们会将很多的系统命令写入一个shell脚本中,这样第一便于保存,我们可以发送给别的人;第二避免不必要的麻烦,因为每次都手动输入一遍命令繁琐不说还容易出错,但是写成一个固定的脚本文件,只要成功执行一次之后都不会出现错误。之前我们介绍的各个ansible模块就相当于我们的linux中的系统命令,playbook就相当于我们的shell脚本。

YAML语言

YAML 语言介绍

YAML:YAML Ain’t Markup Language,即YAML不是标记语言。不过,在开发的这种语言时,YAML的意思其实是:”Yet Another Markup Language”(仍是一种标记语言)
YAML是一个可读性高的用来表达资料序列的格式。YAML参考了其他多种语言,包括:XML、C语言、Python、Perl以及电子邮件格式RFC2822等。Clark Evans在2001年在首次发表了这种语言,另外Ingydöt Net与Oren Ben-Kiki也是这语言的共同设计者,目前很多最新的软件比较流行采用此格式的文件存放配置信息,如:ubuntu,anisble,docker,kubernetes等

YAML 官方网站:http://www.yaml.org
ansible 官网: https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html

YAML 语言特性

  • YAML的可读性好
  • YAML和脚本语言的交互性好
  • YAML使用实现语言的数据类型
  • YAML有一个一致的信息模型
  • YAML易于实现
  • YAML可以基于流来处理
  • YAML表达能力强,扩展性好

YAML语法简介

  • 在单一文件第一行,用连续三个连字号”-” 开始,还有选择性的连续三个点号( … )用来表示文件的结尾
  • 次行开始正常写Playbook的内容,一般建议写明该Playbook的功能
  • 使用#号注释代码
  • 缩进必须是统一的,不能空格和tab混用
  • 缩进的级别也必须是一致的,同样的缩进代表同样的级别,程序判别配置的级别是通过缩进结合换行来实现的
  • YAML文件内容是区别大小写的,key/value的值均需大小写敏感
  • 多个key/value可同行写也可换行写,同行使用,分隔
  • key后面冒号要加一个空格 比如: key: value
  • value可是个字符串,也可是另一个列表
  • YAML文件扩展名通常为yml或yaml

支持的数据类型

YAML 支持以下常用几种数据类型:

  • 标量:单个的、不可再分的值
  • 对象:键值对的集合,又称为映射(mapping)/ 哈希(hashes) / 字典(dictionary)
  • 数组:一组按次序排列的值,又称为序列(sequence) / 列表(list)

标量——scalar

就是一个键值对,可以写在一行,也可以分为两行

写在一行——键与值之间通过冒号+空格来分隔

name: zhang
age: 20

写在多行——键后面写一个冒号,然后换行,换行后需要补两个或三个空格(不固定,但是同一个yml中需要一致),然后再写值

name:
  zhangsan
age:
  20

标量是最基本的,不可再分的值,包括:

  • 字符串
  • 布尔值
  • 整数
  • 浮点数
  • Null
  • 时间
  • 日期

字典——Dictionary

字典由多个key与value(键值对)构成,字典名与键值对可以写在一行,也可以写在多行

写在一行——字典名后面加冒号+空格,键值对写在“{”与“}”之间,注意键值对和“{” “}”之间需要留空格,多个键值对通过逗号分隔

student: { name: zhangsan, age: 20 }

写在多行——字典名独占一行,之后就是每个键值对独占一行,当然字典名与键之间换行后也需要补空格

student:
  name: zhangsan
  age: 20

列表——List

列表由多个元素组成,可以写在一行也可以写在一行

写在一行——将所有元素用 [ ] 括起来放在同一行

colors: [ yellow, green, red ]

写在多行——每个元素放在不同行,且元素前均使用”-“打头,并且 – 后有一个空格

color:
  - yellow
  - green
  - blue

说明:这个元素可以是字典,可以是普通的键值对,也可以是最最普通的元素

color:
  - yellow: gold
  - green: lime
  - blue: deepskyblue

练习:使用yaml表示一个家庭

name: John Smith
age: 41
gender: Male

spouse: { name: Jane Smith, age: 37, gender: Female }   # 写在一行里
spouse:                                                 #也可以写成多行
  name: Jane Smith  
  age: 37
  gender: Female


children: [ {name: Jimmy Smith,age: 17, gender: Male}, {name: Jenny Smith, age: 13, gender: Female}, {name: hao Smith, age: 20, gender: Male } ]    #写在一行
children:                                                       #写在多行,更为推荐的写法
  - name: Jimmy Smith   
    age: 17
    gender: Male
  - {name: Jenny Smith, age: 13, gender: Female}                #这里也可以写成多行
  - {name: hao Smith, age: 20, gender: Male } 

小技巧

每次都要敲N多个空格太麻烦了,还不容易对齐,我们可以修改一下在vim中tab键作为多个空格使用,之后只需要直接tab一下就搞定了,我这边将tab设置为两个空格

[root@test-master ~]# vim /etc/vimrc 
#直接添加以下内容即可
set ts=2

三种常见的数据格式

介绍

  • XML:Extensible Markup Language,可扩展标记语言,可用于数据交换和配置
  • JSON:JavaScript Object Notation, JavaScript 对象表记法,主要用来数据交换或配置,不支持注释
  • YAML:YAML Ain’t Markup Language YAML 不是一种标记语言, 主要用来配置,大小写敏感,不支持tab

优缺点

XML:太冗余了,格式文件比真正的要传递的信息多得多,所以不太使用

JSON:简洁很多,可以写在多行也可以写在一行,但是因为不能添加注释,所以对我们人来说不太友好,所以一般是用来让程序识别分析

YAML:有严格的格式要求,但是很简洁,所以适合我们人来查看,所以适合用来做配置文件

转换工具

可以用工具互相转换,参考网站:

https://www.json2yaml.com/
http://www.bejson.com/json/json2yaml/

Playbook核心组件

官方文档:https://docs.ansible.com/ansible/latest/reference_appendices/playbooks_keywords.html#playbook-keywords

一个playbook 中由多个组件组成,其中所用到的常见组件类型如下:

  • Hosts :执行的远程主机列表
  • Tasks :任务集,由多个task的元素组成的列表实现,每个task是一个字典,一个完整的代码块功能需最少元素需包括 name 和 task,一个name只能包括一个task
  • Variables :内置变量或自定义变量在playbook中调用
  • Templates :模板,可替换模板文件中的变量并实现一些简单逻辑的文件
  • Handlers 和 notify 结合使用,由特定条件触发的操作,满足条件方才执行,否则不执行
  • tags :标签 指定某条任务执行,用于选择运行playbook中的部分代码。ansible具有幂等性,因此会自动跳过没有变化的部分,即便如此,有些代码为测试其确实没有发生变化的时间依然会非常地长。此时,如果确信其没有变化,就可以通过tags跳过此些代码片断

hosts组件

Hosts:playbook中的每一个play的目的都是为了让特定主机以某个指定的用户身份执行任务。hosts用于指定要执行指定任务的主机须事先定义在主机清单中

- hosts: test1:test2                #test1组或者test2组

remote_user组件

remote_user: 设置登录到被控端使用的用户,可以不指定,默认为root执行。可用于Host和task中。也可以通过指定其通过sudo的方式在远程主机上执行任务,其可用于play全局或某任务;此外,甚至可以在sudo时使用sudo_user指定sudo时切换的用户。

- hosts: test1:test2
  remote_user: root                 #默认就是root,可以省略不写

task列表与action组件

play的主体部分是task list,task list中有一个或多个task,各个task 依次逐个在hosts中指定的所有主机上执行,即在所有主机上完成第一个task后,再开始第二个task
task的目的是使用指定的参数执行模块,而在模块参数中可以使用变量。模块执行是幂等的,这意味着多次执行是安全的,因为其结果均一致
每个task都应该有其name,用于playbook的执行结果输出,建议其内容能清晰地描述任务执行步骤。
如果未提供name,则action的结果将用于输出

task格式

action: module arguments                        #示例: action: shell wall hello
module: arguments   建议使用                     #示例: shell: wall hello

注意:shell和command模块后面跟命令,直接在冒号后面写就行了,不是写成shell=wall hello

[root@test-master test]# cat test.yml 
---
- hosts: test1:test2        #主机列表
  remote_user: root         #登录用户
                            #空行可有可无,习惯写一下分个段
  tasks:                    #任务列表
    - name: ping            #第一个任务(列表中的第一个元素)
      ping:                     #ping模块
    - name: wall            #第二个任务(列表中的第二个元素)
      shell: wall hello         #shell模块

playbook命令

格式

ansible-playbook <filename.yml> ... [options]

选项

--syntax-check          #语法检查,可缩写成--syntax, 相当于bash -n
-C --check              #模拟执行,只检测可能会发生的改变,但不真正执行操作,dry run
--list-hosts            #列出运行任务的主机
--list-tags             #列出tag
--list-tasks            #列出task
--limit 主机列表         #只针对主机列表中的特定主机执行
-i INVENTORY            #指定主机清单文件,通常一个项对应一个主机清单文件
-t TAGS                 #指定任务标签,进行执行
--start-at-task START_AT_TASK #从指定task开始执行,而非从头开始,START_AT_TASK为任务的name
-v -vv  -vvv            #显示过程
-e                      #指定变量

使用

检查

#语法检查
[root@test-master test]# ansible-playbook test.yml --syntax-check

playbook: test.yml


#模拟执行(不会真正的执行)
[root@test-master test]# ansible-playbook test.yml -C

PLAY [test1:test2] *************************************************************************************************************

TASK [Gathering Facts] *********************************************************************************************************
ok: [192.168.239.30]
ok: [192.168.239.20]

TASK [ping] ********************************************************************************************************************
ok: [192.168.239.20]
ok: [192.168.239.30]

TASK [wall] ********************************************************************************************************************
skipping: [192.168.239.30]
skipping: [192.168.239.20]

PLAY RECAP *********************************************************************************************************************
192.168.239.20             : ok=2    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   
192.168.239.30             : ok=2    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   

查看

#查看主机
[root@test-master test]# ansible-playbook test.yml --list-hosts
playbook: test.yml
  play #1 (test1:test2): test1:test2    TAGS: []
    pattern: [u'test1:test2']
    hosts (2):
      192.168.239.20
      192.168.239.30

#列出tag
[root@test-master test]# ansible-playbook test.yml --list-tags
playbook: test.yml
  play #1 (test1:test2): test1:test2    TAGS: []
      TASK TAGS: []

#列出task
[root@test-master test]# ansible-playbook test.yml --list-tasks
playbook: test.yml
  play #1 (test1:test2): test1:test2    TAGS: []
    tasks:
      ping  TAGS: []
      wall  TAGS: []

使用

#不加选项直接执行
[root@test-master test]# ansible-playbook test.yml

PLAY [test1:test2] *************************************************************************************************************

TASK [Gathering Facts] *********************************************************************************************************
ok: [192.168.239.20]
ok: [192.168.239.30]

TASK [ping] ********************************************************************************************************************
ok: [192.168.239.20]
ok: [192.168.239.30]

TASK [wall] ********************************************************************************************************************
changed: [192.168.239.30]
changed: [192.168.239.20]

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

#指定主机执行
[root@test-master test]# ansible-playbook test.yml --limit test1

PLAY [test1:test2] *************************************************************************************************************

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

TASK [ping] ********************************************************************************************************************
ok: [192.168.239.20]

TASK [wall] ********************************************************************************************************************
changed: [192.168.239.20]

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


#指定开始的任务
[root@test-master test]# ansible-playbook test.yml --start-at-task wall

PLAY [test1:test2] *************************************************************************************************************

TASK [Gathering Facts] *********************************************************************************************************
ok: [192.168.239.20]
ok: [192.168.239.30]

TASK [wall] ********************************************************************************************************************
changed: [192.168.239.30]
changed: [192.168.239.20]

PLAY RECAP *********************************************************************************************************************
192.168.239.20             : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
192.168.239.30             : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

提示

我们查看执行结果可以看到有一个任务TASK [Gathering Facts] ,但是这个任务并不是我们创建的,是系统默认执行的一个进行信息收集的,所以如果没有特殊的需求,建议关闭此任务,关闭此任务可以提高任务处理速度。

#修改yml文件
[root@test-master test]# cat test.yml 
---
- hosts: test1:test2
  remote_user: root
  gather_facts: no              #添加此行内容即可

  tasks:
    - name: ping
      ping:
    - name: wall
      shell: wall hello

[root@test-master test]# ansible-playbook test.yml -v
Using /etc/ansible/ansible.cfg as config file

PLAY [test1:test2] *************************************************************************************************************

TASK [ping] ********************************************************************************************************************
ok: [192.168.239.30] => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "ping": "pong"}
ok: [192.168.239.20] => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "ping": "pong"}

TASK [wall] ********************************************************************************************************************
changed: [192.168.239.30] => {"changed": true, "cmd": "wall hello", "delta": "0:00:00.004210", "end": "2022-11-17 22:45:13.524508", "rc": 0, "start": "2022-11-17 22:45:13.520298", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
changed: [192.168.239.20] => {"changed": true, "cmd": "wall hello", "delta": "0:00:00.003911", "end": "2022-11-17 22:45:13.858363", "rc": 0, "start": "2022-11-17 22:45:13.854452", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}

PLAY RECAP *********************************************************************************************************************
192.168.239.20             : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
192.168.239.30             : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  

playbook基础用法

利用playbook安装httpd

写yml文件

[root@test-master ansible]# cat http/install_http.yml 
---
- hosts: test1
  gather_facts: no

  tasks:
    - name: install http
      yum: 
        name: httpd
        state: present

    - name: modify listen port
      lineinfile:
        path: /etc/httpd/conf/httpd.conf
        regexp: "^Listen 80"
        line: "Listen 8080"

    - name: mkdir dir
      file: path=/data/html state=directory

    - name: modify config file1
      lineinfile:
        path: /etc/httpd/conf/httpd.conf
        regexp: '^DocumentRoot "/var/www/html"'
        line: 'DocumentRoot "/data/html"'

    - name: modify config file2
      lineinfile:
        path: /etc/httpd/conf/httpd.conf
        regexp: '^<Directory "/var/www/html">'
        line: '<Directory "/data/html">'

    - name: copy html
      copy:
        src: /root/index.html 
        dest: /data/html/

    - name: start httpd.service
      service:
        name: httpd
        state: started
        enabled: yes

检查

#写好yml文件后一定要记得检验一下-C或者--syntax-check都可以
[root@test-master ansible]# ansible-playbook http/install_http.yml -C

PLAY [test1] *******************************************************************************************************************

TASK [install http] ************************************************************************************************************
ok: [192.168.239.20]

TASK [modify listen port] ******************************************************************************************************
ok: [192.168.239.20]

TASK [mkdir dir] ***************************************************************************************************************
ok: [192.168.239.20]

TASK [modify config file1] *****************************************************************************************************
ok: [192.168.239.20]

TASK [modify config file2] *****************************************************************************************************
ok: [192.168.239.20]

TASK [copy html] ***************************************************************************************************************
ok: [192.168.239.20]

TASK [start httpd.service] *****************************************************************************************************
ok: [192.168.239.20]

PLAY RECAP *********************************************************************************************************************
192.168.239.20             : ok=7    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  

执行

#验证无误后就可以执行了
[root@test-master ansible]# ansible-playbook http/install_http.yml 

PLAY [test1] *******************************************************************************************************************

TASK [install http] ************************************************************************************************************
changed: [192.168.239.20]

TASK [modify listen port] ******************************************************************************************************
changed: [192.168.239.20]

TASK [mkdir dir] ***************************************************************************************************************
changed: [192.168.239.20]

TASK [modify config file1] *****************************************************************************************************
changed: [192.168.239.20]

TASK [modify config file2] *****************************************************************************************************
changed: [192.168.239.20]

TASK [copy html] ***************************************************************************************************************
changed: [192.168.239.20]

TASK [start httpd.service] *****************************************************************************************************
changed: [192.168.239.20]

PLAY RECAP *********************************************************************************************************************
192.168.239.20             : ok=7    changed=7    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

验证

#访问测试
[root@test-master ~]# curl 192.168.239.20:8080
hello world!

利用playbook卸载httpd

写yaml文件

#编写yaml文件
[root@test-master ansible]# cat http/remove.yml 
---
- hosts: test1
  gather_facts: no

  tasks:
    - name: stop httpd
      service:
        name: httpd
        state: stopped
        enabled: no

    - name: remove httpd
      yum:
        name: httpd
        state: absent

    - name: remove config file1
      file:
        path: /data/html
        state: absent

    - name: remove config file2
      file:
        path: /etc/httpd
        state: absent

检查

#--syntax
[root@test-master ansible]# ansible-playbook --syntax http/remove.yml 

playbook: http/remove.yml


#-C
[root@test-master ansible]# ansible-playbook -C http/remove.yml 

PLAY [test1] *******************************************************************************************************************

TASK [stop httpd] **************************************************************************************************************
changed: [192.168.239.20]

TASK [remove httpd] ************************************************************************************************************
changed: [192.168.239.20]

TASK [remove config file1] *****************************************************************************************************
changed: [192.168.239.20]

TASK [remove config file2] *****************************************************************************************************
changed: [192.168.239.20]

PLAY RECAP *********************************************************************************************************************
192.168.239.20             : ok=4    changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

执行

[root@test-master ansible]# ansible-playbook http/remove.yml 

PLAY [test1] *******************************************************************************************************************

TASK [stop httpd] **************************************************************************************************************
changed: [192.168.239.20]

TASK [remove httpd] ************************************************************************************************************
changed: [192.168.239.20]

TASK [remove config file1] *****************************************************************************************************
changed: [192.168.239.20]

TASK [remove config file2] *****************************************************************************************************
changed: [192.168.239.20]

PLAY RECAP *********************************************************************************************************************
192.168.239.20             : ok=4    changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

验证

#URL请求
[root@test-master ansible]# curl 192.168.239.20:8080
curl: (7) Failed connect to 192.168.239.20:8080; Connection refused

#查看页面源文件
[root@test-master ansible]# ansible test1 -m shell -a "ls -al /data"
192.168.239.20 | CHANGED | rc=0 >>
total 0
drwxr-xr-x.  2 root root   6 Nov 18 16:25 .
dr-xr-xr-x. 18 root root 256 Nov 17 23:50 ..

#查看端口
[root@test-master ansible]# ansible test1 -m shell -a "ss -ntl"
192.168.239.20 | CHANGED | rc=0 >>
State      Recv-Q Send-Q Local Address:Port               Peer Address:Port              
LISTEN     0      128          *:22                       *:*                  
LISTEN     0      100    127.0.0.1:25                       *:*                  
LISTEN     0      128       [::]:22                    [::]:*                  
LISTEN     0      100      [::1]:25                    [::]:*    

hosts文件

通常来说,在项目中我们每一个palybook都会有一个自己的目录,目录中存放该剧本的yml文件,以及hosts文件,但是没有办法直接调用我们自行创建的hosts文件,因为ansible的配置文件中定义了主机清单列表默认路径为/etc/ansible/hosts,如果我们希望可以使用自己创建的hosts文件可以在执行ansible-playbook的时候加上-i选项,然后指定我们希望加载的hosts文件

但是每一次都有加上-i还是很麻烦的,那就可以使用以下的方法,我们的主配置文件是可以存在多个位置的,而且不同位置的优先级不同,所以我们可以在每个剧本的目录下,创建一个主配置文件,在这个主配置文件中定义要调用的主机清单列表为当前目录下的hosts文件而不是系统默认的hosts文件

查看主配置文件中的主机清单列表

#默认的主配置文件中,主机清单列表的默认路径
[root@test-master ansible]# cat ansible.cfg | grep hosts
#inventory      = /etc/ansible/hosts

在剧本在创建主配置文件

#复制主配置文件到具体的playbook目录下
[root@test-master ansible]# cp ansible.cfg test/
[root@test-master ansible]# ls test/
ansible.cfg  test.yml


#修改test目录下的主配置文件
[root@test-master ansible]# cat test/ansible.cfg | grep hosts
inventory      = ./hosts                #将默认路径修改为主配置文件所在目录下的hosts文件

查看优先级

#查看主配置文件的优先级
[root@test-master ansible]# ansible --version       #在/etc/ansible目录下
ansible 2.9.27
  config file = /etc/ansible/ansible.cfg            #默认的主配置文件优先级最高
  configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python2.7/site-packages/ansible
  executable location = /usr/bin/ansible
  python version = 2.7.5 (default, Oct 14 2020, 14:45:30) [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)]

#切换到test目录下
[root@test-master ansible]# cd test/
[root@test-master test]# ansible --version          #在/etc/ansible/test目录下
ansible 2.9.27
  config file = /etc/ansible/test/ansible.cfg       #当前目录下的主配置文件优先级最高
  configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python2.7/site-packages/ansible
  executable location = /usr/bin/ansible
  python version = 2.7.5 (default, Oct 14 2020, 14:45:30) [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)]

设置test目录下的hosts文件

[root@test-master ansible]# cp hosts test/
[root@test-master ansible]# 
[root@test-master ansible]# vim test/hosts 
[test1]
192.168.239.20
192.168.239.30              #在test1组中添加一个30的地址

编写test.yml

[root@test-master ansible]# cat test/test.yml 
---
- hosts: test1
  remote_user: root
  gather_facts: no

  tasks:
    - name: ping
      ping:
    - name: wall
      shell: wall hello

在ansible目录下执行

[root@test-master ansible]# ansible-playbook test/test.yml 

PLAY [test1] *******************************************************************************************************************

TASK [ping] ********************************************************************************************************************
ok: [192.168.239.20]                        #没有30这个地址

TASK [wall] ********************************************************************************************************************
changed: [192.168.239.20]

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

切换到test目录下进行执行

[root@test-master test]# ansible-playbook test.yml 

PLAY [test1] *******************************************************************************************************************

TASK [ping] ********************************************************************************************************************
ok: [192.168.239.20]
ok: [192.168.239.30]                    #30生效

TASK [wall] ********************************************************************************************************************
changed: [192.168.239.20]
changed: [192.168.239.30]

PLAY RECAP *********************************************************************************************************************
192.168.239.20             : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
192.168.239.30             : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

此时我们test目录下的主配置文件生效,使用的是test目录下的hosts文件作为主机清单列表文件

playbook高级用法

忽略错误ignore_errors

说明

如果一个task出错,默认将不会继续执行后续的其它task
利用 ignore_errors: yes 可以忽略此task的错误,继续向下执行playbook其它task

实例

编写yml
[root@test-master ansible]# cat error/error.yml 
---
- hosts: test
  gather_facts: no

  tasks:
    - name: ping
      ping:

    - name: error
      shell: cat adqwd.txt          #这个文件不存在

    - name: wall
      shell: wall hello

语法检查
[root@test-master ansible]# ansible-playbook --syntax error/error.yml 

playbook: error/error.yml
执行
[root@test-master ansible]# ansible-playbook error/error.yml 

PLAY [test] ********************************************************************************************************************

TASK [ping] ********************************************************************************************************************
ok: [192.168.239.30]
ok: [192.168.239.20]

TASK [error]        #error任务是我们构建的错误任务,所以肯定无法执行,发现直接就不执行wall任务了     *******************************************************************************************************************
fatal: [192.168.239.30]: FAILED! => {"changed": true, "cmd": "cat adqwd.txt", "delta": "0:00:00.004191", "end": "2022-11-18 21:17:57.739673", "msg": "non-zero return code", "rc": 1, "start": "2022-11-18 21:17:57.735482", "stderr": "cat: adqwd.txt: No such file or directory", "stderr_lines": ["cat: adqwd.txt: No such file or directory"], "stdout": "", "stdout_lines": []}
fatal: [192.168.239.20]: FAILED! => {"changed": true, "cmd": "cat adqwd.txt", "delta": "0:00:00.003127", "end": "2022-11-18 21:17:57.812239", "msg": "non-zero return code", "rc": 1, "start": "2022-11-18 21:17:57.809112", "stderr": "cat: adqwd.txt: No such file or directory", "stderr_lines": ["cat: adqwd.txt: No such file or directory"], "stdout": "", "stdout_lines": []}

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


修改yml文件
[root@test-master ansible]# cat error/error.yml 
---
- hosts: test
  gather_facts: no

  tasks:
    - name: ping
      ping:

    - name: error
      shell: cat adqwd.txt
      ignore_errors: yes                #添加此选项忽略此任务的错误

    - name: wall
      shell: wall hello

再次执行
[root@test-master ansible]# ansible-playbook error/error.yml 

PLAY [test] ********************************************************************************************************************

TASK [ping] ********************************************************************************************************************
ok: [192.168.239.30]
ok: [192.168.239.20]

TASK [error]    #error任务依旧是错误,但是不影响wall任务的执行了*******************************************************************************************************************
fatal: [192.168.239.20]: FAILED! => {"changed": true, "cmd": "cat adqwd.txt", "delta": "0:00:00.003420", "end": "2022-11-18 21:23:09.766733", "msg": "non-zero return code", "rc": 1, "start": "2022-11-18 21:23:09.763313", "stderr": "cat: adqwd.txt: No such file or directory", "stderr_lines": ["cat: adqwd.txt: No such file or directory"], "stdout": "", "stdout_lines": []}
...ignoring
fatal: [192.168.239.30]: FAILED! => {"changed": true, "cmd": "cat adqwd.txt", "delta": "0:00:00.003042", "end": "2022-11-18 21:23:09.695230", "msg": "non-zero return code", "rc": 1, "start": "2022-11-18 21:23:09.692188", "stderr": "cat: adqwd.txt: No such file or directory", "stderr_lines": ["cat: adqwd.txt: No such file or directory"], "stdout": "", "stdout_lines": []}
...ignoring

TASK [wall]         #wall任务正常执行********************************************************************************************************************
changed: [192.168.239.20]
changed: [192.168.239.30]

PLAY RECAP *********************************************************************************************************************
192.168.239.20             : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=1          #ignored有一个了
192.168.239.30             : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=1 

触发任务handlers与notify

说明

Handlers也是一个任务列表与tasks列表平级,它类似于MySQL中的触发器触发的行为,其中的task与tasks列表中的task没有什么本质上的不同,只不过tasks列表中的任务是按顺序执行,而handlers中的任务是只有在tasks中特定的任务发生变化时才会执行。

Notify就是用来监控这个特定任务的。

  • handlers要触发的动作
  • notify要监控那个动作

注意

  • 如果多个task通知了相同的handlers, 此handlers仅会在所有tasks结束后运行一 次。
  • 只有notify对应的task发生改变了才会通知handlers, 没有改变则不会触发handlers
  • handlers 是在所有前面的tasks都成功执行才会执行,如果前面任何一个task失败,会导致handler跳过执行,可以使用force_handlers: yes 强制执行handler

实例—handlers任务调用

创建剧本文件
[root@test-master ansible]# cat http/install_http_copy.yml
---
- hosts: test1
  gather_facts: no

  tasks:
    - name: install http
      yum: 
        name: httpd
        state: present

    - name: copy conf
      copy:
        src: /etc/httpd/conf/httpd.conf     #复制本地的httpd.conf文件到被控端
        dest: /etc/httpd/conf/httpd.conf

    - name: mkdir dir
      file: path=/data/html state=directory

    - name: copy html
      copy:
        src: /root/index.html 
        dest: /data/html/

    - name: start httpd.service
      service:
        name: httpd
        state: started
        enabled: yes
执行yml文件
[root@test-master ansible]# ansible-playbook http/install_http_copy.yml 

PLAY [test1] ***********************************************************************************************************************************

TASK [install http] ****************************************************************************************************************************
changed: [192.168.239.20]

TASK [copy conf] *******************************************************************************************************************************
changed: [192.168.239.20]

TASK [mkdir dir] *******************************************************************************************************************************
changed: [192.168.239.20]

TASK [copy html] *******************************************************************************************************************************
changed: [192.168.239.20]

TASK [start httpd.service] *********************************************************************************************************************
changed: [192.168.239.20]

PLAY RECAP *************************************************************************************************************************************
192.168.239.20             : ok=5    changed=5    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

发起访问
[root@test-master ansible]# curl 192.168.239.20
hello world!
修改conf文件

处于安全性考虑,我们需要将默认端口号80修改为8080,我们直接修改配置文件

[root@test-master ansible]# cat /etc/httpd/conf/httpd.conf | grep Listen
# Listen: Allows you to bind Apache to specific IP addresses and/or
# Change this to Listen on specific IP addresses as shown below to 
#Listen 12.34.56.78:80
Listen 8080                                 #将端口号修改为8080
再次执行yml文件
[root@test-master ansible]# ansible-playbook http/install_http_copy.yml 

PLAY [test1] ***********************************************************************************************************************************

TASK [install http] ****************************************************************************************************************************
ok: [192.168.239.20]

TASK [copy conf] *******************************************************************************************************************************
changed: [192.168.239.20]

TASK [mkdir dir] *******************************************************************************************************************************
ok: [192.168.239.20]

TASK [copy html] *******************************************************************************************************************************
ok: [192.168.239.20]

TASK [start httpd.service] *********************************************************************************************************************
ok: [192.168.239.20]

PLAY RECAP *************************************************************************************************************************************
192.168.239.20             : ok=5    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
#发现有一个修改
再次访问
发现8080端口并不能访问,可以被访问的还是80端口
[root@test-master ansible]# curl 192.168.239.20
hello world!

[root@test-master ansible]# curl 192.168.239.20:8080
curl: (7) Failed connect to 192.168.239.20:8080; Connection refused

这是因为我们虽然修改了配置文件,但是没有重启服务,这就导致配置文件并没有生效。下面就使用handlers和notify来解决这个问题

修改yml文件
[root@test-master ansible]# cat http/install_http_copy.yml 
---
- hosts: test1
  gather_facts: no

  tasks:
    - name: install http
      yum: 
        name: httpd
        state: present

    - name: copy conf
      copy:
        src: /etc/httpd/conf/httpd.conf
        dest: /etc/httpd/conf/httpd.conf
      notify: restart httpd     #添加监测点,当copy conf任务执行之后就会触发restart httpd任务

    - name: mkdir dir
      file: path=/data/html state=directory

    - name: copy html
      copy:
        src: /root/index.html 
        dest: /data/html/

    - name: start httpd.service
      service:
        name: httpd
        state: started
        enabled: yes

  handlers:
    - name: restart httpd       #当监测点发现任务copy conf任务执行就会执行此任务(用于重启服务)
      service:
        name: httpd
        state: restarted
修改conf文件

随便在注释行添加一个空格什么都可以,只要conf文件发生变化,这样执行yml文件的时候就会触发copy conf任务,触发copy conf任务就会触发restart httpd任务,这样就会使得8080端口生效

再次执行yml文件
[root@test-master ansible]# ansible-playbook http/install_http_copy.yml 

PLAY [test1] ***********************************************************************************************************************************

TASK [install http] ****************************************************************************************************************************
ok: [192.168.239.20]

TASK [copy conf] *******************************************************************************************************************************
changed: [192.168.239.20]

TASK [mkdir dir] *******************************************************************************************************************************
ok: [192.168.239.20]

TASK [copy html] *******************************************************************************************************************************
ok: [192.168.239.20]

TASK [start httpd.service] *********************************************************************************************************************
ok: [192.168.239.20]

RUNNING HANDLER [restart httpd]     #触发handler中的任务****************************************************************************************************************
changed: [192.168.239.20]

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



再次访问
80端口无法被访问了,8080成功被访问
[root@test-master ansible]# curl 192.168.239.20
curl: (7) Failed connect to 192.168.239.20:80; Connection refused
[root@test-master ansible]# curl 192.168.239.20:8080
hello world!

实例—handlers强制调用

handlers 是在所有前面的tasks都成功执行才会执行,如果前面任何一个task失败,会导致handler跳过执行,可以使用force_handlers: yes 强制执行handler

修改yml文件

修改install_http_copy.yml 文件,将其中的web页面文件路径改错

[root@test-master ansible]# cat http/install_http_copy.yml 
---
- hosts: test1
  gather_facts: no

  tasks:
    - name: install http
      yum: 
        name: httpd
        state: present

    - name: copy conf
      copy:
        src: /etc/httpd/conf/httpd.conf
        dest: /etc/httpd/conf/httpd.conf
      notify: restart httpd

    - name: mkdir dir
      file: path=/data/html state=directory

    - name: copy html
      copy:
        src: /root/haha.html        #此文件不存在
        dest: /data/html/

    - name: start httpd.service
      service:
        name: httpd
        state: started
        enabled: yes

  handlers:
    - name: restart httpd
      service:
        name: httpd
        state: restarted

修改配置文件

在配置文件中做一些无效修改,触发copy conf任务的执行

[root@test-master ansible]# cat /etc/httpd/conf/httpd.conf | grep Listen
# Listen: Allows you to bind Apache to specific IP addresses and/or
# Change this to Listen on specific IP addresses as shown below to 
#    Listen 12.34.56.78:80          #多加了几个空格
Listen 8080
执行yml文件
虽然copy conf任务执行了,但是不会导致handler中的任务执行,这是因为后面的copy html任务执行失败
[root@test-master ansible]# ansible-playbook http/install_http_copy.yml 

PLAY [test1] ***********************************************************************************************************************************

TASK [install http] ****************************************************************************************************************************
ok: [192.168.239.20]

TASK [copy conf]        #此任务执行了*******************************************************************************************************************************
changed: [192.168.239.20]

TASK [mkdir dir] *******************************************************************************************************************************
ok: [192.168.239.20]

TASK [copy html] *******************************************************************************************************************************
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: If you are using a module and expect the file to exist on the remote, see the remote_src option
fatal: [192.168.239.20]: FAILED! => {"changed": false, "msg": "Could not find or access '/root/haha.html' on the Ansible Controller.\nIf you are using a module and expect the file to exist on the remote, see the remote_src option"}

RUNNING HANDLER [restart httpd]         #没有执行****************************************************************************************************************

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

再次修改yml文件

添加强制执行handlers任务的语句

[root@test-master ansible]# cat http/install_http_copy.yml 
---
- hosts: test1
  gather_facts: no
  force_handlers: yes           #添加强制执行handlers任务的语句

  tasks:
    - name: install http
      yum: 
        name: httpd
        state: present

    - name: copy conf
      copy:
        src: /etc/httpd/conf/httpd.conf
        dest: /etc/httpd/conf/httpd.conf
      notify: restart httpd

    - name: mkdir dir
      file: path=/data/html state=directory

    - name: copy html
      copy:
        src: /root/haha.html 
        dest: /data/html/

    - name: start httpd.service
      service:
        name: httpd
        state: started
        enabled: yes

  handlers:
    - name: restart httpd
      service:
        name: httpd
        state: restarted

再次执行yml文件
[root@test-master ansible]# ansible-playbook http/install_http_copy.yml 

PLAY [test1] ***********************************************************************************************************************************

TASK [install http] ****************************************************************************************************************************
ok: [192.168.239.20]

TASK [copy conf] *******************************************************************************************************************************
changed: [192.168.239.20]

TASK [mkdir dir] *******************************************************************************************************************************
ok: [192.168.239.20]

TASK [copy html] *******************************************************************************************************************************
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: If you are using a module and expect the file to exist on the remote, see the remote_src option
fatal: [192.168.239.20]: FAILED! => {"changed": false, "msg": "Could not find or access '/root/haha.html' on the Ansible Controller.\nIf you are using a module and expect the file to exist on the remote, see the remote_src option"}

RUNNING HANDLER [restart httpd] ****************************************************************************************************************
changed: [192.168.239.20]

PLAY RECAP *************************************************************************************************************************************
192.168.239.20             : ok=4    changed=2    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   

任务标签tags组件

在playbook文件中,可以利用tags组件,为特定 task 指定标签,当在执行playbook时,可以只执行特定tags的task,而非整个playbook文件
可以一个task对应多个tag,也可以多个task对应一个tag
还有另外3个特殊关键字用于标签, tagged, untagged 和 all,它们分别是仅运行已标记,只有未标记和所有任务。

使用

编写yml文件
[root@test-master ansible]# cat test/test.yml 
---
- hosts: test1
  remote_user: root
  gather_facts: no

  tasks:
    - name: ping
      ping:
      tags: [ heihei ]              #打标签
    - name: wall
      shell: wall hello
      tags: [ haha,lala ]           #写在一行
        - haha                      #写在多行
        - lala
查看标签
[root@test-master ansible]# ansible-playbook --list-tags test/test.yml 

playbook: test/test.yml

  play #1 (test1): test1    TAGS: []
      TASK TAGS: [haha, heihei, lala]
使用标签
[root@test-master ansible]# ansible-playbook -t heihei test/test.yml 

PLAY [test1] ***********************************************************************************************************************************

TASK [ping] ************************************************************************************************************************************
ok: [192.168.239.20]

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



[root@test-master ansible]# ansible-playbook -t lala test/test.yml 

PLAY [test1] ***********************************************************************************************************************************

TASK [wall] ************************************************************************************************************************************
changed: [192.168.239.20]

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

playbook中变量的使用

变量名

仅能由字母、数字和下划线组成,且只能以字母开头

变量定义

variable=value
variable: value

变量调用方式

通过 {{ variable_name }} 调用变量,且变量名前后建议加空格,有时用”{{ variable_name }}“才生效

变量来源

  • ansible 的 setup facts 远程主机的所有变量都可直接调用,需要设置gather_facts:yes
  • 通过命令行指定变量,优先级最高,通过-e选项指定
  • 在playbook文件中定义,通过vars字典声明变量
  • 在独立的变量YAML文件中定义,通过vars_files指定变量yaml文件名
  • 在主机清单文件中定义
    主机(普通)变量:主机组中主机单独定义,优先级高于公共变量
    组(公共)变量:针对主机组中所有主机定义统一变量
  • 在项目中针对主机和主机组定义
    在项目目录中创建 host_vars和group_vars目录
  • 在role中定义
变量的优先级从高到低如下:
-e 选项定义变量 -->playbook中vars_files --> playbook中vars变量定义 -->host_vars/主机名文件 -->主机清单中主机变量--> group_vars/主机组名文件-->group_vars/all文件--> 主机清单组变量

使用setup模块中变量

作用

本模块自动在playbook调用,不要用ansible命令调用,生成的系统状态信息, 并存放在facts变量中

facts 包括的信息很多,如: 主机名,IP,CPU,内存,网卡等

使用

facts 变量的实际使用场景案例

  • 通过facts变量获取被控端CPU的个数信息,从而生成不同的Nginx配置文件
  • 通过facts变量获取被控端内存大小信息,从而生成不同的memcached的配置文件
  • 通过facts变量获取被控端主机名称信息,从而生成不同的Zabbix配置文件
创建主机名的日志文件
#编写yaml文件
[root@test-master ansible]# cat var1.yml 
---
- hosts: test1
  gather_facts: yes

  tasks:
    - name: cerate log file
      file:
        name: /data/{{ ansible_nodename }}.log      #调用facts中的变量生成日志文件
        state: touch

#执行yaml文件
[root@test-master ansible]# ansible-playbook var1.yml 

PLAY [test1] ***********************************************************************************************************************************

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

TASK [cerate log file] *************************************************************************************************************************
changed: [192.168.239.20]

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

#查看被控端是否生成日志文件
[root@test-master ansible]# ansible test1 -m shell -a "ls -l /data"
192.168.239.20 | CHANGED | rc=0 >>
total 0
drwxr-xr-x 2 root root 24 Nov 21 17:46 html
-rw-r--r-- 1 root root  0 Nov 21 19:54 slave1.log       #生成以主机名开头的日志文件
以IP地址为文件名
#编写yml文件
[root@test-master ansible]# cat ip.yml 
---
- hosts: test1

  tasks:
    - name: show ens33 ip add {{ ansible_ens33.ipv4.address }}  #第一种指定ip地址方式
      debug:
        msg: IP address {{ ansible_facts["ens33"]["ipv4"]["address"] }} #第二种指定ip地址方式
#执行yml文件
[root@test-master ansible]# ansible-playbook ip.yml 

PLAY [test1] ***********************************************************************************************************************************

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

TASK [show ens33 ip add 192.168.239.20] ********************************************************************************************************
ok: [192.168.239.20] => {
    "msg": "IP address 192.168.239.20"
}

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


在playbook命令行中定义变量

在使用ansible-palybook的时候,使用-e选项指定变量,如果通过-e选项指定的是一个文件,需要在文件名前加上@符号进行标识

直接在命令行指定变量

#编写yml文件
[root@test-master ansible]# cat var2.yml 
---
- hosts: test1
  gather_facts: no

  tasks:
    - name: install package 
      yum:
        name: "{{ pack }}"              #声明变量pack注意需要使用双引号
        state: present
#执行yaml文件
[root@test-master ansible]# ansible-playbook -e pack=vsftpd var2.yml 

PLAY [test1] ***********************************************************************************************************************************

TASK [install package] *************************************************************************************************************************
changed: [192.168.239.20]

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

成功下载指定的软件

在外部文件中指定变量

#编辑外部变量文件
[root@test-master ansible]# cat pack.txt 
pack1: vsftpd               #变量与值之间通过冒号空格分隔
pack2: memcached

#编写yml文件
[root@test-master ansible]# cat pack.txt 
pack1: vsftpd
pack2: memcached
[root@test-master ansible]# cat var3.yml 
---
- hosts: test1
  gather_facts: no

  tasks:
    - name: install {{ pack1 }}
      yum:
        name: "{{ pack1 }}"
        state: present

    - name: install {{ pack2 }}
      yum:
        name: "{{ pack2 }}"
        state: present

#调用外部变量文件,执行yml文件
[root@test-master ansible]# ansible-playbook -e "@pack.txt" var3.yml 

PLAY [test1] ***********************************************************************************************************************************

TASK [install vsftpd] **************************************************************************************************************************
changed: [192.168.239.20]

TASK [install memcached] ***********************************************************************************************************************
changed: [192.168.239.20]

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

在playbook文件中定义变量

直接定义

[root@test-master ansible]# cat var4.yml 
---
- hosts: test1
  gather_facts: no
  vars:
    file_path: /data/lala.txt           #定义变量

  tasks:
    - name: touch file
      file:
        path: "{{ file_path }}"
        state: touch

[root@test-master ansible]# ansible-playbook var4.yml 

PLAY [test1] ***********************************************************************************************************************************

TASK [touch file] ******************************************************************************************************************************
changed: [192.168.239.20]

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

变量相互调用

[root@test-master ansible]# cat var5.yml 
---
- hosts: test1
  gather_facts: yes
  vars:
    file_path: "/data/{{ ansible_ens33.ipv4.address }}"     #定义变量使用facts中的信息
    houzhui: txt
    file_name: "{{ file_path }}.{{ houzhui }}"              #将两个变量的值组合成一个新的变量

  tasks:
    - name: touch file
      file:
        path: "{{ file_name }}"
        state: touch

[root@test-master ansible]# ansible-playbook var5.yml 

PLAY [test1] ***********************************************************************************************************************************

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

TASK [touch file] ******************************************************************************************************************************
changed: [192.168.239.20]

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

[root@test-master ansible]# ansible test1 -m shell -a "ls /data"
192.168.239.20 | CHANGED | rc=0 >>
192.168.239.20.txt
html
lala.txt
slave1.log
[root@test-master ansible]# 

使用变量文件

直接我们通过-e选项调用了一个外部的变量文件,这里使用的是在一个独立的playbook文件中定义变量,然后在另一个playbook文件中引用变量文件中的变量,比playbook中定义的变量优先级高

#创建存放变量的yml文件              
[root@test-master ansible]# cat vars.yml 
---
package_name: mariadb-server        #用冒号空格分隔
service_name: mariadb

#创建使用的yml文件
[root@test-master ansible]# cat var6.yml 
---
- hosts: test1
  vars_files:                       #在vars_files列表中指定外部变量yml文件
    - vars.yml                      #列表中每个元素前面有“-”

  tasks:
    - name: "install {{ package_name }}"
      yum:
        name: "{{ package_name }}"

    - name: "start {{ service_name }}"
      service:
        name: "{{ service_name }}"
        state: started
        enabled: yes

#执行yml文件
[root@test-master ansible]# ansible-playbook var6.yml 

PLAY [test1] ***********************************************************************************************************************************

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

TASK [install mariadb-server] ******************************************************************************************************************
changed: [192.168.239.20]

TASK [start mariadb] ***************************************************************************************************************************
changed: [192.168.239.20]

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

#检查服务是否开启
[root@test-master ansible]# ansible test1 -m shell -a "ss -ntl"
192.168.239.20 | CHANGED | rc=0 >>
State      Recv-Q Send-Q Local Address:Port               Peer Address:Port              
LISTEN     0      128          *:22                       *:*                  
LISTEN     0      100    127.0.0.1:25                       *:*                  
LISTEN     0      50           *:3306                     *:*                  
LISTEN     0      128       [::]:8080                  [::]:*                  
LISTEN     0      128       [::]:22                    [::]:*                  
LISTEN     0      100      [::1]:25                    [::]:*    

针对主机和主机组的变量

在主机清单中针对所有项目的主机和主机分组的变量

所有项目的主机变量

在inventory 主机清单文件中为指定的主机定义变量以便于在playbook中使用

所有项目的组(公共)变量

在inventory 主机清单文件中赋予给指定组内所有主机上的在playbook中可用的变量,如果和主机变是同名,优先级低于主机变量

#在主机清单列表的文件中定义变量
[root@test-master ansible]# cat hosts | grep -Ev "^#|^$"
[test1]
192.168.239.20 package_name=nginx service_name=nginx    #定义主机变量

[test1:vars]                                            #定义组变量(组中所有成员使用的变量)
package_name=httpd
service_name=httpd

[all:vars]                                              #定义主机清单所有主机的变量
package_name=mariadb-server
service_name=mariadb

[test2]
192.168.239.30

[test_master]
192.168.239.10

[test:children]
test1
test2

#编辑yml文件
[root@test-master ansible]# cat var7.yml 
---
- hosts: test1

  tasks:
    - name: "install {{ package_name }}"
      yum:
        name: "{{ package_name }}"
        enablerepo: epel

    - name: "start {{ service_name }}"
      service:
        name: "{{ service_name }}"
        state: started
        enabled: yes
#执行yml文件
[root@test-master ansible]# ansible-playbook var7.yml 

PLAY [test1] ***********************************************************************************************************************************

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

TASK [install nginx] ***************************************************************************************************************************
changed: [192.168.239.20]

TASK [start nginx]      #安装的是nginx *****************************************************************************************************************************
changed: [192.168.239.20]

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


使用的是主机变量,没有使用组变量

针对当前项目的主机和主机组的变量

上面的方式是针对所有项目都有效,而官方更建议的方式是使用ansible特定项目的主机变量和组变量

生产建议在项目目录中创建额外的两个变量目录,分别是host_vars和group_vars

  • host_vars下面的文件名和主机清单主机名一致,针对单个主机进行变量定义,格式:host_vars/hostname
  • group_vars下面的文件名和主机清单中组名一致, 针对单个组进行变量定义,格式:gorup_vars/groupname
  • group_vars/all文件内定义的变量对所有组都有效
#创建变量目录
[root@test-master ansible]# mkdir host_vars
[root@test-master ansible]# mkdir grop_vars

#创建主机变量文件
[root@test-master ansible]# touch host_vars/192.168.239.20
[root@test-master ansible]# vim host_vars/192.168.239.20 
pack: nginx
service: nginx

#创建yml文件
[root@test-master ansible]# vim var/var8.yml
[root@test-master ansible]# cat var/var8.yml 
---
- hosts: test1

  tasks:
    - name: stop nginx
      service:
        name: "{{ service }}"
        state: stopped
        enabled: no
    - name: remove nginx
      yum:
        name: "{{ pack }}"
        state: absent


#执行yml文件
[root@test-master ansible]# ansible-playbook var/var8.yml 

PLAY [test1] ***********************************************************************************************************************************

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

TASK [stop nginx] ******************************************************************************************************************************
changed: [192.168.239.20]

TASK [remove nginx] ****************************************************************************************************************************
changed: [192.168.239.20]

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

在生产中建议正对每个项目创建一个项目文件,其中包括主机清单列表(hosts文件),配置文件(ansible.cfg),项目的变量文件(host_vars和group_vars),以及需要执行的playbook的yml文件

register注册变量

在playbook中可以使用register将捕获命令的输出保存在临时变量中,然后使用debug模块进行显示输出

#编写yml文件
[root@test-master ansible]# cat register.yml 
---
- hosts: test1

  tasks:
    - name: get var
      shell: hostname
      register: name    #register注册变量名为name,当shell模块执行完毕后,会将数据放到该变量中
    - name: print var
      debug:
        msg: "{{ name }}"   #将注册变量name的信息通过debug模块显示出来
#执行yml文件
[root@test-master ansible]# ansible-playbook register.yml 

PLAY [test1] ***********************************************************************************************************************************

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

TASK [get var] *********************************************************************************************************************************
changed: [192.168.239.20]

TASK [print var] *******************************************************************************************************************************
ok: [192.168.239.20] => {
    "msg": {
        "changed": true, 
        "cmd": "hostname", 
        "delta": "0:00:00.002893", 
        "end": "2022-11-22 00:13:01.844679", 
        "failed": false, 
        "rc": 0, 
        "start": "2022-11-22 00:13:01.841786", 
        "stderr": "", 
        "stderr_lines": [], 
        "stdout": "slave1", 
        "stdout_lines": [
            "slave1"
        ]
    }
}

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

template模版

模版是一个文本文件,可以作为生成文件的模版,并且模版文件中还可嵌套jinja2语法

jinja2语言

Jinja2 是一个现代的,设计者友好的,仿照 Django 模板的 Python 模板语言。 它速度快,被广泛使用,并且提供了可选的沙箱模板执行环境保证安全:

官方网站

http://jinja.pocoo.org/
https://jinja.palletsprojects.com/en/2.11.x/

特性

  • 沙箱中执行
  • 强大的 HTML 自动转义系统保护系统免受 XSS
  • 模板继承
  • 及时编译最优的 python 代码
  • 可选提前编译模板的时间
  • 易于调试。异常的行数直接指向模板中的对应行
  • 可配置的语法

支持的数据类型

jinja2 语言支持多种数据类型和操作:

  • 字面量,如: 字符串:使用单引号或双引号,数字:整数,浮点数
  • 列表:[item1, item2, …]
  • 元组:(item1, item2, …)
  • 字典:{key1:value1, key2:value2, …}
  • 布尔型:true/false
  • 算术运算:+, -, *, /, //, %, **
  • 比较操作:==, !=, >, >=, <, <=
  • 逻辑运算:and,or,not
  • 流表达式:For,If,When
字面量:

表达式最简单的形式就是字面量。字面量表示诸如字符串和数值的 Python 对象。如”Hello World”双引号或单引号中间的一切都是字符串。无论何时你需要在模板中使用一个字符串(比如函数调用、过滤器或只是包含或继承一个模板的参数),如42,42.23数值可以为整数和浮点数。如果有小数点,则为浮点数,否则为整数。在 Python 里, 42 和 42.0 是不一样的

算术运算:

Jinja 允许用计算值。支持下面的运算符
+:把两个对象加到一起。通常对象是素质,但是如果两者是字符串或列表,你可以用这 种方式来衔接它们。无论如何这不是首选的连接字符串的方式!连接字符串见 ~ 运算符。 {{ 1 + 1 }} 等于 2
-:用第一个数减去第二个数。 {{ 3 – 2 }} 等于 1
/:对两个数做除法。返回值会是一个浮点数。 {{ 1 / 2 }} 等于 0.5
//:对两个数做除法,返回整数商。 {{ 20 // 7 }} 等于 2
%:计算整数除法的余数。 {{ 11 % 7 }} 等于 4
*:用右边的数乘左边的操作数。 {{ 2 * 2 }} 会返回 4 。也可以用于重 复一个字符串多次。 {{ ‘=’ * 80 }}会打印 80 个等号的横条\
**:取左操作数的右操作数次幂。 {{ 2**3 }} 会返回 8

比较操作符

== 比较两个对象是否相等
!= 比较两个对象是否不等

>如果左边大于右边,返回 true

>= 如果左边大于等于右边,返回 true

< 如果左边小于右边,返回 true

<= 如果左边小于等于右边,返回 true`

逻辑运算符

对于 if 语句,在 for 过滤或 if 表达式中,它可以用于联合多个表达式
and 如果左操作数和右操作数同为真,返回 true
or 如果左操作数和右操作数有一个为真,返回 true
not 对一个表达式取反
(expr)表达式组
true / false true 永远是 true ,而 false 始终是 false

template

功能

可以根据和参考模块文件,动态生成相类似的配置文件

目录

  • template文件必须存放于templates目录下,且命名为 .j2 结尾
  • yaml/yml 文件需和templates目录平级

[root@test-master ansible]# tree ./
├── install_http_temp.yml
└── templates
└── index.html.j2

使用

#创建模版文件
[root@test-master ansible]# vim templates/index.html.j2 
hello,"{{ ansible_ens33.ipv4.address }}"        #使用facts收集的变量

#创建yml文件
[root@test-master ansible]# cat install_http_temp.yml 
---
- hosts: test
  gather_facts: yes

  tasks:
    - name: install http
      yum: 
        name: httpd
        state: present

    - name: modify listen port
      lineinfile:
        path: /etc/httpd/conf/httpd.conf
        regexp: "^Listen 80"
        line: "Listen 8080"

    - name: copy conf
      copy:
        src: /etc/httpd/conf/httpd.conf
        dest: /etc/httpd/conf/httpd.conf

    - name: mkdir dir
      file: path=/data/html state=directory

    - name: modify config file1
      lineinfile:
        path: /etc/httpd/conf/httpd.conf
        regexp: '^DocumentRoot "/var/www/html"'
        line: 'DocumentRoot "/data/html"'

    - name: modify config file2
      lineinfile:
        path: /etc/httpd/conf/httpd.conf
        regexp: '^<Directory "/var/www/html">'
        line: '<Directory "/data/html">'

    - name: copy html           #将原先使用的copy模块更改为template模块
#      copy:
#        src: /root/index.html 
#        dest: /data/html/
      template:
        src: index.html.j2      #指定模版文件,因为文件在templas目录下,所以直接写文件名即可
        dest: /data/html/index.html #指定通过模版文件生成的文件的文件路径

    - name: start httpd.service
      service:
        name: httpd
        state: started
        enabled: yes


#运行yml文件
[root@test-master ansible]# ansible-playbook install_http_temp.yml 

PLAY [test] ************************************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************
ok: [192.168.239.20]
ok: [192.168.239.30]

TASK [install http] ****************************************************************************************************************************
changed: [192.168.239.30]
changed: [192.168.239.20]

TASK [modify listen port] **********************************************************************************************************************
changed: [192.168.239.20]
changed: [192.168.239.30]

TASK [copy conf] *******************************************************************************************************************************
changed: [192.168.239.20]
changed: [192.168.239.30]

TASK [mkdir dir] *******************************************************************************************************************************
changed: [192.168.239.20]
changed: [192.168.239.30]

TASK [modify config file1] *********************************************************************************************************************
ok: [192.168.239.20]
ok: [192.168.239.30]

TASK [modify config file2] *********************************************************************************************************************
changed: [192.168.239.20]
changed: [192.168.239.30]

TASK [copy html] *******************************************************************************************************************************
changed: [192.168.239.20]
changed: [192.168.239.30]

TASK [start httpd.service] *********************************************************************************************************************
changed: [192.168.239.20]
changed: [192.168.239.30]

PLAY RECAP *************************************************************************************************************************************
192.168.239.20             : ok=9    changed=7    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
192.168.239.30             : ok=9    changed=7    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   


#访问测试
[root@test-master ansible]# curl 192.168.239.20:8080
hello,"192.168.239.20"          #不同的主机生成了不同的html文件
[root@test-master ansible]# curl 192.168.239.30:8080
hello,"192.168.239.30"

template中使用循环

template中也可以使用流程控制 for 循环和 if 条件判断,实现动态生成文件功能

for循环

格式
{% for i in EXPR %}
    ...
{% endfor %}

示例:
{% for i in range(1,10) %}
    server_name web{{i}};
{% endfor %}
使用
#创建模版文件
[root@test-master ansible]# vim templates/for1.j2
{% for i in range(1,8)%}
    今天是星期{{i}}
{% endfor %}


#创建yml文件
[root@test-master ansible]# vim for1.yml
---
- hosts: test1

  tasks:
    - name: for1
      template:
        src: for1.j2
        dest: /data/for1.txt



#执行yml文件
[root@test-master ansible]# ansible-playbook for1.yml 

PLAY [test1] ***********************************************************************************************************************************

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

TASK [for1] ************************************************************************************************************************************
changed: [192.168.239.20]

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


#查看执行后生成的文件
[root@test-master ansible]# ansible test1 -m shell -a "cat /data/for1.txt"
192.168.239.20 | CHANGED | rc=0 >>
    今天是星期1
    今天是星期2
    今天是星期3
    今天是星期4
    今天是星期5
    今天是星期6
    今天是星期7

if条件判断

在模版文件中还可以使用 if条件判断,决定是否生成相关的配置信息。格式与for循环的格式类似

vrrp_instrance VI_1 {
{% if ansible_fqdn == "ka1" %}
    state MASTER
    priority 100
{% elif ansible_fqdn == "ka2" %}
    state SLAVE
    priority 80
{% endif% }
......
}

使用循环迭代

迭代:当有需要重复性执行的任务时,可以使用迭代机制

迭代 with_items(loop)

对迭代项的引用,固定内置变量名为”item”

要在task中使用with_items给定要迭代的元素列表

注意: ansible2.5版本后,可以用loop代替with_items

作用

当我们需要执行多个类似的任务的时候,比如说创建用户,复制文件,安装软件等,有些模块我们无法一次指定多个任务,比如说创建用户使用user模块无法指定多个用户名进行创建,这就需要拆分成好几个任务,这就很繁琐;但是像安装软件使用yum模块可以同时指定多个软件进行安装,这就很方便。

那么有没有办法帮助这些无法指定多个任务的模块,进行任务简化呢?此时通过循环迭代我们可以通过一个变量来帮我们解决这个问题。

使用

创建多个用户
#直接通过一条命令创建多个用户,直接报错,因为每次只能创建一个用户
[root@test-master ansible]# ansible test1 -m user -a "name=test1,test2,test3 state=present"
192.168.239.20 | FAILED! => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "msg": "useradd: invalid user name 'test1,test2,test3'\n", 
    "name": "test1,test2,test3", 
    "rc": 3
}

#创建单用户,创建成功
[root@test-master ansible]# ansible test1 -m user -a "name=test1 state=present"
192.168.239.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "comment": "", 
    "create_home": true, 
    "group": 10090, 
    "home": "/home/test1", 
    "name": "test1", 
    "shell": "/bin/bash", 
    "state": "present", 
    "system": false, 
    "uid": 10090
}

这对我们想批量创建用户来说就很不方便,如果想同时创建三个用户我们就需要创建三个任务,下面使用迭代完美解决
#使用迭代编写yml文件
[root@test-master ansible]# cat items1.yml 
---
- hosts: test1

  tasks:
    - name: create user
      user:
        name: "{{ item }}"          #调用迭代中的内容
        state: present
      with_items:                   #定义迭代中的内容
        - testuser1
        - testuser2
        - testuser3



#使用yml文件
[root@test-master ansible]# ansible-playbook items1.yml 

PLAY [test1] ***********************************************************************************************************************************

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

TASK [create user] *****************************************************************************************************************************
changed: [192.168.239.20] => (item=testuser1)
changed: [192.168.239.20] => (item=testuser2)
changed: [192.168.239.20] => (item=testuser3)

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


#成功创建多个用户
[root@test-master ansible]# ansible test1 -m shell -a "tail /etc/passwd"
192.168.239.20 | CHANGED | rc=0 >>
zhangsan:x:1002:1002::/home/zhangsan:/bin/bash
lisi:x:1003:1003::/home/lisi:/bin/bash
papa:x:10086:9999::/home/papa:/bin/bash
huahua:x:10010:9999::/home/huahua:/bin/bash
memcached:x:998:995:Memcached daemon:/run/memcached:/sbin/nologin
mysql:x:27:27:MariaDB Server:/var/lib/mysql:/sbin/nologin
nginx:x:997:994:Nginx web server:/var/lib/nginx:/sbin/nologin
testuser1:x:10087:10087::/home/testuser1:/bin/bash
testuser2:x:10088:10088::/home/testuser2:/bin/bash
testuser3:x:10089:10089::/home/testuser3:/bin/bash

复制文件
#编写yml文件
[root@test-master ansible]# cat items2.yml
---
- hosts: test1

  tasks:
    - name: cope file
      copy:
        src: "/etc/ansible/var/{{ item }}"
        dest: "/data/{{ item }}"
      with_items:
        - var1.yml
        - var2.yml
        - var3.yml


#执行yml文件
[root@test-master ansible]# ansible-playbook items2.yml 

PLAY [test1] ***********************************************************************************************************************************

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

TASK [cope file] *******************************************************************************************************************************
changed: [192.168.239.20] => (item=var1.yml)
changed: [192.168.239.20] => (item=var2.yml)
changed: [192.168.239.20] => (item=var3.yml)

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


#查看文件是否创建
[root@test-master ansible]# ansible test1 -m shell -a "ls /data/"
192.168.239.20 | CHANGED | rc=0 >>
var1.yml
var2.yml
var3.yml

迭代嵌套自变量

上面的迭代帮助我们解决了重复编写任务的麻烦,但是这里有一个缺陷,比如说我们的文件复制案例中,我们原文件的文件名就是我们复制后的文件名,在很多时候我们的文件名是需要发生变化的,那这个时候使用之前的迭代就无法满足此目的了

此时我们就可以在迭代中嵌套子变量,关联多个变量在一起使用

使用

#编写yml文件
[root@test-master ansible]# cat items3.yml
---
- hosts: test1

  tasks:
    - name: cope file
      copy:
        src: "/etc/ansible/var/{{ item.src }}"      #指定键名,执行的时候自动获取值
        dest: "/data/{{ item.dest }}"
      with_items:
        - { src: 'var1.yml' , dest: 'test1.yml' }   #指定键值对
        - { src: 'var2.yml' , dest: 'test2.yml' }
        - { src: 'var3.yml' , dest: 'test3.yml' }


#执行yml文件
[root@test-master ansible]# ansible-playbook items3.yml 

PLAY [test1] ***********************************************************************************************************************************

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

TASK [cope file] *******************************************************************************************************************************
changed: [192.168.239.20] => (item={u'dest': u'test1.yml', u'src': u'var1.yml'})
changed: [192.168.239.20] => (item={u'dest': u'test2.yml', u'src': u'var2.yml'})
changed: [192.168.239.20] => (item={u'dest': u'test3.yml', u'src': u'var3.yml'})

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


#查看文件
[root@test-master ansible]# ansible test1 -m shell -a "ls /data/"
192.168.239.20 | CHANGED | rc=0 >>
test1.yml
test2.yml
test3.yml


until循环

当until后面的语句为false时会执行循环,为true才会退出循环,当然我们可以通过retries指定循环次数,默认为3次,还可以通过delay指定每次循环执行后的延时时间,默认为5s。

with_lines逐行处理

#编写yml文件
[root@test-master ansible]# cat  items5.yml 
---
- hosts: test1

  tasks:
    - name: lines
      debug: msg={{ item }}
      with_lines:           #将ps aux命令逐行处理
        - ps aux


#执行yml文件
[root@test-master ansible]# ansible-playbook items5.yml 

PLAY [test1] *******************************************************************************************************************

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

TASK [lines] *******************************************************************************************************************
ok: [192.168.239.20] => (item=USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND) => {
    "msg": "USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND"
}
ok: [192.168.239.20] => (item=root          1  0.2  0.3  46076  6560 ?        Ss   03:40   0:01 /usr/lib/systemd/systemd --switched-root --system --deserialize 22) => {
    "msg": "root          1  0.2  0.3  46076  6560 ?        Ss   03:40   0:01 /usr/lib/systemd/systemd --switched-root --system --deserialize 22"
}
ok: [192.168.239.20] => (item=root          2  0.0  0.0      0     0 ?        S    03:40   0:00 [kthreadd]) => {
    "msg": "root          2  0.0  0.0      0     0 ?        S    03:40   0:00 [kthreadd]"
}

使用when

when语句可以实现条件测试,如果需要根据变量、facts或此前任务的执行结果来作为某task执行与否的前提时要用到条件测试,通过zaitask后添加when子句即可使用条件测试,jinja2的语法格式

使用

条件判断

#编写yml文件
[root@test-master ansible]# cat when1.yml 
---
- hosts: test       #选择主机组test

  tasks:
    - name: reboot slave1
      reboot:
      when: ansible_hostname == "slave1"    #当主机名为slave1时,执行重启操作


#执行yml文件,主机192.168.239.20的主机名为slave1,所以只重启此台设备
[root@test-master ansible]# ansible-playbook when1.yml 

PLAY [test] ********************************************************************************************************************

TASK [Gathering Facts] *********************************************************************************************************
ok: [192.168.239.20]
ok: [192.168.239.30]

TASK [reboot slav1] ************************************************************************************************************
skipping: [192.168.239.30]
changed: [192.168.239.20]

PLAY RECAP *********************************************************************************************************************
192.168.239.20             : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
192.168.239.30             : ok=1    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   

判断服务状态决定是否重启服务

#编写yml文件
[root@test-master ansible]# cat when2.yml 
--- 
- hosts: test1

  tasks:
    - name: check httpd
      shell: systemctl is-active httpd.service 
      ignore_errors: yes        #必须写忽略错误,否则执行上面的命令出现错误信息后面就无法执行
      register: name            #将执行结果赋值给注册变量,变量名为name

    - name: print var           #输出一下变量name的信息,主要是便于获取判断服务状态的信息
      debug:
        msg: "{{ name }}"

    - name: restart httpd
      service:
        name: httpd
        state: restarted
      when: name.rc == 3        #当变量name中的信息有一个键值对的键为rc,对应的值为3表示服务是关闭的,然后执行重启服务的命令


#执行yml文件
[root@test-master ansible]# ansible-playbook when2.yml 

PLAY [test1] *******************************************************************************************************************

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

TASK [check httpd] *************************************************************************************************************
fatal: [192.168.239.20]: FAILED! => {"changed": true, "cmd": "systemctl is-active httpd.service", "delta": "0:00:00.004340", "end": "2022-11-23 04:28:19.327143", "msg": "non-zero return code", "rc": 3, "start": "2022-11-23 04:28:19.322803", "stderr": "", "stderr_lines": [], "stdout": "inactive", "stdout_lines": ["inactive"]}
...ignoring

TASK [print var] ***************************************************************************************************************
ok: [192.168.239.20] => {
    "msg": {
        "changed": true, 
        "cmd": "systemctl is-active httpd.service", 
        "delta": "0:00:00.004340", 
        "end": "2022-11-23 04:28:19.327143", 
        "failed": true, 
        "msg": "non-zero return code", 
        "rc": 3,                            #rc的值为3
        "start": "2022-11-23 04:28:19.322803", 
        "stderr": "", 
        "stderr_lines": [], 
        "stdout": "inactive", 
        "stdout_lines": [
            "inactive"
        ]
    }
}

TASK [restart httpd]        #执行重启服务的任务***********************************************************************************************************
changed: [192.168.239.20]

PLAY RECAP *********************************************************************************************************************
192.168.239.20             : ok=4    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=1   


#任务执行后,再次执行
[root@test-master ansible]# ansible-playbook when2.yml 

PLAY [test1] *******************************************************************************************************************

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

TASK [check httpd]      #此时http服务时启动的,所以shell中的命令有正确的回显,没有出现报错*************************************************************************************************************
changed: [192.168.239.20]

TASK [print var] ***************************************************************************************************************
ok: [192.168.239.20] => {
    "msg": {
        "changed": true, 
        "cmd": "systemctl is-active httpd.service", 
        "delta": "0:00:00.004468", 
        "end": "2022-11-23 04:32:24.965079", 
        "failed": false, 
        "rc": 0,                #此时rc的值为0
        "start": "2022-11-23 04:32:24.960611", 
        "stderr": "", 
        "stderr_lines": [], 
        "stdout": "active", 
        "stdout_lines": [
            "active"
        ]
    }
}

TASK [restart httpd]            #没有重启服务***********************************************************************************************************
skipping: [192.168.239.20]

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

多条件

#编写yml文件,多个条件之间是and的关系
[root@test-master ansible]# vim when3.yml 
---
- hosts: test

  tasks:
    - name: reboot slav1
      reboot:
      when:
        - ansible_hostname == "slave1"          #满足条件主机名为slave1
        - ansible_distribution_version == "7.9" #满足条件操作系统版本为7.9


#执行yml文件
[root@test-master ansible]# ansible-playbook when3.yml 

PLAY [test] ********************************************************************************************************************

TASK [Gathering Facts] *********************************************************************************************************
ok: [192.168.239.20]
ok: [192.168.239.30]

TASK [reboot slav1] ************************************************************************************************************
skipping: [192.168.239.30]
changed: [192.168.239.20]

PLAY RECAP *********************************************************************************************************************
192.168.239.20             : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
192.168.239.30             : ok=1    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0 

failed_when

上面说的都是当条件满足时执行任务,那么下面我们需要在条件满足时不执行任务,可以使用failed_when

#编写yml文件
[root@test-master ansible]# cat when4.yml 
---
- hosts: test2

  tasks:
    - name: reboot slav1
      reboot:
      failed_when: 
        - ansible_hostname == "slave1"      #如果主机名不为slave1就执行任务


#执行yml文件
[root@test-master ansible]# ansible-playbook when4.yml 

PLAY [test2] *******************************************************************************************************************

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

TASK [reboot slav1] ************************************************************************************************************
changed: [192.168.239.30]

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


分组block

有些情况下,我们希望满足指定条件的主机,执行多个任务。此时就需要用到分组了。而不是每个任务都用when

使用

#编写yml文件
[root@test-master ansible]# cat block1.yml 
---
- hosts: test

  tasks:
    - block:
        - name:
          yum:
            name: mariadb-server
        - name:
          service:
            name: mariadb
            state: restarted
            enabled: yes
      when:             #当主机名为slave2时,执行上面分组中的命令
        - ansible_hostname == "slave2"



#执行yml文件
[root@test-master ansible]# ansible-playbook block1.yml

PLAY [test] ********************************************************************************************************************

TASK [Gathering Facts] *********************************************************************************************************
ok: [192.168.239.30]
ok: [192.168.239.20]

TASK [yum]      #跳过了20主机,在30主机上执行*********************************************************************************************************************
skipping: [192.168.239.20]
changed: [192.168.239.30]

TASK [service]      #跳过了20主机,在30主机上执行*****************************************************************************************************************
skipping: [192.168.239.20]
changed: [192.168.239.30]

PLAY RECAP *********************************************************************************************************************
192.168.239.20             : ok=1    changed=0    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0   
192.168.239.30             : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   


#端口查看
[root@test-master ansible]# ansible test -m shell -a "ss -ntl"
192.168.239.30 | CHANGED | rc=0 >>
State      Recv-Q Send-Q Local Address:Port               Peer Address:Port              
LISTEN     0      100    127.0.0.1:25                       *:*                  
LISTEN     0      50           *:3306                     *:*       #开启mysql           
LISTEN     0      128          *:22                       *:*                  
LISTEN     0      100      [::1]:25                    [::]:*                  
LISTEN     0      128       [::]:8080                  [::]:*                  
LISTEN     0      128       [::]:22                    [::]:*                  
192.168.239.20 | CHANGED | rc=0 >>
State      Recv-Q Send-Q Local Address:Port               Peer Address:Port              
LISTEN     0      128          *:22                       *:*                  
LISTEN     0      128       [::]:8080                  [::]:*                  
LISTEN     0      128       [::]:22                    [::]:*    

changed_when

作用

当确定某个task不会对被控制端做修改,但执行结果却显示是黄色的changed状态,可以通过changed_when: false 关闭changed状态

关闭changed状态

#编写yml文件
[root@test-master ansible]# cat changed1.yml 
---
- hosts: test1

  tasks:
    - name: ps
      shell: ps aux | grep sshd
      changed_when: false           #如果不加此项上面的查询语句会显示changed

[root@test-master ansible]# ansible-playbook changed1.yml 

PLAY [test1] *******************************************************************************************************************

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

TASK [ps]       #没有显示changed **********************************************************************************************************************
ok: [192.168.239.20]

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

检查task返回结果

changed_when 检查task返回结果,决定是否继续向下执行

#编写yml文件
[root@test-master ansible]# cat nginx_changed.yml 
---
- hosts: test1

  tasks:
    - name: install nginx
      yum: name=nginx

    - name: config file
      copy: src="/etc/nginx/nginx.conf" dest="/etc/nginx/nginx.conf"
      notify: restart nginx

    - name: check config
      shell: /usr/sbin/nginx -t
      register: check_nginx_config
      changed_when:
        - (check_nginx_config.stdout.find('successful')) #如果执行结果中有successful字符串,则继续执行,如果没有则停止向下执行
        - false                                           #nginx -t 每次成功执行是changed状态,关闭此changed状态

    - name: start service
      service: name=nginx state=started enabled=yes

  handlers:
    - name: restart nginx
      service: name=nginx state=restarted

滚动执行

作用

管理节点过多导致的超时问题解决方法
默认情况下,Ansible将尝试并行管理playbook中所有的机器。对于滚动更新用例,可以使用serial关键字定义Ansible一次应管理多少主机,还可以将serial关键字指定为百分比,表示每次并行执行的主机数占总数的比例

使用

指定一次管理的主机数

#创建yml文件
[root@test-master ansible]# cat test_serial.yml 
---
- hosts: test
  serial: 1       #每次只同时处理1个主机,将所有task执行完成后,再选下2个主机再执行所有task,直至所有主机
  gather_facts: False

  tasks:
    - name: task one
      shell: hostname
    - name: task two
      shell: hostname


#执行yml文件,此时将主机列表中的主机每一个先把任务执行一遍,再换主机进行执行任务
[root@test-master ansible]# ansible-playbook test_serial.yml 

PLAY [test] ********************************************************************************************************************

TASK [task one] ****************************************************************************************************************
changed: [192.168.239.20]

TASK [task two] ****************************************************************************************************************
changed: [192.168.239.20]

PLAY [test] ********************************************************************************************************************

TASK [task one] ****************************************************************************************************************
changed: [192.168.239.30]

TASK [task two] ****************************************************************************************************************
changed: [192.168.239.30]

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


按百分比指定一次管理的主机数

serial: "20%"  #每次只同时处理20%的主机

委派至其它主机执行

作用

利用委托技术,可以在非当前被控主机的其它主机上执行指定操作

使用

使用delegate_to执行指定委派的主机

#编写yml文件
[root@test-master ansible]# cat delegate.yml 
---
- hosts: 192.168.239.20

  tasks:
    - name: ip a
      shell: ip a
      delegate_to: 192.168.239.30       #指定委派的主机


#执行yml文件,不过这里加了一个-v选项便于查看命令的执行结果
[root@test-master ansible]# ansible-playbook delegate.yml -v
Using /etc/ansible/ansible.cfg as config file

PLAY [192.168.239.20] **********************************************************************************************************

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

TASK [ip a]             #指定的主机是20,但是执行ip a 命令的设备是30 ********************************************************************************************************************
changed: [192.168.239.20 -> 192.168.239.30] => {"changed": true, "cmd": "ip a", "delta": "0:00:00.003096", "end": "2022-11-23 20:31:57.755121", "rc": 0, "start": "2022-11-23 20:31:57.752025", "stderr": "", "stderr_lines": [], "stdout": "1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000\n    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00\n    inet 127.0.0.1/8 scope host lo\n       valid_lft forever preferred_lft forever\n    inet6 ::1/128 scope host \n       valid_lft forever preferred_lft forever\n2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000\n    link/ether 00:50:56:30:66:02 brd ff:ff:ff:ff:ff:ff\n    inet 192.168.239.30/24 brd 192.168.239.255 scope global ens33\n       valid_lft forever preferred_lft forever\n    inet6 fe80::250:56ff:fe30:6602/64 scope link \n       valid_lft forever preferred_lft forever", "stdout_lines": ["1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000", "    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00", "    inet 127.0.0.1/8 scope host lo", "       valid_lft forever preferred_lft forever", "    inet6 ::1/128 scope host ", "       valid_lft forever preferred_lft forever", "2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000", "    link/ether 00:50:56:30:66:02 brd ff:ff:ff:ff:ff:ff", "    inet 192.168.239.30/24 brd 192.168.239.255 scope global ens33", "       valid_lft forever preferred_lft forever", "    inet6 fe80::250:56ff:fe30:6602/64 scope link ", "       valid_lft forever preferred_lft forever"]}

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

只执行一次

作用

利用 run_once 指令可以只执行一次,而非在所有被控主机都执行

使用

#编写yml文件
[root@test-master ansible]# cat run_once.yml
---
- hosts: test

  tasks:
    - name: run_once
      shell: hostname
      run_once: true



#执行yml文件
[root@test-master ansible]# ansible-playbook run_once.yml 

PLAY [test] ********************************************************************************************************************

TASK [Gathering Facts] *********************************************************************************************************
ok: [192.168.239.20]
ok: [192.168.239.30]

TASK [run_once]         #只有一台主机执行任务****************************************************************************************************************
changed: [192.168.239.20]

PLAY RECAP *********************************************************************************************************************
192.168.239.20             : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
192.168.239.30             : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

临时修改环境变量

#编写yml文件
[root@test-master ansible]# vim env.yml
---
- hosts: test1

  tasks:
    - name: evn
      shell: echo $PATH
      environment:
        PATH: "/data/html:{{ ansible_env.PATH }}"       #指定临时修改环境变量为此

    - name: echo
      shell: echo $PATH
      register: path

    - name: debug
      debug:
        msg: "{{ path }}"



#执行yml文件
[root@test-master ansible]# ansible-playbook env.yml  -v
Using /etc/ansible/ansible.cfg as config file

PLAY [test1] *******************************************************************************************************************

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

TASK [evn]      #这里临时修改了环境变量*********************************************************************************************************************
changed: [192.168.239.20] => {"changed": true, "cmd": "echo $PATH", "delta": "0:00:00.001997", "end": "2022-11-23 20:55:46.117924", "rc": 0, "start": "2022-11-23 20:55:46.115927", "stderr": "", "stderr_lines": [], "stdout": "/data/html:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin", "stdout_lines": ["/data/html:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin"]}

TASK [echo]         #后面的任务中查看环境变量又恢复为初始的环境变量了********************************************************************************************************************
changed: [192.168.239.20] => {"changed": true, "cmd": "echo $PATH", "delta": "0:00:00.002134", "end": "2022-11-23 20:55:46.411424", "rc": 0, "start": "2022-11-23 20:55:46.409290", "stderr": "", "stderr_lines": [], "stdout": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin", "stdout_lines": ["/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin"]}

TASK [debug] *******************************************************************************************************************
ok: [192.168.239.20] => {
    "msg": {
        "changed": true, 
        "cmd": "echo $PATH", 
        "delta": "0:00:00.002134", 
        "end": "2022-11-23 20:55:46.411424", 
        "failed": false, 
        "rc": 0, 
        "start": "2022-11-23 20:55:46.409290", 
        "stderr": "", 
        "stderr_lines": [], 
        "stdout": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin", 
        "stdout_lines": [
            "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin"
        ]
    }
}

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

wati_for等待条件在执行

#等待端口可用,才能执行任务
#暂停10s等待端口80打开,否则出错
wait_for: port=80 delay=10

#等待直到锁定文件被删除
wait_for: path=/var/lock/file.lock state=absent

yaml文件的相互调用

利用include 或 include_tasks 可以在某个task中调用其它的只有task内容的yaml文件

[root@test-master ansible]#cat a.yml
---
- hosts: websrvs

  tasks:
    - name: run a job
      command: wall run a job
    - name: excute b.yml
      include: b.yml            #调用另一个yaml文件
      #include_tasks: b.yml     #另一种写法

[root@test-master ansible]#cat b.yml
- name: run b job
  command: wall run b job

也可以利用import_playbook将多个包含完整内容的yml文件由一个yml统一调用

[root@test-master ansible]#cat total_tasks.yml
- import_playbook: tasks1.yml
- import_playbook: tasks2.yml

[root@test-master ansible]#cat tasks1.yml
---
- hosts: websrvs

  tasks:
    - name: run task1 job
      command: wall run task1 job

[root@test-master ansible]#cat tasks2.yml
---
- hosts: websrvs

  tasks:
    - name: run task2 job
      command: wall run task2 job

Roles角色

说明

roles能够根据层次型结构自动装载变量文件、tasks以及handlers等。要使用roles只需要在playbook中使用include指令即可。
简单来讲,roles就是通过分别将变量、文件、任务、模板及处理器放置于单独的目录中,并可以便捷地include它们的一种机制。角色一般用于基于主机构建服务的场景中,但也可以是用于构建守护进程等场景中

运维复杂的场景:建议使用 roles,代码复用度高

roles:多个角色的集合目录, 可以将多个的role,分别放至roles目录下的独立子目录中

roles/
    mysql/
    nginx/
    tomcat/
    redis/

默认roles存放的路径

/root/.ansible/roles
/usr/share/ansible/roles
/etc/ansible/roles

官方文档

https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html

roles目录结构

playbook1.yml
playbook2.yml
roles/
    project1/
         tasks/
         files/
         vars/   
         templates/
         handlers/
         default/  
         meta/   
    project2/
         tasks/
         files/
         vars/   
         templates/
         handlers/
         default/  
         meta/
playbook1.yml:用于定义此任务调用哪个role(角色)
roles:此目录中可以包括多个角色,每个角色又有一系列各自的子目录
project1:此目录就包括了一个角色的所有文件
tasks:包含角色要执行的主要任务列表,定义task,role的基本元素,至少应该包含一个名为main.yml的文件;其它的文件需要在此文件中通过include进行包含

files:包含可以通过此角色部署的文件,存放由copy或script模块等调用的文件

vars:角色其他的变量,至少应该包含一个名为main.yml的文件;此目录下的其它的变量文件需要在此文件中通过include进行包含

templates:包含可以通过此角色部署的模板

handlers:包含处理程序,可以由此角色使用,甚至可以在此角色之外的任何位置使用,至少应该包含一个名为main.yml的文件;此目录下的其它的文件需要在此文件中通过include进行包含

defaults:角色默认的变量,使用此目录中的main.yml文件,比vars的优先级低

meta:定义当前角色的特殊设定及其依赖关系,至少应该包含一个名为main.yml的文件,其它文件需在此文件中通过include进行包含

创建role

创建role的步骤

  • 创建role的目录结构.在以roles命名的目录下分别创建以各角色名称命名的目录,如mysql等,在每个角色命名的目录中分别创建相关的目录和文件,比如tasks、files等目录;用不到的目录可以创建为空目录,也可以不创建
  • 编写和准备role的功能文件
  • 编写playbook文件调用需要的角色应用于指定的主机

创建role

#创建目录
[root@test-master ansible]# mkdir roles/httpd/{tasks,files,templates,handlers} -pv


#查看目录结构
[root@test-master ansible]# tree roles/
roles/
└── httpd
    ├── files
    ├── handlers
    ├── tasks
    └── templates

#创建文件
[root@test-master ansible]# touch roles/httpd/tasks/{main,install,copy_conf,mkdir,temp_html,service}.yml
[root@test-master ansible]# touch roles/httpd/handlers/{main,restart}.yml
[root@test-master ansible]# cp /etc/httpd/conf/httpd.conf roles/httpd/files/
[root@test-master ansible]# cp templates/index.html.j2 roles/httpd/templates/


[root@test-master ansible]# tree roles/
roles/
└── httpd
    ├── files
    │   └── httpd.conf
    ├── handlers
    │   ├── main.yml
    │   └── restart.yml
    ├── tasks
    │   ├── copy_conf.yml
    │   ├── temp_html.yml
    │   ├── install.yml
    │   ├── main.yml
    │   ├── mkdir.yml
    │   └── service.yml
    └── templates
        └── index.html.j2

5 directories, 10 files

编辑yml文件

框架创建好了,下面就是完善内容了

tasks目录下的所有文件

必须要有main.yml文件,别的文件可以没有,直接将任务写在main.yml中,如果将任务拆分到各个yml文件中直接通过include调用yml文件即可

[root@test-master ansible]# cat roles/httpd/tasks/main.yml
- include: install.yml          #include调用的yml文件的顺序一定要注意,因为是按顺序执行的
- include: copy_conf.yml
- include: mkdir.yml
- include: temp_html.yml
- include: service.yml

[root@test-master ansible]# cat roles/httpd/tasks/copy_conf.yml
- name: copy conf
  copy:
    src: httpd.conf         #我们调用的文件这些不应当非得是httpd角色目录下中的文件,可以使用外部文件,不过此时需要使用绝对路径或者是从roles开始的相对路径,如:src=roles/httpd/files/index.html
    dest: /etc/httpd/conf/httpd.conf
  notify: restart httpd

[root@test-master ansible]# cat roles/httpd/tasks/install.yml
- name: install http
  yum: 
    name: httpd
    state: present

[root@test-master ansible]# cat roles/httpd/tasks/mkdir.yml
- name: mkdir dir
  file: path=/data/html state=directory

[root@test-master ansible]# cat roles/httpd/tasks/service.yml
- name: start httpd.service
  service:
    name: httpd
    state: started
    enabled: yes

[root@test-master ansible]# cat roles/httpd/tasks/temp_html.yml
- name: temp html
  template:
    src: index.html.j2
    dest: /data/html/index.html

handlers目录下所有的文件

同样的必须要有main.yml文件

[root@test-master ansible]# cat roles/httpd/handlers/main.yml
- include: restart.yml

[root@test-master ansible]# cat roles/httpd/handlers/restart.yml
- name: restart httpd
  service:
    name: httpd
    state: restarted

主剧本文件

这个是调用角色的yml文件,此文件与roles目录平级

[root@test-master ansible]# cat http_role.yml 
---
- hosts: test1
  roles:
    - role: httpd           #调用角色

执行yml文件

[root@test-master ansible]# ansible-playbook http_role.yml

PLAY [test1] *******************************************************************************************************************

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

TASK [httpd : install http] ****************************************************************************************************
changed: [192.168.239.20]

TASK [httpd : copy conf] *******************************************************************************************************
changed: [192.168.239.20]

TASK [httpd : mkdir dir] *******************************************************************************************************
changed: [192.168.239.20]

TASK [httpd : temp html] *******************************************************************************************************
changed: [192.168.239.20]

TASK [start httpd.service] *****************************************************************************************************
changed: [192.168.239.20]

RUNNING HANDLER [restart httpd] ************************************************************************************************
changed: [192.168.239.20]

PLAY RECAP *********************************************************************************************************************
192.168.239.20             : ok=7    changed=6    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@test-master ansible]# curl 192.168.239.20:8080
hello,"192.168.239.20"

完整的角色目录

http_role.yml 
roles/
└── httpd
    ├── files
    │   └── httpd.conf
    ├── handlers
    │   ├── main.yml
    │   └── restart.yml
    ├── tasks
    │   ├── copy_conf.yml
    │   ├── temp_html.yml
    │   ├── install.yml
    │   ├── main.yml
    │   ├── mkdir.yml
    │   └── service.yml
    └── templates
        └── index.html.j2

playbook调用角色

方法一

此方法就是我们上面的角色中使用的方法

---
- hosts: test
  remote_user: root
  roles:
    - mysql
    - memcached
    - nginx 

方法二

键role用于指定角色名称,后续的k/v用于传递变量给角色

---
- hosts: all
  remote_user: root
  roles:
  - role: mysql
  username: mysql
  - { role: nginx, username: nginx }

方法三

基于条件测试实现角色调用

---
- hosts: all
  remote_user: root
  roles:
    - { role: nginx, username: nginx, when: ansible_distribution_major_version =='7' }

roles中tags使用

tags标签和role是平级的字典键值对,这里的标签的值可以是一个列表;

有了标签意味着我们在执行playbook脚本时可以挑一些标签来执行

[root@test-master ansible]# cat tag.yml 
---
- hosts: test
  remote_user: root
  roles:
    - { role: httpd, tags: ['web','httpd'] }
    - { role: nginx, tags: ['web','nginx'] }

ansible tower

我们目前使用的都是命令行的ansible,但是在大的企业中很多时候还是有图看着爽,所以ansible也人性化的为这些有钱的企业提供了一个图形化工具,当然这是要收费的,很可惜我没用过,虽然控制的节点比较少是可以使用免费版本,不过设备少也没必要使用图形化工具。

留下评论