Docker Compose / Rails 8 / Tailwind / SQLite で開発環境の構築をしたメモ
Rails8のミニマムなセットアップ手順をまとめてみた。SQLiteがフィーチャーされているので使ってみたく。
1. フォルダのセットアップ
まずはプロジェクト用のディレクトリを作って、その中に4つの空ファイルを作る。
mkdir my-project
cd my-project
touch Dockerfile.dev
touch docker-compose.yml
touch Gemfile
touch Gemfile.lock
2. プロジェクトの定義
アプリのビルドに必要なファイルをセットアップする。アプリは依存関係を含むDockerコンテナ内で実行される。定義は Dockerfile.dev
で行う。内容は以下の通り。
# syntax = docker/dockerfile:1
# RUBY_VERSIONが.ruby-versionとGemfileのバージョンと一致していることを確認
ARG RUBY_VERSION=3.3.5
FROM registry.docker.com/library/ruby:$RUBY_VERSION-slim-bookworm
# Railsアプリケーションはここに配置
WORKDIR /rails
# gemのビルドに必要なパッケージをインストール
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y build-essential git curl sqlite3
# bundle installを実行
COPY Gemfile Gemfile.lock ./
RUN bundle install
# アプリケーションコードをコピー
COPY . .
# デフォルトでサーバーを起動、実行時に上書き可能
EXPOSE 3000
CMD ["rails", "server", "-b", "0.0.0.0"]
- このファイルの拡張子は必ず
.dev
にしておく。そうしておかないとrails new
を実行したときにDockerfileが上書きされてしまう。 -
# syntax = docker/dockerfile:1
Dockerfileの構文バージョンを指定するパーサーディレクティブ。この行が無いと、従来のDockerビルドエンジンが使用され、新しい機能や最適化が利用できない可能性がある。
次に、Railsだけを読み込む為のGemfileを作成する。これはすぐに rails new
によって上書きされる。この時点では、どのRailsのバージョンでも自由に使うことができる。
source 'https://rubygems.org'
gem 'rails', '~> 8.0'
最後に docker-compose.yml
。このファイルには、アプリを構成するサービスと、アプリの実行に必要な設定が記述されている。
services:
web:
build:
context: .
dockerfile: Dockerfile.dev
command: bash -c "./bin/dev"
tty: true
volumes:
- .:/rails
- /rails/tmp
- /rails/log
- bundle:/usr/local/bundle
ports:
- "3000:3000"
environment:
- RAILS_ENV=development
volumes:
bundle:
- Bundlerでインストールされたgemを永続化保存している。
- DBを永続化していないのでは?と思うが今回はSQLiteの利用を想定している。RailsのSQLiteファイル(たとえば
db/development.sqlite3
)はプロジェクトディレクトリ内に作られる。よってカレントディレクトリ全体を/rails
にバインドマウントしているのでホスト側にファイルが保存される。よって再起動してもDBは消えない。 - css/jsファイルの変更を監視するために
tty:true
としている。疑似ターミナルが割り当てられることで、監視ツールがインタラクティブなセッションであると認識。ファイル変更をリアルタイムで検知できるようになる。今回はNode.jsを使わないので必要ないかも。
3. プロジェクトをビルドする
docker compose run
を使ってRailsスケルトンアプリを作成する。
docker compose run --no-deps web rails new . --name=my-project --force --css=tailwind
- Dockerfile.devファイルを使用してWebサービスのイメージを構築する。
- no-depsはComposeにリンクされたサービスを起動しないように指示する。
- 新しいコンテナ内で
rails new
を実行している。 - 完了すると、フォルダ内に新しいファイル群が生成されているはず。
rails new
することによってGemfileが新しくなった。もう一度イメージをビルドする必要がある。再構築が必要になるのは、この作業とGemfileやDockerfileが変更されたときのみ。
docker compose build
4. プロジェクトの実行
アプリを起動する前に、0.0.0.0
をサーバーにバインドしておく。Procfile.devを以下のように修正する。この記述が無いとブラウザから画面を見ることができない。
web: bin/rails server -b 0.0.0.0
css: bin/rails tailwindcss:watch
続いて Dockerfile.dev
を少し修正する。
ENTRYPOINT ["/rails/bin/docker-entrypoint"]
この記述を COPY . .
の後ろに追加する。
# syntax = docker/dockerfile:1
# RUBY_VERSIONが.ruby-versionとGemfileのバージョンと一致していることを確認
ARG RUBY_VERSION=3.3.5
FROM registry.docker.com/library/ruby:$RUBY_VERSION-slim-bookworm
# Railsアプリケーションはここに配置
WORKDIR /rails
# gemのビルドに必要なパッケージをインストール
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y build-essential git curl sqlite3
# bundle installを実行
COPY Gemfile Gemfile.lock ./
RUN bundle install
# アプリケーションコードをコピー
COPY . .
ENTRYPOINT ["/rails/bin/docker-entrypoint"]
# デフォルトでサーバーを起動、実行時に上書き可能
EXPOSE 3000
CMD ["rails", "server", "-b", "0.0.0.0"]
-
ENTRYPOINT ["/rails/bin/docker-entrypoint"]
これはコンテナが起動する際にまず実行される初期化スクリプトを指定している。データベースの準備などを行っている。
ここで再度 docker compose build
を実行し、その後に docker compose up
を実行する。
5. Railsウェルカムページの表示
http://localhost:3000
で上記の画面が表示されているはず。
6. アプリケーションの停止
プロジェクトディレクトリ内で docker compose down
を実行する。
docker compose down
✔ Container my-project-web-1 Removed0.0s
✔ Network my-project_default Removed
7. アプリケーションの再起動
再び docker compose up
を実行する。
8. アプリケーションの再構築
GemfileやComposeファイルに変更を加えた場合は再構築が必要となる。一部の変更は docker compose up --build
だけで済むが、完全な再構築を行うには、Gemfile.lock の変更をホストに同期するために docker compose run web bundle install
を再実行し、その後に docker compose up --build
を実行する必要がある。
- Composeファイルのポート番号を変更した程度の修正なら部分的な再構築(
docker compose up --build
)でOK。 - gemを追加したときなどはフル再構築(
bundle install
)が必要。
といった具合。
番外. Cursor(VSCode)で開発する
docker composeでアプリケーションを起動しておく。わたしは docker compose up
コマンドより以下のコマンドを組み合わせて利用するケースが多い。理由は誰か教えて欲しい。なんとなくセーフティだから使っている。
docker compose up -d
docker compose logs web -f
Cursorにエクステンション Remote Development をインストールする。
エディタ左端のこのアイコンをクリックし…
このメニューを選択する。
入りたいコンテナを選択する。
すると、新しいCursor画面が立ち上がってDockerコンテナ内に入って作業することができるのだ。
便利。Cursorが便利なので何でもCursorで開発したい。
追記
docker-compose.ymlの tty:true
をナシにすると上手く実行できなかった。以下エラーログ。
web-1 | 14:00:32 web.1 | started with pid 11
web-1 | 14:00:32 css.1 | started with pid 12
web-1 | 14:00:33 web.1 | => Booting Puma
web-1 | 14:00:33 web.1 | => Rails 8.0.1 application starting in development
web-1 | 14:00:33 web.1 | => Run `bin/rails server --help` for more startup options
web-1 | 14:00:33 web.1 | Puma starting in single mode...
web-1 | 14:00:33 web.1 | * Puma version: 6.6.0 ("Return to Forever")
web-1 | 14:00:33 web.1 | * Ruby version: ruby 3.3.5 (2024-09-03 revision ef084cc8f4) +YJIT [aarch64-linux]
web-1 | 14:00:33 web.1 | * Min threads: 3
web-1 | 14:00:33 web.1 | * Max threads: 3
web-1 | 14:00:33 web.1 | * Environment: development
web-1 | 14:00:33 web.1 | * PID: 11
web-1 | 14:00:33 web.1 | * Listening on http://0.0.0.0:3000
web-1 | 14:00:33 web.1 | Use Ctrl-C to stop
web-1 | 14:00:33 css.1 | sh: 1: watchman: not found
web-1 | 14:00:33 css.1 | ≈ tailwindcss v4.0.6
web-1 | 14:00:33 css.1 |
web-1 | 14:00:33 css.1 | Done in 130ms
web-1 | 14:00:34 css.1 | exited with code 0
web-1 | 14:00:34 system | sending SIGTERM to all processes
web-1 | 14:00:34 | exited with code 127
web-1 | 14:00:34 web.1 | - Gracefully stopping, waiting for requests to finish
web-1 | 14:00:34 web.1 | Exiting
web-1 | 14:00:34 web.1 | terminated by SIGTERM
やっぱり必要みたい。
Discussion