====== Резервное копирование PGVector ======
===== Описание =====
Автоматизированный скрипт для создания резервных копий **PGVector** с поддержкой:
* Дампов базы данных PostgreSQL
* Архивирования файлов данных
* Автоматической очистки старых бекапов
* Мониторинга дискового пространства
* Уведомлений в Telegram
===== Требования =====
* Docker и Docker Compose
* Bash 4.0+
* Утилиты: ''tar'', ''gzip'', ''find'', ''curl''
* Права root или sudo
===== Установка =====
==== 1. Создание директорий ====
# Создаем необходимые директории
sudo mkdir -p /opt/scripts
sudo mkdir -p /opt/backups/pgvector
# Устанавливаем права доступа
sudo chmod 755 /opt/scripts
sudo chmod 755 /opt/backups
==== 2. Создание скрипта ====
sudo nano /opt/scripts/pgvector-backup.sh
Скопируйте в файл следующий скрипт:
#!/bin/bash
# Скрипт резервного копирования PGVector
# Запуск: /opt/scripts/pgvector-backup.sh
# Настройки
COMPOSE_FILE="/opt/pgvector/docker-compose.yml"
PGVECTOR_DIR="/opt/pgvector"
BACKUP_DIR="/opt/backups/pgvector"
DATE=$(date +"%Y%m%d_%H%M%S")
BACKUP_NAME="pgvector_backup_${DATE}"
RETENTION_DAYS=10 # Храним бекапы 10 дней
# Настройки базы данных
DB_CONTAINER="pgvector_postgres"
DB_USER="postgres"
DB_PASSWORD="ваш_пароль_здесь"
DB_NAME="postgres"
# Логирование
LOG_FILE="${BACKUP_DIR}/backup.log"
# Функция логирования
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
# Создаем директорию для бекапов если не существует
mkdir -p "$BACKUP_DIR"
log "=== Начало резервного копирования PGVector ==="
# Проверяем что compose файл существует
if [ ! -f "$COMPOSE_FILE" ]; then
log "ОШИБКА: Файл $COMPOSE_FILE не найден!"
exit 1
fi
# Переходим в директорию с PGVector
cd "$PGVECTOR_DIR" || {
log "ОШИБКА: Не удалось перейти в директорию $PGVECTOR_DIR"
exit 1
}
# Проверяем что контейнеры запущены
log "Проверяем статус контейнеров..."
if ! docker-compose -f "$COMPOSE_FILE" ps | grep -q "Up"; then
log "ОШИБКА: Контейнеры не запущены! Запустите их перед созданием бекапа."
exit 1
fi
# Создаем дамп базы данных (пока контейнеры работают)
log "Создаем дамп базы данных..."
DB_DUMP_PATH="${BACKUP_DIR}/${BACKUP_NAME}_database.sql"
# Экспортируем пароль для pg_dump
export PGPASSWORD="$DB_PASSWORD"
docker exec "$DB_CONTAINER" pg_dump -U "$DB_USER" -d "$DB_NAME" --no-password --verbose > "$DB_DUMP_PATH" 2>> "$LOG_FILE"
if [ $? -eq 0 ] && [ -s "$DB_DUMP_PATH" ]; then
log "Дамп базы данных создан: $DB_DUMP_PATH"
# Получаем размер дампа
DB_DUMP_SIZE=$(du -h "$DB_DUMP_PATH" | cut -f1)
log "Размер дампа БД: $DB_DUMP_SIZE"
else
log "ОШИБКА: Не удалось создать дамп базы данных или файл пуст"
rm -f "$DB_DUMP_PATH"
exit 1
fi
# Очищаем переменную окружения
unset PGPASSWORD
# Останавливаем контейнеры
log "Останавливаем контейнеры PGVector..."
docker-compose -f "$COMPOSE_FILE" down
if [ $? -eq 0 ]; then
log "Контейнеры успешно остановлены"
else
log "ОШИБКА: Не удалось остановить контейнеры"
exit 1
fi
# Ждем пару секунд для полной остановки
sleep 5
# Создаем архив с данными (включая дамп БД)
log "Создаем архив данных..."
BACKUP_PATH="${BACKUP_DIR}/${BACKUP_NAME}.tar.gz"
tar -czf "$BACKUP_PATH" \
--exclude='*.log' \
--exclude='**/lost+found' \
-C "$BACKUP_DIR" "${BACKUP_NAME}_database.sql" \
-C "$PGVECTOR_DIR" \
postgres_data/ \
pgadmin_data/ \
docker-compose.yml
if [ $? -eq 0 ]; then
log "Архив создан: $BACKUP_PATH"
# Получаем размер архива
BACKUP_SIZE=$(du -h "$BACKUP_PATH" | cut -f1)
log "Размер архива: $BACKUP_SIZE"
# Удаляем отдельный файл дампа, так как он теперь в архиве
rm -f "$DB_DUMP_PATH"
log "Отдельный файл дампа БД удален (включен в архив)"
else
log "ОШИБКА: Не удалось создать архив"
# Пытаемся запустить контейнеры даже при ошибке
log "Запускаем контейнеры после ошибки..."
docker-compose -f "$COMPOSE_FILE" up -d
exit 1
fi
# Запускаем контейнеры обратно
log "Запускаем контейнеры PGVector..."
docker-compose -f "$COMPOSE_FILE" up -d
if [ $? -eq 0 ]; then
log "Контейнеры успешно запущены"
else
log "ОШИБКА: Не удалось запустить контейнеры"
exit 1
fi
# Ждем запуска сервисов
sleep 15
# Проверяем что все контейнеры работают
log "Проверяем статус контейнеров..."
RUNNING_CONTAINERS=$(docker-compose -f "$COMPOSE_FILE" ps --services --filter "status=running" | wc -l)
TOTAL_CONTAINERS=$(docker-compose -f "$COMPOSE_FILE" ps --services | wc -l)
if [ "$RUNNING_CONTAINERS" -eq "$TOTAL_CONTAINERS" ]; then
log "Все контейнеры ($RUNNING_CONTAINERS/$TOTAL_CONTAINERS) работают корректно"
else
log "ПРЕДУПРЕЖДЕНИЕ: Работают только $RUNNING_CONTAINERS из $TOTAL_CONTAINERS контейнеров"
fi
# Проверяем доступность базы данных
log "Проверяем подключение к базе данных..."
export PGPASSWORD="$DB_PASSWORD"
if docker exec "$DB_CONTAINER" pg_isready -U "$DB_USER" -d "$DB_NAME" > /dev/null 2>&1; then
log "База данных доступна и готова к работе"
else
log "ПРЕДУПРЕЖДЕНИЕ: База данных может быть недоступна"
fi
# Проверяем что расширение pgvector доступно
log "Проверяем расширение pgvector..."
if docker exec "$DB_CONTAINER" psql -U "$DB_USER" -d "$DB_NAME" -c "SELECT extname FROM pg_extension WHERE extname='vector';" 2>/dev/null | grep -q "vector"; then
log "Расширение pgvector активно и работает"
else
log "ПРЕДУПРЕЖДЕНИЕ: Расширение pgvector может быть неактивно"
fi
unset PGPASSWORD
# Удаляем старые бекапы
log "Удаляем бекапы старше $RETENTION_DAYS дней..."
DELETED_COUNT=$(find "$BACKUP_DIR" -name "pgvector_backup_*.tar.gz" -mtime +$RETENTION_DAYS -delete -print | wc -l)
log "Удалено старых бекапов: $DELETED_COUNT"
# Показываем статистику бекапов
BACKUP_COUNT=$(ls -1 "${BACKUP_DIR}"/pgvector_backup_*.tar.gz 2>/dev/null | wc -l)
TOTAL_SIZE=$(du -sh "${BACKUP_DIR}" 2>/dev/null | cut -f1)
log "Общее количество бекапов: $BACKUP_COUNT"
log "Общий размер папки бекапов: $TOTAL_SIZE"
# Получаем информацию о свободном месте на диске
BACKUP_DISK_USAGE=$(df -h "$BACKUP_DIR" | awk 'NR==2 {print $4 " свободно из " $2 " (" $5 " занято)"}')
ROOT_DISK_USAGE=$(df -h / | awk 'NR==2 {print $4 " свободно из " $2 " (" $5 " занято)"}')
log "Свободное место на диске бекапов: $BACKUP_DISK_USAGE"
if [ "$BACKUP_DIR" != "/" ] && [ "$(df "$BACKUP_DIR" | awk 'NR==2 {print $1}')" != "$(df / | awk 'NR==2 {print $1}')" ]; then
log "Свободное место на корневом диске: $ROOT_DISK_USAGE"
fi
# Получаем процент заполнения диска с бекапами для предупреждений
BACKUP_DISK_PERCENT=$(df "$BACKUP_DIR" | awk 'NR==2 {print $5}' | sed 's/%//')
if [ "$BACKUP_DISK_PERCENT" -gt 90 ]; then
log "⚠️ ПРЕДУПРЕЖДЕНИЕ: Диск заполнен более чем на 90%!"
elif [ "$BACKUP_DISK_PERCENT" -gt 80 ]; then
log "⚠️ ВНИМАНИЕ: Диск заполнен более чем на 80%"
fi
log "=== Резервное копирование завершено успешно ==="
# Формируем сообщение для Telegram
TELEGRAM_MESSAGE="✅ PGVector backup completed
📁 Backup: $BACKUP_NAME ($BACKUP_SIZE)
💾 DB dump: $DB_DUMP_SIZE
📊 Total backups: $BACKUP_COUNT
💿 Disk space: $BACKUP_DISK_USAGE"
# Добавляем предупреждение о месте, если нужно
if [ "$BACKUP_DISK_PERCENT" -gt 90 ]; then
TELEGRAM_MESSAGE="$TELEGRAM_MESSAGE
⚠️ WARNING: Disk >90% full!"
elif [ "$BACKUP_DISK_PERCENT" -gt 80 ]; then
TELEGRAM_MESSAGE="$TELEGRAM_MESSAGE
⚠️ ATTENTION: Disk >80% full"
fi
# Отправляем уведомление в Telegram (опционально)
curl -s -X POST "https://api.telegram.org/bot[ВАШ_ТОКЕН]/sendMessage" \
-d chat_id="[ВАШ_CHAT_ID]" \
-d text="$TELEGRAM_MESSAGE" > /dev/null
exit 0
**Обязательно измените следующие параметры:**
* ''DB_PASSWORD="ваш_пароль_здесь"'' - укажите реальный пароль БД
* ''[ВАШ_ТОКЕН]'' - токен Telegram бота
* ''[ВАШ_CHAT_ID]'' - ID чата для уведомлений
==== 3. Установка прав выполнения ====
sudo chmod +x /opt/scripts/pgvector-backup.sh
===== Настройка Telegram уведомлений =====
1. Создайте бота через **@BotFather**
2. Получите **токен бота** и **chat_id**
3. Замените в скрипте:
curl -s -X POST "https://api.telegram.org/bot[ВАШ_ТОКЕН]/sendMessage" \
-d chat_id="[ВАШ_CHAT_ID]" \
-d text="$TELEGRAM_MESSAGE" > /dev/null
===== Автоматизация =====
==== Настройка Cron ====
# Редактируем crontab
sudo crontab -e
# Добавляем задание - ежедневно в 3:00
0 3 * * * /opt/scripts/pgvector-backup.sh >> /var/log/pgvector-backup.log 2>&1
===== Использование =====
==== Ручной запуск ====
sudo /opt/scripts/pgvector-backup.sh
==== Проверка бекапов ====
# Список бекапов
ls -la /opt/backups/pgvector/
# Просмотр лога
tail -f /opt/backups/pgvector/backup.log
===== Структура бекапов =====
/opt/backups/pgvector/
├── pgvector_backup_20250830_201456.tar.gz # Основной архив
├── backup.log # Лог операций
└── [старые бекапы...]
**Содержимое архива:**
* ''pgvector_backup_YYYYMMDD_HHMMSS_database.sql'' - дамп БД
* ''postgres_data/'' - данные PostgreSQL
* ''pgadmin_data/'' - данные PgAdmin
* ''docker-compose.yml'' - конфигурация
===== Восстановление =====
==== Полное восстановление ====
# Останавливаем сервис
cd /opt/pgvector
docker-compose down
# Создаем бекап текущего состояния
sudo mv postgres_data postgres_data.backup.$(date +%Y%m%d_%H%M%S)
# Извлекаем архив
sudo tar -xzf /opt/backups/pgvector/pgvector_backup_YYYYMMDD_HHMMSS.tar.gz
# Запускаем сервис
docker-compose up -d
==== Восстановление только БД ====
# Извлекаем SQL дамп
tar -xzf backup.tar.gz pgvector_backup_YYYYMMDD_HHMMSS_database.sql
# Восстанавливаем
docker exec -i pgvector_postgres psql -U postgres -d postgres < pgvector_backup_YYYYMMDD_HHMMSS_database.sql
===== Мониторинг =====
При успешном бекапе приходит уведомление:
✅ PGVector backup completed
📁 Backup: pgvector_backup_20250830_201456 (45M)
💾 DB dump: 12M
📊 Total backups: 7
💿 Disk space: 2.1G свободно из 10G (79% занято)
Предупреждения при заполнении диска:
⚠️ ATTENTION: Disk >80% full
⚠️ WARNING: Disk >90% full!
===== Устранение проблем =====
=== Контейнеры не запущены ===
docker-compose ps
docker-compose up -d
=== Ошибка дампа БД ===
# Проверяем доступность БД
docker exec pgvector_postgres pg_isready -U postgres
# Проверяем логи
docker logs pgvector_postgres
=== Нет места на диске ===
# Проверяем использование
df -h /opt/backups
# Удаляем старые бекапы
find /opt/backups -name "*backup*.tar.gz" -mtime +5 -delete
===== Безопасность =====
# Устанавливаем правильные права
sudo chmod 700 /opt/scripts/pgvector-backup.sh
sudo chown root:root /opt/scripts/pgvector-backup.sh
# Защищаем директорию бекапов
sudo chmod 755 /opt/backups
sudo chown root:root /opt/backups
----
//Документация обновлена: {{ CURRENTUSER }} {{ date:j.m.Y H:i }}/