...

Linux Scheduler CFS: Funcionalidade e alternativas no alojamento de servidores

O agendador CFS do Linux controla a forma como os núcleos do servidor atribuem o seu tempo aos processos, influenciando assim diretamente a latência, o débito e a equidade no alojamento de servidores. Neste guia, explico como ele funciona, as alavancas de ajuste e alternativas úteis, como ULE, BFS e EEVDF para Hospedagem com servidores web.

Pontos centrais

  • Equidade e vruntime determinam qual a tarefa que fica com a CPU.
  • Grupos C regulamentar as quotas e cpu.shares para o isolamento do cliente.
  • Afinação do kernel através de sched_latency_ns e Granularidade.
  • Alternativas tais como BFS, ULE, EEVDF para Cargas de trabalho.
  • PráticaAfinidade de núcleo, planeador de E/S e Testes combinar.

Como funciona a SFC no dia a dia

Com o Agendador Completamente Justo, um tempo de execução virtual decide qual tarefa deve ser executada em seguida, resultando em um justo e previsível Atribuição é criado. Cada tarefa recebe tempo de CPU proporcional ao valor nice, de modo que um valor nice baixo recebe mais acções. Em ambientes de hospedagem, muitas pequenas solicitações da Web, cronjobs e backups dividem a CPU entre eles sem que um processo ocupe tudo. As cargas de trabalho interactivas, como os pedidos NGINX, beneficiam de fatias de tempo curtas e frequentes, enquanto as tarefas em lote recebem blocos mais longos. Isto significa que os tempos de resposta permanecem fiáveis para os utilizadores, mesmo que muitos sites estejam a processar pedidos em paralelo.

Utilizo Cgroups para limitar os clientes e os serviços, porque cpu.shares e cpu.max asseguram uma Totais das acções e duro Limites. Um valor padrão de 1024 compartilhamentos para “normal” e 512 para “menos importante” distribui os núcleos de forma compreensível. Com cpu.max, por exemplo, defini 50ms num período de 100ms, o que corresponde efetivamente a 50% de quota de CPU. Essa configuração oferece reservas previsíveis para hospedar cargas de trabalho com cargas variáveis. Posso encontrar uma explicação compacta do princípio em distribuição equitativa da CPU.

A mecânica do SFC explicada de forma clara

No seu núcleo, o CFS gere todas as tarefas prontas a executar numa árvore vermelha/preta, ordenada por vruntime e com uma eficiente Seleção do tempo de execução virtual mais pequeno. Essa tarefa é executada em seguida e aumenta seu tempo de execução virtual proporcionalmente ao tempo de CPU consumido e ponderado pelo valor nice. Isso cria um equilíbrio fluido sem filas difíceis, o que fornece resultados limpos, especialmente com cargas de trabalho mistas. Em sistemas com vários núcleos, o agendador move as tarefas entre as filas de execução, mas presta atenção à localidade do cache por meio da afinidade de núcleo. Desta forma, o CFS combina o balanceamento de carga com o menor número possível de migrações dispendiosas.

Para um ajuste fino, parâmetros como sched_latency_ns e sched_min_granularity_ns definem o curso para Latência e Rendimento. Valores de latência mais pequenos favorecem os trabalhos curtos e interactivos, enquanto valores maiores reforçam os trabalhos em lote. Em testes com ferramentas como o stress-ng e o fio, verifico o efeito nos tempos de resposta e na utilização da CPU. À medida que o número de tarefas aumenta, também aumenta a sobrecarga administrativa da árvore, que pode se manifestar como picos de latência. No entanto, quotas e limites corretamente definidos mantêm estes efeitos sob controlo em ambientes de alojamento.

Pontos fortes do CFS no alojamento de servidores

A maior força reside na Equidade, de forma homogénea e compreensível Recursos distribuídos. Para ambientes partilhados, isto significa que nenhum cliente desloca permanentemente os outros porque as quotas e as partilhas definem claramente as ponderações. Os serviços interactivos recebem tempos de resposta rápidos, enquanto as cópias de segurança podem ser executadas sem pressas. A definição de prioridades através de valores simpáticos completa este quadro e deixa-me espaço para a coordenação em função do papel de um serviço. O balanceamento de carga em todos os núcleos permite-me fazer uma boa utilização do poder de computação disponível sem dar demasiado espaço a Jeff moments de threads individuais.

