Pausar automaticamente um trabalho usando o PowerShell e o Azure Functions ou a Automação do Azure

Alguns aplicativos exigem uma abordagem de processamento de fluxo (por exemplo, por meio do Azure Stream Analytics), mas não precisam ser executados continuamente. Os motivos incluem:

  • Dados de entrada que chegam em um agendamento (por exemplo, no início da hora)
  • Um volume esparso ou baixo de dados de entrada (poucos registros por minuto)
  • Processos de negócios que se beneficiam das funcionalidades de janela de tempo, mas são executados em lote por essência (por exemplo, finanças ou RH)
  • Demonstrações, protótipos ou testes que envolvem trabalhos de execução prolongada em baixa escala

O benefício de não executar esses trabalhos continuamente é a economia de custos, pois os trabalhos do Stream Analytics são cobrados por unidade de streaming ao longo do tempo.

Este artigo explica como configurar a pausa automática para um trabalho do Azure Stream Analytics. Configure uma tarefa que pausa automaticamente e retoma um trabalho de acordo com um agendamento. O termo pausar significa que o estado do trabalho está parado para evitar qualquer cobrança.

Este artigo discute o design geral, os componentes necessários e alguns detalhes de implementação.

Observação

Há desvantagens em pausar automaticamente um trabalho. As principais desvantagens são: a perda das funcionalidades de baixa latência/tempo real e os riscos potenciais de permitir que a lista de pendências do evento de entrada cresça sem supervisão enquanto um trabalho está pausado. As organizações não devem considerar a pausa automática na maioria dos cenários de produção executados em escala.

Criar

No exemplo deste artigo, você quer que nosso trabalho seja executado por N minutos, antes de pausá-lo por M minutos. Quando o trabalho é pausado, os dados de entrada não são consumidos e se acumulam no upstream. Após o início do trabalho, ele atualiza a lista de pendências e processa os dados de entrada antes de ser desligado novamente.

Diagrama que ilustra o comportamento de um trabalho pausado automaticamente ao longo do tempo.

Durante a execução do trabalho, a tarefa não deve parar o trabalho até que as respectivas métricas estejam íntegras. As métricas de interesse são a lista de pendências de entrada e a marca-d'água. Você vai verificar se ambas estão na linha de base por pelo menos N minutos. Esse comportamento se traduz em duas ações:

  • Um trabalho interrompido é reiniciado após M minutos.
  • Um trabalho em execução será interrompido a qualquer momento após N minutos, assim que as respectivas métricas de lista de pendências e marca-d'água estiverem íntegras.

Diagrama que mostra os possíveis estados de um trabalho.

Como exemplo, considere que N = 5 minutos e M = 10 minutos. Com essas configurações, um trabalho tem pelo menos 5 minutos para processar todos os dados recebidos em 15. A economia de custo potencial é de até 66%.

Para reiniciar o trabalho, use Quando Parou Pela Última Vezopção inicial. Essa opção informa ao Stream Analytics para processar todos os eventos que foram registrados em lista de pendências upstream desde que o trabalho foi interrompido.

Há duas ressalvas quanto a essa situação. Primeira, o trabalho não pode permanecer parado por mais tempo do que o período de retenção do fluxo de entrada. Se você executar o trabalho apenas uma vez por dia, precisará garantir que o período de retenção para eventos seja maior que um dia. Em segundo lugar, o trabalho precisa ter sido iniciado pelo menos uma vez para que o modo Quando Parou Pela Última Vez seja aceito (ou então, ele literalmente nunca foi interrompido antes). Portanto, a primeira execução de um trabalho precisa ser manual, ou você deve estender o script para cobrir esse caso.

A última consideração é tornar essas ações idempotentes. Você pode então repeti-las à vontade, sem efeitos colaterais, para facilitar o uso e a resiliência.

Componentes

Chamadas à API

Este artigo prevê a necessidade de interagir com o Stream Analytics nos seguintes aspectos:

  • Obter o status atual do trabalho (gerenciamento de recursos do Stream Analytics):
    • Se o trabalho está em execução:
      • Obtenha o tempo desde o início do trabalho (logs).
      • Obtenha os valores métricos atuais (métricas).
      • Se aplicável, interrompa o trabalho (gerenciamento de recursos do Stream Analytics).
    • Se o trabalho é interrompido:
      • Obtenha o tempo desde que o trabalho foi interrompido (logs).
      • Se aplicável, inicie o trabalho (gerenciamento de recursos do Stream Analytics).

