ECS FargateでnginxとNode.js動かすなら、まずtask cpu 512 / memory 1024でよさそう

  • AWS
  • ECS
  • Fargate
  • Node.js

この記事は、構成と文章の整理にAIを使っています。
あくまで読みやすくするための補助で、内容そのものをAIにすべて任せて書いているわけではありませんが、ところどころ相談しながら整えています。

ECS FargateでnginxとNode.jsを1タスクに載せる構成、CPU/メモリどれくらい割り振ればいいのか毎回悩むので一旦基準を決めた。

先に:Node.jsならnginx自体が不要なことが多い

前提として、ALB配下でNode.js(Express / Fastify など)を動かすだけなら、nginxをsidecarに載せる必然性は薄い。TLS終端・ヘルスチェック・ルーティングはALBが担うので、ALB → Node.js 直結で足りるケースがほとんど。

要否の判断軸は ALB配下のEC2でnginxって要る?フレームワーク別に見てみた にまとめた。ECS Fargateでも同じ考え方でよい。

この記事の配分例は、「すでにnginx+Node.jsの2コンテナ構成になっている/外すコストのほうが高い」場合の出発点として読むのがよい。

結論

SSRなし、主にCRUDみたいな軽めのAPIなら、こんな感じで始めればよさそう。

Task:
cpu: 512
memory: 1024
nginx:
cpu: 96
memoryReservationMiB: 128
memoryLimitMiB: 256
node:
cpu: 416
memoryReservationMiB: 704
memoryLimitMiB: 896
command:
- node
- --max-old-space-size=512

nginxは軽いのでメモリもCPUも小さめでいい。Node.js側に寄せる。

もう少しアクセスが多い環境(数十〜数百RPS)なら task cpu 1024 / memory 2048 くらいに上げる。重い処理があるならさらに cpu 2048 / memory 4096。

memoryReservationMiBとmemoryLimitMiBの違い

  • memoryReservationMiB: ソフトリミット。ECSがスケジューリング判断に使う値で、実際はこれを超えて使っても即座には落ちない
  • memoryLimitMiB: ハードリミット。これを超えるとOOM Killされる

なので普段の使用量をreservationに、瞬間的に許容できる上限をlimitに置く、というのが基本の考え方らしい。

あと、各コンテナのreservation/limitを単純に足し算してもTask全体のメモリ上限にはならない。Task Memoryを超えたらそこでOOMになるので、コンテナごとの割り当てはTask全体の枠内に収まるように決める必要がある。

CDKで書くとハマったポイント

FargateTaskDefinition にタスクレベルのメモリを渡すとき、memoryMiB を指定すると通らない。

// これは動かない
const task = new ecs.FargateTaskDefinition(this, 'Task', {
cpu: 512,
memoryMiB: 1024,
});

memoryMiB はEC2/Fargate共通の基底クラスTaskDefinition用のプロパティで、Fargate専用のFargateTaskDefinitionでは存在しない。正しくはmemoryLimitMiB。

const task = new ecs.FargateTaskDefinition(this, 'Task', {
cpu: 512,
memoryLimitMiB: 1024,
});

コンテナ単位のaddContainerではmemoryReservationMiB/memoryLimitMiBのままでいいので、タスクレベルとコンテナレベルで名前の対応が微妙にずれてるのが混乱ポイントだった。

運用で見る指標

一旦小さめで始めて、以下を見ながらスケールを決める。

  • CPUUtilization が60%を超えてくる
  • MemoryUtilization が70%を超えてくる
  • ALBのTargetResponseTime p95が300msを超えてくる

このあたりが継続的に厳しければ、まずタスク数を増やす(スケールアウト)。それでも常時高いなら cpu/memory 自体を1段階上げる(スケールアップ)方向で考える。