DockerでRuby3.1、Rails7.0、MySQL8.0の環境構築

業務でnginxを触っていた中で、より体系的に学んでみたいと思い「nginx実践入門」という書籍を読みました。

当然nginxであれこれする環境を作りたくなったので、まずはアプリケーションとしてrails7系をdockerで構築したメモです。nginxコンテナじゃないんかいとツッコミがありそうですが、nginxコンテナの導入は次の記事で書きます(余力がない)。

環境

準備

基本的な流れは[railsのコンテナを作るための準備をする → railsのコンテナを作る → DBとつなげる]となります。

まずは準備です。

作業ディレクトリ作成

今回はapp > docker > webと掘ります。こんな構成です。

app
└─ docker
    └─ web
       ├── Dockerfile
       ├── entrypoint.sh
       └── start-server.sh
# ディレクトリ作成
$ mkdir -p /app/docker/web
$ cd /app/docker/web

# Dockerfile作成
$ touch Dockerfile

Dockerfile

ruby3.1のイメージを使います。

FROM ruby:3.1

ENV LANG C.UTF-8
ENV APP_ROOT /app
ENV BUNDLE_JOBS 4
ENV BUNDLER_VERSION 2.2.25

RUN mkdir $APP_ROOT
WORKDIR $APP_ROOT

COPY Gemfile $APP_ROOT/Gemfile
COPY Gemfile.lock $APP_ROOT/Gemfile.lock

RUN gem install bundler -v $BUNDLER_VERSION
RUN bundle -v
RUN bundle install

COPY . $APP_ROOT

# Add a script to be executed every time the container starts.
COPY ./docker/web/entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]

EXPOSE 3000

# 起動の度にデフォルトでrails sする(entrypoint.shに持たせても良い)
CMD ["sh", "./docker/web/start-server.sh"]

entrypoint.sh

$ vim entrypoint.sh
#!/bin/bash
set -e

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

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

start-server.sh

$ vim start-server.sh
#!/bin/sh
echo "run start-server.sh" &
rails server -p 3000 -b 0.0.0.0

gemfile

railsをbundle installできるようにします。

$ cd ../.. # /app
$ bundle init
$ vim Gemfile
# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

gem 'rails', '~>7.0.2'

gemfile.lock

ファイルの中身は空でOKです。

$ touch gemfile.lock

docker-compose.yml

mysqlは8.0にしてみます。dataの永続化は/volumes/mysql/data/で行いました。環境変数は適当なので真似しないでね。

$ cd ../.. # /app
$ mkdir -p /volumes/mysql/data/
version: '3'

services:
  web:
    build:
      context: .
      dockerfile: ./docker/web/Dockerfile
    volumes:
      - .:/app # カレントディレクトリ(後のアプリケーションディレクトリ)をマウント
        ports:
      - '3000:3000'
    depends_on:
      - db
    stdin_open: true
    tty: true
    environment:
      DB_ROOT_USERNAME: root
      DB_USERNAME: development
      DB_PASSWORD: password
      TZ: Asia/Tokyo
      DB_HOST: db

  db:
    platform: linux/x86_64
    image: mysql:8.0
    environment:
      - MYSQL_DATABASE=development
      - MYSQL_USER=development
      - MYSQL_PASSWORD=password
      - MYSQL_ROOT_PASSWORD=passwordpassword
      - TZ=Asia/Tokyo
    ports:
      - '3306:3306'
    command: --default-authentication-plugin=mysql_native_password
    volumes:
      - ./volumes/mysql/data/:/var/lib/mysql

railsアプリの作成

ここまでくればdocker-compose runにてビルドし、railsアプリを作成することが可能です。データベースはmysqlを指定します。

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

rails newによりGemfileが更新されたので、再度イメージをビルドし、railsアプリとしてのイメージを作成します。

$ docker-compose build

mysqlとの繋ぎ込み

この時点でdocker-compose upをしてからlocalhost:3000でアプリケーションにアクセスすることは可能になっていますが、DBとの繋ぎ込みができていないため、ActiveRecord::DatabaseConnectionErrorが返ってきているはずです。

docker-compose.ymlのwebコンテナに記載した環境変数に合わせて、database.ymlを編集していきましょう。dbコンテナでMYSQL_DATABASE、MYSQL_USER、MYSQL_PASSWORDを指定していたため、データベースとともに権限を持ったユーザーも作成されているはずなので、そちらを指定していきます。

$ vim config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: <%= ENV["DB_USERNAME"] %>
  password: <%= ENV["DB_PASSWORD"] %>
  host: db # コンテナ名(db)で名前解決

development:
  <<: *default
  database: development

本格的な開発フェーズに入ったら、ENV.fetchやcredentialsを組み合わせて設定するようにします(割愛します)。

動作確認

コンテナを起動します。

$ docker-compose up -d

localhost:3000にアクセスします。

rails7.0
hello world

お疲れさまでした!

mysqlコンテナの環境変数について

補足です。

上記docker-compose.ymlのmysqlコンテナで、MYSQL_DATABASE、MYSQL_USER、MYSQL_PASSWORD、MYSQL_ROOT_PASSWORDを設定しています。

mysqlのdockerのドキュメントを見ればわかりますが、特にMYSQL_USER、MYSQL_PASSWORDを設定しておくと、MYSQL_DATABASEで指定したデータベースに対するスーパーユーザー権限が付与されたユーザーを作ってくれます。

$ docker-compose exec db bash -c 'mysql -u root -p${MYSQL_ROOT_PASSWORD}'

mysql> select user, host from mysql.user;
+------------------+-----------+
| user             | host      |
+------------------+-----------+
| development      | %         |
| root             | %         |
| mysql.infoschema | localhost |
| mysql.session    | localhost |
| mysql.sys        | localhost |
| root             | localhost |
+------------------+-----------+
6 rows in set (0.04 sec)

mysql> show grants for development;
+--------------------------------------------------------------+
| Grants for development@%                                     |
+--------------------------------------------------------------+
| GRANT USAGE ON *.* TO `development`@`%`                      |
| GRANT ALL PRIVILEGES ON `development`.* TO `development`@`%` |
+--------------------------------------------------------------+
2 rows in set (0.00 sec)

次回はnginxコンテナです。

書きました↓

itoka.hatenadiary.com

参考記事

Rails 7 + MySQLの環境構築をDocker composeで作る - Qiita

クィックスタート: Compose と Rails — Docker-docs-ja 19.03 ドキュメント

Docker Hub

Ruby on Railsの開発環境をDockerで構築する方法(Rails 6.x)