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 __)
Docker is __ a tool for building virtual environments __.
__Docker engine __ is the core of Docker, which creates Docker images and starts containers.
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
.
The __Docker image __ is like a recipe for creating a container. There are countless other Docker images such as CentOS, MySQL, Ruby, and more.
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.
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.
--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 a working directory with mkdir
.
$ mkdir myapp
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.
$ 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.
$ 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.
/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
.
$ 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 the database with the following command.
$ docker-compose exec web rails db:create
If you access localhost: 3000
and the following page is displayed, Docker's Rails environment construction is complete.
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
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.
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.
$ docker-compose down
$ docker-compose down -v
__ * Be careful that the persistent data disappears! __
$ docker-compose logs
If you look at the log when the container does not start properly, you can find out the cause.
$ docker images
$docker rmi image ID
$ docker volume ls
$docker volume rm The name of the volume
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.
Recommended Posts