feat(postgres-dump-zstd-docker-sock): 添加通过 Docker unix socket 触发的 PostgreSQL 备份脚本
This commit is contained in:
196
postgres-dump-zstd-docker-sock.sh
Executable file
196
postgres-dump-zstd-docker-sock.sh
Executable file
@@ -0,0 +1,196 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
# PostgreSQL backup triggered through the Docker Engine unix socket.
|
||||||
|
# Example: ./postgres-dump-zstd-docker-sock.sh --socket=/var/run/docker.sock --api-version=v1.51 --container=postgres17 --backup-dir=/backups --backup-prefix=pgvector17_all_databases_zstd_
|
||||||
|
# Remote example: curl -fsSL https://git.1-h.cc/Scripts/Linux/raw/branch/main/postgres-dump-zstd-docker-sock.sh | sh -s -- --socket=/var/run/docker.sock --api-version=v1.51 --container=postgres17 --backup-dir=/backups --backup-prefix=pgvector17_all_databases_zstd_
|
||||||
|
log() {
|
||||||
|
printf '%s\n' "[cron] $(date -u '+%Y-%m-%dT%H:%M:%SZ') $*"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_usage() {
|
||||||
|
cat <<EOF >&2
|
||||||
|
Usage: $0 --socket=PATH --api-version=VERSION --container=NAME --backup-dir=DIR --backup-prefix=PREFIX
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--socket=PATH Docker Engine unix socket path
|
||||||
|
--api-version=VERSION Docker API version (e.g. v1.51)
|
||||||
|
--container=NAME PostgreSQL container name
|
||||||
|
--backup-dir=DIR Directory to store backups
|
||||||
|
--backup-prefix=PREFIX Backup filename prefix
|
||||||
|
--help Show this help message
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
DOCKER_SOCKET=""
|
||||||
|
DOCKER_API_VERSION=""
|
||||||
|
PG_CONTAINER_NAME=""
|
||||||
|
BACKUP_DIR=""
|
||||||
|
BACKUP_PREFIX=""
|
||||||
|
|
||||||
|
missing_value() {
|
||||||
|
printf 'Missing value for %s\n' "$1" >&2
|
||||||
|
print_usage
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
while [ "$#" -gt 0 ]; do
|
||||||
|
case $1 in
|
||||||
|
--socket=*)
|
||||||
|
DOCKER_SOCKET="${1#*=}"
|
||||||
|
;;
|
||||||
|
--socket)
|
||||||
|
if [ "$#" -lt 2 ]; then
|
||||||
|
missing_value '--socket'
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
DOCKER_SOCKET="$1"
|
||||||
|
;;
|
||||||
|
--api-version=*)
|
||||||
|
DOCKER_API_VERSION="${1#*=}"
|
||||||
|
;;
|
||||||
|
--api-version)
|
||||||
|
if [ "$#" -lt 2 ]; then
|
||||||
|
missing_value '--api-version'
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
DOCKER_API_VERSION="$1"
|
||||||
|
;;
|
||||||
|
--container=*)
|
||||||
|
PG_CONTAINER_NAME="${1#*=}"
|
||||||
|
;;
|
||||||
|
--container)
|
||||||
|
if [ "$#" -lt 2 ]; then
|
||||||
|
missing_value '--container'
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
PG_CONTAINER_NAME="$1"
|
||||||
|
;;
|
||||||
|
--backup-dir=*)
|
||||||
|
BACKUP_DIR="${1#*=}"
|
||||||
|
;;
|
||||||
|
--backup-dir)
|
||||||
|
if [ "$#" -lt 2 ]; then
|
||||||
|
missing_value '--backup-dir'
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
BACKUP_DIR="$1"
|
||||||
|
;;
|
||||||
|
--backup-prefix=*)
|
||||||
|
BACKUP_PREFIX="${1#*=}"
|
||||||
|
;;
|
||||||
|
--backup-prefix)
|
||||||
|
if [ "$#" -lt 2 ]; then
|
||||||
|
missing_value '--backup-prefix'
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
BACKUP_PREFIX="$1"
|
||||||
|
;;
|
||||||
|
--help)
|
||||||
|
print_usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
printf 'Unknown option: %s\n' "$1" >&2
|
||||||
|
print_usage
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$#" -gt 0 ]; then
|
||||||
|
printf 'Unexpected positional arguments: %s\n' "$*" >&2
|
||||||
|
print_usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$DOCKER_SOCKET" ] || [ -z "$DOCKER_API_VERSION" ] || [ -z "$PG_CONTAINER_NAME" ] || [ -z "$BACKUP_DIR" ] || [ -z "$BACKUP_PREFIX" ]; then
|
||||||
|
print_usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -S "$DOCKER_SOCKET" ]; then
|
||||||
|
log "docker socket $DOCKER_SOCKET not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
timestamp=$(date +%Y-%m-%d_%H-%M-%S)
|
||||||
|
backup_path="$BACKUP_DIR/${BACKUP_PREFIX}${timestamp}.zst"
|
||||||
|
docker_api_base="http://localhost/${DOCKER_API_VERSION}"
|
||||||
|
exec_create_endpoint="${docker_api_base}/containers/${PG_CONTAINER_NAME}/exec"
|
||||||
|
|
||||||
|
log "preparing database backup at $backup_path via docker unix socket"
|
||||||
|
|
||||||
|
cmd="set -o pipefail && pg_dumpall --username=\"\${POSTGRES_USER:-postgres}\" --clean | zstd > ${backup_path}"
|
||||||
|
create_payload=$(jq -n --arg cmd "$cmd" '{
|
||||||
|
AttachStdin: false,
|
||||||
|
AttachStdout: true,
|
||||||
|
AttachStderr: true,
|
||||||
|
Tty: true,
|
||||||
|
Cmd: ["bash", "-lc", $cmd]
|
||||||
|
}')
|
||||||
|
|
||||||
|
create_response=$(curl --fail --silent --show-error --unix-socket "$DOCKER_SOCKET" \
|
||||||
|
-X POST \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "$create_payload" \
|
||||||
|
"$exec_create_endpoint")
|
||||||
|
|
||||||
|
exec_id=$(printf '%s' "$create_response" | jq -r '.Id // empty')
|
||||||
|
|
||||||
|
if [ -z "$exec_id" ]; then
|
||||||
|
log "failed to create exec for $PG_CONTAINER_NAME"
|
||||||
|
log "docker response: $create_response"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "starting exec $exec_id"
|
||||||
|
|
||||||
|
exec_start_endpoint="${docker_api_base}/exec/${exec_id}/start"
|
||||||
|
start_output=$(curl --fail --show-error --silent --unix-socket "$DOCKER_SOCKET" \
|
||||||
|
-X POST \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"Detach": false, "Tty": true}' \
|
||||||
|
"$exec_start_endpoint")
|
||||||
|
|
||||||
|
if [ -n "$start_output" ]; then
|
||||||
|
printf '%s\n' "$start_output" | while IFS= read -r line; do
|
||||||
|
log "exec output: $line"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec_inspect_endpoint="${docker_api_base}/exec/${exec_id}/json"
|
||||||
|
inspect_response=$(curl --fail --silent --show-error --unix-socket "$DOCKER_SOCKET" "$exec_inspect_endpoint")
|
||||||
|
|
||||||
|
exit_code=$(printf '%s' "$inspect_response" | jq -r '.ExitCode // empty')
|
||||||
|
|
||||||
|
if [ -z "$exit_code" ]; then
|
||||||
|
log "could not determine exec exit code"
|
||||||
|
log "docker inspect response: $inspect_response"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$exit_code" != "0" ]; then
|
||||||
|
log "backup exec exited with status $exit_code"
|
||||||
|
log "docker inspect response: $inspect_response"
|
||||||
|
exit "$exit_code"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "$backup_path" ]; then
|
||||||
|
size=$(du -h "$backup_path" 2>/dev/null | awk '{print $1}')
|
||||||
|
if [ -n "$size" ]; then
|
||||||
|
log "backup completed: $backup_path ($size)"
|
||||||
|
else
|
||||||
|
log "backup completed: $backup_path"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log "backup command succeeded, but file $backup_path not found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "database backup finished"
|
||||||
Reference in New Issue
Block a user