Migrowanie aplikacji serwera Tomcat do kontenerów w usłudze Azure Kubernetes Service

W tym przewodniku opisano, na co należy zwrócić uwagę, aby przeprowadzić migrację istniejącej aplikacji serwera Tomcat w celu uruchomienia jej w usłudze Azure Kubernetes Service (AKS).

Przed migracją

Aby zapewnić pomyślną migrację, przed rozpoczęciem wykonaj kroki oceny i spisu opisane w poniższych sekcjach.

Utworzenie spisu zasobów zewnętrznych

Zasoby zewnętrzne, takie jak źródła danych, brokery komunikatów JMS i inne, są wstrzykiwane za pośrednictwem interfejsu Java Naming and Directory Interface (JNDI). Niektóre takie zasoby mogą wymagać migracji lub ponownej konfiguracji.

W aplikacji

Sprawdź plik META-INF/context.xml. Poszukaj elementów <Resource> wewnątrz elementu <Context>.

Na serwerach aplikacji

Sprawdź pliki $CATALINA_BASE/conf/context.xml i $CATALINA_BASE/conf/server.xml oraz pliki .xml w katalogach $CATALINA_BASE/conf/[nazwa-aparatu]/[nazwa-hosta].

W plikach context.xml zasoby JNDI będą opisywane przez elementy <Resource> w elemencie <Context> najwyższego poziomu.

W plikach server.xml zasoby JNDI będą opisywane przez elementy <Resource> w elemencie <GlobalNamingResources>.

Źródła danych

Źródła danych to zasoby JNDI z atrybutem type ustawionym na javax.sql.DataSource. Dla każdego źródła danych należy udokumentować następujące informacje:

  • Jaka jest nazwa źródła danych?
  • Jaka jest konfiguracja puli połączeń?
  • Gdzie mogę znaleźć plik JAR sterownika JDBC?

Aby uzyskać więcej informacji, zobacz przewodnik JNDI Datasource How-To (Źródło danych JNDI — porady) w dokumentacji serwera Tomcat.

Wszystkie inne zasoby zewnętrzne

Nie jest możliwe udokumentowanie każdej możliwej zależności zewnętrznej w tym przewodniku. Twój zespół odpowiada za sprawdzenie, czy każda zależność zewnętrzna aplikacji może być spełniona po migracji.

Utworzenie spisu wpisów tajnych

Hasła i bezpieczne ciągi

Sprawdź wszystkie pliki właściwości i konfiguracji na serwerach produkcyjnych pod kątem wszelkich ciągów wpisów tajnych i haseł. Sprawdź pliki server.xml i context.xml w katalogu $CATALINA_BASE/conf. Możesz również znaleźć pliki konfiguracji zawierające hasła lub poświadczenia wewnątrz aplikacji. Mogą one obejmować plik META-INF/context.xml oraz, w przypadku aplikacji Spring Boot, pliki application.properties lub application.yml.

Określanie, czy i jak jest używany system plików

Każde użycie systemu plików na serwerze aplikacji będzie wymagało ponownej konfiguracji lub, w rzadkich przypadkach, zmiany architektury. Można zidentyfikować niektóre lub wszystkie z następujących scenariuszy.

Zawartość statyczna tylko do odczytu

Jeśli aplikacja aktualnie obsługuje zawartość statyczną, potrzebna jest dodatkowa lokalizacja. Warto rozważyć przeniesienie zawartości statycznej do usługi Azure Blob Storage i dodanie usługi Azure CDN, aby zapewnić błyskawiczne pobieranie na całym świecie. Aby uzyskać więcej informacji, zobacz Hostowanie statycznej witryny internetowej w usłudze Azure Storage i Szybki start: integrowanie konta usługi Azure Storage z usługą Azure CDN. Możesz również bezpośrednio wdrożyć zawartość statyczną w aplikacji w planie Azure Spring Apps Enterprise. Aby uzyskać więcej informacji, zobacz Wdrażanie internetowych plików statycznych.

Dynamicznie publikowana zawartość statyczna

Jeśli aplikacja zezwala na zawartość statyczną, która została przekazana/utworzona przez aplikację, ale pozostaje niezmienna po jej utworzeniu, możesz użyć usług Azure Blob Storage i Azure CDN, jak opisano powyżej, oraz usługi Azure Function do obsługiwania przekazywania i odświeżania usługi CDN. Udostępniliśmy przykładową implementację do użycia w temacie Przekazywanie zawartości statycznej i jej wstępne ładowanie w usłudze CDN za pomocą usługi Azure Functions. Możesz również bezpośrednio wdrożyć zawartość statyczną w aplikacji w planie Azure Spring Apps Enterprise. Aby uzyskać więcej informacji, zobacz Wdrażanie internetowych plików statycznych.

Zawartość dynamiczna lub wewnętrzna

Na potrzeby plików często zapisywanych i odczytywanych przez aplikację (na przykład plików danych tymczasowych) lub plików statycznych, które są widoczne tylko dla aplikacji, możesz zainstalować udziały plików usługi Azure Storage jako trwałe woluminy. Aby uzyskać więcej informacji, zobacz Dynamiczne tworzenie i korzystanie z trwałego woluminu za pomocą usługi Azure Files w usłudze Azure Kubernetes Service.

Określanie mechanizmu trwałości sesji

Aby zidentyfikować używanego menedżera trwałości sesji, sprawdź pliki context.xml w aplikacji i konfiguracji serwera Tomcat. Poszukaj elementu <Manager>, a następnie zanotuj wartość atrybutu className.

Wbudowane na serwerze Tomcat implementacje aplikacji PersistentManager, na przykład StandardManager czy FileStore, nie są przeznaczone do użycia z rozproszoną, skalowaną platformą, taką jak usługa Kubernetes. Usługa AKS może równoważyć obciążenie między kilkoma zasobnikami i w dowolnym momencie w niewidoczny sposób uruchomić ponownie dowolny zasobnik, więc nie zaleca się utrwalania modyfikowalnego stanu w systemie plików.

Jeśli wymagana jest trwałość sesji, należy użyć alternatywnej PersistentManager implementacji, która będzie zapisywać w zewnętrznym magazynie danych, takim jak VMware Tanzu Session Manager z pamięcią podręczną Redis Cache. Aby uzyskać więcej informacji, zobacz Korzystanie z usługi Redis jako pamięci podręcznej sesji na serwerze Tomcat.

Przypadki szczególne

Niektóre scenariusze produkcyjne mogą wymagać dodatkowych zmian lub wprowadzać dodatkowe ograniczenia. Chociaż takie scenariusze mogą być rzadko spotykane, ważne jest, aby upewnić się, że nie dotyczą aplikacji lub są prawidłowo obsługiwane.

Określanie, czy aplikacja korzysta z zaplanowanych zadań

Nie można używać zaplanowanych zadań, takich jak zadania Quartz Scheduler lub cron, z konteneryzowanymi wdrożeniami usługi Tomcat. Jeśli aplikacja jest skalowana w poziomie, jedno zaplanowane zadanie może być uruchamiane więcej niż jeden raz w zaplanowanym okresie. Ta sytuacja może prowadzić do niezamierzonych konsekwencji.

Utwórz spis wszystkich zaplanowanych zadań, wewnątrz lub na zewnątrz serwera aplikacji.

Określanie, czy aplikacja zawiera kod właściwy dla systemu operacyjnego

Jeśli aplikacja zawiera dowolny kod uwzględniający system operacyjny, w którym jest uruchomiona aplikacja, należy refaktoryzować aplikację, aby NIE POLEGAŁA na bazowym systemie operacyjnym. Na przykład może być konieczne zastąpienie wszystkich przypadków użycia elementów / lub \ w ścieżkach systemu plików za pomocą elementów File.Separator lub Path.get.

Określanie, czy jest używana klasa MemoryRealm

Klasa MemoryRealm wymaga utrwalonego pliku XML. W usłudze Kubernetes ten plik musi zostać dodany do obrazu kontenera lub przekazany do udostępnionego magazynu, który jest dostępny dla kontenerów. Należy odpowiednio zmodyfikować parametr pathName.

Aby ustalić, czy klasa MemoryRealm jest aktualnie używana, sprawdź, czy w plikach server.xml i context.xml znajdują się elementy <Realm>, których atrybut className jest ustawiony na org.apache.catalina.realm.MemoryRealm.

Określanie, czy jest używane śledzenie sesji SSL

W przypadku konteneryzowanych wdrożeń sesje SSL są zwykle odciążane poza kontenerem aplikacji, najczęściej przez kontroler ruchu przychodzącego. Jeśli aplikacja wymaga śledzenia sesji SSL, upewnij się, że ruch SSL jest bezpośrednio przesyłany do kontenera aplikacji.

Określanie, czy jest używana klasa AccessLogValve

W przypadku użycia klasy AccessLogValve parametr directory należy ustawić na zainstalowany udział plików usługi Azure Files lub jeden z jego podkatalogów.

Testowanie w miejscu

Przed utworzeniem obrazów kontenerów dokonaj migracji aplikacji do zestawu JDK i serwera Tomcat, których zamierzasz używać w usłudze AKS. Dokładnie przetestuj swoją aplikację, aby zapewnić jej zgodność i wydajność.

Parametryzacja konfiguracji

Podczas wstępnej migracji w plikach server.xml i context.xml prawdopodobnie zostały zidentyfikowane klucze tajne i zależności zewnętrzne, takie jak źródła danych. W przypadku każdego zidentyfikowanego w ten sposób elementu zastąp wszelkie nazwy użytkownika, hasła, parametry połączenia lub adresy URL zmienną środowiskową.

