Amazon EKS — Prometheus and Grafana
Prometheus is the de-facto standard for Kubernetes metrics. Grafana provides dashboards. Together they give you deep visibility into cluster and application performance — free and open-source.
kube-prometheus-stack
The kube-prometheus-stack Helm chart installs everything in one shot:
- Prometheus server
- Alertmanager
- Grafana
- Node exporter
- kube-state-metrics
- Pre-built dashboards and alerts
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm install kube-prometheus prometheus-community/kube-prometheus-stack \
--namespace monitoring --create-namespace \
--set grafana.adminPassword="admin123" \
--set prometheus.prometheusSpec.storageSpec.volumeClaimTemplate.spec.storageClassName=gp3 \
--set prometheus.prometheusSpec.storageSpec.volumeClaimTemplate.spec.resources.requests.storage=50Gi \
--set prometheus.prometheusSpec.retention=15d
Verify
kubectl get pods -n monitoring
kubectl get svc -n monitoring
Access Grafana
kubectl port-forward svc/kube-prometheus-grafana 3000:80 -n monitoring
# Open http://localhost:3000 — admin / admin123
ServiceMonitor and PodMonitor
Prometheus discovers scrape targets via CRDs:
ServiceMonitor
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: my-app
namespace: monitoring
labels:
release: kube-prometheus
spec:
namespaceSelector:
matchNames:
- production
selector:
matchLabels:
app: my-app
endpoints:
- port: metrics
path: /metrics
interval: 30s
PodMonitor
apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
name: my-app-pods
namespace: monitoring
labels:
release: kube-prometheus
spec:
namespaceSelector:
matchNames:
- production
selector:
matchLabels:
app: my-app
podMetricsEndpoints:
- port: metrics
path: /metrics
interval: 30s
Key: The
labels.releasemust match the Helm release name — otherwise Prometheus won’t pick up the monitor.
Grafana Dashboards for EKS
Import these community dashboards:
| Dashboard ID | Name | Description |
|---|---|---|
| 315 | Kubernetes cluster monitoring | Cluster-wide view |
| 6417 | Kubernetes Pods | Pod-level CPU, memory, network |
| 13770 | Node Exporter Full | Detailed node metrics |
| 14584 | CoreDNS | DNS query performance |
| 15760 | Kubernetes / Networking | Network traffic per namespace |
Import via Grafana UI: Dashboards → Import → Enter ID.
Alertmanager
Configure alerts for EKS-specific scenarios:
PrometheusRule
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: eks-alerts
namespace: monitoring
labels:
release: kube-prometheus
spec:
groups:
- name: eks.rules
rules:
- alert: PodCrashLooping
expr: rate(kube_pod_container_status_restarts_total[15m]) * 60 * 5 > 0
for: 15m
labels:
severity: warning
annotations:
summary: "Pod {{ $labels.namespace }}/{{ $labels.pod }} is crash looping"
- alert: HighMemoryUsage
expr: container_memory_working_set_bytes / container_spec_memory_limit_bytes > 0.9
for: 10m
labels:
severity: critical
annotations:
summary: "Container {{ $labels.container }} in {{ $labels.pod }} at 90%+ memory"
- alert: NodeNotReady
expr: kube_node_status_condition{condition="Ready",status="true"} == 0
for: 5m
labels:
severity: critical
annotations:
summary: "Node {{ $labels.node }} is NotReady"
Alertmanager Config (Slack)
# values.yaml for Helm
alertmanager:
config:
route:
receiver: 'slack'
group_by: ['alertname', 'namespace']
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
receivers:
- name: 'slack'
slack_configs:
- api_url: 'https://hooks.slack.com/services/T00000/B00000/XXXX'
channel: '#eks-alerts'
title: '{{ .GroupLabels.alertname }}'
text: '{{ .CommonAnnotations.summary }}'
Storage for Prometheus
EBS (Default)
Good for single-cluster, short-term retention:
prometheus:
prometheusSpec:
storageSpec:
volumeClaimTemplate:
spec:
storageClassName: gp3
resources:
requests:
storage: 100Gi
retention: 30d
Thanos for Long-Term Storage
For multi-cluster or long-term retention, add Thanos sidecar:
prometheus:
prometheusSpec:
thanos:
objectStorageConfig:
existingSecret:
name: thanos-objstore
key: objstore.yml
Thanos uploads blocks to S3 for unlimited retention at low cost.
Useful PromQL Queries
# Cluster CPU utilization
sum(rate(container_cpu_usage_seconds_total{container!=""}[5m])) / sum(machine_cpu_cores) * 100
# Memory utilization by namespace
sum(container_memory_working_set_bytes{container!=""}) by (namespace) / 1024 / 1024 / 1024
# Pod restart rate (last hour)
sum(increase(kube_pod_container_status_restarts_total[1h])) by (namespace, pod)
# Top 10 CPU-consuming pods
topk(10, sum(rate(container_cpu_usage_seconds_total{container!=""}[5m])) by (namespace, pod))
# Persistent volume usage
kubelet_volume_stats_used_bytes / kubelet_volume_stats_capacity_bytes * 100
Best Practices
- Use persistent storage for Prometheus —
emptyDirloses data on pod restart - Set retention period based on needs — 15-30 days for most; use Thanos for longer
- Use ServiceMonitor/PodMonitor — don’t manually configure scrape targets
- Import community dashboards — don’t build from scratch
- Configure alerting from day one — Prometheus without alerts is just a metrics database
- Right-size Prometheus resources — large clusters need significant CPU/memory for Prometheus