...

Linux Scheduler CFS: funzionalità e alternative nell'hosting di server

Lo scheduler CFS di Linux controlla il modo in cui i core del server assegnano il loro tempo ai processi e quindi influenza direttamente la latenza, il throughput e l'equità nell'hosting dei server. In questa guida vi spiego come funziona, le leve di regolazione e le alternative utili come ULE, BFS ed EEVDF per Ospitare con server web.

Punti centrali

  • Equità e vruntime determinare quale compito riceve la CPU.
  • Gruppi C regolamentare le quote e cpu.shares per l'isolamento dei clienti.
  • Messa a punto del kernel tramite sched_latency_ns e Granularità.
  • Alternative come BFS, ULE, EEVDF per le applicazioni speciali. Carichi di lavoro.
  • PraticaAffinità del nucleo, pianificatore I/O e Test combinare.

Come funziona la CFS nella vita quotidiana

Con il Completely Fair Scheduler, un runtime virtuale decide quale task deve essere eseguito successivamente, con il risultato di una equo e prevedibile Assegnazione viene creato. Ogni task riceve tempo di CPU proporzionale al valore nice, in modo che un valore nice basso riceva più condivisioni. Negli ambienti di hosting, molte piccole richieste web, cronjob e backup dividono la CPU tra loro senza che un processo occupi tutto. I carichi di lavoro interattivi, come le richieste NGINX, beneficiano di fette di tempo frequenti e brevi, mentre le attività batch ricevono blocchi più lunghi. Ciò significa che i tempi di risposta rimangono affidabili per gli utenti, anche se molti siti elaborano le richieste in parallelo.

Uso i Cgroup per limitare i clienti e i servizi, perché cpu.shares e cpu.max assicurano la chiarezza Totale delle azioni e duro Limiti. Un valore predefinito di 1024 azioni per “normale” e 512 per “meno importante” distribuisce i core in modo comprensibile. Con cpu.max, ad esempio, ho impostato 50ms in un periodo di 100ms, che corrisponde effettivamente a 50% di CPU. Questa configurazione offre riserve prevedibili per ospitare carichi di lavoro variabili. Una spiegazione compatta del principio si trova su distribuzione equa della CPU.

La meccanica della CFS spiegata in modo chiaro

Nel suo nucleo, CFS gestisce tutti i task pronti per l'esecuzione in un albero rosso/nero, ordinati in base a vruntime e con un efficiente Selezione del runtime virtuale più piccolo. Questo task viene eseguito successivamente e aumenta il suo vruntime in proporzione al tempo di CPU consumato e ponderato tramite il valore nice. In questo modo si crea un equilibrio fluido senza code rigide, che offre risultati puliti, soprattutto con carichi di lavoro misti. Sui sistemi multi-core, lo scheduler sposta i task tra le code di esecuzione, ma presta attenzione alla localizzazione della cache tramite l'affinità dei core. In questo modo, CFS combina il bilanciamento del carico con il minor numero possibile di migrazioni costose.

Per la regolazione fine, parametri come sched_latency_ns e sched_min_granularity_ns impostano il percorso per Latenza e Produttività. I valori di latenza più piccoli favoriscono i lavori brevi e interattivi, mentre quelli più grandi rafforzano i lavori batch. Nei test con strumenti come stress-ng e fio, verifico l'effetto sui tempi di risposta e sull'utilizzo della CPU. Con l'aumentare del numero di task, aumenta anche l'overhead amministrativo dell'albero, che può manifestarsi con picchi di latenza. Tuttavia, le quote e i limiti impostati correttamente tengono sotto controllo questi effetti negli ambienti di hosting.

Punti di forza di CFS nell'hosting di server

La forza maggiore risiede nella Equità, in modo uniforme e comprensibile Risorse distribuito. Per gli ambienti condivisi, ciò significa che nessun cliente ne sostituisce altri in modo permanente, perché le quote e le condivisioni definiscono chiaramente le ponderazioni. I servizi interattivi ricevono tempi di risposta rapidi, mentre i backup possono essere eseguiti senza fretta. La prioritizzazione tramite valori piacevoli completa questo quadro e mi lascia spazio per il coordinamento a seconda del ruolo di un servizio. Il bilanciamento del carico su tutti i core mi permette di fare un buon uso della potenza di calcolo disponibile senza dare troppo spazio ai momenti di Jeff dei singoli thread.

