よくLambdaからSlackへ投稿する方法など外部のAPIを使用したり、ウェブスクレイピングして定期的に情報を取得するなどの方法が、ネットで調べると出てきたりします。
通常ただLambdaで構築するだけだったら、何も気にせずに行えるのですが、Lambdaで、VPCの紐付けをしていると外部との接続ができなくなります。
当たり前の事かもしれませんが、Lambdaの勉強中にここでハマってしまいました。
どうやら、Lambdaに紐付けするVPC(正確にはサブネット)にNAT Gatewayを使用して外部接続が出来る様にしないと駄目との事でした。
なので、今後の為にメモ代わりに記事にして残しておきたいと思います。
これまでの記事同様、CloudFormation を利用してテンプレート化しています。
NAT GatewayとElastic IPを利用する上での注意点
NAT GatewayとElastic IPが必要な為料金が発生する可能性が存在します。
私は、学習の為の必要経費だと思い気にしませんが注意が必要だと思います。
テンプレートの作成
今回VPCの作成から行っています。
構成は以下の通りです。
- IAM ロールの作成
- VPC作成
- サブネットの作成(public)
- サブネットの作成(private)
- ルートテーブルの作成
- Internet Gatewayの作成
- Elastic IPの作成
- NAT Gatewayの作成
- WebSecurityGroupの作成
- API GatewayでのREST APIの作成
- AWS Lambdaの作成(API Gateway、WebSecurityGroup、VPC,Layerの紐付け含む)
- Layerの作成
LambdaでVPCを紐付けして行う場合公式で2つ以上のSubNetを紐付けする事が推奨されているとの事でした。
なので、外部接続を行うサブネットを2つ、実際にLambdaに紐付けするサブネットを2つ用意します。
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Lberc Slack API
Globals:
Api:
OpenApiVersion: 3.0.2
Resources:
#ここからロールの設定
LambdaExecutionRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: "sts:AssumeRole"
ManagedPolicyArns:
- !Sub 'arn:aws:iam::aws:policy/AmazonVPCFullAccess'
# ここからEIP の設定
NATGatewayEIP:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
# ここからEIP の設定
NATGateway02EIP:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
# ここからNAT Gatewayの設定
NATGateway:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt NATGatewayEIP.AllocationId
SubnetId: !Ref PublicSubNet01a
Tags:
- Key: Name
Value: Sample-VPC-NATGateway-a
NATGateway02:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt NATGateway02EIP.AllocationId
SubnetId: !Ref PublicSubNet02a
Tags:
- Key: Name
Value: Sample-VPC-NATGateway02-a
# ここからVPCの作成
Sample-Vpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsSupport: 'true'
EnableDnsHostnames: 'true'
Tags:
- Key: Name
Value: Sample--VPC
# ここからSubnet Publcの設定
PublicSubNet01a:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: ap-northeast-1a
CidrBlock: 10.0.10.0/24
Tags:
- Key: Name
Value: PublicSubNet
VpcId: !Ref Sample-Vpc
# ここからSubnet Publcの設定
PublicSubNet02a:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: ap-northeast-1a
CidrBlock: 10.0.20.0/24
Tags:
- Key: Name
Value: PublicSubNet02
VpcId: !Ref Sample-Vpc
# ここからSubnet Privateの設定
PrivateSubNet01b:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: ap-northeast-1a
CidrBlock: 10.0.30.0/24
Tags:
- Key: Name
Value: PrivateSubNet
VpcId: !Ref Sample-Vpc
# ここからSubnet Privateの設定
PrivateSubNet02b:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: ap-northeast-1a
CidrBlock: 10.0.40.0/24
Tags:
- Key: Name
Value: PrivateSubNet02
VpcId: !Ref Sample-Vpc
# ここからインターネットゲートウェイの設定
TpInternetGateway:
Type: "AWS::EC2::InternetGateway"
Properties:
Tags:
- Key: Name
Value: Sample-VPC
TpAttachGateway:
Type: "AWS::EC2::VPCGatewayAttachment"
Properties:
InternetGatewayId: !Ref TpInternetGateway
VpcId: !Ref Sample-Vpc
# ここからルートテーブル(PublicSubNet01a)の設定
TpRouteTable01:
Type: "AWS::EC2::RouteTable"
Properties:
Tags:
- Key: Name
Value: Techpit01
VpcId: !Ref Sample-Vpc
TpRouteTableAssociation01a:
Type: "AWS::EC2::SubnetRouteTableAssociation"
Properties:
RouteTableId: !Ref TpRouteTable01
SubnetId: !Ref PublicSubNet01a
TpRoute01:
Type: "AWS::EC2::Route"
Properties:
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref TpInternetGateway
RouteTableId: !Ref TpRouteTable01
# ここからルートテーブル(PublicSubNet02a)の設定
TpRouteTable02:
Type: "AWS::EC2::RouteTable"
Properties:
Tags:
- Key: Name
Value: Techpit02
VpcId: !Ref Sample-Vpc
TpRouteTableAssociation02a:
Type: "AWS::EC2::SubnetRouteTableAssociation"
Properties:
RouteTableId: !Ref TpRouteTable02
SubnetId: !Ref PublicSubNet02a
TpRoute02:
Type: "AWS::EC2::Route"
Properties:
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref TpInternetGateway
RouteTableId: !Ref TpRouteTable02
# ここからルートテーブル(PrivateSubNet01b)の設定
TpRouteTable03:
Type: "AWS::EC2::RouteTable"
Properties:
Tags:
- Key: Name
Value: Techpit03
VpcId: !Ref Sample-Vpc
TpRouteTableAssociation03a:
Type: "AWS::EC2::SubnetRouteTableAssociation"
Properties:
RouteTableId: !Ref TpRouteTable03
SubnetId: !Ref PrivateSubNet01b
TpRoute03:
Type: "AWS::EC2::Route"
Properties:
DestinationCidrBlock: 0.0.0.0/0
RouteTableId: !Ref TpRouteTable03
NatGatewayId: !Ref NATGateway
# ここからルートテーブル(PrivateSubNet02b)の設定
TpRouteTable04:
Type: "AWS::EC2::RouteTable"
Properties:
Tags:
- Key: Name
Value: Techpit04
VpcId: !Ref Sample-Vpc
TpRouteTableAssociation04a:
Type: "AWS::EC2::SubnetRouteTableAssociation"
Properties:
RouteTableId: !Ref TpRouteTable04
SubnetId: !Ref PrivateSubNet02b
TpRoute04:
Type: "AWS::EC2::Route"
Properties:
DestinationCidrBlock: 0.0.0.0/0
RouteTableId: !Ref TpRouteTable04
NatGatewayId: !Ref NATGateway02
# WebSecurityGroupの作成
WebSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: "sample API SecurityGroup"
GroupName: sample-API-SecurityGroup
VpcId: !Ref Sample-Vpc
# ここからAPI Gatewayの設定
RestApi:
Type: AWS::Serverless::Api
Properties:
StageName: dev
OpenApiVersion: 3.0.2
DefinitionBody:
Fn::Transform:
Name: AWS::Include
Parameters:
Location: ./sample-api-dev-swagger-apigateway.yaml # API Gatewayの構成のパス
# AWS Lambdaの作成(API Gateway、WebSecurityGroup、VPC,Layerの紐付け含む)
SampleFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: sample-function
Handler: lambda_function.lambda_handler
Runtime: python3.8
Role: !GetAtt LambdaExecutionRole.Arn # Roleを紐付け
CodeUri: ./sample-function/ # Lambdaのコードのパス
Description: ''
MemorySize: 128
Timeout: 25
Events:
Api:
Type: Api
Properties:
RestApiId: !Ref RestApi # RestApiと紐付け
Path: '/v1/sample-function/{param}'
Method: GET
VpcConfig:
SecurityGroupIds:
- !Ref WebSecurityGroup # WebSecurityGroupと紐付け
SubnetIds:
- !Ref PrivateSubNet01b #Subnet(private)と紐付け
- !Ref PrivateSubNet02b #Subnet(private)と紐付け
Environment:
Variables:
ID: 1234 # 環境変数その1
PASS: hogehoge # 環境変数その2
Tags:
STAGE: dev
Layers:
- Ref: SampleLayers # Layersと紐付け
# Layerの作成
SampleLayers:
Type: AWS::Serverless::LayerVersion
Properties:
LayerName: sample-layers
Description: Sample API Resource Layer
CompatibleRuntimes:
- python3.8
ContentUri: ./sample-layers # Layersのパス
後は、MakeFileを使用するかや直接コマンドを入力してデプロイを行えば完了です。
MakeFileを利用したデプロイの方法に関しては、弊社グループの@shimajiriや私が以前投稿した以下の記事を参考にしてください。