mcpField
L
llm-inference-scaling

llm-inference-scaling

github.com/bagelholeクラウド・DevOps1.2k345 回インストール23 週間前に更新
リポジトリ

llm-inference-scaling について

GPU を使ったモデルサービスを運用するチーム向けの、Kubernetes 上の LLM 推論スケーリングスキルです。メトリクスやキューによる自動スケーリング、GPU ノード管理、スポットインスタンス戦略を扱います。

主な機能

  1. 01Kubernetes 上の GPU 推論デプロイ
  2. 02Prometheus メトリクスによる自動スケーリング
  3. 03キュー駆動のバッチ処理スケーリング
  4. 04GPU ノードの自動スケーリング
  5. 05スポットインスタンス戦略と中断対策

活用シーン

  1. 01変動する LLM API トラフィックへの対応
  2. 02vLLM または TGI 推論 Pod の拡張
  3. 03キューを使ったバッチ推論処理
  4. 04GPU 推論インフラコストの削減

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/bagelhole/devops-security-agent-skills --skill llm-inference-scaling

インストール後、Claude Code で/llm-inference-scalingを入力して読み込まれたか確認します。

詳細

LLM 推論のスケーリング

GPU を考慮したオートスケーリング、リクエストキューイング、コスト効率に優れたスポットインスタンス戦略により、Kubernetes 上で LLM 推論を水平スケーリングします。

このスキルを使用する場面

次の場合にこのスキルを使用します。

  • LLM API のトラフィックが予測できず、自動的なスケールアップ/ダウンが必要な場合
  • Kubernetes 上の vLLM または TGI 推論 Pod のフリートを管理する場合
  • スポット/プリエンプティブル GPU インスタンスで推論コストを削減する場合
  • バッチ推論ジョブ向けにキューベースのオートスケーリングを実装する場合
  • GPU リソースを共有するマルチモデルサービングプラットフォームを構築する場合

前提条件

  • GPU ノードを備えた Kubernetes クラスター(NVIDIA operator がインストール済み)
  • KEDA(Kubernetes Event-Driven Autoscaler)がインストール済み
  • GPU メトリクス(dcgm-exporter または gpu-operator)を備えた Prometheus
  • チャートのデプロイ用の Helm 3+

GPU ノードのセットアップ

# Install NVIDIA GPU Operator (handles drivers, container toolkit, DCGM)
helm repo add nvidia https://helm.ngc.nvidia.com/nvidia
helm repo update

helm install gpu-operator nvidia/gpu-operator \
  --namespace gpu-operator \
  --create-namespace \
  --set driver.enabled=true \
  --set dcgm.enabled=true \
  --set devicePlugin.enabled=true

# Verify GPU nodes are recognized
kubectl get nodes -l nvidia.com/gpu.present=true
kubectl describe node <gpu-node> | grep nvidia

GPU リソースを使用した vLLM のデプロイ

apiVersion: apps/v1
kind: Deployment
metadata:
  name: vllm-llama-8b
  labels:
    app: vllm
    model: llama-3.1-8b
spec:
  replicas: 1
  selector:
    matchLabels:
      app: vllm
      model: llama-3.1-8b
  template:
    metadata:
      labels:
        app: vllm
        model: llama-3.1-8b
    spec:
      nodeSelector:
        nvidia.com/gpu.present: "true"
      tolerations:
      - key: nvidia.com/gpu
        operator: Exists
        effect: NoSchedule
      containers:
      - name: vllm
        image: vllm/vllm-openai:latest
        args:
        - "--model"
        - "meta-llama/Llama-3.1-8B-Instruct"
        - "--tensor-parallel-size"
        - "1"
        - "--gpu-memory-utilization"
        - "0.90"
        - "--max-num-seqs"
        - "128"
        resources:
          requests:
            nvidia.com/gpu: "1"
            memory: "20Gi"
            cpu: "4"
          limits:
            nvidia.com/gpu: "1"
            memory: "24Gi"
            cpu: "8"
        ports:
        - containerPort: 8000
        readinessProbe:
          httpGet:
            path: /health
            port: 8000
          initialDelaySeconds: 60
          periodSeconds: 10
        env:
        - name: HUGGING_FACE_HUB_TOKEN
          valueFrom:
            secretKeyRef:
              name: hf-token
              key: token

