Здравствуйте, меня зовут Александр Зеленин, и я на дуде игрец веб-разработчик. Полтора года назад я рассказывал о разработке онлайн игры. Так вот, она немного разрослась… Суммарный объём исходного кода превысил «Войну и мир» вдвое. Однако в данной статье я хочу рассказать не о коде, а об организации инфраструктуры проекта. Оглавление Оглавление не отражает хронологию, хоть где-то она и будет совпадать. Это не how-to, не мануал и подобное. Это обзорная статья, призванная дать представление о том, что же кроется за небольшим проектом. Практически каждый из пунктов имеет под собой довольно обширную историю (я намеренно сильно сокращал всё) и тянет на отдельную полноценную статью. В случае, если данная статья окажется востребованной — постепенно напишу цикл, рассказывающий, как выбирать и настраивать те или иные инструменты. Читать можно в произвольном порядке. Важными факторами конечной инфраструктуры были: надежность и стоимость поддержания.
Обеспечение всего этого обходится нам менее чем в 15 тысяч рублей в месяц, большую часть из которых съедает VPC. А если пожертвовать стабильностью самого продукта и использовать более дешевый дата-центр, то можно было бы уложиться в 3 тысячи. Данные цифры учитывают амортизацию и замену неисправных элементов. После очередного падения проекта в 2 часа ночи и восстановления работоспособности до утра стало ясно, что продолжаться так больше не может. И это рассказ о правой части схемы.  Хотелось сделать так, чтобы независимо от того что произойдёт, можно было спокойно спать и не волноваться, что проект вдруг станет недоступен по любой из причин, а потом уже в спокойной обстановке неспешно всё чинить. Попутно хотелось добавить возможность свободного горизонтального масштабирования. Классический рецепт — это запускать несколько экземпляров приложения на различных серверах и на уровне выше направлять пользователя на работающее. Точкой синхронизации является база данных, так что она должна обладать не меньшей стабильностью, чем само приложение. 1) База данных: MongoDB
Запускается минимум на двух различных физических серверах. Одна является основной, остальные — вторичными. Т.к. базы и приложения запускаются на различных серверах и имеется шанс произвольной недоступности, необходимо при восстановлении связи достичь полной корректной синхронизации данных. Для этого у MongoDB есть специальный механизм — для избрания сервера как основного необходима связь как минимум 50% + 1 сервера. Также мы хотим, чтобы в данном голосовании участвовали ещё и сервера с приложениями — вследствие того, что изначально планируется всего 2 сервера под базы (50% + 1 от 2 это те же самые два, т.е. недостаточно для голосования). Для этого у MongoDB есть специальный способ запуска экземпляра БД без самих данных, а только в режиме голосования (Arbiter). На каждый сервер с приложением дополнительно ставится такой Arbiter.
Дополнительный Arbiter запускается на сервере резервного копирования, расположенный вне дата-центра. Итого имеем 2 сервера БД онлайн, и 5 арбитров (сервер БД тоже им является). Теперь какой бы экземпляр БД не отвалился — другой становится основным, а отвалившийся переходит в read-only до восстановления связи. После восстановления связи и полной синхронизации он опять становится основным. Если по какой-то причине отваливаются второй сервер и сервер резервного копирования — все базы переходят в режим read-only. Также необходим мониторинг (слежение за работоспособностью кластера): что он доступен, что данные на месте, что резервная копия в порядке, и так далее. 2) Приложение, Nodejs
Аналогично предыдущему — минимум два различных физических сервера. Все приложения равноправны, т.к. точкой синхронизации являются данные. Этого удается добиться, т.к. приложения сами по себе не хранят никакие состояния, так что даже если один пользователь присоединится к разным экземплярам, это ни на что не повлияет.
На каждом сервере находятся минимум 2 экземпляра приложения, которые мониторятся и балансируются с помощью pm2 & nginx. Сверху этого — мониторинг работоспособности. Перед выбором необходимо определить, сколько же ресурсов потребляет наше приложение. Т.к. я использую Meteor, то выбор тестирования нагрузки пал на MeteorDown. Проверять буду на домашнем сервере, выделив 100% ресурсов под приложение. Сервер: Intel® Xeon® CPU E5-1620 v2 @ 3.70GHz (6 ядер), 32гб ECC DRR3 памяти. Тестировать он будет сам себя, т.е. сеть не учитывается. Да, всё это даёт искажения, но приемлемые. Пишутся сценарии поведения пользователей, генерируются наборы данных. Запуск! Нагрузка постепенно растёт, но при этом время ответа на методы держится стабильным. Тысяча онлайн, две, три, пять, шесть. Постепенно начинает расти время ответа. Семь — время ответа превышает полсекунды. Восемь — время ответа становится неприемлемо большим (10+ секунд). Упёрлись в CPU. Шесть тысяч — это было в десять раз выше, чем пиковый онлайн. Значит, ориентируемся на в 2-4 раза меньшие параметры, но закладываем масштабирование. 1) Окончание закрытого бета-теста
Во время бета-теста регистрация была ограничена, соответственно, и онлайн тоже. Архитектура, описанная выше, ещё даже не была в проекте, т.к. до этого момента всё было достаточно стабильно. В это время всё приложение работало на одной KVM VPS'ке (4 ядра 2.2GHz, 4гб памяти). К старту беты было решено переехать (у них же) на «Отказоустойчивый сервер» со стоимостью в два с половиной раза выше за аналогичные характеристики, но вроде как кучей гарантий, что оно всё суперстабильно и круто. С запуском открытого бета-теста мы начали принимать платежи, так что обязательва наши возросли и простоев нельзя было допускать. 2) ОБТ
Запуск был вполне успешен, и вроде как даже всё работало неплохо. Как вдруг спустя пару недель, днём мне звонит друг и сообщает, что сайт недоступен. Окей, бегу домой. Вижу, что VPS'ка лежит от слова совсем. Пытаюсь перезапустить — безуспешно. Пишу в техподдержку — мне отвечают: крашнулся диск, восстановление через пару часов. ШТА? В сраном описании «отказоустойчивого сервера» меня уверяли, что у них сервер и СХД располагаются отдельно, там всё супер дублируется и что будет 100% доступность. Уточняю данные моменты и мне говорят, что компенсируют (та-да-да) 30 рублей! За 6 часов простоя. Отлично. Окей, предположим, мне реально не повезло и произошло какое-то экстраординарное событие. Уточняю частоту подобных событий и шансы возникновения в будущем — заверяют, что всё будет отлично. Спустя два дня — падение в 4 часа ночи. Крашнулся диск, но в этот раз ещё и с потерей данных :-) 3) Первый переезд
Спасибо бекапам, потеря составила сутки (на тот момент был бекап раз в сутки), а за счёт истории транзакций у партнёра восстановить платежи не составило труда. Экстренно ищу альтернативу. Нахожу, проплачиваю и переезжаю. Спустя недели три — ситуация практически аналогичная. М-да. Очень нетривиальная ситуация была для меня. 4) Tier 3, DataLine
Изучаю тему глубже: как же взрослые дяди решают такие вопросы? Чувствую, и нам пришло время повзрослеть. Прорабатываю схему выше, ищу максимально надёжных хостеров и прихожу к тому, что выбор в РФ не такой большой. С маленькими проектами вообще почти никто из крупных игроков работать не заинтересован, кроме DataLine с их упрощённым вариантом CloudLite. Договариваюсь с поддержкой о тестовом периоде и разворачиваю всё как задумал. Переносим домен.  Фух, вроде работает. Но как-то медленно. Запросы возвращаются около 300мс. Но при этом не было никаких скачков нагрузки, т.е. это было стабильные 300мс. Решено было оставить так и понаблюдать несколько дней. В общем-то стабильность радовала, но вот заторможенность раздражала. Начал разбираться в чём дело и сразу стало видно, что узким местом является диск — у нас очень тщательно записывается активность пользователя, и очередь записи была всегда. Оказалось, что в рамках CloudLite предоставляется только VSAN диски и переезд на SSD для устранения проблемы не возможен. Альтернативой было запустить inmemory версию mongo с репликацией уже на диск, но тут было несколько но: mongo предоставляют полноценную версию in-memory только для энтерпрайз за (па-ба-ба) 15 тысяч долларов в год за один сервер. В моем случае это было совсем неоправданно, особенно в сравнении с альтернативными решениями для текущего уровня (переезд на SSD). Ещё был хитрый вариант с созданием memory раздела в linux и разворачивании БД туда, но я не любитель такого рода хаков, а просчитать возможные проблемы подобного подхода было проблематично. В общем, надо было переезжать на SSD. У DataLine это доступно только уже с заключением договора и на иной платформе. На ней мне также предоставили тестовый период. Развернул там всё заново, в этот раз задокументировав все шаги для последующей автоматизации. Повторный перенос домена… Ура! Всё работает быстро и как надо. Постфактум хочу сказать, что очень рад этому переезду, т.к. ни одного простоя по вине DataLine не было (более 8 месяцев уже). Был один интересный простой из-за глюка DNS (мастер зону я хостил всё ещё у первого хостера, т.к. там жили всякие другие мелкие проекты), который внезапно откатился на несколько месяцев. Ну т.е. просто стал DNS возвращать адреса старого сервера и всё. Отловить эту ошибку было очень сложно, т.к. у кого-то работало, а у кого-то нет (у меня изначально работало). Но после сброса кешей DNS на роутере сразу стало всё видно.
В итоге домен тоже был перенесён на DataLine и пока (тьфу-тьфу!) работает без проблем. Итак, теперь у нас несколько серверов и ещё больше экземпляров приложений и надо как-то более-менее равномерно распределять пользователей по ним, чтобы добиться максимальной производительности. Задача разбивается на два шага — балансировка до сервера и балансировка в пределах сервера. 1) Балансировка до сервера
Решается на уровне дата-центра. Все сервера с приложениями заносятся в пул, выбор же самого сервера идёт по двум параметрам: первое — это cookie, а если их нет, то IP hash. Затея в том, чтобы запросы от одного пользователя шли всегда на один и тот же сервер, так кеширование будет работать более эффективно.  Т.к. все сервера приложения равноправные, то и вес ставится одинаковый. Добавляем проверку доступности 5 секунд.
Готово. Теперь запросы пользователей достаточно хорошо распределяются на сервера, а в случае, если один из серверов перестаёт быть доступен, то в течении 5 секунд пользователя переводят на другой. 2) Балансировка в пределах сервера
Тут есть несколько вариантов. Т.к. я использую pm2 для запуска nodejs процессов, то возможен вариант запуска необходимого количества экземпляров приложения (по числу ядер) в cluster режиме, который уже будет разруливаться нодой. Минус данного подхода в том, что механизм sticky sessions не работает, но т.к. мы держим socket подключение, то переключение на другой экземпляр приложения возможно только в случае обрыва. В нашем случае это не доставляет проблем. Второй вариант — это запуск приложений в fork режиме на разных портах и проксирование на них через nginx. Плюсом тут является то, что nginx может ещё и статику раздавать, да SSL сертификат (SSL до сих пор не подключил, кстати, но скоро дойдут руки).
Прописываем в конфиге порты приложений, готово. 3) Проверяем
Проверяю нагрузку на серверах и вижу, что она равномерно распределилась. Отлично! Как и во всех задачах сперва пытаемся понять, какие данные мы хотим собирать: 1) Kadira.io
Во-первых, хочу отметить, что данный сервис, к сожалению, скоро прекратит своё существование — он оказался нерентабельным. Но разработчик обещал выдать исходные коды и информацию, как развернуть всё это у себя. Так что, видимо, инфраструктура проекта ожидает пополнения.  Изобретать велосипед незачем, когда есть замечательные готовые инструменты. Интеграция с приложением представляет из себя всего лишь установку модуля из пакета и прописывание двух строк в конфиге. На выходе получаем мониторинг ресурсов, профилирование тяжёлых запросов, монитор текущей активности, анализатор ошибок и ещё кучу всего. 2) MongoDB Cloud  Интеграция не многим сложнее, но ставится на сервера БД, а не приложения, соответственно. Ставим модуль, запускаем watch'еr. Тут же на месте мониторим ресурсы сервера БД, видим время запросов, лаги репликации, советы обновить БД. Довольно крутая штука тут — это моментальное информирование в случае недоступности БД на почту. 3) Яндекс метрика  Интеграция чуть сложнее, чем Kadira, т.к. помимо установки модуля необходимо модифицировать приложение, чтобы оно информировало скрипт метрики о переходах по страницам (в SPA не всегда очевидно, какие мониторы надо мониторить, потому сделано так). Добавляем нужный код в роутер, и готово: пол, возраст, переходы, посещения, устройства и так далее. Всё в одном месте. Удобно! 4) Действия в игре
Записываются в базу данных приложения, само собой. Впоследствии можно настроить, чтобы старые данные, необходимые только для анализа, переносились на отдельный сервер. Для подобного у монги тоже есть инструменты. Со временем проект обрастает различными дополнениями. Например, у нас была газета, которую сделали сами пользователи. Изначально она была создана и размещена где-то у себя одним из игроков, но потом резко закрылась и игрок пропал. Было решено развернуть у себя аналог на wordpress и дать возможность модераторам публиковать там статьи на околоигровые темы.  Само собой, на существующем сервере нельзя запускать подобное хотя бы из соображений безопасности.  Второстепенные сервера было решено размешать у более дешёвого провайдера, т.к. их доступность некритична. Хотя всё равно был выбран хостер, арендующий мощности в тир 3 дата-центре. Необходимо также публичное место для сбора информации об ошибках. Оптимальным вариантом виделась возможность сбора предложений от игроков и голосования по ним. Совсем опционально — публикация логов изменений тут же. Очень желательна была возможность бесплатного размещения для маленьких компаний или до какого-либо лимита. Я перепробовал около трёх десятков систем и в итоге остановился на helprace.  Прямо будто бы под запрос сделана. Вообще, у них есть интеграция с внешним логином, но только в платной версии. Т.к. одной из целей было сэкономить на стоимости содержания, регистрацию у нас система требует отдельно. Аудитория всегда очень радует — сотни сообщений об ошибках, горы предложений и пожеланий. Иногда даже проскакивает сдержанная похвала игре и разработчикам :-) А это рассказ о левой части схемы.  Немного странный заголовок в рамках данной статьи, но именно этот вопрос повлёк за собой появление домашнего сервера. Перед поездкой в Индию прямо в последний день дёрнул меня чёрт сходить почистить матрицу на Canon D350 в авторизованный центр. Там «мастер» грязной хреновиной поцарапал матрицу в хлам. До самолёта было менее 8 часов, так что времени на разборки не было, надо что-то решать. По отзывам нашли хорошую точку на вокзале, приехали туда, отдали мастерам (терять-то уже нечего). Ребята покрутили, спросили: «что с матрицей случилось :-)»?.. Почистили как-то хитро и вроде стало даже приемлемо (фотографии из Индии сделаны именно на эту камеру), за что им спасибо. Но вот по приезду домой снимать я стал мало, т.к. всякие неприятные пятна на фотографиях были и портили даже очень удачные кадры. Пришло время брать новую камеру, да уж по-серьёзному сразу — FullFrame. Сперва выбор пал на Nikon D800. 37 мегапикселей… Мама дорогая, это же сколько raw весить будет? 50-100 мегабайт! (В итоге, кстати, взял D750 с гораздо более приятным весом raw'ок.)
Мне нравится сохранять архив (удаляя только дубли и совсем неудачные кадры), и со временем стало ясно, что просто домашнего компа мало. При таком весе это 12000 фоток на терабайт. Кажется, что это много, но на самом деле это несколько лет не очень активной съёмки. Для понимания — за прошлые года архив перевалил за 50 тысяч фотографий. И да, иногда для некоторых задач я возвращаюсь к своему фотоархиву, чтобы найти нужную. Немного погуглив, я понял, что NAS придуманы именно для этого. Потом натолкнулся на ZFS и понял, что он замечательно обнаруживает проблемы, которые у меня уже происходили несколько раз:  (Забавно, что картинку с ошибкой загрузить так и не удалось, пришлось делать скриншот и вырезать.) Отследить такие проблемы сложно. Они ещё вполне успешно могли улететь в бекап и продублироваться в рейде. Меня ещё спасло то, что NASы нифига не были доступны в моём городе (Калининград), хотя сейчас ситуация уже немного получше. После очень долгого анализа (в целом на решение задачи в заголовке ушло более 200 часов) было решено собрать свой сервер и поставить туда FreeNAS.  В общем-то, собирал сразу надолго, и потенциально система расширяется до петабайта хранилищ и половине терабайта оперативки. На текущий момент там всего 12 терабайт места и 32гб памяти. Общая стоимость вышла сравнимо или дешевле готовых NAS решений (от 4 дисков), при этом гибкость и потенциал намного выше. Тут же сразу стало очевидно, что старенький dir-300 просто не позволит мне комфортно работать с сервером. Даже если взял и пошёл пофоткать немного, снял 200 кадров и начал сбрасывать (200 * ~75) 15гб, то сам процесс копирования занял бы около 20 минут! Подумаем, какая скорость нам нужна? Судя по тестам, диски в планируемой СХД WD Red выдают 100-150 мегабайт в секунду, значит, гигабитного канала нам хватит при эксклюзивном использовании. А если сразу два пользователя? Например, кто-то из семьи решит фильм в HD 4k посмотреть (с другого физического диска)? Значит, до сервера должно идти минимум 2 канала. После анализа рынка стало ясно, что надо брать MikroTik. Выбор пал на CRS109-8G-1S-2HnD — восемь гигабитных портов и невероятная возможность конфигурирования. Меня очень расстраивало, когда недоступность сети по вине провайдера (Привет, Билайн!) лишала меня возможности работать. (Ну да, есть ещё мобильный интернет, но это вообще даже близко не комфортная работа, особенно при том, что он тут нифига не ловит и тоже требует отдельного решения в виде внешней антенны.)  В нашем подъезде работают два провайдера — Билайн и Ростелеком. Сперва необходимо было настроить их отдельно, с чего я и начал. Ох уж эти пляски подключения Билайна на роутерах микротик! С бубном и плясками удалось подключить, но нервов и времени скушало много. Плюс таки требуется иногда ручная донастройка, т.к. решения с постоянно запущенным скриптом на роутере мне не нравилась от слова совсем. Билайн в подъезде, кстати, тоже с микротика всё раздаёт, что было вдвойне удивительно. А Ростелеком тянет оптоволокно до квартиры (Вилы этому маркетологу!), так что без дополнительного роутера не обойтись. Ну а роутер связать с микротиком вообще проблем не составило, т.к., по сути, это просто сеть. У Билайна за дополнительную плату был приобретён (ещё давно) внешний IP, который я всегда использовал в качестве одного из средств авторизации и для доступа ко внутренним ресурсам. А вот для того, чтобы это всё заработало вместе и одновременно, пришлось подтянуть знание сетей и пакетов. Для более-менее простой настройки и понимания, что происходит, советую ознакомиться с презентацией Bandwidth-based load-balancing with failover. The easy way. На сервере будут некие ресурсы, доступные из внешней сети. Как этого добиться? Как упоминал выше, у меня есть внешний IP. На внешний IP ссылается универсальный поддомен вида *.home.domain Есть несколько вариантов разруливания: layer 7 или по порту. Изначально я настраивал его с использованием layer 7 на роутере, проверяя что же там за звёздочкой и направляя куда надо, но быстро выяснилось, что подобное использование очень сильно нагружает процессор роутера. С учетом того, что ресурсы будут использоваться только небольшим кругом лиц, решено было использовать более дешёвую адресацию по портам.  Прописываем правило в фаерволе, разрешающее подключение на определённые порты на внешний IP, добавляем в NAT правило-связь с внутренним ip и портом… Работает! (Кстати, при работе двух подключений необходимо, чтобы ответ шёл через того же провайдера, на который пришёл запрос. Это называется sticky connections и описано в той же презентации.) Допустим, что мы уезжаем на какое-то время в другую страну. Как получить полный доступ, как будто я нахожусь внутри сети? Конечно же, нужен VPN. На роутере запускаем VPN сервер, добавляем его в bridge, создаем пользователя — готово. Без какого-либо дополнительного ПО создаётся подключение.  Начнём с разделения ценности хранимой информации. Выделим три градации:  1) Легко восстанавливаемая информация (музыка, фильмы).
2) Некритичная, но неприятно терять (фотографии).
3) Важная, невосполнимая информация (рабочие файлы, резервные копии). Изначально я брал 4 диска как раз под различные типы: часто используемая информация (media), фото (photo), рабочие файлы (work), бекап (backup). Первый слой: Настраиваем создание ежедневных снимков для разделов work и некоторых подразделов media.
Создаем задания репликации снимков на раздел backup в отдельные подразделы. Для раздела photo временно используем схожую стратегию, пока диск backup заполнен менее чем на 50%. После заполнения диска стратегия будет пересмотрена в пользу переноса только полноразмерных jpg для экономии места. В этом случае фотографии как минимум остаются, но теряется возможность полноценной постобработки. Для большей части media резервное копирование не настраивается, т.к. всё восстанавливается из открытых источников. Второй слой: Создаём jail, даём ему read-only доступ к разделу backup. Настраиваем программу для копирования в Amazon Glaicer на сохранение снимков work и других важных, а также на сохранение jpg'ов фотографий раз в неделю. Третий слой: Раз в месяц делается полное копирование на внешний жесткий диск, после чего последний кладётся на полку в дальней от сервера комнате квартиры. Всего таких диска два — они чередуются. Т.е. в архиве я храню только два последних месяца. Архив, в данном случае, используется только в случае потери информации. Что имеем в итоге? Чтобы потерять важный данные, надо очень постараться. Файлы хранятся минимум в 5 экземплярах: оригинал, бекап, глейсер и два архива. При этом фактически занимается места не много, т.к. мы грамотно выбрали, что именно хранить. Собрав такую махину, было бы упущением не предоставить доступ близким и друзьям для безопасного складирования файлов. Само собой, одно из применений — это работа с коллегами над проектом. Owncloud — наше решение.  Создаем jail и устанавливаем по инструкции. Пробрасываем в jail внешний раздел для хранения файлов и резервной копии мета-информации. Настраиваем резервное копирование owncloud в проброшенный раздел. Добавляем правила в роутер для доступа извне. Создаем пользователей. Сохранность файлов гарантируем схемой выше. Нужна возможность легко разворачивать тестовые окружения, рабочие пространства, различные инструменты. Virtualbox — простой и знакомый всем инструмент отлично подходит. А ещё устанавливается в пару кликов, т.к. готовый шаблон для jail phpvirtualbox уже вшит во FreeNAS. Пробрасываем несколько рабочих разделов в virtualbox так, чтобы нам они были доступны как сетевой ресурс от freenas, а не от машин по отдельности, для удобства работы. Создаем первую машину, на которую устанавливаем ту же самую ОС, что и будет на боевом сервере, стараясь сконфигурировать максимально похоже. Ставим туда git, nodejs и прочее, что необходимо для функционирования проекта. Разворачиваем проект, запускаем, проверяем работу. Всё ок? Останавливаем машину, создаем образ — теперь мы можем его использовать, чтобы быстро создавать дополнительные тестовые окружения для нас или сотрудников в один клик. Тут можно рассказать про замечательный Docker, но у меня как-то исторически сложился сугубо негативный опыт работы с ним. Сам ни разу не настраивал, но каждый раз, когда приходил в проект и говорили: «У нас тут Docker, развернуть окружение — дело получаса», в итоге всё затягивалось в лучшем случае дня на три, а потом и сами разработчики признавались, что докер у них никогда не работал. Повторюсь — вероятно мне просто не везло в этом плане, и рано или поздно кто-нибудь меня таки впечатлит его использованием, но пока что у меня и так реально развертка в 1 клик :-) Изначально для планирования и простого взаимодействия мы использовали RealtimeBoard, но он довольно быстро разросся. Необходима была платформа для постановки и ведения задач. Сперва у нас появился GitLab, в котором есть возможность ведения задач. Через какое-то время стало ясно, что банально состояний задачи недостаточно для удобной асинхронной работы и взаимодействия друг с другом без постоянного дёрганья. Я всегда думал, что Jira и весь пакет стоит каких-то там тысяч долларов, и затрачивать подобные суммы для ещё незапущенного проекта в мои планы никак не входило. Но абсолютно случайно наткнулся на то, что для команд до 10 человек каждый из продуктов стоит всего 10$. В итоге всего за 60$ был взят полный комплект инструментов. И в этот момент я понял, что не зря в крупных компаниях есть отдельная должность — «жировод».
Для конфигурации запроса необходимо прописать хренову кучу параметров на более чем 20 экранах. Запрос — это когда вы жмёте «Создать задачу». И вот для типа, например, «Ошибка»:  1) Создаем бизнес-процесс, называем его «Ошибка».
2) Создаем 6 статусов внутри: Открыт, В процессе, Не воспроизводится, Проверка кода, Ожидает вливания в мастер (попадание в мастер у нас означает выкатку на продакшен), Готово.
3) Каждому статусу проставляется одна из категорий: Сделать, В процессе, Выполнено.
4) Создаются необходимые переходы между статусами. Переход может иметь: отдельный экран (появляется, когда инициируем его вручную), свойства, триггеры, условия, валидаторы, post-функции. В данном примере 10 переходов.
5) Большинство переходов имеют свой экран: при создании задачи заполняется информация о самой задаче, когда задача берётся в работу, можно проставить оценку времени и дополнить информацией, если не удалось воспроизвести — поясняются условия или причина (например, уже исправлено в другом месте).
6) На некоторые переходы настраиваются права, чтобы только тимлид проекта мог их делать (например, влить в мастер).
7) Некоторые переходы только автоматические. Например, «вмерджено в dev» инициируется через триггер от bitbucket, когда ветка, в которой был коммит с тегом текущей задачи, вмерджена в dev. Для перехода в статус «проверка кода» необходимо создание pull request'а в bitbucket'е из ветки с нужным коммитом.
8) Создается тип запроса — «Ошибка».
9) Тип запроса «Ошибка» помещается в схему типов запроса.
10) Создаётся схема бизнес-процесса, например, «Разработка ПО».
11) Бизнес-процесс «Ошибка» помещается в схему бизнес-процесса со связью с типом запроса «Ошибка».
12)…
13) ???
999) Profit! Я написал только часть этапов. На деле их гораздо больше :-) Хотя результат, в итоге, того стоит. Коректная система прав, автоматические переходы и экономия времени. Вот тут всё ощутимо попроще. Единственное, что приложение надо было связать с основным сервером Jira, чтобы оттуда и управлять всеми правами. В целом это довольно уютный текстовый редактор, интегрированный с Jira. Множество действий вызывает автоматическое создание страниц в нём с кучей информации. Например, при закрытии спринта в Jira автоматом создается ретроспектива с информацией по закрытым задачам и затраченным ресурсам. Удобно!  Также используется для ведения документации проекта. Git репозиторий, Код ревью и всё в таком духе.
Устанавливаем, интегрируем с Jira, связываем с соответствующим проектом.
Настраиваем protected ветки master и dev. Запрещаем force в них, запрещаем влив без проверки кода минимум 1 участником проекта. Вливать в мастер может только владелец проекта. Влитие запрещено, если не пройдены тесты.  После Gitlab CI ощущение было примерно такое же, как при переходе на Jira — будто бы сел в звездолёт. Куча ненужных, на первый взгляд, настроек, но, по факту, после настройки этот инструмент становится ультимативным. Например, теперь с помощью него я могу с нуля развернуть ещё один инстанс приложения или базы данных в 1 клик, только указав логин и пароль от целевого сервера. Настраиваем необходимые триггеры: на пуш любой ветки запускаем тестирование кода, на пуш в dev запускаем дополнительно деплой на stage сервер и прогоняем дополнительные тесты, при пуше в master запускаем деплой в продакшен, тесты, проверяем, что деплой успешен, а в случае провала откатываем до предыдущей версии. Доволен :) Мы в команде используем Slack для общения по рабочим вопросам, т.к. он вполне удобен для подобных задач и предоставляет много различных удобных инструментов. Одной из его фишек является интеграция со сторонними сервисами. Хочется, чтобы важная информация лилась в отдельный канал, информация по билдам — ещё в один. Нотификацию в каналах отключаем, чтобы не отвлекало зазря. Запушили код, глянули в слак → видим результаты тестов. Удобно. Откомментили твою задачу? Слакбот уже стучится к тебе.  Конечно же, вся важная информация приходит и на почту, но возможность посмотреть всё комплексно за день в одном месте экономит какое-то время. В итоге можно спокойно спать, т.к. в случае различных непредвиденных ситуаций все важные системы продолжают работать, а уж если что-то совсем непонятное произойдет, то как минимум всегда есть возможность восстановить все утерянные данные. У нас есть надёжное сетевое хранилище. Пользователи довольны стабильностью и скоростью работы. Мы знаем, что происходит на всех узлах системы, и можем их независимо улучшать.
В случае необходимости мы можем быстро наращивать мощности, просто добавляя сервера в пару кликов. Мы можем обслуживать запросы пользователей (ошибки, идеи, предложения, вопросы), и быть уверенными, что ничего не будет потеряно или забыто. Мы видим темп нашей работы и можем корректно её планировать. Процесс разработки устроен так, что код проверяется несколько раз, в том числе и живым человеком. Мы можем вводить новых членов команды и быть спокойны — их действия ничего не сломают. А игра всё ещё в стадии открытой беты, и сам геймплей пока далёк от текущих планов, но мы к ним двигаемся. То, что выходит, нам очень нравится. Надеемся, что и игрокам тоже :) Сейчас идёт активный редизайн, отчего некоторые экраны немного поехали, зато другие сильно лучше того, что было раньше.  Честно говоря, я думал, что это будет совершенно небольшая обзорная статья, а вышло вот оно как. Повторюсь — абсолютно каждый из пунктов достоин статьи (или даже цикла) аналогичного объёма. Как только появится время и подходящее настроение — всё оформлю. Прошу простить за некоторые неточности — многие вещи писались по памяти, а времени прошло уже много. При подготовке подробных статей уже будут точности :) Ну и, если дочитали до конца, то вы — молодец. Спасибо за это. Software Engineer