Skip to main content
이 예시에서는 CWSANDBOX_API_KEY에 설정된 CoreWeave API 액세스 토큰을 사용합니다. Forge API 키 인증은 시작하기를 참조하세요. CKS 요구 사항은 배치 모드를 참조하세요. 이 가이드에서는 샌드박스가 생성부터 종료까지 진행되는 과정과 각 단계의 상태, 시작 및 대기 방법, 정상적으로 중지하는 방법을 설명합니다. 생성 패턴을 선택하거나, 시작 시점을 제어하거나, SDK 코드에서 종료를 안정적으로 처리해야 할 때 이 가이드를 참고하세요.

라이프사이클 상태

모든 샌드박스는 시작, 실행, 종료 과정에서 일련의 상태를 거칩니다. SDK는 이러한 상태를 SandboxStatus 값으로 나타냅니다. PENDING과 CREATING은 일시적인 상태입니다. SDK는 이 상태를 자동으로 계속 폴링합니다. TERMINATING은 일시적인 백엔드 상태로, wait_until_complete()와 stop().result()는 이 상태를 거쳐 종료 상태에 도달하도록 합니다. RUNNING은 안정적인 동작 상태입니다. COMPLETED와 FAILED는 종료 상태입니다. SDK는 폴링 시 UNSPECIFIED를 COMPLETED로 매핑합니다.

생성 패턴

샌드박스는 두 가지 방식으로 생성할 수 있으며, 시작 RPC가 호출되는 시점이 다릅니다. 다음 섹션에서는 각 패턴과 선택 기준을 설명합니다.

Sandbox.run(): 즉시 시작

Sandbox.run()은 샌드박스를 생성하고 내부적으로 start().result()를 호출하며, 백엔드가 요청을 수락할 때까지 대기합니다:
첫 번째 위치 인수는 명령이고, 나머지는 인수입니다:

session.sandbox(): 지연 시작

session.sandbox()는 네트워크 호출 없이 샌드박스 객체를 생성합니다. 시작 RPC는 처음 사용할 때 실행됩니다:

메인 명령 수명

run() 또는 session.sandbox()에 전달된 메인 명령은 샌드박스의 메인 프로세스입니다. 메인 프로세스가 종료되면 어떻게 처리되는지는 종료 상태에 따라 달라집니다.
  • 종료 상태 0: 샌드박스가 COMPLETED 상태로 전환됩니다.
  • 0이 아닌 종료 상태: 플랫폼이 메인 프로세스의 컨테이너를 재시작합니다. 이미 RUNNING에 도달한 샌드박스는 재시작되는 동안에도 RUNNING 상태를 유지하고, 아직 도달하지 않은 샌드박스는 CREATING 상태를 유지합니다. wait_until_complete()는 재시작되는 동안에도 timeout에 도달할 때까지 계속 대기합니다.
  • 재시작 한도 도달: 메인 프로세스가 0이 아닌 상태로 계속 종료되면, 재시작 한도(현재 6회)에 도달했을 때 샌드박스가 FAILED 상태로 전환됩니다.
메인 프로세스가 성공적으로 종료되면 샌드박스도 종료됩니다.
짧은 명령을 실행하고 그 출력을 캡처해야 하는 경우, 장시간 실행되는 샌드박스에서 exec()을 사용하세요. 짧은 명령을 메인 프로세스로 만들지 마세요.

샌드박스 시작

샌드박스 객체가 존재한 후에는 명시적으로 시작하거나 SDK가 처음 사용할 때 시작하도록 할 수 있습니다. 다음 섹션에서는 두 가지 접근 방식과 컨텍스트 매니저가 어떻게 상호작용하는지 설명합니다.

명시적 시작

start()는 시작 RPC를 전송하고 OperationRef[None]을 반환합니다:
이 기능은 타이밍을 제어하거나 시작 오류를 오퍼레이션 오류와 별도로 처리하려는 경우에 유용합니다.

자동 시작

대부분의 오퍼레이션은 샌드박스가 아직 시작되지 않았다면 자동으로 시작합니다. 일반적으로는 샌드박스를 생성한 후 바로 사용하면 됩니다.

컨텍스트 관리자와 start

