In simple cases, if you use Heroku, application deployment process can be as easy as one shell command. But Heroku does not provide enough scaling and flexibility for more advanced scenarios or more serious load.
If you need to test something and then be able to expand to thousands of requests per second, EC2 from Amazon Web Services is definitely the way to go. It provides you with a virtual system which is totally under your control. You can add additional storage, move storage between servers and increase CPU/memory in almost real-time.
The downside, though, is that you have to setup the whole application infrastructure by yourself: from frontend servers to deployment scripts to security customizations. There is no preferable way of doing one thing or another, so here I’m offering what worked perfectly for me, and what I was not able to find while surfing the Internet for solutions.
The problem we’re solving is Rails app deployment on a remote Amazon EC2 instance with Capistrano.
I’m considering Capistrano as the core component of the solution. Capistrano is used for application deployment and is pretty much industry standard these days. You can find lots of deployment recipes for it on the Internet. With Capistrano we can deploy Rails applications on remote servers, pre-configure databases, precompile assets and do some other fancy stuff.
RVM is used to install the latest Ruby version on the machine no matter the packaging system used. It allows to keep multiple Ruby/Rails versions and enables easy switching between them.
Bundler is a part of Rails installation. Bundler offers very efficient gem version control and deployment system. It will be used with Capistrano to install the necessary gems on the target machine.
Unicorn is a high-performance Unix-like Rack application server. We will be using it as a backend system here. nginx acts as a frontend, but its configuration is out of scope for the current post.
Directory Structure & Users
The directory structure is important for the two reasons:
It provides a skeleton for services configuration.
It serves us to harden application security.
The user we will be deploying our app under is _ec2-user _(standard Amazon EC2 user). We will also be using sudo to drop current user credentials and start Rails application under _rails _user. _rails _user is not allowed to write in our app’s dir, which is a good additional security measure.
This is the final directory structure after $ cap deploy:setup and $ cap deploy commands have been run. You can see custom permissions on log and pids folders, as we want to allow unicorn (after doing $ sudo -u rails) to be able to write into them.
The unicorn configuration is pretty typical:
This is the most interesting part, as we glue all the components together:
That’s pretty much it. I always believed that reading code is the best way to understand what the author wanted to say. So here you will find mostly real-world examples and scenarios, with little or no additional description. If you don’t like this format or finding a hard time understanding what I’m writing about, please feel free to give your feedback below.