In pratica, la forza di CFS diventa evidente quando si verificano i picchi del server web e arrivano molte richieste brevi, poiché CFS assegna slot frequenti a questo tipo di attività. I gruppi C puliti aiutano a stabilire limiti massimi rigidi per cliente o contenitore. Le misurazioni delle medie e dei percentili mostrano tempi di risposta affidabili, che si rivelano utili nelle attività quotidiane. Questo approccio è particolarmente utile per gli stack di applicazioni con molti componenti. È proprio in questi casi che il mix di equità prevedibile e sufficiente flessibilità ottiene un ottimo risultato.

Limiti e ostacoli tipici

Con un numero estremamente elevato di task simultanei, l'overhead delle operazioni ad albero aumenta, mentre non è il caso di Suggerimenti il Latenza può aumentare le prestazioni. Nelle configurazioni di hosting con molte richieste molto brevi, a volte si verificano frequenti cambi di contesto. Questo comportamento di “thrashing” riduce l'efficienza se i valori di granularità sono scelti in modo errato. Un numero minore di fette di tempo, ma più lunghe, può essere utile, a condizione che venga mantenuta l'interattività. CFS reagisce in modo sensibile a quote non corrette, per questo motivo controllo costantemente i limiti con test di carico.

Anche i carichi di lavoro compatibili con l'affinità soffrono se le attività passano troppo spesso da un core all'altro. Un concetto di affinità pulito mantiene le cache calde e riduce i costi di migrazione. Mi piace anche vincolare i lavori batch rumorosi ai propri core, in modo che le richieste web vengano eseguite tranquillamente sui loro core. Per i servizi critici dal punto di vista della latenza, vale la pena di impostare valori bassi e gradevoli e una latenza finemente regolata. Alla fine, ciò che conta è che le misurazioni confermino i parametri selezionati.

Confronto tra le alternative: ULE, BFS e EEVDF

Per i carichi di lavoro speciali, esamino le alternative al fine di Latenza oppure Scala assegnano le priorità in modo diverso. ULE utilizza code più semplici e ottiene risultati con un minore sforzo amministrativo, BFS dà priorità alla reattività e brilla con pochi task, mentre EEVDF combina una distribuzione equa con le scadenze. EEVDF, in particolare, promette tempi di attesa più brevi per i carichi interattivi, perché lo scheduler presta maggiore attenzione alla “prima scadenza ammissibile”. Per i campi di server molto grandi, ciò che conta davvero alla fine è quale mix di efficienza e pianificabilità vince davvero nel proprio stack. Un esame strutturato dei punti di forza, dei punti deboli e dei campi di applicazione aiuta nella selezione.

scheduler Complessità Punti di forza nell'hosting Punti di debolezza Adatto per
CFS Alto Distribuzione equa, Gruppi C Picchi di latenza Hosting condiviso, carichi misti
ULE Basso Spunti semplici, bassi Carico Meno isolamento Macchine virtuali, modelli simili all'HPC
BFS Medio Interattività, Velocità Scalatura debole Desktop, piccoli server
EEVDF Medio Bassa latenza, scadenze Ancora poca pratica Stack di hosting moderno

Messa a punto del kernel: passi pratici per il CFS

Per CFS, spesso cambio sched_autogroup_enabled=0, in modo che nessun gruppo implicito distorca l'immagine e il Distribuzione del carico chiaro resti. Con sched_latency_ns mi piace partire da 20ms, che favorisce i servizi interattivi, e regolare sched_min_granularity_ns per domare i cambiamenti di contesto. I valori dipendono dal profilo: molte richieste web brevi necessitano di una messa a punto diversa rispetto alle finestre di backup. Esamino le modifiche in serie e misuro i percentili invece di guardare solo le medie. In questo modo non solo i valori medi appaiono gradevoli, ma anche le lunghe code si riducono.

Se volete approfondire i parametri di sysctl, troverete una buona introduzione qui: messa a punto di sysctl. Regolo anche la distribuzione degli IRQ, il regolatore della CPU e i profili energetici, in modo che la CPU non passi costantemente a stati economici. Uso i regolatori di prestazioni per gli stack basati sulla latenza, mentre i box batch puri vivono con un controllo bilanciato. Separo chiaramente le fasi di test e di produzione per evitare sorprese. Dopo ogni fase, controllo i log e le metriche prima di andare avanti.

Usare cgroup e quote in modo sensato

