Containerizing a Rails Application

maful

Maful

Posted on November 17, 2022

Containerizing a Rails Application

Introduction

If you actively working in web development, you may ever hear about docker and how docker makes the development are fun and the environment consistent, and isolated.

In this post, we will go through containerizing a Rails API application. Consider you already have a Rails application and you want the same environment for all your team while developing the app and here is the Docker coming in.

Prerequisites

  • Docker installed on your machine
  • Rails application

Create a Dockerfile

Dockerfile is a file that contains all the commands that users can call to build an image for the application. Since we will build an image for rails, we need to add ruby as the base image.

  • Create file Dockerfile (without extension) inside your root project and add ruby for the base image. You can choose another version that is suitable for your app (Ruby image tags).
FROM ruby:2.7.3
Enter fullscreen mode Exit fullscreen mode
  • Create a user and install the required packages
# argument 
ARG WORK_DIR=/my_app
ARG USER=my_user
ARG HOME=/home/${USER}
ARG UID=999

# create user and user group
RUN groupadd -g ${UID} ${USER} && useradd -r -u ${UID} -g ${USER} ${USER}

# update os and install required packages
RUN apt-get update -qq && apt-get upgrade -y && apt-get install -y postgresql-client
Enter fullscreen mode Exit fullscreen mode
  • then, copy all necessary files from the host machine and then install the app dependencies
# set WORK_DIR 
RUN mkdir ${WORK_DIR}
COPY Gemfile ${WORK_DIR}/Gemfile
COPY Gemfile.lock ${WORK_DIR}/Gemfile.lock
COPY . ${WORK_DIR}
RUN chown -R ${USER}:${USER} ${WORK_DIR}

# set HOME
RUN mkdir -p ${HOME}
RUN chown -R ${USER}:${USER} ${HOME}
WORKDIR ${WORK_DIR}

# set user before bundle install for dev permission issue
USER ${USER}
RUN bundle install
Enter fullscreen mode Exit fullscreen mode

Add script and main process, and here we expose port 3000 from the image

# Add a script to be executed every time the container starts. 
USER ${USER}
COPY entrypoint.sh /usr/bin/
ENTRYPOINT ["./usr/bin/entrypoint.sh/"]
EXPOSE 3000

# Start the main process.
CMD ["rails", "server", "-b", "0.0.0.0"]
Enter fullscreen mode Exit fullscreen mode

Here is the full our Dockerfile

FROM ruby:2.7.3

# argument
ARG WORK_DIR=/my_app
ARG USER=my_user
ARG HOME=/home/${USER}
ARG UID=999

# create user and user group
RUN groupadd -g ${UID} ${USER} && useradd -r -u ${UID} -g ${USER} ${USER}

# update os and install required packages
RUN apt-get update -qq && apt-get upgrade -y && apt-get install -y postgresql-client

# set WORK_DIR
RUN mkdir ${WORK_DIR}
COPY Gemfile ${WORK_DIR}/Gemfile
COPY Gemfile.lock ${WORK_DIR}/Gemfile.lock
COPY . ${WORK_DIR}
RUN chown -R ${USER}:${USER} ${WORK_DIR}

# set HOME
RUN mkdir -p ${HOME}
RUN chown -R ${USER}:${USER} ${HOME}
WORKDIR ${WORK_DIR}

# set user before bundle install for dev permission issue
USER ${USER}
RUN bundle install

# Add a script to be executed every time the container starts.
USER ${USER}
COPY entrypoint.sh /usr/bin/
ENTRYPOINT ["./usr/bin/entrypoint.sh/"]
EXPOSE 3000

# Start the main process.
CMD ["rails", "server", "-b", "0.0.0.0"]
Enter fullscreen mode Exit fullscreen mode

The last thing, create entrypoint.sh in your root project and add this script.

#!/bin/bash
set -e

# Remove a potentially pre-existing server.pid for Rails.
rm -f /my_app/tmp/pids/server.pid

# Then exec the container's main process (what's set as CMD in the Dockerfile).
"${@}"
Enter fullscreen mode Exit fullscreen mode

Docker Compose

When you want to run multiple docker containers, you should consider using Docker compose. If your app database is PostgreSQL, you can add a postgres image to compose file.

Create a file called docker-compose.yml in your project directory, and add our image above (Dockerfile) and postgres image for the database.

version: '3'
services:
  db:
    image: postgres:11
    ports:
      - 5432:5432
    environment:
      - POSTGRES_USER=docker
      - POSTGRES_PASSWORD=docker
    volumes:
      - db:/var/lib/postgresql/data

  app:
    build: .
    command: bash -c "bundle exec rails s -p 3000 -b '0.0.0.0'"
    entrypoint: ./entrypoint.sh
    volumes:
      - ./:/my_app
    ports:
      - 3000:3000
    environment:
      - DATABASE_USERNAME=docker
      - DATABASE_PASSWORD=docker
      - DATABASE_HOST=db
    depends_on:
      - db

volumes:
  db:
    external: true
Enter fullscreen mode Exit fullscreen mode

Setting up configuration

If you notice in the compose file above, we set the environment variable for DATABASE_USERNAME, DATABASE_PASSWORD, and DATABASE_HOST. Then, update the config/database.yml for the default part.

default: &default
  adapter: postgresql
  encoding: unicode
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  database: <%= ENV.fetch("DATABASE_NAME") { "myapp" } %>
  timeout: <%= ENV.fetch("DATABASE_TIMEOUT") { 5000 } %>
  username: <%= ENV.fetch("DATABASE_USERNAME") { "docker" } %>
  password: <%= ENV.fetch("DATABASE_PASSWORD") { "docker" }%>
  host: <%= ENV.fetch("DATABASE_HOST") { "localhost" } %>
  port: <%= ENV.fetch("DATABASE_PORT") { 5432 } %>
Enter fullscreen mode Exit fullscreen mode

Build and Run Application

After setting up file configuration, the next thing we have to do is Build services. Open your terminal and navigate to your project directory, run compose build to build or rebuild the services.

docker compose build
Enter fullscreen mode Exit fullscreen mode

It will take some time to download the image from Docker Hub if you haven't ruby image installed. And then run the application.

docker compose up
Enter fullscreen mode Exit fullscreen mode

if you got an error message around database setup things, run the database migration just like usual, the difference is you have to add docker command first

Setup database

docker compose run --rm app bundle exec rake db:setup
Enter fullscreen mode Exit fullscreen mode

Migrate database

docker compose run --rm app bundle exec rake db:migrate
Enter fullscreen mode Exit fullscreen mode

To stop the application, you can use Ctrl+C or docker compose down when you run the application in the background.

Conclusion

If this is your first time using docker, maybe you think you should spend more time configuring all the above things. But, the other thing is you just got new knowledge and when you work with your team, you will have the same environment for your entire team. Sounds great right?

In the next tutorial, you will learn how to create a brand new rails application with Docker. See you in the next one.

💖 💪 🙅 🚩
maful
Maful

Posted on November 17, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related