passenger-dockerでRailsアプリをコンテナ化する（3. 本番モードでコンテナ化する）

<img width="962" alt="passenger-rails.png" src="https://qiita-image-store.s3.amazonaws.com/0/141934/0c5b68a4-9d55-3172-1f58-1d95ae6cb67f.png">

## 参考

[phusion/passenger\-docker: Docker base images for Ruby, Python, Node\.js and Meteor web apps](https://github.com/phusion/passenger-docker)

# 全体の流れ

1. [開発モードでコンテナ化する](https://qiita.com/NaokiIshimura/items/db143bbbc439a7cd10aa)
2. [コンテナからDBに接続できるようにする](https://qiita.com/NaokiIshimura/items/dd76d67ca023bc9351ce)
3. 本番モードでコンテナ化する　←今回の投稿

# 前回やったこと

- 開発モード(development)でRailsアプリをコンテナ化する
- RailsアプリのコンテナからDBに接続できるようにする

# 今回やること

- 本番モード(production)でRailsアプリをコンテナ化する

# もくじ

1. Railsアプリを作成
2. Dockerfileの作成
3. nginxコンフィグを作成
4. DBマイグレーションを実行
5. コンテナをビルド
6. コンテナをローカルで起動

# ソース一式

今回の投稿のために用意したソースは下記で公開しております。
[qiita\-rails\-passenger\-docker/3 at master · NaokiIshimura/qiita\-rails\-passenger\-docker](https://github.com/NaokiIshimura/qiita-rails-passenger-docker/tree/master/3)

# ディレクトリ構成

今回の投稿のために用意した作業ディレクトリは以下の構成です。

| ディレクトリ | 内容 |
| :--- | :--- |
| / | Dockerfile |
| /nginx | nginxコンフィグ一式 |
| /rails-app | railsのソース一式 |

<img width="952" alt="passenger-rails-301.png" src="https://qiita-image-store.s3.amazonaws.com/0/141934/c3b0df1d-ac96-febb-69c6-3f46c2b8f4e9.png">

# 1. Railsアプリを作成

まずはコンテナ化するRailsアプリを用意します。
今回はアクセスした時に静的ページを表示するだけのアプリを用意しました。

![passenger-rails-201.png](https://qiita-image-store.s3.amazonaws.com/0/141934/df2ab368-eada-4861-ba1e-b569c1cc8435.png)

DB接続に必要なパラメータは以下のように環境変数で指定しました。

```ruby:config/database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>

production:
  <<: *default
  database: <%= ENV['DB_DATABASE'] %>
  username: <%= ENV['DB_USER'] %>
  password: <%= ENV['DB_PASSWORD'] %>
  host:     <%= ENV['DB_HOST'] %>
  port:     5432
```

# 2. Dockerfileの作成

Dockerfileについては[phusion/passenger\-docker](https://github.com/phusion/passenger-docker)リポジトリのREADMEを参考にRuby2.5を利用する設定としました。

今回は環境変数`RAILS_ENV`に`production`を指定してます。
また、`rake assets:precompile`の実行を追加してます。

```dockerfile:Dockerfile
##
# phusion/passenger-docker
# https://github.com/phusion/passenger-docker

##
# Getting started

# Ruby images
FROM phusion/passenger-ruby25:latest

# ...put your own build instructions here...

##
# Nginx

# Using Nginx and Passenger
RUN rm -f /etc/service/nginx/down

# Using Nginx and Passenger
RUN rm /etc/nginx/sites-enabled/default
ADD nginx/webapp.conf /etc/nginx/sites-enabled/webapp.conf

# Configuring Nginx
ADD nginx/secret_key.conf /etc/nginx/main.d/secret_key.conf
ADD nginx/gzip_max.conf /etc/nginx/conf.d/gzip_max.conf

##
# Rails application

# Set correct environment variables.
ENV RAILS_ENV production

# Your application should be placed inside /home/app.
COPY --chown=app:app rails-app /home/app/webapp

# bundlerのインストール
# 実行しなかった場合、Railsアプリにアクセスした時に以下のエラーが発生する
# cannot load such file -- bundler/dep_proxy (LoadError)
RUN gem install bundler

# tzdataのインストール
# 実行しなかった場合、Railsアプリにアクセスした時に以下のエラーが発生する
# tzinfo-data is not present.
# Please add gem 'tzinfo-data' to your Gemfile
# and run bundle install (TZInfo::DataSourceNotFound)
RUN apt-get update && apt-get install -y \
  tzdata

# タイムゾーンをJSTに設定
ENV TZ=Asia/Tokyo

# bundle install
WORKDIR /home/app/webapp
RUN bundle install

# assets:precompile
RUN bundle exec rake assets:precompile

EXPOSE 80

# Clean up APT when done.
RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

# Use baseimage-docker's init process.
CMD ["/sbin/my_init"]
```

#3. nginxコンフィグを作成
 
Dockerfileと同様に[phusion/passenger\-docker](https://github.com/phusion/passenger-docker)リポジトリのREADMEを参考にRuby2.5を利用する設定としました。

今回はパラメータ`passenger_app_env`に`production`を指定してます。
また、`assets`にアクセスするための設定を追加してます。

```nginx:nginx/webapp.conf
server {
  listen 80;
  server_name www.webapp.com;
  root /home/app/webapp/public;

  # The following deploys your Ruby/Python/Node.js/Meteor app on Passenger.

  # Not familiar with Passenger, and used (G)Unicorn/Thin/Puma/pure Node before?
  # Yes, this is all you need to deploy on Passenger! All the reverse proxying,
  # socket setup, process management, etc are all taken care automatically for
  # you! Learn more at https://www.phusionpassenger.com/.
  passenger_enabled on;
  passenger_user app;

  # If this is a Ruby app, specify a Ruby version:
  passenger_ruby /usr/bin/ruby2.5;

  # Ensures that RAILS_ENV, NODE_ENV, etc are set to "staging"
  # when your application is started.
  passenger_app_env production;

  client_max_body_size 100m;

  location ^~ /assets/ {
    access_log off;
    gzip_static on;
    expires 0;
    add_header Cache-Control public;
    add_header ETag "";
    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Request-Method *;
  }

}
```

DB接続に利用する環境変数をnginxのコンフィグにも記述しておきます。
SECRET_KEY_BASE, RAILS_LOG_TO_STDOUTも環境変数で指定できるようにしておきます。

```nginx:nginx/secret_key.conf
env DB_HOST;
env DB_USER;
env DB_PASSWORD;
env DB_DATABASE;
env SECRET_KEY_BASE;
env RAILS_LOG_TO_STDOUT;
```

```nginx:nginx/gzip_max.conf
gzip_comp_level 9;
```

# 4. DBマイグレーションを実行

```shell-session
$ cd rails-app

$ export DB_HOST=xxxxx \
  export DB_USER=xxxxx \
  export DB_PASSWORD=xxxxx \
  export DB_DATABASE=xxxxx

$ rake db:migrate RAILS_ENV=production

== 20180329092915 CreateUsers: migrating ======================================
-- create_table(:users)
   -> 0.0134s
== 20180329092915 CreateUsers: migrated (0.0136s) =============================
```

# 4. コンテナをビルド

```shell-session
$ docker build -t rails-image .

...
Successfully built 9a074ceb562e
Successfully tagged rails-image:latest
```

# 6. コンテナをローカルで起動

```shell-session
$ docker run \
  -p 3000:80 \
  -e DB_HOST=xxxxx \
  -e DB_USER=xxxxx \
  -e DB_PASSWORD=xxxxx \
  -e DB_DATABASE=xxxxx \
　　　　-e SECRET_KEY_BASE=xxxxx \
  rails-image
```

ブラウザで`http://localhost:3000`にアクセスしてRailsアプリが表示されることを確認

![passenger-rails-201.png](https://qiita-image-store.s3.amazonaws.com/0/141934/df2ab368-eada-4861-ba1e-b569c1cc8435.png)