Con cpu.shares assegno il relativo Pesi mentre cpu.max è difficile Confini insiemi. Un cliente con 512 azioni ottiene la metà del tempo di calcolo di un cliente con 1024, se entrambi generano carico nello stesso momento. Uso cpu.max per limitare i picchi in modo pulito, ad esempio 50ms in 100ms. Per i lavori dedicati, vale cpuset.cpus, in modo che un servizio utilizzi core fissi e la cache rimanga calda. Tutto sommato, si ottiene una separazione resiliente tra clienti e servizi.

Documento ogni modifica e la confronto con i livelli di servizio che voglio raggiungere. Senza valori misurati, le quote portano rapidamente a interpretazioni errate, per questo motivo accompagno sempre le regolazioni con test di carico. Per i container, suggerisco quote realistiche in grado di gestire i picchi ma senza rallentare l'host. Resta importante avere un budget di errore prevedibile, in modo da rilevare picchi di latenza evidenti. Se lo si fa con costanza, si eviteranno sorprese nei momenti di picco.

Pratica: server web e database sotto CFS

I server web orientati agli eventi riducono i cambi di contesto e si armonizzano con il CFS, con il risultato di una costante Tempi di risposta e migliore Scala generato. Nei test, ho visto che NGINX mantiene tassi di richiesta più elevati con meno jitter sullo stesso hardware. I database reagiscono positivamente all'affinità dei core quando i lavori in background sono tenuti lontani dai core caldi. Semplici regole aiutano: Web sui core A-B, batch su C-D e DB su E-F. In questo modo, lo stack mantiene la pipeline pulita e le cache calde.

Molti piccoli worker PHP FPM causano troppi switch con una granularità aggressiva. Aumento quindi il time slice minimo e verifico se i tempi di risposta rimangono stabili. Allo stesso tempo, limito i log di chat in modo che l'I/O non diventi un freno. Il CFS fornisce la base, ma le prestazioni massime si ottengono con la messa a punto dell'intero stack. In questo modo, tutti gli ingranaggi si incastrano senza togliere il respiro all'host.

I/O di memoria e scheduling della CPU: l'interazione

Lo scheduler della CPU e lo scheduler dell'I/O si influenzano a vicenda, ed è per questo che una configurazione armonizzata può fare una differenza notevole. Vantaggi all'indirizzo Latenza porta. Per NVMe di solito uso Noop o mq-deadline, mentre sugli HDD mq-deadline serve meglio le code lunghe. Se la CPU alloca il tempo in tempo ma il percorso di I/O si blocca, l'effetto complessivo viene annullato. Pertanto, verifico lo scheduler I/O in parallelo con i parametri CFS. Qui fornisco una panoramica di Noop, mq-deadline e BFQ: Pianificatore I/O a confronto.

Per gli host di database, regolo la profondità della coda e il read-ahead in modo che gli slot pianificati dal CFS non si esauriscano a causa del blocco dell'I/O. I server web con molti file di piccole dimensioni traggono vantaggio da una bassa latenza nello stack I/O. Negli scenari di virtualizzazione, mi affido a scheduler coerenti su host e guest per evitare schemi imprevedibili. Questo è il modo in cui lo scheduler della CPU interagisce con il sottosistema di archiviazione. Alla fine, ciò che conta è la catena coerente dalla richiesta alla risposta.

Bilanciamento SMP, affinità dei core e NUMA

Dirigo i thread su core fissi in modo che Cache costi di riscaldamento e migrazione piccolo rimanere. Per gli host NUMA, collego memoria e CPU perché gli accessi remoti alla memoria aumentano la latenza. Il CFS bilancia il carico tra le code di esecuzione, ma spesso le regole di affinità intenzionali permettono di ottenere di più. I servizi con accesso frequente alla cache beneficiano di gruppi di core stabili. I lavori batch possono vagare purché non interferiscano con i core caldi.

In pratica, imposto le opzioni cpuset.cpus e numactl, quindi verifico i tempi di richiesta e i tassi di perdita della CPU. Meno migrazioni non necessarie ci sono, migliore è il tempo di risposta. Valuto anche la distribuzione degli interrupt in modo che i picchi di IRQ non intasino un core. In questo modo, ottengo un clock regolare dei thread importanti. Questa tranquillità si riflette sulle prestazioni complessive dello stack.

Programmazione di gruppo: bella, ponderazione e gerarchie

