254

我正在使用 Ansible 执行一些简单的用户管理任务,其中包含一小部分计算机。目前,我的剧本设置为hosts: all,我的主机文件只是一个包含所有机器的组:

# file: hosts
[office]
imac-1.local
imac-2.local
imac-3.local

我发现自己经常不得不针对一台机器。该ansible-playbook命令可以像这样限制播放:

ansible-playbook --limit imac-2.local user.yml

但这似乎有点脆弱,尤其是对于具有潜在破坏性的剧本而言。省略limit标志意味着剧本将在任何地方运行。由于这些工具只是偶尔使用,因此似乎值得采取措施进行万无一失的播放,这样我们就不会在几个月后意外地破坏某些东西。

是否有将剧本运行限制在单台机器上的最佳实践?理想情况下,如果遗漏了一些重要的细节,剧本应该是无害的。

4

13 回答 13

238

事实证明,可以直接在 playbook 中输入主机名,因此运行 playbookhosts: imac-2.local可以正常工作。但它有点笨拙。

更好的解决方案可能是使用变量定义剧本的主机,然后通过以下方式传递特定的主机地址--extra-vars

# file: user.yml  (playbook)
---
- hosts: '{{ target }}'
  user: ...

运行剧本:

ansible-playbook user.yml --extra-vars "target=imac-2.local"

如果{{ target }}未定义,则剧本不执行任何操作。如果需要,也可以通过 hosts 文件中的组。总体而言,这似乎是构建具有潜在破坏性的剧本的一种更安全的方法。

针对单个主机的剧本:

$ ansible-playbook user.yml --extra-vars "target=imac-2.local" --list-hosts

playbook: user.yml

  play #1 (imac-2.local): host count=1
    imac-2.local

带有一组主机的剧本:

$ ansible-playbook user.yml --extra-vars "target=office" --list-hosts

playbook: user.yml

  play #1 (office): host count=3
    imac-1.local
    imac-2.local
    imac-3.local

忘记定义主机是安全的!

$ ansible-playbook user.yml --list-hosts

playbook: user.yml

  play #1 ({{target}}): host count=0
于 2013-08-12T19:32:34.270 回答
195

还有一个可爱的小技巧,可以让您在命令行上指定单个主机(或多个主机,我猜),而无需中间库存:

ansible-playbook -i "imac1-local," user.yml

注意末尾的逗号(, );这表明它是一个列表,而不是一个文件。

现在,如果您不小心传入了真实的库存文件,这将无法保护您,因此它可能不是解决此特定问题的好方法。但这是一个方便的技巧!

于 2013-08-13T00:56:06.643 回答
91

如果通过检查play_hosts变量提供了多个主机,则此方法将退出。如果不满足单主机条件,则使用失败模块退出。下面的示例使用包含两个主机 alice 和 bob 的主机文件。

user.yml(剧本)

---
- hosts: all
  tasks:
    - name: Check for single host
      fail: msg="Single host check failed."
      when: "{{ play_hosts|length }} != 1"
    - debug: msg='I got executed!'

运行没有主机过滤器的剧本

$ ansible-playbook user.yml
PLAY [all] ****************************************************************
TASK: [Check for single host] *********************************************
failed: [alice] => {"failed": true}
msg: Single host check failed.
failed: [bob] => {"failed": true}
msg: Single host check failed.
FATAL: all hosts have already failed -- aborting

在单个主机上运行 playbook

$ ansible-playbook user.yml --limit=alice

PLAY [all] ****************************************************************

TASK: [Check for single host] *********************************************
skipping: [alice]

TASK: [debug msg='I got executed!'] ***************************************
ok: [alice] => {
    "msg": "I got executed!"
}
于 2015-04-05T10:06:00.453 回答
35

恕我直言,有一种更方便的方式。您确实可以交互式地提示用户他想要应用该剧本的机器,这要归功于vars_prompt

---

- hosts: "{{ setupHosts }}"
  vars_prompt:
    - name: "setupHosts"
      prompt: "Which hosts would you like to setup?"
      private: no
  tasks:
    […]
于 2016-04-01T16:23:04.547 回答
18

为了扩展 joemailer 的答案,如果您希望具有匹配远程机器的任何子集的模式匹配能力(就像ansible命令一样),但仍然想让在所有机器上意外运行 playbook 变得非常困难,这是我想出了什么:

与其他答案相同的剧本:

# file: user.yml  (playbook)
---
- hosts: '{{ target }}'
  user: ...

让我们有以下主机:

imac-10.local
imac-11.local
imac-22.local

现在,要在所有设备上运行命令,您必须明确将目标变量设置为“all”

ansible-playbook user.yml --extra-vars "target=all"

并将其限制为特定模式,您可以设置target=pattern_here

或者,或者,您可以保留target=all并附加--limit参数,例如:

--limit imac-1*

IE。 ansible-playbook user.yml --extra-vars "target=all" --limit imac-1* --list-hosts

这导致:

playbook: user.yml

  play #1 (office): host count=2
    imac-10.local
    imac-11.local
于 2015-01-07T06:01:48.467 回答
11

一个稍微不同的解决方案是使用特殊变量ansible_limit,它是--limit当前执行 Ansible 的 CLI 选项的内容。

- hosts: "{{ ansible_limit | default(omit) }}"

无需在这里定义额外的变量,只需运行带有--limit标志的剧本。

ansible-playbook --limit imac-2.local user.yml
于 2020-05-28T11:55:15.337 回答
10

我真的不明白所有答案都这么复杂,方法很简单:

ansible-playbook user.yml -i hosts/hosts --limit imac-2.local --check

check模式允许您在空运行模式下运行,而无需进行任何更改。

于 2017-10-15T12:13:45.580 回答
6

使用 EC2 外部清单脚本的 AWS 用户可以简单地按实例 ID 进行过滤:

ansible-playbook sample-playbook.yml --limit i-c98d5a71 --list-hosts

这是因为清单脚本创建了默认组

于 2015-11-03T10:16:38.023 回答
5

我们有一些可供大量团队使用的通用手册。我们还有环境特定的清单文件,其中包含多个组声明。

为了强制调用 playbook 的人指定要运行的组,我们在 playbook 顶部播种了一个虚拟条目:

[ansible-dummy-group]
dummy-server

然后,我们将以下检查作为共享剧本中的第一步:

- hosts: all
  gather_facts: False
  run_once: true
  tasks:
  - fail:
      msg: "Please specify a group to run this playbook against"
    when: '"dummy-server" in ansible_play_batch'

如果虚拟服务器出现在这个 playbook 计划运行的主机列表中(ansible_play_batch),那么调用者没有指定组并且 playbook 执行将失败。

于 2017-11-15T21:38:53.233 回答
5

这显示了如何在目标服务器本身上运行 playbook。

如果您想使用本地连接,这有点棘手。但是,如果您为主机设置使用变量并在主机文件中为 localhost 创建一个特殊条目,这应该没问题。

在(所有)剧本中,将 hosts: 行设置为:

- hosts: "{{ target | default('no_hosts')}}"

在清单 hosts 文件中,为 localhost 添加一个条目,将连接设置为本地:

[localhost]
127.0.0.1  ansible_connection=local

然后在命令行上运行命令明确设置目标 - 例如:

$ ansible-playbook --extra-vars "target=localhost" test.yml

这在使用 ansible-pull 时也会起作用:

$ ansible-pull -U <git-repo-here> -d ~/ansible --extra-vars "target=localhost" test.yml

如果您忘记在命令行上设置变量,该命令将安全地出错(只要您没有创建名为“no_hosts”的主机组!)并发出以下警告:

skipping: no hosts matched

如上所述,您可以使用以下命令定位一台机器(只要它在您的主机文件中):

$ ansible-playbook --extra-vars "target=server.domain" test.yml

或具有以下内容的组:

$ ansible-playbook --extra-vars "target=web-servers" test.yml
于 2018-11-25T22:19:04.917 回答
4

自 1.7 版以来,ansible 具有run_once选项。部分还包含对各种其他技术的一些讨论。

于 2015-12-08T04:33:58.800 回答
0

我有一个名为provision 的包装脚本强制您选择目标,因此我不必在其他地方处理它。

对于那些好奇的人,我将 ENV vars 用于我的 vagrantfile 使用的选项(为云系统添加相应的 ansible arg)并让其余的 ansible args 通过。在我一次创建和配置超过 10 台服务器的情况下,我会在失败的服务器上自动重试(只要正在取得进展 - 我发现一次创建 100 台左右的服务器时,通常有几个会在第一次失败)。

echo 'Usage: [VAR=value] bin/provision [options] dev|all|TARGET|vagrant'
echo '  bootstrap - Bootstrap servers ssh port and initial security provisioning'
echo '  dev - Provision localhost for development and control'
echo '  TARGET - specify specific host or group of hosts'
echo '  all - provision all servers'
echo '  vagrant - Provision local vagrant machine (environment vars only)'
echo
echo 'Environment VARS'
echo '  BOOTSTRAP - use cloud providers default user settings if set'
echo '  TAGS - if TAGS env variable is set, then only tasks with these tags are run'
echo '  SKIP_TAGS - only run plays and tasks whose tags do not match these values'
echo '  START_AT_TASK - start the playbook at the task matching this name'
echo
ansible-playbook --help | sed -e '1d
    s#=/etc/ansible/hosts# set by bin/provision argument#
    /-k/s/$/ (use for fresh systems)/
    /--tags/s/$/ (use TAGS var instead)/
    /--skip-tags/s/$/ (use SKIP_TAGS var instead)/
    /--start-at-task/s/$/ (use START_AT_TASK var instead)/
'
于 2016-03-20T11:34:56.550 回答
-1

我建议使用--limit <hostname or ip>

于 2020-07-07T12:33:34.747 回答