🐳
Docker のマルチステージビルドで Rails イメージを軽くする
昨今は本番環境にも当然のように Docker を使用すると思います。
その時、 Docker イメージが重いと CI/CD の速度も遅くなりますし、コンテナの起動も遅くなるので辛いです。Docker イメージを軽くすることの必要性は広まってきたように感じます。
そんなわけで今回は、 Docker のマルチステージビルドを使って Rails イメージを軽量化してみたいと思います🙌
前提
元々の Dockerfile は以下です。
FROM ruby:2.7.2-alpine3.12
ENV ROOT="/app"
ENV LANG=C.UTF-8
ENV TZ=Asia/Tokyo
WORKDIR ${ROOT}
RUN apk update && \
apk add --no-cache \
gcc \
g++ \
libc-dev \
libxml2-dev \
linux-headers \
make \
postgresql \
postgresql-dev \
tzdata && \
apk add --virtual build-packs --no-cache \
build-base \
curl-dev
COPY Gemfile ${ROOT}
COPY Gemfile.lock ${ROOT}
RUN bundle install
RUN apk del build-packs
COPY . ${ROOT}
# Add a script to be executed every time the container starts.
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3001
CMD ["rails", "server", "-b", "0.0.0.0"]
alpine
をベースにしており、 API モードのRailsアプリなので、 node.js
とかは入れていません。フロントは別で用意します。
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
backend_web latest 135f3d0ee69d 5 days ago 779MB
まだRailsにしては軽い方だと思いますが、重い。。。
最終的にどうなったか
FROM ruby:2.7.2-alpine3.12 as builder
ENV ROOT="/app"
ENV LANG=C.UTF-8
ENV TZ=Asia/Tokyo
WORKDIR ${ROOT}
RUN apk update && \
apk add --no-cache \
gcc \
g++ \
libc-dev \
libxml2-dev \
linux-headers \
make \
postgresql-dev \
tzdata && \
apk add --virtual build-packs --no-cache \
build-base \
curl-dev
COPY Gemfile ${ROOT}
COPY Gemfile.lock ${ROOT}
RUN bundle install
RUN apk del build-packs
FROM ruby:2.7.2-alpine3.12
ENV ROOT="/app"
ENV LANG=C.UTF-8
ENV TZ=Asia/Tokyo
RUN apk update && \
apk add \
postgresql-dev \
tzdata
WORKDIR ${ROOT}
COPY /usr/local/bundle /usr/local/bundle
COPY . ${ROOT}
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3001
CMD ["rails", "server", "-b", "0.0.0.0"]
こんな感じでダイエットしました!!
割と王道な使い方だけですが、ポイントは、
- 1個目のステージの責務を
bundle install
を通すだけにする - 2個目のステージ(実際の起動用)では
--from
から1個目のステージを指定してライブラリをコピーする
というところです。
その結果👇
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
backend_web latest 7dbd2aa9ebab 37 seconds ago 405MB
これだけで、 350MB
以上ダイエットに成功しました😊
地味に思うかもしれませんが、これだけの労力で 350MB
以上軽くなるなら結構お得なのではないでしょうか?
まとめ
「マルチステージビルド」と聞くと難しそうに聞こえますが、内容としては簡単です。
-
FROM
を複数用意してそれぞれに名前を付ける(これが「ステージ」になります) - 後のステージから前のステージを
--from
を使って参照できる
別にマルチステージビルドを使わなくても、不要なファイルを rm
とかで地道に消していけば同じようにダイエット効果が得られます。
ただ、それだと非常に Dockerfile の可読性が下がりますし、漏れがあったりします。何よりめんどくさい。。。
なので、「必要なものだけコピーしてあとは放置」というマルチステージビルドを利用することをお勧めします😄
Discussion