Na przykład załóżmy, że plik context.xml zawiera następujący element:

<Resource
    name="jdbc/dbconnection"
    type="javax.sql.DataSource"
    url="jdbc:postgresql://postgresdb.contoso.com/wickedsecret?ssl=true"
    driverClassName="org.postgresql.Driver"
    username="postgres"
    password="t00secure2gue$$"
/>

W takim przypadku można go zmienić w sposób pokazany w następującym przykładzie:

<Resource
    name="jdbc/dbconnection"
    type="javax.sql.DataSource"
    url="${postgresdb.connectionString}"
    driverClassName="org.postgresql.Driver"
    username="${postgresdb.username}"
    password="${postgresdb.password}"
/>

Migracja

Z wyjątkiem pierwszego kroku („Aprowizacja rejestru kontenerów i usługi AKS”) zalecamy wykonanie poniższych czynności osobno dla każdej aplikacji (plik WAR), którą chcesz migrować.

Uwaga

Niektóre wdrożenia produktu Tomcat mogą mieć wiele aplikacji uruchomionych na jednym serwerze Tomcat. Jeśli tak jest w tym wdrożeniu, zdecydowanie zalecamy uruchomienie każdej aplikacji w oddzielnym zasobniku. Pozwala to zoptymalizować wykorzystanie zasobów dla każdej aplikacji przy jednoczesnym zminimalizowaniu złożoności i sprzężenia klas.

Aprowizacja rejestru kontenerów i usługi AKS

Utwórz rejestr kontenerów i klaster usługi Azure Kubernetes, którego nazwa główna usługi ma rolę czytelnika w rejestrze. Upewnij się, że wybrano odpowiedni model sieci dopasowany do wymagań w zakresie łączności sieciowej klastra.

az group create \
    --resource-group $resourceGroup \
    --location eastus
az acr create \
    --resource-group $resourceGroup \
    --name $acrName \
    --sku Standard
az aks create \
    --resource-group $resourceGroup \
    --name $aksName \
    --attach-acr $acrName \
    --network-plugin azure

Przygotowanie komputera do wdrożenia

Sklonuj repozytorium GitHub z przewodnika szybkiego startu „Tomcat w kontenerach”. Repozytorium zawiera pliki konfiguracji Dockerfile i Tomcat z wieloma zalecanymi optymalizacjami. W poniższych krokach przedstawimy zmiany, które prawdopodobnie trzeba będzie wprowadzić w tych plikach przed skompilowaniem obrazu kontenera i wdrożeniem go do usługi AKS.

Otwieranie portów dla klastrowania, jeśli jest konieczne

Jeśli zamierzasz używać klastrowania Tomcat w usłudze AKS, upewnij się, że wymagane zakresy portów są uwidocznione w pliku Dockerfile. Aby określić adres IP serwera w pliku server.xml, należy użyć wartości ze zmiennej, która jest inicjowana przy uruchamianiu kontenera pod adresem IP zasobnika.

Alternatywnie stan sesji można utrwalić w lokalizacji alternatywnej, aby był dostępny w różnych replikach.

Aby ustalić, czy aplikacja używa klastrowania, poszukaj elementu <Cluster> wewnątrz elementów <Host> lub <Engine> w pliku server.xml.

Dodawanie zasobów JNDI

Edytuj plik server.xml, aby dodać zasoby przygotowane w ramach kroków poprzedzających migrację, takie jak źródła danych.

Na przykład:

<!-- Global JNDI resources
      Documentation at /docs/jndi-resources-howto.html
-->
<GlobalNamingResources>
    <!-- Editable user database that can also be used by
         UserDatabaseRealm to authenticate users
    -->
    <Resource name="UserDatabase" auth="Container"
              type="org.apache.catalina.UserDatabase"
              description="User database that can be updated and saved"
              factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
              pathname="conf/tomcat-users.xml"
               />

    <!-- Migrated datasources here: -->
    <Resource
        name="jdbc/dbconnection"
        type="javax.sql.DataSource"
        url="${postgresdb.connectionString}"
        driverClassName="org.postgresql.Driver"
        username="${postgresdb.username}"
        password="${postgresdb.password}"
    />
    <!-- End of migrated datasources -->
</GlobalNamingResources>

Aby uzyskać dodatkowe instrukcje dotyczące źródła danych, zapoznaj się z następującymi sekcjami przewodnika JNDI Datasource How-To (Źródło danych JNDI — porady) w dokumentacji serwera Tomcat:

Kompilacja i wypychanie obrazu

