...

Linux Scheduler CFS : fonctionnement et alternatives dans l'hébergement de serveurs

Le planificateur Linux CFS contrôle la manière dont les cœurs de serveur allouent leur temps aux processus et influence ainsi directement la latence, le débit et l'équité dans l'hébergement de serveurs. Dans ce guide, j'explique le fonctionnement, les leviers de réglage et les alternatives judicieuses comme ULE, BFS et EEVDF pour Hébergement avec Serveurs web.

Points centraux

  • Équité et vruntime déterminer quelle tâche obtient le CPU.
  • Cgroups régissent les quotas et cpu.shares pour l'isolation des clients.
  • Réglage du noyau sur sched_latency_ns et Granularité.
  • Alternatives tels que BFS, ULE, EEVDF pour des Charges de travail.
  • Cabinet médical: Core-Affinity, planificateur d'E/S et Tests combiner.

Comment CFS travaille au quotidien dans le domaine de l'hébergement

Avec le Completely Fair Scheduler, c'est un runtime virtuel qui décide de la prochaine tâche à exécuter, ce qui permet une équitable et prévisibles Attribution est créée. Chaque tâche reçoit du temps CPU proportionnellement à la valeur nice, de sorte qu'une valeur nice faible reçoit plus de parts. Dans les environnements d'hébergement, de nombreuses petites requêtes web, des tâches cron et des sauvegardes séparent le CPU entre eux, sans qu'un processus n'occupe tout. Les charges de travail interactives telles que les requêtes NGINX bénéficient de tranches de temps fréquentes et courtes, tandis que les tâches par lots reçoivent des blocs plus longs. Ainsi, les temps de réponse restent fiables pour les utilisateurs, même si de nombreux sites traitent des requêtes en parallèle.

J'utilise les cgroups pour limiter les clients et les services, car cpu.shares et cpu.max garantissent une répartition claire des ressources. Sommes des parts et dur Limites. Une valeur par défaut de 1024 shares pour “normal” et 512 pour “moins important” répartit les cœurs de manière compréhensible. Avec cpu.max, je définis par exemple 50 ms dans une période de 100 ms, ce qui correspond effectivement à 50% de part de CPU. Pour les charges de travail d'hébergement avec une charge variable, cette configuration offre des réserves planifiables. Les détails du principe sont expliqués de manière compacte sur répartition équitable des CPU.

La mécanique du CFS expliquée de manière compréhensible

Au cœur de CFS, toutes les tâches prêtes à être exécutées sont gérées dans une arborescence rouge et noire, triées par vruntime et avec une efficacité Sélection le plus petit temps d'exécution virtuel. Cette tâche s'exécute ensuite et augmente son vruntime proportionnellement au temps CPU consommé et pondéré par la valeur nice. Il en résulte un équilibre fluide sans files d'attente dures, qui fournit des résultats propres, en particulier pour les charges de travail mixtes. Sur les systèmes multicœurs, le planificateur déplace les tâches entre les files d'attente, mais veille à la localité du cache via l'affinité du cœur. CFS combine ainsi la répartition de la charge avec le moins de migrations coûteuses possible.

Pour le réglage fin, des paramètres comme sched_latency_ns et sched_min_granularity_ns posent les jalons pour Latence et Débit. Des valeurs de latence plus faibles favorisent les tâches courtes et interactives, des valeurs plus élevées renforcent les tâches par lots. Dans des tests avec des outils comme stress-ng et fio, je vérifie l'effet sur les temps de réponse et l'utilisation du CPU. Plus le nombre de tâches augmente, plus la charge administrative de l'arbre augmente, ce qui peut se traduire par des latences de pointe. Des quotas et des limites correctement définis permettent toutefois de maîtriser ces effets dans les environnements d'hébergement.

Points forts de CFS en matière d'hébergement de serveurs

La plus grande force réside dans la Équité, Les résultats de l'enquête ont été Ressources est distribué. Pour les environnements partagés, cela signifie qu'aucun client ne supplante durablement les autres, car les quotas et les partages définissent clairement les priorités. Les services interactifs bénéficient de temps de réaction rapides, tandis que les sauvegardes peuvent se dérouler sans précipitation. La priorisation via les valeurs nice complète ce tableau et me laisse de la place pour la concertation en fonction du rôle d'un service. Grâce à l'équilibrage de la charge sur tous les cœurs, j'utilise bien la puissance de calcul disponible sans donner trop de place aux moments de jeff des différents threads.

