ROMANCE DAWN for the new world

Microsoft Azure を中心とした技術情報を書いています。

Azure Container Apps で Blue-Green Deployments を試してみた

Microsoft Build 2022 で Azure Container Apps が GA されました。東日本リージョンでも使えるようになりましたので、Blue-Green Deployments を試してみました。

Azure Container Apps とは

Azure Container Apps は、複数のコンテナアプリで構成されるシステム向けのサービスです。これまでは Web Apps for Containers か AKS の2択でしたが、新たに Container Apps が追加されたことになります。
docs.microsoft.com

Container Apps は Kubernetes を基盤に構成されています。下記のような Environment / Container App / Revision / Replica の構成要素を理解しておけば OK です。

Microsoft Docs から引用

Container Apps Environment を作成する

Azure Portal では App Service Plan にあたる Environment だけを作成できないため、Bicep を使って Environment を作成します。
environment.bicep では、Environment、LogAnalytics、Application Insights のリソースを定義します。

param environmentName string
param logAnalyticsWorkspaceName string = 'logs-${environmentName}'
param appInsightsName string = 'appins-${environmentName}'
param location string = resourceGroup().location

resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2020-08-01' = {
  name: logAnalyticsWorkspaceName
  location: location
  properties: any({
    retentionInDays: 30
    features: {
      searchVersion: 1
      legacy: 0
      enableLogAccessUsingOnlyResourcePermissions: true
    }
    sku: {
      name: 'PerGB2018'
    }
  })
}

resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
  name: appInsightsName
  location: location
  kind: 'web'
  properties: { 
    Application_Type: 'web'
    WorkspaceResourceId:logAnalyticsWorkspace.id
  }
}

resource environment 'Microsoft.App/managedEnvironments@2022-03-01' = {
  name: environmentName
  location: location
  properties: {
    appLogsConfiguration: {
      destination: 'log-analytics'
      logAnalyticsConfiguration: {
        customerId: logAnalyticsWorkspace.properties.customerId
        sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey
      }
    }
    daprAIInstrumentationKey: appInsights.properties.InstrumentationKey
    zoneRedundant: false
  }
}

output location string = location
output environmentId string = environment.id

main.bicep では、Resource Group 名にプレフィックスをつけて Environment 名を定義します。

param environmentName string = 'env-${resourceGroup().name}'
param location string = resourceGroup().location

module environment 'environment.bicep' = {
  name: 'container-app-environment'
  params: {
    environmentName: environmentName
    location: location
  }
}

Azure CLI を使って、デプロイします。

$ az group create -n <ResourceGroup Name> -l japaneast
$ az deployment group create -f ./deploy/env/main.bicep -g <ResourceGroup Name>

作られたリソースは、こんな感じです。


Container Apps をデプロイする

Container Apps についても、Azure Portal では細かい構成を指定できないので、Bicep を使って作成します。
あらかじめ ASP.NET Core Web アプリケーションの Docker Image を作っておいたので、apps.bicep でデプロイします。

param containerAppName string
param location string = resourceGroup().location
param environmentId string
param containerImage string
param revisionSuffix string
param oldRevisionSuffix string
param isExternalIngress bool

@allowed([
  'multiple'
  'single'
])
param revisionMode string = 'single'

resource containerApp 'Microsoft.App/containerApps@2022-03-01' = {
  name: containerAppName
  location: location
  properties: {
    managedEnvironmentId: environmentId
    configuration: {
      activeRevisionsMode: revisionMode
      ingress: {
        external: isExternalIngress
        targetPort: 80
        transport: 'auto'
        allowInsecure: false
        traffic: ((contains(revisionSuffix, oldRevisionSuffix)) ? [
          {
            weight: 100
            latestRevision: true
          }
        ] : [
          {
            weight: 0
            latestRevision: true
          }
          {
            weight: 100
            revisionName: '${containerAppName}--${oldRevisionSuffix}'
          }
        ])
      }
      dapr:{
        enabled:false
      }
    }
    template: {
      revisionSuffix: revisionSuffix
      containers: [
        {
          image: containerImage
          name: containerAppName
          resources: {
            cpu: '0.25'
            memory: '0.5Gi'
          }
        }
      ]
      scale: {
        minReplicas: 1
        maxReplicas: 10
        rules: [
          {
            name: 'http-scaling-rule'
            http: {
              metadata: {
                concurrentRequests: '10'
              }
            }
          }
        ]
      }
    }
  }
}

