При разработке сайтов возникает проблема хранения множества изображений и прочих медиа-файлов. Такие медиа-файлы могут занимать ощутимое количество места на диске, которое в век повального применения ssd дисков становится дороже. Да и бекапить такие данные часто накладно. Кроме того эти файлы создаются один раз и хранятся всё время существования сайта (зачастую), и таскать эти файлы за собой с сервера на сервер с каждым годом становится неприятно. Знакомо? Давайте попробуем разобраться как дешево можно решить проблему.
Решение очень простое и называется — объектное хранилище. Яркий пример таких хранилищ — Amazon S3 или DigitalOcean Spaces.
Буду рассказывать на примере DO, потому что сам пользуюсь этим провайдером.
Регистрируемся на сайте digitalocean, если вы ещё этого не сделали по каким-то причинам, выбираем сверху вкладку Spaces и видим такую вот страницу:
Остается нажать Create a Space, и заполнить обязательные поля.
Затем необходимо создать ключи для программного доступа к хранилищу. Делается это выбором в верхнем меню надписи API. Пролистываете страницу до надписи Spaces access keys и жмете Generate New Key, вводите название ключа и получаете ключ и секрет
С предварительными ласками действиями покончено, можно приступить к настройкам сервера.
Давайте немного порассуждаем о том, каким образом внедрить другое хранилище файлов в работающий проект почти безболезненно? Переписывать код может стоить слишком дорого как и по времени, так и по стоимости, искать возникающие баги и сталкиваться с другими возможными проблемами. Поэтому предлагаю вам довольно простое решение — отправка файлов по расписанию каждую ночь.
Допустим, все ваши статичные файлы хранятся по адресу http://example.com/static/. Мы настраиваем nginx таким образом, чтобы он проверял наличие файла сначала в этой директории (локальной для вашего сайта), и если его там нет, то он бы обращался во внешнее хранилище.
Вот пример конфигурации для nginx:
location ~ ^/static/(.*)$ { try_files $uri @storage; } location @storage { proxy_pass https://ip_addr:433/$1; proxy_connect_timeout 60; proxy_send_timeout 60; proxy_read_timeout 60; proxy_redirect off; proxy_buffer_size 4k; proxy_buffers 4 32k; proxy_busy_buffers_size 64k; proxy_temp_file_write_size 10m; proxy_hide_header Strict-Transport-Security; add_header X-Cache-Status $upstream_cache_status; proxy_ignore_headers Set-Cookie; proxy_set_header Host "yourname.ams3.digitaloceanspaces.com"; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_cache cache_name; proxy_cache_valid 404 502 1m; proxy_cache_valid any 24h; proxy_cache_revalidate on; proxy_cache_lock on; }
где ip_addr — это IP адрес, который резолвится при обращении к yourname.ams3.digitaloceanspaces.com, а yourname — это имя, которое вы указывали при создании spaces на сайте DO.
Кроме того необходимо добавить в nginx.conf в секцию http вот такую строку:
proxy_cache_path /var/cache/nginx/cache_name levels=1:2 keys_zone=cache_name:10m inactive=48h max_size=10g;
где
/var/cache/nginx/cache_name — путь к кешу (должен иметь права на запись процессом nginx)
keys_zone=cache_name:10m — название кеша (которое используется в секции server для вашего сайта) и размер ключей кеширования 10 мегабайт (достаточно для хранения ключей от нескольких сотен тысяч файлов)
inactive=48h — срок годности файлов в кеше (48 часов)
max_size=10g — максимальный размер в 10 гигабайт кешированной информации
Кеш нам необходим для уменьшения кол-ва запросов к Spaces и для ускорения отдачи статики. Чем больше вы ставите размер кеша и неактивность, тем быстрее всё будет работать.
Теперь осталось переместить файлы в хранилище, чтобы таки начать экономить место на диске :) Тут уже я не смогу вам помочь конкретным кодом, но расскажу логику.
Необходимо написать простой скрипт, который бы рекурсивно проходил директорию static вашего сайта и понемногу каждую ночь копировал файлы, а затем удалял их после копирования. Если вы не умеете программировать, то заказать такой скрипт можно на любом фриланс сайте. Пара лайфхаков:
- Используйте пакет из композера для работы с Spaces, это в разы проще, чем разбираться в API от S3 и писать всё с нуля. Шикарный код можно найти тут: https://flysystem.thephpleague.com/docs/ . Установка тоже простая:
composer require league/flysystem-aws-s3-v3
В скрипте стоит поставить паузу в 200-300мс между каждым копированием файла, потому что API имеет ограничение на частоту запросов и при копировании без задержек начинаются проблемы.
- В скрипте можно копировать по N файлов за один цикл запуска, нет смысла занимать систему на несколько дней и копировать огромный объем файлов. Файлы могут по чуть-чуть копироваться и несколько недель, в этом нет ничего страшного. Nginx сам будет определять какой файл еще есть на диске, а какой уже в Spaces и отдавать его посетителю вашего сайта
- После каждой итерации копирования не забывайте удалять файлы локально, освобождая место
Пример кода для подключения flysystem:
use Aws\S3\S3Client; use League\Flysystem\AwsS3v3\AwsS3Adapter; use League\Flysystem\Filesystem; $client = S3Client::factory([ 'credentials' => [ 'key' => 'your-key', // ваш ключ из API 'secret' => 'your-secret', // ваш секрет из API ], 'region' => 'ams3', // вместо ams3 подставьте регион Spaces 'endpoint' => 'https://ams3.digitaloceanspaces.com', // вместо ams3 подставьте регион Spaces 'version' => 'latest', ]); $adapter = new AwsS3Adapter($client, 'yourname', '/'); // yourname - имя spaces и / - путь относительно корня хранилища. Если меняете этот путь, то его необходимо изменить и в конфиге nginx // например вот так: proxy_pass https://ip_addr:433/mysite/$1; // $adapter = new AwsS3Adapter($client, 'yourname', '/mysite'); // это позволит вам в одном хранилище хранить файлы от нескольких сайтов $filesystem = new Filesystem($adapter);
Надеюсь, этот мануал кому-нибудь поможет сэкономить деньги
Если есть замечания или вопросы — добро пожаловать в комментарии