스쿼드 컨테이너 모드¶
스쿼드 컨테이너 모드는 개별 스쿼드 에이전트를 격리된 컨테이너 안에서 실행해 더 강력한 보안 경계와 재현 가능한 실행 환경을 제공합니다. 컨테이너 모드는 스쿼드 안에서 에이전트별로 켤 수 있어서, 일부 에이전트는 인프로세스로, 나머지는 컨테이너에서 실행하는 구성도 가능합니다.
사전 요구 사항¶
컨테이너 모드를 활성화하기 전에 컨테이너 실행 가이드를 완료하세요:
- Docker, 또는 macOS 26의 Apple Container (각 런타임에서 컨테이너가 모델에 연결하는 방법은 모델 경로 참고)
- 에이전트 러너 이미지 빌드 (
aigo-agent-runner:latest) - 마운트 허용 목록 설정
- 에이전트가 사용할 모델: 인프로세스 에이전트와 똑같이, 에이전트 자신의 모델이나 설정 > 모델의 에이전트 기본 모델
스쿼드 작업에는 자격 증명 프록시 매핑이나 별도 설정이 필요 없습니다. 컨테이너 작업에 필요할 때 앱이 프록시를 시작합니다.
작동 방식¶
에이전트가 컨테이너 모드로 실행될 때:
sequenceDiagram
participant Host as Backend.AI GO 호스트
participant CP as 자격 증명 프록시
participant C as 컨테이너 (에이전트 러너)
participant R as Continuum Router
participant M as 에이전트의 모델
Host->>CP: 필요하면 시작하고 실행 토큰 발급
Host->>C: 워크스페이스 마운트, 프록시 URL, 실행 토큰으로 컨테이너 시작
Host->>C: stdin: 작업 하나와 에이전트의 모델
C->>CP: POST /router/anthropic/v1/messages (실행 토큰)
CP->>R: 앱 자신의 라우터 키로 같은 요청 전달
R->>M: 에이전트의 모델과 프로바이더로 라우팅
M-->>R: 응답
R-->>CP: 응답
CP-->>C: 응답
C->>Host: stdout: 결과 블록 하나
C->>Host: 종료 코드 0 (성공) 또는 1 (오류)
Host->>CP: 실행 토큰 폐기
Host->>Host: 작업 결과 또는 오류 기록 컨테이너 하나가 작업 하나를 실행합니다. 호스트는 후속 메시지를 보내지 않고, 컨테이너는 결과를 기록하자마자 종료하므로, 에이전트가 아직 작업 중인 경우가 아니라면 작업이 컨테이너 타임아웃까지 기다리는 일은 없습니다.
각 컨테이너에는 다음이 제공됩니다:
/workspace: 스쿼드의 공유 워크스페이스 디렉토리 (메인 에이전트는 읽기-쓰기, 나머지는 읽기 전용)/workspace/extra/{name}: Management API로 에이전트에 지정한 추가 마운트 (3단계 참고)/workspace/ipc: 호스트-컨테이너 통신을 위한 IPC 디렉토리.claude/: 에이전트의 SDK 세션 상태
모델 경로¶
컨테이너 에이전트는 에이전트에 설정된 모델로 실행되며, 모델은 인프로세스 에이전트와 같은 방식으로 정해집니다. 에이전트 자신의 모델이 있으면 해당 모델을, 없으면 설정 > 모델의 에이전트 기본 모델을 사용합니다. 에이전트에 선택한 프로바이더, 모델의 컨텍스트 윈도우에 맞춘 출력 예산, 온도도 똑같습니다. 모델이 없는 에이전트는 인프로세스 에이전트와 같은 메시지로 작업이 실패하며, 컨테이너는 시작되지 않습니다.
컨테이너는 그 모델에 직접 연결하지 않고, 자격 증명 프록시의 라우터 경로를 거쳐 연결합니다:
| 변수 | 컨테이너 안의 값 |
|---|---|
ANTHROPIC_BASE_URL | Docker: http://host.docker.internal:<프록시 포트>/router/anthropic (기본 포트는 3001). Apple Container: http://localhost/router/anthropic이며, 여기서 localhost는 Host 헤더로만 쓰입니다. |
ANTHROPIC_API_KEY | 이 작업 전용 cpt_ 토큰입니다. 프록시는 작업이 실행되는 동안에만 받아들이고, 작업이 끝나거나 중지되면 거부합니다. |
AIGO_INFERENCE_SOCKET | Apple Container에서만 설정됩니다: /tmp/aigo-inference.sock, 컨테이너 안으로 중계된 프록시의 Unix 소켓입니다. 러너는 모든 모델 호출을 이 소켓으로 보냅니다. |
프록시는 토큰을 확인하고, Messages 호출에 필요한 몇 가지를 제외한 모든 헤더를 버리고, 앱 자신의 라우터 키를 붙여, 인프로세스 경로가 쓰는 것과 같은 소켓 또는 루프백 포트로 내장 Continuum Router에 요청을 전달합니다. 그다음 라우터가 모델을 로컬 엔진이나 설정된 프로바이더로 연결합니다. 이 경로가 전달하는 요청은 POST /anthropic/v1/messages뿐입니다. 라우터의 모델 목록, Files API, 저장된 응답 같은 다른 경로는 앱의 라우터 키가 컨테이너에 열어 주는 일이 없도록 라우터에 닿기 전에 404로 거부됩니다. 앱의 라우터 키는 컨테이너에 들어가지 않으며, 앱 자신의 환경에 있는 ANTHROPIC_* 변수도 들어가지 않습니다. 전달되는 것은 TZ뿐입니다.
컨테이너가 호스트를 찾는 방법은 런타임에 따라 다릅니다:
| 런타임 | 경로 |
|---|---|
| Docker Desktop (macOS, Windows) | host.docker.internal이 기본으로 제공됩니다. 프록시는 127.0.0.1에서 수신합니다. |
| Linux의 Docker | 컨테이너에 --add-host=host.docker.internal:<docker0 주소>가 주어지고, 프록시도 같은 주소에서 수신하므로 컨테이너에서는 접근할 수 있지만 네트워크에서는 접근할 수 없습니다. |
| Apple Container | 게스트에서 호스트의 수신 대기 소켓으로 가는 경로가 없으므로, 앱이 프록시의 Unix 소켓을 컨테이너 안의 /tmp/aigo-inference.sock으로 중계합니다(aigo-container-cli run --relay-socket). 이 소켓은 앱 소켓 디렉토리의 credential-proxy-<인스턴스>.sock이며 현재 사용자만 읽을 수 있고, 소켓으로 들어온 요청도 TCP로 들어온 요청과 똑같이 토큰 확인과 경로 허용 목록을 거칩니다. 프록시가 소켓을 만들지 못했다면 작업은 컨테이너가 시작되기 전에 Container mode under Apple Container needs the credential proxy's Unix socket, which this process could not create로 실패합니다. |
docker0 브리지가 없는 Docker(rootless Docker, Linux용 Docker Desktop)는 지원하지 않습니다. 이 경우 Container inference route unavailable: <error> 또는 Router unreachable: <error>로 나타납니다. 컨테이너 문제 해결을 참고하세요.
에이전트 러너 프로토콜¶
호스트와 에이전트 러너 이미지는 버전이 매겨진 프로토콜로 통신합니다. 번들된 aigo-agent-runner:latest 이미지는 버전 2를 사용하며, 사용자 지정 이미지도 스쿼드 작업을 실행하려면 같은 버전을 사용해야 합니다.
입력¶
호스트는 컨테이너의 stdin에 JSON 한 줄을 쓰고 stdin을 닫습니다:
{
"protocolVersion": 2,
"task": "## Task: Write the config parser\n\nImplement parse_config in src/config.rs.",
"context": ["### Previous task result (task-schema):\nThe schema lives in docs/schema.md."],
"agentName": "Developer",
"systemPrompt": "You are a careful Rust developer.",
"deadlineMs": 1770000,
"model": "qwen3-8b-instruct",
"maxTokens": 2048,
"backend": "provider-openai-a1b2c3d4",
"temperature": 0.7
}
| 필드 | 필수 | 설명 |
|---|---|---|
protocolVersion | 예 | 항상 2입니다. 러너는 버전이 없거나 다르면 입력을 거부합니다. |
task | 예 | 작업 제목과 설명입니다. 비어 있을 수 없습니다. |
context | 아니요 (기본값 []) | 이 작업이 의존하는 작업들의 결과로, 각각 ### Previous task result (<작업 ID>): 레이블이 붙습니다. 이 작업 자신의 의존 작업만 포함되며, 인프로세스 에이전트가 받는 결과와 같습니다. |
agentName | 아니요 (기본값 "") | 에이전트 이름입니다. |
systemPrompt | 아니요 (기본값 "") | 에이전트의 시스템 프롬프트입니다. 비어 있으면 러너가 일반적인 스쿼드 프롬프트를 사용합니다. |
deadlineMs | 아니요 | 러너가 결과를 써야 하는 시간(밀리초)입니다. 호스트는 컨테이너 타임아웃보다 10%(최대 30초) 짧게 설정하므로, 멈춘 모델 호출은 컨테이너가 강제 종료되는 대신 러너 자신의 오류로 끝납니다. |
model | 예 | 호스트가 에이전트에 대해 정한 모델입니다. 비어 있을 수 없습니다. 러너에는 자체 기본 모델이 없습니다. |
maxTokens | 예 | 모델의 컨텍스트 윈도우에 맞춰 이미 조정된 출력 예산입니다. 양의 정수입니다. |
backend | 아니요 | 에이전트에 선택한 프로바이더의 라우터 백엔드입니다. 러너는 이를 x-backend 헤더로 보냅니다. |
temperature | 아니요 | 샘플링 온도입니다. 모델이 온도를 받지 않으면 빠집니다. |
입력은 최대 1 MiB입니다. 러너가 거부할 것이므로, 호스트는 입력이 더 큰 작업을 컨테이너를 시작하지 않고 실패 처리합니다. 러너가 모르는 필드는 무시되므로, 나중에 선택 필드가 추가되어도 프로토콜 버전은 바뀌지 않습니다. 버전 2에서는 model과 maxTokens가 필수가 되었습니다. 버전 1 러너는 이 필드를 무시하고 자체 기본 모델을 호출했을 것이므로, 대신 버전 오류로 실패합니다.
출력¶
러너는 두 마커 줄 사이에 JSON 블록을 정확히 하나 stdout에 쓴 다음 종료합니다:
---AIGO_OUTPUT_START---
{"protocolVersion":2,"status":"success","result":"Implemented parse_config.","usage":{"promptTokens":2050,"completionTokens":340}}
---AIGO_OUTPUT_END---
| 필드 | 설명 |
|---|---|
protocolVersion | 항상 2 |
status | success 또는 error |
result | 에이전트의 응답. 오류일 때는 null |
error | 오류 메시지. 오류일 때만 있습니다. |
usage | promptTokens(캐시 쓰기와 캐시 읽기를 포함한 입력 토큰)와 completionTokens. 성공일 때만 있으며, 업스트림 API가 토큰 수를 알려 주지 않으면 빠집니다. |
그 밖의 stdout 줄과 모든 stderr 줄은 컨테이너 로그로 보관됩니다.
종료 코드¶
| 종료 코드 | 의미 |
|---|---|
0 | 성공. 작업 결과는 블록의 result입니다. |
1 | 오류. 작업이 Container agent error: <error>로 실패합니다. |
결과는 종료 코드만으로 정해지지 않고 블록이 결정합니다:
- 오류 블록이 있으면 컨테이너가
0으로 종료해도 작업이 실패하고, 컨테이너가 0이 아닌 코드로 종료하면 단순한 종료 상태 대신 블록의 메시지가 오류가 됩니다. protocolVersion이 다른 블록이 있으면 작업이Container agent speaks protocol version <v>; this app speaks 2 (rebuild aigo-agent-runner:latest or update the custom image)로 실패합니다.- 블록을 출력하지 않는 컨테이너는 이전과 같이 동작합니다.
0으로 종료하면 컨테이너 로그 출력을 결과로 작업이 완료되고, 다른 종료 코드면 종료 상태로 실패합니다.
이전 프로토콜용으로 빌드된 이미지¶
이전 프로토콜용으로 빌드된 에이전트 러너 이미지는 위 입력을 실행할 수 없으므로, 그 이미지에 할당된 모든 작업이 실패합니다:
- 프로토콜이 생기기 전에 빌드된 이미지는
(the image predates agent runner protocol 2; rebuild aigo-agent-runner:latest or update the custom image)로 끝나는 메시지와 함께 실패합니다. - 프로토콜 1용으로 빌드된 이미지는
Container agent speaks protocol version 1; this app speaks 2 (rebuild aigo-agent-runner:latest or update the custom image)로 실패합니다.
설정 > 스쿼드 > 에이전트 러너 이미지 → 이미지 재빌드로 이미지를 다시 빌드하거나, 소스 체크아웃에서 ./container/build.sh를 실행하세요. 사용자 지정 이미지는 버전 2 입력을 읽고, 받은 model을 ANTHROPIC_BASE_URL로 호출하고, 출력 블록을 쓰도록 수정해야 합니다. 컨테이너 문제 해결을 참고하세요.
1단계: 스쿼드 생성 또는 편집¶
-
사이드바에서 스쿼드로 이동해 스쿼드 탭을 엽니다.
-
스쿼드 생성을 클릭하거나, 기존 스쿼드 카드의 메뉴에서 편집을 선택합니다. 스쿼드 마법사가 열립니다.
-
마법사의 워크스페이스 단계에서 워크스페이스 디렉토리가 비어 있으면 지정합니다.
2단계: 에이전트에서 컨테이너 모드 활성화¶
-
마법사의 에이전트 단계에서 컨테이너로 실행할 에이전트를 찾습니다. 이미 열어 둔 스쿼드라면 스쿼드의 개요 탭에 있는 에이전트 목록에서 해당 에이전트 카드의 편집을 클릭해도 되는데, 에이전트 편집 대화상자에도 같은 필드가 있습니다.
-
실행 모드를 컨테이너로 바꿉니다 (기본값은 인프로세스).
-
컨테이너 설정 필드가 나타납니다:
필드 기본값 설명 이미지 aigo-agent-runner:latest사용할 컨테이너 이미지 타임아웃 (초) 1800 (30분) 최대 실행 시간, 60초부터 7200초까지 유휴 타임아웃 (초) 300 에이전트 설정에 저장되지만 컨테이너 러너는 아직 이 값을 적용하지 않습니다. 실행 중인 컨테이너를 멈추는 값은 타임아웃 (초) 하나뿐입니다. -
마법사를 스쿼드 만들기 또는 변경 사항 저장으로 마치거나, 에이전트 편집 대화상자에서 저장을 클릭합니다.
3단계: Management API로 마운트 추가 (선택)¶
에이전트 편집 화면에는 추가 마운트를 입력하는 필드가 없습니다. 에이전트가 워크스페이스 밖의 호스트 경로에 접근해야 한다면 PATCH /api/v1/squads/{id}/agents/{agent_id}로 에이전트 containerConfig의 additionalMounts를 지정하세요. 항목마다 다음 값을 받습니다:
hostPath: 마운트할 호스트 경로 (허용 루트 아래여야 함)containerPath: 컨테이너 내부의 마운트 위치 (/workspace/extra/아래여야 함)readOnly: 에이전트가 이 경로에 쓰지 못하게 하려면true
이 요청은 에이전트의 containerConfig 전체를 바꾸므로, additionalMounts와 함께 image와 timeout(밀리초)도 보내야 기본값으로 돌아가지 않습니다.
마운트 보안
허용 루트 아래의 경로만 마운트할 수 있습니다. .ssh, .env, .aws 같은 패턴이 포함된 경로는 항상 차단됩니다.
4단계: 그룹 네임스페이스 설정¶
그룹 네임스페이스는 컨테이너를 격리된 세션 그룹으로 묶습니다. 각 그룹은 고유한 세션 디렉토리와 지침 세트를 갖습니다.
그룹 네임스페이스 생성¶
-
스쿼드 만들기 마법사의 워크스페이스 단계로 이동합니다.
-
그룹 네임스페이스 옆의 새 그룹 만들기를 클릭합니다.
-
그룹 이름을 입력합니다 (파일시스템에 안전한 이름, 예:
dev-team). -
필요하면 설명을 추가합니다.
-
새 그룹 만들기를 클릭해 그룹을 생성합니다.
그룹 지침 설정¶
그룹별 지침(CLAUDE.md에 해당)은 해당 그룹의 모든 컨테이너 세션에 주입됩니다. 앱에는 그룹 지침을 편집하는 화면이 없으므로 PUT /api/v1/container/namespaces/{name}/instructions로 설정하세요. 예시는 그룹 지침 설정에 있습니다.
전역 지침¶
전역 지침은 모든 그룹에 적용됩니다. 그룹별 지침과 결합되어 컨테이너마다 병합된 형태로 주입됩니다:
-
설정 > 스쿼드에서 전역 지시사항 섹션으로 이동합니다.
-
전역 지시 사항을 입력합니다.
-
지시사항 저장을 클릭합니다.
5단계: 스쿼드 실행¶
-
스쿼드를 열고 채팅 탭으로 이동합니다.
-
메시지 입력창에 작업 내용을 입력해 보냅니다.
-
플래너가 작업 그래프를 만들고 에이전트에 작업을 할당합니다.
-
컨테이너 모드 에이전트에 작업이 할당되면:
DATA_DIR/sessions/{group}/아래에 세션 디렉토리가 생성됩니다- 자격 증명 프록시가 실행 중이 아니면 시작되고, 작업에 라우터 토큰을 발급합니다
- 워크스페이스와 IPC 마운트가 연결된 컨테이너가 시작됩니다
- 에이전트 러너가 stdin으로 작업을 받아 stdout에 결과 블록 하나를 쓰고 종료합니다
- 작업은 블록의 결과로 완료되거나, 러너 자신의 오류 메시지 또는 타임아웃으로 실패합니다
컨테이너 실행 모니터링¶
컨테이너 상태 패널¶
컨테이너 모드 에이전트가 작업하는 동안 스쿼드의 채팅 탭에 해당 컨테이너의 상태 카드가 나타나고, 개요 탭에는 실행 중, 대기 중, 종료 중인 컨테이너 수를 보여 주는 컨테이너 큐 섹션이 추가됩니다. 상태 카드에는 다음 상태 중 하나가 프로비저닝 중, 실행 중, 완료, 실패, 타임아웃으로 표시됩니다:
| 상태 | 설명 |
|---|---|
provisioning | 컨테이너 시작 중, 이미지 준비 중 |
running | 컨테이너가 활성 상태로 처리 중 |
completed | 컨테이너가 성공적으로 완료됨 |
failed | 컨테이너가 오류로 종료됨 |
timeout | 컨테이너가 실행 시간 제한을 초과함 |
실행 기록¶
컨테이너 실행 기록은 Management API로만 볼 수 있습니다. 자동화 > 실행 기록에는 자동화 실행 내역만 나오고 컨테이너 실행은 나오지 않습니다.
-
GET /api/v1/container/run-history를 호출합니다. -
항목마다 그룹, 시작 시각과 종료 시각, 종료 코드, 입력과 출력 요약이 들어 있습니다.
감사 로그¶
-
GET /api/v1/container/audit-log를 호출합니다. -
보안 이벤트, 권한 검사, 마운트 검증 내역을 살펴봅니다.
컨테이너 모드 문제 해결¶
에이전트 컨테이너가 시작되지 않음¶
- 에이전트 러너 이미지가 존재하는지 확인: 설정 > 스쿼드 > 에이전트 러너 이미지
- 런타임이 사용 가능한지 확인:
curl http://localhost:8001/api/v1/container/runtime - 감사 로그에서 권한 오류 검토
에이전트가 워크스페이스에 쓸 수 없음¶
- 스쿼드 워크스페이스 디렉토리가 허용 루트 아래에 있는지 확인
- 에이전트가 메인 에이전트로 지정되어 있는지 확인 (메인 에이전트만 워크스페이스 읽기-쓰기 권한을 가집니다)
컨테이너가 타임아웃됨¶
- 에이전트의 컨테이너 설정에서 타임아웃 (초) 늘리기
- 채팅 탭에 있는 에이전트 상태 카드에서 컨테이너 로그 표시를 클릭해 느린 단계 검토
- 작업을 더 작은 하위 작업으로 나누는 것 고려
컨테이너 내부에서 모델 호출 실패¶
- 작업이 실행되는 동안 자격 증명 프록시 상태 엔드포인트가
"running": true를 보고하는지 확인. 스쿼드 작업에는 자격 증명 매핑이 필요 없습니다. - 컨테이너의
ANTHROPIC_BASE_URL이/router/anthropic으로 끝나고ANTHROPIC_API_KEY가cpt_토큰인지 확인 - 작업 오류를 자격 증명 프록시 문제와 대조
API 참조¶
| 엔드포인트 | 메서드 | 설명 |
|---|---|---|
/api/v1/container/queue/groups | GET | 모든 컨테이너 그룹 목록 |
/api/v1/container/queue/groups/{group} | GET | 컨테이너 그룹 상태 조회 |
/api/v1/container/queue/counts | GET | 그룹별 큐 수 조회 |
/api/v1/container/queue/max-concurrent | GET/PUT | 최대 동시 컨테이너 수 조회 또는 설정 |
/api/v1/container/run-history | GET | 컨테이너 실행 기록 조회 |
/api/v1/container/metrics | GET | 현재 컨테이너 메트릭 |
/api/v1/container/analytics | GET | 집계된 컨테이너 분석 |