Déploiement et Maintenance d’Application Web

Déploiement et Maintenance d’Application Web

Le processus de mise en production et les mesures de suivi/maintenance pour notre application web (Laravel 11 en backend / React 19 en frontend, avec Axios, Bootstrap, base MySQL, hébergement O2Switch). L’objectif est de garantir un déploiement fluide, sécurisé et évolutif, même si plusieurs domaines cohabitent sur le même espace d’hébergement.

Spécifications Techniques

  • Backend: Laravel 11
  • Frontend: React 19
  • Bibliothèques clés: Axios pour les requêtes API, Bootstrap pour l’interface
  • Base de données: MySQL
  • Hébergement: O2Switch (mutualisé)

Ce guide détaille les procédures optimisées pour le déploiement et la maintenance d’une application web moderne utilisant Laravel et React dans un environnement d’hébergement mutualisé avec plusieurs domaines.

 

1. Infrastructure de Production

1.1 Configuration de l’Hébergement

Analyse de l’environnement O2Switch

O2Switch propose un hébergement mutualisé avec certaines spécificités à prendre en compte:

  • Type d’accès: CPanel pour la gestion des domaines et sous-domaines
  • Limites potentielles: Ressources partagées, contraintes PHP sur hébergement mutualisé
  • Avantages: Support PHP récent, possibilité d’accès SSH, certificats SSL inclus

Prérequis techniques validés

Vérifiez systématiquement ces points avant tout déploiement:

# Pour PHP, vérifiez la version via SSH (si disponible) ou via un phpinfo()
php -v  # Doit retourner PHP 8.1+ pour Laravel 11

# Vérifiez les extensions PHP requises
php -m | grep -E 'openssl|pdo|mbstring|tokenizer|xml|ctype|json|bcmath|fileinfo'

Configuration multi-domaines recommandée

Pour une architecture multi-domaines efficace:

/home/[user]/
├── public_html/                  # Domaine principal 
   ├── index.php                 # Point d'entrée Laravel (symlink)
   ├── .htaccess                 # Configuration Apache
   ├── ...
   └── build/                    # Build React pour domaine principal

├── [domain2.com]/                # Second domaine
   ├── index.php
   └── build/                    # Build React pour second domaine

├── app/                          # Code Laravel mutualisé (hors racine web)
   ├── laravel-core/             # Code source Laravel
   ├── storage/                  # Stockage partagé
   └── vendor/                   # Dépendances

└── logs/                         # Logs centralisés
    ├── apache/
    ├── php/
    └── mysql/

 

1.2 Optimisation de l’environnement

Configuration PHP spécifique

Créez un fichier .user.ini dans chaque répertoire web:

memory_limit = 256M
upload_max_filesize = 32M
post_max_size = 32M
max_execution_time = 120
max_input_vars = 3000

Configuration Apache optimisée

Fichier .htaccess à la racine de chaque domaine:

# Performance et cache
<IfModule mod_expires.c>
  ExpiresActive On
  ExpiresByType image/jpg "access plus 1 year"
  ExpiresByType image/jpeg "access plus 1 year"
  ExpiresByType image/gif "access plus 1 year"
  ExpiresByType image/png "access plus 1 year"
  ExpiresByType text/css "access plus 1 month"
  ExpiresByType application/pdf "access plus 1 month"
  ExpiresByType text/javascript "access plus 1 month"
  ExpiresByType application/javascript "access plus 1 month"
  ExpiresByType application/x-javascript "access plus 1 month"
  ExpiresByType application/x-font-woff "access plus 1 year"
  ExpiresByType application/x-font-woff2 "access plus 1 year"
  ExpiresByType application/font-woff "access plus 1 year"
  ExpiresByType application/font-woff2 "access plus 1 year"
</IfModule>

