Le 503 à chaque déploiement, et pourquoi le corriger aurait corrompu ma base

· Kubernetes, SQLite

le symptome

À chaque déploiement d'une de mes applications, quelques secondes après le push, le navigateur affichait no available server — la page d'erreur de Traefik quand aucun backend n'est joignable. Le service revenait seul au bout d'une poignée de secondes.

la mauvaise piste

Ma première hypothèse était mauvaise, et je l'ai suivie trop longtemps : je venais d'empiler six migrations de schéma non déployées, et j'étais convaincu que l'une d'elles faisait planter le conteneur au démarrage. J'ai perdu du temps à préparer des procédures de rollback pour un problème qui n'existait pas.

Les logs ont tranché en trois lignes :

$ kubectl -n staging get pods
NAME                          READY   STATUS    RESTARTS   AGE
fridge-app-65bf689567-ljks6   1/1     Running   0          109s

$ kubectl -n staging logs deploy/fridge-app --tail=5
fridge à l'écoute sur 0.0.0.0:8080

Zéro redémarrage. Le pod démarrait parfaitement, migrations comprises. Je testais simplement pendant la fenêtre de bascule.

la cause reelle

Le déploiement utilise strategy: Recreate avec une seule réplique. Kubernetes arrête donc l'ancien pod avant de démarrer le nouveau : entre les deux, Traefik n'a plus aucun backend. D'où le 503, systématique et bref.

la correction evidente, et pourquoi elle est piegeuse

Le réflexe est immédiat — passer en rolling update avec surge :

strategy:
  type: RollingUpdate
  rollingUpdate:
    maxSurge: 1
    maxUnavailable: 0

Le nouveau pod démarre avant l'arrêt de l'ancien, zéro coupure. Sauf que l'application stocke ses données dans SQLite sur un volume persistant. Avec maxSurge: 1, deux processus écrivent quelques secondes durant sur le même fichier. SQLite tolère plusieurs lecteurs, mais un seul écrivain : le résultat probable est un journal WAL incohérent, au mieux des écritures perdues.

Le pool de connexions Go est bien limité à SetMaxOpenConns(1) — mais cette garantie vaut à l'intérieur d'un processus, pas entre deux. Et le PVC en ReadWriteOnce ne bloque rien : les deux pods sont sur le même nœud, Kubernetes monte donc le volume deux fois sans protester.

ce que j'ai fait a la place

J'ai gardé Recreate et attaqué l'autre bout du problème : réduire la durée de la fenêtre plutôt que la supprimer. La sonde de disponibilité attendait 3 secondes puis testait toutes les 10 — un pod prêt en 1 seconde pouvait donc rester déclaré indisponible pendant 13.

readinessProbe:
  exec: { command: ["/app/fridge-app", "--healthcheck"] }
  initialDelaySeconds: 1
  periodSeconds: 2
  failureThreshold: 3

Coupure ramenée d'environ quinze secondes à trois ou quatre. Pour une application familiale, c'est le bon compromis.

ce que je retiens