TL;DR
Lambda のコンテナイメージで headless Chromium を起動しようとして 7 通り試した。全部ダメだった。--single-process でレンダラーが墜落、in-process-gpu でメモリ 2.4GB 食って OOM、swiftshader でも同じ、GPU プロセスが error_code=1002 で即死。最後に ECS Fargate に「重い処理は Fargate、HTTP は Lambda」と分離したら 1 時間で動いた。
試して死んだ組み合わせ
- 何もしない素の起動 → GPU プロセスが
Service responded with error: bus disconnected -
--no-sandbox→ 同じ -
--single-process→ レンダラーがメモリ越えて死ぬ -
--in-process-gpu→ 2.4GB 食って Lambda の上限を超える -
--use-gl=swiftshader→ swiftshader の初期化で hang -
--disable-gpu+ 上記の組み合わせ →error_code=1002 -
chrome.lambdaレイヤー(chromium-aws-lambda 系)→ Playwright が見つけてくれない
7 つ目で諦めた。
なぜ Lambda で詰むのか
Lambda の実行環境は /tmp 以外が読み取り専用で、GPU デバイスが無く、/dev/shm も限定的。Chromium はこの 3 つ全部に依存する。headless: true でも内部的に GPU プロセスをフォークし、共有メモリで描画結果を渡そうとする。それが全部塞がれている。
回避策は --single-process か --in-process-gpu に倒すしかなく、どちらも「メモリ 4GB 以上 + heavy ページを開かない」が前提。Lambda の上限 3008MB(このアカウントの場合)では足りない。
Fargate に逃がしたら
Dockerfile から awslambdaric を外し、entrypoint を python -m functions.run_task に変えた。タスク定義は 1024 vCPU / 2048 MB、Fargate Spot。EventBridge から RunTask で起動。
起動した。最初の goto() で 1.5 秒、page.screenshot() も普通に通った。
構成の落とし所
| 責務 | 実行先 | 理由 |
|---|---|---|
| 承認 UI の API | Lambda | 冷起動の許せる軽い処理。月数百回 |
| 重い Chromium 自動化 | ECS Fargate Spot | Lambda では物理的に動かない |
| 認証 | Cognito JWT | どちらの実行先からも検証可能 |
Lambda を諦めても、Lambda を全部捨てる必要はない。実行特性で分けるだけ。
教訓
Lambda は「サーバーレス全部入り」ではなく「短時間・少メモリ・GPU不要な計算に最適化された箱」。重いブラウザ自動化は、最初から Fargate でいい。7 回試す前にこの 1 行に気付けば、5 時間返ってきた。