Подождите писать в комментариях ссылки на
bind mounting и
volume - эта задача гораздо более многомерная, чем выбор способа хранения данных в Docker. Тут необходимо и на уровне организации Docker-образов поразмыслить, да и при покупке VPS задуматься о дополнительных опциях.
Заранее прошу прощение за длинный вопрос. Длинный он потому, что мне нужно было показать ход моих мыслей, а также что я знаю и чего ещё не знаю.
Что мы имеем изначально? Обычно в проекте серверного приложения у нас есть какая-то папка для хранения загружаемых пользователями файлов. На примере Laravel, это `storage/app/public`:
Иногда полученные от пользователей файлы сохраняют и в
public (причём не только в случае PHP и Laravel), но я не уверен, что это хорошая идея - смешивать в одну кучу файлы, подготовленные разработчиками (стили, логотипы, иконки и так далее) и загруженные пользователями.
В отличие от таких языков, как PHP, в моём случае (TypeScript + Node.js) выходной код
полностью отделён от исходного, и та структура файлов, что на картинке ниже, на 100% сгенерирована (даже package.json - это не тот, что в корне проекта; из этого сгенерированного package.json
полностью исключены зависимости для разработки и те, что подшиты Webpack-ом к скриптам для фронтенда):
На данный момент у меня 2 volum-а: один для базы данных и другой - для файлов проекта, который создаётся автоматически командой `COPY . /var/www/example.com`, прописанной в **FrontServer.Dockerfile**. Я не говорю, что именно так и делаю, но в теории до тех пор, пока не принималось никаких файлов от пользователей, во время деплоя можно было *безопасно* удалить volume
проекта (потому что данные базы данных хранятся на другом volum-е) и добавленные ранее файлы на картинке выше, и только потом загрузить на сервер файлы собранного проекта заново, установить свежие зависимости и запустить приложение. В реальности я это делаю с помощью
rsync, причём с опцией
—delete удаляющей файлы, которых нет в источнике. Теперь же планируется начать принимать файлы от пользователей, а потому необходимо внести изменения в процесс деплоя.
Как я уже сказал выше, не думаю, что это хорошая идея - хранить их
public. При таком раскладе, как сейчас, добавленные пользователями файлы будут просто потеряны, и хотя их можно хранить в одной из подпапок по отношению к
public, исключённой из отслеживания
rsync, ошибка может привести к потере данных. Какие ещё варианты мне известны?
Вариант 1: Хранить загружаемые пользователями файлы в отдельной папке + на отдельном volume
Аналогично Laravel, помимо
public добавить папку
storage для файлов, принимаемых от пользователей.
Данный вариант не предполагает добавления новых образов и контейнеров, а вот насчёт Volum-ов надо подумать. Начнём с того, сама папка
06-ProductionBuilding уже соответствует Volume-у, а тут в ней ещё и
storage появится, которой должен соответствовать отдельному Volum-у. На данный момент точно не знаю, но не уверен, что так можно.
Затем, возможно для данного случая и
bind mounting ничем не хуже.
Однако какой бы вариант не был выбран, при деплое новой версии проекта нужно быть осторожным, чтобы не тронуть папку
storage, а такой расклад - хорошая почва для гипотетического инцидента потери данных пользователей.
Вариант 2: Создать отдельный образ
Возможно, работу с изображениями и другими мультимедийными файлами следует вообще переложить на отдельные образ и контейнер, а не только volume. Сейчас у меня 2 образа: node.js (front_server) и PostgreSQL:
version: "3.5"
services:
front_server:
container_name: Example-Production-FrontServer
build:
context: .
dockerfile: "FrontServer.Dockerfile"
ports: [ "8080:8080" ]
environment:
- DATABASE_HOST=Database
depends_on: [ Database ]
Database:
image: postgres
container_name: Example-Production-Database
ports: [ "${DATABASE_PORT}:${DATABASE_PORT}" ]
environment:
- POSTGRES_PASSWORD=${DATABASE_PASSWORD}
volumes:
- DatabaseData:/data/example.com
volumes:
DatabaseData: {}
но говорят, что для отдачи файлов лучше организовать отдельный сервер, например Nginx. Так как в случае с Docker это не означает покупку ещё одного сервера, то почему бы и нет: отдельный образ, отдельный контейнер, отдельный volume, лёгкость в резервировании данных. Я даже думаю, что при таком раскладе можно у поставщика VPS и отдельный диск арендовать для хранения на нём только файлов, загруженных пользователями.
Подробные технические требования конкретно для моей задачи
Если кратко, то начальные требования весьма скромные, на уровне сайта с фотографиями для относительно узкого круга людей, однако ввиду того, что будет вестись блог с загрузкой новых фотографий, нужно чтобы при необходимости было легко расширить начальный объём доступной памяти.
-
Требования к размеру хранилища файлов: Для начала хватит 2-3Гб, но надо, чтобы этот объём было легко увеличить при необходимости
-
Отказоусточивость: На данный момент создавать несколько реплик, чтобы при отказе одного сервера файлы были доступны, не требуется ввиду малого масштаба проекта. Сейчас достаточно иметь возможность извлечь файлы в процессе процедуры восстановления данных без сверхусилий, если что-то случится.
-
Планируется ли использовать хранилище другими сайтами / приложениями: нет
-
Быстродействие: точного приемлемого интервала времени загрузки изображений не назову, но достаточно среднестатистической скорости загрузки картиной - пускай и не мгновенно, но и не как во времена модема. Адаптация сайта для зарубежных посетителей не планируется. Большая часть файлов - изображения; хранить огромные файлы по несколько гигабайт не планируется.
-
Пропуская способность: особых требований нет; расчётный траффик очень далёк до уровня социальных сетей.
Итог
Наверное, есть и друге варианты, но мне известны только эти два.
Напомню, что в данном вопросе мы не рассматриваем варианты сторонних сервисов, аналогичных AWS Bucket - сейчас задача понять, чего можно достичь с помощью одного VPS и Docker-а.
P. S.
В течение месяца данный вопрос не получил ни одного ответа на русскоязычном Stack Overflow, что надеюсь на Вас, дорогие пользователи Хабра.