Back to Blog
3 min read

Prometheus Remote Write to Azure Monitor: Unified Metrics

Azure Monitor now supports Prometheus remote write, enabling you to send metrics from any Prometheus-compatible source to Azure for long-term storage and analysis.

Setting Up Azure Monitor Workspace

resource azureMonitorWorkspace 'Microsoft.Monitor/accounts@2021-06-03-preview' = {
  name: 'prometheus-${environment}'
  location: location
  properties: {}
}

resource prometheusRuleGroup 'Microsoft.AlertsManagement/prometheusRuleGroups@2021-07-22-preview' = {
  name: 'kubernetes-recording-rules'
  location: location
  properties: {
    scopes: [azureMonitorWorkspace.id]
    enabled: true
    interval: 'PT1M'
    rules: [
      {
        record: 'node:cpu_utilization:avg5m'
        expression: '1 - avg(rate(node_cpu_seconds_total{mode="idle"}[5m])) by (instance)'
      }
      {
        record: 'node:memory_utilization:ratio'
        expression: '1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes'
      }
    ]
  }
}

output queryEndpoint string = azureMonitorWorkspace.properties.metrics.prometheusQueryEndpoint
output ingestEndpoint string = azureMonitorWorkspace.properties.metrics.internalId

Prometheus Configuration

# prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s

remote_write:
  - url: "https://prometheus-prod.australiaeast.prometheus.monitor.azure.com/api/v1/write"
    azure_ad:
      managed_identity:
        client_id: "${AZURE_CLIENT_ID}"
    queue_config:
      max_samples_per_send: 1000
      max_shards: 200
      capacity: 2500

scrape_configs:
  - job_name: 'kubernetes-pods'
    kubernetes_sd_configs:
      - role: pod
    relabel_configs:
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
        action: keep
        regex: true
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
        action: replace
        target_label: __metrics_path__
        regex: (.+)

AKS with Managed Prometheus

resource aksCluster 'Microsoft.ContainerService/managedClusters@2022-03-01' = {
  name: aksClusterName
  location: location
  properties: {
    azureMonitorProfile: {
      metrics: {
        enabled: true
        kubeStateMetrics: {
          metricLabelsAllowlist: '*'
          metricAnnotationsAllowList: '*'
        }
      }
    }
    addonProfiles: {
      omsagent: {
        enabled: true
        config: {
          logAnalyticsWorkspaceResourceID: logAnalyticsWorkspace.id
          useAADAuth: 'true'
        }
      }
    }
  }
}

resource dcrAssociation 'Microsoft.Insights/dataCollectionRuleAssociations@2021-09-01-preview' = {
  name: 'aks-prometheus-dcr'
  scope: aksCluster
  properties: {
    dataCollectionRuleId: prometheusDCR.id
  }
}

Data Collection Rule for Prometheus

resource prometheusDCR 'Microsoft.Insights/dataCollectionRules@2021-09-01-preview' = {
  name: 'dcr-prometheus-metrics'
  location: location
  kind: 'Linux'
  properties: {
    dataSources: {
      prometheusForwarder: [
        {
          name: 'prometheusDataSource'
          streams: ['Microsoft-PrometheusMetrics']
          labelIncludeFilter: {
            'kubernetes_namespace': ['default', 'kube-system', 'monitoring']
          }
        }
      ]
    }
    destinations: {
      monitoringAccounts: [
        {
          name: 'azureMonitor'
          accountResourceId: azureMonitorWorkspace.id
        }
      ]
    }
    dataFlows: [
      {
        streams: ['Microsoft-PrometheusMetrics']
        destinations: ['azureMonitor']
      }
    ]
  }
}

Querying Prometheus Metrics

PromQL in Azure

# CPU usage by pod
sum(rate(container_cpu_usage_seconds_total{namespace="production"}[5m])) by (pod)

# Memory usage percentage
(sum(container_memory_working_set_bytes{namespace="production"}) by (pod))
/
(sum(container_spec_memory_limit_bytes{namespace="production"}) by (pod))
* 100

# Request rate by service
sum(rate(http_requests_total{namespace="production"}[5m])) by (service)

# Error rate
sum(rate(http_requests_total{status=~"5.."}[5m]))
/
sum(rate(http_requests_total[5m]))

Using the API

import requests
from azure.identity import DefaultAzureCredential

class PrometheusQueryClient:
    def __init__(self, query_endpoint: str):
        self.endpoint = query_endpoint
        self.credential = DefaultAzureCredential()

    def query(self, promql: str) -> dict:
        """Execute a PromQL query."""
        token = self.credential.get_token("https://prometheus.monitor.azure.com/.default")

        headers = {
            "Authorization": f"Bearer {token.token}",
            "Content-Type": "application/x-www-form-urlencoded"
        }

        response = requests.post(
            f"{self.endpoint}/api/v1/query",
            headers=headers,
            data={"query": promql}
        )

        return response.json()

    def query_range(self, promql: str, start: str, end: str, step: str) -> dict:
        """Execute a range query."""
        token = self.credential.get_token("https://prometheus.monitor.azure.com/.default")

        headers = {"Authorization": f"Bearer {token.token}"}

        response = requests.get(
            f"{self.endpoint}/api/v1/query_range",
            headers=headers,
            params={
                "query": promql,
                "start": start,
                "end": end,
                "step": step
            }
        )

        return response.json()

# Usage
client = PrometheusQueryClient(
    "https://prometheus-prod.australiaeast.prometheus.monitor.azure.com"
)

result = client.query('up{job="kubernetes-pods"}')

Grafana Integration

{
  "name": "Azure Managed Prometheus",
  "type": "prometheus",
  "access": "proxy",
  "url": "https://prometheus-prod.australiaeast.prometheus.monitor.azure.com",
  "jsonData": {
    "azureCredentials": {
      "authType": "msi"
    },
    "httpMethod": "POST"
  }
}

Alerting with Prometheus Rules

resource alertRuleGroup 'Microsoft.AlertsManagement/prometheusRuleGroups@2021-07-22-preview' = {
  name: 'kubernetes-alerts'
  location: location
  properties: {
    scopes: [azureMonitorWorkspace.id]
    enabled: true
    interval: 'PT1M'
    rules: [
      {
        alert: 'HighPodCPU'
        expression: 'sum(rate(container_cpu_usage_seconds_total[5m])) by (pod, namespace) > 0.8'
        for: 'PT5M'
        severity: 2
        annotations: {
          summary: 'Pod {{ $labels.pod }} in {{ $labels.namespace }} has high CPU usage'
        }
        actions: [
          {
            actionGroupId: actionGroup.id
          }
        ]
      }
      {
        alert: 'PodNotReady'
        expression: 'kube_pod_status_ready{condition="false"} == 1'
        for: 'PT5M'
        severity: 1
        annotations: {
          summary: 'Pod {{ $labels.pod }} is not ready'
        }
      }
    ]
  }
}

Prometheus remote write to Azure Monitor provides a powerful, unified metrics platform for cloud-native applications.

Michael John Peña

Michael John Peña

Senior Data Engineer based in Sydney. Writing about data, cloud, and technology.