Criar alertas de pesquisa de logs nos Insights do contêiner

Os insights do contêiner monitoram o desempenho de cargas de trabalho de contêiner implantadas em clusters do Kubernetes gerenciado ou autogerenciado. Para alertar sobre o que importa, este artigo descreve como criar alertas baseados em log para as seguintes situações com clusters do AKS (Serviço de Kubernetes do Azure):

  • Quando a utilização de CPU ou memória em nós de cluster excede um limite
  • Quando a utilização de CPU ou memória em qualquer contêiner dentro de um controlador excede um limite em comparação com um limite definido no recurso correspondente
  • Contagens de nós de status NotReady
  • Contagens de fase de pod Failed, Pending, Unknown, Running ou Succeeded
  • Quando o espaço livre em disco nos nós de cluster excede um limite

Para alertar sobre a alta utilização de CPU ou memória ou baixo espaço livre em disco nos nós de cluster, use as consultas fornecidas para criar um alerta de métrica ou um alerta de medição de métrica. Os alertas de métrica têm latência menor do que os alertas de pesquisa de logs, mas os alertas de pesquisa de logs fornecem consulta avançada e maior sofisticação. As consultas dos alertas de pesquisa de logs comparam um datetime com o presente usando o operador now e retrocedendo uma hora. (Os insights do contêiner armazenam todas as datas no formato UTC [Tempo Universal Coordenado].)

Importante

As consultas neste artigo dependem dos dados coletados pelos insights do contêiner e armazenados em um workspace do Log Analytics. Se você modificou as configurações de coleta de dados padrão, as consultas podem não retornar os resultados esperados. Mais notavelmente, se você tiver desabilitado a coleta de dados de desempenho desde que habilitou as métricas do Prometheus para o cluster, todas as consultas que usam a tabela Perf não retornarão resultados.

Consulte Configurar a coleta de dados em insights de contêiner usando a regra de coleta de dados para configurações predefinidas, incluindo desabilitar a coleta de dados de desempenho. Consulte Configurar a coleta de dados em insights de contêiner usando ConfigMap para obter mais opções de coleta de dados.

Se você não conhece os alertas do Azure Monitor, confira a visão geral de alertas no Microsoft Azure antes de iniciar. Para saber mais sobre os alertas que usam consultas de log, confira Alertas de pesquisa de logs do Azure Monitor. Para obter mais informações sobre alertas de métricas, consulte Alertas de métricas no Azure Monitor.

Medições de consulta de log

Os alertas de pesquisa de logs podem medir duas coisas diferentes, que podem ser usadas para monitorar máquinas virtuais em cenários diferentes:

  • Contagem de resultados: conta o número de linhas retornadas pela consulta e pode ser usado para trabalhar com eventos como logs de eventos do Windows, Syslog e exceções de aplicativo.
  • Cálculo de um valor: faz um cálculo com base em uma coluna numérica e pode ser usado para incluir qualquer número de recursos. Um exemplo é o percentual de CPU.

Direcionar recursos e dimensões

Você pode usar uma regra para monitorar os valores de várias instâncias usando dimensões. Por exemplo, você usaria dimensões se, por exemplo, quisesse monitorar o uso da CPU em várias instâncias que executam seu site ou aplicativo e criar um alerta para uso da CPU acima de 80%.

Para criar alertas centrados em recursos em escala para uma assinatura ou grupo de recursos, você pode dividir por dimensões. Quando você quer monitorar a mesma condição em vários recursos do Azure, a divisão por dimensões divide os alertas agrupando combinações exclusivas usando colunas numéricas ou de cadeia de caracteres. A divisão na coluna de ID de recurso do Azure transforma o recurso especificado no destino do alerta.

Você também pode decidir não dividir quando quiser uma condição em vários recursos no escopo. Por exemplo, convém criar um alerta se o uso da CPU de pelo menos cinco computadores no escopo do grupo de recursos estiver acima de 80%.

Captura de tela que mostra uma nova regra de alerta de pesquisa de logs com divisão por dimensões.

Talvez você queira ver uma lista dos alertas com os computadores afetados. Você pode usar uma pasta de trabalho personalizada que usa um gráfico de recurso personalizado para fornecer essa exibição. Use a consulta a seguir para exibir alertas e use o Azure Resource Graph da fonte de dados na pasta de trabalho.

Criar uma regra de alerta de pesquisa de logs

Para criar uma regra de alerta de pesquisa de logs usando o portal, confira este exemplo de alerta de pesquisa de logs, que apresenta um passo a passo completo. Você pode usar esses mesmos processos para criar regras de alerta para clusters AKS usando consultas semelhantes àquelas neste artigo.

