Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
I container forniscono un ambiente coerente e portatile per le applicazioni Java nelle fasi di sviluppo, test e produzione. Questo articolo introduce i concetti di containerizzazione per le applicazioni Java e illustra la creazione, il debug, l'ottimizzazione e la distribuzione di applicazioni Java in contenitori in App Azure Container.
In questo articolo vengono illustrati i concetti essenziali di containerizzazione per gli sviluppatori Java e le competenze seguenti:
- Configurazione dell'ambiente di sviluppo per le applicazioni Java containerizzate.
- Creazione di Dockerfile ottimizzati per i carichi di lavoro Java.
- Configurazione dei flussi di lavoro di sviluppo locale con i container.
- Debug di applicazioni Java containerizzate.
- Ottimizzazione dei contenitori Java per la produzione.
- Distribuzione delle applicazioni Java in contenitori in App Azure Container.
Containerizzando le applicazioni Java, si ottengono ambienti coerenti, distribuzione semplificata, utilizzo efficiente delle risorse e maggiore scalabilità.
Contenitori per applicazioni Java
I container impacchettano le applicazioni con le relative dipendenze, garantendo la coerenza tra gli ambienti. Per gli sviluppatori Java, ciò significa raggruppare l'applicazione, le sue dipendenze, il Java Runtime Environment/Java Development Kit (JRE/JDK) e i file di configurazione in un'unica unità portatile.
La containerizzazione presenta vantaggi chiave rispetto alla virtualizzazione che la rendono ideale per lo sviluppo cloud. A differenza di una macchina virtuale, un container viene eseguito nel kernel del sistema operativo host di un server. Ciò è vantaggioso per le applicazioni Java, che vengono già eseguite nella Java Virtual Machine (JVM). La containerizzazione delle applicazioni Java aggiunge un sovraccarico minimo e offre vantaggi significativi per l'implementazione.
L'ecosistema dei container include i seguenti componenti chiave:
- Immagini - i progetti.
- Contenitori: istanze in esecuzione.
- Registri - dove vengono memorizzate le immagini.
- Agenti di orchestrazione: sistemi che gestiscono i contenitori su larga scala.
Docker è la piattaforma di containerizzazione più diffusa ed è ben supportata nell'ecosistema Azure tramite Azure Container Apps.
Configurare l'ambiente di sviluppo
Questa sezione illustra l'installazione degli strumenti necessari e la configurazione dell'ambiente di sviluppo per la creazione, l'esecuzione e il debug di applicazioni Java containerizzate.
Installare gli strumenti necessari
Per containerizzare le applicazioni Java, è necessario che nel computer di sviluppo siano installati i seguenti strumenti:
- Docker Desktop. Fornisce il motore Docker, l'interfaccia della riga di comando e Docker Compose per Windows o macOS.
- Codice di Visual Studio. Disponibile come editor di codice gratuito.
- Le estensioni di Visual Studio Code seguenti:
- Estensione Docker per la gestione dei container.
- Java Extension Pack per lo sviluppo Java.
- Dev Containers per lo sviluppo all'interno di container.
Verificare l'installazione utilizzando i comandi seguenti:
docker --version
docker compose version
Configurare Visual Studio Code per lo sviluppo di contenitori
Per lo sviluppo Java in contenitori, configurare Visual Studio Code installando Java Extension Pack e configurando il JDK. L'estensione Dev Containers consente di aprire qualsiasi cartella all'interno di un contenitore e di utilizzare il set completo di funzionalità di Visual Studio Code all'interno di tale contenitore.
Per consentire a Visual Studio Code di compilare e connettersi automaticamente a un contenitore di sviluppo, creare un file devcontainer.jsoncon estensione devcontainer/ nel progetto.
Ad esempio, la seguente configurazione di esempio definisce una build Java:
{
"name": "Java Development",
"image": "mcr.microsoft.com/devcontainers/java:21",
"customizations": {
"vscode": {
"extensions": [
"vscjava.vscode-java-pack",
"ms-azuretools.vscode-docker"
]
}
},
"forwardPorts": [8080, 5005],
"remoteUser": "vscode"
}
Questa configurazione utilizza l'immagine del contenitore di sviluppo Java di Microsoft, aggiunge estensioni essenziali e inoltra sia la porta 8080
dell'applicazione, , sia la porta di debug, 5005
.
Creare un Dockerfile
Un Dockerfile contiene le istruzioni per la creazione di un'immagine Docker. Per le applicazioni Java, il Dockerfile include in genere i componenti seguenti:
- Un'immagine di base con JDK o JRE.
- Istruzioni per copiare i file dell'applicazione.
- Comandi per impostare le variabili d'ambiente.
- Configurazioni dei punti di ingresso.
Seleziona un'immagine di base
La scelta della giusta immagine di base è fondamentale. Prendi in considerazione queste opzioni:
Descrizione | Nome | Osservazioni: |
---|---|---|
Immagine di sviluppo di Microsoft Java | mcr.microsoft.com/java/jdk:21-zulu-ubuntu |
JDK completo e ottimizzato per Azure |
Immagine di produzione di Microsoft Java | mcr.microsoft.com/java/jre:21-zulu-ubuntu |
Solo runtime e ottimizzato per Azure |
Immagine ufficiale di sviluppo OpenJDK | openjdk:21-jdk |
JDK completo |
Immagine di produzione ufficiale di OpenJDK | openjdk:21-jre |
Solo runtime |
Per gli ambienti di sviluppo, utilizzare un'immagine JDK completa. Per la produzione, utilizzare un'immagine JRE o senza distribuzione per ridurre al minimo le dimensioni e la superficie di attacco dell'applicazione.
Le immagini Microsoft Java vengono fornite con ottimizzazioni specifiche di Azure e vengono regolarmente aggiornate con patch di sicurezza, il che le rende ideali per le applicazioni destinate ad Azure Container Apps.
Esempi di Dockerfile di base
L'esempio seguente mostra un semplice Dockerfile per un'applicazione Java:
FROM mcr.microsoft.com/java/jdk:21-zulu-ubuntu
WORKDIR /app
COPY target/myapp.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
Per le applicazioni Spring Boot, è possibile configurare il Dockerfile con la base seguente:
FROM mcr.microsoft.com/java/jdk:21-zulu-ubuntu
WORKDIR /app
COPY target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-Dspring.profiles.active=docker", "-jar", "app.jar"]
Per le distribuzioni di produzione, utilizzare l'immagine JRE illustrata nell'esempio seguente per ridurre le dimensioni e minimizzare la superficie di attacco dell'applicazione:
FROM mcr.microsoft.com/java/jre:21-zulu-ubuntu
WORKDIR /app
COPY target/*.jar app.jar
EXPOSE 8080
ENV JAVA_OPTS="-Dserver.port=8080"
ENTRYPOINT ["java", ${JAVA_OPTS}, "-jar", "app.jar"]
Sviluppo locale con container
I contenitori sono destinati all'esecuzione in vari contesti. In questa sezione viene illustrato un flusso di sviluppo locale da usare con i contenitori.
Usare Docker Compose per applicazioni multi-contenitore
La maggior parte delle applicazioni Java interagisce con database, cache o altri servizi. Docker Compose consente di definire e orchestrare applicazioni multi-contenitore utilizzando un semplice file di configurazione YAML.
Che cos'è Docker Compose?
Docker Compose è uno strumento che consente di eseguire le seguenti attività:
- Definisci applicazioni multi-contenitore in un unico file.
- Gestisci il ciclo di vita dell'applicazione, inclusi l'avvio, l'arresto e la ricompilazione.
- Mantenere ambienti isolati.
- Creare reti per la comunicazione dei servizi.
- Rendere persistenti i dati utilizzando i volumi.
Esempio: applicazione Java con database
Il seguente file compose.yml configura un'applicazione Java con un database PostgreSQL:
version: '3.8'
services:
app:
build: . # Build from Dockerfile in current directory
ports:
- "8080:8080" # Map HTTP port
- "5005:5005" # Map debug port
environment:
- SPRING_PROFILES_ACTIVE=dev
- SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/myapp
volumes:
- ./target:/app/target # Mount target directory for hot reloads
depends_on:
- db # Ensure database starts first
db:
image: postgres:13
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=myapp
ports:
- "5432:5432" # Expose PostgreSQL port
volumes:
- postgres-data:/var/lib/postgresql/data # Persist database data
volumes:
postgres-data: # Named volume for database persistence
Questo file ha le seguenti caratteristiche:
- I servizi possono fare riferimento l'uno all'altro per nome,
db
ad esempio nell'URL JDBC. - Docker Compose crea automaticamente una rete per i servizi.
- L'applicazione Java attende l'avvio del database, a causa di
depends_on
. - I dati del database vengono mantenuti tra i riavvii utilizzando un volume denominato.
Comandi comuni di Docker Compose
Dopo aver creato il file compose.yml , gestire l'applicazione utilizzando i comandi seguenti:
# Build images without starting containers
docker compose build
# Start all services defined in compose.yml
docker compose up
# Start in detached mode (run in background)
docker compose up -d
# View running containers managed by compose
docker compose ps
# View logs from all containers
docker compose logs
# View logs from a specific service
docker compose logs app
# Stop all services
docker compose down
# Stop and remove volumes (useful for database resets)
docker compose down -v
Flusso di lavoro per lo sviluppo
Un tipico flusso di lavoro di sviluppo Java che utilizza Docker Compose contiene i seguenti passaggi:
- Creare il file compose.yml e il Dockerfile.
- Esegui
docker compose up
per avviare tutti i servizi. - Apporta modifiche al tuo codice Java.
- Ricompilare l'applicazione A seconda della configurazione, potrebbe essere necessario riavviare i contenitori.
- Testare le modifiche nell'ambiente containerizzato.
- Al termine, eseguire
docker compose down
.
Esegui singoli contenitori con Docker
Per gli scenari più semplici in cui non sono necessari più servizi interconnessi, è possibile usare il docker run
comando per avviare singoli contenitori.
I seguenti comandi Docker sono tipici per le applicazioni Java:
# Run a Java application JAR directly
docker run -p 8080:8080 myapp:latest
# Run with environment variables
docker run -p 8080:8080 -e "SPRING_PROFILES_ACTIVE=prod" myapp:latest
# Run in detached mode (background)
docker run -d -p 8080:8080 myapp:latest
# Run with a name for easy reference
docker run -d -p 8080:8080 --name my-java-app myapp:latest
# Run with volume mount for persistent data
docker run -p 8080:8080 -v ./data:/app/data myapp:latest
Eseguire il debug di applicazioni containerizzate
Il debug di applicazioni Java containerizzate a volte è impegnativo perché il codice viene eseguito in un ambiente isolato all'interno del container.
Gli approcci di debug standard non sempre si applicano direttamente, ma con la configurazione corretta è possibile stabilire una connessione di debug remota all'applicazione. Questa sezione illustra come configurare i contenitori per il debug, connettere gli strumenti di sviluppo ai contenitori in esecuzione e risolvere i problemi comuni relativi ai contenitori.
Configurare il debug remoto
Il debug di applicazioni Java containerizzate richiede l'esposizione di una porta di debug e la configurazione dell'IDE per la connessione ad essa. È possibile eseguire queste attività utilizzando la procedura seguente:
Per abilitare il debug, modificare il Dockerfile in modo che includa il contenuto seguente:
Annotazioni
È invece possibile modificare il comando di avvio del contenitore.
FROM mcr.microsoft.com/java/jdk:21-zulu-ubuntu WORKDIR /app COPY target/*.jar app.jar EXPOSE 8080 5005 ENTRYPOINT ["java", "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005", "-jar", "app.jar"]
Configurare il file launch.json di Visual Studio Code per la connessione alla porta di debug, come illustrato nell'esempio seguente:
{ "version": "0.2.0", "configurations": [ { "type": "java", "name": "Debug in Container", "request": "attach", "hostName": "localhost", "port": 5005 } ] }
Avviare il contenitore con la porta
5005
mappata all'host e quindi avviare il debugger in Visual Studio Code.
Risolvere i problemi relativi ai contenitori
Quando i contenitori non si comportano come previsto, puoi esaminare i log dell'app per analizzare il problema.
Utilizzare i comandi seguenti per risolvere i problemi dell'applicazione. Prima di eseguire questi comandi, assicurati di sostituire i segnaposto (<...>
) con i tuoi valori.
# View logs
docker logs <CONTAINER_ID>
# Follow logs in real-time
docker logs -f <CONTAINER_ID>
# Inspect container details
docker inspect <CONTAINER_ID>
# Get a shell in the container
docker exec -it <CONTAINER_ID> bash
Per problemi specifici di Java, abilitare i flag JVM per una migliore diagnostica, come illustrato nell'esempio seguente:
ENTRYPOINT ["java", "-XX:+PrintFlagsFinal", "-XX:+PrintGCDetails", "-jar", "app.jar"]
Nella tabella seguente sono elencati i problemi comuni e le soluzioni corrispondenti:
Errore | Possibile soluzione |
---|---|
Memoria insufficiente | Aumentare i limiti di memoria del contenitore |
Timeout della connessione | Verificare la presenza di errori nella configurazione di rete. Verificare le porte e le regole di routing. |
Problemi di autorizzazione | Verificare le autorizzazioni del file system. |
Problemi relativi al percorso di classe | Controllare la struttura e le dipendenze JAR. |
Ottimizza i contenitori Java
Le applicazioni Java nei contenitori richiedono un'attenzione particolare per garantire prestazioni ottimali. La JVM è stata progettata prima che i container fossero comuni. L'uso dei contenitori può causare problemi di allocazione delle risorse se non sono configurati correttamente.
È possibile migliorare significativamente le prestazioni e l'efficienza delle applicazioni Java containerizzate ottimizzando le impostazioni di memoria, ottimizzando le dimensioni dell'immagine e configurando la garbage collection. Questa sezione illustra le ottimizzazioni essenziali per i container Java, con particolare attenzione alla gestione della memoria, al tempo di avvio e all'utilizzo delle risorse.
Configurazione della memoria JVM nei contenitori
La JVM non rileva automaticamente i limiti di memoria del contenitore in Java 8. Per Java 9+, il riconoscimento dei contenitori è abilitato per impostazione predefinita.
Configura la JVM in modo che rispetti i limiti del container, come mostrato nell'esempio seguente:
FROM mcr.microsoft.com/java/jre:21-zulu-ubuntu
WORKDIR /app
COPY target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-XX:MaxRAMPercentage=75.0", "-jar", "app.jar"]
I seguenti flag JVM sono importanti per le applicazioni containerizzate:
-
-XX:MaxRAMPercentage=75.0
. Imposta l'heap massimo come percentuale della memoria disponibile. -
-XX:InitialRAMPercentage=50.0
. Imposta la dimensione iniziale dell'heap. -
-Xmx
e-Xms
. Anche questi flag sono disponibili, ma richiedono valori fissi.
Preparare la distribuzione di produzione
Lo spostamento di applicazioni Java containerizzate in produzione richiede considerazioni che vanno oltre le funzionalità di base.
Gli ambienti di produzione richiedono una sicurezza solida, un monitoraggio affidabile, un'allocazione corretta delle risorse e flessibilità di configurazione.
Questa sezione illustra le procedure e le configurazioni essenziali necessarie per preparare i contenitori Java per l'uso in produzione. La sezione è incentrata sulla sicurezza, sui controlli di integrità e sulla gestione della configurazione, per garantire che le applicazioni vengano eseguite in modo affidabile nell'ambiente di produzione.
Procedure consigliate per la sicurezza
Proteggi le tue applicazioni Java containerizzate utilizzando le seguenti procedure:
Contesto di sicurezza predefinito. Eseguire le applicazioni come utente non root, come illustrato nell'esempio seguente:
FROM mcr.microsoft.com/java/jre:21-zulu-ubuntu WORKDIR /app COPY target/*.jar app.jar RUN addgroup --system javauser && adduser --system --ingroup javauser javauser USER javauser ENTRYPOINT ["java", "-jar", "app.jar"]
Cerca in modo proattivo i problemi. Analizzare regolarmente le immagini del contenitore alla ricerca di vulnerabilità utilizzando il comando seguente:
docker scan myapp:latest
Freschezza dell'immagine di base. Mantieni aggiornate le tue immagini di base.
Gestione dei segreti. Implementa una corretta gestione dei segreti. Ad esempio, non impostare come hardcoded i dati sensibili nell'applicazione e usare un insieme di credenziali delle chiavi quando possibile.
Contesti di sicurezza con restrizioni. Applicare il principio del privilegio minimo a tutti i contesti di sicurezza.
Accesso al file system. Utilizzare file system di sola lettura quando possibile.
Controlli e monitoraggio dello stato di salute
Controllare l'integrità dell'applicazione con i probe per assicurarsi che l'applicazione venga eseguita correttamente.
Per le applicazioni Spring Boot, includere la dipendenza dell'attuatore per gli endpoint di integrità completi, come illustrato nell'esempio seguente:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
Configura la tua applicazione per l'output dei log in un formato adatto agli ambienti container, come JSON.
Eseguire la distribuzione in App Azure Container
Questa sezione illustra la preparazione dei contenitori Java per la distribuzione di App Azure Container e illustra le considerazioni chiave sulla configurazione.
Preparare il contenitore per Azure
Configurazione delle porte. Assicurarsi che il contenitore sia in ascolto sulla porta fornita da Azure, come illustrato nell'esempio seguente:
FROM mcr.microsoft.com/java/jre:21-zulu-ubuntu WORKDIR /app COPY target/*.jar app.jar ENV PORT=8080 EXPOSE ${PORT} CMD java -jar app.jar --server.port=${PORT}
Sonda per la salute. Implementare probe di integrità per i controlli di attività e conformità di Azure.
Configurazione del registro. Configurare la registrazione per l'output in
stdout
/stderr
.Pianifica l'imprevisto. Impostare una corretta gestione dell'arresto normale con la configurazione del timeout. Per altre informazioni, vedere Gestione del ciclo di vita delle applicazioni in Azure Container Apps.