はじめに
はじめまして。開発部の春日と申します。2020年の10月末に入社し、記事執筆時点ではエンジニア歴4ヶ月目の見習いエンジニアです。
冬も終わりにさしかかり、春が近づいてきました。私は花粉症持ちなので一足先に耳鼻科に行き点鼻薬と点眼薬を確保してきました。花粉症持ちの方はともにこの春を耐え抜きましょう。。
昨年末から始まったエンジニアブログの第3弾として、僭越ながら私が担当することになりました。
本記事ではAWS SAMでVPC内LambdaからVPC外Lambdaを呼び出すための設定について解説したいと思います。
AWSサービスについて
まず最初にAWSサービスのLambda、VPC、SAMについて説明します。
Lambdaはサーバーレスアプリケーションサービスの一つです。自身でサーバーを立ち上げて管理することなく、「実行したい処理」のみを意識して開発することができます。
VPCはAWSのクラウド内で構築できるユーザー専用の仮想ネットワークです。弊社のサービスの多くはAWSのVPC内に配置され、稼働しています。
SAMはサーバーレスアプリケーションのインフラ構成をコードで管理できるフレームワークです。SAMはCloudFormationの拡張であり、なおかつLambdaのインフラ管理に特化したサービスです。
VPC LambdaからLambdaを呼び出す
Lambdaは基本的にVPCを使用せずに利用できるサービスですが、VPC内のプライベートリソースと通信を行いたい場合にはVPCの中にLambdaを配置する必要があります。例えばRDSにLambdaで何かしらの更新処理を加えたいなどといった場合は同じVPC内にLambdaを配置することになります。
ただし一つ注意点があり、VPC内に配置されたLambdaはそのまま使おうとするとVPC外のリソースやインターネットにアクセスできなくなります。今回のテーマである「VPC内Lambda→VPC外Lambda」を実現するためには、ネットワーク設定を考える必要があります。
インフラ構成をどうするか
VPC内にLambdaを設置し、かつ外部との通信を行いたい場合のインフラ構成としてまず最初に思いくのがNAT Gatewayを使用することです。
NAT Gatewayを使用して下図のように設計することで、VPC外のLambdaと通信することができます。
しかし、今回実現したい「VPC内LambdaからVPC外Lambdaにアクセスする」という目的を考えるといささか大げさな構成である印象を受けます。
必要最低限の構成にして、運用にかかる料金もできるだけ抑えたい。
そこで役立つのがインターフェイスエンドポイントというサービスです。
インターフェースエンドポイントとは、「特定のAWSサービスに閉じたネットワークで接続するためのVPCエンドポイント」です。
インターフェイスエンドポイントを用いることでインターネットゲートウェイやNAT Gatewayを使用することなくミニマルなインフラ構成でVPC外のAWSリソースへのアクセス(private link)ができるようになります。
インターフェースエンドポイントを組み込んだインフラ構成は下図のようになります。
(NAT Gatewayの構成図もインターフェースエンドポイントの構成図も便宜上シングルAZ構成ですが、Lambdaの高可用性を確保するためにも本番環境ではマルチAZで構成することを推奨します)
NAT Gatewayの構成図と比較して非常にシンプルな構成であることがわかると思います。今回のようなケースでは、インターフェースエンドポイントでインフラを構成したほうが運用料金も安くなる可能性が高いです。
SAMでインフラを構築する
それでは、このインターフェースエンドポイントを使用したインフラをSAMで設定したいと思います。
※要点をかいつまんで、VPCエンドポイントとVPC内のLambdaの設定のみご紹介します。
AWSTemplateFormatVersion "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: VPC Lambda to Lambda
Parameters:
〜パラメータ省略〜
Resources:
VPCEndpointSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
VpcId: !Ref VPC
SecurityGroupIngress:
- CidrIp: 0.0.0.0/0
IpProtocol: tcp
FromPort: 443
ToPort: 443
GroupDescription: "Security-Group for VPC Endpoint"
LambdaEndpoint:
Type: 'AWS::EC2::VPCEndpoint'
Properties:
VpcEndpointType: Interface
PrivateDnsEnabled: true
SecurityGroupIds:
- !Ref VPCEndpointSecurityGroup
SubnetIds:
- !Ref PrivateSubnet1
- !Ref PrivateSubnet2
ServiceName: !Sub 'com.amazonaws.${AWS::Region}.lambda'
VpcId: !Ref VPC
LambdaSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
VpcId: !Ref VPC
GroupName: LambdaSecurityGroup
GroupDescription: "Security-Group for Lambda function"
VPCLambdaExecutionRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Service:
- "[lambda.amazonaws.com](http://lambda.amazonaws.com/)"
Action:
- "sts:AssumeRole"
VPCLambdaExecutionPolicy:
Type: "AWS::IAM::Policy"
Properties:
PolicyName: !Sub '${AWS::StackName}-vpc-lambda-execution-policy'
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action:
- "logs:CreateLogGroup"
- "logs:CreateLogStream"
- "logs:PutLogEvents"
- "ec2:CreateNetworkInterface"
- "ec2:DescribeNetworkInterfaces"
- "ec2:DetachNetworkInterface"
- "ec2:DeleteNetworkInterface"
- "lambda:InvokeFunction"
Resource: "*"
Roles:
- !Ref VPCLambdaExecutionRole
VPCFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: cmd
Handler: vpc_function.handleRequest
Runtime: go1.x
Timeout: !Ref LambdaFunctionTimeout
VpcConfig:
SecurityGroupIds:
- !Ref LambdaSecurityGroup
SubnetIds:
- !Ref PrivateSubnet1
- !Ref PrivateSubnet2
OuterFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: cmd
Handler: vpc_function.handleRequest
Runtime: go1.x
Timeout: !Ref LambdaFunctionTimeout
躓きやすいポイントとしてはVPCエンドポイントのセキュリティグループの設定やLambdaに付与する権限だと思います。
今回のケースではLambdaからHTTPSリクエストを送っているので、VPCエンドポイントのセキュリティグループのインバウンドルールで適切に443番ポートを開いてあげる必要があります。ちなみにアウトバウンドルールはテンプレートファイル内で設定しなくてもデフォルトで全て許可になっています。
Lambdaに付与する権限にも注意が必要です。SAMはCloudFormationとは異なり、Lambdaにロールを明示的に設定しなくても裏側でロールを生成してくれますが、別のLambdaを呼び出す場合は自分でポリシーとロールを設定する必要があります。上記のテンプレートで設定している権限について軽くご説明します。
- "logs:CreateLogGroup"
- "logs:CreateLogStream"
- "logs:PutLogEvents"
これはLambdaの実行ログをロググループに書き込むために必要な権限です。SAM内でロググループを作成しない場合はLambdaの初回実行時にロググループが自動的に作成されます。
- "ec2:CreateNetworkInterface"
- "ec2:DescribeNetworkInterfaces"
- "ec2:DetachNetworkInterface"
- "ec2:DeleteNetworkInterface"
これはVPC内でLambdaを使用する際に必要な権限です。VPC内でLambdaを使用する際、LambdaはENI(Elastic Network Interface)を作成しENIを介して通信を行いますが、その時に必要な権限です。
- "lambda:InvokeFunction"
これはLambdaを呼び出すために必要な権限です。付与し忘れると呼び出しに失敗してしまうのでお気をつけを。
以上で設定は終了です。
おわりに
今回はSAMを使ってLambdaのインフラを構築するための設定方法について解説しました。
今後のLambdaインフラ構築をする上で本記事がご参考になれば幸いです。
toridoriの開発部はサービスの機能を最適化し、企業やインフルエンサー、”誰もがより使いやすいサービス”を目指して、日々試行錯誤を繰り返しながらみなさんのもとへお届けしています。
そんなtoridori開発部では新メンバーを募集中です✨
インフルエンサーマーケティングという、この先5年間で市場規模が2倍以上になると言われている成長業界の中で、時代を創っていく企業のメンバーとして、一緒に働きませんか?