快速入门使用
功能
-
批量执行远程命令,可以对远程的多台主机同时进行命令的执行
-
批量安装和配置软件服务,可以对远程的多台主机进行自动化的方式配置和管理各种服务
-
编排高级的企业级复杂的IT架构任务, Ansible的Playbook和role可以轻松实现大型的IT复杂架构
-
提供自动化运维工具的开发API, 有很多运维工具,如jumpserver就是基于 ansible 实现自动化管理功能
ansible特性:
-
模块化:调用特定的模块完成特定任务,支持自定义模块,可使用任何编程语言写模块
-
基于Python语言实现
-
部署简单,基于python和SSH(默认已安装),agentless,无需代理不依赖PKI(无需ssl)安全,基于OpenSSH
-
幂等性:一个任务执行1遍和执行n遍效果一样,不因重复执行带来意外情况,此特性非绝对支持playbook编排任务,YAML格式,编排任务,支持丰富的数据结构
-
较强大的多层解决方案 role
Ansible 架构

架构拓扑
控制节点 (Ansible控制机) ←──→ 受控节点 (目标服务器)
│ │
└─ 执行Ansible命令 ────────────┘
控制节点命令 → SSH连接 → 受控节点执行 → 返回结果
↓ ↓ ↓
ansible命令 网络通信 实际安装配置
1. 环境准备(前置步骤)
→ 控制节点安装 Ansible
→ 控制节点配置 SSH 免密登录到受控节点(就是这一步)
→ 控制节点编写 inventory 文件(/etc/ansible/hosts)定义受控节点
2. 执行部署(核心步骤)
→ 选择纯剧本或剧本+角色方式
→ 执行 ansible-playbook 命令部署 LNMP+Discuz

