Cours Docker
Ce cours regroupe les notions et les TP suivants:
- Installation de Docker sur CentOS
- Manipulation des conteneurs
- Construction d'images Docker
- Reseau Docker avec HAProxy
- Volumes (local et NFS)
- Docker Compose
- Registry prive
Les commandes sont presentees telles qu'elles apparaissent dans les supports de TP, puis completees par des explications pratiques.
Certaines operations (partition disque, modification de /etc/docker/daemon.json, restart du service Docker) impactent la machine hote. Realiser ces etapes en environnement de lab.
1. Installation Docker (CentOS)
1.1 Prerequis
- Systeme cible: CentOS 7/8 ou distribution compatible RHEL
- Acces sudo
- Acces Internet pour telecharger les paquets
1.2 Installation via package manager
sudo dnf update -y
sudo dnf -y install dnf-plugins-core
sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl enable --now docker
1.3 Ajouter un utilisateur au groupe docker
sudo usermod -aG docker sadmin
newgrp docker
1.4 Verification
docker run hello-world
Resultat attendu (extrait): message Hello from Docker!.
Si docker run hello-world echoue avec une erreur de permission, ouvrir une nouvelle session shell (ou se reconnecter) pour recharger les groupes.
2. Conteneurs
2.1 Executer un conteneur simple
docker run ubuntu:14.04 echo "hello world"
2.2 Observer PID/UID
docker run ubuntu:14.04 ps -ef
docker run --user=bin ubuntu:14.04 ps -ef
Objectif: observer l'impact de l'utilisateur (root vs bin) sur la colonne UID.
2.3 Lister les conteneurs
docker ps
docker ps -a
2.4 Acces terminal interactif
docker run -it ubuntu:14.04 bash
Dans le conteneur:
touch fic1
ls
exit
2.5 Execution en mode detache
docker run -d -it --name ping centos:8 ping 127.0.0.1 -c 60
docker ps
docker logs ping
docker attach ping
2.6 Commande exec / attach
docker run -d -it --name=centos centos bash
docker attach centos
# puis dans le conteneur
ps -ef
En pratique, preferer souvent docker exec -it <conteneur> bash pour ouvrir un shell sans attacher le process principal.
Exemple:
docker exec -it centos bash
2.7 Supprimer les conteneurs arretes
docker ps --filter='status=exited'
docker rm $(docker ps -q --filter='status=exited')
# ou
docker container prune
docker container prune supprime tous les conteneurs arretes. Verifier avant execution.
3. Images Docker
3.1 Image myhello avec script entrypoint.sh
Fichier entrypoint.sh:
#!/bin/sh
echo $* $(whoami)
Fichier Dockerfile:
FROM alpine:3.17
COPY entrypoint.sh /
CMD ["Bonjour"]
ENTRYPOINT ["/entrypoint.sh"]
Build et test:
docker build -t myhello .
docker run --rm myhello
docker run --rm myhello Salut tout-le-monde
3.2 Hello World en C
Fichier hello.c:
#include <stdio.h>
int main(void)
{
printf("Bonjour en lagage C ...\\n");
}
Version simple (build et run dans la meme image):
FROM alpine:3.17 AS first
COPY hello.c /
RUN apk add gcc libc-dev \
&& gcc -o hello hello.c
CMD ["/hello"]
Version multi-stage (image finale plus legere):
FROM alpine:3.17 AS first
COPY hello.c /
RUN apk add gcc libc-dev \
&& gcc -o hello hello.c
FROM alpine:3.17 AS second
COPY --from=first hello /
CMD ["/hello"]
La version multi-stage limite la taille finale car elle n'embarque pas les outils de compilation.
3.3 Creation d'une image de base avec gcc
Etapes:
docker run -it --name=gcc alpine:3.17
# dans le conteneur
apk add gcc libc-dev
exit
docker export gcc -o gcc.tar
Dockerfile de base:
FROM scratch
ADD gcc.tar /
CMD ["/bin/sh"]
Build et test:
docker build -t gcc:alpine .
docker run -it gcc:alpine
# puis
gcc
docker export fabrique une image a partir d'un etat de conteneur, mais on perd l'historique des couches Dockerfile. A utiliser surtout a des fins pedagogiques.
4. Reseau Docker avec HAProxy
Objectif:
- Creer 2 images
nginx1etnginx2avec pages differentes - Creer une image
myhaproxy - Connecter les conteneurs sur un reseau
web - Exposer HAProxy sur le port 80
4.1 Configuration HAProxy
Fichier haproxy.cfg:
frontend http_frontend
bind *:80
default_backend http_backend
backend http_backend
mode http
balance roundrobin
server server1 web1:80 check
server server2 web2:80 check
4.2 Images Nginx
web1/Dockerfile:
FROM nginx:1.19-alpine
COPY index.html /usr/share/nginx/html
web1/index.html:
Server ONE
web2/Dockerfile:
FROM nginx:1.19-alpine
COPY index.html /usr/share/nginx/html
web2/index.html:
Server TWO TWO
Build:
docker build -t nginx1 web1
docker build -t nginx2 web2
4.3 Image HAProxy
myhaproxy/Dockerfile:
FROM haproxy:1.7
COPY haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg
Build:
docker build -t myhaproxy myhaproxy
4.4 Reseau et execution
docker network create web
docker network ls
docker run -d --network=web --name=web1 nginx1
docker run -d --network=web --name=web2 nginx2
docker run -d --network=web --name=myhaproxy -p 80:80 myhaproxy
Verification du load balancing:
curl localhost
curl localhost
Vous devez voir alterner les reponses (Server One, puis Server Two).
Le DNS interne Docker permet a HAProxy de joindre web1 et web2 par nom de conteneur sur le meme reseau bridge utilisateur.
5. Volumes Docker
5.1 Cas HAProxy avec montages bind
Reprise de l'exercice HAProxy en utilisant des repertoires/fichiers de l'hote pour modifier rapidement le contenu.
Lancer web1 et web2:
docker run -d --network=web --name=web1 -v /home/centos/web1:/usr/share/nginx/html nginx:1.19-alpine
docker run -d --network=web --name=web2 -v /home/centos/web2:/usr/share/nginx/html nginx:1.19-alpine
Lancer myhaproxy:
docker run -d --network=web --name=myhaproxy -p 80:80 -v /home/centos/myhapoxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg haproxy:1.7
Le chemin du TP contient myhapoxy (sans le r). Si votre dossier s'appelle myhaproxy, adaptez la commande.
5.2 Disque local: partition + volume
Creation de partition sur /dev/sdb:
sudo fdisk /dev/sdb
# n
# p
# entree (valeurs par defaut)
# w
sudo mkfs.xfs /dev/sdb1
Creation du volume Docker sur ce disque:
docker volume create \
--opt type=xfs \
--opt device=/dev/sdb1 \
sdb1
Conteneur MySQL avec le volume:
docker run -it -d -e MYSQL_ROOT_PASSWORD=my-secret-p -v sdb1:/var/lib/mysql mysql:8.0
Partitionner ou formater un disque efface potentiellement des donnees. Verifier le device cible avant validation.
5.3 Volume NFS
Creation du volume NFS:
docker volume create \
--opt type=nfs \
--opt o=addr=192.168.1.5 \
--opt device=:/home/shares/user11 \
nfs_web
Variante vue dans le support:
docker volume create \
--opt type=nfs \
--opt o=addr=192.168.1.5 \
--opt device=:/home/shares/userX \
nfs_web
5.4 Producteur / Consommateur
Dockerfile producteur:
FROM alpine:3.17
COPY cmd.sh /
CMD ["/cmd.sh"]
cmd.sh:
#!/bin/sh
while true
do
echo `date` : `hostname` >> /mnt/index.html
sleep 60
done
Build et execution:
docker build -t producteur .
docker run -it -d -v nfs_web:/mnt producteur
docker run -it -d -p 80:80 -v nfs_web:/usr/share/nginx/html nginx:1.23-alpine
Le conteneur producteur ecrit dans le volume partage, et Nginx sert ce contenu immediatement.
6. Docker Compose
6.1 HAProxy + web1 + web2
Fichier docker-compose.yml:
services:
myhaproxy:
image: haproxy:1.7
volumes:
- "/home/sadmin/haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg"
ports:
- "80:80"
depends_on:
- web1
- web2
networks:
- web
web1:
image: nginx:1.19-alpine
container_name: web1
volumes:
- "/home/sadmin/web1:/usr/share/nginx/html"
networks:
- web
web2:
image: nginx:1.19-alpine
container_name: web2
volumes:
- "/home/sadmin/web2:/usr/share/nginx/html"
networks:
- web
networks:
web:
Lancement:
docker compose up -d
6.2 Producteur / Consommateur en Compose
docker-compose.yml:
services:
producteur:
build: .
volumes:
- "nfs_web:/mnt"
consommateur:
image: nginx:1.19-alpine
volumes:
- "nfs_web:/usr/share/nginx/html"
ports:
- "80:80"
volumes:
nfs_web:
driver_opts:
type: "nfs"
o: "addr=192.168.1.5"
device: ":/home/shares/user11"
Dockerfile:
FROM alpine:3.17
COPY cmd.sh /
CMD ["/cmd.sh"]
cmd.sh:
#!/bin/sh
> /mnt/index.html
while true
do
echo `date` : `hostname` >> /mnt/index.html
sleep 60
done
Lancement:
docker compose up -d
La ligne > /mnt/index.html vide le fichier au demarrage pour repartir d'un etat propre.
7. Registry prive
7.1 Demarrer un registry local
docker run -d \
-p 5000:5000 \
--restart=always \
--name registry \
-v /mnt/registry:/var/lib/registry \
registry:2
7.2 Autoriser un registry insecure
Modifier /etc/docker/daemon.json:
{
"insecure-registries": ["host111:5000"]
}
Puis redemarrer Docker:
sudo systemctl restart docker
7.3 Taguer et pousser une image
docker tag myhello host111:5000/myhello
docker push host111:5000/myhello
7.4 Lister les images/tags du registry
curl http://host111:5000/v2/_catalog
curl http://host111:5000/v2/myhello/tags/list
insecure-registries desactive TLS pour ce registre. En production, mettre en place HTTPS avec certificats valides.
8. Bonnes pratiques (complements)
8.1 Nettoyage regulier
docker system df
docker image prune
docker volume prune
8.2 Logs et debug
docker logs -f <conteneur>
docker inspect <conteneur>
docker exec -it <conteneur> sh
8.3 Securite minimale
- Eviter de lancer les applications en root si possible
- Limiter les ports exposes a l'hote
- Epingler des versions d'images (pas seulement
latest) - Scanner les images dans une CI/CD
Pour les scripts shell dans les images, preferer $(date) et $(hostname) a la syntaxe legacy avec backticks pour une meilleure lisibilite.
9. Recap des commandes essentielles
# Containers
docker run -it <image> sh
docker ps -a
docker logs <conteneur>
docker exec -it <conteneur> sh
docker rm <conteneur>
# Images
docker build -t <nom>:<tag> .
docker images
docker rmi <image>
# Reseaux
docker network ls
docker network create <nom>
# Volumes
docker volume ls
docker volume create <nom>
# Compose
docker compose up -d
docker compose down
# Registry
docker tag <image> <host:port>/<image>
docker push <host:port>/<image>
Ce cours combine des cas pedagogiques de niveau debutant/intermediaire. Pour un usage production, ajouter au minimum: supervision, sauvegardes, securisation TLS, gestion des secrets, et pipeline CI/CD.