Serveur CPU Affinity affecte de manière ciblée des processus à des noyaux de CPU fixes, réduisant ainsi les migrations, les changements de contexte et les caches froids dans les piles d'hébergement. Je montre comment cet épinglage crée des latences planifiables, des taux de réussite de cache plus élevés et un débit régulier dans les serveurs web, PHP-FPM, les bases de données, les VM et les conteneurs.
Points centraux
Les aspects clés suivants constituent les garde-fous pour une mise en œuvre efficace d'Affinity dans l'hébergement.
- Proximité de la cache minimise la latence et augmente l'efficacité des charges de travail multithreadées.
- Planification grâce à l'épinglage : moins de valeurs aberrantes à p99 et temps de réponse constant.
- Conscience de la NUMA couple mémoire et CPU, réduit les accès à distance coûteux.
- Cgroups complètent Affinity avec des quotas, des priorités et une répartition équitable.
- Suivi avec perf/Prometheus révèle les migrations et les miss.
Que signifie CPU Affinity dans l'hébergement ?
L'affinité lie Fils de discussion à des noyaux fixes afin que l'ordonnanceur ne les disperse pas sur l'ensemble du socket. Ainsi, les caches L1/L2/L3 restent chauds, ce qui est particulièrement important pour les applications sensibles à la latence. Demandes sur le web compte. Par défaut, le CFS de Linux s'équilibre de manière dynamique, mais génère des migrations superflues dans les phases chaudes. Je limite ces migrations de manière ciblée au lieu de freiner complètement le planificateur. Je vous propose ici une introduction plus approfondie aux alternatives CFS : Options de l'ordonnanceur Linux.
Analyse de la charge de travail et profilage
Avant d'épingler, j'examine Caractéristiques des services. Les serveurs web événementiels génèrent peu de changements de contexte, mais bénéficient fortement de la cohérence du cache. Les bases de données sont sensibles aux migrations du noyau pendant les jointures ou les points de contrôle intensifs. Je mesure la latence p95/p99, je suis les migrations du CPU avec parfait et je regarde les LLC-misses. Ce n'est qu'ensuite que j'écris des règles fixes et que je les teste sous charge de pointe.
Topologie du CPU, SMT et paires de cœurs
Je tiens compte de la topologie physique : complexes de base, tranches L3 et SMT-des frères et sœurs. Pour les services critiques en termes de latence de queue, je n'occupe qu'un seul thread SMT par cœur afin que les threads chauds ne se partagent pas les unités d'exécution. SMT reste actif pour les tâches par lots qui profitent du débit supplémentaire. Sur AMD-EPYC, je fais attention aux limites CCD/CCX : Les travailleurs restent à l'intérieur d'un segment L3 afin que les hits LLC restent élevés et stables. Sur les piles à forte charge NIC, j'apparie les queues RX/TX avec les Noyaux, sur lesquels fonctionnent les workers de l'espace utilisateur. Cet appariement permet d'éviter les snoops cross-core et de maintenir les chemins entre IRQ, SoftIRQ et App courts.
Stratégies d'épinglage pour les serveurs web et PHP-FPM
Pour les frontaux web, j'utilise NGINX souvent sur un ensemble de noyaux étroits, par exemple 0-3, afin de garantir des temps de réponse réguliers. Je divise PHP-FPM : hot-worker sur 4-7, jobs d'arrière-plan sur 8-11. J'allège Node.js avec des worker-threads et je lie les tâches nécessitant beaucoup de CPU à des tâches propres. noyaux. Je maintiens Apache dans le MPM événementiel avec des limites strictes dans des files d'attente d'exécution courtes. De telles dispositions permettent de maintenir les pipelines propres et de réduire sensiblement la gigue.
Paramètres du noyau et de l'ordonnanceur dans le contexte d'Affinity
L'affinité a plus d'effet si le noyau ne contrecarre pas en permanence. Pour les services fortement sensibles au cache, j'augmente la sched_migration_cost_ns, Le CFS doit donc moins souvent considérer les migrations comme „bon marché“. sched_min_granularity_ns et sched_wakeup_granularity_ns influencent les tranches de temps et le comportement de préemption ; j'avance ici à tâtons dans des tests A/B. Pour les noyaux de latence isolés, j'utilise de manière ciblée des housekeeping-CPUs et placer des threads RCU/noyau à l'écart des cœurs chauds (nohz_full/rcu_nocbs sur des hôtes sélectionnés). Ces interventions sont en fonction du contexteJe ne les modifie que par classe de charge de travail et je les remets en place en les surveillant de près si la variance ou le débit en pâtissent.
Bases de données et masques Affinity
Dans les bases de données, une bonne Affectation Transactions en ligne, tâches de maintenance et gestion des E/S. SQL Server prend en charge les masques d'affinité, qui me permettent de définir des ensembles d'unités centrales pour les threads du moteur et séparément pour les E/S. J'évite les chevauchements entre les masques d'affinité et les masques d'E/S, sinon les threads à chaud entrent en concurrence avec les E/S en bloc. Pour les hôtes de plus de 32 cœurs, j'utilise les masques 64 bits étendus. Ainsi, les log-flushers, les checkpointers et les query-workers restent propres les uns par rapport aux autres. isolé.
Chemins de stockage et files d'attente NVMe
À l'adresse suivante : blk-mq je mappe les files d'attente NVMe et de stockage sur les cœurs du même domaine NUMA que les DB-workers. Les threads de flux de logs et les IRQ des files NVMe correspondantes atterrissent sur des cœurs voisins, afin que les confirmations d'écriture ne traversent pas la socket. Je veille à ce que les app-threads et les IRQ de stockage fortement sollicitées ne partagent pas le même noyau, sinon des blocs de tête de ligne apparaissent. J'utilise les ordonnanceurs multi-files de manière à ce que le nombre de files d'attente corresponde au nombre de cœurs effectivement alloués - trop de files d'attente ne font qu'augmenter les frais généraux, trop peu de files d'attente génèrent une contention de verrouillage.
Virtualisation, vCPU pinning et NUMA
Dans KVM ou Hyper-V, je couple vCPU aux noyaux physiques pour éviter le steal time. Je sépare les files d'attente vhost-net/virtio des noyaux chauds des invités afin que IO ne ralentisse pas les threads des applications. NUMA exige en outre de garder un œil sur la localisation de la mémoire, sinon les temps d'accès sont doublés. Pour plus d'informations sur les topologies et le réglage, je vous renvoie à cet article : Architecture NUMA dans l'hébergement. Dans les configurations denses, ce couplage apporte une plus grande régularité. Latence.
Orchestration de conteneurs : politiques de cpuset et QoS
Dans les conteneurs, je mets cpuset.cpus cohérent avec les quotas CPU. Kubernetes fournit avec le gestionnaire de CPU (politique „static“) des cœurs exclusifs pour les pods dans la classe de QoS Guaranteed, lorsque Requests=Limits sont définis. Ainsi, les pods critiques atterrissent sur des cœurs fixes, tandis que les charges de travail Best-Effort restent flexibles. Je planifie les pods en tenant compte de la topologie : je répartis les chemins de latence (Ingress, App, Cache) par nœud NUMA afin que la mémoire et la charge IRQ restent locales. Ce qui est important, c'est la Planification également lors de rollouts : les répliques reçoivent des ensembles de noyaux identiques, sinon les valeurs de mesure dérivent entre les instances.
Cgroupes, équité et isolement
Affinity seul ne garantit pas Équité, c'est pourquoi je les combine avec les cgroupes. cpu.shares donne une priorité relative aux groupes, cpu.max fixe des limites supérieures strictes par tranche de temps. Cela me permet de contenir les voisins bruyants, même s'ils sont connectés au CPU. Dans l'hébergement multi-locataires, je sécurise les services critiques avec des shares plus élevés. Ensemble, ils forment un système clair Séparation sans risque d'overcommit.
Gestion de l'énergie et des fréquences pour des latences prévisibles
Les Power-States influencent sensiblement la gigue. Pour les objectifs stricts de p99, je maintiens des fréquences de base élevées et stables sur les cœurs chauds (performance du gouverneur, ou fréquence de base élevée). energy_performance_preference) et je limite les C-States bas pour que les temps de réveil ne soient pas dominants. J'utilise le turbo avec modération : les threads individuels en profitent, mais les limites thermiques peuvent empêcher l'exécution en parallèle de certains processus. noyaux de la vitesse. Pour obtenir un débit régulier, je fixe des limites de fréquence supérieures/inférieures par socket et je place la logique d'économie d'énergie sur les cœurs froids. Cela permet de réduire la variance sans limiter le débit global de manière excessive.
systemd, taskset et Windows : mise en œuvre
Pour les services permanents, j'utilise systemd avec CPUAffinity=0-3 dans l'unité, combiné avec CPUSchedulingPolicy=fifo pour les charges de travail RT. Je démarre les tâches uniques avec taskset -c 4-7, afin que les sauvegardes ne soient pas envoyées dans des caches à chaud. J'encapsule les conteneurs via cpuset.cpus et cgroupv2 pour que les pods aient leurs noyaux fixes. Sous Windows, je règle le ProcessorAffinity sur un hex de masque de bits via PowerShell. Ces options me donnent des informations précises Contrôle jusqu'à la limite du noyau.
Monitoring et tests : mesurer au lieu de deviner
Je vérifie le succès avec parfait (context-switches, migrations, cache-misses) et suit p95/p99 par timeseries. Les reproductions de charge de travail avec wrk, hey ou sysbench montrent si les valeurs aberrantes diminuent. En outre, j'observe le temps de vol dans les VM et la charge IRQ sur les noyaux hôtes. Une brève comparaison A/B sous charge de pointe met en évidence les hypothèses erronées. Ce n'est qu'une fois que les chiffres correspondent que je fige les règles en tant que règles permanentes. Politiques un.
Risques, limites et anti-patterns
L'épinglage rigide peut être des noyaux tourner à vide si le trafic varie. C'est pourquoi je ne fixe que les threads critiques et laisse les threads non critiques sur le programmateur. L'overcommit consomme également de l'utilité lorsque deux VM bruyantes veulent le même noyau. Si l'on fixe trop, on se heurte plus tard à des points chauds et à une mauvaise utilisation. Une bonne vérification de la réalité : cet article sur le CPU pinning est rarement utile appelle à une utilisation mesurée, avec des objectifs clairs et des Métriques.
Les cas spéciaux : Haute fréquence et temps réel
Pour les sub-millisecondes, je relie Affinity avec la politique RT, le réglage des IRQ et la cohérence NUMA. Je lie les IRQ réseau à leurs propres noyaux et je tiens les threads de l'espace utilisateur à l'écart. Sur les EPYC AMD avec topologie à puce, je sécurise les chemins courts entre le cœur, le contrôleur de mémoire et la carte réseau. Les grandes pages (HugeTLB) aident à réduire les taux d'échec TLB. Ces étapes réduisent considérablement la variance et créent Planification chez HF-Traffic.
Ajustement fin pour les piles populaires
À l'adresse suivante : PHP-FPM je place pm dynamic avec pm.max_children et process_idle_timeout correspondants, afin d'éliminer les worker inoccupés. NGINX fonctionne avec worker_processes auto, mais je lie les travailleurs de manière ciblée aux noyaux chauds. Je garde Apache dans event-MPM brièvement connecté, afin que la file d'attente d'exécution n'augmente pas. Pour Node.js, j'encapsule la charge CPU dans des threads de travail avec leur propre affinité. La boucle d'événements reste ainsi libre et réactive. rapide sur les E/S.
Contrôle IRQ et séparation des E/S
J'épingle IRQ-sur des cœurs dédiés via smp_affinity, afin que les flux de paquets ne suppriment pas les threads d'applications. Je partage les cartes réseau multi-utilisateurs sur plusieurs cœurs, en fonction de la répartition des flux RSS. Je sépare les interruptions de stockage des IRQ réseau afin d'éviter le head-of-line blocking. Les E/S asynchrones et les threadpools dans NGINX empêchent les syscalls bloquants sur les cœurs chauds. Cette séparation permet de raccourcir les chemins et de protéger Charge de pointe.
Guide d'introduction progressive
Je commence avec Profilage sous Real-Traffic, puis je ne définis que les services critiques. Ensuite, je vérifie p95/p99 et les migrations avant de lier d'autres threads. Les cgroups me donnent des possibilités de correction sans redémarrage. Je documente les modifications par hôte et regroupe les règles dans des unités systemd. Ce n'est qu'après avoir obtenu des valeurs de mesure stables que je déroule les Configuration large.
Exploitation, gestion du changement et retour en arrière
Je traite les règles d'affinité comme du code. Je versionne les unités systemd et les politiques de cgroup, je les déroule staged (d'abord Canaries, puis plus large) et prépare un retour clair. Un retour rapide est obligatoire en cas de rupture des SLO p99 ou de baisse du débit. Je gèle les changements avant les périodes de pic et j'observe après chaque étape les taux de migration, les taux d'échec LLC et l'utilisation par noyau. Cela permet de réduire les risques opérationnels et d'éviter que les „bonnes“ optimisations individuelles ne produisent des effets secondaires indésirables lorsqu'elles sont combinées.
Effets de sécurité et d'isolation
Affinity aide aussi à IsolationDans les environnements multi-locataires, je ne partage pas les frères et sœurs SMT entre les clients afin de minimiser la diaphonie et les canaux latéraux. Les services sensibles fonctionnent sur des cœurs exclusifs, séparés des sources IRQ bruyantes. Les migrations du noyau contre les lacunes d'exécution spéculative augmentent les coûts de changement de contexte - un épinglage propre atténue l'effet, car moins de threads se déplacent sur les limites des tuiles. Important : équilibrer les objectifs de sécurité et les objectifs de performance ; parfois, „SMT off“ est justifié pour quelques charges de travail particulièrement sensibles à la protection, tandis que le reste continue à profiter du débit SMT.
KPIs, SLOs et rentabilité
Je définis à l'avance des KPI clairs : latence p95/p99, débit, cs/req (commutateurs contextuels par requête), migrations par seconde et taux de défaillance LLC. Des corridors cibles aident à évaluer les trade-off, par exemple „p99 -25% pour ≤5% de débit max en moins“. Au niveau de l'hôte, j'observe l'équilibre du cœur et le temps d'inactivité afin que l'épinglage ne conduise pas à une inoccupation coûteuse. L'affinité est économiquement judicieuse si la prévisibilité obtenue réduit les pénalités SLO ou augmente la densité dans les clusters, car les tampons de réserve peuvent être plus petits. Sans ces chiffres, l'épinglage reste une intuition - avec elle, il devient une solution solide. Optimisation.
Rétrospective et mise en perspective
Affinity livre sur Serveurs avec un grand nombre de cœurs offrent souvent une prévisibilité étonnante pour de faibles interventions. Dans les VM avec overcommit ou un trafic très fluctuant, j'étrangle l'intervention. La conscience de la NUMA, le réglage de l'IRQ et des quotas équitables sont décisifs pour le succès. Sans monitoring, l'épinglage devient vite un fardeau, avec des chiffres, il reste un outil. Celui qui procède de manière sélective est gagnant Prévisibilité et utilise le matériel de manière efficace.
Résumé
J'utilise Affinity CPU pour serveurs, J'utilise des outils d'affinage pour maintenir les fils d'attente à chaud près de leurs données, réduire les migrations et lisser les pics de latence. Dans les serveurs web, PHP-FPM, bases de données et VMs, je combine Affinity avec Cgroups, IRQ-Tuning et NUMA-Discipline. Les options Systemd, taskset et Container-cpusets rendent la mise en œuvre utilisable au quotidien. Je sécurise l'effet à l'aide de mesures parf et de séries chronologiques et j'agis progressivement sur les régulateurs. En utilisant l'épinglage de manière ciblée, on obtient des temps de réponse constants, des caches propres et une augmentation mesurable de la performance. Débit.


