Eseguire la migrazione di applicazioni Tomcat ai contenitori nel servizio Azure Kubernetes

Questa guida descrive gli aspetti da considerare per la migrazione di un'applicazione Tomcat esistente da eseguire nel servizio Azure Kubernetes.

Pre-migrazione

Per garantire una corretta migrazione, prima di iniziare completare i passaggi di valutazione e inventario descritti nelle sezioni seguenti.

Inventario delle risorse esterne

Le risorse esterne, ad esempio le origini dati, i broker di messaggi JMS e altre, vengono inserite tramite JNDI (Java Naming and Directory Interface). Alcune di queste risorse possono richiedere la migrazione o la riconfigurazione.

All'interno dell'applicazione

Esaminare il file META-INF/context.xml. Cercare gli elementi <Resource> all'interno dell'elemento <Context>.

Nei server applicazioni

Esaminare i file $CATALINA_BASE/conf/context.xml e $CATALINA_BASE/conf/server.xml, oltre ai file con estensione xml disponibili nelle directory $CATALINA_BASE/conf/[nome-motore]/[nome-host].

Nei file context.xml le risorse JNDI verranno descritte dagli elementi <Resource> all'interno dell'elemento <Context> di primo livello.

Nei file server.xml le risorse JNDI verranno descritte dagli elementi <Resource> all'interno dell'elemento <GlobalNamingResources>.

Datasources

Le origini dati sono risorse JNDI con l'attributo type impostato su javax.sql.DataSource. Per ogni origine dati, documentare le informazioni seguenti:

  • Qual è il nome dell'origine dati?
  • Qual è la configurazione del pool di connessioni?
  • Dove è possibile trovare il file JAR del driver JDBC?

Per altre informazioni, vedere la sezione di procedure per le origini dati JNDI nella documentazione di Tomcat.

Tutte le altre risorse esterne

Non è possibile documentare tutte le possibili dipendenze esterne in questa guida. È responsabilità del team verificare che sia possibile soddisfare tutte le dipendenze esterne dell'applicazione dopo la migrazione.

Inventario dei segreti

Password e stringhe sicure

Controllare tutte le proprietà e i file di configurazione nei server di produzione per verificare la presenza di stringhe segrete e password. Assicurarsi di controllare i file server.xml e context.xml in $CATALINA_BASE/conf. È anche possibile trovare i file di configurazione contenenti password o credenziali all'interno dell'applicazione. Possono includere il file META-INF/context.xml e, per le applicazioni Spring Boot, il file application.properties o application.yml.

Determinare se e come viene usato il file system

Qualsiasi utilizzo del file system nel server applicazioni richiede modifiche della configurazione o, in casi rari, dell'architettura. È possibile identificare alcuni o tutti gli scenari seguenti.

Contenuto statico di sola lettura

Se l'applicazione attualmente distribuisce contenuto statico, è necessario modificarne la posizione. Si può scegliere di spostare il contenuto statico in Archiviazione BLOB di Azure e di aggiungere la rete di distribuzione dei contenuti di Azure per accelerare i download a livello globale. Per altre informazioni, vedere Hosting di siti Web statici in Archiviazione di Azure e Avvio rapido: Integrare un account di archiviazione di Azure con Rete CDN di Azure. È anche possibile distribuire direttamente il contenuto statico in un'app nel piano Azure Spring Apps Enterprise. Per altre informazioni, vedere Distribuire file statici Web.

Contenuto statico pubblicato dinamicamente

Se l'applicazione consente contenuto statico caricato/prodotto dall'applicazione ma non modificabile dopo la creazione, è possibile usare Archiviazione BLOB di Azure e la rete di distribuzione dei contenuti di Azure, come descritto sopra, con una funzione di Azure per gestire i caricamenti e l'aggiornamento della rete CDN. Nell'articolo Caricamento e precaricamento nella rete CDN di contenuto statico con Funzioni di Azure è riportata un'implementazione di esempio che è possibile usare. È anche possibile distribuire direttamente il contenuto statico in un'app nel piano Azure Spring Apps Enterprise. Per altre informazioni, vedere Distribuire file statici Web.

Contenuto dinamico o interno

Per i file scritti e letti di frequente dall'applicazione, ad esempio i file di dati temporanei, o i file statici visibili solo all'applicazione, è possibile montare le condivisioni di archiviazione di Azure come volumi persistenti. Per altre informazioni, vedere Creare dinamicamente e usare un volume persistente con File di Azure nel servizio Azure Kubernetes.

Identificare il meccanismo di persistenza delle sessioni

Per identificare il gestore di persistenza delle sessioni in uso, esaminare i file context.xml nell'applicazione e nella configurazione di Tomcat. Cercare l'elemento <Manager>, quindi prendere nota del valore dell'attributo className.

