87 lines
2.7 KiB
Bash
87 lines
2.7 KiB
Bash
#!/bin/sh
|
|
set -eu
|
|
|
|
SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
|
INFRA_DIR=$(CDPATH= cd -- "$SCRIPT_DIR/.." && pwd)
|
|
PROJECT_DIR=$(CDPATH= cd -- "$INFRA_DIR/.." && pwd)
|
|
COMPOSE_FILE=${COMPOSE_FILE:-"$INFRA_DIR/docker-compose.prod.yml"}
|
|
BACKUP_ROOT=${BACKUP_ROOT:-"$PROJECT_DIR/backups"}
|
|
STAMP=$(date -u +%Y%m%dT%H%M%SZ)
|
|
TARGET="$BACKUP_ROOT/$STAMP"
|
|
|
|
mkdir -p "$TARGET"
|
|
TARGET=$(CDPATH= cd -- "$TARGET" && pwd)
|
|
case "$TARGET" in
|
|
"$PROJECT_DIR"/*) ;;
|
|
*) echo "拒绝写入项目目录之外的备份路径:$TARGET" >&2; exit 2 ;;
|
|
esac
|
|
|
|
POSTGRES_DB=${POSTGRES_DB:-ai_ticket_platform}
|
|
POSTGRES_USER=${POSTGRES_USER:-ai_ticket}
|
|
BACKUP_HELPER_IMAGE=${BACKUP_HELPER_IMAGE:-alpine:3.21}
|
|
RUNNING_APPLICATION_SERVICES=""
|
|
MINIO_WAS_RUNNING=false
|
|
|
|
cleanup() {
|
|
MINIO_READY=true
|
|
if [ "$MINIO_WAS_RUNNING" = "true" ]; then
|
|
if ! docker compose -f "$COMPOSE_FILE" up -d --wait minio >/dev/null; then
|
|
echo "备份结束后 MinIO 未恢复健康,应用服务保持停止" >&2
|
|
MINIO_READY=false
|
|
fi
|
|
fi
|
|
if [ "$MINIO_READY" = "true" ] && [ -n "$RUNNING_APPLICATION_SERVICES" ]; then
|
|
docker compose -f "$COMPOSE_FILE" start $RUNNING_APPLICATION_SERVICES >/dev/null || true
|
|
fi
|
|
}
|
|
trap cleanup EXIT INT TERM
|
|
|
|
RUNNING_SERVICES=$(docker compose -f "$COMPOSE_FILE" ps --services --filter status=running)
|
|
if printf '%s\n' "$RUNNING_SERVICES" | grep -qx "minio"; then
|
|
MINIO_WAS_RUNNING=true
|
|
fi
|
|
if [ "${BACKUP_SKIP_APPLICATION_SERVICES:-false}" != "true" ]; then
|
|
for service in backend worker beat; do
|
|
if printf '%s\n' "$RUNNING_SERVICES" | grep -qx "$service"; then
|
|
RUNNING_APPLICATION_SERVICES="$RUNNING_APPLICATION_SERVICES $service"
|
|
fi
|
|
done
|
|
if [ -n "$RUNNING_APPLICATION_SERVICES" ]; then
|
|
docker compose -f "$COMPOSE_FILE" stop $RUNNING_APPLICATION_SERVICES
|
|
fi
|
|
fi
|
|
if [ "$MINIO_WAS_RUNNING" = "true" ]; then
|
|
docker compose -f "$COMPOSE_FILE" stop minio
|
|
fi
|
|
|
|
docker compose -f "$COMPOSE_FILE" exec -T postgres \
|
|
pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB" --format=custom > "$TARGET/postgres.dump"
|
|
docker compose -f "$COMPOSE_FILE" exec -T postgres \
|
|
pg_restore --list < "$TARGET/postgres.dump" > "$TARGET/postgres.contents"
|
|
MINIO_CONTAINER=$(docker compose -f "$COMPOSE_FILE" ps -q minio)
|
|
if [ -z "$MINIO_CONTAINER" ]; then
|
|
echo "未找到 MinIO 容器" >&2
|
|
exit 3
|
|
fi
|
|
docker run --rm \
|
|
--volumes-from "$MINIO_CONTAINER" \
|
|
-v "$TARGET:/backup" \
|
|
"$BACKUP_HELPER_IMAGE" \
|
|
tar -C /data -czf /backup/minio-data.tar.gz .
|
|
|
|
(
|
|
cd "$TARGET"
|
|
sha256sum postgres.dump postgres.contents minio-data.tar.gz > SHA256SUMS
|
|
)
|
|
|
|
cat > "$TARGET/manifest.txt" <<EOF
|
|
created_at=$STAMP
|
|
postgres_database=$POSTGRES_DB
|
|
postgres_user=$POSTGRES_USER
|
|
compose_file=$COMPOSE_FILE
|
|
EOF
|
|
|
|
cleanup
|
|
trap - EXIT INT TERM
|
|
echo "备份已创建并完成结构校验:$TARGET"
|