Пять групп ошибок закрывают почти все инциденты с файловыми менеджерами и облачными хранилищами: обрыв передачи на больших файлах, конфликты синхронизации при записи с двух хостов, ошибки 429 и SlowDown от API, дубликаты имён из-за разной нормализации Unicode и права доступа, которые не переносятся из локальной файловой системы в облако.
Причина чаще лежит на стыке клиента и сервера, а не внутри облака: короткий proxy_read_timeout на балансировщике, расхождение mtime из-за часового пояса, скрипт без retry, локаль без UTF-8, umask 022. Настройки клиента и сервера смотрят парой, иначе половина гипотез отпадает зря.
Начните с классификации симптома по таблице ниже, затем переходите в нужный раздел. Команды копируйте как есть, но значения таймаутов, квот и параметров API сверяйте с документацией провайдера и своей версией утилиты: они меняются.
Почему файловый менеджер и облако ломаются в продакшене
Как быстро классифицировать инцидент по симптому
| Симптом | Вероятная причина | Первая команда проверки | Раздел |
|---|---|---|---|
| Файл зависает на 99%, передача обрывается | Сработал таймаут или RST на прокси, MTU меньше 1500 | curl -v --max-time 30 -o /dev/null URL | Обрывы соединения |
| Появились file (1).txt, file.conflict | Конфликт синхронизации, last-write-wins по mtime | stat --format='%y %n' file | Конфликты синхронизации |
| 429 Too Many Requests, 503 SlowDown | Превышены лимиты API на бакет, префикс или пользователя | curl -D - -o /dev/null URL | Лимиты API |
| Имена с кракозябрами, один файл виден дважды | NFC/NFD, локаль без UTF-8 | locale; ls -b | hexdump -C | Кодировка имён |
| Нет доступа после синхронизации, файл виден снаружи | POSIX-права не переходят в ACL, публичный bucket | ls -la; aws s3api get-bucket-acl | Права доступа |
| Файл виден, но открывается частично | Незавершённая запись, синхронизация каталога живого приложения | sha256sum на обеих сторонах | Конфликты синхронизации |
Какие логи и метрики смотреть в первую очередь
До правки конфигов соберите факты. Для локальных процессов это journalctl -u имя-юнита -n 100 --no-pager (юнит, который запускает rclone, rsync или ваш сервис), dmesg -T | grep -i -E 'tcp|reset' и ss -s. Для клиентов включите подробный лог: rclone --log-level DEBUG --log-file /var/log/rclone.log, rsync -av --itemize-changes, curl -v.
В облаке смотрите audit logs и access logs провайдера: они показывают обращения к объектам независимо от вашей машины. Для S3-совместимых хранилищ сохраняйте из ответов заголовки x-amz-request-id и x-amz-id-2: с ними поддержка быстрее находит конкретный запрос в своих логах.
2026/10/02 09:14:22 ERROR : big.iso: Failed to copy: read tcp 10.0.3.14:41222->storage:443: i/o timeout 2026/10/02 09:14:23 ERROR : big.iso: Failed to copy: unexpected EOF 2026/10/02 09:14:25 NOTICE: big.iso: Received error: 429 Too Many Requests (Retry-After: 30) 2026/10/02 09:14:25 ERROR : big.iso: Listing error: 503 SlowDown
Читайте лог по ключевым словам, а не целиком:
grep -E 'ERROR|429|timeout|reset|EOF|SlowDown' /var/log/rclone.log | tail -20
Расшифровка: i/o timeout указывает на сеть или прокси, unexpected EOF означает, что соединение закрыл сервер на середине, 429 с Retry-After говорит о лимите запросов, 503 SlowDown - о перегрузке бакета или префикса. Это разные причины и разные исправления, поэтому не смешивайте их в одну гипотезу.
Обрывы соединения при передаче больших файлов
Обрыв на 40 ГБ почти никогда не связан с самим файлом. Типичные источники: proxy_read_timeout на nginx или балансировщике меньше времени передачи, MTU меньше 1500 на VPN и PPPoE-линке, потери пакетов с ретрансмитами, клиент без докачки, ограничение на размер тела запроса.
Диагностика сетевых причин: MTU, ретрансмиты, таймауты
ping -M do -s 1472 storage.example.com netstat -s | grep -i retrans ss -s tcpdump -i eth0 -nn 'tcp[tcpflags] & tcp-rst != 0' -c 20 curl -v --max-time 30 -o /dev/null https://storage.example.com
Если ping с -M do -s 1472 отвечает ошибкой вида Frag needed или Message too long, MTU на пути меньше 1500 и крупные сегменты не проходят. Снижайте MTU до 1400-1450 (ip link set dev eth0 mtu 1400) или настраивайте MSS clamping на роутере. Растущий счётчик retrans при свободной полосе говорит о потерях, а RST от прокси в большинстве случаев означает срабатывание idle-таймаута.
location /upload/ {
client_max_body_size 0;
client_body_timeout 300s;
proxy_request_buffering off;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
}
Директива client_max_body_size 0 снимает ограничение на размер тела запроса, proxy_request_buffering off не даёт nginx буферизовать гигабайты на диск. Таймауты задавайте с запасом к самой долгой передаче, иначе RST вернётся: для медленного канала 300s часто мало, считайте реальную скорость.
Настройка клиентов на докачку и повторные попытки
rclone copy big.iso s3:backup/ --retries 10 --low-level-retries 20 --timeout 5m --contimeout 1m --transfers 4 --s3-upload-cutoff 64M --s3-chunk-size 16M --progress rsync -av --partial --append-verify --timeout=300 /data/big.iso host:/data/ curl -C - --retry 5 --retry-delay 10 -o big.iso https://storage.example.com/bucket/big.iso
Ключи различаются по уровню: --retries повторяет прикладную операцию целиком, --low-level-retries переигрывает отдельные HTTP-запросы внутри неё. Multipart-загрузка в S3 (--s3-upload-cutoff и --s3-chunk-size) перезагружает только оборванную часть вместо всего файла, поэтому она устойчивее к обрывам. У rsync флаг --append-verify подходит для неизменяемых файлов, сжатые и шифрованные файлы проверить не получится, для них берите --partial-dir и полную повторную передачу.
Готовые команды для публикации артефактов, проверки контрольных сумм и повторных попыток при обрывах сети мы собрали отдельно: автоматизация загрузки и выгрузки файлов: API, CLI и интеграция с CI/CD.
Конфликты синхронизации: как не потерять данные
Синхронизация без блокировок работает по принципу last-write-wins: побеждает версия с более новым mtime. Проблемы начинаются, когда два хоста пишут в один каталог, часы разъехались или запись не завершилась. Копия без пометки конфликта тихо перетирает свежие данные.
Как определить, какая версия файла актуальна
stat --format='%y %n' report.pdf find /data -name '*.conflict' -o -name '* (1)*' -ls rclone lsl remote:bucket/data/ | head -20 sha256sum report.pdf timedatectl; date
Сравнивайте mtime вместе с размером и хешем. Если локальная версия новее на 5 минут, но заметно меньше по размеру, это скорее всего незавершённая запись, а целая версия осталась на удалённой стороне. При равных mtime rclone и rsync решают по размеру, поэтому размер как единственный критерий ненадёжен: добавляйте --checksum, когда важна точность, и всегда сверяйте часовые пояса через timedatectl, прежде чем доверять меткам времени.
Настройка правил разрешения конфликтов в rclone и rsync
rclone sync /data remote:bucket/data --update --checksum --backup-dir remote:bucket/_conflicts/2026-10-02 --log-file /var/log/rclone.log rsync -av --update --backup --backup-dir=/backup/$(date +%F) /data/ host:/data/
Флаг --update пропускает файлы, у которых mtime на источнике старше, а --backup-dir сохраняет перезаписанные версии вместо их безвозвратного удаления. Флаг --ignore-existing оставляет удалённую копию без изменений. При разнице в 2 часа между серверами любой last-write-wins превращается в лотерею, поэтому синхронизация времени обязательна: timedatectl set-ntp true, chronyc tracking, для контейнеров проверьте tzdata и переменную TZ внутри образа.
Каталоги данных живых приложений синхронизировать файловым менеджером нельзя. Для Moodle переносить каталог moodledata облачной синхронизацией опасно: полный бэкап собирают из базы данных (MySQL, MariaDB или PostgreSQL), каталога moodledata, кода и конфигурации (бэкапы Moodle: как не потерять данные).
Лимиты API облачных хранилищ: как не упереться в 429
Провайдеры ограничивают число запросов в секунду на бакет, на префикс и на пользователя, отдельно лимитируют операции листинга и трафик. Значения отличаются от сервиса к сервису и периодически меняются, поэтому проверяйте актуальные квоты в документации провайдера, а в коде опирайтесь на заголовки ответа, а не на зашитые константы.
Чтение заголовков лимитов и построение backoff
curl -sS -o /dev/null -D - -X PUT --upload-file file.bin https://storage.example.com/bucket/file.bin HTTP/1.1 429 Too Many Requests Retry-After: 30 x-ratelimit-remaining: 0 x-amz-request-id: 8Q2K4T1XR9WZ x-amz-id-2: 9c1fLpQ0aVv3Xm
Если сервер прислал Retry-After, ждите ровно столько или больше: это штатный сигнал, после которого запрос пройдёт. Шаблон с экспоненциальной паузой и джиттером не даёт параллельным воркерам бить по API синхронно:
retry() {
local n=0 max=6 delay
until "$@"; do
n=$((n+1))
if [ "$n" -ge "$max" ]; then return 1; fi
delay=$(( (1 << n) + RANDOM % 5 ))
sleep "$delay"
done
}
Разбор кодов ошибок, порогов алертов и логики circuit breaker для API мы делали на примере сервиса с жёсткими квотами: обработка ошибок и лимитов в API: практический гайд.
rclone умеет backoff сам, но при агрессивных настройках провоцирует 429: десятки параллельных передач легко упираются в лимит префикса. Начните с --transfers 4 --checkers 8 и поднимайте шагами, следя за логом.
Батчинг и мультипарт для снижения числа запросов
- Пакетное удаление: операция DeleteObjects в S3 API позволяет удалить несколько объектов одним HTTP-запросом, и запрос может содержать список до 1000 ключей. Это снижает накладные расходы на каждый запрос по сравнению с отправкой отдельных запросов на удаление. В CLI: aws s3 rm s3://bucket/prefix --recursive.
- Multipart upload: файл режется на части, и обрыв стоит перезагрузки одной части, а не всего объекта.
- Мелкие файлы: складывайте их в tar перед загрузкой. Десять тысяч файлов по 4 КБ дают десять тысяч PUT и столько же операций листинга.
- Batch-эндпоинты провайдеров: Google Drive API поддерживает пакетную обработку и позволяет объединить несколько вызовов API в один HTTP-запрос (не более 100 вызовов в одном пакете, все внутренние запросы должны идти к одному и тому же Google API). Для S3-совместимых хранилищ проверяйте наличие пакетных операций в документации конкретного провайдера.
- Распределение во времени: ночное окно, отдельные бакеты для бэкапа и продакшена, снижение параллелизма на время массовой выгрузки.
Кодировка имён файлов: почему появляются дубликаты и кракозябры
Ключ объекта в S3-совместимом хранилище - это байтовая строка. Unicode допускает несколько представлений одного имени, поэтому «Отчёт.pdf», собранный в NFC и в NFD, даёт два разных объекта, которые в файловом менеджере выглядят одинаково.
Разные файловые системы по-разному обращаются с нормализацией Unicode. Например, APFS не выполняет нормализацию, а сохраняет символы Unicode в том виде, в котором они переданы, независимо от того, нормализованы они или нет. Поэтому при синхронизации каталога между системами с разным поведением нормализации одно и то же имя может дать два разных объекта, и каждое следующее скачивание это подтверждает.
Диагностика кодировки и нормализации
locale; echo "$LANG"
ls -b | head
ls | hexdump -C | head -20
python3 -c 'import sys,unicodedata as u; s=sys.argv[1]; print(u.is_normalized("NFC", s), u.is_normalized("NFD", s), len(s))' "Отчёт.pdf"
find . -maxdepth 1 -print0 | xargs -0 -n1 printf '%q\n'
Вывод is_normalized() вместе с длиной строки показывает, какое представление лежит на диске. Утилиты ls -b и printf с форматом %q экранируют непечатаемые байты и обнажают комбинируемый символ, который визуально сливается с буквой.
Отдельный случай - архивы из Windows, где имена записаны в CP866 или CP1251. Возможности указать кодировку при распаковке зависят от сборки утилиты и версии: проверьте man для своего unzip и tar, потому что набор поддерживаемых ключей отличается между реализациями, иначе после распаковки ждите кракозябры.
Нормализация имён и настройка locale
convmv -f utf-8 -t utf-8 --nfc --notest -r /data convmv -f utf-8 -t utf-8 --nfc -r /data localectl set-locale LANG=en_US.UTF-8 rclone copy /data remote:bucket/data --local-encoding UTF-8 locale -a | grep -i utf
Сначала прогоните convmv с --notest: он только покажет список переименований, и вы убедитесь, что дубликатов не станет. Для S3 нормализуйте имена до загрузки, потому что переименование объекта в облаке - это copy плюс delete, то есть двойной трафик и два запроса на каждый файл.
Права доступа к файлам в облачном хранилище: аудит и исправление
POSIX-права и ACL в объектном хранилище - разные модели. chmod 640 на сервере не сделает объект приватным: у объекта свой владелец и собственные grants, наследования от каталога нет. Публичный доступ появляется тремя путями: ACL с allUsers, bucket policy с Principal «*» и отключённый Block Public Access.
Аудит публичного доступа в S3-совместимых хранилищах
aws s3api get-public-access-block --bucket my-bucket aws s3api get-bucket-acl --bucket my-bucket aws s3api get-bucket-policy --bucket my-bucket aws s3api get-object-acl --bucket my-bucket --key data/report.pdf yc storage bucket get my-bucket
Grantee AllUsers с разрешением READ означает, что бакет или объект открыт в интернет. Закрывайте сразу на двух уровнях:
aws s3api put-public-access-block --bucket my-bucket --public-access-block-configuration BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true aws s3api put-bucket-acl --bucket my-bucket --acl private
Настройка IgnorePublicAcls заставляет Amazon S3 игнорировать все публичные ACL на бакете и объектах в нём, а BlockPublicPolicy заставляет S3 отклонять вызовы PUT Bucket policy, если указанная политика разрешает публичный доступ. Важно: включение IgnorePublicAcls не влияет на сохранение существующих ACL и не предотвращает установку новых публичных ACL, а включение BlockPublicPolicy не влияет на уже существующие политики бакета. Настройки уровня бакета работают вместе с настройками уровня аккаунта, и если конфигурации различаются, Amazon S3 использует наиболее строгую комбинацию настроек уровня бакета и уровня аккаунта. Для Yandex Object Storage проверяйте аналогичные проверки и флаги публичного доступа через yc storage bucket get и консоль или CLI, сверяясь с документацией провайдера.
Настройка прав при синхронизации: umask, chmod, ACL
umask 027 chown -R app:app /data chmod -R 750 /data setfacl -R -m g:devs:rx /data getfacl /data/report.pdf rclone copy /data remote:bucket/data --s3-acl private --s3-no-check-bucket rsync -av --chmod=D750,F640 --chown=app:app /data/ host:/data/
В systemd задайте Umask=0027 прямо в unit, иначе демон получит дефолт 022 и создаст файлы, доступные на чтение всем. Ключ --s3-acl private проставляется на каждый загружаемый объект, но не отменяет bucket policy: проверяйте обе стороны. Чек-лист аудита:
- Закрыть публичный доступ: Block Public Access плюс ACL private.
- Раздавать права ролями IAM, а не точечными ACL на объекты.
- Задать umask 027 и Umask=0027 в systemd-юните.
- Проставить chmod 640/750 и проверить getfacl после синхронизации.
- Повторить get-bucket-acl через сутки: автоматика успевает создать новые объекты.
Инструменты и логи для быстрой локализации инцидента
Логи клиентов: rclone, rsync, curl
rclone copy /data remote:bucket --log-level DEBUG --log-file /var/log/rclone.log --stats 30s
grep -E 'ERROR|429|timeout|reset' /var/log/rclone.log | tail -20
rsync -av --itemize-changes --progress /data/ host:/data/
curl -v -o /dev/null -sS -w '%{http_code} %{time_total}\n' https://storage.example.com
Ключ --stats 30s даёт промежуточные метрики скорости и числа ошибок: по ним видно, деградирует ли передача постепенно или рвётся сразу. У rsync --itemize-changes показывает, что именно переносится, и помогает поймать файл, который уходит повторно из-за метаданных. Скрипты обвязки пишите с проверкой кодов возврата, иначе сбой шага пройдёт незамеченным: каркас bash-скрипта для Linux: set -euo pipefail и чек-лист.
Системные логи и сетевые утилиты
journalctl -u rclone-backup.service -n 100 --no-pager journalctl -k | grep -i -E 'tcp|conntrack|oom' dmesg -T | tail -50 ss -s netstat -s | grep -i -E 'retrans|reset' strace -f -p 12345 -e trace=network -tt lsof -i :443 -n -P tcpdump -i any -nn 'port 443 and tcp[tcpflags] & (tcp-rst|tcp-fin) != 0' -c 50
Растущий счётчик retrans при свободной полосе означает потери, а большое число reset указывает на сбросы соединений. Дамп tcpdump -w incident.pcap сохраните: его можно передать в поддержку провайдера вместе с x-amz-request-id.
| Симптом | Инструмент | Команда |
|---|---|---|
| Передача зависает без ошибок | tcpdump, ss | ss -s; tcpdump -i any -nn port 443 -c 50 |
| Локальный процесс не завершается | journalctl, strace | journalctl -u service -n 100; strace -f -p PID -e trace=network |
| Лимиты API | curl, лог rclone | curl -D - -o /dev/null URL; grep 429 /var/log/rclone.log |
| Дубликаты имён | locale, python3 | locale; python3 -c 'import unicodedata as u; print(u.is_normalized("NFC", s))' |
| Лишние права | aws cli, getfacl | aws s3api get-bucket-acl; getfacl /data |
Профилактика: как не повторять ошибки
Чек-лист перед массовой синхронизацией
rclone sync /data remote:bucket/data --dry-run -v --transfers 4
- Сделайте снимок или бэкап каталога перед первой большой синхронизацией.
- Прогоните операцию с --dry-run и прочитайте список изменений.
- Проверьте владельца и права целевого каталога: ls -la, getfacl.
- Убедитесь, что конфликтов нет: rclone check --one-way или rsync -av --dry-run.
- Ограничьте параллелизм: --transfers 4, чтобы не получить 429.
- Пишите вывод в лог с --log-file: без него разбор инцидента начнётся с нуля.
- Проведите операцию в окне обслуживания, когда приложения не пишут в каталог.
Тестирование восстановления и версионирование
aws s3api put-bucket-versioning --bucket my-bucket --versioning-configuration Status=Enabled aws s3api get-bucket-versioning --bucket my-bucket aws s3api list-object-versions --bucket my-bucket --prefix data/report.pdf
Версионирование защищает от ошибочного удаления и перезаписи, но копит неактуальные версии, поэтому добавьте lifecycle-правило на удаление старых версий по сроку. У Google Drive аналог - корзина и soft delete. Критерии выбора хранилища под командную работу, включая квоты, версионирование, SSO и права, разобраны в отдельном материале: как выбрать корпоративный файловый архив для IT-команды.
Резервная копия курса Moodle не заменяет полную копию системы: она сохраняет выбранные элементы курса и настройки, тогда как полный бэкап включает базу данных, moodledata, код и конфигурацию (бэкапы Moodle: как не потерять данные). Копия, которая ни разу не проходила тест восстановления, остаётся предположением, а не защитой, поэтому архив проверяют на отдельной среде и фиксируют результат проверки (источник по стратегии резервного копирования).
Снимок виртуальной машины ускоряет возврат к рабочей версии перед обновлением, но должен дополняться независимой копией: сбой хранилища может повредить и исходный сервер, и его снимок (там же о снимках и независимых копиях). Секреты не храните в открытом архиве без защиты. Итоговый набор привычек:
- Включить versioning и soft delete в облаке, настроить lifecycle для старых версий.
- Делать регулярные бэкапы и раз в квартал восстанавливать один файл на отдельной среде.
- Запускать массовые операции только после --dry-run.
- Мониторить ответы 429 и SlowDown, алертить по их появлению в логах.
- Нормализовать имена файлов до загрузки, а не после.
- Аудитить права и публичный доступ раз в квартал, включая новые объекты.
Начните с одного шага: прогоните rclone sync --dry-run по проблемному каталогу, включите versioning на тестовом бакете и один раз восстановите файл из версии. Дальше эти же проверки переносятся в остальные хранилища.