output fqdn string = containerApp.properties.configuration.ingress.fqdn

main.bicep では、Blue-Green Deployments を行うため、revisions-mode に multiple を指定します。
新旧 RevisionSuffix に v1 を指定することで、デプロイしたアプリケーションの traffic を 100% にします。

param environmentName string = 'env-${resourceGroup().name}'
param containerAppName string = 'dapr-frontend'
param location string = resourceGroup().location
param containerImage string = 'thara0402/dapr-frontend:0.1.0'
param revisionSuffix string = 'v1'
param oldRevisionSuffix string = 'v1'
param isExternalIngress bool = true
param revisionMode string = 'multiple'

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

module apps 'apps.bicep' = {
  name: 'container-apps'
  params: {
    containerAppName: containerAppName
    location: location
    environmentId: environment.id
    containerImage: containerImage
    revisionSuffix: revisionSuffix
    oldRevisionSuffix: oldRevisionSuffix
    revisionMode: revisionMode
    isExternalIngress: isExternalIngress
  }
}

Azure CLI を使って、デプロイします。

$ az deployment group create -f ./deploy/app/main.bicep -g <ResourceGroup Name>

デプロイすると、dapr-frontend--v1 という Revision が作成されました。

dapr-frontend.bravewave-d4e6d1bd.japaneast.azurecontainerapps.io という FQDN が自動生成されました。
ASP.NET Core Web アプリケーションがブラウザで表示されることを確認できます。


Blue-Green Deployments を行う

Staging へのデプロイを想定し、新しいバージョンをデプロイします。
main.bicep では、containerImage のタグを 0.2.0、revisionSuffix のバージョンを v2 に更新することで、先ほどデプロイした Revision( dapr-frontend--v1)へのトラフィックを 100% のまま、新しい Revision をデプロイします。

param containerImage string = 'thara0402/dapr-frontend:0.2.0'
param revisionSuffix string = 'v2'

デプロイすると、dapr-frontend--v2 という Revision が追加されました。

traffic が「0」の新しい Revision には、ユーザーに公開される FQDN とは異なる FQDN が割り当てられるので、リリース前のテストができます。
dapr-frontend--v2.bravewave-d4e6d1bd.japaneast.azurecontainerapps.io/ という Revision が含まれた FQDN が自動生成されました。

リリース前のテストが完了したので、Azure CLI を使って Swap します。
拡張モジュールをインストールし、名前空間を登録します。Preview のときは Microsoft.Web 名前空間でしたが、Microsoft.App 名前空間に移行されています。

$ az extension add --name containerapp --upgrade
$ az provider register --namespace Microsoft.App
$ az containerapp ingress traffic set -n dapr-frontend -g <ResourceGroup Name> \
     --revision-weight dapr-frontend--v1=0 latest=100

traffic のパーセンテージが入れ替わったことを確認できます。

ユーザーに公開される FQDN にアクセスすると、V2 に更新された ASP.NET Core Web アプリケーションが表示されます。

最後に、旧バージョンの Revision をシャットダウンします。アクティブのままだと、課金対象となってしまいます。

$ az containerapp revision deactivate -n dapr-frontend -g <ResourceGroup Name> --revision dapr-frontend--v1

新バージョンの Revision のみになったことを確認できます。


まとめ

Azure Container Apps で Blue-Green Deployments を試してみました。トラフィック制御の比率を変えることで AB テストやカナリアリリースもできます。
Envoy によるトラフィック制御以外にも、KEDA によるスケーリングや Dapr によるサイドカーなどの面白い機能があるので、引き続き試していきたいと思います。

今回のソースコードは、こちらのリポジトリで公開しています。
github.com