Различия
Показаны различия между двумя версиями страницы.
| Предыдущая версия справа и слева Предыдущая версия Следующая версия | Предыдущая версия | ||
| vm:chatwoot:03-backup [2025/08/28 21:12] – удалено - внешнее изменение (Дата неизвестна) 127.0.0.1 | vm:chatwoot:03-backup [2025/08/30 23:56] (текущий) – admin | ||
|---|---|---|---|
| Строка 1: | Строка 1: | ||
| + | ====== Автоматическое резервное копирование Chatwoot ====== | ||
| + | ===== Описание ===== | ||
| + | |||
| + | Система автоматического резервного копирования для Chatwoot, которая создает полные бекапы всех данных, | ||
| + | |||
| + | **ВАЖНО**: | ||
| + | |||
| + | ===== Что включается в бекап ===== | ||
| + | |||
| + | ^ Компонент ^ Описание ^ Расположение ^ | ||
| + | | База данных PostgreSQL | Полный дамп схемы и данных | `/ | ||
| + | | Redis данные | Дамп всех ключей и значений | `/ | ||
| + | | Медиа файлы | Загруженные файлы пользователей | `/ | ||
| + | | Конфигурация времени | Настройки часового пояса | `/ | ||
| + | | Docker конфигурация | docker-compose.yml и .env | `/ | ||
| + | | **Все новые папки** | Автоматически | `/ | ||
| + | |||
| + | **Преимущество**: | ||
| + | |||
| + | ===== Структура проекта ===== | ||
| + | |||
| + | < | ||
| + | /opt/ | ||
| + | ├── chatwoot/ | ||
| + | │ | ||
| + | │ | ||
| + | │ | ||
| + | │ | ||
| + | │ | ||
| + | │ | ||
| + | │ | ||
| + | │ | ||
| + | ├── backups/ | ||
| + | │ | ||
| + | └── scripts/ | ||
| + | └── chatwoot-backup.sh | ||
| + | </ | ||
| + | |||
| + | ===== Установка и настройка ===== | ||
| + | |||
| + | ==== Шаг 1: Создание структуры папок ==== | ||
| + | |||
| + | <code bash> | ||
| + | # Создание необходимых папок | ||
| + | sudo mkdir -p / | ||
| + | sudo mkdir -p / | ||
| + | sudo chown -R $USER:$USER / | ||
| + | sudo chown -R $USER:$USER / | ||
| + | </ | ||
| + | |||
| + | ==== Шаг 2: Создание скрипта ==== | ||
| + | |||
| + | Создайте файл `/ | ||
| + | |||
| + | <code bash> | ||
| + | sudo nano / | ||
| + | </ | ||
| + | |||
| + | Вставьте следующий код, заменив пароли на свои: | ||
| + | |||
| + | <code bash> | ||
| + | #!/bin/bash | ||
| + | |||
| + | # Скрипт резервного копирования Chatwoot | ||
| + | # Запуск: | ||
| + | |||
| + | # Настройки | ||
| + | COMPOSE_FILE="/ | ||
| + | CHATWOOT_DIR="/ | ||
| + | BACKUP_DIR="/ | ||
| + | DATE=$(date +" | ||
| + | BACKUP_NAME=" | ||
| + | RETENTION_DAYS=10 | ||
| + | |||
| + | # Настройки базы данных - ИЗМЕНИТЕ НА СВОИ | ||
| + | DB_CONTAINER=" | ||
| + | DB_USER=" | ||
| + | DB_PASSWORD=" | ||
| + | DB_NAME=" | ||
| + | |||
| + | # Настройки Redis - ИЗМЕНИТЕ НА СВОИ | ||
| + | REDIS_CONTAINER=" | ||
| + | REDIS_PASSWORD=" | ||
| + | |||
| + | # Логирование | ||
| + | LOG_FILE=" | ||
| + | |||
| + | # Функция логирования | ||
| + | log() { | ||
| + | echo " | ||
| + | } | ||
| + | |||
| + | # Создаем директорию для бекапов если не существует | ||
| + | mkdir -p " | ||
| + | |||
| + | log "=== Начало резервного копирования Chatwoot ===" | ||
| + | |||
| + | # Проверяем что compose файл существует | ||
| + | if [ ! -f " | ||
| + | log " | ||
| + | exit 1 | ||
| + | fi | ||
| + | |||
| + | # Переходим в директорию с Chatwoot | ||
| + | cd " | ||
| + | log " | ||
| + | exit 1 | ||
| + | } | ||
| + | |||
| + | # Проверяем что контейнеры запущены | ||
| + | log " | ||
| + | if ! docker-compose -f " | ||
| + | log " | ||
| + | exit 1 | ||
| + | fi | ||
| + | |||
| + | # Показываем что будет включено в бекап | ||
| + | log " | ||
| + | CHATWOOT_DATA_CONTENTS=$(ls -la / | ||
| + | log " | ||
| + | |||
| + | # Создаем дамп базы данных (пока контейнеры работают) | ||
| + | log " | ||
| + | DB_DUMP_PATH=" | ||
| + | # Экспортируем пароль для pg_dump | ||
| + | export PGPASSWORD=" | ||
| + | docker exec " | ||
| + | if [ $? -eq 0 ] && [ -s " | ||
| + | log " | ||
| + | | ||
| + | # Получаем размер дампа | ||
| + | DB_DUMP_SIZE=$(du -h " | ||
| + | log " | ||
| + | else | ||
| + | log " | ||
| + | rm -f " | ||
| + | exit 1 | ||
| + | fi | ||
| + | |||
| + | # Создаем дамп Redis | ||
| + | log " | ||
| + | REDIS_DUMP_PATH=" | ||
| + | # Создаем дамп внутри контейнера во временный файл | ||
| + | REDIS_TEMP_PATH="/ | ||
| + | docker exec " | ||
| + | if [ $? -eq 0 ]; then | ||
| + | # Копируем дамп из контейнера | ||
| + | docker cp " | ||
| + | if [ -f " | ||
| + | log " | ||
| + | REDIS_DUMP_SIZE=$(du -h " | ||
| + | log " | ||
| + | # Удаляем временный файл из контейнера | ||
| + | docker exec " | ||
| + | else | ||
| + | log " | ||
| + | REDIS_DUMP_SIZE=" | ||
| + | fi | ||
| + | else | ||
| + | log " | ||
| + | REDIS_DUMP_SIZE=" | ||
| + | fi | ||
| + | |||
| + | # Очищаем переменную окружения | ||
| + | unset PGPASSWORD | ||
| + | |||
| + | # Останавливаем контейнеры | ||
| + | log " | ||
| + | docker-compose -f " | ||
| + | |||
| + | if [ $? -eq 0 ]; then | ||
| + | log " | ||
| + | else | ||
| + | log " | ||
| + | exit 1 | ||
| + | fi | ||
| + | |||
| + | # Ждем пару секунд для полной остановки | ||
| + | sleep 5 | ||
| + | |||
| + | # Создаем архив с данными - АВТОМАТИЧЕСКИ ВКЛЮЧАЕТ ВСЁ СОДЕРЖИМОЕ chatwoot_data | ||
| + | log " | ||
| + | BACKUP_PATH=" | ||
| + | |||
| + | # Создаем архив с автоматическим включением ВСЕГО содержимого / | ||
| + | tar -czf " | ||
| + | --exclude=' | ||
| + | --exclude=' | ||
| + | --exclude=' | ||
| + | --exclude=' | ||
| + | --exclude=' | ||
| + | -C " | ||
| + | $([ -f " | ||
| + | -C "/ | ||
| + | chatwoot_data/ | ||
| + | -C " | ||
| + | docker-compose.yml \ | ||
| + | .env | ||
| + | |||
| + | if [ $? -eq 0 ]; then | ||
| + | log " | ||
| + | |||
| + | # Получаем размер архива | ||
| + | BACKUP_SIZE=$(du -h " | ||
| + | log " | ||
| + | | ||
| + | # Удаляем отдельные файлы дампов, | ||
| + | rm -f " | ||
| + | [ -f " | ||
| + | log " | ||
| + | | ||
| + | # Показываем что попало в архив | ||
| + | log " | ||
| + | tar -tzf " | ||
| + | log " | ||
| + | done | ||
| + | TOTAL_FILES=$(tar -tzf " | ||
| + | log " | ||
| + | | ||
| + | else | ||
| + | log " | ||
| + | |||
| + | # Пытаемся запустить контейнеры даже при ошибке | ||
| + | log " | ||
| + | docker-compose -f " | ||
| + | exit 1 | ||
| + | fi | ||
| + | |||
| + | # Запускаем контейнеры обратно | ||
| + | log " | ||
| + | docker-compose -f " | ||
| + | |||
| + | if [ $? -eq 0 ]; then | ||
| + | log " | ||
| + | else | ||
| + | log " | ||
| + | exit 1 | ||
| + | fi | ||
| + | |||
| + | # Ждем запуска сервисов | ||
| + | sleep 15 | ||
| + | |||
| + | # Проверяем что все контейнеры работают | ||
| + | log " | ||
| + | RUNNING_CONTAINERS=$(docker-compose -f " | ||
| + | TOTAL_CONTAINERS=$(docker-compose -f " | ||
| + | if [ " | ||
| + | log " | ||
| + | else | ||
| + | log " | ||
| + | fi | ||
| + | |||
| + | # Проверяем доступность базы данных | ||
| + | log " | ||
| + | export PGPASSWORD=" | ||
| + | if docker exec " | ||
| + | log " | ||
| + | else | ||
| + | log " | ||
| + | fi | ||
| + | |||
| + | # Проверяем что база данных Chatwoot содержит нужные таблицы | ||
| + | log " | ||
| + | if docker exec " | ||
| + | log " | ||
| + | else | ||
| + | log " | ||
| + | fi | ||
| + | |||
| + | # Проверяем Redis контейнер | ||
| + | log " | ||
| + | if docker exec " | ||
| + | log "Redis работает корректно" | ||
| + | else | ||
| + | log " | ||
| + | fi | ||
| + | |||
| + | # Проверяем Rails контейнер | ||
| + | log " | ||
| + | if docker exec chatwoot_rails bundle exec rails runner "puts ' | ||
| + | log "Rails приложение работает корректно" | ||
| + | else | ||
| + | log " | ||
| + | fi | ||
| + | |||
| + | unset PGPASSWORD | ||
| + | |||
| + | # Удаляем старые бекапы | ||
| + | log " | ||
| + | DELETED_COUNT=$(find " | ||
| + | log " | ||
| + | |||
| + | # Показываем статистику бекапов | ||
| + | BACKUP_COUNT=$(ls -1 " | ||
| + | TOTAL_SIZE=$(du -sh " | ||
| + | log " | ||
| + | log " | ||
| + | |||
| + | # Получаем информацию о свободном месте на диске | ||
| + | BACKUP_DISK_USAGE=$(df -h " | ||
| + | ROOT_DISK_USAGE=$(df -h / | awk 'NR==2 {print $4 " свободно из " $2 " (" $5 " занято)" | ||
| + | log " | ||
| + | if [ " | ||
| + | log " | ||
| + | fi | ||
| + | |||
| + | # Получаем процент заполнения диска с бекапами для предупреждений | ||
| + | BACKUP_DISK_PERCENT=$(df " | ||
| + | if [ " | ||
| + | log " | ||
| + | elif [ " | ||
| + | log " | ||
| + | fi | ||
| + | |||
| + | log "=== Резервное копирование завершено успешно ===" | ||
| + | |||
| + | # Формируем сообщение для Telegram | ||
| + | TELEGRAM_MESSAGE=" | ||
| + | 📁 Backup: $BACKUP_NAME ($BACKUP_SIZE) | ||
| + | 💾 DB dump: $DB_DUMP_SIZE | ||
| + | 🗄️ Redis dump: $REDIS_DUMP_SIZE | ||
| + | 📊 Total backups: $BACKUP_COUNT | ||
| + | 📄 Files in archive: $TOTAL_FILES | ||
| + | 💿 Disk space: $BACKUP_DISK_USAGE" | ||
| + | |||
| + | # Добавляем предупреждение о месте, если нужно | ||
| + | if [ " | ||
| + | TELEGRAM_MESSAGE=" | ||
| + | ⚠️ WARNING: Disk >90% full!" | ||
| + | elif [ " | ||
| + | TELEGRAM_MESSAGE=" | ||
| + | ⚠️ ATTENTION: Disk >80% full" | ||
| + | fi | ||
| + | |||
| + | # Отправляем уведомление в Telegram (опционально) | ||
| + | # Замените на свой BOT_TOKEN и CHAT_ID | ||
| + | curl -s -X POST " | ||
| + | -d chat_id=" | ||
| + | -d text=" | ||
| + | |||
| + | exit 0 | ||
| + | </ | ||
| + | |||
| + | **Обязательно измените**: | ||
| + | * `YOUR_POSTGRES_PASSWORD` на ваш пароль PostgreSQL | ||
| + | * `YOUR_REDIS_PASSWORD` на ваш пароль Redis | ||
| + | * `[YOUR_BOT_TOKEN]` на токен вашего Telegram бота (опционально) | ||
| + | * `[YOUR_CHAT_ID]` на ваш Chat ID в Telegram (опционально) | ||
| + | |||
| + | ==== Шаг 3: Настройка прав доступа ==== | ||
| + | |||
| + | <code bash> | ||
| + | # Делаем скрипт исполняемым | ||
| + | sudo chmod +x / | ||
| + | |||
| + | # Проверяем права | ||
| + | ls -la / | ||
| + | </ | ||
| + | |||
| + | ==== Шаг 4: Тестовый запуск ==== | ||
| + | |||
| + | <code bash> | ||
| + | # Запуск тестового бекапа | ||
| + | sudo / | ||
| + | |||
| + | # Проверка результата | ||
| + | ls -la / | ||
| + | cat / | ||
| + | </ | ||
| + | |||
| + | ===== Автоматизация через Cron ===== | ||
| + | |||
| + | ==== Настройка ежедневного бекапа ==== | ||
| + | |||
| + | <code bash> | ||
| + | # Открываем crontab | ||
| + | sudo crontab -e | ||
| + | |||
| + | # Добавляем задачу (бекап каждый день в 2:00 ночи) | ||
| + | 0 2 * * * / | ||
| + | |||
| + | # Сохраняем и выходим (Ctrl+X, Y, Enter) | ||
| + | </ | ||
| + | |||
| + | ==== Альтернативные расписания ==== | ||
| + | |||
| + | < | ||
| + | # Каждый день в 3:00 | ||
| + | 0 3 * * * / | ||
| + | |||
| + | # Каждые 12 часов (в 6:00 и 18:00) | ||
| + | 0 6,18 * * * / | ||
| + | |||
| + | # Каждый день в 1:30 ночи | ||
| + | 30 1 * * * / | ||
| + | |||
| + | # Только в выходные в 4:00 | ||
| + | 0 4 * * 6,0 / | ||
| + | </ | ||
| + | |||
| + | ==== Проверка работы Cron ==== | ||
| + | |||
| + | <code bash> | ||
| + | # Проверить статус cron | ||
| + | sudo systemctl status cron | ||
| + | |||
| + | # Посмотреть активные задачи | ||
| + | sudo crontab -l | ||
| + | |||
| + | # Посмотреть логи cron | ||
| + | sudo tail -f / | ||
| + | </ | ||
| + | |||
| + | ===== Мониторинг и уведомления ===== | ||
| + | |||
| + | ==== Настройка Telegram бота ==== | ||
| + | |||
| + | 1. **Создайте бота**: Отправьте `/start` боту @BotFather в Telegram | ||
| + | 2. **Получите токен**: | ||
| + | 3. **Узнайте Chat ID**: Отправьте сообщение боту, затем откройте: | ||
| + | | ||
| + | |||
| + | ==== Проверка уведомлений ==== | ||
| + | |||
| + | <code bash> | ||
| + | # Тест отправки сообщения (замените токен и chat_id) | ||
| + | curl -s -X POST " | ||
| + | -d chat_id=" | ||
| + | -d text=" | ||
| + | </ | ||
| + | |||
| + | ==== Мониторинг места на диске ==== | ||
| + | |||
| + | <code bash> | ||
| + | # Проверка места в папке бекапов | ||
| + | df -h / | ||
| + | |||
| + | # Размер всех бекапов | ||
| + | du -sh / | ||
| + | |||
| + | # Список всех бекапов с размерами | ||
| + | ls -lah / | ||
| + | </ | ||
| + | |||
| + | ===== Восстановление из бекапа ===== | ||
| + | |||
| + | ==== Полное восстановление ==== | ||
| + | |||
| + | <code bash> | ||
| + | # Остановите Chatwoot | ||
| + | cd / | ||
| + | docker-compose down | ||
| + | |||
| + | # Найдите нужный бекап | ||
| + | ls -la / | ||
| + | |||
| + | # Извлеките архив (замените на ваш файл) | ||
| + | cd /tmp | ||
| + | tar -xzf / | ||
| + | |||
| + | # Восстановите данные | ||
| + | sudo rm -rf / | ||
| + | sudo cp -r chatwoot_data/ | ||
| + | |||
| + | # Восстановите конфигурацию (если нужно) | ||
| + | cp docker-compose.yml / | ||
| + | cp .env / | ||
| + | |||
| + | # Запустите Chatwoot | ||
| + | cd / | ||
| + | docker-compose up -d | ||
| + | </ | ||
| + | |||
| + | ==== Восстановление только базы данных ==== | ||
| + | |||
| + | <code bash> | ||
| + | # Найдите дамп базы в архиве | ||
| + | tar -tzf / | ||
| + | |||
| + | # Извлеките дамп | ||
| + | tar -xzf / | ||
| + | |||
| + | # Восстановите в контейнер (Chatwoot должен быть запущен) | ||
| + | export PGPASSWORD=" | ||
| + | cat chatwoot_backup_YYYYMMDD_HHMMSS_database.sql | docker exec -i chatwoot_postgres psql -U postgres -d chatwoot_production | ||
| + | unset PGPASSWORD | ||
| + | </ | ||
| + | |||
| + | ===== Устранение неполадок ===== | ||
| + | |||
| + | ==== Основные проблемы ==== | ||
| + | |||
| + | === Ошибка " | ||
| + | |||
| + | **Причина**: | ||
| + | |||
| + | **Решение**: | ||
| + | <code bash> | ||
| + | docker ps | ||
| + | # Обновите переменные DB_CONTAINER и REDIS_CONTAINER в скрипте | ||
| + | </ | ||
| + | |||
| + | === Ошибка доступа к базе данных === | ||
| + | |||
| + | **Причина**: | ||
| + | |||
| + | **Решение**: | ||
| + | <code bash> | ||
| + | cat / | ||
| + | # Обновите переменные в скрипте согласно .env | ||
| + | </ | ||
| + | |||
| + | === Нехватка места на диске === | ||
| + | |||
| + | **Причина**: | ||
| + | |||
| + | **Решение**: | ||
| + | <code bash> | ||
| + | # Удалите старые бекапы вручную | ||
| + | find / | ||
| + | |||
| + | # Уменьшите RETENTION_DAYS в скрипте | ||
| + | </ | ||
| + | |||
| + | ==== Проверка логов ==== | ||
| + | |||
| + | <code bash> | ||
| + | # Основной лог скрипта | ||
| + | tail -f / | ||
| + | |||
| + | # Лог cron | ||
| + | tail -f / | ||
| + | |||
| + | # Логи Docker контейнеров | ||
| + | docker logs chatwoot_postgres | ||
| + | docker logs chatwoot_redis | ||
| + | </ | ||
| + | |||
| + | ===== Дополнительные настройки ===== | ||
| + | |||
| + | ==== Изменение времени хранения ==== | ||
| + | |||
| + | В скрипте измените: | ||
| + | <code bash> | ||
| + | RETENTION_DAYS=10 | ||
| + | </ | ||
| + | |||
| + | ==== Изменение папки бекапов ==== | ||
| + | |||
| + | В скрипте измените: | ||
| + | <code bash> | ||
| + | BACKUP_DIR="/ | ||
| + | </ | ||
| + | |||
| + | **Рекомендация**: | ||
| + | |||
| + | ===== Безопасность ===== | ||
| + | |||
| + | ==== Защита скрипта ==== | ||
| + | |||
| + | <code bash> | ||
| + | # Ограничить доступ только root | ||
| + | sudo chown root:root / | ||
| + | sudo chmod 700 / | ||
| + | |||
| + | # Проверить права | ||
| + | ls -la / | ||
| + | </ | ||
| + | |||
| + | ==== Шифрование бекапов ==== | ||
| + | |||
| + | Для дополнительной безопасности можно добавить шифрование: | ||
| + | |||
| + | <code bash> | ||
| + | # Добавьте в скрипт после создания архива: | ||
| + | gpg --symmetric --cipher-algo AES256 " | ||
| + | rm " | ||
| + | </ | ||
| + | |||
| + | ===== Заключение ===== | ||
| + | |||
| + | Данный скрипт обеспечивает: | ||
| + | |||
| + | - **Полное резервное копирование** всех данных Chatwoot | ||
| + | - **Автоматическое включение** новых файлов и папок в `/ | ||
| + | - **Безопасное выполнение** с остановкой и запуском контейнеров | ||
| + | - **Очистку старых бекапов** для экономии места | ||
| + | - **Детальное логирование** всех операций | ||
| + | - **Уведомления в Telegram** о статусе бекапа | ||
| + | - **Мониторинг места на диске** с предупреждениями | ||
| + | |||
| + | **Результат**: | ||
| + | |||
| + | ---- | ||
| + | |||
| + | // | ||