Na prática, a força do CFS torna-se evidente quando ocorrem picos no servidor Web e chegam muitos pedidos curtos, uma vez que o CFS atribui slots frequentes a este tipo de tarefas. Os Clean Cgroups ajudam a definir limites superiores rígidos por cliente ou contentor. As medições das médias e percentis mostram tempos de resposta fiáveis, o que compensa no dia a dia. Esta abordagem é particularmente útil para pilhas de aplicações com muitos componentes. É precisamente aqui que a combinação de equidade previsível e flexibilidade suficiente tem uma pontuação elevada.

Limites e obstáculos típicos

Com um número extremamente elevado de tarefas simultâneas, a sobrecarga das operações em árvore aumenta, o que não acontece com Dicas o Latência pode aumentar o desempenho. Em configurações de alojamento com muitos pedidos muito curtos, há por vezes mudanças de contexto frequentes. Este comportamento de “thrashing” reduz a eficiência se os valores de granularidade forem escolhidos incorretamente. Menos fatias de tempo, mas mais longas, podem ajudar, desde que a interatividade seja mantida. O CFS reage de forma sensível a quotas incorrectas, razão pela qual verifico constantemente os limites com testes de carga.

Mesmo as cargas de trabalho favoráveis à afinidade sofrem se as tarefas alternarem entre núcleos com muita frequência. Um conceito de afinidade limpo mantém as caches quentes e reduz os custos de migração. Eu também gosto de vincular trabalhos em lote barulhentos aos seus próprios núcleos, para que as solicitações da Web sejam executadas silenciosamente em seus núcleos. Para serviços críticos de latência, vale a pena definir valores baixos de nice e uma latência bem ajustada. No final, o que conta é que as medições confirmam os parâmetros selecionados.

Comparação de alternativas: ULE, BFS e EEVDF

Para cargas de trabalho especiais, procuro alternativas para Latência ou Escalonamento estabelecem prioridades de forma diferente. O ULE utiliza filas de espera mais simples e obtém resultados com menos esforço administrativo, o BFS dá prioridade à capacidade de resposta e destaca-se com poucas tarefas e o EEVDF combina uma distribuição equitativa com prazos. O EEVDF, em particular, promete tempos de espera mais curtos para cargas interactivas, porque o programador presta mais atenção ao “prazo mais cedo possível”. Para campos de servidores muito grandes, o que realmente conta no final é qual a combinação de eficiência e capacidade de planeamento que realmente ganha na sua própria pilha. Uma análise estruturada dos pontos fortes e fracos e dos campos de aplicação ajuda na seleção.

agendador Complexidade Pontos fortes do alojamento Pontos fracos Adequado para
CFS Elevado Distribuição equitativa, Grupos C Picos de latência Alojamento partilhado, cargas mistas
ULE Baixa Pistas simples, baixo Carga Menos isolamento VMs, padrões do tipo HPC
BFS Médio Interatividade, Velocidade Escalonamento fraco Computadores de secretária, pequenos servidores
EEVDF Médio Baixa latência, prazos Ainda pouca prática Pilhas de alojamento modernas

Afinação do kernel: passos práticos para o CFS

Para o CFS, altero frequentemente sched_autogroup_enabled=0 para que nenhum grupo implícito distorça a imagem e o Distribuição da carga claro restos. Com sched_latency_ns gosto de começar em 20ms, o que favorece os serviços interactivos, e ajustar sched_min_granularity_ns para domar as alterações de contexto. Os valores dependem do perfil: muitos pedidos web curtos precisam de um ajuste fino diferente das janelas de backup. Eu testo as alterações em série e meço os percentis em vez de olhar apenas para as médias. Isso garante que não apenas os valores médios pareçam bonitos, mas também que as longas filas diminuam.

Se quiser aprofundar os parâmetros sysctl, encontrará uma boa introdução aqui: ajuste do sysctl. Também afino a distribuição de IRQ, o regulador da CPU e os perfis de energia para que a CPU não entre constantemente em estados económicos. Utilizo reguladores de desempenho para pilhas orientadas para a latência, enquanto as caixas de lote puras vivem com um controlo equilibrado. Separo claramente as fases de teste e produção para que não haja surpresas. Após cada passo, verifico os registos e as métricas antes de avançar.

Utilizar cgroups e quotas de forma sensata

Com cpu.shares eu atribuo Pesos enquanto cpu.max é difícil Limites conjuntos. Um cliente com 512 acções obtém metade do tempo de computação que um cliente com 1024, se ambos gerarem carga ao mesmo tempo. Eu uso cpu.max para limitar os picos de forma limpa, por exemplo, 50ms em 100ms. Para trabalhos dedicados, vale a pena usar cpuset.cpus para que um serviço utilize núcleos fixos e a cache se mantenha quente. Em suma, isso resulta em uma separação resiliente entre clientes e serviços.

Documento todas as alterações e comparo-as com os níveis de serviço que pretendo atingir. Sem valores medidos, as quotas levam rapidamente a interpretações erradas, e é por isso que acompanho sempre os ajustes com testes de carga. Para os contentores, sugiro quotas realistas que possam lidar com os picos, mas que não tornem o anfitrião mais lento. Continua a ser importante ter um orçamento de erro previsível para que sejam detectados picos de latência perceptíveis. Se o fizer de forma consistente, evitará surpresas nas horas de ponta.

Prática: Servidor Web e bases de dados no CFS

Os servidores Web orientados para eventos reduzem as mudanças de contexto e harmonizam-se com o CFS, o que resulta numa constante Tempos de resposta e melhor Escalonamento gerado. Nos testes, vejo que o NGINX mantém taxas de solicitação mais altas com menos jitter no mesmo hardware. Os bancos de dados reagem positivamente à afinidade de núcleos quando os trabalhos em segundo plano são mantidos longe dos núcleos quentes. Regras simples ajudam: Web nos núcleos A-B, batch nos C-D e DB nos E-F. Dessa forma, a pilha mantém o pipeline limpo e os caches quentes.

Muitos pequenos trabalhadores do PHP FPM causam demasiados switches com granularidade agressiva. Aumento então a fatia de tempo mínima e verifico se os tempos de resposta permanecem estáveis. Ao mesmo tempo, eu reduzo os logs de conversação para que a E/S não se torne um freio. O CFS fornece a base aqui, mas o desempenho máximo é alcançado através do ajuste fino de toda a pilha. Dessa forma, todas as engrenagens se interligam sem tirar o fôlego do host.

E/S da memória e programação da CPU: a interação

O agendador da CPU e o agendador de E/S influenciam-se mutuamente, razão pela qual uma configuração harmonizada pode fazer uma diferença notável. Vantagens em Latência traz. Para NVMe, costumo usar Noop ou mq-deadline, enquanto em HDDs o mq-deadline atende melhor a filas longas. Se a CPU aloca tempo no tempo, mas o caminho de E/S trava, o efeito geral é cancelado. Por isso, verifico o agendador de E/S em paralelo com os parâmetros do CFS. Eu forneço uma visão geral do Noop, mq-deadline e BFQ aqui: Planeador de E/S em comparação.

Para hosts de banco de dados, eu ajusto a profundidade das filas e o read-ahead para que os slots agendados pelo CFS não sejam perdidos devido ao bloqueio de E/S. As caixas de servidores Web com muitos ficheiros pequenos beneficiam de baixa latência na pilha de E/S. Em cenários de virtualização, eu confio em agendadores consistentes no host e no convidado para evitar padrões imprevisíveis. É assim que o agendador da CPU interage com o subsistema de armazenamento. No final, o que conta é a cadeia coerente desde o pedido até à resposta.

Balanceamento de SMP, afinidade de núcleos e NUMA

Direcciono as threads para núcleos fixos para que Caches custos de aquecimento e migração pequeno permanecer. Para hosts NUMA, eu coloco a memória e a CPU juntas porque os acessos remotos à memória aumentam a latência. O CFS equilibra a carga entre as filas de execução, mas as regras de afinidade deliberadas costumam ser mais eficazes. Serviços com acesso frequente ao cache se beneficiam de grupos de núcleo estáveis. Os trabalhos em lote podem vaguear desde que não interfiram com os núcleos quentes.

Na prática, eu defino as opções cpuset.cpus e numactl, depois testo os tempos de solicitação e as taxas de falha da CPU. Quanto menos migrações desnecessárias, melhor o tempo de resposta. Eu também avalio a distribuição de interrupções para que os picos de IRQs difíceis não obstruam um núcleo. Desta forma, consigo um clocking suave das threads importantes. Esta calma compensa o desempenho geral da pilha.

Planeamento de grupos: bom, ponderação e hierarquias

