AWSを使ったクラウドUnityビルド環境の構築~ビルドサーバー構築編~
エンジニアの岡村です。この記事は以前掲載した『AWSを使ったクラウドUnityビルド環境の構築~ライセンスサーバー構築編~』の続編であり、シリーズの2記事目です。
前回の記事ではEC2を使ってライセンスサーバーを作成したので、そこに接続してライセンス認証をしてビルドを行うWindowsマシン(self-hosted runner)をAWS上に作っていきます。
UnityのCI事情
Unityを使ってCIを行おうとしたときにまず当たるのが、世のCIサービスのUnity対応の渋さです。サービス側がホストしてくれているマシンにUnityがインストールされている事はまずなく、エディタの処理もサイズも軽いわけではないのでデフォルトで用意されている非力なマシンに毎回ダウンロード、インストールして動かすとかなりの無駄が発生してしまいます。公式が提供しているUnity Cloud Buildが一番簡単に使えて楽なのですが、ビルドに時間が掛かってしまうのと、カスタマイズの低さに難があります。
かといって自前のマシンにインストールしたUnityを動かすのにも色々な落とし穴があったりします。セットアップされたUnityインストール済みのマシンを適当なCIツールから叩くのであればまだ楽ですが、例えばコンテナベースでやろうとしたりすると、大量の地雷を踏むこととなります(Windows Server Coreでサイレントクラッシュするなど)。最近のUnityはコマンドライン経由でのインストールや実行、前の記事で紹介したフローティングライセンスをサポートしてくれており、昔よりはだいぶ扱いやすくなったのですが、それでも叩き方にクセがあったりして一筋縄ではいきません。これはクラウドに限らず、物理PCでCIを回すときにも大変苦戦することになりました。今回はその知見もあったのでクラウド移行は割と楽に出来るかと思っていたのですが、そもそもWindows×UnityがCIしんどい環境の掛け合わせなのもあり、AWSに乗せる中でまたしても色々苦労することになりました。
EC2 スポットインスタンスの活用
前述のとおりUnity Editorは軽いアプリケーションではありません。EC2インスタンスは自由なスペックで簡単に用意できてメンテナンスフリーなのが魅力的ですが、強いインスタンスを立てると相応にお金がかかってしまいます。なので不要な時には止めておきたいのですが、その管理も出来れば自動で行いたいです。自動化するとAWSのスポットインスタンスを利用してさらに値段を抑える事が出来ます。という事で今回は必要になったタイミングでEC2のスポットインスタンスを起動し、不要になったら(しばらく利用されなかったら)シャットダウンする仕組みの構築を目指してみました。
philips-labs/terraform-aws-github-runnerの採用
今回スポットインスタンスの管理にはこちらのOSSを利用させて頂きました。他にもいくつか似た機能を実現するOSSはあったのですが、今回こちらを選んだのは主に以下のような点でした。
- AWS, EC2を利用する前提要件を満たしている(コンテナベースもありましたが、コンテナでのビルドは以前挑戦して失敗したので……)
- Terraformで記述されている(社内でTerraformを利用している為)
- Windows, Linuxのインスタンスに対応している(本当はMacも欲しかったのですが、UnityのビルドはWindows上で行えるのと、Github-hosted runnerでXCode入りのMacが使えるので妥協しました)
- コミュニティが活発である
ちなみに、この辺りのOSS選定基準は以前佐藤さんが書かれたこちらの記事も参考になります。
OSSを利用してrunner用の環境を構築する
terraform-aws-github-runnerを使うと、以下のような構造が出来上がります。 (画像はGitHubより引用)
LambdaのコードなどはOSS側で用意してもらえますが、GitHubと繋ぐための設定や、実際に動かすマシンイメージ(後述)と設定は自分で用意する必要があります。基本的な手順はOSSのドキュメントに記載されている為割愛します。
設定項目 | 設定した値(参考) |
---|---|
runner_extra_labels | "self-hosted,unity-2021.2.11f1" |
instance_types | "t3.xlarge" |
volume_size | 50 |
※ここのラベルは今後GitHub Actionsを作成するときにrunnerを指定する為に利用します。
Terraformの知識があれば構築にそう苦労することはないかと思います。(自分は知識がなかったので社内の得意なメンバーにお願いしました。この場を借りて感謝します。)
ただし、ドキュメント中に「GitHubのAppKeyをBase64エンコードしろ」という指示がありますが、これはBase64でエンコードされているpemキーをさらにBase64でエンコードしろという意味なので、ここだけ気をつけてください。
runner用のVPCとライセンスサーバーのVPCをVPC Peeringで接続する
プライベートIPでライセンスサーバーとのやり取りを行う場合には、VPC Peeringを利用してVPC同士を接続すると便利です。
※IPアドレスは一例です
ピアリング接続を作成した後、それぞれのルートテーブルにルートを追加し、それぞれのセキュリティグループで送信元SGを許可するように設定してください。ライセンスサーバーが動いていれば、ランナー用に用意したサブネット内に適当なインスタンスを立ててhttp://***.***.***.***:8080/v1/status
(プライベートIPアドレス)にアクセスすれば疎通確認ができます。
Runner用のAMIを構築する
Unityをインストールしたマシン(を作る為のマシンイメージ)を用意していきます。今回はAWSのサービスの一つであるEC2 Image Builderを利用して作成しました。
手動でEC2インスタンスを立て、Unityをインストールした後イメージ化してもいいのですが、もし手動で構築する場合は、Unityインストール後にSysprepを実行する必要があります。
EC2 Image Builderで利用するための、Unityをインストールするコンポーネントを作成します。サンプルのyamlファイルを紹介するので、http://***.***.***.***:8080
の部分を前回の記事でセットアップしたライセンスサーバーのアドレスに置き換えてください。また、 ここでインストールするエディタのバージョンも指定しているので、利用するバージョンにあわせて書き換えてください。
name: UnityInstallDocument description: schemaVersion: 1.0 phases: - name: build steps: - name: PowerShellVersion action: ExecutePowerShell inputs: commands: - $PSVersionTable - name: DownloadHub action: ExecutePowerShell inputs: commands: - Invoke-WebRequest https://public-cdn.cloud.unity3d.com/hub/prod/UnityHubSetup.exe -OutFile UnityHubSetup.exe - name: InstallHub action: ExecuteBinary inputs: path: .\UnityHubSetup.exe arguments: - /S - name: InstallEditor action: ExecutePowerShell inputs: commands: - 'Start-Process -FilePath "C:\Program Files\Unity Hub\Unity Hub.exe" -Wait -ArgumentList "-- --headless install --version 2021.3.0f1 --changeset 6eacc8284459 -m windows-il2cpp -m android -m android-sdk-ndk-tools -m android-open-jdk -m linux-il2cpp -m ios --childModules"; exit 0' - name: DownloadVisualStudio action: ExecutePowerShell inputs: commands: - Invoke-WebRequest https://aka.ms/vs/17/release/vs_buildtools.exe -OutFile vs_buildtools.exe - name: InstallVisualStudio action: ExecuteBinary inputs: path: .\vs_buildtools.exe arguments: - --quiet - --wait - --add - Microsoft.VisualStudio.Component.VC.Tools.x86.x64 - --add - Microsoft.VisualStudio.Component.Windows10SDK.20348 - name: RegisterEnvironmentVariableForVisualStudio action: ExecutePowerShell inputs: commands: - '[Environment]::SetEnvironmentVariable("VS170COMNTOOLS", "C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\Common7\Tools", "Machine")' - name: CreateLicenseFile action: ExecutePowerShell inputs: commands: - 'New-Item "$env:PROGRAMDATA\Unity\config" -ItemType Directory -Force' - '@{licensingServiceBaseUrl = "http://***.***.***.***:8080"; enableEntitlementLicensing = $true; clientConnectTimeoutSec = 10; clientHandshakeTimeoutSec = 10} | ConvertTo-Json | Out-File "$env:PROGRAMDATA\Unity\config\services-config.json" -Encoding ascii;'
※上記コード中でUnity HubをExecuteBinaryではなくExecutePowerShellで実行しているのは、HubがEditorインストールを正常完了してもリターンコードに1を返す謎仕様でパイプラインが失敗扱いになるのを回避する為です。
※2022年4月15日追記 上記コード中にPowerShellでjsonファイルを作成している箇所がありますが、-Encoding utf8
を指定していたのを-Encoding ascii
に修正しました。PowerShellのOut-File
の引数であるutf8
はBOM付きutf-8を指しています。BOM付きで保存されたservices-config.jsonを使用した場合、Unityを動かしたときにライセンス認証に失敗してしまうため、BOM無しで保存するように修正する必要がありました。BOM無しutf-8で保存する方法は幾つかあるのですが、今回はjson内でascii範囲外の文字を使っていないのでasciiで保存する事で代用しています。(無いとは思いますが)日本語ドメイン等を使っていたらutf-8で保存しなければならないので頑張ってください。PowerShell 6以上であればutf8NoBOM
が使えるのですが、自分が試したときのバージョンは5.1.20348.558でした。
※2022年5月2日追記 Unityのモジュールのインストールが上手く行っていなかったため修正、またWindowsのIL2CPPビルドを行う際にVisualStuidoのSDKが必要だったため、そのインストールプロセスを追加しました。Unityのインストール時にvisualstudioをインストールするオプションもありますが、そちらはパイプライン上ではうまく動作しませんでした。
コンポーネントができたら、それを基にイメージレシピを作成します。
設定項目 | 設定した値(参考) |
---|---|
ベースイメージ | Windows Server 2022 English Full ECS Optimised x86 の最新バージョン |
作業ディレクトリ | C:/ |
コンポーネント | 直前に作成したビルドコンポーネントをアタッチ |
ストレージ | gp2 50GiB(終了時に削除) |
インフラ設定のVPC等は一旦ネットに繋がれば十分ですが、きちんとテストしたい場合は実際に動かす環境を利用するといいかもしれません。ビルド時に利用するIAMロールには、AmazonSSMManagedInstanceCore
とEC2InstanceProfileForImageBuilder
ポリシーのアタッチが最低限必要です。(必要に応じて追加してください。)
イメージレシピ、インフラ設定ができたらそれを基にパイプラインを作成しましょう。スケジュールは今の所AMI作成まで自動化する予定はないので今回は手動に設定しました。ディストリビューションはデフォルトで問題ありません。
パイプラインが完成したら、アクション>パイプラインを実行する
でamiをビルドする事が出来るようになります。もしコンポーネントの設定ミスでビルドに失敗しても、パイプラインの詳細からCloudWatchに記録されたログを確認することが可能です。
AMIが出来上がったら、前述したterraformの設定ファイルにAMI IDを設定して構築を行いましょう。 OSSの説明書通りにAWS側のインフラ構築、及びGitHub Appを作成してください。
次回予告
これでUnityを実行するための環境が整いました。次回(またしても日付未定)は、今回作ったランナーと前回作ったライセンスサーバーを接続して、GitHub Actionsのコードを書いてビルドを行っていきます。