Développer des conteneurs avec des Dockerfiles

1. Présentation

Docker est une plate-forme ouverte permettant de développer, de lancer et d'exécuter des applications. Avec cette solution, vous pouvez séparer les applications de votre infrastructure et traiter cette dernière comme une application gérée. Docker vous fait gagner du temps lors des phases de lancement, de test et de déploiement du code, et raccourcit le cycle entre la rédaction et l'exécution du code.

Pour ce faire, Docker combine des fonctionnalités de conteneurisation de noyau avec des workflows et des outils qui vous aident à gérer et à déployer vos applications.

Les conteneurs Docker peuvent être utilisés directement dans Kubernetes, ce qui permet de les exécuter facilement dans Kubernetes Engine. Après avoir découvert les principes de base de Docker, vous disposerez de toutes les compétences requises pour développer des applications Kubernetes et des applications conteneurisées.

Objectifs de l'atelier

Dans cet atelier, vous allez apprendre à effectuer les tâches suivantes :

  • Créer un Dockerfile pour un exemple d'application
  • Créer une image
  • Exécuter l'image en tant que conteneur localement
  • Modifier le comportement du conteneur
  • Transférer l'image vers Artifact Registry

Prérequis

Cet atelier s'adresse aux débutants. Il ne nécessite aucune connaissance particulière de Docker ni des conteneurs. Une expérience de Cloud Shell et de la ligne de commande est recommandée (mais pas indispensable).

