Where are you using WebAssembly?
Wasm promises to let developers build once and run anywhere. Are you using it yet?
At work, for production apps
At work, but not for production apps
I don’t use WebAssembly but expect to when the technology matures
I have no plans to use WebAssembly
No plans and I get mad whenever I see the buzzword
Containers / Software Development

Ruby in Containers

Nov 16th, 2018 3:00am by
Featued image for: Ruby in Containers
Feature image via Pixabay.

Joannah Nanjekye
Joannah Nanjekye is a software engineer from Kampala Uganda. She is a proud open source contributor having been mentored through programs like Rails Girls Summer of Code and Outreachy, writing mostly Python, Ruby, and Golang. She is the author of Python 2 and 3 Compatibility, a book published by Apress. She also organizes Rails Girls Kampala. Recently reading a lot about the latest developer tools and space.

Software changes environments from a development machine to a UAT (user acceptance testing) server environment or even from a test environment to production. It is required that the software runs consistently and reliably in these environments in the process.

There was a time when deploying software was an event, a ceremony because of the difficulty that was required to keep this consistency. Teams spent a lot of time making the destination environments run the software as the source environment. They thereafter prayed that the gods kept the software running perfectly in production as in development.

With containers, deployments are more frequent because we package our applications with their libraries as a unit making them portable thereby helping us maintain consistency and reliability when moving software between environments. For developers, this is improved productivity, portability and ease of scaling.

Because of this portability, containers have become the universal language of the cloud allowing us to move software from one cloud to another without much trouble.

In this article, I will discuss two major concepts to note while working with containers in Ruby. I will discuss how to create small container images and how to test them.


To run the source code as you follow along the tutorial, you may need to have the following tools installed on your system.

  • Ruby
  • Docker
  • A Linux operating system the image tests implemented using the Container Test Framework may not work on windows for now.
  • Get complete source code.

As we get started, given a hello world sinatra application such this one:

Containerizing this application with Docker requires us to write a Dockerfile such as one below. Read more about how to create Docker files on the Docker website.

We need to care about the performance and security of the images. The way to ensure good performance and secure image is to reduce its size.This is not enough though, there are other aspects we need to give thought to create effective images.

A small image takes a shorter time to build, push and pull to and from the image registry.

Small images also have a small surface area of attack hence reducing security vulnerability. Let us discuss some tips on creating small Docker images.

Reducing the Size of the Image

The instructions we use to create a container image affect the size of the resulting image. To reduce the image size, use the following best practices ;

  • Use a small base image.
  • Command chaining.
  • Clean up your containers.
  • Install what you need.

Let us discuss each of these in depth.

Small Base Image

When creating a Dockerfile for a Ruby application, you have three options to use for your base images. A standard operating system image like Ubuntu, the lightweight Linux Alpine, the Ruby base image and the Ruby alpine base images. We use these base images in different ways and they come in different sizes.

Image Version Size
Ubuntu latest 187.9MB
Alpine latest 5.249MB
Ruby 2.5.1-alpine 55.5MB
Ruby 2.5.1 863MB

A close analysis of the sizes of the available base images makes us come to a conclusion that the Linux Alpine image is the smallest.

Using a small base image significantly reduces the container image. We can now change the Dockerfile to use Linux alpine as the base image.

Linux alpine is lightweight and may lead to extra development work because it doesn’t come with some libraries compared to a full operating system. There are situations where using a full operating system to reduce this development work but also when conforming to standards and security in full operating systems.

Chaining Commands

Commands such as  RUN can be placed on separate lines but this increases the container layers of the image. The more layers we have, the bigger the image. To reduce the layers, we should chain these commands.

Install What You NeedThe RUN command has been chained into one.

When installing packages in Linux, use -no-install-recommends flag to install only what you need in terms of packages.

Clean up your ContainersBy leaving out recommended packages, we are able to work with what we need and yet benefit from a smaller image.

After installing packages in your containers, clean up all cache files to make the image smaller.

From the Docker file we build an image and start the container while mapping the container port 80 to localhost:4000.That is all on creating smaller images. After these changes, we can now build and start our container.

Unit Testing Docker Images

A container image is a blueprint from which we create several container instances. We create this images using instructions in a Dockerfile. Like, software we need to ensure that before a container goes to production, it is tested to ensure it works as required.

We unit test container images to validate the instructions in the Dockerfile work as required. Container images should be tested during development to ascertain the structure and contents of the containers before they are shipped to production.

Container Structure Test Framework

Early this year, Google released the Container Structure Test Framework to help us validate the structure of container images.


The tests are specified in a .yaml or .json file and run through a standalone binary, or a Docker image. Download the binary here or pull the Docker Image.

Once you have downloaded the binary or pulled the docker image, then run the tests as below, using the binary:

Running the Tests

Using the Docker image;

Getting back to our Dockerfile, let us create a test file for it with the following contents.


Command TestsThere are four types of tests we can perform on a container image with this framework;

These tests allow us to execute a given command inside the container image and verify if the output matches what is expected, or is an error. A good example of a command test is to verify the installation of packages or binaries in the container image.

In our case, we test for ruby and bundler installations.

File existence tests are used to check for the existence of expected files in a container image. We often create working directories in our container images and even move around files to this directory. We can check if the files exist in the working directory with the File existence tests.

File Existence Tests

For our Dockerfile, we will test if the Gemfile and Gemfile.lock exists.

File Contents Tests

These are used to verify the contents of the files container file system.

This test checks to ensure that given container metadata is accurate. Use Metadata tests to check instructions such as ENV, LABEL, ENTRYPOINT, CMD, EXPOSE, VOLUME, WORKDIR.

Metadata Test

After adding these tests, let us run the tests on it.

Small container images give us better performance and security for applications. To ensure reliable containers in production, unit test the container images to validate their structure using the container structure test framework from Google.

Group Created with Sketch.
TNS owner Insight Partners is an investor in: Unit, Docker.
THE NEW STACK UPDATE A newsletter digest of the week’s most important stories & analyses.