Docker

Docker est outil de virtualisation qui permet de déployer des applications de manière reproductible quelque soit l’OS :

# Ajouter la clé GPG de Docker
$ sudo apt update
$ sudo apt install ca-certificates curl
$ sudo install -m 0755 -d /etc/apt/keyrings
$ sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
$ sudo chmod a+r /etc/apt/keyrings/docker.asc

# Ajouter le repository de Docker
$ sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Signed-By: /etc/apt/keyrings/docker.asc
EOF

# Installer Docker CLI
$ sudo apt update
$ sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# Vérifier que Docker a bien été installé
$ sudo docker run hello-world

# Exécuter des commandes Docker sans sudo
$ sudo usermod -aG docker $USER

Attention à bien redémarrer votre session sinon vous devrez toujours utiliser sudo

Rapide tutoriel

Dans ce rapide tutoriel, on va prendre l’exemple du déploiement d’une application Flask très simple :

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello_world():
    return "<p>Hello, World!</p>"

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

On commence par créer un fichier appelé Dockerfile. Il permet de créer une image, elle contient tout ce qu’il faut pour pouvoir démarrer notre application :

FROM python:3.14-slim

WORKDIR /app
RUN pip install --no-cache-dir -r requirements.txt

COPY app.py app.py
CMD ["python", "-u", "app.py"]

Ici :

Dans la première étape, on construit notre image, chaque étape est mise en cache pour éviter de la reconstruire de zéro à chaque modification de l’application :

$ docker build -t mon-app-flask .

Enfin, une fois l’image construite, on l’exécute dans un containeur (ajouter -d pour la démarrer en fond) :

$ docker run -p 5000:5000 mon-app-flask

Les images et les containeurs sont totalement dissociés. Il faut reconstruire l’image après chaque modification puis l’exécuter dans un containeur. Similairement, par défaut, il n’y a aucune persistance des données.

Pour nettoyer toutes les images, containeurs, cache de build etc… : docker system prune -a

Avoir une persistance des données

À partir d’ici on va utiliser docker compose par dessus docker. Compose permet de plus facilement gérer plusieurs containers et de les inter-connecter.

Pour ça, on crée un fichier docker-compose.yml et partir du principe qu’on veut une base de données pour notre application Flask :

services:
  database:
    image: mysql
    environment:
      MYSQL_ROOT_PASSWORD: myRootPassword
      MYSQL_DATABASE: myDb
      MYSQL_USER: myUser
      MYSQL_PASSWORD: myPassword
    volumes:
      - "flask_db:/var/lib/mysql"

  app:
    build: .
    container_name: flask-app
    depends_on:
      - "db"

networks:
  default:
    driver: bridge

volumes:
  flask_db:

Ici :

On obtient donc cette arborescence pour notre projet :

.
├── app.py
├── docker-compose.yml
├── Dockerfile
└── requirements.txt

Et on peut accéder à notre base de données mysql dans app.py :

import pymysql
from flask import Flask, jsonify
from contextlib import contextmanager


@contextmanager
def get_conn():
    conn = pymysql.connect(
        host="databse",
        port=3306,
        user="myUser",
        password="myPassword",
        database="myDb",
        cursorclass=pymysql.cursors.DictCursor,
        autocommit=True
    )
    try:
        yield conn
    finally:
        conn.close()


app = Flask(__name__)

@app.route("/")
def index():
    return jsonify({"message": "Hello from Flask + MySQL!"})

@app.route("/users", methods=["GET"])
def list_users():
    # Exemple uniquement, ici la table est jamais créée

    with get_conn() as conn:
        with conn.cursor() as cur:
            cur.execute("SELECT id, name, email FROM users")
            rows = cur.fetchall()

    return jsonify(rows)

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)

Enfin, on peut démarrer notre application avec :

$ cd chemin/vers/docker-compose.yml
$ docker compose up --build -d

Les templates disponibles

Pour Gradle (Java), NodeJS et Python, les trois templates suivent la même logique :

Pour PHP les outils utilisés sont plus variés donc :

entrypoint.sh ne doit pas démarrer de serveur web, seulement PHP FPM qui lui sert le dossier /app/public.