将 Ansible 剧本安全地限制在单台机器上?
Posted
技术标签:
【中文标题】将 Ansible 剧本安全地限制在单台机器上?【英文标题】:Safely limiting Ansible playbooks to a single machine? 【发布时间】:2013-08-14 05:53:52 【问题描述】:我正在使用 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
标志意味着剧本将在任何地方运行。由于这些工具只是偶尔使用,因此似乎值得采取措施进行万无一失的播放,这样我们就不会在几个月后意外地破坏某些东西。
是否有将 playbook 运行限制在单台机器上的最佳做法?理想情况下,如果遗漏了一些重要的细节,剧本应该是无害的。
【问题讨论】:
【参考方案1】:事实证明,可以直接在 playbook 中输入主机名,因此使用 hosts: imac-2.local
运行 playbook 可以正常工作。但它有点笨重。
更好的解决方案可能是使用变量定义 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
【讨论】:
这可以在 1.5.3 中通过--limit office[0]
解决
这是一个“万无一失”的答案,与其他一些答案不同——如果你遗漏了一些东西,它什么也做不了。使用 Ansible 1.7 的 run_once
在“仅”一台主机上运行仍然可能具有破坏性,因此这不是一个好主意。
如果您想要更短的命令,-e
相当于 --extra-vars
如果您的 ansible 配置要求主机不能为空或未定义,则使用结合 jinja 过滤器的变量可以工作,例如:hosts: " target | default('no_hosts')"
您可以将目标变量和默认过滤器更进一步,并添加一个模式以将 extra_vars 输入限制为特定组,例如 webservers:hosts: "webservers:& target | default('no_hosts')"
【参考方案2】:
还有一个可爱的小技巧,可以让您在命令行上指定单个主机(或者我猜是多个主机),而无需中间库存:
ansible-playbook -i "imac1-local," user.yml
注意最后的逗号(,);这表明它是一个列表,而不是一个文件。
现在,如果您不小心传入了真实的库存文件,这将无法保护您,因此它可能不是解决此特定问题的好方法。但这是一个方便的技巧!
【讨论】:
太棒了。我经常使用 -l 标志,它与 etc/ansible/hosts(使用 EC2 发现 API 填充)一起使用,但有时我真的只需要一台机器。谢谢! 这个技巧应该利用hosts文件吗?我将主机用作 AWS EC2 系统的动态清单,它返回:skipping: no hosts matched
。也许自从--limit
有效后,这个技巧就不再有效了?
这个技巧对我不起作用。但这有效:$ ansible-playbook -kK --limit=myhost1 myplaybook.yml
。请参阅 Marwan 的回答。
值得一提的是,要使此功能起作用,必须在游戏中将主机设置为all
- 这花了我一段时间才弄清楚...
ansible-playbook -i "imac1-local," user.yml
究竟是什么意思?我将其读作“使用imac1-local
库存调用user.yml
,并使用user/yml
指定的任何主机”。但在最初的问题中,imac1-local
似乎代表一个主机/组,而不是一个清单。【参考方案3】:
为了扩展 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*
即。
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
【讨论】:
这是我在ansible-django-postgres-nginx中遵循的模式【参考方案4】:如果通过检查play_hosts 变量提供了多个主机,则此方法将退出。 fail module 用于在不满足单主机条件时退出。下面的示例使用包含两个主机 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!'
在没有主机过滤器的情况下运行 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
在单个主机上运行 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!"
【讨论】:
绝对是最好的,--limit
是要走的路
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 上。
@Thomas -- 很好的电话,使用when: ansible_play_hosts|length != 1
很容易修复【参考方案5】:
使用 EC2 外部清单脚本的 AWS 用户可以简单地按实例 ID 进行过滤:
ansible-playbook sample-playbook.yml --limit i-c98d5a71 --list-hosts
这是有效的,因为库存脚本creates default groups。
【讨论】:
选项 --limit 不限于 EC2,可用于托管/分组您的清单名称。谢谢。【参考方案6】:从 1.7 版开始,ansible 有了 run_once 选项。部分还包含对各种其他技术的一些讨论。
【讨论】:
【参考方案7】:我有一个名为 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)/
'
【讨论】:
【参考方案8】:恕我直言,这是一种更方便的方法。感谢vars_prompt
,您确实可以交互式地提示用户他想要应用剧本的机器:
---
- hosts: " setupHosts "
vars_prompt:
- name: "setupHosts"
prompt: "Which hosts would you like to setup?"
private: no
tasks:
[…]
【讨论】:
非常酷。这还有一个优点,即剧本不是特定于清单文件的。 可以从命令行设置主机变量以消除此剧本的提示吗? @andig 与--extra-vars
和你的剧本中的普通 var...
实际上,我无法让它工作 - 似乎 hosts
在输入值之前被评估 - 或者有什么特殊技巧?
这应该不用多说。仔细检查您的缩进和var_prompt
var 的名称。【参考方案9】:
我真的不明白怎么所有的答案都这么复杂,方法很简单:
ansible-playbook user.yml -i hosts/hosts --limit imac-2.local --check
check
模式允许您在试运行模式下运行,无需进行任何更改。
【讨论】:
可能是因为想知道答案,你错过了这个问题,它要求一种方法来防止在错误地省略参数时运行。您建议添加更多违反要求的参数。 啊,当然,但如果人们支持我,可能是因为他们是 Ansible 新手(就像我写答案时一样),他们甚至不知道标志--check
,所以我想这仍然是有用的文档,因为这个问题可能非常googlable【参考方案10】:
我们有一些可供大量团队使用的通用手册。我们还有环境特定的清单文件,其中包含多个组声明。
为了强制调用 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 执行将失败。
【讨论】:
ansible_play_batch
仅列出当前批次,因此在使用批处理时这仍然不安全。最好改用ansible_play_hosts
。
除此之外,这个技巧似乎是最简单和最接近要求的;我正在采用它!【参考方案11】:
这显示了如何在目标服务器本身上运行 playbook。
如果您想使用本地连接,这有点棘手。但是,如果您为 hosts 设置使用变量并在 hosts 文件中为 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
【讨论】:
【参考方案12】:稍微不同的解决方案是使用特殊变量ansible_limit
,它是当前执行Ansible 的--limit
CLI 选项的内容。
- hosts: " ansible_limit | default(omit) "
这里不需要定义额外的变量,只需运行带有--limit
标志的剧本即可。
ansible-playbook --limit imac-2.local user.yml
【讨论】:
【参考方案13】:我建议使用--limit <hostname or ip>
【讨论】:
OP 已经在他们的问题中使用--limit
,因此它本身不能解决问题。以上是关于将 Ansible 剧本安全地限制在单台机器上?的主要内容,如果未能解决你的问题,请参考以下文章
尝试在单台 Mac 机器上使用并行 Jenkins 管道构建 iOS 应用程序时出现缓存问题
如何使 c# windows 窗体应用程序仅在单台 PC 上运行?