Принцип работы и функционал у всех сетей доставки контента примерно одинаков. Получив запрос на загрузку файла, 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. Наши сервера:
Франкфурт, ip: 199.247.18.199
Чикаго, ip: 149.28.121.123
Сингапур, ip: 157.230.240.216
* Vultr и DigitalOcean обещают $100 кредита пользователям, зарегистрированным по ссылкам в статье, сразу после добавления способа оплаты. Автор также от подобного получает небольшой комплимент, что для него сейчас весьма значимо. Пожалуйста, отнеситесь с пониманием.
Настраиваем geoDNS
Чтобы пользователь при обращении к домену или субдомену CDN направлялся на нужный (ближайший к нему) сервер, нам понадобится DNS сервер с функцией geoDNS.
Принцип и порядок работы geoDNS следующий:
- Определяет IP клиента, пославшего DNS запрос, или IP рекурсивного DNS-сервера, который используется при обработке клиентского запроса. Такими рекурсивными серверами обычно являются DNS-ы провайдеров.
- По IP клиента узнает его страну или регион. Для этого используются базы GeoIP, коих сегодня превеликое множество. Есть неплохие бесплатные варианты.
- В зависимости от местоположения клиента отдает ему 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-запись и направим всю Северную Америку на сервер в Чикаго:

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

Последняя дефолтная запись на скриншоте означает, что все незаданные регионы (а это Европа, Африка, пользователи спутникового интернета и т.д.) будут направляться на сервер во Франкфурте.
На этом базовая настройка 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 минут, не прерывайте его. Если возникнет ошибка валидации домена, попробуйте запустить команду еще раз. В конце мы увидим, куда были загружены сертификаты:

Запомним эти пути, их нужно будет указать при копировании сертификата на другие сервера, а также в настройках веб-сервера. На ошибку перезагрузки конфигов Nginx не обращаем внимания, — на полностью настроенном сервере при обновлении сертификатов ее не будет.
Все что нам осталось по SSL, — это скопировать полученный сертификат на два других сервера с сохранением пути к файлам. Создадим на каждом из них такие же директории и сделаем копию:
root@cdn:~# mkdir -p /root/.acme.sh/cdn.sayt.in/ root@cdn:~# scp -r root@199.247.18.199:/root/.acme.sh/cdn.sayt.in/* /root/.acme.sh/cdn.sayt.in/
Чтобы обновление сертификатов было регулярным создадим на обоих серверах ежедневное CRON задание с командой:
scp -r root@199.247.18.199:/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
Вместо дефолтного используем конфиг из спойлера ниже:
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-точке очистить кеш.
#!/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 — здесь и ускорении работы при больших нагрузках: здесь и здесь
ссылка на оригинал статьи https://habr.com/ru/post/496910/
Добавить комментарий