Last active
August 28, 2019 09:47
-
-
Save Ophirr33/cdba2b4d435b3926bcb4e1d6bad7f64c to your computer and use it in GitHub Desktop.
Low level vs high level fargate stacks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ================================================================= | |
// | High level vs Low level "App stacks" | | |
// ================================================================= | |
// | |
// This works, but it creates too many resources (security groups, ingress/egress rules) and can't | |
// be deployed in the locked down environment I work with. Additionally, I would like to pass in | |
// a log group. | |
class HighLevelStack( | |
parent: Construct, | |
id: String, | |
env: MyEnv, | |
region: MyRegion, | |
ecsCluster: Cluster | |
) extends Stack(parent, id, StackProps.builder().withEnv(env.toAmazonENV(region)).build()) { | |
val loadBalancedService = new LoadBalancedFargateService( | |
this, | |
"FargateService", | |
LoadBalancedFargateServiceProps | |
.builder() | |
.withCluster(ecsCluster) | |
.withCpu(512) | |
.withMemoryLimitMiB(2048) | |
.withServiceName(properName("fargate-service")) | |
.withImage(ContainerImage.fromRegistry("nginx:latest")) | |
.withEnableLogging(true) | |
.withPublicLoadBalancer(false) | |
.build() | |
) | |
} | |
// This low level service is effectively the same as the high level service, it just | |
// allows a log group to be passed in as well as doesn't create security | |
// groups/ingress & egress rules | |
class LowLevelStack( | |
parent: Construct, | |
id: String, | |
region: MyRegion, | |
env: MyEnv, | |
externalIdentityLogGroup: LogGroup, | |
ecsCluster: Cluster | |
) extends Stack(parent, id, StackProps.builder().withEnv(env.toAmazonENV(region)).build()) { | |
val vpc = env.vpc(this) | |
val sg = env.httpSecurityGroup(this) | |
val containerName = "web" | |
val task = new FargateTaskDefinition( | |
this, | |
"DemoTask", | |
FargateTaskDefinitionProps | |
.builder() | |
.withCpu(256) | |
.withMemoryLimitMiB(2048) | |
.build | |
) | |
val logDriver = | |
AwsLogDriverProps.builder.withLogGroup(externalIdentityLogGroup).withStreamPrefix(properName("ecs")).build | |
val container = task.addContainer( | |
containerName, | |
ContainerDefinitionOptions | |
.builder() | |
.withEssential(true) | |
.withImage(ContainerImage.fromRegistry("nginx:latest")) | |
.withLogging(LogDriver.awsLogs(logDriver)) | |
.build | |
) | |
container.addPortMappings(PortMapping.builder().withContainerPort(80).build) | |
val service = new FargateService( | |
this, | |
"DemoFargateService", | |
FargateServiceProps | |
.builder() | |
.withCluster(ecsCluster) | |
.withDesiredCount(1) | |
.withVpcSubnets(env.subnetSelection) | |
.withSecurityGroup(sg) | |
// https://aws.amazon.com/premiumsupport/knowledge-center/cloudformation-custom-name/ | |
.withServiceName(properName("service")) | |
.withAssignPublicIp(false) | |
.withTaskDefinition(task) | |
.build | |
) | |
val loadBalancer = new ApplicationLoadBalancer( | |
this, | |
"ALB", | |
ApplicationLoadBalancerProps | |
.builder() | |
.withInternetFacing(false) | |
.withLoadBalancerName(lengthCheck(properName("alb"))) | |
.withSecurityGroup(sg) | |
.withVpc(vpc) | |
.withVpcSubnets(env.subnetSelection) | |
.build | |
) | |
val targetGroup = new CfnTargetGroup( | |
this, | |
"DemoFargateTargetGroup", | |
CfnTargetGroupProps | |
.builder() | |
.withTargetType("ip") | |
.withPort(80) | |
.withProtocol("HTTP") | |
.withVpcId(vpc.getVpcId) | |
.build | |
) | |
val listener = new CfnListener( | |
this, | |
"DemoHTTPListener", | |
CfnListenerProps | |
.builder() | |
.withLoadBalancerArn(loadBalancer.getLoadBalancerArn) | |
.withPort(80) | |
.withProtocol("HTTP") | |
.withDefaultActions( | |
objectList(CfnListener.ActionProperty | |
.builder | |
.withTargetGroupArn(targetGroup.getRef) | |
.withType("forward") | |
.build | |
)) | |
.build | |
) | |
val prop = CfnService.LoadBalancerProperty | |
.builder() | |
.withContainerName(containerName) | |
.withContainerPort(80) | |
.withTargetGroupArn(targetGroup.getRef) | |
.build() | |
val cfnNode = service.getNode | |
cfnNode.addDependency(listener) | |
cfnNode.findChild("Service").asInstanceOf[CfnService].setLoadBalancers(objectList(prop)) | |
private def objectList(args: Any*): java.util.List[Object] = args.asJava.asInstanceOf[java.util.List[Object]] | |
} | |
// ================================================================= | |
// | Shared Dependency Stacks | | |
// ================================================================= | |
// Both the high level and low level stack use a passed-in cluster | |
class ECSClusterStack( | |
parent: Construct, | |
id: String, | |
env: MyEnv, | |
region: MyRegion | |
) extends Stack(parent, id, StackProps.builder().withEnv(env.toAmazonENV(region)).build()) { | |
val vpc = env.vpc(this) | |
val generalCluster = new Cluster( | |
this, | |
"Cluster", | |
ClusterProps | |
.builder() | |
.withClusterName(s"demo-ecs-cluster-$env") | |
.withVpc(vpc) | |
.build | |
) | |
} | |
// I want the high level stack to use demoLogGroup | |
class LogGroupsStack( | |
parent: Construct, | |
id: String, | |
env: MyEnv, | |
region: MyRegion | |
) extends Stack(parent, id, StackProps.builder().withEnv(env.toAmazonENV(region)).build()) { | |
val demoLogGroup = new LogGroup( | |
this, | |
"LogGroupDemo", | |
LogGroupProps.builder | |
.withLogGroupName(s"/demo/$env") | |
.build | |
) | |
} | |
// ================================================================= | |
// | Helper traits for environment/region and vpc importing | | |
// ================================================================= | |
// implemented for each of our different environments, e.g., Prod/Test | |
sealed trait MyEnv { | |
def vpc(scope: Construct): IVpc | |
def subnetSelection: SubnetSelection | |
def toAmazonENV(region: MyRegion): Environment | |
def securityGroupId: String | |
def httpSecurityGroup(scope: Construct): ISecurityGroup = SecurityGroup.fromSecurityGroupId( | |
scope, | |
"HTTPSecurityGroup", | |
securityGroupId | |
) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment