Ansibleタグクイズ

# **はじめに**

全世界 ~~60億人~~ の Ansible ユーザのみなさま、こんにちは。
今回は皆さんが慣れ親しんでいるであろう **タグ機能** についてクイズを出していきたいと思います。

# **環境情報**

クイズは下記の環境から出題します。今流行の **Ansible 2.0** ですのでご注意下さい。

## **各種バージョン**

```
[root@controller ~]# cat /etc/centos-release
CentOS release 6.7 (Final)

[root@controller ~]# python --version
Python 2.6.6

[root@controller ~]# ansible --version
ansible 2.0.0.2
  config file = /etc/ansible/ansible.cfg
  configured module search path = Default w/o overrides
```

## **Ansible構成**

playbook その他はこの構成をベースとします。
変更する場合は変更箇所が分かる形で明記しますが、言及のない部分はこの状態となっているとご認識ください。

```
ansible/
├─roles/
│　├─role-hoge/
│　│　└─tasks/
│　│　   ├─main.yml
│　│　   └─hoge.yml
│　└─role-fuga/
│　 　├─meta/
│　 　│  └─main.yml
│　 　└─tasks/
│　　    └─main.yml
└─main.yml
```

### **ansible/**

ansible-playbook コマンドの実行対象、`ansible/main.yml` がいます。

```:ansible/main.yml
- hosts: targets
  user: root
  tasks:
    - debug: msg="play"
```

基本的なクイズを出題をした後、この playbook から role を読ませて行ったりします。

### **role-hoge/**

単純にメッセージを返すだけの role です。
ここにタスクを追加したりタグをつけたりして role から見たタグの基本的な動きについて出題します。

```:role-hoge/tasks/main.yml
- debug: msg="hoge"
```

### **role-fuga/**

role-fuga は role-hoge に対して dependency を持ちます。

```:role-fuga/tasks/main.yml
- debug: msg="fuga"
```

```:role-fuga/meta/main.yml
dependencies:
  - role-hoge
```

# **クイズ本編**

クイズは全5問です。
個々の問題だけ見ても分かるかもしれませんが、一応上から順に解いていくことを想定しています。

## **1. タグの指定**

### **問題**

playbook を少し変更します。**play** というタグを付けました。

```:ansible/main.yml(抜粋)
  tasks:
    - debug: msg="play"
      tags: play
```

**Q1. 下記それぞれのコマンドで `debug: msg="play"` タスクは実行されるか**

```text:Q1-1
[root@controller ~]# ansible-playbook main.yml
```

```text:Q1-2
[root@controller ~]# ansible-playbook main.yml --tags
```

```text:Q1-3
[root@controller ~]# ansible-playbook main.yml --tags all
```

```text:Q1-4
[root@controller ~]# ansible-playbook main.yml --tags play
```

```text:Q1-5
[root@controller ~]# ansible-playbook main.yml --tags PLAY
```

### **解答**

さて、いかがでしょうか。

**A1. Q1-1, Q1-3, Q1-4 で実行される**

Q1-2ではコマンド実行時の引数エラーとなります。

```
[root@controller ansible]# ansible-playbook main.yml --tags
Usage: ansible-playbook playbook.yml

ansible-playbook: error: --tags option requires an argument
```

また、 Q1-5 はエラーとはなりませんが、 `debug: msg="play"` は実行されません。

### **解説**

タスクに対して **tags 属性** を記述することで、タスクにタグ付けすることができます。
タグ付けされたタスクは ansible-playbook コマンド実行時に **--tags** オプションで対象のタグを指定することで実行されるようになります。
--tags オプションを指定しない場合、 **暗黙に全てのタスクが動く** ようになっていますが、これをオプションで明示的に指定する場合は **all** タグを指定します。
また、Q1-5の結果からわかるように、 **タグは大文字/小文字を区別** します。

## **2. 複数タグの指定**

### **問題**

タスクに複数のタグを付けるにはどうしたらいいでしょうか。

```
[root@controller ~]# ansible-playbook main.yml --tags play1
[root@controller ~]# ansible-playbook main.yml --tags play2
[root@controller ~]# ansible-playbook main.yml --tags play3
```

**Q2. 上記のうち、 play1 と play2 タグを指定したコマンドだけで `debug: msg="play"` タスクが実行されるようにしたいとき、正しいタグの付け方はどれか**

