LoginSignup
12
10

More than 5 years have passed since last update.

Ansibleで体験した地味なトラップ

Last updated at Posted at 2016-04-07

はじめに

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_itemswith_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 時にはスキップされるのでまだマシかなと思っています。

続き

他にもあったら随時追記していきます。

12
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
10