Déployer votre application php avec Deployer

Deployer

Cela va maintenant faire un peu plus d’1 an que ce blog est motorisé par WordPress et non plus par Dotclear. Pour rappel, lors de cette migration vers WordPress, afin d’avoir quelque chose d’un minimum industrialisé, de propre mais surtout qui me satisfasse, j’ai fait plusieurs choix techniques :

  1. Utiliser Timber pour gérer mon thème. Derrière ce choix, la raison était simple: je ne pouvais me faire à l’idée d’utiliser le (non-)moteur de template par défaut de WordPress, à savoir un mix de PHP et d’HTML.
  2. Je voulais pouvoir gérer proprement mes dépendances de mon site. Pour ça j’ai fait le choix de partir sur Composer et le dépôt WordpPress Packagist. Du coup Composer est également mon outil de gestion de tâche pour lancer le build du thème via NPM, vider les caches en post install ou post update, etc…
  3. Utiliser un outil pour déployer mon blog sur mon serveur et non plus un script shell comme précédemment. Au boulot, il y a quelques années nous utilisions Capistrano, mais Capistrano souffre d’un gros défaut AMHA: il est en Ruby. Ceci oblige à rajouter un langage supplémentaire. Du coup j’ai plutôt retenu la solution Deployer qui a l’avantage d’être en PHP et donc d’assurer une certaine cohérence car en plus il peut être installé à partir des dépendances de développement de Composer.

Du coup la mise à jour vers WordPress 5.2 (je sais je suis en retard…) est pour moi l’occasion de relancer quelques déploiements et donc d’en discuter sur ce billet.

La recette de Deployer

Bref, une fois tout ceci, expliqué, je n’avais qu’à me faire une petite recette de déploiement:

<?php
namespace Deployer;

require 'recipe/wordpress.php'; // Use WordPress task.
require 'recipe/slack.php';
require 'recipe/cachetool.php';
require 'recipe/rsync.php';

// Project name
set('application', 'blog.kulakowski.fr');

// Project repository
set('repository', 'git@mygit.com:me/myblog.git');

// Shared files/dirs between deploys
set('shared_files', [
    'web/wp-config.php',
    'web/wp-content/advanced-cache.php',
    'web/wp-content/object-cache.php',
    'web/wp-content/wp-cache-config.php'
]);
set('shared_dirs', [
    'web/wp-content/cache',
    'web/wp-content/languages',
    'web/wp-content/public',
    'web/wp-content/uploads'
]);

// Writable dirs by web server
set('writable_dirs', [
    'web/wp-content',
    'web/wp-content/uploads'
]);

// Set path
set('materialstyle_path', 'web/wp-content/themes/materialstyle');

// Set docker container
set('container_name', 'php');
set('container_uid', '82');
set('container_gid', '82');

// Add composer in more of wordpress default tasks
after('deploy:shared', 'deploy:vendors');

// Hosts
inventory('hosts.yml');

// [Optional] If deploy fails automatically unlock.
after('deploy:failed', 'deploy:unlock');


// ----------------------------------------------------------------- Task: build
desc('Build assets with webpack');
task('build', function () {
    run('composer run-script materialstyle');
    run('echo "Build done !"');
})->local();
after('deploy:prepare', 'build');


// ---------------------------------------------- Override task: deploy:writable
task('deploy:writable', function () {
    $dirs = get('writable_dirs');
    foreach ($dirs as $dir) {
        run('docker exec -i {{container_name}} chown {{container_uid}}:{{container_gid}} {{ release_path }}/' . $dir);
    }
});


// ------------------------------------------------------------- Task: cachetool
set('bin/cachetool', 'cachetool.phar');
after('deploy:symlink', 'cachetool:clear:opcache');

// ----------------------------------------------------------------- Task: slack
before('deploy', 'slack:notify');
after('success', 'slack:notify:success');
after('deploy:failed', 'slack:notify:failure');

// ----------------------------------------------------------------- Task: rsync
set('rsync_src', __DIR__ . '/{{ materialstyle_path }}/dist');
set('rsync_dest', '{{ release_path }}/{{ materialstyle_path }}/dist');
after('deploy:update_code', 'rsync');

Quelques explications s’imposent…

Tout d’abord, mon blog est principalement un site WordPress :

require 'recipe/wordpress.php'; // Use WordPress task.

Je souhaites également rajouter quelques actions supplémentaires fournies par des recettes Deployer. A savoir : vider les caches, notifier Slack et synchroniser mon thème buildé localement avec NPM. Justement le build de mon thème est lancé par Composer et donc intégrable dans Deployer :

desc('Build assets with webpack');
task('build', function () {
    run('composer run-script materialstyle');
    run('echo "Build done !"');
})->local();
after('deploy:prepare', 'build');

Une fois construit, le thème est envoyé sur le serveur via rsync :

set('rsync_src', __DIR__ . '/{{ materialstyle_path }}/dist');
set('rsync_dest', '{{ release_path }}/{{ materialstyle_path }}/dist');
after('deploy:update_code', 'rsync');

Comme expliqué précédemment, mon site est sous WordPress, mais géré par Composer. J’ai donc rajouté une tâche pour lancer le composer install :

// Add composer in more of wordpress default tasks
after('deploy:shared', 'deploy:vendors');

Notons au passage le fait que WordPress ne puissent pas s’écrire lui même est vu comme un problème de sécurité par WordPress…

Pour finir, comme discuté dans un autre article, mon infrastructure est basée sur Docker. J’ai donc une tâche pour remettre certains droits en phases :

task('deploy:writable', function () {
    $dirs = get('writable_dirs');
    foreach ($dirs as $dir) {
        run('docker exec -i {{container_name}} chown {{container_uid}}:{{container_gid}} {{ release_path }}/' . $dir);
    }
});

Bref avec tout ceci, mon blog se déploie en 1 commande.

Deployer

Et Slack dans tout ça ?

J’utilise Slack pour pour suivre mes déploiements sur mon canal perso ainsi que pour recevoir les notification sur mon téléphone :

Commentaires

Guik

De Guik le 3 novembre 2019

Bonjour,

Quelles avantages face à une recette docker ?
J'ai l'impression que l'on peut déjà faire cela dockerFile + un repo sans rajouter de couche supplémentaire.

Guillaume Kulakowski

De Guillaume Kulakowski le 3 novembre 2019

Justement, c'est décorrélé de Docker. Docker est la base de mon architecture mais WP n'est pas dans l'image. De plus je gère du déploiement partiel et du rollback de version.

Laisser un commentaire

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.