Refactoring the ansible-symfony2 deployment post

For a few months now, I've been maintaining the ServerGrove ansible-symfony2 deployment, which by the time was more of a proof-of-concept ansible galaxy role.

In search of deployment

When I was first looking into deploying our Symfony application, I've first tried Capistrano (who hasn't). Although I already worked with ansible at that time, using it for deployment, rather than provisioning was not the first idea I had in mind. After struggling with Capistrano and its ruby-based dependencies, I quickly realized: I need something else with more manageable dependencies and also easy configuration.

ansible-symfony2

After some research (read: using google), I ran into ServerGrove's deployment role. At that time I just copied it and rewrote most parts to fit my application. At that time the repo was already quite abandoned. Seeing the open issues and PRs, I decided to ask if I could maintain the repo and even develop it further - Added a few code pieces from my modified deployment, added testing and thought I was done.

Refactoring

Because some people seemed to use the role for their deployments - or at least as a base to start from - I decided to take it further and incorporate some of the key features of my custom build. This includes a RELEASE file for each release that holds the release's unique identifier, hooks for additional tasks, git shallow copy and splitting into multiple files. Full changelog here.

Updating tests to use docker infrastructure

Getting Travis.ci to do as I wanted for testing, took me quite a while, too ;-). Main problem here: If you use python as the docker container language for version constraining to 2.7 for ansible, only php 5.3.x is available. Using the "old" infrastructure with virtual machines also just brings 5.3.x around - and some more side effects like you really have to install everything yourself. So for current testing, I had to go with symfony 2.6.

Extra tasks: hooks

In my current production deployment for Motor Presse, I need some tasks done with gulp. We're heavily relying on gulp to compile our stylus files to CSS, build minified JS and generally handle asset versioning. So how to include tasks like that into the deployment when it's most likely only an edge case? One option would be to add another trigger like "symfony_project_trigger_gulp" - but more config means more tests and not every project uses something like gulp or grunt. The second problem I was facing was cache flushing. How to flush caches on/after deployment? Depending on the infrastructure this might be a php5-fpm restart - or a apache2 restart, because someone uses mod_php. In our special case, we're forking into the php process and manually trigger php code to flush APCu. These are only three examples which already put so much complexity into our deployment that it's almost not manageable. Thankgod there's the include statement. Using a configuration variable for a (role-external) task path, you can include this YAML file within a task of your role dynamically. In config:

---
symfony_project_post_folder_creation_tasks: "{{playbook_dir}}/hooks/post_folder_creation.yml"

and in the actual task:

---
- include: "{{ symfony_project_post_folder_creation_tasks | default('empty.yml') }}"

Nice about this approach is the errorhandling by including an empty default file. I'd have preferred a solution with something like "skip if no include is provided", but instead of a nice onliner, this would have been a file stat, checks if stat.exists and then include. At least 6 lines of code instead of just one, just to have a "skipped". Ok, I could add a debug to the empty file where it says "skipped: no hook defined", but... no real sense in that.

Future ideas

Since I'm actively using this role to deploy large platforms, I'm trying to improve it step by step. The 2.0.1-alpha release is a huge leap forwards to a clean and flexible role. For future releases, there's still a lot of work: As soon as ansible is released in a 2.x version and error handling is finaly upon us, I'd be glad to integrate it into this role. For my current production deployment I'm using a GELF message curl request to a graylog server to promote a deployment. But how to check if there's an error? Or how to add partial errors/skips to such a message? If you have further ideas, improvements, issues or comments, just let me know on github :-)

Categories: php, symfony2, deployment, ansible

Tags: php, symfony2, deployment, ansible