Problemas comuns com o desempenho de contêineres e como resolvê-los

Por Setembro 13, 2018

Os contêineres permitem uma abordagem de DevOps robusta, permitindo que os desenvolvedores montem um software em contêineres de fácil implementação e desempenho consistente em ambientes de desenvolvimento e produção.

Pequenos e leves, os contêineres usam menos recursos que os hosts virtuais. Eles iniciam, param e migram rapidamente entre os servidores e ajudam a dividir aplicativos monolíticos em componentes menores, em uma arquitetura de microsserviços.

Porém, os contêineres também podem apresentar novos desafios, como aqueles relacionados ao desempenho. Neste artigo, analiso alguns problemas que tive no desempenho de contêineres e explico como resolvê-los ou evitá-los.

Os contêineres são como “caixas pretas”

Os contêineres levam ao extremo o desenvolvimento e o teste de caixa preta e tendem a ser subestimados em revisões de códigos, no monitoramento de componentes internos e até mesmo em revisões de design de nível superior. Cada etapa fornece uma oportunidade para garantir o desempenho necessário (isto é, os acordos de nível de serviço, ou SLAs), identificar possíveis problemas e gargalos de desempenho o mais rápido possível e melhorar o desempenho antes da implementação dos contêineres.

A solução é, sobretudo, cultural e está relacionada ao processo. Mesmo que um contêiner incorpore alguma área da funcionalidade de produção, não deixe de submetê-la a revisões de design e códigos ao longo de todo o processo.

Testes de estresse subestimados

Os contêineres permitem a migração e identificação de servidor com facilidade, rapidez e dinamismo, expandindo de forma flexível para atender às necessidades do usuário e do sistema conforme a demanda, geralmente em ambientes na nuvem que também oferecem suporte a esse recurso. Isso pode fazer com que desenvolvedores e até mesmo gerentes de QA acreditem que o teste de estresse não é necessário – a ferramenta de gerenciamento de contêineres simplesmente gera instâncias adicionais. Um “Consequência – ou – Resultado” é a falta de testes em diferentes ambientes, seja entre provedores ou entre diferentes soluções de um provedor ou datacenter.

Novamente, a solução é, sobretudo, cultural e relacionada ao processo: use as ferramentas para simular um grande número de usuários tanto em cenários aleatórios quanto definidos com padrões de uso. Não se esqueça de considerar além dos valores médios, pois eles encobrem os valores atípicos. Existem ferramentas, como o Blazemeter (Figura 1), que fazem um excelente mapeamento de médias, criando histogramas mapeados para mostrar a carga ao longo do tempo. Essas ferramentas se tornam ainda mais valiosas quando integradas a ferramentas de gerenciamento de desempenho de aplicativos (APM – Application Performance Management), que fornecem os resultados de teste de carga e desempenho e permitem a verificação e a análise detalhadas.

Além disso, os testes de desempenho devem ser realizados para todas as versões continuamente. Também é interessante executar combinações aleatórias de testes de desempenho, não apenas testes individuais. A interferência e a limitação de algum recurso podem ajudar a simular eventos simultâneos aleatórios que ocorrem na realidade.

Problemas relacionados ao tamanho do contêiner

O tamanho do contêiner pode afetar negativamente a migração e a geração de instâncias de contêiner. Testes precisos podem ajudar a identificar quais contêineres precisam ser desmembrados para evitar perdas. Além disso, existem ferramentas e técnicas que ajudam a tornar o processo de migração de instância de contêiner mais eficiente, como as técnicas de transferência e paginação de memória.

Falta de desenvolvimento de contêiner padronizado

As várias estruturas de contêineres e microsserviços são afetadas de formas diferentes em termos de desempenho. Os ambientes de host do contêiner, a capacidade de RAM física, a infraestrutura de rede, o sistema operacional da plataforma e a arquitetura dos ambientes na nuvem que afetam o desempenho da estrutura variam muito. Sempre que possível, use estruturas e ferramentas de contêineres consistentes. A agregação de log, por exemplo, é importante quando cada um dos componentes de contêineres separados registra mensagens que fazem parte de uma transação de aplicativos. A padronização de apenas uma estrutura de log com suporte a contêineres aumentará a confiabilidade e a transparência.

