blog.manj.ioblog.manj.ioごく稀に更新2020-01-03ロールを安全に実行するために冒頭にassertを書こうAnsibleQiitaに書いた記事のバックアップです。Role?Ansibleでは、複数のtaskをまとめて実行するための実行単位としてRoleという仕組みをサポートしています。Roleでは以下のことが可能で、「OOというアプリをインストールする」とか「OOの設定を行なう」というような単位でtaskを分類するのに便利な機能です。tasksという階層構造を持たせることを推奨しており、role単位で一般外部に自作tasksを公開するansible-galaxyというシステムも用意されていることから、Ansibleの中で最も触れる機会が多い機能の一つではないかと思います。defaultsについてroleの機能の中でも特徴的なのが、role毎に「変数のデフォルト値」を設定できる機能です。role内で使われる変数、という前提を置くことで、roleのspecの一部を示すことができるそのRole内でしか使用されない、という前提を置くことで、定数定義に用いることができるJinja2を併用することで、(外部で定義されていることを期待した)変数群を操作して新しい変数を定義する、という操作をset_factを使わずに表現することができるのような使い方が可能です。Role実行時に起こりがちなトラブルAnsibleにおいてRoleはtaskを参照するための名前を分離する機能であり、実行単位を保証する機能ではありません。いいかえると、Roleに含まれるtaskをまとめてアトミックに実行するわけではなく、一部のtaskが失敗すると中途半端な形でAnsibleが終了することがあります。中途半端にroleが終了することでバイナリは配置されたがconfig等が配置されないserviceファイルは配置したがそれが実行できる状態ではないみたいな事態が起こり得ます。deb/rpmのインストール時にconfigファイルをデフォルトのもので置き換えるような挙動をするものもあるので、上記のような事態が発生することでアプリのアップデートなどのユースケースで致命的な障害を発生させてしまうかもしれません。taskが失敗する原因は色々ありますが、経験上9割くらいは変数参照のトラブルです。どこかで同名の変数が定義されていたことによる誤動作defaultsに設定された変数は、Ansibleの仕様上使用の優先度が最低に設定されています。別のroleでset_factされた変数やgroup_vars等で設定された変数と名前が重複すると、その変数は上書きされてしまいます。一般に使われやすい変数名でRoleの定数を定義しているときに、「どこかで同名の変数が設定されている」という事態が起こり得ます。filterにかけるときにエラーが発生したり、不正なconfigを生成してしまうなどのトラブルが発生する恐れがあります。(最悪の場合、不正なconfigが生成されたことに誰も気づかないままroleが流れきってしまうケースもあります)評価タイミングによるトラブルAnsibleの仕様として、変数の評価は参照時に実施されます。これによって、Jinja2構文を用いた変数は参照するタイミングによって評価結果が変わったり、jinja2のエラーによってtask自体が停止することがあり得ます。defaultsに定義された変数についても、role実行開始時には評価されず、task等で参照されたときに初めて評価されるので、このトラブルが発生する恐れがあります。modeで対策できないか?これは「場合による」としか言えないです。modeを流すのが困難なケースが存在します。moduleを使用することを思い付きました。conditionを列挙するとそのうちの1つでもfalseになったときに失敗する、という機能を持っています。「jinjaエンコード失敗によるエラー」等の「変数定義が不正なことによるエラー」をtask実行前に検知することができるので、role実行の安全性を高めることができます。Role変数に対する詳細なspecを定義できるので、コードリーディングや機能追加の大きな助けになります。Role内で用いる変数に必須/任意の区別を付ける空文字やNoneなどをassertで検出することで、間接的に引数の性質を定義することができます。enumな変数の定義inを使うことで、enumやboolをspecとして定義することもできます。みたいな形でリストを定義しておくと見通しがよくなるかもしれません。filterを用いることで、変数に格納されるべき文字列の形式を定義することが可能です。等の変数に入れてしまう方が見やすいです。Filterを通してassertする方法もあります。mac_addr|regex_search('^[0-9A-F]{2}(:[0-9A-F]{2}){5}$')まとめRole内での変数の取り扱いや変数が使用される優先度について深く理解しておかないと、tasksの実行中に予期せぬエラーが発生する恐れがあります。Roleの開始時にassertによる変数の検査をすることで、Role外の要因による変数の汚染やtask失敗による中途半端なデプロイを防止することが可能です。例3に挙げた変数を正規表現を用いてチェックする手法は有用です。assertを用いることでRole外からの上書きを前提とした変数についてもspecを定義できるので、group_vars等で明示的に指定されるべき変数や、Role間で共通的に使われることを想定した変数の定義ミスによるデプロイ事故を防ぐことができます。meta/とかに定義できるような機能があると面白そうなので、気が向いたらコード書いてみようかと思います。以下の8日目の記事で...2020-07-31転職から1年後の話こんな置き土産を残して転職してから1年ちょっと経ちました。manji0.引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。