使用 Ansible Shell 模块在远程服务器上执行命令

Ansible 是一款被广泛采用的配置管理工具,可以简化多台服务器的管理。它通过自动化简化服务器设置和应用安装等任务,让您能够从一个中央控制节点管理大量远程服务器并按顺序执行任务。此外,Ansible 之所以受欢迎,还因为它拥有众多模块,并且能够通过 SSH 无缝运行,无需在目标服务器上安装额外软件。

在本教程中,我们将学习如何使用 Ansible shell 模块在远程服务器上运行命令。此外,我们还将了解如何运行 Ansible 临时(ad-hoc)命令,以及如何创建 playbook 来执行各种 shell 命令。

前提条件

要使用 Ansible shell 模块,您需要满足以下前提条件:

  1. 本指南使用的是 Ubuntu 22.04,因此您的 Ubuntu 系统上应已安装并配置好 Ansible。
  2. 应已配置好一台远程主机,您将使用 Ansible shell 模块在该主机上运行命令。

什么是 Ansible Shell 模块?

Ansible shell 模块允许用户在目标系统的 shell 中执行命令。它让用户能够在远程目标上运行带有管道和重定向的复杂命令,并保持命令执行的原始方式。

在 Ansible shell 模块中运行命令时,它会接收以空格分隔的命令名称和参数,并在远程主机上执行这些命令。

区别:Ansible Shell 模块与 Command 模块

Ansible shell 模块和 command 模块非常相似,得到的结果也相同。不过,这两个模块之间存在一些区别,如下所列:

  1. Ansible shell 模块支持所有特殊运算符、环境变量、管道和重定向,而 command 模块不支持运算符(<、>、&、;、| |)、环境变量和管道。
  2. command 模块不允许用户直接在 shell 中运行命令。在这方面,Ansible shell 模块比 command 模块更加灵活。使用 shell 模块,您可以直接在目标主机的 shell 中执行命令。它默认使用“/bin/sh”shell 运行命令,您也可以配置其他 shell 在远程主机上运行命令。
  3. 从安全角度考虑,Ansible 用户在目标系统上执行命令时更倾向于使用 command 模块。通过 command 模块运行的命令比 Ansible shell 模块更安全,行为也更可预测。此外,command 模块不会影响用户的远程 shell 环境。

Ansible Shell 命令入门:运行临时命令

临时(ad-hoc)命令是一次性的单行命令,以后无法复用。不过,您可以使用 Ansible 临时 shell 命令快速执行单个任务。临时命令的基本语法如下:

ansible [pattern] -m [ansible-module] -a {commands with options}

在上述语法中,pattern 表示目标主机所属的主机组。选项“-m”指定模块类型,选项“-a”用于接收命令参数。

我们通过一个示例来说明 Ansible 临时命令的语法。在这个示例中,我们将尝试显示目标主机的日期和时间。这里我们只连接了一台主机,并创建了一个单独的用户“ansible”。使用以下命令显示远程主机的日期和时间:

$ ansible 192.168.1.14 -u ansible -m shell -a "timedatectl"

上述 Ansible 临时命令会通过 SSH,以用户名“ansible”连接到 IP 地址为“192.168.1.14”的远程主机。之后,将在远程主机上执行“timedatectl”命令,该命令用于显示系统日期和时间设置的相关信息。

$ ansible <ip-address> -u ansible -m shell -a "timedatectl | grep Time"

上面这条 Ansible 临时命令会先运行“timedatectl”命令,然后通过 grep 命令筛选并显示包含“Time”的行。

同样,您可以使用“uptime”命令显示系统运行时间和平均负载等信息。为此,请使用以下命令:

$ ansible <ip-address> -u ansible -m shell -a "uptime"

运行上述 Ansible shell 命令后,它会在连接到远程主机后执行 uptime 命令。

$ ansible <ip-address> -u ansible -m shell -a setup

上述命令用于通过 Ansible 收集远程主机的系统事实(facts)和信息。

如何使用 Ansible Shell 模块?

在运行 Ansible shell 命令之前,您需要了解使用 Ansible shell 模块时要传递的基本 shell 参数:

chdir - 在执行命令之前切换当前目录。

cmd - 一个字符串,包含要运行的命令及其相关参数。

executable - 需要提供绝对路径,用于更改用户所使用的 shell。

