将Ansible剧本安全地限制在一台机器上吗?


227

我正在Ansible中使用一小组计算机执行一些简单的用户管理任务。目前,我将我的剧本设置为,hosts: all并且我的hosts文件只是列出了所有计算机的一个组:

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

我发现自己经常不得不瞄准一台机器。该ansible-playbook命令可以限制播放,如下所示:

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

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

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

Answers:


209

事实证明,可以直接在剧本中输入主机名,因此使用可以运行剧本hosts: imac-2.local。但这有点笨拙。

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

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

运行剧本:

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

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

面向单个主机的Playbook:

$ 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

52
这是在1.5.3与解--limit office[0]
NG。

4
该变量需要加引号-即:'{{ target }}'-根据docs.ansible.com/…–
Limbo Peng

9
与其他一些答案不同,这是一个“故障安全”答案-如果您遗漏了某些内容,则将无济于事。使用Ansible 1.7在“仅”一台主机上运行run_once仍然具有破坏性,因此并不是一个好主意。
RichVel

4
如果您想使用更短的命令,-e则相当于--extra-vars
William Turrell

1
如果您的ansible配置要求主机不能为空或未定义,则可以将变量与jinja过滤器结合使用,例如:hosts: "{{ target | default('no_hosts')}}"
Zach Weg

178

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

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

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

现在,如果您不小心传入了真实的清单文件,这将无法为您提供保护,因此这可能不是解决此特定问题的好方法。但这是一个方便的窍门!


2
棒极了。我经常使用-l标志,该标志可与etc / ansible / hosts(使用EC2发现API填充)一起使用,但是有时我真的只需要一台机器。谢谢!
维克2014年

3
这个技巧应该利用主机文件吗?我使用主机作为AWS EC2系统的动态清单,它返回:skipping: no hosts matched。也许自从这个技巧不再--limit奏效了?
hamx0r 2015年

1
这个技巧对我不起作用。但是这个工作:$ ansible-playbook -kK --limit=myhost1 myplaybook.yml。参见Marwan的答案。
唐·李

2
值得一提的是,要使其正常工作,必须all在剧中设置主持人-这花了我一段时间才能弄清楚……
Remigius Stalder

83

如果通过检查play_hosts变量提供了多个主机,则该方法将退出。该故障模块用于出口,如果单个主机条件不满足。以下示例使用一个具有两个主机alice和bob的hosts文件。

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

在单个主机上运行剧本

$ 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!"
}

1
绝对最好--limit的方法就是走
柏托(Berto)

7
play_hosts在Ansible 2.2中已弃用,并由取代ansible_play_hosts。要在不需要主机的情况下运行--limit,可以使用when: inventory_hostname == ansible_play_hosts[0]
特雷弗·罗宾逊

[WARNING]: conditional statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: {{ play_hosts|length }} == ''在Ansible 2.8.4。
托马斯

32

恕我直言,这是一种更方便的方法。实际上,您可以通过以下方式交互式地提示用户输入要应用该手册的机器vars_prompt

---

- hosts: "{{ setupHosts }}"
  vars_prompt:
    - name: "setupHosts"
      prompt: "Which hosts would you like to setup?"
      private: no
  tasks:
    […]

2
很酷。这还有一个优点,即剧本不是特定于清单文件的。
Erfan

2
谢谢编辑!我实际上想知道为什么默认情况下将输入视为“密码样式”。我已经在文档中错过了:)
Buzut

可以从命令行设置主机var来消除此剧本的提示吗?
andig '17

1
@andig with --extra-vars和您的剧本中的一个普通var…
Buzut

实际上,我无法使它正常工作-似乎{{ hosts }}是在输入值之前对其进行了评估-还是有特殊的技巧?
Remigius Stalder

18

为了扩展joemailer的答案,如果您希望具有模式匹配功能以匹配远程计算机的任何子集(就像该ansible命令一样),但是仍然要使在所有计算机上意外运行该剧本非常困难,这是我想出了什么:

与其他答案中的剧本相同:

# 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*

即。 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


13

我真的不明白所有答案是如此复杂,解决方法很简单:

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

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


7
可能是因为想知道答案,所以您错过了这个问题,该问题要求一种方法来防止错误地省略参数时运行。您建议添加更多违反要求的参数。
techraf

2
啊,当然,但是如果人们支持我,那可能是因为他们是Ansible的新手(就像我写我的答案时一样),甚至都不知道该标志--check,所以我认为这在文档上还是有用的,因为这个问题可能非常刺耳
诺伊斯特19'Jan

6

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

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

这是有效的,因为清单脚本会创建默认组


4
选项--limit不限于EC2,可用于托管/分组清单名称。谢谢。
martinezdelariva 2015年

5

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

为了强制某人调用某个剧本来指定要与之对抗的小组,我们在剧本的顶部添加一个虚拟条目:

[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'

如果虚拟服务器出现在计划将此游戏本运行的主机列表中(ansible_play_batch),则调用者未指定组,则该游戏本执行将失败。


ansible_play_batch仅列出当前批次,因此在使用批次时仍然不安全。最好ansible_play_hosts改用。
托马斯

除此之外,这个技巧似乎是最简单,最接近要求的技巧。我采用了!
托马斯


4

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

如果要使用本地连接,这会有些棘手。但是,如果在主机设置中使用变量,并且在主机文件中为本地主机创建一个特殊条目,则应该可以。

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

- hosts: "{{ target | default('no_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

0

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

对于那些好奇的人,我为我的vagrantfile使用的选项使用ENV变量(为云系统添加相应的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)/
'

0

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

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

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

ansible-playbook --limit imac-2.local user.yml
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.