So you've just built a fancy web application and you're planning to put it online. This can be done in many ways. In this article I'd like to cover one approach to deploy your backend system to your production server. We'll go through the following steps by example of a Laravel application but this can be applied to any other language or technology.
Update
This article was updated to Capistrano 3. More about the new version can be found on capistrano's website.
The Past
Perhaps you have already put some websites online in the past. Probably you've used a FTP client and uploaded the bits and bytes by hand. Or perhaps you always logged into your server via ssh and pulled the changes manually.
The Idea
Our goal is to simplify this process as much as possible. The idea is to use your code repository as a source for every deploy. The deployment tool, in our case capistrano, will automatically log into your server and build your system right out of your repository.
Software deployment is all of the activities that make a software system available for use. - Wikipedia
What You'll Need...
...on Your Remote Server
Your remote server needs to provide ssh access. It also should have installed all necessary dependencies for your project such as GIT, PHP, MySQL, Composer, ... Besides that, you don't need any extra software on your production server.
...on Your Local Machine
In order to install and use capistrano, you need at least Ruby 1.9 (if you don't have Ruby installed, I recommend installing it using rbenv). To install capistrano, you simply have to run:
$ gem install capistrano
So why capistrano, you may ask. As always, there are many ways to accomplish a task but in my case, capistrano always seemed to be the easiest and most flexible approach. You can configure it to all your needs and there are a lot of plugins out there which simplify your work again.
Capistrano is a utility and framework for executing commands in parallel on multiple remote machines, via SSH. It uses a simple DSL (borrowed in part from Rake) that allows you to define tasks, which may be applied to machines in certain roles. It also supports tunneling connections via some gateway machine to allow operations to be performed behind VPN's and firewalls.
Prepare
Now we have everything we need, so let's setup our deployment settings. But first we have to create a folder on the remote server where all the files should be deployed to. Log into your server with SSH and create a folder. A common place is /var/www/
. So let's do this:
$ sudo mkdir /var/www/my-app $ sudo chown -R username:group /var/www/my-app
That's it. There is nothing more to do on the remote server, so you can close the ssh connection and move on. Go into your project (or any other folder, that doesn't matter right now) and run:
$ cd my-project $ cap install
This command will create the basic files we need. After that your folder should look like this.
. ├── Capfile ├── config │ ├── deploy │ │ ├── production.rb │ │ └── staging.rb │ └── deploy.rb └── lib └── capistrano └── tasks
The Capfile
is like the mount point for capistrano but for now we'll just need to edit config/deploy.rb
and config/deploy/production.rb
. The first file is responsible for all the building steps, the second file represents a "stage". You can have several stages like production, staging, testing... In each stage-configuration file you can specify your server(s). Let's open those two files in your favourite text editor and replace the content with the following snippets. We'll go through the code afterwards.
We'll start with config/deploy/production.rb
:
role :app, %w{[email protected]} # EDIT your ssh username and server ip address set :ssh_options, { auth_methods: %w(password), password: "" # EDIT your ssh password }
Next we'll modify config/deploy.rb
:
set :application, "Your app name" # EDIT your app name set :repo_url, "https://github.com/laravel/laravel.git" # EDIT your git repository set :deploy_to, "/var/www/my-app" # EDIT folder where files should be deployed to namespace :deploy do desc "Build" after :updated, :build do on roles(:app) do within release_path do execute :composer, "install --no-dev --quiet" # install dependencies execute :chmod, "u+x artisan" # make artisan executable end end end desc "Restart" task :restart do on roles(:app) do within release_path do execute :chmod, "-R 777 app/storage/cache" execute :chmod, "-R 777 app/storage/logs" execute :chmod, "-R 777 app/storage/meta" execute :chmod, "-R 777 app/storage/sessions" execute :chmod, "-R 777 app/storage/views" end end end end
You now have to put your data in every line with an #EDIT
comment (ip address, git repo, ssh user, password, etc). The :deploy_to
variable should be the folder we just created. Your webserver (Apache, Nginx, ...) should point to /var/www/my-app/current/public
.
In the namespace :deploy
block of the deploy.rb
file you specify what actually should happen for each deploy. So there are two tasks. In the build
task we install all your PHP dependencies, just like you're used to it during development. After that we make the artisan file executable in order to use it for migrations. In the restart
task we fix the permissions for the storage folders.
All those tasks are invoked in the following order. You can hook into each task if you need to but for now we stick with our simple configuration.
deploy:starting - start a deployment, make sure everything is ready deploy:started - started hook (for custom tasks) deploy:updating - update server(s) with a new release deploy:updated - updated hook deploy:publishing - publish the new release deploy:published - published hook deploy:finishing - finish the deployment, clean up everything deploy:finished - finished hook
Every deploy is stored in /var/www/my-app/releases/
. The built-in task deploy:publishing
creates a symbolic link of the recent deploy to the current
folder. This way you can keep older releases and switch versions without going offline for a second. When this task ran, your newest version is online.
You can easily add your own tasks if your build process requires some extra steps. For more detailed information, I recommend you reading the official documentation.
After these basic configurations steps we are prepared for our first deploy.
Fire!
So that's the moment you were waiting for. The hardest part is done. For now on every time you want to deliver your application updates, you just have to run the following magical command. Capistrano will read your config/deploy.rb
config/deploy/production.rb
files and run each task. If a task fails, the deploy will stop and the old version is still online.
$ cap production deploy
You will see a bunch of text output and after little time (depending on your server) everything should be complete. That was easy, wasn't it?
Note: For now we just setup our production stage, but you could replace production
with another stage, for example your testing server and run $ cap staging deploy
.
Further Thoughts
Security
Perhaps you might be a little worried if you have to put your plaintext password in the configuration file. I just choosed that way to make the demonstration as straight forward as possible, but in the real world you might want to use a SSH key. You can import one like this:
set :ssh_options, { keys: %w("/path/to/your/key.pem"), # EDIT your ssh key auth_methods: %w(publickey) }
Database
For now we have just focused on deploying the actual files to their new home but in many scenarios you might also do something with your database. Laravel has a perfect tool for that: migrations. You could just add a new step where you run these migrations. After that our build task might look like this:
desc "Build" after :updated, :build do on roles(:app) do within release_path do execute :composer, "install --no-dev --quiet" # install dependencies execute :chmod, "u+x artisan" # make artisan executable execute :php, "artisan migrate" # run migrations end end end
You also have to add this task in the transaction
block of the update
task. Now everytime you deploy, the database will be updated to your latest migrations.
Rollback
Sometimes you deploy a non-working version of your application and you need to undo these changes. Capistrano has a built-in feature for that called "rollback". Just run:
$ cap production deploy:rollback
Conclusion
You've just learned a very simple way of deploying your application to your production server(s) with Capistrano. Once the configuration work is done, it just takes one command to deploy your latest version in seconds. But as mentioned earlier, this is not the only way to do this.
You should also check out the task runner grunt which suits perfectly for building and deploying JavaScript applications. A complete different approach takes docker which acts like a lightweight VM. The idea here is to deploy your whole environment as a virtual machine. Check them out!
Comments