Para criar uma regra de alerta de consulta usando um modelo do ARM (Azure Resource Manager), confira Amostras de modelo do Resource Manager para regras de alerta de pesquisa de logs no Azure Monitor. Você pode usar esses mesmos processos para criar modelos do ARM para as consultas de log neste artigo.

Utilização de recursos

Utilização média da CPU como uma média da utilização da CPU dos nós de membros a cada minuto (medição métrica):

let endDateTime = now();
let startDateTime = ago(1h);
let trendBinSize = 1m;
let capacityCounterName = 'cpuCapacityNanoCores';
let usageCounterName = 'cpuUsageNanoCores';
KubeNodeInventory
| where TimeGenerated < endDateTime
| where TimeGenerated >= startDateTime
// cluster filter would go here if multiple clusters are reporting to the same Log Analytics workspace
| distinct ClusterName, Computer
| join hint.strategy=shuffle (
  Perf
  | where TimeGenerated < endDateTime
  | where TimeGenerated >= startDateTime
  | where ObjectName == 'K8SNode'
  | where CounterName == capacityCounterName
  | summarize LimitValue = max(CounterValue) by Computer, CounterName, bin(TimeGenerated, trendBinSize)
  | project Computer, CapacityStartTime = TimeGenerated, CapacityEndTime = TimeGenerated + trendBinSize, LimitValue
) on Computer
| join kind=inner hint.strategy=shuffle (
  Perf
  | where TimeGenerated < endDateTime + trendBinSize
  | where TimeGenerated >= startDateTime - trendBinSize
  | where ObjectName == 'K8SNode'
  | where CounterName == usageCounterName
  | project Computer, UsageValue = CounterValue, TimeGenerated
) on Computer
| where TimeGenerated >= CapacityStartTime and TimeGenerated < CapacityEndTime
| project ClusterName, Computer, TimeGenerated, UsagePercent = UsageValue * 100.0 / LimitValue
| summarize AggValue = avg(UsagePercent) by bin(TimeGenerated, trendBinSize), ClusterName

Utilização média da memória como uma média da utilização da memória dos nós de membros a cada minuto (medição métrica):

let endDateTime = now();
let startDateTime = ago(1h);
let trendBinSize = 1m;
let capacityCounterName = 'memoryCapacityBytes';
let usageCounterName = 'memoryRssBytes';
KubeNodeInventory
| where TimeGenerated < endDateTime
| where TimeGenerated >= startDateTime
// cluster filter would go here if multiple clusters are reporting to the same Log Analytics workspace
| distinct ClusterName, Computer
| join hint.strategy=shuffle (
  Perf
  | where TimeGenerated < endDateTime
  | where TimeGenerated >= startDateTime
  | where ObjectName == 'K8SNode'
  | where CounterName == capacityCounterName
  | summarize LimitValue = max(CounterValue) by Computer, CounterName, bin(TimeGenerated, trendBinSize)
  | project Computer, CapacityStartTime = TimeGenerated, CapacityEndTime = TimeGenerated + trendBinSize, LimitValue
) on Computer
| join kind=inner hint.strategy=shuffle (
  Perf
  | where TimeGenerated < endDateTime + trendBinSize
  | where TimeGenerated >= startDateTime - trendBinSize
  | where ObjectName == 'K8SNode'
  | where CounterName == usageCounterName
  | project Computer, UsageValue = CounterValue, TimeGenerated
) on Computer
| where TimeGenerated >= CapacityStartTime and TimeGenerated < CapacityEndTime
| project ClusterName, Computer, TimeGenerated, UsagePercent = UsageValue * 100.0 / LimitValue
| summarize AggValue = avg(UsagePercent) by bin(TimeGenerated, trendBinSize), ClusterName

Importante

As consultas a seguir usam os valores de espaço reservado <your-cluster-name> e <your-controller-name> para representar o cluster e o controlador. Substitua-os por valores específicos para seu ambiente ao configurar alertas.

Utilização média da CPU de todos os contêineres em um controlador como uma média de utilização da CPU de cada instância de contêiner em um controlador a cada minuto (medição métrica):

let endDateTime = now();
let startDateTime = ago(1h);
let trendBinSize = 1m;
let capacityCounterName = 'cpuLimitNanoCores';
let usageCounterName = 'cpuUsageNanoCores';
let clusterName = '<your-cluster-name>';
let controllerName = '<your-controller-name>';
KubePodInventory
| where TimeGenerated < endDateTime
| where TimeGenerated >= startDateTime
| where ClusterName == clusterName
| where ControllerName == controllerName
| extend InstanceName = strcat(ClusterId, '/', ContainerName),
         ContainerName = strcat(controllerName, '/', tostring(split(ContainerName, '/')[1]))
