Helm charts
Six charts indépendants, déployés sur un cluster k3s avec Traefik + cert-manager déjà en place :
| Chart | Rôle |
|---|---|
backend |
API Go/Gin (+ Jobs de migration et de seed) |
frontend |
SPA Vite/React derrière nginx |
postgresql |
Postgres 16 (StatefulSet + PVC Longhorn) |
redis |
Redis 7 avec mot de passe (StatefulSet + PVC Longhorn) |
ingressroute |
IngressRoute Traefik + Certificate TLS du host |
cert-manager |
ClusterIssuers DNS-01 (Cloudflare), manifestes bruts (pas un chart Helm) |
Backend et frontend sont servis sur le même host : ingressroute envoie /api/
et /uploads/ au backend, / au frontend. Le navigateur ne voit qu'une origine,
ce qui garde les cookies de refresh-token same-site sans aucune config CORS
(reproduit le proxy dev de frontend/vite.config.ts).
Prérequis
- Traefik (namespace
traefik) avec les Middlewareswaf-chainetsecurity-headers. - cert-manager installé (ne pas le réinstaller s'il l'est déjà).
- Postgres et Redis : fournis par les charts
postgresqletredis. Par défaut le backend vise les Servicespostgresetredisdu namespace (database.host,redis.host), qui sont lesfullnameOverridepar défaut de ces deux charts, et la basevitrine_db/ l'utilisateurpostgres(database.name,database.user). - DNS : le host pointe vers l'IP du LoadBalancer Traefik.
1. Images
docker build -t <registry>/platform-backend:<tag> backend/
docker build -t <registry>/platform-frontend:<tag> frontend/
docker push <registry>/platform-backend:<tag>
docker push <registry>/platform-frontend:<tag>
2. Certificat : issuers (une seule fois)
kubectl create secret generic cloudflare-api-token-vitrine -n cert-manager \
--from-literal=api-token='<token Zone:DNS:Edit>'
kubectl apply -f charts/cert-manager/cluster-issuer.yml
Ne pas appliquer cert-manager/networkpolicy.yml sur un cluster où cert-manager existe déjà.
3. Déploiement
Les secrets ne sont jamais commités : passer un fichier de values privé (gitignoré) ou --set-string.
kubectl create namespace <ns>
# Les mots de passe doivent être identiques ici et dans le backend ci-dessous.
helm install postgresql charts/postgresql -n <ns> --set-string auth.password='<mot de passe DB>'
helm install redis charts/redis -n <ns> --set-string auth.password='<mot de passe Redis>'
helm install backend charts/backend -n <ns> \
--set image.repository=<registry>/platform-backend \
--set image.tag=<tag> \
--set secrets.JWT_SECRET="$(openssl rand -base64 48)" \
--set secrets.DB_PASSWORD='<mot de passe>' \
--set secrets.REDIS_PASSWORD='<mot de passe>' \
--set env.MEDIA_LOCAL_BASE_URL=https://<host>/uploads
helm install frontend charts/frontend -n <ns> \
--set image.repository=<registry>/platform-frontend \
--set image.tag=<tag>
helm install ingressroute charts/ingressroute -n <ns> --set host=<host>
Les noms de release backend et frontend sont ceux que ingressroute attend
(Services backend-platform-backend et frontend). Avec d'autres noms, surcharger
backend.serviceName / frontend.serviceName côté ingressroute.
Premier essai TLS : ajouter --set tls.certificate.issuerName=letsencrypt-dns-vitrine-staging
à l'install d'ingressroute (pas de quota Let's Encrypt), puis repasser sur
letsencrypt-dns-vitrine.
Chaque chart valide ses valeurs obligatoires au rendu (helm template / install
échoue avec un message clair : secrets.JWT_SECRET pour le backend, host pour
ingressroute).
Premier install : compte admin
backend/cmd/seed n'est pas idempotent (échoue si l'admin existe déjà) : c'est
un hook post-install uniquement. Il ne s'exécute donc que lors du tout premier
helm install : ajouter ces valeurs à la commande helm install backend ci-dessus.
--set seedJob.enabled=true \
--set secrets.SEED_ADMIN_PASSWORD='<12+ caractères>'
Un helm upgrade ne relance jamais le seed. Pour créer l'admin après coup, réinstaller
la release ou lancer /app/seed à la main dans un Pod du backend.
Migrations
migrationJob.enabled (défaut true) exécute backend/migrations (embarquées dans
l'image) en hook pre-install/pre-upgrade. migrate up est idempotent : on peut le
laisser actif à chaque upgrade. Le Secret du backend est lui aussi un hook
(pre-install/pre-upgrade) pour exister avant ce Job ; helm uninstall ne le supprime donc pas.
Stockage des médias
Par défaut (MEDIA_STORAGE_DRIVER=local) les uploads et les pièces de vérification
sont sur deux PVC ReadWriteOnce (persistence.uploads, persistence.verification,
classe longhorn-replicated). Cela impose 1 seul replica (défaut :
replicaCount: 1, autoscaling.enabled: false, stratégie Recreate). Passer à S3
(MEDIA_STORAGE_DRIVER=s3 + env.S3_* / secrets.S3_*) avant d'augmenter les replicas
ou d'activer l'autoscaling.
Vérifier
helm lint charts/backend --set secrets.JWT_SECRET=x
helm lint charts/frontend
helm lint charts/ingressroute --set host=<host>
kubectl get certificate,pods,svc,ingressroute -n <ns>
curl -I https://<host>