はじめに
Ansibleを使うようになってしばらく経ちましたが、たいへん便利なツールだなと思っています。
ベストプラクティスだったり真面目に頭を働かせることもありますが、言われないと気づかないだけの地味なトラップみたいなものもあるのでそちらをまとめてみたいと思います。
各種バージョン
現在 yum の epel-testing リポジトリから入手できる最新の Ansible を使います。
[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.1.0
config file = /etc/ansible/ansible.cfg
configured module search path = Default w/o overrides
トラップ1 : on, offの暗黙的変換
概要
Ansible では変数を定義して template モジュールなど多くの場所で変数を展開することができます。
この変数展開に際し、ダブルクオーテーション等で囲んだ文字列でなく、直接 on, off などを記述するとそれが 暗黙的にtrue, falseの真偽値に変換 されてしまいます。
設定ファイルを展開する際にtemplateモジュールを使うことは多いと思いますが、on, off記法の他にtrue, falseでも受け付けているパターンがあるので意外と気づかないこともあるかもしれません。
例
以下のように変数を定義します。
test_on:
- on
- "on"
test_off:
- off
- "off"
これらの変数を debug モジュールで見てみましょう。
- name: check on value
debug: msg="{{ item }}"
with_items: "{{ test_on }}"
- name: check off value
debug: msg="{{ item }}"
with_items: "{{ test_off }}"
結果がこちらです。
[root@controller ansible]# ansible-playbook -i inventory/hosts check.yml
PLAY ***************************************************************************
TASK [setup] *******************************************************************
ok: [192.168.100.20]
TASK [check on value] **********************************************************
ok: [192.168.100.20] => (item=True) => {
"item": true,
"msg": true
}
ok: [192.168.100.20] => (item=on) => {
"item": "on",
"msg": "on"
}
TASK [check off value] *********************************************************
ok: [192.168.100.20] => (item=False) => {
"item": false,
"msg": false
}
ok: [192.168.100.20] => (item=off) => {
"item": "off",
"msg": "off"
}
PLAY RECAP *********************************************************************
192.168.100.20 : ok=3 changed=0 unreachable=0 failed=0
以上のように、ダブルクオーテーションで囲んでいないものが true, false に変換されていることがわかります。
大小文字によっても少し結果が変わるので、クオートなしとありの出力がどのようになるか表にまとめてみます。
値 | クオートなし | クオートあり |
---|---|---|
on | true | "on" |
oN | "oN" | "oN" |
On | true | "On" |
ON | true | "ON" |
値 | クオートなし | クオートあり |
---|---|---|
off | false | "off" |
oFf | "oFf" | "oFf" |
oFF | "oFF" | "oFF" |
ofF | "ofF" | "ofF" |
Off | false | "Off" |
OfF | "OfF" | "OfF" |
OFf | "OFf" | "OFf" |
OFF | false | "OFF" |
結果を見ればわかると思いますが、"oN" や "oFf" などの キモい組み合わせ には反応しないようです。
トラップ2 : with_dictの出力順
概要
ループを回す際に with_items
や with_dict
を使うかと思いますが、with_items
は 記載した順にループされる のに対し、 with_dict
では そうなりません 。
例
以下のように変数を定義します。
test_items:
- key: A
value: hoge
- key: B
value: fuga
- key: C
value: piyo
test_dict:
A: hoge
B: fuga
C: piyo
例によって debug モジュールで確認します。
- name: check items value
debug: msg="key={{ item.key }}, value={{ item.value }}"
with_items: "{{ test_items }}"
- name: check dict value
debug: msg="key={{ item.key }}, value={{ item.value }}"
with_dict: "{{ test_dict }}"
[root@controller ansible]# ansible-playbook -i inventory/hosts check.yml
PLAY ***************************************************************************
TASK [setup] *******************************************************************
ok: [192.168.100.20]
TASK [check items value] *******************************************************
ok: [192.168.100.20] => (item={u'value': u'hoge', u'key': u'A'}) => {
"item": {
"key": "A",
"value": "hoge"
},
"msg": "key=A, value=hoge"
}
ok: [192.168.100.20] => (item={u'value': u'fuga', u'key': u'B'}) => {
"item": {
"key": "B",
"value": "fuga"
},
"msg": "key=B, value=fuga"
}
ok: [192.168.100.20] => (item={u'value': u'piyo', u'key': u'C'}) => {
"item": {
"key": "C",
"value": "piyo"
},
"msg": "key=C, value=piyo"
}
TASK [check dict value] ********************************************************
ok: [192.168.100.20] => (item={'value': u'hoge', 'key': u'A'}) => {
"item": {
"key": "A",
"value": "hoge"
},
"msg": "key=A, value=hoge"
}
ok: [192.168.100.20] => (item={'value': u'piyo', 'key': u'C'}) => {
"item": {
"key": "C",
"value": "piyo"
},
"msg": "key=C, value=piyo"
}
ok: [192.168.100.20] => (item={'value': u'fuga', 'key': u'B'}) => {
"item": {
"key": "B",
"value": "fuga"
},
"msg": "key=B, value=fuga"
}
PLAY RECAP *********************************************************************
192.168.100.20 : ok=3 changed=0 unreachable=0 failed=0
少し分かりづらいですが、dict側では A → C → B の順で出力されていることが確認できるかと思います。
色々なパターンを試していますが key や value の辞書式などで出力されているわけではないようです。
ただし、何度やってもこの順ではあるため、何かしらのロジックに基づいた順序ではあるようです。
(ソースを見ればいいのですが、一旦は後回しに...気が向いたら追記するかもしれません)
トラップ3 : serviceモジュールがcheckモードでも変更入れてくる
概要
check モードといえばいわゆる dry-run ということで、対象に状態の変更を与えずに実行(想定)結果の確認ができる便利なものです。
ところが、 service モジュールでは check モードで動かしたにも関わらず、対象のサービスがエントリに入ってしまう ということが発生します。
例
以下の様なタスクを考えます。
- name: register hoge service
service: name=hoge enabled=no
これは /etc/init.d 配下にある hoge サービスを off で登録するというものですが、これを check モードで動かすとおかしなことが起きます。
対象ホストの初期状態はこちらです。
[root@controller ansible]# ssh root@192.168.100.20 ls -lR /etc/rc* | grep hoge
-rwxr-xr-x 1 root root 11048 Apr 7 16:33 hoge
[root@controller ansible]# ssh root@192.168.100.20 chkconfig --list hoge
service hoge supports chkconfig, but is not referenced in any runlevel (run 'chkconfig --add hoge')
/etc/init.d 配下に hoge というサービススクリプトが配置されていますが、 /etc/rc*.d からリンクは張られておらず、 chkconfig には登録されていません。
この状態から、 --check で Ansible を実行してみましょう。
[root@controller ansible]# ansible-playbook -i inventory/hosts check.yml --check
PLAY ***************************************************************************
TASK [setup] *******************************************************************
ok: [192.168.100.20]
TASK [register hoge service] ***************************************************
changed: [192.168.100.20]
PLAY RECAP *********************************************************************
192.168.100.20 : ok=2 changed=1 unreachable=0 failed=0
[root@controller ansible]# ssh root@192.168.100.20 ls -lR /etc/rc* | grep hoge
-rwxr-xr-x 1 root root 11048 Apr 7 16:33 hoge
lrwxrwxrwx 1 root root 14 Apr 7 16:47 K92hoge -> ../init.d/hoge
lrwxrwxrwx 1 root root 14 Apr 7 16:47 K92hoge -> ../init.d/hoge
lrwxrwxrwx 1 root root 14 Apr 7 16:47 S08hoge -> ../init.d/hoge
lrwxrwxrwx 1 root root 14 Apr 7 16:47 S08hoge -> ../init.d/hoge
lrwxrwxrwx 1 root root 14 Apr 7 16:47 S08hoge -> ../init.d/hoge
lrwxrwxrwx 1 root root 14 Apr 7 16:47 S08hoge -> ../init.d/hoge
lrwxrwxrwx 1 root root 14 Apr 7 16:47 K92hoge -> ../init.d/hoge
[root@controller ansible]# ssh root@192.168.100.20 chkconfig --list hoge
hoge 0:off 1:off 2:on 3:on 4:on 5:on 6:off
なんということでしょう。
check モードで動かしているにも関わらず、 /etc/rc*.d からリンクが張られてしまいました。
また、 service モジュール自体は off で登録するように書いているのですが、 chkconfig に登録する上、 on の状態 となっています。
そして、 --check を外して実際に動かしてみます。
[root@controller ansible]# ansible-playbook -i inventory/hosts check.yml
PLAY ***************************************************************************
TASK [setup] *******************************************************************
ok: [192.168.100.20]
TASK [register hoge service] ***************************************************
changed: [192.168.100.20]
PLAY RECAP *********************************************************************
192.168.100.20 : ok=2 changed=1 unreachable=0 failed=0
[root@controller ansible]# ssh root@192.168.100.20 ls -lR /etc/rc* | grep hoge
-rwxr-xr-x 1 root root 11048 Apr 7 16:33 hoge
lrwxrwxrwx 1 root root 14 Apr 7 16:47 K92hoge -> ../init.d/hoge
lrwxrwxrwx 1 root root 14 Apr 7 16:47 K92hoge -> ../init.d/hoge
lrwxrwxrwx 1 root root 14 Apr 7 16:47 K92hoge -> ../init.d/hoge
lrwxrwxrwx 1 root root 14 Apr 7 16:47 K92hoge -> ../init.d/hoge
lrwxrwxrwx 1 root root 14 Apr 7 16:47 K92hoge -> ../init.d/hoge
lrwxrwxrwx 1 root root 14 Apr 7 16:47 K92hoge -> ../init.d/hoge
lrwxrwxrwx 1 root root 14 Apr 7 16:47 K92hoge -> ../init.d/hoge
[root@controller ansible]# ssh root@192.168.100.20 chkconfig --list hoge
hoge 0:off 1:off 2:off 3:off 4:off 5:off 6:off
当然ながら、これ自体は Playbook に書いた通りの状態となっています。
結果的には Playbook に書いた通りの状態ではあるのですが、さすがにこれはいただけないと思います。
今のところ代替として、 command モジュールで chkconfig --add hoge ; chkconfig hoge on
みたいなものを書いています。
chkconfig コマンド自体は冪等性のある動きをしているし、 command モジュールは --check 時にはスキップされるのでまだマシかなと思っています。
続き
他にもあったら随時追記していきます。