Pull to refresh

Comments 18

JVM до сих пор не имеет средств, позволяющих определить, что она выполняется в контейнеризированной среде и учесть ограничения некоторых ресурсов, таких, как память и процессор.
А должна? Если Java будет знать, что она в контейнере, то может получиться сильно связанная система, что не есть хорошо.
поддержу. в этом же фишка JVM, она не должна знать что находится «под ней».
В самом начале поста написано, что многие родные утилиты тоже ведут себя, скажем так, странно.
Краткое содержание статьи: не ленитесь, используйте -Xmx в любых средах )

Старый добрый "хаваю памяти сколько хочу" :)
Я думал, что почти все сталкивались с этим, если даже я пару раз натыкался.

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

Но скоро все исправиться и JVM можно будет заставить проверять cgoups лимиты
> Многие разработчики знают, или должны знать, что Java-процессы, исполняемые внутри контейнеров Linux (среди них — docker, rkt, runC, lxcfs, и другие), ведут себя не так, как ожидается.

А что ожидается от виртуальной машины внутри контейнера? — это как бы по определению ортогональные понятия :-)

Вообще если приложение запускается на сервере, то -Xmx должен быть задан в обязательном порядке. Ограничение на metaspace тоже желательно задавать. Другие ограничения по расходу памяти в OpenJDK так же присутствуют (для буферо машинного кода jit, offheap память и т.д.). Но если это все задавать, то какой смысл дублировать это через докер?

Я не проверял последние докеры, но LXC версии 2.x, ограничения у которых тоже реализуется средствами cgroup, корректно определяют доступные ресурсы внутри контейнера:
# main
# free -m
             total       used       free     shared    buffers     cached
Mem:         32078      31807        271       2467       2165      21327

# container
# free -m
             total       used       free     shared    buffers     cached
Mem:         10240       5195       5044       2467          0          0



Debian
lxc 2.0.6-1~bpo8+1
kernel 4.9.13-1~bpo8+1

Посмотрите в сторону обновления докера и ядра.

как-то так:


$ uname --kernel-release
4.8.0-42-generic

$ docker version
Client:
 Version:      17.03.0-ce
 API version:  1.26
 Go version:   go1.7.5
 Git commit:   3a232c8
 Built:        Tue Feb 28 08:01:32 2017
 OS/Arch:      linux/amd64

Server:
 Version:      17.03.0-ce
 API version:  1.26 (minimum version 1.12)
 Go version:   go1.7.5
 Git commit:   3a232c8
 Built:        Tue Feb 28 08:01:32 2017
 OS/Arch:      linux/amd64
 Experimental: false

$ docker run -it --memory="128M" debian:8 free -m
WARNING: Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.
             total       used       free     shared    buffers     cached
Mem:         15925       4578      11347        160        236       1821
-/+ buffers/cache:       2519      13405
Swap:            0          0          0
Покопался в вопросе и таки решил:
LXC информирует контейнер о доступных ресурсах с помощью LXCFS. Если для контейнера это работает из коробки, то для докера нужно в ручную задать перемонтирование соответствующих файлов из /var/lib/lxcfs/proc/ в /proc/, например:
# like docker-compose 
mem_limit: 512m
volumes:
   - /var/lib/lxcfs/proc/meminfo:/proc/meminfo

И ура:
# docker exec CT_name free -m
             total       used       free     shared    buffers     cached
Mem:           512         13        499       4322          0          0


Естественно, lxcfs нужно поставить в систему и, возможно, в ручную смонтировать:
# mount | grep lxcfs
lxcfs on /var/lib/lxcfs type fuse.lxcfs (rw,nosuid,nodev,relatime,user_id=0,group_id=0,allow_other)

У меня на сервере одновременно живёт и LXC и docker, потому лишних телодвижений делать не пришлось

да, в таком виде пашет:


$ docker run -it -v /var/lib/lxcfs/proc/meminfo:/proc/meminfo --memory="128M" debian:8 free -m
WARNING: Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.
             total       used       free     shared    buffers     cached
Mem:           128          2        125        557          0          2
-/+ buffers/cache:          0        127
Swap:            0          0          0
Добавлю к «верному решению проблемы».

Вот такой вариант, как ниже (и в статье), лучше НЕ использовать, так как в зависимости от образа может привести к тому, что Ctrl-C и SIGTERM будет посылаться не java, а bash, что приводит к тому, что контейнер нельзя будет опустить — только убить. Например так себя ведут образы maven
CMD java -XX:+PrintFlagsFinal -XX:+PrintGCDetails $JAVA_OPTIONS -jar java-container.jar


Правильный вариант:

CMD ["java", "-XX:+PrintFlagsFinal", "-XX:+PrintGCDetails", "-jar", "java-container.jar"]


А куда же делся JAVA_OPTIONS, спросите вы?
В джава есть специальная переменная окружения для этого. Называется JAVA_TOOL_OPTIONS. Можно передавать и через `-e`, либо использовать сразу в Dockerfile через `ENV`
Если кто-то читает этот перевод спустя много лет — в оригинальной статье уже появились два обновления: от 15.03.2018 и 21.04.2018 про jdk9 и jdk10.
Привет из 2019. Так же пишут, что Java 8 получила поддержку Docker, проблема уже не актуальна.
Only those users with full accounts are able to leave comments. Log in, please.

Information

Founded
Location
Россия
Website
ruvds.com
Employees
11–30 employees
Registered
Representative
ruvds

Habr blog