Exercises
These exercises require the Grafana Operator, which a cluster administrator installs as described in Setup Requirements. The panels based on the sample application use the application, the ServiceMonitor and the PrometheusRule from the Module 5 exercises. Run the commands in your workshop project:
NS=$(oc project -q)
Exercise 1: Deploy Grafana
In this exercise, you will create a Grafana instance in your project and log in to it.
Step 1: Check Your Permissions
oc auth can-i create grafanas.grafana.integreatly.org -n "$NS"
If the command prints no, the Grafana Operator is not installed, or you are not an administrator or editor of the project. Ask your cluster administrator.
Step 2: Create the Grafana Instance
oc apply \
-n "$NS" \
-f - <<'EOF'
apiVersion: grafana.integreatly.org/v1beta1
kind: Grafana
metadata:
name: grafana
labels:
dashboards: grafana
spec:
route:
spec:
tls:
termination: edge
insecureEdgeTerminationPolicy: Redirect
config:
log:
mode: console
EOF
Wait until the operator has created the deployment and Grafana is running:
oc get grafana grafana -n "$NS"
oc get pods \
-n "$NS" \
-l app=grafana
The STAGE STATUS column of the Grafana resource shows success, and the Grafana pod is Running.
While Grafana is starting, the resource can show failed for a minute or two, because the operator cannot reach Grafana yet; check again after a few minutes.
Step 3: Log In
echo "https://$(oc get route grafana-route \
-n "$NS" \
-o jsonpath='{.spec.host}')"
oc extract secret/grafana-admin-credentials \
-n "$NS" \
--to=-
Open the URL in your browser and log in with the values of GF_SECURITY_ADMIN_USER and GF_SECURITY_ADMIN_PASSWORD. Grafana has no data sources and no dashboards yet.
Exercise 2: Connect Grafana to the Metrics of Your Project
Practice configuring a data source for the Thanos Querier tenancy port.
Step 1: Create the Service Account and Its Token
oc create serviceaccount grafana-thanos -n "$NS"
oc create rolebinding grafana-thanos-view \
-n "$NS" \
--clusterrole=view \
--serviceaccount="$NS:grafana-thanos"
oc apply \
-n "$NS" \
-f - <<'EOF'
apiVersion: v1
kind: Secret
metadata:
name: grafana-thanos-token
annotations:
kubernetes.io/service-account.name: grafana-thanos
type: kubernetes.io/service-account-token
EOF
Check that OpenShift has filled the secret. The command lists the keys ca.crt, namespace, service-ca.crt and token:
oc describe secret grafana-thanos-token -n "$NS"
Step 2: Test the Token
Before you configure Grafana, check that the token can query the tenancy port. Run the query from the api-client pod of Module 1 (recreate it as shown there if it no longer exists):
SA_TOKEN=$(oc get secret grafana-thanos-token \
-n "$NS" \
-o jsonpath='{.data.token}' |
base64 -d)
oc exec -n "$NS" api-client -- curl -sk -G \
-H "Authorization: Bearer $SA_TOKEN" \
--data-urlencode "namespace=$NS" \
--data-urlencode 'query=up' \
https://thanos-querier.openshift-monitoring.svc:9092/api/v1/query
The response has "status":"success". If it is Forbidden, check the role binding from Step 1.
Step 3: Create the Data Source
oc apply \
-n "$NS" \
-f - <<'EOF'
apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDatasource
metadata:
name: thanos-querier
spec:
instanceSelector:
matchLabels:
dashboards: grafana
valuesFrom:
- targetPath: secureJsonData.httpHeaderValue1
valueFrom:
secretKeyRef:
name: grafana-thanos-token
key: token
- targetPath: jsonData.customQueryParameters
valueFrom:
secretKeyRef:
name: grafana-thanos-token
key: namespace
datasource:
name: thanos-querier
uid: thanos-querier
type: prometheus
access: proxy
url: https://thanos-querier.openshift-monitoring.svc.cluster.local:9092
isDefault: true
jsonData:
httpMethod: GET
customQueryParameters: namespace=${namespace}
httpHeaderName1: Authorization
tlsSkipVerify: true
timeInterval: 30s
secureJsonData:
httpHeaderValue1: Bearer ${token}
EOF
Step 4: Test the Data Source
In Grafana, navigate to Connections → Data sources and open thanos-querier. Check that the HTTP method is GET and that the custom query parameters show namespace= followed by your project name, then click Save & test (or Test). Grafana reports that it successfully queried the Prometheus API.
Then open Explore, select the thanos-querier data source, and run:
sum by (pod) (container_memory_working_set_bytes{
container!="",
container!="POD"
})
The result contains only the pods of your project, although the query has no namespace matcher.
Step 5: See Why GET Matters (Optional)
Change the HTTP method of the data source to POST. A merge patch changes only the fields it contains and keeps the other settings of jsonData:
oc patch grafanadatasource thanos-querier \
-n "$NS" \
--type=merge \
-p '{"spec":{"datasource":{"jsonData":{"httpMethod":"POST"}}}}'
After the operator has updated the data source, run the query in Explore again. It fails with an authorization error (403 Forbidden), because the tenancy port checks a POST request against the create verb, which the view role does not grant. Change the method back to GET before you continue:
oc patch grafanadatasource thanos-querier \
-n "$NS" \
--type=merge \
-p '{"spec":{"datasource":{"jsonData":{"httpMethod":"GET"}}}}'
Exercise 3: Create a Dashboard
Practice defining a dashboard as a Kubernetes resource.
Step 1: Create the Dashboard
oc apply \
-n "$NS" \
-f - <<'EOF'
apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDashboard
metadata:
name: prometheus-example-app
spec:
instanceSelector:
matchLabels:
dashboards: grafana
datasources:
- inputName: DS_PROMETHEUS
datasourceName: thanos-querier
json: |
{
"title": "Prometheus Example App",
"uid": "prometheus-example-app",
"schemaVersion": 39,
"time": {"from": "now-1h", "to": "now"},
"refresh": "30s",
"panels": [
{
"id": 1,
"type": "timeseries",
"title": "Request rate by status code",
"gridPos": {"h": 8, "w": 12, "x": 0, "y": 0},
"datasource": {"type": "prometheus", "uid": "${DS_PROMETHEUS}"},
"fieldConfig": {"defaults": {"unit": "reqps"}, "overrides": []},
"targets": [
{
"refId": "A",
"datasource": {"type": "prometheus", "uid": "${DS_PROMETHEUS}"},
"expr": "sum by (code) (rate(http_requests_total{job=\"prometheus-example-app\"}[5m]))",
"legendFormat": "{{code}}"
}
]
},
{
"id": 2,
"type": "timeseries",
"title": "Error ratio (5xx)",
"gridPos": {"h": 8, "w": 12, "x": 12, "y": 0},
"datasource": {"type": "prometheus", "uid": "${DS_PROMETHEUS}"},
"fieldConfig": {"defaults": {"unit": "percentunit", "min": 0}, "overrides": []},
"targets": [
{
"refId": "A",
"datasource": {"type": "prometheus", "uid": "${DS_PROMETHEUS}"},
"expr": "namespace_job:http_requests_errors:ratio_rate5m{job=\"prometheus-example-app\"}",
"legendFormat": "{{job}}"
}
]
},
{
"id": 3,
"type": "timeseries",
"title": "Memory working set by pod",
"gridPos": {"h": 8, "w": 12, "x": 0, "y": 8},
"datasource": {"type": "prometheus", "uid": "${DS_PROMETHEUS}"},
"fieldConfig": {"defaults": {"unit": "bytes"}, "overrides": []},
"targets": [
{
"refId": "A",
"datasource": {"type": "prometheus", "uid": "${DS_PROMETHEUS}"},
"expr": "sum by (pod) (container_memory_working_set_bytes{container!=\"\", container!=\"POD\"})",
"legendFormat": "{{pod}}"
}
]
},
{
"id": 4,
"type": "timeseries",
"title": "CPU usage by pod (cores)",
"gridPos": {"h": 8, "w": 12, "x": 12, "y": 8},
"datasource": {"type": "prometheus", "uid": "${DS_PROMETHEUS}"},
"fieldConfig": {"defaults": {"unit": "short"}, "overrides": []},
"targets": [
{
"refId": "A",
"datasource": {"type": "prometheus", "uid": "${DS_PROMETHEUS}"},
"expr": "sum by (pod) (rate(container_cpu_usage_seconds_total{container!=\"\", container!=\"POD\"}[5m]))",
"legendFormat": "{{pod}}"
}
]
}
]
}
EOF
Step 2: Open the Dashboard
In Grafana, navigate to Dashboards and open Prometheus Example App. If the request rate and error ratio panels show no data, start the traffic loops from the Module 5 exercises again.
Step 3: Add a Panel
Add a fifth panel that shows the number of firing alerts of your project. Edit the GrafanaDashboard resource with:
oc edit grafanadashboard prometheus-example-app -n "$NS"
Add this object to the panels array (add a comma after the previous panel):
{
"id": 5,
"type": "stat",
"title": "Firing alerts",
"gridPos": {"h": 4, "w": 6, "x": 0, "y": 16},
"datasource": {"type": "prometheus", "uid": "${DS_PROMETHEUS}"},
"targets": [
{
"refId": "A",
"datasource": {"type": "prometheus", "uid": "${DS_PROMETHEUS}"},
"expr": "count(ALERTS{alertstate=\"firing\"}) or vector(0)"
}
]
}
The query is:
count(ALERTS{alertstate="firing"}) or vector(0)
count() of an empty result is empty, not 0, so or vector(0) returns 0 when no alert is firing. After the operator has synchronized the resource, reload the dashboard in Grafana and check the new panel. Generate errors as in Module 5, Exercise 3, and watch the value change when ExampleAppHighErrorRate fires.
Step 4: Try a Change in the Grafana UI
In Grafana, change the title of a panel and save the dashboard. Then wait for the operator’s next synchronization (up to 10 minutes, its default resyncPeriod), or change the GrafanaDashboard resource, and reload the dashboard. Which title does the panel have now, and why?
Exercise 4: Clean Up
When you have finished the workshop, delete the resources you created in Modules 5 and 6:
oc delete grafanadashboard prometheus-example-app -n "$NS"
oc delete grafanadatasource thanos-querier -n "$NS"
oc delete grafana grafana -n "$NS"
oc delete secret grafana-thanos-token -n "$NS"
oc delete rolebinding grafana-thanos-view -n "$NS"
oc delete serviceaccount grafana-thanos -n "$NS"
oc delete prometheusrule prometheus-example-app -n "$NS"
oc delete servicemonitor prometheus-example-app -n "$NS"
oc delete route,service,deployment prometheus-example-app -n "$NS"
oc delete pod api-client -n "$NS"
Deleting the secret also revokes the token of the Grafana service account.
Verification
After completing these exercises, verify your understanding:
-
Can you explain why Grafana needs the
namespaceparameter, a bearer token andGETrequests to query the Thanos Querier tenancy port? -
Can you create a Grafana instance, a data source and a dashboard as Kubernetes resources?
-
Can you add a panel with a PromQL query to a dashboard?
-
Can you explain what happens to changes made in the Grafana UI to a dashboard that the operator manages?
If you can answer yes to all these questions, you have successfully completed the Prometheus Query Workshop! You are now equipped to write effective PromQL queries, alert on them and visualize them for your applications on OpenShift.