private void ConfigureService(Configuration settings) { if (EcsCluster == null) { throw new InvalidOperationException($"{nameof(EcsCluster)} has not been set. The {nameof(ConfigureCluster)} method should be called before {nameof(ConfigureService)}"); } if (AppTaskDefinition == null) { throw new InvalidOperationException($"{nameof(AppTaskDefinition)} has not been set. The {nameof(ConfigureTaskDefinition)} method should be called before {nameof(ConfigureService)}"); } var fargateServiceProps = new FargateServiceProps { Cluster = EcsCluster, TaskDefinition = AppTaskDefinition, AssignPublicIp = settings.Vpc.IsDefault, DesiredCount = settings.DesiredCount }; if (!string.IsNullOrEmpty(settings.ECSServiceSecurityGroups)) { var ecsServiceSecurityGroups = new List <ISecurityGroup>(); var count = 1; foreach (var securityGroupId in settings.ECSServiceSecurityGroups.Split(',')) { ecsServiceSecurityGroups.Add(SecurityGroup.FromSecurityGroupId(this, $"AdditionalGroup-{count++}", securityGroupId.Trim(), new SecurityGroupImportOptions { Mutable = false })); } fargateServiceProps.SecurityGroups = ecsServiceSecurityGroups.ToArray(); } AppFargateService = new FargateService(this, nameof(AppFargateService), InvokeCustomizeCDKPropsEvent(nameof(AppFargateService), this, fargateServiceProps)); }
internal CreditoWebApiStack(Construct scope, string id, CustomStackProps props = null) : base(scope, id, props) { var vpc = props.Vpc; var creditoWebApiTargetGroup = new ApplicationTargetGroup(this, "CreditoWebApiTargetGroup", new ApplicationTargetGroupProps { Protocol = ApplicationProtocol.HTTP, Port = 80, Vpc = vpc, TargetType = TargetType.IP, DeregistrationDelay = Duration.Seconds(60), HealthCheck = new Amazon.CDK.AWS.ElasticLoadBalancingV2.HealthCheck { Enabled = true, Path = "/api/credito/_monitor/shallow", Protocol = Amazon.CDK.AWS.ElasticLoadBalancingV2.Protocol.HTTP, Port = "traffic-port", UnhealthyThresholdCount = 2, Interval = Duration.Seconds(60), HealthyThresholdCount = 5, Timeout = Duration.Seconds(5), HealthyHttpCodes = "200" } }); var webApiServiceSecurityGroup = SecurityGroup.FromSecurityGroupId(this, "WebApiServiceSecurityGroup", Fn.ImportValue(Globals.GetDeployEnvironment(this).PutEnvNamePrefixWithDash("WebApiServiceSecurityGroupId"))); var appListener = ApplicationListener.FromApplicationListenerAttributes(this, "AppListener", new ApplicationListenerAttributes { ListenerArn = Fn.ImportValue(Globals.GetDeployEnvironment(this).PutEnvNamePrefixWithDash("AppListenerArn")), SecurityGroup = webApiServiceSecurityGroup }); appListener.AddTargetGroups("CreditoWebApiTargetGroup", new AddApplicationTargetGroupsProps { Conditions = new ListenerCondition[] { ListenerCondition.PathPatterns(new string[] { "/api/credito*" }) }, Priority = 100, TargetGroups = new ApplicationTargetGroup[] { creditoWebApiTargetGroup } }); var creditoWebApiLogGroup = new LogGroup(this, "CreditoWebApiContainerLogGroup", new LogGroupProps { LogGroupName = $"/ecs/{Globals.GetDeployEnvironment(this).EnvName}/credito/web-api", Retention = RetentionDays.FIVE_DAYS, RemovalPolicy = RemovalPolicy.SNAPSHOT }); var creditoWebApiTaskDefinition = new FargateTaskDefinition(this, "CreditoWebApiTaskDefinition", new FargateTaskDefinitionProps { MemoryLimitMiB = 512, Cpu = 256 }); var creditoWebApiLogging = new AwsLogDriver( new AwsLogDriverProps { StreamPrefix = "ecs", LogGroup = creditoWebApiLogGroup }); var creditoWebApiContainer = creditoWebApiTaskDefinition.AddContainer("CreditoWebApiContainer", new ContainerDefinitionOptions { Image = ContainerImage.FromAsset( Directory.GetCurrentDirectory(), new AssetImageProps { File = "src/Credito.WebApi/Dockerfile" }), Logging = creditoWebApiLogging, Environment = new Dictionary <string, string>() { ["CreditoDatabase__ConnectionString"] = StringParameter.ValueFromLookup( this, $"/{Globals.GetDeployEnvironment(this).EnvName}/credito/web-api/db/connection-string"), ["CreditoDatabase__DatabaseName"] = StringParameter.ValueFromLookup( this, $"/{Globals.GetDeployEnvironment(this).EnvName}/credito/web-api/db/database-name") } }); creditoWebApiContainer.AddPortMappings( new PortMapping { ContainerPort = 80, HostPort = 80, Protocol = Amazon.CDK.AWS.ECS.Protocol.TCP }); var cluster = Cluster.FromClusterAttributes(this, "Cluster", new ClusterAttributes { ClusterName = Fn.ImportValue(Globals.GetDeployEnvironment(this).PutEnvNamePrefixWithDash("ClusterName")), Vpc = vpc, SecurityGroups = new SecurityGroup[] { } }); var creditoWebApiService = new FargateService(this, "CreditoWebApiService", new FargateServiceProps { Cluster = cluster, TaskDefinition = creditoWebApiTaskDefinition, DesiredCount = 1, CircuitBreaker = new DeploymentCircuitBreaker { Rollback = true }, AssignPublicIp = false, HealthCheckGracePeriod = Duration.Seconds(60), SecurityGroups = new ISecurityGroup[] { webApiServiceSecurityGroup }, VpcSubnets = new SubnetSelection { SubnetType = SubnetType.PRIVATE } }); creditoWebApiService.AttachToApplicationTargetGroup(creditoWebApiTargetGroup); }
internal CdkStack(Construct scope, string id, IStackProps props = null) : base(scope, id, props) { var stackProps = ReportStackProps.ParseOrDefault(props); var dframeWorkerLogGroup = "MagicOnionBenchWorkerLogGroup"; var dframeMasterLogGroup = "MagicOnionBenchMasterLogGroup"; var benchNetwork = stackProps.GetBenchNetwork(); var recreateMagicOnionTrigger = stackProps.GetBenchmarkServerBinariesHash(); // s3 var s3 = new Bucket(this, "Bucket", new BucketProps { AutoDeleteObjects = true, RemovalPolicy = RemovalPolicy.DESTROY, AccessControl = BucketAccessControl.PRIVATE, }); var lifecycleRule = new LifecycleRule { Enabled = true, Prefix = "reports/", Expiration = Duration.Days(stackProps.DaysKeepReports), AbortIncompleteMultipartUploadAfter = Duration.Days(1), }; s3.AddLifecycleRule(lifecycleRule); s3.AddToResourcePolicy(new PolicyStatement(new PolicyStatementProps { Sid = "AllowPublicRead", Effect = Effect.ALLOW, Principals = new[] { new AnyPrincipal() }, Actions = new[] { "s3:GetObject*" }, Resources = new[] { $"{s3.BucketArn}/html/*" }, })); s3.AddToResourcePolicy(new PolicyStatement(new PolicyStatementProps { Sid = "AllowAwsAccountAccess", Effect = Effect.ALLOW, Principals = new[] { new AccountRootPrincipal() }, Actions = new[] { "s3:*" }, Resources = new[] { $"{s3.BucketArn}/*" }, })); // s3 deploy var masterDllDeployment = new BucketDeployment(this, "DeployMasterDll", new BucketDeploymentProps { DestinationBucket = s3, Sources = new[] { Source.Asset(Path.Combine(Directory.GetCurrentDirectory(), $"out/linux/server")) }, DestinationKeyPrefix = $"assembly/linux/server" }); var userdataDeployment = new BucketDeployment(this, "UserData", new BucketDeploymentProps { DestinationBucket = s3, Sources = new[] { Source.Asset(Path.Combine(Directory.GetCurrentDirectory(), "userdata/")) }, DestinationKeyPrefix = "userdata/" }); // docker deploy var dockerImage = new DockerImageAsset(this, "dframeWorkerImage", new DockerImageAssetProps { Directory = Path.Combine(Directory.GetCurrentDirectory(), "app"), File = "ConsoleAppEcs/Dockerfile.Ecs", }); var dframeImage = ContainerImage.FromDockerImageAsset(dockerImage); // network var vpc = new Vpc(this, "Vpc", new VpcProps { MaxAzs = 2, NatGateways = 0, SubnetConfiguration = new[] { new SubnetConfiguration { Name = "public", SubnetType = SubnetType.PUBLIC } }, }); var allsubnets = new SubnetSelection { Subnets = vpc.PublicSubnets }; var singleSubnets = new SubnetSelection { Subnets = new[] { vpc.PublicSubnets.First() } }; var sg = new SecurityGroup(this, "MasterSg", new SecurityGroupProps { AllowAllOutbound = true, Vpc = vpc, }); foreach (var subnet in vpc.PublicSubnets) { sg.AddIngressRule(Peer.Ipv4(vpc.VpcCidrBlock), Port.AllTcp(), "VPC", true); } // service discovery var serviceDiscoveryDomain = "local"; var serverMapName = "server"; var dframeMapName = "dframe-master"; var ns = new PrivateDnsNamespace(this, "Namespace", new PrivateDnsNamespaceProps { Vpc = vpc, Name = serviceDiscoveryDomain, }); var serviceDiscoveryServer = ns.CreateService("server", new DnsServiceProps { Name = serverMapName, DnsRecordType = DnsRecordType.A, RoutingPolicy = RoutingPolicy.MULTIVALUE, }); // alb var albDnsName = "benchmark-alb"; var benchToMagicOnionDnsName = benchNetwork.RequireAlb ? $"{benchNetwork.EndpointScheme}://{albDnsName}.{stackProps.AlbDomain.domain}" : $"{benchNetwork.EndpointScheme}://{serverMapName}.{serviceDiscoveryDomain}"; IApplicationTargetGroup grpcTargetGroup = null; IApplicationTargetGroup httpsTargetGroup = null; if (benchNetwork.RequireAlb) { // route53 var hostedZone = HostedZone.FromHostedZoneAttributes(this, "HostedZone", new HostedZoneAttributes { HostedZoneId = stackProps.AlbDomain.zoneId, ZoneName = stackProps.AlbDomain.domain, }); // acm var certificate = new DnsValidatedCertificate(this, "certificate", new DnsValidatedCertificateProps { DomainName = $"{albDnsName}.{hostedZone.ZoneName}", HostedZone = hostedZone, }); // alb var lb = new ApplicationLoadBalancer(this, "LB", new ApplicationLoadBalancerProps { Vpc = vpc, VpcSubnets = allsubnets, SecurityGroup = new SecurityGroup(this, "AlbSg", new SecurityGroupProps { AllowAllOutbound = true, Vpc = vpc, }), InternetFacing = false, Http2Enabled = true, }); grpcTargetGroup = AddGrpcTargetGroup(benchNetwork, vpc, certificate, lb); httpsTargetGroup = AddHttpsTargetGroup(benchNetwork, vpc, certificate, lb); // Dns Record _ = new CnameRecord(this, "alb-alias-record", new CnameRecordProps { RecordName = $"{albDnsName}.{stackProps.AlbDomain.domain}", Ttl = Duration.Seconds(60), Zone = hostedZone, DomainName = lb.LoadBalancerDnsName, }); } // iam var iamEc2MagicOnionRole = GetIamEc2MagicOnionRole(s3, serviceDiscoveryServer); var iamEcsTaskExecuteRole = GetIamEcsTaskExecuteRole(new[] { dframeWorkerLogGroup, dframeMasterLogGroup }); var iamDFrameTaskDefRole = GetIamEcsDframeTaskDefRole(s3); var iamWorkerTaskDefRole = GetIamEcsWorkerTaskDefRole(s3); // secrets var ddToken = stackProps.UseEc2DatadogAgentProfiler || stackProps.UseFargateDatadogAgentProfiler ? Amazon.CDK.AWS.SecretsManager.Secret.FromSecretNameV2(this, "dd-token", "magiconion-benchmark-datadog-token") : null; // MagicOnion var asg = new AutoScalingGroup(this, "MagicOnionAsg", new AutoScalingGroupProps { // Monitoring is default DETAILED. SpotPrice = "1.0", // 0.0096 for spot price average for m3.medium Vpc = vpc, SecurityGroup = sg, VpcSubnets = singleSubnets, InstanceType = stackProps.MagicOnionInstanceType, DesiredCapacity = 1, MaxCapacity = 1, MinCapacity = 0, AssociatePublicIpAddress = true, MachineImage = new AmazonLinuxImage(new AmazonLinuxImageProps { CpuType = AmazonLinuxCpuType.X86_64, Generation = AmazonLinuxGeneration.AMAZON_LINUX_2, Storage = AmazonLinuxStorage.GENERAL_PURPOSE, Virtualization = AmazonLinuxVirt.HVM, }), AllowAllOutbound = true, GroupMetrics = new[] { GroupMetrics.All() }, Role = iamEc2MagicOnionRole, UpdatePolicy = UpdatePolicy.ReplacingUpdate(), Signals = Signals.WaitForCount(1, new SignalsOptions { Timeout = Duration.Minutes(10), }), }); asg.AddSecretsReadGrant(ddToken, () => stackProps.UseEc2DatadogAgentProfiler); var userdata = GetUserData(recreateMagicOnionTrigger, s3.BucketName, stackProps.BenchmarkBinaryNames, serviceDiscoveryServer.ServiceId, stackProps.UseEc2CloudWatchAgentProfiler, stackProps.UseEc2DatadogAgentProfiler); asg.AddUserData(userdata); asg.UserData.AddSignalOnExitCommand(asg); asg.Node.AddDependency(masterDllDeployment); asg.Node.AddDependency(userdataDeployment); if (stackProps.EnableMagicOnionScaleInCron) { asg.ScaleOnSchedule("ScheduleOut", new BasicScheduledActionProps { DesiredCapacity = 1, MaxCapacity = 1, // AM9:00 (JST+9) on Monday to Wednesday Schedule = Schedule.Expression("0 0 * 1-3 *"), }); asg.ScaleOnSchedule("ScheduleIn", new BasicScheduledActionProps { DesiredCapacity = 0, MaxCapacity = 0, // PM9:00 (JST+9) on Everyday Schedule = Schedule.Expression("0 12 * 1-7 *"), }); } if (benchNetwork.RequireAlb) { asg.AttachToApplicationTargetGroup(grpcTargetGroup); asg.AttachToApplicationTargetGroup(httpsTargetGroup); } // ECS var cluster = new Cluster(this, "WorkerCluster", new ClusterProps { Vpc = vpc, }); cluster.Node.AddDependency(asg); // wait until asg is up // dframe-worker var dframeWorkerContainerName = "worker"; var dframeWorkerTaskDef = new FargateTaskDefinition(this, "DFrameWorkerTaskDef", new FargateTaskDefinitionProps { ExecutionRole = iamEcsTaskExecuteRole, TaskRole = iamWorkerTaskDefRole, Cpu = stackProps.WorkerFargate.CpuSize, MemoryLimitMiB = stackProps.WorkerFargate.MemorySize, }); dframeWorkerTaskDef.AddContainer(dframeWorkerContainerName, new ContainerDefinitionOptions { Image = dframeImage, Command = new[] { "--worker-flag" }, Environment = new Dictionary <string, string> { { "DFRAME_MASTER_CONNECT_TO_HOST", $"{dframeMapName}.{serviceDiscoveryDomain}" }, { "DFRAME_MASTER_CONNECT_TO_PORT", "12345" }, { "BENCH_SERVER_HOST", benchToMagicOnionDnsName }, { "BENCH_REPORTID", stackProps.ReportId }, { "BENCH_S3BUCKET", s3.BucketName }, }, Logging = LogDriver.AwsLogs(new AwsLogDriverProps { LogGroup = new LogGroup(this, "WorkerLogGroup", new LogGroupProps { LogGroupName = dframeWorkerLogGroup, RemovalPolicy = RemovalPolicy.DESTROY, Retention = RetentionDays.TWO_WEEKS, }), StreamPrefix = dframeWorkerLogGroup, }), }); dframeWorkerTaskDef.AddDatadogContainer($"{dframeWorkerContainerName}-datadog", ddToken, () => stackProps.UseFargateDatadogAgentProfiler); var dframeWorkerService = new FargateService(this, "DFrameWorkerService", new FargateServiceProps { ServiceName = "DFrameWorkerService", DesiredCount = 0, Cluster = cluster, TaskDefinition = dframeWorkerTaskDef, VpcSubnets = singleSubnets, SecurityGroups = new[] { sg }, PlatformVersion = FargatePlatformVersion.VERSION1_4, MinHealthyPercent = 0, AssignPublicIp = true, }); // dframe-master var dframeMasterTaskDef = new FargateTaskDefinition(this, "DFrameMasterTaskDef", new FargateTaskDefinitionProps { ExecutionRole = iamEcsTaskExecuteRole, TaskRole = iamDFrameTaskDefRole, Cpu = stackProps.MasterFargate.CpuSize, MemoryLimitMiB = stackProps.MasterFargate.MemorySize, }); dframeMasterTaskDef.AddContainer("dframe", new ContainerDefinitionOptions { Image = dframeImage, Environment = new Dictionary <string, string> { { "DFRAME_CLUSTER_NAME", cluster.ClusterName }, { "DFRAME_MASTER_SERVICE_NAME", "DFrameMasterService" }, { "DFRAME_WORKER_CONTAINER_NAME", dframeWorkerContainerName }, { "DFRAME_WORKER_SERVICE_NAME", dframeWorkerService.ServiceName }, { "DFRAME_WORKER_TASK_NAME", Fn.Select(1, Fn.Split("/", dframeWorkerTaskDef.TaskDefinitionArn)) }, { "DFRAME_WORKER_IMAGE", dockerImage.ImageUri }, { "BENCH_REPORTID", stackProps.ReportId }, { "BENCH_S3BUCKET", s3.BucketName }, }, Logging = LogDriver.AwsLogs(new AwsLogDriverProps { LogGroup = new LogGroup(this, "MasterLogGroup", new LogGroupProps { LogGroupName = dframeMasterLogGroup, RemovalPolicy = RemovalPolicy.DESTROY, Retention = RetentionDays.TWO_WEEKS, }), StreamPrefix = dframeMasterLogGroup, }), }); dframeMasterTaskDef.AddDatadogContainer($"dframe-datadog", ddToken, () => stackProps.UseFargateDatadogAgentProfiler); var dframeMasterService = new FargateService(this, "DFrameMasterService", new FargateServiceProps { ServiceName = "DFrameMasterService", DesiredCount = 1, Cluster = cluster, TaskDefinition = dframeMasterTaskDef, VpcSubnets = singleSubnets, SecurityGroups = new[] { sg }, PlatformVersion = FargatePlatformVersion.VERSION1_4, MinHealthyPercent = 0, AssignPublicIp = true, }); dframeMasterService.EnableCloudMap(new CloudMapOptions { CloudMapNamespace = ns, Name = dframeMapName, DnsRecordType = DnsRecordType.A, DnsTtl = Duration.Seconds(300), }); // output new CfnOutput(this, "ReportUrl", new CfnOutputProps { Value = $"https://{s3.BucketRegionalDomainName}/html/{stackProps.ReportId}/index.html" }); new CfnOutput(this, "EndPointStyle", new CfnOutputProps { Value = stackProps.BenchmarkEndpoint.ToString() }); new CfnOutput(this, "AsgName", new CfnOutputProps { Value = asg.AutoScalingGroupName }); new CfnOutput(this, "EcsClusterName", new CfnOutputProps { Value = cluster.ClusterName }); new CfnOutput(this, "DFrameWorkerEcsTaskdefImage", new CfnOutputProps { Value = dockerImage.ImageUri }); }
private void ConfigureECSClusterAndService(IRecipeProps <Configuration> recipeConfiguration) { if (AppVpc == null) { throw new InvalidOperationException($"{nameof(AppVpc)} has not been set. The {nameof(ConfigureVpc)} method should be called before {nameof(ConfigureECSClusterAndService)}"); } var settings = recipeConfiguration.Settings; if (settings.ECSCluster.CreateNew) { EcsCluster = new Cluster(this, nameof(EcsCluster), InvokeCustomizeCDKPropsEvent(nameof(EcsCluster), this, new ClusterProps { Vpc = AppVpc, ClusterName = settings.ECSCluster.NewClusterName })); } else { EcsCluster = Cluster.FromClusterAttributes(this, nameof(EcsCluster), InvokeCustomizeCDKPropsEvent(nameof(EcsCluster), this, new ClusterAttributes { ClusterArn = settings.ECSCluster.ClusterArn, ClusterName = ECSFargateUtilities.GetClusterNameFromArn(settings.ECSCluster.ClusterArn), SecurityGroups = new ISecurityGroup[0], Vpc = AppVpc })); } AppTaskDefinition = new FargateTaskDefinition(this, nameof(AppTaskDefinition), InvokeCustomizeCDKPropsEvent(nameof(AppTaskDefinition), this, new FargateTaskDefinitionProps { TaskRole = AppIAMTaskRole, Cpu = settings.TaskCpu, MemoryLimitMiB = settings.TaskMemory })); AppLogging = new AwsLogDriver(InvokeCustomizeCDKPropsEvent(nameof(AppLogging), this, new AwsLogDriverProps { StreamPrefix = recipeConfiguration.StackName })); if (string.IsNullOrEmpty(recipeConfiguration.ECRRepositoryName)) { throw new InvalidOrMissingConfigurationException("The provided ECR Repository Name is null or empty."); } EcrRepository = Repository.FromRepositoryName(this, nameof(EcrRepository), recipeConfiguration.ECRRepositoryName); AppContainerDefinition = AppTaskDefinition.AddContainer(nameof(AppContainerDefinition), InvokeCustomizeCDKPropsEvent(nameof(AppContainerDefinition), this, new ContainerDefinitionOptions { Image = ContainerImage.FromEcrRepository(EcrRepository, recipeConfiguration.ECRImageTag), Logging = AppLogging })); AppContainerDefinition.AddPortMappings(new PortMapping { ContainerPort = 80, Protocol = Amazon.CDK.AWS.ECS.Protocol.TCP }); WebAccessSecurityGroup = new SecurityGroup(this, nameof(WebAccessSecurityGroup), InvokeCustomizeCDKPropsEvent(nameof(WebAccessSecurityGroup), this, new SecurityGroupProps { Vpc = AppVpc, SecurityGroupName = $"{recipeConfiguration.StackName}-ECSService" })); EcsServiceSecurityGroups = new List <ISecurityGroup>(); EcsServiceSecurityGroups.Add(WebAccessSecurityGroup); if (!string.IsNullOrEmpty(settings.AdditionalECSServiceSecurityGroups)) { var count = 1; foreach (var securityGroupId in settings.AdditionalECSServiceSecurityGroups.Split(',')) { EcsServiceSecurityGroups.Add(SecurityGroup.FromSecurityGroupId(this, $"AdditionalGroup-{count++}", securityGroupId.Trim(), new SecurityGroupImportOptions { Mutable = false })); } } AppFargateService = new FargateService(this, nameof(AppFargateService), InvokeCustomizeCDKPropsEvent(nameof(AppFargateService), this, new FargateServiceProps { Cluster = EcsCluster, TaskDefinition = AppTaskDefinition, DesiredCount = settings.DesiredCount, ServiceName = settings.ECSServiceName, AssignPublicIp = settings.Vpc.IsDefault, SecurityGroups = EcsServiceSecurityGroups.ToArray() })); }
internal UsersServiceFargateStack(Construct scope, string id, IUsersServiceFargateStackProps props) : base(scope, id, props) { var repository = Repository.FromRepositoryName(this, "UsersRepository", "users-service"); var taskDefinition = new FargateTaskDefinition(this, "UsersServiceTaskDef", new FargateTaskDefinitionProps { Cpu = 256, MemoryLimitMiB = 512 }); var container = taskDefinition.AddContainer("WebApi", new ContainerDefinitionOptions { Image = ContainerImage.FromEcrRepository(repository), Logging = new AwsLogDriver(new AwsLogDriverProps { LogRetention = Amazon.CDK.AWS.Logs.RetentionDays.TWO_WEEKS, StreamPrefix = "UsersService-" }) }); container.AddPortMappings(new PortMapping { ContainerPort = 80 }); var service = new FargateService(this, "UsersService", new FargateServiceProps { Cluster = props.Cluster, TaskDefinition = taskDefinition, DesiredCount = 2, VpcSubnets = new SubnetSelection { SubnetType = SubnetType.PUBLIC }, AssignPublicIp = true }); service.AutoScaleTaskCount(new EnableScalingProps { MinCapacity = 2, MaxCapacity = 4 }).ScaleOnCpuUtilization("CPU", new CpuUtilizationScalingProps { TargetUtilizationPercent = 70 }); props.Listener.AddTargets("UsersServiceTarget", new AddApplicationTargetsProps { HealthCheck = new HealthCheck { HealthyHttpCodes = "200-299", Path = "/api/users/health", Protocol = Amazon.CDK.AWS.ElasticLoadBalancingV2.Protocol.HTTP, HealthyThresholdCount = 2, UnhealthyThresholdCount = 5, Timeout = Duration.Seconds(10), Interval = Duration.Seconds(70), Port = "80" }, Protocol = ApplicationProtocol.HTTP, Port = 80, Targets = new IApplicationLoadBalancerTarget[] { service }, PathPattern = "/api/users*", Priority = 2 }); }