Najprostszym sposobem kompilowania i przekazywania obrazu do usługi Azure Container Registry (ACR) do użycia przez usługę AKS jest zastosowanie polecenia az acr build. To polecenie nie wymaga, aby platforma Docker była zainstalowana na komputerze. Na przykład jeśli masz powyższy plik Dockerfile oraz pakiet aplikacji petclinic.war w bieżącym katalogu, możesz utworzyć obraz kontenera w usłudze ACR przy użyciu jednego kroku:

az acr build \
    --image "${acrName}.azurecr.io/petclinic:{{.Run.ID}}" \
    --registry $acrName \
    --build-arg APP_FILE=petclinic.war \
    --build-arg=prod.server.xml .

Możesz pominąć parametr --build-arg APP_FILE..., jeśli plik WAR ma nazwę ROOT.war. Możesz pominąć parametr --build-arg SERVER_XML..., jeśli plik XML serwera ma nazwę server.xml. Oba pliki muszą znajdować się w tym samym katalogu co plik Dockerfile.

Można też użyć interfejsu wiersza polecenia platformy Docker, aby utworzyć obraz lokalnie. Takie podejście może uprościć testowanie i udoskonalanie obrazu przed początkowym wdrożeniem w usłudze ACR. Jednak wymaga instalacji interfejsu wiersza polecenia platformy Docker i uruchomienia demona platformy Docker.

# Build the image locally
sudo docker build . --build-arg APP_FILE=petclinic.war -t "${acrName}.azurecr.io/petclinic:1"

# Run the image locally
sudo docker run -d -p 8080:8080 "${acrName}.azurecr.io/petclinic:1"

# Your application can now be accessed with a browser at http://localhost:8080.

# Log into ACR
sudo az acr login --name $acrName

# Push the image to ACR
sudo docker push "${acrName}.azurecr.io/petclinic:1"

Aby uzyskać więcej informacji, zobacz moduł Learn dotyczący tworzenia i przechowywania obrazów kontenerów na platformie Azure.

Aprowizacja publicznego adresu IP

Jeśli aplikacja ma być dostępna spoza sieci wewnętrznej lub wirtualnej, będzie wymagany publiczny, statyczny adres IP. Ten adres IP powinien być aprowizowany wewnątrz grupy zasobów węzła klastra.

export nodeResourceGroup=$(az aks show \
    --resource-group $resourceGroup \
    --name $aksName \
    --query 'nodeResourceGroup' \
    --output tsv)
export publicIp=$(az network public-ip create \
    --resource-group $nodeResourceGroup \
    --name applicationIp \
    --sku Standard \
    --allocation-method Static \
    --query 'publicIp.ipAddress' \
    --output tsv)
echo "Your public IP address is ${publicIp}."

Wdrażanie w usłudze AKS

Twórz i stosuj pliki Kubernetes YAML. Jeśli tworzysz zewnętrzny moduł równoważenia obciążenia (do aplikacji lub kontrolera ruchu przychodzącego), upewnij się, że adres IP aprowizowany w poprzedniej sekcji zostanie podany jako LoadBalancerIP.

Uwzględnij zewnętrzne parametry jako zmienne środowiskowe. Nie dołączaj wpisów tajnych (takich jak hasła, klucze interfejsu API i parametry połączenia JDBC). Wpisy tajne znajdują się w sekcji Konfiguracja usługi KeyVault FlexVolume.

Konfigurowanie magazynu trwałego

Jeśli aplikacja wymaga magazynu trwałego, należy skonfigurować co najmniej jeden wolumin trwały.

Możesz chcieć utworzyć wolumin trwały przy użyciu usługi Azure Files zainstalowanej w katalogu dzienników Tomcat (/tomcat_logs), aby zachowywać dzienniki centralnie. Aby uzyskać więcej informacji, zobacz Dynamiczne tworzenie i korzystanie z trwałego woluminu za pomocą usługi Azure Files w usłudze Azure Kubernetes Service (AKS).

Konfigurowanie usługi KeyVault FlexVolume

Utwórz usługę Azure KeyVault i wypełnij wszystkie niezbędne wpisy tajne. Następnie skonfiguruj usługę KeyVault FlexVolume, aby udostępnić te wpisy tajne w zasobnikach.

Aby zaimportować certyfikaty do lokalnego magazynu kluczy w kontenerze, należy zmodyfikować skrypt uruchamiania (startup.sh w repozytorium GitHub Tomcat w kontenerach).

Migrowanie zaplanowanych zadań

Aby wykonać zaplanowane zadania w klastrze usługi AKS, zdefiniuj zadania cron wedle potrzeby.

Po migracji

Po przeprowadzeniu migracji aplikacji do usługi AKS należy sprawdzić, czy działa ona zgodnie z oczekiwaniami. Po wykonaniu tych czynności skorzystaj z naszych zaleceń, które mogą sprawić, że aplikacja będzie bardziej natywna w chmurze.