Contêinerizar seus aplicativos Java
Este artigo fornece uma visão geral das estratégias e configurações recomendadas para conteinerizar aplicativos Java.
Ao colocar um aplicativo Java em contêineres, considere cuidadosamente quanto tempo de CPU o contêiner terá disponível. Em seguida, considere quanta memória estará disponível em termos de quantidade total de memória e o tamanho do heap da Java Virtual Machine (JVM). Em ambientes conteinerizados, os aplicativos podem ter acesso a todos os processadores e, portanto, ser capazes de executar vários threads em paralelo. É comum, no entanto, que os contêineres tenham uma cota de CPU aplicada que pode limitar o acesso às CPUs.
A JVM possui heurística para determinar o número de "processadores disponíveis" com base na cota da CPU, o que pode influenciar drasticamente o desempenho dos aplicativos Java. A memória alocada para o próprio contêiner e o tamanho da área de heap para a JVM são tão importantes quanto os processadores. Esses fatores determinarão o comportamento do coletor de lixo (GC) e o desempenho geral do sistema.
Conteinerizar um novo aplicativo
Ao colocar uma carga de trabalho Java em contêineres para um novo aplicativo, você deve levar duas coisas em consideração ao pensar em memória:
- A memória alocada para o próprio contêiner.
- A quantidade de memória disponível para o processo Java.
Entender a ergonomia padrão da JVM
Os aplicativos precisam de um ponto de partida e configurações. A JVM tem ergonomia padrão com valores predefinidos que são baseados no número de processadores disponíveis e na quantidade de memória no sistema. Os valores padrão mostrados nas tabelas a seguir são usados quando a JVM é iniciada sem sinalizadores ou parâmetros de inicialização específicos.
A tabela a seguir mostra o GC padrão usado para os recursos disponíveis:
Nos recursos disponíveis | GC padrão |
---|---|
Qualquer número de processadores Até 1791 MB de memória |
SerialGC |
2+ processadores 1792 MB ou mais de memória |
G1GC |
A tabela a seguir mostra o tamanho máximo de heap padrão, dependendo da quantidade de memória disponível no ambiente em que a JVM está sendo executada:
Memória disponível | Tamanho máximo de heap padrão |
---|---|
Até 256 MB | 50% da memória disponível |
256 MB até 512 MB | ~ 127 MB |
Mais de 512 MB | 25% da memória disponível |
O tamanho de heap inicial padrão é 1/64 da memória disponível.
Esses valores são válidos para OpenJDK 11 e posterior e para a maioria das distribuições, incluindo Microsoft Build of OpenJDK, Azul Zulu, Eclipse Temurin, Oracle OpenJDK e outras.
Determinar a memória do contêiner
Escolha uma quantidade de memória de contêiner que atenda melhor à sua carga de trabalho, dependendo das necessidades do seu aplicativo e de seus padrões de uso distintos. Por exemplo, se seu aplicativo criar gráficos de objetos grandes, você provavelmente precisará de mais memória do que precisaria para aplicativos com muitos gráficos de objetos pequenos.
Dica
Se você não sabe quanta memória alocar, um bom ponto de partida é 4 GB.
Determinar a memória heap da JVM
Ao alocar memória de heap da JVM, lembre-se de que a JVM precisa de mais memória do que apenas o que é usado para o heap da JVM. Quando você configura a memória máxima de heap da JVM, ela nunca deve ser igual à quantidade de memória do contêiner, pois isso causará erros de falta de memória (OOM) do contêiner e travamentos do contêiner.
Dica
Aloque 75% da memória do contêiner para o heap da JVM.
No OpenJDK 11 e posterior, você pode definir o tamanho do heap da JVM das seguintes maneiras:
Descrição | Sinalizador | Exemplos |
---|---|---|
Valor fixo | -Xmx |
-Xmx4g |
Valor dinâmico | -XX:MaxRAMPercentage |
-XX:MaxRAMPercentage=75 |
Tamanho mínimo/inicial do heap
Quando o ambiente tem a garantia de ter uma determinada quantidade de memória reservada para uma instância da JVM, como em um contêiner, você deve definir o tamanho mínimo do heap - ou tamanho do heap inicial - para o mesmo tamanho que o tamanho máximo do heap. Essa configuração indica à JVM que ela não deve executar a tarefa de liberar memória para o sistema operacional.
Para definir um tamanho mínimo de heap, use -Xms
para valores absolutos ou -XX:InitialRAMPercentage
para valores percentuais.
Importante
O sinalizador -XX:MinRAMPercentage
, apesar do que o nome sugere, é usado para definir a porcentagem máxima de RAM padrão para sistemas com até 256 MB de RAM disponíveis no sistema.
Determinar qual GC usar
Anteriormente, você determinava a quantidade de memória heap da JVM para começar. O próximo passo é escolher seu GC. A quantidade máxima de memória heap da JVM que você tem geralmente é um fator na escolha do seu GC. A tabela a seguir descreve as características de cada GC.
Fatores | SerialGC | GC paralelo | G1GC | ZGC | ShenandoahGC |
---|---|---|---|---|---|
Número de núcleos | 1 | 2 | 2 | 2 | 2 |
Multi-encadeado | Não | Sim | Sim | Sim | Sim |
Tamanho do heap Java | <4 GBytes | <4 GBytes | >4 GBytes | >4 GBytes | >4 GBytes |
Pausar | Sim | Sim | Sim | Sim (<1 ms) | Sim (<10 ms) |
Custos gerais indiretos | Mínimo | Mínimo | Moderado | Moderado | Moderado |
Efeito de latência de cauda | Alto | Alto | Alto | Baixo | Moderado |
Versão do JDK | Tudo | Tudo | JDK 8+ | JDK 17+ | JDK 11+ |
Mais adequado para | Pequenos montes de núcleo único | Heaps pequenos de vários núcleos ou cargas de trabalho em lote com qualquer tamanho de heap | Responsivo em heaps médios a grandes (interações de solicitação-resposta/banco de dados) | Responsivo em heaps médios a grandes (interações de solicitação-resposta/banco de dados) | Responsivo em heaps médios a grandes (interações de solicitação-resposta/banco de dados) |
Dica
Para a maioria dos aplicativos de microsserviço de uso geral, comece com o GC paralelo.
Determinar quantos núcleos de CPU são necessários
Para qualquer GC diferente do SerialGC, recomendamos dois ou mais núcleos de vCPU - ou pelo menos 2000m
cpu_limit no Kubernetes. Não recomendamos selecionar nada menos que 1 núcleo de vCPU em ambientes conteinerizados.
Dica
Se você não sabe com quantos núcleos começar, uma boa escolha são 2 núcleos de vCPU.
Escolha um ponto de partida
É recomendável começar com duas réplicas ou instâncias em ambientes de orquestração de contêiner, como Kubernetes, OpenShift, Azure Spring Apps, Aplicativos de Contêiner do Azure e Serviço de Aplicativo do Azure. A tabela a seguir resume os pontos de partida recomendados para a conteinerização de seu novo aplicativo Java.
Núcleos de vCPU | Memória de contêiner | Tamanho do heap da JVM | GC | Réplicas |
---|---|---|---|---|
2 | 4 GB | 75% | GC paralelo | 2 |
Os parâmetros da JVM a serem usados são: -XX:+UseParallelGC -XX:MaxRAMPercentage=75
Conteinerizar um aplicativo existente (local)
Se o aplicativo já estiver em execução no local ou em uma VM na nuvem, recomendamos que você comece com:
- A mesma quantidade de memória à qual o aplicativo tem acesso no momento.
- O mesmo número de CPUs (núcleos de vCPU) que o aplicativo tem disponível no momento.
- Os mesmos parâmetros da JVM que você usa atualmente.
Se a combinação de núcleos de vCPU e/ou memória de contêiner não estiver disponível, escolha a mais próxima, arredondando os núcleos de vCPU e a memória de contêiner.
Próximas etapas
Agora que você entende as recomendações gerais para conteinerizar aplicativos Java, continue no seguinte artigo para estabelecer uma linha de base de conteinerização: