diff --git a/.vscode/settings.json b/.vscode/settings.json
index 53fccd6..f302235 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -6,10 +6,10 @@
"files.trimTrailingWhitespace": true,
"sops.defaults.ageKeyFile": "age.key",
"vs-kubernetes": {
- "vs-kubernetes.kubeconfig": "./kubeconfig",
+ "vs-kubernetes.kubeconfig": "/home/laur/dev/ai/ops/homelab/kubeconfig",
"vs-kubernetes.knownKubeconfigs": [
- "./kubeconfig"
- ]
+ "/home/laur/dev/ai/ops/homelab/kubeconfig"
+ ]
},
"yaml.schemaStore.enable": true,
"yaml.schemas": {
diff --git a/kubernetes/apps/database-system/dragonfly-operator/app.ks.yaml b/kubernetes/apps/database-system/dragonfly-operator/app.ks.yaml
new file mode 100644
index 0000000..e69de29
diff --git a/kubernetes/apps/default/homepage/ks.yaml b/kubernetes/apps/default/homepage/ks.yaml
new file mode 100644
index 0000000..83093f8
--- /dev/null
+++ b/kubernetes/apps/default/homepage/ks.yaml
@@ -0,0 +1 @@
+# TODO: Fix this via homelab-u3p: Install homepage dashboard [dashboard, deployment, homepage]
\ No newline at end of file
diff --git a/kubernetes/apps/default/publish-k8s-schemas/app/externalsecrets.yaml b/kubernetes/apps/default/publish-k8s-schemas/app/externalsecrets.yaml
new file mode 100644
index 0000000..6266c7d
--- /dev/null
+++ b/kubernetes/apps/default/publish-k8s-schemas/app/externalsecrets.yaml
@@ -0,0 +1 @@
+# TODO: see implementation of external secrets
\ No newline at end of file
diff --git a/kubernetes/apps/default/publish-k8s-schemas/app/helmrelease.yaml b/kubernetes/apps/default/publish-k8s-schemas/app/helmrelease.yaml
new file mode 100644
index 0000000..d5ea3f4
--- /dev/null
+++ b/kubernetes/apps/default/publish-k8s-schemas/app/helmrelease.yaml
@@ -0,0 +1,130 @@
+---
+# yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/refs/heads/main/helmrelease-helm-v2.json
+apiVersion: helm.toolkit.fluxcd.io/v2
+kind: HelmRelease
+metadata:
+ name: &app publish-k8s-schemas
+spec:
+ interval: 1h
+ chartRef:
+ kind: OCIRepository
+ name: app-template
+ values:
+ controllers:
+ *app :
+ type: cronjob
+ cronjob:
+ backoffLimit: 0
+ concurrencyPolicy: 'Replace'
+ failedJobsHistory: 1
+ schedule: '0 */4 * * *'
+ successfulJobsHistory: 0
+
+ initContainers:
+ generate-schemas:
+ image:
+ repository: ghcr.io/bjw-s-labs/k8s-crd-extractor
+ tag: 2025.12.22@sha256:176cc556053abba6fda83eeb398531cd33c3092553cab8a49a736f68972c057b
+ command: [/usr/bin/catatonit, --, sh]
+ args:
+ - -c
+ - /app/crd-extractor.sh && exec sh /config/map/generate-index.sh
+ resources:
+ requests:
+ cpu: 10m
+ memory: 128Mi
+ limits:
+ memory: 2Gi
+ securityContext:
+ allowPrivilegeEscalation: false
+ readOnlyRootFilesystem: true
+ capabilities: { drop: [ALL] }
+
+ containers:
+ app:
+ image:
+ repository: ghcr.io/bjw-s-labs/wrangler
+ tag: 4.63.0@sha256:1e9dc1c872a6e9cc84ccfc50704c7a2fdfec1f8c8e28672dd7c083a008e4bf39
+ envFrom:
+ - secretRef:
+ name: *app
+ args:
+ - pages
+ - deploy
+ - /data/crdSchemas
+ - --project-name
+ - kubernetes-schemas
+ resources:
+ requests:
+ cpu: 10m
+ memory: 128Mi
+ limits:
+ memory: 1Gi
+ securityContext:
+ allowPrivilegeEscalation: false
+ readOnlyRootFilesystem: true
+ capabilities: { drop: [ALL] }
+ defaultPodOptions:
+ restartPolicy: OnFailure
+ securityContext:
+ runAsGroup: 65534
+ runAsNonRoot: true
+ runAsUser: 65534
+
+ persistence:
+ tmpfs:
+ type: emptyDir
+ advancedMounts:
+ *app :
+ generate-schemas:
+ - path: /config/.datree
+ subPath: data
+ - path: /tmp
+ subPath: tmp
+ app:
+ - path: /config
+ subPath: config
+ - path: /data
+ subPath: data
+ config:
+ type: configMap
+ name: *app
+ advancedMounts:
+ *app :
+ generate-schemas:
+ - path: /config/map
+ readOnly: true
+
+ serviceAccount:
+ *app : {}
+
+ rbac:
+ roles:
+ *app :
+ type: ClusterRole
+ rules:
+ - apiGroups:
+ - apiextensions.k8s.io
+ resources:
+ - customresourcedefinitions
+ - customresourcedefinitions/status
+ verbs:
+ - get
+ - list
+ - watch
+ - apiGroups:
+ - apiregistration.k8s.io
+ resources:
+ - apiservices
+ - apiservices/status
+ verbs:
+ - get
+ - list
+ - watch
+ bindings:
+ *app :
+ type: ClusterRoleBinding
+ roleRef:
+ identifier: *app
+ subjects:
+ - identifier: *app
\ No newline at end of file
diff --git a/kubernetes/apps/default/publish-k8s-schemas/app/kustomization.yaml b/kubernetes/apps/default/publish-k8s-schemas/app/kustomization.yaml
new file mode 100644
index 0000000..e69de29
diff --git a/kubernetes/apps/default/publish-k8s-schemas/app/resources/generate-index.sh b/kubernetes/apps/default/publish-k8s-schemas/app/resources/generate-index.sh
new file mode 100644
index 0000000..9f538c6
--- /dev/null
+++ b/kubernetes/apps/default/publish-k8s-schemas/app/resources/generate-index.sh
@@ -0,0 +1,37 @@
+#!/usr/bin/env bash
+
+cd /config/.datree/crdSchemas/
+
+echo "creating index"
+
+# Count stats
+group_count=$(find . -maxdepth 1 -type d | wc -l)
+group_count=$((group_count - 1))
+schema_count=$(find . -name "*.json" | wc -l)
+updated=$(date -u +"%Y-%m-%d")
+
+echo "groups: $group_count - schemas: $schema_count"
+
+# Generate schema cards HTML
+schemas_html=""
+for dir in */; do
+ dir_name="${dir%/}"
+ file_count=$(find "$dir" -maxdepth 1 -name "*.json" | wc -l)
+ files_html=""
+ for file in "$dir"*.json; do
+ if [ -f "$file" ]; then
+ filename=$(basename "$file")
+ files_html="${files_html}${filename}"
+ fi
+ done
+ schemas_html="${schemas_html}
"
+done
+
+# Copy template and inject data
+cp /config/map/index.html index.html
+sed -i "s||${schemas_html}|g" index.html
+sed -i "s|-
|${group_count}
|g" index.html
+sed -i "s|-
|${schema_count}
|g" index.html
+sed -i "s|-
|${updated}
|g" index.html
+
+echo "index created"
\ No newline at end of file
diff --git a/kubernetes/apps/default/publish-k8s-schemas/app/resources/index.html b/kubernetes/apps/default/publish-k8s-schemas/app/resources/index.html
new file mode 100644
index 0000000..1a67a77
--- /dev/null
+++ b/kubernetes/apps/default/publish-k8s-schemas/app/resources/index.html
@@ -0,0 +1,391 @@
+
+
+
+
+
+
+ Kubernetes Schemas | tholinka.dev
+
+
+
+
+
+
+
+
+
+
Usage
+
+ $schema=https://schemas.tholinka.dev/{group}/{kind}_{version}.json
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/kubernetes/apps/default/publish-k8s-schemas/ks.yaml b/kubernetes/apps/default/publish-k8s-schemas/ks.yaml
new file mode 100644
index 0000000..41df6ea
--- /dev/null
+++ b/kubernetes/apps/default/publish-k8s-schemas/ks.yaml
@@ -0,0 +1,20 @@
+---
+# yaml-language-server: $schema=https://schemas.tholinka.dev/kustomize.toolkit.fluxcd.io/kustomization_v1.json
+apiVersion: kustomize.toolkit.fluxcd.io/v1
+kind: Kustomization
+metadata:
+ name: publish-k8s-schemas
+ namespace: &namespace self-hosted
+spec:
+ interval: 1h
+ path: ./kubernetes/apps/self-hosted/publish-k8s-schemas/app
+ postBuild:
+ substituteFrom:
+ - name: cluster-secrets
+ kind: Secret
+ prune: true
+ sourceRef:
+ kind: GitRepository
+ name: flux-system
+ namespace: flux-system
+ targetNamespace: *namespace
\ No newline at end of file
diff --git a/kubernetes/apps/observability/grafana/instance.ks.yaml b/kubernetes/apps/observability/grafana/instance.ks.yaml
new file mode 100644
index 0000000..257dde9
--- /dev/null
+++ b/kubernetes/apps/observability/grafana/instance.ks.yaml
@@ -0,0 +1,24 @@
+---
+apiVersion: kustomize.toolkit.fluxcd.io/v1
+kind: Kustomization
+metadata:
+ name: grafana-instance
+spec:
+ path: ./kubernetes/apps/monitoring-system/grafana/instance
+ sourceRef:
+ kind: GitRepository
+ name: flux-system
+ namespace: flux-system
+ targetNamespace: monitoring-system
+ interval: 10m
+ prune: true
+ wait: true
+ dependsOn:
+ #? wait for the 1password-connect secret store to be ready
+ - name: 1password-connect
+ namespace: security-system
+ - name: grafana-operator
+ - name: envoy-gateway
+ namespace: network-system
+ - name: openebs
+ namespace: storage-system
\ No newline at end of file
diff --git a/kubernetes/apps/observability/grafana/instance/admin.externalsecret.yaml b/kubernetes/apps/observability/grafana/instance/admin.externalsecret.yaml
new file mode 100644
index 0000000..ed2ab71
--- /dev/null
+++ b/kubernetes/apps/observability/grafana/instance/admin.externalsecret.yaml
@@ -0,0 +1,21 @@
+---
+apiVersion: external-secrets.io/v1
+kind: ExternalSecret
+metadata:
+ name: grafana-admin-password-secret
+spec:
+ refreshInterval: 1h
+
+ secretStoreRef:
+ name: onepassword
+ kind: ClusterSecretStore
+
+ target:
+ name: grafana-admin-password-secret
+ creationPolicy: Owner
+
+ data:
+ - secretKey: GF_SECURITY_ADMIN_PASSWORD
+ remoteRef:
+ key: Grafana Admin
+ property: password
\ No newline at end of file
diff --git a/kubernetes/apps/observability/grafana/instance/data-sources.yaml b/kubernetes/apps/observability/grafana/instance/data-sources.yaml
new file mode 100644
index 0000000..6de8ebf
--- /dev/null
+++ b/kubernetes/apps/observability/grafana/instance/data-sources.yaml
@@ -0,0 +1,34 @@
+---
+apiVersion: grafana.integreatly.org/v1beta1
+kind: GrafanaDatasource
+metadata:
+ name: prometheus
+spec:
+ instanceSelector:
+ matchLabels:
+ grafana.internal/instance: grafana
+
+ datasource:
+ type: prometheus
+ name: prometheus
+ access: proxy
+ isDefault: true
+ url: http://prometheus-operated:9090
+
+---
+apiVersion: grafana.integreatly.org/v1beta1
+kind: GrafanaDatasource
+metadata:
+ name: alertmanager
+spec:
+ instanceSelector:
+ matchLabels:
+ grafana.internal/instance: grafana
+
+ datasource:
+ type: alertmanager
+ name: alertmanager
+ access: proxy
+ jsonData:
+ implementation: prometheus
+ url: http://alertmanager-operated:9093
\ No newline at end of file
diff --git a/kubernetes/apps/observability/grafana/instance/grafana.yaml b/kubernetes/apps/observability/grafana/instance/grafana.yaml
new file mode 100644
index 0000000..642363e
--- /dev/null
+++ b/kubernetes/apps/observability/grafana/instance/grafana.yaml
@@ -0,0 +1,75 @@
+---
+apiVersion: grafana.integreatly.org/v1beta1
+kind: Grafana
+metadata:
+ name: grafana
+ labels:
+ grafana.internal/instance: grafana
+
+spec:
+ config:
+ analytics:
+ check_for_updates: "false"
+ check_for_plugin_updates: "false"
+ feedback_links_enabled: "false"
+ reporting_enabled: "false"
+
+ server:
+ enable_gzip: "true"
+ root_url: https://grafana.admin.mirceanton.com
+
+ auth: {disable_login_form: "false"}
+ auth.anonymous: {enabled: "true"}
+ metrics: {enabled: "true"}
+ news: {news_feed_enabled: "false"}
+ plugins: {plugin_admin_enabled: "false"}
+ security: {angular_support_enabled: "true"}
+
+ deployment:
+ spec:
+ strategy:
+ type: Recreate
+ template:
+ spec:
+ containers:
+ - name: grafana
+ env:
+ - name: GF_SECURITY_ADMIN_PASSWORD
+ valueFrom:
+ secretKeyRef:
+ name: grafana-admin-password-secret
+ key: GF_SECURITY_ADMIN_PASSWORD
+ securityContext:
+ allowPrivilegeEscalation: false
+ readOnlyRootFilesystem: true
+ capabilities: {drop: ["ALL"]}
+ securityContext:
+ runAsNonRoot: true
+ runAsUser: 1000
+ runAsGroup: 1000
+ fsGroup: 1000
+ fsGroupChangePolicy: OnRootMismatch
+ volumes:
+ - name: grafana-data
+ persistentVolumeClaim:
+ claimName: grafana-pvc
+
+ httpRoute:
+ spec:
+ hostnames: ["grafana.admin.mirceanton.com"]
+ parentRefs:
+ - name: envoy-admin
+ namespace: network-system
+ rules:
+ - backendRefs:
+ - name: grafana-service
+ port: 3000
+
+ persistentVolumeClaim:
+ spec:
+ accessModes: ["ReadWriteOnce"]
+ resources:
+ requests:
+ storage: 10Gi
+ storageClassName: openebs-hostpath
+ disableDefaultSecurityContext: All
\ No newline at end of file
diff --git a/kubernetes/apps/observability/grafana/instance/kustomization.yaml b/kubernetes/apps/observability/grafana/instance/kustomization.yaml
new file mode 100644
index 0000000..4799cfc
--- /dev/null
+++ b/kubernetes/apps/observability/grafana/instance/kustomization.yaml
@@ -0,0 +1,9 @@
+---
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+
+resources:
+ #- ./admin.externalsecret.yaml
+ - ./data-sources.yaml
+ - ./grafana.yaml
+ - ./service-monitor.yaml
\ No newline at end of file
diff --git a/kubernetes/apps/observability/grafana/instance/service-monitor.yaml b/kubernetes/apps/observability/grafana/instance/service-monitor.yaml
new file mode 100644
index 0000000..03ebc8d
--- /dev/null
+++ b/kubernetes/apps/observability/grafana/instance/service-monitor.yaml
@@ -0,0 +1,19 @@
+---
+apiVersion: monitoring.coreos.com/v1
+kind: ServiceMonitor
+metadata:
+ name: grafana
+spec:
+ jobLabel: grafana
+
+ endpoints:
+ - port: grafana
+ path: /metrics
+ honorLabels: true
+
+ namespaceSelector:
+ matchNames: ["monitoring-system"]
+
+ selector:
+ matchLabels:
+ grafana.internal/instance: grafana
\ No newline at end of file
diff --git a/kubernetes/apps/observability/grafana/kustomization.yaml b/kubernetes/apps/observability/grafana/kustomization.yaml
new file mode 100644
index 0000000..74ee524
--- /dev/null
+++ b/kubernetes/apps/observability/grafana/kustomization.yaml
@@ -0,0 +1,7 @@
+---
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+
+resources: []
+ #- ./operator.ks.yaml
+ #- ./instance.ks.yaml
\ No newline at end of file
diff --git a/kubernetes/apps/observability/grafana/operator.ks.yaml b/kubernetes/apps/observability/grafana/operator.ks.yaml
new file mode 100644
index 0000000..91ef38b
--- /dev/null
+++ b/kubernetes/apps/observability/grafana/operator.ks.yaml
@@ -0,0 +1,15 @@
+---
+apiVersion: kustomize.toolkit.fluxcd.io/v1
+kind: Kustomization
+metadata:
+ name: grafana-operator
+spec:
+ path: ./kubernetes/apps/monitoring-system/grafana/operator
+ sourceRef:
+ kind: GitRepository
+ name: flux-system
+ namespace: flux-system
+ targetNamespace: monitoring-system
+ interval: 10m
+ prune: true
+ wait: true
\ No newline at end of file
diff --git a/kubernetes/apps/observability/grafana/operator/grafana-dashboard.yaml b/kubernetes/apps/observability/grafana/operator/grafana-dashboard.yaml
new file mode 100644
index 0000000..03ca540
--- /dev/null
+++ b/kubernetes/apps/observability/grafana/operator/grafana-dashboard.yaml
@@ -0,0 +1,16 @@
+---
+apiVersion: grafana.integreatly.org/v1beta1
+kind: GrafanaDashboard
+metadata:
+ name: grafana-operator
+spec:
+ allowCrossNamespaceImport: true
+ instanceSelector:
+ matchLabels:
+ grafana.internal/instance: grafana
+ datasources:
+ - datasourceName: prometheus
+ inputName: DS_PROMETHEUS
+ configMapRef:
+ name: grafana-operator-dashboard
+ key: grafana-operator.json
\ No newline at end of file
diff --git a/kubernetes/apps/observability/grafana/operator/helm-release.yaml b/kubernetes/apps/observability/grafana/operator/helm-release.yaml
new file mode 100644
index 0000000..f983012
--- /dev/null
+++ b/kubernetes/apps/observability/grafana/operator/helm-release.yaml
@@ -0,0 +1,27 @@
+---
+apiVersion: helm.toolkit.fluxcd.io/v2
+kind: HelmRelease
+metadata:
+ name: grafana-operator
+spec:
+ interval: 10m
+ chartRef:
+ kind: OCIRepository
+ name: grafana-operator
+
+ values:
+ replicas: 1
+ resources:
+ requests:
+ cpu: 20m
+ memory: 32Mi
+ limits:
+ memory: 64Mi
+
+ serviceMonitor: {enabled: true}
+ dashboard: {enabled: true}
+
+ maxConcurrentReconciles: 4
+ logging:
+ level: info
+ encoder: json
\ No newline at end of file
diff --git a/kubernetes/apps/observability/grafana/operator/oci-repository.yaml b/kubernetes/apps/observability/grafana/operator/oci-repository.yaml
new file mode 100644
index 0000000..ed34ec0
--- /dev/null
+++ b/kubernetes/apps/observability/grafana/operator/oci-repository.yaml
@@ -0,0 +1,13 @@
+---
+apiVersion: source.toolkit.fluxcd.io/v1
+kind: OCIRepository
+metadata:
+ name: grafana-operator
+spec:
+ interval: 15m
+ url: oci://ghcr.io/grafana/helm-charts/grafana-operator
+ ref: {tag: 5.21.4}
+
+ layerSelector:
+ mediaType: application/vnd.cncf.helm.chart.content.v1.tar+gzip
+ operation: copy
\ No newline at end of file
diff --git a/kubernetes/apps/observability/headlamp/app.ks.yaml b/kubernetes/apps/observability/headlamp/app.ks.yaml
new file mode 100644
index 0000000..2089636
--- /dev/null
+++ b/kubernetes/apps/observability/headlamp/app.ks.yaml
@@ -0,0 +1,20 @@
+# yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/kustomization-kustomize-v1.json
+apiVersion: kustomize.toolkit.fluxcd.io/v1
+kind: Kustomization
+metadata:
+ name: &app headlamp
+spec:
+ commonMetadata:
+ labels:
+ app.kubernetes.io/name: *app
+ path: ./kubernetes/apps/observability/headlamp/app
+ prune: true
+ sourceRef:
+ kind: GitRepository
+ name: flux-system
+ namespace: flux-system
+ interval: 1h
+ retryInterval: 2m
+ timeout: 5m
+
+ # TODO: Add OAuth integration.
\ No newline at end of file
diff --git a/kubernetes/apps/observability/headlamp/app/helmrelease.yaml b/kubernetes/apps/observability/headlamp/app/helmrelease.yaml
new file mode 100644
index 0000000..e69de29
diff --git a/kubernetes/apps/observability/headlamp/app/httproute.yaml b/kubernetes/apps/observability/headlamp/app/httproute.yaml
new file mode 100644
index 0000000..1c413c1
--- /dev/null
+++ b/kubernetes/apps/observability/headlamp/app/httproute.yaml
@@ -0,0 +1,21 @@
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ labels:
+ app.kubernetes.io/instance: headlamp
+ app.kubernetes.io/name: headlamp
+ app.kubernetes.io/part-of: headlamp
+ name: headlamp
+spec:
+ hostnames:
+ - headlamp.kantai.xyz
+ parentRefs:
+ - group: gateway.networking.k8s.io
+ kind: Gateway
+ name: envoy-internal
+ namespace: network
+ rules:
+ - backendRefs:
+ - name: headlamp
+ port: 80
\ No newline at end of file
diff --git a/kubernetes/apps/observability/headlamp/app/kustomization.yaml b/kubernetes/apps/observability/headlamp/app/kustomization.yaml
new file mode 100644
index 0000000..e69de29
diff --git a/kubernetes/apps/observability/headlamp/app/ocirepository.yaml b/kubernetes/apps/observability/headlamp/app/ocirepository.yaml
new file mode 100644
index 0000000..e3924ad
--- /dev/null
+++ b/kubernetes/apps/observability/headlamp/app/ocirepository.yaml
@@ -0,0 +1,19 @@
+---
+# yaml-language-server: $schema=https://kubernetes-schemas.pages.dev/source.toolkit.fluxcd.io/ocirepository_v1.json
+apiVersion: source.toolkit.fluxcd.io/v1
+kind: OCIRepository
+metadata:
+ name: headlamp
+spec:
+ interval: 1h
+ layerSelector:
+ mediaType: application/vnd.cncf.helm.chart.content.v1.tar+gzip
+ operation: copy
+ ref:
+ tag: 0.40.0
+ url: oci://ghcr.io/home-operations/charts-mirror/headlamp
+ verify:
+ provider: cosign
+ matchOIDCIdentity:
+ - issuer: ^https://token.actions.githubusercontent.com$
+ subject: ^https://github.com/home-operations/charts-mirror.*$
\ No newline at end of file
diff --git a/kubernetes/apps/observability/headlamp/kustomization.yaml b/kubernetes/apps/observability/headlamp/kustomization.yaml
new file mode 100644
index 0000000..bffe7fc
--- /dev/null
+++ b/kubernetes/apps/observability/headlamp/kustomization.yaml
@@ -0,0 +1,6 @@
+---
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+
+resources:
+ - ./app.ks.yaml
\ No newline at end of file
diff --git a/kubernetes/apps/observability/kube-prometheus-stack/app.ks.yaml b/kubernetes/apps/observability/kube-prometheus-stack/app.ks.yaml
new file mode 100644
index 0000000..e91fc46
--- /dev/null
+++ b/kubernetes/apps/observability/kube-prometheus-stack/app.ks.yaml
@@ -0,0 +1,20 @@
+---
+apiVersion: kustomize.toolkit.fluxcd.io/v1
+kind: Kustomization
+metadata:
+ name: kube-prometheus-stack
+spec:
+ path: ./kubernetes/apps/monitoring-system/kube-prometheus-stack/app
+ sourceRef:
+ kind: GitRepository
+ name: flux-system
+ namespace: flux-system
+ targetNamespace: monitoring-system
+ interval: 10m
+ prune: true
+ wait: true
+ dependsOn:
+ - name: openebs
+ namespace: storage-system
+ - name: envoy-gateway
+ namespace: network-system
\ No newline at end of file
diff --git a/kubernetes/apps/observability/kube-prometheus-stack/app/grafana-dashboards.yaml b/kubernetes/apps/observability/kube-prometheus-stack/app/grafana-dashboards.yaml
new file mode 100644
index 0000000..7ffdd7d
--- /dev/null
+++ b/kubernetes/apps/observability/kube-prometheus-stack/app/grafana-dashboards.yaml
@@ -0,0 +1,143 @@
+---
+# yaml-language-server: $schema=https://kubernetes-schemas.pages.dev/grafana.integreatly.org/grafanadashboard_v1beta1.json
+apiVersion: grafana.integreatly.org/v1beta1
+kind: GrafanaDashboard
+metadata:
+ name: kubernetes-api-server
+spec:
+ allowCrossNamespaceImport: true
+ instanceSelector:
+ matchLabels:
+ grafana.internal/instance: grafana
+ datasources:
+ - datasourceName: prometheus
+ inputName: DS_PROMETHEUS
+ url: https://grafana.com/api/dashboards/15761/revisions/20/download
+
+---
+# yaml-language-server: $schema=https://kubernetes-schemas.pages.dev/grafana.integreatly.org/grafanadashboard_v1beta1.json
+apiVersion: grafana.integreatly.org/v1beta1
+kind: GrafanaDashboard
+metadata:
+ name: kubernetes-coredns
+spec:
+ allowCrossNamespaceImport: true
+ instanceSelector:
+ matchLabels:
+ grafana.internal/instance: grafana
+ datasources:
+ - datasourceName: prometheus
+ inputName: DS_PROMETHEUS
+ url: https://grafana.com/api/dashboards/15762/revisions/22/download
+
+---
+# yaml-language-server: $schema=https://kubernetes-schemas.pages.dev/grafana.integreatly.org/grafanadashboard_v1beta1.json
+apiVersion: grafana.integreatly.org/v1beta1
+kind: GrafanaDashboard
+metadata:
+ name: kubernetes-global
+spec:
+ allowCrossNamespaceImport: true
+ instanceSelector:
+ matchLabels:
+ grafana.internal/instance: grafana
+ datasources:
+ - datasourceName: prometheus
+ inputName: DS_PROMETHEUS
+ url: https://grafana.com/api/dashboards/15757/revisions/43/download
+
+---
+# yaml-language-server: $schema=https://kubernetes-schemas.pages.dev/grafana.integreatly.org/grafanadashboard_v1beta1.json
+apiVersion: grafana.integreatly.org/v1beta1
+kind: GrafanaDashboard
+metadata:
+ name: kubernetes-namespaces
+spec:
+ allowCrossNamespaceImport: true
+ instanceSelector:
+ matchLabels:
+ grafana.internal/instance: grafana
+ datasources:
+ - datasourceName: prometheus
+ inputName: DS_PROMETHEUS
+ url: https://grafana.com/api/dashboards/15758/revisions/44/download
+
+---
+# yaml-language-server: $schema=https://kubernetes-schemas.pages.dev/grafana.integreatly.org/grafanadashboard_v1beta1.json
+apiVersion: grafana.integreatly.org/v1beta1
+kind: GrafanaDashboard
+metadata:
+ name: kubernetes-nodes
+spec:
+ allowCrossNamespaceImport: true
+ instanceSelector:
+ matchLabels:
+ grafana.internal/instance: grafana
+ datasources:
+ - datasourceName: prometheus
+ inputName: DS_PROMETHEUS
+ url: https://grafana.com/api/dashboards/15759/revisions/40/download
+
+---
+# yaml-language-server: $schema=https://kubernetes-schemas.pages.dev/grafana.integreatly.org/grafanadashboard_v1beta1.json
+apiVersion: grafana.integreatly.org/v1beta1
+kind: GrafanaDashboard
+metadata:
+ name: kubernetes-pods
+spec:
+ allowCrossNamespaceImport: true
+ instanceSelector:
+ matchLabels:
+ grafana.internal/instance: grafana
+ datasources:
+ - datasourceName: prometheus
+ inputName: DS_PROMETHEUS
+ url: https://grafana.com/api/dashboards/15760/revisions/37/download
+
+---
+# yaml-language-server: $schema=https://kubernetes-schemas.pages.dev/grafana.integreatly.org/grafanadashboard_v1beta1.json
+apiVersion: grafana.integreatly.org/v1beta1
+kind: GrafanaDashboard
+metadata:
+ name: kubernetes-volumes
+spec:
+ allowCrossNamespaceImport: true
+ instanceSelector:
+ matchLabels:
+ grafana.internal/instance: grafana
+ datasources:
+ - datasourceName: prometheus
+ inputName: DS_PROMETHEUS
+ url: https://grafana.com/api/dashboards/11454/revisions/14/download
+
+---
+# yaml-language-server: $schema=https://kubernetes-schemas.pages.dev/grafana.integreatly.org/grafanadashboard_v1beta1.json
+apiVersion: grafana.integreatly.org/v1beta1
+kind: GrafanaDashboard
+metadata:
+ name: node-exporter-full
+spec:
+ allowCrossNamespaceImport: true
+ instanceSelector:
+ matchLabels:
+ grafana.internal/instance: grafana
+ datasources:
+ - datasourceName: prometheus
+ inputName: DS_PROMETHEUS
+ url: https://grafana.com/api/dashboards/1860/revisions/42/download
+
+---
+# yaml-language-server: $schema=https://kubernetes-schemas.pages.dev/grafana.integreatly.org/grafanadashboard_v1beta1.json
+apiVersion: grafana.integreatly.org/v1beta1
+kind: GrafanaDashboard
+metadata:
+ name: prometheus
+spec:
+ allowCrossNamespaceImport: true
+ instanceSelector:
+ matchLabels:
+ grafana.internal/instance: grafana
+ datasources:
+ - datasourceName: prometheus
+ inputName: DS_PROMETHEUS
+ url: https://grafana.com/api/dashboards/19105/revisions/8/download
\ No newline at end of file
diff --git a/kubernetes/apps/observability/kube-prometheus-stack/app/helm-release.yaml b/kubernetes/apps/observability/kube-prometheus-stack/app/helm-release.yaml
new file mode 100644
index 0000000..8028bc6
--- /dev/null
+++ b/kubernetes/apps/observability/kube-prometheus-stack/app/helm-release.yaml
@@ -0,0 +1,208 @@
+---
+apiVersion: helm.toolkit.fluxcd.io/v2
+kind: HelmRelease
+metadata:
+ name: kube-prometheus-stack
+spec:
+ interval: 10m
+ chartRef:
+ kind: OCIRepository
+ name: kube-prometheus-stack
+
+ values:
+ crds: {enabled: true}
+ cleanPrometheusOperatorObjectNames: true
+
+ # ==========================================================================
+ # Alertmanager
+ # ==========================================================================
+ alertmanager:
+ enabled: true
+ route:
+ main:
+ enabled: true
+ hostnames: ["alertmanager.admin.mirceanton.com"]
+ parentRefs:
+ - name: envoy-admin
+ namespace: network-system
+
+ alertmanagerSpec:
+ # alertmanagerConfiguration: {name: alertmanager}
+ # =======================================
+ # App Settings
+ # =======================================
+ externalUrl: "https://alertmanager.admin.mirceanton.com"
+ logFormat: json
+ logLevel: info
+
+ # =======================================
+ # Resources
+ # =======================================
+ replicas: 1
+ resources:
+ requests:
+ cpu: 20m
+ memory: 32Mi
+ limits:
+ memory: 128Mi
+ storage:
+ volumeClaimTemplate:
+ spec:
+ storageClassName: openebs-hostpath
+ resources:
+ requests:
+ storage: 1Gi
+
+ # ==========================================================================
+ # Prometheus Operator
+ # ==========================================================================
+ prometheusOperator:
+ enabled: true
+ # =======================================
+ # App Settings
+ # =======================================
+ logFormat: json
+ logLevel: info
+
+ # =======================================
+ # Resources
+ # =======================================
+ resources:
+ requests:
+ cpu: 20m
+ memory: 64Mi
+ limits:
+ memory: 128Mi
+
+ # ==========================================================================
+ # Prometheus
+ # ==========================================================================
+ prometheus:
+ enabled: true
+ route:
+ main:
+ enabled: true
+ hostnames: ["prometheus.admin.mirceanton.com"]
+ parentRefs:
+ - name: envoy-admin
+ namespace: network-system
+
+ prometheusSpec:
+ # =======================================
+ # App Settings
+ # =======================================
+ externalUrl: "https://prometheus.admin.mirceanton.com"
+ enableAdminAPI: true
+ retention: 14d
+ retentionSize: 50GB
+ logLevel: info
+ logFormat: json
+
+ # =======================================
+ #? Replace default Prometheus image with prompp and
+ #? override 'unsupported Prometheus version' error
+ # =======================================
+ version: v2.55.1
+ image:
+ registry: mirror.gcr.io
+ repository: prompp/prompp
+ tag: 0.7.4
+
+ # =======================================
+ # Security
+ # =======================================
+ securityContext:
+ runAsNonRoot: true
+ runAsUser: 64535
+ runAsGroup: 64535
+ fsGroup: 64535
+
+ # =======================================
+ #? Disable prometheus resource to be created with selectors based on
+ #? values in the helm deployment if a nil or {} value is provided
+ # =======================================
+ podMonitorSelectorNilUsesHelmValues: false
+ probeSelectorNilUsesHelmValues: false
+ ruleSelectorNilUsesHelmValues: false
+ scrapeConfigSelectorNilUsesHelmValues: false
+ serviceMonitorSelectorNilUsesHelmValues: false
+
+ # =======================================
+ # Resources
+ # =======================================
+ replicas: 1
+ shards: 1
+ resources:
+ requests:
+ cpu: 50m
+ memory: 128Mi
+ limits:
+ memory: 1Gi
+ storageSpec:
+ volumeClaimTemplate:
+ spec:
+ storageClassName: openebs-hostpath
+ resources:
+ requests:
+ storage: 50Gi
+
+
+ # ==========================================================================
+ # Grafana
+ # ==========================================================================
+ grafana:
+ enabled: false
+ forceDeployDashboards: true
+ operator:
+ dashboardsConfigMapRefEnabled: true
+ folder: monitoring-system
+ matchLabels:
+ grafana.internal/instance: grafana
+
+ # ==========================================================================
+ # Exporters
+ # ==========================================================================
+ coreDns: {enabled: true}
+ kubelet: {enabled: true}
+ kubeApiServer: {enabled: true}
+ kubeControllerManager: {enabled: true}
+ kubeScheduler: {enabled: true}
+ kubeProxy: {enabled: true}
+ kubeEtcd:
+ enabled: true
+ service:
+ selector:
+ component: kube-apiserver
+
+ nodeExporter: {enabled: true}
+ prometheus-node-exporter:
+ resources:
+ requests:
+ cpu: 20m
+ memory: 32Mi
+ limits:
+ memory: 64Mi
+
+ kubeStateMetrics: {enabled: true}
+ kube-state-metrics:
+ resources:
+ requests:
+ cpu: 20m
+ memory: 64Mi
+ limits:
+ memory: 128Mi
+
+ # ==========================================================================
+ # Additional Settings
+ # ==========================================================================
+ additionalPrometheusRulesMap:
+ oom-rules:
+ groups:
+ - name: oom
+ rules:
+ - alert: OomKilled
+ annotations:
+ summary: Container {{ $labels.container }} in pod {{ $labels.namespace }}/{{ $labels.pod }} has been OOMKilled {{ $value }} times in the last 10 minutes.
+ expr: (kube_pod_container_status_restarts_total - kube_pod_container_status_restarts_total offset 10m >= 1) and ignoring (reason) min_over_time(kube_pod_container_status_last_terminated_reason{reason="OOMKilled"}[10m]) == 1
+ labels:
+ severity: critical
\ No newline at end of file
diff --git a/kubernetes/apps/observability/kube-prometheus-stack/app/oci-repository.yaml b/kubernetes/apps/observability/kube-prometheus-stack/app/oci-repository.yaml
new file mode 100644
index 0000000..61b41bc
--- /dev/null
+++ b/kubernetes/apps/observability/kube-prometheus-stack/app/oci-repository.yaml
@@ -0,0 +1,13 @@
+---
+apiVersion: source.toolkit.fluxcd.io/v1
+kind: OCIRepository
+metadata:
+ name: kube-prometheus-stack
+spec:
+ interval: 15m
+ url: oci://ghcr.io/prometheus-community/charts/kube-prometheus-stack
+ ref: {tag: 81.5.0}
+
+ layerSelector:
+ mediaType: application/vnd.cncf.helm.chart.content.v1.tar+gzip
+ operation: copy
\ No newline at end of file
diff --git a/kubernetes/apps/observability/kube-prometheus-stack/kustomization.yaml b/kubernetes/apps/observability/kube-prometheus-stack/kustomization.yaml
new file mode 100644
index 0000000..bffe7fc
--- /dev/null
+++ b/kubernetes/apps/observability/kube-prometheus-stack/kustomization.yaml
@@ -0,0 +1,6 @@
+---
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+
+resources:
+ - ./app.ks.yaml
\ No newline at end of file
diff --git a/kubernetes/apps/observability/kustomization.yaml b/kubernetes/apps/observability/kustomization.yaml
new file mode 100644
index 0000000..d2cc41e
--- /dev/null
+++ b/kubernetes/apps/observability/kustomization.yaml
@@ -0,0 +1,9 @@
+---
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+namespace: monitoring-system
+
+resources:
+ - ./namespace.yaml
+ #- ./grafana
+ #- ./kube-prometheus-stack
\ No newline at end of file
diff --git a/kubernetes/apps/observability/namespace.yaml b/kubernetes/apps/observability/namespace.yaml
new file mode 100644
index 0000000..7fad8e4
--- /dev/null
+++ b/kubernetes/apps/observability/namespace.yaml
@@ -0,0 +1,7 @@
+---
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: _
+ annotations:
+ kustomize.toolkit.fluxcd.io/prune: disabled
\ No newline at end of file
diff --git a/kubernetes/apps/security/kustomization.yaml b/kubernetes/apps/security/kustomization.yaml
new file mode 100644
index 0000000..6cc154c
--- /dev/null
+++ b/kubernetes/apps/security/kustomization.yaml
@@ -0,0 +1,4 @@
+# External secrets should be implemented.
+# Most popular is 1password, but it's paid
+# https://github.com/tholinka/home-ops implements secrets via bitwarden (which is free)
+