常见问题解答

这里有一些常见问题及其答案。

所有的模块都去哪儿了?

2019 年 7 月,我们宣布集合将成为 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?

在磁盘速度较慢的系统(例如 Raspberry PI)上,Ansible 可能会感觉运行缓慢。请参阅如果 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 重用连接、启用 Kerberized SSH,或让 Ansible 注意我的本地 SSH 配置文件?

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

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

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

如何配置跳板机以访问我无法直接访问的服务器?

您可以在 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 [email protected]"'

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

请注意,ssh -W 仅在 OpenSSH 5.4 或更高版本中可用。对于较旧的版本,有必要在堡垒机上执行 nc %h:%p 或某些等效命令。

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

如何让 Ansible 及时注意到死机目标?

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

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

不要尝试从笔记本电脑管理云提供商的一组机器。而是首先连接到此云提供商内部的管理节点,然后从那里运行 Ansible。

如何在远程计算机上处理 /usr/bin/python 中没有 Python 解释器的情况?

虽然您可以使用任何语言编写 Ansible 模块,但大多数 Ansible 模块是用 Python 编写的,包括让 Ansible 正常工作的核心模块。

默认情况下,Ansible 假设可以在您的远程系统上找到 /usr/bin/python,该路径指向 Python2(版本 2.6 或更高版本)或 Python3(版本 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 通过 ansible_python_interpreter 使用该 Python。如果模块的文档中没有提到该模块需要 Python 2,您还可以在我们的 错误跟踪器上报告错误,以便在将来的版本中修复不兼容问题。

不要替换 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 中运行

您可以很轻松地将 Ansible 安装到控制节点上的 virtualenv 中

$ 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] 可能在调用 fork() 时在另一个线程中正在进行。我们无法在 fork() 子进程中安全地调用它或忽略它。而是崩溃。在 objc_initializeAfterForkError 上设置一个断点以进行调试。错误!发现一个工作程序处于死机状态

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

$ export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES

在 macOS 上作为目标运行

通过 SSH 管理 macOS Monterey 12、macOS Ventura 13 或更高版本的系统时,可能会发生以下错误

错误

“eDSPermissionError” DS 错误:-14120 (eDSPermissionError)

这很好地表明尚未启用 *允许远程用户完全磁盘访问*。

另请参阅

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

在 BSD 上运行

在 Solaris 上运行

默认情况下,Solaris 10 及更早版本运行非 POSIX shell,该 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 2.7.6 版本不能与 Ansible 一起使用,因为它在内部将字符串表示为 EBCDIC。

    要解决此限制,请下载并安装更高版本的 z/OS 的 Python(2.7.13 或 3.6.1),该版本在内部将字符串表示为 ASCII。已验证版本 2.7.13 可以正常工作。

  • pipelining = False/etc/ansible/ansible.cfg 中时,Ansible 模块通过 sftp 以二进制模式传输,但是 Python 的执行失败,并显示以下错误:

    错误

    SyntaxError:文件 /a/user1/.ansible/tmp/ansible-tmp-1548232945.35-274513842609025/AnsiballZ_stat.py 第 1 行出现以 '\x83' 开头的非 UTF-8 代码,但未声明编码;有关详细信息,请参阅 https://pythonlang.cn/dev/peps/pep-0263/

    要解决此问题,请在 /etc/ansible/ansible.cfg 中设置 pipelining = True

  • 在目标主机上的默认位置 /usr/bin/python 中找不到 Python 解释器。

    错误

    /usr/bin/python: EDC5129I 没有此类文件或目录

    要解决此问题,请在清单中设置 Python 安装的路径,如下所示:

    zos1 ansible_python_interpreter=/usr/lpp/python/python-2017-04-12-py27/python27/bin/python
    
  • Python 启动失败,并显示 The module libpython2.7.so was not found.

    错误

    EE3501S 找不到模块 libpython2.7.so。

    在 z/OS 上,您必须从 gnu bash 执行 Python。如果 gnu bash 安装在 /usr/lpp/bash,您可以通过指定 ansible_shell_executable 来在清单中解决此问题

    zos1 ansible_shell_executable=/usr/lpp/bash/bin/bash
    

在 fakeroot 下运行

会出现一些问题,因为 fakeroot 默认情况下不会创建完整或符合 POSIX 的系统。已知它不会正确扩展 Ansible 使用的默认 tmp 目录 (~/.ansible/tmp)。如果您看到模块失败,这可能是问题所在。简单的解决方法是将 remote_tmp 设置为可以正确扩展的路径(有关详细信息,请参阅您正在使用的 shell 插件的文档)。

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

remote_tmp=$HOME/.ansible/tmp

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

如果您尚未这样做,请阅读 playbooks 文档中关于“角色”的所有内容。这可以帮助您使 playbook 内容自包含,并且可以很好地与 Git 子模块等工具一起使用,以与他人共享内容。

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