removes - 接收一个文件名,当该文件不存在时跳过此步骤。

stdin - 允许用户将命令的“stdin”设置为特定值。

warn - 取值为 yes(默认)或 no,用于启用或禁用任务警告。

了解这些之后,我们来看一些使用 Ansible shell 的示例。

使用 Shell 模块通过 Ansible 运行命令

Ansible 真正的强大之处在于 playbook,它让您可以在目标主机上执行高级任务。除了使用临时命令,您还可以在 playbook 中使用 Ansible shell 模块,为远程主机定义任务。

使用 Ansible Shell 模块运行单条命令

您也可以使用 Ansible shell 模块运行单条命令。为此,请创建一个 playbook,并将以下代码粘贴到该文件中:

- name: Shell module example
  hosts: database_servers
  tasks:
    - name: Check disk usage
      shell: df -h
      register: os_info
    - name: Show the command output
      debug:
        msg: "{{ os_info.stdout_lines }}"

现在,保存对该文件的修改,并使用“Ctrl+X”退出。然后,使用以下命令运行该 playbook:

$ ansible-playbook --ask-become-pass -i hosts environment_var.yml

运行上述命令后,您将在终端窗口中看到以下信息:

使用 Shell 模块通过 Ansible 运行多条命令

您可以使用 Ansible 按顺序运行多个任务。要创建 Ansible playbook,请在源代码编辑器中新建一个空文件。本指南使用 nano 编辑器创建 playbook。

$ sudo nano testplaybook.yml

将以下 playbook 粘贴到文件中:

- name: Run multiple tasks with the shell module
  hosts: database_servers
  become: yes
  vars:
    new_user: deploy
    package_name: nginx
  tasks:
    - name: Create a user if it does not exist
      shell: id -u {{ new_user }} || useradd -m -s /bin/bash {{ new_user }}
    - name: Update the package cache
      shell: apt-get update
    - name: Install or upgrade the package
      shell: apt-get install -y {{ package_name }}

上述 Ansible playbook 会在指定的远程目标主机上执行多项任务,例如创建用户、更新软件包缓存,以及安装或升级 Nginx 软件包。它使用变量让 playbook 更加灵活、更易复用。

使用“Ctrl+O”保存上述文件,然后使用“Ctrl+x”退出。

这里我们使用的是自定义清单文件“hosts”。要运行该 playbook,请使用以下命令:

$ ansible-playbook --ask-become-pass -i hosts testplaybook.yml

在上述命令中,“--ask-become-pass”会要求您输入 root 密码,随后屏幕上将显示以下输出:

使用 Ansible Shell 模块安装软件包

您还可以在目标远程主机上安装软件包。参考以下 playbook,使用 Ansible 在目标主机上安装“apache2”软件包:

- name: Install apache2 with Ansible
  hosts: database_servers
  become: yes
  tasks:
    - name: Update the apt package cache
      apt:
        update_cache: yes
    - name: Install the apache2 package
      apt:
        name: apache2
        state: present

运行上述 playbook 后,您会看到远程服务器上已经安装了 apache2。

防止 Shell 命令注入

如前所述,Ansible 中的 command 模块被认为是在远程系统上执行命令时更安全的选择,但它在功能方面存在一定限制。

为了安全地使用 shell 模块,您可以采用一种称为“quote 过滤”的技术,防止变量被用于命令注入。下面的示例演示了如何借此防范命令注入,确保 shell 命令的安全。

- name: Ansible quote filter example
  hosts: database_servers
  become: yes
  vars:
    database_name: mydb
    table_name: my_table
  tasks:
    - name: Create a table in the {{ database_name }} database
      shell: >
        mysql -u root -psecret {{ database_name | quote }}
        -e {{ ("CREATE TABLE " ~ table_name ~ " (id INT, name VARCHAR(255));") | quote }}

上述 Ansible playbook 会在指定数据库中创建一个具有唯一名称的 MySQL 表,并使用 quote 过滤器防范表名中的特殊字符可能引发的问题。

结论

Ansible shell 模块是一款灵活而强大的工具,可以增强用户的控制力,并简化远程服务器的配置。在本文中,我们介绍了 Ansible shell 模块的用法、可用参数以及必填参数。希望借助这些知识,您能够在 Linux 系统上高效地使用 Ansible shell 模块。