問題点
S3バケットから700MBのCSVファイルを処理している場面を想像してください。Lambda関数には2GBのRAMが割り当てられているため、処理はスムーズに進むはずです。しかし、突然ディスクスペースエラーが発生して実行が停止してしまいます。これは、デフォルトでAWS Lambdaが/tmpディレクトリに512 MBのエフェメラルストレージしか提供していないために起こります。
[Errno 28] No space left on device: '/tmp/large_file.csv'
コードが512MBを超えるファイルをダウンロードしたり、複数の並列プロセスがその領域を使い果たしたりすると、環境のハードリミットに達します。関数は単に遅くなるだけでなく、即座にクラッシュします。
解決策1:エフェメラルストレージの拡張
最も手っ取り早い修正方法は、割り当てられたディスク容量を増やすことです。AWSでは、/tmpを最大10,240 MB (10 GB) まで拡張できます。最初の512MBは無料ですが、追加のストレージには、ほとんどのリージョンで1GB秒あたり約0.00003090ドルのコストがかかることに注意してください。
AWS コンソールを使用する場合
- Lambdaコンソールに移動し、対象の関数を開きます。
- 設定タブを選択します。
- 左側のメニューから一般設定を選択します。
- 編集をクリックし、エフェメラルストレージの設定項目を見つけます。
- 値(例:2048 MB)を更新し、保存をクリックします。
AWS CLI を使用する場合
CI/CDでインフラを自動化している場合は、次のコマンドを使用して設定を即座に更新できます:
aws lambda update-function-configuration \
--function-name MyProcessingFunction \
--ephemeral-storage '{"Size": 2048}'
解決策2:ストリーミングによるディスクの回避
/tmpへの書き込みは不要な場合が多いです。データをメモリに直接ストリーミングすることで、コストを抑え、パフォーマンスを向上させることができます。このアプローチでは、ファイルをチャンクごとに処理するため、ディスクのボトルネックを完全に回避できます。
例:Python (Boto3) でのS3データのストリーミング
download_file()を使用する代わりに、get_object()を使用してストリーミングボディにアクセスします。これにより、ディスク使用量をゼロに抑えられます。
import boto3
import csv
s3 = boto3.client('s3')
def lambda_handler(event, context):
bucket = "my-data-bucket"
key = "large_file.csv"
response = s3.get_object(Bucket=bucket, Key=key)
# ストリームを1行ずつ処理
lines = (line.decode('utf-8') for line in response['Body'].iter_lines())
reader = csv.DictReader(lines)
for row in reader:
# ここにロジックを記述
pass
解決策3:ウォームコンテナのクリーンアップ管理
AWSは、コールドスタートを減らすためにLambdaの実行環境を再利用することがよくあります。コードが200MBのファイルを/tmpに書き込み、削除し忘れた場合、次の実行時にはすでに200MBが消費された状態で始まります。3回実行されると、512MBの制限に達してしまいます。
ファイル操作は常にtry...finallyブロックで囲むようにしてください。これにより、スクリプトの途中でエラーが発生しても、ファイルが確実に削除されるようになります。
import os
file_path = "/tmp/temp_data.tmp"
try:
# 重い処理を実行
pass
finally:
if os.path.exists(file_path):
os.remove(file_path)
解決策4:大規模なワークロードにAmazon EFSをマウントする
10GBでも足りない場合があります。数テラバイトのデータセットを扱う場合や、複数の関数で共有ワークスペースが必要な場合は、Amazon Elastic File System (EFS) が適しています。これには、LambdaをVPC内で実行する必要があります。
- EFSファイルシステムとアクセスポイントを作成します。
- NFSトラフィック(ポート2049)を許可するようにVPCセキュリティグループを更新します。
- Lambdaの設定で、EFSを
/mnt/storageのようなローカルパスにマウントします。
ストレージ制限の確認方法
変更が反映されたかどうかわからない場合は、shutilライブラリを使用して、利用可能な実際のディスク容量をCloudWatchログに出力できます。
`import shutil
def lambda_handler(event, context):
total, used, free = shutil.disk_usage("/tmp")
print(f"合計容量: {total // (2**20)} MiB")
print(f"現在の使用量: {used // (2**20)} MiB")
クイックヒント
- ディスク vs RAM:
/tmpスペースと関数のメモリを混同しないでください。1GBのファイルを変数に読み込む場合は、エフェメラルストレージではなく、メモリ設定を増やす必要があります。 - 分離: Lambdaの各同時実行には、専用の
/tmpが割り当てられます。ある関数が別の関数のディスクを参照したり、容量を圧迫したりすることはありません。 - コスト: データのストリーミングは、追加のエフェメラルストレージ料金を支払うよりも、ほとんどの場合安価に済みます。