컨텍스트 관리자(with/async with)는 진입 시 start()를 호출하지만 wait()는 호출하지 않습니다. 첫 번째 오퍼레이션을 실행하기 전에 샌드박스가 RUNNING 상태여야 한다면 wait()를 명시적으로 호출하세요:
실제로는 exec()가 내부적으로 RUNNING 상태가 될 때까지 기다리므로 이 부분이 문제가 되는 경우는 드뭅니다. wait()를 명시적으로 호출하면 시작 실패와 오퍼레이션 실패를 구분해서 처리하고 싶을 때 유용합니다.

샌드박스 대기

SDK는 샌드박스가 오퍼레이션을 수행할 준비가 될 때까지 기다릴지, 메인 명령이 완료될 때까지 기다릴지에 따라 두 가지 대기 방법을 제공합니다. 다음 섹션에서는 각 방법과 시작 timeout 및 오퍼레이션 timeout의 관계를 설명합니다.

wait(): RUNNING 상태까지 대기

wait()는 샌드박스가 RUNNING, TERMINATING 또는 종료 상태에 도달할 때까지 폴링합니다:
폴링은 지수 백오프를 사용합니다: 0.2초 간격으로 시작하여 1.5배씩 스케일링하며, 2.0초에 한도를 적용합니다. wait()는 방법 체이닝을 위해 self를 반환합니다:
샌드박스가 시작 중에 종료 상태에 도달하면, wait()가 이를 처리합니다:

wait_until_complete(): 종료 상태까지 대기

wait_until_complete()는 샌드박스가 종료 상태에 도달할 때까지 대기하며, TERMINATING 상태에서도 자동으로 폴링을 계속합니다. 메인 명령이 작업을 수행하는 샌드박스에 사용하세요:
기본값이 True인 raise_on_termination 매개변수는 이 클라이언트가 stop()을 호출한 후 wait_until_complete()가 SandboxTerminatedError를 발생시킬지 제어합니다. 외부 중지는 별도의 오류 없이 COMPLETED로 표시됩니다. 백엔드가 아직 종료 사유 메타데이터를 제공하지 않기 때문입니다. 반면 최대 수명에 도달한 샌드박스는 FAILED 상태로 종료되므로 wait_until_complete()가 SandboxFailedError를 발생시킵니다.

timeout 단계

시작 대기 시간과 오퍼레이션 timeout은 별개의 단계이며, 샌드박스의 최대 수명이 두 단계 모두를 제한합니다:
  • 시작 대기: RUNNING 상태에 도달하기 전 PENDING/CREATING 상태에서 소요되는 시간입니다. wait() 또는 wait_until_complete()의 timeout 매개변수로 제어합니다. 일반적으로 백엔드 스케줄링에 따라 30~60초가 소요됩니다.
  • 오퍼레이션 timeout: 개별 실행, 조회 또는 쓰기에 허용되는 시간입니다. exec()의 timeout_seconds 또는 SandboxDefaults의 request_timeout_seconds로 제어합니다. 시작 대기는 포함하지 않습니다.
  • 최대 수명: 샌드박스가 존재할 수 있는 총 실제 경과 시간으로, 시작 시점부터 계산하며 시작 대기를 포함합니다. max_lifetime_seconds로 제어합니다. 설정하지 않으면 러너 정책의 기본값이 적용되며, 양수 기본값이 설정되어 있지 않으면 플랫폼의 대체 기본값인 10분이 적용됩니다.
시작 대기와 오퍼레이션 timeout은 클라이언트의 대기 시간을 제한하며, 만료되어도 샌드박스는 계속 실행됩니다. 최대 수명은 샌드박스 자체의 수명을 제한합니다. 만료되면 플랫폼이 샌드박스를 종료하고 진행 중인 오퍼레이션도 함께 실패합니다. max_lifetime_seconds를 예상되는 총 작업 시간보다 충분히 길게 설정하세요. 이 값은 샌드박스가 시작된 후에는 변경할 수 없습니다. 네 가지 timeout 설정과 상호 작용 방식은 timeout을 참조하세요. 30일 제한과 Serverless 및 CKS용 설정 예시는 장기 실행 샌드박스 실행을 참조하세요.

오퍼레이션 및 라이프사이클

exec(), read_file(), write_file() 등의 오퍼레이션은 필요한 경우 샌드박스를 자동으로 시작한 다음, RUNNING 상태가 될 때까지 기다린 후 진행합니다:
오퍼레이션 timeout(timeout_seconds)은 샌드박스가 RUNNING 상태가 된 후에만 적용됩니다. 시작 시간은 timeout에 포함되지 않습니다.