Un ostacolo frequente nell'hosting è il Interazione tra bello-Priorità e Pesi del gruppo C. CFS distribuisce prima in modo equo tra i gruppi, poi all'interno del gruppo tra i task. Ciò significa che un processo con nice -5 può comunque ottenere meno CPU di un altro con nice 0 se il suo gruppo (client/container) ha un peso inferiore. Per ottenere risultati coerenti, ho quindi impostato prima il parametro Pesi del gruppo e usare nice solo per la messa a punto all'interno di un servizio.

In pratica, lavoro con alcuni livelli chiari (ad esempio 512/1024/2048 azioni per “basso/normale/alto”) e documento quali servizi sono in esecuzione in quale gruppo. In questo modo si mantiene il Equità rintracciabili nella gerarchia. Chi lavora molto con processi di breve durata (ad esempio, lavori CGI/CLI) trae vantaggio anche da basato su cgroup perché altrimenti i task volatili aggirerebbero involontariamente il corsetto di gruppo. Uso regolarmente le metriche di runtime per verificare se l'allocazione interna corrisponde ancora al profilo di carico.

Contenitori e orchestrazione: richieste, limiti e throttling

Negli ambienti con container, una “richiesta” di solito corrisponde a peso relativo (azioni/peso), un “limite” sulla Quota (cpu.max). L'interazione decide su StrozzaturaSe la quota è troppo stretta, la CPU del container viene rallentata nel periodo - visibile nei rimbalzi della latenza p95/p99. Pertanto, mantengo le quote in modo tale che i normali burst rientrino nel periodo e che i servizi siano raramente sottoposti a un forte rallentamento. Dove è disponibile, uso una quota Burst-(ad esempio, cpu.max.burst) per attenuare i picchi brevi senza distorsioni.

È importante non impostare richieste troppo basse: Se i pesi sono troppo bassi, i servizi interattivi rimarranno indietro rispetto al rumore del batch. Calibro le richieste in base al carico di base misurato e ai limiti di sicurezza, in modo che Bilanci di errore vengono mantenuti durante i momenti di picco. Per i nodi multi-tenant, pianifico anche i core buffer in modo che i picchi di carico dei singoli container non influiscano sui vicini.

Metodi di misurazione e risoluzione dei problemi nel contesto dello scheduler

Non valuto mai la sintonizzazione della CFS alla cieca, ma la misuro in modo mirato. Uso per la visione d'insieme:

  • Lunghezza della coda per CPU (carico rispetto ai core attivi),
  • Cambiamento di contesto al secondo e il numero di thread,
  • Furto di CPU e SoftIRQ-Condivisione,
  • Percentile dei tempi di risposta (p50/p95/p99),
  • Distribuzione di vruntime o le latenze di programmazione.

Se si verificano picchi di latenza, cerco prima di tutto Strozzatura (quota esaurita), poi dopo Migrazioni (cache fredda) e infine dopo Blocchi di I/O (profondità della coda, saturazione dello storage). Osservo gli schemi di risveglio: risvegli frequenti e brevi di molti lavoratori indicano una granularità troppo fine o un I/O troppo chiacchierato. Un aumento della percentuale di ksoftirqd su un core indica code IRQ calde: in questo caso distribuisco gli IRQ e attivo RPS/XPS in modo che il carico di rete sia distribuito più ampiamente.

Classi in tempo reale, prelazione e controllo delle spunte

Oltre alla CFS, esistono le seguenti classi in tempo reale SCHED_FIFO/RR. Essi sovrascrivono il CFS: un thread RT configurato in modo errato può letteralmente togliere l'aria al sistema. Per questo motivo assegno RT-Prio solo in modo molto selettivo (ad esempio per l'audio/telemetria) e definisco dei chiari cani da guardia. Per l'hosting, CFS con pesi puliti è di solito sufficiente.

A PrelazioneLa scelta del modello di prelazione (ad esempio “volontario” o “prelazione completa/dinamica”) modifica il rapporto latenza/throughput. Per gli stack web preferisco più preemption, per gli host batch puri meno. Ottimizzazioni dei tick (nohz-modes) può ridurre il jitter, ma deve essere usato con cautela. Sui core isolati, di tanto in tanto combino nohz_full e Affinity, in modo che i thread caldi funzionino il più indisturbati possibile: è importante che i carichi di sistema e IRQ non migrino inavvertitamente su questi core.

Virtualizzazione: KVM, vCPU pinning e steal time

