Ограничения ресурсов для уровня ОС, Docker, Kubernetes и JVM.

Предисловие

«Контейнер «случайно» отключается на рабочем сервере! И в журнале нет записей о том, что что-то подобное произошло!!

Это утверждение было не в первый раз, когда я слышу его от клиента. Зная, что почти ничто не является действительно случайным для компьютеров, любой должен заподозрить этот момент. Ниже приведены мои шаги по исследованию, чтобы найти основную причину «случайного» завершения работы приложения, и мой рецепт для стабильных сред, в которых работают контейнерные приложения JVM.

Предварительные условия

Прежде чем мы сможем начать, есть некоторые предварительные условия. И я предполагаю, что если вы нашли это руководство, вы

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

Расследование

Обычно я сталкиваюсь с приложениями JVM, работающими на сервере Linux. Таким образом, это руководство исследует упомянутую ошибку в такой системе. Если вы используете что-то другое, вы должны адаптировать системные команды.

План расследования

В результате получился план анализа:

  • Проверить статистику использования ресурсов Docker/Kubernetes
  • Проверить статистику использования ресурсов сервера
  • Проверить статистику потребления оперативной памяти JVM

Но давайте сначала продолжим немного теории Linux 😅 Поскольку это было ключом к пониманию и решению цитируемой «случайности».

Автоматическое завершение процесса в Linux

Ядро Linux может завершить один или несколько процессов, когда системе не хватает ресурсов. Распространенным примером является убийца нехватки памяти (OOM), который действует, когда физическая память системы исчерпана.

Работа убийцы OOM состоит в том, чтобы выбрать минимальное количество процессов, когда системе не хватает памяти, и завершить их. Он использует оценку вредоносности, чтобы решить, какие процессы следует убить. Принимая это решение, он пытается минимизировать ущерб, следя за тем, чтобы:

  • сводит к минимуму потерянную работу
  • восстанавливает как можно больше памяти
  • не убивает невинные процессы, а только те, которые потребляют много памяти
  • минимизирует количество убитых процессов (в идеале всего один)
  • убивает процесс(ы), которые пользователь ожидает

Мы должны знать, что Linux делает эту работу, чтобы избежать нехватки оперативной памяти. Поставить компьютер на колени и ввести ограничения на всех вовлеченных уровнях — это ответ, который поможет предотвратить подобное с вашими службами, ориентированными на производство.

Дополнительные сведения об убийце OOM см. в следующей ссылке: https://www.kernel.org/doc/gorman/html/understand/understand016.html

Следственные казни

Проверить статистику Docker

docker stats $(docker ps --format '{{.Names}}') --no-stream

Дополнительные сведения о команде docker stats см. в следующей ссылке: https://docs.docker.com/engine/reference/commandline/stats/

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

Проверьте статистику Kubernetes

Если вы используете среду Kubernetes, следующая команда предоставит вам те же данные:

kubectl top pod --all-namespaces | sort --reverse --key 3 --numeric

Дополнительные сведения о командеkubectl top podсм. в следующей ссылке: https://docs.docker.com/engine/reference/commandline/stats/

Проверьте использование оперативной памяти

free -m

Дополнительные сведения о бесплатной команде см. в следующей ссылке: https://linuxize.com/post/free-command-in-linux/

Здесь я понял, что у нас должна быть ошибка, связанная с оперативной памятью, так как осталось всего 97 МБ, а также происходит подкачка памяти.

Проверить события уничтожения

Выполните следующую команду Linux на своем сервере, чтобы получить информацию о любых возможных событиях уничтожения:

dmesg -T | grep -i kill

Подробнее о команде dmesg см. в следующей ссылке: https://linuxize.com/post/dmesg-command-in-linux/

Наличие записей в журнале, содержащих kill, является доказательством того, что убийца OOM принял меры. В то же время, именно поэтому в журнале нашего приложения нет видимых записей в журнале или сообщений о корректном завершении работы. Поскольку его просто убили, не успев ничего записать.

Перегрузка ЦП

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

Статистика времени выполнения Java: jmap, jstack, jcmd, jinfo

Обычно мы запускаем наши приложения в контейнере с JRE. Однако у нас есть и обратная сторона: у нас нет таких инструментов, как jmap, jstack, jcmd и jinfo, поскольку они являются частью JDK.

Я начал использовать jattach, инструмент, который объединяет все вышеперечисленные инструменты. Я устанавливаю их в свои контейнеры, чтобы они были готовы к расследованию.

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

jattach <pid> jcmd VM.native_memory

Чтобы получить эти данные, необходимо включить отслеживание встроенной памяти, задав следующий параметр JVM:

-XX:NativeMemoryTracking=summary

Дополнительные сведения о встроенной функции отслеживания памяти см. в следующей ссылке:https://docs.oracle.com/en/java/javase/18/vm/native-memory-tracking.html

Вот как я использовал его для исследования памяти:

Ознакомьтесь с моим примером рабочего файла Dockerfile там; Я включил все эти предварительные условия, которые будут доступны для предстоящих расследований:



Еще один подход… VisualVM

Мы также могли бы использовать для анализа такие инструменты, как VisualVM, но это приводит к недостатку необходимости настраивать и открывать порт JMX, который в контейнере должен быть включен в часть CMD и, следовательно, может быть выполнен только до того, как мы запускаем наше приложение, container.

Если вас интересует этот путь, ознакомьтесь со следующим руководством:



Недостаток: только данные времени выполнения

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

Для дампа кучи Java мы можем настроить его на автоматическое создание при возникновении java.lang.OutOfMemoryError, чтобы помочь нам исследовать ошибку, добавив следующую конфигурацию в JAVA_OPTS:

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=<file-or-dir-path>

Помните, что результирующий размер файла может стать довольно большим (ваш максимальный объем физической памяти или определенное значение -Xmx). Если вы запускаете свои контейнеры с повторной попыткой, теоретически вы можете получить количество повторных попыток файлов. Учитывайте этот факт при определении записи дампа кучи!

Резюме/Вывод

Даже в простом развертывании задействованы три различных ограничения памяти:

  • JVM через параметр -Xmx
  • Docker через параметр docker-compose /
    Kubernetes через resources:limits в файле спецификации Pod
  • ОС через параметр memory.limit_in_bytes cgroups

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

Рекомендации, Дополнительные темы

Определить -Xms, -Xmx и -Xss

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

Мониторинг приложений

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

Следуя некоторым из них, я столкнулся:

Приоритет процесса Linux с использованием nice и renice

Вы можете развить идею приоритизации процессов непосредственно на хосте, чтобы ваши соответствующие службы не были убиты. Дополнительную информацию об этом можно найти на следующем веб-сайте:



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

JProfiler

Это довольно дорого, но в то же время стоит каждого вложенного цента: JProfiler. Так что, возможно, стоит подумать, если вам часто нужно анализировать запущенные приложения JVM. Я также показал бесплатные инструменты, которые мог бы порекомендовать, такие как VisualVM и стек jattach, в качестве альтернативы.

По ссылке на JProfiler:



Если вы хотите использовать JProfiler для проектов с открытым исходным кодом, есть способ получить его бесплатно: https://www.ej-technologies.com/buy/jprofiler/openSource.

Заключительные слова

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

Я также постоянно пытаюсь жить за счет того, что зарабатываю, делая то, что искренне люблю ❤️. Написание технических статей 📝 стало одной из тех вещей, которыми я увлечен. Если бы моя статья поддержала вас, для меня было бы честью получить несколько аплодисментов и подписку.

Ваша поддержка позволит мне тратить еще больше времени на написание таких статей и повысит мою мотивацию. Подписывайтесь на меня здесь, на Medium, чтобы получать больше технических статей и бонусных материалов 😃.

Ниже приведена ссылка, чтобы поддержать меня или сказать спасибо, купив мне кофе: https://ko-fi.com/niksta

Ссылки, Полезные ссылки