From 4c5e0f0d16422413e73dee8ab2dc326b88d43e1f Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Mon, 23 Feb 2026 17:44:51 -0800 Subject: [PATCH 1/9] Rename containers/forgejo-runner to runner-job-image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The forgejo-runner container is the CI job execution environment (Dagger, ArgoCD CLI, etc.), not the runner daemon itself. Rename to runner-job-image to fix the version-check false positive (Dagger 0.19.11 vs daemon 12.7.0) and clarify the distinction. RUNNER_LABELS still references the old image name — will update after building the image under the new name. Co-Authored-By: Claude Opus 4.6 --- .../{forgejo-runner => runner-job-image}/Dockerfile | 2 +- docs/changelog.d/+rename-runner-job-image.infra.md | 1 + docs/how-to/configuration/update-documentation.md | 2 +- service-versions.yaml | 13 +++++++++++-- 4 files changed, 14 insertions(+), 4 deletions(-) rename containers/{forgejo-runner => runner-job-image}/Dockerfile (97%) create mode 100644 docs/changelog.d/+rename-runner-job-image.infra.md diff --git a/containers/forgejo-runner/Dockerfile b/containers/runner-job-image/Dockerfile similarity index 97% rename from containers/forgejo-runner/Dockerfile rename to containers/runner-job-image/Dockerfile index e8b385d..8c233cc 100644 --- a/containers/forgejo-runner/Dockerfile +++ b/containers/runner-job-image/Dockerfile @@ -7,7 +7,7 @@ # so this image only needs: git, Docker CLI, Dagger CLI, ArgoCD CLI, uv, yq, and basic tools. # # Usage: Configure runner with label like: -# docker:docker://registry.ops.eblu.me/blumeops/forgejo-runner:latest +# docker:docker://registry.ops.eblu.me/blumeops/runner-job-image:latest ARG CONTAINER_APP_VERSION=0.19.11 diff --git a/docs/changelog.d/+rename-runner-job-image.infra.md b/docs/changelog.d/+rename-runner-job-image.infra.md new file mode 100644 index 0000000..1d6b7bb --- /dev/null +++ b/docs/changelog.d/+rename-runner-job-image.infra.md @@ -0,0 +1 @@ +Rename `containers/forgejo-runner` to `containers/runner-job-image` to distinguish the CI job execution image from the Forgejo runner daemon, fixing a version-check false positive. diff --git a/docs/how-to/configuration/update-documentation.md b/docs/how-to/configuration/update-documentation.md index 5a5c6bc..fef8ccf 100644 --- a/docs/how-to/configuration/update-documentation.md +++ b/docs/how-to/configuration/update-documentation.md @@ -67,7 +67,7 @@ Fragments are automatically collected into `CHANGELOG.md` (at repo root) during The workflow runs on the `k8s` label, which uses the [[forgejo]]-runner in Kubernetes: - **Runner deployment**: `argocd/manifests/forgejo-runner/` -- **Job image**: `registry.ops.eblu.me/blumeops/forgejo-runner:latest` +- **Job image**: `registry.ops.eblu.me/blumeops/runner-job-image` (commit-SHA tagged) - **Build engine**: [[dagger]] CLI installed at runtime; Node.js and Python run inside Dagger containers The job image is built from `containers/forgejo-runner/Dockerfile`. diff --git a/service-versions.yaml b/service-versions.yaml index 69020e6..318ff82 100644 --- a/service-versions.yaml +++ b/service-versions.yaml @@ -190,8 +190,17 @@ services: current-version: "12.7.0" upstream-source: https://code.forgejo.org/forgejo/runner/releases notes: >- - Runner daemon version. Job execution container (containers/forgejo-runner) - tracks Dagger at v0.19.11. + Runner daemon version (code.forgejo.org/forgejo/runner). Job execution + image is tracked separately as runner-job-image. + + - name: runner-job-image + type: argocd + last-reviewed: 2026-02-23 + current-version: "0.19.11" + upstream-source: https://github.com/dagger/dagger/releases + notes: >- + Forgejo Actions job execution image. CONTAINER_APP_VERSION tracks the + Dagger CLI version, the primary build tool in the image. - name: nix-container-builder type: nixos From 9b419abf2406b2167f36422ce34896f17d2d247e Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Mon, 23 Feb 2026 17:47:14 -0800 Subject: [PATCH 2/9] Update RUNNER_LABELS to use runner-job-image:v0.19.11-4c5e0f0 Now that the image is built under the new name, point the forgejo runner at it. Co-Authored-By: Claude Opus 4.6 --- argocd/manifests/forgejo-runner/deployment.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/argocd/manifests/forgejo-runner/deployment.yaml b/argocd/manifests/forgejo-runner/deployment.yaml index 0361f9b..b4d5b88 100644 --- a/argocd/manifests/forgejo-runner/deployment.yaml +++ b/argocd/manifests/forgejo-runner/deployment.yaml @@ -29,7 +29,7 @@ spec: - name: RUNNER_NAME value: "k8s-runner" - name: RUNNER_LABELS - value: "k8s:docker://registry.ops.eblu.me/blumeops/forgejo-runner:v0.19.11-96a2d42" + value: "k8s:docker://registry.ops.eblu.me/blumeops/runner-job-image:v0.19.11-4c5e0f0" command: - /bin/sh - -c From 629a5d4f3020ac52d323dac842df1900dc6f5094 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Mon, 23 Feb 2026 16:25:46 -0800 Subject: [PATCH 3/9] C2(upgrade-grafana): plan add branch field to goal card Co-Authored-By: Claude Opus 4.6 --- docs/how-to/grafana/upgrade-grafana.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/how-to/grafana/upgrade-grafana.md b/docs/how-to/grafana/upgrade-grafana.md index 4044150..e880057 100644 --- a/docs/how-to/grafana/upgrade-grafana.md +++ b/docs/how-to/grafana/upgrade-grafana.md @@ -1,6 +1,7 @@ --- title: Upgrade Grafana status: active +branch: mikado/upgrade-grafana requires: - kustomize-grafana-deployment - build-grafana-container From b2c3dc527f45fc9f55e3b406261a1d5ac8dba5c8 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Mon, 23 Feb 2026 17:33:05 -0800 Subject: [PATCH 4/9] C2(upgrade-grafana): plan document grafana tarball path lesson Discovered during build: the Grafana OSS tarball extracts to grafana-, not grafana-v. The Dockerfile mv command must match this naming. Co-Authored-By: Claude Opus 4.6 --- docs/how-to/grafana/build-grafana-container.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/how-to/grafana/build-grafana-container.md b/docs/how-to/grafana/build-grafana-container.md index ccec654..08942c4 100644 --- a/docs/how-to/grafana/build-grafana-container.md +++ b/docs/how-to/grafana/build-grafana-container.md @@ -23,6 +23,10 @@ Grafana currently uses the upstream `docker.io/grafana/grafana:11.4.0` image via 3. Tag and push to `forge.ops.eblu.me/eblume/grafana:` 4. Add to `mise run container-list` inventory +## Lessons + +- **Tarball directory name:** The Grafana OSS tarball extracts to `grafana-` (e.g. `grafana-12.3.3`), *not* `grafana-v`. The `mv` command in the Dockerfile must match this. + ## Reference - Follow [[build-container-image]] for the standard container build workflow From d8f7ec4a19f79990fe3914c22e182256c9d43f55 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Mon, 23 Feb 2026 16:56:52 -0800 Subject: [PATCH 5/9] C2(upgrade-grafana): impl build-grafana-container Add home-built Grafana 12.3.3 container image based on Alpine 3.22 with pre-built OSS tarball from dl.grafana.com. Uses UID 472 for PVC compatibility with the official image, standard Grafana paths, and multi-arch support via TARGETPLATFORM detection. Update service-versions.yaml to track 12.3.3. Co-Authored-By: Claude Opus 4.6 --- containers/grafana/Dockerfile | 63 +++++++++++++++++++++++++++++++++++ service-versions.yaml | 4 +-- 2 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 containers/grafana/Dockerfile diff --git a/containers/grafana/Dockerfile b/containers/grafana/Dockerfile new file mode 100644 index 0000000..c3010c7 --- /dev/null +++ b/containers/grafana/Dockerfile @@ -0,0 +1,63 @@ +ARG CONTAINER_APP_VERSION=12.3.3 + +FROM alpine:3.22 + +ARG TARGETPLATFORM +ARG CONTAINER_APP_VERSION +ARG GRAFANA_VERSION=${CONTAINER_APP_VERSION} + +RUN set -e && \ + apk --no-cache add dumb-init curl && \ + # Detect architecture + if [ -n "$TARGETPLATFORM" ]; then \ + echo "TARGETPLATFORM: $TARGETPLATFORM"; \ + case "$TARGETPLATFORM" in \ + linux/arm64*) ARCH="arm64" ;; \ + linux/amd64*) ARCH="amd64" ;; \ + *) ARCH="" ;; \ + esac; \ + else \ + echo "TARGETPLATFORM not set, detecting from uname..."; \ + UNAME_ARCH=$(uname -m); \ + echo "uname -m: $UNAME_ARCH"; \ + case "$UNAME_ARCH" in \ + aarch64|arm64) ARCH="arm64" ;; \ + x86_64) ARCH="amd64" ;; \ + *) ARCH="" ;; \ + esac; \ + fi && \ + if [ -z "$ARCH" ]; then \ + echo "ERROR: Unsupported architecture"; \ + exit 1; \ + fi && \ + url="https://dl.grafana.com/oss/release/grafana-${GRAFANA_VERSION}.linux-${ARCH}.tar.gz" && \ + echo "URL: $url" && \ + curl -fSL "$url" | tar -xz -C /tmp && \ + mv /tmp/grafana-v${GRAFANA_VERSION} /usr/share/grafana && \ + apk del curl + +# Standard Grafana paths +RUN mkdir -p /etc/grafana /var/lib/grafana /var/log/grafana && \ + cp /usr/share/grafana/conf/defaults.ini /etc/grafana/grafana.ini && \ + cp /usr/share/grafana/conf/defaults.ini /etc/grafana/defaults.ini + +# UID 472 matches official Grafana image for PVC compatibility +RUN adduser -D -u 472 -h /usr/share/grafana grafana && \ + chown -R grafana:grafana /usr/share/grafana /etc/grafana /var/lib/grafana /var/log/grafana + +USER grafana +WORKDIR /usr/share/grafana +EXPOSE 3000 + +LABEL org.opencontainers.image.title="Grafana" +LABEL org.opencontainers.image.description="Grafana OSS observability platform" +LABEL org.opencontainers.image.source="https://github.com/grafana/grafana" + +ENTRYPOINT ["/usr/bin/dumb-init", "--"] +CMD ["grafana", "server", \ + "--homepath=/usr/share/grafana", \ + "--config=/etc/grafana/grafana.ini", \ + "cfg:default.paths.data=/var/lib/grafana", \ + "cfg:default.paths.logs=/var/log/grafana", \ + "cfg:default.paths.plugins=/var/lib/grafana/plugins", \ + "cfg:default.paths.provisioning=/etc/grafana/provisioning"] diff --git a/service-versions.yaml b/service-versions.yaml index 318ff82..92e4d65 100644 --- a/service-versions.yaml +++ b/service-versions.yaml @@ -83,9 +83,9 @@ services: - name: grafana type: argocd last-reviewed: 2026-02-23 - current-version: "v11.4.0" + current-version: "12.3.3" upstream-source: https://github.com/grafana/grafana/releases - notes: Helm chart 8.8.2; Mikado chain to upgrade to 12.x with kustomize + notes: Home-built container; upgrading to 12.x via Mikado chain - name: cloudnative-pg type: argocd From cafa44a8178a71bd2ccecdc62cb7af08b3888b13 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Mon, 23 Feb 2026 16:57:50 -0800 Subject: [PATCH 6/9] C2(upgrade-grafana): close build-grafana-container Home-built Grafana 12.3.3 container is ready. Dockerfile builds from Alpine 3.22 + official OSS tarball, verified via dagger and container-version-check. Co-Authored-By: Claude Opus 4.6 --- docs/how-to/grafana/build-grafana-container.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/how-to/grafana/build-grafana-container.md b/docs/how-to/grafana/build-grafana-container.md index 08942c4..ce6c557 100644 --- a/docs/how-to/grafana/build-grafana-container.md +++ b/docs/how-to/grafana/build-grafana-container.md @@ -1,6 +1,5 @@ --- title: Build Grafana Container -status: active modified: 2026-02-23 tags: - how-to From 892d5bc4afd89df631461169dde3c6945dd62b73 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Mon, 23 Feb 2026 17:10:27 -0800 Subject: [PATCH 7/9] 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" From 6566cad5b2c6dd816bd401a18aea3f645efa4e7f Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Mon, 23 Feb 2026 17:10:41 -0800 Subject: [PATCH 8/9] C2(upgrade-grafana): close kustomize-grafana-deployment Co-Authored-By: Claude Opus 4.6 --- docs/how-to/grafana/kustomize-grafana-deployment.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/how-to/grafana/kustomize-grafana-deployment.md b/docs/how-to/grafana/kustomize-grafana-deployment.md index f25d14d..d1ec4e7 100644 --- a/docs/how-to/grafana/kustomize-grafana-deployment.md +++ b/docs/how-to/grafana/kustomize-grafana-deployment.md @@ -1,6 +1,5 @@ --- title: Kustomize Grafana Deployment -status: active modified: 2026-02-23 tags: - how-to From 286a6e70a8dfda862729eac98ca12286978c287e Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Mon, 23 Feb 2026 17:48:42 -0800 Subject: [PATCH 9/9] C2(upgrade-grafana): impl fix grafana tarball extract path The Grafana OSS tarball extracts to grafana- (no v prefix), not grafana-v. Fix the mv command to match. Co-Authored-By: Claude Opus 4.6 --- containers/grafana/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/containers/grafana/Dockerfile b/containers/grafana/Dockerfile index c3010c7..c3107c4 100644 --- a/containers/grafana/Dockerfile +++ b/containers/grafana/Dockerfile @@ -33,7 +33,7 @@ RUN set -e && \ url="https://dl.grafana.com/oss/release/grafana-${GRAFANA_VERSION}.linux-${ARCH}.tar.gz" && \ echo "URL: $url" && \ curl -fSL "$url" | tar -xz -C /tmp && \ - mv /tmp/grafana-v${GRAFANA_VERSION} /usr/share/grafana && \ + mv /tmp/grafana-${GRAFANA_VERSION} /usr/share/grafana && \ apk del curl # Standard Grafana paths