Ansible 自带了模块搜索、列表、详情查询的命令,靠关键词就能快速定位
| 场景 | 命令示例 | 优点 |
|---|---|---|
| 记得关键词(key/ssh) | `ansible-doc -l | grep -i key` |
| 完全忘关键词 | `ansible-doc -l | less` |
| 找到后验证用法 | ansible-doc authorized_key |
看参数 / 示例,避免用错 |
查看指定模块的完整帮助(比如file模块) ansible-doc file # 只看模块的参数说明(精简版) ansible-doc -s file
帮助手册的组成
Ansible 所有模块的帮助手册(
ansible-doc 模块名查看)都遵循统一结构,按优先级拆解如下:1. 基础信息区(手册顶部)
模块名称 + 简介 :比如
file -- Manage files and file properties(模块名 + 核心用途);官方分类 :比如
Category: files(属于文件管理类模块);版本信息 :比如
Version added: historical(模块首次加入的版本)。2. 参数说明区(核心部分,占比 90%)
这是最需要重点看的部分,每个参数的说明都包含以下核心字段(按需出现):
字段名 作用 常见值 / 示例 参数行(=/-)标识参数是否必选: = 参数名→ 必选;- 参数名→ 可选= path(必选)、- recurse(可选)描述文本 一句话说明参数的核心作用 Path to the file being managed.aliases参数别名(简化记忆,多个别名等效) [dest, name]type参数值的类型(传值必须匹配,否则报错) path/bool/str/list/intdefault可选参数的默认值(省略该参数时的默认行为) false/""/[]required显式标识是否必选(和 =/−等效,部分手册会补充)true/falsechoices参数的可选值(只能传这些值,否则报错) [file, directory, link, absent](file 模块 state 参数)added in参数新增的版本(低版本 Ansible 不支持该参数) version 1.1 of ansible-core适用条件文本 参数生效的前置条件(比如依赖其他参数的取值) This applies only whenstate' is set todirectory'.notes参数的注意事项(比如 "该参数仅对 Linux 生效,Windows 不支持") - 3. 示例区(EXAMPLES)
手册末尾会有
EXAMPLES板块,提供该模块的高频实操示例(比如 file 模块的创建目录、修改权限、创建软链等),是新手最易上手的部分。4. 返回值区(RETURN)
说明模块执行后会返回哪些数据(比如 file 模块会返回
path(路径)、state(状态)、mode(权限)等),适合编写 Playbook 时获取模块执行结果。
怎么看帮助手册
查看 ansible-doc 输出的核心逻辑是:先明确模块能做什么 → 再看必选参数(=)→ 再看可选参数的默认值/用法约束 → 最后参考示例验证。
核心查看逻辑(按优先级)
① 先看 "必选参数" :所有
=开头的参数必须传,先确认这些参数的作用和类型(比如 file 模块的path是必选,必须指定要管理的路径);② 再看 "可选参数" 的默认值 :比如
recurse默认false,如果需要递归管理目录,必须手动设为true;③ 看参数的适用条件 :比如
recurse仅对state=directory生效,若管理的是文件(state=file),传recurse=true也无效;④ 看参数别名 :比如 file 模块的
path可以用dest替代,Playbook 里写dest=/tmp/test更符合直觉;⑤ 参考示例区 :如果不知道参数怎么组合使用,直接抄示例改路径 / 值即可(比如创建递归目录的示例:
- name: Create directory Recursively file: path=/tmp/testdir state=directory recurse=yes)。打开
ansible-doc file后,进入交互模式,可通过以下快捷键高效浏览:
/参数名:搜索指定参数(比如输入/recurse快速定位到该参数);上下箭头:逐行滚动;
q:退出帮助手册。
命令行调用模块
**ansible [-a MODULE_ARGS] [-m MODULE_NAME] [-i INVENTORY]
ansible <主机组/单个IP> -m <模块> -a <模块参数>
Ansible 常用命令行选项说明
--version # 显示版本
-m module # 指定模块,默认为command
-v # 显示详细过程,-vv/-vvv可显示更详细内容
--list-hosts # 显示主机列表,可简写为--list
-k, --ask-pass # 提示输入SSH连接密码(默认使用密钥验证)
-u, --user=REMOTE_USER # 指定远程执行任务的用户,默认用户为root
-b, --become # 替代旧版的sudo切换(提权执行)
--become-user=USERNAME # 指定sudo的runas用户,默认用户为root
-K, --ask-become-pass # 提示输入sudo操作的口令
**

#先在受控节点sudo授权
[root@node1 ~]# vim /etc/sudoers
hehe ALL=(ALL) NOPASSWD: ALL
[root@node1 ~]# echo redhat | passwd hehe --stdin
更改用户 hehe 的密码 。
passwd:所有的身份验证令牌已经成功更新。
#以hehe用户连接,并利用sudo使用hehe用户提权,执行cat /etc/shadow
[root@rhce ~]# ansible 192.168.223.151 -m shell -a 'cat /etc/shadow' -u hehe -k -b
SSH password:
192.168.223.151 | CHANGED | rc=0 >>
root:$6$M0deCMG.nnloaq5p$4K6bB7qtDosTRKwKwP.uC5wFq5TAqmsEajYN6pE2s06X11zhPJTlwmSi0.fQPXdfei6YvcVNTWYjgTAFe6HDj.::0:99999:7:::
bin:*:19347:0:99999:7:::
daemon:*:19347:0:99999:7:::
[root@node1 ~]# vim /etc/sudoers
hehe ALL=(ALL) ALL
[root@node1 ~]# echo redhat | passwd hehe --stdin
更改用户 hehe 的密码 。
passwd:所有的身份验证令牌已经成功更新。
[root@rhce ~]# ansible 192.168.223.151 -m shell -a 'cat /etc/shadow' -u hehe -k -b -K
SSH password:
BECOME password[defaults to SSH password]:
192.168.223.151 | CHANGED | rc=0 >>
root:$6$M0deCMG.nnloaq5p$4K6bB7qtDosTRKwKwP.uC5wFq5TAqmsEajYN6pE2s06X11zhPJTlwmSi0.fQPXdfei6YvcVNTWYjgTAFe6HDj.::0:99999:7:::
ansible指令执行过程
控制节点解析命令 → 读主机清单 → 建并发进程 → SSH 连目标主机 → 传模块脚本 → 提权 / 检查后执行 → 收集结果 + 清理临时文件 → 输出最终结果


