All checks were successful
Build & Deploy App / build-and-deploy (push) Successful in 30s
Le pod plantait en Init:CrashLoopBackOff parce que le init container tournait depuis /app/apps/api avec \`node ace\` — qui charge le shim ace.js → bin/console.ts (TS source). Sans devDeps en runtime, pas de loader TS → ERR_UNKNOWN_FILE_EXTENSION. Fix : workingDir /app/apps/api/build + command \`node ace.js migration:run --force\`. build/ contient les .js compilés. Memory mise à jour pour documenter ce piège. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
140 lines
5.9 KiB
Markdown
140 lines
5.9 KiB
Markdown
# Deploy memory — rubis
|
|
|
|
Ce repo contient **deux déploiements distincts** sur la même infra K3s,
|
|
namespace `rubis` :
|
|
|
|
1. **Landing** statique (`rubis.arthurbarre.fr`) — image nginx-alpine.
|
|
2. **App SaaS** (`app.rubis.arthurbarre.fr`) — image AdonisJS + React (V1).
|
|
|
|
Chacun a sa propre image, son propre Dockerfile, son propre workflow CI.
|
|
|
|
---
|
|
|
|
## 1. Landing (`rubis.arthurbarre.fr`)
|
|
|
|
### Infra
|
|
- Deployment : `rubis` · Container : `rubis` · NodePort : `30109`
|
|
- Image : `git.arthurbarre.fr/ordinarthur/rubis`
|
|
- Domaine : https://rubis.arthurbarre.fr (sous-domaine **temporaire** —
|
|
domaine définitif `rubis-sur-l-ongle.fr` pas encore acheté)
|
|
- Manifests : `k3s/{namespace,deployment,service}.yml`
|
|
- Route Traefik : `~/dev/perso/proxmox/ansible/roles/traefik/templates/rubis.yml.j2`
|
|
- Workflow CI : `.gitea/workflows/deploy.yml`
|
|
- Source : `landing/` (servi par nginx)
|
|
|
|
### Mise à jour
|
|
Push git, le CI build + rollout auto (filter sur `landing/**`, `Dockerfile`,
|
|
`k3s/{namespace,deployment,service}.yml`).
|
|
|
|
---
|
|
|
|
## 2. App SaaS (`app.rubis.arthurbarre.fr`)
|
|
|
|
### Infra
|
|
- Deployment : `rubis-app` · Container : `app` · NodePort : `30110`
|
|
- Init container `migrate` : `node ace.js migration:run --force` depuis
|
|
`/app/apps/api/build` (idempotent)
|
|
- Sidecar Redis : Deployment `rubis-redis` (ClusterIP, PVC 1Gi local-path,
|
|
backend BullMQ + cache)
|
|
- Image : `git.arthurbarre.fr/ordinarthur/rubis-app`
|
|
- Domaine : https://app.rubis.arthurbarre.fr
|
|
- Manifests : `k3s/app/{deployment.yml,service.yml,redis.yml}`
|
|
- Route Traefik : `~/dev/perso/proxmox/ansible/roles/traefik/templates/rubis-app.yml.j2`
|
|
- Workflow CI : `.gitea/workflows/deploy-app.yml`
|
|
- Source : `apps/api`, `apps/web`, `packages/shared` (monorepo pnpm)
|
|
|
|
### Dépendances externes (déjà déployées)
|
|
- **Postgres** : 10.10.10.3:5432, base `rubis_prod`, user `rubis`
|
|
- **MinIO** : `minio.minio.svc.cluster.local:9000`, bucket
|
|
`rubis-prod-invoices`, creds = root MinIO
|
|
- **Redis** : interne au namespace `rubis`, pas de dépendance externe
|
|
|
|
### Secrets K3s (`rubis-app-secrets`)
|
|
Posés une fois manuellement via `kubectl create secret generic` (jamais
|
|
dans les manifests). Re-pose si rotation ou ajout :
|
|
```bash
|
|
kubectl --kubeconfig ~/dev/perso/proxmox/k3s/kubeconfig.yaml \
|
|
-n rubis create secret generic rubis-app-secrets \
|
|
--from-literal=APP_KEY=... \
|
|
--from-literal=PG_PASSWORD=... \
|
|
--from-literal=S3_ACCESS_KEY=... \
|
|
--from-literal=S3_SECRET_KEY=... \
|
|
--from-literal=RESEND_API_KEY=... \
|
|
--from-literal=MISTRAL_API_KEY=... \
|
|
--from-literal=REDIS_PASSWORD="" \
|
|
--dry-run=client -o yaml | kubectl apply -f -
|
|
```
|
|
|
|
### Mise à jour
|
|
Push git, le CI build l'image (`Dockerfile.app`, multi-stage), apply les
|
|
manifests `k3s/app/`, set image, rollout auto. Filter sur `apps/**`,
|
|
`packages/**`, `Dockerfile.app`, `k3s/app/**`.
|
|
|
|
### Particularités du Dockerfile.app
|
|
- `node ace build` plante en CI avec `ERR_UNKNOWN_FILE_EXTENSION` car
|
|
`@poppinss/ts-exec` ne s'enregistre pas à temps avant l'import de
|
|
`bin/console.ts`. **Solution** : on appelle ace via `tsx` (esbuild),
|
|
qui gère nativement les `.ts` → `pnpm exec tsx ace.js build --ignore-ts-errors`
|
|
- `--ignore-ts-errors` car `tests/bootstrap.ts` référence un type généré
|
|
dans `.adonisjs/client/registry/schema.d.ts` qui arrive trop tard. Le
|
|
typecheck strict est exécuté côté CI séparément (`pnpm typecheck`).
|
|
- Vite build appelé directement (`pnpm exec vite build`) au lieu de
|
|
`pnpm build` qui fait `tsc -b && vite build` — le `tsc -b` plante sans
|
|
cache `.tsbuildinfo` à cause de @tanstack/router-core.
|
|
- SPA dist (`apps/web/dist`) copié dans `apps/api/build/public/` pour
|
|
être servi par le static middleware AdonisJS. Une route wildcard
|
|
(`start/routes.ts`) sert `index.html` pour les chemins non-API → SPA
|
|
routing TanStack Router.
|
|
|
|
---
|
|
|
|
## Voie manuelle (debug, hors CI)
|
|
|
|
```bash
|
|
TAG=$(git rev-parse --short HEAD)
|
|
# Landing
|
|
docker build --platform linux/amd64 -t git.arthurbarre.fr/ordinarthur/rubis:$TAG .
|
|
docker push git.arthurbarre.fr/ordinarthur/rubis:$TAG
|
|
kubectl --kubeconfig ~/dev/perso/proxmox/k3s/kubeconfig.yaml \
|
|
-n rubis set image deploy/rubis rubis=git.arthurbarre.fr/ordinarthur/rubis:$TAG
|
|
# App
|
|
docker build --platform linux/amd64 -f Dockerfile.app -t git.arthurbarre.fr/ordinarthur/rubis-app:$TAG .
|
|
docker push git.arthurbarre.fr/ordinarthur/rubis-app:$TAG
|
|
kubectl --kubeconfig ~/dev/perso/proxmox/k3s/kubeconfig.yaml \
|
|
-n rubis set image deploy/rubis-app app=git.arthurbarre.fr/ordinarthur/rubis-app:$TAG
|
|
```
|
|
|
|
⚠️ Cross-compile ARM Mac → linux/amd64 plante sur `@swc/core` au build de
|
|
l'app. Préférer pousser et laisser le CI Gitea (linux/amd64 natif).
|
|
|
|
Si changement route/domaine : `cd ~/dev/perso/proxmox/ansible && ansible-playbook playbooks/gateway.yml`. Penser à `sudo systemctl restart traefik`
|
|
sur la gateway si un nouveau domaine ne récupère pas son cert ACME tout
|
|
seul (le hot-reload de la dynamic config ne déclenche pas toujours le
|
|
challenge LE).
|
|
|
|
---
|
|
|
|
## Quand le domaine définitif sera acheté (`rubis-sur-l-ongle.fr`)
|
|
|
|
1. Records DNS chez OVH : A `@`, A `app`, MX/SPF/DKIM pour Resend
|
|
2. Modifier les rules dans `rubis.yml.j2` et `rubis-app.yml.j2` (Host)
|
|
3. Replay `gateway.yml`
|
|
4. Maj `MAIL_FROM_ADDRESS=relances@rubis-sur-l-ongle.fr` dans le secret
|
|
5. (Optionnel) supprimer les A records `rubis.arthurbarre.fr` et
|
|
`app.rubis.arthurbarre.fr` chez OVH
|
|
|
|
---
|
|
|
|
## Déjà fait — NE PAS refaire
|
|
- Dockerfile (landing) + Dockerfile.app (app)
|
|
- Manifests `k3s/` (landing) + `k3s/app/` (app + Redis)
|
|
- Routes Traefik `rubis.yml.j2` + `rubis-app.yml.j2`
|
|
- DNS OVH : A records `rubis` (id 5413044152) + `app.rubis` (id 5413305619)
|
|
- Repo Gitea + secrets CI (`KUBECONFIG`, `REGISTRY_PASSWORD`)
|
|
- Namespace + secret registry K3s (`gitea-registry`)
|
|
- Postgres : base `rubis_prod` + user `rubis` (10.10.10.3)
|
|
- MinIO : bucket `rubis-prod-invoices`
|
|
- Secret K3s `rubis-app-secrets` (APP_KEY, DB pwd, MinIO, Resend, Mistral)
|
|
|
|
Les prochains `/deploy` font uniquement build + rollout via push git.
|