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

GitLab CI/CD

障害のとき本番に何が出ていたのか誰も知らなかった

TT Labで続きを見る

目標

固定環境とブランチごとのレビュー環境、環境を閉じるジョブ、手動の本番デプロイを設定し、コミットSHAで残るデプロイ記録と、その記録から選ぶロールバック、デプロイ凍結のルールを、gitlab-ci-localで実際に動かします。

なぜ重要なのか

デプロイは、元に戻せて初めて安全です。何がどこにいつ出たかがenvironmentで残り、出たものがコミットSHAで識別できてはじめて、事故のとき「そのときのもの」に数分で戻れます。レビュー環境は便利ですが、閉じる相方がないと積み上がって、コストと露出になり、本番デプロイの承認は、allow_failureの値1つでゲートにも飾りにもなります。凍結期間のように「今は出さない」という約束も、設定に刻んでこそ守られます。

ステップ

  1. /root/glci-deployをgitリポジトリにし(.gitignoreに.gitlab-ci-local/を置く)、.gitlab-ci.ymlにstages[deploy, cleanup]、グローバル変数DEPLOY_LOG: /root/glci-deploy-history/deploys.log、そしてmainでだけ作られるジョブdeploy-staging(deploy)を置いてください。environmentは名前staging、URLhttps://staging.example.comで、スクリプトはecho "env=$CI_ENVIRONMENT_NAME url=$CI_ENVIRONMENT_URL"です。mainにコミットして実行してください。
  2. main以外のブランチでだけ作られるジョブreview-app(deploy)を追加してください。environmentの名前はreview/$CI_COMMIT_REF_SLUG、URLはhttps://$CI_COMMIT_REF_SLUG.review.example.comで、スクリプトはステップ1と同じechoです。コミットしてください。採点ツールは、コピーでFeature/Login-Pageブランチに移って、名前がreview/feature-login-page、URLがhttps://feature-login-page.review.example.comかを確認します。
  3. review-appのenvironmentにon_stop: stop-reviewとauto_stop_in: 1 dayを追加し、ジョブstop-review(cleanup)を作ってください。同じ環境名でaction: stop、main以外のブランチでwhen: manual、スクリプトはecho "stopping $CI_ENVIRONMENT_NAME"です。コミットしてください。featureブランチで、stop-reviewが一覧にmanualとしてあり、--manual stop-reviewで動かすとstopping review/<슬러그>が出力されるはずです(プレースホルダーはスラッグです)。
  4. mainでwhen: manual、allow_failure: falseとして作られるジョブdeploy-prod(deploy)を追加してください。environmentはproduction、URLはhttps://www.example.com、スクリプトはecho "env=$CI_ENVIRONMENT_NAME"です。コミットしてください。そのまま実行するとdeploy-prodは動かず、--manual deploy-prodを与えると動くはずです。
  5. /root/glci-deploy/scripts/deploy.sh <환경>を作ってください。ROLLBACK_TOが空なら、タグを$CI_COMMIT_SHORT_SHAにして、$DEPLOY_LOGに<환경> registry.example.com/app:<태그> releaseを1行追記し、deployed ...を出力します(プレースホルダーは、順に環境とタグです)。deploy-staging・deploy-prodのスクリプトの最後にbash scripts/deploy.sh staging・bash scripts/deploy.sh productionを追加して、コミットしてください。採点ツールは、コピーで2つのコミットを順にデプロイして、記録がコミットごとに異なるタグで残るかを確認します。
  6. deploy.shがROLLBACK_TOを受け取ったら、新しいタグを作らず、そのタグを出し直すようにしてください。その環境の履歴にregistry.example.com/app:<ROLLBACK_TO>として出た記録がなければ、이력에 없는 태그입니다を出力して失敗します(韓国語の文は「履歴にないタグです」という意味です)。記録の行の最後の語はrollbackです。採点ツールは、コピーで2回デプロイしたあと、最初のタグに戻す実行と、存在しないタグに戻そうとする実行を行います。
  7. deploy-prodのrulesの一番前に、if: $CI_DEPLOY_FREEZEのときwhen: neverを置いてください。コミットしてください。--variable CI_DEPLOY_FREEZE=1で一覧を見るとdeploy-prodがなく、変数がなければmanualとしてあるはずです。deploy-stagingは、凍結と関係なくあるはずです。

参考

デプロイジョブにラベルを付ける

/root/glci-deployをgitリポジトリにし(.gitignoreに.gitlab-ci-local/を置く)、.gitlab-ci.ymlにstages[deploy, cleanup]、グローバル変数DEPLOY_LOG: /root/glci-deploy-history/deploys.log、そしてmainでだけ作られるジョブdeploy-staging(deploy)を置いてください。environmentは名前staging、URLhttps://staging.example.comで、スクリプトはecho "env=$CI_ENVIRONMENT_NAME url=$CI_ENVIRONMENT_URL"です。mainにコミットして実行してください。

