Zum Hauptinhalt springen

Kubernetes-Bereitstellung

Diese Seite beschreibt die Bereitstellung von FoxPlan auf einem Kubernetes-Cluster mit seinem Helm-Chart. Die gesamte Konfiguration erfolgt über eine Values-Datei: Die Templates des Charts müssen nie geändert werden.

Für einen einzelnen Server ohne Cluster siehe Docker-Compose-Bereitstellung.

Herunterzuladende Dateien​

curl -O https://app.fox-plan.com/docs/onpremise/kubernetes/foxplan-1.0.0.tgz
curl -o foxplan-values.yaml https://app.fox-plan.com/docs/onpremise/kubernetes/values.yaml
Image-Registry von FoxPlan

Die FoxPlan-Images werden in einer frei zugänglichen Registry veröffentlicht: Es sind keine Zugangsdaten anzufordern und keine imagePullSecrets anzulegen. Ihre Adresse teilt Ihnen FoxPlan zusammen mit Ihrer Lizenz mit; sie wird im Value image.registry eingetragen.

Architektur​

KomponenteKubernetes-ObjektRolle
AnwendungDeployment foxplan-appFoxPlan-Server (Port 5000), ein oder mehrere Replikas
DokumentationDeployment foxplan-docsDiese Dokumentation, bereitgestellt unter /docs
Export der PlanungenDeployment foxplan-exportPDF, PNG, Excel, MS Project und Primavera, bereitgestellt unter /gantt-export
MongoDBStatefulSet foxplan-mongodbDatenbank, ein Knoten, persistentes Volume
RedisStatefulSet foxplan-redisSitzungen und Cache, persistentes Volume. Immer bereitgestellt: FoxPlan startet nicht ohne Redis
Garage (optional)StatefulSet foxplan-garageS3-Dateispeicher, falls Sie keinen haben
RoutingIngress foxplan/ → Anwendung, /docs → Dokumentation, /gantt-export → Export

Die Namen erhalten den Namen des Helm-Releases als Präfix (foxplan in den Beispielen). Der Export-Pod enthält einen kleinen nginx-Proxy, der das Präfix /gantt-export entfernt: Das Routing funktioniert so mit jedem Ingress-Controller, ohne controllerspezifische Rewrite-Annotation.

Voraussetzungen​

  • Ein Cluster mit Kubernetes 1.27 oder neuer sowie Helm 3.
  • Ein Ingress-Controller: Traefik, ingress-nginx, HAProxy, der Ihres Managed-Angebots…
  • Eine Standard-Storage-Class (oder eine in den Values angegebene Klasse) für die Volumes von MongoDB, Redis und gegebenenfalls Garage.
  • Mindestens ein amd64-Knoten: Die Export-Images werden nur für amd64 veröffentlicht. Die übrigen Images gibt es auch für arm64.
  • Ein Domainname, der auf Ihren Ingress-Controller zeigt, und ein TLS-Zertifikat: ausgestellt von cert-manager oder von Ihnen in einem Secret bereitgestellt.
  • Ein SMTP-Server: Registrierungscodes, Einladungen und Passwortzurücksetzungen hängen davon ab. Ohne ihn kann sich niemand anmelden.
  • Nur für die automatische Skalierung: metrics-server.

Abruf der Images​

Die Netzwerkflüsse sind die der Docker-Compose-Installation, beschrieben unter Schritt 1: Images herunterladen — Netzwerkflüsse: die FoxPlan-Registry und Docker Hub für die Images von Drittanbietern. Sie gehen von den Knoten des Clusters aus, wenn diese die Images herunterladen. Vom Chart verwendete Drittanbieter-Images:

ImageVerwendet
mongo:8.2.7Immer, außer bei externer MongoDB (mongodb.enabled: false)
redis:7.4Immer
nginxinc/nginx-unprivileged:1.28-alpineProxy des Export-Pods
dxflrs/garage:v2.1.0, curlimages/curl:8.14.1Nur mit garage.enabled: true
Sensibler Sektor: Nutzen Sie Ihre interne Registry