En pratique, cette force se révèle lors des pics du serveur web et de l'arrivée de nombreuses requêtes courtes, car CFS attribue de fréquents créneaux à ces types de tâches. Les Cgroups propres aident à fixer des limites supérieures strictes par client ou conteneur. Les mesures sur les moyennes et les centiles indiquent des temps de réponse fiables, ce qui s'avère payant dans les activités quotidiennes. Cette approche s'avère particulièrement efficace pour les piles d'applications comportant de nombreux composants. C'est précisément là que le mélange d'équité planifiable et de flexibilité suffisante marque des points.

Limites et écueils typiques

En présence d'un nombre extrêmement élevé de tâches simultanées, l'overhead des opérations en arborescence augmente, ce qui, dans le cas de Pointes le Latence peut entraîner. Dans les configurations d'hébergement avec de nombreuses requêtes très courtes, les changements de contexte sont parfois fréquents. Un tel comportement “thrash” réduit l'efficacité si les valeurs de granularité sont mal choisies. Des tranches de temps moins nombreuses mais plus longues peuvent aider, à condition que l'interactivité soit maintenue. CFS est sensible aux quotas erronés, c'est pourquoi je vérifie systématiquement les limites avec des tests de charge.

Même les charges de travail à affinité souffrent lorsque les tâches sautent trop souvent entre les cœurs. Un concept d'affinité propre maintient les caches au chaud et réduit les coûts de migration. En outre, j'aime lier les tâches de traitement par lots bruyantes à leurs propres cœurs, afin que les requêtes web s'exécutent tranquillement sur leurs cœurs. Pour les services critiques en termes de latence, il vaut la peine de définir des valeurs nice basses et une latence finement ajustée. Au final, ce qui compte, c'est que les mesures confirment les paramètres choisis.

Comparaison des alternatives : ULE, BFS et EEVDF

Pour des charges de travail spécifiques, j'étudie des alternatives pour Latence ou Mise à l'échelle de manière différente. ULE utilise des files d'attente plus simples et marque des points avec peu de frais administratifs, BFS donne la priorité à la réactivité et brille avec peu de tâches, et EEVDF combine une répartition équitable avec des délais. EEVDF promet justement des temps d'attente plus courts pour les charges interactives, car l'ordonnanceur tient davantage compte de la “première échéance autorisée”. Pour les très grands champs de serveurs, ce qui compte en fin de compte, c'est quel mélange d'efficacité et de planifiabilité gagne vraiment dans sa propre pile. Un regard structuré sur les forces, les faiblesses et les champs d'application aide à faire son choix.

planificateur Complexité Points forts de l'hébergement Faiblesses Convient pour
CFS Haute Une distribution équitable, Cgroups Pointes de latence Hébergement partagé, charges mixtes
ULE Faible Queues simples, faible Dernier Moins d'isolation VMs, modèles de type HPC
OFS Moyens Interactivité, Tempo Faible mise à l'échelle Ordinateurs de bureau, petits serveurs
EEVDF Moyens Faible latence, dates limites Encore peu de pratique Des piles d'hébergement modernes

Réglage du noyau : étapes pratiques pour CFS

Pour CFS, je commute souvent sched_autogroup_enabled=0 afin qu'aucun groupe implicite ne déforme l'image et que les Répartition de la charge clair reste. Avec sched_latency_ns, j'aime commencer à 20ms, ce qui favorise les services interactifs, et ajuster sched_min_granularity_ns pour dompter les changements de contexte. Les valeurs dépendent du profil : de nombreuses requêtes web courtes nécessitent d'autres réglages fins que les fenêtres de sauvegarde. Je teste les changements en série et mesure les centiles au lieu de considérer uniquement les moyennes. Cela garantit que non seulement les moyennes sont jolies, mais aussi que les longues files d'attente se réduisent.

Les personnes qui souhaitent modifier les paramètres sysctl de manière plus approfondie trouveront une bonne introduction ici : Réglage sysctl. En outre, je règle la répartition des IRQ, le gouverneur du CPU et les profils d'énergie afin que le CPU ne bascule pas constamment dans des états d'économie. Pour les piles à latence élevée, j'utilise des gouverneurs de performance, tandis que les pures boîtes de traitement par lots vivent avec un contrôle équilibré. Je sépare clairement les phases de test et de production afin de ne pas livrer de surprises. Après chaque étape, je vérifie les logs et les métriques avant de continuer à tourner.

Utiliser judicieusement les cgroups et les quotas

Avec cpu.shares, j'assigne des valeurs relatives Poids tandis que cpu.max est un Frontières met en place. Un client avec 512 parts obtient deux fois moins de temps de calcul qu'un client avec 1024 parts, si les deux génèrent une charge en même temps. Avec cpu.max, je limite proprement les pics, par exemple 50 ms en 100 ms. Pour les tâches dédiées, cpuset.cpus est utile pour qu'un service utilise des cœurs fixes et que le cache reste chaud. Au total, cela donne une séparation résiliente entre les clients et les services.

Je documente chaque modification et la compare aux niveaux de service que je veux atteindre. Sans valeurs de mesure, les partages conduisent rapidement à des interprétations erronées, c'est pourquoi j'accompagne toujours les adaptations de tests de charge. Pour les conteneurs, je propose des quotas réalistes qui supportent les pics, mais qui ne ralentissent pas l'hôte. Il reste important de prévoir un budget d'erreurs planifiable afin de détecter les pics de latence perceptibles. Si l'on fait cela de manière conséquente, on évite les surprises aux heures de pointe.

Pratique : serveur web et bases de données sous CFS

Les serveurs web pilotés par des événements réduisent les changements de contexte et s'harmonisent avec CFS, ce qui permet d'obtenir des résultats constants. Temps de réponse et meilleure Mise à l'échelle est généré. Dans les tests, je vois que NGINX tient des taux de requêtes plus élevés avec moins de gigue pour le même matériel. Les bases de données réagissent positivement à l'affinité de cœur lorsque les tâches d'arrière-plan sont tenues à l'écart des cœurs chauds. Des règles simples aident : Web sur les noyaux A-B, batch sur C-D et DB sur E-F. Ainsi, la pile garde le pipeline propre et les caches chauds.

De nombreux petits workers PHP-FPM provoquent trop de commutations en cas de granularité agressive. J'augmente alors la tranche de temps minimale et vérifie si les temps de réponse restent stables. En même temps, j'étrangle les logs Chatty pour que les E/S ne deviennent pas un frein. CFS fournit ici la base, mais le sommet de la performance résulte d'un réglage fin de l'ensemble de la pile. Ainsi, tous les rouages s'engrènent sans couper le souffle de l'hôte.

E/S mémoire et ordonnancement CPU : l'interaction

Le planificateur CPU et le planificateur E/S s'influencent mutuellement, c'est pourquoi une configuration cohérente peut avoir des effets sensibles sur les performances. Avantages à l'adresse suivante : Latence apporte. Pour NVMe, j'utilise généralement Noop ou mq-deadline, tandis que sur les disques durs, mq-deadline sert mieux les longues files d'attente. Si le CPU donne du temps ponctuellement, mais que le chemin d'E/S se bloque, l'effet global bascule. C'est pourquoi j'examine les planificateurs d'E/S en parallèle avec les paramètres CFS. Je présente ici un aperçu de Noop, mq-deadline et BFQ : Comparaison des planificateurs d'E/S.

Pour les hôtes de bases de données, j'adapte les profondeurs de file d'attente et le read-ahead afin que les slots planifiés par CFS ne se perdent pas en I/O bloquantes. Les boîtes de serveurs web avec de nombreux petits fichiers bénéficient d'une faible latence dans la pile d'E/S. Dans les scénarios de virtualisation, je mise sur des planificateurs cohérents sur l'hôte et l'invité afin d'éviter les patterns imprédictibles. Ainsi, le planificateur de l'unité centrale joue avec le sous-système de stockage. Au final, ce qui compte, c'est la cohérence de la chaîne, de la demande à la réponse.

Équilibrage SMP, affinité de cœur et NUMA

Je dirige les threads vers des noyaux fixes pour que Caches chaud et coûts de migration petit resteront en place. Pour les hôtes NUMA, j'épingle la mémoire et le CPU ensemble, car les accès mémoire distants augmentent la latence. CFS équilibre la charge entre les files d'attente, mais des règles d'affinité conscientes permettent souvent d'obtenir davantage. Les services avec un accès fréquent au cache bénéficient de groupes de base stables. Les tâches par lots peuvent se déplacer à condition qu'elles ne perturbent pas les noyaux chauds.

Dans la pratique, je définis les options cpuset.cpus et numactl, puis je teste les temps de réponse et les taux d'échec du processeur. Moins il y a de migrations inutiles, meilleur est le temps de réponse. J'évalue également la répartition des interruptions afin d'éviter que les pics d'IRQ ne bloquent un noyau. J'obtiens ainsi une cadence calme des threads importants. Ce calme se répercute sur les performances globales de la pile.

Group Scheduling : nice, pondération et hiérarchies

