Le conteneur ne choisit pas son quai

Un conteneur maritime a des dimensions normalisées, des coins de préhension au même endroit et un numéro. C'est tout ce que le portique a besoin de savoir. Ce qu'il y a dedans regarde le client, pas le terminal.

L'image de conteneur tient la même promesse côté logiciel. Elle embarque l'application et ses dépendances, elle démarre de la même façon sur ton poste, sur l'agent de construction et sur le quai que tu loues.

La promesse s'arrête là. Une fois la boîte scellée, il faut quelqu'un pour la poser, la surveiller, la remplacer quand elle tombe et en ajouter dix quand le trafic monte. C'est ce quelqu'un qu'on choisit vraiment.

Azure propose trois services qui exécutent des conteneurs. On les présente souvent comme trois degrés de maturité, du plus simple au plus sérieux. C'est une lecture fausse : ils se départagent sur ce que ton équipe accepte d'exploiter au quotidien, pas sur son niveau.

Avant eux, trois questions se posent dans l'ordre. Où vit l'image, et qui a le droit de la tirer ? Qui la démarre ? Qui décide de la redémarrer, de la répliquer ou de la déplacer sur un autre nœud ? Registre, exécution, orchestration.

flowchart LR SRC["Code et fichier de construction"] --> BUILD["Chaîne de livraison
fabrication de l'image"] BUILD --> ACR["Azure Container Registry
stockage, analyse, verrouillage"] ACR --> ACI["Container Instances
exécution sans orchestrateur"] ACR --> ACA["Container Apps
orchestrateur masqué"] ACR --> AKS["AKS
orchestrateur exposé"] ACI --> RUN["Charge en production"] ACA --> RUN AKS --> RUN classDef amont fill:#ede9e1,stroke:#8a4d16,color:#1f2428; classDef reg fill:#b5651d,stroke:#8a4d16,color:#f5f2ec; classDef exec fill:#d89253,stroke:#8a4d16,color:#2c3338; classDef prod fill:#1f2428,stroke:#8a4d16,color:#f5f2ec; class SRC,BUILD amont; class ACR reg; class ACI,ACA,AKS exec; class RUN prod;

App Service et Azure Functions acceptent aussi une image de conteneur, ce qui brouille encore le tableau. Le panorama complet des options de calcul est dans Choisir son service de calcul ; ici, on reste sur les trois services dont le conteneur est le format natif.

Le conteneur rend l'application portable entre les trois services. Il ne rend portable ni ta configuration réseau, ni ta mise à l'échelle, ni ce que tu as branché autour.

Le parc à conteneurs : Azure Container Registry

Sur un terminal, les boîtes ne partent pas directement du camion au navire. Elles passent par un parc, où elles attendent scellées, numérotées, avec une trace de qui les a déposées.

Azure Container Registry joue ce rôle. C'est un registre privé, adressé par un nom de domaine dédié, dans lequel tes images sont poussées puis tirées par les services d'exécution.

Trois niveaux de service existent, et le choix n'est pas seulement une question de volume.

Niveau Ce qu'il apporte Quand il s'impose
De base Stockage et débit d'entrée de gamme, mêmes API Bac à sable, démonstration, projet isolé
Standard Stockage et débit supérieurs La majorité des équipes en production simple
Premium Géoréplication, liaison privée, clés gérées par le client, jetons à portée de dépôt, stratégies de rétention Plusieurs régions, réseau fermé, exigences de conformité

La géoréplication mérite qu'on s'y arrête, parce qu'elle décide du temps de démarrage. Un cluster en Europe qui tire ses images depuis un registre hébergé aux États-Unis paie la latence à chaque nouveau pod, et paie aussi la sortie de données. Une réplique par région d'exécution supprime les deux, avec un seul nom de registre à retenir.

L'authentification est le point où les projets dérapent le plus vite. Le compte administrateur du registre, avec son mot de passe unique et partagé, est désactivé par défaut et devrait le rester. La bonne réponse est une identité managée à laquelle on attribue le rôle de tirage d'images, ce que le rattachement d'un cluster à un registre fait pour toi.

# Rattacher un cluster AKS à un registre : Azure attribue le rôle AcrPull
# à l'identité managée du cluster, sans aucun secret à stocker.
az aks update \
  --resource-group rg-terminal \
  --name aks-quai-nord \
  --attach-acr acrterminal

# Verrouiller une image livrée : plus de réécriture ni de suppression sur ce tag.
az acr repository update \
  --name acrterminal \
  --image api-manifestes:1.4.2 \
  --write-enabled false

Le verrouillage règle un problème concret : un tag mobile réécrit après coup rend un incident impossible à reconstituer. Quand tu remontes une version pour comprendre une panne, tu veux les octets exacts qui tournaient ce jour-là.

Reste l'analyse de vulnérabilités. Elle n'est pas incluse dans le registre : c'est le plan conteneurs de Defender for Cloud qui inspecte les images poussées et celles qui ont été tirées récemment. Sans ce plan activé, ton registre stocke sans rien te dire du contenu.

Dernier réflexe utile, la rétention. Un registre non nettoyé accumule les manifestes sans tag produits par chaque construction, et la facture de stockage suit tranquillement.

Le registre n'est pas un détail d'outillage. C'est la seule ressource que les trois services d'exécution ont en commun, et c'est là que se joue la provenance de ce qui tourne.

Trois manières d'exécuter

Container Instances

Une boîte posée à quai le temps d'une opération, puis reprise. Pas de grue permanente, pas de planning.

Container Instances démarre un groupe de conteneurs, facturé à la seconde de processeur et de mémoire consommée, sans aucune infrastructure à provisionner. Le groupe partage un cycle de vie, une adresse et éventuellement des volumes, dont des partages de fichiers Azure montés directement.

Le service accepte des images Linux et Windows, peut être injecté dans un sous-réseau de ton réseau virtuel, et applique une politique de redémarrage que tu choisis : toujours, en cas d'échec seulement, ou jamais.

Ce qu'il n'a pas est aussi clair : pas d'orchestrateur. Aucune mise à jour progressive, aucune découverte de service, aucune mise à l'échelle automatique. Tu démarres une instance, ou tu en démarres dix, et c'est ton code ou ton pipeline qui décide.

Le profil qui le rend évident : une tâche ponctuelle, un traitement par lot déclenché par un événement, un agent de construction éphémère, un débordement de courte durée. Le profil qui le rend absurde : un service HTTP permanent dont on attend qu'il se répare tout seul.

Il sert aussi de rallonge à un cluster, via les nœuds virtuels d'AKS, qui délèguent des pods à Container Instances au lieu d'attendre qu'un nœud démarre.

Container Apps

Le terminal automatisé. On dépose la boîte à l'entrée, la machine s'occupe du reste, et personne ne voit les portiques.

Container Apps repose sur Kubernetes, KEDA, Dapr et Envoy, mais n'expose aucune de ces API. Tu déclares une application, ses ressources, son entrée réseau et ses règles d'échelle. Le reste est masqué, volontairement.

Trois capacités le distinguent vraiment. La mise à l'échelle jusqu'à zéro : une application sans trafic ne consomme plus rien, au prix d'un démarrage à froid à la requête suivante. Les révisions : chaque modification crée une version, et tu répartis le trafic entre deux révisions, ce qui donne un déploiement progressif sans outillage supplémentaire. La montée en charge pilotée par les événements : la règle d'échelle regarde une profondeur de file ou un décalage de flux, pas seulement le processeur.

properties:
  template:
    scale:
      minReplicas: 0
      maxReplicas: 20
      rules:
        - name: file-manifestes
          custom:
            type: azure-servicebus
            metadata:
              queueName: manifestes
              messageCount: "20"
            identity: system

Cette règle dit une chose simple : une réplique par tranche de vingt messages en attente, zéro quand la file est vide. Le même raisonnement sur un cluster demande un opérateur à installer et à maintenir.

Container Apps gère aussi des tâches, planifiées, déclenchées par un événement ou lancées à la main, ce qui couvre une partie des cas où l'on sortait Container Instances. Un environnement Container Apps s'associe à un sous-réseau, avec une entrée managée, publique ou interne, et un certificat géré pour toi.

Sa limite est franche. Si ta charge dépend d'un opérateur Kubernetes, de définitions de ressources personnalisées, d'un DaemonSet ou d'un accès au nœud, le service ne te le donnera pas.

AKS

Le terminal dont tu tiens toi-même la salle de contrôle. Tu vois tout, tu peux tout, et tu réponds de tout.

Azure Kubernetes Service fournit un plan de contrôle managé : le serveur d'API, le planificateur et le magasin d'état sont exploités par la plateforme. Les nœuds, eux, t'appartiennent, apparaissent dans ton abonnement et sont facturés comme des machines virtuelles, avec le raisonnement de dimensionnement décrit dans Machines virtuelles : tailles, disques, images, mise à l'échelle.

Trois niveaux existent : gratuit, standard avec un engagement de disponibilité sur le plan de contrôle et une capacité de cluster supérieure, premium qui ajoute un support étendu des versions. Le niveau gratuit sert à apprendre, pas à tenir une production.

Ce que tu gagnes est réel : l'API Kubernetes complète, un écosystème d'opérateurs, des manifestes portables vers un autre fournisseur, et un contrôle fin du placement. Ce que tu paies, c'est une plateforme à exploiter en plus de tes applications.

Critère Container Instances Container Apps AKS
Unité déployée Un groupe de conteneurs Une application et ses révisions N'importe quel objet Kubernetes
Orchestration Aucune Masquée par le service Exposée, et à ta charge
Mise à l'échelle Manuelle, instance par instance Jusqu'à zéro, pilotée par les événements Pods et nœuds, à configurer
Réseau Injection dans un sous-réseau Environnement lié à un sous-réseau, entrée managée Modèle réseau à choisir, entrée à installer
Ce que tu exploites Rien de permanent Révisions et règles d'échelle Nœuds, versions, extensions, stratégies
Le profil qui le désigne Tâche ponctuelle, pointe brève Service HTTP, consommateur de file Plateforme partagée, dépendance à l'écosystème
La question n'est pas « lequel est le plus puissant ». Elle est : combien de personnes chez toi sauront diagnostiquer un pod bloqué en attente à 3 heures du matin, et est-ce que ce nombre justifie un cluster.

Concevoir un cluster AKS

Si le choix tombe sur AKS, six décisions structurent le cluster. Prises à la légère, elles se paient pendant des années, parce que la plupart ne se corrigent qu'en redéployant.

Les pools de nœuds

Un cluster a au moins un pool système, qui héberge les composants de la plateforme, la résolution de noms interne en tête. Il tourne sous Linux et se dimensionne pour ces composants, pas pour tes applications.

Les pools utilisateur accueillent tes charges. En avoir plusieurs permet de séparer par profil de machine : usage général, forte mémoire, accélération matérielle, ou instances interruptibles pour ce qui tolère d'être repris.

Le réflexe qui évite un incident : marquer le pool système d'une souillure dédiée, pour qu'aucune application n'y atterrisse par défaut, et ne jamais le laisser à un seul nœud en production.

Le modèle réseau et le plan d'adressage

C'est la décision la plus coûteuse à défaire. Avec Azure CNI en mode sous-réseau de nœud, chaque pod reçoit une adresse du réseau virtuel, donc le sous-réseau doit contenir les nœuds et tous leurs pods.

Posons le calcul. Un pool capable de monter à 20 nœuds, avec la valeur par défaut de 30 pods par nœud en CNI, réclame 20 × (30 + 1) = 620 adresses. Un /24 en offre 251 utilisables une fois retirées les cinq réservées par Azure : il est déjà trop petit. Un /22 en offre 1019, et il faut encore garder de la marge pour les nœuds supplémentaires créés le temps d'une mise à niveau.

Le mode superposé change la donne : les pods reçoivent leurs adresses d'une plage privée séparée, non routée dans le réseau virtuel, et le sous-réseau n'héberge plus que les nœuds. Tu économises des adresses, tu perds l'adressabilité directe des pods depuis le reste du réseau.

Modèle Adresses consommées dans le réseau virtuel Ce que ça permet Ce que ça coûte
Azure CNI, sous-réseau de nœud Nœuds et pods Pod joignable directement depuis le réseau virtuel Plan d'adressage lourd, croissance bornée d'avance
Azure CNI en mode superposé Nœuds seulement Grand nombre de pods sur un petit sous-réseau Pas d'accès entrant direct au pod depuis le réseau virtuel

Le plugin réseau historique, qui joignait les pods par tables de routage, est en voie de retrait, et la documentation Microsoft oriente désormais vers le mode superposé. Le raisonnement d'ensemble sur l'adressage est dans Réseau virtuel : adressage, appairage et connectivité hybride.

Entrer et sortir

Un cluster privé place le serveur d'API derrière un point de terminaison privé. Excellente idée pour la surface d'attaque, contrainte immédiate pour l'exploitation : ton agent de déploiement et tes postes d'administration doivent être dans le réseau, ou y accéder par un rebond, et la zone DNS privée doit être correctement liée.

Côté entrée applicative, tu choisis entre un contrôleur d'entrée managé par le module complémentaire de routage, une passerelle applicative pilotée par tes manifestes, ou un maillage de services. Côté sortie, le comportement par défaut fait passer tout le trafic par un répartiteur de charge partagé, avec un nombre de ports de traduction limité : une application qui ouvre beaucoup de connexions sortantes courtes finit par les épuiser. Une passerelle de traduction d'adresses dédiée règle ce point, un routage défini par l'utilisateur force le passage par un pare-feu.

Sans autorisation de sortie vers le registre et vers les services de la plateforme, le cluster ne démarre même pas ses propres composants. Les dispositifs de filtrage sont traités dans Répartition de charge et sécurité réseau.

Identité et secrets

Le cluster porte une identité managée pour parler aux ressources Azure. Tes pods, eux, ont besoin d'une identité à eux.

La fédération d'identité de charge de travail répond exactement à ça : le compte de service Kubernetes échange son jeton contre un jeton Entra, et aucun secret n'est stocké nulle part.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: sa-manifestes
  namespace: quai-nord
  annotations:
    azure.workload.identity/client-id: "<identifiant-application>"
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-manifestes
  namespace: quai-nord
spec:
  template:
    metadata:
      labels:
        azure.workload.identity/use: "true"
    spec:
      serviceAccountName: sa-manifestes

Pour les secrets eux-mêmes, le pilote CSI de magasin de secrets monte le contenu d'un coffre Key Vault comme des fichiers, avec rotation. À défaut, souviens-toi qu'un secret Kubernetes est encodé, pas chiffré : AKS permet de chiffrer le magasin d'état avec une clé de ton coffre, encore faut-il l'activer.

Mettre à l'échelle

Deux étages, et on les confond tout le temps. Le premier décide du nombre de pods, avec l'autoscaler horizontal sur des métriques ou KEDA sur des événements. Le second décide du nombre de nœuds, avec l'autoscaler de cluster ou l'approvisionnement automatique de nœuds.

flowchart TD EV["Trafic ou messages en file"] --> HPA["HPA ou KEDA
décide du nombre de pods"] HPA --> POD{"Assez de place
sur les nœuds ?"} POD -->|oui| SCHED["Pods planifiés"] POD -->|non| PEND["Pods en attente"] PEND --> CA["Autoscaler de cluster
ajoute un nœud"] CA --> SCHED SCHED --> SERV["Requêtes servies"] NOREQ["Ressources demandées non déclarées
le planificateur ne voit aucune contrainte"] -.-> POD classDef ev fill:#ede9e1,stroke:#8a4d16,color:#1f2428; classDef dec fill:#b5651d,stroke:#8a4d16,color:#f5f2ec; classDef node fill:#d89253,stroke:#8a4d16,color:#2c3338; classDef warn fill:#a0413e,stroke:#8a4d16,color:#f5f2ec; class EV,SERV ev; class HPA,POD dec; class SCHED,PEND,CA node; class NOREQ warn;

Le fil qui casse est toujours le même : l'autoscaler de cluster réagit aux pods qui ne trouvent pas de place, et il ne peut le savoir que si tes conteneurs déclarent ce qu'ils demandent en processeur et en mémoire. Sans ces déclarations, le cluster se remplit jusqu'à l'étouffement sans jamais ajouter un nœud.

Monter de version

Kubernetes avance vite, et la fenêtre de prise en charge des versions par AKS est courte. Monter de version n'est donc pas un projet annuel, c'est une opération de routine.

Le plan de contrôle et les pools de nœuds se mettent à niveau séparément, dans cet ordre. Les canaux de mise à niveau automatique et les images de nœud régulièrement republiées prennent en charge les correctifs, à condition de leur donner une fenêtre de maintenance qui ne tombe pas en pleine heure de pointe.

Deux réglages décident si la bascule se passe bien. La marge de dépassement, qui contrôle combien de nœuds supplémentaires apparaissent pendant l'opération, donc la durée et la consommation d'adresses. Et les budgets d'interruption de pods, qui protègent tes applications pendant le drainage : mal écrits, ils bloquent purement et simplement la mise à niveau, parce que le cluster n'a jamais le droit de retirer le dernier pod.

Décision Ce qu'elle engage Le symptôme quand elle est ratée
Pools de nœuds Placement, isolation, facture Applications qui étouffent les composants système
Modèle réseau Plan d'adressage, croissance maximale Pool bloqué faute d'adresses libres
Cluster privé Chemins d'administration et de déploiement Pipeline qui ne joint plus le serveur d'API
Sortie de trafic Ports de traduction, filtrage Connexions sortantes qui échouent sous charge
Identité des pods Secrets stockés ou non Chaînes de connexion en clair dans les manifestes
Mise à l'échelle Réactivité et coût Nœuds saturés sans ajout automatique
Mise à niveau Continuité de service Version hors support, ou drainage bloqué
Un cluster AKS ne se conçoit pas au moment de sa création, il se conçoit au moment où l'on trace le sous-réseau. Tout le reste se rattrape, l'adressage se redéploie.

Ce qui reste à ta charge

Le terminal loué vient avec ses portiques et son personnel de quai. Il ne vient pas avec l'arrimage de ta cargaison.

Microsoft exploite le plan de contrôle, publie des images de nœud corrigées et maintient les modules complémentaires managés. Le reste te revient, et la liste est plus longue qu'annoncé.

Tes nœuds reçoivent des correctifs, mais le redémarrage qui les applique et la montée d'image restent ta décision. Tes conteneurs déclarent ou non leurs ressources, et ce choix conditionne le placement, la mise à l'échelle et la stabilité sous charge. Les stratégies réseau entre pods n'existent que si tu les écris. Sans analyse branchée sur le registre, la provenance et le contenu de tes images restent une inconnue. Les composants tiers que tu installes dans le cluster sont les tiens, y compris quand ils cassent une mise à niveau.

Et la facture d'un cluster est surtout une facture de machines virtuelles, avec la même question que partout ailleurs : combien de nœuds tournent la nuit pour rien.

Un service managé déplace la frontière de la responsabilité partagée, il ne la supprime pas. Ce que la plateforme ne fait pas et que personne ne fait non plus est exactement l'endroit où les incidents se produisent.

Scénario B : Tesla, février 2018

Le plan de contrôle d'un orchestrateur, c'est la salle de commande du terminal. Qui y entre commande les portiques, lit les manifestes et voit passer les clés des entrepôts.

En février 2018, l'équipe RedLock Cloud Security Intelligence a publié la découverte suivante : une console d'administration Kubernetes appartenant à Tesla était accessible sur Internet sans aucune authentification.

À l'intérieur, dans un pod, se trouvaient des identifiants d'accès à l'environnement cloud de l'entreprise, donnant notamment accès à un espace de stockage objet contenant des données de télémétrie. La console ouverte ne donnait pas seulement la main sur des conteneurs, elle donnait la main sur ce que ces conteneurs avaient le droit d'atteindre.

Les intrus ne s'en sont pas servis pour voler des données. Ils y ont fait tourner du minage de cryptomonnaie, et la publication détaille des précautions inhabituelles : au lieu d'un pool de minage public connu, ils avaient installé leur propre logiciel de pool et pointé le script vers un point de terminaison non listé ; l'adresse réelle du serveur était masquée derrière un service de diffusion de contenu ; le logiciel écoutait sur un port non standard ; et la consommation processeur était maintenue basse. Autant de façons de rester invisible d'une supervision qui ne surveille que les signatures évidentes et les pics d'utilisation.

Tesla, informée via son programme de prime aux vulnérabilités, a indiqué que l'exposition concernait des véhicules d'essai à usage interne et a corrigé la configuration rapidement.

Ce qu'il faut en retenir tient en deux points. La console d'administration d'un orchestrateur est une surface d'attaque de premier ordre, au même titre qu'une base de données exposée. Et les identifiants qu'un pod porte définissent le rayon d'action de quiconque atteint ce pod : c'est précisément l'argument des identités fédérées à portée réduite plutôt qu'une clé longue durée déposée dans un conteneur.

Ouvrir le plan de contrôle, c'est ouvrir la salle de commande. Le contenu des conteneurs n'a plus beaucoup d'importance quand on tient les grues et le trousseau.

Points clés

  • Le choix se fait sur l'exploitation, pas sur la puissance. Container Instances exécute sans orchestrer, Container Apps orchestre sans le montrer, AKS te confie l'orchestrateur entier avec la facture de nœuds et le travail qui va avec.
  • Le registre est le socle commun. Géoréplication pour la latence de démarrage, identité managée plutôt que compte administrateur, analyse activée explicitement, verrouillage des images livrées.
  • Le plan d'adressage décide de la croissance du cluster. Vingt nœuds à trente pods en Azure CNI avec sous-réseau de nœud réclament 620 adresses : un /24 est déjà trop petit, et l'adressage se corrige en redéployant.
  • La mise à l'échelle a deux étages qui ne se parlent que par les ressources déclarées. Sans requests sur tes conteneurs, l'autoscaler de cluster n'ajoutera jamais un nœud.
  • Le managé ne couvre pas les stratégies réseau, la provenance des images, ni les composants que tu installes. C'est là que se logent les incidents, et le cas Tesla en est l'illustration la plus nette.

Dans la série

Palier 2 : Construire les terminaux. Domaine : Calcul.


Pour aller plus loin