| 简化流程步骤 | 具体操作 | 对应完整执行流程的环节 | 关键实操补充(避坑 / 调试) |
|---|---|---|---|
| 步骤 1:加载配置文件 | 读取 Ansible 全局配置(如/etc/ansible/ansible.cfg)、模块默认参数、用户自定义配置 |
完整流程「命令解析与参数校验」→ 加载 Ansible 全局配置子环节 | ① 配置包含 SSH 端口、临时目录、默认模块等核心参数;② 若自定义ansible.cfg,优先级高于全局配置;③ 校验配置合法性(如临时目录是否可写)。 |
| 步骤 2:加载模块文件 | 从 Ansible 内置模块目录(如/usr/lib/python3.9/site-packages/ansible/modules/)读取指定模块(如ping/file)的核心逻辑 |
完整流程「模块传输与临时文件生成」→ 模块准备子环节 | ① 模块多为 Python 脚本(command/shell为文本逻辑);② 校验模块参数(如file模块必传path,缺省则报错);③ 自定义模块需放在~/.ansible/modules/,优先加载。 |
| 步骤 3:生成临时 py 文件并传输 | 控制节点将模块脚本打包为临时 Python 文件(命名规则:ansible-tmp-xxxx/xxx.py),通过 SSH 传输到远程主机临时目录(默认~/.ansible/tmp/) |
完整流程「模块传输与临时文件生成」核心环节 | ① 临时文件命名含随机字符串,避免冲突;② 可通过ansible.cfg的remote_tmp自定义远程临时目录;③ command/shell模块无需生成 py 文件,直接传输命令文本。 |
| 步骤 4:给文件添加执行权限 | 远程主机对临时文件执行chmod +x,确保脚本可运行 |
完整流程「模块执行」→ 执行前准备子环节 | ① 仅给临时文件加执行权限,不修改系统文件;② 若远程主机无 Python(极少),自动适配为 Shell 脚本执行;③ 权限仅在执行阶段有效,执行后立即清理。 |
| 步骤 5:执行并返回结果 | 远程主机运行临时脚本,执行模块核心逻辑(如ping测连通、file创建目录),将结果(状态 / 返回码 / 输出)通过 SSH 传回控制节点 |
完整流程「模块执行」核心环节 | ① 结果状态分 3 类:changed(有变更)/ok(无变更)/failed(执行失败);② 提权(-b)、检查模式(-C)在此环节生效;③ -v/-vv可查看执行的具体命令和返回日志。 |
| 步骤 6:删除临时文件 | 控制节点确认结果接收后,触发远程主机删除~/.ansible/tmp/下的临时脚本 |
完整流程「结果收集与清理」核心环节 | ① 执行失败(如超时)可能残留临时文件,需手动清理:ansible 主机组 -m file -a 'path=~/.ansible/tmp/ state=absent' -b;② 调试模块时可禁用清理:ansible.cfg中设置keep_remote_files=yes;③ 清理操作不影响远程主机系统文件,仅删 Ansible 临时文件。 |
模块的历史发展
版本划分依据:
-
早期版本:通常指 Ansible 1.x ~ 2.0 版本(发布时间 2012-2016 年),功能基础、模块数量少,还未引入 Collection 体系。
-
新版本 :Ansible 2.10 + 版本(2020 年后)开始引入 Collection(集合)体系,后续版本号规则调整为 "Ansible 包版本"(比如你的 7.7.0),对应的核心引擎是
ansible-core 2.14(2023 年左右发布的版本),属于近年的主流新版本,支持 Collection、更多模块和企业级功能。、说明:之前用到的
ansible.posix.authorized_key模块,其实就是调用了ansible.posix这个 Collection 里的authorized_key模块 ------ 这就是 Collection 体系下的模块调用方式(命名空间.集合名.模块名)。Collection 出现前的问题(为什么需要它):
早期 Ansible(2.10 之前)的模块是 "零散存放" 的:
-
官方模块直接放在核心代码里,第三方模块(比如某厂商的设备管理模块)只能手动下载、放在指定目录,容易丢、难更新;
-
不同模块的版本无法单独管理(比如想升级 "Docker 相关模块",只能升级整个 Ansible);
-
模块命名容易冲突(比如不同厂商都有叫
server的模块)。
Collection 到底是什么?
Collection 是一个 "资源包",每个 Collection 里会打包对应场景下的所有相关资源
每个 Collection 都有唯一的 "身份标识":
命名空间.集合名(比如ansible.posix、community.docker):-
命名空间 :区分 "归属方"(比如
ansible是官方、community是社区、cisco是厂商); -
集合名 :区分 "功能场景"(比如
posix是 POSIX 系统工具、docker是 Docker 管理工具)
比如:
-
ansible.posix:官方维护的 "POSIX 系统(Linux/Unix)工具集合",包含你用过的authorized_key模块; -
community.docker:社区维护的 "Docker 管理工具集合",包含docker_container(管理 Docker 容器)等模块; -
cisco.aci:Cisco 厂商维护的 "ACI 网络设备管理集合",包含 Cisco 设备的配置模块。
历史扩展:
UNIX 是 1969 年由贝尔实验室的肯・汤普森、丹尼斯・里奇发的一个多用户、多任务分时操作系统(不是'一台',是软件系统)------ 最初是为小型机开发的,后来因为它能同时支持多个用户、高效处理多任务,在学术界和企业界迅速流行(比如大学、科研机构用它做开发 / 计算)。
但后来 UNIX 的版权被 AT&T(贝尔实验室的母公司)收回并闭源,同时开始对商用授权收费。此时有两类人群分别推动了'类 UNIX 系统'的诞生:
-
学术界(比如加州大学伯克利分校):基于早期 UNIX 的开源源码分支,开发出了BSD 系统(伯克利软件分发),这是最早的类 UNIX 系统之一,后来逐渐脱离 UNIX 版权约束,成为独立的开源类 UNIX 系统;
-
普通用户 / 开发者:因为 UNIX 闭源收费,同时当时个人电脑没有好用的多任务系统,1991 年林纳斯・托瓦兹(大学生)基于'教学用类 UNIX 系统 Minix'的启发,开发出了Linux 内核------ 后来社区把 Linux 内核和各种开源工具(比如 GNU 的命令行工具)组合起来,形成了各种 Linux 发行版(比如 Ubuntu、SUSE、RHEL、CentOS)。
这些'功能、用法和 UNIX 高度相似,但不是官方 UNIX'的系统,就被统称为类 UNIX 系统------ 它不止包含 Linux 系(Ubuntu、SUSE 等都是 Linux 的发行版),还包括 BSD 系(比如 FreeBSD)、商业类 UNIX(比如 Solaris)等。
为了解决这个问题,IEEE(电气和电子工程师协会)制定了POSIX 标准(全称'可移植操作系统接口')------ 它是一套'操作系统接口的统一标准',规定了类 UNIX 系统必须遵守的接口规则(比如系统调用、命令格式、权限逻辑)。
只要类 UNIX 系统符合 POSIX 标准,开发者写的软件(比如 Ansible),不用修改就能直接在所有符合 POSIX 的类 UNIX 系统上运行 ------ 这也是 Ansible 能'用同一个模块管理 Ubuntu 和 RHEL'的核心原因(这两个系统都符合 POSIX)。"
-
2015年底270多个模块,2016年达到540个,2018年01月12日有1378个模块,2018年07月15日1852个模块,2019年05月25日(ansible 2.7.10)时2080个模块,2020年03月02日有3387个模块
早期模块以基础系统运维功能为主,分类简洁:
-
文件与权限管理
- 模块:
file(创建文件 / 目录、修改权限 / 属主)、copy(复制文件)、unarchive(解压)
- 模块:
-
用户与组管理
- 模块:
user(创建 / 修改用户)、group(创建 / 修改用户组)、authorized_key(配置 SSH 密钥)
- 模块:
-
软件包管理
- 模块:
yum(RHEL/CentOS)、apt(Debian/Ubuntu)、pip(Python 包)
- 模块:
-
系统服务管理
- 模块:
service(管理 sysvinit/systemd 服务)
- 模块:
-
命令与脚本执行
- 模块:
command(简单命令)、shell(支持 Shell 语法)、script(执行本地脚本)
- 模块:
-
网络与安全管理
- 模块:
iptables(传统防火墙)、nmcli(网络接口配置)
- 模块:
-
磁盘与存储管理
- 模块:
mount(挂载文件系统)、parted(磁盘分区)
- 模块:
-
配置与定时任务管理
- 模块:
lineinfile(修改单行配置)、cron(系统定时任务)
- 模块:
当前 Ansible 模块覆盖全运维场景,包含核心内置模块 + 第三方 Collections 模块,分类如下:
-
文件与权限管理
- 核心模块:
file(文件 / 目录、权限 / 属主)、copy(复制文件 / 内容)、template(模板渲染)、unarchive(解压)、get_url(下载文件)、synchronize(rsync 同步)
- 核心模块:
-
用户与身份管理
- 核心模块:
user(用户创建 / 修改)、group(用户组)、authorized_key(SSH 密钥)、seuser(SELinux 用户)
- 核心模块:
-
软件包管理
-
系统包:
yum/dnf(RHEL/CentOS 8+)、apt(Debian/Ubuntu)、package(自动适配系统包管理器) -
语言包:
pip(Python)、gem(Ruby)、npm(Node.js)、go(Go)
-
-
系统服务与进程管理
-
服务管理:
systemd(systemd 服务)、service(兼容 sysvinit)、supervisorctl(Supervisor 进程) -
进程操作:
ps(查询进程)、kill(终止进程)
-
-
命令与脚本执行
- 模块:
command(简单命令)、shell(支持 Shell 语法)、script(执行本地脚本)、raw(无 Python 依赖的命令)
- 模块:
-
网络与安全管理
-
网络配置:
nmcli(网络接口)、route(路由)、dnsmasq(DNS 服务) -
安全加固:
firewalld(动态防火墙)、ufw(Ubuntu 防火墙)、selinux(SELinux 状态)、auditd(审计规则)
-
-
磁盘与存储管理
- 模块:
mount(挂载)、parted(磁盘分区)、lvg(逻辑卷组)、lvol(逻辑卷)、filesystem(创建文件系统)
- 模块:
-
配置文件管理
- 模块:
lineinfile(修改单行配置)、replace(文本替换)、ini_file(INI 文件)、xml(XML 文件)、json_file(JSON 文件)
- 模块:
-
数据存储管理
-
数据库:
mysql_db(MySQL/MariaDB)、postgresql_db(PostgreSQL)、mongodb(MongoDB)、redis(Redis) -
存储服务:
nfs_server(NFS 服务)、gluster_volume(GlusterFS)
-
-
定时任务管理
- 模块:
cron(系统定时任务)、at(一次性任务)
- 模块:
-
云资源管理(Collections)
-
公有云:
amazon.aws.ec2_instance(AWS EC2)、azure.azcollection.azure_vm(Azure VM)、aliyun.alicloud.aliyun_ecs(阿里云 ECS) -
私有云:
openstack.cloud.server(OpenStack)、vmware.vmware_rest.vcenter_vm(VMware)
-
-
容器与虚拟化管理
-
容器:
community.docker.docker_container(Docker)、containers.podman.podman_container(Podman)、kubernetes.core.k8s(Kubernetes) -
虚拟化:
libvirt.libvirt.vm(KVM)、vmware.vmware_rest.vcenter_vm(VMware)
-
-
源码与版本控制
- 模块:
git(Git 仓库)、svn(SVN)、hg(Mercurial)
- 模块:
-
工具与辅助模块
- 模块:
wait_for(等待端口 / 文件)、debug(调试输出)、pause(暂停任务)、assert(断言检查)、set_fact(设置变量)
- 模块:
-
消息与通知
- 模块:
slack(Slack 通知)、email(邮件)、telegram(Telegram)、wechat_work(企业微信)
- 模块:
command 模块
-
参数格式与执行范围 :
command模块接收「命令名 + 空格分隔的参数列表」作为输入,指定的命令会在所有选中的远程节点上执行。 -
核心限制(无 Shell 解析):该模块执行的命令
不会经过远程节点的 Shell 解释器处理,因此以下内容均无法生效:
-
环境变量(如
$HOSTNAME); -
Shell 特殊操作符(如
"*"通配符、</>重定向、|管道、;/&多命令拼接)。
-
-
功能替代方案 :若需使用上述 Shell 特性,应替换为
[ansible.builtin.shell]模块。 -
优化任务可读性:对于使用空格分隔参数导致可读性差的
command任务,可通过两种方式传参:-
使用
args任务关键字; -
使用
cmd参数(推荐)。
-
-
必选参数要求 :必须指定「自由格式命令」或
cmd参数二者其一(具体用法见示例)。
说明:如果能用对应模块解决问题的,尽量就不要用command或者shell,因为对应的模块会更安全,且模块会具有幂等性特点
========== 示例:command模块用法 ==========
- name: 将motd文件内容保存到已注册的变量中
ansible.builtin.command: cat /etc/motd
register: mymotd # 注册任务结果到mymotd变量
自由格式(字符串)参数,所有参数写在同一行
- name: 仅当/path/to/database文件不存在时执行命令(不使用'args'参数)
ansible.builtin.command: /usr/bin/make_database.sh db_user db_name creates=/path/to/database
自由格式(字符串)参数,部分参数通过'args'关键字分行书写
'args'是任务关键字,需与模块写在同一层级
- name: 仅当/path/to/database不存在时执行命令(使用'args'关键字)
ansible.builtin.command: /usr/bin/make_database.sh db_user db_name
args:
creates: /path/to/database # 幂等参数:文件不存在则执行命令
'cmd'是模块参数(推荐的规范写法)
- name: 仅当/path/to/database不存在时执行命令(使用'cmd'参数)
ansible.builtin.command:
cmd: /usr/bin/make_database.sh db_user db_name
creates: /path/to/database
- name: 切换工作目录到somedir/并以db_owner用户身份执行命令
ansible.builtin.command: /usr/bin/make_database.sh db_user db_name
become: yes # 开启提权
become_user: db_owner # 切换到db_owner用户执行
args:
chdir: somedir/ # 执行命令前切换工作目录
creates: /path/to/database
argv(列表)参数,每个参数分行书写,无需使用'args'关键字
'argv'是模块参数,需相对于模块名缩进一层
- name: 使用'argv'以列表形式传递命令------将'command'字段留空
ansible.builtin.command:
argv:
-
/usr/bin/make_database.sh
-
Username with whitespace # 含空格的用户名(argv自动处理空格问题)
-
dbname with whitespace # 含空格的数据库名
creates: /path/to/database
实例:
[root@ansible ~]#ansible websrvs -m command -a 'chdir=/etc cat centos-release'
10.0.0.7 | CHANGED | rc=0 >>
CentOS Linux release 7.7.1908 (Core)
10.0.0.8 | CHANGED | rc=0 >>
CentOS Linux release 8.1.1911 (Core)
[root@ansible ~]#ansible websrvs -m command -a 'chdir=/etc creates=/data/f1.txt
cat centos-release'
10.0.0.7 | CHANGED | rc=0 >>
CentOS Linux release 7.7.1908 (Core)
10.0.0.8 | SUCCESS | rc=0 >>
skipped, since /data/f1.txt exists
[root@rhce ~]# ansible newserver -m command -a 'creates=/data/hehe mkdir /data ' -i /hosts
192.168.223.152 | CHANGED | rc=0 >>
192.168.223.151 | CHANGED | rc=0 >>
[root@rhce ~]# ansible newserver -m command -a 'creates=/data/hehe mkdir /data ' -i /hosts
192.168.223.151 | FAILED | rc=1 >>
mkdir: 无法创建目录 "/data": 文件已存在non-zero return code
192.168.223.152 | FAILED | rc=1 >>
mkdir: 无法创建目录 "/data": 文件已存在non-zero return code
[root@rhce ~]#
[root@rhce ~]# ansible newserver -m command -a 'creates=/data/ mkdir /data ' -i /hosts
192.168.223.152 | SUCCESS | rc=0 >>
skipped, since /data/ existsDid not run command since '/data/' exists
192.168.223.151 | SUCCESS | rc=0 >>
skipped, since /data/ existsDid not run command since '/data/' exists
[root@ansible ~]#ansible websrvs -m command -a 'chdir=/etc removes=/data/f1.txt
cat centos-release'
10.0.0.7 | SUCCESS | rc=0 >>
skipped, since /data/f1.txt does not exist
10.0.0.8 | CHANGED | rc=0 >>
CentOS Linux release 8.1.1911 (Core)
习题 1:基础用法 + chdir 辅助参数(切换目录执行命令)
-
要求 :在
newserver主机上,切换到/etc目录后读取redhat-release文件内容(指定 inventory 文件/hosts,提权执行)。 -
正确命令
ansible newserver -m command -a 'chdir=/etc cat redhat-release' -i /hosts -b
-
核心知识点
chdir是command模块的辅助参数,作用是「执行命令前切换到指定目录」,属于高频运维场景(避免写绝对路径)。
在 Ansible 中,模块参数和 指令(命令内容)是完全不同的东西:
-
模块参数:是 Ansible 模块(比如这里的
command模块)的 "配置项",由 Ansible 本身解析、处理,作用是控制模块的执行逻辑(比如执行指令前切换目录、判断文件是否存在等)。比如
chdir=/etc是command模块的专属参数,功能是 "在执行指令前,先切换到/etc目录"。 -
指令内容 :是模块要在目标主机上执行的 "实际命令",由目标主机的系统(比如 Linux)解析、执行,作用是完成具体操作(比如这里的
cat redhat-release是读取文件内容)。 -
注意点:
chdir仅改变命令执行的目录,不能单独使用(必须搭配具体命令,否则报no command given错误)。
扩展1:Ansible 会先解析这模块参数

提问:为什么'cat redhat-release chdir=/etc'这个反这些也能够看到/etc目录下的redhat-realse
因为
chdir=/etc是command模块的参数,而非命令的一部分 ------Ansible 会先解析这个参数,在执行cat redhat-release命令前,自动切换到/etc目录 ,所以此时cat读取的是/etc目录下的redhat-release文件
扩展2:执行多个指令,用&&分隔
如果指令连着写,会被当做前一个指令的参数

如果两个指令由chdir参数分隔开,仅会执行前面的指令,也没有完成目的:

如果要执行多个指令就用&&分隔:
[root@rhce ~]# ansible newserver -m shell -a 'chdir=/etc touch /root/hehel ; ls /root ; cat redhat-release' -i /hosts
[root@rhce ~]# ansible newserver -m shell -a 'chdir=/etc touch /root/hehel && ls /root && cat redhat-release' -i /hosts
192.168.223.151 | CHANGED | rc=0 >>
anaconda-ks.cfg
epel-release-latest-9.noarch.rpm
hehe
hehel
Red Hat Enterprise Linux release 9.3 (Plow)
192.168.223.152 | CHANGED | rc=0 >>
anaconda-ks.cfg
epel-release-latest-9.noarch.rpm
hehe
hehel
Red Hat Enterprise Linux release 9.3 (Plow)
[root@rhce ~]# cat banben.yml
---
- hosts: newserver
tasks:
- name: 执行touch/ls/cat命令(chdir=/etc)
ansible.builtin.shell:
cmd: touch /root/hehel && ls /root && cat redhat-release
chdir: /etc
习题 2:验证「不支持 Shell 特殊语法」(管道 /&&/ 重定向)
-
要求 :尝试用
command模块执行cat /etc/passwd | grep root(过滤 root 用户),观察结果并分析。 -
错误命令
#command 不支持管道 `、重定向`、`$VARNAME`、`;`、`&` 等 Shell 语法
ansible websrvs -m command -a 'service vsftpd start'
ansible websrvs -m command -a 'echo magedu |passwd --stdin wang'
ansible websrvs -m command -a 'rm -rf /data/'
ansible websrvs -m command -a 'echo hello > /data/hello.log'
ansible websrvs -m command -a "echo $HOSTNAME"
-
结果 :执行失败,提示
No such file or directory(系统把|和grep root当成文件名)。 -
核心知识点
command模块直接调用系统命令,不经过 Shell 解析,因此不支持管道、&&、重定向(>/>>)等 Shell 特殊语法。 -
注意点 需用
shell模块替代command实现此类需求。
习题 3:creates 参数实现幂等性(文件存在则跳过执行)
-
要求 :仅当
/tmp/test.txt不存在时,在newserver主机上执行touch /tmp/test.txt(避免重复创建文件)。 -
正确命令
ansible newserver -m command -a 'creates=/test.txt touch /test.txt' -i /hosts
核心知识点
-
creates是高频幂等性参数:指定文件若存在,则跳过命令执行(Ansible 核心特性,避免重复操作)。 -
注意点
对应还有
removes参数(指定文件若存在则执行命令,常用于清理文件)。
shell 模块
-
参数格式要求 :
shell模块接收 "命令名 + 空格分隔的参数列表" 作为输入,必须指定「自由格式命令」或cmd参数(示例见前文),二者任选其一。 -
与
command模块的核心差异 :该模块与[ansible.builtin.command]模块功能几乎完全一致,唯一区别是shell模块会通过远程节点的/bin/shshell 解释器执行命令(支持管道、变量替换等 shell 语法)。
========== 示例:shell模块用法 ==========
- name: 在远程shell中执行命令;将标准输出重定向到指定文件
ansible.builtin.shell: somescript.sh >> somelog.txt
- name: 执行命令前将工作目录切换到somedir/
ansible.builtin.shell: somescript.sh >> somelog.txt
args:
chdir: somedir/
也可以使用'args'格式来传递这些选项。
- name: 该命令会先切换工作目录到somedir/,且仅在somelog.txt不存在时执行
ansible.builtin.shell: somescript.sh >> somelog.txt
args:
chdir: somedir/
creates: somelog.txt
也可以使用'cmd'参数替代自由格式写法。
- name: 该命令会将工作目录切换到somedir/
ansible.builtin.shell:
cmd: ls -l | grep log
chdir: somedir/
习题 1:执行管道 && 组合命令
-
要求 :在
newserver主机上,过滤/etc/passwd中 root 用户,并将结果写入/tmp/root_user.txt。 -
正确命令
ansible newserver -m shell -a 'cat /etc/passwd | grep root > /tmp/root_user.txt' -i /hosts
-
核心知识点
shell模块通过/bin/sh(可指定executable=/bin/bash)解析命令,支持管道、重定向、&&等所有 Shell 语法。 -
注意点
-
注意:调用bash执行命令 类似 cat /tmp/test.md | awk -F'|' '{print 1,2}' &> /tmp/example.txt 这些复杂命令,即使使用shell也可能会失败,解决办法:写到脚本时,copy到远程,执行,再把需要的结果
-
重定向写文件时,需确保目标目录有写入权限(必要时加
-b提权)。 -
拉回执行命令的机器
-
-


范例:将shell模块代替command,设为模块
[root@ansible ~]#vim /etc/ansible/ansible.cfg
#修改下面一行
module_name = shell
习题2:环境变量
[root@rhce ~]# ansible newserver -m command -a 'echo $HOSTNAME' -i /hosts -b
192.168.223.151 | CHANGED | rc=0 >>
$HOSTNAME
192.168.223.152 | CHANGED | rc=0 >>
$HOSTNAME
[root@rhce ~]# ansible newserver -m shell -a 'echo $HOSTNAME' -i /hosts -b
192.168.223.152 | CHANGED | rc=0 >>
node2
192.168.223.151 | CHANGED | rc=0 >>
node1
| 维度 | command 模块 |
shell 模块 |
|---|---|---|
| 执行机制 | 直接调用远程主机的系统命令(如 echo),不经过 Shell(bash/sh)解析 |
先调用远程主机的 Shell(默认 /bin/sh/bash),由 Shell 预处理命令后,再执行系统命令 |
| 环境变量解析 | 不解析任何 Shell 环境变量(如 $HOSTNAME),$HOSTNAME 被当作普通字符串传给 echo,因此原样输出 $HOSTNAME |
Shell 先将 $HOSTNAME 解析为远程主机的实际主机名(node1/node2),再传给 echo,因此输出真实主机名 |
| Shell 特性支持 | 不支持任何 Shell 解析特性(管道、重定向、&&/; 分隔符、流程控制等) |
支持所有 Shell 解析特性(环境变量、管道、重定向、循环 / 条件判断等) |
| 适用场景 | 仅执行 "纯系统命令 + 命令自身参数"(无 Shell 特性),更安全、轻量 | 执行需要 Shell 预处理的复杂命令(含变量、管道、逻辑分隔符) |
可能出现的 "特殊情况"(command 看似解析了变量)
正常情况下
command不解析变量,但以下场景会让command模块 "看似解析了环境变量",容易混淆:1. 远程主机的 "父 Shell 进程" 提前解析了变量
Ansible 通过 SSH 连接远程主机时,SSH 会启动一个 "父 Shell 进程";若远程主机的系统级配置(如
/etc/bashrc//etc/profile)加载了环境变量,且父 Shell 进程强制解析了命令中的变量,command模块的命令会被父 Shell 提前替换变量。
示例:若把
export HOSTNAME=test写入远程主机/etc/bashrc,再执行command -a 'echo $HOSTNAME',可能输出test(而非$HOSTNAME);原因:父 Shell 进程在传递命令给
command模块前,已解析了$HOSTNAME,掩盖了command模块 "不解析变量" 的本质。2. 远程 SSH 配置强制所有命令通过 Shell 执行
若远程主机的 SSH 配置(
/etc/ssh/sshd_config)设置了ForceCommand /bin/bash,所有通过 SSH 执行的命令都会被bash解析 ------ 此时command模块的命令也会被 Shell 预处理,变量会被解析。
- 解决:恢复 SSH 默认配置(注释
ForceCommand),重启sshd服务即可还原command模块的原生逻辑。3. 老旧 Ansible 版本的兼容性问题
Ansible 2.0 之前的版本对
command模块的处理不严格,部分场景下会间接调用 Shell 解析命令;而你使用的 Ansible 7.7.0(新版)严格区分command/shell,因此能看到纯原生的执行结果(command不解析变量)。4. 命令是 "Shell 内置命令" 而非系统二进制命令
比如
cd/alias是 Shell 内置命令(无对应的系统二进制文件),用command模块执行cd /tmp会失败(提示 "找不到命令"),而shell模块能正常执行 ------ 因为command只能调用系统二进制命令,Shell 内置命令必须依赖 Shell 解析。
script 模块
-
参数格式要求 :
script模块接收「脚本名称 + 空格分隔的参数列表」作为输入,必须指定「自由格式命令」或cmd参数(具体用法可参考示例)。 -
脚本执行流程:位于本地路径的脚本会先被传输到远程节点(受控主机),随后在远程节点上执行。
-
执行环境特性:指定的脚本会通过远程节点的 Shell 环境进行处理(支持 Shell 语法,如变量、管道、重定向等)。
-
script优势
-
command/shell执行脚本的前提:你必须先通过copy/synchronize等模块,把脚本传到远程节点,才能执行; -
script模块无需提前传脚本:直接指定主控节点的脚本路径,Ansible 会自动完成「传输→执行」全流程,少一步操作。
-
-
script脚本执行流程
-
主控节点 :读取
/root/init.sh脚本内容; -
受控节点 :Ansible 自动创建临时目录(格式如
/tmp/ansible-tmp-随机字符串/,比如/tmp/ansible-tmp-1752890123.456789/); -
传输 :把主控的
init.sh传输到这个临时目录; -
执行:在受控节点的临时目录中执行该脚本(执行时会通过远程 Shell 解析);
-
后续:执行完成后,临时目录和脚本默认不会自动删除(避免排查问题时无日志)。
简单说:脚本的执行是 "临时目录一次性执行",不会自动放到受控节点的
/root、/usr/bin等常规目录。 -
- name: 直接执行本地脚本(自动传输+执行)
ansible.builtin.script: /root/init.sh
- name: 运行带参数的脚本(自由格式)
ansible.builtin.script: /some/local/script.sh --some-argument 1234
- name: 运行带参数的脚本(使用'cmd'参数)
ansible.builtin.script:
cmd: /some/local/script.sh --some-argument 1234
- name: 仅当远程节点上不存在file.txt时运行脚本
ansible.builtin.script: /some/local/create_file.sh --some-argument 1234
args:
creates: /the/created/file.txt # 受控节点上的文件,不存在则执行脚本
- name: 仅当远程节点上存在file.txt时运行脚本
ansible.builtin.script: /some/local/remove_file.sh --some-argument 1234
args:
removes: /the/removed/file.txt # 受控节点上的文件,存在则执行脚本
- name: script传含空格的参数(安全)
ansible.builtin.script:
argv:
-
/root/create_user.sh
-
Zhang San # 直接写空格,无需转义
习题 1:基础用法(本地简单脚本远程执行)
-
要求 :将本地
/root/test.sh脚本上传到newserver主机执行。 -
本地脚本(/root/test.sh)
[root@rhce ~]# vim test.sh #不给test.sh加x权限,ansible照样可以执行 [root@rhce ~]# cat test.sh #!/bin/bash hostname -I -
正确命令
[root@rhce ~]# ansible newserver -m script -a '/root/test.sh' -i /hosts 192.168.223.151 | CHANGED => { "changed": true, "rc": 0, "stderr": "Shared connection to 192.168.223.151 closed.\r\n", "stderr_lines": [ "Shared connection to 192.168.223.151 closed." ], "stdout": "192.168.223.151 \r\n", "stdout_lines": [ "192.168.223.151 " ] } .....#注意我用redhat用户执行服务器的脚本/root/test.sh
#注意:script 模块的核心是 "传本地脚本到远程临时目录执行",全程不涉及远程 /root 目录的访问,因此 redhat 用户即使无权限访问远程 /root,也能成功执行;只有当你试图直接访问 / 执行远程 /root 下的文件时,才会触发权限失败。
[root@rhce ~]# ansible 192.168.223.151 -m script -a '/root/test.sh' -u redhat -k -i /hosts SSH password: 192.168.223.151 | CHANGED => { "changed": true, "rc": 0, "stderr": "Shared connection to 192.168.223.151 closed.\r\n", "stderr_lines": [ "Shared connection to 192.168.223.151 closed." ], "stdout": "192.168.223.151 \r\n", "stdout_lines": [ "192.168.223.151 " ] } -
核心知识点 模块自动将本地脚本上传到远程主机的临时目录,赋予执行权限后运行,无需手动传文件。
-
注意点 本地脚本需有可读权限,远程主机需有执行脚本的权限(必要时提权)。
[root@rhce ~]# chmod -r /root/test.sh [root@rhce ~]# ansible 192.168.223.151 -m script -a '/root/test.sh' -u redhat -k -i /hosts SSH password: An exception occurred during task execution. To see the full traceback, use -vvv. The error was: NoneType: None 192.168.223.151 | FAILED! => { "changed": true, "msg": "non-zero return code", "rc": 126, # rc=127:rc=127 是「文件不存在」,而 rc=126 是「文件存在但无法执行」; "stderr": "Shared connection to 192.168.223.151 closed.\r\n", "stderr_lines": [ "Shared connection to 192.168.223.151 closed." ], "stdout": "/bin/bash: /home/redhat/.ansible/tmp/ansible-tmp-1765802630.2622228-2074-39475357481292/test.sh: 权限不够\r\n", "stdout_lines": [ "/bin/bash: /home/redhat/.ansible/tmp/ansible-tmp-1765802630.2622228-2074-39475357481292/test.sh: 权限不够" ]
习题 2:脚本带参数执行
-
要求 :本地
/root/create_file.sh脚本接收一个参数(文件名),在远程主机/tmp目录创建该文件,执行时传入参数test_param.txt。 -
本地脚本(/root/create_file.sh)
#!/bin/bash touch /root/$1 -
正确命令
ansible newserver -m script -a '/root/create_file.sh test_param.txt' -i /hosts
-
注意点 参数若包含空格 / 特殊字符,需用引号包裹(如
'/root/create_file.sh "test file.txt"')。
习题 3:creates 参数避免重复执行脚本
-
要求 :仅当远程主机
/tmp/init_done文件不存在时,执行本地/root/init.sh脚本(避免重复初始化)。 -
正确命令
ansible newserver -m script -a '/root/init.sh creates=/tmp/init_done' -i /hosts -
核心知识点
script模块也支持creates/removes参数,实现幂等性(脚本执行后可在末尾添加touch /tmp/init_done标记)。 -
注意点
creates检测的是远程主机的文件,而非本地文件。
习题 4:chdir 参数切换远程执行目录
-
要求 :执行本地
/root/write_file.sh脚本时,先切换到远程主机/etc目录,脚本内容为写入当前目录的test.conf文件。 -
本地脚本(/root/write_file.sh)
#!/bin/bash
echo "test config" > test.conf
-
正确命令
ansible newserver -m script -a '/root/write_file.sh chdir=/etc' -i /hosts -b
-
核心知识点
chdir 参数让脚本在远程主机的指定目录执行,脚本中相对路径会基于该目录(最终/etc/test.conf会被创建)。
-
注意点
脚本中若用绝对路径,
chdir不会影响;仅相对路径会生效。
总结:
command/shell/script 模块的幂等性分两层:
-
「指令层」:直接执行无判断的原生系统指令 / 脚本 → 无幂等性;
-
「模块层」:利用模块自带的
creates/removes等参数约束执行时机 → 具备幂等性。
常用模块:
copy
-
核心功能 :
copy模块用于将文件从「本地机器」或「远程机器」复制到远程机器的指定位置。 -
反向文件复制的替代方案 :若需将文件从远程位置复制到本地机器,应使用
ansible.builtin.fetch模块(copy模块不支持该反向操作)。
- name: 复制文件并设置所有者及权限
ansible.builtin.copy:
src: /srv/myfiles/foo.conf
dest: /etc/foo.conf ##如目标存在,默认覆盖,此处指定先备份
owner: foo
group: foo
mode: '0644'
- name: 复制文件并设置所有者及权限(使用符号权限表示法)
ansible.builtin.copy:
src: /srv/myfiles/foo.conf
dest: /etc/foo.conf
owner: foo
group: foo
mode: u=rw,g=r,o=r
- name: 将内联内容写入文件
ansible.builtin.copy:
content: '# This file was moved to /etc/other.conf'
dest: /etc/mine.conf
实例:
[root@rhce ~]# ansible newserver -i /hosts -m copy -a "src=/etc/yum.repos.d/xixi.repo dest=/etc/yum.repos.d/xixi.repo "
192.168.223.151 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"checksum": "3f776e50a14ec2d5dc90ea945aa00ce5c344f941",
"dest": "/etc/yum.repos.d/xixi.repo",
"gid": 0,
"group": "root",
"mode": "0644",
"owner": "root",
"path": "/etc/yum.repos.d/xixi.repo",
"secontext": "system_u:object_r:system_conf_t:s0",
"size": 112,
"state": "file",
"uid": 0
}
yum_repository
核心功能与适用场景:在基于 RPM 的 Linux 发行版中,添加或删除 YUM 仓库。
- name: 添加YUM仓库
ansible.builtin.yum_repository:
name: epel
description: EPEL YUM repo
baseurl: https://download.fedoraproject.org/pub/epel/$releasever/$basearch/
- name: 向同一个配置文件中添加多个仓库(1/2)
ansible.builtin.yum_repository:
name: epel
description: EPEL YUM repo
file: external_repos
baseurl: https://download.fedoraproject.org/pub/epel/$releasever/$basearch/
gpgcheck: no
- name: 向同一个配置文件中添加多个仓库(2/2)
ansible.builtin.yum_repository:
name: rpmforge
description: RPMforge YUM repo
file: external_repos
baseurl: http://apt.sw.be/redhat/el7/en/$basearch/rpmforge
mirrorlist: http://mirrorlist.repoforge.org/el7/mirrors-rpmforge
enabled: no
- name: 从指定的仓库配置文件中移除仓库
ansible.builtin.yum_repository:
name: epel
file: external_repos
state: absent
实例:
[root@rhce ~]# ansible newserver -i /hosts -m yum_repository -a 'name=epel description="EPEL YUM repo" file=xixi baseurl=https://mirrors.aliyun.com/epel/9/Everything/x86_64/ gpgcheck=no'
192.168.223.152 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": true,
"repo": "epel",
"state": "present"
}
mount
该模块管理 /etc/fstab 中已配置的挂载点及当前活跃的挂载点,隐含两层操作:
-
管理「已配置的挂载点」:修改
/etc/fstab中的挂载配置(如添加、删除、修改挂载项); -
管理「活跃的挂载点」:执行实际的挂载(
mount)或卸载(umount)操作,让配置生效。
- name: Mount DVD read-only
ansible.posix.mount:
path: /mnt/dvd
src: /dev/sr0
fstype: iso9660
opts: ro,noauto
state: present
实例:
#写入文件/etc/fstab并且挂载
[root@rhce ~]# ansible newserver -m mount -a 'path=/mnt src=/dev/sr0 state=mounted fstype=iso9660 ' -i /hosts
[root@rhce ~]# ansible newserver -m mount -a 'path=/mnt src=/dev/sr0 state=present fstype=iso9660 ' -i /hosts
[root@rhce ~]# ansible newserver -m mount -a 'path=/mnt src=/dev/sr0 state=unmounted fstype=iso9660 ' -i /hosts
[root@rhce ~]# ansible newserver -m mount -a 'path=/mnt src=/dev/sr0 state=absent fstype=iso9660 ' -i /hosts
[root@rhce ~]# ansible newserver -m mount -a 'path=/mnt src=/dev/sr0 state=ephemeral fstype=iso9660 ' -i /hosts
补充:state参数
state 参数说明
state是 Ansible 挂载管理模块的核心参数,用于定义存储设备的挂载状态,各取值的具体行为如下:
mounted
设备会被主动挂载,且在
/etc/fstab(文件系统表)中完成对应的持久化配置;若指定的挂载点不存在,会自动创建该挂载点。
unmounted
- 仅卸载设备,不会修改
/etc/fstab中的挂载配置。present
- 仅将设备的挂载配置写入
/etc/fstab,不会触发实际挂载操作,也不要求设备处于已挂载状态。ephemeral(该选项新增于 1.5.0 版本)
仅临时挂载设备,不修改
/etc/fstab;若设备已挂载,会触发重新挂载操作,该取值始终返回
changed=True;若挂载点
path已挂载其他设备(且源设备与src指定的不一致),模块会执行失败,避免意外卸载现有设备或覆盖挂载点;若挂载点不存在,会自动创建挂载点;
完全忽略
/etc/fstab的配置。absent
将设备的挂载条目从
/etc/fstab中移除;同时卸载该设备,并删除对应的挂载点。
remounted(该选项新增于 2.9 版本)
用于强制刷新设备的挂载状态,触发设备重新挂载,该取值始终返回
changed=True;若设置了
opts(挂载选项),这些选项会应用于重新挂载操作,但不会修改/etc/fstab;若设置了
opts且重新挂载命令执行失败,模块会抛出错误,防止挂载配置发生意外变更(建议使用mounted取值规避此问题);该取值要求挂载点已存在于
/etc/fstab中;若需重新挂载未在
/etc/fstab中注册的挂载点(尤其针对 BSD 系统节点),请改用ephemeral取值。absent_from_fstab
仅将设备的挂载条目从
/etc/fstab中移除;不会卸载设备,也不会删除对应的挂载点。
参数基础属性补充
参数类型:字符串(str);
可选值:
absent、absent_from_fstab、mounted、present、unmounted、remounted
dnf
通过 dnf 包管理器执行软件包和软件组的安装、升级、移除及列出操作。
- name: Install the latest version of Apache and MariaDB
ansible.builtin.dnf:
name:
-
httpd
-
mariadb-server
state: latest
- name: Remove the Apache package
ansible.builtin.dnf:
name: httpd
state: absent
补充:
state参数说明(分点翻译)1. 参数核心作用
用于指定软件包的操作行为:安装(
present/latest)或移除(absent)。2. 可选值及含义
present:确保软件包已安装(默认行为,若已安装则不重复操作,不强制更新到最新版本);
latest:确保软件包已安装且为当前可用的最新版本;
absent:确保软件包已从系统中移除;
installed:与present功能完全等效(兼容写法);
removed:与absent功能完全等效(兼容写法)。3. 默认值说明
配置默认值为
null(无显式默认);实际生效逻辑:
未启用模块的
autoremove选项时,默认行为等同于present;启用
autoremove选项时,默认行为等同于absent。
实例:
[root@rhce ~]# ansible newserver -i /hosts -m dnf -a 'name=nginx state=absent'
systemd
-
service模块:-
适合多
init系统混合的环境(如同时管理 CentOS 6 和 CentOS 7 主机),兼容性强但功能有限; -
不推荐在纯
systemd系统中使用(会浪费systemd的专属特性)。
-
-
systemd模块:-
适合纯
systemd系统的环境(现在绝大多数生产环境都是),功能更全、状态更精准; -
不兼容传统
init系统(如 CentOS 6),用在非systemd系统会直接报错。
-
- name: 确保服务单元处于运行状态
ansible.builtin.systemd:
state: started
name: httpd
- name: 若Debian系统上的cron服务正在运行,则停止该服务
ansible.builtin.systemd:
name: cron
state: stopped
- name: 无论何种情况,重启CentOS系统上的crond服务,并执行daemon-reload以加载配置变更
ansible.builtin.systemd:
state: restarted
daemon_reload: true
name: crond
- name: 无论何种情况,重新加载httpd服务配置
ansible.builtin.systemd:
name: httpd.service
state: reloaded
- name: 启用httpd服务并确保其未被屏蔽
ansible.builtin.systemd:
name: httpd
enabled: true
masked: no
lineinfile
-
核心功能 该模块用于确保文件中存在某一特定行,或通过带反向引用的正则表达式替换文件中已存在的行。
-
核心适用场景此模块主要适用于「仅需修改文件中某一行内容」的场景。
-
其他场景的替代模块建议
-
若需修改文件中多行相似内容 ,请使用
ansible.builtin.replace模块; -
若需在文件中「插入 / 更新 / 删除一整块多行内容」,请查看
ansible.builtin.blockinfile模块; -
对于上述之外的其他场景(如完整替换文件、基于模板生成文件),请使用
ansible.builtin.copy或ansible.builtin.template模块。
-
ansible在使用sed进行替换时,经常会遇到需要转义的问题,而且ansible在遇到特殊符号进行替换时,存在问题,无法正常进行替换 。其实在ansible自身提供了两个模块:lineinfile模块和replace模块,可以方便的进行替换
一般在ansible当中去修改某个文件的单行进行替换的时候需要使用lineinfile模块regexp 参数 :使用正则表达式匹配对应的行,当替换文本时,如果有多行文本都能被匹配,则只有最后面被匹配到的那行文本才会被替换,当删除文本时,如果有多行文本都能被匹配,这么这些行都会被删除
如果想进行多行匹配进行替换需要使用 replace模块功能:相当于sed,可以修改文件内容
示例:
注:在2.3版本之前,使用选项'dest'、'destfile'或'name'替代'path'
- name: 确保SELinux设置为强制模式
ansible.builtin.lineinfile:
path: /etc/selinux/config
regexp: '^SELINUX='
line: SELINUX=enforcing
- name: 确保wheel用户组不在sudoers配置中
ansible.builtin.lineinfile:
path: /etc/sudoers
state: absent
regexp: '^%wheel'
- name: 搜索字面字符串替换localhost条目(避免转义操作)
ansible.builtin.lineinfile:
path: /etc/hosts
search_string: '127.0.0.1'
line: 127.0.0.1 localhost
owner: root
group: root
mode: '0644'
- name: 确保Apache默认端口为8080
ansible.builtin.lineinfile:
path: /etc/httpd/conf/httpd.conf
regexp: '^Listen '
insertafter: '^#Listen '
line: Listen 8080
- name: 若文件不存在则创建并添加指定行(无需传递regexp参数)
ansible.builtin.lineinfile:
path: /tmp/testfile
line: 192.168.1.99 foo.lab.net foo
create: yes
注:因行中包含': ',需使用全引号包裹。详见YAML文档中的注意事项。
- name: 保存前验证sudoers文件的合法性
ansible.builtin.lineinfile:
path: /etc/sudoers
state: present
regexp: '^%ADMIN ALL='
line: '%ADMIN ALL=(ALL) NOPASSWD: ALL'
validate: /usr/sbin/visudo -cf %s
在 Shell 命令行 中,反斜杠 \ 是「Shell 转义符」------ Shell 会先解析一次命令,若想让 Ansible / 正则引擎最终拿到 \s,必须写两个 \\(Shell 解析后会把 \\ 变成单个 \ 传给正则引擎)。
简单说:命令行里的 \\s = 正则引擎实际处理的 \s。
| 符号 | 含义 | 区别(* vs +) |
|---|---|---|
\s |
空白字符(空格 / Tab) | - |
\d |
数字(0-9) | - |
* |
匹配 0 个或多个前置字符 | 比如 \s*:允许行首无缩进 |
+ |
匹配 1 个或多个前置字符 | 比如 \s+:必须有至少 1 个空格 |
. |
任意字符(除换行) | 比如 .*:匹配所有附加参数 |
() |
分组,可通过 \1 引用 |
用于保留原格式(如缩进) |
实例:
[root@rhce ~]# ansible newserver -i /hosts -m lineinfile -a "path=/etc/nginx/nginx.conf regexp='^(\s*)listen.*' line='\1listen 100;'"
192.168.223.152 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"backup": "",
"changed": true,
"msg": "line replaced"
}
[root@rhce ~]# ansible newserver -i /hosts -m shell -a 'grep "listen" /etc/nginx/nginx.conf'
192.168.223.152 | CHANGED | rc=0 >>
listen 99;
\1listen 100;
[root@rhce ~]# ansible newserver -i /hosts -u root -m lineinfile -a "path=/etc/nginx/nginx.conf regexp='^\s*listen 99;$' state=absent"
[root@rhce ~]# ansible newserver -i /hosts -u root -m replace -a "path=/etc/nginx/nginx.conf regexp='(\\\\\\1listen 100;\\s*)' replace='listen 90;\n'"
[root@rhce ~]# ansible newserver -i /hosts -u root -m lineinfile -a "path=/etc/nginx/nginx.conf regexp='^(\\s*)listen\\s.*;' line='\\\\1listen 89;' backrefs=yes backup=yes"
#backrefs=yes 是控制「正则分组反向引用生效」的核心参数
search_string参数:
search_string 是 lineinfile 模块中基于 "字面量字符串" 匹配文件内容 的参数(区别于 regexp 的正则匹配),用于精准查找固定字符串(特殊字符无需转义),并配合 state 参数实现 "替换 / 删除 / 添加" 行的操作。
核心特性:字面量匹配(关键区别于 regexp):
-
匹配规则:严格按 "字符原样" 匹配,不会将
.、*、|等视为正则符号; -
示例:若
search_string: "*.conf",只会匹配包含*.conf的行,而非正则中的 "任意.conf 文件"。
replace
-
核心功能
该模块会替换文件中所有匹配指定模式(pattern)的内容实例。
-
幂等性要求
操作的幂等性需由用户自行保障,核心原则是:确保所使用的匹配模式不会匹配到任何通过该模块完成的替换结果(避免因重复匹配导致多次替换,破坏幂等性)
示例:
- name: replace模块实现字面量匹配(等价于search_string)
ansible.builtin.replace:
path: /etc/nginx/conf.d/test.conf
# 用regex_escape转义后,*.conf会被当作字面量,而非正则通配符
regexp: "{{ '*.conf' | regex_escape }}"
replace: "all.conf" # 替换后的内容
backup: yes # 可选:备份原文件
- name: 将旧主机名替换为新主机名(要求Ansible版本≥2.4)
ansible.builtin.replace:
path: /etc/hosts
regexp: '(\s+)old\.host\.name(\s+.*)?$'
replace: '\1new.host.name\2'
- name: 替换表达式之后直至文件末尾的内容(要求Ansible版本≥2.4)
ansible.builtin.replace:
path: /etc/apache2/sites-available/default.conf
after: 'NameVirtualHost [*]'
regexp: '^(.+)$'
replace: '# \1'
- name: 替换表达式之前直至文件开头的内容(要求Ansible版本≥2.4)
ansible.builtin.replace:
path: /etc/apache2/sites-available/default.conf
before: '# live site config'
regexp: '^(.+)$'
replace: '# \1'
# 在Ansible 2.7.10版本之前,同时使用before和after参数会产生与预期相反的效果(内容截断)
# 详情可参考:https://github.com/ansible/ansible/issues/31354。
- name: 替换两个表达式之间的内容(要求Ansible版本≥2.4)
ansible.builtin.replace:
path: /etc/hosts
after: '<VirtualHost [*]>'
before: '</VirtualHost>'
regexp: '^(.+)$'
replace: '# \1'
- name: 支持设置文件的通用属性
ansible.builtin.replace:
path: /home/jdoe/.ssh/known_hosts
regexp: '^old\.host\.name[^\n]*\n'
owner: jdoe
group: jdoe
mode: '0644'
- name: 支持配置验证命令
ansible.builtin.replace:
path: /etc/apache/ports
regexp: '^(NameVirtualHost|Listen)\s+80\s*$'
replace: '\1 127.0.0.1:8080'
validate: '/usr/sbin/apache2ctl -f %s -t'
- name: 简短形式的任务(Ansible 2+版本中)需要使用反斜杠转义序列
ansible.builtin.replace: path=/etc/hosts regexp='\\b(localhost)(\\d*)\\b' replace='\\1\\2.' # 内容截断
- name: 长格式任务则无需转义
ansible.builtin.replace:
path: /etc/hosts
regexp: '\b(localhost)(\d*)\b'
replace: '\1\2.localdomain\2 \1\2'
- name: 在替换内容中显式指定位置匹配分组
ansible.builtin.replace:
path: /etc/ssh/sshd_config
regexp: '^(ListenAddress[ ]+)[^\n]+$'
replace: '\g<1>0.0.0.0'
- name: 显式指定命名匹配分组
ansible.builtin.replace:
path: /etc/ssh/sshd_config
regexp: '^(?P<dctv>ListenAddress[ ]+)(?P<host>[^\n]+)$'
replace: '#\g<dctv>\g<host>\n\g<dctv>0.0.0.0'
参数说明(分点翻译)
1. path
核心作用:指定需要修改的文件路径。
历史版本兼容:在 Ansible 2.3 版本之前,该选项仅能使用
dest、destfile或name作为参数名。别名:
dest、destfile、name(这三个名称与path功能等效)。参数类型:路径类型(path)。
2. regexp
核心作用:指定用于匹配文件内容的正则表达式。
语法依据:遵循 Python 正则表达式语法,详情可参考:https://docs.python.org/3/library/re.html。
匹配模式:启用 MULTILINE(多行)模式,这意味着:
正则符号
^既匹配文件的开头,也匹配文件中 "每一行" 的开头;正则符号
$既匹配文件的结尾,也匹配文件中 "每一行" 的结尾。未启用模式:不启用 DOTALL 模式,这意味着特殊字符
.会匹配 "除换行符(\n)之外" 的任意字符。常见误区:容易误认为像
[^#]这样的否定字符集也会自动匹配不到换行符(实际不会)。换行符排除方法:若需在否定字符集中排除换行符,需将换行符显式添加到字符集中,例如
[^#\n]。转义序列注意事项:自 Ansible 2.0 版本起,简短形式的任务中,所有转义序列都需额外添加反斜杠进行转义(即
\需写成\\),以避免这些转义序列被解析为字符串字面量转义(详见示例)。参数类型:字符串类型(str)。
实例:
[root@rhce ~]# ansible newserver -i /hosts -m replace -a "path=/etc/nginx/nginx.conf regexp='^(\s*)listen.*' replace='\1listen 99;'"
192.168.223.151 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": true,
"msg": "2 replacements made",
"rc": 0
}
....
[root@rhce ~]# ansible newserver -i /hosts -m shell -a 'grep "^ *listen" /etc/nginx/nginx.conf'
192.168.223.151 | CHANGED | rc=0 >>
listen 99;
listen 99;
192.168.223.152 | CHANGED | rc=0 >>
listen 99;
listen 99;
[root@rhce ~]# ansible 192.168.223.151 -m replace -a "path=/fstab regexp='^(UUID.*)' replace='\1'"
[root@rhce ~]# ansible 192.168.223.151 -m replace -a "path=/passwd regexp='^([a-zA-Z].*)$' replace='# \1'"
[root@rhce ~]# ansible 192.168.223.151 -m replace -a "path=/fstab regexp='^#(UUID.*)' replace='\1'"
unarchive
-
核心功能 :该模块的作用是解压归档文件(如
.zip/.tar.gz/.tar.bz2等);但明确限制 ------ 仅处理 "归档文件",无法解压「仅压缩但不含归档结构的单个文件」(比如纯.gz压缩的单个文本文件,而非归档包)。 -
默认执行逻辑:模块默认行为是「先将控制节点(本地)的源归档文件复制到目标远程主机,再在目标主机上执行解压操作」。
-
远程源文件解压配置 :若需解压「已存在于目标远程主机上」的归档文件,需设置参数
remote_src=yes(无需从本地复制文件)。 -
校验和验证的替代方案 :如果需要对归档文件做校验和(checksum)验证,不建议直接用该模块;应先通过
ansible.builtin.get_url或ansible.builtin.uri模块获取文件(自带校验和能力),再配合remote_src=yes用unarchive解压。
- name: 下载foo.conf文件
ansible.builtin.get_url:
url: http://example.com/path/file.conf # 待下载文件的远程URL
dest: /etc/foo.conf # 文件下载后在受控节点的保存路径
mode: '0440' # 下载后文件的权限设置为0440
get_url
-
核心功能:从 HTTP、HTTPS 或 FTP 协议地址下载文件至远程服务器。
-
网络访问要求 :远程服务器必须能够直接访问待下载的远程资源(即目标文件所在的网络地址)。
get_url 模块默认幂等:仅当「远程文件更新」或「本地文件不存在 / 校验和不一致」时才重新下载,避免重复操作。
#若 dest 指定的是目录(而非文件):需确保目录已存在(可先用 file 模块创建),模块会将文件保存到该目录,文件名沿用 URL 中的文件名;
示例:
- name: 先创建目录
ansible.builtin.file:
path: /etc/foo
state: directory
- name: 下载文件到目录(文件名沿用file.conf)
ansible.builtin.get_url:
url: http://example.com/path/file.conf
dest: /etc/foo/ # 结尾加/表示目录
file
-
核心功能 1:设置属性:设置文件、目录或符号链接(symlinks)及其目标对象的属性(如权限、所有者、所属组等)。
-
核心功能 2:删除操作:此外,可执行文件、符号链接或目录的删除操作。
示例:
- name: 修改文件的所有者、所属组及权限
ansible.builtin.file:
path: /etc/foo.conf
owner: foo
group: foo
mode: '0644'
- name: 为已有文件设置宽松权限(低安全级别)
ansible.builtin.file:
path: /work
owner: root
group: root
mode: '1777'
- name: 创建符号链接
ansible.builtin.file:
src: /file/to/link/to
dest: /path/to/symlink
owner: foo
group: foo
state: link
- name: 创建两个硬链接
ansible.builtin.file:
src: '/tmp/{{ item.src }}'
dest: '{{ item.dest }}'
state: hard
loop:
-
{ src: x, dest: y }
-
{ src: z, dest: k }
=====================
第一次循环:
模块参数最终为 src: /tmp/x、dest: y → 在远程主机的当前工作目录下,为 /tmp/x 创建名为 y 的硬链接;
第二次循环:
模块参数最终为 src: /tmp/z、dest: k → 在远程主机的当前工作目录下,为 /tmp/z 创建名为 k 的硬链接。
简单来说,loop 定义了两次操作的 "变量值对",src 和 dest 通过 {{ item.xxx }} 读取这些变量,
====================
- name: 创建目录(若不存在则创建)
ansible.builtin.file:
path: /etc/some_directory
state: directory
mode: '0755'
- name: 递归修改目录的所有者和所属组
ansible.builtin.file:
path: /etc/foo
state: directory
recurse: yes
owner: foo
group: foo
- name: 删除文件
ansible.builtin.file:
path: /etc/foo.txt
state: absent
- name: 递归删除目录
ansible.builtin.file:
path: /etc/foo
state: absent
firewalld
- name: 允许默认区域的HTTPS服务流量
ansible.posix.firewalld:
service: https # 目标服务(HTTPS,对应默认443端口)
permanent: true # 配置永久生效(重启防火墙后不失效)
state: enabled # 启用该规则(允许流量)
- name: 禁止默认区域的8081/TCP端口流量
ansible.posix.firewalld:
port: 8081/tcp # 目标端口(8081端口,TCP协议)
permanent: true # 配置永久生效
state: disabled # 禁用该规则(禁止流量)
immediate参数说明
核心作用 :当配置设为永久生效(
permanent: true)时,是否立即将该配置应用到当前运行中的 firewalld 实例。默认值 :
false(即使设置了permanent: true,也不会立即生效,需通过firewall-cmd --reload或重启服务触发)。参数类型 :布尔值(bool,仅接受
true/false)。补充说明
当
permanent: true且immediate: true时:配置会同时写入防火墙配置文件(持久化)和立即应用到运行时状态,无需额外执行重载命令;当
permanent: false时:该参数无效(临时配置默认立即生效)。
实例:
[root@rhce ~]# ansible newserver -i /hosts -m firewalld -a 'port=89/tcp permanent=true state=enabled immediate=true'
效果等价于:
[root@rhce ~]# ansible newserver -i /hosts -m firewalld -a 'port=89/tcp permanent=true state=enabled'
[root@rhce ~]# ansible newserver -m shell -a "firewall-cmd --reload" -i /hosts
selinux
- name: Enable SELinux
ansible.posix.selinux:
policy: targeted
state: enforcing
- name: Put SELinux in permissive mode, logging actions that would be blocked.
ansible.posix.selinux:
policy: targeted
state: permissive
- name: Disable SELinux
ansible.posix.selinux:
state: disabled
数据库模块
Ansible 的
community.mysql数据库模块本身设计了完善的幂等性机制,你遇到的 "第一次无密码登录成功、第二次需密码登录失败" 的问题,不是模块没有幂等性,而是未针对 "密码从无到有" 的场景做「动态登录适配」------ 只需在 Playbook 中增加 "判断数据库密码状态" 的逻辑,结合模块的幂等参数,就能实现多次执行不失败。问题根源分析
第一次执行:MySQL/MariaDB 初始状态 root 无密码,模块不填
login_password能登录,执行设置密码 / 创建库等操作;第二次执行:数据库已设置 root 密码,但 Playbook 仍用 "无密码" 方式登录,模块连接失败;
核心矛盾:登录参数未根据 "密码是否存在" 动态调整,而非模块本身的幂等性缺失。
#登录受控节点(192.168.223.151),执行以下命令(CentOS/RHEL 系统): # 安装Python3的MySQL库(现在主流是Python3) yum install -y python3-PyMySQL # 或用pip安装(如果没有yum包) pip3 install PyMySQL解决方案(完整幂等 Playbook)
以下 Playbook 实现核心逻辑:
先判断数据库 root 是否已设置密码;
动态适配登录参数(无密码 / 有密码);
利用模块幂等性参数,确保 "设置密码、创建库 / 用户" 仅执行一次。
--- - name: 数据库密码初始化(幂等版,适配无密码→有密码) hosts: newserver # 替换为自己的数据库服务器IP/组名 become: yes # 提权(操作数据库需要root) gather_facts: no vars: db_root_pwd: "Redhat123!" # 学生可自行修改目标密码 db_user: "root" db_host: "localhost" tasks: # 步骤1:判断root是否有密码(核心:用简单命令测试) - name: 测试root无密码能否登录 ansible.builtin.shell: 'mysql -u{{ db_user }} -S /var/lib/mysql/mysql.sock -e "show databases;" 2>/dev/null' register: no_pwd_login ignore_errors: yes # 有密码时登录失败,忽略错误不中断 changed_when: no # 仅判断状态,不标记“变更” # 步骤2:仅无密码时,设置root密码(幂等:设过就不重复设) - name: 初始化root密码(仅第一次执行) community.mysql.mysql_user: name: "{{ db_user }}" host: "localhost" # 简化:只设localhost的密码,去掉冗余host password: "{{ db_root_pwd }}" check_implicit_admin: yes # 无密码时允许登录的关键参数 login_unix_socket: /var/lib/mysql/mysql.sock login_user: "{{ db_user }}" login_host: "{{ db_host }}" login_password: "" # 无密码时填空 state: present # 幂等核心:密码已设则跳过 when: no_pwd_login.rc == 0 # 只有无密码登录成功时才执行 # 步骤3:创建测试库(幂等:已存在则跳过,适配有/无密码登录) - name: 创建test_db数据库(多次执行不报错) community.mysql.mysql_db: name: test_db state: present # 幂等:确保库存在,不存在才创建 encoding: utf8mb4 # 动态适配密码:有密码用db_root_pwd,无密码填空 login_unix_socket: /var/lib/mysql/mysql.sock login_user: "{{ db_user }}" login_host: "{{ db_host }}" login_password: "{{ db_root_pwd }}"
完整版本:
- name: 数据库部署(安装+启动+设密码+建luntan库)
hosts: newserver # 替换为服务器IP/组名
become: yes
gather_facts: no
vars:
db_root_pwd: "Redhat123!" # root密码
db_name: "luntan" # 要创建的数据库名
tasks:
1. 安装MariaDB+Python依赖(列表形式更易读)
- name: 安装mariadb-server和Python依赖
ansible.builtin.dnf:
name:
-
mariadb-server
-
python3-PyMySQL
state: present
2. 启动MariaDB并开机自启
- name: 启动mariadb服务
ansible.builtin.systemd:
name: mariadb
state: started
enabled: yes
3. 测试root无密码登录(简化命令,仅测连通性)
- name: 测试root无密码登录
ansible.builtin.shell: 'mysql -uroot -S /var/lib/mysql/mysql.sock -e "SELECT 1" 2>/dev/null'
register: no_pwd_login
ignore_errors: yes # 【核心】登录失败时忽略错误,保证流程不中断
changed_when: no # 【核心】标记无变更,保证幂等性,多次执行仅判断状态不触发变更
4. 仅无密码时设root密码(删掉冲突的login_host)
- name: 初始化root密码
community.mysql.mysql_user:
name: root
host: localhost
password: "{{ db_root_pwd }}"
check_implicit_admin: yes # 【核心】无密码登录时允许授权操作,是无密码设密码的关键参数
login_unix_socket: /var/lib/mysql/mysql.sock # 【核心】强制走socket认证,避免TCP连接密码校验失败
login_user: root
login_password: ""
state: present # 【核心】幂等核心,密码已设则跳过,保证多次执行不重复操作
when: no_pwd_login.rc == 0 # 【核心】仅无密码登录成功时执行,避免重复设密码
5. 创建luntan数据库(最简参数,无冗余)
- name: 创建luntan数据库
community.mysql.mysql_db:
name: "{{ db_name }}"
state: present # 【核心】幂等:确保库存在,不存在才创建,多次执行无报错
encoding: utf8mb4
login_unix_socket: /var/lib/mysql/mysql.sock
login_user: root
login_password: "{{ db_root_pwd }}"
扩展:
gather_facts、become是 Play 级别通用参数(作用于整个 Play 的所有任务);
ignore_errors、changed_when是 任务级别通用参数(仅作用于单个任务);
check_implicit_admin在 "初始化 root 密码" 的任务中,因为
login_password: ""(无密码登录),如果不配置check_implicit_admin: yes,会触发 "权限不足" 错误,无法完成密码设置 ------ 这是无密码状态下给 root 设密码的必配参数。
when: no_pwd_login.rc == 0
when类型 / 含义:Ansible 的核心关键字,专门用于定义 "任务是否执行" 的条件判断规则。深度拓展:当when后跟随的表达式结果为True时,当前 "初始化 root 密码" 的任务会执行;若表达式结果为False,该任务会被标记为skipped(跳过)。when支持 Python 风格的比较运算符(如==/!=/>/<)和逻辑运算符(如and/or/not),是实现 Ansible 任务条件执行的核心。
no_pwd_login类型 / 含义:前置 "测试 root 无密码能否登录" 任务的 "结果注册变量名"。深度拓展:这个变量来自前置任务里的register: no_pwd_login配置,Ansible 会把该 shell 任务的所有执行结果(包括返回码、命令输出、执行耗时、错误信息等)都存储在这个变量中,供后续任务调用,是 Ansible 跨任务传递执行结果的核心方式。
.rc类型 / 含义:no_pwd_login变量的子字段,全称是 return code(返回码 / 退出码),是 Linux 系统中判断命令执行结果的核心指标。深度拓展:Linux 系统通用规则为 ------rc == 0代表命令执行成功(对应场景:root 无密码登录 MySQL/MariaDB 成功);rc != 0(如 1、1045 等)代表命令执行失败(对应场景:root 无密码登录失败,说明已有密码)。相比解析命令输出内容,通过rc判断执行结果更稳定、不易出错。
== 0类型 / 含义:Python 风格的比较运算符,用于判定no_pwd_login.rc的取值是否等于 0。深度拓展:结合当前场景,该判定逻辑为 ------ 只有当 root 无密码登录成功(rc=0)时,才需要执行 "初始化 root 密码" 的任务;若rc≠0(root 已有密码,无密码登录失败),设密码操作无意义且可能触发 "密码已存在" 报错,因此会跳过该任务,这也是保证 Playbook 幂等性的关键。
ignore_errors: yes
归属:任务级别通用参数(所有类型的 Ansible 任务都可配置);
作用:仅作用于当前 的 shell 任务 ------ 当该任务执行失败(比如 root 已有密码导致登录失败、返回 rc≠0)时,Ansible 不会中断整个 Playbook 的执行流程,而是忽略该错误,继续运行后续的 "初始化 root 密码""创建数据库" 等任务;
层级对比:和任务内的模块同级(属于单个任务的配置)。
changed_when: no
归属:同样是任务级别通用参数;
作用:仅作用于当前 的 shell 任务 ------ 强制将该任务的 "变更状态" 标记为no(即changed=False),无论该 shell 命令是否执行成功,Ansible 都会判定该任务 "无变更";(补充:shell 模块默认会把 "执行了命令" 判定为changed=True,但该任务只是 "只读测试"(没修改任何系统配置 / 数据),标记为no能保证 Playbook 的幂等性统计准确);
encoding: utf8mb4
MySQL/MariaDB 中默认的 "utf8" 编码实际是
utf8mb3,仅支持部分 UTF-8 字符(无法存储 emoji、部分中文生僻字);
utf8mb4是完整兼容 UTF-8 的编码格式,能覆盖所有 Unicode 字符(包括中文、emoji、特殊符号等),从根本上避免数据库存储中文内容时出现乱码、字符无法插入的问题。创建
luntan(论坛)类数据库时,存储的内容大概率包含中文(如帖子、用户名),配置encoding: utf8mb4是避免中文乱码的核心配置,也是学习 / 生产环境中创建中文数据库的标准操作。
setup
-
该模块会被 Playbook 自动调用,用于收集远程主机的有用变量,这些变量可在 Playbook 中直接使用。
-
也可通过
/usr/bin/ansible命令直接执行该模块,以查看某台主机可用的变量。 -
Ansible 会自动提供许多关于系统的「事实信息(facts)」,这些 Facts 包含主机的硬件、系统配置、网络、环境变量等各类基础信息,是编写动态 Playbook(如条件判断、差异化配置)、调试节点信息的关键工具。
功能: setup 模块来收集主机的系统信息,这些 facts 信息可以直接以变量的形式使用,但是如果主机较多,会影响执行速度
可以使用 gather_facts: no 来禁止 Ansible 收集 facts 信息
ansible all -m setup 是基础用法,-a "filter=xxx" 用于过滤指定 Facts(避免输出全量冗余信息)
实战技巧:不用死记所有 Facts,记住「系统 / 硬件 / 网络」三大核心类的常用 Facts 即可,陌生 Facts 按需查询即可。
收集清单中所有节点的全量系统事实(Facts),含硬件、系统、网络等所有信息
ansible newserver -i /hosts -m setup
筛选收集所有节点的完整主机名(FQDN,包含域名)
ansible newserver -i /hosts -m setup -a "filter=ansible_nodename"
筛选收集所有节点的短主机名(不含域名)
ansible newserver -i /hosts -m setup -a "filter=ansible_hostname"
筛选收集所有节点的DNS域名
ansible newserver -i /hosts -m setup -a "filter=ansible_domain"
筛选收集所有节点的物理内存总容量(单位:MB)
ansible newserver -i /hosts -m setup -a "filter=ansible_memtotal_mb"
筛选收集所有节点的内存详细信息(物理内存+交换分区的总/闲/用容量)
ansible newserver -i /hosts -m setup -a "filter=ansible_memory_mb"
筛选收集所有节点的物理内存空闲容量(单位:MB)
ansible newserver -i /hosts -m setup -a "filter=ansible_memfree_mb"
筛选收集所有节点的操作系统家族(如RedHat/Debian/Suse)
ansible newserver -i /hosts -m setup -a "filter=ansible_os_family"
筛选收集所有节点的操作系统发行版主版本号(如7/8/20)
ansible newserver -i /hosts -m setup -a "filter=ansible_distribution_major_version"
筛选收集所有节点的操作系统发行版完整版本号(如7.9/20.04)
ansible newserver -i /hosts l -m setup -a "filter=ansible_distribution_version"
筛选收集所有节点的CPU逻辑核心数
ansible newserver -i /hosts -m setup -a "filter=ansible_processor_vcpus"
筛选收集所有节点的所有IPv4地址列表(不含回环地址)
ansible newserver -i /hosts -m setup -a "filter=ansible_all_ipv4_addresses"
筛选收集所有节点的CPU架构(如x86_64/aarch64)
ansible newserver -i /hosts -m setup -a "filter=ansible_architecture"
筛选收集所有节点的系统运行时长(单位:秒)
ansible newserver -i /hosts -m setup -a "filter=ansible_uptime_seconds"
筛选收集所有节点CPU相关的所有事实(*为通配符,匹配ansible_processor开头的所有项)
ansible newserver -i /hosts -m setup -a "filter=ansible_processor*"
筛选收集所有节点的系统环境变量(如PATH/HOME/USER等)
ansible newserver -i /hosts -m setup -a 'filter=ansible_env'
查看所有节点的全量Facts(输出内容多,建议重定向到文件查看)
ansible newserver -i /hosts -m setup > all_facts.txt
查看单个节点的全量Facts(更高效)
ansible 192.168.223.151 -m setup > single_facts.txt
查看所有CPU相关Facts
ansible newserver -i /hosts -m setup -a "filter=ansible_processor*"
查看所有网络相关Facts
ansible newserver -i /hosts -m setup -a "filter=ansible_*_ipv4*"
查看所有内存相关Facts
ansible newserver -i /hosts -m setup -a "filter=ansible_*mem*"
debug
-
该模块在 Playbook 执行过程中输出指定语句,可用于调试变量或表达式,且无需终止 Playbook 的执行。
-
该模块与
when:指令配合使用时,可有效提升调试效率(是调试场景的常用组合)。
此模块可以用于输出信息,并且通过 msg 定制输出的信息内容
注意: msg后面的变量有时需要加 " " 引起来
范例: debug 模块默认输出Hello world
[root@rhce ~]# ansible newserver -i /hosts -m debug
192.168.223.151 | SUCCESS => {
"msg": "Hello world!"
}
192.168.223.152 | SUCCESS => {
"msg": "Hello world!"
}
或
[root@rhce ~]# vim debug.yml
[root@rhce ~]# cat debug.yml
---
- hosts: newserver
tasks:
- name: output hello world
debug:
[root@rhce ~]#
[root@rhce ~]# ansible-playbook -i /hosts debug.yml
PLAY [newserver] **********************************************************************************************************************************************************
TASK [Gathering Facts] ****************************************************************************************************************************************************
ok: [192.168.223.152]
ok: [192.168.223.151]
TASK [output hello world] *************************************************************************************************************************************************
ok: [192.168.223.151] => {
"msg": "Hello world!"
}
ok: [192.168.223.152] => {
"msg": "Hello world!"
}
PLAY RECAP ****************************************************************************************************************************************************************
192.168.223.151 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.223.152 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
范例: 显示字符串特定字符
[root@rhce ~]# vim debug.yml
[root@rhce ~]# ansible-playbook -i /hosts debug.yml
PLAY [newserver] **********************************************************************************************************************************************************
TASK [debug] **************************************************************************************************************************************************************
ok: [192.168.223.151] => {
"msg": "3"
}
ok: [192.168.223.152] => {
"msg": "3"
}
PLAY RECAP ****************************************************************************************************************************************************************
192.168.223.151 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.223.152 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@rhce ~]# cat debug.yml
---
- hosts: newserver
gather_facts: no
vars:
a: "12345" # 仅用半角空格缩进(2/4个,统一即可)
tasks:
- debug: # 缩进对齐,冒号后加空格(可选,但规范)
msg: "{{a[2]}}" # 比debug多2个半角空格,无全角字符
范例:调试 "仅当命令执行失败时输出信息",可结合register+when+debug(更贴近真实调试):
[root@rhce ~]# vim debug.yml
[root@rhce ~]# cat debug.yml
---
- hosts: newserver
gather_facts: no
tasks:
- name: 模拟执行命令并捕获结果
shell: "ls /rooti"
register: cmd_result
ignore_errors: yes
- name: 仅命令执行失败时输出调试信息
debug:
msg: "调试:命令执行失败!返回码={{ cmd_result.rc }},错误信息={{ cmd_result.stderr }}"
when: cmd_result.rc != 0 # 命令返回码非0(失败)时触发调试输出
[root@rhce ~]# ansible-playbook -i /hosts debug.yml
PLAY [newserver] **********************************************************************************************************************************************************
TASK [模拟执行命令并捕获结果] *********************************************************************************************************************************************
fatal: [192.168.223.152]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"}, "changed": true, "cmd": "ls /rooti", "delta": "0:00:00.006434", "end": "2025-12-19 15:21:55.426362", "msg": "non-zero return code", "rc": 2, "start": "2025-12-19 15:21:55.419928", "stderr": "ls: 无法访问 '/rooti': 没有那个文件或目录", "stderr_lines": ["ls: 无法访问 '/rooti': 没有那个文件或目录"], "stdout": "", "stdout_lines": []}
...ignoring
fatal: [192.168.223.151]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"}, "changed": true, "cmd": "ls /rooti", "delta": "0:00:00.006166", "end": "2025-12-19 15:21:55.429264", "msg": "non-zero return code", "rc": 2, "start": "2025-12-19 15:21:55.423098", "stderr": "ls: 无法访问 '/rooti': 没有那个文件或目录", "stderr_lines": ["ls: 无法访问 '/rooti': 没有那个文件或目录"], "stdout": "", "stdout_lines": []}
...ignoring
TASK [仅命令执行失败时输出调试信息] ***************************************************************************************************************************************
ok: [192.168.223.151] => {
"msg": "调试:命令执行失败!返回码=2,错误信息=ls: 无法访问 '/rooti': 没有那个文件或目录"
}
ok: [192.168.223.152] => {
"msg": "调试:命令执行失败!返回码=2,错误信息=ls: 无法访问 '/rooti': 没有那个文件或目录"
}
PLAY RECAP ****************************************************************************************************************************************************************
192.168.223.151 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=1
192.168.223.152 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=1
范例:在 Playbook 中用 debug 模块输出 Facts 的值和类型,更贴近实战场景
[root@rhce ~]# vim debug.yml
[root@rhce ~]# cat debug.yml
---
- hosts: newserver
gather_facts: yes # 自动收集Facts(默认开启)
tasks:
- name: 查看陌生Facts的具体值
debug:
var: ansible_bios_version # 替换为要查询的Facts名称
[root@rhce ~]# ansible newserver -i /hosts -m setup -a "filter=ansible_bios_version "
192.168.223.152 | SUCCESS => {
"ansible_facts": {
"ansible_bios_version": "6.00",
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false
}
192.168.223.151 | SUCCESS => {
"ansible_facts": {
"ansible_bios_version": "6.00",
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false
}
实验1:用ansible命令行实现nginx部署修改端口号
步骤:
1、写仓库 2、挂载 3、下载nginx 4、修改已存在的配置文件中的部分内容 5、重启配置生效 6、防火墙放行80号端口 7、SELinux设置为宽容模式
1、写仓库
[root@rhce ~]# ansible newserver -i /hosts -m copy -a "src=/etc/yum.repos.d/xixi.repo dest=/etc/yum.repos.d/xixi.repo "
2、挂载
[root@rhce ~]# ansible newserver -m mount -a 'path=/mnt src=/dev/sr0 state=mounted fstype=iso9660 ' -i /hosts
3、下载nginx
[root@rhce ~]# ansible newserver -i /hosts -m dnf -a 'name=nginx state=present'
4、修改已存在的配置文件/etc/nginx.conf中的端口号
[root@rhce ~]# ansible newserver -i /hosts -u root -m lineinfile -a "path=/etc/nginx/nginx.conf regexp='^(\\s*)listen\\s.*;' line='\\1listen 89;' backrefs=yes backup=yes"
7、SELinux设置为宽容模式
[root@rhce ~]# ansible newserver -i /hosts -m selinux -a 'policy=targeted state=permissive'
5、重启配置生效
[root@rhce ~]# ansible newserver -i /hosts -m systemd -a 'state=started name=nginx'
6、防火墙放行80号端口
[root@rhce ~]# ansible newserver -i /hosts -m firewalld -a 'port=89/tcp permanent=true state=enabled'
[root@rhce ~]# ansible newserver -m shell -a "firewall-cmd --reload" -i /hosts
测试:
http://192.168.223.151:89
http://192.168.223.152:89
实验2:创建一个虚拟主机,基于不同端口访问不同页面
1、写虚拟主机文件/etc/nginx/conf.d/nginx.conf
2、添加测试页面
3、重启让配置生效
4、防火墙放行指定端口
5、SELinux设为宽容模式放行指定端口
1、写虚拟主机文件/etc/nginx/conf.d/nginx.conf
[root@rhce ~]# cat /nginx.conf
server {
listen 80;
root /web/port80;
location / {
index index.html;
}
}
server {
listen 666;
root /web/port666;
location / {
index index.html;
}
}
[root@rhce ~]# ansible newserver -i /hosts -m copy -a 'src='/nginx.conf' dest=/etc/nginx/conf.d/ '
[root@rhce ~]# ansible newserver -i /hosts -m shell -a 'ls -lZ /etc/nginx/conf.d/nginx.conf'
192.168.223.151 | CHANGED | rc=0 >>
-rw-r--r--. 1 root root system_u:object_r:httpd_config_t:s0 196 12月 17 11:16 /etc/nginx/conf.d/nginx.conf
192.168.223.152 | CHANGED | rc=0 >>
-rw-r--r--. 1 root root system_u:object_r:httpd_config_t:s0 196 12月 17 11:16 /etc/nginx/conf.d/nginx.conf
2、添加测试页面
[root@rhce ~]# ansible newserver -i /hosts -m file -a 'path=/web/port666 state=directory'
[root@rhce ~]# ansible newserver -i /hosts -m file -a 'path=/web/port80 state=directory'
ansible newserver -i /hosts -u root -m copy -a "content='<h1>Welcome to Port 80</h1>' dest=/web/port80/index.html mode=0644"
ansible newserver -i /hosts -u root -m copy -a "content='<h1>Welcome to Port 666</h1>' dest=/web/port666/index.html mode=0644"
ansible newserver -i /hosts -m shell -a "nginx -t"
3、重启让配置生效
ansible newserver -i /hosts -m service -a "name=nginx state=started enabled=yes"
4、防火墙放行指定端口
ansible newserver -i /hosts -m ansible.posix.firewalld -a "port=80/tcp permanent=yes state=enabled immediate=true"
ansible newserver -i /hosts -u root -m ansible.posix.firewalld -a "port=666/tcp permanent=yes state=enabled immediate=true"
5、SELinux只为宽容模式
[root@rhce ~]# ansible newserver -i /hosts -m selinux -a 'policy=targeted state=permissive'