Recentemente, mudamos nosso ambiente de produção para o Kubernetes. Eu gostaria de impor limites de CPU nos contêineres. Estou recebendo métricas de CPU conflitantes que não se encaixam. Aqui está a minha configuração:
- Agentes do DataDog em execução como um
Daemonset
- Aplicativos existentes em execução sem limites de CPU
- Os contêineres em questão são aplicativos Ruby multithread
- Duas métricas:
kubernetes.cpu.usage.{avg,max}
edocker.cpu.usage
c4.xlarge
nós de cluster (4 vCPUs ou 4000m em termos do Kubernetes)
kubernetes.cpu.usage.max
relatórios ~ 600m para os contêineres em questão. docker.cpu.usage
relatórios ~ 60%. Daqui resulta que um limite de CPU de 1000m seria capacidade mais que suficiente em operação normal.
Defino o limite para 1000m. Em seguida, docker.container.throttles
sobe significativamente, enquanto kubernetes.cpu.usage.max
e docker.cpu.usage
ficar na mesma. O sistema cai de joelhos durante esse período. Isto não faz sentido para mim.
Eu pesquisei estatísticas do Docker. Parece que docker stats
(e a API subjacente) normalizam a carga de acordo com os núcleos da CPU. Então, no meu caso, docker.cpu.usage
de 60% chega (4000m * 0,60) a 2400m em termos do Kubernetes. No entanto, isso não se correlaciona com nenhum número do Kubernetes. Fiz outro experimento para testar minha hipótese de que os números de Kubernetes estão incorretos. Defino o limite para 2600m (para algum espaço extra). Isso não resultou em nenhum acelerador. No entanto, Kubernetes observou que o uso da CPU não mudou. Isso me deixa confuso.
Então, minhas perguntas são:
- Isso parece um bug no Kubernetes (ou algo na pilha?)
- Meu entendimento está correto?
Minha pergunta de acompanhamento está relacionada a como determinar corretamente a CPU para aplicativos Ruby. Um contêiner usa Puma. Este é um servidor da web com vários threads com uma quantidade configurável de threads. Solicitações HTTP são tratadas por um dos threads. O segundo aplicativo é um servidor econômico, usando o servidor encadeado. Cada conexão TCP de entrada é tratada por seu próprio encadeamento. O encadeamento sai quando a conexão é fechada. Ruby como GIL (Global Interpreter Lock), para que apenas um encadeamento possa executar o código Ruby por vez. Isso permite que vários threads executem E / S e coisas assim.
Eu acho que a melhor abordagem é limitar o número de threads em execução em cada aplicativo e aproximar os limites da CPU do Kubernetes com base no número de threads. Os processos não são bifurcados, portanto, o uso total da CPU é mais difícil de prever.
A questão aqui é: como prever adequadamente o uso e os limites da CPU para esses aplicativos?
fonte
Respostas:
Várias coisas aqui:
Você está no AWS ec2; portanto, o que você estiver fazendo em sua instância para medir a CPU está calculando a CPU no nível do hipervisor e não na instância. Para isso, execute qualquer teste de carga e verifique o iostat -ct 1 e o uso da CPU no cloudwatch. O uso da CPU no cloudwatch é sempre 10 a 20% maior do que o que o iostat relatará e isso ocorre porque o iostat fornecerá o uso da CPU no nível do hipervisor.
Como docker para ver como o kubernetes e as métricas do docker se comparam, sugiro executar os contêineres com --cpuset = 1 ou qualquer número para permitir que todos os contêineres usem apenas uma única vCPU.
Também na AWS 1 CPU = 2vcpu. É hyperthreaded para 2. Você pode levar isso em consideração durante o cálculo.
Finalmente, a melhor métrica a ser usada para ver o uso da CPU para um aplicativo específico é usar htop e correlacionar-se com suas métricas do cloudwatch.
Também observei algumas vezes que o daemon do docker se fixa a uma das CPUs virtuais e, portanto, quando você o reduz a 1000m, talvez toda a configuração esteja ficando lenta porque a redução está acontecendo em qualquer um dos vpcus. Você pode usar o mpstat para obter detalhes sobre isso.
Por fim, no nível do host, você pode fixar o docker em uma única CPU e observar mais.
Espero que isso te aproxime um pouco. Atualize-me se você já encontrou uma solução.
fonte