Conteneuriser une application Java

Terminée

Dans cette unité, vous conteneurisez une application Java.

Comme mentionné précédemment, les conteneurs s’exécutent directement sur le système d’exploitation hôte, le noyau et le matériel en tant que processus système ordinaires. Les conteneurs nécessitent moins de ressources système, ce qui réduit l’encombrement, la surcharge et accélère le démarrage des applications. Ces avantages sont de bons exemples de recours à la mise à l’échelle à la demande.

Il existe des conteneurs Windows et des conteneurs Linux. Dans ce module, vous utilisez le runtime Docker largement utilisé pour générer une image conteneur Linux. Vous déployez ensuite l’image conteneur Linux sur le système d’exploitation hôte de votre ordinateur local. Enfin, vous déployez l’image conteneur Linux sur Azure Kubernetes Service.

Vue d’ensemble de Docker

Le runtime Docker est utilisé pour générer, extraire, exécuter et envoyer (push) des images conteneur, comme illustré dans le diagramme suivant :

Diagramme montrant les commandes Docker build, pull, push et run qui connectent le client, l’hôte Docker et le registre.

Le tableau suivant décrit chaque commande Docker :

Commande Docker Descriptif
docker build Génère une image conteneur composée des instructions ou des couches nécessaires à Docker pour créer un conteneur en cours d’exécution à partir d’une image. Le résultat de cette commande est une image.
docker pull Télécharge une image à partir d’un registre dans le magasin d’images du démon Docker sélectionné par le client. Les couches d’images existantes peuvent être réutilisées. Dans ce module, votre environnement Docker local utilise des images à partir de registres pendant le développement ; plus tard, les nœuds AKS extrayent l’image de l’application à partir de Azure Container Registry pour le déploiement.
docker run Crée et démarre un conteneur à partir d’une image. Le conteneur exécute son processus de démarrage configuré, sous réserve de remplacements de ligne de commande. Les couches d’images fournissent le système de fichiers du conteneur ; Les instructions de build Dockerfile ne sont pas réexécutées au démarrage du conteneur.
docker push Azure Container Registry stocke les images afin qu’elles soient facilement disponibles et proches du réseau pour les déploiements et la mise à l’échelle Azure.

Clonez l’application Java

Tout d’abord, clonez le référentiel Flight Booking System for Airline Reservations et accédez au dossier du projet d’application web Airlines.

Remarque

Si la création d’Azure Kubernetes Service est terminée dans votre onglet CLI, utilisez cet onglet. S’il est toujours en cours d’exécution, ouvrez un nouvel onglet et accédez à l’emplacement où vous préférez cloner Flight Booking System for Airline Reservations.

Exécutez les commandes suivantes :

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

Si Java et Maven sont installés, vous pouvez exécuter la commande suivante dans votre console de terminal pour avoir une idée de l’expérience de création de l’application sans Docker. Si vous n’avez pas installé Java et Maven, vous pouvez passer en toute sécurité à la section suivante, construire un fichier Docker. Dans cette section, vous utilisez Docker pour télécharger Java et Maven pour exécuter les builds en votre nom.

mvn clean package

Remarque

Nous avons utilisé la mvn clean package commande pour illustrer les défis opérationnels liés au fait de ne pas utiliser les builds multistage de Docker, que nous abordons ensuite. Là encore, cette étape est facultative. Dans les deux cas, vous pouvez continuer en toute sécurité sans exécuter la commande Maven.

Si le processus a réussi, Maven a généré correctement l’artefact d’archive d’application Web du Flight Booking System for Airline Reservations AirlinesReservationSample-0.0.1-SNAPSHOT.war, comme illustré par la sortie d’historique suivante. L’horodatage et la durée de votre build peuvent différer :

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

Imaginez que vous êtes un développeur Java et que vous venez de créer AirlinesReservationSample-0.0.1-SNAPSHOT.war. L’étape suivante consiste probablement à travailler avec les ingénieurs d’opération pour déployer cet artefact sur un serveur local ou une machine virtuelle. Pour que l’application démarre et s’exécute correctement, les serveurs et les machines virtuelles doivent être disponibles et configurés avec les dépendances requises. Ce processus s’avère difficile et fastidieux, surtout pour les opérations à la demande, lorsque l’augmentation de la charge atteint votre application. Avec les conteneurs, ces défis sont atténués.

Créer un Dockerfile

Vous êtes maintenant prêt à construire un fichier Dockerfile. Un fichier Dockerfile est un document texte qui contient toutes les commandes qu’un utilisateur exécuterait sur la ligne de commande pour assembler une image conteneur. Une image conteneur peut contenir plusieurs couches de système de fichiers en lecture seule. Docker peut réutiliser les résultats de build mis en cache pour les étapes inchangées. Les couches se superposent.

Par exemple, Flight Booking System for Airline Reservations doit être déployé et exécuté à l’intérieur d’un serveur d’applications. Un serveur d’applications n’est pas empaqueté à l’intérieur de AirlinesReservationSample-0.0.1-SNAPSHOT.war. Il s’agit d’une dépendance externe nécessaire pour que AirlinesReservationSample-0.0.1-SNAPSHOT.war s’exécute, écoute et traite les requêtes HTTP, gère les sessions utilisateur et facilite les réservations de vols. Si vous avez utilisé un déploiement traditionnel et non conteneurisé, les ingénieurs d’opérations installeraient et configureraient un serveur d’applications sur un serveur physique ou une machine virtuelle avant d’y déployer AirlinesReservationSample-0.0.1-SNAPSHOT.war. Ces ingénieurs d’exploitation doivent également s’assurer que le JDK utilisé sur votre ordinateur (qui est ce que mvn clean package a utilisé pour compiler le fichier WAR) correspond au même JRE qu’utilise le serveur d’applications. La gestion de ces dépendances est difficile et fastidieuse.

Avec un fichier Dockerfile, vous pouvez écrire les instructions ou les couches nécessaires pour atteindre cet objectif automatiquement, en superposant les étapes nécessaires pour vous assurer que le système de réservation de vols des compagnies aériennes dispose de toutes les dépendances nécessaires au déploiement sur le runtime du conteneur Docker. Cette solution est particulièrement pertinente lorsque vous travaillez avec une mise à l’échelle à la demande à intervalles non planifiés. Chaque couche utilise le cache Docker, qui contient l’état de l’image de conteneur à chaque étape majeure de l’instruction, en optimisant le temps de calcul et la réutilisation. Si une couche ne change pas, les couches mises en cache sont utilisées. Les cas d’usage courants pour les couches mises en cache sont le runtime Java, le serveur d’applications et d’autres dépendances pour l’application Web Flight Booking System for Airline Reservations. Si et quand une version change sur une couche précédemment mise en cache, une nouvelle entrée mise en cache est créée.

Une image conteneur contient des couches de système de fichiers en lecture seule. Dans cet exemple, le fichier Dockerfile copie Tomcat et le fichier WAR flight Booking System dans l’image au moment de la génération. Docker ajoute une couche accessible en écriture distincte lorsqu’il crée un conteneur à partir de l’image. La couche accessible en écriture de chaque conteneur enregistre ses propres modifications de système de fichiers sans modifier les couches d’image.

Remarque

Pour les fichiers stockés dans la couche accessible en écriture du conteneur, les ajouts, les modifications et les suppressions affectent ce conteneur plutôt que l’image d’origine. La modification des fichiers provenant d’une couche d’image utilise le comportement de copie en écriture.

Docker propose des builds multi-étapes, une fonctionnalité qui vous permet de créer une image conteneur plus petite avec une meilleure mise en cache et une empreinte de sécurité plus petite, ce qui permet une optimisation et une maintenance accrues du fichier Dockerfile au fil du temps. Par exemple, vous pouvez séparer l’étape de génération de conteneur pour la compilation et la génération de l’application à partir de la phase d’exécution de l’application. Cette fonctionnalité vous permet de copier uniquement les artefacts générés pendant la génération vers l’image conteneur de production, ce qui réduit l’encombrement. Étant donné que les images conteneur sont mises en cache, si aucune modification n’est apportée, les images mises en cache peuvent être réutilisées, ce qui réduit le coût et le temps de téléchargement à partir du réseau.

Les services exposés dans l’environnement de production doivent être gérés avec soin pour la sécurité. Par conséquent, l’environnement de production utilise et exploite une image conteneur sécurisée. L’exemple utilise une image Linux Azure fournie par Microsoft.

La dernière étape utilise Build Microsoft d’OpenJDK 17 sur Azure Linux 3.0. La 17-azurelinux balise nomme explicitement cette base. L'ancienne balise 17-mariner reste un alias de compatibilité qui reflète l'image Azure Linux ; elle ne doit pas être confondue avec les images 17-mariner-cm2 et 17-mariner-cm1 retirées. Pour plus d’informations, consultez images de conteneur Build Microsoft d’OpenJDK.

Remarque

Microsoft fournit des images de conteneur regroupant OpenJDK, notamment des images Ubuntu, Azure Linux et distroless. L’image distroless a la plus petite taille d’image, mais l’exécution de Tomcat sur elle est difficile. Pour obtenir une conception légère, l’image distroless supprime de nombreuses commandes et outils, y compris l’interpréteur de commandes, ce qui signifie que vous ne pouvez pas appeler catalina.sh pour démarrer Tomcat. L’image distroless est adaptée à l’exécution des fichiers JARs exécutables, tels que ceux utilisés avec Spring Boot ou Quarkus.

