Article

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

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

書き出し:なぜ今「弾力性」か

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)

技術仕様(チートシート)

項目推奨値/方針
主要SLOp95 < 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レイテンシ(スパイク)420ms180ms-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環境)。

実装手順

  1. SLO定義とベースライン計測(1週):現行p95/エラー率/スループット、Pod起動時間、ASG起動時間を取得。
  2. キャパシティモデル化(0.5週):CPU→RPS、キュー到着率→並列度の相関式を作成。最大並列/DB接続上限を明記。
  3. ポリシー設計(0.5週):HPA目標65%、クールダウン120s、最小2・最大50、ASGターゲット追従、Queueは滞留10s以下。
  4. 実装(1.5週):本稿のコードをベースにIaC/CIへ組み込み、Runbook(アラート、手動介入手順)を整備。
  5. 負荷試験・ゲームデイ(1週):k6でスパイク/持続、依存先障害(サーキットブレーカ確認)を実施。
  6. カナリアリリース(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適用から始めてください。負荷試験の数字が、導入の確信とチームの共通言語になります。次回のリリースまでに、コスト曲線とレイテンシ曲線を“需要に追従する形”へ整流しましょう。

参考文献

  1. Flexera. 2024 State of the Cloud Report. https://info.flexera.com/resources/ebooks/state-of-the-cloud-2024
  2. 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/
  3. Cloudwards. Elasticity vs Scalability in Cloud Computing. https://www.cloudwards.net/elasticity-vs-scalability/
  4. GeeksforGeeks. Scalability and Elasticity in Cloud Computing. https://www.geeksforgeeks.org/scalability-and-elasticity-in-cloud-computing/
  5. AWS Documentation. Target tracking scaling policies for Amazon EC2 Auto Scaling. https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-scaling-target-tracking.html
  6. Kubernetes Documentation. HorizontalPodAutoscaler 概要. https://kubernetes.io/ja/docs/concepts/workloads/autoscaling/
  7. Google Cloud Documentation. About Cloud Run instance autoscaling. https://cloud.google.com/run/docs/about-instance-autoscaling
  8. SigNoz. APM Metrics and Example SLOs. https://signoz.io/guides/apm-metrics/