はじめに
こんにちは!MaTTaと申します。プログラミングスクールRunteq50期生です。先日、生成AIを用いた習慣化支援RPGアプリ「3日目に魔王がいる」をMVPリリースしました。その技術要素を細かく切り出して順に備忘録として残していこうと思います。
今回は最初の行程である環境構築についてです。Tailswind CSSの導入もおまけで実施します。
参考
アプリ紹介記事
サービスURL
Githubリポジトリ
本編ここから
作業環境
- MacBook Air 2020 (Apple M1)
- macOS Sonoma 14.4.1
構成
このアプリでは、バックエンドにRails(APIモード)、フロントエンドにReactを採用しており、それぞれ個別に開発環境を構築しています。つまりアプリが二つあるような状態です。
一方で、開発の管理は楽したいので、リポジトリとしては一つの統合アプリとして管理しています。
app
├backend/
│
├frontend/
│
└docker-compose.yml
# これらをまとめてリポジトリとする
バックエンド側の環境構築
Rails APIのDocker開発環境を構築します。リポジトリのルートディレクトリから操作を開始します。ファイル操作はGUIで行っても問題ありません。
touch docker-compose.yml
touch docker-compose.ymlに下記のように記述します。後にフロントエンド部分も追記するのですが、先に書くとエラーを起こすのでひとまずバックエンドのみです。なお、データベースはMySQLを用いています。
version: "3"
services:
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: password
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
back:
build:
context: ./backend
dockerfile: Dockerfile
env_file:
- ./backend/.env.local
command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -b '0.0.0.0'"
volumes:
- ./backend:/app
ports:
- "3000:3000"
depends_on:
- db
tty: true
stdin_open: true
volumes:
mysql_data:
補足
項目 | 説明 |
---|---|
db:volumes:
|
コンテナ内のデータを永続化するためのボリューム。mysql_data というボリュームが/var/lib/mysql にマウントされます。 |
build: |
アプリケーションのビルド設定を指定します。 - context: ./backend : ビルドコンテキスト(Dockerfileの場所)を指定します。 |
env_file: |
環境変数をファイルから読み込むことを指定します。ここでは、./backend/.env.local ファイルが使用されます。 |
command: |
コンテナ起動時に実行されるコマンドを指定します。ここでは、server.pid ファイルを削除し、Railsサーバーを起動します。Railsは server.pid ファイルを使用してサーバープロセスのPIDを保存します。古いPIDファイルが存在すると、サーバーの起動に問題が発生する可能性があるため、起動前にこのファイルを削除しています。 |
db:volumes:
|
./backend ディレクトリがコンテナ内の/app ディレクトリにマウントされます。 |
depends_on: |
サービス間の依存関係を定義します。back サービスはdb サービスに依存しているため、db サービスが起動してからback サービスが起動します。 |
tty: true とstdin_open: true
|
これらのオプションは、コンテナとのインタラクティブなセッションを可能にします。 |
volumes: |
最後のvolumes: セクションでは、名前付きボリューム(Docker によって管理される永続的なデータ保存領域)を定義します。ここでは、mysql_data というボリュームが定義されています。 |
バックエンドのワークスペースを作成し移動。必要な空ファイルを作成する。
mkdir backend
cd backend
touch Dockerfile Gemfile Gemfile.lock entrypoint.sh .env.local
Dockerfileに下記の通り記述します。
注意
一行目の--platform=linux/amd64
について、M1以降のARMアーキテクチャを採用しているMacの場合に必要です。それ以外の開発環境の場合は削除してください。
FROM --platform=linux/amd64 ruby:3.2.2
RUN apt-get update -qq && apt-get install -y nodejs default-mysql-client
WORKDIR /app
COPY Gemfile /app/Gemfile
COPY Gemfile.lock /app/Gemfile.lock
RUN gem install bundler
RUN bundle install
COPY . /app
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3002
CMD ["rails", "server", "-b", "0.0.0.0"]
補足
Dockerfile命令 | 説明 |
---|---|
FROM --platform=linux/amd64 ruby:3.2.2 |
このイメージは、Ruby 3.2.2を使用します。--platform=linux/amd64 は、プラットフォームを指定し、Dockerイメージが特定のアーキテクチャ(この場合はAMD64)で構築されることを保証します。 |
RUN apt-get update -qq && apt-get install -y nodejs default-mysql-client |
apt-get update を実行して、パッケージリストを更新します。apt-get install を使用して、NodeJSとMySQLクライアントをインストールします。これらのパッケージは、Railsアプリケーションの実行に必要です。 |
WORKDIR /app |
コンテナ内の作業ディレクトリを/app に設定します。 |
COPY Gemfile /app/Gemfile COPY Gemfile.lock /app/Gemfile.lock
|
Gemfile とGemfile.lock をコンテナ内の/app ディレクトリにコピーします。これらのファイルは、アプリケーションの依存関係を定義します。 |
RUN gem install bundler RUN bundle install
|
bundler をインストールします。bundle install を実行して、GemfileとGemfile.lockに基づいて依存関係をインストールします。 |
COPY . /app |
カレントディレクトリ(. )の内容全体をコンテナ内の/app ディレクトリにコピーします。これにより、アプリケーションのソースコードがコンテナ内に配置されます。 |
COPY entrypoint.sh /usr/bin/ RUN chmod +x /usr/bin/entrypoint.sh ENTRYPOINT ["entrypoint.sh"]
|
entrypoint.sh ファイルを/usr/bin/ ディレクトリにコピーします。chmod +x を使用して、entrypoint.sh ファイルに実行権限を付与します。ENTRYPOINT 命令を使用して、コンテナの起動時にentrypoint.sh スクリプトが実行されるように設定します。 |
CMD ["rails", "server", "-b", "0.0.0.0"] |
CMD 命令を使用して、コンテナの起動時に実行されるデフォルトのコマンドを指定します。この場合、rails server -b 0.0.0.0 が実行され、Railsサーバーがすべてのネットワークインターフェイス(0.0.0.0 )で起動します。 |
続いてentrypoint.shに下記のように記述します。entrypoint.sh
は、Dockerコンテナが起動する際に実行されるシェルスクリプトです。このスクリプトは、コンテナの起動プロセスをカスタマイズするために使用されます。
#!/bin/bash
set -e
rm -f /app/tmp/pids/server.pid
exec "$@"
補足
スクリプトの行 | 説明 |
---|---|
#!/bin/bash |
スクリプトがBashシェルを使用して実行されることを指定します。 |
set -e |
スクリプト内のコマンドがエラーを返した場合、スクリプトの実行を即座に終了するように指示します。 |
rm -f /app/tmp/pids/server.pid |
古いRailsサーバーのPIDファイルを削除します。 |
exec "$@" |
スクリプトに渡された引数を使用して新しいプロセスを実行し、現在のシェルプロセスを置き換えます。これにより、コンテナのメインプロセスがスクリプトから実行されたプロセスになります。 |
さらにGemfile
にRailsのインストールについて記述します。
source 'https://rubygems.org'
gem 'rails', '~> 7.1.2'
Gemfile.lock
, .env.local
ファイルは空のままで構いません。
Railsアプリを作成する
ターミナルで下記の通り実行します。
docker-compose run back rails new . --force --database=mysql --skip-docker --api
補足
- --skip-docker: rails new時にDockerfileが自動作成されるのですが今回は不要なのでskip - --api : RailsをAPIモードで立ち上げますRailsアプリのファイルがインストールされたらconfig/database.yml
を一部修正します
default: &default
adapter: mysql2
encoding: utf8mb4
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: root
# 下記2 行を修正
password: password
host: db
準備が整ったのでコンテナを立ち上げます。
docker compose build
docker compose up
データベースの作成
docker compose exec back bash
rails db:create
アプリが起動できたらhttp://localhost:3000/
にアクセスして確認します
.gitフォルダの削除
rails new
時に.gitフォルダ
がbackend内に自動作成されるのですが、親ディレクトリでバック・フロント一括git管理しようとしている今回のような場合、backendにも.gitがあるとサブモジュールという扱いになり扱いが面倒になります。backendフォルダ内の.gitフォルダを削除しましょう。
rm -rf backend/.git
その他重要なファイルの格納
.env.local
と.gitignore
はbackendフォルダ内に移す(ない場合は作成)。
.gitignore
には.env.local
の記述を忘れないようにする。
フロントエンド側の環境構築
ReactのDocker開発環境を構築します。create-react-app
でfrontendディレクトリとReact初期ファイル類を揃えることから開始します。
# コンテナのルートで操作します。 backendにいるときは cd ../
npx create-react-app frontend
補足
ここで、npxは、npm(Node Package Manager)によって提供されるコマンドランナーで、ローカルもしくはリモートのnpmパッケージを実行するために使用されます。create-react-appは、Reactアプリケーションのプロジェクトを素早くセットアップするためのツールです。
次に、ワークスペースに移動して必要な空ファイルを作成します。
cd frontend
touch Dockerfile yarn.lock
frontend/Dockerfileに下記のように記述します。
FROM --platform=linux/amd64 node:21.5.0
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn install
COPY . .
RUN yarn build
RUN yarn global add serve
EXPOSE 3000
CMD sh -c 'serve -s build -l tcp://0.0.0.0:$PORT'
それぞれの意味
コマンド | 説明 |
---|---|
FROM --platform=linux/amd64 node:21.5.0 |
ベースイメージとしてNode.jsのバージョン21.5.0を使用し、amd64プラットフォームで動作するように指定します。 |
WORKDIR /app |
コンテナ内の作業ディレクトリを/app に設定します。 |
COPY package.json yarn.lock ./ |
package.json とyarn.lock をコンテナの作業ディレクトリにコピーします。 |
RUN yarn install |
必要なパッケージをインストールします。 |
COPY . . |
現在のディレクトリのすべてのファイルをコンテナの作業ディレクトリにコピーします。 |
RUN yarn build |
アプリケーションをビルドします。 |
RUN yarn global add serve |
serve パッケージをグローバルにインストールします。 |
EXPOSE 3000 |
コンテナがリッスンするポート3000を公開します。 |
CMD sh -c 'serve -s build -l tcp://0.0.0.0:$PORT' |
serve コマンドを使用してビルドされたアプリケーションを起動し、指定されたポートでリッスンします。 |
ルートディレクトリのdocker-compose.yml
にフロントエンド分を追記します
version: "3"
services:
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: password
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
back:
build:
context: ./backend
dockerfile: Dockerfile
env_file:
- ./backend/.env.local
command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -b '0.0.0.0'"
volumes:
- ./backend:/app
ports:
- "3000:3000"
depends_on:
- db
tty: true
stdin_open: true
# ここから追記
front:
build:
context: ./frontend
dockerfile: Dockerfile
volumes:
- ./frontend:/app
command: yarn start
ports:
- "8000:3000"
# ここまで追記
volumes:
mysql_data:
コンテナを立ち上げます
docker-compose up --build
アプリが起動できたらlocalhost:8000にアクセスして動作確認します。
その他重要なファイルの格納
こちらも.env.local
と.gitignore
はfrontendフォルダ内に移す(ない場合は作成)。
.gitignore
には.env.local
の記述を忘れないようにしましょう。
CORS対応
Rails APIとReactの組み合わせだとCORSによるエラーが発生するのであらかじめその対応をとっておきます。
CORSとは?
CORS(Cross-Origin Resource Sharing)は、ウェブブラウザが異なるオリジン間でリソースを共有する際のセキュリティメカニズムです。デフォルトでは、ブラウザはセキュリティ上の理由から、スクリプトが異なるオリジンのリソースにアクセスすることを禁止しています。CORSは、サーバーがブラウザに対して特定のオリジンからのリクエストを許可するための仕組みです。Rails APIとReactのフロントエンドが異なるオリジンにホストされている場合、CORSの設定が必要です。例えば、Rails APIがhttp://api.example.comにあり、Reactアプリがhttp://frontend.example.comにある場合、ReactアプリからRails APIにリクエストを送ると、CORSエラーが発生する可能性があります。
Gemfile
に下記記述します。
gem "rack-cors"
# docker compose exec back bash
bunsle install
Railsのconfig/initializers/cors.rb
に下記のとおり記述することでリクエスト元を明示的に許可するようにします。
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins "localhost:8000", "https://www.本番環境のURLがあるならここにも追加"
resource "*",
headers: :any,
methods: [:get, :post, :put, :patch, :delete, :options, :head]
end
end
(おまけ)Tailswind CSSとDaisyUIを使えるようにする
React側でデザインのハンドリング性を高めるためにTailwindとDaisyUIは自分的には欠かせない存在なので、環境構築の段階でインストールしてしまいます。
公式
フロントのコンテナに入りインストールします。
docker compose exec front bash
yarn add tailwindcss@^3
yarn add postcss autoprefixer -D
npx tailwindcss init
yarn add -D daisyui@latest
frontend/src/index.css
にTailwindを記述します。先頭に追記する形で構いません。
@tailwind base;
@tailwind components;
@tailwind utilities;
tailwind.config.js
ファイルを開き、content セクションを編集して、Tailwindがスタイルを適用するファイルを指定します。また、DaisyUIプラグインを有効にします。
module.exports = {
content: [
"./src/**/*.{js,jsx,ts,tsx}", // すべてのJS、JSX、TS、TSXファイルをスキャン
],
future: {
},
purge: ["./src/**/*.{js,jsx,ts,tsx}", "./public/index.html"],
theme: {
extend: {},
},
variants: {},
plugins: [require("daisyui")],
daisyui: {
themes: ["light", "dark", "cupcake", "aqua", "pastel"],
},
};
最後のほうにある下記はDaisyUIのテンプレートテーマです。任意ですが使えるようにしておくと非常に便利です。
daisyui: {
themes: ["light", "dark", "cupcake", "aqua", "pastel"],
},
DaisyUIのテーマ
frontend
直下にpostcss.config.js
を作成し、以下の内容を追加します。
module.exports = {
plugins: [require("tailwindcss"), require("autoprefixer")],
};
PostCSS
CSSを解析し、プラグインを通じて変換するツールです。多くの機能を持つプラグインを利用することで、CSSの構文チェックや最適化、新しいCSS仕様のサポートなどが行えます。pluginsプロパティ
pluginsプロパティは、PostCSSが使用するプラグインを指定します。この例では、tailwindcssとautoprefixerの2つのプラグインを使用しています。
require("tailwindcss")
Tailwind CSSは、ユーティリティファーストのCSSフレームワークです。これにより、簡単にレスポンシブデザインやカスタマイズが可能なCSSクラスを使うことができます。require("tailwindcss")は、Tailwind CSSのプラグインを読み込み、PostCSSで使用できるようにします。
require("autoprefixer")
Autoprefixerは、ベンダープレフィックスを自動的に追加するPostCSSのプラグインです。これにより、異なるブラウザ間でのCSSの互換性を確保できます。require("autoprefixer")は、Autoprefixerのプラグインを読み込み、PostCSSで使用できるようにします。
dockerを再ビルドすればTailwind CSS, DaisyUIが使えるようになります。
docker compose build
docker compose up
環境構築は以上です。
締めくくり
Rails APIとReactで構成するDocker環境構築の例でした。個人的にはこの構成で2,3個アプリを作っているので安定感はあるのですが、もっとこうした方がいいといったことがあればぜひコメントでご指摘ください。
今回はここまでです。引き続き、MVPアプリの技術要素を切り出してアウトプットしていきたいと思います。
この記事を書いた人
友達募集中ですw