Here is my Dockerfile
# elixir builder
FROM elixir:1.7-alpine as elixir-builder
ENV HOME=/opt/app
WORKDIR $HOME
RUN mix do local.hex --force, local.rebar --force
COPY config/ $HOME/config/
COPY mix.exs mix.lock $HOME/
RUN mix deps.get
# node builder
FROM node:11-alpine as asset-builder
ENV HOME=/opt/app
WORKDIR $HOME
COPY --from=elixir-builder $HOME/deps $HOME/deps
WORKDIR $HOME/assets
COPY assets/ $HOME/assets/
RUN npm install && npm run deploy
# app release
FROM alpine:3.8 as builder
ENV MIX_ENV=prod HOME=/opt/app
WORKDIR $HOME
RUN sed -i -e 's/v[[:digit:]]\.[[:digit:]]/edge/g' /etc/apk/repositories &&\
apk update &&\
apk upgrade &&\
apk add\
build-base\
elixir\
erlang-xmerl\
erlang-runtime-tools
COPY config $HOME/config/
COPY rel $HOME/rel/
COPY mix.exs mix.lock $HOME/
COPY --from=asset-builder $HOME/priv/static $HOME/priv/static
RUN mix local.hex --force &&\
mix local.rebar --force &&\
mix deps.get &&\
mix deps.compile &&\
mix phx.digest &&\
mix release --verbose
# release image
FROM alpine:3.8
LABEL maintainer="shadowlegend@focuzsolution.com"
ENV LANG=en_US.UTF-8\
HOME=/opt/app\
MIX_ENV=prod\
REPLACE_OS_VARS=true\
LD_LIBRARY_PATH=/usr/local/lib:/usr/local/lib64\
SHELL=/bin/bash
WORKDIR $HOME
RUN sed -i -e 's/v[[:digit:]]\.[[:digit:]]/edge/g' /etc/apk/repositories &&\
apk update && apk upgrade --no-cache &&\
apk --no-cache add openssl bash &&\
addgroup genx &&\
adduser -D -G genx xuser &&\
chown -R xuser:genx $HOME
COPY --from=builder $HOME/_build/prod/rel/app_server/releases/**/*.tar.gz $HOME
RUN tar -xzf app_server.tar.gz; rm app_server.tar.gz; ls bin/;
USER xuser
ENTRYPOINT ["bin/app_server"]
CMD ["foreground"]
The mix.exs
defmodule AppServer.MixProject do
use Mix.Project
def project do
[
app: :app_server,
version: "1.0.5",
elixir: "~> 1.7",
elixirc_paths: elixirc_paths(Mix.env()),
compilers: [:phoenix, :gettext] ++ Mix.compilers(),
start_permanent: Mix.env() == :prod,
test_coverage: [tool: ExCoveralls],
preferred_cli_env: [coveralls: :test, "coveralls.detail": :test, "coveralls.post": :test, "coveralls.html": :test],
aliases: aliases(),
deps: deps()
]
end
# Configuration for the OTP application.
#
# Type `mix help compile.app` for more information.
def application do
[
mod: {AppServer.Application, []},
extra_applications: [:logger, :runtime_tools]
]
end
# Specifies which paths to compile per environment.
defp elixirc_paths(:test), do: ["lib", "test/support"]
defp elixirc_paths(_), do: ["lib"]
# Specifies your project dependencies.
#
# Type `mix help deps` for examples and options.
defp deps do
[
{:phoenix, "~> 1.4.0"},
{:phoenix_pubsub, "~> 1.1"},
{:phoenix_ecto, "~> 4.0"},
{:ecto_sql, "~> 3.0"},
{:postgrex, ">= 0.0.0"},
{:phoenix_html, "~> 2.11"},
{:phoenix_live_reload, "~> 1.2", only: :dev},
{:gettext, "~> 0.11"},
{:jason, "~> 1.0"},
{:plug_cowboy, "~> 2.0"},
{:absinthe, "~> 1.4.0"},
{:absinthe_plug, "~> 1.4.0"},
{:absinthe_phoenix, "~> 1.4.0"},
{:absinthe_ecto, ">= 0.0.0"},
{:excoveralls, "~> 0.10", only: :test},
{:dataloader, "~> 1.0.0"},
{:guardian, "~> 1.0"},
{:distillery, "~> 2.0", runtime: false},
{:faker, "~> 0.10", only: [:test, :dev]},
{:hackney, "~> 1.14.0"},
{:sweet_xml, "~> 0.6"},
{:comeonin, "~> 4.0"},
{:argon2_elixir, "~> 1.3"},
{:cors_plug, "~> 2.0"},
{:phoenix_pubsub_redis, "~> 2.1.0"},
{:bamboo, "~> 1.1"},
{:bamboo_smtp, "~> 1.6.0"}
]
end
# Aliases are shortcuts or tasks specific to the current project.
# For example, to create, migrate and run the seeds file at once:
#
# $ mix ecto.setup
#
# See the documentation for `Mix` for more info on aliases.
defp aliases do
[
"ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
"ecto.reset": ["ecto.drop", "ecto.setup"],
test: ["ecto.reset", "coveralls"]
]
end
end
The AppServer.Application
inside lib/app_server/application.ex
defmodule AppServer.Application do
# See https://hexdocs.pm/elixir/Application.html
# for more information on OTP Applications
@moduledoc false
use Application
def start(_type, _args) do
import Supervisor.Spec
# Define workers and child supervisors to be supervised
children = [
# Start the Ecto repository
AppServer.Repo,
# Start the endpoint when the application starts
AppServerWeb.Endpoint,
supervisor(Absinthe.Subscription, [AppServerWeb.Endpoint])
# Start your own worker by calling: AppServer.Worker.start_link(arg1, arg2, arg3)
# worker(AppServer.Worker, [arg1, arg2, arg3]),
]
# See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: AppServer.Supervisor]
Supervisor.start_link(children, opts)
end
# Tell Phoenix to update the endpoint configuration
# whenever the application is updated.
def config_change(changed, _new, removed) do
AppServerWeb.Endpoint.config_change(changed, removed)
:ok
end
end
I could successfully follow the step I did inside Dockerfile
(like MIX_ENV=prod mix deps.get ... mix release
and release-build the app outside of Docker
and run it without any problem. The image also successfully built but when I try running it with docker container run app_server:latest
or with docker-compose up -d
with docker-compose.yml
, this is what I get
03:16:05.974 [info] Application app_server exited: exited in: AppServer.Application.start(:normal, [])
** (EXIT) an exception was raised:
** (UndefinedFunctionError) function AppServer.Application.start/2 is undefined (module AppServer.Application is not available)
AppServer.Application.start(:normal, [])
(kernel) application_master.erl:277: :application_master.start_it_old/4
{"Kernel pid terminated",application_controller,"{application_start_failure,app_server,{bad_return,{{'Elixir.AppServer.Application',start,[normal,[]]},{'EXIT',{undef,[{'Elixir.AppServer.Application',start,[normal,[]],[]},{application_master,start_it_old,4,[{file,\"application_master.erl\"},{line,277}]}]}}}}}"}
Kernel pid terminated (application_controller) ({application_start_failure,app_server,{bad_return,{{'Elixir.AppServer.Application',start,[normal,[]]},{'EXIT',{undef,[{'Elixir.AppServer.Application',st
Crash dump is being written to: erl_crash.dump...done