Roubar: quem rouba tempo de processador de máquinas virtuais

Roubar: quem rouba tempo de processador de máquinas virtuais

Olá! Quero falar em termos simples sobre a mecânica do roubo dentro de máquinas virtuais e sobre alguns artefatos não óbvios que conseguimos descobrir durante sua pesquisa, nos quais tive que mergulhar como diretor técnico de uma plataforma em nuvem Soluções em nuvem Mail.ru. A plataforma funciona em KVM.

O tempo de roubo de CPU é o tempo durante o qual a máquina virtual não recebe recursos do processador para sua execução. Esse tempo só é contabilizado em sistemas operacionais convidados em ambientes de virtualização. As razões para onde vão esses recursos mais alocados, como na vida, são muito vagas. Mas decidimos descobrir e até realizamos vários experimentos. Não é que agora saibamos tudo sobre roubo, mas vamos contar algo interessante agora.

1. O que é roubar

Portanto, roubo é uma métrica que indica falta de tempo de processador para processos dentro de uma máquina virtual. Conforme descrito no patch do kernel KVMStealth é o tempo durante o qual o hipervisor está executando outros processos no sistema operacional host, mesmo que tenha enfileirado o processo da máquina virtual para execução. Ou seja, o roubo é calculado como a diferença entre o tempo em que o processo está pronto para ser executado e o tempo em que o processo recebe tempo de processador.

O kernel da máquina virtual recebe a métrica de roubo do hipervisor. Ao mesmo tempo, o hipervisor não especifica exatamente quais outros processos está executando, simplesmente diz “enquanto estou ocupado, não posso lhe dar tempo”. No KVM, o suporte para cálculo de roubo foi adicionado ao patches. Existem dois pontos principais aqui:

  • A máquina virtual aprende sobre roubo do hipervisor. Ou seja, do ponto de vista das perdas, para processos na própria máquina virtual esta é uma medida indireta que pode estar sujeita a diversas distorções.
  • O hipervisor não compartilha com a máquina virtual informações sobre o que mais ela está fazendo - o principal é que não dedica tempo a isso. Por causa disso, a própria máquina virtual não consegue detectar distorções no indicador de roubo, que poderiam ser avaliadas pela natureza dos processos concorrentes.

2. O que afeta o roubo

2.1. Cálculo de roubo

Essencialmente, o roubo é calculado aproximadamente da mesma forma que o tempo normal de utilização da CPU. Não há muita informação sobre como a reciclagem é considerada. Provavelmente porque a maioria das pessoas considera esta questão óbvia. Mas também existem armadilhas aqui. Para se familiarizar com este processo, você pode ler artigo de Brendan Gregg: você aprenderá muitas nuances ao calcular a utilização e sobre situações em que esse cálculo será errôneo pelos seguintes motivos:

  • O processador superaquece, fazendo com que os ciclos sejam interrompidos.
  • Ativa/desativa o turbo boost, que altera a frequência do clock do processador.
  • Uma alteração na duração do intervalo de tempo que ocorre ao usar tecnologias de economia de energia do processador, como SpeedStep.
  • O problema com o cálculo da média: estimar a utilização de um minuto em 80% pode ocultar um pico de 100% a curto prazo.
  • Um spin lock faz com que o processador seja recuperado, mas o processo do usuário não vê nenhum progresso em sua execução. Como resultado, a utilização calculada do processador pelo processo será de cem por cento, embora o processo não consuma fisicamente o tempo do processador.

Não encontrei um artigo descrevendo um cálculo semelhante para roubo (se você souber, compartilhe nos comentários). Mas, a julgar pelo código-fonte, o mecanismo de cálculo é o mesmo da reciclagem. Simplesmente, outro contador é adicionado no kernel, diretamente para o processo KVM (processo de máquina virtual), que conta a duração do processo KVM aguardando o tempo de CPU. O contador obtém informações sobre o processador a partir de sua especificação e verifica se todos os seus ticks são utilizados pelo processo da máquina virtual. Se isso for tudo, presumimos que o processador estava ocupado apenas com o processo da máquina virtual. Caso contrário, informamos que o processador estava fazendo outra coisa, apareceu roubo.

O processo de contagem de roubos está sujeito aos mesmos problemas que a contagem regular de reciclagem. Não quer dizer que esses problemas apareçam com frequência, mas parecem desanimadores.

2.2. Tipos de virtualização em KVM

Em termos gerais, existem três tipos de virtualização, todos suportados pelo KVM. O mecanismo de ocorrência do roubo pode depender do tipo de virtualização.

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

  1. O sistema operacional convidado envia um comando para seu dispositivo convidado.
  2. O driver de dispositivo convidado recebe o comando, gera uma solicitação para o BIOS do dispositivo e a envia ao hipervisor.
  3. O processo do hipervisor traduz comando em 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 para o próprio dispositivo físico.
  5. Os resultados da execução de comandos voltam pelo mesmo caminho.

