Créer d’alertes de recherche dans les journaux à partir de Container Insights.

Container Insights surveille les performances des charges de travail de conteneur déployées sur des clusters Kubernetes gérés ou auto-gérés. Pour attirer l’attention sur ce qui compte, cet article explique comment créer des alertes basées sur le journal en réponse aux situations suivantes avec des clusters Azure Kubernetes Service (AKS) :

  • Lorsque l’utilisation de la mémoire ou de l’UC sur les nœuds de cluster dépasse un seuil
  • Lorsque l’utilisation de la mémoire ou de l’UC sur n’importe quel conteneur au sein d’un contrôleur dépasse un seuil par rapport à une limite définie sur la ressource correspondante
  • Nombre de nœuds d’état NotReady
  • Nombres de pods en phase Failed, Pending, Unknown, Running ou Succeeded
  • Lorsque espace disque libre sur les nœuds de cluster dépasse un seuil

Pour alerter en cas d’utilisation élevée de la mémoire ou de l’UC, ou de manque d’espace disponible sur les nœuds de cluster, utilisez les requêtes fournies pour créer une alerte de métrique ou une alerte de mesure de métrique. Les alertes métriques présentent une latence inférieure à celle des alertes recherche dans les journaux. Ces dernières offrent toutefois des capacités de requête avancées et une plus grande sophistication. Les requêtes de recherche dans les journaux comparent une valeur DateHeure au présent en utilisant l’opérateur now et en remontant d’une heure. (Container Insights stocke toutes les dates au format de temps universel coordonné UTC.)

Important

Les requêtes de cet article dépendent des données collectées par Container Insights et sont stockées dans un espace de travail Log Analytics. Si vous avez modifié les paramètres de collecte de données par défaut, les requêtes peuvent ne pas retourner les résultats attendus. Plus particulièrement, si vous avez désactivé la collecte des données de performances depuis que vous avez activé les métriques Prometheus pour le cluster, toutes les requêtes utilisant la table Perf ne retourneront pas de résultats.

Consultez Configurer la collecte de données dans Container Insights à l’aide d’une règle de collecte de données pour les configurations prédéfinies, notamment la désactivation de la collecte de données de performances. Consultez Configurer la collecte de données dans Container Insights à l’aide de ConfigMap pour obtenir d’autres options de collecte de données.

Si vous ne connaissez pas les alertes d’Azure Monitor, consultez Vue d’ensemble des alertes dans Microsoft Azure avant de commencer. Pour en savoir plus sur les alertes utilisant des requêtes de recherche dans les journaux, consultez Alertes de journal dans Azure Monitor. Pour en savoir plus sur les alertes de métrique, consultez Alertes de métriques dans Azure Monitor.

Mesures des requêtes de journal

Les alertes de recherche dans les journaux peuvent mesurer deux choses différentes, qui peuvent être utilisées pour surveiller des machines virtuelles dans différents scénarios :

  • Nombre de résultats : compte le nombre de lignes retournées par la requête et permet de travailler avec des événements comme des journaux des événements Windows, Syslog et les exceptions des applications.
  • Calcul d’une valeur : effectue des calculs sur une colonne numérique et permet d’inclure le nombre de ressources de votre choix. Par exemple, le pourcentage d’UC.

Ciblage de ressources et de dimensions

Il est possible d’utiliser une seule règle pour surveiller les valeurs de plusieurs instances à l’aide de dimensions. Par exemple, vous pouvez employer des dimensions si vous voulez superviser l’utilisation du processeur sur plusieurs instances exécutant votre site web ou votre application, et créer une alerte en cas d’utilisation supérieure à 80 % du processeur.

Pour créer des alertes axées sur les ressources à grande échelle pour un abonnement ou un groupe de ressources, vous pouvez effectuer un fractionnement par dimensions. Lorsque vous souhaitez surveiller la même condition sur plusieurs ressources Azure, le fractionnement par dimensions divise les alertes en alertes distinctes en regroupant les combinaisons uniques selon des colonnes numériques ou de type chaîne. En fractionnant une colonne d’ID de ressources Azure, vous faites de la ressource spécifiée la cible d’alerte.

Vous pouvez également décider de ne pas effectuer de fractionnement lorsque vous voulez qu’une condition s’applique à plusieurs ressources de l’étendue. Par exemple, il est possible de déclencher une alerte si au moins cinq machines de l’étendue du groupe de ressources présentent une utilisation du processeur supérieure à 80 %.

Capture d’écran d’une nouvelle règle d’alerte de recherche de journal fractionnée par dimensions.

Vous pouvez visualiser une liste des alertes par l’ordinateur affecté. Vous pouvez utiliser un classeur personnalisé qui s’appuie sur un graphique de ressources personnalisé pour fournir cette vue. Utilisez la requête suivante pour afficher les alertes, et utilisez la source de données Azure Resource Graph dans le classeur.

Créer une règle d’alerte de recherche dans les journaux

Pour créer une règle d’alerte de recherche dans les journaux depuis le portail, consultez cet exemple d’alerte de recherche dans les journaux, qui fournit une procédure pas à pas complète. Vous pouvez suivre ces mêmes procédures afin de créer des règles d’alerte pour les clusters AKS à l’aide de requêtes similaires à celles de cet article.

Pour créer une règle d’alerte de requête à l’aide d’un modèle Azure Resource Manager (ARM), consultez Exemples de modèles Resource Manager pour les règles d’alerte recherche dans les journaux dans Azure Monitor. Vous pouvez utiliser ces mêmes processus pour créer des modèles ARM pour les requêtes de journal dans cet article.

Utilisation des ressources

Utilisation moyenne du processeur exprimée comme la moyenne de l’utilisation du processeur par les nœuds membres chaque minute (mesure de métrique) :

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

Utilisation moyenne de la mémoire exprimée comme la moyenne de l’utilisation de la mémoire par les nœuds membres chaque minute (mesure de métrique) :

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

Important

Les requêtes suivantes utilisent les valeurs d’espace réservé <your-cluster-name> > et <your-controller-name> > pour représenter votre cluster et votre contrôleur. Remplacez-les par des valeurs propres à votre environnement lorsque vous définissez des alertes.

Utilisation moyenne du processeur par tous les conteneurs d’un contrôleur exprimée comme la moyenne de l’utilisation du processeur par chacune des instances de conteneur d’un contrôleur chaque minute (mesure de métrique) :

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

Utilisation moyenne de la mémoire par tous les conteneurs d’un contrôleur exprimée comme la moyenne de l’utilisation de la mémoire par chacune des instances de conteneur d’un contrôleur chaque minute (mesure de métrique) :

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

Disponibilité des ressources

Nœuds et nombres dont l’état est Ready (Prêt) et NotReady (Non prêt) (mesure métrique) :

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

La requête suivante renvoie le nombre de phases de pod pour toutes les phases : Failed, Pending, Unknown, Running et 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)

Notes

Pour alerter sur certaines phases pod, telles que Pending, Failed ou Unknown, modifiez la dernière ligne de la requête. Pour lancer une alerte sur FailedCount par exemple, utilisez | summarize AggValue = avg(FailedCount) by bin(TimeGenerated, trendBinSize).

La requête suivante renvoie les disques de nœuds de clusters qui dépassent 90 % d’espace utilisé. Pour obtenir l’ID de cluster, exécutez tout d’abord la requête suivante et copiez la valeur de la propriété 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

Voici une alerte de redémarrage de conteneur individuel (nombre de résultats) lorsque le nombre de redémarrages de conteneurs système individuels dépasse un certain seuil au cours des 10 dernières minutes :

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

Étapes suivantes