Erstellen von Protokollsuchwarnungen in Container Insights

Container Insights überwacht die Leistung von Containerworkloads, die für verwaltete oder selbstverwaltete Kubernetes-Cluster bereitgestellt werden. Um Warnungen zu wichtigen Aspekten zu erhalten, wird in diesem Artikel beschrieben, wie Sie protokollbasierte Warnungen für die folgenden Situationen mit AKS-Clustern (Azure Kubernetes Service) erstellen:

  • Wenn die CPU- oder Arbeitsspeicherauslastung für Clusterknoten einen Schwellenwert überschreitet
  • Wenn die CPU- oder Arbeitsspeicherauslastung für einen Container innerhalb eines Controllers einen Schwellenwert relativ zu einem für die entsprechende Ressource festgelegten Grenzwert überschreitet
  • Anzahl von Knoten mit Status NotReady
  • Anzahl der Podphasen Failed, Pending, Unknown, Running oder Succeeded
  • Wenn der freie Speicherplatz für Clusterknoten einen Schwellenwert überschreitet

Verwenden Sie zum Auslösen einer Warnung bei hoher CPU- oder Arbeitsspeicherauslastung oder wenig freiem Speicherplatz für Clusterknoten die bereitgestellten Abfragen, um eine Metrikwarnung oder eine Warnung aufgrund von metrischen Messungen zu erstellen. Im Vergleich zu Protokollsuchwarnungen haben Metrikwarnungen zwar eine geringere Wartezeit, aber Protokollsuchwarnungen bieten erweiterte Abfragen und mehr Komplexität. Abfragen für Protokollsuchwarnungen nutzen den now-Operator und gehen eine Stunde zurück, um einen datetime-Wert mit der aktuellen Zeit zu vergleichen. (Container Insights speichert alle Daten im UTC-Format (koordinierte Weltzeit).)

Wichtig

Die Abfragen in diesem Artikel hängen von Daten ab, die von Containereinblicken gesammelt und in einem Log Analytics-Arbeitsbereich gespeichert sind. Wenn Sie die Standardeinstellungen für die Datensammlung geändert haben, geben die Abfragen möglicherweise nicht die erwarteten Ergebnisse zurück. Vor allem, wenn Sie die Sammlung von Leistungsdaten deaktiviert haben, da Sie Prometheus-Metriken für den Cluster aktiviert haben, geben alle Abfragen, welche die Perf-Tabelle verwenden, keine Ergebnisse zurück.

Siehe Konfigurieren der Datensammlung in Containereinblicken mithilfe der Datensammlungsregel für voreingestellte Konfigurationen, einschließlich der Deaktivierung der Leistungsdatensammlung. Weitere Datensammlungsoptionen finden Sie unter Konfigurieren der Datensammlung in Containereinblicken mithilfe von ConfigMap.

Sollten Sie nicht mit Azure Monitor-Warnungen vertraut sein, sehen Sie sich zunächst den Überblick über Warnungen in Microsoft Azure an. Weitere Informationen zu Warnungen, bei denen Protokollabfragen verwendet werden, finden Sie unter Protokollsuchwarnungen in Azure Monitor. Weitere Informationen zu Metrikwarnungen finden Sie unter Metrikwarnungen in Azure Monitor.

Protokollabfragemessungen

Protokollsuchwarnungen können zwei verschiedene Dinge messen, die in verschiedenen Szenarien zum Überwachen von VMs verwendet werden können:

  • Ergebnisanzahl: Erfasst die Anzahl der von der Abfrage zurückgegebenen Zeilen und kann zum Arbeiten mit Ereignissen wie Windows-Ereignisprotokollen, Syslog und Anwendungsausnahmen verwendet werden.
  • Berechnung eines Werts: Führt eine Berechnung basierend auf einer numerischen Spalte durch und kann verwendet werden, um eine beliebige Anzahl von Ressourcen einzuschließen. Ein Beispiel ist die prozentuale CPU-Auslastung.

Ausrichten auf Ressourcen und Dimensionen

Dimensionen ermöglichen die Überwachung der Werte mehrerer Instanzen mithilfe einer einzelnen Regel. Dimensionen können beispielsweise verwendet werden, um die CPU-Auslastung mehrerer Instanzen zu überwachen, die Ihre Website oder App ausführen, und eine Warnung zu erstellen, wenn die CPU-Auslastung 80 Prozent übersteigt.

