ECS上で稼働するNode.jsアプリケーションが、突然「JavaScript heap out of memory」というエラーで停止したので、原因と対応をまとめました。
エラーの内容
エラー時に出力されたログは以下の通りです。
FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
このエラーは、Node.jsがJavaScriptの実行に使用するヒープ領域の上限を超えて、メモリを割り当てられなくなった際に発生します。
Node.jsのデフォルトヒープサイズは、64-bit環境の場合で約1.4 GBで、これを超える負荷がかかると、アプリケーションがクラッシュすることがあります。
エラーの原因
1. Node.jsのヒープサイズ
Node.jsのデフォルトヒープサイズは約1.4 GBで、これを超えるメモリを使用するアプリケーションでは、メモリ不足によるクラッシュが発生します。今回は大量のデータ処理が原因でこの制限を超えてしまったようです。
2. ECSタスク定義のリソース設定
ECSのmemory
(ハードリミット) と memoryReservation
(ソフトリミット)の設定によって、タスクのメモリ使用量に制限がかかります。
今回のタスクは以下の設定でした。
"memory": 2048,
"memoryReservation": 2048
Node.jsプロセスが使用するヒープ領域(1.4 GB)に加えて、その他のメモリ(バッファ、ネイティブコードなど)を収めるには足りませんでした。その結果、メモリ不足によるクラッシュが発生しました。
解決方法
1. ヒープサイズの拡張
Node.jsでは --max-old-space-size
オプションでヒープサイズを拡張できます。以下は、ヒープサイズを2 GBに拡張する例です。
node --max-old-space-size=2048 dist/main.js
ECS環境では、Dockerfile
を変更するか、タスク定義にcommand
を追加してこのオプションを適用できます。
タスク定義での設定例
タスク定義のcommand
セクションで、起動コマンドにオプションを追加します。
"command": [
"node",
"--max-old-space-size=2048",
"dist/main"
]
2. ECSタスクのメモリ設定を増やす
Node.jsプロセス全体では、ヒープサイズ以外にも以下の用途でメモリを消費します。
- ネイティブモジュールやコードキャッシュ
- バッファ領域(ファイルI/O、ネットワーク通信)
そのため、memory
とmemoryReservation
をヒープサイズよりも十分大きく設定する必要があります。
"memoryReservation": 3072,
"memory": 4096
実装例
Dockerfile
Dockerfile
自体を変更する場合は以下のように記述します。
CMD ["node", "--max-old-space-size=2048", "dist/main"]
タスク定義
タスク定義でヒープサイズを設定し、ECSのメモリ制限も調整します。
Dockerfile
で設定を追加すると全環境で共通になってしまい、環境ごとの調整が難しくなるため、今回はタスク定義を修正しています。
{
"containerDefinitions": [
{
"name": "app",
"memory": 3072,
"memoryReservation": 3072,
"command": [
"node",
"--max-old-space-size=2048",
"dist/main"
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/app",
"awslogs-region": "ap-northeast-1",
"awslogs-stream-prefix": "ecs"
}
}
}
],
"cpu": "1024",
"memory": "3072"
}