A vantagem da tradução é que ela permite emular qualquer dispositivo e não requer preparação especial do kernel do sistema operacional. Mas é preciso pagar por isso, antes de tudo, em velocidade.

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

Paravirtualização. A opção mais comum para virtualização de dispositivos em KVM e geralmente o modo de virtualização mais comum para sistemas operacionais convidados. Sua peculiaridade é que o trabalho com alguns subsistemas do hipervisor (por exemplo, com a rede ou pilha de discos) ou a alocação de páginas de memória ocorre por meio da API do hipervisor, sem tradução de comandos de baixo nível. A desvantagem deste método de virtualização é que o kernel do sistema operacional convidado deve ser modificado para que possa se comunicar com o hipervisor usando esta API. Mas isso geralmente é resolvido com a instalação de drivers especiais no sistema operacional convidado. No KVM esta API é chamada API virtio.

Com a paravirtualização, em comparação com a transmissão, o caminho para o dispositivo físico é significativamente reduzido, enviando 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 é feito pela API virtio, que funciona apenas para determinados dispositivos, como adaptadores de rede ou de disco. É por isso que os drivers virtio são instalados dentro de máquinas virtuais.

A desvantagem dessa aceleração é que nem todos os processos executados dentro da máquina virtual permanecem dentro dela. Isso cria alguns efeitos especiais que podem resultar na geração de roubo. Recomendo iniciar um estudo detalhado deste assunto com Uma API para E/S virtual: virtio.

2.3. Agendamento "justo"

Uma máquina virtual em um hipervisor é, na verdade, um processo comum que obedece às leis de escalonamento (distribuição de recursos entre processos) no kernel do Linux, então vamos dar uma olhada nisso.

O Linux usa o chamado CFS, Completely Fair Scheduler, que se tornou o agendador padrão desde o kernel 2.6.23. Para entender esse algoritmo, você pode ler a Arquitetura do Kernel Linux ou o código-fonte. A essência do CFS é distribuir o tempo do processador entre os processos dependendo da duração de sua execução. Quanto mais tempo de CPU um processo requer, menos tempo de CPU ele recebe. Isso garante que todos os processos sejam executados de forma "justa" - de modo que um processo não ocupe constantemente todos os processadores e outros processos também possam ser executados.

Às vezes, esse paradigma leva a artefatos interessantes. Os usuários antigos do Linux provavelmente se lembram do congelamento de um editor de texto normal em um desktop durante a execução de aplicativos que consomem muitos recursos, como um compilador. Isso aconteceu porque as tarefas que não consumiam muitos recursos em aplicativos de desktop competiam com tarefas que consumiam muitos recursos, como o compilador. O CFS acha que isso é injusto, por isso interrompe periodicamente o editor de texto e permite que o processador cuide das tarefas do compilador. Isso foi corrigido usando um mecanismo sched_autogroup, mas muitos outros recursos de distribuição do tempo do processador entre as tarefas permaneceram. Na verdade, esta não é uma história sobre como tudo está ruim no CFS, mas uma tentativa de chamar a atenção para o fato de que a distribuição “justa” do tempo do processador não é a tarefa mais trivial.

Outro ponto importante no escalonador é a preempção. Isso é necessário para eliminar o processo de riso do processador e permitir que outros trabalhem. O processo de ejeção é chamado de troca de contexto. Nesse caso, todo o contexto da tarefa é preservado: o estado da pilha, dos registradores, etc., após o qual o processo é enviado para espera e outro toma o seu lugar. Esta é uma operação cara para o sistema operacional e raramente usada, mas não há nada de inerentemente errado com ela. A troca frequente de contexto pode indicar um problema no sistema operacional, mas geralmente é contínua e não indica nada em particular.

Uma história tão longa é necessária para explicar um fato: quanto mais recursos do processador um processo tentar consumir em um escalonador Linux honesto, mais rápido ele será interrompido para que outros processos também possam funcionar. Se isso está correto ou não, é uma questão complexa que pode ser resolvida de maneira diferente sob diferentes cargas. No Windows, até recentemente, o agendador se concentrava no processamento prioritário de aplicativos de desktop, o que poderia causar o congelamento de processos em segundo plano. Sun Solaris tinha cinco classes diferentes de agendadores. Quando lançamos a virtualização, adicionamos uma sexta, Agendador de compartilhamento justo, porque os cinco anteriores não funcionaram adequadamente com a virtualização do Solaris Zones. Recomendo iniciar um estudo detalhado deste assunto com livros como Internos do Solaris: Solaris 10 e arquitetura OpenSolaris Kernel ou Compreendendo o kernel do Linux.

