TT Lab
はじめる
学ぶ 学習パス コース

CI/CDパイプライン

ブルーグリーン切替とカナリア分析

TT Labで続きを見る

このラボは本物のVM上で動きます

この箱はPodではなく、KubeVirtが起動した仮想マシンです。Linuxカーネルが別に動き、systemdが実際にサービスを管理し、dockerは模倣ではなく本物のDockerエンジンです。docker runで起動したコンテナは実際にプロセスになり、docker execもdocker logsもそのまま動作します。

以前はこのラボがPodの中で動いていました。カーネル権限をすべて手放した箱だったので、コンテナを起動するステップが塞がれており、イメージのアーカイブを自分で展開してみる回り道で学んでいました。もう回り道は必要ありません。

知っておくことが2つあります。

目標

コンテナ2つでブルーグリーン環境を作り、トラフィックの切り替え・ヘルスチェック・自動ロールバック・カナリアの割合の振り分け・自動中止の判定をシェルで実装します。

なぜ重要なのか

デプロイ戦略の違いは、結局のところ、ロールバックの速度とトラフィック制御の精度の交換です。ブルーグリーンは2つの環境を同時に起動するので、リソースがPodの2倍かかりますが、問題が見えたらトラフィックをまるごとすぐに元へ戻せます。カナリアは1%単位で精密にさらせますが、戻すときにはステップが必要です。そのため、ほとんどのサービスにはカナリアを、決済や認証のように1件のエラーでもコストが大きいサービスにはブルーグリーンを使います。このラボでは、アクティブな対象がファイルの1行で表現されます。その1行を誰がいつ変え、変えたあとに何を確認し、確認が失敗したらどう元に戻すのかが、デプロイ自動化のすべてです。

ステップ

  1. レスポンスの本文にversion=blueが入るコンテナを、名前ci3-blueで起動し、127.0.0.1:8091に公開してください。curl http://127.0.0.1:8091/でその文字列が見える必要があります。
  2. 同じ方法でci3-greenを127.0.0.1:8092で起動してください。ただし本文はversion=greenにします。このときci3-blueはrunningのままでなければなりません。
  3. /root/ci3/switch.sh <blue|green>を作ってください。アクティブな対象の名前を/root/ci3/activeに書きますが、ACTIVE_FILE環境変数が設定されていればそのパスに書きます。blue/green以外の値なら何も書かず、0以外のコードで終了してください。このステップを終えたとき、/root/ci3/activeの内容はgreenでなければなりません。
  4. /root/ci3/health.sh <URL>を作ってください。対象が正常に応答したら終了コード0、誰もいないか失敗したら0以外のコードを出します。採点は、8091、8092、そして誰もいない8099で確認します。
  5. /root/ci3/deploy.sh <대상>(プレースホルダーは対象です)を作ってください。現在のアクティブな値を記憶しておき、対象に切り替えたあと、その対象のポートでヘルスチェックをします。失敗したらアクティブな値を以前の値に戻し、0以外のコードで終了してください。成功したらアクティブな値は対象のまま残り、終了コードは0です。ポートはPORT_BLUE(既定は8091)、PORT_GREEN(既定は8092)の環境変数で上書きできる必要があり、ACTIVE_FILEにも対応する必要があります。
  6. /root/ci3/canary.sh <퍼센트>(プレースホルダーはパーセントです)を作ってください。リクエストを100件送り、どこへ行ったかを数えてblue=<수> green=<수>(プレースホルダーは件数です)を出力します。2つの数の合計は常に100で、引数が0ならblue=100 green=0、100ならblue=0 green=100でなければなりません。
  7. /root/ci3/analyze.sh <지표파일> <임계퍼센트>(プレースホルダーはメトリクスファイルとしきい値のパーセントです)を作ってください。メトリクスファイルには、total=1000とerrors=10が1行ずつ入っています。エラー率はerrors * 100 / totalで、しきい値以下なら終了コード0、超えたら実際のエラー率を出力して0以外のコードを出してください。total=0ならサンプルがないので、通過させず、失敗として扱ってください。
  8. /root/ci3/deploy-report.jsonを作ってください。フィールドはstrategy(blue-greenまたはcanary)、active(現在の/root/ci3/activeの内容と同じでなければなりません)、previous(activeと違っていなければなりません)、rollback_used、error_rate_pct、abort_threshold_pctです。rollback_usedには、今回のデプロイで実際にロールバックが起きたかどうかを、JSONの真偽値(trueまたはfalse)で書いてください。文字列"true"は使えません。error_rate_pctはabort_threshold_pct以下でなければなりません。

参考

blue環境を起動する

レスポンスの本文にversion=blueが入るコンテナを、名前ci3-blueで起動し、127.0.0.1:8091に公開してください。curl http://127.0.0.1:8091/でその文字列が見える必要があります。

コンテナ名は正確にci3-blue、公開ポートは127.0.0.1:8091です。1024未満のポートはバインドできません。レスポンスの本文にversion=blueが入る必要があるので、index.htmlを作ってマウントする方法が最も簡単です。

