Привет, Хабр! Меня зовут Ярослав Фоменко, я iOS-разработчик в компании Даблтап. После того, как мы с ребятами из iOS-отдела настроили наш CI/CD на Mac mini, начали задумываться о его масштабировании и инкапсулировании и пошли ресерчить то, как это можно сделать. Первым на ум пришел Docker, но инфы оказалось довольно мало как о нем, так и о других возможных способах. В этой статье мы рассмотрим найденные нами возможные решения по развертыванию Gitlab CI/CD на железе и в облаке.

Варианты развертывания CI/CD

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

  1. На своем железе:

    • без виртуалок и контейнеров;

    • на виртуальных машинах;

    • в докере.

  2. В облачных сервисах:

    • в Xcode Cloud;

    • в Gitlab SaaS;

    • на виртуальных машинах.

Для расчета времени выполнения задач возьмем проект, который активно разрабатывается почти 2 года, имеет 20 зависимостей через Cocoapods, включая Rx, Realm, Firebase, Swinject и прочие.

Сборка на своем железе

Без виртуалок и контейнеров

Можно купить и поставить в офисе Mac mini. Данный способ является довольно удобным, но не самым надежным. Единственное, что нужно сделать, это установить Xcode Command Line Tools, rbenv, Fastlane и Gitlab Runner. Также на локальной машинке можно устанавливать любые необходимые зависимости, что для нас очень важно: при запуске сборки мы используем скрипты на Python для занесения инфы о билде в репозиторий, а также изменяем статус задачи в таск-трекере. После этого можно регистрировать раннеры и запускать pipeline. 

Мы используем Mac mini 2018 года с 6-ядерным Intel Core i7 с 16 ГБ оперативной памяти. Время сборки проекта составляет 3 минуты, а сборка и выгрузка в App Store Connect — еще 9 минут.

Да, вот так все просто. Однако в этой простоте кроется и большой минус: если кто-то решит настроить CI для другого проекта, то он с легкостью может какой-нибудь новой зависимостью положить весь ваш CI/CD. Или же во время настройки у вас ничего не будет работать из-за уже установленных зависимостей.

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

При масштабировании CI/CD появляются сложности с тем, что на каждой машине нужно установить/обновить все необходимые зависимости и следить за тем, чтобы никто ничего не сломал. 

Виртуализируем macOS

Первым способом отделить наши раннеры от внешней среды является виртуализация. Gitlab Runner предлагает Parallels и VirtualBox в качестве executor. В связке с Vagrant можно написать довольно удобный скрипт, который поможет быстро поднять виртуалку с нужными зависимостями и выполнять на ней задания.

Виртуализация является одним из самых ресурсоемких процессов, если развертывать их на своих устройствах: виртуальным машинам нужно приличное количество ресурсов для работы. Поэтому вам точно понадобится мощное железо.

Каждая виртуальная машина — это отдельный образ с полноценной системой и Xcode, что в сумме требует 50 ГБ, а если учитывать зависимости, сам проект, а также кэши Xcode, то под одну VM следует отводить 75 ГБ.

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

На нашем маке без проблем одновременно может работать 2 задачи на сборку проекта. Если же их уже 3, то время выполнения каждой увеличивается с 3 до 5 минут. Таким образом мы можем создать 3 виртуалки по 2 ядра CPU и 5 ГБ оперативки, если хватит пространства SSD.

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

Контейнеризируем macOS

Или все-таки нет?

На Github можно найти Docker-OSX, который для нас показался довольно интересным решением, потому что контейнеры требуют намного меньше ресурсов, запускаются намного быстрее и могут передавать данные между собой. При более детальном рассмотрении выяснилось, что macOS нельзя засунуть в контейнер, а в Docker-OSX macOS на самом деле виртуализируется на Linux и для запуска контейнера нужна Linux или Windows. 

После ресерча по Discord-серверу проекта мы поняли, что это решение изначально создано для тестирования безопасности, а для CI/CD оно не подходит из-за того, что macOS в докере имеет большие проблемы с производительностью: сборка проектов происходит в 2-3 раза дольше, чем на реальной машине. А также существует проблема с сертификатами: данный способ развертывания macOS является нелицензированным, поэтому подпись и распространение сборок невозможны, а полученные .ipa файлы можно запустить лишь на jailbreak-устройствах.

Сборка на облачных сервисах

Xcode Cloud

По моему мнению, это самый простой способ, не требующий почти никакой подготовки, кроме установленного Xcode.

Все, что нужно сделать — это настроить проект для App Store Connect, связать проект с Git и отредактировать воркфлоу. Это все делается поэтапно в интерфейсе Xcode и занимает пару минут. Теперь в вашем распоряжении CI/CD бесплатно на 25 часов в месяц до декабря 2023 года. Дополнительные часы идут за дополнительную плату.

Xcode Cloud позволяет проводить все то же самое, что и обычный Xcode: build, test, analyze, archive. Условием запуска воркфлоу можно выбрать изменения в ветках, пул-реквесты, тэги или же расписание. Также можно выбрать версии macOS и Xcode, на которых должна запускаться воркфлоу. 

Вам определенно стоит присмотреться к Xcode Cloud, если у вас 1-2 проекта, на которых нужно только лишь прогонять билды и делать пуши в тестфлайт без всяких скриптов и доп. условий, а также если вы используете SPM. Данное решение однозначно снимает проблему масштабирования (только заносите Apple деньги). И никто точно не поломает ваш CI/CD никакими зависимостями.

Вам не подойдет Xcode Cloud, если вы используете зависимости по типу Cocoapods или Carthage: для того, чтобы занести сторонние зависимости, нужно взять с сайта Apple скрипт и добавить в проект. Казалось бы, что может быть проще? Но это решение каждый раз ставит зависимости и homebrew и занимает 9 минут, что выльется в копеечку. И еще 5 минут осуществляется сам build. И это против 3 минут на железе. В настройках воркфлоу есть checkbox для clean build, но что без него, что с ним, решение зависимостей и сборка проекта происходят одинаково долго.

Запуск сборок в Testflight — тоже дело непростое. Необходимо: 

  1. создать воркфлоу с каким-нибудь стартовым условием; 

  2. деактивировать воркфлоу, чтобы оно случайно не активировалось;

  3. зайти во вкладку Cloud в Xcode;

  4. выбрать все PR;

  5. выбрать нужный;

  6. стартовать сборку.

Довольно много кликов.

Минусом может стать и то, что для закрытых сторонних зависимостей также нужно давать доступ Xcode Cloud, и здесь тоже нужны права maintainer у репозитория.

При крутом, действительно проработанном API, сервис выглядит сыровато.

Gitlab SaaS

Но что делать, если лишней железки нет, а нужно поднимать раннеры независимо и не хочется их настраивать? Gitlab предлагает SaaS-раннеры на процессорах Intel на 4 ядра и 10 ГБ оперативной памяти, в которых есть последний Xcode и macOS и, скорее всего, все нужные вам зависимости. Для их запуска нужен лишь конфигурационный файл и доступ к сертификатам для распространения сборок.

По факту SaaS-раннеры — это те же самые виртуальные машины, но только уже настроенные за вас и с кучей разных зависимостей.

К слову, раннеры на M1 обещали запустить во второй половине 2022 года, но уже конец декабря, а они все еще недоступны.

SaaS-раннеры доступны, если у вас есть подписка на Gitlab. Использование macOS SaaS-раннеров дороже, чем использование раннеров на Linux. За 1 реальную минуту выполнения CI/CD со счета подписки вычитается 6 минут. На самом продвинутом варианте подписки за 100 долларов доступно 50 000 минут. 

Если взять в расчет, что в SaaS пайплайн будет выполняться по времени как на нашей машине — 12 минут, то это спишет с аккаунта 72 минуты. Чтобы укладываться в лимиты и не докупать минуты по более высоким расценкам, можно разделить репозитории между аккаунтами или выделить специальный аккаунт.

Виртуализируем macOS в облаке

Если вас не устраивает расход минут и количество нужных зависимостей в SaaS-раннерах, то существует множество сервисов, которые предоставляют в аренду доступ к виртуальным машинам, запущенным на mac с помощью Orka или Anka. Цены везде разные, но стоит отметить, что плата идет за аренду одной машины, а не за выполнение задач, как в Gitlab, а саму машину нужно будет дополнительно дольше настраивать под себя. Чаще всего цена аренды виртуалок выше цены аренды SaaS-раннеров.

Вывод

Если вам хватит 1500 минут на проекты и вы не используете сторонние зависимости, то Xcode Cloud — ваш выбор. Также он довольно хорошо подойдет для масштабирования. Цена дополнительных часов будет сопоставима с Gitlab SaaS.

Если у вас всего один проект, который требует больше времени и в перспективе его не нужно будет масштабировать, то CI/CD можно разворачивать напрямую на своем железе.

Если это не так и у вас есть железо, которого хватит на все ваши проекты, то присмотритесь к развертыванию виртуальных машин на нем. Если же его не хватает и вам достаточно времени и зависимостей, предоставляемых Gitlab, то SaaS-раннеры станут отличным решением. Но если нет, то стоит арендовать мощности у сервисов и на них настраивать машину под свои нужды.

Что интересно, Apple недавно зарелизила функцию для создания и конфигурации виртуальных машин прямо в Xcode на Swift. Посмотрим, как это повлияет на использование виртуалок для автоматизированной интеграции и распространения.

В данный момент мы рассматриваем вариант с виртуальными машинами, т.к. количество проектов, которым нужно CI/CD, растет и есть необходимость держать их изолированно. Например, у нас однажды упали джобы после того, как разработчик установил React-Native.

Докер, к сожалению, пока не пригоден для наших целей из-за плохой производительности развернутой таким образом macOS, что скажется на времени выполнения задач CI/CD. Apple реализовала возможность виртуализировать macOS, но, может, в скором времени нам дадут контейнеризировать операционную систему. Остается только ждать. 

А что вы думаете по этому поводу? Какие варианты уже используете для работы с CI/CD?

Комментарии (6)


  1. wizard_s
    30.12.2022 18:40

    Вопрос: что делать с лицензированием macos в виртуалках? (Вариант "поднимаем виртуалки у себя"). Насколько помню, на не-apple железе и десктопные версии нельзя было.


    1. radhold Автор
      30.12.2022 19:08

      Да, действительно, виртуалка будет лицензионной, только если поднимать ее на Mac. Других легальных вариантов здесь нет, к сожалению


      1. tim3lord
        30.12.2022 19:30

        Дизайн Mac Mini не спроста одинаково хорошо смотрится и на рабочем столе и в серверной стойке))


  1. kambala
    31.12.2022 22:37

    Однако в этой простоте кроется и большой минус: если кто-то решит настроить CI для другого проекта, то он с легкостью может какой-нибудь новой зависимостью положить весь ваш CI/CD. Или же во время настройки у вас ничего не будет работать из-за уже установленных зависимостей.

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

    У нас уже лет 5 через дженкинс собирается по 1-4 проекта параллельно на одной машине без сучка и задоринки. Какая у вас была проблема?


    1. radhold Автор
      31.12.2022 22:38

      С параллельностью не было никаких проблем, а вот, когда на этот мак решили накатить что-то для React Native, то CI немного приуныл.


      1. kambala
        31.12.2022 22:58
        +1

        RN - это достаточно специфическая вещь. Стоило бы упомянуть в статье, что именно он помешал выбрать самый простой вариант для CI.

        К тому же, всегда можно было бы поставить несколько его версий на одной машине.