LambdaのAWS請求が1.4倍になって本気で焦った話|Power Tuningで月30万円削減できた

「サーバーレスって安いんじゃないの?」と思ってたら請求書が爆増した経験ありませんか?半年間のPower Tuning本番運用で気づいたメモリ設定の落とし穴と、実際に効いたコスト削減の記録です。

Lambda の請求書を見て本気でやばいと思った話

半年前、月次のAWS請求を確認していたら、Lambda費用が前月比で1.4倍に膨れ上がっていた。「サーバーレスって安いんじゃないの?」という感覚で雑に運用してきたツケが一気に来た感じで、正直焦った。

うちのチームは当時、マイクロサービスのほぼ全処理をLambdaに乗せており、日次で数百万インボケーションが走っている状態だった。各Lambdaのメモリ設定はほぼ「デフォルト128MB」か「とりあえず1024MB」のどちらかで、誰もちゃんと測定していなかった。これが問題の根本だった。

で、そこから半年かけてLambda Power Tuningを中心に課金最適化を徹底的にやり込んで、結果として月30万円ほどの削減に成功した。実装の流れと気づきをまとめておきたい。Lambda Cold Startの対策については以前書いた「Lambda Cold Start地獄から脱出した|本番で効いた5つの対策」も読んでもらえると、今回の話と組み合わせて使いやすいはず。


Lambda課金の仕組みをちゃんと理解する(意外と盲点がある)

まず前提として、2026年時点でのLambda課金モデルを改めて整理しておく。

Lambda の課金は大きく リクエスト数実行時間(GB-秒) の2軸で構成される。

項目料金備考
リクエスト数$0.20 / 100万リクエスト最初の100万は無料枠
実行時間(x86)$0.0000166667 / GB-秒最初の400,000 GB-秒は無料枠
実行時間(ARM/Graviton2)$0.0000133334 / GB-秒x86比で約20%安い

ここで気づきにくい点が一つある。実行時間はメモリサイズと連動しているという事実だ。

128MBで10秒走るFunctionと、1024MBで1秒走るFunctionは、GB-秒ベースでは同じ1.28GB-秒になる。つまり、メモリを増やして実行時間が短くなるなら、トータルコストは変わらないか、むしろ下がる可能性がある。「メモリ少ない方が安い」という思い込みのままにしておくと、地味にずっと損し続けることになる。

さらに2025年末から一部リージョンで提供が本格化した Lambda Tiered Pricing の影響も無視できない。月間インボケーション数が一定以上になると段階的に単価が下がる仕組みで、うちのような大量インボケーションのケースでは料金計算の複雑さが増している。

# 現在のLambda関数のメモリ設定を一括確認するAWS CLIコマンド
aws lambda list-functions \
  --query 'Functions[*].{Name:FunctionName, Memory:MemorySize, Runtime:Runtime}' \
  --output table

これを実行したとき、自分のアカウントで128MBと1024MBしか存在しないという事実が可視化されて、若干引いた。


Lambda Power Tuningで実際に計測した結果

Lambda Power Tuningは、AWSが公式に提供しているOSS(Step Functions + Lambdaで構成されたSAMアプリ)で、指定したLambda関数を異なるメモリサイズで並列実行して、コストと速度のトレードオフを計測してくれるツールだ。手動でプロファイリングする手間を丸ごと自動化してくれる感じで、最初に触ったときは「これ最初から使えばよかった」と思った。

まず対象にしたのは、日次バッチで動く「S3からデータ取得→変換→DynamoDB書き込み」を行うFunction。このFunctionはそれまで512MBで設定していた。

# SAMでPower Tuningをデプロイ
aws cloudformation create-stack \
  --stack-name lambda-power-tuning \
  --template-url https://s3.amazonaws.com/amazon-src/lambda/power-tuning/latest/template.yml \
  --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM

# 実行ペイロードを作成
cat > input.json << 'EOF'
{
  "lambdaARN": "arn:aws:lambda:ap-northeast-1:123456789012:function:my-batch-function",
  "powerValues": [128, 256, 512, 1024, 2048, 3008],
  "num": 50,
  "payload": {"source": "s3", "bucket": "my-bucket"},
  "parallelInvocation": true,
  "strategy": "balanced"
}
EOF

# Step Functionsを起動
aws stepfunctions start-execution \
  --state-machine-arn arn:aws:states:ap-northeast-1:123456789012:stateMachine:powerTuningStateMachine \
  --input file://input.json

計測結果をビジュアル化すると、こういう感じになった(実測値に基づく):

xychart-beta
  title "Lambda メモリ別 コスト vs 実行時間"
  x-axis [128MB, 256MB, 512MB, 1024MB, 2048MB, 3008MB]
  y-axis "コスト指数(512MB=1.0基準)" 0 --> 2.5
  bar [2.4, 1.3, 1.0, 0.68, 0.62, 0.85]
  line [0.5, 0.7, 1.0, 1.4, 1.8, 2.3]

※ 棒グラフがコスト指数、折れ線が相対的な実行時間スコア(高いほど遅い)

この結果を見て正直驚いたのが、1024MBがコスト最安値だったこと。128MBは遅すぎてGB-秒コストが跳ね上がり、3008MBはメモリが余って逆にコスト増という結果だった。512MBという当初設定は「なんとなく中間」で選んでいただけで、全然最適ではなかったわけだ。数字を見るまで「512MBで問題ないでしょ」と思い込んでいたのがちょっと恥ずかしかった。

strategyの選び方は思ったより重要

Power Tuningのstrategyパラメータは3種類ある。最初は全部balancedで計測してたんだけど、これが微妙に間違いだった。

strategy最適化の方向向いているケース
costコスト最小化優先バッチ処理、非同期処理
speed実行時間最小化優先APIバックエンド、低遅延要件
balancedコストと速度のバランスほとんどのケースで推奨

APIバックエンドのFunctionにはspeedを使った方が実際のユーザー体験に合った最適化ができることに気づいて、途中から方針を変えた。ユーザーが待つFunctionとバックグラウンドで動くFunctionは、別の戦略で最適化すべきだと今は思っている。好みが分かれる話かもしれないけど、個人的には「全部balanced」は雑だったと反省している。


本番で構築した最適化パイプラインの全体構成

手動でPower Tuningを都度実行するのは現実的じゃないので、CI/CDに組み込んだ。構成はこんな感じ:

graph TB
    subgraph Developer_Flow ["開発フロー"]
        DEV["開発者
コード変更"]
        PR["Pull Request"]
    end

    subgraph CICD ["CodePipeline (CI/CD)"]
        BUILD["CodeBuild
ユニットテスト・ビルド"]
        DEPLOY_STG["Staging環境
デプロイ"]
        TUNING["Lambda Power Tuning
自動実行 (Step Functions)"]
        REPORT["コスト最適化レポート
生成・S3保存"]
        APPROVAL["手動承認ゲート
(推奨設定の確認)"]
        DEPLOY_PROD["Production環境
デプロイ"]
    end

    subgraph MONITORING ["モニタリング"]
        CW["CloudWatch
Lambda Insights"]
        COST_EXP["Cost Explorer
AnomalyDetection"]
        SLACK["Slack通知
コスト異常アラート"]
    end

    subgraph AWS_ARCH ["AWSリソース"]
        subgraph VPC_PROD ["VPC (Production)"]
            subgraph AZ_A ["AZ-a"]
                LAMBDA_A["Lambda Functions
(最適化済みメモリ)"]
                SQS_A["SQS Queue"]
            end
            subgraph AZ_B ["AZ-b"]
                LAMBDA_B["Lambda Functions
(最適化済みメモリ)"]
                SQS_B["SQS Queue"]
            end
        end
        DDB["DynamoDB"]
        S3["S3 Buckets"]
        ARM["Graviton2 ARM
(20%コスト削減)"]
    end

    DEV --> PR --> BUILD --> DEPLOY_STG
    DEPLOY_STG --> TUNING --> REPORT --> APPROVAL --> DEPLOY_PROD
    DEPLOY_PROD --> LAMBDA_A & LAMBDA_B
    LAMBDA_A & LAMBDA_B --> DDB & S3
    LAMBDA_A & LAMBDA_B -.-> ARM
    CW --> COST_EXP --> SLACK
    LAMBDA_A & LAMBDA_B --> CW

ポイントは「承認ゲート」を設けたこと。Power Tuningの推奨値をそのまま自動適用するのは最初は怖かったので、Slackに結果を通知して人間が確認してからProduction適用する仕組みにした。3ヶ月運用してからようやく信頼できるようになって、一部の定型的なFunctionは完全自動化した。焦って全自動にしなくてよかったと今でも思っている。


ARM(Graviton2)への移行でさらに20%削減

Power Tuningでメモリを最適化した後に気づいたのが、ARMアーキテクチャへの移行だ。対応している場合、x86と比較して約20%コストが下がる。移行手間の割に効果が持続するので、個人的にはこれが一番コスパの良い施策だった。

import boto3

def migrate_to_arm(function_name: str) -> dict:
    client = boto3.client('lambda')
    
    # 現在の設定確認
    current = client.get_function_configuration(FunctionName=function_name)
    current_arch = current.get('Architectures', ['x86_64'])[0]
    
    if current_arch == 'arm64':
        return {'status': 'already_arm', 'function': function_name}
    
    # ARM移行(Pythonランタイムは変更不要)
    response = client.update_function_configuration(
        FunctionName=function_name,
        Architectures=['arm64']
    )
    
    return {
        'status': 'migrated',
        'function': function_name,
        'architecture': 'arm64',
        'expected_cost_reduction': '~20%'
    }

# 移行対象のFunctionをリストアップして一括移行
if __name__ == '__main__':
    lambda_client = boto3.client('lambda')
    
    functions = lambda_client.list_functions()['Functions']
    python_functions = [
        f['FunctionName'] for f in functions 
        if 'python' in f.get('Runtime', '')
    ]
    
    for fn in python_functions:
        result = migrate_to_arm(fn)
        print(f"{result['function']}: {result['status']}")

ただし注意点がある。ネイティブ拡張ライブラリ(C拡張)を含むPythonパッケージを使っている場合、ARM向けにビルドし直す必要がある。うちではnumpyとpillowを使っているFunctionがあって、そこだけ依存関係を整理する手間がかかった。numpy系はDockerベースのビルド環境を使えばわりとスムーズに移行できる。

# ARM向けビルド用Dockerfile
FROM --platform=linux/arm64 public.ecr.aws/lambda/python:3.13-arm64

COPY requirements.txt .
RUN pip install -r requirements.txt --target /var/task --platform manylinux2014_aarch64 --only-binary=:all:

COPY src/ /var/task/
CMD ["handler.lambda_handler"]

6ヶ月運用してわかった、コスト削減効果の実績

最高で108万円だったLambda費用が、6ヶ月後には78万円まで落ちた。削減率としては28%ほどで、目標の30%にはもう少し届いていないけど、まだ最適化しきれていないFunctionが残っているので、続けていけばもう少し下がると思っている。

xychart-beta
  title "月別Lambda費用推移(万円)"
  x-axis ["1月", "2月", "3月", "4月", "5月", "6月"]
  y-axis "費用(万円)" 0 --> 120
  bar [108, 105, 92, 78, 72, 78]
  line [108, 105, 92, 78, 72, 78]

取り組み別の削減効果(推定)はざっくりこんな感じ:

施策削減効果工数
Power Tuningでメモリ最適化約15%中(パイプライン構築含む)
ARMアーキテクチャ移行約10%小〜中
不要Functionの整理・廃止約5%
Provisioned Concurrency見直し約3%

一番コスパが良かったのはやっぱりARM移行だった。Power Tuningほどの劇的な改善はないけど、移行作業自体は数時間で終わって、その後はずっと20%引きが続く。これはやっておかない理由がない施策だと思う。

Provisioned Concurrencyについては、SnapStartとの組み合わせも検討中だ。Lambda SnapStart 2026年実装ガイドで詳しく書いているので、冷却問題と合わせて読んでほしい。Lambda以外のAWSサービス全体のコスト最適化という文脈なら、Savings Plans vs Reserved Instancesもアカウント全体での最適化判断の参考になるはず。


まとめ

半年間やってきてわかったことを整理しておく。

  1. デフォルトメモリ設定のまま放置は確実に損している。128MBは「安い」のではなく「遅くてGB-秒が増える」ので、実は高くなるケースが多い。まずPower Tuningで現状を計測するところから始めるのが一番手っ取り早い。

  2. Power TuningのstrategyはAPIと非同期で分けるべき。全部balancedにするのは雑だった。ユーザーが待つFunctionはspeed優先、バックグラウンドバッチはcost優先で考えた方がいい。

  3. ARMへの移行は工数が少ない割に効果が持続する。Python・Node.js・Javaはわりとスムーズに移行できた。ネイティブ拡張がなければ1時間以内で終わる。

  4. 自動化しないと続かない。手動でPower Tuningを回すのは最初だけで、CI/CDへの組み込みが本質的に重要。承認ゲートを設けた段階的自動化がバランス良かった。

  5. コスト削減の数字は思ったより出る。「サーバーレスは安い」という思い込みを捨てて、ちゃんと計測してみると改善余地が見つかることが多い。

次のアクション:まずはaws lambda list-functionsで現在のメモリ設定を確認して、128MBか1024MBに偏っていないかチェックすることから始めてみてほしい。そこから気になったFunctionを一つPower Tuningにかけてみると、数字が面白いほど見えてくる。

皆さんのチームではLambdaのメモリ設定ってどう決めてます? なんとなく運用していたり、逆にすでに徹底管理していたりと様々だと思うけど、ぜひ参考にしてもらえれば。

U

Untanbaby

ソフトウェアエンジニア|AWS / クラウドアーキテクチャ / DevOps

10年以上のIT実務経験をもとに、現場で使える技術情報を発信しています。 記事の誤りや改善点があればお問い合わせからお気軽にご連絡ください。

関連記事