Skip to main content

スケーリングとマルチテナント

複数のユーザーにサービスを提供し、同時セッションを処理し、インフラストラクチャ全体で水平方向にスケーリングするように、Copilot SDK のデプロイを設計します。 このガイドでは、セッション分離パターン、スケーリング トポロジ、運用のベスト プラクティスについて説明します。

次の場合に最適です。 プラットフォーム開発者、SaaS ビルダー、少数を超える同時実行ユーザーにサービスを提供するデプロイ。

主要な概念

パターンを選択する前に、スケーリングの 3 つのディメンションを理解してください。

図: 説明されたプロセスを示すフローチャート。

セッション分離パターン

パターン 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

複数のユーザーが 1 つの CLI サーバーを共有しますが、一意のセッション ID を介して分離されたセッションを持っています。 リソースは軽くなりますが、分離は弱くなります。

図: 説明されたプロセスを示すフローチャート。

使用するタイミング:

  • 信頼されたユーザーを含む内部ツール
  • リソースに制約のある環境
  • 分離要件の低減
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)低 (1 つの CLI)低 (1 つの CLI + セッション)
複雑さ中程度高 (ロック)
認証の柔軟性
✅ ユーザーごとのトークン
⚠️ サービス トークン
⚠️ サービス トークン
最適な用途マルチテナント SaaS内部ツールコラボレーション

水平スケーリング

ロード バランサーの背後にある複数の 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/にはネットワークストレージが必要です。

垂直スケーリング

1 つの CLI サーバーのチューニング

1 つの 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 Container Instances

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

実稼働チェックリスト

図: 説明されたプロセスを示すフローチャート。

懸念レコメンデーション
セッションのクリーンアップ定期的なクリーンアップを実行して TTL より古いセッションを削除する
ヘルスチェックCLI サーバーに定期的に ping を実行します。応答しない場合は再起動する
Storage
~/.copilot/session-state/ の永続ボリュームをマウントする
シークレットプラットフォームのシークレット マネージャー (Vault、K8s シークレットなど) を使用する
Monitoringアクティブなセッション数、応答の待機時間、エラー率を追跡する
Locking共有セッション アクセスに Redis または類似を使用する
シャットダウンCLI サーバーを停止する前にアクティブなセッションをドレインする

制限事項

制限事項詳細情報
組み込みのセッション ロックなし同時実行アクセス用にアプリケーション レベルのロックを実装する
組み込みの負荷分散なし外部 LB またはサービスメッシュを使用する
セッションの状態はファイル ベースですマルチサーバーセットアップ用の共有ファイルシステムが必要
30 分間の無操作タイムアウトアクティビティのないセッションは CLI によって自動クリーンアップされます
CLI は単一プロセスですスレッドではなく CLI サーバー インスタンスを追加してスケーリングする

次のステップ