Skip to content

Instantly share code, notes, and snippets.

@Ophirr33
Last active August 28, 2019 09:47
Show Gist options
  • Save Ophirr33/cdba2b4d435b3926bcb4e1d6bad7f64c to your computer and use it in GitHub Desktop.
Save Ophirr33/cdba2b4d435b3926bcb4e1d6bad7f64c to your computer and use it in GitHub Desktop.
Low level vs high level fargate stacks
// =================================================================
// | 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