Skip to main content

Skalierung und Mehrinstanzenfähigkeit

Entwerfen Sie Ihre Copilot SDK-Bereitstellung so, dass sie mehrere Benutzer bedient, gleichzeitige Sitzungen verarbeitet und horizontal in der gesamten Infrastruktur skaliert wird. In diesem Leitfaden werden Sitzungsisolationsmuster, Skalierungstopologien und bewährte Methoden für die Produktion behandelt.

Am besten geeignet für: Plattformentwickler, SaaS-Generatoren, jede Bereitstellung, die mehr als eine Handvoll gleichzeitiger Benutzer bedient.

Wichtige Konzepte

Bevor Sie ein Muster auswählen, sollten Sie drei Dimensionen der Skalierung verstehen:

Diagramm: Flussdiagramm mit dem beschriebenen Prozess.

Sitzungsisolationsmuster

Muster 1: isolierte CLI pro Benutzer

Jeder Benutzer erhält eine eigene CLI-Serverinstanz. Stärkste Isolation – Sitzungen, Arbeitsspeicher und Prozesse eines Benutzers werden vollständig getrennt.

Diagramm: Flussdiagramm mit dem beschriebenen Prozess.

Wann verwenden:

  • SaaS mit mehreren Mandanten, bei denen die Datenisolation kritisch ist
  • Benutzer mit unterschiedlichen Authentifizierungsanmeldeinformationen
  • Complianceanforderungen (SOC 2, HIPAA)
// CLI pool manager — one CLI per user
class CLIPool {
    private instances = new Map<string, { client: CopilotClient; port: number }>();
    private nextPort = 5000;

    async getClientForUser(userId: string, token?: string): Promise<CopilotClient> {
        if (this.instances.has(userId)) {
            return this.instances.get(userId)!.client;
        }

        const port = this.nextPort++;

        // Spawn a dedicated CLI for this user
        await spawnCLI(port, token);

        const client = new CopilotClient({
            cliUrl: `localhost:${port}`,
        });

        this.instances.set(userId, { client, port });
        return client;
    }

    async releaseUser(userId: string): Promise<void> {
        const instance = this.instances.get(userId);
        if (instance) {
            await instance.client.stop();
            this.instances.delete(userId);
        }
    }
}

Muster 2: gemeinsam genutzte CLI mit Sitzungsisolation

Mehrere Benutzer teilen einen CLI-Server, weisen jedoch isolierte Sitzungen über eindeutige Sitzungs-IDs auf. Ressourcenschonender, aber mit schwächerer Isolation.

Diagramm: Flussdiagramm mit dem beschriebenen Prozess.

Wann verwenden:

  • Interne Tools mit vertrauenswürdigen Benutzern
  • Ressourcenbeschränkte Umgebungen
  • Niedrigere Isolationsanforderungen
const sharedClient = new CopilotClient({
    cliUrl: "localhost:4321",
});

// Enforce session isolation through naming conventions
function getSessionId(userId: string, purpose: string): string {
    return `${userId}-${purpose}-${Date.now()}`;
}

// Access control: ensure users can only access their own sessions
async function resumeSessionWithAuth(
    sessionId: string,
    currentUserId: string
): Promise<Session> {
    const [sessionUserId] = sessionId.split("-");
    if (sessionUserId !== currentUserId) {
        throw new Error("Access denied: session belongs to another user");
    }
    return sharedClient.resumeSession(sessionId);
}

Muster 3: freigegebene Sitzungen (zusammenarbeiten)

Mehrere Benutzer interagieren mit derselben Sitzung, z. B. einem gemeinsam genutzten Chatraum mit Copilot.

Diagramm: Flussdiagramm mit dem beschriebenen Prozess.

Wann verwenden:

  • Tools für die Teamzusammenarbeit
  • Gemeinsame Code-Review-Sitzungen
  • Pair-Programming-Assistenten

⚠️Wichtig: Das SDK bietet keine integrierte Sitzungssperre. Sie müssen den Zugriff serialisieren, um gleichzeitige Schreibvorgänge in derselben Sitzung zu verhindern.

import Redis from "ioredis";

const redis = new Redis();

async function withSessionLock<T>(
    sessionId: string,
    fn: () => Promise<T>,
    timeoutSec = 300
): Promise<T> {
    const lockKey = `session-lock:${sessionId}`;
    const lockId = crypto.randomUUID();

    // Acquire lock
    const acquired = await redis.set(lockKey, lockId, "NX", "EX", timeoutSec);
    if (!acquired) {
        throw new Error("Session is in use by another user");
    }

    try {
        return await fn();
    } finally {
        // Release lock (only if we still own it)
        const currentLock = await redis.get(lockKey);
        if (currentLock === lockId) {
            await redis.del(lockKey);
        }
    }
}

// Usage: serialize access to shared session
app.post("/team-chat", authMiddleware, async (req, res) => {
    const result = await withSessionLock("team-project-review", async () => {
        const session = await client.resumeSession("team-project-review");
        return session.sendAndWait({ prompt: req.body.message });
    });

    res.json({ content: result?.data.content });
});

Vergleich von Isolationsmustern

Isolierte CLI pro BenutzerShared CLI + SitzungsisolationGeteilte Sitzungen
Isolation
✅ Vollständig
⚠️ Logisch
❌ Geteilt
RessourcennutzungHoch (CLI pro Benutzer)Niedrig (eine CLI)Niedrig (eine CLI + Sitzung)
KomplexitätMediumNiedrigHoch (Verriegelung)
Flexibilität bei der Authentifizierung
✅ Benutzerspezifische Token
⚠️ Diensttoken
⚠️ Diensttoken
Am besten geeignet fürMehrinstanzenfähige SaaSInterne WerkzeugeZusammenarbeit

Horizontale Skalierung

Mehrere CLI-Server hinter einem Lastverteiler

Diagramm: Flussdiagramm mit dem beschriebenen Prozess.

Schlüsselanforderung: Der Sitzungszustand muss sich im freigegebenen Speicher befinden, damit jeder CLI-Server jede Sitzung fortsetzen kann.

// Route sessions to CLI servers
class CLILoadBalancer {
    private servers: string[];
    private currentIndex = 0;

    constructor(servers: string[]) {
        this.servers = servers;
    }

    // Round-robin selection
    getNextServer(): string {
        const server = this.servers[this.currentIndex];
        this.currentIndex = (this.currentIndex + 1) % this.servers.length;
        return server;
    }

    // Sticky sessions: same user always hits same server
    getServerForUser(userId: string): string {
        const hash = this.hashCode(userId);
        return this.servers[hash % this.servers.length];
    }

    private hashCode(str: string): number {
        let hash = 0;
        for (let i = 0; i < str.length; i++) {
            hash = (hash << 5) - hash + str.charCodeAt(i);
            hash |= 0;
        }
        return Math.abs(hash);
    }
}

const lb = new CLILoadBalancer([
    "cli-1:4321",
    "cli-2:4321",
    "cli-3:4321",
]);

app.post("/chat", async (req, res) => {
    const server = lb.getServerForUser(req.user.id);
    const client = new CopilotClient({ cliUrl: server });

    const session = await client.createSession({
        sessionId: `user-${req.user.id}-chat`,
        model: "gpt-4.1",
    });

    const response = await session.sendAndWait({ prompt: req.body.message });
    res.json({ content: response?.data.content });
});

Persistente Sitzungen im Vergleich zu freigegebenem Speicher

Diagramm: Flussdiagramm mit dem beschriebenen Prozess.

Sticky Sessions sind einfacher – Benutzer an bestimmte CLI-Server binden. Kein gemeinsam genutzter Speicher erforderlich, aber die Auslastungsverteilung ist ungleich.

Der freigegebene Speicher ermöglicht es jeder CLI, jede Sitzung zu verarbeiten. Bessere Lastverteilung, erfordert aber Netzwerkspeicher für ~/.copilot/session-state/.

Vertikale Skalierung

Optimieren eines einzelnen CLI-Servers

Ein einzelner CLI-Server kann viele gleichzeitige Sitzungen verarbeiten. Wichtige Überlegungen:

Diagramm: Flussdiagramm mit dem beschriebenen Prozess.

Die Verwaltung des Sitzungslebenszyklus ist der Schlüssel zur vertikalen Skalierung:

// Limit concurrent active sessions
class SessionManager {
    private activeSessions = new Map<string, Session>();
    private maxConcurrent: number;

    constructor(maxConcurrent = 50) {
        this.maxConcurrent = maxConcurrent;
    }

    async getSession(sessionId: string): Promise<Session> {
        // Return existing active session
        if (this.activeSessions.has(sessionId)) {
            return this.activeSessions.get(sessionId)!;
        }

        // Enforce concurrency limit
        if (this.activeSessions.size >= this.maxConcurrent) {
            await this.evictOldestSession();
        }

        // Create or resume
        const session = await client.createSession({
            sessionId,
            model: "gpt-4.1",
        });

        this.activeSessions.set(sessionId, session);
        return session;
    }

    private async evictOldestSession(): Promise<void> {
        const [oldestId] = this.activeSessions.keys();
        const session = this.activeSessions.get(oldestId)!;
        // Session state is persisted automatically — safe to disconnect
        await session.disconnect();
        this.activeSessions.delete(oldestId);
    }
}

Ephemerale vs. persistente Sitzungen

Diagramm: Flussdiagramm mit dem beschriebenen Prozess.

Kurzlebige Sitzungen

Für zustandslose API-Endpunkte, bei denen jede Anforderung unabhängig ist:

app.post("/api/analyze", async (req, res) => {
    const session = await client.createSession({
        model: "gpt-4.1",
    });

    try {
        const response = await session.sendAndWait({
            prompt: req.body.prompt,
        });
        res.json({ result: response?.data.content });
    } finally {
        await session.disconnect();  // Clean up immediately
    }
});

Persistente Sitzungen

Für konversationsbasierte Schnittstellen oder lang laufende Workflows:

// Create a resumable session
app.post("/api/chat/start", async (req, res) => {
    const sessionId = `user-${req.user.id}-${Date.now()}`;

    const session = await client.createSession({
        sessionId,
        model: "gpt-4.1",
        infiniteSessions: {
            enabled: true,
            backgroundCompactionThreshold: 0.80,
        },
    });

    res.json({ sessionId });
});

// Continue the conversation
app.post("/api/chat/message", async (req, res) => {
    const session = await client.resumeSession(req.body.sessionId);
    const response = await session.sendAndWait({ prompt: req.body.message });

    res.json({ content: response?.data.content });
});

// Clean up when done
app.post("/api/chat/end", async (req, res) => {
    await client.deleteSession(req.body.sessionId);
    res.json({ success: true });
});

Containerbereitstellungen

Kubernetes mit persistentem Speicher

apiVersion: apps/v1
kind: Deployment
metadata:
  name: copilot-cli
spec:
  replicas: 3
  selector:
    matchLabels:
      app: copilot-cli
  template:
    metadata:
      labels:
        app: copilot-cli
    spec:
      containers:
        - name: copilot-cli
          image: your-registry/copilot-cli:latest  # See backend-services.md for how to build and push this image
          args: ["--headless", "--host", "0.0.0.0", "--port", "4321"]
          env:
            - name: COPILOT_GITHUB_TOKEN
              valueFrom:
                secretKeyRef:
                  name: copilot-secrets
                  key: github-token
          ports:
            - containerPort: 4321
          volumeMounts:
            - name: session-state
              mountPath: /root/.copilot/session-state
      volumes:
        - name: session-state
          persistentVolumeClaim:
            claimName: copilot-sessions-pvc
---
apiVersion: v1
kind: Service
metadata:
  name: copilot-cli
spec:
  selector:
    app: copilot-cli
  ports:
    - port: 4321
      targetPort: 4321

Diagramm: Flussdiagramm mit dem beschriebenen Prozess.

Azure-Containerinstanzen

containers:
  - name: copilot-cli
    image: your-registry/copilot-cli:latest  # See backend-services.md for how to build and push this image
    command: ["copilot", "--headless", "--host", "0.0.0.0", "--port", "4321"]
    volumeMounts:
      - name: session-storage
        mountPath: /root/.copilot/session-state

volumes:
  - name: session-storage
    azureFile:
      shareName: copilot-sessions
      storageAccountName: myaccount

Produktionsprüfliste

Diagramm: Flussdiagramm mit dem beschriebenen Prozess.

SorgeRecommendation
SitzungsbereinigungFühren Sie eine regelmäßige Bereinigung aus, um Sitzungen zu löschen, deren TTL abgelaufen ist.
SystemüberprüfungenPrüfen Sie den CLI-Server regelmäßig; starten Sie ihn neu, wenn er nicht reagiert.
LagerungBereitstellen persistenter Volumes für ~/.copilot/session-state/
GeheimnisseVerwenden Sie den Secret-Manager Ihrer Plattform (Vault, K8s Secrets usw.)
ÜberwachungNachverfolgen der Aktiven Sitzungsanzahl, Antwortlatenz, Fehlerraten
LockingVerwenden Sie Redis oder Ähnliches für den gemeinsamen Sitzungszugriff
HerunterfahrenAktive Sitzungen beenden, bevor CLI-Server gestoppt werden

Einschränkungen

EinschränkungDetails
Keine integrierte SitzungssperreImplementieren von Sperrmechanismen auf Anwendungsebene bei gleichzeitigem Zugriff
Kein integrierter LastenausgleichExternen Load Balancer oder ein Service Mesh verwenden
Sitzungszustand ist dateibasiertErfordert gemeinsames Dateisystem für Setups mit mehreren Servern
Leerlauf-Timeout von 30 MinutenSitzungen ohne Aktivität werden von der CLI automatisch bereinigt.
CLI ist ein EinzelprozessSkalieren durch Hinzufügen weiterer CLI-Serverinstanzen und nicht durch Threads

Nächste Schritte