Найти в Дзене
Другая сторона DevOps

Запустить Java в Kubernetes и не облажаться.

Рано или поздно вы все равно столкнетесь или уже столкнулись с запуском Java в pod. В чем проблемы запуска и как их обойти? До версии 8 java не знала о контейнерах ничего и просто забирала себе определённый процент от всей доступной памяти. Размер памяти jvm берет из системных данных, игнорируя ограничения cgroup то есть ограничения контейнера. Память контейнера ограниченна (что почти всегда правда в kubernetes), java машина будет пытаться использовать память больше возможной в контейнере, не сможет это сделать и аварийно завершится. С версии 8 менеджер памяти java учитывает ограничения контейнера и почти правильно использует память. Это теория, теперь перейдем к практики. У java есть два параметра -Xms - устанавливает минимум используемой памяти и -Xmx - устанавливает максимум используемой памяти. Мы должны ограничить лимит памяти. Вот реальный пример из настройки контейнера elastic search: И для JVM тоже нужно установить лимиты: Обратите внимание мы просим pod дать 2 гигабайта а java

Рано или поздно вы все равно столкнетесь или уже столкнулись с запуском Java в pod. В чем проблемы запуска и как их обойти?

До версии 8 java не знала о контейнерах ничего и просто забирала себе определённый процент от всей доступной памяти. Размер памяти jvm берет из системных данных, игнорируя ограничения cgroup то есть ограничения контейнера. Память контейнера ограниченна (что почти всегда правда в kubernetes), java машина будет пытаться использовать память больше возможной в контейнере, не сможет это сделать и аварийно завершится. С версии 8 менеджер памяти java учитывает ограничения контейнера и почти правильно использует память.

Это теория, теперь перейдем к практики. У java есть два параметра -Xms - устанавливает минимум используемой памяти и -Xmx - устанавливает максимум используемой памяти. Мы должны ограничить лимит памяти. Вот реальный пример из настройки контейнера elastic search:

И для JVM тоже нужно установить лимиты:

Обратите внимание мы просим pod дать 2 гигабайта а java машину ограничиваем 1,5 гигабайта. Дополнительную память использует система в докер контейнере JVM будет использовать всю отданную ей память сразу так как значение Xms равно Xmx.

Если мы для pod запросим слишком много памяти kubernetes может не удовлетворить такой запрос и pod будет бесконечно находится в состоянии pending. При слишком маленьком лимите java машина не сможет стартовать при попытки выделить требуемую память, и система завершит процесс с помощью OOM Killer.

С версии java 8 и в особенности с 10 версии в jvm встроили поддержку контенеров. Лимиты контейнеров теперь определяются автоматически (если включен UseContainerSupport, а он включен по умолчанию). Появились две новый опции MaxRAMPercentage и MinRAMPercentage они устанавливают минимальный и максимальный процесс использования памяти соответственно. По умолчанию MaxRAMPercentage равен 25% а MinRAMPercentage равен 50%. Значения эти выглядят очень странно, и я рекомендую использовать MaxRAMPercentage: 80 и MinRAMPercentage: 80 если в контейнере только java машина. Будет использоваться 80% памяти контейнера.

С ограничение процессоров история примерно такая же: после версии 8 будут использоваться ограничения процессоров из контейнера до восьмой версии вам следует ограничить число используемых процессоров с помощью опции -cpuset-cpus.

В заключение хотелось бы пожелать перейти на java 10 и не знать проблем, но по своему опыту знаю, что это не всегда возможно.