こんにちは、金融ソリューション事業部の孫です。
前編では、Unreal Editorを含むWindowsコンテナイメージの構築を完了しました。
本記事では、前編で構築したコンテナイメージを利用するAmazon EKS(以下EKS)のWindows Node AMIを作成します。
EKSのドキュメントをよると、AWSはユーザー向けに最適化されたEKS optimized WindowsAMIを提供していることがわかりました。
このEKS optimized WindowsAMIにはEKSでの動作可能なミドルウェアがインストールされていますが、前編で構築したコンテナイメージをサポートするには不足している部分がまだ多く存在します。
そのため、この記事ではUnreal Editor用のカスタムWindows AMIを構築します。
はじめに
本記事では、以下を前提知識として扱います。
- AWSの基本操作
- EKSの使用経験
- コンテナ開発経験
- PowerShellスクリプトの作成経験
今回では、HashiCorp社が提供するPackerツールを使用してAMIを作成します。
また、AWSが提供するEC2 Image Builderを使用してAMIを作成することも可能ですが、以下の理由でPackerを選定しました。
- イメージ作成プロセスを完全に制御したい
- 今後、既存のCI/CDプロセスに統合する予定がある
Packerについて
Packerは、HashiCorpによって開発されたオープンソースのツールで、異なるプラットフォーム向けにサーバーイメージを自動で作成するためのものです。
開発者やシステム管理者がテンプレートファイルを通じてイメージを定義し、AWS、Azure、Google Cloud Platformなど複数のクラウドサービス向けのイメージを作成できます。
Packerの特徴と機能:
- イメージの自動作成: PackerはOSのイメージを自動作成し、手動でのイメージ作成や設定の時間と複雑さを軽減します。
- マルチプラットフォームに対応:Packerは複数のクラウドプラットフォームと仮想化技術をサポートしており、異なる環境間での一貫性を促進します。
- カスタマイズと拡張性: Packerの設定はシンプルなテンプレートファイルを使用して行われ、ユーザーは具体的なニーズに応じてイメージをカスタマイズできます。さらに、Packerは多くのビルダー、事前に設定されたテンプレート、プラグインをサポートしており、機能を拡張できます。
- DevOpsツールチェーンとの統合: Packerは、VagrantやTerraformなどの他のDevOpsツールと容易に統合でき、CI/CDのプロセスをサポートします。
使用手順
- テンプレートの作成:ユーザーはまず、イメージの作成方法を定義したテンプレートファイルを作成する必要があります。
- ビルドの実行:Packerのコマンドラインツールを使用してビルドを実行します。
具体的な作成手順は、次のセクションで詳しく説明します。
実施手順
- Packerツールのインストール
- Packerのテンプレートファイルの作成
- Windowsイメージのビルドとテストの実行
使用する環境およびソフトウェアのバージョンは以下のとおりです:
- OS:Windows 11 Pro 22H2 x64
- RAM: 32GB
- CPU: i5-13600K
- Packer: v1.10.0
- eksctl: 0.176.0
- AWS CLI: version 2
1. Packerツールのインストール
PackerのインストールはTerraformと同様に非常に簡単です。
exeファイルをダウンロードし、環境変数に追加するだけでインストールが完了します。
# PackerのダウンロードURL https://developer.hashicorp.com/packer/install # 環境変数の設定 `Control Panel` ⇒ `System` ⇒ `System Settings` ⇒ `Environment Variables`を順に開き、 `System variables`のボックス内で`PATH`変数をダブルクリックして編集画面を開きます。 変数値の最後に実行ファイルの所在Pathを追加します。例:C:\path
また、Chocolateyを使用してWindowsソフトウェアを管理しているユーザーは、以下のコマンドを実行するだけでインストールが完了します。
$ choco install packer
※他のシステムのユーザーは、Packerのインストールガイドを参考にインストールしてください。
インストール完了後、Packerが正しくインストールされているか確認します:
# 以下のコマンドを実行し、正しくバージョン情報が表示されればインストール成功 $ packer version Packer v1.10.0
2. Packerのテンプレートファイルの作成
構築するWindows AMIに含める必要があるソフトウェアは以下のとおりです。
今回は、Windowsノード上でUnreal Editorを含むコンテナイメージを実行する為、GPU関連のソフトウェアを含めています。
※本記事では取り扱いませんが、EKSを活用してコンテナを運用する手順についてはunrealcontainersを参照してください。
また、Unreal Editorコンテナの起動速度を向上させるために、Windows AMI内に事前にキャッシュしておく必要もあります。
次に、テンプレートファイルの作成に取り掛かります。
Packerテンプレートファイル
Packerテンプレートのフォーマットと文法はTerraformと同様であり、Terraformの使用経験があるユーザーはすぐに使いこなせるでしょう。
Terraformの使用経験がないユーザーでも心配ありません。テンプレートの定義内容は非常に直感的で、何をしようとしているのかが理解しやすいです。
では、テンプレートファイル(eks-worker-node-ami.pkr.hcl)の作成を開始しましょう。
- まず、必要なプラグインおよびそのバージョン情報を定義します。
#ターゲットのクラウドサービスはawsであるため packer { required_plugins { amazon = { version = ">= 1.0.9" source = "github.com/hashicorp/amazon" } } }
- AMI作成のEC2を定義する
この設定は、PackerがEC2インスタンスを構築し、そこからAMIを作成するために使用するパラメータと設定を指定します。
利用されるPackerのSource名はamazon-ebsです。
主なパラメータには、AMI名、ソースAMI、インスタンスタイプ、リージョンなどがあります。
ここでは、AWSが提供する EKS optimized WindowsAMI
をベースイメージとして使用し、その上に必要なシステム環境を構築します。
EKS optimized WindowsAMI
には、EKSに接続するために必要なOS環境が既に構築されているため、この基盤を使用することでプロセスを簡素化できます。
source "amazon-ebs" "eks-worker-node" { ami_name = "eks-windows-worker-node" # AMI name instance_type = "g4dn.2xlarge" # instancetype region = "ap-northeast-1" # region vpc_id = "vpc-xxxxxxxxxxxxxxxxx" # VPC id subnet_id = "subnet-xxxxxxxxxxxxxx" # Subnet id # base AMI source_ami_filter { filters = { name = "Windows_Server-2022-English-Full-EKS_Optimized-1.28-*" root-device-type = "ebs" virtualization-type = "hvm" } most_recent = true owners = ["amazon"] } # Expand the boot disk to 120GB launch_block_device_mappings { device_name = "/dev/sda1" volume_size = 120 volume_type = "gp3" delete_on_termination = true } # Allow S3 access for the VM temporary_iam_instance_profile_policy_document { Version = "2012-10-17" Statement { Action = ["s3:Get*", "s3:List*", "s3:Describe*","s3-object-lambda:Get*","s3-object-lambda:List*"] Effect = "Allow" Resource = ["*"] } } # Use our startup script to enable SSH access user_data_file = "${path.root}/scripts/startup.ps1" # Use SSH for running commands in the VM communicator = "ssh" ssh_username = "Administrator" ssh_timeout = "30m" # Don't automatically stop the instance, since sysprep will perform the shutdown disable_stop_instance = true }
- ビルドプロセスを作成する
ここで、AMI名、参照するソース名(上記で構築したEC2インスタンス)を設定し、最後にPowershellスクリプトを実行して必要な環境を構築します。
ビルドが完了したAMIは、AWSコンソールで確認できます。
build { name = "eks-worker-windows-node" sources = ["source.amazon-ebs.eks-worker-node"] # Run our setup script provisioner "powershell" { script = "${path.root}/scripts/setup_base_eks_optimized_ami.ps1" } # Perform cleanup and shut down the VM provisioner "powershell" { script = "${path.root}/scripts/cleanup.ps1" valid_exit_codes = [0, 2300218] } }
ここまでで、AMI作成のテンプレートファイルの作成が完了しました。
次に、環境を構築するためのPowerShellスクリプトを作成します。
- ① スタートアップスクリプト(startup.ps1)
このスクリプトの目的は、EC2のSSHを構成し、後続のEC2へのログインおよび構築スクリプトの実行に使用することです。
EC2のmetaServiceにはIMDSv1とIMDSv2の2つのバージョンがあります。
ご自身のEC2設定に応じて、対応するバージョンのコードを選択してください。
# startup.ps1 <powershell> # Install the OpenSSH server and set the sshd service to start automatically at system startup Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 Set-Service -Name sshd -StartupType 'Automatic' # Create the OpenSSH configuration directory if it doesn't already exist $sshDir = 'C:\ProgramData\ssh' if ((Test-Path -Path $sshDir) -eq $false) { New-Item -Path $sshDir -ItemType Directory -Force | Out-Null } # Retrieve the SHH public key from the EC2 metadata service $authorisedKeys = "$sshDir\administrators_authorized_keys" # IMDSv2 #$response = Invoke-WebRequest -Uri "http://169.254.169.254/latest/api/token" -Method PUT -Headers @{"X-aws-ec2-metadata-token-ttl-seconds"="21600"} #$token = $response.Content #$metadata = Invoke-WebRequest -Uri "http://169.254.169.254/latest/meta-data/public-keys/0/openssh-key" -Headers @{"X-aws-ec2-metadata-token"=$token} #$metadata.Content | Out-File -FilePath "$authorisedKeys" # Retrieve the SHH public key from the EC2 metadata service # IMDSv1 curl.exe 'http://169.254.169.254/latest/meta-data/public-keys/0/openssh-key' -o "$authorisedKeys" # Set the required ACLs for the authorised keys file icacls.exe "$authorisedKeys" /inheritance:r /grant "Administrators:F" /grant "SYSTEM:F" # Install the Windows feature for containers, which will require a reboot Install-WindowsFeature -Name Containers -IncludeAllSubFeature # Restart the VM Restart-Computer </powershell>
- ② 構築スクリプト(setup.ps1)
まず、使用するバージョン情報およびインストールに使用する一時フォルダーを設定します。
# setup.ps1 # Constants $Containered_Ver = "1.7.11" $eks_optimized_ami_windows_Ver = "1.28.0" $ContainerdPath = "$env:ProgramFiles\containerd" $TempRoot = "C:\TempEKSArtifactDir" $TempPath = "$TempRoot\EKS-Artifacts" # Create each of our directories Write-Host "Create each of our directories" foreach ($dir in @($TempRoot, $TempPath)) { New-Item -Path $dir -ItemType Directory -Force | Out-Null }
バージョン選定に関して注意すべき2点は次の通りです:
Windows Serverのコンテナランタイムの選定については、Microsoft公式がcontainerdの使用を推奨しています。Dockerは現在サポートされておらず、containerdのみがGPUサポートなどのWindows Containerの高度な機能をサポートしています。
- KubernetesでWindows ContainerのGPUサポートプラグインを使用するためには、containerdのバージョンが1.7.0以上である必要があります。
- 参考:https://github.com/TensorWorks/DirectX-Device-Plugins?tab=readme-ov-file
EKS optimized WindowsAMI
にキャッシュイメージを追加するためには、EKS optimized WindowsAMI
の構築Image Builder Componentを使用する必要があります。具体的なバージョンはKubernetesのバージョンに依存しますが、今回は1.28.0バージョンを使用します。
次に、必要なソフトウェアを順にインストールします。
# Install the NVIDIA GPU drivers Write-Host "Install the NVIDIA GPU drivers" $driverBucket = 'ec2-windows-nvidia-drivers' $driver = Get-S3Object -BucketName $driverBucket -KeyPrefix 'latest' -Region 'us-east-1' | Where-Object {$_.Key.Contains('server2022')} Copy-S3Object -BucketName $driverBucket -Key $driver.Key -LocalFile "$TempRoot\driver.exe" -Region 'us-east-1' Start-Process -FilePath "$TempRoot\driver.exe" -ArgumentList @('-s', '-noreboot') -NoNewWindow -Wait
- Vulkan runtime library
# install 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\
- DirectX shader compiler
# 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\ # Clean up any temp files generated during prerequisite installation Remove-Item -LiteralPath "$env:TEMP" -Recurse -Force New-Item -Type directory -Path "$env:TEMP"
そして、EKS optimized WindowsAMI
に含まれているContainerdのバージョンを更新します(1.6.18⇒1.7.1)
# TEMPORARY UNTIL EKS ADDS SUPPORT FOR CONTAINERD v1.7.11: # Download and extract the containerd 1.711 release build Write-Host "Download and extract the containerd 1.7.11 release build" $webClient = New-Object System.Net.WebClient $containerdTarball = "$TempPath\containerd-$Containered_Ver.tar.gz" $containerdFiles = "$TempPath\containerd-$Containered_Ver" $webClient.DownloadFile("https://github.com/containerd/containerd/releases/download/v$Containered_Ver/containerd-$Containered_Ver-windows-amd64.tar.gz", $containerdTarball) New-Item -Path "$containerdFiles" -ItemType Directory -Force | Out-Null tar.exe -xvzf "$containerdTarball" -C "$containerdFiles" # Stop containerd service Stop-Service -Name "containerd" -Force # Upgrade container version from 1.6.18 to 1.7.11 Write-Host "Upgrade container version from 1.6.18 to 1.7.11" Move-Item -Path "$containerdFiles\bin\containerd.exe" -Destination "$ContainerdPath\containerd.exe" -Force Move-Item -Path "$containerdFiles\bin\containerd-shim-runhcs-v1.exe" -Destination "$ContainerdPath\containerd-shim-runhcs-v1.exe" -Force Move-Item -Path "$containerdFiles\bin\ctr.exe" -Destination "$ContainerdPath\ctr.exe" -Force # restart containerd service Start-Service -Name containerd # Clean up the containerd intermediate files Write-Host "Clean up the containerd intermediate files" Remove-Item -Path "$containerdFiles" -Recurse -Force Remove-Item -Path "$containerdTarball" -Force
最後に、EKS optimized WindowsAMI
のImage Builderコンポーネントを使用して、前編で作成したコンテナイメージをキャッシュイメージとして追加します。
# Download the EKS artifacts archive Write-Host "Download the EKS artifacts archive" $webClient.DownloadFile("https://ec2imagebuilder-managed-resources-us-east-1-prod.s3.amazonaws.com/components/eks-optimized-ami-windows/$eks_optimized_ami_windows_Ver/EKS-Artifacts.zip", "C:\EKS-Artifacts.zip") # Extract the EKS artifacts archive Write-Host "Extract the EKS artifacts archive" Expand-Archive -Path "C:\EKS-Artifacts.zip" -DestinationPath $TempRoot Remove-Item -Path "C:\EKS-Artifacts.zip" -Force # Add the unreal-engine-dev-windows-5.1:latest to the list of images to pre-pull Write-Host "Add the unreal-engine-dev-windows-5.1:latest image" $baseLayersFile = "$TempPath\eks.baselayers.config" $baseLayers = Get-Content -Path $baseLayersFile -Raw | ConvertFrom-Json $baseLayers.ue += "unreal-engine-dev-windows-5.1:latest" $patchedJson = ConvertTo-Json -Depth 100 -InputObject $baseLayers Set-Content -Path $baseLayersFile -Value $patchedJson -NoNewline # Get added new BaseLayers Write-Host "Perform EKS worker node setup" Push-Location $TempPath .\Get-EKSBaseLayers.ps1 -ConfigFile eks.baselayers.config -ContainerRuntime containerd Pop-Location # Perform cleanup Write-Host "Perform cleanup" Remove-Item -Path "$TempRoot" -Recurse -Force
- ③ クリーンスクリプト(cleanup.ps1)
このスクリプトの目的は、スタートアップスクリプト(startup.ps1)で設定したSSH環境を削除し、sysprep(システム準備)を開始することです。
# cleanup.ps1 # Perform cleanup Set-Service -Name sshd -StartupType 'Manual' Remove-Item -Path 'C:\ProgramData\ssh\administrators_authorized_keys' -Force # Remove the file for this script, since Packer won't have a chance to perform its own cleanup Remove-Item -Path $PSCommandPath -Force # Perform sysprep and shut down the VM # Need delete edge account for sysprep & "$Env:ProgramFiles\Amazon\EC2Launch\EC2Launch.exe" sysprep --shutdown=true
3. Windowsイメージのビルドとテスト
テンプレートファイルに対してPackerコマンドを実行し、Windows AMIのビルドを開始します。
- ビルドコマンド
$ packer build .\eks-worker-node-ami.pkr.hcl ...出力ログ... ==> Wait completed after 52 minutes 42 seconds ==> Builds finished.
- ビルド完了の確認
AWS Console ⇒ EC2 ⇒ イメージ ⇒ AMI
で作成されたAMIを確認できます。
- Windows AMIのテスト
AMIをテストするために、eksctlを使用してEKSを作成します。
以下のyamlファイルでは、2つのnodegroupを含むKubernetesクラスターを定義しています。
# test_windows_node_eksctl.yaml apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: name: test-windows-node-cluster region: ap-northeast-1 version: '1.28' nodeGroups: - name: windows-ng instanceType: g4dn.2xlarge ami: [作成したAMI id, ami-xxxxx] amiFamily: WindowsServer2022FullContainer volumeSize: 120 minSize: 1 maxSize: 1 - name: linux-ng amiFamily: AmazonLinux2 minSize: 1 maxSize: 1
EKSの作成を開始します:
$ eksctl create cluster -f test_windows_node_eksctl.yaml
完了確認
eksctlのコマンド実行により、AWSはCloudFormationを呼び出してリソースを作成します。
CloudFormationでの確認
EKS管理コンソールでの確認
クラスターの確認
ノードグループの確認
Session Managerを使用してWindowsノードにログインし、ソフトウェアのインストールおよびイメージのキャッシュを確認
終わりに
本記事を読むことで、UEアプリ用のカスタムWindows Node AMIの作成方法について理解していただけたと思います。
もちろん、この記事で作成したスクリプトはUnreal Editorの動作要件に基づいていますが、ビルドテンプレートファイルは共通のものです。
今後、読者はAWS提供の EKS optimized WindowsAMI
がご自身のプロジェクト要件を満たさないと感じた場合、この記事を参考にしてスクリプトを置き換えて独自のWindows AMIを作成できます。
この記事では、Windows ContainerでのGPU利用要件について言及しました。これに興味を持たれる方もいるでしょう。
次回の記事では、今回作成したWindowsノードを使用して、Unreal EngineのPixel Streamingプロジェクトを実現する方法について説明します。
どうぞお楽しみに。
現在、電通総研はweb3領域のグループ横断組織を立ち上げ、Web3およびメタバース領域のR&Dを行っております(カテゴリー「3DCG」の記事はこちら)。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください!
私たちと同じチームで働いてくれる仲間をお待ちしております!
電通総研の採用ページ
参考文献
- Amazon EKS
- EKS optimized WindowsAMI
- Packer
- amazon-ebs
- Windows install-the-container-runtime
- DirectX-Device-Plugins
執筆:@chen.sun、レビュー:寺山 輝 (@terayama.akira)
(Shodoで執筆されました)