Как создать и настроить собственный CDN

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

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

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

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

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

В большинстве остальных случаев целесообразнее выбрать готовый CDN.

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

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

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

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

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

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

Что касается заказа серверов — их стоит арендовать в регионах и странах, где находится ваша пользовательская аудитория. Если проект интерконтинентальный, то удобно выбирать хостинг-провайдеров, предлагающих сразу сервера по всему миру, к примеру PQ.hosting.

Для нашего частного CDN закажем 3 виртуальных сервера на разных континентах. При установке выберем последний Debian или Ubuntu. Наши сервера:

  • Франкфурт (Германия), ip: 199.247.18.199
  • Чикаго (США), ip: 149.28.121.123
  • Сингапур, ip: 157.230.240.216

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

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

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

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

При заказе 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 в меню API&Resellers нужно создать нового 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 --server letsencrypt --reloadcmd "service nginx reload"

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

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

Как создать и настроить собственный CDN

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

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

root@cdn:~# mkdir -p /root/.acme.sh/cdn.sayt.in_ecc/ root@cdn:~# scp -r root@199.247.18.199:/root/.acme.sh/cdn.sayt.in_ecc/* /root/.acme.sh/cdn.sayt.in_ecc/

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

scp -r root@199.247.18.199:/root/.acme.sh/cdn.sayt.in_ecc/* /root/.acme.sh/cdn.sayt.in_ecc/ && 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_ecc/fullchain.cer; ssl_certificate_key /root/.acme.sh/cdn.sayt.in_ecc/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 с точками присутствия на трех континентах полностью готов.

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

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

Как создать и настроить собственный CDN

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

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

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

22
Начать дискуссию