Une pierre d'achoppement fréquente dans l'hébergement est la Interaction entre sympa-priorité et Poids du Cgroup. CFS répartit d'abord équitablement entre les groupes, puis au sein du groupe entre les tâches. Cela signifie qu'un processus avec nice -5 peut quand même recevoir moins de CPU qu'un autre avec nice 0 si son groupe (client/conteneur) porte un poids plus faible. Pour obtenir des résultats cohérents, je commence donc par définir les Poids des groupes et n'utilise nice que pour la correction fine au sein d'un service.

En pratique, je travaille avec quelques niveaux clairs (par exemple 512/1024/2048 shares pour “low/normal/high”) et je documente quels services fonctionnent dans quel groupe. Ainsi, la Équité compréhensible dans la hiérarchie. Ceux qui travaillent beaucoup avec des processus à courte durée de vie (par exemple des jobs CGI/CLI) profitent en outre de basé sur cgroup Contrôle, car sinon les tâches volatiles passent involontairement le corset du groupe. Je vérifie régulièrement à l'aide de métriques de temps d'exécution si la répartition interne correspond encore au profil de charge.

Conteneurs et orchestration : requêtes, limites et throttling

Dans les environnements de conteneurs, une “requête” mappe typiquement sur poids relatif (shares/weight), une “limite” sur le Quota (cpu.max). L'interaction détermine ThrottlingSi le quota est trop serré, l'unité centrale du conteneur est freinée pendant la période - ce qui se voit aux pics de latence p95/p99. Je maintiens donc les quotas de manière à ce que les bursts normaux tiennent dans la période et que les services soient rarement sévèrement limités. Lorsque cela est possible, j'utilise une rafale-réserve (par exemple cpu.max.burst) pour amortir les pics courts sans distorsion.

Il est important de ne pas fixer des requêtes trop basses : En cas de concurrence, des poids trop faibles font que les services interactifs sont relégués derrière le bruit de fond. Je calibre les requêtes à l'aide de la charge de base mesurée et assure des limites de telle sorte que Budgets d'erreurs être maintenus en période de pointe. Pour les nœuds multi-locataires, je prévois en outre des noyaux tampons pour éviter que les pics de charge de certains conteneurs ne se répercutent sur les voisins.

Méthodes de mesure et dépannage dans le contexte de l'ordonnanceur

Je n'évalue jamais le tuning CFS à l'aveugle, mais je le mesure de manière ciblée. Pour avoir une vue d'ensemble, j'utilise

  • Longueur de la runqueue par CPU (charge vs. noyaux actifs),
  • Changement de contexte par seconde et le nombre de threads,
  • Sceau CPU et SoftIRQ-parts,
  • Percentile de temps de réponse (p50/p95/p99),
  • Distribution de vruntime ou des latences d'ordonnancement.

Si des pics de latence surviennent, je cherche d'abord Throttling (quota épuisé), puis vers Migrations (froid de cache) et enfin après Blocages d'E/S (profondeur de la file d'attente, saturation du stockage). Je regarde les modèles de réveil : des réveils fréquents et brefs de nombreux workers indiquent une granularité trop fine ou des I/O de chatty. Une proportion élevée de ksoftirqd sur un noyau indique des files d'attente IRQ à chaud - dans ce cas, je distribue des IRQ et j'active RPS/XPS pour que la charge du réseau soit plus largement répartie.

Classes en temps réel, préemption et contrôle des ticks

Outre CFS, il existe les classes temps réel SCHED_FIFO/RR. Ils surchargent CFS : un thread RT mal configuré peut littéralement couper l'herbe sous le pied du système. C'est pourquoi je n'attribue des RT-Prio que de manière très ciblée (par ex. pour l'audio/la télémétrie) et je définis des watchdogs clairs. Pour l'hébergement, il suffit généralement de CFS avec des poids propres.

À l'adresse suivante : PréemptionLe choix du modèle de préemption (par ex. “voluntary” vs “full/dynamic preempt”) déplace le rapport latence/débit. Je préfère plus de préemption pour les piles web, moins pour les hôtes purement batch. Optimisation des ticks (nohz-) peuvent réduire la gigue, mais doivent être utilisés avec précaution. Sur des noyaux isolés, je combine parfois nohz_full et Affinity, afin que les hot-threads fonctionnent le moins possible - il est important que la charge du système et de l'IRQ ne se déplace alors pas par inadvertance sur ces cœurs.

Virtualisation : KVM, vCPU-Pinning et Steal-Time