Como lidar com a interferência

Esse problema está relacionado principalmente a implementações de “contêiner como serviço” (CaaS – Container as a Service) em ambiente na nuvem pública, em que os servidores host subjacentes e até a infraestrutura de rede são compartilhados em um ambiente de multi-tenancy, isto é, com vários elementos. Não há muito que fazer para se proteger disso, no caso de serviços ou aplicativos baseados em contêiner particularmente sensíveis, então a solução pode ser evitar um provedor de nuvem específico ou negociar um SLA para limitar ou evitar esse problema.

O monitoramento e os testes realizados durante o desenvolvimento com ferramentas que simulam interferência e carregamento do host alertam sobre o impacto desse possível problema. Da mesma forma, a implementação de ferramentas de monitoramento de contêiner para capturar interferência ruidosa de outros componentes garante o serviço adequado do seu provedor.

Limitações de recursos do host

Ao se aprofundar no monitoramento de implementações de host de contêiner, você precisa estar ciente da atividade e da carga nos servidores de host subjacentes, independente da implementação. Eu já vi um impacto negativo quando o host subjacente do contêiner apresenta limitação de recursos. Embora uma abordagem baseada em contêiner possa ser mais leve do que a virtualização por si só, a execução de uma grande quantidade de contêineres geralmente sobrecarrega sua infraestrutura mais do que a execução de um aplicativo monolítico.

Por exemplo, geralmente convém dividir processos e serviços individuais em contêineres separados. Sua implementação em um conjunto limitado de servidores físicos, com agentes de balanceamento de carga associados, serviços de monitoramento e serviços de failover, resulta em maior uso e demanda dos recursos do host.

Para resolver ou evitar isso, use soluções de apenas um fornecedor para o armazenamento em cluster e monitoramento de contêineres e serviços para os vários contêineres, como o registro em log. Também é necessário adotar monitoramento que leve em conta as estatísticas do host e do sistema, como uso da CPU do host, métricas de I/O, memória disponível, largura de banda da rede e até detalhes da arquitetura interna do sistema operacional. Não deixe isso para quando seu aplicativo estiver em produção para confiar somente no monitoramento, pois será tarde demais. Em vez disso, use ferramentas como JMeter e Selenium (ou outras específicas para a sua plataforma) para simular a carga real com precisão.

Teste de interações do contêiner

Indo um pouco além, eu notei que muitos problemas de desempenho envolvem a falta de uma visão holística do desempenho e, em vez disso, o desempenho é considerado em contêineres individuais. As estratégias de teste devem começar com recursos baseados em contêiner e testes de carga (e monitoramento de produção) e também considerar a visão do usuário do sistema. Por exemplo, os problemas geralmente surgem quando os contêineres e os sistemas interagem, e somente os testes de ponta a ponta mostram isso.

Veja esse caso, um aplicativo em que trabalhei usava muitos contêineres, mas dois deles interagiam de uma maneira que afetava o desempenho, algo difícil de descobrir sem os testes adequados. Nesse exemplo, os dois contêineres usavam recursos baseados em sistema de arquivos e, embora cada um deles trabalhasse com arquivos diferentes e de formas diferentes, os efeitos causados por um deles no kernel do sistema operacional afetavam o outro.

Isso mostra que o teste de isolamento de contêineres não é suficiente. É fundamental executar a análise de causa raiz no caso de problemas de desempenho que afetam todo o aplicativo. Também é necessário realizar testes de interações para saber o que está acontecendo em todas as camadas da sua implementação. Além disso, ao aplicar a análise de causa raiz, a criação de imagens Docker geralmente atrapalha a identificação da origem do problema. Para resolver isso, evite criar imagens de contêineres em execução ou o uso das dependências mais recentes. Use versões específicas da camada de contêiner e gere imagens usando uma fonte de contêiner conhecida.

