Edited at

Ansible ( 俺の中で ) 最強の Best Practices

More than 1 year has passed since last update.


前書き

サービスごとに複数のサーバを ansible で管理していく中で、同じような内容で共用したい role が沢山出てきました。それぞれサービス毎に管理していくのは非常に面倒…。困っていたところを下記の記事が救ってくれました。yteraokaさんには本当に感謝しています。

Ansible オレオレベストプラクティス - Qiita

しかし、group_vars 内のファイルや hosts ファイル等が増えていくにつれ、どのファイルがどんな内容かがディレクトリ構成やファイル名を見ただけでは分かりづらいという点や、環境 ( ステージ ) ごとの管理がしづらいという点などの問題が出てきました。


そこで、「Ansible オレオレベストプラクティス」を参考にし、更にオレオレにして「 ( 俺の中で ) 最強のベストプラクティス」に辿り着きました。

本記事は、あくまで 自分の中で最強 ということなので暖かい目で見ていただきたいです。

現在のままでも十分にAnsibleの管理ができている方や、最強でもなんでもないと思う方はスルーしてください。

また、こうすべきやこうした方がいいなどあればコメント下さい。

※ 追記 ( 2016/12/15 )

共用したいロールの管理方法として、 common-roles ではなく Ansible Galaxy を利用する方法を投稿しました。こちらの方がよりスマートかと思います。

You Don't Need common-roles in Ansible - Qiita


筆者が辿り着いたベストプラクティス

.

├── common_playbook/ # サービス全ての共用の playbook
│ ├── vars/ # サービス全ての共用の変数
│ │ └── common.yml
│ └── roles/ # サービス全ての共用のroles
│ ├── common/
│ │ ├── tasks/
│ │ │ └── main.yml
│ │ ├── handlers/
│ │ │ └── main.yml
│ │ ├── templates/
│ │ │ ├── ...
│ │ │ └── ...
│ │ ├── files/
│ │ │ ├── ...
│ │ │ └── ...
│ │ └── defaults/
│ │ └── main.yml
│ ├── role1/
│ │ :
│ ├── role2/
│ │ :
│ └── ...
├── service1/
│ ├── Vagrantfile
│ └── playbook
│ ├── hosts/
│ │ ├── development # デベロップメント環境用サーバのインベントリファイル
│ │ ├── local # ローカル環境用サーバ ( vagrant ) のインベントリファイル
│ │ ├── production # プロダクション環境サーバ用のインベントリファイル
│ │ ├── staging # ステージング環境サーバ用のインベントリファイル
│ │ └── test # テスト環境サーバ用のインベントリファイル
│ ├── group_vars/
│ │ ├── all.yml # service1 に所属するグループ全てへの変数割り当て
│ │ ├── development/ # development に所属するグループをまとめたディレクトリ
│ │ │ ├── all.yml # development に所属するグループ全てへの変数割り当て
│ │ │ ├── group1.yml # development に所属する group1 のみへの変数割り当て
│ │ │ ├── group2.yml # ""
│ │ │ └── ...
│ │ ├── local/ # local に所属するグループをまとめたディレクトリ
│ │ │ :
│ │ ├── production/ # production に所属するグループをまとめたディレクトリ
│ │ │ :
│ │ ├── staging/ # staging に所属するグループをまとめたディレクトリ
│ │ │ :
│ │ └── test/ # test に所属するグループをまとめたディレクトリ
│ ├── host_vars/
│ │ ├── hostname1.yml # システムの特定の変数が必要な場合
│ │ ├── hostname2.yml
│ │ └── ...
│ ├── site.yml # マスター playbook
│ ├── webservers.yml # webserver 層の playbook
│ ├── dbservers.yml # dbserver 層の playbook
│ ├── ...
│ └── roles/ # service1 の roles
│ ├── common/
│ │ ├── tasks/
│ │ │ └── main.yml
│ │ ├── handlers/
│ │ │ └── main.yml
│ │ ├── templates/
│ │ │ ├── ...
│ │ │ └── ...
│ │ ├── files/
│ │ │ ├── ...
│ │ │ └── ...
│ │ ├── vars/
│ │ │ └── main.yml
│ │ ├── defaults/
│ │ │ └── main.yml
│ │ └── meta/
│ ├── role1/
│ │ :
│ ├── role2/
│ │ :
│ └── ...
├── service2/
│ :
└── ...

