「負荷テストで限界が分かったら、次にやるべきことは何だ?」佐藤CTOはホワイトボードにグラフを描きながら言った。「**容量計画(キャパシティプランニング)**だ。今のシステムがどこまで耐えられて、いつスケールすべきか。勘ではなくデータで判断する。」
1. キャパシティプランニングの基礎
キャパシティプランニングのプロセス
| フェーズ | 内容 | アウトプット |
|---|---|---|
| 需要予測 | トラフィック増加のトレンド分析 | 将来の負荷見積もり |
| 現状評価 | 現在のリソース使用率と限界把握 | ボトルネックマップ |
| ギャップ分析 | 需要と供給の差分 | スケーリング要件 |
| 計画策定 | スケーリング戦略とタイムライン | 実行計画書 |
| 実行・検証 | スケーリング実施と負荷テスト | 検証レポート |
// 需要予測モデル
interface DemandForecast {
currentRps: number;
growthRateMonthly: number; // 月次成長率(例: 0.15 = 15%)
seasonalPeakMultiplier: number; // 季節変動のピーク倍率
safetyMargin: number; // 安全マージン(例: 0.3 = 30%)
}
interface CapacityPlan {
month: number;
expectedRps: number;
peakRps: number;
requiredCapacityRps: number;
currentCapacityRps: number;
needsScaling: boolean;
}
function generateCapacityPlan(
forecast: DemandForecast,
currentCapacityRps: number,
months: number
): CapacityPlan[] {
const plans: CapacityPlan[] = [];
for (let m = 1; m <= months; m++) {
const expectedRps = forecast.currentRps *
Math.pow(1 + forecast.growthRateMonthly, m);
const peakRps = expectedRps * forecast.seasonalPeakMultiplier;
const requiredCapacityRps = peakRps * (1 + forecast.safetyMargin);
plans.push({
month: m,
expectedRps: Math.round(expectedRps),
peakRps: Math.round(peakRps),
requiredCapacityRps: Math.round(requiredCapacityRps),
currentCapacityRps,
needsScaling: requiredCapacityRps > currentCapacityRps,
});
}
return plans;
}
// 例: 現在 5,000 RPS、月次15%成長、ピーク2倍、マージン30%
const plan = generateCapacityPlan(
{ currentRps: 5000, growthRateMonthly: 0.15, seasonalPeakMultiplier: 2.0, safetyMargin: 0.3 },
20000,
12
);
// Month 3: required=19770 → OK (20000)
// Month 4: required=22737 → NG → スケーリング必要
2. リトルの法則を活用した容量計算
// 各レイヤーの容量をリトルの法則で計算
// L = λ × W → λ = L / W
interface LayerCapacity {
name: string;
maxConcurrency: number;
avgLatencyMs: number;
maxThroughputRps: number;
}
function calculateLayerCapacity(
name: string, maxConcurrency: number, avgLatencyMs: number
): LayerCapacity {
return {
name,
maxConcurrency,
avgLatencyMs,
maxThroughputRps: Math.round(maxConcurrency / (avgLatencyMs / 1000)),
};
}
const layers = [
calculateLayerCapacity('Load Balancer', 10000, 1), // 10,000,000 RPS
calculateLayerCapacity('Web Server (per pod)', 100, 50), // 2,000 RPS
calculateLayerCapacity('App Server (per pod)', 50, 80), // 625 RPS
calculateLayerCapacity('DB Connection Pool', 100, 15), // 6,667 RPS
calculateLayerCapacity('External API', 20, 200), // 100 RPS ← ボトルネック
];
// ボトルネック = スループット最小のレイヤー
const bottleneck = layers.reduce((min, l) =>
l.maxThroughputRps < min.maxThroughputRps ? l : min
);
3. 水平スケーリングと垂直スケーリング
| 観点 | 垂直 (Scale Up) | 水平 (Scale Out) |
|---|---|---|
| 方法 | マシンスペックを上げる | マシン台数を増やす |
| 上限 | ハードウェア物理限界 | 理論上は無制限 |
| ダウンタイム | 通常必要 | 無停止で追加可能 |
| コスト曲線 | 指数的(高スペックほど割高) | 線形的 |
| 複雑性 | 低い | 高い(分散の課題) |
| 適用場面 | DB、キャッシュ | Web/Appサーバー、ワーカー |
function decideScalingStrategy(context: {
component: string;
isStateful: boolean;
canBeSharded: boolean;
hasSessionAffinity: boolean;
}): { strategy: 'scale_up' | 'scale_out' | 'hybrid'; reason: string } {
if (context.isStateful && !context.canBeSharded) {
return { strategy: 'scale_up', reason: `${context.component}はステートフル。垂直スケールが最適` };
}
if (!context.isStateful && !context.hasSessionAffinity) {
return { strategy: 'scale_out', reason: `${context.component}はステートレス。水平スケールが最もコスト効率が良い` };
}
return { strategy: 'hybrid', reason: `まずScale Up、限界後にScale Out` };
}
4. オートスケーリング設計
「オートスケーリングは魔法ではない」と佐藤CTOは釘を刺した。「設定を間違えるとフラッピング(頻繁な増減)で逆に不安定になる。」
interface AutoScalingPolicy {
minInstances: number;
maxInstances: number;
targetMetrics: {
name: string;
scaleOutThreshold: number;
scaleInThreshold: number;
evaluationPeriodSec: number;
dataPointsRequired: number;
}[];
cooldown: {
scaleOutCooldownSec: number;
scaleInCooldownSec: number;
instanceWarmupSec: number;
};
}
const ecSitePolicy: AutoScalingPolicy = {
minInstances: 3,
maxInstances: 50,
targetMetrics: [
{ name: 'CPUUtilization', scaleOutThreshold: 70, scaleInThreshold: 30,
evaluationPeriodSec: 60, dataPointsRequired: 3 },
{ name: 'RequestCountPerTarget', scaleOutThreshold: 1200, scaleInThreshold: 500,
evaluationPeriodSec: 60, dataPointsRequired: 2 },
],
cooldown: {
scaleOutCooldownSec: 120,
scaleInCooldownSec: 300,
instanceWarmupSec: 180,
},
};
// スケーリングのタイムライン
// 検知(180s) + 判断(10s) + 起動(60s) + 準備(60s) = 約310秒(5分)
// → 5分間はトラフィック増加を既存インスタンスで吸収する必要がある
5. スケーリングの壁と対策
| 問題 | 症状 | 対策 |
|---|---|---|
| DBボトルネック | サーバーを増やしてもRPSが伸びない | リードレプリカ、キャッシュ、CQRS |
| セッション管理 | スケールアウト後にセッション消失 | 外部セッションストア、JWT |
| ホットスポット | 特定シャードに負荷集中 | パーティションキー見直し |
| サンダリングハード | キャッシュ失効時にDB集中 | ジッター付きTTL |
| コーディネーション | ノード増加でスループット低下 | 結果整合性への変更 |
コラム: コスト効率の分析
スケーリング効率は線形ではない。インスタンス数が増えると、コーディネーションのオーバーヘッドで効率が逓減する。定期的にコスト/リクエストを計測し、効率が急激に低下するポイントでアーキテクチャの見直しを検討すべき。
まとめ
| トピック | 要点 |
|---|---|
| キャパシティプランニング | 需要予測 → 現状評価 → ギャップ分析 → 計画 → 検証 |
| リトルの法則 | 各レイヤーで個別に容量算出しボトルネック特定 |
| 水平 vs 垂直 | ステートレス → Scale Out、ステートフル → Scale Up |
| オートスケーリング | メトリクス選択、クールダウン、予測スケーリングが鍵 |
| スケーリングの限界 | DB、セッション、ホットスポットが壁になる |
チェックリスト
- キャパシティプランニングの5フェーズを説明できる
- リトルの法則でボトルネックを特定できる
- 水平/垂直スケーリングの使い分けを判断できる
- オートスケーリングのタイムラインを計算できる
- スケーリングの限界と対策を説明できる
次のステップへ
容量計画とスケーリング戦略を学んだ。次は パフォーマンスCI/CD で、パフォーマンスの継続的な監視と回帰テストの自動化を学ぼう。
推定読了時間: 40分