Agents API를 Cloudflare에서 실행하기, 책임 경계부터 정하라

OpenAI Agents API를 Cloudflare Containers에서 실행하려면 세션 하네스와 실행 환경의 책임부터 분리해야 한다. OpenAI가 세션과 오케스트레이션을 관리하고, 애플리케이션과 Cloudflare 측은 Worker, Container, 비밀, 데이터 및 네트워크 접근을 통제하는 구조다.
구성 순서는 참조 Worker와 Container 배포, 웹훅 등록, 키 분리, 세션별 라우팅 확인, 재연결과 유휴 종료 시험 순이다. 이 경계를 먼저 정하면 세션 상태 문제와 Container 실행 문제를 같은 장애로 다루는 일을 피할 수 있다.
1. 세션 하네스와 실행 환경을 분리한다

OpenAI의 Agents API 소개 는 이 API가 공개 베타이며 OpenAI가 Codex 기반 하네스를 운영하는 한편, 개발자는 OpenAI 관리형 샌드박스·자체 인프라·파트너 환경 중 컴퓨팅 환경을 선택할 수 있다고 설명한다. 긴 세션에서 이전 컨텍스트를 자동 압축하는 기능도 하네스에 포함된다.
따라서 Container 안에 별도의 대화 오케스트레이터를 복제하기보다 실행 책임을 명확히 두는 편이 낫다. Worker는 서명된 이벤트를 받아 세션별 실행 인스턴스로 전달하는 제어면이고, Container는 명령과 에이전트 생성 코드를 실행하며 작업 파일을 다루는 실행면이다.
- OpenAI: 세션, 모델 호출 오케스트레이션, 컨텍스트 관리와 하네스 수준 복구
- Worker: 웹훅 검증, 세션 소유권 확인, 인스턴스 라우팅과 정리 요청 보호
- Container: 명령 실행, 런타임 패키지, 작업 디렉터리와 허용된 외부 연결
- 애플리케이션: 세션 생성, 입력 제출, 이벤트 소비와 명시적 종료
운영 로그와 장애 대응 문서도 이 네 경계를 따라 나누는 것이 좋다. 세션 조회는 성공하지만 명령이 진행되지 않으면 Container와 원격 연결을 확인하고, 이벤트 자체가 도착하지 않으면 Worker의 공개 경로와 서명 검증을 먼저 살핀다.
2. 참조 템플릿과 웹훅을 먼저 완성한다
Cloudflare 공식 배포 절차 는 세션마다 Durable Object와 Container를 연결하고 Container에서 codex exec-server를 실행하는 구성을 제시한다. 문서에 명시된 전제는 Containers 접근 권한이 있는 Cloudflare 계정, Agents API 접근 권한과 OpenAI API 키, curl이며, 수동 배포에는 Node.js 24 이상, npm, Docker와 Wrangler가 추가로 필요하다.
빠른 시작은 Deploy to Cloudflare 버튼을 이용한다. 변경 이력과 실행 이미지를 직접 관리하려면 cloudflare/sandbox-sdk 저장소를 복제하고 의존성을 설치한 뒤 openai/agents-api 디렉터리에서 Wrangler로 배포한다. Dockerfile, Worker 코드와 wrangler.jsonc를 함께 검토하면 이미지 변경과 수명주기 설정 변경을 구분할 수 있다.
- OpenAI에서 에이전트를 만들고 반환된 에이전트 ID를 보관한다.
- 세션 조회용 OPENAI_API_KEY, 제한된 OPENAI_EXECUTOR_API_KEY, OPENAI_AGENT_ID, 정리 엔드포인트용 EXECUTOR_CLIENT_SECRET을 Worker 비밀로 등록한다.
- 첫 배포에서는 OPENAI_WEBHOOK_SECRET에 임시 값을 넣고 Worker와 Container를 배포한다.
- 공개된 /webhook 경로를 OpenAI 프로젝트에 등록하고 session created, action required, in progress, idle, failed 이벤트를 구독한다.
- 등록 과정에서 발급된 서명 비밀로 임시 값을 교체하고 다시 배포한다.
- /health 응답에서 configured와 webhook_configured가 모두 true인지 확인한다.
/webhook은 OpenAI가 접근할 수 있어야 하므로 대화형 로그인 뒤에 두면 안 된다. 다른 Worker 경로를 Cloudflare Access로 보호한다면 웹훅 경로에는 별도 정책을 적용하고 Worker가 서명을 검증하게 한다. 반대로 수동 정리 엔드포인트는 충분히 긴 공유 비밀로 보호한다.
3. 비밀·네트워크·저장소의 권한을 좁힌다

컨트롤러 키와 실행기 키는 같은 OpenAI 조직·프로젝트·사용자 또는 서비스 계정 소유자에 속해야 한다. 문서의 최소 권한은 컨트롤러 키에 api.agents.read, 실행기 키에 api.model.read와 api.agents.environments.connect를 부여하는 구성이다. 제한된 실행기 키만 CODEX_API_KEY로 Container에 전달하고, 컨트롤러 키와 웹훅 서명 비밀, 정리용 비밀은 Worker에 남긴다.
Cloudflare Container 인터페이스 는 envVars, enableInternet, sleepAfter와 수명주기 훅을 제공하며, Container 디스크는 기본적으로 임시 저장소인 반면 Durable Object의 SQLite 저장소는 재시작 후에도 유지된다고 명시한다. 일반 Container 클래스의 sleepAfter 기본값은 10분이지만, 참조 실행기의 EXECUTOR_KEEP_ALIVE_SECONDS와는 역할이 다른 설정이다.
참조 실행기는 OpenAI에 연결해야 하므로 아웃바운드 인터넷이 활성화된다. 운영 환경에서는 전체 인터넷 접근과 범용 자격 증명을 동시에 허용하지 말고, 필요한 목적지만 통과시키는 아웃바운드 제어나 Worker 중계를 적용한다. 데이터베이스·R2·KV를 연결할 때도 세션과 작업에 필요한 읽기·쓰기 범위만 제공한다.
- Dockerfile에 API 키나 고객별 자격 증명을 넣지 않는다.
- 수정할 필요가 없는 자료는 읽기 전용 저장소나 제한된 중계 API로 제공한다.
- 사용자 입력이 목적지 URL을 결정한다면 Worker에서 허용 호스트와 프로토콜을 검사한다.
- Authorization 헤더, 웹훅 서명과 환경 변수의 실제 값은 로그에서 제거한다.
Container 내부 프로세스는 전달된 실행기 키를 읽을 수 있다는 점도 위협 모델에 포함해야 한다. 에이전트가 실행하는 셸과 패키지 도구가 같은 격리 범위에 있으므로, 이미지에는 작업에 필요한 바이너리만 설치하고 비밀의 권한으로 피해 범위를 제한한다.
4. 세션 ID를 수명주기의 기준으로 삼는다
참조 구현에서는 세션 이름을 사용하는 Durable Object가 해당 세션의 Container를 관리한다. agent.session.created 이벤트는 기본적으로 사전 준비를 시작하고, agent.session.action_required 이벤트는 현재 세션 상태와 소유권을 확인한 뒤 환경 ID와 원격 연결 정보를 가져온다. 서로 다른 세션에 같은 작업 디렉터리나 프로세스를 공유하지 않는 구조다.
Container 시작, 환경 연결 작업과 agent.session.in_progress 이벤트는 수명주기 마감 시간을 설정한다. 마감 시간이 끝나도 Worker는 Container를 곧바로 제거하지 않고 세션 상태를 다시 조회한다. 세션이 여전히 활성 상태라면 새 마감 시간을 부여하므로 장시간 작업의 생존 여부가 한 번의 HTTP 요청 시간에 매이지 않는다.
후속 입력은 새 세션이 아니라 기존 세션의 이벤트 스트림으로 보낸다. 다시 도착한 action required 이벤트에 대해 Worker는 환경 ID가 같은 실행 중 Container를 재사용하거나, 저장된 스냅샷을 사용할 수 있으면 다음 환경을 복원한다. 재연결 시험에서는 첫 입력이 /workspace에 만든 파일을 후속 입력이 읽는지 확인하고, 별도 세션에서는 같은 파일이 보이지 않는지도 검사한다.
전체 Container 스냅샷은 비공개 베타이며 내구성 백업이 아니다. 기능을 사용할 수 없거나 비활성화하면 다음 실행기는 새로운 /workspace를 받을 수 있다. 반드시 보존해야 하는 결과물은 R2 같은 별도 영속 계층에 저장하고, Durable Object에는 세션 매핑과 복구에 필요한 작은 상태만 두는 편이 안전하다.
5. 유휴 종료·복구·정리를 함께 시험한다

참조 템플릿에서 Container 유지 간격은 EXECUTOR_KEEP_ALIVE_SECONDS로 조정하며 기본값은 30초다. 이는 사용자 세션의 총 유효기간이 아니라 현재 세션 상태를 다시 확인하기 위한 수명주기 마감 간격이다. Container 클래스의 일반적인 비활성 종료 설정과 같은 값으로 취급하지 않는다.
agent.session.idle 이벤트가 확인되면 지원되는 구성은 전체 Container 스냅샷을 만든 뒤 마감 타이머를 건다. 타이머 만료 시 Worker가 세션 상태를 다시 확인하고 유휴 Container를 중지하며 스냅샷은 다음 연결을 위해 남긴다. agent.session.failed 이벤트나 세션 조회의 404 응답은 Container를 멈추고 저장된 스냅샷까지 지우는 정리 신호다.
OpenAI 세션 삭제만으로 Container 정리 웹훅이 전송되지는 않는다. 사용자가 작업을 명시적으로 종료하는 흐름에서는 OpenAI 세션 삭제와 Worker의 실행기 정리 엔드포인트 호출을 모두 애플리케이션 로직에 포함해야 한다.
- 새 세션을 만들고 세션 ID, 환경 ID와 Container 시작 로그를 하나의 상관관계로 조회한다.
- 작업 중 in progress 이벤트가 수명주기 마감 시간을 갱신하고 Container가 유지되는지 확인한다.
- 유휴 상태 뒤 Container가 중지되는지 확인하고, 스냅샷을 지원하는 계정에서는 후속 입력으로 /workspace가 복원되는지 시험한다.
- 스냅샷을 끈 구성에서는 새 작업 공간으로 재연결돼도 애플리케이션이 정상 동작하는지 검사한다.
- 잘못된 웹훅 서명, 다른 에이전트 소유의 세션, Container 비정상 종료와 세션 조회 404를 각각 주입해 거부·경고·정리 결과를 구분한다.
- 정상 종료 경로에서 OpenAI 세션과 Cloudflare 실행기를 모두 명시적으로 삭제한다.
운영 로그에는 웹훅 수신과 서명 검증 결과, 세션 상태 조회, Container 시작·중지 사유와 종료 코드를 남기되 비밀 값은 제외한다. 준비 상태는 /health, 에이전트 진행은 세션 이벤트 스트림, 인프라 상태는 Worker와 Container 로그로 나누면 실패한 책임 경계를 빠르게 좁힐 수 있다.
뉴스레터 구독
최신 Web3, AI, 암호화폐 뉴스를 이메일로 받아보세요.