PhpRiot
News Archive
PhpRiot Newsletter
Your Email Address:

More information

07.12. One-click Deployment

Note: This article was originally published at Planet PHP on 7 December 2011.
Planet PHP

Today's topic is deployment. It's called one-click deployment for a reason: Developers are lazy.
It's hard to do less than clicking on one button, so that's our goal.

With the growing need for lower time-to-market and faster response to user feedback it is inevitable to not be limited by technical factors (there are enough other obstacles already). The focus lies on reproducible results.

So, what do we need? Actually, not much. Disregarding the tools and practices that build the foundation of agile software development, you only need a central build server. But you've already got that one covered, right?

If you don't, you should get one. It's a huge help to discover errors quickly and be alerted instantly. This usually leads to a shorter time frame until a fix is done. Tests are run continuously and new parts are integrated into the whole code base.

Everything written here is based on Apache ant, so the easy choices here are Hudson and Jenkins. We're focusing on Jenkins here, but we've successfully used some variation of this continuous integration/deployment script on both.

We've also used CruiseControl/phpUnderControl and Atlassian Bamboo in the past for continuous integration, so the deployment parts should work as well.

Back to the hard facts. We're using the Template for Jenkins Jobs for PHP Projects here for our basic checks, so let's assume you got it installed and your build server is working.

The basics

The typical workflow for a developer now consists of these steps:

  • write some unit tests
  • write some lines of code to get the tests to green
  • commit to git/svn, let's call it "release 1.32c"
  • build server integrates the commit automatically and runs all configured checks
  • build status is green (hopefully)


There's not much deployment in this, so a few more steps:

  • after the build is done, a new ant task is started, called "create-package"
  • developer clicks on "deploy-to-staging" (yes, another ant task)
  • some 5 minutes later the staging environment has "release 1.32c" installed

How does this work?

There are a few preconditions in our environment that haven't been named yet. Not all of them are hard requirements, but they make some things a lot easier:

  • all developers have a VM image for this project, based on Ubuntu 11.04
  • this VM image is personalized to a certain degree (user/hostname)
  • it is managed by puppet, all developers can edit puppet manifests
  • the staging environment also consists of 4 VMs
    • Ubuntu 11.04 for the 2 frontend nodes
    • Ubuntu 10.04 for the 2 backend nodes
    • (hindsight is 20/20, we should've used the same version on all 4, but it's not that bad)
  • the machine that Jenkins is running on is also based on that same development image. This helps fight issues with incompatible versions and build tools.


The frontend nodes use a setup including varnish, nginx, PHP 5.3+APC, ZF 1.11, Dojo 1.6 and Doctrine 2.1.
The backend nodes use MySQL(Master+Slave), Apache Solr, RabbitMQ, memcached and ejabberd.

Back to ant.

The task create-package is building a .deb package that is to be deployed to all the staging hosts.

It basically executes these steps:

  • git pull
  • ant phpunit (unit tests, integration tests)
  • ant clean (remove build artifacts)
  • ant compilejs (using Google Closure Compiler)
  • ant compilecss (compiling SASS to CSS)
  • dh_make (read the Debian New Maintainers' Guide for details)

And that's it.

The manually executed task is called deploy-to-staging and consists of these steps:

  • ant deb-sign (sign the package with the development team's key, optional)
  • ant db-staging-up (we're using Liquibase, so roll out the database changes)
  • copy the .deb to all staging nodes via scp

Here's one caveat: We've set up our staging (and production) nodes to include a directory (like /opt/repository) in /etc/apt/sources.list and puppet periodically checks if there are new packages in this directory, and installs it. Of course you could just call dpkg -i through ssh as well.

So far, so good. After waiting 5mins we've got our "release 1.32c" deployed to all 4 staging nodes and the testers can do their work.

Wasn't that easy?

We're not quite done yet though. Th

Truncated by Planet PHP, read more at the original (another 2608 bytes)