Собираем и настраиваем свой CDN

Сети доставки контента (CDN) используются на сайтах и в приложениях в основном для ускорения загрузки статических элементов. Происходит это за счет кеширования файлов на CDN-серверах, расположенных в разных географических регионах. Запросив данные через CDN, пользователь получает их с ближайшего сервера.

Принцип работы и функционал у всех сетей доставки контента примерно одинаков. Получив запрос на загрузку файла, CDN сервер единоразово берет его с оригинального сервера и отдает пользователю, вместе с тем кешируя у себя на заданный промежуток времени. На все последующие запросы ответ отдается из кеша. У всех CDN есть опции предварительной загрузки файлов, очистки кеша, настройки срока его хранения, и многое другое.

Бывает, что в силу тех или иных причин требуется организовать собственную сеть доставки контента, и тогда — да будет в помощь нам инструкция по сборке очередного велосипеда.

Собираем и настраиваем свой CDN
Источник: Infographic vector created by pikisuperstar — www.freepik.com

Когда нужен собственный CDN

Рассмотрим случаи, когда запуск собственного CDN имеет смысл:

  • когда есть желание сэкономить, а текущие расходы даже при использовании недорогих CDN вроде BunnyCDN составляют несколько сотен долларов в месяц
  • если хотим получить постоянный кеш или кеш без соседей по серверу и каналу
  • в нужном вам регионе у CDN сервисов нет точек присутствия
  • требуются какие-либо особые настройки доставки контента
  • хотим ускорить доставку динамического контента, разместив ближе к пользователям продакшн сервера
  • есть опасение, что сторонний CDN сервис может неправомерно собирать или использовать информацию о поведении пользователей (привет сервисам без GDPR-compliant) или заниматься другими неправомерными действиями

В большинстве остальных случаев целесообразнее использовать существующие готовые решения.

Что нужно для запуска

Чудно, если у вас есть своя автономная система (AS). С ней можно назначить одинаковый IP нескольким серверам и по этой инструкции на уровне сети направлять пользователей к ближайшему. Cтоит сказать, что даже с блоком адресов /24 есть возможность построить сеть доставки контента. Некоторые сервер-провайдеры позволяют сделать анонс для использования во всех доступных у них регионах.

Если же вы не счастливый обладатель блока IP адресов, то для запуска простого CDN вам понадобятся:

  • доменное имя или субдомен
  • минимум два сервера в разных регионах. Сервер может быть как выделенный, так и виртуальный
  • geoDNS инструмент. С его помощью пользователь, обратившись к домену, будет направлен на ближайший сервер

Регистрируем домен и заказываем сервера

С регистрацией домена все просто — регистрируем в любой зоне у любого регистратора. Также для CDN можно использовать субдомен, например что-то вроде cdn.имядомена.com. Собственно в нашем примере мы так и поступим.

Что касается заказа серверов — их стоит арендовать в регионах и странах, где находится ваша пользовательская аудитория. Если проект интерконтинентальный, то удобно выбирать хостинг-провайдеров, предлагающих сразу сервера по всему миру. Примеры: OVH, Leaseweb и 100Tb — для выделенных серверов, Vultr и DigitalOcean — для виртуальных облачных*.

Для нашего частного CDN закажем 3 виртуальных сервера на разных континентах. У Vultr на сервере за $5/мес мы получим 25GB SSD места и 1TB трафика. При установке выберем последний Debian. Наши сервера:

Собираем и настраиваем свой CDN Франкфурт, ip: 199.247.18.199

Собираем и настраиваем свой CDN Чикаго, ip: 149.28.121.123

Собираем и настраиваем свой CDN Сингапур, ip: 157.230.240.216

* Vultr и DigitalOcean обещают $100 кредита пользователям, зарегистрированным по ссылкам в статье, сразу после добавления способа оплаты. Автор также от подобного получает небольшой комплимент, что для него сейчас весьма значимо. Пожалуйста, отнеситесь с пониманием.

Настраиваем geoDNS

Чтобы пользователь при обращении к домену или субдомену CDN направлялся на нужный (ближайший к нему) сервер, нам понадобится DNS сервер с функцией geoDNS.

Принцип и порядок работы geoDNS следующий:

  1. Определяет IP клиента, пославшего DNS запрос, или IP рекурсивного DNS-сервера, который используется при обработке клиентского запроса. Такими рекурсивными серверами обычно являются DNS-ы провайдеров.
  2. По IP клиента узнает его страну или регион. Для этого используются базы GeoIP, коих сегодня превеликое множество. Есть неплохие бесплатные варианты.
  3. В зависимости от местоположения клиента отдает ему IP-адрес ближайшего CDN сервера.

DNS сервер с функцией geoDNS можно собрать самостоятельно, но лучше использовать готовые решения с сетью DNS-серверов по всему миру и Anycast из коробки:

  • СlouDNS от $9.95/мес, тариф GeoDNS, по умолчанию есть один DNS Failover
  • Zilore от $25/мес, включен DNS Failover
  • Amazon Route 53 от $35/мес за чистых 50М гео-запросов. DNS Failover тарифицируется отдельно
  • DNS Made Easy от $125/мес, есть 10 DNS Failover
  • Cloudflare, функция «Geo Steering» доступна в Enterprise тарифах

При заказе geoDNS следует обращать внимание на количество включенных в тариф запросов и учитывать, что реальное число обращений к домену может в разы превзойти ожидания. Миллионы пауков, сканнеров, спамеров и прочей нечисти работают безустанно.

Практически у всех DNS-сервисов в стоимость включена незаменимая для построения CDN услуга — DNS Failover. С ее помощью можно настроить мониторинг работы своих серверов и в случае отсутствия признаков жизни автоматически заменять в DNS-ответах адрес нерабочего сервера на резервный.

Для построения нашего CDN воспользуемся ClouDNS, тариф GeoDNS.

Добавим в личном кабинете новую DNS-зону, указав свой домен. Если CDN будем строить на субдомене, а главный домен уже используется, то сразу после добавления зоны не забудьте добавить существующие рабочие DNS-записи. Следующее действие — создание для CDN домена/субдомена нескольких A-записей, каждая из которых будет применяться для заданного нами региона. В качестве регионов можно указать континенты или страны, для США и Канады доступны субрегионы.

В нашем случае CDN будет поднят на субдомене cdn.sayt.in. Добавив зону sayt.in, создадим для субдомена первую A-запись и направим всю Северную Америку на сервер в Чикаго:

Собираем и настраиваем свой CDN
Повторим действие для других регионов, не забыв создать одну запись для регионов по умолчанию. Вот что получится в итоге:

Собираем и настраиваем свой CDN

Последняя дефолтная запись на скриншоте означает, что все незаданные регионы (а это Европа, Африка, пользователи спутникового интернета и т.д.) будут направляться на сервер во Франкфурте.

На этом базовая настройка DNS завершена. Осталось зайти на сайт регистратора домена и заменить текущие NS-ы домена на те, что выдал ClouDNS. И пока NS-ы будут обновляться мы подготовим сервера.

Установка SSL сертификатов

Наш CDN будет работать по HTTPS, поэтому если у вас уже имеются SSL сертификаты для домена или субдомена, — загрузите их на все сервера, например в директорию /etc/ssl/вашдомен/

Если сертификатов нет, можно получить бесплатный от Let’s Encrypt. Для этого отлично подойдет ACME Shell script. Клиент удобен и прост в настройке, а главное — позволяет проводить валидацию домена/субдомена по DNS через API от ClouDNS.

Мы поставим acme.sh только на одном из серверов — европейском 199.247.18.199, с него сертификаты будут копироваться на все остальные. Для установки выполним:

root@cdn:~# wget -O - https://get.acme.sh | bash; source ~/.bashrc

В ходе установки скрипта будет создано CRON задание для дальнейшего обновления сертификатов без нашего участия.

Проверка домена при выдаче сертификата будет проводиться по DNS с использованием API, поэтому в личном кабинете ClouDNS в меню Reseller API нужно создать нового API пользователя и задать для него пароль. Полученный auth-id с паролем пропишем в файле ~/.acme.sh/dnsapi/dns_cloudns.sh (не путать с файлом dns_clouddns.sh). Вот строки, которые требуется раскомментировать и отредактировать:

