This time, I will introduce the procedure to build a Docker environment for a web application that combines Rails 6 and MySQL 8.
Webpacker is installed as standard from Rails 6, and the user authentication method has changed from MySQL 8, so there were various parts that were clogged with environment construction, so I hope it will be helpful.
In order to enable smooth development by multiple people, the goal is to create an environment where ** if you clone from a remote repository, the application will start up just by docker-compose up **.
The various versions are as follows.
The execution environment uses Docker Desktop for Mac (version 2.3.0.4).
Create / move directories.
The Rails application name created this time is sample_app
.
$ mkdir sample_app && cd $_
Create a Gemfile in the local environment using the ruby image.
-v
is an option for bind mount (synchronization of host and container directories).
By synchronizing the current directory of the host and the working directory of the container, the files created on the container are placed on the host.
$ docker run --rm -v `pwd`:/sample_app -w /sample_app ruby:2.7.1 bundle init
Uncomment the gem" rails "
part of the created Gemfile and specify the rails version.
Gemfile
# frozen_string_literal: true
source "https://rubygems.org"
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
- # gem "rails"
+ gem "rails", '~> 6.0.3.2'
Create a Dockerfile to build a Docker environment that runs rails new
.
Rails 6 also runs rails webpacker: install
when you create your application, so don't forget to install ** yarn. ** **
This time, use npm
to install yarn
.
Dockerfile
FROM ruby:2.7.1
RUN apt-get update -qq && \
apt-get install -y nodejs \
npm && \
npm install -g yarn
#Working directory/sample_Specified in app
WORKDIR /sample_app
#Copy local Gemfile to Dokcer
COPY Gemfile /sample_app/Gemfile
# /sample_bundle install on the app directory
RUN bundle install
Start the container using the image created by building the Dockerfile, and do rails new
on the container.
$ docker build -t sample_app .
$ docker run --rm -v `pwd`:/sample_app sample_app rails new . –skip-bundle --database=mysql
Create docker-compose.yml
.
This time, for the sake of brevity, I am connecting to MySQL as the root user. When connecting as a general user, it is necessary to set the following environment variables for the MySQL image.
Environment variable | Contents |
---|---|
MYSQL_USER | username |
MYSQL_PASSWORD | User password |
Database information is persisted by creating a named volume named mysql_data
.
docker-compose.yml
version: '3'
services:
web: #Container launched by Ruby on Rails
build: .
ports:
- '3000:3000' #Make it accessible on localhost port 3000
volumes:
- .:/sample_app #Application file synchronization
depends_on:
- db
command: ["rails", "server", "-b", "0.0.0.0"]
db: #Container that MySQL starts
image: mysql:8.0.21
volumes:
- mysql_data:/var/lib/mysql #Data persistence
command: --default-authentication-plugin=mysql_native_password #Set the authentication method to 8 series or earlier.
environment:
MYSQL_ROOT_PASSWORD: 'pass'
MYSQL_DATABASE: 'sample_app_development'
volumes:
mysql_data: #Data volume registration
--Reference: Docker data is persisted! Introduction to environment construction starting from understanding Data Volume
I will give a supplementary explanation of the above docker-compose.yml
.
The MySQL Docker image will create a database with the name set in MYSQL_DATABASE
.
Since the development environment of Ruby on Rails uses the database [application name] _development
, sample_app_development
is registered in MYSQL_DATABASE
.
Now you can have a database for your development environment without running rails db: create
.
Starting with MySQL 8, the authentication method has changed from mysql_native_password
to caching_sha2_password
.
With the MySQL 8 standard caching_sha2_password
authentication method, it is not possible to connect to the database, and the following error message is displayed when starting the Rails application.
Mysql2::Error::ConnectionError
Plugin caching_sha2_password could not be loaded: /usr/lib/x86_64-linux-gnu/mariadb19/plugin/caching_sha2_password.so: cannot open shared object file: No such file or directory
Therefore, --default-authentication-plugin = mysql_native_password
is used to use the previous authentication method of mysql_native_password
.
Next, set up the database connection for Ruby on Rails.
config/database.yml
default: &default
adapter: mysql2
encoding: utf8
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: root
password: pass # (This time it's root)MYSQL_ROOT_Match with PASSWORD
host: db #Set the database container name
Now that you have a Docker environment that combines Ruby on Rails 6 and MySQL 8, let's start it.
$ docker-compose up
It is OK if you access localhost: 3000
and the following screen is displayed.
Let's also check the persistence of data in the database.
As an example, create a function related to ʻevent with
scaffold`.
$ docker-compose exec web rails g scaffold event title:string
$ docker-compose exec web rails db:migrate
Access localhost: 3000 / events / new
and register a record called" sample event "as an example.
It is OK if the data remains even if the container is deleted and started.
$ docker-compose down
$ docker-compose up
$ docker-compose exec web rails c
> Event.last.title
=> "Sample event"
By creating a database of development environment using MYSQL_DATABASE
, Rails application can be started without executing db: create
.
However, ** this alone is not enough as a development environment ** because the database ([application name] _test
) of the test environment used in the test code has not been created.
In MySQL Docker image, if you place scripts (.sql
, .sh
, .sql.gz
) in /docker-entrypoint-initdb.d
, it will be executed when the container starts. there is. [^ docker-entrypoint-initdb]
Take advantage of this feature to create a database for the test environment.
** Scripts are executed in alphabetical order **, so if there is a dependency between scripts, be aware of the file name as well.
$ mkdir docker-entrypoint-initdb.d && cd $_
$ vim 00_create.sql
00_create.sql
--Create a database for the test environment
CREATE DATABASE sample_app_test;
In addition, the database access right of general users is only the database set to MYSQL_DATABASE
.
Therefore, ** When connecting to a database as a general user, it is necessary not only to create the database but also to grant the following access rights. ** **
01_grant.sql
--When using a general user named webapp
GRANT ALL ON `sample_app_test`.* TO webapp@'%';
Add a bind mount so that the script you created can be read on the container.
docker-compose.yml
version: '3'
services:
web:
build: .
ports:
- '3000:3000'
volumes:
- .:/sample_app
depends_on:
- db
command: ["rails", "server", "-b", "0.0.0.0"]
db:
image: mysql:8.0.21
volumes:
- mysql_data:/var/lib/mysql
+ - ./docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
command: --default-authentication-plugin=mysql_native_password
environment:
MYSQL_ROOT_PASSWORD: 'pass'
MYSQL_DATABASE: 'sample_app_development'
volumes:
mysql_data:
Delete and start the db container and check if the database is created automatically.
It is OK if the databases of [application name] _development
and [application name] _test
exist.
#Create a database from scratch, also deleting database persistence information
$ docker-compose down --volumes
$ docker-compose up
$ docker-compose exec db mysql -uroot -ppass -D sample_app_development
mysql> show databases;
+------------------------+
| Database |
+------------------------+
| information_schema |
| mysql |
| performance_schema |
| sample_app_development |
| sample_app_test |← db for test has been created
| sys |
+------------------------+
6 rows in set (0.00 sec)
You can also connect successfully from a Rails application.
$ docker-compose exec web rails c
> ENV['RAILS_ENV']
=> "development
$ docker-compose exec web rails c -e test
> ENV['RAILS_ENV']
=> "test"
The following patterns are often seen as a way to build a Rails development environment with Docker.
#Launch the container
$ docker-compose up
#Creating a database
$ docker-compose exec web rails db:create
#Table migration
$ docker-compose exec web rails db:migrate
The Rails application and the database are linked by docker-compose, and the database used by the application is built on the container.
There is no problem with the above method, but in this case ** It takes time to manually create the database and table on the container at the first startup. ** **
Considering the case of sharing the Docker environment with multiple people, it is ideal that the environment can be started by just doing docker-compose up
after cloning the application file from the remote repository.
If possible, I would like to avoid the situation where I have to bother to tell the shared members "Create the database on the container at the first startup".
From here, if you clone from a remote repository, just do docker-compose up
to configure the settings so that the Rails application can be started.
In the development environment, source code synchronization using bind mout (synchronization of host and container directories) is often used.
The application file cloned from the remote repository has a package.json
but no node_modules
directory.
Therefore, if you do docker-compose up
after cloning, the directory without node_modules
will be bind-mounted. As a result, an error prompting you to execute yarn install
occurs and the application cannot be started.
========================================
Your Yarn packages are out of date!
Please run `yarn install --check-files` to update.
========================================
To disable this check, please change `check_yarn_integrity`
to `false` in your webpacker config file (config/webpacker.yml).
yarn check v1.22.5
info Visit https://yarnpkg.com/en/docs/cli/check for documentation about this command.
To prevent this, you need to run yarn install
when starting the container.
For example, you can run yarn install
at container startup by doing the following:
docker-compose.yml
version: '3'
services:
web:
build: .
+ command: ["./start.sh"]
- command: ["rails", "server", "-b", "0.0.0.0"]
ports:
- '3000:3000'
volumes:
- .:/sample_app
depends_on:
- db
db:
image: mysql:8.0.21
command: --default-authentication-plugin=mysql_native_password
environment:
MYSQL_ROOT_PASSWORD: 'pass'
MYSQL_DATABASE: 'sample_app_development'
start.sh
#!/bin/bash -eu
yarn
rails server -b '0.0.0.0'
Change shell permissions.
$ chmod 755 start.sh
I call the shell with the command when starting the container, and execute yarn install
in the shell.
After executing yarn install
, the Rails application will start on rails server
, so the previous error will be resolved.
As with yarn install
, table migration should be executed by a shell script that is called when the container is started.
start.sh
#!/bin/bash -eu
yarn
+ rails db:migrate
rails server -b '0.0.0.0'
However, although you can control the container startup order by using depends_on
of docker-compose.yml
, you cannot wait for the container to start [^ startup-order], so prepare to start the db container. If db: migrate
is executed before the end of, the migration will not be successful.
[^ startup-order]: Docker Doc "Control of startup order in Compose"
To automate the migration, you need to wait for the db container to start before db: migrate
is executed.
There are several ways to wait for the database to be ready, but this time wait-for-it Here's how to use>.
If you place wait-for-it.sh
in the current directory and make docker-compose.yml
as follows, the script will be executed after waiting for the database to start.
docker-compose.yml
version: '3'
services:
web:
build: .
- command: [""./start.sh"]
+ command: ["./wait-for-it.sh", "db:3306", "--", "./start.sh"]
ports:
- '3000:3000'
volumes:
- .:/sample_app
depends_on:
- db
db:
image: mysql:8.0.21
command: --default-authentication-plugin=mysql_native_password
environment:
MYSQL_ROOT_PASSWORD: 'pass'
MYSQL_DATABASE: 'sample_app_development'
This concludes the introduction of the procedure for building a Docker environment for Rails 6 + MySQL 8.
By executing the shell when the container is started, the development environment can be built smoothly even when multiple people use the Docker environment.
This time, by calling the shell when the container is started, yarn install
and db: migration
are surely executed, but the command control when the container is started is <a href="https: //". You can also do it with a tool called github.com/progrium/entrykit "target="_blank"> Entrykit .
--Summary of this time --From Rails 6, you need to install yarn in advance. --If a MySQL 8 authentication error occurs, resolve it by reverting to standard authentication. --By executing the shell when the container is started, it is possible to automate table creation etc.
I'm doing Twitter ( @ nishina555 ). I hope you will follow me!
-Recommendation of Entrykit -Too polite Docker-compose rails5 + MySQL on Docker environment construction (Docker for Mac) -Steps for building a Ruby on Rails environment with Docker [Rails 6 compatible] -Detailed explanation of Ruby on Rails environment construction with Docker -I was addicted to building a Rails 6 development environment with Docker
Recommended Posts