Managing PHP Deployment with Capistrano
Deploying applications written in PHP is easy, right? Just copy the files to the webserver and you’re done. It gets a bit boring and repetitive, especially if you’re like us and deploying often so our customers can begin trying out the new functionality as early as possible. It’s also prone to error, particularly if you have multiple servers in the mix, such as test or staging servers.
Wouldn’t you just like to type one command and know that it’s done correctly every time? In this article I’ll introduce you to Capistrano, which makes this possible. In a future article I’ll show you how we use it to deploy TYPO3 based web sites we develop.
What is Capistrano?
It’s a utility written in Ruby by the awesome Jamis Buck. It allows you to define your deployment by writing “recipes” of tasks. Many people think that because it’s written in Ruby (Jamis originally developed it to help deploy Ruby on Rails applications) that’s all it’s limited to. Although by default it expects to be deploying a Rails based application it’s pretty easy to use it to deploy anything or automate many remote server activities.
Getting Capistrano
First off you need Ruby getting and setting that up is beyond the scope of this article but it’s pretty straightforward. Here’s a guide that should get you started whether you use Windows, Mac OS X or Linux. It’ll also help you install RubyGems, a Ruby packaging system, which you’ll also need. Once you have those set up, it’s as simple as typing:
gem install capistrano
If you’re on Mac OS X or Linux you’ll probably need to add sudo to the front of that command
The Basics
I should warn you that you should be reasonably comfortable with the command-line of your chosen operating system; you don’t need to know shell scripting stuff but you should be able to navigate directories and execute commands from the command-line. The Capistrano tasks you will write are written in Ruby, you don’t need to be proficient in Ruby but it will help if you know a little.
To get started we ”Capify” a project by simply changing to the root directory of the project and typing capify . - this creates a Capfile and a directory called config with a deploy.rb file inside. If the config directory doesn’t suit your project, don’t worry it’s not required, it’s just the default—you can update the paths, etc accordingly in the Capfile.
The tasks are defined in deploy.rb. It’s just a plain text file, so any text editor will do for editing it.
image courtesy of hans s
Defining a task
A task is defined like so:
task :list_files, :hosts => "www.foxsoft.net" do
run "ls -la /user/local"
end
You would execute it with cap list_files. Capistrano will then run the command ”ls -la” on the servers specified by hosts, in this case our Foxsoft server. You can keep defining tasks in this manner. If you immediately precede the task definition with desc "detailed list of files in /usr/local" then when you type cap -T you should see something like:
cap invoke # Invoke a single command on the remote servers.
cap list_files # detailed list of files in /usr/local
cap shell # Begin an interactive Capistrano session.
Specifying the hosts every time is boring, so Capistrano has the concept of roles. You can define hosts that play a specific role and then specify that tasks only execute on servers in that role. In fact, by default if you don’t specify a role or a host then the task will be executed on all servers in all roles.
Logging on
Capistrano defaults to logging onto the remote servers with the same username you’re currently logged onto your local machine with. If you need to use a different username you can specify this in the host definition, or if they’re the same on all servers – and preferably they should be – then you can set it once with the :user variable. We’ll come onto those shortly. When you execute a task Capistrano will prompt for your password. That soon gets annoying so I strongly recommend setting up your SSH keys if you haven’t already and then add your public key to the remote servers.
Variables
Capistrano supports setting variables which you can reuse throughout your tasks. It sets, or expects, a few to start with. They’re set using the form:
set :varname, "value"
so to set the username you’d add
set :user, "the_username"
These are the typical bare minimum variables you need to set, along with the role definitions
set :application, "appname" # The name you'll be using for the application you're deployingset :repository, "git@github.com/user/project.git” # The location of your project’s version control systemset :scm, :git # The version control system you're using, Capistrano supports quite a few
A lot of TYPO3 people use Subversion so you’d probably use something like thisset :scm, :svnset :repository, "http://your.subversion.repository/repo/trunk/etc"
Roles are defined with
role :the_role, "server.name"
with the three typical roles being app, web and db
Here’s a typical snippet from one of our deploy.rb files.
The deployment process
Your application will get deployed from your specified source control repository into the directory specified by the deploy_to variable. In here Capistrano expects a shared directory where you can put files and folders that should stay around for every release, like user uploaded data, and a releases directory where it will checkout the files from the source repository. During a deploy it will also create a symbolic link called current which will link to the latest release in the releases directory. Depending on your application layout this current directory or one of it’s subfolders will be what you need to set your webserver’s DocumentRoot to. You should create a setup task that sets this structure up, along with anything else your application requires as well as any permissions. Here’s a simple one that does just that and sets up a log directory for Apache to log to. (You’ll see that this is in the shared directory)
namespace :deploy do
desc "Set up the expected application directory structure on all boxes"
task :setup, :except => { :no_release => true } do
sudo mkdir -p #{deploy_to} #{releases_path} #{shared_path}
sudo "chown -R www-data #{deploy_to}"
sudo "chown -R www-data #{releases_path}"
sudo "chown -R www-data #{shared_path}"
sudo "chgrp -R admin #{deploy_to}"
sudo "chgrp -R admin #{releases_path}"
sudo "chgrp -R admin #{shared_path}"
sudo "chmod -R 770 #{deploy_to}"
sudo "chmod -R 770 #{releases_path}"
sudo "chmod -R 770 #{shared_path}"
# create log directory for Apache
sudo "mkdir #{shared_path}/log"
sudo "chgrp -R www-data #{shared_path}/log"
sudo "chmod -R 775 }shared_path}/log"
end
end
Capistrano initially expects to deploy a Ruby on Rails application so there’s a few tasks we don’t need, we’ll just nullify them by simply redefining them:
namespace :deploy do
task :set_permissions do
# do nothing
end
task :restart do
# do nothing
end
task :migrate do
# do nothing
end
end
You can now run cap deploy:setup, which will run the task we just defined. Hopefully everything went swimmingly, if you made a mistake and it failed you should be able to correct the task and rerun the command until it all goes fine. Then you can run cap deploy:check which will do some pre-flight checks and make sure the major dependencies are satisfied.
If that’s successful then you can now run cap deploy which will check out the source from the repository and create the current symbolic link to it. And you’re done. Every now and then we all deploy a duff release, simply run cap deploy:rollback and Capistrano will return you to your last release.
Now that we’ve covered the basics, in my next article I’ll go into a bit more detail into how we deploy TYPO3 based applications with Capistrano.
image courtesy of jurvetson