クラウドゲーム環境の利便性向上の取り組み

# 0.  概要
ゲーミングPCは高くて買えないけど、Apexとかやってみたいなと思っていました。
AWSの勉強をして、AWS上にゲーム環境構築したら、低スペックPCでも遊べるのでは？と考えました。検索すると、次の記事が出てきました。

https://zenn.dev/unotovive/articles/5b0ae91c05f4f5

この記事を参考にして、AWS上にApex等のゲームができる環境を構築しました。
本記事では、上記記事に書かれていない内容の補足を行います。

### 目次
1. 準備段階でやるべきこと
EC2パスワードの変更
2. ゲーム終了時にやるべきこと
2.1 AMI作成
2.2 S3へAMI保存
3. ゲームを再開するときにすべきこと
3.1 ①S3からAMIを復元
3.2 ②EC2の起動と③IAMロールのアタッチ
(A) マネジメントコンソールの利用　（手動）
(B)　起動テンプレートの利用　（半自動）
(C)　CloudFormationの利用　（ほぼ自動）


# 1.  準備段階でやるべきこと
### EC2のパスワードの変更
デフォルトのEC2のパスワードは覚えにくく、管理が大変です。
EC2のPowershellに次のコマンドを打ち込み、パスワードを変更しましょう。
~~~
net user Administrator "new_password"​
~~~
Administrator　はEC2のデフォルトユーザ名です。
new_password　に設定したい新規パスワード名を入力してください。

# 2. ゲーム終了時にやるべきこと

### 新規AMI作成時
次回以降の起動が楽になるよう、初回はAMIを作成しましょう。

①EC2の停止
②AMIの作成
③EC2の終了
④AMIをS3に移動
⑤AMIの削除

### 新規AMIを作成しないとき
①EC2の終了
②AMIの削除

### EC2自動停止
私はまだ行っていませんが、消し忘れを防ぎたいならこんなこともできます。

https://qiita.com/Shoma0210/items/890965f1bc58c172c803

## 2.1  AMI作成
https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/WindowsGuide/Creating_EBSbacked_WinAMI.html


## 2.2  S3へAMI保存
AMIそのまま保存していたら、EBSのストレージ費用が高つきます。
ストレージ費用を抑えたい場合は、
AMIをS3（頻繁にアクセスしないなら安いONEZONE_IA等）に移動して、
AMIとEBSボリュームのスナップショットを削除しましょう。

https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/WindowsGuide/ami-store-restore.html

AMIをS3に保存する際は、AWS CLIを利用します。
### CLIの導入
https://udemy.benesse.co.jp/development/system/aws-cli.html

この記事ではregion name やoutput formatがデフォルトになっていますが、
東京リージョンを指定し、json形式のアウトプットにしたい場合は
以下コマンドのように設定してください。
~~~
aws configure
AWS Access Key ID [****************TGR3]: AccessKeyID
AWS Secret Access Key [****************IkGR]: SecretAccessKey
Default region name [Tokyo]: ap-northeast-1
Default output format [jason]: json
~~~



###  S3へAMIを保存
AWS CLI 導入後、コマンドプロンプトに以下コマンドを打ち込みます。
~~~
aws ec2 create-store-image-task ^
    --image-id ami-1234567890abcdef0 ^
    --bucket myamibucket
~~~

注意：windows のコマンドプロンプトを使用する場合、
長いコマンドの改行には ^ を利用します。
本記事ではAWS CLI の改行はすべて ^ とします。
linuxユーザは適宜 \ などに読み替えてください。

### AMI保存タスクの進捗確認
~~~
aws ec2 describe-store-image-tasks
# 保存中の出力
{
    "StoreImageTaskResults": [
        {
            "AmiId": "ami-0123",
            "TaskStartTime": "2023-01-01T00:00:00.00000+00:00",
            "Bucket": "aminame",
            "S3objectKey": "ami-0123keyname.bin",
            "ProgressPercentage": 30,
            "StoreTaskState": "InProgress",
            "StoreTaskFailureReason": ""
        }
    ]
}
#完了時の出力
{
    "StoreImageTaskResults": [
        {
            "AmiId": "ami-0123",
            "TaskStartTime": "2023-01-01T00:00:00.00000+00:00",
            "Bucket": "aminame",
            "S3objectKey": "ami-0123keyname.bin",
            "ProgressPercentage": 100,
            "StoreTaskState": "Completed",
            "StoreTaskFailureReason": ""
        }
    ]
}
~~~



### S3のストレージクラス変更
~~~
aws s3 cp s3://your-backet-name/ s3://your-backet-name/ ^
--recursive --storage-class ONEZONE_IA
~~~


