Aller au contenu
Conteneurs & Orchestration medium

ResourceQuota et LimitRange : contrôler les ressources par namespace

18 min de lecture

logo kubernetes

ResourceQuota limite la consommation totale d'un namespace (CPU, mémoire, nombre de pods...), tandis que LimitRange définit des valeurs par défaut et des bornes min/max pour chaque conteneur. Ensemble, ils empêchent qu'une équipe ou une application ne monopolise les ressources du cluster. Ce guide vous montre comment les configurer et les combiner efficacement.

  • Comprendre la différence entre ResourceQuota et LimitRange
  • Créer des quotas pour limiter CPU, mémoire et objets Kubernetes
  • Configurer des valeurs par défaut avec LimitRange
  • Combiner les deux mécanismes pour un namespace multi-tenant
  • Déboguer les erreurs liées aux quotas dépassés

Prérequis : connaître les namespaces Kubernetes et comprendre requests et limits.

Sans quota, un développeur peut déployer un pod qui réclame 100 Go de mémoire. Le scheduler le place sur le nœud qui a le plus de place, le conteneur grossit jusqu'à épuiser la mémoire disponible, et le noyau déclenche alors un OOM kill qui frappe aussi les pods voisins, y compris ceux d'autres équipes. Dans un cluster multi-tenant (plusieurs équipes, plusieurs projets sur la même infrastructure), rien n'isole par défaut la consommation d'un namespace de celle des autres.

ScénarioSans quotaAvec ResourceQuota
Un pod demande 100 Go RAMSchedulé, crash du nœudRejeté immédiatement
Une équipe crée 500 podsCluster saturéLimité à 50 pods max
Création massive de ConfigMapsSurcharge etcdPlafonné à 100 ConfigMaps

Les quotas sont préventifs : ils bloquent le déploiement avant que le problème ne survienne.

Un ResourceQuota définit des limites agrégées pour tout un namespace. Il répond à la question : "Combien de ressources ce namespace peut-il consommer au total ?"

Ces quatre catégories ne servent pas le même objectif. Les quotas Compute protègent les nœuds d'une saturation CPU ou mémoire ; les quotas Objets protègent surtout etcd, qui est la base de données du cluster et son point de contention le plus fragile. Un namespace qui crée dix mille ConfigMaps ne fait crasher aucun nœud, mais il ralentit l'API server pour tout le monde. La catégorie Étendu est le filet universel : sa syntaxe count/<ressource>.<groupe> couvre y compris les ressources personnalisées installées par vos opérateurs.

CatégorieExemples de ressourcesDescription
Computerequests.cpu, limits.memoryCPU et mémoire totaux
Objetspods, services, configmapsNombre d'objets Kubernetes
Stockagerequests.storage, persistentvolumeclaimsEspace disque et PVC
Étenducount/deployments.apps, count/jobs.batchTout type de ressource

Un ResourceQuota se pose sur un namespace existant et prend effet immédiatement, y compris sur les pods déjà en place : ceux-ci continuent de tourner, mais toute nouvelle création est désormais soumise au plafond. Commencez donc par un namespace de test. La troisième étape est la plus instructive : describe affiche la consommation constatée en face de chaque plafond, et c'est le seul moyen de vérifier que le quota compte bien ce que vous croyiez.

  1. Créer le fichier de quota

    Ce quota limite le namespace à 4 CPU en requests, 8 Go de mémoire en limits, et 30 pods maximum.

    quota-team-backend.yaml
    apiVersion: v1
    kind: ResourceQuota
    metadata:
    name: compute-quota
    namespace: team-backend
    spec:
    hard:
    requests.cpu: "4"
    requests.memory: 8Gi
    limits.cpu: "8"
    limits.memory: 16Gi
    pods: "30"
  2. Appliquer le quota

    Fenêtre de terminal
    kubectl apply -f quota-team-backend.yaml
  3. Vérifier l'état du quota

    Fenêtre de terminal
    kubectl describe resourcequota compute-quota -n team-backend

    Sortie attendue :

    Name: compute-quota
    Namespace: team-backend
    Resource Used Hard
    -------- ---- ----
    limits.cpu 0 8
    limits.memory 0 16Gi
    pods 0 30
    requests.cpu 0 4
    requests.memory 0 8Gi

