Monitoring and observability are foundational pillars of system operations. Grafana is an industry-standard, open-source visualization and analytics platform that allows you to query, visualize, alert on, and understand metrics no matter where they are stored.
Deploying Grafana to a Kubernetes cluster involves running a Pod running the Grafana container image and exposing it externally using a NodePort Service so administrators can access the web dashboard interface.
graph TD
User[Administrator / Browser] -->|Accesses NodeIP:32000| Node[Kubernetes Node]
Node -->|kube-proxy routes to Service| Service[Service: grafana-service <br> ClusterIP Port: 3000]
Service -->|Forwards traffic to| Pod[Pod: grafana-deployment-datacenter <br> Container Port: 3000]
When running applications like Grafana inside Kubernetes, several architectural concerns must be considered:
By default, Grafana stores user configurations, dashboards, data sources, and session details inside an internal SQLite database (/var/lib/grafana/grafana.db). Because container filesystems are ephemeral, restarting the Pod would result in the loss of all dashboards.
/var/lib/grafana. Alternatively, configure Grafana to use an external PostgreSQL or MySQL database.To scale Grafana to multiple replicas for high availability (HA):
thordefaultgrafana-deployment-datacentergrafana-containergrafana/grafana:latest13000grafana-serviceNodePort3200030003000SSH from your terminal into the controller command host:
ssh thor@jump_host_ip
Create a unified YAML file named grafana.yaml declaring both the Deployment and Service. Using a single file separated by --- simplifies resource lifecycle management:
apiVersion: apps/v1
kind: Deployment
metadata:
name: grafana-deployment-datacenter
labels:
app: grafana
spec:
replicas: 1
selector:
matchLabels:
app: grafana
template:
metadata:
labels:
app: grafana
spec:
containers:
- name: grafana-container
image: grafana/grafana:latest
ports:
- containerPort: 3000
---
apiVersion: v1
kind: Service
metadata:
name: grafana-service
spec:
type: NodePort
selector:
app: grafana
ports:
- port: 3000
targetPort: 3000
nodePort: 32000
protocol: TCP
Apply the configuration manifest:
kubectl apply -f grafana.yaml
Expected Output:
deployment.apps/grafana-deployment-datacenter created
service/grafana-service created
Verify the Grafana Pod successfully initializes and transitions into the Running state:
kubectl get pods -w -l app=grafana
Expected Output:
NAME READY STATUS RESTARTS AGE
grafana-deployment-datacenter-5f8a9b0c-abcde 0/1 ContainerCreating 0 2s
grafana-deployment-datacenter-5f8a9b0c-abcde 1/1 Running 0 10s
Verify deployment scale:
kubectl get deployment grafana-deployment-datacenter
Expected Output:
NAME READY UP-TO-DATE AVAILABLE AGE
grafana-deployment-datacenter 1/1 1 1 30s
Verify service mapping:
kubectl get service grafana-service
Expected Output:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
grafana-service NodePort 10.96.195.101 <none> 3000:32000/TCP 35s
Send an HTTP request from outside the cluster network (e.g., from the jump host) targeting one of the worker nodes’ IP addresses on NodePort 32000 to confirm that Grafana’s web server responds:
curl -L -I http://<NODE_IP>:32000
Expected Output showing redirect/access to login page:
HTTP/1.1 302 Found
Cache-Control: no-cache
Content-Type: text/html; charset=utf-8
Location: /login
...
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
...
The Grafana instance is now successfully deployed and reachable on static NodePort 32000!