У нас были балансировщики нагрузки, несколько серверов приложений, 5 баз данных, 24 ядра, 32 гигабайта оперативки, nginx, php, redis, memcached и еще куча других сетевых технологий всех форм и расцветок. Не то чтобы это был необходимый минимум для бэкенда, но когда начал делать отличные онлайн-игры, становится трудно остановиться. Мы знали, что рано или поздно перейдем и на облако.



Это теперь мы делаем бэкенд для игр на основе микросервисов — раньше все было совсем по-другому. Был фиксированный аппаратный сетап, постоянные риски, что вот, еще чуть-чуть, и все сломается из-за наплыва игроков. Начинался 2013 год. Тогда мы и выпустили игру 2020: My Country.

Прошло достаточно много времени, проект рос и развивался, нагрузки постепенно увеличивались, и в какой-то момент мы решили перенести бэкенд в облако Azure — we do what we must because we can. Облака дают хороший запас по вычислительной мощности, поэтому начать мы решили с меньшего количества гигабайт и гигагерц, чем было в нашем дата-центре. Основой нового бэкенда стали менее мощные машины с более новыми процессорами. Больше всего мы переживали по поводу нагрузки на новые БД-сервера и даже готовились к разбиению баз, но переживали, как оказалось, зря.


Рис. 1 — Портал Microsoft Azure

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

Но перед этим — внимание, уважаемые знатоки, — вопрос задает начинающий системный инженер из Иркутска: «Хорошо, это один проект. А что, если их будет много, а хостов будут десятки и сотни?» Отвечаем: в таких случаях на помощь приходит terraform от HashiCorp, который у нас уже успешно работает в AWS и также поддерживает Azure.

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

provider "azurerm" {
  subscription_id = "xxxx-xxxx"
  client_id       = "xxxx-xxxx"    
  client_secret   = "xxxx-xxxx"
  tenant_id       = "xxxx-xxxx"
}

Команда terraform plan показывает превью будущих действий — планируется добавление ресурсов, которые уже созданы вручную и существуют.


Рис. 2 — Результат команды terraform plan.

Это не совсем то, что мы бы хотели увидеть, поэтому вспоминаем свой опыт с AWS — в terraform есть команда import, для, очевидно, импорта существующей инфраструктуры.

При импорте нужно ввести идентификаторы существующих ресурсов из Azure. По умолчанию они достаточно длинные и сложные, поэтому (наверное) на портале есть кнопка Copy to clipboard, которая значительно упрощает весь процесс.


Рис. 3 — Результат импорта

Еще один момент: terraform пока не умеет автоматически генерировать конфигурацию импортируемых ресурсов и предлагает нам сделать это вручную. По умолчанию, при отсутствии конфигурации у ресурса, он помечается на удаление при следующем запуске.

Добавляем конфигурацию и смотрим на terraform plan.


Рис. 4 — terraform plan после импорта ресурсов из Azure

Тильда и желтый цвет означают изменение ресурса — terraform хочет пересоздать subnet mc2020 без привязки к security group. Вероятно, это произошло из-та того, что мы не указали security group для подсети. Чтобы узнать причину наверняка, посмотрим на то, как импортирована существующая виртуальная сеть. Это можно сделать с помощью файла terraform.tfstate (нечитабельная JSON-простыня) или командой state. Вполне очевидно, какой способ выбрали мы.


Рис.5 — Снова terraform state

Действительно, привязка к security_group есть, но в изначальной конфигурации мы про нее забыли. Для исправления проходим все шаги заново — находим группу на портале Azure, копируем id, снова импортируем и прописываем в конфигурацию.

После все выглядит так:
resource "azurerm_resource_group" "mc2020" {
    name     = "mc2020"
    location = "${var.location}"
}
resource "azurerm_virtual_network" "mc2020-vnet" {
  name                = "mc2020-vnet"
  address_space       = ["XX.XX.XX.XX/24"]
  location            = "${var.location}"
  resource_group_name = "${azurerm_resource_group.mc2020.name}"
}
resource "azurerm_subnet" "mc2020-snet" {
    name = "mc2020"
    resource_group_name = "${azurerm_resource_group.mc2020.name}"
    virtual_network_name = "${azurerm_virtual_network.mc2020-vnet.name}"
    address_prefix = "XX.XX.XX.XX/24"
    network_security_group_id = "${azurerm_network_security_group.mc2020-nsg.id}"
}
resource "azurerm_network_security_group" "mc2020-nsg" {
    name = "mc2020-nsg"
    location = "${var.location}"
    resource_group_name = "${azurerm_resource_group.mc2020.name}"
    security_rule {
      name = "default-allow-ssh"
      priority = 1000
      direction = "Inbound"
      access = "Allow"
      protocol = "TCP"
      source_port_range = "*"
      destination_port_range = "22"
      source_address_prefix = "XX.XX.XX.XX/24"
      destination_address_prefix = "*"
    }
    security_rule {
      name = "trusted_net"
      priority = 1050
      direction = "Inbound"
      access = "Allow"
      protocol = "*"
      source_port_range = "*"
      destination_port_range = "0-65535"
      source_address_prefix = "XX.XX.XX.XX/32"
      destination_address_prefix = "*"
    }
    security_rule {
      name = "http"
      priority = 1060
      direction = "Inbound"
      access = "Allow"
      protocol = "TCP"
      source_port_range = "*"
      destination_port_range = "80"
      source_address_prefix = "*"
      destination_address_prefix = "*"
    }
    security_rule {
      name = "https"
      priority = 1061
      direction = "Inbound"
      access = "Allow"
      protocol = "TCP"
      source_port_range = "*"
      destination_port_range = "443"
      source_address_prefix = "*"
      destination_address_prefix = "*"
    }
}


