Ansible 基础¶
在本章中,您将学习如何使用 Ansible。
目标:在本章中,您将学会如何
实现 Ansible;
在服务器上应用配置更改;
创建第一个 Ansible Playbooks;
ansible、module(模块)、playbook(剧本)。
知识:
复杂度:
阅读时间: 30 分钟
Ansible 集中化并自动化管理任务。它是
- 无代理(不需要在客户端进行特定部署),
- 幂等性(每次运行效果相同)。
它使用 SSH 协议远程配置 Linux 客户端,或使用 WinRM 协议处理 Windows 客户端。如果这两种协议都不可用,Ansible 也可以使用 API,这使得 Ansible 成为配置服务器、工作站、Docker 服务、网络设备等的真正瑞士军刀(实际上几乎所有东西)。
警告
从 Ansible 服务器到所有客户端的 SSH 或 WinRM 流量的打开,使其成为必须仔细监控的关键架构元素。
由于 Ansible 主要基于推送模式,因此在每次执行之间,它不会保持目标服务器的状态。相反,它会在每次执行时执行新的状态检查。它被称为无状态。
它将在以下方面为您提供帮助
- 置备(部署新虚拟机),
- 应用程序部署,
- 配置管理,
- 自动化,
- 编排(当使用多个目标时)。
注意
Ansible 最初由 Michael DeHaan 编写,他是 Cobbler 等其他工具的创始人。
最早的第一个版本是 0.0.1,于 2012 年 3 月 9 日发布。
2015 年 10 月 17 日,AnsibleWorks(Ansible 背后的公司)被 Red Hat 以 1.5 亿美元收购。
要为您的日常 Ansible 使用提供图形界面,您可以安装一些工具,如 RedHat 的 Ansible Tower(不免费),其开源对应物 Awx,或者 Jenkins 和出色的 Rundeck 等其他项目也可以使用。
摘要
要进行本次培训,您至少需要 2 台 Rocky8 服务器
- 第一台将是 **管理机**,Ansible 将安装在其上。
- 第二台将是需要配置和管理的服务器(另一台 Linux 系统,非 Rocky Linux 也可以)。
在下面的示例中,管理站点的 IP 地址为 172.16.1.10,被管理站点的 IP 地址为 172.16.1.11。您需要根据您的 IP 地址计划来调整示例。
Ansible 词汇表¶
- 管理机:安装了 Ansible 的机器。由于 Ansible 是**无代理**的,因此不会在被管理服务器上部署任何软件。
- 被管理节点:Ansible 管理的目标设备也称为“主机”。它们可以是服务器、网络设备或任何其他计算机。
- Inventory(清单):一个包含被管理服务器信息的的文件。
- Tasks(任务):任务是一个定义要执行过程的块(例如,创建用户或组,安装软件等)。
- Module(模块):模块是对任务的抽象。Ansible 提供了许多模块。
- Playbooks(剧本):一个简单的 yaml 格式文件,定义目标服务器和要执行的任务。
- Role(角色):角色允许您组织 Playbooks 和所有其他必要文件(模板、脚本等),以便于代码共享和重用。
- Collection(集合):集合包括 Playbooks、Roles、Modules 和 Plugins 的逻辑集合。
- Facts(事实):这些是包含系统信息的全局变量(机器名、系统版本、网络接口和配置等)。
- Handlers(处理程序):当发生更改时,它们用于停止或重新启动服务。
在管理服务器上安装¶
Ansible 可在 EPEL 仓库中获得,但有时可能对于当前版本来说太旧了,您可能希望使用更新的版本。
因此,我们将考虑两种安装方式
- 基于 EPEL 仓库的安装
- 基于
pip
python 包管理器的安装
这两种版本都需要 EPEL,所以您可以继续安装它
- EPEL 安装
sudo dnf install epel-release
从 EPEL 安装¶
如果我们从 EPEL 安装 Ansible,我们可以这样做
sudo dnf install ansible
然后验证安装
$ ansible --version
ansible [core 2.14.2]
config file = /etc/ansible/ansible.cfg
configured module search path = ['/home/rocky/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python3.11/site-packages/ansible ansible collection location = /home/rocky/.ansible/collections:/usr/share/ansible/collections
executable location = /usr/bin/ansible
python version = 3.11.2 (main, Jun 22 2023, 04:35:24) [GCC 8.5.0 20210514
(Red Hat 8.5.0-18)] (/usr/bin/python3.11)
jinja version = 3.1.2
libyaml = True
$ python3 --version
Python 3.6.8
请注意,ansible 自带其自己的 python 版本,不同于系统的 python 版本(此处为 3.11.2 对比 3.6.8)。在 pip 安装您安装所需的 python 模块时(例如 pip3.11 install PyVMomi
),您需要考虑这一点。
从 python pip 安装¶
由于我们想使用更高版本的 Ansible,我们将从 python3-pip
安装它
注意
如果之前从 EPEL 安装了 Ansible,请先卸载它。
在此阶段,我们可以选择使用所需的 python 版本安装 ansible。
sudo dnf install python38 python38-pip python38-wheel python3-argcomplete rust cargo curl
注意
python3-argcomplete
由 EPEL 提供。如果尚未完成,请安装 epel-release。此包将帮助您完成 Ansible 命令。
现在我们可以安装 Ansible
pip3.8 install --user ansible
activate-global-python-argcomplete --user
检查您的 Ansible 版本
$ ansible --version
ansible [core 2.13.11]
config file = None
configured module search path = ['/home/rocky/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /home/rocky/.local/lib/python3.8/site-packages/ansible
ansible collection location = /home/rocky/.ansible/collections:/usr/share/ansible/collections
executable location = /home/rocky/.local/bin/ansible
python version = 3.8.16 (default, Jun 25 2023, 05:53:51) [GCC 8.5.0 20210514 (Red Hat 8.5.0-18)]
jinja version = 3.1.2
libyaml = True
注意
在我们的例子中,手动安装的版本比 RPM 打包的版本旧,因为我们使用的是较旧的 python 版本。此观察结果会随着时间和发行版及 python 版本的更新而变化。
配置文件¶
服务器配置位于 /etc/ansible
目录下。
有两个主要配置文件
- 主配置文件
ansible.cfg
,其中包含命令、模块、插件和 ssh 配置; - 客户端机器管理清单文件
hosts
,其中声明了客户端和客户端组。
如果 Ansible 是使用其 RPM 包安装的,则配置文件会自动创建。通过 pip
安装,此文件不存在。我们需要借助 ansible-config
命令手动创建它。
$ ansible-config -h
usage: ansible-config [-h] [--version] [-v] {list,dump,view,init} ...
View ansible configuration.
positional arguments:
{list,dump,view,init}
list Print all config options
dump Dump configuration
view View configuration file
init Create initial configuration
示例
ansible-config init --disabled > /etc/ansible/ansible.cfg
--disabled
选项允许您通过在选项前添加 ;
来注释掉整套选项。
注意
您也可以选择将 ansible 配置嵌入到您的代码存储库中,Ansible 会按以下顺序加载它找到的配置文件(处理它遇到的第一个文件并忽略其余文件)
- 如果设置了环境变量
$ANSIBLE_CONFIG
,则加载指定的配置文件。 ansible.cfg
(如果存在于当前目录)。~/.ansible.cfg
(如果存在于用户主目录)。
如果找不到这三个文件中的任何一个,则会加载默认文件。
Inventory 文件 /etc/ansible/hosts
¶
由于 Ansible 将需要与您所有要配置的设备进行交互,因此为其提供一个(或多个)结构良好且完全符合您组织情况的 Inventory 文件至关重要。
有时需要仔细考虑如何构建此文件。
转到默认的 Inventory 文件,该文件位于 /etc/ansible/hosts
目录下。提供了一些示例并进行了注释。
# This is the default ansible 'hosts' file.
#
# It should live in /etc/ansible/hosts
#
# - Comments begin with the '#' character
# - Blank lines are ignored
# - Groups of hosts are delimited by [header] elements
# - You can enter hostnames or ip addresses
# - A hostname/ip can be a member of multiple groups
# Ex 1: Ungrouped hosts, specify before any group headers:
## green.example.com
## blue.example.com
## 192.168.100.1
## 192.168.100.10
# Ex 2: A collection of hosts belonging to the 'webservers' group:
## [webservers]
## alpha.example.org
## beta.example.org
## 192.168.1.100
## 192.168.1.110
# If you have multiple hosts following a pattern, you can specify
# them like this:
## www[001:006].example.com
# Ex 3: A collection of database servers in the 'dbservers' group:
## [dbservers]
##
## db01.intranet.mydomain.net
## db02.intranet.mydomain.net
## 10.25.1.56
## 10.25.1.57
# Here's another example of host ranges, this time there are no
# leading 0s:
## db-[99:101]-node.example.com
正如您所见,提供的示例文件使用了 INI 格式,这对于系统管理员来说非常熟悉。请注意,您可以选择其他文件格式(例如 yaml),但对于首次测试,INI 格式非常适合我们未来的示例。
在生产环境中,Inventory 可以自动生成,尤其是在您拥有 VMware VSphere 等虚拟化环境或云环境(Aws、OpenStack 或其他)的情况下。
- 在
/etc/ansible/hosts
中创建主机组
正如您可能注意到的,组在方括号中声明。然后是属于这些组的元素。例如,您可以创建一个 rocky8
组,在该文件中插入以下块。
[rocky8]
172.16.1.10
172.16.1.11
组可以嵌套在其他组内。在这种情况下,必须使用 :children
属性指定父组包含子组,如下所示。
[linux:children]
rocky8
debian9
[ansible:children]
ansible_management
ansible_clients
[ansible_management]
172.16.1.10
[ansible_clients]
172.16.1.10
我们不会深入探讨 Inventory,但如果您有兴趣,请查看 此链接。
现在我们的管理服务器已安装且 Inventory 已准备好,是时候运行我们的第一个 ansible
命令了。
ansible
命令行使用¶
ansible
命令在所有目标主机上启动一个任务。
ansible <host-pattern> [-m module_name] [-a args] [options]
示例
警告
由于我们尚未在我们的 2 台测试服务器上配置认证,因此并非所有以下示例都能正常工作。它们是作为示例给出的,以便于理解,并且将在本章的后面完全可用。
- 列出属于 rocky8 组的主机
ansible rocky8 --list-hosts
- 使用
ping
模块 ping 主机组
ansible rocky8 -m ping
- 使用
setup
模块显示主机组的 Facts
ansible rocky8 -m setup
- 通过调用带参数的
command
模块在主机组上运行命令
ansible rocky8 -m command -a 'uptime'
- 以管理员权限运行命令
ansible ansible_clients --become -m command -a 'reboot'
- 使用自定义 Inventory 文件运行命令
ansible rocky8 -i ./local-inventory -m command -a 'date'
注意
正如本例所示,将已管理设备声明分离到多个文件中(例如按云项目)并向 Ansible 提供这些文件的路径,通常比维护一个长 Inventory 文件更简单。
选项 | 信息 |
---|---|
-a 'arguments' |
要传递给模块的参数。 |
-b -K |
请求密码并以更高权限运行命令。 |
--user=username |
使用此用户连接到目标主机,而不是当前用户。 |
--become-user=username |
以此用户身份执行操作(默认:root )。 |
-C |
模拟。不更改目标,而是对其进行测试以查看应该更改的内容。 |
-m module |
运行调用的模块。 |
准备客户端¶
在管理机和客户端上,我们将创建一个专用于 Ansible 执行操作的 ansible
用户。此用户必须具有 sudo 权限,因此必须将其添加到 wheel
组。
此用户将用于
- 在管理站点的方面:运行
ansible
命令并通过 SSH 连接到被管理的客户端。 - 在被管理的站点上(此处作为管理站点的服务器也作为客户端,因此它由自身管理),以执行从管理站点发起的命令:因此它必须具有 sudo 权限。
在两台机器上,创建 ansible
用户,专用于 ansible。
sudo useradd ansible
sudo usermod -aG wheel ansible
为此用户设置密码。
sudo passwd ansible
修改 sudoers 配置,允许 wheel
组的成员无需密码即可 sudo。
sudo visudo
我们的目标是注释掉默认设置,并取消注释 NOPASSWD 选项,以便完成后这些行看起来像这样。
## Allows people in group wheel to run all commands
# %wheel ALL=(ALL) ALL
## Same thing without a password
%wheel ALL=(ALL) NOPASSWD: ALL
警告
如果您在输入 Ansible 命令时收到以下错误消息,则可能意味着您在其中一个客户端上忘记了此步骤:"msg": "Missing sudo password
。
从现在开始管理时,请开始使用此新用户。
sudo su - ansible
使用 ping 模块进行测试¶
默认情况下,Ansible 不允许密码登录。
在 /etc/ansible/ansible.cfg
配置文件中,取消注释 [defaults]
部分中的以下行,并将其设置为 True。
ask_pass = True
在 rocky8 组的每个服务器上运行 ping
。
# ansible rocky8 -m ping
SSH password:
172.16.1.10 | SUCCESS => {
"changed": false,
"ping": "pong"
}
172.16.1.11 | SUCCESS => {
"changed": false,
"ping": "pong"
}
注意
您会被要求输入远程服务器的 ansible
密码,这是一个安全问题……
技巧
如果您遇到此错误 "msg": "to use the 'ssh' connection type with passwords, you must install the sshpass program"
,您只需在管理站安装 sshpass
。
$ sudo dnf install sshpass
摘要
现在您可以测试本章前面不起作用的命令了。
密钥认证¶
密码认证将被更安全的私钥/公钥认证取代。
创建 SSH 密钥¶
双密钥将在管理站上的 ansible
用户使用 ssh-keygen
命令生成。
[ansible]$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/ansible/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/ansible/.ssh/id_rsa.
Your public key has been saved in /home/ansible/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:Oa1d2hYzzdO0e/K10XPad25TA1nrSVRPIuS4fnmKr9g ansible@localhost.localdomain
The key's randomart image is:
+---[RSA 3072]----+
| .o . +|
| o . =.|
| . . + +|
| o . = =.|
| S o = B.o|
| = + = =+|
| . + = o+B|
| o + o *@|
| . Eoo .+B|
+----[SHA256]-----+
公钥可以复制到服务器。
# ssh-copy-id ansible@172.16.1.10
# ssh-copy-id ansible@172.16.1.11
在 /etc/ansible/ansible.cfg
配置文件中的 [defaults]
部分,重新注释掉以下行,以防止密码认证。
#ask_pass = True
私钥认证测试¶
对于下一个测试,使用了允许远程命令执行的 shell
模块。
# ansible rocky8 -m shell -a "uptime"
172.16.1.10 | SUCCESS | rc=0 >>
12:36:18 up 57 min, 1 user, load average: 0.00, 0.00, 0.00
172.16.1.11 | SUCCESS | rc=0 >>
12:37:07 up 57 min, 1 user, load average: 0.00, 0.00, 0.00
不需要密码,私钥/公钥认证有效!
注意
在生产环境中,您现在应该删除之前设置的 ansible
密码,以加强您的安全性(因为现在不需要认证密码)。
使用 Ansible¶
Ansible 可以通过 shell 或 Playbooks 使用。
模块¶
按类别分类的模块列表可以在 这里找到。Ansible 提供了 750 多个模块!
模块现在按模块集合分组,其列表可以在 这里找到。
集合是 Ansible 内容的分发格式,可以包括 Playbooks、Roles、Modules 和 Plugins。
模块使用 ansible
命令的 -m
选项调用。
ansible <host-pattern> [-m module_name] [-a args] [options]
几乎所有需求都有一个模块!因此,建议不要使用 shell 模块,而是寻找适合需求的模块。
每类需求都有其自己的模块。以下是非详尽列表
类型 | 示例 |
---|---|
系统管理 | user (用户管理)、group (组管理)等。 |
软件管理 | dnf 、yum 、apt 、pip 、npm |
文件管理 | copy 、fetch 、lineinfile 、template 、archive |
数据库管理 | mysql 、postgresql 、redis |
云管理 | amazon S3 、cloudstack 、openstack |
集群管理 | consul 、zookeeper |
发送命令 | shell 、script 、expect |
下载 | get_url |
源管理 | git 、gitlab |
软件安装示例¶
dnf
模块允许在目标客户端上安装软件。
# ansible rocky8 --become -m dnf -a name="httpd"
172.16.1.10 | SUCCESS => {
"changed": true,
"msg": "",
"rc": 0,
"results": [
...
\n\nComplete!\n"
]
}
172.16.1.11 | SUCCESS => {
"changed": true,
"msg": "",
"rc": 0,
"results": [
...
\n\nComplete!\n"
]
}
安装的软件是一个服务,现在需要使用 systemd
模块启动它。
# ansible rocky8 --become -m systemd -a "name=httpd state=started"
172.16.1.10 | SUCCESS => {
"changed": true,
"name": "httpd",
"state": "started"
}
172.16.1.11 | SUCCESS => {
"changed": true,
"name": "httpd",
"state": "started"
}
技巧
尝试两次运行最后这两个命令。您会观察到第一次 Ansible 将采取行动以达到命令设定的状态。第二次,它将什么也不做,因为它会检测到状态已达到!
练习¶
为了帮助您更深入地了解 Ansible 并习惯于搜索 Ansible 文档,这里有一些练习,您可以在继续之前进行。
- 创建 Paris、Tokio、NewYork 组。
- 创建
supervisor
用户。 - 更改用户,使其 uid 为 10000。
- 更改用户,使其属于 Paris 组。
- 安装 tree 软件。
- 停止 crond 服务。
- 以
644
权限创建一个空文件。 - 更新您的客户端发行版。
- 重启您的客户端。
警告
不要使用 shell 模块。请在文档中查找合适的模块!
setup
模块:Facts(事实)介绍¶
系统 Facts 是 Ansible 通过其 setup
模块检索到的变量。
查看您客户端的不同 Facts,以了解可以通过简单命令轻松检索到的信息的数量。
我们稍后将看到如何在 Playbooks 中使用 Facts 以及如何创建自己的 Facts。
# ansible ansible_clients -m setup | less
192.168.1.11 | SUCCESS => {
"ansible_facts": {
"ansible_all_ipv4_addresses": [
"192.168.1.11"
],
"ansible_all_ipv6_addresses": [
"2001:861:3dc3:fcf0:a00:27ff:fef7:28be",
"fe80::a00:27ff:fef7:28be"
],
"ansible_apparmor": {
"status": "disabled"
},
"ansible_architecture": "x86_64",
"ansible_bios_date": "12/01/2006",
"ansible_bios_vendor": "innotek GmbH",
"ansible_bios_version": "VirtualBox",
"ansible_board_asset_tag": "NA",
"ansible_board_name": "VirtualBox",
"ansible_board_serial": "NA",
"ansible_board_vendor": "Oracle Corporation",
...
现在我们已经了解了如何在命令行中使用 Ansible 配置远程服务器,我们将能够引入 Playbook 的概念。Playbooks 是另一种使用 Ansible 的方式,它并不复杂多少,但可以更轻松地重用您的代码。
Playbooks(剧本)¶
Ansible 的 Playbooks 描述了应用于远程系统的策略,以强制其配置。Playbooks 以易于理解的文本格式编写,将一组任务组合在一起:yaml
格式。
注意
在此处了解更多关于 yaml 的信息。
ansible-playbook <file.yml> ... [options]
选项与 ansible
命令相同。
命令返回以下错误代码
代码 | 错误 |
---|---|
0 |
OK 或未匹配主机 |
1 |
错误 |
2 |
一个或多个主机失败 |
3 |
一个或多个主机不可达 |
4 |
分析错误 |
5 |
无效或不完整的选项 |
99 |
运行被用户中断 |
250 |
意外错误 |
注意
请注意,当没有主机匹配您的目标时,ansible
将返回 Ok,这可能会误导您!
Apache 和 MySQL Playbook 示例¶
以下 Playbook 允许我们在目标服务器上安装 Apache 和 MariaDB。
创建一个 test.yml
文件,内容如下:
---
- hosts: rocky8 <1>
become: true <2>
become_user: root
tasks:
- name: ensure apache is at the latest version
dnf: name=httpd,php,php-mysqli state=latest
- name: ensure httpd is started
systemd: name=httpd state=started
- name: ensure mariadb is at the latest version
dnf: name=mariadb-server state=latest
- name: ensure mariadb is started
systemd: name=mariadb state=started
...
- <1> 目标组或目标服务器必须存在于 Inventory 中。
- <2> 连接后,用户将变为
root
(默认通过sudo
)。
Playbook 的执行通过 ansible-playbook
命令完成。
$ ansible-playbook test.yml
PLAY [rocky8] ****************************************************************
TASK [setup] ******************************************************************
ok: [172.16.1.10]
ok: [172.16.1.11]
TASK [ensure apache is at the latest version] *********************************
ok: [172.16.1.10]
ok: [172.16.1.11]
TASK [ensure httpd is started] ************************************************
changed: [172.16.1.10]
changed: [172.16.1.11]
TASK [ensure mariadb is at the latest version] **********************************
changed: [172.16.1.10]
changed: [172.16.1.11]
TASK [ensure mariadb is started] ***********************************************
changed: [172.16.1.10]
changed: [172.16.1.11]
PLAY RECAP *********************************************************************
172.16.1.10 : ok=5 changed=3 unreachable=0 failed=0
172.16.1.11 : ok=5 changed=3 unreachable=0 failed=0
为了提高可读性,建议将 Playbooks 编写为完整的 yaml 格式。在前面的示例中,参数与模块在同一行给出,参数值在其名称之后,由 =
分隔。查看同一 Playbook 的完整 yaml 格式。
---
- hosts: rocky8
become: true
become_user: root
tasks:
- name: ensure apache is at the latest version
dnf:
name: httpd,php,php-mysqli
state: latest
- name: ensure httpd is started
systemd:
name: httpd
state: started
- name: ensure mariadb is at the latest version
dnf:
name: mariadb-server
state: latest
- name: ensure mariadb is started
systemd:
name: mariadb
state: started
...
技巧
dnf
是允许将列表作为参数传递的模块之一。
关于集合的说明:Ansible 现在以集合的形式提供模块。一些模块默认在 ansible.builtin
集合中提供,其他模块必须通过以下方式手动安装:
ansible-galaxy collection install [collectionname]
其中 [collectionname] 是集合的名称(这里的方括号用于突出显示需要替换为实际集合名称,而不是命令本身的一部分)。
前面的示例应写成这样。
---
- hosts: rocky8
become: true
become_user: root
tasks:
- name: ensure apache is at the latest version
ansible.builtin.dnf:
name: httpd,php,php-mysqli
state: latest
- name: ensure httpd is started
ansible.builtin.systemd:
name: httpd
state: started
- name: ensure mariadb is at the latest version
ansible.builtin.dnf:
name: mariadb-server
state: latest
- name: ensure mariadb is started
ansible.builtin.systemd:
name: mariadb
state: started
...
一个 Playbook 不仅限于一个目标。
---
- hosts: webservers
become: true
become_user: root
tasks:
- name: ensure apache is at the latest version
ansible.builtin.dnf:
name: httpd,php,php-mysqli
state: latest
- name: ensure httpd is started
ansible.builtin.systemd:
name: httpd
state: started
- hosts: databases
become: true
become_user: root
- name: ensure mariadb is at the latest version
ansible.builtin.dnf:
name: mariadb-server
state: latest
- name: ensure mariadb is started
ansible.builtin.systemd:
name: mariadb
state: started
...
您可以检查 Playbook 的语法。
ansible-playbook --syntax-check play.yml
您还可以使用 yaml 的“linter”。
dnf install -y yamllint
然后检查您的 Playbooks 的 yaml 语法。
$ yamllint test.yml
test.yml
8:1 error syntax error: could not find expected ':' (syntax)
练习结果¶
- 创建 Paris、Tokio、NewYork 组。
- 创建
supervisor
用户。 - 更改用户,使其 uid 为 10000。
- 更改用户,使其属于 Paris 组。
- 安装 tree 软件。
- 停止 crond 服务。
- 以
0644
权限创建一个空文件。 - 更新您的客户端发行版。
- 重启您的客户端。
ansible ansible_clients --become -m group -a "name=Paris"
ansible ansible_clients --become -m group -a "name=Tokio"
ansible ansible_clients --become -m group -a "name=NewYork"
ansible ansible_clients --become -m user -a "name=Supervisor"
ansible ansible_clients --become -m user -a "name=Supervisor uid=10000"
ansible ansible_clients --become -m user -a "name=Supervisor uid=10000 groups=Paris"
ansible ansible_clients --become -m dnf -a "name=tree"
ansible ansible_clients --become -m systemd -a "name=crond state=stopped"
ansible ansible_clients --become -m copy -a "content='' dest=/tmp/test force=no mode=0644"
ansible ansible_clients --become -m dnf -a "name=* state=latest"
ansible ansible_clients --become -m reboot
作者:Antoine Le Morvan
贡献者:Steven Spencer, tianci li, Aditya Putta, Ganna Zhyrnova