Skip to main content

Copilot SDK デプロイのスケーリング

複数のユーザーにサービスを提供し、同時セッションを処理し、インフラストラクチャ全体で水平方向にスケーリングするように、 GitHub Copilot SDK デプロイを設計します。

この機能を使用できるユーザーについて

GitHub Copilot SDK は、すべての Copilot プランで使用できます。

メモ

          Copilot SDK は現在 テクニカル プレビューです。 機能と可用性は変更される場合があります。

アプリケーションを実装するときに、CLI セッションのさまざまな分離パターンと、同時実行セッションとリソースを管理する方法を検討してください。

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

セッション分離パターン

パターンを選択する前に、次の 3 つのディメンションを検討してください。

  • 分離: どのセッションを誰が確認できますか?
  • コンカレンシー: 同時に実行できるセッションの数はいくつですか?
  • 永続性: セッションの存続期間はどれくらいですか?

Copilot SDK デプロイの 3 つのスケーリング ディメンション (分離、コンカレンシー、永続化) を示す図。

パターン 1: ユーザーごとの分離 CLI

各ユーザーは、独自の CLI サーバー インスタンスを取得します。 これは最も強力な分離であり、ユーザーのセッション、メモリ、プロセスは完全に分離されています。

各ユーザーが専用 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 を介して分離されたセッションを持っています。 これはリソースでは軽くなりますが、分離が弱くなります。

複数のユーザーが分離されたセッションで 1 つの 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);
        }
    }
}

// Serialize access to a 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 + セッションの分離共有セッション
隔離完了論理Shared
リソースの使用状況高 (ユーザーあたりの CLI)低 (1 つの CLI)低 (1 つの CLI とセッション)
複雑性中程度高 (ロックが必要です)
認証の柔軟性ユーザーごとのトークンサービス トークンサービス トークン
最適な用途マルチテナント SaaS内部ツールコラボレーション

水平スケーリング

ロード バランサーの背後にある複数の CLI サーバー

より多くの同時実行ユーザーにサービスを提供するには、ロード バランサーの背後で複数の CLI サーバー インスタンスを実行します。 任意の CLI サーバーが任意のセッションを再開できるように、セッション状態は 共有ストレージ 上にある必要があります。

セッション状態の共有ストレージがあるロード バランサーの背後にある複数の CLI サーバーを示す図。

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

スティッキー セッションと共有ストレージ

Copilot SDK デプロイをスケーリングするためのスティッキー セッションと共有ストレージアプローチを比較した図。

          **スティッキー セッションでは** 、各ユーザーが特定の CLI サーバーにピン留めされます。 共有ストレージは必要ありませんが、ユーザー トラフィックが大幅に変化すると、負荷分散が不均一になる可能性があります。

          **共有ストレージ** を使用すると、任意の CLI で任意のセッションを処理できます。 負荷分散はより均等ですが、 `~/.copilot/session-state/`にはネットワークストレージが必要です。

垂直スケーリング

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

1 つの CLI サーバーで、多数の同時セッションを処理できます。 重要なのは、リソースの枯渇を避けるためにセッション ライフサイクルを管理することです。

垂直スケーリングのリソース ディメンション (CPU、メモリ、ディスク I/O、ネットワーク) を示す図。

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

一時的セッションと永続的セッション

Copilot SDK デプロイのエフェメラル セッションと永続的セッションを比較した図。

          **エフェメラル セッション** は要求ごとに作成され、使用後に破棄されます。 これらは、ワンショット タスクやステートレス 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();
    }
});

永続的セッション

// Start a conversation
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

次の例では、任意のレプリカが任意のセッションを再開できるように、 PersistentVolumeClaim を共有する 3 つの CLI レプリカをデプロイします。

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: ghcr.io/github/copilot-cli:latest
          args: ["--headless", "--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

セッション状態の PersistentVolumeClaim を共有する複数の CLI サーバー ポッドを含む Kubernetes デプロイを示す図。

実稼働チェックリスト

懸念レコメンデーション
セッションのクリーンアップ定期的なクリーンアップを実行して、TTL より古いセッションを削除します。
ヘルスチェックCLI サーバーに定期的に ping を実行します。応答しない場合は再起動します。
ストレージ
          `~/.copilot/session-state/`の永続ボリュームをマウントします。 |

| シークレット | プラットフォームのシークレットマネージャー(Vault、Kubernetes Secrets など)を使用します。 | | 監視 | アクティブなセッション数、応答待機時間、エラー率を追跡します。 | | Locking | 共有セッション アクセスには Redis または同様を使用します。 | | シャットダウン | CLI サーバーを停止する前に、アクティブなセッションをドレインします。 |

制限事項

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

次のステップ