Distillery & Docker: How to correct setup release without ERTS and load docker image with Erlang only?

I’m following this excelent post to use Distillery & Docker without including ERTS.

My final docker app is failing to start with “init terminating in do_boot (cannot expand $ERTS_LIB_DIR in bootfile)”

Any extra considerations beyond the article? What is your build process? My ideia is to use Kubernetes also with multiple nodes.

Can you show your distillery config?

Here is my rel/config.exs:

["rel", "plugins", "*.exs"]
|> Path.join()
|> Path.wildcard()
|> Enum.map(&Code.eval_file(&1))

use Mix.Releases.Config,
    default_release: :default,
    default_environment: Mix.env()

environment :dev do
  set dev_mode: true
  set include_erts: false
  set cookie: :"`<7{Sl08LV`/K~n.!G%4<...oN^ek6!R0ms:"
end

environment :prod do
  set include_erts: false
  set include_src: false
  set cookie: :"s(w!_DbkZXEpTzC/~KeS...%`uF0o"
end

release :my_app do
  set version: current_version(:my_app)
  set applications: [
    :runtime_tools
  ]
end

And dockerfile?

Dockerfile.build

FROM bitwalker/alpine-elixir-phoenix:1.6.4

ENV MIX_ENV prod

# Add the files to the image
ADD . .

# Cache Elixir deps
RUN mix deps.get --only prod
RUN mix deps.compile

WORKDIR assets
# Cache Node deps
RUN npm i -g npm
RUN npm update
RUN node node_modules/brunch/bin/brunch build --production

# Compile JavaScript
RUN npm run deploy

WORKDIR ..
# Compile app
RUN mix compile
RUN mix phx.digest

# Generate release
ENTRYPOINT ["mix"]
CMD ["release", "--env=prod"]

Dockerfile.run

FROM bitwalker/alpine-erlang:20.3.4

# Set environment variables
ENV MIX_ENV=prod

# Copy tarball release
ADD myapp.tar.gz ./

# Set user
USER default

# Set entrypoint
ENTRYPOINT ["./bin/myapp"]

Docker-compose.yml

version: "3"
services:
  app:
    image: myapp-release
    container_name: myapp-app
    build:
      context: .
      dockerfile: Dockerfile.run
    command: foreground
    expose:
      - "4000"
    networks:
      - nginx-network
    ports:
      - "4000:4000"
networks:
  nginx-network:
    external: false

Hi, author of that guide here, glad you are using it!

Everything looks fine to me in the config files that you posted. One thing though, why did you comment out the ENTRYPOINT directive in your Dockerfile.run? I don’t see how it would work without it since it won’t know where the foreground command is supposed to go (from the compose file)

You don’t include ERTS in your Distillery release since you are using a Docker image with Erlang already installed which is fine. What version of Distillery are you using? According to this issue that error seems to have been resolved with the newer versions of Distillery. From that issue thread, I’d try upgrading Distillery. If not, try removing start_clean.boot in the folder in your Docker container after the archive is unpacked.

I’m using Distillery 1.5.2 with Elixir, Erlang and Phoenix on last versions.
The comment on ENTRYPOINT was a mistake when I pasted.

How does distillery works with libraries that download a local file, such phone_number and ua_inspector?

Usually you place those files in priv/ and then Distillery will package them up into your release. Then, in your code you can use priv_dir = Application.app_dir(:myapp, "priv").

1 Like

I forgot to add some parameters (server, root and version) listed in “Configuring your Release” at:
https://hexdocs.pm/distillery/use-with-phoenix.html#content

My final prod.exs:

config :myapp, MyAppWeb.Endpoint,
  load_from_system_env: true,
  url: [host: "myapp.io", port: 4000],
  cache_static_manifest: "priv/static/cache_manifest.json",
  server: true,
  root: ".",
  version: Application.spec(:myapp, :vsn)

Also had to change some libraries config to work with priv folder. (tzdata, ua_inspector and phone_number)

@jswny in my case I need to allow write permissions, so I changed dockerfile.run to do so.

# Set user
USER root

It only works with erts included.
If I try to execute a builded version without erts.

 ./docker_run.sh
{"init terminating in do_boot",{load_failed,[gen,gen_event,erl_lint,ets,lists,erl_parse,proc_lib,gen_server,erl_eval,supervisor,filename]}}
init terminating in do_boot ({load_failed,[gen,gen_event,erl_lint,ets,lists,erl_parse,proc_lib,gen_server,erl_eval,supervisor,filename]})

Crash dump is being written to: erl_crash.dump...done
2 Likes

Did you find the solution for “without erts”?

Not yet. Any thoughts?

@jswny I can confirm @pedromvieira`s experience. I must do:

environment :prod do
  set include_erts: true
  set include_src: false
  set cookie: :"oR@iY7<zAy@HoqBi~:6pI|Z]M,6Ou;:T>*AGN9vml(v`R/.EjP*oljK1t4iHKT)I"
end

To NOT get this issue(app name is figtree):

figtree-server | {"init terminating in do_boot",{load_failed,[erl_eval,erl_parse,erl_lint,supervisor,proc_lib,lists,gen_event,gen_server,gen,filename,ets]}}
figtree-server | init terminating in do_boot ({load_failed,[erl_eval,erl_parse,erl_lint,supervisor,proc_lib,lists,gen_event,gen_server,gen,filename,ets]})
figtree-server | 
figtree-server | Crash dump is being written to: erl_crash.dump...done
figtree-server exited with code 1

Dockerfile.build:
FROM bitwalker/alpine-elixir-phoenix:1.6.4

Dockerfile.run:
FROM bitwalker/alpine-erlang:20.3.2

If someone can show me how I can shell into the running docker image and print out the erl_crash.dump I will be glad to…

You can see the RUNNING source here:
https://bitbucket.org/sidha/figtree/pull-requests/1/skeleton-phx/diff
Michael

I’m not very sure if the container is still running after the BEAM has crashed :wink:

But you can use docker cp $container_name:/path/to/erl_crash.dump . to copy the dump even from stopped containers, unless you or docker disposed them already…

Thanks. No idea what the path to the crash dump is though…

“The system writes the crash dump in the current directory of the emulator or in the file pointed out by the environment variable (whatever that means on the current operating system) ERL_CRASH_DUMP. For a crash dump to be written, a writable file system must be mounted.”
From http://erlang.org/doc/apps/erts/crash_dump.html

1 Like

Thanks, I’ll look into it

@maz
The erl_crash.dump is located in your app/directory so you can copy it from there alternatively which is what I just did you can use docker export -o container.tar container_id to export the crashed container’s filesystem which you can then browse for your erl_crash.dmp

Super late to the party but hope it helps

That’s helpful for future reference, thanks.