Para o gerenciamento de recursos do Stream Analytics, é possível usar a API REST, o SDK do .NET ou uma das bibliotecas da CLI (CLI do Azure ou PowerShell).

Para métricas e logs, tudo no Azure é centralizado no Azure Monitor, com uma opção semelhante de superfícies de API. Os logs e as métricas estão sempre atrasados de 1 a 3 minutos quando você consulta as APIs. Portanto, ao definir N como 5, isso geralmente significa que, na realidade, o trabalho será executado em 6 a 8 minutos.

Outra consideração é que as métricas são sempre emitidas. Quando o trabalho é interrompido, a API retorna registros vazios. Limpe a saída das chamadas à API para se concentrar nos valores relevantes.

Idioma do script

Este artigo implementa a pausa automática no PowerShell. A primeira razão para esta escolha é que o PowerShell agora é multiplataforma. Ele pode ser executado em qualquer sistema operacional, o que facilita as implantações. O segundo motivo é que ele recebe e retorna objetos em vez de cadeias de caracteres. Os objetos facilitam a análise e o processamento para tarefas de automação.

No PowerShell, use o módulo Az PowerShell (que inclui Az.Monitor e Az.StreamAnalytics) para tudo que você precisa:

Serviço de hospedagem

Para hospedar nossa tarefa do PowerShell, é necessário um serviço que ofereça execuções agendadas. Há muitas opções, mas aqui estão duas sem servidor:

Se você não se importar com as soluções alternativas, a Automação do Azure será a maneira mais fácil de implantar a tarefa. Porém, neste artigo, você escreve um script local primeiro para que possa comparar. Depois de ter um script em funcionamento, você vai implantá-lo no Functions e em uma conta da Automação.

Ferramentas para desenvolvedores

É altamente recomendável o desenvolvimento local por meio do Visual Studio Code, para o Functions e o Stream Analytics. O uso de um ambiente de desenvolvimento local permite usar o controle do código-fonte e ajuda a repetir facilmente as implantações. Mas, por uma questão de brevidade, este artigo ilustra o processo no portal do Azure.

Escrever o script do PowerShell localmente

A melhor maneira de desenvolver o script é localmente. Como o PowerShell é multiplataforma, você pode escrever o script e testá-lo em qualquer sistema operacional. No Windows, você pode usar o Terminal do Windows com o PowerShell 7 e o Azure PowerShell.

O script final que este artigo usa está disponível para o Azure Functions e a Automação do Azure. É diferente do script a seguir, pois ele está conectado ao ambiente de hospedagem (Functions ou Automação). Este artigo aborda esse aspecto mais tarde. Primeiro, você percorre uma versão do script que é executada apenas localmente.

Este script foi escrito propositalmente de uma forma simples, para que todos possam entendê-lo.

Na parte superior, defina os parâmetros necessários e verifique o status inicial do trabalho:


# Setting variables
$restartThresholdMinute = 10 # This is M
$stopThresholdMinute = 5 # This is N

$maxInputBacklog = 0 # The amount of backlog you tolerate when stopping the job (in event count, 0 is a good starting point)
$maxWatermark = 10 # The amount of watermark you tolerate when stopping the job (in seconds, 10 is a good starting point at low Streaming Units)

$subscriptionId = "<Replace with your Subscription Id - not the name>"
$resourceGroupName = "<Replace with your Resource Group Name>"
$asaJobName = "<Replace with your Stream Analytics job name>"

$resourceId = "/subscriptions/$($subscriptionId )/resourceGroups/$($resourceGroupName )/providers/Microsoft.StreamAnalytics/streamingjobs/$($asaJobName)"

# If not already logged, uncomment and run the two following commands:
# Connect-AzAccount
# Set-AzContext -SubscriptionId $subscriptionId

# Check current Stream Analytics job status
$currentJobState = Get-AzStreamAnalyticsJob  -ResourceGroupName $resourceGroupName -Name $asaJobName | Foreach-Object {$_.JobState}
Write-Output "asaRobotPause - Job $($asaJobName) is $($currentJobState)."

