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, проблема уже не актуальна.
Sign up to leave a comment.