Dans l’exemple suivant, la même version de Build Microsoft d’OpenJDK est utilisée à la fois dans l’étape de génération et dans la phase finale. Cette approche garantit que vous générez le code source avec la même version du JDK que la version de Tomcat utilisée pour le déploiement du service, ce qui permet d’éviter un comportement inattendu en raison d’incompatibilités de version.

L’image suivante illustre la build multistage et ce qui se produit dans chaque étape en fonction des commandes spécifiées dans le fichier Dockerfile :

Diagramme montrant les instructions Dockerfile qui créent des couches d’images dans les phases d’installation de Tomcat, de compilation du code et d’exécution.

À l’étape 0, Tomcat est téléchargé à partir d’une archive Apache spécifique à la version, vérifié avec une somme de contrôle SHA-512 et extrait dans un répertoire spécifié par une variable d’environnement sur une image Ubuntu. La variable TOMCAT_VERSION spécifie la version publiée et l’argument de build TOMCAT_SHA512 spécifie sa somme de contrôle attendue. Mettez à jour les deux valeurs ensemble lors de la sélection d’une version plus récente prise en charge. Docker peut réutiliser la couche d’installation mise en cache tant que ses instructions et entrées sont inchangées. L’installation de Tomcat est copiée dans l’environnement de phase finale pour être utilisée.

À l’étape 1, Maven est installé sur une image Ubuntu, et les fichiers de code source et de configuration créés sont copiés avant de générer le projet Maven. Chaque couche est mise en cache, de sorte que les couches image du système d’exploitation et Maven réutilisent le cache. Si les fichiers de configuration, les fichiers de code source ou le répertoire web sont mis à jour, les couches à partir de ces modifications sont reconstruites. Si la build se termine correctement sans erreur pendant la compilation, un artefact nommé AirlinesReservationSample-0.0.1-SNAPSHOT.war est généré sous le répertoire target. Cet artefact est copié vers l’environnement final pour utilisation.

Au cours de l’étape finale, les artefacts de build Tomcat et Java de l’étape 0 et de l’étape 1, respectivement, sont copiés dans une image Build Microsoft d’OpenJDK 17 sur Azure Linux 3.0. Un utilisateur nommé app possède tous les fichiers utilisés dans le projet et l’application est également exécutée en tant qu’utilisateur app au lieu d’être exécutée avec les privilèges root. Cette configuration garantit que l’image conteneur peut être exploitée en toute sécurité sans accorder d’autorisations inutiles. Enfin, le numéro de port 8080 est exposé et le script catalina.sh est exécuté pour démarrer Tomcat. Quand cette opération est exécutée sur votre Docker Desktop local, vous pouvez y accéder via l’URL http://localhost:8080/AirlinesReservationSample.

Dans containerize-and-deploy-Java-app-to-Azure/Project/Airlines, utilisez un éditeur de texte installé pour créer un fichier nommé Dockerfile. Ajoutez le contenu suivant et enregistrez le fichier :

Important

Avant de créer une image que vous allez exposer publiquement, vérifiez la version actuelle de Tomcat 10.1.x prise en charge sur la page de téléchargement Apache Tomcat et passez en revue les avis de sécurité. Mettez à jour TOMCAT_VERSION et TOMCAT_SHA512 ensemble à l’aide des informations de version publiées par Apache. Un artefact restant disponible dans l’archive Apache ne signifie pas que sa version est actuelle ou recommandée.

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

ENV CATALINA_HOME=/usr/local/tomcat

# Configure the reviewed Tomcat version and checksum
ENV TOMCAT_VERSION=10.1.61
ARG TOMCAT_SHA512=35120a2b1182bac3d55438fbec0dc581d03b0f1c16ce692a930f3bb6a1097cf896b94b389152257071a87cb2928ac8c15f1bad08387c14acc7c17dbc1bb598fb

# Install Tomcat and required packages
RUN apt-get update && \
    apt-get install -y --no-install-recommends curl ca-certificates && \
    curl -fSL \
      "https://archive.apache.org/dist/tomcat/tomcat-10/v${TOMCAT_VERSION}/bin/apache-tomcat-${TOMCAT_VERSION}.tar.gz" \
      -o /tmp/tomcat.tar.gz && \
    echo "${TOMCAT_SHA512}  /tmp/tomcat.tar.gz" | sha512sum -c - && \
    mkdir -p "${CATALINA_HOME}" && \
    tar -xzf /tmp/tomcat.tar.gz --strip-components=1 -C "${CATALINA_HOME}" && \
    rm /tmp/tomcat.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-azurelinux

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

Remarque

