Статьи

Как IronSource масштабирует Node.js с Docker для поддержки миллионов ежедневных пользователей

Автор  Шимон Тольц

ironSource  — ведущая в мире платформа для поиска, распространения, доставки и монетизации программного обеспечения. Решение состоит из четырех ядер — installCore, mobileCore, displayCore и mediaCore — которые связывают разработчиков программного обеспечения и пользователей на разных платформах и устройствах.

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

В этой статье я опишу, как мы в  IronSource  масштабируем Node.js с помощью Docker — автоматически собираем , разворачиваем и запускаем приложение Node.js в контейнере Docker для производства.

Стек приложений IronSource

Мы  широко используем  облако AWS и действительно наслаждаемся им. Наш облачный набор в значительной степени «по книге» (наш консультант AWS весьма доволен):  Elastic Load Balancer  (ELB) получает весь трафик, отправляя его на кучу стандартных машин Ubuntu, и, естественно, мы используем  auto-scaling-group  масштабировать размер нашего кластера автоматически.

Мы используем архитектуру микросервисов, в которой множество отдельных приложений взаимодействуют друг с другом. Мы стараемся ограничить каждый сервис только одним делом. Наш основной язык разработки — Node.js, работающий на Linux. Сервисы взаимодействуют друг с другом через очередь (RabbitMQ). поток (Kinessis) или вызовы HTTP REST API.

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

Так почему Докер?

В настоящее время мы используем предварительно «запеченный» образ (AMI) с предварительно установленными требованиями для запуска приложения. Мы используем скрипт начальной загрузки, который загружает последний код из нашего частного   репозитория npm (Node.js Package Manager) и запускает его. Мы ж
намеревался перейти к полностью автоматизированному циклу инициализации, который позволил бы нам динамически настраивать изображение для приложения. Естественно, на ум пришли два кандидата — шеф-повар и докер. И Chef, и Docker позволяют запускать инфраструктуру в виде кода, но Chef тратит время на подготовку каждого нового экземпляра при загрузке. Это происходит довольно часто, потому что мы динамически масштабируем количество работающих серверов в соответствии с изменяющимся спросом. В процессе сборки Docker стоит один раз только один раз. Нам нравится идея контейнеров, которые работают изолированно через ядро ​​Linux (скоро Windows?). Итак, с последним обманом и массовым внедрением этой технологии, мы решили попробовать Docker.

Наш контейнер

Контейнер Docker запускает приложение Node.js за обратным прокси-сервером Nginx. Мы используем систему управления процессами с именем Supervisor, которая запускает наше приложение в контейнере и действует как сторожевой таймер — в случае сбоя приложения. Каждый контейнер Docker получает точку входа, процесс, запускаемый при запуске контейнера. Supervisor — это точка входа в наш контейнер. Приложения Nginx и Node.js запускаются через Supervisor.

Мы используем тип экземпляра c3.large для большинства наших приложений. В связи с тем, что этот тип экземпляра имеет два ядра процессора. Мы запускаем два процесса Node.js, прослушивая порты 8000 и 8001. Nginx прослушивает порт 80 и действует как обратный прокси-сервер для перенаправления трафика на экземпляры приложения Node.js на портах 8000-8001. Мы используем Ubuntu 14.04 как для хоста, так и для контейнера.

The Magic: Конфигурация начальной загрузки Amazon AMI

Процесс начальной загрузки (пользовательские данные) довольно прост. Мы загружаем стандартный образ Ubuntu (14.04). Затем мы выполняем обновление дистрибутива / безопасности, устанавливаем клиент Docker, входим в репозиторий Docker и запускаем наш контейнер, открывающий порт 80 — это так просто!

 #AMI - c3.large - ubuntu 14.04 - user-data:
 #!/bin/bash
 sudo apt-get update
 sudo apt-get -y dist-upgrade
 sudo apt-get -y install docker.io
 sudo docker login -u <user> -p <password> -e <email>
 sudo docker pull user/container
 sudo docker run -p 80:80 -d user/container

GitHub для технической спецификации и настроек —  https://github.com/ironSource/docker-config

Финальные заметки

Контейнеры Linux и Docker позволяют нам полностью использовать нашу архитектуру микро-сервисов. Нам больше не нужно беспокоиться о разных версиях в разработке и производстве. Это позволяет нам строить для нагрузки и масштабировать с первого дня. Docker по-настоящему перегружен нашими циклами разработки.

Будущие разработки

Некоторое дальнейшее исследование, которое я планирую, заключается в использовании облегченной ОС Linux для контейнера и хоста (CoreOS?). ИТ-мониторинг контейнера для меня все еще неясен. Я рассматриваю возможность установки ИТ-агента NewRelic на каждый контейнер. Я также планирую протестировать новую службу управления контейнерами ( ECS ) Amazon для контроля загрузки парка. Я также хотел бы проверить эту настройку на платформе Microsoft Azure.