environmentを付けたジョブは、GitLabでデプロイとして記録され、環境画面に、何がいつ出たかが残ります。ジョブの中では、CI_ENVIRONMENT_NAMEとCI_ENVIRONMENT_URLで、自分がどこに向かうのかがわかります。

ブランチごとにできるレビュー環境

main以外のブランチでだけ作られるジョブreview-app(deploy)を追加してください。environmentの名前はreview/$CI_COMMIT_REF_SLUG、URLはhttps://$CI_COMMIT_REF_SLUG.review.example.comで、スクリプトはステップ1と同じechoです。コミットしてください。採点ツールは、コピーでFeature/Login-Pageブランチに移って、名前がreview/feature-login-page、URLがhttps://feature-login-page.review.example.comかを確認します。

ブランチ名には、大文字やスラッシュのように、ホスト名に使えない文字が混ざります。CI_COMMIT_REF_SLUGは、小文字に変えて、許可されない文字をハイフンに変えた値なので、URLや環境名にそのまま使えます。

レビュー環境は閉じるジョブと対になる

review-appのenvironmentにon_stop: stop-reviewとauto_stop_in: 1 dayを追加し、ジョブstop-review(cleanup)を作ってください。同じ環境名でaction: stop、main以外のブランチでwhen: manual、スクリプトはecho "stopping $CI_ENVIRONMENT_NAME"です。コミットしてください。featureブランチで、stop-reviewが一覧にmanualとしてあり、--manual stop-reviewで動かすとstopping review/<슬러그>が出力されるはずです(プレースホルダーはスラッグです)。

レビュー環境は、ブランチの数だけ積み上がります。on_stopは「この環境を閉じるジョブはあれ」という結び付けで、ブランチを削除したりマージしたりすると、GitLabがそのジョブを呼び出します。auto_stop_inは、忘れられた環境を、時間が経つと閉じさせます。

本番デプロイは人が押す

mainでwhen: manual、allow_failure: falseとして作られるジョブdeploy-prod(deploy)を追加してください。environmentはproduction、URLはhttps://www.example.com、スクリプトはecho "env=$CI_ENVIRONMENT_NAME"です。コミットしてください。そのまま実行するとdeploy-prodは動かず、--manual deploy-prodを与えると動くはずです。

allow_failure: falseの手動ジョブは、GitLabでは押すまでパイプラインを「ブロック」状態にします。trueだと押さなくてもパイプラインが成功で終わるので、承認ゲートになれません。gitlab-ci-localはこのブロックを再現しないので、一覧のallowFailureの列で確認します。

何が出たかをコミットSHAで残す

/root/glci-deploy/scripts/deploy.sh <환경>を作ってください。ROLLBACK_TOが空なら、タグを$CI_COMMIT_SHORT_SHAにして、$DEPLOY_LOGに<환경> registry.example.com/app:<태그> releaseを1行追記し、deployed ...を出力します(プレースホルダーは、順に環境とタグです)。deploy-staging・deploy-prodのスクリプトの最後にbash scripts/deploy.sh staging・bash scripts/deploy.sh productionを追加して、コミットしてください。採点ツールは、コピーで2つのコミットを順にデプロイして、記録がコミットごとに異なるタグで残るかを確認します。

latestのように動くタグでデプロイすると、事故のとき「そのとき出たもの」を誰も知りません。コミットSHAは、ソース・ビルド・デプロイを1行でつなぐ識別子です。デプロイ記録のパスは変数で受け取り、実行時に変えられるようにしておきます。

ロールバックは履歴にあるタグを出し直すこと

deploy.shがROLLBACK_TOを受け取ったら、新しいタグを作らず、そのタグを出し直すようにしてください。その環境の履歴にregistry.example.com/app:<ROLLBACK_TO>として出た記録がなければ、이력에 없는 태그입니다を出力して失敗します(韓国語の文は「履歴にないタグです」という意味です)。記録の行の最後の語はrollbackです。採点ツールは、コピーで2回デプロイしたあと、最初のタグに戻す実行と、存在しないタグに戻そうとする実行を行います。

ロールバックは、「古いソースでビルドし直すこと」ではなく、「すでに検証されて出たアーティファクトを出し直すこと」です。履歴にないタグを止めておけば、1文字の打ち間違いで、検証されていないイメージが本番に出ることがありません。実行するとき--variable ROLLBACK_TO=<태그>で渡します(プレースホルダーはタグです)。

デプロイ凍結期間には本番デプロイを作らない

deploy-prodのrulesの一番前に、if: $CI_DEPLOY_FREEZEのときwhen: neverを置いてください。コミットしてください。--variable CI_DEPLOY_FREEZE=1で一覧を見るとdeploy-prodがなく、変数がなければmanualとしてあるはずです。deploy-stagingは、凍結と関係なくあるはずです。

GitLabは、プロジェクトに凍結期間を設定しておくと、その時間の間CI_DEPLOY_FREEZEを埋めてくれます。ルールは最初のマッチだけを使うので、凍結のルールがmainのルールより前にある必要があります。凍結を人の記憶に任せず、パイプラインが拒否するようにする仕組みです。