Utilisez le fichier Dockerfile indiqué dans cette unité pour cet exercice. L’application doit être copiée dans webapps/AirlinesReservationSample.war afin que les URL des unités suivantes utilisent le contexte /AirlinesReservationSample/. Un autre nom de fichier WAR modifie le contexte d’application Tomcat par défaut.

Le fichier Dockerfile est divisé en trois étapes, qui sont décrites dans les tableaux suivants :

  • Étape d’installation de Tomcat :

    Commande Docker Descriptif
    FROM FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS tomcat définit l’image de base sur Microsoft Build d’OpenJDK 17 sur Ubuntu et nomme cette étape tomcat. C’est là que Tomcat est installé.
    ENV ENV CATALINA_HOME=/usr/local/tomcat définit une variable d’environnement pour le répertoire d’installation de Tomcat.
    ENV ENV TOMCAT_VERSION=10.1.61 définit la version tomcat épinglée pour cet exemple. Passez en revue les versions prises en charge et mettez-la à jour avec TOMCAT_SHA512, si nécessaire.
    ARG ARG TOMCAT_SHA512 fournit la somme de contrôle SHA-512 publiée d’Apache pour l’archive sélectionnée en tant qu’argument de build.
    RUN Installe curl -fSL et les certificats d’autorité de certification, puis utilise curl pour télécharger l’archive Apache spécifique à la version. -f fait échouer en cas d’erreurs HTTP ; -L suit les redirections. L’archive est vérifiée avec sha512sum -c - avant l’extraction dans CATALINA_HOME avec --strip-components=1. La && chaîne s’arrête en cas d’échec au lieu de masquer les erreurs pendant le nettoyage. Enfin, il supprime l’archive et les packages inutiles et nettoie le cache du package.
  • Phase de génération, qui se compile avec Java 17 :

    Commande Docker Descriptif
    FROM FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS build définit l’image de base sur Microsoft Build d’OpenJDK 17 sur Ubuntu et nomme cette étape build. Cette étape est utilisée pour compiler l’application Java.
    WORKDIR WORKDIR /build définit le répertoire de travail dans le conteneur à /build, où le code source est copié et compilé.
    RUN RUN apt-get update && apt-get install -y maven && mvn --version installe Maven, un outil d’automatisation de build utilisé pour les projets Java et vérifie son installation.
    COPY COPY pom.xml . copie le fichier de configuration Maven dans le répertoire de travail. Ce fichier est essentiel pour la création du projet.
    COPY COPY src ./src copie le répertoire de code source dans le conteneur. C’est là que réside le code de l’application Java.
    COPY COPY web ./web copie le répertoire des ressources web dans le conteneur. Cela inclut les ressources d’application web nécessaires à la génération.
    RUN RUN mvn clean package exécute le processus de génération Maven, qui compile l’application Java et les empaquet dans un fichier WAR.
  • Dernière étape du package :

    Commande Docker Descriptif
    FROM FROM mcr.microsoft.com/openjdk/jdk:17-azurelinuxdéfinit l’image de base sur Build Microsoft d’OpenJDK 17 sur Azure Linux 3.0, qui est utilisée pour le déploiement final de l’application.
    ENV ENV CATALINA_HOME=/usr/local/tomcat définit la variable d’environnement pour le répertoire d’installation Tomcat, similaire à la phase d’installation.
    ENV ENV PATH=$CATALINA_HOME/bin:$PATH ajoute le répertoire bin de Tomcat au PATH système, ce qui permet aux commandes Tomcat d’être exécutées facilement.
    USER USER app spécifie l’utilisateur sous lequel le processus Tomcat s’exécute, ce qui améliore la sécurité en ne s’exécutant pas en tant qu’utilisateur racine.
    COPY COPY --chown=app:app --from=tomcat ${CATALINA_HOME} ${CATALINA_HOME} copie l’installation de Tomcat à partir de la phase tomcat, en définissant la propriété sur l’utilisateur app.
    COPY COPY --chown=app:app tomcat-users.xml ${CATALINA_HOME}/conf copie le fichier de configuration de l’utilisateur Tomcat dans le conteneur, en définissant la propriété sur l’utilisateur app.
    COPY COPY --chown=app:app --from=build /build/target/*.war ${CATALINA_HOME}/webapps/AirlinesReservationSample.war copie le fichier WAR compilé à partir de la build phase dans le répertoire webapps de Tomcat, en définissant le propriétaire sur l’utilisateur app.
    EXPOSE EXPOSE 8080 indique que l’application est censée écouter sur le port TCP 8080. Il ne publie pas de port hôte. L’unité suivante publie le port avec l’option -p 8080:8080 sur docker run.
    CMD CMD ["catalina.sh", "run"] spécifie la commande pour démarrer Tomcat lors de l’exécution du conteneur.

Pour plus d’informations sur la construction de Dockerfile, consultez la référence dockerfile.