Skip to content

Instantly share code, notes, and snippets.

@ellispritchard
Last active July 3, 2017 11:27
Show Gist options
  • Save ellispritchard/fcd15c97be4f83ec66e7d74c6a8793e3 to your computer and use it in GitHub Desktop.
Save ellispritchard/fcd15c97be4f83ec66e7d74c6a8793e3 to your computer and use it in GitHub Desktop.
Mix task to build docker image, and supporting files
defmodule Mix.Tasks.Docker.Build do
use Mix.Task
@shortdoc "Prepare a docker release image"
@moduledoc """
Runs system-independent tasks, such as npm install, and then builds a tagged docker image.
## Configuration
Add the `docker` configuration to your Mix.Project to specify the target `repo`; the
image will be tagged with the repo and `Mix.Project.version`:
```
def project do
[app: :myapp,
version: "0.0.1",
docker: [repo: "nexus.in.ft.com:5000/group/myapp"],
...]
end
```
This would create an image tagged with `nexus.in.ft.com:5000/group/myapp:0.0.1`
You can override the repo using the `--repo` parameter, or the the whole image tag using `--tag`
parameter, or just the version using `--version`, e.g.
```
mix docker.build --tag nexus.in.ft.com:5000/group/myapp:1.0.1
# is same as
mix docker.build --repo nexus.in.ft.com:5000/group/myapp --version 1.0.1
```
> Note that `--version` does not set the `Mix.Project.version`, just the docker tag.
"""
@switches [tag: :string, repo: :string, version: :string]
def run(args) do
config = Mix.Project.config[:docker] || []
{opts, _argv} = OptionParser.parse!(args, strict: @switches)
version = opts[:version] || Mix.Project.config[:version]
tag = opts[:tag]
repo = config[:repo] || opts[:repo] || if(not tag, do: Mix.raise("Need project docker repo config, or --repo or --tag option"))
tag = case tag do
nil -> "#{repo}:#{version}"
tag -> tag
end
Mix.shell.info("Building image tagged with #{tag} version #{version}")
with pwd <- System.cwd!(),
release_dir <- "#{pwd}/.docker.build",
{:ok, _} <- File.rm_rf(release_dir),
:ok <- File.mkdir_p(release_dir),
:ok <- Mix.Task.run("deps.get"),
0 <- Mix.shell.cmd("cd assets && npm install && ./node_modules/.bin/brunch build --production"),
0 <- Mix.shell.cmd("mix phx.digest"),
{uid, 0} <- System.cmd("id", ["-u"]),
{gid, 0} <- System.cmd("id", ["-g"]),
chown <- String.trim(uid) <> ":" <> String.trim(gid),
0 <- Mix.shell.cmd("docker run --rm -v #{pwd}:/project -v #{release_dir}:/release --env CHOWN=#{chown} --env MIX_ENV=docker --env APP_VERSION=#{version} nexus.in.ft.com:5000/membership/elixir-build:latest /project/rel/docker-build.sh"),
:ok <- File.cp("rel/Dockerfile", "#{release_dir}/Dockerfile"),
0 <- Mix.shell.cmd("cd #{release_dir} && docker build . -t #{tag}") do
Mix.shell.info("Built docker image #{tag}.")
else
code -> Mix.shell.error("Build failed: #{inspect code}")
end
end
end
environment :docker do
set dev_mode: false
set include_erts: true
set include_src: false
set cookie: :crypto.hash(:sha256, :crypto.rand_bytes(25)) |> Base.encode16 |> String.to_atom
set output_dir: "/release"
end
#!/bin/ash
# Used to do build inside Docker build container
cd /project
mix release --env=${MIX_ENV}
if [ "${CHOWN}" ] ; then
# fix for CI where Docker runs as root
echo "chown-ing /project/_build/${MIX_ENV}"
chown -R ${CHOWN} /project/_build/${MIX_ENV}
echo "chown-ing /release/*"
chown -R ${CHOWN} /release/*
fi
du -hs /release
FROM alpine:3.6
RUN apk --no-cache update && apk --no-cache upgrade && apk --no-cache add ncurses-libs
ENV REFRESHED_AT=2017-05-30
RUN adduser -D app
WORKDIR /opt/app
COPY . .
USER app
# # Runtime Environment
RUN mkdir /tmp/myapp
ENV RELEASE_MUTABLE_DIR /tmp/myapp
ENV START_ERL_DATA /tmp/myapp/start_erl.data
CMD ["/opt/app/bin/myapp", "foreground"]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment