Привет, Хабр! Меня зовут Ярослав Фоменко, я iOS-разработчик в компании Даблтап. После того, как мы с ребятами из iOS-отдела настроили наш CI/CD на Mac mini, начали задумываться о его масштабировании и инкапсулировании и пошли ресерчить то, как это можно сделать. Первым на ум пришел Docker, но инфы оказалось довольно мало как о нем, так и о других возможных способах. В этой статье мы рассмотрим найденные нами возможные решения по развертыванию Gitlab CI/CD на железе и в облаке. Мы изучили возможные варианты развертывания, сравнили простоту и время настройки, использования и масштабирования:  На своем железе: без виртуалок и контейнеров; на виртуальных машинах; в докере. В облачных сервисах: в 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 появляются сложности с тем, что на каждой машине нужно установить/обновить все необходимые зависимости и следить за тем, чтобы никто ничего не сломал.  Первым способом отделить наши раннеры от внешней среды является виртуализация. Gitlab Runner предлагает Parallels и VirtualBox в качестве executor. В связке с Vagrant можно написать довольно удобный скрипт, который поможет быстро поднять виртуалку с нужными зависимостями и выполнять на ней задания. Виртуализация является одним из самых ресурсоемких процессов, если развертывать их на своих устройствах: виртуальным машинам нужно приличное количество ресурсов для работы. Поэтому вам точно понадобится мощное железо. Каждая виртуальная машина — это отдельный образ с полноценной системой и Xcode, что в сумме требует 50 ГБ, а если учитывать зависимости, сам проект, а также кэши Xcode, то под одну VM следует отводить 75 ГБ. Что касается требований к вычислительным мощностям — каждому инстансу можно дать конкретное количество ресурсов. Но стоит помнить, что если выделенных ресурсов не будет хватать, то джоба будет выполняться дольше.  На нашем маке без проблем одновременно может работать 2 задачи на сборку проекта. Если же их уже 3, то время выполнения каждой увеличивается с 3 до 5 минут. Таким образом мы можем создать 3 виртуалки по 2 ядра CPU и 5 ГБ оперативки, если хватит пространства SSD. Но можно и изменять количество CPU и RAM перед запуском, т.к. нет необходимости держать виртуалку постоянно запущенной: можно закрывать ее или уводить в сон после каждой задачи.  Или все-таки нет? На Github можно найти Docker-OSX, который для нас показался довольно интересным решением, потому что контейнеры требуют намного меньше ресурсов, запускаются намного быстрее и могут передавать данные между собой. При более детальном рассмотрении выяснилось, что macOS нельзя засунуть в контейнер, а в Docker-OSX macOS на самом деле виртуализируется на Linux и для запуска контейнера нужна Linux или Windows.  После ресерча по Discord-серверу проекта мы поняли, что это решение изначально создано для тестирования безопасности, а для CI/CD оно не подходит из-за того, что macOS в докере имеет большие проблемы с производительностью: сборка проектов происходит в 2-3 раза дольше, чем на реальной машине. А также существует проблема с сертификатами: данный способ развертывания macOS является нелицензированным, поэтому подпись и распространение сборок невозможны, а полученные .ipa файлы можно запустить лишь на jailbreak-устройствах. По моему мнению, это самый простой способ, не требующий почти никакой подготовки, кроме установленного 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 — тоже дело непростое. Необходимо:  создать воркфлоу с каким-нибудь стартовым условием;  деактивировать воркфлоу, чтобы оно случайно не активировалось; зайти во вкладку Cloud в Xcode; выбрать все PR; выбрать нужный; стартовать сборку. Довольно много кликов. Минусом может стать и то, что для закрытых сторонних зависимостей также нужно давать доступ Xcode Cloud, и здесь тоже нужны права maintainer у репозитория. При крутом, действительно проработанном API, сервис выглядит сыровато. Но что делать, если лишней железки нет, а нужно поднимать раннеры независимо и не хочется их настраивать? 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 минуты. Чтобы укладываться в лимиты и не докупать минуты по более высоким расценкам, можно разделить репозитории между аккаунтами или выделить специальный аккаунт. Если вас не устраивает расход минут и количество нужных зависимостей в 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?