| distinct Computer, InstanceName, ContainerName
| join hint.strategy=shuffle (
    Perf
    | where TimeGenerated < endDateTime
    | where TimeGenerated >= startDateTime
    | where ObjectName == 'K8SContainer'
    | where CounterName == capacityCounterName
    | summarize LimitValue = max(CounterValue) by Computer, InstanceName, bin(TimeGenerated, trendBinSize)
    | project Computer, InstanceName, LimitStartTime = TimeGenerated, LimitEndTime = TimeGenerated + trendBinSize, LimitValue
) on Computer, InstanceName
| join kind=inner hint.strategy=shuffle (
    Perf
    | where TimeGenerated < endDateTime + trendBinSize
    | where TimeGenerated >= startDateTime - trendBinSize
    | where ObjectName == 'K8SContainer'
    | where CounterName == usageCounterName
    | project Computer, InstanceName, UsageValue = CounterValue, TimeGenerated
) on Computer, InstanceName
| where TimeGenerated >= LimitStartTime and TimeGenerated < LimitEndTime
| project Computer, ContainerName, TimeGenerated, UsagePercent = UsageValue * 100.0 / LimitValue
| summarize AggValue = avg(UsagePercent) by bin(TimeGenerated, trendBinSize) , ContainerName

Utilização média da memória de todos os contêineres em um controlador como uma média de utilização da memória de cada instância de contêiner em um controlador a cada minuto (medição métrica):

let endDateTime = now();
let startDateTime = ago(1h);
let trendBinSize = 1m;
let capacityCounterName = 'memoryLimitBytes';
let usageCounterName = 'memoryRssBytes';
let clusterName = '<your-cluster-name>';
let controllerName = '<your-controller-name>';
KubePodInventory
| where TimeGenerated < endDateTime
| where TimeGenerated >= startDateTime
| where ClusterName == clusterName
| where ControllerName == controllerName
| extend InstanceName = strcat(ClusterId, '/', ContainerName),
         ContainerName = strcat(controllerName, '/', tostring(split(ContainerName, '/')[1]))
| distinct Computer, InstanceName, ContainerName
| join hint.strategy=shuffle (
    Perf
    | where TimeGenerated < endDateTime
    | where TimeGenerated >= startDateTime
    | where ObjectName == 'K8SContainer'
    | where CounterName == capacityCounterName
    | summarize LimitValue = max(CounterValue) by Computer, InstanceName, bin(TimeGenerated, trendBinSize)
    | project Computer, InstanceName, LimitStartTime = TimeGenerated, LimitEndTime = TimeGenerated + trendBinSize, LimitValue
) on Computer, InstanceName
| join kind=inner hint.strategy=shuffle (
    Perf
    | where TimeGenerated < endDateTime + trendBinSize
    | where TimeGenerated >= startDateTime - trendBinSize
    | where ObjectName == 'K8SContainer'
    | where CounterName == usageCounterName
    | project Computer, InstanceName, UsageValue = CounterValue, TimeGenerated
) on Computer, InstanceName
| where TimeGenerated >= LimitStartTime and TimeGenerated < LimitEndTime
| project Computer, ContainerName, TimeGenerated, UsagePercent = UsageValue * 100.0 / LimitValue
| summarize AggValue = avg(UsagePercent) by bin(TimeGenerated, trendBinSize) , ContainerName

Disponibilidade de recursos

Nós e contagens com status Ready e NotReady (medição métrica):

let endDateTime = now();
let startDateTime = ago(1h);
let trendBinSize = 1m;
let clusterName = '<your-cluster-name>';
KubeNodeInventory
| where TimeGenerated < endDateTime
| where TimeGenerated >= startDateTime
| distinct ClusterName, Computer, TimeGenerated
| summarize ClusterSnapshotCount = count() by bin(TimeGenerated, trendBinSize), ClusterName, Computer
| join hint.strategy=broadcast kind=inner (
    KubeNodeInventory
    | where TimeGenerated < endDateTime
    | where TimeGenerated >= startDateTime
    | summarize TotalCount = count(), ReadyCount = sumif(1, Status contains ('Ready'))
                by ClusterName, Computer,  bin(TimeGenerated, trendBinSize)
    | extend NotReadyCount = TotalCount - ReadyCount
) on ClusterName, Computer, TimeGenerated
| project   TimeGenerated,
            ClusterName,
            Computer,
            ReadyCount = todouble(ReadyCount) / ClusterSnapshotCount,
            NotReadyCount = todouble(NotReadyCount) / ClusterSnapshotCount
