Latest: Genstatic, my first sip of coffee

Content with Style

Web Technique

Deploying PHP applications with Vlad and SVN

by Matthias Willerich on May 8 2009, 15:12

In my current day job I have to deploy a number of web apps every few weeks. The person I took over from did the whole thing in a manual and terribly cumbersome way:

  • exporting the project locally,
  • zipping it up,
  • moving it to the server,
  • unpacking and symlinking it, to finally
  • run a small script that would set the correct production config, chmod log and cache folders etc.

In the following article, I'm going to describe how I went from there to deploying my Zend Framework-driven PHP apps with Vlad. To save yourself some time, feel free to skip the back story, the pre-requisite of installing ruby, gem and rake, or go straight to the deployment recipes.

I did the above method for a while, and realized quickly how much time is wasted by this process. Remembering my efforts of deploying PHP apps with Capistrano, I felt it was time to set up an automated deployment routine. While re-reading the article, thoughts returned of how unwieldy and badly documented Capistrano came across, and I started looking for something more lean, that wouldn't scare of my non-ruby colleagues who might have to do my job when I'm away.

Very quickly I was lead to Vlad the Deployer, replacing one deployment app where the person in charge of the name had one tipsy field day with, well, another.

Taking it for its first spin, I felt that while there's a lot of confidence behind Vlad, the word "simple" crops up lots of times, but again, the documentation is a bit of a thinking man's manual; As soon as you go beyond the initial example it skips a few steps here and there, which can leave someone who is unfamiliar with the concept struggling. Maybe it's a case of "it's advanced stuff, just live with it".

Luckily I have my Capistrano experience, so I got started fairly quickly using Vlad for my own purposes. What I liked immediately about Vlad was that it declares as one of the project goals to "use the right tool for the job (ssh, rsync, etc)."

My initial goal was to use Vlad

  • to have a deployment one-liner,
  • to roll back if needed, and
  • to be able to share logs and other assets between releases

For the example below I created a demo Zend Framework application with Zend_Tool, but what I'm describing should work for any PHP app out of the box, actually for any app, if you add any potential server tasks yourself. This example is using Subversion, but there is other examples out there that'll tell you how to use it with Git and other SCM software.

Setup

To be able to deploy with Vlad you need to have the following 4 things set up:

1. unix/linux development platform with SVN

not much to add, Vlad works on Mac OS X and various linux flavours, but not under Windows. For this setup I'm assuming you're working with SVN.

2. server with SVN client

Your webserver should be able to work with SVN, and it should be able to reach your SVN repository.

3. install vlad dependencies

on OS X 10.5 you already have a rails installation coming with the developer tools; just do

% gem update rails

Alternatively and on earlier OS X, you can use MacPorts, to install Ruby with

% port install ruby

You might also be able to use fink, and on debian/ubuntu you can install via aptitude


% sudo apt-get install ruby
% sudo apt-get install rubygems

For other platforms and compiling from source see here:
http://www.ruby-lang.org/en/downloads

Next you need to install Rake. For all systems, to update gem and install Rake, do the following. You don't need to gem update if you started from scratch:


% gem update --system
% gem update

Gem may fail initially - if so this is due to a bug in the gem command and requires you to edit the gem script (found with the command which gem on the command line, it's probably in /usr/bin/gem) by adding to the require statements (under require 'rubygems'):


require 'rubygems/gem_runner'

And finally, to install Rake itself:

% gem install rake

There are 3 more dependencies,

  • Hoe
  • Rubyforge
  • open4

but they're installed automatically with the next step.

4. install vlad as a gem

To install vlad as a gem, all you need to do is

% sudo gem install vlad

while allowing for dependencies to be installed, which is set by default. Congratulations, you're ready to configure Vlad.

Pre-Configure

As Vlad is working closely with your system tools, you should set up your SSH for it first. You need to make sure the remote user is set up correctly, and you'll want some way of not having to enter your password a million times. Luckily I wrote about this recently. For the server I'd like to deploy to I added this. You might not need the ControlMaster/ControlPath, the important line is to set the remote user:


Host myserver.pretendco.com
    ControlMaster auto
    ControlPath ~/.ssh/master-%r@%h:%p
    User remoteuser

Now let's have a look at the server itself. There is a possibility that the web directory isn't writable by your remote user and you need to have root permissions. As you should use the root user for all this, and sudo croaks on remote commands, it's best to set your future releases directory to be owned by your remote user.

on the remote machine


% sudo chown remoteuser:remotegroup /path/to/releases/directory
% chmod 775 /path/to/releases/directory

Configure and Deploy

Next you need to create 2 files for Vlad: A Rakefile in your root directory, and a deploy.rb config file in application/configs. Your project should look a little something like this (yes, this is slightly abbreviated):

 |-application
 |---configs
 |-----config.ini
 |-----deploy.rb  <-- just created
 |---controllers
 |---models
 |---views
 |-library
 |-public
 |-Rakefile     <-- just created

At its core, the Rakefile is very straight forward; I've lifted the initial loading step more or less from the Vlad documentation, with the exception of setting the path for deploy.rb manually. Should your application have a root folder /config, you're following default convention and won't need the instructions in the curly braces. Further down I'm resetting the :update_symlinks task to link to shared logs and media folders, as well as linking the Zend library into my library. By default Vlad would set 3 shared folders: /log, /system and /pids, which I am all omitting here.


require 'rake'
require 'rake/testtask'
require 'rake/rdoctask'

begin
  require 'vlad'
  Vlad.load {:config => 'application/configs/deploy.rb'} # set path for deploy.rb
rescue LoadError
  # do nothing
end

namespace :vlad do
  # Clear existing symlink task so that we can redefine.
  Rake.clear_tasks('vlad:update_symlinks')

  # we've only got log to share
  remote_task :update_symlinks, :roles => :app do
    run "rm -Rf #{latest_release}/log && ln -s #{shared_path}/log #{latest_release}/log && chmod -f 666 #{latest_release}/log/*"
    run "rm -Rf #{latest_release}/public/media && ln -s #{shared_path}/cache #{latest_release}/public/media"
    run "rm -Rf #{latest_release}/library/Zend && ln -s #{shared_path}/Zend #{latest_release}/library/Zend"
  end
end

Next up is deploy.rb, the config file for the to-be-deployed app. The only 3 mandatory lines in all cases are :domain, :deploy_to, and :repository.


set :application, "Example application"
set :domain, "pretendco.com"
set :deploy_to, "/project/example_releases"
set :repository, 'http://svn.pretendco.com/example/trunk'
set :svn_cmd, 'svn --username svnuser --password svnpwd'

The default SVN command is svn, but in my case the SVN user is different to local and remote user, and for some reason the SVN password prompt from within the deployment step kept failing on me, so I added it right here. Not the safest thing in the world, but I'm using a limited read-only SVN user for these purposes, so that's good enough for me. Any suggestions of improvement are welcome.

For your convenience I have zipped up both files, or if you like, the whole demo project.

The first time you run vlad, you do


% rake vlad:setup vlad:update

You can skip the additional vlad:migrate vlad:start mentioned in the vlad documentation, as we're neither migrating DBs (this time), nor does Apache need to be restarted.

vlad:setup has now created a folder structure on your server that looks like this (abbreviated again):

 |-project
 |---example_releases
 |-----current           <-- link to your web application
 |-----releases
 |-------yyyymmddhhiiss  <-- your web application
 |---------application
 |---------library
 |-----------Zend        <-- link to Zend Framework
 |---------log           <-- link to shared logs
 |---------public
 |-----------media       <-- link to shared media
 |-----revisions.log
 |-----scm               <-- folder used to checkout release
 |-----shared
 |-------log
 |-------media
 |-------Zend

vlad:update has exported your application from the HEAD of your SVN repository, pushed it into a release folder, and pointed /current to this release. This means once you've verified this initial deployment step you can set your Apache webroot to point to /project/example_releases/current. You can also see that the folders /log, /public/media and /library/Zend have been symlinked, so you'll want to move all your dynamically created files into the shared folder now.

As a side note, I've had two major criticisms about this, out of which I have fixed one in this example: The first one was that initially I didn't include Zend Framework in my release, but rather added it to the global include_path on the server. "Your applications should be self-contained!", Pascal was shouting from his desk, including some expletives. The other thing he flagged up was that the version of my application config checked into SVN was set up for the live environment, so that I didn't have to change a thing when deploying. I have fixed this in the meantime as well, with a set of other convenient changes, but you'll have to be patient for me to write my next post about it and stick with this solution for now.

The good news are that you're ready to go now! To release a new version:

  • check in your changes into SVN
  • make sure that the repository URL in deploy.rb points to the same as yours
  • run % rake vlad:update to deploy your app
  • put your feet up, and enjoy the rest of your day.

If your release is broken, roll back with

% rake vlad:rollback

This will remove the last release and symlink to the previous version.

As a final word of warning: Using any means of deployment that rely on your code versioning software mean that you have to keep it clean. Only check in code you've tested locally, don't use the branch you deploy from as a backup mechanism. If you and any other people working with your code follow this advice, Vlad will save you lots of time, and turn the pain of updating a site into the joy of releasing something new.

Comments

  • I'm very new to developing PHP applications, so I don't quite follow the benefit of deployment tools as you've discussed. I figured it was just as simple as what you had stated at the very beginning of your article. I mean, zip up the directory structures, unzip them onto the server and change 2 config variables to make it function. That's pretty simple in itself... so learning and writing all the code you've provided seems like a bunch of extra coding/debugging. I've picked up writing code on my own as a hobby, so I am very interested in common/standard practices done by professional programmers and developers. I'm very interested in anything you may have to share in response. Thanks.

    by Jason on May 10 2009, 01:54 - #

  • Speaking for myself, it is never that easy to deploy something. Once the project hits a certain level of complexity, a deployment tool comes in handy to perform tasks that you might not have thought of. Some that come to my mind:

    • Tagging the release from your version conrol
    • Bundling up 3rd party software with it
    • Running unit tests before deployment
    • Providing a rollback facility
    • Deploy to multiple server instances

    Also keep in mind that in an agile environment one might have to deploy ten times a day onto a test platform, in order to give the testers access to the latest changes. Even if it's just the same zipping up, ssh-ing into the server, unzipping, changing two variables ... it's going to get annoying pretty fast.

    Last but not least it's worth mentioning that VLAD is just one flavour of tool, and there are other choices like ANT, PHING or even a straight forward good old shell script

    by Pascal Opitz on May 10 2009, 08:49 - #

  • Jason, there's definitely a bunch of extra coding and debugging in this approach, but it's a matter of write once and use in all eternity.

    If your sites are updated rarely, or with big anticipation, a deployment tool is a bit of an anti-climax: 5 seconds to open a shell and cd'ing your way to the project, then run the deployment one-liner. On the other hand I deploy something every week, and I want this to take as little time as possible.

    There are other benefits, as Pascal said, some projects need more tasks for deployment than others.

    by Matthias Willerich on May 11 2009, 11:54 - #

  • Take Pascal Opitz advice about tagging your production release.

    And carefull with the rollback. Rolling back an app is not as simple as going back one code release. You have to consider the database structure and data integrity resulting of each new change.

    by Nuno Gomes on May 12 2009, 11:05 - #

  • Do you know this other deployment tool for web applications, Fredistrano?

    by Fred on May 12 2009, 12:17 - #

  • Ha, Fredistrano is definitely a good name. I'll have to check it out at some point.

    @Nuno Gomes: This post was about the basics, wait for the next one for some more customization including the possibility to tag your release. I took a lot of stuff out of this example, because getting to this point thoroughly.
    I don't need any data integrity checks for my purposes, so I guess I'll have to invest some time in a good example. Let's see.

    by Matthias Willerich on May 12 2009, 14:55 - #

  • Aaaand if you've arrived in the comment section because above throws this error:
    undefined method `clear_tasks' for Rake:Module
    then time has thrown a spanner in the works: I've only been able to resolve this so far by explicitely installing vlad 1.3.2 (as opposed to the now current 1.4.0).

    I get the feeling this isn't the end of it, as the message clearly blames rake not vlad, but I'm investigating. If you've got any pointers, leave them here in a comment.

    by Matthias Willerich on August 14 2009, 12:14 - #

  • I experienced the same problem with 'clear_tasks' not being defined. To get around this, I put this code in my Rakefile:

    
    module Rake
      def self.remove_task(task_name)
        Rake.application.instance_variable_get('@tasks').delete(task_name.to_s)
      end
    end
    
    Rake.remove_task('vlad:update_symlinks')
    

    by Thomas Albright on September 5 2009, 00:52 - #

  • Regarding the 'clear_tasks' issue, this is from the Vlad FAQ:

    "NOTE: Rake.clear_tasks was moved to Hoe so it could be used more generally."

    require 'hoe/rake'

    namespace :vlad do
    # Clear existing update task so that we can redefine instead of
    # adding to it.
    Rake.clear_tasks('vlad:update')

    remote_task :update, :roles => :app do
    #custom update stuff
    end
    end

    by Dan Blaker on November 8 2010, 14:51 - #

  • Oh how annoying:
    I get pinged on so many posts with spam, but the 2 interesting ones I seem to have not seen or not received. Dan, Thomas, thanks for this, I'll have a look at it.

    by Matthias Willerich on November 11 2010, 08:29 - #

  • Hurrah,
    Dan's approach works just fine. my setup was all mangled up so I did this:
    - sudo port selfupdate
    - sudo port upgrade ruby19 (it was already installed, otherwise it'd be sudo port install ruby19). This already contains a fairly new version of gem
    - sudo port install rb19-hoe
    - sudo gem update --system
    - sudo gem install vlad

    And now it all works fine again! Re-sult!

    by Matthias Willerich on November 24 2010, 14:20 - #


Comments for this article are closed.