company_banner

Непрерывная интеграция в Unity: как сократить время сборок и сэкономить ресурсы + пайплайн в подарок



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

    Платформы непрерывной разработки и интеграции, или CI/CD, сейчас используются повсеместно в тех отраслях, где решающую роль играет итеративность и отлаженность технических процессов. В этой статье речь пойдёт о CI/CD для реализации наших Unity-проектов для мобильной геймдев-разработки: с какими проблемами мы столкнулись, как их удалось решить, каких улучшений мы добились и как прописан наш пайплайн сборок билдов.

    Сразу договоримся, что в качестве сервера CI мы используем TeamCity от JetBrains, в качестве хранилища Git-репозиториев ― GitHub, для хранения артефактов сборки ― Nexus.

    Итак, перед нами возникли следующие проблемы:

    • Отсутствие общего стандарта создания сборок: доступ к серверу TeamCity был практически у всех разработчиков, в результате чего скрипты сборок писались на разных языках программирования (BASH, PowerShell, Python), и логика часто дублировалась;
    • Слабый парк машин: из-за того, что нам необходимо собирать билды для iOS, приходилось использовать парк машин из Mac mini. И поскольку в Unity практически вся сборка происходит в один поток, распараллелить сборки на одной машине оказывалось делом проблематичным;
    • Мало отлаженная оперативность работы: из-за низкой производительности нашего технического обеспечения сборки происходили очень долго;
    • Большие очереди в ожидании сборки при наличии достаточного количества агентов TeamCity;
    • Отдельный пул агентов для каждого проекта: в связи с различным установленным окружением на устройствах, а также конфликтующими между проектами конфигурационными файлами (файлы настройки кэш-сервера и т.д.) невозможно было организовать общий пул.

    Что в результате сделали?


    • Репозиторий для сборочных скриптов

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

    • Новый парк машин

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

    Изначально у нас была ферма из 13 компьютеров Mac mini, но данное решение далеко от оптимального: ввиду особенности сборок на Unity около 80% времени сборки будут производиться только в одном потоке. Добавим к этому солидное количество обращений на запись к жёсткому диску и получим, что один Mac mini едва справляется с 1-2 одновременными сборками.

    Результат ― необходимость пересмотра технического обеспечения.

    В ходе поиска и сравнения альтернатив по Unity-сборкам мы заметили, что компьютеры на базе AMD Ryzen благодаря своей производительности позволяют собирать до 8-12 сборок одновременно без существенной потери производительности, в связи с чем было решено закупить четыре таких устройства с шестью SSD и установить по два агента на каждый жёсткий диск.

    Сравнение того, как было и что стало, приведено в таблице.



    Среднее время сборок:



    Кроме того, мы организовали приоритезацию выбора агентов TeamCity для уменьшения времени нахождения сборки в очереди. Раньше у каждого нашего проекта был свой пул агентов, и ввиду мультиплатформенности игр проектно-зависимое окружение не позволяло создать общий пул. Теперь, после реорганизации работы системы, мы оставили ряд закрепленных за проектами агентов, которые служат для автоматических сборок, но смогли добавить и несколько общих агентов для всех проектов: они включаются в работу, когда заняты все агенты, привязанные к нужному проекту.

    • Библиотека BuildPipeline для Unity

    Завели небольшую библиотеку для Unity, которая даёт возможность задавать настройки сборки билдов в отдельном окне редактора Unity, а также обладает возможностью запускать сборку билдов в режиме batchmode. Из основного функционала библиотеки: она позволяет добавлять и удалять defines перед сборкой, отключать сторонние библиотеки или конкретные файлы, добавлять кастомные шаги пре- и пост-обработки, все её настройки хранятся в конфигурационных файлах, также есть возможность их наследования.


    Окно defines в библиотеке BuildPipeline

    Наш текущий пайплайн CI/CD


    Сборка PullRequest. Для каждого коммита производится:

    1. запуск Unity для проверки на ошибки компиляции, обновления defines и генерации решений;
    2. запуск тестов;
    3. запуск статического анализатора: с его помощью производится инкрементальный анализ на файлы, затронутые в рамках текущего PullRequest;
    4. сообщение о результате проверки, которое сохраняется в GitHub.

    Шаги сборки Unity-проекта:

    1. Установка Pipenv и запуск скриптов для сборки на Python: обновление и установка сторонних библиотек Python с нашего сервера (проксирование хранилища pypi.org) и последующий запуск скрипта сборки.

    2. Предварительная подготовка для сборки Unity:

    • удаление папки Library, Bundles, выборочных ассетов (по маске и/или конкретных файлов), удаление solutions (файлов .sln) ― при необходимости;
    • генерация файла с информацией о сборке: наименование ветки, номер сборки и проч. ― для дальнейшего использования в билде для отладки и тестирования;
    • установка кэш-сервера Unity для проекта. У каждого проекта он свой. Также у каждого разработчика выставлен кэш-сервер для более быстрого наполнения: при добавлении разработчиком нового ассета он автоматически появится на кэш-сервере и на сервере сборки, ― таким образом импорт ассетов происходит гораздо быстрее.

    3. Запуск Unity для проверки на ошибки компиляции, обновление defines и генерации решений.

    4. Запуск тестов и выход из них при наличии ошибок ― при необходимости.

    5. Запуск Unity BuildPipeline с указанием нужной конфигурации и дополнительных параметров проекта.

    6. Для сборок Android/iOS ― запуск Gradle/Xcode:

    • Gradle ― GradleWrapper;
    • XCode ― архивируем XcodeProject, полученный после Unity, и копируем его на Mac mini. Отдельно устанавливаем и обновляем все необходимые сертификаты и файлы Provisioning Profile. Запускаем команды Clean, Archive, Export.
    • На этапе экспорта есть возможность разделения подписи билда, разработчика и AppStore. В зависимости от того, что собираем, выбираем нужный plist, либо каждый по очереди. На выходе получаем два файла: Developer и Release ― для установки на тестовые девайсы и для заливки в AppStore соответственно.

    7. Заливка собранных билдов и сопутствующих файлов (логи, результаты тестов, .obb, manifest для установки iOS приложений, dsym-файлы и т.д.) в хранилище артефактов, для standalone-сборок ― заливка в хранилище архива собранного билда.

    8. Генерация страницы с QR-кодом для установки билда, добавление ссылок из хранилища и информации по билду в базу данных для дальнейшей работы с приложением PixLauncher ― о нем расскажем далее.

    9. Сообщение в Slack о результате сборки: тому, кто запустил сборку, а также в необходимые каналы.


    Такие сообщения приходят в Slack

    Дальнейшие шаги


    В качестве завершающего шага пайплана происходит распространение собранных билдов на устройства для дальнейшего тестирования и загрузки в сторы.

    Для установки билдов на устройства мы написали небольшое приложение для Android и iOS ― PixLauncher. Его мы устанавливаем на каждое устройство, где есть возможность выбора билда из TeamCity. Для удобства в нём можно установить фильтры ― например, добавить конфигурацию в избранное и далее производить действия с ней в один клик. В случае билдов для Android при необходимости автоматически скачивается файл в разрешении .obb.

    Кроме того, мы организовали возможность установки билда через push-уведомления: на сервер TeamCity мы добавили самописный плагин, который на странице с билдом позволяет выбрать MAC-адреса девайсов, подключенных к локальной сети. Затем на эти устройства поступает push-уведомление со ссылкой на установку ― таким образом она теперь осуществляется в одно нажатие.

    Так, приложение позволило ускорить поиск нужного билда QA отделом и установку на девайсы для последующей проверки.


    Внешний вид приложения PixLauncher для iOS

    Наконец, загрузка билдов в сторы


    После всех произведённых действий естественным образом образуется потребность в гарантированной заливке билда и метаинформации по приложению в сторы.

    Изначально проблемы возникали по большей части с AppStore:

    • заливка в стор производится только с устройства MacOS;
    • необходимо загружать видеоролики, скриншоты и описания приложения более чем на 25 языках.

    Это приводило к большим потерям времени на заливку и сбоям в загрузке файлов в админку стора. Поэтому мы задумались над автоматизацией процесса.

    В результате имеем следующее:

    1. В Google Disk мы завели табличку с описанием приложения на всех языках;
    2. Видеоролики и скриншоты приложения разложили по папкам с определённым неймингом;
    3. В Teamcity для возможности выбора уже собранного билда сделали конфигурацию, зависимую от релизной сборки;
    4. Через API GooglePlay и iTMSTransporter для Apple заливаем билды и мета-информацию о приложении в стор для всех необходимых языков. При возникновении проблем (например, с сетью) ― производим несколько попыток и присылаем сообщение в Slack с текстом ошибок.


    Так выглядит выгрузка билда в AppStore

    В качестве итога ― немного цифр


    • Теперь у нас производится около 400 сборок и до 60 установок билдов на девайсы в день;
    • Существует 57 различных конфигураций сборок на TeamCity;
    • Мы используем 22 агента TeamCity, при этом есть возможность расширения без существенной потери производительности до 48 агентов;
    • Есть возможность горизонтального расширения парка машин.
    Pixonic
    Разрабатываем и издаем игры с 2009 года

    Комментарии 8

      0
      Судя по тому как вы легко с маков перескочили на винду, вас андроид-сборки интересуют больше, чем айос?
        +2
        Мы по-прежнему собираем все платформы, в том числе и iOS. Есть дополнительный степ после сборки Unity. XCodeProject мы банально копируем на Mac mini и завершаем там сборку. Так как Mac mini и «Райзены» у нас находятся в общей сети, то скидывание проекта занимает меньше минуты. И поскольку сборка xcode достаточно быстрая (3-7 минут в зависимости от проекта) и многопоточная, то одного Мак мини нам хватило на все наши проекты.
          0

          Что делаете с внешними плагинами, которые через баш-скрипты пытаются править xcode-проект в постбилде? Настраиваете окружение для перла и тп штук или у вас таких проблемных ассетов уже нет и все нужные работают через юнити-апи для патчей xcode-проекта? Просто раньше с этим была большая проблема и на винде не все можно было собрать, чтобы потом унести на тот же macmini одной папкой.

            0
            Да, постскриптов у нас практически не осталось. Везде, где необходимо, используется Unity API, либо через xcodebuld прикидываем нужные аргументы.
        +1
        А вы смотрели в сторону Unity Build, проводили с ним какие-то сравнения?
          +1
          Если речь про Cloud Build, то да, смотрели и сравнивали, но он имеет ряд ограничений, которые нам не подходят, ну и еще мы используем разные платформы и специфичное окружение.
            +2
            Pixonic да, его имел в виду. Сейчас это все под эгидой Unity Teams, так что смешались названия. Было бы интересно как раз про сравнение и ограничения с учетом того, что у них там есть сборка под бол-во платформ и залив же на них для ревью. Особенно было бы интересно про скорость сборки и общее время (скорость загрузки ассетов в облако vs. скорость сборки).
              +1
              Конкретные цифры сейчас не приведу, но поясню пару моментов.

              Наш сервер VCS находится в той же сети, что и сервер TeamCity. Соответственно, овертайм на передачу данных минимален. Ну и у нас достаточно мощный парк машин: есть возможность передачи большого количества кастомных аргументов во время сборки, через которые могут запускаться отдельные процессы со специфичным окружением (например, во время сборки мы интегрируемся с базами данных, запускаем отдельные процессы для собирания атласов и т.д.) либо выбор кэш-сервера для конкретного билда. Существуют зависимые сборки по триггерам запуска сторонних конфигураций, запуски на PullRequest и запуски автотестов на нашем парке девайсов.

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

              Залив на ревью в маркеты у нас осуществляется в полуавтоматическом режиме по нажатию кнопки на сервере TeamCity, когда все наши внутренние проверки пройдены.

              Короче говоря, инфраструктура у нас гораздо более сложная, чем может обеспечить Cloud Build.

        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

        Самое читаемое