概要
仕事でひたすらplaybook書いてたら「冪等生ってなんだ?」ってなったので調べたメモ。
そもそも冪等性とは
同じ操作を何度繰り返しても、同じ結果が得られるという性質です。以下あたりが詳しく書かれてます。
よくある冪等生の説明の数式ですが冪等生がある関数fは何回実行しても結果が同一という性質があります。例えば絶対値を求めるabs()なんかがこの性質を持った関数です。
f() = f(f())
冪等性がない関数の場合は上記の式は成り立ちません。上の記事にあるようなsqrt()なんかがそうです。
sqrt(16) ≠ sqrt(sqrt(16))
Ansibleにおける冪等性の話
Ansibleといえば冪等性の話題が必ずと言っていいほど挙げられます。Ansibleによる冪等性はサーバの状態を保つことです。ここでポイントになるのがサーバの状態を保つとはどういうことなのかという点です。
Ansibleの公式のモジュールは冪等性を保つような実装をされていますが厳密な意味で上記で挙げたような冪等性を保つような実装はなされていません。以下がその例です。
tasks: - name: yum yum: name: httpd state: installed
上記のtaskは実際何回実行してもhttpdがインストールされた状態というのは保たれます。これは一見して冪等性を保っている実装に見えますが実際にはyumを実行するとことでyum独自のdbファイルへのアクセスや書き込みが裏では行われます。
アクセスが発生するだけで困るケースやモジュールの先で使うようなログファイルが変更されるのが困るケースがあるかないかはいったん置いておきます。
ここで言いたいのはあくまでもAnsibleが実行するtasksの冪等性は完全なものではなくモジュールの実装次第で冪等性のカバーする範囲が変わってしまうという点です。
f() = f(f())
Ansibleがいう冪等性は上記を満たすものではなく関数に特定範囲の値を取りうるパラメータeを与えた場合に冪等生が成り立つ以下の式となる。todo: 式考える (eは「httpdがインストールされている状態」のような値を取りうる)
f(x, e) = f(f(x, e), e)
Ansibleに置いていえばこのサーバの状態が違う場合は冪等性という性質は成り立たなくなる。例えば特定のディレクトリAが存在する状態をe1。存在しない状態をe2としたらそれぞれのタスクは同一の結果を得られないような場合があります。
f(x, e1) = f(x, e2)
モジュールないで何かをビルドしたりするケースやOS設定時のパラメータファイルの存在有無のタスクなんかがこれに当てはまります。
httpにおける冪等性
冪等性の記事でhttpのgetとかdeleteとかも割とよく見かけます。httpリクエストの場合はRFCでも言及されていて割と参考になったりしたので書いておきます。
Like the definition of safe, the idempotent property only applies to what has been requested by the user; a server is free to log each request separately, retain a revision control history, or implement other non-idempotent side effects for each idempotent request.
google翻訳する
セーフの定義と同様に、べき等プロパティはユーザーが要求したものにのみ適用されます。 サーバーは、各要求を個別にログに記録したり、リビジョン管理履歴を保持したり、 各べき等要求に対して他の非べき等の副作用を実装したりすることができます。
ざっくりまとめるとユーザが要求したものについては冪等性を担保するけどその先でサーバがなんかするのは担保しないよというもの。Ansibleでも似たような性質が見れるのでこれがしっくりきた。
Ansibleの場合はさっきあげたタスクを例にすると「httpdをインストールされた状態は保つけどyumが使うDBやその他システムファイルは関与しないよ」と言った考えになります。
この辺はissueとかでも議論されていて見解としては全てのモジュールが冪等じゃないしこの辺は今後も厳密にやってく気はないと言った感じで決着がついていた。
whenで冪等性
ansible-lintでshellモジュールを使うとwhenなしで使うと怒られる。これは上のAnsibleの思想(というかhttpの冪等性の思想?)によるものなんじゃないかと思う。
shellモジュールは特性上ユーザが自由にスクリプトを書いて実行できるので冪等性のない書き方をひたすらやって実行の都度chengedステータスが出るようになってしまう。
Ansibleの冪等性は状態というパラメータを持つ必要があるのでshellモジュールでこの状態を表す方法としてwhenが必要と言っているのかなって思った。