フロントとバックエンドを同一コンテナで済ませるパターンと, 1 プロセス 1 コンテナの原則に従い個別コンテナでやるパターンです.
他にも, 共有ストレージ専用の busybox を利用するパターン などがあるようです.
今回の要件
- フロントエンドは Nginx, バックエンドは Rails5 (Puma)
- Nginx-Puma 間は socket 通信
- Rails の静的コンテンツ (
public/
配下) は Nginx が処理する
パターンA: Nginx と Rails が同一コンテナ
全てのコードはこちらにあります.
https://github.com/na-o-ys/rails_docker_sample/tree/single-container
全体図
$ rails new
直後のディレクトリに Dockerfile
などを配置したもの.
一つの Dockerfile で Rails のセットアップ, Nginx のインストール & セットアップを行うため, 複数コンテナの連携など特別なことをする必要は無い.
.
├── Dockerfile
├── Gemfile
├── Gemfile.lock
├── app/
├── config
│ ├── ...
│ └── puma.rb
├── log
├── nginx.conf
├── public
│ ├── 404.html
│ └── ...
└── .dockerignore
Puma
最終行で, #{app_root}/tmp/sockets/puma.sock
に bind しています.
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }.to_i
threads threads_count, threads_count
port ENV.fetch("PORT") { 3000 }
environment ENV.fetch("RAILS_ENV") { "development" }
plugin :tmp_restart
app_root = File.expand_path("../..", __FILE__)
bind "unix://#{app_root}/tmp/sockets/puma.sock"
Nginx
-
/app/public
を静的コンテンツとして配信 - その他を
/app/tmp/sockets/puma.sock
にプロキシ
# https://github.com/puma/puma/blob/master/docs/nginx.md
upstream app {
server unix:///app/tmp/sockets/puma.sock;
}
server {
listen 80;
server_name localhost; # TODO
keepalive_timeout 5;
# static files
root /app/public;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
# static files
if (-f $request_filename) {
break;
}
if (-f $request_filename.html) {
rewrite (.*) $1/index.html break;
}
if (-f $request_filename.html) {
rewrite (.*) $1.html break;
}
if (!-f $request_filename) {
proxy_pass http://app;
break;
}
}
location ~* \.(ico|css|gif|jpe?g|png|js)(\?[0-9]+)?$ {
expires max;
break;
}
}
Dockerfile
ホストのカレントディレクトリ (Rails root) を /app
にマウントしています.
FROM ruby:2.3.1
RUN apt-get update -qq && \
apt-get install -y build-essential libpq-dev nodejs && \
apt-get install -y nginx
# Nginx
ADD nginx.conf /etc/nginx/sites-available/app.conf
RUN rm -f /etc/nginx/sites-enabled/default && \
ln -s /etc/nginx/sites-available/app.conf /etc/nginx/sites-enabled/app.conf
# Rails App
RUN mkdir /app
WORKDIR /app
ADD Gemfile /app/Gemfile
ADD Gemfile.lock /app/Gemfile.lock
RUN bundle install
ADD . /app
RUN mkdir /app/tmp/sockets
# Start Server
CMD bundle exec puma -d && \
/usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf
.dockerignore
ホスト側のゴミファイルが紛れ込まないように.
tmp/*
log/*
.git
パターンB: Nginx と Rails が個別コンテナ
全体図
全てのコードはこちらにあります.
https://github.com/na-o-ys/rails_docker_sample/tree/multiple-container
$ rails new
直後のディレクトリに Dockerfile
などを配置したもの.
Rails 用の Dockerfile (ルート直下) と Nginx 用の Dockerfile (containers/nginx/Dockerfile
) を作成し, Docker Compose で2つのコンテナをまとめて起動します.
.
├── Dockerfile
├── Gemfile
├── Gemfile.lock
├── app/
├── config
│ ├── ...
│ └── puma.rb
├── containers
│ └── nginx
│ ├── Dockerfile
│ └── nginx.conf
├── docker-compose.yml
├── log/
├── public
│ ├── 404.html
│ └── ...
└── .dockerignore
.dockerignore
containers/
を ignore する.
tmp/*
log/*
.git
containers
Puma
ファイルはパターンAと全く同じです. 最終行で #{app_root}/tmp/sockets/puma.sock
に bind しているのがポイント.
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }.to_i
threads threads_count, threads_count
port ENV.fetch("PORT") { 3000 }
environment ENV.fetch("RAILS_ENV") { "development" }
plugin :tmp_restart
app_root = File.expand_path("../..", __FILE__)
bind "unix://#{app_root}/tmp/sockets/puma.sock"
Dockerfile (Rails)
VOLUME
を使って, /app/public
と /app/tmp
を他コンテナからマウント可能にしています. それぞれ静的コンテンツと socket を Nginx コンテナから利用するためです.
FROM ruby:2.3.1
RUN apt-get update -qq && \
apt-get install -y build-essential libpq-dev nodejs
# Rails App
RUN mkdir /app
WORKDIR /app
ADD Gemfile /app/Gemfile
ADD Gemfile.lock /app/Gemfile.lock
RUN bundle install
ADD . /app
RUN mkdir -p tmp/sockets
# Expose volumes to frontend
VOLUME /app/public
VOLUME /app/tmp
# Start Server
# TODO: environment
CMD bundle exec puma
Nginx
パターンAと全く同じです.
-
/app/public
を静的コンテンツとして配信 - その他を
/app/tmp/sockets/puma.sock
にプロキシ
# https://github.com/puma/puma/blob/master/docs/nginx.md
upstream app {
server unix:///app/tmp/sockets/puma.sock;
}
server {
listen 80;
server_name localhost; # TODO
keepalive_timeout 5;
# static files
root /app/public;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
# static files
if (-f $request_filename) {
break;
}
if (-f $request_filename.html) {
rewrite (.*) $1/index.html break;
}
if (-f $request_filename.html) {
rewrite (.*) $1.html break;
}
if (!-f $request_filename) {
proxy_pass http://app;
break;
}
}
location ~* \.(ico|css|gif|jpe?g|png|js)(\?[0-9]+)?$ {
expires max;
break;
}
}
Dockerfile (Nginx)
nginx のイメージを使います. /etc/nginx/sites-enabled/
がデフォルトで認識されないので, /etc/nginx/conf.d/
にコンフィグを配置しています.
FROM nginx:1.11.3
RUN rm -f /etc/nginx/conf.d/*
ADD nginx.conf /etc/nginx/conf.d/app.conf
CMD /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf
Docker Compose
ポイントは nginx
の volumes_from: web
です. これにより, web
コンテナの /app/public
と /app/tmp
が nginx
コンテナにマウントされ, 静的コンテンツ配信と socket 通信が可能になります.
version: '2'
services:
web:
build: .
volumes:
- /app/log # persist logs
depends_on:
- db
nginx:
build: containers/nginx
ports:
- "80:80"
volumes_from:
- web
depends_on:
- web
db:
image: postgres
参考
- Quickstart: Docker Compose and Rails - https://docs.docker.com/compose/rails/
- VOLUME - https://docs.docker.com/engine/reference/builder/#/volume
- Compose file reference - https://docs.docker.com/compose/compose-file/
- 複雑な構成のrailsアプリをdockerで動かしたい場合はどうするべきか - http://qiita.com/k-shogo/items/3c887cbf86b3cab85b72
- Docker上でnginx + unicorn + mysql環境を構築 - http://qiita.com/konpyu/items/2c93e52e9cabc547d94b