Разработка
Dmitry Soroka
1642

Java внутри Docker: что нужно знать, чтобы не было проблем

В закладки

На моем последнем проекте я столкнулся с проблемой: приложения Java внутри Linux Docker-контейнеров потребляли очень много памяти. Например, некоторые из наших микросервисов (на основе Spring Boot) внутри контейнера могли использовать до двух гигабайт памяти, тогда как при обычном запуске вне контейнера показатель был в районе 200-300 мегабайт.

Ситуация, когда java внутри контейнера съедает очень много памяти

После недолгого гугления оказалось, что когда мы запускаем приложения как "java -jar mypplication-fat.jar", то JVM по умолчанию приспосабливается к среде выполнения, в которой она работает и таким образом обеспечивается максимально возможное быстродействие java-программ.

CGroups и старые приложения

В Docker-контейнерах, как и в виртуальных машинах (Virtual Machines, VM), можно разграничивать ресурсы, выделять количество ядер и виртуальной памяти. При этом контейнеры обладают отличной изоляцией ресурсов (CPU, память, файловые системы), и один контейнер совершенно ничего не знает о других. Таким образом контейнеры не мешают работе друг друга, и мы можем запускать десятки контейнеров с разными переменными средами (environment) и конфигурациями. Такая изоляция возможно только благодаря возможности ядра Linux — cgroups.

Однако некоторые из приложений были разработаны задолго до появления cgroups. Такие инструменты, как 'top', 'ps', 'free', а также и JVM, не оптимизированы для эффективной работы в контейнерах.

Ограничим память

Первое, что может прийти на ум — это ограничить размер самого Docker-контейнера. Создадим простое приложение на Spring Boot и запустим контейнер, поставив ограничение памяти 150 мегабайт. Вот пример команды на хосте с одним гигабайтом памяти:

$ docker run -it --rm --name mycontainer150 -p 8080:8080 -m 150M rafabene/java-container:openjdk

В приложении заранее был создан rest-метод, который загружает память до отказа объектами типа String. Вызовем его:

$ curl http://`docker-machine ip docker1024`:8080/api/memory

Вызванный метод возвращает что-то вроде "Выделено более чем 80% памяти (219.8 MiB) из максимально доступных JVM (241.7 MiB) ".

Но отсюда два вопроса:

1. Почему максимальный размер для JVM 241 мегабайт?

2. Если мы выше указали, что контейнеру доступно только 150 мегабайт памяти, то почему приложение потребляет почти 220?

Первое, что нужно помнить о максимальном размере кучи (max heap size) у JVM, это то, что размер по умолчанию будет занимать 1/4 памяти. И второе — когда мы при запуске контейнера выше указывали флаг "-m 150M", то докер зарезервировал 150 мегабайт в оперативной памяти и 150 мегабайт в Swap. Поэтому-то приложение и не рухнуло и отработало как нужно. Но при этом вы должны понимать, что увеличение памяти в контейнере — это не решение проблемы. Всегда может случиться ситуация, когда Java-приложение может выйти за рамки лимита, и тогда процесс будет убит.

Решение проблемы

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

Начиная с JDK 8u131+ и JDK 9 нам дали возможность использовать экспериментальные возможности JVM, которые позволяют нам обращаться к возможностям вышеупомянутых CGroups. Одна из этих возможностей — это настроить JVM так, чтобы она автоматически определяла максимальное возможное потребление памяти с помощью опции -XX:+UseCGroupMemoryLimitForHeap. Попробуем:

$ docker run -d --name mycontainer8g-jdk9 -p 8080:8080 -m 600M rafabene/java-container:openjdk-cgroup $ docker logs mycontainer8g-jdk9|grep MaxHeapSize size_t MaxHeapSize = 157286400 {product} {ergonomic}

JVM прочитала, что максимум доступно памяти 600 мегабайт, и создала JVM с максимальным размером памяти примерно 150 мегабайт. Ровно четверть, как и говорилось выше.

Ну и как пример, мое приложение на работе без ограничений использовало 1.7 гигабайта памяти, после того, как я использовал экспериментальные фичи и ограничил размер контейнера до 512 мегабайт — 331 мегабайт.

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

Материал подготовлен и частично переведен при помощи статьи с developers.readhat.com

Материал опубликован пользователем.
Нажмите кнопку «Написать», чтобы рассказать свою историю.

Написать
{ "author_name": "Dmitry Soroka", "author_type": "self", "tags": [], "comments": 15, "likes": 26, "favorites": 12, "is_advertisement": false, "subsite_label": "dev", "id": 116103, "is_wide": false, "is_ugc": true, "date": "Thu, 12 Sep 2019 18:16:38 +0300", "is_special": false }
0
{ "id": 116103, "author_id": 106382, "diff_limit": 1000, "urls": {"diff":"\/comments\/116103\/get","add":"\/comments\/116103\/add","edit":"\/comments\/edit","remove":"\/admin\/comments\/remove","pin":"\/admin\/comments\/pin","get4edit":"\/comments\/get4edit","complain":"\/comments\/complain","load_more":"\/comments\/loading\/116103"}, "attach_limit": 2, "max_comment_text_length": 5000, "subsite_id": 232424, "last_count_and_date": null }
15 комментариев
Популярные
По порядку
Написать комментарий...
3

приложения Java потребляли очень много памяти

Так и должно быть)

Ответить
0

мне кажется такой контент на хабр лучше. не? 

Ответить
5

мне просто было пофиг, скучно, поддержал мертвый раздел на tj

Ответить
0

имхо он мертворожденный. если было бы что-то околопрограмминга, то может и зашло бы

Ответить
0

Можно мне, как ламеру, сказать, чем отличаются докеры от каких-нибудь флетпаков и снапов?

Ответить
3

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

flatpak сделан с учетом потребностей GUI приложений, имплементированы порталы для доступа к файлам или сенсорам, поддерживаются обращения к x-серверу хоста или к api графических тулкитов, но при этом в нём например нет возможностей создания виртуальных сетей, вообще вменяемого клиентского интерфейса управления запущенными контейнерами, инструментария для сборки образов "на коленке" и поддержки внешних регистри.

Кроме того, докер тупо старше, индустриальный стандарт, с которым в первую очередь работают системы оркестрации, так он что вне конкуренции в корпоративной среде просто по факту.

Ответить
0

Гуйню кстать в докере тоже можно гонять. Можно и на потолке спать в принципе, никто не запрещается. Просто он не делался виз экзактли зис ин майнд.

http://fabiorehm.com/blog/2014/09/11/running-gui-apps-with-docker

Ответить
0

тем что он везде 

и я трахаюсь с ним уже третий день пытаясь развернуть cockpit cms на azure web app

Ответить
0

Как я понял, снапы работают и взаимодействуют с хостом напрямую, имеют доступы к памяти, фс и прочие плюшки. В то время как контейнеры сами в себе- работают в докере, ограничены во всем, довольствуются только своей песочницей.

Ответить
0

снапы работают и взаимодействуют с хостом напрямую, имеют доступы к памяти, фс и прочие плюшки

По умолчанию — не имеют, какие разрешения в snapcraft.yaml опишешь, туда доступ и будут иметь. Аналогично ты можешь хитровыебано описать в docker compose вольюмы с тем же результатом, просто это будет через жепу немношк.

Ответить
0

Ну я упрощенно.

Ответить
0

на линухе уже давно контейнеры нативными стали, изолирован лишь контекст.

а ресурсы и возможности - общие

Ответить
0

А кто чистый докер в продакшене юзает?

Ответить
0

это не продакшен, кто сказал такое?

