本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
Amazon EC2 Spot 实例教程
本教程介绍如何使用 适用于 .NET 的 AWS 开发工具包 管理 Amazon EC2 Spot 实例。
Overview
通过 Spot 实例,您可以请求低于按需价格的未使用Amazon EC2容量。这可以显著降低可能中断的应用程序的 EC2 成本。
以下是如何请求和使用 Spot 实例的高级摘要。
-
创建 Spot 实例请求,并指定您愿意支付的最高价。
-
满足请求后,像运行任何其他实例一样运行Amazon EC2实例。
-
根据需要运行实例,然后终止实例,除非 Spot 价格发生变化,从而为您终止实例。
-
当您不再需要 Spot 实例时清除它,以便不再创建 Spot 实例。
这是 Spot 实例的简要概述。您可以通过在 适用于 Linux 的 EC2 用户指南 或 中阅读有关 Spot 实例的信息来更好地了解它们适用于 Windows 的 EC2 用户指南。
关于本教程
在按照本教程操作时,您将使用 适用于 .NET 的 AWS 开发工具包 执行以下操作:
-
创建 Spot 实例请求
-
确定 Spot 实例请求的执行时间
-
取消 Spot 实例请求
-
终止相关实例
以下部分提供了此示例中的代码段和其他信息。示例完成代码的完整代码显示在代码段之后,可以按原样构建和运行。
Prerequisites
有关 APIs 和先决条件的信息,请参阅父部分 (使用 Amazon EC2)。
收集所需的内容
要创建 Spot 实例请求,您需要几个事物。
-
实例的数量及其实例类型。有多种实例类型可供选择,您可以在 适用于 Linux 的 EC2 用户指南 或 适用于 Windows 的 EC2 用户指南中看到它们。本教程的默认数量为 1。
-
用于创建实例的 Amazon 系统映像 (AMI)。请参阅 适用于 Linux 的 EC2 用户指南 或 中有关 AMIs 适用于 Windows 的 EC2 用户指南 的信息。例如,阅读有关 适用于 Linux 的 EC2 用户指南 或 中的共享 AMIs 适用于 Windows 的 EC2 用户指南 的信息。
-
您愿意为每实例小时支付的最高价。您可以在 定价页面上查看所有 实例类型(包括按需实例和 Spot 实例Amazon EC2
)的价格。稍后将说明本教程的默认价格。
-
如果您要远程连接到实例,请使用适当的配置和资源创建一个安全组。这在 中进行了介绍在中与授权组合作 Amazon EC2,并提供了有关收集所需内容以及连接到 中的实例的信息启动 Amazon EC2 实例。为简单起见,本教程使用所有较新的 AWS 账户都具有的名为 default 的安全组。
有很多方法可以请求 Spot 实例。以下是常见的策略:
-
发出确保成本低于按需定价的请求。
-
根据生成的计算值发出请求。
-
发出请求以便尽快获取计算容量。
以下说明引用了 适用于 Linux 的 EC2 用户指南 或 中的 Spot 适用于 Windows 的 EC2 用户指南 价格历史记录。
您需要进行花费数小时或数天的批处理工作。然而,您可以灵活调整启动和结束时间。您希望看到是否以低于按需实例的成本完成。
您可以使用 Amazon EC2 控制台或 Amazon EC2 API 检查实例类型的 Spot 价格历史记录。在您分析了给定可用区内所需实例类型的价格记录之后,您有两种可供选择的方法发出请求:
-
指定 Spot 价格范围(这仍然低于按需价格)的上限的请求,预测您的一次性 Spot 实例请求很有可能会执行,并运行足够的连续计算时间来完成作业。
-
指定在 Spot 价格范围的下限发出请求,随着时间的推移,通过持久的请求,计划结合多种已启动实例。总计一下,该实例会以较低的总成本、花费很长时间来完成这项工作。
您需要进行数据处理工作。您将会非常了解作业结果的价值,从而足以知道它们在计算成本方面的价值。
在分析了实例类型的 Spot 价格记录之后,您选择一个计算时间成本不高于该工作结果成本的价格。由于 Spot 价格的波动,该价格可能会达到或低于您的请求,所以您要创建一个持久请求,并允许它间歇运行。
您对附加容量有一个无法预料的短期需求,该容量不能通过按需实例获得。在您分析了实例类型的 Spot 价格历史记录之后,您选择一个高于历史最高价格的价格,以大幅提高快速执行请求的可能性,并继续计算,直到完成实例。
收集所需内容并选择策略后,您便可以请求 Spot 实例。对于本教程,默认的最高 Spot 实例价格设置为与按需价格相同(本教程为 0.003 美元)。以这种方式设置价格可最大限度地提高请求被执行的机会。
创建 Spot 实例请求
以下代码段说明如何使用之前收集的元素创建 Spot 实例请求。
本主题完成代码末尾的示例显示了此代码段正在使用中。
// // 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]; }
此方法返回的重要值是 Spot 实例请求 ID,它包含在返回的 SpotInstanceRequestId
SpotInstanceRequest对象的 成员中。
确定您的 Spot 实例请求的状态
以下代码段说明如何获取有关 Spot 实例请求的信息。您可以使用该信息在代码中做出某些决策,例如,是否继续等待 Spot 实例请求完成。
本主题完成代码末尾的示例显示了此代码段正在使用中。
// // 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]; }
方法返回有关 Spot 实例请求的信息,例如实例 ID、状态和状态代码。您可以在 适用于 Linux 的 EC2 用户指南 或 中查看 Spot 实例请求的状态代码适用于 Windows 的 EC2 用户指南。
清除 Spot 实例请求
当您不再需要请求 Spot 实例时,请务必取消任何未完成的请求以防止这些请求重新执行。以下代码段说明如何取消 Spot 实例请求。
本主题完成代码末尾的示例显示了此代码段正在使用中。
// // 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); }
清除 Spot 实例
为避免产生不必要的费用,终止从 Spot 实例请求启动的任何实例非常重要;只需取消 Spot 实例请求不会终止您的实例,这意味着您需要继续为它们付费。以下代码段向您演示如何在获取活动 Spot 实例的实例标识符后终止实例。
本主题完成代码末尾的示例显示了此代码段正在使用中。
// // 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"); } } }
完成代码
以下代码示例调用上述方法来创建和取消 Spot 实例请求并终止 Spot 实例。
NuGet 程序包:
编程元素:
-
命名空间 EC2
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"); } } } } }
其他注意事项
-
运行本教程后,最好登录 Amazon EC2 控制台
,以验证 Spot 实例请求 是否已取消以及 Spot 实例 是否已终止。