2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Ruby on Railsの環境構築をDocker上で"ゼロから"やってみた

Posted at

はじめに

この記事では、Ruby on Railsのアプリを開発する際に必要となるDockerfileやcompose.ymlといったファイルをコピペではなくゼロから作成した手順を紹介します。環境構築の際に必要な情報はググればたくさんヒットするのですが、なぜそのような書き方をするのかを理解しないままコピペして使うのは個人的に嫌だったため、本記事を執筆するに至りました。

使用するDockerのバージョンは次の通りです。

docker --version                                              
# Docker version 27.1.1, build 6312585

本記事を参考に環境構築を行う場合、最終的に必要な手順は最終節に書かれているので、お急ぎの方はそちらをご覧ください。

コンテナ上で試行錯誤してみる

ゼロからいきなりDockerfile、compose.ymlを書くのはさすがに難しいです。そこで、まず最初に、Railsアプリを立ち上げるまでに必要なプロセスをコンテナ上で確認します。

コンテナを立ち上げるためのコマンドは次の通りです。

docker run -it -p 3000:3000 ruby:3.3.4-slim-bookworm bash

ベースイメージのバージョンは本記事執筆時の最新安定版です。今回使用しているベースイメージruby:3.3.4-slim-bookwormのサイズは約210MBで、slimでないruby:3.3.4-bookwormはサイズが約1GBになります。どちらを使ってもいいのですが、イメージサイズの小さい方がビルドにかかる時間を節約できるので、今回はruby:3.3.4-slim-bookwormをベースイメージとしています。(ただしruby:3.3.4-bookwormをベースイメージにするとこの後のプロセスのいくつかを省略することができます。)

コンテナを立ち上げた時点でrubyとBundlerを使用することができます。

bash
ruby --version
# ruby 3.3.4 (2024-07-09 revision be1089c8ec) [aarch64-linux]
bundle --version
# Bundler version 2.5.11

rails gemをインストールする

まず最初にRailsアプリを作成するディレクトリappを作ります。

bash
mkdir /app
cd /app

続いてappディレクトリでGemfileを作成します。

bash
bundle init
# Writing new Gemfile to /app/Gemfile

Gemfileを編集したいので以下の手順でvimをインストールします。

bash
apt-get update
apt-get install -y vim

vimでGemfileの最終行のコメントアウトを解除します。

Gemfile
# frozen_string_literal: true

source "https://rubygems.org"

gem "rails"

このGemfileをもとにrails gemをインストールします。

bash
bundle install

実はこのコマンドは失敗します。エラーメッセージの中に、

To see why this extension failed to compile, please check the mkmf.log which can be found here:

  /usr/local/bundle/extensions/aarch64-linux/3.3.0/bigdecimal-3.1.8/mkmf.log

という記述があるので、このログファイルを見てみます。

bash
cat /usr/local/bundle/extensions/aarch64-linux/3.3.0/bigdecimal-3.1.8/mkmf.log
# LD_LIBRARY_PATH=.:/usr/local/lib "gcc -o conftest -I/usr/local/include/ruby-3.3.0/aarch64-linux -I/usr/local/include/ruby-3.3.0/ruby/backward -I/usr/local/include/ruby-3.3.0 -I.    -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef  -fPIC conftest.c  -L. -L/usr/local/lib -Wl,-rpath,/usr/local/lib -L. -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,--no-as-needed     -Wl,-rpath,/usr/local/lib -L/usr/local/lib -lruby  -lm -lpthread  -lc"
# checked program was:
# /* begin */
# 1: #include "ruby.h"
# 2:
# 3: int main(int argc, char **argv)
# 4: {
# 5:   return !!argv[argc];
# 6: }
# /* end */
#

どうやらgccコマンドを利用しようとしているようですが、そもそもslimイメージにはgccは入っていません。

bash
gcc --version
# bash: gcc: command not found

そこでgccをインストールします。

bash
apt-get install -y gcc

インストールが完了した後、途中で終わっていたbundle installを再度実行します。すると今度は別のエラーが発生します。表示されるエラーメッセージを見ると、

make failedNo such file or directory - make

の記述があることがわかります。そこでmakeをインストールします。

bash
apt-get install -y make

インストール完了後、再度bundle installを実行すると、今度は成功し、Gemfile.lockが生成されます。ちなみにインストールされたrailsのバージョンは本記事執筆時の最新版である7.2.0です。

