Всем привет!
Данная заметка является примером реализации задачи, возникшей в одном из проектов. Сам проект представляет собой небольшой сайт-магазин, где потребовалось ускорить загрузку статического контента (карточек и изображений).
Исходя из этого, первым делом я решил двигаться в сторону CDN от Cloudflare, так как ранее уже имел с ним опыт работы. Также я отказался от хранения изображений на сервере и перенёс всё в S3 от Hetzner.
Однако в процессе тестирования этой связки выяснилось, что S3-сервис, предоставляемый Hetzner, довольно сырой и не поддерживает работу с Cloudflare Proxy. Стоит отметить, что на Reddit можно встретить немало негативных отзывов об этом S3-сервисе, в том числе из-за его высокой стоимости.
Теоретически его можно настроить для работы через Cloudflare Proxy, добавив заголовок Host или подменив SNI. Но эти возможности доступны только в Enterprise-подписке.
В качестве альтернативного, но более дорогого варианта можно использовать промежуточную VPS для перенаправления запросов на S3. Решение сомнительное, но допустимое.
В результате поисков и по совету коллег было принято решение попробовать аналогичный S3-сервис от DigitalOcean. К моему удивлению, он оказался крайне удобным и простым в настройке.
Об этом теперь подробнее…
Подготовка - аккаунты и утилиты
Для настройки тестового сетапа потребуется зарегистрировать Free Tier-аккаунт на DigitalOcean, который предоставляет возможность попробовать их сервисы. По условиям предоставляется 60 дней и 200 кредитных долларов на аккаунт. При регистрации потребуется привязать платежную карту, с которой спишется $12 (позже деньги вернутся). К сожалению, скипнуть этот момент невозможно.
Доступ к S3-корзинам будет предоставляться через Cloudflare Proxy. Для работы с ним достаточно иметь бесплатный аккаунт. Соответственно, также нужно будет зарегистрироваться.
Чтобы взаимодействовать с хранилищем, в документации DigitalOcean рекомендуется использовать CLI-утилиту — s3cmd
.
Утилита устанавливается из стандартных репозиториев и представляет собой программу-клиент для взаимодействия с S3 при помощи команды:
# На ArchLinux
sudo pacman -Sy s3cmd
# На MacOS
brew install s3cmd
# На Ubuntu
sudo apt-get install s3cmd
Также для тестирования и проверки бакетов понадобится cURL
. Настройку s3cmd выполним позже.
Для Windows есть графические утилиты — S3 Browser или Cyberduck.
Первый бакет
На главной странице своего DO аккаунтапереходим в раздел Spaces Object Storage и нажимаем кнопку Create Bucket:
Нас перебросит на новую страницу настройки корзины. Там выбираем дата-центр, где хотим разместить нашу корзину. Далее нажимаем кнопку, включаем CDN, указываем имя бакета и создаём его.
После этого корзина будет создана. Заливать файлы в неё можно простым перетаскиванием или перемещением на страницу браузера в панели. Однако мы будем работать с корзиной через терминал, соответственно, требуется создать ключи доступа к бакету.
Чтобы создать ключи, переходим в сетевые настройки бакета — Settings — и нажимаем кнопку Create Access Key в разделе с ключами.
Отобразится новое окно, в котором нужно выбрать права доступа. Выбираем All Permissions (Bucket & Objects), убеждаемся в корректности выбранного бакета и создаём ключ.
В production имеет смысл выделять более ограниченный набор прав для конкретного бакета.
После этого нас перебросит в настройки бакета, и в разделе ключей отобразится наш ключ. В параметрах ключа копируем в KeePass — Access Key ID и Secret Key.
Работа с бакетом через терминал
Перед началом произведем настройки утилиты - s3cmd
,
s3cmd --configure
После ввода команды, запуститься интерактивная оболочка, где нужно будет указать данные подключаения к s3.
Сначала указываем Access/Secret токены:
Access Key []: XXXXXxxxx
Secret Key []: XXXXXxxxx
Далее потребуется указать регион, площадку. У меня это Франквут:
Default Region []: fra1
В следующем этапе указываем адрес S3:
S3 Endpoint []: fra1.digitaloceanspaces.com
Затем указываем шаблон URL-адреса для доступа к вашей корзине. Поскольку DO Spaces поддерживает URL-адреса конечных точек на основе DNS.
DNS-style bucket+hostname:port template for accessing a bucket []: %(bucket)s.fra1.digitaloceanspaces.com
Последующие пункты настраивают GPG и ожидают пароль для шифрования файлов с целью защиты от чтения. Нам это не нужно, поэтому просто нажимаем Enter.
Encryption password:
Path to GPG program [/usr/bin/gpg]:
На вопрос об использовании HTTPS по умолчанию установлено значение Yes — соглашаемся.
Use HTTPS protocol [Yes]:
Если же в вашей огранизации используется корпоративный прокси, то в следующем промте можете указать его адрес. Иначе просто прожимайте Enter.
HTTP Proxy server name:
В конце мы соглашаемся, что хотим протестировать доступ к S3:
Test access with supplied credentials? [Y/n] Y
Ожидаемый результат:
Please wait, attempting to list all buckets...
Success. Your access key and secret key worked fine :-)
При успешном результате увидите подобное сообщение.
В завершение соглашаемся на сохранение конфигурации. Конфигурационный файл будет расположен в домашней директории пользователя:
ls ~/.s3cfg
--
/home/tony/.s3cfg
Убедится в том, что все работает можем командой:
s3cmd la
---
2025-01-25 15:25 203290 s3://devs3bkt1/botlogo.png
Данная команда отобразит список доступных нам объектов (корзинок и файликов).
Теперь загрузим картинки с файлухи сервера на s3:
s3cmd put -r storage/ s3://devs3bkt1/storage/
Описание агрументов:
put
- команда для загрузки (upload) файлов или папок в указанный бакет S3.-r
- флаг, означающий рекурсивную загрузку (все файлы и поддиректории из storage/ будут загружены).storage/
- локальная директория, которая загружается в S3.s3://devs3bkt1/storage/
- путь в бакете DigitalOcean Spaces куда будут загружены файлы.
Аналогичная команда:
s3cmd sync storage/ s3://devs3bkt1/storage/
По сути работает также, как и s3cmd put -r
.
После добавления файлов, их нужно сделать доступными для всех. По умолчанию доступ к ним закрыт. Сделать это можно командой:
s3cmd setacl --acl-public -r s3://devs3bkt1/storage/
В завершении этой главы возвращаемся в панельку DO, и копируем URL-адрес (CDN Endpoint). Он понадобится нам при настройке на стороне CF.
Проверить endpoint и убедится в том, что картинка отдается из кеша можно через curl запрос:
curl -I https://mybucket.nyc1.cdn.digitaloceanspaces.com/folder/1/logo.png
---
HTTP/2 200
date: Sat, 25 Jan 2025 17:06:05 GMT
content-type: image/png
content-length: 2744
last-modified: Sat, 25 Jan 2025 17:05:46 GMT
....
cf-cache-status: HIT <-----
В HTTP-респонсе, ищем заголовок - cf-cache-status
. Его значение HIT
говорит нам, что в ресурс был отдан из кеша, без обращения к серверу.
Другие возможнозные значения могу означать:
Значение | Описание |
---|---|
MISS | Ресурс не найден в кеше, запросил его с исходного сервера и закешировал. |
EXPIRED | Кешированный ресурс устарел, запросил новый у сервера и обновил кеш. |
BYPASS | Кеширование отключено для данного ресурса (например, заголовком Cache-Control: no-cache ). |
DYNAMIC | Ресурс определен как динамический и не кешируется (например, страницы с авторизацией). |
STALE | отдал устаревшую версию ресурса, так как новый пока недоступен. |
REVALIDATED | проверил у сервера, можно ли использовать кешированную версию. |
Если при обращении к ресурсу вы получаете cf-cache-status: MISS
, не стоит расстраиваться — кеширование может занять 3–5 минут.
Кастомный домен для CDN
Для CDN DigitalOcean предоставляет свой URL-адрес, но нам это не подходит — мы хотим раздавать всю статику со своего поддомена, например, cdn.nixhub.ru.
В настройках S3-бакета есть возможность добавить кастомный домен. Тут есть два варианта:
- Выпустить сертификат от Let’s Encrypt через панель DigitalOcean. Это будет работать только если ваш домен хостится в DO.
- Использовать уже имеющийся сертификат. Этот вариант подходит, если у вас уже есть ранее выпущенный сертификат. В таком случае его можно загрузить и использовать для своего домена. Например, сертификат от Let’s Encrypt тоже подойдёт.
Мы пойдём вторым путём, так как у меня уже есть wildcard-сертификат — я просто загружу его в панель.
Стоит отметить, что ранее DO Spaces* поддерживали сертификаты Cloudflare Origin SSL. Но на момент написания статьи и тестирования выяснилось, что эта возможность больше не поддерживается (это подтвердил технический саппорт после обращения).
Что бы добавить свой сертификат переходим в раздел настроек корзины, и меняем настройки CDN:
Здесь мы добавим сертификат для нашего поддомена - cdn.nixhub.ru
.
В новом окне переходим во вкладку Bring Your Own Certificate и заполняем поля:
- Присваиваем имя сертификату.
- Вставляем содержимое сертификата и ключа
После добавляния сертификата, в новом окне, поле - subdomain
прописываем поддомен. И жмем на save.
Если на этом этапе никаких конфиктов при добавлении сертификата не произошло, значит можно переходить к следующей главе.
CORS Настройка для S3
Это механизм, который позволяет или запрещает веб-браузерам выполнять кросс-доменные HTTP-запросы.
При хранении статического контента в S3, если ваш фронтенд (например, React, Vue.js) и пытается получить данные из бакета S3, браузер может заблокировать этот запрос. Чтобы разрешить доступ, необходимо настроить CORS.
Для любого S3 провайдера CORS - настройки представляют из себя json-структуру с описанием разрешенных http методов и заголовках.
С точки зрения настроек в DO, там все еще проще. Мы переходим в настройки нашей S3-корзины, и поле CORS Configurations
жмем на кнопку добавить:
В новом окне добавляем поля:
Согласно этим настройкам, любой сайт/ресурс сможет загружать файлы с нашего бакета - поле Origin: *
. С точки зрения безопасности, это не всегда корректно и рекомендуется точечно настраивать разрешенные источники.
Здесь же мы разрешаем использовать запросы с методами - GET
и HEAD
. Буквально разрешаем скачивание файлов и просмотр их залоговков.
Ну и в конце указываем время кеширования, 86400 секунд (24 часа). После сохраняем настройки.
В интернетах довольно часто можно встретить рекомендованные настройки, такие:
[
{
"AllowedHeaders": ["*"],
"AllowedMethods": ["GET", "HEAD", "POST", "PUT"],
"AllowedOrigins": ["https://cdn.nixhub.ru"],
"ExposeHeaders": ["ETag"],
"MaxAgeSeconds": 86400
}
]
Если вы не особо хотите заморачиваться относительно этого, можно использовать их..
Настройка CF Proxy
В админке Cloudflare переходим в настройки → DNS → Records и нажимаем кнопку Add Record. Для новой записи необходимо указать следующие параметры:
- Type:
CNAME
- Name: Здесь можете указать любое предпочтительное для вас имя. Например,
cdn.example.com
илиimg.example.com
. - Target: В этом поле указываем адрес для CDN Endpoint, тот что мы сохранили ранее.
После настройки и небольшой задержки можно проверить работу связки с помощью cURL-запроса:
curl -I https://cdn.nixhub.ru/folder/1/logo.png
Если в ответе на запрос получаете 200 HTTP Сode
, значит все работает корректно..
Рефералка в DO
Если вдруг вы планируете реализовать что-то подобное или решите зарегистрировать новый аккаунт, то у меня к вам небольшая просьба — сделать это по моей реферальной ссылке:
👉 https://m.do.co/c/9d7625bea525
С вашей стороны это никак не изменит условия — вы всё так же получите бесплатный Free Tier с 200$ на 60 дней для тестирования сервисов DigitalOcean. Ну а с моей стороны — мне начислят небольшие бонусы за рекомендацию 😊
Заранее спасибо! 🚀