電通総研 テックブログ

電通総研が運営する技術ブログ

UEアプリ用のカスタムWindows Node AMIを作成する 前編

こんにちは、金融ソリューション事業部の孫です。
本記事ではUEアプリ用のカスタムWindows Node AMIを作成します。
これまでの記事ではLinuxベースのノードで運用しておりました。
しかし、開発プロジェクトによってはWindowsノードは必須となるケースがあるので、その場合に本記事の知見が読者の皆様にご参考になれば幸いです。

本記事の内容は、前編後編に分けて紹介します。

前編では、Unreal Editorを動作させるためのコンテナイメージの構築に焦点を当てています。
後編では、そのUnreal EditorコンテナイメージをWindows AMIに統合し、EKSでノードとして使用できるようにします。

はじめに

まず、Unreal EngineのRuntimeとDevelopmentコンテナイメージの違いを説明します:

  • Runtimeコンテナイメージ:このタイプのコンテナは、パッケージ化されたUEアプリケーションを実行するための環境のみを含み、完全な開発機能は含まれていません。これは Java Runtime.NET Runtime に例えることができます。
  • Developmentコンテナイメージ:このタイプのコンテナにはUnreal Editorおよびその他のビルドツールが含まれており、完全なUnreal Engineのバージョンに相当します。これは Java SDK.NET SDK に例えることができます。

UEの公式ドキュメントより、技術的および法的な制約のため、WindowsベースのDockerイメージは Runtimeコンテナイメージのみ提供という制限がわかりました。

制限を乗り越えるために、TensorWorksue4-dockerプロジェクトのドキュメントを参考にし、実作のWindows Developmentコンテナイメージを作成します。

コンテナイメージの構築環境

Unreal Engine 5のWindows Developmentコンテナイメージを構築するために、以下の環境を使用しました:

実施手順

  1. 既存のLinuxコンテナイメージの構築プロセス
  2. Windowsコンテナイメージ構築方法の検討
  3. Windowsコンテナイメージ用Dockerfile作成
  4. Windowsコンテナのテスト

1. 既存のLinuxコンテナイメージの構築プロセス

Epic社はLinux DevelopmentコンテナイメージのDockerfileを公開しており、これを参考にWindows Developmentコンテナイメージの構築プロセスを設計します。

  • ベースイメージと環境の設定
    まず、Dockerfileで必要なベースイメージを設定します。
    Linuxの配布版はUbuntuCentOSRed Hatなど多岐にわたるため、ベースイメージは開発者が選択するパラメーター形式で設定されます。
  ARG BASEIMAGE
  FROM ${BASEIMAGE} as prerequisites

次に、NVIDIA GPU Driverをインストールするために、環境変数を設定しています。

  ENV DEBIAN_FRONTEND=noninteractive
  ENV NVIDIA_DRIVER_CAPABILITIES ${NVIDIA_DRIVER_CAPABILITIES},compute,display,video
  • UEソースコードコンパイル環境構築
    必要なシステムパッケージやツール( build-essentialgitpython3DirextXなど)をインストールします。
  RUN apt-get update && apt-get install -y --no-install-recommends build-essential 
  ...
  libxv1 x11-xkb-utils xauth xfonts-base xkb-data
  • ユーザー作成
    非ルートユーザーを作成し、Git LFSを設定し、Gitクローン操作のための認証ヘルパーをインストールします。
  RUN useradd --create-home --home /home/ue4 --shell /bin/bash --uid 1000 ue4
  RUN git lfs install
  ...
  RUN mkdir /home/ue4/UnrealEngine && \
      cd /home/ue4/UnrealEngine && \
      git init && \
      git remote add origin "$GIT_REPO" && \
      git fetch --progress --depth 1 origin "$GIT_BRANCH" && \
      git checkout FETCH_HEAD
  • UE Dependenciesのインストール
    Setup.shスクリプトを実行してUEに必要なパッケージをインストールします。
  RUN --mount=type=cache,target=/home/ue4/gitdeps,uid=1000,gid=1000 sudo apt-get update && \
      ./Setup.sh --exclude=Android --exclude=Mac --exclude=Win32 --exclude=Win64
  • UEのソースビルド
    .gitディレクトリを削除し、Unreal Build Tool (UBT)を利用して、UEソースをビルドします。
  RUN ./Engine/Build/BatchFiles/Linux/Build.sh UnrealHeaderTool Linux Development -SkipBuild

  RUN ./Engine/Build/BatchFiles/RunUAT.sh ...
  • 最終イメージの作成
    UEの実行ファイルを新しいクリーンなイメージにコピーし、最終的な成果物にします。
  COPY --from=builder --chown=ue4:ue4 /home/ue4/UnrealEngine/LocalBuilds/Engine/Linux /home/ue4/UnrealEngine
  ...

2. Windowsコンテナイメージ構築方法の検討

LinuxのDevelopment Dockerfileを解析した上で、UEを実行するコンテナを作成する方法を理解しました。
その中でも、最も重要なのはUEのソースコードコンパイルすることです。

WindowsシステムでUEをインストールする際、 Epic Games Launcher というGUIツールがありますが、このツールはコンテナ内では使用できません。
したがって、WindowsコンテナにUEを搭載するには、ソースコードからのコンパイルのみ可能です。

Linuxコンテナの構築プロセスを参考にして、以下の三つの主要プロセスを設計しました。

  • ソースコードコンパイル環境の設定と必要なツール・ライブラリのダウンロード
    Windowsには、Linuxと同様にコマンドラインからソフトウェアをインストールできるツール Chocolatey があります。
    まずはそれをインストールします。

    そして、 Chocolatey を利用してソースビルドと実行用のVisual Studio 2019 DirectX Vulkan .Net Framework などのツールをインストールします。
    Visual Studioのバージョンについては、公式の推奨バージョンがあり、 公式ドキュメントを確認して選定してください。

  • ② UEのソースコードのダウンロードとコンパイル
    ソースコードのダウンロードは、LinuxのDockerfileの処理を参考にします。

    ソースコードコンパイルには、UEのソース内にある \Engine\Build\BatchFiles\RunUAT.bat ビルドツールを使用します。

    また、イメージサイズを縮小するために、デバッグ時にのみ使用されるDebugSymbolsやプロジェクト作成時に含まれるテンプレートプロジェクトおよびサンプルを削除します。

  • コンパイル済みの実行ファイルを新しいクリーンなイメージに移動して構築を完了
    このプロセスは、LinuxのDockerfileの処理を参考にします。

    また、WindowsのRuntimeイメージのDockerfileも確認すると、vendor-specific graphics APIs を有効にするためのPowershellスクリプトがあります。それも忘れずコンテナ内にコピーします。
    Windowsのコアアーキテクチャに関連しているため、詳細はこのブログを確認してください。

3. Windowsコンテナ用のDockerfileの作成

上記に検討したプロセスに従って、Dockerfileを作成します。

ARG BASEIMAGE="mcr.microsoft.com/windows/server:ltsc2022"

FROM ${BASEIMAGE} as prerequisites
SHELL ["cmd", "/S", "/C"]

# Install Chocolatey
RUN powershell -NoProfile -ExecutionPolicy Bypass -Command "$env:chocolateyVersion = '1.4.0'; Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && echo. && echo.RUN directive complete. Docker will now commit the filesystem layer to disk. && echo.Note that for large filesystem layers this can take quite some time. && echo.Performing filesystem layer commit... && echo.

# Install the rest of our build prerequisites and clean up afterwards to minimise image size
COPY install-prerequisites.ps1 C:\
ARG VISUAL_STUDIO_BUILD_NUMBER=16
RUN powershell -ExecutionPolicy Bypass -File C:\install-prerequisites.ps1 %VISUAL_STUDIO_BUILD_NUMBER% && echo. && echo.RUN directive complete. Docker will now commit the filesystem layer to disk. && echo.Note that for large filesystem layers this can take quite some time. && echo.Performing filesystem layer commit... && echo.

install-prerequisites.ps1 の内容は以下です。

# variable and importing the Chocolatey profile module.
$env:ChocolateyInstall = Convert-Path "$( (Get-Command choco).Path )\..\.."
Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1"

