目的
LambdaからEFS(Amazon Elastic File System)を使用するためのCloudFormationテンプレートを作成する
VPC内にLambdaを配置する必要があるため、以下の定義を行う
- VPC関連リソース
- EFS関連リソース
- Lambda(SQSトリガ)
- VPC LambdaからAWSリソースへアクセスするためのVPCエンドポイント
テンプレート
vpcSample:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsSupport: true
EnableDnsHostnames: true
InstanceTenancy: default
Tags:
- Key: Name # タグで名前を設定する
Value: !Sub ${EnvName}-vpcSample
subnetSample1:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone:
Fn::Select:
- 0 # 使用可能なAZリストのindex0を使用
- !GetAZs # 使用可能なAZリスト取得
Ref: AWS::Region
CidrBlock: 10.0.0.0/24
VpcId: !Ref vpcSample
Tags:
- Key: Name # タグで名前を設定する
Value: !Sub ${EnvName}-subnetSample1
subnetSample2:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone:
Fn::Select:
- 1
- !GetAZs
Ref: AWS::Region
CidrBlock: 10.0.1.0/24
VpcId: !Ref vpcSample
Tags:
- Key: Name
Value: !Sub ${EnvName}-subnetSample2
# VPC作成時に自動作成されるメインルートテーブルは、
# cfnから参照できないためルートテーブルを作成
routeTableSample:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref vpcSample
# ルートテーブルとサブネットの関連付け
subnetRouteTableSample1:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref routeTableSample
SubnetId: !Ref subnetSample1
subnetRouteTableSample2:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref routeTableSample
SubnetId: !Ref subnetSample2
# VPC LambdaからS3にアクセスするためのVPCエンドポイント
vpcEndpointS3Sample:
Type: AWS::EC2::VPCEndpoint
Properties:
VpcId: !Ref vpcSample
VpcEndpointType: Gateway
ServiceName: !Sub com.amazonaws.${AWS::Region}.s3
RouteTableIds:
- !Ref routeTableSample
# VPC LambdaからDynamoDBにアクセスするためのVPCエンドポイント
vpcEndpointDynamodbSample:
Type: AWS::EC2::VPCEndpoint
Properties:
VpcId: !Ref vpcSample
VpcEndpointType: Gateway
ServiceName: !Sub com.amazonaws.${AWS::Region}.dynamodb
RouteTableIds:
- !Ref routeTableSample
# VPC LambdaからSQSにアクセスするためのVPCエンドポイント
vpcEndpointSqsSample:
Type: AWS::EC2::VPCEndpoint
Properties:
VpcId: !Ref vpcSample
VpcEndpointType: Interface # Gatewayタイプが対応しているのはS3, DynamoDBのみ
PrivateDnsEnabled: true
ServiceName: !Sub com.amazonaws.${AWS::Region}.sqs
SubnetIds:
- !Ref subnetSample1
- !Ref subnetSample2
SecurityGroupIds:
- !GetAtt securityGroupSampleSQS.GroupId
# Lambdaが使用するセキュリティグループ
securityGroupSampleLambda:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub ${EnvName}-securityGroupSampleLambda
GroupDescription: for lambda
VpcId: !Ref vpcSample
SecurityGroupEgress:
- CidrIp: 0.0.0.0/0
IpProtocol: -1
# EFSに設定するセキュリティグループ
securityGroupSampleEFS:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub ${EnvName}-securityGroupSampleEFS
GroupDescription: for efs
VpcId: !Ref vpcSample
SecurityGroupEgress:
- CidrIp: 0.0.0.0/0
IpProtocol: -1
SecurityGroupIngress:
- IpProtocol: tcp
SourceSecurityGroupId: !GetAtt securityGroupSampleLambda.GroupId # Lambdaが使用するsgを通す
FromPort: 2049 # NFSに使用するポート番号
ToPort: 2049
# SQS用のVPCエンドポイントに設定するセキュリティグループ
securityGroupSampleSQS:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub ${EnvName}-securityGroupSampleSQS
GroupDescription: for sqs
VpcId: !Ref vpcSample
SecurityGroupEgress:
- CidrIp: 0.0.0.0/0
IpProtocol: -1
SecurityGroupIngress:
- IpProtocol: tcp
SourceSecurityGroupId: !GetAtt securityGroupSampleLambda.GroupId # Lambdaが使用するsgを通す
FromPort: 443 # HTTPSに使用するポート番号
ToPort: 443
efsFileSystemSample:
Type: AWS::EFS::FileSystem
Properties:
BackupPolicy:
Status: ENABLED
PerformanceMode: generalPurpose
Encrypted: true
LifecyclePolicies:
- TransitionToIA: AFTER_30_DAYS
- TransitionToPrimaryStorageClass: AFTER_1_ACCESS
FileSystemTags:
- Key: Name # タグで名前を設定する
Value: !Sub ${EnvName}-efsFileSystemSample
efsMountTargetSample1:
Type: AWS::EFS::MountTarget
Properties:
FileSystemId: !Ref efsFileSystemSample
SubnetId: !Ref subnetSample1
SecurityGroups:
- !GetAtt securityGroupSampleEFS.GroupId
efsMountTargetSample2:
Type: AWS::EFS::MountTarget
Properties:
FileSystemId: !Ref efsFileSystemSample
SubnetId: !Ref subnetSample2
SecurityGroups:
- !GetAtt securityGroupSampleEFS.GroupId
efsAccessPointSample:
Type: AWS::EFS::AccessPoint
Properties:
FileSystemId: !Ref efsFileSystemSample
PosixUser: # このアクセスポイントを使用するLambdaが使用するユーザ/グループID
Uid: 1001
Gid: 1001
RootDirectory:
CreationInfo: # ディレクトリ作成者のユーザ/グループID
OwnerGid: 1001
OwnerUid: 1001
Permissions: 755 # パーミッション
Path: /my-function # ファイルシステム上に生成されるディレクトリ
SampleLambdaRole:
Type: AWS::IAM::Role
Properties:
RoleName: SampleLambdaRole
Path: /
MaxSessionDuration: 3600
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: SampleVpcAccess
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Resource: [
'*'
]
Action: [
ec2:CreateNetworkInterface,
ec2:DescribeNetworkInterfaces,
ec2:DeleteNetworkInterface,
ec2:AssignPrivateIpAddresses,
ec2:UnassignPrivateIpAddresses
]
- PolicyName: SampleEfsAccess
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Resource: [
'*'
]
Action: [
elasticfilesystem:ClientMount,
elasticfilesystem:ClientWrite,
elasticfilesystem:DescribeMountTargets
]
sqsSampleLambda:
Type: AWS::Serverless::Function
DependsOn:
- efsMountTargetSample1 # LambdaからEFS使用に必須
- efsMountTargetSample2
Properties:
Handler: main.lambda_handler
Runtime: python3.7
Role: !Sub arn:aws:iam::${AWS::AccountId}:role/SampleLambdaRole
# ...省略
Environment:
Variables:
DNS_NAME: !Sub https://sqs.${AWS::Region}.amazonaws.com # VPC LambdaからSQS使用の際に必要
Events:
sqsSampleQueueEvent:
Type: SQS # LambdaのトリガとしてSQSを使用するのみであればSQS用のVPCエンドポイントは不要
Properties:
Queue:
Fn::GetAtt:
- sqsSampleQueue
- Arn
BatchSize: 1
VpcConfig: # VPC設定
SecurityGroupIds:
- !GetAtt securityGroupSampleLambda.GroupId
SubnetIds:
- !Ref subnetSample1
- !Ref subnetSample2
FileSystemConfigs: # EFS設定
- Arn: !GetAtt efsAccessPointSample.Arn
LocalMountPath: /mnt/efs # /mnt/efs にアクセスポイントパス /my-function がマウントされる
sqsSampleQueue:
Type: AWS::SQS::Queue
Properties:
DelaySeconds: 0
KmsMasterKeyId: alias/aws/sqs
# ...省略
EFSアクセスポイント定義内容
EFSアクセスポイントには以下の設定を行う
- ディレクトリパス
- ユーザID、グループID(このアクセスポイントを使用してアクセスした者に割り当たるID)
- ディレクトリ所有者のユーザID、グループID
- パーミッション
上記テンプレートは、
ユーザID、グループID(Lambdaに割り当てるID)とディレクトリ所有者のユーザID、グループIDと同じとし、
Lambdaはディレクトリ所有者としてアクセスするよう設定している
パーミッションは755とし、ディレクトリ所有者は読み書き可能にしている
EFSアクセスポイントの注意点
アクセスポイントを作成すると、
EFS上に指定した属性(ディレクトリ所有者のユーザID、グループID、パーミッション)でディレクトリが作成される
このディレクトリは、アクセスポイントの削除や、同一ディレクトリパスでアクセスポイントを追加しても削除/更新されない
VPC LambdaからAWSリソースアクセス時の注意点
VPC Lambdaからboto3でAWSリソースアクセス時、以下のようにendpoint_url
を指定する必要があるものがある
このオプションがない場合、アクセスタイムアウトが発生する
Environment:
Variables:
DNS_NAME: !Sub https://sqs.${AWS::Region}.amazonaws.com # VPC LambdaからSQS使用の際に必要
sqs_dns_name = os.environ.get('SQS_DNS_NAME')
sqs = boto3.resource('sqs', endpoint_url=sqs_dns_name)
SQS以外にSTSも同様にendpoint_url
が必要だったが、すべてのAWSリソースアクセスに必要なわけではない
(S3は不要だった)
client = boto3.client('sts', endpoint_url=dns_name)