loading...
Cover image for Setting up Ruby on Rails with docker and MySQL
ITNEXT

Setting up Ruby on Rails with docker and MySQL

tallangroberg profile image Tallan Groberg ・11 min read

Docker is a technology that will revolutionize the way that you develop.

If you like what I'm teaching, please give it a Heart and a friendly comment if you found this helpful.

What I'm teaching in this tutorial is how to develop a rails application inside a docker container so that you only have to set up docker on your mac to develop a rails application. (no rails or Ruby operating system config required to start.)

This is popular with businesses because containerization or, standardizing units of software, is a revolution in how applications are made.

if you are new to docker, and you follow this tutorial. This will be the start to an amazing journey to get applications running that you can scale indefinately. You can start developing everything through a docker container without the stress of host system configurations.

Here is what I'm going to teach.

This is the start to a multi-part series that will teach you how to make 3 docker containers and publish them on heroku

  1. ruby on rails backend.

  2. mysql database

  3. react-app

Provide helpful resources geared toward beginners.
I’ll show you how to install docker on mac.

Docker is also available for windows.

Prerequisites:

  1. basic commandline.
  2. a text editor. I will be using VS code.
  3. attention to detail.

    helpful but not nessesscary.

    1. knowledge of ruby
    2. knowlege of .yaml or .yml files
    3. Knowledge of docker.

helpful links.

docker docs

Visit the official website and install docker.

docker for mac desktop

Getting started

Open your terminal and verify docker installed correctly.

docker --version

Your output should look something like this but may be a different version.

Docker version 19.03.8, build afacb8b

if you didn't see any version or you got an error. Go back to the link where docker for mac desktop and try do the steps again.

Before moving on, make sure that you open docker desktop.

press command + spacebar to open the searchable view.

Alt Text

then search for docker.

Alt Text

a small whale icon with shipping containers on its back should be visible at the top-right of your desktop.

Alt Text

Click on it.

Now click to open desktop.

Make sure that docker desktop is running the whole time, if docker is off then docker commands will not execute.

Alt Text

You will be prompted to do the hello world version of docker which I highly recommend doing the simple walk through.

If everything worked with the hello world app, one last check to ensure that you can follow this tutorial for a rails application is to ensure that you have access to docker-compose, this should be installed with the rest of docker command-line tools and this is the command to make sure.

docker-compose -v 

the expected output.

docker-compose version 1.25.5, build some-hexadecimal

If the versions aren't the same everything should still work.

Start of the tutorial.

First, let's make an empty directory called rails-docker.

mkdir rails-docker

cd into the directory.

cd rails-docker

The first file we want to make is the Gemfile, this is because we will have docker instantiate the entire dev environment instead of running rails commands directly through our host operating system.

touch Gemfile

Inside the Gemfile, we will write the source that we get our ruby gems.

source 'https://rubygems.org'

underneath the source add a line for the rails version we want.

gem 'rails', '5.0.7'

If you want the newest version of rails(6.0.3.2 as of this writing), you can install whichever version you want but this is the one that I have tested and recommend for this tutorial.

We also need and empty Gemfile.lock, this is where the Gemfile versions are recorded by rails.

The app will not run without an empty Gemfile.lock

touch Gemfile.lock

Now we need to make a Dockerfile, this is where we define what technologies our docker image will have, such as ruby versions node.js versions.

touch Dockerfile

Inside the Dockerfile we will specify the minimum requirements for docker to make an image to run a rails app. Each new command can be thought of like a script making layers that become an operating system.

First is the programming language that everything else will need to run on top of. In our case ruby, using the FROM command.

FROM ruby:2.5.1 

Dockerfile

Next is the RUN command a typical suffix for RUN is apt-get update && apt-get install problems can arise because updating should happen outside your docker image on dockerhub. see a detailed explanation here

RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs

build-essential is a package of trusted compilers for our application.

libpq-dev is a compiler system to help with port forwarding to databases from virtual machines. A detailed explanation is in the link.

nodejs is a javascript engine that will enable you to run javascript outside of a browser.

We will add more RUN commands which you can think of as the docker equivalent of a command-line script.

RUN mkdir /app

This will make a directory only within the context of our docker-image.

Now we want to make the /app directory the working directory with the WORKDIR command, its the bash equivalent of cd into a folder. This means that all the commands that you run after this line will happen in the /app directory.

