Ruby on Rails のセットアップ
#Ruby
要件
サーバは 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 "$ENABLE_LOCAL_BUILD" != 'true' && bundle config set without 'development test'
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 が使いたいがこのバージョンではリリースされていなかった
https://github.com/rails/rails/pull/39282 を参照
コンテナ内で生成されたファイルは(Linuxだと) root になるので chown しておくこと
新規リポジトリで git init も兼ねるときは --skip-git を外すこと
--skip-gitしたときは https://github.com/github/gitignore/blob/master/Rails.gitignore から.gitignoreを拝借する
もしくは一旦、--skip-git無しで .gitignoreを生成してからコピーしておく
8. Dockerfile でサーバを起動するように設定
code:Dockerfile
EXPOSE 3000
CMD "bundle", "exec", "rails", "server", "-b", "0.0.0.0"
この状態だと 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 の警告に対応する
bundle installする際のtzinfo-dataのwarningがウザい - Qiita
Windows でタイムゾーンを取得するための Gem らしいので、Linux コンテナを使ってる限りはそのまま消しても大丈夫だろう
9. unicorn を設定
unicorn: Rack HTTP server for fast clients and Unix
unicorn | RubyGems.org | コミュニティのGemホスティングサービス
Gemfile に gem 'unicorn' を追記
Dockerfile の起動コマンドを
code:dockerfile
CMD "bundle", "exec", "rails", "server", "-b", "0.0.0.0"
にする
デプロイ用に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の順にロードされていた
kzk/unicorn-worker-killer を入れる
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
https://yhbt.net/unicorn/SIGNALS.html
unicorn は SIGQUIT で gracefull shutdown を行うが、Kubernetes は SIGTERM で行うので変換をしてやる
11. ActiveRecord の Connection pool 設定
database.ymlの pool を設定する
unicorn は process fork モデルなので、Active Record の connection pool はそれぞれのプロセスに用意されることになる
なので、スレッドを使わなければ pool 数は常に1でもよさそう
Rails4.2のコネクションプールの実装を理解する - Akatsuki Hackers Lab | 株式会社アカツキ(Akatsuki Inc.)
こっそりスレッドを使っているライブラリなどがあったら困りそうではある
参考
Using Rails for API-only Applications — Ruby on Rails Guides