Configuration de l'environnement au rythme de chacun

  1. Connectez-vous à la console Google Cloud, puis créez un projet ou réutilisez un projet existant. (Si vous ne possédez pas encore de compte Gmail ou Google Workspace, vous devez en créer un.)

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • Le nom du projet est le nom à afficher pour les participants au projet. Il s'agit d'une chaîne de caractères qui n'est pas utilisée par les API Google, et que vous pouvez modifier à tout moment.
  • L'ID du projet doit être unique sur l'ensemble des projets Google Cloud et doit être immuable (vous ne pouvez pas le modifier une fois que vous l'avez défini). Cloud Console génère automatiquement une chaîne unique dont la composition importe peu, en général. Dans la plupart des ateliers de programmation, vous devrez référencer l'ID du projet (généralement identifié comme PROJECT_ID), donc s'il ne vous convient pas, générez-en un autre au hasard ou définissez le vôtre, puis vérifiez s'il est disponible. Il est ensuite "gelé" une fois le projet créé.
  • La troisième valeur est le numéro de projet, utilisé par certaines API. Pour en savoir plus sur ces trois valeurs, consultez la documentation.
  1. Vous devez ensuite activer la facturation dans Cloud Console afin d'utiliser les ressources/API Cloud. L'exécution de cet atelier de programmation est très peu coûteuse, voire sans frais. Pour arrêter les ressources afin d'éviter qu'elles ne vous soient facturées après ce tutoriel, suivez les instructions de nettoyage indiquées à la fin de l'atelier. Les nouveaux utilisateurs de Google Cloud peuvent participer au programme d'essai gratuit pour bénéficier d'un crédit de 300 $.

2. Exemple d'application

Un exemple d'application est fourni pour faciliter cet atelier. Dans cette section, vous allez récupérer le code source et créer l'application dans son format natif avant de passer au processus de conteneurisation.

Code source

Le code source de cet atelier est disponible dans le dépôt GoogleCloudPlatform/container-developer-workshop, ainsi que dans l'exemple de documentation d'application.

Configurer Git

git config --global user.name ${USER}
git config --global user.email ${USER}@qwiklabs.net

Cloner l'exemple d'application Cloud Source Repositories

gcloud source repos clone sample-app ${HOME}/sample-app &&
cd ${HOME}/sample-app &&
git checkout main

Sortie

Cloning into '/home/student_03_49720296e995/sample-app'...
remote: Finding sources: 100% (16/16)
remote: Total 16 (delta 0), reused 16 (delta 0)
Receiving objects: 100% (16/16), 47.23 KiB | 681.00 KiB/s, done.
warning: remote HEAD refers to nonexistent ref, unable to checkout.

Project [qwiklabs-gcp-02-4327c4e03d82] repository [sample-app] was cloned to [/home/student_03_49720296e995/sample-app].
Branch 'main' set up to track remote branch 'main' from 'origin'.
Switched to a new branch 'main'

Créer l'exemple d'application

cd ${HOME}/sample-app
./mvnw compile

Sortie

[INFO] Scanning for projects...
...
[INFO] Compiling 1 source file to /home/student_03_49720296e995/sample-app/target/classes
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  10.080 s
[INFO] Finished at: 2022-02-23T17:14:30Z
[INFO] ------------------------------------------------------------------------

Exécuter l'exemple d'application

cd ${HOME}/sample-app
./mvnw exec:java

Sortie

[INFO] Scanning for projects...
...
Listening at http://localhost:8080

Prévisualiser l'application en cours d'exécution

  • Cliquez sur le bouton "Aperçu sur le Web" de Cloud Shell
  • Cliquez sur "Prévisualiser" sur le port 8080.

Lorsque vous avez terminé

  • Appuyez sur Ctrl+C dans Cloud Shell pour arrêter l'application en cours d'exécution.

3. Dockerfile

Conteneuriser l'application avec un Dockerfile

L'une des méthodes d'empaquetage d'une application dans un conteneur consiste à utiliser un Dockerfile. Ce Dockerfile est semblable à un script qui indique au daemon comment assembler l'image de conteneur. Pour en savoir plus, consultez la documentation de référence sur Dockerfile.

Créez un Dockerfile vide dans le dépôt de l'exemple d'application.

touch ${HOME}/sample-app/Dockerfile

Ouvrez le Dockerfile dans l'éditeur de votre choix.

vi ${HOME}/sample-app/Dockerfile

Choisir une image de départ

L'utilisation de la méthode Dockerfile pour créer un conteneur nécessite une connaissance directe de l'application afin d'assembler le conteneur. La première étape de la création d'un Dockerfile consiste à sélectionner une image qui servira de base pour votre image.Cette image doit être une image parente ou de base gérée et publiée par une source fiable, généralement votre entreprise.

L'instruction FROM initialise une nouvelle étape de compilation et définit l'image de base pour les commandes séquentielles suivantes. Ainsi, l'instruction FROM est généralement la première d'un Dockerfile et ne peut être précédée que d'une instruction ARG facultative pour accepter les variables.

Syntaxe : FROM <image>[:<tag> | @<digest>] [AS <name>]

Le format d'une image est <image>:<tag> ou <image>@<digest>. Si aucun tag ou condensé n'est spécifié, le tag :latest est utilisé par défaut. Le format de <image> varie en fonction du registre utilisé pour stocker l'image. Pour Artifact Registry, le format <image> est <region>-docker.pkg.dev/<project ID>/<repository name>/<image name>:<image tag>.

Pour cet atelier, nous utilisons l'image openjdk:11.0-jdk publique. Ajoutez la ligne suivante à votre Dockerfile

FROM openjdk:11.0-jdk

Définir le répertoire de travail

L'instruction WORKDIR définit le répertoire de travail pour toutes les instructions séquentielles qui suivent dans le Dockerfile. Pour en savoir plus, consultez la section WORKDIR de la documentation de référence sur Dockerfile.

Syntaxe : WORKDIR <path>

Pour cet atelier, nous utilisons le répertoire /app comme WORKDIR, et ajoutez la ligne suivante au bas de votre Dockerfile.

WORKDIR /app

Copier les fichiers de l'application

L'instruction COPY copie les répertoires ou les fichiers de l'emplacement <source> vers le chemin d'accès <destination> du système de fichiers d'image. Vous pouvez spécifier plusieurs ressources <source>. Elles sont toutes relatives au contexte de compilation. Le contexte de compilation est abordé plus en détail dans la section "Compiler". Pour en savoir plus, consultez la section COPIE de la documentation de référence sur Dockerfile.

Syntaxe : COPY <source>... <destination>

Pour cet atelier, nous allons copier tous les fichiers du dépôt dans le système de fichiers d'images et ajouter la ligne suivante au bas de votre Dockerfile.

COPY . /app

Compiler l'application

L'instruction RUN exécute les commandes dans une nouvelle couche d'image au-dessus de l'image actuelle et valide les résultats. L'image validée obtenue sera utilisée pour les étapes séquentielles dans le Dockerfile. Pour en savoir plus, consultez la section RUN de la documentation de référence sur Dockerfile.

Syntaxe : RUN <command>

Dans cet atelier, nous allons utiliser Maven pour compiler l'application dans un fichier JAR et ajouter la ligne suivante au bas de votre Dockerfile.

RUN ./mvnw compile assembly:single

Démarrer l'application

L'instruction CMD fournit la commande par défaut pour un conteneur en cours d'exécution. Un Dockerfile ne peut contenir qu'une seule instruction CMD. Si plusieurs instructions CMD sont spécifiées, seule la dernière instruction CMD prend effet. Il existe des fonctionnalités plus avancées à l'aide des instructions CMD et ENTRYPOINT, mais elles ne sont pas abordées dans cet atelier. Pour en savoir plus, consultez la section "CMD" de la documentation de référence sur Dockerfile.

Syntaxe : CMD ["executable","param1","param2"]

Pour cet atelier, nous exécutons le fichier JAR que nous avons compilé. Ajoutez la ligne suivante au bas de votre Dockerfile

CMD ["java","-jar","/app/target/sample-app-1.0.0-jar-with-dependencies.jar"]

Fichier Dockerfile final

Le Dockerfile final sera

FROM openjdk:11.0-jdk
WORKDIR /app
COPY . /app
RUN ./mvnw compile assembly:single
CMD ["java","-jar","/app/target/sample-app-1.0.0-jar-with-dependencies.jar"]

Valider le Dockerfile localement

cd ${HOME}/sample-app
git add Dockerfile
git commit -m "Added Dockerfile"

4. Build

Nous allons maintenant créer l'image à partir du Dockerfile à l'aide de la commande docker build. Cette commande indique au daemon Docker de créer l'image en suivant les instructions de notre Dockerfile. Pour en savoir plus, consultez la documentation de référence sur la compilation Docker.

Créer l'image

cd ${HOME}/sample-app
export IMAGE_TAG=$(git rev-parse --short HEAD)
docker build --tag sample-app:${IMAGE_TAG} .

Sortie

Sending build context to Docker daemon  221.2kB
Step 1/4 : FROM openjdk:11.0-jdk
11.0-jdk: Pulling from library/openjdk
0c6b8ff8c37e: Pull complete
412caad352a3: Pull complete
e6d3e61f7a50: Pull complete
461bb1d8c517: Pull complete
e442ee9d8dd9: Pull complete
542c9fe4a7ba: Pull complete
41de18d1833d: Pull complete
Digest: sha256:d72b1b9e94e07278649d91c635e34737ae8f181c191b771bde6816f9bb4bd08a
Status: Downloaded newer image for openjdk:11.0-jdk
---> 2924126f1829
Step 2/4 : WORKDIR /app
---> Running in ea037abb273d
Removing intermediate container ea037abb273d
---> bd9b6d078082
Step 3/4 : COPY . /app
---> b9aec2b5de51
Step 4/4 : RUN ./mvnw compile jar:jar
---> Running in 3f5ff737b7fd
[INFO] Scanning for projects...
...
[INFO] Building jar: /app/target/sample-app-1.0.0.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  22.952 s
[INFO] Finished at: 2022-02-23T18:09:08Z
[INFO] ------------------------------------------------------------------------
Removing intermediate container 331443caebd3
---> 152f65cc441e
Step 5/5 : CMD ["java", "-jar", "/app/target/sample-app-1.0.0.jar"]
---> Running in 3d595a72231c
Removing intermediate container 3d595a72231c
---> 0e40d7548cab
Successfully built 0e40d7548cab
Successfully tagged sample-app:aaa8895

5. Exécuter

Une fois notre image de conteneur créée, nous pouvons exécuter notre application et nous assurer qu'elle se comporte comme prévu à l'aide de la commande docker run. Cette commande lancera notre conteneur au premier plan de notre invite de commande à des fins de test ou de débogage. Pour en savoir plus, consultez la documentation de référence sur docker run.

Exécuter un conteneur à l'aide de l'image

cd ${HOME}/sample-app
export IMAGE_TAG=$(git rev-parse --short HEAD)
docker run \
  --rm \
  -p 8080:8080 \
  sample-app:${IMAGE_TAG}

Sortie

Listening at http://localhost:8080

Prévisualiser l'application exécutée dans un conteneur

  • Cliquez sur le bouton "Aperçu sur le Web" de Cloud Shell
  • Cliquez sur "Prévisualiser" sur le port 8080.
  • Appuyez sur Ctrl+C dans Cloud Shell pour arrêter les conteneurs.

Modifier le comportement du conteneur

L'exécution de Docker Run utilise la configuration par défaut du Dockerfile. Des instructions et des paramètres supplémentaires peuvent être ajoutés pour modifier ce comportement.

Activer la journalisation TRACE

cd ${HOME}/sample-app
export IMAGE_TAG=$(git rev-parse --short HEAD)
docker run \
  --rm \
  -p 8080:8080 \
  sample-app:${IMAGE_TAG} \
  java -Dorg.slf4j.simpleLogger.defaultLogLevel=trace -jar /app/target/sample-app-1.0.0-jar-with-dependencies.jar

Prévisualiser l'application exécutée dans un conteneur

  • Cliquez sur le bouton "Aperçu sur le Web" de Cloud Shell
  • Cliquez sur "Prévisualiser" sur le port 8080.
  • Passez à l'onglet Cloud Shell et observez les journaux
  • Appuyez sur CTRL+C dans Cloud Shell pour arrêter le conteneur

Modifier le port

cd ${HOME}/sample-app
export IMAGE_TAG=$(git rev-parse --short HEAD)
docker run \
--rm \
-e PORT=8081 \
-p 8081:8081 \
sample-app:${IMAGE_TAG}

Prévisualiser l'application exécutée dans un conteneur

  • Cliquez sur le bouton "Aperçu sur le Web" de Cloud Shell
  • Cliquez sur "Modifier le port".
  • Saisissez 8081
  • Cliquez sur "Modifier et prévisualiser"
  • Appuyez sur CTRL+C dans Cloud Shell pour arrêter le conteneur

6. Push

Une fois que nous sommes sûrs que l'image du conteneur s'exécute correctement et que nous souhaitons que ce conteneur puisse s'exécuter dans d'autres environnements et/ou par d'autres utilisateurs, nous devons transférer l'image vers un dépôt partagé. Cela devrait se produire dans le cadre d'un pipeline de compilation automatisé, mais dans notre environnement de test, nous avons déjà configuré un dépôt et nous pouvons transférer manuellement notre image.

Transférer le commit Dockerfile dans le dépôt sample-app

cd ${HOME}/sample-app
export IMAGE_TAG=$(git rev-parse --short HEAD)
git push

Ajouter un tag à l'image pour Artifact Registry

docker tag sample-app:${IMAGE_TAG} \
    us-central1-docker.pkg.dev/${GOOGLE_CLOUD_PROJECT}/apps/sample-app:${IMAGE_TAG}

Configurer vos identifiants pour Artifact Registry

gcloud auth configure-docker us-central1-docker.pkg.dev

Lorsque vous y êtes invité, Do you want to continue (Y/n)? répondez y et appuyez sur Enter.

Transférer l'image vers Artifact Registry

docker push us-central1-docker.pkg.dev/${GOOGLE_CLOUD_PROJECT}/apps/sample-app:${IMAGE_TAG}

Sortie

 The push refers to repository [us-central1-docker.pkg.dev/qwiklabs-gcp-04-b47ced695a3c/apps/sample-app]
  453b97f86449: Pushed
  e86791aa0382: Pushed
  d404c7ee0850: Pushed
  fe4f44af763d: Pushed
  7c072cee6a29: Pushed
  1e5fdc3d671c: Pushed
  613ab28cf833: Pushed
  bed676ceab7a: Pushed
  6398d5cccd2c: Pushed
  0b0f2f2f5279: Pushed
  aaa8895: digest: sha256:459de00f86f159cc63f98687f7c9563fd65a2eb9bcc71c23dda3351baf13607a size: 2424

7. Félicitations !

Félicitations, vous avez terminé cet atelier de programmation.

Points abordés

  • Créer un Dockerfile pour un exemple d'application
  • Création d'une image
  • exécuté l'image en tant que conteneur localement.
  • Modification du comportement du conteneur
  • Transfert de l'image vers Artifact Registry