Skip to main content

Масштабирование и многоарендность

Спроектируйте развертывание Copilot SDK так, чтобы обслуживать нескольких пользователей, обрабатывать одновременные сессии и масштабироваться горизонтально по инфраструктуре. Это руководство охватывает схемы изоляции сессий, топологии масштабирования и лучшие практики производства.

Лучше всего для: Разработчики платформы, SaaS-конструкторы, любое развертывание, обслуживающее более нескольких одновременных пользователей.

Основные понятия

Перед выбором паттерна изучите три измерения масштабирования:

Диаграмма: блок-схема, показывающая описанный процесс.

Шаблоны изоляции сессий

Шаблон 1: изолированный CLI на пользователя

Каждый пользователь получает свой собственный экземпляр CLI-сервера. Самая сильная изоляция — сессии, память и процессы пользователя полностью разделены.

Диаграмма: блок-схема, показывающая описанный процесс.

Когда использовать:

  • Мультиарендный SaaS, где изоляция данных критически важна
  • Пользователи с разными авторскими данными
  • Требования к соблюдению (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);
        }
    }
}

Шаблон 2: общий CLI с изоляцией сессий

Несколько пользователей используют один CLI-сервер, но имеют изолированные сессии через уникальные идентификаторы сессий. Ресурсы меньше, но изоляция слабее.

Диаграмма: блок-схема, показывающая описанный процесс.

Когда использовать:

  • Внутренние инструменты с надёжными пользователями
  • Среды с ограниченными ресурсами
  • Низкие требования к изоляции
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);
}

Шаблон 3: совместные сессии (совместные)

Несколько пользователей взаимодействуют с одной и той же сессией — как в общем чате с Copilot.

Диаграмма: блок-схема, показывающая описанный процесс.

Когда использовать:

  • Инструменты для командной работы
  • Сессии совместного обзора кода
  • Парные ассистенты программирования

⚠️Важно: SDK не обеспечивает встроенную блокировку сессий. Необходимо сериализировать доступ, чтобы предотвратить одновременные записи в одну и ту же сессию.

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 });
});

Сравнение моделей изоляции

Изолированный CLI на пользователяОбщая CLI + изоляция сессийОбщие сеансы
Isolation
✅ Завершено
⚠️ Логичность
❌ Общие
Использование ресурсовВысокий (CLI на пользователя)Низкий (один CLI)Низкий уровень (один CLI + сеанс)
СложностьСредняяНизкийВысокий (блокировка)
Гибкость аутентификации
✅ Токены на пользователя
⚠️ Сервисный токен
⚠️ Сервисный токен
лучше всего подходит дляМногопользовательский SaaSВнутренние инструментыCollaboration

Горизонтальное масштабирование

Несколько CLI-серверов за балансировщиком нагрузки

Диаграмма: блок-схема, показывающая описанный процесс.

Ключевое требование: Состояние сессии должно находиться на общем хранилище , чтобы любой CLI-сервер мог возобновить любую сессию.

// 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 });
});

Закреплённые сессии против общего хранилища

Диаграмма: блок-схема, показывающая описанный процесс.

Закреплённые сессии проще — пользователи связаны с определёнными CLI-серверами. Общее хранилище не требуется, но распределение нагрузки неравномерное.

Общее хранилище позволяет любому CLI обрабатывать любую сессию. Лучшее распределение нагрузки, но требует сетевого хранения для ~/.copilot/session-state/.

Вертикальное масштабирование

Настройка одного CLI-сервера

Один CLI-сервер может обрабатывать множество одновременных сессий. Ключевые вопросы

Диаграмма: блок-схема, показывающая описанный процесс.

Управление жизненным циклом сессии является ключом к вертикальному масштабированию:

// 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);
    }
}

Эфемерные и постоянные сессии

Диаграмма: блок-схема, показывающая описанный процесс.

Эфемерные сессии

Для конечных точек API без состояния, где каждый запрос независим:

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
    }
});

Постоянные сеансы

Для разговорных интерфейсов или долгосрочных рабочих процессов:

// 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 });
});

Развертывания контейнеров

Kubernetes с постоянным хранением памяти

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

Диаграмма: блок-схема, показывающая описанный процесс.

Экземпляры контейнеров Azure (инстансы контейнеров в Azure)

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

Контрольный список рабочей среды

Диаграмма: блок-схема, показывающая описанный процесс.

БеспокойствоRecommendation
Уборка сессииЗапускайте периодическую очистку, чтобы удалять сессии старше вашего TTL
Медицинские проверкиПериодически пингуйте CLI-сервер; Перезагрузка, если не отвечает
ХранениеМонтировать постоянные тома для ~/.copilot/session-state/
СекретыИспользуйте менеджер секретов вашей платформы (Vault, K8s Secrets и т.д.)
MonitoringОтслеживайте количество активных сессий, задержку ответа, уровень ошибок
LockingИспользуйте Redis или подобные для доступа к совместной сессии
Завершение работыОпустошите активные сессии перед закрытием CLI-серверов

Ограничения

LimitationDetails
Нет встроенной блокировки сессииРеализовать блокировку на уровне приложения для одновременного доступа
Нет встроенной балансировки нагрузкиИспользуйте внешний LB или сервисный меш
Состояние сессии основано на файлахТребуется общая файловая система для настройки нескольких серверов
30-минутный тайм-аут на холостом ходуЗанятия без активности автоматически очищаются CLI
CLI — это однопроцессный процессМасштабируйтесь, добавляя больше экземпляров CLI-серверов, а не потоков

Дальнейшие действия