Conteneuriser une application Java

Effectué

Dans cette leçon, vous allez conteneuriser une application Java.

Comme mentionné précédemment, les conteneurs s’exécutent directement sur le système d’exploitation, le noyau et le matériel hôtes, comme tous les autres processus du système. Les conteneurs ont besoin de moins de ressources système, ce qui réduit l’empreinte, diminue la charge et accélère le démarrage des applications. Il s’agit de cas d’usage importants pour la mise à l’échelle à la demande.

Il y a des conteneurs Windows et Linux. Dans ce module, vous allez exploiter le populaire runtime Docker utilisé pour créer une image conteneur Linux. Vous allez ensuite déployer l’image conteneur Linux sur le système d’exploitation hôte de votre ordinateur local. Enfin, vous allez déployer 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 des images conteneur. L’illustration suivante représente ces cas d’usage avec leur description et une explication de la commande Docker.

Diagram showing Docker commands.

Commande docker Description
docker build Génère une image conteneur—essentiellement les instructions/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 Les conteneurs sont initialisés à partir d’images qui sont extraites de référentiels comme l’Azure Container Registry. C’est de là qu’Azure Kubernetes Service extrait les éléments. Le résultat de cette commande est l’extraction réseau d’une image qui sera dans Azure. Notez que vous pouvez éventuellement extraire des images localement, ce qui est courant lors de la génération d’images nécessitant des dépendances/couches dont votre application peut avoir besoin, comme un serveur d’applications.
docker run Une instance en cours d’exécution d’une image est un conteneur. Cette commande exécute toutes les couches nécessaires à l’exécution et à l’interaction avec l’application conteneur en cours d’exécution. Le résultat de cette commande est un processus d’application en cours d’exécution sur le système d’exploitation hôte.
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.

Cloner l’application Java

Tout d’abord, vous allez cloner le référentiel Flight Booking System for Airline Reservations et le cd vers le dossier du projet d’application web Airlines.

Notes

Si la création de l’instance Azure Kubernetes Service est terminée dans votre onglet CLI, utilisez cet onglet. Si elle est toujours en cours d’exécution, ouvrez un nouvel onglet et un cd à l’emplacement où vous préférez cloner le système Flight Booking System for Airline Reservations.

Exécutez la commande suivante dans CLI :

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

Exécutez la commande suivante dans CLI :

cd containerize-and-deploy-Java-app-to-Azure/Project/Airlines

Remarque

De façon facultative, si Java et Maven sont installés, vous pouvez aussi exécuter la ou les commandes suivantes dans votre CLI pour avoir une idée de l’expérience de création de l’application sans Docker. Si Java et Maven ne sont pas installés, vous pouvez tout à fait passer à la section suivante intitulée Construire un fichier Docker. Dans cette section, vous utilisez Docker pour extraire Java et Maven afin qu’ils exécutent les builds à votre place.

Si Maven et un JDK (8) ou une version ultérieure sont installés, vous pouvez exécuter la commande suivante dans votre interface CLI (facultatif) :

mvn clean install

Remarque

Nous avons utilisé la commande mvn clean install pour illustrer les défis opérationnels lorsque vous n’utilisez pas de builds multiphases Docker. Nous aborderons ces derniers par après. Là encore, cette étape est facultative. Vous pouvez de toute façon continuer sans exécuter la commande Maven.

Maven doit avoir correctement créé l’artefact d’archive d’application web Flight Booking System for Airline Reservations FlightBookingSystemSample-0.0.-SNAPSHOT.war, comme dans la sortie suivante :

[INFO] Building war: /mnt/c/Users/chtrembl/dev/git/containerize-and-deploy-Java-app-to-Azure/Project/FlightBookingSystemSample/target/FlightBookingSystemSample-0.0.1-SNAPSHOT.war
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  17.698 s
[INFO] Finished at: 2021-09-28T15:18:07-04:00
[INFO] ------------------------------------------------------------------------

Imaginez que vous êtes développeur Java et que vous venez de créer FlightBookingSystemSample-0.0.1-SNAPSHOT.war. Votre prochaine étape consisterait probablement à travailler avec les ingénieurs d’exploitation pour déployer cet artefact sur un serveur local ou sur une machine virtuelle. Pour que l’application démarre et s’exécute correctement, les serveurs et les machines virtuelles sont disponibles et configurés avec les dépendances requises. Cela 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

À ce stade, vous êtes prêt à créer un Dockerfile. Un fichier Dockerfile est un document texte qui contient toutes les commandes qu’un utilisateur peut exécuter sur la ligne de commande pour assembler une image conteneur. Chacune d’entre elles prend la forme de couches (pouvant être mises en cache pour plus d’efficacité), qui sont générées les unes par-dessus les autres.

