AnsibleのInventory Groupで環境毎に異なるlocalhostを構築する

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

渡辺です。

Ansibleでは、ホストをInventoryファイル(スクリプト)で定義します。 この時、次のようにグループ化することが可能です。

[prd]
10.0.0.10
10.0.0.20
[stg]
10.0.100.10
10.0.100.20

ここではprdグループとstgグループにホストを定義しています。 ホストをグループ化することで、グループ変数(group variable)が利用できるため、本番環境(prd)と検証環境(stg)における構成の差異を反映した構成管理を行えます。

ここまではよくある話。 今回、ハマったのは本番環境も検証環境もlocalhostの場合です。 すなわちAnsibleをローカル実行させる場合に期待通りのグループ変数が適用されなかったというお話です。

期待通りにいかないファイル構成

こちらは上手く動作しなかった構成です。

# hosts
[stg]
localhost
[prd]
localhost
# stg.yml
- hosts: stg
  connection: local
  tasks:
    - debug: var=stage
# prd.yml
- hosts: prd
  connection: local
  tasks:
    - debug: var=stage
# group_vars/stg.yml
stage: stg
# group_vars/prd.yml
stage: prd

実行してみます。

$ ansible-playbook -i hosts stg.yml 

PLAY ***************************************************************************

TASK [setup] *******************************************************************
ok: [localhost]

TASK [debug] *******************************************************************
ok: [localhost] => {
    "stage": "stg"
}

PLAY RECAP *********************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0  

期待通りです。

PLAY ***************************************************************************

TASK [setup] *******************************************************************
ok: [localhost]

TASK [debug] *******************************************************************
ok: [localhost] => {
    "stage": "stg"
}

PLAY RECAP *********************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0 

あれ・・・prdのはずが・・・。

1

調査中...

対策後の構成

試行錯誤の結果、次のような構成としました。

# local
[local]
localhost
# local.yml
- hosts: local
  connection: local
  tasks:
    - group_by: key={{ env }}
    - debug: var=stage
# group_vars/stg.yml
stage: stg
# group_vars/prd.yml
stage: prd

実行してみます。

$ ansible-playbook --extra-vars "env=stg" -i local local.yml 
PLAY ***************************************************************************

TASK [setup] *******************************************************************
ok: [localhost]

TASK [group_by] ****************************************************************
ok: [localhost]

TASK [debug] *******************************************************************
ok: [localhost] => {
    "stage": "stg"
}

PLAY RECAP *********************************************************************
localhost                  : ok=3    changed=0    unreachable=0    failed=0   
$ ansible-playbook --extra-vars "env=prd" -i local local.yml 

PLAY ***************************************************************************

TASK [setup] *******************************************************************
ok: [localhost]

TASK [group_by] ****************************************************************
ok: [localhost]

TASK [debug] *******************************************************************
ok: [localhost] => {
    "stage": "prd"
}

PLAY RECAP *********************************************************************
localhost                  : ok=3    changed=0    unreachable=0    failed=0   

期待通りです!

考察

最初の構成は、localhostという同一のホストが複数のグループに属している状態になります。 つまり、localhostはstgグループにもprdグループにも属しています。

Ansibleは、ホスト毎に実行を行います。 この時、属しているグループからグループ変数を適用する流れになります。 localhostは、stgグループにもprdグループにも属しているので、両方のグループ変数ファイルが適用されます 今回、優先されたのがstgグループのグループ変数だったわけです。

つまり、localhostを環境毎にグループ変数を適用するにはこの定義では上手くいきません。

--extra-varsオプションとgroup_byモジュールの利用

これらの問題を解決するために、--extra-varsオプションとgroup_byモジュールを利用しました。 他の方法もありますが、設定ファイルの数が少なく、重複も減らしたいとなると、この方法がベストだと思います。

--extra-varsオプションは次のようにansible-playbookのコマンド実行時に指定します。

$ ansible-playbook --extra-vars "env=prd" -i local local.yml 

これでenv=prdが変数として渡されることになります。

group_byモジュールは、playbook実行時に動的にグループを指定するモジュールです。 実行環境から取得する情報などかグループを指定しますが、ここでは--extra-varsオプションで指定されたenvを使い、prd(stg)グループに追加します。

  tasks:
    - group_by: key={{ env }}

prd(stg)グループに属すれば、それぞれのグループ変数が適用されることになります。

まとめ

Ansibleは、あくまでホストを主人公として実行されます。 同じホストに異なる設定を行いたい場合は、動的にグループを指定してください(inventoryファイル自体を環境分作成する方法もあります)。 動的なグループ追加はgroup_byモジュールで実現します。

localhostの時はご注意を!