【问题标题】:Ansible: Switch/Override variables depending on target inventoryAnsible:根据目标库存切换/覆盖变量
【发布时间】:2020-08-28 07:15:33
【问题描述】:

我正在考虑使用 Ansible 在三种不同环境(prod/stag/dev)中的主机上部署 rails 应用程序。但是很难为每个主机设置不同的RAILS_ENV

我尝试了以下设置。

目录结构:

.
├── group_vars
│   └── app1.yml
├── inventories
│   ├── develop
│   │   └── group_vars
│   │        └── app1.yml
│   ├── production
│   └── staging
└── roles

group_vars/app1.yml(用于跨环境的通用变量)

services:
  - name: app
    environment:
      {env_vars other than RAILS_ENV}: foo
  - name: db
...

inventories/develop/group_vars/app1.yml(针对特定库存变量)

services:
  - name: app
    environment:
      RAILS_ENV: development

在判断变量是否已经定义时,似乎 Ansible 只查看顶层的键。所以第二个文件中的RAILS_ENV 会被无意忽略。 (不知道为什么库存变量的优先顺序较低,我认为它应该更高,因为它更具体?)

有什么干净的方法可以做到这一点吗?

【问题讨论】:

标签: ansible


【解决方案1】:

正如 Ansible 文档的 散列行为 文章中所建议的:

DEFAULT_HASH_BEHAVIOUR

说明:此设置控制变量在 Ansible 中的合并方式。默认情况下,Ansible 将以特定的优先顺序覆盖变量,如变量中所述。当更高优先级的变量获胜时,它将替换另一个值。一些用户更喜欢合并散列变量(在 Python 术语中也称为“字典”)。此设置称为“合并”。这不是默认行为,它不会影响值为标量(整数、字符串)或数组的变量。我们通常建议不要使用此设置,除非您认为您绝对需要它,并且官方示例 repos 中的剧本不使用此设置在版本 2.0 中添加了combine 过滤器以允许对特定变量执行此操作(描述在过滤器中)。

来源:https://docs.ansible.com/ansible/latest/reference_appendices/config.html#default-hash-behaviour
实现此目的的推荐方法是使用combine 过滤器。

也就是说,您的字典中确实有一个列表,这将使其成为一项相当复杂的任务。
将其切换到字典会减轻痛苦:

services:
  app:
    environment:
      RAILS_ENV: development
      overridable_var: foo
  db:
    foo: bar

如果我理解正确,您正在尝试实施 alternative directory layout 的 Ansible 最佳实践。

如果是这种情况,这里可能是一个与您的用例相匹配的解决方案,因为您可以将您的列表转置到字典中的 services 中,如上文所述:

  1. 在每个环境的 group_var 文件中,将环境名称作为 services 字典键的前缀,例如develop_services
    develop_services:
      app:
        environment:
          RAILS_ENV: development
          overridable_var: develop from inventories group_vars
    
  2. 在您的剧本中,作为第一个任务,甚至作为pre_taskcombine 与您的环境匹配的services 字典,您可以将其作为inventory_file 路径的一部分:
    pre_tasks:
      - set_fact: 
          services: "{{ services | combine(vars[inventory_file.split('/')[-2] ~ '_services'], recursive=True) }}"
    

那就用吧。

给定目录布局:

.
├── group_vars
│   └── app1.yml
├── inventories
│   ├── develop
│   │   ├── group_vars
│   │   │   └── app1.yml
│   │   └── hosts
│   └── staging
│       ├── group_vars
│       │   └── app1.yml
│       └── hosts
└── play.yml

group_vars/app1.yml

services:
  app:
    environment:
      bar: foo
      overridable_var: from root group_vars
      do_not_override_me: from root group_vars
  db:
      engine: postgres

库存/开发/主机

all:
  children:
    app1:
      hosts:
        app:

inventories/develop/group_vars/app1.yml

develop_services:
  app:
    environment:
      RAILS_ENV: development
      overridable_var: develop from inventories group_vars  

库存/暂存/主机

all:
  children:
    app1:
      hosts:
        app:

inventories/staging/group_vars/app1.yml

staging_services:
  app:
    environment:
      RAILS_ENV: staging
      overridable_var: staging from inventories group_vars  

play.yml

- hosts: all
  gather_facts: no
     
  pre_tasks:
    - set_fact: 
        services: "{{ services | combine(vars[inventory_file.split('/')[-2] ~ '_services'], recursive=True) }}"

  tasks:
    - debug:
        msg: "{{ services }}"

develop 运行它会给出回顾:

$ ansible-playbook play.yml -i inventories/develop

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

TASK [set_fact] ****************************************************************************************************
ok: [app]

TASK [debug] *******************************************************************************************************
ok: [app] => {
    "msg": {
        "app": {
            "environment": {
                "RAILS_ENV": "development",
                "bar": "foo",
                "do_not_override_me": "from root group_vars",
                "overridable_var": "develop from inventories group_vars"
            }
        },
        "db": {
            "engine": "postgres"
        }
    }
}

PLAY RECAP *********************************************************************************************************
app                        : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

staging 运行它会给出回顾:

$ ansible-playbook play.yml -i inventories/staging

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

TASK [set_fact] ****************************************************************************************************
ok: [app]

TASK [debug] *******************************************************************************************************
ok: [app] => {
    "msg": {
        "app": {
            "environment": {
                "RAILS_ENV": "staging",
                "bar": "foo",
                "do_not_override_me": "from root group_vars",
                "overridable_var": "staging from inventories group_vars"
            }
        },
        "db": {
            "engine": "postgres"
        }
    }
}

PLAY RECAP *********************************************************************************************************
app                        : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

额外说明: 请注意,这将不允许 playbook 同时在两个环境中运行,正如 使用文档的多个库存来源

请记住,如果清单中存在变量冲突,则根据How variables are mergedVariable precedence: Where should I put a variable? 中描述的规则解决它们。合并顺序由库存源参数的顺序控制。如果暂存库存中的[all:vars] 定义了myvar = 1,但生产库存定义了myvar = 2,则剧本将使用myvar = 2 运行。如果 playbook 使用 -i production -i staging 运行,结果将被反转。

来源:https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html#using-multiple-inventory-sources

【讨论】:

  • 不幸的是,我无法将services 更改为字典,因为许多其他剧本都在提及这一点。但我认为你的回答看起来是实现我想做的最好的方法。如果有一天我有机会修改所有代码库,我可能会尝试这个。谢谢!
猜你喜欢
  • 2018-02-27
  • 1970-01-01
  • 2022-01-23
  • 2013-12-19
  • 2018-11-04
  • 1970-01-01
  • 1970-01-01
  • 2019-11-21
  • 1970-01-01
相关资源
最近更新 更多