Chapitre 13
13 — DevOps PHP
13 — DevOps PHP
Course : DevOps PHP
1. Introduction au DevOps pour PHP
1.1 Qu'est-ce que le DevOps ?
Le DevOps est l'intersection entre le développement (Dev) et les opérations (Ops). Il vise à automatiser et intégrer les processus entre ces deux équipes pour livrer des logiciels plus rapidement et de manière plus fiable.
1.2 Pipeline DevOps typique
Code → Build → Test → Deploy → Monitor
│ │ │ │ │
Git CI/CD PHPUnit Docker Pulse
GitHub Pest Laravel Envoyer
Actions Forge
1.3 Outils de l'écosystème PHP
- Docker : conteneurisation de l'application
- Laravel Forge : gestion de serveurs
- Laravel Vapor : serverless AWS Lambda
- Deployer : déploiement automatisé
- GitHub Actions / GitLab CI : CI/CD
- Laravel Pulse : monitoring des performances
- Envoyer : déploiement zero-downtime
2. Docker
2.1 Dockerfile multi-stage
# Stage 1 : Build
FROM php:8.3-fpm-alpine AS builder
RUN apk add --no-cache \
git \
unzip \
libzip-dev \
libpng-dev \
libjpeg-turbo-dev \
&& docker-php-ext-install pdo_mysql zip gd intl
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
WORKDIR /var/www
COPY composer.json composer.lock ./
RUN composer install --no-dev --no-interaction --optimize-autoloader
COPY . .
RUN php artisan optimize
# Stage 2 : Production
FROM php:8.3-fpm-alpine
RUN apk add --no-cache \
libzip \
libpng \
libjpeg-turbo \
nginx \
supervisor
COPY --from=builder /var/www /var/www
# Configuration
COPY docker/php.ini /usr/local/etc/php/conf.d/app.ini
COPY docker/nginx.conf /etc/nginx/nginx.conf
COPY docker/supervisord.conf /etc/supervisord.conf
EXPOSE 80
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]
2.2 docker-compose.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "80:80"
environment:
- APP_ENV=production
- DB_HOST=database
- REDIS_HOST=cache
depends_on:
- database
- cache
database:
image: mysql:8.3
environment:
MYSQL_DATABASE: app
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
volumes:
- db_data:/var/lib/mysql
ports:
- "3306:3306"
cache:
image: redis:7-alpine
volumes:
- redis_data:/data
ports:
- "6379:6379"
queue:
build:
context: .
dockerfile: Dockerfile
command: php artisan horizon
depends_on:
- database
- cache
scheduler:
build:
context: .
dockerfile: Dockerfile
command: php artisan schedule:work
depends_on:
- database
- cache
volumes:
db_data:
redis_data:
2.3 Extensions PHP essentielles
# Installation d'extensions
RUN docker-php-ext-install \
pdo_mysql \
mbstring \
exif \
pcntl \
bcmath \
gd \
intl \
zip
# PECL extensions
RUN pecl install \
redis \
xdebug \
apcu \
&& docker-php-ext-enable redis apcu
2.4 Optimisation des images
# Utiliser des versions Alpine
FROM php:8.3-fpm-alpine # ~50MB vs ~300MB
# Nettoyer les caches
RUN apk add ... && docker-php-ext-install ... \
&& rm -rf /var/cache/apk/* /tmp/*
# Layers minimales (regrouper les RUN)
RUN apk add --no-cache pkg1 pkg2 \
&& docker-php-ext-install ext1 ext2
3. Laravel Forge & Vapor
3.1 Laravel Forge
Forge gère l'infrastructure : provisionnement de serveurs (DigitalOcean, AWS, Linode, Vultr), installation de PHP, Nginx, MySQL, Redis.
Fonctionnalités clés :
- Quick Deploy : déploiement automatique sur push Git
- Daemon management : Horizon, Reverb, schedule
- SSL : Let's Encrypt automatique
- Queues : configuration des workers
- Scheduling : cron jobs
3.2 Laravel Vapor
Vapor est un déploiement serverless sur AWS Lambda :
# vapor.yml
id: 12345
name: my-app
environments:
production:
memory: 1024
timeout: 30
database: postgres-production
cache: redis-production
queue: sqs-production
cli:
- php artisan migrate --force
build:
- 'composer install --no-dev'
- 'php artisan optimize'
deploy:
- 'php artisan migrate --force'
Avantages Vapor : scalabilité automatique, pas de serveur à gérer, paiement à l'usage.
4. Deployer
4.1 Installation
composer require --dev deployer/deployer
4.2 Configuration
<?php
// deploy.php
namespace Deployer;
require 'recipe/laravel.php';
// Configuration
set('application', 'Mon Application');
set('repository', 'git@github.com:user/repo.git');
set('git_tty', true);
add('shared_files', []);
add('shared_dirs', ['storage']);
add('writable_dirs', ['storage']);
set('allow_anonymous_stats', false);
// Hôtes
host('production')
->setHostname('185.123.456.789')
->setRemoteUser('forge')
->setPort(22)
->setIdentityFile('~/.ssh/id_rsa')
->set('branch', 'main')
->set('deploy_path', '/home/forge/myapp.com');
host('staging')
->setHostname('staging.example.com')
->setRemoteUser('forge')
->set('branch', 'develop')
->set('deploy_path', '/home/forge/staging.myapp.com');
// Tâches
task('build:assets', function () {
run('cd {{release_path}} && npm ci && npm run build');
});
task('deploy:queue:restart', function () {
run('cd {{release_path}} && php artisan horizon:terminate');
});
after('deploy:success', 'deploy:queue:restart');
after('deploy:failed', 'deploy:unlock');
// Rollback
task('rollback:database', function () {
run('cd {{release_path}} && php artisan migrate:rollback');
});
4.3 Déploiement
# Déploiement
dep deploy production
# Rollback (si problème)
dep rollback production
# Lister les versions
dep releases production
4.4 Structure des répertoires
/home/forge/myapp.com/
├── current -> releases/20240729120000 (symlink)
├── releases/
│ ├── 20240729120000/
│ ├── 20240728120000/
│ └── 20240727120000/
├── shared/
│ ├── .env
│ ├── storage/
│ └── vendor/
└── deploy.lock
5. CI/CD
5.1 GitHub Actions
name: CI/CD Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
quality:
name: Code Quality
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: 8.3
coverage: xdebug
- run: composer install --no-interaction
- run: ./vendor/bin/pint --test # Laravel Pint
- run: ./vendor/bin/phpstan analyse --level=max
- run: php artisan test --coverage --min=80
deploy:
name: Deploy to Production
needs: quality
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: 8.3
- run: composer install --no-dev --optimize-autoloader
- run: npm ci && npm run build
- name: Deploy with Deployer
run: php vendor/bin/dep deploy production
env:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
5.2 GitLab CI
stages:
- test
- build
- deploy
variables:
PHP_IMAGE: php:8.3-cli-alpine
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- vendor/
- node_modules/
before_script:
- docker-php-ext-install pdo_mysql
- curl -sS https://getcomposer.org/installer | php
- php composer.phar install --no-interaction
test:
stage: test
script:
- vendor/bin/phpunit --coverage-text --colors=never
coverage: '/^\s*Lines:\s*\d+.\d+%/'
build:
stage: build
script:
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
deploy:
stage: deploy
script:
- docker pull $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
- docker-compose up -d
environment:
name: production
5.3 Environnements et variables
# GitHub Environments
environments:
production:
url: https://myapp.com
deployment_branch: main
staging:
url: https://staging.myapp.com
deployment_branch: develop
6. Laravel Pulse
6.1 Installation
composer require laravel/pulse
php artisan vendor:publish --provider="Laravel\Pulse\PulseServiceProvider"
php artisan migrate
6.2 Configuration
// config/pulse.php
return [
'recorders' => [
CacheInteractions::class => [
'enabled' => env('PULSE_CACHE_ENABLED', true),
],
Exceptions::class => [
'enabled' => env('PULSE_EXCEPTIONS_ENABLED', true),
],
Queues::class => [
'enabled' => env('PULSE_QUEUES_ENABLED', true),
],
Servers::class => [
'enabled' => env('PULSE_SERVERS_ENABLED', true),
'directories' => [
'/',
],
],
SlowJobs::class => [
'enabled' => env('PULSE_SLOW_JOBS_ENABLED', true),
'threshold' => env('PULSE_SLOW_JOBS_THRESHOLD', 1000),
],
SlowOutgoingRequests::class => [
'enabled' => env('PULSE_SLOW_OUTGOING_REQUESTS_ENABLED', true),
'threshold' => env('PULSE_SLOW_OUTGOING_REQUESTS_THRESHOLD', 1000),
],
SlowQueries::class => [
'enabled' => env('PULSE_SLOW_QUERIES_ENABLED', true),
'threshold' => env('PULSE_SLOW_QUERIES_THRESHOLD', 1000),
],
SlowRequests::class => [
'enabled' => env('PULSE_SLOW_REQUESTS_ENABLED', true),
'threshold' => env('PULSE_SLOW_REQUESTS_THRESHOLD', 1000),
],
UserSessions::class => [
'enabled' => env('PULSE_USER_SESSIONS_ENABLED', true),
],
],
];
6.3 Dashboard
// routes/pulse.php
use Laravel\Pulse\Facades\Pulse;
use Illuminate\Support\Facades\Route;
Route::get('/pulse', function () {
return view('pulse::dashboard');
})->middleware(['web', 'auth'])->can('viewPulse');
6.4 Alertes
// App\Providers\PulseServiceProvider.php
use Laravel\Pulse\Facades\Pulse;
Pulse::alert(function ($alerts) {
foreach ($alerts as $alert) {
if ($alert->type === 'slow_requests') {
Notification::route('slack', env('SLACK_WEBHOOK'))
->notify(new PerformanceAlert($alert));
}
}
});
7. Envoyer (Zero-Downtime Deploy)
7.1 Fonctionnement
Envoyer est un service de déploiement zero-downtime développé par Laravel :
- Pull du code
- Installation des dépendances
- Build des assets
- Activation du nouveau déploiement
- Vérification de santé
- Basculer le trafic vers la nouvelle version
7.2 Configuration
# Envoyer recipe
servers:
production:
ip: 185.123.456.789
user: forge
path: /home/forge/myapp.com
scripts:
- cd /home/forge/myapp.com && git pull origin main
- composer install --no-dev --no-interaction
- npm ci && npm run production
- php artisan optimize
- php artisan migrate --force
- php artisan horizon:terminate
hooks:
activated:
- php artisan up
deactivated:
- php artisan down --retry=30
health-check:
url: /health
expected: 200
8. Monitoring et Observabilité
8.1 Logs centralisés
// config/logging.php
'channels' => [
'stack' => [
'driver' => 'stack',
'channels' => ['daily', 'sentry'],
],
'sentry' => [
'driver' => 'monolog',
'handler' => \Monolog\Handler\RavenHandler::class,
'handler_with' => [
'client' => new \Raven_Client(env('SENTRY_DSN')),
],
],
],
8.2 Métriques personnalisées Pulse
// Enregistrer une métrique
Pulse::record('api_requests', $endpoint)->count();
// Jauges
Pulse::set('active_users', Cache::get('active_users_count'));
// Enregistrer une valeur
Pulse::record('order_value', $order->total)->avg();
9. Résumé
Le DevOps pour PHP repose sur l'automatisation et les bonnes pratiques :
- Docker : conteneurisation reproducible (multi-stage, Alpine)
- Forge/Vapor : gestion de serveurs et serverless
- Deployer : déploiement automatisé avec rollback
- CI/CD : GitHub Actions / GitLab CI pour qualité et déploiement
- Pulse : monitoring en temps réel des performances
- Envoyer : zero-downtime deploy
L'objectif final : un pipeline où un push sur main déclenche tests → build → déploiement → monitoring, le tout automatisé et fiable.