Share via


Azure コンテナー アプリでの Blue-Green デプロイ

ブルーグリーン デプロイは、ダウンタイムを最小限に抑え、アプリケーションの新しいバージョンのデプロイに関連するリスクを軽減することを目的としたソフトウェア リリース戦略です。 ブルーグリーン デプロイでは、"ブルー" と "グリーン" という 2 つの同じ環境が設定されます。 一方の環境 (ブルー) は現在のアプリケーション バージョンを実行し、もう一方の環境 (グリーン) は新しいアプリケーション バージョンを実行します。

グリーン デプロイがテストされると、ライブ トラフィックがそれに送られ、ブルーの環境を使用して次のデプロイ サイクル中に新しいアプリケーション バージョンがデプロイされます。

Azure Container Apps でブルーグリーン デプロイを有効にするには、コンテナー アプリのリビジョントラフィックの重みリビジョン ラベル を組み合わせて使用します。

Screenshot of Azure Container Apps: Blue/Green deployment.

リビジョンを使用して、アプリケーションのブルーとグリーンのバージョンのインスタンスを作成します。

リビジョン 説明
"ブルー" リビジョン blue のラベルが付いたリビジョンは、現在実行中の安定バージョンのアプリケーションです。 このリビジョンが、ユーザーが操作するリビジョンであり、運用トラフィックのターゲットです。
"グリーン" リビジョン green のラベルが付いたリビジョンは、"ブルー" リビジョンのコピーですが、新しいバージョンのアプリ コードと、場合によって新しい環境変数のセットを使用する点が違います。 最初は運用トラフィックを受信しませんが、ラベル付きの完全修飾ドメイン名 (FQDN) を使用してアクセスできます。

新しいリビジョンをテストして確認したら、運用トラフィックを新しいリビジョンにポイントできます。 問題が発生した場合は、以前のバージョンに簡単にロールバックできます。

アクション 説明
テストと検証 "グリーン" リビジョンは、アプリケーションの新しいバージョンが期待どおりに機能することを確認するために徹底的にテストされ、検証されます。 このテストには、機能テスト、パフォーマンス テスト、互換性チェックなど、さまざまなタスクが含まれる場合があります。
トラフィック スイッチ グリーン リビジョンが必要なすべてのテストに合格すると、グリーン リビジョンで運用環境の負荷の提供を開始するようにトラフィック スイッチが実行されます。 このスイッチは制御された方法で行われるため、スムーズな切り替えが保証されます。
ロールバック "グリーン" リビジョンで問題が発生した場合は、トラフィック スイッチを元に戻し、トラフィックを安定した "ブルー" リビジョンにルーティングできます。 このロールバックにより、新しいバージョンに問題がある場合のユーザーへの影響を最小限に抑えることができます。 グリーン リビジョンは、次のデプロイでも引き続き使用できます。
役割の変更 ブルーとグリーンのリビジョンの役割は、"グリーン" リビジョンに正常にデプロイされた後に変更されます。 次のリリース サイクル中、"グリーン" リビジョンは安定した運用環境を表し、新しいバージョンのアプリケーション コードは "ブルー" リビジョンでデプロイされ、テストされます。

この記事では、コンテナー アプリにブルーグリーン デプロイを実装する方法について説明します。 以降の例を実行するには、新しいアプリを作成できるコンテナー アプリ環境が必要です。

Note

Container Apps のブルーグリーン デプロイを実装する GitHub ワークフローの完全な例については、containerapps-blue-green リポジトリに関する記事を参照してください。

複数のアクティブなリビジョンが有効になっているコンテナー アプリを作成する

トラフィックの分割を有効にするには、コンテナー アプリで configuration.activeRevisionsMode プロパティを multiple に設定する必要があります。 決定的なリビジョン名を取得するには、リリースを一意に識別する文字列値に template.revisionSuffix 構成設定を設定します。 たとえば、ビルド番号や、git のコミット ハッシュ (短縮) を使用できます。

次のコマンドでは、一連のコミット ハッシュが使用されています。

export APP_NAME=<APP_NAME>
export APP_ENVIRONMENT_NAME=<APP_ENVIRONMENT_NAME>
export RESOURCE_GROUP=<RESOURCE_GROUP>

# A commitId that is assumed to correspond to the app code currently in production
export BLUE_COMMIT_ID=fb699ef
# A commitId that is assumed to correspond to the new version of the code to be deployed
export GREEN_COMMIT_ID=c6f1515

# create a new app with a new revision
az containerapp create --name $APP_NAME \
  --environment $APP_ENVIRONMENT_NAME \
  --resource-group $RESOURCE_GROUP \
  --image mcr.microsoft.com/k8se/samples/test-app:$BLUE_COMMIT_ID \
  --revision-suffix $BLUE_COMMIT_ID \
  --env-vars REVISION_COMMIT_ID=$BLUE_COMMIT_ID \
  --ingress external \
  --target-port 80 \
  --revisions-mode multiple

# Fix 100% of traffic to the revision
az containerapp ingress traffic set \
  --name $APP_NAME \
  --resource-group $RESOURCE_GROUP \
  --revision-weight $APP_NAME--$BLUE_COMMIT_ID=100

# give that revision a label 'blue'
az containerapp revision label add \
  --name $APP_NAME \
  --resource-group $RESOURCE_GROUP \
  --label blue \
  --revision $APP_NAME--$BLUE_COMMIT_ID

main.bicep という名前のファイルに次のコードを保存します。

targetScope = 'resourceGroup'
param location string = resourceGroup().location

@minLength(1)
@maxLength(64)
@description('Name of containerapp')
param appName string

@minLength(1)
@maxLength(64)
@description('Container environment name')
param containerAppsEnvironmentName string

@minLength(1)
@maxLength(64)
@description('CommitId for blue revision')
param blueCommitId string

@maxLength(64)
@description('CommitId for green revision')
param greenCommitId string = ''

@maxLength(64)
@description('CommitId for the latest deployed revision')
param latestCommitId string = ''

@allowed([
  'blue'
  'green'
])
@description('Name of the label that gets 100% of the traffic')
param productionLabel string = 'blue'

var currentCommitId = !empty(latestCommitId) ? latestCommitId : blueCommitId

resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2022-03-01' existing = {
  name: containerAppsEnvironmentName
}

resource blueGreenDeploymentApp 'Microsoft.App/containerApps@2022-11-01-preview' = {
  name: appName
  location: location
  tags: {
    blueCommitId: blueCommitId
    greenCommitId: greenCommitId
    latestCommitId: currentCommitId
    productionLabel: productionLabel
  }
  properties: {
    environmentId: containerAppsEnvironment.id
    configuration: {
      maxInactiveRevisions: 10 // Remove old inactive revisions
      activeRevisionsMode: 'multiple' // Multiple active revisions mode is required when using traffic weights
      ingress: {
        external: true
        targetPort: 80
        traffic: !empty(blueCommitId) && !empty(greenCommitId) ? [
          {
            revisionName: '${appName}--${blueCommitId}'
            label: 'blue'
            weight: productionLabel == 'blue' ? 100 : 0
          }
          {
            revisionName: '${appName}--${greenCommitId}'
            label: 'green'
            weight: productionLabel == 'green' ? 100 : 0
          }
        ] : [
          {
            revisionName: '${appName}--${blueCommitId}'
            label: 'blue'
            weight: 100
          }
        ]
      }
    }
    template: {
      revisionSuffix: currentCommitId
      containers:[
        {
          image: 'mcr.microsoft.com/k8se/samples/test-app:${currentCommitId}'
          name: appName
          resources: {
            cpu: json('0.5')
            memory: '1.0Gi'
          }
          env: [
            {
              name: 'REVISION_COMMIT_ID'
              value: currentCommitId
            }
          ]
        }
      ]
    }
  }
}

output fqdn string = blueGreenDeploymentApp.properties.configuration.ingress.fqdn
output latestRevisionName string = blueGreenDeploymentApp.properties.latestRevisionName

次のコマンドを使用して、Bicep テンプレートを使用してアプリをデプロイします。

export APP_NAME=<APP_NAME>
export APP_ENVIRONMENT_NAME=<APP_ENVIRONMENT_NAME>
export RESOURCE_GROUP=<RESOURCE_GROUP>

# A commitId that is assumed to belong to the app code currently in production
export BLUE_COMMIT_ID=fb699ef
# A commitId that is assumed to belong to the new version of the code to be deployed
export GREEN_COMMIT_ID=c6f1515

# create a new app with a blue revision
az deployment group create \
    --name createapp-$BLUE_COMMIT_ID \
    --resource-group $RESOURCE_GROUP \
    --template-file main.bicep \
    --parameters appName=$APP_NAME blueCommitId=$BLUE_COMMIT_ID containerAppsEnvironmentName=$APP_ENVIRONMENT_NAME \
    --query properties.outputs.fqdn

新しいリビジョンのデプロイとラベルの割り当て

blue ラベルは現在、アプリの FQDN に到着する運用トラフィックを取得するリビジョンを指しています。 green ラベルは、運用環境にロールアウトされる予定のアプリの新しいバージョンを指しています。 新しいコミット ハッシュは、アプリ コードの新しいバージョンを識別します。 次のコマンドは、そのコミット ハッシュの新しいリビジョンをデプロイし、green ラベルでマークします。

#create a second revision for green commitId
az containerapp update --name $APP_NAME \
  --resource-group $RESOURCE_GROUP \
  --image mcr.microsoft.com/k8se/samples/test-app:$GREEN_COMMIT_ID \
  --revision-suffix $GREEN_COMMIT_ID  \
  --set-env-vars REVISION_COMMIT_ID=$GREEN_COMMIT_ID

#give that revision a 'green' label
az containerapp revision label add \
  --name $APP_NAME \
  --resource-group $RESOURCE_GROUP \
  --label green \
  --revision $APP_NAME--$GREEN_COMMIT_ID
#deploy a new version of the app to green revision
az deployment group create \
    --name deploy-to-green-$GREEN_COMMIT_ID \
    --resource-group $RESOURCE_GROUP \
    --template-file main.bicep \
    --parameters appName=$APP_NAME blueCommitId=$BLUE_COMMIT_ID greenCommitId=$GREEN_COMMIT_ID latestCommitId=$GREEN_COMMIT_ID productionLabel=blue containerAppsEnvironmentName=$APP_ENVIRONMENT_NAME \
    --query properties.outputs.fqdn

次の例は、トラフィック セクションの構成方法を示しています。 bluecommitId のリビジョンでは、運用トラフィックの 100% が発生しますが、greencommitId の新しくデプロイされたリビジョンでは、運用トラフィックは発生しません。

{ 
  "traffic": [
    {
      "revisionName": "<APP_NAME>--fb699ef",
      "weight": 100,
      "label": "blue"
    },
    {
      "revisionName": "<APP_NAME>--c6f1515",
      "weight": 0,
      "label": "green"
    }
  ]
}

新しくデプロイされたリビジョンは、ラベル固有の FQDN を使用してテストできます。

#get the containerapp environment default domain
export APP_DOMAIN=$(az containerapp env show -g $RESOURCE_GROUP -n $APP_ENVIRONMENT_NAME --query properties.defaultDomain -o tsv | tr -d '\r\n')

#Test the production FQDN
curl -s https://$APP_NAME.$APP_DOMAIN/api/env | jq | grep COMMIT

#Test the blue lable FQDN
curl -s https://$APP_NAME---blue.$APP_DOMAIN/api/env | jq | grep COMMIT

#Test the green lable FQDN
curl -s https://$APP_NAME---green.$APP_DOMAIN/api/env | jq | grep COMMIT

運用トラフィックをグリーン リビジョンに送信する

"グリーン" リビジョンのアプリ コードが期待どおりに動作することが確認されると、運用トラフィックの 100% がリビジョンに送信されます。 これで、"グリーン" リビジョンが運用リビジョンになります。

# set 100% of traffic to green revision
az containerapp ingress traffic set \
  --name $APP_NAME \
  --resource-group $RESOURCE_GROUP \
  --label-weight blue=0 green=100

