Ansibleの冪等性とPlaybook
渡辺です。
Ansibleを利用する時、避けて通れない概念が「冪等性(べきとうせい/idempotence)」です。 冪等性は数学方面の用語で、大雑把に言えば「1回だけ操作を行っても、不数回(N回)行っても結果が変わらない特性」のことを指します。
例えば、有理数の乗算であれば1と0には冪等性があります。 1は、Nに何回かけても結果はNです。 同様に、0は、Nに何回かけても結果は0です。 とはいえ、Ansibleの冪等性は、あくまで構成管理を行う上でのことなので、数学的な冪等性については「そんな概念なんだ」程度の理解で良いと思います。
Ansibleによる冪等性はサーバの状態を保つこと
サーバの定義を記述したAnsibleのPlaybookを実行すると、定義に合わせたミドルウェアなどがサーバにインストールされ、サーバの状態が変更されます。 そして、サーバの定義、すなわちPlaybookを変更しないのであれば、Playbookを何回実行してもサーバの状態が変更されないことが自然です。 この自然な状態を「Playbookが冪等性を保っている」と定義します。 言い換えれば、冪等性を持たないPlaybookは、実行する度にサーバの状態が変更されるのです。
例えば、素のAmazonLinuxのAMIからサーバを立ち上げ、この状態を「初期状態」とします。 ここに次のPlaybookを流しました。
--- - hosts: all tasks: - name: apache24 installed yum: name: httpd24
Ansibleのyumモジュールにより、Apache2.4がインストールされます。 この時、サーバの状態が変更されています。
サーバの状態が変更された時、Ansibleの実行結果は次のようにchanged
を示します。
TASK [apache24 installed] ****************************************************** changed: [Web]
さて、再度、同じPlaybookを流します。
サーバには既にApache2.4がインストールされているので、yum
モジュールはApache2.4をインストールしません。
この時、サーバの状態は変更されません。
サーバの状態が変更されなかった時、Ansibleの実行結果は次のようにok
を示します。
TASK [apache24 installed] ****************************************************** ok: [Web]
すなわち、すべての実行結果がok
であるならば、サーバの状態が変更されていないということです。
さらに、3回目・4回目と同じPlaybookを流しても、サーバの状態は変更されません。 このPlaybookは冪等性を保っています。
Ansibleの冪等性と範囲
Ansibleの冪等性とは無関係にサーバの状態は変化することも現実です。
例えば、Apacheにアクセスがあればアクセスログやエラーログが出力されるでしょう。 Ansibleで構成管理を行っていたとしても、サーバの状態が厳密に変更されないことは、まずありません。 手動でPHPをインストールした場合も同様です。 この時、サーバの状態は変化しますが、Ansibleを実行する上ではサーバの状態は変更がないとみなされます。
つまり、Ansibleで保たれる冪等性はPlaybookで定義した範囲に限定されます。 言い換えると、なるべくAnsibleで管理する範囲を広くしておいた方が、サーバの再構築などが簡単にできますし、構成管理に冪等性の概念を活用する事ができることになります。
冪等性を破壊するcommandモジュール
AnsibleはサーバにSSH接続を行い、各種のコマンドを実行することで構成管理を行います。 Playbookはその定義を記述したものであり、本質的にはセットアップスクリプトやセットアップ手順書と同じモノです。
Ansibleでは、構成管理したい内容によって様々なモジュールが提供されています。
そして、モジュールではカバーできない作業を行う最終手段としてcommand
モジュールが提供されています。
command
モジュールを使うと、文字通りサーバ上で任意のコマンドを実行できます。
例えば、次のようなPlaybookでApacheをインストール可能です。
--- - hosts: all tasks: - name: apache24 installed command: "yum -y install httpd24"
ところが、command
モジュールは常にchanged
を返します。
実際に実行されたコマンドがサーバの状態を変更しているとみなされます。
したがって、command
モジュールを使ったPlaybookは冪等性を保つことができません。
冪等性を保つPlaybookに必要なこと
Ansibleで構成管理を行う時、サーバの定義であるPlaybookに変更がなければ、サーバの状態を変更させない、すなわちPlaybookに冪等性を持たせることが重要です。 このためにはAnsibleのモジュールの特性を理解し、サブ構文を駆使する必要があります。
commandモジュールを利用しない
command
モジュールは強力です。
しかし、常にchanged
を返すため、Playbookの冪等性を破壊します。
一方、yumモジュールでは内部的に対象がインストールされているかをチェックし、インストールの必要がなければok
を返し、必要があればインストールしてchanged
を返します。
つまり、他の冪等性を保てるモジュールを利用できる場合に、command
モジュールを利用すべきではありません。
changed_whenで制御する
サブ構文のひとつchanged_when
は文字どおり、changed
の条件を記述できます。
changed_when: False
とすれば、どのようなモジュールが実行されてもサーバに変更を与えないタスクとなります。
例えば、次のタスクはcommand
モジュールでjavaコマンドを実行し、javaがインストールされているかをチェックします。
commandモジュールなのでjavaコマンドはサーバの状態を変更するとみなされ、cahnged
を返します。
しかし、changed_when: False
があるため、このタスクは常にokを返すのです。
- name: "check '{{ java_home }}/bin/java' if exist" command: "{{ java_home }}/bin/java -version" failed_when: False changed_when: False register: java_result
このようにチェックのためにコマンドを実行するような場合、register
と組み合わせ、command
を使う事があります。
やっぱりcommandモジュールを利用しない
なお、パスの判定であれば、 stat
モジュールを利用すれば次のように書くこともできます。
- name: "check '{{ java_home }}/bin/java' if exist" stat: path: "{{ java_home }}/bin/java" register: java_result
stat
モジュールはpath
で指定されたパスが存在するかを判定する専用モジュールです。
このモジュールはパスが存在しても存在しなくともokを返しますので、changed_when
やfailed_when
に依存しなくて済むでしょう。
changed_when
が必要になったならば、一度モジュールの一覧を確認し、代用モジュールがないか確認してください。
まとめ
今回はAnsibleで重要な概念である「冪等性」について、command
モジュールやchanegd_when
を交えつつ解説しました。
Playbookで冪等性を保てれば、Playbookでの修正がサーバの変更と同等となります。
サーバの構築手順書はすべてPlaybookになると、gitなどのバージョン管理システムでの管理とも相性が良いです。
手間はかかりますが、それだけの価値があるのです。