CircleCIでblue-greenデプロイメントを設定する方法
このチュートリアルで学ぶ内容:
- 前提条件
- Kubernetesでのblue-greenデプロイメントの仕組み
- ステップ1 — サンプルアプリケーションのビルドとコンテナ化
- ステップ2 — Kubernetesマニフェストの作成
- ステップ3 — 承認ゲート付きCircleCIパイプラインの作成
- ステップ4 — 切り替え前にgreen環境を検証する
- ステップ5 — データベースマイグレーションを安全に処理する
- ステップ6 — 即時ロールバックの設定
- ステップ7 — リリースの監視と確認
- CircleCIがblue-greenデプロイメントを可能にする仕組み
- blue-greenを使うべき場面と使わない方が良い場面
- まとめ
blue-greenデプロイメントは、同一の本番環境を2つ実行し、新しいバージョンをリリースする際にトラフィックを切り替える手法です。
1つの環境が「アクティブ」となり、すべてのトラフィックを処理します。もう一方はアイドル状態です。デプロイ時には、新しいバージョンをアイドル環境にプッシュし、検証した後でトラフィックを切り替えます。問題が発生した場合、トラフィックは数秒で元に戻せます。Podの再起動は発生せず、ロールバックのためのローリングアップデートも必要ありません。
CI/CDワークフロー(コードの変更が自動的にビルド・テスト・デプロイされる仕組み)では、blue-greenはデプロイステップとリリースステップを明確に分離します。CircleCIは各ステージを自動化します。イメージのビルド、アイドル環境へのデプロイ、検証テストの実行、承認ゲートでの待機、人間による確認後のトラフィック切り替えです。
このチュートリアルでは、Kubernetes上でblue-greenデプロイメントをCircleCIパイプライン内で自動化する方法を解説します。デュアル環境マニフェスト、切り替え前検証用のプレビューService、承認ゲート付きワークフロー、デプロイ追跡、即時ロールバック、およびblue-greenで最もよく発生する障害を防ぐデータベースマイグレーションパターンをカバーします。
前提条件
開始前に、チームは以下を用意する必要があります:
- CircleCIアカウント(無料プランで利用可能)。
kubectlが設定済みのKubernetesクラスタ(このチュートリアルではGKEを使用しますが、EKS、AKS、minikubeやkindなどのローカルクラスタも利用可能です)。- サンプルイメージのビルド用にローカルにインストールされたDocker。
- コンテナレジストリ用のDocker Hubアカウント。
- GitHubからクローンしたサンプルリポジトリ: CIRCLECI-GWP/kubernetes-blue-green-deployment
Kubernetesでのblue-greenデプロイメントの仕組み
2つのKubernetes Deploymentが並行して存在します。1つはversion: blueラベル、もう1つはversion: greenラベルが付けられています。Serviceはセレクタに一致するDeploymentにトラフィックをルーティングします。一度にアクティブになるのは1つのみで、もう一方は次のリリースまでアイドル状態で待機します。
blueからgreenへの切り替えは、Serviceのセレクタをversion: blueからversion: greenにパッチすることで行います。これはメタデータの変更であり、Pod操作ではありません。トラフィックは即座に切り替わります。Podの作成・終了・再起動は発生しません。
切り替え前に、チームはアイドル環境を検証できます。独立した「プレビュー」Serviceは常にgreen Deploymentを指しており、パイプラインが新しいバージョンに対してスモークテスト、統合テスト、負荷テストを実行するためのエンドポイントを提供します。green Podは本番インフラストラクチャ上で本番データベースにアクセスして実行されるため、ここでのテストはステージング環境では検出できない問題を発見できます。
両方のDeploymentは同じデータベースと外部サービスを共有します。これがblue-greenの障害の主な原因です。greenコードでは動作するがblueコードでは破綻するスキーマ変更は、ロールバックを不可能にします。ステップ5では、これを防ぐマイグレーションパターンを解説します。
トレードオフ: blue-greenはデプロイウィンドウ中に両方の環境が完全なレプリカセットで実行されるため、コンピュートリソースが2倍必要になります。このコストが問題となるチームには、ローリングデプロイメントが重複環境なしでPodを段階的に更新する選択肢となります。
ステップ1 — サンプルアプリケーションのビルドとコンテナ化
サンプルアプリケーションは依存関係のないNode.js HTTPサーバーです。3つのエンドポイントを提供します:
GET /は、実行中のバージョン、デプロイスロット(blueまたはgreen)、Podのホスト名、タイムスタンプを表示するスタイル付きHTMLステータスページを返します。GET /apiは同じ情報をJSONで返します。GET /healthはreadinessおよびlivenessプローブ用に{"status":"ok"}を返します。
バージョンはAPP_VERSION環境変数から取得し、スロットはDEPLOY_SLOTから取得します。両方ともKubernetesマニフェストがデプロイ時に設定します。GET /apiをblueとgreen環境で実行すると異なるslot値が返るため、切り替えが視認できます。
完全なソースはサンプルリポジトリにあります。Dockerfileはnode:20-alpineを使用し、非rootユーザーで実行します:
FROM node:20-alpine
WORKDIR /app
COPY package.json .
COPY index.js .
EXPOSE 3000
USER node
CMD ["node", "index.js"]
プッシュ前にローカルでビルドとテストを行います:
docker build -t blue-green-tutorial-app:local ./app
docker run -p 3000:3000 -e DEPLOY_SLOT=blue blue-green-tutorial-app:local
# 別のターミナルで:
curl localhost:3000/api
# {"version":"1.0.0","slot":"blue","hostname":"...","timestamp":"..."}
curl localhost:3000/health
# {"status":"ok"}
ステップ2 — Kubernetesマニフェストの作成
Kubernetes上のblue-greenには4つのマニフェストが必要です。2つのDeployment(環境ごとに1つ)と2つのService(本番用とプレビュー用)です。4つすべてでenvsubstプレースホルダーを使用し、CircleCIパイプラインがデプロイ時に値を埋め込みます。envsubstはファイル内の${VARIABLE}参照を環境変数の値で置換するため、パイプラインは適用前にイメージタグとDocker Hubユーザー名を注入できます。
Blue Deployment
k8s/deployment-blue.yml内のblue Deploymentは現在の本番バージョンを実行します。version: blueラベルにより、green Podと区別されます。
apiVersion: apps/v1
kind: Deployment
metadata:
name: blue-green-tutorial-app-blue
namespace: default
annotations:
circleci.com/project-id: ${CIRCLE_PROJECT_ID}
circleci.com/operation-timeout: 10m
labels:
app: blue-green-tutorial-app
version: blue
circleci.com/component-name: blue-green-tutorial-app
circleci.com/version: ${IMAGE_TAG}
spec:
replicas: 3
selector:
matchLabels:
app: blue-green-tutorial-app
version: blue
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: blue-green-tutorial-app
version: blue
circleci.com/component-name: blue-green-tutorial-app
circleci.com/version: ${IMAGE_TAG}
spec:
containers:
- name: blue-green-tutorial-app
image: ${DOCKERHUB_USERNAME}/blue-green-tutorial-app:${IMAGE_TAG}
ports:
- containerPort: 3000
env:
- name: APP_VERSION
value: "${IMAGE_TAG}"
- name: DEPLOY_SLOT
value: "blue"
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 3
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 15
periodSeconds: 10
注目すべき設定:
version: blueラベル。これはselector.matchLabels、Podテンプレート、本番Serviceのセレクタに登場します。これによりblue Podが独立したセットとして分離され、Serviceが特定してターゲットにできます。maxUnavailable: 0とmaxSurge: 1。パイプラインがgreen Deploymentに新しいイメージを適用する際、置き換えが準備できるまでPodはオフラインにならず、一度に余分に実行されるPodは1つだけです。circleci.com/*ラベルとアノテーション。CircleCIのデプロイ追跡は実行中のPodからこれらを読み取ります。これらはKubernetesが不変と扱うspec.selector.matchLabelsには含めないでください。完全なリファレンスはコンポーネント設定ドキュメントを参照してください。
k8s/deployment-green.yml内のgreen Deploymentは、すべてのblue参照をgreenに置き換えた以外は同一です(メタデータ名、versionラベル、DEPLOY_SLOT値)。
本番Service
k8s/service.yml内の本番Serviceは、アクティブな環境にトラフィックをルーティングします。初期状態ではversion: blueを選択します:
apiVersion: v1
kind: Service
metadata:
name: blue-green-tutorial-app
namespace: default
spec:
selector:
app: blue-green-tutorial-app
version: blue
ports:
- port: 80
targetPort: 3000
type: LoadBalancer
トラフィックの切り替えは、このServiceのセレクタをversion: blueからversion: greenにパッチすることで行います。この1つの変更により、すべての本番トラフィックがgreen Podにリダイレクトされます。
プレビューService
k8s/service-preview.yml内のプレビューServiceは常にgreen環境を指します。新しいバージョンを本番切り替え前に検証するためのエンドポイントをパイプラインに提供します:
apiVersion: v1
kind: Service
metadata:
name: blue-green-tutorial-app-preview
namespace: default
spec:
selector:
app: blue-green-tutorial-app
version: green
ports:
- port: 80
targetPort: 3000
type: LoadBalancer
クラスタの作成
このチュートリアルではクラスタにGKEを使用しますが、任意のKubernetesクラスタで動作します。マニフェストとパイプラインはクラウド非依存です。EKS、AKS、DigitalOcean、minikubeやkindなどのローカルクラスタでも同じワークフローが実行できます。
GKEの場合、1つのgcloudコマンドでクラスタを作成します:
gcloud container clusters create blue-green-tutorial \
--zone=us-central1-a \
--num-nodes=3 \
--machine-type=e2-medium
次に新しいクラスタに接続するようkubectlを設定します:
gcloud container clusters get-credentials blue-green-tutorial --zone=us-central1-a
get-credentialsはクラスタのエンドポイントと認証情報を~/.kube/configに書き込み、アクティブなコンテキストとして設定します。以降、すべてのkubectlコマンドはこの新しいクラスタをターゲットにします。別途接続手順は必要ありません。
Google CloudのGKEクイックスタートには、プロジェクト設定、認証、課金を含む完全なセットアップ手順が記載されています。他のプロバイダーの場合は、それぞれの同等ドキュメントに従ってください。パターンは同じです。クラスタをプロビジョニングし、kubectlをそれに向けます。
進む前に接続を確認します:
kubectl get nodes
ノードが返ってきたら、このチュートリアルの残りはクラウド非依存です。以降のすべてのコマンドは、クラスタの場所に関係なく同じように動作します。
初期セットアップ
最初のパイプライン実行前に、両方のDeploymentと両方のServiceを手動で適用します:
export IMAGE_TAG=initial
export DOCKERHUB_USERNAME=<your-dockerhub-username>
export CIRCLE_PROJECT_ID=<your-circleci-project-id>
envsubst < k8s/deployment-blue.yml | kubectl apply -f -
envsubst < k8s/deployment-green.yml | kubectl apply -f -
kubectl apply -f k8s/service.yml
kubectl apply -f k8s/service-preview.yml
両方の環境が同じイメージで開始します。以降、green環境への更新はすべてパイプラインが処理します。
ステップ3 — 承認ゲート付きCircleCIパイプラインの作成
リポジトリの接続
CircleCIで左サイドバーのProjectsをクリックし、リポジトリを見つけてSet Up Projectをクリックします。Fastestオプションを選択して既存の.circleci/config.ymlを使用します。詳細な手順はCircleCIのGetting startedを参照してください。
コンテキストの作成
パイプラインにはリポジトリに保存すべきでないシークレットが必要です。CircleCIのcontextsは、ジョブ実行時に利用可能な環境変数を保存します。blue-green-tutorialという名前のコンテキストを作成します:
- CircleCIでOrganization Settings > Contextsに移動します。
- Create Contextをクリックし、
blue-green-tutorialと命名します。 - 3つの環境変数を追加します:
| 変数 | 値 |
|---|---|
DOCKERHUB_USERNAME |
Docker Hubユーザー名 |
DOCKERHUB_PASSWORD |
Docker Hubパスワードまたはアクセストークン |
KUBECONFIG_DATA |
Base64エンコードされたクラスタのkubeconfig |
GKE用のKUBECONFIG_DATAの生成方法:
gcloud container clusters get-credentials <cluster-name> --zone <zone> --project <project-id>
cat ~/.kube/config | base64 | tr -d '[:space:]'
EKS、AKS、その他のプロバイダーの場合は、プロバイダーのドキュメントに従ってkubectlアクセスを設定し、得られたkubeconfigを同じ方法でエンコードしてください。
パイプライン設定
パイプラインは線形ワークフロー内の5つのジョブで構成されます。ここがblue-greenとローリングの違いです。1ステップでデプロイとリリースを行うのではなく、検証ゲートで分離します。
version: 2.1
commands:
setup-kubectl:
description: Install kubectl and configure cluster access
steps:
- run:
name: Install kubectl
command: |
KUBECTL_VERSION=$(curl -L -s https://dl.k8s.io/release/stable.txt)
curl -LO "https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/amd64/kubectl"
chmod +x kubectl
sudo mv kubectl /usr/local/bin/kubectl
- run:
name: Configure kubeconfig
command: |
mkdir -p ~/.kube
echo "$KUBECONFIG_DATA" | tr -d '[:space:]' | base64 --decode > ~/.kube/config
chmod 600 ~/.kube/config
jobs:
build-and-push:
docker:
- image: cimg/base:stable
steps:
- checkout
- setup_remote_docker:
docker_layer_caching: true
- run:
name: Build Docker image
command: |
docker build -t $DOCKERHUB_USERNAME/blue-green-tutorial-app:$CIRCLE_SHA1 ./app
- run:
name: Push to Docker Hub
command: |
echo "$DOCKERHUB_PASSWORD" | docker login -u "$DOCKERHUB_USERNAME" --password-stdin
docker push $DOCKERHUB_USERNAME/blue-green-tutorial-app:$CIRCLE_SHA1
deploy-to-green:
docker:
- image: cimg/base:stable
steps:
- checkout
- setup-kubectl
- run:
name: Install envsubst
command: sudo apt-get update -qq && sudo apt-get install -y gettext-base
- run:
name: Deploy to green environment
command: |
export IMAGE_TAG=$CIRCLE_SHA1
envsubst < k8s/deployment-green.yml | kubectl apply -f -
kubectl apply -f k8s/service.yml
kubectl apply -f k8s/service-preview.yml
- run:
name: Wait for green rollout
command: kubectl rollout status deployment/blue-green-tutorial-app-green --timeout=5m
validate-green:
docker:
- image: cimg/base:stable
steps:
- setup-kubectl
- run:
name: Get preview service endpoint
command: |
for i in $(seq 1 30); do
PREVIEW_IP=$(kubectl get svc blue-green-tutorial-app-preview \
-o jsonpath='{.status.loadBalancer.ingress[0].ip}')
if [ -n "$PREVIEW_IP" ]; then
echo "Preview IP: $PREVIEW_IP"
echo "export PREVIEW_IP=$PREVIEW_IP" >> "$BASH_ENV"
break
fi
sleep 10
done
if [ -z "$PREVIEW_IP" ]; then
echo "Preview service did not receive an external IP"
exit 1
fi
- run:
name: Smoke test - health check
command: |
curl -sf http://$PREVIEW_IP/health | grep -q '"status":"ok"'
- run:
name: Smoke test - version check
command: |
curl -sf http://$PREVIEW_IP/api | grep -q "$CIRCLE_SHA1"
- run:
name: Smoke test - slot check
command: |
curl -sf http://$PREVIEW_IP/api | grep -q '"slot":"green"'
switch-traffic:
docker:
- image: cimg/base:stable
steps:
- setup-kubectl
- run:
name: Plan deployment
command: |
circleci run release plan "${CIRCLE_JOB}" \
--environment-name="production" \
--component-name="blue-green-tutorial-app" \
--target-version="$CIRCLE_SHA1"
- run:
name: Switch production traffic to green
command: |
kubectl patch svc blue-green-tutorial-app \
-p '{"spec":{"selector":{"version":"green"}}}'
- run:
name: Update deployment status to running
command: circleci run release update "${CIRCLE_JOB}" --status=RUNNING
- run:
name: Log deploy marker
command: |
circleci run release log \
--component-name=blue-green-tutorial-app \
--environment-name=production \
--target-version=$CIRCLE_SHA1
- run:
name: Update deployment status to success
command: circleci run release update "${CIRCLE_JOB}" --status=SUCCESS
when: on_success
- run:
name: Update deployment status to failed
command: circleci run release update "${CIRCLE_JOB}" --status=FAILED
when: on_fail
workflows:
build-deploy:
jobs:
- build-and-push:
context: blue-green-tutorial
- deploy-to-green:
requires:
- build-and-push
context: blue-green-tutorial
filters:
branches:
only: main
- validate-green:
requires:
- deploy-to-green
context: blue-green-tutorial
- hold-for-approval:
type: approval
requires:
- validate-green
- switch-traffic:
requires:
- hold-for-approval
context: blue-green-tutorial
各ジョブの解説:
build-and-pushはDockerイメージをビルドし、GitコミットSHA($CIRCLE_SHA1)でタグ付けします。すべてのイメージは、それを生成した正確なコミットにトレースできます。
deploy-to-greenは新しいイメージタグでgreen Deploymentマニフェストと両方のServiceマニフェストを適用します。その後、greenのロールアウト完了を待機します。この時点で新しいコードはgreen環境で実行されていますが、本番トラフィックはまだblueに向かっています。
validate-greenはプレビューServiceに対してスモークテストを実行します。ヘルスエンドポイントが応答すること、APIが期待されるバージョンを返すこと、レスポンスがgreenスロットから来ていることを確認します。いずれかのテストが失敗するとジョブは失敗し、承認ゲートは表示されません。本番はblueのままです。
hold-for-approvalはCircleCIの手動承認ジョブです。パイプラインはここで一時停止します。エンジニアは検証結果を確認し、必要に応じてプレビューエンドポイントに対して追加の手動チェックを実行し、CircleCI UIで「Approve」をクリックして続行します。これはコードのデプロイとユーザーへのリリースの間の人間によるチェックポイントです。
switch-trafficは本番Serviceのセレクタをversion: blueからversion: greenにパッチします。トラフィックは即座に切り替わります。これがリリースの瞬間です。
このジョブはまた、デプロイマーカーを書き込みます。release planは作業開始前に計画されたデプロイを登録します。切り替え後、release update --status=RUNNINGはアクティブであることをマークし、release logはイベントを記録します。最後のrelease updateはSUCCESSまたはFAILEDでライフサイクルを閉じます。これらのマーカーはCircleCIのDeploysダッシュボードにフィードされ、各リリースはパイプラインとコミットにリンクされたタイムラインエントリとして表示されます。
デプロイチェーンはmainでのみ実行されます。プルリクエストビルドはbuild-and-pushで停止し、デプロイはトリガーされません。
ステップ4 — 切り替え前にgreen環境を検証する
validate-greenジョブはプレビューServiceエンドポイントに対して3つのチェックを実行します:
- ヘルスチェック:
curl -sf http://$PREVIEW_IP/healthが{"status":"ok"}を返します。 - バージョンチェック:
/apiレスポンスに期待されるコミットSHAが含まれます。 - スロットチェック:
/apiレスポンスが"slot":"green"であることを確認します。
これらは最低限です。実際の本番セットアップでは、重要なAPIエンドポイントをカバーするスモークテストスイート、プレビューエンドポイントに対する軽量負荷テスト、アプリケーションが必要とするデータベース接続やマイグレーションチェックを追加します。プレビューServiceは安定したターゲットを提供するため、検証ジョブはHTTPリクエストを実行する任意の処理を実行できます。
テストスイートが何であれ、本番インフラストラクチャに対して実行されます。コネクションプール制限、実際のデータエッジケース、本番限定の設定値による障害は、トラフィックが切り替わる前にここで表面化します。
検証が失敗した場合、パイプラインは停止します。承認ゲートは表示されません。本番トラフィックはblueのままです。チームはgreen環境を調査し、kubectl logs -l version=greenでログを確認し、次のパイプライン実行前に問題を修正できます。
パイプラインが実行され、トラフィックがgreenに切り替わると、アプリケーションはデプロイを確認するステータスページを提供します:
ステップ5 — データベースマイグレーションを安全に処理する
expand-migrate-contractパターンは、blue-greenパイプラインを通じてスキーマ変更をどちらのバージョンのアプリも破綻させることなくリリースできるようにします。3つのフェーズで動作します:
Expandフェーズ。既存の列やテーブルと並行して新しい列やテーブルを追加します。何も削除しません。このスキーマ変更をアプリケーション変更より前にデプロイします。現在のblueコードは依存していたものが削除されていないため動作し続けます。新しいgreenコードは新しい構造が存在するため動作します。
Migrateフェーズ。古い構造から新しい構造へデータをバックフィルします。これはバックグラウンドジョブとして、またはスキーマexpand適用後のパイプラインステップとして実行できます。
Contractフェーズ。すべてのトラフィックがgreenバージョンで実行され、blue環境がロールバックに不要であることが確認された後、古い列を削除します。これは別個のデプロイとして、数日または数週間後に実行されます。
例として、user_name列をusernameにリネームする場合の流れは次のようになります:
| フェーズ | スキーマ状態 | Blueコード | Greenコード |
|---|---|---|---|
| Before | user_nameが存在 |
user_nameを読み取り |
— |
| Expand | 両方の列が存在、トリガーが同期 | user_nameを読み取り |
usernameを読み取り |
| Switch | 両方の列が存在 | まだ実行中、user_nameを読み取り |
アクティブ、usernameを読み取り |
| Contract | usernameのみが存在 |
スケールダウン | usernameを読み取り |
全体を通じての制約はN-1互換性です。新しいアプリバージョンは古いスキーマと新しいスキーマの両方で動作する必要があります。古いアプリバージョンも新しいスキーマで動作する必要があります。いずれかの互換性が破綻すると、ロールバックは失敗します。
スキーママイグレーションはdeploy-to-greenジョブより前にパイプライン内の別ジョブとして実行し、新しいコードが到達する前にデータベースを準備します。
ステップ6 — 即時ロールバックの設定
ロールバックは本番Serviceのセレクタをversion: blueに戻すパッチを適用します。トラフィックは即座に以前のバージョンに戻ります。Podの再起動、新しいReplicaSet、逆方向のローリングアップデートは発生しません。blue Deploymentは以前のイメージでまだ実行されているため、切り替えは数秒で効果を発揮するネットワークレベルの変更です。
.circleci/rollback.yml内のロールバックパイプラインは、CircleCI Deploysダッシュボードからこれを自動化します:
version: 2.1
commands:
setup-kubectl:
description: Install kubectl and configure cluster access
steps:
- run:
name: Install kubectl
command: |
KUBECTL_VERSION=$(curl -L -s https://dl.k8s.io/release/stable.txt)
curl -LO "https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/amd64/kubectl"
chmod +x kubectl && sudo mv kubectl /usr/local/bin/kubectl
- run:
name: Configure kubeconfig
command: |
mkdir -p ~/.kube
echo "$KUBECONFIG_DATA" | tr -d '[:space:]' | base64 --decode > ~/.kube/config
chmod 600 ~/.kube/config
jobs:
rollback:
docker:
- image: cimg/base:stable
environment:
COMPONENT_NAME: << pipeline.deploy.component_name >>
ENVIRONMENT_NAME: << pipeline.deploy.environment_name >>
TARGET_VERSION: << pipeline.deploy.target_version >>
steps:
- setup-kubectl
- run:
name: Plan rollback release
command: |
circleci run release plan "${CIRCLE_JOB}" \
--component-name=${COMPONENT_NAME} \
--environment-name=${ENVIRONMENT_NAME} \
--target-version=${TARGET_VERSION} \
--rollback
- run:
name: Switch production traffic back to blue
command: |
kubectl patch svc blue-green-tutorial-app \
-p '{"spec":{"selector":{"version":"blue"}}}'
- run:
name: Verify rollback
command: |
PROD_IP=$(kubectl get svc blue-green-tutorial-app \
-o jsonpath='{.status.loadBalancer.ingress[0].ip}')
curl -sf http://$PROD_IP/api | grep -q '"slot":"blue"'
- run:
name: Update rollback status to SUCCESS
command: circleci run release update "${CIRCLE_JOB}" --status=SUCCESS
when: on_success
- run:
name: Update rollback status to FAILED
command: circleci run release update "${CIRCLE_JOB}" --status=FAILED
when: on_fail
cancel-rollback:
docker:
- image: cimg/base:stable
steps:
- run:
name: Update rollback status to CANCELED
command: circleci run release update "${CIRCLE_JOB}" --status=CANCELED
workflows:
rollback:
jobs:
- rollback:
context: blue-green-tutorial
- cancel-rollback:
context: blue-green-tutorial
requires:
- rollback:
- canceled
留意点:
pipeline.deploy.*パラメータ。CircleCIがロールバックをトリガーした際に自動的に注入されます。release plan --rollback。ダッシュボードで通常のデプロイと区別します。cancel-rollbackジョブ。明示的なキャンセル時のみ起動し、古い「進行中」エントリを防止します。
ロールバックパイプラインの接続
ステップ3のデプロイマーカーをmainにコミットした状態で、プロジェクトのOverviewページからRollback > Create rollback pipelineをクリックします。ウィザードは4つのステップを処理します:
- GitHub Appのインストール。この機能はGitHub限定です。必要に応じてウィザードがAppをインストールします。
- パイプライン定義。プロジェクトのリポジトリを選択します。
- デプロイマーカー。
switch-trafficジョブに既に含まれているため、I’ll make the config changes myselfを選択し、次にI’ve updated my configを選択します。 - Rollback設定。
.circleci/rollback.ymlを使用するためI already have a rollback configを選択し、Setup rollback pipelineをクリックします。
これでロールバックはプロジェクトOverviewページからトリガー可能になります。パイプラインの切り替えについては、rollback pipeline documentationのProject Settings > Deploysを参照してください。
ステップ7 — リリースの監視と確認
トラフィック切り替え後、切り替え前後のウィンドウでエラー率とレイテンシを比較します。切り替え直後にエラーが急増した場合、通常はgreenコードが検証テストでカバーされていなかった本番条件に遭遇したことを意味します。
データベースクエリパターンを監視します。greenコードからの新しいクエリが予期しない負荷を生む可能性があります。コネクションプール使用量とスロークエリログが最初に確認すべき場所です。
各切り替えはCircleCIのDeploysダッシュボードにタイムスタンプが記録されるため、リリースイベントはPrometheus、Datadog、Grafanaのメトリック変化と整合します。
切り替え後、定義されたソーク期間(30分、1時間、チームのリスク許容度による)の間、blue環境を稼働させ続けます。リリースが安定しておりロールバックが不要であると確信できた後にのみスケールダウンします。blue Deploymentがまだ実行されている間は、ロールバックは数秒で完了します。スケールダウンした後は、以前のイメージを再デプロイする必要があります。
両方の環境の現在の状態を確認するには:
kubectl get pods -l app=blue-green-tutorial-app -L version
これによりすべてのPodとそのversionラベルが表示され、どの環境が何を実行しているかが明確になります。
CircleCIがblue-greenデプロイメントを可能にする仕組み
このパイプラインにおけるCircleCIの役割は単にステップを実行することではありません。承認ジョブがblue-greenデプロイをゲート付きリリースに変えるものです。コードはmainへのプッシュごとに本番インフラストラクチャに到達しますが、トラフィックが切り替わるのはエンジニアがCircleCI UIでApproveをクリックしたときだけです。デプロイとリリースの分離が、blue-greenのロールバックを高速にする理由です。以前のバージョンはまだ実行されているため、Serviceセレクタの反転で元に戻せます。デプロイマーカーは各リリースをDeploysダッシュボードで追跡し、ロールバックパイプラインはその同じダッシュボードから1クリックで巻き戻しを可能にします。
blue-greenは多くのデプロイメント戦略の1つです。他の戦略は速度、リスク、インフラストラクチャコストの異なるトレードオフを提供します。CircleCIのプリミティブ(承認ゲート、デプロイマーカー、ロールバックパイプライン)はすべてに適用可能です。
blue-greenを使うべき場面と使わない方が良い場面
blue-greenが適している場合:
- 即時ロールバックが必須要件である場合(金融サービス、eコマースチェックアウト、ヘルスケア)。
- チームがデプロイウィンドウ中に2倍のコンピュートコストを負担できる場合。
- 本番環境での切り替え前検証が重要である場合。
- リリース頻度が低く、ステークが高い場合。
blue-greenが不適切な場合:
- インフラストラクチャ予算が厳しい場合(canaryはリソースのほんの一部を使用)。
- 1日に複数回デプロイする場合(2環境のオーバーヘッドは高頻度では割に合わない)。
- パーセンテージベースのトラフィックシフトとメトリック駆動のプロモーションが必要な場合(これはcanaryの領域)。
戦略の比較については、Kubernetes上のローリングデプロイメントを参照してください。
まとめ
このチュートリアルでは、Kubernetes上で完全なblue-greenパイプラインを構築しました。並行環境へのデプロイ、本番インフラストラクチャに対する検証、人間による承認の待機、トラフィックの切り替え、問題発生時のダッシュボードからのロールバックです。パイプライン全体はリポジトリ内のYAMLとして存在し、CircleCIの無料プランで実行でき、GKE、EKS、AKS、または任意の自己管理Kubernetesクラスタに対して動作します。
構築を開始するには、無料のCircleCIアカウントにサインアップし、パイプライン設定を自身のプロジェクトに適応させてください。
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.