Compare commits
7 Commits
2ea9f3973f
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 08dfe346d9 | |||
| 2da631dc81 | |||
| 76945105d7 | |||
| 9905abd9b4 | |||
| 1125b8b072 | |||
| 39079615f5 | |||
| be9329d313 |
Vendored
+5
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"yaml.schemas": {
|
||||||
|
"kubernetes://schema/v1@persistentvolumeclaim": "file:///home/henry/HomeLabScripts/k3s/apps/gitLab/manifest/pv-pvc.yaml"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
# HomeLabScripts
|
||||||
|
|
||||||
|
HomeLabScripts ist ein Infrastruktur-Repository fuer mein HomeLab auf Basis von K3s.
|
||||||
|
Es enthaelt Installationsskripte, Kubernetes-Manifeste und Helm-Values fuer mehrere Self-Hosting-Services.
|
||||||
|
|
||||||
|
## Ziele
|
||||||
|
|
||||||
|
- Reproduzierbarer Aufbau eines K3s-Clusters
|
||||||
|
- Betrieb zentraler Self-Hosting-Dienste im Heimnetz
|
||||||
|
- Trennung von Storage-Workloads zwischen NFS (gross) und Longhorn (lokal/schnell)
|
||||||
|
- Nachvollziehbare Betriebsablaeufe ueber Skripte und YAML-Manifeste
|
||||||
|
|
||||||
|
## Tech-Stack
|
||||||
|
|
||||||
|
- K3s (Kubernetes)
|
||||||
|
- Helm
|
||||||
|
- NFS
|
||||||
|
- Longhorn
|
||||||
|
- MariaDB / PostgreSQL / Redis
|
||||||
|
- Authentik
|
||||||
|
|
||||||
|
## Repository-Struktur
|
||||||
|
|
||||||
|
```text
|
||||||
|
HomeLabScripts/
|
||||||
|
├── k3s/
|
||||||
|
│ ├── install.sh
|
||||||
|
│ ├── get_helm.sh
|
||||||
|
│ ├── installHelm.sh
|
||||||
|
│ ├── k8sUser/
|
||||||
|
│ │ └── addUser.sh
|
||||||
|
│ └── apps/
|
||||||
|
│ ├── authentik/
|
||||||
|
│ ├── dashboard/
|
||||||
|
│ ├── gitea/
|
||||||
|
│ ├── gitLab/
|
||||||
|
│ ├── homarr/
|
||||||
|
│ ├── longhorn/
|
||||||
|
│ ├── Nextcloud/
|
||||||
|
│ ├── nfs-pv/
|
||||||
|
│ ├── photo/
|
||||||
|
│ └── speedtest-tracker/
|
||||||
|
├── nfs/
|
||||||
|
│ ├── server.sh
|
||||||
|
│ ├── client.sh
|
||||||
|
│ ├── nfsClient2.sh
|
||||||
|
│ ├── nfsClient2SlowData.sh
|
||||||
|
│ └── nfsSlowData.sh
|
||||||
|
├── mountscript/
|
||||||
|
│ └── mount-plus.sh
|
||||||
|
└── WISSENSBASIS.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Enthaltene Services (Auswahl)
|
||||||
|
|
||||||
|
- Authentik
|
||||||
|
- Kubernetes Dashboard
|
||||||
|
- Gitea
|
||||||
|
- GitLab
|
||||||
|
- Nextcloud
|
||||||
|
- Immich
|
||||||
|
- PhotoPrism
|
||||||
|
- iCloudPD
|
||||||
|
- Homarr
|
||||||
|
- Speedtest Tracker
|
||||||
|
- Longhorn
|
||||||
|
|
||||||
|
## Storage-Strategie
|
||||||
|
|
||||||
|
- Longhorn fuer Datenbanken und kleinere persistente Volumes
|
||||||
|
- NFS fuer grosse RWX-Workloads wie Medien, Repositories und Cloud-Daten
|
||||||
|
|
||||||
|
Empfehlung:
|
||||||
|
- Performance-kritische DB-Workloads nicht auf langsames NFS legen
|
||||||
|
- RWX-Daten (z. B. Medien) bevorzugt auf NFS
|
||||||
|
|
||||||
|
## Schnellstart (neu aufsetzen)
|
||||||
|
|
||||||
|
1. System vorbereiten (Kernel, Netzwerk, Zeitsync, DNS)
|
||||||
|
2. K3s installieren:
|
||||||
|
- `bash k3s/install.sh`
|
||||||
|
3. Helm installieren:
|
||||||
|
- `bash k3s/installHelm.sh`
|
||||||
|
4. Optionalen K8s-User/Kubeconfig erstellen:
|
||||||
|
- `bash k3s/k8sUser/addUser.sh`
|
||||||
|
5. Storage bereitstellen:
|
||||||
|
- NFS via Skripte unter `nfs/`
|
||||||
|
- Longhorn-StorageClass unter `k3s/apps/longhorn/`
|
||||||
|
6. Apps schrittweise deployen (pro App-Ordner)
|
||||||
|
|
||||||
|
## Deployment-Hinweise
|
||||||
|
|
||||||
|
- Erst Namespace und Secrets anwenden, dann Deployments/Stateful Workloads
|
||||||
|
- Bei Helm-basierten Setups zuerst `values.yaml` pruefen
|
||||||
|
- Migrationen (z. B. auf Longhorn) nur mit Backup durchfuehren
|
||||||
|
- NodeSelector, fsGroup und Permissions pro App pruefen
|
||||||
|
|
||||||
|
## Wichtige Skripte
|
||||||
|
|
||||||
|
- `k3s/install.sh`: K3s-Installation
|
||||||
|
- `k3s/installHelm.sh`: Helm-Installation
|
||||||
|
- `k3s/k8sUser/addUser.sh`: ServiceAccount + RBAC + Kubeconfig
|
||||||
|
- `k3s/apps/dashboard/getToken.sh`: Dashboard-Token abrufen
|
||||||
|
- `k3s/apps/Nextcloud/helm/cleanRestart.sh`: Nextcloud sauber neu starten
|
||||||
|
- `nfs/server.sh`: NFS-Server einrichten
|
||||||
|
- `nfs/client.sh`: NFS-Client einrichten
|
||||||
|
- `mountscript/mount-plus.sh`: Datentraeger partitionieren/mounten
|
||||||
|
|
||||||
|
## Konventionen
|
||||||
|
|
||||||
|
- Manifeste: `*-deployment.yaml`, `*-service.yaml`, `*-secret.yaml`, `namespace.yaml`
|
||||||
|
- Secrets nie im Klartext committen
|
||||||
|
- Keine produktiven Zugangsdaten in Doku oder Repo-Historie speichern
|
||||||
|
- Aenderungen pro App in kleinen, nachvollziehbaren Commits
|
||||||
|
|
||||||
|
## Sicherheit
|
||||||
|
|
||||||
|
- Bereits veroeffentlichte Tokens/Passwoerter sofort rotieren
|
||||||
|
- Secrets ueber Kubernetes Secrets oder externe Secret-Manager verwalten
|
||||||
|
- Admin-UIs nur intern oder hinter Authentik/Ingress absichern
|
||||||
|
- Regelmaessige Backups fuer DB und Volumes einplanen
|
||||||
|
|
||||||
|
## Betrieb und Troubleshooting
|
||||||
|
|
||||||
|
Nuetzliche Kommandos:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl get pods -A
|
||||||
|
kubectl get pvc -A
|
||||||
|
kubectl describe pod <pod-name> -n <namespace>
|
||||||
|
kubectl logs <pod-name> -n <namespace>
|
||||||
|
```
|
||||||
|
|
||||||
|
Falls ein Dienst nicht startet:
|
||||||
|
|
||||||
|
1. Namespace und Secret vorhanden?
|
||||||
|
2. PVC gebunden?
|
||||||
|
3. DB erreichbar?
|
||||||
|
4. Berechtigungen (uid/gid, fsGroup, NFS export options) korrekt?
|
||||||
|
|
||||||
|
## Roadmap (optional)
|
||||||
|
|
||||||
|
- Einheitliche Ingress-Strategie fuer alle Services
|
||||||
|
- Zentralisiertes Secret-Management (z. B. SOPS/Sealed Secrets/Vault)
|
||||||
|
- Monitoring/Alerting (Prometheus + Grafana + Alertmanager)
|
||||||
|
- CI-Pruefungen fuer Manifeste (lint/validate)
|
||||||
|
|
||||||
|
## Hinweis
|
||||||
|
|
||||||
|
Details zu bestehenden Setups und internen Notizen stehen in `WISSENSBASIS.md`.
|
||||||
|
Diese README dient als zentraler Einstiegspunkt fuer Aufbau und Betrieb.
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
# HomeLabScripts – Wissensbasis
|
||||||
|
|
||||||
|
## Zweck
|
||||||
|
Persönliches Home-Lab-Infrastruktur-Repository. Orchestriert eine selbst gehostete Cloud-Umgebung auf K3s (Kubernetes) auf lokaler Hardware.
|
||||||
|
|
||||||
|
## Technologien
|
||||||
|
- **K3s** – Kubernetes-Distribution
|
||||||
|
- **Helm** – Paketmanager für K8s
|
||||||
|
- **NFS** – Netzwerk-Dateisystem (shared storage)
|
||||||
|
- **Longhorn** – verteilter Block-Storage (2 Replicas)
|
||||||
|
- **PostgreSQL / MariaDB / Redis** – Datenbanken & Caching
|
||||||
|
- **Authentik** – OAuth2/OIDC-Authentifizierungsserver
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Repo-Struktur
|
||||||
|
```
|
||||||
|
HomeLabScripts/
|
||||||
|
├── k3s/
|
||||||
|
│ ├── apps/ # App-Manifeste & Helm-Charts
|
||||||
|
│ ├── install.sh # K3s-Cluster-Installation
|
||||||
|
│ ├── get_helm.sh # Helm herunterladen
|
||||||
|
│ ├── installHelm.sh # Helm installieren
|
||||||
|
│ └── k8sUser/ # Benutzer-/Kubeconfig-Setup
|
||||||
|
├── nfs/ # NFS-Server- & Client-Skripte
|
||||||
|
└── mountscript/ # Disk-Partitionierung & Einhängen
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Anwendungen
|
||||||
|
|
||||||
|
| App | Namespace | NodePort | Storage | Beschreibung |
|
||||||
|
|-----|-----------|----------|---------|--------------|
|
||||||
|
| **Authentik** | authentik | 32222 | PostgreSQL (intern) | OAuth2/OIDC-Provider |
|
||||||
|
| **Homarr** | homarr | 30757 | Longhorn 5Gi | Homepage-Dashboard |
|
||||||
|
| **K8s Dashboard** | kubernetes-dashboard | 443 | – | Cluster-Management-UI |
|
||||||
|
| **Gitea** | gitea | – | NFS 30Gi (Repos) + 10Gi (DB) | Leichter Git-Dienst |
|
||||||
|
| **GitLab** | gitlab | 80/443/22 | NFS 50Gi (RWX) | Full GitLab mit CI/CD |
|
||||||
|
| **Nextcloud** | nextcloud | 30180 | NFS (Daten) + Longhorn (DB) | Datei-Hosting |
|
||||||
|
| **Immich** | photoprism | 3001/2283 | NFS photos | Fotoverwaltung (Google Photos Alternative) |
|
||||||
|
| **PhotoPrism** | photoprism | – | NFS photos | KI-Fotoverwaltung |
|
||||||
|
| **iCloudPD** | photoprism | – | NFS /data/originals | Apple-iCloud-Foto-Sync |
|
||||||
|
| **Longhorn** | longhorn-system | – | – | Storage-Provisioner |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Storage-Strategie
|
||||||
|
- **Longhorn** → Datenbanken, kleine Konfigurationen (schnell, lokal)
|
||||||
|
- **NFS** → Medien, Repos, Nextcloud-Daten (groß, geteilt, RWX)
|
||||||
|
- NFS-Server: `192.168.178.166`, Pfade: `/export/fastData/`, `/export/slowData/`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Netzwerk
|
||||||
|
- **NodePort** für externen Zugriff
|
||||||
|
- **ClusterIP** für Pod-to-Pod-Kommunikation (DBs)
|
||||||
|
- **Multus** bei Immich (separates IoT-Netz: `192.168.1.192/24`)
|
||||||
|
- Domain: `henryathome.home64.de`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Datenbank-Zuordnung
|
||||||
|
| App | DB-Typ | User | Hinweis |
|
||||||
|
|-----|--------|------|---------|
|
||||||
|
| Authentik | PostgreSQL | – | intern |
|
||||||
|
| Nextcloud | MariaDB 10.8 | nextcloud | PW: nextcloud |
|
||||||
|
| GitLab | MariaDB | – | NFS-Backend |
|
||||||
|
| Gitea | PostgreSQL | – | NFS-Backend |
|
||||||
|
| Immich | PostgreSQL 14 (pgvecto-rs) | immich | PW: password |
|
||||||
|
| PhotoPrism | MariaDB | photoprism | PW: photoprism |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Wichtige Skripte
|
||||||
|
|
||||||
|
| Skript | Zweck |
|
||||||
|
|--------|-------|
|
||||||
|
| `k3s/install.sh` | K3s installieren |
|
||||||
|
| `k3s/installHelm.sh` | Helm 3 installieren |
|
||||||
|
| `k3s/k8sUser/addUser.sh` | ServiceAccount + ClusterRoleBinding + Kubeconfig erstellen |
|
||||||
|
| `k3s/apps/dashboard/getToken.sh` | Admin-Token für K8s Dashboard |
|
||||||
|
| `k3s/apps/photo/icloudpd/base64pw.sh` | iCloud-Passwort base64-kodieren |
|
||||||
|
| `k3s/apps/Nextcloud/helm/cleanRestart.sh` | Nextcloud sauber neu starten |
|
||||||
|
| `nfs/server.sh` | NFS-Server konfigurieren |
|
||||||
|
| `nfs/client.sh` / `nfsClient2.sh` | NFS-Client einrichten & in fstab eintragen |
|
||||||
|
| `mountscript/mount-plus.sh` | Festplatte partitionieren, formatieren, einhängen |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Muster & Konventionen
|
||||||
|
- Secrets: `*-secret.yaml` je App, base64-kodiert
|
||||||
|
- Init-Container: Warten auf DB-Bereitschaft (Nextcloud, Immich)
|
||||||
|
- `imagePullPolicy: IfNotPresent` (kein automatisches Re-Pull)
|
||||||
|
- `nodeSelector: knode0` bei Nextcloud (Kernel 6.1 für NFS)
|
||||||
|
- fsGroup für NFS-Berechtigungen (z. B. `33` für www-data)
|
||||||
|
- GitLab-Runner-Token: `glrt-3nNma_nEvL1Bq2zc8m5Zu286MQpwOjIKdDozCnU6MTAQ`
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: mariadb-backup
|
||||||
|
namespace: nextcloud
|
||||||
|
spec:
|
||||||
|
nodeName: knode0 # <-- FIXED ON node0
|
||||||
|
containers:
|
||||||
|
- name: backup
|
||||||
|
image: busybox
|
||||||
|
command: ["sleep", "3600"]
|
||||||
|
volumeMounts:
|
||||||
|
- name: old
|
||||||
|
mountPath: /old
|
||||||
|
volumes:
|
||||||
|
- name: old
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: nextcloud-mariadb-pvc
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: config-migration
|
||||||
|
namespace: nextcloud
|
||||||
|
spec:
|
||||||
|
volumes:
|
||||||
|
- name: old-config
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: nextcloud-config-pvc
|
||||||
|
- name: new-config
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: nextcloud-config-pvc-longhorn
|
||||||
|
containers:
|
||||||
|
- name: migrator
|
||||||
|
image: alpine:3.18
|
||||||
|
volumeMounts:
|
||||||
|
- name: old-config
|
||||||
|
mountPath: /old
|
||||||
|
- name: new-config
|
||||||
|
mountPath: /new
|
||||||
|
command: ['sh']
|
||||||
|
args:
|
||||||
|
- -c
|
||||||
|
- |
|
||||||
|
echo "Copying config data..."
|
||||||
|
if [ "$(ls -A /old)" ]; then
|
||||||
|
cp -rv /old/* /new/ 2>/dev/null || true
|
||||||
|
echo "Config migration completed"
|
||||||
|
else
|
||||||
|
echo "Old config is empty"
|
||||||
|
fi
|
||||||
|
sleep infinity
|
||||||
|
restartPolicy: Never
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: apps-migration
|
||||||
|
namespace: nextcloud
|
||||||
|
spec:
|
||||||
|
volumes:
|
||||||
|
- name: old-apps
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: nextcloud-apps-pvc
|
||||||
|
- name: new-apps
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: nextcloud-apps-pvc-longhorn
|
||||||
|
containers:
|
||||||
|
- name: migrator
|
||||||
|
image: alpine:3.18
|
||||||
|
volumeMounts:
|
||||||
|
- name: old-apps
|
||||||
|
mountPath: /old
|
||||||
|
- name: new-apps
|
||||||
|
mountPath: /new
|
||||||
|
command: ['sh']
|
||||||
|
args:
|
||||||
|
- -c
|
||||||
|
- |
|
||||||
|
echo "Copying apps data..."
|
||||||
|
if [ "$(ls -A /old)" ]; then
|
||||||
|
cp -rv /old/* /new/ 2>/dev/null || true
|
||||||
|
echo "Apps migration completed"
|
||||||
|
else
|
||||||
|
echo "Old apps is empty"
|
||||||
|
fi
|
||||||
|
sleep infinity
|
||||||
|
restartPolicy: Never
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: nextcloud-mariadb-pvc-longhorn
|
||||||
|
namespace: nextcloud
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
storageClassName: longhorn
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 10Gi
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: mariadb-restore
|
||||||
|
namespace: nextcloud
|
||||||
|
spec:
|
||||||
|
nodeName: knode0
|
||||||
|
containers:
|
||||||
|
- name: restore
|
||||||
|
image: busybox
|
||||||
|
command: ["sleep", "3600"]
|
||||||
|
volumeMounts:
|
||||||
|
- name: new
|
||||||
|
mountPath: /new
|
||||||
|
- name: oldbackup
|
||||||
|
mountPath: /backup
|
||||||
|
volumes:
|
||||||
|
- name: new
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: nextcloud-mariadb-pvc-longhorn
|
||||||
|
- name: oldbackup
|
||||||
|
hostPath:
|
||||||
|
path: /var/lib/nextcloud/mariadb-backup
|
||||||
|
type: DirectoryOrCreate
|
||||||
@@ -28,6 +28,11 @@ spec:
|
|||||||
value: nextcloud
|
value: nextcloud
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 3306
|
- containerPort: 3306
|
||||||
|
readinessProbe:
|
||||||
|
tcpSocket:
|
||||||
|
port: 3306
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 10
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
memory: "256Mi"
|
memory: "256Mi"
|
||||||
@@ -41,4 +46,4 @@ spec:
|
|||||||
volumes:
|
volumes:
|
||||||
- name: mariadb-data
|
- name: mariadb-data
|
||||||
persistentVolumeClaim:
|
persistentVolumeClaim:
|
||||||
claimName: nextcloud-mariadb-pvc
|
claimName: nextcloud-mariadb-pvc-longhorn
|
||||||
@@ -9,24 +9,4 @@ spec:
|
|||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
storage: 10Gi
|
storage: 10Gi
|
||||||
storageClassName: local-path # <-- explicit default StorageClass
|
storageClassName: longhorn
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: PersistentVolume
|
|
||||||
metadata:
|
|
||||||
name: nextcloud-mariadb-pv
|
|
||||||
labels:
|
|
||||||
app: mariadb
|
|
||||||
spec:
|
|
||||||
capacity:
|
|
||||||
storage: 10Gi
|
|
||||||
accessModes:
|
|
||||||
- ReadWriteOnce
|
|
||||||
persistentVolumeReclaimPolicy: Retain
|
|
||||||
storageClassName: local-path
|
|
||||||
claimRef:
|
|
||||||
namespace: nextcloud
|
|
||||||
name: nextcloud-mariadb-pvc
|
|
||||||
hostPath:
|
|
||||||
path: /var/lib/nextcloud/mariadb-data
|
|
||||||
type: DirectoryOrCreate
|
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: nextcloud-apps-pvc-longhorn
|
||||||
|
namespace: nextcloud
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
storageClassName: longhorn
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 1Gi
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: nextcloud-config-pvc-longhorn
|
||||||
|
namespace: nextcloud
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
storageClassName: longhorn
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 5Gi
|
||||||
@@ -16,12 +16,19 @@ spec:
|
|||||||
# fsGroup sorgt dafür, dass gemountete Volumes die Gruppe www-data (33) bekommen
|
# fsGroup sorgt dafür, dass gemountete Volumes die Gruppe www-data (33) bekommen
|
||||||
securityContext:
|
securityContext:
|
||||||
fsGroup: 33
|
fsGroup: 33
|
||||||
|
# Auf knode0 zwingen (hat Kernel 6.1 mit NFS - Kompatibilität)
|
||||||
|
nodeSelector:
|
||||||
|
kubernetes.io/hostname: knode0
|
||||||
# hostAliases mappt die öffentliche Domain intern auf die Service-ClusterIP,
|
# hostAliases mappt die öffentliche Domain intern auf die Service-ClusterIP,
|
||||||
# damit der Pod henryathome.home64.de direkt intern erreicht (vermeidet externe Loopback/Firewall/403)
|
# damit der Pod henryathome.home64.de direkt intern erreicht (vermeidet externe Loopback/Firewall/403)
|
||||||
hostAliases:
|
hostAliases:
|
||||||
- ip: "10.43.107.87"
|
- ip: "10.43.107.87"
|
||||||
hostnames:
|
hostnames:
|
||||||
- "henryathome.home64.de"
|
- "henryathome.home64.de"
|
||||||
|
initContainers:
|
||||||
|
- name: wait-for-mariadb
|
||||||
|
image: busybox:1.34
|
||||||
|
command: ['sh', '-c', 'until nc -z mariadb.nextcloud.svc.cluster.local 3306; do echo waiting for mariadb; sleep 2; done;']
|
||||||
containers:
|
containers:
|
||||||
- name: nextcloud
|
- name: nextcloud
|
||||||
image: nextcloud:33-apache
|
image: nextcloud:33-apache
|
||||||
@@ -44,15 +51,15 @@ spec:
|
|||||||
- name: REDIS_HOST
|
- name: REDIS_HOST
|
||||||
value: redis.nextcloud.svc.cluster.local
|
value: redis.nextcloud.svc.cluster.local
|
||||||
- name: NEXTCLOUD_TRUSTED_DOMAINS
|
- name: NEXTCLOUD_TRUSTED_DOMAINS
|
||||||
value: "henryathome.home64.de,192.168.178.0/24,192.168.178.138,nextcloud.nextcloud.svc.cluster.local"
|
value: "cloud.henryathome.home64.de,192.168.178.0/24,192.168.178.138,nextcloud.nextcloud.svc.cluster.local"
|
||||||
- name: TRUSTED_PROXIES
|
- name: TRUSTED_PROXIES
|
||||||
value: "192.168.178.120"
|
value: "192.168.178.120"
|
||||||
- name: OVERWRITEHOST
|
- name: OVERWRITEHOST
|
||||||
value: "henryathome.home64.de"
|
value: "cloud.henryathome.home64.de"
|
||||||
- name: OVERWRITEPROTOCOL
|
- name: OVERWRITEPROTOCOL
|
||||||
value: "https"
|
value: "https"
|
||||||
- name: OVERWRITECLIURL
|
- name: OVERWRITECLIURL
|
||||||
value: "https://henryathome.home64.de"
|
value: "https://cloud.henryathome.home64.de"
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
memory: "512Mi"
|
memory: "512Mi"
|
||||||
@@ -73,7 +80,7 @@ spec:
|
|||||||
claimName: nextcloud-data-pvc
|
claimName: nextcloud-data-pvc
|
||||||
- name: config
|
- name: config
|
||||||
persistentVolumeClaim:
|
persistentVolumeClaim:
|
||||||
claimName: nextcloud-config-pvc
|
claimName: nextcloud-config-pvc-longhorn
|
||||||
- name: apps
|
- name: apps
|
||||||
persistentVolumeClaim:
|
persistentVolumeClaim:
|
||||||
claimName: nextcloud-apps-pvc
|
claimName: nextcloud-apps-pvc-longhorn
|
||||||
@@ -10,10 +10,13 @@ spec:
|
|||||||
persistentVolumeReclaimPolicy: Retain
|
persistentVolumeReclaimPolicy: Retain
|
||||||
storageClassName: nfs
|
storageClassName: nfs
|
||||||
mountOptions:
|
mountOptions:
|
||||||
- vers=4
|
- vers=3
|
||||||
- rsize=65536
|
- rsize=65536
|
||||||
- wsize=65536
|
- wsize=65536
|
||||||
- noatime
|
- noatime
|
||||||
|
- soft
|
||||||
|
- timeo=20
|
||||||
|
- retrans=2
|
||||||
nfs:
|
nfs:
|
||||||
server: 192.168.178.186 # <-- ERSETZEN: IP oder Hostname deiner NAS
|
server: 192.168.178.186 # <-- ERSETZEN: IP oder Hostname deiner NAS
|
||||||
path: /volume1/Nextcloud/data # <-- ERSETZEN: Pfad zum Share auf der NAS (z.B. /volume1/nextcloud)
|
path: /volume1/Nextcloud/data # <-- ERSETZEN: Pfad zum Share auf der NAS (z.B. /volume1/nextcloud)
|
||||||
|
|||||||
+495
@@ -0,0 +1,495 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
NAMESPACE="authentik"
|
||||||
|
RELEASE="authentik"
|
||||||
|
CHART="authentik/authentik"
|
||||||
|
VALUES_FILE="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/values.yaml"
|
||||||
|
TARGET_PVC="authentik-postgresql-longhorn-pvc"
|
||||||
|
LONGHORN_STORAGE_CLASS="longhorn"
|
||||||
|
JOB_NAME="authentik-postgres-migrate-to-longhorn"
|
||||||
|
|
||||||
|
PVC_BIND_TIMEOUT_SECONDS=900
|
||||||
|
POD_STOP_TIMEOUT_SECONDS=300
|
||||||
|
JOB_TIMEOUT_SECONDS=3600
|
||||||
|
POSTGRES_READY_TIMEOUT_SECONDS=300
|
||||||
|
|
||||||
|
DRY_RUN="false"
|
||||||
|
AUTO_CONFIRM="false"
|
||||||
|
|
||||||
|
ORIGINAL_POSTGRES_REPLICAS="1"
|
||||||
|
SOURCE_NODE_NAME=""
|
||||||
|
SOURCE_PVC=""
|
||||||
|
SOURCE_STORAGE=""
|
||||||
|
POSTGRES_STS=""
|
||||||
|
POSTGRES_POD=""
|
||||||
|
|
||||||
|
DEPLOYMENTS_TO_RESTORE=()
|
||||||
|
DEPLOYMENT_REPLICAS=()
|
||||||
|
AUTHENTIK_SCALED_DOWN="false"
|
||||||
|
POSTGRES_SCALED_DOWN="false"
|
||||||
|
|
||||||
|
info() {
|
||||||
|
echo "[INFO] $*"
|
||||||
|
}
|
||||||
|
|
||||||
|
warn() {
|
||||||
|
echo "[WARN] $*" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
err() {
|
||||||
|
echo "[ERROR] $*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'EOF'
|
||||||
|
Usage: migrate-postgres-to-longhorn.sh [options]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--namespace <name> Kubernetes namespace (default: authentik)
|
||||||
|
--release <name> Helm release name (default: authentik)
|
||||||
|
--chart <ref> Helm chart ref/path (default: authentik/authentik)
|
||||||
|
--values <path> Helm values file to use (default: ./values.yaml)
|
||||||
|
--target-pvc <name> Target Longhorn PVC name (default: authentik-postgresql-longhorn-pvc)
|
||||||
|
--storage-class <name> StorageClass for target PVC (default: longhorn)
|
||||||
|
--dry-run Print plan without changing cluster state
|
||||||
|
--yes Skip interactive confirmation
|
||||||
|
--help Show this help
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_args() {
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--namespace)
|
||||||
|
NAMESPACE="$2"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--release)
|
||||||
|
RELEASE="$2"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--chart)
|
||||||
|
CHART="$2"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--values)
|
||||||
|
VALUES_FILE="$2"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--target-pvc)
|
||||||
|
TARGET_PVC="$2"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--storage-class)
|
||||||
|
LONGHORN_STORAGE_CLASS="$2"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--dry-run)
|
||||||
|
DRY_RUN="true"
|
||||||
|
;;
|
||||||
|
--yes)
|
||||||
|
AUTO_CONFIRM="true"
|
||||||
|
;;
|
||||||
|
--help|-h)
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
err "Unknown argument: $1"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
require_tool() {
|
||||||
|
local tool="$1"
|
||||||
|
command -v "${tool}" >/dev/null 2>&1 || err "Required tool not found: ${tool}"
|
||||||
|
}
|
||||||
|
|
||||||
|
require_file() {
|
||||||
|
local path="$1"
|
||||||
|
[[ -f "${path}" ]] || err "Required file not found: ${path}"
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for_pvc_bound() {
|
||||||
|
local pvc_name="$1"
|
||||||
|
local timeout="$2"
|
||||||
|
local elapsed=0
|
||||||
|
local interval=5
|
||||||
|
|
||||||
|
info "Waiting for PVC ${pvc_name} to become Bound..."
|
||||||
|
while true; do
|
||||||
|
local phase
|
||||||
|
phase="$(kubectl -n "${NAMESPACE}" get pvc "${pvc_name}" -o jsonpath='{.status.phase}' 2>/dev/null || true)"
|
||||||
|
|
||||||
|
if [[ "${phase}" == "Bound" ]]; then
|
||||||
|
info "PVC ${pvc_name} is Bound."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( elapsed >= timeout )); then
|
||||||
|
err "Timeout while waiting for PVC ${pvc_name} to bind."
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep "${interval}"
|
||||||
|
elapsed=$((elapsed + interval))
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for_statefulset_replicas() {
|
||||||
|
local sts_name="$1"
|
||||||
|
local expected="$2"
|
||||||
|
local timeout="$3"
|
||||||
|
local elapsed=0
|
||||||
|
local interval=5
|
||||||
|
|
||||||
|
info "Waiting for StatefulSet ${sts_name} to reach ${expected} ready replicas..."
|
||||||
|
while true; do
|
||||||
|
local ready
|
||||||
|
ready="$(kubectl -n "${NAMESPACE}" get sts "${sts_name}" -o jsonpath='{.status.readyReplicas}' 2>/dev/null || true)"
|
||||||
|
ready="${ready:-0}"
|
||||||
|
|
||||||
|
if [[ "${ready}" == "${expected}" ]]; then
|
||||||
|
info "StatefulSet ${sts_name} has ${expected} ready replicas."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( elapsed >= timeout )); then
|
||||||
|
err "Timeout while waiting for StatefulSet ${sts_name} ready replicas ${expected}."
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep "${interval}"
|
||||||
|
elapsed=$((elapsed + interval))
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for_no_pods_for_release() {
|
||||||
|
local selector="$1"
|
||||||
|
local timeout="$2"
|
||||||
|
local elapsed=0
|
||||||
|
local interval=5
|
||||||
|
|
||||||
|
info "Waiting for pods with selector ${selector} to stop..."
|
||||||
|
while true; do
|
||||||
|
local count
|
||||||
|
count="$(kubectl -n "${NAMESPACE}" get pods -l "${selector}" --no-headers 2>/dev/null | awk '$3 != "Completed" && $3 != "Succeeded" {count++} END {print count+0}')"
|
||||||
|
|
||||||
|
if [[ "${count}" == "0" ]]; then
|
||||||
|
info "No active pods left for selector ${selector}."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( elapsed >= timeout )); then
|
||||||
|
err "Timeout while waiting for pods with selector ${selector} to stop."
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep "${interval}"
|
||||||
|
elapsed=$((elapsed + interval))
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
discover_postgres_runtime() {
|
||||||
|
POSTGRES_STS="$(kubectl -n "${NAMESPACE}" get sts -l "app.kubernetes.io/name=postgresql,app.kubernetes.io/instance=${RELEASE}" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || true)"
|
||||||
|
[[ -n "${POSTGRES_STS}" ]] || err "Could not find PostgreSQL StatefulSet for release ${RELEASE}."
|
||||||
|
|
||||||
|
POSTGRES_POD="${POSTGRES_STS}-0"
|
||||||
|
|
||||||
|
SOURCE_PVC="$(kubectl -n "${NAMESPACE}" get sts "${POSTGRES_STS}" -o jsonpath='{.spec.volumeClaimTemplates[0].metadata.name}' 2>/dev/null || true)"
|
||||||
|
[[ -n "${SOURCE_PVC}" ]] || err "Could not detect PostgreSQL volumeClaimTemplate name from StatefulSet ${POSTGRES_STS}."
|
||||||
|
SOURCE_PVC="${SOURCE_PVC}-${POSTGRES_STS}-0"
|
||||||
|
kubectl -n "${NAMESPACE}" get pvc "${SOURCE_PVC}" >/dev/null 2>&1 || err "Source PVC not found: ${SOURCE_PVC}"
|
||||||
|
|
||||||
|
SOURCE_NODE_NAME="$(kubectl -n "${NAMESPACE}" get pod "${POSTGRES_POD}" -o jsonpath='{.spec.nodeName}' 2>/dev/null || true)"
|
||||||
|
|
||||||
|
SOURCE_STORAGE="$(kubectl -n "${NAMESPACE}" get pvc "${SOURCE_PVC}" -o jsonpath='{.spec.resources.requests.storage}' 2>/dev/null || true)"
|
||||||
|
[[ -n "${SOURCE_STORAGE}" ]] || err "Could not read requested storage size from source PVC ${SOURCE_PVC}."
|
||||||
|
|
||||||
|
ORIGINAL_POSTGRES_REPLICAS="$(kubectl -n "${NAMESPACE}" get sts "${POSTGRES_STS}" -o jsonpath='{.spec.replicas}' 2>/dev/null || true)"
|
||||||
|
ORIGINAL_POSTGRES_REPLICAS="${ORIGINAL_POSTGRES_REPLICAS:-1}"
|
||||||
|
|
||||||
|
info "Detected PostgreSQL StatefulSet: ${POSTGRES_STS}"
|
||||||
|
info "Detected source PVC: ${SOURCE_PVC}"
|
||||||
|
if [[ -n "${SOURCE_NODE_NAME}" ]]; then
|
||||||
|
info "Detected source node: ${SOURCE_NODE_NAME}"
|
||||||
|
else
|
||||||
|
info "Source PostgreSQL pod currently not running, proceeding without node pinning"
|
||||||
|
fi
|
||||||
|
info "Detected source requested storage: ${SOURCE_STORAGE}"
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_target_pvc() {
|
||||||
|
info "Ensuring Longhorn target PVC ${TARGET_PVC} exists."
|
||||||
|
|
||||||
|
cat <<EOF | kubectl apply -f - >/dev/null
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: ${TARGET_PVC}
|
||||||
|
namespace: ${NAMESPACE}
|
||||||
|
spec:
|
||||||
|
storageClassName: ${LONGHORN_STORAGE_CLASS}
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: ${SOURCE_STORAGE}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
wait_for_pvc_bound "${TARGET_PVC}" "${PVC_BIND_TIMEOUT_SECONDS}"
|
||||||
|
}
|
||||||
|
|
||||||
|
scale_down_authentik() {
|
||||||
|
local deployment_lines
|
||||||
|
deployment_lines="$(kubectl -n "${NAMESPACE}" get deploy -l "app.kubernetes.io/instance=${RELEASE}" -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.replicas}{"\n"}{end}' 2>/dev/null || true)"
|
||||||
|
|
||||||
|
if [[ -z "${deployment_lines}" ]]; then
|
||||||
|
warn "No Authentik deployments found for release ${RELEASE}."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
while IFS=$'\t' read -r name replicas; do
|
||||||
|
[[ -n "${name}" ]] || continue
|
||||||
|
replicas="${replicas:-1}"
|
||||||
|
DEPLOYMENTS_TO_RESTORE+=("${name}")
|
||||||
|
DEPLOYMENT_REPLICAS+=("${replicas}")
|
||||||
|
if [[ "${replicas}" != "0" ]]; then
|
||||||
|
info "Scaling deployment ${name} to 0."
|
||||||
|
kubectl -n "${NAMESPACE}" scale deploy "${name}" --replicas=0 >/dev/null
|
||||||
|
fi
|
||||||
|
done <<< "${deployment_lines}"
|
||||||
|
|
||||||
|
AUTHENTIK_SCALED_DOWN="true"
|
||||||
|
wait_for_no_pods_for_release "app.kubernetes.io/instance=${RELEASE},app.kubernetes.io/name=authentik" "${POD_STOP_TIMEOUT_SECONDS}"
|
||||||
|
}
|
||||||
|
|
||||||
|
scale_down_postgres() {
|
||||||
|
info "Scaling StatefulSet ${POSTGRES_STS} to 0 for consistent copy."
|
||||||
|
kubectl -n "${NAMESPACE}" scale sts "${POSTGRES_STS}" --replicas=0 >/dev/null
|
||||||
|
POSTGRES_SCALED_DOWN="true"
|
||||||
|
wait_for_statefulset_replicas "${POSTGRES_STS}" "0" "${POD_STOP_TIMEOUT_SECONDS}"
|
||||||
|
}
|
||||||
|
|
||||||
|
run_migration_job() {
|
||||||
|
info "Recreating migration job ${JOB_NAME}."
|
||||||
|
kubectl -n "${NAMESPACE}" delete job "${JOB_NAME}" --ignore-not-found >/dev/null
|
||||||
|
|
||||||
|
cat <<EOF | kubectl apply -f - >/dev/null
|
||||||
|
apiVersion: batch/v1
|
||||||
|
kind: Job
|
||||||
|
metadata:
|
||||||
|
name: ${JOB_NAME}
|
||||||
|
namespace: ${NAMESPACE}
|
||||||
|
spec:
|
||||||
|
backoffLimit: 2
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
name: ${JOB_NAME}
|
||||||
|
spec:
|
||||||
|
restartPolicy: Never
|
||||||
|
containers:
|
||||||
|
- name: migrate
|
||||||
|
image: alpine:3.20
|
||||||
|
command:
|
||||||
|
- sh
|
||||||
|
- -c
|
||||||
|
- |
|
||||||
|
set -euo pipefail
|
||||||
|
apk add --no-cache rsync findutils coreutils
|
||||||
|
|
||||||
|
src_count="\$(find /source -mindepth 1 | wc -l | tr -d ' ')"
|
||||||
|
echo "Source entries before copy: \${src_count}"
|
||||||
|
|
||||||
|
rsync -aHAX --numeric-ids --delete /source/ /target/
|
||||||
|
|
||||||
|
target_count="\$(find /target -mindepth 1 | wc -l | tr -d ' ')"
|
||||||
|
echo "Target entries after copy: \${target_count}"
|
||||||
|
|
||||||
|
if [ -f /target/PG_VERSION ] && [ -d /target/base ]; then
|
||||||
|
pg_root="/target"
|
||||||
|
elif [ -f /target/data/PG_VERSION ] && [ -d /target/data/base ]; then
|
||||||
|
pg_root="/target/data"
|
||||||
|
else
|
||||||
|
echo "Could not find PostgreSQL data root on target" >&2
|
||||||
|
echo "Top-level target contents:" >&2
|
||||||
|
ls -la /target >&2 || true
|
||||||
|
echo "Nested target/data contents:" >&2
|
||||||
|
ls -la /target/data >&2 || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
test "\${target_count}" -gt 0
|
||||||
|
echo "Detected PostgreSQL data root: \${pg_root}"
|
||||||
|
|
||||||
|
echo "Top-level target contents:"
|
||||||
|
ls -la /target
|
||||||
|
echo "Migration verification successful"
|
||||||
|
volumeMounts:
|
||||||
|
- name: source
|
||||||
|
mountPath: /source
|
||||||
|
readOnly: true
|
||||||
|
- name: target
|
||||||
|
mountPath: /target
|
||||||
|
volumes:
|
||||||
|
- name: source
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: ${SOURCE_PVC}
|
||||||
|
- name: target
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: ${TARGET_PVC}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
info "Streaming migration job logs."
|
||||||
|
kubectl -n "${NAMESPACE}" logs -f "job/${JOB_NAME}" || true
|
||||||
|
|
||||||
|
info "Waiting for migration job completion."
|
||||||
|
if ! kubectl -n "${NAMESPACE}" wait --for=condition=complete "job/${JOB_NAME}" --timeout="${JOB_TIMEOUT_SECONDS}s" >/dev/null; then
|
||||||
|
kubectl -n "${NAMESPACE}" describe job "${JOB_NAME}" || true
|
||||||
|
kubectl -n "${NAMESPACE}" logs "job/${JOB_NAME}" --tail=-1 || true
|
||||||
|
err "Migration job failed."
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "Migration job completed."
|
||||||
|
}
|
||||||
|
|
||||||
|
verify_target_pvc_has_data() {
|
||||||
|
info "Verifying copied PostgreSQL markers on target PVC ${TARGET_PVC}."
|
||||||
|
kubectl -n "${NAMESPACE}" run authentik-postgres-verify \
|
||||||
|
--rm --restart=Never --image=alpine:3.20 \
|
||||||
|
--overrides="{\"apiVersion\":\"v1\",\"spec\":{\"containers\":[{\"name\":\"verify\",\"image\":\"alpine:3.20\",\"command\":[\"sh\",\"-c\",\"set -e; if [ -f /target/PG_VERSION ] && [ -d /target/base ]; then root=/target; elif [ -f /target/data/PG_VERSION ] && [ -d /target/data/base ]; then root=/target/data; else echo missing-postgres-layout >&2; ls -la /target >&2 || true; ls -la /target/data >&2 || true; exit 1; fi; echo detected-root=\$root; find /target -mindepth 1 | wc -l; ls -la /target | head -n 20\"],\"volumeMounts\":[{\"name\":\"target\",\"mountPath\":\"/target\"}]}],\"volumes\":[{\"name\":\"target\",\"persistentVolumeClaim\":{\"claimName\":\"${TARGET_PVC}\"}}]}}" \
|
||||||
|
--attach=true >/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
helm_switch_postgres_to_target_pvc() {
|
||||||
|
info "Upgrading Helm release ${RELEASE} to use existing PostgreSQL claim ${TARGET_PVC}."
|
||||||
|
helm -n "${NAMESPACE}" upgrade --install "${RELEASE}" "${CHART}" \
|
||||||
|
-f "${VALUES_FILE}" \
|
||||||
|
--reuse-values \
|
||||||
|
--set "postgresql.primary.persistence.enabled=true" \
|
||||||
|
--set "postgresql.primary.persistence.existingClaim=${TARGET_PVC}" \
|
||||||
|
--set "authentik.existingSecret.secretName=" >/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
verify_postgres_uses_target_claim() {
|
||||||
|
info "Waiting for PostgreSQL StatefulSet to become ready after Helm upgrade."
|
||||||
|
wait_for_statefulset_replicas "${POSTGRES_STS}" "1" "${POSTGRES_READY_TIMEOUT_SECONDS}"
|
||||||
|
|
||||||
|
local mounted_claim
|
||||||
|
mounted_claim="$(kubectl -n "${NAMESPACE}" get pod "${POSTGRES_STS}-0" -o jsonpath='{.spec.volumes[?(@.name=="data")].persistentVolumeClaim.claimName}' 2>/dev/null || true)"
|
||||||
|
if [[ -z "${mounted_claim}" ]]; then
|
||||||
|
mounted_claim="$(kubectl -n "${NAMESPACE}" get pod "${POSTGRES_STS}-0" -o jsonpath='{.spec.volumes[?(@.persistentVolumeClaim)].persistentVolumeClaim.claimName}' 2>/dev/null | awk '{print $1}')"
|
||||||
|
fi
|
||||||
|
|
||||||
|
[[ -n "${mounted_claim}" ]] || err "Could not detect mounted PostgreSQL claim after upgrade."
|
||||||
|
[[ "${mounted_claim}" == "${TARGET_PVC}" ]] || err "PostgreSQL pod mounts ${mounted_claim}, expected ${TARGET_PVC}."
|
||||||
|
|
||||||
|
info "PostgreSQL now mounts target claim ${mounted_claim}."
|
||||||
|
}
|
||||||
|
|
||||||
|
restore_authentik_deployments() {
|
||||||
|
if [[ "${AUTHENTIK_SCALED_DOWN}" != "true" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local index
|
||||||
|
for index in "${!DEPLOYMENTS_TO_RESTORE[@]}"; do
|
||||||
|
local name="${DEPLOYMENTS_TO_RESTORE[${index}]}"
|
||||||
|
local replicas="${DEPLOYMENT_REPLICAS[${index}]}"
|
||||||
|
if [[ "${replicas}" == "0" ]]; then
|
||||||
|
info "Skipping restore of deployment ${name} to 0 replicas."
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
info "Restoring deployment ${name} replicas to ${replicas}."
|
||||||
|
kubectl -n "${NAMESPACE}" scale deploy "${name}" --replicas="${replicas}" >/dev/null
|
||||||
|
done
|
||||||
|
|
||||||
|
AUTHENTIK_SCALED_DOWN="false"
|
||||||
|
}
|
||||||
|
|
||||||
|
restore_on_error() {
|
||||||
|
if [[ "${POSTGRES_SCALED_DOWN}" == "true" ]]; then
|
||||||
|
warn "Restoring StatefulSet ${POSTGRES_STS} replicas to ${ORIGINAL_POSTGRES_REPLICAS}."
|
||||||
|
kubectl -n "${NAMESPACE}" scale sts "${POSTGRES_STS}" --replicas="${ORIGINAL_POSTGRES_REPLICAS}" >/dev/null || true
|
||||||
|
POSTGRES_SCALED_DOWN="false"
|
||||||
|
fi
|
||||||
|
|
||||||
|
restore_authentik_deployments || true
|
||||||
|
}
|
||||||
|
|
||||||
|
on_exit() {
|
||||||
|
local exit_code=$?
|
||||||
|
if [[ ${exit_code} -ne 0 ]]; then
|
||||||
|
restore_on_error
|
||||||
|
warn "Script failed with exit code ${exit_code}."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
trap on_exit EXIT
|
||||||
|
|
||||||
|
validate_prerequisites() {
|
||||||
|
require_tool kubectl
|
||||||
|
require_tool helm
|
||||||
|
require_file "${VALUES_FILE}"
|
||||||
|
|
||||||
|
kubectl get namespace "${NAMESPACE}" >/dev/null 2>&1 || err "Namespace does not exist: ${NAMESPACE}"
|
||||||
|
helm -n "${NAMESPACE}" status "${RELEASE}" >/dev/null 2>&1 || err "Helm release not found: ${RELEASE} in namespace ${NAMESPACE}"
|
||||||
|
}
|
||||||
|
|
||||||
|
confirm_migration() {
|
||||||
|
if [[ "${AUTO_CONFIRM}" == "true" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
warn "Authentik and PostgreSQL workloads in namespace ${NAMESPACE} will be scaled down during migration."
|
||||||
|
read -r -p "Type MIGRATE to continue: " confirmation
|
||||||
|
if [[ "${confirmation}" != "MIGRATE" ]]; then
|
||||||
|
err "Confirmation failed. Aborted by user."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
print_dry_run_plan() {
|
||||||
|
info "Dry-run mode active. No cluster changes will be made."
|
||||||
|
info "Planned steps:"
|
||||||
|
info "1) Discover PostgreSQL StatefulSet, pod, source PVC, and size"
|
||||||
|
info "2) Create Longhorn target PVC ${TARGET_PVC}"
|
||||||
|
info "3) Scale down Authentik deployments and PostgreSQL StatefulSet"
|
||||||
|
info "4) Run rsync migration job ${JOB_NAME}"
|
||||||
|
info "5) Verify copied DB files on target PVC"
|
||||||
|
info "6) Helm upgrade release ${RELEASE} with postgresql.primary.persistence.existingClaim=${TARGET_PVC}"
|
||||||
|
info "7) Verify new PostgreSQL pod uses target PVC"
|
||||||
|
info "8) Restore Authentik deployments"
|
||||||
|
}
|
||||||
|
|
||||||
|
run_migration() {
|
||||||
|
discover_postgres_runtime
|
||||||
|
|
||||||
|
if [[ "${DRY_RUN}" == "true" ]]; then
|
||||||
|
print_dry_run_plan
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
confirm_migration
|
||||||
|
ensure_target_pvc
|
||||||
|
scale_down_authentik
|
||||||
|
scale_down_postgres
|
||||||
|
run_migration_job
|
||||||
|
verify_target_pvc_has_data
|
||||||
|
helm_switch_postgres_to_target_pvc
|
||||||
|
POSTGRES_SCALED_DOWN="false"
|
||||||
|
verify_postgres_uses_target_claim
|
||||||
|
restore_authentik_deployments
|
||||||
|
|
||||||
|
info "Migration finished successfully."
|
||||||
|
info "PostgreSQL is now running on Longhorn PVC ${TARGET_PVC}."
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
parse_args "$@"
|
||||||
|
info "Starting Authentik PostgreSQL migration to Longhorn PVC."
|
||||||
|
validate_prerequisites
|
||||||
|
run_migration
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
@@ -2,6 +2,8 @@
|
|||||||
namespace: authentik
|
namespace: authentik
|
||||||
authentik:
|
authentik:
|
||||||
secret_key: "6sNotXqR3cvcVHx3RbYCViX6J/OmMvopb4b7ge80V3EdSgBtWzG0l4SXBPo80J3mRy0BDaCCfb1EZoz+"
|
secret_key: "6sNotXqR3cvcVHx3RbYCViX6J/OmMvopb4b7ge80V3EdSgBtWzG0l4SXBPo80J3mRy0BDaCCfb1EZoz+"
|
||||||
|
existingSecret:
|
||||||
|
secretName: ""
|
||||||
# This sends anonymous usage-data, stack traces on errors and
|
# This sends anonymous usage-data, stack traces on errors and
|
||||||
# performance data to sentry.io, and is fully opt-in
|
# performance data to sentry.io, and is fully opt-in
|
||||||
error_reporting:
|
error_reporting:
|
||||||
@@ -9,6 +11,16 @@ authentik:
|
|||||||
postgresql:
|
postgresql:
|
||||||
password: "WoPbKRCEeLoLb9J840FqwDE95ergX8CqXq7jC6nbJkoNSiTSlA"
|
password: "WoPbKRCEeLoLb9J840FqwDE95ergX8CqXq7jC6nbJkoNSiTSlA"
|
||||||
|
|
||||||
|
global:
|
||||||
|
volumeMounts:
|
||||||
|
- name: authentik-rbac-migration-fix
|
||||||
|
mountPath: /authentik/rbac/migrations/0010_remove_role_group_alter_role_name.py
|
||||||
|
subPath: 0010_remove_role_group_alter_role_name.py
|
||||||
|
volumes:
|
||||||
|
- name: authentik-rbac-migration-fix
|
||||||
|
configMap:
|
||||||
|
name: authentik-rbac-migration-fix
|
||||||
|
|
||||||
server:
|
server:
|
||||||
ingress:
|
ingress:
|
||||||
# Specify kubernetes ingress controller class name
|
# Specify kubernetes ingress controller class name
|
||||||
@@ -25,4 +37,38 @@ server:
|
|||||||
postgresql:
|
postgresql:
|
||||||
enabled: true
|
enabled: true
|
||||||
auth:
|
auth:
|
||||||
password: "WoPbKRCEeLoLb9J840FqwDE95ergX8CqXq7jC6nbJkoNSiTSlA"
|
password: "WoPbKRCEeLoLb9J840FqwDE95ergX8CqXq7jC6nbJkoNSiTSlA"
|
||||||
|
primary:
|
||||||
|
persistence:
|
||||||
|
enabled: true
|
||||||
|
existingClaim: authentik-postgresql-longhorn-pvc
|
||||||
|
|
||||||
|
additionalObjects:
|
||||||
|
- apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: authentik-rbac-migration-fix
|
||||||
|
namespace: authentik
|
||||||
|
data:
|
||||||
|
0010_remove_role_group_alter_role_name.py: |
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("authentik_rbac", "0009_remove_initialpermissions_mode"),
|
||||||
|
("authentik_core", "0056_user_roles"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name="role",
|
||||||
|
name="group",
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="role",
|
||||||
|
name="name",
|
||||||
|
field=models.TextField(unique=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -99,7 +99,7 @@ spec:
|
|||||||
volumes:
|
volumes:
|
||||||
- name: gitlab-data # lokal (postgresql, redis, etc.)
|
- name: gitlab-data # lokal (postgresql, redis, etc.)
|
||||||
persistentVolumeClaim:
|
persistentVolumeClaim:
|
||||||
claimName: gitlab-data-pvc
|
claimName: gitlab-data-longhorn-pvc
|
||||||
|
|
||||||
- name: gitlab-git # NFS (Git-Repositories)
|
- name: gitlab-git # NFS (Git-Repositories)
|
||||||
persistentVolumeClaim:
|
persistentVolumeClaim:
|
||||||
@@ -107,7 +107,7 @@ spec:
|
|||||||
|
|
||||||
- name: gitlab-config # lokal
|
- name: gitlab-config # lokal
|
||||||
persistentVolumeClaim:
|
persistentVolumeClaim:
|
||||||
claimName: gitlab-config-pvc
|
claimName: gitlab-config-longhorn-pvc
|
||||||
|
|
||||||
- name: gitlab-logs # ephemeral
|
- name: gitlab-logs # ephemeral
|
||||||
emptyDir: {}
|
emptyDir: {}
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
apiVersion: batch/v1
|
||||||
|
kind: Job
|
||||||
|
metadata:
|
||||||
|
name: gitlab-migrate-to-longhorn
|
||||||
|
namespace: gitlab
|
||||||
|
spec:
|
||||||
|
backoffLimit: 4
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
name: gitlab-migrate-to-longhorn
|
||||||
|
spec:
|
||||||
|
restartPolicy: Never
|
||||||
|
containers:
|
||||||
|
- name: migrate
|
||||||
|
image: alpine:3.18
|
||||||
|
command:
|
||||||
|
- sh
|
||||||
|
- -c
|
||||||
|
- |
|
||||||
|
set -euo pipefail
|
||||||
|
apk add --no-cache rsync
|
||||||
|
echo "Starting migration: /var/opt/gitlab (data)"
|
||||||
|
rsync -aHAX --numeric-ids --delete /var/opt_gitlab_old/ /var/opt_gitlab_new/
|
||||||
|
echo "Finished data copy. Starting config copy: /etc/gitlab"
|
||||||
|
rsync -aHAX --numeric-ids --delete /etc_gitlab_old/ /etc_gitlab_new/
|
||||||
|
echo "Migration complete"
|
||||||
|
volumeMounts:
|
||||||
|
- name: old-data
|
||||||
|
mountPath: /var/opt_gitlab_old
|
||||||
|
- name: new-data
|
||||||
|
mountPath: /var/opt_gitlab_new
|
||||||
|
- name: old-config
|
||||||
|
mountPath: /etc_gitlab_old
|
||||||
|
- name: new-config
|
||||||
|
mountPath: /etc_gitlab_new
|
||||||
|
volumes:
|
||||||
|
- name: old-data
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: gitlab-data-pvc
|
||||||
|
- name: new-data
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: gitlab-data-longhorn-pvc
|
||||||
|
- name: old-config
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: gitlab-config-pvc
|
||||||
|
- name: new-config
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: gitlab-config-longhorn-pvc
|
||||||
@@ -96,3 +96,32 @@ spec:
|
|||||||
requests:
|
requests:
|
||||||
storage: 1Gi
|
storage: 1Gi
|
||||||
volumeName: gitlab-config-pv
|
volumeName: gitlab-config-pv
|
||||||
|
|
||||||
|
---
|
||||||
|
# ─── Neue Longhorn-PVCs zum Migrieren der Daten (dynamisch provisioniert) ─
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: gitlab-data-longhorn-pvc
|
||||||
|
namespace: gitlab
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
storageClassName: longhorn
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 20Gi
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: gitlab-config-longhorn-pvc
|
||||||
|
namespace: gitlab
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
storageClassName: longhorn
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 1Gi
|
||||||
|
|||||||
@@ -0,0 +1,139 @@
|
|||||||
|
# ─── ServiceAccount ───────────────────────────────────────────────
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: gitlab-runner
|
||||||
|
namespace: gitlab
|
||||||
|
|
||||||
|
---
|
||||||
|
# ─── Role ─────────────────────────────────────────────────────────
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
name: gitlab-runner
|
||||||
|
namespace: gitlab
|
||||||
|
rules:
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["pods", "pods/exec", "pods/attach", "pods/log", "secrets", "configmaps", "services"]
|
||||||
|
verbs: ["get", "list", "watch", "create", "delete", "update", "patch"]
|
||||||
|
|
||||||
|
---
|
||||||
|
# ─── RoleBinding ──────────────────────────────────────────────────
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
name: gitlab-runner
|
||||||
|
namespace: gitlab
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: gitlab-runner
|
||||||
|
namespace: gitlab
|
||||||
|
roleRef:
|
||||||
|
kind: Role
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
name: gitlab-runner
|
||||||
|
|
||||||
|
---
|
||||||
|
# ─── Secret (Runner Authentication Token, GitLab 16+) ────────────
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: gitlab-runner-secret
|
||||||
|
namespace: gitlab
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
runner-token: "glrt-3nNma_nEvL1Bq2zc8m5Zu286MQpwOjIKdDozCnU6MTAQ.01.181jg6jja"
|
||||||
|
|
||||||
|
---
|
||||||
|
# ─── ConfigMap (config.toml) ──────────────────────────────────────
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: gitlab-runner-config
|
||||||
|
namespace: gitlab
|
||||||
|
data:
|
||||||
|
config.toml: |
|
||||||
|
concurrent = 4
|
||||||
|
check_interval = 10
|
||||||
|
log_level = "info"
|
||||||
|
|
||||||
|
[session_server]
|
||||||
|
session_timeout = 1800
|
||||||
|
|
||||||
|
---
|
||||||
|
# ─── Deployment ───────────────────────────────────────────────────
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: gitlab-runner
|
||||||
|
namespace: gitlab
|
||||||
|
labels:
|
||||||
|
app: gitlab-runner
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: gitlab-runner
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: gitlab-runner
|
||||||
|
spec:
|
||||||
|
serviceAccountName: gitlab-runner
|
||||||
|
|
||||||
|
initContainers:
|
||||||
|
- name: register-runner
|
||||||
|
image: gitlab/gitlab-runner:latest
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
command:
|
||||||
|
- sh
|
||||||
|
- -c
|
||||||
|
- |
|
||||||
|
gitlab-runner register \
|
||||||
|
--non-interactive \
|
||||||
|
--url "$CI_SERVER_URL" \
|
||||||
|
--token "$RUNNER_TOKEN" \
|
||||||
|
--executor kubernetes \
|
||||||
|
--kubernetes-namespace gitlab \
|
||||||
|
--kubernetes-service-account gitlab-runner \
|
||||||
|
--kubernetes-pull-policy if-not-present \
|
||||||
|
--kubernetes-privileged true \
|
||||||
|
--output-limit 4096 \
|
||||||
|
--kubernetes-cpu-request "100m" \
|
||||||
|
--kubernetes-cpu-limit "500m" \
|
||||||
|
--kubernetes-memory-request "256Mi" \
|
||||||
|
--kubernetes-memory-limit "4Gi"
|
||||||
|
env:
|
||||||
|
- name: CI_SERVER_URL
|
||||||
|
value: "https://gitlab.henryathome.home64.de"
|
||||||
|
- name: RUNNER_TOKEN
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: gitlab-runner-secret
|
||||||
|
key: runner-token
|
||||||
|
volumeMounts:
|
||||||
|
- name: runner-config
|
||||||
|
mountPath: /etc/gitlab-runner
|
||||||
|
|
||||||
|
containers:
|
||||||
|
- name: gitlab-runner
|
||||||
|
image: gitlab/gitlab-runner:latest
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
command: ["gitlab-runner", "run", "--user=gitlab-runner", "--working-directory=/home/gitlab-runner"]
|
||||||
|
env:
|
||||||
|
- name: CI_SERVER_URL
|
||||||
|
value: "https://gitlab.henryathome.home64.de"
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: "128Mi"
|
||||||
|
cpu: "100m"
|
||||||
|
limits:
|
||||||
|
memory: "512Mi"
|
||||||
|
cpu: "1000m"
|
||||||
|
volumeMounts:
|
||||||
|
- name: runner-config
|
||||||
|
mountPath: /etc/gitlab-runner
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- name: runner-config
|
||||||
|
emptyDir: {}
|
||||||
@@ -18,6 +18,10 @@ stringData:
|
|||||||
}
|
}
|
||||||
prometheus_monitoring['enable'] = false
|
prometheus_monitoring['enable'] = false
|
||||||
|
|
||||||
|
registry_external_url 'https://registry.henryathome.home64.de'
|
||||||
|
registry_nginx['listen_port'] = 5050
|
||||||
|
registry_nginx['listen_https'] = false
|
||||||
|
|
||||||
# Authentik SSO (OpenID Connect)
|
# Authentik SSO (OpenID Connect)
|
||||||
gitlab_rails['omniauth_enabled'] = true
|
gitlab_rails['omniauth_enabled'] = true
|
||||||
gitlab_rails['omniauth_allow_single_sign_on'] = ['openid_connect']
|
gitlab_rails['omniauth_allow_single_sign_on'] = ['openid_connect']
|
||||||
|
|||||||
@@ -17,9 +17,13 @@ spec:
|
|||||||
targetPort: 443
|
targetPort: 443
|
||||||
nodePort: 31443
|
nodePort: 31443
|
||||||
- name: ssh
|
- name: ssh
|
||||||
port: 31022
|
port: 22
|
||||||
targetPort: 31022
|
targetPort: 22
|
||||||
nodePort: 31022
|
nodePort: 31022
|
||||||
|
- name: registry
|
||||||
|
port: 5050
|
||||||
|
targetPort: 5050
|
||||||
|
nodePort: 31050
|
||||||
type: NodePort
|
type: NodePort
|
||||||
|
|
||||||
# ---
|
# ---
|
||||||
|
|||||||
Executable
+254
@@ -0,0 +1,254 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
NAMESPACE="gitlab"
|
||||||
|
DEPLOYMENT_NAME="gitlab"
|
||||||
|
JOB_NAME="gitlab-migrate-to-longhorn"
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
MANIFEST_DIR="${SCRIPT_DIR}/manifest"
|
||||||
|
PV_PVC_MANIFEST="${MANIFEST_DIR}/pv-pvc.yaml"
|
||||||
|
MIGRATION_JOB_MANIFEST="${MANIFEST_DIR}/migrate-to-longhorn.yaml"
|
||||||
|
|
||||||
|
SOURCE_DATA_PVC="gitlab-data-pvc"
|
||||||
|
SOURCE_CONFIG_PVC="gitlab-config-pvc"
|
||||||
|
TARGET_DATA_PVC="gitlab-data-longhorn-pvc"
|
||||||
|
TARGET_CONFIG_PVC="gitlab-config-longhorn-pvc"
|
||||||
|
|
||||||
|
BIND_TIMEOUT_SECONDS=900
|
||||||
|
JOB_TIMEOUT_SECONDS=7200
|
||||||
|
POD_STOP_TIMEOUT_SECONDS=300
|
||||||
|
|
||||||
|
ORIGINAL_REPLICAS="1"
|
||||||
|
SCALED_DOWN="false"
|
||||||
|
DRY_RUN="false"
|
||||||
|
AUTO_CONFIRM="false"
|
||||||
|
|
||||||
|
info() {
|
||||||
|
echo "[INFO] $*"
|
||||||
|
}
|
||||||
|
|
||||||
|
warn() {
|
||||||
|
echo "[WARN] $*" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
err() {
|
||||||
|
echo "[ERROR] $*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'EOF'
|
||||||
|
Usage: migrate-to-longhorn.sh [--dry-run] [--yes] [--help]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--dry-run Show planned actions without changing cluster state.
|
||||||
|
--yes Skip interactive confirmation before scaling down GitLab.
|
||||||
|
--help Show this help.
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_args() {
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--dry-run)
|
||||||
|
DRY_RUN="true"
|
||||||
|
;;
|
||||||
|
--yes)
|
||||||
|
AUTO_CONFIRM="true"
|
||||||
|
;;
|
||||||
|
--help|-h)
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
err "Unknown argument: $1"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
restore_deployment_on_error() {
|
||||||
|
if [[ "${SCALED_DOWN}" == "true" ]]; then
|
||||||
|
warn "Migration failed. Restoring deployment replicas to ${ORIGINAL_REPLICAS}."
|
||||||
|
kubectl -n "${NAMESPACE}" scale deployment "${DEPLOYMENT_NAME}" --replicas="${ORIGINAL_REPLICAS}" >/dev/null || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
on_exit() {
|
||||||
|
local exit_code=$?
|
||||||
|
if [[ ${exit_code} -ne 0 ]]; then
|
||||||
|
restore_deployment_on_error
|
||||||
|
warn "Script failed with exit code ${exit_code}."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
trap on_exit EXIT
|
||||||
|
|
||||||
|
require_tool() {
|
||||||
|
local tool="$1"
|
||||||
|
command -v "${tool}" >/dev/null 2>&1 || err "Required tool not found: ${tool}"
|
||||||
|
}
|
||||||
|
|
||||||
|
require_file() {
|
||||||
|
local path="$1"
|
||||||
|
[[ -f "${path}" ]] || err "Required file not found: ${path}"
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for_pvc_bound() {
|
||||||
|
local pvc_name="$1"
|
||||||
|
local timeout="$2"
|
||||||
|
local elapsed=0
|
||||||
|
local interval=5
|
||||||
|
|
||||||
|
info "Waiting for PVC ${pvc_name} to become Bound..."
|
||||||
|
while true; do
|
||||||
|
local phase
|
||||||
|
phase="$(kubectl -n "${NAMESPACE}" get pvc "${pvc_name}" -o jsonpath='{.status.phase}' 2>/dev/null || true)"
|
||||||
|
|
||||||
|
if [[ "${phase}" == "Bound" ]]; then
|
||||||
|
info "PVC ${pvc_name} is Bound."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( elapsed >= timeout )); then
|
||||||
|
err "Timeout while waiting for PVC ${pvc_name} to bind."
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep "${interval}"
|
||||||
|
elapsed=$((elapsed + interval))
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for_gitlab_pods_stopped() {
|
||||||
|
local timeout="$1"
|
||||||
|
local elapsed=0
|
||||||
|
local interval=5
|
||||||
|
|
||||||
|
info "Waiting for pods with label app=gitlab to stop..."
|
||||||
|
while true; do
|
||||||
|
local count
|
||||||
|
count="$(kubectl -n "${NAMESPACE}" get pods -l app=gitlab --no-headers 2>/dev/null | wc -l | tr -d ' ')"
|
||||||
|
|
||||||
|
if [[ "${count}" == "0" ]]; then
|
||||||
|
info "All GitLab pods are stopped."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( elapsed >= timeout )); then
|
||||||
|
err "Timeout while waiting for GitLab pods to stop."
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep "${interval}"
|
||||||
|
elapsed=$((elapsed + interval))
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for_job_complete() {
|
||||||
|
local timeout="$1"
|
||||||
|
|
||||||
|
info "Waiting for migration job ${JOB_NAME} to complete..."
|
||||||
|
if kubectl -n "${NAMESPACE}" wait --for=condition=complete "job/${JOB_NAME}" --timeout="${timeout}s" >/dev/null; then
|
||||||
|
info "Migration job completed successfully."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
warn "Migration job did not complete in time or failed."
|
||||||
|
kubectl -n "${NAMESPACE}" describe job "${JOB_NAME}" || true
|
||||||
|
kubectl -n "${NAMESPACE}" logs "job/${JOB_NAME}" --tail=-1 || true
|
||||||
|
err "Migration job failed."
|
||||||
|
}
|
||||||
|
|
||||||
|
validate_prerequisites() {
|
||||||
|
require_tool kubectl
|
||||||
|
require_file "${PV_PVC_MANIFEST}"
|
||||||
|
require_file "${MIGRATION_JOB_MANIFEST}"
|
||||||
|
|
||||||
|
kubectl get namespace "${NAMESPACE}" >/dev/null 2>&1 || err "Namespace does not exist: ${NAMESPACE}"
|
||||||
|
kubectl -n "${NAMESPACE}" get deployment "${DEPLOYMENT_NAME}" >/dev/null 2>&1 || err "Deployment not found: ${DEPLOYMENT_NAME}"
|
||||||
|
|
||||||
|
kubectl -n "${NAMESPACE}" get pvc "${SOURCE_DATA_PVC}" >/dev/null 2>&1 || err "Source PVC not found: ${SOURCE_DATA_PVC}"
|
||||||
|
kubectl -n "${NAMESPACE}" get pvc "${SOURCE_CONFIG_PVC}" >/dev/null 2>&1 || err "Source PVC not found: ${SOURCE_CONFIG_PVC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
confirm_scale_down() {
|
||||||
|
if [[ "${AUTO_CONFIRM}" == "true" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
warn "GitLab deployment ${DEPLOYMENT_NAME} in namespace ${NAMESPACE} will be scaled to 0 during migration."
|
||||||
|
read -r -p "Type MIGRATE to continue: " confirmation
|
||||||
|
if [[ "${confirmation}" != "MIGRATE" ]]; then
|
||||||
|
err "Confirmation failed. Aborted by user."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
print_dry_run_plan() {
|
||||||
|
info "Dry-run mode active. No cluster changes will be made."
|
||||||
|
info "Planned steps:"
|
||||||
|
info "1) kubectl apply -f ${PV_PVC_MANIFEST}"
|
||||||
|
info "2) Wait for PVCs ${TARGET_DATA_PVC} and ${TARGET_CONFIG_PVC} to be Bound"
|
||||||
|
info "3) Scale deployment ${DEPLOYMENT_NAME} to 0"
|
||||||
|
info "4) Recreate and run job ${JOB_NAME} from ${MIGRATION_JOB_MANIFEST}"
|
||||||
|
info "5) Wait for job completion and verify target PVCs"
|
||||||
|
info "6) Scale deployment ${DEPLOYMENT_NAME} back to ${ORIGINAL_REPLICAS}"
|
||||||
|
info "No PVC switch in deployment is performed by this script."
|
||||||
|
}
|
||||||
|
|
||||||
|
run_migration() {
|
||||||
|
ORIGINAL_REPLICAS="$(kubectl -n "${NAMESPACE}" get deployment "${DEPLOYMENT_NAME}" -o jsonpath='{.spec.replicas}')"
|
||||||
|
ORIGINAL_REPLICAS="${ORIGINAL_REPLICAS:-1}"
|
||||||
|
|
||||||
|
if [[ "${DRY_RUN}" == "true" ]]; then
|
||||||
|
print_dry_run_plan
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
confirm_scale_down
|
||||||
|
|
||||||
|
info "Applying PVC manifest to ensure Longhorn target PVCs exist."
|
||||||
|
kubectl apply -f "${PV_PVC_MANIFEST}" >/dev/null
|
||||||
|
|
||||||
|
wait_for_pvc_bound "${TARGET_DATA_PVC}" "${BIND_TIMEOUT_SECONDS}"
|
||||||
|
wait_for_pvc_bound "${TARGET_CONFIG_PVC}" "${BIND_TIMEOUT_SECONDS}"
|
||||||
|
|
||||||
|
info "Scaling deployment ${DEPLOYMENT_NAME} down to 0 for consistent copy."
|
||||||
|
kubectl -n "${NAMESPACE}" scale deployment "${DEPLOYMENT_NAME}" --replicas=0 >/dev/null
|
||||||
|
SCALED_DOWN="true"
|
||||||
|
wait_for_gitlab_pods_stopped "${POD_STOP_TIMEOUT_SECONDS}"
|
||||||
|
|
||||||
|
info "Recreating migration job ${JOB_NAME}."
|
||||||
|
kubectl -n "${NAMESPACE}" delete job "${JOB_NAME}" --ignore-not-found >/dev/null
|
||||||
|
kubectl apply -f "${MIGRATION_JOB_MANIFEST}" >/dev/null
|
||||||
|
|
||||||
|
info "Streaming migration job logs."
|
||||||
|
kubectl -n "${NAMESPACE}" logs -f "job/${JOB_NAME}" || true
|
||||||
|
|
||||||
|
wait_for_job_complete "${JOB_TIMEOUT_SECONDS}"
|
||||||
|
|
||||||
|
info "Quick verification: target PVCs are still Bound."
|
||||||
|
wait_for_pvc_bound "${TARGET_DATA_PVC}" 30
|
||||||
|
wait_for_pvc_bound "${TARGET_CONFIG_PVC}" 30
|
||||||
|
|
||||||
|
info "Scaling deployment ${DEPLOYMENT_NAME} back to ${ORIGINAL_REPLICAS}."
|
||||||
|
kubectl -n "${NAMESPACE}" scale deployment "${DEPLOYMENT_NAME}" --replicas="${ORIGINAL_REPLICAS}" >/dev/null
|
||||||
|
SCALED_DOWN="false"
|
||||||
|
|
||||||
|
info "Migration finished successfully."
|
||||||
|
info "No deployment PVC switch was done by this script."
|
||||||
|
info "NFS PVC/PV (gitlab-git-pvc / gitlab-git-pv) were not changed."
|
||||||
|
info "If you want to switch GitLab to Longhorn later, apply the updated deployment manifest manually."
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
parse_args "$@"
|
||||||
|
info "Starting GitLab local PVC to Longhorn migration."
|
||||||
|
if [[ "${DRY_RUN}" == "true" ]]; then
|
||||||
|
info "Running in dry-run mode."
|
||||||
|
fi
|
||||||
|
validate_prerequisites
|
||||||
|
run_migration
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolume
|
||||||
|
metadata:
|
||||||
|
name: gitea-pv-target
|
||||||
|
spec:
|
||||||
|
storageClassName: nfs
|
||||||
|
capacity:
|
||||||
|
storage: 30Gi
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteMany
|
||||||
|
persistentVolumeReclaimPolicy: Retain
|
||||||
|
nfs:
|
||||||
|
server: 192.168.178.186
|
||||||
|
path: /volume1/giteaRepos
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: gitea-pvc-target
|
||||||
|
namespace: gitea
|
||||||
|
spec:
|
||||||
|
storageClassName: nfs
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteMany
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 30Gi
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: gitea-runner-data
|
||||||
|
namespace: gitea
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
storageClassName: longhorn
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 5Gi
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: gitea-runner
|
||||||
|
namespace: gitea
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: gitea-runner
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: gitea-runner
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: runner
|
||||||
|
image: gitea/act_runner:latest
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
env:
|
||||||
|
- name: GITEA_INSTANCE_URL
|
||||||
|
value: "http://gitea.gitea.svc.cluster.local"
|
||||||
|
- name: GITEA_RUNNER_NAME
|
||||||
|
value: "k3s-runner-1"
|
||||||
|
- name: GITEA_RUNNER_LABELS
|
||||||
|
value: "linux-x64:host,ubuntu-latest:docker://catthehacker/ubuntu:act-latest,ubuntu-22.04:docker://catthehacker/ubuntu:act-22.04,alpine:docker://alpine:3.20"
|
||||||
|
- name: GITEA_RUNNER_REGISTRATION_TOKEN
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: gitea-runner-secret
|
||||||
|
key: GITEA_RUNNER_REGISTRATION_TOKEN
|
||||||
|
- name: DOCKER_HOST
|
||||||
|
value: "unix:///var/run/docker.sock"
|
||||||
|
command:
|
||||||
|
- /bin/sh
|
||||||
|
- -c
|
||||||
|
args:
|
||||||
|
- |
|
||||||
|
set -e
|
||||||
|
until [ -S /var/run/docker.sock ]; do
|
||||||
|
echo "Waiting for Docker socket..."
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
if [ -f /data/.runner_labels ] && [ "$(cat /data/.runner_labels)" != "${GITEA_RUNNER_LABELS}" ]; then
|
||||||
|
rm -f /data/.runner
|
||||||
|
fi
|
||||||
|
printf '%s' "${GITEA_RUNNER_LABELS}" > /data/.runner_labels
|
||||||
|
if [ ! -f /data/.runner ]; then
|
||||||
|
act_runner register \
|
||||||
|
--no-interactive \
|
||||||
|
--instance "${GITEA_INSTANCE_URL}" \
|
||||||
|
--token "${GITEA_RUNNER_REGISTRATION_TOKEN}" \
|
||||||
|
--name "${GITEA_RUNNER_NAME}" \
|
||||||
|
--labels "${GITEA_RUNNER_LABELS}"
|
||||||
|
fi
|
||||||
|
exec act_runner daemon
|
||||||
|
volumeMounts:
|
||||||
|
- name: runner-data
|
||||||
|
mountPath: /data
|
||||||
|
- name: docker-run
|
||||||
|
mountPath: /var/run
|
||||||
|
- name: dind
|
||||||
|
image: docker:27-dind
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
securityContext:
|
||||||
|
privileged: true
|
||||||
|
args:
|
||||||
|
- --insecure-registry=gitea.gitea.svc.cluster.local
|
||||||
|
env:
|
||||||
|
- name: DOCKER_TLS_CERTDIR
|
||||||
|
value: ""
|
||||||
|
volumeMounts:
|
||||||
|
- name: docker-lib
|
||||||
|
mountPath: /var/lib/docker
|
||||||
|
- name: docker-run
|
||||||
|
mountPath: /var/run
|
||||||
|
volumes:
|
||||||
|
- name: runner-data
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: gitea-runner-data
|
||||||
|
- name: docker-lib
|
||||||
|
emptyDir: {}
|
||||||
|
- name: docker-run
|
||||||
|
emptyDir: {}
|
||||||
+24
-61
@@ -5,65 +5,20 @@ kind: Namespace
|
|||||||
metadata:
|
metadata:
|
||||||
name: gitea
|
name: gitea
|
||||||
|
|
||||||
# PV + PVC: Gitea (NFS)
|
# PVC: PostgreSQL (Longhorn target)
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: PersistentVolume
|
|
||||||
metadata:
|
|
||||||
name: gitea-pv
|
|
||||||
spec:
|
|
||||||
storageClassName: nfs
|
|
||||||
capacity:
|
|
||||||
storage: 30Gi
|
|
||||||
accessModes:
|
|
||||||
- ReadWriteMany
|
|
||||||
persistentVolumeReclaimPolicy: Retain
|
|
||||||
nfs:
|
|
||||||
server: 192.168.178.166
|
|
||||||
path: /export/fastData/gitea/repos
|
|
||||||
---
|
---
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: PersistentVolumeClaim
|
kind: PersistentVolumeClaim
|
||||||
metadata:
|
metadata:
|
||||||
name: gitea-pvc
|
name: postgres-longhorn-pvc-3g
|
||||||
namespace: gitea
|
namespace: gitea
|
||||||
spec:
|
spec:
|
||||||
storageClassName: nfs
|
storageClassName: longhorn
|
||||||
accessModes:
|
|
||||||
- ReadWriteMany
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
storage: 30Gi
|
|
||||||
|
|
||||||
# PV + PVC: PostgreSQL (NFS)
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: PersistentVolume
|
|
||||||
metadata:
|
|
||||||
name: postgres-pv
|
|
||||||
spec:
|
|
||||||
storageClassName: nfs
|
|
||||||
capacity:
|
|
||||||
storage: 10Gi
|
|
||||||
accessModes:
|
|
||||||
- ReadWriteOnce
|
|
||||||
persistentVolumeReclaimPolicy: Retain
|
|
||||||
nfs:
|
|
||||||
server: 192.168.178.166
|
|
||||||
path: /export/fastData/gitea/postgres
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: PersistentVolumeClaim
|
|
||||||
metadata:
|
|
||||||
name: postgres-pvc
|
|
||||||
namespace: gitea
|
|
||||||
spec:
|
|
||||||
storageClassName: nfs
|
|
||||||
accessModes:
|
accessModes:
|
||||||
- ReadWriteOnce
|
- ReadWriteOnce
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
storage: 10Gi
|
storage: 3Gi
|
||||||
|
|
||||||
# Deployment: PostgreSQL
|
# Deployment: PostgreSQL
|
||||||
---
|
---
|
||||||
@@ -74,6 +29,8 @@ metadata:
|
|||||||
namespace: gitea
|
namespace: gitea
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
|
strategy:
|
||||||
|
type: Recreate
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app: postgres
|
app: postgres
|
||||||
@@ -97,14 +54,12 @@ spec:
|
|||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: postgres-storage
|
- name: postgres-storage
|
||||||
mountPath: /var/lib/postgresql/data
|
mountPath: /var/lib/postgresql/data
|
||||||
securityContext:
|
securityContext:
|
||||||
runAsUser: 1001
|
fsGroup: 999
|
||||||
runAsGroup: 1000
|
|
||||||
# fsGroup: 1000
|
|
||||||
volumes:
|
volumes:
|
||||||
- name: postgres-storage
|
- name: postgres-storage
|
||||||
persistentVolumeClaim:
|
persistentVolumeClaim:
|
||||||
claimName: postgres-pvc
|
claimName: postgres-longhorn-pvc-3g
|
||||||
|
|
||||||
# Service: PostgreSQL
|
# Service: PostgreSQL
|
||||||
---
|
---
|
||||||
@@ -138,14 +93,16 @@ spec:
|
|||||||
labels:
|
labels:
|
||||||
app: gitea
|
app: gitea
|
||||||
spec:
|
spec:
|
||||||
|
nodeSelector:
|
||||||
|
kubernetes.io/hostname: knode1
|
||||||
containers:
|
containers:
|
||||||
- name: gitea
|
- name: gitea
|
||||||
image: gitea/gitea:latest
|
image: gitea/gitea:latest
|
||||||
env:
|
env:
|
||||||
- name: USER_UID
|
- name: USER_UID
|
||||||
value: "1000"
|
value: "1024"
|
||||||
- name: USER_GID
|
- name: USER_GID
|
||||||
value: "1000"
|
value: "100"
|
||||||
- name: GITEA__database__DB_TYPE
|
- name: GITEA__database__DB_TYPE
|
||||||
value: postgres
|
value: postgres
|
||||||
- name: GITEA__database__HOST
|
- name: GITEA__database__HOST
|
||||||
@@ -158,6 +115,10 @@ spec:
|
|||||||
value: giteapassword
|
value: giteapassword
|
||||||
- name: GITEA__server__ROOT_URL
|
- name: GITEA__server__ROOT_URL
|
||||||
value: "https://git.henryathome.home64.de"
|
value: "https://git.henryathome.home64.de"
|
||||||
|
- name: GITEA__server__DOMAIN
|
||||||
|
value: git.henryathome.home64.de
|
||||||
|
- name: GITEA__server__PROTOCOL
|
||||||
|
value: http
|
||||||
- name: GITEA__server__SSH_DOMAIN
|
- name: GITEA__server__SSH_DOMAIN
|
||||||
value: git.henryathome.home64.de
|
value: git.henryathome.home64.de
|
||||||
- name: GITEA__server__START_SSH_SERVER
|
- name: GITEA__server__START_SSH_SERVER
|
||||||
@@ -166,20 +127,22 @@ spec:
|
|||||||
value: "32000"
|
value: "32000"
|
||||||
- name: GITEA__server__SSH_PORT
|
- name: GITEA__server__SSH_PORT
|
||||||
value: "32000"
|
value: "32000"
|
||||||
|
- name: GITEA__packages__ENABLED
|
||||||
|
value: "true"
|
||||||
|
- name: GITEA__repository__ROOT
|
||||||
|
value: /data/gitea/git/repositories
|
||||||
|
- name: GITEA__lfs__PATH
|
||||||
|
value: /data/gitea/git/lfs
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 3000 # HTTP
|
- containerPort: 3000 # HTTP
|
||||||
- containerPort: 32000 # SSH
|
- containerPort: 32000 # SSH
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: gitea-storage
|
- name: gitea-storage
|
||||||
mountPath: /data
|
mountPath: /data
|
||||||
securityContext:
|
|
||||||
# runAsUser: 1001
|
|
||||||
# runAsGroup: 1000
|
|
||||||
# fsGroup: 1000
|
|
||||||
volumes:
|
volumes:
|
||||||
- name: gitea-storage
|
- name: gitea-storage
|
||||||
persistentVolumeClaim:
|
persistentVolumeClaim:
|
||||||
claimName: gitea-pvc
|
claimName: gitea-pvc-target
|
||||||
|
|
||||||
# Service: Gitea (inkl. SSH)
|
# Service: Gitea (inkl. SSH)
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: homarr
|
||||||
|
namespace: homarr
|
||||||
|
labels:
|
||||||
|
app: homarr
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: homarr
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: homarr
|
||||||
|
spec:
|
||||||
|
securityContext:
|
||||||
|
fsGroup: 1000
|
||||||
|
containers:
|
||||||
|
- name: homarr
|
||||||
|
image: ghcr.io/homarr-labs/homarr:latest
|
||||||
|
imagePullPolicy: Always
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 100m
|
||||||
|
memory: 512Mi
|
||||||
|
limits:
|
||||||
|
cpu: 1000m
|
||||||
|
memory: 1Gi
|
||||||
|
env:
|
||||||
|
- name: AUTH_PROVIDERS
|
||||||
|
value: "credentials,oidc"
|
||||||
|
- name: AUTH_OIDC_ISSUER
|
||||||
|
value: "https://authentik.henryathome.home64.de/application/o/homarr/"
|
||||||
|
- name: AUTH_OIDC_CLIENT_ID
|
||||||
|
value: "gLJekZnT5uwDXqWoTolP6YyktjdTAPmSAx7EVLcK"
|
||||||
|
- name: AUTH_OIDC_CLIENT_SECRET
|
||||||
|
value: "nX9qYyvtIH1PO3FFM13dvvKakv2eovyO9pFKNDYUKF0sycM8UFl0MgGkysqG5irpFsValNb2QkBLUKCRnCIcUt3M6ztCEe4po1Qqfvr0QZHRdH8d21vSHXMMdQmjQ2WN"
|
||||||
|
- name: AUTH_OIDC_CLIENT_NAME
|
||||||
|
value: "Authentik"
|
||||||
|
- name: SECRET_ENCRYPTION_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: homarr-secret
|
||||||
|
key: SECRET_ENCRYPTION_KEY
|
||||||
|
ports:
|
||||||
|
- containerPort: 7575
|
||||||
|
protocol: TCP
|
||||||
|
volumeMounts:
|
||||||
|
- name: homarr-data
|
||||||
|
mountPath: /appdata
|
||||||
|
- name: homarr-data
|
||||||
|
mountPath: /app/data
|
||||||
|
volumes:
|
||||||
|
- name: homarr-data
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: homarr-pvc
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: homarr-pvc
|
||||||
|
namespace: homarr
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
storageClassName: longhorn
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 5Gi
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: homarr-secret
|
||||||
|
namespace: homarr
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
SECRET_ENCRYPTION_KEY: "0fddc6753cb94b4a1dc38f26c52c4d4dbce019237457ede59893fb1a74017512"
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: homarr
|
||||||
|
namespace: homarr
|
||||||
|
spec:
|
||||||
|
type: NodePort
|
||||||
|
selector:
|
||||||
|
app: homarr
|
||||||
|
ports:
|
||||||
|
- port: 7575
|
||||||
|
targetPort: 7575
|
||||||
|
protocol: TCP
|
||||||
|
nodePort: 30757
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: homarr
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
apiVersion: storage.k8s.io/v1
|
||||||
|
kind: StorageClass
|
||||||
|
metadata:
|
||||||
|
name: longhorn-2replicas
|
||||||
|
provisioner: driver.longhorn.io
|
||||||
|
parameters:
|
||||||
|
numberOfReplicas: "2"
|
||||||
|
staleReplicaTimeout: "30"
|
||||||
|
reclaimPolicy: Delete
|
||||||
|
volumeBindingMode: Immediate
|
||||||
|
allowVolumeExpansion: true
|
||||||
@@ -2,7 +2,7 @@ kind: ConfigMap
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
metadata:
|
metadata:
|
||||||
name: immich-env
|
name: immich-env
|
||||||
namespace: photoprism
|
namespace: immich
|
||||||
labels:
|
labels:
|
||||||
app: immich
|
app: immich
|
||||||
data:
|
data:
|
||||||
@@ -18,6 +18,6 @@ data:
|
|||||||
DISABLE_REVERSE_GEOCODING: "false"
|
DISABLE_REVERSE_GEOCODING: "false"
|
||||||
REVERSE_GEOCODING_PRECISION: "2"
|
REVERSE_GEOCODING_PRECISION: "2"
|
||||||
PUBLIC_LOGIN_PAGE_MESSAGE: ""
|
PUBLIC_LOGIN_PAGE_MESSAGE: ""
|
||||||
PUID: "0"
|
PUID: "1001"
|
||||||
PGID: "0"
|
PGID: "100"
|
||||||
DB_PASSWORD: "password"
|
DB_PASSWORD: "password"
|
||||||
|
|||||||
@@ -2,9 +2,11 @@ apiVersion: apps/v1
|
|||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
name: immich-database
|
name: immich-database
|
||||||
namespace: photoprism
|
namespace: immich
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
|
strategy:
|
||||||
|
type: Recreate
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app: immich-database
|
app: immich-database
|
||||||
@@ -13,6 +15,8 @@ spec:
|
|||||||
labels:
|
labels:
|
||||||
app: immich-database
|
app: immich-database
|
||||||
spec:
|
spec:
|
||||||
|
securityContext:
|
||||||
|
fsGroup: 1000
|
||||||
containers:
|
containers:
|
||||||
- name: immich-postgres
|
- name: immich-postgres
|
||||||
image: "docker.io/tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:90724186f0a3517cf6914295b5ab410db9ce23190a2d9d0b9dd6463e3fa298f0"
|
image: "docker.io/tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:90724186f0a3517cf6914295b5ab410db9ce23190a2d9d0b9dd6463e3fa298f0"
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ kind: PersistentVolume
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
metadata:
|
metadata:
|
||||||
name: immich-db-pv
|
name: immich-db-pv
|
||||||
namespace: photoprism
|
|
||||||
labels:
|
labels:
|
||||||
app: immich-postgresql
|
app: immich-postgresql
|
||||||
spec:
|
spec:
|
||||||
|
|||||||
@@ -2,14 +2,13 @@ kind: PersistentVolumeClaim
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
metadata:
|
metadata:
|
||||||
name: immich-db-pvc
|
name: immich-db-pvc
|
||||||
namespace: photoprism
|
namespace: immich
|
||||||
labels:
|
labels:
|
||||||
app: immich
|
app: immich
|
||||||
spec:
|
spec:
|
||||||
accessModes:
|
accessModes:
|
||||||
- ReadWriteMany
|
- ReadWriteOnce
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
storage: 20Gi # Match or be less than the PV's capacity
|
storage: 8Gi
|
||||||
volumeName: immich-db-pv # Bind explicitly to the PV created above
|
storageClassName: longhorn
|
||||||
storageClassName: nfs
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ kind: Service
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
metadata:
|
metadata:
|
||||||
name: immich-database
|
name: immich-database
|
||||||
namespace: photoprism
|
namespace: immich
|
||||||
labels:
|
labels:
|
||||||
app: immich-database
|
app: immich-database
|
||||||
spec:
|
spec:
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolume
|
||||||
|
metadata:
|
||||||
|
name: immich-library-immich-pv
|
||||||
|
labels:
|
||||||
|
app: immich
|
||||||
|
spec:
|
||||||
|
capacity:
|
||||||
|
storage: 50Gi
|
||||||
|
storageClassName: nfs
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteMany
|
||||||
|
persistentVolumeReclaimPolicy: Retain
|
||||||
|
nfs:
|
||||||
|
path: /export/immichLibrary
|
||||||
|
server: 192.168.178.166
|
||||||
@@ -2,7 +2,6 @@ kind: PersistentVolume
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
metadata:
|
metadata:
|
||||||
name: immich-library-pv
|
name: immich-library-pv
|
||||||
namespace: immich
|
|
||||||
labels:
|
labels:
|
||||||
app: immich
|
app: immich
|
||||||
spec:
|
spec:
|
||||||
@@ -12,5 +11,5 @@ spec:
|
|||||||
accessModes:
|
accessModes:
|
||||||
- ReadWriteMany
|
- ReadWriteMany
|
||||||
nfs:
|
nfs:
|
||||||
path: /export/fastData/immichLibrary # Static path on the NFS server
|
path: /export/immichLibrary # Static path on the NFS server
|
||||||
server: 192.168.178.166
|
server: 192.168.178.166
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ kind: PersistentVolumeClaim
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
metadata:
|
metadata:
|
||||||
name: immich-library-pvc
|
name: immich-library-pvc
|
||||||
namespace: photoprism
|
namespace: immich
|
||||||
labels:
|
labels:
|
||||||
app: immich
|
app: immich
|
||||||
spec:
|
spec:
|
||||||
@@ -11,5 +11,5 @@ spec:
|
|||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
storage: 50Gi # Match or be less than the PV's capacity
|
storage: 50Gi # Match or be less than the PV's capacity
|
||||||
volumeName: immich-library-pv # Bind explicitly to the PV created above
|
volumeName: immich-library-immich-pv
|
||||||
storageClassName: nfs
|
storageClassName: nfs
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ kind: Deployment
|
|||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
metadata:
|
metadata:
|
||||||
name: immich-machine-learning
|
name: immich-machine-learning
|
||||||
namespace: photoprism
|
namespace: immich
|
||||||
labels:
|
labels:
|
||||||
app: immich-machine-learning
|
app: immich-machine-learning
|
||||||
spec:
|
spec:
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ kind: Service
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
metadata:
|
metadata:
|
||||||
name: immich-machine-learning
|
name: immich-machine-learning
|
||||||
namespace: photoprism
|
namespace: immich
|
||||||
labels:
|
labels:
|
||||||
app: immich-machine-learning
|
app: immich-machine-learning
|
||||||
spec:
|
spec:
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolume
|
||||||
|
metadata:
|
||||||
|
name: immich-photos-pv
|
||||||
|
labels:
|
||||||
|
app: immich
|
||||||
|
spec:
|
||||||
|
capacity:
|
||||||
|
storage: 200Gi
|
||||||
|
storageClassName: nfs
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteMany
|
||||||
|
persistentVolumeReclaimPolicy: Retain
|
||||||
|
nfs:
|
||||||
|
server: 192.168.178.186
|
||||||
|
path: /volume1/Photos
|
||||||
|
mountOptions:
|
||||||
|
- hard
|
||||||
|
- nfsvers=4
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: immich-photos-pvc
|
||||||
|
namespace: immich
|
||||||
|
labels:
|
||||||
|
app: immich
|
||||||
|
spec:
|
||||||
|
storageClassName: nfs
|
||||||
|
volumeName: immich-photos-pv
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteMany
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 50Gi
|
||||||
@@ -2,7 +2,7 @@ kind: Deployment
|
|||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
metadata:
|
metadata:
|
||||||
name: immich-server
|
name: immich-server
|
||||||
namespace: photoprism
|
namespace: immich
|
||||||
labels:
|
labels:
|
||||||
app: immich-server
|
app: immich-server
|
||||||
spec:
|
spec:
|
||||||
@@ -25,7 +25,8 @@ spec:
|
|||||||
}]
|
}]
|
||||||
spec:
|
spec:
|
||||||
securityContext:
|
securityContext:
|
||||||
fsGroup: 0
|
fsGroup: 100
|
||||||
|
fsGroupChangePolicy: OnRootMismatch
|
||||||
serviceAccountName: default
|
serviceAccountName: default
|
||||||
dnsPolicy: ClusterFirst
|
dnsPolicy: ClusterFirst
|
||||||
initContainers:
|
initContainers:
|
||||||
@@ -60,7 +61,8 @@ spec:
|
|||||||
image: "ghcr.io/immich-app/immich-server:release"
|
image: "ghcr.io/immich-app/immich-server:release"
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
securityContext:
|
securityContext:
|
||||||
runAsUser: 0
|
runAsUser: 1001
|
||||||
|
runAsGroup: 100
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 3001
|
- containerPort: 3001
|
||||||
env:
|
env:
|
||||||
@@ -112,4 +114,4 @@ spec:
|
|||||||
claimName: immich-library-pvc
|
claimName: immich-library-pvc
|
||||||
- name: ext-library
|
- name: ext-library
|
||||||
persistentVolumeClaim:
|
persistentVolumeClaim:
|
||||||
claimName: photoprism-library-pvc
|
claimName: immich-photos-pvc
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ kind: Service
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
metadata:
|
metadata:
|
||||||
name: immich-server
|
name: immich-server
|
||||||
namespace: photoprism
|
namespace: immich
|
||||||
labels:
|
labels:
|
||||||
app: immich-server
|
app: immich-server
|
||||||
spec:
|
spec:
|
||||||
@@ -12,4 +12,5 @@ spec:
|
|||||||
ports:
|
ports:
|
||||||
- port: 2283
|
- port: 2283
|
||||||
targetPort: 2283
|
targetPort: 2283
|
||||||
|
nodePort: 31139
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
apiVersion: batch/v1
|
||||||
|
kind: Job
|
||||||
|
metadata:
|
||||||
|
name: immich-db-nfs-to-longhorn
|
||||||
|
namespace: immich
|
||||||
|
spec:
|
||||||
|
backoffLimit: 2
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
restartPolicy: Never
|
||||||
|
containers:
|
||||||
|
- name: migrate-db
|
||||||
|
image: alpine:3.20
|
||||||
|
command:
|
||||||
|
- sh
|
||||||
|
- -c
|
||||||
|
- |
|
||||||
|
set -e
|
||||||
|
apk add --no-cache rsync
|
||||||
|
test -d /source/postgres
|
||||||
|
mkdir -p /target/postgres
|
||||||
|
rsync -aHAX --numeric-ids /source/postgres/ /target/postgres/
|
||||||
|
test -f /target/postgres/PG_VERSION
|
||||||
|
test -d /target/postgres/base
|
||||||
|
volumeMounts:
|
||||||
|
- name: source-nfs
|
||||||
|
mountPath: /source
|
||||||
|
readOnly: true
|
||||||
|
- name: target-longhorn
|
||||||
|
mountPath: /target
|
||||||
|
volumes:
|
||||||
|
- name: source-nfs
|
||||||
|
nfs:
|
||||||
|
server: 192.168.178.166
|
||||||
|
path: /export/fastData/immichDB
|
||||||
|
- name: target-longhorn
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: immich-db-pvc
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: immich
|
||||||
@@ -9,7 +9,7 @@ apiVersion: apps/v1
|
|||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
name: redis-server
|
name: redis-server
|
||||||
namespace: photoprism
|
namespace: immich
|
||||||
labels:
|
labels:
|
||||||
app: redis-server
|
app: redis-server
|
||||||
spec:
|
spec:
|
||||||
@@ -35,7 +35,7 @@ apiVersion: v1
|
|||||||
kind: Service
|
kind: Service
|
||||||
metadata:
|
metadata:
|
||||||
name: redis-server
|
name: redis-server
|
||||||
namespace: photoprism
|
namespace: immich
|
||||||
labels:
|
labels:
|
||||||
app: redis-server
|
app: redis-server
|
||||||
spec:
|
spec:
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: speedtest-tracker
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: speedtest-tracker
|
||||||
|
namespace: speedtest-tracker
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: speedtest-tracker
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: speedtest-tracker
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: speedtest-tracker
|
||||||
|
spec:
|
||||||
|
dnsPolicy: None
|
||||||
|
dnsConfig:
|
||||||
|
nameservers:
|
||||||
|
- 10.152.183.10
|
||||||
|
- 8.8.8.8
|
||||||
|
searches:
|
||||||
|
- speedtest-tracker.svc.cluster.local
|
||||||
|
options:
|
||||||
|
- name: ndots
|
||||||
|
value: "5"
|
||||||
|
containers:
|
||||||
|
- name: speedtest-tracker
|
||||||
|
image: lscr.io/linuxserver/speedtest-tracker:latest
|
||||||
|
env:
|
||||||
|
- name: PUID
|
||||||
|
value: "1000"
|
||||||
|
- name: PGID
|
||||||
|
value: "1000"
|
||||||
|
- name: APP_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: speedtest-tracker-secret
|
||||||
|
key: APP_KEY
|
||||||
|
- name: DISPLAY_TIMEZONE
|
||||||
|
value: Europe/Berlin
|
||||||
|
- name: DB_CONNECTION
|
||||||
|
value: sqlite
|
||||||
|
- name: SPEEDTEST_SCHEDULE
|
||||||
|
value: "0 * * * *"
|
||||||
|
- name: SPEEDTEST_SERVERS
|
||||||
|
value: ""
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 100m
|
||||||
|
memory: 128Mi
|
||||||
|
limits:
|
||||||
|
cpu: 500m
|
||||||
|
memory: 512Mi
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /config
|
||||||
|
name: speedtest-tracker
|
||||||
|
volumes:
|
||||||
|
- name: speedtest-tracker
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: speedtest-tracker
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: speedtest-tracker
|
||||||
|
namespace: speedtest-tracker
|
||||||
|
annotations:
|
||||||
|
traefik.ingress.kubernetes.io/router.entrypoints: web
|
||||||
|
spec:
|
||||||
|
ingressClassName: traefik
|
||||||
|
rules:
|
||||||
|
- host: speedtest.henryathome.home64.de
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: speedtest-tracker
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: speedtest-tracker
|
||||||
|
namespace: speedtest-tracker
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
storageClassName: longhorn
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 5Gi
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: speedtest-tracker-secret
|
||||||
|
namespace: speedtest-tracker
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
APP_KEY: "base64:kRwkJqieSmtYw0+066zNiNgXInLSexYxT9RgIyONNMI=" # https://speedtest-tracker.dev
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: speedtest-tracker
|
||||||
|
namespace: speedtest-tracker
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: speedtest-tracker
|
||||||
|
spec:
|
||||||
|
type: NodePort
|
||||||
|
selector:
|
||||||
|
app.kubernetes.io/name: speedtest-tracker
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
targetPort: 80
|
||||||
|
protocol: TCP
|
||||||
|
nodePort: 30800
|
||||||
Reference in New Issue
Block a user