Présentation des conteneurs pour les applications Java

Les conteneurs fournissent un environnement cohérent et portable pour vos applications Java au cours des phases de développement, de test et de production. Cet article présente les concepts de conteneurisation des applications Java et vous guide tout au long de la création, du débogage, de l’optimisation et du déploiement d’applications Java conteneurisées dans Azure Container Apps.

Dans cet article, vous allez découvrir les concepts de conteneurisation essentiels pour les développeurs Java et les compétences suivantes :

  • Configuration de votre environnement de développement pour les applications Java conteneurisées.
  • Création de fichiers Dockerfile optimisés pour les charges de travail Java.
  • Configuration des flux de travail de développement locaux avec des conteneurs.
  • Débogage d’applications Java conteneurisées.
  • Optimisation des conteneurs Java pour la production.
  • Déploiement de vos applications Java conteneurisées sur Azure Container Apps.

En conteneurisant vos applications Java, vous obtenez des environnements cohérents, un déploiement simplifié, une utilisation efficace des ressources et une scalabilité améliorée.

Conteneurs pour les applications Java

Les conteneurs empaquetent des applications avec leurs dépendances, garantissant ainsi la cohérence entre les environnements. Pour les développeurs Java, cela signifie regrouper l’application, ses dépendances, le Java Runtime Environment/Java Development Kit (JRE/JDK) et les fichiers de configuration dans une unité portable unique.

La conteneurisation présente des avantages clés par rapport à la virtualisation qui le rend idéal pour le développement cloud. Contrairement à une machine virtuelle, un conteneur s’exécute sur le noyau du système d’exploitation hôte d’un serveur. Cela est bénéfique pour les applications Java, qui s’exécutent déjà dans la machine virtuelle Java (JVM). La conteneurisation d’applications Java ajoute une surcharge minimale et offre des avantages significatifs en matière de déploiement.

L’écosystème de conteneurs comprend les composants clés suivants :

  • Images : blueprints.
  • Conteneurs : exécution d’instances.
  • Registres : où sont stockées les images.
  • Orchestrators : systèmes qui gèrent des conteneurs à grande échelle.

Docker est la plateforme de conteneurisation la plus populaire et elle est bien prise en charge dans l’écosystème Azure via Azure Container Apps.

Configurer votre environnement de développement

Cette section vous guide tout au long de l’installation des outils nécessaires et de la configuration de votre environnement de développement pour générer, exécuter et déboguer des applications Java conteneurisées.

Installer les outils requis

Pour conteneuriser des applications Java, vous avez besoin des outils suivants installés sur votre ordinateur de développement :

Vérifiez votre installation à l’aide des commandes suivantes :

docker --version
docker compose version

Configurer Visual Studio Code pour le développement de conteneurs

Pour le développement Java dans des conteneurs, configurez Visual Studio Code en installant le pack d’extension Java et en configurant votre JDK. L’extension Dev Containers vous permet d’ouvrir n’importe quel dossier à l’intérieur d’un conteneur et d’utiliser le jeu de fonctionnalités complet de Visual Studio Code à l’intérieur de ce conteneur.

Pour permettre à Visual Studio Code de générer et de se connecter automatiquement à un conteneur de développement, créez un fichier .devcontainer/devcontainer.json dans votre projet.

Par exemple, l’exemple de configuration suivant définit une 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"
}

Cette configuration utilise l’image conteneur de développement Java de Microsoft, ajoute des extensions essentielles et transfère à la fois le port d’application et 8080le port de débogage. 5005

Créer un Dockerfile

Un fichier Dockerfile contient des instructions pour créer une image Docker. Pour les applications Java, le fichier Dockerfile inclut généralement les composants suivants :

  • Image de base avec le JDK ou JRE.
  • Instructions pour copier des fichiers d’application.
  • Commandes pour définir des variables d’environnement.
  • Configurations des points d’entrée.

Sélectionner une image de base

Le choix de l’image de base appropriée est crucial. Tenez compte de ces options :

Descriptif Nom Remarques
Image de développement Java par Microsoft mcr.microsoft.com/java/jdk:21-zulu-ubuntu JDK complet et optimisé pour Azure
Image de production de Java de Microsoft mcr.microsoft.com/java/jre:21-zulu-ubuntu Runtime uniquement et optimisé pour Azure
Image de développement OpenJDK officielle openjdk:21-jdk JDK complet
Image officielle de production OpenJDK openjdk:21-jre Runtime uniquement

Pour les environnements de développement, utilisez une image JDK complète. Pour la production, utilisez une image JRE ou distroless pour réduire la taille et la surface d’attaque de votre application.

Les images Microsoft Java sont fournies avec des optimisations spécifiques à Azure et sont régulièrement mises à jour avec des correctifs de sécurité, ce qui les rend idéales pour les applications ciblant Azure Container Apps.

Exemples de Dockerfile de base

L’exemple suivant montre un fichier Dockerfile simple pour une application 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"]

Pour les applications Spring Boot, vous pouvez configurer votre fichier Dockerfile avec la base suivante :

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

Pour les déploiements de production, utilisez l’image JRE illustrée dans l’exemple suivant pour réduire la taille et réduire la surface d’attaque de votre application :

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

Développement local avec des conteneurs

Les conteneurs sont destinés à s’exécuter dans différents contextes. Dans cette section, vous allez apprendre un flux de développement local à utiliser avec des conteneurs.

Utiliser Docker Compose pour les applications multiconteneurs

La plupart des applications Java interagissent avec les bases de données, les caches ou d’autres services. Docker Compose vous permet de définir et d’orchestrer des applications multiconteneurs à l’aide d’un fichier de configuration YAML simple.

Qu’est-ce que Docker Compose ?

Docker Compose est un outil qui vous permet d’effectuer les tâches suivantes :

  • Définissez des applications multiconteneurs dans un seul fichier.
  • Gérez le cycle de vie de l’application, notamment le démarrage, l’arrêt et la reconstruction.
  • Gérer les environnements isolés.
  • Créez des réseaux pour la communication de service.
  • Conserver les données à l’aide de volumes.

Exemple : application Java avec base de données

Le fichier compose.yml suivant configure une application Java avec une base de données 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

Ce fichier présente les caractéristiques suivantes :

  • Les services peuvent se référencer par nom , par exemple, db dans l’URL JDBC.
  • Docker Compose crée automatiquement un réseau pour les services.
  • L’application Java attend que la base de données démarre, en raison de depends_on.
  • Les données de base de données persistent entre les redémarrages à l’aide d’un volume nommé.

Commandes Docker Compose courantes

Après avoir créé votre fichier compose.yml , gérez votre application à l’aide des commandes suivantes :

# 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

Flux de travail de développement

Un workflow de développement Java classique à l’aide de Docker Compose contient les étapes suivantes :

  1. Créez le fichier compose.yml et le fichier Dockerfile.
  2. Exécutez docker compose up pour démarrer tous les services.
  3. Apportez des modifications à votre code Java.
  4. Régénérez votre application. Selon la configuration, vous devrez peut-être redémarrer vos conteneurs.
  5. Testez les modifications dans l’environnement conteneurisé.
  6. Lorsque vous avez terminé, exécutez docker compose down.

Exécuter des conteneurs uniques avec Docker

Pour des scénarios plus simples lorsque vous n’avez pas besoin de plusieurs services interconnectés, vous pouvez utiliser la docker run commande pour démarrer des conteneurs individuels.

Les commandes Docker suivantes sont typiques pour les applications 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

Déboguer des applications conteneurisées

Le débogage d’applications Java conteneurisées est parfois difficile, car votre code s’exécute dans un environnement isolé à l’intérieur du conteneur.

Les approches de débogage standard ne s’appliquent pas toujours directement, mais avec la configuration appropriée, vous pouvez établir une connexion de débogage à distance à votre application. Cette section vous montre comment configurer vos conteneurs pour le débogage, connecter vos outils de développement à des conteneurs en cours d’exécution et résoudre les problèmes courants liés aux conteneurs.

Configurer le débogage à distance

Le débogage d’applications Java conteneurisées nécessite l’exposition d’un port de débogage et la configuration de votre IDE pour la connecter. Vous pouvez effectuer ces tâches en procédant comme suit :

  1. Pour activer le débogage, modifiez votre fichier Dockerfile afin qu’il contienne le contenu suivant :

    Remarque

    Vous pouvez modifier plutôt la commande de démarrage de votre conteneur.

    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"]
    
  2. Configurez le fichier launch.jsonde Visual Studio Code pour vous connecter au port de débogage, comme illustré dans l’exemple suivant :

    {
      "version": "0.2.0",
      "configurations": [
        {
          "type": "java",
          "name": "Debug in Container",
          "request": "attach",
          "hostName": "localhost",
          "port": 5005
        }
      ]
    }
    
  3. Démarrez votre conteneur avec le port 5005 mappé à votre hôte, puis lancez le débogueur dans Visual Studio Code.

Résoudre les problèmes de conteneur

Lorsque les conteneurs ne se comportent pas comme prévu, vous pouvez inspecter les journaux de votre application pour examiner le problème.

Utilisez les commandes suivantes pour résoudre les problèmes de votre application. Avant d’exécuter ces commandes, veillez à remplacer les espaces réservés (<...>) par vos propres valeurs.

# 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

Pour les problèmes spécifiques à Java, activez les indicateurs JVM pour de meilleurs diagnostics, comme illustré dans l’exemple suivant :

ENTRYPOINT ["java", "-XX:+PrintFlagsFinal", "-XX:+PrintGCDetails", "-jar", "app.jar"]

Le tableau suivant répertorie les problèmes courants et les solutions correspondantes :

Erreur Solution possible
Mémoire insuffisante Augmenter les limites de mémoire du conteneur
Expiration du délai de connexion Vérifiez la configuration du réseau pour connaître les erreurs. Vérifiez les ports et les règles de routage.
Problèmes d’autorisation Vérifiez les autorisations du système de fichiers.
Problèmes classpath Vérifiez la structure et les dépendances JAR.

Optimiser les conteneurs Java

Les applications Java dans les conteneurs nécessitent une considération particulière pour des performances optimales. La machine virtuelle JVM a été conçue avant que les conteneurs ne soient courants. L’utilisation de conteneurs peut entraîner des problèmes d’allocation de ressources s’ils ne sont pas correctement configurés.

Vous pouvez améliorer considérablement les performances et l’efficacité de vos applications Java conteneurisées en configurant les paramètres de mémoire, en optimisant la taille de l’image et en configurant le garbage collection. Cette section traite des optimisations essentielles pour les conteneurs Java, avec un focus sur la gestion de la mémoire, le temps de démarrage et l’utilisation des ressources.

Configuration de la mémoire JVM dans les conteneurs

La machine virtuelle JVM ne détecte pas automatiquement les limites de mémoire du conteneur dans Java 8. Pour Java 9+, la prise en charge des conteneurs est activée par défaut.

Configurez votre machine virtuelle JVM pour respecter les limites de conteneur, comme indiqué dans l’exemple suivant :

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

Les indicateurs JVM suivants sont importants pour les applications en conteneur :

  • -XX:MaxRAMPercentage=75.0. Définit le tas maximal sous la forme d’un pourcentage de mémoire disponible.
  • -XX:InitialRAMPercentage=50.0. Définit la taille initiale du tas.
  • -Xmx et -Xms. Ces indicateurs sont également disponibles, mais ils nécessitent des valeurs fixes.

Préparer le déploiement de production

Le déplacement d’applications Java en conteneur vers la production nécessite des considérations au-delà des fonctionnalités de base.

Les environnements de production demandent une sécurité robuste, une surveillance fiable, une allocation de ressources appropriée et une flexibilité de configuration.

Cette section décrit les pratiques et configurations essentielles nécessaires pour préparer vos conteneurs Java pour une utilisation en production. La section se concentre sur la sécurité, les contrôles d’intégrité et la gestion de la configuration pour garantir que vos applications s’exécutent de manière fiable en production.

Bonnes pratiques de sécurité

Sécurisez vos applications Java conteneurisées à l’aide des pratiques suivantes :

  • Contexte de sécurité par défaut. Exécutez vos applications en tant qu’utilisateur non racine, comme illustré dans l’exemple suivant :

    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"]
    
  • Recherchez de manière proactive des problèmes. Analysez régulièrement les images conteneur pour détecter les vulnérabilités à l’aide de la commande suivante :

    docker scan myapp:latest
    
  • Actualisation de l’image de base. Conservez vos images de base à jour.

  • Gestion des secrets. Implémentez une gestion appropriée des secrets. Par exemple, ne codez pas en dur les données sensibles dans votre application et utilisez un coffre de clés dans la mesure du possible.

  • Contextes de sécurité restreints. Appliquez le principe du privilège minimum à tous les contextes de sécurité.

  • Accès au système de fichiers. Utilisez les systèmes de fichiers en lecture seule dans la mesure du possible.

Examens de santé et surveillance

Vérifiez l’intégrité de l’application avec des sondes pour vous assurer que votre application s’exécute correctement.

Pour les applications Spring Boot, incluez la dépendance d’actionneur pour les points de terminaison d’intégrité complets, comme illustré dans l’exemple suivant :

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

Configurez votre application pour générer des journaux d’activité dans un format adapté aux environnements de conteneur, tels que JSON.

Déployer sur Azure Container Apps

Cette section vous guide tout au long de la préparation de vos conteneurs Java pour le déploiement d’Azure Container Apps et met en évidence les principales considérations de configuration.

Préparer votre conteneur pour Azure

  • Configuration du port. Vérifiez que votre conteneur écoute sur le port fourni par Azure, comme illustré dans l’exemple suivant :

    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}
    
  • Sondez l’intégrité. Implémentez des sondes d’intégrité pour les vérifications de disponibilité et d’état de préparation d’Azure.

  • Configuration du journal. Configurez la journalisation en sortie vers stdout/stderr.

  • Planifiez l’inattendu. Configurez la gestion de l’arrêt approprié avec une configuration de délai d’attente. Pour plus d’informations, consultez Gestion du cycle de vie des applications dans Azure Container Apps.