WORKDIR /app

we have to ADD the Gemfile and the Gemfile.lock for docker to reference for the build of our rails app.

ADD Gemfile /app/Gemfile
ADD Gemfile.lock /app/Gemfile.lock

Since we have our Gemfile and the Gemfile.lock inside the directory where we will build, the next command is to RUN bundle install. This adds all of the rails dependencies.

RUN bundle install

now we want to ADD the current directory to the application directory after that is installed like so.

ADD . /app

the finished file should look like this.

FROM ruby:2.5.1 
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs
RUN mkdir /app
WORKDIR /app 
ADD Gemfile /app/Gemfile
ADD Gemfile.lock /app/Gemfile.lock
RUN bundle install
ADD . /app

this is the file that will be referenced every time we use the build command on the command-line.

as the next step in our configuration, let’s make a docker-compose.yml.

touch docker-compose.yml

In this file we will define a config object for the services involved in this app such as the database.

Within this file, because of how docker objects are defined, improper indentation can cause the build to fail.

I will provide a full list of the file after I walk you through what is going on with proper indentation.

version: '2'
services: 

under services, we will define the external services for the app such as the database (MYSQL).

db:
    image: mysql:5.7

This is how we can pull an image database from docker hub, in our case MySQL.

some configuration for how we want our database to behave on restart and access defined by environment: variables.

restart: always
    environment:
        MYSQL_ROOT_PASSWORD: password
        MYSQL_DATABASE: app
        MYSQL_USER: user
        MYSQL_PASSWORD: password

We want to give our app a port forwarding service so that the process that docker is running is linked to the process the host operating system is running.

    ports:
       - "3307:3306"

let's add our next service called app:

app: 

below app, we want our app to reference the rails application. this means that instead of calling on an image from docker hub like we did with MySQL. We are going to tell docker to build the app that we configured in the Dockerfile with the build:

we will add the current directory with build: .

  build: .

now we will run rails with the command: then bind it to port 0.0.0.0

command: rails s -p 3000 -b '0.0.0.0'

we need a thing called volumes:

It is used to say, the directory where the host system shares the /app directory.

        volumes: 
            - ".:/app"

we want to set up the port forwarding so that when docker refers its port 3000 in the container, it will forward that to 3001 to the operating system in the same way that we did so for the database.

    ports: 
       - "3001:3000"

let's make the app service depend_on the db: service so that when we use our rails app it refers to the database to store data and it is linked to the database.

depends_on:
      - db
links:
      - db

Now we want to add the environment variables for the rails application.

         environment:
             DB_USER: person
             DB_NAME: app
             DB_PASSWORD: password
             DB_HOST: db

The whole file should look like this.

version: '2'
services:
    db: 
        image: mysql:5.7
        restart: always
        environment:
            MYSQL_ROOT_PASSWORD: password
            MYSQL_DATABASE: app
            MYSQL_USER: user
            MYSQL_PASSWORD: password
        ports:
            - "3307:3306"
    app: 
        build: .
        command: bundle exec rails s -p 3000 -b '0.0.0.0'
        volumes: 
            - ".:/app"
        ports: 
            - "3001:3000"
        depends_on:
            - db
        links:
            - db
         environment:
            DB_USER: root
            DB_NAME: app
            DB_PASSWORD: password
            DB_HOST: db

docker-compose.yml

We are going to be interacting with the command-line again in a way that we prefix all of our ordinary rails commands with docker-compose run app

This is the command to download all of the boiler-plate rails.

note: this will take a few minutes the first time but will be quick next time. the --api flag can be added, is optional and but it will be easier to get started if you use embedded ruby views instead of a front end framework.

docker-compose run app rails new . --force --database=mysql --skip-bundle

This should have created a rails structure and a database structure to use but we still have some work to do.

Open the database.yml in the config folder that was downloaded in the rails install. Delete all the comments.

Without all the comments. It should look something like this.


default: &default
  adapter: mysql2
  encoding: utf8
  pool: 5
  username: root
  password:
  host: localhost

#...rest of file... 

under pool: 5, erase the username, password, host and replace them with this code so that they reference the ENV variables in the docker-compose.yml

database: <%= ENV['DB_NAME'] %>
  username: <%= ENV['DB_USER'] %>
  password: <%= ENV['DB_PASSWORD'] %>
  host: <%= ENV['DB_HOST'] %>

