Red Green Repeat Adventures of a Spec Driven Junkie

Creating Docker Image rails-init-rgr

I have been digging around with Docker recently and want to start create Docker images for Rails development.

Docker
Icon

Ruby on Rails
Icon

The official Rails Docker image isn’t maintained as the current best practice is to build the image from the Ruby Docker image. This is understandable as Rails is a library of Ruby, not a core language feature, no matter how popular it is. :-)

Motivation

I want to create a Dockerfile to create an image for:

  • Ruby on Rails development with the least amount interference on the host system.
  • Have any changes from spilling over into the host system.
  • Have the Ruby on Rails system environment consistent inside the Docker container.

Audience

The intended audience of this article are for Ruby on Rails programmers that want to integrate Docker into their development workflow, from the beginning. The worflow I discuss will require only installing Docker on the host system.

System Requirements

To follow along this article, one needs to have:

That’s it! Isn’t that beautiful? I love having clean code. Speaking of code…

External Resources

  • Code discussed in this article will be on this Github repository.

  • The Docker image created is available for download on docker hub.

New Ruby on Rails Project

Normally, creating a new Ruby on Rails project requires itself, but to install Ruby on Rails, there needs to be changes to the host system, such as installing a compatible version of Ruby.

Instead of installing anything to the host system, I will install Ruby and Ruby on Rails inside a Docker image so that it can create new Ruby on Rails projects.

This is the Dockerfile contents and I will explain each section:

FROM ruby:2.5.0-slim

RUN apt-get update && apt-get install -qq -y --no-install-recommends \
    build-essential \
    libsqlite3-dev \
    nodejs \
    sqlite3 \
 && rm -rf /var/lib/apt/lists/*

RUN mkdir /rails_app
WORKDIR /rails_app

RUN echo "ruby '2.5.0'\n source 'https://rubygems.org'\n gem 'rails', '~> 5.1.0'\n" > Gemfile
RUN bundle install

RUN rails new temp_app
RUN rm -rf temp_app

FROM ruby

This creates the current Docker image from the Ruby-2.5 slimmed down image.

FROM ruby:2.5.0-slim

Docker encourages high reuse and this Docker image builds from the Ruby image. The slim version of the Docker image uses fewer components than the normal version as we will focus on Ruby on Rails development, not Ruby development.

RUN apt-get

This installs all the base system libraries for a new base Ruby on Rails project.

RUN apt-get update && apt-get install -qq -y --no-install-recommends \
    build-essential \
    libsqlite3-dev \
    nodejs \
    sqlite3 \
 && rm -rf /var/lib/apt/lists/*

Ruby on Rails needs additional system components installed, such as:

  • libsqlite3-dev - the development library for SQLite, a file based database.
  • nodejs - the nodejs runtime, for JavaScript processing.
  • sqlite3 - the SQLite executable.

RUN mkdir

This makes and sets the working directory to the root of the system named: /rails_app

RUN mkdir /rails_app
WORKDIR /rails_app

RUN echo

This creates the initial Gemfile to install the Ruby on Rails gem.

RUN echo "ruby '2.5.0'\n source 'https://rubygems.org'\n gem 'rails', '~> 5.1.0'\n" > Gemfile
RUN bundle install

The contents of echo can be a separate file, if that is the case, replace the RUN echo command with:

COPY Gemfile .

The expected file contents of Gemfile to be:

ruby '2.5.0'
source 'https://rubygems.org'
gem 'rails', '~> 5.1.0'

This is the most basic Gem file possible to install Ruby on Rails on a system.

RUN rails new

This command uses the newly installed Ruby on Rails gem and creates a new project, then deletes it.

RUN rails new temp_app
RUN rm -rf temp_app

The main reason to do this is so all the additional Ruby on Rails gems are also installed when creating a new project.

The are the additional gems installed:

  • rails
  • sqlite3
  • puma
  • sass-rails
  • uglifier
  • coffee-rails
  • turbolinks
  • jbuilder
  • byebug
  • capybara
  • selenium-webdriver
  • web-console
  • listen
  • spring
  • spring-watcher-listen
  • tzinfo-data

Without these Gems, the Docker image generated will only be able to create this Rails app directory, nothing else. That is not too useful of a Docker image!

By installing these Gems, any project created by this Docker image is executable by this image as well.

Building rails-init-rgr Docker Image

Run the following command to build the above image:

$ docker build . -t rails-init-rgr

The final image size is approximately 500MB. If it is the first time to build this image, have a solid network connection.

Using rails-init-rgr

With the Docker image built, let’s take it for a spin!

Creating a new project

The following command will create a new Ruby on Rails project in the current directory named: new_app:

$ docker run --mount type=bind,source="$(pwd)",target=/rails_app rails-init-rgr rails new new_app

Run app

The following command will run the new_app created with the previous command:

$ docker run --mount type=bind,source="$(pwd)/new_app",target=/rails_app -p 3000:3000 rails-init-rgr rails serve

Open a browser and go to address: http://localhost:3000 (the Ruby on Rails default) and you will see:

Rails init
page

Conclusion

We created a new Docker image that can:

  • create a new Ruby on Rails app
  • serve the created Ruby on Rails app

This was all done with only installing one application on the host system: Docker. There was no need to install Ruby, Ruby on Rails, or any support Ruby Gems or external libraries. The system is self-contained because there are no dependencies on the host system other than Docker. The Docker image creates everything required for basic usage, such as creating a new Ruby on Rails app and serving it.

In a future article, I will go over using this image further for development.