Docker dans le cycle de développement logiciel
Sommaire
Docker comme outil pour les développeurs : cohérence des environnements
Dans le monde du développement logiciel, l'un des défis majeurs est la gestion des environnements. Les différences entre les configurations des machines des développeurs, les serveurs de test et les environnements de production peuvent provoquer des dysfonctionnements imprévus. Docker, grâce à sa technologie de conteneurisation, offre une solution élégante pour garantir la cohérence des environnements à chaque étape du cycle de vie du développement logiciel.
La problématique des environnements incohérents
Les environnements de développement, de test et de production sont souvent hétérogènes. Cela peut résulter de différences dans les versions des bibliothèques, des dépendances ou même des systèmes d'exploitation. Ces écarts peuvent entraîner des bogues difficiles à reproduire, des pertes de temps lors de la phase de débogage et des retards dans les cycles de livraison.
Prenons un exemple concret : un développeur travaille sur une application Python sur sa machine locale avec une version spécifique de Python (par exemple, 3.9). En revanche, le serveur de test utilise une version plus ancienne (par exemple, 3.6), ce qui provoque des incompatibilités dans certaines fonctionnalités. Résultat : l'application fonctionne parfaitement en local, mais échoue en test ou en production. Docker élimine ce type de problème en encapsulant l'application et ses dépendances dans un conteneur isolé, garantissant que l'environnement reste identique où qu'il soit exécuté.
Docker et la portabilité des environnements
Ainsi que nous l'avons déjà abordé, Docker repose sur la création d'images, qui sont des modèles immuables contenant tout ce qui est nécessaire pour exécuter une application : le code source, les dépendances, les bibliothèques, les fichiers de configuration, et même le système d'exploitation si nécessaire. À partir de ces images, les développeurs peuvent lancer des conteneurs, qui sont des instances en cours d'exécution de ces images. Cette approche garantit que l'application s'exécute exactement de la même manière, que ce soit sur la machine d'un développeur, sur un serveur de test ou en production.
Prenons un autre exemple : imaginons une équipe qui développe une application web basée sur Node.js. Chaque développeur peut exécuter la commande suivante pour lancer l'application dans un environnement Docker, identique à celui utilisé par ses collègues ou les serveurs de production :
docker run -it -p 3000:3000 node:14
Cette commande crée un conteneur avec l'image officielle de Node.js version 14, sans que le développeur ait besoin d’installer Node.js directement sur sa machine. Peu importe que la machine soit sous Windows, macOS ou Linux : le conteneur s’exécutera de manière identique partout.
Cohérence des environnements dans le cycle de développement
Docker joue un rôle clé à chaque étape du cycle de développement logiciel :
- Développement local : les développeurs peuvent utiliser des fichiers
docker-compose.ymlpour orchestrer facilement plusieurs services. Par exemple, une application web avec une base de données PostgreSQL peut être configurée ainsi :
version: '3.8'
services:
web:
image: python:3.9
volumes:
- .:/code
ports:
- "8000:8000"
command: python manage.py runserver
db:
image: postgres:13
Avec cette configuration, chaque membre de l'équipe travaille dans le même environnement, réduisant les risques d’incohérences.
- Tests automatisés : Docker permet de créer des environnements éphémères pour exécuter des suites de tests. Par exemple, un pipeline CI/CD peut automatiquement lancer des conteneurs pour tester une application avec différentes configurations, puis les supprimer une fois les tests terminés.
- Production : les conteneurs Docker peuvent être déployés directement sur des serveurs ou des orchestrateurs tels que Kubernetes. Cela garantit que ce qui a été testé et validé est identique à ce qui est déployé en production.
Réduction des conflits entre développeurs
Dans une équipe, il est courant que différents développeurs aient des configurations logicielles différentes. Docker résout ce problème en isolant les environnements de développement. Par exemple, si un développeur travaille sur une fonctionnalité nécessitant une ancienne version de PHP (par exemple, 7.4), et qu’un autre utilise une version plus récente (par exemple, 8.1), ils peuvent chacun exécuter leurs conteneurs sans interférer les uns avec les autres. Cela simplifie la collaboration et réduit les conflits liés aux environnements.
Standardisation des environnements grâce à Docker Hub
Docker Hub joue un rôle crucial dans la standardisation des environnements. Les développeurs peuvent utiliser des images officielles, maintenues par la communauté ou par les éditeurs de logiciels, comme base pour leurs projets. Par exemple, les images officielles de MySQL, Redis ou Python garantissent que l’application utilise un environnement fiable et cohérent.
De plus, les équipes peuvent créer leurs propres images personnalisées et les stocker sur des registries privés pour partager des environnements spécifiques à leurs projets. Cela renforce encore la cohérence et la portabilité entre les développeurs et les serveurs.
Exemple concret : gestion d'un projet complexe
Imaginons une équipe qui développe une application de commerce en ligne. L'application nécessite les éléments suivants :
- Un serveur web basé sur Python.
- Une base de données MySQL.
- Un serveur de cache Redis.
Grâce à Docker, l'équipe peut configurer un fichier docker-compose.yml comme suit :
version: '3.8'
services:
web:
image: python:3.9
ports:
- "5000:5000"
volumes:
- ./app:/app
command: python /app/app.py
db:
image: mysql:8
environment:
MYSQL_ROOT_PASSWORD: example
cache:
image: redis:latest
Avec cette configuration, chaque membre de l'équipe peut lancer tous les services nécessaires en exécutant une seule commande :
docker compose up
Tous les développeurs utilisent ainsi un environnement identique, sans avoir à installer chaque composant individuellement sur leurs machines.
Intégration de Docker dans les workflows de développement (local et CI/CD)
Docker a transformé la manière dont les développeurs conçoivent, testent et déploient des applications. En s'intégrant parfaitement dans les workflows de développement, Docker permet d'uniformiser les environnements et d'automatiser les processus, que ce soit pour le travail local ou dans des pipelines d'intégration et de déploiement continus (CI/CD).
Docker dans le développement local
L'intégration de Docker dans le développement local repose sur la capacité de Docker à isoler les environnements et à simplifier la gestion des dépendances. Au lieu de configurer chaque machine avec des outils, bibliothèques ou bases de données spécifiques, Docker permet aux développeurs de tout encapsuler dans des conteneurs.
Exemple : développement d'une application Node.js
Un développeur travaillant sur une application Node.js peut utiliser Docker pour s'assurer que l'environnement est identique à celui de ses coéquipiers. Voici un exemple d'utilisation :
Commençons par créer un fichier Dockerfile :
FROM node:14
WORKDIR /app
COPY package.json ./
RUN npm install
COPY . .
CMD ["npm", "start"]
EXPOSE 3000
Lançons un conteneur pour l'application avec les deux commandes suivantes :
docker build -t mon-app .
docker run -p 3000:3000 -v $(pwd):/app -d mon-app
Avec cette approche, tout développeur peut cloner le projet, exécuter la commande Docker et obtenir un environnement fonctionnel, sans avoir à installer Node.js ou les dépendances localement.
Docker dans les pipelines CI/CD
Dans les workflows modernes, l'intégration continue (CI) et le déploiement continu (CD) sont essentiels pour assurer la qualité et la rapidité des livraisons. Docker s'intègre parfaitement dans ces processus en permettant d’automatiser les tests, la construction des images, et le déploiement.
Exemple : pipeline CI avec GitHub Actions
Voyons ensemble comment Docker peut être intégré dans un pipeline CI pour une application Python.
Commençons par créer un fichier Dockerfile :
FROM python:3.9
WORKDIR /app
COPY requirements.txt ./
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "app.py"]
Nous procédons à la configuration de GitHub Actions (.github/workflows/ci.yml) :
name: CI Pipeline
on:
push:
branches:
- main
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Build Docker image
run: docker build -t mon-app .
- name: Run tests
run: docker run mon-app pytest
Que fait ce pipeline ?
Il effectue les étapes suivantes :
- Récupère le code source.
- Construit une image Docker.
- Exécute les tests unitaires dans un conteneur.
Déploiement continu avec Docker
Une fois les tests validés, Docker facilite également le déploiement :
Nous publions l'image sur une registry, par exemple Docker Hub :
docker tag mon-app mon-organisation/mon-app:v1.0
docker push mon-organisation/mon-app:v1.0
Il nous reste ensuite à déployer l'image sur un orchestrateur comme Kubernetes ou directement sur un serveur, ce que nous aurons l'occasion de pratiquer ultérieurement.
TP : intégration de Docker dans un workflow CI/CD avec Apache et PHP
Nous allons pratiquer ensemble un TP afin de concrétiser nos explications exposées tout au long de ce cours.
Objectif : mettre en place un environnement conteneurisé pour une application PHP utilisant Apache, puis configurer un pipeline CI/CD pour automatiser les tests et le déploiement.
Étape 1 : préparation de l'application PHP
Créez un dossier nommé workflow-apache-php contenant index.php :
<?php
echo "Bonjour, je suis une super application PHP pour le TP de workflow avec Apache et PHP !";
?>
Étape 2 : création du Dockerfile
Le Dockerfile définira l'environnement Apache et PHP.
# Utiliser une image de base Apache avec PHP préinstallé
FROM php:8.2-apache
# Copier les fichiers de l'application dans le dossier racine web d'Apache
COPY . /var/www/html/
# Donner les permissions nécessaires
RUN chown -R www-data:www-data /var/www/html
# Exposer le port 80 pour le serveur Apache
EXPOSE 80
Étape 3 : test local de l'application avec Docker
Construisons l'image Docker :
docker build -t workflow-apache-php .

