====== Автоматическое резервное копирование Chatwoot ======
===== Описание =====
Система автоматического резервного копирования для Chatwoot, которая создает полные бекапы всех данных, включая базу данных PostgreSQL, Redis, файлы медиа и конфигурации.
**ВАЖНО**: Скрипт автоматически включает ВСЁ содержимое папки `/opt/chatwoot/chatwoot_data/` - любые новые файлы и папки попадут в бекап автоматически.
===== Что включается в бекап =====
^ Компонент ^ Описание ^ Расположение ^
| База данных PostgreSQL | Полный дамп схемы и данных | `/opt/chatwoot/chatwoot_data/postgres_data/` |
| Redis данные | Дамп всех ключей и значений | `/opt/chatwoot/chatwoot_data/redis_data/` |
| Медиа файлы | Загруженные файлы пользователей | `/opt/chatwoot/chatwoot_data/storage/` |
| Конфигурация времени | Настройки часового пояса | `/opt/chatwoot/chatwoot_data/config/` |
| Docker конфигурация | docker-compose.yml и .env | `/opt/chatwoot/chatwoot_dock/` |
| **Все новые папки** | Автоматически | `/opt/chatwoot/chatwoot_data/*` |
**Преимущество**: При добавлении новых папок в `/opt/chatwoot/chatwoot_data/` они автоматически попадут в следующий бекап без изменения скрипта.
===== Структура проекта =====
/opt/
├── chatwoot/
│ ├── chatwoot_dock/ # Docker Compose проект
│ │ ├── docker-compose.yml # Конфигурация контейнеров
│ │ └── .env # Переменные окружения
│ └── chatwoot_data/ # Все данные приложения
│ ├── postgres_data/ # База данных
│ ├── redis_data/ # Кэш
│ ├── storage/ # Медиа файлы
│ └── config/ # Пользовательские конфигурации
├── backups/
│ └── chatwoot/ # Папка для бекапов
└── scripts/
└── chatwoot-backup.sh # Скрипт резервного копирования
===== Установка и настройка =====
==== Шаг 1: Создание структуры папок ====
# Создание необходимых папок
sudo mkdir -p /opt/scripts
sudo mkdir -p /opt/backups/chatwoot
sudo chown -R $USER:$USER /opt/backups
sudo chown -R $USER:$USER /opt/scripts
==== Шаг 2: Создание скрипта ====
Создайте файл `/opt/scripts/chatwoot-backup.sh`:
sudo nano /opt/scripts/chatwoot-backup.sh
Вставьте следующий код, заменив пароли на свои:
#!/bin/bash
# Скрипт резервного копирования Chatwoot
# Запуск: /opt/scripts/chatwoot-backup.sh
# Настройки
COMPOSE_FILE="/opt/chatwoot/chatwoot_dock/docker-compose.yml"
CHATWOOT_DIR="/opt/chatwoot/chatwoot_dock"
BACKUP_DIR="/opt/backups/chatwoot"
DATE=$(date +"%Y%m%d_%H%M%S")
BACKUP_NAME="chatwoot_backup_${DATE}"
RETENTION_DAYS=10 # Храним бекапы 10 дней
# Настройки базы данных - ИЗМЕНИТЕ НА СВОИ
DB_CONTAINER="chatwoot_postgres"
DB_USER="postgres"
DB_PASSWORD="YOUR_POSTGRES_PASSWORD" # Замените на ваш пароль
DB_NAME="chatwoot_production"
# Настройки Redis - ИЗМЕНИТЕ НА СВОИ
REDIS_CONTAINER="chatwoot_redis"
REDIS_PASSWORD="YOUR_REDIS_PASSWORD" # Замените на ваш пароль
# Логирование
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 "=== Начало резервного копирования Chatwoot ==="
# Проверяем что compose файл существует
if [ ! -f "$COMPOSE_FILE" ]; then
log "ОШИБКА: Файл $COMPOSE_FILE не найден!"
exit 1
fi
# Переходим в директорию с Chatwoot
cd "$CHATWOOT_DIR" || {
log "ОШИБКА: Не удалось перейти в директорию $CHATWOOT_DIR"
exit 1
}
# Проверяем что контейнеры запущены
log "Проверяем статус контейнеров..."
if ! docker-compose -f "$COMPOSE_FILE" ps | grep -q "Up"; then
log "ОШИБКА: Контейнеры не запущены! Запустите их перед созданием бекапа."
exit 1
fi
# Показываем что будет включено в бекап
log "Анализируем содержимое /opt/chatwoot/chatwoot_data/..."
CHATWOOT_DATA_CONTENTS=$(ls -la /opt/chatwoot/chatwoot_data/ 2>/dev/null | grep -v "^total" | grep -v "^\." | awk '{print $9}' | grep -v "^$" | tr '\n' ' ')
log "Будет включено в бекап: $CHATWOOT_DATA_CONTENTS"
# Создаем дамп базы данных (пока контейнеры работают)
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
# Создаем дамп Redis
log "Создаем дамп Redis..."
REDIS_DUMP_PATH="${BACKUP_DIR}/${BACKUP_NAME}_redis.rdb"
# Создаем дамп внутри контейнера во временный файл
REDIS_TEMP_PATH="/tmp/redis_dump.rdb"
docker exec "$REDIS_CONTAINER" redis-cli -a "$REDIS_PASSWORD" --rdb "$REDIS_TEMP_PATH" 2>> "$LOG_FILE"
if [ $? -eq 0 ]; then
# Копируем дамп из контейнера
docker cp "$REDIS_CONTAINER:$REDIS_TEMP_PATH" "$REDIS_DUMP_PATH" 2>> "$LOG_FILE"
if [ -f "$REDIS_DUMP_PATH" ]; then
log "Дамп Redis создан: $REDIS_DUMP_PATH"
REDIS_DUMP_SIZE=$(du -h "$REDIS_DUMP_PATH" | cut -f1)
log "Размер дампа Redis: $REDIS_DUMP_SIZE"
# Удаляем временный файл из контейнера
docker exec "$REDIS_CONTAINER" rm -f "$REDIS_TEMP_PATH" 2>/dev/null
else
log "ПРЕДУПРЕЖДЕНИЕ: Файл дампа Redis не найден"
REDIS_DUMP_SIZE="0B"
fi
else
log "ПРЕДУПРЕЖДЕНИЕ: Не удалось создать дамп Redis"
REDIS_DUMP_SIZE="0B"
fi
# Очищаем переменную окружения
unset PGPASSWORD
# Останавливаем контейнеры
log "Останавливаем контейнеры Chatwoot..."
docker-compose -f "$COMPOSE_FILE" down
if [ $? -eq 0 ]; then
log "Контейнеры успешно остановлены"
else
log "ОШИБКА: Не удалось остановить контейнеры"
exit 1
fi
# Ждем пару секунд для полной остановки
sleep 5
# Создаем архив с данными - АВТОМАТИЧЕСКИ ВКЛЮЧАЕТ ВСЁ СОДЕРЖИМОЕ chatwoot_data
log "Создаем архив данных (включает ВСЁ содержимое chatwoot_data)..."
BACKUP_PATH="${BACKUP_DIR}/${BACKUP_NAME}.tar.gz"
# Создаем архив с автоматическим включением ВСЕГО содержимого /opt/chatwoot/chatwoot_data/
tar -czf "$BACKUP_PATH" \
--exclude='*.log' \
--exclude='**/lost+found' \
--exclude='**/.tmp' \
--exclude='**/tmp/*' \
--exclude='**/.cache' \
-C "$BACKUP_DIR" "${BACKUP_NAME}_database.sql" \
$([ -f "$REDIS_DUMP_PATH" ] && echo "-C $BACKUP_DIR ${BACKUP_NAME}_redis.rdb") \
-C "/opt/chatwoot" \
chatwoot_data/ \
-C "$CHATWOOT_DIR" \
docker-compose.yml \
.env
if [ $? -eq 0 ]; then
log "Архив создан: $BACKUP_PATH"
# Получаем размер архива
BACKUP_SIZE=$(du -h "$BACKUP_PATH" | cut -f1)
log "Размер архива: $BACKUP_SIZE"
# Удаляем отдельные файлы дампов, так как они теперь в архиве
rm -f "$DB_DUMP_PATH"
[ -f "$REDIS_DUMP_PATH" ] && rm -f "$REDIS_DUMP_PATH"
log "Отдельные файлы дампов удалены (включены в архив)"
# Показываем что попало в архив
log "Содержимое архива (основные папки и файлы):"
tar -tzf "$BACKUP_PATH" | head -20 | while read file; do
log " $file"
done
TOTAL_FILES=$(tar -tzf "$BACKUP_PATH" | wc -l)
log "Общее количество файлов в архиве: $TOTAL_FILES"
else
log "ОШИБКА: Не удалось создать архив"
# Пытаемся запустить контейнеры даже при ошибке
log "Запускаем контейнеры после ошибки..."
docker-compose -f "$COMPOSE_FILE" up -d
exit 1
fi
# Запускаем контейнеры обратно
log "Запускаем контейнеры Chatwoot..."
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
# Проверяем что база данных Chatwoot содержит нужные таблицы
log "Проверяем структуру базы данных Chatwoot..."
if docker exec "$DB_CONTAINER" psql -U "$DB_USER" -d "$DB_NAME" -c "\dt" 2>/dev/null | grep -q "accounts\|users\|conversations"; then
log "База данных Chatwoot содержит основные таблицы"
else
log "ПРЕДУПРЕЖДЕНИЕ: База данных Chatwoot может быть неполной"
fi
# Проверяем Redis контейнер
log "Проверяем Redis..."
if docker exec "$REDIS_CONTAINER" redis-cli -a "$REDIS_PASSWORD" ping 2>/dev/null | grep -q "PONG"; then
log "Redis работает корректно"
else
log "ПРЕДУПРЕЖДЕНИЕ: Redis может работать некорректно"
fi
# Проверяем Rails контейнер
log "Проверяем Rails приложение..."
if docker exec chatwoot_rails bundle exec rails runner "puts 'Chatwoot OK'" 2>/dev/null | grep -q "Chatwoot OK"; then
log "Rails приложение работает корректно"
else
log "ПРЕДУПРЕЖДЕНИЕ: Rails приложение может работать некорректно"
fi
unset PGPASSWORD
# Удаляем старые бекапы
log "Удаляем бекапы старше $RETENTION_DAYS дней..."
DELETED_COUNT=$(find "$BACKUP_DIR" -name "chatwoot_backup_*.tar.gz" -mtime +$RETENTION_DAYS -delete -print | wc -l)
log "Удалено старых бекапов: $DELETED_COUNT"
# Показываем статистику бекапов
BACKUP_COUNT=$(ls -1 "${BACKUP_DIR}"/chatwoot_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="✅ Chatwoot backup completed
📁 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 [ "$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 (опционально)
# Замените на свой BOT_TOKEN и CHAT_ID
curl -s -X POST "https://api.telegram.org/bot[YOUR_BOT_TOKEN]/sendMessage" \
-d chat_id="[YOUR_CHAT_ID]" \
-d text="$TELEGRAM_MESSAGE" > /dev/null
exit 0
**Обязательно измените**:
* `YOUR_POSTGRES_PASSWORD` на ваш пароль PostgreSQL
* `YOUR_REDIS_PASSWORD` на ваш пароль Redis
* `[YOUR_BOT_TOKEN]` на токен вашего Telegram бота (опционально)
* `[YOUR_CHAT_ID]` на ваш Chat ID в Telegram (опционально)
==== Шаг 3: Настройка прав доступа ====
# Делаем скрипт исполняемым
sudo chmod +x /opt/scripts/chatwoot-backup.sh
# Проверяем права
ls -la /opt/scripts/chatwoot-backup.sh
==== Шаг 4: Тестовый запуск ====
# Запуск тестового бекапа
sudo /opt/scripts/chatwoot-backup.sh
# Проверка результата
ls -la /opt/backups/chatwoot/
cat /opt/backups/chatwoot/backup.log
===== Автоматизация через Cron =====
==== Настройка ежедневного бекапа ====
# Открываем crontab
sudo crontab -e
# Добавляем задачу (бекап каждый день в 2:00 ночи)
0 2 * * * /opt/scripts/chatwoot-backup.sh >> /var/log/chatwoot-backup-cron.log 2>&1
# Сохраняем и выходим (Ctrl+X, Y, Enter)
==== Альтернативные расписания ====
# Каждый день в 3:00
0 3 * * * /opt/scripts/chatwoot-backup.sh
# Каждые 12 часов (в 6:00 и 18:00)
0 6,18 * * * /opt/scripts/chatwoot-backup.sh
# Каждый день в 1:30 ночи
30 1 * * * /opt/scripts/chatwoot-backup.sh
# Только в выходные в 4:00
0 4 * * 6,0 /opt/scripts/chatwoot-backup.sh
==== Проверка работы Cron ====
# Проверить статус cron
sudo systemctl status cron
# Посмотреть активные задачи
sudo crontab -l
# Посмотреть логи cron
sudo tail -f /var/log/chatwoot-backup-cron.log
===== Мониторинг и уведомления =====
==== Настройка Telegram бота ====
1. **Создайте бота**: Отправьте `/start` боту @BotFather в Telegram
2. **Получите токен**: Сохраните Bot Token
3. **Узнайте Chat ID**: Отправьте сообщение боту, затем откройте:
`https://api.telegram.org/bot[YOUR_BOT_TOKEN]/getUpdates`
==== Проверка уведомлений ====
# Тест отправки сообщения (замените токен и chat_id)
curl -s -X POST "https://api.telegram.org/bot[YOUR_BOT_TOKEN]/sendMessage" \
-d chat_id="[YOUR_CHAT_ID]" \
-d text="Test message from Chatwoot backup script"
==== Мониторинг места на диске ====
# Проверка места в папке бекапов
df -h /opt/backups/chatwoot/
# Размер всех бекапов
du -sh /opt/backups/chatwoot/
# Список всех бекапов с размерами
ls -lah /opt/backups/chatwoot/chatwoot_backup_*.tar.gz
===== Восстановление из бекапа =====
==== Полное восстановление ====
# Остановите Chatwoot
cd /opt/chatwoot/chatwoot_dock
docker-compose down
# Найдите нужный бекап
ls -la /opt/backups/chatwoot/
# Извлеките архив (замените на ваш файл)
cd /tmp
tar -xzf /opt/backups/chatwoot/chatwoot_backup_YYYYMMDD_HHMMSS.tar.gz
# Восстановите данные
sudo rm -rf /opt/chatwoot/chatwoot_data/*
sudo cp -r chatwoot_data/* /opt/chatwoot/chatwoot_data/
# Восстановите конфигурацию (если нужно)
cp docker-compose.yml /opt/chatwoot/chatwoot_dock/
cp .env /opt/chatwoot/chatwoot_dock/
# Запустите Chatwoot
cd /opt/chatwoot/chatwoot_dock
docker-compose up -d
==== Восстановление только базы данных ====
# Найдите дамп базы в архиве
tar -tzf /opt/backups/chatwoot/chatwoot_backup_YYYYMMDD_HHMMSS.tar.gz | grep database.sql
# Извлеките дамп
tar -xzf /opt/backups/chatwoot/chatwoot_backup_YYYYMMDD_HHMMSS.tar.gz chatwoot_backup_YYYYMMDD_HHMMSS_database.sql
# Восстановите в контейнер (Chatwoot должен быть запущен)
export PGPASSWORD="YOUR_POSTGRES_PASSWORD"
cat chatwoot_backup_YYYYMMDD_HHMMSS_database.sql | docker exec -i chatwoot_postgres psql -U postgres -d chatwoot_production
unset PGPASSWORD
===== Устранение неполадок =====
==== Основные проблемы ====
=== Ошибка "Container not found" ===
**Причина**: Неправильные имена контейнеров
**Решение**: Проверьте имена контейнеров:
docker ps
# Обновите переменные DB_CONTAINER и REDIS_CONTAINER в скрипте
=== Ошибка доступа к базе данных ===
**Причина**: Неправильный пароль или имя базы
**Решение**: Проверьте настройки в `.env` файле:
cat /opt/chatwoot/chatwoot_dock/.env | grep -i postgres
# Обновите переменные в скрипте согласно .env
=== Нехватка места на диске ===
**Причина**: Заполнен диск
**Решение**:
# Удалите старые бекапы вручную
find /opt/backups/chatwoot/ -name "chatwoot_backup_*.tar.gz" -mtime +7 -delete
# Уменьшите RETENTION_DAYS в скрипте
==== Проверка логов ====
# Основной лог скрипта
tail -f /opt/backups/chatwoot/backup.log
# Лог cron
tail -f /var/log/chatwoot-backup-cron.log
# Логи Docker контейнеров
docker logs chatwoot_postgres
docker logs chatwoot_redis
===== Дополнительные настройки =====
==== Изменение времени хранения ====
В скрипте измените:
RETENTION_DAYS=10 # Храним бекапы 10 дней
==== Изменение папки бекапов ====
В скрипте измените:
BACKUP_DIR="/opt/backups/chatwoot"
**Рекомендация**: Для больших проектов рассмотрите возможность хранения бекапов на отдельном диске или в облачном хранилище.
===== Безопасность =====
==== Защита скрипта ====
# Ограничить доступ только root
sudo chown root:root /opt/scripts/chatwoot-backup.sh
sudo chmod 700 /opt/scripts/chatwoot-backup.sh
# Проверить права
ls -la /opt/scripts/chatwoot-backup.sh
==== Шифрование бекапов ====
Для дополнительной безопасности можно добавить шифрование:
# Добавьте в скрипт после создания архива:
gpg --symmetric --cipher-algo AES256 "$BACKUP_PATH"
rm "$BACKUP_PATH" # Удаляем незашифрованную версию
===== Заключение =====
Данный скрипт обеспечивает:
- **Полное резервное копирование** всех данных Chatwoot
- **Автоматическое включение** новых файлов и папок в `/opt/chatwoot/chatwoot_data/`
- **Безопасное выполнение** с остановкой и запуском контейнеров
- **Очистку старых бекапов** для экономии места
- **Детальное логирование** всех операций
- **Уведомления в Telegram** о статусе бекапа
- **Мониторинг места на диске** с предупреждениями
**Результат**: Надежная система резервного копирования, которая автоматически адаптируется к изменениям в структуре данных Chatwoot.
----
//Инструкция протестирована на Ubuntu Server с Docker Compose. Регулярно проверяйте работоспособность бекапов и процедуры восстановления.//