1. 左侧:输入 / 数据源
-
USERS(用户):Ansible 的操作发起者,可从「公有 / 私有云」「CMDB(配置管理数据库)」获取资产信息,也可直接操作 Ansible。
-
CMDB:配置管理数据库,存储企业所有 IT 资产(服务器、网络设备等)的信息,会将资产数据同步到 Ansible 的「Inventory(主机清单)」。
-
ANSIBLE PLAYBOOK:用户编写的自动化任务编排文件(YAML 格式),是 Ansible 执行复杂任务的核心输入。
2. 中间:Ansible 自动化引擎(核心)
这是 Ansible 的 "大脑",包含 4 个核心组件:
-
INVENTORY(主机清单):存储受控节点(服务器、网络设备)的信息(IP、分组、变量等),数据来源可以是用户手动维护、CMDB 同步、云平台自动拉取。
-
API:Ansible 提供的编程接口,用于和其他系统(如运维平台、CI/CD 工具)集成,实现自动化流程的对接。
-
MODULES(模块) :Ansible 的 "功能单元",是执行具体任务的最小工具(比如
yum模块装软件、service模块启停服务),Ansible 通过调用模块完成对受控节点的操作。 -
PLUGINS(插件):扩展 Ansible 功能的组件(比如连接插件控制 SSH 连接、回调插件自定义输出格式),是 Ansible 灵活性的关键。
3. 右侧:管理对象
-
HOSTS(受控主机):服务器、虚拟机等计算节点,是 Ansible 的核心管理目标。
-
NETWORKING(网络设备):交换机、路由器等网络硬件,Ansible 也支持通过专用模块管理这类设备。
实验: rpm包安装ansible
[root@rhce ~]# wget https://mirrors.aliyun.com/epel/epel-release-latest-9.noarch.rpm
[root@rhce ~]# rpm -ivh epel-release-latest-9.noarch.rpm
[root@rhce ~]# yum install ansible -y
实验:主控节点与受控节点建立连接(密码登录)
写法1:
[root@rhce ~]# vi /etc/ansible/hosts
192.168.223.152 ansible_ssh_user=root ansible_ssh_port=22 ansible_ssh_pass=redhat
[root@rhce /]# vim /etc/ansible/ansible.cfg
[defaults]
host_key_checking=False
[root@rhce /]# ansible 192.168.223.152 -m ping
192.168.223.152 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
[root@rhce /]# ansible 192.168.223.152 -m shell -a "id -u"
Ansible 的输出颜色是对执行结果的直观区分,和 "成功 / 失败" 的对应关系很简单:
| 颜色 | 核心状态 | 含义 |
|---|---|---|
| 绿色 | SUCCESS |
操作成功,且没有产生任何状态变化(比如目标已经是挂载状态,无需再执行); |
| 黄色 | CHANGED |
操作成功,且产生了实际状态变化(比如之前未挂载,现在成功挂载了); |
| 红色 | FAILED |
操作失败(会明确显示错误信息,比如权限不足、设备不存在等)。 |
写法2:
[root@rhce /]# vim /etc/ansible/hosts
192.168.223.152 ansible_ssh_pass=redhat
[server]
192.168.223.152 ansible_ssh_pass=redhat
192.168.223.151 ansible_ssh_pass=redhat
[root@rhce /]# vim /etc/ansible/ansible.cfg
[defaults]
host_key_checking=False
[root@rhce /]# ansible server -m ping
192.168.223.151 | SUCCESS => {
"ansible_facts": {
#ansible_facts是 Ansible 中的 "事实集合" 字段 ------ 它专门用来存储 Ansible 从受控节点收集到的系统级基础信息(比如操作系统版本、IP 地址、Python 解释器路径、内存大小等),这些信息被称为「Ansible Facts」("事实")。
"discovered_interpreter_python": "/usr/bin/python3" #discovered_interpreter_python是 Ansible 自动探测到的 "可用 Python 解释器路径",确保后续推送的模块能正常被解释执行。
},
"changed": false,
"ping": "pong"
}
192.168.223.152 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
写法3:
[root@master ~]# ansible-config init --disabled > /ansible.cfg
#把/etc/ansible.cfg里的配置删除,然后配置/ansible.cfg,切换到当前目录下,ansible就不读取/etc/ansible.cfg,只读取/ansible.cfg并使生效
[root@master ~]# vim /ansible.cfg
[defaults]
host_key_checking=False
[root@master ~]# cd /
[root@rhce /]# vim /etc/ansible/hosts
[server]
192.168.223.15[1:2] ansible_ssh_pass=redhat
[root@rhce /]# ansible server -m ping
192.168.223.152 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
192.168.223.151 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
ansible.cfg的加载顺序:
1.环境变量 ANSIBLE_CONFIG 指定的路径:
#示例:若项目下的ansible.cfg在/root/my-ansib1e/目录下,执行:export ANSIBLE_CONFIG=/root/my-ansible/ansible.cfg
2.当前工作目录下的 ansible.cfg(即你项目目录下的配置);只需进入项目目录(ansib1e.cfg 所在的目录),再执行Ansib!e命令(如 ansible、ansib1e-p1aybook),Ansib!e会自动优先读取项目下的ansib1e.cfg,无需额外操作。
3.用户家目录的.ansible.cfg;
4.系统全局配置 /etc/ansible/ansible.cfg。
写法4:
[root@rhce /]# vim /hosts
[newserver]
192.168.223.15[1:2] ansible_ssh_user=redhat ansible_ssh_pass=redhat
[root@rhce /]# vim /ansible.cfg
[defaults]
host_key_checking=False
[root@rhce /]# cd /
[root@rhce /]# ansible newserver -i /hosts -m ping
192.168.223.151 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
192.168.223.152 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
清单来源的加载优先级(从高到低):
-
若你在执行命令时用了 `-i /tmp/test-hosts`,无论环境变量 /ansible.cfg/ 默认路径有没有 hosts,Ansible 只会读取 `/tmp/test-hosts`;
-
若没指定命令行 `-i`,但环境变量 `ANSIBLE_INVENTORY` 配置了路径,就用环境变量指定的 hosts;
-
若以上都没配置,才会读 `ansible.cfg` 中 `inventory` 项指定的 hosts;
-
只有前 3 项都没配置时,才会用系统默认的 `/etc/ansible/hosts`。
`/etc/ansible/hosts` 是 Ansible 管理受控节点的**主机清单文件**,支持多种写法以适配不同场景,以下是常用格式:
实验:主控节点与受控节点建立连接(密钥登录)
[root@rhce /]# cat /hosts
[newserver]
192.168.223.15[1:2] ansible_ssh_private_key_file=/root/.ssh/id_rsa
[root@rhce /]# cat /ansible.cfg
[defaults]
remote_user = root
timeout = 3
host_key_checking=False
[root@rhce /]# ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_rsa
Your public key has been saved in /root/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:V2LfaWLetM+MIES+iKOJWFXhOG7DtKFbidPgc/zA/hk root@rhce
The key's randomart image is:
+---[RSA 3072]----+
| . |
| o . |
| . = o + . |
| . @ * + + . . |
| * / S + + = |
| X + . + + = . |
| o . E . o o o |
| o . + + . . = |
|. . o o . +|
+----[SHA256]-----+
[root@rhce /]# ansible newserver -m authorized_key -a "user=root state=present key='{{ lookup('file', '/root/.ssh/id_rsa.pub') }}'" -e "ansible_ssh_pass=redhat" -i /hosts
192.168.223.151 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": true,
"comment": null,
"exclusive": false,
"follow": false,
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCMlu3p/HeB0XB1nMkKDT+chy53xpj72T55yambR5X/GhrXe1nKc2oqjhto/oS4zuGic/aAMw6ODRaPmFMCdSsf+ZwuOY3AgftCjb4nYdHXFItLJ8qAKtN/oQBvU57byBsH4aeLs0MITJppnzKBmwb963imgfVAN+vEgXUYMcWBWjHo3GuylYjpi0E1xyMuoXu56Fe3BOkWtHLfgC/LXZOcUJwCj69aom54e8QozUbFQWlechZPG1GqkPnBx8dHR1Sx1TRmccEW9wwhrfWSNXponHnuISQrFV2cu+kPaHSvpwTVerEZFppLjtsWkVWi50VJ+ng8mjafU7po1+sx4C7HOwl/FS7SSVmqN1MrZVZcL02OalXOO0P3egM77ToggOXtMe3WpOEGywEKG22JmlcK6HUV6E/AABZ1u/3ENKSKvcR4Rhg3mp3noxjKIyesQQbpmxt4f6bOkeElZv4/tdKb4PcPZEwyavGjLAl5BxdyiQfRC0pGBqHvBkI3wDC5UlE= root@rhce",
"key_options": null,
"keyfile": "/root/.ssh/authorized_keys",
"manage_dir": true,
"path": null,
"state": "present",
"user": "root",
"validate_certs": true
}
192.168.223.152 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": true,
"comment": null,
"exclusive": false,
"follow": false,
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCMlu3p/HeB0XB1nMkKDT+chy53xpj72T55yambR5X/GhrXe1nKc2oqjhto/oS4zuGic/aAMw6ODRaPmFMCdSsf+ZwuOY3AgftCjb4nYdHXFItLJ8qAKtN/oQBvU57byBsH4aeLs0MITJppnzKBmwb963imgfVAN+vEgXUYMcWBWjHo3GuylYjpi0E1xyMuoXu56Fe3BOkWtHLfgC/LXZOcUJwCj69aom54e8QozUbFQWlechZPG1GqkPnBx8dHR1Sx1TRmccEW9wwhrfWSNXponHnuISQrFV2cu+kPaHSvpwTVerEZFppLjtsWkVWi50VJ+ng8mjafU7po1+sx4C7HOwl/FS7SSVmqN1MrZVZcL02OalXOO0P3egM77ToggOXtMe3WpOEGywEKG22JmlcK6HUV6E/AABZ1u/3ENKSKvcR4Rhg3mp3noxjKIyesQQbpmxt4f6bOkeElZv4/tdKb4PcPZEwyavGjLAl5BxdyiQfRC0pGBqHvBkI3wDC5UlE= root@rhce",
"key_options": null,
"keyfile": "/root/.ssh/authorized_keys",
"manage_dir": true,
"path": null,
"state": "present",
"user": "root",
"validate_certs": true
}
说明:
1.命令行中 key=后的公钥内容:
o若用 1ookup 函数,需保证引号嵌套正确(外层双引号,内层单引号);
。若直接粘贴公钥字符串,需去掉 {{ 1ookup(...)}},直接写公钥(如 key='ssh-ed25519AAAxxx... root@rhce')
2.权限问题: authorized_key 模块会自动将 authorized_keys 文件权限设为 600(符合 SSH 要求)无需手动修改。
3.免密验证:推送完成后,执行 ansible servers -m ping 验证免密是否生效(无需再传 -eansible_ssh pass)。
yam!格式重写写authorized_key模块:
[root@rhce /]# ansible newserver -m authorized_key -a "user=root state=absent key='{{ lookup('file', '/root/.ssh/id_rsa.pub') }}'" -i /hosts
[root@rhce /]# cat /hosts
[newserver]
192.168.223.15[1:2] ansible_ssh_private_key_file=/root/.ssh/id_rsa
[root@rhce /]# cat /auth.yaml
---
- hosts: newserver # 目标主机组(对应你清单里的[newserver])
gather_facts: no # 关闭事实收集(首次推送公钥时,受控节点可能未配置Python依赖)
vars:
ansible_ssh_pass: "redhat"
ansible_host_key_checking: False
tasks:
- name: 给root用户添加SSH公钥(Ansible管理)
authorized_key:
user: root # 指定目标用户(必填)
state: present # 确保公钥存在(添加)
key: "{{ lookup('file', '/root/.ssh/id_rsa.pub') }}" # 读取本地公钥
[root@rhce /]# ansible-playbook /auth.yaml -i /hosts
模块
帮助
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%)
标识参数是否必选:= 参数名 → 必选;- 参数名 → 可选
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 # 提取执行,默认提权到root用户,是否能提权到root,还要看受控节点的/etc/sudoers文件有没有该用户
--become-user=USERNAME # 指定sudo的runas用户,默认用户为root,指定用户提权到哪个用户
-K, --ask-become-pass # 提示输入sudo操作的口令,默认就是sudoers文件里面的用户的登录密码
范例:将普通用户提升权限
#先在受控节点sudo授权
[root@node1 ~]# vim /etc/sudoers
hehe ALL=(ALL) NOPASSWD: ALL
[root@node1 ~]# echo redhat | passwd hehe --stdin
更改用户 hehe 的密码 。
passwd:所有的身份验证令牌已经成功更新。
注意:-u hehe是受控节点的用户,并且该用户需要写到/etc/sudoers
#ansible要成功执行模块需要::确保ansible能够连接到受控节点,然后再做下面的指令
#以hehe用户连接,并利用sudo使用hehe用户提权,执行cat /etc/shadow
#如果远程登录受控节点的hehe用户,并且以密码的方式登录:-u hehe -k,由于ansible已经把主控节点的公钥保存到受控节点的hehe用户的家目录,所以不要-k也可远程能连接
#-b 默认将hehe提权到root用户,需要输入hehe用户
[root@rhce ~]# ansible 192.168.223.151 -m shell -a 'cat /etc/shadow' -u hehe -k -b
#如果是创建/hosts、/ansible.cfg文件,则需要加-i /hosts :ansible 192.168.60.133 -i /hosts -m shell -a 'cat /etc/shadow' -u xixi -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(企业微信)
- 模块:
幂等性
定义:多次执行同一个 Ansible 操作,最终结果和执行一次完全一致,不会产生冗余变更、不会重复创建资源、不会报错,就像 "开关灯"------ 按一次开灯,再按多次还是开灯状态,不会有额外变化。
先明确两个关键概念
1. 声明式模块(原生支持幂等)
这类模块的核心是「声明 "要达到什么状态",而非 "要执行什么动作"」,模块会自动对比 "当前实际状态" 和 "目标状态",仅当二者不一致时才执行操作,这是幂等性的本质。
-
典型模块:
file、package、service、mount、firewalld等。 -
状态类参数:这类模块中用于声明目标状态的参数(如
state、enabled等),是实现幂等的关键载体。
2. 命令式模块(无原生幂等)
这类模块的核心是「单纯执行指定动作」,不会判断目标状态,也不会对比当前状态,仅机械执行命令,因此无原生幂等性。
-
典型模块:
command、shell、raw等。 -
无状态类参数:这类模块仅接收 "要执行的命令 / 动作" 作为参数,不支持声明目标状态。
注意:并不是模块的参数都单独具有幂等性
以file模块创建目录为例:
- name: 确保目录存在
file:
path: /tmp/test
state: directory # 状态类参数:声明"目标状态是目录存在"
-
参数的作用 :
state: directory只是告诉模块 "我的目标是让/tmp/test成为目录并存在",它本身不具备幂等能力; -
模块内置逻辑的作用:
file模块会先检查/tmp/test的当前状态:-
若目录不存在:执行创建动作(
changed: true); -
若目录已存在:不执行任何操作(
ok: true);
-
-
幂等性的实现 :是 "
state参数声明目标状态" + "模块内置的状态对比逻辑",共同让多次执行结果一致,而非state参数单独实现了幂等。
如果只保留参数,去掉模块内置逻辑(比如手动用command模块执行mkdir),即使有类似 "创建目录" 的参数,也无法实现幂等(多次执行会报错)。
command 模块本身不原生支持幂等性 (它是 "单纯执行命令动作",而非 "确保某种状态"),比如直接用 command: mkdir /tmp/test,第一次执行成功,第二次会报错 "目录已存在"。要实现幂等性,需通过「先检查状态,再条件执行」的逻辑补充
[root@rhce ~]# ansible 192.168.223.151 -m command -a 'mkdir /data'
192.168.223.151 | CHANGED | rc=0 >>
[root@rhce ~]# ansible 192.168.223.151 -m command -a 'mkdir /data '
192.168.223.151 | FAILED | rc=1 >>
mkdir: 无法创建目录 "/data": 文件已存在non-zero return code
#creates先判断/data文件是否存在,存在就不执行指令,不存在就执行指令
[root@rhce ~]# ansible 192.168.223.151 -m command -a 'mkdir /data creates=/data '
192.168.223.151 | SUCCESS | rc=0 >>
skipped, since /data existsDid not run command since '/data' exists
command 模块
-
参数格式与执行范围 :
command模块接收「命令名 + 空格分隔的参数列表」作为输入,指定的命令会在所有选中的远程节点上执行。 -
command模块通过cmd参数或args关键字,无法实现同时执行多个指令 -
核心限制(无 Shell 解析):该模块执行的命令
不会经过远程节点的 Shell 解释器处理,因此以下内容均无法生效:
-
环境变量(如
$HOSTNAME); -
Shell 特殊操作符(如
"*"通配符、</>重定向、|管道、;/&多命令拼接)。
-
-
功能替代方案 :若需使用shell特殊操作符,应替换为
[ansible.builtin.shell]模块。
说明:如果能用对应模块解决问题的,尽量就不要用command或者shell,因为对应的模块会更安全,且模块会具有幂等性特点
语法:
# '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
#args 是 Ansible 用于优化「命令执行类任务」的通用关键字,核心作用是传递命令的附加执行选项,而非命令自身参数,它能让命令执行更灵活(如切换工作目录)、更安全(如条件跳过实现幂等),写法也比直接写复合 Shell 命令更规范、更易维护。
#若需要控制命令的执行规则(如切换目录、条件跳过),用 args 关键字;
#若需要安全传递命令参数(尤其是带空格、特殊字符的参数),用 argv 参数
# 'argv'是模块参数,需相对于模块名缩进一层
- name: 使用'argv'以列表形式传递命令------将'command'字段留空
ansible.builtin.command:
argv:
- /usr/bin/make_database.sh
- Username with whitespace # 含空格的用户名(argv自动处理空格问题)
- dbname with whitespace # 含空格的数据库名
creates: /path/to/database
#argv 是 command模块(优先)/shell模块的专属参数,属于模块内部参数,作用是「以列表形式传递命令及其参数」,替代传统的字符串命令写法,避免特殊字符(空格、引号)导致的解析错误。
实例:
注意:区分指令和参数
指令:touch、ls。。。
参数:模块command的参数 chdir removes creates
#creates先判断如果/datafile是否存在,如果存在,就不执行指令;如果不存在,就执行指令
[root@rhce ~]# ansible 192.168.223.151 -m command -a 'touch /datafile creates=/datafile'
192.168.223.151 | CHANGED | rc=0 >>
[root@rhce ~]# ansible 192.168.223.151 -m command -a 'ls -l /datafile'
192.168.223.151 | CHANGED | rc=0 >>
-rw-r--r--. 1 root root 0 12月 19 20:37 /datafile
#removes先判断/datafile是否存在,如果存在,就执行指令;如果不存在,就不执行指令
[root@rhce ~]# ansible 192.168.223.151 -m command -a 'touch /datafile removes=/datafile'
192.168.223.151 | CHANGED | rc=0 >>
[root@rhce ~]# ansible 192.168.223.151 -m command -a 'ls -l /datafile'
192.168.223.151 | CHANGED | rc=0 >>
-rw-r--r--. 1 root root 0 12月 19 20:39 /datafile
[root@rhce ~]# ansible 192.168.223.151 -m command -a 'rm -rf /datafile removes=/datafile'
192.168.223.151 | CHANGED | rc=0 >>
[root@rhce ~]# ansible 192.168.223.151 -m command -a 'ls -l /datafile'
192.168.223.151 | FAILED | rc=2 >>
ls: 无法访问 '/datafile': 没有那个文件或目录non-zero return code
- name: Run command if /path/to/database does not exist (with 'cmd' parameter)
ansible.builtin.command:
cmd: /usr/bin/make_database.sh db_user db_name
creates: /path/to/database
chdir: /etc
扩展:playbook调用模块
[root@rhce ~]# cat com.yaml
---
- name: 测试提权
hosts: 192.168.223.151
remote_user: redhat
gather_facts: no
tasks:
- name: 查看shadow文件,以redhat身份执行
command: cat /etc/shadow
become: yes
become_user: root
args:
creates: /data2
[root@rhce ~]# ansible-playbook -i /hosts com.yaml -K
BECOME password:
PLAY [192.168.223.151] *********************************************************
==============说明==============
# 整体格式说明:
# 1. 所有缩进必须使用【半角空格】,禁止使用全角空格/制表符(Tab),默认2个空格为1个缩进层级
# 2. 键值对格式为「key: value」,冒号后必须跟1个半角空格,不能直接跟内容
# 3. YAML列表项以「-」开头,「-」后必须跟1个半角空格
# 4. 顶层无缩进,每向下一个层级,增加2个半角空格
扩展1:Ansible 会先解析模块参数
-
要求 :在
newserver主机上,切换到/etc目录后读取redhat-release文件内容(指定 inventory 文件/hosts,提权执行)。ansible newserver -m command -a 'chdir=/etc cat redhat-release' -i /hosts
Ansible 会先解析模块参数

