5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

クラウドにすべて置いてきた 〜iPadからゲーミングPCを召喚するまで〜

Posted at

クラウドにすべて置いてきた

〜iPadからゲーミングPCを召喚するまで〜

突然ですがわたし、ガジェットオタクなんです。
同じ趣味嗜好の方には共感していただけると思うのですが、

パソコンやタブレットって勝手にいつの間にか増殖しますよね!

気がついたら・・・

  • ゲーミングWindows PC
  • モバイルWindows PC
  • Mac mini
  • iPad×5台 など

この超カオスな「PC、タブレット多すぎ問題」をなんとかしたい。
そこから今回の話は始まりました。

わたしはこのカオスな部屋の風景に絶望し、徐々にミニマリストに憧れを持ち始めました。

そんなわたしの思い描く究極のガジェットとは

オールインワン(すべての機能が1台にまとまったもの)

なんです。

その究極の1台を探すため、検証を進める日々、この検証過程で物が増えてしまうのです。

いくら探しても究極の1台は存在しない・・・そこで発想を逆転させます。

自分で作っちゃえ!

ハードから作る技術なんてないわたしでも仮想的に作っちゃえば可能なんです。
そう、クラウドならね!

ゲーム用のPCも、モバイル用のWindows環境もぜんぶクラウドに置いてしまいましょう。

そして手元にはiPad1台だけを残して
遊びたいときも、作業したいときも、iPadからクラウドのマシンを呼び出して使う。

そんな環境を、AWS上に「趣味&検証目的」で作ってみました。
この記事は、その過程をストーリー仕立て+ゆるめの技術解説でまとめたものです。

※あくまで個人利用の検証構成です。
実際に利用する場合はAWS・Microsoft・各ゲームの利用規約を必ずご自身で確認してください。


やりたいことの全体像

やりたいことをざっくり言うと、こんな構成です。

  • クラウド上に「ゲームもできるWindowsマシン」を用意する
    • AWS EC2(仮想マシン)
    • OS は Windows Server 2022(安定性を評価して採用)
    • GPU付きインスタンスでゲームも動かす
  • iPadからゲームストリーミングで遊ぶ
    • サーバー側:Sunshine
    • iPad側:Moonlight
  • 接続できるのは自分だけに制限
  • 同じマシンをリモートデスクトップ(RDP)でも使う
  • 使うときだけ起動して、終わったら削除
    → ランニングコストを最小化
  • iPadのショートカットから「どのインスタンスを起動するか」を選べる
    • RDP用:t3.xlarge
    • ゲーム(フルHDくらい):g4dn.2xlarge
    • ゲーム(フルHDより高解像度):g5.xlarge
  • スポットインスタンスを使用(AWSの余剰リソースを激安で使わせてもらう)
  • 将来的には、起動ログと料金をDynamoDBに記録して可視化したい(現時点では未実装)
  • ランニングコストは可能な限り削減する(遊び終わったら毎回不要なリソースは消す)

ざっくり図にするとこんな感じです。
EC2 AMI (1).png


Step 1: まずはクラウドに「ゲームPCのベース」を作る

Windows Server 2022 を選んだ理由

クラウド側のOSには Windows Server 2022 を選びました。

  • AWSが公式に提供していて扱いやすい
  • ライセンス込みの料金なので、細かいライセンス計算を自分でしなくていい
  • GPU付きインスタンスにもそのまま乗せ替えしやすい
  • 実際に使ってみて、安定して動作してくれる印象

というあたりを評価しています。

補足:EC2とは
AWSが提供する「クラウド上の仮想マシン」です。
ローカルPCの代わりに、クラウドの中に Windows や Linux を立てて使えます。

ここで、

  • Windowsの初期設定
  • 日本語環境のセットアップ
  • リモートデスクトップの有効化

などを行い、ゲームPCのベースを作っていきます。

image.png


Step 2: Sunshine と Moonlight と「同一ネットワーク」の壁

名称未設定のデザイン.png

ゲームのストリーミングにはこちらを使いました。

  • サーバー側(EC2):Sunshine
    → ゲーム画面をネットワーク越しに配信するソフト
  • クライアント側(iPad):Moonlight
    → Sunshineに接続して映像と操作を送受信するアプリ

Steam(PCゲーム配信プラットフォーム) と DDNS クライアント(後ほど説明)もサーバーに入れておきます。

「同一ネットワーク必須」の罠

最初にぶつかった問題がこれです。

iPad の Moonlight から、EC2 上の Sunshine にペアリングできない。

調べていくと、初回ペアリング時は“同じネットワーク”にいる必要があるらしい
でも現実は、

  • iPad:自宅(福岡)のWi-Fi(家庭内LAN)
  • EC2:AWSのデータセンター(関東のどこか)

明らかに別世界です。

一瞬、

「じゃあ AWS のデータセンターまで LAN ケーブル引っ張るか……?」

と考えましたが、(冗談ですが)
さすがにそれは無理なので、別の方法を考えます。

VPNで「同じネットワークっぽく」見せる

採った手段は、

  • 自宅にVPN用の入口を作る
  • EC2をそのVPNに参加させる

という方法です。

補足:VPNとは?
VPNは、自宅とデータセンターを暗号化された“トンネル”でつなぎ、同じネットワークのように扱える仕組みです。

こうすることで、論理的には

  • iPad
  • EC2

が同じネットワーク上に見えるようになり、
Moonlightからの初回ペアリングが通るようになりました。


DDNSとは何か?なぜ必要なのか

ここで出てきた DDNS について、少しだけ説明しておきます。

普通にクラウドPCを立てると何が起きるか

AWSでEC2を起動すると、基本的には

  • 起動のたびにグローバルIPアドレスが変わる

という挙動になります。

もちろん、Elastic IP という「固定IP」を使う方法もありますが、

  • Elastic IP は 使っていない時に料金が発生する

ので、
「ランニングコストは可能な限り削減」という今回のポリシーに反するので使いません。

DDNSの仕組み

そこで使うのが DDNS(Dynamic DNS) です。

ざっくりいうと、

mypc.example.com という名前に、
今この瞬間の起動したクラウドPCのグローバルIPアドレスをひも付けてくれるサービス」

です。

  • クラウドPCが起動したタイミングで
    • 現在のグローバルIPアドレスをDDNSサービスに通知
  • クライアント(iPadなど)は
    • いつも mypc.example.com に接続するだけでOK
    • 中身のIPアドレスは起動するたびに自動更新される

という形になります。

IPアドレスは毎回変わっても、名前(ドメイン名)は変わらないので、
繋ぐ際にグローバルIPアドレスを調べる必要がなくなり便利です。


Step 3: AMI化して「ゲームPCのイメージ」を保存する

ある程度環境が整ったら、
一度スナップショットを取り、AMI(Amazon Machine Image) を作成します。

AMIとは
「この時点のサーバー状態をテンプレート化したもの」です。
新しいEC2インスタンスをAMIから起動すると、
そのときインストールされていたソフトや設定を引き継いで立ち上がってくれます。

この時点の構成は、

  • Windows Server 2022(日本語環境)
  • Steam インストール済み
  • Sunshine インストール済み
  • DDNSクライアント設定済み

という、「ゲームPCのイメージ」です。

このAMIを元に、
後でGPU付きインスタンス(g4dn, g5)を起動して使っていきます。
IMG_0004.jpg


Step 4: iPadショートカットからゲームPCを召喚する

ここからは、「召喚ボタン」を作成していきます。

ショートカットでインスタンスタイプを選ぶ

iPad側では ショートカットアプリ を使っています。

ショートカットの実行時に

  • どの用途で使うか?を選択
    • RDP用:t3.xlarge
    • ゲーム(フルHDくらい):g4dn.2xlarge
    • ゲーム(フルHDより高解像度):g5.xlarge
      ※GPU付きインスタンスの起動は初め制限がかかっているのでAWSに申請が必要です

という形でインスタンスタイプを選べるようにし、
その値をAPIのパラメータとして送っています。

