😽

Rails(Docker)をProductionモードで起動してみる (CI/CDまでの道⑤)

2022/01/23に公開

はじめに

前回は本番環境用にdocker-compose.production.ymlを作成して、Nginxを導入しました。そこでEC2デプロイにすでにチャレンジしてなんとか成功したのですが、そこでRailsProductionモードを知り、その設定が必要だったので今回は設定をしていきます。
いまのdocker-compose.production.ymlでは本番環境では動かすことができませんでした。

作成に利用するリポジトリはこちらになります。

CI/CDの道シリーズ

環境

  • WSL2 (Ubuntu20.04)
  • Docker 20.10.9
  • docker-compose 1.29.1
  • Git 2.25.1
  • VSCode

本番環境で起動するために知るべきこと

まず本番環境で起動するために知っておくべきことをまとめておきます。

DBに関してはRDSというAWSのデータベースを利用するため本来であればDBコンテナに関するものは一切必要なくなります。ですが、今回はローカルで起動するため、次回削除して実際にRDSを利用します。また、それにともなって次回database.ymlに修正を加えます。

Webpackerは本番では利用しません。WebpackerはJavascriptやCSSをコードを書いてくれたらすぐにコンパイルして読み込んでくれる用途で利用しています。ですので本番では開発することはないので最初にRailsのコンパイルコマンドを利用してすべてコンパイルすればそれ以降コンパイルする必要はなくなります。ということで不要となりますのでdocker-compose.production.ymlからは消します。

NginxはRailsの前に置くことでアクセス負荷に対応できたり、高速にViewを返したりすることができるため本番環境では必要になります。開発環境(docker-compose.yml)では必要ないので入れていません。

この点を意識してProductionモードの起動に挑戦します。

ファイルの修正

上で説明した考慮点を踏まえてファイル編集をしていきます。
まずはdocker-compose.production.ymlを修正します。

docker-compose.production.yml
version: "3.9"
services:
  rails:
    build: .
    container_name: rails
    command: bundle exec puma -C config/puma.rb -e production
    volumes:
      - .:/myapp
      - public-data:/myapp/public
      - tmp-data:/myapp/tmp
      - log-data:/myapp/log
      - /myapp/node_modules
    env_file:
      - .env
    depends_on:
      - db
    user: root

  db:
    image: mysql:8.0.27
    container_name: db
    environment:
      TZ: Asia/Tokyo
      MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
    ports:
      - "3306:3306"
    volumes:
      - db:/var/lib/mysql
  
  web:
    build:
      context: containers/nginx
    volumes:
      - public-data:/myapp/public
      - tmp-data:/myapp/tmp
    ports:
      - 80:80
    depends_on:
      - rails

volumes:
  db:
    driver: local
  public-data:
  tmp-data:
  log-data:

修正は大きく4つです。

  • Webpacker関連はすべて削除
  • Railsのコマンドに-e productionを追加(Productionモードで起動)
  • volumesの不要だったものを削除(bundleは不要だったので消しておきます)
  • Railsをrootユーザーで起動(コンテナが作成したファイルを編集することがないのでrootにする(compile時のエラー対策))

起動

では実際にProductionモードで起動してみます。

$ docker-compose -f docker-compose.production.yml build

次にコンパイルをproductionモードで行います。最初にこのコンパイルをおこなうことでJavascriptやCSSが読み込めるようになります。

$ docker-compose -f docker-compose.production.yml run rails rails assets:precompile RAILS_ENV=production
# エラーがでたら以下の対処

ここではdocker runでコンテナ内でコンパイルコマンドを実行しています。これはコンテナ内で実行することも可能ですが、Railsコンテナが立ち上がるときにpublic/packs/にあるコンパイル済みファイルを読み込むため、コンテナ内でコマンドを実行したら一度落としてから立ち上げる必要があります。それは手間なのでdocker runを利用しています。

※ コンパイルでエラーが発生したら

以下のようなエラーが発生するかもしれないです。

rails aborted!
ArgumentError: Missing `secret_key_base` for 'production' environment, set this string with `bin/rails credentials:edit`
/myapp/config/environment.rb:7:in `<main>'
/myapp/bin/rails:5:in `<top (required)>'
/myapp/bin/spring:10:in `block in <top (required)>'
/myapp/bin/spring:7:in `<top (required)>'
Tasks: TOP => environment
(See full trace by running task with --trace)
ERROR: 1

productionモードではディレクトリ内にconfig/master.keyconfig/credentials.ymlというファイルが必要になります。これは鍵と鍵穴のような関係で暗号と複合を行っているファイルです。

以下の記事に詳しい話があるので気になる方はみてください。
【Rails】世界で一番わかりやすい!!「credentials.yml.enc」+「master.key」使い方徹底攻略!

master.keygitignoreの対象となるのでgit cloneでは手に入らないです。ですので両方がなくエラーになることもあります。

今回リポジトリからcloneしてきた方は/config/credentials.ymlを削除してから以下の手順でファイル作成を行います。

まずはファイル作成のために開発モードでRailsコンテナを起動します。

docker-compose.production.ymlを一時修正します。

docker-compose.production.yml
version: "3.9"
services:
  rails:
    build: .
    container_name: rails
    # command: bundle exec puma -C config/puma.rb -e production
    command: bundle exec puma -C config/puma.rb
(省略)

Railsだけを起動して必要なファイルを生成します。(ローカルにpublic/credentials.yml.encpublic/master.keyがある場合は削除します)

$ docker-compose -f docker-compose.production.yml up rails

# 別のターミナルを開く
$ docker exec -it rails sh

# ファイルの生成
$ EDITOR="vi" rails credentials:edit
# :wqでvimを抜ける

public/credentials.yml.encpublic/master.keyができました。このファイルはEC2デプロイに利用しますのでローカルに保存しておいてくださいさい。

コンテナを落とします。

# ctril + Cで落とす
# または 
# $ docker-compose -f docker-compose.production.yml down

docker-compose.production.ymlを先ほどの状態に戻します。

docker-compose.production.yml
version: "3.9"
services:
  rails:
    build: .
    container_name: rails
    command: bundle exec puma -C config/puma.rb -e production

失敗したコマンドを再度実行します。

docker-compose -f docker-compose.production.yml run rails rails assets:precompile RAILS_ENV=production
(省略)
    Entrypoint mini-css-extract-plugin = *
    [0] ./node_modules/css-loader/dist/cjs.js??ref--3-1!./node_modules/postcss-loader/src??ref--3-2!./node_modules/sass-loader/dist/cjs.js??ref--3-3!./app/javascript/stylesheets/application.scss 620 KiB {0} [built]
        + 1 hidden module]

うまくいきました。

DBを作成する

話を戻して環境構築を行っていきます。

まずはproductionで作成するときのDBの設定を変更します。config/database.ymlproductionをいかに修正します。

config/database.yml
(省略)
production:
  <<: *default
  database: myapp_production
  username: root # 修正
  password: <%= ENV["DB_PASSWORD"] %> # 修正

DB作成をdocker runで作成します。コンパイルとDB作成のコマンドをあとでshellにまとめてまとめて実行するようにしようと思うのでコンテナの中での実行ではなくdocker runを利用しています。

# db作成
$ docker-compose -f docker-compose.production.yml run rails rails db:create  RAILS_ENV=production
# エラーが発生したら以下

DB作成でエラーが発生したら

私の環境では以下のエラーが発生します。

jinwatanabe@DESKTOP-4DGUR4Q:~/workspace/CICD_Road/chapter05$ docker-compose -f docker-compose.production.yml run rails rails db:create  RAILS_ENV=production
Creating chapter05_rails_run ... done
/usr/local/bundle/gems/spring-4.0.0/lib/spring/application.rb:101:in `block in preload': Spring reloads, and therefore needs the application to have reloading enabled.
Please, set config.cache_classes to false in config/environments/production.rb.
 (RuntimeError)
        from /usr/local/bundle/gems/railties-6.1.4.4/lib/rails/initializable.rb:32:in `instance_exec'
(省略)

ここで注目してほしいのは

Please, set config.cache_classes to false in config/environments/production.rb.

ここの部分です。config/environments/production.ymlcache_classesfalseにしてくださいと言われています。修正していきます。

config/environments/production.yml
require "active_support/core_ext/integer/time"

Rails.application.configure do
  # Settings specified here will take precedence over those in config/application.rb.

  # Code is not reloaded between requests.
  config.cache_classes = false # 修正

  (省略)

config.cache_claseesをtureからfalseに変更しました。

DB作成コマンドを再度実行します。

# 変更をコンテナに適応するためにビルドしなおす
$ docker-compose -f docker-compose.production.yml build

# DB作成
$ docker-compose -f docker-compose.production.yml run rails rails db:create  RAILS_ENV=production

成功しました

Created database 'myapp_production'

Productionモードで起動する

それではコンテナを起動していきます。

$ docker-compose -f docker-compose.production.yml up

loclahost/testにアクセスしてJavascriptやCSSが効いていれば成功です。

私はJavascriptやCSSが効かずにTemplate errorがでて苦戦しました。(/myapp/log/production.ymlでログが見れます)

またproductionモードなのでlocalhostyah Ruby on Rails(万歳)は見れなくなっています。

/testがIPの後ろに必要になるので注意してください(これでエラーが起きていると誤解しました)

おわりに

作成したものはこちらのリポジトリに用意しています。(cloneして起動する場合はmaster.keyとcredentials.yml.encをpublic/にいれる必要があります)

Productionモードでの起動ができるようになりました。
次回はAWSのインフラを手作業で一から構築してみようと思います。その次には初めてのデプロイの記事を作成する予定です。

参考

GitHubで編集を提案

Discussion