Skip to content

Gestion des ressources et affectation des pods

Requests et Limits

Dans Kubernetes, chaque container peut définir des requests (ressources demandées) et des limits (ressources maximales). Ces paramètres sont essentiels pour :

  • Garantir que les pods ont les ressources nécessaires
  • Éviter qu'un pod consomme toutes les ressources d'un nœud
  • Permettre au scheduler de placer les pods intelligemment

Concepts clés

ParamètreDescriptionImpact
requests.cpuCPU minimum garantiUtilisé par le scheduler pour placer le pod
requests.memoryMémoire minimum garantieUtilisé par le scheduler pour placer le pod
limits.cpuCPU maximum autoriséLe container sera throttlé s'il dépasse
limits.memoryMémoire maximum autoriséeLe container sera OOMKilled s'il dépasse

Unités

CPU :

  • 1 = 1 vCPU/Core
  • 500m = 0.5 CPU (500 millicores)
  • 100m = 0.1 CPU

Mémoire :

  • 128Mi = 128 Mebibytes
  • 1Gi = 1 Gibibyte
  • 256M = 256 Megabytes (base 10)

Exemple complet

yaml
apiVersion: v1
kind: Pod
metadata:
  name: resource-demo
spec:
  containers:
  - name: app
    image: nginx
    resources:
      requests:
        memory: "128Mi"
        cpu: "100m"
      limits:
        memory: "256Mi"
        cpu: "500m"

Comportement du scheduler

Le scheduler utilise les requests pour décider où placer un pod :

Ressources disponibles sur le nœud = Capacité - Somme des requests des pods existants

Si un pod demande requests.cpu: 500m et qu'aucun nœud n'a 500m de CPU disponible, le pod restera en Pending.

Que se passe-t-il en cas de dépassement ?

-> Dépassement CPU (limits.cpu)

Le container est throttlé (ralenti). Il ne sera pas tué, mais ses performances seront dégradées.

-> Dépassement mémoire (limits.memory)

Le container est OOMKilled (Out Of Memory Killed). Kubernetes le redémarrera selon la restartPolicy.

Mise en pratique

-> Créez un pod avec des ressources définies

yaml
# resource-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: resource-test
spec:
  containers:
  - name: stress
    image: polinux/stress
    command: ["stress"]
    args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"]
    resources:
      requests:
        memory: "100Mi"
        cpu: "100m"
      limits:
        memory: "200Mi"
        cpu: "200m"

-> Appliquez et observez

sh
kubectl apply -f resource-pod.yaml
kubectl describe pod resource-test
# Regardez la section "Requests" et "Limits"

kubectl top pod resource-test
# Affiche la consommation réelle (nécessite metrics-server)

-> Testez un dépassement mémoire

yaml
# oom-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: oom-test
spec:
  containers:
  - name: stress
    image: polinux/stress
    command: ["stress"]
    args: ["--vm", "1", "--vm-bytes", "300M", "--vm-hang", "1"]
    resources:
      limits:
        memory: "100Mi"
sh
kubectl apply -f oom-pod.yaml
kubectl get pods -w
# Le pod sera OOMKilled et redémarrera en boucle

Bonnes pratiques

  1. Toujours définir des requests ET des limits pour éviter les surprises
  2. Requests ≈ consommation moyenne de l'application
  3. Limits = requests × 1.5 à 2 pour absorber les pics
  4. Ne pas mettre limits.cpu trop bas car le throttling dégrade les performances
  5. Monitorer la consommation réelle avant de fixer les valeurs définitives

Quality of Service (QoS)

Kubernetes assigne une classe QoS à chaque pod en fonction de ses requests/limits :

ClasseConditionPriorité en cas de pression
Guaranteedrequests = limits (pour tous les containers)Derniers à être évincés
Burstablerequests < limits ou partiellement définisÉvincés avant Guaranteed
BestEffortAucun request ni limit définiPremiers à être évincés
sh
kubectl describe pod <pod-name> | grep "QoS Class"

nodeSelector

Le nodeSelector est le moyen le plus simple d'affecter un pod à un nœud spécifique basé sur des labels.

Labelliser un nœud

sh
# Ajouter un label
kubectl label nodes worker-1 disktype=ssd
kubectl label nodes worker-2 disktype=hdd
kubectl label nodes worker-3 gpu=nvidia

# Voir les labels
kubectl get nodes --show-labels

Utiliser nodeSelector dans un pod

yaml
apiVersion: v1
kind: Pod
metadata:
  name: ssd-pod
spec:
  nodeSelector:
    disktype: ssd
  containers:
  - name: nginx
    image: nginx

Ce pod ne sera schedulé que sur les nœuds ayant le label disktype=ssd.

Cas d'usage

  • Pods nécessitant un GPU → gpu: nvidia
  • Pods nécessitant des SSD → disktype: ssd
  • Pods dans une zone géographique spécifique → topology.kubernetes.io/zone: eu-west-1a

Node Affinity

L'affinity offre plus de flexibilité que nodeSelector avec des règles "required" ou "preferred".

Types d'affinity

TypeComportement
requiredDuringSchedulingIgnoredDuringExecutionObligatoire : le pod ne sera pas schedulé si la condition n'est pas remplie
preferredDuringSchedulingIgnoredDuringExecutionPréférentiel : le scheduler essaiera de respecter la condition, mais ce n'est pas obligatoire

Exemple : Required Affinity

Le pod doit être sur un nœud avec un SSD :

yaml
apiVersion: v1
kind: Pod
metadata:
  name: required-affinity-pod
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: disktype
            operator: In
            values:
            - ssd
  containers:
  - name: nginx
    image: nginx

Exemple : Preferred Affinity

Le pod préfère être sur un nœud avec un SSD, mais peut aller ailleurs :

yaml
apiVersion: v1
kind: Pod
metadata:
  name: preferred-affinity-pod
spec:
  affinity:
    nodeAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 80
        preference:
          matchExpressions:
          - key: disktype
            operator: In
            values:
            - ssd
      - weight: 20
        preference:
          matchExpressions:
          - key: disktype
            operator: In
            values:
            - hdd
  containers:
  - name: nginx
    image: nginx

Le weight (1-100) indique la priorité de chaque préférence.

Opérateurs disponibles

OpérateurDescription
InLa valeur du label est dans la liste
NotInLa valeur du label n'est pas dans la liste
ExistsLe label existe (peu importe sa valeur)
DoesNotExistLe label n'existe pas
GtGreater than (pour valeurs numériques)
LtLess than (pour valeurs numériques)

Pod Affinity et Anti-Affinity

Au-delà des nœuds, on peut aussi définir des règles basées sur la présence d'autres pods.

Pod Affinity : Colocate des pods ensemble

Exemple : placer les pods frontend sur le même nœud que les pods backend (pour réduire la latence réseau) :

yaml
apiVersion: v1
kind: Pod
metadata:
  name: frontend
  labels:
    app: frontend
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - backend
        topologyKey: kubernetes.io/hostname
  containers:
  - name: nginx
    image: nginx

Pod Anti-Affinity : Séparer des pods

Exemple : s'assurer que les réplicas d'un même deployment sont sur des nœuds différents (haute disponibilité) :

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis
spec:
  replicas: 3
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - redis
            topologyKey: kubernetes.io/hostname
      containers:
      - name: redis
        image: redis

Avec cette configuration, chaque pod Redis sera sur un nœud différent.

topologyKey

Le topologyKey définit le "domaine" de l'affinity :

topologyKeySignification
kubernetes.io/hostnameMême nœud
topology.kubernetes.io/zoneMême zone (cloud)
topology.kubernetes.io/regionMême région (cloud)

Taints et Tolerations

Les taints sont l'inverse du nodeSelector : elles repoussent les pods plutôt que de les attirer.

Ajouter une taint à un nœud

sh
# Syntax: kubectl taint nodes <node> <key>=<value>:<effect>
kubectl taint nodes worker-3 gpu=true:NoSchedule

Effects disponibles

EffectComportement
NoScheduleLes nouveaux pods sans toleration ne seront pas schedulés
PreferNoScheduleLe scheduler évitera ce nœud, mais ce n'est pas garanti
NoExecuteLes pods existants sans toleration seront évincés

Tolerations dans un pod

Pour qu'un pod puisse être schedulé sur un nœud avec une taint :

yaml
apiVersion: v1
kind: Pod
metadata:
  name: gpu-pod
spec:
  tolerations:
  - key: "gpu"
    operator: "Equal"
    value: "true"
    effect: "NoSchedule"
  containers:
  - name: cuda
    image: nvidia/cuda

Cas d'usage

  • Nœuds dédiés : taint pour réserver des nœuds à certains workloads
  • Nœuds GPU : seuls les pods GPU peuvent y aller
  • Maintenance : taint NoExecute pour vider un nœud avant maintenance

Supprimer une taint

sh
kubectl taint nodes worker-3 gpu=true:NoSchedule-
# Le "-" à la fin supprime la taint

Récapitulatif

MécanismeUsageDirection
nodeSelectorAffecter à un nœud par label (simple)Pod → Node
Node AffinityAffecter à un nœud par label (avancé)Pod → Node
Pod AffinityColocate avec d'autres podsPod → Pod
Pod Anti-AffinitySéparer des autres podsPod ↔ Pod
TaintsRepousser les pods d'un nœudNode → Pod
TolerationsPermettre à un pod d'aller sur un nœud taintéPod → Node