Monitoring Module¶
Terraform module for deploying the Prometheus Stack (Prometheus, Grafana, AlertManager) and ElastAlert2 to Kubernetes. Prometheus Operator CRDs are owned by the standalone chart in the Kubernetes module; this module disables the stack chart's CRD installation.
Architecture¶
flowchart TB
subgraph terraform [Stage 2 Terraform ordering]
Preflight["module.preflight<br/>chart compatibility gate"]
KubernetesModule["module.kubernetes"]
CRDRelease["helm_release.prometheus_operator_crds"]
NginxModule["module.nginx"]
CertManagerModule["module.cert_manager_letsencrypt"]
LoggingModule["module.logging<br/>optional"]
MonitoringModule["module.monitoring"]
RuleResources["kubectl_manifest.prometheus_rules"]
StackValues["prometheus-stack-values.tftpl<br/>crds.enabled=false"]
StackRelease["helm_release.prometheus_operator"]
end
subgraph external [External]
User[User Browser]
Slack[Slack]
end
subgraph k8s [Kubernetes Cluster]
PrometheusCRDs["Cluster-scoped Prometheus Operator CRDs"]
subgraph ingress [Ingress Layer]
Nginx[NGINX Ingress]
OAuth[OAuth2 Proxy]
end
subgraph ns [Namespace: monitoring]
subgraph prom [Prometheus Stack]
Prometheus[Prometheus]
Grafana[Grafana]
AlertManager[AlertManager]
end
subgraph elastic [ElastAlert2]
ElastAlert[ElastAlert2]
end
subgraph rules [PrometheusRules]
LonghornRules[Longhorn Rules]
OmniRouteRules[OmniRoute Rules]
PostgresRules[PostgreSQL Rules]
RedisRules[Redis Rules]
end
PromPVC[(Prometheus PVC)]
GrafanaPVC[(Grafana PVC)]
end
subgraph targets [Scrape Targets]
NodeExporter[Node Exporter]
KubeStateMetrics[kube-state-metrics]
MinIO[MinIO Metrics]
ServiceMonitors[ServiceMonitors]
end
subgraph logging [Logging Module]
Elasticsearch[(Elasticsearch)]
end
end
Preflight -->|must pass| KubernetesModule
KubernetesModule -->|creates| CRDRelease
KubernetesModule -->|dependency| NginxModule
NginxModule -->|dependency| CertManagerModule
CertManagerModule -->|dependency| LoggingModule
CertManagerModule -->|dependency| MonitoringModule
LoggingModule -->|dependency| MonitoringModule
MonitoringModule -->|creates| RuleResources
MonitoringModule -->|renders| StackValues
MonitoringModule -->|creates| StackRelease
StackValues -->|configures| StackRelease
CRDRelease -->|installs and owns| PrometheusCRDs
PrometheusCRDs -->|schemas available before install| StackRelease
RuleResources -->|creates| LonghornRules
RuleResources -->|creates| OmniRouteRules
RuleResources -->|creates| PostgresRules
RuleResources -->|creates| RedisRules
StackRelease -->|deploys| Prometheus
StackRelease -->|deploys| Grafana
StackRelease -->|deploys| AlertManager
StackRelease -->|deploys| NodeExporter
StackRelease -->|deploys| KubeStateMetrics
User --> Nginx
Nginx --> OAuth
OAuth --> Grafana
OAuth --> Prometheus
OAuth --> AlertManager
Prometheus -->|scrape| targets
Prometheus --> PromPVC
Prometheus -->|evaluate| rules
rules -->|fire alerts| AlertManager
AlertManager -->|notify| Slack
ElastAlert -->|query| Elasticsearch
ElastAlert -->|alert| Slack
Grafana --> GrafanaPVC
Grafana -->|query| Prometheus
Alert Flow¶
sequenceDiagram
participant Prometheus
participant Rules as PrometheusRules
participant AM as AlertManager
participant Slack
Prometheus->>Prometheus: Scrape metrics
Prometheus->>Rules: Evaluate rules
Rules-->>Prometheus: Alert firing
Prometheus->>AM: Send alert
AM->>AM: Group/Deduplicate
AM->>Slack: Send notification
CRD Ownership¶
module.kubernetes.helm_release.prometheus_operator_crds is the only Terraform resource that installs and owns Prometheus Operator CRDs. The preflight gate verifies that its chart and this module's kube-prometheus-stack chart package the same Prometheus Operator version before Terraform can begin the cluster dependency chain.
The stack values set crds.enabled=false. Do not enable it while the standalone release exists, because that would create two Helm releases claiming responsibility for the same cluster-scoped objects.
Resources Created¶
kubernetes_namespace_v1.monitoring_namespace- Dedicated namespacekubernetes_secret_v1.frontend_basic_auth- Basic auth for UIsrandom_password.grafana_admin_password- Grafana admin passwordkubectl_manifest.prometheus_rules- Custom PrometheusRuleshelm_release.prometheus_operator- kube-prometheus-stackkubernetes_secret_v1.elastalert2_credentials- ElastAlert2 credentialskubernetes_secret_v1.elastalert2_config- ElastAlert2 configurationhelm_release.elastalert2- ElastAlert2 chart
Variables¶
Prometheus Stack¶
| Name | Description | Default |
|---|---|---|
nginx_frontend_basic_auth_base64 |
Basic auth credentials | (required, sensitive) |
prometheus_alertmanager_domain |
AlertManager hostname | alertmanager.chrislee.local |
prometheus_grafana_domain |
Grafana hostname | grafana.chrislee.local |
prometheus_grafana_storage_class |
Grafana storage class | longhorn |
prometheus_ingress_class_name |
Ingress class | nginx |
prometheus_ingress_enable_tls |
Enable TLS | true |
prometheus_prometheus_domain |
Prometheus hostname | prometheus.chrislee.local |
prometheus_persistence_storage_class_name |
Storage class | longhorn |
prometheus_persistence_size |
Prometheus storage size | 10Gi |
prometheus_alertmanager_slack_channel |
Slack channel for alerts | (required) |
prometheus_alertmanager_slack_credentials |
Slack bot token (xoxb-) with the chat:write scope |
(required, sensitive) |
prometheus_minio_job_bearer_token |
MinIO metrics token | (required, sensitive) |
ElastAlert2¶
| Name | Description | Default |
|---|---|---|
elastalert2_elasticsearch_enabled |
Enable ElastAlert2 | true |
elastalert2_elasticsearch_host |
Elasticsearch host | (from logging module) |
elastalert2_elasticsearch_port |
Elasticsearch port | (from logging module) |
elastalert2_elasticsearch_username |
Elasticsearch username | (from logging module) |
elastalert2_elasticsearch_password |
Elasticsearch password | (from logging module, sensitive) |
Usage¶
Configure Slack Alerts¶
Alertmanager posts through the Slack Web API (chat.postMessage), not an incoming webhook, so the credential is a bot token and the bot has to be a member of the target channel. Give the channel name without a leading #, the template adds it.
TF_VAR_prometheus_alertmanager_slack_channel="alerts"
TF_VAR_prometheus_alertmanager_slack_credentials="xoxb-..."
Each alert group keeps one Slack message. The first notification posts it, and every later notification for the same group rewrites it via chat.update, so the resolve and the 12h repeat both land as edits rather than new messages. An edit does not ping. Two gaps follow and are accepted deliberately: a second instance of the same alert in the same namespace joins the group and arrives as a silent edit, and an alert that resolves and re-fires while the message reference is still held is edited back to red without pinging. alertname is in the group key so that different rules at least never collide this way. Alertmanager holds the reference in its notification log for twice the repeat interval, on an emptyDir volume that is lost when the pod is replaced, and either expiry starts a fresh message. Deleting an alert message in Slack stops that group notifying until the reference expires, because the failed edit is not retried and never falls back to posting.
Access Dashboards¶
| Service | URL | Authentication |
|---|---|---|
| Grafana | https://grafana.chrislee.local | OAuth2 + Admin password |
| Prometheus | https://prometheus.chrislee.local | OAuth2 |
| AlertManager | https://alertmanager.chrislee.local | OAuth2 |
Get Grafana Admin Password¶
kubectl -n monitoring get secret kube-prometheus-stack-grafana -o jsonpath="{.data.admin-password}" | base64 -d
Helm Charts¶
| Component | Repository | Chart |
|---|---|---|
| Prometheus Stack | prometheus-community | kube-prometheus-stack |
| ElastAlert2 | jertel | elastalert2 |
PrometheusRules¶
Custom alerting rules are defined in prometheus-rules/:
| File | Purpose |
|---|---|
longhorn-rules.tftpl |
Longhorn storage alerts |
omniroute-rules.tftpl |
OmniRoute AI gateway alerts |
postgres-rules.tftpl |
PostgreSQL database alerts |
redis-rules.tftpl |
Redis cache alerts |
Pre-configured Dashboards¶
Grafana includes dashboards for:
- Kubernetes cluster overview
- Node metrics
- Pod resources
- Persistent volumes
- NGINX Ingress
- CoreDNS
- GitLab (if enabled)