Индустрия видеоигр — странное место: она одновременно может отставать от остальной технологической отрасли на полдесятка лет в одних областях, и на годы опережать её в других.
В эту отрасль меня привлекла не возможность работы над развлекательными продуктами или создания продуктов, которые мне понравится использовать (не могу назвать себя геймером): я люблю решать задачи, и особенно задачи, которые нелегко решать.
Когда я пришёл в Ubisoft в 2014 году, меня назначили в отдел программирования онлайна на должность руководителя Ops. Это было ужасно, потому что все работали под Windows.
Kubernetes ещё не было на горизонте, да если бы он и был, сам Docker оставался крайне сырым и пока не мог выполнять нативные двоичные файлы Windows.
Вместо него мы использовали собственную реализацию распределённых систем.
▍ Среда
Сильно оптимизированная и чрезвычайно надёжная система обнаружения сервисов, обратные прокси, достаточно умные для того, чтобы принудительно обеспечивать экспоненциальную задержку клиентов без получения нагрузки, супервизор, которым можно было управлять через веб-сокеты, внутреннее шифрование между сервисами с централизованной системой ротации ключей, приложения просмотра логов в памяти, доступ к которым возможен по сети через браузер, и даже сборщики статистики, запускаемые в браузере. Всё это написано вручную на C++, никаких готовых решений, крайне минимальные зависимости (из серьёзных можно упомянуть только OpenSSL), всё работает в Windows и полностью уникально.
Если вы в первую очередь админсис* Unix, то в такой ситуации можете сделать следующее:
*Намеренное искажение термина «сисадмин», обычно употребляемое теми, кто помнит времена, когда сисадмин занимался тем, что сегодня делают «ребята с devops в названии должности». Это было ещё до того, как сисадмины ушли в историю как должность службы технической поддержки.
1) Удвоить усилия на основе имеющихся у меня знаний и попытаться преобразовать задачу в решаемую. Читай: наверно, использовать Wine.
2) Подчиниться окружающей экосистеме и заново научиться тому, как лучше всего делать вещи. Пойти по пути Microsoft с SCCM + GPO.
Я выбрал вариант 3: относиться к Windows как к устройству или чёрному ящику, полагаться на фреймворк исполнения, имевший надёжный Windows-агент, писать весь наш инструментарий на скриптовом языке общего назначения, который бы мог вызывать удалённый фреймворк исполнения. Мы выбрали SaltStack+Python.
Такой подход был удобен тем, что мы наконец-то в точности разобрались, что происходит в нашей среде. Не было ничего неизвестного, отсутствовали «магические» программы или сервисы, но в то же время не было ничего, на что бы можно было положиться: никакой оболочки, никаких инструментов Unix наподобие sed/awk, никакого SSH. Если вам нужно изменять файл, то для этого нужно написать программу. Если вам нужно заставить Windows делать то, что обычно делает GPO, то придётся вручную редактировать записи реестра, а в противном случае вы будете вынуждены создавать странные комбинации из цепочек RDP-сессий через двойной VPN (да здравствуют корпоративные политики!).
Сообразительный читатель может задаться вопросом: «Разве Ubisoft не могла сделать всё это правильно? Это крупный издатель игр и, вероятно, у её игр раньше были Windows-серверы! Правда ведь?»
Вы совершенно правы.
▍ Как поступит Ubisoft?
Опыт Ubisoft в онлайн-играх ограничивался крошечными малонадёжными системами.
Читай: взаимодействующая с серверами NAT с нечастым доступом, единожды использованный матчмейкинг для упрощения клиентских соединений peer-to-peer, таблицы лидеров, крайне редко реальные игровые серверы (но такое было в диковинку).
Онлайн-подсистемам необходимо было поддерживать игру уровня The Division, а это был большой шаг вперёд по сравнению с тем, что компания когда-либо делала раньше. Самое близкое, что у неё было — это только нелюбимая игроками постоянно находящаяся онлайн DRM-система под внутренним названием «Orbit», и Uplay, которую тоже критиковали. Ubisoft создала организацию, оптимизированную под то, чтобы обращаться с разработчиками, как с идиотами, а потому загнала себя в угол. Все процессы были спроектированы на основании идеи того, что оборудование имеет одинаковые параметры, что всё должно быть похожим, и самое главное: что разработчики не понимают, что требуется, поэтому нельзя позволять им задавать требования.
Так что поверьте, нам приходилось буквально драться за то, чтобы в наши машины установили хотя бы ещё один диск.
▍ Согласованность данных как требование
Концепция Games as a Service (фу) накладывает дополнительную нагрузку, которая может быть абсурдно очевидной, но её, похоже, часто недооценивают: мы сами храним профили игроков и следим за ними.
Мы не храним профиль игрока в консоли или PC, игрок никогда не увидит его в его двоичном виде. Вместо этого мы передаём профиль игрока с сервера игры в центральную точку и снова на сервер игры. На самом деле, этот процесс относительно быстр и вызывает лишь незначительные блокировки. Мне дали задачу обеспечить максимальную производительность и надёжность хранилища данных. «Даунтайм предпочтительнее, чем потеря данных».
И понятно, почему: представьте, что вы только что завершили сложную задачу в игре и вас вознаградили чрезвычайно редким призом, получить который повторно крайне маловероятно. Если вы его потеряете, то имеете полное право разгневаться.
Моя задача заключалась в том, чтобы мы не теряли никакие сохраняемые данные.
Поначалу это может показаться довольно простым: многие люди считают, что диски относительно надёжны, однако когда ты заявляешь «Я не теряю данные» и начинаешь настоящее расследование, то быстро обнаруживаешь, что многие популярные базы данных вполне себе теряют данные. Мне первым делом вспоминается MongoDB. Другие примеры: подобия hbase, только гарантирующие постоянное хранение в VFS, то есть они не сбрасывают свои операции записи на диск, а просто предполагают, что теперь это ответственность операционной системы. Это не очень успокаивает, если вспомнить, что VFS кэшируется в памяти…
Учитывая наш предыдущий опыт построения всего самостоятельно, я считал, что в данном случае определённо не стоит этого делать. Взросление баз данных занимает примерно десять лет, а мои навыки администрирования Linux применимы, только когда дело касается самых популярных систем баз данных, потому что они работают в Linux!
В то время, когда я пришёл в компанию, в качестве основного хранилища игры использовалась MySQL. Я потратил целых три месяца на анализ MySQL, на тестирование производительности на «нереалистичном» оборудовании, чтобы найти внутренние узкие места, вызывающие блокировку, выясняя, где система будет терять данные и при каких условиях. Основные выводы были такими, что можно убедить MySQL не терять данные, однако внутренние блокировки ухудшали её производительность во многоядерных системах. PostgresSQL проявляла себя гораздо лучше и имела дополнительное преимущество: могла чётко разделять на отдельные RAID-устройства упреждающее протоколирование (которое в основном записывается последовательно) и данные. Эту возможность MySQL поддерживает плохо, её пришлось бы реализовывать, используя символические ссылки при каждом создании таблицы.
PostgreSQL надёжна в этом отношении, в ней можно гораздо больше, чем в большинстве движков баз данных, повысить вероятность сохранения данных на диск, а также отключить режим отложенной перезаписи в RAID-контроллере. В итоге вы получите почти полную гарантию отсутствия потерь данных, если не считать этого единственного случая.
Любые постоянно хранящиеся данные, которые вы считаете важными, разумеется, нужно бэкапить, поэтому я начал исследовать готовые решения для резервного копирования баз данных Postgres.
▍ Бэкапы: на сцене появляется PgBackRest
Рассмотрев несколько вариантов (в том числе несколько ручных способов), я остановился на инструменте под названием PgBackRest, имеющем множество интересных возможностей. Однако самое важное было то, что он обеспечивал согласованность резервных копий!
Я протестировал его и заказал хранилище, которое мне понадобилось бы для создания скользящего окна в 90 дней для бэкапов (при этом старые бэкапы хранились удалённо).
Мою просьбу о предоставлении оборудования отклонили.
Когда я спросил, почему, мне ответили, что у Ubisoft есть стандартное решение для резервного копирования, которое реплицируется по всему миру и отправляет данные для хранения офлайн в каком-то парижском банковском хранилище. Мне сказали, что так делается потому, что однажды мы потеряли какой-то исходный код и не могли из-за этого собирать часть игр. Разумеется, «исходный код» даже не был доступен в этой сети, потому что имелась чёткая сегментация, но я принял ответ во внимание.
«Ну ладно, тем лучше, меньше заморочек с заказами!», — сказал я.
Я протестировал своё решение на паре жёстких дисков SAS на 400 ГиБ, оказалось, всё работает неплохо, поэтому я продолжил.
Когда я наконец пообщался с нужными людьми, чтобы мне дали доступ к этой системе (по сути, мне дали IP и сказали, что это NFS), она показалась очень бойкой, данные в неё отправлялись очень быстро: мне даже выдали оптоволоконные линии, напрямую подключённые к серверам базы данных, и при моём тестировании я смог полностью заполнить накопители, использовавшиеся для локальных бэкапов.
Я был доволен.
Но на следующий день ситуация изменилась.
Дело в том, что PgBackRest «умный»: он считывает данные, которые вы записали ранее, чтобы создавать «инкрементальные» бэкапы (это выглядит как полный бэкап, поэтому вам нужно просто восстановить его и продолжать воспроизводить журнализацию изменений с этой точки, что ускоряет восстановление). Это значит, что можно выполнять большой бэкап раз в день, что блокирует базу данных и создаёт небольшой бэклог, а также создавать почасовые инкрементальные бэкапы, занимающие меньше места на диске и гораздо менее затратные. Чтобы генерировать и дополнительно верифицировать эти инкрементальные бэкапы, PgBackRest обязан считывать данные.
▍ Система хранения
Нашей системе хранения резервных копий не нравилось, когда из неё считывают данные, производительность была ужасной, сравнить это можно только с AWS Glacier, однако это была NFS, и все, кто знает, что делает NFS, когда удалённый узел медленный или не отвечает, скажут вам: это может убить ваш сервер. Linux, по сути, продолжает помещать операции ввода-вывода в очередь ожидающих задач, и рано или поздно всё ломается, так как ядро тратит всё своё время CPU на попытки понять, что ему делать дальше, поскольку очередь ожидающего ввода-вывода заполнена элементами, которые, по сути, просто ждут, а список продолжает разрастаться.
Поговорив с администраторами хранилища, архитекторами, большим руководством, моим руководством, продюсерами и постепенно повышая уровень напряжения, я понял: мы не будем покупать специализированное оборудование для хранения бэкапов, даже если создание надёжных бэкапов при помощи имеющейся системы невозможно.
Я изучил альтернативы: скидывал данные напрямую в систему, однако считать их обратно было невозможно, время восстановления измерялось бы неделями, а не минутами или часами, как хотелось мне.
Позже я выяснил, что эта система называлась «DataDomain», и прочитав техническую спецификацию, понял, что она работает, как и задумывалось. «Регидрация» — это затратная для устройства операция, а оно предназначено для более долговременного архивного хранения.
Когда я начал упорно задавать вопросы, почему они подвергают проект такому риску, потратив сотни миллионов на новый игровой движок, на новую интеллектуальную собственность (и её маркетинг) и на совершенно новую подсистему онлайна…
… Ответ был простым: «Мы потратили миллион долларов Ива Гиймо, и будет нехорошо, если ты их не используешь».
▍ Утерянные данные
Забавно, что как будто существует какое-то божество, решившее меня покарать: вскоре после этого неконтролируемый и устаревший узел игрового сервера восстал из мёртвых и начал повреждать профили игроков.
Исключительно благодаря бэкапам, которые я создавал во время своих тестов, мы смогли восстановить эти повреждённые профили (однако данные в них были старее, чем бы мне хотелось).
Вскоре после этого нам всё-таки выдали оборудование.
▍ Какой урок я могу из этого извлечь?
Когда разумно что-то купить, правильно будет задаться вопросом, какие функции приоритетны для покупаемого сервиса или продукта. Наша система EMC DataDomain была в основном оптимизирована под потребление огромных объёмов трафика, однако если нам требовались инкрементальные бэкапы, то возможно, следовало поискать что-то менее интеллектуальное.
Ubisoft создала мощную структуру, подстроенную под единый тип нагрузки, и не способна была увидеть другого способа работы. Я наблюдаю, как это эхом отзывается в наших поставщиках облачных услуг и в том, как мы подгоняем все рабочие процессы под имеющиеся (а иногда и отсутствующие) ограничения.
Только то, что ты потратил миллион долларов на продукт, потому что он подходит для общего случая, не значит, что он подойдёт для любого случая.
И это заставляет вспомнить один недавно прочитанный комментарий, вдохновивший меня на эту тираду: когда кто-то говорит, что Amazon инвестировала много денег в безопасность, я вспоминаю о том, что Ubisoft потратила миллион долларов на решение для бэкапа данных, не подходящее для той самой игры, для которой предназначалось.
Мне любопытно, что же поставщики услуг делают с этими деньгами на самом деле, потому что когда дело касается облачных услуг, о поддержке даже речи не идёт**. Я думаю, что одной из основных причин проблемы несоответствия инструмента задаче была непрозрачность — мне потребовались месяцы, чтобы хотя бы узнать, что конечная IP-точка моей NFS называлась «DataDomain», и о том, что изменить её практически невозможно. Сначала решение привело бы к катастрофическому провалу. Это немного напоминает мне тот случай, когда Amazon отказалась сообщать мне, почему недоступны мои инстансы, потому что компания скрывала масштабные перебои в работе. От этих инцидентов ощущается схожая аура.
** Разумеется, я в курсе, что можно заплатить за определённый уровень поддержки, однако тогда затраты на облако поднимаются всего с десятикратных до вызывающих слёзы 12-13-кратных.
Ещё я думаю о дилемме «создавать своё или покупать чужое», потому что созданные нами системы работали всегда***. Единственная проблема заключалась в неконтролируемых поставщиках услуг и в той власти, которую они имели над нами.
*** За очень заметным исключением пришедшего к жизни неконтролируемого инстанса, убившего профили всех игроков.
Других выводов из этой ситуации я сделать не могу.
Telegram-канал с розыгрышами призов, новостями IT и постами о ретроиграх ????️
Комментарии (19)
FlashHaos
03.04.2023 13:28+10История странная со всех сторон. Автор статьи мог выбрать конкретную СУБД и конкретную технологию ее бекапа, но не мог выбрать бекапную хранилку и даже не мог узнать ее модель? Предположим, такая дурацкая система полномочий. Но если бекапная хранилка обеспечивает низкую скорость чтения, то надо снижать требования к RTO и отказываться от инкрементальных бекапов. Бекапьте фул и логи, какие вопросы? Восстанавливаться будет долго, ну так хранилка и так медленная, значит руководство считает это нормальным.
Или меняйте архитектуру базы и делайте защиту через отстающий стендбай, раз есть возможность самому крутить что угодно.
Data Domain - чудесная железка, но она действительно не для того предназначена, чтобы с нее читать и считать разницу на стороне клиента. Она на своей стороне это прекрасно делает сама.
ACO-Andrew
03.04.2023 13:28Ксати да! Data Domain просто монстр! Только нужно понимать для чего оно и правильно использовать. Очень хороший de-dup и скорость вливания.
FlashHaos
03.04.2023 13:28А еще даже без специального софта можно использовать не nfs, а bostfs. Будет дедуп на стороне источника.
akakoychenko
03.04.2023 13:28+7Кстати, так и не понял, почему просто не хранить один последний бекап локально ради возможности завтра сделать инкремент
swshoo
03.04.2023 13:28+1За пару десятков лет работы я понял одно - документация по бекапам и по процедуре восстановления (что, откуда, куда, как и главное в какой последовательности), это редчайший документ во многих компаниях. Делать такую документацию, это нудятина ещё та, да и память у многих админов просто супердолговременная -). До первого жёсткого факапа.
FlashHaos
03.04.2023 13:28+3Ладно документация, а вот регулярные тестовые восстановления с проверками логической целостности не делает примерно никто. Дорого, долго.
Единственный рабочий способ заставить людей это делать - это внедрить систему, при которой тестовые или отчетные базы регулярно пересоздаются именно из бекапа. Тогда прикладные проверки появятся естественным образом.
sasha_semen
03.04.2023 13:28ну после очередного факапа обычно регламенты и корректируют. до следующего раза.
vlad4kr7
03.04.2023 13:28"The issue was caused by a malfunctioning server that couldn't synchronize character data correctly and corrupted them instead,"
Игровой сервер не может прочитать данные из (общей) базы корректно, но может их удалить?
База(ы) здесь как-бы совсем не причем. Бакап позволил восстановить потертое.
Имхо, основная проблема в архитектуре апи бакенда, или ее отсутствия.
Да простейшее решение типа soft-delete не привело-бы к таким громким последствиям.
mayorovp
03.04.2023 13:28Тут не soft delete нужен, тут нужно хранение версий.
Судя по симптомам, там было нетипизированное хранилище документов, в котором хранились либо персонажи, либо профили целиком. Игровой сервер тут и был бекендом, и работал с этим хранилищем он как с файлами — читал целиком данные и сохранял обновлённые.
vlad4kr7
03.04.2023 13:28Версионность, это было-бы хорошо, но в статье рассматривается Postgres vs MySQL а не хранилище документов, а в реляционой модели делать версионность немного сложнее.
"Хотя-бы сделали решение типа soft-delete."
Да, кстати игровой сервер как бекенд, тоже совсем не правильно. По нормальному надо делать отдельно, фасад апи с игровой логикои, и бэк с версионостью апи, кешем, распределением нагрузки итп.
mayorovp
03.04.2023 13:28Нет смысла. Основная нагрузка всё равно тут на самом игровом сервере и на стороне СУБД, бэкенд тут лишь почтальоном поработать может, он никак не распределит нагрузку. Версионировать тут тоже нечего, единственный клиент — это игровой сервер, и ему нужна конкретная версия api. Кешировать в таких сценариях тоже особо нечего.
И, главное, проблема "устаревшая программа запорола документ при редактировании" остаётся как компоненты не выноси, только вариантов "запарывания" от увеличения числа звеньев прибавляется.
статье рассматривается Postgres vs MySQL а не хранилище документов, а в реляционой модели делать версионность немного сложнее.
Без проблем, просто нужна отдельная таблица для истории изменений.
Я всё ещё уверен, что игровой сервер использовал Postgres как хранилище документов.
Ivan22
Это все последствия выбора "безобразно но однообразно" vs "большой зоопарк".
Первое приводит к проблемам как у автора. Второе к другим, но не менее сложным проблемам. Когда из-за зоопарка только одних видов субд в компании будет десятка два и бэкапить это будут каждый кто во что горазд, и 100% какие-то системы будут вообще без бэкапов жить.
mayorovp
Нет, это последствия вот этого:
Ivan22
ну если позволять им задавать все требования и выбирать инструменты, скорость разрастания зоопарка станет сверхсветовой и через год никто в компании даже представлять не будет что и где используется
mayorovp
Если вообще каждому дать выбрать инструмент — то да, но есть же и более адекватные способы.
Ivan22
адекватно, это продумывать это на уровне архитектуры, архитекторам
mayorovp
Архитектор — это тоже разработчик, а не менеджер.
Ivan22
архитектор - это архитектор, не надо все упрощать и сводить к черному и белому. А то из-за таких упрощений и появляются потом одна система бэкапа на всю компанию
mayorovp
Но до менеджера упрощать архитектора тоже не следует.