Dockerを使ってRailsのAPI環境を構築する機会があり、環境構築でわからない事の調べ直しに時間が掛かっていたので、備忘録として記事にまとめた。
下記の開発環境で実施しました。
開発環境
- Edition: Windows 11 Home, Version: 22H2, OSビルド: 22621.1702
- WSL2
- Docker Desktop for Windows: 4.20.1 (110738)
- Docker Engine: 24.0.2
- Docker Compose: v2.18.1
- Ruby: 3.2.2
- Rails: 7.0.5
- MySQL: 8.0.33
次に、ディレクトリは下記の構成にしました。
ディレクトリ名は適宜変えて下さい。
ディレクトリ構成
real_world
├realworld
│├Gemfile
│├Gemfile.lock
│├entrypoint.sh
│└Dockerfile
└docker-compose.yml
各種設定ファイルを作成、記述していきます。
まずは、必要なディレクト・ファイルを作成します。
mkdir real_world
cd real_world
mkdir realworld
touch docker-compose.yml
touch ./realworld/Gemfile
touch ./realworld/Gemfile.lock
touch ./realworld/entrypoint.sh
touch ./realworld/Dockerfile
次に、各ファイルを記述していきます。
Gemfile
source 'https://rubygems.org'
gem 'rails', '~>7.0.5'
Railsは最新の7.0.5を使用しました。
Railsのバージョンは下記サイトから確認して下さい。
Gemfile.lock
Gemfile.lockファイルはビルド後に、バージョン等の情報が記載されるので、現状は空ファイルのままで大丈夫です。
entrypoint.sh
#!/bin/bash
set -e
rm -f /api/tmp/pids/server.pid
exec "$@"
Railsにはサーバー内にserver.pidというファイルが先に存在していたときに、サーバーが再起動できなくなる問題があります。それを回避するためのスクリプトを作成します。
Dockerfile
FROM ruby:latest
ARG RUBYGEMS_VERSION=3.4.6
RUN mkdir /api
WORKDIR /api
COPY Gemfile /api/Gemfile
COPY Gemfile.lock /api/Gemfile.lock
RUN gem update --system ${RUBYGEMS_VERSION} && \
bundle install
COPY . /api
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
CMD ["rails", "server", "-b", "0.0.0.0"]
おおまかに内容を説明すると、イメージを指定した後にコンテナ内にapiというディレクトリを作成し、作業ディレクトリを指定しています。
次に、ローカルのGemfileとGemfile.lockをコンテナ内にコピーし、その後に、Gemをインストールします。
最後に、コンテナ起動時に実行するスクリプトをコピーし、実行権限を与えて、エンドポイントを設定。コンテナ起動時にRailsサーバが起動するようにしてあります。
docker-compose.yml
version: '3'
services:
api:
build: ./realworld/
command: /bin/sh -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
volumes:
- ./realworld:/api
ports:
- 3000:3000
depends_on:
- db
tty: true
stdin_open: true
db:
image: mysql:latest
command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci
volumes:
- db-volume:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: password
TZ: "Asia/Tokyo"
ports:
- "3306:3306"
volumes:
db-volume:
おおまかに内容を説明すると、apiというサービス名を指定して、realworld配下のDockerfileを基にイメージをビルドしています。
volumesではローカルのrealworldディレクトリをコンテナ内のapiディレクトリにマウントしています。
depends_onでは、サービスの依存関係を指定しています。今回のように記述した場合、起動時には、db→apiの順に起動します。また、停止時には、api→dbの順に停止します。
また、dbサービスには最新のMySQLイメージを指定しています。
文字化け防止のために、utf8を指定しました。
volumesを記述するとコンテナを作り直したとしてもPCにデータを保存する領域が作成されるので今回はdb-volumesをボリュームしました。
docker compose run & build
docker compose run api rails new . --force --database=mysql --api
docker compose build
apiモードでRailsプロジェクトを作成します。
docker compose runでは引数に指定したサービスのコンテナ内でコマンドを実行します。
api:docker-compose.ymlのservices直下のサービス名
--force:上書きオプション
--database=mysql:使用するデータベース
--api:APIモードの指定
Railsプロジェクトが作成できたら、イメージをビルドします。
データベース設定
default: &default
adapter: mysql2
encoding: utf8mb4
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: root
password: password
host: db
./realworld/config/database.ymlを編集します。
デフォルトではpasswordが空白、hostがlocalhostとなっているのでdocker上で設定した値に修正します。
データベースの作成
docker compose up -d
docker compose exec api rails db:create
docker compose up -dでコンテナをバックグランドで実行。
docker compose exec api rails db:createでデータベースを作成しています。
コンテナ起動確認とRailsロゴの表示確認
docker-compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS
PORTS
real_world-api-1 real_world-api "entrypoint.sh /bin/…" api 34 hours ago Up 10 minutes 0.0.0.0:3000->3000/tcp
real_world-db-1 mysql:latest "docker-entrypoint.s…" db 34 hours ago Up 10 minutes 0.0.0.0:3306->3306/tcp, 33060/tcp
docker-compose psコマンドでコンテナ情報を確認。
apiとdbが起動(up,running)している事が確認出来たら、下記のアドレスにアクセスしてみましょう。
http://localhost:3000
Railsのロゴが表示されたら成功です。
CORS設定
忘れないうちにCORS設定をしておきます。
Gemfileに記載されている下記の部分のコメントアウトを解除します。
gem "rack-cors"
コメントアウトして、gemが追加されたのでbundle installします。
docker compose exec api bundle install
config/initializers/cors.rbのRails.application.config.middleware.insert_before以下をコメントアウトします。
originsの部分はlocalhost:3000に置き換えて下さい。
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins "localhost:3000"
resource "*",
headers: :any,
methods: [:get, :post, :put, :patch, :delete, :options, :head]
end
end
下記の記事を参考にさせて頂きました。