Типичные ошибки при работе с файловым менеджером и облачным хранилищем: диагностика, команды и решения | AdminWiki

Типичные ошибки при работе с файловым менеджером и облачным хранилищем: диагностика, команды и решения

02 октября 2026 14 мин. чтения
Содержание статьи

Пять групп ошибок закрывают почти все инциденты с файловыми менеджерами и облачными хранилищами: обрыв передачи на больших файлах, конфликты синхронизации при записи с двух хостов, ошибки 429 и SlowDown от API, дубликаты имён из-за разной нормализации Unicode и права доступа, которые не переносятся из локальной файловой системы в облако.

Причина чаще лежит на стыке клиента и сервера, а не внутри облака: короткий proxy_read_timeout на балансировщике, расхождение mtime из-за часового пояса, скрипт без retry, локаль без UTF-8, umask 022. Настройки клиента и сервера смотрят парой, иначе половина гипотез отпадает зря.

Начните с классификации симптома по таблице ниже, затем переходите в нужный раздел. Команды копируйте как есть, но значения таймаутов, квот и параметров API сверяйте с документацией провайдера и своей версией утилиты: они меняются.

Почему файловый менеджер и облако ломаются в продакшене

Как быстро классифицировать инцидент по симптому

СимптомВероятная причинаПервая команда проверкиРаздел
Файл зависает на 99%, передача обрываетсяСработал таймаут или RST на прокси, MTU меньше 1500curl -v --max-time 30 -o /dev/null URLОбрывы соединения
Появились file (1).txt, file.conflictКонфликт синхронизации, last-write-wins по mtimestat --format='%y %n' fileКонфликты синхронизации
429 Too Many Requests, 503 SlowDownПревышены лимиты API на бакет, префикс или пользователяcurl -D - -o /dev/null URLЛимиты API
Имена с кракозябрами, один файл виден дваждыNFC/NFD, локаль без UTF-8locale; ls -b | hexdump -CКодировка имён
Нет доступа после синхронизации, файл виден снаружиPOSIX-права не переходят в ACL, публичный bucketls -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: проверяйте обе стороны. Чек-лист аудита:

  1. Закрыть публичный доступ: Block Public Access плюс ACL private.
  2. Раздавать права ролями IAM, а не точечными ACL на объекты.
  3. Задать umask 027 и Umask=0027 в systemd-юните.
  4. Проставить chmod 640/750 и проверить getfacl после синхронизации.
  5. Повторить 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, ssss -s; tcpdump -i any -nn port 443 -c 50
Локальный процесс не завершаетсяjournalctl, stracejournalctl -u service -n 100; strace -f -p PID -e trace=network
Лимиты APIcurl, лог rclonecurl -D - -o /dev/null URL; grep 429 /var/log/rclone.log
Дубликаты имёнlocale, python3locale; python3 -c 'import unicodedata as u; print(u.is_normalized("NFC", s))'
Лишние праваaws cli, getfaclaws s3api get-bucket-acl; getfacl /data

Профилактика: как не повторять ошибки

Чек-лист перед массовой синхронизацией

rclone sync /data remote:bucket/data --dry-run -v --transfers 4
  1. Сделайте снимок или бэкап каталога перед первой большой синхронизацией.
  2. Прогоните операцию с --dry-run и прочитайте список изменений.
  3. Проверьте владельца и права целевого каталога: ls -la, getfacl.
  4. Убедитесь, что конфликтов нет: rclone check --one-way или rsync -av --dry-run.
  5. Ограничьте параллелизм: --transfers 4, чтобы не получить 429.
  6. Пишите вывод в лог с --log-file: без него разбор инцидента начнётся с нуля.
  7. Проведите операцию в окне обслуживания, когда приложения не пишут в каталог.

Тестирование восстановления и версионирование

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: как не потерять данные). Копия, которая ни разу не проходила тест восстановления, остаётся предположением, а не защитой, поэтому архив проверяют на отдельной среде и фиксируют результат проверки (источник по стратегии резервного копирования).

Снимок виртуальной машины ускоряет возврат к рабочей версии перед обновлением, но должен дополняться независимой копией: сбой хранилища может повредить и исходный сервер, и его снимок (там же о снимках и независимых копиях). Секреты не храните в открытом архиве без защиты. Итоговый набор привычек:

  1. Включить versioning и soft delete в облаке, настроить lifecycle для старых версий.
  2. Делать регулярные бэкапы и раз в квартал восстанавливать один файл на отдельной среде.
  3. Запускать массовые операции только после --dry-run.
  4. Мониторить ответы 429 и SlowDown, алертить по их появлению в логах.
  5. Нормализовать имена файлов до загрузки, а не после.
  6. Аудитить права и публичный доступ раз в квартал, включая новые объекты.

Начните с одного шага: прогоните rclone sync --dry-run по проблемному каталогу, включите versioning на тестовом бакете и один раз восстановите файл из версии. Дальше эти же проверки переносятся в остальные хранилища.

Поделиться: