Резервное копирование — это не «на всякий случай», а базовая защита от потери данных. Когда я только начинал администрировать тестовый сервер в Бугульме, первым делом после настройки SSH-доступа написал простой скрипт для копирования конфигов. Потому что потерять неделю правок из-за случайного rm -rf — это больно. На практике удобнее всего начинать с Python-скрипта, который копирует нужные папки в отдельный каталог, добавляет метку времени и, при необходимости, упаковывает копию в архив. Такой подход хорошо подходит для домашнего сервера, учебного Linux-хоста, рабочего проекта или небольшого сайта.
Что должен уметь хороший backup-скрипт
Перед написанием кода важно понять задачу. Скрипт резервного копирования не обязан быть сложным, но он должен делать несколько вещей стабильно:
- копировать исходные файлы и папки;
- не затирать старые копии;
- сохранять структуру каталогов;
- создавать понятное имя бэкапа;
- писать лог выполнения;
- корректно реагировать на ошибки;
- быть удобным для запуска по расписанию.
Если нужен минимальный рабочий вариант, достаточно копирования папки в новый каталог. Если задача ближе к реальной эксплуатации, лучше сразу добавить архивирование, timestamp в имени и исключения для временных файлов. В своей практике я всегда закладываю логирование с первого же прототипа — потом это экономит часы отладки, когда что-то идёт не так в три часа ночи.
Какие библиотеки Python использовать
Для резервного копирования обычно хватает стандартной библиотеки Python. Это плюс: не нужно ставить лишние зависимости, и скрипт можно запустить на любой машине, где есть интерпретатор.
| Модуль | Для чего нужен | Где полезен |
|---|---|---|
os |
Работа с путями, файлами и каталогами | Проверка существования папок, обход директорий |
shutil |
Копирование, архивирование, удаление | copy2, copytree, make_archive |
datetime |
Получение текущей даты и времени | Метка времени в имени бэкапа |
pathlib |
Более удобная работа с путями | Читабельный современный код |
logging |
Запись журнала работы | Отладка и контроль выполнения |
zipfile |
Создание ZIP-архивов | Когда нужен именно архив, а не папка-копия |
Для большинства задач достаточно shutil и datetime. Если нужен аккуратный и расширяемый код, лучше использовать pathlib — он делает работу с путями более явной и меньше шансов ошибиться при склеивании строк. В своих скриптах я почти всегда начинаю с from pathlib import Path.
Простой вариант: копируем папку целиком
Ниже базовый пример. Он копирует содержимое папки source в папку backup, добавляя в имя текущую дату и время.
import shutil
from datetime import datetime
from pathlib import Path
SOURCE_DIR = Path("/home/user/myproject")
BACKUP_ROOT = Path("/home/user/backups")
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
backup_name = f"backup_{timestamp}"
backup_path = BACKUP_ROOT / backup_name
shutil.copytree(SOURCE_DIR, backup_path)
print(f"Резервная копия создана: {backup_path}")
Что здесь происходит
SOURCE_DIR— папка, которую нужно сохранить.BACKUP_ROOT— каталог, куда складываются копии.datetime.now().strftime(...)— создает метку времени.shutil.copytree(...)— копирует папку целиком.
Это хороший старт, но у варианта есть ограничения. Он не умеет обновлять уже существующую копию и не подходит, если вы хотите хранить бэкап в виде одного файла-архива. Кроме того, copytree требует, чтобы целевая папка не существовала — иначе получите исключение. В реальном скрипте я бы сначала проверил, нет ли уже такого каталога, и при необходимости добавил суффикс или удалил старый.
Когда лучше делать архив, а не папку
Архив удобнее в нескольких случаях:
- нужно отправить копию на другой сервер;
- важно экономить место;
- копию надо хранить как единый файл;
- планируется выгрузка в облако или на внешний диск;
- бэкап нужно легко переносить.
Для этого подойдет shutil.make_archive() или работа через zipfile.
Пример создания ZIP-архива
import shutil
from datetime import datetime
from pathlib import Path
SOURCE_DIR = Path("/home/user/myproject")
BACKUP_ROOT = Path("/home/user/backups")
BACKUP_ROOT.mkdir(parents=True, exist_ok=True)
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
archive_name = f"backup_{timestamp}"
archive_path = BACKUP_ROOT / archive_name
shutil.make_archive(str(archive_path), 'zip', SOURCE_DIR)
print(f"Архив создан: {archive_path}.zip")
Плюсы этого варианта
- один файл вместо дерева каталогов;
- проще передавать и хранить;
- удобно делать ежедневные копии;
- меньше шанс случайно изменить бэкап вручную.
На практике я часто использую именно архив, особенно когда нужно перебросить бэкап по scp на другой хост. Один файл передаётся быстрее, чем тысячи мелких, да и контрольная сумма считается проще.
Как добавить исключения
На практике нельзя копировать вообще всё подряд. Обычно стоит исключать:
- временные файлы;
- кэш;
- логи;
- виртуальные окружения;
node_modules;- крупные каталоги, которые можно восстановить отдельно.
В стандартной библиотеке нет универсального «умного» фильтра для copytree, но исключения можно реализовать вручную. Например, через обход дерева файлов и фильтрацию по маскам.
Идея для исключений
import shutil
from pathlib import Path
EXCLUDE_PATTERNS = {'.git', '__pycache__', 'node_modules', '*.log', '*.tmp'}
def ignore_func(directory, contents):
ignored = []
for item in contents:
full_path = Path(directory) / item
if full_path.name in EXCLUDE_PATTERNS or full_path.suffix in {'.log', '.tmp'}:
ignored.append(item)
return ignored
shutil.copytree(SOURCE_DIR, backup_path, ignore=ignore_func)
Такой подход даёт гибкость: можно добавлять любые условия, например, исключать файлы больше определённого размера или старше N дней. В боевых скриптах я часто выношу список исключений в отдельный конфигурационный файл, чтобы не лезть в код каждый раз.
Почему copy2 лучше, чем copy
shutil.copy() копирует только содержимое и базовые права доступа.shutil.copy2() дополнительно старается сохранить метаданные: время модификации и другие атрибуты, если это возможно.
Для резервного копирования copy2 обычно предпочтительнее, потому что:
- копия ближе к оригиналу;
- проще сравнивать изменения;
- меньше сюрпризов при восстановлении.
Когда я восстанавливаю файлы из бэкапа, мне важно видеть оригинальные даты правок — это помогает понять, что это за версия. Поэтому в своих скриптах я всегда использую copy2.
Как сделать скрипт полезным в реальной жизни
Если скрипт нужен не только для тестов, добавьте несколько обязательных вещей.
1. Проверка путей
Перед запуском убедитесь, что исходная папка существует, а каталог для бэкапов доступен на запись. Простейшая проверка:
if not SOURCE_DIR.exists():
raise FileNotFoundError(f"Исходная папка не найдена: {SOURCE_DIR}")
BACKUP_ROOT.mkdir(parents=True, exist_ok=True)
if not os.access(BACKUP_ROOT, os.W_OK):
raise PermissionError(f"Нет прав на запись в {BACKUP_ROOT}")
2. Логирование
Без логов сложно понять, что именно сломалось. Даже простой журнал в файл уже полезен.
import logging
logging.basicConfig(
filename='/var/log/backup.log',
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logging.info("Запуск резервного копирования")
try:
shutil.copytree(SOURCE_DIR, backup_path)
logging.info(f"Успешно: {backup_path}")
except Exception as e:
logging.error(f"Ошибка: {e}")
3. Обработка ошибок
Если место на диске закончилось или файл заблокирован, скрипт не должен падать молча.
try:
shutil.make_archive(str(archive_path), 'zip', SOURCE_DIR)
except FileNotFoundError as e:
logging.error(f"Файл не найден: {e}")
except PermissionError as e:
logging.error(f"Нет доступа: {e}")
except Exception as e:
logging.error(f"Неожиданная ошибка: {e}")
4. Проверка свободного места
Это особенно важно для больших каталогов. Иначе скрипт начнет копирование и оборвется на середине. Можно использовать shutil.disk_usage и сравнить с ожидаемым размером исходной папки.
5. Понятная структура папок
Хорошая практика — хранить бэкапы в отдельной иерархии:
backups/project-name/2026-10-05_02-00-00.zipbackups/server-config/2026-10-05_03-00-00/
Так проще искать нужную копию и удалять старые версии. Я обычно добавляю ещё подпапку с именем хоста, если скрипт бегает на нескольких машинах и складывает бэкапы в общее хранилище.
Пошаговый алгоритм написания backup-скрипта
- Определите, что именно нужно копировать.
- Выберите формат хранения: папка или архив.
- Добавьте метку времени в имя бэкапа.
- Проверьте наличие исходной папки.
- Создайте каталог назначения.
- Скопируйте данные с сохранением структуры.
- Запишите результат в лог.
- Добавьте обработку ошибок.
- Протестируйте восстановление.
- Настройте запуск по расписанию.
Этот порядок я рекомендую новичкам на своих занятиях. Он помогает не упустить критически важные моменты и сразу получить надёжный инструмент, а не «поделку на коленке».
Типовые ошибки новичков
| Ошибка | Почему это плохо | Как сделать правильно |
|---|---|---|
| Бэкап в ту же папку, где лежат исходные файлы | Можно случайно копировать копию и раздувать каталог | Выносить резервные копии отдельно |
| Перезапись одного и того же файла | Старые копии теряются | Добавлять timestamp |
| Копирование всего подряд | Лишний мусор и большой размер | Исключать временные и служебные каталоги |
| Отсутствие логов | Непонятно, что произошло при сбое | Писать журнал выполнения |
| Нет проверки восстановления | Бэкап есть, но пользоваться им нельзя | Периодически делать тестовый restore |
Отдельно отмечу: многие новички думают, что если скрипт не выдал ошибок, то всё хорошо. Но молчание — не признак успеха. Всегда проверяйте целостность копии, хотя бы сравнивая количество файлов и общий размер.
Как проверить, что бэкап действительно работает
Самая частая ошибка — считать успешным любой скрипт, который «ничего не выдал в консоль». Проверять нужно иначе.
Минимальный тест
- создайте тестовую папку с несколькими файлами;
- запустите скрипт;
- сравните размер и структуру исходной и резервной копии;
- откройте один-два файла из бэкапа;
- удалите тестовый файл и попробуйте восстановить его из копии.
Если скрипт делает архив, проверьте еще и распаковку. Я обычно автоматизирую такой тест: отдельный скрипт создаёт тестовые данные, запускает бэкап, удаляет оригинал и восстанавливает, а потом сравнивает контрольные суммы. Это даёт уверенность, что в ночь перед дедлайном бэкап не подведёт.
Как запускать резервное копирование автоматически
Для Linux самый распространенный вариант — cron. Это особенно удобно на Ubuntu-серверах и домашних машинах.
Пример записи в crontab:
0 2 * * * /usr/bin/python3 /home/user/backup.py >> /var/log/backup_cron.log 2>&1
Что это значит:
- запуск каждый день в 02:00;
- вывод пишется в лог;
- ошибки тоже попадают в файл.
Для Windows обычно используют «Планировщик заданий». В обоих случаях важно убедиться, что скрипт запускается от имени пользователя с достаточными правами и что переменные окружения (например, PATH) настроены корректно. Частая проблема — скрипт работает из терминала, но не работает из cron, потому что не видит нужных библиотек или путей. Я всегда прописываю полные пути к интерпретатору и файлам, а в самом скрипте использую абсолютные пути.
Практический совет: начните с малого
Если задача учебная или вам нужен первый рабочий вариант, не пытайтесь сразу сделать «комбайн» на все случаи жизни. Для старта достаточно такого набора:
pathlibдля путей;shutil.copy2()илиshutil.make_archive()для копирования;datetimeдля имени;loggingдля журнала;cronдля запуска по расписанию.
А уже потом можно добавлять:
- инкрементальные копии;
- ротацию старых бэкапов;
- шифрование;
- выгрузку на удаленный сервер;
- резервирование баз данных и конфигов отдельно.
Мой первый production-скрипт был именно таким — простым, но надёжным. Он до сих пор работает на одном из серверов, только оброс дополнительными проверками и уведомлениями в Telegram.
Когда одного Python-скрипта мало
Python-скрипт отлично подходит для локальных файлов, конфигов, учебных проектов и небольших серверов. Но если речь идет о важных данных, одной копии недостаточно. В нормальной схеме должны быть:
- минимум две копии;
- разные носители;
- понятный срок хранения;
- периодическая проверка восстановления;
- отдельное хранение критичных данных.
Для серверов, где важна надежность, Python-скрипт часто становится не заменой, а надстройкой над общей политикой резервного копирования. Например, он может собирать конфиги и дампы баз, а затем rsync или rclone синхронизирует их в облако. Главное — не полагаться на единственный метод.
Вывод
Написать Python-скрипт для резервного копирования несложно, если сразу решить, что именно вы хотите получить: папку-копию или ZIP-архив. Для большинства задач хватает стандартной библиотеки, метки времени, логирования и проверки ошибок. Если сделать скрипт аккуратно с самого начала, он станет не одноразовым примером, а реальным рабочим инструментом. И помните: бэкап, который ни разу не проверяли восстановлением, — это не бэкап, а надежда.
FAQ
Какой модуль Python лучше всего подходит для бэкапа?
Для простого копирования обычно хватает shutil. Для работы с путями удобен pathlib, для именования копий — datetime.
Что лучше: папка или ZIP-архив?
Если нужен быстрый перенос и экономия места, лучше ZIP. Если важна простота восстановления и прозрачная структура, подойдет папка.
Как не копировать лишние файлы?
Добавьте список исключений: временные файлы, логи, кэш, __pycache__, .git, node_modules.
Как часто делать резервную копию?
Зависит от ценности данных. Для рабочих файлов — ежедневно или чаще. Для учебных проектов может хватать еженедельного графика.
Можно ли использовать такой скрипт для сервера?
Да, если объем данных небольшой и понятны границы ответственности. Для критичных систем нужен более строгий подход: ротация, проверка, несколько уровней хранения и тестовое восстановление.
