Growi を構築する Ansible Playbook 作成
これまで雰囲気で使用していた Ansible についてもう少し整理された理解をしたかったので、単一ホストに Growi を構築する Playbook を作成してみました。以下はそこで得た理解をまとめたものです。
Growi インストールの手順は Growi のインストール on CentOS を参考にしました。
プロジェクトのディレクトリ構成
今回のプロジェクトは以下のようなディレクトリ構成にしました。これは Sample Ansible setup を元に必要ないものを省いた形になっています。
.
├── group_vars # Group 変数置場
│ ├── all.yaml
│ └── sample.yaml
│
├── hosts # Inventory ファイル
│
├── host_vars # Host 変数置場
│ └── growi.example.com.yaml
│
├── roles # Role 置場
│ ├── common # 共通 Role, 内部省略
│ │
│ ├── elasticsearch # Elasticsearch 用 Role, 内部省略
│ │
│ ├── growi # Growi 用 Role, 内部省略
│ │
│ ├── mongodb # MongoDB 用 Role, 内部省略
│ │
│ └── proxy # Reverse Proxy 用 Role
│ ├── defaults
│ │ └── main.yaml
│ ├── handlers
│ │ └── main.yaml
│ ├── tasks
│ │ └── main.yaml
│ └── templates
│ └── etc
│ ├── nginx
│ │ └── conf.d
│ │ └── growi.conf
│ └── yum.repos.d
│ └── nginx.repo
│
├── site.yaml # Playbook
プロジェクトの一番上の階層に注目すると以下の 4 種の Ansible リソースが含まれています。
- Inventory (
hosts
ファイル) - Role (
roles
ディレクトリ) - Playbook (
site.yaml
ファイル) - Variable (
group_vars
,host_vars
ディレクトリ)
Inventory
- Ansible でプロビジョニングしたい Host の一覧
- 各 Host は 1 以上の Group に所属される
- Host は必ず
all
という Group に含まれる - ユーザ独自に作成した Group にも所属できる
- Host は必ず
- 静的に定義する場合は環境ごとに作成されることが多そう (e.g. staging, production)
- プラグインを使用すれば動的に定義することもできる (ref. Working with dynamic inventory)
今回作成したプロジェクトでは hosts
ファイルで Inventory を定義しました。
[sample]
growi.example.com ansible_host=192.168.33.10
ここでは growi.example.com
という Host を all
と sample
Group に所属させています。
Host 名として直接 IP を記述することもできますが、それよりもわかりやすい名前をつけて ansible_host
で IP を指定する方が管理しやすいかと思います。
Role
- プロビジョニングしたい内容を記述する
- 例えば以下の要素で構成される
- Task
- 一連のプロビジョニング処理
- やりたい処理は Module とそれに渡す引数という形で記述する
- Handler
- プロビジョニング中に (別 Task から) notify された際に実行する Task を記述する
- デフォルトでは Handler は他の Task が終了したあとに実行される
- Template
- プロビジョニング中に使用する設定ファイル等のための Jinja2 テンプレート
- defaults
- Role のデフォルト変数
- Role で共通に使用する値や外から変更したい値を設定する
- Task
以下に今回 proxy Role で使用した Task を記載しました。
# roles/proxy/tasks/main.yaml
---
- name: Setup Nginx repository
ansible.builtin.template:
src: etc/yum.repos.d/nginx.repo
dest: /etc/yum.repos.d/nginx.repo
owner: root
group: root
mode: '0644'
- name: Install Nginx
ansible.builtin.yum:
name: nginx
enablerepo: nginx
state: present
# ... (省略)
- name: Add Nginx conf
ansible.builtin.template:
src: etc/nginx/conf.d/growi.conf
dest: /etc/nginx/conf.d/growi.conf
owner: root
group: root
mode: '0644'
notify: Restart Nginx
- name: Start Nginx
ansible.builtin.service:
name: nginx
state: started
enabled: yes
Module というのはここでいう ansible.builtin.template
や ansible.builtin.yum
のことで、このように Ansible では Module を選択することでやりたい処理を記述します。
Template は ansible.builtin.template
Module を使用して、予め用意した Jinja2 テンプレートファイルを元にプロビジョニング先のホストにファイルを配置する仕組みです。ここでは Yum のリポジトリファイルや Nginx の設定ファイルを用意するために使用しています。
Handler はある Task を実行して何らかの変更が加えられた (changed が返された) ときにだけ処理を実行するための仕組みです。ここでは Add Nginx conf
Task で Restart Nginx
Task に notify することで、Nginx の設定ファイルを書き換えたときに Nginx をリスタートするようにしています。
# roles/proxy/handlers/main.yaml
---
- name: Restart Nginx
service:
name: nginx
state: restarted
Role をどういった単位でまとめるべきか自分の中で整理できていないのですが、最低限意味のある単位で細かく分けるのがいいのかなと考えています。今回は Elasticsearch, MongoDB, proxy (Nginx), Growi のようにミドルウェア毎に Role を分けてみました。各サービスのホスト、ポートを変数として共有できれば全ミドルウェアを単一ホストにインストールすることも、DB だけ別のホストにインストールする、といったこともできそうなので程良い粒度ではないかと思っています。
Playbook
Ansible における Playbook というのはけっこう意味が広いというか「Playbook を適用する」みたいなことはよく言うけど具体的に Playbook で何を指しているのかわかりづらいという印象でした。ただ Intro to playbooks をちゃんと読んで自分がわかりにくく感じているのは Playbook として何か画一的なものをイメージしているからであって、実際は「Group と (Role を介した間接的な) Task の結びつけ」を Playbook と呼べばいいのかなと思いました。
なので以下のような Group と Task をひとまとめにした形も、
---
- name: update web servers
hosts: webservers
remote_user: root
tasks:
- name: ensure apache is at the latest version
yum:
name: httpd
state: latest
- name: write the apache config file
template:
src: /srv/httpd.j2
dest: /etc/httpd.conf
- name: update db servers
hosts: databases
remote_user: root
tasks:
- name: ensure postgresql is at the latest version
yum:
name: postgresql
state: latest
- name: ensure that postgresql is started
service:
name: postgresql
state: started
あるいは今回のプロジェクトで採用したように Group と Role を結びつけたような形も、
# site.yaml
---
- hosts: sample
roles:
- common
- mongodb
- elasticsearch
- growi
- proxy
become: True
一見別のフォーマットに見えますがどちらもこれを使用して ansible-playbook
コマンドで実行できる Playbook ということなんだと思います。というか試してみたら後者のファイルでも tasks
を生やして Task を記述できたので結局フォーマットも同じですね。ちょっとしたプロビジョニングなら前者のように直接 Task を書けばいいし、複雑なものになってくると Role を使用した後者のような形で書きたくなるというだけの話っぽいです。
Variable
Using Variables に Playbook で設定できる箇所や優先度についてまとめられています。 今回のプロジェクトでは以下のような箇所では変数を管理しました。
- Role のデフォルト値として設定する (e.g.
roles/proxy/defaults/main.yaml
ファイル) - Group に設定する (
group_vars
ディレクトリ) - Host に設定する (
host_vars
ディレクトリ) - Runtime 時に (CLI の
--extra-vars
オプションとして) 設定する
また Runtime 時の設定でいうと環境変数を {{ lookup('env', 'PWD') }}
のように参照することもできます。