こちらの投稿は2025 Japan AWS Jr.Championsの有志メンバーで作成した『30日間で主要AWSサービスを構築できるようになる』をテーマにした初学者向けのハンズオン問題集のDAY9になります!
問題集の趣旨や作成に至るまでの経緯は以下の記事をご覧いただければと思います。
https://qiita.com/satosato_kozakana/items/446971c2deca7e27d0aa
概要
| 項目 | 内容 |
|---|---|
| 所要時間 | 約1時間30分 |
| メインサービス | AWS Lambda, Serverless Application Repository, CloudFormation |
| 学べること | Lambda関数の実装、CloudFormationテンプレート作成、SARへのデプロイ |
| 想定費用 | 約5円(Lambda実行、S3ストレージ、SAR公開無料) |
注意:Serverless Application Repository自体は無料ですが、デプロイされるAWSリソースには料金が発生します。今回はリソース削除まで行いますが、途中で離脱する等で、以下のリソースを削除し忘れると課金が継続してしまいます。
- AWS Lambda - 無料枠: 月100万リクエスト
- Amazon S3 - ストレージとリクエスト料金
課題内容
Lambda関数を作成し、CloudFormationテンプレートでパッケージ化して、Serverless Application Repository(SAR)に公開します。
これにより、自分の作ったサーバーレスアプリケーションを他の人が簡単に利用できるようになります。
アーキテクチャ図
🔧 実装機能
ステップ1: Lambda関数の開発
- AWSコンソールでLambda関数を作成
- Python で画像リサイズ処理を実装
- S3イベントトリガーの設定
- Lambda Layerの活用
ステップ2: CloudFormationテンプレートの作成
- Lambda関数をCloudFormationで定義
- S3バケットとイベント設定を含める
- パラメータ化で再利用可能にする
ステップ3: SARへの公開
- CloudFormationテンプレートをSARに公開
- メタデータとドキュメントの設定
- 公開したアプリケーションのテスト
ステップ4: 公開アプリの利用
- 自分が公開したアプリケーションをSARからデプロイ
- パラメータのカスタマイズ
- 動作確認
💡 実装のヒント
Lambda関数の基本構造
Lambda関数はeventとcontextを受け取り、処理結果を返します。
S3イベントからバケット名とオブジェクトキーを取得し、画像処理を実行します。
CloudFormationテンプレートの構造
CloudFormationテンプレートは以下の要素で構成されます。
- AWSTemplateFormatVersion: テンプレートのバージョン
- Description: テンプレートの説明
- Parameters: 入力パラメータ
- Resources: AWSリソースの定義
- Outputs: 出力値
- Metadata: SARメタデータ
SARへの公開要件
SARに公開するには以下が必要です。
- 適切なメタデータの設定
- README.mdファイル
- ライセンス情報
- セマンティックバージョニング
- 適切なタグ付け
Lambda Layerの活用
Pillowライブラリなどの外部ライブラリは、Lambda Layerとして追加します。
これにより、関数のデプロイパッケージサイズを小さく保てます。
✅ 完成後のチェックポイント
Lambda関数の作成
- Lambda関数が正常に作成される
- 画像リサイズ機能が動作する
- S3イベントトリガーが設定される
- 3種類のサイズの画像が生成される
- CloudWatch Logsで実行ログを確認できる
CloudFormationテンプレートの作成
- テンプレートが正しく記述される
- パラメータが適切に定義される
- SARメタデータが設定される
- テンプレートでスタックが作成できる
SARへの公開
- SARに正常に公開される
- 公開したアプリケーションが検索できる
- README.mdが適切に表示される
- メタデータが正しく設定される
公開アプリの利用
- SARから自分のアプリケーションをデプロイできる
- パラメータをカスタマイズできる
- デプロイしたアプリケーションが正常に動作する
使用資材
Lambda関数のサンプルコード(Python)
import json
import boto3
import os
from PIL import Image
from io import BytesIO
import urllib.parse
s3_client = boto3.client('s3')
def lambda_handler(event, context):
"""
S3にアップロードされた画像をリサイズする関数
"""
try:
# S3イベントから情報を取得
for record in event['Records']:
bucket = record['s3']['bucket']['name']
key = urllib.parse.unquote_plus(record['s3']['object']['key'])
print(f"Processing {key} from {bucket}")
# 画像をリサイズ
resize_image(bucket, key)
return {
'statusCode': 200,
'body': json.dumps({
'message': 'Images processed successfully'
})
}
except Exception as e:
print(f"Error: {str(e)}")
return {
'statusCode': 500,
'body': json.dumps({
'error': str(e)
})
}
def resize_image(source_bucket, source_key):
"""画像をリサイズして保存"""
destination_bucket = os.environ['DESTINATION_BUCKET']
# 元画像をダウンロード
response = s3_client.get_object(Bucket=source_bucket, Key=source_key)
image_content = response['Body'].read()
# 画像を開く
image = Image.open(BytesIO(image_content))
# 各サイズにリサイズ
sizes = {
'thumbnail': (150, 150),
'medium': (800, 600),
'large': (1920, 1080)
}
for size_name, (width, height) in sizes.items():
# アスペクト比を保持してリサイズ
resized_image = image.copy()
resized_image.thumbnail((width, height), Image.Resampling.LANCZOS)
# リサイズした画像を保存
output_buffer = BytesIO()
format = image.format if image.format else 'JPEG'
resized_image.save(output_buffer, format=format)
output_buffer.seek(0)
# S3にアップロード
destination_key = f"{size_name}/{source_key}"
s3_client.put_object(
Bucket=destination_bucket,
Key=destination_key,
Body=output_buffer.getvalue(),
ContentType=f'image/{format.lower()}'
)
print(f"Uploaded {destination_key} to {destination_bucket}")
CloudFormationテンプレート(template.yaml)
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Image Resize Application - Automatically resize images uploaded to S3'
Metadata:
AWS::ServerlessRepo::Application:
Name: image-resize-app
Description: 'Automatically resize images uploaded to S3 into multiple sizes'
Author: 'Your Name'
SpdxLicenseId: MIT
LicenseUrl: LICENSE
ReadmeUrl: README.md
Labels: ['image', 'resize', 'thumbnail', 's3', 'lambda']
HomePageUrl: 'https://github.com/your-username/image-resize-app'
SemanticVersion: 1.0.0
SourceCodeUrl: 'https://github.com/your-username/image-resize-app'
Parameters:
SourceBucketName:
Type: String
Description: 'Name of the S3 bucket for source images'
Default: 'my-source-images-bucket'
DestinationBucketName:
Type: String
Description: 'Name of the S3 bucket for resized images'
Default: 'my-resized-images-bucket'
LambdaFunctionName:
Type: String
Description: 'Name of the existing Lambda function'
Default: 'ImageResizeFunction'
Resources:
# Source S3 Bucket
SourceBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Ref SourceBucketName
NotificationConfiguration:
LambdaConfigurations:
- Event: s3:ObjectCreated:*
Function: !Sub 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${LambdaFunctionName}'
Filter:
S3Key:
Rules:
- Name: suffix
Value: .jpg
- Name: suffix
Value: .jpeg
- Name: suffix
Value: .png
# Destination S3 Bucket
DestinationBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Ref DestinationBucketName
# Lambda Permission for S3
S3InvokePermission:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !Ref LambdaFunctionName
Action: lambda:InvokeFunction
Principal: s3.amazonaws.com
SourceArn: !Sub '${SourceBucket}/*'
Outputs:
SourceBucketName:
Description: 'Name of the source S3 bucket'
Value: !Ref SourceBucket
DestinationBucketName:
Description: 'Name of the destination S3 bucket'
Value: !Ref DestinationBucket
LambdaFunctionName:
Description: 'Name of the Lambda function'
Value: !Ref LambdaFunctionName
README.md(SARアプリケーション用)
# Image Resize Application
## 概要
S3にアップロードされた画像を自動的に複数のサイズにリサイズするサーバーレスアプリケーションです。
## 機能
- JPEG、PNG形式の画像に対応
- サムネイル(150x150)、中サイズ(800x600)、大サイズ(1920x1080)の3種類を生成
- S3イベントトリガーによる自動処理
## 前提条件
- 事前にLambda関数(ImageResizeFunction)が作成されていること
- Lambda関数にPillowライブラリのLayerが追加されていること
- 適切なIAMロールが設定されていること
## デプロイ方法
1. Serverless Application Repositoryから本アプリケーションを検索
2. パラメータを設定してデプロイ
3. ソースバケットに画像をアップロードしてテスト
## パラメータ
- **SourceBucketName**: 元画像を保存するS3バケット名
- **DestinationBucketName**: リサイズ画像を保存するS3バケット名
- **LambdaFunctionName**: 既存のLambda関数名
## 使用方法
1. ソースバケットに画像ファイルをアップロード
2. 数秒後、デスティネーションバケットにリサイズされた画像が生成される
3. CloudWatch Logsで処理ログを確認可能
## ライセンス
MIT License
Lambda Layer用 requirements.txt
Pillow==10.0.1
Lambda Layerを作成する際に使用します。
リファレンスリンク
- AWS Lambda 開発者ガイド
- AWS Serverless Application Repository 開発者ガイド
- AWS CloudFormation ユーザーガイド
- Lambda Layers の使用
- Amazon S3 イベント通知
- Pillow (PIL Fork) ドキュメント
解答・構築手順
解答と構築手順を見る
✅ ステップ1:S3バケットの作成(テスト用)
- AWSマネジメントコンソールを開く
- S3を検索し、移動する
- 「バケットを作成」をクリック
- テスト用ソースバケット名:
test-source-images-[ランダム文字列] - テスト用デスティネーションバケット名:
test-resized-images-[ランダム文字列] - 両方のバケットを作成
✅ ステップ2:Lambda Layer の作成(Pillowライブラリ用)
- Cloud9またはローカル環境で以下を実行:
mkdir python pip install Pillow==10.0.1 -t python/ zip -r pillow-layer.zip python/ - Lambdaコンソールで「レイヤー」→「レイヤーの作成」
- レイヤー名:
pillow-layer - zipファイルをアップロード
- 互換性のあるランタイム:Python 3.9
✅ ステップ3:IAMロールの作成
- IAMコンソールを開く
- 「ロール」→「ロールを作成」
- 信頼されたエンティティ:AWS Lambda
- ポリシーを追加:
AWSLambdaBasicExecutionRoleAmazonS3FullAccess
- ロール名:
ImageResizeLambdaRole
✅ ステップ4:Lambda関数の作成
- Lambdaコンソールを開く
- 「関数の作成」をクリック
- 「一から作成」を選択
- 関数名:
ImageResizeFunction - ランタイム:Python 3.9
- 実行ロール:既存のロールを使用 →
ImageResizeLambdaRole - 使用資材のPythonコードをコピー&ペースト
✅ ステップ5:Lambda関数の設定
- 「レイヤー」セクションで「レイヤーを追加」
- 作成した
pillow-layerを選択 - 「環境変数」で以下を設定:
-
DESTINATION_BUCKET: テスト用デスティネーションバケット名
-
- タイムアウトを30秒に変更
- 「デプロイ」をクリック
✅ ステップ6:Lambda関数のテスト
- テスト用ソースバケットに画像ファイルを手動でアップロード
- Lambda関数を手動実行してテスト
- デスティネーションバケットでリサイズ結果を確認
- 正常に動作することを確認
✅ ステップ7:CloudFormationテンプレートの作成
- ローカル環境で
template.yamlファイルを作成 - 使用資材のCloudFormationテンプレートをコピー
- メタデータセクションを自分の情報に更新
- パラメータとリソースを確認
✅ ステップ8:README.mdとライセンスファイルの作成
-
README.mdファイルを作成 - 使用資材のREADME内容をコピー・カスタマイズ
-
LICENSEファイルを作成(MIT Licenseなど) - 必要に応じて他のドキュメントも作成
✅ ステップ9:S3バケットへのアップロード
- SARパッケージ用のS3バケットを作成
aws s3 mb s3://your-sar-package-bucket - テンプレートとドキュメントをアップロード
aws s3 cp template.yaml s3://your-sar-package-bucket/ aws s3 cp README.md s3://your-sar-package-bucket/ aws s3 cp LICENSE s3://your-sar-package-bucket/
✅ ステップ10:SARへの公開
- Serverless Application Repositoryコンソールを開く
- 「アプリケーションを公開」をクリック
- 以下の情報を入力:
-
アプリケーション名:
image-resize-app - 説明: アプリケーションの説明
- 作成者: 自分の名前
- テンプレートURL: S3のtemplate.yamlのURL
- README URL: S3のREADME.mdのURL
- ライセンス: MIT
-
アプリケーション名:
- 「公開」をクリック
✅ ステップ11:公開アプリケーションの確認
- SARコンソールで「マイアプリケーション」を確認
- 公開したアプリケーションが表示されることを確認
- アプリケーションの詳細ページを確認
- README.mdが正しく表示されることを確認
✅ ステップ12:公開アプリケーションのデプロイテスト
- SARコンソールで自分のアプリケーションを検索
- 「デプロイ」をクリック
- パラメータを設定:
-
SourceBucketName:
sar-test-source-[ランダム文字列] -
DestinationBucketName:
sar-test-resized-[ランダム文字列] -
LambdaFunctionName:
ImageResizeFunction
-
SourceBucketName:
- デプロイ完了を待機
✅ ステップ13:デプロイしたアプリケーションの動作確認
- 新しく作成されたソースバケットに画像をアップロード
- S3イベントトリガーが正常に動作することを確認
- デスティネーションバケットでリサイズ結果を確認
- CloudWatch Logsで実行ログを確認
片付け(リソース削除)
- CloudFormationコンソールでSARデプロイしたスタックを削除
- 手動作成したS3バケット内のオブジェクトを削除
- 手動作成したS3バケットを削除
- Lambda関数を削除
- Lambda Layerを削除
- IAMロールを削除
- SARパッケージ用バケットを削除
- SARから公開したアプリケーションを削除(必要に応じて)
おつかれさまでした!
この課題では、完成後のチェックポイントに加えて、サーバーレスアプリケーションの開発から公開までの全工程が理解できればクリアです!
今回はLambda関数の作成から始まり、CloudFormationテンプレートでのパッケージ化、そしてSARへの公開まで、実際の開発現場で必要な一連の流れを体験できました。これにより、自分の作ったソリューションを他の人が簡単に利用できるようになります。
特に重要なのは、再利用可能なアプリケーションの設計です。パラメータ化、適切なドキュメント作成、メタデータの設定など、他の人が使いやすいアプリケーションを作るためのベストプラクティスを学べたと思います。
SARへの公開は、オープンソースコミュニティへの貢献でもあります。今回学んだスキルを活用して、実際のプロジェクトでも再利用可能なサーバーレスアプリケーションを作成し、チームや組織の開発効率向上に貢献してみましょう!
次回は応用編として、より複雑なアプリケーションの公開や、バージョン管理、CI/CDパイプラインとの統合にも挑戦してみましょう!
