Skip to main content

EFS and ECS

efs.yml

AWSTemplateFormatVersion: "2010-09-09"

Parameters:
  VPC:
    Type: AWS::EC2::VPC::Id
  KeyWord:
    Type: String
  SecurityGroupWeb:
    Type: AWS::EC2::SecurityGroup::Id
  Subnet1:
    #Type: List<AWS::EC2::Subnet::Id>
    Type: AWS::EC2::Subnet::Id
  Subnet2:
    Type: AWS::EC2::Subnet::Id

Resources:
  SecurityGroupEFS:
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
      VpcId: !Ref VPC
      GroupName: !Sub ${KeyWord}_SecurityGroupEFS
      GroupDescription: 'efs-filesystem securitygroup'
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 2049
          ToPort: 2049
          SourceSecurityGroupId: !Ref SecurityGroupWeb
      Tags:
        - Key: Name
          Value: !Sub ${KeyWord}_SecurityGroupEFS

  ElasticFileSystem:
    Type: 'AWS::EFS::FileSystem'
    Properties:
      BackupPolicy:
        Status: DISABLED
      Encrypted: true
      FileSystemTags:
        - Key: Name
          Value: !Sub ${KeyWord}_test_efs
      FileSystemPolicy:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Action:
              - "elasticfilesystem:ClientMount"
              - "elasticfilesystem:ClientWrite"
            Principal:
              # AWS: !GetAtt RoleECS.Arn
              AWS: '*'
      PerformanceMode: 'generalPurpose'
      ThroughputMode: 'bursting'

  MountTargetResource1:
    Type: AWS::EFS::MountTarget
    Properties:
      FileSystemId: !Ref ElasticFileSystem
      SubnetId: !Ref Subnet1
      SecurityGroups:
        - !Ref SecurityGroupEFS

  MountTargetResource2:
    Type: AWS::EFS::MountTarget
    Properties:
      FileSystemId: !Ref ElasticFileSystem
      SubnetId: !Ref Subnet2
      SecurityGroups:
        - !Ref SecurityGroupEFS

  AccessPointResource:
    Type: 'AWS::EFS::AccessPoint'
    Properties:
      FileSystemId: !Ref ElasticFileSystem
      PosixUser:
        Uid: "1000"
        Gid: "1000"
      RootDirectory:
        CreationInfo:
          OwnerGid: "1000"
          OwnerUid: "1000"
          Permissions: "0755"
        Path: "/efs/accesspoint1"

Outputs:
  SecurityGroupEFS:
    Value: !Ref SecurityGroupEFS
  ElasticFileSystem:
    Value: !Ref ElasticFileSystem
  AccessPointResource:
    Value: !Ref AccessPointResource

ecs.yml

AWSTemplateFormatVersion: '2010-09-09'
Parameters:
  KeyWord:
    Type: String
  ElasticFileSystem:
    Type: String
  SecurityGroup1:
    Type: String
  Subnet1:
    Type: String
  Subnet2:
    Type: String
  AccessPointResource:
    Type: String

Resources:
  RoleECS:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: 'Allow'
            Principal:
              Service: 'ecs-tasks.amazonaws.com'
            Action: 'sts:AssumeRole'

  ECSCluster:
    Type: 'AWS::ECS::Cluster'
    Properties:
      ClusterName: !Sub ecs-cluster-${KeyWord}
      CapacityProviders:
        - FARGATE
      Tags:
        - Key: Name
          Value: !Sub ${KeyWord}_ecs_cluster

  ECSTaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      ContainerDefinitions:
        - Name: !Sub ${KeyWord}_taskdefinition
          Image: "nginx"
          PortMappings:
            - ContainerPort: 80
          MountPoints:
            - SourceVolume: 'efs-filesystem'
              ContainerPath: '/usr/share/nginx/html'
      RequiresCompatibilities:
        - FARGATE
      Cpu: 256
      Memory: 512
      NetworkMode: awsvpc
      TaskRoleArn: !GetAtt RoleECS.Arn
      Volumes:
        - Name: 'efs-filesystem'
          EFSVolumeConfiguration:
            AuthorizationConfig:
              AccessPointId: !Ref AccessPointResource
              IAM: 'ENABLED'
            FilesystemId: !Ref ElasticFileSystem
            TransitEncryption: 'ENABLED'
      Tags:
        - Key: Name
          Value: !Sub ${KeyWord}_ecs_taskdefinition

  ECSService:
    Type: AWS::ECS::Service
    Properties:
      Cluster: !Ref ECSCluster
      DesiredCount: 0
      TaskDefinition: !Ref ECSTaskDefinition
      LaunchType: FARGATE
      NetworkConfiguration:
        AwsvpcConfiguration:
          AssignPublicIp: ENABLED
          SecurityGroups:
            - !Ref SecurityGroup1
          Subnets:
            - !Ref Subnet1
            - !Ref Subnet2
      Tags:
        - Key: Name
          Value: !Sub ${KeyWord}_ecs_service

Outputs:
  RoleECS:
    Value: !Ref RoleECS
  ECSCluster:
    Value: !Ref ECSCluster
  ECSServiceName:
    Value: !GetAtt ECSService.Name

parent_efs.yml

AWSTemplateFormatVersion: "2010-09-09"

Parameters:
  KeyWord:
    Default: nestedstacktest
    Type: String

Resources:
  EFS:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: efs.yml
      Parameters:
        KeyWord : !Ref KeyWord
        VPC: !ImportValue mystack-Vpc
        Subnet1: !ImportValue mystack-PublicSubnet1
        Subnet2: !ImportValue mystack-PublicSubnet2
        SecurityGroupWeb: !ImportValue mystack-SecurityGroupPub

  ECS:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: ecs.yml
      Parameters:
        KeyWord : !Ref KeyWord
        Subnet1: !ImportValue mystack-PublicSubnet1
        Subnet2: !ImportValue mystack-PublicSubnet2
        SecurityGroup1: !ImportValue mystack-SecurityGroupPub
        AccessPointResource: !GetAtt EFS.Outputs.AccessPointResource
        ElasticFileSystem: !GetAtt EFS.Outputs.ElasticFileSystem

Outputs:
  SecurityGroupEFS:
    Value: !GetAtt EFS.Outputs.SecurityGroupEFS
  ElasticFileSystem:
    Value: !GetAtt EFS.Outputs.ElasticFileSystem
  RoleECS:
    Value: !GetAtt ECS.Outputs.RoleECS
  ECSCluster:
    Value: !GetAtt ECS.Outputs.ECSCluster
  ECSServiceName:
    Value: !GetAtt ECS.Outputs.ECSServiceName

Makefile.efs

template_file=parent_efs.yml
package_file=parent_efs_packaged.yml
bucket_name=mybucketname
stack_name=testefs

validate:
    aws cloudformation validate-template --template-body file://efs.yml
    aws cloudformation validate-template --template-body file://ecs.yml
    aws cloudformation validate-template --template-body file://parent_efs.yml

$(package_file):
    aws cloudformation package --template-file $(template_file) --s3-bucket $(bucket_name) --output-template-file $(package_file)

package: $(package_file)

build: $(package_file)
    aws cloudformation deploy --template-file $(package_file) --stack-name $(stack_name) --tags Name=testnestedstack --capabilities CAPABILITY_IAM

events:
    aws cloudformation describe-stack-events --stack-name $(stack_name)

describe:
    aws cloudformation describe-stacks --stack-name $(stack_name)

delete:
    aws cloudformation delete-stack --stack-name $(stack_name)

clean:
    rm $(package_file)

confirm filesystem and acccess points

$ aws efs describe-file-systems
$ aws efs describe-access-points --file-system-id fs-xxxx

install efs-utils on debian EC2 instance

for detail see https://docs.aws.amazon.com/ja_jp/efs/latest/ug/installing-amazon-efs-utils.html

$ sudo apt update -y && sudo apt install -y git binutils
$ git clone https://github.com/aws/efs-utils
$ cd efs-utils
$ ./build-deb.sh
$ sudo apt install ./build/amazon-efs-utils-*_all.deb

mount and test

$ sudo mount -t efs -o tls,accesspoint=fsap-xxxx fs-xxxx:/ /mnt
or sudo mount -t efs -o tls fs-xxxx:/ /mnt

$ echo hellow world /mnt/index.html

update desired count using AWS CLI

$ aws ecs list-clusters
$ aws ecs describe-clusters --cluster xxxx
$ aws ecs list-services --cluster xxxx
$ aws ecs describe-services --cluster xxxx --services testefs-ECS-xxxx
$ aws ecs update-service --cluster xxxx --service testefs-ECS-xxxx  --desired-count 1

confirm Public IP address

$ aws ecs list-tasks --cluster xxxx
$ aws ecs describe-tasks --cluster xxxx --task xxxx
$ aws ec2 describe-network-interfaces --network-interface-ids eni-xxxx