# ============================================================================= # Grafana (TASK-0135, phase-15) — Deployment + Service, ns `monitoring`. # # Provisioning + dashboards are generated into ConfigMaps by kustomization.yaml # FROM infrastructure/observability/grafana/ (single source of truth): # /etc/grafana/provisioning/datasources/datasource.yaml (uid factory-prometheus) # /etc/grafana/provisioning/dashboards/dashboards.yaml (provider → folder "Factory") # /var/lib/grafana/dashboards/*.json (the six dashboards) # # The admin password comes from the `grafana-admin` secret (key # admin-password) — create it before applying (like factory-secrets): # kubectl -n monitoring create secret generic grafana-admin \ # --from-literal=admin-password='$(openssl rand -base64 18)' # Image pinned: grafana/grafana:10.4.0. # ============================================================================= apiVersion: apps/v1 kind: Deployment metadata: name: grafana namespace: monitoring labels: app.kubernetes.io/name: grafana app.kubernetes.io/part-of: ai-software-factory spec: replicas: 1 selector: matchLabels: app.kubernetes.io/name: grafana strategy: type: Recreate template: metadata: labels: app.kubernetes.io/name: grafana spec: containers: - name: grafana image: grafana/grafana:10.4.0 imagePullPolicy: IfNotPresent ports: - containerPort: 3000 name: http env: - name: GF_SECURITY_ADMIN_USER value: admin - name: GF_SECURITY_ADMIN_PASSWORD__FILE value: /etc/grafana-secret/admin-password - name: GF_USERS_ALLOW_SIGN_UP value: "false" - name: GF_ANALYTICS_REPORTING_ENABLED value: "false" - name: GF_ANALYTICS_CHECK_FOR_UPDATES value: "false" volumeMounts: - name: datasources mountPath: /etc/grafana/provisioning/datasources readOnly: true - name: dashboards-provider mountPath: /etc/grafana/provisioning/dashboards readOnly: true - name: dashboards mountPath: /var/lib/grafana/dashboards readOnly: true - name: admin-secret mountPath: /etc/grafana-secret readOnly: true - name: data mountPath: /var/lib/grafana readinessProbe: httpGet: path: /api/health port: 3000 initialDelaySeconds: 10 periodSeconds: 10 livenessProbe: httpGet: path: /api/health port: 3000 initialDelaySeconds: 30 periodSeconds: 15 resources: requests: cpu: 100m memory: 256Mi limits: cpu: 500m memory: 1Gi securityContext: allowPrivilegeEscalation: false runAsNonRoot: true # the grafana image runs as `grafana` (uid 472) by default runAsUser: 472 runAsGroup: 472 seccompProfile: type: RuntimeDefault terminationGracePeriodSeconds: 20 volumes: - name: datasources configMap: name: grafana-datasources # hashed name substituted by kustomize - name: dashboards-provider configMap: name: grafana-dashboards-provider # hashed name substituted by kustomize - name: dashboards configMap: name: grafana-dashboards # hashed name substituted by kustomize - name: admin-secret secret: secretName: grafana-admin # created out-of-band (see header) - name: data emptyDir: {} --- apiVersion: v1 kind: Service metadata: name: grafana namespace: monitoring labels: app.kubernetes.io/name: grafana app.kubernetes.io/part-of: ai-software-factory spec: selector: app.kubernetes.io/name: grafana ports: - name: http port: 3000 targetPort: 3000