Blog
11 mai 2026 - 9 MIN DE LECTURE

Une architecture GitOps minimale pour Laravel sur k3s

Une architecture compacte pour exécuter plusieurs applications Laravel sur un petit VPS avec k3s, Argo CD, Redis, Postgres, sauvegardes, supervision et reprise rapide après incident.

Voici une architecture GitOps minimale pour exécuter plusieurs applications Laravel sur un petit VPS.

L’objectif est pratique :

  • un VPS peu coûteux
  • cinq applications Laravel
  • HTTPS
  • Redis
  • Postgres
  • sauvegardes des fichiers et des bases de données
  • supervision de disponibilité
  • déploiements progressifs
  • reprise après incident en moins de 30 minutes

Ce n’est pas de la haute disponibilité. C’est une configuration peu coûteuse et reproductible, où la stratégie de reprise est plus solide que le serveur lui-même.

Architecture

                              Internet
                                  |
                                  v
                         +----------------+
                         | Traefik + TLS  |
                         +----------------+
                                  |
              +-------------------+-------------------+
              |                   |                   |
              v                   v                   v
          Laravel A           Laravel B           Laravel C...
              |                   |                   |
      +-------+-------+   +-------+-------+   +-------+-------+
      | réplicas web  |   | réplicas web  |   | réplicas web  |
      | worker queue  |   | worker queue  |   | worker queue  |
      | tâche cron    |   | tâche cron    |   | tâche cron    |
      +-------+-------+   +-------+-------+   +-------+-------+
              |                   |                   |
              +----------+--------+--------+----------+
                         |                 |
                         v                 v
                    +---------+       +----------+
                    |  Redis  |       | Postgres |
                    +---------+       +----------+
                                           |
                                           v
                                  sauvegardes compatibles S3

Le cluster reste assez petit pour être compris rapidement :

system
  Argo CD
  cert-manager
  secrets scellés ou externes
  stockage
  contrôleur de sauvegarde

data
  Postgres
  Redis

monitoring
  contrôles de disponibilité

apps
  app-a
  app-b
  app-c
  app-d
  app-e

La règle est simple : l’infrastructure partagée va dans des namespaces partagés, les applications ont leurs propres namespaces, et tout ce qui compte peut être recréé depuis Git.

Flux GitOps

Développeur
   |
   | git push
   v
Pipeline CI
   |
   | construit l’image
   | pousse l’image
   v
Registre de conteneurs
   |
   | met à jour le tag ou le digest dans Git
   v
Dépôt GitOps
   |
   | Argo CD surveille Git
   v
Cluster k3s
   |
   | rolling update
   | readiness probes avant trafic
   v
Production

Le serveur ne devrait pas être configuré à la main après le bootstrap. Les changements manuels disparaissent. Les changements Git survivent.

Stack d’outils

BesoinOutil
Kubernetes légerk3s
Déploiement GitOpsArgo CD
Ingress et routageTraefik
Certificats TLScert-manager
Secrets dans GitSealed Secrets ou External Secrets
Base de donnéesCloudNativePG
Cache et queuesRedis
Fichiers LaravelLonghorn ou volumes persistants locaux
Restauration du clusterVelero
Contrôles publicsUptime Kuma

Les outils peuvent changer, mais évitez de supprimer les rôles. Même une petite plateforme a besoin de routage, secrets, stockage, sauvegardes et contrôles de santé.

L’unité Laravel

Chaque application Laravel devrait ressembler au même petit bloc :

namespace applicatif
  Ingress
    -> Service
      -> web Deployment, 2 réplicas

  queue Deployment, 1+ réplicas
  scheduler CronJob
  app Secret
  storage PVC
  déclaration de base de données

Pour cinq applications, répétez cette forme cinq fois.

apps
  app-a  -> web + queue + scheduler + fichiers + db
  app-b  -> web + queue + scheduler + fichiers + db
  app-c  -> web + queue + scheduler + fichiers + db
  app-d  -> web + queue + scheduler + fichiers + db
  app-e  -> web + queue + scheduler + fichiers + db

Le processus web n’est qu’une partie de Laravel. Les queues et les commandes planifiées doivent être traitées comme des workloads à part entière.

Déploiement web

Exécutez le processus web avec deux réplicas quand vous voulez des déploiements sans interruption.

apiVersion: apps/v1
kind: Deployment
spec:
  replicas: 2
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 0
      maxSurge: 1

Le trafic ne doit atteindre que les pods prêts.

livenessProbe:
  httpGet:
    path: /up
    port: 8080

readinessProbe:
  httpGet:
    path: /ready
    port: 8080

Utilisez /up pour dire que le processus est vivant. Utilisez /ready pour dire que le pod peut recevoir du trafic.

L’application reçoit son environnement depuis un secret Kubernetes :

envFrom:
  - secretRef:
      name: app-env

Le répertoire Laravel mutable devrait être un volume monté :

/var/www/html/storage/app

Tout le reste devrait être dans l’image.

Queues et scheduler

Les queues ne devraient pas tourner dans le pod web.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: queue
spec:
  replicas: 1
  template:
    spec:
      containers:
        - name: queue
          command: ["php", "artisan", "horizon"]

Si une queue est critique, Redis doit être durable ou la queue doit utiliser un backend durable.

Le scheduler est un CronJob Kubernetes :

apiVersion: batch/v1
kind: CronJob
metadata:
  name: scheduler
spec:
  schedule: "* * * * *"
  concurrencyPolicy: Forbid
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: scheduler
              command: ["php", "artisan", "schedule:run"]

Cela remplace un démon cron permanent dans le conteneur.

Base de données et Redis

Utilisez un cluster Postgres managé dans Kubernetes et créez une base par application.

Cluster Postgres
  base app_a
  base app_b
  base app_c
  base app_d
  base app_e

CloudNativePG fournit un contrôleur Postgres natif Kubernetes, des bases déclaratives, des sauvegardes et des workflows de restauration.

Une déclaration de base peut rester minuscule :

apiVersion: postgresql.cnpg.io/v1
kind: Database
metadata:
  name: app-a
spec:
  cluster:
    name: postgres
  name: app_a
  owner: app

Redis peut être partagé par les applications :

Redis
  cache
  sessions
  queues
  horizon

Sur les configurations peu coûteuses, Redis est souvent le service le moins protégé. Soyez explicite :

Usage RedisPersistance nécessaire ?
cachenon
sessions jetablespeut-être
queuesoui, sauf si perdre des jobs est acceptable
métriques Horizonnon

Déploiements sans interruption

Le chemin de déploiement devrait ressembler à ceci :

ancien pod prêt
ancien pod prêt
       |
       | nouvelle image
       v
ancien pod prêt
ancien pod prêt
nouveau pod en démarrage
       |
       | readiness OK
       v
ancien pod prêt
nouveau pod prêt
nouveau pod prêt
       |
       | arrêt des anciens pods
       v
nouveau pod prêt
nouveau pod prêt

Les prérequis minimum :

  • deux réplicas web
  • maxUnavailable: 0
  • une vraie readiness probe
  • assez de mémoire pour les anciens et nouveaux pods pendant le rollout
  • aucune tâche destructive au démarrage
  • migrations exécutées séparément
  • migrations de base de données rétrocompatibles

La règle de migration est celle que beaucoup oublient :

déploiement 1 : ajouter une colonne nullable ou une nouvelle table
déploiement 2 : livrer le code qui l’utilise
déploiement 3 : supprimer l’ancienne colonne seulement après disparition de l’ancien code

Ne faites pas dépendre le nouveau pod d’un schéma qui casse l’ancien pod encore en train de recevoir du trafic.

Sauvegardes

Les sauvegardes doivent couvrir quatre choses différentes.

+-------------------+-----------------------------+------------------------+
| État              | Sauvegarde                  | Cible de restauration  |
+-------------------+-----------------------------+------------------------+
| Données Postgres  | sauvegarde DB + WAL         | cluster Postgres       |
| Fichiers Laravel  | sauvegarde de volume        | PVC applicatif         |
| État Kubernetes   | Velero                      | ressources du cluster  |
| Secrets           | scellés dans Git + copie clé| secrets applicatifs    |
+-------------------+-----------------------------+------------------------+

Le stockage objet est la cible la plus courante :

cluster k3s
  sauvegardes Postgres ----+
  sauvegardes volumes -----+----> bucket compatible S3
  sauvegardes Velero ------+

Base de départ pour une sauvegarde quotidienne :

schedule: "10 2 * * *"
ttl: 168h
destination: s3://production-backups

Sept jours de rétention n’ont rien de magique. C’est seulement un point de départ. L’important est que les sauvegardes de bases, de fichiers et de métadonnées du cluster soient toutes couvertes.

Reprise après incident en 30 minutes

La reprise rapide vient de la réduction des décisions pendant l’incident.

00:00  créer un nouveau VPS
05:00  installer k3s
08:00  installer Argo CD
10:00  restaurer l’accès Git et la clé de déchiffrement des secrets
12:00  synchroniser les contrôleurs de plateforme
15:00  restaurer Postgres et les volumes depuis S3
22:00  synchroniser les applications Laravel
25:00  pointer le DNS ou l’IP flottante vers le nouveau serveur
30:00  contrôles de disponibilité au vert

Le schéma de reprise :

Dépôt Git                    Sauvegardes S3
  manifests plateforme          postgres
  manifests apps                fichiers
  secrets scellés               ressources cluster
        |                              |
        +--------------+---------------+
                       |
                       v
                 nouveau serveur k3s
                       |
                       v
                 applications restaurées

Le cluster est jetable. Git et les sauvegardes ne le sont pas.

Supervision minimale

Commencez par des contrôles sur lesquels vous réagirez vraiment.

Uptime Kuma
  GET https://app-a.example.com/up
  GET https://app-b.example.com/up
  GET https://app-c.example.com/up
  GET https://app-d.example.com/up
  GET https://app-e.example.com/up

Argo CD
  apps synchronisées
  apps saines

Base de données
  cluster sain
  sauvegarde récente

Sauvegardes
  dernière sauvegarde Velero terminée
  cible de sauvegarde des volumes joignable

Nœud
  disque non plein
  mémoire non épuisée

Prometheus et Grafana sont utiles plus tard. Pour une première version, des contrôles publics de disponibilité et la fraîcheur des sauvegardes détectent déjà les pannes les plus importantes.

Checklist finale

Pour chaque application Laravel :

  • un namespace
  • un déploiement web avec deux réplicas
  • un service
  • un ingress avec TLS
  • un déploiement queue si des jobs sont utilisés
  • un CronJob scheduler
  • un secret d’environnement
  • un PVC pour storage/app si les fichiers sont locaux
  • une base de données
  • des readiness et liveness probes
  • l’application incluse dans les contrôles de disponibilité
  • l’état applicatif inclus dans les sauvegardes

Pour la plateforme :

  • Argo CD peut reconstruire le cluster depuis Git
  • les secrets peuvent être déchiffrés après une reprise
  • les sauvegardes Postgres se restaurent correctement
  • les sauvegardes de fichiers se restaurent correctement
  • Velero peut restaurer les ressources Kubernetes
  • le basculement DNS ou IP est documenté

Voilà la vraie architecture. Kubernetes n’est que le runtime. Le système fonctionne parce que le déploiement, les sauvegardes et la reprise sont conçus ensemble.