What You'll Build
In this lesson you will complete the capstone observability layer by deploying a comprehensive PrometheusRule with four alerting rules and one recording rule, configuring Alertmanager routing that sends critical alerts to PagerDuty and all alerts to appropriate Slack channels, and verifying the complete alerting pipeline end-to-end. You will also add an alert specifically for Argo Rollouts canary degradation, connecting the delivery layer's failures directly to the on-call notification system.
The completed observability layer connects all three capstone components: the CI pipeline's signed images flow through the GitOps delivery, the canary rollout's Prometheus-driven analysis determines promotion, and when the canary degrades, an alert fires in Alertmanager and a notification reaches the on-call channel—all without human intervention between code commit and incident notification.
Prerequisites
- The kube-prometheus-stack from M5 Lab 35 deployed with the india-squad-api ServiceMonitor active and scraping metrics from squad-prod.
- Argo Rollouts deployed from Lesson 38 with the Rollout resource active—the canary degradation alert requires the kube_customresource_status_condition metric from kube-state-metrics.
- Slack webhook URL and PagerDuty integration key configured as Kubernetes Secrets in the monitoring namespace for Alertmanager to reference.
- The PrometheusRule from Exercise 34 as a starting point—this lesson extends those rules with the canary rollout alert and refines the Alertmanager routing.
- kubectl port-forward access to Prometheus (9090), Alertmanager (9093), and Grafana (3000) for verification steps.
Step 1 — Deploy the PrometheusRule
Apply the complete PrometheusRule containing the recording rule, three golden signal alerts, and the canary rollout degradation alert. The canary degradation alert uses the `kube_customresource_status_condition` metric from kube-state-metrics to detect when the Rollout resource transitions to Degraded status—connecting the delivery layer's failure state directly to the alerting system without requiring custom exporters or application-level instrumentation.
# Complete PrometheusRule + Alertmanager config for the capstone platform.
# ── PrometheusRule: four golden signal alerts + SLO burn rate ─────────────
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: india-squad-capstone-alerts
namespace: squad-prod
labels:
prometheus: kube-prometheus
role: alert-rules
spec:
groups:
- name: india-squad-capstone.rules
rules:
# Recording rule (reused by alerts and dashboards)
- record: job:http_error_ratio:rate5m
expr: |
sum(rate(http_requests_total{namespace='squad-prod',status=~'5..'}[5m]))
/
sum(rate(http_requests_total{namespace='squad-prod'}[5m]))
- alert: IndiaSquadHighErrorRate
expr: job:http_error_ratio:rate5m > 0.01
for: 5m
labels: { severity: warning, team: india-squad }
annotations:
summary: 'High error rate: {{ $value | humanizePercentage }}'
description: 'Error rate above 1% SLO threshold for 5 minutes.'
runbook_url: 'https://wiki.india-squad.example.com/runbooks/high-error-rate'
- alert: IndiaSquadHighP99Latency
expr: |
histogram_quantile(0.99,
sum(rate(http_request_duration_seconds_bucket{namespace='squad-prod'}[5m])) by (le)
) > 0.5
for: 10m
labels: { severity: warning, team: india-squad }
annotations:
summary: 'P99 latency {{ $value | humanizeDuration }} exceeds 500ms'
- alert: IndiaSquadSLOBudgetBurning
expr: |
sum(rate(http_requests_total{namespace='squad-prod',status=~'5..'}[1h]))
/ (sum(rate(http_requests_total{namespace='squad-prod'}[1h])) * 0.001) > 14.4
for: 2m
labels: { severity: critical, team: india-squad }
annotations:
summary: 'SLO budget burning at {{ $value | humanize }}x rate'
description: >
99.9% SLO error budget exhaustion in approx
{{ div 2 $value | humanizeDuration }} at current burn rate.
- alert: IndiaSquadCanaryRolloutDegraded
expr: |
kube_customresource_status_condition{
customresource_kind='Rollout',
customresource_name='india-squad-api',
namespace='squad-prod',
condition='Degraded',
status='True'
} == 1
for: 0m
labels: { severity: critical, team: india-squad }
annotations:
summary: 'India Squad API canary rollout failed and rolled back'
description: 'The Argo Rollout has transitioned to Degraded. A failed canary deployment was auto-rolled back.'Step 2 — Configure Alertmanager Routing
Apply the Alertmanager configuration that routes critical alerts—SLO budget burning and canary rollout degradation—to both PagerDuty and the on-call Slack channel, while routing warning alerts to a separate team Slack channel. The inhibition rule prevents warning alerts from flooding the channel when a critical alert is already firing for the same service, reducing notification noise during active incidents.
# Alertmanager routes: severity-based routing to Slack and PagerDuty.
# ── alertmanager-config.yaml (applied via kube-prometheus-stack values) ───
global:
resolve_timeout: 5m
slack_api_url: '$SLACK_WEBHOOK_URL' # injected from K8s Secret
templates:
- '/etc/alertmanager/templates/*.tmpl'
route:
group_by: [alertname, namespace, service]
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
receiver: default
routes:
# Critical alerts: immediate PagerDuty page + Slack notification
- matchers:
- severity = critical
- team = india-squad
receiver: india-squad-critical
group_wait: 0s # page immediately, no grouping delay
repeat_interval: 1h
# Warning alerts: Slack only, grouped
- matchers:
- severity = warning
- team = india-squad
receiver: india-squad-warning
receivers:
- name: default
slack_configs:
- channel: '#india-squad-alerts-default'
title: '{{ .CommonLabels.alertname }}'
text: '{{ range .Alerts }}{{ .Annotations.summary }}{{ end }}'
- name: india-squad-critical
pagerduty_configs:
- routing_key: '$PAGERDUTY_KEY'
severity: critical
description: '{{ .CommonLabels.alertname }}: {{ .CommonAnnotations.summary }}'
slack_configs:
- channel: '#india-squad-oncall'
title: '🚨 CRITICAL: {{ .CommonLabels.alertname }}'
text: '{{ range .Alerts }}{{ .Annotations.description }}{{ end }}'
- name: india-squad-warning
slack_configs:
- channel: '#india-squad-alerts'
title: '⚠️ WARNING: {{ .CommonLabels.alertname }}'
text: '{{ range .Alerts }}{{ .Annotations.summary }}{{ end }}'
inhibit_rules:
# Inhibit warning alerts when a critical alert is firing for the same service
- source_matchers: [severity = critical]
target_matchers: [severity = warning]
equal: [namespace, service]Step 3 — Verify End-to-End Alerting
Confirm the complete alerting chain works: PrometheusRule alerts appear in the Prometheus Alerts page, the recording rule produces a metric, Alertmanager routing configuration is active, and a test alert posted via the Alertmanager API produces a Slack notification in the correct channel. Verify the Grafana dashboard shows live golden signal metrics from the running application.
# End-to-end alerting verification.
# 1. Confirm all PrometheusRule alerts are loaded
kubectl port-forward -n monitoring svc/prometheus-operated 9090:9090 &
# Open http://localhost:9090/alerts
# Expected: IndiaSquadHighErrorRate, IndiaSquadHighP99Latency,
# IndiaSquadSLOBudgetBurning, IndiaSquadCanaryRolloutDegraded
# All in 'inactive' state (no current violations)
# 2. Trigger the IndiaSquadHighErrorRate alert for testing
# Generate errors by calling a non-existent endpoint:
kubectl port-forward -n squad-prod svc/india-squad-api-stable 8080:80 &
for i in $(seq 1 200); do
curl -s http://localhost:8080/nonexistent > /dev/null # 404
done
# Note: 404s are not 5xx errors. To trigger the error rate alert,
# temporarily modify the app to return 500 for a specific endpoint.
# 3. Verify the recording rule is producing output
# Expression: job:http_error_ratio:rate5m
# Expected: a value between 0 and 1 for squad-prod
# 4. Verify Alertmanager routing configuration
kubectl port-forward -n monitoring svc/alertmanager-operated 9093:9093 &
# Open http://localhost:9093/#/status
# Expected: routing config shows the india-squad-critical and india-squad-warning routes
# 5. Test Alertmanager Slack routing without triggering a real alert
# Use the Alertmanager API to post a test alert:
curl -X POST http://localhost:9093/api/v2/alerts \
-H 'Content-Type: application/json' \
-d '[{
"labels": {
"alertname": "TestAlert",
"severity": "warning",
"team": "india-squad"
},
"annotations": {"summary": "Test alert from capstone verification"},
"generatorURL": "http://prometheus:9090"
}]'
# Expected: a Slack message appears in #india-squad-alerts
# 6. Confirm Grafana dashboard shows live golden signal metrics
kubectl port-forward -n monitoring svc/kube-prometheus-stack-grafana 3000:80 &
# Open http://localhost:3000/dashboards
# Expected: India Squad API — Golden Signals dashboard
# Panels: Request Rate, Error Rate, P99 Latency all showing dataStep 4 — Connect to Argo Rollouts
Verify that the canary rollout degradation alert works by triggering the auto-rollback from Lesson 38's Step 3—deploying the broken version that returns 500 errors. After the Argo Rollouts AnalysisRun detects the high error rate and the Rollout transitions to Degraded, the `IndiaSquadCanaryRolloutDegraded` alert should fire in Prometheus and a critical notification should appear in the on-call Slack channel within 2 minutes.
# 1. Trigger a rollback by deploying the broken version (from L38 Step 3)
# (temporarily add: raise HTTPException(500) to the API route)
# 2. Watch the rollout degrade
kubectl argo rollouts get rollout india-squad-api -n squad-prod --watch
# Expected: Rollout transitions to Degraded after AnalysisRun fails
# 3. Check the alert fires
# In Prometheus UI (http://localhost:9090/alerts):
# Expected: IndiaSquadCanaryRolloutDegraded in 'firing' state
# 4. Confirm Alertmanager received the alert
# In Alertmanager UI (http://localhost:9093/#/alerts):
# Expected: IndiaSquadCanaryRolloutDegraded alert with severity=critical
# 5. Confirm the Slack notification arrived in #india-squad-oncall
# Expected message: 🚨 CRITICAL: IndiaSquadCanaryRolloutDegraded
# India Squad API canary rollout failed and rolled back
# 6. Clean up: remove the 500 error and re-run the CI pipeline
# The new pipeline run will produce a clean image; ArgoCD will sync it
# and the rollout will succeed, resolving the Degraded alert.Warning: The `IndiaSquadCanaryRolloutDegraded` alert uses `for: 0m`, which means it fires immediately without a confirmation window the moment the Rollout transitions to Degraded. This is intentional for rollback events—there is no transient state where a Rollout is momentarily Degraded and then recovers without intervention. However, if you use `for: 0m` on other alert types like error rate, it will fire on every transient spike, generating excessive notifications. Reserve `for: 0m` only for discrete state transitions—resource phase changes, deployment failures—where the state is unambiguously problematic from the first moment it is observed.
Capstone Tip: Add a deployment annotation to Grafana from the `update-gitops` job in Lesson 37's CI pipeline. After the GitOps commit, post to the Grafana annotations API with the image digest and pipeline run URL. This creates a vertical marker on every Grafana panel at the exact moment of each deployment, allowing the Grafana golden signals dashboard to serve as the primary incident investigation tool: the error rate spike, the latency increase, and the deployment event that caused them are all visible on the same graph.