Ce tutoriel a pour but de découvrir la solution de conteneur Docker et son écosystème.
Les fichiers utilisés dans ce tutoriel sont disponibles dans le dépot git suivant : https://gitlab.in2p3.fr/cavet/tp-docker-obs |
Pour récupérer les fichiers vous pouvez cloner le dépot git :
$ git clone https://gitlab.in2p3.fr/cavet/tp-docker-obs.git
Dans ce tutoriel, nous allons utiliser différents OS pour les exécuter les conteneurs : CentOS 7, Ubuntu, Alpine…
Installation de Docker
Utilisation de Docker version 19.03.5-ce, disponible ici : Docker Website.
-
Linux CentOs : Docker Website
-
Docker client/démon :
-
$ yum install -y yum-utils device-mapper-persistent-data lvm2 $ yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo $ yum install -y docker-ce docker-ce-cli containerd.io $ systemctl start docker
-
Linux Ubuntu : Docker Website
-
Docker client/démon :
-
$ apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common $ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - $ add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" $ apt-get install docker-ce docker-ce-cli containerd.io
-
Docker Compose (v1.24.1) :
$ curl -L "https://github.com/docker/compose/releases/download/1.24.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose $ chmod +x /usr/local/bin/docker-compose $ ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
-
Docker Machine (v0.16.2) :
$ curl -L https://github.com/docker/machine/releases/download/v0.16.2/docker-machine-`uname -s`-`uname -m` >/tmp/docker-machine && chmod +x /tmp/docker-machine && sudo cp /tmp/docker-machine /usr/local/bin/docker-machine $ ln -s /usr/local/bin/docker-machine /usr/bin/docker-machine
-
Mac OS : Docker Website pour Docker Desktop.
-
Windows : Docker Website pour Docker Desktop.
Pour tester :
$ docker version $ docker-compose version $ docker-machine version
L’aide de Docker est accessible via la commande suivante :
$ docker help Usage: docker [OPTIONS] COMMAND A self-sufficient runtime for containers Options: --config string Location of client config files (default "/root/.docker") -c, --context string Name of the context to use to connect to the daemon (overrides DOCKER_HOST env var and default context set with "docker context use") -D, --debug Enable debug mode -H, --host list Daemon socket(s) to connect to -l, --log-level string Set the logging level ("debug"|"info"|"warn"|"error"|"fatal") (default "info") --tls Use TLS; implied by --tlsverify --tlscacert string Trust certs signed only by this CA (default "/root/.docker/ca.pem") --tlscert string Path to TLS certificate file (default "/root/.docker/cert.pem") --tlskey string Path to TLS key file (default "/root/.docker/key.pem") --tlsverify Use TLS and verify the remote -v, --version Print version information and quit Management Commands: builder Manage builds config Manage Docker configs container Manage containers context Manage contexts engine Manage the docker engine image Manage images network Manage networks node Manage Swarm nodes plugin Manage plugins secret Manage Docker secrets service Manage services stack Manage Docker stacks swarm Manage Swarm system Manage Docker trust Manage trust on Docker images volume Manage volumes Commands: attach Attach local standard input, output, and error streams to a running container build Build an image from a Dockerfile commit Create a new image from a container's changes cp Copy files/folders between a container and the local filesystem create Create a new container diff Inspect changes to files or directories on a container's filesystem events Get real time events from the server exec Run a command in a running container export Export a container's filesystem as a tar archive history Show the history of an image images List images import Import the contents from a tarball to create a filesystem image info Display system-wide information inspect Return low-level information on Docker objects kill Kill one or more running containers load Load an image from a tar archive or STDIN login Log in to a Docker registry logout Log out from a Docker registry logs Fetch the logs of a container pause Pause all processes within one or more containers port List port mappings or a specific mapping for the container ps List containers pull Pull an image or a repository from a registry push Push an image or a repository to a registry rename Rename a container restart Restart one or more containers rm Remove one or more containers rmi Remove one or more images run Run a command in a new container save Save one or more images to a tar archive (streamed to STDOUT by default) search Search the Docker Hub for images start Start one or more stopped containers stats Display a live stream of container(s) resource usage statistics stop Stop one or more running containers tag Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE top Display the running processes of a container unpause Unpause all processes within one or more containers update Update configuration of one or more containers version Show the Docker version information wait Block until one or more containers stop, then print their exit codes Run 'docker COMMAND --help' for more information on a command.
Instructions globales pour le tutoriel
-
root@id : super utilisateur dans le conteneur avec l’identifiant
id = CONTAINER ID = bb9720…
-
Toutes les instructions (noms, commandes) sont en minuscule sauf le
Dockerfile
. -
Commande : précédé d’un
$
. -
Output d’une commande : sans
$
. -
Instruction sur la ligne suivante :
\
. -
Attention au chemin local :
$ pwd
.
Mes premiers conteneurs
Exécution du premier conteneur
Lancer un conteneur basique avec le client Docker :
-
Récupérer la dernière version de l’image CentOS depuis le Docker Hub (Docker Store) et l’éxécuter en ouvrant un shell bash.
-
Options : i - STDIN open, t - pseudo-tty terminal, rm - remove container after use.
$ docker pull centos:latest $ docker run -it --name mycontainer --rm centos [root@id /]$ cat /etc/redhat-release CentOS Linux release 8.0.1905 (Core) [root@id /]$ yum install -y nano [root@id /]$ exit
$ docker ps $ docker images $ docker logs mycontainer $ docker inspect mycontainer $ docker ps -a
Le client Docker permet de lancer des conteneurs de deux manières : |
$ docker run ... $ docker container run ...
Quelques commandes pour gérer le système, nettoyer les conteneurs et les images
Ne pas supprimer l’image Jupyter avec : $ docker rmi $(docker images -q)
|
$ docker system df $ docker system prune $ docker rm -f id $ docker rm -f $(docker ps -a -q) $ docker rmi id $ docker rmi $(docker images -q)
Exécution du second conteneur
Lancer un conteneur avec Python :
-
Récupérer et lancer un conteneur utilisant une image avec le shell Python pré-installé puis le même conteneur en ouvrant le shell bash.
-
Choisir le tag de l’image : 2.7 ou 3.7.
$ export tag=3.7 $ docker run -it --name mycontainer --rm python:$tag Python XXX >>> print('Hello Python') Hello Python >>> exit() $ docker run -it --name mycontainer --rm python:$tag /bin/bash [root@id /]$ python Python XXX
$ docker ps -a
Mes premières applications
On distingue deux types d’applications : micro-service ou application scientifique. |
Exécution de la première application
Lancer une application micro-service permettant d’exécuter des Notebooks Python :
-
Récupérer l’image Jupyter Scipy Notebook qui est basée sur Ubuntu et qui contient le Jupyter Notebook serveur, Scipy et JupyterLab (vous pouvez aussi utiliser une image de taille plus petite, la Jupyter Base Notebook :
base-notebook
). Attention : la taille compressée de Jupyter Scipy Notebook est de 2 GB (4,39 GB décompréssée), le temps de téléchargement avoisine ∼10min. -
Lancer l’image en mode détaché et avec la publication des ports réseaux (association des ports conteneur/hote).
-
Options : d - detach processus, p - network port.
$ docker pull jupyter/scipy-notebook Already exists Download complete Extracting Pull complete $ docker run -d --name myjupyter -p 8888:8888 jupyter/scipy-notebook $ docker ps
Se connecter à l’interface Jupyter :
-
Vérifier que l’utilisateur est
jovyan
et récupérer son jeton de connexion.
$ docker logs myjupyter http://127.0.0.1:8888/?token=... $ docker exec myjupyter jupyter notebook list $ docker exec myjupyter pwd /home/jovyan
http://127.0.0.1:8888/?token=...
Copier un fichier stocké localement dans le conteneur pour l’exécuter :
-
Utiliser un Notebook Python ou récupérer celui du détecteur d’ondes gravitationnelles LIGO, mettre le Notebook dans un répertoire local et le copier dans le conteneur :
$ mkdir local_work && cd local_work $ wget https://losc.ligo.org/s/events/LOSC_Event_tutorial.ipynb $ docker cp LOSC_Event_tutorial.ipynb myjupyter:/home/jovyan/work
> Run LOSC_Event_tutorial.ipynb ModuleNotFoundError: No module named 'readligo'
Exécution de la deuxième application
Lancer une deuxième instance du serveur Jupyter afin d’exécuter le Notebook permettant d’analyser les données d’un évènement de LIGO :
-
Récupérer l’ensembles des données de LIGO.
-
Exporter le chemin local dans une variable d’environnement.
-
Changer le port du conteneur redirigé localement.
-
Monter le répertoire local
local_work
dans le conteneur. -
Options : v - mount volume.
$ wget https://losc.ligo.org/s/events/LOSC_Event_tutorial.zip && unzip LOSC_Event_tutorial.zip $ export user_path=`pwd` $ docker run -d --name myjupyter2 -p 8889:8888 -v $user_path/LOSC_Event_tutorial:/home/jovyan/work jupyter/scipy-notebook
> Run LOSC_Event_tutorial.ipynb
Tuer les deux instances Jupyter.
Création d’images
Création de la première image
Construire l’image d’une application echo
et la lancer dans un conteneur :
-
Copier le
Dockerfile
(recette de construction d’image) et le fichier.dockerignore
(sélectionne les fichiers copiés dans le conteneur lors dubuild
) dans un répertoirelocal_build
. -
Construire une image de l’application et la lancer.
-
Options : t - tag name
FROM centos:7.7.1908 MAINTAINER Cecile Cavet "ccavet@apc.in2p3.fr" RUN yum install -y nano ENTRYPOINT ["echo"] CMD ["Le runscript est la commande par défaut du conteneur !"]
#.dockerignore .DS_Store .git Dockerfile*
$ mkdir local_build && cd local_build $ ls -a .dockerignore Dockerfile $ docker build -t myapp . $ docker images myapp latest 208c47af0787 3 days ago 918MB $ docker run myapp Le runscript est ... $ docker run myapp hello world hello world
Création de la deuxième image
Construire une image d’une application scientifique en installant des paquets spécifiques et la lancer dans un conteneur :
-
Copier le
Dockerfile
dans un fichierDockerfile.app
dans le répertoirelocal_build
. -
Copier les fichiers
requirements.txt
etLOSC_Event_tutorial.py
dans le répertoirelocal_build
. -
Modifier le
Dockerfile.app
pour qu’il puisse exécuter le script PythonLOSC_Event_tutorial.py
(modifier/rajouter les instructionsCOPY
,ENTRYPOINT
etCOMMAND
). -
Construire une image de l’application et la lancer avec le volume des données LIGO monté dans le conteneur.
numpy==1.17.2 scipy==1.3.1 h5py==2.9.0 simplejson==3.16.0 matplotlib==3.1.1 ipython==7.8.0 ipython-genutils==0.2.0
WORKDIR /app COPY . /app/ RUN pip install --no-cache-dir -r requirements.txt ENTRYPOINT ["python"] CMD ["LOSC_Event_tutorial.py"]
$ cp ../local_work/LOSC_Event_tutorial/LOSC_Event_tutorial.py . $ docker build -t myapp -f Dockerfile.app . $ docker images myapp latest 208c47af0787 3 days ago 1.15GB $ docker run -v $user_path/LOSC_Event_tutorial:/app myapp $ ls -lrt ../local_work/LOSC_Event_tutorial GW150914_strain.png ...
Gestion des images
Docker Hub
Explorer le Docker Hub (Store, https://hub.docker.com) dans le but de trouver l’image Python utilisée précédemment et son Dockerfile.
Docker Registry
Mettre en place un registre local et pousser une image dans ce gestionnaire d’image :
-
Récupérer l’image Registry et lancer le service correspondant.
-
Tagger l’image myapp et la pousser sur le registre.
-
Options : restart always - always restart container in case of failure.
$ docker run -d -p 5000:5000 --restart always --name registry registry:2 $ docker tag myapp localhost:5000/myapp $ docker images localhost:5000/myapp latest 88280d4a85ec 9 days ago 1.15GB $ docker push localhost:5000/myapp $ docker exec registry ls /var/lib/registry/docker/registry/v2/repositories
GitLab-CI
Réutiliser le projet en Python myapp
et utiliser l’intégration continue pour permettre la construction automatique d’une image Docker accessible via le registre GitLab :
-
Copier le fichier d’intégration continue
.gitlab-ci.yaml.example
danslocal_build/.gitlab-ci.yaml
-
Pousser le contenu du répertoire
local_build
dans un projet de GitLab. -
Vérifier l’éxécution du pipeline de CI sur l’interface graphique de GitLab (
/projet/pipelines
). -
Vérifier la construction de l’image Docker sur l’interface graphique de GitLab (
/projet/container_registry
).
variables: DOCKER_DRIVER: overlay2 stages: - build build: image: docker:latest services: - docker:dind before_script: - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY stage: build script: - export IMAGE_TAG=$(echo -en $CI_BUILD_REF_NAME | tr -c '[:alnum:]_.-' '-') - docker build --pull -t "$CI_REGISTRY_IMAGE:$IMAGE_TAG" . - docker push "$CI_REGISTRY_IMAGE:$IMAGE_TAG" - echo 'pushed on registry'
$ ls -la .dockerignore .gitlab-ci.yml Dockerfile LOSC_Event_tutorial.py requirements.txt $ git init $ git remote add origin git@gitlab.fr:projet/myapp.git $ git add . $ git commit -m "Initial commit" $ git push -u origin master
Utiliser la nouvelle image Docker de votre projet qui a été construite automatiquement :
$ docker login https://gitlab-registry.fr $ cat ~/.docker/config.json $ docker pull gitlab-registry.fr/user/projet:latest $ docker run -v $user_path/LOSC_Event_tutorial:/app gitlab-registry.fr/user/projet:latest
Déploiement de conteneurs avec Docker Compose
Permet de lancer plusieurs conteneurs en interaction avec une seule commande. |
Ma première application composée
Lancer une application de micro-service via Docker Compose :
-
Copier le fichier
docker-compose.yml
qui permet de lancer un seul service (le Jupyter serveur). -
Exporter la variable d’environnement
LOCAL_PATH
. -
Exécuter le fichier YAML avec Docker Compose.
-
Options : le mot clé version 3 correspond à la dernière version de Docker Compose compatible avec la version 2 et Docker Swarm.
version: "3" services: jupyter: image: jupyter/scipy-notebook container_name: jupyter volumes: - $LOCAL_PATH:/home/jovyan/work/local ports: - "8888:8888" volumes: workspace:
$ export LOCAL_PATH=`pwd` $ docker-compose up -d Creating jupyter ... done $ docker-compose ps Name Command State Ports ------------------------------------------------ jupyter tini... Up 0.0.0.0:8888->8888/tcp $ docker-compose logs jupyter $ docker-compose exec jupyter $ docker-compose down
Création de Machines Virtuelles avec Docker Machine
Permet de créer des MV locales ou sur le cloud avec Docker installé et configuré. |
Lancer une MV via Docker Machine :
-
en local si vous avez VirtualBox installé.
-
sur une infrastructure de cloud computing (OpenStack) si vous avez un compte sur ce type de ressources. Il est nécessaire de sourcer d’abord les identifiants du compte.
$ docker-machine create --driver virtualbox --virtualbox-memory 2048 --virtualbox-cpu-count "2" --virtualbox-disk-size "2000" default $ docker-machine ls $ docker-machine ssh default $ docker-machine rm -f default
$ source ~/.novacreds/novarc.sh $ docker-machine create --driver openstack --openstack-flavor-name "m1.small" --openstack-image-name centos-7 --openstack-domain-name default --openstack-net-name cloud-net --openstack-floatingip-pool ext-net --openstack-keypair-name cloudkey --openstack-private-key-file ~/.novacreds/cloudkey --openstack-ssh-user centos test