Udostępnij za pomocą


Konteneryzowanie aplikacji Java

Ten artykuł zawiera omówienie zalecanych strategii i ustawień konteneryzowania aplikacji Java. Podczas konteneryzowania aplikacji Java należy dokładnie zastanowić się, ile czasu procesora CPU ma dostępny kontener. Następnie zastanów się, ile pamięci jest dostępne zarówno pod względem całkowitej ilości pamięci, jak i rozmiar sterty maszyny wirtualnej Java (JVM). W środowiskach konteneryzowanych aplikacje mogą mieć dostęp do wszystkich procesorów i dlatego mogą równolegle uruchamiać wiele wątków. Często jednak kontenery mają zastosowany limit procesora, który może ograniczać dostęp do procesorów.

Maszyny JVM mają heurystyki umożliwiające określenie liczby "dostępnych procesorów" na podstawie limitu przydziału procesora CPU, co może znacząco wpłynąć na wydajność aplikacji Java. Pamięć przydzielona do samego kontenera oraz rozmiar obszaru sterty dla maszyny wirtualnej Java (JVM) są równie ważne jak procesory. Czynniki te określają zachowanie kolektora śmieci (GC) i wydajność całego systemu.

Konteneryzowanie nowej aplikacji

Podczas konteneryzacji obciążenia Java dla nowej aplikacji należy wziąć pod uwagę dwie kwestie podczas myślenia o pamięci:

  • Pamięć przydzielona dla samego kontenera.
  • Ilość pamięci dostępnej dla procesu Java.

Omówienie domyślnej anatomii maszyny wirtualnej JVM

Aplikacje wymagają punktu początkowego i ustawień. JVM ma domyślną ergonomię ze wstępnie zdefiniowanymi wartościami opartymi na liczbie dostępnych procesorów i wielkości pamięci w systemie. Wartości domyślne wyświetlane w poniższych tabelach są używane podczas uruchamiania maszyny wirtualnej JVM bez określonych flag uruchamiania lub parametrów.

W poniższej tabeli przedstawiono domyślną GC używaną dla dostępnych zasobów:

Dostępne zasoby Domyślna GC
Dowolna liczba procesorów
Do 1791 MB pamięci
SerialGC
2 lub więcej procesorów
1792 MB lub więcej pamięci
G1GC

W poniższej tabeli przedstawiono domyślny maksymalny rozmiar sterty w zależności od ilości pamięci dostępnej w środowisku, w którym działa maszyna wirtualna JVM:

Dostępna pamięć Domyślny maksymalny rozmiar sterty
Do 256 MB 50% dostępnej pamięci
Od 256 MB do 512 MB ~127 MB
Więcej niż 512 MB 25% dostępnej pamięci RAM

Domyślny początkowy rozmiar sterty to 1/64 dostępnej pamięci. Te wartości są ważne dla OpenJDK 11 i nowszych — oraz dla większości dystrybucji, w tym Microsoft Build of OpenJDK, Azul Zulu, Eclipse Temurin, Oracle OpenJDK i innych.

Określanie pamięci kontenera

Wybierz ilość pamięci kontenera, która najlepiej obsługuje obciążenie pracy, w zależności od potrzeb aplikacji i jej charakterystycznych wzorców użycia. Jeśli na przykład aplikacja tworzy duże grafy obiektów, prawdopodobnie potrzebujesz więcej pamięci niż w przypadku aplikacji z wieloma małymi grafami obiektów.

Wskazówka

Jeśli nie wiesz, ile pamięci należy przydzielić, dobrym punktem wyjścia jest 4 GB.

Określanie pamięci stert JVM

W przypadku przydzielenia pamięci stertowej JVM maszyny JVM potrzebuje więcej pamięci niż to, co jest używane na stercie JVM. Ustawiając maksymalną pamięć stertową JVM, nie powinna ona nigdy być równa ilości pamięci kontenera, ponieważ powoduje to błędy braku pamięci (OOM) i awarie kontenera.

Wskazówka

Przydziel 75% pamięci kontenera na stertę JVM.

Od OpenJDK 11 i nowszych można ustawić rozmiar sterty JVM w następujący sposób:

Opis Flaga Przykłady
Stała wartość -Xmx -Xmx4g
Wartość dynamiczna -XX:MaxRAMPercentage -XX:MaxRAMPercentage=75

Minimalny/początkowy rozmiar sterty