Le implementazioni predefinite di PersistentManager di Tomcat, ad esempio StandardManager o FileStore non sono progettate per l'uso con una piattaforma distribuita e scalabile come Kubernetes. Il servizio Azure Kubernetes può bilanciare il carico tra diversi pod e riavviare in modo trasparente qualsiasi pod in qualsiasi momento, quindi non è consigliabile rendere persistente lo stato modificabile di un file system.

Se è richiesta la persistenza delle sessioni, è necessario usare un'implementazione di PersistentManager alternativa che scriverà in un archivio dati esterno, ad esempio VMware Tanzu Session Manager con Cache Redis. Per altre informazioni, vedere Usare Redis come cache di sessione con Tomcat.

Casi speciali

Alcuni scenari di produzione possono richiedere ulteriori modifiche o imporre limitazioni aggiuntive. Sebbene tali scenari siano poco frequenti, è importante assicurarsi che siano inapplicabili all'applicazione o risolti correttamente.

Determinare se l'applicazione si basa su processi pianificati

I processi pianificati, ad esempio le attività di Quartz Scheduler o i processi Cron, non possono essere usati con le distribuzioni di Tomcat in contenitori. Se l'applicazione viene ampliata, un processo pianificato può essere eseguito più di una volta per ogni periodo pianificato. Questa situazione può provocare conseguenze indesiderate.

Creare un inventario di tutti i processi pianificati, all'interno o all'esterno del server applicazioni.

Determinare se l'applicazione contiene codice specifico del sistema operativo

Se l'applicazione contiene codice che ospita il sistema operativo in cui è in esecuzione l'applicazione, è necessario eseguire il refactoring dell'applicazione in modo che NON si basi sul sistema operativo sottostante. Ad esempio, potrebbe essere necessario sostituire qualsiasi utilizzo di / o \ nei percorsi del file system con File.Separator o Path.get.

Determinare se viene usato MemoryRealm

MemoryRealm richiede un file XML persistente. In Kubernetes questo file deve essere aggiunto all'immagine del contenitore o caricato nello spazio di archiviazione condiviso reso disponibile per i contenitori. Il parametro pathName dovrà essere modificato di conseguenza.

Per determinare se MemoryRealm è attualmente in uso, esaminare i file server.xml e context.xml e cercare gli elementi <Realm> in cui l'attributo className è impostato su org.apache.catalina.realm.MemoryRealm.

Determinare se viene usato il monitoraggio delle sessioni SSL

Nelle distribuzioni in contenitori, il carico delle sessioni SSL viene in genere trasferito all'esterno del contenitore dell'applicazione, in genere dal controller in ingresso. Se l'applicazione richiede il monitoraggio delle sessioni SSL, verificare che il traffico SSL passi direttamente al contenitore dell'applicazione.

Determinare se viene usato AccessLogValve

Se viene usato AccessLogValve, il parametro directory deve essere impostato su una condivisione montata di File di Azure o su una delle relative sottodirectory.

Test sul posto

Prima di creare immagini del contenitore, eseguire la migrazione dell'applicazione all'istanza di JDK e Tomcat che si intende usare nel servizio Azure Kubernetes. Testare accuratamente l'applicazione per garantirne la compatibilità e le prestazioni.

Parametrizzare la configurazione

Nella fase di pre-migrazione è probabile che nei file server.xml e context.xml siano stati identificati segreti e dipendenze esterne, ad esempio origini dati. Per ogni elemento di questo tipo identificato, sostituire l'eventuale nome utente, password, stringa di connessione o URL con una variabile di ambiente.

Si supponga, ad esempio, che il file context.xml contenga l'elemento seguente:

<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$$"
/>

In questo caso, è possibile cambiarlo come illustrato nell'esempio seguente:

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

Migrazione

Fatta eccezione per il primo passaggio ("Provisioning del registro contenitori e del servizio Azure Kubernetes"), è consigliabile seguire la procedura descritta di seguito singolarmente per ogni applicazione (file WAR) di cui eseguire la migrazione.

Nota

Alcune distribuzioni di Tomcat possono avere più applicazioni in esecuzione in un singolo server Tomcat. Se questo è il caso, è consigliabile eseguire ogni applicazione in un pod separato. In questo modo è possibile ottimizzare l'utilizzo delle risorse per ogni applicazione riducendo al tempo stesso la complessità e l'accoppiamento.

Provisioning del registro contenitori e del servizio Azure Kubernetes

Creare un registro contenitori e un cluster di Azure Kubernetes la cui entità servizio abbia il ruolo di lettore nel registro. Assicurarsi di scegliere il modello di rete appropriato per i requisiti di rete del cluster.

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

Preparare gli artefatti della distribuzione