| order by ClusterName asc, Computer asc, TimeGenerated desc

A consulta a seguir retorna contagens de fase de pod com base em todas as fases: Failed, Pending, Unknown, Running ou Succeeded.

let endDateTime = now(); 
let startDateTime = ago(1h);
let trendBinSize = 1m;
let clusterName = '<your-cluster-name>';
KubePodInventory
    | where TimeGenerated < endDateTime
    | where TimeGenerated >= startDateTime
    | where ClusterName == clusterName
    | distinct ClusterName, TimeGenerated
    | summarize ClusterSnapshotCount = count() by bin(TimeGenerated, trendBinSize), ClusterName
    | join hint.strategy=broadcast (
        KubePodInventory
        | where TimeGenerated < endDateTime
        | where TimeGenerated >= startDateTime
        | summarize PodStatus=any(PodStatus) by TimeGenerated, PodUid, ClusterName
        | summarize TotalCount = count(),
                    PendingCount = sumif(1, PodStatus =~ 'Pending'),
                    RunningCount = sumif(1, PodStatus =~ 'Running'),
                    SucceededCount = sumif(1, PodStatus =~ 'Succeeded'),
                    FailedCount = sumif(1, PodStatus =~ 'Failed')
                by ClusterName, bin(TimeGenerated, trendBinSize)
    ) on ClusterName, TimeGenerated
    | extend UnknownCount = TotalCount - PendingCount - RunningCount - SucceededCount - FailedCount
    | project TimeGenerated,
              TotalCount = todouble(TotalCount) / ClusterSnapshotCount,
              PendingCount = todouble(PendingCount) / ClusterSnapshotCount,
              RunningCount = todouble(RunningCount) / ClusterSnapshotCount,
              SucceededCount = todouble(SucceededCount) / ClusterSnapshotCount,
              FailedCount = todouble(FailedCount) / ClusterSnapshotCount,
              UnknownCount = todouble(UnknownCount) / ClusterSnapshotCount
| summarize AggValue = avg(PendingCount) by bin(TimeGenerated, trendBinSize)

Observação

Para alertar sobre certas fases de pod, como Pending, Failed ou Unknown, modifique a última linha da consulta. Por exemplo, para alertar sobre FailedCount, use | summarize AggValue = avg(FailedCount) by bin(TimeGenerated, trendBinSize).

A consulta a seguir retorna discos de nós de cluster que excedem 90% de espaço livre usado. Para obter a ID do cluster, primeiro execute a seguinte consulta e copie o valor da propriedade ClusterId:

InsightsMetrics
| extend Tags = todynamic(Tags)            
| project ClusterId = Tags['container.azm.ms/clusterId']   
| distinct tostring(ClusterId)   
let clusterId = '<cluster-id>';
let endDateTime = now();
let startDateTime = ago(1h);
let trendBinSize = 1m;
InsightsMetrics
| where TimeGenerated < endDateTime
| where TimeGenerated >= startDateTime
| where Origin == 'container.azm.ms/telegraf'            
| where Namespace == 'container.azm.ms/disk'            
| extend Tags = todynamic(Tags)            
| project TimeGenerated, ClusterId = Tags['container.azm.ms/clusterId'], Computer = tostring(Tags.hostName), Device = tostring(Tags.device), Path = tostring(Tags.path), DiskMetricName = Name, DiskMetricValue = Val   
| where ClusterId =~ clusterId       
| where DiskMetricName == 'used_percent'
| summarize AggValue = max(DiskMetricValue) by bin(TimeGenerated, trendBinSize)
| where AggValue >= 90

O contêiner individual reinicia o alerta (número de resultados) quando a contagem de reinicialização de contêiner do sistema individual excede um limite para os últimos 10 minutos:

let _threshold = 10m; 
let _alertThreshold = 2;
let Timenow = (datetime(now) - _threshold); 
let starttime = ago(5m); 
KubePodInventory
| where TimeGenerated >= starttime
| where Namespace in ('default', 'kube-system') // the namespace filter goes here
| where ContainerRestartCount > _alertThreshold
| extend Tags = todynamic(ContainerLastStatus)
| extend startedAt = todynamic(Tags.startedAt)
| where startedAt >= Timenow
| summarize arg_max(TimeGenerated, *) by Name

Próximas etapas