Recuperar de uma implantação malsucedida de uma aplicação num plano Flex Consumption

Quando uma implementação introduz um bug, precisa de uma forma de recuperar rapidamente. Este artigo mostra-lhe como recuperar de uma implementação deficiente numa aplicação de função Flex Consumption utilizando um processo de integração contínua e implementação contínua (CI/CD). Este processo inclui os seguintes métodos de recuperação:

Método de recuperação Velocidade Quando utilizar Descrição
Repetir a execução anterior bem-sucedida (reverter para trás) Mais rápido Existe uma versão boa conhecida, ou não existe alterações de dados ou estado entre versões Selecione uma execução anterior e execute-a novamente, em seguida aguarde a compilação e a implementação.
git revert e depois git push (avançar) Moderado A correção é simples; caso contrário, o estado externo não pode ser revertido. Identifica o commit que causou a falha, reverte-o e faz push. Depois, aguarda o mesmo tempo de build e de deploy.
git commit um hotfix e depois git push (avançar) Mais lento A causa raiz é conhecida e precisa de uma solução direcionada Identificar a causa principal; criar, rever, testar e integrar uma correção; e depois esperar que a compilação e a implementação terminem.

Estas estratégias recuperam tanto o código da tua função como as definições da app, por isso as alterações de configuração são recuperáveis com alterações no código.

Por que precisas de CI/CD para recuperar uma implementação

Quando implementa código na sua aplicação de plano Flex Consumption:

  • O seu código é implementado sob a forma de um pacote ZIP para um contentor no armazenamento de blobs, que é montado no arranque.
  • Cada implantação substitui o pacote atual.
  • A plataforma não mantém qualquer histórico de revisões incorporado para preservar versões anteriores.
  • A funcionalidade de slots de implantação não é suportada atualmente.
  • As definições da aplicação são aplicadas separadamente através do portal Azure, CLI ou infrastructure-as-code (IaC), e a plataforma não consegue restaurá-las para um estado anterior.

Estes comportamentos de implementação limitam as suas opções para recuperar a sua aplicação do plano Flex Consumption de uma implementação deficiente.

O teu histórico Git e o processo CI/CD fornecem a única forma de acompanhar implementações de código num dado momento. Criar uma implementação que inclua tanto o código como a configuração permite-lhe recuperar de um lançamento problemático, apontando a execução seguinte para um commit válido conhecido.

Para mais informações sobre o modelo de implementação Flex Consumption, consulte plano Flex Consumption e Atualizações do site no plano Flex Consumption.

Pré-requisitos

  • Uma aplicação de funções existente implementada num plano Flex Consumption no Azure.

  • Código-fonte num repositório Git. Utilize tags do Git ou registe os SHAs dos commits de cada lançamento para que possa identificar rapidamente para que versão reverter.

  • Uma implementação configurada para a sua aplicação de funções:

    Um fluxo de trabalho configurado com autenticação OIDC, conforme descrito em Continuous delivery, utilizando o GitHub Actions. O fluxo de trabalho deve usar azure/login. A autenticação por perfil de publicação não suporta os comandos az cli usados neste guia.

Prepare a sua implementação para gerir as definições da aplicação

Antes de poder efetuar uma reversão completa, tem de colocar as definições da aplicação em controlo de código-fonte e aplicá-las como parte da implantação. Embora esta abordagem permita recuperar tanto código como configuração numa única repetição, requer passos adicionais, como aplicar definições, esperar por reinicios e limpar valores não declarados.

Tip

Quando a sua implementação não inclui definições, só pode reverter o código implementado, mas não a configuração.

Esta secção detalha duas abordagens para gerir as definições da aplicação como parte da sua implementação:

Approach No fluxo de trabalho (CLI do Azure) Na definição de serviço (Bicep)
Como funciona Os passos de implementação aplicam definições de um ficheiro JSON usando az functionapp config appsettings, e depois removem definições não declaradas O modelo Bicep declara definições em siteConfig.appSettings; O ARM substitui toda a coleção durante a implementação
Complexidade Moderado. ficheiro JSON mais passos adicionais de CLI no teu fluxo de trabalho Mais alto. Requer conhecimento de Bicep
Deteção de deriva No Sim (what-if)
Melhor para A começar, equipas pequenas Infraestrutura complexa, multiambiente, preparada para produção

Para informações mais gerais sobre as definições da aplicação, consulte a referência de definições da aplicação para o Funções do Azure.

