常见问题解答

以下是一些常见问题及其解答。

所有的模块都去哪了?

2019 年 7 月,我们宣布集合(Collections)将成为 Ansible 内容分发的未来。集合是一种用于 Ansible 内容的分发格式,可以包含 Playbook、角色、模块和插件。在 Ansible 2.9 中,我们增加了对集合的支持。在 Ansible 2.10 中,我们 从主要的 ansible/ansible 仓库中提取了大部分模块,并将其放入了 集合 中。集合可以由 Ansible 团队、Ansible 社区或 Ansible 合作伙伴维护。现在 ansible/ansible 仓库 仅包含基础特性和功能(例如将模块代码复制到托管节点)的代码。这些代码也被称为 ansible-core(在 2.10 版本中曾短暂地称为 ansible-base)。

  • 要了解有关使用集合的更多信息,请参阅 使用 Ansible 集合

  • 要了解有关开发集合的更多信息,请参阅 开发集合

  • 要了解有关为现有集合做出贡献的更多信息,请参阅各个集合仓库中的准则,或参阅 为 Ansible 维护的集合做出贡献,以便为 Ansible 维护的集合之一做出贡献。

这个特定的模块去哪了?

如果您正在搜索特定的模块,可以查看 runtime.yml 文件,其中列出了我们从主要的 ansible/ansible 仓库中提取的每个模块的初步去向。自那时起,一些模块可能又移动了。您也可以在 Ansible Galaxy 上搜索或在我们的 聊天频道 之一询问。

在磁盘较慢的系统上,我该如何加速 Ansible?

Ansible 在树莓派(Raspberry PI)等磁盘较慢的系统上可能会感觉迟钝。请参阅 如果 libyaml 不可用,Ansible 可能运行缓慢 以获取有关如何改善此状况的建议。

如何为任务或整个 Play 设置 PATH 或其他环境变量?

可以使用 environment 关键字来设置环境变量。它可以用于 Play 中的任务或其他级别。

shell:
  cmd: date
environment:
  LANG=fr_FR.UTF-8
hosts: servers
environment:
  PATH: "{{ ansible_env.PATH }}:/thingy/bin"
  SOME: value

注意

从 2.0.1 版本开始,gather_facts 的 setup 任务也继承了 Play 中的 environment 指令,如果是在 Play 级别设置,您可能需要使用 |default 过滤器来避免错误。

如果不同的机器需要使用不同的用户账户或端口登录,我该如何处理?

在清单文件中设置清单变量是最简单的方法。

例如,假设这些主机有不同的用户名和端口:

[webservers]
asdf.example.com  ansible_port=5000   ansible_user=alice
jkl.example.com   ansible_port=5001   ansible_user=bob

如果您愿意,还可以指定要使用的连接类型:

[testcluster]
localhost           ansible_connection=local
/path/to/chroot1    ansible_connection=chroot
foo.example.com     ansible_connection=paramiko

您可能还希望将这些保存在组变量中,或者存放在 group_vars/<groupname> 文件中。有关如何组织变量的更多信息,请参阅文档的其余部分。

如何让 Ansible 复用连接、启用 Kerberos SSH 或让 Ansible 使用我本地的 SSH 配置文件?

将配置文件中的默认连接类型更改为 ssh,或者使用 -c ssh 来使用原生 OpenSSH 进行连接,而不是使用 Python 的 paramiko 库。在 Ansible 1.2.1 及更高版本中,如果 OpenSSH 版本足够新,支持 ControlPersist 选项,则默认将使用 ssh

Paramiko 非常适合入门,但 OpenSSH 类型提供了许多高级选项。如果您使用此连接类型,建议从足以支持 ControlPersist 的机器运行 Ansible。您仍然可以管理旧的客户端。如果您使用的是 RHEL 6、CentOS 6、SLES 10 或 SLES 11,则其 OpenSSH 版本比较老,因此请考虑从 Fedora 或 openSUSE 客户端进行管理,或者只使用 paramiko。

我们保留 paramiko 作为默认值,因为如果您是首次在这些企业操作系统上安装 Ansible,它为新用户提供了更好的体验。