Prometheus メトリクスに基づく KEDA オートスケーリング

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: vllm-scaledobject
spec:
  scaleTargetRef:
    name: vllm-llama-8b
  minReplicaCount: 1
  maxReplicaCount: 8
  cooldownPeriod: 300          # 5 min before scale-down
  pollingInterval: 15
  triggers:
  - type: prometheus
    metadata:
      serverAddress: http://prometheus-server.monitoring:9090
      metricName: vllm_num_requests_waiting
      threshold: "10"           # scale up if >10 requests waiting
      query: |
        sum(vllm:num_requests_waiting{deployment="vllm-llama-8b"})
  - type: prometheus
    metadata:
      serverAddress: http://prometheus-server.monitoring:9090
      metricName: vllm_gpu_cache_usage
      threshold: "0.8"          # scale up if KV cache >80% full
      query: |
        avg(vllm:gpu_cache_usage_perc{deployment="vllm-llama-8b"})

キューベースのスケーリング(Redis + KEDA)

# ScaledJob for async batch inference
apiVersion: keda.sh/v1alpha1
kind: ScaledJob
metadata:
  name: llm-batch-inference
spec:
  jobTargetRef:
    template:
      spec:
        containers:
        - name: inference-worker
          image: myapp/inference-worker:latest
          env:
          - name: REDIS_URL
            value: redis://redis:6379
          - name: QUEUE_NAME
            value: inference-jobs
        restartPolicy: OnFailure
  minReplicaCount: 0
  maxReplicaCount: 20
  pollingInterval: 5
  successfulJobsHistoryLimit: 3
  triggers:
  - type: redis
    metadata:
      address: redis:6379
      listName: inference-jobs
      listLength: "5"           # 1 worker per 5 queued jobs

スポットインスタンス戦略

# Mixed node pool: on-demand + spot GPUs
apiVersion: v1
kind: ConfigMap
metadata:
  name: cluster-autoscaler-priority-config
data:
  priorities: |
    10:  # low priority = prefer
    - .*spot.*
    50:
    - .*on-demand.*
---
# Node affinity for spot with on-demand fallback
spec:
  affinity:
    nodeAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 80
        preference:
          matchExpressions:
          - key: node.kubernetes.io/lifecycle
            operator: In
            values: [spot]
      - weight: 20
        preference:
          matchExpressions:
          - key: node.kubernetes.io/lifecycle
            operator: In
            values: [on-demand]

GPU ノード向けクラスターオートスケーラー

# AWS EKS — enable cluster autoscaler for GPU node group
helm install cluster-autoscaler autoscaler/cluster-autoscaler \
  --namespace kube-system \
  --set autoDiscovery.clusterName=my-cluster \
  --set awsRegion=us-east-1 \
  --set rbac.serviceAccount.annotations."eks\.amazonaws\.com/role-arn"=arn:aws:iam::ACCOUNT:role/ClusterAutoscalerRole \
  --set extraArgs.skip-nodes-with-local-storage=false \
  --set extraArgs.expander=least-waste

# Annotate GPU node group for autoscaler
kubectl annotate node <node> \
  cluster-autoscaler.kubernetes.io/safe-to-evict="false"

監視するスケーリングメトリクス

# Prometheus queries for scaling decisions
# Requests waiting in vLLM queue
sum(vllm:num_requests_waiting) by (model)

# GPU KV cache utilization (>80% = bottleneck)
avg(vllm:gpu_cache_usage_perc) by (pod)

# Tokens per second throughput
sum(rate(vllm:generation_tokens_total[5m])) by (model)

# P99 time-to-first-token
histogram_quantile(0.99, rate(vllm:time_to_first_token_seconds_bucket[5m]))

よくある問題

問題原因対処方法
Pod が Pending のままになる利用可能な GPU ノードがないクラスターオートスケーラーのログを確認し、ノードグループの上限を検証します
スケールアップが遅すぎるクラスターオートスケーラーの遅延とモデルの読み込み時間レプリカを事前にウォームアップし、minReplicaCount を増やします
GPU の断片化大容量 GPU 上で複数の小規模モデルを実行しているMIG パーティショニングを使用するか、モデルサイズを統合します
スポットインスタンスの退避によるエラースポットインスタンスの回収PodDisruptionBudget を追加し、グレースフルシャットダウンを使用します
KEDA がスケーリングしないPrometheus クエリがデータを返さないまず Prometheus UI でクエリをテストします

ベストプラクティス

  • コールドスタートを避けるために minReplicaCount: 1 を設定します。0 へのスケールはバッチジョブに限ります。
  • スポットインスタンスの退避に備え、minAvailable: 1 を指定した PodDisruptionBudget を使用します。
  • モデルの重みを共有 PVC に事前にプルし、Pod の起動を 5~10 倍高速化します。
  • ノードプールごとにモデルファミリーを分けます(7B には A10G、70B には A100)。
  • KEDA でレプリカ数を管理しながら、CPU/メモリの適正化には Kubernetes VPA を使用します。

関連スキル

コメント