LoginSignup
5
1

More than 3 years have passed since last update.

Ruby 3.x, Rails 6.x, MySQL 8.x の Docker 環境構築。

Last updated at Posted at 2021-02-25

概要

Docker と docker-compose を使い、アプリケーションサーバを Ruby 3.x, Ruby on Rails 6.x、DB サーバを MySQL 8.x でコンテナの構築するまでの手順となります。

なお、この記事ではセキュリティについての考慮は一切していません。

Windows 10 の WSL2 - Ubuntu 18.04 と Mac で確認していますが、後述する理由により Mac の方がお勧めです。

PostgreSQL の方が好みの方は、以下のページをご確認ください。
cf. クィックスタート: Compose と Rails

前提

下記の環境が設定されていること。

Windows 10

  • WSL2 Ubuntu 18.04+
  • Docker
  • docker-compose
  • MySQL Client(必要に応じて)

Mac

  • Docker
  • docker-compose
  • Docker Desktop for Mac
  • MySQL Client(必要に応じて)

また、Docker のサービスが起動済みであること。

初期ファイル構成

以下のファイルから Rails プロジェクトを新規に作成します。

.
├── docker
│   ├── app
│   │   ├── Dockerfile
│   │   └── entrypoint.sh
│   └── db
│       ├── Dockerfile
│       ├── conf.d
│       │   └── my.cnf
│       └── initdb.d
│           └── init.ddl.sql
├── scripts
│   └── wait-for-it.sh
├── docker-compose.yml
├── Gemfile
└── Gemfile.lock

初期ファイル設定

docker-compose.yml

注意点は app 側の build: context: で基準になるフォルダを root として、Dockerfile のパスを指定しているところです。
これは Dockerfile 内で Gemfile をコピーする必要があるのですが、build: ./docker/app としてしまうとフォルダを遡って Gemfile の操作ができないため、起点を root にしています。
Dockerfile が root にあれば関係ないのですが、今回は docker フォルダ下にしているため、このような対応となります。

また app の command: で先述の wait-for-it.sh を利用し、DB が起動するまで rails server を立ち上げないようにしています。

docker-compose.yml
version: "3.3"

services:
  db:
    container_name: "db"
    build: ./docker/db
    restart: always
    tty: true
    environment:
      MYSQL_DATABASE: app_development
      MYSQL_USER: user
      MYSQL_PASSWORD: password
      MYSQL_ROOT_PASSWORD: password
      TZ: 'Asia/Tokyo'
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    ports:
      - "3306:3306"
    volumes:
      - ./docker/db/conf.d:/etc/mysql/conf.d
      - ./docker/db/initdb.d:/docker-entrypoint-initdb.d
    networks:
      - backend

  app:
    container_name: "app"
    build:
      context: ./
      dockerfile: ./docker/app/Dockerfile
    ports:
      - "3000:3000"
    environment:
      PORT: 3000
      BINDING: 0.0.0.0
    tty: true
    depends_on:
      - "db"
    command: ["./scripts/wait-for-it.sh", "db:3306", "--", "bundle", "exec", "rails", "s", "-p", "3000", "-b", "0.0.0.0"]
    volumes:
      - .:/app
    networks:
      - frontend
      - backend

networks:
  frontend:
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 192.168.10.0/24
  backend:
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 192.168.20.0/24

app 用設定ファイル

docker/app/Dockerfile

ここで Gemfile および Gemfile.lock をホスト(ローカル)からゲスト(コンテナ)にコピーしています。

docker/app/Dockerfile
FROM ruby:3.0

ENV LANG C.UTF-8
ENV TZ Asia/Tokyo

RUN apt-get update -qq && \
    apt-get install -y --no-install-recommends sudo curl apt-transport-https wget build-essential libpq-dev nodejs default-mysql-client

RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
    echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
    apt-get update && \
    apt-get install --no-install-recommends -y yarn

