Aller au contenu
Conteneurs & Orchestration medium

Karpenter : l'autoscaler Kubernetes nouvelle génération

18 min de lecture

Vos pods restent bloqués en Pending parce que le cluster manque de capacité. Karpenter détecte ces pods en attente et déclenche le provisionnement en quelques secondes — mais le temps jusqu’à un nœud Ready dépend du provider cloud, du bootstrap (OS, runtime, CNI) et des pulls d’images. En pratique, comptez 1 à 4 minutes selon votre environnement.

Les nœuds sont dimensionnés exactement pour vos pods et disparaissent automatiquement quand ils ne servent plus. Ce guide vous donne le modèle mental pour comprendre comment Karpenter fonctionne, son architecture interne, et pourquoi il surpasse les autoscalers traditionnels.

  • Comprendre les ressources clés : NodePool, NodeClass, NodeClaim, NodeOverlay
  • Maîtriser la Disruption : consolidation, drift, expiration, budgets
  • Comprendre le Scheduling : comment Karpenter simule et sélectionne les instances
  • Visualiser l’architecture interne de Karpenter et le rôle de chaque contrôleur
  • Distinguer ce que fait Karpenter vs ce que fait kube-scheduler
  • Comparer Karpenter avec Cluster Autoscaler pour choisir le bon outil

Dans un cluster Kubernetes traditionnel, l’autoscaling des nœuds repose sur le Cluster Autoscaler. Ce dernier ajuste le nombre de nœuds au sein de groupes de nœuds (Node Groups ou Auto Scaling Groups). Cette approche présente plusieurs limitations :

LimitationImpact
RigiditéVous devez créer et maintenir de nombreux groupes de nœuds pour différents types d’instances
LenteurL’ajout d’un nœud peut prendre plusieurs minutes (négociation avec ASG, attente de scaling)
InefficacitéLa sélection du type d’instance est limitée au groupe, pas optimisée pour le pod
ComplexitéGérer des centaines de groupes devient vite ingérable

Karpenter élimine ces contraintes en provisionnant directement les instances sans passer par des groupes intermédiaires. Il analyse les pods en attente, détermine le type d’instance optimal parmi tous ceux disponibles, et crée immédiatement la capacité nécessaire.

Karpenter brille quand :

  • Vos workloads ont des besoins variés (architectures, types d’instances, spot/on-demand)
  • Vous voulez une consolidation automatique des nœuds sous-utilisés
  • La réactivité compte (pods schedulés en secondes, pas en minutes)
  • Vous déployez sur un cloud avec un provider Karpenter disponible

Karpenter est conçu pour être extensible. Plusieurs providers existent :

ProviderMaintenu parMaturitéNodeClassRepo
AWSAWS✅ ProductionEC2NodeClassaws/karpenter-provider-aws
Azure/AKSMicrosoft✅ ProductionAKSNodeClassAzure/karpenter-provider-azure
GCP/GKECloudPilot AI🔶 PreviewGCPNodeClasscloudpilot-ai/karpenter-provider-gcp
Alibaba CloudCloudPilot AI🔶 PreviewECSNodeClasscloudpilot-ai/karpenter-provider-alibabacloud

Karpenter utilise trois Custom Resource Definitions (CRDs) qui travaillent ensemble. Comprendre leur rôle est essentiel pour configurer et dépanner Karpenter.

Karpenter CRDs

La NodeClass répond à la question : “Comment créer un nœud chez mon provider ?”

Chaque provider cloud a sa propre NodeClass (EC2NodeClass pour AWS, AKSNodeClass pour Azure. Elle contient :

  • Image machine : AMI sur AWS, ca serait OMI sur Outscale
  • Réseau : subnets, security groups
  • IAM/Credentials : rôle pour le nœud
  • User-data : script d’initialisation
apiVersion: karpenter.k8s.aws/v1
kind: EC2NodeClass
metadata:
name: default
spec:
role: "KarpenterNodeRole-my-cluster"
amiSelectorTerms:
- alias: al2023@latest
subnetSelectorTerms:
- tags:
karpenter.sh/discovery: "my-cluster"
securityGroupSelectorTerms:
- tags:
karpenter.sh/discovery: "my-cluster"

Le NodePool répond à la question : “Quels types de nœuds Karpenter peut-il créer ?”

Il définit :

  • Requirements : architectures autorisées, types d’instances, zones de disponibilité
  • Taints : restrictions sur les pods pouvant être schedulés
  • Limites : capacité maximale du pool (CPU, mémoire)
  • Disruption : comment et quand Karpenter peut supprimer des nœuds
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: default
spec:
template:
spec:
nodeClassRef:
group: karpenter.k8s.aws # ou karpenter.outscale.com, etc.
kind: EC2NodeClass
name: default
requirements:
- key: kubernetes.io/arch
operator: In
values: ["amd64"]
- key: karpenter.sh/capacity-type
operator: In
values: ["spot", "on-demand"]
limits:
cpu: 100
memory: 200Gi
disruption:
consolidationPolicy: WhenEmptyOrUnderutilized
consolidateAfter: 5m

Le NodeClaim est une ressource interne créée automatiquement par Karpenter. Il représente une “demande de nœud” avec des spécifications précises. Vous ne créez jamais de NodeClaim manuellement.

Un NodeClaim contient :

  • Le type d’instance choisi
  • La zone de disponibilité
  • Le providerID (identifiant cloud de l’instance)
  • Les conditions de progression (Launched, Registered, Initialized, Ready)
# Exemple de NodeClaim créé automatiquement
apiVersion: karpenter.sh/v1
kind: NodeClaim
metadata:
name: default-abc123
labels:
karpenter.sh/nodepool: default
spec:
requirements:
- key: topology.kubernetes.io/zone
operator: In
values: ["eu-west-2a"]
- key: node.kubernetes.io/instance-type
operator: In
values: ["m5.large"]
status:
providerID: aws:///eu-west-2a/i-0123456789abcdef0
conditions:
- type: Ready
status: "True"

Karpenter se compose de plusieurs contrôleurs qui travaillent ensemble pour gérer le cycle de vie complet des nœuds.

Architecture Karpenter : 4 contrôleurs et CloudProvider Interface

Le Provisioner Controller est le cœur du provisionnement. Il surveille en permanence les pods marqués comme Unschedulable par kube-scheduler et décide quels nœuds créer.

Flux de travail :

  1. Observation : écoute les événements pods via l’API Kubernetes
  2. Batching : regroupe les pods pending pendant ~10 secondes max (configurable via --batch-max-duration)
  3. Simulation : simule le scheduling pour déterminer les NodeClaims nécessaires
  4. Création : crée les ressources NodeClaim correspondantes

Le batching est crucial : au lieu de créer un nœud par pod, Karpenter attend jusqu’à 10 secondes (1s idle par défaut) pour regrouper plusieurs pods sur le même nœud si possible.

Le Disruption Controller optimise le cluster en identifiant les nœuds à supprimer ou remplacer. Il opère selon un ordre de priorité :

  1. Drift : nœuds dont la configuration ne correspond plus au NodePool
  2. Consolidation : nœuds vides ou sous-utilisés
  3. Expiration : nœuds ayant dépassé leur durée de vie (expireAfter)

Types de consolidation :

TypeDescription
EmptySupprime les nœuds sans pods applicatifs (en parallèle)
Multi-NodeMigre les pods de plusieurs nœuds vers moins de nœuds
Single-NodeRemplace un nœud par un type d’instance moins cher

Le Garbage Collection Controller vérifie périodiquement (~30 secondes) que chaque NodeClaim correspond à une VM réelle dans le cloud en appelant CloudProvider.List().

Fonctionnement :

  1. Appelle List() qui retourne toutes les VMs gérées par Karpenter
  2. Compare cette liste avec les NodeClaims existants
  3. Si un NodeClaim n’a pas de VM correspondante → suppression immédiate

Quand un nœud doit être supprimé, le Termination Controller gère la séquence de shutdown gracieux :

  1. Taint : ajoute karpenter.sh/disrupted:NoSchedule pour empêcher de nouveaux pods
  2. Drain : évince les pods via l’API Eviction (respecte les PDBs)
  3. Cleanup : attend que les VolumeAttachments soient supprimés
  4. Delete : appelle le cloud provider pour terminer l’instance
  5. Finalize : retire le finalizer pour permettre la suppression de l’objet Node

Point critique : Karpenter provisionne des nœuds, mais c’est kube-scheduler qui schedule les pods dessus.

Flux de provisionnement Karpenter : du pod Pending au node Ready

  1. Un pod est créé → kube-scheduler le marque Unschedulable (pas de nœud disponible)

  2. Karpenter observe ce pod Pending et analyse ses contraintes

  3. Karpenter crée un NodeClaim → l’API cloud provisionne l’instance

  4. Le kubelet s’enregistre → le node rejoint le cluster

  5. kube-scheduler (pas Karpenter) schedule le pod sur le nouveau nœud

Comprendre les phases d’un NodeClaim est essentiel pour le debugging.

PhaseConditionQue se passe-t-il ?
CreatedKarpenter a détecté des pods pending et créé un objet NodeClaim
LaunchedLaunched=TrueLe provider cloud a créé la VM (instance ID connu)
RegisteredRegistered=TrueLe kubelet s’est connecté et Karpenter a lié le Node au NodeClaim
InitializedInitialized=TrueLe nœud est Ready au sens Kubernetes (CNI, runtime, DaemonSets critiques)
ReadyReady=TrueLes taints d’initialisation sont retirées, les pods applicatifs peuvent être schedulés

La Disruption est le mécanisme par lequel Karpenter supprime ou remplace des nœuds. Elle se divise en deux catégories : les méthodes graceful (respectent les budgets) et les méthodes forceful (drainent immédiatement).

MéthodeDéclencheurDescription
DriftNodePool/NodeClass modifiésRemplace les nœuds dont la config ne correspond plus
ConsolidationSous-utilisationRéduit les coûts en fusionnant les workloads

La consolidation s’exécute selon trois stratégies (dans cet ordre) :

  1. Empty Node — Supprime les nœuds vides en parallèle
  2. Multi-Node — Migre les pods de N nœuds vers moins de nœuds
  3. Single-Node — Remplace un nœud par un type moins cher
apiVersion: karpenter.sh/v1
kind: NodePool
spec:
disruption:
consolidationPolicy: WhenEmptyOrUnderutilized # ou WhenEmpty
consolidateAfter: 5m # attendre 5 min avant de considérer le nœud

Karpenter détecte automatiquement si un NodeClaim ne correspond plus à son NodePool ou NodeClass :

  • NodePool : changements dans spec.template.spec.requirements
  • NodeClass : changements dans amiSelectorTerms, subnetSelectorTerms, securityGroupSelectorTerms

Les nœuds driftés sont remplacés progressivement selon les budgets.

MéthodeDéclencheurDescription
ExpirationexpireAfter atteintForce le remplacement après une durée max
InterruptionSpot interruption, maintenance AWSÉvacuation préventive avant interruption cloud
Node Auto RepairNode unhealthy > 30 minRemplace les nœuds défectueux (alpha)
apiVersion: karpenter.sh/v1
kind: NodePool
spec:
template:
spec:
expireAfter: 720h # 30 jours max
terminationGracePeriod: 1h # temps max pour drainer

Limitez le nombre de nœuds perturbés simultanément :

apiVersion: karpenter.sh/v1
kind: NodePool
spec:
disruption:
consolidationPolicy: WhenEmptyOrUnderutilized
budgets:
- nodes: "20%" # max 20% des nœuds en disruption
reasons: ["Drifted", "Empty"]
- nodes: "5" # plafond absolu
- nodes: "0" # bloquer pendant maintenance
schedule: "@daily"
duration: 30m
reasons: ["Underutilized"]
AnnotationEffet
karpenter.sh/do-not-disrupt: "true" sur podBloque la disruption volontaire du nœud
karpenter.sh/do-not-disrupt: "true" sur nodeBloque toute disruption volontaire

Quand Karpenter doit créer un nœud, il simule le scheduling pour choisir le type d’instance optimal.

  1. Filtrage — Élimine les types incompatibles avec les requirements
  2. Simulation — Calcule combien de pods pending tiennent sur chaque type
  3. Scoring — Choisit le type le moins cher qui satisfait les contraintes
  4. Batching — Regroupe les pods pendant ~10s max (configurable)

Les requirements dans le NodePool filtrent les types d’instances disponibles :

LabelDescriptionExemple
kubernetes.io/archArchitecture CPUamd64, arm64
kubernetes.io/osSystème d’exploitationlinux, windows
node.kubernetes.io/instance-typeType exactm5.large, c6i.xlarge
topology.kubernetes.io/zoneZone de disponibilitéeu-west-1a
karpenter.sh/capacity-typeSpot ou On-Demandspot, on-demand

Quand plusieurs NodePools peuvent satisfaire un pod, Karpenter utilise le weight :

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: spot-pool
spec:
weight: 100 # Priorité haute → essayé en premier
template:
spec:
requirements:
- key: karpenter.sh/capacity-type
operator: In
values: ["spot"]
---
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: on-demand-fallback
spec:
weight: 10 # Priorité basse → fallback
template:
spec:
requirements:
- key: karpenter.sh/capacity-type
operator: In
values: ["on-demand"]

Les NodeOverlays permettent de modifier les informations d’instances que Karpenter utilise pour ses décisions :

  • Ajuster les prix pour refléter vos Savings Plans ou contrats
  • Ajouter des extended resources non détectées automatiquement
apiVersion: karpenter.sh/v1alpha1
kind: NodeOverlay
metadata:
name: savings-plan-discount
spec:
requirements:
- key: node.kubernetes.io/instance-type
operator: In
values: ["m5.large", "m5.xlarge", "m5.2xlarge"]
priceAdjustment: "-30%" # Réduction de 30% (Savings Plan)
apiVersion: karpenter.sh/v1alpha1
kind: NodeOverlay
metadata:
name: custom-devices
spec:
requirements:
- key: node.kubernetes.io/instance-type
operator: In
values: ["g4dn.xlarge"]
capacity:
smarter-devices/fuse: "1"
custom-hardware/inference-unit: "4"

Quand plusieurs overlays matchent le même type d’instance :

  1. weight le plus élevé gagne
  2. À weight égal, ordre alphabétique
  3. Les capacity sont fusionnées (pas de conflit)
ChampComportement
price / priceAdjustmentLe overlay avec le plus haut weight gagne
capacityFusionné entre tous les overlays
  1. Karpenter provisionne, kube-scheduler schedule — ne confondez pas les rôles
  2. NodePool = contraintes (types, arch, limits) ; NodeClass = config cloud (AMI, réseau, IAM)
  3. nodeClassRef complet : group, kind, name — les trois sont obligatoires
  4. Toujours définir des limits — sinon Karpenter peut créer des centaines de nœuds
  5. 4 contrôleurs : Provisioner, Disruption, Termination, Garbage Collection
  6. Le GC Controller est critique — une erreur dans List() supprime tous vos nœuds
  7. Disruption graceful (consolidation, drift) respecte les budgets ; forceful (expiration, interruption) non
  8. NodeOverlay (alpha) permet d’ajuster prix et capacités pour la simulation

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.