Skip to content

Commit

Permalink
Feat(python/fargate): Added a Python example for Fargate Service with…
Browse files Browse the repository at this point in the history
… EFS (#1045)

* added initial resources

* Create cdk.json

* Create requirements.txt

* added access point and task definition

* added volume and mount point to container def

* added faraget service and scalable target

* fix ecs sample container_path

* updated task role iam policy

* added env variable in ecs task role condition

* added prefix, app path and volume variables

* Added ReadMe, updated aws-cdk-lib, cleaned up and commented app,py

* Updated Readme

* Updated service name to aws-fargate-service-with-efs

* rename stack to aws-fargate-service-with-efs

* revert stack name to aws-fargate-application-autoscaling

---------

Co-authored-by: Jacky Fan <[email protected]>
Co-authored-by: Bruce McLeod <[email protected]>
Co-authored-by: Jacky Fan <[email protected]>
Co-authored-by: Michael Kaiser <[email protected]>
  • Loading branch information
5 people authored Aug 3, 2024
1 parent 4d19f89 commit 4833109
Show file tree
Hide file tree
Showing 4 changed files with 303 additions and 0 deletions.
50 changes: 50 additions & 0 deletions python/ecs/fargate-service-with-efs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Fargate Service using EFS
This is a CDK sample that creates a public facing load balanced Fargate service with an EFS Filesystem mount. The intention of this sample is to demonstrate how shared storage can be deployed and utilised that is seperate from your compute, which, in this case is AWS Fargate.

This sample is Is based on this blog post: https://aws.amazon.com/blogs/aws/amazon-ecs-supports-efs/

## Sample overview

The `cdk.json` file tells the CDK Toolkit how to execute your app.

This project is set up like a standard Python project. The initialization process also creates a virtualenv within this project, stored under the `.env` directory. To create the virtualenv it assumes that there is a `python3` (or `python` for Windows) executable in your path with access to the `venv` package. If for any reason the automatic creation of the virtualenv fails, you can create the virtualenv manually.

To manually create a virtualenv on MacOS and Linux:

```
$ python3 -m venv .env
```

After the init process completes and the virtualenv is created, you can use the following
step to activate your virtualenv.

```
$ source .env/bin/activate
```

If you are a Windows platform, you would activate the virtualenv like this:

```
% .env\Scripts\activate.bat
```

Once the virtualenv is activated, you can install the required dependencies.

```
$ pip install -r requirements.txt
```

If you have not deployed with CDK into your account before, you will need to bootsrap the account and environment with

```
$ cdk bootstrap
```

At this point you can now deploy the CloudFormation template for this code.

```
$ cdk deploy
```

## Testing the app
Upon successful deployment, you should see a new Amazon Elastic Container Service cluster deployed with the name "AWS-CDK-Fargate-efssampleCluster<Unique ID>". Similarly you should see an Amazon EFS Volume deployed with the name "AWS-CDK-Fargate-/efs-sample-EFS(<Unique ID>)", and an Application Load Balancer "AWS-CD-efssa--<Unique ID>"
184 changes: 184 additions & 0 deletions python/ecs/fargate-service-with-efs/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
#!/usr/bin/env python3
from aws_cdk import (
aws_ec2 as ec2,
aws_ecs as ecs,
aws_efs as efs,
aws_iam as iam,
aws_logs as logs,
aws_ecs_patterns as ecs_patterns,
App, Stack
)
from constructs import Construct
import os

class FargateServiceWithEfs(Stack):

def __init__(self, scope: Construct, id: str, **kwargs) -> None:
super().__init__(scope, id, *kwargs)

PREFIX = 'efs-sample-'
APP_PATH = '/var/www/'
VOLUME_NAME = 'cdk-ecs-sample-efs-volume'

vpc = ec2.Vpc(
self, PREFIX + 'Vpc',
max_azs=2
)

ecs_cluster = ecs.Cluster(
self, PREFIX + 'Cluster',
vpc=vpc,
)

# Create an Amazon Elastic File System (EFS), with the logical ID CDK-efs-sample-EFS
file_system = efs.FileSystem(
self, PREFIX + 'EFS',
vpc=vpc,
lifecycle_policy=efs.LifecyclePolicy.AFTER_14_DAYS,
performance_mode=efs.PerformanceMode.GENERAL_PURPOSE,
)

# Create an Access Point for the EFS, with the logical ID CDK-efs-sample-AccessPoint
access_point = efs.AccessPoint(
self, PREFIX + 'AccessPoint',
file_system=file_system,
)

# Create a new EFS volume configuration for the ECS Task
efs_volume_configuration = ecs.EfsVolumeConfiguration(
file_system_id=file_system.file_system_id,

# The logical ID of the Access Point to use.
# This is a string, not an ARN.
authorization_config=ecs.AuthorizationConfig(
access_point_id=access_point.access_point_id,
iam='ENABLED',
),
transit_encryption='ENABLED',
)

# Create a new IAM Role for the ECS Task
task_role = iam.Role (
self, PREFIX + 'EcsTaskRole',
assumed_by=iam.ServicePrincipal('ecs-tasks.amazonaws.com').with_conditions({
"StringEquals": {
"aws:SourceAccount": Stack.of(self).account
},
"ArnLike":{
"aws:SourceArn":"arn:aws:ecs:" + Stack.of(self).region + ":" + Stack.of(self).account + ":*"
},
}),
)

# Attach a managed policy to the IAM Role
task_role.attach_inline_policy(
iam.Policy(self, PREFIX +'Policy',
statements=[
iam.PolicyStatement(
effect=iam.Effect.ALLOW,
resources=['*'],
actions=[
"ecr:GetAuthorizationToken",
"ec2:DescribeAvailabilityZones"
]
),
iam.PolicyStatement(
sid='AllowEfsAccess',
effect=iam.Effect.ALLOW,
resources=['*'],
actions=[
'elasticfilesystem:ClientRootAccess',
'elasticfilesystem:ClientWrite',
'elasticfilesystem:ClientMount',
'elasticfilesystem:DescribeMountTargets'
]
)
]
)
)

# Create a new Fargate Task Definition
task_definition = ecs.FargateTaskDefinition(
self, PREFIX + 'FargateTaskDef',
task_role=task_role,
)

# Add a new volume to the Fargate Task Definition
task_definition.add_volume(
name=VOLUME_NAME,
efs_volume_configuration=efs_volume_configuration,
)

# Add a new container to the Fargate Task Definition
mount_point = ecs.MountPoint(
container_path=APP_PATH+VOLUME_NAME,
source_volume=VOLUME_NAME,
read_only=False,
)

# Add a new port mapping to the Fargate Task Definition
port_mapping = ecs.PortMapping(
container_port=80,
host_port=80,
protocol=ecs.Protocol.TCP,
)

# Add a new container to the Fargate Task Definition
container = ecs.ContainerDefinition(
self, 'ecs-cdk-sample-container',
task_definition=task_definition,
image=ecs.ContainerImage.from_registry('amazon/amazon-ecs-sample'),
logging=ecs.LogDrivers.aws_logs(
stream_prefix='cdk-ecs-sample',
log_retention=logs.RetentionDays.ONE_MONTH,
)
)

# Add a new volume to the Fargate Task Definition
container.add_mount_points(mount_point),

# Add a new port mapping to the Fargate Task Definition
container.add_port_mappings(port_mapping),

# Create a new Fargate Service with ALB
fargate_service = ecs_patterns.ApplicationLoadBalancedFargateService(
self, PREFIX + 'Service',
cluster=ecs_cluster,
desired_count=1,
task_definition=task_definition,
task_subnets=ec2.SubnetSelection(
subnet_type=ec2.SubnetType.PRIVATE_WITH_EGRESS,
),
platform_version=ecs.FargatePlatformVersion.LATEST,
public_load_balancer=True,
enable_execute_command=True,
enable_ecs_managed_tags=True,

)

# Allow the ECS Service to connect to the EFS
fargate_service.service.connections.allow_from(file_system, ec2.Port.tcp(2049)),

# Allow the EFS to connect to the ECS Service
fargate_service.service.connections.allow_to(file_system, ec2.Port.tcp(2049)),

# Create a new Auto Scaling Policy for the ECS Service
scalable_target = fargate_service.service.auto_scale_task_count(
min_capacity=1,
max_capacity=20,
)

# Create a new Auto Scaling Policy for the ECS Service
scalable_target.scale_on_cpu_utilization("CpuScaling",
target_utilization_percent=50,
)

# Create a new Auto Scaling Policy for the ECS Service
scalable_target.scale_on_memory_utilization("MemoryScaling",
target_utilization_percent=50,
)

# Create the new CDK Application
cdk_application = App()
FargateServiceWithEfs(cdk_application, "aws-fargate-service-with-efs")
cdk_application.synth()
67 changes: 67 additions & 0 deletions python/ecs/fargate-service-with-efs/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
{
"app": "python3 app.py",
"watch": {
"include": [
"**"
],
"exclude": [
"README.md",
"cdk*.json",
"requirements*.txt",
"source.bat",
"**/__init__.py",
"**/__pycache__",
"tests"
]
},
"context": {
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
"@aws-cdk/core:checkSecretUsage": true,
"@aws-cdk/core:target-partitions": [
"aws",
"aws-cn"
],
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
"@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
"@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
"@aws-cdk/aws-iam:minimizePolicies": true,
"@aws-cdk/core:validateSnapshotRemovalPolicy": true,
"@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
"@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
"@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
"@aws-cdk/aws-apigateway:disableCloudWatchRole": true,
"@aws-cdk/core:enablePartitionLiterals": true,
"@aws-cdk/aws-events:eventsTargetQueueSameAccount": true,
"@aws-cdk/aws-iam:standardizedServicePrincipals": true,
"@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true,
"@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true,
"@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true,
"@aws-cdk/aws-route53-patters:useCertificate": true,
"@aws-cdk/customresources:installLatestAwsSdkDefault": false,
"@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true,
"@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true,
"@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true,
"@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true,
"@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true,
"@aws-cdk/aws-redshift:columnId": true,
"@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true,
"@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true,
"@aws-cdk/aws-apigateway:requestValidatorUniqueId": true,
"@aws-cdk/aws-kms:aliasNameRef": true,
"@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true,
"@aws-cdk/core:includePrefixInUniqueNameGeneration": true,
"@aws-cdk/aws-efs:denyAnonymousAccess": true,
"@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true,
"@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true,
"@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true,
"@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true,
"@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true,
"@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true,
"@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true,
"@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": true,
"@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse": true,
"@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": true,
"@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": true,
"@aws-cdk/aws-eks:nodegroupNameAttribute": true
}
}
2 changes: 2 additions & 0 deletions python/ecs/fargate-service-with-efs/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
aws-cdk-lib==2.144.0
constructs>=10.0.0,<11.0.0

0 comments on commit 4833109

Please sign in to comment.