Представим, что у вас есть CDN, но нет необходимости/желания/денег ставить выделенные железные серверы под шилдинг(shielding). Шилдинг - это дополнительный уровень кэширования для защиты origin(источник контента), чтобы запросы от edge к origin шли через промежуточные серверы. Он необходим для снижения нагрузки на origin, так как edge-нод может быть очень много и если каждая будет обращаться к origin напрямую, то он начнет погибать под нагрузкой. С шилдингом на origin всегда будет обращаться только выделенная группа серверов.
Архитектура
Есть 3 раздающие edge-ноды и 2 origin-сервера:
🔍edge-ноды: edge1, edge2, edge3
🔍софт: Nginx
🔍origin: origin1, origin2
Будет использоваться директива split_clients на каждой edge-ноде. Она делит запросы на три равные части (по 33%):
🔍33% - остаются на текущей edge-ноде и уходят напрямую в origin
🔍33% - уходят на первую соседнюю edge-ноду
🔍33% - уходят на вторую соседнюю edge-ноду
При этом split_clients гарантирует идемпотентность(красиво звучит): один и тот же $request_uri всегда попадает в одну и ту же группу. Благодаря этому 99% запросов к кластеру стабильно распределяются по всем трём edge-нодам и далее на origin без «скачков».
Пример конфигурации для архитектуры из 3-х edge-нод.
Ввиду ограничения на количество символов в одном посте, конфигурация упрощена до минимально необходимой, чтобы показать логику работы.
Базовый апстрим для origin на каждой edge-ноду:
upstream origin{
server origin1;
server origin2;
}
Апстримы для шилдирования на соседние edge-ноды:
upstream edge1-shield {
server edge1;
server origin1 backup;
server origin2 backup;
}
upstream edge2-shield {
server edge2;
server origin1 backup;
server origin2 backup;
}
upstream edge3-shield {
server edge3;
server origin1 backup;
server origin2 backup;
}
Итого, каждый апстрим смотрит на соседа по кластеру и бекапом на origin.
Далее настраиваем распределение трафика через split_clients на каждой edge-ноде.
edge1
split_clients $request_uri $upstream_backend {
33% origin;
33% edge2-shield;
33% edge3-shield;
* origin;
}
edge2
split_clients $request_uri $upstream_backend {
33% edge1-shield;
33% origin;
33% edge3-shield;
* origin;
}
edge3
split_clients $request_uri $upstream_backend {
33% edge1-shield;
33% edge2-shield;
33% origin;
* origin;
}
Как это работает.
$request_uri - ключ, на основе которого происходит распределение/хэширование.
$upstream_backend - переменная, в которую будет сохранён выбранный upstream.
То есть для примера 33% origin - это если хэш попадает в первые 33% диапазона, то переменной $upstream_backend присваивается значение origin.
* - значение по умолчанию для оставшихся случаев (1%).
Далее в location эта переменная используется для указания апстрима, например:
location / {
proxy_pass http://$upstream_backend;
}
То есть в зависимости от того, в какой диапазон попал хэш $request_uri$, upstream_backend может принимать значения:
🔍origin
🔍edge1-shield
🔍edge2-shield
🔍edge3-shield
Это актуально, когда 50-100 edge-нод начнут идти за одним и тем же контентом на origin и ему станет тяжело, как минимум можно быстро упереться в емкость сетевых карт.
С использованием split_clients (один uri):
Клиент1 -> edge1 -> edge2 -> origin (edge2 кэширует)
Клиент2 -> edge1 -> edge2 -> из кэша edge2 (без origin!)
Клиент3 -> edge2 -> из кэша edge2 (без origin!)
Клиент4 -> edge2 -> из кэша edge2 (без origin!)
Клиент5 -> edge3 -> edge2 -> из кэша edge2 (без origin!)
Клиент6 -> edge3 -> edge2 -> из кэша edge2 (без origin!)
Работает только на уровне одного кластера edge и если их много (в разных городах), то каждый все равно будет обращаться на origin, но уже не каждая edge-нода!
Мы это использовали для определенного типа трафика, при котором origin-серверов было минимальное количество, была опасность их «положить».
🎤 Будни сетевика 😊 #Задача