Implementing a deployment strategy with Phing

Having a good deployment strategy can save you a lot of time and pain. Commonly most developers will start out by deploying projects over FTP, tracking any changes for new features and re-uploading as required. For smaller projects or when you’re working alone this simple system can be perfectly adequate, but when your projects and development team get bigger, tracking all the changes can be a logistical nightmare. In addition to this re-uploading an entire site via FTP and manually updating a database is tiresome. Surely there's a better way...

In this article we’ll be looking at how to set up a deployment method that can be triggered with a single command to execute a range of tasks defined in a build file. The goal will be to create an automated process for taking care of your backups, sql updates and deployment of code. To achieve this we’ll be implementing a piece of software called Phing.

 

Pre-requisites

Version Control

In my previous post I introduced you to version control, you’ll need this up and running to implement today’s deployment strategy.

Server

To utilise a deployment strategy you’ll need somewhere to deploy to! This article is orientated towards those with their own server (PHP 5.2 or above will need to be running). While I’ll be documenting all the steps, I am assuming the reader is comfortable with basic Linux commands and making changes to the server.

What is Phing?

Phing is the PHP version of Apache’s Java written ant. It is a deployment tool that lives on your server and allows you to create xml ‘build’ files that can execute a range of commands for various tasks. To illustrate its capabilities I’ll outline the deployment strategy we’re seeking to accomplish today…

The deployment strategy

  • Two branches will be created in your version control; one for ‘development’ and one for ‘production’. If you don’t have a ‘development’ environment you can just use the latter.
  • Once code is ready for deployment to an environment, that code will be merged and committed to the appropriate branch using version control.
  • SSH is loaded and a Phing build file is executed by typing ‘Phing’ in the appropriate directory.
  • This will execute your Phing build file which will:

    • Backup your database
    • Take a full copy of your web site to roll back to in the event of a problem
    • Check out the latest version of the code using SVN from the environment branch to the server
    • Redirect your site to a temporary ‘Updates in progress…’ page
    • Execute any MySQL database changes
    • Redirect your site to the new version

What does this achieve? We don’t have to worry about database or site backups, we don’t have to track or manually upload any files and everything is executed in order with minimum impact on your users.

Why Phing?

There a many different ways to accomplish deploying code with or without version control or Phing, this is simply a method that has worked well for me. As an alternative example, the version control software Springloops I recommend in the previous article has its own method and interface for deploying code without getting your hands dirty with the command line (and there are many other excellent services such as Beanstalk which do this very well).

I personally like to use Phing due to the ease of automating all your processes including running tests, executing server commands and the connectivity to version control without being constrained to a web application interface. The build file contains all the steps involved in your deployment process which you have full control over and can be easily transferred to other environments. I should also point out that Phing can easily be integrated with a range of other services including the above mentioned. Oh, and it’s free :)

So with no further ado, let’s get started…

Installing Phing

The first step is to get Phing installed on your server. To do this, connect via SSH and switch to the root user so we have the right privileges.

su root

The easiest way to install Phing is using the PEAR installer, if you don’t already have it then run the code below, or if you’d prefer to use another method there are alternatives on the Phing web site:

apt-get install php-pear

With PEAR installed you can now install Phing:

pear channel-discover pear.phing.info
pear install phing/phing

DEPENDENCIES

You’ll also need to ensure the SVN package is installed on your server, you can get it using:

apt-get install subversion
pear install VersionControl_SVN-0.5.1

Configuring the site

The next part is a little tricky. Normally you would set your vhost record to point directly to the sites publicly viewable files, but because we’ll be uploading new versions of the site on each deployment we’re going to change the structure slightly:

(Directories inside your root project directory)

- site (symlink)
- deployments
     - database
     - holding
          - public
     - deployment_080613
          - public
          - other site directories and files…
     - deployment_090613
          - public
          - other site directories and files…

The vhost record will then be updated to point to yourproject/site/public, but the site ‘directory’ will actually be a ‘symlink’ that points to the latest version of your site. The benefit of this is that you can easily roll back to a previous version by changing the symlink to a previous deployment directory. If you’re not familiar with symlinks you can think of these as shortcuts to other directories.

