From 241673885dca0d8dfd1b0ff3797f1a0b4a5dcc21 Mon Sep 17 00:00:00 2001 From: Pau RE Date: Wed, 4 Sep 2024 10:19:17 +0200 Subject: [PATCH 1/5] Add prevalidate environment on start --- alpine.Dockerfile | 4 +-- backup.sh | 59 +--------------------------------------- debian.Dockerfile | 4 +-- env.sh | 68 +++++++++++++++++++++++++++++++++++++++++++++++ init.sh | 7 +++++ 5 files changed, 80 insertions(+), 62 deletions(-) create mode 100755 env.sh create mode 100755 init.sh diff --git a/alpine.Dockerfile b/alpine.Dockerfile index 4d94e79..993b022 100644 --- a/alpine.Dockerfile +++ b/alpine.Dockerfile @@ -36,12 +36,12 @@ ENV POSTGRES_DB="**None**" \ WEBHOOK_EXTRA_ARGS="" COPY hooks /hooks -COPY backup.sh /backup.sh +COPY backup.sh env.sh init.sh / VOLUME /backups ENTRYPOINT ["/bin/sh", "-c"] -CMD ["exec /usr/local/bin/go-cron -s \"$SCHEDULE\" -p \"$HEALTHCHECK_PORT\" -- /backup.sh"] +CMD ["exec /init.sh"] HEALTHCHECK --interval=5m --timeout=3s \ CMD curl -f "http://localhost:$HEALTHCHECK_PORT/" || exit 1 diff --git a/backup.sh b/backup.sh index 4a5066a..cbb82dc 100755 --- a/backup.sh +++ b/backup.sh @@ -9,64 +9,7 @@ if [ -d "${HOOKS_DIR}" ]; then trap 'on_error' ERR fi -if [ "${POSTGRES_DB}" = "**None**" -a "${POSTGRES_DB_FILE}" = "**None**" ]; then - echo "You need to set the POSTGRES_DB or POSTGRES_DB_FILE environment variable." - exit 1 -fi - -if [ "${POSTGRES_HOST}" = "**None**" ]; then - if [ -n "${POSTGRES_PORT_5432_TCP_ADDR}" ]; then - POSTGRES_HOST=${POSTGRES_PORT_5432_TCP_ADDR} - POSTGRES_PORT=${POSTGRES_PORT_5432_TCP_PORT} - else - echo "You need to set the POSTGRES_HOST environment variable." - exit 1 - fi -fi - -if [ "${POSTGRES_USER}" = "**None**" -a "${POSTGRES_USER_FILE}" = "**None**" ]; then - echo "You need to set the POSTGRES_USER or POSTGRES_USER_FILE environment variable." - exit 1 -fi - -if [ "${POSTGRES_PASSWORD}" = "**None**" -a "${POSTGRES_PASSWORD_FILE}" = "**None**" -a "${POSTGRES_PASSFILE_STORE}" = "**None**" ]; then - echo "You need to set the POSTGRES_PASSWORD or POSTGRES_PASSWORD_FILE or POSTGRES_PASSFILE_STORE environment variable or link to a container named POSTGRES." - exit 1 -fi - -#Process vars -if [ "${POSTGRES_DB_FILE}" = "**None**" ]; then - POSTGRES_DBS=$(echo "${POSTGRES_DB}" | tr , " ") -elif [ -r "${POSTGRES_DB_FILE}" ]; then - POSTGRES_DBS=$(cat "${POSTGRES_DB_FILE}") -else - echo "Missing POSTGRES_DB_FILE file." - exit 1 -fi -if [ "${POSTGRES_USER_FILE}" = "**None**" ]; then - export PGUSER="${POSTGRES_USER}" -elif [ -r "${POSTGRES_USER_FILE}" ]; then - export PGUSER=$(cat "${POSTGRES_USER_FILE}") -else - echo "Missing POSTGRES_USER_FILE file." - exit 1 -fi -if [ "${POSTGRES_PASSWORD_FILE}" = "**None**" -a "${POSTGRES_PASSFILE_STORE}" = "**None**" ]; then - export PGPASSWORD="${POSTGRES_PASSWORD}" -elif [ -r "${POSTGRES_PASSWORD_FILE}" ]; then - export PGPASSWORD=$(cat "${POSTGRES_PASSWORD_FILE}") -elif [ -r "${POSTGRES_PASSFILE_STORE}" ]; then - export PGPASSFILE="${POSTGRES_PASSFILE_STORE}" -else - echo "Missing POSTGRES_PASSWORD_FILE or POSTGRES_PASSFILE_STORE file." - exit 1 -fi -export PGHOST="${POSTGRES_HOST}" -export PGPORT="${POSTGRES_PORT}" -KEEP_MINS=${BACKUP_KEEP_MINS} -KEEP_DAYS=${BACKUP_KEEP_DAYS} -KEEP_WEEKS=`expr $(((${BACKUP_KEEP_WEEKS} * 7) + 1))` -KEEP_MONTHS=`expr $(((${BACKUP_KEEP_MONTHS} * 31) + 1))` +source "$(dirname "$0")/env.sh" # Pre-backup hook if [ -d "${HOOKS_DIR}" ]; then diff --git a/debian.Dockerfile b/debian.Dockerfile index 5b77f8c..2af98ed 100644 --- a/debian.Dockerfile +++ b/debian.Dockerfile @@ -50,12 +50,12 @@ ENV POSTGRES_DB="**None**" \ WEBHOOK_EXTRA_ARGS="" COPY hooks /hooks -COPY backup.sh /backup.sh +COPY backup.sh env.sh init.sh / VOLUME /backups ENTRYPOINT ["/bin/sh", "-c"] -CMD ["exec /usr/local/bin/go-cron -s \"$SCHEDULE\" -p \"$HEALTHCHECK_PORT\" -- /backup.sh"] +CMD ["exec /init.sh"] HEALTHCHECK --interval=5m --timeout=3s \ CMD curl -f "http://localhost:$HEALTHCHECK_PORT/" || exit 1 diff --git a/env.sh b/env.sh new file mode 100755 index 0000000..ccbe801 --- /dev/null +++ b/env.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +# Pre-validate the environment +if [ "${POSTGRES_DB}" = "**None**" -a "${POSTGRES_DB_FILE}" = "**None**" ]; then + echo "You need to set the POSTGRES_DB or POSTGRES_DB_FILE environment variable." + exit 1 +fi + +if [ "${POSTGRES_HOST}" = "**None**" ]; then + if [ -n "${POSTGRES_PORT_5432_TCP_ADDR}" ]; then + POSTGRES_HOST=${POSTGRES_PORT_5432_TCP_ADDR} + POSTGRES_PORT=${POSTGRES_PORT_5432_TCP_PORT} + else + echo "You need to set the POSTGRES_HOST environment variable." + exit 1 + fi +fi + +if [ "${POSTGRES_USER}" = "**None**" -a "${POSTGRES_USER_FILE}" = "**None**" ]; then + echo "You need to set the POSTGRES_USER or POSTGRES_USER_FILE environment variable." + exit 1 +fi + +if [ "${POSTGRES_PASSWORD}" = "**None**" -a "${POSTGRES_PASSWORD_FILE}" = "**None**" -a "${POSTGRES_PASSFILE_STORE}" = "**None**" ]; then + echo "You need to set the POSTGRES_PASSWORD or POSTGRES_PASSWORD_FILE or POSTGRES_PASSFILE_STORE environment variable or link to a container named POSTGRES." + exit 1 +fi + +#Process vars +if [ "${POSTGRES_DB_FILE}" = "**None**" ]; then + POSTGRES_DBS=$(echo "${POSTGRES_DB}" | tr , " ") +elif [ -r "${POSTGRES_DB_FILE}" ]; then + POSTGRES_DBS=$(cat "${POSTGRES_DB_FILE}") +else + echo "Missing POSTGRES_DB_FILE file." + exit 1 +fi +if [ "${POSTGRES_USER_FILE}" = "**None**" ]; then + export PGUSER="${POSTGRES_USER}" +elif [ -r "${POSTGRES_USER_FILE}" ]; then + export PGUSER=$(cat "${POSTGRES_USER_FILE}") +else + echo "Missing POSTGRES_USER_FILE file." + exit 1 +fi +if [ "${POSTGRES_PASSWORD_FILE}" = "**None**" -a "${POSTGRES_PASSFILE_STORE}" = "**None**" ]; then + export PGPASSWORD="${POSTGRES_PASSWORD}" +elif [ -r "${POSTGRES_PASSWORD_FILE}" ]; then + export PGPASSWORD=$(cat "${POSTGRES_PASSWORD_FILE}") +elif [ -r "${POSTGRES_PASSFILE_STORE}" ]; then + export PGPASSFILE="${POSTGRES_PASSFILE_STORE}" +else + echo "Missing POSTGRES_PASSWORD_FILE or POSTGRES_PASSFILE_STORE file." + exit 1 +fi +export PGHOST="${POSTGRES_HOST}" +export PGPORT="${POSTGRES_PORT}" +KEEP_MINS=${BACKUP_KEEP_MINS} +KEEP_DAYS=${BACKUP_KEEP_DAYS} +KEEP_WEEKS=`expr $(((${BACKUP_KEEP_WEEKS} * 7) + 1))` +KEEP_MONTHS=`expr $(((${BACKUP_KEEP_MONTHS} * 31) + 1))` + +# Validate backup dir +if [ -d "${BACKUP_DIR}" -a -w "${BACKUP_DIR}" -a -x "${BACKUP_DIR}" ]; then + echo "Backups will be stored at ${BACKUP_DIR}." +else + echo "BACKUP_DIR points to a file or folder with insufficient permissions." + exit 1 +fi diff --git a/init.sh b/init.sh new file mode 100755 index 0000000..8674bff --- /dev/null +++ b/init.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +set -Eeo pipefail + +# Prevalidate configuration (don't source) +/env.sh + +exec /usr/local/bin/go-cron -s "$SCHEDULE" -p "$HEALTHCHECK_PORT" -- /backup.sh From 9160ab929084376e8de485f2aa0c5c751d555d15 Mon Sep 17 00:00:00 2001 From: Pau RE Date: Wed, 4 Sep 2024 10:46:14 +0200 Subject: [PATCH 2/5] Disable CMD as shell text --- alpine.Dockerfile | 4 ++-- debian.Dockerfile | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/alpine.Dockerfile b/alpine.Dockerfile index 993b022..9ca0d1b 100644 --- a/alpine.Dockerfile +++ b/alpine.Dockerfile @@ -40,8 +40,8 @@ COPY backup.sh env.sh init.sh / VOLUME /backups -ENTRYPOINT ["/bin/sh", "-c"] -CMD ["exec /init.sh"] +ENTRYPOINT [] +CMD ["/init.sh"] HEALTHCHECK --interval=5m --timeout=3s \ CMD curl -f "http://localhost:$HEALTHCHECK_PORT/" || exit 1 diff --git a/debian.Dockerfile b/debian.Dockerfile index 2af98ed..40f19bf 100644 --- a/debian.Dockerfile +++ b/debian.Dockerfile @@ -54,8 +54,8 @@ COPY backup.sh env.sh init.sh / VOLUME /backups -ENTRYPOINT ["/bin/sh", "-c"] -CMD ["exec /init.sh"] +ENTRYPOINT [] +CMD ["/init.sh"] HEALTHCHECK --interval=5m --timeout=3s \ CMD curl -f "http://localhost:$HEALTHCHECK_PORT/" || exit 1 From 0ec036071e6f9c26266c9dd61d6971b1716672c7 Mon Sep 17 00:00:00 2001 From: Pau RE Date: Wed, 4 Sep 2024 11:26:45 +0200 Subject: [PATCH 3/5] Add support to launch backups on startup --- README.md | 2 ++ alpine.Dockerfile | 1 + debian.Dockerfile | 1 + init.sh | 7 +++++++ 4 files changed, 11 insertions(+) diff --git a/README.md b/README.md index ac415e5..f47c24a 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ services: # - POSTGRES_PASSWORD_FILE=/run/secrets/db_password <-- alternative for POSTGRES_PASSWORD (to use with docker secrets) - POSTGRES_EXTRA_OPTS=-Z1 --schema=public --blobs - SCHEDULE=@daily + - BACKUP_ON_START=TRUE - BACKUP_KEEP_DAYS=7 - BACKUP_KEEP_WEEKS=4 - BACKUP_KEEP_MONTHS=6 @@ -75,6 +76,7 @@ Most variables are the same as in the [official postgres image](https://hub.dock |--|--| | BACKUP_DIR | Directory to save the backup at. Defaults to `/backups`. | | BACKUP_SUFFIX | Filename suffix to save the backup. Defaults to `.sql.gz`. | +| BACKUP_ON_START | If set to `TRUE` performs an backup on each container start or restart. Defaults to `FALSE`. | | BACKUP_KEEP_DAYS | Number of daily backups to keep before removal. Defaults to `7`. | | BACKUP_KEEP_WEEKS | Number of weekly backups to keep before removal. Defaults to `4`. | | BACKUP_KEEP_MONTHS | Number of monthly backups to keep before removal. Defaults to `6`. | diff --git a/alpine.Dockerfile b/alpine.Dockerfile index 9ca0d1b..e345ec4 100644 --- a/alpine.Dockerfile +++ b/alpine.Dockerfile @@ -21,6 +21,7 @@ ENV POSTGRES_DB="**None**" \ POSTGRES_EXTRA_OPTS="-Z1" \ POSTGRES_CLUSTER="FALSE" \ SCHEDULE="@daily" \ + BACKUP_ON_START="FALSE" \ BACKUP_DIR="/backups" \ BACKUP_SUFFIX=".sql.gz" \ BACKUP_LATEST_TYPE="symlink" \ diff --git a/debian.Dockerfile b/debian.Dockerfile index 40f19bf..0019334 100644 --- a/debian.Dockerfile +++ b/debian.Dockerfile @@ -35,6 +35,7 @@ ENV POSTGRES_DB="**None**" \ POSTGRES_EXTRA_OPTS="-Z1" \ POSTGRES_CLUSTER="FALSE" \ SCHEDULE="@daily" \ + BACKUP_ON_START="FALSE" \ BACKUP_DIR="/backups" \ BACKUP_SUFFIX=".sql.gz" \ BACKUP_LATEST_TYPE="symlink" \ diff --git a/init.sh b/init.sh index 8674bff..055973f 100755 --- a/init.sh +++ b/init.sh @@ -4,4 +4,11 @@ set -Eeo pipefail # Prevalidate configuration (don't source) /env.sh +# Initial background backup +if [ "${BACKUP_ON_START}" = "TRUE" ]; then + echo "Launching an startup backup as a background job..." + /backup.sh & +fi + +echo "Starting go-cron ($SCHEDULE)..." exec /usr/local/bin/go-cron -s "$SCHEDULE" -p "$HEALTHCHECK_PORT" -- /backup.sh From 5aec20408d36359fc4acf0d368f0ba2b73aaf70e Mon Sep 17 00:00:00 2001 From: Pau RE Date: Wed, 4 Sep 2024 13:27:55 +0200 Subject: [PATCH 4/5] Upgrade to go-cron 11 to avoid zombie processes --- alpine.Dockerfile | 2 +- debian.Dockerfile | 2 +- docker-bake.hcl | 4 ++-- generate-docker-bake.sh | 2 +- init.sh | 6 +++--- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/alpine.Dockerfile b/alpine.Dockerfile index e345ec4..ff2ecc7 100644 --- a/alpine.Dockerfile +++ b/alpine.Dockerfile @@ -1,7 +1,7 @@ ARG BASETAG=alpine FROM postgres:$BASETAG -ARG GOCRONVER=v0.0.10 +ARG GOCRONVER=v0.0.11 ARG TARGETOS ARG TARGETARCH RUN set -x \ diff --git a/debian.Dockerfile b/debian.Dockerfile index 0019334..9bb78e4 100644 --- a/debian.Dockerfile +++ b/debian.Dockerfile @@ -1,7 +1,7 @@ ARG BASETAG=latest FROM postgres:$BASETAG -ARG GOCRONVER=v0.0.10 +ARG GOCRONVER=v0.0.11 ARG TARGETOS ARG TARGETARCH diff --git a/docker-bake.hcl b/docker-bake.hcl index 654a3a6..27ff39e 100644 --- a/docker-bake.hcl +++ b/docker-bake.hcl @@ -15,12 +15,12 @@ variable "BUILD_REVISION" { } target "debian" { - args = {"GOCRONVER" = "v0.0.10"} + args = {"GOCRONVER" = "v0.0.11"} dockerfile = "debian.Dockerfile" } target "alpine" { - args = {"GOCRONVER" = "v0.0.10"} + args = {"GOCRONVER" = "v0.0.11"} dockerfile = "alpine.Dockerfile" } diff --git a/generate-docker-bake.sh b/generate-docker-bake.sh index 6da3c72..15d1ff4 100755 --- a/generate-docker-bake.sh +++ b/generate-docker-bake.sh @@ -2,7 +2,7 @@ set -e -GOCRONVER="v0.0.10" +GOCRONVER="v0.0.11" MAIN_TAG="16" TAGS_EXTRA="15 14 13 12" PLATFORMS="linux/amd64 linux/arm64 linux/arm/v7 linux/s390x linux/ppc64le" diff --git a/init.sh b/init.sh index 055973f..0e5bd69 100755 --- a/init.sh +++ b/init.sh @@ -4,11 +4,11 @@ set -Eeo pipefail # Prevalidate configuration (don't source) /env.sh +EXTRA_ARGS="" # Initial background backup if [ "${BACKUP_ON_START}" = "TRUE" ]; then - echo "Launching an startup backup as a background job..." - /backup.sh & + EXTRA_ARGS="-i" fi echo "Starting go-cron ($SCHEDULE)..." -exec /usr/local/bin/go-cron -s "$SCHEDULE" -p "$HEALTHCHECK_PORT" -- /backup.sh +exec /usr/local/bin/go-cron -s "$SCHEDULE" -p "$HEALTHCHECK_PORT" $EXTRA_ARGS -- /backup.sh From 8aa2ae7104c9c1a9b65605cfc773794f5ce3f989 Mon Sep 17 00:00:00 2001 From: Pau RE Date: Wed, 4 Sep 2024 13:29:14 +0200 Subject: [PATCH 5/5] Clean initial stdout --- env.sh | 4 +--- init.sh | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/env.sh b/env.sh index ccbe801..cce7d84 100755 --- a/env.sh +++ b/env.sh @@ -60,9 +60,7 @@ KEEP_WEEKS=`expr $(((${BACKUP_KEEP_WEEKS} * 7) + 1))` KEEP_MONTHS=`expr $(((${BACKUP_KEEP_MONTHS} * 31) + 1))` # Validate backup dir -if [ -d "${BACKUP_DIR}" -a -w "${BACKUP_DIR}" -a -x "${BACKUP_DIR}" ]; then - echo "Backups will be stored at ${BACKUP_DIR}." -else +if [ '!' -d "${BACKUP_DIR}" -o '!' -w "${BACKUP_DIR}" -o '!' -x "${BACKUP_DIR}" ]; then echo "BACKUP_DIR points to a file or folder with insufficient permissions." exit 1 fi diff --git a/init.sh b/init.sh index 0e5bd69..0c5428d 100755 --- a/init.sh +++ b/init.sh @@ -10,5 +10,4 @@ if [ "${BACKUP_ON_START}" = "TRUE" ]; then EXTRA_ARGS="-i" fi -echo "Starting go-cron ($SCHEDULE)..." exec /usr/local/bin/go-cron -s "$SCHEDULE" -p "$HEALTHCHECK_PORT" $EXTRA_ARGS -- /backup.sh