Міжнародная канферэнцыя распрацоўнікаў і карыстальнікаў свабодных праграм

Базовая серверная архитектура для высоконагруженного стартапа

Михаил Пянко, Минск, ООО Анакреон

This article describes how to create a good environment for start-up project: what kind of server do we need for each role, and how these servers should communicate. Architecture restrictions are considered. Technical and architecture suggestions are made.

На начальном этапе разработки стартапа многие разработчики сталкиваются с проблемой создания достаточно гибкой и в будущем масштабируемой архитектуры приложения, оптимизированной для большой нагрузки. Мы хотели бы поделиться опытом в этой области. В качестве примера рассмотрим методы построения ресурса, базирующегося на Ruby On Rails. Тем не менее примеры, которые будут приведены ниже, с легкостью могут быть использованы для LAMP-решений.

Серверы можно разделить на несколько типов по ролям:

  • Application
  • Database
  • Load Balancer
  • Utils
  • Tools

Application — это frontend-сервер. Возможно использование как одного, так и нескольких Application-серверов. В случае использования одного Application-сервера надобности в балансировке нагрузки нет.

Application-сервер выполняет следующие функции:

  • обработка запросов от пользователей (получает запросы от балансировщика нагрузки),
  • взаимодействие с базой данных (Database server),
  • отправка сообщений в очередь сообщений (Utils server),
  • взаимодействие с CDN,
  • взаимодействие с search engines (Util server),
  • инвалидация кэша,
  • работа с кэшем (Database server).

Application-сервер не должен производить следующих действий:

  • преобразование медиа контента (видео, аудио, слайдшоу, и т.д.),
  • преобразование/кропинг картинок (если позволяет архитектура),
  • отправка сообщений электронной почты (если позволяет архитектура),
  • запросы на сторонние ресурсы через RestClient, curl, etc. (если позволяет архитектура).

Database — сервер, на котором находится база данных. В некоторых случаях на Database-сервер также устанавливается memcached, но это зависит от загрузки и конфигурации Database-сервера.

Database-сервер выполняет следующие функции:

  • обработка запросов от Application и Utils серверов,
  • хранение кэша (в случае установки memcached на DB сервер).

Load Balancer — балансировщик нагрузки, необходимый для распределения запросов пользователей между Application-серверами. Простейшим решением является использование nginx в качестве балансировщика нагрузки.

Load Balancer выполняет следующие функции:

  • распределение запросов между Application серверами,
  • обработка HTTPS соединений (SSL сертификат должен быть сконфигурирован на балансировщике),
  • кэширование при использовании Varnish или связки nginx + memcache, Firewall.

Utils — сервер, на котором располагаются сопутствующие сервисы, необходимые для отложенной обработки данных. Вот краткий список:

  • message queue (RabbitMQ, Apache Message Queue, etc),
  • search engines (Solr, Sphinx, etc),
  • обработчики сообщений,
  • сервисы для обработки Video, Audio, SlideShow,
  • отправка сообщений электронной почты,
  • взаимодействие с CDN,
  • инвалидация кэша,
  • взаимодействие со сторонними ресурсами (PDF converters, SendGrid, etc.).

Tools — сервер, необходимый для установки сторонних решений для работы приложения. В частности, приложения, базирующиеся на Java/Tomcat, система мониторинга и ресурсы, в безопасности которых вы сомневаетесь (Spellchecker, etc.). Tools-сервер не может установить соединения с Application, Database, Utils-серверами. Единственный протокол взаимодействия — это HTTP. Это дает гарантию того, что в результате взлома стороннего компонента основная ферма не пострадает. Сервер с этой ролью не обязателен.

Ограничения, которые накладываются на архитектуру приложения

Хранение кэша

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

Хранение сессий пользователя

В данном случае целесообразно хранить сессии в memcached или в Cookies. Отдача сессии на сторону пользователя — менее безопасное и более медленное решение, чем хранение в memcached.

Хранение медиа-данных пользователя

Любой контент, загруженный пользователем на сервер, не может быть просто сохранен в папку /public/upload. Наилучшим способом хранения данных является CDN.

Рекомендации по автоматизированной конфигурации серверов

Существует достаточно большое количество ПО для поддержания конфигурации серверов: Сfengine, Puppet, Chef, Sprinkle.

Для автоматизированного конфигурирования серверов мы используем Sprinkle. Sprinkle — это достаточно простое приложение на RoR, позволяющее разрабатывать сценарии по установке ПО на UNIX"=сервера. Sprinkle поддерживает разделение серверов на роли, фермы и т.д. Так же поддерживаются различные типы инсталяторов, методов проверки текущей конфигурации, источников данных. Sprinkle имеет открытый исходный код, что позволяет вносить изменения в логику работы отдельных компонентов или создавать дополнительные модули.

Варианты инсталляции

Существует несколько вариантов ферм.

  • Single server installation.
  • На одном сервере располагаются Application + Util + Database
  • Light farm
  • 1 сервер: Application
  • 1 сервер: Util + DB
  • Big farm:
  • 1 сервер: Load Ballancer
  • x серверов: Application
  • 1-x серверов: Util
  • 1-x серверов: Database (о репликациях и масштабировании базы данных можем поговорить чуть позже)

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

Рекомендации по архитектуре. Несколько примеров

Интеграция со сторонними ресурсами

Зачастую необходима интеграция со сторонними ресурсами для преобразования видео и аудио, обработки слайд-шоу, отсылки писем и т.д.

Это подразумевает установку соединения по протоколам TCP, HTTP, etc. В данном случае существует несколько рисков:

  • сервер недоступен
  • сервер перегружен

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

Лучшим решением в данном случае является отправка события в очередь сообщений и обработка на стороне util-сервера.

Обработка картинок пользователя

Если ваш ресурс поддерживает возможность загрузки аватаров пользователей, логотипов с последующим ресайзингом изображений, вы можете столкнуться с проблемой большой нагрузки на сервер.

Есть несколько распространенных методов ресайзинга изображений:

  • оригинальное изображение сохраняется в хранилище без обработки. Доступ к изображению со страниц сайта реализован через врапер с элементарной логикой. Если картинка сгенерирована, то необходимо отдать ее через STDOUT или вернуть редирект на статический. Если картинка отсутствует, то сгенерировать и выполнить предыдущий шаг. Проблема этого метода в том, что нагрузка, которую может создать данный алгоритм, не может быть спрогнозирована. При добавлении нового размера изображения достаточно визита поискового бота – и скорее всего сервера будут перегружены.
  • оригинальное изображение преобразуется в нужные форматы и размеры непосредственно после загрузки на сервер. Это дает единовременную нагрузку на Application-сервера. Минусом данного метода является задержка в возврате страницы пользователю, что может вызвать дискомфорт.
  • последний вариант является модифицированной версией
  • предыдущего. Вместо непосредственной обработки изображения на Application-сервере необходимо отправить событие в очередь сообщения и произвести обработку изображения на Util-сервере.

Заключение

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