--- date: 2021-06-04 title: Managing Docker Compose with OpenRC tags: - Gentoo - OpenRC - Docker - DockerCompose --- 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. ```sh 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. ```sh # 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. ```sh 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`](https://github.com/azlux/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. ```sh #!/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 } ```