ansibleで変数を共通使いにして、inventoryを環境毎に使い分ける方法

2021年11月23日2021年11月24日

ansibleでplaybookを流す対象を指定するときはinventoryを宣言することになる。

# /bin/bash
ansible-playbook playbook.yml -i inventory.yml


inventoryはホスト情報を定義しており、roleで使える変数を定義することができる。

# inventory.yml
all:
  children:
    web:
      hosts:
        httpd-1:
          ansible_host: 192.168.1.1
        httpd-2:
          ansible_host: 192.168.1.2
      vars:
        config:
          logs_enable: true
          process_enable: true
    db:
      hosts:
        mysql-1:
          ansible_host: 192.168.2.1
        mysql-2:
          ansible_host: 192.168.2.2
      vars:
        mount:
          path: "/data"
        config:
          logs_enable: true
          process_enable: true


production環境ではこのinventoryだが、staging環境ではホストの定義情報が変わる。
しかし変数は各環境で共通して使いたい場合、inventoryを複製すると変数を2重に管理することになってしまう。

そこで変数の2重管理にならないようgroup_varsディレクトリに、group名.ymlで配置するとinventoryを読み込んだときに自動でgroup名.ymlの変数を取り込んでくれる。
変数の切り出し前後は変数の抜けがないよう、ansible-inventoryを使うことで差分を確認することができる。

ansible-inventory -i inventories/inventory.yml --list --yaml


切り出し前の確認

切り出し前に確認をします。

$ ansible-inventory -i inventories/inventory.yml --list --yaml 
all:
  children:
    db:
      hosts:
        mysql-1:
          ansible_host: 192.168.2.1
          config: &id001
            logs_enable: true
            process_enable: true
          mount: &id002
            path: /data
        mysql-2:
          ansible_host: 192.168.2.2
          config: *id001
          mount: *id002
    ungrouped: {}
    web:
      hosts:
        httpd-1:
          ansible_host: 192.168.1.1
          config: &id003
            logs_enable: true
            process_enable: true
        httpd-2:
          ansible_host: 192.168.1.2
          config: *id003


最後にdiffるのでファイルに書き出しておきます。

ansible-inventory -i inventory.yml --list --yaml >> diff/before.yml


切り出し作業

切り出し前の構成です。

# before
$ tree
.
├── inventories
│   └── inventory.yml
└── roles
    ├── role1
    │   ├── files
    │   │   └── index.html
    │   └── tasks
    │       └── main.yml
    └── role2
        ├── tasks
        │   └── main.yml
        └── templates
            └── index.html.j2


8 directories, 5 files


それでは切り出して行きましょう。

  1. group_varsディレクトリの作成
  2. web.ymlの作成
  3. db.ymlの作成
  4. inventoryからvars部分のみ切り出し


切り出し後の構成です。

$ tree
.
├── group_vars
│   ├── db.yml
│   └── web.yml
├── inventories
│   └── inventory.yml
└── roles
    ├── role1
    │   ├── files
    │   │   └── index.html
    │   └── tasks
    │       └── main.yml
    └── role2
        ├── tasks
        │   └── main.yml
        └── templates
            └── index.html.j2


9 directories, 7 files


続いてinventoryとgroup_varsの内容です。
ちなみにvars: という宣言は、group_varsに移動したタイミングで不要になるため削除しています。

# inventories/inventory.yml
all:
  children:
    web:
      hosts:
        httpd-1:
          ansible_host: 192.168.1.1
        httpd-2:
          ansible_host: 192.168.1.2
    db:
      hosts:
        mysql-1:
          ansible_host: 192.168.2.1
        mysql-2:
          ansible_host: 192.168.2.2


# group_vars/web.yml
config:
  logs_enable: true
  process_enable: true


# group_vars/db.yml
mount:
  path: "/data"
config:
  logs_enable: true
  process_enable: true


切り出し後の確認

いよいよ確認していきます。

$ ansible-inventory -i inventories/inventory.yml --list --yaml 
all:
  children:
    db:
      hosts:
        mysql-1:
          ansible_host: 192.168.2.1
          config: &id001
            logs_enable: true
            process_enable: true
          mount: &id002
            path: /data
        mysql-2:
          ansible_host: 192.168.2.2
          config: *id001
          mount: *id002
    ungrouped: {}
    web:
      hosts:
        httpd-1:
          ansible_host: 192.168.1.1
          config: &id003
            logs_enable: true
            process_enable: true
        httpd-2:
          ansible_host: 192.168.1.2
          config: *id003


ファイルに書き出してdiffって見ましょう。

ansible-inventory -i inventories/inventory.yml --list --yaml >> diff/after.yml


差分確認

$ diff diff/before.yml diff/after.yml 
$ echo $0


ということで差分も出てなく、無事に切り出し完了です。

staging環境のinventoryを作って確認

次にstaging環境のinventoryを作ります。

# inventories/inventory_staging.yml
all:
  children:
    web:
      hosts:
        httpd-1:
          ansible_host: 192.168.11.1
        httpd-2:
          ansible_host: 192.168.11.2
    db:
      hosts:
        mysql-1:
          ansible_host: 192.168.12.1
        mysql-2:
          ansible_host: 192.168.12.2


diffってみる

まずは同じようにinventoryを出力します。

ansible-inventory -i inventories/inventory_staging.yml --list --yaml >> diff/after_staging.yml


想定はホストのみ変更しており、変数は共通で使えるはずです。

$ diff diff/after.yml diff/after_staging.yml
6c6
<           ansible_host: 192.168.2.1
---
>           ansible_host: 192.168.12.1
13c13
<           ansible_host: 192.168.2.2
---
>           ansible_host: 192.168.12.2
20c20
<           ansible_host: 192.168.1.1
---
>           ansible_host: 192.168.11.1
25c25
<           ansible_host: 192.168.1.2
---
>           ansible_host: 192.168.11.2


ホストだけが変更になっていることが確認できました。

[注意]変数の上書きはできない

ちなみに共通の変数からproduction環境だけ「この値にしたい」というとき、今まで通り直接inventoryにvarsを追加しても変数をオーバーライドすることができません。
理由はinventory host_varsよりinventory group_varsの方が優先度が高いためです。

command line values (for example, -u my_user, these are not variables)
role defaults (defined in role/defaults/main.yml) 1
inventory file or script group vars 2
inventory group_vars/all 3
playbook group_vars/all 3
inventory group_vars/* 3
playbook group_vars/* 3
inventory file or script host vars 2
inventory host_vars/* 3
playbook host_vars/* 3
host facts / cached set_facts 4
play vars
play vars_prompt
play vars_files
role vars (defined in role/vars/main.yml)
block vars (only for tasks in block)
task vars (only for the task)
include_vars
set_facts / registered vars
role (and include_role) params
include params
extra vars (for example, -e "user=my_user")(always win precedence)


https://docs.ansible.com/ansible/latest/userguide/playbooksvariables.html#understanding-variable-precedence

まとめ

inventoryから変数を切り分けることで環境に応じたinventoryを作成することが出来ました。
切り出し前後にはansible-inventory --list --yamlをすることでinventoryを事前に確認することができます。

環境毎による管理の複雑性を取り除くことができました。
絶対に2重管理したくないマン!