Um obstáculo frequente no alojamento é a Interação entre legal-Prioridades e Pesos do grupo C. O CFS primeiro distribui de forma justa entre os grupos e depois dentro do grupo entre as tarefas. Isso significa que um processo com nice -5 ainda pode obter menos CPU do que outro com nice 0 se seu grupo (cliente/contêiner) tiver um peso menor. Para obter resultados consistentes, primeiro defino o Pesos dos grupos e utilizar o nice apenas para afinações num serviço.

Na prática, trabalho com alguns níveis claros (por exemplo, 512/1024/2048 partilhas para “baixo/normal/alto”) e documento que serviços estão a ser executados em que grupo. Isso mantém o Equidade rastreáveis na hierarquia. Aqueles que trabalham muito com processos de curta duração (por exemplo, trabalhos CGI/CLI) também beneficiam de baseado em grupos porque, de outra forma, as tarefas voláteis contornariam involuntariamente o espartilho de grupo. Utilizo regularmente métricas de tempo de execução para verificar se a alocação interna ainda corresponde ao perfil de carga.

Contentores e orquestração: pedidos, limites e estrangulamento

Em ambientes de contentor, um “pedido” é normalmente mapeado para peso relativo (acções/peso), um “limite” para a Quota (cpu.max). A interação decide sobre EstrangulamentoSe a quota for muito apertada, a CPU do contentor fica mais lenta durante o período - visível nos saltos de latência p95/p99. Por isso, mantenho as quotas de forma a que as explosões normais se enquadrem no período e os serviços raramente sejam estrangulados. Quando disponível, eu uso um Explosão-reserva (por exemplo, cpu.max.burst) para amortecer picos curtos sem distorções.

É importante não definir pedidos demasiado baixos: Se os pesos forem demasiado baixos, os serviços interactivos ficarão atrás do ruído do lote. Calibro os pedidos com base na carga de base medida e nos limites de segurança, de modo a que Orçamentos de erro são mantidos durante as horas de ponta. Para nós multilocatários, também planeio núcleos de buffer para que os picos de carga de contentores individuais não afectem os vizinhos.

Métodos de medição e resolução de problemas no contexto do programador

Nunca avalio a sintonização da SFC às cegas, mas meço-a de uma forma direcionada. Utilizo-o para ter uma visão geral:

  • Comprimento da fila de espera por CPU (carga vs. núcleos activos),
  • Mudança de contexto por segundo e o número de threads,
  • Roubo de CPU e SoftIRQ-Acções,
  • Percentil dos tempos de resposta (p50/p95/p99),
  • Distribuição de vruntime ou latências de programação.

Se ocorrerem picos de latência, procuro primeiro Estrangulamento (quota esgotada), depois de Migrações (cache cold) e, finalmente, depois de Bloqueios de E/S (profundidade da fila, saturação do armazenamento). Observo os padrões de despertar: despertares curtos e freqüentes de muitos trabalhadores indicam granularidade muito fina ou E/S tagarela. Uma maior proporção de ksoftirqd num núcleo indica filas de IRQs quentes - neste caso, distribuo IRQs e ativo RPS/XPS para que a carga da rede seja distribuída mais amplamente.

Classes em tempo real, preempção e controlo de carraças

Para além da SFC, existem as seguintes classes de tempo real SCHED_FIFO/RR. Eles sobrepõem-se ao CFS: uma thread RT incorretamente configurada pode literalmente tirar o ar do sistema. Por isso, só atribuo RT-Prio de forma muito selectiva (por exemplo, para áudio/telemetria) e defino watchdogs claros. Para o alojamento, o CFS com pesos limpos é normalmente suficiente.

Para PreempçãoA escolha do modelo de preempção (por exemplo, “voluntário” vs. “preempção total/dinâmica”) altera a relação latência/rendimento. Para pilhas web eu prefiro mais preempção, para hosts puramente batch menos. Optimizações de ticks (nohz-modes) pode reduzir o jitter, mas deve ser usado com cautela. Em núcleos isolados, eu ocasionalmente combino nohz_full e Affinity para que as threads quentes funcionem o menos perturbadas possível - é importante que as cargas do sistema e IRQ não migrem inadvertidamente para estes núcleos.

Virtualização: KVM, fixação de vCPU e tempo de roubo

