Skip to main content

Cours Docker

Ce cours regroupe les notions et les TP suivants:

  1. Installation de Docker sur CentOS
  2. Manipulation des conteneurs
  3. Construction d'images Docker
  4. Reseau Docker avec HAProxy
  5. Volumes (local et NFS)
  6. Docker Compose
  7. Registry prive
info

Les commandes sont presentees telles qu'elles apparaissent dans les supports de TP, puis completees par des explications pratiques.

warning

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!.

tip

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
info

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
warning

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"]
tip

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
warning

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 nginx1 et nginx2 avec 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).

tip

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
warning

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
danger

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
tip

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
info

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
warning

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
tip

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>
important

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.