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

  • 179
    いいね
  • 1
    コメント

前書き

サービスごとに複数のサーバを 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