Ответить
Обсуждаемое
Новости
Павла Устинова, осуждённого за вывих плеча росгвардейца на митинге 3 августа, выпустили из СИЗО
Об этом просил адвокат Анатолий Кучерена и ходатайствовала Генпрокуратура.
Истории
Фоторепортаж: Школьники Нью-Йорка бастуют против изменения климата на «Пятнице ради будущего»
Тысячи американских подростков на улицах города, следующих заветам 16-летней экоактивистки Греты Тунберг.
Разборы
Хроники исчезновения: как в России пропали очереди за iPhone и с чем это может быть связано
Компания произвела больше устройств, магазины улучшили логистику или упал ажиотаж.
Популярное за три дня
Интернет и мемы
Новости
В детском саду в Коми чиновники торжественно открыли окна
Красные ленточки перерезали также в честь новых балконных дверей.
Интернет и мемы
Росгвардейские частушки с просторов интернета, озвученные ботом из телеграма
В связи с недавними событиями народный фольклор пополнился еще одним произведением. Оно посвящено Росгвардейцу Александру Лягину, которому актер Павел Устинов якобы "вывихнул плечо".

Прямой эфир

[ { "id": 1, "label": "100%×150_Branding_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox_method": "createAdaptive", "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "cndo", "p2": "ezfl" } } }, { "id": 2, "label": "1200х400", "provider": "adfox", "adaptive": [ "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "cndo", "p2": "ezfn" } } }, { "id": 3, "label": "240х200 _ТГБ_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "i", "ps": "cndo", "p2": "fizc" } } }, { "id": 4, "label": "Article Branding", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "p1": "cfovy", "p2": "glug" } } }, { "id": 5, "label": "300x500_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "cndo", "p2": "ezfk" } } }, { "id": 6, "disable": true, "label": "1180х250_Interpool_баннер над комментариями_Desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "h", "ps": "clmf", "p2": "ffyh" } } }, { "id": 7, "label": "Article Footer 100%_desktop_mobile", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "p1": "byswn", "p2": "fjxb" } } }, { "id": 8, "label": "Fullscreen Desktop", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "cndo", "p2": "fjoh" } } }, { "id": 9, "label": "Fullscreen Mobile", "provider": "adfox", "adaptive": [ "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "cndo", "p2": "fjog" } } }, { "id": 10, "disable": true, "label": "Native Partner Desktop", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fmyb" } } }, { "id": 11, "disable": true, "label": "Native Partner Mobile", "provider": "adfox", "adaptive": [ "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fmyc" } } }, { "id": 12, "label": "Кнопка в шапке", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "cndo", "p2": "fdhx" } } }, { "id": 13, "label": "DM InPage Video PartnerCode", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox_method": "createAdaptive", "adfox": { "ownerId": 228129, "params": { "pp": "h", "ps": "cndo", "p2": "flvn" } } }, { "id": 14, "label": "Yandex context video banner", "provider": "yandex", "yandex": { "block_id": "VI-223677-0", "render_to": "inpage_VI-223677-0-130073047", "adfox_url": "//ads.adfox.ru/228129/getCode?pp=h&ps=cndo&p2=fpjw&puid1=&puid2=&puid3=&puid4=&puid8=&puid9=&puid10=&puid21=&puid22=&puid31=&puid32=&puid33=&fmt=1&dl={REFERER}&pr=" } }, { "id": 15, "label": "Баннер в ленте на главной", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "p1": "byudv", "p2": "ftjf" } } }, { "id": 16, "label": "Кнопка в шапке мобайл", "provider": "adfox", "adaptive": [ "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "p1": "ccydt", "p2": "ftwx" } } }, { "id": 17, "label": "Stratum Desktop", "provider": "adfox", "adaptive": [ "desktop" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "cndo", "p2": "fzvb" } } }, { "id": 18, "label": "Stratum Mobile", "provider": "adfox", "adaptive": [ "tablet", "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "cndo", "p2": "fzvc" } } } ]