2.4. Como monitorar o roubo?

Monitorar o roubo dentro de uma máquina virtual, como qualquer outra métrica de processador, é simples: você pode usar qualquer ferramenta de métrica de processador. O principal é que a máquina virtual esteja no Linux. Por algum motivo, o Windows não fornece essas informações aos seus usuários. 🙁

Roubar: quem rouba tempo de processador de máquinas virtuais
Saída do comando superior: detalhes da carga do processador, na coluna mais à direita - roubar

A dificuldade surge ao tentar obter essas informações do hipervisor. Você pode tentar prever o roubo na máquina host, por exemplo, usando o parâmetro Load Average (LA) - o valor médio do número de processos aguardando na fila de execução. O método de cálculo deste parâmetro não é simples, mas em geral, se o LA normalizado pelo número de threads do processador for maior que 1, isso indica que o servidor Linux está sobrecarregado com alguma coisa.

O que todos esses processos estão esperando? A resposta óbvia é o processador. Mas a resposta não está totalmente correta, porque às vezes o processador é gratuito, mas o LA sai da escala. Lembrar como o NFS cai e como LA cresce. O mesmo pode acontecer com um disco e outros dispositivos de entrada/saída. Mas, na verdade, os processos podem esperar pelo fim de qualquer bloqueio, seja físico, associado a um dispositivo de E/S, ou lógico, como um mutex. Isso também inclui bloqueio no nível de hardware (a mesma resposta do disco) ou lógica (as chamadas primitivas de bloqueio, que incluem um monte de entidades, mutex adaptativo e spin, semáforos, variáveis ​​​​de condição, bloqueios rw, bloqueios ipc ...).

Outra característica do LA é que ele é considerado uma média do sistema operacional. Por exemplo, 100 processos estão competindo por um arquivo e então LA=50. Um valor tão grande parece indicar que o sistema operacional está ruim. Mas para outros códigos escritos de maneira torta, esse pode ser um estado normal, apesar do fato de que apenas ele é ruim e outros processos no sistema operacional não sofrem.

Devido a esta média (e em não menos de um minuto), determinar qualquer coisa pelo indicador LA não é a tarefa mais gratificante, com resultados muito incertos em casos específicos. Se você tentar descobrir, descobrirá que os artigos da Wikipedia e de outros recursos disponíveis descrevem apenas os casos mais simples, sem uma explicação profunda do processo. Envio a todos os interessados, novamente, aqui para Brendan Gregg  - siga os links abaixo. Quem tem preguiça de falar inglês - tradução de seu popular artigo sobre LA.

3. Efeitos especiais

Vejamos agora os principais casos de roubo que encontramos. Vou lhe contar como eles decorrem de todos os itens acima e como se relacionam com os indicadores no hipervisor.

Reciclando. O mais simples e comum: o hipervisor foi reaproveitado. Na verdade, há muitas máquinas virtuais em execução, alto consumo de processador dentro delas, muita competição, a utilização do LA é maior que 1 (normalizada pelos threads do processador). Tudo dentro de todas as máquinas virtuais fica mais lento. O roubo transmitido do hipervisor também está crescendo, é preciso redistribuir a carga ou desligar alguém. Em geral, tudo é lógico e compreensível.

Paravirtualização vs. Instâncias Únicas. Há apenas uma máquina virtual no hipervisor; ela consome uma pequena parte dela, mas produz uma grande carga de E/S, por exemplo, em disco. E de algum lugar aparece um pequeno roubo, de até 10% (como mostrado por vários experimentos).

O caso é interessante. O roubo aparece aqui justamente por causa do bloqueio no nível dos drivers paravirtualizados. Uma interrupção é criada dentro da máquina virtual, processada pelo driver e enviada ao hipervisor. Devido ao tratamento de interrupções no hipervisor, para a máquina virtual parece uma solicitação enviada, está pronta para execução e aguardando o processador, mas não recebe tempo de processador. A garota virtual pensa que esse tempo foi roubado.

Isso acontece no momento em que o buffer é enviado, ele vai para o espaço do kernel do hipervisor e começamos a esperar por ele. Embora, do ponto de vista da máquina virtual, ele deva retornar imediatamente. Portanto, de acordo com o algoritmo de cálculo de roubo, esse tempo é considerado roubado. Muito provavelmente, nesta situação pode haver outros mecanismos (por exemplo, processar algumas outras chamadas de sistema), mas eles não devem ser muito diferentes.