Se o trabalho estiver em execução, verifique se ele está em execução há pelo menos N minutos. Verifique também a lista de pendências e a marca d'água.


# Switch state
if ($currentJobState -eq "Running")
{
    # First, look up the job start time with Get-AzActivityLog
    ## Get-AzActivityLog issues warnings about deprecation coming in future releases. Here you ignore them via -WarningAction Ignore.
    ## You check in 1,000 records of history, to make sure you're not missing what you're looking for. It might need adjustment for a job that has a lot of logging happening.
    ## There's a bug in Get-AzActivityLog that triggers an error when Select-Object First is in the same pipeline (on the same line). So you move it down.
    $startTimeStamp = Get-AzActivityLog -ResourceId $resourceId -MaxRecord 1000 -WarningAction Ignore | Where-Object {$_.EventName.Value -like "Start Job*"}
    $startTimeStamp = $startTimeStamp | Select-Object -First 1 | Foreach-Object {$_.EventTimeStamp}

    # Then gather the current metric values
    ## Get-AzMetric issues warnings about deprecation coming in future releases. Here you ignore them via -WarningAction Ignore.
    $currentBacklog = Get-AzMetric -ResourceId $resourceId -TimeGrain 00:01:00 -MetricName "InputEventsSourcesBacklogged" -DetailedOutput -WarningAction Ignore
    $currentWatermark = Get-AzMetric -ResourceId $resourceId -TimeGrain 00:01:00 -MetricName "OutputWatermarkDelaySeconds" -DetailedOutput -WarningAction Ignore

    # Metrics are always lagging 1-3 minutes behind, so grabbing the last N minutes actually means checking N+3. This might be overly safe and can be fine-tuned down per job.
    $Backlog =  $currentBacklog.Data |
                    Where-Object {$_.Maximum -ge 0} | # Remove the empty records (when the job is stopped or starting)
                    Sort-Object -Property Timestamp -Descending |
                    Where-Object {$_.Timestamp -ge $startTimeStamp} | # Keep only the records of the latest run
                    Select-Object -First $stopThresholdMinute | # Take the last N records
                    Measure-Object -Sum Maximum # Sum over those N records
    $BacklogSum = $Backlog.Sum

    $Watermark = $currentWatermark.Data |
                    Where-Object {$_.Maximum -ge 0} |
                    Sort-Object -Property Timestamp -Descending |
                    Where-Object {$_.Timestamp -ge $startTimeStamp} |
                    Select-Object -First $stopThresholdMinute |
                    Measure-Object -Average Maximum # Here you average
    $WatermarkAvg = [int]$Watermark.Average # Rounding the decimal value and casting it to integer

    # Because you called Get-AzMetric with a TimeGrain of a minute, counting the number of records gives you the duration in minutes
    Write-Output "asaRobotPause - Job $($asaJobName) is running since $($startTimeStamp) with a sum of $($BacklogSum) backlogged events, and an average watermark of $($WatermarkAvg) sec, for $($Watermark.Count) minutes."

    # -le for lesser or equal, -ge for greater or equal
    if (
        ($BacklogSum -ge 0) -and ($BacklogSum -le $maxInputBacklog) -and ` # is not null and is under the threshold
        ($WatermarkAvg -ge 0) -and ($WatermarkAvg -le $maxWatermark) -and ` # is not null and is under the threshold
        ($Watermark.Count -ge $stopThresholdMinute) # at least N values
        )
    {
        Write-Output "asaRobotPause - Job $($asaJobName) is stopping..."
        Stop-AzStreamAnalyticsJob -ResourceGroupName $resourceGroupName -Name $asaJobName
    }
    else {
        Write-Output "asaRobotPause - Job $($asaJobName) is not stopping yet, it needs to have less than $($maxInputBacklog) backlogged events and under $($maxWatermark) sec watermark for at least $($stopThresholdMinute) minutes."
    }
}

Se o trabalho for interrompido, verifique o log para saber quando ocorreu a última ação Stop Job:


elseif ($currentJobState -eq "Stopped")
{
    # First, look up the job start time with Get-AzActivityLog
    ## Get-AzActivityLog issues warnings about deprecation coming in future releases. Here you ignore them via -WarningAction Ignore.
    ## You check in 1,000 records of history, to make sure you're not missing what you're looking for. It might need adjustment for a job that has a lot of logging happening.
    ## There's a bug in Get-AzActivityLog that triggers an error when Select-Object First is in the same pipeline (on the same line). So you move it down.
    $stopTimeStamp = Get-AzActivityLog -ResourceId $resourceId -MaxRecord 1000 -WarningAction Ignore | Where-Object {$_.EventName.Value -like "Stop Job*"}
    $stopTimeStamp = $stopTimeStamp | Select-Object -First 1 | Foreach-Object {$_.EventTimeStamp}

    # Get-Date returns a local time. You project it to the same time zone (universal) as the result of Get-AzActivityLog that you extracted earlier.
    $minutesSinceStopped = ((Get-Date).ToUniversalTime()- $stopTimeStamp).TotalMinutes

    # -ge for greater or equal
    if ($minutesSinceStopped -ge $restartThresholdMinute)
    {
        Write-Output "asaRobotPause - Job $($jobName) was paused $([int]$minutesSinceStopped) minutes ago, set interval is $($restartThresholdMinute), it is now starting..."
        Start-AzStreamAnalyticsJob -ResourceGroupName $resourceGroupName -Name $asaJobName -OutputStartMode LastOutputEventTime
    }
    else{
        Write-Output "asaRobotPause - Job $($jobName) was paused $([int]$minutesSinceStopped) minutes ago, set interval is $($restartThresholdMinute), it will not be restarted yet."
    }
}
else {
    Write-Output "asaRobotPause - Job $($jobName) is not in a state I can manage: $($currentJobState). Let's wait a bit, but consider helping is that doesn't go away!"
}

No final, registre a conclusão do trabalho:


# Final Stream Analytics job status check
$newJobState = Get-AzStreamAnalyticsJob  -ResourceGroupName $resourceGroupName -Name $asaJobName | Foreach-Object {$_.JobState}
Write-Output "asaRobotPause - Job $($asaJobName) was $($currentJobState), is now $($newJobState). Job completed."

Opção 1: hospedar a tarefa no Azure Functions

Para referência, a equipe Azure Functions mantém um guia completo do desenvolvedor do PowerShell.

Primeiro, é necessário um novo aplicativo de funções. Um aplicativo de funções é semelhante a uma solução que pode hospedar várias funções.

Você pode obter o procedimento completo, mas o gist deve acessar o portal do Azure e criar um novo aplicativo de funções com:

  • Publicar: código
  • Runtime: PowerShell Core
  • Versão: 7+

Depois de provisionar o aplicativo de funções, comece com sua configuração geral.

Identidade gerenciada no Azure Functions

A função precisa de permissões para iniciar e parar o trabalho do Stream Analytics. Atribua essas permissões usando uma identidade gerenciada.

A primeira etapa é habilitar uma identidade gerenciada atribuída pelo sistema para a função, seguindo esse procedimento.

Agora, você pode conceder as permissões certas a essa identidade no trabalho do Stream Analytics que deseja pausar automaticamente. Para esta tarefa, na área do portal para o trabalho do Stream Analytics (não no da função), em Controle de acesso (IAM), adicione uma atribuição de função à função Colaborador para um membro do tipo Identidade Gerenciada. Selecione o nome da função anterior.

Captura de tela das configurações de controle de acesso para um trabalho do Stream Analytics.

No script do PowerShell, você pode adicionar uma verificação para garantir que a identidade gerenciada esteja definida corretamente. (O script final está disponível no GitHub.)


# Check if a managed identity has been enabled and granted access to a subscription, resource group, or resource
$AzContext = Get-AzContext -ErrorAction SilentlyContinue
if (-not $AzContext.Subscription.Id)
{
    Throw ("Managed identity is not enabled for this app or it has not been granted access to any Azure resources. Please see /azure/app-service/overview-managed-identity for additional details.")
}

Adicione algumas informações de log para verificar se a função está sendo acionada:


$currentUTCtime = (Get-Date).ToUniversalTime()

# Write an information log with the current time.
Write-Host "asaRobotPause - PowerShell timer trigger function is starting at time: $currentUTCtime"

Parâmetros para Funções do Azure

A melhor maneira de passar os parâmetros para o script no Functions é usar as configurações do aplicativo de funções como variáveis de ambiente.

A primeira etapa é seguir o procedimento para definir seus parâmetros como Configurações do Aplicativo na página do aplicativo de funções. Você precisa de:

Nome Valor
maxInputBacklog A quantidade de pendências que você tolera ao interromper o trabalho. Na contagem de eventos, 0 é um bom ponto de partida.
maxWatermark A quantidade de marcas-d'água que você tolera ao interromper o trabalho. Em segundos, 10 é um bom ponto de partida em unidades de streaming baixas.
restartThresholdMinute M: o tempo (em minutos) até que um trabalho interrompido seja reiniciado.
stopThresholdMinute N: o tempo (em minutos) de resfriamento até que um trabalho em execução seja interrompido. A lista de pendências de entrada precisará permanecer em 0 durante esse tempo.
subscriptionId A ID da assinatura (não o nome) do trabalho do Stream Analytics a ser pausado automaticamente.
resourceGroupName O nome do grupo de recursos do trabalho do Stream Analytics a ser pausado automaticamente.
asaJobName O nome do trabalho do Stream Analytics a ser pausado automaticamente.

Em seguida, atualize o script do PowerShell para carregar as variáveis de acordo:

$maxInputBacklog = $env:maxInputBacklog
$maxWatermark = $env:maxWatermark

$restartThresholdMinute = $env:restartThresholdMinute
$stopThresholdMinute = $env:stopThresholdMinute

$subscriptionId = $env:subscriptionId
$resourceGroupName = $env:resourceGroupName
$asaJobName = $env:asaJobName

Requisitos de módulo do PowerShell

Da mesma forma que você precisou instalar o Azure PowerShell localmente para usar os comandos do Stream Analytics (como Start-AzStreamAnalyticsJob), é necessário adicioná-lo ao host do aplicativo de funções:

  1. Na página do aplicativo de funções, em Funções, selecione Arquivos do aplicativo e selecione requirements.psd1.
  2. Remova a marca de comentário da linha 'Az' = '6.*'.
  3. Para que essa alteração entre em vigor, reinicie o aplicativo.

Captura de tela das configurações de arquivos de aplicativo do aplicativo de funções.

Criando a função

Depois de você concluir essa configuração, poderá criar a função específica dentro do aplicativo de funções para executar o script.

No portal, desenvolva uma função acionada em um temporizador. Verifique se a função foi acionada a cada minuto com 0 */1 * * * * e se aparece "no segundo 0 de cada 1 minuto".

Captura de tela da criação de uma nova função de gatilho do temporizador em um aplicativo de funções.

Se necessário, você poderá alterar o valor temporal em Integração ao atualizar o agendamento.

Captura de tela das configurações de integração de uma função.

Em seguida, em Código + Teste, você pode copiar o script em run.ps1 e testá-lo. Ou pode copiar o script completo do GitHub. A lógica de negócios foi movida para uma instrução try/catch para gerar erros apropriados caso haja falhas durante o processamento.

Captura de tela do painel Code+Test da função.

Para verificar se tudo está funcionando bem, selecione Teste/Executar no painel Código + Teste. Você também pode verificar o painel Monitor, mas ele sempre atrasa algumas execuções.

Captura de tela da saída de uma execução bem-sucedida.

Definindo um alerta na execução da função

Por fim, você quer ser notificado por meio de um alerta caso a função não seja executada com êxito. Os alertas têm um custo pequeno, mas podem evitar situações com custo maior.

Na página do aplicativo de funções, em Logs, execute a consulta a seguir. Retorna todas as execuções malsucedidas nos últimos 5 minutos.

requests
| where success == false
| where timestamp > ago(5min)
| summarize failedCount=sum(itemCount) by operation_Name
| order by failedCount desc

No editor da consulta, selecione Nova regra de alerta. No painel aberto, defina Medida como:

  • Medida: failedCount
  • Tipo de agregação: Total
  • Granularidade de agregação: 5 minutos

Depois, configure a Lógica de alerta da seguinte maneira:

  • Operador: Maior que
  • Valor do limite: 0
  • Frequência de avaliação: 5 minutos

A partir daí, reutilize ou crie um novo grupo de ações. Em seguida, conclua a configuração.

Para verificar se configurou o alerta corretamente, você pode adicionar throw "Testing the alert" em qualquer lugar no script do PowerShell e aguardar 5 minutos para receber um email.

