Images

Support de présentation : https://soleil-docker.jrobert-orleans.fr/02-Images-Dockerfiles

Gestion des images

Image docker – Présentation

Les points qui vous ont été présentés :

  • Une image = fichiers + meta-données. Standard « OCI » (open container initiative).

  • Un conteneur = instanciation d’une image avec montage en lecture seule de l’image + une couche de persistance.

  • Une image est constituée de couches.

  • Dockerfile : fichier permettant de construire chaque couche d’une image.

Image docker - manipulations

Pour gérer les images la commande à utiliser sera docker image :

  • Lister les images présentes sur la machine : docker image ls (avec la version raccourcie : docker images )

  • Récupérer une image : docker image pull (avec la version raccourcie : docker pull )

  • Supprimer une image : docker image rm (avec la version raccourcie : docker rmi )

  • Obtenir des informations sur une image : docker image history et docker image inspect (avec les versions raccourcies : docker history et docker inspect)

Appliquez ces commandes pour :

  • Déterminer le nombre d’images que vous avez sur votre machine.

  • Déterminer la taille de l’image alpine:latest.

  • Déterminer le nombre de couches de l’image nginx:latest.

  • Supprimer les images que vous avez inutilement téléchargées.

Image docker - nettoyage

  • Listez les images inutilisées : docker image ls -f dangling=true

  • Nettoyez les images : docker image prune

  • Vérifiez l’espace disque récupéré : docker system df

Création d’images

Avant de commencer

Dans la suite, nous allons utiliser le moteur de création d’images « buildkit », qui est plus intéressant (performances, fonctionnalités) que celui « par défaut » de docker.

Pour cela, vous devez exécuter :

docker buildx install

Note: avec les versions de docker récentes, il n’est plus nécessaire de faire docker buildx install.

Dockerfile

Créez un dossier « premier_dockerfile ».

Dans ce dossier écrivez un fichier hello.sh avec le contenu suivant :

#!/bin/sh
echo Bonjour tout le monde

Créez un second fichier, nommé Dockerfile avec le contenu suivant :

# syntax=docker/dockerfile:1
# L'instruction suivante indique qu'on souhaite se baser sur l'image ubuntu avec le tag 24.04
FROM ubuntu:24.04
# L'instruction suivante permet de copier hello.sh dans le dossier /bin/ de l'image en lui donnant le droit 755
COPY --chmod=755 hello.sh /bin/

Créez une image en exécutant :

docker build -t "ma_premiere_image:0.1" .

Remarquez que si vous lancez de nouveau cette commande, son exécution est bien plus rapide.

Lancez un conteneur à partir de cette image:

docker run -it ma_premiere_image:0.1 bash

Constatez que vous pouvez y lancer « hello.sh »

Layers

Exécutez la commande suivante :

docker image history ma_premiere_image:0.1
  • Dans quel ordre les couches sont elles indiquées ?

  • À quoi correspond la colonne SIZE ?

  • Remarquez la colonne « CREATED ». Relancez le build et observez de nouveau cette colonne : les temps n’ont pas changé, le nouveau build n’a en fait rien fait d’autre que de constater qu’il n’avait rien à faire.

  • Essayez de nouveau, mais cette fois ci en ajoutant l’option --no-cache à docker buid. Constatez que cette fois les images ont bien été recréées.

Essais

Créez un fichier README.txt avec quelques lignes. Ajoutez une instruction à la fin de votre Dockerfile pour que ce fichier soit copié dans /opt/.

Lancez le build avec ce nouveau Dockerfile. Constatez avec docker image history que seul le cache a été utilisé pour les anciennes couches.

Ajoutez l’instruction suivante à la fin de votre Dockerfile :

RUN <<EOF
    apt update
    apt install --no-install-recommends -y vim-tiny
    rm -rf /var/lib/apt/lists/*
EOF
  • Observez la taille des couches avec docker history.

  • Constatez que si vous lancez à nouveau le build, docker utilise son cache et que tout est exécuté très rapidement.

Les instructions Dockerfile

Jusqu’ici nous avons utilisé trois instructions différentes : FROM, COPY et RUN :

  • FROM permet de dire de quelle image on part. La bonne pratique voudra qu’on spécifie une version bien précise (ubuntu:22.04 plutôt que ubuntu ou que ubuntu:latest)

  • COPY permet de copier un fichier ou dossier dans le système de fichiers de l’image. L’option --chmod permet de préciser les droits que l’on donne aux fichiers copiés

  • RUN permet d’exécuter une commande et si celle ci impacte les fichiers, les modifications feront partie de l’image.

Il y a en tout une quinzaine d’instructions, documentées ici : https://docs.docker.com/engine/reference/builder/ et avec une dizaine on est déjà très bien. Plus que quelques unes à connaître !

ENTRYPOINT et CMD

Dans la suite, nous nous assurerons systématiquement que notre conteneur contient les deux instructions suivantes :

ENTRYPOINT ["commande", "argument1", "argument2"]
CMD ["argument_supplémentaire_par_défaut1", "argument_supplémentaire_par_défaut2"]

Lorsqu’on lance un conteneur en faisant docker run -it NOM_IMAGE, le processus qui sera lancé à l’intérieur du conteneur sera : commande argument1 argument2 argument_supplémentaire_par_défaut1 argument_supplémentaire_par_défaut2.

Lorsqu’on lance un conteneur en faisant docker run -it NOM_IMAGE argument_supp1 argumentsupp2, le processus qui sera lancé à l’intérieur du conteneur sera : commande argument1 argument2 argumentsupp1 argumentsupp2.

Par exemple, supposons qu’on ait une image essai_entrypoint construite à partir du Dockerfile suivant :

# syntax=docker/dockerfile:1
FROM alpine:3.16
ENTRYPOINT ["/bin/echo", "Bonjour"]
CMD ["tout","le","monde!"]
  • Sans essayer : quelle serait la sortie de la commande docker run essai_entrypoint ?

  • Sans essayer : quelle serait la sortie de la commande docker run essai_entrypoint Hello ?

  • Ecrivez un dockerfile ayant pour image de base alpine:3.16, avec comme ENTRYPOINT ["/bin/ls"] et comme CMD ["-lah"]. Que cela fait-il ?

  • Ecrivez un dockerfile ayant pour image de base alpine:3.16, avec comme ENTRYPOINT ["/bin/sh","-c"] et comme CMD ["sh"]. Que cela fait-il ?

On peut ne pas spécifier ENTRYPOINT ou CMD, mais le fonctionnement n’est alors pas celui décrit ci-dessus. Dans la suite nous nous assurerons de systématiquement les renseigner.

principales instuctions pour Dockerfile

Commandes

Utilisation

FROM

IMAGE

LABEL

Ajouter des métadonnées

RUN

Lancer une commande

ADD [–chown=<user>:<group>] <src> <dest>

Ajout au conteneur

COPY [–chown=<user>:<group>] <url> <dest>

Ajout au conteneur et aussi COPY --from=<image> <src><dest>

WORKDIR

Répertoire de travail

EXPOSE

Port d’écoute

ENTRYPOINT / CMD

Commande à lancer au démarrage du container

ENV

Définition de variables d’environnemment

USER

Nom d’utilisateur à utiliser

ARG

Passage arguments ex: docker build –build-arg machin=bidule

Exemple plus complet

Ecrivons une image un peu plus complète pour finir avec une petite app nodejs dans un conteneur.

Créer un répertoire de base node-serv-fic.

  • Fabriquer le fichier lireFic.js et le placer à la racine :

let http=require('http'),fs=require('fs');
http.createServer(function (req, res) {
// Ouvre et lit servHello.js
fs.readFile('hello.txt','utf8',function(err, data) {
    res.writeHead(200,{'Content-Type': 'text/plain'});
    if (err)
        res.write('Pb ouverture fichier\n');
    else// On renvoie le fichier au client
        res.write(data);
    res.end();
    });
}).listen(8200, function() {console.log('Connecte au port 8200');});
console.log('Serveur tourne sur port 8200');
  • On utilise un fichier package.json pour les dépendances :

{
    "name": "nodejs-simple",
    "version": "1.0.0",
    "description": "Envoi fichier par serveur node",
    "main": "lireFic.js",
    "keywords": [
        "node",
        "serveur",
        "fichier"
    ],
    "author": "Super Geek",
    "license": "ISC",
    "dependencies": {
        "http": "^0.0.1-security"
    }
}
  • un fichier hello.txt pour publication (contenu à votre guise !)

  • Enfin le Dockerfile :

FROM node:23.11-alpine
WORKDIR /app
COPY package.json .
RUN npm install
COPY lireFic.js .
COPY hello.txt .
EXPOSE 8200
ENTRYPOINT ["node", "lireFic.js"]
CMD []

On build et on lance :

  • docker build -t node-serv-fic .

  • docker run -d --name node-serv -p 8000:8200 node-serv-fic

  • on visite http://localhost:8000

Exemple avec votre llm favori

En partant d’une page blanche, demandez à chatGPT ou autre de :

  • Créer une application python qui sert le contenu d’un fichier sur le port 3004

  • Créer un Dockerfile pour faire tourner cette application

  • Donner une commande pour faire tourner cette application sur votre machine de façon à ce qu’elle soit accessible sur le port 8000

Avant de lancer, discutez et validez (si vous avez des doutes, ne lancez rien !). Discutez les éventuelles erreurs / approximations du llm.

Quiz

--- primary_color: orange secondary_color: lightgray text_color: black shuffle_questions: false --- ## OCI, acronyme de "Open Container Initiative" est une structure qui - [x] fournit un standard pour décrire les images de conteneurs - [ ] est à l'initiative de Docker ## Une image de conteneur - [x] est composée de plusieurs couches - [x] contient des métadonnées permettant par exemple de savoir quelle commande exécuter par défaut - [ ] représente généralement une baleine sur fond bleu - [x] peut être construite avec docker build - [ ] est modifiable au travers de l'exécution d'un conteneur ## L'ordre des commandes exécutées dans un Dockerfile - [ ] n'a pas d'importance. - [x] influe sur la strucutre des couches de l'image crée. - [ ] influe sur le temps du premier "build". - [x] influe sur le temps des build grace à un système de cache. ## Une commande RUN dans un Dockerfile - [ ] est exécutée systématiquement à chaque build. - [x] n'est exécutée que si elle ne l'avait pas été au cours du build précédent. ## L'instruction ENTRYPOINT - [x] Définit la commande qui sera exécuté à l'intérieur du conteneur. - [ ] Définit les arguments par défaut qui seront passés à au processus du conteneur. ## L'instruction CMD - [ ] Définit la commande qui sera exécuté à l'intérieur du conteneur. - [x] Définit les arguments par défaut qui seront passés à au processus du conteneur. ## La taille d'une image - [ ] n'a pas vraiment d'importance. - [ ] influe sur le temps de lancement d'un conteneur, même si l'image est déjà téléchargée. - [x] influe sur le temps de premier lancement d'un conteneur car il faut la télécharger. - [x] est la somme des tailles de chaque couche de l'image. - [ ] fait au minimum 50Mo - [x] peut être minimisée en travaillant sur le Dockerfile