【Ansible備忘録２】「import_role」を使う

# 前回
https://qiita.com/pon1202/items/1b9eef884f6a07e1fc67

# 前書き
ansibleのハンズオン例を色々と調べていると、「**やりたいタスクをplaybookに直接記載する方法**」をよく目にします。
ansibleに慣れるまではその方がシンプルで分かりやすいと思いましたが、実務でansibleを使う場合は管理のしやすさやミスを防ぐという点であまり使用されないのかなと思います。
（実際、私がそうです。。）

**playbook.yml**にやりたいタスクを直接記述するのではなく、実行したいタスクごとに**role**を作成し、そのrole内にやりたいタスクを記述する方法を知ったので、簡単にその方法を書き連ねたいと思います。

# 検証環境
### 検証環境構築について
Dockerを使用して検証環境を構築しました。
ansible環境の構築に関しては下記ページを参考にしております。
（OS等は下記ページをご覧ください。）
https://qiita.com/Shoma0210/items/7d7d24d7c3f95f19b427

### 環境概要
##### ▼ノード
```
<コントロールノード>
ansible_main

<ターゲットノード>
ansible_test01
ansible_test02
ansible_test03
```
⇒コントロールノード（ansible_main）でplaybook.ymlを実行したら、そのplaybook.ymlに記載されているタスクは、ターゲットノード（ansible_test01,02,03）にて実行される。

##### ▼ディレクトリ構造
```ruby:/root/work配下(コントロールノードのみ)
$ tree
.
|-- ansible_playbook_apache.yml
|-- ansible_playbook_debug.yml
|-- hosts
|-- roles
    |-- role_apache
    |   `-- tasks
    |       `-- main.yml
    |-- role_debug
    |   `-- tasks
    |       `-- main.yml
    |-- role_debug_2
        `-- tasks
            `-- main.yml

7 directories, 6 files
```
⇒今回、主に使用するのは以下となります。
`ansible_playbook_debug.yml`
`roles/role_debug/tasks/main.yml`
`roles/role_debug_2/tasks/main.yml`
`hosts`



# 内容解説（import_role）
今回は簡単な例として、
>**・ansible_test01と02には、“Hello,World!”というメッセージを出力させる。**
>**・ansible_test03には、"Unnecessary"というメッセージを出力させる。**

という処理を実行するようにしました。
（この程度のタスクならわざわざroleに分割しなくても良いと思いますが、あくまで例なので悪しからず。。）

### 処理の流れ

処理の流れとしては、以下の通りになります。（①以外は全て自動で行われます。）
>①コントロールノード（ansible_main）にて **playbook（ansible_playbook_debug.yml）** を実行する。
>②①により、**ansible_playbook_debug.yml**に記載されているタスクが上から順に実行される。
>③ansible_playbook_debug.ymlには「**import_role**」というモジュールが使われている為、「**roles**」ディレクトリ配下にあるroleを読み取りに行く。
　→今回は「role_debug」と「role_debug_2」というロールを使用
>④使用するroleディレクトリ配下にある「**tasks/main.yml**」に記載されたタスクが実行される。
>⑤④の実行結果が自動的に出力される。

### ①&②playbookについて
今回使用するplaybookである**ansible_playbook_debug.yml**は下記のような内容になっています。
```ruby:ansible_playbook_debug.yml
---
- hosts: all
  become: yes
  tasks:
  - name: Play Debug role
    import_role:
      name: role_debug
    tags:
     - debug
    when: "'debug' in group_names"

  - name: Play Debug2 role
    import_role:
      name: role_debug_2
    tags:
     - debug_2
    when: "'debug_2' in group_names"
```
上から順に実行されていくので、タスクとしては最初に「**Play Debug role**」という名称のタスクが実行されます。

その中身を見てみると、
「**import_role**」というモジュールを使用しており、「role_debug」というロールを読み取りに行くように設定されています。
この設定により、role_debugディレクトリ配下で設定（定義）されているタスク等が実行されるようになります。

また、その下には「**tags: - debug**」と「**when: "'debug' in group_names"**」という記載があります。

<br>

#### tags

「**tags**」は、これを設定しておくことで、playbook実行時に「特定のタスクのみを実行する」という使い方が出来るようになります。
今回であれば、playbook実行時に下記のように指定することで、「Play Debug role」内に記載されたタスクのみを実行します。（→role_debugを読み取りに行く。）

```ruby:tags debugを指定して実行
$ ansible-playbook --tags debug ansible_playbook_debug.yml

PLAY [all] *************************************************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************************
ok: [ansible_test03]
ok: [ansible_test02]
ok: [ansible_test01]

TASK [role_debug : TEST Message] ***************************************************************************************************************************
ok: [ansible_test01] => {
    "msg": " Hello,World! "
}
ok: [ansible_test02] => {
    "msg": " Hello,World! "
}
skipping: [ansible_test03]

PLAY RECAP *************************************************************************************************************************************************
ansible_test01             : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
ansible_test02             : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
ansible_test03             : ok=1    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0
```


<br>
<br>

#### when

「**when**」は、文字通り「実行する場合の条件」を表します。
今回は
>**when: "'debug' in group_names"**

と記載されているので、
「**"debug"というグループに含まれているサーバのみ実行する**」ということになります。

このグループの定義は「**hosts**」というファイル内で定義されています。
インベントリファイルと呼ばれるやつですね。
今回、インベントリファイルは下記のように設定されています。

```ruby:hosts
[node]
ansible_test01
ansible_test02
ansible_test03

[debug]
ansible_test01
ansible_test02

[debug_2]
ansible_test03
```
**[debug]** の配下にansible_test01と02が記載されています。

今回の例で言うと、
「Play Debug role」というタスクには、「when: "'debug' in group_names"」という条件が付いているので、
「Play Debug role」を実施するサーバ（ノード）は**ansible_test01とansible_test02**のみになります。

また、**ansible_test03**は、**[debug_2]** に含まれており、[debug]には含まれていないサーバになるので、[debug]グループに対して実行されるタスクは**スキップ**されます。
上述の実行結果（tags debugを指定して実行）でも、**ansible_test03**は「skipping」となっています。


このようにwhen句とインベントリファイルを組み合わせて使うことで、実行対象を絞ることも可能になります。
（勿論、when句・インベントリファイルともに他の使い方も出来ます。）



### ③&④roleについて
>①コントロールノード（ansible_main）にて playbook（ansible_playbook_debug.yml） を実行する。
②①により、ansible_playbook_debug.ymlに記載されているタスクが上から順に実行される。
**③ansible_playbook_debug.ymlには「import_role」というモジュールが使われている為、「roles」ディレクトリ配下にあるroleを読み取りに行く。
　→今回は「role_debug」と「role_debug_2」というロールを使用
④使用するroleディレクトリ配下にある「tasks/main.yml」に記載されたタスクが実行される。**
⑤④の実行結果が自動的に出力される。


<br>

#### role

「**roles**」ディレクトリには複数のロールを作成することが出来ます。
（今回は下記の通りroleは3つ）
```ruby:role
|-- roles
    |-- role_apache
    |   `-- tasks
    |       `-- main.yml
    |-- role_debug
    |   `-- tasks
    |       `-- main.yml
    |-- role_debug_2
        `-- tasks
            `-- main.yml
```

このように複数のロールを作成できることはいくつかのメリットがあると思います。

例えば、MWごとにroleを作成することで、下記のメリットが挙げられます。
>・MWごとに実施するタスク等の管理が楽になる
>・各MWのタスクが分別される為、意図しない動作を防げる
...

「**apacheについてはrole_apache、tomcatについてはrole_tomcat**」
という具合に分かれている方が明示的で分かりやすいですよね。

もし、roleを使わずにplaybookに各々のMWのタスクを全量記載した場合、とんでもない記述量になりますし、修正・加筆する際も労力がかかります。

もし、roleを使わずにplaybookに各々のMWのタスクを全量記載した場合、修正・加筆の際、別のMWの記述箇所をイジってしまう危険性もあります。

<br>
<br>

作業や管理をする上で、「**一目で分かる**」というのは非常に大事だと思いますし、何よりも「**ヒューマンエラー**」を防がなければなりません。
物量が多くなったり、いくつもの要素が複雑に織り交ざったりすると、人間は見落としてしまうので、playbookに直接記入せず、roleに分けて管理する方が良いのかなと思います。

<br>

#### tasks/main.yml

各roleの中身にはいくつかのディレクトリが存在します。
前回の【Ansible備忘録1】でも紹介した「**handler**」も各role配下のディレクトリで設定できます。
（他にも「default」や「template」、「files」などなど…）

今回はタスクの記載に関するお話なので、「**tasks/main.yml**」に関してのみ書き記します。

roleを使用する場合、**tasks/main.ymlに記載した処理（タスク）がplaybook実行時に実施されます**。

>playbook実行
↓
playbook内のimport_roleで定義されているroleへ
↓
そのroleのtasks/main.ymlに記載されたタスクを実施

流れとしてはこんな感じですかね。

<br>
<br>

今回は
>**・ansible_test01と02には、“Hello,World!”というメッセージを出力させる。**
>**・ansible_test03には、"Unnecessary"というメッセージを出力させる。**

というタスクを行う為、tasks/main.ymlは下記のようになります。
```ruby:roles/role_debug/tasks/main.yml
---
- name: "TEST Message"
  debug:
    msg: ' Hello,World! '
```

```ruby:roles/role_debug_2/tasks/main.yml
---
- name: "Debug_Message_2"
  debug:
    msg: ' Unnecessary '
```

勿論、debug以外にも各種モジュールは使用可能です。



### ⑤実行結果
上述した内容を実行すると下記の結果となります。

```ruby:タグ指定なしの実行結果
$ ansible-playbook ansible_playbook_debug.yml

PLAY [all] *************************************************************************************************************************************************
TASK [Gathering Facts] *************************************************************************************************************************************
ok: [ansible_test02]
ok: [ansible_test03]
ok: [ansible_test01]

TASK [role_debug : TEST Message] ***************************************************************************************************************************
ok: [ansible_test01] => {
    "msg": " Hello,World! "
}
ok: [ansible_test02] => {
    "msg": " Hello,World! "
}
skipping: [ansible_test03]

TASK [role_debug_2 : Debug_Message_2] **********************************************************************************************************************
skipping: [ansible_test01]
skipping: [ansible_test02]
ok: [ansible_test03] => {
    "msg": " Unnecessary "
}

PLAY RECAP *************************************************************************************************************************************************
ansible_test01             : ok=2    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0
ansible_test02             : ok=2    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0
ansible_test03             : ok=2    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0
```

ページ下部に各種設定の記載例及び実行結果（タグ別）を載せております。ご参考までに。




# 記載例
```ruby:ansible_playbook_debug.yml
---
- hosts: all
  become: yes
  tasks:
  - name: Play Debug role
    import_role:
      name: role_debug
    tags:
     - debug
    when: "'debug' in group_names"

  - name: Play Debug2 role
    import_role:
      name: role_debug_2
    tags:
     - debug_2
    when: "'debug_2' in group_names"
```

```ruby:roles/role_debug/tasks/main.yml
---
- name: "TEST Message"
  debug:
    msg: ' Hello,World! '
```

```ruby:roles/role_debug_2/tasks/main.yml
---
- name: "Debug_Message_2"
  debug:
    msg: ' Unnecessary '
```

```ruby:hosts
[node]
ansible_test01
ansible_test02
ansible_test03

[debug]
ansible_test01
ansible_test02

[debug_2]
ansible_test03
```

# 実行結果
```ruby:（１）タグ指定なしの実行結果
$ ansible-playbook ansible_playbook_debug.yml

PLAY [all] *************************************************************************************************************************************************
TASK [Gathering Facts] *************************************************************************************************************************************
ok: [ansible_test02]
ok: [ansible_test03]
ok: [ansible_test01]

TASK [role_debug : TEST Message] ***************************************************************************************************************************
ok: [ansible_test01] => {
    "msg": " Hello,World! "
}
ok: [ansible_test02] => {
    "msg": " Hello,World! "
}
skipping: [ansible_test03]

TASK [role_debug_2 : Debug_Message_2] **********************************************************************************************************************
skipping: [ansible_test01]
skipping: [ansible_test02]
ok: [ansible_test03] => {
    "msg": " Unnecessary "
}

PLAY RECAP *************************************************************************************************************************************************
ansible_test01             : ok=2    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0
ansible_test02             : ok=2    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0
ansible_test03             : ok=2    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0
```


```ruby:（2）tags debugを指定して実行
$ ansible-playbook --tags debug ansible_playbook_debug.yml

PLAY [all] *************************************************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************************
ok: [ansible_test03]
ok: [ansible_test02]
ok: [ansible_test01]

TASK [role_debug : TEST Message] ***************************************************************************************************************************
ok: [ansible_test01] => {
    "msg": " Hello,World! "
}
ok: [ansible_test02] => {
    "msg": " Hello,World! "
}
skipping: [ansible_test03]

PLAY RECAP *************************************************************************************************************************************************
ansible_test01             : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
ansible_test02             : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
ansible_test03             : ok=1    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0
```


```ruby:（3）tags debug_2を指定して実行
$ ansible-playbook --tags debug_2 ansible_playbook_debug.yml

PLAY [all] *************************************************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************************
ok: [ansible_test02]
ok: [ansible_test03]
ok: [ansible_test01]

TASK [role_debug_2 : Debug_Message_2] **********************************************************************************************************************
skipping: [ansible_test01]
skipping: [ansible_test02]
ok: [ansible_test03] => {
    "msg": " Unnecessary "
}

PLAY RECAP *************************************************************************************************************************************************
ansible_test01             : ok=1    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0
ansible_test02             : ok=1    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0
ansible_test03             : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
```


# 終わりに
実務での経験をベースに出来るだけ分かりやすくアウトプットしてみました。
執筆にあたり色々とimport_roleについて調べたところ、どうやら「roles」という記述でも「import_role」と同様の動きが出来そう。。。
流石にそれらの比較までは手が回らなかったので、今回は「import_role」についてのみを***備忘録*として書き記しました。

自動化は非常に便利ですが、管理するのは結局のところ**人間**なので、今回のrole構造のような仕組みを用いて整理することは大事なのかなと思います。