なお今回gccとmakeをインストールしましたが、代わりにbuild-essentialというパッケージをインストールしても構いません。(そちらの方が一般的かと思います。)このパッケージにはgccやmakeを含む様々なライブラリが含まれています。

アプリに必要なファイルをrails newで用意する

続いてRailsアプリを作成します。今回はDBにMySQLを使用します。(デフォルトではSQLiteです。)

bash
rails new . --database=mysql

するとGemfileを上書きするかの確認を求められるので上書きします。

実はこの時点でもエラーがでます。これは、rails new中に自動で実行されるgit initが失敗するからです。

bash
git --version
# bash: git: command not found

そこでgitを入れます。

bash
apt-get install -y git

git自体はrailsに必須のものではないですし、rails new--skip-gitオプションでgit initをスキップできます。しかしこのオプションは.gitignoreの自動生成もスキップしてしまいます。今回はこの.gitignoreを使用したいので--skip-gitオプションは用いないことにします。

gitを入れてもう一度rails newを実行すると、bundle install中にエラーが発生します。これはMySQLクライアントが入っていないためです。エラーメッセージに

mysql client is missing. You may need to 'sudo apt-get install libmariadb-dev', 'sudo apt-get install
libmysqlclient-dev' or 'sudo yum install mysql-devel', and try again.

とあるので、これを参考にしてMySQLクライアントをインストールします。まず今回はDebianを使用しているのでyum install mysql-develは使いません。またMariaDBとMySQLは微妙に違うので

bash
apt-get install -y libmysqlclient-dev

を選択します。しかしこのコマンドを実行すると

Package libmysqlclient-dev is not available, but is referred to by another package.
This may mean that the package is missing, has been obsoleted, or
is only available from another source
However the following packages replace it:
  libmariadb-dev-compat libmariadb-dev

E: Package 'libmysqlclient-dev' has no installation candidate

とエラーが表示され、MariaDBが提案されてしまいます。apt-cache searchでパッケージを検索するとlibmysqlclient-devではなくdefault-libmysqlclient-devがヒットします。

bash
apt-cache search libmysqlclient-dev
# default-libmysqlclient-dev - MySQL database development files (metapackage)

このパッケージはMySQLクライアントライブラリの開発用ヘッダーファイルとライブラリを提供するようです。

ちなみにこのパッケージはRailsガイドにも載っています。このパッケージを次のコマンドでインストールします。

bash
apt-get install -y default-libmysqlclient-dev

インストール完了後bundle installを実行すると今度は成功します。

DBサーバーに接続

次のコマンドでサーバーを立ち上げます。

bash
rails server -b 0.0.0.0

この-bオプションを使用しないとコンテナ外からアプリにアクセスできません。

http://0.0.0.0:3000 にアクセスするとエラーが表示されます。

