Elixirアプリケーションのコンテナをできるだけダイエットした話
概要
Elixirで書いたchatops用アプリケーション anago
をGKEで動かそうとdocker buildしたらかなり重めのRails並の容量になってしまったのでどこまでそぎ落とせるか検証した話です。
Qiitaに書いた記事の転載です
FROM elixir:latest
WORKDIR /app
ENV MIX_ENV=prod
RUN mix local.hex --force && \
mix local.rebar --force
COPY mix.exs mix.exs
COPY mix.lock mix.lock
COPY config config
RUN mix deps.get --only prod && \
mix deps.compile
COPY . .
CMD ["mix", "run", "--no-halt"]
最初の頃のDockerfileはこんな感じです。
このDockerfileでビルドしたアプリケーションのサイズは下記の通り...
docker images
##
REPOSITORY TAG IMAGE ID CREATED SIZE
anago latest aa7e0779d912 2 seconds ago 1.27GB
画像もJSONもDBもない簡単なアプリケーションでのサイズが1.27GBと出てきたらさすがにダイエットを決意するよね...
ベースイメージの確認
Elixirのベースイメージサイズは下記の通り。
docker pull elixir
## docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
elixir latest 83478b936eba 2 days ago 1.24GB
比較にGo言語のコンテナを取得してみると...
docker pull golang
### docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
golang latest 5f9d35ce5cfe 3 weeks ago 839MB
やっぱりファイルサイズはビルドしたライブラリのコード含めても30MB程度。
だけどベースイメージがそもそも1.24GBもある...
比較に取ったGoのイメージと比べても300MB近くElixirのほうが重たい...
alpineにしてみる
イメージサイズのダイエットの入り口はベースイメージをalpineにすることだと思うのでベースイメージを変えてみる。
docker pull elixir:alpine
## docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
elixir alpine dd10458addd0 2 days ago 84.9MB
圧倒的シェイプ!!
これにアプリケーションの30MBを追加しても114MBに!
REPOSITORY TAG IMAGE ID CREATED SIZE
anago alpine c8b8c08b7aef About a minute ago 114MB
もともとのサイズと比較すると10%未満にダイエット成功!!
もうこれで良いんじゃない?
escript、動きます
Erlangにはアプリケーションを一つの実行ファイルに固めるescriptという仕組みがあります。
実行ファイルはErlangのランタイムがある環境でなら実行可能だったりします。
ということはErlang:alpineのイメージで動くんじゃない?
さっそくErlang:alpineのベースイメージを手に入れます。
docker pull erlang:alpine
## docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
erlang alpine daa6bbdd7458 2 weeks ago 69.4MB
ElixirはErlang上で動くためErlangのベースイメージのほうが軽量だったりします。
15MB程度ですが軽量ですね。
これで行きましょう!!
ということでDockerfileに mix escript build
を追加。
ついでにソースコードも要らないのでマルチステージビルドもやりましょう。
FROM elixir:alpine as build
WORKDIR /app
ENV MIX_ENV=prod
RUN mix local.hex --force && \
mix local.rebar --force
COPY mix.exs mix.exs
COPY mix.lock mix.lock
COPY config config
RUN mix deps.get --only prod && \
mix deps.compile
COPY . .
RUN mix escript.build
FROM erlang:alpine
WORKDIR /app
COPY /app/anago /app/anago
これでヨシ!
あらためで docker build
してサイズを見てみると :eyes:
REPOSITORY TAG IMAGE ID CREATED SIZE
anago erlang_alpine a01f810d11dc 2 seconds ago 71.4MB
71.4MBまで軽量化に成功!!!
ついに100MBの壁を突破しましたよ!!
閑話休題
このアプリケーションはもともと mix run --no-halt
で動かすつもりだったのですがescriptにしたら即終了されてしまいます。
そういう時は timer.sleep(:infinity)
と書いておくとずっと動いてたりします。
defmodule Anago.CLI do
def main(_args) do
:timer.sleep(:infinity)
end
end
どこまで減らせる?
erlang:alpineだって最低限何らか一緒に入っているだろうとは思うのでalpineに apk add erlang
してどこまで減るのか気になってきました。
バニラalpineのサイズはわずかに5MBです。
試しにalpineにerlangだけインストールするDockerfileを作ってみます。
FROM alpine:latest
RUN apk add --no-cache erlang
このDockerfileをビルドすると結果は 63.6MBまで軽量化されます!(Erlangが58MBくらいの容量になるのでこれ以上のダイエットは無理)
ここまで減らしちゃうとちゃんと動くのかが不安になりますがここまで来たら目標もどこまでサイズを削ぎおとせるかにシフトしているのでアプリケーションを含めてビルドしてみます。
REPOSITORY TAG IMAGE ID CREATED SIZE
anago custom_alpine fb1de28697a1 9 minutes ago 65.6MB
65.6MBまで来ました :tada:
あとがき
ちなみにこのイメージもちゃんと実行できました(すごい...)
Erlangのサイズの限界でおそらくこれ以上の軽量化はできないと思うんで軽量化の限界は63.6MB + escriptとなるようです。
Discussion