# Compression GZIP
<IfModule mod_deflate.c>
  AddOutputFilterByType DEFLATE text/plain
  AddOutputFilterByType DEFLATE text/html
  AddOutputFilterByType DEFLATE text/xml
  AddOutputFilterByType DEFLATE text/css
  AddOutputFilterByType DEFLATE application/xml
  AddOutputFilterByType DEFLATE application/xhtml+xml
  AddOutputFilterByType DEFLATE application/rss+xml
  AddOutputFilterByType DEFLATE application/javascript
  AddOutputFilterByType DEFLATE application/x-javascript
  AddOutputFilterByType DEFLATE application/x-font-ttf
  AddOutputFilterByType DEFLATE application/vnd.ms-fontobject
  AddOutputFilterByType DEFLATE font/opentype font/ttf font/eot font/otf
</IfModule>

# Redirection vers HTTPS
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

# Laravel
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]

# Sécurité
<FilesMatch "^\.">
  Order allow,deny
  Deny from all
</FilesMatch>

# Protection des fichiers sensibles
<FilesMatch "(^\.env|composer\.json|package\.json|package-lock\.json|composer\.lock)$">
  Order allow,deny
  Deny from all
</FilesMatch>

 

2. Procédures de Déploiement Optimisées

2.1 Préparation de l’environnement local

Configuration du pipeline de développement

  1. Structure Git recommandée:
    ├── .github/                   # CI/CD workflows (si GitHub)
       └── workflows/
           ├── test.yml
           └── deploy.yml
    ├── backend/                   # Code Laravel
       └── ...
    ├── frontend/                  # Code React
       └── ...
    └── scripts/                   # Scripts de déploiement
        ├── deploy.sh
        └── rollback.sh
  2. Scripts d’automatisation: scripts/deploy.sh:
#!/bin/bash

# Variables
ENV=${1:-production}  # Permet de spécifier l'environnement (par défaut: production)

# Préparation Backend
echo "Building Laravel application..."
cd backend
composer install --no-dev --optimize-autoloader
php artisan config:cache
php artisan route:cache
php artisan view:cache

# Préparation Frontend
echo "Building React application..."
cd ../frontend
npm ci  # Installation précise des dépendances selon package-lock.json
npm run build

# Préparation du package de déploiement
echo "Creating deployment package..."
mkdir -p ../deploy
cp -r ../backend/app ../deploy/
cp -r ../backend/bootstrap ../deploy/
cp -r ../backend/config ../deploy/
cp -r ../backend/database ../deploy/
cp -r ../backend/public ../deploy/
cp -r ../backend/resources ../deploy/
cp -r ../backend/routes ../deploy/
cp -r ../backend/storage ../deploy/
cp -r ../backend/vendor ../deploy/
cp ../backend/.env.${ENV} ../deploy/.env
cp ../backend/artisan ../deploy/