Jeśli środowisko ma zagwarantowaną określoną ilość pamięci zarezerwowaną dla instancji JVM, na przykład w kontenerze, powinieneś ustawić minimalny lub początkowy rozmiar stosu na taki sam, jak maksymalny rozmiar stosu. To ustawienie informuje JVM, że nie powinno wykonywać zadania zwalniania pamięci do systemu operacyjnego.

Aby ustawić minimalny rozmiar sterty, użyj wartości -Xms bezwzględnych lub -XX:InitialRAMPercentage wartości procentowych.

Ważne

Flaga -XX:MinRAMPercentage, pomimo tego, co sugeruje nazwa, jest używana do ustawiania domyślnej maksymalnej wartości procentowej pamięci RAM dla systemów z maksymalnie 256 MB pamięci RAM dostępnej w systemie.

Wykres przedstawiający domyślny rozmiar sterty w OpenJDK 17.

Określić, który GC użyć

Wcześniej określiłeś, ile pamięci stertowej JVM należy przydzielić na początek. Następnym krokiem jest wybranie swojego GC. Maksymalna ilość pamięci stertowej JVM, którą masz, jest często czynnikiem podczas wybierania GC. W poniższej tabeli opisano cechy każdej GC.

Czynniki SerialGC ParallelGC G1GC ZGC ShenandoahGC
Liczba rdzeni 1 2 2 2 2
Wielowątkowy Nie. Tak Tak Tak Tak
Rozmiar sterty Java <4 GB <4 GB >4 GB >4 GB >4 GB
Wstrzymanie Tak Tak Tak Tak (<1 ms) Tak (<10 ms)
Koszty ogólne Minimalny Minimalny Umiarkowane Umiarkowane Umiarkowane
Efekt opóźnienia końcowego Wysoki Wysoki Wysoki Niski Umiarkowane
Wersja zestawu JDK Wszystko Wszystko JDK 8+ JDK 17+ JDK 11+
Najlepsze dla Mała sterta z pojedynczym rdzeniem Wielordzeniowe małe sterty lub obciążenia wsadowe z dowolnym rozmiarem sterty Responsywność w pracy ze średnimi i dużymi stosami (interakcje żądanie-odpowiedź/bazodanowe) Responsywność w pracy ze średnimi i dużymi stosami (interakcje żądanie-odpowiedź/bazodanowe) Responsywność w pracy ze średnimi i dużymi stosami (interakcje żądanie-odpowiedź/bazodanowe)

Wskazówka

W przypadku większości aplikacji mikrousług ogólnego przeznaczenia zacznij od równoległego GC.

Określanie, ile rdzeni procesora CPU jest potrzebnych

W przypadku dowolnego GC innego niż SerialGC zalecamy co najmniej dwa rdzenie wirtualne procesora — lub przynajmniej 2000m w przypadku cpu_limit na platformie Kubernetes. Nie zalecamy wybierania mniej niż jednego rdzenia procesorów wirtualnych w środowiskach konteneryzowanych.

Wskazówka

Jeśli nie wiesz, od ilu rdzeni vCPU zacząć, dobrym wyborem są dwa rdzenie.

Wybierz punkt początkowy

Zalecamy rozpoczęcie od dwóch replik lub instancji w środowiskach orkiestracji kontenerów, takich jak Kubernetes, OpenShift, Azure Spring Apps, Azure Container Apps i Azure App Service. Poniższa tabela zawiera podsumowanie zalecanych punktów początkowych dla konteneryzacji nowej aplikacji Java.

Rdzenie vCPU Pamięć kontenera Rozmiar sterty JVM GC Repliki
2 4 GB 75% ParallelGC 2

Użyj następujących parametrów JVM:

-XX:+UseParallelGC -XX:MaxRAMPercentage=75

Konteneryzowanie istniejącej aplikacji lokalnej

Jeśli aplikacja jest już uruchomiona lokalnie lub na maszynie wirtualnej w chmurze, zalecamy rozpoczęcie od następującej konfiguracji:

  • Ta sama ilość pamięci, do której aplikacja ma obecnie dostęp.
  • Ta sama liczba procesorów CPU lub rdzeni wirtualnych CPU, które aplikacja ma obecnie dostępne.
  • Te same parametry JVM, których obecnie używasz.

Jeśli dostępna kombinacja rdzeni procesorów wirtualnych lub pamięci kontenera nie istnieje, wybierz najbliższą dostępną opcję, zaokrąglając w górę liczbę rdzeni procesorów wirtualnych i pamięć kontenera.

Dalsze kroki

Teraz, po zapoznaniu się z ogólnymi zaleceniami dotyczącymi konteneryzowania aplikacji Java, przejdź do następującego artykułu, aby ustanowić punkt odniesienia konteneryzacji: