Nginx + Rails (unicorn) + MySQL 環境を Docker で構築する
はじめに
元の記事同様、次の図のような構成の Rails 環境を Docker を利用して構築していきます。Nginx コンテナはリバースプロキシとして挟んでいて、主な構成は Rails と MySQL の 2 つのコンテナになります。
本記事で利用するディレクトリの構成は以下のようになっています。記事内で出てくるファイル名は、適宜こちらを参考にしてください。また、いくつかメインではない設定ファイルがあり、こちらは記事が冗長になることを避けて掲載していません。成果物は GitHub においてありますので、詳細はこちらを参照していただけると幸いです。
.
├── Gemfile
├── Gemfile.lock
├── README.md
├── docker
│ ├── gitignore # .gitignore としてコピーして利用することを想定
│ ├── mysql
│ │ ├── Dockerfile
│ │ ├── charset.cnf
│ │ └── password.yml
│ ├── nginx
│ │ ├── Dockerfile
│ │ ├── default.conf
│ │ └── nginx.conf
│ └── rails
│ ├── Dockerfile
│ └── unicorn.rb
└── docker-compose.yml
Rails コンテナの定義と unicorn の設定
はじめに、Rails のコンテナイメージを作成していきましょう。これは、docker/rails/Dockerfile
として保存します。
FROM ruby:2.6.3
RUN apt-get update -qq && \
apt-get install -y apt-utils \
build-essential \
libpq-dev \
nodejs \
default-mysql-client
RUN mkdir /app
WORKDIR /app
ADD Gemfile /app/Gemfile
ADD Gemfile.lock /app/Gemfile.lock
RUN bundle install -j4
ADD . /app
EXPOSE 3000
また、Rails をインストールするために以下のような Gemfile も作成しておきます。今回は、ディレクトリ直下に作成しています。
source 'https://rubygems.org'
gem 'rails', '5.2.3'
空の Gemfile.lock も一緒に作成します。
touch Gemfile.lock
最後に、docker/rails/unicorn.rb
を以下の内容で作成します。これは、app コンテナで config/unicorn.rb
として配置されることになります。
worker_processes 8
pid "/var/run/unicorn.pid"
listen "/var/tmp/unicorn.sock"
stdout_path "./log/unicorn.stdout.log"
stderr_path "./log/unicorn.stderr.log"
ここで、/var/tmp/unicorn.sock
という Unix ソケットを Listen するように設定しています。Unix ソケットは、Nginx でリバースプロキシするときに利用するファイルなので覚えておいてください。
MySQL コンテナの定義
つぎに、docker/mysql/Dockerfile
を書き、MySQL コンテナのイメージを作成します。
FROM mysql:5.7
RUN apt-get update && \
apt-get install -y apt-utils \
locales && \
rm -rf /var/lib/apt/lists/* && \
echo "ja_JP.UTF-8 UTF-8" > /etc/locale.gen && \
locale-gen ja_JP.UTF-8
ENV LC_ALL ja_JP.UTF-8
ADD ./docker/mysql/charset.cnf /etc/mysql/conf.d/charset.cnf
Rails アプリケーションの作成と起動
Rails と MySQL のコンテナイメージの定義ができたので、docker-compose.yml を書いてオーケストレーションをしていきましょう。ここでは、以下のような内容になります。
version: '2'
services:
app:
build:
context: .
dockerfile: ./docker/rails/Dockerfile
# command: bundle exec unicorn -p 3000 -c /app/config/unicorn.rb
command: bundle exec rails s -p 3000 -b '0.0.0.0'
ports:
- '3000:3000'
volumes:
- /var/tmp
- .:/app
depends_on:
- db
extends:
file: ./docker/mysql/password.yml
service: password
db:
build:
context: .
dockerfile: ./docker/mysql/Dockerfile
ports:
- '3306:3306'
volumes:
- db_data:/var/lib/mysql
extends:
file: ./docker/mysql/password.yml
service: password
volumes:
db_data:
Nginx コンテナはこのあと設定するので、Rails コンテナが起動するときに実行するコマンドには rails s
を設定しました。
docker-compose.yml が書けたら、ビルドしてみましょう。docker-compose では次のコマンドを叩くことでビルドが可能です。
docker-compose build
このビルドで、bundle install
まで走るので、Rails に必要な gem もコンテナイメージにインストールされることになります。
ビルドが成功しても、この段階ではアプリケーションは作成されていません。そこで、docker-compose コマンドを利用して app サービス上で rails new
を実行していきます。
docker ps -aq | xargs docker rm -f
docker-compose run app rails new . --force --database=mysql --skip-bundle
これで、Rails アプリケーションに必要なファイルがホストマシン上にも作成されたかと思います。
このとき、Gemfile が更新されているので、gem 'unicorn'
という行を追加して再度 docker-compose build
してコンテナイメージを更新しておきましょう。ここで単に bundle install
しても、コンテナが終了してしまえば状態は保存されず破棄されてしまうからです。
また、docker-compose.yml の volumes の設定によって、docker-compose の置かれているホスト上のアプリケーションルートと Docker 上のアプリケーションルートを同期するように設定してあります。したがって、ホスト上にもアプリケーションのファイル群が作成されています。
これらのファイルはコンテナ上のファイルと同期されているので、ホストに作成されたファイルを編集することで開発を進めることができます。
パスワードなど非公開情報の隔離
docker-compose の設定ファイルでは、docker/mysql/password.yml
の内容を extends する形で読み込んでいます。以下に読み込んでいる内容の例を示しました。
version: '2'
services:
password:
environment:
MYSQL_ROOT_PASSWORD: password
MYSQL_USER: hoge
MYSQL_PASSWORD: hogepassword
TZ: "Asia/Tokyo"
docker-compose では、このようにサービスをファイルに分けることができるので、上記のような非公開情報を隔離することができます。バージョン管理にはこのファイルを含めないようにすることで、非公開情報を GitHub に置くことを避けることができます。事故を防ぐために .gitignore にも追加しておくのが望ましいでしょう。
スキーマの作成
アプリケーションが作成できたら、次に示す config/database.yml
の一部を編集して Rails が MySQL にアクセスできるようにします。docker-compose で定義したサービス名 db
で、コンテナから別のコンテナにアクセスすることができます。
default: &default
adapter: mysql2
encoding: utf8
pool: 5
username: root
password: <%= ENV['MYSQL_ROOT_PASSWORD'] %>
host: db
この編集が終わったら、rake db:create
して MySQL にスキーマを作成しましょう。
docker-compose run app rake db:create
Rails アプリケーションの起動確認
ここまでくると、Rails アプリケーションを起動させることができます。以下のコマンドを叩いてアプリケーションを起動しましょう。
docker-compose up
うまく設定ができていれば、ブラウザで localhost:3000
にアクセスすると、次のような画面が表示されます。
unicorn の設定
Rails アプリケーションを立ち上げることができたので、最後に unicorn の設定をおこないましょう。まずは、docker-compose の app サービスが次のコマンドを実行するようにコメントアウトを切り替えておきます。
command: bundle exec unicorn -p 3000 -c /app/config/unicorn.rb
このコマンドでも指定しているように、./config/unicorn.rb
を配置しておく必要があるので、次のようにコピーしておきます。
cp ./docker/rails/unicorn.rb ./config/
これで、unicorn でアプリケーションを起動できるようになります。unicorn.rb
自体はリポジトリを参照ください。
Nginx によるリバースプロキシ
ここまでで、Rails アプリケーションを Docker コンテナを利用して起動させることができるようになりました。本節では、ここに Nginx コンテナを加えて、リバースプロキシさせるようにしていきます。
まずは、Nginx の Dockerfile を書いてコンテナイメージを作成しましょう。今回は以下のように書きました。
FROM nginx:1.16
RUN apt-get update && \
apt-get install -y apt-utils \
locales && \
echo "ja_JP.UTF-8 UTF-8" > /etc/locale.gen && \
locale-gen ja_JP.UTF-8
ENV LC_ALL ja_JP.UTF-8
ADD ./docker/nginx/nginx.conf /etc/nginx/nginx.conf
ADD ./docker/nginx/default.conf /etc/nginx/conf.d/default.conf
Nginx で unicorn に対してリバースプロキシする設定を ./docker/nginx/default.conf
に書いています。全体はリポジトリのほうで見ていただくとして、重要なところを次に抜粋しました。
upstream unicorn {
server unix:/var/tmp/unicorn.sock;
}
server {
listen 80;
server_name localhost;
# 略
location / {
proxy_pass http://unicorn;
}
# 略
このあと docker-compose の設定を見直して app サービスを nginx サービスに volumes_from するので、nginx サービスから app サービスの /app/tmp/sockets/unicorn.sock
というパスにおくソケットにアクセスできるようになります。
また、メインの nginx.conf
については特に目立った変更をしていませんが、一応ホストから編集できるように、Dockerfile で ADD するようにしています。
リバースプロキシを含めたアプリケーションシステムの起動
あとは、Nginx のサービス設定を加えた docker-compose を用意し、docker-compose 経由で全コンテナを起動することでアプリケーションが立ち上げることができます。最終的な docker-compose.yml
はリポジトリからご覧ください。
docker-compose up
今回の設定では、http://localhost でブラウザからアクセスすることができます。アクセスして Nginx でリバースプロキシする以前と同様の画面が表示されたら成功したと言っていいでしょう。
おわりに
すこし説明が煩雑になってしまったかもしれません。不具合、ご指摘などありましたら、コメントいただければ幸いです。
Discussion