L’image Docker peut être considérée comme un ensemble d’instructions sur la façon de créer le conteneur. Une image peut être héritée d’une autre image (ou basée sur elle), en ajoutant des instructions supplémentaires par-dessus les instructions de base. Chaque image est constituée de plusieurs couches, qui sont effectivement immuables.

Choisir une image Docker de base

Souvent nous accordons peu d’attention à la taille d’une image, car nous la téléchargeons une seule fois et l’exécute pour toujours. Alors que la taille des images Docker est en fait très importante et elle a un impact sur :

  • La latence du réseau : nécessité de transférer l’image Docker sur le web
  • Le stockage : il faut stocker tous les binaires quelque part
  • La disponibilité et l’élasticité du service : avec l’utilisation d’un orchestrateur de conteneurs, comme Kubernetes, Swarm (hors production), Nomad ou autre.
  • Sécurité : il existe des librairies que vous ne devez pas utiliser, car elles présentent des vulnérabilités importantes (par exemple : libpng)
  • L’agilité du développement : de petites images Docker = un temps de construction et un déploiement plus rapide. Dans la suite nous construirons une image docker basée sur l’openJDK en présentant différentes méthodes de construction.

Type et variante d’un image docker OpenJDK

Les images OpenJDK existent en plusieurs versions, chacune étant conçue pour un cas d’utilisation spécifique.

  • openjdk:<version>
    C’est l’image de facto. Si vous n’êtes pas sûr de vos besoins, vous devriez probablement utiliser celle-ci. Elle est conçue pour être utilisée à la fois comme un conteneur jetable, ainsi que comme base pour construire d’autres images. Certaines de ces balises peuvent avoir des noms comme « jessie » ou « stretch ». Ce sont les noms des versions de Debian et ils indiquent la version utilisée.
  • openjdk:<version>-alpine
    Cette image est basée sur le populaire Alpine Linux, disponible dans l’image officielle alpine. Alpine Linux est beaucoup plus petite que la plupart des images de base des distributions (~5MB), et conduit donc à des images beaucoup plus fines en général.
  • openjdk:<version>-windowsservercore
    Cette image est basée sur Windows Server Core (microsoft/windowsservercore). En tant que telle, elle ne fonctionne que dans les endroits où cette image fonctionne, comme Windows 10 Professionnel/Entreprise (Anniversar Edition) ou Windows Server 2016.
  • openjdk:<version>-slim
    Cette image installe le paquetage -headless d’OpenJDK et il lui manque donc de nombreuses bibliothèques Java liées à l’interface utilisateur et certains paquets courants contenus dans la balise par défaut. Elle ne contient que les paquets minimaux nécessaires à l’exécution de Java.

Construire une image Docker pour une application Java basée sur l’OpenJDK

Nous passons en revue 3 façons différentes de créer des images Docker pour une application Java.

Première méthode : Construction de package uniquement 

Dans le cas d’une construction par package, nous déléguons à un outil construction (Maven ou Gradle par exemple) le contrôle du processus de construction.

  1. Nous ajoutons un Dockerfile à la racine du projet Java, comme un manuel de construction de l’image Docker,

    22-image-article-jdk
    source : magazine programmez hors série 4

     

  2. Maintenant, nous construisons l’application dans un .jarmvn clean package
  3. Et ensuite construire l’image Docker
    docker build -t
    creative-tech/docker-package-only-build-demo:0.0.1-SNAPSHOT ./
  4. Pour exécuter le conteneur à partir de l’image construite :
    docker run -d -p 8080:8080
    creative-tech/docker-package-only-build-demo:0.0.1-SNAPSHOT
  5.  Naviguez vers http://localhost:8080, et vous devriez voir ce qui suit :

    23-image-article-jdk
    source : magazine programmez hors série 4

     

Les avantages de cette approche :

  • Elle permet d’obtenir une image Docker légère.
  • Ne nécessite pas que Maven soit inclus dans l’image Docker.
  • Il n’est pas nécessaire d’inclure les dépendances de notre application dans l’image.

Inconvénients de cette approche :

  • Il faut que Maven et JDK soient installés sur la machine hôte.
  • La construction Docker échouera si la construction Maven échoue ou n’est pas exécutée au préalable. Cela devient un problème lorsqu’on veut intégrer des services qui construisent (seulement) automatiquement en utilisant le présent fichier Docker.

Deuxième méthode : Construction par Docker

Dans cette construction, Docker contrôle le processus de construction.

24-image-article-jdk
source : magazine programmez hors série 4

 

  1. Maintenant, nous construirons une nouvelle image comme dans l’étape 1 :
    docker build -t creative-tech/docker-maven-build-demo:0.0.1-SNAPSHOT ./
  2. Et exécutons le conteneur :

docker run -d -p 8080:8080 creative-tech/docker-maven-build-demo: 0.0.1-SNAPSHOT

Les avantages de cette approche :

  • Docker contrôle le processus de construction, cette méthode ne nécessite donc pas l’installation préalable de l’outil de construction ou du JDK sur la machine hôte. 
  • S’intègre bien aux services qui se contentent de construire (seulement) automatiquement en utilisant le fichier Docker présent.

Inconvénients de cette approche :

  • Donne lieu à la plus grande image Docker des trois méthodes.
  • Cette méthode de construction a non seulement packager l’application, mais aussi toutes ses dépendances et l’outil de construction lui-même, qui n’est pas nécessaires pour exécuter le JAR.
  • Si la couche applicative est reconstruite, la commande mvn package forcera toutes les dépendances Maven à être tirées du dépôt distant à nouveau (on perd le cache Maven local). 
  • Si utilisation de repo privés (Nexus ou Frog), il est important de rajouter une configuration settings.xml afin de configurer les accès

Troisième méthode : Construction en plusieurs étapes

Avec les constructions Docker à plusieurs étapes, on utilise plusieurs instructions pour chaque étape de construction. Chaque instruction crée une nouvelle couche de base et élimine tout ce dont nous n’avons pas besoin dans l’étape précédente.

25-image-article-jdk
source : magazine programmez hors série 4

 

  1. Construire l’image :

docker build -t ceative-tech/docker-multi-stage-demo:1.0-SNAPSHOT ./

  1.  Et ensuite, exécuter le conteneur :

docker run -d -p 8080:8080 ceative-tech/docker-multi-stage-demo:1.0- SNAPSHOT


Les avantages de cette approche :

  • Elle permet d’obtenir une image Docker de petite taille.
  • Ne nécessite pas l’installation préalable de l’outil de construction ou du JDK sur la machine hôte (Docker contrôle le processus de construction).
  • S’intègre bien aux services qui se contentent de construire (seulement) automatiquement en utilisant le présent fichier Docker.
  • Seuls les artefacts dont nous avons besoin sont copiés d’une étape à l’autre (c’est-à-dire que les dépendances de l’application ne sont pas intégrées à l’image finale comme dans la méthode précédente).
  • Nous créons autant d’étapes de construction que nous le souhaitons
  • Il est possible de s’arrêter à n’importe quelle étape de la construction d’une image en utilisant -target, par exemple
    docker build – target MAVEN_BUILD -t ceative-tech/docker-multi-stage-
    demo:1.0-SNAPSHOT ./
Figure 7
source : magazine programmez hors série 4

 

Inconvénients de cette approche :

  • Si la couche applicative est reconstruite, la commande mvn package forcera toutes les dépendances Maven à être tirées du dépôt distant une nouvelle fois (perte de l’avantage du cache Maven local). 

Pour vérifier la taille des images construites
docker image ls
Figure 7

Parmi les trois méthodes de construction d’images Docker décrites dans cet article, la construction en plusieurs étapes est la plus appropriée. Nous obtenons le meilleur des deux mondes lors du package du code de l’application. Docker contrôle la construction, mais il est possible d’extraire uniquement les artefacts nécessaires. Cela devient particulièrement important lorsque nous stockons des conteneurs sur le cloud :

  • Vous passerez ainsi moins de temps à créer et à transférer vos conteneurs sur le cloud, car les images sont beaucoup plus petites.
  • Pour le coût, plus l’image est petite, moins le stockage est coûteux.
  • Pour la sécurité, le vecteur d’attaque est plus petit, car la suppression des dépendances supplémentaires de l’image la rend moins vulnérable aux attaques.

Dans cette série d’articles nous avons présenté les différents fournisseurs de l’OpenJDK comme alternative de l’OracleJDK et des éléments clefs pour décider et justifier le choix d’une distribution par rapport à une autre. Nous avons abordé le sujet de l’optimisation de la construction des images de base docker pour les applications JAVA de point de vue taille de l’image et de distribution JDK sachant qu’il y a d’autres aspects à optimiser aussi comme la sécurité et limitation des vecteurs d’attaque et de spectre des vulnérabilités, l’optimisation de la couche réseau …