「Azure DevOps で実現する Unity アプリのハイパフォーマンス CI/CD」フォローアップ

## 概要

2021年1月24日にオンラインで開催されたイベント [July Tech Festa 2021 winter (JTF2021w)](https://techfesta.connpass.com/event/193966/) にて「Azure DevOps で実現する Unity アプリのハイパフォーマンス CI/CD」というセッションで登壇しました。

イベントからかなり時間が経ってしまいましたが、この記事ではそのフォローアップと振り返りとして、セッションの要約とサンプルプロジェクトの紹介、補足のトピックについてお伝えしたいと思います。

セッションのスライド資料は [SpeakerDeck](https://speakerdeck.com/yaegashi/cd) で閲覧できます。
[![](https://files.speakerdeck.com/presentations/a25ee7b047a34bb487666aaa39bf2748/slide_0.jpg)](https://speakerdeck.com/yaegashi/cd)

またセッションの録画も [YouTube](http://www.youtube.com/watch?v=TqmbZwh5op0) で公開されていますのであわせてご覧ください。
[![](http://img.youtube.com/vi/TqmbZwh5op0/0.jpg)](http://www.youtube.com/watch?v=TqmbZwh5op0)

## セッションの要約

JTF2021w は #推しテク総選挙 というテーマでの開催でしたが、今回のセッションでは Android/iOS Unity アプリ CI/CD のクラウドシフトに関する内容で、次のような推しテクを紹介させていただきました。

- Unity Build Server ライセンスの導入と運用
- Azure DevOps によるオートスケール CI/CD パイプライン構築
- Azure Image Builder と Customazed CLI による VM イメージ構築

これらのトピックを簡単に要約してお伝えします。

### Unity Build Server ライセンスの導入と運用

Unity アプリの CI/CD を語る上で Unity Editor のライセンスは避けては通れない話題です。本セッションでは 2020 年に登場した新しいライセンスである [Unity Build Server](https://unity.com/ja/products/unity-build-server) を紹介しました。

Unity Build Server は複数のビルドサーバやユーザーによる共有が許可されたビルド専用のフローティングライセンスで、これを使えばライセンス違反の懸念がなく、信頼性が高いスケールするビルドサーバを安価に構築できます。

また SSH トンネルを設定して遠隔のネットワークで動くライセンスサーバに Unity Editor を接続させるテクニックも紹介しました。これにより GitHub Actions や CircleCI といったマネージド CI サービスからでも安全に正規の Unity Editor ライセンスが利用可能となり CI/CD 構成の自由度が大きく向上します。

なお本セッションの内容はユニティ・テクノロジーズ・ジャパン様に監修していただきました。これらの情報がみなさまの Unity Build Server の導入検討の参考になればと思います。

### Azure DevOps によるオートスケール CI/CD パイプライン構築

Android/iOS Unity アプリの CI/CD として本セッションでは [Azure DevOps](https://azure.microsoft.com/ja-jp/services/devops/) のマネージド CI サービスである [Azure Pipelines](https://azure.microsoft.com/ja-jp/services/devops/pipelines/) と [Visual Studio App Center](https://visualstudio.microsoft.com/ja/app-center/) による構成を紹介しました。 Unity Editor のビルドは self hosted エージェントの Windows VM を使用し、Xcode のビルドは MS hosted エージェントの macOS を利用しています。

さらに self hosted エージェントでは[仮想マシンスケールセット (VMSS) エージェントプール](https://docs.microsoft.com/ja-jp/azure/devops/pipelines/agents/scale-set-agents)を活用しています。これは Azure Pipelines が Azure の [VMSS リソース](https://docs.microsoft.com/ja-jp/azure/virtual-machine-scale-sets/overview)を直接制御し、実行待ちのジョブの数に応じて VM の稼働数をオートスケールする機能です。VM を 0 台まで減らすことも可能で VM の稼働コストと管理コストの削減に大きな効果があります。

VMSS エージェントプールと Unity Build Server ライセンスの組み合わせにより、Unity Editor ビルドジョブをハイスペックな VM が並列に処理するハイパフォーマンス CI/CD パイプラインが手軽に実現できるようになりました。これはプラットフォームの種類や開発・リリースの区別など、掛け算で増えていく構成すべてを短時間でビルドするための大きな武器となります。

### Azure Image Builder と Customazed CLI による VM イメージ構築

VMSS エージェントプールによるビルドでは VM の作成と削除が頻繁に行われるため、Unity Editor などのビルドツールをあらかじめインストールした適切な VM イメージを準備することが、パフォーマンス向上のために重要となります。

しかしながら VM イメージのカスタマイズは多くの手間と時間がかかる作業であり、ミスを誘発しやすいものでした。そこで本セッションではこの問題を解決する自動化ツールとして [Azure Image Builder](https://docs.microsoft.com/ja-jp/azure/virtual-machines/image-builder-overview) と拙作の [Customazed CLI](https://github.com/yaegashi/customazed) を紹介しました。

これらの詳細については過去の発表および資料を参照してください。

- [Customazed CLI: カスタムVMイメージ作成支援ツール](https://speakerdeck.com/yaegashi/customazed-cli-kasutamuvmimezizuo-cheng-zhi-yuan-turu)
- [Azure Image Builder と Customazed CLI による簡単 VM イメージ構築](https://qiita.com/yaegashi/items/5216a9b37c2041b93a3a)

また後述する [bananedemo-images](https://github.com/bananedemo/bananedemo-images) にイメージ構築の具体例がありますので参考にしてください。

## サンプルプロジェクト

今回のセッションのデモンストレーションとなるサンプルプロジェクトを紹介します。

### bananedemo-images

[bananedemo-images](https://github.com/bananedemo/bananedemo-images) で公開している [BanaNEDemo_WS2019_UnityDev](https://github.com/bananedemo/bananedemo-images/tree/master/BanaNEDemo_WS2019_UnityDev) は Unity の開発・テストおよび CI 用の Windows VM イメージ構築のサンプルです。

[README.md](https://github.com/bananedemo/bananedemo-images/blob/master/BanaNEDemo_WS2019_UnityDev/README.md) の手順に従って実行すると、 Unity Hub や Google Chrome、VSCode、NodeJS といった各種アプリやツールをプリインストールした VM イメージが得られます。

### LostCrypt (GitHub)

[GitHub の LostCrypt](https://github.com/bananedemo/LostCrypt) は今回のセッションに関する検証のために作成した Unity のサンプルプロジェクトのリポジトリです。

[Unity Technologies によるサンプルプロジェクト](https://assetstore.unity.com/packages/essentials/tutorial-projects/lost-crypt-2d-sample-project-158673)をベースにしており、Unity Editor のビルドスクリプトや、前述の BanaNEDemo_WS2019_UnityDev イメージでビルドするための PowerShell スクリプト、Azure Pipelines YAML ファイルが含まれています。

PowerShell スクリプトは [AzureDevOps/Scripts](https://github.com/bananedemo/LostCrypt/tree/master/AzureDevOps/Scripts) フォルダにあります。

- [UnityTunnel.ps1](https://github.com/bananedemo/LostCrypt/blob/master/AzureDevOps/Scripts/UnityTunnel.ps1)
ライセンスサーバに接続するための SSH トンネルを作成します。自動的にライセンスの設定ファイル C:\ProgramData\Unity\config\services-config.json を作成する機能もあります。

- [UnityInstall.ps1](https://github.com/bananedemo/LostCrypt/blob/master/AzureDevOps/Scripts/UnityInstall.ps1)
Unity Hub CLI を用いて Unity Editor をインストールします。プロジェクトが使用する Unity バージョンを自動的に検出し、ビルドターゲットにあわせて Android や iOS のサポートモジュールをインストールする機能があります。

- [UnityBuild.ps1](https://github.com/bananedemo/LostCrypt/blob/master/AzureDevOps/Scripts/UnityBuild.ps1)
Unity Editor によるプレイヤーのビルドを実行します。
Unity アセット内の名前空間 [AppBuilder](https://github.com/bananedemo/LostCrypt/tree/master/Assets/Editor/AppBuilder) のエントリポイント `AppBuilder.Tasks.CommandLineBuild` を呼び出しています。これは設定を [buildSettings.yml](https://github.com/bananedemo/LostCrypt/blob/master/buildSettings.yml) から読み込んでプレイヤーをビルドする簡単なスクリプトです。
また Unity Editor の起動前に Unity.Licensing.Client を起動し、フローティングライセンスのシートが獲得できるまで待つ機能もあります。

Azure Pipelines YAML ファイルは [AzureDevOps/Pipelines](https://github.com/bananedemo/LostCrypt/tree/master/AzureDevOps/Pipelines) にあります。エントリポイントは次の 2 つのファイルです。ここから別のファイルで定義された共通の変数定義やステップを template で呼び出す構成になっています。

- [pipeline-android.yml](https://github.com/bananedemo/LostCrypt/blob/master/AzureDevOps/Pipelines/pipeline-android.yml)
- [pipeline-ios.yml](https://github.com/bananedemo/LostCrypt/blob/master/AzureDevOps/Pipelines/pipeline-ios.yml)

### LostCrypt (Azure DevOps)

[Azure DevOps の LostCrypt プロジェクト](https://dev.azure.com/bananedemo/LostCrypt)は Unity プロジェクトをビルドして Android/iOS アプリを生成する Azure Pipelines をホストしています。

|Platform|Development|Release|
|---|---|---|
|Android|[![Build Status](https://dev.azure.com/bananedemo/LostCrypt/_apis/build/status/LostCrypt%20Android%20Development?branchName=master)](https://dev.azure.com/bananedemo/LostCrypt/_build/latest?definitionId=11&branchName=master)|[![Build Status](https://dev.azure.com/bananedemo/LostCrypt/_apis/build/status/LostCrypt%20Android%20Release?branchName=master)](https://dev.azure.com/bananedemo/LostCrypt/_build/latest?definitionId=10&branchName=master)|
|iOS|[![Build Status](https://dev.azure.com/bananedemo/LostCrypt/_apis/build/status/LostCrypt%20iOS%20Development?branchName=master)](https://dev.azure.com/bananedemo/LostCrypt/_build/latest?definitionId=13&branchName=master)|[![Build Status](https://dev.azure.com/bananedemo/LostCrypt/_apis/build/status/LostCrypt%20iOS%20Release?branchName=master)](https://dev.azure.com/bananedemo/LostCrypt/_build/latest?definitionId=12&branchName=master)|

公開プロジェクトなので実際のビルドのログを見ることができます。 GitHub の Azure Pipelines YAML ファイルと合わせて参考にしてください。

### LostCrypt (App Center)

Azure Pipelines の成果物である iOS/Android アプリは  Visual Studio App Center で配布しています。

- [bananedemo/LostCrypt-Android-Development](https://install.appcenter.ms/orgs/bananedemo/apps/lostcrypt-android-development/distribution_groups/public)
- [bananedemo/LostCrypt-Android-Release](https://install.appcenter.ms/orgs/bananedemo/apps/lostcrypt-android-release/distribution_groups/public)
- [bananedemo/LostCrypt-iOS-Development](https://install.appcenter.ms/orgs/bananedemo/apps/lostcrypt-ios-development/distribution_groups/public)
- [bananedemo/LostCrypt-iOS-Release](https://install.appcenter.ms/orgs/bananedemo/apps/lostcrypt-ios-release/distribution_groups/public)

公開プロジェクトなので誰でもダウンロードができます。ただし iOS アプリについては個人開発者向けのプロビジョニングプロファイルを適用していますので、みなさまの iOS デバイスでは動きません。ご了承ください。

## 補足のトピック

### ライセンスサーバのモニタリング

現時点の Unity Build Server には、フローティングライセンスのリース状況などを知る方法が公式には提供されていません。これはトラブルシューティングやライセンスの調達数の決定をする上で重要な情報になりますので、本格運用が始まる前にわかりやすく視覚化できないかと思っています。

今回のセッションでも少しだけ紹介しましたが Unity.Licensing.Server にはリース状況を調べることができる REST API があるので、たとえば Prometheus の exporter を書くなどして、時系列変化を追えるようにすることを検討しています。

### Linux VM によるビルド

現在 Linux 版の Unity Editor が開発が進んでおり、プレビュー版がリリースされています。これを使えば Linux VM でのビルドができるようになり、Windows VM と比較して必要なメモリやストレージのリソースが大きく削減できる可能性があるため、期待しています。

現時点では Linux 版の Unity Editor は限られた Unity バージョンでしか利用できないため、互換性を重視する場合は Windows 版を使うしかない状況ですが、 CLI によるビルドに限れば Linux 版も実用になるという話も聞くので、近日中に検証してみたいと思っています。その場合は、次に述べるコンテナを使うことになるでしょう。

### コンテナの活用

今回の検証では Azure Pipelines の VMSS エージェントプールを使用しました。これは自前のビルドホストでのオートスケールが簡単に実現できる素晴らしい機能ですが、一方で VM が使用するイメージの準備が面倒で時間がかかるという問題もあります。

VM と比較してコンテナはこのイメージの構築や管理という点で非常に効率的であり、またフットプリントも小さく起動が高速であるという特長があります。また Unity Editor のビルドに必要なのは CLI だけであり GUI Editor が動く VM は必要ないので、コンテナで動くならそれを採用するほうが好ましいでしょう。

コンテナ自体は Linux と Windows で利用可能です。 Linux コンテナでの Unity Editor には利用実績が見つかりますが、Windows コンテナではまだ事例を聞いたことがないので、これを検証できないかと思っています。プロダクションに使える Unity Editor のイメージがコンテナレジストリで流通できるようになると便利でしょう。

コンテナ実行環境に関しては、現時点の Azure Pipelines では VM エージェント上で動くコンテナしかサポートされておらず、それではコンテナの利点を十分に活かせません。将来的には Azure DevOps の持ち味である Azure との緊密な連携を生かして [Azure Container Instances](https://azure.microsoft.com/ja-jp/services/container-instances/) によるエージェントプールをネイティブにサポートしてくれないかと思っています。あるいは Kubernetes のような汎用オーケストレータで CI/CD パイプラインを組んでみるのも面白いかもしれません。

### 永続化・キャッシュ戦略

ビルドワークスペースの一部を永続化もしくはキャッシュすることは、高速なビルドを実現する上で欠かせない検討項目のひとつです。今回のセッションでも話題にするつもりでいたのですが、実際には調査や検証の時間が足りず、とりあげることができませんでした。

ここでは Azure DevOps と Azure VM のビルド環境で考えられる Unity ビルド高速化のための永続化・キャッシュ戦略を 3 つ記しておきます。

1. **Library キャッシュ**
Unity のビルドでは、プロジェクトのインポート済みのアセットは Library という名前のディレクトリに格納されます。このディレクトリを [Azure Pipelines でキャッシュ](https://docs.microsoft.com/en-us/azure/devops/pipelines/release/caching?view=azure-devops)するだけで、ビルド全体の時間をかなり短縮できます。</dd>

2. **Unity Accelerator**
最近の Unity Editor には [Unity Accelerator](https://docs.unity3d.com/Manual/UnityAccelerator.html) というキャッシュサーバを活用する能力があります。これを使えば複数のビルドホスト間のキャッシュ共有が実現できますが、キャッシュサーバのための VM やコンテナの稼働コストが必要になります。また例によって認証機能が不十分なので、マネージド CI サービスから利用するにはライセンスサーバと同様に SSH トンネルを使うような工夫が必要となるでしょう。

3. **管理ディスク**
まだ構想レベルですが、パイプラインごとのビルドワークスペースを Premium SSD や [Ultra Disk](https://docs.microsoft.com/ja-jp/azure/virtual-machines/disks-enable-ultra-ssd) のような高速な外付けの管理ディスクに置くという方法です。単純なキャッシュの利用では避けられない GB 単位のデータの読み込みと保存を、ボリュームのマウント・アンマウントで済ませられるようになります。

この他にも、Unity Editor インストールや Xcode ビルドなど、永続化・キャッシュによる高速化の余地がある部分がたくさんあります。今後あわせて検証していきたいと思います。

### Azure Pipelines アーティファクト

今回のセッションで示した Azure Pipelines において iOS アプリのビルドでは Xcode でのビルドが必要になる都合上 Windows と macOS の 2 つのビルドホストにまたがるジョブに分割しています。最初のジョブの Unity Editor が生成する Xcode プロジェクトをアーティファクトとして転送しているのですが、これはそれなりの大きさになると思われるので、所要時間が心配になります。実際にはどうだったのでしょうか。

[iOS アプリのパイプラインの実行結果のひとつ](https://dev.azure.com/bananedemo/LostCrypt/_build/results?buildId=66&view=logs&j=fe57f888-f72a-52b9-2082-2565d2fa3d07)を見ていただくと詳細がわかります。最初のジョブの [Publish artifacts ステップ](https://dev.azure.com/bananedemo/LostCrypt/_build/results?buildId=66&view=logs&j=fe57f888-f72a-52b9-2082-2565d2fa3d07&t=8c02d410-e550-5b49-21e9-02839f7a43ea)は 9 秒、次のジョブの [Download ステップ](https://dev.azure.com/bananedemo/LostCrypt/_build/results?buildId=66&view=logs&j=10ad58c5-ca2a-5ef5-ca63-3d41709d191c&t=fd45a7be-8cca-5519-bd6c-4cd03aea5c18)は 29 秒でした。実際に生成されたアーティファクト xcode はファイル数が 837 あり、合計サイズは約 1.3GB です。このファイル数と合計サイズでこの程度の時間で済むのであれば実用上は問題ないといえるでしょう。

特にアーティファクトのアップロード (publish) については、重複排除 (dedup) と圧縮によりかなり効率的に処理されていることが伺えます。エンジニアは巨大なアーティファクトがあると余計な気を利かせて zip や tar でまとめることをやりがちなのですが、 Azure Pipelines でそれをやるとかえって時間がかかってしまう結果になると思われます。

Xcode プロジェクトのアーティファクトにはもうひとつ注意点があります。Windows で作った Xcode プロジェクトでは適切なファイルパーミッションが設定されないため、これを単純に macOS に持ってきてビルドするとエラーになります。これはビルド開始前に *.sh ファイルに実行権限を設定することで回避できます。

```bash
find xcode -name '*.sh' -print0 | xargs -0 chmod +x
```

### リージョンの選択

Azure DevOps では組織の作成時にそれをホストするデータセンターの所在地を選択することができます。これは Azure のリージョンと同じようなものですが、すべてのリージョンから選べるわけではありません。

今回紹介した手法で Android/iOS Unity アプリの CI/CD パイプラインを構築しようとする場合、Azure DevOps 組織のリージョンは US を、 Azure のリージョンは West US 2 を選択することをおすすめします。

その理由は次のとおりです (ただし 2021 年 1 月の情報に基づきます)。

- MS hosted の macOS エージェントは US にしかない (参考: [Microsoft-hosted agents](https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/hosted#hardware), [Data locations for Azure DevOps](https://docs.microsoft.com/en-us/azure/devops/organizations/security/data-location))
- Visual Studio App Center のデータセンターは US にある (参考:[Visual Studio App Center Security and Compliance](https://docs.microsoft.com/en-us/appcenter/general/app-center-security))
- Azure Image Builder はまだプレビューであり West US 2 を含む一部のリージョンでしか利用できない (参考: [リージョン](https://docs.microsoft.com/ja-jp/azure/virtual-machines/image-builder-overview#regions))
- West US 2 は NCasT4v3 や NVv4 のような最新世代 Azure VM がいちはやく利用できる (参考: [リージョン別の利用可能な製品](https://azure.microsoft.com/ja-jp/global-infrastructure/services/?products=virtual-machines&regions=japan-west,japan-east,us-east,us-north-central,us-south-central,us-west-central,us-west,us-west-2,us-central,us-east-2))
- West US 2 の Azure VM 価格は Japan East よりも 1 割以上安い

CI/CD の用途であれば、日本からの距離の遠さはさほど問題になりません。パイプラインを通るデータの所在は局所化したほうがパフォーマンスが向上するし、VM の価格も安いというメリットのほうが大きいという判断です。

実際に試してみるとわかりますが、東京から West US 2 の Windows VM を RDP でメンテナンスしてもさほど遅延が気になることはなく、成果物の転送も問題ない速度が出ました。

特別なケース、例えばデータの所在の厳密な管理が求められるとか、日本にあるプロジェクトリポジトリから膨大なデータ転送が発生するといった事情がなければ、この選択でよいのではないかと思います。

## おわりに

Unity Build Server ライセンスの登場や Azure Pipelines のような CI サービスの発展、そしてこれは偶然ですがコロナ禍をきっかけとした業界・組織全体のクラウドシフト推進強化のトレンドなど、昨年来の出来事のおかげでこれまで手がつけられなかった厄介な Unity アプリ CI/CD パイプラインの構築にも様々なブレイクスルーが生まれ、設計構成の幅が広がったことを実感しています。

今回紹介した事例を参考にして、みなさま自身の Unity アプリ の CI/CD に役立てていただければと思います。そして得られた新しい事例や知見をまたコミュニティに共有していただけるとありがたいです。よろしくお願いします。

セッションでの発表およびこの記事をまとめるにあたり、ユニティ・テクノロジーズ・ジャパンおよび日本マイクロソフトのみなさまには大変お世話になりました。この場を借りて改めてお礼を申しあげます。

また今回のアウトプットの機会を提供していただいていた JTF2021w 実行委員会およびスポンサーのみなさまにも感謝したいと思います。ありがとうございました。