From 04cbf6b1af51142940e994918807bb2b99d7d62d Mon Sep 17 00:00:00 2001 From: Alec Turner Date: Fri, 8 Mar 2024 11:42:17 -0800 Subject: [PATCH] Reimplement scripts to support multiple app instances An additional config file `instances.conf` is added to define instance names and port numbers. The existing `app.conf` was also modified to include an `instances` field and to move node id and location under this new object. A `.env` file is used to manage configuration for each instance. Additional scripts are added: `start`, `stop`, `monitor` - see docs for description. The main entry script is also modified such that it will run until any service stops, then brings down the remaining services and exits. --- README.md | 24 +++++++++-- .../streamline-emr/docker-compose.yml | 8 ++-- .../streamline/streamline-emr/entry | 27 ++++++------ .../streamline/streamline-emr/monitor | 19 +++++++++ .../streamline/streamline-emr/start | 41 +++++++++++++++++++ .../clientapps/streamline/streamline-emr/stop | 11 +++++ .../streamline-emr/template.app.conf | 38 ++++++++--------- .../streamline/streamline-emr/template.env | 5 +++ .../streamline-emr/template.instances.conf | 2 + template.env | 4 -- 10 files changed, 136 insertions(+), 43 deletions(-) create mode 100755 root/var/signalytic/clientapps/streamline/streamline-emr/monitor create mode 100755 root/var/signalytic/clientapps/streamline/streamline-emr/start create mode 100755 root/var/signalytic/clientapps/streamline/streamline-emr/stop create mode 100644 root/var/signalytic/clientapps/streamline/streamline-emr/template.env create mode 100644 root/var/signalytic/clientapps/streamline/streamline-emr/template.instances.conf delete mode 100644 template.env diff --git a/README.md b/README.md index 07a7fd50..8917c08e 100644 --- a/README.md +++ b/README.md @@ -32,14 +32,30 @@ In a docker environment, the application must be started manually. This is done ``` /var/signalytic/clientapps/streamline/streamline-emr/entry ``` +Note that the entry script will block until the first service stops or until interrupted. If multiple instances are run from the same machine, additional configuration may be required to differentiate the instances. This is generally the case when using the "sibling container" method for running docker from within a docker container. See the `Configuration` section for more details. ## Configuration Certain configuration options can be adjusted by setting environment variables or by defining these variables in a `.env` file stored in the same directory as the `docker-compose.yml` file. These variables are: -`APP_PORT`: port mapping for the web app server (default: `3000`) -`CONTAINER_NAME`: name of the application container (default: `streamline-emr`) -`DATA_DIR`: directory to be mounted for peristent data (default: `/var/signalytic/clientapps/streamline/streamline-emr/data`) +`APP_PORT`: port mapping for the web app server +`CONTAINER_NAME`: name of the application container +`DATA_DIR`: directory to be mounted for peristent data `COMPOSE_PROJECT_NAME`: assign the project name associate with the compose file - this avoids conflicts when the "same" compose file is used to manage multiple instances (e.g. when using docker sibling containers) +`IMAGE`: optional, overrides the default `streamline-emr:2.0` image -Important note when using "sibling containers": the sibling container method uses the host docker socket to provide containers with access to docker. As a result, port mappings and volumne mount points all refer to the host system rather than the container from which the docker command originates. \ No newline at end of file +Each instance of the app uses its own `.env` file to specify its configuration. This is automated through the `start` script using information stored in a file `instances.conf`. This file defines two lists: +`INSTANCES`: a list of unique instance names +`APP_PORTS`: a corresponding list of port numbers to map to the local host + +Important note when using "sibling containers": the sibling container method uses the host docker socket to provide containers with access to docker. As a result, port mappings and volumne mount points all refer to the host system rather than the container from which the docker command originates. + +### App Configuration +An `app.conf` file is required for integration with other Signalytic services. A template is provided which demonstrates integration with the `dbsync` service. + +## Scripts +Several scripts are used to manage app instances, including: +`entry`: main entry script used to start all configured instances - does not return until a service stops or the process is interrupted. +`start`: loads configuration details from `instances.conf`, then configures and starts each application instance in a dedicated subfolder +`stop`: locates and stops all instances, even if no longer defined in `instances.conf` +`monitor`: checks the status of each instance service and sends data to `bgsystemlog` if possible (loc: `streamline_emr`) \ No newline at end of file diff --git a/root/var/signalytic/clientapps/streamline/streamline-emr/docker-compose.yml b/root/var/signalytic/clientapps/streamline/streamline-emr/docker-compose.yml index 1ad963a5..d6153312 100644 --- a/root/var/signalytic/clientapps/streamline/streamline-emr/docker-compose.yml +++ b/root/var/signalytic/clientapps/streamline/streamline-emr/docker-compose.yml @@ -1,9 +1,9 @@ version: '3.9' services: streamline-emr: - image: streamline-emr:2.0 - container_name: ${CONTAINER_NAME:-streamline-emr} + image: ${IMAGE:-streamline-emr:2.0} + container_name: ${CONTAINER_NAME} volumes: - - "${DATA_DIR:-/var/signalytic/clientapps/streamline/streamline-emr/data}:/var/lib/mysql" + - "${DATA_DIR}:/var/lib/mysql" ports: - - "${APP_PORT:-3000}:80" \ No newline at end of file + - "${APP_PORT}:80" \ No newline at end of file diff --git a/root/var/signalytic/clientapps/streamline/streamline-emr/entry b/root/var/signalytic/clientapps/streamline/streamline-emr/entry index 0eeb9dac..86f2f95e 100755 --- a/root/var/signalytic/clientapps/streamline/streamline-emr/entry +++ b/root/var/signalytic/clientapps/streamline/streamline-emr/entry @@ -1,17 +1,20 @@ #!/bin/bash -APPROOT="$(dirname "${BASH_SOURCE[0]}")" +LOCATION="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" -echo "entering app root: $APPROOT" -cd $APPROOT +# start all instances +${LOCATION}/start -echo "creating data folder (if necessary)" -mkdir -p data +# wait for any service to stop +pids=() +for f in $(find ${LOCATION} -mindepth 2 -maxdepth 3 -name docker-compose.yml); do + pushd $(dirname $f) > /dev/null + docker compose wait streamline-emr & + pids+=($!) + popd > /dev/null +done +echo "waiting on processes: ${pids[@]}" +wait -n ${pids[@]} > /dev/null -echo "starting app" -docker compose up - -echo "cleaning up" -docker compose down - -echo "done." +# cleanup +${LOCATION}/stop diff --git a/root/var/signalytic/clientapps/streamline/streamline-emr/monitor b/root/var/signalytic/clientapps/streamline/streamline-emr/monitor new file mode 100755 index 00000000..e9a6f6cd --- /dev/null +++ b/root/var/signalytic/clientapps/streamline/streamline-emr/monitor @@ -0,0 +1,19 @@ +#!/bin/bash + +LOCATION="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +for f in $(find ${LOCATION} -mindepth 2 -maxdepth 3 -name docker-compose.yml); do + pushd $(dirname $f) > /dev/null + status=$(docker compose ps --format '{"name":"{{.Names}}","created":"{{.CreatedAt}}","elapsed":"{{.RunningFor}}","state":"{{.State}}","status":"{{.Status}}"}') + popd > /dev/null + if [ "${status}" ]; then + echo $status + if command -v bgsystemlog &> /dev/null + then + bgsystemlog -j streamline_emr -m "'${status}'" + fi + else + echo "no services found" + fi +done +echo "done." \ No newline at end of file diff --git a/root/var/signalytic/clientapps/streamline/streamline-emr/start b/root/var/signalytic/clientapps/streamline/streamline-emr/start new file mode 100755 index 00000000..3f147d0e --- /dev/null +++ b/root/var/signalytic/clientapps/streamline/streamline-emr/start @@ -0,0 +1,41 @@ +#!/bin/bash + +LOCATION="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +CONF_FILE=${LOCATION}/instances.conf +INSTANCES_DIR=${LOCATION}/instances +COMPOSE_FILE=${LOCATION}/docker-compose.yml +ENV_TEMPLATE=${LOCATION}/template.env + +echo "starting all instances" + +if [ ! -f "${CONF_FILE}" ]; then + echo "missing conf file: ${CONF_FILE}" + exit 1 +fi + +echo "processing config file: ${CONF_FILE}" + +# load instance names and port numbers from conf file +instances=($(sed -n "s/INSTANCES=//p" ${CONF_FILE})) +ports=($(sed -n "s/APP_PORTS=//p" ${CONF_FILE})) +for ((i=0;i<${#instances[@]};i++)); do + + # prepare directory and config files for instance + instance=$(echo ${instances[$i]} | tr -d '"') + port=$(echo ${ports[$i]} | tr -d '"') + echo + echo "configuring instance $((i+1)) / ${#instances[@]}" + echo " name: ${instance}" + echo " port: ${port}" + mkdir -p ${INSTANCES_DIR}/${instance}/data + sed -e "s/{{INSTANCE_NAME}}/${instance}/g" -e "s/{{INSTANCE_PORT}}/${port}/g" ${ENV_TEMPLATE} > ${INSTANCES_DIR}/${instance}/.env + cp ${COMPOSE_FILE} ${INSTANCES_DIR}/${instance}/ + + # start app + pushd ${INSTANCES_DIR}/${instance} > /dev/null + docker compose up -d --force-recreate + popd > /dev/null +done + +echo "done." \ No newline at end of file diff --git a/root/var/signalytic/clientapps/streamline/streamline-emr/stop b/root/var/signalytic/clientapps/streamline/streamline-emr/stop new file mode 100755 index 00000000..a4540aa6 --- /dev/null +++ b/root/var/signalytic/clientapps/streamline/streamline-emr/stop @@ -0,0 +1,11 @@ +#!/bin/bash + +LOCATION="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +echo "stopping all instances" +for f in $(find ${LOCATION} -mindepth 2 -maxdepth 3 -name docker-compose.yml); do + pushd $(dirname $f) > /dev/null + docker compose down + popd > /dev/null +done +echo "done." \ No newline at end of file diff --git a/root/var/signalytic/clientapps/streamline/streamline-emr/template.app.conf b/root/var/signalytic/clientapps/streamline/streamline-emr/template.app.conf index 5be3965c..a99d451c 100644 --- a/root/var/signalytic/clientapps/streamline/streamline-emr/template.app.conf +++ b/root/var/signalytic/clientapps/streamline/streamline-emr/template.app.conf @@ -1,22 +1,22 @@ { "name": "Streamline EMR", - "database": { - "type": "mysql", - "version": "mariadb:15.1", - "name": "streamline", - "source": "streamline-emr", - "username": "", - "password": "", - "db_path": "/var/lib/mysql", - "logs_path": "/var/lib/mysql/logs", - "log_pattern": "bin.[0-9]*" - }, - "blockgraph": { - "district": "", - "name": "", - "node": "", - "location": "", - "username": "", - "password": "" + "dbsync": { + "database": { + "type": "mysql", + "version": "8.0.23", + "name": "main", + "username": "", + "password": "", + "db_path": "/var/lib/mysql", + "logs_path": "/var/lib/mysql/logs", + "log_pattern": "binlog.[0-9]*" + }, + "blockgraph": { + "district": "", + "name": "", + "username": "", + "password": "" + }, + "instances": {} } -} +} \ No newline at end of file diff --git a/root/var/signalytic/clientapps/streamline/streamline-emr/template.env b/root/var/signalytic/clientapps/streamline/streamline-emr/template.env new file mode 100644 index 00000000..cb2ad4be --- /dev/null +++ b/root/var/signalytic/clientapps/streamline/streamline-emr/template.env @@ -0,0 +1,5 @@ +COMPOSE_PROJECT_NAME={{INSTANCE_NAME}} +CONTAINER_NAME=streamline-emr-{{INSTANCE_NAME}} +DATA_DIR=/var/signalytic/clientapps/streamline/streamline-emr/instances/{{INSTANCE_NAME}}/data +APP_PORT={{INSTANCE_PORT}} +#IMAGE=streamline-emr:2.0 \ No newline at end of file diff --git a/root/var/signalytic/clientapps/streamline/streamline-emr/template.instances.conf b/root/var/signalytic/clientapps/streamline/streamline-emr/template.instances.conf new file mode 100644 index 00000000..1451e354 --- /dev/null +++ b/root/var/signalytic/clientapps/streamline/streamline-emr/template.instances.conf @@ -0,0 +1,2 @@ +INSTANCES= +APP_PORTS= \ No newline at end of file diff --git a/template.env b/template.env deleted file mode 100644 index 23e8a83b..00000000 --- a/template.env +++ /dev/null @@ -1,4 +0,0 @@ -APP_PORT=3000 -CONTAINER_NAME=streamline-emr-1 -COMPOSE_PROJECT_NAME=streamline-emr-1 -DATA_DIR=/path/to/volume/mount/dir \ No newline at end of file