Ansible:定义剧本“默认”变量,优先于角色,但在清单中可覆盖

Pie*_* B. 5 ansible ansible-2.x ansible-inventory

TL; DR

使用 Ansible,我试图在剧本级别定义某种默认变量 - 让我们称之为剧本默认值- 它优先于角色默认值,但可以被库存清单group_vars/所有变量覆盖。

如何定义某种剧本默认变量,该变量优先于角色默认值,但同时可以被清单(环境)覆盖?


目前 Ansible 2.x 变量优先级如下:

  • 角色默认值
  • 库存文件或脚本组变量
  • 库存组_vars/全部
  • 剧本 group_vars/all

我希望实现的目标是:

  • 角色默认值
  • 剧本默认值
  • 库存文件或脚本组变量
  • 库存组_vars/全部
  • 剧本 group_vars/all

从我之前使用的大多数工具和应用程序来看,在环境级别(开发、测试、QA、产品等)定义的变量优先于“应用程序”变量,而“应用程序”变量本身优先于“外部”组件。那么优先顺序如下:

  • “外部”组件变量(来自 Galaxy 的角色)
  • “应用程序”变量(剧本变量)
  • “环境”变量(开发、测试等)

环境具有最高优先级。但我找不到用 Ansible 重现这种模式的方法。这些事情在 Chef(外部说明书默认值被包装器说明书默认值覆盖)和 Puppet 中很容易,但我找不到使用 Ansible 实现相同目的的方法。

一个简单应用程序的示例

让我们考虑一个由多个与 Web 服务器交互的服务组成的应用程序。这些服务是通用的,并在我组织的其他部分中重复使用,但就我而言,我使用了 apache2。我有这些角色:

  • 阿帕奇
  • 绿色服务
  • 蓝色服务
  • 红色服务

这些默认变量:

# roles/apache/defaults/main.yml
apache_listen_port: 80

# roles/green_service/defaults/main.yml
green_service_port: 80

# roles/blue_service/defaults/main.yml
blue_service_port: 80

# roles/red_service/defaults/main.yml
red_service_port: 80
Run Code Online (Sandbox Code Playgroud)

每个服务都必须知道 apache 端口,这在默认值中表示。如果其他应用程序需要 Nginx 或其他网络服务器的绿色服务,则只需包含相关角色即可。

现在,如果我想在一个或多个环境中将端口从 80 更改为 8081,该怎么办?

“直接”的方式

..我最终不得不在每个环境中复制所有这些变量,例如:

# production/group_vars/all/all.yml
apache_listen_port: 8081
green_service_port: 8081
blue_service_port: 8081
red_service_port: 8081

# ... and so on in each environments inventory!
Run Code Online (Sandbox Code Playgroud)

在这个简单的例子中这可能没问题,但是当你有 10 个以上的服务和 5 个以上的环境时,它就变成了一场噩梦......更新一个简单的变量可能会对很多服务产生影响,并且变得难以维护和理解。

除了答案“您的服务设计可能是错误的......”之外,我正在寻找一种方法来避免这种情况。

我想实现什么

我想要实现的是能够在剧本级别表示我的服务和网络服务器之间的变量链接,例如:

# somewhere in my playbook .yml
# these variables override roles default
# but can be overriden in my inventories
myapp_port: 80

apache_listen_port: "{{ myapp_port }}"
green_service_port: "{{ myapp_port }}"
blue_service_port: "{{ myapp_port }}"
red_service_port: "{{ myapp_port }}"
Run Code Online (Sandbox Code Playgroud)

现在,在每个环境中,我只需要覆盖一个且仅一个变量,例如:

# production/group_vars/all/all.yml
myapp_port: 8081
Run Code Online (Sandbox Code Playgroud)

我没有找到一种明智的方法,而不使我的剧本过于复杂化来实现类似的目标。

使用 Ansible,是否有适当的方法来实现 Playbook 变量优先于默认变量但不优先于库存变量?

Kon*_*rov 4

请记住,在 Ansible 中,{{ ... }}表达式通常以惰性方式模板化(除了set_fact),因此您不会“分配”变量,而是告诉 Ansible 如何在运行时获取值。

如果您可以控制角色:

你(们)能做到roles/apache/defaults/main.yml

apache_listen_port: "{{ myapp_port | default(80) }}"
Run Code Online (Sandbox Code Playgroud)

roles/green_service/defaults/main.yml

green_service_port: "{{ myapp_port | default(80) }}"
Run Code Online (Sandbox Code Playgroud)

在这种情况下,当 Ansible 遇到apache_listen_port某个表达式时,它会将值模板化为 的值myapp_port(如果在该特定时刻可用)或80以其他方式使用。

如果您myapp_port在所需库存中定义,则apache_listen_portgreen_service_port将获得其值。

如果您无法控制角色:

你可以在剧本中使用这个技巧:

vars:
  myapp_port_playbook: "{{ myapp_port | default(80) }}"
  apache_listen_port: "{{ myapp_port_playbook }}"
  green_service_port: "{{ myapp_port_playbook }}"
Run Code Online (Sandbox Code Playgroud)

在本例中apache_listen_port, 和green_service_port直接模板化为myapp_port_playbook的值,但其值又是myapp_port的值或80默认值。