Aller au contenu principal

Déployer Mastra sur Kubernetes

Exécutez une application Mastra sur plusieurs pods Kubernetes afin qu'elle puisse évoluer horizontalement derrière un équilibreur de charge. Chaque pod constituant un processus distinct, les pods doivent partager un service pub/sub et une base de données ; sans cela, les opérations démarrées sur un pod restent invisibles aux autres.

info

Ce guide explique comment déployer le serveur Mastra. Si vous utilisez un adaptateur de serveur ou un framework web, suivez votre procédure habituelle de déploiement pour celui-ci.

attention

La prise en charge de plusieurs pods repose sur les agents durables, actuellement en bêta. Les API peuvent évoluer dans les versions mineures. Consultez les limitations connues avant d'utiliser cette configuration en production.

Avant de commencer
Lien direct vers Avant de commencer

Vous aurez besoin des éléments suivants :

  • Une application Mastra
  • Un cluster Kubernetes et kubectl
  • Un registre de conteneurs auquel votre cluster peut accéder pour télécharger des images
  • Une instance Redis partagée, accessible depuis chaque pod
  • Une base de données PostgreSQL partagée, accessible depuis chaque pod

Pourquoi plusieurs pods nécessitent une infrastructure partagée
Lien direct vers Pourquoi plusieurs pods nécessitent une infrastructure partagée

Un pod unique conserve l'état d'exécution dans sa propre mémoire. Cela fonctionne avec un seul pod, car chaque requête atteint le même processus. Avec plusieurs pods, ce fonctionnement n'est plus assuré : un navigateur peut recevoir un flux depuis le pod A, tandis que la requête suivante de l'utilisateur est acheminée vers le pod B, qui ne possède aucune trace de l'exécution sur le pod A.

Redis et Postgres assurent la continuité entre les pods :

  • Le système pub/sub transmet les événements entre les pods. Lorsqu'un événement est publié sur un pod, les autres le reçoivent. Mastra utilise RedisStreamsPubSub, qui fournit également un mécanisme de bail par fil de discussion garantissant qu'un seul pod est propriétaire d'une conversation à la fois. Consultez la page PubSub.
  • Le stockage conserve l'état d'exécution. Les agents durables enregistrent chaque exécution sous forme d'instantané de Workflow, ce qui permet à n'importe quel pod de reprendre une exécution depuis la base de données après un redémarrage ou lorsqu'une requête est acheminée ailleurs.

Configurer l'infrastructure partagée
Lien direct vers Configurer l'infrastructure partagée

Configurez l'instance Mastra pour utiliser Redis et Postgres. Lisez les informations de connexion depuis des variables d'environnement afin que la même image puisse s'exécuter dans chaque pod.

Installez les services requis :

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

Configurez le système pub/sub, le stockage et un cache partagé sur l'instance Mastra :

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!) }),
})

Le cache permet aux flux pouvant être repris de fonctionner entre plusieurs pods. Lorsqu'un client se reconnecte, il récupère dans ce cache les événements manqués ; celui-ci doit donc être partagé. Le cache en mémoire utilisé par défaut ne permet de récupérer les événements qu'au sein d'un même processus.

Utiliser des agents durables
Lien direct vers Utiliser des agents durables

Un Agent standard conserve son flux et son état d'approbation dans la mémoire d'un seul pod. Ces données ne sont donc plus disponibles si une requête arrive sur un autre pod. Un agent durable exécute la boucle agentique au sein d'un Workflow et conserve son état, ce qui permet à n'importe quel pod d'observer ou de reprendre la même exécution.

Encapsulez l'agent avec 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 })

Enregistrez l'agent durable auprès de l'instance Mastra ci-dessus. Son état d'exécution est désormais stocké dans Postgres et ses événements transitent par Redis ; l'exécution est donc accessible depuis chaque pod.

Déployer
Lien direct vers Déployer

  1. Compilez et conteneurisez le serveur Mastra, puis envoyez l'image vers votre registre. Suivez le guide du serveur Mastra pour la compilation et vérifiez que le serveur lit process.env.PORT et écoute sur 0.0.0.0.

  2. Stockez les chaînes de connexion partagées dans un 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. Appliquez un Deployment qui exécute l'image en lisant le Secret partagé. Commencez avec un seul réplicat afin que le premier pod crée lui-même le schéma de la base de données, puis augmentez le nombre de réplicats à l'étape suivante :

    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

    La valeur resources.requests.cpu est requise pour le HorizontalPodAutoscaler ci-dessous. Kubernetes calcule l'utilisation du processeur en divisant la consommation par la quantité demandée. Sans demande de ressources CPU, le mécanisme de mise à l'échelle automatique ne peut pas calculer de cible et n'ajuste pas le nombre de réplicats.

    kubectl apply -f deployment.yaml

    Une fois le premier pod prêt, augmentez le nombre de réplicats :

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

    Chaque réplicat exécute la même image et se connecte aux mêmes instances Redis et Postgres. C'est cette infrastructure partagée, et non le nombre de pods, qui permet aux exécutions de passer d'un pod à l'autre.

  4. Exposez le Deployment à l'aide d'un 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. Ajustez automatiquement le nombre de réplicats à l'aide d'un HorizontalPodAutoscaler :

    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
    remarque

    La mise à l'échelle automatique fondée sur l'utilisation du processeur nécessite que le metrics-server s'exécute dans le cluster. Les clusters gérés tels que GKE, EKS et AKS l'incluent. Ce n'est pas le cas des clusters locaux comme kind et minikube : vous devez donc d'abord l'y activer (par exemple avec minikube addons enable metrics-server).

  6. Vérifiez que les pods sont en cours d'exécution :

    kubectl get pods -l app=mastra

    Redirigez le port du service dans un terminal. Cette commande reste exécutée au premier plan :

    kubectl port-forward service/mastra 8080:80

    Dans un second terminal, appelez l'API :

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

    Si vous obtenez une liste JSON de vos agents, le déploiement traite correctement les requêtes.

    attention

    Configurez l'authentification avant d'exposer publiquement vos points de terminaison.

Diffusion en continu et reconnexion
Lien direct vers Diffusion en continu et reconnexion

Un agent durable publie les fragments du flux dans un canal propre à chaque exécution par l'intermédiaire du système pub/sub partagé. Après une déconnexion, un client se reconnecte en appelant observe() avec l'ID de l'exécution, puis récupère dans le cache partagé les fragments qu'il a manqués :

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()

Comme l'état d'exécution se trouve dans Postgres et les événements dans Redis, la requête de reconnexion peut être traitée par n'importe quel pod, et pas seulement par celui qui a démarré l'exécution. Consultez la section Flux pouvant être repris.

Plusieurs clients peuvent observer simultanément la même exécution. Chaque appel à observe() reçoit l'intégralité du flux : un utilisateur qui le consulte depuis deux appareils, ou deux personnes qui suivent la même exécution, restent donc synchronisés.

Approbation des Tools entre les pods
Lien direct vers Approbation des Tools entre les pods

Un agent durable se met en pause lors de l'appel d'un Tool jusqu'à ce qu'une personne l'approuve. L'exécution suspendue étant enregistrée dans Postgres, l'approbation peut arriver sur n'importe quel pod, et pas seulement sur celui qui a démarré l'exécution.

Démarrez une exécution qui nécessite une approbation :

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

L'exécution est suspendue avant le lancement du Tool. Approuvez-la ensuite depuis n'importe quel pod :

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

Le pod qui traite l'approbation charge l'exécution suspendue depuis Postgres. Il exécute ensuite le Tool approuvé et publie le résultat par l'intermédiaire du système pub/sub partagé, afin qu'un client qui observe l'exécution en reçoive la suite. Consultez la section Approbation des Tools.

Limitations connues
Lien direct vers Limitations connues

  • La configuration en processus utilisée par défaut conserve l'état d'exécution dans la mémoire d'un seul pod et ne le partage pas entre les pods. Utilisez des agents durables avec des instances Redis et Postgres partagées afin que la diffusion en continu, les approbations et la reconnexion fonctionnent entre les pods.
  • La diffusion en continu, les approbations et la reconnexion entre les pods nécessitent le recours aux agents durables. Un agent standard conserve l'état d'exécution en mémoire et ne peut pas reprendre sur un autre pod.
  • Lorsque plusieurs pods démarrent simultanément avec une base de données non initialisée, ils peuvent tenter de créer le schéma en même temps et l'un d'eux risque de ne pas démarrer. Commencez avec un seul réplicat afin que le schéma ne soit créé qu'une fois, puis augmentez le nombre de réplicats.
  • Pour une configuration plus stricte, initialisez le schéma en dehors de l'application (par exemple à l'aide d'un Job Kubernetes ponctuel) et définissez disableInit: true sur le PostgresStore de chaque pod.