はじめに
APIGateway、Lambdaを使った簡単なサーバーレスアプリケーションを作りたいと思います。
作るもの
対象読者
- サーバーレスアプリケーションに触れてみたい人
- ServerlessFrameworkを使ってみたい人
AWS マネージドサービスについて
APIGatewayとは
Amazon API Gateway は、あらゆる規模の REST、HTTP、WebSocket API を作成、公開、管理、モニタリング、保護するための AWS のサービスです。
https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/welcome.html
物理サーバや仮想サーバの場合は、httpリクエストを受付ようとすると、 Nginx などwebサーバを構築する必要があります。
構築後は、アクセス負荷分散・ログローテーション・死活監視など気にする点は多いはずです。
APIGatewayは、それらの気にする点を引き受けてくれるサービスになります。
利用者は、リクエスト数に応じた費用を支払います。
https://aws.amazon.com/jp/api-gateway/pricing/
AWSの他のサービスを利用して、アクセス制限(WAF)、認証(Cognite)などと連携することが容易です。
細かい仕組みになっているのですが、詳しくはクラスメソッドさんの記事がわかりやすいです。
Amazon API Gateway は何をしてるのか
Lambdaとは
AWS Lambda はイベント発生時にお客様のコードを実行し、基盤となるコンピューティングリソースをお客様に代わって管理する、サーバーレスコンピューティングサービスです。
https://aws.amazon.com/jp/lambda/features/
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/welcome.html
物理サーバや仮想サーバの場合は、プログラムを動作するアプリケーションサーバを構築する必要があります。
構築後は、アクセス負荷分散・ログローテーション・ディスク容量監視・死活監視など気にする点は多いはずです。
Lambdaは、それらの気にする点を引き受けてくれるサービスになります。
利用者は、リクエスト数と実行時間に応じた費用を支払います。
https://aws.amazon.com/jp/lambda/pricing/
開発者が管理するのは、プログラムコードのみになります。
プログラムコードをLambdaにデプロイすることで動作します。
ServerlessFrameworkについて
サーバーレスフレームワークオープンソースを使用すると、サーバーレスアプリケーションを開発して、AWS、Azure、GCPなどにデプロイできます。
https://www.serverless.com/framework/docs/
AWS SAMがありますが、AWS以外のクラウド環境にも対応しています。
Yamlファイルの記述に慣れが必要ですが、極力なツールだと思います。
実行環境を構築
Docker環境を準備
Docker環境で用意しました。
Githubからクローン後は、以下2つの手順で環境は完成します。
Github
https://github.com/acronhub/sls-python3
- AWSのIAMアカウントのアクセスキーを環境ファイルに設定します。
- Github上のコードでは .env.example を用意しているので名前を .env に変えて利用します。
AWS_ACCESS_KEY_ID=XXXXXXXXXXXXXXXXXXXX
AWS_SECRET_ACCESS_KEY=YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
AWS_DEFAULT_REGION=ap-northeast-1
SERVERLESS_VERSION=2.15.0
- ビルド
docker-compose build
- コンテナ起動
docker-compose up -d
Pythonで環境を準備
- 起動中のコンテナに入る
docker-compose exec app bash
- サービスの作成
- この工程でプロジェクトが作成されます。
serverless create --template aws-python3
- とりあえずローカルで実行
- HelloWorldっぽいのが作られているので実行します。
serverless invoke local --function hello
- 以下のように返ってきます。
{
"statusCode": 200,
"body": "{\"message\": \"Go Serverless v1.0! Your function executed successfully!\", \"input\": {}}"
}
AWSにデプロイ
- HTTPリクエストで応答するように設定します。
- APIGatewayを利用します。
- ServerlessFrameworkはS3を使ってデプロイします。
- 今回はバケットを指定していないのでサービス名のバケットが自動で作られます。
# Welcome to Serverless!
service: sls-python3 < 変更(デプロイしたときにLambdaの関数名になります)
provider:
name: aws
runtime: python3.8
region: ap-northeast-1 < 追加
functions:
hello:
handler: handler.hello
events: < 追加
- http: < 追加(HTTPを指定することでAPIGatewayが作られます)
path: / < 追加
method: GET < 追加
- デプロイ
serverless deploy
- 以下のようにメッセージが表示されてくると思います。
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
........
Serverless: Stack create finished...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service sls-python3.zip file to S3 (965 B)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
...........................
Serverless: Stack update finished...
Service Information
service: sls-python3
stage: dev
region: ap-northeast-1
stack: sls-python3-dev
resources: 10
api keys:
None
endpoints:
GET - https://b02w4yeoh5.execute-api.ap-northeast-1.amazonaws.com/dev/
functions:
hello: sls-python3-dev-hello
layers:
None
動作確認
- エンドポイントがログに出力されているので、そこにアクセスします。
https://b02w4yeoh5.execute-api.ap-northeast-1.amazonaws.com/dev/
- JSON形式なので、Postmanを利用して取得しました。
{
"message": "Go Serverless v1.0! Your function executed successfully!",
"input": {
"resource": "/",
"path": "/",
"httpMethod": "GET",
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate, br",
"CloudFront-Forwarded-Proto": "https",
"CloudFront-Is-Desktop-Viewer": "true",
"CloudFront-Is-Mobile-Viewer": "false",
"CloudFront-Is-SmartTV-Viewer": "false",
"CloudFront-Is-Tablet-Viewer": "false",
"CloudFront-Viewer-Country": "JP",
"Host": "b02w4yeoh5.execute-api.ap-northeast-1.amazonaws.com",
"Postman-Token": "4b053914-cc6c-4837-aaaa-e75ac445df85",
"User-Agent": "PostmanRuntime/7.26.8",
"Via": "1.1 e6b20196b0d9593ce8bf37920e475b8d.cloudfront.net (CloudFront)",
"X-Amz-Cf-Id": "feG9_lzfO_cd2a1OOgKA0wnU7f7h9Sq00mA0cyYzwhxpiD3HHi9DFw==",
"X-Amzn-Trace-Id": "Root=1-5fd2bc3e-3cf565a8213417de68ea2c7c",
"X-Forwarded-For": "xxx.xxx.xxx.xxx",
"X-Forwarded-Port": "443",
"X-Forwarded-Proto": "https"
},
"multiValueHeaders": {
"Accept": [
"*/*"
],
"Accept-Encoding": [
"gzip, deflate, br"
],
"CloudFront-Forwarded-Proto": [
"https"
],
"CloudFront-Is-Desktop-Viewer": [
"true"
],
"CloudFront-Is-Mobile-Viewer": [
"false"
],
"CloudFront-Is-SmartTV-Viewer": [
"false"
],
"CloudFront-Is-Tablet-Viewer": [
"false"
],
"CloudFront-Viewer-Country": [
"JP"
],
"Host": [
"b02w4yeoh5.execute-api.ap-northeast-1.amazonaws.com"
],
"Postman-Token": [
"4b053914-cc6c-4837-aaaa-e75ac445df85"
],
"User-Agent": [
"PostmanRuntime/7.26.8"
],
"Via": [
"1.1 e6b20196b0d9593ce8bf37920e475b8d.cloudfront.net (CloudFront)"
],
"X-Amz-Cf-Id": [
"feG9_lzfO_cd2a1OOgKA0wnU7f7h9Sq00mA0cyYzwhxpiD3HHi9DFw=="
],
"X-Amzn-Trace-Id": [
"Root=1-5fd2bc3e-3cf565a8213417de68ea2c7c"
],
"X-Forwarded-For": [
"xxx.xxx.xxx.xxx"
],
"X-Forwarded-Port": [
"443"
],
"X-Forwarded-Proto": [
"https"
]
},
"queryStringParameters": null,
"multiValueQueryStringParameters": null,
"pathParameters": null,
"stageVariables": null,
"requestContext": {
"resourceId": "bzg9qviz6f",
"resourcePath": "/",
"httpMethod": "GET",
"extendedRequestId": "XXJZxEL8NjMFStQ=",
"requestTime": "11/Dec/2020:00:24:30 +0000",
"path": "/dev/",
"accountId": "XXXXXXXXXXXX",
"protocol": "HTTP/1.1",
"stage": "dev",
"domainPrefix": "b02w4yeoh5",
"requestTimeEpoch": 1607646270497,
"requestId": "e3230533-5aa1-479b-962e-33e05cbdacab",
"identity": {
"cognitoIdentityPoolId": null,
"accountId": null,
"cognitoIdentityId": null,
"caller": null,
"sourceIp": "xxx.xxx.xxx.xxx",
"principalOrgId": null,
"accessKey": null,
"cognitoAuthenticationType": null,
"cognitoAuthenticationProvider": null,
"userArn": null,
"userAgent": "PostmanRuntime/7.26.8",
"user": null
},
"domainName": "b02w4yeoh5.execute-api.ap-northeast-1.amazonaws.com",
"apiId": "b02w4yeoh5"
},
"body": null,
"isBase64Encoded": false
}
}
おわりに
今回はHelloWorldで終わりました。
実業務で利用している感覚ですが、動的なポータルサイトなどにも利用しても十分に対応できており、コストも抑えることができています。
ただ実行時間の制限など制約があるため、重ため処理にはあまり向いていません。
今後もまずサーバーレスで実現できないか考えつつガンガン利用をしていきたいと思っています。