Докеризованное приложение spring boot на k8s занимает почти всю доступную память

У меня есть простое приложение Spring Boot, развернутое на k8s (2 модуля). Кратко описанное приложение принимает сообщения от производителя и обрабатывает их потребителю. Ничего сложного.

UPD:

  • версия java: 1.8.172
  • javaMemoryOpts: -Xmx2048m -XX: + UnlockExperimentalVMOptions -XX: + UseCGroupMemoryLimitForHeap

memory monitoring Это потребление памяти для одного из 2-х подов.

  • синяя линия - запрошенная память k8s
  • оранжевая линия - рабочий набор
  • зеленая линия - использование услуги
  • желтая линия - лимит памяти по k8s

Проблема в большом использовании памяти, несмотря на простоту обслуживания. Я профилировал приложение, но с сервисом вроде нормально: всего около 60 потоков, никаких утечек памяти и так далее.

Используемая память никогда не превышает лимит k8s, даже если он очень близок к нему (без OOM). Конечно, я могу добавить больше стручков, и потребление станет равным, но я думаю, что это неправильный путь.

Одно смущает меня, почему использованная память всегда превышает требуемую даже в самом начале.

На самом деле, я не знаю, что с этим не так. Есть ли у кого-нибудь идеи или, может быть, знает, как уменьшить использование памяти приложением?

Вы настроили максимальную кучу JVM, чтобы вообще ее ограничить? В зависимости от версии Java, которую вы используете, максимальный размер кучи по умолчанию может зависеть от объема памяти, доступной на хосте, а не в вашем контейнере Docker.

Andy Wilkinson 31.10.2018 11:47

@AndyWilkinson да, я пропустил это, чтобы заметить в описании. Я обновлю это

yevtsy 31.10.2018 11:54
0
2
5 140
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Как правило, Kubernetes представляет запросы и ограничивает механизмы для управления ресурсами (ЦП, память). Миссия запросов направлена ​​на предоставление достаточного количества ресурсов для контейнера внутри кластерных модулей. Ограничения гарантируют, что контейнер никогда не достигнет определенного значения для конкретного ресурса. Совершите экскурсию и посетите следующие статьи:

Настройка JVM - довольно сложный процесс для достижения хороших результатов и достаточного уровня использования вычислительной системы. Я бы порекомендовал просмотреть следующие веб-ссылки по этой теме:

Ответ от mk_sta полезен, и вся необходимая информация, вероятно, есть в этих документах, но я считаю, что стоит обобщить основные моменты в полном ответе.

Ваш параметр -Xmx2048m (2Gb) устанавливает максимальный размер кучи, но приложение будет использовать больше памяти, чем это - Metaspace, сборщик мусора и множество других накладных расходов (это память "вне кучи").

Независимо от того, насколько просто ваше приложение, Java будет использовать доступный размер кучи. Трехстрочное приложение, выводящее случайные строки, при наличии кучи 2 ГБ в конечном итоге будет использовать все это. Так что не имеет значения, является ли ваше приложение Spring Boot «простым» - куча будет расти, пока не достигнет максимума, а затем вы увидите сборку мусора - это зазубренные зубы на вашей зеленой линии.

Таким образом, эти две вещи вместе, вероятно, объясняют, почему вы видите верхнюю границу использования памяти около 3,8 ГБ.

Графики, которые вы строите, вероятно, показывают хорошо работающее приложение, так что не беспокойтесь об утечках памяти. Я не могу сказать по картинке, мелкие это коллекции или крупные. Т.е. я не могу сделать вывод из рисунка, насколько маленьким можно рискнуть уменьшить размер Xmx.

Хотя вы говорите, что ваше приложение Spring Boot «простое», без просмотра его содержимого невозможно узнать, насколько оно сложное на самом деле. Но документ, связанный с mk_ska здесь ...

https://github.com/dsyer/spring-boot-memory-blog/blob/master/cf.md

... очень полезен, потому что он показывает некоторые хорошие значения по умолчанию для "небольших" приложений Spring Boot - например, приложение, использующее Freemarker для генерации нестатического контента, может успешно работать с кучей 32 Мб.

Таким образом, простой ответ - попытаться уменьшить -Xmx до минимума, прежде чем вы увидите, что сборщик мусора мешает. Вы тоже можете уменьшить его до 32 МБ.

Затем вы можете использовать эти результаты, чтобы установить некоторые разумные значения для лимита ресурсов и запроса ресурсов в ваших манифестах K8S. Вам понадобится намного больше, чем, например, 32 МБ размера кучи - как указано в этом документе, приложения для загрузки Spring намного лучше с контейнерной памятью размером 512 МБ или 1 ГБ. Вы можете уйти с 256 Мб, но этого мало.

Я написал руководство, которое поможет определить размер контейнеров Spring Boot на k8s. Перед установкой предельного значения памяти необходимо проверить множество параметров. И зонды важны.

Фактические LTS JVM не готовы использовать негарантированную память (зона между запросом и предельным значением) из-за объема памяти Java. JVM будет ждать полного GC, чтобы освободить неиспользуемую память в системе, поэтому оставаться в этой зоне долгое время рискованно. И во время такого GC ваши потоки будут расти, а также память стека Java.

Я подробно описал здесь шаг за шагом: https://loadteststories.com/java-kubernetes-sizing-my-docker-openjdk-spring-boot-microservice/

Другие вопросы по теме