如何修复 Ansible script 模块中的 'Permission denied' 错误

AnsibleBeginner
立即练习

介绍

Ansible 是一个强大的自动化工具,它简化了基础设施管理和配置。Ansible 的 script 模块允许你在 playbook 中执行自定义脚本,从而扩展 Ansible 的功能,超越其内置模块。然而,用户在使用此模块时,经常会遇到 'Permission denied' 错误,这会阻碍自动化工作流程。

本实验将指导你理解和解决 Ansible 的 script 模块中与权限相关的问题。你将学习如何识别权限错误的原因,并实施各种解决方案,以确保你的 Ansible 脚本成功运行。

安装 Ansible 并设置环境

在探索 Ansible 脚本的权限问题之前,我们需要设置我们的环境。让我们安装 Ansible 并为我们的测试环境创建一个基本结构。

安装 Ansible

首先,让我们更新软件包索引并安装 Ansible:

sudo apt update
sudo apt install -y ansible

安装完成后,验证 Ansible 是否已正确安装:

ansible --version

你应该看到类似这样的输出,显示 Ansible 的版本和配置详细信息:

ansible [core 2.12.x]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/labex/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3/dist-packages/ansible
  ansible collection location = /home/labex/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/bin/ansible
  python version = 3.10.x (default, Mar 01 2023, 12:34:56) [GCC 11.2.0]
  jinja version = 3.0.3
  libyaml = True

设置项目结构

现在,让我们为我们的 Ansible playbook 和脚本创建一个项目目录结构:

mkdir -p ~/project/ansible-lab/{playbooks,scripts}
cd ~/project/ansible-lab

在这个结构中:

  • playbooks/ 将包含我们的 Ansible playbooks
  • scripts/ 将包含我们的 shell 脚本,这些脚本将由 Ansible 执行

创建一个简单的脚本

让我们创建一个简单的 shell 脚本,我们将它与 Ansible 一起使用。在 scripts 目录中创建一个名为 hello.sh 的文件:

cd ~/project/ansible-lab/scripts
touch hello.sh

在编辑器中打开 hello.sh 文件,并添加以下内容:

#!/bin/bash
echo "Hello from $(hostname)!"
echo "Current time: $(date)"
echo "Current user: $(whoami)"

此脚本将输出主机名、当前时间和执行脚本的用户。

创建一个 Inventory 文件

接下来,让我们为 Ansible 创建一个简单的 inventory 文件。在 Ansible 中,inventory 文件定义了 Ansible 将管理的主机和组:

cd ~/project/ansible-lab
touch inventory.ini

打开 inventory.ini 文件并添加以下内容:

[local]
localhost ansible_connection=local

此 inventory 文件告诉 Ansible 在本地机器上运行命令。

创建一个基本的 Playbook

现在,让我们创建一个使用 script 模块的基本 Ansible playbook:

cd ~/project/ansible-lab/playbooks
touch run_script.yml

打开 run_script.yml 文件并添加以下内容:

---
- name: Run a script
  hosts: local
  tasks:
    - name: Execute the hello script
      script: ../scripts/hello.sh

此 playbook 将尝试在本地机器上执行我们的 hello.sh 脚本。

通过这个基本设置,我们准备好在接下来的步骤中探索 Ansible 脚本的权限问题。

遇到 Permission Denied 错误

现在我们已经设置好了环境,让我们尝试运行我们的 Ansible playbook,并了解当我们遇到 permission denied 错误时会发生什么。

运行 Playbook

让我们尝试运行我们的 playbook:

cd ~/project/ansible-lab
ansible-playbook -i inventory.ini playbooks/run_script.yml

你可能会遇到类似于以下的错误消息:

TASK [Execute the hello script] *******************************
fatal: [localhost]: FAILED! => {"changed": false, "msg": "failed to execute the script: /bin/sh: 1: /home/labex/.ansible/tmp/ansible-tmp-1234567890.12-123456789012345/AnsiballZ_script.py: Permission denied"}

这是使用 Ansible script 模块时常见的 permission denied 错误。发生此错误的原因是我们的脚本没有执行权限,而执行权限是运行脚本所必需的。

理解 Linux 中的文件权限

在 Linux 中,每个文件都有权限,这些权限决定了谁可以读取、写入或执行它。有三种类型的权限:

  1. 读取 (r):允许读取文件的内容
  2. 写入 (w):允许修改文件
  3. 执行 (x):允许将文件作为程序执行

这些权限被分配给三个不同的用户类别:

  1. 用户 (所有者):文件的所有者
  2. :属于文件组的成员的用户
  3. 其他:所有其他用户

你可以使用 ls -l 命令查看文件的权限:

ls -l ~/project/ansible-lab/scripts/hello.sh

你可能会看到类似这样的输出:

-rw-rw-r-- 1 labex labex 95 Jun 10 12:34 /home/labex/project/ansible-lab/scripts/hello.sh

在此输出中,第一组字符 (-rw-rw-r--) 代表文件的权限:

  • 第一个字符 (-) 表示这是一个常规文件
  • 接下来的三个字符 (rw-) 是所有者的权限(读取、写入,无执行)
  • 接下来的三个 (rw-) 是组的权限
  • 最后的三个 (r--) 是其他用户的权限

请注意,所有用户类别都缺少执行权限 (x),这就是我们收到 permission denied 错误的原因。

检查当前权限

让我们检查一下我们脚本的当前权限:

ls -l ~/project/ansible-lab/scripts/hello.sh

你将看到该脚本缺少执行权限,而 Ansible 运行它需要执行权限。

在下一步中,我们将学习如何解决此权限问题并成功运行我们的 Ansible playbook。

使用 chmod 修复权限问题

修复“Permission denied”错误的最常见方法是向脚本文件添加执行权限。我们可以使用 chmod 命令来做到这一点。

理解 chmod 命令

chmod 命令用于更改 Linux 中文件或目录的权限。该命令有几种指定权限的方式:

  1. 符号模式 (Symbolic mode):使用字母 (r, w, x) 表示权限
  2. 数字模式 (Numeric mode):使用数字 (4, 2, 1) 表示权限

为了我们的目的,我们将使用符号模式来添加执行权限。

向脚本添加执行权限

让我们向我们的脚本添加执行权限:

chmod +x ~/project/ansible-lab/scripts/hello.sh

+x 选项为所有用户类别(用户、组和其他用户)添加执行权限。

让我们验证权限是否已更新:

ls -l ~/project/ansible-lab/scripts/hello.sh

你现在应该看到类似这样的输出:

-rwxrwxr-x 1 labex labex 95 Jun 10 12:34 /home/labex/project/ansible-lab/scripts/hello.sh

注意权限字符串中的 x,这表明已添加执行权限。

再次运行 Playbook

现在我们已经向我们的脚本添加了执行权限,让我们再次运行 Ansible playbook:

cd ~/project/ansible-lab
ansible-playbook -i inventory.ini playbooks/run_script.yml

这次,playbook 应该会成功执行:

PLAY [Run a script] ******************************************

TASK [Gathering Facts] ***************************************
ok: [localhost]

TASK [Execute the hello script] *****************************
changed: [localhost]

PLAY RECAP *************************************************
localhost                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

查看脚本输出

让我们检查一下我们脚本的输出。Ansible 捕获脚本的输出并将其包含在任务结果中。要查看详细输出,让我们修改我们的 playbook 以注册和显示输出:

cd ~/project/ansible-lab/playbooks

编辑 run_script.yml 文件以包含 register 和 debug 任务:

---
- name: Run a script
  hosts: local
  tasks:
    - name: Execute the hello script
      script: ../scripts/hello.sh
      register: script_output

    - name: Display script output
      debug:
        var: script_output.stdout_lines

现在让我们再次运行 playbook:

cd ~/project/ansible-lab
ansible-playbook -i inventory.ini playbooks/run_script.yml

你应该看到类似这样的输出:

PLAY [Run a script] ******************************************

TASK [Gathering Facts] ***************************************
ok: [localhost]

TASK [Execute the hello script] *****************************
changed: [localhost]

TASK [Display script output] ********************************
ok: [localhost] => {
    "script_output.stdout_lines": [
        "Hello from localhost!",
        "Current time: Wed Jun 10 12:34:56 UTC 2023",
        "Current user: labex"
    ]
}

PLAY RECAP *************************************************
localhost                  : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

现在你可以看到我们脚本的完整输出。该脚本以 labex 用户的身份运行,这是我们的当前用户。

通过使用 chmod +x 添加执行权限,我们已经成功修复了“Permission denied”错误,现在可以通过 Ansible 运行我们的脚本。

使用 Become 进行权限提升

有时,你需要使用提升的权限运行脚本,例如运行需要 root 访问权限的命令。在这些情况下,仅向脚本添加执行权限可能还不够。Ansible 提供了 become 指令来使用权限提升运行任务。

理解 Become 指令

Ansible 中的 become 指令允许你以不同的用户身份执行任务,通常具有提升的权限。这类似于在命令行中使用 sudo

become 指令的关键选项包括:

  • become: yes:启用权限提升
  • become_user: <username>:指定要成为哪个用户(默认为 root)
  • become_method: <method>:指定如何成为用户(默认为 sudo)

创建一个需要 Root 权限的脚本

让我们创建一个需要 root 权限才能成功执行的脚本:

cd ~/project/ansible-lab/scripts
touch system_info.sh

将以下内容添加到 system_info.sh 文件中:

#!/bin/bash
echo "System information - requires root privileges"
echo "Hostname: $(hostname)"
echo "Kernel version: $(uname -r)"
echo "Available disk space:"
df -h /
echo "User executing the script: $(whoami)"

使脚本可执行:

chmod +x ~/project/ansible-lab/scripts/system_info.sh

创建一个带有 Become 的 Playbook

现在,让我们创建一个使用 become 指令的 playbook:

cd ~/project/ansible-lab/playbooks
touch root_script.yml

将以下内容添加到 root_script.yml 文件中:

---
- name: Run a script as root
  hosts: local
  tasks:
    - name: Execute the system info script
      script: ../scripts/system_info.sh
      become: yes
      register: script_output

    - name: Display script output
      debug:
        var: script_output.stdout_lines

become: yes 指令告诉 Ansible 以提升的权限运行脚本。

使用 Become 运行 Playbook

让我们运行我们的新 playbook:

cd ~/project/ansible-lab
ansible-playbook -i inventory.ini playbooks/root_script.yml

playbook 应该会成功执行,你应该看到类似这样的输出:

PLAY [Run a script as root] *********************************

TASK [Gathering Facts] **************************************
ok: [localhost]

TASK [Execute the system info script] **********************
changed: [localhost]

TASK [Display script output] *******************************
ok: [localhost] => {
    "script_output.stdout_lines": [
        "System information - requires root privileges",
        "Hostname: localhost",
        "Kernel version: 5.15.0-1015-aws",
        "Available disk space:",
        "Filesystem      Size  Used Avail Use% Mounted on",
        "/dev/root        59G   17G   42G  29% /",
        "User executing the script: root"
    ]
}

PLAY RECAP ************************************************
localhost                  : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

请注意,在脚本输出中,“User executing the script”现在是 root,而不是 labex。这表明我们的脚本由于 become: yes 指令而以提升的权限运行。

理解何时使用 Become

你应该在以下情况下使用 become 指令:

  1. 当脚本需要访问需要 root 权限的系统文件或目录时
  2. 当脚本需要安装软件包或修改系统配置时
  3. 当脚本需要运行通常需要在命令行中使用 sudo 的命令时

通过适当地使用 become 指令,你可以确保你的脚本具有成功执行所需的权限,从而避免 permission denied 错误。

避免权限问题的最佳实践

现在我们已经了解了如何使用 chmodbecome 修复权限问题,让我们探讨一些最佳实践,以防止权限问题首先发生。

1. 在使用脚本之前,始终使其可执行

在 Ansible 中使用脚本之前,请务必确保它具有执行权限:

chmod +x path/to/script.sh

在你的脚本创建过程中这样做是一个好习惯。

2. 使用版本控制和正确的文件模式

