概要
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
-
wait-for-it.sh
は https://github.com/vishnubob/wait-for-it からいただきました。最初にchmod +x scripts/wait-for-it.sh
で実行権限を付加しておいてください。
初期ファイル設定
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
を立ち上げないようにしています。
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 をホスト(ローカル)からゲスト(コンテナ)にコピーしています。
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
#!/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
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
で認証プラグインを変更しておきます。
[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
フォルダではシェルの実行も可能なので、組み込み次第ではいろいろと対応できるようです。
CREATE DATABASE IF NOT EXISTS `app_test`;
GRANT ALL ON app_test.* TO 'user'@'%';
Gemfile
Gemfile
初期は Rails のバージョン指定のみとなります。
source 'https://rubygems.org'
gem 'rails', '~>6'
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 へのアクセス設定を変更します。
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 の初期画面が表示されます。
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
実際の 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)
今回は以上となります。
参考資料
以下の記事、情報を参考にさせていただきました。
ありがとうございます。