Edited at

Ansibleまとめ

More than 1 year has passed since last update.

リファレンスを翻訳して読み解いたり、他の方の記事を見ていて結びついた知識をなるべくわかりやすくまとめていきます。


1. playbook

リモートマシンに対する処理を手続きとして書くファイル。

playbookは非同期でも同期でも実行できる。


1.1. playbookの実行

実行コマンドの基本は以下の形。

ansible-playbook -i <インベントリファイルのパス> <playbookのパス>

その他、主なオプションは以下の通り。

オプション
効果

--list-hosts
コマンド実行時に対象となるホスト名が表示される

--syntax-check
playbookの構文チェック(対象のplaybookのみであり、
include,importされるものは対象でなく、
インベントリファイルも対象でないため意外と使う場面が謎)

-C
ドライラン。処理が成功したらこうなるよという変化を表示してくれる。
ドライランは実際の処理の成功を保証するものではないことに注意
(実際に実行してみたらアクセス権限に問題があって無理でしたーとかがある。)

-v
実行結果をより詳細に表示してくれる。vの数が多くなるほど詳細に(限界は-vvv)


1.2. playbookの基本構造

playbookはplay領域task領域から成り立つ。play領域はtask領域を含む。

task領域には具体的な処理を記述する。

play領域の先頭でリモートホストで実行するユーザーだったり、対象のホスト(通常は後述のインベントリファイル内で定義したグループ名を記載)だったりを指定する。

2018y05m14d_193140732.jpg


1.3. include

playbook内で別の場所にあるplaybookもインクルードできるし、taskもインクルードできる。

# playbookとtask双方のインクルードを行うplaybookの例

- include: <playbook名>
tasks:
- include: <taskファイル名(※)>

taskファイルは、task領域だけを記述したファイル。上の例で言えば以下のような内容のファイル。

# sample_task_file.yml

- name:httpd をインストール
yum: name=httpd state=latest
...

ファイルをインクルードすると、そのファイルの内容がその場にベタッと貼り付けられるイメージ(インクルード先のファイル内のインデントは考慮されるらしい)。

# sample_task_file.yml をインクルード

- tasks:
include: sample_task_file.yml

この場合は以下のような形で動作する。

- tasks:

- name:httpd をインストール
yum: name=httpd state=latest
...

includeした行のインデントを基準にベタッと貼り付けられる。


1.4. handler

handlerは一連のtasksの処理を実行する中で特定のtaskが実際に実行された(処理結果でいうところのchangedになった)ときに、一連のtasksの処理の終了後に一度だけ処理を実行するという機能。変更した設定を読み込ませるためのプロセスの再起動が主な使い所の例か。


書き方

changedになったときにどのhandlerを実行するtaskにおいて、どのhandlerを実行するのかを指定(notify: )しておく必要がある。

具体的には以下のような書き方をする。

- tasks

- name: task01
<なんかの処理>
notify: handler01

- name: task02
<なんかの処理>
notify: handler02

- handlers
- name: handler01
<なんかの処理>

- name: handler02
<なんかの処理>

task01が実際に実行されたならば、task02完了後にhandler01が実行される。

task01, task02が実際に実行されたならば、task02完了後にhandler01, handler02が実行される。


1.5. role

roleは、以下のようなディレクトリ・ファイル構造にしてやると、分割した各ファイルをいい感じに読んで実行してくれるという機能。

tasks/, handlers/共にmain.ymlは必須である。なお、それぞれのディレクトリは不要であるものは存在しなくてよい(role機能を使うにはどれか最低ひとつディレクトリが存在している必要あり)。

site.yml

インベントリファイル
roles/ (インベントリファイルと同階層に置く)
|
+--common/ # ロールの例であり、特にこの名前でなければならないわけではない
|
+-- tasks/ # roleの核となるtaskファイルの置き場。
| |
| +-- main.yml # 必須
| +-- ◯◯.yml
+-- handlers/ # tasks/main.ymlの実行が終わったあとに1回だけ実行されるtaskファイルの置き場
| +-- main.yml
| +-- ★★.yml
+-- files/ # このroleでデプロイするファイルの置き場
|
+-- templates/ # このroleでデプロイするテンプレートファイルの置き場
|
+-- vars/ # このロールのその他変数ファイル置き場 (現状、優先順位不明)
| |
| +-- main.yml # ここに変数を書く
|
+--defaults/ # このロールのデフォルト変数置き場 (後述のグループ変数, ホスト変数の方が強い)
| |
| +-- main.yml # ここに変数を書く
|
+--meta/ # このロールのメタデータ定義

