跳转至

Ansible部署

一. ansible概述和运行机制

1. ansible概述

Absible是Red Hat公司使用python研发的开源自动化运维工具,类似于saltstack和puppet这样的老牌自动化运维工具

Ansible有一个不同和优点是我们不需要再借点中安装任何客户端。它使用SSH来和节点进行通信。所以只需要被管理节点具备SSH服务以及python即可,无需任何多于操作就可以实现自动化管理。而管理节点只需要安装Ansible工具,以及python,openssl即可

总结:

Ansible是一个部署一群远程主机的工具,Ansible通过SSH协议实现远程节点和管理节点之间的通信。理论来说,只要管理员能够通过ssh登录到任意远程主机上能做到的操作,Ansible都能做到。Ansible是由python开发的,因此会依赖一些python库和组件。如paramiko,PyYaml和jinja三个关键组件。

2. ansible的基本架构

ansible系统由管理节点和被管理系欸但组成。ansible在管理节点将ansible模块通过SSH协议推送到被管理端执行,执行完毕后自动删除,可以使用SVN、GIT等来管理自定义模块及编排

Ansible组由五个部分构成

alt text

剧本: playbooks

Ansible: ansible核心程序

Modules: 包括Ansible自带的核心模块以及自定义模块

  • Core Modules: Ansible执行任何管理任务都不是由Ansible自己完成的,而是由其核心模块完成。Ansible管理主机之前,先调用core Modules中的模块,然后指明管理Host Inventory中的主机,就可以完成管理主机

  • Custom Modules: 自定义模块,完成Ansible核心模块无法完成的功能。此模块支持任何语言进行编写

Plugins: 模块功能补充,通过插件来实现记录日志,发送邮件或其他功能

Connectior Plugins: 连接插件,ansible基于连接插件连接到各个主机上,默认使用ssh连接

Playbooks: 剧本,yaaml文件。多个任务定义在一个文件中。定义主机需要调用哪些模块在完成功能定义,及实现自动化部署文件

Host Inventory: 记录由Ansible管理的主机信息,包括端口、密码、IP地址等

右边绿色部分是被管理的主机(虚拟机、物理机、云主机等),从以上架构图中可以看出ansible是由主机清单,playbook以及各个模块插件组成

简单说就是用户(管理员)通过ansible的主机清单配置或Playbook配置(一组任务),调用ansible的各个模块以及参数来对清单中的主机进行统一管理

3. ansible任务执行方式

ansible执行自动化任务,分为两种执行方式

  1. ad-hoc: 即ansible命令,命令行界面。单挑命令的批量执行,称之为ad-hoc。这种方式主要用来进行功能测试,或者是简单的应用部署
  2. playbook: Playbook方式(剧本方式)。从字面意思就可以知道,我们的剧本方式都是提前设置好内容,进行剧本的演示。即把多个想要执行的任务放到一个playbook中,当然多个任务在事务逻辑上最好是有上下文联系的。通过多个任务可以完成一个总体目标,这就是playbook。playbook方式适用于大型自动化部署应用

ad-Hoc、Ansible-playbooks这两种方式没有本质区别,Ad-Hoc用于临时执行命令;ansible-playbooks可以理解为Ad-Hoc的集合,通过一定的规则编排在一起,也就是剧本

二. 安装并配置Ansible管理两个节点

1. 部署前准备:关闭SELinux(重要!)

⚠️ 新机器一定要先看这里!

新装的 Rocky/CentOS 默认 SELinux 是 enforcing(强制) 模式。此时跑 ansible 会出现各种莫名其妙的报错(连接超时、权限拒绝、模块执行失败等),但错误信息不会直接告诉你是 SELinux 干的,排查半天找不到原因。所以开始部署前,先在管理端和被管理端检查并关闭 SELinux:

# 1. 检查当前 SELinux 状态
getenforce        # 显示 Enforcing 就是没关;Permissive/Disabled 才是放行

# 2. 临时关闭(当前生效,重启后失效)
setenforce 0

# 3. 永久关闭(修改配置文件,重启后仍生效)
sed -i 's/^SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config

# 4. 验证
getenforce        # 应显示 Permissive 或 Disabled

备选:不关闭而是"放行"——按需用 setsebool 放行特定行为(如 setsebool -P httpd_can_network_connect 1)。学习环境直接关闭最简单。

2. 环境拓扑

主机名 IP 角色 安装软件
1 192.168.100.99 ansible管理服务器 ansible
2 192.168.100.100 Clent
3 192.168.100.101 Clent

