capistrano was the best Ruby on Rails deployment tool for a years, but if you want to apply DevOps practices in your project then you might want to try out ansible. It allows you to significantly simplify your operations by creating easy YAML-based playbooks. It’s good for configuration automation, deployments and orchestration. And most important - ansible is very easy to learn.
I’ve used capistrano previously for fairly complex multi-server deployments. I’ve also used chef for configuration automation. But I literally fell in love with ansible after trying it for the first time because it’s very easy to start using it and it does not requires server to run agent - it directly connects over SSH to the server and applies configuration. ansible fits into keep it simple principle - having one tool to rule them all makes more sense than using separate tools for configuration and for deployments.
If you want to start with ansible quickly then you can try out railsbox.io which will generate ansible playbooks for common Ruby on Rails deployments. Keep reading if you’d like to get familiar with ansible and to create deployment playbook manually.
Deploying with capistrano
capistrano deploys to the new dir each time and keeps the latest release as a symlink so that frontend and backend servers can both be configured to point to this symlink to serve latest application code. Typical Ruby on Rails application which is deployed with capistrano has the following file structure on server:
application/ shared/ config/ log/ pids/ system/ releases/ 20150312000000/ 20150313000000/ 20150314000000/ current -> releases/20150314000000
When you run
cap deploy, capistrano will create new release directory under
application/releases and copy the latest git snapshot into it. It will then run the following tasks:
- symlink configuration files from
application/shared/configdirectory to the current release
- restart server
capistrano is great, but if you’re already using ansible for configuration management or planning to start using it then it totally makes sense to get rid of capistrano as unneeded dependency and reproduce the same procedure with ansible playbook.
Deploying with ansible
ansible is using YAML for configuration and for actual commands. It has less flexibility compared to capistrano which uses Ruby DSL, but at the same time helps you to keep your playbooks simple. ansible also extremely good with orchestration and rolling deployments.
We need to create configuration file for our deployments first - it will reside in
group_vars/all/config.yml file. I will use configuration for railsbox.io as an example.
Let’s also create inventory file which ansible will use to associate real hosts with groups. Create file named
production and put the following into it:
It tells ansible that
railsbox.io host belongs to
production group. We will also use SSH agent forwarding so that it will be possible to deploy applications which reside in private repositories without need for uploading deployment keys. Create
ansible.cfg with the following contents:
[ssh_connection] ssh_args = -o ForwardAgent=yes -o ControlMaster=auto -o ControlPersist=60s
Since ansible is using SSH for running commands, we need to use OpenSSH ControlMaster feature to keep things fast. ansible will create SSH connection at the beginning and then will reuse it for all subsequent commands in play.
Now when we have configuration we can create the actual playbook for doing capistrano-style deployments. Create
Above playbook should be self-explanotary, but I wanted to outline a couple of moments.
Make sure that you’re pointing
git_url to SSH-type URL so that it uses SSH keys for connecting to git server. If you’re using private repository and you’ve enabled SSH agent forwarding, it will just work. You don’t have to upload your private key to the server.
You should run and deploy your application using non-privileged user. It will help to enforce the security, but can complicate the deployment sometimes. railsbox.io creates upstart script for unicorn server, but it has one problem - only root user can start and stop upstart services. That’s why we have to use
sudo. We’re limiting it to be able to run only 3 commands by creating
/etc/sudoers.d/railsbox with the following contents:
railsbox ALL=NOPASSWD: /sbin/start railsbox, /sbin/stop railsbox, /sbin/restart railsbox
Once everything is set up, you can run the following command to deploy:
ansible-playbook -u railsbox -i production deploy.yml