#0. はじめに
ここでは掲題の環境を構築するためのファイル構成と作業手順について、要点絞って記録しています。詳細を理解したい方は、参考文献を以下に記載しますのでそちらをご参照ください。
【参考文献】
(1) そもそものDocker・コンテナについて
【連載】世界一わかりみが深いコンテナ & Docker入門
(2) 本記事に示したDockerfileやdocker-compose.ymlの中身について
Docker + Rails + Puma + Nginx + MySQL
【Rails】Rails 6.0 x Docker x MySQLで環境構築
DockerでNginx+unicorn+rails+Mysqlの開発環境を作ってみた
(3) nginxとunicornの設定ファイルの中身について
(デプロイ編①)世界一丁寧なAWS解説。EC2を利用して、RailsアプリをAWSにあげるまで
#1. ファイル構成
testフォルダの中で作業をしていくことにして、最初に準備するファイルの構成は以下の通り。
test ─── docker-compose.yml
├─ Dockerfile
├─ Gemfile
├─ Gemfile.lock
└─ Nginx ─── Dockerfile
└─ nginx.conf
続いて、一つずつファイルの中身を見ていきます。
###1. docker-compose.yml
db, unicorn, nginxの順にコンテナを作成・起動し、連携するよう記述。(※起動順序を制御するdepends_onは起動の完了までは保証しないので、事前に起動が完了して欲しいサービスが立ち上がっているか確認するには別のスクリプトを書く必要あり。)
version: '3.5'
services:
db:
image: mariadb:10.3
ports:
- '3306:3306'
environment:
MYSQL_DATABASE: docker_development
MYSQL_ROOT_PASSWORD: password
MYSQL_USER: root
MYSQL_PASSWORD: password
volumes: #dbコンテナの/var/lib/mysqlディレクトリをmysql-dataボリュームにマウント
- mysql-data:/var/lib/mysql
unicorn:
build:
context: .
dockerfile: Dockerfile
command: bundle exec unicorn -c /myproject/config/unicorn.conf.rb
tty: true #pry-byebugを使えるようにする
stdin_open: true
depends_on:
- db
ports:
- "3000:3000"
volumes:
- .:/myproject:cached
#unicornコンテナの/myproject/tmp/socketsディレクトリをtmp-dataボリュームにマウント
#ソケット通信用ファイルをnginxコンテナと共有するため
- tmp-data:/myproject/tmp/sockets
#unicornコンテナの/myproject/publicディレクトリをpublic-dataボリュームにマウント
#画像データなどをnginxと共有するため
- public-data:/myproject/public
nginx:
build:
context: ./nginx
dockerfile: Dockerfile
ports:
- 80:80
restart: always #restartポリシーをalwaysにすると、ホストOSやDockerデーモン起動時にコンテナが自動的に起動する。
volumes:
#nginxコンテナの/myproject/tmp/socketsディレクトリをtmp-dataボリュームにマウント
#ソケット通信用ファイルをunicornコンテナと共有するため
- tmp-data:/myproject/tmp/sockets
#nginxコンテナの/myproject/publicディレクトリをpublic-dataボリュームにマウント
#画像データなどをunicornと共有するため
- public-data:/myproject/public
depends_on:
- unicorn
# ホストOS上にpublic-data, tmp-data, mysql-dataという名前のデータ保存領域を作成
volumes:
public-data:
tmp-data:
mysql-data:
driver: local
2. Dockerfile
Rails6を動作させるコンテナイメージのレシピ。Rails6.0からはWebpackerを使ったJSの管理がデフォルトとなっており、Webpackerを動作させるにはNode.jsとyarnが必要。なので、rubyイメージを取ってきた後でNode.jsとyarnをインストールする。その後、Host側にあるGemfileとGemfile.lockファイルをコンテナ内にコピーし、bundler gemをインストールし、bundle install
コマンドで必要なgemを全てインストール。
#既存のプロジェクトのrubyのバージョンを指定
FROM ruby:2.7
#パッケージの取得
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
&& echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list \
&& apt-get update \
&& apt-get install -y --no-install-recommends\
nodejs \
yarn \
mariadb-client \
build-essential
WORKDIR /myproject
COPY Gemfile /myproject/Gemfile
COPY Gemfile.lock /myproject/Gemfile.lock
RUN gem install bundler
RUN bundle install
COPY . /myproject
###3. Gemfile
Ralsコンテナにインストールするgemの一覧ファイル。rails new
で必要なgemが自動追記されるので、最初はrubyとrailsのgemのみ記載。bundle install
でエラー(参照:RailsのGPL混入問題についてまとめ(mimemagic))が出た(2021年3月29日時点)ので、mimemagicに依存しないrails 6.0.3.6を指定してインストール。
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
ruby '2.7.2'
gem 'rails', "6.0.3.6"
###4. Gemfile.lock
自動追記されるので何も記述しない。
#空ファイル
###5. nginx/Dockerfile
nginxイメージの作成レシピ。ホスト側で用意したnginx設定ファイルをコンテナ内にコピーし、コンテナ起動と同時にnginxサーバが起動するように記述。
FROM nginx:stable
#デフォルトのnginxファイルを削除して作成したものコンテナ内にコピー
RUN rm -f /etc/nginx/conf.d/*
COPY nginx.conf /etc/nginx/conf.d/myproject.conf
#-c以降の設定ファイルを指定して起動 daemon offでフォアグラウンドで起動
CMD /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf
###6. nginx/nginx.conf
/etc/nginx/conf.d/myproject.confにコピーされるnginxの設定ファイル。
upstream unicorn {
#ユニコーンソケットの設定
server unix:/myproject/tmp/sockets/.unicorn.sock fail_timeout=0;
}
server {
#IPとポートの指定
listen 80 default;
#サーバーネームの指定
server_name localhost;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
#ドキュメントルートの指定
root /myproject/public;
client_max_body_size 100m;
error_page 404 /404.html;
error_page 505 502 503 504 /500.html;
try_files $uri/index.html $uri @unicorn;
keepalive_timeout 5;
location @unicorn {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_pass http://unicorn;
}
}
#2. 作業手順
(1) railsアプリを作成。
$ docker-compose run unicorn rails new . --force --database=mysql --webpacker
(2) Gemfileにunicorn gemを追加して、docker-compose build
を実行。ここで、必要なgemが全てインストールされる。
gem 'unicorn'
$ docker-compose build
(3) unicorn.conf.rbを下記のように記述。
$worker = 2
$timeout = 30
$app_dir = "/myproject" #自分のアプリケーションまでのpath
$listen = File.expand_path 'tmp/sockets/.unicorn.sock', $app_dir
$pid = File.expand_path 'tmp/pids/unicorn.pid', $app_dir
$std_log = File.expand_path 'log/unicorn.log', $app_dir
# set config
worker_processes $worker
working_directory $app_dir
stderr_path $std_log
stdout_path $std_log
timeout $timeout
listen $listen
pid $pid
# loading booster
preload_app true
# before starting processes
before_fork do |server, worker|
defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect!
old_pid = "#{server.config[:pid]}.oldbin"
if old_pid != server.pid
begin
Process.kill "QUIT", File.read(old_pid).to_i
rescue Errno::ENOENT, Errno::ESRCH
end
end
end
# after finishing processes
after_fork do |server, worker|
defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection
end
(4) docker-compose.ymlの設定と整合するようにdatabase.ymlを修正し、docker-compose run unicorn bundle exec rails db:create
コマンドでDataBaseを作成。その後、docker-compose build
コマンドにてdbコンテナに反映させておく。
default: &default
adapter: mysql2
encoding: utf8mb4
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: root
password: password
host: db
development:
<<: *default
database: docker_development
$ docker-compose run unicorn bundle exec rails db:create
$ docker-compose run unicorn bundle exec rails db:migrate
(5) docker-compose up -d
コマンドでコンテナを起動。localhost:80にアクセスできることを確認。
$ docker-compose up -d