🚀

GitHub ActionsのSelf-hosted RunnerをM1 Macで構築して、iOSのCI/CDが快適になった話

2022/11/09に公開

こんにちは!テラーノベルでiOS/Android/Webとフロントエンド周りを担当している @kazutoyoです!

テラーノベルのCI/CDは、Bitriseを利用していました。(旧プランでTeamsプラン移行前のもの)
BitriseはモバイルにフォーカスされたCI/CDサービスで、かんたんにモバイルでのCI/CDパイプラインを構築できる素晴らしいサービスです。

ただし、テラーノベルのアプリ開発において、以下のような問題点がありました。

テラーノベルでのBitriseを利用する問題点1: マシンタイプが現在では古い

こちらは旧プランを利用していたためGen2マシンを利用できなかったからなのですが、当時のEliteプランでもMac mini 2018年モデルでした。

Model Identifier: Macmini6,2

machdep.cpu.brand: 0
machdep.cpu.brand_string: Intel(R) Core(TM) i7-8700B CPU @ 3.20GHz
machdep.cpu.core_count: 4

そのマシンでビルドやテストを行ったとき、以下のような実行時間となっていました。

  • テスト: 19m7s(p50)
  • ビルドとAppDistributionへの配布: 30m27s(p50)
  • ビルドとAppStoreへアップロード: 30m6s(p50)

1テストを実行するのに20分😱

テラーノベルでのBitriseを利用する問題点2: 料金体系の変更

旧プランの料金プランは$100/月 * 並列数で、並列数に応じて固定額となっていたが、Teamsプランでは購入したクレジットでの費用となり、並列数の制限はないのですが実行時間によって料金が変動するプランになりました。
Teamsプランでは最大実行時間の制限が4時間に緩和されたりいい面もあるのですが、現在の開発チームにおいてそれほど並列実行数が重要ではないのと、現状のビルド時間を考えると料金的に上がってしまう可能性がありました。

テラーノベルでのBitriseを利用する問題点3: 独自のWorkflow構築方法

Bitriseのデフォルト設定では、GUI上でCI/CDのワークフローを構築するようになっています。

ポチポチするだけでCI/CDパイプラインを作れるため、簡単にできるのですが、人によっては設定しづらいと感じることもあります。
また、YAMLで設定も可能ではありますが、複数のパイプラインを作成すると bitrise.yml が肥大化してしまい、メンテナンスが難しくなってしまいます。

💡 解決策としてのGitHub Actions Self-hosted Runner

GitHub Actions Self-hosted RunnerはGitHub Actionsを自分の好きなマシンで実行できます。
オフィスの引っ越しもあり、自由にネットワークやマシンがオフィスに置いておける環境もできたので、オフィスにSelf-hosted Runnerを置いてみたいなぁってSlackでつぶやいたら、CFOが速攻でM1 Mac Miniマシンを購入していました😳

多少のメンテナンスが必要になってくると想定しながらも、それ以上にCI/CDの実行時間が高速化されて時間的にも金額的にもメリットのほうが大きいんじゃないかと思って突き進むことにしました。
(最悪CI/CDでFAILしても、サービスには直接影響ないという強い勇気を持って)

GitHub Actions Self-hosted Runnerの構築する

こちらのスライドを参考に構築をしました。
Self-hosted Apple Silicon GitHub Runner
https://tome.app/tome/self-hosted-apple-silicon-github-runner-cl142srti2504584j3sa5snuzfc

  • Xcodeのインストール
    • CIマシンでは複数のXcodeのバージョンを扱いたい場合もあるため、xcodes などで複数バージョンのXcodeのインストール/切り替えをできるようにするのがおすすめです。
      • ariaを導入していると、ダウンロードが高速化されるため、こちらも導入しておくと便利です。
  • Homebrewのインストール
  • Rosseta2のインストール

Self-hosted Runnerに登録

GitHubの Settings から、 Actions → Runners → New self-hosted runner で新たに登録するself-hosted runnerを作成します。

New self-hosted runner をクリックすると、次のようにセットアップ方法があるので、こちらを参考にセットアップしていきます。

(※下線部の部分を変更)

今回は1台のマシンで、2つのプロセスを立ち上げておく(ジョブを2並列まで)ようにしたので、 actions-runner.a と actions-runner.b というディレクトリをつくり、その中でself-hosted runnerを設定します。

actions-runner.a

スクリプトをダウンロードして設定する。

# ****Download****
## Create a folder
$ mkdir actions-runner.a && cd actions-runner.a
# Download the latest runner package
$ curl -o actions-runner-osx-x64-2.298.2.tar.gz -L https://github.com/actions/runner/releases/download/v2.298.2/actions-runner-osx-x64-2.298.2.tar.gz
# Optional: Validate the hash
$ echo "0fb116f0d16ac75bcafa68c8db7c816f36688d3674266fe65139eefec3a9eb04  actions-runner-osx-x64-2.298.2.tar.gz" | shasum -a 256 -c
# Extract the installer
$ tar xzf ./actions-runner-osx-x64-2.298.2.tar.gz

# Create the runner and start the configuration experience
$ ./config.sh --url "リポジトリのURL" --token "トークン" --name apple-m1-vm.a --labels apple-silicon --unattended

actions-runner.b

先に設定した actions-runner.a と同様に行う。

# ****Download****
## Create a folder
$ mkdir actions-runner.b && cd actions-runner.b
# Download the latest runner package
$ curl -o actions-runner-osx-x64-2.298.2.tar.gz -L https://github.com/actions/runner/releases/download/v2.298.2/actions-runner-osx-x64-2.298.2.tar.gz
# Optional: Validate the hash
$ echo "0fb116f0d16ac75bcafa68c8db7c816f36688d3674266fe65139eefec3a9eb04  actions-runner-osx-x64-2.298.2.tar.gz" | shasum -a 256 -c
# Extract the installer
$ tar xzf ./actions-runner-osx-x64-2.298.2.tar.gz

# Create the runner and start the configuration experience
$ ./config.sh --url "リポジトリのURL" --token "トークン" --name apple-m1-vm.b --labels apple-silicon --unattended

設定ができたら、起動時に自動起動しておくための、以下のようなシェルスクリプトを作っておきます。

#!/bin/bash

cd /Users/runner/actions-runner.a
./run.sh
#!/bin/bash

cd /Users/runner/actions-runner.b
./run.sh

また、それぞれ実行権限を付与

$ chmod 755 Run.A.command Run.B.command

このスクリプトを実行し、このようにGitHubへ繋がったと出たらOK!

先程のRunnersの設定画面で、オンラインになっていることを確認します。

良さそうですね!

あとは、Macのシステム環境設定→ユーザとグループ→ログイン項目に先程のRunnersの起動をするスクリプトをD&Dして、起動時に実行するように登録します。

また、Macがスリープしないように省エネルギー設定でスリープをさせないように設定します。

GitHub ActionsのAction YAMLを作成

あとはリポジトリの .github/workflows 配下にYAMLを追加していきます。

name: Xcode Test

on:
  pull_request:
    types: [opened, synchronize]
jobs:
  test:
    name: Test
    runs-on: [self-hosted, macOS, apple-silicon] # ← こちらでself-hosted runnerを指定する。(設定時のラベル)
    timeout-minutes: 20

    steps:
      - uses: actions/checkout@v3
      - name: bundle install
        run: bundle install
      - name: pod install
        run: bundle exec pod install
      - name: Test
        run: bundle exec fastlane scan --workspace teller.xcworkspace --scheme ci-test --clean

結果 🎖️

並列実行数が2のためキューにたくさん溜まっていると時間はかかるのですが、キューにないときだと以下の時間くらいでCI/CDができるようになりました!🎉

  • テスト: 4~5min
  • ビルドとAppDistributionへの配布: 5~8min前後
  • ビルドとAppStoreへアップロード: 7~12min前後

使ってみた感想(良い面)

  • ActionsでのWorkflow管理が簡単!
    • GitHubとの連携も良く、Actionsでいろんなワークフローをつくれるのが最高!
  • ビルド時間が既存より短縮化されたのが嬉しい!

使ってみた感想(悪い面)

  • なんだかんだやっぱり運用コストはある
    • Xcodeやマシンなどのアップデート
    • インフラやセキュリティなどにも気を配らないといけない
    • たまにネットワークから切れたりしてActionsが実行されていなかったりしていた
    • 出社していないときにCIマシンが不調になったときに、強制リブートする手段がない😱
  • 並列数が2しかないので、並列実行をたくさんしたいときに詰まる
    • そこまで不満はないのですが、こちらの解決策としては物理マシンを増やしたり、Macのベアメタルクラウドを使うとかかもです。

おわりに

悪い面のほうが多く書かれちゃいましたが、半年くらい使った結果だと、CI/CD自体が速くなったのと、Actionsでワークフロー管理ができるようになったので開発体験がかなり良くなりました!

今のチームの規模感だと1台のマシンでなんとかなっていますが、中規模〜大規模になったらスケールする構成も考えないといけないので、その点なんとかなりそうであれば良い選択肢になりそうですね…!

テラーノベル テックブログ

Discussion