Docker 101: guide de démarrage rapide

Devpulsion
7 min readFeb 15, 2021

--

Cet articles est une synthèse des bases pour commencer avec Docker. Pas de longue théorie, just les indispensables pour débuter rapidement.

Après la lecture de cette article, vous serez en mesure de :

  • 📚 comprendre les principaux concepts de Docker et le vocabulaire indispensable;
  • 🚢 utiliser une image Docker et gérer son lifecycle;
  • ✍🏻 écrire votre propre image sur mesure et l’optimiser en multistage.

📚 Concepts

Qu’est ce que Docker?

Docker est une application publiée en 2013 pour gérer des conteneurs. Il en existe d’autre, mais à ce jour c’est probablement la plus connue et utilisée. La conteneurisation est une évolution radicale de ce que permettent les machines virtuelles.

Les machines virtuelles tout comme Docker permettent de faire fonctionner une application dans un environnement maitrisé et cloisonné sur le système d’exploitation de votre ordinateur ou d’un serveur (nommé host).

Rappel sur l’architecture des machines virtuelles (VM)

Pour bien comprendre, petit rappel sur le principe des machines virtuelles. Elles permettent d’installer un système d’exploitation complet sur votre machine, et dans cet OS vous embarquez votre application.

Exemple: sur votre machine Windows, vous lancez un serveur Debian embarquant un serveur web.

Le principal intérêt, c’est que votre application (le serveur web*) fonctionnera dans un environnement totalement connu et maitrisé (Debian*). La machine virtuelle est quand a elle gérée par un hyperviseur qui fait le lien avec votre machine (Windows*).
(*) font référence à l’exemple précité

Schéma de 2 machines virtuelles embarquant respectivement 2 applications à gauche et 1 application à droite.

L’architecture Docker

A contrario, Docker est une application sur votre machine qui permet de spawner des conteneurs. Ces conteneurs embarquent uniquement les librairies et outils nécessaires au bon fonctionnement de votre application et ne nécessitent pas d’OS propres car ils ont directement accès aux ressources de votre machine (host).

En comparant les schéma d’architecture, on comprend immédiatement que les conteneurs sont donc :

  • bien plus légers en terme d’espace disque nécessaire cat ils n’embarquent que le minimum requis ;
  • beaucoup plus rapides à démarrer ou s’arrêter car il n’y a pas d’OS ;
  • bien plus proche des ressources de votre machine hôte et donc plus réactifs;

Néanmoins, l’accès direct aux ressources de votre machine ou d’un serveur, nécessite que ce soit sur un linux ou un OS compatible.

💥 Ce qu’il faut retenir 💥

A partir d’images, Docker créé des conteneurs dont il assure le cycle de vie au sein d’un espace privé. Ces images peuvent être récupérées depuis un registry ou créées par vos soins pour répondre à vos besoins spécifiques.

Et les mots clés importants suivants:

  • Host: la machine qui héberge Docker et partage ses ressources avec les conteneurs. C’est votre ordinateur ou un serveur par exemple.
  • Image: une image est un fichier qui décrit les librairies et outils nécessaires à votre application. Il est habituellement nommé Dockerfile.
  • Conteneur: c’est une image qui a été construite, c’est à dire que tout le nécessaire a été installé.
  • Cycle de vie: un conteneur une fois buildé peut aussi bien être a l’arrêt, démarré, mais aussi dans un état de transition (démarrage, redémarrage, …) ou instable (crash)
  • Registry: c’est un serveur hébergeant et permettant l’accès à des des conteneurs Docker prêt à l’emploi. Il peut être public ou privé.

🚢 Les commandes indispensables

A présent qu’on a la théorie, passons à la pratique. Pour commencer, il vous faudra installer docker. L’installation dépendra de votre OS: https://docs.docker.com/get-docker/

Pour l’exemple nous utiliserons l’image docker hello-world, mais vous pouvez essayer directement avec d’autres images.

Démarrer une conteneur docker

C’est la commande dont les options sont les plus utiles à comprendre. Dans tous les exemples suivant, j’utilise l’image hello-world comme d’exemple.

# Démarrer le conteneur de façon interactive (au Ctrl-C, stopper le conteneur)
> docker run -it hello-world
# Démarrer le conteneur et lui donner un nom pour le retrouver facilement
> docker run -it --name myimage hello-world
# Démarrer le conteneur et le laisser tourner en tache de fond (daemon)
> docker run -it hello-world -d

Rapidement, vous utiliserez des conteneurs exposant des ports permettant de communiquer avec l’application. D’autres conteneurs permettront de paramétrer les applications via des variables d’environnement

# Démarrer un conteneur qui expose le port 3000 et le rendre accessible sur votre machine sur le port 8080 
docker run -p8080:3000 hello-world
# Démarrer un conteneur en partageant un volume local
> docker run -v $(pwd):/backup hello-world
# Démarrer un conteneur en lui passant une variable d'environnement
> docker run -e myCustomEnvVarName=someValueINeed hello-world

Pour connaitre les ports, volumes et variables d’environnement disponibles, il vous faudra vous référer au cas par cas aux explications fournies avec chaque image (ou lire directement les fichiers images si la doc est manquante…).

De très nombreuses autres options existent, notamment pour les volumes à monter (droits et privilèges par exemple). Référez vous à la documentation le moment venu :)

Gérer ses conteneurs

Une fois lancés, vos conteneurs docker peuvent tourner en fond (-d deamon) et il vous faudra pouvoir les lister, stopper, supprimer, relancer… Ci dessous la liste des commandes de base les plus courantes:

# lister les conteneurs existants
> docker ps -a
# arrêter un conteneur
> docker stop ID_DU_CONTENEUR
# redémarrer un conteneur arrêté
> docker start ID_DU_CONTENEUR
# supprimer un conteneur
docker rm ID_DU_CONTENEUR

Nettoyer les instances Docker

Docker peut rapidement occuper beaucoup de votre espace disque et il est bon de savoir faire le nettoyage :)

# Nettoyer les images, conteneurs, volumes et réseaux qui sont en suspens (non associées à un conteneur) :
> docker system prune
# Supprimer en plus tous les conteneurs arrêtés et toutes les images non utilisées
> docker system prune -a
# supprimer les images inutilisées
> docker images purge
# supprimer les conteneurs quittés
> docker rm $(docker ps -a -f status=exited -q)

Pour aller plus loin

De très nombreuses autres commandes existent: pour gérer les networks (pour faire communiquer vos conteneurs avec votre host ou d’autres conteneurs), pour récolter des métrics, ou même pour les commandes présentées (filtrer les résultats, rechercher des conteneurs en fonction d’images, …). Reportez vous à la documentation !

✍🏻 Créer son image docker sur mesure

Bien que les registry puissent proposer un grand nombre d’images, elles restent très génériques. Si elles sont bien pensées, utiliser des volumes, ports ou les configurer via les variables d’environnement apporte une première flexibilité. Pour aller plus loin vous pouvez, à partir de chacune d’elles, rajouter des phases de build propres à votre projet.

Prenons comme exemple un serveur d’api en node. Pour fonctionner il nécessitera:

  • Un environnement supportant node 10
  • Le code source de notre application (contenu dans le même répertoire que le Dockerfile)
  • L’installation de ses dépendances (node_modules)
  • Une phase de build

Une image basique

# Image docker de base: alpine avec node. D'autres sont dispo, à vous de trouver celle qui vous convient le mieux
FROM mhart/alpine-node:12.13.1
# Créé un répertoire de travail sur l'image où nous mettrons le code source de l'application
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
# Installation de dépendances dans notre répertoire de travail
COPY package.json yarn.lock ./
RUN set -ex; \
yarn install --no-cache --frozen-lockfile --production;
# Copy les sources de notre app (ici, on copie tout, à vous d'ajuster car bien souvent, seuls quelques répertoires sont vraiment utiles (src, assets, public...)
COPY . .
# Build l'app avec le script npm `production-build`, à remplacer par le votre
RUN set -ex; \
yarn run production-build;
# Démarrage par défaut, qui pourra être différent à l'utilisation de votre image
EXPOSE 3000
CMD ["yarn", "start"]

Une image en utilisant les variables d’env pour un build conditionnel

Exemple pratique qui utilise la variable d’environnement NODE_ENV pour savoir si on installe les dépendances npm de développement ou pas.

FROM mhart/alpine-node:12.13.1RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
COPY package.json yarn.lock ./
ARG NODE_ENV
ENV NODE_ENV $NODE_ENV
RUN set -ex; \
if [ "$NODE_ENV" = "production" ]; then \
yarn install --no-cache --frozen-lockfile --production; \
else \
yarn install --no-cache --development; \
fi;
COPY . .RUN set -ex; \
if [ "$NODE_ENV" = "development" ]; then \
echo "Skip production build"; \
else \
echo "Production build"; \
yarn run production-build; \
fi;
EXPOSE 3000
CMD ["yarn", "start"]

Une image optimisée (multistage)

Exemple en multi stage. L’image est buildée en plusieurs étapes avec des builds intermédiaires. Si aucun changement n’a eu lieu lors d’une étape, Docker utilisera le build intermédiaire mis en cache, vous évitant par exemple de retélécharger tous les paquets npm.

# base
FROM mhart/alpine-node:12.13.1 AS base
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
COPY package.json yarn.lock ./
# dependancies
FROM base AS dependancies
ARG NODE_ENV
ENV NODE_ENV $NODE_ENV
RUN set -ex; \
if [ "$NODE_ENV" = "production" ]; then \
yarn install --no-cache --frozen-lockfile --production; \
else \
yarn install --no-cache --development; \
fi;
# build only in production
FROM dependancies AS build
COPY . .RUN set -ex; \
if [ "$NODE_ENV" = "development" ]; then \
echo "Skip production build"; \
else \
echo "Production build"; \
yarn run production-build; \
fi;
EXPOSE 3000
CMD ["yarn", "start"]

Pour aller plus loin

A présent, vous devriez pouvoir utiliser une image docker sans trop de mal.

Rapidement, vous aurez envie ou besoin de faire tourner plusieurs applications ensemble: un frontend et une api, une api et une base de données, … De là, dirigez-vous vers docker-compose, un orchestration qui, avec un simple fichier de configuration, vous permet de créer des stacks complètes sur un serveur.

Si une image docker peut assurer un service simple sur un serveur unique, vous aurez probablement la nécessité de le rendre scalable pour supporter plus de charges (connexions, load balancing, ressources serveurs, etc…). Vous pourrez alors vous plonger dans le mode swarm ou kubernete permettant de passer vos conteneurs en cluster (entre autre).

Liens

Outils

KB:

--

--

Devpulsion

Fullstack JS Freelance Developper, in love with code and startups :)