In distillery 2 release, module MyApp.Application is not available

docker
distillery

#1

I have a project I want to deploy a release build for, and so I’ve written a docker file that I believe should work, but when I start my app I get the below error. The frustrating part of this is that I feel like I’ve had this error before and after a lot of time I solved it… somehow. But I don’t remember how.

(UndefinedFunctionError) function MyApp.Application.start/2 is undefined (module MyApp.Application is not available)

So everything works in dev mode with iex -S mix. Application starts and everything runs just fine. The module does exist. The docker build does work, and the release files are all generated, so far as I can tell. But something is missing somewhere.

Here’s the docker file

FROM elixir:1.7.4 as release

RUN mkdir /app
WORKDIR /app

RUN mix local.hex --force && \
    mix local.rebar --force

ENV MIX_ENV=prod

COPY mix.exs mix.lock ./
COPY config ./config

RUN mix deps.get
RUN mix deps.compile

COPY rel rel/
RUN mix release --no-tar --verbose --env=prod

FROM erlang:21 as prod

RUN mkdir /app  && chown -R nobody: /app
WORKDIR /app
USER nobody
ENV HOME=/app

COPY --from=release /app/_build/prod/rel/myapp ./

And my mix.exs

defmodule MyApp.Mixfile do
  use Mix.Project

  def project do
    [app: :myapp,
     version: "0.2.0",
     elixir: "~> 1.7.4",
     elixirc_paths: elixirc_paths(Mix.env),
     compilers: [:phoenix, :gettext] ++ Mix.compilers,
     build_embedded: Mix.env == :prod,
     start_permanent: Mix.env == :prod,
     deps: deps()]
  end

  # Specifies which paths to compile per environment.
  defp elixirc_paths(:test), do: ["lib", "test/support"]
  defp elixirc_paths(_),     do: ["lib"]

  def application do
    [
      extra_applications: [:logger],
      mod: {MyApp.Application, []},
    ]
  end

  defp deps do
    [
      {:httpotion, "~> 3.0"},
      {:distillery, "~> 2.0"}
    ] 
  end
end

And my rel/config.exs is just the standard thing that mix release.init tosses out. I can’t find anything in it to change that seems to help.

I’ve tried adding :myapp to various of :applications, :included_applications, and such but it causes an infinite loop when I try to run it with iex -S mix or causes the release to get into an infinite loop.

Has anyone seen this before? I feel like I’ve spent a lot of time on this with no progress.


#2

You never add actual code into the release stage, you only copy mix.{exs,lock}, config/ and rel/ into the container, but never lib/ or anything else, so of course, stuff from inside of it, can’t be part of your release and the boot will fail.


#3

Once I added a COPY lib lib/, then everything worked just fine. It seems so obvious in hindsight but I couldn’t see it.

Thank you!


#4

Yes, I had to stare on the dockerfile quite a while and have read it a couple of times until I have seen what happened.

Also I prefer to simply use COPY . . (having _build and deps in a .dockerignore), that way I do not forget to copy anything, and since I only rebuilt docker for actual releases or because I want to update the base layer, the dep-cache-layer you try to create wouldn’t help me either. Also building them happens in CI, where that dep-layer wouldn’t even be available anymore.


#5

I originally had a COPY . ., but I took it out in favor of explicitly copying only what was needed. I didn’t want things like large amounts test data copied into the build. I also wanted to speed up the build by preventing relying on anything that might cause the docker cache to be invalidated, causing rebuilds (although after messing with it a bit I’m no longer sure docker is actually that smart).

But after having wasted a few hours I would have been better off leaving it in and just .dockerignoring stuff.


#6

Its safer to ignore stuff you know you wont need via .dockerignore than trying to manually put things you need into the container.

But yes, if build times and cached layers are important for you, than COPY selectively is probably the way to go, but as I said, projects I usually work on do not have that requirement, therefore we put everything into the building stage.