Самостоятельный хостинг macOS CI на Apple Silicon с приложением Cilicon

Самостоятельный хостинг macOS CI на Apple Silicon с приложением Cilicon

Коротко: мы выпустили новое приложение для macOS под названием Cilicon, которое предоставляет и запускает эфемерные виртуальные машины для CI (Cilicon Installer – автономное приложение). Используя его, мы смогли переключиться на собственные Actions Runners и ускорить нашу CI в 3 раза, а также дать вторую жизнь некоторым из наших поврежденных устройств M1 MacBook Pro.

Наши проблемы с CI на macOS

Когда мы запускали Trade Republic, наше мобильное CI состояло из одного Mac Pro 2013 года выпуска, на котором работал агент Buildkite Agent. Его производительность была удовлетворительной, но быстро стало ясно, что ручное обслуживание более чем одной машины в долгосрочной перспективе нецелесообразно. В 2020 году, когда команда выросла и начались карантины из-за COVID-19, мы начали искать альтернативы.

Хотя это означало снижение производительности, мы решили переключиться на раннеры (runner — это специальное приложение, предназначенное для запуска тестов, проверки выходных данных и предоставления инструментов для отладки и диагностики тестов и приложений), размещенные на GitHub. Это позволило нам выполнять больше заданий параллельно и вообще не беспокоиться об обслуживании.

В то время наш рабочий процесс iOS выполнялся всего около 10 минут (при использовании кэшированных зависимостей), но это быстро увеличилось до 30 минут по мере роста команды и кодовой базы.

Учитывая 10-кратный множитель цен на минуты запуска macOS, а также принятие GitHub Actions в других репозиториях, мы быстро начали превышать 50 000 бесплатных минут в месяц, включенных в наш корпоративный план. Кроме того, наши команды все больше разочаровывались в производительности раннеров, размещенных на GitHub, поэтому какое-то время искали альтернативы.

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

Однако однажды я случайно наткнулся на Apple Virtualization Framework. Из любопытства я скачал пример кода и запустил его. К моему большому удивлению, код оказался очень простым и понятным. Вскоре стал очевиден потенциал создания и запуска виртуальных машин с помощью кода. Примерно в то же время мне сообщили, что у нас на складе есть несколько MacBook Pro M1, которые либо сломаны, либо находятся в слишком плохом состоянии, чтобы раздавать их сотрудникам. Самостоятельный хостинг нашего CI внезапно стал более реалистичным, и родилась идея Cilicon (CI + Silicon).

Представляем Cilicon

Концепция Cilicon сводится к простому циклу:

Самостоятельный хостинг macOS CI на Apple Silicon с приложением Cilicon

Duplicate Image (дублировать изображение)

Cilicon создает клон пакета (папки) вашей виртуальной машины для каждого запуска. Благодаря отличной функции клонирования в APFS это происходит очень быстро, даже с большими пакетами.

Provision Shared Folder (предоставить общую папку)

В зависимости от выбранного вами поставщика, Cilicon помещает файлы, необходимые вашей гостевой ОС, в папку Resources вашего пакета.

GitHub Actions Provisioner подготавливает образ с URL-адресом загрузки исполнителя, токеном регистрации, именем и метками.

Process Provisioner запускает исполняемый файл по вашему выбору при подготовке и удалении пакета.

Вы также можете отказаться от использования поставщика, установив тип поставщика на none. Это может нормально работать с такими сервисами, как Buildkite, которые используют токены регистрации с неограниченным сроком действия.

Start Virtual Machine (запустить виртуальную машину)

Cilicon запускает виртуальную машину и автоматически монтирует папку пакета Resources в гостевой ОС.

Listen for Shutdown (слушать выключение)

Cilicon прослушивает выключение гостевой ОС и удаляет использованный образ перед запуском заново.

Для создания пакетов виртуальных машин Cilicon поставляется с собственным автономным приложением под названием “Cilicon Installer”. После создания всё, что осталось сделать, это запустить виртуальную машину в режиме редактора, установить все необходимые зависимости и добавить start.command из общей папки ресурсов в качестве Login Item (элемента входа).

Наш предыдущий опыт

После двухнедельного пробного периода в ноябре, в течение которого мы одновременно работали на собственном хостинге и на GitHub-хостинге, мы почувствовали уверенность в том, что сможем переключиться. С тех пор, как мы используем наш парк исключительно из восьми M1 MacBook Pro и без единого сбоя, мы наслаждаемся работой в 3 раза быстрее. Большинство MacBook, которые мы использовали, считались непригодными для использования сотрудниками, поскольку у них были дефекты дисплея, клавиатуры или косметические дефекты. Хотя изначально мы планировали перейти на M1 Mac Mini после тестового периода, компьютеры MacBook проделали такую большую работу, что мы пока продолжим их использовать.

наша первоначальная установка, состоящая из 8 M1 Macbook Pro
наша первоначальная установка, состоящая из 8 M1 Macbook Pro
обзор Github Actions Runner
обзор Github Actions Runner

Возвращение к размещённым на Github раннерам

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

Для этого мы добавили в наш рабочий процесс новое задание, которое проверяет, содержит ли PR метку с именем “Run on GitHub Hosted Runner”, и соответственно выбирает runs-on label (метку запуска) для последующего задания. Нежелание добавлять labeled trigger (помеченный триггер) в наш рабочий процесс означало, что нам необходимо было получать метки через GitHub API, а не извлекать их из предоставленных контекстных переменных, поскольку повторные запуски обеспечивают точный снимок данных из начального запуска. Поскольку раннеры, размещённые на Github, работают на x86, вы также можете включить arch раннера в свои ключи кэша, используя ${{ runner.arch }}.

jobs:   runner_type_job:     name: Runner Type Selection     runs-on: ubuntu-latest     env:       GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}     outputs:       runner_type: ${{ steps.set_runner_type.outputs.runner_type }}     steps:       - id: set_runner_type         run: |           # Fetching fresh PR Labels using GH CLI, needed on Re-run Workflows.           GH_PR_URL="https://github.com/${{ github.repository }}/pull/${{ github.event.number }}"           GH_PR_LABELS=$((gh pr view $GH_PR_URL --json=labels --jq='.labels | map(.name) | @sh') | tr -d \')           echo "Labels applied: $GH_PR_LABELS"           RUN_ON_GH_HOSTED_RUNNER="Run on Github Hosted Runner"           if [[ ! " ${GH_PR_LABELS[*]} " =~ " ${RUN_ON_GH_HOSTED_RUNNER} " ]]; then             echo 'runner_type=["self-hosted", "macos-13", "ARM64", "xcode-14.1"]' >> $GITHUB_OUTPUT           else             echo 'runner_type="macos-12"' >> $GITHUB_OUTPUT           fi   test:     name: Unit Tests     needs: runner_type_job     runs-on: ${{ fromJSON(needs.runner_type_job.outputs.runner_type) }}

Обслуживание

Чтобы свести усилия по обслуживанию к минимуму, Cilicon предлагает несколько хитростей в рукаве.

Поскольку Cilicon не поддерживает подготовку образов по сети, в настоящее время рекомендуется передавать образ через SSD. Таким образом, чтобы исключить любое взаимодействие с ОС (тем более, что некоторые из используемых нами устройств имеют повреждённые дисплеи), Cilicon можно настроить на сканирование подключенного тома с определённым именем и автоматическое копирование VM.bundle. Начало и конец процесса копирования сопровождаются системными звуками, что избавляет от необходимости открывать крышку или взаимодействовать с клавиатурой.

Также не помешает время от времени перезапускать устройства, поэтому Cilicon можно настроить на перезапуск хост-машины после заданного количества запусков.

Заключение

Мы надеемся, что больше компаний и частных лиц будут использовать Cilicon для снижения затрат и ускорения своего CI. В то время как оборудование для самостоятельного хостинга может не подойти для многих, Hetzner предлагает очень доступный хостинг M1 Mac Mini примерно за 70 евро в месяц, и подтверждено, что он работает с Cilicon.

Вклад в проект также приветствуется!

Подписывайся на наши соцсети: Telegram / VKontakte

Вступай в открытый чат для iOS-разработчиков: t.me/swiftbook_chat

Начать дискуссию