# make green the prod revision
az deployment group create \
    --name make-green-prod-$GREEN_COMMIT_ID \
    --resource-group $RESOURCE_GROUP \
    --template-file main.bicep \
    --parameters appName=$APP_NAME blueCommitId=$BLUE_COMMIT_ID greenCommitId=$GREEN_COMMIT_ID latestCommitId=$GREEN_COMMIT_ID productionLabel=green containerAppsEnvironmentName=$APP_ENVIRONMENT_NAME \
    --query properties.outputs.fqdn

次の例は、このステップの後の、traffic セクションの構成方法を示しています。 新しいアプリケーション コードによる "グリーン" リビジョンは、すべてのユーザー トラフィックを受け取りますが、古いアプリケーション バージョンの "ブルー" リビジョンはユーザー要求を受け入れません。

{ 
  "traffic": [
    {
      "revisionName": "<APP_NAME>--fb699ef",
      "weight": 0,
      "label": "blue"
    },
    {
      "revisionName": "<APP_NAME>--c6f1515",
      "weight": 100,
      "label": "green"
    }
  ]
}

問題が発生した場合にデプロイをロールバックする

運用環境で実行した後、新しいリビジョンにバグがあることが判明した場合は、以前の良好な状態にロールバックできます。 ロールバック後、トラフィックの 100% が "ブルー" リビジョンの古いバージョンに送信され、そのリビジョンが再び運用リビジョンとして指定されます。

# set 100% of traffic to green revision
az containerapp ingress traffic set \
  --name $APP_NAME \
  --resource-group $RESOURCE_GROUP \
  --label-weight blue=100 green=0
# rollback traffic to blue revision
az deployment group create \
    --name rollback-to-blue-$GREEN_COMMIT_ID \
    --resource-group $RESOURCE_GROUP \
    --template-file main.bicep \
    --parameters appName=$APP_NAME blueCommitId=$BLUE_COMMIT_ID greenCommitId=$GREEN_COMMIT_ID latestCommitId=$GREEN_COMMIT_ID productionLabel=blue containerAppsEnvironmentName=$APP_ENVIRONMENT_NAME \
    --query properties.outputs.fqdn

バグが修正されると、新しいバージョンのアプリケーションが再び "グリーン" リビジョンとしてデプロイされます。 最終的には、"グリーン" バージョンが運用リビジョンになります。

次のデプロイ サイクル

これで、現在安定した運用コードを実行しているリビジョンに green ラベルがマークされるようになります。

次のデプロイ サイクルで、blue は、運用環境にロールアウトされる新しいアプリケーション バージョンのリビジョンを識別します。

次のコマンドは、次のデプロイ サイクルの準備を示しています。

# set the new commitId
export BLUE_COMMIT_ID=ad1436b

# create a third revision for blue commitId
az containerapp update --name $APP_NAME \
  --resource-group $RESOURCE_GROUP \
  --image mcr.microsoft.com/k8se/samples/test-app:$BLUE_COMMIT_ID \
  --revision-suffix $BLUE_COMMIT_ID  \
  --set-env-vars REVISION_COMMIT_ID=$BLUE_COMMIT_ID

# give that revision a 'blue' label
az containerapp revision label add \
  --name $APP_NAME \
  --resource-group $RESOURCE_GROUP \
  --label blue \
  --revision $APP_NAME--$BLUE_COMMIT_ID
# set the new commitId
export BLUE_COMMIT_ID=ad1436b

# deploy new version of the app to blue revision
az deployment group create \
    --name deploy-to-blue-$BLUE_COMMIT_ID \
    --resource-group $RESOURCE_GROUP \
    --template-file main.bicep \
    --parameters appName=$APP_NAME blueCommitId=$BLUE_COMMIT_ID greenCommitId=$GREEN_COMMIT_ID latestCommitId=$BLUE_COMMIT_ID productionLabel=green containerAppsEnvironmentName=$APP_ENVIRONMENT_NAME \
    --query properties.outputs.fqdn

次のステップ