diff --git a/README.md b/README.md new file mode 100644 index 0000000..17b8d70 --- /dev/null +++ b/README.md @@ -0,0 +1,125 @@ +# 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 Middlewares `waf-chain` et `security-headers`. +- cert-manager installé (ne pas le réinstaller s'il l'est déjà). +- Postgres et Redis : fournis par les charts `postgresql` et `redis`. Par défaut le + backend vise les Services `postgres` et `redis` du namespace (`database.host`, + `redis.host`), qui sont les `fullnameOverride` par défaut de ces deux charts, et + la base `vitrine_db` / l'utilisateur `postgres` (`database.name`, `database.user`). +- DNS : le host pointe vers l'IP du LoadBalancer Traefik. + +## 1. Images + +```bash +docker build -t /platform-backend: backend/ +docker build -t /platform-frontend: frontend/ +docker push /platform-backend: +docker push /platform-frontend: +``` + +## 2. Certificat : issuers (une seule fois) + +```bash +kubectl create secret generic cloudflare-api-token-vitrine -n cert-manager \ + --from-literal=api-token='' +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`. + +```bash +kubectl create namespace + +# Les mots de passe doivent être identiques ici et dans le backend ci-dessous. +helm install postgresql charts/postgresql -n --set-string auth.password='' +helm install redis charts/redis -n --set-string auth.password='' + +helm install backend charts/backend -n \ + --set image.repository=/platform-backend \ + --set image.tag= \ + --set secrets.JWT_SECRET="$(openssl rand -base64 48)" \ + --set secrets.DB_PASSWORD='' \ + --set secrets.REDIS_PASSWORD='' \ + --set env.MEDIA_LOCAL_BASE_URL=https:///uploads + +helm install frontend charts/frontend -n \ + --set image.repository=/platform-frontend \ + --set image.tag= + +helm install ingressroute charts/ingressroute -n --set 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. + +```bash + --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 + +```bash +helm lint charts/backend --set secrets.JWT_SECRET=x +helm lint charts/frontend +helm lint charts/ingressroute --set host= + +kubectl get certificate,pods,svc,ingressroute -n +curl -I https:// +```