The story of dieting the container of the Elixir application as much as possible

Overview

When I tried to run the chatops application anago written in Elixir with GKE, it became a fairly heavy Rails-like capacity, so I verified how far it can be removed.

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"]

The Dockerfile at the beginning looks like this. The size of the application built with this Dockerfile is as follows ...

docker images
##
REPOSITORY                                TAG             IMAGE ID       CREATED          SIZE
anago                                     latest          aa7e0779d912   2 seconds ago    1.27GB

If the size of a simple application with no images, JSON, or DB comes out as 1.27GB, you will definitely decide to go on a diet ...

Confirmation of base image

The base image size of Elixir is as follows.

docker pull elixir
## docker images
REPOSITORY                                TAG             IMAGE ID       CREATED         SIZE
elixir                                    latest          83478b936eba   2 days ago      1.24GB

When I get a Go language container for comparison ...

docker pull golang
### docker images
REPOSITORY                                TAG             IMAGE ID       CREATED          SIZE
golang                                    latest          5f9d35ce5cfe   3 weeks ago      839MB

After all, the file size is about 30MB including the code of the built library. But the base image is 1.24GB in the first place ... Elixir is nearly 300MB heavier than the image of Go for comparison: upside_down:

Try alpine

I think that the entrance to the image size diet is to make the base image alpine, so I will change the base image.

docker pull elixir:alpine
## docker images
REPOSITORY                                TAG             IMAGE ID       CREATED         SIZE
elixir                                    alpine          dd10458addd0   2 days ago       84.9MB

Overwhelming shape! 96% reduction! Even if you add 30MB of application to this, it will be 114MB!

REPOSITORY                                TAG             IMAGE ID       CREATED              SIZE
anago                                     alpine          c8b8c08b7aef   About a minute ago   114MB

Successful dieting to less than 10% compared to the original size! !! Isn't this all right? : thinking:

escript works

Erlang has a mechanism called escript that consolidates an application into a single executable file. The executable file may be executable in an environment with the Erlang runtime. Does that mean it works with the image of Erlang: alpine? : thinking: Get the base image of Erlang: alpine right away.

docker pull erlang:alpine
## docker images

REPOSITORY                                TAG             IMAGE ID       CREATED          SIZE
erlang                                    alpine          daa6bbdd7458   2 weeks ago      69.4MB

Since Elixir runs on Erlang, the base image of Erlang may be lighter. It's about 15MB, but it's lightweight. Let's go with this! !!

So I added mix escript build to Dockerfile. By the way, you don't need the source code, so let's do a multi-stage 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 --from=build /app/anago /app/anago

This is Yoshi! : thumbsup: When I try docker build again and look at the size: eyes:

REPOSITORY                                TAG             IMAGE ID       CREATED          SIZE
anago                                     erlang_alpine   a01f810d11dc   2 seconds ago    71.4MB

Succeeded in reducing the weight to 71.4MB! !! !! We have finally broken through the 100MB wall! !!


Quiet talk

I originally intended to run this application with mix run --no-halt, but when I use escript, it ends immediately. In such a case, if you write timer.sleep (: infinity), it will work all the time.

defmodule Anago.CLI do
  def main(_args) do
    :timer.sleep(:infinity)
  end
end

How much can you reduce?

erlang: I think that alpine will be included at least, so I'm wondering how much it will be reduced by apk add erlang to alpine. The vanilla alpine is only 5MB in size. Let's create a Dockerfile that installs only erlang in alpine.

FROM alpine:latest
RUN apk add --no-cache erlang

Building this Dockerfile will reduce the result to 63.6MB! (Erlang has a capacity of about 58MB, so no more diet is possible) If you reduce it to this point, you will be worried that it will work properly, but when you come to this point, the goal has shifted to how much you can reduce the size, so I will build it including the application.

REPOSITORY                                TAG             IMAGE ID       CREATED             SIZE
anago                                     custom_alpine   fb1de28697a1   9 minutes ago       65.6MB

I came up to 65.6MB: tada:

Afterword

By the way, I was able to execute this image properly: tada: (Wow ...) It seems that the limit of weight reduction is 63.6MB + escript because I think that the size limit of Erlang probably cannot reduce the weight any more.

Recommended Posts

The story of dieting the container of the Elixir application as much as possible
The story of updating SonarQube's Docker Container
[Docker] Start the container as soon as possible
Efforts to improve the development efficiency of legacy in-house Web systems as much as possible
[Java version] The story of serialization
Avoid Java Calendar as much as possible
The story of encountering Spring custom annotation
Introducing the features of JavaFX SceneBuilder container
The story of RxJava suffering from NoSuchElementException
The story of AppClip support in Anyca
The story of writing Java in Emacs
The story of making an Android application that can adjust the sampling frequency of the accelerometer