Балансировщик для видео хостинга
Сразу же скажу, что это статья не про громадные хостинги по типу youtube, rutube, vimeo и т.д. А гораздо в меньших масштабах.
Но все же я получил некоторый опыт и хотел бы поделиться им с вами.
Задача
У меня был вэб-сервер на базе nginx + vod_module. На сервере была примонтирована директория с видеофайлами в формате mp4. Они были достаточно большие от 10 до 40Гб в среднем. Поэтому мне и пригодился модуль VOD, что бы отдавать видео контент в формате HLS. Подробнее об этом можно найти информацию в интернете. Я сейчас не буду об этом, статья у меня немного о другом. Так вот, модуль VOD довольно ресурсоемкий, особенно при большом количестве запросов. Он отъедает процессорное время и в лучшем случае видео загружается дольше. В худшем, их невозможно смотреть. И именно с этим я и столкнулся в конечном итоге.
Мне нужно было решить, как оптимизировать все это. Либо найти способы как сделать масштабирование.
Процесс
Сперва, я сделал небольшую оптимизация путем более тонкой настройки модуля. Но это не дало сильного прироста производительности.
Тогда я начал рассматривать вариант с масштабированием. Проект у меня бесплатный, поэтому я искал дешевые vps. Не очень хотелось платить еще за несколько vps из своего кармана.
В итоге, я нашел вполне хорошего хостера во всех смыслах. И цены приятные, ниже рынка и при этом отличная поддержка. Заказал у них сперва один сервер, настроил его. А после, попросил через поддержку склонировать виртуальные диски еще на 2 сервера.
Что получилось: 3 абсолютно идентично настроенных сервера.
Далее я настроил DNS, добавив 3 A-записи по типу hls.example.com с разными ip-адресами. Которые принадлежали этим серверам соответственно.
Но самое интересное, это настройка самого 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 разобрались. Теперь нужно настроить фаервол.
# Закрываем изначально ВСЁ (т.е. изначально все что не разрешено - запрещено): $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? Все просто, наверно многие из вас знают. Что часто приходят различные робот-спайдеры которые "засерают" логи, различные сканеры, поисковые роботы, парсеры и т.д.
А упомянутые правила, как раз избавляют нас от всего этого. Теперь на наши сервера будут поступать только реальные запросы от клиентов. Но есть важный момент. Мне не нужны поисковики для этого поддомена! Основной индексируется нормально.
Здесь нет ничего необычного, кроме того. Что я решил избавиться от лишних логов, на несуществующие домены.
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. В добавок я сделал защиту от скачиваний. Но это уже другая история.