How to build a Ruby on Rails environment using Docker (for Docker beginners)

When studying Docker for the first time, I think that many people will learn from the basics in "reference books" and "introductory Docker and other sites". However, I think that there are many people who do not understand at all even if various words are listed without any image of what Docker is. (At least I was.) So, in the extreme, let's skip the basics and learn while actually building a virtual environment with Docker. And I think it's okay to do something, so after grasping the image, you should learn the basics. Many of those who are new to Docker may have a portfolio of applications developed with Ruby on Rails. So this time, I will introduce how to build a Ruby on Rails environment using __Docker __.

However, let me explain only the super-basic things first. (Anyway, if you built the environment quickly, please fly to __ Premise __)

What is Docker

1_y6CvfE6WUgoIdT8Mp0Ev_g.png Docker is __ a tool for building virtual environments __.

--What is the Docker engine?

__Docker engine __ is the core of Docker, which creates Docker images and starts containers.

--What is a container?

Container is the virtual environment itself built on the Docker engine. Containers can build a variety of environments, including operating systems such as CentOS and Ubuntu, middleware such as Nginx and MySQL, and applications such as Rails and WordPress.

--What is a Docker image?

The __Docker image __ is like a recipe for creating a container. There are countless other Docker images such as CentOS, MySQL, Ruby, and more.

--What is Dockerfile?

Dockerfile is like a design document for creating your own Docker image. You can create a new image based on an existing Docker image by installing packages or rewriting files.

--What is Docker compose?

docker-compose-button.jpg Docker compose is a tool that automates the process of building and running multiple containers. The file that describes the procedure etc. is docker-compose.yml, which allows you to start multiple containers with a small number of command executions.

environment

Premise

--Please install Docker and Docker compose --Please change the myapp part of the following explanation to your own application name. ――If you want to build the environment quickly, skip the explanation, copy and paste it, and execute the command.

Create working directory for application

Create a working directory with mkdir.

$ mkdir myapp

Prepare the necessary files

The file structure is as follows.

myapp
  |-- Dockerfile
  |-- docker-compose.yml
  |-- Gemfile
  |-- Gemfile.lock

Now, create the following files in the directory you created earlier.

① Gemfile

Gemfile


source 'https://rubygems.org'
gem 'rails', '~> 5.2.4', '>= 5.2.4.2'

There is an item to bundle install in the Dockerfile created in ③, but at that time, use this Gemfile. ② Gemfile.lock

Gemfile.lock


The contents of Gemfile.lock are empty and OK. Gemfile.lock is required as a set with Gemfile, so prepare it.

③ Dockerfile

Dockerfile


FROM ruby:2.5.7
#Specify the image to base on

RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs default-mysql-client vim
#Install the packages needed to install Rails and connect to MySQL

RUN mkdir /myapp
#Create myapp directory in the container

WORKDIR /myapp
#Set the created myapp directory as a working directory

COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
#Local Gemfile and Gemfile.Copy lock under myapp in the container

RUN bundle install
#Bundle install of Gemfile copied in container

COPY . /myapp
#Copy files under myapp locally to under myapp in the container

Dockerfile is like a design document for creating your own image. This time, based on the image originally created by someone called ruby: 2.5.7, you can install the packages required for Rails installation, copy myapp, bundle install, and so on. I'm adding things.

④ docker-compose.yml

docker-compose.yml


version: '3'
# docker-Specifies the format version of compose.(In principle, specify the latest)

services:
  db:
    image: mysql:5.7
    environment:
      MYSQL_USER: root
      MYSQL_ROOT_PASSWORD: pass
    ports:
      - "3306:3306"
    volumes:
      - mysql_data:/var/lib/mysql

  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/myapp/
    ports:
      - 3000:3000
    depends_on:
      - db

volumes:
  mysql_data:

docker-compose.yml is a file for defining and executing multiple containers. I will explain each word written in the file one by one.

services: Define a container in services. This time, we will containerize MySQL and Rails, so name them db and web for easy understanding. The container name that is actually created is __ "application name + service name + serial number" __ and becomes myapp_db_1, myapp_web_1.

