前書き
ローカル環境にて、表題の通り環境を構築したので、その構築方法について記載します。
Rails 8
では、Thruster
と言うHTTP/2
プロキシサーバーが、構築時から導入されている為、必ずしもNginx
等のリバースプロシキを導入する必要はないです。それを、理解した上で今回は構築を行なっております。
用語説明
用語 | 説明 |
---|---|
TCP | インターネットでデータを確実に届ける為の通信プロトコル(規則)。YouTubeの動画視聴、Webページの閲覧、メールの送受信等、正確なデータ転送を行う際に使用する重要な仕組み。 |
HTTP/2 | 1つのTCP 接続を使って複数のリクエストを同時に処理が出来る為、HTTP/1.1 で発生していた「リクエストの順番待ち(ヘッドオブラインブロッキング)」の問題を解消。更に、データ圧縮 や サーバープッシュ(サーバーからクライアントへ事前にデータを送る仕組み) により、Webページの表示速度が向上する。 |
Thruster |
Rails 8 に導入された HTTP/2 対応のリバースプロキシサーバー。従来Nginx が担っていたHTTP/2 処理や負荷分散をRails 環境に最適化。静的ファイルの配信やHTTPS の終端処理 (SSL/TLS の処理) も可能。 |
プロキシサーバー | クライアント(ブラウザなど)とサーバーの間に入り、リクエストを中継するサーバー。通信の最適化やキャッシュ、セキュリティ向上を目的とする。フォワードプロキシとリバースプロキシの2種類がある。 |
リバースプロキシ | クライアントからのリクエストを受け取り、適切なバックエンドサーバー(Puma 等)へ転送するプロキシ。負荷分散 (ロードバランシング) やHTTP/2 対応、静的ファイルの配信を効率化出来る。 |
Nginx | 高速なHTTP サーバー兼リバースプロキシ。静的ファイルの配信、HTTP/2 対応、ロードバランシング、SSL/TLS 終端処理等に優れる。Rails ではPuma の前段に置いて、処理の最適化を行うのが一般的だったが、Rails 8 のThruster により不要になるケースも増える。 |
構築手順
必要ファイルの作成
- 任意のディレクトリを作成後、そのディレクトリー内で下記のコマンドを実行する
mkdir -p backend/web1/settings backend/nginx/conf backend/nginx/error_page db/conf phpmyadmin/conf && \
touch backend/web1/settings/.env && \
touch backend/web1/settings/entrypoint.sh && \
touch backend/web1/Dockerfile.dev && \
touch backend/web1/Gemfile && \
touch backend/web1/Gemfile.lock && \
touch backend/nginx/conf/web1.conf && \
touch backend/nginx/error_page/404.html && \
touch backend/nginx/error_page/422.html && \
touch backend/nginx/error_page/500.html && \
touch backend/nginx/Dockerfile && \
touch db/conf/.env && \
touch phpmyadmin/conf/.env && \
touch compose.yml && \
touch README.md && \
touch .gitignore
- 上記コマンドを実行した後のディレクトリー構成は下記の状態と
.
├── backend/
│ ├── web1/
│ │ ├── settings/
│ │ │ ├── env
│ │ │ └── entrypoint.sh
│ │ ├── Dockerfile.dev
│ │ ├── Gemfile
│ │ └── Gemfile.lock
│ └── nginx/
│ ├── conf/
│ │ └── web1.conf
│ ├── error_page/
│ │ ├── 404.html
│ │ ├── 422.html
│ │ └── 500.html
│ └── Dockerfile
├── db/
│ └── conf/
│ └── .env
├── phpmyadmin/
│ └── conf/
│ └── .env
├── compose.yml
├── .gitignore
└── README.md
.gitignore
-
.gitignore
に、git
へ含めたくないファイルの情報を記載する
.gitignore
# db関連
mysql_volume
# .env関連
db/conf/.env
phpmyadmin/conf/.env
backend/web1/settings/.env
# その他
.DS_Store
Gem
関連の設定
- Gemfileに下記を記載
source "https://rubygems.org"
# 新規で立ち上げる際は、最新バージョンにする
gem "rails", "~> 8.0.1"
-
Gemfile.lock
は、何も記載しない
# 何も記載しない
-
entrypoint.sh
に下記を記載
settings/entrypoint.sh
#!/bin/bash
set -e
# 古いPIDファイルやソケットファイルを削除
rm -rf /web1/tmp/{pids,sockets}
# 必要なディレクトリを作成
mkdir -p /web1/tmp/{pids,sockets}
# マイグレーションを実行
bundle exec rails db:migrate
exec "$@"
-
.env
下記を記載 - 下記の設定は、
DB
を接続する際に必要
settings/.env
HOST=db
DATABASE=web1_development
USER_NAME=root
PASSWORD=password
Docker関連の構築
-
Rails
を起動するDockerfile
に、下記を記載
# ベースイメージを指定
ARG RUBY_VERSION=3.4.1
FROM ruby:${RUBY_VERSION}-slim-bookworm
# 環境変数の設定
ENV LANG=C.UTF-8 \
TZ=Asia/Tokyo \
RUBY_YJIT_ENABLE=1 \
RAILS_ENV=development \
APP_ROOT_PATH=/web1
# 必要なパッケージをインストール
RUN apt-get update && apt-get install -y \
build-essential \
libgmp-dev \
libssl-dev \
libmariadb-dev-compat \
curl \
git \
&& rm -rf /var/lib/apt/lists/*
# 作業ディレクトリを設定
WORKDIR ${APP_ROOT_PATH}
# Gemfile と Gemfile.lock をコピーし、依存関係をインストール
COPY Gemfile Gemfile.lock ${APP_ROOT_PATH}/
RUN bundle install
# プロジェクトのソースコードをコピー
COPY . ${APP_ROOT_PATH}/
# エントリポイントスクリプトをコピーし、実行可能にする
COPY settings/entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
Docker composeの構築
-
Rails
の環境は、デバッグが出来る様に構築 -
phpmyadmin
は、DB
の中身を可視化する為に入れています
compose.yml
services:
nginx:
container_name: nginx
image: nginx:latest
build: ./backend/nginx
ports:
- "80:80"
volumes:
- tmp_volume:/web1/tmp
depends_on:
- web1
web1:
container_name: web1
image: web1:latest
build:
context: ./backend/web1
dockerfile: Dockerfile.dev
env_file:
- ./backend/web1/settings/.env
volumes:
- ./backend/web1:/web1
- tmp_volume:/web1/tmp
ports:
- 1111:1111
depends_on:
- db
tty: true
stdin_open: true
command: >
bash -c "
bundle install &&
rails db:migrate &&
rdbg -n --open --host 0.0.0.0 --port 1111 -c -- bundle exec pumactl start
"
db:
container_name: mysql
image: mysql:latest
env_file: ./db/conf/.env
volumes:
- ./db/mysql_volume:/var/lib/mysql
ports:
- "3306:3306"
restart: always
phpmyadmin:
container_name: phpmyadmin
image: phpmyadmin:latest
restart: always
depends_on:
- db
env_file:
- ./phpmyadmin/conf/.env
ports:
- "8080:80"
volumes:
tmp_volume:
driver: local
driver_opts:
type: tmpfs
device: tmpfs
volumes内で使用しているメソッドと説明
メソッド | 説明 |
---|---|
volumes |
Docker コンテナで使用するボリュームを定義する。ボリュームを指定する事で、コンテナが停止・削除されてもデータを保持できる。ただし、一時的なボリューム(今回の場合は、tmpfs )を指定すると、コンテナ停止時にデータが消える。 |
tmp_volume |
volumes で定義されたボリュームの名前。コンテナ内で、この名前のボリュームをマウント(接続)できる。この設定では、一時的なメモリ上のストレージとして機能する。 |
driver | ボリュームの管理方法(ドライバ)を指定する。local を指定すると、ホストマシンのストレージ(ディスクやメモリ)を利用する。特に、tmpfs と組み合わせるとメモリ上にデータを保持し、高速アクセスが可能になる。 |
driver_opts | ボリュームの詳細な設定を行う為のオプション。ここで指定した内容により、ボリュームの動作が変わる。例えば、type =tmpfs を指定すると、ボリュームがメモリ上に作成され、ディスクを使用しない一時的なストレージとして機能する。 |
type | ボリュームの種類を指定する。tmpfs を指定すると、ボリュームがRAM(メモリ)上に作成され、データの読み書き速度が高速になる。ただし、コンテナが停止するとデータは消える。 |
device | 使用するストレージのデバイスを指定する。ここでは、tmpfs を指定している為、ディスクではなくメモリをストレージとして扱う事になる。これにより、ディスクI/O を減らし、高速なデータ処理が可能になる。 |
Nginxの構築
-
Nginx
用のDockerfile
に、下記の内容を記載
FROM nginx:latest
RUN apt-get update && apt-get install -y curl
RUN rm /etc/nginx/conf.d/default.conf
COPY conf/web1.conf /etc/nginx/conf.d/web1.conf
COPY error_page /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
-
conf/web1.conf
に、下記の内容を記載
# unixドメインソケット通信でpumaとnginxを接続する
upstream web1 {
server unix:///web1/tmp/sockets/puma.sock;
}
server {
listen 80;
server_name localhost;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
proxy_pass http://web1;
proxy_intercept_errors on;
}
# エラーページの設定
error_page 404 /404.html;
error_page 422 /422.html;
error_page 500 502 503 504 /500.html;
# 静的エラーページの内部設定
location ~ ^/(404|422|500)\.html$ {
root /usr/share/nginx/html;
internal;
}
}
-
404.html
、422.html
、500.html
を作成する
404.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>404 - ページが見つかりません</title>
<style>
body {
font-family: Arial, sans-serif;
text-align: center;
padding: 50px;
}
h1 {
font-size: 50px;
}
p {
font-size: 20px;
}
a {
color: #007BFF;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<h1>404</h1>
<p>お探しのページは見つかりませんでした。</p>
</body>
</html>
422.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>422 - 処理できない要求</title>
<style>
body {
font-family: Arial, sans-serif;
text-align: center;
padding: 50px;
}
h1 {
font-size: 50px;
}
p {
font-size: 20px;
}
a {
color: #007BFF;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<h1>422</h1>
<p>リクエストを処理できませんでした。</p>
</body>
</html>
500.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>500 - サーバーエラー</title>
<style>
body {
font-family: Arial, sans-serif;
text-align: center;
padding: 50px;
}
h1 {
font-size: 50px;
}
p {
font-size: 20px;
}
a {
color: #007BFF;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<h1>500</h1>
<p>内部サーバーエラーが発生しました。</p>
</body>
</html>
db(MySQL)の設定
-
db
ディレクトリー内にある.env
ファイルへ、下記を記載する
conf/.env
MYSQL_DATABASE=web1_development
MYSQL_ROOT_PASSWORD=password
TZ=Asia/Tokyo
phpMyAdmin
-
phpmyAdmin
ディレクトリー内にある.env
ファイルへ、下記を記載する
conf/.env
PMA_ARBITRARY=1
PMA_HOSTS=db
PMA_USER=root
PMA_PASSWORD=password
起動手順
新しいRails
アプリケーションを作成する
- 下記のコマンドを
compose.yml
ファイルの階層にてコマンドを実行する -
web1
のディレクトリー内に、Rails
を立ち上げる為に必要なファイルとディレクトリーが生成される
docker compose run web1 rails new . \
--force \
--no-deps \
--database=mysql \
--css=tailwind \
--skip-action-mailbox \
--skip-action-mailer \
--skip-test
Rails
を立ち上げる為のコマンドと説明
コマンド | 説明 |
---|---|
docker compose run |
docker-compose.yml の定義に基づいて、コンテナを作成・実行する |
web1 |
docker-compose.yml で定義された web1 サービスのコンテナ内でコマンドを実行する |
rails new . | カレントディレクトリに新しいRails アプリを作成する |
--force | 既存のファイルがあっても上書きする |
--no-deps | 依存する他のコンテナ(例: db 等)を起動せずに web1 のみを実行する |
--database=mysql | データベースとしてMySQL を使用するようにRails アプリを設定する |
--css=tailwind |
CSS フレームワークとしてTailwind CSS を使用する |
--skip-action-mailbox |
Action Mailbox (メール受信機能)のファイルを生成しない |
--skip-action-mailer |
Action Mailer (メール送信機能)のファイルを生成しない |
--skip-test | デフォルトのテストファイルを生成しない |
-
Gemfile
に、環境変数を管理する為のRubyGem
を記載する - 詳細 : [GitHub] bkeepers/dotenv
gem 'dotenv-rails'
- 下記のファイルに、以下の修正を行う
-
backend/web1/settings/.env
にある.env
を読み込ませる設定を加える
backend/web1/config/application.rb
# 省略
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
if %w[development test].include?(Rails.env)
Dotenv.load(File.expand_path('../settings/.env', __dir__))
end
module Web1
# 省略
-
database.yml
を下記の様に修正
database.yml
の注意点
-
development
には、直接データーベースの設定を書いても良い -
credentials.yml.enc
をデーターベースの設定を書いて運用しても良い
backend/web1/config/database.yml
default: &default
adapter: mysql2
encoding: utf8mb4
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
development:
<<: *default
host: <%= ENV["HOST"] %>
database: <%= ENV["DATABASE"] %>
username: <%= ENV["USER_NAME"] %>
password: <%= ENV["PASSWORD"] %>
test:
<<: *default
database: web1_test
production:
primary: &primary_production
<<: *default
database: web1_production
username: web1
password: <%= ENV["WEB1_DATABASE_PASSWORD"] %>
cache:
<<: *primary_production
database: web1_production_cache
migrations_paths: db/cache_migrate
queue:
<<: *primary_production
database: web1_production_queue
migrations_paths: db/queue_migrate
cable:
<<: *primary_production
database: web1_production_cable
migrations_paths: db/cable_migrate
-
puma
の設定を修正
backend/web1/config/puma.rb
threads_count = ENV.fetch("RAILS_MAX_THREADS", 3)
threads threads_count, threads_count
port ENV.fetch("PORT", 3000)
plugin :tmp_restart
plugin :solid_queue if ENV["SOLID_QUEUE_IN_PUMA"]
pidfile ENV["PIDFILE"] if ENV["PIDFILE"]
# 下記を追加する
app_root = File.expand_path("..", __dir__)
bind "unix://#{app_root}/tmp/sockets/puma.sock"
- 下記の順番でコンテナーを立ち上げる
- 起動しているコンテナーを全て立ち下げる
- 再度、ビルドを行い、イメージを更新する
- コンテナを起動する
# コンテナーの立ち下げ
docker rm $(docker ps -aq)
# イメージをビルド
docker compose build --no-cache
# 不要なイメージを消去
docker images -f "dangling=true" -q | xargs docker rmi
# コンテナーの起動
docker compose up -d
- URLを叩くとRailsの初期画面が表示される
- http://localhost/
GitHub
おまけ
- デバッグ機能を付与する為、web1の中に
.vscode
を作成した後、launch.json
を作成する -
launch.json
に下記の内容を追記する - デバッグの設定の詳細は、VSCode + Docker + Docker-compose +Railsのデバッグの設定方法 を見て下さい
.vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"type": "rdbg",
"name": "Debug Ruby",
"request": "attach",
"debugPort": "localhost:1111",
"localfsMap": "/web1:${workspaceFolder}",
},
]
}
参考記事
- VSCode + Docker + Docker-compose +Railsのデバッグの設定方法
- 【初心者向け】Rails 7+MySQL 8.0の開発環境をDocker composeで作る方法
- サクッとDocker+Rails7.13+Mysqlの環境構築
- DockerでNginx、Puma(Rails7.0)の環境構築
- 【速攻】DockerでMySQLとphpMyAdminのコンテナ作成
- Ruby on RailsをNginxで起動
- [Railsドキュメント] 新しいRailsのアプリケーションを作成
- [Docker Hub] phpmyadmin
- 【速攻】DockerでMySQLとphpMyAdminのコンテナ作成
感想
今回、以前から挑戦したかったRailsとNginxのソケット通信をローカル環境で構築しました。特に重要だったのは、ボリュームを利用して2つのコンテナ(Rails
とNginx
)を共有させるという構成です。当初はこの仕組みが理解できず苦戦しましたが、試行錯誤を重ねる中で徐々に構造を把握し、最終的に念願だった環境を構築する事ができました。
今回の構成の様に、アプリケーションサーバーの前段で処理を行う手法には、様々な選択肢があります。引き続き理解を深め、より最適な環境構築に取り組んでいきたいと思います。