※ 自分の使い方に合うように、自由にディレクトリやファイルの変更や整理してください。


yteraokaさんのベストプラクティス

Ansible オレオレベストプラクティス - Qiita


yteraokaさんのベストプラクティスからの変更点

ディレクトリ構成を見れば大体分かると思いますが、変更点を以下にまとめます。

大きく分けると 二つ のみです!やったことは本当にシンプルです。


これしか変更してないのに最強ってなんだよって話ですが…。


ディレクトリ構成を ( 自分的に ) 分かりやすくした


  • common_roles, private_vars, varsをひとまとめにした

  • 環境ごとのhostsファイルをhosts/ という名でひとまとめにした

  • service ごとに Vagrantfile を追加し、playbook を playbook/ という名でひとまとめにした


環境ごとの切り替えや変数の管理を楽にした


  • group_vars以下に環境ごとのグループ名でディレクトリを用意した


vars ファイルの変数が割り当てられる範囲

ファイル
割り当てられる範囲

common_playbook/vars/common.yml
サービス全て

<service>/playbook/group_vars/all.yml
サービスごとに所属するグループ全て

<service>/playbook/group_vars/<environment>/all.yml
環境ごとに所属するグループ全て

<service>/playbook/group_vars/<environment>/<group>.yml
環境ごとに所属するグループのみ


筆者のベストプラクティス構成の設定例

会社やサービスごとによって 環境 ( development | local | production | staging | test ) は異なると思いますので、hosts ファイルや group_vars 内のディレクトリは必要に応じて削除してください。

これから local と production 二つの環境で、ディレクトリ構成とファイルの設定例を説明していきます。


playbook


構成

以下のようにグループごとに playbook を分けます。

今回の例では webservers と dbservers 二つに分けました。

...

│ ├── site.yml
│ ├── webservers.yml
│ ├── dbservers.yml
...


設定例

インフラ全体を定義するplaybookです。

webservers と dbservers の playbook をインクルードしているだけです。


site.yml

---

# file: site.yml
- include: webservers.yml
- include: dbservers.yml