CLOUDNS_AUTH_ID=<auth-id>
CLOUDNS_AUTH_PASSWORD="<пароль>"

Теперь запросим получение SSL сертификата для cdn.sayt.in

root@cdn:~# acme.sh --issue --dns dns_cloudns -d cdn.sayt.in --reloadcmd "service nginx reload"

В параметрах, на будущее, мы указали команду для автоматической перезагрузки конфигурации веб-сервера после каждого обновления срока действия сертификата в дальнейшем.

Весь процесс получения сертификата может занять до 2 минут, не прерывайте его. Если возникнет ошибка валидации домена, попробуйте запустить команду еще раз. В конце мы увидим, куда были загружены сертификаты:

Собираем и настраиваем свой CDN

Запомним эти пути, их нужно будет указать при копировании сертификата на другие сервера, а также в настройках веб-сервера. На ошибку перезагрузки конфигов Nginx не обращаем внимания, — на полностью настроенном сервере при обновлении сертификатов ее не будет.

Все что нам осталось по SSL, — это скопировать полученный сертификат на два других сервера с сохранением пути к файлам. Создадим на каждом из них такие же директории и сделаем копию:

root@cdn:~# mkdir -p /root/.acme.sh/cdn.sayt.in/
root@cdn:~# scp -r [email protected]:/root/.acme.sh/cdn.sayt.in/* /root/.acme.sh/cdn.sayt.in/

Чтобы обновление сертификатов было регулярным создадим на обоих серверах ежедневное CRON задание с командой:

