Creación de alertas de búsqueda de registros desde Container Insights

Container Insights supervisa el rendimiento de las cargas de trabajo de contenedor implementadas en clústeres de Kubernetes administrados o autoadministrados. Para alertar sobre lo que importa, en este artículo se describe cómo crear alertas basadas en registro para las siguientes situaciones con los clústeres de Azure Kubernetes Service (AKS):

  • Cuando el uso de CPU o memoria en los nodos del clúster supera un umbral
  • Cuando el uso de CPU o memoria en cualquier contenedor dentro de un controlador supera un umbral en comparación con un límite que se establece en el recurso correspondiente
  • Recuentos de nodos de estado NotReady
  • Recuentos de fases de pod Failed, Pending, Unknown, Running o Succeeded
  • Cuando el espacio libre en disco de los nodos del clúster supera un umbral

Para generar una alerta sobre el uso elevado de CPU o de memoria o sobre poco espacio libre en disco en los nodos del clúster, use las consultas que se proporcionan para crear una alerta de métrica o una alerta de medida de métrica. Las alertas de métricas tienen una latencia menor que las alertas de búsqueda de registros, pero las alertas de búsqueda de registros proporcionan consultas avanzadas y mayor sofisticación. Las consultas de alertas de búsqueda de registros comparan una fecha y hora con el operador now y vuelven una hora. (Container Insights almacena todas las fechas en el formato Hora universal coordinada [UTC].)

Importante

Las consultas de este artículo dependen de los datos recopilados por Container Insights y almacenados en un área de trabajo de Log Analytics. Si has modificado la configuración predeterminada de recopilación de datos, es posible que las consultas no devuelvan los resultados esperados. En particular, si has deshabilitado la recopilación de datos de rendimiento, puesto que has habilitado las métricas de Prometheus para el clúster, las consultas que usen la tabla Perf no devolverán resultados.

Consulta Configuración de la recopilación de datos en Container Insights mediante la regla de recopilación de datos para obtener información sobre las configuraciones preestablecidas, incluida la deshabilitación de la recopilación de datos de rendimiento. Consulta Configuración de la recopilación de datos en Container Insights mediante ConfigMap para ver más opciones de recopilación de datos.

Si no está familiarizado con las alertas en Azure Monitor, consulte Información general sobre las alertas en Microsoft Azure antes de empezar. Para más información acerca de las alertas que usan consultas de búsqueda de registros, consulte Alertas de registro en Azure Monitor. Para más información acerca de las alertas de métrica consulte Comprender cómo funcionan las alertas de métricas en Azure Monitor.

Medidas de consulta de registro

Las alertas de búsqueda de registros miden dos cosas diferentes que se pueden usar para supervisar las máquinas virtuales en escenarios diferentes:

  • Recuento de resultados: cuenta el número de filas devueltas por la consulta y se puede usar para trabajar con eventos como registros de eventos de Windows, Syslog y excepciones de aplicación.
  • Cálculo de un valor: realiza un cálculo a partir de una columna numérica y se puede usar para incluir cualquier número de recursos. Un ejemplo es el porcentaje de CPU.

Recursos de destino y dimensiones

Puede utilizar una regla para supervisar los valores de varias instancias mediante dimensiones. Por ejemplo, usaría dimensiones si deseara supervisar el uso de CPU en varias instancias que ejecutan el sitio web o la aplicación y crear una alerta para un uso de CPU por encima del 80 %.

Para crear alertas centradas en recursos a escala para una suscripción o grupo de recursos, puede usar Dividir por dimensiones. Si desea supervisar la misma condición en varios recursos de Azure, la división por dimensiones divide las alertas en alertas independientes mediante la agrupación de combinaciones únicas con columnas numéricas o de cadena. Al dividir una columna de identificador de recurso de Azure, el recurso especificado se convierte en el destino de la alerta.

También puede decidir no dividir cuando desea una condición en varios recursos del ámbito. Por ejemplo, quiere crear una alerta si al menos cinco máquinas del ámbito del grupo de recursos tienen un uso de CPU por encima del 80 %.

Captura de pantalla que muestra una nueva regla de alertas de búsqueda de registros con Dividir por dimensiones.

Es posible que quiera ver una lista de las alertas por equipo afectado. Para proporcionar esta vista, puede usar un libro personalizado que emplee un gráfico de recursos personalizado. Use la consulta siguiente para mostrar alertas y utilice el origen de datos Azure Resource Graph en el libro.

Creación de una regla de alertas de búsqueda de registros

Para crear una regla de alertas de búsqueda de registros mediante el portal, consulte este ejemplo de una alerta de búsqueda de registros, que proporciona un tutorial completo. Puede usar estos mismos procesos para crear reglas de alerta para clústeres de AKS mediante consultas similares a las de este artículo.

Para crear una regla de alertas de consulta mediante una plantilla de Azure Resource Manager (ARM), consulte los ejemplos de plantillas de Resource Manager para reglas de alertas de búsqueda de registros en Azure Monitor. Puede usar estos mismos procesos para crear plantillas de ARM para las consultas de registro de este artículo.

Utilización de recursos

Uso medio de la CPU como promedio de uso de la CPU de los nodos miembros cada minuto (unidad 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

Uso medio de la memoria como promedio de uso de la memoria de los nodos miembros cada minuto (unidad 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

En las consultas siguientes se utilizan los valores de marcador de posición <your-cluster-name> y <your-controller-name> para representar el clúster y el controlador. Reemplácelos por valores específicos del entorno al configurar las alertas.

Uso medio de la CPU de todos los contenedores de un controlador como promedio de uso de la CPU de cada instancia de contenedor en un controlador cada minuto (unidad 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

Uso medio de la memoria de todos los contenedores de un controlador como promedio de uso de la memoria de cada instancia de contenedor en un controlador cada minuto (unidad 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

Disponibilidad de recursos

Nodos y recuentos que tienen el estado Ready y NotReady (unidad 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

En la siguiente consulta se devuelven recuentos de fases de pod basados en todas las fases: Failed, Pending, Unknown, Running o 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)

Nota

Para generar una alerta en ciertas fases de pod, tales como Pending, Failed o Unknown, modifique la última línea de la consulta. Por ejemplo, para alertar sobre FailedCount, utilice | summarize AggValue = avg(FailedCount) by bin(TimeGenerated, trendBinSize).

En la consulta siguiente se devuelven los discos de los nodos de clúster que superan el 90 % del espacio libre utilizado. Para obtener el id. de clúster, primero ejecute la siguiente consulta y copie el valor de la propiedad 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

Alerta de reinicios de contenedores individuales (número de resultados) cuando el recuento de reinicios de contenedores individuales del sistema supera un umbral durante los ú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

Pasos siguientes