最近映画の「スパイダーバース」観ました。スパイダーマン映画で2番目に好きになりました。
1番はアメスパ2です(※個人の感想です)。
今回は初めて触るAWS CloudFormationで、よくある構成のAPI GatewayとLambdaの構築をしてみたいと思います。
構成は以下のようになります。
LambdaのソースはS3に保存しておき、CloudFormationのスタックの作成開始時に参照します。
Amazon Connectを使うことが多いので、とりあえずLambdaはAmazon ConnectのAPIを実行するものを作成します。
目次
AWS CloudFormationとは
まずはCloudFormationについて簡単に。
AWS CloudFormationは、AWS上に構築するリソースや構成をコードによって管理するためのサービスです。
JSONまたはYAML形式でCloudFormationテンプレートを記述し、AWSコンソールなどからアップロードすることでCloudFormationスタック(テンプレートによりプロビジョニングされたリソース)を作成します。
CloudFormationテンプレートでインフラのコード化(IaC)をすることにより、インフラの管理、複製における手間や人的ミスの削減が期待できます。
それでは、導入で概要を説明した構成を実際にCloudFormationで構築していきます。
Lambda関数とテンプレートの作成
Lambda関数の作成
まずは普通にLambda関数を作成します。
内容は、特定のAmazon Connectインスタンスのユーザーリストを取得して、それを返すだけのものです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
const AWS = require('aws-sdk'); const connect = new AWS.Connect({region:'ap-northeast-1'}); exports.handler = async (event) => { let param = { InstanceId : process.env.INSTANCE_ID, MaxResults : 100 }; try{ let result = await connect.listUsers(param).promise(); console.log(result); return { statusCode: 200, headers: { "Access-Control-Allow-Headers" : "Content-Type", "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "OPTIONS,POST,GET" }, body: JSON.stringify(result) }; }catch(e){ return{ statusCode: 500, headers: { "Access-Control-Allow-Headers" : "Content-Type", "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "OPTIONS,POST,GET" }, body: JSON.stringify(e) }; } }; |
このLambda関数はZIP化して任意のS3バケットに保存します。
テンプレートの作成
では、上で作成したLambdaソースをS3から取得し、Lambda関数を作成するCloudFormationのテンプレートを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
AWSTemplateFormatVersion: '2010-09-09' Parameters: FunctionName: Type: String Description: "get_amazonconnect_users" Resources: # Lambda Lambda: Type: 'AWS::Lambda::Function' Properties: Code: S3Bucket: "# S3バケット名" S3Key: !Sub "# S3キー名(ZIPファイル名)" Description: "Lambda function to list amazon connect users" FunctionName: !Sub "${FunctionName}" Handler: index.handler Runtime: nodejs14.x MemorySize: 128 Timeout: 10 Role: '# Lambdaに付与するロールARN' Environment: Variables: INSTANCE_ID: "# Amazon ConnectインスタンスID" |
以下がキーになります。
- 関数名はFunctionNameとしてパラメータに定義して、テンプレート内で使いまわせるようにします。後で書くAPI Gatewayのテンプレートでもこの関数名を使いまわします。
- 関数内で使用する環境変数もテンプレート内で定義します。
- 今回の場合Lambdaに付与するロールは事前に作成しておきます(ロールの作成は今回のスコープ外なので手作業で作成しました)。通常のLambda実行ポリシーにAmazonConnectReadOnlyAccessを追加しています。
一旦この状態でCloudFormationのスタックを作成してリソースが作成されることを確認しました。
API Gatewayのテンプレート作成
続いてAPI Gatewayのテンプレートを作成します。
今回作成するAPI Gatewayの概要は以下となります。
- リソースは1つ。メソッドはGETのみ。
- 1のGETメソッドはLambda ProxyによりLambdaを実行し、そのレスポンスをそのまま返す。
- ステージ名はdev。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
AWSTemplateFormatVersion: '2010-09-09' Parameters: FunctionName: Type: String Description: "get_amazonconnect_users" Resources: # API Gateway Api: Type: "AWS::ApiGateway::RestApi" Properties: Name: "api_cfn_test" Resource: Type: "AWS::ApiGateway::Resource" Properties: RestApiId: !Ref Api ParentId: !GetAtt Api.RootResourceId PathPart: !Sub "${FunctionName}" DependsOn: - Api LambdaPermission: Type: "AWS::Lambda::Permission" Properties: FunctionName: !Sub "${FunctionName}" Action: "lambda:InvokeFunction" Principal: "apigateway.amazonaws.com" DependsOn: Resource ResourceMethod: Type: "AWS::ApiGateway::Method" Properties: RestApiId: !Ref Api ResourceId: !Ref Resource AuthorizationType: "None" HttpMethod: "GET" Integration: Type: "AWS_PROXY" IntegrationHttpMethod: "POST" Uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${FunctionName}/invocations" DependsOn: LambdaPermission Deployment: Type: AWS::ApiGateway::Deployment Properties: RestApiId: !Ref Api DependsOn: ResourceMethod Stage: Type: AWS::ApiGateway::Stage Properties: StageName: dev Description: dev stage RestApiId: !Ref Api DeploymentId: !Ref Deployment DependsOn: Deployment |
以下がキーになります。
- Lambda Proxyを使用する場合、APIのメソッドはGETやDELETEであろうと、IntegrationHttpMethodにはPOSTを指定する
Lambda 統合では、関数呼び出しの Lambda サービスアクションの仕様に従って、統合リクエストに
POST
の HTTP メソッドを使用する必要があります。
- リソースの作成順によってはまだ作成されていないリソースを参照しようとしてエラーになるのでDependsOnで依存関係を記述する
テンプレートをまとめる
最終的にCloudFormationのテンプレートは以下のようになりました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
AWSTemplateFormatVersion: '2010-09-09' Description: "Cloudformation template for API Gateway & Lambda." Parameters: FunctionName: Type: String Description: "get_amazonconnect_users" Resources: # Lambda Lambda: Type: 'AWS::Lambda::Function' Properties: Code: S3Bucket: "# S3バケット名" S3Key: !Sub "# S3キー名(ZIPファイル名)" Description: "Lambda function to list amazon connect users" FunctionName: !Sub "${FunctionName}" Handler: index.handler Runtime: nodejs14.x MemorySize: 128 Timeout: 10 Role: '# Lambdaに付与するロールARN' Environment: Variables: INSTANCE_ID: "# Amazon ConnectインスタンスID" # API Gateway Api: Type: "AWS::ApiGateway::RestApi" Properties: Name: "api_cfn_test" Resource: Type: "AWS::ApiGateway::Resource" Properties: RestApiId: !Ref Api ParentId: !GetAtt Api.RootResourceId PathPart: !Sub "${FunctionName}" DependsOn: - Api - Lambda LambdaPermission: Type: "AWS::Lambda::Permission" Properties: FunctionName: !Sub "${FunctionName}" Action: "lambda:InvokeFunction" Principal: "apigateway.amazonaws.com" DependsOn: Resource ResourceMethod: Type: "AWS::ApiGateway::Method" Properties: RestApiId: !Ref Api ResourceId: !Ref Resource AuthorizationType: "None" HttpMethod: "GET" Integration: Type: "AWS_PROXY" IntegrationHttpMethod: "POST" Uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${FunctionName}/invocations" DependsOn: LambdaPermission Deployment: Type: AWS::ApiGateway::Deployment Properties: RestApiId: !Ref Api DependsOn: ResourceMethod Stage: Type: AWS::ApiGateway::Stage Properties: StageName: dev Description: dev stage RestApiId: !Ref Api DeploymentId: !Ref Deployment DependsOn: Deployment |
また、上記テンプレートでCloudFormationのスタックを作成すると、無事各リソースの作成を行うことができました。
初歩の初歩ではありますが、これでIaC(Infrastructure as Code)への第一歩が踏み出せました。
構成としては単純なAPI GatewayとLambdaですが、テンプレートにするとやっぱりそこそこ行数があります。
本格的にやろうとすると依存関係などが複雑になりそうです。今回作成しているテンプレートは依存関係を書きすぎているかもしれません。
まとめ
今回API Gateway + Lambdaの構成をCloudformationのテンプレートで作成しました。思ったこととしては、
- API Gatewayの場合はOpenAPI(Swagger)ファイルを読み込むことができるようなので、ファイルを分割できる、Swaggerツールが使える、などの点からそちらの方法を取りたい
- LambdaのデプロイはCI/CDまで考慮して、AWS SAM(CloudFormationのServerless Application向け拡張)とCode PipeLineで構築するようにしたほうが良さそう
- AWSのIaCといえば他にもterraformがあるので、CloudFormationとの違いを実感するためにも試してみたい
と、いろいろ試したいこともあるので、忘れないうちにやりたいです。
- Remix SPAモードで静的WebサイトをGithub Pagesにデプロイする - 2024-11-05
- LangGraph入門!LLMアプリ開発ライブラリのQuick StartをAmazon Bedrockでやってみる - 2024-10-10
- AWS ChatbotでSlackからAmazon Bedrock Agentにアクセスしてみる - 2024-09-18
- Amazon Bedrockの新機能「Prompt flows」を試してみた - 2024-07-11
- Amazon ConnectのZero-ETLがGAされたので試してみた - 2024-06-03