私が話をするあらゆるチームが、AIエージェントを作っています。LangChain が動いていて、ベクターデータベースも快調で、CEO を感心させるデモまで用意されています。
すると誰かがこう聞きます: 「いいね。これを 10,000 人のユーザーにどうやって動かすの?」
そのあたりで話が静かになります。
私は過去1年、Kubernetes 上でエージェント型AIシステムをデプロイしてきました――いわゆるノートブックのデモではなく、実際のトラフィックを処理しているものです。そして学んだのは、「自分のノートPCでは動く」と「本番で動く」の間には途方もないギャップがある、ということです。
この記事は、2026年の本番環境でAIエージェントを動かす実際のスタックについてです。LLMの部分ではありません――そこは誰もが扱っています。誰も書かないけれど、誰もが必死に必要としているインフラ層の話をします。
誰も話さない問題
私が少なくとも週に1回はしている、典型的な会話を紹介します:
エンジニア: 「データベースをクエリできる、ドキュメントを検索できる、レポートを下書きできるAIエージェントを作りました。」
私: 「それらのシステムにはどう接続してるんですか?」
エンジニア: 「オーダーメイドのPythonスクリプトです。ツールごとに別々のものを。」
私: 「どうやってデプロイしますか?」
エンジニア: 「1台のVM上でDockerコンテナとして動かしてます。」
私: 「新しいツールを追加する必要が出たらどうします?」
エンジニア: 沈黙
根本的な問題は、多くのAIエージェントのアーキテクチャがだいたいこう見えることです:
LLM ──→ ハードコードされたツールA(カスタムRESTクライアント)
──→ ハードコードされたツールB(カスタムgRPCラッパー)
──→ ハードコードされたツールC(カスタムDBコネクタ)
──→ ハードコードされたツールD(カスタムファイルリーダー)
ツール連携はすべて個別に作り込み。デプロイはすべて脆い。新しい能力を追加するたびに、エージェントの半分を書き換える必要がある。これではスケールしません。
MCPを導入: AIエージェントのためのUSB-Cポート
Model Context Protocol(MCP)は、この方程式を根本から変えます。AIにとってのUSB-Cだと思ってください――モデルとツールをつなぐ、あらゆるものを受け入れる標準化されたインターフェースです。
ツールごとに個別の連携を作り込む代わりに、MCPは次を提供します:
LLM ──→ MCPクライアント ──→ MCPサーバー: データベース
──→ MCPサーバー: 検索
──→ MCPサーバー: Kubernetes
──→ MCPサーバー: Slack
──→ MCPサーバー: (なんでも)
以下は、ツールとしてKubernetesのヘルスチェックを公開する最小のMCPサーバー定義です:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import * as k8s from "@kubernetes/client-node";
const server = new McpServer({
name: "k8s-health",
version: "1.0.0",
});
server.tool(
"check_pod_status",
"名前空間内のPodのヘルス状態を確認します",
{ namespace: { type: "string", description: "Kubernetesの名前空間" } },
async ({ namespace }) => {
const kc = new k8s.KubeConfig();
kc.loadFromDefault();
const k8sApi = kc.makeApiClient(k8s.CoreV1Api);
const res = await k8sApi.listNamespacedPod({ namespace });
const unhealthy = res.items.filter(
(p) => p.status?.phase !== "Running" && p.status?.phase !== "Succeeded"
);
返却形式: {"translated": "翻訳されたHTML"}return {
content: [
{
type: "text",
text: unhealthy.length === 0
? `名前空間 ${namespace} のポッドはすべて正常です。`
: ` ${unhealthy.length} 個の異常なポッドが見つかりました:
` +
unhealthy
.map((p) => `- ${p.metadata?.name}: ${p.status?.phase}`)
.join("
"),
},
],
};
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
エージェントは、Kubernetes がどのように動くかを知る必要はありません。MCP 経由で check_pod_status を呼び出すだけで、サーバーが残りを処理します。
ここからが面白いところです。MCP エコシステムは爆発的に拡大しています。2026年3月時点で、10,000 件以上の公開 MCP サーバー、月間 9,700 万件以上の SDK ダウンロード、そして Claude、ChatGPT、Gemini、VS Code、Cursor への採用が進んでいます。OpenAI、Google、Microsoft もすべてこれをサポートしています。もはや賭けではありません — それはインフラです。
The Part Everyone Skips: Running This on Kubernetes
つまり、MCP を使ってエージェントをツールに接続できています。素晴らしい。ですが、これらの MCP サーバーは実際に どこで動いているのでしょうか? LLM のために GPU リソースをどう管理しますか?トラフィックが急増したらどうなりますか?
ここで Kubernetes が、AI インフラの OS になります。
Architecture: Agent Stack on Kubernetes
こちらが私がデプロイする本番環境のアーキテクチャです。
┌──────────────────────────────────────────────────────┐
│ Kubernetes クラスタ │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ 入口 / Gateway API │ │
│ │ 推論ゲートウェイ · モデルを考慮したルーティング │ │
│ └───────────────────┬──────────────────────────┘ │
│ │ │
│ ┌─────────────┐ ┌───┴──────────┐ ┌──────────────┐ │
│ │ エージェント API │─│MCP サーバー │ │ 主な指標 │ │
│ │ FastAPI + │ │プール │ │ TTFT <500ms │ │
│ │ LangGraph │ │ mcp-postgres │ │ GPU >70% │ │
│ └──────┬──────┘ │ mcp-search │ │ ツール >99.5% │ │
│ │ │ mcp-k8s │ │ コスト -39% │ │
│ │ └──────┬───────┘ └──────────────┘ │
│ ┌──────┴──────┐ ┌──────┴───────┐ │
│ │ LLM サーバー │ │ ベクタDB │ │
│ │ vLLM + DRA │ │ Weaviate │ │
│ │ Llama-70B │ │ ハイブリッド検索 │ │
│ └─────────────┘ └─────────────┘ │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ 観測可能性: OTel → Prometheus → Grafana │ │
│ └──────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────┘
各レイヤーを順に説明します。
レイヤー 1: 動的リソース割り当てによる GPU スケジューリング
AI インフラにおける最大の悩みは GPU 管理です。DRA(Dynamic Resource Allocation)の前は、デバイスプラグインに縛られていました — 陳腐で、オール・オア・ナッシングの GPU 割り当てです。
DRA は Kubernetes 1.34 で GA に到達し、状況が一変しました。推論ワークロード用の ResourceClaim の例を示します:
apiVersion: resource.k8s.io/v1beta2
kind: ResourceClaim
metadata:
name: llm-inference-gpu
namespace: ai-agents
spec:
devices:
requests:
- name: gpu
deviceClassName: gpu.nvidia.com
selectors:
- cel:
expression: >
device.attributes["gpu.nvidia.com"].memory >= 24000 &&
device.attributes["gpu.nvidia.com"].productName.startsWith("A")
constraints:
- requests: ["gpu"]
matchAttribute: "gpu.nvidia.com/numa-node"
これが行うこと:
- CEL ベースのフィルタリング: 24GB 以上の VRAM と A シリーズ(A100/A10G)の GPU を選択します
- トポロジーを考慮: NUMA ノードを一致させることで、GPU が CPU と同じ物理ソケット上にあることを保証します — それだけで推論レイテンシを 15〜20% 削減します
-
宣言的:
nvidia-smiを確認するシェルスクリプトは不要になります
そして、それを使用する Deployment はこちらです:
apiVersion: apps/v1
kind: Deployment
metadata:
name: vllm-server
namespace: ai-agents
spec:
replicas: 2
selector:
matchLabels:
app: vllm-server
template:
metadata:
labels:
app: vllm-server
spec:
containers:
- name: vllm
image: vllm/vllm-openai:v0.7.3
args:
- "--model=meta-llama/Llama-3.3-70B-Instruct"
- "--tensor-parallel-size=1"
- "--max-model-len=8192"
- "--enforce-eager"
ports:
- containerPort: 8000
resources:
claims:
- name: llm-gpu
resourceClaims:
- name: llm-gpu
resourceClaimTemplateName: llm-inference-gpu
導入の早いユーザーは、固定のデバイスプラグインと比べてDRAによりGPU利用率が25%向上すると報告しています。これは単なる僅差ではありません。クラウドのGPU価格を考えると、これは毎月数万ドル規模のコスト削減につながる可能性があります。
レイヤー2:トラフィックルーティングのためのInference Gateway
ユーザーをvLLMインスタンスに直接向けるわけではありません。Inference Gateway(現在GA)は、モデル名、LoRAアダプタ、エンドポイントの健全性に基づいて推論トラフィックをルーティングします:
apiVersion: inference.networking.x-k8s.io/v1alpha2
kind: InferencePool
metadata:
name: llm-pool
namespace: ai-agents
spec:
targetPortNumber: 8000
selector:
matchLabels:
app: vllm-server
---
apiVersion: inference.networking.x-k8s.io/v1alpha2
kind: InferenceModel
metadata:
name: agent-model
namespace: ai-agents
spec:
modelName: "agent-llama-70b"
targetModels:
- name: "meta-llama/Llama-3.3-70B-Instruct"
weight: 100
poolRef:
name: llm-pool
これにより、以下が得られます:
-
モデルに基づくルーティング:
agent-llama-70bへのリクエストは、自動的に適切なプールに送られます - 健全性に基づくフェイルオーバー:不健全なエンドポイントはローテーションから除外されます
- マルチテナント提供:異なるチームが、互いに干渉せず同じGPUプールを共有できます
レイヤー3:Kubernetes DeploymentとしてのMCP Servers
各MCPサーバーは、標準的なKubernetesのDeploymentとして動作します。以下が私が使うパターンです:
apiVersion: apps/v1
kind: Deployment
metadata:
name: mcp-postgres
namespace: ai-agents
labels:
mcp-server: "true"
mcp-tool: "database"
spec:
replicas: 2
selector:
matchLabels:
app: mcp-postgres
template:
metadata:
labels:
app: mcp-postgres
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9090"
spec:
serviceAccountName: mcp-postgres-sa
containers:
- name:
重要な判断:
- MCPサーバーごとにデプロイを分離:障害を隔離します。壊れたSlackのMCPサーバーが、あなたのデータベースのMCPサーバーをダウンさせません。
- エージェントとMCPサーバー間のmTLS:本番では必須です。エージェント ↔ MCP の通信には認証情報と機密データが含まれます。
- 水平スケーリング:外部API(Slack、GitHub)にアクセスするMCPサーバーは、ピーク時間帯にレプリカ数を増やす必要があるかもしれません。
Layer 4: Observability — The AI-Specific Metrics
標準のCPU/メモリ指標だけでは不十分です。AIエージェントのワークロードでは、次が必要です。
# AI指標用のOpenTelemetry Collector設定
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
processors:
batch:
timeout: 5s
exporters:
prometheus:
endpoint: 0.0.0.0:8889
namespace: ai_agent
service:
pipelines:
metrics:
receivers: [otlp]
processors: [batch]
exporters: [prometheus]
重要な指標:
| Metric | Target | Why It Matters |
|---|---|---|
| Time to First Token (TTFT) | < 500ms | ユーザー体感のレスポンシブネス |
| Tokens/second | > 30 tok/s | スループット能力 |
| MCP tool call latency | < 200ms p99 | エージェントのステップ実行速度 |
| GPU utilization | > 70% | コスト効率 |
| Queue depth | < 10 | 容量(キャパシティ)のシグナル |
| Tool call success rate | > 99.5% | 信頼性の指標 |
| Agent task completion rate | > 95% | エンドツーエンドの品質 |
こちらは、何度も助けられたGrafanaダッシュボードのクエリです。
# Alert: Agent tool calls failing above threshold
(
sum(rate(mcp_tool_call_errors_total[5m])) by (tool_name)
/
sum(rate(mcp_tool_call_total[5m])) by (tool_name)
) > 0.05
もしMCPツールのいずれかが、コールの5%超で失敗し始めたら、ユーザーがエージェントの誤った回答に気づく はるか前に、それを知りたいはずです。
What I Got Wrong (So You Don't Have To)
本番デプロイから得た、いくつかの厳しい学び:
1. Don't run MCP servers as sidecars
最初の直感は、エージェントの隣にMCPサーバーをサイドカーコンテナとして動かすことでした。ダメです。PostgresのMCPサーバーにパッチを当てる必要があるとき、エージェントのポッド全体を再起動したくありません。デプロイは分けてください。常に。
2. GPU over-provisioning is worse than under-provisioning
DRAがあると、使える最大のGPUを要求したくなります。でもやめてください。Llama-3.3-8Bモデルで推論を動かすのに、A100 80GBは必要ありません。まずはレイテンシ目標を満たす最小のGPUから始め、データに応じて必要になったときだけスケールアップしてください。
3. Agents need circuit breakers on tool calls
失敗しているMCPツールでエージェントがリトライループに陥ると、驚くほど速くトークン(そしてお金)を消費します。サーキットブレーカーを実装してください。
from circuitbreaker import circuit
返却形式: {"translated": "翻訳されたHTML"}@circuit(failure_threshold=3, recovery_timeout=30)
async def call_mcp_tool(server: str, tool: str, params: dict):
async with mcp_client.connect(server) as client:
result = await client.call_tool(tool, params)
return result
連続する3回の失敗の後、サーキットがオープンし、エージェントはタイムアウトを待つ代わりに即座に「ツールが利用できません」という応答を受け取ります。
4. 可観測性はオプションではない——初日から必要
あるエージェントが意味のない回答を返していた理由を調べるのに、私は6時間費やしたことがあります。原因は何だったのでしょう?ベクターデータベースのMCPサーバーが、2日前にバックグラウンドのインデックス作成ジョブがサイレントに失敗していたため、古い埋め込み(stale embeddings)を返していました。埋め込みの鮮度をきちんと測る指標があれば、数分で見つけられていたはずです。
数字
このスタックが実運用で提供する内容は以下の通りです:
| 指標 | 以前(場当たり) | 以後(このスタック) |
|---|---|---|
| エージェント応答時間(p95) | 12s | 3.2s |
| GPU利用率 | ~45% | ~72% |
| ツール統合時間 | 2〜3週間 | 2〜3日(MCP) |
| インシデント検知 | 数時間 | 数分(OTel) |
| 月間GPUコスト(同一ワークロード) | $18K | $11K |
GPUコストの削減だけでも、最初の1か月でエンジニアリングの工数分は回収できました。
まず始めるには
AIエージェントを運用している(あるいはこれから始める)なら、私のおすすめの手順は次の通りです:
MCPから始める — 既存のツール連携をMCPサーバーとしてラップします。Kubernetes以前であっても、この標準化はすぐに効果を発揮します。
DRAでKubernetesにデプロイする — K8s 1.34+を使っているなら、GPUノードにDRAを有効化してください。トポロジーを意識したスケジューリングだけでも価値があります。
Inference Gatewayを追加する — モデル提供のための独自ロードバランシングを作らないでください。Gateway APIのInference Extensionが、モデル認識型のルーティングをネイティブに扱います。
初日から計測する — OpenTelemetry + Prometheus + Grafana。TTFT、ツール呼び出しレイテンシ、GPU利用率をゴールデンシグナルとして追跡しましょう。
MCPサーバーを独立したデプロイとして実行する — サイドカーではありません。各MCPサーバーには独自のライフサイクル、スケーリング、ヘルスチェックが付与されます。
次に何がある?
これは、生産環境におけるAIインフラに関する連載の第1回です。次回は:
- パート2: DRAによるGPUスケジューリングの深掘り — ベンチマーク、落とし穴、そして高度なパターン
- パート3: LLMワークロードのための可観測性 — 本当に何が悪いかを教えてくれるダッシュボードを作る
役に立ったなら、ハートを押してフォローしてください——私は、十分に注目されないAIインフラ側について書いています。
この記事はAIアシスタンスを用いて調査・執筆しました。ManoIT Tech Blogで、クラウドネイティブなエンジニアリングの洞察をさらに読むことができます。



