メインコンテンツへ移動

Mastra を Kubernetes にデプロイする

Mastra アプリケーションを Kubernetes 上の複数 Pod で実行し、Load Balancer の背後で水平スケーリングします。各 Pod は独立したプロセスであるため、Pub/Sub Backend とデータベースを共有する必要があります。共有しない場合、1 つの Pod で開始した作業を他の Pod から確認できません。

情報

このガイドでは、Mastra サーバーのデプロイについて説明します。Server Adapter または Web フレームワークを使用している場合は、そのフレームワークの通常の方法でデプロイしてください。

警告

複数 Pod のサポートは、現在 BetaDurable Agent に依存します。API は Minor Version で変更される可能性があります。本番環境で使用する前に、既知の制限を確認してください。

始める前に
始める前にへの直接リンク

次のものが必要です。

複数 Pod に共有インフラストラクチャが必要な理由
複数 Pod に共有インフラストラクチャが必要な理由への直接リンク

単一の Pod は Run の状態を自身のメモリに保持します。Pod が 1 つであれば、すべてのリクエストが同じプロセスに届くため問題ありません。複数 Pod では、ブラウザーが Pod A からストリーミングしている間に、ユーザーの次のリクエストが Pod B にルーティングされる可能性があります。Pod B には Pod A の Run に関する記録がありません。

Redis と Postgres がこの問題を解決します。

  • Pub/Sub は Pod 間でイベントを転送します。1 つの Pod でイベントを Publish すると、他の Pod が受信します。Mastra は RedisStreamsPubSub を使用します。これは、同時に 1 つの Pod だけが会話を所有するようにする Thread ごとの Lease も提供します。PubSub を参照してください。
  • ストレージ は Run の状態を永続化します。Durable Agent は各 Run を Workflow Snapshot として保存するため、再起動後や別の場所にルーティングされた場合でも、どの Pod からでもデータベースから Run を再開できます。

共有インフラストラクチャを設定する
共有インフラストラクチャを設定するへの直接リンク

Mastra インスタンスを Redis と Postgres に接続します。すべての Pod で同じイメージを実行できるように、接続情報を環境変数から読み取ります。

Backend をインストールします。

npm install @mastra/redis-streams @mastra/pg @mastra/redis ioredis

Mastra インスタンスに Pub/Sub、ストレージ、共有 Cache を設定します。

src/mastra/index.ts
import { Mastra } from '@mastra/core'
import { RedisStreamsPubSub } from '@mastra/redis-streams'
import { RedisServerCache } from '@mastra/redis'
import { PostgresStore } from '@mastra/pg'
import Redis from 'ioredis'

export const mastra = new Mastra({
// Carries events between pods, and provides
// per-thread leases so one pod owns a conversation at a time.
pubsub: new RedisStreamsPubSub({
url: process.env.REDIS_URL!,
}),
// Persists run state so any pod can resume a run.
storage: new PostgresStore({
id: 'mastra-storage',
connectionString: process.env.DATABASE_URL!,
}),
// Shared event cache so a reconnecting client can replay missed chunks
// from any pod, not only the one that started the run.
cache: new RedisServerCache({ client: new Redis(process.env.REDIS_URL!) }),
})

cache により、再開可能なストリームが Pod 間で動作します。再接続する Client は、この Cache から受信できなかったイベントを再生するため、Cache を共有する必要があります。デフォルトの In-memory Cache は、1 つのプロセス内でのみ再生に対応します。

Durable Agent を使用する
Durable Agent を使用するへの直接リンク

通常の Agent はストリームと承認状態を 1 つの Pod のメモリに保持するため、リクエストが別の Pod に届くと維持できません。Durable Agent は Agent Loop を Workflow 内で実行して状態を永続化するため、どの Pod からでも同じ Run を確認または再開できます。

Agent を createDurableAgent() でラップします。

src/mastra/agents/assistant.ts
import { Agent } from '@mastra/core/agent'
import { createDurableAgent } from '@mastra/core/agent/durable'

const agent = new Agent({
id: 'assistant',
name: 'Assistant',
instructions: 'You are a helpful assistant.',
model: 'openai/gpt-5.6-sol',
})

export const durableAssistant = createDurableAgent({ agent })

上記の Mastra インスタンスに Durable Agent を登録します。Run の状態が Postgres に保存され、イベントが Redis を通過するため、すべての Pod から Run にアクセスできるようになります。

