こんにちは、金融ソリューション事業部の孫です。
前回の記事で、Amazon EKS(AWSが提供するKubernetesサービス、以下EKSと略)用のWindowsノードAMI(Amazon Machine Images、以下AMIと略)の構築方法について紹介しました。
今回は、このAMIを使用してWindowsノードを作成し、UnrealEngine(以下はUE)アプリケーションを配信する方法を説明します。
今回のUEアプリケーションサーバーはWindowsコンテナで実行され、PixelStreaming技術を使用してWebブラウザからアクセスできます。
従来のアプリケーションのインストールとは異なり、ユーザーはアプリケーションをローカルにインストールする必要がなく、Webブラウザを通じていつでもどこでもアクセスできます。
- はじめに
- 実施手順
- 1. PixelStreamingアーキテクチャの紹介
- 2. Signalling ServerのDockerイメージの構築
- 3. TURN/STUN ServerのDockerイメージの構築
- 4. デモ用UEアプリケーションのDockerイメージの構築
- 5. ローカルテストおよびECRへのアップロード
- おわりに
- 参考文献
はじめに
Kubernetesの運用において、GPUリソースを利用する際にはいくつかの大きな課題があります。
これはホストマシンのハードウェアに特別な要求があるだけでなく、関連する設定も非常に複雑です。
Linuxコンテナに対しては、Nvidia公式がNVIDIA device plugin for Kubernetesを提供しており、開発者がホストマシン上のGPUリソースへ簡単にアクセスできます。
一方、Windowsコンテナに対しては、Nvidiaからはまだプラグインのサポートが出ていません。
幸いにも、TensorWorks社がKubernetes Device Plugins for DirectXをオープンソース化している為、これを利用することでWindowsコンテナにおいてもホストマシン上のGPUリソースへのアクセスが可能になります。
また、今回のデモで使用するUEアプリケーションは、UnrealEngineのPixelStreaming sampleを利用します。
今回のブログ内容は多岐にわたるため、ブログを前編と後編の2つに分けて紹介します。
前編では、Dockerイメージの構築に焦点を当てます:
- Signalling Serverサービスイメージの構築
- TURN/STUN Serverサービスイメージの構築
- デモ用UEアプリケーションイメージの構築
- ローカルテスト
後編では、EKSの構築とKubernetes yamlファイルの作成について説明します:
- Kubernetes Device Plugins for DirectXのインストールとテスト
- EKS環境の構築
- yamlファイルの作成
- プロジェクトのデプロイとゲームの展示
この記事を読む前に、読者が以下の知識を持っていることを前提とします:
- AWSの基本操作
- EKSの使用経験
- コンテナの開発経験
- UnrealEngineの基本操作
実施手順
- PixelStreamingアーキテクチャの紹介
- Signalling ServerのDockerイメージの構築
- TURN/STUN ServerのDockerイメージの構築
- デモ用UEアプリケーションのDockerイメージの構築
- ローカルテストおよびECRへのアップロード
開発環境
- OS:Windows 11 Pro 22H2 x64
- RAM:32GB
- CPU:Intel Core i5-13600K
- GPU:GeForce RTX 3080
- UnrealEngineのバージョン:5.3
- Docker Desktopのバージョン:4.30.0
※注意:Docker DesktopはWindowsコンテナモードに切り替えてください。
前提準備
サービスの構築を始める前に、以下の環境準備が完了していることを前提とします:
- AWSアカウントの登録
- ローカルのAWS CLI環境の設定が完了
- Docker Desktopがインストールされている
※注意:環境設定の詳細ステップについては、関連公式ドキュメントを参照してください。本文では詳しく述べません。
1. PixelStreamingアーキテクチャの紹介
Pixel Streamingについては、金融ソリューション事業部の山下さんのブログにご紹介したとおり、ビデオストリームをユーザーのWebブラウザに直接送信する技術です。
PixelStreamingのアーキテクチャは、主に三つの部分から構成されます:
- UE+PixelStreaming Plugin:ゲームのメディアストリーム出力を提供します。
- Signalling and Web Server:クライアントとのWebRTC接続を確立するサービスとユーザーがアクセスするWebページを提供します。
- STUN/TURN Server:NATトラバーサルサービスを提供します。
※詳細な技術情報については、UnrealEngineドキュメントを参照してください。ここでは詳しく述べません。
これから、これら三つの部分のDockerイメージをそれぞれ構築します。
2. Signalling ServerのDockerイメージの構築
Unreal Engineの公式にはSignallingサーバーのDockerイメージが提供されていますが、これはLinuxベースのDockerイメージであり、Windowsサーバー上では動作しません。
そのため、公式のDockerfileを参考にして、Windowsシステムで動作するDockerfileを構築する必要があります。
作成したDockerfileの主な処理手順は以下のとおりです:
- Node.jsをインストール
- Signalling Serverのコードをコンテナにコピー
- Signalling Serverアプリケーションをインストール
- エントリーポイント(entrypoint)プログラムを設定
ここで注意すべき点:
- 基本イメージの選択について
- Dockerイメージのサイズを可能な限り小さくするために、
mcr.microsoft.com/windows/servercore:ltsc2022
をBase Imageとして選択しました
- Dockerイメージのサイズを可能な限り小さくするために、
- Signalling Serverのコードについて
- Epic公式のリポジトリからダウンロード可能
- フロントエンドについては、事前に
SignallingWebServer/platform_scripts/cmd/setup.bat
を実行して構築する必要があります - 完了すると
Public
フォルダが生成されます - イメージサイズを小さくするために、不要なフォルダ(Docs、platform_scripts)を削除します
- 最終的なファイル構成は以下のとおりです
SignallingWebServer │ .dockerignore │ cirrus.js │ config.json │ Dockerfile │ package-lock.json │ package.json │ Readme.md │ turnserver.conf ├─modules ├─Public └─tps
上記の手順に基づいて作成されたDockerfileは以下のとおりです:
# escape=` FROM mcr.microsoft.com/windows/servercore:ltsc2022 as installer SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop';$ProgressPreference='silentlyContinue';"] # Install Node.js RUN Invoke-WebRequest -OutFile nodejs.zip -UseBasicParsing "https://nodejs.org/dist/v19.9.0/node-v19.9.0-win-x64.zip"; Expand-Archive nodejs.zip -DestinationPath C:\; Rename-Item "C:\\node-v19.9.0-win-x64" c:\nodejs FROM mcr.microsoft.com/windows/servercore:ltsc2022 as signalling-server SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop';$ProgressPreference='silentlyContinue';"] # Set Node Environment variable WORKDIR C:\nodejs COPY --from=installer C:\nodejs\ . RUN SETX PATH C:\nodejs RUN npm config set registry https://registry.npmjs.org/ # copy SignallingWerServer source COPY SignallingWebServer C:\SignallingWebServer # install dependencies COPY SignallingWebServer\package*.json C:\tmp\ RUN cd C:\tmp\; npm ci # RUN New-Item -ItemType Directory -Path C:/app RUN Copy-Item -Path C:\tmp\node_modules -Destination C:\SignallingWebServer -Recurse RUN Remove-Item -Path C:\tmp\node_modules -Recurse WORKDIR C:\SignallingWebServer RUN npm prune --production RUN ls -l . # Expose TCP ports 80 for player WebSocket connections and web server HTTPaccess EXPOSE 80 # Expose TCP port 8888 for streamer WebSocket connections EXPOSE 8888 EXPOSE 8888/udp # Expose TCP port 19302 for connections to Google's stun server EXPOSE 19302 # entrypoint ENTRYPOINT [ "node", "cirrus.js" ]
次に、イメージのビルドを実行します:
## Build $ cd D:\SignallingWebServer $ docker build -t signalling-demo . ## 確認 $ docker images signalling-demo REPOSITORY TAG IMAGE ID CREATED SIZE signalling-demo latest xxxxxxxxxxx 2 days ago 4.68GB
3. TURN/STUN ServerのDockerイメージの構築
Epic公式と同様に、coturnを使用してTURN/STUNサービスを提供します。
Dockerfileの処理手順は以下のとおりです:
- 非常にシンプルで、coturnを直接インストールするだけです。
FROM alpine:3.17 as turn-server # add dependencies RUN apk update && \ apk add -u --no-cache bind-tools coturn # set entrypoint script COPY entrypoint.sh / RUN chmod +x /entrypoint.sh CMD ["/entrypoint.sh"]
エントリーファイル(entrypoint.sh)の内容は次の通りです:
- エントリーファイルには、coturnサービスを開始するために必要なパラメータが定義されています。
#!/bin/sh export INTERNAL_IP="${INTERNAL_IP:-$(ip a | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1' | head -n 1)}" export EXTERNAL_IP="${EXTERNAL_IP:-$(dig +short myip.opendns.com @resolver1.opendns.com)}" echo "---------" echo "INTERNAL_IP : $INTERNAL_IP" echo "EXTERNAL_IP : $EXTERNAL_IP" echo "TURN_REALM : $TURN_REALM" echo "---------" turnadmin -a \ -u ${TURN_USER:-turn} \ -p ${TURN_PASS:-change} \ -r ${TURN_REALM:-example.com} # Start coturn server with options # https://github.com/coturn/coturn/blob/master/README.turnserver # https://github.com/coturn/coturn/wiki/turnserver turnserver -n --no-cli \ --verbose \ --listening-port=${TURN_PORT:-3478} \ --relay-ip="${INTERNAL_IP}" \ --listening-ip="${INTERNAL_IP}" \ --external-ip="${EXTERNAL_IP?missing external ip}/${INTERNAL_IP}" \ --server-name=${TURN_REALM:-example.com} \ --fingerprint \ --lt-cred-mech \ --realm=${TURN_REALM:-example.com} \ --user="${TURN_USER:-pixel}:${TURN_PASS:-changeme}" \ --rest-api-separator=":" \ --channel-lifetime=${TURN_CHANNEL_LIFETIME:-"-1"} \ --min-port=${TURN_MIN_PORT:-49152} \ --max-port=${TURN_MAX_PORT:-65535} ${EXTRA_ARGS} \ --no-tlsv1 \ --no-tlsv1_1 \ --no-tlsv1_2 \ --secure-stun \ --tls-listening-port=${TLS_PORT:-5349} \ --cert=${CERT_FILEPATH:-/etc/turn/tls.crt} \ --pkey=${PKEY_FILEPATH:-/etc/turn/tls.key}
注意すべき点は、ネットワーク遅延の観点からLinuxサーバーがWindowsサーバーよりも優れているため、Linuxシステムを使用してTURN/STUNサービスを提供することです。
コンパイル時にはDocker Desktopの実行モードを一時的にLinuxコンテナに切り替える必要があります。
切り替え後、イメージのビルドを行います。
## Build $ cd D:\TURN $ docker build -t turn-demo . ## 確認 $ docker images turn-demo REPOSITORY TAG IMAGE ID CREATED SIZE turn-demo latest xxxxxxxxxxx 2 days ago 31MB
4. デモ用UEアプリケーションのDockerイメージの構築
Unreal EngineのDockerイメージに関しては、Epic公式が提供するRuntimeイメージruntime-windows-ltsc2022があります。
しかし、Unreal EngineのアプリケーションをEpicが提供したWindows Runtimeコンテナ上で実行すると、以下のエラーが発生します。
LogWindows: Error: appError called: Assertion failed: (((HRESULT)(Hr)) >= 0) [File:D:\build\++UE5\Sync\Engine\Plugins\Runtime\WindowsMoviePlayer\Source\WindowsMoviePlayer\Private\WindowsMoviePlayer.cpp] [Line: 45] LogWindows: Error: Failed to create dialog. The operation completed successfully. Error: 0x0 (0) LogWindows: Error: === Critical error: === LogWindows: Error: LogWindows: Error: Assertion failed: (((HRESULT)(Hr)) >= 0) [File:D:\build\++UE5\Sync\Engine\Plugins\Runtime\WindowsMoviePlayer\Source\WindowsMoviePlayer\Private\WindowsMoviePlayer.cpp] [Line: 45] LogWindows: Error: LogWindows: Error:
どうやら、イメージの構築中にいくつかのミドルウェアが不足しているようです。
一方、 mcr.microsoft.com/windows/server:ltsc2022
ベースイメージを使用すると問題は発生しません。
そのため、公式が使用する windows/servercore
イメージの代わりに windows/server
ベースイメージを使用して再コンパイルします。
Pixel Streamingプロジェクトの準備
イメージを構築する前に、Unreal Engineからデモアプリケーションを作成し、そしてパッケージ化にします。
まず、Epic Games Launcher
を開き、Unreal Engine
⇒ Sample
に移動します。
次に、画面を下にスクロールして Pixel Streaming Demo
プロジェクトを見つけます。
クリックして、Create Project
ボタンを押してUnreal Engine 5.3プロジェクトを作成します。
プロジェクトが作成されたら、プロジェクトを起動します。
これはPixel Streamingのサンプルなので、Pixel Streamingに関する設定は事前に完了しています。
そして、プロジェクトをパッケージ化します。ターゲットプラットフォームはWindowsです。
パッケージ化が完了した後のファイル構造は次の通りです:
Dockerfileの作成
次に、Dockerfileの作成を開始します。
Epic公式のDockerfileを参考にします。
基本的な処理手順は次の通りです:
- DirectX runtimeのインストール
- DirectX shader compilerのインストール
- Visual C++ runtimeのインストール
- entrypointファイルをコンテナにコピー
- パッケージ化されたPixel Streamingプロジェクトをコンテナにコピー
Dockerfileの全内容は以下のとおりです:
# escape=` FROM mcr.microsoft.com/windows/server:ltsc2022 # Retrieve the DirectX runtime files required by the Unreal Engine, since even the full Windows base image does not include them RUN curl --progress-bar -L "https://download.microsoft.com/download/8/4/A/84A35BF1-DAFE-4AE8-82AF-AD2AE20B6B14/directx_Jun2010_redist.exe" --output %TEMP%\directx_redist.exe && ` start /wait %TEMP%\directx_redist.exe /Q /T:%TEMP%\DirectX && ` expand %TEMP%\DirectX\APR2007_xinput_x64.cab -F:xinput1_3.dll C:\Windows\System32\ && ` expand %TEMP%\DirectX\Feb2010_X3DAudio_x64.cab -F:X3DAudio1_7.dll C:\Windows\System32\ && ` expand %TEMP%\DirectX\Jun2010_D3DCompiler_43_x64.cab -F:D3DCompiler_43.dll C:\Windows\System32\ && ` expand %TEMP%\DirectX\Jun2010_XAudio_x64.cab -F:XAudio2_7.dll C:\Windows\System32\ && ` expand %TEMP%\DirectX\Jun2010_XAudio_x64.cab -F:XAPOFX1_5.dll C:\Windows\System32\ # Retrieve the DirectX shader compiler files needed for DirectX Raytracing (DXR) RUN curl --progress-bar -L "https://github.com/microsoft/DirectXShaderCompiler/releases/download/v1.6.2104/dxc_2021_04-20.zip" --output %TEMP%\dxc.zip && ` powershell -Command "Expand-Archive -Path \"$env:TEMP\dxc.zip\" -DestinationPath $env:TEMP" && ` xcopy /y %TEMP%\bin\x64\dxcompiler.dll C:\Windows\System32\ && ` xcopy /y %TEMP%\bin\x64\dxil.dll C:\Windows\System32\ # Install the Visual C++ runtime files using Chocolatey RUN powershell -NoProfile -ExecutionPolicy Bypass -Command "[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" RUN choco install -y vcredist-all COPY entrypoint.cmd C:\entrypoint.cmd COPY enable-graphics-apis.ps1 C:\enable-graphics-apis.ps1 ################## # Project Settings COPY UnrealEngine-Demo C:\UnrealEngine-Demo # Set our Unreal Engine application as the container's entrypoint, wrapped in the helper script ENTRYPOINT ["cmd.exe", "/S", "/C", "C:\\entrypoint.cmd", "C:\\UnrealEngine-Demo\\PixelStreaming_53\\Binaries\\Win64\\PixelStreaming_53.exe", "-stdout", "-FullStdOutLogOutput", "-AudioMixer", "-RenderOffscreen", "-unattended", "-PixelStreamingURL=ws://127.0.0.1:8888"]
以下の点に注意してください:
- System DLLのコピーについて
- Windows Serverベースイメージに変更したため、必要なSystem DLLはすでに含まれているのでコピーは不要です
- entrypoint.cmdファイルについて
- DirectX以外のグラフィックスAPIを提供することが目的です
- 参考ドキュメント:Enabling vendor-specific graphics APIs in Windows containers
Dockerイメージのビルド
以下のコマンドを実行してイメージをビルドします:
## Build $ cd D:\UnrealEngine-runtime $ docker build -t UnrealEngine-demo . ## 確認 $ docker images unreal-engine-demo REPOSITORY TAG IMAGE ID CREATED SIZE unreal-engine-demo latest xxxxxxxxxxx 2 days ago 11.8GB
5. ローカルテストおよびECRへのアップロード
次に、UnrealEngine-demoとsignalling-demoの両方のイメージが正常に動作するかをローカルでテストします。
Docker ではLinuxコンテナとWindowsコンテナを同時に起動できないため、turn-demo
イメージのローカルテストは行いません。
signalling-demoイメージの起動
以下のコマンドを実行してコンテナを作成します:
$ docker run --rm -p 8888:8888 -p 8888:8888/udp -p 8080:80 signalling-demo
UnrealEngine-demoイメージの起動
Windows OS上でGPUリソースにアクセスするには、以下の起動パラメータを追加する必要があります:
--isolation process --device class/5B45201D-F2F2-4F3B-85BB-30FF1F953599
※参考ドキュメント:GPU acceleration in Windows containers
したがって、GPUリソースが必要なコンテナの起動コマンドは以下のとおりです:
$ docker run --rm -ti --isolation process --device class/5B45201D-F2F2-4F3B-85BB-30FF1F953599 --entrypoint "cmd.exe" unreal-engine-demo /S /C C:\\entrypoint.cmd C:\\UnrealEngine-Demo\\PixelStreaming_53\\Binaries\\Win64\\PixelStreaming_53.exe -stdout -FullStdOutLogOutput -AudioMixer -RenderOffscreen -unattended -PixelStreamingURL=ws://[Local Public IP]:8888
Signallingサーバーに正常に接続されると、Signallingサーバーに以下のログが出力されます:
08:19:52.154 Streamer connected: ::ffff:[Local Public IP] 08:19:52.155 ::ffff:[Local Public IP] <- {"type":"identify"} 08:19:52.652 ::ffff:[Local Public IP] -> {"type":"endpointId","id":"DefaultStreamer"} 08:19:52.652 Registered new streamer: DefaultStreamer 08:20:52.646 DefaultStreamer -> {"type":"ping","time":1715761252}
Webブラウザでの確認
ブラウザを開き、127.0.0.1:8080
と入力すると、以下のUEアプリケーション画面が表示されます:
Signallingサーバーのログ出力でも、ユーザー接続のログが表示されます:
これで、イメージが正常に動作することを確認できました。
Amazon Elastic Container Registryへのイメージのアップロード
次に、ローカルのイメージをAmazon Elastic Container Registry(以下はECR)にアップロードします。
これにより、これから構築するEKSで使用できます。
AWS Console
⇒ Amazon ECR
⇒ Private registry
⇒ Repositories
を開きます。
「リポジトリを作成」ボタンをクリックし、以下の3つのイメージリポジトリを順番に作成します:
作成完了後
ローカルのDockerイメージのアップロード
## Amazon ECR login $ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin 236960927670.dkr.ecr.ap-northeast-1.amazonaws.com ## Set tags $ docker tag signalling-demo:latest 236960927670.dkr.ecr.ap-northeast-1.amazonaws.com/signalling-windows-demo:latest $ docker tag unreal-engine-demo:latest 236960927670.dkr.ecr.ap-northeast-1.amazonaws.com/unreal-engine-windows-demo:latest $ docker tag turn-demo:latest 236960927670.dkr.ecr.ap-northeast-1.amazonaws.com/turn-windows-demo:latest ## Upload Docker images $ docker push signalling-windows-demo:latest 236960927670.dkr.ecr.ap-northeast-1.amazonaws.com/signalling-windows-demo:latest $ docker push unreal-engine-windows-demo:latest 236960927670.dkr.ecr.ap-northeast-1.amazonaws.com/unreal-engine-windows-demo:latest $ docker push turn-windows-demo:latest 236960927670.dkr.ecr.ap-northeast-1.amazonaws.com/turn-windows-demo:latest
これで、サービスに必要なDokcerイメージの作成タスクが完了しました。
おわりに
今回の前編では、Pixel Streamingアーキテクチャに必要なサービスのDockerイメージを構築しました。
次回の後編では、EKSの構築に焦点を当てて、Kubernetesに基づくUEアプリケーションを配信する手順を紹介します。
現在、電通総研はweb3領域のグループ横断組織を立ち上げ、Web3およびメタバース領域のR&Dを行っております(カテゴリー「3DCG」の記事はこちら)。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください!
私たちと同じチームで働いてくれる仲間をお待ちしております!
電通総研の採用ページ
参考文献
- PixelStreaming
- Pixel Streaming use cases
- PixelStreaming Hosting and Networking Guide
- NVIDIA device plugin for Kubernetes
- UnrealEngine
- Enabling vendor-specific graphics APIs in Windows containers
- GPU acceleration in Windows containers
- Turn on GPU access with Docker Compose
- Development images vs. runtime images
執筆:@chen.sun、レビュー:@iwasaka.emari
(Shodoで執筆されました)