Как сделать и настроить свой CDN

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

Хранение контента у большинства сетей доставки контента организовано так: CDN сервер, получив в первый раз от пользователя запрос на отдачу файла, загружает его с оригинального сервера к себе, кэширует и тут же отдает пользователю. Для всех последующих запросов файл уже выдается из кэша. Некоторые сервисы позволяют настраивать длительность хранения кэшируемых данных, а также их предварительную загрузку (прекэш).

Иногда может понадобиться настроить собственную сеть доставки контента. Давайте рассмотрим, для чего это нужно и как это сделать.

t-mskkhzvvgwxoybdrkd8-stndi.png
Это наша будущая CDN из 5 серверов, которая будет раздавать контент на весь мир

Зачем настраивать собственный CDN


Приведем несколько примеров, когда стоит задуматься о создании своей собственной сети доставки контента:

  • расходы на услуги стороннего CDN сервиса существенно превышают затраты на создание своего
  • когда хотим иметь гарантированный канал и фиксированное дисковое пространство для постоянного кэша, а не разделять их с другими клиентами
  • если необходимы особые настройки хранения и доставки контента
  • в случае размещения нескольких production-серверов в разных регионах для ускорения доставки динамических данных
  • если не хотим, чтобы сторонние сервисы собирали и хранили данные о наших пользователях, например: IP адрес, запрашиваемые URL, и т.п.
  • когда в нужном нам регионе у других сервисов нет точек присутствия

В большинстве других случаев лучше воспользоваться услугами платного CDN сервиса.

 

Готовимся к запуску


Для реализации задуманного нам понадобится:

  1. Несколько серверов в разных регионах, можно виртуальных (VPS)
  2. Отдельный субдомен. В нашем примере это будет cdn.nashsait.org
  3. geoDNS сервис, с помощью которого пользователь, обращаясь к субдомену, будет направлен на ближайший в его регионе сервер


Арендуем сервера и настраиваем geoDNS


Сперва определимся, где преимущественно находится основная пользовательская аудитория. В нашем примере это Казахстан, поэтому мы определенно должны иметь точку присутствия в этом регионе, чтобы у большинства пользователей всё работало максимально быстро. Остальные сервера будут «разбросаны» по миру. Арендовать их удобнее всего у хостинг-провайдеров, предлагающих сразу несколько локаций для размещения.

Мы закажем 5 виртуальных серверов с 25GB диска, безлимитным трафиком и последним Debian или Ubuntu:

4cwqthc-1djl-u3yjp5bsfqnrt0.pngКазахстан, ip: 86.104.73.235

awjhqwlcnsmbuykpknlq5cwthau.pngНидерланды, ip: 94.232.245.17

o0ubrtkvwsjucjlhh5xavufmwm4.pngСША, ip: 45.89.53.214

ljsomemkcdzzcfeq0s7mzb3rxn4.pngБразилия, ip: 95.164.5.110

ephnhm840crrlnxe1qlxaaw8ipu.pngЯпония, ip: 5.253.41.115

Чтобы пользователь при обращении к cdn.nashsait.org направлялся на ближайший для него сервер, нам нужен рабочий DNS с функцией geoDNS. Его можно настроить самому или использовать готовый сервис, например СlouDNS.

Мы будем использовать СlouDNS: регистрируемся, выбираем тариф GeoDNS и в личном кабинете добавляем новую DNS-зону, указав наш главный домен nashsait.org. В процессе создания зоны будет предложено выбрать для домена будущие NS-сервера. Отметим все доступные и на будущее скопируем их себе в отдельный текстовый файлик.

Если главный домен уже нами используется (например, на нем размещен сайт или работает электронная почта), то сразу после создания зоны нужно добавить существующие рабочие DNS-записи.

Затем для субдомена cdn.nashsait.org нужно создать несколько A-записей, каждая из которых в зависимости от региона пользователя будет указывать на один из наших CDN серверов. В качестве регионов можно указывать континенты, страны или отдельные штаты (для США и Канады). Начнем с Южной Америки и направим все запросы оттуда на сервер в Бразилии:

j9af3jynv8m_qqtzcld_ni5hv5q.png


Сделаем то же самое для других регионов, при этом один из них рекомендуется добавить как регион «по умолчанию». В итоге список A-записей будет выглядеть так:

c0anho-iulxwcwpvdnqoqkz-7xa.png

Самая нижняя запись «Default» означает, что все остальные, не указанные в других записях регионы (Европа, Африка, спутниковый интернет и т.п.), будут направляться на сервер в Нидерландах.

На этом настройка geoDNS завершена, осталось зайти на сайт регистратора нашего домена и изменить NS-сервера для nashsait.org на те, что мы ранее скопировали в отдельный текстовый файл.

 

Добавление SSL сертификатов


Чтобы CDN работал по протоколу HTTPS, мы установим бесплатный SSL сертификат от Let’s Encrypt. Это удобно делать с помощью ACME Shell скрипта, который позволяет валидировать домен по DNS через ClouDNS API.

Достаточно установить acme.sh на одном из серверов, а затем скопировать полученный сертификат на все остальные. Выполним установку на сервере в Нидерландах:

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


Стоит заметить, что во время установки создается отдельная CRON задача для автоматического обновления сертификатов в будущем.

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

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


Далее запустим получение SSL сертификата для cdn.nashsait.org

root@cdn:~# acme.sh --issue --dns dns_cloudns -d cdn.nashsait.org --server letsencrypt --reloadcmd "service nginx reload"


Процесс может занимать до нескольких минут и если возникнет ошибка проверки субдомена, попробуйте запустить команду повторно. При успешном результате на экране появится список файлов установленного сертификата:

zhp43ar84yqo7kj7cvhlias5tj0.png

Запомним эти пути, они будут нужны при копировании сертификата на другие CDN локации и для настройки веб-сервера. Ошибка «Reload error for» не существенна и при дальнейшем обновлении сертификатов на полностью настроенном сервере ее не будет.

Войдем на четыре других сервера и скопируем на каждый полученный сертификат, создав соответствующие директории, чтобы пути к файлам были везде одинаковы:

root@cdn:~# mkdir -p /root/.acme.sh/cdn.nashsait.org_ecc/
root@cdn:~# scp -r root@94.232.245.17:/root/.acme.sh/cdn.nashsait.org_ecc/* /root/.acme.sh/cdn.nashsait.org_ecc/


Это копирование нужно сделать регулярным, поэтому на этих же четырех серверах в CRON добавляем ежедневный запуск команды:

scp -r root@94.232.245.17:/root/.acme.sh/cdn.nashsait.org_ecc/* /root/.acme.sh/cdn.nashsait.org_ecc/ && service nginx reload


Чтобы всё работало, доступ нидерландскому серверу со всех остальных четырех должен быть настроен по ключу, без необходимости ввода пароля. Обязательно сделайте это.

 

Настройка Nginx


На всех пяти CDN точках, в качестве веб-сервера для раздачи контента мы установим Nginx и настроим его как кэширующий proxy-сервер:

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


Дефолтный файл конфига /etc/nginx/nginx.conf заменим на приведенный ниже:

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=14d;
    proxy_cache_bypass $http_x_update;

server {
  listen 443 ssl;
  server_name cdn.nashsait.org;

  ssl_certificate /root/.acme.sh/cdn.nashsait.org_ecc/fullchain.cer;
  ssl_certificate_key /root/.acme.sh/cdn.nashsait.org_ecc/cdn.nashsait.org.key;

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


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

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

Обратите внимание на схожесть директив inactive и proxy_cache_valid. Чтобы не запутаться, рассмотрим их на простом примере. Вот что происходит при inactive=14d и proxy_cache_valid 90d:

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

Указав нужные значения в nginx.conf, применим их:

root@cdn:~# service nginx reload


Обратите внимание, что Nginx не будет кэшировать данные, если они получены с оригинального сервера с какими-либо cookies (заголовок «Set-Cookie»). Игнорировать это условие можно, добавив в конфиг следующие директивы:

proxy_ignore_headers "Set-Cookie";
proxy_hide_header "Set-Cookie";


На этом настройка завершена. Дополнительно можно создать bash-скрипт для очищения кэша:

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


С помощью онлайн ping-сервисов можно проверить пинги к нашей сети доставки контента из разных мест:
Пинг хороший, теперь разместим в корне основного сайта картинку test.jpg и на сервисе Ping-Admin посмотрим на скорость ее загрузки через CDN:
Всё работает, контент раздается быстро. Теперь у нас есть собственный рабочий CDN с безлимитным трафиком и точками присутствия на всех континентах.
 

© Habrahabr.ru