How I deploy a Rails app using Docker

Michiel Sikkes
on
September 22, 2014

This blog post shows how I deploy a Ruby on Rails app that runs in a Dockercontainer on a DigitalOcean server. To keep it simple, I’m going to explain a very manual process on simply getting an app deployed inside a Docker container.

In this post I’ll show you:

  • How I installed Docker on a server
  • The Dockerfile for my Rails app
  • Building with bundled gems from Gemfile
  • Building with compiled assets
  • Running my app with Docker
  • Docker environment variables for database.yml

Let’s get started with the installation on a server.

Installing Docker on my server

First, I boot a new Ubuntu 14.04 server on DigitalOcean and install Docker:

CODE: https://gist.github.com/jansn/27177efbbcb0a350d4ba1a205b47bb83.js

The Dockerfile and nginx.conf

Now I’m going to build a Docker image from my Rails app. It happens that Jeroen blogged about this last week: How I build a Docker image for my Rails app. I am using his blog post as a base for the next steps.

I’ll be building the Docker image on the same server that I want to host my app. I build it on that server because I want my application to be private, so using the public Docker registry is not an option. I could set up my private Docker registry but then I would have to maintain it, and I don’t want to do that at the moment. In this post, I’m looking at the simplest way to use Docker to host my app.

In my Rails project intercity-website, I added the following Dockerfile and Nginx config:

Dockerfile

CODE: https://gist.github.com/jansn/d991b5f9edc4e521e9fe077e5c5e3b17.js

As you can see this Dockerfile uses the phusion/passenger-ruby21 base image. It adds the Nginx config, adds the application code, runs bundler to install the gems and precompiles the assets.

nginx.conf

CODE: https://gist.github.com/jansn/1af7921c9c1d39b8f79934010dd6f5c4.js

Building the app container image

I committed these files to my repository. Now I’m going to upload it to my server and build the container:

CODE: https://gist.github.com/jansn/7ea760c3869ebb72520217f830895c37.js

This command produces a lot of output and results in a Docker image. The first
time I run that docker build command it took a few minutes. That’s because
Docker needs to download the phusion/passenger-ruby21 base image. It only does
this once. When downloading the base image is done it will continue with my own
Dockerfile.

The docker images command now shows me my image:

CODE: https://gist.github.com/jansn/447e21d6fc52ddc5b2f81bffa73218b4.js

Running the container for the first time

Time to run my app for the first time. Here we go:

CODE: https://gist.github.com/jansn/b49faf325fbcf9708fa8d654bfdceeb0.js

This command starts my container, produces some output that it is starting things and finally will result in a line:

CODE: https://gist.github.com/jansn/54510e8bd4e6c959e2cd67d4953da9b3.js

Now let’s see if my app is running correctly. Let’s try with curl:

CODE: https://gist.github.com/jansn/8ce046c42c0d5143e1d2d4aa67252c2d.js

Oops! Apparently something is wrong. On examination of the application log file
inside the running container (which I accessed using
the docker-bash tool by Phusion)
it seems that I don’t have a database yet. So I’m going to install MySQL on my server first.

Installing the database

I’ll use the standard MySQL server available in Ubuntu 14.04:

CODE: https://gist.github.com/jansn/729aff33de545538ef1a69384ff0f428.js

After the installation where I set my root password, I can now create the app database:

CODE: https://gist.github.com/jansn/985c68b9a9f22676eecdcaa6879c5f81.js

After this I modified /etc/mysql/my.cnf and changed the bind-address setting
from 127.0.0.1 to my public IP address, 178.62.232.206. This way the Rails app inside my Docker container can use it:

In /etc/mysql/my.cnf I updated the line to this:

CODE: https://gist.github.com/jansn/b430c6acb995c113f5c228be96f96e0d.js

And restarted MySQL:

CODE: https://gist.github.com/jansn/e6fff57cca01bf3206159827d9522018.js

Using ENV variables to configure the database

I’m going to use ENV variables to have my container use these mysql credentials. To do this, I need to do two things: 1) Prepare my database.yml file inside the repository to use ENV vars. And 2) configure Nginx to pass these variables into my Passenger process.

Here is my new database.yml, prepared for environment variables:

CODE: https://gist.github.com/jansn/8392d033f5758c81c541453aed1ecc7d.js

To make these ENV variables work for my Rails app, I need to add a custom Nginx configuration files. This is because Nginx flushes all variables from the environment, except the ones you define.

In my Rails application code, I add another Nginx configuration file called rails-env.conf:

CODE: https://gist.github.com/jansn/423d57f963e16c8963614a72c510f265.js

And I modify my Dockerfile, so it adds the rails_env configuration file when building my container:

CODE: https://gist.github.com/jansn/c141c7c673023f2526bada9ac801b9fd.js

Building the image with ENV var support

I committed the new nginx ENV var configuration. Now I’m going to build a new version of my container:

CODE: https://gist.github.com/jansn/e65aa0e58acea501360bb5380fca1447.js

Running rake with ENV vars

After building my container on my server, I can set up the database. In the following command, I use ENV variables to pass my database connection information for running the rake task to set up my database. Note that I added the -u app argument to the command. That argument makes sure the rake db:setup task is run as our app user inside the container.

CODE: https://gist.github.com/jansn/1be381ad1fdff56ec623e4b61b53fb7f.js

Awesome. It worked!

Running the app container with ENV vars

Now I can run the container with the same environment variables as the previous command and try to access it via my browser to see if it works now:

CODE: https://gist.github.com/jansn/bc0c03ec7616465ca30b1533f3b06adf.js

When I browse to http://178.62.232.206, I see my Rails app that is connected to the database, and I see that it has compiled the correct assets. Victory!

Conclusion

This concludes this post where I:

  1. Installed Docker on my server
  2. Set up a Dockerfile and built the container image
  3. Configured my database settings by using environment variables

Issues to solve and next steps

I still have questions that need answering. I and the other Intercity developers will write about them. Here are some of the issues that plan to solve:

  • How do I automate deployments? Maybe a tool like Capistrano?
  • What do I need to get zero downtime? When I stop and start a container to run the new app version, there will be connections dropped.
  • Where am I going to store the ENV vars for each of the apps I deploy on a server?
  • How do I speed up building the container? Do I need to run bundler and rake assets:precompile on every deploy?

I hope you enjoyed this post. Please offer advice or ask questions if you have them! Thank you very much for reading.