Terraform, Azure, Іркутськ і ще 1207 слів про перенесення гри в хмару

У нас були балансировщики навантаження, кілька серверів додатків, 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 з коробки дозволяє відчути переваги автоматичного масштабування, дає можливість миттєво додати ресурси у разі підвищення навантаження і все інше добро і позитив, що приносять у життя розробників хмарні сервіси.

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

Чекаємо ваших питань у коментарях. Спасибі за увагу!
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.