318 lines
14 KiB
Markdown
318 lines
14 KiB
Markdown
# TP Docker
|
|
|
|
## Introduction
|
|
|
|
Ce TP a pour but de vous familiariser avec Docker et Docker Compose.
|
|
Dans un premier temps, nous allons _dockeriser_ une application Java qui utilise OpenCV. Cela consiste à créer un Dockerfile contenant les dépendances nécessaires pour compiler et exécuter l'application.
|
|
Ensuite, nous allons configurer Docker Compose pour déployer plusieurs instances de l'application avec un serveur web en reverse proxy.
|
|
|
|
## Liens Utiles
|
|
|
|
<details>
|
|
<summary>Cliquer pour déplier</summary>
|
|
|
|
- [Documentation Docker](https://docs.docker.com/)
|
|
- [Tutoriel Docker](https://docs.docker.com/get-started/)
|
|
- [Reverse Proxy Nginx Automatisé pour Docker](http://jasonwilder.com/blog/2014/03/25/automated-nginx-reverse-proxy-for-docker/)
|
|
- [Compilation OpenCV sur Ubuntu](https://advancedweb.hu/2016/03/01/opencv_ubuntu/)
|
|
|
|
</details>
|
|
|
|
# Modalités de rendu
|
|
|
|
Tous les rendus TLC se font à travers le gitlab de l'ISTIC : https://gitlab2.istic.univ-rennes1.fr/
|
|
|
|
Si vous ne l'avez pas déjà fait, créez sur Gitlab ISTIC un groupe nommé `TLC_2025_<votre_nom>_<votre_prenom>`
|
|
|
|
Pour chaque TP, vous devrez créer un projet dans ce groupe (il est fortement conseillé de fork le repo du projet en le plaçant dans votre groupe gitlab), nommé `TP<numero>_<votre_nom>_<votre_prenom>` et le projet finale nommé `Projet_<votre_nom>_<votre_prenom>`
|
|
|
|
Pour chaque TP, vous devrez ajouter votre enseignant en tant que membre du projet avec le rôle de "Reporter" pour permettre la correction.
|
|
|
|
### Étape 0: Prérequis
|
|
Il est fortement conseillé de dérouler ce TP sur une machine Linux (Ubuntu, Fedora, etc.) ou en utilisant une machine virtuelle (VirtualBox, Vagrant, etc.).
|
|
|
|
1. Installez Docker dans votre environnement de développement.
|
|
2. Clonez ce dépot.
|
|
|
|
## [PARTIE 1] Dockeriser une application
|
|
|
|
### Commandes Utiles
|
|
|
|
<details>
|
|
<summary>pour interagir avec les images</summary>
|
|
|
|
- `docker build -t <nom_image> .` : Construit une image Docker à partir d'un Dockerfile situé dans le répertoire courant et lui donne un nom.
|
|
- `docker rmi <nom_image>` : Supprime une image.
|
|
- `docker images` : Liste les images.
|
|
- `docker pull <nom_image>` : Télécharge une image depuis le Docker Hub.
|
|
|
|
</details>
|
|
|
|
<details>
|
|
<summary>pour interagir avec les conteneurs</summary>
|
|
|
|
- `docker run [nom_image]` : Démarre un conteneur à partir d'une image.
|
|
- `docker ps` : Liste les conteneurs en cours d'exécution.
|
|
- `docker exec -it [nom_conteneur] /bin/bash` : Exécute la commande `/bin/bash` dans un conteneur avec le mode interactif -> ça donne un shell (si bash est installé).
|
|
- `docker stop [nom_conteneur]` : Arrête un conteneur.
|
|
- `docker rm [nom_conteneur]` : Supprime un conteneur.
|
|
- `docker logs [nom_conteneur]` : Affiche les logs d'un conteneur.
|
|
</details>
|
|
|
|
D'autres commandes sont disponibles [ici](https://docs.docker.com/reference/cli/docker/).
|
|
|
|
### Tâche 1 : Créer une Image Docker à partir de `scratch`
|
|
|
|
> Créez un `Dockerfile` à partir d'une image vierge :
|
|
> - Compilez le fichier `hello.c` (dans le dossier **step1.1**) avec `gcc -o hello_dyn hello.c`
|
|
> - Créez un Dockerfile et utilisez `scratch` comme image de base.
|
|
> - Ajoutez le fichier binaire `hello_dyn` et définissez la commande de démarrage.
|
|
> - Build et exécutez l'image. Est-ce que le conteneur démarre correctement ?
|
|
|
|
> Compilez maintenant le fichier `hello.c` avec `gcc -static -o hello hello.c` et utilisez ce binaire au lieu de `hello_dyn`.
|
|
> - Quelle est la différence entre les deux commandes de compilation ?
|
|
|
|
> Corrigez maintenant le Dockerfile pour faire fonctionner le binaire dynamique aussi.
|
|
|
|
<details>
|
|
<summary>Cliquer pour des indices</summary>
|
|
|
|
- Lisez bien les erreurs sur terminal au lancement du conteneur. Ajoutez à l'image ce qui manque.
|
|
</details>
|
|
|
|
### Tâche 2 : Containériser une application existante
|
|
|
|
> Maintenant, on va utiliser comme image de base quelque chose de plus traditionnel, par exemple `ubuntu:18.04` (ou voir d'autres images pertinentes dans le Docker Hub).
|
|
> - Installez les dépendances nécessaires pour OpenCV.
|
|
> - Ajoutez les fichiers sources (dans le dossier **step1.2**) et compilez l'application.
|
|
> - Assurez-vous d'avoir la bonne commande de démarrage du conteneur.
|
|
|
|
<details>
|
|
<summary>Cliquer pour des liens utiles</summary>
|
|
|
|
- [Dockerhub - repo images](https://hub.docker.com/)
|
|
- [Dockerfile Reference](https://docs.docker.com/reference/dockerfile)
|
|
</details>
|
|
|
|
<details>
|
|
<summary>Cliquer pour des indices</summary>
|
|
|
|
- Installez des dépendances comme OpenJDK, Maven, et OpenCV :
|
|
- `openjdk-8-jdk maven libpng16-16 libjasper1 libdc1394-22`
|
|
- Installez OpenCV avec :
|
|
- `mvn install:install-file -Dfile=./lib/opencv-3410.jar -DgroupId=org.opencv -DartifactId=opencv -Dversion=3.4.10 -Dpackaging=jar` (Adaptez les path en fonction de votre Dockerfile).
|
|
- Pour compiler l'application :
|
|
- `mvn package`
|
|
- Pour exécuter l'application :
|
|
- `java -Djava.library.path=lib/ -jar target/fatjar-0.0.1-SNAPSHOT.jar`
|
|
- si vous avez Ubuntu > 18.04, utilisez `java -Djava.library.path=lib/ubuntuupperthan18 -jar target/fatjar-0.0.1-SNAPSHOT.jar`
|
|
- L'application est accessible sur le port 8080. Assurez-vous d'exposer ce port ou de le bind à un port de votre choix au démarrage du conteneur. Si tout est correct, http://localhost:8080 devrait être ouvert depuis votre navigateur.
|
|
</details>
|
|
|
|
### Tâche 3 : Améliorer le Dockerfile pour une image plus _light_
|
|
|
|
>Maintenant que vous avez une image fonctionnelle, vous allez essayer de la rendre plus légère.
|
|
>
|
|
>Proposez un nouveau fichier Dockerfile qui permet de créer une image de taille réduite.
|
|
|
|
<details>
|
|
<summary>Cliquer pour des liens utiles</summary>
|
|
|
|
- [Build Multi-Stage Docker 1](https://learnk8s.io/blog/smaller-docker-images)
|
|
- [Build Multi-Stage Docker 2](https://docs.docker.com/develop/develop-images/multistage-build/)
|
|
|
|
</details>
|
|
|
|
## [PARTIE 2] Configurer un reverse proxy sous Docker
|
|
### Tâche 1 : Simple reverse proxy avec ligne de commande `docker`
|
|
|
|
<details>
|
|
<summary> Cliquer pour des liens utiles</summary>
|
|
|
|
Pour le nginx en reverse proxy, nous allons partir de l'image [suivante](https://github.com/jwilder/nginx-proxy).
|
|
|
|
L'explication du fonctionnement est disponible [ici](http://jasonwilder.com/blog/2014/03/25/automated-nginx-reverse-proxy-for-docker/).
|
|
</details>
|
|
|
|
> Si vous n'avez pas la tête à lire ça, la version abrégée est que le reverse proxy vous permet tout un tas de choses, y compris de gérer le fait que les containers ont des adresses IP (un peu) trop dynamiques, ce qui fait qu'à chaque changement/lancement de container, il y aurait des problèmes de binding de port. Le reverse proxy va vous permettre de cacher ces aspects-là, puisqu'ils seront gérés par ce composant. Ainsi, les chargements de versions modifiées de votre service n'auront pas besoin d'une gestion fine à la main des connexions, les différents utilisateurs qui voudront envoyer des requêtes simultanées au même service ne seront pas embêtés par des ports qui ne sont pas accessibles, etc.
|
|
|
|
- Lancement de nginx en reverse proxy :
|
|
>
|
|
> ```bash
|
|
> docker run -d -p 8080:80 -v /var/run/docker.sock:/tmp/docker.sock -t jwilder/nginx-proxy
|
|
> ```
|
|
>
|
|
> ⚠️ Pour certaines installations comme sur la dernière édition de Fedora, les règles de sécurité par défaut ont évolué. Pour que le container puisse accéder à la socket Docker, il faut ajouter l'option suivante :
|
|
>
|
|
> ```bash
|
|
> docker run --security-opt=label:type:docker_t -d -p 8080:80 -v /var/run/docker.sock:/tmp/docker.sock -t jwilder/nginx-proxy
|
|
> ```
|
|
>
|
|
- Si vous êtes sur votre propre portable, modifiez votre fichier `/etc/hosts` pour faire correspondre **m** vers localhost. Ce serait à faire sur votre gestionnaire de nom de domaine en temps normal.
|
|
> Vous devez avoir une ligne qui ressemble à cela :
|
|
>
|
|
> ```txt
|
|
> 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain m
|
|
> ```
|
|
>
|
|
> Pour ceux qui n'ont pas les droits root, exécutez les commandes suivantes :
|
|
>
|
|
> ```bash
|
|
> echo 'm localhost' >> ~/.hosts
|
|
> export HOSTALIASES=~/.hosts
|
|
> curl m:8080
|
|
> ```
|
|
>
|
|
- Puis créez plusieurs fenêtres dans votre terminal. Ce seront vos différentes machines host émulées. Vous pouvez en créer au moins 3 ou 4. Dans ces terminaux, lancez la commande suivante pour tester votre reverse proxy :
|
|
>
|
|
> ```bash
|
|
> docker run -e VIRTUAL_HOST=m -t -i nginx
|
|
> ```
|
|
>
|
|
- Testez votre reverse proxy en lançant la commande suivante dans votre terminal originel :
|
|
>
|
|
> ```bash
|
|
> curl m:8080
|
|
> ```
|
|
>
|
|
> En l'exécutant plusieurs fois et suffisamment rapidement, vous devriez voir tantôt une fenêtre terminator se mettre à jour, tantôt une autre. C'est l'effet du load balancer (un autre service qui est géré par votre nginx).
|
|
>
|
|
> En tapant la commande suivante, vous pouvez regarder le fichier de configuration nginx qui sera généré à l'adresse suivante `/etc/nginx/conf.d/default.conf`.
|
|
>
|
|
> (N'oubliez pas de remplacer `865c1e67a00e` par l'id de votre nginx en reverse proxy (`docker ps`) pour récupérer la liste des containers en cours d'exécution) :
|
|
> ```bash
|
|
> docker exec -it 865c1e67a00e bash
|
|
> ```
|
|
>
|
|
|
|
> ️️⚠️ N'oubliez pas de tuer les conteneurs lancés pour libérer des ressources :
|
|
>
|
|
> ```bash
|
|
> docker ps # pour avoir la liste
|
|
> docker kill "IDDOCKER" # pour tuer un docker
|
|
> ```
|
|
|
|
|
|
### Tâche 2 : Configurer le reverse proxy dans un fichier Docker Compose
|
|
<details>
|
|
<summary>pour interagir avec un deploiement compose</summary>
|
|
|
|
- `docker-compose up` : Démarre les services.
|
|
- `docker-compose down` : Arrête les services.
|
|
- `docker-compose up -f <fichier>` : Démarre les services à partir d'un fichier spécifique.
|
|
- D'autres commandes sont disponibles [ici](https://docs.docker.com/reference/cli/docker/compose/).
|
|
</details>
|
|
|
|
> - Créez un fichier **docker-compose.yml** avec `jwilder/nginx-proxy`
|
|
|
|
<details>
|
|
<summary>Cliquer pour un exemple</summary>
|
|
|
|
> Presque toutes les commandes Docker peuvent être traduites en fichier **docker-compose.yml**. Cela permet de "scripter" le lancement de plusieurs conteneurs et surtout permet de simplifier la communication entre eux.
|
|
>
|
|
> Par exemple, le fichier compose suivant permet de lancer deux conteneurs:
|
|
> ```yaml
|
|
> version: '3'
|
|
> services:
|
|
> serviceA:
|
|
> image: debian
|
|
> command: ping serviceB
|
|
> serviceB:
|
|
> image: debian
|
|
> command: sleep 1000
|
|
> ```
|
|
> Le serviceA peut simplement ping le serviceB en utilisant son nom de service.
|
|
|
|
> Pour le reverse proxy, le fichier **docker-compose.yml** pour démarrer pourrait ressembler à ceci:
|
|
>```yaml
|
|
> version: '3'
|
|
> services:
|
|
> nginx-proxy:
|
|
> image: jwilder/nginx-proxy
|
|
> ports:
|
|
> - "8080:80"
|
|
> volumes:
|
|
> - /var/run/docker.sock:/tmp/docker.sock
|
|
>```
|
|
</details>
|
|
|
|
|
|
> - Ajoutez un service nginx classique qui utiliserait le reverse proxy et donnez lui un nom vhost.
|
|
> - Assurez-vous que votre fichier **/etc/hosts** contient une entrée pour le nom de domaine que vous avez choisi (vhost).
|
|
> - Vérifiez que tout fonctionne correctement en accédant à l'URL du vhost.
|
|
|
|
|
|
<details>
|
|
<summary>Cliquer pour des liens utiles</summary>
|
|
|
|
- [Docker compose services options](https://docs.docker.com/reference/compose-file/services/)
|
|
- [Repo officiel jwilder/nginx-proxy](https://hub.docker.com/r/jwilder/nginx-proxy/)
|
|
</details>
|
|
|
|
### Tâche 3 : Docker Compose avec 4 Instances
|
|
> Maintenant que vous avez familiarisé avec Docker Compose et le reverse proxy, ajoutez au **docker-compose.yaml** votre application Java en veillant à bien configurer le service.
|
|
> - Vérifiez que l'application fonctionne correctement en accédant à l'URL du vhost.
|
|
> - Modifiez le fichier compose pour permettre l'exécution de 4 instances de l'application.
|
|
|
|
<details>
|
|
<summary>Cliquer pour des liens utiles</summary>
|
|
|
|
- [Docker compose services options](https://docs.docker.com/reference/compose-file/services/)
|
|
</details>
|
|
|
|
|
|
|
|
## Rendu TP Docker
|
|
- Un fichier `Dockerfile` pour l'image scratch avec le binaire dynamique.
|
|
- Un fichier `Dockerfile` pour l'application Java.
|
|
- Un fichier `Dockerfile` pour l'application Java version light.
|
|
- Un fichier `docker-compose.yml` avec le reverse proxy, un service web simple et 4 instances de l'application Java.
|
|
|
|
# Annexes
|
|
<details>
|
|
<summary>Annexe 1 : Description de l'Application Java</summary>
|
|
|
|
# How to compile this application
|
|
|
|
Simple example of using OpenCV in a Web application build using jersey.
|
|
|
|
This application takes a picture using web browsers camera API (available in modern browsers)
|
|
and runs OpenCV face recognition algorithm (using [CascadeClassifier](http://docs.opencv.org/java/org/opencv/objdetect/CascadeClassifier.html) ) for it. If a face is detected a "troll face" is added on top of it.
|
|
|
|

|
|
|
|
This application was inspired by the ingenious ["Trollator" mobile Android application](https://play.google.com/store/apps/details?id=com.fredagapps.android.trollator).
|
|
|
|
1. OpenCV Installation for local Maven repository
|
|
---
|
|
OpenCV is a native library with Java bindings so you need to install this to your system.
|
|
- *libopencv_java3410.so* installed in you java.library.path (
|
|
- *opencv-3410.jar* availble for application
|
|
|
|
There are good instructions how to build OpenCV with Java bindings for your own platform here: http://docs.opencv.org/doc/tutorials/introduction/desktop_java/java_dev_intro.html
|
|
|
|
Once you have built the Java library you can install the resulting jar file to your local Maven repository using
|
|
```shell
|
|
mvn install:install-file -Dfile=./lib/opencv-3410.jar -DgroupId=org.opencv -DartifactId=opencv -Dversion=3.4.10 -Dpackaging=jar
|
|
```
|
|
|
|
2. Building this application
|
|
----
|
|
Once OpenCV jar library is available as a local Maven dependency, you can clone and build this application simply using Git and Maven:
|
|
|
|
```bash
|
|
mvn install
|
|
```
|
|
|
|
And run the application using the embedded Jetty plugin in http://localhost:8080
|
|
|
|
```bash
|
|
mvn package
|
|
|
|
java -Djava.library.path=lib/ -jar target/fatjar-0.0.1-SNAPSHOT.jar
|
|
# Do not forget to update the path to your opencv install in Main.java
|
|
# You can change the image trollface ;)
|
|
```
|
|
|
|
</details>
|