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

我使用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标志意味着剧本将在任何地方运行。因为这些工具只是偶尔被使用,所以似乎有必要采取措施进行万无一失的回放,这样我们就不会在几个月后意外地破坏一些东西。

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

284490 次浏览

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

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

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

运行剧本:

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

如果\{\{ target }}没有定义,playbook什么也不做。如果需要,还可以传递来自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

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

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

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

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

为了扩展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*
< p > ie。 ansible-playbook user.yml --extra-vars "target=all" --limit imac-1* --list-hosts < / p >

结果是:

playbook: user.yml


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

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

用户。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!'

不带主机过滤器运行playbook

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

使用EC2外部目录脚本的AWS用户可以简单地通过实例id进行过滤:

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

这是因为目录脚本创建默认组

自1.7版ansible有run_once选项。本节还讨论了各种其他技术。

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

对于那些好奇的人,我使用ENV vars为我的vagrantfile使用的选项(为云系统添加相应的ansible arg),并让其余的ansible arg通过。当我一次创建和配置超过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)/
'

恕我直言,有一个更方便的方法。由于vars_prompt,你确实可以交互式地提示用户他想要应用剧本的机器:

---


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

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

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

check模式允许你在不做任何改变的情况下以干运行模式运行。

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

为了迫使调用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'

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

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

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

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

- 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

如上所述,你可以针对一台机器(只要它在你的hosts文件中):

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

或者像这样的团体:

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

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

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

这里不需要定义额外的变量,只需使用--limit标志运行playbook即可。

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

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