3. 配置ansible源

1) 配置阿里云epel源

yum install -y https://mirrors.aliyun.com/epel/epel-release-latest-9.noarch.rpm
sed -i 's|^#baseurl=https://download.example/pub|baseurl=https://mirrors.aliyun.com|' /etc/yum.repos.d/epel*
sed -i 's|^metalink|#metalink|' /etc/yum.repos.d/epel*

2) 设置时区

dnf -y install glibc-langpack-zh
# echo "export LC_ALL=en_US.UTF-8" >> /etc/profile
# 系统为英文字体使用该命令
echo "export LC_ALL=zh_CN.UTF-8" >> /etc/profile
# 系统为中文字体使用该命令
source /etc/profile
sed -i 's@enabled=1@enabled=0@g' /etc/yum/pluginconf.d/subscription-manager.conf

4. 安装ansible

pip install mysql-connector-python
pip install --upgrade ansible

5. 配置文件与说明

执行命令rpm -ql ansible-core会看到一大堆相关文件,就不一个个列出了。这些主要是配置文件和可执行文件,以及所依赖的python库文件。

1) 配置与执行文件说明

ansible的主配置文件: /etc/ansible/ansible.cfg

这个文件主要定义了roles_path路径,连接清单中的主机方式等配置,这些大部的默认配置已经足够我们平时使用,如有需要特特别配置可以自行修改。

主机清单文件: /etc/ansible/hosts

这个配置文件就是默认主机清单配置文件,可以通过ansible.cfg重新定义

如定义一组主机: [web_servers]其中不能包含特殊字符,下划线可以其他特殊字符一律不行。比如

[web_servers]
192.168.2.112
192.168.2.113

2) 其他文件

除了以上两个重要配置文件还有三个重要可执行文件是

[root@1 ~]# ls /usr/local/bin/ansible*
/usr/local/bin/ansible            /usr/local/bin/ansible-connection  /usr/local/bin/ansible-galaxy     /usr/local/bin/ansible-pull
/usr/local/bin/ansible-community  /usr/local/bin/ansible-console     /usr/local/bin/ansible-inventory  /usr/local/bin/ansible-test
/usr/local/bin/ansible-config     /usr/local/bin/ansible-doc         /usr/local/bin/ansible-playbook   /usr/local/bin/ansible-vault
  • ansible主执行程序,一般用于命令行下执行。位于/usr/local/bin/ansible
  • ansible-playbook 执行playbook任务
  • ansible-doc 获取各个模块的帮助信息

但是在新版本ansible中,ansible主配置文件默认不存在,需要自己使用ansible-config init --disabled > /etc/ansible/ansible.cfg命令生成

6. 定义主机清单(inventory)

在前面了解了ansible的两种运行方式后,我们来看一下使用命令行的方式命令主机运行。如果想要进行测试则需要被管理的主机添加到/etc/ansible/hosts中。(新版本ansible这个文件也默认不在,需要自己手写)

7. 基于端口,用户,密码定义主机清单

ansible基于ssh连接指定的远程主机,可以写端口,用户,密码

1) 生成配置文件

# 创建存放ansible相关文件的目录
mkdir /etc/ansible
# 生成主配置
ansible-config init --disabled > /etc/ansible/ansible.cfg

2) 配置主机清单

文件/etc/ansible/hosts维护者ansible中被管理的服务器的清单,当前我们需要手写配置文件

cat << 'EOF' | tee /etc/ansible/hosts
[web_servers]
192.168.100.100 ansible_ssh_port=22 ansible_ssh_user=root
192.168.100.101 ansible_ssh_port=22 ansible_ssh_user=root
EOF

其中中括号内的web_servers是组名,组名下方为改组包括的所有主机。每一行定义一个主机,组名需要唯一,而组内的一台主机则可以同时属于多个不同组。

ansible_ssh_port: 指定ssh端口(如果是默认端口为22,此参数可以省略)

ansible_ssh_user: 指定ssh用户

ansible_ssh_pass: 指定ssh用户登录的认证密码(明文密码不安全)

ansible_sudo_pass: 指明sudo时候的密码

# 注: 如果是第一次连接某个虚拟机可能会有因为启动了主机密钥检查(Host Key checking)而连接失败,因为sshpass不支持该功能。为了解决这个问题,你可以将主机公钥添加到本地的known_hosts文件中,以允许你使用密码连接主机

提示如下

