Painless Gems With Docker

I love Docker and I found Ruby a very funny environment to do some usefull stuffs with no extra effort, only a small problem I found during various projects on my Raspberry PI, GEM INSTALL TIME.

DOcker + Ruby

I found this issue trying to use some site generator written in Ruby that requires a lot of gems to produce web pages to do something in record time, like this blog written with Jekyll or a wallboard written in Smashing, and try to deploy with Docker I notice a very long time to build the image ready to use.

But if Docker extends deploy time, in the meantime, saves me with bind mounts. Let’s try out!

1. Write some code#

For this tutorial I want to use a simple Sinatra app.

Suppose this is our app.rb:

# app.rb

require 'sinatra'

set :port, 8080
set :bind, '0.0.0.0'

get '/some-crazy-api' do
  'Some magic code'
end

puts 'Listening on port :8080'

This is our Gemfile:

# Gemfile
source 'https://rubygems.org'

gem 'sinatra'
gem 'sinatra-contrib'

And running:

bundle install && ruby app.rb

You can go to 127.0.0.1:8080/some-crazy-api and see the result.

2. Dockerfile#

Now, time to prepare Dockerfile:

FROM ruby:2.6

WORKDIR /app

COPY  Gemfile .
COPY  Gemfile.lock .

RUN apt-get update && \
    apt-get upgrade -yq && \
    gem install bundler:2.2.16

RUN bundle install

EXPOSE 8080

CMD ["ruby", "app.rb"]

And build docker image:

docker build -y awesome-app .

The concept behind this Dockerfile is to prepare a generic image ready to run a ruby app with the specifics gems you need, the only assumption is to have a file called app.rb at image startup. To do this we are going to use bind mounts with docker-compose.

3. Docker-Compose#

And finally docker-compose.yml:

version: "3"
services:
  awesome-app:
    image: awesome-app
    volumes:
      - ./app.rb:/app/app.rb # <- bind mounts 
    ports:
      - 8080:8080

The configuration is very simple, but the magic comes with the volume mount. We just bind app.rb file into Docker container under its path /app. The goal is to avoid recreating every time the Docker image, instead restart container and rebind your .rb files.

4. Testing#

First starts the container with:

docker-compose up -d

and navigate to your-awesome-app in your browser. The response is:

  'Some magic code'

Now try to modify app.rb like this:

get '/some-crazy-api' do
  'Some magic api'
end

and simply restart the container with:

docker-compose restart

This time the result is:

  'Some magic api'

5. Conclusion#

With this simple example we just build one time the Docker image and for every code-logic changes we just need to restart the container and avoid useless builds of the same image.

Stay tuned folks.#