Par exemple, le système 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 du FlightBookingSystemSample-0.0.1-SNAPSHOT.war. Il s’agit d’une dépendance externe nécessaire pour que FlightBookingSystemSample-0.0.1-SNAPSHOT.war exécute, écoute et traite les requêtes HTTP, gère les sessions utilisateur et facilite les réservations de vols. S’il s’agissait d’un déploiement traditionnel (non conteneurisé), les ingénieurs d’exploitation installeraient et configureraient un serveur d’applications sur certains serveurs physiques et/ou machines virtuelles, avant de déployer FlightBookingSystemSample-0.0.1-SNAPSHOT.war sur ce déploiement. Ces ingénieurs opérationnels doivent également s’assurer que le JDK utilisé sur votre ordinateur (ce que mvn clean install utilise 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 Dockerfile, vous pouvez écrire les instructions (couches) nécessaires pour effectuer cette procédure automatiquement, en superposant les étapes nécessaires afin de vous assurer que le système Flight Booking System for Airline Reservations dispose de toutes les dépendances nécessaires pour effectuer le déploiement sur le runtime du conteneur Docker. Cela est très intéressant lorsque vous commencez à réfléchir à la mise à l’échelle à la demande à des intervalles non planifiés. Chaque couche utilise le cache Docker, qui contient l’état de l’image conteneur à chaque étape majeure de l’instruction, en optimisant le temps de calcul et la réutilisation. Si une couche ne se modifie pas, les couches mises en cache sont utilisées. Les cas d’usage courants pour les couches mises en cache sont des éléments tels que le runtime Java, le serveur d’applications et/ou d’autres dépendances pour l’application web Flight Booking System for Airline Reservations. Si et lorsqu’une version est modifiée sur une couche précédemment mise en cache, une nouvelle entrée mise en cache est créée.

L’image suivante représente les couches d’une image conteneur. Vous remarquerez que la couche supérieure est la couche d’application web Flight Booking System for Airline Reservations en lecture/écriture, qui repose sur les couches en lecture seule précédentes, qui sont toutes le résultat des commandes du Dockerfile.

Diagram showing the Docker layers.

Docker présente également le concept de builds multiphases. C’est 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. Cela permet une optimisation et une maintenance accrues du fichier Dockerfile au fil du temps. Par exemple, cela peut être des instructions que vous pouvez utiliser pour effectuer une compilation de l’application (FlightBookingSystemSample-0.0.1-SNAPSHOT.war) ainsi qu’un build de l’image conteneur elle-même, vous permettant de délaisser le reste de la compilation FlightBookingSystemSample-0.0.1-SNAPSHOT.war, entraînant ainsi une empreinte plus petite. À long terme, vous êtes gagnant lorsque vous commencez à penser à ces images qui se baladent sur le réseau. Avec les builds multiphases, vous utilisez plusieurs instructions FROM dans votre Dockerfile. Chaque instruction FROM peut utiliser une base différente, et chacune de ces instructions commence par une nouvelle ardoise, supprimant tous les fichiers inutiles de la couche de mise en cache qui pourraient normalement être mis en cache.

L’application doit absolument être générée par le JDK correspondant au même JRE qui sera isolé dans l’image conteneur au moment de l’exécution. Dans l’exemple suivant, vous allez avoir une phase Génération qui tire profit d’une version spécifique de Maven et d’une version spécifique du JDK pour compiler FlightBookingSystemSample-0.0.1-SNAPSHOT.war. Cette phase garantit que tout runtime Docker qui l’exécute obtient le code d’octet généré attendu que l’auteur du Dockerfile a spécifié (dans le cas contraire, les ingénieurs d’opération doivent croiser leur runtime Java et de leur serveur d’application avec celui du développeur). L’étape du package utilisera ensuite une version spécifique de Tomcat et le JRE correspondant au JDK à l’étape de génération. Là encore, cela permet de s’assurer que toutes les dépendances (Java Development Kit JDK, Java Runtime Environment JRE, serveur d’applications) sont contrôlées et isolées pour garantir le comportement attendu sur tous les ordinateurs où cette image s’exécutera.

Il est également intéressant de noter qu’avec ce build multiphase, il n’est techniquement pas nécessaire d’installer Maven et Java sur le système. Docker les extrait afin de les utiliser pour la création et le runtime de l’application, ce qui évite tout conflit potentiel de version et tout comportement inattendu, sauf si, bien sûr, vous compilez du code et créez des artefacts en dehors de Docker.

L’image suivante représente le build multiphase et ce qui se produit à chaque étape en fonction des commandes spécifiées dans le fichier Dockerfile. Dans la phase 0, la phase Génération, l’application web Flight Booking System for Airline Reservations est compilée et FlightBookingSystemSample-0.0.1-SNAPSHOT.war est généré. Cette étape permet d’assurer la cohérence des versions de Maven et Java utilisées pour compiler cette application. Une fois que FlightBookingSystemSample-0.0.1-SNAPSHOT.war est créé, il s’agit de la seule couche nécessaire pour la phase 1 (phase Runtime), toutes les couches précédentes peuvent être ignorées. Docker utilise ensuite cette couche FlightBookingSystemSample-0.0.1-SNAPSHOT.war de la phase 0 pour créer les couches restantes nécessaires pour l’exécution. Dans le cas présent, il s’agit de configurer le serveur d’applications et de démarrer l’application.

Diagram showing the Docker multistage build.

Dans la racine de votre projet, containerize-and-deploy-Java-app-to-Azure/Project/Airlines, créez un fichier appelé Dockerfile :

vi Dockerfile

Ajoutez le contenu suivant au fichier Dockerfile. Ensuite, enregistrez et quittez en appuyant sur Échap, puis tapez :wq! et appuyez sur Entrée :

#
# Build stage
#
FROM maven:3.6.0-jdk-11-slim AS build
WORKDIR /build
COPY pom.xml .
COPY src ./src
COPY web ./web
RUN mvn clean package

#
# Package stage
#
FROM tomcat:8.5.72-jre11-openjdk-slim
COPY tomcat-users.xml /usr/local/tomcat/conf
COPY --from=build /build/target/*.war /usr/local/tomcat/webapps/FlightBookingSystemSample.war
EXPOSE 8080
CMD ["catalina.sh", "run"]

Remarque

Le fichier Dockerfile_Solution à la racine de votre projet peut avoir le contenu nécessaire.

Cette phase Génération de Dockerfile comprend six instructions :

Commande docker Description
FROM FROM maven est la couche de base à partir de laquelle ce FlightBookingSystemSample-0.0.1-SNAPSHOT.war est généré, une version spécifique de Maven et une version spécifique du JDK pour garantir l’existence de la même compilation de code d’octet sur les ordinateurs qui exécutent ce build.
WORKDIR WORKDIR est utilisé pour définir le répertoire de travail d’un conteneur à un moment donné. Ici, il s’agit de l’endroit où résident les artefacts compilés.
COPY COPY ajoute des fichiers à partir du répertoire actif de votre client Docker. Lors de la configuration des fichiers nécessaires à la compilation de Maven, pom.xml sera requis par le contexte Docker.
COPY Configure les fichiers nécessaires à la compilation de Maven. Le contexte Docker aura besoin du dossier src qui contient l’application web Flight Booking System for Airline Reservations.
COPY Configure les fichiers nécessaires à la compilation de Maven. Le contexte Docker web aura besoin du dossier qui contient les dépendances de l’application web Flight Booking System for Airline Reservations.
EXÉCUTER L’instruction RUN mvn clean package est utilisée pour exécuter une commande par-dessus l’image actuelle. Dans le cas présent, RUN est utilisé pour exécuter la build Maven, qui compilera FlightBookingSystemSample-0.0.1-SNAPSHOT.war.

Cette phase Package du Dockerfile comprend cinq instructions :

Commande docker Description
FROM FROM tomcat sera la couche de base sur laquelle cette image conteneur sera générée. L’image conteneur Flight Booking System for Airline Reservation sera une image générée sur l’image Tomcat. Le runtime Docker tente de localiser l’image tomcat localement. S’il ne dispose pas de cette version, il en extrait une du registre. Si vous inspectez l’image Tomcat référencée ici, vous verrez qu’elle est créée à l’aide de nombreuses autres couches qui la rendent réutilisable en tant qu’image conteneur de serveur d’applications empaquetée à utiliser lors du déploiement de son application Java. Nous avons sélectionné et testé tomcat:8.5.72-jre11-openjdk-slim dans le cadre du module. Notez que toutes les couches précédentes de la première phase Génération disparaissent une fois que Docker reconnaît cette seconde instruction FROM.
COPY COPY tomcat-users.xml copie le fichier tomcat-users.xml qui gère les utilisateurs Flight Booking System for Airline Reservations (gérés dans le contrôle de code source à l’aide de l’identité Tomcat, généralement dans un système de gestion des identités externe) dans l’image conteneur Tomcat afin de l’avoir dans l’image conteneur à chaque fois qu’une image conteneur est créée.
ADD ADD target/*.war /usr/local/tomcat/webapps/FlightBookingSystemSample.war copie le FlightBookingSystemSample-0.0.1-SNAPSHOT.war compilé par Maven dans le dossier webapps des images Tomcat afin de garantir qu’il trouve le FlightBookingSystemSample-0.0.1-SNAPSHOT.war à installer sur le serveur d’application lors de l’initialisation de Tomcat.
EXPOSE EXPOSE 8080 est nécessaire, car Tomcat est configuré pour écouter le trafic sur le port 8080. Cela garantit que le processus Docker écoute sur ce port.
CMD L’instruction CMD définit une commande à exécuter lors de l’exécution du conteneur. Ici, CMD ["catalina.sh", "run"] demande à Docker d’initialiser le serveur d’applications Tomcat.

Remarque

Sans balise de version sur la ligne FROM tomcat, la dernière version sera appliquée. En général, il y a un avantage à tirer profit d’une balise de version (n’oubliez pas que la mise en cache est appliquée, donc si les couches changent constamment, cela implique de la bande passante, de la latence, du temps de calcul et/ou des effets secondaires provenant des builds/couches non testés). Pour les besoins de ce module, nous avons présélectionné des balises spécifiques Maven, Tomcat et Java JRE/JDK qui sont testées pour fonctionner avec FlightBookingSystemSample-0.0.1-SNAPSHOT.war au moment de l’exécution.

Pour plus d’informations sur la construction des Dockerfiles, consultez la référence Dockerfile