192.168.100.101 | FAILED! => {
    "msg": "Using a SSH password instead of a key is not possible because Host Key checking is enabled and sshpass does not support this.  Please add this host's fingerprint to your known_hosts file to manage this host."
}
  • 解决方法1: 手动ssh登录一次目标主机
  • 解决方法2: 做免密登录
  • 解决方法3: ssh-keycan ~/.ssh/known_hosts
  • 解决方法4: 修改/etc/ansible/ansible.cfg的主配置文件,把host_key_checking=True改成False

三. 执行命令操作实践

1. 执行测试命令

[root@1 ~]# ansible -i /etc/ansible/hosts web_servers -m ping -k
SSH password: 
192.168.100.100 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}
192.168.100.101 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}
# 注: 这里只能指定一次密码同时提交给两台主机,如果两台主机密码不一致,则有一台可能会验证失败

2. 基于ssh密钥开访问定义主机清单

一般来说,在/etc/ansible/hosts中使用明文密码不安全。所以增加主机无密码访问,也即是利用密钥对验证。在Ansible服务端生成密钥对,然后将其中公钥复制到节点中。

a. 创造密钥对

[root@1 ~]# 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:pPDBzw7yAniBo6W6h9sMAWCOSKIf4bxGTIx9QQewsQU root@1
The key's randomart image is:
+---[RSA 3072]----+
|o*E+=o.          |
|@*o*.o           |
|B.@.. o .        |
|oB + o *         |
|= * . + S        |
|.+ . o o         |
|o.  . . .        |
|.=.  .           |
|ooo              |
+----[SHA256]-----+

# 注: 存放密钥对的位置为/root/.ssh/id_rsa

b. 将公钥复制到另外两台主机上

[root@1 ~]# ssh-copy-id root@192.168.100.100
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/root/.ssh/id_rsa.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
root@192.168.100.100's password: 

Number of key(s) added: 1

Now try logging into the machine, with:   "ssh 'root@192.168.100.100'"
and check to make sure that only the key(s) you wanted were added.

[root@1 ~]# ssh-copy-id root@192.168.100.101
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/root/.ssh/id_rsa.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
root@192.168.100.101's password: 

Number of key(s) added: 1

Now try logging into the machine, with:   "ssh 'root@192.168.100.101'"
and check to make sure that only the key(s) you wanted were added.

c. 验证

cat << 'EOF' | tee -a ~/.bashrc
alias ssh2='ssh root@192.168.100.100'
alias ssh3='ssh root@192.168.100.101'
EOF
source ~/.bashrc
[root@1 ~]# ssh2
Last login: Wed Aug 12 21:54:28 2026 from 192.168.100.99
[root@K8s2 ~]# exit
注销
Connection to 192.168.100.100 closed.
[root@1 ~]# ssh3
Last login: Wed Aug 12 21:54:28 2026 from 192.168.100.99
[root@K8s3 ~]# exit
注销
Connection to 192.168.100.101 closed.

此时使用1主机ssh连接2和3就不需要输入密码了

3. 重新配置主机清单

修改为以下内容

cat << 'EOF' | tee /etc/ansible/hosts
[web_servers]
2 ansible_ssh_host=192.168.100.100 ansible_ssh_port=22 ansible_ssh_user=root
3 ansible_ssh_host=192.168.100.101 ansible_ssh_port=22 ansible_ssh_user=root
EOF

如果没有修改过ssh默认的22端口,并且使用root用户身份连接,ansible_ssh_port=22和ansible_ssh_user=root可以不写

4. 执行命令测试

ping模块检查网络连通性

[root@1 ~]# ansible -i /etc/ansible/hosts web_servers -m ping
3 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}
2 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}
# 或使用命令ansible -i web_servers -m ping

四. ansible命令参数

1. ansible命令语法

可以使用命令ansible --help查看帮助

usage: ansible [options] 使用方法: ansible+主机+选项

例如: ansible [组名] [-i 主机文件] [-m 模块名称] [-a 模块参数]

