7.8 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)
Architecture en 2 services : nginx en frontal (web) + API Node interne.
Traefik :443 → app.rubis.arthurbarre.fr:30110 → rubis-web (nginx)
├─ / → SPA static (try_files)
└─ /api/* → rubis-api (ClusterIP :3333)
rubis-web — frontend (NodePort 30110, exposé)
- Image :
git.arthurbarre.fr/ordinarthur/rubis-web - Container :
web(nginx-alpine + SPA Vite dist) - Sert /assets/* (cache 1y immutable), / (SPA fallback try_files)
- Reverse-proxy /api/* → rubis-api.rubis.svc.cluster.local:3333
- Manifest :
k3s/app/web.yml - Workflow CI :
.gitea/workflows/deploy-web.yml - Source :
apps/web,packages/shared+apps/web/nginx.conf
rubis-api — backend (ClusterIP, interne uniquement)
- Image :
git.arthurbarre.fr/ordinarthur/rubis-api - Container :
api(Node 22 + AdonisJS V7) - Workers BullMQ dans le même process (cf.
start/queue.ts) - Init container
migrate:node ace.js migration:run --forcedepuis/app/apps/api/build(idempotent) - Probes K3s sur
/api/v1/health - Manifest :
k3s/app/api.yml(Deployment + Service ClusterIP + ConfigMap) - Workflow CI :
.gitea/workflows/deploy-api.yml - Source :
apps/api,packages/shared
rubis-redis — backend BullMQ + cache (ClusterIP)
- Image :
redis:7.4-alpine - PVC 1Gi local-path, AOF on, maxmemory 256mb allkeys-lru
- Manifest :
k3s/app/redis.yml - Re-déployé par le workflow API (path filter inclut
redis.yml)
Infra commune
- Domaine : https://app.rubis.arthurbarre.fr (DNS A 5413305619)
- Route Traefik :
~/dev/perso/proxmox/ansible/roles/traefik/templates/rubis-app.yml.j2
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 → un (ou les deux) workflow(s) CI se déclenchent selon les paths modifiés. Build+rollout indépendants.
| Path modifié | Workflow déclenché |
|---|---|
apps/web/**, Dockerfile.web, k3s/app/web.yml |
deploy-web.yml |
apps/api/**, Dockerfile.api, k3s/app/api.yml, k3s/app/redis.yml |
deploy-api.yml |
packages/shared/**, pnpm-lock.yaml, tsconfig.base.json, … |
les deux |
Particularités du Dockerfile.api
node ace buildplante avecERR_UNKNOWN_FILE_EXTENSIONcar@poppinss/ts-execne s'enregistre pas à temps avant l'import debin/console.ts. Solution :pnpm exec tsx ace.js build --ignore-ts-errors(tsx = esbuild, gère .ts nativement).--ignore-ts-errorscartests/bootstrap.tsréférence un type généré tardivement (.adonisjs/client/registry/schema.d.ts). Le typecheck strict est exécuté en CI séparée (pnpm typecheck).
Particularités du Dockerfile.web
- Vite build appelé directement (
pnpm exec vite build) au lieu du scripttsc -b && vite buildqui plante sans cache.tsbuildinfoà cause de @tanstack/router-core. - nginx.conf inclut le upstream
rubis-api.rubis.svc.cluster.local:3333. Si on renomme le service API, c'est ici qu'il faut mettre à jour (en plus du manifest).
Voie manuelle (debug, hors CI)
TAG=$(git rev-parse --short HEAD)
# Landing (rubis.arthurbarre.fr)
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 — web (frontend)
docker build --platform linux/amd64 -f Dockerfile.web \
-t git.arthurbarre.fr/ordinarthur/rubis-web:$TAG .
docker push git.arthurbarre.fr/ordinarthur/rubis-web:$TAG
kubectl --kubeconfig ~/dev/perso/proxmox/k3s/kubeconfig.yaml \
-n rubis set image deploy/rubis-web web=git.arthurbarre.fr/ordinarthur/rubis-web:$TAG
# App — api (backend)
docker build --platform linux/amd64 -f Dockerfile.api \
-t git.arthurbarre.fr/ordinarthur/rubis-api:$TAG .
docker push git.arthurbarre.fr/ordinarthur/rubis-api:$TAG
kubectl --kubeconfig ~/dev/perso/proxmox/k3s/kubeconfig.yaml \
-n rubis set image deploy/rubis-api api=git.arthurbarre.fr/ordinarthur/rubis-api:$TAG \
&& kubectl --kubeconfig ~/dev/perso/proxmox/k3s/kubeconfig.yaml \
-n rubis patch deploy/rubis-api --type=json \
-p="[{\"op\":\"replace\",\"path\":\"/spec/template/spec/initContainers/0/image\",\"value\":\"git.arthurbarre.fr/ordinarthur/rubis-api:$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.web + Dockerfile.api (app split)
- nginx.conf (apps/web/nginx.conf) avec upstream rubis-api
- Manifests
k3s/(landing) +k3s/app/{api,web,redis}.yml - 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) - ConfigMap
rubis-api-config(env non-sensibles)
Les prochains /deploy font uniquement build + rollout via push git.