web-dev-qa-db-ja.com

DockerでのJavaメモリの動作を理解する

Google Container Engine(GKE)で実行されている現在のKubernetesクラスターは、Googleが管理しているDebian GNU/Linux 7.9(wheezy)を実行しているn1-standard-1マシンタイプ(1つの仮想CPUと3.75 GBのメモリ)で構成されています。サービスの負荷とメモリ使用量が増加しているため、ノードをより大きなマシンタイプにアップグレードする必要があります。私たちのテストクラスターでこれを試している間、私たちは(私たちにとって)非常に奇妙に見える何かを経験しました。

GoogleノードにデプロイされたときにJVMアプリケーションによって消費されるメモリは、ノードで使用可能なコアの数に比例しているようです。 JVMの最大メモリ(Xmx)を128Mbに設定しても、1コアマシンで約250Mbを消費します(これは、JVMがGCやJVM自体などのために最大制限よりも多くのメモリを消費するため、理解できます)。 2コアマシン(n1-standard-2)で700Mb、4コアマシン(n1-standard-4)で約1.4Gb。唯一異なるのはマシンタイプであり、まったく同じDockerイメージと構成が使用されます。

たとえば、n1-standard-4マシンタイプを使用してマシンにSSHで接続し、Sudo docker stats <container_name>を実行すると、次のようになります。

CONTAINER CPU %               MEM USAGE / LIMIT    MEM %               NET I/O             BLOCK I/O
k8s.name  3.84%               1.11 GB / 1.611 GB   68.91%              0 B / 0 B 

sameDockerイメージを正確なsame(アプリケーション)構成でローカルに実行すると(mac osxおよびdocker-machine)が表示されます:

CONTAINER CPU %               MEM USAGE / LIMIT    MEM %               NET I/O               BLOCK I/O
name      1.49%               236.6 MB / 1.044 GB  22.66%              25.86 kB / 14.84 kB   0 B / 0 B         

これは、Xmx設定により、私が期待するものとはるかに調和しています(レコードの場合、8コアと16Gbのメモリがあります)。 GKEインスタンスでtop -p <pid>を実行すると、同じことが確認されます。これにより、1.1〜1.4GbのRES/RSSメモリ割り当てが得られます。

Dockerイメージは次のように定義されます。

FROM Java:8u91-jre
EXPOSE 8080
EXPOSE 8081

ENV Java_TOOL_OPTIONS -Dfile.encoding=UTF-8

# Add jar
ADD uberjar.jar /data/uberjar.jar

CMD Java -jar /data/uberjar.jar -Xmx128m -server

私も追加してみました:

ENV MALLOC_ARENA_MAX 4

this などのいくつかのスレッドで推奨されているのを見てきましたが、これまでのところ何の違いもないようです。また、別のJavaベースイメージに変更したり、Alpine Linuxを使用したりしてみましたが、これでも状況は変わらないようです。

ローカルのDockerバージョンは1.11.1で、Kubernetes/GKEのDockerバージョンは1.9.1です。 Kubernetesのバージョン(重要な場合)はv1.2.4です。

また興味深いのは、ポッド/コンテナの複数のインスタンスを同じマシン/ノードにデプロイすると、一部のインスタンスが割り当てるメモリがはるかに少なくなることです。たとえば、最初の3つは1.1〜1.4Gbのメモリを割り当てますが、後続の10個のコンテナはそれぞれ約250 Mbしか割り当てません。これは、すべてのインスタンスが割り当てるとほぼ予想される量です。問題は、マシンのメモリ制限に達した場合、最初の3つのインスタンス(1.1Gbメモリを割り当てているインスタンス)が割り当てられたメモリを解放していないように見えることです。マシンの圧力が高いときにメモリを解放する場合、これについては心配しませんが、マシンがロードされてもメモリ割り当てを保持するため、問題になります(このマシンで他のコンテナをスケジュールすることが禁止されているため、したがって、リソースが無駄になります)。

質問:

  1. この動作の原因は何ですか?それはJVMの問題ですか? Dockerの問題? VM問題?Linuxの問題?構成の問題?それとも組み合わせ?
  2. この場合、JVMのメモリ割り当てを制限するために何を試みることができますか?
2
Johan

問題は、Dockerfileで指定されたJVM設定にありました。次のようにJVMを開始しました。

CMD Java -jar /data/uberjar.jar -Xmx128m -server

しかし、切り替えたとき:

CMD Java -Xmx128m -server -jar /data/uberjar.jar

設定が考慮されました。なぜこれをローカルで見なかったのかはまだわかりませんが、なんとか解決できて良かったです。

1
Johan

指定する場合

_CMD Java -jar /data/uberjar.jar -Xmx128m -server
_

次に、JVM引数(_-Xmx128m -server_)にする予定の値が、コマンドライン引数として.jarファイルのMainクラスに渡されます。これらは、public static void main(String... args)メソッドの引数として使用できます。

これは、実行可能なjarファイルを指定するのではなく、名前でメインクラスを実行している場合にも当てはまることに注意してください。

それらをプログラムへの引数ではなくJVM引数として渡すには、_-jar_引数の前にそれらを指定する必要があります。

https://stackoverflow.com/questions/5536476/passing-arguments-to-jar-which-is-required-by-Java-interpreter を参照してください

4
erk