Dockerizing a Rails app
Tags: docker, rails, webdev
Published: 2021-03-08 01:43:58

Docker is a container solution developed by the [Docker Inc](https://www.docker.com/) and backed by the [open source community](https://forums.docker.com/). The idea of the docker solution is to provide a [container based tool](https://www.redhat.com/pt-br/topics/containers/whats-a-linux-container) with an image-based deployment model. You can create a complete development/production ready environment with a simple set of configuration and wrap it into an image that can be distributed on your team or to the community.

In this article we are going to build the configuration of a development environment to a simple rails application using a PostgreSQL database.

## Getting the hands dirty

First you need some application to work on created and [Docker](https://docs.docker.com/engine/install/)/[Compose](https://docs.docker.com/compose/install/) installed.

The folder structure that we will follow is based in creating a docker folder inside the root of the project containig the `Dockerfile` and the `entrypoint.sh` script. The `docker-compose.yml` file will be placed at the root of the project.

### The Dockerfile

The Dockerfile configures the image with all dependencies of the project.

```dockerfile
ARG RUBY_VERSION=2.7.2
FROM ruby:$RUBY_VERSION
ARG DEBIAN_FRONTEND=noninteractive

RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
RUN curl -sL https://deb.nodesource.com/setup_12.x | bash -

RUN apt-get update && apt-get install -y \
      build-essential \
      nodejs \
      yarn \
      locales \
      netcat \
      sudo \
  && apt-get clean

ENV LANG C.UTF-8

RUN mkdir -p /app && chown $USER:$USER /app
WORKDIR /app

RUN gem install bundler
```

The **FROM** statement defines the base image of our container which is the official ruby image of the 2.7.2 version of the language.
We are also installing the dependecies to run the application and preparing the system to receive the code.

### The docker-compose.yml file

The compose file configures the services of the application using a YAML syntax.

```yaml
version: '3.7'

services:
  postgres:
    image: postgres:11
    environment:
      - POSTGRES_HOST_AUTH_METHOD=trust
    volumes:
      - postgres:/var/lib/postgresql/data
  web:
    build:
      context: ./docker/
    environment:
      - DATABASE_USERNAME=postgres
      - DATABASE_PASSWORD=
      - DATABASE_HOST=postgres
      - DATABASE_PORT=5432
    depends_on:
      - postgres
    entrypoint: docker/entrypoint.sh
    ports:
      - "3000:3000"
    volumes:
      - .:/app:cached
    tty: true
    stdin_open: true

volumes:
    gems:
    postgres:
    rails_cache:
```

With this config file we are defining two services:

- The database service, using the version 11 of PostgreSQL and with a [volume](https://docs.docker.com/storage/volumes/) mounted storing the database data.
- The application service thats going to use our previouslly created Dockerfile to build its container. Here we can define some parameters of the application run:
  - The environment variables with the `environment` key.
  - The ports binding from the host machine with the container.
  - The entrypoint script, which will bootstrap the application during the services startup.

### The entrypoint.sh script

Here we just install all rails/node dependencies, configure the database and start the application server process.

```bash
  #! /bin/bash
  set -e

  bundle install
  yarn install

  bundle exec rails db:prepare
  bundle exec rails server -p 3000 -b 0.0.0.0
```

### Adding new dependencies

At this point the application is configured to run the rails server connected to a PostgreSQL database.
If you need more dependencies, such Redis, ElasticSearch or MongoDB you can simply add a new service that uses an image that wraps the depedency.

The [Docker Hub](https://hub.docker.com) platform is a public respository used by many companies and individual developers to share Docker images. You can use it to search the service you need to attach to your application.

### Wrapping up and running the application

To run the application you can use the following commands:

- `docker-compose up` will raise all services described on the compose file.
- `docker-compose exec SERVICE_NAME COMMAND` will run the given command on the command line of the indicated service.
- `docker-compose down` is going to stop all services.

Now you have a fully functional and ready to use configuration to your project. I used a Ruby on Rails application as example, but the concepts can be applied to any stack you use, you just need to adjust the Dockerfile to your project dependencies and configure the services on the compose file.