IT
April 15

Балансировщик для видео хостинга

Сразу же скажу, что это статья не про громадные хостинги по типу youtube, rutube, vimeo и т.д. А гораздо в меньших масштабах.

Но все же я получил некоторый опыт и хотел бы поделиться им с вами.

Задача

У меня был вэб-сервер на базе nginx + vod_module. На сервере была примонтирована директория с видеофайлами в формате mp4. Они были достаточно большие от 10 до 40Гб в среднем. Поэтому мне и пригодился модуль VOD, что бы отдавать видео контент в формате HLS. Подробнее об этом можно найти информацию в интернете. Я сейчас не буду об этом, статья у меня немного о другом. Так вот, модуль VOD довольно ресурсоемкий, особенно при большом количестве запросов. Он отъедает процессорное время и в лучшем случае видео загружается дольше. В худшем, их невозможно смотреть. И именно с этим я и столкнулся в конечном итоге.

Мне нужно было решить, как оптимизировать все это. Либо найти способы как сделать масштабирование.

Процесс

Сперва, я сделал небольшую оптимизация путем более тонкой настройки модуля. Но это не дало сильного прироста производительности.

Тогда я начал рассматривать вариант с масштабированием. Проект у меня бесплатный, поэтому я искал дешевые vps. Не очень хотелось платить еще за несколько vps из своего кармана.

В итоге, я нашел вполне хорошего хостера во всех смыслах. И цены приятные, ниже рынка и при этом отличная поддержка. Заказал у них сперва один сервер, настроил его. А после, попросил через поддержку склонировать виртуальные диски еще на 2 сервера.

Что получилось: 3 абсолютно идентично настроенных сервера.

Далее я настроил DNS, добавив 3 A-записи по типу hls.example.com с разными ip-адресами. Которые принадлежали этим серверам соответственно.

Это решило для меня:

  • Распределение нагрузки по серверам (round robin)
  • Распределение трафика

Но самое интересное, это настройка самого nginx. Ниже я опишу 2 способа конфигурации. Т.к. чуть позже РКН подпортили мне все и пришлось сделать еще один вариант.

Решение

Способ 1. Подключаем CloudFlare.

Cloudflare — это глобальная сеть доставки контента (CDN), которая предоставляет решения для повышения безопасности и производительности веб-сайтов.

Я решил, что было бы не лишним подключить CF. Что бы получить дополнительное кеширование + защиту от ddos и т.д.

В общем, для начале я закрыл все соединения извне. Разрешив обращаться к вэб-серверу только с ip-адресов CF. Найди их можно здесь: https://www.cloudflare.com/ips-v4

Для автообновления этого списка я сделал небольшой скрипт:

#!/usr/bin/env bash

file='cloudflare';

rm -f $file
touch $file

for i in `curl https://www.cloudflare.com/ips-v4`; do echo 'set_real_ip_from '$i';' >> $file; done
echo 'real_ip_header CF-Connecting-IP;' >> $file

Он создает файл cloudflare с заполненными в правильном формате данными для nginx.

Поставил, что бы он запускался раз в день по крону и забыл о нем.

Пример:

0 0 * * * cd /usr/local/nginx/conf && ./update_cloudflare.sh

Обновляем в 12 ночи каждый день.

Далее подключаем его в секцию http конфига nginx:

#cloudflare
include cloudflare;

Чуть не забыл сказать, что делает этот файл :) Он нам нужен для того, что бы nginx принимал от указанных ip-адресов заголовок "CF-Connecting-IP" и обрабатывал настоящий ip-адрес клиентов.

С nginx разобрались. Теперь нужно настроить фаервол.

Пример iptables:

# Закрываем изначально ВСЁ (т.е. изначально все что не разрешено - запрещено):
$IPT -P INPUT DROP

# Пропускаем соединения изнутри
$IPT -A INPUT --match state --state ESTABLISHED,RELATED -j ACCEPT

# clouflare
CF="173.245.48.0/20,103.21.244.0/22,103.22.200.0/22,103.31.4.0/22,141.101.64.0/18,108.162.192.0/18,190.93.240.0/20,188.114.96.0/20,197.234.240.0/22,198.41.128.0/17,162.158.0.0/15,104.16.0.0/13,104.24.0.0/14,172.64.0.0/13,131.0.72.0/22"

# web
$IPT -A INPUT -p tcp -m multiport --dports 80,443 -s $CF -j ACCEPT

Естественно, это не полный набор правил. Я выделил лишь основное.

Зачем мы закрыли доступ всем на порт 80,443? Все просто, наверно многие из вас знают. Что часто приходят различные робот-спайдеры которые "засерают" логи, различные сканеры, поисковые роботы, парсеры и т.д.

А упомянутые правила, как раз избавляют нас от всего этого. Теперь на наши сервера будут поступать только реальные запросы от клиентов. Но есть важный момент. Мне не нужны поисковики для этого поддомена! Основной индексируется нормально.

Способ 2. Напрямую.

Здесь нет ничего необычного, кроме того. Что я решил избавиться от лишних логов, на несуществующие домены.

Пример конфига nginx:

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    listen 443 ssl default_server;
    listen [::]:443 ssl default_server;

    ssl_certificate /usr/local/nginx/ssl/nginx-selfsigned.crt;  # Фиктивный сертификат (можно сгенерировать самоподписанный)
    ssl_certificate_key /usr/local/nginx/ssl/nginx-selfsigned.key;  # Фиктивный ключ

    server_name _;  # Сопоставляется с любым доменом

    # Отвечаем 444 (No Response) или 404 (Not Found)
    return 444;
    # или
    # return 404;

    # Отключаем логирование
    access_log off;
    log_not_found off;

    # Закрываем соединение для HTTPS (если используется 444)
    ssl_reject_handshake on;
}

Итог

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

P.S. В добавок я сделал защиту от скачиваний. Но это уже другая история.