Après la leçon 1, voilà mes notes prises lors de la seconde leçon sur Docker.
Fabriquer une image
Interactivement
$ docker commit <container id> <repository:tag>
- le tag par défaut est
latest
- le nom du dépôt est souvent composé du nom du registry (basé par exemple sur le nom de l’utilisateur ou dockerhub) et du nom de l’application.
Imaginons par exemple que je souhaite construire une image avec tous mes outils préférés déjà installés :
$ docker run -i -t ubuntu:14.04 /bin/bash
$ apt-get install -y ... # tous mes outils préférés
$ exit
$ docker ps -a # pour récupérer d'id
$ docker commit <id> anne/ma_base:1.0
$ docker images # on doit voir la nouvelle image
Pour utiliser cette nouvelle image :
docker run -i -t anne/ma_base:1.0 /bin/bash
A partir d’un fichier de configuration
Un Dockerfile
est fichier contenant des instructions pour construire une
nouvelle image.
# Un petit exemple
FROM ubuntu:14.04
RUN apt-get install -y vim
Attention, chaque commande conduit à construire une nouvelle image. Il peut donc être préférable de les regrouper :
RUN apt-get update \
&& apt-get install -y vim git ...
L’intérêt d’avoir les images intermédiaires, c’est que si on ajoute des lignes
au Dockerfile
par la suite, il n’aura pas besoin de refaire les premières
étapes car il les aura sauvegardé, et il sait les retrouver.
Pour construire l’image :
$ docker build -t <repository:tag> <contexte>
Le contexte indique où chercher le Dockerfile
.
Par exemple le contexte est .
pour le répertoire courant :
$ vim Dockerfile
$ docker build -t anne/test:1.0 .
$ docker images
L’instruction CMD
donne la commande à lancer quand on utilise le container.
Elle n’est pas exécutée lors de la création de l’image.
Si on ne donne pas de commande dans le Dockerfile
,
il lance la commande par défaut du container de départ. Par
exemple, pour Ubuntu, il s’agit de la commande bash
.
Il faut noter que la commande peut être donnée lors de l’utilisation du
container. C’est alors cette dernière qui a la priorité.
Donc si on utilise l’image précédente, il ne se passe rien :
docker run anne/test:1.0
Si on ajoute une commande au Dockerfile
:
CMD ["ping", "127.0.0.1", "-c", "10"]
Et qu’on construit et lance une nouvelle image :
$ docker build -t anne/test:1.1 .
$ docker run anne/test:1.1
On voit les ping
s’exécuter. On peut surcharger cette commande en faisant :
$ docker run anne/test:1.1 echo "coucou"
Cette fois, plus de ping
, mais juste coucou
.
La commande ENTRYPOINT
est similaire à CMD
, sauf qu’on ne peut pas la
surcharger, et que par contre, on peut lui passer des arguments.
Si on remplace la commande CMD
dans le Dockerfile
par :
ENTRYPOINT ["ping"]
Et qu’on construit et lance une nouvelle image :
$ docker build -t anne/test:1.2 .
$ docker run anne/test:1.2
Ça râle, car ping
attend des arguments, mais on peut alors faire :
docker run anne/test:1.2 127.0.0.1 -c 5
Gestion des images et des containers
Pour arrêter un container qui tourne en arrière plan :
$ docker stop <id|nom>
(pour trouver l’identificateur ou le nom, docker ps
avec éventuellement
-a
).
Pour le redémarrer :
$ docker start <id|nom>
On peut lancer un nouveau processus dans container qui tourne avec :
$ docker exec -it <id|nom> bash
Cette fois, quand on quitte bash, le container ne s’arrête pas de tourner car il ne s’agit pas du processus 1.
Nettoyage
Pour détruire complètement un container (qui est arrêté) :
$ docker rm <id|nom>
Pour détruire localement une image :
docker rmi <image_id|repo:tag>
Par exemple:
docker images
docker rmi anne/test:1.0
Transfert sur DockerHub
Sur DockerHub, on peut créer des dépôts publiques ou privés.
On les crée à partir de l’interface web.
Pour pousser (push
) une image, il faut que le nom de dépôt
soit le même que sur DockerHub.
Si ce n’est pas le cas, on peut renommer l’image avec :
$ docker tag anne/test:1.0 anne/exercices:1.0
(attention, c’est plutôt une copie qu’un renommage).
On peut ensuite faire :
docker push anne/exercices:1.0
(la première fois, il décliner son identité et mot de passe).
On peut vérifier sur l’interface web de DockerHub que l’image est bien là.
Volumes
Les volumes sur des répertoires persistants du container.
Les données sont indépendantes du cycle de vie du container.
Quand on modifie une image, les données persistantes ne seront pas modifiées.
Elles persistent même si le container est détruit,
et peuvent être partagées entre plusieurs container.
Pour monter un volume lorsqu’on lance un container, on utilise l’option -v
:
$ docker run -d -v /mon_volume anne/test:1.2
Les volumes être associés à des répertoires sur l’hôte :
$ docker run -i -t -v /data/src:/test/src anne/test:1.2
Ici, /data/src
est le répertoire sur la machine hôte,
et /test/src
le point de montage dans le container.
Bien sûr, on peut spécifier ça dans le Dockerfile
.
Il y a plusieurs syntaxes :
VOLUME /mon_volume
VOLUME /www/site1.com /www/site2.com
VOLUME ["vol_1", "vol_2"]
Par contre, cela ne permet pas de monter un répertoire de l’hôte,
car les Dockerfile
sont conçus pour pouvoir être déplacés,
et exécuter sur différentes machines.
Les volumes permettent de stocker des données que l’on peut garder indépendamment du container, comme des fichiers de log par exemple. Ça permet aussi de partager des données entre plusieurs container.
Attention, monter des répertoires de l’hôte n’est à utiliser que lors de tests. Ce n’est pas à utiliser en production car cela lie fortement le container à la machine hôte, ce qui n’est pas souhaitable.
Exemple :
$ docker run -i -t -v /volume ubuntu:14.04 /bin/bash
# ls
# echo "toto" > /volume/test
# cat /volume/test
# exit
$ docker ps -a
$ docker commit <id> voltest:1.0
$ docker run -i -t voltest:1.0 bash
# ls /volume
On observe que le répertoire /volume
est bien là, mais il est vide,
car les données ne sont pas exportées.
Créer un volume de données
Pour créer un volume et mettre des données dedans, on fait :
$ docker create -v /volume --name mes_donnees busybox
L’image busybox
est juste une petite image toute simple utilisée comme base.
On peut vérifier que le répertoire /volume
est vide :
$ docker run --rm --volumes-from mes_donnees ubuntu ls -la /volume
(--rm
permet de créer un container temporaire qui est automatiquement
détruit après usage).
$ docker run -ti --rm --volumes-from mes_donnees ubuntu bash
# ls volume
# echo "coucou" > /volume/test
# cat /volume/test
# exit
$ docker run -ti --rm --volumes-from mes_donnees ubuntu bash
# ls /volume
# cat /volume/test
# exit
Cette fois, les données sont bien toujours là.
On peut voir le container de donné en faisant :
$ docker ps -a
Pour un exemple d’utilisation des volumes, voir là.
Attention, lorsqu’on crée un volume de données, l’image de base (ici busybox) n’a pas d’importance. Du coup, il est intéressant d’utiliser la même image que le container utilisateur. Comme ça, on n’a besoin que d’une seule image, et en plus, on a la même environnement (groupes, utilisateurs, etc).
Réseau
Brancher les ports
Pour parler à des applications web
dans le container, il faut associer les ports.
Pour exemple, si le serveur nginx écoute sur le port 80,
on peut le faire correspondre au port 8085 sur la machine hôte
avec l’option -p
:
$ docker run -d -p 8085:80 nginx:1.7
On voit l’association avec docker ps
.
On peut ensuite vérifier qu’on a bien accès au serveur :
$ firefox localhost:8085
L’option -P
le numéro de port sur l’hôte est choisi automatiquement
parmis les grands nombres, mais seuls les ports spécifiés
par la commande EXPOSE
seront associés. Par exemple :
FROM ubuntu:14.04
RUN apt-get update && apt-get install -y nginx
EXPOSE 80 443
CMD ["nginx", "-g", "daemon off;"]
Associer des containers
On peut aussi lier des containers : le container destinataire a alors accès aux données du container source. La liaison utilise les noms des containers. Il est donc préférable de spécifier le nom plutôt que de laisser docker les choisir comme précédemment. On va d’abord créer le container source :
$ docker run -d --name database postgres
Puis créer le container destination en le liant au précédent :
$ docker run -it --name website --link database:db ubuntu:14.04 /bin/bash
On peut voir dans le fichier /etc/hosts
que le nom db
est associé à une adresse IP.
Si on sort maintenant du container website
, et que l’on cherche
l’adresse IP du container source en faisant :
$ docker instpect database | grep IPAddress
On peut vérifier que c’est la même.
On remarque aussi que quand on lie deux containers source -> destination, on n’est pas obligé d’exposer les ports de la destination. Ceci ne seront donc accessible que de la source.
Intégration continue
La fin du cours explique comment connecter GitHub et DockerHub pour faire des compilations automatiques, mais ça ne me concerne pas pour l’instant…
La suite, pour ma part, est plutôt de voir comment utiliser Jenkins dans Docker.
Voir aussi :
- Afficher un pourcentage dans une page HTML
- VNC : Virtual Network Computing
- Git : déménagement d'un dépôt
- Quelques liens au sujet de l'analyse statique
- Ocaml: mon principal langage de développement
- Disque dur externe
- Les profiles dans Firefox
- Cryptographie et mail sous Android
- Quelques liens au sujet du C
- Git rebase : pour diviser un commit