Docker 환경에서의 자원 관리

Jinhan Choi
10 min readJan 20, 2020

--

JVM 운영 환경에서 적절한 heap size를 지정하는 것은 매우 중요하다. 그런데 fusion과 같은 container 환경에서는 몇 가지 추가적인 고려가 필요하다.

Docker 환경에서의 자원 관리

먼저 Docker는 일반적인 가상화와 다르게 완전히 격리된 host 환경을 제공하지 않는다. 다음 명령을 실행 해보자.

% docker run --memory 512m -it --rm ubuntu:18.04 /bin/bash

root@baf2168834a3:/# free

total used free shared buff/cache available

Mem: 2046940 233420 606064 860 1207456 1652744

Swap: 1048572 208 1048364

분명 메모리 사이즈를 512m로 제한했지만 free 명령으로 확인하면 2G로 나온다(여기서 2G는 테스트 장비의 전체 메모리 크기). /proc/meminfo도 마찬가지 정보를 return한다.

root@baf2168834a3:/# cat /proc/meminfo

MemTotal: 2046940 kB

MemFree: 606888 kB

MemAvailable: 1653544 kB

...

...

하지만 메모리를 512m 이상 사용하면 OOM이 발생한다.

root@efc87a14ea9d:/# apt-get update && apt-get install -y stress

root@21617e58fe2a:/# stress -m 1 --vm-bytes 1G --vm-keep

stress: info: [239] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd

stress: FAIL: [239] (415) <-- worker 240 got signal 9

stress: WARN: [239] (417) now reaping child worker processes

stress: FAIL: [239] (451) failed run completed in 2s

이는 /proc의 정보들은 host의 정보를 보여주기 때문으로 Docker 내부에서 사용 가능한 진짜 제한은 cgroup에서 봐야 한다.

root@21617e58fe2a:~# cat /sys/fs/cgroup/memory/memory.limit_in_bytes

536870912

Docker 환경에서 JVM 메모리 관리

최근 버전의 JVM들은 OS의 메모리에 맞춰 memory 크기를 자동으로 설정하게 되어 있는데 fusion에서 default 설정으로 사용한다면 모든 container가 128G의 메모리를 가지고 있다고 착각을 하게 된다.

이 경우 메모리가 매우 많기 때문에 GC를 거의 안하게 되고 성능은 좋아질 수 있겠으나 조금만 시간이 지나도 메모리가 부족해서 OOM이 발생하기 시작할 것이다.

따라서 -Xmx 옵션으로 정확한 메모리를 할당하는 것이 가장 확실하나 다음과 같은 방법을 사용하면 향후 메모리 할당량 변경을 fusion에서 동적으로 할 수 있으니 같이 검토할 필요가 있다.

Java 8

Java 8에서는 다음 옵션을 주면 cgroup의 정보를 기준으로 메모리 크기를 결정한다.

-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap

그리고 다음 옵션으로 전체 메모리중 얼마를 JVM이 사용할지 결정한다.

-XX:MaxRAMFraction=1

-XX:MaxRAMFraction=2

-XX:MaxRAMFraction=3

여기서 이 옵션이 좀 문제가 있는데 이 의미가 1%, 2%, 3%가 아니라 1/1, 1/2, 1/3의 의미로 각각 100%, 50%, 33%의 설정이 된다.

왜 이렇게 만들어 놨는지 도무지 이해할 수 없는 설정인데 Java 8에서는 어쩔 수 없이 이 설정을 사용할 수 밖에 없다.

문제는 100%는 JVM 이외의 시스템이 사용할 메모리가 전혀 없다는 문제가 있고 50%는 또 너무 작다는 문제가 있다.

따라서 Docker에서 필요한 메모리 보다 좀더 여유있게 할당하고 그의 50%로 설정 하는 꼼수를 사용 해야 한다.

~ % docker run -m 512m -it --rm openjdk:8-jre-alpine java -XshowSettings:vm -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=1 -version

VM settings:

Max. Heap Size (Estimated): 455.50M

Ergonomics Machine Class: server

Using VM: OpenJDK 64-Bit Server VM

openjdk version "1.8.0_151"

OpenJDK Runtime Environment (IcedTea 3.6.0) (Alpine 8.151.12-r0)

OpenJDK 64-Bit Server VM (build 25.151-b12, mixed mode)

~ % docker run -m 800m -it --rm openjdk:8-jre-alpine java -XshowSettings:vm -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=1 -version

VM settings:

Max. Heap Size (Estimated): 711.50M

Ergonomics Machine Class: server

Using VM: OpenJDK 64-Bit Server VM

openjdk version "1.8.0_151"

OpenJDK Runtime Environment (IcedTea 3.6.0) (Alpine 8.151.12-r0)

OpenJDK 64-Bit Server VM (build 25.151-b12, mixed mode)

~ % docker run -m 1800m -it --rm openjdk:8-jre-alpine java -XshowSettings:vm -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=2 -version

VM settings:

Max. Heap Size (Estimated): 800.00M

Ergonomics Machine Class: server

Using VM: OpenJDK 64-Bit Server VM

openjdk version "1.8.0_151"

OpenJDK Runtime Environment (IcedTea 3.6.0) (Alpine 8.151.12-r0)

OpenJDK 64-Bit Server VM (build 25.151-b12, mixed mode)

Java 10 이후

Java 10에서는 위의 이상한 옵션을 deprecation 시키고 percent 단위 옵션이 추가되었다. Java 8에 backport가 될지는 알 수 없으나 향후 사용이 가능해 진다면 이게 가장 좋은 옵션이다.

-XX:MaxRAMPercentage=80

그리고 Java 10 부터는 옵션을 사용하지 않아도 자동으로 cgroup의 정보를 사용한다. 따라서 다음과 같이 간단해 진다.

~ % docker run -m 512m -it --rm openjdk:10-jre java -XshowSettings:vm -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=2 -version

OpenJDK 64-Bit Server VM warning: Option UseCGroupMemoryLimitForHeap was deprecated in version 10.0 and will likely be removed in a future release.

OpenJDK 64-Bit Server VM warning: Option MaxRAMFraction was deprecated in version 10.0 and will likely be removed in a future release.

VM settings:

Max. Heap Size (Estimated): 247.50M

Using VM: OpenJDK 64-Bit Server VM

openjdk version "10.0.1" 2018-04-17

OpenJDK Runtime Environment (build 10.0.1+10-Debian-4)

OpenJDK 64-Bit Server VM (build 10.0.1+10-Debian-4, mixed mode)

~ % docker run -m 512m -it --rm openjdk:10-jre java -XshowSettings:vm -XX:MaxRAMPercentage=100 -version

VM settings:

Max. Heap Size (Estimated): 494.94M

Using VM: OpenJDK 64-Bit Server VM

openjdk version "10.0.1" 2018-04-17

OpenJDK Runtime Environment (build 10.0.1+10-Debian-4)

OpenJDK 64-Bit Server VM (build 10.0.1+10-Debian-4, mixed mode)

~ % docker run -m 512m -it --rm openjdk:10-jre java -XshowSettings:vm -XX:MaxRAMPercentage=50 -version

VM settings:

Max. Heap Size (Estimated): 247.50M

Using VM: OpenJDK 64-Bit Server VM

openjdk version "10.0.1" 2018-04-17

OpenJDK Runtime Environment (build 10.0.1+10-Debian-4)

OpenJDK 64-Bit Server VM (build 10.0.1+10-Debian-4, mixed mode)

~ % docker run -m 512m -it --rm openjdk:10-jre java -XshowSettings:vm -XX:MaxRAMPercentage=80 -version

VM settings:

Max. Heap Size (Estimated): 396.38M

Using VM: OpenJDK 64-Bit Server VM

openjdk version "10.0.1" 2018-04-17

OpenJDK Runtime Environment (build 10.0.1+10-Debian-4)

OpenJDK 64-Bit Server VM (build 10.0.1+10-Debian-4, mixed mode)

--

--

No responses yet