Deploying with Deployer

For years, I have been using Deployer as my choice of tool for automated deployment of my personal projects.

I like it because it is incredibly easy and straightforward to set up, and is specifically designed for PHP projects so there are lots of pre-sets.

For this blog post, I will show you snippets from my Unix Timestamp Convertor and UniverseSite - both of which use deployer.

Installation

The first step is to install deployer on your machine. It is very simple. In the terminal, first download the phar file:

curl -LO https://deployer.org/deployer.phar

Then move it to your local /bin:

mv deployer.phar /usr/local/bin/dep

Then adjust permissions to allow it to be executable:

chmod +x /usr/local/bin/dep

Thats it. You should be able to run it now using the dep command.

Task File

Every step in your deployment is called a "task". These go into a file called deploy.php which should be attached to the project.

As you can see from the file name, the tasks are written in PHP (another reason I like this tool).

Recipes

Recipes are basically pre-built deploy tasks that can be used in your flow. There are a few third party ones which can be found here. The one that I always use is the composer recipe that allows me to run the composer file on deployment.

To include it, just require it on the first line:

 require 'recipe/composer.php';

I believe that the composer one is built in. To use a third-party one, you may first have to install it before including it by running:

composer require --dev deployer/recipes
Basic Deploy Tasks

Here is the basic deploy that I always start with. I will show you later how to add custom steps.

The first thing you have to do in deploy.php is add:

 namespace Deployer;

to the very first line so that all of the functions are available for use.

Configure the Server
First, we have to configure the server. So inside deploy.php, I have the following set up:

host('live')
    ->hostname('123.45.67.890')
    ->user('root')
    ->password(null)
    ->stage('production')
    ->set('deploy_path', '/var/www/my-project');

set('keep_releases', 2);

set('repository', 'git@bitbucket.org:antonydandrea/my-repo.git');

The first line give a name for this server, in this case, I am calling it "live". This means you can set up multiple servers and deploy to any of them in the same script. I also specify the IP address.

Next two lines are the username and password. The user should have privileges to read and write in the location of your project. Notice that I have set the password to null. This means that I don't have the password on the repository, instead, the terminal will ask me to enter the password when the deploy starts. You can use identityFile instead to specify ssh keys instead.

The next line is the stage. In this case its "production". This is useful for if you have different servers that you want to deploy at the same time. When you do the deploy command (later), instead of using the server names, you use the stage name instead.

The last line, sets the location of the deployment on that server. This depends how you have your vhosts set up.

set can be used to set global settings or it you call it from a host it will just be for that particular server.

The next bit, keep_releases, tells deployer how many past releases it should keep. When it does a deploy, it creates a symbolic link to the latest codebase (so you will need to adjust your vhosts to handle this). The old codebases are automatically deleted but this settings states how many should be kept. You should keep a couple for easy rolling back.

Finally, you specify the git repository where the code is. For this reason, the server needs to have git installed and have a user with privileges to download the repository. One thing I recommend doing is creating a deploy key. This means you do not leave the password on the server and the key only has read-only access.

Deploy Pipeline
Using the composer recipe, I usually start with this built in pipeline that handles all of the basic deploy tasks:

task('deploy', [
    'deploy:prepare',
    'deploy:release',
    'deploy:update_code',
    'deploy:vendors',
    'deploy:symlink',
]);

The first parameter is the name of the pipeline (which you will use when running this task). The second parameter is the list of steps.

As I said before, it fetches the git repository (the master branch) and puts it in a new folder in the deploy_path. Then it runs the composer command (so you will also need this installed on the remote server). Finally, it switches the symlink from the previous release to this new one once everything is done.

And that is it! This is automated deployment!

But you probably have some extra steps that you want to include, here is how you can do that and the extra steps that I usually include.

Custom Steps

One of the great features of deployer, is that you can do almost anything. Especially if you your extra steps can be run from the command line.

To create custom steps, first use the task function to create a task:

task('my_special_step', function()
{
    //do stuff
})->desc('This will print what is happening');

Then simply add it to the pipeline where you want to use it:

task('deploy', [
    'deploy:prepare',
    'deploy:release',
    'deploy:update_code',
    'deploy:vendors',
    'my_special_step'
    'deploy:symlink',
]);

One task that I like to do is called remove_files:

task('remove_files', function () {
    run("rm -rf {{release_path}}/install");
    run("rm -rf {{release_path}}/composer.phar");
    run("rm -rf {{release_path}}/composer.json");
    run("rm -rf {{release_path}}/composer.lock");
    run("rm -rf {{release_path}}/deploy.php");
    run("rm -rf {{release_path}}/.git");
    run("rm -rf {{release_path}}/.gitignore");
})->desc('Removing install');

I run this just before the last step and it just breaks links with the git repository and deletes other folders that I have in the repository but are not needed in the code.

You can see that I use the run function. This allows you to execute any shell command (so in this case, I am using the rm command). {{release_path}} is a magic variable that points to within the new folder that is created on deployment.

There are a whole bunch of commands that you can do which can be found here in their documentation.

If it can be run in the command line, you will be able to do it. For example, to update MySQL database as part of the process:

task('update_mysql', function() {
    run("mysql -uuser -ppass -e 'DROP DATABASE IF EXISTS database;'");
    run("mysql -uuser -ppass -e 'CREATE DATABASE database;'");
    run("mysql -uuser -ppass database < {{release_path}}/install/my_schema.sql;");
})->desc('Updating Database');

I usually have a "maintenance" page. To do this, I have a setting in a config file to turn it on and off. To add this setting in deployer, I use the command line to add and remove the line from the start of the file:

task('maintenance_on', function () {
    run("sed -i 's/maintenance_mode\: 0/maintenance_mode\: 1/g' {{deploy_path}}/current/config.yml");
})->desc('Turn on maintenance mode of existing file');

task('maintenance_off', function () {
    run("sed -i 's/maintenance_mode\: 1/maintenance_mode\: 0/g' {{release_path}}/config.yml");
})->desc('Turn off maintenance mode of new file');

{{deploy_path}}/current is the symbolic link to current release. So I put the maintenance mode on here. Once it has switched the new release, I remove the line from the new config file (which has it on by default).

Running the Deployment

Once you have all your tasks, it is time to run them.

In the same folder as your deploy.php. Simply run:

dep deploy live

Now this will work for what I have done above. But you can have different things here.

dep is required of course.

deploy specifies the name of the pipeline you created. You could have called this anything in the world.

live is the name of the server. Again you could have called this anything. Also, as I said earlier, you could also specify a stage here, to deploy to multiple servers.

That is it! Sit back watch the commands get run one by one, or go and make a cup of coffee and tell yourself "Good job!"

Summary

As you can see, it is very easy to start automating your deployment. You can literally break down the steps of what you currently do manually, and bit by bit, implement it here.

I hope this has been useful to get you started. The documentation can be found here. Good luck!


© 2012-2017