配置文件位于何处,我可以在其中配置什么?

请参阅 配置 Ansible

如何禁用 cowsay?

如果安装了 cowsay,Ansible 会在运行 playbook 时让您的一天更快乐。如果您决定在专业的无牛环境中工作,您可以卸载 cowsay,在 ansible.cfg 中设置 nocows=1,或者设置 ANSIBLE_NOCOWS 环境变量

export ANSIBLE_NOCOWS=1

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

Ansible 默认情况下会收集有关受管理计算机的“事实”,这些事实可以在 playbook 和模板中访问。要查看有关计算机的所有可用事实的列表,您可以运行 setup 模块作为临时操作

ansible -m setup hostname

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

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

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

ansible-inventory --list --yaml

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

要查看所有主机特定变量,其中可能包括事实和其他来源

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

除非您使用事实缓存,否则您通常需要先使用一个 playbook 来收集事实,以获取上面任务中包含的事实。

如何在模板中循环访问组中的主机列表?

一个很常见的模式是在主机组中迭代主机列表,可能是为了用服务器列表填充模板配置文件。为此,您可以像这样在模板中访问“$groups”字典

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

如果您需要访问有关这些主机的事实,例如,每个主机名的 IP 地址,则需要确保已填充这些事实。例如,请确保您有一个与 db_servers 通信的 playbook

- 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 模块具有一个递归参数。但是,如果您想对大量文件执行更高效的操作,请查看 synchronize 模块。synchronize 模块包装了 rsync。请参阅模块索引以获取有关这两个模块的信息。

如何访问 shell 环境变量?

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

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

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

{{ ansible_env.HOME }}

如果您需要为 TASK 执行设置环境变量,请参阅 设置远程环境,在 高级 Playbook 部分中。有几种方法可以在目标计算机上设置环境变量。您可以使用 templatereplacelineinfile 模块将环境变量引入文件。要编辑的确切文件因您的操作系统、发行版和本地配置而异。

如何为用户模块生成加密密码?

Ansible 临时命令是最简单的选择

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

大多数 Linux 系统上提供的 mkpasswd 实用程序也是一个不错的选择

mkpasswd --method=sha-512

如果您的系统上未安装此实用程序(例如,您使用的是 macOS),则仍然可以使用 Python 轻松生成这些密码。首先,请确保安装了 Passlib 密码哈希库

pip install passlib

准备好该库后,可以按如下方式生成 SHA512 密码值

python -c "from passlib.hash import sha512_crypt; import getpass; print(sha512_crypt.using(rounds=5000).hash(getpass.getpass()))"

使用集成的 哈希和加密字符串和密码来生成密码的哈希版本。您不应将纯文本密码放在 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 界面/REST API/GUI?

是的!开源 Web 界面是 Ansible AWX。支持的 Red Hat 产品使 Ansible 更加强大和易于使用,它是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插件

{{ 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 客户端存在一个bug,当使用 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

为什么 regex_search 过滤器返回 None 而不是空字符串?

在 jinja2 2.10 版本之前,Jinja 只能返回字符串,但在某些情况下 Ansible 需要 Python 对象。 Ansible 使用 safe_eval,并且只通过此函数发送看起来像某些类型的 Python 对象的字符串。 对于没有找到匹配项的 regex_search,结果 (None) 会被转换为字符串“None”,这在非原生 jinja2 中没有用处。

以下是一个单独模板操作的示例,展示了此行为

{{ 'ansible' | regex_search('foobar') }}

此示例不会产生 Python None,因此 Ansible 过去会将其转换为 ""(空字符串)。

原生的 jinja2 功能实际上允许我们返回完整的 Python 对象,这些对象始终在任何地方都表示为 Python 对象,因此使用 regex_search 的单个模板操作的结果可能会产生 Python None

注意

regex_search 用作中间结果,然后与 jinja2 none 测试进行比较时,不需要原生的 jinja2 功能。

{{ 'ansible' | regex_search('foobar') is none }}

我如何提交对文档的更改?

Ansible 的文档保存在主项目 Git 存储库中,有关贡献的完整说明可以在 docs README 中找到,可在 GitHub 上查看。谢谢!

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

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

ansible.builtin 集合仅指随 ansible-core 提供的插件。

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

此外,当你未指定 FQCN 时,默认情况下会得到 ansible.legacy。所以这个

- shell: echo hi

实际上等同于

- ansible.legacy.shell: echo hi

但是,如果你不覆盖 shell 模块,你也可以直接将其写为 ansible.builtin.shell,因为 legacy 将解析为内置集合。

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

如果你没有找到你的问题的答案,请咨询社区! 请访问 Ansible 通信指南了解详细信息。

另请参阅

使用 Playbook

Playbook 简介

Ansible 提示和技巧

Playbook 的提示和技巧

沟通

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