Vous pouvez limiter le nombre de n'importe quel type d'objet Kubernetes :

quota-objects.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
name: object-quota
namespace: team-backend
spec:
hard:
configmaps: "30"
secrets: "30"
services: "10"
services.loadbalancers: "2"
services.nodeports: "5"
persistentvolumeclaims: "10"
count/deployments.apps: "20"
count/jobs.batch: "50"

Vous pouvez segmenter les quotas selon la PriorityClass des pods :

quota-priority.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
name: high-priority-quota
namespace: team-backend
spec:
hard:
requests.cpu: "2"
requests.memory: 4Gi
scopeSelector:
matchExpressions:
- operator: In
scopeName: PriorityClass
values: ["high-priority"]

Ce quota ne s'applique qu'aux pods avec priorityClassName: high-priority.

Un LimitRange définit des contraintes au niveau de chaque conteneur ou pod individuel. Il répond à : "Quelles sont les valeurs min, max et par défaut pour un conteneur ?"

Poser un ResourceQuota sur les ressources de calcul a un effet de bord que beaucoup découvrent en production : le namespace devient plus strict pour tous les pods, y compris ceux qui fonctionnaient très bien avant. Kubernetes ne peut en effet pas décompter d'un quota un pod qui ne déclare pas ce qu'il consomme, alors il refuse de le créer. Le manifeste ci-dessous n'a pas changé, c'est le namespace autour de lui qui est devenu exigeant.

Le LimitRange résout ce problème en définissant des valeurs par défaut automatiques.

L'objet ci-dessous mélange deux rôles qu'il faut distinguer avant de le lire. default et defaultRequest sont permissifs : ils complètent un manifeste incomplet et laissent passer le pod. max et min sont bloquants : ils rejettent le pod si les valeurs déclarées sortent des bornes. La troisième étape le vérifie concrètement, en créant un pod dépourvu de section resources et en relisant ce que l'API server a réellement enregistré.

  1. Créer le fichier LimitRange

    limitrange-team-backend.yaml
    apiVersion: v1
    kind: LimitRange
    metadata:
    name: container-limits
    namespace: team-backend
    spec:
    limits:
    - type: Container
    default:
    cpu: "500m"
    memory: "512Mi"
    defaultRequest:
    cpu: "100m"
    memory: "128Mi"
    max:
    cpu: "2"
    memory: "4Gi"
    min:
    cpu: "50m"
    memory: "64Mi"
  2. Appliquer le LimitRange

    Fenêtre de terminal
    kubectl apply -f limitrange-team-backend.yaml
  3. Tester avec un pod sans limits

    pod-sans-limits.yaml
    apiVersion: v1
    kind: Pod
    metadata:
    name: test-pod
    namespace: team-backend
    spec:
    containers:
    - name: nginx
    image: nginx:1.25
    # Pas de resources déclarées
    Fenêtre de terminal
    kubectl apply -f pod-sans-limits.yaml
    kubectl get pod test-pod -n team-backend -o yaml | grep -A 10 resources

    Résultat : Les valeurs par défaut sont automatiquement injectées.

La colonne Appliqué quand est la clé de lecture de ce tableau : les deux premiers champs n'entrent en jeu que si le manifeste est silencieux, les deux derniers s'appliquent dans tous les cas. Une erreur classique consiste à définir default sans defaultRequest : Kubernetes recopie alors la valeur de limits dans requests, et vous réservez bien plus de ressources que prévu sur chaque nœud.

