From 892d5bc4afd89df631461169dde3c6945dd62b73 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Mon, 23 Feb 2026 17:10:27 -0800 Subject: [PATCH] C2(upgrade-grafana): impl kustomize-grafana-deployment Replace Helm chart with plain kustomize manifests: - deployment.yaml: Grafana 12.3.3 (home-built) + k8s-sidecar + init container - configmap.yaml: grafana.ini (Authentik OIDC, datasources, paths) - service.yaml, pvc.yaml, serviceaccount.yaml, rbac.yaml - ArgoCD app converted from Helm multi-source to single kustomize source - Removed Helm values.yaml Co-Authored-By: Claude Opus 4.6 --- argocd/apps/grafana.yaml | 19 +- argocd/manifests/grafana/configmap.yaml | 98 +++++++++++ argocd/manifests/grafana/deployment.yaml | 176 +++++++++++++++++++ argocd/manifests/grafana/kustomization.yaml | 12 ++ argocd/manifests/grafana/pvc.yaml | 14 ++ argocd/manifests/grafana/rbac.yaml | 54 ++++++ argocd/manifests/grafana/service.yaml | 18 ++ argocd/manifests/grafana/serviceaccount.yaml | 9 + argocd/manifests/grafana/values.yaml | 108 ------------ 9 files changed, 385 insertions(+), 123 deletions(-) create mode 100644 argocd/manifests/grafana/configmap.yaml create mode 100644 argocd/manifests/grafana/deployment.yaml create mode 100644 argocd/manifests/grafana/kustomization.yaml create mode 100644 argocd/manifests/grafana/pvc.yaml create mode 100644 argocd/manifests/grafana/rbac.yaml create mode 100644 argocd/manifests/grafana/service.yaml create mode 100644 argocd/manifests/grafana/serviceaccount.yaml delete mode 100644 argocd/manifests/grafana/values.yaml diff --git a/argocd/apps/grafana.yaml b/argocd/apps/grafana.yaml index ec9262e..3a2cdd0 100644 --- a/argocd/apps/grafana.yaml +++ b/argocd/apps/grafana.yaml @@ -1,7 +1,5 @@ # Grafana - Dashboards & Observability # -# Chart mirrored from https://github.com/grafana/helm-charts to forge -# # Before syncing, create the admin password secret: # kubectl create namespace monitoring # op inject -i argocd/manifests/grafana-config/secret-admin.yaml.tpl | kubectl apply -f - @@ -12,19 +10,10 @@ metadata: namespace: argocd spec: project: default - sources: - # Helm chart from forge mirror (SSH via egress) - - repoURL: ssh://forgejo@forge.ops.eblu.me:2222/eblume/grafana-helm-charts.git - targetRevision: grafana-8.8.2 - path: charts/grafana - helm: - releaseName: grafana - valueFiles: - - $values/argocd/manifests/grafana/values.yaml - # Values from our git repo - - repoURL: ssh://forgejo@forge.ops.eblu.me:2222/eblume/blumeops.git - targetRevision: main - ref: values + source: + repoURL: ssh://forgejo@forge.ops.eblu.me:2222/eblume/blumeops.git + targetRevision: main + path: argocd/manifests/grafana destination: server: https://kubernetes.default.svc namespace: monitoring diff --git a/argocd/manifests/grafana/configmap.yaml b/argocd/manifests/grafana/configmap.yaml new file mode 100644 index 0000000..f0c00a7 --- /dev/null +++ b/argocd/manifests/grafana/configmap.yaml @@ -0,0 +1,98 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: grafana + namespace: monitoring + labels: + app.kubernetes.io/name: grafana + app.kubernetes.io/instance: grafana +data: + grafana.ini: | + [analytics] + check_for_updates = false + reporting_enabled = false + + [auth.generic_oauth] + allow_sign_up = true + api_url = https://authentik.ops.eblu.me/application/o/userinfo/ + auth_url = https://authentik.ops.eblu.me/application/o/authorize/ + auto_login = false + client_id = grafana + client_secret = $__env{GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET} + enabled = true + name = Authentik + role_attribute_path = 'Admin' + scopes = openid profile email + token_url = https://authentik.ops.eblu.me/application/o/token/ + + [log] + mode = console + + [paths] + data = /var/lib/grafana/ + logs = /var/log/grafana + plugins = /var/lib/grafana/plugins + provisioning = /etc/grafana/provisioning + + [security] + allow_embedding = false + + [server] + root_url = https://grafana.ops.eblu.me + + datasources.yaml: | + apiVersion: 1 + datasources: + - access: proxy + editable: false + isDefault: true + name: Prometheus + orgId: 1 + type: prometheus + uid: prometheus + url: http://prometheus.monitoring.svc.cluster.local:9090 + - access: proxy + editable: false + name: Loki + orgId: 1 + type: loki + uid: loki + url: http://loki.monitoring.svc.cluster.local:3100 + - access: proxy + database: teslamate + editable: false + jsonData: + connMaxLifetime: 14400 + maxIdleConns: 2 + maxOpenConns: 5 + sslmode: disable + name: TeslaMate + orgId: 1 + secureJsonData: + password: $TESLAMATE_DB_PASSWORD + type: postgres + uid: TeslaMate + url: blumeops-pg-rw.databases.svc.cluster.local:5432 + user: teslamate +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: grafana-config-dashboards + namespace: monitoring + labels: + app.kubernetes.io/name: grafana + app.kubernetes.io/instance: grafana +data: + provider.yaml: | + apiVersion: 1 + providers: + - name: 'sidecarProvider' + orgId: 1 + type: file + disableDeletion: false + allowUiUpdates: false + updateIntervalSeconds: 30 + options: + foldersFromFilesStructure: true + path: /tmp/dashboards diff --git a/argocd/manifests/grafana/deployment.yaml b/argocd/manifests/grafana/deployment.yaml new file mode 100644 index 0000000..00a5970 --- /dev/null +++ b/argocd/manifests/grafana/deployment.yaml @@ -0,0 +1,176 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: grafana + namespace: monitoring + labels: + app.kubernetes.io/name: grafana + app.kubernetes.io/instance: grafana +spec: + replicas: 1 + revisionHistoryLimit: 10 + selector: + matchLabels: + app.kubernetes.io/name: grafana + app.kubernetes.io/instance: grafana + strategy: + type: RollingUpdate + template: + metadata: + labels: + app.kubernetes.io/name: grafana + app.kubernetes.io/instance: grafana + annotations: + kubectl.kubernetes.io/default-container: grafana + spec: + automountServiceAccountToken: true + serviceAccountName: grafana + securityContext: + fsGroup: 472 + runAsGroup: 472 + runAsNonRoot: true + runAsUser: 472 + initContainers: + - name: init-chown-data + image: docker.io/library/busybox:1.31.1 + imagePullPolicy: IfNotPresent + command: ["chown", "-R", "472:472", "/var/lib/grafana"] + securityContext: + runAsNonRoot: false + runAsUser: 0 + capabilities: + add: ["CHOWN"] + seccompProfile: + type: RuntimeDefault + volumeMounts: + - name: storage + mountPath: /var/lib/grafana + containers: + # Dashboard sidecar - watches ConfigMaps with grafana_dashboard=1 + - name: grafana-sc-dashboard + image: quay.io/kiwigrid/k8s-sidecar:1.28.0 + imagePullPolicy: IfNotPresent + env: + - name: METHOD + value: WATCH + - name: LABEL + value: grafana_dashboard + - name: LABEL_VALUE + value: "1" + - name: FOLDER + value: /tmp/dashboards + - name: RESOURCE + value: both + - name: FOLDER_ANNOTATION + value: grafana_folder + - name: REQ_USERNAME + valueFrom: + secretKeyRef: + name: grafana-admin + key: admin-user + - name: REQ_PASSWORD + valueFrom: + secretKeyRef: + name: grafana-admin + key: admin-password + - name: REQ_URL + value: http://localhost:3000/api/admin/provisioning/dashboards/reload + - name: REQ_METHOD + value: POST + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + seccompProfile: + type: RuntimeDefault + volumeMounts: + - name: sc-dashboard-volume + mountPath: /tmp/dashboards + # Grafana + - name: grafana + image: registry.ops.eblu.me/blumeops/grafana:v12.3.3-b1ea762 + imagePullPolicy: IfNotPresent + env: + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: GF_SECURITY_ADMIN_USER + valueFrom: + secretKeyRef: + name: grafana-admin + key: admin-user + - name: GF_SECURITY_ADMIN_PASSWORD + valueFrom: + secretKeyRef: + name: grafana-admin + key: admin-password + - name: GF_PATHS_DATA + value: /var/lib/grafana/ + - name: GF_PATHS_LOGS + value: /var/log/grafana + - name: GF_PATHS_PLUGINS + value: /var/lib/grafana/plugins + - name: GF_PATHS_PROVISIONING + value: /etc/grafana/provisioning + envFrom: + - secretRef: + name: grafana-teslamate-datasource + optional: true + - secretRef: + name: grafana-authentik-oauth + optional: true + ports: + - name: http + containerPort: 3000 + protocol: TCP + livenessProbe: + httpGet: + path: /api/health + port: 3000 + initialDelaySeconds: 60 + timeoutSeconds: 30 + failureThreshold: 10 + readinessProbe: + httpGet: + path: /api/health + port: 3000 + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 500m + memory: 512Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + seccompProfile: + type: RuntimeDefault + volumeMounts: + - name: config + mountPath: /etc/grafana/grafana.ini + subPath: grafana.ini + - name: config + mountPath: /etc/grafana/provisioning/datasources/datasources.yaml + subPath: datasources.yaml + - name: storage + mountPath: /var/lib/grafana + - name: sc-dashboard-volume + mountPath: /tmp/dashboards + - name: sc-dashboard-provider + mountPath: /etc/grafana/provisioning/dashboards/sc-dashboardproviders.yaml + subPath: provider.yaml + volumes: + - name: config + configMap: + name: grafana + - name: storage + persistentVolumeClaim: + claimName: grafana + - name: sc-dashboard-volume + emptyDir: {} + - name: sc-dashboard-provider + configMap: + name: grafana-config-dashboards diff --git a/argocd/manifests/grafana/kustomization.yaml b/argocd/manifests/grafana/kustomization.yaml new file mode 100644 index 0000000..2707d42 --- /dev/null +++ b/argocd/manifests/grafana/kustomization.yaml @@ -0,0 +1,12 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: monitoring + +resources: + - serviceaccount.yaml + - configmap.yaml + - pvc.yaml + - deployment.yaml + - service.yaml + - rbac.yaml diff --git a/argocd/manifests/grafana/pvc.yaml b/argocd/manifests/grafana/pvc.yaml new file mode 100644 index 0000000..e119e3a --- /dev/null +++ b/argocd/manifests/grafana/pvc.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: grafana + namespace: monitoring + labels: + app.kubernetes.io/name: grafana + app.kubernetes.io/instance: grafana +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi diff --git a/argocd/manifests/grafana/rbac.yaml b/argocd/manifests/grafana/rbac.yaml new file mode 100644 index 0000000..d0d0c843 --- /dev/null +++ b/argocd/manifests/grafana/rbac.yaml @@ -0,0 +1,54 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: grafana-clusterrole + labels: + app.kubernetes.io/name: grafana + app.kubernetes.io/instance: grafana +rules: + - apiGroups: [""] + resources: ["configmaps", "secrets"] + verbs: ["get", "watch", "list"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: grafana-clusterrolebinding + labels: + app.kubernetes.io/name: grafana + app.kubernetes.io/instance: grafana +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: grafana-clusterrole +subjects: + - kind: ServiceAccount + name: grafana + namespace: monitoring +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: grafana + namespace: monitoring + labels: + app.kubernetes.io/name: grafana + app.kubernetes.io/instance: grafana +rules: [] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: grafana + namespace: monitoring + labels: + app.kubernetes.io/name: grafana + app.kubernetes.io/instance: grafana +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: grafana +subjects: + - kind: ServiceAccount + name: grafana + namespace: monitoring diff --git a/argocd/manifests/grafana/service.yaml b/argocd/manifests/grafana/service.yaml new file mode 100644 index 0000000..eea02f1 --- /dev/null +++ b/argocd/manifests/grafana/service.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Service +metadata: + name: grafana + namespace: monitoring + labels: + app.kubernetes.io/name: grafana + app.kubernetes.io/instance: grafana +spec: + type: ClusterIP + ports: + - name: http + port: 80 + targetPort: 3000 + protocol: TCP + selector: + app.kubernetes.io/name: grafana + app.kubernetes.io/instance: grafana diff --git a/argocd/manifests/grafana/serviceaccount.yaml b/argocd/manifests/grafana/serviceaccount.yaml new file mode 100644 index 0000000..4a1363e --- /dev/null +++ b/argocd/manifests/grafana/serviceaccount.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: grafana + namespace: monitoring + labels: + app.kubernetes.io/name: grafana + app.kubernetes.io/instance: grafana +automountServiceAccountToken: false diff --git a/argocd/manifests/grafana/values.yaml b/argocd/manifests/grafana/values.yaml deleted file mode 100644 index e9e2207..0000000 --- a/argocd/manifests/grafana/values.yaml +++ /dev/null @@ -1,108 +0,0 @@ -# Grafana Helm values for blumeops -# Chart: https://github.com/grafana/helm-charts/tree/main/charts/grafana - -# Admin credentials from pre-created secret -# Secret must exist before deploying - see grafana-config/README.md -admin: - existingSecret: grafana-admin - userKey: admin-user - passwordKey: admin-password - -# Environment variables from secrets (for datasource credentials) -envFromSecrets: - - name: grafana-teslamate-datasource - optional: true - - name: grafana-authentik-oauth - optional: true - -# Persistence with PVC for SQLite database -persistence: - enabled: true - type: pvc - size: 1Gi - accessModes: - - ReadWriteOnce - -# Grafana configuration via grafana.ini -grafana.ini: - server: - root_url: https://grafana.ops.eblu.me - security: - # Embedding disabled - iframe approach didn't work well for Homepage - allow_embedding: false - auth.generic_oauth: - enabled: true - name: Authentik - client_id: grafana - client_secret: $__env{GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET} - scopes: openid profile email - auth_url: https://authentik.ops.eblu.me/application/o/authorize/ - token_url: https://authentik.ops.eblu.me/application/o/token/ - api_url: https://authentik.ops.eblu.me/application/o/userinfo/ - allow_sign_up: true - role_attribute_path: "'Admin'" - auto_login: false - analytics: - check_for_updates: false - reporting_enabled: false - -# Datasources - point to k8s-internal services -datasources: - datasources.yaml: - apiVersion: 1 - datasources: - - name: Prometheus - type: prometheus - access: proxy - orgId: 1 - uid: prometheus - url: http://prometheus.monitoring.svc.cluster.local:9090 - isDefault: true - editable: false - - name: Loki - type: loki - access: proxy - orgId: 1 - uid: loki - url: http://loki.monitoring.svc.cluster.local:3100 - editable: false - - name: TeslaMate - type: postgres - access: proxy - orgId: 1 - uid: TeslaMate - url: blumeops-pg-rw.databases.svc.cluster.local:5432 - database: teslamate - user: teslamate - editable: false - jsonData: - sslmode: disable - maxOpenConns: 5 - maxIdleConns: 2 - connMaxLifetime: 14400 - secureJsonData: - password: $TESLAMATE_DB_PASSWORD - -# Dashboard provisioning - sidecar watches for ConfigMaps with label -sidecar: - dashboards: - enabled: true - label: grafana_dashboard - labelValue: "1" - folderAnnotation: grafana_folder - provider: - foldersFromFilesStructure: true - -# Service configuration (Ingress will handle external access) -service: - type: ClusterIP - port: 80 - -# Resource limits for minikube -resources: - requests: - memory: "128Mi" - cpu: "100m" - limits: - memory: "512Mi" - cpu: "500m"