Azure Container Apps의 파란색-녹색 배포

파란색-녹색 배포는 가동 중지 시간을 최소화하고 애플리케이션의 새 버전 배포와 관련된 위험을 줄이는 것을 목표로 하는 소프트웨어 릴리스 전략입니다. 파란색-녹색 배포에서는 "파란색"과 "녹색"이라고 하는 두 개의 동일한 환경이 설정됩니다. 한 환경(파란색)은 현재 애플리케이션 버전을 실행하고 있고, 다른 환경(녹색)은 새 애플리케이션 버전을 실행하고 있습니다.

그린 환경이 테스트되면 라이브 트래픽이 해당 환경으로 전달되고 파란색 환경은 다음 배포 주기 동안 새 애플리케이션 버전을 배포하는 데 사용됩니다.

Container Apps 수정 버전, 트래픽 가중치수정 버전 레이블을 결합하여 Azure Container Apps에서 파란색-녹색 배포를 사용하도록 설정할 수 있습니다.

Screenshot of Azure Container Apps: Blue/Green deployment.

수정 버전을 사용하여 애플리케이션의 파란색 및 녹색 수정 버전 인스턴스를 만듭니다.

Revision 설명
파란색 수정 버전 파란색으로 표시된 수정 버전은 현재 실행 중인 안정적인 애플리케이션 수정 버전입니다. 이 수정 버전은 사용자가 상호 작용하는 수정 버전이며 프로덕션 트래픽의 대상입니다.
녹색 수정 버전 녹색으로 표시된 수정 버전은 최신 수정 버전의 앱 코드와 새로운 환경 변수 집합을 사용한다는 점을 제외하면 파란색 수정 버전의 복사본입니다. 처음에는 프로덕션 트래픽을 수신하지 않지만 레이블이 지정된 FQDN(정규화된 도메인 이름)을 통해 액세스할 수 있습니다.

새 수정 버전을 테스트하고 확인한 후에는 프로덕션 트래픽이 새 수정 버전을 가리키도록 할 수 있습니다. 문제가 발생하면 쉽게 이전 버전으로 롤백할 수 있습니다.

actions 설명
테스트 및 확인 녹색 수정 버전은 새 수정 버전의 애플리케이션이 예상대로 작동하는지 확인하기 위해 철저한 테스트와 검증을 거쳤습니다. 이 테스트에는 기능 테스트, 성능 테스트, 호환성 확인을 포함한 다양한 작업이 포함될 수 있습니다.
트래픽 스위치 녹색 수정 버전이 필요한 모든 테스트를 통과하면 녹색 수정 버전이 프로덕션 로드 서비스를 시작하도록 트래픽 스위치가 수행됩니다. 이 스위치는 제어된 방식으로 수행되어 원활한 전환을 보장합니다.
롤백 녹색 수정 버전에서 문제가 발생하면 트래픽 스위치를 되돌려 트래픽을 안정적인 파란색 수정 버전으로 다시 라우팅할 수 있습니다. 이 롤백을 통해 새 버전에 문제가 있는 경우 사용자에게 미치는 영향을 최소화할 수 있습니다. 녹색 수정 버전은 다음 배포에도 계속 사용할 수 있습니다.
역할 변경 파란색 및 녹색 수정 버전의 역할은 녹색 수정 버전에 성공적으로 배포된 후 변경됩니다. 다음 릴리스 주기 동안 녹색 수정 버전은 안정적인 프로덕션 환경을 나타내고, 애플리케이션 코드의 새 수정 버전은 파란색 수정 버전에서 배포 및 테스트됩니다.

이 문서에서는 컨테이너 앱에서 파란색-녹색 배포를 구현하는 방법을 보여 줍니다. 다음 예를 실행하려면 새 앱을 만들 수 있는 컨테이너 앱 환경이 필요합니다.

참고 항목

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

새 수정 버전 배포 및 레이블 지정

파란색 레이블은 현재 앱의 FQDN에 도착하는 프로덕션 트래픽을 사용하는 수정 버전을 나타냅니다. 녹색 레이블은 프로덕션에 곧 출시될 앱의 새 버전을 나타냅니다. 새로운 커밋 해시는 앱 코드의 새 버전을 식별합니다. 다음 명령은 해당 커밋 해시에 대한 새 수정 버전을 배포하고 이를 녹색 레이블로 표시합니다.

#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

다음 예에서는 트래픽 섹션이 구성되는 방식을 보여 줍니다. 파란색commitId이 표시된 수정 버전은 프로덕션 트래픽을 100% 사용하는 반면, 녹색commitId이 표시된 새로 배포된 수정 버전은 프로덕션 트래픽을 전혀 사용하지 않습니다.

{ 
  "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

버그가 수정되면 새 수정 버전의 애플리케이션이 다시 녹색 수정 버전으로 배포됩니다. 녹색 수정 버전은 최종적으로 프로덕션 수정 버전이 됩니다.

다음 배포 주기

이제 녹색 레이블은 현재 안정적인 프로덕션 코드를 실행 중인 수정 버전을 표시합니다.

다음 배포 주기 동안 파란색은 프로덕션에 출시되는 새 애플리케이션 수정 버전이 포함된 수정 버전을 식별합니다.

다음 명령은 다음 배포 주기를 준비하는 방법을 보여 줍니다.

# 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

다음 단계