ChampDescriptionAppliqué quand
defaultValeur limits par défautSi limits non spécifié
defaultRequestValeur requests par défautSi requests non spécifié
maxValeur maximale autoriséeToujours (rejet si dépassé)
minValeur minimale autoriséeToujours (rejet si inférieur)

Vous pouvez aussi définir des limites au niveau du pod entier (somme de tous les conteneurs) :

apiVersion: v1
kind: LimitRange
metadata:
name: pod-limits
namespace: team-backend
spec:
limits:
- type: Pod
max:
cpu: "4"
memory: "8Gi"

Le LimitRange peut aussi contraindre la taille des PVC :

apiVersion: v1
kind: LimitRange
metadata:
name: storage-limits
namespace: team-backend
spec:
limits:
- type: PersistentVolumeClaim
max:
storage: 50Gi
min:
storage: 1Gi

En production, vous utilisez toujours les deux ensemble. Voici l'ordre d'application recommandé :

  1. Créer le namespace avec des labels

    apiVersion: v1
    kind: Namespace
    metadata:
    name: team-backend
    labels:
    team: backend
    environment: production
  2. Appliquer le LimitRange en premier

    Le LimitRange garantit que tous les pods auront des requests/limits, même non déclarés.

    apiVersion: v1
    kind: LimitRange
    metadata:
    name: container-limits
    namespace: team-backend
    spec:
    limits:
    - type: Container
    default:
    cpu: "500m"
    memory: "512Mi"
    defaultRequest:
    cpu: "100m"
    memory: "128Mi"
    max:
    cpu: "2"
    memory: "4Gi"
    min:
    cpu: "50m"
    memory: "64Mi"
  3. Appliquer le ResourceQuota ensuite

    Le ResourceQuota limite la consommation totale du namespace.

    apiVersion: v1
    kind: ResourceQuota
    metadata:
    name: compute-quota
    namespace: team-backend
    spec:
    hard:
    requests.cpu: "8"
    requests.memory: 16Gi
    limits.cpu: "16"
    limits.memory: 32Gi
    pods: "50"
    configmaps: "50"
    secrets: "50"
    services: "20"
    persistentvolumeclaims: "20"

Quand vous créez un pod, voici ce qui se passe :

1. Le pod arrive à l'API server
2. LimitRange injecte les defaults si manquants
3. LimitRange vérifie min/max par conteneur
4. ResourceQuota vérifie si le total du namespace permet ce pod
5. Si tout est OK → pod créé
6. Sinon → erreur avec message explicite

get répond à la question « y a-t-il un quota ici », describe à la question « où en est-on ». Seule la seconde affiche les colonnes Used et Hard côte à côte, qui sont l'information utile au quotidien : c'est en voyant Used s'approcher de Hard qu'on anticipe le blocage, plutôt que de le découvrir sur un déploiement refusé un vendredi soir.

Fenêtre de terminal
# Voir tous les quotas d'un namespace
kubectl get resourcequota -n team-backend
# Détails avec utilisation actuelle
kubectl describe resourcequota compute-quota -n team-backend
# Voir les LimitRange
kubectl describe limitrange -n team-backend

L'API expose l'utilisation des quotas :

Fenêtre de terminal
kubectl get resourcequota compute-quota -n team-backend -o jsonpath='{.status}'

Pour le monitoring, kube-state-metrics expose les métriques :

  • kube_resourcequota : limites et utilisation
  • kube_limitrange : valeurs configurées

C'est le message le plus lisible de la série : il donne les trois nombres nécessaires au diagnostic dans la même ligne. requested est ce que le nouveau pod demande, used ce que le namespace consomme déjà, limited le plafond. Si la somme des deux premiers dépasse le troisième, le pod est refusé.

Error from server (Forbidden): pods "my-pod" is forbidden:
exceeded quota: compute-quota, requested: requests.memory=2Gi,
used: requests.memory=7Gi, limited: requests.memory=8Gi