Durch Aufteilen nach Dimensionen können Sie ressourcenorientierte Warnungen im großen Stil für ein Abonnement oder eine Ressourcengruppe erstellen. Wenn Sie die gleiche Bedingung für mehrere Azure-Ressourcen überwachen möchten, werden die Warnungen durch Aufteilen nach Dimensionen in separate Warnungen aufgeteilt, indem eindeutige Kombinationen mit numerischen Spalten oder Zeichenfolgenspalten gruppiert werden. Wenn Sie eine Ressourcen-ID-Spalte von Azure aufteilen, wird die angegebene Ressource zum Warnungsziel.

Sie können sich auch gegen eine Aufteilung entscheiden, wenn Sie eine Bedingung für mehrere Ressourcen in dem Bereich haben möchten. Es kann beispielsweise wünschenswert sein, eine Warnung zu erstellen, wenn mindestens fünf Computer im Ressourcengruppenbereich eine CPU-Auslastung von über 80 Prozent aufweisen.

Screenshot, der eine neue Warnungsregel für die Protokollsuche mit Aufteilung nach Dimensionen zeigt.

Möglicherweise möchten Sie eine Liste der Warnungen des betroffenen Computers anzeigen. Sie erhalten eine solche Ansicht durch die Verwendung einer benutzerdefinierten Arbeitsmappe, die eine benutzerdefinierte Instanz von Resource Graph verwendet. Verwenden Sie die folgende Abfrage, um Warnungen anzuzeigen, und verwenden Sie in der Arbeitsmappe die Datenquelle Azure Resource Graph.

Erstellen einer Warnungsregel für die Protokollsuche

Informationen dazu, wie Sie eine Warnungsregel für die Protokollsuche über das Portal erstellen, finden Sie in diesem Beispiel für eine Protokollsuchwarnung, das eine exemplarische Vorgehensweise bietet. Sie können die gleichen Prozesse verwenden, um Warnungsregeln für AKS-Cluster zu erstellen, indem Sie ähnliche Abfragen wie in diesem Artikel verwenden.

Informationen zum Erstellen einer Abfragewarnungsregel mit einer ARM-Vorlage (Azure Resource Manager) finden Sie unter Resource Manager-Beispielvorlagen für Warnungsregeln für die Protokollsuche in Azure Monitor. Sie können dieselben Prozesse verwenden, um ARM-Vorlagen für die Protokollabfragen in diesem Artikel zu erstellen.

Ressourcenverwendung

Durchschnittliche CPU-Auslastung als durchschnittliche CPU-Auslastung von Memberknoten pro Minute (metrische Maßeinheit):

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

Durchschnittliche Arbeitsspeicherauslastung als durchschnittliche Arbeitsspeicherauslastung der Mitgliedsknoten pro Minute (metrische Maßeinheit):

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

Wichtig

In den folgenden Abfragen werden die Platzhalterwerte „<Ihr-Cluster-Name>“ und „<Ihr-Controller-Name>“ für Ihren Cluster bzw. Controller verwendet. Ersetzen Sie sie durch spezifische Werte für Ihre Umgebung, wenn Sie Warnungen einrichten.

Durchschnittliche CPU-Auslastung aller Container in einem Controller als durchschnittliche CPU-Auslastung der einzelnen Containerinstanzen in einem Controller pro Minute (metrische Maßeinheit):

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

Durchschnittliche Arbeitsspeicherauslastung aller Container in einem Controller als durchschnittliche Arbeitsspeicherauslastung der einzelnen Containerinstanzen in einem Controller pro Minute (metrische Maßeinheit):

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

Ressourcenverfügbarkeit

Knoten und Anzahlen mit dem Status „Ready“ und „NotReady“ (metrische Maßeinheit):

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

Die folgende Abfrage gibt die Podphasenanzahl, basierend auf allen Phasen (Failed, Pending, Unknown, Running oder Succeeded) zurück.

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)

Hinweis

Ändern Sie zum Ausgeben von Warnungen bei bestimmten Podphasen (z.B. Pending, Failed oder Unknown) die letzte Zeile der Abfrage. Verwenden Sie beispielsweise | summarize AggValue = avg(FailedCount) by bin(TimeGenerated, trendBinSize), um Warnungen für FailedCount zu erhalten.

Die folgende Abfrage gibt die Festplatten der Clusterknoten zurück, die mehr als 90 % freien Speicherplatz belegen. Führen Sie zum Abrufen der Cluster-ID zunächst die folgende Abfrage aus, und kopieren Sie den Wert aus der Eigenschaft 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

Warnung zum Neustart einzelner Container (Anzahl von Ergebnissen), wenn die Anzahl von Neustarts einzelner Systemcontainer einen Schwellenwert für die letzten zehn Minuten überschreitet:

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

Nächste Schritte