Ansible模块速查指南:高效定位与实战技巧

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/int
default 可选参数的默认值(省略该参数时的默认行为) false/""/[]
required 显式标识是否必选(和 =/− 等效,部分手册会补充) true/false
choices 参数的可选值(只能传这些值,否则报错) [file, directory, link, absent](file 模块 state 参数)
added in 参数新增的版本(低版本 Ansible 不支持该参数) version 1.1 of ansible-core
适用条件文本 参数生效的前置条件(比如依赖其他参数的取值) This applies only whenstate' is set to directory'.
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.cfgremote_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.posixcommunity.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 系统'的诞生:

    1. 学术界(比如加州大学伯克利分校):基于早期 UNIX 的开源源码分支,开发出了BSD 系统(伯克利软件分发),这是最早的类 UNIX 系统之一,后来逐渐脱离 UNIX 版权约束,成为独立的开源类 UNIX 系统;

    2. 普通用户 / 开发者:因为 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个模块

早期模块以基础系统运维功能为主,分类简洁:

  1. 文件与权限管理

    • 模块:file(创建文件 / 目录、修改权限 / 属主)、copy(复制文件)、unarchive(解压)
  2. 用户与组管理

    • 模块:user(创建 / 修改用户)、group(创建 / 修改用户组)、authorized_key(配置 SSH 密钥)
  3. 软件包管理

    • 模块:yum(RHEL/CentOS)、apt(Debian/Ubuntu)、pip(Python 包)
  4. 系统服务管理

    • 模块:service(管理 sysvinit/systemd 服务)
  5. 命令与脚本执行

    • 模块:command(简单命令)、shell(支持 Shell 语法)、script(执行本地脚本)
  6. 网络与安全管理

    • 模块:iptables(传统防火墙)、nmcli(网络接口配置)
  7. 磁盘与存储管理

    • 模块:mount(挂载文件系统)、parted(磁盘分区)
  8. 配置与定时任务管理

    • 模块:lineinfile(修改单行配置)、cron(系统定时任务)

当前 Ansible 模块覆盖全运维场景,包含核心内置模块 + 第三方 Collections 模块,分类如下:

  1. 文件与权限管理

    • 核心模块:file(文件 / 目录、权限 / 属主)、copy(复制文件 / 内容)、template(模板渲染)、unarchive(解压)、get_url(下载文件)、synchronize(rsync 同步)
  2. 用户与身份管理

    • 核心模块:user(用户创建 / 修改)、group(用户组)、authorized_key(SSH 密钥)、seuser(SELinux 用户)
  3. 软件包管理

    • 系统包:yum/dnf(RHEL/CentOS 8+)、apt(Debian/Ubuntu)、package(自动适配系统包管理器)

    • 语言包:pip(Python)、gem(Ruby)、npm(Node.js)、go(Go)

  4. 系统服务与进程管理

    • 服务管理:systemd(systemd 服务)、service(兼容 sysvinit)、supervisorctl(Supervisor 进程)

    • 进程操作:ps(查询进程)、kill(终止进程)

  5. 命令与脚本执行

    • 模块:command(简单命令)、shell(支持 Shell 语法)、script(执行本地脚本)、raw(无 Python 依赖的命令)
  6. 网络与安全管理

    • 网络配置:nmcli(网络接口)、route(路由)、dnsmasq(DNS 服务)

    • 安全加固:firewalld(动态防火墙)、ufw(Ubuntu 防火墙)、selinux(SELinux 状态)、auditd(审计规则)

  7. 磁盘与存储管理

    • 模块:mount(挂载)、parted(磁盘分区)、lvg(逻辑卷组)、lvol(逻辑卷)、filesystem(创建文件系统)
  8. 配置文件管理

    • 模块:lineinfile(修改单行配置)、replace(文本替换)、ini_file(INI 文件)、xml(XML 文件)、json_file(JSON 文件)
  9. 数据存储管理

    • 数据库:mysql_db(MySQL/MariaDB)、postgresql_db(PostgreSQL)、mongodb(MongoDB)、redis(Redis)

    • 存储服务:nfs_server(NFS 服务)、gluster_volume(GlusterFS)

  10. 定时任务管理

    • 模块:cron(系统定时任务)、at(一次性任务)
  11. 云资源管理(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)

  12. 容器与虚拟化管理

    • 容器:community.docker.docker_container(Docker)、containers.podman.podman_container(Podman)、kubernetes.core.k8s(Kubernetes)

    • 虚拟化:libvirt.libvirt.vm(KVM)、vmware.vmware_rest.vcenter_vm(VMware)

  13. 源码与版本控制

    • 模块:git(Git 仓库)、svn(SVN)、hg(Mercurial)
  14. 工具与辅助模块

    • 模块:wait_for(等待端口 / 文件)、debug(调试输出)、pause(暂停任务)、assert(断言检查)、set_fact(设置变量)
  15. 消息与通知

    • 模块:slack(Slack 通知)、email(邮件)、telegram(Telegram)、wechat_work(企业微信)

