Que tipo de carga nos servidores cria mecanismos de rede?

Ao analisar a operação do subsistema de rede dos servidores, geralmente é dada atenção a indicadores como latência, taxa de transferência do sistema e o número de pacotes que podem ser processados ​​por segundo (PPS, Pacotes por Segundo). Esses indicadores são usados ​​para entender sob qual carga máxima o computador em estudo pode funcionar. E, embora essas métricas sejam importantes e geralmente possam dizer muito sobre o sistema, elas não fornecem informações sobre o impacto do processamento de pacotes de rede nos programas em execução no servidor. Este material tem como objetivo estudar a carga criada pelos mecanismos de rede nos servidores. Em particular, falaremos sobre quanto tempo do processador a solução para problemas de rede pode "roubar" de vários processos em execução nos sistemas Linux.





Processamento de pacotes de rede no Linux


O Linux processa um número significativo de pacotes no contexto de qualquer processo executado pelo processador no momento do processamento da IRQ correspondente. O mecanismo de contabilidade do sistema atribuirá os ciclos do processador usados ​​para isso a qualquer processo em execução no momento. Isso será feito mesmo que esse processo não tenha nada a ver com o processamento de pacotes de rede. Por exemplo, uma equipe toppode indicar que um processo parece estar usando mais de 99% dos recursos do processador, mas, na verdade, 60% do tempo do processador será gasto no processamento dos pacotes. E isso significa que o próprio processo, resolvendo seus próprios problemas, utiliza apenas 40% dos recursos da CPU.

Manipulador de entradanet_rx_actiongeralmente realizado muito, muito rápido. Por exemplo, em menos de 25 μs. (Esses dados foram obtidos das medições usando o eBPF. Se você estiver interessado nos detalhes, consulte aqui net_rx_action .) O processador pode processar até 64 pacotes por instância NAPI (NIC ou RPS) antes de adiar a tarefa para outro ciclo do SoftIRQ. Um após o outro, sem interrupção, podem ser seguidos até 10 ciclos do SoftIRQ, o que leva cerca de 2 ms (você pode descobrir mais sobre isso lendo sobre __do_softirq). Se o vetor SoftIRQ, após o número máximo de ciclos ou o tempo passar, ainda tiver problemas não resolvidos, a solução desses problemas será adiada para execução no encadeamentoksoftirqdCPU específica. Quando isso acontece, o sistema se torna um pouco mais transparente no sentido de obter informações sobre a carga do processador criada pelas operações da rede (embora essa análise seja realizada com base no pressuposto de que é estudado o SoftIRQ, que está relacionado ao processamento de pacotes e não a outra coisa) .

Uma maneira de obter os indicadores acima é usar perf:

sudo perf record -a \
        -e irq:irq_handler_entry,irq:irq_handler_exit
        -e irq:softirq_entry --filter="vec == 3" \
        -e irq:softirq_exit --filter="vec == 3"  \
        -e napi:napi_poll \
        -- sleep 1

sudo perf script

Aqui está o resultado:

swapper     0 [005] 176146.491879: irq:irq_handler_entry: irq=152 name=mlx5_comp2@pci:0000:d8:00.0
swapper     0 [005] 176146.491880:  irq:irq_handler_exit: irq=152 ret=handled
swapper     0 [005] 176146.491880:     irq:softirq_entry: vec=3 [action=NET_RX]
swapper     0 [005] 176146.491942:        napi:napi_poll: napi poll on napi struct 0xffff9d3d53863e88 for device eth0 work 64 budget 64
swapper     0 [005] 176146.491943:      irq:softirq_exit: vec=3 [action=NET_RX]
swapper     0 [005] 176146.491943:     irq:softirq_entry: vec=3 [action=NET_RX]
swapper     0 [005] 176146.491971:        napi:napi_poll: napi poll on napi struct 0xffff9d3d53863e88 for device eth0 work 27 budget 64
swapper     0 [005] 176146.491971:      irq:softirq_exit: vec=3 [action=NET_RX]
swapper     0 [005] 176146.492200: irq:irq_handler_entry: irq=152 name=mlx5_comp2@pci:0000:d8:00.0