Agendador versus máquinas virtuais altamente carregadas. Quando uma máquina virtual sofre roubos mais que outras, isso se deve ao escalonador. Quanto mais um processo carrega o processador, mais cedo o escalonador o expulsará para que os demais também possam trabalhar. Se a máquina virtual consumir pouco, dificilmente verá roubo: seu processo honestamente sentou e esperou, precisamos dar mais tempo. Se uma máquina virtual produz a carga máxima em todos os seus núcleos, ela geralmente é expulsa do processador e eles tentam não dedicar muito tempo.

É ainda pior quando os processos dentro da máquina virtual tentam obter mais processador porque não conseguem lidar com o processamento de dados. Assim, o sistema operacional no hipervisor, devido à otimização honesta, fornecerá cada vez menos tempo de processador. Este processo ocorre como uma avalanche e rouba saltos para o céu, embora outras máquinas virtuais dificilmente percebam. E quanto mais núcleos, pior será a máquina afetada. Resumindo, as máquinas virtuais altamente carregadas e com muitos núcleos são as que mais sofrem.

Baixa LA, mas há roubo. Se LA for aproximadamente 0,7 (ou seja, o hipervisor parece estar sobrecarregado), mas o roubo é observado dentro de máquinas virtuais individuais:

  • A opção com paravirtualização já descrita acima. A máquina virtual pode receber métricas indicando roubo, embora o hipervisor esteja funcionando bem. De acordo com os resultados dos nossos experimentos, essa opção de roubo não ultrapassa 10% e não deve ter impacto significativo no desempenho dos aplicativos dentro da máquina virtual.
  • O parâmetro LA foi calculado incorretamente. Mais precisamente, em cada momento específico é calculado corretamente, mas quando calculado a média de um minuto acaba sendo subestimado. Por exemplo, se uma máquina virtual por terço do hipervisor consumir todos os seus processadores por exatamente meio minuto, então o LA por minuto no hipervisor será de 0,15; quatro dessas máquinas virtuais trabalhando simultaneamente darão 0,6. E o fato de que durante meio minuto em cada um deles houve um roubo selvagem de 25% de acordo com o indicador LA não pode mais ser retirado.
  • Novamente, por causa do agendador que decidiu que alguém estava comendo demais e deixou esse alguém esperar. Enquanto isso, mudarei o contexto, tratarei das interrupções e cuidarei de outras coisas importantes do sistema. Como resultado, algumas máquinas virtuais não apresentam problemas, enquanto outras apresentam grave degradação de desempenho.

4. Outras distorções

Existem mais um milhão de razões para distorcer o retorno justo do tempo do processador em uma máquina virtual. Por exemplo, hyperthreading e NUMA introduzem dificuldades nos cálculos. Confundem completamente a escolha do kernel para execução do processo, pois o escalonador utiliza coeficientes - pesos, que dificultam ainda mais o cálculo na troca de contexto.

Existem distorções devido a tecnologias como turbo boost ou, inversamente, modo de economia de energia, que, no cálculo da utilização, podem aumentar ou diminuir artificialmente a frequência ou mesmo o intervalo de tempo no servidor. A ativação do turbo boost reduz o desempenho de um thread do processador devido a um aumento no desempenho de outro. Neste momento, a informação sobre a frequência atual do processador não é transmitida para a máquina virtual, e ela acredita que alguém está roubando seu tempo (por exemplo, solicitou 2 GHz, mas recebeu metade disso).

Em geral, pode haver muitos motivos para distorção. Você pode encontrar algo mais em um sistema específico. É melhor começar com os livros para os quais forneci links acima e recuperar estatísticas do hipervisor usando utilitários como perf, sysdig, systemtap, dos quais dezenas.

5. Conclusões

  1. Algum roubo pode ocorrer devido à paravirtualização e isso pode ser considerado normal. Escrevem na internet que esse valor pode ser de 5 a 10%. Depende dos aplicativos dentro da máquina virtual e da carga que ela coloca em seus dispositivos físicos. Aqui é importante prestar atenção em como os aplicativos se comportam dentro das máquinas virtuais.
  2. A proporção entre a carga no hipervisor e o roubo dentro da máquina virtual nem sempre está claramente inter-relacionada; ambas as estimativas de roubo podem estar erradas em situações específicas sob cargas diferentes.
  3. O escalonador tem uma atitude ruim em relação a processos que exigem muito. Ele tenta dar menos a quem pede mais. Grandes máquinas virtuais são más.
  4. Um pequeno roubo pode ser a norma mesmo sem paravirtualização (levando em consideração a carga dentro da máquina virtual, as características da carga dos vizinhos, a distribuição da carga entre threads e outros fatores).
  5. Se você quiser descobrir o roubo em um sistema específico, terá que explorar várias opções, coletar métricas, analisá-las cuidadosamente e pensar em como distribuir uniformemente a carga. São possíveis desvios de qualquer caso, que devem ser confirmados experimentalmente ou observados no depurador do kernel.

Fonte: habr.com

Adicionar um comentário