Lançons le conteneur :
docker run -d -p 8080:80 workflow-apache-php
Une fois le conteneur lancé, vérifions. Pour ce faire, faisons curl -f http://localhost:8080 | grep "Bonjour". Nous devrions voir le message dans la console :
Bonjour, je suis une super application PHP pour le TP de workflow avec Apache et PHP !

Étape 4 : Automatisation avec Docker Compose
Ajoutons un fichier docker-compose.yml pour orchestrer facilement le lancement de tous les services. Nous reviendrons plus tard, dans un prochain chapitre, sur les explications détaillées sur Docker Compose.
version: '3.8'
services:
web:
build: .
ports:
- "8080:80"
volumes:
- .:/var/www/html
Nous lançons tous les services (en mode détaché) :
docker compose up -d

Étape 5 : Configuration d'un pipeline CI avec GitHub Actions
Au préalable, nous devons nous connecter sur GitHub. Vous devez créer un compte si vous n'en avez pas.
Vous devez créer un nouveau dépôt (repository). Une fois dans votre nouveau dépôt, cliquez sur l’onglet Actions dans la barre de navigation.
GitHub nous propose des modèles pré-configurés pour les workflows. Cliquez sur Configure pour en utiliser un ou choisissez New workflow pour créer le vôtre.
Nous choisissons de créer un workflow personnalisé avec Set up a workflow yourself.

