При разработке сайтов возникает проблема хранения множества изображений и прочих медиа-файлов. Такие медиа-файлы могут занимать ощутимое количество места на диске, которое в век повального применения 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);
Надеюсь, этот мануал кому-нибудь поможет сэкономить деньги
Если есть замечания или вопросы — добро пожаловать в комментарии