# 3. ゲームを再開するときにすべきこと
①S3に保存したAMIの復元
②EC2を起動
③IAMロールのアタッチ（Eventbridge+Lambdaを用いて自動化）
④parasecからアクセス
⑤ゲームの立ち上げ


## 3.1 ①S3からAMIを復元
~~~
aws ec2 create-restore-image-task \
    --object-key path1/path2/ami-1234567890abcdef0.bin \
    --bucket myamibucket \
    --name "New AMI Name"
~~~

## 3.2 ②EC2の起動と③IAMロールのアタッチ
マネジメントコンソールで②や③を行うのは面倒です。自動化しましょう。

## (A) マネジメントコンソールの利用　（手動）
難易度　★★（設定が大変）　　起動時の快適さ　★　（様々な項目の入力が面倒）

最初に引用した以下記事の、
(2)EC2上にインスタンスを起動　のセクションの、 
2. AMIの選択　のステップで、
自分で保存したAMIを選択します。
セキュリティグループは以前作成したものを、リストの中から選択可能です。

https://zenn.dev/unotovive/articles/5b0ae91c05f4f5#2.ec2%E4%B8%8A%E3%81%AB%E3%82%A4%E3%83%B3%E3%82%B9%E3%82%BF%E3%83%B3%E3%82%B9%E3%82%92%E8%B5%B7%E5%8B%95


## (B)　起動テンプレートの利用　（半自動）
難易度　★　　起動時の快適さ　★★★
ゲームを再開するときにすべきことの、②と③を自動化できます。
### 方法
手動でEC2を起動し、IAMロールのアタッチも終了した後、
マネジメントコンソールで以下のように起動テンプレートを作成します。

EC2→インスタンス→アクション​
→イメージとテンプレート→インスタンスからテンプレートを作成​

![スクリーンショット (14).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3482344/96d44e53-219b-0a75-c561-909e4350fb3d.png)

作成したテンプレートを利用すると、次回EC2作成時にAMIやキーペア、セキュリティグループが自動で入力されます。

テンプレートをもとに、
AMIやインスタンスタイプなどを手動変更することも可能です。


## (C)　CloudFormationの利用　（ほぼ自動）
難易度　★★★　　起動時の快適さ　★★（AMI　IDの手入力が必要）
以下記事を参考に、CloudFormationテンプレートを作成しました。
CloudFormationでのリソースの作成方法もこの記事で解説されています。
エラーが出たら、ログを確認してテンプレートを修正しましょう。

https://qiita.com/tyoshitake/items/c5176c0ef4de8d7cf5d8

### 作成したテンプレート
~~~template.yaml
#テンプレート形式のバージョン
AWSTemplateFormatVersion: "2010-09-09"

#テンプレートに関する説明
Description: Provision EC2


#入力パラメータ
Parameters: 

  #EC2へのアクセスのキーペア名を入力パラメータとして設定
  #パラメータの論理名
  KeyName:
    #パラメータの説明
    Description: The EC2 Key Pair to allow SSH access to the instance
    #パラメータのタイプ
    Type: "AWS::EC2::KeyPair::KeyName"

  #AMIのIDを入力パラメータとして設定
  MyAMIID:
    Description: my AMI ID
    Type: AWS::EC2::Image::Id

  # EC2にRDPアクセス可能なIPをパラメータとして設定
  MyIP:
    Description: IP address allowed to access EC2
    Type: String


# 構築するリソースの記述
Resources:

  #IAMロールの作成
  #リソースの論理名
  MyIamRole:
    #リソースのタイプ
    Type: "AWS::IAM::Role"
    #リソースのプロパティ
    Properties:
      # Iam信頼ポリシー。どのエンティティがこのロールを引き受けるかを記載。
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement: 
          - 
            Effect: "Allow"
            Principal: 
              #EC2がこのロールを引き受けられる。
              Service: 
                - "ec2.amazonaws.com"
            Action: 
              - "sts:AssumeRole"
      #このロールに付与するマネジメントポリシーのarn
      ManagedPolicyArns: 
        - arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
        - arn:aws:iam::aws:policy/AWSBackupServiceRolePolicyForS3Restore
        - arn:aws:iam::aws:policy/AWSBackupServiceRolePolicyForS3Backup
      Path: "/"
     #ロールの名称
      RoleName: MyIamRole

  #Iamインスタンスプロフィールの作成
  MyIamInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      InstanceProfileName: "MyInstanceProfile"
      Path: "/"
      #どのIamロールを使用するか記載。
      Roles:
      - !Ref MyIamRole
      #テンプレートの組み込み関数Refを用いることで、
      #リソースの論理名からロール名を取得

  #VPCの作成
  VPC: 
    Type: AWS::EC2::VPC
    Properties:
      # VPCのCIDR（プライベートIPアドレス）
      CidrBlock: 10.1.0.0/16
      # Tags属性で、Nameタグを作成
      Tags:
        - Key: Name
          Value: vpc-cf

  #インターネットゲートウェイ(IGW)の作成
  IGW:
    Type: AWS::EC2::InternetGateway
    Properties:
      # Tags属性で、Nameタグを作成
      Tags:
        - Key: Name
          Value: igw-cf

  #IGWをVPCにアタッチ
  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      #VpcIDの指定
      VpcId: !Ref VPC
      #IGW IDの指定
      InternetGatewayId: !Ref IGW
      #テンプレートの組み込み関数Refを用いることで、
      #リソースの論理名からIGWのIDを取得

 #VPC内に（パブリック）サブネットを作成
  PubSub:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: ap-northeast-1a
      VpcId: !Ref VPC
      # サブネットのCIDR（VPCのCIDR内で作成）
      # VPCのCIDRが10.1.0.0/16なので、
      # サブネットのCIDRは10.1.0.0/17 から10.1.128.0/17の範囲
      CidrBlock: 10.1.1.0/24
      # Tags属性で、Nameタグを作成
      Tags:
        - Key: Name
          Value: pub-sub-a-cf

  #ルートテーブルの作成
  PubSubRT:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      # Tags属性で、Nameタグを作成
      Tags:
        - Key: Name
          Value: pub-sub-a-rt-cf

  # パブリックサブネット-インターネット間のルーティング
  PubSubToInternet:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PubSubRT
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref IGW

  # ルートテーブルをサブネットに関連付け
  AssoPubSubRT:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PubSub
      RouteTableId: !Ref PubSubRT

  #EC2の作成
  EC2: 
    Type: AWS::EC2::Instance
    Properties: 
      #AMI ID
      ImageId: !Ref MyAMIID

      #キーペアのキーネーム
      KeyName: !Ref KeyName

      #インスタンスタイプ
      InstanceType: g4dn.2xlarge
      
      #パブリックIPの自動割り振り
      NetworkInterfaces: 
        - AssociatePublicIpAddress: "true"
          DeviceIndex: "0"
          SubnetId: !Ref PubSub
          GroupSet:
            - !Ref EC2SG

      #EC2インスタンスにIAMインスタンスプロフィールを付与
      IamInstanceProfile:
        !Ref MyIamInstanceProfile

      # Tags属性で、Nameタグを作成
      Tags:
          - Key: Name
            Value: ec2-a-cf


  #EC2インスタンスのセキュリティグループの記述
  EC2SG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: ec2-sg-cf
      GroupDescription: Allow SSH and HTTP access only MyIP
      VpcId: !Ref VPC

      #セキュリティグループのIngressアクセス
      #（Egressはステートフルで許可）
      SecurityGroupIngress:

        #自分のIPアドレスからのみIngress RDP トラフィックを許可
        #(RDP：リモートデスクトップ接続プロトコル)
        - IpProtocol: tcp
          FromPort: 3389
          ToPort: 3389
          CidrIp: !Ref MyIP   

        #すべてのIPアドレスからのhttpsトラフィックを許可
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0



# 出力パラメータ
Outputs:
  #EC2のパブリックIPをアウトプット
  EC2PublicIP:
    Value: !GetAtt EC2.PublicIp
    Description: Public IP of EC2 instance

~~~


不明点等を以下にまとめました。
Cloudformation テンプレート作成時の参考にしてください。

### パラメータについて
~~~Parameters.yaml
#入力パラメータ
Parameters:
#パラメータの論理ID
  ParameterLogicalID:
    #パラメータの説明
    Description: The EC2 Key Pair to allow SSH access to the instance
    #パラメータのタイプ
    Type: DataType　
    #パラメータのプロパティ
    ParameterProperty: value
~~~

### パラメータのタイプ一覧

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html

いろんなパラメータが載っています。他にもあるかも。

### AMIのIDを指定するパラメータ
AWS::EC2::Image::Id　
Amazon EC2 イメージ ID の配列 です。
(ami-0ff8a91507f77f867, ami-0a584ac55a7631c0c など)
AWS CloudFormation コンソールには、このパラメータータイプの値のドロップダウンリストが表示されないことに注意してください。

### CloudFormationでEC2にIAMロール付与

https://qiita.com/predora005/items/480dc3db258e84fcee81


AWS::IAM::InstanceProfileなど、パラメータタイプについて調べたいときは、
これをそのままブラウザに打ち込むと、英語の公式ドキュメントがヒットします。
公式ドキュメントを見て、各種パラメータのプロパティを記入してください。


https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-iam-instanceprofile.html



https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html#cfn-iam-role-permissionsboundary


https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-iam-policy.html