Add durable storage to an ECS task, with Amazon Elastic File System
CloudFormation template showing how to mount an Elastic File System to a path inside of a container.
Nathan Peck
Senior Developer Advocate at AWS
About
In this example you will deploy two NGINX web server tasks that have a shared durable web content folder stored on an Elastic File System. You will also use Amazon ECS Exec to access the containers and verify that data is synced across tasks and persisted across task restarts.
AWSTemplateFormatVersion:'2010-09-09'Transform:AWS::LanguageExtensionsDescription:An example of how to provision an Elastic File System andmount it to an ECS task running in AWS FargateParameters:VpcId:Type:AWS::EC2::VPC::IdDescription:The virtual private network into which to launch all resourcesSubnetOne:Type:AWS::EC2::Subnet::IdDescription:The first subnet across which to distribute the application andEFS accessSubnetTwo:Type:AWS::EC2::Subnet::IdDescription:The second subnet across which to distribute the application andEFS accessResources:# The ECS cluster that will be controlling the tasks in AWS FargateCluster:Type:AWS::ECS::Cluster# The filesystem itselfEFSFileSystem:Type:AWS::EFS::FileSystemProperties:Encrypted:truePerformanceMode:generalPurposeThroughputMode:bursting# Mount target allows usage of the EFS inside of subnet oneEFSMountTargetOne:Type:AWS::EFS::MountTargetProperties:FileSystemId:!Ref EFSFileSystemSubnetId:!Ref SubnetOneSecurityGroups:- !Ref EFSFileSystemSecurityGroup# Mount target allows usage of the EFS inside of subnet twoEFSMountTargetTwo:Type:AWS::EFS::MountTargetProperties:FileSystemId:!Ref EFSFileSystemSubnetId:!Ref SubnetTwoSecurityGroups:- !Ref EFSFileSystemSecurityGroup# This security group is used by the mount targets so# that they will allow inbound NFS connections from# the AWS Fargate tasks that we launchEFSFileSystemSecurityGroup:Type:AWS::EC2::SecurityGroupProperties:GroupDescription:Security group for EFS file systemVpcId:!Ref VpcIdSecurityGroupIngress:- IpProtocol:tcpFromPort:2049ToPort:2049SourceSecurityGroupId:!Ref ServiceSecurityGroup# This role is used to setup the execution environment for# the task, in this case to connect to the Elastic File SystemTaskExecutionRole:Type:AWS::IAM::RoleProperties:AssumeRolePolicyDocument:Statement:- Effect:AllowPrincipal:Service:[ecs-tasks.amazonaws.com]Action:['sts:AssumeRole']Condition:ArnLike:aws:SourceArn:!Sub arn:aws:ecs:${AWS::Region}:${AWS::AccountId}:*StringEquals:aws:SourceAccount:!Ref AWS::AccountIdManagedPolicyArns:- arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicyPolicies:- PolicyName:EFSAccessPolicyDocument:Version:'2012-10-17'Statement:- Effect:AllowAction:- elasticfilesystem:ClientMount- elasticfilesystem:ClientWrite- elasticfilesystem:DescribeMountTargets- elasticfilesystem:DescribeFileSystemsResource:!GetAtt EFSFileSystem.Arn# This role is used at runtime.TaskRole:Type:AWS::IAM::RoleProperties:AssumeRolePolicyDocument:Statement:- Effect:AllowPrincipal:Service:[ecs-tasks.amazonaws.com]Action:['sts:AssumeRole']Condition:ArnLike:aws:SourceArn:!Sub arn:aws:ecs:${AWS::Region}:${AWS::AccountId}:*StringEquals:aws:SourceAccount:!Ref AWS::AccountIdPolicies:- PolicyName:ExecAccessPolicyDocument:Version:'2012-10-17'Statement:- Effect:AllowAction:- ssmmessages:CreateControlChannel- ssmmessages:CreateDataChannel- ssmmessages:OpenControlChannel- ssmmessages:OpenDataChannelResource:'*'# Store the logs from the task for inspection and review# for up to 7 daysEfsTaskLogGroup:Type:AWS::Logs::LogGroupProperties:RetentionInDays:7# This task definition describes how to launch the application, and how# to mount the Elastic File System into the containerTaskDefinition:Type:AWS::ECS::TaskDefinitionProperties:Family:my-ecs-taskTaskRoleArn:!GetAtt TaskRole.ArnExecutionRoleArn:!GetAtt TaskExecutionRole.ArnNetworkMode:awsvpcContainerDefinitions:- Name:nginxImage:public.ecr.aws/nginx/nginx:latestEssential:trueLinuxParameters:InitProcessEnabled:trueMountPoints:- SourceVolume:efs-volumeContainerPath:/usr/share/nginx/htmlLogConfiguration:LogDriver:awslogsOptions:mode:non-blockingmax-buffer-size:25mawslogs-group:!Ref EfsTaskLogGroupawslogs-region:!Ref AWS::Regionawslogs-stream-prefix:efs-taskPortMappings:- ContainerPort:80Protocol:tcpVolumes:- Name:efs-volumeEFSVolumeConfiguration:FilesystemId:!Ref EFSFileSystemRootDirectory:/TransitEncryption:ENABLEDRequiresCompatibilities:- FARGATECpu:'256'Memory:'512'# Security group that the task will use to runServiceSecurityGroup:Type:AWS::EC2::SecurityGroupProperties:GroupDescription:Security group for serviceVpcId:!Ref VpcId# Launch multiple copies of the task as a serviceService:Type:AWS::ECS::ServiceDependsOn:# This ensures that the service doesn't# try to start tasks before the EFS filesystem is# actually available in the VPC- EFSMountTargetOne- EFSMountTargetTwoProperties:ServiceName:'ecs-efs-demo'Cluster:!Ref ClusterLaunchType:FARGATEDeploymentConfiguration:MaximumPercent:200MinimumHealthyPercent:75DesiredCount:2EnableExecuteCommand:trueNetworkConfiguration:AwsvpcConfiguration:AssignPublicIp:ENABLEDSecurityGroups:- !Ref ServiceSecurityGroupSubnets:- !Ref SubnetOne- !Ref SubnetTwoTaskDefinition:!Ref 'TaskDefinition'
This CloudFormation template provisions an Elastic File System (EFS) and mounts it to an ECS task running in AWS Fargate.
The template begins with defining parameters that will be passed into the CloudFormation stack. The stack requires the following three parameters:
VpcId - A virtual private cloud ID. This can be the default VPC that comes with your AWS account. Example value: vpc-79508710
SubnetOne - A public subnet inside of that VPC. Example value: subnet-b4676dfe
SubnetTwo - Another public subnet inside of that VPC. Example value: subnet-c71ebfae
💡 Tip: When using the CloudFormation web console it will suggest appropriate parameter values in the drop down on each parameter field, if you are not certain what to enter for each parameter.
The CloudFormation stack deploys the following resources:
Cluster: an ECS cluster that will be controlling the tasks in AWS Fargate.
EFSFileSystem: the EFS filesystem itself with properties including encryption, performance mode, and throughput mode.
EFSMountTargetOne and EFSMountTargetTwo: mount targets that allow usage of the EFS inside subnet one and subnet two, respectively.
EFSFileSystemSecurityGroup: a security group used by the mount targets that allows inbound NFS connections from the AWS Fargate tasks launched.
TaskExecutionRole: a role used to setup the execution environment for the task, including connecting to the EFS.
TaskRole: a role used by the containerized task at runtime.
EfsTaskLogGroup: a log group to store the logs from the task for up to 7 days.
TaskDefinition: describes how to launch the application and how to mount the Elastic File System into the container, with properties including the task role ARN, execution role ARN, network mode, container definitions, volumes, and requires compatibilities.
This CloudFormation template creates a complete infrastructure to run an application on AWS Fargate, which requires shared, durable file persistence.
Deploy the CloudFormation template by using the web console, or with the following AWS CLI command. (Substitute your own VPC details.)
Run the above command twice in two different terminals, and with two different task ID’s so that you have one session open to each task.
In each session type:
cd /usr/share/nginx/html
ls
This will verify that both directories are empty.
Try running the following command in both containers:
curl localhost:80
You will see a 403 Forbidden error from NGINX because there is no content in the shared HTML directory.
Run the following command in only one of the open sessions:
echo"Hello world" > index.html
In the other container’s session run ls to see that an index.html file has appeared.
Run the curl command again in both containers:
curl localhost:80
Observe that both NGINX web servers are seeing the same web content over the Elastic File System.
Test data durability
Terminate both tasks using the Amazon ECS web console, or API.
When the ECS service starts a replacement task, open a new ECS Exec session to
the replacement task.
Run the following commands again in this replacement task:
cd /usr/share/nginx/html
ls
curl localhost:80
You will see that the index.html file you wrote to the Elastic File System in the
previous instance of the container has been persisted and is still
there in the new replacement task.