如何配置跳转主机(Jump Host)以访问我无法直接访问的服务器?

您可以在 ansible_ssh_common_args 清单变量中设置 ProxyCommand。连接到相关主机时,该变量中指定的任何参数都会添加到 sftp/scp/ssh 命令行中。考虑以下清单组:

[gatewayed]
foo ansible_host=192.0.2.1
bar ansible_host=192.0.2.2

您可以创建包含以下内容的 group_vars/gatewayed.yml

ansible_ssh_common_args: '-o ProxyCommand="ssh -W %h:%p -q user@gateway.example.com"'

当尝试连接到 gatewayed 组中的任何主机时,Ansible 会将这些参数附加到命令行中。(这些参数将与 ansible.cfg 中的任何 ssh_args 一起使用,因此您无需在 ansible_ssh_common_args 中重复全局 ControlPersist 设置。)

请注意,ssh -W 仅适用于 OpenSSH 5.4 或更高版本。在旧版本中,必须在堡垒机(Bastion Host)上执行 nc %h:%p 或类似的命令。

在早期版本的 Ansible 中,必须在 ~/.ssh/config 中为一个或多个主机配置合适的 ProxyCommand,或者通过在 ansible.cfg 中设置 ssh_args 进行全局配置。

如何让 Ansible 及时发现目标机器已宕机?

您可以将 -o ServerAliveInterval=NumberOfSeconds 添加到 SSH 连接插件ssh_args 参数中。如果没有此选项,SSH(进而 Ansible)将等到 TCP 连接超时。另一个解决方案是将 ServerAliveInterval 添加到您的全局 SSH 配置中。一个好的 ServerAliveInterval 值由您决定;请记住,ServerAliveCountMax=3 是 SSH 的默认值,因此您设置的任何值在终止 SSH 会话之前都会被乘以 3 倍。

如何加速在云服务提供商(EC2、OpenStack 等)上的 Ansible 运行速度?

不要尝试从您的笔记本电脑管理云服务提供商中的机器集群。相反,应首先连接到该云服务提供商内部的管理节点,并从那里运行 Ansible。

如果远程机器上的 /usr/bin/python 没有 Python 解释器,我该如何处理?

虽然您可以用任何语言编写 Ansible 模块,但大多数 Ansible 模块(包括 Ansible 正常工作所必需的那些模块)都是用 Python 编写的。

默认情况下,Ansible 假设它可以在远程系统上找到一个 /usr/bin/python,即 Python 2.6 或更高版本,或者是 Python 3.5 或更高版本。

在任何主机上设置清单变量 ansible_python_interpreter 将告诉 Ansible 自动用该值替换 Python 解释器。因此,如果系统上的 /usr/bin/python 指向的不是兼容的 Python 解释器,您可以指向系统上的任何所需 Python 版本。

某些平台可能默认仅安装了 Python 3。如果它未安装为 /usr/bin/python,则需要通过 ansible_python_interpreter 配置解释器的路径。虽然大多数核心模块可以在 Python 3 下工作,但可能存在一些特殊用途的模块无法正常工作,或者您可能会在极端情况下遇到错误。作为临时的变通方法,您可以在托管主机上安装 Python 2,并通过 ansible_python_interpreter 配置 Ansible 使用该 Python。如果模块文档中没有提到该模块需要 Python 2,您也可以在我们的 Bug 跟踪器 上报告错误,以便在以后的版本中修复不兼容问题。

不要替换 Python 模块的 shebang 行。Ansible 会在部署时自动为您完成此操作。

此外,这适用于任何解释器,例如 ruby:ansible_ruby_interpreter,perl:ansible_perl_interpreter 等,因此您可以将其用于以任何脚本语言编写的自定义模块,并控制解释器的位置。

请记住,如果您在模块的 shebang 行中使用 env#!/usr/bin/env <other>),这将不起作用,并会被评估为一个字符串(包括 env<other> 之间的空格)。不支持也不建议在此处添加参数。

在安装 Ansible 过程中,我该如何处理 Ansible 软件包所需的依赖项?

安装 Ansible 时,有时可能会遇到诸如 No package ‘libffi’ foundfatal error: Python.h: No such file or directory 之类的错误。这些错误通常是由缺失的软件包引起的,这些软件包是 Ansible 所需软件包的依赖项。例如,libffi 软件包是 pynaclparamiko 的依赖项(Ansible -> paramiko -> pynacl -> libffi)。

为了解决此类依赖问题,您可能需要使用操作系统的原生包管理器(如 yumdnfapt)来安装所需的软件包,或者按照软件包安装指南中的说明进行安装。

有关此类依赖项及其安装方法,请参阅相应软件包的文档。

常见系统问题

在 virtualenv 中运行

您可以非常简单地在控制节点上的 virtualenv 中安装 Ansible。

$ virtualenv ansible
$ source ./ansible/bin/activate
$ pip install ansible

如果您想在 Python 3 而非 Python 2 下运行,可能需要稍作更改。

$ virtualenv -p python3 ansible
$ source ./ansible/bin/activate
$ pip install ansible

如果您需要使用 pip 无法获取的任何库(例如,在启用了 SELinux 的 Red Hat Enterprise Linux 或 Fedora 等系统上的 SELinux Python 绑定),则需要将它们安装到 virtualenv 中。有两种方法:

  • 创建 virtualenv 时,指定 --system-site-packages 以利用系统 Python 中安装的任何库。

    $ virtualenv ansible --system-site-packages
    
  • 从系统中手动复制这些文件。例如,对于 SELinux 绑定,您可以执行:

    $ virtualenv ansible --system-site-packages
    $ cp -r -v /usr/lib64/python3.*/site-packages/selinux/ ./py3-ansible/lib64/python3.*/site-packages/
    $ cp -v /usr/lib64/python3.*/site-packages/*selinux*.so ./py3-ansible/lib64/python3.*/site-packages/
    

在 macOS 上作为控制节点运行

当在 macOS 作为控制节点的系统上执行 Ansible 时,可能会遇到以下错误:

错误

+[__NSCFConstantString initialize] may have been in progress in another thread when fork() was called. We cannot safely call it or ignore it in the fork() child process. Crashing instead. Set a breakpoint on objc_initializeAfterForkError to debug. ERROR! A worker was found in a dead state

通常建议的解决方法是在 shell 中设置以下环境变量:

$ export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES

在 macOS 上作为目标节点运行

当通过 SSH 管理运行 macOS Monterey 12、macOS Ventura 13 或更高版本的系统时,可能会出现以下错误:

错误

“eDSPermissionError” DS Error: -14120 (eDSPermissionError)

这清楚地表明尚未启用 “允许远程用户完全磁盘访问”

另请参阅

有关更多详细信息,请查看 Apple 官方用户指南文章

在 BSD 上运行

在 Solaris 上运行

默认情况下,Solaris 10 及更早版本运行非 POSIX shell,它不能正确扩展 Ansible 使用的默认 tmp 目录(~/.ansible/tmp)。如果您在 Solaris 机器上看到模块失败,这很可能是问题所在。有几种变通方法:

  • 您可以将 remote_tmp 设置为在使用 shell 时可以正确扩展的路径(请参阅插件文档以了解 C shellfish shellPowershell)。例如,在 ansible 配置文件中,您可以设置:

    remote_tmp=$HOME/.ansible/tmp
    

    在 Ansible 2.5 及更高版本中,您还可以像这样在清单中为每个主机设置:

    solaris1 ansible_remote_tmp=$HOME/.ansible/tmp
    
  • 您可以将 ansible_shell_executable 设置为 POSIX 兼容 shell 的路径。例如,许多 Solaris 主机在 /usr/xpg4/bin/sh 有一个 POSIX shell,因此您可以像这样在清单中设置它:

    solaris1 ansible_shell_executable=/usr/xpg4/bin/sh
    

    (如果您安装了 bash、ksh 和 zsh,它们也应该是 POSIX 兼容的)。

在 z/OS 上运行

  • 一般而言,z/OS 不能用作 Ansible 控制节点。有关更多详细信息,请参阅 将 z/OS 用作控制节点

  • 当目标主机上的 Python 解释器路径在默认位置找不到时,可能会导致以下错误:

    错误

    /usr/bin/python: FSUM7351 not found

    Ansible 需要一个 Python 解释器在远程主机上执行模块,并在“默认”路径 /usr/bin/python 下进行检查。

    在 z/OS 上,Python 3 解释器(来自 IBM Open Enterprise SDK for Python)通常安装在不同的路径中,通常类似于:/usr/lpp/cyp/v3r12/pyz

    Python 解释器的路径可以使用 Ansible 清单变量 ansible_python_interpreter 进行配置。例如:

    zos1 ansible_python_interpreter:/usr/lpp/cyp/v3r12/pyz
    

    有关更多详细信息,请参阅:如果远程机器上的 /usr/bin/python 没有 Python 解释器,我该如何处理?

  • 当未启用 ANSIBLE_PIPELINING 时,或者启用了 Ansible 流水线但 PYTHONSTDINENCODING 属性未正确设置时,可能会导致以下错误:

    错误

    SyntaxError: Non-UTF-8 code starting with ‘\x81’ in file <stdin> on line 1, but no encoding declared; see https://peps.pythonlang.cn/pep-0263/ for details

    请注意,下面的十六进制 '\x81' 可能会根据导致错误的数据源而有所不同。

    当启用 Ansible 流水线时,Ansible 会通过 Python 的 stdin 管道将所有模块代码传递到远程目标,并在单次调用中运行它们。有关流水线的更多详细信息,请参阅:流水线(Pipelining)

    对于在 z/OS 托管节点上执行的任何任务,请在环境中包含以下内容:

    PYTHONSTDINENCODING: "cp1047"
    
  • 某些语言环境(LE)配置可启用 z/OS 系统上 Python(IBM Open Enterprise SDK for Python)所需的自动转换和自动文件标记功能。

    在为任何 z/OS 托管节点设置远程环境时,请包含以下配置:

    _BPXK_AUTOCVT: "ON"
    _CEE_RUNOPTS: "FILETAG(AUTOCVT,AUTOTAG) POSIX(ON)"
    
    _TAG_REDIR_ERR: "txt"
    _TAG_REDIR_IN: "txt"
    _TAG_REDIR_OUT: "txt"
    

    Ansible 可以通过这些选项配置远程环境变量

    • inventory - inventory.yml, group_vars/all.yml, 或 host_vars/all.yml

    • playbook - Playbook 顶部的 environment 变量。

    • block 或 task - environment 关键字。

    有关更多详细信息,请参阅 设置远程环境

在 fakeroot 下运行

出现一些问题是因为 fakeroot 默认情况下不会创建完整且符合 POSIX 标准的系统。众所周知,它不会正确扩展 Ansible 使用的默认 tmp 目录(~/.ansible/tmp)。如果您看到模块失败,很可能是这个问题。简单的变通方法是将 remote_tmp 设置为一个可以正确扩展的路径(具体细节请参阅您正在使用的 shell 插件的文档)。

例如,在 ansible 配置文件(或通过环境变量)中,您可以设置:

remote_tmp=$HOME/.ansible/tmp

使内容可重用/可再分发的最佳方式是什么?

如果您还没有这样做,请阅读 Playbook 文档中关于“角色(Roles)”的所有内容。这有助于您使 Playbook 内容自包含,并能很好地与 Git 子模块等工具结合使用,以便与他人共享内容。

如果其中一些插件类型对您来说很陌生,请参阅 API 文档以了解有关扩展 Ansible 的方法的更多详细信息。

配置文件存放在哪里,我可以在其中配置什么?

请参阅 配置 Ansible

如何禁用 cowsay?

如果安装了 cowsay,Ansible 会在运行 Playbook 时让您的日子更开心。如果您决定在专业的“无牛”环境中工作,您可以卸载 cowsay,在 ansible.cfg 中设置 nocows=1,或者设置 ANSIBLE_NOCOWS 环境变量。

export ANSIBLE_NOCOWS=1

如何查看所有 ansible_ 变量的列表?

Ansible 默认会收集有关被管理机器的“事实(Facts)”,这些事实可以在 Playbook 和模板中访问。要查看有关某台机器可用的所有事实列表,您可以运行 setup 模块作为 ad hoc 操作:

ansible -m setup hostname

这将打印出该特定主机可用的所有事实的字典。您可能需要将输出通过管道传递给分页器。这不包括清单变量或内部“魔法”变量。如果您需要的不止是“事实”,请参阅下一个问题。

如何查看为主机定义的所有清单(Inventory)变量?

通过运行以下命令,您可以查看主机的清单变量:

ansible-inventory --list --yaml

如何查看特定于主机的变量?

要查看所有主机特定的变量(可能包括事实和其他来源):

ansible -m debug -a "var=hostvars['hostname']" localhost

除非您正在使用事实缓存,否则对于上述任务中包含的事实,您通常需要先运行一个收集事实的 Play。

如何在模板中遍历组内的主机列表?

一个非常常见的模式是遍历主机组中的主机列表,例如为了使用服务器列表填充模板配置文件。要做到这一点,您只需在模板中访问 “$groups” 字典,如下所示:

{% for host in groups['db_servers'] %}
    {{ host }}
{% endfor %}

如果您需要访问关于这些主机的事实(例如每个主机名的 IP 地址),则需要确保已经填充了事实。例如,确保您有一个与 db_servers 对话的 Play:

- hosts:  db_servers
  tasks:
    - debug: msg="doesn't matter what you do, just that they were talked to previously."

然后,您可以像这样在模板中使用事实:

{% for host in groups['db_servers'] %}
   {{ hostvars[host]['ansible_eth0']['ipv4']['address'] }}
{% endfor %}

如何以编程方式访问变量名称?

可能会出现我们需要获取任意接口的 ipv4 地址的情况,其中要使用的接口可以通过角色参数或其他输入提供。变量名可以通过使用 “~” 将字符串连接在一起而构建,如下所示:

{{ hostvars[inventory_hostname]['ansible_' ~ which_interface]['ipv4']['address'] }}

通过 hostvars 遍历的技巧是必要的,因为它是一个包含整个变量命名空间的字典。inventory_hostname 是一个魔法变量,指示您在主机循环中当前正在遍历的主机。

在上面的示例中,如果您的接口名称中有连字符,必须将它们替换为下划线:

{{ hostvars[inventory_hostname]['ansible_' ~ which_interface | replace('_', '-') ]['ipv4']['address'] }}

另请参阅 dynamic_variables

如何访问组变量?

技术上来说,您不能这样做,Ansible 实际上并不直接使用组。组是用于主机选择的标签,也是一种批量分配变量的方式,它们不是一等实体,Ansible 只关心主机和任务。

也就是说,您可以简单地通过选择属于该组的主机来访问变量,有关示例,请参阅下面的 first_host_in_a_group

如何访问组中第一台主机的变量?

如果我们想获取 webservers 组中第一台 web 服务器的 IP 地址会发生什么?嗯,我们也可以做到这一点。请注意,如果您使用动态清单,哪台主机是“第一台”可能不一致,因此除非您的清单是静态且可预测的,否则您不会想这样做。(如果您使用的是 AWX 或 Red Hat Ansible Automation Platform,它将使用数据库顺序,因此即使您使用的是基于云的清单脚本,这也不是问题)。

无论如何,技巧如下:

{{ hostvars[groups['webservers'][0]]['ansible_eth0']['ipv4']['address'] }}

注意我们是如何提取 webservers 组的第一台机器的主机名的。如果您在模板中这样做,可以使用 Jinja2 的 ‘#set’ 指令来简化,或者在 Playbook 中,您也可以使用 set_fact:

- set_fact: headnode={{ groups['webservers'][0] }}

- debug: msg={{ hostvars[headnode].ansible_eth0.ipv4.address }}

注意我们是如何互换使用括号语法和点号语法的——这可以在任何地方完成。

如何将文件递归地复制到目标主机?

copy 模块有一个 recursive 参数。但是,如果您想为大量文件执行更高效的操作,请查看 synchronize 模块。synchronize 模块封装了 rsync。有关这两个模块的信息,请参阅模块索引。

如何访问 shell 环境变量?

在控制节点机器上: 要访问控制节点上的现有变量,请使用 env 查找插件。例如,访问管理机器上的 HOME 环境变量值:

---
# ...
  vars:
     local_home: "{{ lookup('env','HOME') }}"

在目标机器上: 环境变量可通过 ansible_env 变量中的事实获得:

{{ ansible_env.HOME }}

如果您需要为任务执行设置环境变量,请参阅 设置远程环境 部分(位于 高级 Playbook 部分中)。在目标机器上设置环境变量有多种方法。您可以使用 templatereplacelineinfile 模块将环境变量引入文件。具体要编辑的文件取决于您的操作系统、发行版和本地配置。

如何为 user 模块生成加密密码?

Ansible ad hoc 命令是最简单的选择:

ansible all -i localhost, -m debug -a "msg={{ 'mypassword' | password_hash('sha512', 'mysecretsalt') }}"

大多数 Linux 系统上提供的 mkpasswd 工具也是一个很好的选择:

mkpasswd --method=sha-512

openssl 工具是另一个极好的选择:

openssl passwd -6 -noverify

这将生成一个不带 salt 值、默认轮数为 5000 的 SHA512 密码哈希。检查 openssl passwd 文档 以获取更多选项。

使用集成的 哈希和加密字符串与密码 来生成密码的哈希版本。您不应在 Playbook 或 host_vars 中放置明文密码;相反,请使用 使用加密变量和文件 来加密敏感数据。

在 OpenBSD 中,基础系统中提供了一个名为 encrypt (1) 的类似选项。

Ansible 允许使用点号表示法和数组表示法来表示变量。我应该使用哪种表示法?

点号表示法来自 Jinja,对于没有特殊字符的变量可以正常工作。如果您的变量包含点号 (.)、冒号 (:) 或连字符 (-),如果键以两个下划线开头和结尾,或者如果键使用了任何已知的公共属性,则使用数组表示法更安全。有关已知公共属性的列表,请参阅 使用变量

item[0]['checksum:md5']
item['section']['2.1']
item['region']['Mid-Atlantic']
It is {{ temperature['Celsius']['-3'] }} outside.

此外,数组表示法允许进行动态变量组合,请参阅 dynamic_variables

“点号表示法”的另一个问题是,有些键可能会因为与 Python 字典的属性和方法冲突而导致问题。

  • item 是字典时,错误语法的示例:

item.update

这种变体导致语法错误,因为 update() 是 Python 字典的方法。

  • 正确语法的示例:

item['update']

何时使用变量批量设置任务参数是不安全的?

您可以从字典类型的变量设置任务的所有参数。此技术在某些动态执行场景中很有用。但是,它引入了安全风险。我们不建议这样做,因此当您做类似下面的操作时,Ansible 会发出警告:

#...
vars:
  usermod_args:
    name: testuser
    state: present
    update_password: always
tasks:
- user: '{{ usermod_args }}'

这个特定的示例是安全的。但是,像这样构建任务是有风险的,因为传递给 usermod_args 的参数和值可能会被受感染目标机器上的 host facts 中的恶意值覆盖。为了降低此风险:

我可以获得 Ansible 的培训吗?

是的!请参阅我们的 服务页面 以获取有关我们的服务和培训产品的信息。发送电子邮件至 info@ansible.com 获取更多详情。

我们还定期提供免费的基于 Web 的培训课程。有关即将举行的网络研讨会的更多信息,请参阅我们的 网络研讨会页面

有 Web 界面 / REST API / GUI 吗?

是的!开源 Web 界面是 Ansible AWX。使 Ansible 更加强大且易于使用的受支持 Red Hat 产品是 Red Hat Ansible Automation Platform

如何保护 Playbook 中的敏感数据?

如果您希望将敏感数据保留在 Ansible 内容中,同时仍然将其公开共享或保留在版本控制中,请参阅 使用加密变量和文件

如果您有一个任务,在使用 -v(详细)模式时不想显示其结果或执行的命令,以下任务或 Playbook 属性可能很有用:

- name: secret task
  shell: /usr/bin/do_something --value={{ secret_value }}
  no_log: True

这可用于保持详细输出,同时向其他想要查看输出的人隐藏敏感信息。

no_log 属性也可以应用于整个 Play:

- hosts: all
  no_log: True

虽然这会使 Play 在调试时有些困难。建议仅在 Playbook 完成后将其应用于单个任务。请注意,即使使用了 no_log 属性,也不会阻止在通过 ANSIBLE_DEBUG 环境变量调试 Ansible 本身时显示数据。

何时应该使用 {{ }}?另外,如何插值变量或使用动态变量名?

一个不变的规则是“总是使用 {{ }},除非是在 when: 中”。条件始终通过 Jinja2 运行以解析表达式,因此 when:failed_when:changed_when: 总是被模板化,您应该避免添加 {{ }}

在大多数其他情况下,您应该始终使用括号,即使以前您可以在不指定的情况下使用变量(例如 loopwith_ 子句),因为这使得区分未定义的变量和字符串变得困难。

另一个规则是“花括号不能堆叠”。我们经常看到这种情况:

{{ somevar_{{other_var}} }}

上面的内容无法按预期工作。如果需要使用动态变量,请视情况使用以下内容:

{{ hostvars[inventory_hostname]['somevar_' ~ other_var] }}

对于“非主机变量”,可以使用 vars 查找 插件。

{{ lookup('vars', 'somevar_' ~ other_var) }}

要确定关键字是否需要 {{ }} 甚至是否支持模板化,请使用 ansible-doc -t keyword <name>,这将返回有关关键字的文档,包括一个 template 字段,其值为 explicit(需要 {{ }})、implicit(假设 {{ }},因此不需要)或 static(不支持模板化,所有字符将被字面解释)。

委托任务时,如何获取原始的 ansible_host?

正如文档所述,连接变量取自 delegate_to 主机,因此 ansible_host 被覆盖,但您仍然可以通过 hostvars 访问原始主机:

original_host: "{{ hostvars[inventory_hostname]['ansible_host'] }}"

这适用于所有被覆盖的连接变量,例如 ansible_useransible_port 等。

获取文件时,如何修复“protocol error: file name does not match request”错误?

自 OpenSSH 发布 7.9p1 以来,SCP 客户端中存在一个 错误,当使用 SCP 作为文件传输机制时,该错误会在 Ansible 控制节点上触发此错误:

错误

failed to transfer file to /tmp/ansible/file.txtrnprotocol error: file name does not match request

在这些版本中,SCP 会尝试验证要获取的文件路径是否与请求的路径匹配。如果远程文件名需要在其路径中引用空格或非 ascii 字符,验证就会失败。为了避免此错误:

  • 确保您使用的是 SFTP,这是安全性、速度和可靠性的最佳传输方式。检查您是否正在执行以下操作之一:
    • 依赖默认设置,即 smart — 如果没有在任何地方显式设置 ssh_transfer_method,这将有效。

    • 在清单中设置 主机变量组变量ansible_ssh_transfer_method: smart

    • 在控制节点上设置环境变量:export ANSIBLE_SSH_TRANSFER_METHOD=smart

    • 运行 Ansible 时传递环境变量:ANSIBLE_SSH_TRANSFER_METHOD=smart ansible-playbook

    • 修改您的 ansible.cfg 文件:将 ssh_transfer_method=smart 添加到 [ssh_connection] 部分。smart 设置会尝试使用 sftp 进行传输,如果失败则回退到 scp,然后是 dd。如果您希望在 SFTP 不可用时传输失败,请将 ssh_transfer_method=sftp 添加到 [ssh_connection] 部分。

  • 如果您必须使用 SCP,请设置 -T 参数以告诉 SCP 客户端忽略路径验证。您可以通过以下三种方式之一执行此操作:
    • 设置 主机变量组变量ansible_scp_extra_args=-T

    • 导出或传递环境变量:ANSIBLE_SCP_EXTRA_ARGS=-T

    • 修改您的 ansible.cfg 文件:将 scp_extra_args=-T 添加到 [ssh_connection] 部分。