Opção 2: hospedar a tarefa na Automação do Azure

Primeiro, é necessária uma nova conta de Automação. Uma conta de Automação é semelhante a uma solução que pode hospedar vários runbooks.

Para o procedimento, consulte o início rápido Criar uma conta de Automação usando o portal do Azure. É possível usar uma identidade gerenciada atribuída pelo sistema diretamente na guia Avançado.

Para referência, a equipe de Automação tem um tutorial para começar a usar runbooks do PowerShell.

Parâmetros para a Automação do Azure

Com um runbook, você pode usar a sintaxe de parâmetro clássica do PowerShell para passar argumentos:

Param(
    [string]$subscriptionId,
    [string]$resourceGroupName,
    [string]$asaJobName,

    [int]$restartThresholdMinute,
    [int]$stopThresholdMinute,

    [int]$maxInputBacklog,
    [int]$maxWatermark
)

Identidade gerenciada para a Automação do Azure

A conta de Automação deve ter recebido uma identidade gerenciada durante o provisionamento. Mas, se necessário, você pode habilitar uma identidade gerenciada usando este procedimento.

Como você fez para a função, é necessário conceder as permissões corretas no trabalho do Stream Analytics que deseja pausar automaticamente.

Para conceder as permissões, na área do portal do trabalho do Stream Analytics (não na página Automação), em Controle de acesso (IAM), adicione uma atribuição de função à função Colaborador para um membro do tipo Identidade Gerenciada. Especifica o nome da conta de Automação anterior.

Captura de tela das configurações de controle de acesso para um trabalho do Stream Analytics.

No script do PowerShell, você pode adicionar uma verificação para garantir que a identidade gerenciada esteja definida corretamente. (O script final está disponível no GitHub.)

# Ensure that you don't inherit an AzContext in your runbook
Disable-AzContextAutosave -Scope Process | Out-Null

# Connect by using a managed service identity
try {
        $AzureContext = (Connect-AzAccount -Identity).context
    }
catch{
        Write-Output "There is no system-assigned user identity. Aborting.";
        exit
    }

Criando o runbook

Quando você concluir a configuração, poderá criar o runbook específico dentro da conta de Automação para executar o script. Aqui, não é necessário adicionar o Azure PowerShell como um requisito. Já está integrado.

No portal, em Automação de Processo, selecione Runbooks. Em seguida, selecione Criar um runbook, selecione PowerShell como o tipo de runbook e escolha qualquer versão acima de 7 (no momento, 7.1 (versão prévia)).

Agora, é possível colar o script e testá-lo. Você pode copiar o script completo do GitHub. A lógica de negócios foi movida para uma instrução try/catch para gerar erros apropriados caso haja falhas durante o processamento.

Captura de tela do editor de scripts de runbook na Automação do Azure.

Você pode verificar se tudo está conectado corretamente em Painel de teste.

Depois disso, é necessário publicar o trabalho (selecionando Publicar) para que você possa vincular o runbook a um agendamento. Criar e vincular o agendamento é um processo simples. Agora é um bom momento para se lembrar de que há soluções alternativas para alcançar intervalos de agendamento abaixo de uma hora.

Por fim, você pode configurar um alerta. A primeira etapa é habilitar logs usando as configurações de diagnóstico da conta de Automação. A segunda etapa é capturar erros usando uma consulta, como você fez no Functions.

Resultado

Em seu trabalho do Stream Analytics, você pode verificar se tudo está em execução conforme o esperado em dois locais.

Aqui está o log de atividades:

Captura de tela dos logs do trabalho do Stream Analytics.

E aqui estão as métricas:

Captura de tela das métricas do trabalho do Stream Analytics.

Depois de entender o script, retrabalhá-lo para estender seu escopo é uma tarefa simples. Você pode atualizar o script facilmente para ser direcionado a uma lista de trabalhos em vez de apenas um. É possível definir e processar escopos maiores usando marcas, grupos de recursos ou até mesmo assinaturas inteiras.

Obter suporte

Para obter mais assistência, experimente a página do Microsoft Q&A sobre o Azure Stream Analytics.

Próximas etapas

Você aprendeu as noções básicas do uso do PowerShell para automatizar o gerenciamento de trabalhos do Azure Stream Analytics. Para saber mais, leia os seguintes artigos: