一部だけ回したのに、なぜ最終状態ではないのか
目標
タグでプレイブックの一部だけを実行する方法を身に付け、その便利さがどのようなリスクと対になっているかを、自分で計測します。最後に、どのタグの選択なら完走できるかを判定するツールを作成します。
なぜ重要なのか
プレイブックは成長します。タスクが80個あるのに、設定の1行だけを直したい日が来て、タグは、その場所に置かれた道具です。文法が簡単なので、人々は学んだ翌日から本番で使います。事故は、そこで起きます。タグは実行を切り分ける刃物ですが、プレイブックは、切り分けられてもよいようには設計されていないからです。前のタスクが作成したディレクトリを後ろのタスクが使い、設定を変更したタスクが、ハンドラーを呼びます。一部だけを選ぶと、その結び付きが切れ、運が悪ければ、失敗すらせずに、半分だけ合った状態が残ります。そのため、このラボは、タグの文法を身に付ける半分と、切り分けた実行が何を保証できないかを目で確かめる半分で、構成されています。
ステップ
/root/anstags/hosts.iniを作成してください。[web]にweb1(ansible_host=127.0.0.1、ansible_port=2222)、[all:vars]でansible_user=rootです。/root/anstags/site.ymlを作成してください。プレイ変数app_env(デフォルトはlab)、ハンドラーreload app(/root/anstags/out/reload.markerにreloadedの1行を書きます)、そして4つのタスクです。/root/anstags/appディレクトリを0755で作成する배포 자리를 만든다(韓国語で「デプロイ用の場所を作る」を意味するタスク名です。タグはsetup)、/root/anstags/app/app.confにenv=<app_env>の1行を0644で書き込み、ハンドラーをnotifyする설정을 쓴다(韓国語で「設定を書く」を意味するタスク名です。タグはconfig)、release_idをr-2026に決める배포 번호를 정한다(韓国語で「デプロイ番号を決める」を意味するタスク名です。タグはprep、set_fact)、/root/anstags/app/release.txtにrelease_idを0644で書き込む배포 번호를 기록한다(韓国語で「デプロイ番号を記録する」を意味するタスク名です。タグはdeploy)です。全体を一度収束させて、出力を/root/anstags/out/full.txtに保存し、続けて、--tags configでもう一度実行して、/root/anstags/out/config.txtに保存してください。ansible-playbook -i hosts.ini site.yml --list-tagsの出力を/root/anstags/out/list-tags.txtに保存してください。そのあと、--list-tasks --tags configの出力を/root/anstags/out/list-config.txtに保存してください。2つ目のファイルには、설정을 쓴다(韓国語で「設定を書く」を意味するタスク名)はあり、배포 번호를 기록한다(韓国語で「デプロイ番号を記録する」を意味するタスク名)はない必要があります。--skip-tags prep,deployでプレイブックを実際に実行して、出力を/root/anstags/out/skip.txtに保存してください。出力には、배포 자리를 만든다(韓国語で「デプロイ用の場所を作る」を意味するタスク名)と설정을 쓴다(韓国語で「設定を書く」を意味するタスク名)のTASK [...]の行があり、배포 번호를 정한다(韓国語で「デプロイ番号を決める」を意味するタスク名)と배포 번호를 기록한다(韓国語で「デプロイ番号を記録する」を意味するタスク名)の行はない必要があります。そして、--list-tasks --skip-tags prep,deployの出力を/root/anstags/out/list-skip.txtに保存してください。- プレイ自体にタグ
platformを付けてください。そして、설정을 검증한다(韓国語で「設定を検証する」を意味する名前)というブロックを、タスクの最後に追加して、そのブロックにタグverifyを付けてください。ブロックの中には、タグを付けていないタスクが2つ入ります。/root/anstags/app/app.confの状態を読み取ってconf_statでregisterする설정 파일의 상태를 읽는다(韓国語で「設定ファイルの状態を読み取る」を意味するタスク名)と、その結果でファイルがあるかどうかをアサートする설정 파일이 있는지 단언한다(韓国語で「設定ファイルがあるかどうかをアサートする」を意味するタスク名)です。--list-tasksの全体の出力を/root/anstags/out/inherit.txtに、--list-tasks --tags verifyの出力を/root/anstags/out/block.txtに保存してください。 - タスクをあと2つ追加してください。
어떤 선택에서도 남기는 표식(韓国語で「どの選択でも残す目印」を意味するタスク名)は、/root/anstags/out/always.markerにalwaysの1行を0644で書き込み、タグはalways1つです。함부로 돌면 안 되는 태스크(韓国語で「むやみに実行してはいけないタスク」を意味するタスク名)は、/root/anstags/out/never.markerにdangerの1行を0644で書き込み、タグはneverとdangerの2つです。--tags configで実際に実行して、出力を/root/anstags/out/always.txtに保存してください。어떤 선택에서도 남기는 표식が一緒に実行される必要があります。そして、--list-tasks --tags dangerの出力を/root/anstags/out/danger.txtに保存してください。/root/anstags/out/never.markerは、このラボが終わるまで作成されてはいけません。 /root/anstags/tasks/common.ymlを作成してください。タスクは2つです。공통 점검 하나(韓国語で「共通点検1」を意味するタスク名)は、common-oneを出力し、タグはありません。공통 점검 둘(韓国語で「共通点検2」を意味するタスク名)は、common-twoを出力し、タグdeepを持ちます。そのあと、site.ymlの最後に、タスクを2つ追加してください。import 로 공통 점검을 끌어온다(韓国語で「importで共通点検を取り込む」を意味するタスク名です。import_tasksでそのファイルを取り込み、タグはimported)と、include 로 공통 점검을 끌어온다(韓国語で「includeで共通点検を取り込む」を意味するタスク名です。include_tasksで同じファイルを取り込み、タグはincluded)です。--list-tasks --tags importedの出力を/root/anstags/out/reuse-import.txtに、--list-tasks --tags includedの出力を/root/anstags/out/reuse-include.txtに保存してください。- まず、
/root/anstags/out/reload.markerを削除してください。そのあと、--start-at-task "배포 번호를 정한다" -e app_env=prod(--start-at-taskの引数は、韓国語で「デプロイ番号を決める」を意味するタスク名です)でプレイブックを実際に実行し、出力を/root/anstags/out/startat.txtに保存してください。終わったあと、/root/anstags/app/app.confの内容と、/root/anstags/out/reload.markerが存在するかどうかを、続けて/root/anstags/out/aftermath.txtに残してください。1行目はconf=<app.conf 의 env 줄>、2行目はmarker=<yes 또는 no>です(プレースホルダーは、app.confのenvの行と、yesまたはnoです)。 /root/anstags/tag-check.sh <태그목록>を作成してください。その選択でsite.ymlをチェックモードで実行してみて、正常に終われば、1行目にSAFE <태그목록>を出力して0で、終わらなければ、1行目にUNSAFE <태그목록> rc=<종료코드>を出力して1で終了します(プレースホルダーはタグのリストと終了コードです)。そのあと、./tag-check.sh deployと./tag-check.sh prep,deployを順に実行して、2行を/root/anstags/out/tagcheck.txtに続けて保存してください。前者はUNSAFE、後者はSAFEである必要があります。
参考
- まず、ステップ1でインベントリとプレイブックを作成して、全体を一度収束させてください。タグは、その次から使う道具です。
- コマンドのヒント:
--list-tagsはどんなタグがあるかを、--list-tasks [--tags X]はその選択で何が実行されるかを、教えてくれます。どちらも対象に接続しないので、本番でも安全です。 - コマンドのヒント: タスク名に空白があるので、
--start-at-task '배포 번호를 정한다'のように、引用符で囲みます(韓国語の部分は「デプロイ番号を決める」を意味するタスク名です)。 - よくある間違い:
--tagsで絞り込まれたタスクがskippingと表示されると期待することです。絞り込まれたタスクは、出力からまるごと抜け、サマリーのskippedも0のままです。 - よくある間違い:
include_tasksにタグを付けて、そのタグで実行したあと、「何も起こらない」と慌てることです。 - よくある間違い: 途中から実行したものが
failed=0で終わったのを見て、デプロイが終わったと判断することです。 --stepは、人がキーを押さなければ進まない対話式のモードなので、このラボでは扱いません。ロールにタグを付ける話も、ロール自体が次のコースのテーマなので、ここでは扱いません。- タグで一部だけを実行する・途中から実行する・importとinclude・ハンドラー・ansible-playbookのオプション
タスクにタグを付けて、全体を一度収束させる
/root/anstags/hosts.iniを作成してください。[web]にweb1(ansible_host=127.0.0.1、ansible_port=2222)、[all:vars]でansible_user=rootです。/root/anstags/site.ymlを作成してください。プレイ変数app_env(デフォルトはlab)、ハンドラーreload app(/root/anstags/out/reload.markerにreloadedの1行を書きます)、そして4つのタスクです。/root/anstags/appディレクトリを0755で作成する배포 자리를 만든다(韓国語で「デプロイ用の場所を作る」を意味するタスク名です。タグはsetup)、/root/anstags/app/app.confにenv=<app_env>の1行を0644で書き込み、ハンドラーをnotifyする설정을 쓴다(韓国語で「設定を書く」を意味するタスク名です。タグはconfig)、release_idをr-2026に決める배포 번호를 정한다(韓国語で「デプロイ番号を決める」を意味するタスク名です。タグはprep、set_fact)、/root/anstags/app/release.txtにrelease_idを0644で書き込む배포 번호를 기록한다(韓国語で「デプロイ番号を記録する」を意味するタスク名です。タグはdeploy)です。全体を一度収束させて、出力を/root/anstags/out/full.txtに保存し、続けて、--tags configでもう一度実行して、/root/anstags/out/config.txtに保存してください。
タグは、すでに一度全体が実行されたシステムに使う道具です。新しいサーバーに--tags configだけをかけると、ディレクトリがなくて失敗するので、最初の収束は必ず全体で行います。そのため、このステップは、全体の実行が先です。タグは、タスクにtags: [이름]として付けます(プレースホルダーは名前です)。--tags configで実行した出力に、残りの3つのタスクのTASK [...]の行がまったくないことを確認してください。絞り込まれたタスクは、スキップされたと表示されるのではなく、出力からまるごと抜けます。
実行する前に、何が実行されるかを一覧で先に見る
ansible-playbook -i hosts.ini site.yml --list-tagsの出力を/root/anstags/out/list-tags.txtに保存してください。そのあと、--list-tasks --tags configの出力を/root/anstags/out/list-config.txtに保存してください。2つ目のファイルには、설정을 쓴다(韓国語で「設定を書く」を意味するタスク名)はあり、배포 번호를 기록한다(韓国語で「デプロイ番号を記録する」を意味するタスク名)はない必要があります。
この2つの道具は、対象に接続せず、何も変更しません。本番でタグを初めて使うときに、最初にかけるべきものが、これです。--list-tagsは「このプレイブックにどんなタグがあるか」を、--list-tasksは「今回の選択で何が実行されるか」を、順番に教えてくれます。--list-tasksに--tagsを一緒に指定すると、選択がそのまま反映されます。タスクごとに付いたタグの一覧も同じ行に出力されるので、継承を目で確認するときにも使います。
除外する側で選ぶ
--skip-tags prep,deployでプレイブックを実際に実行して、出力を/root/anstags/out/skip.txtに保存してください。出力には、배포 자리를 만든다(韓国語で「デプロイ用の場所を作る」を意味するタスク名)と설정을 쓴다(韓国語で「設定を書く」を意味するタスク名)のTASK [...]の行があり、배포 번호를 정한다(韓国語で「デプロイ番号を決める」を意味するタスク名)と배포 번호를 기록한다(韓国語で「デプロイ番号を記録する」を意味するタスク名)の行はない必要があります。そして、--list-tasks --skip-tags prep,deployの出力を/root/anstags/out/list-skip.txtに保存してください。
選ぶ方法は2つあります。含めるものを指定するか(--tags)、除くものを指定するか(--skip-tags)です。タグが増えると、後者のほうが楽なことが多いです。両方を一緒に指定することもでき、その場合は、除いたものが勝ちます。一覧はカンマでつなぎます。実行する前に一覧で先に確認する習慣は、--skip-tagsでも同じく価値があります。
ブロックとプレイに付けたタグが下へ流れる
プレイ自体にタグplatformを付けてください。そして、설정을 검증한다(韓国語で「設定を検証する」を意味する名前)というブロックを、タスクの最後に追加して、そのブロックにタグverifyを付けてください。ブロックの中には、タグを付けていないタスクが2つ入ります。/root/anstags/app/app.confの状態を読み取ってconf_statでregisterする설정 파일의 상태를 읽는다(韓国語で「設定ファイルの状態を読み取る」を意味するタスク名)と、その結果でファイルがあるかどうかをアサートする설정 파일이 있는지 단언한다(韓国語で「設定ファイルがあるかどうかをアサートする」を意味するタスク名)です。--list-tasksの全体の出力を/root/anstags/out/inherit.txtに、--list-tasks --tags verifyの出力を/root/anstags/out/block.txtに保存してください。
タグは、付けた場所から下へ流れます。ブロックに付ければブロック内のすべてのタスクが持ち、プレイに付ければ、そのプレイのすべてのタスクが持ちます。その事実は、--list-tasksの各行の末尾に付くTAGS: [...]に、そのまま表れます。ブロック内のタスクに何も付けていないのにタグが2つ見えれば、継承が目に見えたということです。状態を読み取るモジュールと、前提条件をアサートするモジュールは、名前が思い浮かばなければ、ansible-doc -l ansible.builtin | grep -iE 'stat|assert'で探してください。
常に実行されるタスクと、絶対に実行されないタスク
タスクをあと2つ追加してください。어떤 선택에서도 남기는 표식(韓国語で「どの選択でも残す目印」を意味するタスク名)は、/root/anstags/out/always.markerにalwaysの1行を0644で書き込み、タグはalways1つです。함부로 돌면 안 되는 태스크(韓国語で「むやみに実行してはいけないタスク」を意味するタスク名)は、/root/anstags/out/never.markerにdangerの1行を0644で書き込み、タグはneverとdangerの2つです。--tags configで実際に実行して、出力を/root/anstags/out/always.txtに保存してください。어떤 선택에서도 남기는 표식が一緒に実行される必要があります。そして、--list-tasks --tags dangerの出力を/root/anstags/out/danger.txtに保存してください。/root/anstags/out/never.markerは、このラボが終わるまで作成されてはいけません。
特殊なタグは5つです。always・never・tagged・untagged・all。前の2つが実務で使われます。alwaysは、どの選択からも抜けてはいけないタスク(共通変数の設定、ファクトの収集)に付け、neverは、コードとしては残しつつ、手が滑って実行されてはいけないタスクに付けます。コメントアウトはいつか外されますが、neverは外れません。neverが付いたタスクを呼ぶには、一緒に付けた別のラベルを、直接指定する必要があります。そのタスクは、--list-tasksをそのまま実行したときに、一覧に出てもきません。まず、それから確認してみてください。
importはタグを引き継ぎ、includeは引き継がない
/root/anstags/tasks/common.ymlを作成してください。タスクは2つです。공통 점검 하나(韓国語で「共通点検1」を意味するタスク名)は、common-oneを出力し、タグはありません。공통 점검 둘(韓国語で「共通点検2」を意味するタスク名)は、common-twoを出力し、タグdeepを持ちます。そのあと、site.ymlの最後に、タスクを2つ追加してください。import 로 공통 점검을 끌어온다(韓国語で「importで共通点検を取り込む」を意味するタスク名です。import_tasksでそのファイルを取り込み、タグはimported)と、include 로 공통 점검을 끌어온다(韓国語で「includeで共通点検を取り込む」を意味するタスク名です。include_tasksで同じファイルを取り込み、タグはincluded)です。--list-tasks --tags importedの出力を/root/anstags/out/reuse-import.txtに、--list-tasks --tags includedの出力を/root/anstags/out/reuse-include.txtに保存してください。
2つは同じファイルを取り込みますが、いつ取り込むかが異なります。片方は、プレイブックを読み込む時点にその場所で展開され(静的)、もう片方は、実行中になって初めて割り込みます(動的)。その違いが、タグの継承を分けます。あらかじめ展開されたほうは、タグがタスク1つ1つに付き、実行中に割り込むほうは、文そのものにだけ付きます。2つの一覧ファイルを並べて、取り込んだファイルの中のタスク名が見える側と見えない側を確認してください。これが「エラーなしで何も起こらない」故障の正体です。
途中から実行すると、ハンドラーも動かない
まず、/root/anstags/out/reload.markerを削除してください。そのあと、--start-at-task "배포 번호를 정한다" -e app_env=prod(--start-at-taskの引数は、韓国語で「デプロイ番号を決める」を意味するタスク名です)でプレイブックを実際に実行し、出力を/root/anstags/out/startat.txtに保存してください。終わったあと、/root/anstags/app/app.confの内容と、/root/anstags/out/reload.markerが存在するかどうかを、続けて/root/anstags/out/aftermath.txtに残してください。1行目はconf=<app.conf 의 env 줄>、2行目はmarker=<yes 또는 no>です(プレースホルダーは、app.confのenvの行と、yesまたはnoです)。
--start-at-taskは、その名前のタスクから開始します。長いプレイブックが途中で落ちたときに、1番目から再実行しないために使う道具です。ところが、前のタスクが作成していたはずのものも、前のタスクが送っていたはずのnotifyもない状態で開始します。そのため、failed=0で終わっても、システムはプレイブックが約束した状態ではありません。-e app_env=prodを一緒に指定したのに、設定ファイルがどうなっているか、ハンドラーの目印ができたかを、自分で確認してみてください。このステップの答えは、コマンドではなく、その2つの事実です。
どのタグの選択なら完走できるかを判定するツール
/root/anstags/tag-check.sh <태그목록>を作成してください。その選択でsite.ymlをチェックモードで実行してみて、正常に終われば、1行目にSAFE <태그목록>を出力して0で、終わらなければ、1行目にUNSAFE <태그목록> rc=<종료코드>を出力して1で終了します(プレースホルダーはタグのリストと終了コードです)。そのあと、./tag-check.sh deployと./tag-check.sh prep,deployを順に実行して、2行を/root/anstags/out/tagcheck.txtに続けて保存してください。前者はUNSAFE、後者はSAFEである必要があります。
배포 번호를 기록한다(韓国語で「デプロイ番号を記録する」を意味するタスク名)は、배포 번호를 정한다(韓国語で「デプロイ番号を決める」を意味するタスク名)が決めた値を使います。前を除いて後ろだけを選ぶと、その変数は未定義のままで、それが、部分実行が危険な1つ目の方式です。このツールは、そのリスクを、人の記憶ではなく、コマンドに変えます。判定にチェックモードを使う理由は、本番でもそのまま実行できる必要があるからです。判定するために本当に変更してしまったら、ツールではなく事故です。ゲートが1で終了するので、結果をファイルに集めるとき、シェルがそこで止まらないようにしてください。