샌드박스 중지 및 수명 종료

이 섹션에서는 샌드박스를 종료하는 방법, 중지 후 발생하는 동작, 그리고 stop()과 delete()를 각각 언제 사용해야 하는지 설명합니다.

stop()

stop()은 중지 요청을 보내고 OperationRef[None]을 반환합니다. 샌드박스는 TERMINATING(유예 기간 동안 드레이닝) 상태를 거쳐 종료 상태에 도달합니다. 반환된 OperationRef는 중지 RPC가 성공했을 때가 아니라 백엔드가 종료 상태를 확인했을 때 완료됩니다:
매개변수:
stop()은 진행 중인 시작 요청을 처리합니다. 시작 요청이 아직 처리 중이면 시작이 완료될 때까지 기다린 후 중지합니다. 동시에 또는 반복해서 stop() Call을 수행하면 하나의 중지 오퍼레이션을 공유하며 중복된 중지 RPC를 보내지 않습니다. 따라서 stop() Call을 반복해도 안전하며 비용이 적게 듭니다.

중지 후 동작

stop()이 호출되면 샌드박스는 TERMINATING(유예 기간 동안 드레이닝하는 상태)을 거쳐 종료 상태(COMPLETED 또는 FAILED)에 도달합니다. stop()이 호출된 후에는 샌드박스를 사용할 수 없습니다. 이후 오퍼레이션은 SandboxNotRunningError를 발생시킵니다:
status 속성은 마지막 API 호출에서 캐시됩니다. 중지하기 전에 최신 데이터를 조회하려면 get_status()를 사용하세요:

컨텍스트 매니저 종료

컨텍스트 매니저는 종료 시 stop()을 자동으로 호출합니다:
예외가 진행 중인 경우, 컨텍스트 매니저는 원래 예외를 가리지 않기 위해 stop 오류를 억제합니다.

stop()와 delete() 비교

활성 상태로 사용 중인 샌드박스에는 stop()을 사용하세요. 샌드박스의 정리에는 delete()를 사용하세요. Sandbox.list() 또는 Sandbox.from_id()를 통해 발견한 샌드박스:
정리 패턴 가이드에서 고아 샌드박스 관리 및 일괄 정리 전략을 확인하세요.

내부 동작

SDK는 자체 asyncio 이벤트 루프가 있는 백그라운드 데몬 스레드에서 모든 gRPC 오퍼레이션을 실행합니다. 이 설계는 다음과 같이 동작합니다:
  • 동기 API (.result())는 백그라운드 루프가 네트워크 호출을 처리하는 동안 호출 스레드를 차단합니다.
  • 비동기 API (await)는 동일한 백그라운드 루프에 연결되므로 두 방식 모두 동일한 내부 구현을 사용합니다.
  • 백그라운드 루프는 처음 사용할 때 지연 시작됩니다. gRPC 채널도 지연 생성됩니다.
  • 자동 시작은 각 오퍼레이션 전에 sandbox_id가 None인지 확인하고, None이면 start()를 트리거하는 방식으로 동작합니다.
  • 프로세스가 종료되면 정리 핸들러(atexit + 시그널 핸들러)가 등록된 세션의 모든 샌드박스를 중지합니다. 정리 중 Ctrl+C를 한 번 더 누르면 즉시 강제 종료됩니다.
이 아키텍처는 서로 다른 이벤트 루프 간의 문제를 방지하며, Jupyter 노트북에서도 nest_asyncio 없이 작동합니다. 사용 방식은 동기 및 비동기 방식 비교 가이드를 참조하세요.

일반적인 패턴

다음 예시는 앞서 설명한 라이프사이클 단계를 조합한 엔드투엔드 패턴을 보여줍니다.

간단한 일회성 실행

명령을 실행하고 결과를 조회하세요. 컨텍스트 매니저가 정리 작업을 처리합니다:

제어된 시작

시작 오류와 오퍼레이션 오류를 분리하세요:

장시간 실행되는 샌드박스

메인 명령이 완료될 때까지 기다리세요:

재연결

이전 세션 또는 프로세스에서 사용하던 샌드박스에 다시 연결합니다.

세션을 사용한 병렬 배치 처리

여러 샌드박스를 생성하고 결과를 기다립니다.
마지막 수정일 2026년 10월 15일