mkdir -p ../deploy/public/build
cp -r ../frontend/build/* ../deploy/public/build/

# Archive pour déploiement
cd ../deploy
zip -r ../deploy-package.zip .

echo "Deployment package ready: deploy-package.zip"
    1. Configuration des environnements: Créez plusieurs fichiers d’environnement pour différents contextes:
      • .env.development – Variables pour environnement de développement
      • .env.staging – Variables pour environnement de préproduction
      • .env.production – Variables pour environnement de production

      Exemple de .env.production:

      APP_NAME="MyApp"
      APP_ENV=production
      APP_KEY=base64:votreclé
      APP_DEBUG=false
      APP_URL=https://votredomaine.com
      
      LOG_CHANNEL=stack
      LOG_DEPRECATIONS_CHANNEL=null
      LOG_LEVEL=error
      
      DB_CONNECTION=mysql
      DB_HOST=localhost
      DB_PORT=3306
      DB_DATABASE=dbname
      DB_USERNAME=dbuser
      DB_PASSWORD=dbpassword
      
      BROADCAST_DRIVER=log
      CACHE_DRIVER=file
      FILESYSTEM_DISK=local
      QUEUE_CONNECTION=sync
      SESSION_DRIVER=file
      SESSION_LIFETIME=120
      
      # Optimisations de performance
      CACHE_PREFIX=myapp_cache
      
      # Sécurité
      SESSION_SECURE_COOKIE=true
      SANCTUM_STATEFUL_DOMAINS=votredomaine.com

 

2.2 Optimisation du build frontend

Pour des performances optimales du build React:

// Dans package.json, ajoutez ces optimisations:
{
  "scripts": {
    "build": "GENERATE_SOURCEMAP=false react-scripts build && npm run postbuild",
    "postbuild": "node ./scripts/optimize-build.js"
  }
}

Créez frontend/scripts/optimize-build.js:

const fs = require('fs');
const path = require('path');

// Fonction pour parcourir récursivement et optimiser
function processFiles(directory) {
  const files = fs.readdirSync(directory);
  
  files.forEach(file => {
    const fullPath = path.join(directory, file);
    
    if (fs.statSync(fullPath).isDirectory()) {
      processFiles(fullPath);
      return;
    }
    
    // Traitement des JS
    if (file.endsWith('.js')) {
      console.log(`Optimizing JS: ${fullPath}`);
      // Ici vous pourriez intégrer une minification supplémentaire si nécessaire
    }
    
    // Traitement des CSS
    if (file.endsWith('.css')) {
      console.log(`Optimizing CSS: ${fullPath}`);
      // Ici vous pourriez intégrer une optimisation CSS supplémentaire
    }
  });
}

// Point d'entrée
console.log('Post-build optimization starting...');
processFiles(path.resolve(__dirname, '../build'));
console.log('Post-build optimization complete');

2.3 Déploiement vers O2Switch

Méthode 1: Déploiement via SFTP (Basique)

  1. Connectez-vous via SFTP (FileZilla ou autre client)
  2. Uploadez les fichiers du package de déploiement
  3. Exécutez les migrations et commandes post-déploiement via SSH

Méthode 2: Déploiement automatisé (Recommandée)

  1. Préparation: Configurer l’accès SSH avec clés
  2. Script de déploiement distant:
#!/bin/bash

# Variables
REMOTE_USER="o2switch_user"
REMOTE_HOST="ssh.cluster0XX.hosting.ovh.net"  # À adapter selon votre serveur O2Switch
REMOTE_PATH="/home/user/app"
PACKAGE_PATH="./deploy-package.zip"

# Transfert du package
echo "Uploading deployment package..."
scp $PACKAGE_PATH $REMOTE_USER@$REMOTE_HOST:/tmp/

# Exécution à distance
ssh $REMOTE_USER@$REMOTE_HOST << 'EOF'
  # Création d'un backup
  timestamp=$(date +%Y%m%d%H%M%S)
  if [ -d "$REMOTE_PATH" ]; then
    echo "Creating backup..."
    mkdir -p ~/backups
    zip -r ~/backups/app-backup-$timestamp.zip $REMOTE_PATH
  fi
  
  # Déploiement
  echo "Deploying new version..."
  mkdir -p $REMOTE_PATH.new
  cd $REMOTE_PATH.new
  unzip /tmp/deploy-package.zip
  
  # Vérification et configuration des permissions
  chmod -R 755 storage bootstrap/cache
  
  # Mise à jour des liens symboliques
  if [ -d "$REMOTE_PATH" ]; then
    # Préserver les uploads et données persistantes
    if [ -d "$REMOTE_PATH/storage/app" ]; then
      cp -rp $REMOTE_PATH/storage/app $REMOTE_PATH.new/storage/
    fi
  fi
  
  # Switch vers la nouvelle version
  if [ -d "$REMOTE_PATH" ]; then
    mv $REMOTE_PATH $REMOTE_PATH.old
  fi
  mv $REMOTE_PATH.new $REMOTE_PATH
  
  # Configuration des domaines
  # Ces commandes sont à adapter selon votre structure
  cd $REMOTE_PATH
  ln -sf $REMOTE_PATH/public ~/public_html
  
  # Migrations et cache
  cd $REMOTE_PATH
  php artisan migrate --force
  php artisan config:cache
  php artisan route:cache
  php artisan view:cache
  php artisan storage:link
  
  # Nettoyage
  rm /tmp/deploy-package.zip
  rm -rf $REMOTE_PATH.old
  
  echo "Deployment complete!"
EOF
  1. Intégration à GitHub Actions (si utilisé): .github/workflows/deploy.yml:
name: Deploy to Production

on:
  push:
    branches: [main]
    
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.1'
          extensions: mbstring, intl, pdo_mysql
        
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '16'
          
      - name: Install Dependencies
        run: |
          composer install --no-dev --optimize-autoloader
          cd frontend
          npm ci
          
      - name: Build
        run: |
          cd frontend
          npm run build
          
      - name: Prepare Deployment
        run: bash ./scripts/deploy.sh production
      
      - name: Deploy to O2Switch
        uses: appleboy/scp-
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USERNAME }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          source: "deploy-package.zip"
          target: "/tmp"
          
      - name: Execute Remote Commands
        uses: appleboy/ssh-
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USERNAME }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            # Script de déploiement distant
            # Copier le contenu du script SSH ci-dessus

2.4 Vérifications Post-Déploiement

Mettez en place un processus systématique de vérification:

  1. Vérification de base:
# Vérifier le statut HTTP
curl -Is https://votredomaine.com | head -1

# Vérifier les redirections
curl -Is http://votredomaine.com | grep -i location
  1. Vérification API:
# Tester un endpoint API
curl -s https://votredomaine.com/api/status | grep -i success
  1. Liste de contrôle complète:
    • ✅ Site accessible via HTTPS
    • ✅ Redirection HTTP vers HTTPS fonctionnelle
    • ✅ Chargement des assets JS/CSS sans erreur
    • ✅ Connexion à la BDD opérationnelle
    • ✅ API fonctionnelle
    • ✅ Formulaires fonctionnels
    • ✅ Page d’accueil correctement rendue
    • ✅ Logs sans erreurs critiques

 

3. Maintenance et Surveillance

3.1 Stratégie de sauvegarde

Mettez en place une stratégie de sauvegarde robuste:

#!/bin/bash
# backup.sh - À exécuter via cron

# Variables
BACKUP_DIR="/home/user/backups"
SITE_DIR="/home/user/app"
DB_NAME="nom_database"
DB_USER="user_database"
DB_PASS="password_database"
DATE=$(date +"%Y-%m-%d")

# Création du répertoire si nécessaire
mkdir -p $BACKUP_DIR/$DATE

# Sauvegarde des fichiers
echo "Backing up files..."
zip -r $BACKUP_DIR/$DATE/files-$DATE.zip $SITE_DIR -x "*/node_modules/*" -x "*/vendor/*"

# Sauvegarde de la base de données
echo "Backing up database..."
mysqldump -u $DB_USER -p$DB_PASS $DB_NAME > $BACKUP_DIR/$DATE/db-$DATE.sql
gzip $BACKUP_DIR/$DATE/db-$DATE.sql

# Rotation (garder 30 jours)
find $BACKUP_DIR -type d -mtime +30 -exec rm -rf {} \;

echo "Backup completed successfully!"

Configuration cron

# Sauvegarde quotidienne à 3h du matin
0 3 * * * /home/user/scripts/backup.sh >> /home/user/logs/backup.log 2>&1

3.2 Monitoring et alertes

Solution de base avec UptimeRobot

  1. Créez un compte sur UptimeRobot
  2. Configurez des moniteurs pour:
    • Disponibilité du site: https://votredomaine.com
    • API health check: https://votredomaine.com/api/health
    • Certificat SSL (expiration)

Solution avancée avec NewRelic ou Datadog

Pour une surveillance plus complète, intégrez:

// Dans AppServiceProvider.php
public function boot()
{
    // Ajout de l'observateur de base de données pour le monitoring
    DB::listen(function ($query) {
        // Enregistrement des requêtes lentes (>100ms)
        if ($query->time > 100) {
            Log::channel('performance')->info('Slow query: ' . $query->sql, [
                'time' => $query->time,
                'bindings' => $query->bindings
            ]);
        }
    });
}

3.3 Processus de mise à jour

Mises à jour de sécurité

Mettez en place un processus régulier de vérification:

# Vérification des dépendances PHP
composer audit

# Vérification des dépendances JS
cd frontend
npm audit

Mises à jour logicielles

Procédure recommandée pour les mises à jour:

  1. Créez une branche dédiée: git checkout -b update/laravel-security-fix
  2. Mettez à jour les dépendances: composer update --with-dependencies symfony/http-kernel
  3. Exécutez les tests: php artisan test
  4. Déployez en préproduction et validez
  5. Fusionnez via Pull Request et déployez en production

3.4 Gestion des incidents

Préparez un plan de réponse aux incidents:

  1. Détection:
    • Alertes automatisées (UptimeRobot, logs)
    • Retours utilisateurs
  2. Évaluation:
# Vérifier les erreurs récentes
tail -n 100 /home/user/logs/laravel.log

# Vérifier la charge serveur
top -b -n 1
  1. Résolution:
    • Rollback rapide si nécessaire:
# Script de rollback
cd /home/user
unzip -o backups/app-backup-TIMESTAMP.zip -d /home/user/
cd app
php artisan migrate:status  # Vérifier l'état des migrations avant rollback
  1. Documentation et post-mortem: Documentez chaque incident pour analyse et amélioration continue

 

4. Optimisations Avancées

4.1 Mise en cache des requêtes API

Implémentez un système de cache pour les requêtes API fréquentes:

// Dans un contrôleur API
public function getProducts()
{
    return Cache::remember('products.all', now()->addHours(1), function () {
        return Product::with('categories')->get();
    });
}

// Invalidation du cache lors de modifications
public function updateProduct(Request $request, $id)
{
    $product = Product::findOrFail($id);
    $product->update($request->validated());
    
    Cache::forget('products.all');
    Cache::forget("product.{$id}");
    
    return response()->json($product);
}

4.2 Configuration Nginx (si disponible)

Si O2Switch permet la configuration Nginx personnalisée (à vérifier), utilisez ce modèle:

# Gzip
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

# Browser caching
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
    expires 1y;
    add_header Cache-Control "public, max-age=31536000";
}

# PHP handling
location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;
}

4.3 Configuration optimisée de la base de données

Ajoutez ces optimisations à votre fichier config/database.php:

'mysql' => [
    'driver' => 'mysql',
    'url' => env('DATABASE_URL'),
    'host' => env('DB_HOST', '127.0.0.1'),
    'port' => env('DB_PORT', '3306'),
    'database' => env('DB_DATABASE', 'forge'),
    'username' => env('DB_USERNAME', 'forge'),
    'password' => env('DB_PASSWORD', ''),
    'unix_socket' => env('DB_SOCKET', ''),
    'charset' => 'utf8mb4',
    'collation' => 'utf8mb4_unicode_ci',
    'prefix' => '',
    'prefix_indexes' => true,
    'strict' => true,
    'engine' => null,
    'options' => extension_loaded('pdo_mysql') ? array_filter([
        PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
        // Nouvelles optimisations
        PDO::ATTR_EMULATE_PREPARES => false, // Requêtes préparées côté serveur
        PDO::ATTR_PERSISTENT => true, // Connexions persistantes
    ]) : [],
],

Lexique

Terme Description
CI/CD Continuous Integration/Continuous Deployment – Processus d’automatisation du déploiement
Build Processus de compilation et préparation du code pour la production
Artefact Fichier ou ensemble de fichiers résultant d’un build (ex: ZIP de déploiement)
Rollback Retour à une version précédente en cas de problème
Zero-downtime deployment Méthode de déploiement sans interruption de service
Blue-Green Deployment Technique de déploiement utilisant deux environnements identiques
Environnement Contexte d’exécution (dev, staging, production)
Canary Release Déploiement progressif sur un sous-ensemble d’utilisateurs
Health Check Vérification automatique de la santé d’une application
Caching Stockage temporaire de données pour améliorer les performances

 

Documentation o2switch