Замечание: при импорте ресурсов нужно внимательно смотреть их атрибуты — они case-sensitive, и важно не ошибаться при их наборе. Все это издержки того, что мы импортируем уже существующую инфраструктуру и кодируем ее.

После всех правок команда terraform plan выдает нам то, что мы ожидали увидеть изначально.

No changes. Infrastructure is up-to-date. This means that Terraform
could not detect any differences between your configuration and
the real physical resources that exist. As a result, Terraform
doesn't need to do anything.

(Здесь был скучный процесс импорта\записи инфраструктуры всего проекта, который мы пропустили по очевидным причинам)

Мы рекомендуем создавать инфраструктуру сразу в виде кода. Магия делается следующим образом:

  1. Добавляем ресурс;
  2. Смотрим terraform plan;
  3. Выполняем terraform apply;
  4. Смотрим на чудеса автоматической переконфигурации ресурсов;

Примерно так. Спасибо за вопрос, внимательный читатель! Вернемся к паре обещанных ранее вещей.

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


Рис.6 — Итоговая схема инфрастуктуры в Azure

На момент переноса в нескольких базах было около 500 гигабайт данных. Данные с двух самых объемных серверов мы перенесли между дата-центрами через xtrabackup на скорости в 1 Гбит/c, а для дампа необходимых баз с третьего сервера мы использовали myloader, который работает быстрее стандартного mysqldump. После этого мы допили оставшийся кофе и провели окончательные настройки всего-что-можно-было-настроить.

Во-вторых, мы начали потихоньку переводить трафик на новые балансировщики нагрузки, следя за ошибками и нагрузкой. Для обработки всего трафика мы добавили еще один app-сервер и в итоге получили ~30% нагрузки app- и 8-15% db-серверов.

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

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

Для полного переноса осталось:

  • Пропатчить клиенты, чтобы прописать новые хосты (“az-”);
  • Перенести файлы статики, которые заливаются скриптом на один из старых серверов и служат Origin для Akamai CDN;
  • Через некоторое время отключить старые сервера.

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

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

Ждем ваших вопросов в комментариях. Спасибо за внимание!
Поделиться с друзьями
-->

Комментарии (9)


  1. deril
    30.01.2017 15:02
    +2

    Зачем скрины консоли? Может есть другой способ вставить текст?
    Почему выбрали именно Azure а не amazon например? Что подтолкнуло к такому выбору?


    1. evil_me
      30.01.2017 15:10
      +3

      Спасибо за вопросы!
      1) Мы честно пытались минимизировать количество скринов. В дальнейшем будем просить у хранителей консоли логи сразу текстом)
      2) Мы работаем и с Amazon, вот здесь рассказ о том, как мы выбирали платформу для другой нашей игры.


  1. adminguru
    30.01.2017 15:15
    +2

    Azure хорош по возможностям, цены только хотелось бы поменьше. Если не секрет, намного дороже новое решение по сравнение с датацентром раньше.


    1. evil_me
      30.01.2017 15:28
      +2

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


  1. antirek
    30.01.2017 17:44
    +1

    Хорошое начало, как будто сам старина Рауль Дьюк рассказывает: )


    1. evil_me
      30.01.2017 18:18

      Ходят слухи, что продолжение статьи тоже ничего так :)
      Тем не менее, спасибо на добром слове!


  1. fuCtor
    30.01.2017 19:09
    +1

    Terraform штука отличная, поверх его state файлов сделал обвязку для автоматизации типовых задач с сервисами (AWS).


    Если используете docker и в принципе архитектура не критична к пропаданиям узлов, посмотрите Spotinst, для AWS при массовых деплоях позволяет хорошо экономить.


    1. evil_me
      30.01.2017 19:22

      Спасибо! Передам ваш совет хранителям консоли :)


  1. arzonus
    31.01.2017 12:19

    Перенести файлы статики, которые заливаются скриптом на один из старых серверов и служат Origin для Akamai CDN;


    Можно использовать не один старый сервер, а на прямую BLOB. CDN умеет получать информацию напрямую.