README.md

# 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`.