Roubo: quem rouba tempo de CPU de máquinas virtuais

Roubo: quem rouba tempo de CPU de máquinas virtuais

Olá! Quero explicar de forma simples a mecânica da ocorrência de steal dentro das máquinas virtuais e alguns artefatos não óbvios que conseguimos descobrir durante sua investigação, na qual tive que me aprofundar como diretor técnico da plataforma em nuvem. Soluções em Nuvem Mail.ru. A plataforma opera em KVM.

O tempo de steal da CPU é o tempo em que a máquina virtual não recebe recursos de CPU para sua execução. Esse tempo é considerado apenas em sistemas operacionais convidados em ambientes de virtualização. As razões pelas quais esses recursos alocados desaparecem, assim como na vida, são bastante nebulosas. Mas decidimos investigar, até fizemos uma série de experimentos. Não que agora saibamos tudo sobre steal, mas temos algumas informações interessantes para compartilhar.

1. O que é steal

Então, steal é uma métrica que indica a falta de tempo de CPU para processos dentro da máquina virtual. Como descrito no patch do kernel KVM, steal é o tempo em que o hipervisor executa outros processos no sistema operacional host, enquanto colocou o processo da máquina virtual em espera para execução. Ou seja, steal é considerado como a diferença entre o tempo em que o processo está pronto para ser executado e o tempo em que lhe é alocado tempo de CPU.

A métrica steal é obtida pelo kernel da máquina virtual do hipervisor. O hipervisor, no entanto, não especifica quais outros processos ele está executando, apenas "estou ocupado, não posso dedicar tempo a você agora". No KVM, o suporte para o cálculo de steal foi adicionado em patches. Há dois pontos-chave aqui:

  • A máquina virtual descobre sobre steal através do hipervisor. Ou seja, do ponto de vista das perdas, para os processos na própria máquina virtual, é uma medição indireta, que pode estar sujeita a várias distorções.
  • O hipervisor não compartilha com a máquina virtual informações sobre o que mais está fazendo — o importante é que não está dedicando tempo a ela. Por causa disso, a própria máquina virtual não pode identificar distorções na métrica steal que poderiam ser avaliadas segundo o tipo de processos concorrentes.

2. O que afeta o steal

2.1. Cálculo do steal

Essencialmente, steal é calculado de forma semelhante ao tempo normal de utilização da CPU. Há poucas informações sobre como a utilização é calculada. Provavelmente, porque a maioria considera essa questão óbvia. Mas também existem armadilhas aqui. Para se familiarizar com esse processo, pode-se ler o artigo de Brendann Gregg: você aprenderá sobre uma série de nuances ao calcular a utilização e sobre situações em que esse cálculo pode estar incorreto por razões seguintes:

  • Superaquecimento do processador, durante o qual ciclos são perdidos.
  • Ativação/desativação do turbo boost, resultando em uma alteração na frequência do processador.
  • Mudança na duração do quanta de tempo, que ocorre ao usar tecnologias de economia de energia do processador, como o SpeedStep.
  • O problema do cálculo médio: uma avaliação de utilização de 80% ao longo de um minuto pode ocultar um pico de curto prazo de 100%.
  • O bloqueio cíclico (spin lock) resulta em uma utilização do processador, mas o processo do usuário não vê progresso em sua execução. Como resultado, a utilização calculada do processador pelo processo será de cem por cento, embora o tempo do processador não esteja sendo consumido fisicamente.

Não encontrei artigos descrevendo esse cálculo para steal (se você souber — compartilhe nos comentários). Mas, conforme as fontes, o mecanismo de cálculo é o mesmo que para utilização. Apenas no núcleo é adicionado mais um contador, diretamente para o processo KVM (do processo da máquina virtual), que conta a duração da permanência do processo KVM em estado de espera por tempo de CPU. O contador obtém informações sobre o processador de suas especificações e verifica se todos os seus ciclos foram utilizados pelo processo da máquina virtual. Se sim, consideramos que o processador estava apenas com o processo da máquina virtual. Caso contrário, informamos que o processador estava ocupado com algo mais, ocorreu steal.

O processo de cálculo do steal enfrenta os mesmos problemas que o cálculo normal de utilização. Não se pode dizer que esses problemas apareçam frequentemente, mas têm um aspecto desanimador.

2.2. Tipos de virtualização no KVM

De maneira geral, existem três tipos de virtualização, e todos são suportados pelo KVM. O tipo de virtualização pode influenciar o mecanismo de ocorrência de steal.

Transmissão. Nesse caso, a operação do sistema operacional da máquina virtual com dispositivos físicos do hipervisor ocorre aproximadamente assim:

  1. O sistema operacional convidado envia um comando para seu dispositivo convidado.
  2. O driver do dispositivo convidado recebe o comando, forma uma solicitação para a BIOS do dispositivo e a envia ao hipervisor.
  3. O processo do hipervisor traduz o comando em um comando para o dispositivo físico, tornando-o, entre outras coisas, mais seguro.
  4. O driver do dispositivo físico aceita o comando modificado e o envia ao próprio dispositivo físico.
  5. Os resultados da execução dos comandos retornam pelo mesmo caminho.

A vantagem da tradução é que ela permite emular qualquer dispositivo e não requer preparação especial do núcleo do sistema operacional. Mas isso vem à custa, principalmente, do desempenho.

Virtualização de hardware. Nesse caso, o dispositivo em nível de hardware compreende os comandos do sistema operacional. Esta é a maneira mais rápida e eficiente. Mas, infelizmente, não é suportada por todos os dispositivos físicos, hipervisores e sistemas operacionais convidados. Atualmente, os principais dispositivos que suportam virtualização de hardware são os processadores.

Paravirtualização. A opção mais comum de virtualização de dispositivos no KVM e, de forma geral, o modo de virtualização mais amplamente utilizado para sistemas operacionais convidados. A característica é que a interação com alguns subsistemas do hipervisor (por exemplo, a pilha de rede ou de disco) ou a alocação de páginas de memória é realizada utilizando a API do hipervisor, sem a tradução de comandos de baixo nível. A desvantagem desse método de virtualização é a necessidade de modificar o núcleo do sistema operacional convidado para que ele possa interagir com o hipervisor através dessa API. No entanto, isso geralmente é resolvido por meio da instalação de drivers especiais no sistema operacional convidado. No KVM, essa API é chamada virtio API.

Na paravirtualização, em comparação com a tradução, o caminho até o dispositivo físico é significativamente encurtado ao enviar comandos diretamente da máquina virtual para o processo do hipervisor no host. Isso permite acelerar a execução de todas as instruções dentro da máquina virtual. No KVM, isso é gerido pela virtio API, que funciona apenas para dispositivos específicos, como adaptadores de rede ou de disco. É por isso que drivers virtio são instalados dentro das máquinas virtuais.

O reverso desse aumento de desempenho é que nem todos os processos que ocorrem dentro da máquina virtual permanecem dentro dela. Isso cria alguns efeitos especiais que podem levar à aparição em steal. Recomendo começar um estudo mais profundo sobre este assunto com Uma API para I/O virtual: virtio.

2.3. Agendamento 'Justo'

Uma máquina virtual em um hipervisor é, na verdade, um processo comum que segue as leis de escalonamento (distribuição de recursos entre processos) no núcleo Linux, então vamos examiná-la mais detalhadamente.

No Linux, utiliza-se o chamado CFS, Completely Fair Scheduler, que desde o núcleo 2.6.23 tornou-se o escalonador padrão. Para entender melhor este algoritmo, pode-se ler o Linux Kernel Architecture ou os códigos-fonte. A essência do CFS reside na distribuição do tempo de CPU entre os processos, dependendo da duração de sua execução. Quanto mais tempo de CPU um processo exige, menos tempo ele recebe. Isso garante a execução 'justa' de todos os processos — para que um único processo não monopolize toda a CPU continuamente, permitindo que os outros processos também sejam executados.

