So Your System’s on Fire, Now What?
You’ve got services running, databases humming, containers doing container things. How do you know if everything’s okay before someone emails you at 2 AM saying “your site’s down lol”?
Enter Prometheus. It’s open-source, handles time-series data, and watches how your system behaves over time.
How Prometheus Works
Most monitoring tools wait for data to come to them. Prometheus does the opposite. It goes out and asks for it.
The Pull Model:
Instead of services pushing data (which they’d probably forget to do), Prometheus periodically reaches out to your apps, databases, and servers and asks for their status. It scrapes endpoints (usually at /metrics) and stores everything with timestamps.
cpu_usage{host="web-1"} = 73% # CPU usage on host web-1 is currently at 73%
memory_free{host="db-1"} = 2.4GB # Free memory on database host db-1 is 2.4 GB
http_requests_total{method="GET", status="200"} = 142 # Total count of successful (200) GET requests so far
The Data Model
Every metric in Prometheus has labels — key-value pairs that tell you what you’re measuring.
Instead of a boring number like 42, you get:
http_requests_total{method="POST", status="200", endpoint="/api/predict"} = 42
That tells you 42 POST requests to /api/predict returned 200 OK. Labels let you slice and dice however you want.
Why Airflow + Prometheus
Airflow DAGs aren’t just for batch inference. They’re also good for scheduled metric checks and self-healing workflows.
1. Export Airflow Itself to Prometheus
Airflow has a built-in Prometheus exporter. Enable it and Prometheus can scrape Airflow’s own metrics — DAG duration, task failures, scheduler health:
# In your airflow.cfg or helm values # Place this configuration in airflow.cfg or as Helm chart values
[metrics] # Beginning of the metrics configuration section in Airflow
prometheus_enabled = True # Enable the Prometheus metrics exporter in Airflow
prometheus_endpoint = /metrics # Set the endpoint path where Prometheus scrapes Airflow metrics
Now Prometheus scrapes Airflow and you can set up alerts for slow or failing DAGs.
2. Airflow Runs Your Custom Metric Scrapers
Sometimes Prometheus can’t reach a service directly. It might be behind a VPN or only runs on a schedule. Airflow can scrape those metrics and push them to a Prometheus-compatible endpoint:
from airflow import DAG # Import the core DAG class for defining Airflow workflows
from airflow.operators.python import PythonOperator # Import the operator that runs arbitrary Python functions
from datetime import datetime # Import datetime for specifying the DAG's start_date parameter
import requests # Import the HTTP library for scraping metrics from internal endpoints
def scrape_metrics(): # Define the function that will scrape internal endpoints and push to PushGateway
targets = [ # List of internal service URLs that expose Prometheus metrics
"http://internal-api:8080/metrics", # Internal API service metrics endpoint
"http://batch-worker:9090/metrics", # Batch worker service metrics endpoint
]
for target in targets: # Iterate over each target endpoint
try: # Wrap scraping in try-except to handle transient network or service failures
resp = requests.get(target, timeout=10) # Send a GET request to the target with a 10-second timeout
requests.post( # Forward the scraped metrics to Prometheus's PushGateway
"http://prometheus-pushgateway:9091/metrics/job/batch_scraper", # PushGateway URL with job label "batch_scraper"
data=resp.text, # Use the raw response body as the POST payload (Prometheus text format)
)
except Exception as e: # Catch any exception (network error, timeout, connection refused)
print(f"Failed to scrape {target}: {e}") # Log the failure with the target URL and error message
with DAG( # Enter the DAG context manager to define the workflow structure
"prometheus-metric-scraper", # Unique DAG identifier displayed in the Airflow UI and stored in the database
schedule="*/5 * * * *", # Cron expression: execute this DAG every 5 minutes
start_date=datetime(2026, 1, 1), # DAG start date (prevents automatic backfilling before this date)
catchup=False, # Skip creating DAG runs for past schedule intervals that were missed
tags=["monitoring"], # Tag this DAG as "monitoring" for easier filtering in the Airflow UI
) as dag: # Bind the DAG object to the variable `dag`
scrape = PythonOperator( # Instantiate a PythonOperator task
task_id="scrape-internal-metrics", # Unique task identifier within this DAG
python_callable=scrape_metrics, # Reference the Python function to execute when this task runs
)
Airflow scrapes internal endpoints every 5 minutes and pushes them to Prometheus’s PushGateway.
3. Airflow Alerts on Prometheus Metrics
Use Airflow’s HttpSensor to check Prometheus before running a DAG. If your model’s error rate spiked, skip the batch job:
from airflow.sensors.http import HttpSensor # Import the HTTP sensor that checks an external HTTP endpoint
check_model_health = HttpSensor( # Create an HttpSensor task instance
task_id="check-prometheus-alert", # Unique task identifier for Airflow's internal tracking
http_conn_id="prometheus_conn", # Reference to an Airflow connection storing Prometheus base URL and credentials
endpoint="/api/v1/query", # Prometheus API endpoint for running instant queries
request_params={ # Dictionary of query parameters appended to the endpoint URL
"query": 'http_requests_total{endpoint="/invocations", status="5xx"} > 100' # PromQL: trigger if 5xx errors exceed 100
},
response_check=lambda resp: len(resp.json()["data"]["result"]) == 0, # Return True (healthy) if no results match
mode="reschedule", # If the sensor condition is not met, reschedule the check instead of blocking a worker slot
timeout=300, # Maximum number of seconds the sensor will wait before timing out
)
If Prometheus sees more than 100 5xx errors on your model endpoint, the sensor waits and Airflow doesn’t fire batch inference.
4. Monitor Airflow DAGs with Prometheus + Grafana
Now that Prometheus is scraping Airflow, plug it into Grafana. Make a dashboard that shows:
- DAG success/failure rate
- Task duration percentiles
- Running tasks vs. available slots
- Scheduler health
One pane of glass for your DAGs.
The DAG That Watches Itself
You can build monitoring DAGs in Airflow that exist solely to check on other stuff. No data processing, just observability:
from airflow import DAG # Import the core DAG class for defining Airflow workflows
from airflow.operators.python import PythonOperator # Import the operator that runs arbitrary Python functions
from datetime import datetime # Import datetime for specifying the DAG's start_date parameter
import requests # Import the HTTP library for communicating with Prometheus and Alertmanager APIs
PROMETHEUS = "http://prometheus:9090" # Internal cluster URL for the Prometheus server
ALERTMANAGER = "http://alertmanager:9093" # Internal cluster URL for the Alertmanager service
def check_and_alert(): # Define the function that queries Prometheus and fires alerts via Alertmanager
query = 'rate(cpu_usage{app="ml-model"}[5m]) > 0.8' # PromQL: trigger if 5-minute average CPU exceeds 80%
resp = requests.get( # Send a GET request to Prometheus's instant query API
f"{PROMETHEUS}/api/v1/query", # Construct the full URL by appending the API path to the Prometheus base URL
params={"query": query}, # Pass the PromQL query as a URL query parameter
)
results = resp.json()["data"]["result"] # Extract the result array from Prometheus's JSON response
if results: # If the result array is non-empty, the CPU threshold was breached on at least one pod
alert = { # Build an alert payload that conforms to Alertmanager's API schema
"labels": {"alertname": "HighCPU", "severity": "warning"}, # Standard labels: alert name and severity level
"annotations": {"summary": f"CPU spike detected on {len(results)} pods"}, # Human-readable summary annotation
}
requests.post(f"{ALERTMANAGER}/api/v1/alerts", json=[alert]) # POST the alert payload to Alertmanager's API
print(f"Alert sent: {alert}") # Log the alert details so they appear in Airflow task logs
with DAG( # Enter the DAG context manager to define the workflow structure
"prometheus-self-check", # Unique DAG identifier displayed in the Airflow UI and stored in the database
schedule="*/1 * * * *", # Cron expression: execute this DAG every minute
start_date=datetime(2026, 1, 1), # DAG start date (prevents automatic backfilling before this date)
catchup=False, # Skip creating DAG runs for past schedule intervals that were missed
) as dag: # Bind the DAG object to the variable `dag`
check = PythonOperator( # Instantiate a PythonOperator task
task_id="check-prometheus-alerts", # Unique task identifier within this DAG
python_callable=check_and_alert, # Reference the Python function to execute when this task runs
)
Every minute, this DAG asks Prometheus if everything’s cool. If not, it fires an alert.
Quick Setup: Prometheus + Airflow
Step 1: Deploy Prometheus
On Kubernetes, use the Helm chart:
# Register the Prometheus community Helm chart repository to make its charts available locally
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
# Install or upgrade the full kube-prometheus-stack (Prometheus, Alertmanager, Grafana) in the monitoring namespace
helm upgrade --install prometheus prometheus-community/kube-prometheus-stack \
--namespace monitoring --create-namespace # Target the "monitoring" namespace; create it if it does not exist
That gets you Prometheus, Alertmanager, and Grafana in one shot.
Step 2: Enable Airflow Metrics
Pass these to your Airflow Helm chart:
config: # Root key for Airflow configuration overrides within the Helm chart values
metrics: # Subsection for metrics-related configuration options
prometheus_enabled: "True" # Enable the Prometheus metrics exporter in Airflow
prometheus_endpoint: "/metrics" # Path where Airflow exposes Prometheus-formatted metrics
extraPipPackages: # List of additional Python packages to install in the Airflow container
- "apache-airflow[prometheus]" # Install the Prometheus extra package to enable the exporter
Port-forward and check:
# Forward local port 8080 to the Airflow webserver service running in the Kubernetes cluster
kubectl port-forward svc/airflow-webserver 8080:8080
# Fetch the Prometheus metrics from the local forwarded endpoint to verify the exporter is working
curl http://localhost:8080/metrics
Step 3: Tell Prometheus to Scrape Airflow
Add a ServiceMonitor:
apiVersion: monitoring.coreos.com/v1 # API version for the Prometheus Operator custom resource definition
kind: ServiceMonitor # Custom resource that tells Prometheus Operator which Kubernetes services to scrape
metadata: # Metadata block for the resource name and namespace
name: airflow-monitor # Name of this ServiceMonitor resource
namespace: monitoring # Namespace where the resource is created (same namespace as Prometheus)
spec: # Specification block defining the service selection and scrape configuration
selector: # Label selector to find the target Kubernetes service(s)
matchLabels: # Match services that have all of the specified labels
component: airflow # Select services labeled with "component: airflow"
endpoints: # List of scrape endpoint configurations for the selected service
- port: metrics # Name of the port on the service where metrics are served
path: /metrics # HTTP path to scrape metrics from
interval: 15s # Scrape interval: collect metrics from this endpoint every 15 seconds
Prometheus now scrapes Airflow every 15 seconds.
You’re Not Done Yet
Monitoring is never done. But here’s the foundation:
- Prometheus collects everything — system metrics, app metrics, custom counters
- Airflow schedules periodic checks, scrapes what Prometheus can’t reach, and decides whether to run jobs based on Prometheus data
- Grafana makes it visible
The combo means you don’t get surprised at 2 AM. Prometheus saw the memory leak coming. Airflow triggered the cleanup DAG before anyone noticed.
Comments
Leave a comment