🦔
rails7をdockerで起動させるテンプレート作成してみた
今回作るものRails7をdockerで環境構築してみた
今回は環境に依存しないようにするためにcloud9で作ってみました。
PC環境でも動きます!
rails 7系
ruby 3.1.1
mysql 8.0.29
githubリンク
rails_app/Dockerfile
# dockerhubからイメージ取得
FROM ruby:3.1.1
# railsに必要な依存ライブラリー追加(build-essential libpq-dev nodejs)
# 参考: https://docs.docker.jp/compose/rails.html
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs
# APP_PATHという変数をセット
ENV APP_PATH /myapp
# /myappというディレクトリー作成
RUN mkdir $APP_PATH
# /myappというディレクトリー移動
WORKDIR $APP_PATH
# ローカルからGemfileをコンテナの/myappのGemfileにコピー
COPY Gemfile $APP_PATH/Gemfile
# ローカルからGemfile.lockをコンテナの/myappのGemfile.lockにコピー
COPY Gemfile.lock $APP_PATH/Gemfile.lock
# Gemfile, Gemfile/lockを元にgemをインストール
RUN bundle install
# ローカルの全てのrailsファイルをコンテナ側にコピー
COPY . $APP_PATH
EXPOSE 3000
USER root
rails_app/docker-compose.yml
version: "3"
services:
db:
# dockerhubからmysqlイメージ取得
image: mysql:8.0.29
# 起動時に常にスタートするように設定
restart: always
# 環境変数にMYSQL_ROOT_PASSWORDをセットしrootユーザーのパスワードを指定してDBコンテナを起動させる
environment:
MYSQL_ROOT_PASSWORD: password
# mysql 8.0から認証方法が変わったので以前の認証方法で起動させるようにし、railsのエラーを防ぐ
command: --default-authentication-plugin=mysql_native_password
# mysql-dataとコンテナの/var/lib/mysqlをリンクさせ、データの永続化する
volumes:
- mysql-data:/var/lib/mysql
# ローカルの3306ポートとコンテナの3306ポートを結びつける
ports:
- 3306:3306
# 使用する環境を指定
platform: linux/x86_64/v8
rails:
build:
# Dockerfileのある位置を指定
context: .
dockerfile: Dockerfile
# 起動時に行うコマンドを指定
command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
# ローカルの全てのファイルとコンテナのmyappを紐付けることで、vscodeでの編集内容を常時コンテナに転送している
volumes:
- .:/myapp
# 環境変数を指定する。ほぼDBへの接続設定時に使用される
environment:
TZ: Asia/Tokyo
RAILS_ENV: development
DB_HOST: db
DB_USER: root
MYSQL_ROOT_PASSWORD: password
# ローカルの80ポートとコンテナの3000ポートを紐付けブラウザーの80ポートで接続できるようにしている
ports:
- "80:3000"
# 正常終了してしまうとコンテナに入れないので, 正常終了しないようにしている
tty:
true
depends_on:
- db
# host(pc)に保存領域作成
volumes:
mysql-data:
config/database.ymlの下記にように変更
default: &default
adapter: mysql2
encoding: utf8mb4
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: <%= ENV['DB_USER'] %>
password: <%= ENV['MYSQL_ROOT_PASSWORD'] %>
host: <%= ENV['DB_HOST'] %>
cloud9の場合はホストブロックがかかるから
config/environment/development.rbに下記を追加してね!
config.hosts.clear
rails-nginx構成で作成するには
こちらのリポジトリーを参考にしてください
こちらのリポジリーではnginxコンテナを追加し、railsとの連携をしています。
DB初期化のためのshell script作成
entrypoint.sh
#!/bin/bash
set -e
bundle exec rails db:create
bundle exec rails db:migrate
bundle exec rails db:seed
exec "$@"
rails Dockerfile修正
FROM ruby:3.1.1
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs
ENV APP_PATH /myapp
RUN mkdir $APP_PATH
WORKDIR $APP_PATH
COPY Gemfile $APP_PATH/Gemfile
COPY Gemfile.lock $APP_PATH/Gemfile.lock
RUN bundle install
COPY . $APP_PATH
EXPOSE 3000
USER root
# ここから追加
VOLUME /myapp/public <= nginxとファイル共有用のvolume作成
VOLUME /myapp/tmp <= nginxとファイル共有用のvolume作成
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
CMD /bin/bash -c "rm -f tmp/pids/server.pid && bundle exec puma -C config/puma.rb"
nginxコンテナ
containers/nginx/Dockerfile
FROM nginx:1.15.8
# インクルード用のディレクトリ内を削除
RUN rm -f /etc/nginx/conf.d/*
# Nginxの設定ファイルをコンテナにコピー
ADD nginx.conf /etc/nginx/conf.d/webapp.conf
# ビルド完了後にNginxを起動
CMD /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf
docker-compose.ymlにnginxコンテナを追加
version: "3"
services:
db:
image: mysql:8.0.29
restart: always
environment:
MYSQL_ROOT_PASSWORD: password
command: --default-authentication-plugin=mysql_native_password
volumes:
- mysql-data:/var/lib/mysql
ports:
- 3306:3306
platform: linux/x86_64/v8
rails:
build:
context: .
dockerfile: Dockerfile
volumes:
- .:/myapp
- public-data:/myapp/public <= nginxにデータを送るためにhost volumeにファイル共有することで共有
- tmp-data:/myapp/tmp <= railsのソケットを共有するために/myapp/tmpもhost volumeにファイル共有
environment:
TZ: Asia/Tokyo
RAILS_ENV: development
DB_HOST: db
DB_USER: root
MYSQL_ROOT_PASSWORD: password
ports:
- "3000:3000"
tty:
true
depends_on:
- db
web: <= ここ追加
build:
context: containers/nginx
volumes:
- public-data:/myapp/public <= myapp/publicにrails コンテナのmyapp/publicを共有
- tmp-data:/myapp/tmp <= myapp/tmpにrails コンテナのmyapp/tmpを共有。ソケット共有のため
ports:
- 80:80
depends_on:
- rails
volumes:
mysql-data:
public-data: <= railsとnginxコンテナの情報を共有するために作成
tmp-data: <= railsのソケットをnginxコンテナに共有するために作成
pumaの設定変更
# Puma can serve each request in a thread from an internal thread pool.
# The `threads` method setting takes two numbers: a minimum and maximum.
# Any libraries that use thread pools should be configured to match
# the maximum value specified for Puma. Default is set to 5 threads for minimum
# and maximum; this matches the default thread size of Active Record.
#
max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count }
threads min_threads_count, max_threads_count
# Specifies the `worker_timeout` threshold that Puma will use to wait before
# terminating a worker in development environments.
#
worker_timeout 3600 if ENV.fetch("RAILS_ENV", "development") == "development"
# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
#
# port ENV.fetch("PORT") { 3000 }
# Specifies the `environment` that Puma will run in.
#
environment ENV.fetch("RAILS_ENV") { "development" }
# Specifies the `pidfile` that Puma will use.
pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" }
# Specifies the number of `workers` to boot in clustered mode.
# Workers are forked web server processes. If using threads and workers together
# the concurrency of the application would be max `threads` * `workers`.
# Workers do not work on JRuby or Windows (both of which do not support
# processes).
#
# workers ENV.fetch("WEB_CONCURRENCY") { 2 }
# Use the `preload_app!` method when specifying a `workers` number.
# This directive tells Puma to first boot the application and load code
# before forking the application. This takes advantage of Copy On Write
# process behavior so workers use less memory.
#
# preload_app!
# Allow puma to be restarted by `rails restart` command.
plugin :tmp_restart
app_root = File.expand_path("..", __dir__)
# nginx.confのserverと一致させる。
bind "unix://#{app_root}/tmp/sockets/puma.sock"
stdout_redirect "#{app_root}/log/puma.stdout.log", "#{app_root}/log/puma.stderr.log", true
nginxの設定
containers/nginx/nginx.conf
# プロキシ先の指定
# Nginxが受け取ったリクエストをバックエンドのpumaに送信
upstream myapp {
# ソケット通信したいのでpuma.sockを指定volumeでrailsコンテナとnginxコンテナを連携しているので通信ができる
server unix:///myapp/tmp/sockets/puma.sock;
}
server {
listen 80;
# ドメインもしくはIPを指定
server_name localhost;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
# ドキュメントルートの指定
root /myapp/public; <= volumeでnginxコンテナとrailsコンテナの/myapp/publicを共有しているので指定できる
client_max_body_size 100m;
error_page 404 /404.html;
error_page 505 502 503 504 /500.html;
try_files $uri/index.html $uri @myapp;
keepalive_timeout 5;
# リバースプロキシ関連の設定
location @myapp {
proxy_set_header X-Real-IP $remote_addr; <= $remote_addrはアクセス元のIP。ネットワーク層の情報。基本的に直前のIPを保持している、X-Real-IPロードバランサやプロキシを経由する時に送信元を判別するために利用。アプリケーション層の情報。
X-Forwarded-Forと同じような値だけど、複数の可能性があるX-Forwarded-Forと違って1つ。
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; <= X-Forwarded-ForはHTTPヘッダの一つ。ロードバランサやプロキシを経由する時に送信元を判別するために利用
proxy_set_header Host $http_host; <= HostはHost!
proxy_pass http://myapp; <= これで上に定義しているupstream myapp に処理が移動する
}
}
- X-Forwarded-Forについて
Client
↓ X-Forwarded-For: ""
Proxy1
↓ X-Forwarded-For: "Client"
Proxy2
↓ X-Forwarded-For: "Client, Proxy1"
ELB
↓ X-Forwarded-For: "Client, Proxy1, Proxy2"
Nginx
HTTPヘッダの一つ。ロードバランサやプロキシを経由する時に送信元を判別するために利用。アプリケーション層の情報。という順に追加されていく。なので送信元を知りたければ1つ目の要素を見れば良い
Discussion