Ansible 基础¶
在本章中,您将学习如何使用 Ansible。
**目标**: 在本章中,您将学习如何
实施 Ansible;
在服务器上应用配置更改;
创建第一个 Ansible 剧本;
**ansible**、**模块**、**剧本**。
**知识**:
**复杂度**:
**阅读时间**: 30 分钟
Ansible 集中和自动化管理任务。它是
- **无代理**(它不需要在客户端上进行特定部署),
- **幂等**(每次运行都具有相同的效果)。
它使用 **SSH** 协议远程配置 Linux 客户端,或使用 **WinRM** 协议与 Windows 客户端协作。如果这些协议都不可用,Ansible 始终可以使用 API,这使得 Ansible 成为配置服务器、工作站、Docker 服务、网络设备等的真正瑞士军刀(实际上几乎所有东西)。
警告
从 Ansible 服务器到所有客户端打开 SSH 或 WinRM 流,使其成为架构中的关键元素,必须对其进行仔细监控。
由于 Ansible 主要基于推送,因此它不会在每次执行之间保留其目标服务器的状态。相反,它将在每次执行时执行新的状态检查。它被认为是无状态的。
它将帮助您进行
- 供应(部署新 VM),
- 应用程序部署,
- 配置管理,
- 自动化,
- 编排(当使用多个目标时)。
注意
Ansible 最初由 Michael DeHaan 编写,他是其他工具(如 Cobbler)的创始人。
最早的第一个版本是 0.0.1,于 2012 年 3 月 9 日发布。
2015 年 10 月 17 日,AnsibleWorks(Ansible 背后的公司)被 Red Hat 以 1.5 亿美元收购。
为了在您日常使用 Ansible 时提供图形界面,您可以安装一些工具,如 Ansible Tower(RedHat),它不是免费的,它的开源对应物 Awx,或者其他项目,如 Jenkins 和优秀的 Rundeck 也可以使用。
摘要
要进行本培训,您至少需要 2 台 Rocky8 服务器
- 第一台将是**管理机器**,Ansible 将安装在其上。
- 第二台将是需要配置和管理的服务器(除了 Rocky Linux 之外的其他 Linux 也一样)。
在下面的示例中,管理站的 IP 地址为 172.16.1.10,被管理站为 172.16.1.11。您可以根据自己的 IP 地址方案调整示例。
Ansible 词汇表¶
- **管理机器**: 安装 Ansible 的机器。由于 Ansible 是**无代理的**,因此不会在被管理的服务器上部署任何软件。
- **被管理节点**: Ansible 管理的目标设备也称为“主机”。这些可以是服务器、网络设备或任何其他计算机。
- **清单**: 包含有关被管理服务器的信息的文件。
- **任务**: 任务是一个定义要执行的过程的块(例如,创建用户或组、安装软件包等)。
- **模块**: 模块抽象了任务。Ansible 提供了许多模块。
- **剧本**: 一个用 yaml 格式编写的简单文件,定义目标服务器和要执行的任务。
- **角色**: 角色允许您组织剧本和其他所有必要文件(模板、脚本等),以便于共享和重用代码。
- **集合**: 集合包含剧本、角色、模块和插件的逻辑集合。
- **事实**: 这些是包含有关系统信息(机器名称、系统版本、网络接口和配置等)的全局变量。
- 处理程序:这些用于在发生更改时导致服务停止或重新启动。
在管理服务器上安装¶
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
,则加载该文件。
如果没有找到这三个文件中的任何一个,则加载默认文件。
清单文件 /etc/ansible/hosts
¶
由于 Ansible 将必须处理所有要配置的设备,因此为其提供一个(或多个)与您的组织完美匹配的结构良好的清单文件至关重要。
有时需要仔细考虑如何构建此文件。
转到默认清单文件,该文件位于 /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 格式非常适合我们未来的示例。
清单可以在生产中自动生成,尤其是在您拥有 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
我们不会深入探讨清单,但如果您有兴趣,请查看 此链接。
现在我们的管理服务器已经安装好,我们的清单也准备好了,是时候运行我们的第一个 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
模块从主机组显示事实
ansible rocky8 -m setup
- 通过使用带参数的
command
模块,在主机组上运行命令
ansible rocky8 -m command -a 'uptime'
- 以管理员权限运行命令
ansible ansible_clients --become -m command -a 'reboot'
- 使用自定义清单文件运行命令
ansible rocky8 -i ./local-inventory -m command -a 'date'
注意
与本例一样,有时将受管理设备的声明分成多个文件(例如,按云项目划分)并向 Ansible 提供这些文件的路径,比维护一个很长的清单文件更简单。
选项 | 信息 |
---|---|
-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 密钥¶
双密钥将使用 ssh-keygen
命令在管理站上的 ansible
用户生成
[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 或通过 playbook 使用。
模块¶
按类别分类的模块列表可以 在此处找到。Ansible 提供了超过 750 个模块!
模块现在分组到模块集合中,可以 在此处找到 集合列表。
集合是 Ansible 内容的一种分发格式,可以包含 playbook、角色、模块和插件。
使用 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
模块:事实介绍¶
系统事实是 Ansible 通过其 setup
模块检索的变量。
查看你的客户端的不同事实,以了解通过简单命令可以轻松检索的的信息量。
我们将在后面看到如何在我们的剧本中使用事实以及如何创建我们自己的事实。
# 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 配置远程服务器,我们将能够介绍剧本的概念。剧本是使用 Ansible 的另一种方式,它并不比命令行复杂多少,但它将使你更容易重用你的代码。
剧本¶
Ansible 的剧本描述了要应用于远程系统的策略,以强制执行其配置。剧本以易于理解的文本格式编写,将一组任务组合在一起:yaml
格式。
注意
了解更多关于 yaml 的信息
ansible-playbook <file.yml> ... [options]
选项与 ansible
命令相同。
命令返回以下错误代码
代码 | 错误 |
---|---|
0 | OK 或没有匹配的主机 |
1 | 错误 |
2 | 一个或多个主机正在失败 |
3 | 一个或多个主机无法访问 |
4 | 分析错误 |
5 | 选项错误或不完整 |
99 | 用户中断运行 |
250 | 意外错误 |
注意
请注意,当没有主机与你的目标匹配时,ansible
将返回 Ok,这可能会误导你!
Apache 和 MySQL 剧本示例¶
以下剧本允许我们在目标服务器上安装 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> 目标组或目标服务器必须存在于清单中
- <2> 连接后,用户将成为
root
(默认情况下通过sudo
)
剧本的执行是使用命令 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
为了更易读,建议以完整的 yaml 格式编写你的剧本。在前面的示例中,参数在与模块相同的行上给出,参数的值在其名称之后使用 =
分隔。查看以完整 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
...
剧本不限于一个目标
---
- 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
...
你可以检查你的剧本的语法
ansible-playbook --syntax-check play.yml
你也可以使用 yaml 的“linter”
dnf install -y yamllint
然后检查你的剧本的 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