Skip to content

Latest commit

 

History

History
229 lines (171 loc) · 28.5 KB

README.md

File metadata and controls

229 lines (171 loc) · 28.5 KB

HighloadArchitectureCourseWork

1. Выбор темы

В качестве темы был выбран сервис облачного хранения данных по типу Dropbox или Yandex.Disk. У сервиса должны быть функции, такие как:

  • загрузить файл
  • удалить файл
  • посмотреть информацию о файле
  • поиск по директории
  • поделиться ссылкой на файл

Сводная табличка по аналогичным сервисам

2. Определение возможного диапазона нагрузок

Положим количество пользователей сервиса равным 5М. Сервис будет ориентирован на аудиторию России. Известно, что основная часть населения России проживает в часовых поясах -1, 0, +1, +2 и +4 относительно Московского региона, который в свою очередь находится в зоне GMT+3, т.е. в европейской части России и рядом с ней.

Сервис будет предоставлять 4гб дискового пространства для бесплатного аккаунта, 100гб для платного. Ограничим объем загружаемых и скачиваемых данных для пользователя за день размером в 200% от его дискового пространства, т.е. 8гб/день для бесплатного аккаунта, 200гб/день для платного. Также положим, что 96% пользователей сервиса имеют бесплатный аккаунт, а 4% будут иметь платную подписку, т.е. платный аккаунт. Исходя из этого, получмем следующие данные:

account type storage percentage daily traffic total storage size total daily traffic
free 4GB 96% 8GB/day 4GB * 0.96 * 5 * 10^6 = 76.8 * 10^6 GB = 19.2PB 38.4PB/day ~ 444.5GB/sec = 3 556 Gbit/sec ~= 3.556 Tbit/sec
paid 100GB 4% 200GB/day 100GB * 0.04 * 5 * 10^6 = 80 * 10^6GB = 20PB 40PB/day ~ 463GB/sec = 3 704 Gbit/sec ~= 3.704 Tbit/sec

Из рассчетов получаем, что суммарных объем хранилища должен составлять 39.2PB.

Из данных similarweb и tokar.ua можно получить, что:

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

3. Выбор планируемой нагрузки

Введём некоторые допущения исходя из-за некоторой нехватки данных и отсутствия исследований рынка на эту тему.

Итак:

  • Экономика таких сервисов строится на том, что пользователи в среднем используют примерно 50% своего доступного места
  • Пользователь обновляет в среднем где-то 30%-50% от своего свободного места в год, остальной контент остаётся неизменным Для рассчета будет все измерять в изображениях и фото, т.к. в основном такие сервисы используются для загрузки фото и медиа контента пользователей Положим средний размер изображения в 2МБ, а обновление контента пусть будет 50% от хранимых данных
  • Пиковый трафик будет примерно в 4 раза больше, чем средний по рассчету
  • Также, обычно траффик на загрузку будет значительно выше, чем на скачивание. Но для простоты рассчёта примем, что они входящий траффик по объему такой же, как и исходящий
  • Хранить чанки файлов мы будем как минимум в двух хранилищах в разных зонах доступности
  • Размер чанка будет 2МБ

4. Логическая схема базы данных

Database scheme

Табличка Share (публичные ссылки на файлы) находится в Redis, а также там будут находиться сессии пользователей. Все остальные таблички находятся в RocksDB, причем один пользователь == одна база и имя базы будет идентично логину пользователя, из этого можно будет понять, в какую конкретно базу надо идти для каждого запроса. Соответственно, Redis общий для всех, а база метаданных отдельная для каждого пользователя.

5. Физические системы хранения

В качестве хранения метаданных о файлах пользователей возьмем RocksDB, для хранилища общих ссылок возьмём Redis с репликами.

Хранить метаданные файлов и их расположения мы будем для каждого пользователя в отдельности в RocksDB, т.е. один пользователь это одна база.

RocksDB - это высокопроизводительная встраиваемая open source база данных типа key-value для быстрых хранилищ (flash and RAM storages), поддерживаемая и разрабатываемая компанией Facebook. База данных основана на LSM-деревьях и написана на языке C++, поддерживает конкурентный доступ к данным, в особенности параллельную запись благодаря LSM-деревьям, т.к. эта структура данных крайне хорошо подходит для записи с большой пропускной способностью.

Redis с парой реплик вполне подойдёт для хранения общих ссылок, т.к. обычно их не так много (исходя из рассчёта на одного пользователя). Учитывая, что пользователи в основном будут хранить медиаконтент, то скорее всего он будет разбиваться на альбомы/папки и уже будет отдаваться ссылка на целый альбом. Собственно, поэтому очень грубо примем, что каждый пользователь имеет в среднем 10 общих ссылок. Также здесь будем хранить сессии пользователей.

Minio – использование в качестве S3 хранилища для клиентских чанков. Позволит нам организовать отказоустойчивую систему по хранению пользовательских данных, которая будет проста в эксплуатации и для интеграции в остальные части системы.

6. Прочие технологии (ЯП, протоколы взаимодействия, веб-сервера)

Поскольку файлы пользователя будут биться на чанки определённого размера, необходимы бекенды для сборки файлов из чанков. Также необходим бекенд, который будет отвечать за составление плана сборки файла из чанков и разборки на них и, соответственно, составлению плана запроса для бекендов-сборщиков (о них далее). Также он будет производить авторизацию и отдачу метаданных о файлах (список файлов, директорий, размер, дата создания, ...). Это будет бекенд-планировщик. Он также будет отвечать за шардинг, а точнее распределение загружаемых чанков на S3 Minio. Поскольку Minio сам будет заниматься растаскиванием на чанков по железным серверам, отвечать за отказоустойчивость и перераспределение файлов, задача этого сервера состоит в том, чтобы выбрать, в какие S3 совместимые хранилища грузить. Бекенд планировщик напишем на языке C++, а модули которые требуют максимальной безопасности и надёжности на языке Rust, т.к. он подходит для надёжных высокопроизводительных приложений.

В качестве сервера сборщика файлов возьмём nginx с самописным модулем, который будет осуществлять сборку и разборку файла пользователя по плану сборки, который поступает на него от бекенда-планировщика. Тажке, этот сервер будет осуществлять сжатие пользовательского контента, что даст некоторую экономию. Сборку он будет осуществлять по порядковому номеру чанка файла, информацию о котором будет отдавать бекенд-планировщик. Тажке для сборки можно уже вопспользоваться готовым модулем nginx SSI вкупе с самописным модулем. Также далее в тексте будет встречаться понятие nginx-сборщик.

Frontend - HTML, CSS, TypeScript. Для TypeScript будем использовать фреймворк React + ES7 ReactTS. Минификация позволит сократить размер контента примерно до 3МБ, поэтому их смогут спокойно раздавать nginx-сборщики, на которые будут литься траффик. Контент необходимо будет закешировать, дабы производительность была выше.

7. Рассчет нагрузки и потребного оборудования

Итак, учитывая сказанное в пункте 3 можно сделать несколько рассчетов и получить, что:

  • размер хранилища можно сократить в 2 раза, поэтому оно будет занимать 19.6ПБ
  • поскольку в год обновляется примерно 50% данных у каждого пользователя, то соответственно за год 9.8ПБ данных загружается на сервер, и учитывая, что пиковая нагрузка может быть как минимум в 4 раза выше, получается, что 9.8ПБ/год ==>> 1.243ГБ/сек. Возьмём с запасом примерно в 3 раза, поэтому будем считать, что входящий и исходящий трафик это 3ГБ/сек или 24Гбит/сек
  • поскольку мы условились, что измерения и рассчеты будут проводиться относительно картинок, то выходит, что на сервера записывается и считывается около 1500 изображений в секунду

Проведём рассчёт, сколько будет весить каждая запись в БД и сколько будет весить вся БД при заявленном числе пользователей.

  • 1 user record = 8 + 64 + 64 + 1 + 11 = 148Байт
  • 1 shared link = 36 + 8 + 8 = 52Байт
  • 1 file record = 8 + 8 + 12 + 256 + 8 + 8 + 8 = 308Байт
  • 1 chunk record = 8 + 8 + 8 + 32 * 3(кол-во зон доступности, пояснения далее по тексту) + 8 = 128Байт

на один загруженный файл приходится 436Байт

Учитывая, что размер хранилища у нас 19.6ПБ, то количество файлов будет около 19.6 * 10^9 МБ / 2 МБ = 9.8 * 10^9.

Итого:

  • все записи пользователей будут весить 148 * 5 * 10^6 = 740 * 10^6 байт = 740МБ
  • все записи общих ссылок будут весить 52 * 10 (кол-во ссылок на пользователя) * 5 * 10^6 = 2600 * 10^6 байт = 2.6ГБ
  • все записи для файлов будут весить 308 * 9.8 * 10^9 = 3018.4ГБ
  • все записи для чанков будут весить 128 * 9.8 * 10^9 = 1254.4ГБ

Суммарно, для хранения в RocksDB будет требоваться около 4.3ТБ дискового пространства.

Учитывая, что сессия пользователя это 64 байта UUID + 8 байт = 72 байта ID пользователя, получаем что для них нужно 72 * 5 * 10^6 = 360МБ. Т.е. для Redis необходимо около 3ГБ RAM.

Соответственно выходит, что в секнуду в базы RocsDB записывается около 436 Байт * 1500 = 654КБ. Для такой нагрузки хватит и одного среднего сервера с 6ТБ SSD. Но мы возьмем 6 SSD по 4ГБ в RAID10 и это даст двухкратное ускорение записи и четырёхкратное ускорение чтения, итоговый размер будет 12 ГБ, соотв имеем запас в почти 3 раза. Также для надёжности будем использовать ещё 2 slave реплики, а репликацию будем осуществлять с помощью open source решение rocksplicator для master-slave репликации RocksDB, разработанное проектом Pinterest.

Для хранения сессий и общих ссылок нам хватит небольшого сервера с 8ГБ-16ГБ RAM.

Для хранения данных пользователей будем использовать такое решение как Minio. Minio - S3 соместимое объектное отказоустойчивое хранилище данных. Это в как раз то, что нам нужно для хранение чанков пользователей. Для такого сервиса в кацестве дисков, на которых будут лежать чанки файлов, целесообразно использовать HDD, т.к. они ёмкие и в основном контент именно записывается, а не читается. Возьмем диски WD по 20ТБ и стенд от WD c 68 дисками (следовательно в сервер помещаетя 1360ТБ), большим объемом ОЗУ и двумя мощными процессорами Intel XEON. Данные пользователей будем хранить с использованием RAID10, соответственно каждый такой стенд даёт 680ТБ дискового пространства, а использование RAID10 с таким объемом даёт увеличение скорости чтения в 68 раз, а записи в 34 раза по сравнению с одинарным диском.

Итого:

  • В стенд от WD с 68 слотами с использоанием дисков по 20ТБ влезает 1360ТБ в базе, с использованием RAID10 - 680ТБ + увеличение скорости чтения в 68 раз, а записи в 34 раза по сравнению с одинарным диском.
  • Для покрытия 19.2 петабайт данных пользователей требуется 19.6ПБ / 0.68ПБ = 29 стендов.
  • Для надёжности у нас будет 2 реплики хранилища данных, поэтому по итогу будет 56 таких стенда.
  • Для хранения сессий и shared ссылок пользователей вполне хватит и сервера с 8ГБ оперативной памяти и дешевым процессором. Также будем это реплицировать ещё на 2 аналогичных сервера.
  • Для сервера планировщика запросов требуется 6 SSD по 4ТБ в RAID10 для хранения (это 12ТБ + ускорение записи в 2 раза, чтения в 4 раза), среднее количество оперативной памяти и средний процессор. Также у этого сервера будет по 2 реплики. Репликация будет осуществляться по принципу master-slave с использованием rocksplicator.
  • Для серверов nginx-сборщиков потребуется мощный процессор и много оперативной памяти, диск им не нужен, т.к. HTML, CSS, JS они могут отдавать с серверов-хранилищ и кешировать его.
  • Исходя из документации nginx, можно сделать вывод, что нам с головой хватит и 2 nginx-сборщиков даже с учётом использования HTTPS. Для ускорения возьмем столько же, чтобы другие были в другой географической зоне для меньших задержек. Также, сделаем резервирование каждого сборщика. Итого, нам потребуется 8 nginx-сборщиков, из которых половина будет в резерве.
  • Учитывая объём трафика и количествщ nginx-сборщиков возьмём 10Гбит/с сетевые карты, этого хватит с запасом.

Сводная таблица для простоты восприятия:

Server CPU RAM Disk Type RAID Type Disks count Storage Size with RAID Amount Replicas count Total Amount
nginx-сбрщик 32 128GB 512GB SDD - 1 - 2 & 2 in reserve = 4 2 12
Redis (ссылки + сессии) 8 16GB 1TB SDD - 1 - 1 2 3
бекенд-планировщик 24 32G 4TB SSD RAID10 6 12TB 1 2 3
Minio S3 хранилища 24 + 24 (dual core) 32GB * 16 = 512GB 20TB HDD RAID10 68 680TB 29 2 84

Используемые материалы:

8. Выбор хостинга / облачного провайдера и расположения серверов

Сервера расположим вблизи сетевых магистралей, а также в местах с высокой плотностью населения. Поэтому расположим наши сервера в Москве и Новосибирске, это позволит удовлетворить наши требования и снизить время отдачи/загрузки контента для пользователя. В Новосибирске возьмём 1 датацентр, а в Москве возьмём 2 разных датацентра, т.к. основная часть пользователей находится в европейской части России, а из Новосибирска в европейскую часть отдавать контент будет очень затратно и долго. В качестве провайдера стоит выбрать Ростелеком или ТТК, поскольку они имеют наибольшее покрытие в РФ.

9. Cхема балансировки нагрузки

Для балансировки воспользуемся geo-ip балансировкой. В качестве решения возьмём облачное решение Route53 от Amazon, т.к. он поддерживает такой вид балансировки, а также довольно прост в конфигурировании. Для надёжности укажем небольшой TTL DNS записи (например 5 минут), чтобы в случае отказа даже двух серверов (основного и резервного) пользователи всё равно смогли получить доступ к ресурсу, тем самым снижая время отказа.

Резервирование будем осуществлять с использованием CARP. Это технология позволит сделать резервирование наших nginx-сборщиков и почти моментально подменять упавший основной сервер в случае внештатных ситуаций. Работать будет следующим обраом:

  • Обслуживать клиентов будет основной nginx-сборщик
  • В случае если основной сборщик не отвечает происходит переключение лидирующей роли на вторичный сервер, он становится лидирующим

10. Oбеспечение отказоустойчивости

Многое по этому пункту уже было сказано в предыдущих пунктах, резюмируем и добавим ещё некоторые вещи, а именно:

  • Репликация метаданных пользователей с помощью rocksplicator

  • Репликация данных пользователей (чанков) в разные ДЦ в разные зоны доступности

  • Репликация in memory базы данных Redis с сессиями пользователей и общими ссылками

  • Резервирование nginx-сборщиков для быстрой подмены на рабочие в случае отказа основного

  • GeoIP DNS балансировка для снижения задержек на сервис и уменьшения latency

  • Использование RAID для хранения данных (как бд, так и пользователей) для ускорения записи и чтения вкупе с повышением надёжности

  • Использование bonding для объединения сетевых интерфейсов в Linux. Это даст большую пропускную способность на nginx-сборщике, а также позволит повысить отказоустойчивость в случае, если один из каналов станет недоступен, т.е. к на сервер возьмём 2 сетевые карты 10Гбит Ethernet и объединим в один логический интерфейс.

  • Необходимо придерживаться современных практик при разработке, тестировании и эксплуатации, дабы повысить качество продукта, тем самым снизить вероятность его отказа и происхождения различного рода внештатных ситуаций. А конкретнее:

    • Использование TDD там, где это возможно
    • Написание большого количества smoke-, unit-, end-to-end-тестов. (Это особенно важно, т.к. мы будем работать с языкыками с ручным управлением памятью)
    • Регулярные code review
    • Использование Agile/scrum подходов к разработке (Позволяет ускорить процесс разработки)
    • CI & CD
    • Внедрение метрик в разработанные сервисы, а также подключение систем для сбора и отображения этих метрик + оповещения в случае внештатных ситуаций