注意

如果您在使用 -T 时看到 invalid argument 错误,则说明您的 SCP 客户端没有执行文件名验证,不会触发此错误。

Ansible 是否支持多因素身份验证 2FA/MFA/生物识别/指纹/USB 密钥/OTP 等?

不支持。Ansible 旨在针对多个目标执行多个任务,从而最大限度地减少用户交互。与大多数自动化工具一样,它与旨在处理人为交互的交互式安全系统不兼容。这些系统中的大多数需要在每个目标上进行二级提示,这阻碍了扩展到数千个目标。它们往往还有非常短的过期时间,因此需要频繁重新授权,这也是许多主机和/或长任务集合带来的一个问题。

在此类环境中,我们建议在 Ansible 执行周围加强安全措施,但仍允许其使用不需要此类措施的“自动化用户”。借助 AWX 或 Red Hat Ansible Automation Platform,管理员可以设置对清单的 RBAC 访问权限,并管理凭据和作业执行。

‘validate’ 选项无法满足我的需求,该怎么办?

许多创建或更新文件的 Ansible 模块都有一个 validate 选项,如果验证命令失败,该选项允许您中止更新。它使用 Ansible 在执行最终更新之前创建的临时文件。在许多情况下,这不起作用,因为特定应用程序的验证工具需要特定的名称、多个文件或此简单功能中不存在的其他因素。

对于这些情况,您必须自己处理验证和恢复。以下是如何使用 block/rescue 和备份来执行此操作的简单示例,大多数基于文件的模块也支持此功能:

- name: maintain config and backout if validation after change fails
  block:
    - name: do the actual update, works with copy, lineinfile and any action that allows for `backup`.
      template: src=template.j2 dest=/x/y/z backup=yes moreoptions=stuff
      register: updated

    - name: run validation, this will change a lot as needed. We assume it returns an error when not passing, use `failed_when` if otherwise.
      shell: run_validation_commmand
      become: true
      become_user: requiredbyapp
      environment:
        WEIRD_REQUIREMENT: 1
      when: updated is changed
  rescue:
    - name: restore backup file to original, in the hope the previous configuration was working.
      copy:
         remote_src: true
         dest: /x/y/z
         src: "{{ updated['backup_file'] }}"
      when: updated is changed
  always:
    - name: We choose to always delete backup, but could copy or move, or only delete in rescue.
      file:
         path: "{{ updated['backup_file'] }}"
         state: absent
      when: updated is changed

如何提交文档更改?

Ansible 的文档保留在 ansible/ansible-documentation 项目 Git 仓库中。有关详细信息,请参阅 为 Ansible 文档做出贡献

ansible.legacyansible.builtin 集合之间有什么区别?

它们都不是真正的集合。它们是由核心引擎虚拟构建的(合成集合)。

ansible.builtin 集合仅指随 ansible-core 一起分发的插件。

ansible.legacy 集合是 ansible.builtin 的超集(您可以引用 builtin 中的插件,通过 ansible.legacy)。您还可以在 配置的路径和相邻目录 中添加“自定义”插件,并具有覆盖具有相同名称的内置插件的能力。

此外,ansible.legacy 是您在未指定 FQCN 时默认获得的结果。所以这个:

- shell: echo hi

确实等同于:

- ansible.legacy.shell: echo hi

当然,如果您没有覆盖 shell 模块,您也可以只将其写为 ansible.builtin.shell,因为 legacy 将解析为内置集合。

我没有在这里看到我的问题

如果您还没有找到问题的答案,请询问社区!有关详细信息,请访问 Ansible 沟通指南

另请参阅

使用 Playbook

Playbook 简介

Ansible 提示与技巧

Playbook 的提示与技巧

交流方式

有疑问?需要帮助?想分享你的想法?请访问 Ansible 通信指南