ansible 详细参数
-v                      # 详细模式。如果命令执行成功,输出详细结果 (-vv -vvv -vvvv)
-i                      # 指定清单文件(hosts)路径,默认是在` /etc/ansible/hosts `
-m                      # 指定使用的module名称,默认使用command模块
-a                      # 指定moudule模块的参数
-u                      # ssh连接的用户数。默认用root,ansible.cfg中可以配置
-k                      # 提示输入ssh登陆密码。当使用密码验证的时候用
-b,--become             # 远程执行命令时使用sudo方式。相当于Linux系统下的sudo命令。
--become-user           # SUDO USERNAME,sudo到那个用户,默认用root
# 注意,老版本-b,--become是-s,--sudo;--become-user是-U,--sudo-user
-k,--ask-becom-pass     # 使用--become或者--become-user时使用的密码认证(即提示输入sudo密码,当不是NOPASSWD模式时需要使用)
-C                      # 测试命令执行效果而不会真的执行
-f FORKS,--forks=FORKS  # 并行线程数量,当有数量巨大的机器需要配置时可以使用高并发线程。默认5个。
--list-host             # 只打印有哪些主机会执行命令,不会执行

ansible-doc详细参数
ansible-doc-l           # 列出所有的模块列表
ansible-doc-s           # 查看指定模块的参数
-s,--snippet            # 片段 

2. 测试-m和-a选项

1) 检查Ansible被管理节点的CPU负载(UPTIME)

[root@1 ~]# ansible web_servers -m command -a "uptime"
3 | CHANGED | rc=0 >>
 08:47:16 up 22 min,  1 user,  load average: 0.00, 0.00, 0.00
2 | CHANGED | rc=0 >>
 08:47:16 up 22 min,  1 user,  load average: 0.00, 0.00, 0.00

说明: -m指定ansible要调用的模块;-a指定具体命令。省略-m默认使用command模块

也可以把主机清单组名写到最后,这样方便阅读命令

[root@1 ~]# ansible -a "uptime" web_servers
2 | CHANGED | rc=0 >>
 08:50:35 up 25 min,  1 user,  load average: 0.00, 0.00, 0.00
3 | CHANGED | rc=0 >>
 08:50:36 up 25 min,  1 user,  load average: 0.00, 0.00, 0.00

2) 给ansible被管理节点增加用户

a. 先创建用户

# 创建用户
[root@1 ~]# ansible -m command -a "useradd user1" web_servers
2 | CHANGED | rc=0 >>

3 | CHANGED | rc=0 >>

# 查看用户密码信息
[root@1 ~]# ansible -a "grep user1 /etc/passwd" web_servers
3 | CHANGED | rc=0 >>
user1:x:1000:1000::/home/user1:/bin/bash
2 | CHANGED | rc=0 >>
user1:x:1000:1000::/home/user1:/bin/bash

b. 给用户设置密码

[root@1 ~]# ansible -a "echo 'user1:123456'|chpasswd" web_servers
2 | CHANGED | rc=0 >>
user1:123456|chpasswd
3 | CHANGED | rc=0 >>
user1:123456|chpasswd
[root@1 ~]# ansible -a "grep user1 /etc/shadow" web_servers
3 | CHANGED | rc=0 >>
user1:!!:20678:0:99999:7:::
2 | CHANGED | rc=0 >>
user1:!!:20678:0:99999:7:::

command模块内部不支持管道符"|"和输出重定向">>",所以这里需要shell模块

[root@1 ~]# ansible -m shell -a "echo 'user1:123456'|chpasswd" web_servers
3 | CHANGED | rc=0 >>

2 | CHANGED | rc=0 >>

[root@1 ~]# ansible -a "grep user1 /etc/shadow" web_servers
3 | CHANGED | rc=0 >>
user1:$6$MkarfP8ZGvvJaUJA$6Rzj14Ur7N3v5gYO7GGTiqBEXelb/HjxdAZo5BC5v3w.l5j2/L9MDnJor/uxFQ3xD9UrLut/bEjRYRv/gBlZB0:20678:0:99999:7:::
2 | CHANGED | rc=0 >>
user1:$6$bfpUDJo26MoU/mqi$wz44/T1lnCQpz1ACnhRge59X7oLnqRsLncsJip6lo.oxDykJNz5ghj5cEseHNk5KwQYXG3dGrNFxMteVPDB3N/:20678:0:99999:7:::

3) 将df命令在所有结点指向后,重定向输出到本机的/tmp/command-output.txt文件中

