🚃

docker-compose + Rails 7 + Postgres の環境構築

2022/10/10に公開

Rails 7 と Postgres を、docker-compose を用いてセットアップする方法です。

仕事で Rails を使う機会があり、自分でも1からセットアップしようと思ったらいくつかつまずいたポイントがあったため、n番煎じですが備忘録も兼ねて文章にしました。

基本的には Docker の公式チュートリアルに沿って進めていきますが、一部バージョンが古い部分等を補っています。
https://docs.docker.jp/compose/rails.html

Rails や Postgres、 Docker 自体が何者かは分かる前提で書いていきますのでご了承ください。


今回のゴール

Docker Compose 上で以下のコンテナを動かし、 Rails アプリケーションを開発できる状態にする

  • PostgreSQL
  • Rails 7.0.4
    • ruby 3.1

Docker Desktop のインストール

まだ Docker Desktop がインストールされていない場合はインストールします。
https://docs.docker.com/get-docker/

Docker の準備

プロジェクトを配置するフォルダを準備します。ここが Rails のルートフォルダになります。

mkdir rails-proj

続いて、 Rails を実行するための Ruby コンテナを作成する Dockerfile を作成します。
今回 Ruby のバージョンは 3.1 を利用します。

Dockerfile
FROM ruby:3.1

USER root
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs
RUN mkdir /webapp
WORKDIR /webapp
ADD Gemfile /webapp/Gemfile
ADD Gemfile.lock /webapp/Gemfile.lock
RUN bundle install
ADD . /webapp

さらに、上の Dockerfile で作成する Rails アプリケーションコンテナと、 Postgres データベースコンテナを起動するための docker-compose.yml を作成します。

docker-compose.yml
version: '3'
services:
  db:
    container_name: rails_db
    image: postgres
  web:
    container_name: rails_web
    build: .
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    volumes:
      - .:/webapp
    ports:
      - 3000:3000
    depends_on:
      - db

Rails のインストール

Rails のインストールをするため Gemfile を記述します。

Gemfile
source 'https://rubygems.org'
gem 'rails', '~> 7.0.4'

また、空で良いので Gemfile.lock も必要なので作成します。

touch Gemfile.lock

この時点で、ファイルが4つあるはずです。

ls
Dockerfile  Gemfile  Gemfile.lock docker-compose.yml

ここまできたら、コンテナ内で Rails のインストールを行うことで必要なファイルを Rails が自動で作成してくれます。
以下を実行すると、Dockerfile の記述に従って bundle install が走って Rails がインストールされた後、 Rails のアプリケーション作成コマンドが実行されます。

docker compose run --rm web rails new . --force --database=postgresql

実行後、大量のフォルダ・ファイルが作成されているはずです。
ログには Could not find gem ~ などのエラーが表示されているはずですが、今のうちは問題ありません。

ls
Dockerfile  Gemfile  Gemfile.lock  README.md  Rakefile  app  bin  config  config.ru  db  docker-compose.yml  lib  log  public  storage  test  tmp  vendor

データベースの設定

config/database.yml 内の default: &default の設定を以下の通り変更します。

config.database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  # For details on connection pooling, see Rails configuration guide
  # https://guides.rubyonrails.org/configuring.html#database-pooling
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
+ host: db
+ username: postgres
+ password: admin

また、環境変数経由で Postgres のパスワードを設定するために docker-compose.yml に追記します。

docker-compose.yml
services:
  db:
    container_name: rails_db
    image: postgres
+   environment:
+     - POSTGRES_PASSWORD=admin

Rails を起動する

rails new を実行したことにより、 Gemfile が変更されているはずです。
なので、新しい gem をインストールするために、コンテナを再ビルドして起動します。

docker compose up --build

ここまでくれば、 Rails と Postgres が正常に起動するはずです。

データベースの作成

お疲れ様です!ここまできたら後もう少しです。
Postgres 内にデータベースを作成します。
rails_web コンテナ内で、rake db:create を実行することでデータベースが作成できます。

docker exec rails_web rake db:create 

以下が表示されれば成功です。

Created database 'webapp_development'
Created database 'webapp_test'

http://localhost:3000/ にアクセスしてみましょう。
Rails と Postgres の設定が正常にできていれば、下の画像のようなページが表示されます。

Yay! You're on Rails!
Yay! You’re on Rails!

お疲れ様でした。以降はローカルで開発するのと同様に Rails アプリケーションを作成できます。
Rails を実行するには docker compose up、終了するには Ctrl+C/Cmd+C を押下します。

Rails による実際のアプリケーション作成がはじめての場合は、以下のサイトが参考になるかと思います。
https://railsguides.jp/getting_started.html#railsで「hello」と表示する

おまけ

Postgres のデータがコンテナ再作成で消えないようにする

このままだと docker compose down した際に、 rails_db のコンテナが消えると同時にデータベース内のデータも消えてしまいます。

Docker の Volume 機能を活用して、データを永続化することができます。

docker-compose.yml
version: "3"
services:
  db:
    container_name: rails_db
    image: postgres
+   volumes:
+     - postgres-volume:/var/lib/postgresql/data
    environment:
      - POSTGRES_PASSWORD=admin
  web:
    container_name: rails_web
    build: .
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    volumes:
      - .:/webapp
    ports:
      - 3000:3000
    depends_on:
      - db
+volumes:
+  postgres-volume:

この設定を行うことで、データベースに書き込まれたデータが Volume に同期されるようになり、新しくコンテナを作ったときも Volume のデータをコンテナに同期してくれるようになります。
なお、変更した後はデータベースの再作成が必要です。

docker exec rails_web rake db:create

Gem がコンテナ再作成で消えないようにする

上の Postgres と同様に、コンテナ再作成ごとにインストールが走ってしまう Gem も永続化できます。

docker-compose.yml
version: "3"
services:
  db:
    container_name: rails_db
    image: postgres
    volumes:
      - postgres-volume:/var/lib/postgresql/data
    environment:
      - POSTGRES_PASSWORD=admin
  web:
    container_name: rails_web
    build: .
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    volumes:
      - .:/webapp
+     - gems-volume:/usr/local/bundle
    ports:
      - 3000:3000
    depends_on:
      - db
volumes:
  postgres-volume:
+ gems-volume:

コンテナを再ビルドすることで Dockerfile 経由で bundle install を実行します。

docker compose build

VSCode の Remote Container で開発する

VSCode の Remote Container 内で開発すると、ホスト側に Ruby や Gem をインストールせずとも rubocopsolargraph 等を利用しながら開発ができるので、幸せになれるかもしれません。

.devcontainer ディレクトリを作成し、以下の2ファイルを作成します。

.devcontainer/devcontainer.json
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.245.2/containers/docker-existing-docker-compose
// If you want to run as a non-root user in the container, see .devcontainer/docker-compose.yml.
{
  "name": "rails-webapp",

  // Update the 'dockerComposeFile' list if you have more compose files or use different names.
  // The .devcontainer/docker-compose.yml file contains any overrides you need/want to make.
  "dockerComposeFile": ["../docker-compose.yml", "./docker-compose.yml"],

  // The 'service' property is the name of the service for the container that VS Code should
  // use. Update this value and .devcontainer/docker-compose.yml to the real service name.
  "service": "web",

  // The optional 'workspaceFolder' property is the path VS Code should open by default when
  // connected. This is typically a file mount in .devcontainer/docker-compose.yml
  "workspaceFolder": "/webapp"
}

また、ホストとの同期オプションで delegated を指定するとファイル読み書きが高速化するかもしれないので指定しておきます。

.devcontainer/docker-compose.yml
version: "3"
services:
  web:
    volumes:
      - .:/webapp:delegated

:delegated:cached について詳しくは↓をご参照ください。
https://docs.docker.jp/docker-for-mac/osxfs-caching.html

VSCode でプロジェクトを開き、 Dev Containers: Reopen in Container を実行することでコンテナ内で開発できます。

参考
docker-compose.ymlで複数コマンドを実行する方法
docker-compose upしたときに「A server is already running.」って言われないようにする

Discussion