Considerações sobre as definições da aplicação

Preste atenção a estas considerações ao trabalhar com a configuração programática das definições da aplicação.

Important

Não incluir todas as definições necessárias usando qualquer uma das abordagens pode quebrar a sua aplicação implementada.

  • Ambas as abordagens nesta secção tratam o seu ficheiro de definições como o estado completo desejado. Eles removem todas as definições da aplicação que não estejam declaradas no ficheiro a partir da aplicação em cada implementação. Inclua sempre todas as definições necessárias para evitar que a sua aplicação quebre.

  • Trate app-settings.json e as alterações ao modelo de ficheiro Bicep com o mesmo escrutínio que as alterações de código. Revise as alterações nas definições nos pull requests para detetar erros de configuração antes de chegarem à produção.

  • Para uma segurança ótima, siga estas orientações para as suas ligações:

    • Use ligações de identidade gerida sempre que possível. Configure ligações com base na identidade para o armazenamento do anfitrião (AzureWebJobsStorage), o armazenamento de implementação e as ligações de acionador/associação. Para obter mais informações, consulte Definir configurações de implantação.

      • Use referências ao Key Vault quando os segredos forem inevitáveis. A Key Vault guarda os seus segredos de forma segura. Em vez de armazenar segredos diretamente, pode usar uma referência para aceder de forma segura ao segredo necessário em tempo de execução. Para mais informações, consulte Utilizar referências do Key Vault.
  • Quando usas CI/CD, cada alteração nas definições da tua aplicação ou no teu código desencadeia uma atualização separada do site. Por defeito, este processo de implementação produz pelo menos duas atualizações do site: primeiro quando as definições são aplicadas e depois quando o código é implementado. Para implementações sem tempo de inatividade, utilize, em vez disso, uma estratégia de atualização progressiva, em que as instâncias são retiradas de serviço e substituídas em lotes. Para mais informações, consulte as atualizações do site no plano Flex Consumption.

Configurar as definições da app no fluxo de trabalho

Use estes passos básicos para adicionar um ficheiro de configuração de definições de aplicação à implementação do seu projeto:

  1. Crie um ficheiro JSON no seu repositório, como em infra/app-settings.json. Este ficheiro deve incluir todas as definições necessárias da aplicação num formato JSON que se assemelhe ao seguinte exemplo:

    [
      {
        "name": "FUNCTIONS_EXTENSION_VERSION",
        "value": "~4"
      },
      {
        "name": "FUNCTIONS_WORKER_RUNTIME",
        "value": "dotnet-isolated"
      },
      {
        "name": "APPLICATIONINSIGHTS_CONNECTION_STRING",
        "value": "InstrumentationKey=00000000-..."
      },
      {
        "name": "AzureWebJobsStorage__blobServiceUri",
        "value": "https://mystorageaccount.blob.core.windows.net"
      },
      {
        "name": "AzureWebJobsStorage__queueServiceUri",
        "value": "https://mystorageaccount.queue.core.windows.net"
      },
      {
        "name": "AzureWebJobsStorage__tableServiceUri",
        "value": "https://mystorageaccount.table.core.windows.net"
      },
      {
        "name": "MyFeatureFlag",
        "value": "true"
      },
      {
        "name": "ServiceBus__fullyQualifiedNamespace",
        "value": "my-namespace.servicebus.windows.net"
      }
    ]
    
  2. Na sua definição específica de implementação, adicione passos que apliquem as definições antes da implementação do código, com uma pausa de 30 segundos entre eles.

As secções seguintes fornecem exemplos específicos de implementação utilizando ambas as abordagens.

Exemplo: implementação de app-settings.json

Este exemplo de implementação mostra como configurar a sua implantação para incluir a configuração. Escolhe a aba que corresponde ao teu método CI/CD.