如果你正在使用 Git 或其他版本控制系统,请确保它保留文件模式(权限)。在 Git 中,你可以使用以下命令进行配置:

git config core.fileMode true

对于现有的存储库,你可能需要更新文件模式:

git update-index --chmod=+x path/to/script.sh

3. 创建一个脚本来检查和修复权限

让我们创建一个实用脚本,用于检查和修复我们项目中所有脚本的权限:

cd ~/project/ansible-lab
touch fix_permissions.sh

将以下内容添加到 fix_permissions.sh 文件中:

#!/bin/bash
echo "Fixing permissions for scripts in ansible-lab"

## Find all .sh files and make them executable
find ~/project/ansible-lab -name "*.sh" -type f -exec chmod +x {} \;

echo "Done. All script files now have execute permissions."

使脚本可执行:

chmod +x ~/project/ansible-lab/fix_permissions.sh

运行脚本以确保你项目中的所有脚本都具有执行权限:

./fix_permissions.sh

4. 使用 Ansible 的 File 模块设置权限

你还可以使用 Ansible 的 file 模块来确保脚本文件具有正确的权限。让我们创建一个 playbook 来执行此操作:

cd ~/project/ansible-lab/playbooks
touch set_permissions.yml

将以下内容添加到 set_permissions.yml 文件中:

---
- name: Set correct permissions for scripts
  hosts: local
  tasks:
    - name: Find all script files
      find:
        paths: /home/labex/project/ansible-lab
        patterns: "*.sh"
        recurse: yes
      register: script_files

    - name: Make script files executable
      file:
        path: "{{ item.path }}"
        mode: "0755"
      loop: "{{ script_files.files }}"

运行此 playbook 以确保所有脚本都具有正确的权限:

cd ~/project/ansible-lab
ansible-playbook -i inventory.ini playbooks/set_permissions.yml

5. 创建一个预检查 Playbook

最后,让我们创建一个预检查 playbook,在你的主 playbook 之前运行,以验证所有内容是否已正确设置:

cd ~/project/ansible-lab/playbooks
touch preflight_check.yml

将以下内容添加到 preflight_check.yml 文件中:

---
- name: Pre-flight checks
  hosts: local
  tasks:
    - name: Check if scripts are executable
      find:
        paths: /home/labex/project/ansible-lab
        patterns: "*.sh"
        recurse: yes
      register: script_files

    - name: Verify script permissions
      stat:
        path: "{{ item.path }}"
      register: stat_results
      loop: "{{ script_files.files }}"
      failed_when: not stat_results.stat.executable
      ignore_errors: yes

此 playbook 检查所有 .sh 文件是否可执行,并报告任何不可执行的文件。

让我们运行预检查:

cd ~/project/ansible-lab
ansible-playbook -i inventory.ini playbooks/preflight_check.yml

如果你的所有脚本都具有正确的权限,playbook 应该在没有错误的情况下完成。如果任何脚本缺少执行权限,你将看到一个通知。

通过遵循这些最佳实践,你可以避免 Ansible 脚本中出现 permission denied 错误,并确保你的自动化顺利运行。

总结

在这个实验中,你已经学习了在使用 Ansible script 模块时,如何识别和解决“Permission denied”错误。

本实验的主要内容包括:

  1. 了解 Linux 中文件权限的重要性,以及它们如何影响 Ansible 中的脚本执行
  2. 使用 chmod 命令向脚本文件添加执行权限
  3. 利用 Ansible 的 become 指令进行权限提升,以运行需要 root 访问权限的脚本
  4. 实施最佳实践以防止权限问题,包括:
    • 在使用脚本之前,使其可执行
    • 在版本控制中维护正确的文件模式
    • 创建实用脚本来检查和修复权限
    • 使用 Ansible 的 file 模块设置权限
    • 实施预检查以验证你的环境

通过应用这些技术,你可以确保你的 Ansible 脚本顺利运行,而不会出现权限错误,从而提高你的自动化工作流程的可靠性。

你在本实验中学习的技能是有效使用 Ansible 的基础,并且可以应用于广泛的自动化场景。