[root@1 ~]# ansible -m command -a "df -hT" web_servers > /tmp/command-output.txt
[root@1 ~]# cat /tmp/command-output.txt
2 | CHANGED | rc=0 >>
文件系统            类型      容量  已用  可用 已用% 挂载点
devtmpfs            devtmpfs  4.0M     0  4.0M    0% /dev
tmpfs               tmpfs     870M     0  870M    0% /dev/shm
tmpfs               tmpfs     348M  5.1M  343M    2% /run
/dev/mapper/rl-root xfs        52G  2.7G   50G    6% /
/dev/nvme0n1p1      xfs       960M  243M  718M   26% /boot
/dev/mapper/rl-home xfs        26G  213M   25G    1% /home
tmpfs               tmpfs     174M     0  174M    0% /run/user/0
3 | CHANGED | rc=0 >>
文件系统            类型      容量  已用  可用 已用% 挂载点
devtmpfs            devtmpfs  4.0M     0  4.0M    0% /dev
tmpfs               tmpfs     870M     0  870M    0% /dev/shm
tmpfs               tmpfs     348M  5.1M  343M    2% /run
/dev/mapper/rl-root xfs        52G  2.7G   50G    6% /
/dev/nvme0n1p1      xfs       960M  243M  718M   26% /boot
/dev/mapper/rl-home xfs        26G  213M   25G    1% /home
tmpfs               tmpfs     174M     0  174M    0% /run/user/0

4) 测试-m,-u,-k选项

修改/etc/ansible/hosts文件

cat << 'EOF' | tee /etc/ansible/hosts
[web_servers]
192.168.100.100
192.168.100.101
EOF

-m 指定ping模块,以user1用户身份执行ping存活检测。如果不指定用户,在远程机器上默认以root用户身份执行

[root@1 ~]# ansible 192.168.100.100 -m ping -u user1 -k
SSH password: 
192.168.100.100 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}

注意,ip需要在/etc/ansible/hosts里面提前配置。如果想在多个主机测试这里可以改成all或组名。当然前提是需要在hosts文件配置好

3. 测试-b

以user1用户sudo到root执行ping存活检测。这种方式需要在远程主机/etc/sudoers文件中配置user1用户,如果没有NOPASSWD,则还需要加上-K选项

先使用visudo命令进入该文件,然后添加user1 ALL=()。

但是这样太麻烦,我就直接把用户user1加入wheel组了。

[root@1 ~]# ansible -m command -a "usermod -aG wheel user1" web_servers
192.168.100.100 | CHANGED | rc=0 >>

192.168.100.101 | CHANGED | rc=0 >>

然后执行ping命令

[root@1 ~]# ansible 192.168.100.100 -m ping -u user1 -b -K
BECOME password: 
192.168.100.100 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}

注: 因为已经设置了ssh免密连接,所以-k就没必要了。这里我们要使用-K输入sudo密码

4. 列出将执行该命令的主机

0) 先将/etc/ansible/hosts文件修改

cat << 'EOF' | tee /etc/ansible/hosts
[web_servers1]
2 ansible_ssh_host=192.168.100.100 ansible_ssh_port=22 ansible_ssh_user=root
[web_servers2]
3 ansible_ssh_host=192.168.100.101 ansible_ssh_port=22 ansible_ssh_user=root
EOF

然后列出执行该命令的主机

[root@1 ~]# ansible -a "ls" --list-hosts all
  hosts (2):
    2
    3

总结: ansible指定要执行操作的被管理节点或组

1) 指定执行机器

注意: ip需要在/etc/ansible/hosts提前里面配置

a. 指定192.168.100/24网段上的所有主机

[root@1 ~]# ansible 192.168.100.* -m ping
192.168.100.101 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}
192.168.100.100 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}

b. 指定one、two两台机器

[root@1 ~]# ansible web_servers1:web_servers2 -m ping
2 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}
3 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}

web_servers1和web_servers2是两个主机集合,他们之间用":"连接

比如

指定A组的用户

A

指定A组和B组两个组的用户

A:B

指定在A组但不在B组的用户

A:!B 其中\为转义符

指定同时在A组和B组的用户

A:\&B

五. ansible常见模块及使用方法

1. 查看支持的模块

ansible-doc -l这里的l小写

# 列出所有支持的模块
[root@1 ~]# ansible-doc -l
# 查看支持的模块个数
[root@1 ~]# ansible-doc -l|wc -l
8394
# 查看ansible的版本号
[root@1 ~]# ansible --version
ansible [core 2.15.13]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python3.9/site-packages/ansible
  ansible collection location = /root/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/local/bin/ansible
  python version = 3.9.25 (main, Jul 13 2026, 00:00:00) [GCC 11.5.0 20240719 (Red Hat 11.5.0-14)] (/usr/bin/python3)
  jinja version = 3.1.6
  libyaml = True

2. 获取模块的帮助

ansible-doc 模块名或者ansible-doc -s 模块名

这里我们使用ansible-doc获取下command模块的使用方式