Às vezes, essa paradigma leva a artefatos interessantes. Usuários antigos do Linux certamente lembram da paralisação de editores de texto comuns na área de trabalho durante a execução de aplicativos que consomem muitos recursos, como compiladores. Isso acontecia porque tarefas menos exigentes de aplicativos de desktop competiam com tarefas que consomem recursos ativamente, como o compilador. O CFS considera isso injusto, por isso, periodicamente, ele interrompe o editor de texto e permite que a CPU processe as tarefas do compilador. Isso foi corrigido com o mecanismo sched_autogroup, mas muitas outras peculiaridades da distribuição do tempo de CPU entre tarefas ainda permanecem. Na verdade, esta é uma narrativa não sobre como tudo é ruim no CFS, mas uma tentativa de chamar atenção para o fato de que a distribuição 'justa' do tempo de CPU não é uma tarefa trivial.

Outro ponto importante no escalonador é a preemption. Isso é necessário para remover um processo que está consumindo muitos recursos do processador e permitir que outros possam ser executados. O processo de remoção é chamado de context switching, ou troca de contexto do processador. Durante isso, todo o contexto da tarefa é preservado: o estado da pilha, os registradores e outros elementos, após o que o processo é enviado para esperar, e outro ocupa seu lugar. Essa é uma operação cara para o sistema operacional, utilizada raramente, mas essencialmente não há nada de ruim nisso. Uma troca de contexto frequente pode indicar um problema no sistema operacional, mas, normalmente, ela ocorre continuamente e não indica especificamente nada.

Essa longa explicação é necessária para clarificar um fato: quanto mais recursos do processador um processo tenta consumir em um escalonador honesto do Linux, mais rapidamente ele será interrompido para que outros processos também possam ser executados. Se isso é certo ou não é uma questão complexa, que é resolvida de formas diferentes sob diferentes cargas. No Windows, até recentemente, o escalonador era orientado para o processamento prioritário de aplicativos de desktop, o que poderia causar travamentos em processos em segundo plano. No Sun Solaris, havia cinco classes diferentes de escalonadores. Quando a virtualização foi introduzida, uma sexta classe foi adicionada, escalonador de compartilhamento justo, porque as cinco anteriores lidavam com a virtualização das Solaris Zones de forma inadequada. Recomendo começar um estudo detalhado desse assunto com livros como Solaris Internals: Solaris 10 e Arquitetura do Kernel OpenSolaris ou Understanding the Linux Kernel.

2.4. Como monitorar o steal?

Monitorar o steal dentro de uma máquina virtual, assim como qualquer outra métrica de processador, é simples: pode-se usar qualquer ferramenta de coleta de métricas de CPU. A única condição é que a virtualização esteja no Linux. O Windows, por algum motivo, não fornece essa informação aos seus usuários. 🙁

Roubo: quem rouba tempo de CPU de máquinas virtuais
Saída do comando top: detalhamento da carga na CPU, na coluna mais à direita — steal

A dificuldade surge ao tentar obter essa informação a partir do hipervisor. É possível tentar prever o steal na máquina host, por exemplo, pelo parâmetro Load Average (LA) — valor médio do número de processos esperando na fila para serem executados. A metodologia para calcular esse parâmetro não é simples, mas, de modo geral, se o LA, normalizado pelo número de threads do processador, for maior que 1, isso indica que o servidor Linux está sobrecarregado.

O que todos esses processos estão esperando? A resposta óbvia é o processador. Mas a resposta não está completamente correta, porque às vezes o processador está livre e a LA está elevada. Lembre-se, de como o NFS falha e como a LA cresce nesse caso.Assim pode acontecer também com o disco e outros dispositivos de entrada/saída. Mas, na verdade, os processos podem estar esperando pelo término de qualquer bloqueio, seja físico, relacionado ao dispositivo de entrada/saída, ou lógico, como um mutex. Isso inclui bloqueios a nível de hardware (uma resposta do disco, por exemplo) ou de lógica (os chamados primitivos de bloqueio, que incluem várias entidades, mutex adaptativos e spin, semáforos, variáveis de condição, rw locks, ipc locks…).

Outra característica da LA é que ela é considerada como uma média para o sistema operacional. Por exemplo, 100 processos competindo por um arquivo, então LA=50. Um valor tão alto poderia, à primeira vista, indicar que o sistema operacional está com problemas. Mas, para um código mal escrito, isso pode ser uma condição normal, onde apenas ele está com problemas, enquanto os outros processos no sistema operacional não sofrem.

Devido a essa média (e não menos do que um minuto), definir qualquer coisa pela métrica de LA não é uma tarefa fácil, com resultados bem indefinidos em casos específicos. Se você tentar se aprofundar, descobrirá que nos artigos da Wikipedia e em outros recursos disponíveis estão descritos apenas os casos mais simples, sem uma explicação profunda do processo. Para todos os interessados, envio, mais uma vez, para cá, para Brendann Gregg  — e siga os links. Para quem não quer ler em inglês — a tradução de seu artigo popular sobre LA.

3. Efeitos Especiais

Agora, vamos nos concentrar nos principais casos de aparecimento de steal, com os quais já nos deparamos. Vou contar como eles surgem a partir de tudo que foi dito e como se relacionam com os indicadores no hipervisor.

Reutilização. O mais simples e comum: o hipervisor está sobrecarregado. Realmente, muitas máquinas virtuais rodando, alto consumo de CPU dentro delas, grande competição, utilização por LA superior a 1 (normalizado por threads de processador). Dentro de todas as máquinas virtuais, tudo fica lento. O steal, passado pelo hipervisor, também aumenta; é necessário redistribuir a carga ou desligar alguém. No geral, tudo faz sentido e é claro.

Paravirtualização contra instâncias solitárias. No hipervisor há uma única máquina virtual, que consome uma pequena parte dele, mas gera uma grande carga de entrada/saída, por exemplo, no disco. E de algum lugar aparece um pequeno steal nela, de até 10% (como mostram alguns experimentos realizados).

Um caso interessante. O steal aqui aparece exatamente devido a bloqueios no nível dos drivers paravirtualizados. Dentro da máquina virtual, uma interrupção é criada, é processada pelo driver e vai para o hipervisor. Devido ao processamento da interrupção no hipervisor, para a máquina virtual isso parece um pedido enviado, ela está pronta para execução e espera pelo processador, mas não recebe tempo de processador. A máquina virtual pensa que esse tempo foi roubado.

Isso acontece no momento em que o buffer é enviado, ele vai para o kernel space do hipervisor, e começamos a aguardá-lo. Embora, do ponto de vista da máquina virtual, ele deva retornar imediatamente. Consequentemente, de acordo com o algoritmo de cálculo do steal, esse tempo é considerado roubado. Provavelmente, nessa situação, outros mecanismos podem estar presentes (como o processamento de outras chamadas do sistema), mas eles não devem ser muito diferentes.

Escalonador contra máquinas virtuais de alto desempenho. Quando uma máquina virtual sofre de steal mais do que as outras, isso está realmente relacionado ao escalonador. Quanto mais forte o processo carga o processador, mais rápido o escalonador o expulsará, para que outros também possam trabalhar. Se a máquina virtual consome pouco, ela quase não perceberá steal: seu processo honestamente ficou esperando, precisa de mais tempo. Se a máquina virtual gera carga máxima em todos os seus núcleos, ela é expulsa com mais frequência do processador e tentam não lhe dar muito tempo.

É ainda pior quando processos dentro da máquina virtual tentam obter mais do processador, porque não conseguem lidar com o processamento de dados. Nesse caso, o sistema operacional no hipervisor, através de otimização justa, dará cada vez menos tempo de processador. Esse processo ocorre de forma avalanche, e o steal dispara para as alturas, embora as outras máquinas virtuais possam quase não notar. E quanto mais núcleos, pior para a máquina que está passando por isso. Em resumo, máquinas virtuais de alto desempenho com muitos núcleos são as que mais sofrem.

LA baixo, mas há steal. Se o LA está em torno de 0,7 (ou seja, o hipervisor parece subcarregado), mas dentro de máquinas virtuais específicas há steal:

  • A variante já descrita acima com paravirtualização. A máquina virtual pode receber métricas indicando steal, embora o hipervisor esteja bem. De acordo com nossos experimentos, tal steal não ultrapassa 10% e não deve ter um impacto significativo no desempenho dos aplicativos dentro da máquina virtual.
  • O parâmetro LA é calculado incorretamente. Na verdade, em cada momento específico, ele é calculado corretamente, mas ao ser mediado por um minuto, o resultado fica subestimado. Por exemplo, se uma máquina virtual consome todos os seus processadores por exatamente meia minuto, o LA do hipervisor será 0,15; quatro máquinas virtuais desse tipo funcionando ao mesmo tempo darão 0,6. Entretanto, o que aconteceu por meia minuto, onde cada uma delas apresentou um steal de até 25% em LA, não pode ser recuperado.
  • Novamente, devido ao escalonador, que decidiu que alguém está consumindo demais e que essa pessoa deve esperar. Enquanto isso, eu troco o contexto, lido com as interrupções e cuido de outras questões importantes do sistema. Como resultado, algumas máquinas virtuais não veem nenhum problema, enquanto outras experimentam uma degradação significativa no desempenho.

4. Outras distorções

Há ainda milhões de razões para distorções na alocação justa do tempo de CPU em uma máquina virtual. Por exemplo, as complexidades no cálculo são trazidas pelo hyper-threading e NUMA. Eles complicam definitivamente a escolha do núcleo para a execução do processo, já que o escalonador utiliza coeficientes — pesos que tornam o cálculo ainda mais complexo na troca de contexto.

Pode haver distorções devido a tecnologias como turbo boost ou, ao contrário, modo de economia de energia, que ao calcular a utilização podem aumentar ou diminuir artificialmente a frequência ou até mesmo o tempo de quântum no servidor. A ativação do turbo boost reduz o desempenho de um thread da CPU devido ao aumento de desempenho de outro. Nesse momento, a informação sobre a frequência atual do processador não é transmitida para a máquina virtual, que acredita que seu tempo está sendo roubado (por exemplo, ela solicitava 2 GHz, mas recebeu metade disso).

Em geral, pode haver muitas razões para distorções. Em um sistema específico, você pode descobrir algo mais. É melhor começar com os livros que citei acima e coletar estatísticas do hipervisor usando utilitários como perf, sysdig, systemtap, que são dezena.

5. Conclusões

  1. Uma certa quantidade de steal pode ocorrer devido à paravirtualização, e isso pode ser considerado normal. Na internet, escreve-se que esse valor pode variar de 5 a 10%. Depende dos aplicativos dentro da máquina virtual e da carga que ela impõe em seus dispositivos físicos. Aqui é importante observar como os aplicativos se comportam dentro das máquinas virtuais.
  2. A relação entre a carga no hipervisor e o steal dentro da máquina virtual nem sempre está claramente interligada; ambas as avaliações de steal podem ser imprecisas em situações específicas com diferentes cargas.
  3. O escalonador tende a ser hostil com processos que fazem muitos pedidos. Ele tenta dar menos tempo a quem pede mais. Grandes máquinas virtuais são uma desvantagem.
  4. Um pequeno steal pode ser normal mesmo sem paravirtualização (considerando a carga dentro da máquina virtual, características das cargas vizinhas, distribuição da carga por threads e outros fatores).
  5. Se você deseja descobrir o steal em um sistema específico, é necessário investigar várias opções, coletar métricas, analisá-las cuidadosamente e pensar em como distribuir a carga de forma equitativa. A partir de quaisquer casos, podem ocorrer desvios que precisam ser confirmados experimentalmente ou analisados no depurador do núcleo.

Fonte: habr.com

Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS 🔥 Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS | ProHoster