command 模块

  1. 参数格式与执行范围command 模块接收「命令名 + 空格分隔的参数列表」作为输入,指定的命令会在所有选中的远程节点上执行。

  2. 核心限制(无 Shell 解析):该模块执行的命令

    不会经过远程节点的 Shell 解释器处理,因此以下内容均无法生效:

    • 环境变量(如 $HOSTNAME);

    • Shell 特殊操作符(如 "*" 通配符、</> 重定向、| 管道、;/& 多命令拼接)。

  3. 功能替代方案 :若需使用上述 Shell 特性,应替换为 [ansible.builtin.shell] 模块。

  4. 优化任务可读性:对于使用空格分隔参数导致可读性差的command任务,可通过两种方式传参:

    • 使用 args 任务关键字;

    • 使用 cmd 参数(推荐)。

  5. 必选参数要求 :必须指定「自由格式命令」或 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

  • 核心知识点

chdircommand模块的辅助参数,作用是「执行命令前切换到指定目录」,属于高频运维场景(避免写绝对路径)。

在 Ansible 中,模块参数和 指令(命令内容)是完全不同的东西:

  • 模块参数:是 Ansible 模块(比如这里的command模块)的 "配置项",由 Ansible 本身解析、处理,作用是控制模块的执行逻辑(比如执行指令前切换目录、判断文件是否存在等)。

    比如chdir=/etccommand模块的专属参数,功能是 "在执行指令前,先切换到/etc目录"。

  • 指令内容 :是模块要在目标主机上执行的 "实际命令",由目标主机的系统(比如 Linux)解析、执行,作用是完成具体操作(比如这里的cat redhat-release是读取文件内容)。

  • 注意点:chdir仅改变命令执行的目录,不能单独使用(必须搭配具体命令,否则报no command given错误)。

扩展1:Ansible 会先解析这模块参数

提问:为什么'cat redhat-release chdir=/etc'这个反这些也能够看到/etc目录下的redhat-realse

因为 chdir=/etccommand 模块的参数,而非命令的一部分 ------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 模块

  1. 参数格式要求shell 模块接收 "命令名 + 空格分隔的参数列表" 作为输入,必须指定「自由格式命令」或 cmd 参数(示例见前文),二者任选其一。

  2. command 模块的核心差异 :该模块与 [ansible.builtin.command] 模块功能几乎完全一致,唯一区别是 shell 模块会通过远程节点的 /bin/sh shell 解释器执行命令(支持管道、变量替换等 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 模块

  1. 参数格式要求script 模块接收「脚本名称 + 空格分隔的参数列表」作为输入,必须指定「自由格式命令」或 cmd 参数(具体用法可参考示例)。

  2. 脚本执行流程:位于本地路径的脚本会先被传输到远程节点(受控主机),随后在远程节点上执行。

  3. 执行环境特性:指定的脚本会通过远程节点的 Shell 环境进行处理(支持 Shell 语法,如变量、管道、重定向等)。

  4. script优势

    • command/shell 执行脚本的前提:你必须先通过copy/synchronize等模块,把脚本传到远程节点,才能执行;

    • script 模块无需提前传脚本:直接指定主控节点的脚本路径,Ansible 会自动完成「传输→执行」全流程,少一步操作。

  5. 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 模块的幂等性分两层:

  1. 「指令层」:直接执行无判断的原生系统指令 / 脚本 → 无幂等性;

  2. 「模块层」:利用模块自带的 creates/removes 等参数约束执行时机 → 具备幂等性。

常用模块:

copy
  1. 核心功能copy 模块用于将文件从「本地机器」或「远程机器」复制到远程机器的指定位置。

  2. 反向文件复制的替代方案 :若需将文件从远程位置复制到本地机器,应使用 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 挂载管理模块的核心参数,用于定义存储设备的挂载状态,各取值的具体行为如下:

  1. mounted

    • 设备会被主动挂载,且在 /etc/fstab(文件系统表)中完成对应的持久化配置;

    • 若指定的挂载点不存在,会自动创建该挂载点。

  2. unmounted

    • 仅卸载设备,不会修改 /etc/fstab 中的挂载配置。
  3. present

    • 仅将设备的挂载配置写入 /etc/fstab,不会触发实际挂载操作,也不要求设备处于已挂载状态。
  4. ephemeral(该选项新增于 1.5.0 版本)

    • 仅临时挂载设备,不修改 /etc/fstab

    • 若设备已挂载,会触发重新挂载操作,该取值始终返回 changed=True

    • 若挂载点 path 已挂载其他设备(且源设备与 src 指定的不一致),模块会执行失败,避免意外卸载现有设备或覆盖挂载点;

    • 若挂载点不存在,会自动创建挂载点;

    • 完全忽略 /etc/fstab 的配置。

  5. absent

    • 将设备的挂载条目从 /etc/fstab 中移除;

    • 同时卸载该设备,并删除对应的挂载点。

  6. remounted(该选项新增于 2.9 版本)

    • 用于强制刷新设备的挂载状态,触发设备重新挂载,该取值始终返回 changed=True

    • 若设置了 opts(挂载选项),这些选项会应用于重新挂载操作,但不会修改 /etc/fstab

    • 若设置了 opts 且重新挂载命令执行失败,模块会抛出错误,防止挂载配置发生意外变更(建议使用 mounted 取值规避此问题);

    • 该取值要求挂载点已存在于 /etc/fstab 中;

    • 若需重新挂载未在 /etc/fstab 中注册的挂载点(尤其针对 BSD 系统节点),请改用 ephemeral 取值。

  7. absent_from_fstab

    • 仅将设备的挂载条目从 /etc/fstab 中移除;

    • 不会卸载设备,也不会删除对应的挂载点。

参数基础属性补充

  • 参数类型:字符串(str);

  • 可选值:absentabsent_from_fstabmountedpresentunmountedremounted

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
  1. service 模块

    • 适合init 系统混合的环境(如同时管理 CentOS 6 和 CentOS 7 主机),兼容性强但功能有限;

    • 不推荐在纯 systemd 系统中使用(会浪费 systemd 的专属特性)。

  2. 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
  1. 核心功能 该模块用于确保文件中存在某一特定行,或通过带反向引用的正则表达式替换文件中已存在的行。

  2. 核心适用场景此模块主要适用于「仅需修改文件中某一行内容」的场景。

  3. 其他场景的替代模块建议

    • 若需修改文件中多行相似内容 ,请使用 ansible.builtin.replace 模块;

    • 若需在文件中「插入 / 更新 / 删除一整块多行内容」,请查看 ansible.builtin.blockinfile 模块;

    • 对于上述之外的其他场景(如完整替换文件、基于模板生成文件),请使用 ansible.builtin.copyansible.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
  1. 核心功能

    该模块会替换文件中所有匹配指定模式(pattern)的内容实例。

  2. 幂等性要求

    操作的幂等性需由用户自行保障,核心原则是:确保所使用的匹配模式不会匹配到任何通过该模块完成的替换结果(避免因重复匹配导致多次替换,破坏幂等性)

复制代码
示例:
- 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 版本之前,该选项仅能使用 destdestfilename 作为参数名。

  • 别名:destdestfilename(这三个名称与 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
  1. 核心功能 :该模块的作用是解压归档文件(如 .zip/.tar.gz/.tar.bz2 等);但明确限制 ------ 仅处理 "归档文件",无法解压「仅压缩但不含归档结构的单个文件」(比如纯 .gz 压缩的单个文本文件,而非归档包)。

  2. 默认执行逻辑:模块默认行为是「先将控制节点(本地)的源归档文件复制到目标远程主机,再在目标主机上执行解压操作」。

  3. 远程源文件解压配置 :若需解压「已存在于目标远程主机上」的归档文件,需设置参数 remote_src=yes(无需从本地复制文件)。

  4. 校验和验证的替代方案 :如果需要对归档文件做校验和(checksum)验证,不建议直接用该模块;应先通过 ansible.builtin.get_urlansible.builtin.uri 模块获取文件(自带校验和能力),再配合 remote_src=yesunarchive 解压。

  • name: 下载foo.conf文件

ansible.builtin.get_url:

url: http://example.com/path/file.conf # 待下载文件的远程URL

dest: /etc/foo.conf # 文件下载后在受控节点的保存路径

mode: '0440' # 下载后文件的权限设置为0440

get_url
  1. 核心功能:从 HTTP、HTTPS 或 FTP 协议地址下载文件至远程服务器。

  2. 网络访问要求 :远程服务器必须能够直接访问待下载的远程资源(即目标文件所在的网络地址)。

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. 核心功能 1:设置属性:设置文件、目录或符号链接(symlinks)及其目标对象的属性(如权限、所有者、所属组等)。

  2. 核心功能 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: trueimmediate: 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 实现核心逻辑:

  1. 先判断数据库 root 是否已设置密码;

  2. 动态适配登录参数(无密码 / 有密码);

  3. 利用模块幂等性参数,确保 "设置密码、创建库 / 用户" 仅执行一次。

复制代码
---
- 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_factsbecomePlay 级别通用参数(作用于整个 Play 的所有任务);

  • ignore_errorschanged_when任务级别通用参数(仅作用于单个任务);

check_implicit_admin

在 "初始化 root 密码" 的任务中,因为login_password: ""(无密码登录),如果不配置check_implicit_admin: yes,会触发 "权限不足" 错误,无法完成密码设置 ------ 这是无密码状态下给 root 设密码的必配参数

when: no_pwd_login.rc == 0

  1. when 类型 / 含义:Ansible 的核心关键字,专门用于定义 "任务是否执行" 的条件判断规则。深度拓展:当when后跟随的表达式结果为True时,当前 "初始化 root 密码" 的任务会执行;若表达式结果为False,该任务会被标记为skipped(跳过)。when支持 Python 风格的比较运算符(如==/!=/>/<)和逻辑运算符(如and/or/not),是实现 Ansible 任务条件执行的核心。

  2. no_pwd_login 类型 / 含义:前置 "测试 root 无密码能否登录" 任务的 "结果注册变量名"。深度拓展:这个变量来自前置任务里的register: no_pwd_login配置,Ansible 会把该 shell 任务的所有执行结果(包括返回码、命令输出、执行耗时、错误信息等)都存储在这个变量中,供后续任务调用,是 Ansible 跨任务传递执行结果的核心方式。

  3. .rc 类型 / 含义:no_pwd_login变量的子字段,全称是 return code(返回码 / 退出码),是 Linux 系统中判断命令执行结果的核心指标。深度拓展:Linux 系统通用规则为 ------rc == 0代表命令执行成功(对应场景:root 无密码登录 MySQL/MariaDB 成功);rc != 0(如 1、1045 等)代表命令执行失败(对应场景:root 无密码登录失败,说明已有密码)。相比解析命令输出内容,通过rc判断执行结果更稳定、不易出错。

  4. == 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
  1. 该模块会被 Playbook 自动调用,用于收集远程主机的有用变量,这些变量可在 Playbook 中直接使用。

  2. 也可通过 /usr/bin/ansible 命令直接执行该模块,以查看某台主机可用的变量。

  3. 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

  1. 该模块在 Playbook 执行过程中输出指定语句,可用于调试变量或表达式,且无需终止 Playbook 的执行。

  2. 该模块与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'
复制代码
相关推荐
zhengfei6112 小时前
AutoPentestX – Linux 自动化渗透测试和漏洞报告工具
linux·运维·自动化
我材不敲代码2 小时前
在Linux系统上安装MySQL
linux·运维·服务器
计算机毕业编程指导师2 小时前
大数据可视化毕设:Hadoop+Spark交通分析系统从零到上线 毕业设计 选题推荐 毕设选题 数据分析 机器学习 数据挖掘
大数据·hadoop·python·计算机·spark·毕业设计·城市交通
obboda2 小时前
CICD 部署与使用
运维
甘露s2 小时前
深入理解 Redis:事务、持久化与过期策略全解析
数据库·redis
计算机毕业编程指导师2 小时前
【计算机毕设选题】基于Spark的车辆排放分析:2026年热门大数据项目 毕业设计 选题推荐 毕设选题 数据分析 机器学习 数据挖掘
大数据·hadoop·python·计算机·spark·毕业设计·车辆排放
yuezhilangniao2 小时前
阿里云服务器Alibaba Cloud Linux 3 安装Python3.11简明指南
linux·运维·python3.11
珠海西格2 小时前
远动通信装置为何是电网安全运行的“神经中枢”?
大数据·服务器·网络·数据库·分布式·安全·区块链
小宇的天下2 小时前
Calibre 3Dstack --每日一个命令day18【floating_trace】(3-18)
服务器·前端·数据库