Ruby on Rails のセットアップ
要件
サーバは unicorn
バックエンドの API サーバを想定
フロントエンドのものは全て不要
DB は PostgreSQL
テストは mintest
環境
Ruby 2.7.2
Ruby on Rails 6.0.3.4
手順
1. 空の ruby Dockerfile を作って build する
code:Dockerfile
ARG RUBY_VERSION=2.7.2
FROM ruby:$RUBY_VERSION
RUN apt-get --quiet update && \
apt-get --quiet --yes install --no-install-recommends postgresql-client
ENV BUNDLE_JOBS=4 \
BUNDLE_GEMFILE=/app/Gemfile \
BUNDLE_PATH=/vendor/bundle \
RAILS_LOG_TO_STDOUT=true
RUN mkdir -p $BUNDLE_PATH
WORKDIR /app
COPY . /app
2. ローカルのプロジェクトを設定したいディレクトリをマウントして起動する
3.bundle initして Gemfile を生成する
4. Gemfile に rails を追加する
5. bundle installする
Gemfile.lock が追加される
6. Dockerfile に次を追加
code:Dockerfile
RUN bundle config set frozen 'true'
RUN bundle config set without 'development test'
# production にデプロイするものと共用したいときは次のようにする
RUN bundle install
7. rails new
code:sh
bundle exec rails new . --database=postgresql --skip-git --skip-keeps --skip-action-mailer --skip-action-mailbox --skip-action-text --skip-active-storage --skip-puma --skip-action-cable --skip-sprockets --skip-spring --skip-javascript --skip-turbolinks --skip-system-test --api --skip-webpack-install
--minimal が使いたいがこのバージョンではリリースされていなかった
コンテナ内で生成されたファイルは(Linuxだと) root になるので chown しておくこと
新規リポジトリで git init も兼ねるときは --skip-git を外すこと
もしくは一旦、--skip-git無しで .gitignoreを生成してからコピーしておく
8. Dockerfile でサーバを起動するように設定
code:Dockerfile
EXPOSE 3000
この状態だと WEBrick で起動する
アクセスするとDB設定をしていないので 500 が返ることに注意
DB 設定を無効化するには active_record の require と environment/development.rb の config をコメントアウトする
9. 不要なファイル/ディレクトリを消す
使われなくても生成されてしまうものがあるようだったのでここで消す
code:sh
git clean -ndx # 確認用
git clean -fdx
# ActiveJob を削除
rm app/job
nvim config/application.rb # active_job の require を消す
次のファイルを削除
code:sh
config/initializers/application_controller_renderer.rb
config/initializers/backtrace_silencers.rb
config/initializers/cors.rb
config/initializers/inflections.rb
config/initializers/mime_types.rb
config/locales/en.yml
tzinfo の警告に対応する
Windows でタイムゾーンを取得するための Gem らしいので、Linux コンテナを使ってる限りはそのまま消しても大丈夫だろう
9. unicorn を設定
Gemfile に gem 'unicorn' を追記
Dockerfile の起動コマンドを
code:dockerfile
にする
デプロイ用にRACK_ENVを Kubernetes の ConfigMap に書くか、--env productionを付けておくこと
個別の設定は --config-file で指定する
config/unicorn.rb が慣習ぽい
unicorn will look for the config.ru file used by rackup in APP_ROOT.
とあったので、 --config-fileを指定すると config.ru は読まなくなるのかと思ったら、そんなことは無かった
適当にppしてみると指定ファイル、config.ruの順にロードされていた
code:config/unicorn.rb
worker_num = Integer(ENV.fetch('UNICORN_WORKER_NUM'))
worker_processes(worker_num)
code:config.ru
memory_size_bytes_per_container = ENV.fetch('UNICORN_MEMORY_SIZE_BYTES_PER_CONTAINER').to_i
memory_size_bytes_per_worker = memory_size_bytes_per_container / ENV.fetch('UNICORN_WORKER_NUM').to_i
worker_memory_margin = ENV.fetch('UNICORN_WORKER_MEMORY_MARGIN').to_f
memory_limit_min_per_worker = (memory_size_bytes_per_worker * worker_memory_margin).ceil
memory_limit_max_per_worker = memory_size_bytes_per_worker
use Unicorn::WorkerKiller::Oom, memory_limit_min_per_worker, memory_limit_max_per_worker
--config-file config/unicorn.rbを起動コマンドに追加すること
to_i/to_fは数字文字列以外を渡すと0/0.0にしてくるので例外を投げるInteger/Floatを使うほうがミスったときに気付きやすい
10. unicorn の graceful shutdown
code:config/unicorn.rb
before_fork do |_server, _worker|
Signal.trap('TERM') do
puts 'Kubernetes expects SIGTERM to gracefully shutdown, but unicorn expects SIGQUIT for that. Convert TERM->QUIT'
Process.kill('QUIT', Process.pid)
end
end
unicorn は SIGQUIT で gracefull shutdown を行うが、Kubernetes は SIGTERM で行うので変換をしてやる
11. ActiveRecord の Connection pool 設定
database.ymlの pool を設定する
unicorn は process fork モデルなので、Active Record の connection pool はそれぞれのプロセスに用意されることになる
なので、スレッドを使わなければ pool 数は常に1でもよさそう
こっそりスレッドを使っているライブラリなどがあったら困りそうではある
参考