image: Specify the image to be used and build the container.

enviroment: Set environment variables for MySQL. (This time only the password) You can decide the password yourself.

ports: Set for the port number. The default port numbers are 3306 for MySQL and 3000 for Rails. As for Rails, it's the familiar localhost: 3000 3000. If you built the environment locally without using Docker, you should have accessed this localhost: 3000 directly, but if you use Docker, you can not access the container from the outside, so a little ingenuity is required. .. That's where the ports: comes in. ports: is represented by -local port number: container port number. This time, it is set to ports: --3000: 3000, but for the sake of clarity, let's assume that it is set to ports: --9000: 3000. This means that the 3000 port on the container side is allowed access on the 9000 port. So in this case it will be accessible at localhost: 9000.

volumes: Make settings related to the volume. The volume is for permanently storing the data generated in the container. Taking MySQL as an example, suppose you containerize MySQL and store data such as tables and columns in it. However, if you delete the MySQL container, the saved data such as tables and columns will also be deleted. It's bad, so this mechanism of storing only data locally is called volume. By using this volume, even if you delete a container, you can reuse the data when you start a new container.

There are two main ways to save a volume. The first is __ how to mount the directory you want to save __, and the second is __ how to use a named volume __.

This time MySQL uses the second method of using named volumes.

This method is to create a locally named volume and store the specified directory on the container side there. For named volumes, volumes: is represented by -volume name: the directory you want to save on the container side. This time, the volume name is mysql_data, and the directory to be saved on the container side is/var/lib/mysql, which is the data storage location of MySQL. The result is volumes: --mysql_data:/var/lib/mysql. Then, in the same paragraph (top level) as version: and services :, write the volume name in volumes: written at the bottom to clearly indicate that it is a named volume.

Then Rails uses the first method to mount the directory you want to save. This method synchronizes the "specified directory on the local side" and the "specified directory on the container side" to keep them in the same state at all times. When mounting, volumes: is represented by -the directory you want to synchronize on the local side: the directory you want to synchronize on the container side. This time, it is volumes: --.:/Myapp /. In other words, it synchronizes . (the directory where docker-compose.yml is located, that is, under myapp on the local side) and/myapp/(under myapp on the container side). As a result, if you edit a file under the local myapp after starting the container, the edit will be reflected on the container side as well.

build: Specify the directory where Dockerfile is located. (Since Dockerfile exists in the same directory as seen from docker-compose.yml, .) In this step, create an original image using the Dockerfile explained in ③ and build a container.

command: Set the command to be executed when the container starts. The command to set this time is bash -c" rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0' ". bash -c" " is an instruction to go inside the container and execute the command in " ". If the rails server was started with rm -f tmp/pids/server.pid, it will be stopped. (Because the new rails server cannot be started if the rails server has already started for some reason.) Start the rails server with bundle exec rails s -p 3000 -b '0.0.0.0'. && is used when executing multiple commands.

depends_on: This is the setting of the container creation order. depends_on: --db means that the MySQL container is started and then the Rails container is started.

Run rails new

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

__ Go to your application's working directory __, run rails new with the docker-compose run command.

--force: Overwrite if the same file exists --database = mysql: Specify MySQL for the database --skip-bundle: Skip bundle install. (Bundle install will be done later.)

This command creates an image and a container based on the Dockerfile and runs rails new in that container. Therefore, I think that the file that you are familiar with locally has been created.

Run docker-compose build

$ docker-compose build

Now that Gemfile has been updated with rails new, run docker-compose build to bundle install. The image created by docker-compose run earlier is an image of bundle install the Gemfile before rails new, so recreate the image of the updated Gemfile`` bundle install. need to do it.

Edit database.yml

/config/database.yml


# MySQL. Versions 5.1.10 and up are supported.
#
# Install the MySQL driver
#   gem install mysql2
#
# Ensure the MySQL gem is defined in your Gemfile
#   gem 'mysql2'
#
# And be sure to use new-style password hashing:
#   https://dev.mysql.com/doc/refman/5.7/en/password-hashing.html
#
default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
#Edit the following two lines
  password: pass
  host: db