No ambiente do hipervisor, o agendador do anfitrião determina quando vCPUs pode funcionar. Criar sobrerreservas Tempo de roubo nos convidados, o que funciona como uma “latência invisível”. Para locatários críticos em termos de latência, eu fixo vCPUs em núcleos físicos e mantenho o overcommit moderado. Também separo os threads do emulador (threads de IO, vhost) dos núcleos quentes dos convidados para que eles não interfiram uns nos outros.

Evito a limitação dupla: se o convidado já estiver a utilizar o cpu.max, não defino quaisquer quotas rígidas adicionais no anfitrião para a mesma carga de trabalho. O controlo de frequência continua a ser a tarefa do anfitrião; os convidados beneficiam indiretamente se o regulador do anfitrião se adaptar à carga de trabalho real. Para latências uniformes, considero a estabilidade além das execuções de frequência máxima pura mais importante do que o pico de GHz no papel.

AutoNUMA, localização de memória e THP

O NUMA pode ser um ganho de desempenho ou uma armadilha para o desempenho. AutoNUMA muitas vezes ajuda, mas pode gerar sobrecarga adicional com threads de roaming intenso. Em pilhas de hospedagem com limites de serviço claros, eu fixo CPU e Memória (cpuset.cpus e cpuset.mems) em conjunto. Isto significa que os dados quentes permanecem locais e que o CFS tem de compensar com menos migrações.

Páginas grandes (THP) reduzem a pressão da TLB, mas não se adequam a todos os perfis. No caso das bases de dados, o “madvise” pode fazer mais sentido do que um “always” generalizado. O bloqueio de falhas de página atinge fortemente a latência interativa; portanto, planejo buffers (cache de página, buffer compartilhado) para que os slots CFS sejam usados produtivamente e não esperem por eventos de E/S ou MMU. Isso pode ser medido através de taxas de falha de página e curvas de falha de cache.

Caminho de rede: controlo de IRQ, RPS/XPS e sondagem de ocupado

Muitas cargas de trabalho da Web são dominadas por NICs. Eu distribuo IRQfilas de espera da placa de rede em vários núcleos e mantê-las afim para as threads de trabalho para que os despertares permaneçam locais. RPS/XPS ajuda a resolver hotspots de soft se filas RX/TX individuais estiverem carregando muita carga. Se o ksoftirqd ficar visivelmente quente, isso é uma indicação de SoftIRQs transbordando - eu então igualo os fluxos e aumento os parâmetros de orçamento, se necessário, sem perder a equidade.

O busy polling opcional pode fazer sentido em configurações muito especiais de baixa latência, mas custa tempo de CPU. Eu raramente o uso e somente se eu puder provar por medição que o p99 cai significativamente sem estressar o host em geral. Normalmente, afinidade de IRQ limpa, grupos C e granularidade CFS fornecem a melhor relação custo-benefício.

Perspectivas: Do CFS ao EEVDF e abordagens do espaço do utilizador

O EEVDF alarga a distribuição equitativa aos prazos, o que é notável mais curto e mais previsível Respostas promessas. Especialmente com alvos de latência interactiva, isto pode fazer toda a diferença. Eu mantenho-me atento às versões do kernel e testo o EEVDF separadamente antes de mudar. Ao mesmo tempo, o agendamento do espaço do utilizador através de padrões eBPF está a ganhar força, o que pode permitir um controlo adicional, dependendo da carga de trabalho. O CFS continua a ser relevante para as infra-estruturas de alojamento, mas o EEVDF vai estabelecer-se rapidamente.

Continua a ser importante um percurso de migração claro: testes, implementação em anfitriões selecionados e, em seguida, expansão. Esta é a única forma de manter os percentis e as taxas de erro sob controlo. Mantenho os parâmetros de referência próximos da realidade, incluindo fases de explosão e backends lentos. Só depois é que intervenho em ambientes reais. Desta forma, o progresso pode ser alcançado sem surpresas desagradáveis.

Brevemente resumido

O Linux Scheduler CFS oferece uma distribuição justa, integrações sólidas e boas Controlo via Grupos C. Com parâmetros sysctl adequados, afinidade limpa e quotas realistas, mantenho as latências baixas e a taxa de transferência alta. ULE, BFS ou EEVDF oferecem uma vantagem adicional para padrões especiais. Meço, comparo e implemento as alterações por fases para limitar os riscos. Isto mantém o alojamento previsível - e o desempenho no seu devido lugar.

Artigos actuais