を使用した Auto Scaling の例 AWS SDK for .NET - AWS SDK コードの例

Doc AWS SDK Examples リポジトリには、他にも SDK の例があります。 AWS GitHub

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

を使用した Auto Scaling の例 AWS SDK for .NET

次のコード例は、Auto Scaling AWS SDK for .NET で を使用してアクションを実行し、一般的なシナリオを実装する方法を示しています。

アクションはより大きなプログラムからのコードの抜粋であり、コンテキスト内で実行する必要があります。アクションは個々のサービス機能を呼び出す方法を示していますが、関連するシナリオやサービス間の例ではアクションのコンテキストが確認できます。

「シナリオ」は、同じサービス内で複数の関数を呼び出して、特定のタスクを実行する方法を示すコード例です。

各例には、 へのリンクが含まれています。ここでは GitHub、コンテキスト内でコードを設定および実行する方法の手順を確認できます。

開始方法

次のコード例は、Auto Scaling の使用を開始する方法を示しています。

AWS SDK for .NET
注記

については、「」を参照してください GitHub。AWS コード例リポジトリ で全く同じ例を見つけて、設定と実行の方法を確認してください。

namespace AutoScalingActions; using Amazon.AutoScaling; public class HelloAutoScaling { /// <summary> /// Hello Amazon EC2 Auto Scaling. List EC2 Auto Scaling groups. /// </summary> /// <param name="args"></param> /// <returns>Async Task.</returns> static async Task Main(string[] args) { var client = new AmazonAutoScalingClient(); Console.WriteLine("Welcome to Amazon EC2 Auto Scaling."); Console.WriteLine("Let's get a description of your Auto Scaling groups."); var response = await client.DescribeAutoScalingGroupsAsync(); response.AutoScalingGroups.ForEach(autoScalingGroup => { Console.WriteLine($"{autoScalingGroup.AutoScalingGroupName}\t{autoScalingGroup.AvailabilityZones}"); }); if (response.AutoScalingGroups.Count == 0) { Console.WriteLine("Sorry, you don't have any Amazon EC2 Auto Scaling groups."); } } }
  • API の詳細については、「 API リファレンスDescribeAutoScalingGroups」の「」を参照してください。 AWS SDK for .NET

アクション

次の例は、AttachLoadBalancerTargetGroups を使用する方法を説明しています。

AWS SDK for .NET
注記

については、「」を参照してください GitHub。AWS コード例リポジトリ で全く同じ例を見つけて、設定と実行の方法を確認してください。

/// <summary> /// Attaches an Elastic Load Balancing (ELB) target group to this EC2 Auto Scaling group. /// The /// </summary> /// <param name="autoScalingGroupName">The name of the Auto Scaling group.</param> /// <param name="targetGroupArn">The Arn for the target group.</param> /// <returns>Async task.</returns> public async Task AttachLoadBalancerToGroup(string autoScalingGroupName, string targetGroupArn) { await _amazonAutoScaling.AttachLoadBalancerTargetGroupsAsync( new AttachLoadBalancerTargetGroupsRequest() { AutoScalingGroupName = autoScalingGroupName, TargetGroupARNs = new List<string>() { targetGroupArn } }); }

次の例は、CreateAutoScalingGroup を使用する方法を説明しています。

AWS SDK for .NET
注記

については、「」を参照してください GitHub。AWS コード例リポジトリ で全く同じ例を見つけて、設定と実行の方法を確認してください。

/// <summary> /// Create a new Amazon EC2 Auto Scaling group. /// </summary> /// <param name="groupName">The name to use for the new Auto Scaling /// group.</param> /// <param name="launchTemplateName">The name of the Amazon EC2 Auto Scaling /// launch template to use to create instances in the group.</param> /// <returns>A Boolean value indicating the success of the action.</returns> public async Task<bool> CreateAutoScalingGroupAsync( string groupName, string launchTemplateName, string availabilityZone) { var templateSpecification = new LaunchTemplateSpecification { LaunchTemplateName = launchTemplateName, }; var zoneList = new List<string> { availabilityZone, }; var request = new CreateAutoScalingGroupRequest { AutoScalingGroupName = groupName, AvailabilityZones = zoneList, LaunchTemplate = templateSpecification, MaxSize = 6, MinSize = 1 }; var response = await _amazonAutoScaling.CreateAutoScalingGroupAsync(request); Console.WriteLine($"{groupName} Auto Scaling Group created"); return response.HttpStatusCode == System.Net.HttpStatusCode.OK; }
  • API の詳細については、「 API リファレンスCreateAutoScalingGroup」の「」を参照してください。 AWS SDK for .NET

次の例は、DeleteAutoScalingGroup を使用する方法を説明しています。

AWS SDK for .NET
注記

については、「」を参照してください GitHub。AWS コード例リポジトリ で全く同じ例を見つけて、設定と実行の方法を確認してください。

Auto Scaling グループの最小サイズをゼロに更新し、グループ内のすべてのインスタンスを終了して、グループを削除します。

/// <summary> /// Try to terminate an instance by its Id. /// </summary> /// <param name="instanceId">The Id of the instance to terminate.</param> /// <returns>Async task.</returns> public async Task TryTerminateInstanceById(string instanceId) { var stopping = false; Console.WriteLine($"Stopping {instanceId}..."); while (!stopping) { try { await _amazonAutoScaling.TerminateInstanceInAutoScalingGroupAsync( new TerminateInstanceInAutoScalingGroupRequest() { InstanceId = instanceId, ShouldDecrementDesiredCapacity = false }); stopping = true; } catch (ScalingActivityInProgressException) { Console.WriteLine($"Scaling activity in progress for {instanceId}. Waiting..."); Thread.Sleep(10000); } } } /// <summary> /// Tries to delete the EC2 Auto Scaling group. If the group is in use or in progress, /// waits and retries until the group is successfully deleted. /// </summary> /// <param name="groupName">The name of the group to try to delete.</param> /// <returns>Async task.</returns> public async Task TryDeleteGroupByName(string groupName) { var stopped = false; while (!stopped) { try { await _amazonAutoScaling.DeleteAutoScalingGroupAsync( new DeleteAutoScalingGroupRequest() { AutoScalingGroupName = groupName }); stopped = true; } catch (Exception e) when ((e is ScalingActivityInProgressException) || (e is Amazon.AutoScaling.Model.ResourceInUseException)) { Console.WriteLine($"Some instances are still running. Waiting..."); Thread.Sleep(10000); } } } /// <summary> /// Terminate instances and delete the Auto Scaling group by name. /// </summary> /// <param name="groupName">The name of the group to delete.</param> /// <returns>Async task.</returns> public async Task TerminateAndDeleteAutoScalingGroupWithName(string groupName) { var describeGroupsResponse = await _amazonAutoScaling.DescribeAutoScalingGroupsAsync( new DescribeAutoScalingGroupsRequest() { AutoScalingGroupNames = new List<string>() { groupName } }); if (describeGroupsResponse.AutoScalingGroups.Any()) { // Update the size to 0. await _amazonAutoScaling.UpdateAutoScalingGroupAsync( new UpdateAutoScalingGroupRequest() { AutoScalingGroupName = groupName, MinSize = 0 }); var group = describeGroupsResponse.AutoScalingGroups[0]; foreach (var instance in group.Instances) { await TryTerminateInstanceById(instance.InstanceId); } await TryDeleteGroupByName(groupName); } else { Console.WriteLine($"No groups found with name {groupName}."); } }
/// <summary> /// Delete an Auto Scaling group. /// </summary> /// <param name="groupName">The name of the Amazon EC2 Auto Scaling group.</param> /// <returns>A Boolean value indicating the success of the action.</returns> public async Task<bool> DeleteAutoScalingGroupAsync( string groupName) { var deleteAutoScalingGroupRequest = new DeleteAutoScalingGroupRequest { AutoScalingGroupName = groupName, ForceDelete = true, }; var response = await _amazonAutoScaling.DeleteAutoScalingGroupAsync(deleteAutoScalingGroupRequest); if (response.HttpStatusCode == System.Net.HttpStatusCode.OK) { Console.WriteLine($"You successfully deleted {groupName}"); return true; } Console.WriteLine($"Couldn't delete {groupName}."); return false; }
  • API の詳細については、「 API リファレンスDeleteAutoScalingGroup」の「」を参照してください。 AWS SDK for .NET

次の例は、DescribeAutoScalingGroups を使用する方法を説明しています。

AWS SDK for .NET
注記

については、「」を参照してください GitHub。AWS コード例リポジトリ で全く同じ例を見つけて、設定と実行の方法を確認してください。

/// <summary> /// Get data about the instances in an Amazon EC2 Auto Scaling group. /// </summary> /// <param name="groupName">The name of the Amazon EC2 Auto Scaling group.</param> /// <returns>A list of Amazon EC2 Auto Scaling details.</returns> public async Task<List<AutoScalingInstanceDetails>> DescribeAutoScalingInstancesAsync( string groupName) { var groups = await DescribeAutoScalingGroupsAsync(groupName); var instanceIds = new List<string>(); groups!.ForEach(group => { if (group.AutoScalingGroupName == groupName) { group.Instances.ForEach(instance => { instanceIds.Add(instance.InstanceId); }); } }); var scalingGroupsRequest = new DescribeAutoScalingInstancesRequest { MaxRecords = 10, InstanceIds = instanceIds, }; var response = await _amazonAutoScaling.DescribeAutoScalingInstancesAsync(scalingGroupsRequest); var instanceDetails = response.AutoScalingInstances; return instanceDetails; }
  • API の詳細については、「 API リファレンスDescribeAutoScalingGroups」の「」を参照してください。 AWS SDK for .NET

次の例は、DescribeAutoScalingInstances を使用する方法を説明しています。

AWS SDK for .NET
注記

については、「」を参照してください GitHub。AWS コード例リポジトリ で全く同じ例を見つけて、設定と実行の方法を確認してください。

/// <summary> /// Get data about the instances in an Amazon EC2 Auto Scaling group. /// </summary> /// <param name="groupName">The name of the Amazon EC2 Auto Scaling group.</param> /// <returns>A list of Amazon EC2 Auto Scaling details.</returns> public async Task<List<AutoScalingInstanceDetails>> DescribeAutoScalingInstancesAsync( string groupName) { var groups = await DescribeAutoScalingGroupsAsync(groupName); var instanceIds = new List<string>(); groups!.ForEach(group => { if (group.AutoScalingGroupName == groupName) { group.Instances.ForEach(instance => { instanceIds.Add(instance.InstanceId); }); } }); var scalingGroupsRequest = new DescribeAutoScalingInstancesRequest { MaxRecords = 10, InstanceIds = instanceIds, }; var response = await _amazonAutoScaling.DescribeAutoScalingInstancesAsync(scalingGroupsRequest); var instanceDetails = response.AutoScalingInstances; return instanceDetails; }
  • API の詳細については、「 API リファレンスDescribeAutoScalingInstances」の「」を参照してください。 AWS SDK for .NET

次の例は、DescribeScalingActivities を使用する方法を説明しています。

AWS SDK for .NET
注記

については、「」を参照してください GitHub。AWS コード例リポジトリ で全く同じ例を見つけて、設定と実行の方法を確認してください。

/// <summary> /// Retrieve a list of the Amazon EC2 Auto Scaling activities for an /// Amazon EC2 Auto Scaling group. /// </summary> /// <param name="groupName">The name of the Amazon EC2 Auto Scaling group.</param> /// <returns>A list of Amazon EC2 Auto Scaling activities.</returns> public async Task<List<Amazon.AutoScaling.Model.Activity>> DescribeScalingActivitiesAsync( string groupName) { var scalingActivitiesRequest = new DescribeScalingActivitiesRequest { AutoScalingGroupName = groupName, MaxRecords = 10, }; var response = await _amazonAutoScaling.DescribeScalingActivitiesAsync(scalingActivitiesRequest); return response.Activities; }
  • API の詳細については、「 API リファレンスDescribeScalingActivities」の「」を参照してください。 AWS SDK for .NET

次の例は、DisableMetricsCollection を使用する方法を説明しています。

AWS SDK for .NET
注記

については、「」を参照してください GitHub。AWS コード例リポジトリ で全く同じ例を見つけて、設定と実行の方法を確認してください。

/// <summary> /// Disable the collection of metric data for an Amazon EC2 Auto Scaling /// group. /// </summary> /// <param name="groupName">The name of the Auto Scaling group.</param> /// <returns>A Boolean value that indicates the success or failure of /// the operation.</returns> public async Task<bool> DisableMetricsCollectionAsync(string groupName) { var request = new DisableMetricsCollectionRequest { AutoScalingGroupName = groupName, }; var response = await _amazonAutoScaling.DisableMetricsCollectionAsync(request); return response.HttpStatusCode == System.Net.HttpStatusCode.OK; }
  • API の詳細については、「 API リファレンスDisableMetricsCollection」の「」を参照してください。 AWS SDK for .NET

次の例は、EnableMetricsCollection を使用する方法を説明しています。

AWS SDK for .NET
注記

については、「」を参照してください GitHub。AWS コード例リポジトリ で全く同じ例を見つけて、設定と実行の方法を確認してください。

/// <summary> /// Enable the collection of metric data for an Auto Scaling group. /// </summary> /// <param name="groupName">The name of the Auto Scaling group.</param> /// <returns>A Boolean value indicating the success of the action.</returns> public async Task<bool> EnableMetricsCollectionAsync(string groupName) { var listMetrics = new List<string> { "GroupMaxSize", }; var collectionRequest = new EnableMetricsCollectionRequest { AutoScalingGroupName = groupName, Metrics = listMetrics, Granularity = "1Minute", }; var response = await _amazonAutoScaling.EnableMetricsCollectionAsync(collectionRequest); return response.HttpStatusCode == System.Net.HttpStatusCode.OK; }
  • API の詳細については、「 API リファレンスEnableMetricsCollection」の「」を参照してください。 AWS SDK for .NET

次の例は、SetDesiredCapacity を使用する方法を説明しています。

AWS SDK for .NET
注記

については、「」を参照してください GitHub。AWS コード例リポジトリ で全く同じ例を見つけて、設定と実行の方法を確認してください。

/// <summary> /// Set the desired capacity of an Auto Scaling group. /// </summary> /// <param name="groupName">The name of the Auto Scaling group.</param> /// <param name="desiredCapacity">The desired capacity for the Auto /// Scaling group.</param> /// <returns>A Boolean value indicating the success of the action.</returns> public async Task<bool> SetDesiredCapacityAsync( string groupName, int desiredCapacity) { var capacityRequest = new SetDesiredCapacityRequest { AutoScalingGroupName = groupName, DesiredCapacity = desiredCapacity, }; var response = await _amazonAutoScaling.SetDesiredCapacityAsync(capacityRequest); Console.WriteLine($"You have set the DesiredCapacity to {desiredCapacity}."); return response.HttpStatusCode == System.Net.HttpStatusCode.OK; }
  • API の詳細については、「 API リファレンスSetDesiredCapacity」の「」を参照してください。 AWS SDK for .NET

次の例は、TerminateInstanceInAutoScalingGroup を使用する方法を説明しています。

AWS SDK for .NET
注記

については、「」を参照してください GitHub。AWS コード例リポジトリ で全く同じ例を見つけて、設定と実行の方法を確認してください。

/// <summary> /// Terminate all instances in the Auto Scaling group in preparation for /// deleting the group. /// </summary> /// <param name="instanceId">The instance Id of the instance to terminate.</param> /// <returns>A Boolean value that indicates the success or failure of /// the operation.</returns> public async Task<bool> TerminateInstanceInAutoScalingGroupAsync( string instanceId) { var request = new TerminateInstanceInAutoScalingGroupRequest { InstanceId = instanceId, ShouldDecrementDesiredCapacity = false, }; var response = await _amazonAutoScaling.TerminateInstanceInAutoScalingGroupAsync(request); if (response.HttpStatusCode == System.Net.HttpStatusCode.OK) { Console.WriteLine($"You have terminated the instance: {instanceId}"); return true; } Console.WriteLine($"Could not terminate {instanceId}"); return false; }

次の例は、UpdateAutoScalingGroup を使用する方法を説明しています。

AWS SDK for .NET
注記

については、「」を参照してください GitHub。AWS コード例リポジトリ で全く同じ例を見つけて、設定と実行の方法を確認してください。

/// <summary> /// Update the capacity of an Auto Scaling group. /// </summary> /// <param name="groupName">The name of the Auto Scaling group.</param> /// <param name="launchTemplateName">The name of the EC2 launch template.</param> /// <param name="maxSize">The maximum number of instances that can be /// created for the Auto Scaling group.</param> /// <returns>A Boolean value indicating the success of the action.</returns> public async Task<bool> UpdateAutoScalingGroupAsync( string groupName, string launchTemplateName, int maxSize) { var templateSpecification = new LaunchTemplateSpecification { LaunchTemplateName = launchTemplateName, }; var groupRequest = new UpdateAutoScalingGroupRequest { MaxSize = maxSize, AutoScalingGroupName = groupName, LaunchTemplate = templateSpecification, }; var response = await _amazonAutoScaling.UpdateAutoScalingGroupAsync(groupRequest); if (response.HttpStatusCode == System.Net.HttpStatusCode.OK) { Console.WriteLine($"You successfully updated the Auto Scaling group {groupName}."); return true; } else { return false; } }
  • API の詳細については、「 API リファレンスUpdateAutoScalingGroup」の「」を参照してください。 AWS SDK for .NET

シナリオ

次のコード例は、本、映画、曲のレコメンデーションを返す負荷分散型ウェブサービスの作成方法を示しています。この例は、障害に対するサービスの対応方法と、障害発生時の耐障害性を高めるためにサービスを再構築する方法を示しています。

