static void Main(string[] args) { var app = new App(null); var stack = new Stack(app, "mystack"); var rule = new Rule(stack, "myrule", new RuleProps { Targets = new IRuleTarget[] { new MyEventTarget() }, Schedule = Schedule.Cron(new CronOptions { Day = "20" }) }); var lb = new ApplicationLoadBalancer(stack, "myapplb", new ApplicationLoadBalancerProps { Vpc = new Vpc(stack, "myvpc") }); var listener = lb.AddListener("Listener", new ApplicationListenerProps { Port = 80 }); listener.AddTargets("mylbtarget", new AddApplicationTargetsProps { Targets = new IApplicationLoadBalancerTarget[] { new MyLBTarget() }, Port = 80 }); app.Synth(); }
internal InternalLoadBalancerStack(Construct scope, string id, IInternalLoadBalancerStackProps props) : base(scope, id, props) { var loadBalancer = new ApplicationLoadBalancer(this, "LoadBalancer", new ApplicationLoadBalancerProps { Vpc = props.Vpc, InternetFacing = true }); var defaultTarget = new ApplicationTargetGroup(this, "TargetGroup", new ApplicationTargetGroupProps { Vpc = props.Vpc, Port = 80 }); Listener = loadBalancer.AddListener("Listener", new BaseApplicationListenerProps { Port = 80, DefaultTargetGroups = new ApplicationTargetGroup[] { defaultTarget } }); new CfnOutput(this, "LoadBalancerDNS", new CfnOutputProps { Value = loadBalancer.LoadBalancerDnsName }); }
private ApplicationListener AddListener(ApplicationLoadBalancer lb, int port, string certArn = null) { var certs = (certArn == null) ? null : new ListenerCertificate[] { new ListenerCertificate(certArn) }; var listener = lb.AddListener($"App2BalancerListener_{port}", new BaseApplicationListenerProps { Open = true, Certificates = certs, Port = port, }); return(listener); }
internal AutoScaledInstances( CdkStack stack, CfnParameter targetPlatform, Vpc vpc, bool publicAccess, SecurityGroup albSecurityGroup, SecurityGroup instanceSecurityGroup, Database database = null, Policy policy = null, ApplicationLoadBalancer restApiLoadBalancer = null) { IMachineImage selectedImage; bool targetWindows = false; if (targetWindows) { var userData = UserData.ForWindows(); userData.AddCommands( "New-Item -Path c:\\temp -ItemType Directory -Force", $"Read-S3Object -BucketName aws-codedeploy-{stack.Region}/latest -Key codedeploy-agent.msi -File c:\\temp\\codedeploy-agent.msi", "Start-Process -Wait -FilePath c:\\temp\\codedeploy-agent.msi -WindowStyle Hidden" ); selectedImage = new WindowsImage( WindowsVersion.WINDOWS_SERVER_2019_ENGLISH_CORE_BASE, new WindowsImageProps { UserData = userData } ); } else { var userData = UserData.ForLinux(new LinuxUserDataOptions { Shebang = "#!/bin/bash -xe" }); userData.AddCommands( "exec > >(tee /var/log/user-data.log|logger -t user-data -s 2>/dev/console) 2>&1", "yum install -y aws-cli ruby jq", "yum -y update", "cd /tmp/", $"curl -O https://aws-codedeploy-{stack.Region}.s3.amazonaws.com/latest/install", "chmod +x ./install", "if ./install auto; then", " echo \"CodeDeploy Agent installation completed successfully!\"", " exit 0", "else", " echo \"CodeDeploy Agent installation failed, please investigate.\"", " rm -f /tmp/install", " exit 1", "fi", "rm -rf /tmp/*" ); selectedImage = new AmazonLinuxImage(new AmazonLinuxImageProps { Edition = AmazonLinuxEdition.STANDARD, Virtualization = AmazonLinuxVirt.HVM, Generation = AmazonLinuxGeneration.AMAZON_LINUX_2, Storage = AmazonLinuxStorage.EBS, UserData = userData }); }; var alb = new ApplicationLoadBalancer(stack, $"ApplicationLoadBalancer-{(publicAccess ? "public" : "private")}", new ApplicationLoadBalancerProps { InternetFacing = publicAccess, Vpc = vpc, VpcSubnets = new SubnetSelection { SubnetType = publicAccess ? SubnetType.PUBLIC : SubnetType.PRIVATE }, SecurityGroup = albSecurityGroup, IpAddressType = IpAddressType.IPV4, Http2Enabled = true }); var albTargetGroup = new ApplicationTargetGroup(stack, $"ApplicationTargetGroup-{(publicAccess ? "public" : "private")}", new ApplicationTargetGroupProps { Vpc = vpc, Port = 80, Protocol = ApplicationProtocol.HTTP, TargetType = TargetType.INSTANCE, HealthCheck = new Amazon.CDK.AWS.ElasticLoadBalancingV2.HealthCheck { Timeout = Duration.Seconds(5), Interval = Duration.Seconds(10), HealthyThresholdCount = 2 } }); var albListener = new ApplicationListener(stack, $"ApplicationListener-{(publicAccess ? "public" : "private")}", new ApplicationListenerProps { Port = 80, Protocol = ApplicationProtocol.HTTP, DefaultAction = ListenerAction.Forward(new[] { albTargetGroup }), LoadBalancer = alb }); var asg = new AutoScalingGroup(stack, $"ASG-{(publicAccess ? "public" : "private")}", new AutoScalingGroupProps { Vpc = vpc, MinCapacity = 2, InstanceType = InstanceType.Of(InstanceClass.BURSTABLE3, InstanceSize.MEDIUM), MachineImage = selectedImage, BlockDevices = new[] { new Amazon.CDK.AWS.AutoScaling.BlockDevice() { DeviceName = "/dev/xvda", Volume = Amazon.CDK.AWS.AutoScaling.BlockDeviceVolume.Ebs( targetWindows ? 30: 8, new Amazon.CDK.AWS.AutoScaling.EbsDeviceOptions { VolumeType = Amazon.CDK.AWS.AutoScaling.EbsDeviceVolumeType.GP2, DeleteOnTermination = true } ) } }, AssociatePublicIpAddress = false, VpcSubnets = new SubnetSelection { SubnetType = SubnetType.PRIVATE } }); if (policy != null) { asg.Role.AttachInlinePolicy(policy); } asg.Role.AddToPrincipalPolicy( new PolicyStatement(new PolicyStatementProps { Effect = Effect.ALLOW, Actions = new[] { "ec2:DescribeTags" }, Resources = new[] { "*" } }) ); asg.Role.AddManagedPolicy(ManagedPolicy.FromAwsManagedPolicyName("AmazonSSMManagedInstanceCore")); asg.Role.AddManagedPolicy(ManagedPolicy.FromAwsManagedPolicyName("AWSXRayDaemonWriteAccess")); asg.Role.AddManagedPolicy(ManagedPolicy.FromAwsManagedPolicyName("CloudWatchAgentServerPolicy")); Tag.Add(asg, "Application", stack.StackName); if (publicAccess) { Tag.Add(asg, "ApplicationRole", "Front End"); Tag.Add(asg, "RESTAPIAddress", restApiLoadBalancer.LoadBalancerDnsName); } else { Tag.Add(asg, "ApplicationRole", "REST API"); } if (database != null) { asg.Node.AddDependency(database.DatabaseResource); Tag.Add(asg, "DBSecretArn", database.Password.SecretArn); } // Enable access from the ALB asg.AddSecurityGroup(instanceSecurityGroup); Result = new LoadBalancedInstancesResult { AutoScalingGroup = asg, TargetGroup = albTargetGroup, LoadBalancer = alb }; }
public ApplicationLoadBalancer Create(Construct construct, Amazon.CDK.AWS.EC2.Vpc vpc, Amazon.CDK.AWS.AutoScaling.AutoScalingGroup asg, SecurityGroup sg) { var lb = new ApplicationLoadBalancer(construct, _config.Alb.Name, new ApplicationLoadBalancerProps { Vpc = vpc, InternetFacing = true, LoadBalancerName = _config.Alb.Name, SecurityGroup = sg }); Amazon.CDK.Tags.Of(lb).Add("Name", $"{_config.Alb.Name}"); // add a listener var listener = AddListener(lb, 80, null); var appPort = 80; var group = listener.AddTargets($"AppFleet", new AddApplicationTargetsProps { Port = appPort, Targets = new[] { asg } }); // add specific tags Amazon.CDK.Tags.Of(listener).Add("Name", $"{_config.Alb.Name}-listner"); Amazon.CDK.Tags.Of(group).Add("Name", $"{_config.Alb.Name}-fleet"); // exmple of a fixed ok message returned by the LB listener.AddAction($"FixedOkMessage", new AddApplicationActionProps { Priority = 10, Conditions = new[] { ListenerCondition.PathPatterns(new[] { "/ok" }) }, Action = ListenerAction.FixedResponse(200, new FixedResponseOptions { ContentType = "text/html", MessageBody = "OK" }) }); // example of a fixed health status message returned by LB listener.AddAction($"LBHealthInfo", new AddApplicationActionProps { Priority = 15, Conditions = new[] { ListenerCondition.PathPatterns(new[] { "/lb-status" }) }, Action = ListenerAction.FixedResponse(200, new FixedResponseOptions { ContentType = "application/json", MessageBody = "{ \"lb\": { \"type\": \"application-load-balancer\", \"launchDateUtc\": \"{" + DateTime.UtcNow + "}\", \"status\": \"ok\" } }" }) }); //"arn:aws:acm:us-east-1:xxxxxxxxx:certificate/eb2b584c-421d-4134-b679-1746642b5e3f" if (_config.Alb.CertArn != null) { listener = AddListener(lb, 443, _config.Alb.CertArn); // forward any ssl requests to the target group listener.AddAction("SSLForward", new AddApplicationActionProps { Action = ListenerAction.Forward(new[] { group }), }); } return(lb); }
internal InfraStack(Construct scope, string id, CustomStackProps props = null) : base(scope, id, props) { var vpc = props.Vpc; var cluster = new Cluster(this, "Cluster", new ClusterProps { Vpc = vpc, ClusterName = Globals.GetDeployEnvironment(this).PutEnvNamePrefixWithDash("Cluster") }); var albSecurityGroup = new SecurityGroup(this, "AlbSecurityGroup", new SecurityGroupProps { Vpc = vpc, AllowAllOutbound = true }); albSecurityGroup.AddIngressRule(Peer.AnyIpv4(), Port.Tcp(80)); var alb = new ApplicationLoadBalancer(this, "ALB", new ApplicationLoadBalancerProps { Vpc = vpc, InternetFacing = true, Http2Enabled = true, IdleTimeout = Duration.Seconds(60), IpAddressType = IpAddressType.IPV4, SecurityGroup = albSecurityGroup }); var webApiServiceSecurityGroup = new SecurityGroup(this, "WebApiServiceSecurityGroup", new SecurityGroupProps { Vpc = vpc, AllowAllOutbound = true }); webApiServiceSecurityGroup.AddIngressRule(albSecurityGroup, Port.Tcp(80)); var appListener = alb.AddListener("AppListener", new BaseApplicationListenerProps { Port = 80, Protocol = ApplicationProtocol.HTTP, DefaultAction = ListenerAction.FixedResponse(404, new FixedResponseOptions { ContentType = "text/plain", MessageBody = "This is not here..." }) }); new CfnOutput(this, "ClusterName", new CfnOutputProps { ExportName = Globals.GetDeployEnvironment(this).PutEnvNamePrefixWithDash("ClusterName"), Value = cluster.ClusterName }); new CfnOutput(this, "WebApiServiceSecurityGroupId", new CfnOutputProps { ExportName = Globals.GetDeployEnvironment(this).PutEnvNamePrefixWithDash("WebApiServiceSecurityGroupId"), Value = albSecurityGroup.SecurityGroupId }); new CfnOutput(this, "AppListenerArn", new CfnOutputProps { ExportName = Globals.GetDeployEnvironment(this).PutEnvNamePrefixWithDash("AppListenerArn"), Value = appListener.ListenerArn }); }
public CdkExampleStack(Construct parent, string id, IStackProps props) : base(parent, id, props) { var vpc = new Vpc(this, "MainVPC", new VpcProps { Cidr = "192.168.0.0/16" }); var loadBalancer = new ApplicationLoadBalancer(this, "PublicALB", new ApplicationLoadBalancerProps { InternetFacing = true, Vpc = vpc }); var listener = loadBalancer.AddListener("MyListener", new ApplicationListenerProps { Port = 80 }); var userData = UserData.ForLinux(new LinuxUserDataOptions { Shebang = "#!/bin/bash" }); userData.AddCommands( "yum update -y", "yum install httpd -y", "echo \"Hello World\" >> /var/www/html/index.html", "service httpd start", "chkconfig httpd on"); var ec2SG = new SecurityGroup(this, "Ec2SecurityGroup", new SecurityGroupProps { Vpc = vpc, SecurityGroupName = "Ec2SG" }); ec2SG.Connections.AllowFrom(loadBalancer, Port.Tcp(80), "FROM ALB"); var instanceIds = new List <string>(); for (var ix = 0; ix < vpc.PrivateSubnets.Length; ix++) { var instance = new Instance_(this, $"Instance-{ix}", new InstanceProps { InstanceType = InstanceType.Of(InstanceClass.BURSTABLE3, InstanceSize.MICRO), VpcSubnets = new SubnetSelection() { SubnetType = SubnetType.PRIVATE }, AvailabilityZone = vpc.PrivateSubnets[ix].AvailabilityZone, Vpc = vpc, MachineImage = new AmazonLinuxImage(), UserData = userData, KeyName = "test-cdk", SecurityGroup = ec2SG }); instanceIds.Add(instance.InstanceId); } listener.AddTargets("Targets", new AddApplicationTargetsProps { Port = 80, Targets = instanceIds.Select(i => new InstanceIdTarget(i, 80)).ToArray() }); }
internal AppdeploymentStack(Construct scope, string id, IStackProps props = null) : base(scope, id, props) { #region Application hosting resources var vpc = new Vpc(this, "appVpc", new VpcProps { MaxAzs = 3 }); var image = new LookupMachineImage(new LookupMachineImageProps { // maps to "Amazon Linux 2 with .NET Core 3.0 and Mono 5.18" Name = "amzn2-ami-hvm-2.0.*-x86_64-gp2-mono-*", Owners = new [] { "amazon" } }); var userData = UserData.ForLinux(); userData.AddCommands(new string[] { "sudo yum install -y httpd", "sudo systemctl start httpd", "sudo systemctl enable httpd" }); var scalingGroup = new AutoScalingGroup(this, "appASG", new AutoScalingGroupProps { Vpc = vpc, InstanceType = InstanceType.Of(InstanceClass.BURSTABLE3, InstanceSize.MEDIUM), MachineImage = image, MinCapacity = 1, MaxCapacity = 4, AllowAllOutbound = true, UserData = userData }); var alb = new ApplicationLoadBalancer(this, "appLB", new ApplicationLoadBalancerProps { Vpc = vpc, InternetFacing = true }); var albListener = alb.AddListener("Port80Listener", new BaseApplicationListenerProps { Port = 80 }); albListener.AddTargets("Port80ListenerTargets", new AddApplicationTargetsProps { Port = 80, Targets = new [] { scalingGroup } }); albListener.Connections.AllowDefaultPortFromAnyIpv4("Open access to port 80"); scalingGroup.ScaleOnRequestCount("ScaleOnModestLoad", new RequestCountScalingProps { TargetRequestsPerSecond = 1 }); #endregion #region CI/CD resources var _sourceOutput = new Artifact_("Source"); var _buildOutput = new Artifact_("Build"); var build = new PipelineProject(this, "CodeBuild", new PipelineProjectProps { // relative path to sample app's file (single html page for now) BuildSpec = BuildSpec.FromSourceFilename("talk-demos/appdeployment/SimplePage/buildspec.yml"), Environment = new BuildEnvironment { BuildImage = LinuxBuildImage.AMAZON_LINUX_2_2 }, }); var appDeployment = new ServerApplication(this, "appDeployment"); // we will use CodeDeploy's default one-at-a-time deployment mode as we are // not specifying a deployment config var deploymentGroup = new ServerDeploymentGroup(this, "appDeploymentGroup", new ServerDeploymentGroupProps { Application = appDeployment, InstallAgent = true, AutoRollback = new AutoRollbackConfig { FailedDeployment = true }, AutoScalingGroups = new [] { scalingGroup } }); // SecretValue.SsmSecure is not currently supported for setting OauthToken, // and haven't gotten the SecretsManager approach to work either so // resorting to keeping my token in an environment var for now! var oauthToken = SecretValue.PlainText(System.Environment.GetEnvironmentVariable("GitHubPersonalToken")); var pipeline = new Pipeline(this, "sampleappPipeline", new PipelineProps { Stages = new StageProps[] { new StageProps { StageName = "Source", Actions = new IAction[] { new GitHubSourceAction(new GitHubSourceActionProps { ActionName = "GitHubSource", Branch = "master", Repo = this.Node.TryGetContext("repo-name").ToString(), Owner = this.Node.TryGetContext("repo-owner").ToString(), OauthToken = oauthToken, Output = _sourceOutput }) } }, new StageProps { StageName = "Build", Actions = new IAction[] { new CodeBuildAction(new CodeBuildActionProps { ActionName = "Build-app", Project = build, Input = _sourceOutput, Outputs = new Artifact_[] { _buildOutput }, RunOrder = 1 }) } }, new StageProps { StageName = "Deploy", Actions = new IAction[] { new CodeDeployServerDeployAction(new CodeDeployServerDeployActionProps { ActionName = "Deploy-app", Input = _buildOutput, RunOrder = 2, DeploymentGroup = deploymentGroup }) } } } }); #endregion }
private void ConfigureLoadBalancer(Configuration settings) { if (AppVpc == null) { throw new InvalidOperationException($"{nameof(AppVpc)} has not been set. The {nameof(ConfigureVpc)} method should be called before {nameof(ConfigureLoadBalancer)}"); } if (EcsCluster == null) { throw new InvalidOperationException($"{nameof(EcsCluster)} has not been set. The {nameof(ConfigureECSClusterAndService)} method should be called before {nameof(ConfigureLoadBalancer)}"); } if (AppFargateService == null) { throw new InvalidOperationException($"{nameof(AppFargateService)} has not been set. The {nameof(ConfigureECSClusterAndService)} method should be called before {nameof(ConfigureLoadBalancer)}"); } if (settings.LoadBalancer.CreateNew) { ServiceLoadBalancer = new ApplicationLoadBalancer(this, nameof(ServiceLoadBalancer), InvokeCustomizeCDKPropsEvent(nameof(ServiceLoadBalancer), this, new ApplicationLoadBalancerProps { Vpc = AppVpc, InternetFacing = true })); LoadBalancerListener = ServiceLoadBalancer.AddListener(nameof(LoadBalancerListener), InvokeCustomizeCDKPropsEvent(nameof(LoadBalancerListener), this, new ApplicationListenerProps { Protocol = ApplicationProtocol.HTTP, Port = 80, Open = true })); ServiceTargetGroup = LoadBalancerListener.AddTargets(nameof(ServiceTargetGroup), InvokeCustomizeCDKPropsEvent(nameof(ServiceTargetGroup), this, new AddApplicationTargetsProps { Protocol = ApplicationProtocol.HTTP, DeregistrationDelay = Duration.Seconds(settings.LoadBalancer.DeregistrationDelayInSeconds) })); } else { ServiceLoadBalancer = ApplicationLoadBalancer.FromLookup(this, nameof(ServiceLoadBalancer), InvokeCustomizeCDKPropsEvent(nameof(ServiceLoadBalancer), this, new ApplicationLoadBalancerLookupOptions { LoadBalancerArn = settings.LoadBalancer.ExistingLoadBalancerArn })); LoadBalancerListener = ApplicationListener.FromLookup(this, nameof(LoadBalancerListener), InvokeCustomizeCDKPropsEvent(nameof(LoadBalancerListener), this, new ApplicationListenerLookupOptions { LoadBalancerArn = settings.LoadBalancer.ExistingLoadBalancerArn, ListenerPort = 80 })); ServiceTargetGroup = new ApplicationTargetGroup(this, nameof(ServiceTargetGroup), InvokeCustomizeCDKPropsEvent(nameof(ServiceTargetGroup), this, new ApplicationTargetGroupProps { Port = 80, Vpc = EcsCluster.Vpc, })); var addApplicationTargetGroupsProps = new AddApplicationTargetGroupsProps { TargetGroups = new[] { ServiceTargetGroup } }; if (settings.LoadBalancer.ListenerConditionType != LoadBalancerConfiguration.ListenerConditionTypeEnum.None) { addApplicationTargetGroupsProps.Priority = settings.LoadBalancer.ListenerConditionPriority; } if (settings.LoadBalancer.ListenerConditionType == LoadBalancerConfiguration.ListenerConditionTypeEnum.Path) { if (settings.LoadBalancer.ListenerConditionPathPattern == null) { throw new ArgumentNullException("Listener condition type was set to \"Path\" but no value was set for the \"TargetPathPattern\""); } addApplicationTargetGroupsProps.Conditions = new ListenerCondition[] { ListenerCondition.PathPatterns(new [] { settings.LoadBalancer.ListenerConditionPathPattern }) }; } LoadBalancerListener.AddTargetGroups("AddTargetGroup", InvokeCustomizeCDKPropsEvent("AddTargetGroup", this, addApplicationTargetGroupsProps)); } // Configure health check for ALB Target Group var healthCheck = new Amazon.CDK.AWS.ElasticLoadBalancingV2.HealthCheck(); if (settings.LoadBalancer.HealthCheckPath != null) { var path = settings.LoadBalancer.HealthCheckPath; if (!path.StartsWith("/")) { path = "/" + path; } healthCheck.Path = path; } if (settings.LoadBalancer.HealthCheckInternval.HasValue) { healthCheck.Interval = Duration.Seconds(settings.LoadBalancer.HealthCheckInternval.Value); } if (settings.LoadBalancer.HealthyThresholdCount.HasValue) { healthCheck.HealthyThresholdCount = settings.LoadBalancer.HealthyThresholdCount.Value; } if (settings.LoadBalancer.UnhealthyThresholdCount.HasValue) { healthCheck.UnhealthyThresholdCount = settings.LoadBalancer.UnhealthyThresholdCount.Value; } ServiceTargetGroup.ConfigureHealthCheck(healthCheck); ServiceTargetGroup.AddTarget(AppFargateService); }
private IApplicationTargetGroup AddGrpcTargetGroup(BenchNetwork benchNetwork, Vpc vpc, DnsValidatedCertificate certificate, ApplicationLoadBalancer lb) { var grpcTargetGroupResource = CreateGrpcTargetGroup(vpc, new Dictionary <string, object>() { { "Name", "MagicOnionBench-grpc-target" }, { "Port", benchNetwork.AlbGrpcPort.targetgroupPort }, { "Protocol", ApplicationProtocol.HTTP.ToString() }, { "ProtocolVersion", "GRPC" }, { "VpcId", vpc.VpcId }, { "TargetType", "instance" }, { "HealthCheckEnabled", true }, { "HealthCheckProtocol", Amazon.CDK.AWS.ElasticLoadBalancingV2.Protocol.HTTP.ToString() }, { "HealthyThresholdCount", 2 }, { "HealthCheckIntervalSeconds", 15 }, { "HealthCheckTimeoutSeconds", 10 }, { "HealthCheckPath", "/grpc.health.v1.Health/Check" }, { "Matcher", new Dictionary <string, string> { { "GrpcCode", "0-99" } } }, }); var targetGroup = ApplicationTargetGroup.FromTargetGroupAttributes(this, "grpc-target-group", new TargetGroupAttributes { TargetGroupArn = grpcTargetGroupResource.Ref, }); var listener = lb.AddListener("GrpcListener", new BaseApplicationListenerProps { Port = benchNetwork.AlbGrpcPort.listenerPort, Protocol = ApplicationProtocol.HTTPS, Certificates = new[] { new ListenerCertificate(certificate.CertificateArn) }, }); listener.AddTargetGroups("GrpcTargetGroupAttachment", new AddApplicationTargetGroupsProps { TargetGroups = new[] { targetGroup }, }); listener.Node.AddDependency(grpcTargetGroupResource); return(targetGroup); }
private IApplicationTargetGroup AddHttpsTargetGroup(BenchNetwork benchNetwork, Vpc vpc, DnsValidatedCertificate certificate, ApplicationLoadBalancer lb) { var targetGroup = new ApplicationTargetGroup(this, $"{StackName}-https-target-group", new ApplicationTargetGroupProps { Port = benchNetwork.AlbHttpsPort.targetgroupPort, Protocol = ApplicationProtocol.HTTP, Vpc = vpc, TargetType = TargetType.INSTANCE, HealthCheck = new Amazon.CDK.AWS.ElasticLoadBalancingV2.HealthCheck { Enabled = true, Protocol = Amazon.CDK.AWS.ElasticLoadBalancingV2.Protocol.HTTP, HealthyThresholdCount = 2, Interval = Duration.Seconds(15), Timeout = Duration.Seconds(10), Path = "/health", }, DeregistrationDelay = Duration.Seconds(30), }); var listener = lb.AddListener("HttpsListener", new BaseApplicationListenerProps { Port = benchNetwork.AlbHttpsPort.listenerPort, Protocol = ApplicationProtocol.HTTPS, Certificates = new[] { new ListenerCertificate(certificate.CertificateArn) }, }); listener.AddTargetGroups("HttpsTargetGroupAttachment", new AddApplicationTargetGroupsProps { TargetGroups = new[] { targetGroup }, }); return(targetGroup); }
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 }); }
public ApiStack(Construct parent, string id, IApiStackProps props) : base(parent, id, props) { var cluster = new Cluster( this, "Example", new ClusterProps { Vpc = props.Vpc, }); var logging = new AwsLogDriver(new AwsLogDriverProps { StreamPrefix = "Example", }); var taskDef = new FargateTaskDefinition( this, "Task", new FargateTaskDefinitionProps { MemoryLimitMiB = 512, Cpu = 256, }); var repo = Repository.FromRepositoryName( this, "EcrRepository", props.Repository.RepositoryName); var imageTag = new CfnParameter( this, props.ApiImageTag, new CfnParameterProps { Default = "latest", }); var container = new ContainerDefinition( this, "ApiContainer", new ContainerDefinitionProps { TaskDefinition = taskDef, Image = ContainerImage.FromEcrRepository(repo, imageTag.ValueAsString), Logging = logging, }); container.AddPortMappings(new PortMapping { ContainerPort = 80, HostPort = 80, Protocol = Amazon.CDK.AWS.ECS.Protocol.TCP, }); var loadBalancer = new ApplicationLoadBalancer( this, "LoadBalancer", new ApplicationLoadBalancerProps { Vpc = props.Vpc, Http2Enabled = false, IdleTimeout = Duration.Seconds(5), InternetFacing = true, IpAddressType = IpAddressType.IPV4, VpcSubnets = new SubnetSelection { Subnets = props.Vpc.PublicSubnets, }, }); var ecsService = new ApplicationLoadBalancedFargateService( this, "Service", new ApplicationLoadBalancedFargateServiceProps { Cluster = cluster, TaskDefinition = taskDef, AssignPublicIp = false, PublicLoadBalancer = true, LoadBalancer = loadBalancer, }); PrintLoadBalancerDnsName(ecsService); }