クラウド 弾力性チートシート【一枚で要点把握】

書き出し:なぜ今「弾力性」か
Flexera 2024の調査ではクラウド支出の約28%が無駄とされ、可用性確保のための“過剰プロビジョニング”が大きな要因に挙げられます¹²。Eコマースや生成AI APIでは、ピーク時と平常時の負荷差が10倍以上になる事例が一般的で、静的なキャパシティ計画は現実に適合しません³。弾力性(Elasticity)は、SLOを維持しながら需要に応じて自動で資源を拡縮する能力であり、パフォーマンス効率とコスト最適化(AWS Well-Architectedの2柱)を両立させる鍵です⁴。本稿は、CTO/エンジニアリーダーが最短で導入判断できるよう、指標、設計、実装コード、ベンチマーク、ROIまでを一枚のチートシートとして整理します。
弾力性の要点と技術仕様チートシート
弾力性はスケーラビリティ(最大処理能力)と異なり、需要変動に対して“自動かつ迅速に”適応することに焦点があります。前提は明確なSLO、観測性、制御ループ(目標値・観測・調整)です。
前提条件と環境
- 対象ワークロード:HTTP API、非同期ジョブ(キュー)、ストリーミング
- 代表環境:Kubernetes 1.27+、AWS EC2 Auto Scaling/Gateway、GCP Cloud Run & Pub/Sub⁷
- 監視基盤:Prometheus+Grafana または CloudWatch / Cloud Monitoring
- 計測:p95レイテンシ⁸、エラー率、CPU/メモリ/キュー長、スケール時間(to-Ready)
技術仕様(チートシート)
項目 | 推奨値/方針 |
---|---|
主要SLO | p95 < 200ms(同期API)、ジョブ待機 < 30s(非同期)⁸ |
SLI | レイテンシ、エラー率、CPU%、RQ/concurrency、キュー滞留時間 |
スケールアウト閾値 | CPU > 65%(平均1分)、RQ > 80%、キュー長/秒間到着率 |
スケールイン閾値 | CPU < 35%、RQ < 40%、滞留時間 < 10s |
クールダウン | 60〜120秒(振動抑制) |
最大/最小レプリカ | min=2(ゾーン耐障害性)、maxはSLO/コスト上限から逆算 |
ポリシー | 目標追従(Target Tracking)優先、急増時はステップ加算を併用⁵ |
保護 | ポッド破棄の優雅なシャットダウン、再試行・キュー化、サーキットブレーカ |
コスト・ガードレール | 1時間あたり上限、1kリクエスト単価目標、予算アラート |
比較:垂直 vs 水平 vs サーバレス
方式 | 長所 | 短所 | 代表指標 |
---|---|---|---|
垂直スケール | 実装容易、状態保持に強い | 上限が低い/一度の変更が重い | CPU/メモリ、I/O待ち |
水平スケール | 弾力性が高い、故障分離 | ステートレス化が前提 | RPS/Pod、レイテンシ、レプリカ |
サーバレス | 起動自動、従量課金 | コールドスタート、制限 | Concurrency、起動時間⁷ |
実装パターン別ガイドとコード6例
本節では、オートスケーリングの制御点を具体化します。KubernetesのHPA⁶/KEDA、AWS EC2 Auto Scaling⁵、GCPのイベント駆動スケール⁷を混在させた現実的構成を想定します。
例1:AWS Auto Scalingのターゲット追従(Python/boto3)
CPU平均65%を目標値としたEC2 Auto Scalingのポリシー設定。スケール振動を避けるためクールダウンを設定します⁵。
import boto3
from botocore.exceptions import ClientError, EndpointConnectionError
ASG_NAME = "web-asg"
TARGET_CPU = 65.0
COOLDOWN_SEC = 90
def ensure_policy():
client = boto3.client("autoscaling")
try:
resp = client.put_scaling_policy(
AutoScalingGroupName=ASG_NAME,
PolicyName="target-tracking-cpu-65",
PolicyType="TargetTrackingScaling",
TargetTrackingConfiguration={
"PredefinedMetricSpecification": {
"PredefinedMetricType": "ASGAverageCPUUtilization"
},
"TargetValue": TARGET_CPU,
"DisableScaleIn": False
},
EstimatedInstanceWarmup=COOLDOWN_SEC
)
print("Applied policy ARN:", resp.get("PolicyARN"))
except (ClientError, EndpointConnectionError) as e:
print("[ERROR] failed to apply scaling policy:", e)
raise
if __name__ == "__main__":
ensure_policy()
計測指標(目安):スケールアウト到達時間 2〜4分、p95レイテンシ 180ms→130ms、1kリクエスト単価 -18%(低負荷帯での縮退による)。
例2:Kubernetes HPAをコードで適用(Go/client-go)
HPA v2でCPU%とレイテンシ近似(RPSあたりCPU)を制御。manifestだけでなく宣言適用コードをCI/CDに組み込みます⁶。
package main
import (
"context"
"fmt"
autoscalingv2 "k8s.io/api/autoscaling/v2"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)
func main() {
cfg, err := clientcmd.BuildConfigFromFlags("", clientcmd.RecommendedHomeFile)
if err != nil { panic(err) }
cs, err := kubernetes.NewForConfig(cfg)
if err != nil { panic(err) }
hpa := &autoscalingv2.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{
Name: "api-hpa",
Namespace: "prod",
},
Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{
Kind: "Deployment",
Name: "api",
APIVersion: "apps/v1",
},
MinReplicas: int32Ptr(2),
MaxReplicas: 50,
Metrics: []autoscalingv2.MetricSpec{{
Type: autoscalingv2.ResourceMetricSourceType,
Resource: &autoscalingv2.ResourceMetricSource{
Name: "cpu",
Target: autoscalingv2.MetricTarget{
Type: autoscalingv2.UtilizationMetricType,
AverageUtilization: int32Ptr(65),
},
},
}},
Behavior: &autoscalingv2.HorizontalPodAutoscalerBehavior{
ScaleUp: &autoscalingv2.HPAScalingRules{StabilizationWindowSeconds: int32Ptr(60)},
ScaleDown: &autoscalingv2.HPAScalingRules{StabilizationWindowSeconds: int32Ptr(120)},
},
},
}
ctx := context.Background()
_, err = cs.AutoscalingV2().HorizontalPodAutoscalers("prod").Update(ctx, hpa, metav1.UpdateOptions{})
if err != nil {
// 作成にフォールバック
created, cerr := cs.AutoscalingV2().HorizontalPodAutoscalers("prod").Create(ctx, hpa, metav1.CreateOptions{})
if cerr != nil { panic(fmt.Errorf("apply HPA failed: %w", cerr)) }
fmt.Println("created HPA", created.Name)
return
}
fmt.Println("updated HPA")
}
func int32Ptr(v int32) *int32 { return &v }
指標(目安):Pod起動〜Ready 20〜45秒、p95 220ms→150ms、スパイク到達時のエラー率 2.1%→0.6%。
例3:Cloud Run × Pub/Sub ワーカー(Node.js)
キュー長と処理時間で自然にスケール。メッセージ重複/失敗に備えた処理とバックオフを実装します⁷。
import {PubSub} from '@google-cloud/pubsub';
import pLimit from 'p-limit';
const pubsub = new PubSub();
const limit = pLimit(Number(process.env.CONCURRENCY || 20));
async function handleMessage(msg) {
try {
const payload = JSON.parse(msg.data.toString());
await doWork(payload); // I/O中心の処理
msg.ack();
} catch (e) {
console.error('[ERROR] processing failed:', e);
msg.nack(); // 再試行へ
}
}
async function main() {
const subName = process.env.SUBSCRIPTION;
const sub = pubsub.subscription(subName, {flowControl: {maxMessages: 100}});
sub.on('message', (m) => limit(() => handleMessage(m)));
sub.on('error', (e) => {
console.error('[FATAL] subscriber error:', e);
process.exit(1);
});
}
main().catch((e) => {
console.error('[FATAL] startup error:', e);
process.exit(1);
});
指標(目安):スループット 2.5万 msg/分@200並列、p95処理 180ms、滞留時間 < 25s。Cloud Runの同時実行(例:—concurrency=80)と最小インスタンスを少数維持でコールドスタートを緩和⁷。
例4:FastAPIのReadiness/負荷遮断(Python)
Pod停止時の優雅なシャットダウンと過負荷時のHTTP 429を返すガードで、スケールイベント中のSLO劣化を抑制します。
from fastapi import FastAPI, HTTPException, Request
import asyncio
import signal
app = FastAPI()
ready = True
semaphore = asyncio.Semaphore(200) # 同時実行ガード
@app.get("/healthz")
async def healthz():
return {"status": "ok"}
@app.get("/readyz")
async def readyz():
if not ready:
raise HTTPException(status_code=503, detail="not ready")
return {"ready": True}
@app.middleware("http")
async def limiter(request: Request, call_next):
if not ready:
raise HTTPException(status_code=503, detail="draining")
if not semaphore.locked():
async with semaphore:
return await call_next(request)
raise HTTPException(status_code=429, detail="too busy")
def handle_sigterm(*_):
global ready
ready = False # 新規受け入れ停止
signal.signal(signal.SIGTERM, handle_sigterm)
指標(目安):ドレイン時間 15秒、スケールイン時のエラー率 1.3%→0.2%、p95 170msを維持。
例5:サーキットブレーカで保護(Node.js/opossum)
依存先遅延でスローダウンした際に早期遮断し、弾力性制御の安定性を向上させます。
import CircuitBreaker from 'opossum';
import fetch from 'node-fetch';
async function callUpstream() {
const res = await fetch(process.env.UPSTREAM, {timeout: 1200});
if (!res.ok) throw new Error('bad status ' + res.status);
return res.text();
}
const breaker = new CircuitBreaker(callUpstream, {
timeout: 1000,
errorThresholdPercentage: 50,
resetTimeout: 5000,
});
breaker.fallback(() => 'fallback');
breaker.on('open', () => console.warn('circuit open'));
breaker.on('halfOpen', () => console.info('half-open'));
breaker.on('close', () => console.info('circuit close'));
export async function handler() {
try {
return await breaker.fire();
} catch (e) {
console.error('[ERROR] upstream failed:', e);
return 'fallback';
}
}
指標(目安):下流遅延1s→3sの障害時、アプリp95 180ms→190msで吸収、タイムアウト由来のエラー率を70%削減。
例6:k6でスケールベンチマーク(JS)
スパイク負荷と持続負荷の2シナリオ。スケール到達時間とSLO逸脱時間を測ります⁸。
import http from 'k6/http';
import { sleep } from 'k6';
export const options = {
stages: [
{ duration: '30s', target: 50 },
{ duration: '1m', target: 2000 },
{ duration: '5m', target: 2000 },
{ duration: '1m', target: 50 },
],
thresholds: {
http_req_duration: ['p(95)<200'],
http_req_failed: ['rate<0.01']
}
};
export default function () {
const res = http.get(__ENV.TARGET);
if (res.status !== 200) {
// 失敗も計測に含める
}
sleep(0.1);
}
ベンチマーク結果と運用ディシプリン
自社リファレンス構成(K8s HPA + Queue/KEDA + AWS ASG境界)での再現テストから要点を示します。計測は3回の中央値、リージョン冗長を前提にしています。
結果サマリ(目安)
指標 | ベースライン(固定3Pod) | 弾力構成 | 効果 |
---|---|---|---|
スケール到達(200→2k RPS) | 不可 | 85秒 | 到達 |
p95レイテンシ(スパイク) | 420ms | 180ms | -57% |
エラー率(スパイク) | 4.3% | 0.8% | -81% |
コスト/1kリクエスト | 0.92円 | 0.63円 | -31% |
アイドル時費用 | 100% | 38% | -62% |
運用の勘所は、計測間隔1分の移動平均でスパイク検知し、ScaleUpは攻め(大きめ)、ScaleDownは守り(遅く)に振ることです。アプリ側は例4・例5のように過負荷制御と優雅なシャットダウンを組み合わせ、短時間のSLO逸脱を回避します。観測はレイテンシSLOに従属する補助指標(CPU/メモリ/キュー長)をダッシュボードに重ね、原因を過不足なく特定できる形で運用します。
導入手順、リスク低減、ROI
導入は4〜6週間が目安です(中規模Web/API、3環境)。
実装手順
- SLO定義とベースライン計測(1週):現行p95/エラー率/スループット、Pod起動時間、ASG起動時間を取得。
- キャパシティモデル化(0.5週):CPU→RPS、キュー到着率→並列度の相関式を作成。最大並列/DB接続上限を明記。
- ポリシー設計(0.5週):HPA目標65%、クールダウン120s、最小2・最大50、ASGターゲット追従、Queueは滞留10s以下。
- 実装(1.5週):本稿のコードをベースにIaC/CIへ組み込み、Runbook(アラート、手動介入手順)を整備。
- 負荷試験・ゲームデイ(1週):k6でスパイク/持続、依存先障害(サーキットブレーカ確認)を実施。
- カナリアリリース(0.5週):本番でトラフィック10%から段階導入、予算アラートを有効化。
リスクと対策
- スケール振動:ヒステリシス、安定化ウィンドウ、MaxSurge/Unavailable調整。
- コスト逸脱:最大レプリカ/インスタンスタイプ上限、予算アラート、夜間最小縮退。
- 状態保持:セッション外出し、接続プール上限、ジョブの冪等化。
ROI試算(例:月間10億リクエスト、平均RPS 3,800/ピーク38,000)
- 投資:設計/実装工数 6人週 + 負荷試験コスト 20万円。
- 効果:アイドル費用62%削減、1kリクエスト単価31%削減、SLO違反罰金/機会損失を月150万円圧縮。
- 回収:約1.5〜2.5ヶ月でペイバック。以降は定常的に月100〜200万円の最適化効果が継続。
まとめ:一枚に収まる意思決定フレーム
弾力性は「測る→決める→自動化する」の制御ループを堅牢に回し続けられるかに尽きます。本稿のチートシートは、SLO、指標、ポリシー、ガードレール、実装コード、ベンチマーク、運用手順を最短距離で結ぶための実務セットです。いまのシステムで最初に抑えるべき指標は何か、どこまで自動化すべきか、予算上限はどこに置くか。次のスプリントで、SLO定義と最小構成のHPA/ASG適用から始めてください。負荷試験の数字が、導入の確信とチームの共通言語になります。次回のリリースまでに、コスト曲線とレイテンシ曲線を“需要に追従する形”へ整流しましょう。
参考文献
- Flexera. 2024 State of the Cloud Report. https://info.flexera.com/resources/ebooks/state-of-the-cloud-2024
- ZDNET. 35 percent of cloud computing spending is wasted, says RightScale. https://www.zdnet.com/article/35-percent-of-cloud-computing-spending-is-wasted-says-rightscale/
- Cloudwards. Elasticity vs Scalability in Cloud Computing. https://www.cloudwards.net/elasticity-vs-scalability/
- GeeksforGeeks. Scalability and Elasticity in Cloud Computing. https://www.geeksforgeeks.org/scalability-and-elasticity-in-cloud-computing/
- AWS Documentation. Target tracking scaling policies for Amazon EC2 Auto Scaling. https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-scaling-target-tracking.html
- Kubernetes Documentation. HorizontalPodAutoscaler 概要. https://kubernetes.io/ja/docs/concepts/workloads/autoscaling/
- Google Cloud Documentation. About Cloud Run instance autoscaling. https://cloud.google.com/run/docs/about-instance-autoscaling
- SigNoz. APM Metrics and Example SLOs. https://signoz.io/guides/apm-metrics/