Containerizzare un'app Java

Completato

In questa unità si containerizza un'applicazione Java.

Come accennato in precedenza, i contenitori vengono eseguiti direttamente sul sistema operativo host, sul kernel e sull'hardware come processi di sistema ordinari. I contenitori richiedono un minor numero di risorse di sistema, con un footprint inferiore, un sovraccarico inferiore e tempi di avvio delle applicazioni più veloci. Questi vantaggi sono casi d'uso eccezionali per la scalabilità su richiesta.

Sono disponibili contenitori Windows e contenitori Linux. In questo modulo si usa il runtime Docker ampiamente usato per compilare un'immagine del contenitore Linux. Si distribuisce quindi l'immagine del contenitore Linux nel sistema operativo host del computer locale. Infine, si distribuisce l'immagine del contenitore Linux nel servizio Azure Kubernetes.

Panoramica di Docker

Il runtime Docker viene usato per compilare, eseguire, eseguire ed eseguire immagini del contenitore push, come illustrato nel diagramma seguente:

Diagramma che mostra i comandi Docker.

La tabella seguente descrive ogni comando Docker:

Comando Docker Descrizione
docker build Compila un'immagine del contenitore costituita dalle istruzioni o dai livelli necessari per Docker per creare un contenitore in esecuzione da un'immagine. Il risultato di questo comando è un'immagine.
docker pull I contenitori vengono inizializzati dalle immagini, che vengono estratte dai registri, ad esempio Registro Azure Container. In questo Registro di sistema viene eseguito il pull del servizio Azure Kubernetes. Il risultato di questo comando è un pull di rete di un'immagine, che si verifica in Azure. Facoltativamente, è possibile eseguire il pull delle immagini in locale. Questa opzione è comune quando si creano immagini che richiedono dipendenze o livelli che potrebbero essere necessari per l'applicazione, ad esempio un server applicazioni.
docker run Un'istanza in esecuzione di un'immagine è un contenitore e questo comando esegue tutti i livelli necessari per eseguire e interagire con l'applicazione contenitore in esecuzione. Il risultato di questo comando è un processo dell'applicazione in esecuzione nel sistema operativo host.
docker push Registro Azure Container archivia le immagini in modo che siano facilmente disponibili e vicine alla rete per le distribuzioni e la scalabilità di Azure.

Clonare l'applicazione Java

Prima di tutto, clonare il repository Flight Booking System for Airline Reservations (Sistema di prenotazione dei voli per le prenotazioni aeree) e passare alla cartella del progetto dell'applicazione Web Airlines.

Annotazioni

Se la creazione del servizio Azure Kubernetes viene completata nella scheda dell'interfaccia della riga di comando, usare tale scheda. Se è ancora in esecuzione, aprire una nuova scheda e passare alla posizione in cui si preferisce clonare il sistema di prenotazione dei voli per le prenotazioni aeree.

Eseguire i comandi seguenti:

git clone https://github.com/Azure-Samples/containerize-and-deploy-Java-app-to-Azure.git
cd containerize-and-deploy-Java-app-to-Azure/Project/Airlines

Facoltativamente, se è installato Java e Maven, è possibile eseguire il comando seguente nella console del terminale per ottenere un'idea dell'esperienza di compilazione dell'applicazione senza Docker. Se Java e Maven non sono installati, è possibile passare alla sezione successiva Costruire un file Docker. In questa sezione si usa Docker per eseguire il pull down di Java e Maven per eseguire le compilazioni per conto dell'utente.

mvn clean package

Annotazioni

È stato usato il mvn clean package comando per illustrare le sfide operative di non usare build multistage Docker, illustrate di seguito. Anche in questo caso, questo passaggio è facoltativo. In entrambi i casi, è possibile procedere in modo sicuro senza eseguire il comando Maven.