Un éditeur en ligne s'ouvrira avec un fichier prérempli nommé main.yml. Modifions son contenu pour inclure notre propre workflow :

La structure du pipeline est composée de telle sorte à ce qu'il puisse :
- Récupérer le code.
- Construire l'image Docker.
- Lancer un conteneur pour tester l'application.
Créons un fichier GitHub Actions (.github/workflows/ci.yml) :
name: CI Apache PHP
on:
push:
branches:
- main
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- name: checkout code
uses: actions/checkout@v3
- name: set up Docker
uses: docker/setup-buildx-action@v2
- name: build Docker image
run: docker build -t workflow-apache-php .
- name: run container and test application
run: |
docker run -d -p 8080:80 workflow-apache-php
sleep 5
curl -f http://localhost:8080
Étape 6 : Déploiement continu
Ajoutez une étape pour pousser l'image sur Docker Hub après validation des tests.
Configurons les secrets GitHub : nous devons ajouter les secrets DOCKER_USERNAME et DOCKER_PASSWORD dans les paramètres de votre dépôt GitHub.
Puis nous mettons à jour le fichier CI pour inclure le déploiement :
- name: push Docker image
run: |
echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
docker tag workflow-apache-php:latest my-dockerhub-account/workflow-apache-php:latest
docker push my-dockerhub-account/workflow-apache-php:latest
Voici notre fichier GitHub Actions finalisé :
name: CI/CD Pipeline for Docker
on:
push:
branches:
- main
jobs:
build-test-push:
runs-on: ubuntu-latest
steps:
# Étape 1 : récupérer le code source
- name: checkout code
uses: actions/checkout@v3
# Étape 2 : construire l'image Docker
- name: build Docker image
run: docker build -t workflow-apache-php .
# Étape 3 : tester l'application
- name: run container and test application
run: |
docker run -d -p 8080:80 --name test-container workflow-apache-php
sleep 10
curl -f http://localhost:8080
docker stop test-container
docker rm test-container
# Étape 4 : pousser l'image sur Docker Hub
- name: push Docker image
env:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
run: |
echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
docker tag workflow-apache-php:latest $DOCKER_USERNAME/workflow-apache-php:latest
docker push $DOCKER_USERNAME/workflow-apache-php:latest
Pour rappel, $DOCKER_USERNAME et $DOCKER_PASSWORD sont nos secrets contenant nos identifiants Docker Hub. Ils sont définis dans les paramètres de sécurité de GitHub Actions.

Enregistrons le fichier main.yml avec le bouton Commit changes (situé sur la droite).
À présent, poussons nos fichiers sur le repository Git. Nous supposons avoir déjà initialisé le repo local avec git init.

Notre action se déclenche comme prévu.

Après un petit temps d'attente, nous constatons que celui-ci a été correctement terminé.


Notre image a été automatiquement correctement poussée sur le registry Docker Hub et s'affiche dans la liste de notre repository personnel :

Quels sont les composants que nous avons mis en place ?
- Une application PHP fonctionnelle servie par Apache, testée et conteneurisée.
- Un pipeline CI qui construit, teste et déploie l'application.
- Une image Docker publiée sur Docker Hub, prête à être déployée.
Bonus (facultatif) : déploiement sur un serveur
Pour aller plus loin, déployez l'application sur un serveur ou un cluster Kubernetes en utilisant l'image Docker générée. Vous pouvez par exemple utiliser un fichier kubectl pour automatiser ce déploiement.
Dans les prochains chapitres de ce cours, ces notions seront abordées plus en détail et nous évoquerons aussi l'utilisation de Kubernetes.