Rufen Sie die Images aus einer von Ihrem Informationssystem unabhängigen Umgebung ab, wie für Docker Compose beschrieben, und pushen Sie sie dann in Ihre interne Registry. Geben Sie diese anschließend in den Values an: image.registry für die FoxPlan-Images, mongodb.image, redis.image, export.proxy.image, garage.image und garage.initImage für die übrigen. Wenn diese Registry eine Authentifizierung verlangt, fügen Sie ihr Secret in imagePullSecrets hinzu. Die Knoten haben dann keinerlei Flüsse mehr ins Internet.

Ports und SSO​

Enterprise-SSO erfordert keinen zusätzlichen Port, ob es sich um die Standard-Anmeldung mit Microsoft oder Google oder um ein benutzerdefiniertes SSO handelt. Die Rückkehr einer Anmeldung wird vom Browser des Benutzers an https://<host>/login/oauth2/code/… übermittelt, über den Ingress wie der Rest der Anwendung: Der Identitätsanbieter baut nie eine Verbindung zum Cluster auf.

RichtungPortZwischenZweck
Eingehend443 (TCP)Arbeitsplätze der Benutzer → Ingress-ControllerDie Anwendung und die SSO-Rückkehr
Eingehend80 (TCP)Arbeitsplätze der Benutzer → Ingress-ControllerWeiterleitung auf 443; HTTP-01-Challenge von cert-manager
Ausgehend443 (TCP)Pods foxplan-app → IdentitätsanbieterToken-Austausch, Signaturschlüssel, Profil — Domains in der Netzwerkfluss-Matrix

Stellen Sie den Port 5000 der Pods nicht bereit (NodePort, LoadBalancer): Der Ingress genügt. Filtern Ihre NetworkPolicy-Objekte ausgehenden Verkehr, erlauben Sie den Pods foxplan-app den Zugriff auf den Identitätsanbieter, auf 443 oder auf dessen eigenem Port — Keycloak auf 8443 zum Beispiel.

  • Standard-SSO (Microsoft, Google): Variablen über app.extraEnv und app.extraSecretEnv. Die Weiterleitungs-URL wird aus host gebildet: https://<host>/login/oauth2/code/azure oder …/google, unverändert bei Microsoft Entra oder Google einzutragen.
  • Benutzerdefiniertes SSO (Okta, Keycloak, jeder OIDC-Anbieter, eingerichtet unter Unternehmens-SSO): Die Weiterleitungs-URL wird aus den vom Ingress-Controller gesetzten Headern X-Forwarded-Proto, X-Forwarded-Host und X-Forwarded-Port rekonstruiert. Die Schaltfläche Konfiguration prüfen zeigt die genaue einzutragende URL.
TLS vor dem Ingress terminiert

Terminiert ein Load Balancer vor dem Ingress-Controller TLS, empfängt der Controller HTTP und gibt X-Forwarded-Proto: http weiter: FoxPlan berechnet dann eine http://-URL, die der Anbieter ablehnt (redirect_uri mismatch). Lassen Sie die Header des Load Balancers durchreichen — use-forwarded-headers: "true" in der ConfigMap von ingress-nginx, forwardedHeaders.trustedIPs am Einstiegspunkt von Traefik — oder betreiben Sie den Load Balancer im TCP-Modus, ohne Entschlüsselung. Der öffentliche Port muss 443 bleiben.

Dimensionierung​

Standardmäßige Anforderungen (requests) und Obergrenzen (limits) des Charts:

KomponenteAngeforderte CPUAngeforderter SpeicherSpeicherobergrenzeVolume
Anwendung (pro Replika)14 Gi6 Gi—
MongoDB0,52 Gi4 Gi50 Gi
Redis0,1256 Mi1 Gi5 Gi
Export (3 Container)0,36656 Mi2,6 Gi—
Dokumentation0,0132 Mi256 Mi—
Garage (optional)0,1256 Mi1 Gi1 Gi + 100 Gi

Richtwerte für die auf den Knoten verfügbare Kapazität:

BenutzerCPUArbeitsspeicherFestplatte
Bis 1004 vCPU16 GB100 GB
100 bis 5008 vCPU32 GB, mit 2 Replikas der Anwendung200 GB
DarüberAutomatische Skalierung der Anwendung, MongoDB als Replica Set