development:
  <<: *default
  database: myapp_development

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
  <<: *default
  database: myapp_test

# As with config/secrets.yml, you never want to store sensitive information,
# like your database password, in your source code. If your source code is
# ever seen by anyone, they now have access to your database.
#
# Instead, provide the password as a unix environment variable when you boot
# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
# for a full rundown on how to provide these environment variables in a
# production deployment.
#
# On Heroku and other platform providers, you may have a full connection URL
# available as an environment variable. For example:
#
#   DATABASE_URL="mysql2://myuser:mypass@localhost/somedatabase"
#
# You can use this database configuration with:
#
#   production:
#     url: <%= ENV['DATABASE_URL'] %>
#
production:
  <<: *default
  database: myapp_production
  username: myapp
  password: <%= ENV['MYAPP_DATABASE_PASSWORD'] %>

Edit password: and host:. For password:, enter the password written in MYSQL_ROOT_PASSWORD: of docker-compose.yml. In host:, enter the MySQL container name db named in docker-compose.yml.

Start container with docker-compose up -d

$ docker-compose up -d

Start the container with this command. Start in the background by adding -d.

The container is now ready to start. Let's check if it is started properly with the following command.

$ docker-compose ps

   Name                  Command               State                 Ports              
----------------------------------------------------------------------------------------
myapp_db_1    docker-entrypoint.sh mysqld      Up      0.0.0.0:3306->3306/tcp, 33060/tcp
myapp_web_1   bash -c rm -f tmp/pids/ser ...   Up      0.0.0.0:3000->3000/tcp 

In this way, if each container is UP, the startup is successful.

Create database

Create the database with the following command.

$ docker-compose exec web rails db:create

Access localhost: 3000

If you access localhost: 3000 and the following page is displayed, Docker's Rails environment construction is complete. スクリーンショット 2021-01-20 19.39.11.png

How to develop after building the environment

As before, __file editing etc. is done in the basic local environment __. However, __rails commands __ such as rails g and rails db: migrate run inside the container. So you have to put it in a container. Enter the Rails container with the following command.

$ docker-compose exec web bash

Then the prompt (the left part of the terminal) will change as follows. (The ID of the container after @)

root@97b8e3430f3f:/myapp#

Now you have it in the container. Now run the rails command. In the development in the local environment so far, all the commands executed in the terminal are executed in this container __. (The git command is done in the local environment)

To exit the container, execute the following command. (Or control + d)

$ exit

--Display log -

I think I used to start the server with rails s and see the logs. The method to display the log in the container is as follows.

__ Enter the container __ and execute the following command.

$ tail -f log/development.log

The log is now displayed.

It is efficient to open three tabs in the terminal and divide them into __ "tabs for local operations" __, __ "tabs for executing rails commands in the container" __, and __ "tabs for displaying logs" __ You may be able to develop.

--About debugging -

Suppose you get an error during development and you edit and debug your controller. In this case, I think you will restart the server once.

For Docker, restart each Rails container (myapp_web_1) with the following command.

$ docker-compose restart web

However, it is inefficient to restart the container every time you debug. So, set it to check for code updates even while the server is running.

The following listed at the bottom of config/environments/development.rb

config.file_watcher = ActiveSupport::EventedFileUpdateChecker

Make the following changes:

config.file_watcher = ActiveSupport::FileUpdateChecker

Now you can debug without rebooting.

Other basic Docker commands

-Stop and delete containers

$ docker-compose down

· Stop and delete containers and delete associated named volumes

$ docker-compose down -v

__ * Be careful that the persistent data disappears! __

-Output docker-compose log

$ docker-compose logs

If you look at the log when the container does not start properly, you can find out the cause.

・ List of images

$ docker images

-Delete image

$docker rmi image ID

· List of named volumes

$ docker volume ls

· Delete named volume

$docker volume rm The name of the volume

Summary