Nesse caso, o processador está ocioso (daí a aparência das entradas swapperpara o processo), o IRQ é chamado para a fila Rx na CPU 5, o processamento SoftIRQ é chamado duas vezes, 64 pacotes são processados ​​primeiro e depois 27. O próximo IRQ é chamado após 229 μs e inicia o ciclo novamente.

Esses dados foram obtidos em um sistema inativo. Mas no processador, qualquer tarefa pode ser executada. Nesse caso, a sequência de eventos acima ocorre, interrompendo esta tarefa e executando tarefas IRQ / SoftIRQ. Ao mesmo tempo, a Contabilidade do sistema atribui ao processo interrompido a carga criada pelo processador. Como resultado, as tarefas de processamento de pacotes de rede geralmente são ocultas das ferramentas convencionais de monitoramento de carga do processador. Eles são executados no contexto de algum processo selecionado aleatoriamente, no contexto do "processo da vítima". Isso nos leva a algumas perguntas. Como estimar o tempo durante o qual o processo é interrompido para o processamento de pacotes? Como comparar duas soluções de rede diferentes para entender qual delas tem um efeito menor em várias tarefas resolvidas em um computador?

Ao usar mecanismos RSS, RPS, RFS, o processamento de pacotes geralmente é distribuído entre os núcleos do processador. Portanto, a sequência de processamento de pacotes acima está relacionada a cada CPU específica. À medida que a taxa de chegada de pacotes aumenta (acho que podemos falar sobre velocidades de 100.000 pacotes por segundo ou mais), cada CPU precisa processar milhares ou dezenas de milhares de pacotes por segundo. O processamento de tantos pacotes afetará inevitavelmente outras tarefas executadas no servidor.

Considere uma maneira de avaliar esse efeito.

Desabilitando o processamento de pacotes distribuídos


Para começar, vamos parar o processamento distribuído de pacotes desativando o RPS e configurando regras de controle de fluxo destinadas a organizar o processamento de todos os pacotes relacionados a um endereço MAC específico na única CPU conhecida por nós. Meu sistema possui 2 NICs agregadas em uma configuração 802.3ad. As tarefas de rede são atribuídas a uma única máquina virtual em execução em um computador.

O RPS nos adaptadores de rede está desabilitado da seguinte maneira:

for d in eth0 eth1; do
    find /sys/class/net/${d}/queues -name rps_cpus |
    while read f; do
            echo 0 | sudo tee ${f}
    done
done

Em seguida, configuramos as regras de controle de fluxo para garantir que os pacotes entrem na máquina virtual de teste usando uma única CPU:

DMAC=12:34:de:ad:ca:fe
sudo ethtool -N eth0 flow-type ether dst ${DMAC} action 2
sudo ethtool -N eth1 flow-type ether dst ${DMAC} action 2

Desabilitar o RPS e usar regras de controle de fluxo nos permite garantir que todos os pacotes destinados à nossa máquina virtual sejam processados ​​na mesma CPU. Para garantir que os pacotes sejam enviados para a fila para a qual eles devem ser enviados, você pode usar um comando como ethq . Então você pode descobrir qual CPU essa fila pertence a usar /proc/interrupts. No meu caso, o turno 2 é processado por meio da CPU 5.

Comando de velocidade Openssl


Eu poderia usar utilitários perfou analisar os tempos de execução do SoftIRQ responsáveis ​​pelo processamento do tráfego recebido bpf, mas essa abordagem é bastante complicada. Além disso, o próprio processo de observação afeta definitivamente os resultados. Uma solução muito mais simples e compreensível é identificar a carga criada pelas operações de rede no sistema usando alguma tarefa, que cria uma carga conhecida no sistema. Por exemplo, este é um comando openssl speedusado para testar o desempenho do OpenSSL. Isso permitirá descobrir quanto recursos do processador o programa obtém na realidade e compará-lo com a quantidade de recursos que ele deve receber (isso ajudará a descobrir quanto recursos são gastos nas tarefas de rede).

