37台は成功と表示された — 失敗を設計する
目標
1台だけが失敗するようにしたホスト3つのインベントリで、失敗を扱うつまみを1つずつ有効にして、そのたびに何が止まり何が続行するかを、マーカーファイルで直接確認します。
なぜ重要なのか
失敗は起こります。設計すべきことは、失敗をなくすことではなく、失敗したときに何が起きるかをあらかじめ決めておくことです。Ansibleのデフォルト値は、「失敗したホストだけが静かに外れ、残りは続行する」で、サーバーを1台ずつ独立して直す作業には良いデフォルト値であり、40台が1つのサービスを構成しているなら悪いデフォルト値です。そのため、このラボは、3つの質問を順に扱います。この失敗は本当の失敗なのか(failed_when)、失敗したそのホストはどうするのか(ignore_errors・block/rescue)、ほかのホストはどうするのか(any_errors_fatal・max_fail_percentage)。ここに、開始前に止めるassertと、失敗とは別の欄で数えるunreachableが加わると、デプロイレポートを読めるようになります。最後のステップで、そのレポートを自分で作成します。
ステップ
/root/ans/err/hosts.iniに、webグループ(web1、web2)・dbグループ(db1)・ghostsグループ(ghost1)を書いてください。4つのホストすべてがansible_host=127.0.0.1、ansible_user=rootで、ポートはghost1だけが2223、残りは2222です。そして、/root/ans/err/p01.ymlで、web:dbに対して実行するプレイを作成してください。最初のタスクはweb2でのみ失敗し、2つ目のタスクは/root/ans/err/a1/reached-<호스트이름>を残します(プレースホルダーはホスト名です)。実行出力は/root/ans/err/out/run1.txtに保存してください。/root/ans/err/p02.ymlを、ステップ1と同じ構造で作成し、失敗するタスクにignore_errorsを付け、マーカーは/root/ans/err/a2/reached-<호스트이름>に残してください(プレースホルダーはホスト名です)。出力は/root/ans/err/out/run2.txtに保存します。今回は、3つのホストすべてにマーカーが残り、PLAY RECAPのignoredの欄が上がる必要があります。/root/ans/err/sample.confを、listen_port=8080・env=prod・workers=4の3行で作成してください。そして、/root/ans/err/p03.ymlで、web1でそのファイルにnosuchkeyが何回出てくるかを数えるタスクを作成してください。取得にすぎないので、変更として数えないようにし、見つからなくて出る終了コードは失敗にならないよう、基準を決めてください。続けて、その終了コードを/root/ans/err/a3/grep-rc.txtに1行で残し、出力は/root/ans/err/out/run3.txtに保存してください。/root/ans/err/p04.ymlで、web1に対して実行するプレイを作成してください。blockの中に、失敗するタスクと、その後に/root/ans/err/a4/never.txtを作成するタスクを置き、rescueは/root/ans/err/a4/rescued.txtに失敗したタスクの名前を書き込み、alwaysは/root/ans/err/a4/always.txtを残すようにしてください。出力は/root/ans/err/out/run4.txtに保存します。never.txtは作成されてはいけず、PLAY RECAPのrescuedが1である必要があります。/root/ans/err/p05.ymlをlocalhostに対して実行するプレイとして作成し、assertタスクを1つだけ置いてください。deploy_envがdev・stage・prodのうちのどれかを検査し、fail_msgは허용되지 않은 배포 환경입니다(韓国語で「許可されていないデプロイ環境です」を意味する文字列です)で始まって、現在の値も一緒に表示する必要があります。-e deploy_env=prodで実行した出力を/root/ans/err/out/assert-ok.txtに、-e deploy_env=qaで実行した出力を/root/ans/err/out/assert-fail.txtに保存してください。/root/ans/err/p06.ymlを、ステップ1と同じ構造で作成し、プレイにany_errors_fatalをオンにして、マーカーは/root/ans/err/a6/reached-<호스트이름>に残すようにしてください(プレースホルダーはホスト名です)。出力は/root/ans/err/out/run6.txtに保存します。今回は、どのホストもマーカーを残せない必要があります。/root/ans/err/p07.ymlで、web:dbに対して実行するプレイを作成してください。max_fail_percentageは50、変数run_tagのデフォルト値はcontinue、変数doomedのデフォルト値はweb21つです。最初のタスクが/root/ans/err/a7/<run_tag>ディレクトリを作成し、次のタスクが、doomedに含まれるホストでのみ失敗し、最後のタスクが/root/ans/err/a7/<run_tag>/reached-<호스트이름>を残します(プレースホルダーはホスト名です)。デフォルト値で1回実行して/root/ans/err/out/run7-continue.txtに、run_tagをabortにして、doomedにweb2とdb1を入れてもう1回実行して、/root/ans/err/out/run7-abort.txtに保存してください。/root/ans/err/p08.ymlをallに対して実行するプレイとして作成してください。最初のタスクはpingですが、到達できないものは無視し、2つ目のタスクは/root/ans/err/a8/reached-<호스트이름>を残します(プレースホルダーはホスト名です)。出力は/root/ans/err/out/run8.txtに保存してください。そして、実行記録ファイル1つを引数として受け取り、hosts=N ok=N changed=N unreachable=N failed=N skipped=N rescued=N ignored=Nの1行を出力する/root/ans/err/recap.shを書き、それで/root/ans/err/out/failure-report.jsonを作成してください。キーは、default_failed(ステップ1の実行のfailedの合計)・ignored(ステップ2)・rescued(ステップ4)・unreachable(ステップ8)・fatal_reached(a6のマーカーの数)・maxfail_reached(a7/continueのマーカーの数)の6つで、値はすべて数値です。
参考
- 作業ディレクトリは
/root/ans/errです。インベントリのホストは、このPodの中の127.0.0.1:2222のsshdに接続し、ghost1だけが、誰も待ち受けていない2223を見ます。 - 失敗するプレイブックは、0以外の値で終了します。出力をファイルに保存するとき、シェルがそこで止まらないようにしてください。
PLAY RECAPの欄は7つです。ok・changed・unreachable・failed・skipped・rescued・ignored。- よくある間違い:
ignore_errorsを、到達できないホストに期待することです。それはタスクの失敗ではなく、接続の失敗です。 - よくある間違い: マーカーのディレクトリを作成せずに実行して、空であることとないことを区別できないことです。
- よくある間違い:
rescueが救い出した実行を、緑のランプとしてだけ見て、ロールバックが実行された事実を、どこにも残さないことです。 - エラー処理ガイド・ブロックによるタスクのグループ化・assertモジュール・failモジュール・実行ストラテジーとforks
デフォルトの動作: 失敗したホストだけが外れる
/root/ans/err/hosts.iniに、webグループ(web1、web2)・dbグループ(db1)・ghostsグループ(ghost1)を書いてください。4つのホストすべてがansible_host=127.0.0.1、ansible_user=rootで、ポートはghost1だけが2223、残りは2222です。そして、/root/ans/err/p01.ymlで、web:dbに対して実行するプレイを作成してください。最初のタスクはweb2でのみ失敗し、2つ目のタスクは/root/ans/err/a1/reached-<호스트이름>を残します(プレースホルダーはホスト名です)。実行出力は/root/ans/err/out/run1.txtに保存してください。
ホストがすべて同じsshdに接続するので、「この1台だけ失敗」は、ファイルではなくinventory_hostnameの条件で作ります。ghost1のポートは、誰も待ち受けていない番号なので、ステップ8で、到達できないホストの役を果たします。プレイブックが0以外の値で終了するので、出力を保存するとき、スクリプトがそこで止まらないようにしてください。
結果だけを無視すると、何が変わるか
/root/ans/err/p02.ymlを、ステップ1と同じ構造で作成し、失敗するタスクにignore_errorsを付け、マーカーは/root/ans/err/a2/reached-<호스트이름>に残してください(プレースホルダーはホスト名です)。出力は/root/ans/err/out/run2.txtに保存します。今回は、3つのホストすべてにマーカーが残り、PLAY RECAPのignoredの欄が上がる必要があります。
ignore_errorsは、判定を変えずに、結果だけを無視します。そのため、タスクは依然として失敗として記録されますが、ホストは外れません。PLAY RECAPで、failedではなく、どの欄が上がるかを、自分で見てください。
何を失敗と見なすかを自分で決める
/root/ans/err/sample.confを、listen_port=8080・env=prod・workers=4の3行で作成してください。そして、/root/ans/err/p03.ymlで、web1でそのファイルにnosuchkeyが何回出てくるかを数えるタスクを作成してください。取得にすぎないので、変更として数えないようにし、見つからなくて出る終了コードは失敗にならないよう、基準を決めてください。続けて、その終了コードを/root/ans/err/a3/grep-rc.txtに1行で残し、出力は/root/ans/err/out/run3.txtに保存してください。
探す単語がなければ1が出て、ファイルがない、または引数が間違っていれば、2以上が出ます。前者は答えで、後者がエラーです。registerで受け取った結果の終了コードで、基準を書いてください。ignore_errorsで覆うこととは、別のことです。このプレイブックは、failed=0できれいに終わる必要があります。
block・rescue・alwaysでロールバックを構造にする
/root/ans/err/p04.ymlで、web1に対して実行するプレイを作成してください。blockの中に、失敗するタスクと、その後に/root/ans/err/a4/never.txtを作成するタスクを置き、rescueは/root/ans/err/a4/rescued.txtに失敗したタスクの名前を書き込み、alwaysは/root/ans/err/a4/always.txtを残すようにしてください。出力は/root/ans/err/out/run4.txtに保存します。never.txtは作成されてはいけず、PLAY RECAPのrescuedが1である必要があります。
失敗したタスクの名前は、rescueの中でだけ使える変数に入っています。名前にfailedとtaskが含まれます。rescueが最後まで成功すると、そのホストは失敗していないものとして扱われて、緑になります。そのため、ロールバックが実行された証拠をファイルに残すことが、この構造の半分です。
前提が食い違ったら、何かを触る前に止まる
/root/ans/err/p05.ymlをlocalhostに対して実行するプレイとして作成し、assertタスクを1つだけ置いてください。deploy_envがdev・stage・prodのうちのどれかを検査し、fail_msgは허용되지 않은 배포 환경입니다(韓国語で「許可されていないデプロイ環境です」を意味する文字列です)で始まって、現在の値も一緒に表示する必要があります。-e deploy_env=prodで実行した出力を/root/ans/err/out/assert-ok.txtに、-e deploy_env=qaで実行した出力を/root/ans/err/out/assert-fail.txtに保存してください。
デフォルトの失敗メッセージは1行だけなので、条件が複数あると、どれが偽だったのかがわかりません。現在の値をメッセージに埋め込んでおけば、ログ1行で終わります。コマンドラインの変数は、プレイのvarsより強いので、同じプレイブックを、値だけを変えて2回実行できます。採点ツールも、同じ方法でこのプレイブックを直接実行してみます。
1台でも失敗したら、すべてを止める
/root/ans/err/p06.ymlを、ステップ1と同じ構造で作成し、プレイにany_errors_fatalをオンにして、マーカーは/root/ans/err/a6/reached-<호스트이름>に残すようにしてください(プレースホルダーはホスト名です)。出力は/root/ans/err/out/run6.txtに保存します。今回は、どのホストもマーカーを残せない必要があります。
このつまみは、プレイのレベルに付きます(タスクではありません)。失敗していないホストも、残りのタスクを実行しないので、マーカーのディレクトリは空になります。PLAY RECAPで、失敗していないホストのokの欄がどうなるかを見てください。マーカーのディレクトリは、あらかじめ作成しておく必要があります。そうすれば、「空である」と「ない」を区別できます。
何台までは耐えるか: 割合で決める
/root/ans/err/p07.ymlで、web:dbに対して実行するプレイを作成してください。max_fail_percentageは50、変数run_tagのデフォルト値はcontinue、変数doomedのデフォルト値はweb21つです。最初のタスクが/root/ans/err/a7/<run_tag>ディレクトリを作成し、次のタスクが、doomedに含まれるホストでのみ失敗し、最後のタスクが/root/ans/err/a7/<run_tag>/reached-<호스트이름>を残します(プレースホルダーはホスト名です)。デフォルト値で1回実行して/root/ans/err/out/run7-continue.txtに、run_tagをabortにして、doomedにweb2とdb1を入れてもう1回実行して、/root/ans/err/out/run7-abort.txtに保存してください。
3台のうち1台は33パーセント、2台は66パーセントです。基準を超えたかどうかが、分かれ道です。リスト変数をコマンドラインで上書きするときは、JSONでまるごと渡すと楽です。マーカーのディレクトリを作成するタスクを、失敗するタスクより前に置いてください。そうすれば、止まった実行でも、空のディレクトリが残ります。
到達できないホストと、6回の実行を1枚にまとめる
/root/ans/err/p08.ymlをallに対して実行するプレイとして作成してください。最初のタスクはpingですが、到達できないものは無視し、2つ目のタスクは/root/ans/err/a8/reached-<호스트이름>を残します(プレースホルダーはホスト名です)。出力は/root/ans/err/out/run8.txtに保存してください。そして、実行記録ファイル1つを引数として受け取り、hosts=N ok=N changed=N unreachable=N failed=N skipped=N rescued=N ignored=Nの1行を出力する/root/ans/err/recap.shを書き、それで/root/ans/err/out/failure-report.jsonを作成してください。キーは、default_failed(ステップ1の実行のfailedの合計)・ignored(ステップ2)・rescued(ステップ4)・unreachable(ステップ8)・fatal_reached(a6のマーカーの数)・maxfail_reached(a7/continueのマーカーの数)の6つで、値はすべて数値です。
到達できないことは、タスクの失敗ではなく接続の失敗なので、無視するための引数が別にあります。その引数は、付けたタスクにだけ効き、無視することにしても、プレイブックの終了コードは0ではありません。合算は、PLAY RECAPの行の下だけを読む必要があります。上のタスクの出力にも、似た文字が混ざっているからです。採点ツールは、自分で作成した記録ファイルでも、このスクリプトを実行してみるので、答えを書いておいただけのスクリプトは、通過できません。