如何在 Ansible Playbook 中配置 gather_facts 选项

AnsibleAnsibleBeginner
立即练习

💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版

介绍

Ansible 是一个强大的 IT 自动化工具,可以帮助系统管理员和开发人员高效地管理基础设施。它的一个关键特性是能够收集关于目标系统的信息,这些信息被称为“facts”(事实)。Ansible 中的 gather_facts 选项决定了在 playbook 执行期间是否以及如何收集这些信息。

在这个实践实验(Lab)中,你将学习如何在 Ansible playbooks 中配置 gather_facts 选项。你将探索不同的设置,了解何时启用或禁用事实收集,并发现如何使用收集到的事实使你的 playbooks 更加动态和高效。通过完成这个实验,你将能够通过根据你的特定需求控制事实收集过程来优化你的 Ansible 工作流程。

安装 Ansible 并探索 gather_facts 选项

让我们从安装 Ansible 并探索 gather_facts 选项的作用开始。在这一步中,我们将安装 Ansible,创建一个简单的 inventory(清单),并运行一个命令来查看收集了哪些 facts(事实)。

安装 Ansible

首先,让我们在我们的系统上安装 Ansible:

sudo apt update
sudo apt install -y ansible

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

ansible --version

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

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, Aug 14 2022, 00:00:00) [GCC 11.2.0]
  jinja version = 3.0.3
  libyaml = True

创建一个简单的 inventory(清单)

现在,让我们创建一个简单的 inventory 文件来使用。inventory 文件定义了 Ansible 将要管理的主机。对于这个实验,我们将创建一个本地 inventory:

mkdir -p ~/project/ansible
cd ~/project/ansible

使用编辑器创建一个名为 hosts 的 inventory 文件:

  1. 点击 WebIDE 中的 Explorer 图标
  2. 导航到 /home/labex/project/ansible 目录
  3. 右键单击并选择“New File”(新建文件)
  4. 将文件命名为 hosts
  5. 添加以下内容:
[local]
localhost ansible_connection=local

这个 inventory 设置了一个名为 local 的组,其中只有一个主机 - localhostansible_connection=local 参数告诉 Ansible 直接在本地机器上执行命令,而无需使用 SSH。

探索 gather_facts

让我们运行一个简单的 Ansible 命令来查看默认情况下收集了哪些 facts(事实):

cd ~/project/ansible
ansible local -i hosts -m setup

上面的命令使用:

  • local:来自我们 inventory 的组
  • -i hosts:指定我们的 inventory 文件
  • -m setup:运行 setup 模块,该模块收集 facts(事实)

你将看到一个大型的 JSON 输出,其中包含有关你系统的详细信息,包括:

  • 硬件信息(CPU,内存)
  • 网络配置
  • 操作系统详细信息
  • 环境变量
  • 等等

这些信息是 Ansible 在启用 gather_facts 时收集的(这是默认行为)。这些 facts(事实)可以在 playbooks 中使用,以便根据目标系统的特征做出决策或自定义任务。

创建一个具有默认事实收集的基本 playbook

在这一步中,我们将创建一个基本 Ansible playbook,它使用默认的事实收集行为并显示一些收集到的信息。

理解 Ansible playbooks

一个 Ansible playbook 是一个 YAML 文件,其中包含要在受管理主机上执行的任务列表。Playbooks 提供了一种以简单、人类可读的格式定义配置、部署和编排步骤的方法。

创建你的第一个 playbook

让我们创建一个简单的 playbook,它将显示 Ansible 默认收集的一些 facts(事实):

  1. 在 WebIDE 中,导航到 /home/labex/project/ansible 目录
  2. 创建一个名为 facts_playbook.yml 的新文件
  3. 添加以下内容:
---
- name: Show System Facts
  hosts: local
  ## By default, gather_facts is set to 'true'

  tasks:
    - name: Display operating system
      debug:
        msg: "Operating System: {{ ansible_distribution }} {{ ansible_distribution_version }}"

    - name: Display CPU information
      debug:
        msg: "CPU: {{ ansible_processor[1] }} with {{ ansible_processor_cores }} cores"

    - name: Display memory information
      debug:
        msg: "Total Memory: {{ ansible_memtotal_mb }} MB"

    - name: Display Python version
      debug:
        msg: "Python version: {{ ansible_python_version }}"

这个 playbook:

  • 以我们在 inventory 中定义的 local 组为目标
  • 隐式地启用事实收集(默认行为)
  • 包含四个任务,这些任务显示 Ansible 收集的不同信息

运行 playbook

现在让我们运行 playbook,看看收集到的 facts(事实)是如何运作的:

cd ~/project/ansible
ansible-playbook -i hosts facts_playbook.yml

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

PLAY [Show System Facts] *****************************************************

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

TASK [Display operating system] **********************************************
ok: [localhost] => {
    "msg": "Operating System: Ubuntu 22.04"
}

TASK [Display CPU information] ***********************************************
ok: [localhost] => {
    "msg": "CPU: Intel(R) Xeon(R) CPU with 2 cores"
}

TASK [Display memory information] ********************************************
ok: [localhost] => {
    "msg": "Total Memory: 3907 MB"
}

TASK [Display Python version] ************************************************
ok: [localhost] => {
    "msg": "Python version: 3.10.6"
}

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

注意输出中的第一个任务:TASK [Gathering Facts]。这是 Ansible 在运行我们定义的任何任务之前自动收集 facts(事实),因为 gather_facts 的默认值为 true

然后,playbook 成功地使用收集到的 facts(事实)显示了关于你系统的信息。每个 fact(事实)都使用带有 ansible_ 前缀的变量来引用。

禁用事实收集以提高性能

在这一步中,我们将学习如何禁用事实收集,以在不需要事实的情况下提高 playbook 的性能。

理解何时禁用事实收集

虽然收集事实很有用,但在某些情况下,它会增加不必要的开销:

  • 当你运行不需要系统信息的简单任务时
  • 当你经常执行 playbooks 并且事实没有改变时
  • 当你想要优化 playbook 执行时间时

禁用事实收集可以显著提高 playbook 的执行速度,尤其是在管理多个主机时。

创建一个禁用事实收集的 playbook

让我们创建一个新的 playbook,它禁用了事实收集:

  1. 在 WebIDE 中,导航到 /home/labex/project/ansible 目录
  2. 创建一个名为 no_facts_playbook.yml 的新文件
  3. 添加以下内容:
---
- name: Playbook with Disabled Fact Gathering
  hosts: local
  gather_facts: false

  tasks:
    - name: Display current time
      command: date
      register: current_time

    - name: Show the current time
      debug:
        msg: "Current time is: {{ current_time.stdout }}"

    - name: List files in the project directory
      command: ls -la ~/project
      register: file_list

    - name: Show file list
      debug:
        msg: "Project directory contents:\n{{ file_list.stdout }}"

这个 playbook:

  • 使用 gather_facts: false 显式地禁用事实收集
  • 运行不依赖于系统事实的命令
  • 使用 register 关键字捕获命令输出
  • 使用 debug 模块显示捕获的信息

运行禁用事实收集的 playbook

让我们运行 playbook 并观察差异:

cd ~/project/ansible
ansible-playbook -i hosts no_facts_playbook.yml

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

PLAY [Playbook with Disabled Fact Gathering] *********************************

TASK [Display current time] **************************************************
changed: [localhost]

TASK [Show the current time] *************************************************
ok: [localhost] => {
    "msg": "Current time is: Wed May 17 15:30:45 UTC 2023"
}

TASK [List files in the project directory] ***********************************
changed: [localhost]

TASK [Show file list] ********************************************************
ok: [localhost] => {
    "msg": "Project directory contents:\ntotal 20\ndrwxr-xr-x 3 labex labex 4096 May 17 15:25 .\ndrwxr-xr-x 4 labex labex 4096 May 17 15:20 ..\ndrwxr-xr-x 2 labex labex 4096 May 17 15:25 ansible\n"
}

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

注意,这次输出中没有 Gathering Facts 任务。Playbook 直接从我们定义的第一个任务开始。

比较执行时间

为了查看性能差异,让我们创建一个简单的计时脚本:

  1. 在 WebIDE 中,导航到 /home/labex/project/ansible 目录
  2. 创建一个名为 compare_timing.sh 的新文件
  3. 添加以下内容:
#!/bin/bash

echo "Running playbook with fact gathering enabled..."
time ansible-playbook -i hosts facts_playbook.yml > /dev/null

echo -e "\nRunning playbook with fact gathering disabled..."
time ansible-playbook -i hosts no_facts_playbook.yml > /dev/null
  1. 使脚本可执行:
chmod +x compare_timing.sh
  1. 运行比较脚本:
./compare_timing.sh

你应该看到输出显示禁用事实收集的 playbook 运行速度比启用事实收集的 playbook 快。在我们的简单示例中,差异可能很小,但在多个远程主机上运行复杂的 playbooks 时,差异可能很大。

使用选择性事实收集

在某些情况下,你可能只需要特定的 facts(事实),而不是所有系统信息。Ansible 允许选择性事实收集,以优化性能,同时仍然收集你需要的信息。

理解事实子集

Ansible 将 facts(事实)组织成子集,例如:

  • all:所有 facts(默认)
  • min / minimal:一组最少的 facts
  • hardware:CPU、内存和设备信息
  • network:网络接口和路由信息
  • virtual:虚拟化详细信息
  • ohai:来自 Ohai 的 facts(如果可用)
  • facter:来自 Facter 的 facts(如果可用)

通过仅选择你需要的事实,你可以提高 playbook 的性能,同时仍然可以访问必要的信息。

创建一个具有选择性事实收集的 playbook

让我们创建一个 playbook,它仅收集与硬件相关的事实:

  1. 在 WebIDE 中,导航到 /home/labex/project/ansible 目录
  2. 创建一个名为 selective_facts_playbook.yml 的新文件
  3. 添加以下内容:
---
- name: Selective Fact Gathering
  hosts: local
  gather_facts: true
  gather_subset:
    - "!all" ## Exclude all facts by default
    - "hardware" ## Then include only hardware facts

  tasks:
    - name: Display CPU information
      debug:
        msg: "CPU: {{ ansible_processor[1] }} with {{ ansible_processor_cores }} cores"

    - name: Display memory information
      debug:
        msg: "Total Memory: {{ ansible_memtotal_mb }} MB"

    - name: Try to access network facts (should fail)
      debug:
        msg: "Default IPv4 Address: {{ ansible_default_ipv4.address }}"
      ignore_errors: true

这个 playbook:

  • 使用 gather_facts: true 启用事实收集
  • 使用 gather_subset 来限制收集哪些 facts(事实)
  • 首先使用 !all 排除所有 facts
  • 然后仅使用 hardware 包含硬件 facts
  • 尝试访问网络 facts(未收集),以演示此限制

运行具有选择性事实收集的 playbook

让我们运行 playbook,看看选择性事实收集是如何运作的:

cd ~/project/ansible
ansible-playbook -i hosts selective_facts_playbook.yml

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

PLAY [Selective Fact Gathering] **********************************************

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

TASK [Display CPU information] ***********************************************
ok: [localhost] => {
    "msg": "CPU: Intel(R) Xeon(R) CPU with 2 cores"
}

TASK [Display memory information] ********************************************
ok: [localhost] => {
    "msg": "Total Memory: 3907 MB"
}

TASK [Try to access network facts (should fail)] *****************************
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'address'..."}
...ignoring

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

注意,前两个任务成功了,因为它们访问了已收集的硬件 facts,但第三个任务失败了,因为没有收集网络 facts。我们使用了 ignore_errors: true 来在发生此错误的情况下继续 playbook 的执行。

创建一个具有多个事实子集的 playbook

现在,让我们创建一个 playbook,它收集硬件和网络 facts:

  1. 在 WebIDE 中,创建一个名为 multiple_subsets_playbook.yml 的新文件
  2. 添加以下内容:
---
- name: Multiple Fact Subsets
  hosts: local
  gather_facts: true
  gather_subset:
    - "!all" ## Exclude all facts by default
    - "hardware" ## Include hardware facts
    - "network" ## Include network facts

  tasks:
    - name: Display CPU information
      debug:
        msg: "CPU: {{ ansible_processor[1] }} with {{ ansible_processor_cores }} cores"

    - name: Display memory information
      debug:
        msg: "Total Memory: {{ ansible_memtotal_mb }} MB"

    - name: Display network information
      debug:
        msg: "Default IPv4 Address: {{ ansible_default_ipv4.address }}"

运行此 playbook:

ansible-playbook -i hosts multiple_subsets_playbook.yml

这次,所有任务都应该成功,因为我们已经收集了硬件和网络 facts。

在条件任务中使用已收集的事实

使用已收集的事实最强大的功能之一是在你的 playbooks 中实现条件逻辑。在这一步中,我们将创建一个 playbook,它使用 facts(事实)来决定运行哪些任务。

理解 Ansible 中的条件任务

Ansible 允许你使用 when 关键字,根据变量、facts(事实)或任务结果有条件地执行任务。这使你能够创建更具动态性和适应性的 playbooks。

创建一个具有条件任务的 playbook

让我们创建一个 playbook,它根据操作系统执行不同的操作:

  1. 在 WebIDE 中,导航到 /home/labex/project/ansible 目录
  2. 创建一个名为 conditional_facts_playbook.yml 的新文件
  3. 添加以下内容:
---
- name: Conditional Tasks Based on Facts
  hosts: local
  gather_facts: true

  tasks:
    - name: Display OS information
      debug:
        msg: "Running on {{ ansible_distribution }} {{ ansible_distribution_version }}"

    - name: Task for Ubuntu systems
      debug:
        msg: "This is an Ubuntu system. Would run apt commands here."
      when: ansible_distribution == "Ubuntu"

    - name: Task for CentOS systems
      debug:
        msg: "This is a CentOS system. Would run yum commands here."
      when: ansible_distribution == "CentOS"

    - name: Task for systems with at least 2GB RAM
      debug:
        msg: "This system has {{ ansible_memtotal_mb }} MB RAM, which is sufficient for our application."
      when: ansible_memtotal_mb >= 2048

    - name: Task for systems with less than 2GB RAM
      debug:
        msg: "This system has only {{ ansible_memtotal_mb }} MB RAM, which may not be sufficient."
      when: ansible_memtotal_mb < 2048

这个 playbook:

  • 收集有关系统的所有 facts(事实)
  • 显示操作系统信息
  • 根据操作系统类型有条件地执行任务
  • 根据 RAM 的数量有条件地执行任务

运行条件 playbook

让我们运行 playbook,看看条件任务是如何运作的:

cd ~/project/ansible
ansible-playbook -i hosts conditional_facts_playbook.yml

由于我们正在 Ubuntu 上运行,你应该看到类似这样的输出:

PLAY [Conditional Tasks Based on Facts] **************************************

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

TASK [Display OS information] ************************************************
ok: [localhost] => {
    "msg": "Running on Ubuntu 22.04"
}

TASK [Task for Ubuntu systems] ***********************************************
ok: [localhost] => {
    "msg": "This is an Ubuntu system. Would run apt commands here."
}

TASK [Task for CentOS systems] ***********************************************
skipping: [localhost]

TASK [Task for systems with at least 2GB RAM] ********************************
ok: [localhost] => {
    "msg": "This system has 3907 MB RAM, which is sufficient for our application."
}

TASK [Task for systems with less than 2GB RAM] *******************************
skipping: [localhost]

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

注意,一些任务是如何执行的,而另一些任务是根据条件跳过的。CentOS 任务被跳过,因为我们正在 Ubuntu 上运行,并且“小于 2GB RAM”任务被跳过,因为我们的系统有超过 2GB RAM。

创建一个更实用的例子

现在,让我们创建一个更实用的例子,可以在真实环境中使用的例子:

  1. 在 WebIDE 中,创建一个名为 practical_conditional_playbook.yml 的新文件
  2. 添加以下内容:
---
- name: Practical Conditional Playbook
  hosts: local
  gather_facts: true

  vars:
    app_dir: "/home/labex/project/app"

  tasks:
    - name: Create application directory
      file:
        path: "{{ app_dir }}"
        state: directory
        mode: "0755"

    - name: Configure for production environment
      copy:
        dest: "{{ app_dir }}/config.yml"
        content: |
          environment: production
          memory_limit: high
          debug: false
      when: ansible_memtotal_mb >= 4096

    - name: Configure for development environment
      copy:
        dest: "{{ app_dir }}/config.yml"
        content: |
          environment: development
          memory_limit: low
          debug: true
      when: ansible_memtotal_mb < 4096

    - name: Display configuration
      command: cat {{ app_dir }}/config.yml
      register: config_content

    - name: Show configuration
      debug:
        msg: "{{ config_content.stdout_lines }}"

这个 playbook:

  • 为应用程序创建一个目录
  • 根据可用的系统内存写入不同的配置文件
  • 显示结果配置

运行实用的 playbook:

ansible-playbook -i hosts practical_conditional_playbook.yml

此示例演示了如何使用已收集的 facts(事实)根据系统特性自动调整配置。

总结

在这个实验中,你已经学会了如何在 Ansible playbooks 中有效地配置和使用 gather_facts 选项。以下是你已经完成的工作的总结:

  1. 基本事实收集:你已经安装了 Ansible,并探索了默认的事实收集行为,看到了 Ansible 收集的各种系统信息。

  2. 禁用事实收集:你已经学会了如何禁用事实收集,以在不需要 facts(事实)时提高 playbook 的性能。

  3. 选择性事实收集:你已经发现了如何仅收集特定子集的 facts(事实),以在性能和拥有必要信息之间取得平衡。

  4. 条件任务:你已经在你的 playbooks 中实现了基于已收集 facts(事实)的条件逻辑,从而允许根据系统特性进行动态行为。

  5. 实际应用:你已经创建了实际的例子,演示了如何在实际场景中使用已收集的 facts(事实)。

通过掌握 gather_facts 选项,你可以优化你的 Ansible playbooks,以获得更好的性能,同时仍然可以访问你需要的系统信息。这些知识将帮助你创建更有效、更灵活、更强大的自动化工作流程。

一些需要记住的最佳实践:

  • 仅在必要时启用事实收集
  • 当你只需要特定信息时,使用选择性事实收集
  • 利用已收集的 facts(事实)进行条件任务,使你的 playbooks 更具适应性
  • 考虑在同一主机上频繁运行 playbooks 时缓存 facts(事实)

有了这些技能,你已经为你的基础设施管理需求创建更复杂和更有效的 Ansible 自动化做好了充分准备。