提问:为什么'cat redhat-release chdir=/etc'这个反这些也能够看到/etc目录下的redhat-realse
因为 chdir=/etc 是 command 模块的参数,而非命令的一部分 ------Ansible 会先解析这个参数,在执行 cat redhat-release 命令前,自动切换到 /etc 目录 ,所以此时 cat 读取的是 /etc 目录下的 redhat-release 文件
扩展2:验证「不支持 Shell 特殊语法」(管道 /&&/ 重定向)
-
要求 :尝试用
command模块执行cat /etc/passwd | grep root(过滤 root 用户),观察结果并分析。 -
错误命令
#command 不支持管道
、重定向、$VARNAME、;、&等 Shell 语法
ansible newserver -i /hosts -m command -a 'echo redhat |passwd --stdin wang'
ansible newserver -i /hosts -m command -a 'rm -rf /data/'
ansible newserver -i /hosts -m command -a 'echo hello > /data/hello.log'
ansible newserver -i /hosts -m command -a "echo $HOSTNAME"
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:执行管道 && 组合命令
-
shell模块通过/bin/sh(可指定executable=/bin/bash)解析命令,支持管道、重定向、&&等所有 Shell 语法。ansible newserver -m shell -a 'cat /etc/passwd | grep root > /tmp/root_user.txt' -i /hosts
-
问题:调用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:SHELL支持环境变量
[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 预处理的复杂命令(含变量、管道、逻辑分隔符) |
注意:shell打印环境变量这功能是不稳定的,了解即可
script 模块
-
位于本地路径的脚本会先被传输到远程节点(受控主机),随后在远程节点上执行。
-
脚本会通过远程节点的 Shell 环境进行处理(支持 Shell 语法,如变量、管道、重定向等)。
-
script优势
-
command/shell执行脚本的前提:你必须先通过copy/synchronize等模块,把脚本传到远程节点,才能执行; -
script模块无需提前传脚本:直接指定主控节点的脚本路径,Ansible 会自动完成「传输→执行」全流程,少一步操作。
-
-
script脚本执行流程
-
主控节点 :r权限,读取
/root/init.sh脚本内容; -
受控节点:Ansible 自动创建临时目录;
-
传输 :把主控的
init.sh传输到这个临时目录; -
执行:在受控节点的临时目录中执行该脚本(执行时会通过远程 Shell 解析);
-
后续:执行完成后,临时目录和脚本默认不会自动删除(避免排查问题时无日志)。
简单说:脚本的执行是 "临时目录一次性执行",不会自动放到受控节点的
/root、/usr/bin等常规目录。-
name: 直接执行本地脚本(自动传输+执行)
ansible.builtin.script: /root/init.sh -
name: 运行带参数的脚本(使用'cmd'参数)
ansible.builtin.script:
cmd: /some/local/script.sh 传参1 传参2 传参3 -
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 # 受控节点上的文件,存在则执行脚本
-
扩展1:提权执行脚本
-
要求 :将本地
/root/3.sh脚本上传到192.168.223.151主机执行。[root@rhce ~]# vim /3.sh
#不给test.sh加x权限,ansible照样可以执行
[root@rhce ~]# cat /3.sh
#!/bin/bash
cat /etc/shadow -
正确命令
#成功原因:ansible默认远程连接root用户,以root什么调用模块,权限是足够的
[root@rhce ~]# ansible 192.168.223.151 -m script -a '/3.sh' -i /hosts
192.168.223.151 | CHANGED => {#失败原因:redhat作为普通用户没有权限cat /etc/shadow
[root@rhce ~]# ansible 192.168.223.151 -m script -a '/3.sh' -i /hosts -u redhat
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: NoneType: None
192.168.223.151 | FAILED! => {#成功原因:由于脚本里面的指令需要提高权限才能使用,所以提权
[root@rhce ~]# ansible 192.168.223.151 -m script -a '/3.sh' -i /hosts -u redhat -b -K
BECOME password:
192.168.223.151 | CHANGED => {
习题 2:脚本带参数执行
主控节点编辑脚本:
[root@rhce ~]# cat /root/1.sh
#!/bin/bash
mkdir $1 $2 $3 $4
#注意:第一次执行没报错
[root@rhce ~]# ansible 192.168.223.151 -m script -a '/root/1.sh he1 he2 he3 he4 chdir=/data'
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": "",
"stdout_lines": []
}
[root@node ~]# tree /data #受控节点
/data
├── he1
├── he2
├── he3
└── he4
#第二次执行:报错,原因是目前的参数都没有原生的幂等性判断逻辑
[root@rhce ~]# ansible 192.168.223.151 -m script -a '/root/1.sh he1_dir he2_dir he3_dir he4_dir chdir=/data'
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": 1,
"stderr": "Shared connection to 192.168.223.151 closed.\r\n",
"stderr_lines": [
"Shared connection to 192.168.223.151 closed."
],
"stdout": "mkdir: 无法创建目录 "he1_dir": 文件已存在\r\nmkdir: 无法创建目录 "he2_dir": 文件已存在\r\nmkdir: 无法创建目录 "he3_dir": 文件已存在\r\nmkdir: 无法创建目录 "he4_dir": 文件已存在\r\n",
"stdout_lines": [
"mkdir: 无法创建目录 "he1_dir": 文件已存在",
"mkdir: 无法创建目录 "he2_dir": 文件已存在",
"mkdir: 无法创建目录 "he3_dir": 文件已存在",
"mkdir: 无法创建目录 "he4_dir": 文件已存在"
]
}
#第三次执行:改进,加上creates保证幂等
[root@rhce ~]# ansible 192.168.223.151 -m script -a '/root/1.sh he1_dir he2_dir he3_dir he4_dir chdir=/data creates=/data/he1_dir'
192.168.223.151 | SKIPPED
#注意:参数若包含空格 / 特殊字符,需用引号包裹(如`'/root/create_file.sh "test file.txt"'`)。
ansible newserver -m script -a '/root/create_file.sh test_param.txt' -i /hosts
扩展:
================扩展:ansible的命令行调用模块--->ansible的playbook调用模块=======================
[root@rhce ~]# cat 1.sh
#!/bin/bash
mkdir $1 $2 $3 $4
[root@rhce ~]# cat 1.yaml
---
- hosts: 192.168.223.151
tasks:
- name: 用argv参数结构化传递多个参数(含特殊字符)
ansible.builtin.script:
cmd: /root/1.sh ha1 ha2 ha3 ha4
args:
chdir: /data
creates: /data/ha1 #重点注意:当/data/ha1存在就不执行cmd模块
[root@rhce ~]# ansible-playbook -i /hosts /root/1.yaml
PLAY [192.168.223.151] *********************************************************
TASK [Gathering Facts] *********************************************************
ok: [192.168.223.151]
TASK [用argv参数结构化传递多个参数(含特殊字符)] ******************************
skipping: [192.168.223.151]
PLAY RECAP *********************************************************************
192.168.223.151 : ok=1 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
==============说明==============
# 整体格式说明:
# 1. 所有缩进必须使用【半角空格】,禁止使用全角空格/制表符(Tab),默认2个空格为1个缩进层级
# 2. 键值对格式为「key: value」,冒号后必须跟1个半角空格,不能直接跟内容
# 3. YAML列表项以「-」开头,「-」后必须跟1个半角空格
# 4. 顶层无缩进,每向下一个层级,增加2个半角空格
其他常用模块:
copy
-
核心功能 :
copy模块用于将文件从「本地机器」或「远程机器」复制到远程机器的指定位置。 -
反向文件复制的替代方案 :若需将文件从远程位置复制到本地机器,应使用
ansible.builtin.fetch模块(copy模块不支持该反向操作)。-
name: 复制文件并设置所有者及权限
ansible.builtin.copy:
src: /srv/myfiles/foo.conf
dest: /etc/foo.conf ##如目标存在,默认覆盖,此处指定先备份,如果不存在/etc/foo.conf,则复制过来并创建
#如果是个dest这里是个目录,则不改变源文件的名字
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"
}
实例:playbook的方式调用模块
---
- name: 给web服务器部署nginx # Play的名字(方便看日志)
hosts: webservers # 这个Play要操作的主机(对应你的Hosts列表)
tasks: # 这个Play里的Task列表
- name: 配置仓库base
yum_repository:
file: x
name: base
description: base
state: present
enabled: yes
gpgcheck: no
baseurl: /mnt/BaseOS
- name: 配置仓库app
yum_repository:
file: x
name: app
description: app
state: present
enabled: yes
gpgcheck: no
baseurl: /mnt/AppStream
- name: 挂载光盘
mount:
path: /mnt
src: /dev/sr0
state: mounted
fstype: iso9660 # 光盘专属文件系统类型(必须加)
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
- name: Mount DVD read-only
实例:
#写入文件/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是 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在使用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
在 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;
#删除这行:\1listen 100;
[root@rhce ~]# ansible 192.168.223.151 -i /hosts -m lineinfile -a "path=/etc/nginx/nginx.conf search_string='\1listen 100;' state=absent"
192.168.223.151 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"backup": "",
"changed": true,
"found": 1,
"msg": "1 line(s) removed"
}
[root@rhce ~]# ansible 192.168.223.151 -i /hosts -m lineinfile -a "path=/etc/nginx/nginx.conf search_string=' listen 80;' line='listen 97;'"
192.168.223.151 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"backup": "",
"changed": true,
"msg": "line replaced"
}
[root@rhce ~]# ansible 192.168.223.151 -i /hosts -m shell -a 'grep listen /etc/nginx/nginx.conf'
192.168.223.151 | CHANGED | rc=0 >>
listen 97;
# listen 443 ssl http2;
# listen [::]:443 ssl http2;
[root@rhce ~]# ansible 192.168.223.151 -i /hosts -m lineinfile -a "path=/etc/nginx/nginx.conf search_string='listen 97;' line=' listen 97;'"
192.168.223.151 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"backup": "",
"changed": true,
"msg": "line replaced"
}
[root@rhce ~]# ansible 192.168.223.151 -i /hosts -m shell -a 'grep listen /etc/nginx/nginx.conf'
192.168.223.151 | CHANGED | rc=0 >>
listen 97;
# listen 443 ssl http2;
# listen [::]:443 ssl http2;
扩展参数:
search_string 是 lineinfile 模块中基于 "字面量字符串" 匹配文件内容 的参数(区别于 regexp 的正则匹配),用于精准查找固定字符串(特殊字符无需转义),并配合 state 参数实现 "替换 / 删除 / 添加" 行的操作。
核心特性:字面量匹配(关键区别于 regexp):
-
匹配规则:严格按 "字符原样" 匹配,不会将
.、*、|等视为正则符号; -
示例:若
search_string: "*.conf",只会匹配包含*.conf的行,而非正则中的 "任意.conf 文件"。
replace
-
核心功能
该模块会替换文件中所有匹配指定模式(pattern)的内容实例。
-
幂等性要求
操作的幂等性需由用户自行保障,核心原则是:确保所使用的匹配模式不会匹配到任何通过该模块完成的替换结果(避免因重复匹配导致多次替换,破坏幂等性)
示例:
-
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参数会产生与预期相反的效果(内容截断)
-
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/hosts
regexp: '\b(localhost)(\d*)\b'
replace: '\1\2.localdomain\2 \1\2'
-
实例:
[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(无需从本地复制文件)。-
name: 下载foo.conf文件
ansible.builtin.get_url:
url: http://example.com/path/file.conf # 待下载文件的远程URL
dest: /etc/foo.conf # 文件下载后在受控节点的保存路径
mode: '0440' # 下载后文件的权限设置为0440- name: 解压本地压缩包(test.tar.gz)到受控节点/tmp/test_unarchive
ansible.builtin.unarchive:
src: /root/test.tar.gz # 控制节点本地压缩包的绝对路径(支持.tar/.tar.gz/.zip等)
dest: /tmp/test_unarchive # 受控节点的解压目标目录(必须存在)
backup: no # 可选:是否备份已存在的目标文件(默认no)
mode: 0755 # 可选:解压后文件/目录的权限(仅对新增文件生效)
# remote_src: no # 默认值,可省略,标识src是控制节点本地路径
- name: 解压本地压缩包(test.tar.gz)到受控节点/tmp/test_unarchive
核心任务:解压远程主机上已存在的压缩包(remote_src=yes)
- name: 解压远程主机本地压缩包到目标目录 ansible.builtin.unarchive: src: /root/remote_archive.tar.gz # 远程主机上的压缩包绝对路径(必填) dest: /tmp/remote_unarchive # 远程主机的解压目标目录(必填,必须存在) remote_src: yes # 关键参数:标识src是远程主机本地路径 mode: 0644 # 可选:解压后文件的权限(仅新增文件生效) owner: root # 可选:解压后文件所属用户 group: root # 可选:解压后文件所属组 backup: no # 可选:目标目录有同名文件时是否备份(默认no) -
实例:
常见参数:
copy:默认为yes,当copy=yes,拷贝的文件是从ansible主机复制到远程主机上,如果设置为copy=no,会在远程主机上寻找src源文件
remote_src:和copy功能一样且互斥,yes表示在远程主机,不在ansible主机,no表示文件在ansible主机上
src:源路径,可以是ansible主机上的路径,也可以是远程主机(被管理端或者第三方主机)上的路径,如果是远程主机上的路径,则需要设置copy=no
dest:远程主机上的目标路径,该目录要确保存在
mode:设置解压缩后的文件权限
ansible all -m unarchive -a 'src=/data/foo.tgz dest=/var/lib/foo owner=redhat group=redhat'
ansible all -m unarchive -a 'src=/tmp/foo.zip dest=/data copy=no mode=0777'
ansible all -m unarchive -a 'src=https://example.com/example.zip dest=/data copy=no'
ansible websrvs -m unarchive -a 'src=https://releases.ansible.com/ansible/ansible-2.1.6.0-0.1.rc1.tar.gz dest=/data/ owner=root remote_src=yes'
ansible websrvs -m unarchive -a 'src=http://nginx.org/download/nginx-1.18.0.tar.gz dest=/usr/local/src/ copy=no
get_url
-
核心功能:从 HTTP、HTTPS 或 FTP 协议地址下载文件至远程服务器。
-
网络访问要求 :远程服务器必须能够直接访问待下载的远程资源(即目标文件所在的网络地址)。
get_url 模块默认幂等:仅当「远程文件更新」或「本地文件不存在 / 校验和不一致」时才重新下载,避免重复操作。
- name: 下载foo.conf文件
ansible.builtin.get_url:
url: http://example.com/path/file.conf # 待下载文件的远程URL
dest: /etc/foo.conf # 文件下载后在受控节点的保存路径
mode: '0440' # 下载后文件的权限设置为0440
实例
#若 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 # 配置永久生效(重启防火墙后不失效)
immediate: true
state: enabled # 启用该规则(允许流量)
- name: 禁止默认区域的8081/TCP端口流量
ansible.posix.firewalld:
port: 8081/tcp # 目标端口(8081端口,TCP协议)
permanent: true # 配置永久生效
immediate: true
state: disabled # 禁用该规则(禁止流量)
immediate参数说明
- 当
permanent: true且immediate: true时:配置会同时写入防火墙配置文件(持久化)和立即应用到运行时状态,无需额外执行重载命令;
实例:
[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
先判断数据库 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密码(仅第一次执行)
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: 创建luntan数据库(多次执行不报错)
mysql_db:
name: luntan
state: present # 幂等:确保库存在,不存在才创建动态适配密码:有密码用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
# 查看单个节点的全量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后面的变量有时需要加 " " 引起来
范例1: 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
范例2: 显示字符串特定字符
[root@rhce ~]# vim debug.yml
[root@rhce ~]# cat debug.yml
---
- hosts: newserver
gather_facts: no
vars:
a: "12345" # 仅用半角空格缩进(2/4个,统一即可)
tasks:
- debug: # 缩进对齐,冒号后加空格(可选,但规范)
msg: "{{a[2]}}" # 比debug多2个半角空格,无全角字符
[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
范例3:调试 "仅当命令执行失败时输出信息",可结合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
范例4:打印注册变量
---
- name: 测试提权
hosts: 192.168.223.151
remote_user: redhat
gather_facts: no
tasks:
- name: 打印当前登录的用户名称
command: whoami
register: current_user # 注册第一个任务的结果(成功时获取stdout)
- name: 输出提权前用户的标准输出
debug:
var: current_user.stdout # 输出标准输出
- name: 查看shadow文件(提权执行)
command: cat /etc/shadow
become: yes
become_user: root
args:
creates: /data2 # 若/data2存在则跳过任务,可临时删除该配置测试失败场景
register: shadow_result # 注册该任务的结果(单独注册,避免覆盖前一个变量)
- name: 输出shadow任务的各类结果(包括标准错误)
debug:
msg:
- "标准输出:{{ shadow_result.stdout }}"
- "标准错误:{{ shadow_result.stderr }}"
- "返回码:{{ shadow_result.rc }}"
======================================
场景 1:任务执行成功(提权生效,redhat 用户有 sudo 权限):
shadow_result.stdout:会获取到 /etc/shadow 文件的内容(标准输出);
shadow_result.stderr:为空字符串(无错误输出);
shadow_result.rc:返回 0(执行成功)。
场景 2:任务执行失败(比如未配置免密提权,或取消become: yes):
shadow_result.stdout:为空字符串(无正常输出);
shadow_result.stderr:会获取到错误信息(如 cat: /etc/shadow: 权限不够);
shadow_result.rc:返回非 0 值(如 1,表示执行失败);
扩展5:理解字典变量
字典(也叫键值对)就像一本带标签的信息登记册,每一条信息都有一个唯一的 "标签"(称为「键 /key」),对应一段具体的 "内容"(称为「值 /value」),通过 "标签" 就能快速找到对应的 "内容"。
比如:一个人的信息登记册(字典),结构如下:
标签(键 /key) 内容(值 /value) 姓名 张三 年龄 28 职业 运维工程师 当你用
register: 变量名注册任务结果时,Ansible 会自动把这个任务的所有执行信息,按照 "标签(键)+ 内容(值)" 的格式整理成一个字典,这个 "变量名" 就是整个字典的名称,通过「变量名.键」就能获取对应的执行信息。例如:
场景 1:任务执行成功(提权生效,redhat 用户有 sudo 权限):
shadow_result.stdout:会获取到/etc/shadow文件的内容(标准输出);
shadow_result.stderr:为空字符串(无错误输出);
shadow_result.rc:返回0(执行成功)。场景 2:任务执行失败 (比如未配置免密提权,或取消
become: yes):
shadow_result.stdout:为空字符串(无正常输出);
shadow_result.stderr:会获取到错误信息(如cat: /etc/shadow: 权限不够);
shadow_result.rc:返回非 0 值(如1,表示执行失败);Ansible 会默认将失败任务标记为
fatal,终止后续任务(若需忽略失败,可添加ignore_errors: yes)。
范例6:在 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
}
实验:用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='\\listen 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、防火墙放行89号端口
[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
实验:创建一个虚拟主机,基于不同端口访问不同页面
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'