```text:Q2-1
  tasks:
    - debug: msg="play"
      tags: play1, play2
```

```text:Q2-2
  tasks:
    - debug: msg="play"
      tags: { play1, play2 }
```

```text:Q2-3
  tasks:
    - debug: msg="play"
      tags: [ play1, play2 ]
```

```text:Q2-4
  tasks:
    - debug: msg="play"
      tags:
        - play1
        - play2
```

### **解答**

**A2. Q2-2 を除く全て**

Q2-2では構文エラーとなります。

```
[root@controller ansible]# ansible-playbook main.yml --tags play1
ERROR! tags must be specified as a list

The error appears to have been in '/ansible/main.yml': line 5, column 13, but may
be elsewhere in the file depending on the exact syntax problem.

The offending line appears to be:

    - debug: msg="play"
      tags: { play1, play2 }
            ^ here
```
### **解説**

タスクに対して複数のタグを付けたい場合、 tags 属性に対して **カンマ区切り** または **シーケンス** でタグを与えることができます。
カンマ区切りだと指定されている個々のタグが見えづらくなりますので、 Q2-4 のように複数タグを指定する場合は **改行を用いたシーケンスを使う** のが個人的にはよいと思います。
ちなみに問題でわざわざ play3 まで出しましたが、単純に play1 と play2 で動けばいいのであれば **always** タグを使うことも可能です。

## **3. ロールへのタグ付け**

### **問題**

role-hoge を main.yml に導入します。

```:ansible/main.yml(抜粋)
  tasks:
    - debug: msg="play"
  
  roles:
    - role: role-hoge
```

**Q3. role-hoge が下記のように play-hoge タグ指定時に実行されるタグ付けはどれか**


```
[root@controller ansible]# ansible-playbook main.yml --tags play-hoge
```

```text:Q3-1
  roles:
    - role: role-hoge
      tags: 
        - play-hoge
```

```text:Q3-2
  roles:
    - { role: role-hoge, tags: [ play-hoge ] }
```

```text:Q3-3
  roles:
    - { role: role-hoge, tags=play-hoge }
```

### **解答**

**A3 Q3-1 と Q3-2**

Q3-3 はコマンド実行は可能ですが、role-hoge が実行されません。

### **解説**

ロールに対してもタスクと同じようにタグ付けをすることができます。
Q3-1 の書き方が最も基本的ですが、Q3-2 のように全体を `{ }` でくくれば改行を使わずに書くことも可能です。

ちなみに、次の場合はどのように実行されるでしょうか。
Q3-1 とほぼ同じですが、 `tags` の位置に注目してください。

```text:Q3(追加問題)
  roles:
    - role: role-hoge
  tags:
    - play-hoge
```

実際に実行してみると、role だけでなく task も実行されたことがわかります。

```
[root@controller ansible]# ansible-playbook main.yml --tags play-hoge

PLAY ***************************************************************************
 (略)
TASK [role-hoge : debug] *******************************************************
ok: [192.168.100.20] => {
    "msg": "hoge"
}

TASK [debug] *******************************************************************
ok: [192.168.100.20] => {
    "msg": "play"
}
 (略)
```

インデントの位置が変わって **tags 属性が `roles:` だけでなく `tasks:` にもかかった** ために、このようなことが起こります。
Python も YAML もインデントが大きな意味を持つため、慣れないうちは注意が必要です。
また、Chef との比較において「**上から順に実行されるAnsible**」と言われる Ansible ですが、この例からもわかるように role と task では **role が先に実行される** ことがわかります。

## **4. ロール内のタグ付け**

### **問題**

今度は role-hoge に **include** をさせてみます。

```:ansible/main.yml(抜粋)
  tasks:
    - debug: msg="play"
  
  roles:
    - role: role-hoge
      tags:
        - play-hoge
```

role-hoge に include を追記します。

```:role-hoge/tasks/main.yml
- debug: msg="hoge"
  tags:
    - role-hoge

- include: hoge.yml
```

```:role-hoge/tasks/hoge.yml
- debug: msg="included hoge"
  tags:
    - include-hoge
```

**Q.4 下記コマンドをそれぞれ実行した時、 role-hoge 内の `debug: msg="hoge"` タスクは実行されるか**

```text:Q4-1
[root@controller ~]# ansible-playbook main.yml --tags play-hoge
```

```text:Q4-2
[root@controller ~]# ansible-playbook main.yml --tags role-hoge
```

