Hey, Ruby web developers! Having trouble asking deployment questions? Not sure what Provisioning or Orchestration are? Getting bad results Googling your questions, but not sure why?
Let’s go over some deployment vocabulary from your point of view — that makes it a lot easier to Google and to read information about deploying your app to a server, and setting up that server in the first place.
As a nice side effect, you may have an easier time talking to your Ops guy at work.
First, let’s go over what the word “deployment” means:
‘Deployment’ is the process of putting a new application, or new version of an application, onto a prepared application server.
If you are talking to a developer, it may also mean the process of preparing the server, perhaps by installing libraries or daemons. If you are talking to an operations professional, it DOES NOT. They use the word “provisioning” for that.
(Developers are going to keep using the word “deploy” for everything involved in getting our app and its dependencies installed. That’s cool. But don’t be surprised if you confuse your Ops guy.)
Are you deploying a Rails app? Here are lots of things you’ll probably have to watch for as you do… I’ve been writing an open-source gem and a (edited to add: now defunct) for-pay online class about this for months now. Let me share some things.
There are lots of big reasons that deploying a Rails app is hard. And lots of well-known, big tasks. But when you’re provisioning a server and deploying your application to it, lots of little things go wrong too.
I feel like the big things get a lot of respect already. Let’s look into some of the under-appreciated little things you have to get right in order to make a good deploy happen.
For starters, a lot of these small issues can require a lot of debugging. You know those two-line fixes that take three hours to find? Deployment is full of those. Here are some of mine to speed you on your way.
You can’t compile recent Ruby on a VM with less than approximately 1GB of RAM. The amount varies a bit, of course.
When you try, you discover that GCC gives an internal compiler error as its “out of RAM” notice. Also, that the RVM cookbook tells you a completely wrong directory for the six logfiles to check for errors. At least, if you install RVM in user directories.
(Also known as “why doesn’t Capistrano pick up my changes???”)
One of the cool things about Capistrano is that you don’t even have to put your Capistrano files into your app repo. You can put the Capfile and config/deploy.rb and so on in a different repo, or its own repo, or no repo or whatever.
One of the really infuriating things about Capistrano is that it’s designed to allow you to do that. See the bottom of this post for a quick workaround, or just keep reading.
A few weeks ago, I was hacking away on prototype software for my new class. I was doing that horrible thing where you make a few changes, hit “rebuild” and wait for five minutes to find out if you fixed the problem. And I realized something surprisingly awesome about configuring servers.
It’s easy to find posts about how to set up a Rails server manually. There are a ton of them out there.
It’s also easy to put off setting up Chef until later on. After all, how often are you going to set up a server? Do it manually, then automate when you need to, right? That’s the tradeoff.
Why would you use Chef before you had to?
That’s a good question. Let’s talk about that.
If you use Vagrant you can easily put together a test server. Unfortunately, if you just type a bunch of commands into your Vagrant server, you’re always at the mercy of the VM going away.
Or you can use Chef. And then you can always get a clean new VM matching your server pretty well.
With a test server, you can answer questions like, “will this work if I type it on the production server?” And you can do it without crashing production. So that’s kind of cool.
Deployment comes with far too many tools. For instance, this whole set is often used together:
And that’s not counting the smaller things like Vagrant plugins, Capistrano plugins, the many Chef cookbooks and so on, which would also be used with these. And it’s definitely not counting the many, many competitors. That whole list is just one stack of tools.
For some of them, it’s clear why you use them. VirtualBox? Yeah, okay, something has to make a VM if you test locally.
But why do you need both an “app deploy” tool (Capistrano) and a “server configuration” tool (Chef)? Can’t your server configuration just include your app? For that matter, if your “app deploy” tool runs scripts as root, can’t it configure your server?
Some people do use just one. It’s possible to deploy apps through Chef, but please don’t! It’s possible to do all your server configuration through Capistrano — really, really don’t.
For best results, think of Chef and Capistrano as two separate tools for two different jobs.
So what are those jobs?
First off, let’s just say: Heroku is awesome. There are all kinds of great reasons to use it, and I would never, never claim that Heroku is wrong for all deployments.
But it’s also not right for all deployments. Let’s talk about when it’s wrong and when it’s right.
Heroku’s big advantages are:
Those are big advantages. Sometimes they’re the most important thing for your project. When they are, you should absolutely use Heroku.
But why might you not?
First, the elephant in the room: the cost. Heroku gives you limited free hours per month, and after that it’s expensive. Background threads or processes also cost extra.
A single minimum-power dyno runs you $36 per month (note: pricing is out of date), assuming you need barely any space in Postgres. Heroku is basically going to cost you at least twice what a VPS would cost (e.g. Linode or Digital Ocean) and give you much less power. If you process many simultaneous requests or run many background threads, it will be at least three or four times as expensive for the same power.
For a powerful server you’re actually using, it’s easy to pay $250/mo for what Amazon might have delivered for $100/mo or Digital Ocean for $80/mo.
A reader recently asked me why Ruby web app deploys usually have a web server (NGinX, Apache) and an application server (Unicorn, Thin, Puma, Racer, Mongrel, Passenger, Jakarta, TorqueBox or whatever I’ve forgotten this week).
Actually, he asked, “why do I need to run NGinX and Unicorn?” It’s a fair...