RUN apt-get clean && \
    rm -rf /var/lib/apt/lists/*

RUN mkdir /app
WORKDIR /app
COPY Gemfile /Gemfile
COPY Gemfile.lock /Gemfile.lock
RUN bundle install
COPY . /app

COPY docker/app/entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

docker/app/entrypoint.sh

こちらは以下のサイトの entrypoint.sh からいただきました。
cf. Quickstart: Compose and Rails

docker/app/entrypoint.sh
#!/bin/bash
set -e

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

# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"

db 用設定ファイル

docker/db/Dockerfile

docker/db/Dockerfile
FROM mysql:8.0

RUN apt-get update -qq && \
    apt-get install -y --no-install-recommends locales && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/* && \
    locale-gen ja_JP.UTF-8

RUN sed -i -E 's/# (ja_JP.UTF-8)/\1/' /etc/locale.gen && locale-gen

ENV LANG ja_JP.UTF-8
ENV TZ Asia/Tokyo

docker/db/conf.d/my.cnf

my.cnf は文字コード指定が中心ですが、今回はシンプルに Rails の実行のみを考えているため、default_authentication_plugin=mysql_native_password で認証プラグインを変更しておきます。

docker/db/conf.d/my.cnf
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_bin
default-storage-engine=INNODB
explicit-defaults-for-timestamp=1
general-log=1
general-log-file=/var/log/mysql/mysqld.log

default_authentication_plugin=mysql_native_password

[mysqldump]
default-character-set=utf8mb4

[mysql]
default-character-set=utf8mb4

[client]
default-character-set=utf8mb4

docker/db/initdb.d/init.ddl.sql

データベースを development 以外に test 用も作成する場合のファイルです。
実際は test 用データベースは rake db:create で作成されるはずですのでなくても問題ないと思われます。
コピー先の /docker-entrypoint-initdb.d フォルダではシェルの実行も可能なので、組み込み次第ではいろいろと対応できるようです。

docker/db/initdb.d/init.ddl.sql
CREATE DATABASE IF NOT EXISTS `app_test`;
GRANT ALL ON app_test.* TO 'user'@'%';

Gemfile

Gemfile

初期は Rails のバージョン指定のみとなります。

Gemfile
source 'https://rubygems.org'
gem 'rails', '~>6'

Gemfile.lock

初期状態は空ファイルとなります。

Gemfile.lock

初期起動までの手順

rails new

Docker のサービスが起動しているか確認の上、docker-compose.yml があるフォルダで database を MySQL に指定して rails new を実行します。

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

問題なく実行が完了すると、実行したフォルダに Rails アプリのファイル群が作成されます。
このとき、Mac の場合は実行したユーザーの権限でファイルが作成されますが、WSL の場合は root 権限となり、そのままではファイルの更新が行えません。

cf. 【Docker】 WSL 2 を利用したコンテナー内開発で権限をどう設定するべきか

根本的な解決ではないとは思いますが、とりあえず以下のコマンドで権限を実行ユーザーに振り替えて対応することは可能です。
ただし、ここだけではなく、scaffold など rails のコマンドでファイルを作成・編集するごとに権限を書き換える必要があります。
(これが WSL よりも Mac をお勧めする理由です)

$ sudo chown -R $USER:$USER .

この時点で、作成された config/database.yml を編集し、MySQL へのアクセス設定を変更します。

config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: user
  password: password
  host: db

docker-compose build, up

build でサービスを作成し、問題がなければ up でコンテナを起動します。
この際、--no-cache オプションを付けるのは、bundle install 実行時に gem ファイルを巧く取り込めない場合がある(らしい)ためです。

$ docker-compose build --no-cache
$ docker-compose up -d

この時、db および app がほぼ同時に立ち上がりますが、docker-compose.yml で記述した通り、app の rails server は MySQL サーバとの接続が確立するまで実行されないようになっています。
それぞれのコンテナのログは docker logs で確認できるため、以下のように確認して下さい。

$ docker logs app # アプリケーションサーバのログ
$ docker logs db # DB サーバのログ

実行に問題がなければ、ブラウザまたは curl などで http://localhost:3000/ にアクセスすることで、いつもの Rails の初期画面が表示されます。

image.png

scaffold 作成と実行の確認

scaffold で MVC を作成して動作が可能か確認します。

$ docker-compose run app rails g scaffold user name:string email:string

WSL で操作している場合はファイル権限の変更をしてください。

$ sudo chown -R $USER:$USER .

db:migrate でテーブルを作成します。

$ docker-compose run app rails db:migrate

ブラウザで http://localhost:3000/users にアクセスすることで scaffold で作成した Rails 標準の UI が表示され、CRUD の一連の操作が可能なことが確認できます。

http://localhost:3000/users/new
image.png

http://localhost:3000/users
image.png

実際の DB を確認したい場合は MySQL Client が入っていればコマンドで確認できます。
(設定を変えていない場合は、user アカウントのパスワードは password となります)

$ mysql -u user -h 127.0.0.1 -D app_development -p
mysql> show create table users;
+-------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                                                                                                                                                                                                              |
+-------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| users | CREATE TABLE `users` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `email` varchar(255) DEFAULT NULL,
  `created_at` datetime(6) NOT NULL,
  `updated_at` datetime(6) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci |
+-------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> select * from users;
+----+------+-----------------+----------------------------+----------------------------+
| id | name | email           | created_at                 | updated_at                 |
+----+------+-----------------+----------------------------+----------------------------+
|  1 | test | aaa             | 2021-02-25 15:56:02.123864 | 2021-02-25 15:56:02.123864 |
|  2 | test | aaa@example.com | 2021-02-25 16:38:33.311981 | 2021-02-25 16:38:33.311981 |
+----+------+-----------------+----------------------------+----------------------------+
2 rows in set (0.00 sec)

今回は以上となります。

参考資料

以下の記事、情報を参考にさせていただきました。
ありがとうございます。

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1