Montag, 25. Januar 2021

OpenShift 4 Deploy ein Standalone Grafana ohne Grafana Operator

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"
Einen Teil meiner Lösungen habe ich dann in dem Artikel von Red Hat gefunden.

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
kind: ServiceAccount
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.

Keine Kommentare:

Kommentar veröffentlichen