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 będzie dostępny w kontenerze. Następnie zastanów się, ile pamięci będzie dostępna 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 przydziału procesora CPU, który może ograniczać dostęp do procesorów CPU.
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 i rozmiar obszaru sterty dla maszyny JVM są tak ważne jak procesory. Te czynniki określają zachowanie modułu odśmiecania pamięci (GC) i ogólną wydajność 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 do 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ń. Maszyny JVM mają domyślną klawiaturę ze wstępnie zdefiniowanymi wartościami, które są oparte na liczbie dostępnych procesorów i iloś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 |
Procesory 2+ 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 |
Domyślny początkowy rozmiar sterty to 1/64 dostępnej pamięci.
Te wartości są prawidłowe dla zestawu OpenJDK 11 lub nowszego 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 będzie najlepiej obsługiwać 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.
Napiwek
Jeśli nie wiesz, ile pamięci należy przydzielić, dobrym punktem wyjścia jest 4 GB.
Określanie pamięci stert JVM
Podczas przydzielania pamięci stertowej JVM należy pamiętać, że JVM potrzebuje więcej pamięci niż tylko to, co jest używane na stercie JVM. Po ustawieniu maksymalnej pamięci stertowej JVM nigdy nie powinna być równa ilości pamięci kontenera, ponieważ spowoduje to awarie kontenera poza pamięcią (OOM, Container Out of Memory) i kontenera.
Napiwek
Przydziel 75% pamięci kontenera na stertę JVM.
W zestawie OpenJDK 11 lub nowszym można ustawić rozmiar sterty JVM na następujące sposoby:
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 zarezerwowanej dla wystąpienia JVM, takiego jak w kontenerze, należy ustawić minimalny rozmiar sterty — lub początkowy rozmiar sterty — na taki sam rozmiar, jak maksymalny rozmiar sterty. To ustawienie wskazuje maszynę wirtualną 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.
Określanie, którego GC użyć
Wcześniej określono ilość pamięci stertowej JVM na początek. Następnym krokiem jest wybranie 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.
Czynników | SerialGC | ParallelGC | G1GC | ZGC | ShenandoahGC |
---|---|---|---|---|---|
Liczba rdzeni | 1 | 2 | 2 | 2 | 2 |
Wielowątkowy | Nie. | Tak | Tak | Tak | Tak |
Rozmiar sterty java | <4 Gb/s | <4 Gb/s | >4 Gb/s | >4 Gb/s | >4 Gb/s |
Wstrzymanie | Tak | Tak | Tak | Tak (<1 ms) | Tak (<10 ms) |
Obciążenie | Minimalny | Minimalny | Umiarkowane | Umiarkowane | Umiarkowane |
Efekt opóźnienia końcowego | Wys. | Wys. | Wys. | Niski | Umiarkowane |
Wersja zestawu JDK | wszystkie | wszystkie | JDK 8+ | JDK 17+ | JDK 11+ |
Optymalne zastosowanie | Mała sterta z pojedynczym rdzeniem | Wielordzeniowe małe sterty lub obciążenia wsadowe z dowolnym rozmiarem sterty | Odpowiadanie w średnich i dużych stertach (interakcja z żądaniem/bazą danych) | Odpowiadanie w średnich i dużych stertach (interakcja z żądaniem/bazą danych) | Odpowiadanie w średnich i dużych stertach (interakcja z żądaniem/bazą danych) |
Napiwek
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 procesorów wirtualnych — lub co najmniej 2000m
cpu_limit na platformie Kubernetes. Nie zalecamy wybierania mniej niż 1 rdzenia procesorów wirtualnych w środowiskach konteneryzowanych.
Napiwek
Jeśli nie wiesz, ile rdzeni należy zacząć od, dobrym wyborem jest 2 rdzenie procesorów wirtualnych.
Wybierz punkt początkowy
Zalecamy rozpoczęcie od dwóch replik lub wystąpień w środowiskach orkiestracji kontenerów, takich jak Kubernetes, OpenShift, Azure Spring Apps, Azure Container Apps i aplikacja systemu Azure Service. Poniższa tabela zawiera podsumowanie zalecanych punktów początkowych dla konteneryzacji nowej aplikacji Java.
Rdzenie procesorów wirtualnych | Pamięć kontenera | Rozmiar sterty JVM | GC | Repliki |
---|---|---|---|---|
2 | 4 GB | 75% | ParallelGC | 2 |
Parametry maszyny JVM do użycia to: -XX:+UseParallelGC -XX:MaxRAMPercentage=75
Konteneryzowanie istniejącej (lokalnej) aplikacji
Jeśli aplikacja jest już uruchomiona lokalnie lub na maszynie wirtualnej w chmurze, zalecamy rozpoczęcie od:
- Ta sama ilość pamięci, do którego aplikacja ma obecnie dostęp.
- Ta sama liczba procesorów CPU (rdzeni procesorów wirtualnych) jest obecnie dostępna dla aplikacji.
- Te same parametry JVM, których obecnie używasz.
Jeśli kombinacja rdzeni procesorów wirtualnych i/lub pamięci kontenera jest niedostępna, wybierz najbliższą, zaokrąglając rdzenie procesorów wirtualnych i pamięć kontenera.
Następne 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: