Amazon EC2 スポットインスタンスのチュートリアル - AWS SDK for .NET

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

Amazon EC2 スポットインスタンスのチュートリアル

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

概要

スポットインスタンスを使用すると、未使用の Amazon EC2 容量をオンデマンド料金より安くリクエストできます。これにより、中断される可能性のあるアプリケーションの EC2 コストを大幅に削減できます。

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

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

  2. リクエストが満たされたら、他の Amazon EC2 インスタンスと同じようにインスタンスを実行します。

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

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

以上が、スポットインスタンスに関する非常に大まかな概要です。スポットインスタンスの詳細については、Amazon EC2 ユーザーガイドスポットインスタンスを参照してください。

このチュートリアルの内容

このチュートリアルでは、 AWS SDK for .NET を使用して次の操作を行います。

  • スポットインスタンスリクエストを作成する

  • スポットインスタンスリクエストが受理されたかどうかを判断する

  • スポットインスタンスリクエストをキャンセルする

  • 関連するインスタンスを終了させる

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

前提条件

APIs と前提条件の詳細については、親セクション () を参照してくださいAmazon EC2 の使用

必要な要素を集める

スポットインスタンスリクエストを作成するには、いくつかの要素が必要です。

スポットインスタンスをリクエストするには複数の方法があります。一般的な戦略は以下のとおりです。

  • オンデマンド料金を明確に下回るようにリクエストを作成します。

  • 計算結果の価値に基づいてリクエストを作成します。

  • コンピューティング性能をできるだけ早く獲得するようにリクエストを作成します。

以下の説明は、Amazon EC2 ユーザーガイドスポットインスタンスの料金履歴を示しています。

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

Amazon EC2 コンソールまたは Amazon Word を使用して、インスタンスタイプのスポット料金履歴を調べますEC2API。使用したいインスタンスタイプの、特定のアベイラビリティーゾーンでの価格履歴を分析した後は、リクエストのアプローチとして次の 2 つも考えられます。

  • スポット料金の範囲の上限 (ただしオンデマンド料金よりは下) でリクエストを指定します。こうすることで、この 1 回限りのスポットリクエストが受理されて、ジョブが完了するまで連続して実行される可能性が高くなります。

  • 価格範囲の下限でリクエストを指定し、1 つの永続リクエストで次々とインスタンスを起動するよう計画を立てます。これらのインスタンスの実行時間を合計すると、ジョブを完了するのに十分な長さとなり、合計コストも低くなります。

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

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

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

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

スポットインスタンスリクエストの作成

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

このトピックの最後で、スニペットが実際に使用されている例を確認できます。

// // 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]; }

このメソッドから返される重要な値は、返される SpotInstanceRequest オブジェクトSpotInstanceRequestIdのメンバーに含まれるスポットインスタンスリクエスト ID です。

注記

起動したすべてのスポットインスタンスに対して料金が発生します。不要なコストを回避するには、リクエストをキャンセルしてインスタンスを削除するようにしてください。

スポットインスタンスリクエストの状態を判断する

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

このトピックの最後で、スニペットが実際に使用されている例を確認できます。

// // 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 ユーザーガイド「スポットリクエストのステータス」を参照してください。

スポットインスタンスリクエストのクリーンアップ

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

このトピックの最後で、スニペットが実際に使用されている例を確認できます。

// // 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"); } } }

コード全文

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

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"); } } } } }

追加の考慮事項