'How can I use Erlang with Docker to run a Phoenix application?

I want to use a docker image in production to run a Phoenix container, However, since Elixir is just a layer on top of Erlang, it feels like it might be a waste of space to have Elixir running in my production environment.

Ideally, I would be able to compile an entire Phoenix application into Erlang, and then use an image from erlang:alpine to actually run the app in production. Something like this...

FROM elixir:alpine as builder
(install dependencies and copy files)
RUN mix compile_app_to_erlang

FROM erlang:alpine
COPY --from=builder /path/to/compiled/erlang /some/other/path
CMD ["erlang", "run"]

note: compile_app_to_erlang is not a real command, but I'm looking for something like it. Also, I have no idea how erlang runs, so all the code in there is completely made up.

Also, from what I know, there is a project called distillery that kind of does this, but this seems like the type of thing that shouldn't be too complicated (if I knew how erlang worked,) and I'd rather not rely on another dependency if I don't have too. Plus it looks like if you use distillery you also have to use custom made docker images to run the code which is something I try to avoid.

Is something like this even possible? If so, anyone know a DIY solution?



Solution 1:[1]

Elixir 1.9 added the concept of a "release" to Mix. (This was released about 11 months after the question was initially asked.) Running mix release will generate a tree containing the BEAM runtime, your compiled applications, and all of its dependencies. There is extensive documentation for the mix release task on hexdocs.pm.

In a Docker context, you can combine this with a multi-stage build to do exactly what you're requesting: start from the complete elixir image, create a tree containing the minimum required to run the image, and COPY it into a runtime image. I've been working with a Dockerfile like:

FROM elixir:1.13 AS build
WORKDIR /build
ENV MIX_ENV=prod

# Install two tools needed to build other dependencies.
RUN mix do local.hex --force, local.rebar --force

# Download dependencies.
COPY mix.exs mix.lock ./
RUN mix deps.get --only prod

# Compile dependencies (could depend on config/config.exs)
COPY config/ config/
RUN mix deps.compile

# Build the rest of the application.
COPY lib/ lib/
COPY priv/ priv/
RUN mix release --path /app

FROM ubuntu:20.04

# Get the OpenSSL runtime library
RUN apt-get update \
 && DEBIAN_FRONTEND=noninteractive \
    apt-get install --no-install-recommends --assume-yes \
      libssl1.1

# Get the compiled application.
COPY --from=build /app /app
ENV PATH=/app/bin:$PATH

# Set ordinary metadata to run the container.
EXPOSE 4000
CMD ["myapp", "start"]

If you're using Phoenix, the Phoenix documentation has a much longer example. On the one hand that covers some details like asset compilation; on the other, its runtime image seems to have a bit more in it than may be necessary. That page also has some useful discussion on running Ecto migrations; with the Elixir fragment described there you could docker run a temporary container to do migrations, run them in an entrypoint wrapper script, or use any other ordinary Docker technique.

Solution 2:[2]

I suggest you to use distillery to build a binary.

Then just run a alpine container, mount the distillery release to it, run the binary. Yeah, you can eve use supervisor to run it.

You can use remote_console of distillery to link to the console of this binary.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 David Maze
Solution 2