Se il processo ha avuto esito positivo, Maven ha compilato correttamente il sistema di prenotazione dei voli per l'archivio dell'applicazione Web delle prenotazioni aeree AirlinesReservationSample-0.0.1-SNAPSHOT.war, come illustrato nell'output seguente:

[INFO] Building war: $PROJECT_PATH/containerize-and-deploy-Java-app-to-Azure/Project/Airlines/target/AirlinesReservationSample-0.0.1-SNAPSHOT.war
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  11.776 s
[INFO] Finished at: 2024-11-15T09:33:26+09:00
[INFO] ------------------------------------------------------------------------

Si supponga di essere uno sviluppatore Java e di aver appena creato AirlinesReservationSample-0.0.1-SNAPSHOT.war. Il passaggio successivo consiste probabilmente nell'collaborare con i tecnici delle operazioni per distribuire questo artefatto in un server locale o in una macchina virtuale. Affinché l'applicazione venga avviata ed eseguita correttamente, i server e le macchine virtuali devono essere disponibili e configurati con le dipendenze necessarie. Questo processo è complesso e richiede molto tempo, soprattutto su richiesta quando un aumento del carico raggiunge l'applicazione. Con i contenitori, queste sfide vengono risolte.

Costruire un Dockerfile

È ora possibile creare un Dockerfile. Un Dockerfile è un documento di testo che contiene tutti i comandi che un utente eseguirà sulla riga di comando per assemblare un'immagine del contenitore. Ogni immagine è un livello che può essere memorizzato nella cache per un'efficienza. I livelli si basano l'uno sull'altro.

Ad esempio, il sistema di prenotazione dei voli per le prenotazioni aeree deve essere distribuito ed eseguito all'interno di un server applicazioni. Un server applicazioni non è in pacchetto all'interno di AirlinesReservationSample-0.0.1-SNAPSHOT.war. Si tratta di una dipendenza esterna necessaria per airlinesReservationSample-0.0.1-SNAPSHOT.war per eseguire, ascoltare ed elaborare le richieste HTTP, gestire le sessioni utente e facilitare le prenotazioni dei voli. Se è stata usata una distribuzione tradizionale non in contenitori, i tecnici delle operazioni installano e configurano un server applicazioni in un server fisico o in una macchina virtuale prima di distribuire AirlinesReservationSample-0.0.1-SNAPSHOT.war . Questi tecnici dell'operazione devono anche assicurarsi che il JDK usato nel computer, ovvero quello mvn clean package usato per compilare il file WAR, corrisponde in effetti allo stesso JRE usato dal server applicazioni. La gestione di queste dipendenze è complessa e richiede molto tempo.

Con un Dockerfile, è possibile scrivere automaticamente le istruzioni o i livelli necessari per raggiungere questo obiettivo, inserendo i passaggi necessari per garantire che il sistema di prenotazione dei voli per le prenotazioni aeree abbia tutte le dipendenze necessarie per la distribuzione nel runtime del contenitore Docker. Questa soluzione è interessante quando si lavora con scalabilità su richiesta a intervalli non pianificati. Ogni livello usa la cache Docker, che contiene lo stato dell'immagine del contenitore in ogni attività cardine dell'istruzione, ottimizzando il tempo di calcolo e il riutilizzo. Se un livello non cambia, vengono usati livelli memorizzati nella cache. I casi d'uso comuni per i livelli memorizzati nella cache sono il runtime Java, il server applicazioni e altre dipendenze per l'applicazione Web Flight Booking System for Airline Reservations. Se e quando una versione cambia in un livello memorizzato nella cache in precedenza, viene creata una nuova voce memorizzata nella cache.

Il diagramma seguente illustra i livelli di un'immagine del contenitore. Quando vengono eseguiti i comandi nel Dockerfile, vengono creati i livelli. Il livello superiore è il sistema di prenotazione dei voli di lettura/scrittura per il livello applicazione Web Prenotazioni aeree. Tale livello è basato sui livelli di sola lettura precedenti.