スポットインスタンスを使っているので、

  • g4dn.2xlargeg5.xlarge のインスタンスタイプは昼間、よく利用されるようで「キャパシティ不足(空きリソースなし)」で起動できない・・・

ということもあります。※夜は比較的空いています

↓iPadショートカット作成画面
IMG_0005.jpg

↓実行画面
IMG_0001.jpg

起動フローの裏側

起動の裏側はこんな流れです。

  1. iPadのショートカットから、API Gateway のURLを叩く
    → どのインスタンスを使いたいかパラメータで渡す
  2. API Gateway が Lambda を呼び出す
  3. Lambda は
    • 指定されたインスタンスタイプ
    • 最新の AMI ID
    • 呼び出し元のグローバルIPアドレス
      を元に、CloudFormation用のパラメータを組み立てる
  4. CloudFormation テンプレート(S3に置いてある)を使ってEC2を起動
  5. 数分すると、Moonlightから接続できる状態になる

セキュリティグループでは

  • Sunshine/Moonlight用のポート
  • RDP用のポート(3389)

を開けつつ、接続元IPは「ショートカットを実行した時点のわたしのグローバルIPだけ」を許可するようにしています。

↓CloudFormationテンプレートはこんな感じ

moonlight-ec2.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: "Moonlight EC2 instance with Elastic IP"

Parameters:
  AmiId:
    Type: AWS::EC2::Image::Id
    Description: 使用するWindows AMI ID

  InstanceType:
    Type: String
    Default: t3.xlarge
    AllowedValues:
      - g4dn.2xlarge  # GPUを使ったゲーム用途向け
      - g5.xlarge     # GPUを使ったゲーム用途向け(高スペック)
      - t3.xlarge     # 軽量なRDP用
    Description: 使用目的に応じてインスタンスタイプを選択

  ClientIp:
    Type: String
    Description: 接続元のグローバルIPアドレス(CIDR形式、例:xxx.xxx.xxx.xxx/32)

Resources:

  # 必要なポートのみを接続元IPに限定して開放するセキュリティグループ
  InstanceSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for Moonlight EC2
      VpcId: !Ref AWS::NoValue 
      SecurityGroupIngress:
        # RDPポート
        - IpProtocol: tcp
          FromPort: 3389
          ToPort: 3389
          CidrIp: !Ref ClientIp
        # MoonlightのTCPポート
        - IpProtocol: tcp
          FromPort: 47984
          ToPort: 47984
          CidrIp: !Ref ClientIp
        - IpProtocol: tcp
          FromPort: 47989
          ToPort: 47989
          CidrIp: !Ref ClientIp
        - IpProtocol: tcp
          FromPort: 48010
          ToPort: 48010
          CidrIp: !Ref ClientIp
        # MoonlightのUDPポート
        - IpProtocol: udp
          FromPort: 47998
          ToPort: 48000
          CidrIp: !Ref ClientIp
        - IpProtocol: udp
          FromPort: 48002
          ToPort: 48002
          CidrIp: !Ref ClientIp
        - IpProtocol: udp
          FromPort: 48010
          ToPort: 48010
          CidrIp: !Ref ClientIp

  # 固定グローバルIP用 Elastic IP
  ElasticIP:
    Type: AWS::EC2::EIP
    Properties:
      Domain: vpc

  # EC2インスタンス用のLaunchTemplate(スポットインスタンス設定)
  MoonlightLaunchTemplate:
    Type: AWS::EC2::LaunchTemplate
    Properties:
      LaunchTemplateName: MoonlightLaunchTemplate
      LaunchTemplateData:
        ImageId: !Ref AmiId
        InstanceType: !Ref InstanceType
        KeyName: xxxxx  # EC2キーペア
        SecurityGroupIds:
          - !GetAtt InstanceSecurityGroup.GroupId
        IamInstanceProfile:
          Name: MoonlightInstanceProfile
        BlockDeviceMappings:
          # Cドライブ(OS用)
          - DeviceName: /dev/sda1
            Ebs:
              VolumeSize: 100
              VolumeType: gp3
              DeleteOnTermination: true
              VolumeInitializationRate: 100
        InstanceMarketOptions:
          MarketType: spot
          SpotOptions:
            SpotInstanceType: one-time
            InstanceInterruptionBehavior: terminate

  # EC2インスタンス(LaunchTemplateを使用)
  MoonlightInstance:
    Type: AWS::EC2::Instance
    Properties:
      LaunchTemplate:
        LaunchTemplateId: !Ref MoonlightLaunchTemplate
        Version: !GetAtt MoonlightLaunchTemplate.LatestVersionNumber
      Tags:
        - Key: Name
          Value: Moonlight-Instance

  # Elastic IP をインスタンスに関連付け
  EipAssociation:
    Type: AWS::EC2::EIPAssociation
    Properties:
      InstanceId: !Ref MoonlightInstance
      AllocationId: !GetAtt ElasticIP.AllocationId

↓起動完了
IMG_0003.jpg


↓以降は構築の際に発生した色々な問題です。

Step 5: 停止時にスナップショット増殖にハマる

起動はうまくいきました。
次は「どうやって止めるか」です。

当初の停止フロー

最初は、停止時に次のようなことをしていました。

  1. Cドライブのスナップショットを作成
  2. Dドライブ(ゲームデータ)のスナップショットを作成
  3. CloudFormationスタックを削除(EC2も含めて全部削除)

これも Lambda を使って実行していたのですが…。

EBSのスナップショットは、変更量が多いと作成に結構時間がかかります。

  • Lambdaがタイムアウトし、「失敗したからリトライするね!」とLambdaのリトライ設定により同じ処理を試みる

結果として、

  • C・Dドライブのスナップショットが毎回無駄に3つずつ増える

という、お財布に優しくない現象が発生しました。

わたしのお財布を攻撃してきたのは、敵キャラではなく Lambda でした。

停止する際もiPadのショートカットから実行出来ます。
IMG_0006.jpg


Step 6: 性能調査 〜CPU100%問題とGPUが働かない問題〜

起動・停止の仕組みができたので、次は性能のチューニングです。

g4dn.xlarge の CPU100%問題(→ g4dn.2xlarge へ引き上げ)

フルHD向けゲーム用として使っている g4dn.2xlarge は、当初 g4dn.xlarge を使っていました。
しかし、

  • ゲーム中にCPUが100%張り付き
  • GPU側に余力があるのに、CPUがボトルネックになる

という状況がありました。

なのでvCPUが2倍になる g4dn.2xlarge を使用すると良い感じの性能を発揮するようになりました。

g5.xlarge でGPUが使われない問題

一方、より解像度の高いゲーム用に g5.xlarge を使ったところ、

  • ゲーム中でもGPU使用率がまったく上がらない
  • Windows標準のディスプレイドライバが良くない動きをしている

という現象も起きました。

この問題は標準のディスプレイドライバを無効化すると、ようやくGPUがきちんと使われるようになりました。


Step 7: Dドライブのゲーム読み込みが異常に遅い

ディスク周りでも、こんな現象がありました。

  • ゲームをDドライブに置くと、初回起動だけロードが異常に遅い
  • 一度起動すると、同じインスタンスでは次回以降スムーズ
  • しかしスナップショットから新しく立てたインスタンスではまた遅い
  • ゲームによってはほとんど気にならないものもある

EBSの「ボリューム初期化レート」の調整

原因の一つとして考えたのが、EBSボリュームの初期化です。

Amazon EBS には「ボリューム初期化レート」を制御する機能があり、
これを 100 MiB/s に設定してみたところ、

  • 初回起動時のロード時間が体感でかなりマシになった
  • スナップショットから復元した直後でも、そこまで待たされない

という改善が見られました。

クラウドのディスクは、

「最初に一度“ならす”作業(初期化)」

することでアクセス速度を上げることができるようです。


Step 8: iPad がカクつく AWDL問題

クライアント側にも問題が発生しました。

Wi-Fiだとカクカク、有線だと快適

iPad から接続していると、

  • Wi-Fi接続時には、1秒ごとに「プチッ」とコマ落ちするようなカクつき
  • 有線LANやiPhoneの有線テザリング経由だと、かなり快適

という現象が起きました。

調べていくと、iOSやiPadOSの AWDL(AirDropなどに使われる仕組み)が、

  • Wi-Fiを細かく別用途に使う
  • そのタイミングでリアルタイムの映像伝送が影響を受ける

といった話が見つかり、
今回の現象ともだいぶ符号が合っていました。

iCloudサインアウトで改善するが…

一部の情報では、

  • iCloudからサインアウトすると改善する

という話もあり、実際試してみると確かにカクつきは無くなりました。

ただ、

  • 普段の生活で iCloud をバリバリ使っている身としては、
    サインアウトしたまま運用するのは現実的ではない

ということで、

  • 本気で遊びたいとき:有線テザリング or 物理的な有線接続
  • Wi-Fiで軽く遊びたいとき:たまにカクつくのは仕様として割り切る

くらいに考えるようにしました。


問題を乗り越えて遊べるようになりました

そしてiPadからクラウドPCを起動してBluetoothコントローラーを接続したら準備完了!
IMG_0069.jpg
IMG_0065.jpg
IMG_0068.jpg

料金について

最後に料金ですが、スポットインスタンスの価格は需給のバランスによって変わります。
image.png

ここ3ヶ月の平均だと1時間あたりは以下の通りです。

t3.xlarge:$0.1412 約 21.89 円
g4dn.2xlarge:$0.6709 約 104.00 円
g5.xlarge:$0.6192 約 95.98 円
※1ドル155円計算

実は100%CPU張り付き問題の際に g4dn.xlargeg4dn.2xlarge に変更したことで
g5.xlarge の価格を超えてしまいました。

GPU(グラフィック)性能は g5.xlarge の方が高いため、ゲーム目的だと g4dn.2xlarge を使う意味は無くなってしまいました...

また、その他にAMI(スナップショット)の保管料が必要になります。
↓100GBのスナップショットを1ヶ月保管した場合は約5ドル(約800円)必要になります。
image.png

利用規約まわりについて、ゆるく触れておく

最後に、クラウドゲーミングとリモートデスクトップの利用規約まわりについて軽く触れておきます。

この構成はあくまで、

  • 自分のAWSアカウント上で
  • 自分専用インスタンスを
  • 自分だけが利用する

という前提で作っています。

公開情報を眺める限り、このレベルの個人利用が
大きな問題になるとは考えにくい、という認識を持っています。

ただし、もし将来的に

  • 友人・第三者にクラウドPCを貸し出す
  • お金を取って「クラウドゲームPCサービス」を始める

といったサービス提供の形に進める場合は、

  • Windows Server の RDS ライセンス
  • 各ゲームタイトルの利用許諾(クラウド利用/レンタル禁止条項など)

など、しっかり確認すべきポイントが一気に増えます。

なのでこの記事はあくまで、
「クラウド好きが個人でやってみた、趣味の検証記録」
として読んでいただければと思います。


まとめ 〜クラウドにすべて置いてきたもの〜

こうして、自宅にあったPC達の役目はクラウドPCによってまかなえるようになりました。
クラウドにすべて置くことが出来ました。
(「すべて」は言いすぎでしたね、「ほとんど」に訂正してお詫び申し上げます)

そして残ったのは、

  • iPad
  • キーボード
  • コントローラー
  • そして月初に届くAWSの請求書

くらいです。

それにしても問題がいくつも出て解決するのは大変でしたが、
どこからでも iPad ひとつで「自分専用のクラウドゲームPC」を呼び出せる体験は、
かなり快適でした。

今後は、

  • スナップショット運用の見直し
  • さらにコストを抑える工夫
  • DynamoDBでのログ&料金可視化

などを続けつつ、
「クラウドにすべてほとんど置いてきた」生活をもう少し楽しんでみようと思います。

IMG_0047.jpg

と、この記事を書いている裏で MacBookPro M5 モデルを購入してしまい、もうすぐ届くことは内緒話です🤫

おしまい。

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?