A equipe é openssl speedquase 100% uma equipe de espaço do usuário. Se você o vincular a uma determinada CPU, ele, durante a execução dos testes, utilizará todos os recursos disponíveis. A equipe trabalha configurando o cronômetro para o intervalo especificado (aqui, por exemplo, para facilitar os cálculos, leva 10 segundos), executando o teste e, em seguida, quando o cronômetro é acionado, usando times()-o para descobrir quanto tempo o processador realmente conseguiu. Do ponto de vista syscall, fica assim:

alarm(10)                               = 0
times({tms_utime=0, tms_stime=0, tms_cutime=0, tms_cstime=0}) = 1726601344
--- SIGALRM {si_signo=SIGALRM, si_code=SI_KERNEL} ---
rt_sigaction(SIGALRM, ...) = 0
rt_sigreturn({mask=[]}) = 2782545353
times({tms_utime=1000, tms_stime=0, tms_cutime=0, tms_cstime=0}) = 1726602344

Ou seja, acontece que alarm()muito poucas chamadas do sistema foram feitas entre ligar e verificar os resultados. Se o programa não foi interrompido ou muito raramente, o tempo tms_utimecoincide com o tempo do teste (neste caso, 10 segundos).

Como se trata de um teste realizado exclusivamente no espaço do usuário, qualquer hora do sistema exibida times()significa alguma carga adicional no sistema. Acontece que, embora openssleste seja um processo executado na CPU, a própria CPU pode estar ocupada com outra coisa. Por exemplo, processando pacotes de rede:

alarm(10)                               = 0
times({tms_utime=0, tms_stime=0, tms_cutime=0, tms_cstime=0}) = 1726617896
--- SIGALRM {si_signo=SIGALRM, si_code=SI_KERNEL} ---
rt_sigaction(SIGALRM, ...) = 0
rt_sigreturn({mask=[]}) = 4079301579
times({tms_utime=178, tms_stime=571, tms_cutime=0, tms_cstime=0}) = 1726618896

Aqui você pode ver que foi opensslpossível trabalhar no processador por 7,49 segundos (178 + 571 em unidades de medida correspondentes a 0,01 s.). Mas, ao mesmo tempo, 5,71 s. esse intervalo é representado pela hora do sistema. Como ele opensslnão está ocupado com nenhum negócio no espaço do kernel, isso significa que 5,71 s. - Este é o resultado de uma carga adicional no sistema. Ou seja, é a hora em que o processo foi "roubado" para atender às necessidades do sistema.

Usando o comando openssl speed para detectar a carga do sistema causada por mecanismos de rede


Agora que descobrimos como a equipe funciona openssl speed, veremos os resultados que ela produz em um servidor praticamente inativo:

$ taskset -c 5 openssl speed -seconds 10 aes-256-cbc >/dev/null
Doing aes-256 cbc for 10s on 16 size blocks: 66675623 aes-256 cbc's in 9.99s
Doing aes-256 cbc for 10s on 64 size blocks: 18096647 aes-256 cbc's in 10.00s
Doing aes-256 cbc for 10s on 256 size blocks: 4607752 aes-256 cbc's in 10.00s
Doing aes-256 cbc for 10s on 1024 size blocks: 1162429 aes-256 cbc's in 10.00s
Doing aes-256 cbc for 10s on 8192 size blocks: 145251 aes-256 cbc's in 10.00s
Doing aes-256 cbc for 10s on 16384 size blocks: 72831 aes-256 cbc's in 10.00s

Como você pode ver, somos informados de que o programa passa de 9,99 a 10 segundos para processar blocos de tamanhos diferentes. Isso confirma que os mecanismos do sistema não levam tempo do processador a partir do programa. Agora, usando netperf, carregaremos o servidor processando pacotes provenientes de duas fontes. Execute o teste novamente:

$ taskset -c 5 openssl speed -seconds 10 aes-256-cbc >/dev/null
Doing aes-256 cbc for 10s on 16 size blocks: 12061658 aes-256 cbc's in 1.96s
Doing aes-256 cbc for 10s on 64 size blocks: 3457491 aes-256 cbc's in 2.10s
Doing aes-256 cbc for 10s on 256 size blocks: 893939 aes-256 cbc's in 2.01s
Doing aes-256 cbc for 10s on 1024 size blocks: 201756 aes-256 cbc's in 1.86s
Doing aes-256 cbc for 10s on 8192 size blocks: 25117 aes-256 cbc's in 1.78s
Doing aes-256 cbc for 10s on 16384 size blocks: 13859 aes-256 cbc's in 1.89s

