fragen stichworte

Java-Speicherverhalten in Docker

Unser aktueller Kubernetes-Cluster, der in der Google Container Engine (GKE) ausgeführt wird, besteht aus n1-standard-1 -Maschinentypen (1 virtuelle CPU und 3,75 GB Arbeitsspeicher), auf denen Debian GNU/Linux 7.9 (Wheezy) ausgeführt wird, die von Google verwaltet wird. Aufgrund der erhöhten Last und des Speicherverbrauchs unserer Services müssen wir unsere Knoten auf einen größeren Maschinentyp aufrüsten. Beim Testen in unserem Testcluster haben wir etwas erlebt, das (für uns) ziemlich seltsam erscheint.

Der von der JVM-Anwendung bei der Bereitstellung auf einem Google-Knoten verbrauchte Speicher scheint der Anzahl der auf dem Knoten verfügbaren Kerne proportional zu sein. Selbst wenn der JVM-Max-Speicher (Xmx) auf 128 MB gesetzt ist, verbraucht er auf einer 1-Core-Maschine etwa 250 MB (dies ist verständlich, da die JVM aufgrund von GC mehr Speicher benötigt als die Höchstgrenze, die JVM selbst usw.), jedoch ungefähr 700 MB auf einer 2-Kern-Maschine (n1-standard-2) und etwa 1,4 GB auf einer 4-Kern-Maschine (n1-standard-4). Das einzige, was sich unterscheidet, ist der Maschinentyp, es werden genau das gleiche Docker-Image und Konfigurationen verwendet.

Wenn ich zum Beispiel eine SSH in eine Maschine mit einem n1-standard-4 Maschinentyp einführe und sudo docker stats <container_name> starte, erhalte ich Folgendes:

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 

Wenn ich das gleiche Docker-Image mit der genauen gleichen (Anwendungs) -Konfiguration lokal (Mac OSX und Docker-Maschine) starte, sehe ich:

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         

Was viel mehr im Einklang mit dem ist, was ich aufgrund der Einstellung von Xmx erwarten würde (für das Protokoll habe ich 8 Kerne und 16 GB Speicher). Dasselbe wird bestätigt, wenn ich top -p <pid> auf der GKE-Instanz ausführte, was mir eine RES/RSS-Speicherzuordnung von 1,1 bis 1,4 Gb gibt.

Das Docker-Image wird wie folgt definiert:

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

Ich habe auch folgendes hinzugefügt:

ENV MALLOC_ARENA_MAX 4

was ich in mehreren Threads empfohlen habe, wie this, aber es scheint keinen Unterschied zu machen, was auch immer so ist. Ich habe auch versucht, zu einem anderen Java-Basis-Image zu wechseln und alpines Linux zu verwenden, aber dies scheint auch nichts zu ändern.

Meine lokale Docker-Version ist 1.11.1 und die Docker-Version in Kubernetes/GKE ist 1.9.1. Die Kubernetes-Version (wenn es darauf ankommt) ist v1.2.4.

Interessant ist auch, dass bei der Bereitstellung mehrerer Instanzen des Pods/Containers auf demselben Computer/Knoten bei einigen Instanzen viel weniger Speicher zugewiesen wird. Zum Beispiel können die ersten drei Speicher 1,1-1,4 GB Speicher zuweisen, aber dann ordnen die 10 nachfolgenden Container jeweils nur etwa 250 MB zu, was ungefähr der Größe entspricht, die jeder -Instanz zuzuteilen wäre. Das Problem ist, dass, wenn wir die Speicherbegrenzung der Maschine erreichen, die ersten drei Instanzen (diejenigen, die 1,1 GB Speicher zuweisen) nie den zugewiesenen Speicher freigeben. Wenn sie den Speicher freigeben würden, wenn die Maschine unter erhöhtem Druck steht, würde ich mir keine Sorgen machen, aber da sie die Speicherzuordnung beibehalten, wird sie auch beim Laden der Maschine zu einem Problem (da andere Container auf dieser Maschine nicht geplant werden können) somit werden Ressourcen verschwendet).

Fragen:

  1. Was könnte dieses Verhalten verursachen? Ist es ein Problem der JVM? Docker-Problem? VM Problem? Linux Problem? Konfigurationsproblem Oder vielleicht eine Kombination?
  2. Was kann ich in diesem Fall tun, um die Speicherzuordnung der JVM zu begrenzen?

antworten

Wenn Sie

angeben  
CMD java -jar/data/uberjar.jar -Xmx128m -server

Dann werden die Werte, die Sie als JVM-Argumente (-Xmx128m -server) verwenden möchten, als Befehlszeilenargumente an die Main-Klasse in der JAR-Datei übergeben. Sie sind als Argumente in Ihrer public static void main(String... args) -Methode verfügbar.

Beachten Sie, dass dies auch gilt, wenn Sie eine Hauptklasse nach Namen ausführen, anstatt eine ausführbare jar -Datei anzugeben.

Um sie als JVM-Argumente anstelle von Argumenten an Ihr Programm zu übergeben, müssen Sie sie vor dem Argument -jar angeben.

Siehe https://stackoverflow.com/questions/5536476/passing-arguments-to-jar-which-is-required-by-java-interpreter

Das Problem war mit den JVM-Einstellungen, die in Dockerfile angegeben sind. Wir haben unsere JVM so gestartet:

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

Aber wenn wir zu:

gewechselt haben  
CMD java -Xmx128m -server -jar/data/uberjar.jar

Die Einstellungen wurden berücksichtigt. Ich verstehe immer noch nicht, warum ich das nicht lokal gesehen habe, aber ich bin nur froh, dass wir es gelöst haben.