So inside your ‘www’ directory where your sites are stored on the server, setup the directory structure above using the commands below. Note we’ll also be adding a dummy deployed site directory with today’s date and an index file to get us started:

mkdirmyproject
cd myproject
mkdir deployments
cd deployments
mkdir database
mkdir deployment_10062013
cd deployment_10062013
mkdir public
cd public
touch index.html
nano index.html

Type ‘hello world’ and press CTRL+X to save, press ‘Y’ and enter.

Now we’re going to add one more thing to this structure which is a holding page for when we’re running updates.

cd ..
mkdir holding
cd holding
mkdir public
touch index.html
nano index.html

Type ‘Updates in progress…’ and press CTRL+X to save as before, press ‘Y’ and enter. Note, you’ll probably want to come back and style this file later but you get the idea for now.

So now we’ve got the directories, test file and a holding page setup, next to create the symlink:

cd ../..
ln –s deployments/deployment_10062013 site

The symlink command takes a destination and alias, so the ‘site’ symlink will now direct to the dummy deployment directory. With everything setup, all you need to do now is configure your vhost record to point to myproject/site/public (setting up sites and vhost records is beyond the scope of this article and varies depending on your server, for Ubuntu users rackspace have a good guide here).
Don’t forget to restart apache; once that’s done you should be able to browse to your domain and view the ‘hello world’ message.

Creating a build file

Now onto the fun part. We’re going to put together an XML build file for Phing to process, I’d recommend storing this file inside the ‘deployments’ directory where it isn’t going to get overwritten. I mention this first as you can either create and edit the file directly on your server via SSH, or you can create it on your local computer and FTP or copy it up afterwards. 

The build file should be titled ‘build.xml’ if you want Phing to read it automatically, otherwise you’ll have to pass in the file name when you want to execute it later.  So let’s go ahead create the main structure:

<?xml version="1.0"?>

<project name="build" default="deploy">
<property name="exportpath" value="/path/to/deployments" />
<property name="rootpath" value="/path/to/project" />
<property name="svnpath" value="/usr/bin/svn.exe" />

<property name="svn.username" value="username" />
<property name="svn.password" value="password" />
<property name="svn.repo" value="https://yourrepositoryurl.com/branches/development" />

<tstamp>
<format property="build.time" pattern="%Y%m%d-%H%I%S" />
</tstamp>

<!-- Database vars -->
<property name="db.name" value="database" />
<property name="db.user" value="username" />
<property name="db.password" value="password" />
<property name="db.backup.dir" value="${exportpath}/database" />
<property name="deploydir" value="deploy_${build.time}" />
<property name="doctrinedir" value="${exportpath}/${deploydir}/scripts" />

<taskdef name="symlink" classname="phing.tasks.ext.SymlinkTask" />

<target name="deploy">
</target>
</project>

Let’s break this down.

The property lines will define variables that can be used throughout the build file; to start with we’ve defined some paths that we can re-use.We've also got a set of properties for the version control connection details, and another set for the database connection details.

‘Project’ is the containing node which uses the ‘default’ attribute to look for a target node; this in turn will seek that target in the code and run any commands inside the XML. Note you can add as many targets as you like and pass this in as a variable when you run Phing to execute different tasks.

The taskdef line allows us to use Phing's symlink command later in the script and also I've added an additional path for doctrine, though you won't need this if you're not using it.

Next we'll add some commands inside the default target to back up the database:

<!-- Backup the database -->
<exec command="mysqldump --quick --password=${db.password} --user=${db.user} ${db.name} > yourproject_${build.time}.sql"
dir="${db.backup.dir}"
escape="false" />
<echo message="Database dumped ${db.backup.dir}/yourproject_${build.time}.sql"/>


So you can see above, the ‘Command’ attribute lets Phing know you want to run an SSH command, and in this example we are doing a SQLDump of the database. Notice how we're using the properties we defined above and also how you can use echo messages to update the command line with what tasks are complete.

The next step is to do an SVN export:

<!-- Get an export of the latest development / production branch -->
<svnexport
svnpath="${svn.path}"
username="${svn.username}"
password="${svn.password}"
nocache="true"
repositoryurl="${svn.repo}"
todir="${exportpath}/${deploydir}"
/>

<!-- Chmod any writeable directories -->
<exec command = "chmod -R 777 ${exportpath}/${deploydir}/public/images/captcha" />
<exec command = "chmod -R 777 ${exportpath}/${deploydir}/docs/error.log" />
<echo message="Updated CHMOD for any writeable directories or files"/>

This code relies on the SVN dependency and allows you to check out code from an SVN server. The ‘todir’ sets the new directory which will get created for you in your deployments directory we created earlier. The last few lines demonstrate how you can update the permissions on certain directories if they need to be writable (such as the error log). 

So with our database backed up and all the code in place,  now we want to run any MySQL updates. The only problem is – any commands that take a long time could affect site users. So before we run any SQL we’ll direct our visitors to the temporary holding page we created earlier.

<!-- Direct the site to a holding page while the database updates are run -->
<symlink
target="${exportpath}/holding"
overwrite="true"
link="${rootpath}/site"
/>

The above code updates the symlink to direct it to the holding directory which contains the ‘Updates in progress…’ file. Note you’ll need to set the ‘overwrite’ attribute if the symlink already exists, which in our case it does. So while our site is directing to the holding page we can run the SQL without worrying about how it will affect our users. Now there are different ways of executing SQL which may depend on how your project is built. You could simply store all your SQL changes in a text file and execute this, or if you’re using something like Doctrine ORM you could also execute these commands directly via SSH. Here’s a couple of examples:

<exec command = " /usr/bin/mysql –user=${db.user} –password=${db.password} –database={db.database} < ${exportpath}/${deploydir}/sql/updates.sql" />

OR

<!-- Update the database using doctrine -->
<exec command="php doctrine.php orm:schema-tool:update --force" dir="${doctrinedir}" />
<exec command="php doctrine.php orm:generate-repositories ../library" dir="${doctrinedir}" />

Great, so code is deployed and the database is up to date! The last part of the script is to direct our users to the newly deployed code:

<!-- Update the symlink to direct to the latest deployment version -->
<symlink
target="${exportpath}/${deploydir}"
overwrite="true"
link="${rootpath}/site"
/>

With everything setup all that is left to do is upload or copy the Phing build file to your server (in the deployments directory titled build.xml), browse to the directory and type ‘Phing’ (if you get any errors, typing ‘Phing –verbose’ can help provide a bit more information when debugging). All being well you can now go to your web site and view the updated code.

The Next Level

So today’s tutorial should give you a good introduction into how Phing can be utilised to create an automated deployment strategy, but there is so much more it can do. To give you some ideas on how this deployment strategy could be developed:

  • Post-commit hooks can be setup with your version control so that after you’ve committed to the development branch it would automatically send an SSH command to trigger Phing – so you wouldn’t even need to login to your server!
  • Testing – Phing can execute unit tests for you and stop on failure, this is a great way to ensure only safe and ready code is being deployed.
  • Creating additional targets – what if you needed to roll back to an old version? You could create a ‘rollback’ target that would switch your symlink back to an older version and / or restore the previous database.

To conclude, Phing is an incredibly flexible piece of kit with a range of options for achieving a deployment strategy that will suit your needs. Now go code something to deploy (with Phing).

Troubleshooting

Authorization failed

If you get this type of error being returned it may be due to the server certificate. This can sometimes be resolved by running the command 'svn ls http://repository/url' and then permanently accepting the certificate if this is an available option.

Resources

Phing Setup Guide: http://www.phing.info/docs/guide/stable/chapters/Setup.html

Sign Up

NEXT: Getting Started with Version Control

There are a handful of developer technologies that can revolutionise the way that you work. Version control is one of them and an absolute must if you’re working in a team. In this article we’ll be looking at what version control is, how it can help you, and where to start.

comments powered by Disqus
Sign Up

Popular Tags

350x250

Need a web developer?

If you'd like to work with code synthesis on your next project get in touch via the contact page.