Задание курса Highload третьего семестра основной программы Технопарка Mail.ru. Требовалость создать архитектуру сервиса и вычислить количество оборудования, необходимое для создания сервиса, покрывающего 30% выбранного рынка.
VPN provider.
количество пользователей интернета в России = 90000000
количество пользователей VPN в России = 24%
средний трафик на мобильное устройство в месяц = 8 Gb
моделируемая для рынка в России = 30%
Получаем нагрузку в 20 Gb/s
Особенности хорошего VPN провайдера:
- Не хранит данные пользователей, не логгирует конечные адреса.
- Предоставляет безлимитный доступ, только ограничивает максимальную скорость в зависимости от тарифа. По мнению МТС: "Наши клиенты живут в цифровой среде и не хотят думать о лимитах на интернет, они воспринимают их как искусственные ограничения."
Поэтому в базе хранится только чувствительная и важная информация: Логин, пароль пользователя, его текущий тариф в Kb/s.
5 Физическая системы хранения (конкретные СУБД, шардинг, расчет нагрузки, обоснование реализуемости на основе результатов нагрузочного тестирования)
Расчёт нагрузки на базу данных:
VPN сервера делают 1 select в момент подключения клиента, Billing cистема делает update раз в месяц.
среднее время посещения pikabu.ru = 9 мин.
Рассматрим этот развлекательный сайт как типичный случай использования. Пользователь будет переподключаться к VPN, генерируя 1 запрос в базу, раз в 9 минут.
Получается, что наши потенциальные 6480000 пользователей, используя сервис единовременно, будут генерировать нагрузку 12000 rps select по индексу для проверки авторизации.
6 Выбор прочих технологий: языки программирования, фреймфорки, протоколы взаимодействия, веб-сервера и т.д. (с обоcнованием выбора)
Части, из которых состоит сервис:
- Сервис обеспечивает VPN. В качестве сервера выберем openvpn, он свободный и является стандартом. Используя его, мы получаем совместимость с официальным мобильным приложением, а пользователи получают большое количество обзоров.
- Сервис проверяет, заплатил ли пользователь за услугу. Нужна база данных с логинами / паролями / тарифами. Выберем Postgres как надёжное хранилище данных.
- OpenVPN должен ходить в сторонний сервис для авторизации. Для этого нужен плагин на C. См обоснование необходимости в 6.1.
- Так как писать логику шардирования и походов в базу данных на C сложно, нужен отдельный сервис. Он должен выдерживать 12000 rps. Golang способен справиться с такой нагрузкой и изменяется в след за бизнес требованиями гораздо легче С. Для устойчивости используем 2 таких сервера на соседних серверах.
- Сервис должен масштабироваться самостоятельно. Мы хотим экономить на персонале, дешевле сразу сделать хорошо. Упаковываем сервера с бизнес логикой в docker. Для openvpn серверов, где производительность сети является ключевым элементом, отключаем виртуализацию сети c помощью
docker run --network=host
. Для самой первой версии продукта используем Ansible, потом переходим на kubernets, как только будут решены проблемы бизнес логики. - Так как пользователи изначально используют VPN частично для защиты личных данных и анонимного доступа, то нормальный сервис не должен собирать данные о соединениях. Нужен мониторинг только нагрузки оборудования и сети. Для сбора данных используем ClickHouse. Для отображения используем grafana и официальный плагин для доступа к clickhouse. Настраиваем себе оповещения через в telegram.
- Сервису нужна точка входа. Собираем landing c кратким описанием возможностей, ценой для разных скоростей. Экономим на начальном этапе, собираем визитку на tilda.
- Сервис должет предоставлять небольшой личный кабинет. Пользователь будет заходить туда несколько раз в месяц. При моделируемом количестве пользователей получаем 400 rps. Необходимые функции: просмотр статуса пользователя, тарифа. Кнопка "попробовать" или "продлить" или "оплатить". Нужен безпроблемно расширяющийся с бизнес логикой монолит, выберем Django. Судя по тестам производительности, 2-x серверов хватит. В начале и конце месяца нужно иметь возможность увеличить производительность, kubernets уже запланирован.
- Сервис должен принимать оплату c как можно большего количества систем. По опыту одного успешного студенческого startup'а интегрируемся с robokassa.ru , который снимает с разработчиков проблемы других множественных интеграций.
OpenVPN поддерживает авторизацию, для этого используются плагины. На данный момент не существует готового решения для подключения к PostgreSQL. Интерфейс плагинов авторизации - набор функций на С. Он хорошо задокументирован. Есть официальный легковесный плагин, позволяющий запускать внешний исполняемый файл или скрипт. Он делает exec при каджой попытке авторизации, передавая описание пользователя через переменные окружения. Мы считаем, что пользователи равномерно переподключаются к 30 серверам, создавая на одном сервере 400 событий авторизации в секунду.
Примитивный замер производительности exec:
time bash -c ' \
for ((i = 0; i < 400; i++)); do \
python3 -c \
"import os; print(os.environ);" \
> "/dev/null"; \
done \
';
real 8.429s user 7.095s sys 1.346s
показывает, что каждый раз инициализировать интерпретатор не получится. В то же время замеры для простого бинарника
time bash -c ' \
for ((i = 0; i < 400; i++)); do \
"/usr/bin/env" > "/dev/null"; \
done \
';
real 0.361s user 0.270s sys 0.109s
доказыает, что возможно запускать простой бинарник, хотя это и занимает от 20% до 50% 1 ядра. Так как всё равно придётся писать на компилируемом языке, то лучше всего написать свой плагин на C, используя плагин для запуска скриптов как пример. Так как писать бизнес логику на С довольно долго и дорого, плагин должен:
- устанавливать постоянное соединение с backend сервером
- упаковывать передаваемые данные о новом подключении в json
- отправлять в сервер с логикой
- асинхронно ожидать ответа
- закрывать все соединения при выключении
- Расчёт пропускной способности VPN. Возьмём официальные замеры производительности. Требуются сервера с аппаратной реализацией шифрования: с набором инструкций AES-NI. Это увеличивает производительность с 156 Mb/s до 750 Mb/s. Учитывая моделируемую нагрузку 20Gb/s при одновременном подключении всех пользователей, необходимо 30 серверов Intel Xeon X5660 CPU @ 2.80GHz с сетевой картой gigabit ethernet (как в тесте).
- Расчет базы данных: В базе хранится логин пароль и тариф. Такая база на 90000000 записей будет занимать < 1 gb и помещаться в оперативную память. Потому данные можно не шардировать, только создать несколько реплик для масштабирования нагрузки.
По опыту Yandex, стоит выбрать Финляндию для расположения серверов. Преимущества: датацентры стоят дешевле, за счёт охлаждения уличным воздухом напрямую. Терпимый ping для европейской части России.
ping | Helsinki |
Moscow | 28.15ms |
Novosibirsk | 94.05ms |
St Petersburg | 7.00ms |
Vladivostok | 120.13ms |
Выбираем самый дешёвый способ: свои сервера в арендованных стойках.
Наш сервис ориентируется на множество мобильных пользователей, на множественные медленные длинноживущие соединения. Все сервера VPN одинаковы. Можно использовать DNS балансировку. Вся нагрузка приходится на VPN сервера, доступные по белому ip адресу непосредственно из сети. Также можно использовать балансировку в момент оплаты. В личном кабинете выдаётся ключ и адрес для подключения. Выдавать стоит не все сервера, а небольшое подмножество. Это также позволяет отправлять новых пользователей на новый, только что установленный, сервер, не замедляя соединения старых клиентов. Будем использовать абстракцию: пользователь знает об одном адресе, при этом за ним будет скрываться несколько физических серверов с одним и тем же сертификатом. Позволяет увеличивать мощность конкретного адреса, заменять физические машины без изменения адресов подключения на клиентских устройствах.
Для обеспечения отказоустойчивости не должно быть единых точек отказа в критически важной части сервиса.
1 У пользователя есть URL и ключ.
2 При падении физического сервера с OpenVPN клиенты пытаются соединиться заново, ip перемещается на рабочий сервер.
3 При падении go сервера авторизации плагин использует другое соединение.
4 Выше говорилось, что размер базы данных с логином паролем будет около 1 gb. Это позволяет не делать шардирование, и ходить в любой из экземпляров базы данных. При падении базы данных можно просто сходить в соседнюю.
5 Application сервер на Django имеет несколько экземпляров. При падении Nginx балансировщик перестаёт отправлять запросы на упавший сервис.