Kubernetes scale-to-zero : réduire les coûts sans dégrader les SLA d’un SaaS

KUBERNETES · FINOPS · SRE

Le Kubernetes scale-to-zero peut supprimer le coût des workers inactifs, mais seulement si le réveil, la file d’attente et le SLA sont conçus ensemble. Voici une méthode pour choisir les bonnes charges et les tester sans mettre la production en danger.

Kubernetes scale-to-zero : file d’attente et workers autoscalés pour réduire les coûts sans dégrader le SLA
Une architecture scale-to-zero sûre conserve un chemin de reprise et une file durable.

💶

Ciblez les bons workers

Les traitements asynchrones et intermittents sont de meilleurs candidats que les API synchrones.

⏱️

Mesurez le réveil

Le coût évité ne compense pas un délai de démarrage incompatible avec votre engagement client.

🧱

Protégez la reprise

Une métrique durable et une file tampon empêchent la perte de travail pendant le retour à zéro.

Le scale-to-zero consiste à autoriser un déploiement à descendre à zéro réplique lorsqu’il ne traite plus de charge. Pour une équipe SaaS, l’intérêt est concret : ne plus réserver des pods de workers pendant les nuits, week-ends ou creux d’activité.

La décision ne se résume pourtant pas à régler minReplicas: 0. Une requête HTTP n’attend pas toujours le temps nécessaire pour créer un pod, télécharger une image et rendre l’application prête. Le bon périmètre est donc un choix de service, pas un simple réglage de coût.

Kubernetes scale-to-zero : ce que vous gagnez, et ce que vous échangez

Avec un autoscaler classique, un service conserve souvent une ou plusieurs répliques minimales. Elles absorbent les premières requêtes et maintiennent les connexions chaudes, mais elles représentent aussi une consommation permanente de CPU, mémoire et parfois de nœuds.

Le scale-to-zero transfère ce coût vers un délai de reprise. Ce délai comprend le déclenchement de l’autoscaling, le placement du pod, le pull éventuel de l’image, le démarrage applicatif et les contrôles de disponibilité. Il doit être comparé à un objectif de service explicite.

💡

Le bon indicateur n’est pas le nombre de pods

Suivez le temps entre l’arrivée du travail et le premier traitement réussi. C’est cette latence de reprise qui détermine si zéro réplique reste acceptable.

Quels workloads peuvent vraiment passer à zéro ?

Les meilleurs candidats sont les consommateurs de file : génération de rapports, conversion de documents, synchronisation, tâches planifiées, analyse d’images ou traitements IA par lot. Le travail persiste dans une file et peut attendre quelques secondes ou minutes.

Une API client ou un endpoint de paiement est plus délicat. Sans proxy, buffering ou capacité minimale, la première requête peut expirer avant que le pod soit prêt. Garder une réplique, ou séparer l’API du worker asynchrone, est généralement plus sûr.

Bon candidat

Worker qui consomme RabbitMQ, SQS ou Kafka, avec message durable et idempotence.

⚖️

À tester

Backend HTTP peu fréquenté, derrière un proxy capable de tamponner ou de déclencher le réveil.

À éviter au départ

Service synchrone critique, connexion longue durée ou composant qui conserve un état non répliqué.

Une architecture qui absorbe le réveil sans perdre de travail

Flux à garder en tête

ÉvénementFile durableAutoscalerWorker prêt

La file protège le travail ; l’autoscaler transforme sa profondeur en capacité de calcul.

KEDA est souvent adapté à ce modèle parce qu’il sait déclencher des déploiements à partir de nombreuses sources de métriques, dont les files. Le HPA Kubernetes peut aussi être utilisé lorsque la métrique objet ou externe est disponible et correctement exposée.

Dans les deux cas, la métrique doit survivre à l’absence de pods. Une métrique calculée uniquement par l’application arrêtée ne peut pas déclencher son propre redémarrage. Préférez la profondeur de file, un compteur externe ou un événement durable.

⚠️

Préparez les images et dépendances

Un gros téléchargement d’image ou une migration exécutée au démarrage peut faire exploser le délai de reprise. Mesurez-le sur un nœud froid, pas seulement sur votre poste.

Mini-lab : tester un worker Redis avec KEDA en 20 minutes

Faites ce test dans un cluster de développement, un namespace isolé et avec une instance Redis dédiée. N’utilisez pas une file de production : l’objectif est de mesurer le comportement de reprise sans impacter les consommateurs existants.

✅ Pré-requis

✓ Un cluster Kubernetes de test et kubectl configuré
✓ KEDA installé dans le cluster
✓ Une file Redis de test et des identifiants limités à ce namespace
✓ Un worker idempotent : rejouer un message ne doit pas créer de doublon

Déployez d’abord un worker minimal, puis appliquez un ScaledObject. La commande suivante force un déploiement sans réplique initiale ; adaptez l’image et la connexion Redis à votre environnement de test.

kubectl -n scale-lab create deployment worker --image=busybox:1.36 \
  -- /bin/sh -c 'while true; do echo worker-ready; sleep 30; done'

kubectl -n scale-lab scale deployment worker --replicas=0
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: worker-from-queue
  namespace: scale-lab
spec:
  scaleTargetRef:
    name: worker
  minReplicaCount: 0
  maxReplicaCount: 5
  pollingInterval: 15
  cooldownPeriod: 120
  triggers:
    - type: redis
      metadata:
        address: redis.scale-lab.svc.cluster.local:6379
        listName: jobs
        listLength: "1"

Ajoutez ensuite un message dans la file de test et observez le déploiement. Le résultat attendu est le passage de zéro à au moins un pod Ready, sans disparition du message avant son traitement effectif.

kubectl -n scale-lab get deploy,pods -w
# Dans un second terminal, injecter un message dans la file de test.

Notez l’heure de l’injection, l’heure de création du pod et l’heure du premier traitement confirmé. Répétez après avoir supprimé le pod et, si possible, sur un nœud où l’image n’est pas en cache. Vous obtenez ainsi un budget de démarrage à froid réaliste.

Pour revenir en arrière, supprimez le ScaledObject puis remettez une réplique minimale. Vérifiez enfin que la file est vide avant de supprimer le namespace de test.

kubectl -n scale-lab delete scaledobject worker-from-queue
kubectl -n scale-lab scale deployment worker --replicas=1
# Après vérification : kubectl delete namespace scale-lab

Passer en production sans dégrader le SLA

Commencez par un seul worker non critique et définissez un seuil de succès : par exemple, 99 % des travaux pris en charge en moins de 30 secondes, y compris après une période inactive. Sans seuil, vous ne saurez pas arbitrer entre économie et expérience client.

Ajoutez des alertes sur l’âge du message le plus ancien, la profondeur de file, les échecs de traitement et la durée de démarrage. Une alerte CPU ne suffit pas lorsque l’application est volontairement à zéro.

🔴

Ne confondez pas économie et absence de capacité

Gardez une capacité minimale pour les chemins synchrones et pour les opérations de reprise. Une file qui grandit sans limite devient une indisponibilité différée.

Les erreurs qui font échouer un scale-to-zero

⏱ Une sonde de démarrage trop optimiste

Le pod existe, mais l’application n’est pas prête. Utilisez une startupProbe et ne considérez le réveil terminé qu’après le premier traitement réussi.

📬 Une file non durable

Si le message disparaît avant l’accusé de traitement, un arrêt ou un redémarrage produit des pertes. Exigez des acknowledgements et de l’idempotence.

📉 Un cooldown mal réglé

Un retour à zéro trop rapide crée du yo-yo. Réglez le délai à partir du rythme réel des événements et observez les oscillations.

Piloter le coût et la fiabilité dans le même tableau de bord

Un projet scale-to-zero doit être suivi par le produit, la plateforme et le support. Le gain financier est visible dans les heures de ressources évitées, mais le risque apparaît d’abord dans la latence des tâches et les tickets clients.

Construisez un tableau de bord par workload. Affichez le nombre de réveils, la durée médiane et p95 de démarrage, l’âge des messages, les échecs, la consommation demandée et le coût estimé par période inactive.

Ajoutez un repère métier : délai de livraison d’un export, temps de génération d’un document ou date de traitement d’une synchronisation. Une métrique technique seule ne dit pas si l’attente devient visible pour le client.

Fixez un budget de reprise avant de réduire la capacité. Si un worker doit répondre en moins de dix secondes et que le démarrage p95 atteint vingt secondes, la bonne décision est une réplique minimale, une image plus légère ou un chemin asynchrone.

Évitez aussi de faire descendre simultanément tous les composants. La file, le stockage des résultats, l’observabilité et le mécanisme de déclenchement doivent rester disponibles, sinon aucun signal fiable ne peut relancer les workers.

Un déploiement progressif est plus sûr

Activez le scale-to-zero sur un pourcentage réduit des traitements ou sur un tenant interne. Observez plusieurs cycles de charge faible et de reprise. Augmentez ensuite le périmètre lorsque les délais et les erreurs restent dans le budget fixé.

Gardez un rollback simple dans le manifeste : remonter minReplicaCount à un ou désactiver le ScaledObject. Ce changement doit être connu de l’astreinte et testé avant que le mécanisme ne devienne une dépendance de production.

Décision pragmatique

Commencez par les workers dont la file retient déjà le travail. C’est le chemin le plus court pour faire baisser les coûts sans déplacer le risque sur une requête utilisateur.

FAQ : Kubernetes scale-to-zero

Le scale-to-zero convient-il aux API HTTP ?
Oui, seulement si le chemin d’entrée absorbe le démarrage à froid. Pour une API avec SLA serré, conservez une réplique ou utilisez une couche de buffering adaptée.
Faut-il choisir HPA ou KEDA ?
Le HPA convient aux métriques Kubernetes et externes bien exposées. KEDA est pratique quand la charge provient d’une file, d’un événement ou d’un connecteur dédié.
Comment mesurer un démarrage à froid ?
Mesurez de l’arrivée du message au premier résultat confirmé. Faites plusieurs essais, dont un avec une image absente du cache du nœud.
Quelle est la première alerte à ajouter ?
Alertez sur l’âge du plus ancien message. Il révèle une attente client réelle, même lorsque la consommation CPU reste faible.

Le scale-to-zero est un excellent levier FinOps quand il s’applique à des traitements asynchrones, observables et réversibles. La meilleure première mise en œuvre est modeste : un worker, une file durable, un seuil de latence et un rollback documenté.

Réduire les coûts Kubernetes sans sacrifier la disponibilité

Linux-Man vous aide à qualifier vos workloads, mesurer le démarrage à froid et sécuriser le passage en production.

Contacte-moi →

Laisser un commentaire