はじめに
「画像認識の仕組みをコードで管理して、いつでも同じ環境を再現できるようにしたい」という要件に、AWS SAM(Serverless Application Model) は最適な選択肢のひとつです。
この記事では、AWS SAM を使って、S3 + Lambda + Rekognition による画像認識パイプラインをゼロから構築するハンズオンを紹介します。
ユーザー
↓ aws s3 cp image.jpg s3://bucket/
S3 バケット(STACK-upload-ACCOUNT_ID)
↓ ObjectCreated イベント(自動)
Lambda(DetectFunction / Python 3.12)
↓ boto3 rekognition.detect_labels(S3Object={...})
Amazon Rekognition
↓ Labels: [{Name, Confidence}, ...]
Lambda
↓ print(json.dumps(result))
CloudWatch Logsこのハンズオンで体験できること:
AWS::S3::Bucket/AWS::Serverless::Functionの SAM 定義S3ReadPolicy/RekognitionDetectOnlyPolicy— SAM 組み込みポリシーによる1行での権限付与Events: Type: S3(ObjectCreated)— Lambda の S3 トリガーをコードで定義する方法- コンソール版との権限設定の違いを体感
このハンズオンの特徴:
sam build+sam deployの 2コマンドで全リソースをデプロイ- S3 バケット + Lambda + IAM ロールを
template.yaml1ファイルで管理 sam deleteで全リソースを一括削除(コンソール版では S3 / Lambda / IAM / ロググループを個別に削除)
SAM vs コンソール:どれだけ違うか
| 比較項目 | SAM(コード) | コンソール(手動) |
|---|---|---|
| S3 トリガー | Events: Type: S3 で1箇所に定義 | Lambda → トリガーを追加(手動クリック) |
| Rekognition 権限 | RekognitionDetectOnlyPolicy: {} の1行 | IAM → AmazonRekognitionReadOnlyAccess を手動アタッチ |
| S3 GetObject 権限 | S3ReadPolicy で特定バケットに自動付与 | IAM → インラインポリシーを手動作成 |
| 削除 | sam delete 1コマンド | S3 / Lambda / IAM / ロググループを個別に削除 |
| 再現性 | チームで同じ環境を即座に再現できる | 手順書が必要 |
キーワード解説
| 用語 | 意味 |
|---|---|
| DetectLabels | Rekognition API。画像内の物体・シーンを検出してラベル名と信頼度を返す |
| S3Object 参照 | 画像データを Lambda に転送せず、S3 の場所を Rekognition に渡す方式 |
| RekognitionDetectOnlyPolicy | SAM 組み込みポリシー。DetectLabels / DetectFaces / DetectText などを付与 |
| S3ReadPolicy | SAM 組み込みポリシー。特定バケットへの s3:GetObject を付与。コンソール版より スコープが狭くセキュア |
| 無料枠 | 最初の12ヶ月 / 月5,000枚まで無料。超過後は $0.001/枚 |
前提条件
| ツール | 確認コマンド | 最低バージョン目安 |
|---|---|---|
| AWS CLI v2 | aws --version | 2.x |
| AWS SAM CLI | sam --version | 1.x |
| Python 3.12 | python --version | 3.12 |
AWS認証確認:
aws sts get-caller-identityアカウントIDが表示されれば認証設定済みです。
使用するAWSサービス
| サービス | 役割 | 料金 |
|---|---|---|
| S3 | 画像のアップロード先バケット | 月5GBまで無料 |
| Lambda | S3 イベントを受けて Rekognition を呼び出す | 月100万リクエスト・400,000 GB-秒まで無料 |
| Rekognition | 画像ラベル検出(DetectLabels) | 最初の12ヶ月 / 月5,000枚まで無料 |
| IAM | Lambda の実行権限管理 | 無料 |
| S3(SAMデプロイ用) | SAM デプロイパッケージ置き場 | 少量のため実質無料 |
| CloudWatch Logs | Lambda の実行ログ | 月5GBまで無料 |
Step 1: プロジェクトフォルダを開く
cd C:\my-aws\aws-learning-projects\rekognition-image-pipelineフォルダ構造
rekognition-image-pipeline/
├── template.yaml # SAMテンプレート(S3 + Lambda + Rekognition 定義)
├── samconfig.toml # デプロイ設定(gitignore 対象・毎回手動作成が必要)
├── docs/
│ ├── 1_console.md # AWSコンソール版手順
│ └── 2_sam.md # SAM版手順
└── src/
└── app.py # Lambda 関数(detect_labels + ログ出力)Step 2: SAMテンプレートの確認(template.yaml)
template.yaml は全 AWSリソースの設計図です。コンソール版との違いに注目しながらポイントを確認します。
Resources:
UploadBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub "${AWS::StackName}-upload-${AWS::AccountId}"
# Lambda 関数
DetectFunction:
Type: AWS::Serverless::Function
Properties:
Policies:
# SAM 組み込みポリシー: 特定バケットへの s3:GetObject を付与
- S3ReadPolicy:
BucketName: !Sub "${AWS::StackName}-upload-${AWS::AccountId}"
# SAM 組み込みポリシー: DetectLabels / DetectFaces / DetectText などを付与
- RekognitionDetectOnlyPolicy: {}
Events:
S3UploadEvent:
Type: S3
Properties:
Bucket: !Ref UploadBucket
Events: s3:ObjectCreated:*template.yaml のポイント:
| ポイント | 説明 |
|---|---|
RekognitionDetectOnlyPolicy: {} | SAM 組み込みポリシー。コンソール版では AmazonRekognitionReadOnlyAccess を手動アタッチ |
S3ReadPolicy | 特定バケットのみに s3:GetObject を付与。コンソール版よりスコープが狭くセキュア |
BucketName: !Sub "${AWS::StackName}-upload-${AWS::AccountId}" | アカウントIDを含めることでグローバルユニークを保証 |
Events: Type: S3(ObjectCreated) | Lambda に S3 トリガーをコードで定義。コンソール版では手動でトリガーを追加 |
Step 3: Lambda コードの確認(src/app.py)
コードはすでに作成済みです。構造を把握しておきます。
import json
import urllib.parse
import boto3
rekognition = boto3.client("rekognition") # Lambda 起動時に1回だけ初期化
def lambda_handler(event, context):
for record in event["Records"]:
bucket = record["s3"]["bucket"]["name"]
key = urllib.parse.unquote_plus(record["s3"]["object"]["key"]) # 日本語対応
response = rekognition.detect_labels(
Image={"S3Object": {"Bucket": bucket, "Name": key}},
MaxLabels=10, # 最大10ラベル
MinConfidence=70, # 70%以上のラベルのみ返す
)
labels = [{"name": l["Name"], "confidence": round(l["Confidence"], 1)}
for l in response["Labels"]]
print(json.dumps({"key": key, "label_count": len(labels), "labels": labels},
ensure_ascii=False))
return {"processedCount": len(event["Records"])}app.py のポイント:
S3Object参照: 画像データを Lambda のメモリに転送せず S3 の場所を Rekognition に渡す。メモリ効率が良いurllib.parse.unquote_plus(key): 日本語や空白を含むファイル名は S3 イベントで URL エンコードされるboto3.client("rekognition")をグローバルスコープに置く: Lambda のコールドスタート時に1回だけ初期化され、ウォームスタート時は再利用される(パフォーマンス向上)
Step 4: samconfig.toml を作成する
samconfig.toml は .gitignore で管理外のため、毎回手動で作成する必要があります。
rekognition-image-pipeline/samconfig.toml を新規作成して以下を貼り付けます。
version = 0.1
[default.deploy.parameters]
stack_name = "rekognition-image-pipeline-stack"
resolve_s3 = true
s3_prefix = "rekognition-image-pipeline-stack"
region = "ap-northeast-1"
confirm_changeset = true
capabilities = "CAPABILITY_IAM"
disable_rollback = true
image_repositories = []
[default.global.parameters]
region = "ap-northeast-1"
samconfig.tomlの置き場所:rekognition-image-pipeline/フォルダの直下に置く必要があります。
Step 5: sam build(ビルド)
cd C:\my-aws\aws-learning-projects\rekognition-image-pipeline
sam build成功すると以下のように表示されます。
Build Succeeded
Built Artifacts : .aws-sam/build
Built Template : .aws-sam/build/template.yaml
sam buildが行っていること:src/フォルダを ZIP パッケージ化して.aws-sam/build/に配置し、Lambda デプロイの準備を整えます。
Step 6: sam deploy(デプロイ)
sam deploy変更内容が表示されて確認を求められます。
Deploy this changeset? [y/N]: yy を入力して進めます。数分でデプロイが完了します。
デプロイ完了の確認
ターミナルに Outputs が表示されます。
Outputs
----------------------------------------------------------------------
Key BucketName
Value rekognition-image-pipeline-stack-upload-123456789012
Key DetectFunctionArn
Value arn:aws:lambda:ap-northeast-1:123456789012:function:rekognition-image-pipeline-stack-DetectFunction-XXXX
----------------------------------------------------------------------BucketName を控えておきます。
デプロイされるリソース一覧
S3 バケット × 1 : rekognition-image-pipeline-stack-upload-ACCOUNT_ID
Lambda 関数 × 1 : rekognition-image-pipeline-stack-DetectFunction-XXXX
IAM ロール × 1 : Lambda 用(S3 GetObject + Rekognition DetectLabels 権限付き)
S3(SAM用) : デプロイパッケージ
CloudWatch Logs : Lambda 自動生成ログStep 7: 動作テスト
事前準備: バケット名を変数に設定する
set BUCKET=rekognition-image-pipeline-stack-upload-123456789012
123456789012を自分のアカウントIDに置き換える(Outputs の値をそのままコピー)。
テスト1: 画像をアップロードしてラベルを確認する
任意の JPEG または PNG 画像(15MB 以下)を用意してアップロードする。
aws s3 cp "C:\Users\yourname\Pictures\dog.jpg" s3://%BUCKET%/ --region ap-northeast-1アップロード直後(数秒以内)に Lambda が自動起動する。
CloudWatch Logs でラベル検出結果を確認:
aws logs tail /aws/lambda/rekognition-image-pipeline-stack-DetectFunction-XXXX --follow期待するログ出力:
{"status": "start", "bucket": "rekognition-image-pipeline-stack-upload-...", "key": "dog.jpg"}
{
"key": "dog.jpg",
"label_count": 8,
"labels": [
{"name": "Dog", "confidence": 98.5},
{"name": "Pet", "confidence": 98.5},
{"name": "Animal", "confidence": 98.5},
{"name": "Canine", "confidence": 98.5},
{"name": "Mammal", "confidence": 97.2},
{"name": "Golden Retriever", "confidence": 85.3},
{"name": "Grass", "confidence": 76.1},
{"name": "Outdoors", "confidence": 71.4}
]
}
{"processedCount": 1}テスト2: 複数枚アップロードして無料枠を確認する
aws s3 cp "C:\Users\yourname\Pictures\cat.jpg" s3://%BUCKET%/ --region ap-northeast-1
aws s3 cp "C:\Users\yourname\Pictures\city.jpg" s3://%BUCKET%/ --region ap-northeast-1各ファイルのアップロードごとに Lambda が起動し、それぞれのラベル検出結果がログに記録される。
無料枠の管理:
AWS コンソール → Billing → 無料利用枠 → 「Amazon Rekognition」で当月の使用枚数を確認できます。
Step 8: AWSコンソールで確認(任意)
SAMでデプロイしたリソースはコンソールでも確認できます。
- S3: S3 →
rekognition-image-pipeline-stack-upload-...→ アップロードしたファイルを確認 - Lambda: Lambda → 関数 →
DetectFunction-XXXX→ 「設定」→「トリガー」で S3 トリガーを確認 - IAM: IAM → ロール → Lambda のロールに
S3ReadPolicy/RekognitionDetectOnlyPolicyが適用されていることを確認 - CloudWatch Logs: Lambda → 「モニタリング」→「CloudWatch Logs を表示」でログ詳細を確認
Step 9: リソースの削除
課金を止めるために、ハンズオン完了後は必ず削除してください。
【注意】 S3 バケットにオブジェクトが残っていると
sam deleteが失敗します。
先にバケットを空にしてから実行してください。
rem Step 1: バケットを空にする
aws s3 rm s3://%BUCKET% --recursive --region ap-northeast-1
rem Step 2: スタック削除
sam delete --stack-name rekognition-image-pipeline-stack --region ap-northeast-1Are you sure you want to delete the stack rekognition-image-pipeline-stack? [y/N]: y
Are you sure you want to delete the folder rekognition-image-pipeline-stack in S3? [y/N]: y削除完了の確認:
aws cloudformation describe-stacks --stack-name rekognition-image-pipeline-stack --region ap-northeast-1Stack with id rekognition-image-pipeline-stack does not exist が表示されれば削除完了。
削除されるリソース一覧:
- S3 バケット(事前に空にした場合)
- Lambda 関数
- IAM ロール
- CloudWatch Logs ロググループ
Rekognition はサービス自体(バケット・コレクション等)を作成しないため、削除作業は不要です。
トラブルシューティング
| 症状 | 原因 | 対処 |
|---|---|---|
sam deploy で BucketAlreadyOwnedByYou | バケット名が既存と重複 | stack_name を変更して samconfig.toml を更新する |
| 画像アップロード後 Lambda が動作しない | S3 トリガーが未設定(デプロイエラー) | sam deploy が正常完了しているか確認。CloudFormation スタックイベントを確認 |
AccessDeniedException (Rekognition) | RekognitionDetectOnlyPolicy が適用されていない | sam deploy を再実行 |
AccessDenied (S3) | S3ReadPolicy が適用されていない | Lambda と S3 が同一リージョンかを確認。sam deploy を再実行 |
InvalidImageFormatException | JPEG/PNG 以外のファイルをアップロードした | JPEG または PNG 形式の画像を使用する |
ImageTooLargeException | 15MB を超える画像をアップロードした | 15MB 以下の画像を使用する |
sam delete で BucketNotEmpty エラー | S3 バケットにオブジェクトが残っている | aws s3 rm s3://%BUCKET% --recursive で先に空にする |
| ラベルが 0 件または少ない | MinConfidence が高い、または被写体が不明瞭 | MinConfidence=70 を下げて再デプロイ。または別の画像を試す |
まとめ
今回のハンズオンで実現したこと:
| 確認項目 | 内容 |
|---|---|
| S3 + Lambda の連携 | 画像アップロードを契機に Lambda が自動起動する仕組みをコードで定義 |
| Rekognition DetectLabels | 画像に写った物体・動物・シーンが自動でラベル付けされる様子を確認 |
| SAM 組み込みポリシー | S3ReadPolicy / RekognitionDetectOnlyPolicy で権限設定が1行で完結 |
| 一括削除 | sam delete でS3 / Lambda / IAM を一括クリーンアップ |
SAMのメリットを実感できたポイント
RekognitionDetectOnlyPolicy: {}とS3ReadPolicyにより IAM 権限設定がワンライナーで完結- S3 バケット名に
!Sub "${AWS::StackName}-upload-${AWS::AccountId}"を使うことでグローバルユニークを自動保証 sam deleteで S3 / Lambda / IAM を一括削除。コンソール版のような個別削除が不要
コンソール版と比較してみる
SAMが裏で何をやっているか、同じ構成をAWSコンソールのみで構築する手順をまとめました。コンソール版では IAM の権限追加を S3 用・Rekognition 用と2回行う必要があるなど、SAMの組み込みポリシーがどれだけ手間を削減しているかが明確に分かります。
コメント