各playbook、taskファイルが以下の状態である場合を仮定する。

site.yml

- hosts: webservers

become: yes
roles:
- common

tasks/main.yml

- include: ◯◯.yml

tasks/◯◯.yml

- name: task◯◯

yum: name=httpd state=latest
notify: handler★★

handlers/main.yml

- include: ★★.yml

handlers/★★.yml

- name: handler★★

service: name=httpd state=restarted

これをansible-playbook -i インベントリファイル site.ymlと実行した場合、実行順は以下のような感じ。


  1. task/main.yml

  2. task/◯◯.yml (task/main.ymlにインクルードされる)

  3. handlers/main.yml

  4. handlers/★★.yml (handlers/main.ymlにインクルードされる)

挙動としては、site.ymlのrole: が以下のように置き換わる模様。

site.yml

- hosts: webservers

become: yes
tasks:
- include: tasks/main.yml
handlers:
- include: handlers/main.yml

個人的な所感としてrole機能を利用する場合には以下の利点があると思う。


  • "- tasks" や "- handlers" の記述が必要なくすっきりする

  • 処理を階層的に書き分けられる


    • 大工程:role名

    • 中工程:tasks/,handlers/のmain.yml以外のファイル名

    • 小工程:tasks/,handlers/のmain.yml以外のファイルに記載する各taskの"name"



  • ファイル・ディレクトリ構成に条件があることで、どのディレクトリにどういったものがあるというイメージができるため、他の人が書いたAnsibleソースが読みやすい。


2. インベントリファイル


  • ansibleを実行するリモートホストを設定するファイル。インベントリファイル上でリモートホストはグループとしてまとめておくことができる。

  • ansible-playbookコマンド実行時に-iオプションで対象のインベントリファイルのパスを指定する。


    • 何も指定しない場合、/etc/ansible/hostsというパスで読もうとする。



  • INI形式の他、実はYAML形式でもかけるがINI形式が主流の模様(というかYAML形式のインベントリファイル書いている人みたことない)。


2.1. グループ

例えば、以下のINI形式で記載したインベントリファイルがあるとする。

server01

[webservers]
webserver01 # 実はここは、
webserver02 # webserver[01:02]とも書ける

[dbservers]
dbserver01 # 実はここは、
dbserver02 # dbserver[01:02]とも書ける

[webdbservers:children]
webservers
dbservers

このとき、それぞれのリモートホストとグループは以下のような関係になる。

2018y05m14d_161046040.jpg

例えば、dbserver01はdbserversにもwebdbserversにもallにも所属していることになる。


2.1.1. all, ungroupedグループ

デフォルトで暗黙的に存在するグループ。group_namesのようなグループリストには表示されない。



  • allグループ:全てのホストが所属するグループ


  • ungroupedグループ:ユーザーによって明示的にグループへの紐付けがされていない全てのホストが所属するグループ


2.1.2. グループ名

システムの分類やいつ何の目的で管理するのかという体で記載する。


2.1.3. グループの入れ子(childrenの利用)

グループにはさらにグループを所属させることができる。以下のように記述する。

  [<親グループ名>:children]

<子グループ名>
<子グループ名>
...


2.1.4. インベントリファイル内のグループ変数、ホスト変数

以下のようにインベントリファイル内でグループ単位、ホスト単位で変数を定義することができる(それぞれグループ変数、ホスト変数と呼ばれる)。なお、インベントリファイル内での変数定義は非推奨となっており、group_varsで指定するのがよいとされている。

server01

[webservers]
webserver01 a=1 b=2 # ホスト変数定義
webserver02 a=2 b=3 # ホスト変数定義

[dbservers]
dbserver01
dbserver02

# グループの入れ子
[webdbservers:children]
webservers
dbservers

# グループ変数定義
[webserbers:vars]
a=5
b=6

[dbservers:vars]
a=6
b=7

[webdbservers:vars]
a=7
b=8

[all:vars]
a=8
b=9

グループ変数はAnsibleの実行時に最終的に各ホストに適用されるのだが、同じ変数名がある場合上書きされていく。優先度は以下の通り。



  • [最高] 当該ホスト変数

  • 当該ホストが直接所属するグループのグループ変数

  • 当該ホストが直接所属するグループが所属するグループのグループ変数

  • 当該ホストが直接所属するグループが所属するグループが所属する(以下略)


  • [最低] allグループのグループ変数

つまり、自分により近い変数値が優先されるということ。

上記の仕様を踏まえ、各ホストに適用される変数値は以下の通り。


  • server01:a=8, b=9

  • webserver01:a=1, b=2

  • webserver02:a=2, b=b

  • dbserver01:a=6, b=7

  • dbserver02:a=6, b=7

ホスト変数とグループ変数では書き方が違うことにも注意。

書き方を一般化すると以下のような形。

グループ変数

グループ自体の定義([<グループ名>]や[<グループ名>:children])とは別に記述しなければならないことにも注意。

[<グループ名>:vars]

<変数名>=<変数値>

ホスト変数

<ホスト名> <変数名>=<変数値>


2.1.5. グループ変数、ホスト変数の外出し

グループ変数自体の解説は先述の通り。インベントリファイルに直接書かず、各グループ毎のファイルに分ける方法と制約(Ansible公式推奨)は以下の通り。

制約として、以下のどちらかのファイル構成でなければ各グループ変数、ホスト変数は読み込まれない

= インベントリファイルと同階層にgroup_varsディレクトリが存在し、配下にインベントリファイル内で定義したグループ名、ホスト名と同名のファイルもしくはディレクトリが存在。

----- インベントリファイル(内部でwebservers, dbserversというグループとwebserver01というホストが定義されているとする)

|
+-- group_vars/(グループ変数管理ディレクトリ。名前はコレでないとダメ、インベントリファイルと同階層にないとダメ)
| |
| +-- webservers(ファイル群。名前はインベントリファイル内に定義したグループ名or all, ungroupedでなければダメ
| +-- dbservers 拡張子をつける場合は.yml or .yaml or .json でなければダメ)
| +-- all
| +-- ungrouped
+-- host_vars/(ホスト変数管理ディレクトリ。名前はコレでないとダメ、インベントリファイルと同階層にないとダメ)
|
+-- webserver01

もしくは以下(上記構成で各グループの変数定義が肥大化した場合にどうぞ、とはAnsible公式の弁)

----- インベントリファイル(内部でwebservers, dbserversというグループが定義されているとする)

|
+-- group_vars/(グループ変数管理ディレクトリ。名前はコレでないとダメ、インベントリファイルと同階層にないとダメ)
| |
| +-- webservers/(ディレクトリ。名前はインベントリファイル内に定義したグループ名or all, ungroupedでなければダメ)
| | |
| | +任意のファイル名 (拡張子をつける場合は.yml or .yaml or .json でなければダメ)
| | +任意のファイル名
| +-- dbservers/
| | |
| | +任意のファイル名
| | +任意のファイル名
| +-- all/
| | |
| | +任意のファイル名
| | +任意のファイル名
| +-- ungrouped/
| |
| +任意のファイル名
| +任意のファイル名
|
+-- host_vars/(ホスト変数管理ディレクトリ。名前はコレでないとダメ、インベントリファイルと同階層にないとダメ)
|
+-- webserver01/
|
+任意のファイル名

なお、変数定義が不要なグループであればそのグループのファイル・ディレクトリは存在しなくてOK。つまり全てのグループについてファイル、ディレクトリを用意する必要はない。

外出ししたグループ変数ファイル、ホスト変数ファイルの中身は以下のようになる。

(インベントリファイル内で定義していたグループ変数の体裁ともホスト変数の体裁とも異なることに注意)

<変数名>: <変数値>

具体的にいえば、以下のような形

a: 1

b: 2
xyz: 5


3. 変数


変数名

Ansibleでは、変数名に文字・数字・アンダースコアを使うことができる。

ただし、変数名の先頭は文字から始まり、先頭および末尾に"__"(アンダースコア2つ)を使うことはできない。


変数の参照

以下のようにする。

{{ <変数名> }}


連想配列

いわゆるマップやハッシュと呼ばれるものでキー、値の組を持つもの。

Ansibleでは以下のように定義する。

<変数名>

<キー>: <値>
<キー>: <値>

具体的には以下の通り

foo:

field1: "one"
field2: "two"

値の参照は以下のようにカッコ記法で行う。

foo['field1']

なお、以下のようドット記法でも参照できるが、pythonの予約後と衝突する可能性があり(Ansible公式より)おすすめはできない。

foo.fileld1