We have discussed "Deploying Azure Managed Grafana with Terraform" ealier. Now that we have managed grafana available in Azure for the AKS clsuters we can enable managed prometheus. You can setup prometheus on your own in AKS however it is beter to use Azure managed prometheus with AKS as it would leverage the capabilities of Azure monitor and Azure managed grafana to better monitoring and alerting abilities and avoid additional work required to setup full monitoring and observability on your own in an AKS cluster. The offcial documentation is here for leaning more information on the setup.
The expectaion is to have monotring setup with managed prometheus as shown below.
To setup we need below terraform variables setup in local.
locals {
kubernetes_version = "1.33.2"
subscription_id = "subscription_id"
tenant_id = "tenant_id"
spn_app_id = "spn_app_id"
spn_pwd = "spn_pwd" # replace with your actual service principal password
log_dataflow_streams = [
"Microsoft-ContainerLogV2",
"Microsoft-KubeEvents",
"Microsoft-KubePodInventory",
"Microsoft-KubeNodeInventory",
"Microsoft-KubePVInventory",
"Microsoft-KubeServices",
"Microsoft-KubeMonAgentEvents",
"Microsoft-InsightsMetrics",
"Microsoft-ContainerInventory",
"Microsoft-ContainerNodeInventory",
"Microsoft-Perf"
]
enable_high_log_scale_mode = contains(local.log_dataflow_streams, "Microsoft-ContainerLogV2-HighScale")
}
Then we can set aks clsuter with managed prometheus using below terraform code. This is using the managed grafana setup as described in "Deploying Azure Managed Grafana with Terraform". Full code example is available in GitHub here as well. If you have windows nodes as well enable the windows rule groups as well.
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "=4.38.1"
}
azuread = {
source = "hashicorp/azuread"
version = "=3.4.0"
}
}
}
provider "azurerm" {
features {}
subscription_id = local.subscription_id
}
#region Basic AKS setup
# resource group for aks
resource "azurerm_resource_group" "aks_rg" {
name = "rg-chdemo-dev01"
location = "eastus"
}
# vnet for aks
resource "azurerm_virtual_network" "aks_vnet" {
name = "vnet_chdemo_dev01"
resource_group_name = azurerm_resource_group.aks_rg.name
location = azurerm_resource_group.aks_rg.location
address_space = ["10.235.0.0/16"]
}
# subnet for aks
resource "azurerm_subnet" "aks_snet" {
name = "snet-aks-chdemo-dev01"
resource_group_name = azurerm_resource_group.aks_rg.name
virtual_network_name = azurerm_virtual_network.aks_vnet.name
address_prefixes = ["10.235.128.0/22"]
}
# refer to my team ad group to assign as aks admins
data "azuread_group" "myteam" {
display_name = "sub_owners"
security_enabled = true
}
# AKS user assigned identity
resource "azurerm_user_assigned_identity" "aks" {
location = azurerm_resource_group.aks_rg.location
name = "uai-aks-chdemo-dev01"
resource_group_name = azurerm_resource_group.aks_rg.name
}
# Log analytics workspace for AKS and Application Insights
resource "azurerm_log_analytics_workspace" "instance_log" {
name = "log-chdemo-dev01"
location = azurerm_resource_group.aks_rg.location
resource_group_name = azurerm_resource_group.aks_rg.name
retention_in_days = 30
}
# acr
resource "azurerm_container_registry" "acr" {
name = "acrchdemodev01"
resource_group_name = azurerm_resource_group.aks_rg.name
location = azurerm_resource_group.aks_rg.location
sku = "Standard"
admin_enabled = false
}
# aks cluster
resource "azurerm_kubernetes_cluster" "aks" {
# any autoscaling should not be reset by TF after intial setup
lifecycle {
ignore_changes = [default_node_pool[0].node_count]
}
name = "aks-chdemo-dev01"
kubernetes_version = local.kubernetes_version
location = azurerm_resource_group.aks_rg.location
resource_group_name = azurerm_resource_group.aks_rg.name
dns_prefix = "aks-chdemo-dev01-dns"
node_resource_group = "rg-chdemo-aks-dev01"
image_cleaner_enabled = false
image_cleaner_interval_hours = 48
network_profile {
network_plugin = "azure"
load_balancer_sku = "standard"
network_plugin_mode = "overlay"
pod_cidr = "100.112.0.0/12"
}
storage_profile {
file_driver_enabled = true
}
default_node_pool {
name = "chlinux"
orchestrator_version = local.kubernetes_version
node_count = 1
auto_scaling_enabled = true
min_count = 1
max_count = 4
vm_size = "Standard_B4ms"
os_sku = "Ubuntu"
vnet_subnet_id = azurerm_subnet.aks_snet.id
max_pods = 30
type = "VirtualMachineScaleSets"
scale_down_mode = "Delete"
zones = ["1", "2", "3"]
upgrade_settings {
drain_timeout_in_minutes = 0
max_surge = "10%"
node_soak_duration_in_minutes = 0
}
}
#region promethus
monitor_metrics {
annotations_allowed = null
labels_allowed = null
}
#endregion promethus
timeouts {
update = "180m"
delete = "180m"
}
identity {
type = "UserAssigned"
identity_ids = [azurerm_user_assigned_identity.aks.id]
}
key_vault_secrets_provider {
secret_rotation_enabled = false
}
azure_active_directory_role_based_access_control {
azure_rbac_enabled = false
tenant_id = local.tenant_id
# add my team as cluster admin
admin_group_object_ids = [
data.azuread_group.myteam.object_id] # azure AD group object ID
}
oms_agent {
log_analytics_workspace_id = azurerm_log_analytics_workspace.instance_log.id
msi_auth_for_monitoring_enabled = true
}
}
resource "azurerm_role_assignment" "acr_attach" {
principal_id = azurerm_kubernetes_cluster.aks.kubelet_identity[0].object_id
role_definition_name = "AcrPull"
scope = azurerm_container_registry.acr.id
skip_service_principal_aad_check = true
depends_on = [
azurerm_kubernetes_cluster.aks,
azurerm_container_registry.acr
]
}
#endregion Basic AKS setup
#region Managed Prometheus setup
# Refer https://learn.microsoft.com/en-us/azure/azure-monitor/containers/kubernetes-monitoring-enable?tabs=terraform
# Azure monitor workspace
resource "azurerm_monitor_workspace" "instance_amw" {
name = "amw-chdemo-dev01"
location = azurerm_resource_group.aks_rg.location
resource_group_name = azurerm_resource_group.aks_rg.name
}
# Common data collection endpoint for prometheus and container insights
resource "azurerm_monitor_data_collection_endpoint" "dce" {
name = substr("MSProm-${azurerm_resource_group.aks_rg.location}-${azurerm_kubernetes_cluster.aks.name}", 0, min(44, length("MSProm-${azurerm_resource_group.aks_rg.location}-${azurerm_kubernetes_cluster.aks.name}")))
resource_group_name = azurerm_resource_group.aks_rg.name
location = azurerm_resource_group.aks_rg.location
kind = "Linux"
depends_on = [
azurerm_kubernetes_cluster.aks
]
}
# Prometheus data collection rule
resource "azurerm_monitor_data_collection_rule" "prometheus_dcr" {
name = substr("MSProm-${azurerm_resource_group.aks_rg.location}-${azurerm_kubernetes_cluster.aks.name}", 0, min(64, length("MSProm-${azurerm_resource_group.aks_rg.location}-${azurerm_kubernetes_cluster.aks.name}")))
resource_group_name = azurerm_resource_group.aks_rg.name
location = azurerm_resource_group.aks_rg.location
data_collection_endpoint_id = azurerm_monitor_data_collection_endpoint.dce.id
kind = "Linux"
destinations {
monitor_account {
monitor_account_id = azurerm_monitor_workspace.instance_amw.id
name = "MonitoringAccount1"
}
}
data_flow {
streams = ["Microsoft-PrometheusMetrics"]
destinations = ["MonitoringAccount1"]
}
data_sources {
prometheus_forwarder {
streams = ["Microsoft-PrometheusMetrics"]
name = "PrometheusDataSource"
}
}
description = "DCR for Azure Monitor Metrics Profile (Managed Prometheus)"
depends_on = [
azurerm_monitor_data_collection_endpoint.dce
]
}
# Prometheus data collection rule association
resource "azurerm_monitor_data_collection_rule_association" "prometheus_dcra" {
name = "MSProm-${azurerm_resource_group.aks_rg.location}-${azurerm_kubernetes_cluster.aks.name}"
target_resource_id = azurerm_kubernetes_cluster.aks.id
data_collection_rule_id = azurerm_monitor_data_collection_rule.prometheus_dcr.id
description = "Association of data collection rule. Deleting this association will break the data collection for this AKS Cluster."
depends_on = [
azurerm_monitor_data_collection_rule.prometheus_dcr
]
}
# Container Insights data collection rule
resource "azurerm_monitor_data_collection_rule" "ci_dcr" {
name = "MSCI-${azurerm_resource_group.aks_rg.location}-${azurerm_kubernetes_cluster.aks.name}"
resource_group_name = azurerm_resource_group.aks_rg.name
location = azurerm_resource_group.aks_rg.location
destinations {
log_analytics {
workspace_resource_id = azurerm_log_analytics_workspace.instance_log.id
name = "ciworkspace"
}
}
data_flow {
streams = local.log_dataflow_streams
destinations = ["ciworkspace"]
}
data_sources {
extension {
streams = local.log_dataflow_streams
extension_name = "ContainerInsights"
extension_json = jsonencode({
"dataCollectionSettings" : {
"interval" : "1m",
"namespaceFilteringMode" : "Off",
"namespaces" : ["kube-system", "gatekeeper-system"]
"enableContainerLogV2" : true
}
})
name = "ContainerInsightsExtension"
}
}
data_collection_endpoint_id = local.enable_high_log_scale_mode ? azurerm_monitor_data_collection_endpoint.dce.id : null
description = "DCR for Azure Monitor Container Insights"
depends_on = [
azurerm_monitor_data_collection_endpoint.dce
]
}
# Container Insights data collection rule association
resource "azurerm_monitor_data_collection_rule_association" "ci_dcra" {
name = "ContainerInsightsExtension"
target_resource_id = azurerm_kubernetes_cluster.aks.id
data_collection_rule_id = azurerm_monitor_data_collection_rule.ci_dcr.id
description = "Association of container insights data collection rule. Deleting this association will break the data collection for this AKS Cluster."
depends_on = [
azurerm_monitor_data_collection_rule.ci_dcr
]
}
# Managed Grafana setup
data "azurerm_resource_group" "grafana_rg" {
name = "ch-demo-grafana-shared-rg"
}
data "azurerm_dashboard_grafana" "grafana" {
name = "ch-demo-shared-dg-001"
resource_group_name = data.azurerm_resource_group.grafana_rg.name
}
# Managed Grafana integration with Azure Monitor Workspace
resource "null_resource" "amw_grafana" {
triggers = {
amw_id = azurerm_monitor_workspace.instance_amw.id
}
depends_on = [
azurerm_monitor_workspace.instance_amw,
data.azurerm_dashboard_grafana.grafana
]
provisioner "local-exec" {
command = <<-SHELL
az login --service-principal -u ${local.spn_app_id} -p ${local.spn_pwd} --tenant ${local.tenant_id}
az extension add --name amg --upgrade --yes
az grafana integrations monitor add --monitor-name ${azurerm_monitor_workspace.instance_amw.name} --monitor-resource-group-name ${azurerm_resource_group.aks_rg.name} --monitor-subscription-id ${local.subscription_id} --name ${data.azurerm_dashboard_grafana.grafana.name} --resource-group ${data.azurerm_resource_group.grafana_rg.name} --subscription ${local.subscription_id}
SHELL
interpreter = ["PowerShell"]
}
}
# Assign "Monitoring Data Reader" role to the Azure monitor workspace for Grafana
# https://www.azadvertizer.net/azrolesadvertizer/b0d8363b-8ddd-447d-831f-62ca05bff136.html
resource "azurerm_role_assignment" "datareaderrole" {
scope = azurerm_monitor_workspace.instance_amw.id
role_definition_id = "/subscriptions/${split("/", azurerm_monitor_workspace.instance_amw.id)[2]}/providers/Microsoft.Authorization/roleDefinitions/b0d8363b-8ddd-447d-831f-62ca05bff136"
principal_id = data.azurerm_dashboard_grafana.grafana.identity.0.principal_id
}
# Managed Prometheus recording rules for AKS
resource "azurerm_monitor_alert_prometheus_rule_group" "node_recording_rules_rule_group" {
name = "NodeRecordingRulesRuleGroup-${azurerm_kubernetes_cluster.aks.name}"
location = azurerm_resource_group.aks_rg.location
resource_group_name = azurerm_resource_group.aks_rg.name
cluster_name = azurerm_kubernetes_cluster.aks.name
description = "Node Recording Rules Rule Group"
rule_group_enabled = true
interval = "PT1M"
scopes = [azurerm_monitor_workspace.instance_amw.id, azurerm_kubernetes_cluster.aks.id]
rule {
enabled = true
record = "instance:node_num_cpu:sum"
expression = <<EOF
count without (cpu, mode) ( node_cpu_seconds_total{job="node",mode="idle"})
EOF
}
rule {
enabled = true
record = "instance:node_cpu_utilisation:rate5m"
expression = <<EOF
1 - avg without (cpu) ( sum without (mode) (rate(node_cpu_seconds_total{job="node", mode=~"idle|iowait|steal"}[5m])))
EOF
}
rule {
enabled = true
record = "instance:node_load1_per_cpu:ratio"
expression = <<EOF
( node_load1{job="node"}/ instance:node_num_cpu:sum{job="node"})
EOF
}
rule {
enabled = true
record = "instance:node_memory_utilisation:ratio"
expression = <<EOF
1 - ( ( node_memory_MemAvailable_bytes{job="node"} or ( node_memory_Buffers_bytes{job="node"} + node_memory_Cached_bytes{job="node"} + node_memory_MemFree_bytes{job="node"} + node_memory_Slab_bytes{job="node"} ) )/ node_memory_MemTotal_bytes{job="node"})
EOF
}
rule {
enabled = true
record = "instance:node_vmstat_pgmajfault:rate5m"
expression = <<EOF
rate(node_vmstat_pgmajfault{job="node"}[5m])
EOF
}
rule {
enabled = true
record = "instance_device:node_disk_io_time_seconds:rate5m"
expression = <<EOF
rate(node_disk_io_time_seconds_total{job="node", device!=""}[5m])
EOF
}
rule {
enabled = true
record = "instance_device:node_disk_io_time_weighted_seconds:rate5m"
expression = <<EOF
rate(node_disk_io_time_weighted_seconds_total{job="node", device!=""}[5m])
EOF
}
rule {
enabled = true
record = "instance:node_network_receive_bytes_excluding_lo:rate5m"
expression = <<EOF
sum without (device) ( rate(node_network_receive_bytes_total{job="node", device!="lo"}[5m]))
EOF
}
rule {
enabled = true
record = "instance:node_network_transmit_bytes_excluding_lo:rate5m"
expression = <<EOF
sum without (device) ( rate(node_network_transmit_bytes_total{job="node", device!="lo"}[5m]))
EOF
}
rule {
enabled = true
record = "instance:node_network_receive_drop_excluding_lo:rate5m"
expression = <<EOF
sum without (device) ( rate(node_network_receive_drop_total{job="node", device!="lo"}[5m]))
EOF
}
rule {
enabled = true
record = "instance:node_network_transmit_drop_excluding_lo:rate5m"
expression = <<EOF
sum without (device) ( rate(node_network_transmit_drop_total{job="node", device!="lo"}[5m]))
EOF
}
depends_on = [
azurerm_monitor_workspace.instance_amw,
azurerm_kubernetes_cluster.aks,
azurerm_monitor_data_collection_endpoint.dce,
azurerm_monitor_data_collection_rule.prometheus_dcr,
azurerm_monitor_data_collection_rule_association.prometheus_dcra
]
}
resource "azurerm_monitor_alert_prometheus_rule_group" "kubernetes_recording_rules_rule_group" {
name = "KubernetesRecordingRulesRuleGroup-${azurerm_kubernetes_cluster.aks.name}"
location = azurerm_resource_group.aks_rg.location
resource_group_name = azurerm_resource_group.aks_rg.name
cluster_name = azurerm_kubernetes_cluster.aks.name
description = "Kubernetes Recording Rules Rule Group"
rule_group_enabled = true
interval = "PT1M"
scopes = [azurerm_monitor_workspace.instance_amw.id, azurerm_kubernetes_cluster.aks.id]
rule {
enabled = true
record = "node_namespace_pod_container:container_cpu_usage_seconds_total:sum_irate"
expression = <<EOF
sum by (cluster, namespace, pod, container) ( irate(container_cpu_usage_seconds_total{job="cadvisor", image!=""}[5m])) * on (cluster, namespace, pod) group_left(node) topk by (cluster, namespace, pod) ( 1, max by(cluster, namespace, pod, node) (kube_pod_info{node!=""}))
EOF
}
rule {
enabled = true
record = "node_namespace_pod_container:container_memory_working_set_bytes"
expression = <<EOF
container_memory_working_set_bytes{job="cadvisor", image!=""}* on (namespace, pod) group_left(node) topk by(namespace, pod) (1, max by(namespace, pod, node) (kube_pod_info{node!=""}))
EOF
}
rule {
enabled = true
record = "node_namespace_pod_container:container_memory_rss"
expression = <<EOF
container_memory_rss{job="cadvisor", image!=""}* on (namespace, pod) group_left(node) topk by(namespace, pod) (1, max by(namespace, pod, node) (kube_pod_info{node!=""}))
EOF
}
rule {
enabled = true
record = "node_namespace_pod_container:container_memory_cache"
expression = <<EOF
container_memory_cache{job="cadvisor", image!=""}* on (namespace, pod) group_left(node) topk by(namespace, pod) (1, max by(namespace, pod, node) (kube_pod_info{node!=""}))
EOF
}
rule {
enabled = true
record = "node_namespace_pod_container:container_memory_swap"
expression = <<EOF
container_memory_swap{job="cadvisor", image!=""}* on (namespace, pod) group_left(node) topk by(namespace, pod) (1, max by(namespace, pod, node) (kube_pod_info{node!=""}))
EOF
}
rule {
enabled = true
record = "cluster:namespace:pod_memory:active:kube_pod_container_resource_requests"
expression = <<EOF
kube_pod_container_resource_requests{resource="memory",job="kube-state-metrics"} * on (namespace, pod, cluster)group_left() max by (namespace, pod, cluster) ( (kube_pod_status_phase{phase=~"Pending|Running"} == 1))
EOF
}
rule {
enabled = true
record = "namespace_memory:kube_pod_container_resource_requests:sum"
expression = <<EOF
sum by (namespace, cluster) ( sum by (namespace, pod, cluster) ( max by (namespace, pod, container, cluster) ( kube_pod_container_resource_requests{resource="memory",job="kube-state-metrics"} ) * on(namespace, pod, cluster) group_left() max by (namespace, pod, cluster) ( kube_pod_status_phase{phase=~"Pending|Running"} == 1 ) ))
EOF
}
rule {
enabled = true
record = "cluster:namespace:pod_cpu:active:kube_pod_container_resource_requests"
expression = <<EOF
kube_pod_container_resource_requests{resource="cpu",job="kube-state-metrics"} * on (namespace, pod, cluster)group_left() max by (namespace, pod, cluster) ( (kube_pod_status_phase{phase=~"Pending|Running"} == 1))
EOF
}
rule {
enabled = true
record = "namespace_cpu:kube_pod_container_resource_requests:sum"
expression = <<EOF
sum by (namespace, cluster) ( sum by (namespace, pod, cluster) ( max by (namespace, pod, container, cluster) ( kube_pod_container_resource_requests{resource="cpu",job="kube-state-metrics"} ) * on(namespace, pod, cluster) group_left() max by (namespace, pod, cluster) ( kube_pod_status_phase{phase=~"Pending|Running"} == 1 ) ))
EOF
}
rule {
enabled = true
record = "cluster:namespace:pod_memory:active:kube_pod_container_resource_limits"
expression = <<EOF
kube_pod_container_resource_limits{resource="memory",job="kube-state-metrics"} * on (namespace, pod, cluster)group_left() max by (namespace, pod, cluster) ( (kube_pod_status_phase{phase=~"Pending|Running"} == 1))
EOF
}
rule {
enabled = true
record = "namespace_memory:kube_pod_container_resource_limits:sum"
expression = <<EOF
sum by (namespace, cluster) ( sum by (namespace, pod, cluster) ( max by (namespace, pod, container, cluster) ( kube_pod_container_resource_limits{resource="memory",job="kube-state-metrics"} ) * on(namespace, pod, cluster) group_left() max by (namespace, pod, cluster) ( kube_pod_status_phase{phase=~"Pending|Running"} == 1 ) ))
EOF
}
rule {
enabled = true
record = "cluster:namespace:pod_cpu:active:kube_pod_container_resource_limits"
expression = <<EOF
kube_pod_container_resource_limits{resource="cpu",job="kube-state-metrics"} * on (namespace, pod, cluster)group_left() max by (namespace, pod, cluster) ( (kube_pod_status_phase{phase=~"Pending|Running"} == 1) )
EOF
}
rule {
enabled = true
record = "namespace_cpu:kube_pod_container_resource_limits:sum"
expression = <<EOF
sum by (namespace, cluster) ( sum by (namespace, pod, cluster) ( max by (namespace, pod, container, cluster) ( kube_pod_container_resource_limits{resource="cpu",job="kube-state-metrics"} ) * on(namespace, pod, cluster) group_left() max by (namespace, pod, cluster) ( kube_pod_status_phase{phase=~"Pending|Running"} == 1 ) ))
EOF
}
rule {
enabled = true
record = "namespace_workload_pod:kube_pod_owner:relabel"
expression = <<EOF
max by (cluster, namespace, workload, pod) ( label_replace( label_replace( kube_pod_owner{job="kube-state-metrics", owner_kind="ReplicaSet"}, "replicaset", "$1", "owner_name", "(.*)" ) * on(replicaset, namespace) group_left(owner_name) topk by(replicaset, namespace) ( 1, max by (replicaset, namespace, owner_name) ( kube_replicaset_owner{job="kube-state-metrics"} ) ), "workload", "$1", "owner_name", "(.*)" ))
EOF
labels = {
workload_type = "deployment"
}
}
rule {
enabled = true
record = "namespace_workload_pod:kube_pod_owner:relabel"
expression = <<EOF
max by (cluster, namespace, workload, pod) ( label_replace( kube_pod_owner{job="kube-state-metrics", owner_kind="DaemonSet"}, "workload", "$1", "owner_name", "(.*)" ))
EOF
labels = {
workload_type = "daemonset"
}
}
rule {
enabled = true
record = "namespace_workload_pod:kube_pod_owner:relabel"
expression = <<EOF
max by (cluster, namespace, workload, pod) ( label_replace( kube_pod_owner{job="kube-state-metrics", owner_kind="StatefulSet"}, "workload", "$1", "owner_name", "(.*)" ))
EOF
labels = {
workload_type = "statefulset"
}
}
rule {
enabled = true
record = "namespace_workload_pod:kube_pod_owner:relabel"
expression = <<EOF
max by (cluster, namespace, workload, pod) ( label_replace( kube_pod_owner{job="kube-state-metrics", owner_kind="Job"}, "workload", "$1", "owner_name", "(.*)" ))
EOF
labels = {
workload_type = "job"
}
}
rule {
enabled = true
record = ":node_memory_MemAvailable_bytes:sum"
expression = <<EOF
sum( node_memory_MemAvailable_bytes{job="node"} or ( node_memory_Buffers_bytes{job="node"} + node_memory_Cached_bytes{job="node"} + node_memory_MemFree_bytes{job="node"} + node_memory_Slab_bytes{job="node"} )) by (cluster)
EOF
}
rule {
enabled = true
record = "cluster:node_cpu:ratio_rate5m"
expression = <<EOF
sum(rate(node_cpu_seconds_total{job="node",mode!="idle",mode!="iowait",mode!="steal"}[5m])) by (cluster) /count(sum(node_cpu_seconds_total{job="node"}) by (cluster, instance, cpu)) by (cluster)
EOF
}
depends_on = [
azurerm_monitor_workspace.instance_amw,
azurerm_kubernetes_cluster.aks,
azurerm_monitor_data_collection_endpoint.dce,
azurerm_monitor_data_collection_rule.prometheus_dcr,
azurerm_monitor_data_collection_rule_association.prometheus_dcra
]
}
resource "azurerm_monitor_alert_prometheus_rule_group" "node_and_kubernetes_recording_rules_rule_group_win" {
name = "NodeAndKubernetesRecordingRulesRuleGroup-Win-${azurerm_kubernetes_cluster.aks.name}"
location = azurerm_resource_group.aks_rg.location
resource_group_name = azurerm_resource_group.aks_rg.name
cluster_name = azurerm_kubernetes_cluster.aks.name
description = "Node and Kubernetes Recording Rules Rule Group for Windows Nodes"
rule_group_enabled = false # set as true if windows nodes need this rule
interval = "PT1M"
scopes = [azurerm_monitor_workspace.instance_amw.id, azurerm_kubernetes_cluster.aks.id]
rule {
enabled = true
record = "node:windows_node_filesystem_usage:"
expression = <<EOF
max by (instance,volume)((windows_logical_disk_size_bytes{job="windows-exporter"} - windows_logical_disk_free_bytes{job="windows-exporter"}) / windows_logical_disk_size_bytes{job="windows-exporter"})
EOF
}
rule {
enabled = true
record = "node:windows_node_filesystem_avail:"
expression = <<EOF
max by (instance, volume) (windows_logical_disk_free_bytes{job="windows-exporter"} / windows_logical_disk_size_bytes{job="windows-exporter"})
EOF
}
rule {
enabled = true
record = ":windows_node_net_utilisation:sum_irate"
expression = <<EOF
sum(irate(windows_net_bytes_total{job="windows-exporter"}[5m]))
EOF
}
rule {
enabled = true
record = "node:windows_node_net_utilisation:sum_irate"
expression = <<EOF
sum by (instance) ((irate(windows_net_bytes_total{job="windows-exporter"}[5m])))
EOF
}
rule {
enabled = true
record = ":windows_node_net_saturation:sum_irate"
expression = <<EOF
sum(irate(windows_net_packets_received_discarded_total{job="windows-exporter"}[5m])) + sum(irate(windows_net_packets_outbound_discarded_total{job="windows-exporter"}[5m]))
EOF
}
rule {
enabled = true
record = "node:windows_node_net_saturation:sum_irate"
expression = <<EOF
sum by (instance) ((irate(windows_net_packets_received_discarded_total{job="windows-exporter"}[5m]) + irate(windows_net_packets_outbound_discarded_total{job="windows-exporter"}[5m])))
EOF
}
rule {
enabled = true
record = "windows_pod_container_available"
expression = <<EOF
windows_container_available{job="windows-exporter", container_id != ""} * on(container_id) group_left(container, pod, namespace) max(kube_pod_container_info{job="kube-state-metrics", container_id != ""}) by(container, container_id, pod, namespace)
EOF
}
rule {
enabled = true
record = "windows_container_total_runtime"
expression = <<EOF
windows_container_cpu_usage_seconds_total{job="windows-exporter", container_id != ""} * on(container_id) group_left(container, pod, namespace) max(kube_pod_container_info{job="kube-state-metrics", container_id != ""}) by(container, container_id, pod, namespace)
EOF
}
rule {
enabled = true
record = "windows_container_memory_usage"
expression = <<EOF
windows_container_memory_usage_commit_bytes{job="windows-exporter", container_id != ""} * on(container_id) group_left(container, pod, namespace) max(kube_pod_container_info{job="kube-state-metrics", container_id != ""}) by(container, container_id, pod, namespace)
EOF
}
rule {
enabled = true
record = "windows_container_private_working_set_usage"
expression = <<EOF
windows_container_memory_usage_private_working_set_bytes{job="windows-exporter", container_id != ""} * on(container_id) group_left(container, pod, namespace) max(kube_pod_container_info{job="kube-state-metrics", container_id != ""}) by(container, container_id, pod, namespace)
EOF
}
rule {
enabled = true
record = "windows_container_network_received_bytes_total"
expression = <<EOF
windows_container_network_receive_bytes_total{job="windows-exporter", container_id != ""} * on(container_id) group_left(container, pod, namespace) max(kube_pod_container_info{job="kube-state-metrics", container_id != ""}) by(container, container_id, pod, namespace)
EOF
}
rule {
enabled = true
record = "windows_container_network_transmitted_bytes_total"
expression = <<EOF
windows_container_network_transmit_bytes_total{job="windows-exporter", container_id != ""} * on(container_id) group_left(container, pod, namespace) max(kube_pod_container_info{job="kube-state-metrics", container_id != ""}) by(container, container_id, pod, namespace)
EOF
}
rule {
enabled = true
record = "kube_pod_windows_container_resource_memory_request"
expression = <<EOF
max by (namespace, pod, container) (kube_pod_container_resource_requests{resource="memory",job="kube-state-metrics"}) * on(container,pod,namespace) (windows_pod_container_available)
EOF
}
rule {
enabled = true
record = "kube_pod_windows_container_resource_memory_limit"
expression = <<EOF
kube_pod_container_resource_limits{resource="memory",job="kube-state-metrics"} * on(container,pod,namespace) (windows_pod_container_available)
EOF
}
rule {
enabled = true
record = "kube_pod_windows_container_resource_cpu_cores_request"
expression = <<EOF
max by (namespace, pod, container) ( kube_pod_container_resource_requests{resource="cpu",job="kube-state-metrics"}) * on(container,pod,namespace) (windows_pod_container_available)
EOF
}
rule {
enabled = true
record = "kube_pod_windows_container_resource_cpu_cores_limit"
expression = <<EOF
kube_pod_container_resource_limits{resource="cpu",job="kube-state-metrics"} * on(container,pod,namespace) (windows_pod_container_available)
EOF
}
rule {
enabled = true
record = "namespace_pod_container:windows_container_cpu_usage_seconds_total:sum_rate"
expression = <<EOF
sum by (namespace, pod, container) (rate(windows_container_total_runtime{}[5m]))
EOF
}
depends_on = [
azurerm_monitor_workspace.instance_amw,
azurerm_kubernetes_cluster.aks,
azurerm_monitor_data_collection_endpoint.dce,
azurerm_monitor_data_collection_rule.prometheus_dcr,
azurerm_monitor_data_collection_rule_association.prometheus_dcra
]
}
resource "azurerm_monitor_alert_prometheus_rule_group" "node_recording_rules_rule_group_win" {
name = "NodeRecordingRulesRuleGroup-Win-${azurerm_kubernetes_cluster.aks.name}"
location = azurerm_resource_group.aks_rg.location
resource_group_name = azurerm_resource_group.aks_rg.name
cluster_name = azurerm_kubernetes_cluster.aks.name
description = "Node and Kubernetes Recording Rules Rule Group for Windows Nodes"
rule_group_enabled = false # set as true if windows nodes need this rule
interval = "PT1M"
scopes = [azurerm_monitor_workspace.instance_amw.id, azurerm_kubernetes_cluster.aks.id]
rule {
enabled = true
record = "node:windows_node:sum"
expression = <<EOF
count (windows_system_boot_time_timestamp_seconds{job="windows-exporter"})
EOF
}
rule {
enabled = true
record = "node:windows_node_num_cpu:sum"
expression = <<EOF
count by (instance) (sum by (instance, core) (windows_cpu_time_total{job="windows-exporter"}))
EOF
}
rule {
enabled = true
record = ":windows_node_cpu_utilisation:avg5m"
expression = <<EOF
1 - avg(rate(windows_cpu_time_total{job="windows-exporter",mode="idle"}[5m]))
EOF
}
rule {
enabled = true
record = "node:windows_node_cpu_utilisation:avg5m"
expression = <<EOF
1 - avg by (instance) (rate(windows_cpu_time_total{job="windows-exporter",mode="idle"}[5m]))
EOF
}
rule {
enabled = true
record = ":windows_node_memory_utilisation:"
expression = <<EOF
1 -sum(windows_memory_available_bytes{job="windows-exporter"})/sum(windows_os_visible_memory_bytes{job="windows-exporter"})
EOF
}
rule {
enabled = true
record = ":windows_node_memory_MemFreeCached_bytes:sum"
expression = <<EOF
sum(windows_memory_available_bytes{job="windows-exporter"} + windows_memory_cache_bytes{job="windows-exporter"})
EOF
}
rule {
enabled = true
record = "node:windows_node_memory_totalCached_bytes:sum"
expression = <<EOF
(windows_memory_cache_bytes{job="windows-exporter"} + windows_memory_modified_page_list_bytes{job="windows-exporter"} + windows_memory_standby_cache_core_bytes{job="windows-exporter"} + windows_memory_standby_cache_normal_priority_bytes{job="windows-exporter"} + windows_memory_standby_cache_reserve_bytes{job="windows-exporter"})
EOF
}
rule {
enabled = true
record = ":windows_node_memory_MemTotal_bytes:sum"
expression = <<EOF
sum(windows_os_visible_memory_bytes{job="windows-exporter"})
EOF
}
rule {
enabled = true
record = "node:windows_node_memory_bytes_available:sum"
expression = <<EOF
sum by (instance) ((windows_memory_available_bytes{job="windows-exporter"}))
EOF
}
rule {
enabled = true
record = "node:windows_node_memory_bytes_total:sum"
expression = <<EOF
sum by (instance) (windows_os_visible_memory_bytes{job="windows-exporter"})
EOF
}
rule {
enabled = true
record = "node:windows_node_memory_utilisation:ratio"
expression = <<EOF
(node:windows_node_memory_bytes_total:sum - node:windows_node_memory_bytes_available:sum) / scalar(sum(node:windows_node_memory_bytes_total:sum))
EOF
}
rule {
enabled = true
record = "node:windows_node_memory_utilisation:"
expression = <<EOF
1 - (node:windows_node_memory_bytes_available:sum / node:windows_node_memory_bytes_total:sum)
EOF
}
rule {
enabled = true
record = "node:windows_node_memory_swap_io_pages:irate"
expression = <<EOF
irate(windows_memory_swap_page_operations_total{job="windows-exporter"}[5m])
EOF
}
rule {
enabled = true
record = ":windows_node_disk_utilisation:avg_irate"
expression = <<EOF
avg(irate(windows_logical_disk_read_seconds_total{job="windows-exporter"}[5m]) + irate(windows_logical_disk_write_seconds_total{job="windows-exporter"}[5m]))
EOF
}
rule {
enabled = true
record = "node:windows_node_disk_utilisation:avg_irate"
expression = <<EOF
avg by (instance) ((irate(windows_logical_disk_read_seconds_total{job="windows-exporter"}[5m]) + irate(windows_logical_disk_write_seconds_total{job="windows-exporter"}[5m])))
EOF
}
depends_on = [
azurerm_monitor_workspace.instance_amw,
azurerm_kubernetes_cluster.aks,
azurerm_monitor_data_collection_endpoint.dce,
azurerm_monitor_data_collection_rule.prometheus_dcr,
azurerm_monitor_data_collection_rule_association.prometheus_dcra
]
}
resource "azurerm_monitor_alert_prometheus_rule_group" "ux_recording_rules_rule_group" {
name = "UXRecordingRulesRuleGroup - ${azurerm_kubernetes_cluster.aks.name}"
location = azurerm_resource_group.aks_rg.location
resource_group_name = azurerm_resource_group.aks_rg.name
cluster_name = azurerm_kubernetes_cluster.aks.name
description = "UX recording rules for Linux"
rule_group_enabled = true
interval = "PT1M"
scopes = [azurerm_monitor_workspace.instance_amw.id, azurerm_kubernetes_cluster.aks.id]
rule {
enabled = true
record = "ux:pod_cpu_usage:sum_irate"
expression = <<EOF
(sum by (namespace, pod, cluster, microsoft_resourceid) (
irate(container_cpu_usage_seconds_total{container != "", pod != "", job = "cadvisor"}[5m])
)) * on (pod, namespace, cluster, microsoft_resourceid) group_left (node, created_by_name, created_by_kind)
(max by (node, created_by_name, created_by_kind, pod, namespace, cluster, microsoft_resourceid) (kube_pod_info{pod != "", job = "kube-state-metrics"}))
EOF
}
rule {
enabled = true
record = "ux:controller_cpu_usage:sum_irate"
expression = <<EOF
sum by (namespace, node, cluster, created_by_name, created_by_kind, microsoft_resourceid) (
ux:pod_cpu_usage:sum_irate
)
EOF
}
rule {
enabled = true
record = "ux:pod_workingset_memory:sum"
expression = <<EOF
(
sum by (namespace, pod, cluster, microsoft_resourceid) (
container_memory_working_set_bytes{container != "", pod != "", job = "cadvisor"}
)
) * on (pod, namespace, cluster, microsoft_resourceid) group_left (node, created_by_name, created_by_kind)
(max by (node, created_by_name, created_by_kind, pod, namespace, cluster, microsoft_resourceid) (kube_pod_info{pod != "", job = "kube-state-metrics"}))
EOF
}
rule {
enabled = true
record = "ux:controller_workingset_memory:sum"
expression = <<EOF
sum by (namespace, node, cluster, created_by_name, created_by_kind, microsoft_resourceid) (
ux:pod_workingset_memory:sum
)
EOF
}
rule {
enabled = true
record = "ux:pod_rss_memory:sum"
expression = <<EOF
(
sum by (namespace, pod, cluster, microsoft_resourceid) (
container_memory_rss{container != "", pod != "", job = "cadvisor"}
)
) * on (pod, namespace, cluster, microsoft_resourceid) group_left (node, created_by_name, created_by_kind)
(max by (node, created_by_name, created_by_kind, pod, namespace, cluster, microsoft_resourceid) (kube_pod_info{pod != "", job = "kube-state-metrics"}))
EOF
}
rule {
enabled = true
record = "ux:controller_rss_memory:sum"
expression = <<EOF
sum by (namespace, node, cluster, created_by_name, created_by_kind, microsoft_resourceid) (
ux:pod_rss_memory:sum
)
EOF
}
rule {
enabled = true
record = "ux:pod_container_count:sum"
expression = <<EOF
sum by (node, created_by_name, created_by_kind, namespace, cluster, pod, microsoft_resourceid) (
((
sum by (container, pod, namespace, cluster, microsoft_resourceid) (kube_pod_container_info{container != "", pod != "", container_id != "", job = "kube-state-metrics"})
or sum by (container, pod, namespace, cluster, microsoft_resourceid) (kube_pod_init_container_info{container != "", pod != "", container_id != "", job = "kube-state-metrics"})
)
* on (pod, namespace, cluster, microsoft_resourceid) group_left (node, created_by_name, created_by_kind)
(
max by (node, created_by_name, created_by_kind, pod, namespace, cluster, microsoft_resourceid) (
kube_pod_info{pod != "", job = "kube-state-metrics"}
)
)
)
)
EOF
}
rule {
enabled = true
record = "ux:controller_container_count:sum"
expression = <<EOF
sum by (node, created_by_name, created_by_kind, namespace, cluster, microsoft_resourceid) (
ux:pod_container_count:sum
)
EOF
}
rule {
enabled = true
record = "ux:pod_container_restarts:max"
expression = <<EOF
max by (node, created_by_name, created_by_kind, namespace, cluster, pod, microsoft_resourceid) (
((
max by (container, pod, namespace, cluster, microsoft_resourceid) (kube_pod_container_status_restarts_total{container != "", pod != "", job = "kube-state-metrics"})
or sum by (container, pod, namespace, cluster, microsoft_resourceid) (kube_pod_init_status_restarts_total{container != "", pod != "", job = "kube-state-metrics"})
)
* on (pod, namespace, cluster, microsoft_resourceid) group_left (node, created_by_name, created_by_kind)
(
max by (node, created_by_name, created_by_kind, pod, namespace, cluster, microsoft_resourceid) (
kube_pod_info{pod != "", job = "kube-state-metrics"}
)
)
)
)
EOF
}
rule {
enabled = true
record = "ux:controller_container_restarts:max"
expression = <<EOF
max by (node, created_by_name, created_by_kind, namespace, cluster, microsoft_resourceid) (
ux:pod_container_restarts:max
)
EOF
}
rule {
enabled = true
record = "ux:pod_resource_limit:sum"
expression = <<EOF
(sum by (cluster, pod, namespace, resource, microsoft_resourceid) (
(
max by (cluster, microsoft_resourceid, pod, container, namespace, resource)
(kube_pod_container_resource_limits{container != "", pod != "", job = "kube-state-metrics"})
)
)unless (count by (pod, namespace, cluster, resource, microsoft_resourceid)
(kube_pod_container_resource_limits{container != "", pod != "", job = "kube-state-metrics"})
!= on (pod, namespace, cluster, microsoft_resourceid) group_left()
sum by (pod, namespace, cluster, microsoft_resourceid)
(kube_pod_container_info{container != "", pod != "", job = "kube-state-metrics"})
)
)* on (namespace, pod, cluster, microsoft_resourceid) group_left (node, created_by_kind, created_by_name)
(
kube_pod_info{pod != "", job = "kube-state-metrics"}
)
EOF
}
rule {
enabled = true
record = "ux:controller_resource_limit:sum"
expression = <<EOF
sum by (cluster, namespace, created_by_name, created_by_kind, node, resource, microsoft_resourceid) (
ux:pod_resource_limit:sum
)
EOF
}
rule {
enabled = true
record = "ux:controller_pod_phase_count:sum"
expression = <<EOF
sum by (cluster, phase, node, created_by_kind, created_by_name, namespace, microsoft_resourceid) ( (
(kube_pod_status_phase{job="kube-state-metrics",pod!=""})
or (label_replace((count(kube_pod_deletion_timestamp{job="kube-state-metrics",pod!=""}) by (namespace, pod, cluster, microsoft_resourceid) * count(kube_pod_status_reason{reason="NodeLost", job="kube-state-metrics"} == 0) by (namespace, pod, cluster, microsoft_resourceid)), "phase", "terminating", "", ""))) * on (pod, namespace, cluster, microsoft_resourceid) group_left (node, created_by_name, created_by_kind)
(
max by (node, created_by_name, created_by_kind, pod, namespace, cluster, microsoft_resourceid) (
kube_pod_info{job="kube-state-metrics",pod!=""}
)
)
)
EOF
}
rule {
enabled = true
record = "ux:cluster_pod_phase_count:sum"
expression = <<EOF
sum by (cluster, phase, node, namespace, microsoft_resourceid) (
ux:controller_pod_phase_count:sum
)
EOF
}
rule {
enabled = true
record = "ux:node_cpu_usage:sum_irate"
expression = <<EOF
sum by (instance, cluster, microsoft_resourceid) (
(1 - irate(node_cpu_seconds_total{job="node", mode="idle"}[5m]))
)
EOF
}
rule {
enabled = true
record = "ux:node_memory_usage:sum"
expression = <<EOF
sum by (instance, cluster, microsoft_resourceid) ((
node_memory_MemTotal_bytes{job = "node"}
- node_memory_MemFree_bytes{job = "node"}
- node_memory_cached_bytes{job = "node"}
- node_memory_buffers_bytes{job = "node"}
))
EOF
}
rule {
enabled = true
record = "ux:node_network_receive_drop_total:sum_irate"
expression = <<EOF
sum by (instance, cluster, microsoft_resourceid) (irate(node_network_receive_drop_total{job="node", device!="lo"}[5m]))
EOF
}
rule {
enabled = true
record = "ux:node_network_transmit_drop_total:sum_irate"
expression = <<EOF
sum by (instance, cluster, microsoft_resourceid) (irate(node_network_transmit_drop_total{job="node", device!="lo"}[5m]))
EOF
}
depends_on = [
azurerm_monitor_workspace.instance_amw,
azurerm_kubernetes_cluster.aks,
azurerm_monitor_data_collection_endpoint.dce,
azurerm_monitor_data_collection_rule.prometheus_dcr,
azurerm_monitor_data_collection_rule_association.prometheus_dcra
]
}
resource "azurerm_monitor_alert_prometheus_rule_group" "ux_recording_rules_rule_group_windows" {
name = "UXRecordingRulesRuleGroup-Win - ${azurerm_kubernetes_cluster.aks.name}"
location = azurerm_resource_group.aks_rg.location
resource_group_name = azurerm_resource_group.aks_rg.name
cluster_name = azurerm_kubernetes_cluster.aks.name
description = "UX recording rules for Windows"
rule_group_enabled = false # set as true if windows nodes need this rule
interval = "PT1M"
scopes = [
azurerm_monitor_workspace.instance_amw.id,
azurerm_kubernetes_cluster.aks.id
]
rule {
enabled = true
record = "ux:pod_cpu_usage_windows:sum_irate"
expression = <<EOF
sum by (cluster, pod, namespace, node, created_by_kind, created_by_name, microsoft_resourceid) (
(
max by (instance, container_id, cluster, microsoft_resourceid) (
irate(windows_container_cpu_usage_seconds_total{ container_id != "", job = "windows-exporter"}[5m])
) * on (container_id, cluster, microsoft_resourceid) group_left (container, pod, namespace) (
max by (container, container_id, pod, namespace, cluster, microsoft_resourceid) (
kube_pod_container_info{container != "", pod != "", container_id != "", job = "kube-state-metrics"}
)
)
) * on (pod, namespace, cluster, microsoft_resourceid) group_left (node, created_by_name, created_by_kind) (
max by (node, created_by_name, created_by_kind, pod, namespace, cluster, microsoft_resourceid) (
kube_pod_info{ pod != "", job = "kube-state-metrics"}
)
)
)
EOF
}
rule {
enabled = true
record = "ux:controller_cpu_usage_windows:sum_irate"
expression = "sum by (namespace, node, cluster, created_by_name, created_by_kind, microsoft_resourceid) (ux:pod_cpu_usage_windows:sum_irate)"
}
rule {
enabled = true
record = "ux:pod_workingset_memory_windows:sum"
expression = <<EOF
sum by (cluster, pod, namespace, node, created_by_kind, created_by_name, microsoft_resourceid) (
(
max by (instance, container_id, cluster, microsoft_resourceid) (
windows_container_memory_usage_private_working_set_bytes{ container_id != "", job = "windows-exporter"}
) * on (container_id, cluster, microsoft_resourceid) group_left (container, pod, namespace) (
max by (container, container_id, pod, namespace, cluster, microsoft_resourceid) (
kube_pod_container_info{container != "", pod != "", container_id != "", job = "kube-state-metrics"}
)
)
) * on (pod, namespace, cluster, microsoft_resourceid) group_left (node, created_by_name, created_by_kind) (
max by (node, created_by_name, created_by_kind, pod, namespace, cluster, microsoft_resourceid) (
kube_pod_info{ pod != "", job = "kube-state-metrics"}
)
)
)
EOF
}
rule {
enabled = true
record = "ux:controller_workingset_memory_windows:sum"
expression = "sum by (namespace, node, cluster, created_by_name, created_by_kind, microsoft_resourceid) (ux:pod_workingset_memory_windows:sum)"
}
rule {
enabled = true
record = "ux:node_cpu_usage_windows:sum_irate"
expression = "sum by (instance, cluster, microsoft_resourceid) ((1 - irate(windows_cpu_time_total{job=\"windows-exporter\", mode=\"idle\"}[5m])))"
}
rule {
enabled = true
record = "ux:node_memory_usage_windows:sum"
expression = "sum by (instance, cluster, microsoft_resourceid) ((windows_os_visible_memory_bytes{job = \"windows-exporter\"}- windows_memory_available_bytes{job = \"windows-exporter\"}))"
}
rule {
enabled = true
record = "ux:node_network_packets_received_drop_total_windows:sum_irate"
expression = "sum by (instance, cluster, microsoft_resourceid) (irate(windows_net_packets_received_discarded_total{job=\"windows-exporter\", device!=\"lo\"}[5m]))"
}
rule {
enabled = true
record = "ux:node_network_packets_outbound_drop_total_windows:sum_irate"
expression = "sum by (instance, cluster, microsoft_resourceid) (irate(windows_net_packets_outbound_discarded_total{job=\"windows-exporter\", device!=\"lo\"}[5m]))"
}
depends_on = [
azurerm_monitor_workspace.instance_amw,
azurerm_kubernetes_cluster.aks,
azurerm_monitor_data_collection_endpoint.dce,
azurerm_monitor_data_collection_rule.prometheus_dcr,
azurerm_monitor_data_collection_rule_association.prometheus_dcra
]
}
#endregion Managed Prometheus setup
You can further enhance it to use shared Azure monitor workspace with multiple AKS clsuters say you have blue green deployments and want to monitor blue and green AKS clsuters when they are available.
So you can have comon montroing workspace, data collection endpoint and rules setup as below.
# Refer https://learn.microsoft.com/en-us/azure/azure-monitor/containers/kubernetes-monitoring-enable?tabs=terraform # Azure monitor workspace resource "azurerm_monitor_workspace" "instance_amw" { name = "${var.PREFIX}-${var.PROJECT}-${var.ENVNAME}-amw" location = azurerm_resource_group.instancerg.location resource_group_name = azurerm_resource_group.instancerg.name tags = merge(tomap({ Service = "monitoring workspace" }), local.tags) } # Common data collection endpoint for prometheus and container insights resource "azurerm_monitor_data_collection_endpoint" "dce" { name = "${var.PREFIX}-${var.PROJECT}-${var.ENVNAME}-dce" resource_group_name = azurerm_resource_group.instancerg.name location = azurerm_resource_group.instancerg.location tags = merge(tomap({ Service = "monitoring data collection endpoint" }), local.tags) } # Prometheus data collection rule # Refer https://github.com/Azure/prometheus-collector/blob/main/AddonTerraformTemplate/main.tf resource "azurerm_monitor_data_collection_rule" "prometheus_dcr" { name = "${var.PREFIX}-${var.PROJECT}-${var.ENVNAME}-prometheus-dcr" resource_group_name = azurerm_resource_group.instancerg.name location = azurerm_resource_group.instancerg.location data_collection_endpoint_id = azurerm_monitor_data_collection_endpoint.dce.id destinations { monitor_account { monitor_account_id = azurerm_monitor_workspace.instance_amw.id name = "MonitoringAccount1" } } data_flow { streams = ["Microsoft-PrometheusMetrics"] destinations = ["MonitoringAccount1"] } data_sources { prometheus_forwarder { streams = ["Microsoft-PrometheusMetrics"] name = "PrometheusDataSource" } } description = "DCR for Azure Monitor Metrics Profile (Managed Prometheus)" tags = merge(tomap({ Service = "monitoring AKS prometheus data collection rule" }), local.tags) depends_on = [ azurerm_monitor_data_collection_endpoint.dce ] } # Container Insights data collection rule # Refer https://learn.microsoft.com/en-us/azure/azure-monitor/containers/kubernetes-monitoring-enable?tabs=terraform#new-aks-cluster resource "azurerm_monitor_data_collection_rule" "ci_dcr" { name = "${var.PREFIX}-${var.PROJECT}-${var.ENVNAME}-ci-dcr" resource_group_name = azurerm_resource_group.instancerg.name location = azurerm_resource_group.instancerg.location destinations { log_analytics { workspace_resource_id = azurerm_log_analytics_workspace.instance_log.id name = "ciworkspace" } } data_flow { streams = local.log_dataflow_streams destinations = ["ciworkspace"] } data_sources { extension { streams = local.log_dataflow_streams extension_name = "ContainerInsights" extension_json = jsonencode({ "dataCollectionSettings" : { "interval" : "1m", "namespaceFilteringMode" : "Off", "namespaces" : ["kube-system", "gatekeeper-system"] "enableContainerLogV2" : true } }) name = "ContainerInsightsExtension" } } data_collection_endpoint_id = local.enable_high_log_scale_mode ? azurerm_monitor_data_collection_endpoint.dce.id : null description = "DCR for Azure Monitor Container Insights" tags = merge(tomap({ Service = "monitoring AKS container insights data collection rule" }), local.tags) depends_on = [ azurerm_monitor_data_collection_endpoint.dce ] } # Managed Grafana setup data "azurerm_resource_group" "grafana_rg" { provider = azurerm.mgmt name = "ch-demo-grafana-shared-rg" } data "azurerm_dashboard_grafana" "grafana" { provider = azurerm.mgmt name = "ch-demo-shared-dg-001" resource_group_name = data.azurerm_resource_group.grafana_rg.name } # Managed Grafana integration with Azure Monitor Workspace resource "null_resource" "amw_grafana" { triggers = { amw_id = azurerm_monitor_workspace.instance_amw.id } depends_on = [ azurerm_monitor_workspace.instance_amw, data.azurerm_dashboard_grafana.grafana ] provisioner "local-exec" { command = <<-SHELL az login --service-principal -u ${var.DEVOPSSERVICECONNECTIONAID} -p ${var.DEVOPSSERVICECONNECTIONPW} --tenant ${var.TENANTID} az extension add --name amg --upgrade --yes az grafana integrations monitor add --monitor-name ${azurerm_monitor_workspace.instance_amw.name} --monitor-resource-group-name ${azurerm_resource_group.instancerg.name} --monitor-subscription-id ${var.SUBSCRIPTIONID} --name ${data.azurerm_dashboard_grafana.grafana.name} --resource-group ${data.azurerm_resource_group.grafana_rg.name} --subscription ${var.MANAGEMENTSUBSCRIPTIONID} SHELL interpreter = ["PowerShell"] } } # Assign "Monitoring Data Reader" role to the Azure monitor workspace for Grafana # https://www.azadvertizer.net/azrolesadvertizer/b0d8363b-8ddd-447d-831f-62ca05bff136.html resource "azurerm_role_assignment" "datareaderrole" { scope = azurerm_monitor_workspace.instance_amw.id role_definition_id = "/subscriptions/${var.SUBSCRIPTIONID}/providers/Microsoft.Authorization/roleDefinitions/b0d8363b-8ddd-447d-831f-62ca05bff136" principal_id = data.azurerm_dashboard_grafana.grafana.identity.0.principal_id }
Then in the AKS module you can setup associations and rules for individual AKS instance as shown below.
# Prometheus data collection rule association resource "azurerm_monitor_data_collection_rule_association" "prometheus_dcra" { name = "${azurerm_kubernetes_cluster.aks_cluster.name}-prometheus-dcra" target_resource_id = azurerm_kubernetes_cluster.aks_cluster.id data_collection_rule_id = var.prometheus_dcr_id description = "Association of data collection rule. Deleting this association will break the data collection for this AKS Cluster ${var.deployment_name}." depends_on = [ azurerm_kubernetes_cluster.aks_cluster, azurerm_kubernetes_cluster_node_pool.windows_pool ] } # Container Insights data collection rule association resource "azurerm_monitor_data_collection_rule_association" "ci_dcra" { name = "${azurerm_kubernetes_cluster.aks_cluster.name}-ci-dcra" target_resource_id = azurerm_kubernetes_cluster.aks_cluster.id data_collection_rule_id = var.container_insights_dcr_id description = "Association of container insights data collection rule. Deleting this association will break the data collection for this AKS Cluster ${var.deployment_name}." depends_on = [ azurerm_kubernetes_cluster.aks_cluster, azurerm_kubernetes_cluster_node_pool.windows_pool ] } #region Managed Prometheus recording rules for AKS # Refer https://github.com/Azure/prometheus-collector/blob/main/AddonTerraformTemplate/main.tf resource "azurerm_monitor_alert_prometheus_rule_group" "node_recording_rules_rule_group" { name = "NodeRecordingRulesRuleGroup-${azurerm_kubernetes_cluster.aks_cluster.name}" location = var.location resource_group_name = var.rg_name cluster_name = azurerm_kubernetes_cluster.aks_cluster.name description = "Node Recording Rules Rule Group" rule_group_enabled = true interval = "PT1M" scopes = [var.monitoring_workspace_id, azurerm_kubernetes_cluster.aks_cluster.id] rule { enabled = true record = "instance:node_num_cpu:sum" expression = <<EOF count without (cpu, mode) ( node_cpu_seconds_total{job="node",mode="idle"}) EOF } rule { enabled = true record = "instance:node_cpu_utilisation:rate5m" expression = <<EOF 1 - avg without (cpu) ( sum without (mode) (rate(node_cpu_seconds_total{job="node", mode=~"idle|iowait|steal"}[5m]))) EOF } rule { enabled = true record = "instance:node_load1_per_cpu:ratio" expression = <<EOF ( node_load1{job="node"}/ instance:node_num_cpu:sum{job="node"}) EOF } rule { enabled = true record = "instance:node_memory_utilisation:ratio" expression = <<EOF 1 - ( ( node_memory_MemAvailable_bytes{job="node"} or ( node_memory_Buffers_bytes{job="node"} + node_memory_Cached_bytes{job="node"} + node_memory_MemFree_bytes{job="node"} + node_memory_Slab_bytes{job="node"} ) )/ node_memory_MemTotal_bytes{job="node"}) EOF } rule { enabled = true record = "instance:node_vmstat_pgmajfault:rate5m" expression = <<EOF rate(node_vmstat_pgmajfault{job="node"}[5m]) EOF } rule { enabled = true record = "instance_device:node_disk_io_time_seconds:rate5m" expression = <<EOF rate(node_disk_io_time_seconds_total{job="node", device!=""}[5m]) EOF } rule { enabled = true record = "instance_device:node_disk_io_time_weighted_seconds:rate5m" expression = <<EOF rate(node_disk_io_time_weighted_seconds_total{job="node", device!=""}[5m]) EOF } rule { enabled = true record = "instance:node_network_receive_bytes_excluding_lo:rate5m" expression = <<EOF sum without (device) ( rate(node_network_receive_bytes_total{job="node", device!="lo"}[5m])) EOF } rule { enabled = true record = "instance:node_network_transmit_bytes_excluding_lo:rate5m" expression = <<EOF sum without (device) ( rate(node_network_transmit_bytes_total{job="node", device!="lo"}[5m])) EOF } rule { enabled = true record = "instance:node_network_receive_drop_excluding_lo:rate5m" expression = <<EOF sum without (device) ( rate(node_network_receive_drop_total{job="node", device!="lo"}[5m])) EOF } rule { enabled = true record = "instance:node_network_transmit_drop_excluding_lo:rate5m" expression = <<EOF sum without (device) ( rate(node_network_transmit_drop_total{job="node", device!="lo"}[5m])) EOF } tags = merge(tomap({ Service = "AKS monitoring rules" }), var.tags) depends_on = [ azurerm_kubernetes_cluster.aks_cluster, azurerm_monitor_data_collection_rule_association.prometheus_dcra, azurerm_monitor_data_collection_rule_association.ci_dcra ] } resource "azurerm_monitor_alert_prometheus_rule_group" "kubernetes_recording_rules_rule_group" { name = "KubernetesRecordingRulesRuleGroup-${azurerm_kubernetes_cluster.aks_cluster.name}" location = var.location resource_group_name = var.rg_name cluster_name = azurerm_kubernetes_cluster.aks_cluster.name description = "Kubernetes Recording Rules Rule Group" rule_group_enabled = true interval = "PT1M" scopes = [var.monitoring_workspace_id, azurerm_kubernetes_cluster.aks_cluster.id] rule { enabled = true record = "node_namespace_pod_container:container_cpu_usage_seconds_total:sum_irate" expression = <<EOF sum by (cluster, namespace, pod, container) ( irate(container_cpu_usage_seconds_total{job="cadvisor", image!=""}[5m])) * on (cluster, namespace, pod) group_left(node) topk by (cluster, namespace, pod) ( 1, max by(cluster, namespace, pod, node) (kube_pod_info{node!=""})) EOF } rule { enabled = true record = "node_namespace_pod_container:container_memory_working_set_bytes" expression = <<EOF container_memory_working_set_bytes{job="cadvisor", image!=""}* on (namespace, pod) group_left(node) topk by(namespace, pod) (1, max by(namespace, pod, node) (kube_pod_info{node!=""})) EOF } rule { enabled = true record = "node_namespace_pod_container:container_memory_rss" expression = <<EOF container_memory_rss{job="cadvisor", image!=""}* on (namespace, pod) group_left(node) topk by(namespace, pod) (1, max by(namespace, pod, node) (kube_pod_info{node!=""})) EOF } rule { enabled = true record = "node_namespace_pod_container:container_memory_cache" expression = <<EOF container_memory_cache{job="cadvisor", image!=""}* on (namespace, pod) group_left(node) topk by(namespace, pod) (1, max by(namespace, pod, node) (kube_pod_info{node!=""})) EOF } rule { enabled = true record = "node_namespace_pod_container:container_memory_swap" expression = <<EOF container_memory_swap{job="cadvisor", image!=""}* on (namespace, pod) group_left(node) topk by(namespace, pod) (1, max by(namespace, pod, node) (kube_pod_info{node!=""})) EOF } rule { enabled = true record = "cluster:namespace:pod_memory:active:kube_pod_container_resource_requests" expression = <<EOF kube_pod_container_resource_requests{resource="memory",job="kube-state-metrics"} * on (namespace, pod, cluster)group_left() max by (namespace, pod, cluster) ( (kube_pod_status_phase{phase=~"Pending|Running"} == 1)) EOF } rule { enabled = true record = "namespace_memory:kube_pod_container_resource_requests:sum" expression = <<EOF sum by (namespace, cluster) ( sum by (namespace, pod, cluster) ( max by (namespace, pod, container, cluster) ( kube_pod_container_resource_requests{resource="memory",job="kube-state-metrics"} ) * on(namespace, pod, cluster) group_left() max by (namespace, pod, cluster) ( kube_pod_status_phase{phase=~"Pending|Running"} == 1 ) )) EOF } rule { enabled = true record = "cluster:namespace:pod_cpu:active:kube_pod_container_resource_requests" expression = <<EOF kube_pod_container_resource_requests{resource="cpu",job="kube-state-metrics"} * on (namespace, pod, cluster)group_left() max by (namespace, pod, cluster) ( (kube_pod_status_phase{phase=~"Pending|Running"} == 1)) EOF } rule { enabled = true record = "namespace_cpu:kube_pod_container_resource_requests:sum" expression = <<EOF sum by (namespace, cluster) ( sum by (namespace, pod, cluster) ( max by (namespace, pod, container, cluster) ( kube_pod_container_resource_requests{resource="cpu",job="kube-state-metrics"} ) * on(namespace, pod, cluster) group_left() max by (namespace, pod, cluster) ( kube_pod_status_phase{phase=~"Pending|Running"} == 1 ) )) EOF } rule { enabled = true record = "cluster:namespace:pod_memory:active:kube_pod_container_resource_limits" expression = <<EOF kube_pod_container_resource_limits{resource="memory",job="kube-state-metrics"} * on (namespace, pod, cluster)group_left() max by (namespace, pod, cluster) ( (kube_pod_status_phase{phase=~"Pending|Running"} == 1)) EOF } rule { enabled = true record = "namespace_memory:kube_pod_container_resource_limits:sum" expression = <<EOF sum by (namespace, cluster) ( sum by (namespace, pod, cluster) ( max by (namespace, pod, container, cluster) ( kube_pod_container_resource_limits{resource="memory",job="kube-state-metrics"} ) * on(namespace, pod, cluster) group_left() max by (namespace, pod, cluster) ( kube_pod_status_phase{phase=~"Pending|Running"} == 1 ) )) EOF } rule { enabled = true record = "cluster:namespace:pod_cpu:active:kube_pod_container_resource_limits" expression = <<EOF kube_pod_container_resource_limits{resource="cpu",job="kube-state-metrics"} * on (namespace, pod, cluster)group_left() max by (namespace, pod, cluster) ( (kube_pod_status_phase{phase=~"Pending|Running"} == 1) ) EOF } rule { enabled = true record = "namespace_cpu:kube_pod_container_resource_limits:sum" expression = <<EOF sum by (namespace, cluster) ( sum by (namespace, pod, cluster) ( max by (namespace, pod, container, cluster) ( kube_pod_container_resource_limits{resource="cpu",job="kube-state-metrics"} ) * on(namespace, pod, cluster) group_left() max by (namespace, pod, cluster) ( kube_pod_status_phase{phase=~"Pending|Running"} == 1 ) )) EOF } rule { enabled = true record = "namespace_workload_pod:kube_pod_owner:relabel" expression = <<EOF max by (cluster, namespace, workload, pod) ( label_replace( label_replace( kube_pod_owner{job="kube-state-metrics", owner_kind="ReplicaSet"}, "replicaset", "$1", "owner_name", "(.*)" ) * on(replicaset, namespace) group_left(owner_name) topk by(replicaset, namespace) ( 1, max by (replicaset, namespace, owner_name) ( kube_replicaset_owner{job="kube-state-metrics"} ) ), "workload", "$1", "owner_name", "(.*)" )) EOF labels = { workload_type = "deployment" } } rule { enabled = true record = "namespace_workload_pod:kube_pod_owner:relabel" expression = <<EOF max by (cluster, namespace, workload, pod) ( label_replace( kube_pod_owner{job="kube-state-metrics", owner_kind="DaemonSet"}, "workload", "$1", "owner_name", "(.*)" )) EOF labels = { workload_type = "daemonset" } } rule { enabled = true record = "namespace_workload_pod:kube_pod_owner:relabel" expression = <<EOF max by (cluster, namespace, workload, pod) ( label_replace( kube_pod_owner{job="kube-state-metrics", owner_kind="StatefulSet"}, "workload", "$1", "owner_name", "(.*)" )) EOF labels = { workload_type = "statefulset" } } rule { enabled = true record = "namespace_workload_pod:kube_pod_owner:relabel" expression = <<EOF max by (cluster, namespace, workload, pod) ( label_replace( kube_pod_owner{job="kube-state-metrics", owner_kind="Job"}, "workload", "$1", "owner_name", "(.*)" )) EOF labels = { workload_type = "job" } } rule { enabled = true record = ":node_memory_MemAvailable_bytes:sum" expression = <<EOF sum( node_memory_MemAvailable_bytes{job="node"} or ( node_memory_Buffers_bytes{job="node"} + node_memory_Cached_bytes{job="node"} + node_memory_MemFree_bytes{job="node"} + node_memory_Slab_bytes{job="node"} )) by (cluster) EOF } rule { enabled = true record = "cluster:node_cpu:ratio_rate5m" expression = <<EOF sum(rate(node_cpu_seconds_total{job="node",mode!="idle",mode!="iowait",mode!="steal"}[5m])) by (cluster) /count(sum(node_cpu_seconds_total{job="node"}) by (cluster, instance, cpu)) by (cluster) EOF } tags = merge(tomap({ Service = "AKS monitoring rules" }), var.tags) depends_on = [ azurerm_kubernetes_cluster.aks_cluster, azurerm_monitor_data_collection_rule_association.prometheus_dcra, azurerm_monitor_data_collection_rule_association.ci_dcra ] } resource "azurerm_monitor_alert_prometheus_rule_group" "node_and_kubernetes_recording_rules_rule_group_win" { name = "NodeAndKubernetesRecordingRulesRuleGroup-Win-${azurerm_kubernetes_cluster.aks_cluster.name}" location = var.location resource_group_name = var.rg_name cluster_name = azurerm_kubernetes_cluster.aks_cluster.name description = "Node and Kubernetes Recording Rules Rule Group for Windows Nodes" rule_group_enabled = true interval = "PT1M" scopes = [var.monitoring_workspace_id, azurerm_kubernetes_cluster.aks_cluster.id] rule { enabled = true record = "node:windows_node_filesystem_usage:" expression = <<EOF max by (instance,volume)((windows_logical_disk_size_bytes{job="windows-exporter"} - windows_logical_disk_free_bytes{job="windows-exporter"}) / windows_logical_disk_size_bytes{job="windows-exporter"}) EOF } rule { enabled = true record = "node:windows_node_filesystem_avail:" expression = <<EOF max by (instance, volume) (windows_logical_disk_free_bytes{job="windows-exporter"} / windows_logical_disk_size_bytes{job="windows-exporter"}) EOF } rule { enabled = true record = ":windows_node_net_utilisation:sum_irate" expression = <<EOF sum(irate(windows_net_bytes_total{job="windows-exporter"}[5m])) EOF } rule { enabled = true record = "node:windows_node_net_utilisation:sum_irate" expression = <<EOF sum by (instance) ((irate(windows_net_bytes_total{job="windows-exporter"}[5m]))) EOF } rule { enabled = true record = ":windows_node_net_saturation:sum_irate" expression = <<EOF sum(irate(windows_net_packets_received_discarded_total{job="windows-exporter"}[5m])) + sum(irate(windows_net_packets_outbound_discarded_total{job="windows-exporter"}[5m])) EOF } rule { enabled = true record = "node:windows_node_net_saturation:sum_irate" expression = <<EOF sum by (instance) ((irate(windows_net_packets_received_discarded_total{job="windows-exporter"}[5m]) + irate(windows_net_packets_outbound_discarded_total{job="windows-exporter"}[5m]))) EOF } rule { enabled = true record = "windows_pod_container_available" expression = <<EOF windows_container_available{job="windows-exporter", container_id != ""} * on(container_id) group_left(container, pod, namespace) max(kube_pod_container_info{job="kube-state-metrics", container_id != ""}) by(container, container_id, pod, namespace) EOF } rule { enabled = true record = "windows_container_total_runtime" expression = <<EOF windows_container_cpu_usage_seconds_total{job="windows-exporter", container_id != ""} * on(container_id) group_left(container, pod, namespace) max(kube_pod_container_info{job="kube-state-metrics", container_id != ""}) by(container, container_id, pod, namespace) EOF } rule { enabled = true record = "windows_container_memory_usage" expression = <<EOF windows_container_memory_usage_commit_bytes{job="windows-exporter", container_id != ""} * on(container_id) group_left(container, pod, namespace) max(kube_pod_container_info{job="kube-state-metrics", container_id != ""}) by(container, container_id, pod, namespace) EOF } rule { enabled = true record = "windows_container_private_working_set_usage" expression = <<EOF windows_container_memory_usage_private_working_set_bytes{job="windows-exporter", container_id != ""} * on(container_id) group_left(container, pod, namespace) max(kube_pod_container_info{job="kube-state-metrics", container_id != ""}) by(container, container_id, pod, namespace) EOF } rule { enabled = true record = "windows_container_network_received_bytes_total" expression = <<EOF windows_container_network_receive_bytes_total{job="windows-exporter", container_id != ""} * on(container_id) group_left(container, pod, namespace) max(kube_pod_container_info{job="kube-state-metrics", container_id != ""}) by(container, container_id, pod, namespace) EOF } rule { enabled = true record = "windows_container_network_transmitted_bytes_total" expression = <<EOF windows_container_network_transmit_bytes_total{job="windows-exporter", container_id != ""} * on(container_id) group_left(container, pod, namespace) max(kube_pod_container_info{job="kube-state-metrics", container_id != ""}) by(container, container_id, pod, namespace) EOF } rule { enabled = true record = "kube_pod_windows_container_resource_memory_request" expression = <<EOF max by (namespace, pod, container) (kube_pod_container_resource_requests{resource="memory",job="kube-state-metrics"}) * on(container,pod,namespace) (windows_pod_container_available) EOF } rule { enabled = true record = "kube_pod_windows_container_resource_memory_limit" expression = <<EOF kube_pod_container_resource_limits{resource="memory",job="kube-state-metrics"} * on(container,pod,namespace) (windows_pod_container_available) EOF } rule { enabled = true record = "kube_pod_windows_container_resource_cpu_cores_request" expression = <<EOF max by (namespace, pod, container) ( kube_pod_container_resource_requests{resource="cpu",job="kube-state-metrics"}) * on(container,pod,namespace) (windows_pod_container_available) EOF } rule { enabled = true record = "kube_pod_windows_container_resource_cpu_cores_limit" expression = <<EOF kube_pod_container_resource_limits{resource="cpu",job="kube-state-metrics"} * on(container,pod,namespace) (windows_pod_container_available) EOF } rule { enabled = true record = "namespace_pod_container:windows_container_cpu_usage_seconds_total:sum_rate" expression = <<EOF sum by (namespace, pod, container) (rate(windows_container_total_runtime{}[5m])) EOF } tags = merge(tomap({ Service = "AKS monitoring rules" }), var.tags) depends_on = [ azurerm_kubernetes_cluster.aks_cluster, azurerm_monitor_data_collection_rule_association.prometheus_dcra, azurerm_monitor_data_collection_rule_association.ci_dcra ] } resource "azurerm_monitor_alert_prometheus_rule_group" "node_recording_rules_rule_group_win" { name = "NodeRecordingRulesRuleGroup-Win-${azurerm_kubernetes_cluster.aks_cluster.name}" location = var.location resource_group_name = var.rg_name cluster_name = azurerm_kubernetes_cluster.aks_cluster.name description = "Node and Kubernetes Recording Rules Rule Group for Windows Nodes" rule_group_enabled = true interval = "PT1M" scopes = [var.monitoring_workspace_id, azurerm_kubernetes_cluster.aks_cluster.id] rule { enabled = true record = "node:windows_node:sum" expression = <<EOF count (windows_system_boot_time_timestamp_seconds{job="windows-exporter"}) EOF } rule { enabled = true record = "node:windows_node_num_cpu:sum" expression = <<EOF count by (instance) (sum by (instance, core) (windows_cpu_time_total{job="windows-exporter"})) EOF } rule { enabled = true record = ":windows_node_cpu_utilisation:avg5m" expression = <<EOF 1 - avg(rate(windows_cpu_time_total{job="windows-exporter",mode="idle"}[5m])) EOF } rule { enabled = true record = "node:windows_node_cpu_utilisation:avg5m" expression = <<EOF 1 - avg by (instance) (rate(windows_cpu_time_total{job="windows-exporter",mode="idle"}[5m])) EOF } rule { enabled = true record = ":windows_node_memory_utilisation:" expression = <<EOF 1 -sum(windows_memory_available_bytes{job="windows-exporter"})/sum(windows_os_visible_memory_bytes{job="windows-exporter"}) EOF } rule { enabled = true record = ":windows_node_memory_MemFreeCached_bytes:sum" expression = <<EOF sum(windows_memory_available_bytes{job="windows-exporter"} + windows_memory_cache_bytes{job="windows-exporter"}) EOF } rule { enabled = true record = "node:windows_node_memory_totalCached_bytes:sum" expression = <<EOF (windows_memory_cache_bytes{job="windows-exporter"} + windows_memory_modified_page_list_bytes{job="windows-exporter"} + windows_memory_standby_cache_core_bytes{job="windows-exporter"} + windows_memory_standby_cache_normal_priority_bytes{job="windows-exporter"} + windows_memory_standby_cache_reserve_bytes{job="windows-exporter"}) EOF } rule { enabled = true record = ":windows_node_memory_MemTotal_bytes:sum" expression = <<EOF sum(windows_os_visible_memory_bytes{job="windows-exporter"}) EOF } rule { enabled = true record = "node:windows_node_memory_bytes_available:sum" expression = <<EOF sum by (instance) ((windows_memory_available_bytes{job="windows-exporter"})) EOF } rule { enabled = true record = "node:windows_node_memory_bytes_total:sum" expression = <<EOF sum by (instance) (windows_os_visible_memory_bytes{job="windows-exporter"}) EOF } rule { enabled = true record = "node:windows_node_memory_utilisation:ratio" expression = <<EOF (node:windows_node_memory_bytes_total:sum - node:windows_node_memory_bytes_available:sum) / scalar(sum(node:windows_node_memory_bytes_total:sum)) EOF } rule { enabled = true record = "node:windows_node_memory_utilisation:" expression = <<EOF 1 - (node:windows_node_memory_bytes_available:sum / node:windows_node_memory_bytes_total:sum) EOF } rule { enabled = true record = "node:windows_node_memory_swap_io_pages:irate" expression = <<EOF irate(windows_memory_swap_page_operations_total{job="windows-exporter"}[5m]) EOF } rule { enabled = true record = ":windows_node_disk_utilisation:avg_irate" expression = <<EOF avg(irate(windows_logical_disk_read_seconds_total{job="windows-exporter"}[5m]) + irate(windows_logical_disk_write_seconds_total{job="windows-exporter"}[5m])) EOF } rule { enabled = true record = "node:windows_node_disk_utilisation:avg_irate" expression = <<EOF avg by (instance) ((irate(windows_logical_disk_read_seconds_total{job="windows-exporter"}[5m]) + irate(windows_logical_disk_write_seconds_total{job="windows-exporter"}[5m]))) EOF } tags = merge(tomap({ Service = "AKS monitoring rules" }), var.tags) depends_on = [ azurerm_kubernetes_cluster.aks_cluster, azurerm_monitor_data_collection_rule_association.prometheus_dcra, azurerm_monitor_data_collection_rule_association.ci_dcra ] } resource "azurerm_monitor_alert_prometheus_rule_group" "ux_recording_rules_rule_group" { name = "UXRecordingRulesRuleGroup - ${azurerm_kubernetes_cluster.aks_cluster.name}" location = var.location resource_group_name = var.rg_name cluster_name = azurerm_kubernetes_cluster.aks_cluster.name description = "UX recording rules for Linux" rule_group_enabled = true interval = "PT1M" scopes = [var.monitoring_workspace_id, azurerm_kubernetes_cluster.aks_cluster.id] rule { enabled = true record = "ux:pod_cpu_usage:sum_irate" expression = <<EOF (sum by (namespace, pod, cluster, microsoft_resourceid) ( irate(container_cpu_usage_seconds_total{container != "", pod != "", job = "cadvisor"}[5m]) )) * on (pod, namespace, cluster, microsoft_resourceid) group_left (node, created_by_name, created_by_kind) (max by (node, created_by_name, created_by_kind, pod, namespace, cluster, microsoft_resourceid) (kube_pod_info{pod != "", job = "kube-state-metrics"})) EOF } rule { enabled = true record = "ux:controller_cpu_usage:sum_irate" expression = <<EOF sum by (namespace, node, cluster, created_by_name, created_by_kind, microsoft_resourceid) ( ux:pod_cpu_usage:sum_irate ) EOF } rule { enabled = true record = "ux:pod_workingset_memory:sum" expression = <<EOF ( sum by (namespace, pod, cluster, microsoft_resourceid) ( container_memory_working_set_bytes{container != "", pod != "", job = "cadvisor"} ) ) * on (pod, namespace, cluster, microsoft_resourceid) group_left (node, created_by_name, created_by_kind) (max by (node, created_by_name, created_by_kind, pod, namespace, cluster, microsoft_resourceid) (kube_pod_info{pod != "", job = "kube-state-metrics"})) EOF } rule { enabled = true record = "ux:controller_workingset_memory:sum" expression = <<EOF sum by (namespace, node, cluster, created_by_name, created_by_kind, microsoft_resourceid) ( ux:pod_workingset_memory:sum ) EOF } rule { enabled = true record = "ux:pod_rss_memory:sum" expression = <<EOF ( sum by (namespace, pod, cluster, microsoft_resourceid) ( container_memory_rss{container != "", pod != "", job = "cadvisor"} ) ) * on (pod, namespace, cluster, microsoft_resourceid) group_left (node, created_by_name, created_by_kind) (max by (node, created_by_name, created_by_kind, pod, namespace, cluster, microsoft_resourceid) (kube_pod_info{pod != "", job = "kube-state-metrics"})) EOF } rule { enabled = true record = "ux:controller_rss_memory:sum" expression = <<EOF sum by (namespace, node, cluster, created_by_name, created_by_kind, microsoft_resourceid) ( ux:pod_rss_memory:sum ) EOF } rule { enabled = true record = "ux:pod_container_count:sum" expression = <<EOF sum by (node, created_by_name, created_by_kind, namespace, cluster, pod, microsoft_resourceid) ( (( sum by (container, pod, namespace, cluster, microsoft_resourceid) (kube_pod_container_info{container != "", pod != "", container_id != "", job = "kube-state-metrics"}) or sum by (container, pod, namespace, cluster, microsoft_resourceid) (kube_pod_init_container_info{container != "", pod != "", container_id != "", job = "kube-state-metrics"}) ) * on (pod, namespace, cluster, microsoft_resourceid) group_left (node, created_by_name, created_by_kind) ( max by (node, created_by_name, created_by_kind, pod, namespace, cluster, microsoft_resourceid) ( kube_pod_info{pod != "", job = "kube-state-metrics"} ) ) ) ) EOF } rule { enabled = true record = "ux:controller_container_count:sum" expression = <<EOF sum by (node, created_by_name, created_by_kind, namespace, cluster, microsoft_resourceid) ( ux:pod_container_count:sum ) EOF } rule { enabled = true record = "ux:pod_container_restarts:max" expression = <<EOF max by (node, created_by_name, created_by_kind, namespace, cluster, pod, microsoft_resourceid) ( (( max by (container, pod, namespace, cluster, microsoft_resourceid) (kube_pod_container_status_restarts_total{container != "", pod != "", job = "kube-state-metrics"}) or sum by (container, pod, namespace, cluster, microsoft_resourceid) (kube_pod_init_status_restarts_total{container != "", pod != "", job = "kube-state-metrics"}) ) * on (pod, namespace, cluster, microsoft_resourceid) group_left (node, created_by_name, created_by_kind) ( max by (node, created_by_name, created_by_kind, pod, namespace, cluster, microsoft_resourceid) ( kube_pod_info{pod != "", job = "kube-state-metrics"} ) ) ) ) EOF } rule { enabled = true record = "ux:controller_container_restarts:max" expression = <<EOF max by (node, created_by_name, created_by_kind, namespace, cluster, microsoft_resourceid) ( ux:pod_container_restarts:max ) EOF } rule { enabled = true record = "ux:pod_resource_limit:sum" expression = <<EOF (sum by (cluster, pod, namespace, resource, microsoft_resourceid) ( ( max by (cluster, microsoft_resourceid, pod, container, namespace, resource) (kube_pod_container_resource_limits{container != "", pod != "", job = "kube-state-metrics"}) ) )unless (count by (pod, namespace, cluster, resource, microsoft_resourceid) (kube_pod_container_resource_limits{container != "", pod != "", job = "kube-state-metrics"}) != on (pod, namespace, cluster, microsoft_resourceid) group_left() sum by (pod, namespace, cluster, microsoft_resourceid) (kube_pod_container_info{container != "", pod != "", job = "kube-state-metrics"}) ) )* on (namespace, pod, cluster, microsoft_resourceid) group_left (node, created_by_kind, created_by_name) ( kube_pod_info{pod != "", job = "kube-state-metrics"} ) EOF } rule { enabled = true record = "ux:controller_resource_limit:sum" expression = <<EOF sum by (cluster, namespace, created_by_name, created_by_kind, node, resource, microsoft_resourceid) ( ux:pod_resource_limit:sum ) EOF } rule { enabled = true record = "ux:controller_pod_phase_count:sum" expression = <<EOF sum by (cluster, phase, node, created_by_kind, created_by_name, namespace, microsoft_resourceid) ( ( (kube_pod_status_phase{job="kube-state-metrics",pod!=""}) or (label_replace((count(kube_pod_deletion_timestamp{job="kube-state-metrics",pod!=""}) by (namespace, pod, cluster, microsoft_resourceid) * count(kube_pod_status_reason{reason="NodeLost", job="kube-state-metrics"} == 0) by (namespace, pod, cluster, microsoft_resourceid)), "phase", "terminating", "", ""))) * on (pod, namespace, cluster, microsoft_resourceid) group_left (node, created_by_name, created_by_kind) ( max by (node, created_by_name, created_by_kind, pod, namespace, cluster, microsoft_resourceid) ( kube_pod_info{job="kube-state-metrics",pod!=""} ) ) ) EOF } rule { enabled = true record = "ux:cluster_pod_phase_count:sum" expression = <<EOF sum by (cluster, phase, node, namespace, microsoft_resourceid) ( ux:controller_pod_phase_count:sum ) EOF } rule { enabled = true record = "ux:node_cpu_usage:sum_irate" expression = <<EOF sum by (instance, cluster, microsoft_resourceid) ( (1 - irate(node_cpu_seconds_total{job="node", mode="idle"}[5m])) ) EOF } rule { enabled = true record = "ux:node_memory_usage:sum" expression = <<EOF sum by (instance, cluster, microsoft_resourceid) (( node_memory_MemTotal_bytes{job = "node"} - node_memory_MemFree_bytes{job = "node"} - node_memory_cached_bytes{job = "node"} - node_memory_buffers_bytes{job = "node"} )) EOF } rule { enabled = true record = "ux:node_network_receive_drop_total:sum_irate" expression = <<EOF sum by (instance, cluster, microsoft_resourceid) (irate(node_network_receive_drop_total{job="node", device!="lo"}[5m])) EOF } rule { enabled = true record = "ux:node_network_transmit_drop_total:sum_irate" expression = <<EOF sum by (instance, cluster, microsoft_resourceid) (irate(node_network_transmit_drop_total{job="node", device!="lo"}[5m])) EOF } tags = merge(tomap({ Service = "AKS monitoring rules" }), var.tags) depends_on = [ azurerm_kubernetes_cluster.aks_cluster, azurerm_monitor_data_collection_rule_association.prometheus_dcra, azurerm_monitor_data_collection_rule_association.ci_dcra ] } resource "azurerm_monitor_alert_prometheus_rule_group" "ux_recording_rules_rule_group_windows" { name = "UXRecordingRulesRuleGroup-Win - ${azurerm_kubernetes_cluster.aks_cluster.name}" location = var.location resource_group_name = var.rg_name cluster_name = azurerm_kubernetes_cluster.aks_cluster.name description = "UX recording rules for Windows" rule_group_enabled = true interval = "PT1M" scopes = [ var.monitoring_workspace_id, azurerm_kubernetes_cluster.aks_cluster.id ] rule { enabled = true record = "ux:pod_cpu_usage_windows:sum_irate" expression = <<EOF sum by (cluster, pod, namespace, node, created_by_kind, created_by_name, microsoft_resourceid) ( ( max by (instance, container_id, cluster, microsoft_resourceid) ( irate(windows_container_cpu_usage_seconds_total{ container_id != "", job = "windows-exporter"}[5m]) ) * on (container_id, cluster, microsoft_resourceid) group_left (container, pod, namespace) ( max by (container, container_id, pod, namespace, cluster, microsoft_resourceid) ( kube_pod_container_info{container != "", pod != "", container_id != "", job = "kube-state-metrics"} ) ) ) * on (pod, namespace, cluster, microsoft_resourceid) group_left (node, created_by_name, created_by_kind) ( max by (node, created_by_name, created_by_kind, pod, namespace, cluster, microsoft_resourceid) ( kube_pod_info{ pod != "", job = "kube-state-metrics"} ) ) ) EOF } rule { enabled = true record = "ux:controller_cpu_usage_windows:sum_irate" expression = "sum by (namespace, node, cluster, created_by_name, created_by_kind, microsoft_resourceid) (ux:pod_cpu_usage_windows:sum_irate)" } rule { enabled = true record = "ux:pod_workingset_memory_windows:sum" expression = <<EOF sum by (cluster, pod, namespace, node, created_by_kind, created_by_name, microsoft_resourceid) ( ( max by (instance, container_id, cluster, microsoft_resourceid) ( windows_container_memory_usage_private_working_set_bytes{ container_id != "", job = "windows-exporter"} ) * on (container_id, cluster, microsoft_resourceid) group_left (container, pod, namespace) ( max by (container, container_id, pod, namespace, cluster, microsoft_resourceid) ( kube_pod_container_info{container != "", pod != "", container_id != "", job = "kube-state-metrics"} ) ) ) * on (pod, namespace, cluster, microsoft_resourceid) group_left (node, created_by_name, created_by_kind) ( max by (node, created_by_name, created_by_kind, pod, namespace, cluster, microsoft_resourceid) ( kube_pod_info{ pod != "", job = "kube-state-metrics"} ) ) ) EOF } rule { enabled = true record = "ux:controller_workingset_memory_windows:sum" expression = "sum by (namespace, node, cluster, created_by_name, created_by_kind, microsoft_resourceid) (ux:pod_workingset_memory_windows:sum)" } rule { enabled = true record = "ux:node_cpu_usage_windows:sum_irate" expression = "sum by (instance, cluster, microsoft_resourceid) ((1 - irate(windows_cpu_time_total{job=\"windows-exporter\", mode=\"idle\"}[5m])))" } rule { enabled = true record = "ux:node_memory_usage_windows:sum" expression = "sum by (instance, cluster, microsoft_resourceid) ((windows_os_visible_memory_bytes{job = \"windows-exporter\"}- windows_memory_available_bytes{job = \"windows-exporter\"}))" } rule { enabled = true record = "ux:node_network_packets_received_drop_total_windows:sum_irate" expression = "sum by (instance, cluster, microsoft_resourceid) (irate(windows_net_packets_received_discarded_total{job=\"windows-exporter\", device!=\"lo\"}[5m]))" } rule { enabled = true record = "ux:node_network_packets_outbound_drop_total_windows:sum_irate" expression = "sum by (instance, cluster, microsoft_resourceid) (irate(windows_net_packets_outbound_discarded_total{job=\"windows-exporter\", device!=\"lo\"}[5m]))" } tags = merge(tomap({ Service = "AKS monitoring rules" }), var.tags) depends_on = [ azurerm_kubernetes_cluster.aks_cluster, azurerm_monitor_data_collection_rule_association.prometheus_dcra, azurerm_monitor_data_collection_rule_association.ci_dcra ] } #endregion Managed Prometheus Rule Groups
After setting up check your managed grafana and you would be able to see thethe Azure minotr workspace prometheus data source is available in managed grafana as shown below.
Then we can adda variable in a Grafana dashboard as shown below to dynamically select the prometheus data source.
No comments:
Post a Comment