Nell'ambiente dell'ipervisore, lo scheduler dell'host determina quando vCPU può essere eseguito. Creare overbooking Tempo di furto nei guest, che agisce come una “latenza invisibile”. Per i tenant critici per la latenza, collego le vCPU ai core fisici e mantengo moderato l'overcommit. Inoltre, separo i thread dell'emulatore (thread IO, vhost) dai core caldi dei guest in modo che non interferiscano tra loro.

Evito il doppio throttling: se il guest sta già utilizzando cpu.max, non imposto ulteriori quote rigide sull'host per lo stesso carico di lavoro. Il controllo della frequenza rimane compito dell'host; gli ospiti ne beneficiano indirettamente se il governor dell'host scala in modo pulito con il carico di lavoro effettivo. Per le latenze uniformi, ritengo che la stabilità al di là della pura frequenza massima sia più importante del picco di GHz sulla carta.

AutoNUMA, localizzazione della memoria e THP

NUMA può essere un guadagno di prestazioni o una trappola per le prestazioni. AutoNUMA spesso aiuta, ma può generare un sovraccarico aggiuntivo con i thread in forte roaming. Negli stack di hosting con chiari confini di servizio, io applico CPU e Memoria (cpuset.cpus e cpuset.mems) insieme. Ciò significa che i dati caldi rimangono locali e il CFS deve compensare un minor numero di migrazioni.

Pagine grandi (THP) riducono la pressione TLB, ma non sono adatte a tutti i profili. Per i database, “madvise” può avere più senso di un “sempre” generico. I page fault bloccanti colpiscono duramente la latenza interattiva; per questo motivo pianifico i buffer (page cache, buffer condiviso) in modo che gli slot CFS siano utilizzati in modo produttivo e non attendano eventi di I/O o MMU. Questo può essere misurato attraverso i tassi di errore di pagina e le curve di miss della cache.

Percorso di rete: controllo IRQ, RPS/XPS e polling di occupato

Molti carichi di lavoro web sono dominati dalle NIC. Distribuisco IRQ-della scheda di rete su più core e mantenerli affine ai thread worker, in modo che i risvegli rimangano locali. RPS/XPS aiuta a risolvere gli hotspot soft se le singole code RX/TX stanno sopportando un carico eccessivo. Se ksoftirqd diventa visibilmente caldo, ciò indica che le SoftIRQ sono sovraccariche - quindi equalizzo i flussi e aumento i parametri di budget, se necessario, senza perdere l'equità.

Il polling occupato opzionale può avere senso in configurazioni molto speciali a bassa latenza, ma costa tempo alla CPU. Lo uso raramente e solo se posso dimostrare con misurazioni che p99 cala in modo significativo senza stressare l'host in generale. Normalmente, l'affinità IRQ pulita, i gruppi C e la granularità CFS offrono un miglior rapporto costi-benefici.

Prospettive: Da CFS a EEVDF e approcci userspace

EEVDF estende la distribuzione equa per includere le scadenze, il che è sensibilmente più breve e più prevedibile Risposte promesse. Soprattutto con gli obiettivi interattivi di latenza, questo può fare la differenza. Tengo d'occhio le versioni del kernel e provo EEVDF separatamente prima di cambiare. Allo stesso tempo, si sta affermando lo scheduling dello spazio utente tramite modelli eBPF, che possono consentire un controllo aggiuntivo a seconda del carico di lavoro. CFS rimane rilevante per le infrastrutture di hosting, ma EEVDF si affermerà rapidamente.

Rimane importante un chiaro percorso di migrazione: test, rollout su host selezionati, quindi espansione. Questo è l'unico modo per tenere sotto controllo percentili e tassi di errore. Mantengo i benchmark vicini alla realtà, comprese le fasi di burst e i backend lenti. Solo allora intervengo negli ambienti live. In questo modo è possibile ottenere progressi senza brutte sorprese.

Riassumendo brevemente

Lo Scheduler CFS di Linux offre una distribuzione equa, solide integrazioni e una buona Controllo tramite Gruppi C. Con parametri sysctl adeguati, affinità pulita e quote realistiche, mantengo basse le latenze e alto il throughput. ULE, BFS o EEVDF offrono un'ulteriore leva per modelli speciali. Misuro, confronto e introduco le modifiche per gradi per limitare i rischi. In questo modo l'hosting rimane prevedibile e le prestazioni sono al loro posto.

Articoli attuali