# DockerDistiller
This package uses Distillery and Docker to create containerized
builds of Elixir apps that are small and nimble.
To that end, it uses Alpine Linux as a base container OS and
[Marlus Saraiva's Great Work](https://github.com/msaraiva/alpine-erlang) on
keeping current versions of Erlang and Elixir for Alpine/Docker.
Elixir apps expect `Application.get_env` to spit out values which by default
come from `config/*.exs` and are environment-specific. Applications like
`Logger` may want to even change their compile-time (macro) behavior based
on the settings there and the current configuration set in effect (usually
based on Mix.env). Distillery builds for one of these environments but a
typical use of Docker is that you build one container for all environments.
There are solutions to this but they all revolve around extra libraries and/or
code to be written over the relatively clean and straightforward Mix configuration.
We shove the problem under the rug by building for all environments and dropping
all distributions in a single container. This way, we can have our cake and it
it, too. We need some conventions to be setup for this to work:
* The application version must include the Mix environment; this way we build
different packages per environment which we can still install in one distribution
tree;
* We expect a 1:1 correspondence between Mix and Distillery environments.
The Installation/Use section has specific instructions on how to set things up.
## Installation/Use
If [available in Hex](https://hex.pm/docs/publish), the package can be installed as:
1. Add `docker_distiller` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[{:docker_distiller, "~> 0.1.0"}]
end
```
1. Run `mix release.init` to get a Distillery config setup. See the [Distillery
Docs](https://hexdocs.pm/distillery/getting-started.html) for details.
Some changes in `rel/config.exs` you need to make:
1. Set `default_environment` to `Mix.env`. You usually want this anyway.
1. Include an `environment` stanza for every Mix environment you want a build for. We
read the list of environments to decide what to build.
1. Add a target `docker_repo` setting to your application in `mix.exs`. Also, your version
MUST become Mix environment specific so we can build parallel releases. Change
the `version` setting in `mix.exs` to something like `"0.1.0-#{Mix.env}"`. The dash
and Mix environment are mandatory. You will end up with something like:
```elixir
def project do
[app: :my_app,
version: "0.1.0-#{Mix.env}",
docker_repo: "quay.io/my_quay_repo",
...
]
end
```
1. (Optionally) override the build image. By default, we will use the `msaraiva/elixir-dev`
images for the correct elixir version, but you can override this in the project
settings (in case you use a newer Elixir than published, or you can't use public
Docker images for security reasons, etcetera):
```elixir
def project do
[...
build_image: "super.repo.io/myrepo/elixir-dev:1.2.3"
...
]
end
```
1. Add a template Dockerfile called `Dockerfile.deploy.eex`. This template will get
stuff injected from the build and then written as `Dockerfile`, overwriting
existing things. The following is sent to the template:
* `add_dist_tars`: Put an expansion for this in the Dockerfile at the location where
you want the `ADD (tarball) (appname)` lines to land
* `cmd_for_mix_env`: The command to run given an environment of MIX_ENV
An example minimal template:
```
FROM msaraiva/erlang:18.3
# Default version to run. We use MIX_ENV to be compatible
ENV MIX_ENV=prod
<%= add_dist_tars %>
CMD <%= cmd_for_mix_env %> foreground
```
Note that even though the distribution tar contains Erlang, there are packages that
Erlang needs (like the libncurses) so building from a basic Alpine image will most
likely not work.
1. Run `mix publish` to build a release and publish it. You can now run your container
in different environments by just setting `MIX_ENV`.