デプロイする
デプロイするへの直接リンク

  1. Mastra サーバーをビルドしてコンテナ化し、イメージを Registry にプッシュします。ビルドについては Mastra サーバーガイドに従い、サーバーが process.env.PORT を読み取り、0.0.0.0 で Listen することを確認してください。

  2. 共有接続文字列を Secret として保存します。

    kubectl create secret generic mastra-secrets \
    --from-literal=REDIS_URL='redis://redis:6379' \
    --from-literal=DATABASE_URL='postgresql://user:pass@postgres:5432/mastra'
  3. 共有 Secret を読み取ってイメージを実行する Deployment を適用します。最初の Pod が単独でデータベース Schema を作成できるように、まず 1 つの Replica で開始し、次の Step でスケールアップします。

    deployment.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: mastra
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: mastra
    template:
    metadata:
    labels:
    app: mastra
    spec:
    containers:
    - name: mastra
    image: your-registry/mastra:latest
    ports:
    - containerPort: 8080
    env:
    - name: PORT
    value: '8080'
    envFrom:
    - secretRef:
    name: mastra-secrets
    readinessProbe:
    tcpSocket:
    port: 8080
    livenessProbe:
    tcpSocket:
    port: 8080
    resources:
    requests:
    cpu: 500m
    memory: 512Mi

    後述の HorizontalPodAutoscaler では、resources.requests.cpu の値が必要です。Kubernetes は使用量を要求量で割って CPU 使用率を計算するため、CPU Request がないと Autoscaler は目標値を計算できず、スケーリングしません。

    kubectl apply -f deployment.yaml

    最初の Pod の準備ができたら、スケールアップします。

    kubectl wait --for=condition=available deployment/mastra
    kubectl scale deployment/mastra --replicas=3
    注記

    すべての Replica が同じイメージを実行し、同じ Redis と Postgres に接続します。Run が Pod をまたげるのは、Pod 数ではなくこの共有インフラストラクチャによるものです。

  4. Deployment を Service として公開します。

    service.yaml
    apiVersion: v1
    kind: Service
    metadata:
    name: mastra
    spec:
    selector:
    app: mastra
    ports:
    - port: 80
    targetPort: 8080
    kubectl apply -f service.yaml
  5. HorizontalPodAutoscaler で Replica を自動的にスケーリングします。

    hpa.yaml
    apiVersion: autoscaling/v2
    kind: HorizontalPodAutoscaler
    metadata:
    name: mastra
    spec:
    scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: mastra
    minReplicas: 3
    maxReplicas: 10
    metrics:
    - type: Resource
    resource:
    name: cpu
    target:
    type: Utilization
    averageUtilization: 70
    kubectl apply -f hpa.yaml
    注記

    CPU ベースの自動スケーリングには、Cluster で metrics-server を実行する必要があります。GKE、EKS、AKS などのマネージド Cluster には含まれています。kind や minikube などのローカル Cluster には含まれないため、先に有効にしてください(minikube addons enable metrics-server など)。

  6. Pod が実行されていることを確認します。

    kubectl get pods -l app=mastra

    1 つのターミナルで Service を Port Forward します。このコマンドはフォアグラウンドで実行され続けます。

    kubectl port-forward service/mastra 8080:80

    2 つ目のターミナルから API を呼び出します。

    curl http://localhost:8080/api/agents

    Agent の一覧が JSON で返れば、デプロイから配信されています。

    警告

    エンドポイントを公開する前に、認証を設定してください。

ストリーミングと再接続
ストリーミングと再接続への直接リンク

Durable Agent は共有 Pub/Sub を介して、Run ごとの Topic にストリームチャンクを Publish します。切断した Client は Run ID とともに observe() を呼び出して再接続し、共有 Cache から受信できなかったチャンクを再生します。

src/server/reconnect.ts
const { output, cleanup } = await durableAssistant.observe(runId)

for await (const chunk of output.fullStream) {
// Chunks from the run, including any missed while disconnected
}

cleanup()

Run の状態が Postgres、イベントが Redis にあるため、再接続リクエストは Run を開始した Pod だけでなく、どの Pod でも処理できます。再開可能なストリームを参照してください。

複数の Client が同じ Run を同時に確認できます。各 observe() 呼び出しは完全なストリームを受信するため、1 人のユーザーが 2 台のデバイスから確認する場合も、2 人が同じ Run を追跡する場合も同期を維持できます。

Pod をまたぐ Tool 承認
Pod をまたぐ Tool 承認への直接リンク

Durable Agent は人間が承認するまで Tool 呼び出しを一時停止します。中断された Run は Postgres に保存されるため、Run を開始した Pod だけでなく、どの Pod でも承認を受け取れます。

承認が必要な Run を開始します。

src/server/approval.ts
const { runId } = await durableAssistant.stream('Delete the archived records', {
requireToolApproval: true,
memory: { thread: 'thread-1', resource: 'user-1' },
})

Run は Tool の実行前に中断されます。後から任意の Pod で承認します。

await durableAssistant.resume(runId, { approved: true })

承認を処理する Pod が中断された Run を Postgres から読み込みます。その後、承認された Tool を実行して共有 Pub/Sub 経由で結果を Publish するため、Run を確認している Client が続きを受信します。Tool 承認を参照してください。

既知の制限
既知の制限への直接リンク

  • デフォルトの In-process 設定では、Run の状態を 1 つの Pod のメモリに保持し、Pod 間で共有しません。ストリーミング、承認、再接続を Pod 間で動作させるには、共有 Redis と Postgres を持つ Durable Agent を使用してください。
  • Pod をまたぐストリーミング、承認、再接続には Durable Agent が必要です。通常の Agent は Run の状態をメモリに保持し、別の Pod では再開しません。
  • 初期化されていないデータベースに対して複数の Pod を同時に起動すると、Schema 作成が競合して Pod の起動に失敗することがあります。まず 1 つの Replica で Schema を一度だけ作成し、その後スケールアップしてください。
  • より厳密な構成では、アプリ外部で Schema を初期化し(1 回限りの Kubernetes Job など)、すべての Pod の PostgresStoredisableInit: true を設定します。