Куда сохранять файлы приложения: тома, S3 и деплой в Inquir
Почему файлы исчезают после пересоздания контейнера, когда нужен постоянный том и как проверить загрузки, права доступа и восстановление в Inquir.
Пользователь загрузил документ, API вернул успех, а после нового деплоя ссылка перестала работать. Такое случается, когда файл записывают в каталог приложения и принимают файловую систему текущего контейнера за постоянное хранилище.
Перед первым рабочим релизом решите, какие данные можно пересоздать, какие нужно сохранить и кто должен иметь к ним доступ. Для небольшого сервиса выбор часто сводится к постоянному тому или объектному хранилищу с S3-совместимым API. Они решают разные задачи и по-разному влияют на обновление приложения.
Разделите три вида файлов
Статические файлы сборки — JavaScript, CSS, шаблоны — входят в образ и меняются вместе с релизом. Временные данные, например промежуточный архив, могут жить в рабочем каталоге, если приложение умеет повторить операцию после потери файла. Пользовательские документы и результаты, которые нельзя воспроизвести, должны иметь отдельный жизненный цикл.
Не делайте вывод о сохранности данных по обычному перезапуску процесса. Проверка должна включать замену контейнера новым релизом. И не путайте сохранение файла после пересоздания с защитой от удаления, повреждения или потери машины: это уже задача резервного копирования и восстановления.
Когда подходит постоянный том
Том удобен, если приложение ожидает обычные пути файловой системы: /data/uploads/report.pdf, создание каталогов, чтение через стандартный файловый API. Общая идея отделения жизненного цикла данных от контейнера описана в документации Docker о томах; команды управления Docker на хосте не нужно переносить в Inquir буквально.
В стандартном режиме Inquir приложение с записываемым томом работает с одной репликой. Stateful-режим — отдельная возможность, где у реплик собственные тома; это не общий каталог загрузок. Если нужен доступ нескольких экземпляров API к одним документам, заранее выберите подходящее совместно доступное хранилище.
Убедитесь, что пользователь процесса может писать в точку монтирования. COPY --chown в Dockerfile не гарантирует владельца данных на уже подключённом томе. При ошибке EACCES проверьте фактические UID/GID и права каталога; не исправляйте её безусловным chmod 777.
Подключите том в Inquir
Предположим, ваше приложение уже умеет брать путь из UPLOAD_DIR, слушает порт 3000 и отвечает на /healthz. После настройки CLI создайте его с томом:
inquir apps create uploads-api --ingress public --port 3000 --health-path /healthz --volume uploads:/data --set UPLOAD_DIR=/data/uploads
inquir apps deploy uploads-api --promote
inquir apps status uploads-api
Это конфигурация существующего кода, а не готовая реализация загрузчика. UPLOAD_DIR не создаёт HTTP-маршрут автоматически. Приложение должно создать вложенный каталог при наличии прав, ограничить размер загрузки и сохранять только разрешённые данные.
Имя тома и путь задаются отдельно: uploads — имя, /data — точка монтирования. Не монтируйте том поверх всего /app, иначе можете скрыть файлы кода из образа. При следующих релизах сохраняйте ту же конфигурацию тома. Состояние хранилища проверяйте в разделе Volumes приложения и по документации приложений.
Когда выбрать объектное хранилище
Объектное хранилище удобнее для нескольких экземпляров API, больших файлов и независимого жизненного цикла документов. В базе храните ключ объекта, владельца, размер, контрольную сумму и статус; сами байты отправляйте через SDK выбранного сервиса.
Не используйте присланное имя файла как доверенный путь. Назначайте случайный ключ, например UUID, а исходное имя сохраняйте как метаданные. Перед выдачей файла проверяйте права пользователя. Если применяется подписанная ссылка, её срок действия ограничивает доступ по ссылке, но не заменяет авторизацию при её создании.
При прямой загрузке браузером полезен двухэтапный процесс: API выдаёт разрешение, а затем подтверждает существование, размер и допустимый тип объекта перед сменой статуса на «готов». Недоверенные HTML и SVG не стоит выдавать с того же origin, что и интерфейс приложения, без отдельной политики безопасности. Учтите также лимиты и стоимость операций конкретного поставщика; здесь нет универсального обещания бесплатного трафика.
Проверьте не только загрузку, но и восстановление
Загрузите небольшой тестовый файл, сохраните его контрольную сумму, выпустите новый релиз и скачайте файл снова. Проверьте, что другой пользователь не получает доступ по изменённому идентификатору, а прерванная загрузка не оставляет документ в статусе «готов».
Отдельно восстановите копию данных в изолированное окружение. Если метаданные лежат в PostgreSQL, а байты в другом хранилище, нужен согласованный план: восстановленная база не должна массово ссылаться на отсутствующие объекты. Отметьте допустимое окно потери изменений и измерьте время восстановления.
Для первого сервиса на одном экземпляре том может быть достаточным. Переход к нескольким экземплярам, большим объёмам и независимой раздаче файлов — повод пересмотреть хранение до масштабирования, а не после первой пропавшей загрузки.