Что нам стоит CDN построить?

    Привет Хабр! В этой статье мы будем строить свой CDN. Почему не воспользоваться готовыми решениями? Потому что сайт автора полностью статический, сделанный на Jekyll, с большими картинками, которые нужно отдавать максимально быстро. Сервер не должен быть кэширующим, он должен хранить сайт целиком, поддерживать HTTP/2 и Brotli, а на всех серверах должен быть установлен один и тот же сертификат.

    Ещё мы сделаем это всё на IIS, работающим под Windows Server 2019 Core.


    Кратко о реализации


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

    1. Active Directory
    2. DFS
    3. IIS
    4. WinAcme
    5. RSAT

    Опционально, но рекомендуется:

    1. Windows Admin Center

    На узлах с сайтами нужны только службы IIS и DFS, Active Directory нужна обязательно, она требуется для DFS, которая будет синхронизировать содержимое сайта между серверами. RSAT нужен, чтобы управлять компонентами, а Windows Admin Center — чтобы править реестр. Это можно сделать и через Powershell, о чём тоже расскажу. 

    Домен автора называется ***.***.wtf (пожалуйста не пугайтесь), а серверы именованы по дата-центрам и имеют имена вида cache-zur1, cahe-ru и так далее. AD развёрнута, а серверы подключены к ней. Теперь по порядку.

    Выбор точек


    У RUVDS 8 дата-центров — 3 в Европе и 5 в России. Мой выбор пал на Rucloud в Москве и LD8 (тот что в Лондоне). Rucloud потому, что почти ничем не отличается от М9, а через Лондон идёт трансконтинентальный кабель до США, поэтому в Европе маст хэв. Для более плотного размещения по Европе можно выбрать Швейцарию или Германию — на этом, в целом, можно остановиться.

    Выбор DNS GeoIP


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

    Мы можем использовать свой DNS (BIND с плагином для Maxmind) или готовое решение (Route53). Подписка на GeoIP-базы Maxmind стоит $25 в месяц, а Route53 стоит $0.50 за одну доменную зону, плюс копейки, если выйдешь за миллион запросов. К тому же, создавать ещё одну точку усиления DDoS-атак это последнее дело, поэтому мой выбор пал на Route53.

    В этой статье речь идёт о CDN для Европы; если вам нужен CDN для России, то выбор в сторону своего DNS очевиден, потому что Route53 осуществляет роутинг по странам, а не по городам.
    Сервисы подобного рода есть и у Майкрософт (Azure Traffic Manager).

    1. Установка IIS


    1.1. Установка IIS

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



    Через Powershell: 

    Install-WindowsFeature Web-Server, Web-CertProvider

    На главном сервере нужно дополнительно установить: 

    Install-WindowsFeature Web-Mgmt-Service

    1.1.1 Включаем удалённое управление

    Чтобы мы могли удаленно управлять сервером IIS, нам нужно поднять службу удаленного управления IIS. На главном сервере запускаем службу:

    start-service WMSVC
    set-service -Name WMSVC -StartupType Automatic

    И теперь включаем возможность управления как таковую через реестр.

    HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WebManagement\Server

    Проще всего управлять реестром, конечно, через Windows Admin Center. 



    Но это можно провернуть и через Powershell:

    Set-Itemproperty -path "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WebManagement\Server" -Name "EnableRemoteManagement" -value "1"

    В Windows Server 2012 и 2016 правило фаерволла не поднимается само, нужно править фаерволл. 



    Set-NetFirewallRule -Name IIS-WebServerRole-WMSVC-In-TCP -Enabled True

    Проверяем, поднялось ли:



    Test-NetConnection 8.8.8.8 -port 8172

    Три правки в трёх разных местах и теперь мы можем подключаться через диспетчер IIS к нашему серверу, используя другой сервер с десктопом. Это нужно было лишь для удобства последующего управления.

    1.2 Врубаем централизацию конфигов

    Для установки централизованных конфигураций и сертификатов в диспетчере IIS нет кнопок, они есть только в диспетчере локального сервера, так что для Server Core делать всё придется через Powershell.

    Централизация конфигов и сертификатов осуществляется через SMB. Поэтому я сделал двух бесправных пользователей (отдельно для конфигов и отдельно для сертификатов), которые имеют доступ на чтение только к своей папке и назвал их certadmin и configadmin.

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

    1.2.1 Делаем общую папку

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

    New-SmbShare -ReadAccess configadmin@**.**wtf -Path C:\windows\System32\Inetsrv\Config -Name sharedconfigs

    А для сертификатов можем выбрать произвольный. Я помещаю их в папку с IIS.

    New-SmbShare -Path C:\inetpub\centralizedcerts -Name sharedconfigs -ReadAccess configadmin 

    1.2.2 Подключаем узлы к конфигам

    Настройку нужно проводить только для тех серверов, которые этот конфиг будут читать. Мой главный сервер под именем cache-ru будет головой, поэтому я осуществлял настройку на двух других.
    Вводим пароль пользователя, имеющего доступ к папке:

    $pass = Read-Host -AsSecureString
    Enable-IISSharedConfig -PhysicalPath \\cache-ru.**.**wtf\SharedConfig -UserName configadmin@**.**wtf -Password $pass -DontCopyRemoteKeys

    Сразу после ввода должен открыться новый сеанс. Чтобы проверить, что всё заработало, можно открыть RSAT → Управление компьютером → Общие папки → Сеансы. Мы увидим сеансы пользователя и ip-адрес сервера, который под этим пользователем читает общую папку. На строне клиента проверяется командлетом:

    Get-IISSharedConfig

    Вот так это выглядело у меня:



    1.2.2 Подключаем узлы к сертификатам

    Следующую строку можно вводить только напрямую, имея прямое подключение к рабочему столу под управлением Server c GUI, на Server Core она не работает.

    $pass = Read-Host -AsSecureString
    Enable-IISCentralCertProvider -CertStoreLocation \\cache-ru.**.**wtf\centralizedcerts -UserName certadmin@**.**wtf -Password $pass

    Если попытаетесь ввести её через Winrm, то увидите такой вывод:



    Чтобы сделать это удалённо, нужно править реестр. Как удобно! Залетаем в:

    HKLM:\SOFTWARE\Microsoft\IIS\CentralCertProvider\

    Создаём 32-битный DWORD «Enabled» с параметром «1»:



    Set-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\IIS\CentralCertProvider\ -Name Enabled -Value 1

    Потом создаём строковое значение CertStoreLocation с параметром \\cache-ru.**.**wtf\centralizedcerts:

    Set-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\IIS\CentralCertProvider\ -Name CertStoreLocation -Value <a href="about:blank">\\cache-ru.**.**wtf\centralizedcerts</a>

    Чтобы проверить, всё ли в порядке, используем:

    Get-IISCentralCertProvider

    Аутпут должен быть таким:



    И только после этого вводим:

    $pass = Read-Host -AsSecureString
    Set-IISCentralCertProvider -UserName certadmin@**.**wtf -Password $pass

    Ещё раз проверяем:

    Get-IISCentralCertProvider

    Всё из коробки, что называется. В отличие от общих конфигов, централизованные сертификаты не создают активную SMB-сессию.

    1.2.3 Brotli

    На каждый из серверов скачиваем и запускаем файл IIS Compression

    Start-Process .\iiscompression_amd64.msi -ArgumentList /quiet

    Мы уже расшарили конфиги, поэтому конфигурировать что-либо нужно только в одном месте. С помощью диспетчера IIS переходим в редактор конфигураций на главном сервере.



    system.webServer/httpCompression

    И устанавливаем StaticCompressionLevel на 11 для Brotli и на 9 для Gzip, это максимум, что они умеют.

    1.2.4 MIME и заголовки

    Из коробки IIS не передаёт заголовок о кодировке, из-за чего кириллица превращается в руны. Также в MIME отсутствует запись о формате webp, которую нужно добавить.

    1. Идём в диспетчер → MIME. Находим .HTML и модифицируем его тип на: text/html; charset=UTF-8



    2. Не забываем про Webp — этот MIME нужно добавить вручную.



    2. Выпускаем сертификат


    С помощью Winacme. Сначала, нужно забиндить домены к сайту — это можно сделать через диспетчер IIS. При построении CDN не выбирайте верификацию по хосту, т.к. сразу начинаются проблемы с подтверждением владения доменом, наш выбор — верификация по TXT-записи. Придётся вручную вбивать путь к папке с cертификатами, так же вручную копировать запись из челленджа. А теперь, наберите воздуха в грудь.

    Чтобы не печатать всё вручную, включаем RDP на Server Core следующей командой:

    cscript C:\Windows\System32\Scregedit.wsf /ar 0

    Вот так выглядит RDP на Server Core:



    После выполнения челленджа сертификаты падают в папку, которая была указана в Winacme. Winacme ставит задачу в планировщик на обновление сертификата. Установили и забыли.



    Ну а как закончили, можем выключить RDP — не зря ставили Server Core, столько нюансов обнаружили.

    cscript C:\Windows\System32\Scregedit.wsf /ar 1

    3. Биндим сертификат к узлам


    Теперь нужно настроить две другие ноды, чтобы наконец заработал HTTPS. В это время на сервере, где хранятся сертификаты, HTTPS уже работает. Чтобы и остальные начали работать по HTTPS, ну, вообщем, это делается через netsh. 

    Как уже мы привыкли, подключаемся к Windows Server Core по RDP чтобы запустить эту оснастку. Не смотрите на сайт в майкрософт, особенно в это руководство. Шаги в нём описаны неправильно.

    С помощью netsh http show sslcert, на мастер-ноде нужно получить appid, который вводим так, как написано ниже:

    netsh http add sslcert ccs=443 appid= '{4dc3e181-e14b-4a21-b022-59fc669b0914}'

    Значение appid должно быть в кавычках, иначе не сработает.

    4. Установка DFS


    Не синхронизируйте сертификаты и конфиги через DFS, специализированные инструменты придуманы не просто так. Я пытался и вышло плохо, по какой-то неясной причине конфигурации на серверах, которые конфиг брали, просто перестали читать изменения, хоть и по первому времени это всё работало. Причину сбоя я не выяснял и решил сделать по-другому.

    Дальнейшая настройка производится исключительно через RSAT, командлеты которые указаны в документации, работают только под управлением Windows Server с GUI. Развернуть репликацию DFS, используя исключительно Server Core, невозможно. Нужен либо ещё один сервер с GUI, либо компьютер на Windows 10 Pro с установленным RSAT, присоединённым к домену.

    2.1. Устанавливаем

    На каждый из серверов нужно установить DFS Replication:



    Install-WindowsFeature FS-DFS-Replication, FS-DFS-Namespace

    2.2. Делаем новую группу репликации



    Сперва нужно пошло назвать нашу группу репликации.



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





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



    В качестве папки для репликации я выбрал дефолтную папку с сайтами: 

    C:\inetpub\wwwroot

    Эта папка — путь к каталогу конкретно этого, первого сервера. Реплицировать содержимое этой папки можно куда угодно, независимо от пути.



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



    Закончили.

    5. Тестируем производительность


    Чтобы понять, сколько посетители сайта реально выиграли, нужно провести замеры. Проводиться они будут из двух точек. ПК автора в Москве и виртуальный сервер во Франкфурте. Тестироваться будут отдельно все три точки. Вот краткое содержание.



    Заходим в Devtools и наблюдаем водопад. В среднем по больнице, CDN сократит около 200 миллисекунд до полной загрузки сайта. Картинка, самый большой объект на странице, загружается как только попадает во вьюпорт, поэтому обратите внимание на синюю вертикальную черту. CDN ускорил чистый HTML + стили и скрипты на 10-15 миллисекунд, а картинка загрузилась на 220 миллисекунд быстрее, это очень значительная разница.

    Москва — Москва:

    Лондон — Москва:

    Цюрих — Москва:

    Также я замерял скорость Jekyll’a на локальном хосте:



    Переходим к лайтхаусу и видим странные результаты:

    Москва — Москва:

    Лондон — Москва:

    Цюрих — Москва:

    Localhost — Jekyll:

    В этой синтетике сервер, который за тридевять земель обходит локального хоста. Но почему?
    Давайте ради интереса избавимся от Google Fonts и перенесём их на наши серверы.

    Москва — Москва:

    Лондон — Москва:

    Цюрих — Москва:

    Localhost — Jekyll:

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

    Теперь воспользуемся VPS с двумя ядрами и 4 гигабайтами ОЗУ, расположенную во Франкфурте и посмотрим, что скажет она. Водопады:

    Москва — Франкфурт:

    Лондон — Франкфурт:

    Цюрих — Франкфурт:

    Тут, при правильно выбранной точке экономия времени лишь на стилях и HTML составила 200 миллисекунд. То есть в случае с клиентом из Франкфурта сайт просто будет быстрее на 200 миллисекунд. Это также отражается и в лайтхаусе. В подобных случаях CDN ускорит не только интернет-магазин, но и лёгкий блог.

    Москва — Франкфурт:

    Лондон — Франкфурт:

    Цюрих — Франкфурт:

    А теперь включим Google Fonts и снова посмотрим на лайтхаус.

    Москва — Франкфурт:

    Лондон — Франкфурт:

    Цюрих — Франкфурт:

    К сожалению у меня больше нет точек, на которых можно было бы провести тесты, поэтому и закончу.

    Выводы


    • CDN для вывода всей статики необходим, особенно если речь идёт о тяжёлых картинках или длинных скриптах. 
    • Использовать Google Fonts можно и нужно, в тяжёлых сценариях это действительно ускоряет сайт.
    • Главный сервер, если решите делать всё это на Windows, желательно должен иметь GUI.
    • Пользователей, которые будут брать конфиги с главного сервера, лучше всего делать локальными — в случае выхода из строя контроллера домена, ноды могут потерять связь с конфигами и сертификатами, а Application Pool будет остановлен, т.к. не сможет прочитать конфиг.


    • +20
    • 5,1k
    • 7
    RUVDS.com
    1 048,37
    RUVDS – хостинг VDS/VPS серверов
    Поделиться публикацией

    Комментарии 7

      0
      А можно то же самое на Ubuntu?
        +1
        DNS GeoIP — а что если у пользователя будет не провайдерский DNS? Например он делает запрос из Австралии, а DNS'ы у него прописаны США?

        Не совсем понял как дела обстоят с автоматизацией? То есть настройка происходит как описано в статье? Руками надо выполнять команды в powershell, прожимать галочки, кнопочки и т.д.? А если дропнуть все сервера, за сколько времени поднимите аналогичную настройку?

        На мой взгляд гораздо проще и выгодней было поднять, тоже самое но на: linux + nginx, и все это автоматизировать ansilble (или другой scm).
          –2
          Когда вы покупаете доменное имя, одной дефолтной записью является запись о делегировании имени на DNS. По умолчанию это DNS регистратора.
          В этой статье было делегирование на Route53, то есть все остальные DNS сервера будут обращаться за записями именно к ним.
          Таким же образом вы можете поднять свой DNS и делегировать имя на него,

          Теперь рассмотрим как это происходит на простом примере. Мы сделали новый сайт, о котором еще никто не знает.

          1. Пользователь набирает в браузерной строке доменное имя.
          2. Браузер начинает разрешать имя в ip адрес и обычно идет за этим к DNS провайдера
          3. Если на DNS записях провайдера информации о имени нет, он смотрит на какие DNS делегировано имя и за записью идёт к ним.
          4. Route53, и Azure Traffic Manager или ваш собственный BIND смотрит в геоданные ip адреса обратившегося и даёт ему релевантный ответ.

          А если дропнуть все сервера, за сколько времени поднимите аналогичную настройку?
          Две минуты, если хоть один из них остался жив, и около получаса, если умерли все.
          Самая сложная часть здесь это выпуск сертификатов и установка модуля для компрессии.

          На мой взгляд гораздо проще и выгодней было поднять, тоже самое но на: linux + nginx, и все это автоматизировать ansilble (или другой scm).
          В случае RUVDS цена на виртуальный сервер c ОС Linux не отличается от цены VPS с ОС Windows Server.
            0
            а что если у пользователя будет не провайдерский DNS

            Если рекурсивный днс сервер поддерживает EDNS Client Subnet — на днс сервер будет отправлена подсеть того, кто запросил ответ. Подробнее тут https://tools.ietf.org/html/rfc7871
            Из консоли можно потыкать так:
            dig 1669655317.rsc.cdn77.org. @ns1.cdn77.org. +subnet=181.214.240.0/24

            0
            В 2019 году все так же необходимо руками править реестр?
              0
              Можно поверщелью
              0
              Если клиент стучится до сайта, то как понять, какой сервер ему должен передать данные? С помощью DNS конечно. То есть в зависимости от ip-адреса того, кто обратился к DNS, будет дан релевантный ответ


              CDN из нулевых подъехал. BGP anycast — далекое еще будущее (Сарказм)

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

              Самое читаемое