

 AWS SDK for .NET V3 がメンテナンスモードになりました。

[AWS SDK for .NET V4](https://docs.aws.amazon.com/sdk-for-net/v4/developer-guide/welcome.html) に移行することをお勧めします。移行方法の詳細と情報については、[メンテナンスモードのお知らせ](https://aws.amazon.com/blogs/developer/aws-sdk-for-net-v3-maintenance-mode-announcement/)を参照してください。

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

# Amazon EC2 スポットインスタンスのチュートリアル
<a name="how-to-spot-instances"></a>

このチュートリアルでは、 AWS SDK for .NET を使用して Amazon EC2 スポットインスタンスを管理する方法を示します。

## 概要:
<a name="tutor-spot-net-overview"></a>

スポットインスタンスでは、未使用の Amazon EC2 コンピューティング性能をオンデマンド料金よりも低価格でリクエストできます。これにより、中断が可能なアプリケーションの EC2 コストを大幅に削減できます。

スポットインスタンスのリクエスト方法と使用方法の概要は次のとおりです。

1. 支払う上限価格を指定して、スポットインスタンスリクエストを作成します。

1. リクエストが受理されたら、他の Amazon EC2 インスタンスと同様にインスタンスを実行します。

1. *スポット料金*の変更によってインスタンスが自動的に終了しない限り、必要な期間インスタンスを実行し、終了します。

1. 不要になったスポットインスタンスリクエストをクリーンアップして、スポットインスタンスがそれ以上作成されないようにします。

以上が、スポットインスタンスに関する非常に大まかな概要です。スポットインスタンスの詳細については、「[Amazon EC2 ユーザーガイド](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)」の「[スポットインスタンス](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-spot-instances.html)」を参照してください。

## このチュートリアルの内容
<a name="about-spot-instances-tutorial"></a>

このチュートリアルでは、 AWS SDK for .NET を使用して以下を実行します。
+ スポットインスタンスリクエストを作成する
+ スポットインスタンスリクエストが受理されたかどうかを判断する
+ スポットインスタンスリクエストをキャンセルする
+ 関連するインスタンスを終了させる

以下のセクションでは、この例のスニペットとその他の情報を確認できます。スニペットの下には、[この例のコードの全文](#tutor-spot-net-main)が示されており、そのままビルドして実行できます。

**Topics**
+ [概要:](#tutor-spot-net-overview)
+ [このチュートリアルの内容](#about-spot-instances-tutorial)
+ [前提条件](#tutor-spot-net-prereq)
+ [必要な要素を集める](#tutor-spot-net-gather)
+ [スポットインスタンスリクエストの作成](#tutor-spot-net-submit)
+ [スポットインスタンスリクエストの状態を判断する](#tutor-spot-net-request-state)
+ [スポットインスタンスリクエストのクリーンアップ](#tutor-spot-net-clean-up-request)
+ [スポットインスタンスのクリーンアップ](#tutor-spot-net-clean-up-instance)
+ [コード全文](#tutor-spot-net-main)
+ [その他の考慮事項](#tutor-spot-net-additional)

## 前提条件
<a name="tutor-spot-net-prereq"></a>

API の詳細と前提条件については、親セクション ([Amazon EC2 の使用](ec2-apis-intro.md)) を参照してください。

## 必要な要素を集める
<a name="tutor-spot-net-gather"></a>

スポットインスタンスリクエストを作成するには、いくつかの要素が必要です。
+ インスタンスの数とそのインスタンスタイプ。選択できるインスタンスタイプは複数あります。詳細については、「[Amazon EC2 ユーザーガイド](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)」の「[Amazon EC2 インスタンスタイプ](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-types.html)」を参照してください。「[インスタンスタイプの詳細](https://aws.amazon.com/ec2/instance-types/)」と「[インスタンスタイプエクスプローラー](https://aws.amazon.com/ec2/instance-explorer/)」も参照してください。

  このチュートリアルでは、デフォルトの数は 1 です。
+ インスタンスの作成に使用する Amazon マシンイメージ (AMI)。AMI の詳細については、「[Amazon EC2 ユーザーガイド](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)」の「[Amazon マシンイメージ (AMI)](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AMIs.html)」を参照してください。特に、「[AMI の検索](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/finding-an-ami.html)」と「[共有 AMI](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/sharing-amis.html)」を参照してください。
+ インスタンス時間あたりに支払う上限価格。すべてのインスタンスタイプ (オンデマンドインスタンスとスポットインスタンスの両方) の料金は、[Amazon EC2 の料金ページ](https://aws.amazon.com/ec2/pricing/)で確認いただけます。このチュートリアルのデフォルト料金については、後で説明します。
+ インスタンスにリモートで接続する場合は、適切な構成とリソースを持つセキュリティグループ。この詳細については「[Amazon EC2 でのセキュリティグループの使用](security-groups.md)」で説明されており、[必要な要素の収集](run-instance.md#run-instance-gather)と[インスタンスへの接続](run-instance.md#connect-to-instance)に関する情報は「[Amazon EC2 インスタンスの起動](run-instance.md)」で説明されています。簡単にするために、このチュートリアルではすべての新しい AWS アカウントに存在する **default** という名前のセキュリティグループを使用します。

スポットインスタンスをリクエストするには複数の方法があります。一般的な戦略は以下のとおりです。
+ オンデマンド料金を明確に下回るようにリクエストを作成します。
+ 計算結果の価値に基づいてリクエストを作成します。
+ コンピューティング性能をできるだけ早く獲得するようにリクエストを作成します。

以下の説明は、「[Amazon EC2 ユーザーガイド](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)」の「[スポットインスタンスの料金履歴](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-spot-instances-history.html)」を示しています。

### コストをオンデマンドよりも削減する
<a name="reduce-cost"></a>

実行完了までに何時間も、あるいは何日間もかかるバッチ処理ジョブがあるとします。ただし、いつ開始していつ終了するかについては、特に決められていないものとします。このジョブを完了するためのコストを、オンデマンドインスタンスを使用する場合よりも低くできるかどうかを考えます。

Amazon EC2 コンソールまたは Amazon EC2 API を使用して、インスタンスタイプ別のスポット料金の履歴を調べます。使用したいインスタンスタイプの、特定のアベイラビリティーゾーンでの価格履歴を分析した後は、リクエストのアプローチとして次の 2 つも考えられます。
+ スポット料金の範囲の上限 (ただしオンデマンド料金よりは下) でリクエストを指定します。こうすることで、この 1 回限りのスポットリクエストが受理されて、ジョブが完了するまで連続して実行される可能性が高くなります。
+ 価格範囲の下限でリクエストを指定し、1 つの永続リクエストで次々とインスタンスを起動するよう計画を立てます。これらのインスタンスの実行時間を合計すると、ジョブを完了するのに十分な長さとなり、合計コストも低くなります。

### 結果の価値以上は支払わない
<a name="value-of-result"></a>

データ処理ジョブを実行するとします。このジョブの結果が持つ価値は判明しており、計算コストに換算してどれくらいになるかもわかっています。

使用するインスタンスタイプのスポット料金履歴の分析が完了したら、計算時間のコストがこのジョブの結果の価値を上回ることのないように料金を選択します。永続リクエストを作成し、スポット料金がリクエスト以下となったときに断続的に実行するよう設定します。

### コンピューティング性能をすぐに獲得する
<a name="acquire-quickly"></a>

追加のコンピューティング性能が突然、短期間だけ必要になり、オンデマンドインスタンスではそのコンピューティング性能を確保できないとします。使用するインスタンスタイプのスポット料金履歴の分析が完了したら、履歴での最高料金を超える料金を選択します。こうすることで、リクエストがすぐに受理され、計算が完了するまで連続して計算できる可能性が飛躍的に高まります。

必要な要素を集めて戦略を選択したら、スポットインスタンスをリクエストする準備ができました。このチュートリアルでは、デフォルトの最大スポットインスタンス料金をオンデマンド料金と同額 (このチュートリアルでは 0.003 ドル) に設定します。このように料金を設定すると、リクエストが達成される可能性が最大になります。

## スポットインスタンスリクエストの作成
<a name="tutor-spot-net-submit"></a>

次のスニペットでは、上記で集めた要素を使用してスポットインスタンスリクエストを作成する方法を示しています。

[このトピックの最後](#tutor-spot-net-main)で、スニペットが実際に使用されている例を確認できます。

```
    //
    // Method to create a Spot Instance request
    private static async Task<SpotInstanceRequest> CreateSpotInstanceRequest(
      IAmazonEC2 ec2Client, string amiId, string securityGroupName,
      InstanceType instanceType, string spotPrice, int instanceCount)
    {
      var launchSpecification = new LaunchSpecification{
        ImageId = amiId,
        InstanceType = instanceType
      };
      launchSpecification.SecurityGroups.Add(securityGroupName);
      var request = new RequestSpotInstancesRequest{
        SpotPrice = spotPrice,
        InstanceCount = instanceCount,
        LaunchSpecification = launchSpecification
      };

      RequestSpotInstancesResponse result =
        await ec2Client.RequestSpotInstancesAsync(request);
      return result.SpotInstanceRequests[0];
    }
```

このメソッドから返される重要な値は、スポットインスタンスリクエスト ID です。これは返された [SpotInstanceRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TSpotInstanceRequest.html) オブジェクトの `SpotInstanceRequestId` メンバーに含まれています。

**注記**  
起動したすべてのスポットインスタンスに対して料金が発生します。不要なコストを回避するには、[リクエストをキャンセル](#tutor-spot-net-clean-up-request)して[インスタンスを削除](#tutor-spot-net-clean-up-instance)するようにしてください。

## スポットインスタンスリクエストの状態を判断する
<a name="tutor-spot-net-request-state"></a>

次のスニペットでは、スポットインスタンスリクエストに関する情報を取得する方法を示しています。この情報を使用して、スポットインスタンスリクエストが受理されるのを待つかどうかといった特定の決定をコード内で下すことができます。

[このトピックの最後](#tutor-spot-net-main)で、スニペットが実際に使用されている例を確認できます。

```
    //
    // Method to get information about a Spot Instance request, including the status,
    // instance ID, etc.
    // It gets the information for a specific request (as opposed to all requests).
    private static async Task<SpotInstanceRequest> GetSpotInstanceRequestInfo(
      IAmazonEC2 ec2Client, string requestId)
    {
      var describeRequest = new DescribeSpotInstanceRequestsRequest();
      describeRequest.SpotInstanceRequestIds.Add(requestId);

      DescribeSpotInstanceRequestsResponse describeResponse =
        await ec2Client.DescribeSpotInstanceRequestsAsync(describeRequest);
      return describeResponse.SpotInstanceRequests[0];
    }
```

このメソッドでは、スポットインスタンスリクエストに関する情報 (インスタンス ID、状態、ステータスコードなど) が返されます。スポットインスタンスリクエストのステータスコードの詳細については、「[Amazon EC2 ユーザーガイド](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)」の「[スポットリクエストのステータス](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/spot-bid-status.html#spot-instance-bid-status-understand)」を参照してください。

## スポットインスタンスリクエストのクリーンアップ
<a name="tutor-spot-net-clean-up-request"></a>

スポットインスタンスをリクエストする必要がなくなった場合、未処理のリクエストをキャンセルして、リクエストが再び受理されないようにすることが重要です。次のスニペットでは、スポットインスタンスリクエストをキャンセルする方法を示しています。

[このトピックの最後](#tutor-spot-net-main)で、スニペットが実際に使用されている例を確認できます。

```
    //
    // Method to cancel a Spot Instance request
    private static async Task CancelSpotInstanceRequest(
      IAmazonEC2 ec2Client, string requestId)
    {
      var cancelRequest = new CancelSpotInstanceRequestsRequest();
      cancelRequest.SpotInstanceRequestIds.Add(requestId);

      await ec2Client.CancelSpotInstanceRequestsAsync(cancelRequest);
    }
```

## スポットインスタンスのクリーンアップ
<a name="tutor-spot-net-clean-up-instance"></a>

不要なコストを回避するには、スポットインスタンスリクエストから起動したインスタンスをすべて終了させることが重要です。単にスポットインスタンスリクエストをキャンセルするだけではインスタンスは終了しないので、引き続きインスタンスに対して料金が発生することになります。次のスニペットでは、アクティブなスポットインスタンスのインスタンス識別子を取得した後にインスタンスを終了する方法を示しています。

[このトピックの最後](#tutor-spot-net-main)で、スニペットが実際に使用されている例を確認できます。

```
    //
    // Method to terminate a Spot Instance
    private static async Task TerminateSpotInstance(
      IAmazonEC2 ec2Client, string requestId)
    {
      var describeRequest = new DescribeSpotInstanceRequestsRequest();
      describeRequest.SpotInstanceRequestIds.Add(requestId);

      // Retrieve the Spot Instance request to check for running instances.
      DescribeSpotInstanceRequestsResponse describeResponse =
        await ec2Client.DescribeSpotInstanceRequestsAsync(describeRequest);

      // If there are any running instances, terminate them
      if(   (describeResponse.SpotInstanceRequests[0].Status.Code
              == "request-canceled-and-instance-running")
         || (describeResponse.SpotInstanceRequests[0].State == SpotInstanceState.Active))
      {
        TerminateInstancesResponse response =
          await ec2Client.TerminateInstancesAsync(new TerminateInstancesRequest{
            InstanceIds = new List<string>(){
              describeResponse.SpotInstanceRequests[0].InstanceId } });
        foreach (InstanceStateChange item in response.TerminatingInstances)
        {
          Console.WriteLine($"\n  Terminated instance: {item.InstanceId}");
          Console.WriteLine($"  Instance state: {item.CurrentState.Name}\n");
        }
      }
    }
```

## コード全文
<a name="tutor-spot-net-main"></a>

次のコード例では、前述のメソッドを呼び出して、スポットインスタンスリクエストを作成およびキャンセルし、スポットインスタンスを終了しています。

### SDK リファレンス
<a name="w2aac19c15c17c21c43b5b1"></a>

NuGet パッケージ:
+ [AWSSDK.EC2](https://www.nuget.org/packages/AWSSDK.EC2)

プログラミング要素:
+ 名前空間 [Amazon.EC2](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2.html)

  クラス [AmazonEC2Client](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TEC2Client.html)

  クラス [InstanceType](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TInstanceType.html)
+ 名前空間 [Amazon.EC2.Model](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2Model.html)

  クラス [CancelSpotInstanceRequestsRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TCancelSpotInstanceRequestsRequest.html)

  クラス [DescribeSpotInstanceRequestsRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TDescribeSpotInstanceRequestsRequest.html)

  クラス [DescribeSpotInstanceRequestsResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TDescribeSpotInstanceRequestsResponse.html)

  クラス [InstanceStateChange](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TInstanceStateChange.html)

  クラス [LaunchSpecification](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TLaunchSpecification.html)

  クラス [RequestSpotInstancesRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TRequestSpotInstancesRequest.html)

  クラス [RequestSpotInstancesResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TRequestSpotInstancesResponse.html)

  クラス [SpotInstanceRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TSpotInstanceRequest.html)

  クラス [TerminateInstancesRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TTerminateInstancesRequest.html)

  クラス [TerminateInstancesResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TTerminateInstancesResponse.html)

### コード
<a name="w2aac19c15c17c21c43b7b1"></a>

```
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
using Amazon.EC2;
using Amazon.EC2.Model;

namespace EC2SpotInstanceRequests
{
  class Program
  {
    static async Task Main(string[] args)
    {
      // Some default values.
      // These could be made into command-line arguments instead.
      var instanceType = InstanceType.T1Micro;
      string securityGroupName = "default";
      string spotPrice = "0.003";
      int instanceCount = 1;

      // Parse the command line arguments
      if((args.Length != 1) || (!args[0].StartsWith("ami-")))
      {
        Console.WriteLine("\nUsage: EC2SpotInstanceRequests ami");
        Console.WriteLine("  ami: the Amazon Machine Image to use for the Spot Instances.");
        return;
      }

      // Create the Amazon EC2 client.
      var ec2Client = new AmazonEC2Client();

      // Create the Spot Instance request and record its ID
      Console.WriteLine("\nCreating spot instance request...");
      var req = await CreateSpotInstanceRequest(
        ec2Client, args[0], securityGroupName, instanceType, spotPrice, instanceCount);
      string requestId = req.SpotInstanceRequestId;

      // Wait for an EC2 Spot Instance to become active
      Console.WriteLine(
        $"Waiting for Spot Instance request with ID {requestId} to become active...");
      int wait = 1;
      var start = DateTime.Now;
      while(true)
      {
        Console.Write(".");

        // Get and check the status to see if the request has been fulfilled.
        var requestInfo = await GetSpotInstanceRequestInfo(ec2Client, requestId);
        if(requestInfo.Status.Code == "fulfilled")
        {
          Console.WriteLine($"\nSpot Instance request {requestId} " +
            $"has been fulfilled by instance {requestInfo.InstanceId}.\n");
          break;
        }

        // Wait a bit and try again, longer each time (1, 2, 4, ...)
        Thread.Sleep(wait);
        wait = wait * 2;
      }

      // Show the user how long it took to fulfill the Spot Instance request.
      TimeSpan span = DateTime.Now.Subtract(start);
      Console.WriteLine($"That took {span.TotalMilliseconds} milliseconds");

      // Perform actions here as needed.
      // For this example, simply wait for the user to hit a key.
      // That gives them a chance to look at the EC2 console to see
      // the running instance if they want to.
      Console.WriteLine("Press any key to start the cleanup...");
      Console.ReadKey(true);

      // Cancel the request.
      // Do this first to make sure that the request can't be re-fulfilled
      // once the Spot Instance has been terminated.
      Console.WriteLine("Canceling Spot Instance request...");
      await CancelSpotInstanceRequest(ec2Client, requestId);

      // Terminate the Spot Instance that's running.
      Console.WriteLine("Terminating the running Spot Instance...");
      await TerminateSpotInstance(ec2Client, requestId);

      Console.WriteLine("Done. Press any key to exit...");
      Console.ReadKey(true);
    }


    //
    // Method to create a Spot Instance request
    private static async Task<SpotInstanceRequest> CreateSpotInstanceRequest(
      IAmazonEC2 ec2Client, string amiId, string securityGroupName,
      InstanceType instanceType, string spotPrice, int instanceCount)
    {
      var launchSpecification = new LaunchSpecification{
        ImageId = amiId,
        InstanceType = instanceType
      };
      launchSpecification.SecurityGroups.Add(securityGroupName);
      var request = new RequestSpotInstancesRequest{
        SpotPrice = spotPrice,
        InstanceCount = instanceCount,
        LaunchSpecification = launchSpecification
      };

      RequestSpotInstancesResponse result =
        await ec2Client.RequestSpotInstancesAsync(request);
      return result.SpotInstanceRequests[0];
    }


    //
    // Method to get information about a Spot Instance request, including the status,
    // instance ID, etc.
    // It gets the information for a specific request (as opposed to all requests).
    private static async Task<SpotInstanceRequest> GetSpotInstanceRequestInfo(
      IAmazonEC2 ec2Client, string requestId)
    {
      var describeRequest = new DescribeSpotInstanceRequestsRequest();
      describeRequest.SpotInstanceRequestIds.Add(requestId);

      DescribeSpotInstanceRequestsResponse describeResponse =
        await ec2Client.DescribeSpotInstanceRequestsAsync(describeRequest);
      return describeResponse.SpotInstanceRequests[0];
    }


    //
    // Method to cancel a Spot Instance request
    private static async Task CancelSpotInstanceRequest(
      IAmazonEC2 ec2Client, string requestId)
    {
      var cancelRequest = new CancelSpotInstanceRequestsRequest();
      cancelRequest.SpotInstanceRequestIds.Add(requestId);

      await ec2Client.CancelSpotInstanceRequestsAsync(cancelRequest);
    }


    //
    // Method to terminate a Spot Instance
    private static async Task TerminateSpotInstance(
      IAmazonEC2 ec2Client, string requestId)
    {
      var describeRequest = new DescribeSpotInstanceRequestsRequest();
      describeRequest.SpotInstanceRequestIds.Add(requestId);

      // Retrieve the Spot Instance request to check for running instances.
      DescribeSpotInstanceRequestsResponse describeResponse =
        await ec2Client.DescribeSpotInstanceRequestsAsync(describeRequest);

      // If there are any running instances, terminate them
      if(   (describeResponse.SpotInstanceRequests[0].Status.Code
              == "request-canceled-and-instance-running")
         || (describeResponse.SpotInstanceRequests[0].State == SpotInstanceState.Active))
      {
        TerminateInstancesResponse response =
          await ec2Client.TerminateInstancesAsync(new TerminateInstancesRequest{
            InstanceIds = new List<string>(){
              describeResponse.SpotInstanceRequests[0].InstanceId } });
        foreach (InstanceStateChange item in response.TerminatingInstances)
        {
          Console.WriteLine($"\n  Terminated instance: {item.InstanceId}");
          Console.WriteLine($"  Instance state: {item.CurrentState.Name}\n");
        }
      }
    }
  }
}
```

## その他の考慮事項
<a name="tutor-spot-net-additional"></a>
+ チュートリアルを実行したら、[Amazon EC2 コンソール](https://console.aws.amazon.com/ec2/)にサインインして、[スポットインスタンスリクエスト](https://console.aws.amazon.com/ec2/home#SpotInstances:)がキャンセル済みで[スポットインスタンス](https://console.aws.amazon.com/ec2/v2/home#Instances)が終了していることを確認するようお勧めします。