# Install the chocolatey packages we need
choco install --no-progress -y git --params "'/GitOnlyOnPath /NoAutoCrlf /WindowsTerminal /NoShellIntegration /NoCredentialManager'"

# pdbcopy.exe from Windows SDK is needed for creating an Installed Build of the Engine
choco install --no-progress -y choco-cleaner python vcredist-all windows-sdk-10-version-1809-windbg

# Reload our environment variables from the registry so the `git` command works
Update-SessionEnvironment

# Gather the required DirectX runtime files, since Windows Server Core does not include them
Invoke-WebRequest -Uri "https://download.microsoft.com/download/8/4/A/84A35BF1-DAFE-4AE8-82AF-AD2AE20B6B14/directx_Jun2010_redist.exe" -OutFile "$env:TEMP\directx_redist.exe"
Start-Process -FilePath "$env:TEMP\directx_redist.exe" -ArgumentList "/Q", "/T:$env:TEMP" -Wait
expand "$env:TEMP\APR2007_xinput_x64.cab" -F:xinput1_3.dll C:\Windows\System32\
expand "$env:TEMP\Jun2010_D3DCompiler_43_x64.cab" -F:D3DCompiler_43.dll C:\Windows\System32\
expand "$env:TEMP\Feb2010_X3DAudio_x64.cab" -F:X3DAudio1_7.dll C:\Windows\System32\
expand "$env:TEMP\Jun2010_XAudio_x64.cab" -F:XAPOFX1_5.dll C:\Windows\System32\
expand "$env:TEMP\Jun2010_XAudio_x64.cab" -F:XAudio2_7.dll C:\Windows\System32\

# Retrieve the DirectX shader compiler files needed for DirectX Raytracing (DXR)
Invoke-WebRequest -Uri "https://github.com/microsoft/DirectXShaderCompiler/releases/download/v1.6.2104/dxc_2021_04-20.zip" -OutFile "$env:TEMP\dxc.zip"
Expand-Archive -Path "$env:TEMP\dxc.zip" -DestinationPath "$env:TEMP"
Copy-Item -Path "$env:TEMP\bin\x64\dxcompiler.dll" C:\Windows\System32\
Copy-Item -Path "$env:TEMP\bin\x64\dxil.dll" C:\Windows\System32\

# Gather the Vulkan runtime library
Invoke-WebRequest -Uri "https://sdk.lunarg.com/sdk/download/latest/windows/vulkan-runtime-components.zip?u=" -OutFile "$env:TEMP\vulkan-runtime-components.zip"
Expand-Archive -Path "$env:TEMP\vulkan-runtime-components.zip" -DestinationPath "$env:TEMP"
Copy-Item -Path "*\x64\vulkan-1.dll" -Destination C:\Windows\System32\

$visual_studio_build = $args[0]
$windows_sdk_version = 20348

Invoke-WebRequest -Uri "https://aka.ms/vs/17/release/vs_buildtools.exe" -OutFile "$env:TEMP\vs_buildtools.exe"

# NOTE: Microsoft.NetCore.Component.SDK only exists for VS2019+. And it is actually *needed* only for UE5
$vs_args = @(
    "--quiet",
    "--wait",
    "--norestart",
    "--nocache",
    "--installPath", "C:\BuildTools",
    "--channelUri", "https://aka.ms/vs/$visual_studio_build/release/channel",
    "--installChannelUri", "https://aka.ms/vs/$visual_studio_build/release/channel",
    "--channelId", "VisualStudio.$visual_studio_build.Release",
    "--productId", "Microsoft.VisualStudio.Product.BuildTools",
    "--locale", "en-US",
    "--add", "Microsoft.VisualStudio.Workload.VCTools",
    "--add", "Microsoft.VisualStudio.Workload.MSBuildTools",
    "--add", "Microsoft.VisualStudio.Component.NuGet",
    "--add", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
    "--add", "Microsoft.VisualStudio.Component.Windows10SDK.$windows_sdk_version",
    "--add", "Microsoft.Net.Component.4.5.TargetingPack",
    "--add", "Microsoft.Net.Component.4.6.2.TargetingPack",
    "--add", "Microsoft.Net.ComponentGroup.DevelopmentPrerequisites",
    "--add", "Microsoft.NetCore.Component.SDK",
    "--add", "Microsoft.NetCore.Component.Runtime.3.1"
)

# Install the Visual Studio Build Tools workloads and components we need
Start-Process -FilePath "$env:TEMP\vs_buildtools.exe" -ArgumentList $vs_args -Wait

# Clean up any temp files generated during prerequisite installation
Remove-Item -LiteralPath "$env:TEMP" -Recurse -Force
New-Item -Type directory -Path "$env:TEMP"

# This shaves off ~300MB as of 2021-08-31
choco-cleaner

if (Test-Path "$env:APPDATA\NuGet")
{
    Remove-Item -LiteralPath "$env:APPDATA\NuGet" -Recurse -Force
}

# Display a human-readable completion message
Write-Output "Finished installing build prerequisites and cleaning up temporary files."
  • ステップ2:UEのソースコードのダウンロードとコンパイル
    UEのソースコードをダウンロードするためには、EpicのGitHubで登録開発者になる必要があります。詳細は公式ガイドを参照してください。
    git cloneコマンドを実行する前に、GitHubアカウントにログインし、システムにログイン状態を保存してください。
FROM prerequisites as source

# Enable verbose output for steps that patch files?
ARG VERBOSE_OUTPUT=0

ARG GIT_REPO="https://github.com/EpicGames/UnrealEngine.git"

# The git branch/tag/commit that we will checkout
ARG GIT_BRANCH="5.1"

# Clone the UE5 git repository using the host-supplied credentials
WORKDIR C:\
RUN mkdir C:\UnrealEngine && `
    cd C:\UnrealEngine && `
    git init && `
    git remote add origin %GIT_REPO% && `
    git fetch --progress --depth 1 origin %GIT_BRANCH% && `
    git checkout FETCH_HEAD && echo. && echo.RUN directive complete. Docker will now commit the filesystem layer to disk. && echo.Note that for large filesystem layers this can take quite some time. && echo.Performing filesystem layer commit... && echo.

# Run post-clone setup steps
# (Note that the `-no-cache` flag disables caching of dependency data in `.git/ue4-gitdeps`, saving disk space)
WORKDIR C:\UnrealEngine
RUN Setup.bat -no-cache --exclude=Android --exclude=Mac --exclude=Linux && echo. && echo.RUN directive complete. Docker will now commit the filesystem layer to disk. && echo.Note that for large filesystem layers this can take quite some time. && echo.Performing filesystem layer commit... && echo.

# Remove the sample `XXX` example platform cod
RUN rmdir /s /q C:\UnrealEngine\Engine\Platforms\XXX 2>NUL || exit 0 && echo. && echo.RUN directive complete. Docker will now commit the filesystem layer to disk. && echo.Note that for large filesystem layers this can take quite some time. && echo.Performing filesystem layer commit... && echo.

# escape=`
FROM source as builder

# Set the changelist number in Build.version to ensure our Build ID is generated correctly
ARG CHANGELIST
COPY set-changelist.py C:\set-changelist.py
RUN python C:\set-changelist.py C:\UnrealEngine\Engine\Build\Build.version %CHANGELIST% && echo. && echo.RUN directive complete. Docker will now commit the filesystem layer to disk. && echo.Note that for large filesystem layers this can take quite some time. && echo.Performing filesystem layer commit... && echo.

# Remove the .git directory to disable UBT `git status` calls and speed up the build process
RUN if exist C:\UnrealEngine\.git rmdir /s /q C:\UnrealEngine\.git && echo. && echo.RUN directive complete. Docker will now commit the filesystem layer to disk. && echo.Note that for large filesystem layers this can take quite some time. && echo.Performing filesystem layer commit... && echo.

# Create an Installed Build of the Engine
WORKDIR C:\UnrealEngine
RUN .\Engine\Build\BatchFiles\RunUAT.bat BuildGraph `
    -target="Make Installed Build Win64" `
    -script=Engine/Build/InstalledEngineBuild.xml `
    -set:HostPlatformOnly=true `
    -set:WithDDC=true `
     -set:VS2019=true && `
    (if exist C:\UnrealEngine\LocalBuilds\InstalledDDC rmdir /s /q C:\UnrealEngine\LocalBuilds\InstalledDDC) && `
    rmdir /s /q C:\UnrealEngine\Engine && echo. && echo.RUN directive complete. Docker will now commit the filesystem layer to disk. && echo.Note that for large filesystem layers this can take quite some time. && echo.Performing filesystem layer commit... && echo.
  • ステップ3:コンパイル済みの実行ファイルを新しいクリーンなイメージに移動して構築を完了
    コンパイル済みのファイルを新しいコンテナイメージに移動し、最終イメージの容量を削減するためにクリーンアップを行います。
# Copy the Installed Build into a clean image, discarding the source tree
FROM prerequisites as minimal

# Copy the Installed Build files from the builder image
COPY --from=builder C:\UnrealEngine\LocalBuilds\Engine\Windows C:\UnrealEngine
COPY --from=builder C:\UnrealEngine\Components\DDC C:\UnrealEngine
WORKDIR C:\UnrealEngine

# Enable NVIDIA API Support
COPY enable-graphics-apis.ps1 C:\
RUN powershell -ExecutionPolicy Bypass -File C:\enable-graphics-apis.ps1 && echo. && echo.RUN directive complete. Docker will now commit the filesystem layer to disk. && echo.Note that for large filesystem layers this can take quite some time. && echo.Performing filesystem layer commit... && echo.

以上で、Dockerfileの作成は完了しました。
そして、以下のコマンドを実行し、ビルドします。

$ docker build -t unreal-engine-dev-windows-5.1 .

4.Windowsコンテナのテスト

テスト1:コンテナを利用してプロジェクトを編集する

Runtimeコンテナとは異なり、今回はUnreal Editorもイメージに含めました。
これにより、プロジェクトを開いて開発作業が行えます。

テスト前に、Unreal Editor GUIを使用してUEプロジェクトを作成します。
※コマンドを使用してプロジェクトの作成も可能ですが、UEプラグインのインストールはGUIで行う方が便利ですので、今回はGUIを使用してプロジェクトを作成しました。

Unreal Editorを開き、テンプレートを使用して WinContainerTest という新しいプロジェクトを作成します。

作成が完了したら、Pixel Streamingプラグインを有効にします。

メニューバーから EditPlugins を選択し、検索ボックスに Pixel Streaming を入力します。


プラグインをチェックしてUnreal Editorを再起動し、インストールを完了します。

全部完了したら、コンテナを使用してこのプロジェクトを起動し、Web上で確認できるかを確認します。

以下のDockerコマンドを実行して、プロジェクトをコンテナ内のUnreal Editorで開きます:

$ docker run --rm -ti  \
      --isolation process --device class/5B45201D-F2F2-4F3B-85BB-30FF1F953599 \
      -v "D:\Unreal Projects\WinContainerTest:C:\WinContainerTest" \
      -p 8080:80  --entrypoint "cmd.exe" unreal-engine-dev-windows-5.1:latest \ 
      /S /C C:\\UnrealEngine\\Engine\\Binaries\\Win64\\UnrealEditor-Cmd.exe \
      C:\\wincontainertest\\WinContainerTest.uproject -AudioMixer \
      -RenderOffscreen -stdout -FullStdOutLogOutput -unattended  \
      -EditorPixelStreamingStartOnLaunch=true

※ここで注意していただきたい点は以下のとおりです:

  • WindowsでコンテナのGPUサポートについて
  • 起動パラメータについて:
    • -EditorPixelStreamingStartOnLaunch=true パラメータを使用すると、バックグラウンド起動時に自動的にPixel Streamingサービスが開始されます
    • -stdout -FullStdOutLogOutput パラメータを使用すると、フルログが出力されます
    • -unattended パラメータを使用すると、UEプロセスが終了したときに DialogBoxWMessageBoxW GUIポップアップを呼び出さず、正常に閉じられます

プロジェクトのロードが完了したら、ログにPixelStreamingサービスの開始ログが確認できます。

この時点で、ブラウザを開いて URL127.0.0.1:8080 を入力すると、UEプロジェクトの編集画面が表示されます。

このWebブラウザ上で、遠隔操作でUnreal Editorを使用してプロジェクトを開発することができ、非常に便利でしょう。

テスト2:コンテナを使用してアプリケーションをパッケージングする

コマンドラインを開き、以下のDockerコマンドを実行します:

$ docker run --rm -ti -v "D:\Unreal Projects\WinContainerTest:C:\WinContainerTest"  \
      --entrypoint "cmd.exe" unreal-engine-dev-windows-5.1:latest /S /C \
     C:\\UnrealEngine\\Engine\\Build\\BatchFiles\\RunUAT.bat BuildCookRun \
      -clientconfig=Development -serverconfig=Development \
      -project=C:\\WinContainerTest\\WinContainerTest.uproject \
      -utf8output -nodebuginfo -allmaps -noP4 -cook -build -stage  \
      -prereqs -pak -archive \
      -archivedirectory=C:\\WinContainerTest\\Packages  \
      -platform=Win64


完了後、[project]/Packages フォルダで、パッケージされたファイルが確認できます。

問題集

今回のコンテナを使用しているときに、いくつかの問題が発見されました、ここで共有いたします。

  • 新規作成されたUEプロジェクトは、GUIで一度も開いたことがない場合、コンテナで直接開くことができません。
    • これは、プロジェクトを起動する前に、最新のエディターDLLを取得する必要があるためです。
    • 解決するには、開く前に、コンテナでプロジェクトのエディターターゲットを一回コンパイルしてください。 bash Build/BatchFiles/Build.bat <ProjectName>Editor Win64 Development <PathToProjectFile>.uproject -WaitMute
    • 参考文献:https://blog.mi.hdm-stuttgart.de/index.php/2017/02/11/uat-automation/
  • コンテナでプロジェクトを開く速度が遅い。
    • これは、キャッシュ(DDC)を再作成する必要があるためです。
    • Shared DDCを設定することで解決できます

Shared DDCの設定方法

まず、プロジェクト用のDDCを作成します。
以下のコマンドを実行すると、プロジェクトフォルダーに Project/DerivedDataCache/DDC.ddp キャッシュファイルが生成されます。

$ Engine\Binaries\Win64\UnrealEditor.exe ProjectName -run=DerivedDataCache -fill -DDC=CreatePak

次にShared DDCのパスを指定します。[ProjectPath]/Config/DefaultEngine.ini の最後に以下のコードを追加します。

#Default Engine.ini
[DerivedDataBackendGraph]
Shared=(Type=FileSystem, ReadOnly=false, Clean=false, Flush=false, DeleteUnused=true, UnusedFileAge=10, FoldersToClean=10, MaxFileChecksPerSec=1, Path=%GAMEDIR%DerivedDataCache/DDC.ddp, EnvPathOverride=UE-SharedDataCachePath, EditorOverrideSetting=SharedDerivedDataCache, CommandLineOverride=SharedDataCachePath)

※Pathをプロジェクトフォルダー内の DerivedDataCache/DDC.ddp を設定してください。

Shared=(Type=FileSystem,... Path=%GAMEDIR%DerivedDataCache/DDC.ddp,...)

おわりに

今回の記事では、Unreal Editorを含めるWindowsコンテナイメージの構築方法をご紹介しました。
次の後編で、このWindowsコンテナイメージをEKS用のWindows AMIに統合し、Windowsノードとして稼働させる方法を紹介します。

現在、電通総研はweb3領域のグループ横断組織を立ち上げ、Web3およびメタバース領域のR&Dを行っております(カテゴリー「3DCG」の記事はこちら)。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください!
私たちと同じチームで働いてくれる仲間をお待ちしています!
電通総研の採用ページ

参考文献

執筆:@chen.sun、レビュー:@akutsu.masahiro
Shodoで執筆されました