はじめに
クラウドサービスを活用したサーバーレス開発に興味はあるけれど、AWSの利用料金が気になって一歩踏み出せない…
そんなときは、LocalStackを使ってローカル環境でサーバーレスアーキテクチャを構築する方法をお試しください。
これにより、0円でAWSの主要サービスを模擬し、開発・テストを効率的に行うことができます。
LocalStackとは?
LocalStackは、AWSクラウドの主要サービスをローカル環境でエミュレートできるオープンソースのプラットフォームです。
これを使えば、AWSのインフラに依存せずに開発・テストが可能となり、コストをかけずにサーバーレスアーキテクチャを体験できます。
対応サービスは多岐にわたり、今回はLambda, API Gateway, DynamoDB, S3, SQSを中心に紹介します。
システム構成図
以下の図は、今回のサンプルのLocalStack
を利用したサーバーレスアーキテクチャの全体像を示しています。
図1: LocalStackを用いたサーバーレスアーキテクチャの全体図
必要なものを準備しよう
まず、開発を始める前に以下の環境が整っていることを確認しましょう。
- macOS(Homebrewが利用可能)
- Python 3.7以上
- pip(Pythonパッケージ管理ツール)
- Homebrew(macOS用パッケージマネージャ)
- AWS CLI(後述でインストールします)
LocalStackのインストールと起動
Homebrewを使ってLocalStack CLIをインストール
ターミナルを開き、以下のコマンドを実行してLocalStack CLIをインストールします。
brew install localstack/tap/localstack-cli
LocalStackをバックグラウンドで起動
インストールが完了したら、次のコマンドでLocalStackをデタッチモード(バックグラウンド)で起動します。
localstack start -d
起動したサービスを確認
正常に起動しているか確認するため、以下のコマンドを実行します。
localstack status services
表示されたサービス一覧に、lambda
, apigateway
, dynamodb
, s3
, sqs
が含まれていることを確認しましょう。
サービス構成図
LocalStackで利用するサービスの関係を以下の図で確認しましょう。
図2: LocalStack内のサービス間の関係
AWS CLIの設定
LocalStackを操作するために、AWS CLIの設定が必要です。まだインストールしていない場合は、以下のコマンドでインストールします。
brew install awscli
AWS CLIの初期設定
ローカル環境用にAWS CLIを設定します。ダミーのアクセスキーとシークレットキーを使用します。
aws configure
プロンプトに従い、以下のように入力してください。
AWS Access Key ID [None]: test
AWS Secret Access Key [None]: test
Default region name [None]: us-east-1
Default output format [None]: json
これで、AWS CLIがLocalStackと連携する準備が整いました。
サーバーレスアプリケーションを構築
ここからは、LocalStack上にサーバーレスアーキテクチャの各コンポーネントを構築していきます。
Lambda関数の作成
まず、簡単なLambda関数を作成します。プロジェクト用のディレクトリを作成し、lambda_function.py
というファイルを作成して以下の内容を記述します。
# lambda_function.py
import json
import boto3
import os
def handler(event, context):
# DynamoDBにデータを追加
dynamodb = boto3.resource('dynamodb', endpoint_url=os.getenv('DYNAMODB_ENDPOINT'))
table = dynamodb.Table('Users')
user_id = event['queryStringParameters']['id']
user_name = event['queryStringParameters']['name']
table.put_item(
Item={
'id': user_id,
'name': user_name
}
)
# S3にファイルをアップロード
s3 = boto3.client('s3', endpoint_url=os.getenv('S3_ENDPOINT'))
s3.put_object(Bucket='my-bucket', Key=f'{user_id}.txt', Body=f'User: {user_name}')
# SQSにメッセージを送信
sqs = boto3.client('sqs', endpoint_url=os.getenv('SQS_ENDPOINT'))
queue_url = os.getenv('SQS_QUEUE_URL')
sqs.send_message(QueueUrl=queue_url, MessageBody=json.dumps({'id': user_id, 'name': user_name}))
return {
'statusCode': 200,
'body': json.dumps({'message': 'User created successfully'})
}
この関数は、APIリクエストからユーザー情報を受け取り、DynamoDBに保存し、S3にファイルをアップロードし、SQSにメッセージを送信します。
DynamoDBテーブルの作成
次に、DynamoDBテーブルを作成します。
以下のコマンドを実行してください。
aws dynamodb create-table \
--table-name Users \
--attribute-definitions AttributeName=id,AttributeType=S \
--key-schema AttributeName=id,KeyType=HASH \
--provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5 \
--endpoint-url=http://localhost:4566
S3バケットの作成
S3バケットを作成します。
aws s3api create-bucket \
--bucket my-bucket \
--create-bucket-configuration LocationConstraint=ap-northeast-1 \
--endpoint-url=http://localhost:4566
SQSキューの作成
SQSキューを作成し、キューURLを取得します。
QUEUE_URL=$(aws sqs create-queue --queue-name my-queue --endpoint-url=http://localhost:4566 --query 'QueueUrl' --output text)
echo $QUEUE_URL
このURLは後でLambda関数に環境変数として渡します。
Lambda関数のデプロイ
Lambda関数をデプロイするために、まずZIPファイルを作成します。
zip lambda_function.zip lambda_function.py
次に、Lambda関数を作成します。
aws lambda create-function \
--function-name my-function \
--runtime python3.9 \
--handler lambda_function.handler \
--zip-file fileb://lambda_function.zip \
--role arn:aws:iam::000000000000:role/lambda-role \
--environment Variables="{DYNAMODB_ENDPOINT=http://localhost:4566,S3_ENDPOINT=http://localhost:4566,SQS_ENDPOINT=http://localhost:4566,SQS_QUEUE_URL=$QUEUE_URL}" \
--endpoint-url=http://localhost:4566
注: --role
にはダミーのARNを指定していますが、LocalStackでは実際の権限チェックは行われません。
API Gatewayの設定
API Gatewayを設定し、Lambda関数と連携させます。
以下のステップを順に実行してください。
1. APIの作成
API_ID=$(aws apigateway create-rest-api --name 'MyAPI' --endpoint-url=http://localhost:4566 --query 'id' --output text)
echo $API_ID
2. リソースの取得
ROOT_RESOURCE_ID=$(aws apigateway get-resources --rest-api-id $API_ID --endpoint-url=http://localhost:4566 --query 'items[0].id' --output text)
echo $ROOT_RESOURCE_ID
3. 新しいリソースの作成
RESOURCE_ID=$(aws apigateway create-resource --rest-api-id $API_ID --parent-id $ROOT_RESOURCE_ID --path-part 'users' --endpoint-url=http://localhost:4566 --query 'id' --output text)
echo $RESOURCE_ID
4. メソッドの作成(POST)
aws apigateway put-method --rest-api-id $API_ID --resource-id $RESOURCE_ID --http-method POST --authorization-type "NONE" --endpoint-url=http://localhost:4566
5. Lambda関数との統合
aws apigateway put-integration \
--rest-api-id $API_ID \
--resource-id $RESOURCE_ID \
--http-method POST \
--type AWS_PROXY \
--integration-http-method POST \
--uri arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:000000000000:function:my-function/invocations \
--endpoint-url=http://localhost:4566
6. デプロイ
aws apigateway create-deployment --rest-api-id $API_ID --stage-name dev --endpoint-url=http://localhost:4566
7. Lambda関数の権限付与
API GatewayからLambda関数を呼び出せるように権限を付与します。
aws lambda add-permission \
--function-name my-function \
--statement-id apigateway-test-2 \
--action "lambda:InvokeFunction" \
--principal apigateway.amazonaws.com \
--source-arn "arn:aws:execute-api:us-east-1:000000000000:$API_ID/dev/POST/users" \
--endpoint-url=http://localhost:4566
API Gateway構成図
API GatewayとLambdaの連携を以下の図で確認しましょう。
図3: API GatewayとLambdaの連携シーケンス
サンプルコードの実装
次に、サーバーレスアプリケーションを操作するためのPythonコードを実装します。
このコードでは、API Gatewayを介してユーザー情報を追加し、DynamoDB、S3、SQSにデータを保存します。
# app.py
import requests
import json
API_ID = 'YOUR_API_ID' # 実際のAPI IDに置き換えてください
API_URL = f'http://localhost:4566/restapis/{API_ID}/dev/_user_request_/users'
def create_user(user_id, user_name):
params = {
'id': user_id,
'name': user_name
}
response = requests.post(API_URL, params=params)
if response.status_code == 200:
print('User created successfully.')
else:
print('Failed to create user:', response.text)
if __name__ == '__main__':
create_user('1', 'Alice')
create_user('2', 'Bob')
注意: API_ID
部分は、実際に作成したAPI IDに置き換えてください。API IDは前のステップで取得した$API_ID
を使用します。
データフロー図
ユーザー作成リクエストのデータフローを以下の図で示します。
図4: ユーザー作成時のデータフロー
動作確認で安心!
すべての設定が完了したら、動作を確認してみましょう。
1. ユーザーの作成
app.py
を実行して、ユーザーを作成します。
python app.py
正常に動作すれば、以下のように表示されます。
User created successfully.
User created successfully.
2. DynamoDBの確認
DynamoDBにデータが追加されているか確認します。
aws dynamodb scan --table-name Users --endpoint-url=http://localhost:4566
出力例:
{
"Items": [
{
"id": {
"S": "1"
},
"name": {
"S": "Alice"
}
},
{
"id": {
"S": "2"
},
"name": {
"S": "Bob"
}
}
],
"Count": 2,
"ScannedCount": 2
}
3. S3の確認
S3バケットにファイルがアップロードされているか確認します。
aws s3 ls s3://my-bucket --endpoint-url=http://localhost:4566
出力例:
2024-04-27 12:00:00 12 1.txt
2024-04-27 12:00:00 10 2.txt
4. SQSの確認
SQSキューにメッセージが送信されているか確認します。
aws sqs receive-message --queue-url $QUEUE_URL --endpoint-url=http://localhost:4566
出力例:
{
"Messages": [
{
"MessageId": "12345",
"ReceiptHandle": "abcdef",
"MD5OfBody": "e99a18c428cb38d5f260853678922e03",
"Body": "{\"id\": \"1\", \"name\": \"Alice\"}"
},
{
"MessageId": "67890",
"ReceiptHandle": "ghijkl",
"MD5OfBody": "ab56b4d92b40713acc5af89985d4b786",
"Body": "{\"id\": \"2\", \"name\": \"Bob\"}"
}
]
}
動作確認フロー図
動作確認のステップを以下の図で表現します。
図5: 動作確認のフローチャート
まとめ
今回、LocalStackを使用して0円でサーバーレス開発環境を構築する方法を紹介しました。
LocalStackを活用することで、AWSの各種サービスをローカルでエミュレートし、開発・テストを効率的に行うことが可能です。
特に、コストを抑えたい初心者や、クラウドに依存せずに開発を進めたい方には最適なツールです。
いつまでも0円で使用せず、売上を上げてAWSへお金を払えるようにしたいですね!