Die JVM der Anwendung nutzt 75 % ihrer Speicherobergrenze (app.javaOptions): Um ihr mehr Speicher zu geben, erhöhen Sie app.resources.limits.memory, ohne weitere Einstellung.

Installation​

1. Namespace und Zertifikat​

kubectl create namespace foxplan

Mit cert-manager ist nichts anzulegen: Die Annotation des Issuers in den Values genügt (siehe nächster Schritt). Andernfalls legen Sie das TLS-Secret der Domain an, einschließlich der vollständigen Kette:

kubectl -n foxplan create secret tls foxplan-tls --cert=fullchain.pem --key=privkey.pem

2. Values​

Gehen Sie von der heruntergeladenen values.yaml aus oder von einer Datei, die nur die Änderungen enthält. Das Minimum:

# foxplan-values.yaml
image:
registry: <von FoxPlan mitgeteilte Adresse>
tag: latest # in der Produktion die bereitgestellte Version festlegen

host: foxplan.mon-entreprise.fr

ingress:
className: traefik # oder nginx, haproxy… leer = Standardklasse des Clusters
annotations:
cert-manager.io/cluster-issuer: letsencrypt # wenn Sie cert-manager verwenden

app:
replicas: 1 # Anzahl der Anwendungsinstanzen — siehe „Anzahl der Instanzen“

mail:
from: foxplan@mon-entreprise.fr
host: smtp.mon-entreprise.fr
port: 587
username: foxplan@mon-entreprise.fr
password: <SMTP-Passwort>

Ohne image.registry, host, mail.from oder mail.host verweigert Helm die Installation mit einer Meldung, die den fehlenden Value nennt.

ingress-nginx

ingress-nginx begrenzt Uploads standardmäßig auf 1 MB, was Anhänge blockiert. Fügen Sie diese Annotationen hinzu:

ingress:
className: nginx
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
# Große Header in SSO-Antworten (SAML, OIDC)
nginx.ingress.kubernetes.io/proxy-buffer-size: "16k"
nginx.ingress.kubernetes.io/proxy-read-timeout: "120"

Traefik setzt keine Grenze: Es ist keine Annotation erforderlich.

3. Bereitstellung​

helm upgrade --install foxplan foxplan-1.0.0.tgz -n foxplan -f foxplan-values.yaml
kubectl -n foxplan get pods -w

Beim ersten Start werden die Images heruntergeladen und die Datenbank initialisiert: Rechnen Sie mit einigen Minuten, bis der Pod foxplan-app bereit ist.

4. Erstes Konto anlegen​

Öffnen Sie https://IHRE_DOMAIN: Solange keine Instanz existiert, öffnet sich eine Diagnoseseite (Bildschirm Ersteinrichtung) und zeigt Ihnen, ob alles korrekt funktioniert — siehe was geprüft wird. Wenn alles antwortet, klicken Sie auf Erste Instanz mit meinem Konto anlegen. Der Registrierungscode wird über Ihren SMTP-Server versendet; geben Sie ihn ein, folgen Sie dem Assistenten und wählen Sie dann Ihr Passwort.

Danach öffnet das Diagnosesymbol rechts neben der Sprachauswahl auf der Anmeldeseite diese Diagnose jederzeit erneut.

Wichtigste Values​

ValueStandardRolle
image.registry—Adresse der FoxPlan-Registry. Pflicht
image.taglatestVersion der FoxPlan-Images
image.pullPolicyAlwaysMit latest wird das Image bei jedem Pod-Start neu heruntergeladen
host—Domainname. Pflicht
ingress.className, ingress.annotations—Ingress-Controller und seine Einstellungen
ingress.tls.secretNamefoxplan-tlsTLS-Secret der Domain
mail.*—SMTP-Server. Pflicht
s3.*leerIhr S3-Speicher — siehe Dateispeicher
app.replicas1Replikas der Anwendung
autoscaling.enabledfalseAutomatische Skalierung nach CPU-Last
app.extraEnv, app.extraSecretEnv—Zusätzliche Variablen: SSO Microsoft oder Google…
mongodb.enabled, mongodb.externalUritrueIntegrierte MongoDB oder Ihre eigene
export.amd64OnlytruePlatziert den Export-Pod auf einem amd64-Knoten
garage.enabledfalseIntegrierter S3-Speicher
secrets.*generiertJWT-Schlüssel und Passwörter — siehe Secrets

Die heruntergeladene Datei values.yaml dokumentiert jeden Value.

Dateispeicher​

Projektdokumente, Vertragsanhänge und Profilfotos werden in einem S3-kompatiblen Objektspeicher abgelegt. Dort bleiben sie privat: Die Anwendung liefert sie selbst aus, nach Prüfung der Berechtigungen.

Ihr vorhandener Speicher — Ceph, NetApp StorageGRID, Scality, MinIO, AWS… Legen Sie einen Bucket und für FoxPlan dedizierte Zugangsdaten an, dann:

s3:
url: https://s3.mon-entreprise.local # ohne den Bucket-Namen
bucket: foxplan
accessKey: <Zugriffskennung>
secretKey: <geheimer Schlüssel>
region: <vom Speicher erwartete Region>

Die Region geht in die Signatur jeder Anfrage ein: Weicht sie von der des Speichers ab, werden alle Anfragen abgelehnt und die Diagnose zeigt den Speicher als fehlerhaft an. Der Bucket wird im Pfad der URL adressiert: Es ist kein DNS pro Bucket erforderlich.

Garage, falls Sie keinen haben — garage.enabled: true stellt Garage bereit, einen in Frankreich entwickelten Open-Source-S3-Speicher. Ein Job legt bei der Installation den Bucket und den Zugriffsschlüssel von FoxPlan an; die Zugangsdaten werden generiert und im Secret abgelegt. Der Abschnitt s3 wird dann ignoriert. Garage läuft auf einem einzigen Knoten: Sichern Sie ihn (siehe Sicherung).

Ohne Speicher funktioniert die Anwendung; nur die Dateien sind nicht verfügbar. On Premise wird kein Kontingent angewendet.

Anzahl der Instanzen​

Die Anzahl der Anwendungsinstanzen wird in Ihrer Wertedatei festgelegt, auf eine von zwei Arten.

Feste Anzahl — app.replicas:

app:
replicas: 3

Automatisch — je nach CPU-Last, zwischen einem Minimum und einem Maximum (erfordert metrics-server). app.replicas wird dann ignoriert:

autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 6
targetCPUUtilizationPercentage: 70

Wenden Sie die Änderung mit helm upgrade an (vollständiger Befehl unter Versionsupdate) und prüfen Sie dann:

kubectl -n foxplan get pods -l app.kubernetes.io/component=app
kubectl -n foxplan get hpa # mit automatischer Skalierung

Legen Sie diese Anzahl immer in den Werten fest: Ein kubectl scale wird beim nächsten Update überschrieben. Jede Instanz fordert 1 vCPU und 4 Gi Arbeitsspeicher an (app.resources): Ihre Knoten müssen alle aufnehmen können, einschließlich des Skalierungsmaximums. Ab 2 Instanzen fügt das Chart ein PodDisruptionBudget hinzu, das während Arbeiten an den Knoten mindestens eine laufen lässt. Richtwert: 2 Instanzen für 100 bis 500 Benutzer, darüber automatische Skalierung.

Die Sitzungen liegen in Redis, und geplante Aufgaben setzen eine Sperre in Redis: Die Instanzen teilen sich die Benutzer ohne Sitzungsaffinität, und eine Aufgabe läuft nur einmal.

Hochverfügbarkeit​

  • Anwendung: 2 Instanzen oder mehr — siehe Anzahl der Instanzen.
  • MongoDB: Die MongoDB des Charts ist ein einzelner Knoten. Für Hochverfügbarkeit verwenden Sie ein Replica Set (MongoDB-Operator, verwalteter Dienst) mit mongodb.enabled: false und mongodb.externalUri: mongodb://utilisateur:mot-de-passe@hote1,hote2,hote3/foxplanweb?replicaSet=rs0&authSource=admin.
  • Redis: immer vom Chart bereitgestellt, mit Persistenz auf Festplatte. Ein Ausfall von Redis meldet die Benutzer ab, ohne Datenverlust: Redis enthält nur die Sitzungen und den Cache.

Eingehende und ausgehende Zertifikate​

FoxPlan verwendet zwei verschiedene Zertifikate, eines pro Verkehrsrichtung:

