この記事でやりたいこと
本番環境で Rails application を動かすとき、public ディレクトリ配下の assets を nginx から直接配信したいですよね。Dockerのversion 17.05 以前だと、public配下のassetsを取り出した docker imageを作成して、 docker run
で pipe して、それ上で nginx の docker image を作成したりといろいろ手間がかかっていました。
でも、Docker の version 17.05 以降からサポートされた multi-stage build を使ったらかなりシンプルに書き換えれるのでそれを試して見たいと思います。
事前準備
前提として、手元に Rails new を実行して Rails application があるとします。
まずは、Rails application 用の Dockerfile を作成して、docker image を作成しておきます。Dockerfileは以下のような感じです。Dockerfile 内で assets:precompile
をしておきます。
FROM ruby:2.5.1
ENV LANG C.UTF-8
RUN apt-get update -qq
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends apt-utils
RUN apt-get install -y libpq-dev graphviz imagemagick
RUN curl -sL https://deb.nodesource.com/setup_8.x | bash - && apt-get install -y nodejs build-essential
RUN npm install -g yarn
RUN gem install bundler
RUN gem update
ENV APP_HOME /app
RUN mkdir $APP_HOME
WORKDIR $APP_HOME
ADD Gemfile* $APP_HOME/
ENV GEM_HOME /bundle
ENV BUNDLE_GEMFILE=$APP_HOME/Gemfile \
BUNDLE_JOBS=2 \
BUNDLE_PATH="$GEM_HOME"
RUN bundle install
ADD package.json $APP_HOME/
ADD yarn.lock $APP_HOME/
RUN yarn install
ADD . $APP_HOME
RUN bundle exec rails assets:precompile
CMD ["bundle", "exec", "rails", "s", "-p", "3000", "-b", "0.0.0.0"]
docker コマンドで Rails application を build しちゃいます。
docker build -t yourproject/rails .
nginx 用の Dockerfile の作成
続いて、 multi-stage build を用いて nginx 用の Dockerfile を作成します。ポイントは
-
FROM
コマンドを2回呼び出します。1回目は assets を取り出す用の rails application の docker image です。 -
COPY --from=0
で1回目にFROM
で呼び出した image から assets だけをコピーします。 - ついでにですが、dockerize というツールを使って、起動時に環境変数からnginxの設定ファイルを書き換えれるようにしておきます。
FROM yourproject/rails
FROM nginx:1.11.9-alpine
RUN apk --no-cache --update add curl
ENV DOCKERIZE_VERSION v0.6.1
RUN curl -sL https://github.com/jwilder/dockerize/releases/download/${DOCKERIZE_VERSION}/dockerize-linux-amd64-${DOCKERIZE_VERSION}.tar.gz | tar -C /usr/local/bin -xz
RUN mkdir -p /etc/nginx/sites-enabled
COPY nginx.conf /etc/nginx/nginx.conf
COPY rails.tmpl /srv/rails.tmpl
COPY --from=0 /app/public /srv/shared/public
CMD ["dockerize", "-template", "/srv/rails.tmpl:/etc/nginx/sites-enabled/rails", "nginx", "-g", "daemon off;"]
以下は nginx.conf と sites-enabled
配下に配備する Rails application 用の nginx の設定ファイルです。
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
access_log /var/log/nginx/access.log;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
gzip on;
gzip_http_version 1.0;
gzip_comp_level 2;
gzip_proxied any;
gzip_vary off;
gzip_types text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript application/json;
gzip_min_length 1000;
gzip_disable "MSIE [1-6]\.";
server_names_hash_bucket_size 64;
types_hash_max_size 2048;
types_hash_bucket_size 64;
# use this when need to record response time.
log_format timed_combined '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'$request_time $upstream_response_time';
include /etc/nginx/sites-enabled/*;
}
server {
server_name _;
listen *:{{ default .Env.SERVER_LISTEN_PORT "80" }};
listen [::]:{{ default .Env.SERVER_LISTEN_PORT "80" }} default ipv6only=on;
location /assets {
autoindex on;
alias /srv/shared/public/assets;
}
location /packs {
autoindex on;
alias /srv/shared/public/packs;
}
location /nginx_status {
empty_gif;
break;
}
location /cable {
proxy_pass http://127.0.0.1:3000/cable;
proxy_http_version 1.1;
proxy_set_header Upgrade websocket;
proxy_set_header Connection Upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $proxy_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $http_host;
client_max_body_size 30M;
proxy_read_timeout 600s;
proxy_connect_timeout 60s;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
}
}
以上のファイルを配備したら、 nginx 用の docker image を build します。
docker build -t yourproject/nginx -f Dockerfile.nginx
こんな感じで簡単に public 配下の assets を配信可能な nginx の docker image が簡単に作成できます。あとは、kubernetes なり、ECS なりでいい感じにデプロイすればよいかと思います。今個人的に GKE 上で Rails application のデプロイ環境を構築しているので、そこらへんの構築手順の記事もそのうち書ければと思います。