Os resultados são muito diferentes daqueles obtidos em um servidor inativo. Espera-se que cada um dos testes seja executado em 10 segundos, mas times()informa que o tempo real de execução é de 1,78 a 2,1 segundos. Isso significa que o tempo restante, variando de 7,9 a 8,22 segundos, foi gasto no processamento dos pacotes, no contexto do processo opensslou no ksoftirqd.

Vamos dar uma olhada no que a equipe divulgará topao analisar o lançamento recém-concluído openssl speed.

PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND              P 
 8180 libvirt+  20   0 33.269g 1.649g 1.565g S 279.9  0.9  18:57.81 qemu-system-x86     75
 8374 root      20   0       0      0      0 R  99.4  0.0   2:57.97 vhost-8180          89
 1684 dahern    20   0   17112   4400   3892 R  73.6  0.0   0:09.91 openssl              5    
   38 root      20   0       0      0      0 R  26.2  0.0   0:31.86 ksoftirqd/5          5

Aqui você pode pensar que ele opensslusa aproximadamente 73% dos recursos da CPU 5, e os ksoftirqdrecursos restantes são obtidos. Mas, na realidade, no contexto openssl, o processamento de um número tão grande de pacotes é realizado que o próprio programa leva apenas 18 a 21% do tempo do processador para resolver seus problemas.

Se você reduzir a carga da rede para 1 fluxo, opensslterá a sensação de que 99% dos recursos do sistema estão sendo consumidos.

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND              P
 8180 libvirt+  20   0 33.269g 1.722g 1.637g S 325.1  0.9 166:38.12 qemu-system-x86     29
44218 dahern    20   0   17112   4488   3996 R  99.2  0.0   0:28.55 openssl              5
 8374 root      20   0       0      0      0 R  64.7  0.0  60:40.50 vhost-8180          55
   38 root      20   0       0      0      0 S   1.0  0.0   4:51.98 ksoftirqd/5          5

Mas, na realidade, verifica-se que o programa em execução no espaço do usuário obtém, nos 10 segundos esperados, apenas cerca de 4 segundos:

Doing aes-256 cbc for 10s on 16 size blocks: 26596388 aes-256 cbc's in 4.01s
Doing aes-256 cbc for 10s on 64 size blocks: 7137481 aes-256 cbc's in 4.14s
Doing aes-256 cbc for 10s on 256 size blocks: 1844565 aes-256 cbc's in 4.31s
Doing aes-256 cbc for 10s on 1024 size blocks: 472687 aes-256 cbc's in 4.28s
Doing aes-256 cbc for 10s on 8192 size blocks: 59001 aes-256 cbc's in 4.46s
Doing aes-256 cbc for 10s on 16384 size blocks: 28569 aes-256 cbc's in 4.16s

As ferramentas convencionais de monitoramento de processos indicam que o programa usa quase todos os recursos do processador, mas, na realidade, verifica-se que 55-80% dos recursos da CPU são gastos no processamento de pacotes de rede. A taxa de transferência do sistema ao mesmo tempo parece ótima (mais de 22 Gb / s por linha de 25 Gb / s), mas isso tem um tremendo impacto nos processos em execução nesse sistema.

Sumário


Aqui examinamos um exemplo de como os mecanismos de processamento de pacotes "roubam" os relógios do processador de um benchmark simples e não muito importante. Mas em um servidor real, os processos afetados de maneira semelhante podem ser qualquer coisa. Podem ser processadores virtuais, threads de emulador, threads de vhost de máquinas virtuais. Podem ser diferentes processos do sistema, cujo impacto pode ter um impacto diferente no desempenho desses processos e em todo o sistema.

Você considera, analisando seus servidores, o impacto no desempenho real da carga associada às operações de rede?


All Articles