Ansible ダイナミックインベントリと変数定義

Ansible ではプレイブックでソフトウェア構築を行う。 構築するソフトウェアとインフラの対応付けをインベントリで行う。

これを前提に Ansible のプレイブック・ロールで利用する変数の定義箇所を整理する。 またダイナミックインベントリを使う前提で考える。

Ansible では対象となる環境情報から動的に対応づけをするためめにダイナミックインベントリと呼ばれる仕組みがある。ダイナミックインベントリでは静的なファイルの代わりに JSON を生成するスクリプトを利用する。スクリプトは標準出力に JSON を吐き出せば何でも構わないのだけど、できるだけ自作したくない。

そこでサポート対象外ではあるものの公式リポジトリに含まれているダイナミックインベントリを利用する。

コピーしてリポジトリに含める場合にはライセンスの記載が必要だが、スクリプト自体に書かれているので削らない限り問題ない。

具体例として Vagrantm,EC2 を考える。

TL;DR

結局、下のように整理することにした。 ec2 の中に同一ステージが複数あった場合とかについてはデプロイ時に -l オプションで対象を切り替えているのが納得いかない。

  • 環境ごとのインベントリは inventories/{infra} ディレクトリにまとめる
  • ダイナミックインベントリとプレイブックのグループは静的ファイルで関連づける
  • インフラ依存の変数は inventories/{infra}/group_vars で解決する
  • ホストグループに関する変数はプレイブックの group_vars 配下で解決する
  • ステージ依存の変数はプレイブックの vars 配下に置き vars_file で読み込む
  • 環境依存,ステージ依存の秘密情報は ansible-vault で暗号化
  • ansible-vault のパスワードファイルは1つにして ansible.cfg で指定する

ansible dynamic inventory(vagrant.py)を使う

対応するインフラの状況に応じて動的にインベントリが作成される。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
(ansible) $ ./inventories/vagrant/vagrant.py --list | jq .
{
  "vagrant": [
    "sban"
  ],
  "_meta": {
    "hostvars": {
      "sban": {
        "ansible_ssh_user": "vagrant",
        "ansible_ssh_host": "127.0.0.1",
        "ansible_ssh_private_key_file": "/home/masumi/works/sandbox-ansible/.vagrant/machines/sban/virtualbox/private_key",
        "ansible_ssh_port": "2222"
      }
    }
  }
}

ansible dynamic inventory(ec2.py)を使う

長くなるから書かないけど、{"_meta": {"hostvars": {}}, "group_name": ... } の形式になっている。何を元にグルーピングされるかは ec2.ini で制御できる。

設定パラメタ 利用する属性 グループ名
group_by_instance_id インスタンスID {instance_id}
group_by_availability_zone AZ {az_name}
group_by_key_pair キーペア key_{key_name}
group_by_security_group セキュリティグループ security_{security_group_name}
group_by_ami_id AMI ID {ami_id | replace("-", “_")}
group_by_instance_type インスタンスタイプ type_{instance_type_name}
group_by_tag_keys タグ tag_{tag_key},tag_{tag_key}_{tag_value}

他にも色々ある、実験したりコード読んで特定する。

対応づけ

インフラとコンポーネントを分離する

ダイナミックインベントリ でのグルーピングだけを使うと プレイブック にインフラ(e.g. ec2)の知識が漏れてしまう。

しかし ansible-playbook-i オプションでディレクトリを指定すると配下の全てのスクリプト・静的ファイルが利用できることを使えば解決できる。

そこでインベントリはディレクトリ単位で定義することにする。

./inventories/vagrant/hosts.yml children フィールドを用いてインフラの情報を論理的な関係(プレイブックのホストグループ)に対応させる。

1
2
3
4
5
6
api:
  children:
    vagrant:
monitor:
  children:
    vagrant:

逆にホストグループなど論理的な制約や変数をインベントリ側で解決しない様に注意する プレイブックからインフラへの依存を取り除けた。 プレイはコンポーネントに対応するためコンポーネントはホストグループとして group_vars/{group} 配下で定義すればよい。

ステージによる定義を分離する

dev,staging,prod といったステージごとの設定を定義する場所が問題になる。 考えられる方法は2つある。

  • vars/{stage}.yml に定義して vars_files で読む
  • グループとして定義してプレイブックの group_vars/$(prod|stage|dev) で定義する

実際のところ、いまは1つめを使っている。だけど良さそうなのは2つめ。 インベントリで動的にグループを割り当てる2つめが良さそう。

秘密情報

秘密情報は下みたいに暗号化する。

1
$ ansible-vault --vault-password-file=./system.vault-pass encrypt ./group_vars/prod/secret.yml

ansuble.cfg でデフォルトの鍵ファイルを指定しておくとオプションを減らせる。

1
2
[default]
vault_password_file=./system.vault-pass

構成

結局、ベストプラクティスと一致したと思われる。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
.
|-- Makefile
|-- README.md
|-- Vagrantfile
|-- ansible.cfg
|-- group_vars
|   |-- all
|   |-- dev
|   |   |-- main.yml
|   |   `-- secret.yml
|   `-- prod
|       |-- main.yml
|       `-- secret.yml
|-- inventories
|   |-- ec2
|   |   |-- ec2.ini
|   |   |-- ec2.py
|   |   |-- group_vars
|   |   |   |-- all
|   |   |   |   `-- main.yml
|   |   |   |-- prod
|   |   |   |   `-- main.yml
|   |   |   `-- stage
|   |   |       `-- main.yml
|   |   `-- hosts.yml
|   |-- localhost
|   |   |-- group_vars
|   |   |   `-- all
|   |   |       `-- main.yml
|   |   `-- hosts.yml
|   `-- vagrant
|       |-- group_vars
|       |   `-- all
|       |       `-- main.yml
|       |-- hosts.yml
|       `-- vagrant.py
|-- roles
|   `-- common
|       |-- defaults
|       |   `-- main.yml
|       `-- tasks
|           `-- main.yml
|-- site.yml
`-- vars
comments powered by Disqus