Quand votre image conteneur est prête, où s'exécute-t-elle réellement ?
Vous avez construit l'image. Vous l'avez scannée pour détecter les vulnérabilités. Vous l'avez poussée vers un registre. Voici maintenant le moment qui sépare un pipeline fonctionnel d'un déploiement réel : exécuter ce conteneur quelque part où les utilisateurs peuvent l'atteindre.
La façon dont vous exécutez ce conteneur dépend entièrement de sa destination. Un serveur unique et un cluster Kubernetes se ressemblent sur le papier — les deux exécutent des conteneurs — mais l'expérience opérationnelle est complètement différente. Le choix affecte la façon dont vous mettez à jour, dont vous récupérez après des échecs, et la quantité de coordination manuelle que votre équipe doit effectuer à chaque nouvelle version.
Exécuter des conteneurs sur un serveur unique
Un déploiement sur un serveur unique semble simple. Vous vous connectez en SSH à la machine, exécutez docker run avec le tag d'image que vous venez de promouvoir, et l'application démarre. Dans un environnement de démonstration, l'histoire s'arrête là.
En pratique, un serveur unique exécute rarement un seul conteneur. Vous avez généralement un conteneur d'application, un conteneur de base de données, un cache, peut-être un worker de file d'attente. Ces conteneurs doivent démarrer dans le bon ordre, communiquer entre eux sur le bon réseau, et gérer le cas où l'un d'eux plante. C'est là que docker-compose devient utile. Vous définissez tous les services, leurs dépendances, leurs ports et leurs politiques de redémarrage dans un seul fichier. Une seule commande démarre tout dans le bon ordre.
Le vrai défi apparaît lorsque vous devez mettre à jour la version de l'application. Sur un serveur unique, vous arrêtez l'ancien conteneur et démarrez le nouveau. Pendant cette fenêtre, l'application ne peut pas répondre aux requêtes. Si l'application est utilisée par de vraies personnes, ce temps d'arrêt est important.
La façon la plus simple de réduire le temps d'arrêt est d'exécuter deux conteneurs côte à côte. Gardez l'ancienne version en cours d'exécution pendant que la nouvelle version démarre. Une fois que le nouveau conteneur est prêt à accepter des connexions, basculez le trafic vers lui, puis arrêtez l'ancien conteneur. C'est une mise à jour progressive dans sa forme la plus basique. Vous pouvez le faire manuellement avec un script, ou utiliser un proxy inverse comme Nginx ou Traefik pour gérer le basculement du trafic.
Mais même avec un modèle de mise à jour progressive, un serveur unique a une limite stricte. Si le serveur lui-même tombe en panne, l'application tombe en panne. Si vous devez appliquer un correctif de sécurité au système d'exploitation hôte, vous devez planifier un temps d'arrêt. Pour les outils internes utilisés par une petite équipe, ce compromis est souvent acceptable. Pour les applications destinées aux clients, ce ne l'est généralement pas.
Exécuter des conteneurs sur Kubernetes
Kubernetes traite les problèmes des déploiements sur serveur unique comme des problèmes résolus et s'appuie sur eux. Vous ne gérez pas les conteneurs directement. Vous définissez un objet Deployment qui décrit l'état souhaité : quelle image exécuter, combien de réplicas, quelles sondes de santé utiliser, et comment effectuer les mises à jour.
Lorsque vous mettez à jour le tag d'image dans un Deployment, Kubernetes ne s'arrête pas et ne redémarre pas tout. Il crée de nouveaux pods avec la nouvelle image, attend qu'ils passent leurs sondes de santé, puis termine progressivement les anciens pods. Pendant tout le processus, il y a toujours au moins un pod qui sert le trafic. Les utilisateurs ne voient pas d'interruption de service.
Un pod est la plus petite unité dans Kubernetes. Il peut exécuter un ou plusieurs conteneurs, mais l'idée clé est qu'un pod est éphémère. Kubernetes crée des pods, les détruit et les déplace vers différents nœuds selon les besoins. Vous ne pensez jamais au serveur spécifique sur lequel un pod s'exécute. Le cluster s'en charge.
La différence entre un serveur unique et Kubernetes ne concerne pas seulement la mise à l'échelle pour plus de trafic. Il s'agit de savoir qui possède la coordination. Sur un serveur unique, vous décidez de l'ordre de démarrage, de la politique de redémarrage et de la gestion des échecs. Vous écrivez des scripts ou utilisez docker-compose pour appliquer ces décisions. Sur Kubernetes, l'orchestrateur possède cette coordination. Il vérifie périodiquement la santé des pods, redémarre les pods défaillants et redistribue les pods vers des nœuds sains lorsqu'un nœud tombe en panne.
Ce changement de propriété modifie la façon dont votre équipe opère. Vous arrêtez d'écrire des scripts qui gèrent le cycle de vie des conteneurs. Vous commencez à écrire des manifests Deployment qui décrivent l'état souhaité, et vous laissez le cluster trouver comment atteindre cet état.
Voici à quoi ressemble un manifest Deployment minimal en pratique :
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: my-registry/my-app:v1.2.3
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
Ce manifest indique à Kubernetes d'exécuter trois réplicas, de les mettre à jour un par un, et de ne router le trafic vers un pod qu'après que son endpoint /health répond avec succès.
Comment choisir entre les deux
Le choix entre un serveur unique et Kubernetes n'est pas un test de pureté technique. C'est une décision basée sur les exigences opérationnelles.
Le diagramme suivant peut vous aider à décider quelle voie correspond à votre situation :
Utilisez un serveur unique avec docker-compose lorsque :
- L'application est utilisée par une petite équipe interne.
- Les temps d'arrêt pour les mises à jour ou la maintenance sont acceptables.
- Vous avez un ou deux services à gérer.
- Vous n'avez pas besoin de mise à l'échelle horizontale.
- La taille de votre équipe est petite et vous souhaitez une complexité d'infrastructure minimale.
Utilisez Kubernetes lorsque :
- L'application doit être disponible même pendant les mises à jour.
- Vous devez faire évoluer les services indépendamment en fonction du trafic.
- Vous exécutez plusieurs services qui doivent être déployés et mis à jour séparément.
- Vous souhaitez une récupération automatisée en cas de panne de nœud.
- Votre équipe a la maturité opérationnelle pour gérer un cluster.
Il existe un terrain d'entente. Certaines équipes exécutent un petit cluster Kubernetes avec un seul nœud à l'aide d'outils comme K3s ou MicroK8s. Cela vous donne les fonctionnalités de mise à jour progressive et de sonde de santé de Kubernetes sans la complexité totale d'un cluster multi-nœuds. Cela vaut la peine d'être envisagé si vous souhaitez les modèles de déploiement mais n'avez pas encore besoin de l'échelle.
La règle qui ne change jamais
Quel que soit l'endroit où vous déployez, une règle reste la même : l'image qui s'exécute en production doit être exactement la même image qui a passé tous les tests et scans dans le pipeline.
Ne reconstruisez jamais l'image sur le serveur. Ne tirez jamais un tag différent parce que "cela devrait être le même". Ne laissez jamais personne se connecter en SSH au serveur et exécuter un conteneur avec une image modifiée localement. Si l'image dans le registre n'est pas l'image qui s'exécute, vous avez perdu la capacité de reproduire, d'auditer et de revenir en arrière.
C'est pourquoi le tagging et la promotion des images sont importants. Lorsque vous promouvez une image de l'environnement de staging vers la production, vous ne reconstruisez rien. Vous changez simplement quel environnement est autorisé à tirer ce tag spécifique. Les octets sont identiques.
Liste de contrôle pratique pour le déploiement de conteneurs
Avant de déployer un conteneur dans un environnement quelconque, confirmez ces points :
- Le tag d'image dans le déploiement correspond au tag qui a passé le pipeline.
- Le conteneur a un endpoint de sonde de santé qui indique à l'orchestrateur quand il est prêt.
- Les variables d'environnement et les secrets sont correctement définis pour l'environnement cible.
- La stratégie de mise à jour est définie : mise à jour progressive pour zéro temps d'arrêt, recréation pour les cas simples.
- Vous avez un moyen de voir quelle version de l'image est actuellement en cours d'exécution.
- Vous avez un plan de retour en arrière : soit un tag d'image précédent, soit un manifest Deployment précédent.
Quelle est la suite
Exécuter le conteneur n'est que la moitié du travail. Une fois qu'il est en cours d'exécution, vous devez savoir quelle version sert réellement le trafic, si elle est saine et quoi faire lorsque la nouvelle version a un problème. C'est là que le suivi de version d'image et le retour en arrière entrent en jeu. Ce sont les sujets de la prochaine partie de cette discussion.
Pour l'instant, l'important est de choisir la cible de déploiement qui correspond à votre réalité opérationnelle, et de vous assurer que l'image que vous exécutez est l'image que vous avez testée. Tout le reste en découle.