1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

EKSでNext.jsを安定運用するためのSRE実践ノウハウ

Posted at

はじめに

こんにちは。元はフロントエンドエンジニアとしてNext.jsアプリを開発していて、現在はSREとしてそのアプリをEKS上で運用しているゾイです!
Next.jsは「作って動かす」までは簡単ですが、本番で安定して動かし続けるにはSRE的な設計・監視・最適化が欠かせません。
この記事では、私が実運用で直面し改善してきたポイントをEKS+Next.jsに絞って解説します。


1. ヘルスチェック設計(readiness/liveness/startup)

readinesslivenessを分けるのは必須です。
外部依存(DB/外部API)がある場合はreadinessで疎通確認を行い、失敗時はトラフィックを切る。一方、**livenessは「プロセス生存確認」**に留め、頻繁に落とさない設定にします。起動が重いSSRはstartupProbeを使うと安定します。

ハンドラー例(依存先あり)

// /api/health/readiness
app.get('/health/readiness/', (_, reply) => {
  const ok = await checkExternalDeps(); // DBや外部APIの疎通確認
  reply.code(200).send({ status: 'READY' })
})

ハンドラー例(依存先なし)

一方で、依存先がないシンプルなアプリなら共通化もOK。

app.get(/^\/health\/(liveness|readiness)\/?$/, (_, reply) => {
  reply.code(200).send({ status: 'READY' })
})

Probe設定例(EKS)

readinessProbe:
  httpGet: { path: /health/readiness/, port: 3000 }
  initialDelaySeconds: 3
  periodSeconds: 10
livenessProbe:
  httpGet: { path: /health/liveness/, port: 3000 }
  initialDelaySeconds: 10
  periodSeconds: 30
startupProbe: # 起動が重いSSRは導入を検討
  httpGet: { path: /health/liveness/, port: 3000 }
  failureThreshold: 30
  periodSeconds: 5

優先度の目安

  • startupProbe:起動完了までlivenessの実行を遅らせる
  • readiness:依存先の状態に追随(失敗中はLBから外す)
  • liveness:プロセスがハングしたら再起動(頻度高すぎると逆効果)

2. SSRとスケーリングの誤解

CPUコアを増やせば比例して速くなるとは限りません。
Node.jsは基本シングルスレッドで動くため、2コア割り当てても1コアしか使わない状況が起こりえます。clusterやプロセスマネージャー(例: PM2)でプロセスをコア数に合わせて分岐させるのが定石です。

詳しくは、こちらの記事をどうぞ!
https://qiita.com/quicksilversel/items/336ef9270427f89dd2d3

3. SSR/CSRを踏まえた障害試験シナリオ

Next.jsはSSRとCSRで障害時の見え方が違うため、バックエンドの「200/500だけ見る」試験では不十分です。

  • SSR経路:依存が落ちるとHTTPエラーが返る傾向
  • CSR経路:画面は200でも部分的に欠落(フェイルソフト)しがち

私が組むテストマトリクス(例)

観点 シナリオ 期待挙動(SSR) 期待挙動(CSR)
依存API障害 API 5xx ページ5xx or エラーページ ページは200、該当ウィジェット非表示/プレースホルダー
レイテンシ API 3s→10s SSRタイムアウト/リトライ UIローディング表示→タイムアウト扱い
認証失効 セッション切れ リダイレクト/エラーページ クライアントで再ログイン誘導

実運用では画面要素単位の可用性(部分劣化)を受け入れる設計が多いです。PlaywrightなどのE2Eに視覚リグレッション特定コンポーネントの可視性チェックを入れると再現性が高まります。

4. キャッシュ設計(CDN×SSR)

CDNを前段に置くだけで、SSRサーバーの負荷は大きく下げられます。重要なのはなにを・どれくらいキャッシュするか。

  • 静的アセット/_next/static/*、画像、フォント)

    • 長寿命&ハッシュ付きファイル名 → Cache-Control: public, max-age=31536000, immutable
    • 変更時はbuildIdが変更されてファイル名も変わるため追加の破棄は不要(いわゆるcache busting)。
  • SSR HTML

    • パーソナライズや認証が絡む場合は基本ノーキャッシュCache-Control: no-store)。
    • 共有キャッシュ(CDN)のみ短命で許可したい場合はs-maxageを短く設定(例: 数十秒)。

ざっくりした図

Client → CDN(Akamai/CloudFront等) → ALB → Next.js SSR
                      ↑
            ここにキャッシュを入れる

5. ログ設計(構造化・相関ID・レイテンシ)

Next.jsはデフォルトではアクセスログが弱いので、カスタムサーバーで構造化ログを出すのが実践的です。FastifyのonResponseレイテンシ相関IDを記録するだけでも、トラブル時の切り分けが劇的に楽になります。

Fastify例(機微情報なし/最小構成)

import Fastify from "fastify";

const app = Fastify({ logger: true });

app.addHook("onResponse", (req, reply, done) => {
  req.log.info({
    http_method: req.method,
    http_status: reply.statusCode,
    user_agent: req.headers["user-agent"] ?? null,
    latency: reply.getResponseTime() / 1000
  });

  done();
});

計測したい基本メトリクス

  • レイテンシ(p50/p90/p99)
  • エラー率(HTTP 5xx/4xx)
  • リクエスト数(リソース別/ルート別)
  • 相関ID(分散トレースの起点)

まとめ(TL;DR)

  1. readiness / liveness / startupを正しく設計し、依存障害を巻き込まない
  2. cluster / PM2で1Podの並列度を上げ、必要に応じてHPAで水平スケールする
  3. SSR/CSRの違いを踏まえた障害試験(部分劣化を含めて検証)
  4. CDNキャッシュ方針を明確化(静的は長寿命、SSR HTMLは慎重に)
  5. 構造化ログ+相関IDで可観測性を底上げ

Next.jsの運用に困っていたら、ぜひ試してみてください!
内容について、ご意見やツッコミもお寄せいただけると嬉しいです🥰

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?