../../common_playbook/vars/* で共用の vars ファイルを読み込み、  

../../common_playbook/roles/* で共用の role を読み込みます。

hosts: webservers で分かる通り hosts に webservers グループを指定します。


webservers.yml

---

# file: webservers.yml
- hosts: webservers
vars_files:
- ../../common_playbook/vars/common.yml
roles:
- ../../common_playbook/roles/common
- common
- ../../common_playbook/roles/awscli
- ../../common_playbook/roles/nginx
- nginx

hosts: dbservers.yml で分かる通り hosts に dbservers.yml グループを指定します。


dbservers.yml

---

# file: dbservers.yml
- hosts: dbservers
vars_files:
- ../../common_playbook/vars/common.yml
roles:
- ../../common_playbook/roles/common
- common
- ../../common_playbook/roles/awscli
- ../../common_playbook/roles/mysql
- mysql


inventory


ディレクトリ構成

ホストの目的および地理またはデータセンターの場所に基いて hosts ファイルにグループを定義します。

local ファイルは全てのローカルホストのインベントリを含み、production ファイルはすべての本番ホストのインベントリを含みます。

...

│ ├── hosts/
│ │ ├── local
│ │ └── production
...


設定例

local の hosts ファイル

# file: hots/local

[webservers]
192.168.33.10

[dbservers]
192.168.33.11

[local:children]
webservers
dbservers

production の hosts ファイル

# file: hots/production

# アジアパシフィック ( 東京 ) リージョンの webservers
[tokyo-webservers]
web1-ap-northeast-1.example.com
web2-ap-northeast-1.example.com

# 米国西部 ( オレゴン )
[oregon-webservers]
web1-us-west-2.example.com
web2-us-west-2.example.com

# アジアパシフィック ( 東京 ) リージョンの dbservers
[tokyo-dbservers]
db1-ap-northeast-1.example.com
db2-ap-northeast-1.example.com

# 米国西部 ( オレゴン ) リージョンの dbservers
[oregon-dbservers]
db1-us-west-2.example.com

# 全てのリージョンの webservers
[webservers:children]
tokyo-webservers
oregon-webservers

# 全てのリージョンの dbserver
[dbservers:children]
tokyo-dbservers
oregon-dbservers

# アジアパシフィック ( 東京 ) リージョンの全てのホスト
[tokyo:children]
tokyo-webservers
tokyo-dbservers

# 米国西部 ( オレゴン ) リージョンの全てのホスト
[oregon:children]
oregon-webservers
oregon-dbservers

# 全てのホスト
[production:children]
webservers
dbservers
tokyo
oregon


group_vars


ディレクトリ構成

group_vars 以下は グループ名でディレクトリを作ることが可能 です。group_vars 以下に環境ごとのグループ名でディレクトリを用意します。そして、そのディレクトリ内に環境ごとに所属するグループファイルを配置します。

そうすることで、group_vars 以下を見たときに、環境ごとに整理されているため編集するファイルに迷うことがなくなり、環境ごとに所属するグループがどんなグループかも分かります。

上記の vars ファイルの変数が割り当てられる範囲 でも説明しましたが、



  • group_vars/all.yml は、サービスごとに所属するグループ全て (group_vars/ 以下に配置したグループ全て)


  • group_vars/<environment>/all.yml には、環境ごとに所属するグループ全て ( group_vars/<environment>/ 以下に配置したグループ全て )


  • group_vars/production/<group>.yml には、環境ごとに所属するグループのみ ( group_vars/production/.yml のみ )

に変数が割り当てられます。

上記内容におそらく誤りがあります。詳細はコメント欄を参照してください。

また、環境ごと所属するグループでグループ名が等しくても、ディレクトリごとに分かれているため、hosts ファイルを見て、環境ごと所属するグループをそれぞれ読み込みに行きます。下記のディレクトリ構成を例にすると、 webservers.ymldbservers.yml が環境ごとのグループでグループ名が等しくなっています。


ansible-playbook --inventory-file ./playbook/hosts/local ./playbook/webservers.yml


と実行すると、group_vars/production/weservers.yml で定義した変数は適用されずに group_vars/all.yml group_vars/local/all.yml group_vars/local/weservers.yml の 3 ファイルで定義した変数が適用されます。

...

│ ├── group_vars/
│ │ ├── all.yml
│ │ ├── local/
│ │ │ ├── all.yml
│ │ │ ├── webservers.yml
│ │ │ └── dbservers.yml
│ │ └── production/
│ │ ├── all.yml
│ │ ├── webservers.yml
│ │ ├── dbservers.yml
│ │ ├── tokyo.yml
│ │ └── oregon.yml
...

もし、 group_vars/local/webservers.ymlgroup_vars/production/webservers.yml の変数の定義が全く一緒の内容だった場合、group_vars/local/webservers.ymlgroup_vars/production/webservers.yml のどちらか片方を修正するたびにもう片方も修正しなければいけません。


そのような、二重管理を防ぐためには下記のようなディレクトリ構成にしておくことが望ましいでしょう。

...

│ ├── group_vars/
│ │ ├── all.yml
│ │ ├── webservers.yml
│ │ ├── local/
│ │ │ ├── all.yml
│ │ │ └── dbservers.yml
│ │ └── production/
│ │ ├── all.yml
│ │ ├── dbservers.yml
│ │ ├── tokyo.yml
│ │ └── oregon.yml
...

このように webservers.yml を外出しし group_vars/webservers.yml に配置することで、 local グループ及び production グループの webservers グループが変数を共用できるようになります。

group_vars 以下に、ただ単にグループ名の vars ファイルを配置するのではなくディレクトリを上手く使った構成にすることで管理のしやすさが格段に上がります。


設定例


group_vars/local/all.yml

---

# file: group_vars/local/all.yml
ansible_ssh_user: vagrant


group_vars/production/all.yml

---

# file: group_vars/production/all.yml
ansible_ssh_user: ubuntu


group_vars/production/tokyo.yml

---

# file: group_vars/production/tokyo.yml
aws_output: json
aws_region: ap-northeast-1
aws_access_key_id: XXXXXXXXXXXX
aws_secret_access_key: XXXXXXXXXXXX


group_vars/production/tokyo.yml

---

# file: group_vars/production/oregon.yml
aws_output: text
aws_region: us-west-2
aws_access_key_id: XXXXXXXXXXXX
aws_secret_access_key: XXXXXXXXXXXX


common_playbook


構成

上記の playbook- ../../common_playbook/roles/awscli と awscli の共用 roles を読み込み、 group_varsaws_region: ap-northeast-1 をはじめとした awscli の変数を定義しています。

今回は変数を上手く使いグループごとの切り分けを行っていますが、 when などをはじめとした様々なグループごとの切り分け方法があります。

...

│ └── roles/
│ ├── awscli/
│ │ ├── tasks/
│ │ │ └── main.yml
│ │ ├── handlers/
│ │ │ └── main.yml
│ │ ├── templates/
│ │ │ ├── config.j2
│ │ │ └── credentials.j2
...


設定例


common_playbook/roles/aws/tasks/main.yml

---

# file: common_playbook/roles/aws/tasks/main.yml
- name: install awscli
pip: name=awscli

- name: create directory /home/{{ ansible_ssh_user }}/.aws
file: path=/home/{{ ansible_ssh_user }}/.aws/ state=directory owner=root group=root mode=0755

- name: template /home/{{ ansible_ssh_user }}/.aws/config
template: src=config.j2 dest=/home/{{ ansible_ssh_user }}/.aws/config group=root owner=root mode=0644

- name: template /home/{{ ansible_ssh_user }}/.aws/credentials.j2
template: src=credentials.j2 dest=/home/{{ ansible_ssh_user }}/.aws/credentials group=root owner=root mode=0644



common_playbook/roles/aws/templates/config.j2

# common_playbook/roles/aws/templates/config.j2

[default]
output = {{ aws_output }}
region = {{ aws_region }}


common_playbook/roles/aws/templates/credentials.j2

# file: common_playbook/roles/aws/templates/credentials.j2

[default]
aws_access_key_id = {{ aws_access_key_id }}
aws_secret_access_key = {{ aws_secret_access_key }}


上記構成の設定例でできること


Vagrant


Vagrantfile を設定

# file: Vagrantfile

Vagrant.configure(2) do |config|
config.vm.box = "ubuntu/trusty64"
config.vm.define "webservers" do |webservers|
webservers.vm.hostname = "webservers"
webservers.vm.network "private_network", ip: "192.168.33.10"
webservers.vm.provision "ansible" do |ansible|
ansible.inventory_path = "./playbook/hosts/local"
ansible.playbook = "./playbook/webservers.yml"
end
end
config.vm.define "dbservers" do |dbservers|
dbservers.vm.hostname = "dbservers"
dbservers.vm.network "private_network", ip: "192.168.33.11"
dbservers.vm.provision "ansible" do |ansible|
ansible.inventory_path = "./playbook/hosts/local"
ansible.playbook = "./playbook/dbservers.yml"
end
end
end


Vagrant コマンド


インフラ全体を構成

初回の起動時のみプロビジョニングが走ります。

$ vagrant up


インフラ全体を再構成

二回目以降の起動時にプロビジョニングを走らせたい場合。

$ vagrant up --provision

起動中にプロビジョニングを走らせたい場合。

$ vagrant provision


webserver だけを再構成

$ vagrant provision webservers


ansible-playbook コマンド


インフラ全体を再構成

$ ansible-playbook --inventory-file ./playbook/hosts/production ./playbook/site.yml


webserver だけを再構成

$ ansible-playbook --inventory-file ./playbook/hosts/production ./playbook/webservers.yml 


tokyo だけを再構成

$ ansible-playbook --inventory-file ./playbook/hosts/production ./playbook/site.yml --limit tokyo


tokyo の webserver だけを再構成

$ ansible-playbook --inventory-file ./playbook/hosts/production ./playbook/webservers.yml --limit tokyo


備考

まとめ方が割と雑ですし追記していきたい部分も沢山あるので、少しずつ更新かけてより詳しく書いていく予定です。 ( 何より説明が分かりづらいww すみません。 )


参考 URL