Implementación azul-verde en Azure Container Apps

Blue-Green Deployment es una estrategia de versión de software que pretende minimizar el tiempo de inactividad y reducir el riesgo asociado a la implementación de nuevas versiones de una aplicación. En una implementación azul-verde, se configuran dos entornos idénticos, denominados "azul" y "verde". Un entorno (azul) ejecuta la versión actual de la aplicación y un entorno (verde) ejecuta la nueva versión de la aplicación.

Una vez probado el entorno verde, el tráfico activo se dirige a él y el entorno azul se usa para implementar una nueva versión de aplicación durante el siguiente ciclo de implementación.

Puede habilitar la implementación azul-verde en Azure Container Apps mediante la combinación de revisiones de aplicaciones de contenedor, pesos de tráfico y etiquetas de revisión.

Screenshot of Azure Container Apps: Blue/Green deployment.

Las revisiones se usan para crear instancias de las versiones azul y verde de la aplicación.

Revision Descripción
Revisión azul La revisión etiquetada como azul es la versión actualmente en ejecución y estable de la aplicación. Esta revisión es la que interactúan los usuarios y es el destino del tráfico de producción.
Revisión verde La revisión etiquetada como verde es una copia de la revisión azul , excepto que usa una versión más reciente del código de la aplicación y posiblemente un nuevo conjunto de variables de entorno. No recibe ningún tráfico de producción inicialmente, pero es accesible a través de un nombre de dominio completo (FQDN) etiquetado.

Después de probar y comprobar la nueva revisión, puede apuntar el tráfico de producción a la nueva revisión. Si encuentra problemas, puede revertir fácilmente a la versión anterior.

Acciones Descripción
Pruebas y comprobación La revisión verde se prueba y comprueba exhaustivamente para asegurarse de que la nueva versión de la aplicación funciona según lo previsto. Esta prueba puede implicar varias tareas, incluidas las pruebas funcionales, las pruebas de rendimiento y las comprobaciones de compatibilidad.
Conmutador de tráfico Una vez que la revisión verde supera todas las pruebas necesarias, se realiza un conmutador de tráfico para que la revisión verde comience a atender la carga de producción. Este modificador se realiza de forma controlada, lo que garantiza una transición sin problemas.
Reversión Si se producen problemas en la revisión verde, puede revertir el conmutador de tráfico, enrutando el tráfico a la revisión azul estable. Esta reversión garantiza un impacto mínimo en los usuarios si hay problemas en la nueva versión. La revisión verde sigue estando disponible para la siguiente implementación.
Cambio de rol Los roles de las revisiones azules y verdes cambian después de una implementación correcta a la revisión verde . Durante el siguiente ciclo de versión, la revisión verde representa el entorno de producción estable mientras que la nueva versión del código de aplicación se implementa y prueba en la revisión azul .

En este artículo se muestra cómo implementar la implementación azul-verde en una aplicación de contenedor. Para ejecutar los ejemplos siguientes, necesita un entorno de aplicación de contenedor donde puede crear una aplicación.

Nota:

Consulte el repositorio containerapps-blue-green para obtener un ejemplo completo de un flujo de trabajo de GitHub que implementa la implementación azul-verde para Container Apps.

Creación de una aplicación contenedora con varias revisiones activas habilitadas

La aplicación contenedora debe tener la propiedad establecida en multiple para habilitar la configuration.activeRevisionsMode división de tráfico. Para obtener nombres de revisión deterministas, puede establecer la template.revisionSuffix configuración en un valor de cadena que identifica de forma única una versión. Por ejemplo, puede usar números de compilación o git confirma hashes cortos.

Para los comandos siguientes, se usó un conjunto de hashes de confirmación.

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

Guarde el código siguiente en un archivo denominado 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

Implemente la aplicación con la plantilla de Bicep mediante este comando:

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

Implementación de una nueva revisión y asignación de etiquetas

La etiqueta azul hace referencia actualmente a una revisión que toma el tráfico de producción que llega al FQDN de la aplicación. La etiqueta verde hace referencia a una nueva versión de una aplicación que está a punto de implementarse en producción. Un nuevo hash de confirmación identifica la nueva versión del código de la aplicación. El siguiente comando implementa una nueva revisión para ese hash de confirmación y la marca con etiqueta verde .

#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

En el ejemplo siguiente se muestra cómo se configura la sección de tráfico. La revisión con el azulcommitId está tomando el 100 % del tráfico de producción, mientras que la revisión recién implementada con verdecommitId no toma ningún tráfico de producción.

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

La revisión recién implementada se puede probar mediante el FQDN específico de la etiqueta:

#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

Envío del tráfico de producción a la revisión verde

Después de confirmar que el código de la aplicación en la revisión verde funciona según lo previsto, el 100 % del tráfico de producción se envía a la revisión. La revisión verde ahora se convierte en la revisión de producción.

# 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

En el ejemplo siguiente se muestra cómo se configura la traffic sección después de este paso. La revisión verde con el nuevo código de aplicación toma todo el tráfico de usuario mientras que la revisión azul con la versión anterior de la aplicación no acepta solicitudes de usuario.

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

Revertir la implementación si hay problemas

Si después de ejecutarse en producción, se detecta que la nueva revisión tiene errores, puede revertir al estado correcto anterior. Después de la reversión, el 100 % del tráfico se envía a la versión anterior en la revisión azul y esa revisión se designa de nuevo como revisión de producción.

# 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

Una vez corregidos los errores, la nueva versión de la aplicación se vuelve a implementar como una revisión verde . La versión verde finalmente se convierte en la revisión de producción.

Siguiente ciclo de implementación

Ahora la etiqueta verde marca la revisión que ejecuta actualmente el código de producción estable.

Durante el siguiente ciclo de implementación, el azul identifica la revisión con la nueva versión de la aplicación que se va a implementar en producción.

Los siguientes comandos muestran cómo prepararse para el siguiente ciclo de implementación.

# 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

Pasos siguientes