前書き
サービスごとに複数のサーバを ansible で管理していく中で、同じような内容で共用したい role が沢山出てきました。それぞれサービス毎に管理していくのは非常に面倒…。困っていたところを下記の記事が救ってくれました。yteraokaさんには本当に感謝しています。
しかし、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さんのベストプラクティス
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 をインクルードしているだけです。
---
# file: site.yml
- include: webservers.yml
- include: dbservers.yml
../../common_playbook/vars/*
で共用の vars ファイルを読み込み、
../../common_playbook/roles/*
で共用の role を読み込みます。
hosts: webservers
で分かる通り hosts に webservers グループを指定します。
---
# 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 グループを指定します。
---
# 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.yml
と dbservers.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.yml
と group_vars/production/webservers.yml
の変数の定義が全く一緒の内容だった場合、group_vars/local/webservers.yml
と group_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 ファイルを配置するのではなくディレクトリを上手く使った構成にすることで管理のしやすさが格段に上がります。
設定例
---
# file: group_vars/local/all.yml
ansible_ssh_user: vagrant
---
# file: group_vars/production/all.yml
ansible_ssh_user: ubuntu
---
# file: group_vars/production/tokyo.yml
aws_output: json
aws_region: ap-northeast-1
aws_access_key_id: XXXXXXXXXXXX
aws_secret_access_key: XXXXXXXXXXXX
---
# 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_vars で aws_region: ap-northeast-1
をはじめとした awscli の変数を定義しています。
今回は変数を上手く使いグループごとの切り分けを行っていますが、 when などをはじめとした様々なグループごとの切り分け方法があります。
...
│ └── roles/
│ ├── awscli/
│ │ ├── tasks/
│ │ │ └── main.yml
│ │ ├── handlers/
│ │ │ └── main.yml
│ │ ├── templates/
│ │ │ ├── config.j2
│ │ │ └── credentials.j2
...
設定例
---
# 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
[default]
output = {{ aws_output }}
region = {{ aws_region }}
# 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 すみません。 )