ajout dev docker
This commit is contained in:
@@ -0,0 +1,6 @@
|
|||||||
|
# BUT2FA-DOCKER
|
||||||
|
|
||||||
|
## Ce dépôt git contient le dossier de chaque TP :
|
||||||
|
* TP1 L'introduction au cycle de vie des dockers et la différenciation des conteneurs et des images
|
||||||
|
|
||||||
|
* TP2 Les volumes et bind-mounts
|
||||||
@@ -0,0 +1,213 @@
|
|||||||
|
# Commandes utilisées - TP1
|
||||||
|
|
||||||
|
## 2. Installation
|
||||||
|
|
||||||
|
### 2.2 Vérification de l'installation :
|
||||||
|
```shell
|
||||||
|
sudo docker --version
|
||||||
|
# Docker version 28.3.3, build 980b856816
|
||||||
|
|
||||||
|
sudo docker run hello-world
|
||||||
|
# Unable to find image 'hello-world:latest' locally
|
||||||
|
# latest: Pulling from library/hello-world
|
||||||
|
# 4f55086f7dd0: Pull complete
|
||||||
|
# Digest: sha256:452a468a4bf985040037cb6d5392410206e47db9bf5b7278d281f94d1c2d0931
|
||||||
|
# Status: Downloaded newer image for hello-world:latest
|
||||||
|
|
||||||
|
# Hello from Docker!
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3 Premiers pas avec Docker
|
||||||
|
|
||||||
|
### 3.1 Images Docker
|
||||||
|
### 3.1.1 Lister les images disponibles
|
||||||
|
```shell
|
||||||
|
sudo docker images
|
||||||
|
# REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||||
|
# hello-world latest e2ac70e7319a 2 weeks ago 10.1kB
|
||||||
|
# prog_python latest 3dabe55bd71c 7 weeks ago 1.12GB
|
||||||
|
# <none> <none> 21d22a499c76 7 weeks ago 1.12GB
|
||||||
|
# python 3.15-rc-trixie 3bf0ec7eb030 2 months ago 1.12GB
|
||||||
|
# imunes/template latest 58009592de73 2 years ago 387MB
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.1.2 Télécharger une image
|
||||||
|
```shell
|
||||||
|
sudo docker pull nginx
|
||||||
|
# Using default tag: latest
|
||||||
|
# latest: Pulling from library/nginx
|
||||||
|
# 5435b2dcdf5c: Pull complete
|
||||||
|
# 054715a6bffa: Pull complete
|
||||||
|
# 88d1d984b765: Pull complete
|
||||||
|
# 4a038fd18db1: Pull complete
|
||||||
|
# 84e114c2bb36: Pull complete
|
||||||
|
# 7b5d674621c2: Pull complete
|
||||||
|
# 448ea5cac5d5: Pull complete
|
||||||
|
# Digest: sha256:7f0adca1fc6c29c8dc49a2e90037a10ba20dc266baaed0988e9fb4d0d8b85ba0
|
||||||
|
# Status: Downloaded newer image for nginx:latest
|
||||||
|
# docker.io/library/nginx:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.1.3 Rechercher une image
|
||||||
|
```shell
|
||||||
|
sudo docker search ubuntu
|
||||||
|
# NAME DESCRIPTION STARS OFFICIAL
|
||||||
|
# ubuntu Ubuntu is a Debian-based Linux operating sys… 17808 [OK]
|
||||||
|
# ubuntu/squid Squid is a caching proxy for the Web. Long-t… 125
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3.2 Gestion basique des conteneurs
|
||||||
|
1. Créer un premier conteneur :
|
||||||
|
```shell
|
||||||
|
docker run nginx
|
||||||
|
# Il tente de start le process/conteneur nginx et reste en stand by. On peut donc l'arrêter avec <CTRL>+C
|
||||||
|
```
|
||||||
|
|
||||||
|
2.
|
||||||
|
```shell
|
||||||
|
docker run -d nginx
|
||||||
|
# 30dfd1b500397ea6b1845ba194f583dd8bae8e55ba828f2ddc2819ede8d7ed00
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4 Cycle de vie des conteneurs
|
||||||
|
|
||||||
|
### 4.1.1 Commandes d'observation
|
||||||
|
```shell
|
||||||
|
docker ps # conteneurs en cours d'exécution
|
||||||
|
# CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||||
|
# 30dfd1b50039 nginx "/docker-entrypoint.…" About a minute ago Up About a minute 80/tcp admiring_hermann
|
||||||
|
|
||||||
|
docker ps -a # tous les conteneurs
|
||||||
|
# CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||||
|
# 30dfd1b50039 nginx "/docker-entrypoint.…" 2 minutes ago Up 2 minutes 80/tcp admiring_hermann
|
||||||
|
# 5a9d4e300f22 nginx "/docker-entrypoint.…" 4 minutes ago Exited (0) 4 minutes ago infallible_banach
|
||||||
|
|
||||||
|
# Pour supprimer toutes les images il faut faire :
|
||||||
|
docker system prune
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.1.2 Exercice pratique
|
||||||
|
1. Créer trois conteneurs nginx avec des noms différents :
|
||||||
|
```shell
|
||||||
|
docker run -d --name web1 nginx
|
||||||
|
# 9de9a334b5001da00fb27ae33e3fc09f4b29a78e9b3606d9cdf61acebaf9941e
|
||||||
|
|
||||||
|
docker run -d --name web2 nginx
|
||||||
|
# db98128c8820faa3720643f7a83d9126a11bf27522a24db7218539023f680a6c
|
||||||
|
|
||||||
|
docker run -d --name web3 nginx
|
||||||
|
# a0d91897890b1eb028571846e5aced4a76bbae8c8c970222a95185b0342e697e
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Les états de chaque conteneurs se remarquent en faisant
|
||||||
|
|
||||||
|
3.
|
||||||
|
```shell
|
||||||
|
docker ps # Après avoir lancé les conteneurs précédents
|
||||||
|
# CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||||
|
# a0d91897890b nginx "/docker-entrypoint.…" 39 seconds ago Up 38 seconds 80/tcp web3
|
||||||
|
# db98128c8820 nginx "/docker-entrypoint.…" 52 seconds ago Up 51 seconds 80/tcp web2
|
||||||
|
# 9de9a334b500 nginx "/docker-entrypoint.…" About a minute ago Up About a minute 80/tcp web1
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4.2 Manipulation des états
|
||||||
|
```shell
|
||||||
|
docker stop web1
|
||||||
|
# ça renvoie : web1 et en faisant docker ps, on voit qu'il n'apparait plus.
|
||||||
|
docker start web1
|
||||||
|
# ça renvoie : web1
|
||||||
|
docker restart web2
|
||||||
|
# Renvoie web2 et en faisant ps on voit que son timer a recommencé
|
||||||
|
docker pause web3
|
||||||
|
# renvoie web3 et affiche ceci dans ps : a0d91897890b nginx "/docker-entrypoint.…" 10 minutes ago Up 10 minutes (Paused) 80/tcp web3
|
||||||
|
docker unpause web3 # le remet à la normal
|
||||||
|
docker kill web1 # n'apparait plus dans le ps. Et dans ps -a : 9de9a334b500 nginx "/docker-entrypoint.…" 12 minutes ago Exited (137) 28 seconds ago web1
|
||||||
|
```
|
||||||
|
|
||||||
|
# 5 Inspection et Debug
|
||||||
|
## 5.1 Logs et monitoring
|
||||||
|
```shell
|
||||||
|
docker logs [container-name]
|
||||||
|
# montre les logs de l'image actuelle du container
|
||||||
|
docker logs -f [container-name]
|
||||||
|
# montre les logs du container en temps réel
|
||||||
|
docker stats
|
||||||
|
# CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
|
||||||
|
# a0d91897890b web3 0.00% 10.43MiB / 15.42GiB 0.07% 3.15kB / 126B 0B / 12.3kB 13
|
||||||
|
# db98128c8820 web2 0.00% 10.41MiB / 15.42GiB 0.07% 2.83kB / 126B 0B / 4.1kB 13
|
||||||
|
# 30dfd1b50039 admiring_hermann 0.00% 10.43MiB / 15.42GiB 0.07% 6.26kB / 126B 0B / 12.3kB 13
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5.2 Inspection détaillée
|
||||||
|
```shell
|
||||||
|
docker inspect [container-name]
|
||||||
|
# "Id": "db98128c8820faa3720643f7a83d9126a11bf27522a24db7218539023f680a6c",
|
||||||
|
# "Created": "2026-04-13T09:08:18.800415878Z",
|
||||||
|
# "Path": "/docker-entrypoint.sh",
|
||||||
|
|
||||||
|
docker exec -it [container-name] /bin/bash
|
||||||
|
# renvoie : root@db98128c8820:/# Il permet d'exécuter des commandes dans un container en tant que root
|
||||||
|
```
|
||||||
|
|
||||||
|
# 6 Exercice final
|
||||||
|
```shell
|
||||||
|
# 1. Créer un conteneur nginx en mode détaché avec le nom ”web-test”
|
||||||
|
docker run -d --name web-test nginx
|
||||||
|
fac3038fbd6c11d119683f146c9be888158e3a145b1ba7050caad8283bb5ac78
|
||||||
|
docker docker ps
|
||||||
|
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||||
|
fac3038fbd6c nginx "/docker-entrypoint.…" 3 seconds ago Up 2 seconds 80/tcp web-test
|
||||||
|
|
||||||
|
# 2. Pratiquer toutes les commandes du cycle de vie vues précédemment
|
||||||
|
```shell
|
||||||
|
[srivasta@salle234-09 TP1]$ docker stop web-test
|
||||||
|
web-test
|
||||||
|
[srivasta@salle234-09 TP1]$ docker start web-test
|
||||||
|
web-test
|
||||||
|
[srivasta@salle234-09 TP1]$ docker restart web-test
|
||||||
|
web-test
|
||||||
|
[srivasta@salle234-09 TP1]$ docker pause web-test
|
||||||
|
web-test
|
||||||
|
[srivasta@salle234-09 TP1]$ docker unpause web-test
|
||||||
|
web-test
|
||||||
|
[srivasta@salle234-09 TP1]$ docker kill web-test
|
||||||
|
web-test
|
||||||
|
```
|
||||||
|
|
||||||
|
# 3. Analyser les logs et les différents états
|
||||||
|
```shell
|
||||||
|
[srivasta@salle234-09 TP1]$ docker logs web-test
|
||||||
|
# Le log est TRES long car l'enchainement de start, restart, stop, kill, chaque démarrage ou arrêt, Nginx lance ou coupe des dizaines de sous-processus ("workers"), ce qui génère instantanément une cascade de lignes pour chaque action.
|
||||||
|
|
||||||
|
docker inspect web-test
|
||||||
|
# On voit que ça renvoie un JSON avec l'état actuel (le conteneur est éteint "Status": "exited", "Running": false). Le code d'erreur "ExitCode": 137 indique qu'il a été arrêté brutalement avec le kill que j'ai utilisé. Il est basé sur l'image "nginx", est configuré pour exposer le port "80/tcp".
|
||||||
|
|
||||||
|
# 4. Nettoyer l’environnement (suppression des conteneurs)
|
||||||
|
docker stop web-test # Il n'apparait donc plus lorsque l'on fait docker ps
|
||||||
|
```
|
||||||
|
|
||||||
|
# 7 Travaux Pratiques Supplémentaires
|
||||||
|
## 7.1 Rapatrier l'image officielle du serveur web apache (httpd)
|
||||||
|
```shell
|
||||||
|
docker pull httpd:alpine3.17
|
||||||
|
# Digest: sha256:03c154f29d68648c335a19c8bfca1562251bc8af534e10c6f87361551c381d21
|
||||||
|
# Status: Downloaded newer image for httpd:alpine3.17
|
||||||
|
# docker.io/library/httpd:alpine3.17
|
||||||
|
```
|
||||||
|
|
||||||
|
## 7.2 Créer un volume
|
||||||
|
```shell
|
||||||
|
docker volume create volume_serveur_web #on crée le volume
|
||||||
|
|
||||||
|
docker run --rm -v volume_serveur_web:/data alpine sh -c 'echo "<!DOCTYPE html><html><title>C MOI</title><body><h1>Emmanuel</h1></br><p>SRIVASTAVA-TIAMZON</p></body></html>" >> /data/index.html' # On crée le volume qui contient le fichier html demandé.
|
||||||
|
```
|
||||||
|
|
||||||
|
## 7.3 Démarrer un conteneur
|
||||||
|
```shell
|
||||||
|
docker run -d --name tp21 httpd # Démarrer un conteneur en mode détaché nommé tp21
|
||||||
|
|
||||||
|
docker run -d --name tp22 -v volume_serveur_web:/usr/local/apache2/htdocs/ httpd:alpine3.17 # Démarrer un conteneur en mode détaché en montant le volume précédemment créé nommé tp22
|
||||||
|
|
||||||
|
docker run -d --name tp23 -v volume_serveur_web:/usr/local/apache2/htdocs/ -p 8080:80 httpd:alpine3.17 # Démarrer un conteneur en mode détaché en montant le volume précédemment créé et en exposant le port 80 nommé tp23
|
||||||
|
```
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# 8 Questions de compréhension
|
||||||
|
|
||||||
|
1. Quelle est la différence entre une image et un conteneur ?
|
||||||
|
Un conteneur est un environnement (Linux, web, etc...) qui peut être exécuté, et qui peut exécuter des commandes.
|
||||||
|
Alors qu'une image est un modèle en lecture seule qui contient des instructions pour créer un conteneur.
|
||||||
|
On peut prendre des images donc pour créer/modifier un conteneur alors qu'un conteneur est la base d'une image.
|
||||||
|
|
||||||
|
2. La différence entre un docker stop et un docker kill :
|
||||||
|
Le 'docker stop' arrête le conteneur qui est en cours, mais proprement, donc sans répercussion car on ne peut faire de docker stop que dans des cas précis.
|
||||||
|
Alors qu'un 'docker kill' passe au dessus des cas et des règlementations. Il arrête directement le conteneur mais le fait mal. En vérifiant le log du conteneur suite à un docker kill. On peut voir comment il a mal arrêté les processus et en renvoyant des erreurs.
|
||||||
|
|
||||||
|
3. On utilise un mode détaché car lorsque l'on met un conteneur snas mode détaché, il dépend de la machine et donc quand il s'arrête le conteneur s'arrête aussi. Les données enregistrées se perdent donc à la suite. Mettre un conteneur en mode détaché le rend pas dépendant de la machine.
|
||||||
|
|
||||||
|
4. Pour vérifier la santé d'un conteneur il faut
|
||||||
|
Il faut faire docker inspect [container-name] et vérifier le State puis le status pour voir s'il fonctionne et donc qu'il est en bonne santé.
|
||||||
|
|
||||||
|
5. Les différents états d'un conteneur sont :
|
||||||
|
NEW : The task was initialized.
|
||||||
|
PENDING : Resources for the task were allocated.
|
||||||
|
ASSIGNED : Docker assigned the task to nodes.
|
||||||
|
ACCEPTED : The task was accepted by a worker node. If a worker node rejects the task, the state changes to REJECTED.
|
||||||
|
READY : The worker node is ready to start the task
|
||||||
|
PREPARING : Docker is preparing the task.
|
||||||
|
STARTING : Docker is starting the task.
|
||||||
|
RUNNING : The task is executing.
|
||||||
|
COMPLETE : The task exited without an error code.
|
||||||
|
FAILED : The task exited with an error code.
|
||||||
|
SHUTDOWN : Docker requested the task to shut down.
|
||||||
|
REJECTED : The worker node rejected the task.
|
||||||
|
ORPHANED : The node was down for too long.
|
||||||
|
REMOVE : The task is not terminal but the associated service was removed or scaled down.
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
# TP2 : Volumes et bind-mounts
|
||||||
|
|
||||||
|
## Exercice 1 : Docker volumes
|
||||||
|
|
||||||
|
### Rapatrier l’image officielle du serveur web apache (httpd)
|
||||||
|
```shell
|
||||||
|
[srivasta@salle234-09 TP2]$ docker pull httpd:alpine3.17
|
||||||
|
alpine3.17: Pulling from library/httpd
|
||||||
|
f56be85fc22e: Pull complete
|
||||||
|
3b0762594298: Pull complete
|
||||||
|
d57f06c5a70c: Pull complete
|
||||||
|
add4d5bc8a71: Pull complete
|
||||||
|
ae430e2a98db: Pull complete
|
||||||
|
36d8ddf7fbf3: Pull complete
|
||||||
|
Digest: sha256:03c154f29d68648c335a19c8bfca1562251bc8af534e10c6f87361551c381d21
|
||||||
|
Status: Downloaded newer image for httpd:alpine3.17
|
||||||
|
docker.io/library/httpd:alpine3.17
|
||||||
|
```
|
||||||
|
|
||||||
|
### Créer un volume
|
||||||
|
```shell
|
||||||
|
docker volume create volume_serveur_web #on crée le volume
|
||||||
|
docker run --rm -v volume_serveur_web:/data alpine sh -c 'echo "<!DOCTYPE html><html><title>C MOI</title><body><h1>Emmanuel</h1></br><p>SRIVASTAVA-TIAMZON</p></body></html>" >> /data/index.html' # On crée le volume qui contient le fichier html demandé.
|
||||||
|
|
||||||
|
docker run -d --name volume_serveur_web_stat -v volume_serveur_web:/usr/local/apache2/htdocs/ -p 6767:80 httpd # On crée le volume temporaire qui prend le volume web et on crée le lien qui va avec en respectant les normes
|
||||||
|
firefox http://localhost:6767/ # On consulte le fichier index.html
|
||||||
|
```
|
||||||
|