Dans un environnement d'hyperviseur, le planificateur hôte détermine quand vCPU peuvent fonctionner. Créer des surréservations Steal-Time dans les invités, qui agit comme une “latence invisible”. Pour les tenants critiques en termes de latence, j'épingle des vCPU sur des cœurs physiques et je maintiens l'overcommit à un niveau modéré. En outre, je sépare les threads de l'émulateur (threads IO, vhost) des cœurs chauds des invités afin qu'ils ne se gênent pas mutuellement.

J'évite le double étranglement : si l'invité utilise déjà cpu.max, je ne mets pas de quotas durs supplémentaires sur la même charge de travail sur l'hôte. Le contrôle de la fréquence reste la tâche de l'hôte ; les invités en profitent indirectement si le host-governor s'adapte proprement à la charge de travail réelle. Pour des latences régulières, je considère que la stabilité au-delà des simples runs à fréquence maximale est plus importante que les GHz de pointe sur le papier.

AutoNUMA, localisation de la mémoire et THP

Le NUMA peut être un gain ou un piège de performance. AutoNUMA aide souvent, mais peut générer des surcharges supplémentaires en cas de forte migration des threads. Dans les piles d'hébergement avec des limites de service claires, j'épingle l'unité centrale et le processeur. Mémoire (cpuset.cpus et cpuset.mems) en commun. Ainsi, les données à chaud restent locales et CFS doit compenser moins de migrations.

Grandes pages (THP) abaissent TLB-Pressure, mais ne conviennent pas à tous les profils. Pour les bases de données, “madvise” peut être plus judicieux qu'un “always” généralisé. Les page-faults bloquants affectent durement la latence interactive ; je planifie donc les tampons (cache de page, tampon partagé) de manière à ce que les slots CFS soient utilisés de manière productive et n'attendent pas des événements I/O ou MMU. Cela peut être mesuré par les taux de page par défaut et les courbes d'erreur des caches.

Chemin d'accès au réseau : contrôle IRQ, RPS/XPS et Busy-Polling

De nombreuses charges de travail web sont dominées par des NIC. Je distribue IRQ-de la carte réseau sur plusieurs cœurs et les maintenir en place. affin vers les fils de discussion des travailleurs, afin que les réveils restent locaux. RPS/XPS aide à résoudre les hotspots mous lorsque des queues RX/TX individuelles portent une charge trop importante. Si ksoftirqd s'échauffe visiblement, cela indique que les softIRQs débordent - j'égalise alors les flux et augmente les paramètres budgétaires si nécessaire, sans perdre l'équité.

Le busy polling optionnel peut être utile dans des configurations à faible latence très particulières, mais il coûte du temps de CPU. Je l'utilise rarement et seulement si je peux prouver par des mesures que p99 baisse de manière significative sans stresser l'hôte dans son ensemble. En général, une affinité d'IRQ propre, des Cgroups et une granularité CFS fournissent un meilleur rapport coût/bénéfice.

Perspectives d'avenir : Du CFS à l'EEVDF et aux approches Userspace

L'EEVDF ajoute des dates limites à la distribution équitable, ce qui est sensible plus court et plus planifiable Réponses promet de faire. C'est justement sous des objectifs de latence interactifs que cela peut faire pencher la balance. Je surveille de près les versions du noyau et teste EEVDF séparément avant de changer. Parallèlement, l'ordonnancement de l'espace utilisateur via le modèle eBPF prend de l'ampleur et peut permettre un contrôle supplémentaire en fonction de la charge de travail. Pour les infrastructures d'hébergement, CFS reste pertinent, mais EEVDF s'établira rapidement.

Il reste important de suivre un chemin de migration clair : tests, déploiement sur des hôtes sélectionnés, puis extension. Ce n'est qu'ainsi que les centiles et les taux d'erreur restent contrôlables. Je garde les benchmarks proches de la réalité, y compris les phases de burst et les backends lents. Ce n'est qu'ensuite que j'interviens dans des environnements réels. C'est ainsi que l'on peut progresser sans mauvaises surprises.

En bref

Le planificateur Linux CFS fournit une distribution équitable, des intégrations solides et de bonnes Contrôle via Cgroups. Avec des paramètres sysctl adaptés, une affinité propre et des quotas réalistes, je maintiens les latences à un niveau faible et le débit à un niveau élevé. Pour les modèles spéciaux, ULE, BFS ou EEVDF offrent des leviers supplémentaires. Je mesure, compare et déploie les changements par étapes afin de limiter les risques. Ainsi, l'hébergement reste prévisible - et la performance là où elle doit être.

Derniers articles