[root@1 ~]# ansible-doc command
[root@1 ~]# ansible-doc -s command

六. 3个远程命令模块的区别

1. command模块

command模块可以帮助我们在远处主机上执行命令

注意: 使用command模块在远处主机中执行命令时,不会经过远程主机的shell处理,在使用command模块时,如果需要执行的命令中含有重定向、管道符等操作时,这些符号也会失效。比如"<",">","|"和"&"这些符号。如果你需要这些功能,可以参考后面介绍的shell模块。还有一点需要注意。执行ansible时,不加-m默认使用command模块,这方面可以在/etc/ansible/ansible.cfg中修改

[root@1 ~]# grep -B1 ';module_name=command' /etc/ansible/ansible.cfg 
# (string) Module to use with the ``ansible`` AdHoc command, if none is specified via ``-m``.
;module_name=command

缺点: 不支持管道符,没办法批量执行命令

[root@1 ~]# ansible -m command -a 'ip a s ens160' web_servers*
3 | CHANGED | rc=0 >>
2: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:0c:29:37:c1:48 brd ff:ff:ff:ff:ff:ff
    altname enp3s0
    inet 192.168.100.101/24 brd 192.168.100.255 scope global noprefixroute ens160
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:fe37:c148/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever
2 | CHANGED | rc=0 >>
2: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:0c:29:6d:8f:df brd ff:ff:ff:ff:ff:ff
    altname enp3s0
    inet 192.168.100.100/24 brd 192.168.100.255 scope global noprefixroute ens160
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:fe6d:8fdf/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever

2. shell模块

shell模块可以帮助我们在远程主机执行命令。与command模块不同的是shell模块在远程主机中执行命令的时候会经过远程主机上的/bin/sh程序处理,shell模块支持管道符与重定向等符号,所以我们在终端输入大部分命令都可以使用

# 管道符
[root@1 ~]# ansible -i /etc/ansible/hosts -m shell -a "free -m|grep Mem" web_servers* 
2 | CHANGED | rc=0 >>
Mem:            1738         356        1329           6         203        1382
3 | CHANGED | rc=0 >>
Mem:            1738         348        1323           6         217        1390
# 注:我们自己定义在/etc/profile或~/.bash_profile中的环境变量。shell模块由于不会加载所以也无法识别。如果想要使用自定义的环境变量就需要在最开始执行加载自定义脚本的语句

对shell模块的使用可以分为两块

1) 如果待执行的语句少,可以直接写在一句话中

例如在192.168.100.100上部署java环境

[root@K8s2 ~]# tar -xf jdk-21.0.11_linux-x64_bin.tar.gz 
[root@K8s2 ~]# cat << 'EOF' | tee -a /etc/profile
JAVA_HOME=/usr/local/jdk-21.0.11/
PATH=$JAVA_HOME/bin:$PATH
export PATH JAVA_HOME
EOF
JAVA_HOME=/usr/local/jdk-21.0.11/
PATH=$JAVA_HOME/bin:$PATH
export PATH JAVA_HOME
[root@K8s2 ~]# source /etc/profile
[root@K8s2 ~]# java --version
java 21.0.11 2026-04-21 LTS
Java(TM) SE Runtime Environment (build 21.0.11+9-LTS-211)
Java HotSpot(TM) 64-Bit Server VM (build 21.0.11+9-LTS-211, mixed mode, sharing)

此时切换到1

[root@1 ~]# ansible -m shell -a "java --version" web_servers1
2 | FAILED | rc=127 >>
/bin/sh:行1: java:未找到命令non-zero return code

失败的原因是没有加载/etc/profile中定义的java环境变量

[root@1 ~]# ansible -m shell -a "source /etc/profile && java --version" web_servers1
2 | CHANGED | rc=0 >>
java 21.0.11 2026-04-21 LTS
Java(TM) SE Runtime Environment (build 21.0.11+9-LTS-211)
Java HotSpot(TM) 64-Bit Server VM (build 21.0.11+9-LTS-211, mixed mode, sharing)

2) 如果命令比较复杂,就需要写脚本,通过shell模块执行

利用shell脚本需要以下几个步骤

第一个步骤:编写一个脚本

第二个步骤:将脚本发送到远程主机

第三个步骤:给脚本添加执行权限

第四个步骤:允许ansible命令执行脚本

第一个步骤: 编写一个脚本

例如执行检查磁盘脚本,内容如下

#!/bin/bash
basedir=$(cd "$(dirname "$0")" && pwd)
echo $basedir
diskmax=10      #磁盘的阈值

function check_max () {
    local disk_size=$1
    if [ $disk_size -ge $diskmax ]
    then
        echo "unhealthy"
    else
        echo "healthy"
    fi
}

function check_disk_info () {
    df -h|grep "^/dev"|grep -v "/dev/sr0"|awk '{print $1,$5}'|cut -d '%' -f 1|while read disk_name disk_size
        do
            echo ""
            echo "disk_name=$disk_name,disk_size=$disk_size%"
            check_max $disk_size
        done
}
check_disk_info

说明:

dirname命令可以取出定路径的目录部分。这个命令很少直接在shell命令行中使用,一般它用在shell脚本中用于取出脚本文件所在的路径,然后将当前目录切换过去。

Local命令一般用于shell内局部变量的定义,多使用在函数内部

# 反向命令是 basename,作用是取出“文件名/最后一级”部分

第二个步骤: 将脚本发送到远程主机

拷贝脚本之前,先在远程上创建好准备拷贝过去的文件目录,其中远程创建文件目录有两种方式,操作如下

方式1:shell命令方式

[root@1 ~]# ansible -m shell -a "mkdir -p /work/ansible" web_servers*
3 | CHANGED | rc=0 >>

2 | CHANGED | rc=0 >>

方式2: file模块执行创建文件夹

[root@1 ~]# ansible -m file -a "path=/work/file state=directory mode=0700" web_servers*
2 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": true,
    "gid": 0,
    "group": "root",
    "mode": "0700",
    "owner": "root",
    "path": "/work/file",
    "secontext": "unconfined_u:object_r:default_t:s0",
    "size": 6,
    "state": "directory",
    "uid": 0
}
3 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": true,
    "gid": 0,
    "group": "root",
    "mode": "0700",
    "owner": "root",
    "path": "/work/file",
    "secontext": "unconfined_u:object_r:default_t:s0",
    "size": 6,
    "state": "directory",
    "uid": 0
}
[root@1 ~]# ansible -m shell -a "ls /work" web_servers*
3 | CHANGED | rc=0 >>
ansible
file
2 | CHANGED | rc=0 >>
ansible
file
第三个步骤: 将脚本发送到远程主机并修改权限

批量拷贝shell脚本到各台服务器

ansible 主机组 -m copy -a "src=拷贝文件路径 dest=拷贝文件路径 mode=0755"

a. 先写脚本

cat << 'EOF' | tee /root/test.sh
#!/bin/bash
df -hT|awk '/^\/dev/ {gsub("%","",$6); if($6>15) print $1,$6"%"}'
EOF

b. 将脚本复制到远程主机

[root@1 ~]# ansible -m copy -a "src=/root/test.sh dest=/work/ansible mode=0755" web_servers*
3 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": true,
    "checksum": "bcfe38e1223a6a1bd0abb41590aed1b36d34f6a4",
    "dest": "/work/ansible/test.sh",
    "gid": 0,
    "group": "root",
    "md5sum": "8441fce8f609ae87564977bb801dc8cd",
    "mode": "0755",
    "owner": "root",
    "secontext": "system_u:object_r:default_t:s0",
    "size": 78,
    "src": "/root/.ansible/tmp/ansible-tmp-1786616336.70452-5437-280145070668128/source",
    "state": "file",
    "uid": 0
}
2 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": true,
    "checksum": "bcfe38e1223a6a1bd0abb41590aed1b36d34f6a4",
    "dest": "/work/ansible/test.sh",
    "gid": 0,
    "group": "root",
    "md5sum": "8441fce8f609ae87564977bb801dc8cd",
    "mode": "0755",
    "owner": "root",
    "secontext": "system_u:object_r:default_t:s0",
    "size": 78,
    "src": "/root/.ansible/tmp/ansible-tmp-1786616336.6889963-5436-75998371169642/source",
    "state": "file",
    "uid": 0
}

说明:

copy模块: 实现主控端像目标主机copy文件

#src 主控端文件位置

#dest 被控端目标位置

#owner 文件被复制过去的所有者

#group 文件复制过去后的所属组

#mode 文件的权限设定(755或0755)

第四个步骤: 运行ansible命令执行脚本
[root@1 ~]# ansible -m shell -a "/work/ansible/test.sh" web_servers*
2 | CHANGED | rc=0 >>
/dev/mapper/rl-root 7%
/dev/nvme0n1p1 26%
3 | CHANGED | rc=0 >>
/dev/mapper/rl-root 6%
/dev/nvme0n1p1 26%

