mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-06-29 15:31:05 +08:00
feat(migration): support docker compose -p project name for backup/restore (#13191)
### What problem does this PR solve?
When users start RAGFlow with `docker compose -p <alias>`, Docker
creates volumes prefixed with the alias (e.g., `myproject_mysql_data`).
The migration script (`docker/migration.sh`) previously hardcoded the
`docker_` prefix in volume names, causing backup/restore to silently
skip all volumes for any non-default project name.
This PR adds a `-p <project_name>` option so the script correctly
targets volumes regardless of the Docker Compose project name used.
### Type of change
- [ ] Bug Fix (non-breaking change which fixes an issue)
- [x] New Feature (non-breaking change which adds functionality)
- [ ] Documentation Update
- [ ] Refactoring
- [ ] Performance Improvement
- [ ] Other (please describe):
### Changes
- Add `-p <project_name>` flag (default: `docker`) for specifying Docker
Compose project name
- Build volume names dynamically: `${project_name}_${base_name}`
- Update help text with new option documentation and examples
- Show project-aware `docker compose` commands in error messages
- Fix deprecated `docker-compose` to `docker compose` in hints
- Use dynamic step count instead of hardcoded `4`
- Fully backward compatible — existing usage without `-p` works
unchanged
### Usage
```bash
# Existing usage (unchanged)
./migration.sh backup
./migration.sh restore my_backup
# New: custom project name
./migration.sh -p myproject backup
./migration.sh -p myproject restore my_backup
```
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# RAGFlow Data Migration Script
|
# RAGFlow Data Migration Script
|
||||||
# Usage: ./migration.sh [backup|restore] [backup_folder]
|
# Usage: ./migration.sh [-p project_name] [backup|restore] [backup_folder]
|
||||||
#
|
#
|
||||||
# This script helps you backup and restore RAGFlow Docker volumes
|
# This script helps you backup and restore RAGFlow Docker volumes
|
||||||
# including MySQL, MinIO, Redis, and Elasticsearch data.
|
# including MySQL, MinIO, Redis, and Elasticsearch data.
|
||||||
|
|
||||||
@@ -11,35 +11,55 @@ set -e # Exit on any error
|
|||||||
|
|
||||||
# Default values
|
# Default values
|
||||||
DEFAULT_BACKUP_FOLDER="backup"
|
DEFAULT_BACKUP_FOLDER="backup"
|
||||||
VOLUMES=("docker_mysql_data" "docker_minio_data" "docker_redis_data" "docker_esdata01")
|
DEFAULT_PROJECT_NAME="docker"
|
||||||
|
VOLUME_BASES=("mysql_data" "minio_data" "redis_data" "esdata01")
|
||||||
BACKUP_FILES=("mysql_backup.tar.gz" "minio_backup.tar.gz" "redis_backup.tar.gz" "es_backup.tar.gz")
|
BACKUP_FILES=("mysql_backup.tar.gz" "minio_backup.tar.gz" "redis_backup.tar.gz" "es_backup.tar.gz")
|
||||||
|
|
||||||
|
# Build volume names from project name and base names
|
||||||
|
build_volume_names() {
|
||||||
|
VOLUMES=()
|
||||||
|
for base in "${VOLUME_BASES[@]}"; do
|
||||||
|
VOLUMES+=("${PROJECT_NAME}_${base}")
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
# Function to display help information
|
# Function to display help information
|
||||||
show_help() {
|
show_help() {
|
||||||
echo "RAGFlow Data Migration Tool"
|
echo "RAGFlow Data Migration Tool"
|
||||||
echo ""
|
echo ""
|
||||||
echo "USAGE:"
|
echo "USAGE:"
|
||||||
echo " $0 <operation> [backup_folder]"
|
echo " $0 [-p project_name] <operation> [backup_folder]"
|
||||||
echo ""
|
echo ""
|
||||||
echo "OPERATIONS:"
|
echo "OPERATIONS:"
|
||||||
echo " backup - Create backup of all RAGFlow data volumes"
|
echo " backup - Create backup of all RAGFlow data volumes"
|
||||||
echo " restore - Restore RAGFlow data volumes from backup"
|
echo " restore - Restore RAGFlow data volumes from backup"
|
||||||
echo " help - Show this help message"
|
echo " help - Show this help message"
|
||||||
echo ""
|
echo ""
|
||||||
|
echo "OPTIONS:"
|
||||||
|
echo " -p project_name - Docker Compose project name (default: '$DEFAULT_PROJECT_NAME')"
|
||||||
|
echo " Use this when you started RAGFlow with 'docker compose -p <name>'"
|
||||||
|
echo ""
|
||||||
echo "PARAMETERS:"
|
echo "PARAMETERS:"
|
||||||
echo " backup_folder - Name of backup folder (default: '$DEFAULT_BACKUP_FOLDER')"
|
echo " backup_folder - Name of backup folder (default: '$DEFAULT_BACKUP_FOLDER')"
|
||||||
echo ""
|
echo ""
|
||||||
echo "EXAMPLES:"
|
echo "EXAMPLES:"
|
||||||
echo " $0 backup # Backup to './backup' folder"
|
echo " $0 backup # Backup with default project name 'docker'"
|
||||||
echo " $0 backup my_backup # Backup to './my_backup' folder"
|
echo " $0 backup my_backup # Backup to './my_backup' folder"
|
||||||
echo " $0 restore # Restore from './backup' folder"
|
echo " $0 restore # Restore from './backup' folder"
|
||||||
echo " $0 restore my_backup # Restore from './my_backup' folder"
|
echo " $0 restore my_backup # Restore from './my_backup' folder"
|
||||||
|
echo " $0 -p ragflow backup # Backup volumes for project 'ragflow'"
|
||||||
|
echo " $0 -p ragflow restore my_backup # Restore volumes for project 'ragflow'"
|
||||||
echo ""
|
echo ""
|
||||||
echo "DOCKER VOLUMES:"
|
echo "DOCKER VOLUMES (with default project name '$DEFAULT_PROJECT_NAME'):"
|
||||||
echo " - docker_mysql_data (MySQL database)"
|
echo " - ${DEFAULT_PROJECT_NAME}_mysql_data (MySQL database)"
|
||||||
echo " - docker_minio_data (MinIO object storage)"
|
echo " - ${DEFAULT_PROJECT_NAME}_minio_data (MinIO object storage)"
|
||||||
echo " - docker_redis_data (Redis cache)"
|
echo " - ${DEFAULT_PROJECT_NAME}_redis_data (Redis cache)"
|
||||||
echo " - docker_esdata01 (Elasticsearch indices)"
|
echo " - ${DEFAULT_PROJECT_NAME}_esdata01 (Elasticsearch indices)"
|
||||||
|
echo ""
|
||||||
|
echo "NOTE:"
|
||||||
|
echo " If you started RAGFlow with 'docker compose -p myproject up', the volume"
|
||||||
|
echo " names will be prefixed with 'myproject' instead of 'docker'. In that case,"
|
||||||
|
echo " use '-p myproject' with this script to match the correct volumes."
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to check if Docker is running
|
# Function to check if Docker is running
|
||||||
@@ -60,23 +80,23 @@ volume_exists() {
|
|||||||
# Function to check if any containers are using the target volumes
|
# Function to check if any containers are using the target volumes
|
||||||
check_containers_using_volumes() {
|
check_containers_using_volumes() {
|
||||||
echo "🔍 Checking for running containers that might be using target volumes..."
|
echo "🔍 Checking for running containers that might be using target volumes..."
|
||||||
|
|
||||||
# Get all running containers
|
# Get all running containers
|
||||||
local running_containers=$(docker ps --format "{{.Names}}")
|
local running_containers=$(docker ps --format "{{.Names}}")
|
||||||
|
|
||||||
if [ -z "$running_containers" ]; then
|
if [ -z "$running_containers" ]; then
|
||||||
echo "✅ No running containers found"
|
echo "✅ No running containers found"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check each running container for volume usage
|
# Check each running container for volume usage
|
||||||
local containers_using_volumes=()
|
local containers_using_volumes=()
|
||||||
local volume_usage_details=()
|
local volume_usage_details=()
|
||||||
|
|
||||||
for container in $running_containers; do
|
for container in $running_containers; do
|
||||||
# Get container's mount information
|
# Get container's mount information
|
||||||
local mounts=$(docker inspect "$container" --format '{{range .Mounts}}{{.Source}}{{"|"}}{{end}}' 2>/dev/null || echo "")
|
local mounts=$(docker inspect "$container" --format '{{range .Mounts}}{{.Source}}{{"|"}}{{end}}' 2>/dev/null || echo "")
|
||||||
|
|
||||||
# Check if any of our target volumes are used by this container
|
# Check if any of our target volumes are used by this container
|
||||||
for volume in "${VOLUMES[@]}"; do
|
for volume in "${VOLUMES[@]}"; do
|
||||||
if echo "$mounts" | grep -q "$volume"; then
|
if echo "$mounts" | grep -q "$volume"; then
|
||||||
@@ -86,7 +106,7 @@ check_containers_using_volumes() {
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
done
|
done
|
||||||
|
|
||||||
# If any containers are using our volumes, show error and exit
|
# If any containers are using our volumes, show error and exit
|
||||||
if [ ${#containers_using_volumes[@]} -gt 0 ]; then
|
if [ ${#containers_using_volumes[@]} -gt 0 ]; then
|
||||||
echo ""
|
echo ""
|
||||||
@@ -100,15 +120,19 @@ check_containers_using_volumes() {
|
|||||||
echo " - $detail"
|
echo " - $detail"
|
||||||
done
|
done
|
||||||
echo ""
|
echo ""
|
||||||
echo "🛑 SOLUTION: Stop the containers before performing backup/restore operations:"
|
if [ "$PROJECT_NAME" = "$DEFAULT_PROJECT_NAME" ]; then
|
||||||
echo " docker-compose -f docker/<your-docker-compose-file>.yml down"
|
echo "🛑 SOLUTION: Stop the containers before performing backup/restore operations:"
|
||||||
|
echo " docker compose -f docker/<your-docker-compose-file>.yml down"
|
||||||
|
else
|
||||||
|
echo "🛑 SOLUTION: Stop the containers before performing backup/restore operations:"
|
||||||
|
echo " docker compose -p $PROJECT_NAME -f docker/<your-docker-compose-file>.yml down"
|
||||||
|
fi
|
||||||
echo ""
|
echo ""
|
||||||
echo "💡 After backup/restore, you can restart with:"
|
echo "💡 After backup/restore, you can restart with the corresponding 'up -d' command."
|
||||||
echo " docker-compose -f docker/<your-docker-compose-file>.yml up -d"
|
|
||||||
echo ""
|
echo ""
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "✅ No containers are using target volumes, safe to proceed"
|
echo "✅ No containers are using target volumes, safe to proceed"
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -127,25 +151,28 @@ confirm_action() {
|
|||||||
# Function to perform backup
|
# Function to perform backup
|
||||||
perform_backup() {
|
perform_backup() {
|
||||||
local backup_folder=$1
|
local backup_folder=$1
|
||||||
|
|
||||||
echo "🚀 Starting RAGFlow data backup..."
|
echo "🚀 Starting RAGFlow data backup..."
|
||||||
echo "📁 Backup folder: $backup_folder"
|
echo "📁 Backup folder: $backup_folder"
|
||||||
|
echo "🏷️ Project name: $PROJECT_NAME"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Check if any containers are using the volumes
|
# Check if any containers are using the volumes
|
||||||
check_containers_using_volumes
|
check_containers_using_volumes
|
||||||
|
|
||||||
# Create backup folder if it doesn't exist
|
# Create backup folder if it doesn't exist
|
||||||
mkdir -p "$backup_folder"
|
mkdir -p "$backup_folder"
|
||||||
|
|
||||||
|
local total=${#VOLUMES[@]}
|
||||||
|
|
||||||
# Backup each volume
|
# Backup each volume
|
||||||
for i in "${!VOLUMES[@]}"; do
|
for i in "${!VOLUMES[@]}"; do
|
||||||
local volume="${VOLUMES[$i]}"
|
local volume="${VOLUMES[$i]}"
|
||||||
local backup_file="${BACKUP_FILES[$i]}"
|
local backup_file="${BACKUP_FILES[$i]}"
|
||||||
local step=$((i + 1))
|
local step=$((i + 1))
|
||||||
|
|
||||||
echo "📦 Step $step/4: Backing up $volume..."
|
echo "📦 Step $step/$total: Backing up $volume..."
|
||||||
|
|
||||||
if volume_exists "$volume"; then
|
if volume_exists "$volume"; then
|
||||||
docker run --rm \
|
docker run --rm \
|
||||||
-v "$volume":/source \
|
-v "$volume":/source \
|
||||||
@@ -157,10 +184,10 @@ perform_backup() {
|
|||||||
fi
|
fi
|
||||||
echo ""
|
echo ""
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "🎉 Backup completed successfully!"
|
echo "🎉 Backup completed successfully!"
|
||||||
echo "📍 Backup location: $(pwd)/$backup_folder"
|
echo "📍 Backup location: $(pwd)/$backup_folder"
|
||||||
|
|
||||||
# List backup files with sizes
|
# List backup files with sizes
|
||||||
echo ""
|
echo ""
|
||||||
echo "📋 Backup files created:"
|
echo "📋 Backup files created:"
|
||||||
@@ -175,20 +202,21 @@ perform_backup() {
|
|||||||
# Function to perform restore
|
# Function to perform restore
|
||||||
perform_restore() {
|
perform_restore() {
|
||||||
local backup_folder=$1
|
local backup_folder=$1
|
||||||
|
|
||||||
echo "🔄 Starting RAGFlow data restore..."
|
echo "🔄 Starting RAGFlow data restore..."
|
||||||
echo "📁 Backup folder: $backup_folder"
|
echo "📁 Backup folder: $backup_folder"
|
||||||
|
echo "🏷️ Project name: $PROJECT_NAME"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Check if any containers are using the volumes
|
# Check if any containers are using the volumes
|
||||||
check_containers_using_volumes
|
check_containers_using_volumes
|
||||||
|
|
||||||
# Check if backup folder exists
|
# Check if backup folder exists
|
||||||
if [ ! -d "$backup_folder" ]; then
|
if [ ! -d "$backup_folder" ]; then
|
||||||
echo "❌ Error: Backup folder '$backup_folder' does not exist"
|
echo "❌ Error: Backup folder '$backup_folder' does not exist"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if all backup files exist
|
# Check if all backup files exist
|
||||||
local missing_files=()
|
local missing_files=()
|
||||||
for backup_file in "${BACKUP_FILES[@]}"; do
|
for backup_file in "${BACKUP_FILES[@]}"; do
|
||||||
@@ -196,7 +224,7 @@ perform_restore() {
|
|||||||
missing_files+=("$backup_file")
|
missing_files+=("$backup_file")
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
if [ ${#missing_files[@]} -gt 0 ]; then
|
if [ ${#missing_files[@]} -gt 0 ]; then
|
||||||
echo "❌ Error: Missing backup files:"
|
echo "❌ Error: Missing backup files:"
|
||||||
for file in "${missing_files[@]}"; do
|
for file in "${missing_files[@]}"; do
|
||||||
@@ -205,7 +233,7 @@ perform_restore() {
|
|||||||
echo "Please ensure all backup files are present in '$backup_folder'"
|
echo "Please ensure all backup files are present in '$backup_folder'"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check for existing volumes and warn user
|
# Check for existing volumes and warn user
|
||||||
local existing_volumes=()
|
local existing_volumes=()
|
||||||
for volume in "${VOLUMES[@]}"; do
|
for volume in "${VOLUMES[@]}"; do
|
||||||
@@ -213,7 +241,7 @@ perform_restore() {
|
|||||||
existing_volumes+=("$volume")
|
existing_volumes+=("$volume")
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
if [ ${#existing_volumes[@]} -gt 0 ]; then
|
if [ ${#existing_volumes[@]} -gt 0 ]; then
|
||||||
echo "⚠️ WARNING: The following Docker volumes already exist:"
|
echo "⚠️ WARNING: The following Docker volumes already exist:"
|
||||||
for volume in "${existing_volumes[@]}"; do
|
for volume in "${existing_volumes[@]}"; do
|
||||||
@@ -222,23 +250,25 @@ perform_restore() {
|
|||||||
echo ""
|
echo ""
|
||||||
echo "🔴 IMPORTANT: Restoring will OVERWRITE existing data!"
|
echo "🔴 IMPORTANT: Restoring will OVERWRITE existing data!"
|
||||||
echo "💡 Recommendation: Create a backup of your current data first:"
|
echo "💡 Recommendation: Create a backup of your current data first:"
|
||||||
echo " $0 backup current_backup_$(date +%Y%m%d_%H%M%S)"
|
echo " $0 -p $PROJECT_NAME backup current_backup_$(date +%Y%m%d_%H%M%S)"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
if ! confirm_action "Do you want to continue with the restore operation?"; then
|
if ! confirm_action "Do you want to continue with the restore operation?"; then
|
||||||
echo "❌ Restore operation cancelled by user"
|
echo "❌ Restore operation cancelled by user"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
local total=${#VOLUMES[@]}
|
||||||
|
|
||||||
# Create volumes and restore data
|
# Create volumes and restore data
|
||||||
for i in "${!VOLUMES[@]}"; do
|
for i in "${!VOLUMES[@]}"; do
|
||||||
local volume="${VOLUMES[$i]}"
|
local volume="${VOLUMES[$i]}"
|
||||||
local backup_file="${BACKUP_FILES[$i]}"
|
local backup_file="${BACKUP_FILES[$i]}"
|
||||||
local step=$((i + 1))
|
local step=$((i + 1))
|
||||||
|
|
||||||
echo "🔧 Step $step/4: Restoring $volume..."
|
echo "🔧 Step $step/$total: Restoring $volume..."
|
||||||
|
|
||||||
# Create volume if it doesn't exist
|
# Create volume if it doesn't exist
|
||||||
if ! volume_exists "$volume"; then
|
if ! volume_exists "$volume"; then
|
||||||
echo " 📋 Creating Docker volume: $volume"
|
echo " 📋 Creating Docker volume: $volume"
|
||||||
@@ -246,18 +276,18 @@ perform_restore() {
|
|||||||
else
|
else
|
||||||
echo " 📋 Using existing Docker volume: $volume"
|
echo " 📋 Using existing Docker volume: $volume"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Restore data
|
# Restore data
|
||||||
echo " 📥 Restoring data from $backup_file..."
|
echo " 📥 Restoring data from $backup_file..."
|
||||||
docker run --rm \
|
docker run --rm \
|
||||||
-v "$volume":/target \
|
-v "$volume":/target \
|
||||||
-v "$(pwd)/$backup_folder":/backup \
|
-v "$(pwd)/$backup_folder":/backup \
|
||||||
alpine tar xzf "/backup/$backup_file" -C /target
|
alpine tar xzf "/backup/$backup_file" -C /target
|
||||||
|
|
||||||
echo "✅ Successfully restored $volume"
|
echo "✅ Successfully restored $volume"
|
||||||
echo ""
|
echo ""
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "🎉 Restore completed successfully!"
|
echo "🎉 Restore completed successfully!"
|
||||||
echo "💡 You can now start your RAGFlow services"
|
echo "💡 You can now start your RAGFlow services"
|
||||||
}
|
}
|
||||||
@@ -266,17 +296,38 @@ perform_restore() {
|
|||||||
main() {
|
main() {
|
||||||
# Check if Docker is available
|
# Check if Docker is available
|
||||||
check_docker
|
check_docker
|
||||||
|
|
||||||
# Parse command line arguments
|
# Parse -p flag
|
||||||
|
PROJECT_NAME="$DEFAULT_PROJECT_NAME"
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
-p)
|
||||||
|
if [ -z "${2:-}" ]; then
|
||||||
|
echo "❌ Error: -p requires a project name argument"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
PROJECT_NAME="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Build volume names based on project name
|
||||||
|
build_volume_names
|
||||||
|
|
||||||
|
# Parse remaining positional arguments
|
||||||
local operation=${1:-}
|
local operation=${1:-}
|
||||||
local backup_folder=${2:-$DEFAULT_BACKUP_FOLDER}
|
local backup_folder=${2:-$DEFAULT_BACKUP_FOLDER}
|
||||||
|
|
||||||
# Handle help or no arguments
|
# Handle help or no arguments
|
||||||
if [ -z "$operation" ] || [ "$operation" = "help" ] || [ "$operation" = "-h" ] || [ "$operation" = "--help" ]; then
|
if [ -z "$operation" ] || [ "$operation" = "help" ] || [ "$operation" = "-h" ] || [ "$operation" = "--help" ]; then
|
||||||
show_help
|
show_help
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Validate operation
|
# Validate operation
|
||||||
case "$operation" in
|
case "$operation" in
|
||||||
backup)
|
backup)
|
||||||
@@ -295,4 +346,4 @@ main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Run main function with all arguments
|
# Run main function with all arguments
|
||||||
main "$@"
|
main "$@"
|
||||||
|
|||||||
Reference in New Issue
Block a user