Docker で Rails を動かす

先日書いた、Ruby3 を試そうでは、Docker で Ruby3 を動作させました。
Rails を動かすにあたっては、もう一工夫必要だったのでメモです。

参考

作るもの

  • Docker で Rails を動作する環境構築
  • Webpacker は使用しません

環境構築 1 問題あり

Dockerfile

Dockerfile は、Ruby3 を試そうで使用したものに加え、3000 番ポートを解放する設定を追加します。

1
2
3
4
5
6
FROM ruby:3.0.0

WORKDIR /usr/src/app

# 3000番ポートを解放
EXPOSE 3000

操作

先に示した Dockerfile を用意し、以下の操作で起動します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 任意ディレクトリで
docker build -t ruby3 .
docker run -it --name c_ruby3 -p 3000:3000 -v "$($(pwd).Path):/usr/src/app" ruby3 bash

# 以下コンテナ内操作
bundle init

# Gemfileの中の gem 'rails' のコメントを削除

bundle install --path=vendor/bundle
bundle exec rails new .
# <= Y

# Gemfileの中gem 'webpacker'~~ をコメントアウト

# コンテナ外からアクセスするために、-b 0.0.0.0 を付与する
bundle exec rails s -b 0.0.0.0

こちらの操作で、Rails が起動できます。
http://localhost:3000にアクセスすると、いつもの楽しい Rails の初期画面が表示されます。

が、遅いです。それはもう、ものすごく。

起動するまでに、30 秒くらいかかります。
この状態で開発を続けるのは、とてもストレスフル。

他にも行っていないこととして、データベースが SQLite のままになっているなどの問題があるので、次の章で修正します。

環境構築 2 問題を解決

Docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
version: "3"
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "127.0.0.1:3000:3000"
volumes:
# :cached を付与して、高速化
- .:/usr/src/app:cached
# vendor以下などホストと連携しなくてもよいものを、上書きして永続化の対象外にする。
# exclude volumes
- /usr/src/app/vendor
- /usr/src/app/tmp
- /usr/src/app/log
- /usr/src/app/.git
tty: true

volumes:
bundle:
driver: local

操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
docker-compose build
docker-compose up -d
docker-compose exec app bash

# 以下コンテナ内操作
bundle init

# Gemfileの中の gem 'rails' のコメントを削除

bundle install --path=vendor/bundle
bundle exec rails new .
# <= Y

# Gemfileの中gem 'webpacker'~~ をコメントアウト

# コンテナ外からアクセスするために、-b 0.0.0.0 を付与する
bundle exec rails s -b 0.0.0.0

こちらの操作で、Rails が 「高速に」 起動できます。
http://localhost:3000にアクセスすると、いつもの楽しい Rails の初期画面が表示されます。

データベースを MYSQL に変更

MYSQL もコンテナで用意する例も見かけますが、rbenv の代わりに ruby のバージョン管理代わりに Docker を使う目的なので、
MYSQL 自体はホストマシンで立ち上げます。

Gemfile に以下を追記して、bundle installします。

1
gem 'mysql2'

config/database.yml を以下のように書き換えます。
ポイントは、ホスト指定のhost.docker.internalです。この指定で、ホストマシンのローカルを指定できます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
default: &default
adapter: mysql2
encoding: utf8
host: host.docker.internal
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
timeout: 5000

devuser: &devuser
username: [任意]
password:

development:
<<: *default
database: [任意]/DB_developmnt
<<: *devuser

test:
<<: *default
database: [任意]/DB_test
<<: *devuser

production:
<<: *default
database: [任意]/DB_production
username:
password:

config/database.yml を編集し、以下コマンドでホストマシンの MYSQL にデータベースの作成ができるようになります。

1
bundle exec rails db:create

ファイルの変更が反映されない

普段なら変更した内容を即時反映するはずが、そうならないということが起きたので、調査。
以下の内容を書き換えます。

config/environments/development.rb
1
2
3
4
5
# もともとはこっち
config.file_watcher = ActiveSupport::EventedFileUpdateChecker

# こっちに書き換える
config.file_watcher = ActiveSupport::FileUpdateChecker

ファイルの更新イベントを検知する仕組みがデフォルト設定ですが、どうも Docker でマウントしたドライブ内は検知できないようです。
書き換えると、非 Docker での起動と同様に、即時反映するようになりました。

ページを増やすとエラーになる

API サーバーとして使っていれば問題ないのですが、view を使うと以下のようなエラーを起こします。

1
2
3
4
5
6
7
8
9
10
ActionView::Template::Error (undefined method `javascript_pack_tag' for #<ActionView::Base:0x00000000007ee0>
Did you mean? javascript_path
javascript_tag):
7: <%= csp_meta_tag %>
8:
9: <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
10: <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
11: </head>
12:
13: <body>

webpacker を使っていないので当然です。
<%= javascript_pack_tag ~~~は削除しておきます。
(webpacker も動くコンテナの作成はまた別途考えたいところです。)

asset pipeline で Javascript を使う

Webpacker を今回は使用しないので asset pipeline で javascript を設定を追加します。

app/assets/config/manifest.js を編集

以下の記述を app/assets/config/manifest.js に追記します。

1
//= link_directory ../javascripts .js

JavaScript ファイル本体の設置

app/assets/javascripts ディレクトリを作成し、続けて app/assets/javascripts/application.js を作成します。

app/assets/javascripts/application.js
1
//= require_tree

順番に依存するものがあれば、//= require_treeよりも前に、//= require hogehogeのように記述します。

テンプレートでの読み込み

app/views/layouts/application.html.erb に<%= javascript_pack_tag ~~~を削除していました。
代わりとして<%= javascript_include_tag ~~~を記述します。

1
2
3
4
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>

<!--以下の記述を追加-->
<%= javascript_include_tag "application" %>

以上で、app/assets/javascripts 以下のファイルを読んでくれます。


ではでは。