Current section
Files
Jump to
Current section
Files
docker/start_container
#!/usr/bin/env bash
# This script starts an erlang or elixir edeliver release as docker container
# and can be used as template for custom release start scripts.
# It allows to start a release in the foreground or as daemon and
# to connect to it via the remote console.
# It requires an /$APP/bin/$APP-<version> relx extended start script to
# exist in the image. This file is extracted by edeliver to
# $DELIVER_TO/$APP/bin/$APP and the string {{edeliver-version}} is replaced
# with the installed version.
set -e -o pipefail
INTERNAL_INTERFACE="${INTERNAL_INTERFACE:-127.0.0.1}"
HOST_NAME="${HOST_NAME:-$(hostname -f)}"
ERL_DIST_PORT="${ERL_DIST_PORT:-4321}"
ERLANG_COOKIE_FILE="${ERLANG_COOKIE_FILE:-"$HOME/.erlang.cookie"}"
DEFAULT_ERLANG_COOKIE_TARGET="/root/.erlang.cookie"
INET_DIST_USE_INTERFACE="${INET_DIST_USE_INTERFACE:-"{$(echo "$INTERNAL_INTERFACE" | tr "." ",")}"}"
EPMD_MODULE="${EPMD_MODULE:-edeliver_epmd}"
ERL_ZFLAGS="${ERL_ZFLAGS:-"-start_epmd false -epmd_module ${EPMD_MODULE} ${ADDITIONAL_ERL_ZFLAGS}"}"
BASE_DIR=$( cd "${0%/*}/.." && pwd -P )
REPLACE_OS_VARS=${REPLACE_OS_VARS:-true}
_info() {
echo $@
}
_error() {
echo "" >&2
echo $@ >&2
echo "" >&2
exit 1
}
DOCKER_RUN_IMAGE="${DOCKER_RUN_IMAGE:-"{{edeliver-docker-image}}"}"
if [[ "$DOCKER_RUN_IMAGE" == *edeliver-docker-image* ]]; then
_error -e "DOCKER_RUN_IMAGE must be set.\nUsually the image name in the docker registry set as docker RELEASE_STORE."
fi
APP="${APP:-"{{edeliver-app}}"}"
if [[ "$APP" == *edeliver-app* ]]; then
_error -e "APP must be set.\nUsually the same name as set in the edeliver config."
fi
CONTAINER_NAME="${CONTAINER_NAME:-$APP}"
COMMAND="$1"
PIPE_DIR="${BASE_DIR}/pipes/${APP}"
mkdir -p "$PIPE_DIR"
if ! [ -f "$ERLANG_COOKIE_FILE" ]; then
echo
echo " Warning: No cookie file found: $ERLANG_COOKIE_FILE"
echo " Creating random cookie file at $ERLANG_COOKIE_FILE"
echo " Please regenerate it or set \$ERLANG_COOKIE_FILE"
echo " to a custom cookie file."
( # run in subshell to disable pipefail only temporarily
set +o pipefail # disable pipefail because head command closes pipe
LC_ALL=C tr -dc A-Za-z0-9 </dev/random 2>/dev/null | head -c 30 > "$ERLANG_COOKIE_FILE" || {
echo
echo " Warning: Not enough entropy to generate secure random cookie."
echo " Using /dev/urandom instead. Please recreate cookie."
echo
LC_ALL=C tr -dc A-Za-z0-9 </dev/urandom 2>/dev/null | head -c 30 > "$ERLANG_COOKIE_FILE"
}
)
echo '' >> "$ERLANG_COOKIE_FILE"
chmod 400 "$ERLANG_COOKIE_FILE"
echo
fi
RELEASE_CMD="${RELEASE_CMD:-"{{edeliver-release-command}}"}"
if [[ "$DOCKER_OPTS" == *edeliver-release-command* ]]; then
RELEASE_CMD="distillery" # | mix | rebar3
fi
if [ "$RELEASE_CMD" = "mix" ]; then
DEFAULT_ERLANG_COOKIE_TARGET="/${APP}/releases/COOKIE"
fi
ARGS=()
for ARG in $@; do
case "$ARG" in
start|daemon)
if [ "$RELEASE_CMD" = "mix" ]; then
ARGS+=("start")
else
ARGS+=("console")
fi
;;
--debug)
DEBUG=1
DEBUG_BOOT=1
;;
*)
ARGS+=("$ARG")
;;
esac
done
TAG="${TAG:-"{{edeliver-version}}"}"
if [[ "$TAG" == *edeliver-version* ]]; then
# if not passed and not replaced by edeliver, use latest:
TAG="$(docker image ls --format "{{.Tag}}" "${DOCKER_RUN_IMAGE}" | head -n1)"
if [ -n "$TAG" ]; then
_info "Using latest tag $TAG of image ${DOCKER_RUN_IMAGE}."
_info "If multiple tags are available, you can set the TAG environment variable."
else
_error -e "Image $DOCKER_RUN_IMAGE not found.\nPlease pull that image."
fi
else
_info "Using tag $TAG of image $DOCKER_RUN_IMAGE"
fi
DOCKER_OPTS="${DOCKER_OPTS:-"{{edeliver-docker-opts}}"}"
if [[ "$DOCKER_OPTS" == *edeliver-docker-opts* ]]; then
DOCKER_OPTS=""
fi
case "$COMMAND" in
console|start|foreground|daemon|daemon_iex|exec|start_iex)
DOCKER_OPTS+=" --env INET_DIST_USE_INTERFACE='{0,0,0,0}'" # bind to all interfaces in container to allow port mapping
DOCKER_OPTS+=" --hostname ${HOST_NAME}" # required for long node names
DOCKER_OPTS+=" --env ERL_DIST_PORT=$ERL_DIST_PORT" # pin port for distribution
DOCKER_OPTS+=" --publish ${INTERNAL_INTERFACE}:${ERL_DIST_PORT}:${ERL_DIST_PORT}" # forward distribution port to allow connections from other nodes or remote_console
DOCKER_OPTS+=" --name $CONTAINER_NAME"
EXISTING_CONTAINER="$(docker ps -aq --filter name="${CONTAINER_NAME}")"
echo " Using hostname $HOST_NAME"
if [ "$COMMAND" != "exec" ] && [ -n "$EXISTING_CONTAINER" ]; then
echo " Removing old container $CONTAINER_NAME ($EXISTING_CONTAINER)"
docker rm "$EXISTING_CONTAINER" >/dev/null 2>&1 || {
echo
echo "Failed to remove existing container ${EXISTING_CONTAINER}!" >&2
echo "Container seems to be running." >&2
echo >&2
exit 1
}
else
echo " Using container name $CONTAINER_NAME"
fi
;;
*) # e.g. remote | remote_console | ping
DOCKER_OPTS=" --network host"
if [ "$RELEASE_CMD" = "rebar3" ]; then
# extended start script from rebar uses this to connecto to the
# remote node for ping or remote_console command
DOCKER_OPTS+=" --env ERL_DIST_PORT=${ERL_DIST_PORT}"
else
# use random port to bind remote_console node to for distillery
# and mix releases
DOCKER_OPTS+=" --env ERL_DIST_PORT=0"
fi
DOCKER_OPTS+=" --env DEFAULT_DIST_PORT=${ERL_DIST_PORT}" # port to connect remote console to and default ports for all nodes
DOCKER_OPTS+=" --add-host ${HOST_NAME}:${INTERNAL_INTERFACE}"
DOCKER_OPTS+=" --env INET_DIST_USE_INTERFACE={$(echo "$INTERNAL_INTERFACE" | tr "." ",")}"
;;
esac
# distillery specific, mainly to set the host name of the long
# node name
DOCKER_OPTS+=" --env HOST_NAME=${HOST_NAME}"
DOCKER_OPTS+=" --env REPLACE_OS_VARS=${REPLACE_OS_VARS}"
# mix specific, mainly to set the host name of the long
# node name
RELEASE_DISTRIBUTION="${RELEASE_DISTRIBUTION:-name}"
RELEASE_NODE="${RELEASE_NODE:-${APP}@${HOST_NAME}}"
DOCKER_OPTS+=" --env RELEASE_DISTRIBUTION=${RELEASE_DISTRIBUTION}"
DOCKER_OPTS+=" --env RELEASE_NODE=${RELEASE_NODE}"
if [ "$COMMAND" = "start" ] || [ "$COMMAND" = "daemon" ]; then
DOCKER_OPTS+=" --detach --tty"
elif [ -t 0 ]; then # terminal attatched
DOCKER_OPTS+=" --tty --interactive"
fi
ERLANG_COOKIE_TARGET="${ERLANG_COOKIE_TARGET:-$DEFAULT_ERLANG_COOKIE_TARGET}"
if [ "$COMMAND" = "exec" ]; then
docker exec -ti $CONTAINER_NAME bash
else
docker run \
$DOCKER_OPTS \
--workdir "/${APP}" \
--env ERL_ZFLAGS="${ERL_ZFLAGS}" \
--env DEBUG_BOOT="${DEBUG_BOOT}" \
--env PIPE_DIR=/${APP}/pipes \
--mount type=bind,source="${ERLANG_COOKIE_FILE},target=${ERLANG_COOKIE_TARGET}" \
--mount type=bind,source="${PIPE_DIR}",target=/${APP}/pipes \
"${DOCKER_RUN_IMAGE}:${TAG}" bin/${APP} ${ARGS[@]}
fi