一、Ansible 概述
Ansible 是一个简单的自动化引擎,可以完成配置管理、应用部署、服务编排等需求。Ansible 是一款使用 Python 语言开发实现的开源软件,依赖 Jinja2、paramiko 和 PyYAML。
1、Ansible 的优点
① 安装部署简单:Ansible 只需在主控端部署环境,被控端无须做任何操作。
② 基于 SSH 进行配置管理,充分利用现成的机制。
③ 不需要守护进程。
④ 日志集中存储:所有操作日志都存储在 Ansible 发起服务器。
⑤ 简单易用:运行一个部署命令就可以完成应用的部署。此外,Ansible 使用 YAML 语法管理配置,YAML 本身是一种可读性非常强的标记语言。
⑥ 功能强大:通过模块来实现各种功能。
⑦ 设计优秀,便于分享:使用 role 组织 Playbook 并提供了分享 role 的平台(galaxy.ansible.com)。
⑧ 对云计算和大数据平台都有很好的支持。
2、Ansible 的安装
pip install ansible
# 安装完成后,控制端服务器会增加以下可执行程序:
ansible
ansible-doc
ansible-playbook
ansible-vault
ansible-console
ansible-galaxy
ansible-pull3、Ansible 的运行环境
3.1、编辑配置文件
配置文件优先级:
环境变量
当前目录下的 .ansible.cfg 文件
当前目录下的 ansible.cfg 文件
/etc/ansible/ansible.cfg
# 在 Ansible 中,有多种方式使用 ansible.cfg 文件。查找顺序如下:
ANSIBLE_CONFIG 环境变量指定的配置文件
当前目录下的 ansible.cfg 文件
当前用户家目录下的 .ansible.cfg 文件
Ansible 默认的 /etc/ansible/ansible.cfg 文件
# ansible.cfg 中常用的配置项:
## 默认配置
inventory: 指定 Inventory 文件的路径
remote_user: ssh 连接时使用的用户名
remote_port: ssh 连接时使用的端口号
private_key_file: ssh 连接时使用的私钥文件
roles_path: 查找 role 的路径,可以指定多个查找路径,路径之间用冒号分隔
log_path: Ansible 的日志文件路径
host_key_checking: 类似于 ssh 命令中的 StrictHostKeyChecking,当该选项设置为 False 时,不检查远程服务器是否存在于 know_hosts 文件中
forks: 并行进程的数量
gathering: 控制收集 Facts 变量的策略
## ssh 连接配置
ssh_args: 控制 ssh 连接
pipelining: 多个 task 共享 ssh 连接
control_path: 保存 ControlPath socket 的路径
## 权限提升配置
become: 是否进行权限提升
become_method: 权限提升的方式,默认为 sudo
become_user: 提升为哪个用户的权限,默认为 root
become_ask_pass: 默认为 False,表示权限提升时不需要密码3.1.1、示例文件
[defaults]
inventory = ~/.ansible/hosts
remote_tmp = ~/.ansible/tmp
local_tmp = ~/.ansible/tmp
poll_interval = 15
module_name = command
nocolor = false
host_key_checking = false
[paramiko_connection]
timeout = 10
[privilege_escalation]
become_allow_same_user = true
become_method = sudo
become_ask_pass = False3.1.2、解释
[defaults]
inventory = ~/.ansible/hosts #指定主机清单配置文件inventory
remote_tmp = ~/.ansible/tmp #指定远程主机的执行路径的临时目录,用于存放远程主机的模块文件
local_tmp = ~/.ansible/tmp #ansible管理节点的临时目录
poll_interval = 15 #轮询时间
module_name = command #定义ansible默认执行模块
nocolor = false #是否带颜色输出,true为不带颜色,默认为false
ask_pass = false #是否需要输入登录密码
private_key_file = /path/to/file.pem #指定ssh私钥文件
host_key_checking = false #定义是否需要检查主机的首次连接的指纹验证
remote_user = root #指定远程登录用户
forks = 5 #用于设置ansible的最大fork数。
[paramiko_connection]
remote_port = 22 #远程ssh端口
timeout = 10 #设置超时时间
[privilege_escalation]
become_allow_same_user = true
become_method = sudo
become_user = root
become_ask_pass = False3.2、编辑hosts文件
使用 Ansible 操作远程服务器时,首先需要确定的是操作哪些服务器,然后再确定执行哪些操作。Ansible 会默认读取 /etc/ansible/hosts 文件中配置的远程服务器列表。例如:
cat /etc/ansible/hosts
[test1]
10.10.1.1
10.10.1.2
[test2]
10.10.1.3
# Ansible 默认使用当前用户和默认的 22 端口号与远程服务器建立 SSH 连接,如果需要其他用户或端口号,可以自行配置:
cat /etc/ansible/hosts
[test1]
10.10.1.1 ansible_user=abc ansible_port=1234
10.10.1.2 ansible_user=abc ansible_port=1234
[test2]
10.10.1.3 ansible_user=abc ansible_port=1234
# Ansible 默认使用 /etc/ansible/ansible.cfg 文件,可以在这个配置文件中设定一些默认值:
cat /etc/ansible/ansible.cfg
[defaults]
remote_port = 1234
remote_user = abc二、Ansible 的 ad-hoc 模式
# 格式:
ansible {服务器组名} -m {模块名} -a "参数"
# 例子:
ansible test -m ping
ansible test -m command -a "hostname"
ansible test -m copy -a "src=/tmp/data.txt dest=/tmp/data.txt"
-become 参数的作用等同于 sudo
# 其他参数:
-k,--ask-pass 登录密码,提示输入SSH密码而不是假设基于密钥的验证
--ask-su-pass su切换密码
-K,--ask-sudo-pass 提示密码使用sudo,sudo表示提权操作
--ask-vault-pass 假设我们设定了加密的密码,则用该选项进行访问
-B SECONDS #后台运行超时时间
-C #模拟运行环境并进行预运行,可以进行查错测试
-c CONNECTION #连接类型使用
-f FORKS #并行任务数,默认为5
--list-hosts #查看有哪些主机组
-o #压缩输出,尝试将所有结果在一行输出,一般针对收集工具使用
-S #用 su 命令
-s #用 sudo 命令
-R SU_USER #指定 su 的用户,默认为 root 用户
-U SUDO_USER #指定 sudo 到哪个用户,默认为 root 用户
-T TIMEOUT #指定 ssh 默认超时时间,默认为10s,也可在配置文件中修改
-u REMOTE_USER #远程用户,默认为 root 用户
-v #查看详细信息,同时支持-vvv,-vvvv可查看更详细信息三、Ansible 的 Inventory 管理
1、文件位置
# Inventory 指的是可管理的服务器的集合。
# 在 Ansible 中,有三种指定 hosts 文件的方式:
① 默认读取 /etc/ansible/hosts 文件
② 通过命令行参数的 -i 指定 hosts 文件
③ 通过 ansible.cfg 文件中的 inventory 选项指定。
# 这里的 --list-hosts 的功能是列出匹配的服务器列表。
ansible test -i hosts --list-hosts
# 也可以在 ansible.cfg 中指定:
cat /etc/ansible/ansible.cfg
[defaults]
remote_port = 1234
remote_user = abc
inventory = /home/abc/hosts2、灵活定义和匹配 hosts 文件内容
# 可以将服务器分组:
test.example.com
[webservers]
foo.example.com
bar.example.com
[dbservers]
one.example.com
two.example.com
# 分组后,可以使用组的名称匹配改组下的所有服务器:
root@xkj-Thinkbook16:~# ansible webservers --list-hosts
hosts (2):
foo.example.com
bar.example.com
# all 或 * 代表所有服务器:
root@xkj-Thinkbook16:~# ansible '*' --list-hosts
root@xkj-Thinkbook16:~# ansible all --list-hosts
hosts (5):
test.example.com
foo.example.com
bar.example.com
one.example.com
two.example.com
# 也可以定义一个包含服务器组名称的组,其中的“:children”用来声明这是一个特殊的组:
[common:children]
webservers
dbservers
# 定义格式也可以更简单:
[webservers]
web[1:3].example.com
[dbservers]
db[a:b].example.com
root@xkj-Thinkbook16:~# ansible all --list-hosts
hosts (5):
web1.example.com
web2.example.com
web3.example.com
dba.example.com
dbb.example.com
# 服务器匹配规则:3、Inventory 行为参数
行为参数指的是在指定服务器地址时,可以同时指定的一些参数,这些参数可以通过 ansible.cfg 更改默认值:
4、定义普通变量
# 在 hosts 文件中也可以定义普通变量。例如:
[test]
10.10.1.1 mysql_port=3306
10.10.1.2 mysql_port=3307
# 可以查看这个普通变量的值:
ansible all -a 'echo {{mysql_port}}'
10.10.1.1 | SUCCESS | rc=0 >>
3306
10.10.1.2 | SUCCESS | rc=0 >>
3307
# 如果一个组里边同一个变量的取值相同,也可以定义组变量:
[test]
10.10.1.1
10.10.1.2
[test:vars]
mysql_port=3306
# Ansible 提供了更好的方法来管理服务器与群组的变量,即为每个服务器和群组创建独立的变量文件。
将组的变量存放在一个名为 group_vars 目录下,目录下的文件名与组的名称相同,文件的扩展名可以是 .yml 或 .yaml,也可以没有扩展名。
服务器的变量存放在一个名为 host_vars 的目录下,该目录下的文件名为服务器的名称。
# Ansible 将依次在 playbook 所在的目录、hosts 文件所在的目录和 /etc/ansible 目录寻找 group_vars 目录和 host_vars 目录。这里假设位于 /etc/ansible 目录下:
$ cd /etc/ansible
$ tree
.
|---- ansible.cfg
└---- group_vars
| └---- test.yaml
|---- hosts
└---- host_vars
└---- 127.0.0.1.yaml
$ cat group_vars/test.yaml
mysql_port: 3306四、Ansible 模块
1、介绍
# Ansible 对远程服务器的操作实际上是通过模块完成的,其工作原理如下:
1)将模块拷贝到远程服务器
2)执行模块定义的操作,完成对服务器的修改
3)在远程服务器中删除模块
# Ansible 中的模块是幂等的,多次执行的操作,只有第一次生效。
ansible-doc -l # 查看模块列表
ansible-doc {模块名} # 获取模块的帮助信息
ansible-doc -l {模块名} # 获取模块的帮助信息2、常用模块
2.1、ping 模块
# ping 模块用来测试现有的 ssh 参数是否能够顺利连通远程服务器。
ansible test -m ping2.2、远程命令模块
# command 是默认模块,可以不指定模块名称直接运行 linux 命令。command 模块的重要选项有:
chdir:在执行指令之前,先切换到指定的目录
executable:切换 shell 来执行命令
command 模块在执行 linux 命令时不能使用管道。
# 示例
ansible test -a 'hostname'
ansible test -m command -a 'hostname'
# 如果执行的命令需要使用管道,可以使用 raw 模块,
raw 模块相当于使用 ssh 直接执行 linux 命令,不会进入到 Ansible 的模块子系统中。
也可以使用 shell 模块,shell 模块还可以执行远程服务器上的 shell 脚本文件,脚本文件需要使用绝对路径。
ansible test -m raw -a 'cat /etc/passwd | wc -l'
ansible test -m shell -a 'cat /etc/passwd | wc -l'
ansible test -m shell -a '/home/abc/test.sh'
# script 模块可以在远程服务器上执行主控节点中的脚本文件。
ansible test -m script -a 'test.sh'2.3、file 模块
file 模块主要用于对远程服务器上的文件(包括链接和目录)进行操作,包括修改文件的权限、修改文件的所有者、创建文件、删除文件等。
# 重要选项有:
path: 指定文件/目录的路径
recurse: 递归设置文件属性,只对目录有效
group: 定义文件/目录的组
mode: 定义文件/目录的权限
owner: 定义文件/目录的所有者
src: 被链接的源文件路径,只应用于 state 为 link 的情况
dest: 被链接到的路径,只应用于 state 为 link 的情况
force:
在两种情况下会强制创建软链接,一种是源文件不存在但之后会建立的情况;另一种是目标软链接已存在,需要先取消之前的软链接,然后创建新的软链接,默认取值为 no
state: 有多种取值。
directory 如果目录不存在,创建目录
file 即使文件不存在也不会被创建,存在则返回文件的信息
link 创建软链接
hard 创建硬链接
touch 如果文件不存在,创建一个新的文件,如果文件或目录已存在,更新其最后访问时间和修改时间
absent 删除目录、文件或链接
# 创建一个目录
ansible test -m file -a 'path=/tmp/aaa state=directory mode=0755'
# 修改文件的权限
ansible test -m file -a 'path=/tmp/aaa state=touch mode="u=rw,g=r,o=r"'
# 创建软链接
ansible test -m file -a 'src=/tmp/aaa dest=/tmp/bbb owner=abc group=abc state=link'
# 修改文件的所有者
ansible test -m file -a 'path=/tmp/aaa owner=root group=root mode=0644' -become
2.4、copy 模块
# 将主控节点的文件或目录拷贝到远程服务器上,同时也可以设置文件在远程服务器的权限和所有者。
src:
要复制到远程服务器的文件地址,可以是绝对路径或相对路径,如果路径是目录,将递归复制。如果路径以 “/” 结尾,只复制目录里的内容,否则复制包含目录在内的整个内容。
dest:
文件复制的目的地,必须是绝对路径,如果源文件是一个目录,dest 指向也必须是一个目录。
force:
默认值为 yes,表示目标服务器包含该文件,但内容不同时会强制覆盖。如果该选项设置为 no,则只有当目标服务器的目标位置不存在于该文件时,才会进行复制。
backup:
默认值为 no,如果配置为 yes,在覆盖之前将原文件进行备份。
directory_mode:
递归设定目录权限,默认为系统默认权限。
remote_src:
默认为false,设置src是否在远程主机上,
所有 file 模块的选项都可以在这里使用
# 拷贝文件到远程服务器
ansible test -m copy -a "src=test.sh dest=/tmp/test.sh"
# 拷贝文件到远程服务器,如果文件已经存在,备份该文件
ansible test -m copy -a "src=test.sh dest=/tmp/test.sh backup=yes force=yes"
# 拷贝文件到远程服务器,并且修改文件的所有者和权限
ansible test -m copy -a "src=test.sh dest=/tmp/test.sh owner=root group=root mode=644 force=yes" -become2.5、user 和 group 模块
# user 模块请求的是 useradd、userdel、usermod 三个指令,group 模块请求的是 groupadd、groupdel、groupmod 三个指令。
name: 需要操作的用户名(或组名)
comment: 用户的详细描述
createhome: 创建用户时,是否创建家目录,默认为 yes
home: 指定用户的家目录,需要与 createhome 选项配合使用
groups: 指定用户的属组
uid: 设置用户的 uid
gid: 设置组的 gid
password: 设置用户的密码
update_password 假如设置的密码不同于原密码,会更新密码
state: 是创建用户(组)还是删除用户(组),取值包括 present 和 absent
expires: 用户的过期时间
shell 指定用户的 shell 环境
generate_ssh_key 设置为yes将会为用户生成密钥,这不会覆盖原来的密钥
ssh_key_type 指定用户的密钥类型,默认rsa,具体类型取决于被管理节点
remove 当与 state=absent 一起使用时,删除一个用户及关联的目录,比如家目录, 邮箱目录,yes|no
# 创建一个用户
ansible test -m user -a 'name=abc comment="test" uid=1234 group=root' -become
# 删除一个用户
ansible test -m user -a 'name=abc state=absent' -become
# 创建一个用户,并产生一对秘钥
ansible test -m user -a 'name=abc comment="test" generate_ssh_key=yes ssh_key_bits=2048' -become
# 创建组
ansible test -m group -a "name=ansible state=present gid=1234" -become
# 删除组
ansible test -m group -a "name=ansible state=absent" -become2.6、apt 模块
# 在 Debian/Ubuntu 系统中安装、删除软件
name: 软件包的名称
state: 软件包的状态,可以取值为 latest、absent、present、build-dep,默认为 present
autoremove: 默认值为 no,如果值为 yes,移除不需要的软件包
force: 强制安装或删除软件包
update_cache: 相当于 apt-get update
deb: deb文件的路径
# 安装软件包
ansible test -m apt -a "name=git state=present" -become
# 卸载软件包
ansible test -m apt -a "name=git state=absent" -become
# 更新源
ansible test -m apt -a "update_cache=yes" -become2.7、yum 模块
# 在 Redhat 或 centOS 系统中安装、删除软件
name: 软件包名
state: 软件包的状态,可以取值为 present、installed、latest、absent 和 removed
disable_gpg_check: 禁用 rpm 包的公钥 gpg 验证
enable_repo: 指定安装软件包时临时启用的yum源
disable_repo: 指定安装软件包时临时禁用的yum源
# 安装软件包
ansible test -m yum -a "name=git state=present" -become
# 卸载软件包
ansible test -m yum -a "name=git state=absent" -become2.8、get_url 模块
# 从互联网下载数据到本地,可以控制下载后的数据所有者、权限以及检查下载数据的 checksum 等
url: 文件的下载地址
dest: 文件保存的绝对路径
mode: 文件的权限
checksum: 文件的校验码
headers: 传递给下载服务器的 HTTP Headers
backup: 如果本地已经存在同名文件,则备份文件
timeout: 下载的超时时间
所有 file 模块的选项都可以在这里使用2.9、unarchive 模块
# 用于解压文件,将控制节点的压缩包拷贝到远程服务器
remote_src:yes
表示解压的文件存在远程服务器中,no 表示解压的文件存在控制节点所在的服务器中,默认值为 no,表示解压之前先把文件拷贝到远程主机中。
src: 制定压缩文件的路径,所在服务器取决于 remote_src 的取值。
dest: 远程服务器上的绝对路径,表示压缩文件解压的路径。
list_files: 默认 no。取 yes 时,在解压的之后的返回值中列出压缩包里的文件。
exclude: 解压文件时排除的文件或目录
keep_newer: 默认 False。如果为 True,当目标地址中存在同名的文件且文件比压缩包中的文件更新时,不进行覆盖。
owner: 解压后的所有者
group: 解压后的所属的组
mode: 解压后的权限
# 解压本地文件
ansible test -m unarchive -a "src=data.tar.gz dest=/tmp/data list_files=yes"
# 解压远程的文件
ansible test -m unarchive -a "src=/tmp/data.tar.bz2 dest=/tmp remote_src=yes"2.10、git 模块
# 在远程服务器执行 git 相关的操作
repo: 远程 git 库的地址,可以是一个 git 协议、ssh 协议或 http 协议 的 git 库地址
dest: git 库 clone 到本地服务器后保存的绝对路径
version: git 库的版本,可以取 HEAD、分支的名称、tag 的名称,也可以是一个 commit 的 hash 值
force: 默认 no。如果 yes,如果本地 git 库有修改,会抛弃本地的修改
accept_hostkey:
如果 git 库的服务器不在 know_hosts 中,则添加到 know_hosts 中,key_file 指定克隆远程 git 库地址时使用的私钥
# 示例
ansible test -m git -a "repo=https://github.com/kennethreitz/requests.git dest=/tmp/requests version=HEAD"2.11、stat 模块
# 用于获取远程服务器上的文件信息,可以获取 atime、ctime、mtime、checksum、size、uid、gid 等信息
ansible test -m stat -a "path=/etc/passwd"2.12、cron 模块
# 管理计划任务
backup:默认 no,表示修改之前先备份
state:present 或 absent,表示创建还是删除该计划任务
name:计划任务的描述
job:添加或删除任务,取决于 state 的取值
user:指定 crontab 所属用户
cron_file:如果指定该选项,用该文件替换远程服务器上的 cron.d 目录下的用户任务计划
month weekday day minute hour:指定 crontab 的时间参数
# 示例
ansible test -m cron -a 'backup=yes name="test cron" minute=*/2 hour=* job="ls /tmp > /dev/null"'2.13、service 模块
# 相当于 service 命令,用来启动、停止、重启服务
name:服务的名称
state:可以取值为 started、stopped、restarted 和 reloaded。started 和 stopped 是幂等的。如果服务已经启动了,执行 started 不会执行任何操作。
sleep:重启的过程中,先停止服务然后 sleep 几秒再启动
pattern:定义一个模式,Ansible 首先通过 status 命令查看服务的状态,判断服务是否在运行。如果通过 status 查看服务状态时没有响应,Ansible 会尝试匹配 ps 命令的输出,当匹配到相应的模式时,认为服务已经启动,否则认为没有启动。
enabled:设置服务是否开机启动
# 停止 Apache
ansible test -m service -a "name=apache2 state=stopped"
# 重启 Apache
ansible test -m service -a "name=apache2 state=restarted"2.14、systemd 模块
# 相当于 systemctl 命令,用来启动、停止、重启服务
name:服务名称
pattern:定义一个模式,Ansible 首先通过 status 命令查看服务的状态,判断服务是否在运行。如果通过 status 查看服务状态时没有响应,Ansible 会尝试匹配 ps 命令的输出,当匹配到相应的模式时,认为服务已经启动,否则认为没有启动。
sleep:重启的过程中,先停止服务然后 sleep 几秒再启动
state:可取值为 started、stopped、restarted、reloaded
enabled:设置服务是否开机启动
daemon_reload:重新载入 systemd2.15、sysctl 模块
# 用于控制内核参数
name:需要设置的参数
value:需要设置的值
sysctl_file:sysctl.conf 文件的绝对路径,默认路径是 /etc/sysctl.conf
reload:默认yes,表示设置完成后是否需要执行 sysctl -p
# 设置 overcommit_memory 参数的值为 1
ansible test -m sysctl -a "name=vm.overcommit_memory value=1" -become2.16、mount 模块
# 在远程服务器上挂载磁盘,如果挂载点指定的路径不存在,将创建该路径
name:挂载点的路径
state:可取值为 present、absent、mounted、unmounted,其中,mounted 和 unmounted 用来处理磁盘的挂载和卸载,且正确配置 fstab 文件,present 和 absent 只会设置 fstab 文件,不会操作磁盘
fstype:指定文件系统类型,当 state 取值为 present 或 mounted 时,此为必选项
src:挂载的设备
# 示例
ansible test -m mount -a "name=/mnt/data src=/dev/vda fstype=ext4 state=mounted"2.17、syncronize 模块
# 对 rsync 的封装
src:需要同步的文件或目录
dest:远程服务器保存数据的路径
archive:默认值为 yes,相当于同时开启 recursive、links、perms、times、owner、group、-D 等选项
compress:在同步过程中是否启用压缩
delete:默认为 no,取值为 yes 时,表示删除 dest 中存在而 src 中不存在的文件
# 示例
ansible test -m syncronize -a "src=test dest=/tmp"2.18、gather_facts
# 用于收集有关远程主机的信息,例如主机名、IP地址、操作系统、网络接口、硬件信息等。它可以帮助您获取主机的事实信息,并将这些信息存储在Ansible的变量中,以供后续任务使用。
# 默认情况下调用 ansible.builtin.setup 模块
# 命令行
ansible all -m gather_facts
# playbook
- name: Gather Facts
hosts: all
gather_facts: yes
tasks:
- name: Display Facts
debug:
var: ansible_factsansible all -m gather_facts
192.168.2.52 | SUCCESS => {
"ansible_facts": {
"ansible_all_ipv4_addresses": [
"172.17.0.1",
"192.168.2.52"
],
"ansible_all_ipv6_addresses": [
"fe80::a157:95cb:e63c:10dc"
],
"ansible_apparmor": {
"status": "disabled"
},
"ansible_architecture": "x86_64",
"ansible_bios_date": "11/12/2020",
"ansible_bios_vendor": "Phoenix Technologies LTD",
"ansible_bios_version": "6.00",
"ansible_board_asset_tag": "NA",
"ansible_board_name": "440BX Desktop Reference Platform",
"ansible_board_serial": "None",
"ansible_board_vendor": "Intel Corporation",
"ansible_board_version": "None",
"ansible_chassis_asset_tag": "No Asset Tag",
"ansible_chassis_serial": "None",
"ansible_chassis_vendor": "No Enclosure",
"ansible_chassis_version": "N/A",
"ansible_cmdline": {
"BOOT_IMAGE": "/vmlinuz-3.10.0-1160.el7.x86_64",
"LANG": "en_US.UTF-8",
"quiet": true,
"rd.lvm.lv": "centos/swap",
"rhgb": true,
"ro": true,
"root": "/dev/mapper/centos-root"
},
"ansible_date_time": {
"date": "2023-12-29",
"day": "29",
"epoch": "1703818015",
"epoch_int": "1703818015",
"hour": "10",
"iso8601": "2023-12-29T02:46:55Z",
"iso8601_basic": "20231229T104655687445",
"iso8601_basic_short": "20231229T104655",
"iso8601_micro": "2023-12-29T02:46:55.687445Z",
"minute": "46",
"month": "12",
"second": "55",
"time": "10:46:55",
"tz": "CST",
"tz_dst": "CST",
"tz_offset": "+0800",
"weekday": "星期五",
"weekday_number": "5",
"weeknumber": "52",
"year": "2023"
},
"ansible_default_ipv4": {
"address": "192.168.2.52",
"alias": "ens33",
"broadcast": "192.168.2.255",
"gateway": "192.168.2.1",
"interface": "ens33",
"macaddress": "00:0c:29:1d:c2:4f",
"mtu": 1500,
"netmask": "255.255.255.0",
"network": "192.168.2.0",
"prefix": "24",
"type": "ether"
},
"ansible_default_ipv6": {},
"ansible_device_links": {
"ids": {
"dm-0": [
"dm-name-centos-root",
"dm-uuid-LVM-SJen3omEsNW5XUfryBMiUG9dk1VsTwbHDg0UdvWIxxiMWICoXjkhZvYcDfYWPwBw"
],
"dm-1": [
"dm-name-centos-swap",
"dm-uuid-LVM-SJen3omEsNW5XUfryBMiUG9dk1VsTwbHHv8kIG9QXQiYL8HyznjqKOGod8NGPNjJ"
],
"dm-2": [
"dm-name-centos-home",
"dm-uuid-LVM-SJen3omEsNW5XUfryBMiUG9dk1VsTwbHXT7h4M80G2UxiojdOyNnW3W9QZiExlzr"
],
"sda2": [
"lvm-pv-uuid-nmGG2H-Jie4-MLvx-EYS8-NGGJ-VDm8-303P14"
],
"sr0": [
"ata-VMware_Virtual_IDE_CDROM_Drive_00000000000000000001"
]
},
"labels": {
"sr0": [
"CentOS\\x207\\x20x86_64"
]
},
"masters": {
"sda2": [
"dm-0",
"dm-1",
"dm-2"
]
},
"uuids": {
"dm-0": [
"c49578c1-a5ed-4f02-b6aa-fb84c9adba87"
],
"dm-1": [
"d5067ec0-4f37-4d25-a9ca-2241ebf26514"
],
"dm-2": [
"47c77545-e92a-4057-b348-c43e12402268"
],
"sda1": [
"8dffff29-d37b-4980-8be6-c531a832b9b7"
],
"sr0": [
"2020-11-04-11-36-43-00"
]
}
},
"ansible_devices": {
"dm-0": {
"holders": [],
"host": "",
"links": {
"ids": [
"dm-name-centos-root",
"dm-uuid-LVM-SJen3omEsNW5XUfryBMiUG9dk1VsTwbHDg0UdvWIxxiMWICoXjkhZvYcDfYWPwBw"
],
"labels": [],
"masters": [],
"uuids": [
"c49578c1-a5ed-4f02-b6aa-fb84c9adba87"
]
},
"model": null,
"partitions": {},
"removable": "0",
"rotational": "1",
"sas_address": null,
"sas_device_handle": null,
"scheduler_mode": "",
"sectors": "104857600",
"sectorsize": "512",
"size": "50.00 GB",
"support_discard": "0",
"vendor": null,
"virtual": 1
},
"dm-1": {
"holders": [],
"host": "",
"links": {
"ids": [
"dm-name-centos-swap",
"dm-uuid-LVM-SJen3omEsNW5XUfryBMiUG9dk1VsTwbHHv8kIG9QXQiYL8HyznjqKOGod8NGPNjJ"
],
"labels": [],
"masters": [],
"uuids": [
"d5067ec0-4f37-4d25-a9ca-2241ebf26514"
]
},
"model": null,
"partitions": {},
"removable": "0",
"rotational": "1",
"sas_address": null,
"sas_device_handle": null,
"scheduler_mode": "",
"sectors": "16515072",
"sectorsize": "512",
"size": "7.88 GB",
"support_discard": "0",
"vendor": null,
"virtual": 1
},
"dm-2": {
"holders": [],
"host": "",
"links": {
"ids": [
"dm-name-centos-home",
"dm-uuid-LVM-SJen3omEsNW5XUfryBMiUG9dk1VsTwbHXT7h4M80G2UxiojdOyNnW3W9QZiExlzr"
],
"labels": [],
"masters": [],
"uuids": [
"47c77545-e92a-4057-b348-c43e12402268"
]
},
"model": null,
"partitions": {},
"removable": "0",
"rotational": "1",
"sas_address": null,
"sas_device_handle": null,
"scheduler_mode": "",
"sectors": "295944192",
"sectorsize": "512",
"size": "141.12 GB",
"support_discard": "0",
"vendor": null,
"virtual": 1
},
"sda": {
"holders": [],
"host": "SCSI storage controller: Broadcom / LSI 53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI (rev 01)",
"links": {
"ids": [],
"labels": [],
"masters": [],
"uuids": []
},
"model": "VMware Virtual S",
"partitions": {
"sda1": {
"holders": [],
"links": {
"ids": [],
"labels": [],
"masters": [],
"uuids": [
"8dffff29-d37b-4980-8be6-c531a832b9b7"
]
},
"sectors": "2097152",
"sectorsize": 512,
"size": "1.00 GB",
"start": "2048",
"uuid": "8dffff29-d37b-4980-8be6-c531a832b9b7"
},
"sda2": {
"holders": [
"centos-root",
"centos-swap",
"centos-home"
],
"links": {
"ids": [
"lvm-pv-uuid-nmGG2H-Jie4-MLvx-EYS8-NGGJ-VDm8-303P14"
],
"labels": [],
"masters": [
"dm-0",
"dm-1",
"dm-2"
],
"uuids": []
},
"sectors": "417331200",
"sectorsize": 512,
"size": "199.00 GB",
"start": "2099200",
"uuid": null
}
},
"removable": "0",
"rotational": "1",
"sas_address": null,
"sas_device_handle": null,
"scheduler_mode": "deadline",
"sectors": "419430400",
"sectorsize": "512",
"size": "200.00 GB",
"support_discard": "0",
"vendor": "VMware,",
"virtual": 1
},
"sr0": {
"holders": [],
"host": "IDE interface: Intel Corporation 82371AB/EB/MB PIIX4 IDE (rev 01)",
"links": {
"ids": [
"ata-VMware_Virtual_IDE_CDROM_Drive_00000000000000000001"
],
"labels": [
"CentOS\\x207\\x20x86_64"
],
"masters": [],
"uuids": [
"2020-11-04-11-36-43-00"
]
},
"model": "VMware IDE CDR00",
"partitions": {},
"removable": "1",
"rotational": "1",
"sas_address": null,
"sas_device_handle": null,
"scheduler_mode": "deadline",
"sectors": "9203712",
"sectorsize": "2048",
"size": "4.39 GB",
"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": [
"223.5.5.5"
]
},
"ansible_docker0": {
"active": false,
"device": "docker0",
"features": {
"busy_poll": "off [fixed]",
"fcoe_mtu": "off [fixed]",
"generic_receive_offload": "on",
"generic_segmentation_offload": "on",
"highdma": "on",
"hw_tc_offload": "off [fixed]",
"l2_fwd_offload": "off [fixed]",
"large_receive_offload": "off [fixed]",
"loopback": "off [fixed]",
"netns_local": "on [fixed]",
"ntuple_filters": "off [fixed]",
"receive_hashing": "off [fixed]",
"rx_all": "off [fixed]",
"rx_checksumming": "off [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",
"tx_checksum_ipv4": "off [fixed]",
"tx_checksum_ipv6": "off [fixed]",
"tx_checksum_sctp": "off [fixed]",
"tx_checksumming": "on",
"tx_fcoe_segmentation": "on",
"tx_gre_csum_segmentation": "on",
"tx_gre_segmentation": "on",
"tx_gso_partial": "on",
"tx_gso_robust": "on",
"tx_ipip_segmentation": "on",
"tx_lockless": "on [fixed]",
"tx_nocache_copy": "off",
"tx_scatter_gather": "on",
"tx_scatter_gather_fraglist": "on",
"tx_sctp_segmentation": "on",
"tx_sit_segmentation": "on",
"tx_tcp6_segmentation": "on",
"tx_tcp_ecn_segmentation": "on",
"tx_tcp_mangleid_segmentation": "on",
"tx_tcp_segmentation": "on",
"tx_udp_tnl_csum_segmentation": "on",
"tx_udp_tnl_segmentation": "on",
"tx_vlan_offload": "on",
"tx_vlan_stag_hw_insert": "on",
"udp_fragmentation_offload": "on",
"vlan_challenged": "off [fixed]"
},
"hw_timestamp_filters": [],
"id": "8000.02421336b3b7",
"interfaces": [],
"ipv4": {
"address": "172.17.0.1",
"broadcast": "172.17.255.255",
"netmask": "255.255.0.0",
"network": "172.17.0.0",
"prefix": "16"
},
"macaddress": "02:42:13:36:b3:b7",
"mtu": 1500,
"promisc": false,
"stp": false,
"timestamping": [
"rx_software",
"software"
],
"type": "bridge"
},
"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.2.52",
"broadcast": "192.168.2.255",
"netmask": "255.255.255.0",
"network": "192.168.2.0",
"prefix": "24"
},
"ipv6": [
{
"address": "fe80::a157:95cb:e63c:10dc",
"prefix": "64",
"scope": "link"
}
],
"macaddress": "00:0c:29:1d:c2:4f",
"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": "zh_CN.UTF-8",
"LESSOPEN": "||/usr/bin/lesspipe.sh %s",
"LOGNAME": "root",
"LS_COLORS": "rs=0:di=38;5;27:ln=38;5;51:mh=44;38;5;15:pi=40;38;5;11:so=38;5;13:do=38;5;5:bd=48;5;232;38;5;11:cd=48;5;232;38;5;3:or=48;5;232;38;5;9:mi=05;48;5;232;38;5;15:su=48;5;196;38;5;15:sg=48;5;11;38;5;16:ca=48;5;196;38;5;226:tw=48;5;10;38;5;16:ow=48;5;10;38;5;21:st=48;5;21;38;5;15:ex=38;5;34:*.tar=38;5;9:*.tgz=38;5;9:*.arc=38;5;9:*.arj=38;5;9:*.taz=38;5;9:*.lha=38;5;9:*.lz4=38;5;9:*.lzh=38;5;9:*.lzma=38;5;9:*.tlz=38;5;9:*.txz=38;5;9:*.tzo=38;5;9:*.t7z=38;5;9:*.zip=38;5;9:*.z=38;5;9:*.Z=38;5;9:*.dz=38;5;9:*.gz=38;5;9:*.lrz=38;5;9:*.lz=38;5;9:*.lzo=38;5;9:*.xz=38;5;9:*.bz2=38;5;9:*.bz=38;5;9:*.tbz=38;5;9:*.tbz2=38;5;9:*.tz=38;5;9:*.deb=38;5;9:*.rpm=38;5;9:*.jar=38;5;9:*.war=38;5;9:*.ear=38;5;9:*.sar=38;5;9:*.rar=38;5;9:*.alz=38;5;9:*.ace=38;5;9:*.zoo=38;5;9:*.cpio=38;5;9:*.7z=38;5;9:*.rz=38;5;9:*.cab=38;5;9:*.jpg=38;5;13:*.jpeg=38;5;13:*.gif=38;5;13:*.bmp=38;5;13:*.pbm=38;5;13:*.pgm=38;5;13:*.ppm=38;5;13:*.tga=38;5;13:*.xbm=38;5;13:*.xpm=38;5;13:*.tif=38;5;13:*.tiff=38;5;13:*.png=38;5;13:*.svg=38;5;13:*.svgz=38;5;13:*.mng=38;5;13:*.pcx=38;5;13:*.mov=38;5;13:*.mpg=38;5;13:*.mpeg=38;5;13:*.m2v=38;5;13:*.mkv=38;5;13:*.webm=38;5;13:*.ogm=38;5;13:*.mp4=38;5;13:*.m4v=38;5;13:*.mp4v=38;5;13:*.vob=38;5;13:*.qt=38;5;13:*.nuv=38;5;13:*.wmv=38;5;13:*.asf=38;5;13:*.rm=38;5;13:*.rmvb=38;5;13:*.flc=38;5;13:*.avi=38;5;13:*.fli=38;5;13:*.flv=38;5;13:*.gl=38;5;13:*.dl=38;5;13:*.xcf=38;5;13:*.xwd=38;5;13:*.yuv=38;5;13:*.cgm=38;5;13:*.emf=38;5;13:*.axv=38;5;13:*.anx=38;5;13:*.ogv=38;5;13:*.ogx=38;5;13:*.aac=38;5;45:*.au=38;5;45:*.flac=38;5;45:*.mid=38;5;45:*.midi=38;5;45:*.mka=38;5;45:*.mp3=38;5;45:*.mpc=38;5;45:*.ogg=38;5;45:*.ra=38;5;45:*.wav=38;5;45:*.axa=38;5;45:*.oga=38;5;45:*.spx=38;5;45:*.xspf=38;5;45:",
"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.2.7 38822 22",
"SSH_CONNECTION": "192.168.2.7 38822 192.168.2.52 22",
"SSH_TTY": "/dev/pts/0",
"TERM": "xterm-256color",
"USER": "root",
"XDG_RUNTIME_DIR": "/run/user/0",
"XDG_SESSION_ID": "19",
"_": "/usr/bin/python"
},
"ansible_fibre_channel_wwn": [],
"ansible_fips": false,
"ansible_form_factor": "Other",
"ansible_fqdn": "node1",
"ansible_hostname": "node1",
"ansible_hostnqn": "",
"ansible_interfaces": [
"lo",
"docker0",
"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",
"prefix": "8"
},
"ipv6": [
{
"address": "::1",
"prefix": "128",
"scope": "host"
}
],
"mtu": 65536,
"promisc": false,
"timestamping": [
"rx_software",
"software"
],
"type": "loopback"
},
"ansible_loadavg": {
"15m": 0.05,
"1m": 0.0,
"5m": 0.04
},
"ansible_local": {},
"ansible_locally_reachable_ips": {
"ipv4": [
"127.0.0.0/8",
"127.0.0.1",
"172.17.0.1",
"192.168.2.52"
],
"ipv6": [
"::1",
"fe80::a157:95cb:e63c:10dc"
]
},
"ansible_lsb": {},
"ansible_lvm": {
"lvs": {
"home": {
"size_g": "141.12",
"vg": "centos"
},
"root": {
"size_g": "50.00",
"vg": "centos"
},
"swap": {
"size_g": "7.88",
"vg": "centos"
}
},
"pvs": {
"/dev/sda2": {
"free_g": "0.00",
"size_g": "199.00",
"vg": "centos"
}
},
"vgs": {
"centos": {
"free_g": "0.00",
"num_lvs": "3",
"num_pvs": "1",
"size_g": "199.00"
}
}
},
"ansible_machine": "x86_64",
"ansible_machine_id": "8f493910db044b7badeae1143229a5f7",
"ansible_memfree_mb": 7275,
"ansible_memory_mb": {
"nocache": {
"free": 7492,
"used": 471
},
"real": {
"free": 7275,
"total": 7963,
"used": 688
},
"swap": {
"cached": 0,
"free": 0,
"total": 0,
"used": 0
}
},
"ansible_memtotal_mb": 7963,
"ansible_mounts": [
{
"block_available": 224504,
"block_size": 4096,
"block_total": 259584,
"block_used": 35080,
"device": "/dev/sda1",
"fstype": "xfs",
"inode_available": 523962,
"inode_total": 524288,
"inode_used": 326,
"mount": "/boot",
"options": "rw,relatime,attr2,inode64,noquota",
"size_available": 919568384,
"size_total": 1063256064,
"uuid": "8dffff29-d37b-4980-8be6-c531a832b9b7"
},
{
"block_available": 12295926,
"block_size": 4096,
"block_total": 13100800,
"block_used": 804874,
"device": "/dev/mapper/centos-root",
"fstype": "xfs",
"inode_available": 26140015,
"inode_total": 26214400,
"inode_used": 74385,
"mount": "/",
"options": "rw,relatime,attr2,inode64,noquota",
"size_available": 50364112896,
"size_total": 53660876800,
"uuid": "c49578c1-a5ed-4f02-b6aa-fb84c9adba87"
},
{
"block_available": 36966713,
"block_size": 4096,
"block_total": 36974961,
"block_used": 8248,
"device": "/dev/mapper/centos-home",
"fstype": "xfs",
"inode_available": 73986045,
"inode_total": 73986048,
"inode_used": 3,
"mount": "/home",
"options": "rw,relatime,attr2,inode64,noquota",
"size_available": 151415656448,
"size_total": 151449440256,
"uuid": "47c77545-e92a-4057-b348-c43e12402268"
}
],
"ansible_nodename": "node1",
"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",
"quiet": true,
"rd.lvm.lv": [
"centos/root",
"centos/swap"
],
"rhgb": true,
"ro": true,
"root": "/dev/mapper/centos-root"
},
"ansible_processor": [
"0",
"GenuineIntel",
"Intel(R) Xeon(R) CPU E5-2670 v2 @ 2.50GHz",
"1",
"GenuineIntel",
"Intel(R) Xeon(R) CPU E5-2670 v2 @ 2.50GHz",
"2",
"GenuineIntel",
"Intel(R) Xeon(R) CPU E5-2670 v2 @ 2.50GHz",
"3",
"GenuineIntel",
"Intel(R) Xeon(R) CPU E5-2670 v2 @ 2.50GHz"
],
"ansible_processor_cores": 2,
"ansible_processor_count": 2,
"ansible_processor_nproc": 4,
"ansible_processor_threads_per_core": 1,
"ansible_processor_vcpus": 4,
"ansible_product_name": "VMware Virtual Platform",
"ansible_product_serial": "VMware-56 4d 0c d2 13 5b 0b d8-d3 c8 5d c7 45 1d c2 4f",
"ansible_product_uuid": "D20C4D56-5B13-D80B-D3C8-5DC7451DC24F",
"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": "AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBClI+DooIWu08pKeb4gYBiMEWcKAbi8cpu32+A9lBmcRBpAKqLz8/uO3L9chG6XFSOU02qCdkzGdZ3bapmI6I5A=",
"ansible_ssh_host_key_ecdsa_public_keytype": "ecdsa-sha2-nistp256",
"ansible_ssh_host_key_ed25519_public": "AAAAC3NzaC1lZDI1NTE5AAAAIGMoseg2ywDbDKCDS4h6IMVspZnBe1xP0AB5+mKzQAjj",
"ansible_ssh_host_key_ed25519_public_keytype": "ssh-ed25519",
"ansible_ssh_host_key_rsa_public": "AAAAB3NzaC1yc2EAAAADAQABAAABAQC9zlBHycpjLSIoqYJrZrqr0n/r04/YM4YXjkZ84aXfAEiM25t2lbMf1wt3gnCoT0YHTv727h13GQMpQFB1wpZKWh2+L2KGEp9EfbZU6IBRdsxZvtsJ1LhBfMSLKQlZF3CxrfAmkdS9D1LI/5CWfHeFTZ1nzq7BIv0++tJ3gLliHth9VXLBL0x1c8Qimrir8OC2wYMujl9EwVI8/CW3EYAKHulr6PxVvBhyWfcs2KKApId/dewBOUaIaeexvEYC17hvCwK9sBZdN+APK+GLShoAo7baa0eikdc64cF0uQlvjwvpH1FdQ9TIq+/dAN+2/4hQQRLVeauFfVWbg9HT8U+3",
"ansible_ssh_host_key_rsa_public_keytype": "ssh-rsa",
"ansible_swapfree_mb": 0,
"ansible_swaptotal_mb": 0,
"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": 587,
"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_tech_guest": [
"VMware"
],
"ansible_virtualization_tech_host": [],
"ansible_virtualization_type": "VMware",
"discovered_interpreter_python": "/usr/bin/python",
"gather_subset": [
"all"
],
"module_setup": true
},
"changed": false,
"deprecations": [],
"warnings": []
}3、模块的返回值
五、ansible变量
1、变量定义的方式:
通过命令行传递变量参数定义
1.1、在playbook中定义变量
通过vars定义变量
cat test.yaml
---
- hosts: node
vars:
- web_packages: nginx
tasks:
- name: Installed web
yum: "{{ web_packages }}"通过var_files定义变量,创建一个yml文件专门创建变量,属于公用变量
cat test.yaml
---
- hosts: node
vars_file:
- ./var.yml
tasks:
- name: Installed web
yum: "{{ web_packages }}"1.2、通过inventory在主机组或单个主机中设置变量
通过host_vars对主机进行定义
通过group_vars对主机组进行定义
cat /etc/ansible/hosts
# 主机变量
[web]
192.168.10.2 myid=1 state=master
192.168.10.3 myid=2 state=backup
# 组变量
[web:vars]
port=80为主机单独创建变量
mkdir host_vars
vim host_vars/192.168.10.2
state: master为主机组创建变量,方式类似,文件名要与主机组名一致
mkdir host_vars
vim host_vars/web外置传递变量,可以在执行playbook时定义变量
ansible-playbook test.yml -e "route_id=8888"2、ansible变量优先级:
外置传参——>playbook(vars_file——>vars)——>主机变量——>group_vars——>inventory_group3、Register Variables
Ansible的register变量是一种变量类型,用于在运行任务时存储命令、模块或脚本的输出。它们通常用于条件语句或后续任务中,以根据输出结果采取特定的操作。
注册变量可在任务执行完成后访问,以检查命令、模块或脚本执行的输出或结果。您可以使用register变量来存储远程主机上测试的结果,或者正则表达式的匹配结果,甚至是命令的输出。
Register可以将task执行的任务结果存储至某个变量中,便于后续引用
---
- hosts: all
tasks:
- name: Get network port
shell: netstat -lntp
register: System_port
- name: Debug
debug:
msg: "{{ System_port }}"这段代码的含义是将netstat -lntp的执行结果注入到System_port变量中。
ok: [192.168.10.2] => {
"msg": {
"changed": true,
"cmd": "netstat -lntp",
"delta": "0:00:00.033419",
"end": "2023-03-12 08:44:38.655001",
"failed": false,
"rc": 0,
"start": "2023-03-12 08:44:38.621582",
"stderr": "",
"stderr_lines": [],
"stdout": "Active Internet connections (only servers)\nProto Recv-Q Send-Q Local Address Foreign Address State PID/Program name \ntcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1055/sshd \ntcp 0 0 127.0.0.1:40663 0.0.0.0:* LISTEN 11868/containerd \ntcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 1166/master \ntcp6 0 0 :::9100 :::* LISTEN 1053/node_exporter \ntcp6 0 0 :::22 :::* LISTEN 1055/sshd \ntcp6 0 0 :::3000 :::* LISTEN 1054/grafana-server \ntcp6 0 0 ::1:25 :::* LISTEN 1166/master \ntcp6 0 0 :::9090 :::* LISTEN 1058/prometheus ",
"stdout_lines": [
"Active Internet connections (only servers)",
"Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name ",
"tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1055/sshd ",
"tcp 0 0 127.0.0.1:40663 0.0.0.0:* LISTEN 11868/containerd ",
"tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 1166/master ",
"tcp6 0 0 :::9100 :::* LISTEN 1053/node_exporter ",
"tcp6 0 0 :::22 :::* LISTEN 1055/sshd ",
"tcp6 0 0 :::3000 :::* LISTEN 1054/grafana-server ",
"tcp6 0 0 ::1:25 :::* LISTEN 1166/master ",
"tcp6 0 0 :::9090 :::* LISTEN 1058/prometheus "
]
}
}如果要选用其中的某个参数,只需要在变量名后加入即可:
- name: Debug
debug:
msg: "{{ System_port.stdout_lines }}"3.1、示例
# 示例:批量修改主机名
将192.168.10.2-3两台主机名修改为指定格式:
---
- hosts: all
tasks:
- name: String
shell: echo $RANDOM | md5sum | cut -c 2-10
register: system_sj
- name: Chanage hostname
hostname:
name: "centos_7.9_{{ system_sj.stdout }}"
# 执行结果分别在两台主机查看:
# 192.168.10.2
centos_7.9_1348277e0
# 192.168.10.3
centos_7.9_e087607624、Facts Variables
# Ansible中的facts是由Ansible在目标机器上运行的“setup”模块生成的系统和环境信息。这些信息存储在变量中,可以通过Ansible playbook和模板使用。
# Facts变量包括:
系统信息:操作系统、主机名、内核参数、网络接口、CPU信息等。
硬件信息:服务器制造商、BIOS版本、物理内存、磁盘容量等。
网络信息:IP地址、MAC地址、路由列表等。
环境变量:PATH、PYTHONPATH等环境变量。
# 在使用场景中:
检查CPU信息,生成不同的nginx配置文件
检查内存信息。生成不同的memcached配置文件
检查主机名信息,生成不同的zabbix配置文件
检查IP信息,生成不同的redis配置文件
# 可以使用下面这条命令查看可用信息:
ansible all -m setup4.1、示例: 基于IP生成redis配置文件
# playbook.yml
---
- hosts: all
tasks:
- name: Installed redis
yum:
name: redis
state: present
- name: Configure redis server
template:
src: ./redis.conf.j2
dest: /etc/redis.conf
notify: Restart redis server
- name: Started redis server
systemd:
name: redis
state: started
enabled: yes
handlers:
- name: Restart redis server
service:
name: redis
state: restarted
# redis.conf.j2
bind 127.0.0.1 {{ ansible_ens33.ipv4.address }}
# 查看结果
[root@centos_7 ~]# netstat -utpln | grep 6379
tcp 0 0 192.168.10.2:6379 0.0.0.0:* LISTEN 30584/redis-server
tcp 0 0 127.0.0.1:6379 0.0.0.0:* LISTEN 30584/redis-server
[root@centos_7 ~]# netstat -utpln | grep 6379
tcp 0 0 192.168.10.3:6379 0.0.0.0:* LISTEN 32967/redis-server
tcp 0 0 127.0.0.1:6379 0.0.0.0:* LISTEN 32967/redis-server 4.2、facts优化
# 使用gather_facts: no关闭facts
- hosts: all
gather_facts: no
# 使用redis进行缓存vim ansible.cfg
gathering = smart
fact_caching_timeout = 86400
fact_caching = redis
fact_caching_connection = 192.168.10.3:6379
# 如果redis有密码
fact_caching_connection = 192.168.10.3:6379:0:passwd六、Playbook
YAML 语法
YAML 文件的第一行为 “---”,表示这是一个 YAML 文件
YAML 中的字段大小写敏感
YAML 与 Python 一样,使用缩进表示层级关系
YAML 的缩进不允许使用 Tab 键,只允许使用空格,且空格的数目不重要,只要相同层级的元素左侧对齐即可
“#” 表示注释,从这个字符一直到行尾都会被解析器忽略
# YAML 支持三种格式的数据:
对象:键值对的集合,又称为映射,类似于 Python 中的字典
数组:一组按次序排列的值,又称为序列,类似于 Python 中的列表
纯量:单个的、不可再分的值,如字符串、布尔值和数字1、定义
Playbook 的功能强大,可以实现各种高级功能,如指定任务的执行顺序,委派其他主机来执行某一个任务,与监控服务器和负载均衡组件进行交互等。Ansible 中的模块类似 linux 下的命令,Playbook 类似于 linux 下的 Shell 脚本文件。Playbook 将各个模块组合起来实现复杂的部署功能。
# 在 Ansible 中,一个 Play 必须包含以下两项
hosts:需要对哪些远程服务器执行操作
tasks:需要在这些服务器上执行的任务列表
# 举个例子:
---
- hosts: dbservers
become: yes
become_method: sudo
tasks:
- name: install mongodb
apt: name=mongodb-server state=present
- hosts: webservers
tasks:
- name: copy file
copy: src=/tmp/data.txt dest=/tmp/data.txt
- name: change mode
file: dest=/tmp/data.txt mode=655 owner=abc group=abc
# tasks 的格式是:
tasks:
- name: task1
module1: args1
- name: task2
module2: args2
...
也可以折叠换行:
- name: install apache
apt: >
name=apache2
update_cache=yes
state=present
- name: install apache
apt:
name: apache2
update_cache: yes
state: present
# name 是可选的,所以可以不写。建议写上,方便使用者知道当前执行到哪一步。
# 在上边的例子中展示的 Playbook,可以拆分成两个 Playbook(db.yml 和 web.yml)。可以编写一个 all.yml:
---
- include: db.yml
- include: web.yml
在执行 all.yml 时,db.yml 和 web.yml 会依次执行。2、使用 ansible-playbook 执行 Playbook
# ansible-playbook 的命令行选项:
-T --timeout:建立 ssh 连接的超时时间
--key-file --private-key:建立 ssh 连接的私钥文件
-i --inventory-file:指定 Inventory 文件,默认是 /etc/ansible/hosts
-f --forks:并发执行的进程数,默认为 5
--list-hosts:匹配的服务器列表
--list-tasks:列出任务列表
--step:每执行一个任务后停止,等待用户确认
--syntax-check:检查 Playbook 的语法
-C --check:预测 Playbook 的执行结果3、详细语法
3.1、权限
# 在 Ansible 中,默认以当前用户连接远程服务器执行操作。可以在 ansible.cfg 文件中配置默认用户,也可以在 Play 定义中配置:
---
- hosts: webservers
remote_user: root
# 可以细分 task 对应的用户:
---
- hosts: webservers
remote_user: root
tasks:
- name: test connection
ping:
remote_user: abc
# 可以使用管理员身份执行操作:
---
- hosts: webservers
remote_user: abc
tasks:
- service: name=nginx state=started
become: yes
become_method: sudo3.2、通知
# 通过 notify 和 handler 机制来实现。handler 是 Ansible 提供的条件机制,与 task 类似。但是 handler 只有在被 notify 触发后才会执行:
---
- hosts: webservers
tasks:
- name: ensure apache is at the latest version
yum: name=httpd state=latest
- name: write the apache config file
template: src=/srv/httpd.j2 dest=/etc/httpd.conf
notify:
- restart apache
- name: ensure apache is running
service: name=httpd state=started
handlers:
- name: restart apache
service: name=httpd state=restarted
# handler 只会在 task 执行完之后执行,即使一个 handler 被触发多次,也只会执行一次。
handler 是按照定义顺序执行。官方文档提到 handler 的唯一用途就是重启服务与服务器。3.3、变量
# 最直接的定义变量的方式是定义在 Playbook 的 vars 选项中:
- hosts: dbservers
vars:
mysql_port: 3306
# 在 Playbook 中定义的变量,可以在模板渲染时使用,例如,官方例子:
[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
user=mysql
port={{ mysql_port }}
# 当变量比较多时,可以将变量保存在一个独立的文件中,通过 vars_files 引用该文件:
---
- hosts: all
vars:
favcolor: blue
vars_files:
- /vars/external_vars.yml
tasks:
- name: this is just a placeholder
command: /bin/echo foo
# 保存变量的文件是一个 YAML 格式的字典:
---
somevar: somevalue
password: magic
# 在 Ansible 中,可以获取任务的执行结果,将执行结果保存在一个变量中。这样的变量使用 register 进行定义,也称为注册变量。
- hosts: webservers
tasks:
- shell: /usr/bin/foo
register: foo_result
ignore_errors: True
- shell: /usr/bin/bar
when: foo_result.rc == 5
# ignore_errors 表示忽略当前 task 中的错误,when 是一个条件语句,条件为真时才会执行这个 task。3.4、Facts 变量
# 在 Ansible 中,有一些特殊的变量,这些变量不需要进行任何设置就可以直接使用,这样的变量称为 Facts 变量。
Facts 变量是 Ansible 执行远程部署之前从远程服务器中获取的系统信息。
在 Playbook 中,可以通过 gather_facts 选项控制是否收集远程服务器的信息,默认值为yes。
---
- hosts: all
tasks:
- shell: echo {{ansible_os_family}}
...
when: ansible_os_family == "Debian"
# 要访问复杂变量的子属性时,需要使用嵌套结构:
ansible_eth0["ipv4"]["address"]
ansible_eth0.ipv4.address # 常见内置变量
# 一般连接
ansible_host #用于指定被管理的主机的真实IP
ansible_port #用于指定连接到被管理主机的ssh端口号,默认是22
ansible_user #ssh连接时默认使用的用户名
# 特定ssh连接
ansible_connection #SSH连接的类型:local, ssh, paramiko,在ansible 1.2 之前默认是
paramiko,后来智能选择,优先使用基于ControlPersist的ssh(如果支持的话)
ansible_ssh_pass #ssh连接时的密码
ansible_ssh_private_key_file #秘钥文件路径,如果不想使用ssh‐agent管理秘钥文件时可以使用此选项
ansible_ssh_executable #如果ssh指令不在默认路径当中,可以使用该变量来定义其路径
# 特权升级
ansible_become #相当于ansible_sudo或者ansible_su,允许强制特权升级
ansible_become_user #通过特权升级到的用户,相当于ansible_sudo_user或者ansible_su_user
ansible_become_pass # 提升特权时,如果需要密码的话,可以通过该变量指定,相当于
ansible_sudo_pass或者ansible_su_pass
ansible_sudo_exec #如果sudo命令不在默认路径,需要指定sudo命令路径
# 远程主机环境参数
ansible_shell_executable # 设置目标机上使用的shell,默认为/bin/sh
ansible_python_interpreter #用来指定python解释器的路径,默认为/usr/bin/python 同样可以指
定ruby 、perl 的路径
ansible_*_interpreter #其他解释器路径,用法与ansible_python_interpreter类似,这里"*"可以
是ruby或才perl等其他语3.5、循环
# 举例:
- name: Install Mysql package
yum: name={{ item }} state=installed
with_items:
- mysql-server
- MySQL-python
- libselinux-python
- libsemanage-python3.6、条件判断
# 使用 when
tasks:
- name: shut down Debian flavored systems
command: /sbin/shutdown -t now
when: ansible_os_family == "Debian"
# 多个条件语句:
...
when:
- ansible_distribution == "CentOS"
- ansible_distribution_major_version == "6"
# 使用 and or:
...
when: (ansible_distribution == "CentOS" and ansible_distribution_major_version == "6") or
(ansible_distribution == "CentOS" and ansible_distribution_major_version == "7")
# 使用 jinja2 的过滤器:
...
when: result|failed
# 可以读取变量的取值:
...
epic: true
...
when: epic
# 可以和循环一起使用:
tasks:
- command: echo {{ item }}
with_items: [ 0, 2, 4, 6, 8, 10 ]
when: item > 53.7、任务执行策略
# 从 Ansible 2.0 开始,支持 free 的任务执行策略,允许执行较快的远程服务器提前完成 Play 的部署:
- hosts: all
strategy: free
...4、高级语法
4.1、线性更新服务器
# 使用 Ansible 的 serial 选项,可以取值为一个数字,表示一次更新多少台服务器,也可以取值为一个百分比,例如:
- name: test play
hosts: webservers
serial: 1
- name: test play
hosts: webservers
serial: "30%"
# 下面的配置表示先更新 1 台,再更新 5 台,最后更新 10 台服务器:
- name: test play
hosts: webservers
serial:
- 1
- 5
- 104.2、使用 delegate_to 实现任务委派功能
name: take out of load balancer pool
command: /usr/bin/take_out_of_pool {{ inventory_hostname }}
delegate_to: 127.0.0.14.3、使用 local_action 在控制服务器执行操作
tasks:
- name: take out of load balancer pool
local_action: command /usr/bin/take_out_of_pool {{ inventory_hostname }}4.4、使用 run_once 保证任务只执行一次
...
run_once: true4.5、高级循环结构
# 除了 with_items 之外,还包含以下循环:
with_lines
with_fileglob
with_first_found
with_dict
with_flattened
with_indexed_items
with_nested
with_random_choice
with_sequence
with_together
with_subelements
with_file
# with_items 的每一项都可以是字典,通过 item.key 的方式引用字典:
- name: Add several users
ansible.builtin.user:
name: "{{ item.name }}"
state: present
groups: "{{ item.groups }}"
with_items:
- { name: 'testuser1', groups: 'wheel' }
- { name: 'testuser2', groups: 'root' }
# 如果循环的元素是一个嵌套字典,需要使用 with_dict 遍历元素:
---
users:
alice:
name: Alice Appleworth
telephone: 123-456-7890
bob:
name: Bob Bananarama
telephone: 987-654-3210
tasks:
- name: Print phone records
debug:
msg: "User {{ item.key }} is {{ item.value.name }} ({{ item.value.telephone }})"
with_dict: "{{ users }}"
# 如果循环的每一项是列表,可以使用 with_nested 遍历,通过下标的方式访问列表中的元素:
- name: give users access to multiple databases
mysql_user:
name: "{{ item[0] }}"
priv: "{{ item[1] }}.*:ALL"
append_privs: yes
password: "foo"
with_nested:
- [ 'alice', 'bob' ]
- [ 'clientdb', 'employeedb', 'providerdb' ]
# 可以使用 with_sequence 产生数字列表,指定起点、终点和步长:
- user:
name: "{{ item }}"
state: present
groups: "evens"
with_sequence: start=0, end=32, format=testuser%02x
# with_random_choice 随机选择一项:
- debug:
msg: "{{ item }}"
with_random_choice:
- "go through the door"
- "drink from the goblet"
- "press the red button"
- "do nothing"4.6、使用标签灵活控制执行
- name: install packages
yum: name={{ item }} state=installed
with_items:
- httpd
- memcached
tags:
- packages
- name: uploading config file
template: src=templates/src.j2 dest=/etc/foo.conf
tags:
- configuration
- name: be sure ntpd is running and enabled
service: name=ntpd state=started enabled=yes
tags: ntp
# 在这个例子中,用 tags 为每个任务打上了标签,使用下边的方法可以控制标签执行:
ansible-playbook example.yml --tags "configuration,packages"
ansible-playbook example.yml --skip-tags "ntp"4.7、使用 changed_when 控制对 changed 字段的定义
# 在使用 shell 模块时,往往会根据自己的判断报告是否对远程服务器进行了修改。
tasks:
- shell: /usr/bin/billybass --mode="take me to the river"
register: bass_result
changed_when: "bass_result.rc != 2"4.8、使用 failed_when 控制对 failed 字段的定义
# 对于一些特殊的命令,无法通过返回码判断命令是否执行成功,可以使用 failed_when 自定义命令执行失败的标准:
- name: this command prints FAILED when it fails
command: /usr/bin/example-command -x -y -z
register: command_result
failed_when: "'FAILED' in command_result.stderr"5、Ansible Handlers 自动化中的关键组件
在现代的IT环境中,自动化已经成为提高效率和减少错误的关键。Ansible作为一款流行的自动化工具,通过使用Playbooks来定义和执行任务。而Handlers作为Ansible的组件之一,在自动化过程中发挥着重要的作用。本文将深入探讨Handlers的概念、作用和使用方法,帮助读者更好地理解和应用Ansible中的Handlers。
5.1、Handlers概述
Handlers是Ansible Playbooks中的一种特殊任务类型。它们类似于事件处理程序,用于在特定条件下触发和执行任务。Handlers通常与任务关联,当任务的状态发生变化时,Handlers会被触发执行。Handlers可以用于执行各种操作,如重启服务、重新加载配置文件等。
5.2、Handlers的工作原理
Handlers的触发和执行是基于事件驱动的。当一个任务执行完成后,Ansible会检查与该任务关联的Handlers,并将其添加到一个队列中。当Playbook的所有任务执行完毕时,Ansible会遍历Handlers队列,并按顺序执行队列中的Handlers任务。这种机制确保了Handlers的执行顺序和可靠性。
5.3、Handlers的关联和触发
Handlers与任务之间的关联是通过名称来实现的。在Playbooks中,可以使用notify关键字将Handlers与任务关联起来。当任务的状态发生变化时,可以使用notify关键字通知Handlers执行。Handlers只有在被通知时才会执行,这样可以避免不必要的执行和资源浪费。
5.4、使用Handlers的示例
下面是一个使用Handlers的示例,演示了如何在Ansible Playbook中定义和使用Handlers:
- name: Install and configure Nginx
hosts: web_servers
tasks:
- name: Install Nginx
apt:
name: nginx
state: present
notify: Restart Nginx
- name: Copy Nginx configuration file
copy:
src: nginx.conf
dest: /etc/nginx/nginx.conf
notify: Reload Nginx
handlers:
- name: Restart Nginx
service:
name: nginx
state: restarted
- name: Reload Nginx
service:
name: nginx
state: reloaded在上述示例中,当安装Nginx和复制配置文件的任务执行完成后,分别使用notify关键字通知了两个Handlers:Restart Nginx和Reload Nginx。这两个Handlers会在Playbook的所有任务执行完毕后被触发执行,分别重启和重新加载Nginx服务。5.5、最佳实践和注意事项
在使用Handlers时,有一些最佳实践和注意事项需要考虑。
首先,Handlers的命名应具有描述性,以便于理解和维护。
其次,Handlers的关联应与任务的关联一致,确保正确触发和执行。
此外,Handlers的执行顺序也需要仔细规划和测试,以避免意外的行为。
Handlers作为Ansible自动化工具中的重要组件,为自动化任务的触发和响应提供了强大的功能。通过Handlers,可以实现任务的自动执行和响应,提高自动化的效率和可靠性。
七、role 的定义与使用
1、role 的概念
# role 是一种将复杂的 Playbook 分割成多个文件的机制,简化了 Playbook 的编写,使 Playbook 的复用变得简单。
每个 role 都会有一个名字,比如 mongodb,与 mongodb role 相关的文件都存放在 /etc/ansible/roles/mongodb 目录中:
$ tree mongodb
mongodb
|-----defaults
| └----main.yml
|-----files
|-----handlers
| └----main.yml
|-----meta
| └----main.yml
|-----README.md
|-----tasks
| └----main.yml
|-----templates
└-----vars
└----main.yml
defaults/main.yml: 可以被覆盖的默认变量
files: 目录,保存了需要同步到远程服务器的文件
handlers/main.yml: 与 Playbook 中的 handlers 选项类似,包含了所有 handler
meta/main.yml: role 的依赖信息
README.md: role 的说明文件
tasks/main.yml: 包含了任务列表
templates: 目录,保存了 jinja2 模板文件
vars/main.yml: 不应该被覆盖的变量,与 Playbook 中的 vars 或 vars_file 类似
# 注意:这里的文件都是可选的。templates 和 files 目录中的文件都是相对引用,不需要写路径。2、使用 ansible-galaxy 管理 role
ansible-galaxy 的用法:
# 初始化一个 roles 的目录结构:
ansible-galaxy init /etc/ansible/roles/role_name
# 安装别人写好的 role:
ansible-galaxy install -p /etc/ansible/roles role_name
# 安装到指定目录
ansible-galaxy -p ./roles install role_name
Ansible 中,默认将 role 下载到 /etc/ansible/roles 目录,也可以在 ansible.cfg 中配置 role_path。
# 列出已安装的 roles:
ansible-galaxy list
# 查看已安装的 role 的信息:
ansible-galaxy info role_name
# 卸载 role
ansible-galaxy remove role_name3、使用 role
# 在使用 role 之前需要编写一个 Playbook,例如:
---
- hosts: webservers
become: yes
become_method: sudo
roles:
- role: role_name
# 执行时使用以下格式:
ansible-playbook -i hosts role_name.yml
评论区