Demandas da rede relacionadas ao contêiner

A grande escala de contêineres em grandes implementações coloca muito estresse na rede. Os aplicativos baseados em contêiner tendem a ser mais distribuídos, dependendo mais do desempenho da rede em um ambiente virtualizado ou de SDN. Ignorar o desempenho da rede (e se concentrar apenas no desempenho do aplicativo e do banco de dados, por exemplo) pode se tornar rapidamente um grande problema.

Os desenvolvedores geralmente criam e testam os contêineres em seu ambiente local com rede em ponte ou baseada em NAT. Isso funciona bem, mas pode encobrir problemas de desempenho que surgem quando usados em datacenters de produção ou em implementações baseadas na nuvem. A solução é incluir testes de integração contínua que simulem com precisão o ambiente de produção (a propósito, este é um princípio básico do DevOps) para descobrir problemas de comportamento e desempenho de rede e descobrir complexidades na atividade de rede que dificultarão o monitoramento.

Use uma ferramenta que permite gerar tráfego de rede real para testar os efeitos em diferentes configurações de rede antes da implementação. Por exemplo, você pode testar diferentes técnicas de ligação de rede, algoritmos de balanceamento de carga, configurações de cluster e a comunicação entre contêineres.

APIs legadas

Com relação ao aspecto apresentado acima, os aplicativos baseados em contêiner geralmente são originados a partir de aplicativos monolíticos ou corporativos já existentes. Por isso, as APIs internas podem não ser ideais para uso entre contêineres que abrangem toda a rede. O resultado disso pode ser acesso remoto ineficiente, deslocamentos excessivos ou dados transmitidos em formatos não otimizados. Além das ineficiências de dados e de rede, o processamento interno do contêiner pode não ter sido construído para acesso remoto, resultando em alta utilização dos recursos.

Por exemplo, as requisições assíncronas de API, quando estendidas entre contêineres, podem resultar em um grande número de threads gerados para REST ou outra comunicação. Os padrões N+1 geralmente usados para redundância também podem causar problemas de desempenho da rede com os contêineres, que se estendem aos bancos de dados, dispositivos REST, serviços da Web, armazenamento em cache, e outros itens.

Conclusão: Uma estratégia de desempenho de contêiner

Uma boa estratégia de desempenho de contêiner começa com as especificações do seu aplicativo e as garantias de SLA do usuário, que devem incluir processos, como revisões de definição de código e contêiner, testes específicos do contêiner e testes de ponta a ponta de estresse/carga do usuário, além de métricas de sistema operacional, rede e hardware. As estratégias de expansão de desempenho no futuro também incluem suporte de hardware, integração contínua e testes, além de automação de versões e modelagem de tempo de execução de contêiner para ajudar a prever problemas de desempenho antes de chegar aos usuários.

Independente do seu aplicativo, processo de desenvolvimento ou estratégia de teste de desempenho, parcerias com os fornecedores e o uso das ferramentas certas (como as mencionadas aqui) ajudam muito para atender aos requisitos de desempenho do contêiner.

Eric Bruno é autor e editor de várias publicações online, com mais de 20 anos de experiência na área de tecnologia da informação. Ele é moderador e palestrante muito requisitado para uma grande variedade de conferências e outros eventos sobre tecnologias que variam desde desktop até datacenter. Há mais de dez anos, escreve artigos, textos para blogs, white papers e livros sobre arquitetura e desenvolvimento de software. Ele também é arquiteto corporativo, desenvolvedor e analista do setor com experiência em ciclo de vida completo, arquitetura, design e desenvolvimento de software de grande escala para empresas do mundo inteiro. Seu trabalho inclui o desenvolvimento de sistemas altamente distribuídos, desenvolvimentos para web em várias camadas, desenvolvimentos em tempo real e o desenvolvimento de software transacional. Veja o seu trabalho editorial online em www.ericbruno.com.