Docker et Kubernetes : Optimisation pour le Cloud moderne

Introduction
Docker et Kubernetes sont les deux faces d'une même pièce : Docker empaquette les applications dans des conteneurs reproductibles, Kubernetes les orchestre sur le cloud à grande échelle. Mais l'optimisation de ce duo pour tirer le meilleur du cloud -- en performance, en coûts et en sécurité -- demande de l'expérience. Après des dizaines de projets sur 6 providers cloud (AWS, GCP, Azure, Scaleway, OVH, Outscale), voici les optimisations qui ont le plus d'impact concret.
Images Docker optimisées : chaque mégaoctet compte sur le cloud
Tout commence par l'image Docker, et sur le cloud, chaque mégaoctet a un impact direct sur les temps de déploiement et les coûts de stockage. Chez WizOps.fr, le frontend Nuxt utilise un multi-stage build rigoureux : un stage builder avec Node 22 Alpine qui installe les dépendances via pnpm et exécute nuxt build, puis un stage runner qui ne contient que le serveur Node minimal et le répertoire .output/ généré. L'image finale fait 150 Mo au lieu de 1.2 Go en single-stage. Sur Kubernetes Scaleway, ça signifie un pull en 4 secondes au lieu de 30 secondes, et un rolling update complet en 20 secondes.
Chez KNDS (Défense), les contraintes de sécurité imposaient l'utilisation d'images distroless Google : pas de shell, pas de package manager, pas d'utilitaires -- uniquement le runtime applicatif et ses dépendances directes. L'image finale du backend Python faisait 85 Mo. La surface d'attaque était minimale : un attaquant qui compromettait le conteneur ne trouvait aucun outil exploitable. Chez Bloomflow, chaque équipe avait un template Dockerfile validé avec des règles strictes : image de base Alpine officielle, utilisateur non-root, HEALTHCHECK déclaré, et pas de apt-get install dans le stage final. Le respect de ces règles était vérifié par un linter Dockerfile dans la CI.
Scheduling Kubernetes : placer les pods au bon endroit
Sur le cloud, le placement des pods influence directement les coûts et la performance. Chez F2R2 avec EKS Fargate, chaque pod tourne dans sa propre microVM Firecracker, ce qui garantit une isolation totale (pas de noisy neighbor) mais implique un coût par pod. L'optimisation passait par le rightsizing précis des requests : un pod qui demandait 256 Mo au lieu de 1 Go en request coûtait 4 fois moins en Fargate pricing. L'analyse VPA (Vertical Pod Autoscaler) en mode recommendation pendant 2 semaines a permis d'ajuster chaque pod au plus près de sa consommation réelle, avec une marge de 20%.
Chez Bloomflow sur AWS EC2, les stratégies étaient plus sophistiquées. Les node affinities dirigeaient les workloads batch (jobs de traitement nocturne, builds CI, analyses de données) vers des instances Spot EC2 (70% moins chères que les On-Demand), tandis que les workloads critiques (API, frontends, bases de données) restaient sur des instances On-Demand pour la stabilité. Les pod anti-affinities garantissaient que les replicas d'un service critique étaient répartis sur des noeuds différents et dans des AZ différentes, pour survivre à la perte d'un noeud ou d'une AZ complète. Chez Padam Mobility, cette configuration anti-affinity avait été validée par un test de chaos engineering : on terminait un noeud aléatoirement, et le service devait rester disponible.
Autoscaling multi-niveaux : absorber les pics sans gaspiller
L'autoscaling sur le cloud fonctionne à deux niveaux qui doivent être coordonnés : les pods (HPA -- Horizontal Pod Autoscaler) et les noeuds (Cluster Autoscaler ou Karpenter). Chez Bloomflow, le HPA scalait les pods des microservices API de 2 replicas (nuit/week-end) à 20 replicas (pic de charge en journée) selon deux métriques : le CPU moyen par pod (seuil à 70%) et le nombre de requêtes HTTP par seconde via les métriques custom Prometheus (seuil à 500 req/s par pod). Le Cluster Autoscaler ajoutait des noeuds EC2 quand les pods en pending ne trouvaient plus de place sur les noeuds existants.
Chez F2R2, Karpenter (le successeur du Cluster Autoscaler sur AWS, développé par AWS) sélectionnait automatiquement le type d'instance optimal selon les besoins agrégés des pods en attente : si les pods demandaient beaucoup de mémoire, Karpenter provisionnait des instances r6i ; si c'était du CPU, des instances c6i. Le temps de scaling mesuré : 90 secondes pour le scale-up d'un nouveau pod sur un noeud existant, 3 minutes pour un nouveau noeud. Chez TEKYN, ce mécanisme a absorbé les pics du Black Friday (trafic multiplié par 8) sans intervention manuelle et sans incident. Le scale-down après le pic libérait les noeuds inutiles en 10 minutes.
Sécurité en profondeur : Docker + Kubernetes + Cloud
L'optimisation inclut la sécurité, et sur le cloud, la surface d'attaque est plus large qu'on ne le pense. Chez KNDS (Défense), le pipeline CI intégrait un scan Trivy de chaque image Docker avant le push vers le registre. Les vulnérabilités de sévérité Critical et High bloquaient le build : l'image n'était pas poussée et le développeur recevait le rapport CVE détaillé dans la PR. Les images étaient ensuite signées avec Cosign et une clé KMS, et un admission controller Kyverno sur le cluster vérifiait la signature avant d'autoriser le déploiement de tout pod.
Chez Bloomflow (ISO 27001), les pods tournaient avec un filesystem en read-only (readOnlyRootFilesystem: true), sans privilèges root (runAsNonRoot: true), avec des profils seccomp restrictifs qui bloquaient les appels système non nécessaires (ptrace, mount, socket raw). Les NetworkPolicies isolaient chaque namespace : un pod dans le namespace payment ne pouvait communiquer qu'avec le namespace api, pas avec admin ou monitoring. Sur Outscale (SecNumCloud), les mêmes contraintes s'appliquaient avec en plus le chiffrement au repos des volumes via les clés OKMS et le chiffrement en transit (mTLS) entre les services via un service mesh Linkerd.
Observabilité : le pilotage par les données
Pour optimiser le duo Docker/Kubernetes sur le cloud, il faut voir ce qui se passe -- et bien voir. Ma stack d'observabilité standard, déployée sur chaque cluster : Prometheus pour les métriques (nodes, pods, réseau, latences applicatives, métriques custom business), Grafana pour la visualisation (dashboards pré-configurés par namespace et par service), et Loki pour les logs centralisés (recherche et corrélation temporelle).
Chez Metronome sur OVH Cloud, les dashboards Grafana montraient par service : le CPU/mémoire réel vs demandé (le ratio révèle immédiatement le gaspillage -- un pod qui utilise 10% de sa request est un candidat au rightsizing), le nombre de restarts (signe d'instabilité : OOMKill, probe failure, crash applicatif), la latence des requêtes (P50 pour l'expérience typique, P95 pour les cas limites, P99 pour les pires cas), et les erreurs HTTP 5xx par minute. Chez Bloomflow, OpenTelemetry ajoutait le tracing distribué : suivre une requête à travers 30 microservices pour identifier en 30 secondes le service responsable d'un ralentissement. Ces données pilotent toutes les décisions d'optimisation : sans elles, on optimise par intuition, et l'intuition se trompe souvent.
Conclusion
L'optimisation de Docker et Kubernetes pour le cloud repose sur cinq piliers interdépendants : des images légères et sécurisées (de 1.2 Go à 85 Mo), un scheduling intelligent avec affinities et Spot instances (-70% sur le compute batch), un autoscaling multi-niveaux qui absorbe les pics sans gaspiller, une sécurité en profondeur adaptée au contexte réglementaire, et une observabilité complète qui pilote les décisions. Appliquées ensemble sur 6 providers cloud et des dizaines de projets, ces optimisations transforment le cloud d'un centre de coût imprévisible en une plateforme performante, économique et sécurisée.