Managing Docker Compose with OpenRC
Gentoo OpenRC Docker DockerCompose — Published on .
On one of my machines, I host a couple services using docker-compose
. I
wanted to start/restart/stop these using the default init/service manager used
on the machine, openrc
. This would allow them to start/stop automatically
with Docker (which coincides with the machine powering on or off,
respectively).
I’ve set this up through a single docker-compose
meta-service. To add new
docker-compose
projects to be managed, all I need to do for openrc
configuration is creating a symlink, and configure the path to the
docker-compose.yaml
file.
The meta-service lives at /etc/init.d/docker-compose
, just like all other
services managed by openrc
. This file is quite straightforward. To start off,
a number of variables are set and exported.
name="$RC_SVCNAME"
description="OpenRC script for managing the $name docker-compose project"
# Set default values
DOCKER_COMPOSE="${DOCKER_COMPOSE:-docker-compose} $DOCKER_COMPOSE_ARGS"
COMPOSE_PROJECT_NAME="${COMPOSE_PROJECT_NAME:-$name}"
# Export all variables used by docker-compose CLI
export COMPOSE_PROJECT_NAME
export COMPOSE_FILE
export COMPOSE_PROFILES
export COMPOSE_API_VERSION
export DOCKER_HOST
export DOCKER_TLS_VERIFY
export DOCKER_CERT_PATH
export COMPOSE_HTTP_TIMEOUT
export COMPOSE_TLS_VERSION
export COMPOSE_CONVERT_WINDOWS_PATHS
export COMPOSE_PATH_SEPARATOR
export COMPOSE_FORCE_WINDOWS_HOST
export COMPOSE_IGNORE_ORPHANS
export COMPOSE_PARALLEL_LIMIT
export COMPOSE_INTERACTIVE_NO_CLI
export COMPOSE_DOCKER_CLI_BUILD
One of the services I use is also configured with its own external
network. I
want it to be created if it doesn’t exist, to ensure that the service can start
up properly. I do not want it to be removed, so I left that out.
# Set up (external) networks
for name in "${DOCKER_NETWORKS[@]}"
do
# Create the network if needed
if ! docker network ls | awk '{ print $2 }' | grep -q "$name"
then
einfo "Creating docker network '$name'"
docker network create "$name" > /dev/null
fi
# Expose some variables for the networks
network_id="DOCKER_NETWORK_${name}_ID"
declare -gx DOCKER_NETWORK_${name}_ID="$(docker network ls | awk '$2 == "'"$name"'" { print $1 }')"
declare -gx DOCKER_NETWORK_${name}_GATEWAY="$(docker network inspect "${!network_id}" | jq -r '.[0].IPAM.Config[0].Gateway')"
unset network_id
done
And lastly, there’s the four simple functions to declare dependencies,
configure how to start
or stop
, and how to get the status
of the service.
depend() {
need docker
}
start() {
$DOCKER_COMPOSE --project-directory "$COMPOSE_PROJECT_DIRECTORY" up -d
}
status() {
$DOCKER_COMPOSE --project-directory "$COMPOSE_PROJECT_DIRECTORY" ps
}
stop() {
$DOCKER_COMPOSE --project-directory "$COMPOSE_PROJECT_DIRECTORY" down
}
Now, to actually create a service file to manage a docker-compose
project, a
symlink must be made. I’ll take my
botamusique
service as an example.
ln -s /etc/init.d/docker-compose /etc/init.d/botamusique
This service can’t start just yet, as there’s no $COMPOSE_PROJECT_DIRECTORY
configured for it yet. For this, a similarly named file should be made in
/etc/conf.d
. In here, any variable used by the service can be configured.
$EDITOR /etc/conf.d/botamusique
In my case, it only pertains the $COMPOSE_PROJECT_DIRECTORY
variable.
COMPOSE_PROJECT_DIRECTORY="/var/docker-compose/botamusique"
And that’s it. For additional docker-compose
projects I need to make only a
symlink and a configuration file. If I discover a bug or nuisance, only a
single file needs to be altered to get the benefit on all the docker-compose
services.
For reference, here’s the full /etc/init.d/docker-compose
file.
#!/sbin/openrc-run
# Copyright 2021 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
name="$RC_SVCNAME"
description="OpenRC script for managing the $name docker-compose project"
# Set default values
DOCKER_COMPOSE="${DOCKER_COMPOSE:-docker-compose} $DOCKER_COMPOSE_ARGS"
COMPOSE_PROJECT_NAME="${COMPOSE_PROJECT_NAME:-$name}"
# Export all variables used by docker-compose CLI
export COMPOSE_PROJECT_NAME
export COMPOSE_FILE
export COMPOSE_PROFILES
export COMPOSE_API_VERSION
export DOCKER_HOST
export DOCKER_TLS_VERIFY
export DOCKER_CERT_PATH
export COMPOSE_HTTP_TIMEOUT
export COMPOSE_TLS_VERSION
export COMPOSE_CONVERT_WINDOWS_PATHS
export COMPOSE_PATH_SEPARATOR
export COMPOSE_FORCE_WINDOWS_HOST
export COMPOSE_IGNORE_ORPHANS
export COMPOSE_PARALLEL_LIMIT
export COMPOSE_INTERACTIVE_NO_CLI
export COMPOSE_DOCKER_CLI_BUILD
# Set up (external) networks
for name in "${DOCKER_NETWORKS[@]}"
do
# Create the network if needed
if ! docker network ls | awk '{ print $2 }' | grep -q "$name"
then
einfo "Creating docker network '$name'"
docker network create "$name" > /dev/null
fi
# Expose some variables for the networks
network_id="DOCKER_NETWORK_${name}_ID"
declare -gx DOCKER_NETWORK_${name}_ID="$(docker network ls | awk '$2 == "'"$name"'" { print $1 }')"
declare -gx DOCKER_NETWORK_${name}_GATEWAY="$(docker network inspect "${!network_id}" | jq -r '.[0].IPAM.Config[0].Gateway')"
unset network_id
done
depend() {
need docker
}
start() {
$DOCKER_COMPOSE --project-directory "$COMPOSE_PROJECT_DIRECTORY" up -d
}
status() {
$DOCKER_COMPOSE --project-directory "$COMPOSE_PROJECT_DIRECTORY" ps
}
stop() {
$DOCKER_COMPOSE --project-directory "$COMPOSE_PROJECT_DIRECTORY" down
}