Nosso cluster atual do Kubernetes em execução no Google Container Engine (GKE) consiste em n1-standard-1
tipos de máquinas (1 CPU virtual e 3,75 GB de memória) com Debian GNU / Linux 7.9 (wheezy), mantido pelo Google. Devido ao aumento de carga e uso de memória de nossos serviços, precisamos atualizar nossos nós para um tipo de máquina maior. Enquanto tentamos isso em nosso cluster de teste, experimentamos algo que (para nós) parece bastante estranho.
A memória consumida pelo aplicativo JVM quando implantada em um nó do Google parece ser proporcional ao número de núcleos disponíveis no nó. Mesmo se definirmos a memória máxima da JVM (Xmx) para 128Mb, ela consome cerca de 250Mb em uma máquina de 1 núcleo (isso é compreensível, pois a JVM consome mais memória do que o limite máximo devido ao GC, a própria JVM etc.), mas consome cerca de 700Mb em uma máquina de 2 núcleos ( n1-standard-2
) e cerca de 1,4Gb em uma máquina de 4 núcleos ( n1-standard-4
). A única coisa diferente é o tipo de máquina, a mesma imagem e configurações do Docker são usadas.
Por exemplo, se eu usar o SSH em uma máquina usando um tipo de máquina n1-standard-4
e executar sudo docker stats <container_name>
, obtenho isto:
CONTAINER CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O
k8s.name 3.84% 1.11 GB / 1.611 GB 68.91% 0 B / 0 B
Quando executo a imagem do same Docker com a configuração exata same (aplicativo) localmente (mac osx e docker-machine), vejo:
CONTAINER CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O
name 1.49% 236.6 MB / 1.044 GB 22.66% 25.86 kB / 14.84 kB 0 B / 0 B
O que está muito mais em sintonia com o que eu esperaria devido à configuração Xmx
(para o registro eu tenho 8 núcleos e 16Gb de memória). A mesma coisa é confirmada quando executo top -p <pid>
na instância do GKE, o que me dá uma alocação de memória RES / RSS de 1,1 a 1,4 Gb.
A imagem do Docker é definida assim:
FROM java:8u91-jre
EXPOSE 8080
EXPOSE 8081
ENV JAVA_TOOL_OPTIONS -Dfile.encoding=UTF-8
# Add jar
ADD uberjar.jar /data/uberjar.jar
CMD java -jar /data/uberjar.jar -Xmx128m -server
Eu também tentei adicionar:
ENV MALLOC_ARENA_MAX 4
que eu já vi recomendado em vários segmentos, como isso , mas não parece fazer diferença alguma. Eu também tentei mudar para outra imagem de base Java, bem como usando o linux alpino, mas isso não parece mudar as coisas também.
Minha versão local do Docker é 1.11.1 e a versão do Docker no Kubernetes / GKE é 1.9.1. A versão do Kubernetes (se for importante) é v1.2.4.
O que também é interessante é que, se implantarmos várias instâncias do pod / container na mesma máquina / nó, algumas instâncias alocarão muito menos memória. Por exemplo, os três primeiros podem alocar 1,1-1,4 Gb de memória, mas os 10 contêineres sucessivos alocam apenas cerca de 250 Mb cada, o que é aproximadamente o que eu esperaria que cada instância alocasse. O problema é que, se atingirmos a limitação de memória da máquina, as três primeiras instâncias (aquelas que alocam a memória de 1.1Gb) nunca parecem liberar sua memória alocada. Se eles fossem liberar memória quando a máquina estivesse sob pressão aumentada, eu não me preocuparia com isso, mas como eles mantêm a alocação de memória mesmo quando a máquina é carregada, isso se torna um problema (pois proíbe que outros recipientes sejam programados nesta máquina e assim, os recursos são desperdiçados).
Perguntas:
- O que poderia estar causando esse comportamento? É um problema da JVM? Problema do Docker? Problema de VM? Problema do Linux? Questão de configuração? Ou talvez uma combinação?
- O que eu posso tentar fazer para limitar a alocação de memória da JVM neste caso?