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>
5.9 KiB
Deploy memory — rubis
Ce repo contient deux déploiements distincts sur la même infra K3s,
namespace rubis :
- Landing statique (
rubis.arthurbarre.fr) — image nginx-alpine. - 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.frpas 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 --forcedepuis/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, userrubis - MinIO :
minio.minio.svc.cluster.local:9000, bucketrubis-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 :
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 buildplante en CI avecERR_UNKNOWN_FILE_EXTENSIONcar@poppinss/ts-execne s'enregistre pas à temps avant l'import debin/console.ts. Solution : on appelle ace viatsx(esbuild), qui gère nativement les.ts→pnpm exec tsx ace.js build --ignore-ts-errors--ignore-ts-errorscartests/bootstrap.tsréférence un type généré dans.adonisjs/client/registry/schema.d.tsqui 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 depnpm buildqui faittsc -b && vite build— letsc -bplante sans cache.tsbuildinfoà cause de @tanstack/router-core. - SPA dist (
apps/web/dist) copié dansapps/api/build/public/pour être servi par le static middleware AdonisJS. Une route wildcard (start/routes.ts) sertindex.htmlpour les chemins non-API → SPA routing TanStack Router.
Voie manuelle (debug, hors CI)
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)
- Records DNS chez OVH : A
@, Aapp, MX/SPF/DKIM pour Resend - Modifier les rules dans
rubis.yml.j2etrubis-app.yml.j2(Host) - Replay
gateway.yml - Maj
MAIL_FROM_ADDRESS=relances@rubis-sur-l-ongle.frdans le secret - (Optionnel) supprimer les A records
rubis.arthurbarre.fretapp.rubis.arthurbarre.frchez 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+ userrubis(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.