Commentaires
Capifony et Capistrano sont dans un bateau
Capifony est un outil destiné à l'industrialisation des déploiements d'applications sur des serveurs distants via SSH, ce n'est qu'une extension de Capistrano qui a été conçue initialement pour le déploiement d'applications Ruby. C'est donc pour cela que ce dernier est écrit en Ruby, langage qui se prête bien plus à ce genre de tâche que notre PHP...
Capifony nous permet donc de gérer très facilement le déploiement d'applications Symfony.
Je ne vais pas vous faire un énième article sur l’installation, la configuration et l’utilisation de Capifony, le site capifony.org est le point de départ et la source de la plupart des réponses à vos questions. Je vais juste essayer de vous montrer les pistes à prendre si vous avez besoin d'aller un peu loin avec Capifony et vous partager aussi certains retours d'expérience.
Le projet
Capifony n'est donc qu'une surcouche à Capistrano permettant la prise en compte des commandes spécifiques à Symfony et à PHP.
Ce projet a été initié par everzet il y a 4 ans. Il est maintenu actuellement par William Durand
Il n'y a pas de secret, la meilleure façon de comprendre et d'apprendre ce qui se passe est de naviguer dans le code source. Le langage Ruby est assez facile à appréhender surtout que les concepts nécessaires restent assez basiques. Le point de départ est donc les répertoires où sont installées les sources de Capifony et Capistrano.
Sur OSX ça se passe ici : /Library/Ruby/Gems
Task
Les Tasks sont les bases de Capistrano, elles peuvent être lancées directement par la ligne de commande ou bien être appelées dans une autre tâche.
desc "Lorem ipsum dolor sit amet, consectetur adipisicing elit."
task :backup do
puts "In Example Backup Web-Server"
...
run "#{try_sudo} sh -c 'cd #{latest_release} && #{php_bin} #{symfony_vendors} #{cmd}'"
...
run_locally "cd #{$temp_destination} && #{mytool_bin} install #{options}"
end
Pour exécuter cette tâche, il suffit de lancer la commande :
$ cap backup
Task & Namespace
Pour améliorer la visibilité et éviter les noms à rallonge, le regroupement de tâches se fait par Namespace sous la forme suivante.
namespace :web_server do
task :backup do
puts "In Example Backup Web-Server"
end
end
Pour exécuter cette tâche, il suffit de lancer la commande
$ cap web_server:backup
Vous pouvez aussi utiliser plusieurs niveaux de namespace
namespace :web do
namespace :server do
task :backup do
puts "In Example Backup Web-Server"
end
end
end
Pour exécuter cette tâche, il suffit de lancer la commande
$ cap web:server:backup
Je vous laisse approfondir le sujet sur le wiki du projet
Multistage
C'est une extension indispensable à toute installation de Capifony, elle permet la gestion de plusieurs environnements comme les classiques recette, preprod et prod.
Le principe est très simple : chaque environnement a son propre fichier où l'on peut surcharger des variables et des tâches.
Ensuite, il suffit de rajouter le nom de l'environement pour chaque commande
$ cap rec deploy
$ cap prod deploy
Plus d'infos sur le site de capifony.
Etendre Capifony
Où est ce que je mets mon code ?
Partout et c'est un peu le souci! Vous pouvez le poser dans le fichier Capfile, le app/config/deploy.rb
ou bien dans les fichiers spécifiques par environnement.
Ensuite si vous souhaitez partager du code entre les projets et les postes de dev, il va falloir passer par une extension et il y en a beaucoup. Essayez la commande suivante :
$ gem search capistrano
Surcharge d'une Task
Rien de plus simple, il suffit de la redéfinir pour la surcharger.
namespace :symfony do
namespace :project do
desc "We need our controllers on demo stage"
task :clear_controllers do ; end
end
end
Voici ce que nous utilisions avant la mise en place de l'option permettant de piloter la suppression de controleur de dev.
Chainage Task
Vous pouvez changer les workflows de tâches définies en intercalant vos propres tâches à n'importe quelles étapes.
Vous disposez pour cela des méthodes after
et before
.
namespace :deploy do
# .. this is a default namespace with lots of its own tasks
end
namespace :notifier do
task :email_the_boss do
# Implement your plain ruby emailing code here with [`TMail`](http://tmail.rubyforge.org/)
end
end
after (:deploy, "notifier:email_the_boss")
L'utilisation la plus courante est le lancement de la tâche de cleanup
après un deploy
after :deploy, 'deploy:cleanup'
Cas concrets et retours d'expérience
Stratégie de déploiement
Dans la majorité des cas, les fichiers de configuration seront intégrés à votre projet Symfony et vous lancerez les commandes de déploiement à partir de votre poste de dev où est installé votre projet.
Cependant nous avons eu l'occasion de prendre une autre approche pour un de nos clients ‘grand compte’. Nous avions une dizaine d'applications et les personnes qui étaient en charge des déploiements n'avaient pas les projets sur leur poste qui en plus était sous Windows...
Nous avons donc mis en place un serveur dédié au déploiement sur lequel on devait se connecter en SSH. Toutes les configurations sont gérées au sein d'un même projet versionné.
Cela permet aussi une gestion fine des autorisations déploiement avec les gestions des droits au niveau des fichiers de configuration. Il suffit de ne pas donner le droit de lecture au fichier de configuration de prod pour en bloquer le déploiement.
Utilisation d'une gateway
Il est possible dans le cas où les frontaux web ne disposent pas d'IP publique ou d'accès direct à internet pour des raisons de sécurité d'utiliser une "gateway", pour cela il faut définir les paramètres de connexion à celle-ci via la directive "gateway".
Attention ce type de configuration nécessite :
- D'activer l'option "Forward agent".
- De mettre en place une authentification par clé à la fois sur la gateway mais également sur le serveur/VM où s'effectue le déploiement.
Exemple:
ssh_options[:forward_agent] = true
set :user, "deploy"
set :gateway, "deploy@lb.elao.com"
Pas de connexion internet
Dans le cas où même la solution de gateway SSH ci-dessus ne fonctionne pas et que donc ni le serveur cible ni la machine de deploiement ne dispose de connexion à internet.
Dans ce cas 2 solutions s'offrent à vous :
- Faire un mirroir pour Composer avec Satis, mais cela devient complexe à maintenir particulièrement pour des projets avec beaucoup de dependances.
- Packager (fichier zip) les vendors dans le depot GIT de votre projet mais pas super glop pour la taille de votre repo.
Définition des rôles et des hosts
role(:app) { ['web1.elao.local', {:master => true, :user => 'deploy1'] }
role(:app) { ['web2.elao.local', {:user => 'deploy2'] }
role(:db) { ["db1.elao.local", {:master => true, :user => 'deploy1'}] }
role(:db) { ["db2.elao.local", {:user => 'deploy2'}]
Diverses variables de configuration peuvent être affectées à des hosts spécifique en fonction des rôles définis.
Récupérer les hosts d'un rôle
Dans une configuration de multi hosts par rôle, nous avons des besoins d'exécuter des tâches sur chacun d'entre eux. Le code ci-dessous nous permet de générer le cache de nos applications symfony 1.x sur tous les frontaux web.
servers = find_servers(:roles => :app)
servers.each_with_index do |server, idx|
idx = idx+1
logger.info("Processing Frontend App on #{server} ...")
run "wget -qnv --no-check-certificate -O - https://#{application}s#{idx}.elao.local/index.php/ping"
logger.info("Processing Backoffice App on #{server} ...")
run "wget -qnv --no-check-certificate -O - https://#{application}s#{idx}.elao.local/backoffice.php/ping"
logger.info("Processing RestApi App on #{server} ...")
run "wget -qnv --no-check-certificate -O - https://#{application}s#{idx}.elao.local/apiRest.php/ping"
logger.info("Processing SoapApi App on #{server} ...")
run "wget -qnv --no-check-certificate -O - https://#{application}s#{idx}.elao.local/apiSoap.php/ping"
logger.important("Symfony cache generated !")
end
Utilisation du logger
Je vous conseille fortement d'abuser du logger dans vos tâches perso, je ne vais pas m'attarder sur le sujet, le code ci-dessous parle de lui même.
logger.info("Processing frontend on #{server} ...")
logger.important("Symfony cache generated !")
logger.debug("...")
logger.trace("...")
Vous avez aussi la possibilité de contrôler le niveau de log produit lors de l'exécution.
logger.level = Logger::MAX_LEVEL
Exécuter des tasks uniquement sur le host master
Très pratique pour exécuter des tâches seulement sur le master dans le cadre d'une réplication MASTER > SLAVE
desc "Runnin migration"
task :migrate, :roles => :db, :only => { :master => true } do
run_migration
end
Capifony & Capistrano V3
Capistrano 3 est sortie cet été avec une refonte en profondeur du projet, les principaux changements sont :
- Abandon de leur propre DSL pour se reposer sur celui de Rake
- Le support de Multi Stage en natif (enfin !)
- Le passage Net::SSH à SSHKit pour la librairie SSH qui a permis beaucoup d'évolution sur les composants suivant : logging, formatting, SSH, connection management and pooling, parallelism, batch execution
- Mecurial, Subversion, et CVS Support ne sont plus supportés
L'annonce détaillé de Capistrano 3 est disponible ici.
Donc beaucoup de changement qui ont un impact fort sur Capifony, ce qui a déclenché cette belle PR sur le rewrite de Capifony pour sa version 3. C'est Peter Mitchell, le nouveau lead developer du projet qui porte le sujet.
A suivre donc...
Conclusion
J'espère que cet article va donner, aux utilisateurs actuels de Capifony, l'envie d'aller un peu plus loin sur le sujet et permettre à ceux qui ne le connaissent pas de laisser lui donner sa chance.
N'hésitez à partager vos recipes Capistrano et autres retours d'expérience sur le sujet, je suis le premier preneur.