Chuyển đến nội dung chính

レッスン 20: 本番環境の監視とスケーリング

Pino 構造化ログ、OpenTelemetry トレース。 Prometheus メトリクス、Grafana ダッシュボード。 PM2エコシステム。正常なシャットダウン、ヘルスチェック。水平スケーリング、ステートレスなデザイン。

💻 プログラミング — レッスン 20 レッスン 20: 本番環境の監視とスケーリング

Node.js コア: 基本から高度まで

パート 5: テスト、パフォーマンス、および実稼働

xdev.asia

1. 構造化ロギング (Pino)

import pino from 'pino'

const logger = pino({
  level: process.env.LOG_LEVEL || 'info',
  transport: process.env.NODE_ENV !== 'production'
    ? { target: 'pino-pretty', options: { colorize: true } }
    : undefined,
  redact: ['req.headers.authorization', 'password'],
})

// Context logging
const reqLogger = logger.child({ requestId: crypto.randomUUID() })
reqLogger.info({ userId: '123', action: 'login' }, 'User logged in')
reqLogger.error({ err, orderId: '456' }, 'Payment failed')

// HTTP request logging (middleware)
import pinoHttp from 'pino-http'
const httpLogger = pinoHttp({ logger })

2.オープンテレメトリー

// tracing.ts — import first!
import { NodeSDK } from '@opentelemetry/sdk-node'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'

const sdk = new NodeSDK({
  traceExporter: new OTLPTraceExporter({
    url: 'http://jaeger:4318/v1/traces',
  }),
  instrumentations: [
    getNodeAutoInstrumentations({
      '@opentelemetry/instrumentation-http': { enabled: true },
      '@opentelemetry/instrumentation-pg': { enabled: true },
      '@opentelemetry/instrumentation-redis': { enabled: true },
    }),
  ],
})

sdk.start()
process.on('SIGTERM', () => sdk.shutdown())
node --require ./tracing.js app.js

3. プロメテウスのメトリクス

import { Registry, Counter, Histogram, collectDefaultMetrics } from 'prom-client'

const register = new Registry()
collectDefaultMetrics({ register })

const httpRequests = new Counter({
  name: 'http_requests_total',
  help: 'Total HTTP requests',
  labelNames: ['method', 'path', 'status'],
  registers: [register],
})

const httpDuration = new Histogram({
  name: 'http_request_duration_seconds',
  help: 'HTTP request duration',
  labelNames: ['method', 'path'],
  buckets: [0.01, 0.05, 0.1, 0.5, 1, 5],
  registers: [register],
})

// Middleware
function metricsMiddleware(req, res, next) {
  const end = httpDuration.startTimer({ method: req.method, path: req.path })
  res.on('finish', () => {
    httpRequests.inc({ method: req.method, path: req.path, status: res.statusCode })
    end()
  })
  next()
}

// Metrics endpoint
app.get('/metrics', async (req, res) => {
  res.set('Content-Type', register.contentType)
  res.end(await register.metrics())
})

4. ヘルスチェック

app.get('/health', async (req, res) => {
  const checks = {
    uptime: process.uptime(),
    timestamp: Date.now(),
    status: 'ok' as string,
    checks: {} as Record<string, string>,
  }

  try {
    await pool.query('SELECT 1')
    checks.checks.database = 'ok'
  } catch {
    checks.checks.database = 'error'
    checks.status = 'degraded'
  }

  try {
    await redis.ping()
    checks.checks.redis = 'ok'
  } catch {
    checks.checks.redis = 'error'
    checks.status = 'degraded'
  }

  const statusCode = checks.status === 'ok' ? 200 : 503
  res.status(statusCode).json(checks)
})

5. 正常なシャットダウン

async function gracefulShutdown(signal: string) {
  logger.info(`Received ${signal}. Starting graceful shutdown...`)

  // Stop accepting new connections
  server.close()

  // Drain existing connections (max 10s)
  const timeout = setTimeout(() => {
    logger.error('Forced shutdown after timeout')
    process.exit(1)
  }, 10000)

  try {
    await pool.end()      // Close DB connections
    await redis.quit()    // Close Redis
    clearTimeout(timeout)
    logger.info('Shutdown complete')
    process.exit(0)
  } catch (err) {
    logger.error({ err }, 'Error during shutdown')
    process.exit(1)
  }
}

process.on('SIGTERM', () => gracefulShutdown('SIGTERM'))
process.on('SIGINT', () => gracefulShutdown('SIGINT'))

6. 制作チェックリスト

カテゴリチェックリスト
ロギングPino 構造、リクエスト ID、ログ レベル
モニタリングプロメテウス + グラファナ、OpenTelemetry
エラー追跡セントリー、unhandledRejection ハンドラー
ヘルスチェック/health エンドポイント、readiness プローブ
正常なシャットダウンSIGTERM ハンドラー、接続ドレイン
セキュリティヘルメット、CORS、レート制限、入力検証
パフォーマンスクラスター/PM2、接続プーリング、キャッシュ

🎉 シリーズを完了する Node.js コア: 基本から高度まで!