
Ajustement des limites de ressources (CPU, mémoire, I/O)
Apprenez à définir des limites précises de CPU, mémoire et I/O pour vos conteneurs Docker afin d'assurer la stabilité, la performance et l'optimisation des coûts de votre infrastructure.
Pourquoi contrôler les ressources des conteneurs ?
Par défaut, un conteneur Docker peut consommer autant de ressources CPU, mémoire et I/O que l'hôte lui permet. Si cela peut être acceptable pour un seul conteneur sur une machine dédiée, cela devient rapidement problématique dans des environnements multi-conteneurs. Un conteneur gourmand ou défaillant pourrait monopoliser les ressources, affectant la performance des autres conteneurs, voire déstabilisant l'ensemble du système hôte.
Définir des limites de ressources est donc essentiel pour plusieurs raisons : garantir une qualité de service (QoS) prévisible pour chaque application, assurer un partage équitable des ressources entre les différents services, prévenir les pannes en cascade dues à l'épuisement des ressources (Out Of Memory - OOM, CPU à 100%), et optimiser l'utilisation de l'infrastructure et potentiellement les coûts associés.
Docker fournit des mécanismes robustes, basés sur les cgroups (Control Groups) de Linux, pour spécifier des limites et des réservations précises pour les principales ressources : la mémoire, le CPU et, dans une moindre mesure, les entrées/sorties disque (Block I/O).
Contrôler la consommation de mémoire
La gestion de la mémoire est souvent la plus critique, car l'épuisement de la mémoire peut entraîner l'arrêt brutal de processus (par l'OOM Killer du noyau). Docker propose plusieurs options pour la contrôler :
L'option `--memory` (ou `-m`) définit une limite stricte (hard limit) sur la quantité de mémoire vive que le conteneur peut utiliser. Si le conteneur tente de dépasser cette limite, il risque d'être tué par l'OOM Killer. Les unités peuvent être `b` (bytes), `k` (kilobytes), `m` (megabytes), ou `g` (gigabytes).
# Limite le conteneur à 512 mégaoctets de RAM
docker run -d --name mon_app --memory="512m" mon_imageL'option `--memory-swap` définit la limite totale de mémoire + swap que le conteneur peut utiliser. C'est une option délicate. Si `--memory-swap` est égal à `--memory`, cela désactive l'utilisation du swap par le conteneur. Si `--memory-swap` est supérieur à `--memory`, le conteneur peut utiliser du swap jusqu'à la limite `memory-swap - memory`. La valeur `-1` (par défaut si `--memory` est défini) signifie que le conteneur peut utiliser autant de swap que l'hôte le permet. Il est souvent recommandé de définir `--memory-swap` à la même valeur que `--memory` pour éviter les dégradations de performance imprévisibles dues au swapping.
L'option `--memory-reservation` définit une limite souple (soft limit). Docker garantit que le conteneur aura au moins cette quantité de mémoire disponible. Le conteneur peut utiliser plus de mémoire, jusqu'à la limite stricte définie par `--memory` (ou la mémoire de l'hôte si aucune limite stricte n'est définie), mais sans garantie au-delà de la réservation. Cette option est utile pour s'assurer qu'un conteneur dispose d'un minimum vital même sous forte contention mémoire.
Lorsque la mémoire est limitée, il est crucial que l'application à l'intérieur du conteneur soit consciente de cette limite (par exemple, configurer correctement la taille maximale du tas JVM via les options Java appropriées) pour éviter d'atteindre brutalement la limite et d'être tuée.
Gérer l'utilisation du CPU
Docker offre plusieurs moyens de contrôler l'accès du conteneur aux ressources CPU :
L'option `--cpu-shares` (ou `-c`) définit une pondération relative pour l'accès au CPU. Par défaut, chaque conteneur a une pondération de 1024. Si deux conteneurs tournent sur un hôte avec des CPU disponibles, celui avec `--cpu-shares=2048` obtiendra environ deux fois plus de temps CPU que celui avec la valeur par défaut 1024, mais uniquement en cas de contention CPU. Si le CPU n'est pas saturé, un conteneur peut utiliser autant de CPU qu'il le souhaite, quelle que soit sa pondération.
L'option `--cpus` permet de définir une limite absolue sur le nombre de coeurs CPU qu'un conteneur peut utiliser. Cette option est souvent plus intuitive que `--cpu-shares`. Par exemple, `--cpus="1.5"` signifie que le conteneur peut utiliser au maximum l'équivalent de 1.5 coeurs CPU sur la durée. S'il y a 4 coeurs disponibles, le conteneur ne pourra jamais utiliser plus de 150% du temps CPU total d'un seul coeur.
# Limite le conteneur à l'utilisation de 0.5 coeur CPU au maximum
docker run -d --name service_leger --cpus="0.5" mon_imageLes options `--cpu-period` et `--cpu-quota` offrent un contrôle plus fin basé sur le planificateur CFS (Completely Fair Scheduler) du noyau Linux. `--cpu-period` définit la période de temps (en microsecondes) sur laquelle l'utilisation CPU est mesurée (typiquement 100000 µs). `--cpu-quota` définit la quantité totale de temps CPU (en microsecondes) que le conteneur peut utiliser pendant cette période. L'option `--cpus` est une abstraction plus simple qui configure implicitement ces deux valeurs. Par exemple, `--cpus="1.5"` est équivalent à `--cpu-period=100000 --cpu-quota=150000`.
L'option `--cpuset-cpus` permet d'épingler l'exécution du conteneur à des coeurs CPU spécifiques. Par exemple, `--cpuset-cpus="0,3"` restreint le conteneur aux coeurs 0 et 3. `--cpuset-cpus="0-1"` le restreint aux coeurs 0 et 1. Cela peut être utile pour dédier des coeurs à des applications critiques ou pour optimiser la localité du cache CPU, mais doit être utilisé avec précaution.
Limiter les entrées/sorties disque (Block I/O)
Le contrôle des entrées/sorties disque (Block I/O) est moins courant et dépend davantage de la configuration de l'hôte et du pilote de stockage utilisé par Docker. Néanmoins, Docker expose des options basées sur les cgroups pour influencer ou limiter les I/O disque :
L'option `--blkio-weight` fonctionne de manière similaire à `--cpu-shares` mais pour les I/O disque. Elle définit une pondération relative (entre 10 et 1000, par défaut 500) pour l'accès aux I/O, qui ne s'applique qu'en cas de contention sur le périphérique de stockage.
L'option `--blkio-weight-device` permet d'affiner cette pondération pour un périphérique spécifique.
# Donne une priorité d'I/O plus élevée à ce conteneur sur /dev/sda
docker run -it --blkio-weight-device "/dev/sda:700" ubuntuLes options les plus directes pour limiter les I/O sont `--device-read-bps`, `--device-write-bps` (limites de débit en octets par seconde) et `--device-read-iops`, `--device-write-iops` (limites en opérations d'I/O par seconde). Ces options nécessitent de spécifier le périphérique bloc concerné.
# Limite la lecture sur /dev/sda à 1 Mo/s
docker run -it --device-read-bps /dev/sda:1mb ubuntuIl est crucial de noter que l'efficacité et la disponibilité de ces options dépendent fortement du pilote de stockage Docker configuré (par exemple, elles sont mieux supportées avec `overlay2` sur des systèmes de fichiers récents ou avec des pilotes comme `direct-lvm` ou `zfs`) et de la version du noyau Linux. Leur utilisation est généralement réservée à des cas d'usage spécifiques où la contention I/O est un problème avéré.
Déterminer et ajuster les bonnes limites
Trouver les limites de ressources optimales pour un conteneur est un processus itératif. Il n'y a pas de règle universelle, car cela dépend fortement de la nature de l'application, de sa charge de travail typique et de ses pics d'activité.
La première étape consiste à monitorer la consommation de ressources du conteneur sans limites (ou avec des limites très larges) dans des conditions réalistes. La commande `docker stats [nom_conteneur]` est l'outil de base pour observer en temps réel l'utilisation CPU, mémoire, réseau et I/O disque.
Sur la base de ces observations, définissez des limites initiales. Il est souvent judicieux de commencer par une limite légèrement supérieure à la consommation de pointe observée (par exemple, +20-30%) pour laisser une marge de manoeuvre.
Ensuite, testez l'application sous charge avec ces limites en place. Observez si les performances sont acceptables et si l'application reste stable. Si l'application est tuée (OOM), si elle ralentit considérablement, ou si elle montre des signes d'instabilité, les limites sont probablement trop strictes.
Ajustez les limites de manière itérative : augmentez-les si l'application souffre, ou essayez de les réduire prudemment si la consommation reste bien en deçà des limites définies, afin d'optimiser l'utilisation des ressources globales de l'hôte.
N'oubliez pas de prendre en compte les besoins variables de l'application. Une tâche batch peut nécessiter beaucoup de CPU temporairement, tandis qu'un serveur web peut avoir besoin de plus de mémoire sous forte charge utilisateur. Les limites doivent idéalement accommoder les pics de demande légitimes.
Points clés de l'ajustement des ressources
L'ajustement des limites de ressources est une pratique fondamentale pour garantir la stabilité et la performance des environnements conteneurisés et optimiser l'utilisation de l'infrastructure.
Utilisez `--memory` pour définir une limite stricte de RAM et envisagez `--memory-reservation` pour garantir un minimum vital. Soyez prudent avec `--memory-swap`.
Pour le CPU, préférez l'option `--cpus` pour définir une limite absolue sur le nombre de coeurs utilisables. Utilisez `--cpu-shares` pour une pondération relative en cas de contention.
Les limites d'I/O disque (`--blkio-weight`, `--device-*-bps`, `--device-*-iops`) sont plus spécifiques et dépendent de la configuration de l'hôte ; utilisez-les lorsque la contention I/O est un problème identifié.
Le processus de définition des limites est itératif : monitorez (`docker stats`), définissez des limites initiales, testez sous charge, et ajustez en fonction des performances et de la stabilité observées. Une configuration précise des ressources est la clé d'un déploiement Docker robuste et efficace.