Die Herausforderung besteht daraus, ein Standalone Grafana in einem OpenShift 4.5 Cluster zu deployen, welches einen OpenShift Login hat und auf den internen Prometheus des Cluster Monitorings zugreift. Somit wird es möglich, das vorhanden Cluster Monitoring um eigene Dashboards zu erweitern.
Eine weitere Herausforderung, die sich mir stellte war, dass ich nicht nur den Grafana Operator nicht verwenden konnte, sondern auch die von Red Hat bereitgestellten Images verwenden musste. Damit war das Bitnami Helm Chart raus ;-).
Nach einigen Versuchen bin ich dazu übergegangen, die Grafana Komponente aus dem "openshift-monitoring" Projekt zu kopieren. Dabei bin ich über ein paar Besonderheiten gestolpert, die ich hier erläutere:
- Der ServiceAccount "grafana" wird als oauth Client verwendet
- Wie generiere ich ein session secret für den outh-proxy? -> Auflösung unten
- Und das Secret "grafana-tls" ist ein generiertes Secret, welches direkt über den Service "grafana" erzeugt wird.
- Ach ja und die spezielle ConfigMap "grafana-trusted-ca-bundle"
Wie habe ich nun das Projekt aufgebaut?
(Alle Dateien werden mit oc apply -f <datei.yaml> an das Projekt übergeben)
oc new-project grafana -> Erzeugt ein neues Projekt
ServiceAccount "grafana"
apiVersion: v1
metadata:
annotations:
serviceaccounts.openshift.io/oauth-redirectreference.grafana: '{"kind":"OAuthRedirectReference","apiVersion":"v1","reference":{"kind":"Route","name":"grafana"}}'
name: grafana
namespace: grafana
ConfigMap "grafana-trusted-ca-bundle"
apiVersion: v1
kind: ConfigMap
metadata:
labels:
config.openshift.io/inject-trusted-cabundle: "true"
name: grafana-trusted-ca-bundle
namespace: grafana
Service "grafana", der über die Annotaion auch das Secret "grafana-tls" erzeugt
apiVersion: v1
kind: Service
metadata:
annotations:
service.alpha.openshift.io/serving-cert-secret-name: grafana-tls
labels:
app: grafana
name: grafana
spec:
ports:
- name: https
port: 3000
protocol: TCP
targetPort: https
selector:
app: grafana
type: ClusterIP
Session Secret für den oauth Proxy erzeugen
oc create secret generic grafana-proxy --from-literal=session_secret=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c43)
Datei "grafana.ini" erzeugen, aus der dann das Secret "grafana-config" erzeugt wird
[analytics]
check_for_updates = false
reporting_enabled = false
[auth]
disable_login_form = true
disable_signout_menu = true
[auth.basic]
enabled = false
[auth.proxy]
auto_sign_up = true
enabled = true
header_name = X-Forwarded-User
[paths]
data = /var/lib/grafana
logs = /var/lib/grafana/logs
plugins = /var/lib/grafana/plugins
provisioning = /etc/grafana/provisioning
[security]
admin_user = admin
admin_password = password
cookie_secure = true
[server]
http_addr = 127.0.0.1
http_port = 3001
oc create secret generic grafana-config --from-file=grafana.ini
Und nun das Herzstück: Das eigentliche Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: grafana
name: grafana
spec:
replicas: 1
selector:
matchLabels:
app: grafana
template:
metadata:
creationTimestamp: null
labels:
app: grafana
spec:
restartPolicy: Always
schedulerName: default-scheduler
terminationGracePeriodSeconds: 30
securityContext: {}
serviceAccount: grafana
containers:
- resources:
requests:
cpu: 4m
memory: 100Mi
terminationMessagePath: /dev/termination-log
name: grafana
ports:
- name: http
containerPort: 3001
protocol: TCP
imagePullPolicy: IfNotPresent
volumeMounts:
- name: grafana-storage
mountPath: /var/lib/grafana
- name: grafana-log
mountPath: /var/log/grafana
# - name: grafana-datasources
# mountPath: /etc/grafana/provisioning/datasources
# - name: grafana-dashboards
# mountPath: /etc/grafana/provisioning/dashboards
# - name: grafana-dashboard-cluster-overview
# mountPath: /grafana-dashboard-definitions/0/cluster-overview
- name: grafana-config
mountPath: /etc/grafana
terminationMessagePolicy: File
image: registry.redhat.io/openshift4/ose-grafana
args:
- '-config=/etc/grafana/grafana.ini'
- resources:
requests:
cpu: 1m
memory: 20Mi
readinessProbe:
httpGet:
path: /oauth/healthz
port: https
scheme: HTTPS
timeoutSeconds: 1
periodSeconds: 10
successThreshold: 1
failureThreshold: 3
terminationMessagePath: /dev/termination-log
name: grafana-proxy
env:
- name: HTTP_PROXY
- name: HTTPS_PROXY
- name: NO_PROXY
ports:
- name: https
containerPort: 3000
protocol: TCP
imagePullPolicy: IfNotPresent
volumeMounts:
- name: secret-grafana-tls
mountPath: /etc/tls/private
- name: secret-grafana-proxy
mountPath: /etc/proxy/secrets
- name: grafana-trusted-ca-bundle
readOnly: true
mountPath: /etc/pki/ca-trust/extracted/pem/
terminationMessagePolicy: File
image: registry.redhat.io/openshift4/ose-oauth-proxy
args:
- '-provider=openshift'
- '-https-address=:3000'
- '-http-address='
- '-email-domain=*'
- '-upstream=http://localhost:3001'
- '-openshift-sar={"resource": "namespaces", "verb": "get"}'
- >-
-openshift-delegate-urls={"/": {"resource": "namespaces", "verb":
"get"}}
- '-tls-cert=/etc/tls/private/tls.crt'
- '-tls-key=/etc/tls/private/tls.key'
- >-
-client-secret-file=/var/run/secrets/kubernetes.io/serviceaccount/token
- '-cookie-secret-file=/etc/proxy/secrets/session_secret'
- '-openshift-service-account=grafana'
- '-openshift-ca=/etc/pki/tls/cert.pem'
- '-openshift-ca=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt'
- '-skip-auth-regex=^/metrics'
serviceAccount: grafana
volumes:
- name: grafana-storage
emptyDir: {}
- name: grafana-log
emptyDir: {}
# - name: grafana-datasources
# secret:
# secretName: grafana-datasources
# defaultMode: 420
# - name: grafana-dashboards
# configMap:
# name: grafana-dashboards
# defaultMode: 420
# - name: grafana-dashboard-cluster-overview
# configMap:
# name: grafana-dashboard-cluster-overview
# defaultMode: 420
- name: grafana-config
secret:
secretName: grafana-config
defaultMode: 420
- name: secret-grafana-tls
secret:
secretName: grafana-tls
defaultMode: 420
- name: secret-grafana-proxy
secret:
secretName: grafana-proxy
defaultMode: 420
- name: grafana-trusted-ca-bundle
configMap:
name: grafana-trusted-ca-bundle
items:
- key: ca-bundle.crt
path: tls-ca-bundle.pem
defaultMode: 420
optional: true
dnsPolicy: ClusterFirst
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 25%
maxSurge: 25%
revisionHistoryLimit: 10
progressDeadlineSeconds: 600
Nachdem das Deployment erfolgreich durch geführt wurde, müssen wir noch eine Route bauen
apiVersion: route.openshift.io/v1
kind: Route
metadata:
name: grafana
namespace: grafana
spec:
# host: <host-name>
port:
targetPort: https
tls:
insecureEdgeTerminationPolicy: Redirect
termination: reencrypt
to:
kind: Service
name: grafana
weight: 100
wildcardPolicy: None
Jetzt sollte das nackte Grafana erreichbar sein (wenn wir alle Router, Host-Dateien und sonstiges richtig gefüttert haben)
Hat das funktioniert, können wir die Dashboards und die Datasource einfügen. Da ich für meinen Anwendungsfall den schon vorhandenen Prometheus einbinden wollte, führte ich folgenden Befehl aus:
oc get secret grafana-datasources -o yaml -n openshift-monitoring > secret-grafana-datasources.yaml
Die Datei secret-grafana-datasources.yaml muss noch an den Namespace "grafana" angepasst werden, und die ganzen unnötigen Zeilen entfernt werden. Danach mit oc apply -f übergeben.
Für ein Dashboard müsst ihr ein Dashboard in Json Format in eine ConfigMap packen. In meinem Fall habe ich die ConfigMap "grafana-dashboard-cluster-overview" genannt. Und damit es die übergordnete Struktur, wie auch im Cluster-Monitoring gibt, habe ich noch die ConfigMap grafana-dashboards aus dem "openshift-monitoring" Projekt kopiert und in dem Namespace "grafana" erzeugt.
Dann nehmen wir die Kommentare aus der Deployment-Datei raus, übergeben die Änderungen an OpenShift, et voilá.... wir haben ein Grafana, welches auf den internen Prometheus zugreift und die Daten für eigene Dashboards verwendet.