  • Amazon EC2 Auto Scaling グループを使用して、起動テンプレートに基づいて Amazon Elastic Compute Cloud (Amazon EC2) インスタンスを作成し、インスタンス数を所定の範囲内に維持します。

  • Elastic Load Balancing で HTTP リクエストを処理して配信します。

  • Auto Scaling グループ内のインスタンスの状態を監視し、正常なインスタンスにのみリクエストを転送します。

  • 各 EC2 インスタンスで Python ウェブサーバーを実行して HTTP リクエストを処理します。ウェブサーバーはレコメンデーションとヘルスチェックを返します。

  • Amazon DynamoDB テーブルを使用してレコメンデーションサービスをシミュレートできます。

  • AWS Systems Manager パラメータを更新して、リクエストとヘルスチェックに対するウェブサーバーの応答を制御します。

AWS SDK for .NET
注記

については、「」を参照してください GitHub。AWS コード例リポジトリ で全く同じ例を見つけて、設定と実行の方法を確認してください。

コマンドプロンプトからインタラクティブのシナリオを実行します。

static async Task Main(string[] args) { _configuration = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("settings.json") // Load settings from .json file. .AddJsonFile("settings.local.json", true) // Optionally, load local settings. .Build(); // Set up dependency injection for the AWS services. using var host = Host.CreateDefaultBuilder(args) .ConfigureLogging(logging => logging.AddFilter("System", LogLevel.Debug) .AddFilter<DebugLoggerProvider>("Microsoft", LogLevel.Information) .AddFilter<ConsoleLoggerProvider>("Microsoft", LogLevel.Trace)) .ConfigureServices((_, services) => services.AddAWSService<IAmazonIdentityManagementService>() .AddAWSService<IAmazonDynamoDB>() .AddAWSService<IAmazonElasticLoadBalancingV2>() .AddAWSService<IAmazonSimpleSystemsManagement>() .AddAWSService<IAmazonAutoScaling>() .AddAWSService<IAmazonEC2>() .AddTransient<AutoScalerWrapper>() .AddTransient<ElasticLoadBalancerWrapper>() .AddTransient<SmParameterWrapper>() .AddTransient<Recommendations>() .AddSingleton<IConfiguration>(_configuration) ) .Build(); ServicesSetup(host); ResourcesSetup(); try { Console.WriteLine(new string('-', 80)); Console.WriteLine("Welcome to the Resilient Architecture Example Scenario."); Console.WriteLine(new string('-', 80)); await Deploy(true); Console.WriteLine("Now let's begin the scenario."); Console.WriteLine(new string('-', 80)); await Demo(true); Console.WriteLine(new string('-', 80)); Console.WriteLine("Finally, let's clean up our resources."); Console.WriteLine(new string('-', 80)); await DestroyResources(true); Console.WriteLine(new string('-', 80)); Console.WriteLine("Resilient Architecture Example Scenario is complete."); Console.WriteLine(new string('-', 80)); } catch (Exception ex) { Console.WriteLine(new string('-', 80)); Console.WriteLine($"There was a problem running the scenario: {ex.Message}"); await DestroyResources(true); Console.WriteLine(new string('-', 80)); } } /// <summary> /// Setup any common resources, also used for integration testing. /// </summary> public static void ResourcesSetup() { _httpClient = new HttpClient(); } /// <summary> /// Populate the services for use within the console application. /// </summary> /// <param name="host">The services host.</param> private static void ServicesSetup(IHost host) { _elasticLoadBalancerWrapper = host.Services.GetRequiredService<ElasticLoadBalancerWrapper>(); _iamClient = host.Services.GetRequiredService<IAmazonIdentityManagementService>(); _recommendations = host.Services.GetRequiredService<Recommendations>(); _autoScalerWrapper = host.Services.GetRequiredService<AutoScalerWrapper>(); _smParameterWrapper = host.Services.GetRequiredService<SmParameterWrapper>(); } /// <summary> /// Deploy necessary resources for the scenario. /// </summary> /// <param name="interactive">True to run as interactive.</param> /// <returns>True if successful.</returns> public static async Task<bool> Deploy(bool interactive) { var protocol = "HTTP"; var port = 80; var sshPort = 22; Console.WriteLine( "\nFor this demo, we'll use the AWS SDK for .NET to create several AWS resources\n" + "to set up a load-balanced web service endpoint and explore some ways to make it resilient\n" + "against various kinds of failures.\n\n" + "Some of the resources create by this demo are:\n"); Console.WriteLine( "\t* A DynamoDB table that the web service depends on to provide book, movie, and song recommendations."); Console.WriteLine( "\t* An EC2 launch template that defines EC2 instances that each contain a Python web server."); Console.WriteLine( "\t* An EC2 Auto Scaling group that manages EC2 instances across several Availability Zones."); Console.WriteLine( "\t* An Elastic Load Balancing (ELB) load balancer that targets the Auto Scaling group to distribute requests."); Console.WriteLine(new string('-', 80)); Console.WriteLine("Press Enter when you're ready to start deploying resources."); if (interactive) Console.ReadLine(); // Create and populate the DynamoDB table. var databaseTableName = _configuration["databaseName"]; var recommendationsPath = Path.Join(_configuration["resourcePath"], "recommendations_objects.json"); Console.WriteLine($"Creating and populating a DynamoDB table named {databaseTableName}."); await _recommendations.CreateDatabaseWithName(databaseTableName); await _recommendations.PopulateDatabase(databaseTableName, recommendationsPath); Console.WriteLine(new string('-', 80)); // Create the EC2 Launch Template. Console.WriteLine( $"Creating an EC2 launch template that runs 'server_startup_script.sh' when an instance starts.\n" + "\nThis script starts a Python web server defined in the `server.py` script. The web server\n" + "listens to HTTP requests on port 80 and responds to requests to '/' and to '/healthcheck'.\n" + "For demo purposes, this server is run as the root user. In production, the best practice is to\n" + "run a web server, such as Apache, with least-privileged credentials."); Console.WriteLine( "\nThe template also defines an IAM policy that each instance uses to assume a role that grants\n" + "permissions to access the DynamoDB recommendation table and Systems Manager parameters\n" + "that control the flow of the demo."); var startupScriptPath = Path.Join(_configuration["resourcePath"], "server_startup_script.sh"); var instancePolicyPath = Path.Join(_configuration["resourcePath"], "instance_policy.json"); await _autoScalerWrapper.CreateTemplate(startupScriptPath, instancePolicyPath); Console.WriteLine(new string('-', 80)); Console.WriteLine( "Creating an EC2 Auto Scaling group that maintains three EC2 instances, each in a different\n" + "Availability Zone.\n"); var zones = await _autoScalerWrapper.DescribeAvailabilityZones(); await _autoScalerWrapper.CreateGroupOfSize(3, _autoScalerWrapper.GroupName, zones); Console.WriteLine(new string('-', 80)); Console.WriteLine( "At this point, you have EC2 instances created. Once each instance starts, it listens for\n" + "HTTP requests. You can see these instances in the console or continue with the demo.\n"); Console.WriteLine(new string('-', 80)); Console.WriteLine("Press Enter when you're ready to continue."); if (interactive) Console.ReadLine(); Console.WriteLine("Creating variables that control the flow of the demo."); await _smParameterWrapper.Reset(); Console.WriteLine( "\nCreating an Elastic Load Balancing target group and load balancer. The target group\n" + "defines how the load balancer connects to instances. The load balancer provides a\n" + "single endpoint where clients connect and dispatches requests to instances in the group."); var defaultVpc = await _autoScalerWrapper.GetDefaultVpc(); var subnets = await _autoScalerWrapper.GetAllVpcSubnetsForZones(defaultVpc.VpcId, zones); var subnetIds = subnets.Select(s => s.SubnetId).ToList(); var targetGroup = await _elasticLoadBalancerWrapper.CreateTargetGroupOnVpc(_elasticLoadBalancerWrapper.TargetGroupName, protocol, port, defaultVpc.VpcId); await _elasticLoadBalancerWrapper.CreateLoadBalancerAndListener(_elasticLoadBalancerWrapper.LoadBalancerName, subnetIds, targetGroup); await _autoScalerWrapper.AttachLoadBalancerToGroup(_autoScalerWrapper.GroupName, targetGroup.TargetGroupArn); Console.WriteLine("\nVerifying access to the load balancer endpoint..."); var endPoint = await _elasticLoadBalancerWrapper.GetEndpointForLoadBalancerByName(_elasticLoadBalancerWrapper.LoadBalancerName); var loadBalancerAccess = await _elasticLoadBalancerWrapper.VerifyLoadBalancerEndpoint(endPoint); if (!loadBalancerAccess) { Console.WriteLine("\nCouldn't connect to the load balancer, verifying that the port is open..."); var ipString = await _httpClient.GetStringAsync("https://checkip.amazonaws.com"); ipString = ipString.Trim(); var defaultSecurityGroup = await _autoScalerWrapper.GetDefaultSecurityGroupForVpc(defaultVpc); var portIsOpen = _autoScalerWrapper.VerifyInboundPortForGroup(defaultSecurityGroup, port, ipString); var sshPortIsOpen = _autoScalerWrapper.VerifyInboundPortForGroup(defaultSecurityGroup, sshPort, ipString); if (!portIsOpen) { Console.WriteLine( "\nFor this example to work, the default security group for your default VPC must\n" + "allows access from this computer. You can either add it automatically from this\n" + "example or add it yourself using the AWS Management Console.\n"); if (!interactive || GetYesNoResponse( "Do you want to add a rule to the security group to allow inbound traffic from your computer's IP address?")) { await _autoScalerWrapper.OpenInboundPort(defaultSecurityGroup.GroupId, port, ipString); } } if (!sshPortIsOpen) { if (!interactive || GetYesNoResponse( "Do you want to add a rule to the security group to allow inbound SSH traffic for debugging from your computer's IP address?")) { await _autoScalerWrapper.OpenInboundPort(defaultSecurityGroup.GroupId, sshPort, ipString); } } loadBalancerAccess = await _elasticLoadBalancerWrapper.VerifyLoadBalancerEndpoint(endPoint); } if (loadBalancerAccess) { Console.WriteLine("Your load balancer is ready. You can access it by browsing to:"); Console.WriteLine($"\thttp://{endPoint}\n"); } else { Console.WriteLine( "\nCouldn't get a successful response from the load balancer endpoint. Troubleshoot by\n" + "manually verifying that your VPC and security group are configured correctly and that\n" + "you can successfully make a GET request to the load balancer endpoint:\n"); Console.WriteLine($"\thttp://{endPoint}\n"); } Console.WriteLine(new string('-', 80)); Console.WriteLine("Press Enter when you're ready to continue with the demo."); if (interactive) Console.ReadLine(); return true; } /// <summary> /// Demonstrate the steps of the scenario. /// </summary> /// <param name="interactive">True to run as an interactive scenario.</param> /// <returns>Async task.</returns> public static async Task<bool> Demo(bool interactive) { var ssmOnlyPolicy = Path.Join(_configuration["resourcePath"], "ssm_only_policy.json"); Console.WriteLine(new string('-', 80)); Console.WriteLine("Resetting parameters to starting values for demo."); await _smParameterWrapper.Reset(); Console.WriteLine("\nThis part of the demonstration shows how to toggle different parts of the system\n" + "to create situations where the web service fails, and shows how using a resilient\n" + "architecture can keep the web service running in spite of these failures."); Console.WriteLine(new string('-', 88)); Console.WriteLine("At the start, the load balancer endpoint returns recommendations and reports that all targets are healthy."); if (interactive) await DemoActionChoices(); Console.WriteLine($"The web service running on the EC2 instances gets recommendations by querying a DynamoDB table.\n" + $"The table name is contained in a Systems Manager parameter named '{_smParameterWrapper.TableParameter}'.\n" + $"To simulate a failure of the recommendation service, let's set this parameter to name a non-existent table.\n"); await _smParameterWrapper.PutParameterByName(_smParameterWrapper.TableParameter, "this-is-not-a-table"); Console.WriteLine("\nNow, sending a GET request to the load balancer endpoint returns a failure code. But, the service reports as\n" + "healthy to the load balancer because shallow health checks don't check for failure of the recommendation service."); if (interactive) await DemoActionChoices(); Console.WriteLine("Instead of failing when the recommendation service fails, the web service can return a static response."); Console.WriteLine("While this is not a perfect solution, it presents the customer with a somewhat better experience than failure."); await _smParameterWrapper.PutParameterByName(_smParameterWrapper.FailureResponseParameter, "static"); Console.WriteLine("\nNow, sending a GET request to the load balancer endpoint returns a static response."); Console.WriteLine("The service still reports as healthy because health checks are still shallow."); if (interactive) await DemoActionChoices(); Console.WriteLine("Let's reinstate the recommendation service.\n"); await _smParameterWrapper.PutParameterByName(_smParameterWrapper.TableParameter, _smParameterWrapper.TableName); Console.WriteLine( "\nLet's also substitute bad credentials for one of the instances in the target group so that it can't\n" + "access the DynamoDB recommendation table.\n" ); await _autoScalerWrapper.CreateInstanceProfileWithName( _autoScalerWrapper.BadCredsPolicyName, _autoScalerWrapper.BadCredsRoleName, _autoScalerWrapper.BadCredsProfileName, ssmOnlyPolicy, new List<string> { "AmazonSSMManagedInstanceCore" } ); var instances = await _autoScalerWrapper.GetInstancesByGroupName(_autoScalerWrapper.GroupName); var badInstanceId = instances.First(); var instanceProfile = await _autoScalerWrapper.GetInstanceProfile(badInstanceId); Console.WriteLine( $"Replacing the profile for instance {badInstanceId} with a profile that contains\n" + "bad credentials...\n" ); await _autoScalerWrapper.ReplaceInstanceProfile( badInstanceId, _autoScalerWrapper.BadCredsProfileName, instanceProfile.AssociationId ); Console.WriteLine( "Now, sending a GET request to the load balancer endpoint returns either a recommendation or a static response,\n" + "depending on which instance is selected by the load balancer.\n" ); if (interactive) await DemoActionChoices(); Console.WriteLine("\nLet's implement a deep health check. For this demo, a deep health check tests whether"); Console.WriteLine("the web service can access the DynamoDB table that it depends on for recommendations. Note that"); Console.WriteLine("the deep health check is only for ELB routing and not for Auto Scaling instance health."); Console.WriteLine("This kind of deep health check is not recommended for Auto Scaling instance health, because it"); Console.WriteLine("risks accidental termination of all instances in the Auto Scaling group when a dependent service fails."); Console.WriteLine("\nBy implementing deep health checks, the load balancer can detect when one of the instances is failing"); Console.WriteLine("and take that instance out of rotation."); await _smParameterWrapper.PutParameterByName(_smParameterWrapper.HealthCheckParameter, "deep"); Console.WriteLine($"\nNow, checking target health indicates that the instance with bad credentials ({badInstanceId})"); Console.WriteLine("is unhealthy. Note that it might take a minute or two for the load balancer to detect the unhealthy"); Console.WriteLine("instance. Sending a GET request to the load balancer endpoint always returns a recommendation, because"); Console.WriteLine("the load balancer takes unhealthy instances out of its rotation."); if (interactive) await DemoActionChoices(); Console.WriteLine("\nBecause the instances in this demo are controlled by an auto scaler, the simplest way to fix an unhealthy"); Console.WriteLine("instance is to terminate it and let the auto scaler start a new instance to replace it."); await _autoScalerWrapper.TryTerminateInstanceById(badInstanceId); Console.WriteLine($"\nEven while the instance is terminating and the new instance is starting, sending a GET"); Console.WriteLine("request to the web service continues to get a successful recommendation response because"); Console.WriteLine("starts and reports as healthy, it is included in the load balancing rotation."); Console.WriteLine("Note that terminating and replacing an instance typically takes several minutes, during which time you"); Console.WriteLine("can see the changing health check status until the new instance is running and healthy."); if (interactive) await DemoActionChoices(); Console.WriteLine("\nIf the recommendation service fails now, deep health checks mean all instances report as unhealthy."); await _smParameterWrapper.PutParameterByName(_smParameterWrapper.TableParameter, "this-is-not-a-table"); Console.WriteLine($"\nWhen all instances are unhealthy, the load balancer continues to route requests even to"); Console.WriteLine("unhealthy instances, allowing them to fail open and return a static response rather than fail"); Console.WriteLine("closed and report failure to the customer."); if (interactive) await DemoActionChoices(); await _smParameterWrapper.Reset(); Console.WriteLine(new string('-', 80)); return true; } /// <summary> /// Clean up the resources from the scenario. /// </summary> /// <param name="interactive">True to ask the user for cleanup.</param> /// <returns>Async task.</returns> public static async Task<bool> DestroyResources(bool interactive) { Console.WriteLine(new string('-', 80)); Console.WriteLine( "To keep things tidy and to avoid unwanted charges on your account, we can clean up all AWS resources\n" + "that were created for this demo." ); if (!interactive || GetYesNoResponse("Do you want to clean up all demo resources? (y/n) ")) { await _elasticLoadBalancerWrapper.DeleteLoadBalancerByName(_elasticLoadBalancerWrapper.LoadBalancerName); await _elasticLoadBalancerWrapper.DeleteTargetGroupByName(_elasticLoadBalancerWrapper.TargetGroupName); await _autoScalerWrapper.TerminateAndDeleteAutoScalingGroupWithName(_autoScalerWrapper.GroupName); await _autoScalerWrapper.DeleteKeyPairByName(_autoScalerWrapper.KeyPairName); await _autoScalerWrapper.DeleteTemplateByName(_autoScalerWrapper.LaunchTemplateName); await _autoScalerWrapper.DeleteInstanceProfile( _autoScalerWrapper.BadCredsProfileName, _autoScalerWrapper.BadCredsRoleName ); await _recommendations.DestroyDatabaseByName(_recommendations.TableName); } else { Console.WriteLine( "Ok, we'll leave the resources intact.\n" + "Don't forget to delete them when you're done with them or you might incur unexpected charges." ); } Console.WriteLine(new string('-', 80)); return true; }

Auto Scaling と Amazon EC2 のアクションをラップするクラスを作成します。

/// <summary> /// Encapsulates Amazon EC2 Auto Scaling and EC2 management methods. /// </summary> public class AutoScalerWrapper { private readonly IAmazonAutoScaling _amazonAutoScaling; private readonly IAmazonEC2 _amazonEc2; private readonly IAmazonSimpleSystemsManagement _amazonSsm; private readonly IAmazonIdentityManagementService _amazonIam; private readonly string _instanceType = ""; private readonly string _amiParam = ""; private readonly string _launchTemplateName = ""; private readonly string _groupName = ""; private readonly string _instancePolicyName = ""; private readonly string _instanceRoleName = ""; private readonly string _instanceProfileName = ""; private readonly string _badCredsProfileName = ""; private readonly string _badCredsRoleName = ""; private readonly string _badCredsPolicyName = ""; private readonly string _keyPairName = ""; public string GroupName => _groupName; public string KeyPairName => _keyPairName; public string LaunchTemplateName => _launchTemplateName; public string InstancePolicyName => _instancePolicyName; public string BadCredsProfileName => _badCredsProfileName; public string BadCredsRoleName => _badCredsRoleName; public string BadCredsPolicyName => _badCredsPolicyName; /// <summary> /// Constructor for the AutoScalerWrapper. /// </summary> /// <param name="amazonAutoScaling">The injected AutoScaling client.</param> /// <param name="amazonEc2">The injected EC2 client.</param> /// <param name="amazonIam">The injected IAM client.</param> /// <param name="amazonSsm">The injected SSM client.</param> public AutoScalerWrapper( IAmazonAutoScaling amazonAutoScaling, IAmazonEC2 amazonEc2, IAmazonSimpleSystemsManagement amazonSsm, IAmazonIdentityManagementService amazonIam, IConfiguration configuration) { _amazonAutoScaling = amazonAutoScaling; _amazonEc2 = amazonEc2; _amazonSsm = amazonSsm; _amazonIam = amazonIam; var prefix = configuration["resourcePrefix"]; _instanceType = configuration["instanceType"]; _amiParam = configuration["amiParam"]; _launchTemplateName = prefix + "-template"; _groupName = prefix + "-group"; _instancePolicyName = prefix + "-pol"; _instanceRoleName = prefix + "-role"; _instanceProfileName = prefix + "-prof"; _badCredsPolicyName = prefix + "-bc-pol"; _badCredsRoleName = prefix + "-bc-role"; _badCredsProfileName = prefix + "-bc-prof"; _keyPairName = prefix + "-key-pair"; } /// <summary> /// Create a policy, role, and profile that is associated with instances with a specified name. /// An instance's associated profile defines a role that is assumed by the /// instance.The role has attached policies that specify the AWS permissions granted to /// clients that run on the instance. /// </summary> /// <param name="policyName">Name to use for the policy.</param> /// <param name="roleName">Name to use for the role.</param> /// <param name="profileName">Name to use for the profile.</param> /// <param name="ssmOnlyPolicyFile">Path to a policy file for SSM.</param> /// <param name="awsManagedPolicies">AWS Managed policies to be attached to the role.</param> /// <returns>The Arn of the profile.</returns> public async Task<string> CreateInstanceProfileWithName( string policyName, string roleName, string profileName, string ssmOnlyPolicyFile, List<string>? awsManagedPolicies = null) { var assumeRoleDoc = "{" + "\"Version\": \"2012-10-17\"," + "\"Statement\": [{" + "\"Effect\": \"Allow\"," + "\"Principal\": {" + "\"Service\": [" + "\"ec2.amazonaws.com\"" + "]" + "}," + "\"Action\": \"sts:AssumeRole\"" + "}]" + "}"; var policyDocument = await File.ReadAllTextAsync(ssmOnlyPolicyFile); var policyArn = ""; try { var createPolicyResult = await _amazonIam.CreatePolicyAsync( new CreatePolicyRequest { PolicyName = policyName, PolicyDocument = policyDocument }); policyArn = createPolicyResult.Policy.Arn; } catch (EntityAlreadyExistsException) { // The policy already exists, so we look it up to get the Arn. var policiesPaginator = _amazonIam.Paginators.ListPolicies( new ListPoliciesRequest() { Scope = PolicyScopeType.Local }); // Get the entire list using the paginator. await foreach (var policy in policiesPaginator.Policies) { if (policy.PolicyName.Equals(policyName)) { policyArn = policy.Arn; } } if (policyArn == null) { throw new InvalidOperationException("Policy not found"); } } try { await _amazonIam.CreateRoleAsync(new CreateRoleRequest() { RoleName = roleName, AssumeRolePolicyDocument = assumeRoleDoc, }); await _amazonIam.AttachRolePolicyAsync(new AttachRolePolicyRequest() { RoleName = roleName, PolicyArn = policyArn }); if (awsManagedPolicies != null) { foreach (var awsPolicy in awsManagedPolicies) { await _amazonIam.AttachRolePolicyAsync(new AttachRolePolicyRequest() { PolicyArn = $"arn:aws:iam::aws:policy/{awsPolicy}", RoleName = roleName }); } } } catch (EntityAlreadyExistsException) { Console.WriteLine("Role already exists."); } string profileArn = ""; try { var profileCreateResponse = await _amazonIam.CreateInstanceProfileAsync( new CreateInstanceProfileRequest() { InstanceProfileName = profileName }); // Allow time for the profile to be ready. profileArn = profileCreateResponse.InstanceProfile.Arn; Thread.Sleep(10000); await _amazonIam.AddRoleToInstanceProfileAsync( new AddRoleToInstanceProfileRequest() { InstanceProfileName = profileName, RoleName = roleName }); } catch (EntityAlreadyExistsException) { Console.WriteLine("Policy already exists."); var profileGetResponse = await _amazonIam.GetInstanceProfileAsync( new GetInstanceProfileRequest() { InstanceProfileName = profileName }); profileArn = profileGetResponse.InstanceProfile.Arn; } return profileArn; } /// <summary> /// Create a new key pair and save the file. /// </summary> /// <param name="newKeyPairName">The name of the new key pair.</param> /// <returns>Async task.</returns> public async Task CreateKeyPair(string newKeyPairName) { try { var keyResponse = await _amazonEc2.CreateKeyPairAsync( new CreateKeyPairRequest() { KeyName = newKeyPairName }); await File.WriteAllTextAsync($"{newKeyPairName}.pem", keyResponse.KeyPair.KeyMaterial); Console.WriteLine($"Created key pair {newKeyPairName}."); } catch (AlreadyExistsException) { Console.WriteLine("Key pair already exists."); } } /// <summary> /// Delete the key pair and file by name. /// </summary> /// <param name="deleteKeyPairName">The key pair to delete.</param> /// <returns>Async task.</returns> public async Task DeleteKeyPairByName(string deleteKeyPairName) { try { await _amazonEc2.DeleteKeyPairAsync( new DeleteKeyPairRequest() { KeyName = deleteKeyPairName }); File.Delete($"{deleteKeyPairName}.pem"); } catch (FileNotFoundException) { Console.WriteLine($"Key pair {deleteKeyPairName} not found."); } } /// <summary> /// Creates an Amazon EC2 launch template to use with Amazon EC2 Auto Scaling. /// The launch template specifies a Bash script in its user data field that runs after /// the instance is started. This script installs the Python packages and starts a Python /// web server on the instance. /// </summary> /// <param name="startupScriptPath">The path to a Bash script file that is run.</param> /// <param name="instancePolicyPath">The path to a permissions policy to create and attach to the profile.</param> /// <returns>The template object.</returns> public async Task<Amazon.EC2.Model.LaunchTemplate> CreateTemplate(string startupScriptPath, string instancePolicyPath) { await CreateKeyPair(_keyPairName); await CreateInstanceProfileWithName(_instancePolicyName, _instanceRoleName, _instanceProfileName, instancePolicyPath); var startServerText = await File.ReadAllTextAsync(startupScriptPath); var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(startServerText); var amiLatest = await _amazonSsm.GetParameterAsync( new GetParameterRequest() { Name = _amiParam }); var amiId = amiLatest.Parameter.Value; var launchTemplateResponse = await _amazonEc2.CreateLaunchTemplateAsync( new CreateLaunchTemplateRequest() { LaunchTemplateName = _launchTemplateName, LaunchTemplateData = new RequestLaunchTemplateData() { InstanceType = _instanceType, ImageId = amiId, IamInstanceProfile = new LaunchTemplateIamInstanceProfileSpecificationRequest() { Name = _instanceProfileName }, KeyName = _keyPairName, UserData = System.Convert.ToBase64String(plainTextBytes) } }); return launchTemplateResponse.LaunchTemplate; } /// <summary> /// Get a list of Availability Zones in the AWS Region of the Amazon EC2 Client. /// </summary> /// <returns>A list of availability zones.</returns> public async Task<List<string>> DescribeAvailabilityZones() { var zoneResponse = await _amazonEc2.DescribeAvailabilityZonesAsync( new DescribeAvailabilityZonesRequest()); return zoneResponse.AvailabilityZones.Select(z => z.ZoneName).ToList(); } /// <summary> /// Create an EC2 Auto Scaling group of a specified size and name. /// </summary> /// <param name="groupSize">The size for the group.</param> /// <param name="groupName">The name for the group.</param> /// <param name="availabilityZones">The availability zones for the group.</param> /// <returns>Async task.</returns> public async Task CreateGroupOfSize(int groupSize, string groupName, List<string> availabilityZones) { try { await _amazonAutoScaling.CreateAutoScalingGroupAsync( new CreateAutoScalingGroupRequest() { AutoScalingGroupName = groupName, AvailabilityZones = availabilityZones, LaunchTemplate = new Amazon.AutoScaling.Model.LaunchTemplateSpecification() { LaunchTemplateName = _launchTemplateName, Version = "$Default" }, MaxSize = groupSize, MinSize = groupSize }); Console.WriteLine($"Created EC2 Auto Scaling group {groupName} with size {groupSize}."); } catch (EntityAlreadyExistsException) { Console.WriteLine($"EC2 Auto Scaling group {groupName} already exists."); } } /// <summary> /// Get the default VPC for the account. /// </summary> /// <returns>The default VPC object.</returns> public async Task<Vpc> GetDefaultVpc() { var vpcResponse = await _amazonEc2.DescribeVpcsAsync( new DescribeVpcsRequest() { Filters = new List<Amazon.EC2.Model.Filter>() { new ("is-default", new List<string>() { "true" }) } }); return vpcResponse.Vpcs[0]; } /// <summary> /// Get all the subnets for a Vpc in a set of availability zones. /// </summary> /// <param name="vpcId">The Id of the Vpc.</param> /// <param name="availabilityZones">The list of availability zones.</param> /// <returns>The collection of subnet objects.</returns> public async Task<List<Subnet>> GetAllVpcSubnetsForZones(string vpcId, List<string> availabilityZones) { var subnets = new List<Subnet>(); var subnetPaginator = _amazonEc2.Paginators.DescribeSubnets( new DescribeSubnetsRequest() { Filters = new List<Amazon.EC2.Model.Filter>() { new ("vpc-id", new List<string>() { vpcId}), new ("availability-zone", availabilityZones), new ("default-for-az", new List<string>() { "true" }) } }); // Get the entire list using the paginator. await foreach (var subnet in subnetPaginator.Subnets) { subnets.Add(subnet); } return subnets; } /// <summary> /// Delete a launch template by name. /// </summary> /// <param name="templateName">The name of the template to delete.</param> /// <returns>Async task.</returns> public async Task DeleteTemplateByName(string templateName) { try { await _amazonEc2.DeleteLaunchTemplateAsync( new DeleteLaunchTemplateRequest() { LaunchTemplateName = templateName }); } catch (AmazonClientException) { Console.WriteLine($"Unable to delete template {templateName}."); } } /// <summary> /// Detaches a role from an instance profile, detaches policies from the role, /// and deletes all the resources. /// </summary> /// <param name="profileName">The name of the profile to delete.</param> /// <param name="roleName">The name of the role to delete.</param> /// <returns>Async task.</returns> public async Task DeleteInstanceProfile(string profileName, string roleName) { try { await _amazonIam.RemoveRoleFromInstanceProfileAsync( new RemoveRoleFromInstanceProfileRequest() { InstanceProfileName = profileName, RoleName = roleName }); await _amazonIam.DeleteInstanceProfileAsync( new DeleteInstanceProfileRequest() { InstanceProfileName = profileName }); var attachedPolicies = await _amazonIam.ListAttachedRolePoliciesAsync( new ListAttachedRolePoliciesRequest() { RoleName = roleName }); foreach (var policy in attachedPolicies.AttachedPolicies) { await _amazonIam.DetachRolePolicyAsync( new DetachRolePolicyRequest() { RoleName = roleName, PolicyArn = policy.PolicyArn }); // Delete the custom policies only. if (!policy.PolicyArn.StartsWith("arn:aws:iam::aws")) { await _amazonIam.DeletePolicyAsync( new Amazon.IdentityManagement.Model.DeletePolicyRequest() { PolicyArn = policy.PolicyArn }); } } await _amazonIam.DeleteRoleAsync( new DeleteRoleRequest() { RoleName = roleName }); } catch (NoSuchEntityException) { Console.WriteLine($"Instance profile {profileName} does not exist."); } } /// <summary> /// Gets data about the instances in an EC2 Auto Scaling group by its group name. /// </summary> /// <param name="group">The name of the auto scaling group.</param> /// <returns>A collection of instance Ids.</returns> public async Task<IEnumerable<string>> GetInstancesByGroupName(string group) { var instanceResponse = await _amazonAutoScaling.DescribeAutoScalingGroupsAsync( new DescribeAutoScalingGroupsRequest() { AutoScalingGroupNames = new List<string>() { group } }); var instanceIds = instanceResponse.AutoScalingGroups.SelectMany( g => g.Instances.Select(i => i.InstanceId)); return instanceIds; } /// <summary> /// Get the instance profile association data for an instance. /// </summary> /// <param name="instanceId">The Id of the instance.</param> /// <returns>Instance profile associations data.</returns> public async Task<IamInstanceProfileAssociation> GetInstanceProfile(string instanceId) { var response = await _amazonEc2.DescribeIamInstanceProfileAssociationsAsync( new DescribeIamInstanceProfileAssociationsRequest() { Filters = new List<Amazon.EC2.Model.Filter>() { new ("instance-id", new List<string>() { instanceId }) }, }); return response.IamInstanceProfileAssociations[0]; } /// <summary> /// Replace the profile associated with a running instance. After the profile is replaced, the instance /// is rebooted to ensure that it uses the new profile. When the instance is ready, Systems Manager is /// used to restart the Python web server. /// </summary> /// <param name="instanceId">The Id of the instance to update.</param> /// <param name="credsProfileName">The name of the new profile to associate with the specified instance.</param> /// <param name="associationId">The Id of the existing profile association for the instance.</param> /// <returns>Async task.</returns> public async Task ReplaceInstanceProfile(string instanceId, string credsProfileName, string associationId) { await _amazonEc2.ReplaceIamInstanceProfileAssociationAsync( new ReplaceIamInstanceProfileAssociationRequest() { AssociationId = associationId, IamInstanceProfile = new IamInstanceProfileSpecification() { Name = credsProfileName } }); // Allow time before resetting. Thread.Sleep(25000); var instanceReady = false; var retries = 5; while (retries-- > 0 && !instanceReady) { await _amazonEc2.RebootInstancesAsync( new RebootInstancesRequest(new List<string>() { instanceId })); Thread.Sleep(10000); var instancesPaginator = _amazonSsm.Paginators.DescribeInstanceInformation( new DescribeInstanceInformationRequest()); // Get the entire list using the paginator. await foreach (var instance in instancesPaginator.InstanceInformationList) { instanceReady = instance.InstanceId == instanceId; if (instanceReady) { break; } } } Console.WriteLine($"Sending restart command to instance {instanceId}"); await _amazonSsm.SendCommandAsync( new SendCommandRequest() { InstanceIds = new List<string>() { instanceId }, DocumentName = "AWS-RunShellScript", Parameters = new Dictionary<string, List<string>>() { {"commands", new List<string>() { "cd / && sudo python3 server.py 80" }} } }); Console.WriteLine($"Restarted the web server on instance {instanceId}"); } /// <summary> /// Try to terminate an instance by its Id. /// </summary> /// <param name="instanceId">The Id of the instance to terminate.</param> /// <returns>Async task.</returns> public async Task TryTerminateInstanceById(string instanceId) { var stopping = false; Console.WriteLine($"Stopping {instanceId}..."); while (!stopping) { try { await _amazonAutoScaling.TerminateInstanceInAutoScalingGroupAsync( new TerminateInstanceInAutoScalingGroupRequest() { InstanceId = instanceId, ShouldDecrementDesiredCapacity = false }); stopping = true; } catch (ScalingActivityInProgressException) { Console.WriteLine($"Scaling activity in progress for {instanceId}. Waiting..."); Thread.Sleep(10000); } } } /// <summary> /// Tries to delete the EC2 Auto Scaling group. If the group is in use or in progress, /// waits and retries until the group is successfully deleted. /// </summary> /// <param name="groupName">The name of the group to try to delete.</param> /// <returns>Async task.</returns> public async Task TryDeleteGroupByName(string groupName) { var stopped = false; while (!stopped) { try { await _amazonAutoScaling.DeleteAutoScalingGroupAsync( new DeleteAutoScalingGroupRequest() { AutoScalingGroupName = groupName }); stopped = true; } catch (Exception e) when ((e is ScalingActivityInProgressException) || (e is Amazon.AutoScaling.Model.ResourceInUseException)) { Console.WriteLine($"Some instances are still running. Waiting..."); Thread.Sleep(10000); } } } /// <summary> /// Terminate instances and delete the Auto Scaling group by name. /// </summary> /// <param name="groupName">The name of the group to delete.</param> /// <returns>Async task.</returns> public async Task TerminateAndDeleteAutoScalingGroupWithName(string groupName) { var describeGroupsResponse = await _amazonAutoScaling.DescribeAutoScalingGroupsAsync( new DescribeAutoScalingGroupsRequest() { AutoScalingGroupNames = new List<string>() { groupName } }); if (describeGroupsResponse.AutoScalingGroups.Any()) { // Update the size to 0. await _amazonAutoScaling.UpdateAutoScalingGroupAsync( new UpdateAutoScalingGroupRequest() { AutoScalingGroupName = groupName, MinSize = 0 }); var group = describeGroupsResponse.AutoScalingGroups[0]; foreach (var instance in group.Instances) { await TryTerminateInstanceById(instance.InstanceId); } await TryDeleteGroupByName(groupName); } else { Console.WriteLine($"No groups found with name {groupName}."); } } /// <summary> /// Get the default security group for a specified Vpc. /// </summary> /// <param name="vpc">The Vpc to search.</param> /// <returns>The default security group.</returns> public async Task<SecurityGroup> GetDefaultSecurityGroupForVpc(Vpc vpc) { var groupResponse = await _amazonEc2.DescribeSecurityGroupsAsync( new DescribeSecurityGroupsRequest() { Filters = new List<Amazon.EC2.Model.Filter>() { new ("group-name", new List<string>() { "default" }), new ("vpc-id", new List<string>() { vpc.VpcId }) } }); return groupResponse.SecurityGroups[0]; } /// <summary> /// Verify the default security group of a Vpc allows ingress from the calling computer. /// This can be done by allowing ingress from this computer's IP address. /// In some situations, such as connecting from a corporate network, you must instead specify /// a prefix list Id. You can also temporarily open the port to any IP address while running this example. /// If you do, be sure to remove public access when you're done. /// </summary> /// <param name="vpc">The group to check.</param> /// <param name="port">The port to verify.</param> /// <param name="ipAddress">This computer's IP address.</param> /// <returns>True if the ip address is allowed on the group.</returns> public bool VerifyInboundPortForGroup(SecurityGroup group, int port, string ipAddress) { var portIsOpen = false; foreach (var ipPermission in group.IpPermissions) { if (ipPermission.FromPort == port) { foreach (var ipRange in ipPermission.Ipv4Ranges) { var cidr = ipRange.CidrIp; if (cidr.StartsWith(ipAddress) || cidr == "0.0.0.0/0") { portIsOpen = true; } } if (ipPermission.PrefixListIds.Any()) { portIsOpen = true; } if (!portIsOpen) { Console.WriteLine("The inbound rule does not appear to be open to either this computer's IP\n" + "address, to all IP addresses (0.0.0.0/0), or to a prefix list ID."); } else { break; } } } return portIsOpen; } /// <summary> /// Add an ingress rule to the specified security group that allows access on the /// specified port from the specified IP address. /// </summary> /// <param name="groupId">The Id of the security group to modify.</param> /// <param name="port">The port to open.</param> /// <param name="ipAddress">The IP address to allow access.</param> /// <returns>Async task.</returns> public async Task OpenInboundPort(string groupId, int port, string ipAddress) { await _amazonEc2.AuthorizeSecurityGroupIngressAsync( new AuthorizeSecurityGroupIngressRequest() { GroupId = groupId, IpPermissions = new List<IpPermission>() { new IpPermission() { FromPort = port, ToPort = port, IpProtocol = "tcp", Ipv4Ranges = new List<IpRange>() { new IpRange() { CidrIp = $"{ipAddress}/32" } } } } }); } /// <summary> /// Attaches an Elastic Load Balancing (ELB) target group to this EC2 Auto Scaling group. /// The /// </summary> /// <param name="autoScalingGroupName">The name of the Auto Scaling group.</param> /// <param name="targetGroupArn">The Arn for the target group.</param> /// <returns>Async task.</returns> public async Task AttachLoadBalancerToGroup(string autoScalingGroupName, string targetGroupArn) { await _amazonAutoScaling.AttachLoadBalancerTargetGroupsAsync( new AttachLoadBalancerTargetGroupsRequest() { AutoScalingGroupName = autoScalingGroupName, TargetGroupARNs = new List<string>() { targetGroupArn } }); } }

Elastic Load Balancing のアクションをラップするクラスを作成します。

/// <summary> /// Encapsulates Elastic Load Balancer actions. /// </summary> public class ElasticLoadBalancerWrapper { private readonly IAmazonElasticLoadBalancingV2 _amazonElasticLoadBalancingV2; private string? _endpoint = null; private readonly string _targetGroupName = ""; private readonly string _loadBalancerName = ""; HttpClient _httpClient = new(); public string TargetGroupName => _targetGroupName; public string LoadBalancerName => _loadBalancerName; /// <summary> /// Constructor for the Elastic Load Balancer wrapper. /// </summary> /// <param name="amazonElasticLoadBalancingV2">The injected load balancing v2 client.</param> /// <param name="configuration">The injected configuration.</param> public ElasticLoadBalancerWrapper( IAmazonElasticLoadBalancingV2 amazonElasticLoadBalancingV2, IConfiguration configuration) { _amazonElasticLoadBalancingV2 = amazonElasticLoadBalancingV2; var prefix = configuration["resourcePrefix"]; _targetGroupName = prefix + "-tg"; _loadBalancerName = prefix + "-lb"; } /// <summary> /// Get the HTTP Endpoint of a load balancer by its name. /// </summary> /// <param name="loadBalancerName">The name of the load balancer.</param> /// <returns>The HTTP endpoint.</returns> public async Task<string> GetEndpointForLoadBalancerByName(string loadBalancerName) { if (_endpoint == null) { var endpointResponse = await _amazonElasticLoadBalancingV2.DescribeLoadBalancersAsync( new DescribeLoadBalancersRequest() { Names = new List<string>() { loadBalancerName } }); _endpoint = endpointResponse.LoadBalancers[0].DNSName; } return _endpoint; } /// <summary> /// Return the GET response for an endpoint as text. /// </summary> /// <param name="endpoint">The endpoint for the request.</param> /// <returns>The request response.</returns> public async Task<string> GetEndPointResponse(string endpoint) { var endpointResponse = await _httpClient.GetAsync($"http://{endpoint}"); var textResponse = await endpointResponse.Content.ReadAsStringAsync(); return textResponse!; } /// <summary> /// Get the target health for a group by name. /// </summary> /// <param name="groupName">The name of the group.</param> /// <returns>The collection of health descriptions.</returns> public async Task<List<TargetHealthDescription>> CheckTargetHealthForGroup(string groupName) { List<TargetHealthDescription> result = null!; try { var groupResponse = await _amazonElasticLoadBalancingV2.DescribeTargetGroupsAsync( new DescribeTargetGroupsRequest() { Names = new List<string>() { groupName } }); var healthResponse = await _amazonElasticLoadBalancingV2.DescribeTargetHealthAsync( new DescribeTargetHealthRequest() { TargetGroupArn = groupResponse.TargetGroups[0].TargetGroupArn }); ; result = healthResponse.TargetHealthDescriptions; } catch (TargetGroupNotFoundException) { Console.WriteLine($"Target group {groupName} not found."); } return result; } /// <summary> /// Create an Elastic Load Balancing target group. The target group specifies how the load balancer forwards /// requests to instances in the group and how instance health is checked. /// /// To speed up this demo, the health check is configured with shortened times and lower thresholds. In production, /// you might want to decrease the sensitivity of your health checks to avoid unwanted failures. /// </summary> /// <param name="groupName">The name for the group.</param> /// <param name="protocol">The protocol, such as HTTP.</param> /// <param name="port">The port to use to forward requests, such as 80.</param> /// <param name="vpcId">The Id of the Vpc in which the load balancer exists.</param> /// <returns>The new TargetGroup object.</returns> public async Task<TargetGroup> CreateTargetGroupOnVpc(string groupName, ProtocolEnum protocol, int port, string vpcId) { var createResponse = await _amazonElasticLoadBalancingV2.CreateTargetGroupAsync( new CreateTargetGroupRequest() { Name = groupName, Protocol = protocol, Port = port, HealthCheckPath = "/healthcheck", HealthCheckIntervalSeconds = 10, HealthCheckTimeoutSeconds = 5, HealthyThresholdCount = 2, UnhealthyThresholdCount = 2, VpcId = vpcId }); var targetGroup = createResponse.TargetGroups[0]; return targetGroup; } /// <summary> /// Create an Elastic Load Balancing load balancer that uses the specified subnets /// and forwards requests to the specified target group. /// </summary> /// <param name="name">The name for the new load balancer.</param> /// <param name="subnetIds">Subnets for the load balancer.</param> /// <param name="targetGroup">Target group for forwarded requests.</param> /// <returns>The new LoadBalancer object.</returns> public async Task<LoadBalancer> CreateLoadBalancerAndListener(string name, List<string> subnetIds, TargetGroup targetGroup) { var createLbResponse = await _amazonElasticLoadBalancingV2.CreateLoadBalancerAsync( new CreateLoadBalancerRequest() { Name = name, Subnets = subnetIds }); var loadBalancerArn = createLbResponse.LoadBalancers[0].LoadBalancerArn; // Wait for load balancer to be available. var loadBalancerReady = false; while (!loadBalancerReady) { try { var describeResponse = await _amazonElasticLoadBalancingV2.DescribeLoadBalancersAsync( new DescribeLoadBalancersRequest() { Names = new List<string>() { name } }); var loadBalancerState = describeResponse.LoadBalancers[0].State.Code; loadBalancerReady = loadBalancerState == LoadBalancerStateEnum.Active; } catch (LoadBalancerNotFoundException) { loadBalancerReady = false; } Thread.Sleep(10000); } // Create the listener. await _amazonElasticLoadBalancingV2.CreateListenerAsync( new CreateListenerRequest() { LoadBalancerArn = loadBalancerArn, Protocol = targetGroup.Protocol, Port = targetGroup.Port, DefaultActions = new List<Action>() { new Action() { Type = ActionTypeEnum.Forward, TargetGroupArn = targetGroup.TargetGroupArn } } }); return createLbResponse.LoadBalancers[0]; } /// <summary> /// Verify this computer can successfully send a GET request to the /// load balancer endpoint. /// </summary> /// <param name="endpoint">The endpoint to check.</param> /// <returns>True if successful.</returns> public async Task<bool> VerifyLoadBalancerEndpoint(string endpoint) { var success = false; var retries = 3; while (!success && retries > 0) { try { var endpointResponse = await _httpClient.GetAsync($"http://{endpoint}"); Console.WriteLine($"Response: {endpointResponse.StatusCode}."); if (endpointResponse.IsSuccessStatusCode) { success = true; } else { retries = 0; } } catch (HttpRequestException) { Console.WriteLine("Connection error, retrying..."); retries--; Thread.Sleep(10000); } } return success; } /// <summary> /// Delete a load balancer by its specified name. /// </summary> /// <param name="name">The name of the load balancer to delete.</param> /// <returns>Async task.</returns> public async Task DeleteLoadBalancerByName(string name) { try { var describeLoadBalancerResponse = await _amazonElasticLoadBalancingV2.DescribeLoadBalancersAsync( new DescribeLoadBalancersRequest() { Names = new List<string>() { name } }); var lbArn = describeLoadBalancerResponse.LoadBalancers[0].LoadBalancerArn; await _amazonElasticLoadBalancingV2.DeleteLoadBalancerAsync( new DeleteLoadBalancerRequest() { LoadBalancerArn = lbArn } ); } catch (LoadBalancerNotFoundException) { Console.WriteLine($"Load balancer {name} not found."); } } /// <summary> /// Delete a TargetGroup by its specified name. /// </summary> /// <param name="groupName">Name of the group to delete.</param> /// <returns>Async task.</returns> public async Task DeleteTargetGroupByName(string groupName) { var done = false; while (!done) { try { var groupResponse = await _amazonElasticLoadBalancingV2.DescribeTargetGroupsAsync( new DescribeTargetGroupsRequest() { Names = new List<string>() { groupName } }); var targetArn = groupResponse.TargetGroups[0].TargetGroupArn; await _amazonElasticLoadBalancingV2.DeleteTargetGroupAsync( new DeleteTargetGroupRequest() { TargetGroupArn = targetArn }); Console.WriteLine($"Deleted load balancing target group {groupName}."); done = true; } catch (TargetGroupNotFoundException) { Console.WriteLine( $"Target group {groupName} not found, could not delete."); done = true; } catch (ResourceInUseException) { Console.WriteLine("Target group not yet released, waiting..."); Thread.Sleep(10000); } } } }

DynamoDB を使用してレコメンデーションサービスをシミュレートするクラスを作成します。

/// <summary> /// Encapsulates a DynamoDB table to use as a service that recommends books, movies, and songs. /// </summary> public class Recommendations { private readonly IAmazonDynamoDB _amazonDynamoDb; private readonly DynamoDBContext _context; private readonly string _tableName; public string TableName => _tableName; /// <summary> /// Constructor for the Recommendations service. /// </summary> /// <param name="amazonDynamoDb">The injected DynamoDb client.</param> /// <param name="configuration">The injected configuration.</param> public Recommendations(IAmazonDynamoDB amazonDynamoDb, IConfiguration configuration) { _amazonDynamoDb = amazonDynamoDb; _context = new DynamoDBContext(_amazonDynamoDb); _tableName = configuration["databaseName"]!; } /// <summary> /// Create the DynamoDb table with a specified name. /// </summary> /// <param name="tableName">The name for the table.</param> /// <returns>True when ready.</returns> public async Task<bool> CreateDatabaseWithName(string tableName) { try { Console.Write($"Creating table {tableName}..."); var createRequest = new CreateTableRequest() { TableName = tableName, AttributeDefinitions = new List<AttributeDefinition>() { new AttributeDefinition() { AttributeName = "MediaType", AttributeType = ScalarAttributeType.S }, new AttributeDefinition() { AttributeName = "ItemId", AttributeType = ScalarAttributeType.N } }, KeySchema = new List<KeySchemaElement>() { new KeySchemaElement() { AttributeName = "MediaType", KeyType = KeyType.HASH }, new KeySchemaElement() { AttributeName = "ItemId", KeyType = KeyType.RANGE } }, ProvisionedThroughput = new ProvisionedThroughput() { ReadCapacityUnits = 5, WriteCapacityUnits = 5 } }; await _amazonDynamoDb.CreateTableAsync(createRequest); // Wait until the table is ACTIVE and then report success. Console.Write("\nWaiting for table to become active..."); var request = new DescribeTableRequest { TableName = tableName }; TableStatus status; do { Thread.Sleep(2000); var describeTableResponse = await _amazonDynamoDb.DescribeTableAsync(request); status = describeTableResponse.Table.TableStatus; Console.Write("."); } while (status != "ACTIVE"); return status == TableStatus.ACTIVE; } catch (ResourceInUseException) { Console.WriteLine($"Table {tableName} already exists."); return false; } } /// <summary> /// Populate the database table with data from a specified path. /// </summary> /// <param name="databaseTableName">The name of the table.</param> /// <param name="recommendationsPath">The path of the recommendations data.</param> /// <returns>Async task.</returns> public async Task PopulateDatabase(string databaseTableName, string recommendationsPath) { var recommendationsText = await File.ReadAllTextAsync(recommendationsPath); var records = JsonSerializer.Deserialize<RecommendationModel[]>(recommendationsText); var batchWrite = _context.CreateBatchWrite<RecommendationModel>(); foreach (var record in records!) { batchWrite.AddPutItem(record); } await batchWrite.ExecuteAsync(); } /// <summary> /// Delete the recommendation table by name. /// </summary> /// <param name="tableName">The name of the recommendation table.</param> /// <returns>Async task.</returns> public async Task DestroyDatabaseByName(string tableName) { try { await _amazonDynamoDb.DeleteTableAsync( new DeleteTableRequest() { TableName = tableName }); Console.WriteLine($"Table {tableName} was deleted."); } catch (ResourceNotFoundException) { Console.WriteLine($"Table {tableName} not found"); } } }

Systems Manager のアクションをラップするクラスを作成します。

/// <summary> /// Encapsulates Systems Manager parameter operations. This example uses these parameters /// to drive the demonstration of resilient architecture, such as failure of a dependency or /// how the service responds to a health check. /// </summary> public class SmParameterWrapper { private readonly IAmazonSimpleSystemsManagement _amazonSimpleSystemsManagement; private readonly string _tableParameter = "doc-example-resilient-architecture-table"; private readonly string _failureResponseParameter = "doc-example-resilient-architecture-failure-response"; private readonly string _healthCheckParameter = "doc-example-resilient-architecture-health-check"; private readonly string _tableName = ""; public string TableParameter => _tableParameter; public string TableName => _tableName; public string HealthCheckParameter => _healthCheckParameter; public string FailureResponseParameter => _failureResponseParameter; /// <summary> /// Constructor for the SmParameterWrapper. /// </summary> /// <param name="amazonSimpleSystemsManagement">The injected Simple Systems Management client.</param> /// <param name="configuration">The injected configuration.</param> public SmParameterWrapper(IAmazonSimpleSystemsManagement amazonSimpleSystemsManagement, IConfiguration configuration) { _amazonSimpleSystemsManagement = amazonSimpleSystemsManagement; _tableName = configuration["databaseName"]!; } /// <summary> /// Reset the Systems Manager parameters to starting values for the demo. /// </summary> /// <returns>Async task.</returns> public async Task Reset() { await this.PutParameterByName(_tableParameter, _tableName); await this.PutParameterByName(_failureResponseParameter, "none"); await this.PutParameterByName(_healthCheckParameter, "shallow"); } /// <summary> /// Set the value of a named Systems Manager parameter. /// </summary> /// <param name="name">The name of the parameter.</param> /// <param name="value">The value to set.</param> /// <returns>Async task.</returns> public async Task PutParameterByName(string name, string value) { await _amazonSimpleSystemsManagement.PutParameterAsync( new PutParameterRequest() { Name = name, Value = value, Overwrite = true }); } }

次のコードサンプルは、以下の操作方法を示しています。

  • 起動テンプレートとアベイラビリティーゾーンを使用して、Amazon EC2 Auto Scaling グループを作成し、実行中のインスタンスに関する情報を取得します。

  • Amazon CloudWatch メトリクス収集を有効にします。

  • グループの希望するキャパシティを更新し、インスタンスが起動するのを待ちます。

  • グループ内の最も古いインスタンスを削除します。

  • ユーザーのリクエストやキャパシティの変更に応じて発生するスケーリングアクティビティを一覧表示します。

  • CloudWatch メトリクスの統計情報を取得し、リソースをクリーンアップします。

AWS SDK for .NET
注記

については、「」を参照してください GitHub。AWS コード例リポジトリ で全く同じ例を見つけて、設定と実行の方法を確認してください。

global using Amazon.AutoScaling; global using Amazon.AutoScaling.Model; global using Amazon.CloudWatch; global using AutoScalingActions; global using Microsoft.Extensions.DependencyInjection; global using Microsoft.Extensions.Hosting; global using Microsoft.Extensions.Logging; global using Microsoft.Extensions.Logging.Console; global using Microsoft.Extensions.Logging.Debug; using Amazon.EC2; using Microsoft.Extensions.Configuration; using Host = Microsoft.Extensions.Hosting.Host; namespace AutoScalingBasics; public class AutoScalingBasics { static async Task Main(string[] args) { // Set up dependency injection for Amazon EC2 Auto Scaling, Amazon // CloudWatch, and Amazon EC2. using var host = Host.CreateDefaultBuilder(args) .ConfigureLogging(logging => logging.AddFilter("System", LogLevel.Debug) .AddFilter<DebugLoggerProvider>("Microsoft", LogLevel.Information) .AddFilter<ConsoleLoggerProvider>("Microsoft", LogLevel.Trace)) .ConfigureServices((_, services) => services.AddAWSService<IAmazonAutoScaling>() .AddAWSService<IAmazonCloudWatch>() .AddAWSService<IAmazonEC2>() .AddTransient<AutoScalingWrapper>() .AddTransient<CloudWatchWrapper>() .AddTransient<EC2Wrapper>() .AddTransient<UIWrapper>() ) .Build(); var autoScalingWrapper = host.Services.GetRequiredService<AutoScalingWrapper>(); var cloudWatchWrapper = host.Services.GetRequiredService<CloudWatchWrapper>(); var ec2Wrapper = host.Services.GetRequiredService<EC2Wrapper>(); var uiWrapper = host.Services.GetRequiredService<UIWrapper>(); var configuration = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("settings.json") // Load test settings from .json file. .AddJsonFile("settings.local.json", true) // Optionally load local settings. .Build(); var imageId = configuration["ImageId"]; var instanceType = configuration["InstanceType"]; var launchTemplateName = configuration["LaunchTemplateName"]; launchTemplateName += Guid.NewGuid().ToString(); // The name of the Auto Scaling group. var groupName = configuration["GroupName"]; uiWrapper.DisplayTitle("Auto Scaling Basics"); uiWrapper.DisplayAutoScalingBasicsDescription(); // Create the launch template and save the template Id to use when deleting the // launch template at the end of the application. var launchTemplateId = await ec2Wrapper.CreateLaunchTemplateAsync(imageId!, instanceType!, launchTemplateName); // Confirm that the template was created by asking for a description of it. await ec2Wrapper.DescribeLaunchTemplateAsync(launchTemplateName); uiWrapper.PressEnter(); var availabilityZones = await ec2Wrapper.ListAvailabilityZonesAsync(); Console.WriteLine($"Creating an Auto Scaling group named {groupName}."); await autoScalingWrapper.CreateAutoScalingGroupAsync( groupName!, launchTemplateName, availabilityZones.First().ZoneName); // Keep checking the details of the new group until its lifecycle state // is "InService". Console.WriteLine($"Waiting for the Auto Scaling group to be active."); List<AutoScalingInstanceDetails> instanceDetails; do { instanceDetails = await autoScalingWrapper.DescribeAutoScalingInstancesAsync(groupName!); } while (instanceDetails.Count <= 0); Console.WriteLine($"Auto scaling group {groupName} successfully created."); Console.WriteLine($"{instanceDetails.Count} instances were created for the group."); // Display the details of the Auto Scaling group. instanceDetails.ForEach(detail => { Console.WriteLine($"Group name: {detail.AutoScalingGroupName}"); }); uiWrapper.PressEnter(); uiWrapper.DisplayTitle("Metrics collection"); Console.WriteLine($"Enable metrics collection for {groupName}"); await autoScalingWrapper.EnableMetricsCollectionAsync(groupName!); // Show the metrics that are collected for the group. // Update the maximum size of the group to three instances. Console.WriteLine("--- Update the Auto Scaling group to increase max size to 3 ---"); int maxSize = 3; await autoScalingWrapper.UpdateAutoScalingGroupAsync(groupName!, launchTemplateName, maxSize); Console.WriteLine("--- Describe all Auto Scaling groups to show the current state of the group ---"); var groups = await autoScalingWrapper.DescribeAutoScalingGroupsAsync(groupName!); uiWrapper.DisplayGroupDetails(groups!); uiWrapper.PressEnter(); uiWrapper.DisplayTitle("Describe account limits"); await autoScalingWrapper.DescribeAccountLimitsAsync(); uiWrapper.WaitABit(60, "Waiting for the resources to be ready."); uiWrapper.DisplayTitle("Set desired capacity"); int desiredCapacity = 2; await autoScalingWrapper.SetDesiredCapacityAsync(groupName!, desiredCapacity); Console.WriteLine("Get the two instance Id values"); // Empty the group before getting the details again. groups!.Clear(); groups = await autoScalingWrapper.DescribeAutoScalingGroupsAsync(groupName!); if (groups is not null) { foreach (AutoScalingGroup group in groups) { Console.WriteLine($"The group name is {group.AutoScalingGroupName}"); Console.WriteLine($"The group ARN is {group.AutoScalingGroupARN}"); var instances = group.Instances; foreach (Amazon.AutoScaling.Model.Instance instance in instances) { Console.WriteLine($"The instance id is {instance.InstanceId}"); Console.WriteLine($"The lifecycle state is {instance.LifecycleState}"); } } } uiWrapper.DisplayTitle("Scaling Activities"); Console.WriteLine("Let's list the scaling activities that have occurred for the group."); var activities = await autoScalingWrapper.DescribeScalingActivitiesAsync(groupName!); if (activities is not null) { activities.ForEach(activity => { Console.WriteLine($"The activity Id is {activity.ActivityId}"); Console.WriteLine($"The activity details are {activity.Details}"); }); } // Display the Amazon CloudWatch metrics that have been collected. var metrics = await cloudWatchWrapper.GetCloudWatchMetricsAsync(groupName!); Console.WriteLine($"Metrics collected for {groupName}:"); metrics.ForEach(metric => { Console.Write($"Metric name: {metric.MetricName}\t"); Console.WriteLine($"Namespace: {metric.Namespace}"); }); var dataPoints = await cloudWatchWrapper.GetMetricStatisticsAsync(groupName!); Console.WriteLine("Details for the metrics collected:"); dataPoints.ForEach(detail => { Console.WriteLine(detail); }); // Disable metrics collection. Console.WriteLine("Disabling the collection of metrics for {groupName}."); var success = await autoScalingWrapper.DisableMetricsCollectionAsync(groupName!); if (success) { Console.WriteLine($"Successfully stopped metrics collection for {groupName}."); } else { Console.WriteLine($"Could not stop metrics collection for {groupName}."); } // Terminate all instances in the group. uiWrapper.DisplayTitle("Terminating Auto Scaling instances"); Console.WriteLine("Now terminating all instances in the Auto Scaling group."); if (groups is not null) { groups.ForEach(group => { // Only delete instances in the AutoScaling group we created. if (group.AutoScalingGroupName == groupName) { group.Instances.ForEach(async instance => { await autoScalingWrapper.TerminateInstanceInAutoScalingGroupAsync(instance.InstanceId); }); } }); } // After all instances are terminated, delete the group. uiWrapper.DisplayTitle("Clean up resources"); Console.WriteLine("Deleting the Auto Scaling group."); await autoScalingWrapper.DeleteAutoScalingGroupAsync(groupName!); // Delete the launch template. var deletedLaunchTemplateName = await ec2Wrapper.DeleteLaunchTemplateAsync(launchTemplateId); if (deletedLaunchTemplateName == launchTemplateName) { Console.WriteLine("Successfully deleted the launch template."); } Console.WriteLine("The demo is now concluded."); } } namespace AutoScalingBasics; /// <summary> /// A class to provide user interface methods for the EC2 AutoScaling Basics /// scenario. /// </summary> public class UIWrapper { public readonly string SepBar = new('-', Console.WindowWidth); /// <summary> /// Describe the steps in the EC2 AutoScaling Basics scenario. /// </summary> public void DisplayAutoScalingBasicsDescription() { Console.WriteLine("This code example performs the following operations:"); Console.WriteLine(" 1. Creates an Amazon EC2 launch template."); Console.WriteLine(" 2. Creates an Auto Scaling group."); Console.WriteLine(" 3. Shows the details of the new Auto Scaling group"); Console.WriteLine(" to show that only one instance was created."); Console.WriteLine(" 4. Enables metrics collection."); Console.WriteLine(" 5. Updates the Auto Scaling group to increase the"); Console.WriteLine(" capacity to three."); Console.WriteLine(" 6. Describes Auto Scaling groups again to show the"); Console.WriteLine(" current state of the group."); Console.WriteLine(" 7. Changes the desired capacity of the Auto Scaling"); Console.WriteLine(" group to use an additional instance."); Console.WriteLine(" 8. Shows that there are now instances in the group."); Console.WriteLine(" 9. Lists the scaling activities that have occurred for the group."); Console.WriteLine("10. Displays the Amazon CloudWatch metrics that have"); Console.WriteLine(" been collected."); Console.WriteLine("11. Disables metrics collection."); Console.WriteLine("12. Terminates all instances in the Auto Scaling group."); Console.WriteLine("13. Deletes the Auto Scaling group."); Console.WriteLine("14. Deletes the Amazon EC2 launch template."); PressEnter(); } /// <summary> /// Display information about the Amazon Ec2 AutoScaling groups passed /// in the list of AutoScalingGroup objects. /// </summary> /// <param name="groups">A list of AutoScalingGroup objects.</param> public void DisplayGroupDetails(List<AutoScalingGroup> groups) { if (groups is null) return; groups.ForEach(group => { Console.WriteLine($"Group name:\t{group.AutoScalingGroupName}"); Console.WriteLine($"Group created:\t{group.CreatedTime}"); Console.WriteLine($"Maximum number of instances:\t{group.MaxSize}"); Console.WriteLine($"Desired number of instances:\t{group.DesiredCapacity}"); }); } /// <summary> /// Display a message and wait until the user presses enter. /// </summary> public void PressEnter() { Console.Write("\nPress <Enter> to continue. "); _ = Console.ReadLine(); Console.WriteLine(); } /// <summary> /// Pad a string with spaces to center it on the console display. /// </summary> /// <param name="strToCenter">The string to be centered.</param> /// <returns>The padded string.</returns> public string CenterString(string strToCenter) { var padAmount = (Console.WindowWidth - strToCenter.Length) / 2; var leftPad = new string(' ', padAmount); return $"{leftPad}{strToCenter}"; } /// <summary> /// Display a line of hyphens, the centered text of the title and another /// line of hyphens. /// </summary> /// <param name="strTitle">The string to be displayed.</param> public void DisplayTitle(string strTitle) { Console.WriteLine(SepBar); Console.WriteLine(CenterString(strTitle)); Console.WriteLine(SepBar); } /// <summary> /// Display a countdown and wait for a number of seconds. /// </summary> /// <param name="numSeconds">The number of seconds to wait.</param> public void WaitABit(int numSeconds, string msg) { Console.WriteLine(msg); // Wait for the requested number of seconds. for (int i = numSeconds; i > 0; i--) { System.Threading.Thread.Sleep(1000); Console.Write($"{i}..."); } PressEnter(); } }

起動テンプレートとメトリクスを管理するためにシナリオによって呼び出される関数を定義します。これらの関数は、Auto Scaling 、Amazon EC2、および CloudWatch アクションをラップします。 Amazon EC2

namespace AutoScalingActions; using Amazon.AutoScaling; using Amazon.AutoScaling.Model; /// <summary> /// A class that includes methods to perform Amazon EC2 Auto Scaling /// actions. /// </summary> public class AutoScalingWrapper { private readonly IAmazonAutoScaling _amazonAutoScaling; /// <summary> /// Constructor for the AutoScalingWrapper class. /// </summary> /// <param name="amazonAutoScaling">The injected Amazon EC2 Auto Scaling client.</param> public AutoScalingWrapper(IAmazonAutoScaling amazonAutoScaling) { _amazonAutoScaling = amazonAutoScaling; } /// <summary> /// Create a new Amazon EC2 Auto Scaling group. /// </summary> /// <param name="groupName">The name to use for the new Auto Scaling /// group.</param> /// <param name="launchTemplateName">The name of the Amazon EC2 Auto Scaling /// launch template to use to create instances in the group.</param> /// <returns>A Boolean value indicating the success of the action.</returns> public async Task<bool> CreateAutoScalingGroupAsync( string groupName, string launchTemplateName, string availabilityZone) { var templateSpecification = new LaunchTemplateSpecification { LaunchTemplateName = launchTemplateName, }; var zoneList = new List<string> { availabilityZone, }; var request = new CreateAutoScalingGroupRequest { AutoScalingGroupName = groupName, AvailabilityZones = zoneList, LaunchTemplate = templateSpecification, MaxSize = 6, MinSize = 1 }; var response = await _amazonAutoScaling.CreateAutoScalingGroupAsync(request); Console.WriteLine($"{groupName} Auto Scaling Group created"); return response.HttpStatusCode == System.Net.HttpStatusCode.OK; } /// <summary> /// Retrieve information about Amazon EC2 Auto Scaling quotas to the /// active AWS account. /// </summary> /// <returns>A Boolean value indicating the success of the action.</returns> public async Task<bool> DescribeAccountLimitsAsync() { var response = await _amazonAutoScaling.DescribeAccountLimitsAsync(); Console.WriteLine("The maximum number of Auto Scaling groups is " + response.MaxNumberOfAutoScalingGroups); Console.WriteLine("The current number of Auto Scaling groups is " + response.NumberOfAutoScalingGroups); return response.HttpStatusCode == System.Net.HttpStatusCode.OK; } /// <summary> /// Retrieve a list of the Amazon EC2 Auto Scaling activities for an /// Amazon EC2 Auto Scaling group. /// </summary> /// <param name="groupName">The name of the Amazon EC2 Auto Scaling group.</param> /// <returns>A list of Amazon EC2 Auto Scaling activities.</returns> public async Task<List<Amazon.AutoScaling.Model.Activity>> DescribeScalingActivitiesAsync( string groupName) { var scalingActivitiesRequest = new DescribeScalingActivitiesRequest { AutoScalingGroupName = groupName, MaxRecords = 10, }; var response = await _amazonAutoScaling.DescribeScalingActivitiesAsync(scalingActivitiesRequest); return response.Activities; } /// <summary> /// Get data about the instances in an Amazon EC2 Auto Scaling group. /// </summary> /// <param name="groupName">The name of the Amazon EC2 Auto Scaling group.</param> /// <returns>A list of Amazon EC2 Auto Scaling details.</returns> public async Task<List<AutoScalingInstanceDetails>> DescribeAutoScalingInstancesAsync( string groupName) { var groups = await DescribeAutoScalingGroupsAsync(groupName); var instanceIds = new List<string>(); groups!.ForEach(group => { if (group.AutoScalingGroupName == groupName) { group.Instances.ForEach(instance => { instanceIds.Add(instance.InstanceId); }); } }); var scalingGroupsRequest = new DescribeAutoScalingInstancesRequest { MaxRecords = 10, InstanceIds = instanceIds, }; var response = await _amazonAutoScaling.DescribeAutoScalingInstancesAsync(scalingGroupsRequest); var instanceDetails = response.AutoScalingInstances; return instanceDetails; } /// <summary> /// Retrieve a list of information about Amazon EC2 Auto Scaling groups. /// </summary> /// <param name="groupName">The name of the Amazon EC2 Auto Scaling group.</param> /// <returns>A list of Amazon EC2 Auto Scaling groups.</returns> public async Task<List<AutoScalingGroup>?> DescribeAutoScalingGroupsAsync( string groupName) { var groupList = new List<string> { groupName, }; var request = new DescribeAutoScalingGroupsRequest { AutoScalingGroupNames = groupList, }; var response = await _amazonAutoScaling.DescribeAutoScalingGroupsAsync(request); var groups = response.AutoScalingGroups; return groups; } /// <summary> /// Delete an Auto Scaling group. /// </summary> /// <param name="groupName">The name of the Amazon EC2 Auto Scaling group.</param> /// <returns>A Boolean value indicating the success of the action.</returns> public async Task<bool> DeleteAutoScalingGroupAsync( string groupName) { var deleteAutoScalingGroupRequest = new DeleteAutoScalingGroupRequest { AutoScalingGroupName = groupName, ForceDelete = true, }; var response = await _amazonAutoScaling.DeleteAutoScalingGroupAsync(deleteAutoScalingGroupRequest); if (response.HttpStatusCode == System.Net.HttpStatusCode.OK) { Console.WriteLine($"You successfully deleted {groupName}"); return true; } Console.WriteLine($"Couldn't delete {groupName}."); return false; } /// <summary> /// Disable the collection of metric data for an Amazon EC2 Auto Scaling /// group. /// </summary> /// <param name="groupName">The name of the Auto Scaling group.</param> /// <returns>A Boolean value that indicates the success or failure of /// the operation.</returns> public async Task<bool> DisableMetricsCollectionAsync(string groupName) { var request = new DisableMetricsCollectionRequest { AutoScalingGroupName = groupName, }; var response = await _amazonAutoScaling.DisableMetricsCollectionAsync(request); return response.HttpStatusCode == System.Net.HttpStatusCode.OK; } /// <summary> /// Enable the collection of metric data for an Auto Scaling group. /// </summary> /// <param name="groupName">The name of the Auto Scaling group.</param> /// <returns>A Boolean value indicating the success of the action.</returns> public async Task<bool> EnableMetricsCollectionAsync(string groupName) { var listMetrics = new List<string> { "GroupMaxSize", }; var collectionRequest = new EnableMetricsCollectionRequest { AutoScalingGroupName = groupName, Metrics = listMetrics, Granularity = "1Minute", }; var response = await _amazonAutoScaling.EnableMetricsCollectionAsync(collectionRequest); return response.HttpStatusCode == System.Net.HttpStatusCode.OK; } /// <summary> /// Set the desired capacity of an Auto Scaling group. /// </summary> /// <param name="groupName">The name of the Auto Scaling group.</param> /// <param name="desiredCapacity">The desired capacity for the Auto /// Scaling group.</param> /// <returns>A Boolean value indicating the success of the action.</returns> public async Task<bool> SetDesiredCapacityAsync( string groupName, int desiredCapacity) { var capacityRequest = new SetDesiredCapacityRequest { AutoScalingGroupName = groupName, DesiredCapacity = desiredCapacity, }; var response = await _amazonAutoScaling.SetDesiredCapacityAsync(capacityRequest); Console.WriteLine($"You have set the DesiredCapacity to {desiredCapacity}."); return response.HttpStatusCode == System.Net.HttpStatusCode.OK; } /// <summary> /// Terminate all instances in the Auto Scaling group in preparation for /// deleting the group. /// </summary> /// <param name="instanceId">The instance Id of the instance to terminate.</param> /// <returns>A Boolean value that indicates the success or failure of /// the operation.</returns> public async Task<bool> TerminateInstanceInAutoScalingGroupAsync( string instanceId) { var request = new TerminateInstanceInAutoScalingGroupRequest { InstanceId = instanceId, ShouldDecrementDesiredCapacity = false, }; var response = await _amazonAutoScaling.TerminateInstanceInAutoScalingGroupAsync(request); if (response.HttpStatusCode == System.Net.HttpStatusCode.OK) { Console.WriteLine($"You have terminated the instance: {instanceId}"); return true; } Console.WriteLine($"Could not terminate {instanceId}"); return false; } /// <summary> /// Update the capacity of an Auto Scaling group. /// </summary> /// <param name="groupName">The name of the Auto Scaling group.</param> /// <param name="launchTemplateName">The name of the EC2 launch template.</param> /// <param name="maxSize">The maximum number of instances that can be /// created for the Auto Scaling group.</param> /// <returns>A Boolean value indicating the success of the action.</returns> public async Task<bool> UpdateAutoScalingGroupAsync( string groupName, string launchTemplateName, int maxSize) { var templateSpecification = new LaunchTemplateSpecification { LaunchTemplateName = launchTemplateName, }; var groupRequest = new UpdateAutoScalingGroupRequest { MaxSize = maxSize, AutoScalingGroupName = groupName, LaunchTemplate = templateSpecification, }; var response = await _amazonAutoScaling.UpdateAutoScalingGroupAsync(groupRequest); if (response.HttpStatusCode == System.Net.HttpStatusCode.OK) { Console.WriteLine($"You successfully updated the Auto Scaling group {groupName}."); return true; } else { return false; } } } namespace AutoScalingActions; using Amazon.EC2; using Amazon.EC2.Model; public class EC2Wrapper { private readonly IAmazonEC2 _amazonEc2; /// <summary> /// Constructor for the EC2Wrapper class. /// </summary> /// <param name="amazonEc2">The injected Amazon EC2 client.</param> public EC2Wrapper(IAmazonEC2 amazonEc2) { _amazonEc2 = amazonEc2; } /// <summary> /// Create a new Amazon EC2 launch template. /// </summary> /// <param name="imageId">The image Id to use for instances launched /// using the Amazon EC2 launch template.</param> /// <param name="instanceType">The type of EC2 instances to create.</param> /// <param name="launchTemplateName">The name of the launch template.</param> /// <returns>Returns the TemplateID of the new launch template.</returns> public async Task<string> CreateLaunchTemplateAsync( string imageId, string instanceType, string launchTemplateName) { var request = new CreateLaunchTemplateRequest { LaunchTemplateData = new RequestLaunchTemplateData { ImageId = imageId, InstanceType = instanceType, }, LaunchTemplateName = launchTemplateName, }; var response = await _amazonEc2.CreateLaunchTemplateAsync(request); return response.LaunchTemplate.LaunchTemplateId; } /// <summary> /// Delete an Amazon EC2 launch template. /// </summary> /// <param name="launchTemplateId">The TemplateId of the launch template to /// delete.</param> /// <returns>The name of the EC2 launch template that was deleted.</returns> public async Task<string> DeleteLaunchTemplateAsync(string launchTemplateId) { var request = new DeleteLaunchTemplateRequest { LaunchTemplateId = launchTemplateId, }; var response = await _amazonEc2.DeleteLaunchTemplateAsync(request); return response.LaunchTemplate.LaunchTemplateName; } /// <summary> /// Retrieve information about an EC2 launch template. /// </summary> /// <param name="launchTemplateName">The name of the EC2 launch template.</param> /// <returns>A Boolean value that indicates the success or failure of /// the operation.</returns> public async Task<bool> DescribeLaunchTemplateAsync(string launchTemplateName) { var request = new DescribeLaunchTemplatesRequest { LaunchTemplateNames = new List<string> { launchTemplateName, }, }; var response = await _amazonEc2.DescribeLaunchTemplatesAsync(request); if (response.LaunchTemplates is not null) { response.LaunchTemplates.ForEach(template => { Console.Write($"{template.LaunchTemplateName}\t"); Console.WriteLine(template.LaunchTemplateId); }); return true; } return false; } /// <summary> /// Retrieve the availability zones for the current region. /// </summary> /// <returns>A collection of availability zones.</returns> public async Task<List<AvailabilityZone>> ListAvailabilityZonesAsync() { var response = await _amazonEc2.DescribeAvailabilityZonesAsync( new DescribeAvailabilityZonesRequest()); return response.AvailabilityZones; } } namespace AutoScalingActions; using Amazon.CloudWatch; using Amazon.CloudWatch.Model; /// <summary> /// Contains methods to access Amazon CloudWatch metrics for the /// Amazon EC2 Auto Scaling basics scenario. /// </summary> public class CloudWatchWrapper { private readonly IAmazonCloudWatch _amazonCloudWatch; /// <summary> /// Constructor for the CloudWatchWrapper. /// </summary> /// <param name="amazonCloudWatch">The injected CloudWatch client.</param> public CloudWatchWrapper(IAmazonCloudWatch amazonCloudWatch) { _amazonCloudWatch = amazonCloudWatch; } /// <summary> /// Retrieve the metrics information collection for the Auto Scaling group. /// </summary> /// <param name="groupName">The name of the Auto Scaling group.</param> /// <returns>A list of Metrics collected for the Auto Scaling group.</returns> public async Task<List<Amazon.CloudWatch.Model.Metric>> GetCloudWatchMetricsAsync(string groupName) { var filter = new DimensionFilter { Name = "AutoScalingGroupName", Value = $"{groupName}", }; var request = new ListMetricsRequest { MetricName = "AutoScalingGroupName", Dimensions = new List<DimensionFilter> { filter }, Namespace = "AWS/AutoScaling", }; var response = await _amazonCloudWatch.ListMetricsAsync(request); return response.Metrics; } /// <summary> /// Retrieve the metric data collected for an Amazon EC2 Auto Scaling group. /// </summary> /// <param name="groupName">The name of the Amazon EC2 Auto Scaling group.</param> /// <returns>A list of data points.</returns> public async Task<List<Datapoint>> GetMetricStatisticsAsync(string groupName) { var metricDimensions = new List<Dimension> { new Dimension { Name = "AutoScalingGroupName", Value = $"{groupName}", }, }; // The start time will be yesterday. var startTime = DateTime.UtcNow.AddDays(-1); var request = new GetMetricStatisticsRequest { MetricName = "AutoScalingGroupName", Dimensions = metricDimensions, Namespace = "AWS/AutoScaling", Period = 60, // 60 seconds. Statistics = new List<string>() { "Minimum" }, StartTimeUtc = startTime, EndTimeUtc = DateTime.UtcNow, }; var response = await _amazonCloudWatch.GetMetricStatisticsAsync(request); return response.Datapoints; } }