```text:Q4-3
[root@controller ~]# ansible-playbook main.yml --tags include-hoge
```

### **解答**

**A.4 Q4-1, Q4-2 で実行される**

タグと実行タスクの関係を表にすると下記のようになります。

|タグ|debug: msg="hoge"|debug: msg="included hoge"|
|:---:|:---:|:---:|
|play-hoge|○|○|
|role-hoge|○|×|
|include-hoge|×|○|

### **解説**

playbook でロールに付けたタグを実行時に指定しなくても、ロール内部のタグを指定するとちゃんと対応するタスクが実行されます。
ただし、 include-hoge タグの例から分かるように、 **上位に位置するタスクが全て実行されるわけではない** ため注意が必要です。

ちなみに、 **include タスクに限って** 下記のような **tags 変数** を用いたタグの付け方があります。

```:role-hoge/tasks/main.yml
- include: hoge.yml tags=include-hoge
```

ただし、この **tags 変数は Ansible 2.0 から非推奨の方法** となったため、 Ansible 2.0 での実行時にはこのような WARNING が出てきます。

```
[DEPRECATION WARNING]: You should not specify tags in the include parameters. All tags should be specified
using the task-level option. This feature will be removed in a future release. Deprecation warnings can be
disabled by setting deprecation_warnings=False in ansible.cfg.
```

また、 **tags 属性によるタグ指定と tags 変数によるタグ指定を併用していた場合、エラーとなる** 点にも注意が必要です。

```
ERROR! Include tasks should not specify tags in more than one way (both via args and directly on the task)
```

## **5. dependency とタグ**

### **問題**

playbook で直接指定するロールを role-fuga に切り替えます。

```:ansible/main.yml(抜粋)
  roles:
    - role: role-fuga
      tags:
        - play-fuga
```

そして、 role-fuga では **dependencies** を使って、 role-hoge も実行されるようにします。

```:role-fuga/tasks/main.yml
- debug: msg="fuga"
  tags:
    - role-fuga
```

```:role-fuga/meta/main.yml
dependencies:
  - role-hoge
```

role-hoge については Q.4 と同じ状態ですので、 role-hoge タグや include-hoge タグを内部に持ちます。

**Q.5 下記のコマンドを実行した際、role-hoge 内の `debug: msg="hoge"` タスクは実行されるか。**

```text:Q5-1
[root@controller ~]# ansible-playbook main.yml --tags play-fuga
```

```text:Q5-2
[root@controller ~]# ansible-playbook main.yml --tags role-fuga
```

```text:Q5-3
[root@controller ~]# ansible-playbook main.yml --tags role-hoge
```

```text:Q5-4
[root@controller ~]# ansible-playbook main.yml --tags include-hoge
```

### **解答**

**A.5 Q5-1, Q5-3 で実行される**

タグと実行タスクの関係を表にすると下記のようになります。

|タグ|debug: msg="fuga"|debug: msg="hoge"|debug: msg="included hoge"|
|:---:|:---:|:---:|:---:|
|play-fuga|○|○|○|
|role-fuga|○|×|×|
|role-hoge|×|○|×|
|include-hoge|×|×|○|

### **解説**

dependencies で指定されるロール内のタスクに対しても、このようにきちんとタグが評価されます。
role-hoge や include-hoge タグを指定する Q5-3 と Q5-4 では **role-fuga 内で動くタスクがないにも関わらず、 role-hoge 内部にまでタグ評価** をしてくれます。

# **おわりに**

クイズを作るにあたり、色々なタグ指定の方法を試しましたが、意外と素直に動いてくれるという印象を持ちました。
ただ、 role, include, dependency など色々な階層にタグを散りばめてしまうと、 **どんな動き方をするのかわかりにくくなってしまう** 面があると思いました。
今回の例では、

1. role-fuga を動かす前に **role-hoge を必ず動かしたい** (→ dependencies を使う)
2. role-fuga で実行されるタスクを一部選べるようにしたい (→ tags を使う)

というようにしたとき、指定するタグによっては dependencies で指定しているにも関わらず **role-hoge のタスクが動かない** ことが起こりえます。
クイズの中では **Q5-2** がそのイメージに近いでしょうか。
このケースであれば role-hoge 側のタスクに always タグが必要になってきますが、中々難しいような気もします。

タグは非常に便利な機能ではありますが、

「 **ご利用は計画的に！** 」

されるとよいかと思います。