Adicione os passos seguintes ao job deploy do seu fluxo de trabalho. Adicione actions/checkout@v4 ao trabalho deploy para que infra/app-settings.json fique disponível e adicione RESOURCE_GROUP ao bloco env ao nível do fluxo de trabalho. Os passos aplicam primeiro as definições, esperam pelo reinício, implementam o código e depois limpam as definições não declaradas.

  deploy:
    needs: build
    steps:
      - name: 'Checkout repository'
        uses: actions/checkout@v4

      - name: 'Download artifact from build job'
        uses: actions/download-artifact@v4
        with:
          name: ${{ env.BUILD_ARTIFACT_NAME }}
          path: ./downloaded-artifact

      - name: 'Log in to Azure'
        uses: azure/login@v2
        with:
          client-id: ${{ vars.AZURE_CLIENT_ID }}
          tenant-id: ${{ vars.AZURE_TENANT_ID }}
          subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}

      - name: 'Apply app settings'
        run: |
          az functionapp config appsettings set \
            --name ${{ env.AZURE_FUNCTIONAPP_NAME }} \
            --resource-group ${{ env.RESOURCE_GROUP }} \
            --settings @infra/app-settings.json

      - name: 'Wait for settings restart'
        run: sleep 30

      - name: 'Deploy code'
        uses: Azure/functions-action@v1
        with:
          app-name: ${{ env.AZURE_FUNCTIONAPP_NAME }}
          package: ./downloaded-artifact

      - name: 'Remove undeclared app settings'
        run: |
          DESIRED=$(jq -r '.[].name' infra/app-settings.json)
          CURRENT=$(az functionapp config appsettings list \
            --name ${{ env.AZURE_FUNCTIONAPP_NAME }} \
            --resource-group ${{ env.RESOURCE_GROUP }} \
            --query "[].name" -o tsv)
          TO_DELETE=""
          for setting in $CURRENT; do
            if ! echo "$DESIRED" | grep -qx "$setting"; then
              TO_DELETE="$TO_DELETE $setting"
            fi
          done
          if [ -n "$TO_DELETE" ]; then
            az functionapp config appsettings delete \
              --name ${{ env.AZURE_FUNCTIONAPP_NAME }} \
              --resource-group ${{ env.RESOURCE_GROUP }} \
              --setting-names $TO_DELETE
          fi

A azure/login@v2 etapa no fluxo de trabalho baseado em OIDC fornece a autenticação necessária para os az cli comandos.

Definir configurações numa implementação em Bicep

Para implementações de IaC, gere as definições da aplicação diretamente no modelo Bicep. O Bicep substitui nativamente a coleção siteConfig.appSettings inteira em cada implementação, sem necessitar de uma etapa de limpeza separada. Também suporta deteção de deriva usando what-if.

Quando atualiza apenas a secção de definições da aplicação do ficheiro Bicep, todos os outros recursos do Azure permanecem inalterados durante a implementação. Este artigo não aborda a criação em Bicep do início ao fim. Para modelos completos de Flex Consumption, veja Funções do Azure infrastructure as code.

Aqui está o fragmento relevante do modelo Bicep (apenas a parte appSettings):

siteConfig: {
  appSettings: [
    {
      name: 'MyFeatureFlag'
      value: 'true'
    }
    {
      name: 'ServiceBus__Connection'
      value: '@Microsoft.KeyVault(SecretUri=https://my-vault.vault.azure.net/secrets/sb-conn)'
    }
  ]
}

Adicione passos para implementar o modelo Bicep antes da implementação do código, com uma espera de 30 segundos pelo meio. A atualização das definições da aplicação com o Bicep é assíncrona. A aplicação reinicia para captar os novos valores, e a implementação de código durante o reinício pode falhar.

      - name: 'Deploy infrastructure'
        run: |
          az deployment group create \
            --resource-group ${{ env.RESOURCE_GROUP }} \
            --template-file infra/main.bicep \
            --parameters appName=${{ env.AZURE_FUNCTIONAPP_NAME }}

      - name: 'Wait for settings restart'
        run: sleep 30

      - name: 'Deploy code'
        uses: Azure/functions-action@v1
        with:
          app-name: ${{ env.AZURE_FUNCTIONAPP_NAME }}
          package: ./downloaded-artifact

Reverter uma implementação

Reverter significa repetir uma corrida anterior bem-sucedida. Uma re-execução verifica o commit SHA original que desencadeou essa execução (não o branch HEAD atual), por isso reconstrói e implementa o código e o ficheiro de definições a partir desse momento. Não precisas de novos commits.

Para reverter uma implementação repetindo uma execução anterior bem-sucedida:

  1. Vai ao separador Ações no teu repositório do GitHub.
  2. Encontre a última execução de fluxo de trabalho bem-sucedida antes da má implementação.
  3. Selecionar Reexecutar todos os trabalhos.
  4. O fluxo de trabalho verifica o commit da execução, reconstrói, implementa o código e sincroniza as definições da app a partir desse app-settings.jsoncommit.

O que a nova execução da reconstrução recria

A re-execução reconstrói o código a partir do commit antigo. Não reutiliza o artefacto binário original. Este comportamento significa:

  • Com ficheiros de bloqueio de dependência fixados (package-lock.json, requirements.txt, e assim sucessivamente), a saída é funcionalmente idêntica.
  • O tempo de construção é igual ao de uma implementação normal. Não é instantâneo.
  • Dependências externas obtidas em tempo de compilação (NuGet, npm, pip) devem continuar disponíveis.

Considerações de reversão

  • Fixe os ficheiros de bloqueio de dependência (package-lock.json, requirements.txt, ou versões bloqueadas .csproj ) no controlo de versão. Os ficheiros de bloqueio fixados garantem que a reexecução de uma implantação produz uma compilação funcionalmente idêntica, independentemente do momento em que ocorra.

  • A re-execução utiliza o workflow ou pipeline YAML do commit original. No entanto, os segredos e as variáveis do pipeline resolvem-se para os seus valores atuais no momento em que são reexecutados. Quando rodares os segredos entre a execução original e uma nova execução, será utilizado o novo valor do segredo.

  • Uma nova execução restaura a tua aplicação implementada para um estado conhecido como válido, mas não altera o teu histórico do Git. O teu HEAD de ramo ainda aponta para o commit quebrado.

  • O que executar novamente não restaura:

    • Configuração de recursos do Azure gerida fora da sua implementação, incluindo planos de alojamento, redes e atribuições de identidade.
    • Alterações de dados ou esquemas em serviços a jusante, como bases de dados, filas de mensagens e armazenamento.
    • Valores secretos do Key Vault. Apenas as referências do Key Vault são mantidas no controlo do código-fonte. A rotação de segredos é uma operação do Key Vault.
  • Teste regularmente o seu processo de reversão. Não espere por um incidente de produção para descobrir que está avariado.

Corrigir o ramo após um rollback

Como a próxima execução acionada por push redistribui o commit quebrado, outros programadores que puxam o branch continuam a ver o código quebrado. Deve resolver esta situação com uma destas ações:

  • Cria um commit hotfix que resolva o problema, que é essencialmente uma operação de roll forward.
  • Execute um git revert para desfazer o commit quebrado criando um novo commit, que também é uma ação de roll forward.
  • Uma política de sucursal que impeça a fusão até resolver o problema.

Enquanto não concluíres uma destas ações, evita iniciar uma nova execução nessa ramificação. Para orientações de validação, consulte Validar antes da fusão.

Efetuar o avanço de uma implementação

Avançar significa enviar um novo commit para corrigir o problema. A implementação falhada mantém-se ativa até a correção ser implementada, por isso esta estratégia funciona melhor quando a aplicação tolera comportamentos degradados durante esse tempo.

  1. Cria um commit de correção no teu ramo. Esta correção pode ser:

    • Um hotfix que resolve diretamente o problema.
    • Um git revert <bad-commit-sha> que cria um novo commit que desfaz as alterações incorretas. Mesmo que git revert reverta o código, trata-se de um avanço porque gera um novo commit e desencadeia uma nova implantação.
  2. Se a correção também exigir alterações de configuração, atualize as definições da sua aplicação (dentro app-settings.json ou dentro do seu modelo Bicep) no mesmo commit.

  3. Empurra o compromisso. A sua implementação constrói e implementa automaticamente a correção.

Validar antes de fundir

Independentemente da sua estratégia de recuperação, valide e corrija os commits antes de os integrar no seu ramo de produção para evitar agravar o problema.

Estas recomendações aplicam-se tanto à progressão proativa como à correção da ramificação após uma reversão:

  • Utilize um ambiente de preparação: Como o plano Flex Consumption atualmente não suporta ranhuras de implementação, considere implementar antes numa aplicação separada num plano Flex Consumption para validar a correção antes de a intercalar na ramificação de produção.

  • Automatizar verificações de saúde: Adicione um passo pós-implementação que chame um endpoint de verificação de saúde na sua aplicação e verifique uma resposta positiva. Em caso de falhar, considere ativar uma repetição da corrida anterior bem-sucedida para reverter automaticamente o processo.

  • Monitorizar após a implementação: Configurar alertas Application Insights para deteção de regressão. A deteção precoce reduz o impacto de uma má implementação.