scp -r [email protected]:/root/.acme.sh/cdn.sayt.in/* /root/.acme.sh/cdn.sayt.in/ && service nginx reload

При этом доступ к удаленному серверу-источнику должен быть настроен по ключу, т.е. без ввода пароля. Не забудьте сделать это.

Установка и настройка Nginx

Для отдачи статического контента мы будем использовать Nginx, сконфигурированный в режиме кеширующего proxy-сервера. Обновим списки пакетов и установим его на всех трех серверах:

root@cdn:~# apt update
root@cdn:~# apt install nginx

Вместо дефолтного используем конфиг из спойлера ниже:
nginx.conf

user www-data;
worker_processes auto;
pid /run/nginx.pid;

events {
    worker_connections 4096;
    multi_accept on;
}

http {
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    types_hash_max_size 2048;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    access_log off;
    error_log /var/log/nginx/error.log;

    gzip on;
    gzip_disable "msie6";
    gzip_comp_level 6;
    gzip_proxied any;
    gzip_vary on;
    gzip_types text/plain application/javascript text/javascript text/css application/json application/xml text/xml application/rss+xml;
    gunzip on;            

    proxy_temp_path    /var/cache/tmp;
    proxy_cache_path   /var/cache/cdn levels=1:2 keys_zone=cdn:64m max_size=20g inactive=7d;
    proxy_cache_bypass $http_x_update;

server {
  listen 443 ssl;
  server_name cdn.sayt.in;

  ssl_certificate /root/.acme.sh/cdn.sayt.in/cdn.sayt.in.cer;
  ssl_certificate_key /root/.acme.sh/cdn.sayt.in/cdn.sayt.in.key;

  location / {
    proxy_cache cdn;
    proxy_cache_key $uri$is_args$args;
    proxy_cache_valid 90d;
    proxy_pass https://sayt.in;
    }
  }
}

В конфиге отредактируем:

  • max_size — размер кеша, не превышающий доступное на диске место
  • inactive — время хранения закешированых данных, к которым никто не обращался
  • ssl_certificate и ssl_certificate_key — пути к файлам SSL сертификата и ключа
  • proxy_cache_valid — время хранения закешированых данных
  • proxy_pass — адрес оригинального сервера, с которого CDN будет запрашивать файлы для кеширования. В нашем примере это sayt.in

Как видим, всё просто. Сложность лишь может возникнуть в настройке времени кеширования из-за схожести директив inactive и proxy_cache_valid. Разберем их на нашем примере. Вот что происходит при inactive=7d и proxy_cache_valid 90d:

  • если запрос не повторится в течение 7 дней, то данные удалятся из кеша по истечении этого периода
  • если запрос будет повторяться хотя бы раз в 7 дней, то данные в кеше будут считаться устаревшими по истечении 90 дней и при очередном запросе Nginx обновит их, взяв с оригинального сервера

Закончив править nginx.conf, перезагрузим конфигурацию:

root@cdn:~# service nginx reload

Наш CDN полностью готов. За $15/мес. мы получили точки присутствия на трех континентах и 3 Тб трафика: по 1 Тб в каждой локации.

Проверяем работу CDN

Посмотрим на пинги к нашему CDN из разных географических локаций. Для этого подойдут любые пинг-сервисы.

Точка запуска
Хост
IP
Avg time, мсек

Германия, Берлин
cdn.sayt.in
199.247.18.199
9.6

Нидерланды, Амстердам
cdn.sayt.in
199.247.18.199
10.1

Франция, Париж
cdn.sayt.in
199.247.18.199
16.3

Великобритания, Лондон
cdn.sayt.in
199.247.18.199
14.9

Канада, Торонто
cdn.sayt.in
149.28.121.123
16.2

США, Сан-Франциско
cdn.sayt.in
149.28.121.123
52.7

США, Даллас
cdn.sayt.in
149.28.121.123
23.1

США, Чикаго
cdn.sayt.in
149.28.121.123
2.6

США, Нью-Йорк
cdn.sayt.in
149.28.121.123
19.8

Сингапур
cdn.sayt.in
157.230.240.216
1.7

Япония, Токио
cdn.sayt.in
157.230.240.216
74.8

Австралия, Сидней
cdn.sayt.in
157.230.240.216
95.9

Результаты хорошие. Разместим теперь в корне основного сайта тестовую картинку test.jpg и проверим скорость ее загрузки через CDN. Сказано, — сделано. Контент отдается быстро.

Напишем небольшой скрипт на случай, если захотим на CDN-точке очистить кеш.
purge.sh

#!/bin/bash
if [ -z "$1" ]
then
    echo "Purging all cache"
    rm -rf /var/cache/cdn/*
else
    echo "Purging $1"
    FILE=`echo -n "$1" | md5sum | awk '{print $1}'`
    FULLPATH=/var/cache/cdn/${FILE:31:1}/${FILE:29:2}/${FILE}
    rm -f "${FULLPATH}"
fi

Для удаления всего кеша достаточно просто запустить его, отдельный файл можно почистить так:

root@cdn:~# ./purge.sh /test.jpg

Вместо выводов

Напоследок хочу дать несколько полезных советов, дабы сразу переступить через грабли, сделавшие в свое время больную голову мне:

  • Для повышения отказоустойчивости CDN рекомендуется настроить DNS Failover, помогающий быстро сменить A запись в случае поломки сервера. Делается это в панели управления DNS записями домена
  • Сайты с широким географическим охватом без сомнения требуют большого числа точек CDN, но давайте без фанатизма. Скорее всего пользователь не заметит существенной разницы в сравнении с платным CDN, если вы разместите сервера в 6-7 местах: Европа, Северная Америка (восток), Северная Америка (запад), Сингапур, Австралия, Гонконг или Япония
  • Иногда хостеры не разрешают использовать арендованные сервера для целей CDN. Поэтому, если вы вдруг решите развернуть сеть доставки контента как сервис, не забудьте заранее прочитать правила конкретного хостинг-провайдера
  • Изучите карту подводных коммуникаций, чтобы представлять, как связаны континенты и учитывать это при построении сети доставки контента
  • Попробуйте проверить пинги из разных мест к вашим серверам. Так можно увидеть ближайшие к CDN-точкам регионы и правильнее настроить GeoDNS
  • В зависимости от задач нелишним будет донастроить Nginx под конкретные требования кеширования и с учетом нагрузки на сервер. В этом мне очень помогли статьи о кеше Nginx — здесь и ускорении работы при больших нагрузках: здесь и здесь

Источник: habr.com