Cause : Le namespace a déjà utilisé 7 Gi sur 8 Gi autorisés.

Solutions :

  1. Réduire les requests du nouveau pod
  2. Supprimer des pods existants
  3. Augmenter le quota (si justifié)
Fenêtre de terminal
# Voir l'utilisation actuelle
kubectl describe resourcequota -n team-backend
# Identifier les gros consommateurs
kubectl top pods -n team-backend --sort-by=memory

Ne confondez pas ce message avec le précédent : failed quota signale un manifeste incomplet, pas un plafond atteint. Le namespace peut être totalement vide et produire quand même cette erreur. Le message énumère les ressources manquantes et le conteneur concerné, ce qui vous indique directement quoi ajouter.

Error from server (Forbidden): pods "my-pod" is forbidden:
failed quota: compute-quota: must specify limits.cpu for: nginx; limits.memory for: nginx

Cause : Un ResourceQuota existe mais pas de LimitRange, et le pod ne déclare pas ses limits.

Solution : Ajouter un LimitRange avec des valeurs par défaut.

Celle-ci vient du LimitRange, pas du ResourceQuota : elle se déclenche même si le namespace est presque vide, parce qu'elle borne chaque conteneur pris isolément. Le mot per Container dans le message est le repère qui permet de distinguer immédiatement les deux mécanismes.

Error from server (Forbidden): pods "my-pod" is forbidden:
maximum cpu usage per Container is 2, but limit is 4

Cause : Le pod demande plus que le max autorisé par le LimitRange.

Solution : Réduire les limits du conteneur ou ajuster le LimitRange.

Un pod peut être créé (quota OK) mais rester Pending si le cluster n'a pas assez de ressources disponibles. Le quota ne garantit pas la disponibilité, il garantit seulement les limites.

Fenêtre de terminal
kubectl describe pod my-pod -n team-backend
# Regarder la section Events pour "Insufficient cpu" ou "Insufficient memory"

Un quota trop serré transforme chaque déploiement en ticket d'exploitation ; un quota trop large ne protège de rien. La règle qui fonctionne est de partir de la consommation mesurée (kubectl top pods sur quelques jours) et non des chiffres annoncés par l'équipe, puis d'ajouter la marge indiquée ci-dessous. Prévoyez de revoir ces valeurs après le premier trimestre : un quota posé une fois et jamais relu finit toujours par bloquer quelqu'un.

MétriqueRecommandation
requests.cpuSomme des requests de tous les pods attendus
limits.cpu1.5x à 2x les requests (marge pour burst)
podsNombre max réaliste + 20% marge
PVCBasé sur les besoins réels de stockage

Assurez-vous que le LimitRange est cohérent avec le ResourceQuota :

# Si LimitRange.default.cpu = 500m et ResourceQuota.limits.cpu = 8
# Alors max 16 conteneurs avec defaults (16 × 500m = 8 CPU)

Adaptez les quotas selon l'environnement :

EnvironnementQuota CPU requestsQuota mémoirePods max
dev24Gi20
staging48Gi30
production1632Gi100
  • ResourceQuota limite la consommation totale d'un namespace (CPU, mémoire, objets)
  • LimitRange définit des valeurs par défaut et des bornes min/max par conteneur
  • Avec un ResourceQuota portant sur le calcul, les pods doivent déclarer leurs requests/limits → utilisez LimitRange pour les defaults
  • Appliquez LimitRange avant ResourceQuota pour éviter les rejets de pods
  • Les quotas sont préventifs : ils bloquent avant que le problème ne survienne
  • Surveillez l'utilisation avec kubectl describe resourcequota

Ce quiz porte sur les points où les deux objets se confondent le plus souvent : quel mécanisme rejette quoi, et dans quel ordre les appliquer. Répondez sans revenir en arrière, les questions manquées vous indiqueront la section à relire.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

10 questions
8 min.
70% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

Ce site vous est utile ?

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

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn