Docker - Creation d'une image

Docker Logo

Création d’une image personnalisée

La création d’image s’appelle un build. Il s’effectue à l’aide de la commande docker build.

Il faut passer en paramètre à cette commande un dossier contenant :

  • au moins un Dockerfile.
  • les autres fichiers éventuellement nécessaires au Dockerfile.

Ce dossier peut être directement à la racine d’un repo git, ou via un tar du dossier dans l’entrée standard (avec le Dockerfile à la racine). Le client docker va alors transférer l’intégralité de ce dossier au serveur docker, qui va effectuer toutes les étapes du build. L’option “-t de docker build permet de mettre un tag à l’image qui sera créée. Il est aussi possible de faire ça manuellement ensuite avec docker tag. Une fois l’image créée, il est préférable de la pousser sur un registry à l’aide de docker push pour pouvoir plus facilement la versionner et réutiliser.

Qu’est-ce qu’un fichier DockerFile

L’élément le plus important d’un build est le fichier Dockerfile, c’est lui qui définit toutes les étapes de création d’une image.

Il s’agit d’une suite de ligne de type :

INSTRUCTION arguments

Les instructions disponibles sont :

  • FROM
  • RUN
  • ENV
  • LABEL
  • ADD
  • COPY
  • ENTRYPOINT
  • CMD
  • HEALTHCHECK
  • EXPOSE
  • WORKDIR
  • VOLUME
  • USER
  • ONBUILD
  • STOPSIGNAL
  • SHELL

FROM

L’instruction FROM doit être la première du Dockerfile. Elle définit à partir de quelle image existante on construit notre image.

FROM debian:latest

ENV

ENV permet de définir une variable d’environnement. Ces variables sont visibles :

  • Dans les étapes suivantes du Dockerfile.
  • Dans les images construites à partir de cette image,
  • Lors du lancement d’un container à partir de cette image

    ENV var1 a
    ENV var1=a var2=b var3="c d"

LABEL

Les labels fonctionnent comme ENV pour la déclaration et propagation, mais ne servent qu’à donner des indications sur l’image.

LABEL version 1
LABEL author=FredGrnd email=fredgr@pm.me

RUN

RUN permet d’exécuter une commande pour créer une nouvelle image à partir de l’image précédente sur laquelle on exécute cette commande. Chaque instruction RUN ajoute une nouvelle couche à l’image finale, il faut donc essayer de limiter leur nombre.

FROM debian:latest
RUN apt-get update
RUN apt-get install -y apache

Ici on aura 3 couches :

  • Debian
  • le cache apt
  • le package apache téléchargé dans le cache apt + apache installé

On pourrait tenter de réduire la taille en faisant un apt-get clean :

FROM debian:latest
RUN apt-get update
RUN apt-get install -y apache
RUN apt-get clean

On a maintenant 4 couches :

  • Debian
  • le cache apt
  • le package apache téléchargé dans le cache apt + apache installé
  • l’indication de purger le cache apt dont le package .deb téléchargé (une couche qui masque des fichiers de l’image précédente)

La bonne façon de faire ça et de grouper les commandes dans un seul RUN :

FROM debian:latest
RUN apt-get update \
 && apt-get install -y apache \
 && apt-get clean

Il ne reste que deux couches avec juste le minimum nécessaire :

  • Debian
  • le package apache installé

Les couches étant réutilisables, il ne faut pas chercher non plus à tout mettre dans la même : ajouter un git clone de la dernière version d’une application dans le même RUN va forcer la réinstallation d’apache à chaque fois, et faire une multitude d’images apache+application alors qu’il aurait pu n’y avoir que plein de petites couches pour les versions de l’application.

ADD

ADD permet d’ajouter des fichiers dans l’image. Avec ADD, la source de la copie peut aussi être une URL ou une archive tar qui sera décompresser.

ADD /entrypoint.sh /entrypoint.sh

COPY

COPY permet d’ajouter des fichiers comme ADD, mais uniquement à partir du dossier du Dockerfile

ENTRYPOINT et CMD

ENTRYPOINT définit la commande qui sera lancée par défaut lors d’un run de l’image. CMD définit ce qui sera immédiatement après.

Dans un docker run, ce qui est après le nom de l’image remplace uniquement le CMD

ENTRYPOINT /etc/init.d/apache
CMD start

Ici par défaut le docker run va lancer /etc/init.d/apache start

Si on lance le container ainsi :

docker run mon_container configtest

Alors la commande lancée sera :

/etc/init.d/apache configtest

Attention La commande lancée via ENTRYPOINT et CMD ne doit pas lancer un process en arrière plan et rendre la main : si ce premier process meurt, le container est stoppé. Il faut donc toujours lancer la commande en foreground (souvent grace aux options foreground ou debug des outils). Si jamais il est nécessaire d’avoir plusieurs outils dans le même container, il faut utiliser un wrapper comme runit ou supervisord pour jouer le rôle que joue habituellement init sur un système classique.

HEALTHCHECK

HEALTHCHECK permet de définir une commande qui permet de valider qu’un container est fonctionnel. Par exemple une requête HTTP sur l’application pour vérifier qu’elle est correctement démarrée.

HEALTHCHECK --interval=5m --timeout=3s \
  CMD curl -f http://localhost/ || exit 1

Ce test sera effectué par docker avec l’intervalle indiqué pour vérifier que le service est fonctionnel, lors du démarrage du container, et pour détecter qu’il ne marche plus et le redémarrer quand le run a été fait avec –restart always.

EXPOSE

EXPOSE indique à Docker sur quels ports écoute un container. Docker ne fera pas de bind automatique des ports exposés, sauf si le docker run est fait avec l’option “-P” qui va binder pour chaque port exposé du container un port haut aléatoire de l’hôte.

EXPOSE 80 443

VOLUME

VOLUME indique qu’un dossier de l’image est un point de montage. Lors du docker run de l’image, pour chaque volume un dossier de stockage éphémère sera créé, et le contenu de ce dossier dans l’image sera copié dans le dossier éphémère. Il est conseillé de monter des volumes persistants dans ces dossiers avec docker run -v pour faire persister ces données.

Remarque Si un build est fait à partir d’une image qui a des volumes, le contenu éventuellement modifié dans ces volumes sera perdu.

USER

USER permet de spécifier un nom d’utilisateur ou un uid qui sera utilisé pour toutes les :

  • RUN suivants du Dockerfile
  • ENTRYPOINT et CMD au démarrage des containers utilisant cette image

    USER www-data

Le username est converti en UID via le /etc/passwd de l’image et non de l’hôte.

[Atelier] Création d’un conteneur personnalisé apache

Créer une image docker avec :

  • OS : Fedora
  • Installation d’un serveur web apache
  • Mettre une page statique dans le site
  • Déclarer le dossier du site comme étant un montage
  • Lancement du serveur web au run de l’image
  • Ajout d’un healthcheck qui test le site

Création de l’arborescence.

mkdir -p docker_fedora_httpd/volumes/html
mkdir -p docker_fedora_httpd/files
echo "Hello Word (volume)" > docker_fedora_httpd/volumes/html/index.html
echo "Hello Word" > docker_fedora_httpd/files/index.html

Création du Dockerfile

FROM fedora:latest
LABEL Version 1

RUN yum -y update && yum -y install httpd && yum clean all
ADD /files/index.html /var/www/html/index.html

VOLUME /var/www/html

ENTRYPOINT /usr/sbin/httpd -X

EXPOSE 80 443

HEALTHCHECK --interval=5m --timeout=3s CMD curl -f http://localhost/ || exit 1%

Construire une image a partir du Dockerfile

docker build -t docker_fedora_httpd .

Vérifier que l’image est bien présente dans le repository local

docker images
REPOSITORY            TAG                 IMAGE ID            CREATED             SIZE
docker_fedora_httpd   latest              e365a8fa7e86        2 minutes ago       387MB

Démarrer l’image sans le point de montage

docker run -d --name docker_fedora_httpd -p 8080:80 docker_fedora_httpd

Tester le site.

curl -i localhost:8080/index.html
HTTP/1.1 200 OK
Date: Wed, 29 Nov 2017 08:22:12 GMT
Server: Apache/2.4.29 (Fedora)
Last-Modified: Thu, 16 Nov 2017 06:10:29 GMT
ETag: "86-55e137a12df40"
Accept-Ranges: bytes
Content-Length: 134
Content-Type: text/html; charset=UTF-8

Hello World

Modifier la page d’accueil du site directement dans le conteneur

docker exec -it docker_fedora_httpd bash
[root@65a18fefa034 /]# vim
# vi /var/www/html/index.html

Tester de nouveau le site.

curl -i http://localhost:8080
HTTP/1.1 200 OK
Date: Wed, 29 Nov 2017 08:26:32 GMT
Server: Apache/2.4.29 (Fedora)
Last-Modified: Wed, 29 Nov 2017 08:26:04 GMT
ETag: "17-55f1ae2ee2b18"
Accept-Ranges: bytes
Content-Length: 23
Content-Type: text/html; charset=UTF-8

Hello World (Modified)

Stopper, supprimer et relancer le conteneur.

docker stop docker_fedora_httpd
docker rm docker_fedora_httpd
docker run -d -p 8080:80 docker_fedora_httpd

Tester le site.

curl -i localhost:8080/index.html
HTTP/1.1 200 OK
Date: Wed, 29 Nov 2017 08:22:12 GMT
Server: Apache/2.4.29 (Fedora)
Last-Modified: Thu, 16 Nov 2017 06:10:29 GMT
ETag: "86-55e137a12df40"
Accept-Ranges: bytes
Content-Length: 134
Content-Type: text/html; charset=UTF-8

Hello World

Le site est revenu à l’initial, les données modifiées ne sont pas persistantes.

Stopper et nettoyer le conteneur

docker stop docker_fedora_httpd
docker rm docker_fedora_httpd

Lancer le conteneur en montant le répertoire volumes/html/

docker run -d --name docker_fedora_httpd -p 8080:80 -v ~/docker_fedora_httpd/volumes/html:/var/www/html docker_fedora_httpd

Test de nouveau le site

curl -i http://localhost:8080
HTTP/1.1 200 OK
Date: Wed, 29 Nov 2017 09:16:03 GMT
Server: Apache/2.4.29 (Fedora)
Last-Modified: Wed, 29 Nov 2017 09:15:49 GMT
ETag: "15-55f1b94d07b40"
Accept-Ranges: bytes
Content-Length: 21
Content-Type: text/html; charset=UTF-8

Hello World (volume)

Stopper et nettoyer le conteneur

docker stop docker_fedora_httpd
docker rm docker_fedora_httpd

Cet article fait partie d’une série sur Docker

comments powered by Disqus