Pourquoi Deployer Laravel avec Docker ?
Deployer Laravel avec Docker offre plusieurs avantages, notamment :
- Isolation et portabilité : Docker permet d'isoler l'environnement de développement sur la machine locale, ce qui facilite la portabilité des applications entre différents environnements.
- Facilité de déploiement : Avec Docker, le déploiement peut être réalisé rapidement et facilement en quelques étapes, sans avoir à configurer manuellement les dépendances du projet.
- Régularité et cohérence : En utilisant des conteneurs Docker, vous garantissez que l'environnement de développement est toujours identique sur toutes les machines.
Un cas concret est une application qui nécessite un environnement PHP 8.1 et Composer pour installer des dépendances spécifiques. Avec Docker, vous pouvez créer un conteneur personnalisé avec ces configurations, assurant ainsi que le code fonctionne correctement dans n'importe quel environnement.
Prerequis
Pour suivre ce tutoriel, vous aurez besoin de :
- PHP 8.1 ou plus récent
- Docker Desktop (Windows/Mac) ou Docker Engine (Linux)
- Composer
- Un éditeur de code (par exemple Visual Studio Code)
Installation des outils
PHP 8.1
Vous pouvez installer PHP 8.1 en suivant les instructions officielles pour votre système d'exploitation :
- Windows/Linux : Utilisez le gestionnaire de paquets.
- MacOS : Installez avec Homebrew :
brew install php@8.1
Docker Desktop
- Windows/Mac : Téléchargez et installez Docker Desktop.
- Linux : Installez Docker Engine en suivant les instructions sur le site officiel.
Composer
Composer est généralement installé avec PHP. Vous pouvez vérifier l'installation en exécutant :
composer --version
Si ce n'est pas le cas, téléchargez-le depuis composer.org.
Concepts fondamentaux
1. Dockerfile
Un Dockerfile est un fichier texte qui contient des instructions pour construire une image Docker. Voici un exemple simple :
## Utilise une image PHP base
FROM php:8.1-fpm
## Installe les extensions nécessaires
RUN docker-php-ext-install pdo_mysql
## Copie le fichier de configuration de l'application
COPY . /var/www/html
## Définit le répertoire de travail
WORKDIR /var/www/html
## Installe les dépendances Composer
RUN composer install --no-dev --optimize-autoloader
## Expose le port 9000 (default for PHP-FPM)
EXPOSE 9000
## Démarre le serveur PHP-FPM
CMD ["php-fpm"]
2. docker-compose.yml
docker-compose.yml est un fichier qui définit et configure les services, les réseaux et les volumes Docker pour une application multi-conteneurs.
version: '3'
services:
web:
build: .
ports:
- "9000:9000"
volumes:
- .:/var/www/html
depends_on:
- db
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: laravel
MYSQL_USER: user
MYSQL_PASSWORD: password
volumes:
- db_data:/var/lib/mysql
volumes:
db_data:
Mise en pratique : Projet fil rouge
Dans ce tutoriel, nous allons créer un gestionnaire de tâches simple.
Étape 1 : Initialisation du projet
Créez un nouveau répertoire et initialisez un nouveau projet Laravel :
mkdir task-manager && cd task-manager
composer create-project --prefer-dist laravel/laravel .
Étape 2 : Configuration Dockerfile
Créez un Dockerfile dans le répertoire racine du projet avec le contenu suivant :
## Utilise une image PHP base
FROM php:8.1-fpm
## Installe les extensions nécessaires
RUN docker-php-ext-install pdo_mysql
## Copie le fichier de configuration de l'application
COPY . /var/www/html
## Définit le répertoire de travail
WORKDIR /var/www/html
## Installe les dépendances Composer
RUN composer install --no-dev --optimize-autoloader
## Expose le port 9000 (default for PHP-FPM)
EXPOSE 9000
## Démarre le serveur PHP-FPM
CMD ["php-fpm"]
Étape 3 : Configuration docker-compose.yml
Créez un docker-compose.yml dans le répertoire racine du projet avec le contenu suivant :
version: '3'
services:
web:
build: .
ports:
- "9000:9000"
volumes:
- .:/var/www/html
depends_on:
- db
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: laravel
MYSQL_USER: user
MYSQL_PASSWORD: password
volumes:
- db_data:/var/lib/mysql
volumes:
db_data:
Étape 4 : Lancement du conteneur
Lancez le conteneur Docker avec la commande suivante :
docker-compose up --build
Assurez-vous que les services web et db sont en cours d'exécution.
Étape 5 : Création de la migration
Créez une migration pour la table des tâches :
php artisan make:migration create_tasks_table --create=tasks
Editez le fichier de migration pour ajouter les champs nécessaires :
// database/migrations/xxxx_xx_xx_xxxxxx_create_tasks_table.php
public function up()
{
Schema::create('tasks', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('description')->nullable();
$table->boolean('completed')->default(false);
$table->timestamps();
});
}
Exécutez la migration :
php artisan migrate
Étape 6 : Création du modèle et de la vue
Créez un modèle pour les tâches :
php artisan make:model Task
Editez le fichier Task.php :
// app/Models/Task.php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Task extends Model
{
use HasFactory;
protected $fillable = ['title', 'description', 'completed'];
}
Créez une migration pour la table des tâches :
php artisan make:migration create_tasks_table --create=tasks
Editez le fichier de migration pour ajouter les champs nécessaires :
// database/migrations/xxxx_xx_xx_xxxxxx_create_tasks_table.php
public function up()
{
Schema::create('tasks', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('description')->nullable();
$table->boolean('completed')->default(false);
$table->timestamps();
});
}
Exécutez la migration :
php artisan migrate
Étape 7 : Création du contrôleur
Créez un contrôleur pour gérer les tâches :
php artisan make:controller TaskController --resource
Editez le fichier TaskController.php :
// app/Http/Controllers/TaskController.php
namespace App\Http\Controllers;
use App\Models\Task;
use Illuminate\Http\Request;
class TaskController extends Controller
{
public function index()
{
$tasks = Task::all();
return view('tasks.index', compact('tasks'));
}
public function create()
{
return view('tasks.create');
}
public function store(Request $request)
{
$validatedData = $request->validate([
'title' => 'required|string|max:255',
'description' => 'nullable|string'
]);
Task::create($validatedData);
return redirect()->route('tasks.index')->with('success', 'Tâche créée avec succès');
}
public function show(Task $task)
{
//
}
public function edit(Task $task)
{
//
}
public function update(Request $request, Task $task)
{
//
}
public function destroy(Task $task)
{
//
}
}
Étape 8 : Création des vues
Créez les vues pour afficher et créer des tâches :
mkdir resources/views/tasks
touch resources/views/tasks/index.blade.php
touch resources/views/tasks/create.blade.php
Editez resources/views/tasks/index.blade.php :
@extends('layouts.app')
@section('content')
<div class="container">
<h1>Tâches</h1>
<a href="route('tasks.create')" class="btn btn-primary mb-3">Nouvelle tâche</a>
<table class="table">
<thead>
<tr>
<th>Titre</th>
<th>Description</th>
<th>Date de création</th>
<th>Action</th>
</tr>
</thead>
<tbody>
@foreach ($tasks as $task)
<tr>
<td>$task->title</td>
<td>$task->description</td>
<td>$task->created_at->format('Y-m-d H:i:s')</td>
<td>
<a href="route('tasks.edit', $task)" class="btn btn-primary">Éditer</a>
<form action="route('tasks.destroy', $task)" method="POST" style="display: inline;">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-danger" onclick="return confirm('Voulez-vous vraiment supprimer cette tâche ?')">Supprimer</button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endsection
Editez resources/views/tasks/create.blade.php :
@extends('layouts.app')
@section('content')
<div class="container">
<h1>Nouvelle tâche</h1>
<form action="route('tasks.store')" method="POST" enctype="multipart/form-data">
@csrf
<div class="mb-3">
<label for="title" class="form-label">Titre</label>
<input type="text" name="title" id="title" class="form-control" required>
</div>
<div class="mb-3">
<label for="description" class="form-label">Description</label>
<textarea name="description" id="description" class="form-control"></textarea>
</div>
<button type="submit" class="btn btn-primary">Enregistrer</button>
</form>
</div>
@endsection
Étape 9 : Ajout de routes
Ajoutez les routes dans routes/web.php :
// routes/web.php
use App\Http\Controllers\TaskController;
Route::resource('tasks', TaskController::class);
Erreurs frequentes et debugging
1. Erreur : docker-compose up --build échoue avec ERROR: Service 'web' failed to build: The command '/bin/sh -c composer install --no-dev --optimize-autoloader' returned a non-zero code
Cause : Composer n'est pas trouvé dans l'image Docker.
Correction :
Ajoutez la commande pour installer Composer dans le Dockerfile :
## Installe Composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
2. Erreur : php artisan migrate échoue avec PDOException: SQLSTATE[42000]: Syntax error or access violation: 1067 Invalid default value for column 'created_at'
Cause : MySQL ne prend pas en charge les valeurs par défaut de type NULL.
Correction :
Modifiez la migration pour utiliser une valeur par défaut :
// database/migrations/xxxx_xx_xx_xxxxxx_create_tasks_table.php
public function up()
{
Schema::create('tasks', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('description')->nullable();
$table->boolean('completed')->default(false);
$table->timestamp('created_at')->useCurrent();
$table->timestamp('updated_at')->useCurrent()->useCurrentOnUpdate();
});
}
3. Erreur : docker-compose up --build échoue avec ERROR: Service 'web' failed to build: The command '/bin/sh -c php artisan migrate --no-interaction' returned a non-zero code
Cause : La base de données n'est pas prête lorsqu'il y a une erreur lors du migration.
Correction :
Ajoutez un script pour attendre que la base de données soit prête :
## docker-compose.yml
version: '3'
services:
web:
build: .
ports:
- "9000:9000"
volumes:
- .:/var/www/html
depends_on:
- db
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: laravel
MYSQL_USER: user
MYSQL_PASSWORD: password
volumes:
- db_data:/var/lib/mysql
volumes:
db_data:
## docker-entrypoint.sh (ajoutez ce fichier dans le répertoire racine du projet)
#!/bin/sh
until mysql -h db -u root -proot -e "SELECT 1"; do
echo "MySQL is unavailable, sleeping..."
sleep 2
done
php artisan migrate --no-interaction
Ajoutez le script au conteneur web dans le Dockerfile :
## Utilise une image PHP base
FROM php:8.1-fpm
## Installe les extensions nécessaires
RUN docker-php-ext-install pdo_mysql
## Copie le fichier de configuration de l'application
COPY . /var/www/html
## Définit le répertoire de travail
WORKDIR /var/www/html
## Installe les dépendances Composer
RUN composer install --no-dev --optimize-autoloader
## Expose le port 9000 (default for PHP-FPM)
EXPOSE 9000
## Copie le script docker-entrypoint.sh
COPY docker-entrypoint.sh /usr/local/bin/
## Démarre le serveur PHP-FPM avec le script
CMD ["/usr/local/bin/docker-entrypoint.sh"]
Pour aller plus loin
1. Ajout de Redis pour la gestion des tâches asynchrones
Ajoutez une configuration pour Redis dans docker-compose.yml et installez les dépendances nécessaires dans le projet.
2. Utilisation de Laravel Forge pour le déploiement en production
Laravel Forge est un outil d'hébergement et de déploiement simplifié pour Laravel applications.
3. Création d'un service API RESTful
Créez une API RESTful utilisant Laravel pour gérer les tâches via des requêtes HTTP.
Conclusion
Ce guide vous a aidé à créer une application Laravel avec Docker, en incluant le déploiement et la gestion de base de données. Vous avez appris à configurer Docker Compose pour un développement local efficace, et comment utiliser Laravel pour créer une application web complète. N'hésitez pas à explorer davantage les fonctionnalités de Laravel et de Docker pour développer des applications plus complexes et robustes.