「導入とパフォーマンスの最適化」
チャーリーがnode app.jsを使用してAPIを本番サーバーにデプロイした後、未処理の例外によりプロセスが頻繁にクラッシュし、無人運用中に長時間のサービス停止が発生していました。そこで彼は、自動再起動とクラスターモードを有効にするためにPM2を導入し、Dockerによるコンテナ化とNginxのリバースプロキシを組み合わせた結果、サービスの可用性を95%から99.9%へと向上させ、ようやく安眠できるようになりました。
学習内容:
- PM2 プロセス管理(起動/再起動/ログ/monit)
- Dockerの基礎(Dockerfile / docker-compose)
- 環境変数の管理(.env / 本番環境の設定)
- クラスタモジュール
- パフォーマンス分析(console.time / パフォーマンスフック / clinic.js)
- ヘルスチェックのエンドポイントの設計
- Nginxのリバースプロキシ設定
1. PM2 プロセス管理
(1) なぜプロセスマネージャーが必要なのか?
本番環境では、Node.js プロセスは、未処理の例外、メモリリーク、またはシステムリソースの不足によりクラッシュすることがあります。node app.js で直接起動した場合、クラッシュ後にプロセスは再起動されないため、サービスが中断してしまいます。PM2 は Node.js 向けに最も広く利用されているプロセスマネージャーであり、自動再起動、ログ管理、ロードバランシング、および監視機能を提供しています。
(2) インストールと基本コマンド
npm install -g pm2
pm2 start app.js --name "my-api"
pm2 restart my-api
pm2 stop my-api
pm2 delete my-api
pm2 logs my-api
pm2 monit
(3) PM2の一般的なコマンドのクイックリファレンス
| コマンド | 機能 | 一般的な使用例 |
|---|---|---|
pm2 start app.js |
アプリを起動 | 初回デプロイ |
pm2 restart <name> |
アプリを再起動 | コードの更新後 |
pm2 reload <name> |
ダウンタイムゼロの再起動 | 本番環境の更新 |
pm2 stop <name> |
サービス停止中 | メンテナンス中 |
pm2 delete <name> |
プロセスの削除 | 完全に削除 |
pm2 logs [name] |
ログを表示 | トラブルシューティング |
pm2 monit |
リアルタイム監視ダッシュボード | リソース使用状況の監視 |
pm2 list |
プロセス一覧 | 実行状況の表示 |
pm2 describe <name> |
プロセスの詳細 | 詳細な診断 |
pm2 save |
プロセス一覧の保存 | 起動設定 |
pm2 startup |
起動スクリプトを生成 | サーバー再起動後に自動的に復元 |
▶ 例:PM2の起動と監視
pm2 start app.js --name "charlie-api" -i max
pm2 monit
pm2 save
pm2 startup
┌─────┬──────────────┬─────────────┬─────────┬─────────┬──────────┐
│ id │ name │ mode │ ↺ │ status │ cpu │
├─────┼──────────────┼─────────────┼─────────┼─────────┼──────────┤
│ 0 │ charlie-api │ cluster │ 15 │ online │ 12% │
│ 1 │ charlie-api │ cluster │ 2 │ online │ 8% │
│ 2 │ charlie-api │ cluster │ 0 │ online │ 5% │
│ 3 │ charlie-api │ cluster │ 1 │ online │ 3% │
└─────┴──────────────┴─────────────┴─────────┴─────────┴──────────┘
2. Dockerの基礎
(1) なぜDockerを使うのか
Dockerは、アプリケーションとその依存関係をコンテナイメージにパッケージ化し、開発、テスト、本番環境全体で一貫性を確保します。以前はローカルマシンで正常に動作していたチャーリーのコードも、Node.jsのバージョンの違いにより、サーバーにデプロイされた後にエラーが発生していましたが、Dockerによってこの問題は完全に解決されました。
(2) Dockerfileの作成
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s \
CMD node healthcheck.js
USER node
CMD ["node", "app.js"]
(3) Docker Compose によるマルチサービスのオーケストレーション
version: "3.8"
services:
api:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- MONGO_URI=mongodb://mongo:27017/myapp
- REDIS_URL=redis://redis:6379
depends_on:
- mongo
- redis
restart: always
healthcheck:
test: ["CMD", "node", "healthcheck.js"]
interval: 30s
timeout: 3s
retries: 3
mongo:
image: mongo:7
volumes:
- mongo-data:/data/db
restart: always
redis:
image: redis:7-alpine
restart: always
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- api
restart: always
volumes:
mongo-data:
(4) 導入方法の比較
| カテゴリ | ベアメタル展開 | Docker 展開 | クラウドサービス展開 |
|---|---|---|---|
| 環境の一貫性 | 低い | 優れている | 優れている |
| 展開速度 | 遅い | 速い | 最も速い |
| 運用の複雑さ | 高 | 中 | 低 |
| リソース利用率 | 高 | 中 | オンデマンド |
| 拡張性の柔軟性 | 低い | 良い | 非常に良い |
| コスト | 低 | 中 | 高 |
| ユースケース | 小規模プロジェクト | 中・大規模プロジェクト | エンタープライズレベルのプロジェクト |
| 学習曲線 | 低 | 中 | 高 |
▶ 例:Docker イメージの作成と実行
docker build -t charlie-api:1.0 .
docker run -d -p 3000:3000 --env-file .env charlie-api:1.0
docker-compose up -d
docker-compose logs -f api
3. 環境変数の管理
(1) なぜ環境変数が必要なのか?
データベースのアドレス、ポート番号、パスワードなどの設定は、環境(開発、テスト、本番)によって異なります。これらの設定をコードにハードコーディングすると、セキュリティ上のリスクが生じ、環境間の切り替えが困難になります。環境変数を使用することで、Twelve-Factor App の方法論に沿って、設定とコードを分離することができます。
(2) dotenv および .env ファイル
const dotenv = require('dotenv');
dotenv.config({ path: `.env.${process.env.NODE_ENV || 'development'}` });
const config = {
port: parseInt(process.env.PORT, 10) || 3000,
mongoUri: process.env.MONGO_URI,
jwtSecret: process.env.JWT_SECRET,
redisUrl: process.env.REDIS_URL,
logLevel: process.env.LOG_LEVEL || 'info',
};
module.exports = config;
(3) 本番環境の構成戦略
.env # Default Configuration(Do not submit Git)
.env.development # Development Environment
.env.test # Test Environment
.env.production # Production Environment(Through CI/CD Inject,Do not commit to the code repository)
(4) 環境変数の管理方法の比較
| 手法 | セキュリティ | 柔軟性 | チームでの共同作業 | 活用事例 |
|---|---|---|---|---|
.env ファイル |
中 | 高 | 良好 | 一般的なプロジェクト |
| システム環境変数 | 高 | 低 | 不良 | 導入の容易さ |
| Dockerのシークレット | 高 | 中 | 良好 | Docker環境 |
| K8s Secrets | 高 | 高 | 優秀 | Kubernetes |
| クラウドプラットフォーム設定センター | 高 | 高 | 優秀 | クラウドネイティブプロジェクト |
▶ 例:マルチ環境設定の読み込み
const path = require('path');
const dotenv = require('dotenv');
const env = process.env.NODE_ENV || 'development';
const envFile = path.resolve(process.cwd(), `.env.${env}`);
dotenv.config({ path: envFile });
if (env === 'development') {
dotenv.config({ path: path.resolve(process.cwd(), '.env') });
}
const required = ['MONGO_URI', 'JWT_SECRET'];
const missing = required.filter(key => !process.env[key]);
if (missing.length) {
throw new Error(`Missing required env vars: ${missing.join(', ')}`);
}
module.exports = {
env,
port: parseInt(process.env.PORT, 10) || 3000,
mongoUri: process.env.MONGO_URI,
jwtSecret: process.env.JWT_SECRET,
redisUrl: process.env.REDIS_URL,
logLevel: process.env.LOG_LEVEL || 'info',
};
4. クラスタモード
(1) なぜクラスターが必要なのか?
Node.js はシングルスレッドであるため、1つのCPUコア上で実行できるインスタンスは1つだけです。クラスターモードでは、cluster モジュールを使用して複数のワーカープロセスを作成し、マルチコアCPUを最大限に活用することで、スループットと可用性を大幅に向上させます。
(2) クラスタ・モジュールの仕組み
マスタープロセスは、ポートでのリスニングとリクエストの分配を担当し、ワーカープロセスは実際のビジネスロジックを処理します。複数のワーカープロセスが同じポートを共有することで、負荷分散を実現しています。
(3) クラスタの手動設定
const cluster = require('cluster');
const os = require('os');
const http = require('http');
if (cluster.isPrimary) {
const numCPUs = os.cpus().length;
console.log(`Master ${process.pid} is running`);
console.log(`Forking ${numCPUs} workers...`);
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`Worker ${worker.process.pid} died. Restarting...`);
cluster.fork();
});
} else {
http.createServer((req, res) => {
res.writeHead(200);
res.end(`Handled by worker ${process.pid}\n`);
}).listen(3000);
console.log(`Worker ${process.pid} started`);
}
(4) PM2 クラスターモード
PM2には、コードを変更することなく有効化できるクラスタ機能が組み込まれています:
pm2 start app.js -i max
pm2 start app.js -i 4
▶ 例:クラスタモードの性能比較
node single.js &
ab -n 10000 -c 100 http://localhost:3000/
Single process:
Requests per second: 3254.21 [#/sec]
Cluster (4 workers):
Requests per second: 11280.67 [#/sec]
5. パフォーマンス分析
(1) console.time を使った基本的なタイミング
パフォーマンスを測定する最も簡単な方法で、処理が遅い箇所を素早く特定するのに最適です:
app.get('/api/users', async (req, res) => {
console.time('fetch-users');
const users = await User.find().lean();
console.timeEnd('fetch-users');
res.json(users);
});
(2) パフォーマンスフック:正確な測定
Node.js に組み込まれている perf_hooks モジュールは、高精度なタイミング機能を提供します:
const { performance, PerformanceObserver } = require('perf_hooks');
const obs = new PerformanceObserver((list) => {
const entries = list.getEntries();
entries.forEach((entry) => {
console.log(`${entry.name}: ${entry.duration.toFixed(2)}ms`);
});
});
obs.observe({ type: 'measure', buffered: true });
function measureAsync(label, fn) {
return async (...args) => {
performance.mark(`${label}-start`);
const result = await fn(...args);
performance.mark(`${label}-end`);
performance.measure(label, `${label}-start`, `${label}-end`);
return result;
};
}
const fastQuery = measureAsync('db-query', async () => {
return await User.find().lean();
});
(3) Clinic.js プロフェッショナル診断
Clinic.js は、Node.js が推奨する公式のパフォーマンス診断ツールキットです:
npm install -g clinic
clinic doctor -- node app.js
clinic flame -- node app.js
clinic bubbleprof -- node app.js
(4) 性能最適化戦略の比較
| 戦略 | ツール/手法 | 適用可能なシナリオ | 難易度 | 有効性 |
|---|---|---|---|---|
| タイミング解析 | console.time | クイック検索 | 低 | 中 |
| 正確な測定 | perf_hooks | クリティカルパス | 中 | 高 |
| CPU分析 | クリニック・フレイム | CPU負荷が高い | 中 | 高 |
| イベントループ | 診療医 | I/O ブロッキング | 中 | 高 |
| メモリリーク | heapdump / memwatch | メモリ関連の問題 | 高 | 高 |
| 負荷テスト | オートキャノン / ab | キャパシティプランニング | 低 | 中 |
| APMモニタリング | New Relic / Datadog | 継続的モニタリング | 中 | 高 |
▶ 例:負荷テストにおけるオートキャノンの使用
npm install -g autocannon
autocannon -c 100 -d 10 http://localhost:3000/api/health
┌─────────┬──────┬──────┬───────┬──────┬───────┬───────┬───────┐
│ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │
├─────────┼──────┼──────┼───────┼──────┼───────┼───────┼───────┤
│ Latency │ 2 ms │ 4 ms │ 12 ms │ 18ms │ 5 ms │ 3 ms │ 45 ms │
└─────────┴──────┴──────┴───────┴──────┴───────┴───────┴───────┘
Requests/sec: 18523.6
6. ヘルスチェックのエンドポイント
(1) なぜ健康診断が必要なのでしょうか?
コンテナオーケストレーションシステム(Docker、K8s)やロードバランサーは、サービスが正常に稼働しているかどうかを把握する必要があります。ヘルスチェックエンドポイントは、サービスの状態を検知するための標準化されたインターフェースを提供し、自動的な障害検知やトラフィックの切り替えを可能にします。
(2) 基本健康診断
app.get('/health', (req, res) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() });
});
(3) 詳細なヘルスチェック
app.get('/health', async (req, res) => {
const checks = {
server: 'ok',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
memory: process.memoryUsage(),
};
try {
await mongoose.connection.db.admin().ping();
checks.database = 'ok';
} catch (err) {
checks.database = 'error';
}
try {
await redisClient.ping();
checks.redis = 'ok';
} catch (err) {
checks.redis = 'error';
}
const isHealthy = checks.database === 'ok' && checks.redis === 'ok';
res.status(isHealthy ? 200 : 503).json(checks);
});
(4) 準備状態と稼働状態の分離
本番環境では、ヘルスチェックは通常、「稼働状態(プロセスが稼働しているかどうか)」と「応答状態(トラフィックを受け入れる準備ができているかどうか)」の2種類に分類されます:
app.get('/healthz', (req, res) => {
res.json({ status: 'alive' });
});
app.get('/readyz', async (req, res) => {
try {
await mongoose.connection.db.admin().ping();
await redisClient.ping();
res.json({ status: 'ready' });
} catch {
res.status(503).json({ status: 'not ready' });
}
});
▶ 例:Dockerのヘルスチェックスクリプト
const http = require('http');
const options = {
hostname: 'localhost',
port: process.env.PORT || 3000,
path: '/healthz',
timeout: 2000,
};
const req = http.request(options, (res) => {
if (res.statusCode === 200) {
process.exit(0);
} else {
process.exit(1);
}
});
req.on('error', () => process.exit(1));
req.on('timeout', () => { req.destroy(); process.exit(1); });
req.end();
7. Nginxのリバースプロキシ
(1) なぜNginxが必要なのか?
Nginxはリバースプロキシとして機能し、SSL終端、負荷分散、静的リソースの配信、リクエストのスロットリングなどの機能を提供します。Node.jsはビジネスロジックに重点を置き、Nginxはネットワーク層の最適化を担当します。それぞれが独自の役割を果たしています。
(2) Node.js の本番環境展開アーキテクチャ
graph LR
Client[Client] --> Nginx[Nginx Reverse Proxy<br/>:80/:443]
Nginx --> PM2[PM2 Cluster Management]
PM2 --> W1[Worker 1<br/>:3000]
PM2 --> W2[Worker 2<br/>:3000]
PM2 --> W3[Worker 3<br/>:3000]
PM2 --> W4[Worker 4<br/>:3000]
W1 --> DB[(MongoDB)]
W2 --> DB
W3 --> Redis[(Redis)]
W4 --> Redis
(3) Nginxの設定
upstream nodejs_backend {
least_conn;
server 127.0.0.1:3000;
server 127.0.0.1:3001;
server 127.0.0.1:3002;
keepalive 64;
}
server {
listen 80;
server_name api.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /etc/ssl/certs/api.example.com.crt;
ssl_certificate_key /etc/ssl/certs/api.example.com.key;
location / {
proxy_pass http://nodejs_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
location /health {
proxy_pass http://nodejs_backend/health;
access_log off;
}
location /static/ {
alias /app/public/;
expires 30d;
add_header Cache-Control "public, immutable";
}
limit_req_zone $binary_remote_addr zone=api:10m rate=30r/s;
limit_req zone=api burst=50 nodelay;
}
▶ 例:Docker における Nginx のリバースプロキシ設定
# docker-compose.yml Excerpt
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/ssl/certs:ro
depends_on:
api:
condition: service_healthy
restart: always
8. 包括的な例:デプロイメント構成の全体像
チャーリーは、すべてのデプロイ設定を単一のシステムに統合し、コードから本番環境までのプロセス全体を自動化しました。
▶ 例:プロジェクトのディレクトリ構造
charlie-api/
├── app.js
├── healthcheck.js
├── config/
│ └── index.js
├── ecosystem.config.js
├── Dockerfile
├── docker-compose.yml
├── nginx.conf
├── .env.example
├── .dockerignore
└── package.json
▶ 例:PM2 の ecosystem.config.js
module.exports = {
apps: [
{
name: 'charlie-api',
script: 'app.js',
instances: 'max',
exec_mode: 'cluster',
autorestart: true,
watch: false,
max_memory_restart: '512M',
env_development: {
NODE_ENV: 'development',
PORT: 3000,
},
env_production: {
NODE_ENV: 'production',
PORT: 3000,
},
error_file: './logs/error.log',
out_file: './logs/out.log',
merge_logs: true,
log_date_format: 'YYYY-MM-DD HH:mm:ss',
max_restarts: 10,
restart_delay: 4000,
kill_timeout: 5000,
listen_timeout: 10000,
},
],
deploy: {
production: {
user: 'deploy',
host: 'api.example.com',
ref: 'origin/main',
repo: 'git@github.com:charlie/api.git',
path: '/var/www/charlie-api',
'pre-deploy-local': '',
'post-deploy':
'npm ci && pm2 reload ecosystem.config.js --env production',
'pre-setup': '',
},
},
};
▶ 例:Dockerfile(マルチステージビルド向けに最適化)
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build 2>/dev/null || true
FROM node:20-alpine AS production
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
COPY --from=builder /app ./
RUN addgroup -g 1001 -S nodejs && adduser -S nodejs -u 1001
USER nodejs
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
CMD node healthcheck.js
CMD ["node", "app.js"]
▶ 例:.env.example
NODE_ENV=production
PORT=3000
MONGO_URI=mongodb://mongo:27017/charlie-api
REDIS_URL=redis://redis:6379
JWT_SECRET=your-secret-key-here
LOG_LEVEL=info
CORS_ORIGIN=https://example.com
RATE_LIMIT_WINDOW=60000
RATE_LIMIT_MAX=100
▶ 例:healthcheck.js
const http = require('http');
const req = http.request(
{
hostname: '127.0.0.1',
port: parseInt(process.env.PORT, 10) || 3000,
path: '/healthz',
timeout: 2000,
},
(res) => {
process.exit(res.statusCode === 200 ? 0 : 1);
}
);
req.on('error', () => process.exit(1));
req.on('timeout', () => { req.destroy(); process.exit(1); });
req.end();
▶ 例:app.js 内のヘルスチェックエンドポイント
const express = require('express');
const mongoose = require('mongoose');
const config = require('./config');
const app = express();
app.use(express.json());
app.get('/healthz', (req, res) => {
res.json({ status: 'alive', pid: process.pid });
});
app.get('/readyz', async (req, res) => {
try {
await mongoose.connection.db.admin().ping();
res.json({ status: 'ready', pid: process.pid, uptime: process.uptime() });
} catch {
res.status(503).json({ status: 'not ready', pid: process.pid });
}
});
app.get('/health', async (req, res) => {
const checks = {
status: 'ok',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
memory: process.memoryUsage(),
pid: process.pid,
};
try {
await mongoose.connection.db.admin().ping();
checks.database = 'ok';
} catch {
checks.database = 'error';
checks.status = 'degraded';
}
const isHealthy = checks.database === 'ok';
res.status(isHealthy ? 200 : 503).json(checks);
});
app.get('/api/users', async (req, res) => {
console.time('fetch-users');
const users = await mongoose.model('User').find().lean();
console.timeEnd('fetch-users');
res.json({ success: true, data: users });
});
mongoose.connect(config.mongoUri).then(() => {
app.listen(config.port, () => {
console.log(`Server running on port ${config.port} [${config.env}]`);
});
});
process.on('SIGTERM', () => {
console.log('SIGTERM received, shutting down gracefully...');
mongoose.connection.close();
process.exit(0);
});
process.on('SIGINT', () => {
console.log('SIGINT received, shutting down gracefully...');
mongoose.connection.close();
process.exit(0);
});
❓ よくある質問
Q: PM2とDocker、どちらを使うべきですか? A: 両者は互いに排他的ではありません。Dockerは環境の一貫性に関する問題を解決し、PM2はプロセス管理とクラスタリングを担当します。本番環境では、DockerとPM2を併用し、Dockerコンテナ内でPM2を実行して複数のプロセスを管理することをお勧めします。
Q: ダウンタイムゼロのデプロイを実現するにはどうすればよいですか? A: pm2 restart を pm2 reload に置き換えてください。PM2 はワーカーを1つずつ再起動し、常に一部のインスタンスがオンライン状態を維持してリクエストを処理できるようにします。Docker 環境では、ブルーグリーンデプロイやローリングアップデートといった戦略を利用できます。
Q: クラスタモードにはどのような制限がありますか? A: クラスタモードでは、プロセスはメモリを共有しません。セッションはRedisなどの外部ストレージを使用して共有する必要があります。また、WebSocketsを使用する場合は、スティッキーセッションまたはPub/Subメッセージ同期と組み合わせて使用する必要があります。さらに、ファイルシステムのキャッシュはプロセスごとに独立しています。
Q: 本番環境をどのように監視すればよいですか? A: PM2 には pm2 monit リアルタイム監視機能と pm2 plus オンラインダッシュボードが用意されています。商用ソリューションとしては、New Relic、Datadog、Prometheus + Grafana などがあります。主要な指標には、CPU、メモリ、イベントループのレイテンシ、リクエストの応答時間などがあります。
Q: Docker イメージのサイズを縮小するにはどうすればよいですか? A: node:alpine ベースイメージを使用し、マルチステージビルドでコンパイル依存関係を分離し、.dockerignore 不要なファイルを除外し、npm ci --only=production 本番環境用の依存関係のみをインストールし、npm キャッシュをクリアします npm cache clean --force。
Q: Nginxをリバースプロキシとして使用するメリットは何ですか? A: SSLオフロードによりNode.jsの暗号化処理の負荷が軽減されます。静的リソースはNginxが直接処理します。ロードバランシングによりリクエストが分散されます。レート制限によりDDoS攻撃が防止されます。gzip圧縮によりデータ転送量が削減されます。また、キャッシュによりレスポンスが高速化されます。
📖 まとめ
(1) このレッスンの要点
- PM2 は、プロセスの自動再起動、クラスタモード、およびログ管理機能を実装しています
- Dockerによるコンテナ化により、環境の一貫性と標準化されたデプロイが確保されます
- 環境変数を用いた個別の設定が可能で、複数の環境間の切り替えに対応しています
- クラスターモードは、マルチコアCPUの性能を最大限に活用してスループットを向上させます
- パフォーマンス・フックとClinic.js:パフォーマンスのボトルネックを特定する
- ヘルスチェックのエンドポイントは、コンテナオーケストレーションとロードバランシングの基盤となるものです
- SSL、ロードバランシング、および静的リソース向けのNginxリバースプロキシ
(2) 宿題
- PM2 を使用して Express プロジェクトを起動し、
ecosystem.config.jsを設定してクラスタモードを有効にし、pm2 monitを使用して実行状況を監視します。 - プロジェクト用の Dockerfile を作成し、マルチステージビルドを使用してイメージサイズを最適化し、
docker-composeを使用して Node.js と MongoDB を同時に起動する - 3つのヘルスチェックエンドポイント
/health、/healthz、/readyzを実装し、Docker 内でHEALTHCHECKを設定します。 autocannonを使用して API の負荷テストを実施し、シングルプロセスモードとクラスタモードのスループットの違いを比較してください。- SSLの終端処理と負荷分散を実装するためのNginxのリバースプロキシ設定を作成し、
abまたはcurlを使用して、プロキシ転送が正常に機能していることを確認します。
📝 練習問題
- このレッスンにあるすべてのコード例を完成させ、それぞれが正しく動作することを確認してください。
- 包括的な例を修正し、独自の拡張機能を追加する
- 公式ドキュメントを確認し、このレッスンで取り上げられていないAPIを1~2つ見つけ、それらのテストコードを作成してください。
- 振り返り:このレッスンで学んだことを、実際のプロジェクトにどのように活かしますか?
- このレッスンで学んだことと、これまでのレッスンの内容を組み合わせて、小さなプロジェクトを作成してみてください。