Clonare il repository GitHub indicato nell'argomento di avvio rapido su Tomcat nei contenitori. Contiene un Dockerfile e i file di configurazione di Tomcat con diverse ottimizzazioni consigliate. Nei passaggi seguenti vengono illustrate le modifiche che è probabile sia necessario apportare a questi file prima di creare l'immagine del contenitore e distribuirla nel servizio Azure Kubernetes.

Se necessario, aprire le porte per il clustering

Se si intende usare il clustering Tomcat nel servizio Azure Kubernetes, verificare che gli intervalli di porte necessari siano esposti nel Dockerfile. Per specificare l'indirizzo IP del server in server.xml, assicurarsi di usare un valore di una variabile inizializzata all'avvio del contenitore nell'indirizzo IP del pod.

In alternativa, lo stato della sessione può essere reso persistente in una posizione alternativa ai fini della disponibilità tra repliche.

Per determinare se l'applicazione usa il clustering, cercare l'elemento <Cluster> all'interno degli elementi <Host> o <Engine> nel file server.xml.

Aggiungere risorse JNDI

Modificare il file server.xml per aggiungere le risorse preparate nei passaggi di pre-migrazione, ad esempio le origini dati.

Ad esempio:

<!-- 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>

Per altre istruzioni sulle origini dati, vedere le sezioni seguenti sulle procedure sulle origini dati JNDI nella documentazione di Tomcat:

Creare l'immagine ed eseguirne il push

Il modo più semplice per creare e caricare l'immagine in Registro Azure Container per l'uso da parte del servizio Azure Kubernetes consiste nell'usare il comando az acr build. Questo comando non richiede l'installazione di Docker nel computer. Ad esempio, se il Dockerfile precedente e il pacchetto dell'applicazione petclinic.war si trovano nella directory corrente, è possibile creare l'immagine del contenitore in Registro Azure Container con un solo passaggio:

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

È possibile omettere il parametro --build-arg APP_FILE... se il file WAR è denominato ROOT.war. È possibile omettere il parametro --build-arg SERVER_XML... se il file XML del server è denominato server.xml. Entrambi i file devono trovarsi nella stessa directory del Dockerfile.

In alternativa, è possibile usare l'interfaccia della riga di comando di Docker per creare l'immagine localmente. Questo approccio può semplificare il test e il perfezionamento dell'immagine prima della distribuzione iniziale in Registro Azure Container. Tuttavia, è necessario che l'interfaccia della riga di comando di Docker sia installata e che il daemon Docker sia in esecuzione.

# 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"

Per altre informazioni, vedere il modulo Learn per la compilazione e l'archiviazione di immagini del contenitore in Azure.

Provisioning di un indirizzo IP pubblico

Se l'applicazione deve essere accessibile dall'esterno delle reti virtuali o interne, sarà necessario un indirizzo IP statico pubblico. È necessario eseguire il provisioning di questo indirizzo IP nel gruppo di risorse del nodo del cluster.

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}."

Distribuire in servizio Azure Kubernetes

Creare e applicare i file YAML di Kubernetes. Se si crea un servizio di bilanciamento del carico esterno (per l'applicazione o per un controller di ingresso), assicurarsi di fornire l'indirizzo IP di cui è stato effettuato il provisioning nella sezione precedente come LoadBalancerIP.

Includere parametri esternalizzati come variabili di ambiente. Non includere segreti, ad esempio password, chiavi API e stringhe di connessione JDBC. I segreti sono descritti nella sezione Configurare KeyVault FlexVolume.

Configurare la risorsa di archiviazione persistente

Se l'applicazione richiede una risorsa di archiviazione non volatile, configurare uno o più volumi persistenti.

Si potrebbe scegliere di creare un volume persistente usando File di Azure, montato nella directory dei log di Tomcat (/tomcat_logs) per mantenere i log a livello centrale. Per altre informazioni, vedere Creare dinamicamente e usare un volume persistente con File di Azure nel servizio Azure Kubernetes.

Configurare KeyVault FlexVolume

Creare un'istanza di Azure KeyVault e popolarla con i segreti necessari. Configurare quindi KeyVault FlexVolume per rendere tali segreti accessibili ai pod.

Sarà necessario modificare lo script di avvio (startup.sh) nel repository di GitHub per Tomcat nei contenitori per importare i certificati nell'archivio chiavi locale del contenitore.

Eseguire la migrazione dei processi pianificati

Per eseguire i processi pianificati nel cluster del servizio Azure Kubernetes, definire processi Cron in base alle esigenze.

Dopo la migrazione

Ora che è stata eseguita la migrazione dell'applicazione al servizio Azure Kubernetes, è necessario verificare che funzioni come previsto. Una volta completata questa operazione, sono disponibili alcune raccomandazioni per rendere l'applicazione maggiormente nativa del cloud.