Eingehende FlüsseAusgehende Flüsse
ZweckDen Browsern nachweisen, dass der Server wirklich host istDer Anwendung erlauben, Ihre internen Dienste zu erkennen: AD FS, SMTP mit TLS, S3…
Verwendet vonDem Ingress-ControllerDen Pods foxplan-app (Java)
InhaltDas Zertifikat der FoxPlan-Domain und sein privater SchlüsselDas öffentliche Zertifikat der Zertifizierungsstelle, die diese Dienste signiert hat — Stamm- und Zwischenzertifikate. Niemals ein privater Schlüssel
AblageortTLS-Secret foxplan-tls (ingress.tls.secretName) oder cert-managerWert app.outboundCaCerts oder eine bestehende ConfigMap, angegeben über app.outboundCaConfigMap
FormatPEM: --cert = Serverzertifikat, gefolgt von den Zwischenzertifikaten, --key = unverschlüsselter SchlüsselPEM, Schlüsselnamen auf .crt. Eine Datei darf mehrere Zertifikate enthalten
PflichtJaNur wenn ein aufgerufener Dienst von einer nicht öffentlichen Stelle signiert ist
ÜbernahmeDer Controller liest das Secret bei dessen Aktualisierung neuBeim Start der Pods. helm upgrade startet sie neu; eine geänderte externe ConfigMap erfordert kubectl -n foxplan rollout restart deployment foxplan-app

Es sind nicht dieselben Zertifikate, sie stammen aber oft aus derselben PKI: Das Zertifikat der Stammzertifizierungsstelle, die das von FoxPlan signiert hat, ist dann auch das für die ausgehenden Flüsse bereitzustellende.

Für die ausgehenden Flüsse:

# foxplan-values.yaml
app:
outboundCaCerts:
ca-interne.crt: |
-----BEGIN CERTIFICATE-----
MIIDFzCCAf+gAwIBAgIU...
-----END CERTIFICATE-----

oder, ohne die Datei in die Werte zu kopieren:

helm upgrade --install foxplan foxplan-1.0.0.tgz -n foxplan -f foxplan-values.yaml \
--set-file 'app.outboundCaCerts.ca-interne\.crt=ca-interne.crt'

Das Chart lehnt einen Namen ohne Endung .crt und einen Inhalt mit privatem Schlüssel ab; es behält nur die Blöcke BEGIN/END CERTIFICATE. Wie Sie feststellen, ob eine Stelle nötig ist, und wie Sie eine .cer-, .p7b- oder .pfx-Datei in PEM umwandeln, steht unter Docker Compose: Die Befehle sind dieselben.

Secrets​

Bleiben sie in den Values leer, werden der JWT-Schlüssel und die Passwörter von MongoDB und Redis (sowie die Schlüssel von Garage) bei der ersten Installation generiert, im Secret foxplan-secrets abgelegt und bei jedem Update beibehalten. Dieses Secret übersteht auch helm uninstall.

Sichern Sie das Secret zusammen mit der Datenbank

SECRET_KEY_JWT verschlüsselt auch die in der Datenbank gespeicherten SSO-Konfigurationen. Wird eine Datenbank ohne diesen Schlüssel wiederhergestellt, sind diese Konfigurationen nicht mehr lesbar.

kubectl -n foxplan get secret foxplan-secrets -o yaml > foxplan-secrets.yaml

Um die Secrets selbst zu verwalten (Vault, External Secrets…), legen Sie ein Secret mit SECRET_KEY_JWT, MONGO_PASSWORD, REDIS_PASSWORD, MAIL_SMTP_PASSWORD, FILE_S3_ACCESS_KEY und FILE_S3_SECRET_KEY an (bei externer MongoDB zusätzlich URL_MONGO) und geben Sie es in secrets.existingSecret an. Die MongoDB-Passwörter müssen alphanumerisch bleiben: Sie gehen unverändert in die Verbindungs-URL ein.

Sicherung​

Gemeinsam und zum selben Zeitpunkt zu sichern: die Datenbank, die Dateien und das Secret.

Datenbank — im laufenden Betrieb, aus dem MongoDB-Pod, der die Werkzeuge enthält:

kubectl -n foxplan exec foxplan-mongodb-0 -- sh -c \
'mongodump --username "$MONGO_INITDB_ROOT_USERNAME" \
--password "$MONGO_INITDB_ROOT_PASSWORD" \
--authenticationDatabase admin --archive --gzip' \
> foxplan-$(date +%F-%H%M).archive.gz

Zur Wiederherstellung derselbe Befehl mit mongorestore --drop und kubectl exec -i … < archive. Leeren Sie anschließend den Redis-Cache, sonst liefert die Anwendung veraltete Daten aus:

kubectl -n foxplan exec foxplan-redis-0 -- sh -c 'redis-cli -a "$REDIS_PASSWORD" --no-auth-warning FLUSHALL'

Dateien in Garage — im laufenden Betrieb, mit rclone von einem Administrationsrechner aus:

kubectl -n foxplan port-forward svc/foxplan-garage 3900:3900 &
KEY=$(kubectl -n foxplan get secret foxplan-secrets -o jsonpath='{.data.FILE_S3_ACCESS_KEY}' | base64 -d)
SECRET=$(kubectl -n foxplan get secret foxplan-secrets -o jsonpath='{.data.FILE_S3_SECRET_KEY}' | base64 -d)
rclone sync :s3:foxplan ./foxplan-files \
--s3-provider Other --s3-endpoint http://127.0.0.1:3900 --s3-region garage \
--s3-access-key-id "$KEY" --s3-secret-access-key "$SECRET"

Zur Wiederherstellung vertauschen Sie Quelle und Ziel. Bei Ihrem eigenen S3-Speicher wenden Sie dessen übliche Sicherungsrichtlinie an. Die Volume-Snapshots Ihrer Plattform (VolumeSnapshot) eignen sich ebenfalls, sofern MongoDB und Garage zum selben Zeitpunkt erfasst werden.

Versionsupdate​

helm upgrade foxplan foxplan-1.0.0.tgz -n foxplan -f foxplan-values.yaml
  • Festgelegte Version (image.tag): Ändern Sie den Value und führen Sie den Befehl erneut aus.
  • Mit latest: Der Befehl allein ändert nichts, wenn die Values identisch sind. Starten Sie die Pods neu, damit sie das Image erneut herunterladen:
kubectl -n foxplan rollout restart deployment

Die Bereitstellung der Anwendung erfolgt schrittweise: Ein neues Replika startet und wird bereit, bevor das alte stoppt.

Deinstallation​

helm uninstall foxplan -n foxplan

Die Volumes (MongoDB, Redis, Garage) und das Secret werden beibehalten: Eine Neuinstallation findet die Daten wieder. Um alles zu löschen, entfernen Sie anschließend die PersistentVolumeClaim-Objekte und das Secret aus dem Namespace.

Fehlerbehebung​

SymptomWahrscheinliche Ursache
helm verweigert: „image.registry est obligatoire“Pflicht-Value fehlt: image.registry, host, mail.from oder mail.host
Pod foxplan-export im Status PendingKein amd64-Knoten: Fügen Sie einen hinzu, oder export.amd64Only: false, wenn Ihre ARM-Knoten amd64 emulieren
Pod foxplan-app startet in einer Schleife neuLogs: kubectl -n foxplan logs deploy/foxplan-app. Meist ist MongoDB oder Redis nicht erreichbar
Anhang abgelehnt, Fehler 413Upload-Grenze des Ingress-Controllers: siehe Hinweis zu ingress-nginx
Speicher in der Diagnose fehlerhaftRegion s3.region weicht von der des Speichers ab, oder Speicher von den Pods aus nicht erreichbar
Kein Registrierungscode erhaltenSMTP: Die Diagnose der Anmeldeseite zeigt, ob er erreichbar ist
latest wird nicht aktualisiertkubectl -n foxplan rollout restart deployment: siehe Versionsupdate
SSO-Konfiguration nach Wiederherstellung nicht lesbarSecret foxplan-secrets weicht von dem zum Zeitpunkt der Sicherung gültigen ab
PKIX path building failed in den Protokollen von foxplan-appInterne Zertifizierungsstelle von AD FS, SMTP oder S3 fehlt in app.outboundCaCerts: siehe Eingehende und ausgehende Zertifikate