Skip to content

Commit 54dda5f

Browse files
committed
Add KubeAI dashboard configMap sources + conversion script
Signed-off-by: Eero Tamminen <eero.t.tamminen@intel.com>
1 parent 278886c commit 54dda5f

4 files changed

Lines changed: 2784 additions & 0 deletions

File tree

kubeai/grafana/README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Files
2+
3+
## Dashboards
4+
5+
- `vllm-scaling.*`: Cluster overview of how much and well vLLM is scaling
6+
- `vllm-details.*`: More detailed per-model and/or per-instance vLLM metrics
7+
8+
## File types
9+
10+
- `*.yaml`: Grafana dashboard configMaps that Grafana will load automatically
11+
- `*.json`: Grafana Dashboard specs from which configMaps are generated
12+
- `*.png`: Screenshots of those dashboards (updated manually)
13+
14+
## Other files
15+
16+
- `convert-dashboard.sh`: convert dashboard `*.json` file to configMap `*.yaml` file
17+
- `README.md`: this file
18+
19+
## Dashboard formats
20+
21+
Dashboard JSON files need to be loaded from the Grafana GUI manually,
22+
their changes can be saved and Grafana maintains update history for
23+
them, but those are lost if Grafana is uninstalled.
24+
25+
Whereas Grafana will automatically load suitably labeled dashboard
26+
configMaps, they persist even if Grafana gets re-installed, and one
27+
can save their updates as JSON files.
28+
29+
Update process for the dashboards is following:
30+
31+
- Apply configMap to K8s so it's visible in Grafana dashboards list
32+
- Update dashboard in Grafana
33+
- Save it as JSON
34+
- Convert JSON file to configMap YAML: `./convert-dashboard.sh *.json`
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
#!/bin/sh
2+
#
3+
# Copyright (C) 2025 Intel Corporation
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
set -e
7+
8+
# OPEA CI requires copyright/license for the converted file
9+
COPYRIGHT="Copyright (C) 2025 Intel Corporation"
10+
LICENSE="SPDX-License-Identifier: Apache-2.0"
11+
12+
# Label needed in configMap to get (Helm installed) Grafana to load it as dashboard
13+
LABEL="grafana_dashboard=1"
14+
15+
PREFIX="opea-"
16+
NS=""
17+
18+
error_exit ()
19+
{
20+
name=${0##*/}
21+
cat << EOF
22+
23+
ERROR: $1!
24+
25+
Convert given Grafana *.json dashboard specs to Grafana (Helm install) *.yaml configMaps.
26+
27+
Usage:
28+
$name <JSON files>
29+
30+
Example:
31+
$name vllm.json
32+
kubectl apply -n monitoring vllm.yaml
33+
34+
=> Creates 'vllm.yaml' configMap named as '${PREFIX}vllm' for Grafana in 'monitoring' namespace.
35+
36+
ERROR: $1!
37+
EOF
38+
exit 1
39+
}
40+
41+
if [ -z "$(which jq)" ]; then
42+
error_exit "'jq' required for dashboard checks, please install 'jq' first"
43+
fi
44+
45+
if [ -z "$(which kubectl)" ]; then
46+
error_exit "'kubectl' required for dashboard conversion, please install 'kubernetes-client' first"
47+
fi
48+
49+
if ! kubectl version; then
50+
error_exit "Broken/missing 'kubectl' cluster config (script does not need it, but kubectl still fails)"
51+
fi
52+
53+
echo
54+
echo "Got following Grafana dashboards:"
55+
for file in "$@"; do
56+
if [ ! -f "$file" ]; then
57+
error_exit "JSON file '$file' does not exist"
58+
fi
59+
if [ "${file%.json}" = "$file" ]; then
60+
error_exit "JSON file '$file' does not exist"
61+
fi
62+
63+
# Dashboard 'uid' is optional as Grafana can generate one...
64+
uid=$(jq .uid "$file" | tail -1 | tr -d '"')
65+
if [ -z "$uid" ]; then
66+
error_exit "'$file' dashboard has invalid JSON"
67+
elif [ "$uid" = "null" ]; then
68+
echo "WARNING: no dashboard 'uid', Grafana will assign new one on every load: $file"
69+
elif echo "$uid" | grep -q -v '^[-0-9a-f]*$'; then
70+
echo "DEBUG: dashboard 'uid' not in hex format: '$uid'?"
71+
fi
72+
73+
# ...but it should have a title.
74+
title=$(jq .title "$file" | tail -1 | tr -d '"')
75+
if [ "$title" = "null" ]; then
76+
error_exit "'$file' dashboard has no 'title' field"
77+
fi
78+
79+
echo "- file: $file, uid: '$uid', title: '$title'"
80+
done
81+
82+
echo
83+
echo "Converting:"
84+
for file in "$@"; do
85+
base=${file##*/}
86+
name=${base%.json}
87+
dst="${name}.yaml"
88+
89+
# if no prefix, add one
90+
if [ "${name#"$PREFIX"}" = "$name" ]; then
91+
name="${PREFIX}${name}"
92+
fi
93+
94+
# convert to k8s object name ("[a-z0-9][-a-z0-9]*[a-z0-9]"):
95+
# - upper-case -> lowercase, '_' -> '-'
96+
# - drop anything outside [-a-z]
97+
# - drop '-' prefix & suffix and successive '-' chars
98+
k8name=$(echo "$name" | tr A-Z_ a-z- | tr -d -c a-z- | sed -e 's/^-*//' -e 's/-*$//' -e 's/--*/-/g')
99+
100+
echo "- file: $dst, configMap: $k8name, title: $(jq .title "$file" | tail -1)"
101+
102+
echo "# $COPYRIGHT" > "$dst"
103+
echo "# $LICENSE" >> "$dst"
104+
echo "#" >> "$dst"
105+
echo "# ${0##*/}: $base -> $dst" >> "$dst"
106+
107+
kubectl create cm -n "$NS" --from-file "$file" --dry-run=client -o yaml "$k8name" |\
108+
kubectl label -f- --local --dry-run=client -o yaml "$LABEL" |\
109+
grep -v -e "^ creationTimestamp:" >> "$dst"
110+
done
111+
112+
echo
113+
echo "DONE!"

0 commit comments

Comments
 (0)