green環境を並べて起動する

同じ方法でci3-greenを127.0.0.1:8092で起動してください。ただし本文はversion=greenにします。このときci3-blueはrunningのままでなければなりません。

ci3-greenを8092で起動し、本文はversion=greenです。このときblueを停止してはいけません。2つの環境が同時に生きていてこそ、無停止の切り替えが成り立ちます。

アクティブな対象の切り替えスクリプト

/root/ci3/switch.sh <blue|green>を作ってください。アクティブな対象の名前を/root/ci3/activeに書きますが、ACTIVE_FILE環境変数が設定されていればそのパスに書きます。blue/green以外の値なら何も書かず、0以外のコードで終了してください。このステップを終えたとき、/root/ci3/activeの内容はgreenでなければなりません。

switch.sh <blue|green>で、既定の書き込み先のファイルは/root/ci3/activeです。ACTIVE_FILE環境変数があれば、そのパスを使う必要があります(採点は一時的なパスで検証します)。知らない値なら何も書かずに失敗してください。1文字のタイプミスが、トラフィックを存在しない場所へ送ります。

ヘルスチェック

/root/ci3/health.sh <URL>を作ってください。対象が正常に応答したら終了コード0、誰もいないか失敗したら0以外のコードを出します。採点は、8091、8092、そして誰もいない8099で確認します。

health.sh <URL>は、生きていれば0、そうでなければ0以外のコードです。curl -fsS -m 3のように、失敗時にエラーになり、タイムアウトがある形で呼び出してください。常に通過するヘルスチェックは、ないのと同じです。

失敗したら自動でロールバックする

/root/ci3/deploy.sh <대상>(プレースホルダーは対象です)を作ってください。現在のアクティブな値を記憶しておき、対象に切り替えたあと、その対象のポートでヘルスチェックをします。失敗したらアクティブな値を以前の値に戻し、0以外のコードで終了してください。成功したらアクティブな値は対象のまま残り、終了コードは0です。ポートはPORT_BLUE(既定は8091)、PORT_GREEN(既定は8092)の環境変数で上書きできる必要があり、ACTIVE_FILEにも対応する必要があります。

deploy.sh <대상>(プレースホルダーは対象です)は、現在のアクティブな値を先に記憶しておかないと元に戻せません。ポートはPORT_BLUE(既定は8091)、PORT_GREEN(既定は8092)で上書きできる必要があり、ACTIVE_FILEにも対応する必要があります。ヘルスチェックが失敗したら、以前の値に復元し、0以外の終了コードを返します。

割合の振り分け

/root/ci3/canary.sh <퍼센트>(プレースホルダーはパーセントです)を作ってください。リクエストを100件送り、どこへ行ったかを数えてblue=<수> green=<수>(プレースホルダーは件数です)を出力します。2つの数の合計は常に100で、引数が0ならblue=100 green=0、100ならblue=0 green=100でなければなりません。

canary.sh <퍼센트>(プレースホルダーはパーセントです)は、リクエストを100件送り、blue=<수> green=<수>(プレースホルダーは件数です)を出力します。合計は常に100でなければならないので、リクエストの失敗をそのまま見逃してはいけません。乱数より連番に基づく振り分けのほうが、再現性が高いです。

自動中止の判定

/root/ci3/analyze.sh <지표파일> <임계퍼센트>(プレースホルダーはメトリクスファイルとしきい値のパーセントです)を作ってください。メトリクスファイルには、total=1000とerrors=10が1行ずつ入っています。エラー率はerrors * 100 / totalで、しきい値以下なら終了コード0、超えたら実際のエラー率を出力して0以外のコードを出してください。total=0ならサンプルがないので、通過させず、失敗として扱ってください。

analyze.sh <지표파일> <임계퍼센트>(プレースホルダーはメトリクスファイルとしきい値のパーセントです)で、メトリクスファイルはtotal=1000とerrors=10の2行です。しきい値と同じなら通過です。totalが0ならサンプルがなく判断できないので、通過させてはいけません。中止するときは、実際のエラー率を出力してください。

デプロイレポート

/root/ci3/deploy-report.jsonを作ってください。フィールドはstrategy(blue-greenまたはcanary)、active(現在の/root/ci3/activeの内容と同じでなければなりません)、previous(activeと違っていなければなりません)、rollback_used、error_rate_pct、abort_threshold_pctです。rollback_usedには、今回のデプロイで実際にロールバックが起きたかどうかを、JSONの真偽値(trueまたはfalse)で書いてください。文字列"true"は使えません。error_rate_pctはabort_threshold_pct以下でなければなりません。

/root/ci3/deploy-report.jsonのactiveは/root/ci3/activeの内容と同じで、previousは違っている必要があります。rollback_usedには、今回のデプロイで実際にロールバックが起きたかどうかを、真か偽かで書きます。引用符のないJSONの真偽値でなければなりません。error_rate_pctはabort_threshold_pct以下でなければなりません。