У нас были балансировщики нагрузки, несколько серверов приложений, 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.
(Здесь был скучный процесс импортазаписи инфраструктуры всего проекта, который мы пропустили по очевидным причинам)
Мы рекомендуем создавать инфраструктуру сразу в виде кода. Магия делается следующим образом:
- Добавляем ресурс;
- Смотрим terraform plan;
- Выполняем terraform apply;
- Смотрим на чудеса автоматической переконфигурации ресурсов;
Примерно так. Спасибо за вопрос, внимательный читатель! Вернемся к паре обещанных ранее вещей.
Во-первых, нам нужно было перенести большое количество данных с боевого железа. Чтобы обновить 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 из коробки позволяет почувствовать преимущества автоматического масштабирования, дает возможность мгновенно добавить ресурсы в случае повышения нагрузки и все остальное добро и позитив, которое приносят в жизнь разработчиков облачные сервисы.
При правильном подходе к организации процесса и выборе эффективных инструментов самая ответственная и сложная часть — переезд и запуск существующего бэкенда в облачной инфраструктуре — становится простой, предсказуемой и понятной, чем мы с удовольствием и воспользовались. Задокументировав процесс простого переезда на этой технологии, мы продолжим перенос в облако и других наших проектов.
Ждем ваших вопросов в комментариях. Спасибо за внимание!
Автор: Game Insight