总结:

  1. command模块和shell模块的区别是shell模块支持管道符等特殊符号以及脚本

  2. shell模块在远处执行脚本。现在ansible管理端写一个脚本,再通过copy模块传到远端,然后再执行。但是这样就涉及到了两次ansible调用,对于这种需求ansible以及为我们考虑好了。script模块就是做这个的

3. script模块

script模块只能执行脚本,不能调用其他命令。但是script执行的是存放在ansible管理端的脚本

script模块跟上面类似,但执行脚本只需要两步

第一个步骤:编写一个脚本

第二个步骤:运行ansible命令执行脚本

使用script模块可以在本地写一个脚本,在远程服务器上执行

这里还是以上面test.sh脚本为例

# 先清理原来的脚本
[root@1 ~]# ansible -m shell -a "rm -rf /work/ansible/*" web_servers*
2 | CHANGED | rc=0 >>

3 | CHANGED | rc=0 >>

[root@1 ~]# ansible -m shell -a "ls /work/ansible/" web_servers*
3 | CHANGED | rc=0 >>

2 | CHANGED | rc=0 >>

测试利用script模块执行脚本

[root@1 ~]# ansible -i /etc/ansible/hosts -m script -a "/root/test.sh" web_servers*
2 | CHANGED => {
    "changed": true,
    "rc": 0,
    "stderr": "Shared connection to 192.168.100.100 closed.\r\n",
    "stderr_lines": [
        "Shared connection to 192.168.100.100 closed."
    ],
    "stdout": "/dev/mapper/rl-root 7%\r\n/dev/nvme0n1p1 26%\r\n",
    "stdout_lines": [
        "/dev/mapper/rl-root 7%",
        "/dev/nvme0n1p1 26%"
    ]
}
3 | CHANGED => {
    "changed": true,
    "rc": 0,
    "stderr": "Shared connection to 192.168.100.101 closed.\r\n",
    "stderr_lines": [
        "Shared connection to 192.168.100.101 closed."
    ],
    "stdout": "/dev/mapper/rl-root 6%\r\n/dev/nvme0n1p1 26%\r\n",
    "stdout_lines": [
        "/dev/mapper/rl-root 6%",
        "/dev/nvme0n1p1 26%"
    ]
}

总结:script和shell的区别是一个执行控制端的脚本,一个执行远程被控制端的脚本

七. args参数

1. args参数的简介

args是command和shell模块专门用来传递"附加选项"的一组参数。他解决的问题是:command/shell模块只有一个cmd主参数。有时候我们需要给这条命令加一些"行为控制"的选项,比如切换目录、指定条件跳过、设置环境变量等

2. args的常用参数

参数 作用 类比
chdir 执行命令前先切换到指定目录 cd /xxx && cmd
creates 若指定的文件/目录存在,则跳过该任务 幂等开关
removes 若指定的文件/目录不存在则跳过该任务 幂等开关
executable 用指定的解释器执行(shell模块用) 指定bash等
stdin 给命令传入标准输入 echo xxx | cmd

3. 逐个举例

1) chdir --- 先切换目录再执行

[root@my-project tasks]# ansible -m shell -a 'chdir=/root ls -l' "2"
2 | CHANGED | rc=0 >>
总用量 4
-rw-------. 1 root root 1013  8月  2 11:53 anaconda-ks.cfg

2) creates --- 执行命令前进行文件或目录存在与否的判断

[root@my-project tasks]# ansible -m shell -a "mkdir /root/aa" "2"
2 | FAILED | rc=1 >>
mkdir: 无法创建目录 “/root/aa”: 文件已存在non-zero return code
[root@my-project tasks]# ansible -m shell -a "creates=/root/aa mkdir /root/aa" "2"
2 | SUCCESS | rc=0 >>
skipped, since /root/aa existsDid not run command since '/root/aa' exists

3) removes

[root@my-project tasks]# ansible -m shell -a 'removes=/root/aa rm -rf /root/aa' "2"
2 | CHANGED | rc=0 >>

[root@my-project tasks]# ansible -m shell -a 'ls /root/aa' "2"
2 | FAILED | rc=2 >>
ls: 无法访问 '/root/aa': 没有那个文件或目录non-zero return code
[root@my-project tasks]# ansible -m shell -a 'removes=/root/aa mkdir /root/aa ; ls /root/aa' "2"
2 | SUCCESS | rc=0 >>
skipped, since /root/aa does not existDid not run command since '/root/aa' does not exist