ActiveRecord::ConnectionNotEstablished (Can't connect to local server through socket '/run/mysqld/mysqld.sock' (2)):

これはMySQLサーバーが起動していないためです。そこでMySQL用のコンテナを立ち上げます。先に現在railsの環境構築を行なっているコンテナを止めてdocker commitします。

docker commit <rails-container-id> myapp:version1

以下のコマンドでMySQLサーバーを立ち上げます。

docker network create myapp
docker run -d \
  --network myapp --network-alias mysql \
  -e MYSQL_ROOT_PASSWORD=secret \
  mysql:9.0.1-oraclelinux9

次にrailsのコンテナを立ち上げます。

docker run -it -p 3000:3000 \
  --network myapp \
  -e MYSQL_PASSWORD=secret \
  myapp:version1 bash

続いてconfig/database.ymlを編集してDBに接続するための情報を追記します。

bash
cd app
vim config/database.yml
config/database.yml
# 略
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
- password:
+ password: secret
- host: <%= ENV.fetch("DB_HOST") { "localhost" } %>
+ host: mysql
# 略

最後に次のコマンドでDBを作成します。

bash
rails db:create
# Created database 'app_development'
# Created database 'app_test'

その後、

bash
rails server -b 0.0.0.0

でサーバーを起動し、http://0.0.0.0:3000 にアクセスすると、Railsの初期画面が出ます。

rails_new_app.png

DBを操作できるようにする

次のコマンドでMySQLサーバーを立ち上げているコンテナに入ります。

docker exec -it <mysql-container-id> mysql -u root -p

このコンテナ上でDBの一覧を確認すると、先ほどrails db:createで作成したDBが表示されます。

mysql
SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| app_development    |
| app_test           |
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
6 rows in set (0.00 sec)

ただ通常Railsアプリを開発中にDBを操作するときはrails dbコマンドを使用します。このコマンドは現時点では使用することができないので、追加で設定が必要です。

まず次のコマンドでRailsのコンテナに入ります。

docker exec -it -w /app <rails-container-id> bash

試しにrails dbコマンドを叩くとエラーが表示されます。

bash
rails db
# Couldn't find database client: mysql, mysql5. Check your $PATH and try again.

MySQLクライアントが入っていないので、次のパッケージをインストールします。

bash
apt-get install -y default-mysql-client

インストール完了後、rails dbを実行するとDBに接続することができます。

MySQL [app_development]
SHOW TABLES;
+---------------------------+
| Tables_in_app_development |
+---------------------------+
| ar_internal_metadata      |
| schema_migrations         |
+---------------------------+
2 rows in set (0.003 sec)

以上でRailsアプリをゼロから立ち上げるために必要なプロセスがわかりました。このプロセスをDockerfileまたはcompose.ymlに書き起こしていきます。

Dockerfile、compose.ymlを書いてみる

まず最初にDockerfileを用意します。基本的にコンテナ上で行ったコマンドを書き起こせば大丈夫です。

Dockerfile
FROM ruby:3.3.4-slim-bookworm

RUN apt-get update && \
    apt-get install -y --no-install-recommends \
        gcc \
        make \
        git \
        default-libmysqlclient-dev \
        default-mysql-client && \
    rm -rf /var/lib/apt/lists/*

WORKDIR /app

COPY Gemfile Gemfile.lock /app/

RUN bundle install

EXPOSE 3000

CMD [ "bash" ]

apt-get install--no-install-recommendsオプションは、あるパッケージをインストールするときに推奨パッケージもインストールされてしまうのを防ぎます。

またrm -rf /var/lib/apt/lists/*はインストール時のキャッシュを削除します。これらの記述を追加することでイメージサイズの削減に繋がります。またapt-get updateapt-get installを一つのRUN句内に記述しているのはパッケージの最新バージョンが正しくインストールされるのを保証するためです。このあたりの話はDockerの公式ガイドに詳しい記述があります。

またGemfileは以下の内容で用意します。

Gemfile
# frozen_string_literal: true

source "https://rubygems.org"

gem "rails"

Gemfile.lockは空ファイルを用意します。

続いてcompose.ymlを用意します。ここにはdockerコマンドに渡したオプションを明記していくイメージです。

compose.yml
x-db-config: &db-config
  MYSQL_ROOT_PASSWORD: secret

services:
  web:
    build: .
    command: rails server -b 0.0.0.0
    ports:
      - "3000:3000"
    volumes:
      - ./:/app
    environment:
      <<: *db-config
    depends_on:
      - db
  
  db:
    image: mysql:9.0.1-oraclelinux9
    volumes:
      - db-data:/var/lib/mysql
    environment:
      <<: *db-config

volumes:
  db-data:

ここでローカルのディレクトリとコンテナ上のディレクトリをバインドマウントしているので、コンテナを作成した後ローカルでファイルを編集すれば、その変更がコンテナにも反映されます。そのためDockerfileでvimをインストールする手順は省いています。

これらのファイル(Dockerfile、compose.yml、Gemfile、Gemfile.lock)を用意してコンテナを立ち上げます。まず次のコマンドでRailsアプリに必要なファイルを作成します。

docker compose run --rm web rails new . --database=mysql --skip-docker

--skip-dockerオプションはDockerfileの上書きと.dockerignoreなどのファイルの生成をスキップします。
このコマンドを実行するとGemfileとGemfile.lockが上書きされるので、それらを元にイメージを作成します。

docker compose build

続いてconfig/database.ymlを編集し、DBに接続できるようにします。

config/database.yml
# 略
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
- password:
+ password: secret
- host: localhost
+ host: db
# 略

続いてDBを作成します。

docker compose run --rm web rails db:create

以上で完成です。次のコマンドを実行するとWebサーバーとDBサーバーが立ち上がります。

docker compose up

コンテナ上でコマンドを実行するときは、例えば次のように実行すればいけます。

docker compose exec web rails db

おわりに

ここまで読んでくださりありがとうございました。何か不備があればコメントで教えてください。

2
0
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
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?