Last active
July 26, 2023 08:40
-
-
Save cajames/3daec680b1101c8358e2ff30dfadd52a to your computer and use it in GitHub Desktop.
Sample Fargate + EFS CDK Stack. Written up here: https://caj.ms/writing/aws-fargate-with-efs
This file contains 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
import cdk = require("@aws-cdk/core"); | |
import { Vpc, Port } from "@aws-cdk/aws-ec2"; | |
import { | |
Cluster, | |
ContainerImage, | |
AwsLogDriver, | |
FargatePlatformVersion, | |
NetworkMode, | |
CfnService, | |
} from "@aws-cdk/aws-ecs"; | |
import { Certificate } from "@aws-cdk/aws-certificatemanager"; | |
import { ApplicationLoadBalancedFargateService } from "@aws-cdk/aws-ecs-patterns"; | |
import { LogGroup, RetentionDays } from "@aws-cdk/aws-logs"; | |
import { HostedZone } from "@aws-cdk/aws-route53"; | |
import { | |
AwsCustomResource, | |
AwsCustomResourcePolicy, | |
PhysicalResourceId, | |
} from "@aws-cdk/custom-resources"; | |
import { | |
FileSystem, | |
LifecyclePolicy, | |
PerformanceMode, | |
ThroughputMode, | |
} from "@aws-cdk/aws-efs"; | |
export class FargateEfsStack extends cdk.Stack { | |
public serviceUrl: string; | |
constructor( | |
scope: cdk.App, | |
id: string, | |
props?: cdk.StackProps | |
) { | |
super(scope, id, props); | |
const vpc = Vpc.fromLookup(...); | |
const cluster = Cluster.fromClusterAttributes(...); | |
const hostedZone = HostedZone.fromLookup(...); | |
const certificateArn = cdk.Fn.importValue("..."); | |
const certificate = Certificate.fromCertificateArn( | |
this, | |
"Certificate", | |
certificateArn | |
); | |
const containerImage = "..."; // For example something from a registry | |
// Create a public ALB Fargate Service, with a task definition, which | |
// we'll change in later steps. | |
const fargateService = new ApplicationLoadBalancedFargateService( | |
this, | |
"FargateService", | |
{ | |
serviceName: id, | |
cluster, | |
certificate, | |
// need platform version 1.4.0 to mount EFS volumes | |
platformVersion: FargatePlatformVersion.VERSION1_4, | |
publicLoadBalancer: true, | |
domainName: "...", | |
domainZone: hostedZone, | |
taskImageOptions: { | |
image: ContainerImage.fromRegistry(containerImage), | |
family: id, | |
containerName: "app", | |
containerPort: 9999, | |
logDriver: new AwsLogDriver({ | |
streamPrefix: "app", | |
logGroup: new LogGroup(this, "LogGroup", { | |
logGroupName: `/app/${id}`, | |
retention: RetentionDays.TWO_MONTHS, | |
}), | |
}), | |
}, | |
} | |
); | |
// Create the file system | |
const fileSystem = new FileSystem(this, "AppEFS", { | |
vpc, | |
lifecyclePolicy: LifecyclePolicy.AFTER_14_DAYS, | |
performanceMode: PerformanceMode.GENERAL_PURPOSE, | |
throughputMode: ThroughputMode.BURSTING, | |
}); | |
const volumeConfig = { | |
name: "efs-volume", | |
// this is the main config | |
efsVolumeConfiguration: { | |
fileSystemId: fileSystem.fileSystemId, | |
}, | |
}; | |
const mountPoints = [ | |
{ | |
containerPath: "/root", | |
sourceVolume: volumeConfig.name, | |
readOnly: false, | |
}, | |
]; | |
/* | |
This object is the final task definition, which includes the | |
EFS volume configurations. This definiton is created as a Custom Resource through | |
the aws-sdk. This is a stop-gap measure that will be replaced when this | |
capability is fully supported in CloudFormation and CDK. | |
*/ | |
const customTaskDefinitionJson = { | |
containerDefinitions: [ | |
{ | |
essential: true, | |
image: containerImage, | |
logConfiguration: { | |
logDriver: | |
fargateService.taskDefinition.defaultContainer?.logDriverConfig | |
?.logDriver, | |
options: | |
fargateService.taskDefinition.defaultContainer?.logDriverConfig | |
?.options, | |
}, | |
memory: 8192, | |
mountPoints, | |
name: fargateService.taskDefinition.defaultContainer?.containerName, | |
portMappings: [ | |
{ | |
containerPort: 9999, | |
protocol: "tcp", | |
}, | |
], | |
}, | |
], | |
cpu: "2048", | |
executionRoleArn: fargateService.taskDefinition.executionRole?.roleArn, | |
family: fargateService.taskDefinition.family, | |
memory: "8192", | |
networkMode: NetworkMode.AWS_VPC, | |
requiresCompatibilities: ["FARGATE"], | |
tags: [ | |
], | |
taskRoleArn: fargateService.taskDefinition.taskRole.roleArn, | |
volumes: [volumeConfig], | |
}; | |
const createOrUpdateCustomTaskDefinition = { | |
service: "ECS", | |
action: "registerTaskDefinition", | |
outputPath: "taskDefinition.taskDefinitionArn", | |
parameters: customTaskDefinitionJson, | |
physicalResourceId: PhysicalResourceId.fromResponse( | |
"taskDefinition.taskDefinitionArn" | |
), | |
}; | |
const customTaskDefinition = new AwsCustomResource( | |
fargateService, | |
"CustomFargateTaskDefinition", | |
{ | |
onCreate: createOrUpdateCustomTaskDefinition, | |
onUpdate: createOrUpdateCustomTaskDefinition, | |
policy: AwsCustomResourcePolicy.fromSdkCalls({ | |
resources: AwsCustomResourcePolicy.ANY_RESOURCE, | |
}), | |
} | |
); | |
fargateService.taskDefinition.executionRole?.grantPassRole( | |
customTaskDefinition.grantPrincipal | |
); | |
fargateService.taskDefinition.taskRole.grantPassRole( | |
customTaskDefinition.grantPrincipal | |
); | |
// Need to add permissions to and from the file system to the target, | |
// or else the task will timeout trying to mount the file system. | |
fargateService.service.connections.allowFrom(fileSystem, Port.tcp(2049)); | |
fargateService.service.connections.allowTo(fileSystem, Port.tcp(2049)); | |
/* | |
After creating the task definition custom resouce, update the | |
fargate service to use the new task definition revision above. | |
This will get around the current limitation of not being able to create | |
ecs services with task definition arns. | |
*/ | |
(fargateService.service.node.tryFindChild( | |
"Service" | |
) as CfnService)?.addPropertyOverride( | |
"TaskDefinition", | |
customTaskDefinition.getResponseField("taskDefinition.taskDefinitionArn") | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment