Easy deployment for Elixir apps


Keywords
deployment, distillery, elixir, systemd
License
MIT

Documentation

Deli

Build Status Hex.pm Hexdocs.pm

Provides a simple deployment solution for Elixir applications, using a couple of edeliver tasks under the hood.

Releases are built with distillery, locally, through docker.

Git tags are autogenerated, and deli will ask you to bump app version, if there are new commits since last version bump.

You can configure a different release / versioning strategy if you want to.

Getting Started

Minimal versions required: Elixir 1.8+, OTP 20+.

Add deli to your deps:

def deps do
  [
    # ...
    {:deli, "~> 0.2.0-rc.6", runtime: false}
  ]
end

You don't need to add edeliver or distillery, as they're already included.

No need for edeliver config (for basic scenario). You will need to setup distillery.

Then add some configuration in your config/config.exs:

config :deli,
  hosts: [
    staging: [
      "staging-01.your_app.com",
      "staging-02.your_app.com"
    ],
    prod: [
      "prod-01.your_app.com",
      "prod-02.your_app.com",
      "prod-03.your_app.com",
      "prod-04.your_app.com",
      "prod-05.your_app.com",
      "prod-06.your_app.com"
    ]
  ]

See lib/deli/config.ex for all configuration options (and their defaults).

Main task

$ mix deli

The task above does full cycle deploy:

  • Creates a local docker build env
  • Builds a new release in this env
  • Deploys release to target env hosts
  • Restarts each app in target hosts, and checks their status

It will assume staging environment by default.

To target prod environment, do:

$ mix deli -t prod

It will ask for confirmation after release is built, before deploy. If you don't want that extra step, pass -y when calling this task.

Other tasks provided by deli

# Starts all hosts for target prod
$ mix deli.start -t prod

# Stops all hosts for target prod, without confirmation
$ mix deli.stop -t prod -y

# Restarts all hosts for target staging that match ~r/02/
$ mix deli.restart -h 02

# Cleans autogenerated config files
$ mix deli.clean

# Does only release build phase
$ mix deli.release

# Does only deploy/restart/check (release should be available)
$ mix deli.deploy

# Checks status in all prod hosts
$ mix deli.status -t prod

# Pings all staging hosts (bin ping)
$ mix deli.ping

# Checks version in all staging hosts that match ~r/01/
$ mix deli.version -h 01

# Checks local version
$ mix deli.version -t dev

# Compares local version with all prod hosts
$ mix deli.version -c -t prod

# Opens a IEx remote console from local machine
$ eval $(mix deli.shell)

# Opens a local observer connected to remote node
$ eval $(mix deli.shell -o)

# When there are more than one host for target,
# eval will just work if you filter it to just return one host
$ eval $(mix deli.shell -h 01)

# If you don't know yet how to filter, use it without eval,
# and it will do the port forwarding and print out the command to connect,
# but before, it lets you choose the host:
$ mix deli.shell
[0] staging-01.myapp.com
[1] staging-02.myapp.com
Choose a number: 0
iex --name local@127.0.0.1 --cookie awesome_cookie --remsh myapp@127.0.0.1

Run mix tasks (locally) in remote nodes

By using Deli.Command, you can have this:

# runs locally
$ mix my_app.xyz --arg_example=1

# runs in all prod hosts
$ mix my_app.xyz --arg_example=1 -t prod

# runs in prod hosts matching ~r/03/
$ mix my_app.xyz --arg_example=1 -t prod -h 03

Edeliver and Distillery

Releases should be configured in your application with distillery.

You don't need to think about edeliver with deli (unless you want to). Deli generates any config needed for edeliver, and adds them to your gitignore by default. If you want to maintain a custom version, just remove from .gitignore, and also remove the comment line saying autogenerated by deli. Edeliver mix commands are available to you. But if you use the systemctl controller, avoid using edeliver admin commands (start / stop / ...)

At the moment, this package exists for reusing among similarly configured apps. It might not be flexible enough for your needs yet.

Advanced configuration

Checkout Release configuration for more options.

Potential future work

  • Add more documentation
  • Remove edeliver dependency, replacing its steps by local code
  • See about using elixir releases instead of distillery (1.9+)
  • Concurrent restarts / checks / ...
  • Retry / rollback strategy
  • Upgrades
  • Accept host labels in config, and filter by it in commands
  • Associate labels with different build targets (first label wins)
  • Configure a custom deploy strategy
  • Plugin behaviour to allow custom hooks and integrations (e.g. slack notification)
  • Use github releases as release store
  • Allow production to be default target if there's no staging
  • mix deli.eval
  • Allow developers to create custom docker build hooks, by checking paths: .deli/docker_build_hooks/{(before_|after)(build|setup|setup_(otp|elixir|rebar3))}/script.(sh|exs)
  • Allow to change target path to something else than /opt/APP
  • Autogenerated files with version and checksums for expiration
  • Integrate with a terminal-based observer
  • Do rebar3 checksum in docker build images
  • Allow to specify hex version in docker build images
  • Configure a remote docker host
  • Provide common release service assets (nginx, systemd, logrotate etc)
  • Provision/setup new target hosts (with hooks for custom setup)
  • Use s3 as release store
  • Use a scp release store (allows CI to build releases for all build targets upfront)