Diagramma che mostra i livelli Docker.

Docker ha il concetto di compilazioni a più passaggi, una funzionalità che consente di creare un'immagine del contenitore più piccola con una memorizzazione nella cache migliore e un footprint di sicurezza inferiore, consentendo un'maggiore ottimizzazione e manutenzione del Dockerfile nel tempo. Ad esempio, è possibile separare la fase di compilazione del contenitore per la compilazione e la compilazione dell'applicazione dalla fase per l'esecuzione dell'applicazione. Questa funzionalità consente di copiare solo gli artefatti generati durante la compilazione nell'immagine del contenitore di produzione, riducendo così il footprint. Poiché le immagini del contenitore vengono memorizzate nella cache, se non sono presenti modifiche, le immagini memorizzate nella cache possono essere riutilizzate, riducendo il costo e il tempo di download dalla rete.

I servizi esposti nell'ambiente di produzione devono essere gestiti con attenzione per la sicurezza. Pertanto, l'ambiente di produzione usa e gestisce un'immagine del contenitore sicura. Nell'esempio viene usata l'immagine CBL-Mariner fornita da Microsoft.

CBL-Mariner Linux è un sistema operativo leggero, contenente solo i pacchetti necessari per un ambiente cloud. È possibile personalizzarla tramite pacchetti e strumenti personalizzati in base ai requisiti dell'applicazione. CBL-Mariner sottoposti a test di convalida di Azure ed è compatibile con gli agenti di Azure. Microsoft compila e testa CBL-Mariner per alimentare vari casi d'uso, tra i servizi di Azure e l'infrastruttura IoT. Si tratta della distribuzione Linux consigliata internamente per l'uso con i servizi cloud Microsoft e i prodotti correlati.

Annotazioni

Microsoft fornisce immagini del contenitore in bundle con OpenJDK, incluse Ubuntule immagini , CBL-Marinere distroless . L'immagine distroless ha le dimensioni più piccole dell'immagine, ma l'esecuzione di Tomcat su di esso è complessa. Per ottenere una progettazione leggera, l'immagine distroless rimuove molti comandi e strumenti, inclusa la shell, il che significa che non è possibile chiamare catalina.sh per avviare Tomcat. L'immagine distroless è adatta per l'esecuzione di file JAR eseguibili, ad esempio quelle usate con Spring Boot o Quarkus.

Nell'esempio seguente viene usata la stessa versione di Microsoft Build di OpenJDK sia nella fase di compilazione che nella fase finale. Questo approccio garantisce la compilazione del codice sorgente con la stessa versione di JDK usata dalla distribuzione del servizio Tomcat, che consente di evitare comportamenti imprevisti a causa di mancate corrispondenze della versione.

L'immagine seguente illustra la compilazione a più fasi e le operazioni eseguite in ogni fase in base ai comandi specificati nel Dockerfile:

Diagramma che mostra la compilazione multistage di Docker.

Nella fase 0, Tomcat viene scaricato ed estratto in una directory specificata da una variabile di ambiente in un'immagine Ubuntu. La TOMCAT_VERSION variabile specifica la versione di Tomcat da scaricare. Se viene rilasciata una nuova versione di Tomcat, è necessario aggiornare il numero di versione, poiché una nuova immagine viene recuperata solo quando il numero di versione cambia. In caso contrario, viene usata l'immagine memorizzata nella cache. Tomcat scaricato viene copiato nell'ambiente di fase finale da usare.

Nella fase 1, Maven viene installato in un'immagine Ubuntu e i file di configurazione e il codice sorgente creati vengono copiati prima di compilare il progetto Maven. Ogni livello viene memorizzato nella cache, quindi i livelli immagine del sistema operativo e immagine Maven riutilizzano la cache. Se i file di configurazione, i file del codice sorgente o la directory Web vengono aggiornati, i livelli dalle modifiche in poi vengono ricompilati. Se la compilazione viene completata correttamente senza errori durante la compilazione, viene generato un artefatto denominato AirlinesReservationSample-0.0.1-SNAPSHOT.war nella directory di destinazione . Questo artefatto viene copiato nell'ambiente di fase finale da usare.

Nella fase finale, l'immagine sicura CBL-Mariner fornita da Microsoft viene usata rispettivamente per copiare gli artefatti di compilazione Tomcat e Java dalla fase 0 e dalla fase 1. Un utente denominato app è proprietario di tutti i file usati all'interno del progetto e l'applicazione viene eseguita anche come app utente anziché avere root privilegi. Questa configurazione garantisce che l'immagine del contenitore possa essere gestita in modo sicuro senza concedere autorizzazioni non necessarie. Infine, viene esposto il numero di porta 8080 e lo script catalina.sh viene eseguito per avviare Tomcat. Quando questa operazione viene eseguita nel desktop Docker locale, è possibile accedervi tramite l'URL http://localhost:8080/AirlinesReservationSample.

Nella cartella radice del progetto, containerize-and-deploy-Java-app-to-Azure/Project/Airlines, usare il comando seguente per creare un file denominato Dockerfile:

vi Dockerfile

Aggiungere il contenuto seguente al Dockerfile, quindi salvare e uscire. Per salvare e uscire, premere ESC, digitare :wq!, quindi premere INVIO.

############################################
# Tomcat Intall stage
############################################
FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS tomcat

ENV CATALINA_HOME=/usr/local/tomcat

# Configure Tomcat Version (Be sure to use the latest version)
ENV TOMCAT_VERSION=10.1.33

# Install Tomcat and required packages
RUN apt-get update ; \
    apt-get install -y curl ; \
    curl -O https://downloads.apache.org/tomcat/tomcat-10/v${TOMCAT_VERSION}/bin/apache-tomcat-${TOMCAT_VERSION}.tar.gz ; \
    tar xzf apache-tomcat-${TOMCAT_VERSION}.tar.gz ; \
    mv apache-tomcat-${TOMCAT_VERSION} ${CATALINA_HOME} ; \
    rm apache-tomcat-${TOMCAT_VERSION}.tar.gz && \
    apt-get remove --purge -y curl && \
    apt-get autoremove -y && \
    apt-get clean

############################################
# Build stage (Compiles with Java 17)
############################################
FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS build

WORKDIR /build

# Install Maven
RUN apt-get update && apt-get install -y maven && mvn --version

# Copy source code
COPY pom.xml .
COPY src ./src
COPY web ./web

# Build the project
RUN mvn clean package

############################################
# Package final stage
############################################
FROM mcr.microsoft.com/openjdk/jdk:17-mariner

# Configure the location of the Tomcat installation
ENV CATALINA_HOME=/usr/local/tomcat
# Configure the path to the Tomcat binaries
ENV PATH=$CATALINA_HOME/bin:$PATH

# This is the user that runs the Tomcat process
USER app

# Copy the Tomcat installation from the Tomcat stage
COPY --chown=app:app --from=tomcat ${CATALINA_HOME} ${CATALINA_HOME}
# Copy the Tomcat configuration files
COPY --chown=app:app tomcat-users.xml ${CATALINA_HOME}/conf
# Copy the compiled WAR file from the build stage
COPY --chown=app:app --from=build /build/target/*.war ${CATALINA_HOME}/webapps/AirlinesReservationSample.war

# Expose the default Tomcat port
EXPOSE 8080
# Start Tomcat
CMD ["catalina.sh", "run"]

Annotazioni

Facoltativamente, è possibile usare il file Dockerfile_Solution nella radice del progetto, che contiene il contenuto necessario.

Il Dockerfile è suddiviso in tre fasi, descritte nelle tabelle seguenti:

  • Fase di installazione di Tomcat:

    Comando Docker Descrizione
    FROM FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS tomcat imposta l'immagine di base su Microsoft Build di OpenJDK 17 in Ubuntu e denomina questa fase tomcat. Questo è il percorso in cui viene installato Tomcat.
    ENV ENV CATALINA_HOME=/usr/local/tomcat imposta una variabile di ambiente per la directory di installazione di Tomcat.
    ENV ENV TOMCAT_VERSION=10.1.33 imposta la versione di Tomcat da installare. Questa operazione deve essere aggiornata alla versione più recente in base alle esigenze.
    RUN Il RUN comando aggiorna l'elenco dei pacchetti, installa curl, scarica la versione specificata di Tomcat, lo estrae, lo sposta nella directory specificata e pulisce i file e i pacchetti non necessari. Ciò garantisce che l'immagine rimanga leggera.
  • Fase di compilazione, che viene compilata con Java 17:

    Comando Docker Descrizione
    FROM FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS build imposta l'immagine di base su Microsoft Build di OpenJDK 17 in Ubuntu e denomina questa fase build. Questa fase viene usata per compilare l'applicazione Java.
    WORKDIR WORKDIR /build imposta la directory di lavoro all'interno del contenitore su /build, in cui il codice sorgente viene copiato e compilato.
    RUN RUN apt-get update && apt-get install -y maven && mvn --version installa Maven, uno strumento di automazione della compilazione usato per i progetti Java e ne verifica l'installazione.
    COPY COPY pom.xml . copia il file di configurazione Maven nella directory di lavoro. Questo file è essenziale per la compilazione del progetto.
    COPY COPY src ./src copia la directory del codice sorgente nel contenitore. Qui risiede il codice dell'applicazione Java.
    COPY COPY web ./web copia la directory delle risorse Web nel contenitore. Sono incluse le risorse dell'applicazione Web necessarie per la compilazione.
    RUN RUN mvn clean package esegue il processo di compilazione Maven, che compila l'applicazione Java e lo inserisce in un file WAR.
  • Fase finale del pacchetto:

    Comando Docker Descrizione
    FROM FROM mcr.microsoft.com/openjdk/jdk:17-mariner imposta l'immagine di base su Microsoft Build di OpenJDK 17 in CBL-Mariner, che viene usata per la distribuzione finale dell'applicazione.
    ENV ENV CATALINA_HOME=/usr/local/tomcat imposta la variabile di ambiente per la directory di installazione di Tomcat, simile alla fase di installazione.
    ENV ENV PATH=$CATALINA_HOME/bin:$PATH aggiunge la directory bin Tomcat al sistema PATH, consentendo l'esecuzione semplice dei comandi Tomcat.
    USER USER app specifica l'utente con cui viene eseguito il processo Tomcat, migliorando la sicurezza non eseguendo come utente radice.
    COPY COPY --chown=app:app --from=tomcat ${CATALINA_HOME} ${CATALINA_HOME} copia l'installazione di Tomcat dalla tomcat fase, impostando la proprietà sull'utente app .
    COPY COPY --chown=app:app tomcat-users.xml ${CATALINA_HOME}/conf copia il file di configurazione utente Tomcat nel contenitore, impostando la proprietà sull'utente app .
    COPY COPY --chown=app:app --from=build /build/target/*.war ${CATALINA_HOME}/webapps/AirlinesReservationSample.war copia il file WAR compilato dalla build fase nella directory webapps Tomcat, impostando la proprietà sull'utente app .
    EXPOSE EXPOSE 8080 espone la porta 8080, la porta predefinita per Tomcat, consentendo l'accesso esterno all'applicazione.
    CMD CMD ["catalina.sh", "run"] specifica il comando per avviare Tomcat quando viene eseguito il contenitore.

Per altre informazioni sulla costruzione di Dockerfile, vedere le informazioni di riferimento su Dockerfile.