We want to standardize the naming conventions by setting the rest of the file's dev, test, and production environments to default.


default: &default
  adapter: mysql2
  encoding: utf8
  pool: 5
  database: <%= ENV['DB_NAME'] %>
  username: <%= ENV['DB_USER'] %>
  password: <%= ENV['DB_PASSWORD'] %>
  host: <%= ENV['DB_HOST'] %>

development:
  <<: *default

test:
  <<: *default

production:
  <<: *default


We want to make the database username, password, and host reference environment variables that we created in the docker-compose.yml

Now, in the command-line we can run docker-compose build

docker-compose build

This will bundle install all the gems for the rails application that were pulled in from rails boiler-plate.

Now run docker-compose up on the command-line in order to start both the MySQL database and the rails application.

docker-compose up

When you go to localhost:3001 you will see the rails logo.

Alt Text

Trouble Shooting and Reconfiguration.

I will be following this up with a multi-part series that shows how to deploy multiple containers to Heroku to make full-stack containerized applications.

In order to do that easily, we have to know how to tear this app down and redo it, maybe you want to find out whether this will work with the latest version of ruby and rails. Or with a PostgreSQL database instead.

The first step is to open docker dashboard and purge data.

Alt Text

Click on the bug icon in the top left corner of the screen.

Alt Text

Click the red clean/ purge data and yes, we are sure.

Alt Text

Once you confirm this will restart docker, not opening the dashboard but running in the background so that you can use the command-line.

now delete the rails app from the file directory, carefully so that you don't delete any of the docker related files, Gemfile or the Gemfile.lock

You can do this in vscode by clicking on the files and folders while holding down the command key.

Alt Text

right-click over the selected files and select delete and confirm.

Alt Text

Alt Text

Open the Gemfile and delete everything except the source and the rails gem, you could delete the content of the Gemfile and copy and paste these two lines of code into the file.

You could also try the rest of this rebuild with a different rails version and if it doesn't work out, you can repeat the steps above for troubleshooting and reconfiguration.

source 'https://rubygems.org'
gem 'rails', '~> 5.0.7', '>= 5.0.7.2'

Now we want to make the Gemfile.lock empty since it will get filled again when we run docker-compose build.

This can easily be done by clicking on the file content and pressing command + a, highlighting everything then press delete, leaving the file empty.

Now you can go to the command-line and run the steps for creating a new image.make sure docker is running first

These commands are.

docker-compose run app rails new . --force --database=mysql --skip-bundle

This will take a while since it's just like the first time.

Build what you pulled in.

docker-compose build

While that's reinstalling, go into the database.yml and replace it with this file referencing the environment variables.


default: &default
  adapter: mysql2
  encoding: utf8
  pool: 5
  database: <%= ENV['DB_NAME'] %>
  username: <%= ENV['DB_USER'] %>
  password: <%= ENV['DB_PASSWORD'] %>
  host: <%= ENV['DB_HOST'] %>

development:
  <<: *default


test:
  <<: *default


production:
  <<: *default

To make sure it works run...

docker-compose up

if this doesn't work the first time you may have to restart docker which can be done by clicking the icon at the top right of the desktop and quitting the application.

Visit localhost:3001 and you will see yourself right back where you started.

Conclusion

Docker has a learning curve but it will save you so much time in the long run not having to worry about operating system related errors.

I will be doing more tutorials that branch off from this one showing you how to make full-stack web apps to production with rails and react.

If you run into problems getting this to work, please leave a comment and I will get back to you.

Here are some additional resources.

finished tutorial on github.

Check out the following.

docker-compose.yml

Dockerfile

and the database.yml in the config folder

an excellent youtube from
Chandra Shettigar who does an excellent tutorial on how to set up a ruby on rails app with docker but leaves the setup of docker to you and doesn't show you how to tear-down,
rebuild or reconfigure.

If this helped you get started with docker, please give it a heart, unicorn and a tag.

For the next part of this series where I will show you how to set up a react application within a docker container.

Thanks and I look forward to hearing from you in the comments
below.

Posted on May 24 by:

tallangroberg profile

Tallan Groberg

@tallangroberg

I love to write, I love all things tech I recently published a novel.

ITNEXT

ITNEXT is a platform for software developers, engineers, IT architects, system engineers and IT enthousiasts to share knowledge, connect and connect.

Discussion

markdown guide