Ruby
Rails
docker

Docker Multi-Stage BuildsでRailsのイメージを軽量化してみる

はじめに

RailsのDockerイメージのサイズを半分以下にする事ができました!

Docker Multi-Stage Buildsとは

Docker17以降のバージョンでDockerfileにFrom句を複数書けるようになりました。
これがマジでヤバイ!

何がヤバイかというと、
複数のビルドしたイメージから必要なものだけをコピーできるということ。
ライブラリを展開する為に必要なツールのインストールなど行う場合が多々あるかと思いますが、最終的なイメージには不要なものもあるかと思います。

そういった不要なものを省く事が簡単にできます。
イメージはなるべくサイズを抑えるべきです。
イメージのビルド、デリバリの時間を圧縮でき、これはCI/CDの基本です。

Docker Multi-Stage Buildsに関しては、こちらの記事で詳しく解説されています。
Docker multi stage buildで変わるDockerfileの常識

Before And After

Rails用のdocker imageのサイズがどのくらい変わるか結果を先にお見せします。

Before

771MB

➜  rails_docker git:(master) ✗ docker images | grep web
railsdocker_web                latest              f2772a54bc64        54 seconds ago      771MB

After

355MB
なんと、半分以下のサイズになっています。

➜  rails_docker git:(master) ✗ docker images | grep web   
railsdocker_web                latest              bfeeacbc82e7        12 days ago         355MB

Dockerfile

続いてDockerfileのBefore, Afterを比較してみましょう。

Before

FROM ruby:2.3.2-alpine
ENV LANG ja_JP.UTF-8
RUN apk --update add --virtual build-dependencies \
    build-base \
    curl-dev \
    mysql-dev \
    linux-headers
RUN apk --update add \
    bash \
    nodejs \
    mariadb-dev \
    tzdata \
    && rm /usr/lib/libmysqld*

RUN gem install bundler

WORKDIR /tmp
COPY Gemfile Gemfile
COPY Gemfile.lock Gemfile.lock
ENV BUNDLE_JOBS=4
RUN bundle install
RUN apk del build-dependencies

ENV APP_HOME /myapp
RUN mkdir -p $APP_HOME
WORKDIR $APP_HOME
COPY . $APP_HOME

After

FROM ruby:2.3.2-alpine as builder
RUN apk --update add --virtual build-dependencies \
    build-base \
    curl-dev \
    mysql-dev \
    linux-headers
RUN gem install bundler
WORKDIR /tmp
COPY Gemfile Gemfile
COPY Gemfile.lock Gemfile.lock
ENV BUNDLE_JOBS=4
RUN bundle install
RUN apk del build-dependencies

FROM ruby:2.3.2-alpine
ENV LANG ja_JP.UTF-8
RUN apk --update add \
    bash \
    nodejs \
    mariadb-dev \
    tzdata \
    && rm /usr/lib/libmysqld*
RUN gem install bundler

WORKDIR /tmp
COPY Gemfile Gemfile
COPY Gemfile.lock Gemfile.lock
COPY --from=builder /usr/local/bundle /usr/local/bundle

ENV APP_HOME /myapp
RUN mkdir -p $APP_HOME
WORKDIR $APP_HOME
COPY . $APP_HOME

ここでポイントとなるのが、

FROM ruby:2.3.2-alpine as builder

と、

COPY --from=builder /usr/local/bundle /usr/local/bundle

です。
あと、FROMが2回登場している事がわかります。
最初のビルドでbuilderというエイリアスをつけており、そこから、

/usr/local/bundle

を次のビルドでコピーしています。
要は、最初のビルドではbundle installするだけが目的です。
その為のツールは全て不要です。

さいごに

Multi-Stage BuildsによってDockerイメージの軽量化ができました。
もちろん、Multi-Stage Buildsを利用せずとも軽量化はできます。
不要なファイルを手動で個別に削除していけば良いわけです。
しかし、Multi-Stage Buildsを使えば削除の手順を省く事ができるので、Dockerfileの見通しがよくなります。

更に、イメージの軽量化は可能ですが、細かいTipsは割愛します。

RailsのDockerイメージは肥大しやすいので、イメージサイズを少し意識してみてはいかがでしょうか?

快適なDockerライフを。