I think there are many things that you can't understand at first. I think it is better to understand vaguely as a whole, rather than steadily understanding one by one.

__ If you have any questions after reading this article, please comment and we will answer! __

Thank you for reading until the end.

reference

Recommended Posts

How to build a Ruby on Rails environment using Docker (for Docker beginners)
How to build a Ruby on Rails development environment with Docker (Rails 6.x)
How to build a Ruby on Rails development environment with Docker (Rails 5.x)
How to build Rails 6 environment with Docker
Steps to build a Ruby on Rails development environment with Vagrant
Build a development environment for Docker + Rails6 + Postgresql
[Rails] How to build an environment with Docker
How to build a Pytorch environment on Ubuntu
How to create a query using variables in GraphQL [Using Ruby on Rails]
[Docker] How to create a virtual environment for Rails and Nuxt.js apps
How to build an environment for any version of Ruby using rbenv
Build a Ruby on Rails development environment on AWS Cloud9
How to build docker environment with Gradle for intelliJ
Try to build a Java development environment using Docker
[2021] Build a Docker + Vagrant environment for using React / TypeScript
Build a development environment to create Ruby on Jets + React apps with Docker
How to use Ruby on Rails
How to build CloudStack using Docker
[Procedure 1 for beginners] Ruby on Rails: Construction of development environment
How to display a graph in Ruby on Rails (LazyHighChart)
How to build Rails, Postgres, ElasticSearch development environment with Docker
Explanation of Ruby on rails for beginners ③ ~ Creating a database ~
[Rails] [Docker] Copy and paste is OK! How to build a Rails development environment with Docker
How to quit Docker for Mac and build a Docker development environment with Ubuntu + Vagrant
Explanation of Ruby on rails for beginners ④ ~ Naming convention and how to use form_Tag ~
[Ruby on Rails] About bundler (for beginners)
[Ruby] How to use slice for beginners
[Ruby on Rails] How to use redirect_to
[Ruby on Rails] How to use kaminari
Explanation of Ruby on rails for beginners ①
[RSpec on Rails] How to write test code for beginners by beginners
Build a development environment where Ruby on Rails breakpoints work on Windows
How to build Rails + Vue + MySQL environment with Docker [2020/09 latest version]
Create a development environment for Ruby 3.0.0 and Rails 6.1.0 on Ubuntu 20.04.1 LTS
[Ruby on Rails] How to avoid creating unnecessary routes for devise
<For super beginners> Why don't you make a chatbot using "Talk API"? ?? [Ruby on Rails]
Build debug environment on container --Build local development environment for Rails tutorial with Docker-
[Rails] How to create a graph using lazy_high_charts
Building a Ruby environment for classes on Mac
How to build an environment with Docker, which is the minimum required to start a Rails application
Build a local development environment for Rails tutorials with Docker (Rails 6 + PostgreSQL + Webpack)
[Ruby on Rails] Let's build an environment on mac
How to solve the local environment construction of Ruby on Rails (MAC)!
[For beginners] Procedure for creating a controller using rails
Build a development environment for Docker, java, vscode
[Ruby on Rails] How to install Bootstrap in Rails
Template: Build a Ruby / Rails development environment with a Docker container (Ubuntu version)
[Ruby on Rails] How to use session method
[Ruby On Rails] How to search the contents of params using include?
[For beginners] Build the environment for Ruby on Rails Tutorial 4th Edition (Rails 5.1) in less than an hour!
Template: Build a Ruby / Rails development environment with a Docker container (Mac version)
[Ruby on Rails] When logging in for the first time ・ How to split the screen in half using jQuery
How to implement image posting function using Active Storage in Ruby on Rails
Memo to build a Servlet environment on AWS EC2
[Road _node.js_1-1] Road to build Node.js Express MySQL environment using Docker
[Introduction] Try to create a Ruby on Rails application
How to implement login request processing (Rails / for beginners)
[Rails] How to create a signed URL for CloudFront
How to build Docker + Springboot app (for basic learning)
[Ruby on Rails] How to write enum in Japanese
Docker the development environment of Ruby on Rails project