|
||||||
|
|
||||||
|
### Démarrer un conteneur
|
||||||
|
```shell
|
||||||
|
docker run -d --name tp21 httpd # Démarrer un conteneur en mode détaché nommé tp21
|
||||||
|
# 933f36f8b9bb2ebac8e1a909d2174023445d6e38abfacd41cb45effdb4453450
|
||||||
|
|
||||||
|
docker run -d --name tp22 -v volume_serveur_web:/usr/local/apache2/htdocs/ httpd:alpine3.17 # Démarrer un conteneur en mode détaché en montant le volume précédemment créé nommé tp22
|
||||||
|
# 28fa428591cbb76279093082f554753b54d0e7c64491d5d1faceb41c5c9ef598
|
||||||
|
|
||||||
|
docker run -d --name tp23 -v volume_serveur_web:/usr/local/apache2/htdocs/ -p 8080:80 httpd:alpine3.17 # Démarrer un conteneur en mode détaché en montant le volume précédemment créé et en exposant le port 80 nommé tp23
|
||||||
|
# 247b80255b2ac69bdb85f76c4c8b8d2423cacd41a5ebdd8e10c791602ce10841
|
||||||
|
```
|
||||||
|
|
||||||
|
### Exercice complémentaire
|
||||||
|
Créer un deuxième volume nommé backup_web Copier le contenu du premier volume vers ce nouveau volume Démarrer un conteneur tp24 qui monte les deux volumes simultanément.
|
||||||
|
```shell
|
||||||
|
docker volume create backup_web # backup_web
|
||||||
|
docker run -d --name tp24 -v volume_serveur_web:/usr/local/apache2/htdocs/ -v backup_web:/backup httpd:alpine3.17
|
||||||
|
# 0622eae7a25de9a2d11456e58f48091eda488d3ca7bce813ee872a2ed4adaaf0
|
||||||
|
```
|
||||||
|
|
||||||
|
## Exercice 2 : Docker bind mounts
|
||||||
|
Pour cet exercice l'arborescence [web](./web) a été crée avec un [index.html](./web/index.html), un [style.jss](./web/style.css) et un [script.js](./web/script.js).
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# Démarrer un conteneur en mode détaché nommé tp25 qui monte le dossier web local dans /usr/local/apache2/htdocs
|
||||||
|
docker run -d --name tp25 -v "$PWD"/web/:/usr/local/apache2/htdocs httpd:alpine3.17
|
||||||
|
# 071b845b325823e3720ab0ec89e42f429d1c9dac928ea342fa425b911603f0cb
|
||||||
|
|
||||||
|
# Démarrer un conteneur en mode détaché nommé tp26 qui monte le dossier web et expose le port 8080
|
||||||
|
docker run -d --name tp26 -v "$PWD"/web/:/usr/local/apache2/htdocs/web/ -p 8080:80 httpd:alpine3.17
|
||||||
|
# 0fe9b31d025df32c42dea857403952ce988fbc7ac858430e64fb837e8ab2fce4
|
||||||
|
|
||||||
|
# Démarrer un conteneur en mode détaché nommé tp27 qui monte uniquement le fichier index.html (et non tout le dossier)
|
||||||
|
docker run -d --name tp27 -v "$PWD"/web/index.html/:/usr/local/apache2/htdocs/index.html httpd:alpine3.17
|
||||||
|
# 7b0bad50feff61a4d6dd37c17b7d0f0617ec1837bce88583d0794b3c70414064
|
||||||
|
```
|
||||||
|
|
||||||
|
## Exercice complémentaire
|
||||||
|
```shell
|
||||||
|
# 1. Modifier le fichier index.html directement sur votre machine
|
||||||
|
nano/vim web/index.html # pour apporter une modif
|
||||||
|
|
||||||
|
# 2. Vérifier que les changements sont visibles dans le conteneur
|
||||||
|
docker exec tp26 cat /usr/local/apache2/htdocs/index.html
|
||||||
|
|
||||||
|
# 3. Créer un nouveau fichier about.html dans le dossier web
|
||||||
|
touch /web/about.html
|
||||||
|
|
||||||
|
# 4. Vérifier qu'il est automatiquement accessible dans le conteneur
|
||||||
|
docker exec tp26 ls /web/
|
||||||
|
```
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# Questions de compréhension
|
||||||
|
|
||||||
|
1. Quelle est la différence entre un bind mount et un volume Docker ?
|
||||||
|
Un bind mount utilise les dossiers de la machine host, tandis qu'un volume est un espace de stockage entièrement géré par Docker.
|
||||||
|
|
||||||
|
2. Où sont stockés physiquement les volumes Docker sur votre machine ?
|
||||||
|
Les volumes Docker sont physiquement stockés dans le répertoire protégé /var/lib/docker/volumes/ (sous Windows et Mac, ils sont cachés dans la machine virtuelle gérée par Docker Desktop).
|
||||||
|
|
||||||
|
3. Comment lister les volumes existants ?
|
||||||
|
Pour lister les volumes existants, utilisez la commande suivante : docker volume ls
|
||||||
|
|
||||||
|
4. Comment inspecter le contenu d'un volume ?
|
||||||
|
On peut inspecter le contenu d'un volume simplement avec : docker volume inspect nom_du_volume
|
||||||
|
Mais pour voir les fichiers à l'intérieur du volume : docker run --rm -v nom_du_volume:/donnees alpine ls -l /donnees
|
||||||
|
|
||||||
|
5. Que se passe-t-il pour les données d'un volume quand on supprime le conteneur associé ?
|
||||||
|
Un volume est indépendant du cycle de vie du conteneur. Même si on stop ou rm le conteneur, le volume et toutes ses données restent intacts sur la machine jusqu'à ce qu'on décide de supprimer avec la commande docker volume rm nom_du_volume.
|
||||||
|
|
||||||
|
# Suite questions de compréhension
|
||||||
|
|
||||||
|
6. Quand utiliser un bind mount plutôt qu'un volume Docker ?
|
||||||
|
On utilise un bind mount pour le développement local afin de voir les modifications de code en temps réel, il faut aussi priviligier un volume pour sauvegarder les données de manière sécurisée en production.
|
||||||
|
|
||||||
|
7. Peut-on monter plusieurs dossiers dans un même conteneur ?
|
||||||
|
Oui, en ajoutant simplement plusieurs options -v dans la commande de démarrage.
|
||||||
|
|
||||||
|
8. Quelle est la différence entre monter un dossier et monter un fichier unique ?
|
||||||
|
Monter un dossier fusionne ou remplace tout le contenu du répertoire cible dans le conteneur, alors que monter un fichier unique ne remplace que ce fichier précis en laissant le reste du dossier intact.
|
||||||
|
|
||||||
|
9. Les modifications sont-elles bidirectionnelles avec un bind mount ?
|
||||||
|
Oui, un changement fait sur la machine se répercute instantanément dans le conteneur, et inversement
|
||||||
|
|
||||||
|
10. Comment vérifier les bind mounts actifs sur un conteneur ?
|
||||||
|
Avec docker inspect nom_du_conteneur il faut examiner la section "Mounts" de la réponse pour lister toutes les liaisons en cours
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 72 KiB |
@@ -0,0 +1,10 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="fr">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Moi</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Emmanuel SRIVASTAVA-TIAMZON</h1>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="fr">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Moi</title>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Emmanuel SRIVASTAVA-TIAMZON</h1>
|
||||||
|
<p>Petite modification ! (exo complémentaire)</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
alert("Bienvenue");
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
h1 {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
# TP3 : Docker Networks
|
||||||
|
|
||||||
|
1. Création des réseaux
|
||||||
|
```shell
|
||||||
|
docker network create --driver bridge bridge-tp3 # Création d'un réseau de type bridge.
|
||||||
|
# c82958e2f11e582accf806f4fe24c35dd652f834e35ce9c975c8ab41057f9eb2
|
||||||
|
|
||||||
|
docker network create --driver host host-tp3
|
||||||
|
docker network create --drive none none-tp3
|
||||||
|
# Error response from daemon: only one instance of "host" network is allowed : CAR il y a déjà un network host ainsi que none, alors que les dockers network peuvent être crée à volonté.
|
||||||
|
|
||||||
|
# En vérifiant avec la commande ci-dessous, on peut vérifier les réseaux dockers déjà instanciés
|
||||||
|
docker network ls
|
||||||
|
[srivasta@salle234-09 exo1-networks]$ dar network ls
|
||||||
|
NETWORK ID NAME DRIVER SCOPE
|
||||||
|
ad5c5a581e67 bridge bridge local
|
||||||
|
c82958e2f11e bridge-tp3 bridge local
|
||||||
|
55ace904f2d5 host host local
|
||||||
|
59c5b3091f5b none null local
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Création des conteneurs
|
||||||
|
```shell
|
||||||
|
# De notre côté on crée des dossiers web1,2,3 et met un index.html dans chacun d'entre eux.
|
||||||
|
# Ensuite on lances les conteneurs web1 et web2 en montant le fichier index.html à l'emplacement demandé.
|
||||||
|
docker run -d --name web1 --network bridge-tp3 -v "$PWD"/web1/index.html:/usr/share/nginx/html/index.html nginx:alpine
|
||||||
|
# docker run -d --name web1 --network bridge-tp3 -v "$PWD"/web1/index.html:/usr/share/nginx/html/index.html nginx:alpine web1 sur bridge-tp3
|
||||||
|
|
||||||
|
docker run -d --name web2 --network bridge-tp3 -v "$PWD"/web2/index.html:/usr/share/nginx/html/index.html nginx:alpine
|
||||||
|
# 9dd106f74be931c67d7a29efc25eb4cbcc64da8826773a9100cd244220d94d83 web2 sur bridge-tp3
|
||||||
|
|
||||||
|
docker run -d --name web3 --network host -v "$PWD"/web3/index.html:/usr/share/nginx/html/index.html nginx:alpine
|
||||||
|
# 4fd0e8ba56605f04722e99ffe34d70329dbd69bcf455b80bf00f087e6ee8f523
|
||||||
|
|
||||||
|
docker run -d --name web4 --network none -v "$PWD"/web4/index.html:/usr/share/nginx/html/index.html nginx:alpine
|
||||||
|
# 5bc61fee7ca46a1bba6a3738611d2f53ae3e19cfe8af0b386ce2dff8efab13a4
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Tests de communication
|
||||||
|
```shell
|
||||||
|
# Test ping entre web1 et web2
|
||||||
|
[srivasta@salle234-09 exo1-networks]$ docker exec web1 ping web2
|
||||||
|
PING web2 (172.18.0.3): 56 data bytes
|
||||||
|
64 bytes from 172.18.0.3: seq=0 ttl=64 time=0.052 ms
|
||||||
|
64 bytes from 172.18.0.3: seq=1 ttl=64 time=0.038 ms
|
||||||
|
^C
|
||||||
|
|
||||||
|
# Test ping entre web1 et web3
|
||||||
|
[srivasta@salle234-09 exo1-networks]$ dar exec web1 ping 172.17.0.1
|
||||||
|
PING 172.17.0.1 (172.17.0.1): 56 data bytes
|
||||||
|
64 bytes from 172.17.0.1: seq=0 ttl=64 time=0.053 ms
|
||||||
|
64 bytes from 172.17.0.1: seq=1 ttl=64 time=0.179 ms
|
||||||
|
^C
|
||||||
|
|
||||||
|
#Test ping entre web1 et web4 Ping impossible car inatteignable
|
||||||
|
[srivasta@salle234-09 exo1-networks]$ dar exec web1 ping web4
|
||||||
|
ping: bad address 'web4'
|
||||||
|
```
|
||||||
|
* L'adresse du conteneur web1 : http://172.18.0.2/ En allant sur firefox, la fenêtre s'affiche bien
|
||||||
|
* web2 : http://172.18.0.3/
|
||||||
|
* web3 : http://172.17.0.1/
|
||||||
|
* web4 n'a pas d'adresse ip qui lui est propre car c'est un none.
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
## 1. Création des réseaux
|
||||||
|
* bridge-tp3 : La création a réussi. Docker permet de créer plusieurs réseaux de type "bridge" personnalisés pour isoler des groupes de conteneurs.
|
||||||
|
* host-tp3 & none-tp3 : La création a échoué car Docker n'autorise qu'une seule instance des drivers "host" et "none". Ces réseaux existent déjà par défaut lors de l'installation de Docker.
|
||||||
|
|
||||||
|
## 2. Tests de communication
|
||||||
|
* web1 vers web2 : Réussi.
|
||||||
|
* Sur un réseau bridge personnalisé, Docker active un service DNS interne. Il permet aux conteneurs de communiquer entre eux en utilisant leurs noms au lieu de leurs adresses IP ce qui nous facilite la tâche.
|
||||||
|
* web1 vers web3 : Réussi (via l'IP 172.17.0.1).
|
||||||
|
* Le conteneur web3 utilise le réseau host. On peut donc le joindre avec l'adresse IP de la passerelle (gateway) qui relie le bridge de l'hôte au système.
|
||||||
|
* web1 vers web4 : Échec ("bad address").
|
||||||
|
* Le conteneur web4 est configuré avec le driver "none". Il ne possède aucune interface réseau externe ni adresse IP, ce qui le rend totalement isolé et inatteignable par le réseau comme dit dans le cours.
|
||||||
|
|
||||||
|
## 3. Accès HTTP et vérification du contenu
|
||||||
|
* web1 (172.18.0.2) & web2 (172.18.0.3) : Les pages sont accessibles avec le navigateur. Le montage de volume (`-v`) a correctement remplacé le fichier par défaut de Nginx par notre `index.html`.
|
||||||
|
* web3 (172.17.0.1) : La page s'affiche correctement. En mode host, le serveur Nginx écoute directement sur les interfaces réseau de la machine hôte.
|
||||||
|
* web4 : Aucun accès possible. Comme il n'a pas d'adresse IP, aucun flux HTTP ne peut aller vers ce conteneur.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 57 KiB |
@@ -0,0 +1,61 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="fr">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Container Info</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
background-color: #f0f2f5;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px;
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
color: #2496ed;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
color: #384c54;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>Container: web1</h1>
|
||||||
|
<h2>Date et heure: <span id="datetime"></span></h2>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
function updateDateTime() {
|
||||||
|
const now = new Date();
|
||||||
|
const options = {
|
||||||
|
weekday: 'long',
|
||||||
|
year: 'numeric',
|
||||||
|
month: 'long',
|
||||||
|
day: 'numeric',
|
||||||
|
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
second: '2-digit',
|
||||||
|
hour12: false
|
||||||
|
};
|
||||||
|
document.getElementById('datetime').textContent =
|
||||||
|
now.toLocaleDateString('fr-FR', options);
|
||||||
|
}
|
||||||
|
// Mise à jour initiale
|
||||||
|
updateDateTime();
|
||||||
|
// Mise à jour toutes les secondes
|
||||||
|
setInterval(updateDateTime, 1000);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="fr">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Container Info</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
background-color: #f0f2f5;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px;
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
color: #2496ed;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
color: #384c54;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>Container: web2</h1>
|
||||||
|
<h2>Date et heure: <span id="datetime"></span></h2>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
function updateDateTime() {
|
||||||
|
const now = new Date();
|
||||||
|
const options = {
|
||||||
|
weekday: 'long',
|
||||||
|
year: 'numeric',
|
||||||
|
month: 'long',
|
||||||
|
day: 'numeric',
|
||||||
|
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
second: '2-digit',
|
||||||
|
hour12: false
|
||||||
|
};
|
||||||
|
document.getElementById('datetime').textContent =
|
||||||
|
now.toLocaleDateString('fr-FR', options);
|
||||||
|
}
|
||||||
|
// Mise à jour initiale
|
||||||
|
updateDateTime();
|
||||||
|
// Mise à jour toutes les secondes
|
||||||
|
setInterval(updateDateTime, 1000);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="fr">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Container Info</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
background-color: #f0f2f5;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px;
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
color: #2496ed;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
color: #384c54;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>Container: web3</h1>
|
||||||
|
<h2>Date et heure: <span id="datetime"></span></h2>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
function updateDateTime() {
|
||||||
|
const now = new Date();
|
||||||
|
const options = {
|
||||||
|
weekday: 'long',
|
||||||
|
year: 'numeric',
|
||||||
|
month: 'long',
|
||||||
|
day: 'numeric',
|
||||||
|
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
second: '2-digit',
|
||||||
|
hour12: false
|
||||||
|
};
|
||||||
|
document.getElementById('datetime').textContent =
|
||||||
|
now.toLocaleDateString('fr-FR', options);
|
||||||
|
}
|
||||||
|
// Mise à jour initiale
|
||||||
|
updateDateTime();
|
||||||
|
// Mise à jour toutes les secondes
|
||||||
|
setInterval(updateDateTime, 1000);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="fr">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Container Info</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
background-color: #f0f2f5;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px;
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
color: #2496ed;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
color: #384c54;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>Container: web4</h1>
|
||||||
|
<h2>Date et heure: <span id="datetime"></span></h2>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
function updateDateTime() {
|
||||||
|
const now = new Date();
|
||||||
|
const options = {
|
||||||
|
weekday: 'long',
|
||||||
|
year: 'numeric',
|
||||||
|
month: 'long',
|
||||||
|
day: 'numeric',
|
||||||
|
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
second: '2-digit',
|
||||||
|
hour12: false
|
||||||
|
};
|
||||||
|
document.getElementById('datetime').textContent =
|
||||||
|
now.toLocaleDateString('fr-FR', options);
|
||||||
|
}
|
||||||
|
// Mise à jour initiale
|
||||||
|
updateDateTime();
|
||||||
|
// Mise à jour toutes les secondes
|
||||||
|
setInterval(updateDateTime, 1000);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# TP 3 : Dockerfile
|
||||||
|
|
||||||
|
## Actions à réaliser
|
||||||
|
### 1. Création du Dockerfile
|
||||||
|
```Dockerfile
|
||||||
|
FROM alpine:3.23 AS DF
|
||||||
|
RUN apt install apache2
|
||||||
|
LABEL maintainer="srivastava" version="1.8" TP="3"
|
||||||
|
```
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
FROM alpine:3.23
|
||||||
|
|
||||||
|
RUN apk add apache2
|
||||||
|
|
||||||
|
LABEL MAINTAINER="srivastava" VERSION="1.8" TP="3"
|
||||||
|
# Ajout de métadonnées pour le mainteneur, la version et le numéro du TP
|
||||||
|
|
||||||
|
ENV SCHOOL="IUT" LEVEL="1.8"
|
||||||
|
# Définition de variables d environnement pour l école et le niveau
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
|
# Exposition du port 80 pour le serveur web
|
||||||
|
|
||||||
|
RUN apk add git && git clone https://github.com/MaximePIERRONT/beforeStage.git /tmp/repo && cp /tmp/repo/static-site.html /var/www/localhost/htdocs/static-site.html && rm -rf /tmp/repo && apk del git
|
||||||
|
# Installation de git, clonage du dépôt, copie du fichier HTML dans le répertoire de l hôte web, suppression du dépôt et désinstallation de git
|
||||||
|
|
||||||
|
RUN sed -i 's/NOM PRENOM/SRIVASTAVA Emmanuel/g' /var/www/localhost/htdocs/static-site.html
|
||||||
|
# Modification du fichier HTML pour remplacer "NOM PRENOM" par "SRIVASTAVA Emmanuel"
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=1m --timeout=1s CMD wget --no-verbose --tries=1 --spider http://localhost/ || exit 1
|
||||||
|
|
||||||
|
CMD ["/usr/sbin/httpd", "-D", "FOREGROUND"]
|
||||||
|
# Commande pour démarrer le serveur Apache en mode premier plan
|
||||||
Binary file not shown.
@@ -0,0 +1,59 @@
|
|||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
|
||||||
|
const HealthCheck = () => {
|
||||||
|
const [status, setStatus] = useState<'loading' | 'online' | 'offline'>('loading');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const checkHealth = async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/health');
|
||||||
|
const data = await response.json();
|
||||||
|
setStatus(data.status === 'ok' ? 'online' : 'offline');
|
||||||
|
} catch (error) {
|
||||||
|
setStatus('offline');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
checkHealth();
|
||||||
|
|
||||||
|
const interval = setInterval(checkHealth, 30000);
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const getStatusStyles = () => {
|
||||||
|
switch (status) {
|
||||||
|
case 'online':
|
||||||
|
return { backgroundColor: '#deffde', padding: '1rem', borderRadius: '4px' };
|
||||||
|
case 'offline':
|
||||||
|
return { backgroundColor: '#ffeded', padding: '1rem', borderRadius: '4px' };
|
||||||
|
default:
|
||||||
|
return { backgroundColor: '#f3f3f3', padding: '1rem', borderRadius: '4px' };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStatusMessage = () => {
|
||||||
|
switch (status) {
|
||||||
|
case 'online':
|
||||||
|
return '✅ Le backend est accessible et fonctionne correctement';
|
||||||
|
case 'offline':
|
||||||
|
return '❌ Le backend n\'est pas accessible';
|
||||||
|
default:
|
||||||
|
return '⏳ Vérification de la connexion avec le backend...';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ padding: '1rem' }}>
|
||||||
|
<div style={getStatusStyles()}>
|
||||||
|
{status === 'loading' && (
|
||||||
|
<span style={{ display: 'inline-block', animation: 'spin 1s linear infinite' }}>🔄</span>
|
||||||
|
)}
|
||||||
|
<span style={{ marginLeft: status === 'loading' ? '8px' : '0' }}>
|
||||||
|
{getStatusMessage()}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default HealthCheck;
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
FROM python:3.9-slim
|
||||||
|
WORKDIR /app
|
||||||
|
COPY requirements.txt .
|
||||||
|
RUN pip install -r requirements.txt
|
||||||
|
COPY . .
|
||||||
|
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
from fastapi import FastAPI
|
||||||
|
from pymongo import MongoClient
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
# MongoDB connection
|
||||||
|
client = MongoClient("mongodb://mongodb:27017/")
|
||||||
|
db = client.memes_db
|
||||||
|
|
||||||
|
@app.get("/memes")
|
||||||
|
def get_memes():
|
||||||
|
memes = list(db.memes.find({}, {"_id": 0}))
|
||||||
|
return {"memes": memes}
|
||||||
|
|
||||||
|
@app.post("/memes")
|
||||||
|
def create_meme(title: str, url: str):
|
||||||
|
meme = {"title": title, "url": url}
|
||||||
|
db.memes.insert_one(meme)
|
||||||
|
return {"message": "Meme added successfully"}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
fastapi==0.68.0
|
||||||
|
uvicorn[standard]==0.15.0
|
||||||
|
python-multipart
|
||||||
|
pymongo==3.12.0
|
||||||
|
websockets==10.0
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
version: '3.8'
|
||||||
|
services:
|
||||||
|
mongodb:
|
||||||
|
image: mongodb/mongodb-atlas-local
|
||||||
|
volumes:
|
||||||
|
- mongodb_data:/data/db
|
||||||
|
networks:
|
||||||
|
- backend-network
|
||||||
|
restart: always
|
||||||
|
|
||||||
|
backend:
|
||||||
|
build: ./backend
|
||||||
|
depends_on:
|
||||||
|
- mongodb
|
||||||
|
networks:
|
||||||
|
- backend-network
|
||||||
|
- frontend-network
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
build: ./frontend
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
networks:
|
||||||
|
- frontend-network
|
||||||
|
depends_on:
|
||||||
|
- backend
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
mongodb_data:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
backend-network:
|
||||||
|
internal: true
|
||||||
|
frontend-network:
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
FROM nginx:alpine
|
||||||
|
COPY index.html /usr/share/nginx/html/index.html
|
||||||
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Meme Gallery</title>
|
||||||
|
<style>
|
||||||
|
.meme { margin: 10px; padding: 10px; border: 1px solid #ccc; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Meme Gallery</h1>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h2>Add a Meme</h2>
|
||||||
|
<input type="text" id="title" placeholder="Title">
|
||||||
|
<input type="text" id="url" placeholder="Image URL">
|
||||||
|
<button onclick="addMeme()">Add Meme</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="memes"></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
async function loadMemes() {
|
||||||
|
const response = await fetch('/backend/memes');
|
||||||
|
const data = await response.json();
|
||||||
|
const memesDiv = document.getElementById('memes');
|
||||||
|
memesDiv.innerHTML = data.memes.map(meme => `
|
||||||
|
<div class="meme">
|
||||||
|
<h3>${meme.title}</h3>
|
||||||
|
<img src="${meme.url}" width="200">
|
||||||
|
</div>
|
||||||
|
`).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addMeme() {
|
||||||
|
const title = document.getElementById('title').value;
|
||||||
|
const url = document.getElementById('url').value;
|
||||||
|
|
||||||
|
await fetch('/backend/memes?' + new URLSearchParams({
|
||||||
|
title: title,
|
||||||
|
url: url
|
||||||
|
}), {method: 'POST'});
|
||||||
|
|
||||||
|
loadMemes();
|
||||||
|
}
|
||||||
|
|
||||||
|
loadMemes();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name localhost;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /backend/ {
|
||||||
|
proxy_pass http://backend:8000/;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection 'upgrade';
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_cache_bypass $http_upgrade;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
# Docker Compose - Aide-mémoire
|
||||||
|
|
||||||
|
## Commandes Essentielles
|
||||||
|
|
||||||
|
### Démarrage et Arrêt
|
||||||
|
```bash
|
||||||
|
# Démarrer les services
|
||||||
|
docker compose up -d # Mode détaché (arrière-plan)
|
||||||
|
docker compose up # Mode interactif (voir les logs)
|
||||||
|
|
||||||
|
# Arrêter les services
|
||||||
|
docker compose down # Arrête et supprime les conteneurs
|
||||||
|
docker compose stop # Arrête sans supprimer
|
||||||
|
```
|
||||||
|
|
||||||
|
### Logs et Surveillance
|
||||||
|
```bash
|
||||||
|
# Voir les logs
|
||||||
|
docker compose logs # Tous les logs
|
||||||
|
docker compose logs -f # Suivre les logs en direct
|
||||||
|
docker compose logs [service] # Logs d'un service spécifique
|
||||||
|
|
||||||
|
# État des conteneurs
|
||||||
|
docker compose ps # Liste et état des conteneurs
|
||||||
|
```
|
||||||
|
|
||||||
|
### Construction et Reconstruction
|
||||||
|
```bash
|
||||||
|
# Construire les images
|
||||||
|
docker compose build # Tous les services
|
||||||
|
docker compose build [service] # Un service spécifique
|
||||||
|
|
||||||
|
# Reconstruction et démarrage
|
||||||
|
docker compose up -d --build # Reconstruire puis démarrer
|
||||||
|
```
|
||||||
|
|
||||||
|
## Structure docker-compose.yml
|
||||||
|
|
||||||
|
### Services
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
monservice:
|
||||||
|
image: ... # Utiliser une image existante
|
||||||
|
build: ... # Construire depuis un Dockerfile
|
||||||
|
ports: # Exposer des ports
|
||||||
|
volumes: # Monter des volumes
|
||||||
|
depends_on: # Définir des dépendances
|
||||||
|
networks: # Connecter aux réseaux
|
||||||
|
```
|
||||||
|
|
||||||
|
### Réseaux
|
||||||
|
```yaml
|
||||||
|
networks:
|
||||||
|
monreseau: # Réseau simple
|
||||||
|
monreseau_interne:
|
||||||
|
internal: true # Réseau isolé
|
||||||
|
```
|
||||||
|
|
||||||
|
### Volumes
|
||||||
|
```yaml
|
||||||
|
volumes:
|
||||||
|
monvolume: # Volume pour persistance
|
||||||
|
```
|
||||||
|
|
||||||
|
## Commandes Utiles
|
||||||
|
|
||||||
|
### Inspection
|
||||||
|
```bash
|
||||||
|
# Vérifier la configuration
|
||||||
|
docker compose config # Valider le fichier compose
|
||||||
|
|
||||||
|
# Voir les réseaux
|
||||||
|
docker network ls # Lister les réseaux
|
||||||
|
docker network inspect [réseau] # Détails d'un réseau
|
||||||
|
|
||||||
|
# Voir les volumes
|
||||||
|
docker volume ls # Lister les volumes
|
||||||
|
```
|
||||||
|
|
||||||
|
### Debug
|
||||||
|
```bash
|
||||||
|
# Accéder à un conteneur
|
||||||
|
docker compose exec [service] sh # Ouvrir un shell
|
||||||
|
|
||||||
|
# Voir les processus
|
||||||
|
docker compose top # Processus en cours
|
||||||
|
```
|
||||||
|
|
||||||
|
### Gestion des Services
|
||||||
|
```bash
|
||||||
|
# Redémarrer des services
|
||||||
|
docker compose restart # Tous les services
|
||||||
|
docker compose restart [service] # Un service spécifique
|
||||||
|
|
||||||
|
# Recréer des conteneurs
|
||||||
|
docker compose up -d --force-recreate
|
||||||
|
```
|
||||||
|
|
||||||
|
## Conseils
|
||||||
|
|
||||||
|
- Utilisez toujours `-d` en production, jamais en développement
|
||||||
|
- Pensez à vérifier les logs en cas de problème
|
||||||
|
- `docker compose config` est votre ami pour valider la syntaxe
|
||||||
|
- Les réseaux se créent automatiquement
|
||||||
|
- Les volumes persistent après un `down` (sauf si `-v`)
|
||||||
@@ -0,0 +1,205 @@
|
|||||||
|
# TP4 Docker Compose - Application de Mèmes
|
||||||
|
### Objectif
|
||||||
|
Créer une application web de partage de mèmes en utilisant Docker Compose pour orchestrer 3 conteneurs :
|
||||||
|
- Un frontend (nginx)
|
||||||
|
- Un backend (Python/FastAPI)
|
||||||
|
- Une base de données (MongoDB)
|
||||||
|
|
||||||
|
### Prérequis
|
||||||
|
- Docker et Docker Compose installés
|
||||||
|
- Un éditeur de code (VS Code recommandé)
|
||||||
|
|
||||||
|
### Structure du projet
|
||||||
|
```
|
||||||
|
meme-app/
|
||||||
|
├── backend/
|
||||||
|
│ ├── Dockerfile
|
||||||
|
│ ├── requirements.txt
|
||||||
|
│ └── app.py
|
||||||
|
├── frontend/
|
||||||
|
│ ├── Dockerfile
|
||||||
|
│ ├── nginx.conf
|
||||||
|
│ └── index.html
|
||||||
|
└── docker-compose.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
### Étape 1 : Mise en place de la structure (5 min)
|
||||||
|
```bash
|
||||||
|
# Créer l'arborescence
|
||||||
|
mkdir meme-app
|
||||||
|
cd meme-app
|
||||||
|
mkdir backend frontend
|
||||||
|
touch docker-compose.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
### Étape 2 : Backend (20 min)
|
||||||
|
|
||||||
|
1. Créer backend/requirements.txt :
|
||||||
|
```
|
||||||
|
fastapi==0.68.0
|
||||||
|
uvicorn[standard]==0.15.0
|
||||||
|
python-multipart
|
||||||
|
pymongo==3.12.0
|
||||||
|
websockets==10.0
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Créer backend/app.py :
|
||||||
|
```python
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from pymongo import MongoClient
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
# MongoDB connection
|
||||||
|
client = MongoClient("mongodb://mongodb:27017/")
|
||||||
|
db = client.memes_db
|
||||||
|
|
||||||
|
@app.get("/memes")
|
||||||
|
def get_memes():
|
||||||
|
memes = list(db.memes.find({}, {"_id": 0}))
|
||||||
|
return {"memes": memes}
|
||||||
|
|
||||||
|
@app.post("/memes")
|
||||||
|
def create_meme(title: str, url: str):
|
||||||
|
meme = {"title": title, "url": url}
|
||||||
|
db.memes.insert_one(meme)
|
||||||
|
return {"message": "Meme added successfully"}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Créer backend/Dockerfile :
|
||||||
|
```dockerfile
|
||||||
|
FROM python:3.9-slim
|
||||||
|
WORKDIR /app
|
||||||
|
COPY requirements.txt .
|
||||||
|
RUN pip install -r requirements.txt
|
||||||
|
COPY . .
|
||||||
|
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Étape 3 : Frontend (20 min)
|
||||||
|
|
||||||
|
1. Créer frontend/index.html :
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Meme Gallery</title>
|
||||||
|
<style>
|
||||||
|
.meme { margin: 10px; padding: 10px; border: 1px solid #ccc; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Meme Gallery</h1>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h2>Add a Meme</h2>
|
||||||
|
<input type="text" id="title" placeholder="Title">
|
||||||
|
<input type="text" id="url" placeholder="Image URL">
|
||||||
|
<button onclick="addMeme()">Add Meme</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="memes"></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
async function loadMemes() {
|
||||||
|
const response = await fetch('/backend/memes');
|
||||||
|
const data = await response.json();
|
||||||
|
const memesDiv = document.getElementById('memes');
|
||||||
|
memesDiv.innerHTML = data.memes.map(meme => `
|
||||||
|
<div class="meme">
|
||||||
|
<h3>${meme.title}</h3>
|
||||||
|
<img src="${meme.url}" width="200">
|
||||||
|
</div>
|
||||||
|
`).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addMeme() {
|
||||||
|
const title = document.getElementById('title').value;
|
||||||
|
const url = document.getElementById('url').value;
|
||||||
|
|
||||||
|
await fetch('/backend/memes?' + new URLSearchParams({
|
||||||
|
title: title,
|
||||||
|
url: url
|
||||||
|
}), {method: 'POST'});
|
||||||
|
|
||||||
|
loadMemes();
|
||||||
|
}
|
||||||
|
|
||||||
|
loadMemes();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Créer frontend/nginx.conf :
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name localhost;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /backend/ {
|
||||||
|
proxy_pass http://backend:8000/;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection 'upgrade';
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_cache_bypass $http_upgrade;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Créer frontend/Dockerfile :
|
||||||
|
```dockerfile
|
||||||
|
FROM nginx:alpine
|
||||||
|
COPY index.html /usr/share/nginx/html/index.html
|
||||||
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
### Étape 4 : Docker Compose (45 min)
|
||||||
|
|
||||||
|
Votre mission : Créer le fichier docker-compose.yml pour :
|
||||||
|
1. Démarrer MongoDB
|
||||||
|
2. Démarrer le backend Python en le connectant à MongoDB
|
||||||
|
3. Démarrer le frontend Nginx en le connectant au backend
|
||||||
|
4. Gérer les réseaux pour isoler MongoDB
|
||||||
|
5. Configurer un volume pour les données MongoDB
|
||||||
|
|
||||||
|
Points à considérer :
|
||||||
|
- Quels services doivent communiquer entre eux ?
|
||||||
|
- Quels ports doivent être exposés ?
|
||||||
|
- Comment assurer la persistance des données ?
|
||||||
|
- Dans quel ordre les services doivent-ils démarrer ?
|
||||||
|
|
||||||
|
### Étape 5 : Tests (30 min)
|
||||||
|
|
||||||
|
1. Démarrer l'application :
|
||||||
|
```bash
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Vérifier que tout fonctionne :
|
||||||
|
- Ouvrir http://localhost dans le navigateur
|
||||||
|
- Ajouter un mème (utilisez une URL d'image depuis le web)
|
||||||
|
- Vérifier qu'il s'affiche dans la galerie
|
||||||
|
|
||||||
|
3. Tester la persistance :
|
||||||
|
```bash
|
||||||
|
docker compose down
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
Les mèmes doivent toujours être là !
|
||||||
|
|
||||||
|
### Rendu attendu
|
||||||
|
- Le fichier docker-compose.yml complet et fonctionnel
|
||||||
|
- Une explication de vos choix de configuration
|
||||||
|
- Un exemple de mème ajouté via l'interface
|
||||||
|
|
||||||
|
### Pour aller plus loin
|
||||||
|
- Ajoutez des variables d'environnement pour la configuration
|
||||||
|
- Implémentez un healthcheck pour MongoDB
|
||||||
|
- Configurez un backup automatique de la base de données
|
||||||
Reference in New Issue
Block a user