에이전트 수백만 개를 쿠버네티스처럼 굴린다: google/ax(Agent Executor) 심층 분석 — Task·Workspace·Model 3원소, Postgres 상태 분리, PID 1 러너 계약과 클러스터 없이 잰 러너 스모크 테스트

한 줄 요약: google/ax(Agent Executor, 줄여서 AX)는 코딩 에이전트 같은 “자율 에이전트 작업”을 쿠버네티스 위에서 선언형 워크로드로 다루게 해 주는 Go 기반 오케스트레이터입니다. Task·Workspace·Model 세 가지 리소스와 kubectl을 닮은 ax CLI로 샌드박스 생성, 작업 공간 준비, 일시정지·재개(suspend/resume), 내부 접속(ax ssh)을 처리합니다. 오늘(10/31) GitHub Trending Go 일간 목록에 올라왔고(당일 +47), 스타는 약 1.44만입니다. 이 글은 저장소 구조와 설계 판단, 러너 계약, 설치 경로, 평가 방법, 비슷한 도구와의 차이, 그리고 GPU도 쿠버네티스도 없는 8코어 박스에서 러너만 떼어 내 직접 잰 스모크 테스트 결과를 정리합니다.

google/ax의 구조: Task·Workspace·Model 매니페스트를 ax apply로 ax-server(gRPC)에 넣으면 PostgreSQL에 상태를 저장하고 Agent Substrate와 직접 조정해, ax-task-runner가 PID 1인 샌드박스들을 띄운다
google/ax의 구조: Task·Workspace·Model 매니페스트를 ax apply로 ax-server(gRPC)에 넣으면 PostgreSQL에 상태를 저장하고 Agent Substrate와 직접 조정해, ax-task-runner가 PID 1인 샌드박스들을 띄운다

1. 지금 왜 AX인가: 에이전트는 “새로운 종류의 워크로드”

README는 문제를 이렇게 정의합니다. 에이전트는 상태 없는 마이크로서비스도 아니고, 한 번 돌고 끝나는 배치 잡도 아닙니다. 상태가 쌓이고, 엄격한 격리가 필요하고, 모델 API와 도구 서버를 호출하고, 아무도 보지 않으면 루프를 돌며 돈을 태울 수 있습니다. 그래서 AX는 “에이전트 프레임워크”가 되기를 피하고 실행 기반(executor)에만 집중합니다. 에이전트가 계획하고, 위임하고, 재시도하고, 일을 쪼개는 모양 자체는 모델링하지 않습니다. 대신 싸게 만들고, 격리하고, 멈추고, 버릴 수 있는 원소 하나(Task)를 주고, 에이전트가 필요한 만큼 조합하게 합니다.

항목 확인한 값 (2026-10-31 KST 기준)
저장소 google/ax: “Agent Executor (AX) is Google’s open agentic orchestration runtime”
라이선스 Apache-2.0 (로고 파일은 Apache-2.0 또는 CC-BY-4.0 중 선택)
언어 · 모듈 Go (go 1.27.1 요구), 모듈 github.com/google/ax
최신 릴리스 v0.3.1 (2026-09-25 22:25 KST). main은 그 뒤로 47커밋 앞서 있음
스타 · 포크 약 14,383 · 768 (GitHub API)
트렌딩 GitHub Trending Go 일간 23위, 당일 +47
생성 · 최근 푸시 2026-03-31 생성, 2026-10-31 08:25 KST 푸시 (f0228f1: Reconcile을 명시적 Suspend/Resume으로 교체)
기여자 24명 (API 첫 페이지 기준)
성숙도 README 첫 줄에 “heavy development” 경고. 안정 릴리스 전 큰 호환성 깨짐 예고, API는 ax.io/v1alpha1
필수 의존 쿠버네티스 + agent-substrate/substrate(Apache-2.0, 약 6.0k 스타) + PostgreSQL + 컨테이너 레지스트리

2. 세 가지 리소스: Task · Workspace · Model

모든 리소스는 atespace라는 이름공간 안에 있고, 기본값은 default입니다. 쿠버네티스 네임스페이스와 비슷하지만 Agent Substrate 쪽 개념입니다. 이름과 atespace는 Substrate 리소스 이름이 되므로 소문자 RFC 1123 라벨(최대 63자)이어야 합니다.

리소스 역할 핵심 필드 설계 포인트
Task 가장 작은 격리 실행 단위 image, command, env, resources.limits, workspace, config, debug 생성 후 불변. 바꾸려면 새 Task. 한 Task가 작업 전체일 수도, 하위 Task 트리의 루트일 수도 있음
Workspace 파일시스템과 도구 환경을 선언형으로 준비 git[](repo·branch·dir·depth), files[], mcp(registries·servers), skills(registries·path) 한 번 선언해 여러 Task가 바인딩. Task당 바인딩은 최대 1개
Model 모델 provider·식별자·생성 파라미터·시크릿 참조 provider, model, secretKey 키 교체·모델 버전 고정을 ax apply 한 번으로. AX 자체 컴포넌트도 Model을 읽음

README의 예시는 “Go 저장소를 특정 브랜치로 클론하고 AGENTS.md를 넣어 둔 Workspace에, 코딩 에이전트 이미지를 돌리는 Task를 바인딩”하는 모양입니다.

apiVersion: ax.io/v1alpha1
kind: Workspace
metadata:
  name: golang
spec:
  git:
    - repo: https://github.com/golang/go.git
      branch: "my-fix"
  files:
    - path: "AGENTS.md"
      content: |
        - Run `go test` to ensure that tests are passing.
---
apiVersion: ax.io/v1alpha1
kind: Task
metadata:
  name: test
spec:
  image: "ghcr.io/my-org/my-coding-agent@sha256:..."
  config:
    goal: "Fix what's broken please."
  workspace:
    name: golang
  debug: true   # ax ssh 허용

Task 수명주기와 조건

status.phase는 Running, Suspended, Failed, Terminating 같은 한 단어 요약이고, 세부 사항은 조건(condition)에 있습니다. 기다려야 할 조건은 Ready 하나입니다. 설치 가이드에 따르면 새로 만든 Task는 먼저 Suspended 상태가 되고, resume해야 Running/Ready=True로 갑니다.

조건 · 사유 의미 운영에서 볼 때
WorkspaceReady 워크스페이스 준비 완료. 한 번 True면 계속 True 러너의 /readyz가 200이 되면 설정됨
Ready Task 실행 중이고 WorkspaceReady도 True 대기 조건은 이것 하나
Ready=False / Suspended suspend로 체크포인트 후 정지 resume 시 다시 True
TemplateCreationFailed Substrate ActorTemplate 생성 실패 태그만 쓴 이미지, 한도 거부 등
ActorResumeFailed 재개 실패 (wakeup probe 등) 워커 용량, 이미지 pull, 스냅샷 스토리지, 워커 로그 확인
WorkspaceInitializing 재개 후 최대 15초 준비 폴링 중 ax-server와 게스트·라우터 연결 확인

3. 아키텍처: 왜 CRD가 아니라 PostgreSQL인가

쿠버네티스에 익숙한 사람이 가장 먼저 묻는 질문은 “왜 CRD + 컨트롤러로 안 만들었나”입니다. DESIGN.md의 답은 분명합니다. 수백만 개의 단명 Task를 CRD로 저장하면 etcd가 감당하지 못한다는 것입니다(한 자릿수 GB 저장 한도, 쓰기 속도 병목, 컨트롤 플레인 저하). 그래서 AX는 상태를 PostgreSQL에 두고, 세밀한 분산 락 아래에서 Agent Substrate와 직접 조정합니다. 오늘 푸시된 커밋(#536)도 범용 Reconcile을 명시적인 Suspend/Resume으로 바꿨고, 그 직전 커밋(#532)은 Reconciler 인터페이스를 없앴습니다. “선언형 API, 직접 실행형 내부”로 가는 흐름입니다.

상태 분리: AX가 소유한 spec·metadata·자체 단계는 PostgreSQL에, actor의 phase·workerIP·Ready는 조회할 때마다 Agent Substrate에서 읽어 어긋날 사본을 두지 않는다. 수백만 단명 Task를 etcd(CRD)에 두지 않는 이유
상태 분리: AX가 소유한 spec·metadata·자체 단계는 PostgreSQL에, actor의 phase·workerIP·Ready는 조회할 때마다 Agent Substrate에서 읽어 어긋날 사본을 두지 않는다. 수백만 단명 Task를 etcd(CRD)에 두지 않는 이유
컴포넌트 역할 메모
ax 개발자 CLI. apply·get·describe·delete·suspend·resume·ssh·ctx·tunnel gRPC로 컨트롤 플레인 호출. 활성 kube 컨텍스트를 따라가며 백그라운드 터널 생성(~/.ax/tunnels)
ax-server gRPC API(포트 8080), 매니페스트 검증, 분산 락, Substrate 조정, Postgres 저장 GET /healthz는 같은 포트의 HTTP. 기동 시 테이블 자동 생성, 버전 마이그레이션은 아직 없음
ax-task-runner 모든 Task 컨테이너의 PID 1 runner 패키지를 감싼 얇은 래퍼. 커스텀 이미지가 패키지를 직접 임베드 가능
PostgreSQL 리소스를 protojson 문서로 저장 + 락 리스 락 대기자는 폴링. LISTEN/NOTIFY에 의존하지 않아 어떤 커넥션 풀러와도 동작
Agent Substrate atespace 프로비저닝, actor 생성·활성화, 워커 배정, 스냅샷 ate-system 네임스페이스, Control API api.ate-system.svc.cluster.local:443

설계에서 가장 눈여겨볼 부분은 상태 분리입니다. 스토어는 AX가 소유한 것만 가집니다. spec, metadata, 그리고 AX 자신의 작업을 설명하는 상태(Pending, Failed, Terminating, task ID, 워크스페이스 준비)입니다. 반면 Task의 actor에 속한 것(프로비저닝된 동안의 phase, actor, workerIP, Ready 조건)은 조회할 때마다 Substrate에서 읽습니다. 그러니 어긋날 사본 자체가 없습니다. 이 분리는 internal/server/server.go의 몇몇 헬퍼(getTask, listTasks, saveTask, updateTask, deleteTask)에 모여 있어서, 스토리지 구현은 Substrate를 몰라도 됩니다. 대가도 있습니다. ax get tasks가 매번 Substrate를 호출하므로, 목록 조회 비용과 지연이 Substrate API 상태에 직접 묶입니다.

gRPC API 표면은 ax.v1alpha1.AX 서비스 하나입니다. Task는 GetTask·ListTasks(페이지네이션)·CreateTask·DeleteTask·SuspendTask·ResumeTask이고, 불변이라 Update가 없습니다. Workspace와 Model은 Get·List·Update(생성 겸 수정, upsert)·Delete 네 가지입니다. 요청과 응답 타입은 <Method>Request/<Method>Response 규칙을 따릅니다.

4. 저장소 구조 읽기

경로 내용 읽을 때 포인트
cmd/ax, cmd/ax-server, cmd/ax-task-runner 세 바이너리 진입점 서버 플래그: --store(Postgres URL), --substrate-endpoint, --template, --default-task-image
cmd/runners/claude-code-runner Claude Code를 헤드리스로 돌리는 러너 예제 spec.config를 claude --print 플래그로 변환하고 Workspace MCP 서버를 --mcp-config로 연결
runner/ 공개 러너 패키지 (runner.Run, LoadSpec) 커스텀 러너의 출발점
pkg/apis/v1alpha1 ax.proto와 생성된 Go 타입, 검증 함수 ValidateTask, ValidateResources, 크기 한도 상수
internal/controller, server, substrate 조정 로직, API 서버, Substrate 클라이언트 substrate/substratetest에 페이크가 있어 클러스터 없이도 컨트롤러 테스트 가능
internal/store (memory, postgres), lock 스토어·락 인터페이스와 구현 Postgres 통합 테스트는 DB가 없으면 SKIP
internal/workspace 최초 1회 준비(maiden run): files, Git, skills 경로 마커 /ax/initialized-*, 로그 git-success.log/git-error.log
internal/guest, metadata, tunnel 게스트 서비스, 메타데이터 서버, CLI 터널 ax ssh 경로 이해에 필요
deploy/, Dockerfile.*, Makefile, demo.sh 매니페스트, 이미지 빌드, 데모 기본 러너 이미지는 python:3.12-slim + git·curl·openssh-client·procps·bash
skills/, AGENTS.md 저장소 자체에 대한 에이전트용 계약·스킬 10/30 추가 (#535)

5. 러너 계약: 샌드박스 안의 PID 1

AX의 확장성은 대부분 러너 계약에서 나옵니다. 컨트롤 플레인은 spec.command를 컨테이너 엔트리포인트로 쓰지 않습니다. 항상 /usr/local/bin/ax-task-runner를 띄우고 나머지를 러너에 맡깁니다. 그러니 이 경로에 실행 파일(심볼릭 링크나 셸 래퍼도 가능)만 있으면 어떤 언어로 만든 러너든 기본 러너처럼 취급됩니다.

러너 계약: 스펙 로드 → 포트 80 프로브·루프백 8099 메타데이터 → 최초 1회 워크스페이스 준비와 마커 → 명령 감독, SIGTERM 10초 유예와 suspend 시 /workspace 스냅샷, debug:true일 때만 게스트 서비스
러너 계약: 스펙 로드 → 포트 80 프로브·루프백 8099 메타데이터 → 최초 1회 워크스페이스 준비와 마커 → 명령 감독, SIGTERM 10초 유예와 suspend 시 /workspace 스냅샷, debug:true일 때만 게스트 서비스
AX가 넣어 주는 것 값
컨테이너 이미지 spec.image, 없으면 서버의 기본 러너 이미지
컨테이너 명령 항상 /usr/local/bin/ax-task-runner
AX_TASK_YAML status를 뺀 Task 실행 구성 YAML
AX_WORKSPACE_YAML 바인딩된 Workspace YAML (없으면 미설정)
볼륨 /workspace의 내구성 디렉터리 (suspend 시 스냅샷, resume 시 새 컨테이너로 복원)
준비 프로브 포트 80의 GET /readyz
러너 의무 세부 내용 어기면
포트 80 HTTP /healthz는 즉시 200, /readyz는 준비 전 503, 준비 후 200 (HTTP/1.1 + h2c) WorkspaceReady가 서지 않음
루프백 메타데이터 127.0.0.1:8099의 /metadata/v1alpha1/ax/task, /workspace(없으면 404), URL은 AX_METADATA_URL로 전달 SDK 없는 자기 설정 조회 불가
최초 1회만 준비 마커를 남기고 이후 부팅에서는 건너뜀 resume 때 재클론하면 에이전트 상태가 날아감
명령 감독 별도 프로세스 그룹, cwd는 워크스페이스, 명령이 끝나도 PID 1 유지 러너가 죽으면 메타데이터와 ax ssh도 같이 죽음
SIGTERM 처리 프로세스 그룹에 전달 → 유예(기본 러너 10초) → SIGKILL 스냅샷 전에 저장 못 한 상태 유실
게스트 서비스 spec.debug: true일 때만 gRPC Process·FileSystem 서비스 노출 임의 프로세스 실행과 파일 접근이 열림 (기본 off가 맞음)

spec.config는 러너 몫의 자유 형식 객체입니다. 컨트롤 플레인은 해석하지 않고, JSON 기준 16KiB 이하의 객체인지와 Task 전체 YAML이 31KiB 이하인지만 봅니다. 31KiB인 이유는 Task가 환경 변수 하나(AX_TASK_YAML)로 전달되고, Substrate가 값 하나를 32KiB로 제한하며, 1KiB를 컨트롤 플레인이 채울 필드용으로 남겨 두기 때문입니다. 평문으로 저장되고 ax describe에 그대로 보이므로 자격 증명은 넣으면 안 됩니다.

커스터마이즈 단계는 세 가지입니다. ① 기본 이미지를 FROM으로 확장해 도구만 추가, ② runner.Run을 임베드하고 OnCommandExit 훅으로 산출물 업로드나 웹훅 처리, ③ 아무 언어로나 계약만 지키는 러너를 새로 작성. 문서 권고대로 가장 얕은 단계부터 시작하는 게 맞습니다.

6. 네트워킹과 ax ssh

Task에는 자체 Service나 Ingress가 없습니다. 모든 요청은 Substrate의 atenet router(atenet-router.ate-system)를 지나고, 라우터는 ate-target-actor: <atespace>/<task> 헤더 하나로 대상 actor를 찾습니다. 대상이 일시정지 상태면 먼저 재개한 뒤 프록시합니다. gRPC라면 같은 키를 소문자 메타데이터로 보냅니다. ax ssh는 먼저 워커 IP의 TCP(기본 80)를 시도하고, 실패하면 443 라우터 mTLS로 갑니다. 설치 가이드는 “직접 TCP 탐색은 성공했는데 그 뒤 RPC가 실패하면 라우터로 재시도하지 않는다”는 함정도 적어 둡니다. Pod CIDR 겹침이나 프록시 환경에서 자주 보게 될 증상입니다.

# 클러스터 밖에서 Task의 readyz 보기
kubectl -n ate-system port-forward svc/atenet-router 8001:80
curl -H "ate-target-actor: default/task123" http://localhost:8001/readyz

7. 설치와 실행: 무엇이 먼저 있어야 하나

AX는 단독으로 돌지 않습니다. 순서는 쿠버네티스 → Agent Substrate(gVisor WorkerPool 포함) → PostgreSQL + ax-server → CLI입니다. 10/29에 추가된 버전 고정 설치 가이드(docs/installation.md)는 꽤 엄격합니다. Substrate 커밋을 고정하고, 스냅샷 스토리지를 클러스터 전역으로 설정하고(PerTemplateSnapshotLocation은 끄고), ax-server 서비스 계정 권한과 라우터 ingress SPIFFE ID를 허용하고, 이미지는 레지스트리 digest로 고정합니다. 워커 커널이 overlay의 lowerdir+를 지원하지 않으면 resume 단계에서 실패합니다.

# 1) CLI
go install github.com/google/ax/cmd/ax@latest      # 또는 GOBIN="$PWD/bin" make install

# 2) 이미지 빌드·푸시 (podman 또는 docker)
export AX_IMAGE_REPO=registry.example.com/your-team
make images CONTAINER_CLI=docker IMAGE_ARCH=amd64

# 3) 컨트롤 플레인 (Postgres StatefulSet + ax-server, ax-system 네임스페이스)
make deploy

# 4) 첫 Task
ax apply -f examples/simple.yaml
ax get tasks
ax resume task simple-task
ax ssh simple-task -- ls -la /workspace
ax suspend task simple-task
함정 증상 대응
태그만 쓴 이미지 (my-agent:v1) Ready=False, TemplateCreationFailed crane digest 등으로 @sha256: 고정. 태그는 움직여서 스냅샷을 무효화할 수 있음
resources.requests 지정 apply 거부 Substrate는 limits만으로 크기를 정함. 생략 시 기본 1 CPU, 512Mi
Suspended/Failed Task 다시 apply 불변이라 갱신 불가 delete 후 재생성
예제의 비공개 이미지 pull 실패 같은 체크아웃에서 직접 빌드한 러너 digest로 교체 (#424)
공개 Postgres 이미지·빌드 의존성 태그 재현성 저하 해결된 매니페스트, 소스 ref, 모듈 버전, 이미지 digest를 따로 기록

8. 박스 스모크 테스트: 쿠버네티스 없이 러너만 떼어 돌리기

GPU도 쿠버네티스도 없는 박스(8 vCPU)에서는 전체 흐름(Substrate + gVisor + 스냅샷)을 재현할 수 없습니다. 그래서 클러스터 없이 확인할 수 있는 것만 쟀습니다. main f0228f1, Go 1.27.2 기준입니다. 러너는 --task-file/--workspace-file과 --port/--metadata-port 플래그로 로컬 실행을 지원합니다. 다만 /ax와 /workspace를 하드코딩해 쓰므로, bwrap으로 두 경로를 임시 디렉터리에 바인드해 격리했습니다. 아래는 모두 실제로 잰 값이고, 단일 실행이라 네트워크 상태에 따라 달라질 수 있습니다.

박스 스모크 테스트(main f0228f1, 8 vCPU, 쿠버네티스 없음): 빌드 10.4초, 테스트 PASS 294·SKIP 5, 콜드 준비 2.69초 대 웜 0.03초, TERM 무시 명령 10.05초 후 강제 종료, 없는 저장소도 13.75초 뒤 Ready
박스 스모크 테스트(main f0228f1, 8 vCPU, 쿠버네티스 없음): 빌드 10.4초, 테스트 PASS 294·SKIP 5, 콜드 준비 2.69초 대 웜 0.03초, TERM 무시 명령 10.05초 후 강제 종료, 없는 저장소도 13.75초 뒤 Ready
go build -o bin/ ./cmd/ax ./cmd/ax-server ./cmd/ax-task-runner
go test -count=1 ./...
bwrap ... --bind state/ax /ax --bind state/ws /workspace \
  ax-task-runner --port 18080 --metadata-port 18099 \
  --task-file task.yaml --workspace-file ws.yaml
# ws.yaml: git chalk/chalk (main, depth 1) + files AGENTS.md + skills.path
측정 항목 결과 해석
3개 바이너리 빌드 real 10.4s (user 48.1s). ax 20.45MB, ax-server 27.95MB, ax-task-runner 20.92MB 정적 Go 바이너리. 러너 이미지 레이어 부담이 작음
go test ./... 전 패키지 ok, 19.1s. -v 기준 PASS 294(서브테스트 포함), SKIP 5 SKIP 5개는 모두 Postgres 통합 테스트(락 리스 갱신·만료 인계, 재시작 시 락 해제 등). DB 없이는 락 경로가 검증되지 않음
콜드 부팅 (클론 포함) 첫 HTTP 응답 0.03s, /readyz 503→200 2.69s 준비 시간은 사실상 Git 클론 시간
웜 부팅 (마커 존재 = resume 모사) /readyz 200까지 0.03s, 로그 “workspace already initialized; skipping” 재클론을 하지 않는 계약이 실제로 동작
러너 RSS 약 15.5~17.1MB (실행별), 잘못된 저장소 실행에서 19.4MB PID 1 오버헤드는 무시할 만한 수준
SIGTERM → 종료 정상 명령 0.003~0.007s, TERM을 무시하는 명령 10.047s(“did not exit within grace period; killing”) 문서의 10초 유예와 일치. 체크포인트 전에 저장할 시간은 최대 10초
메타데이터·환경 $AX_METADATA_URL로 spec.config까지 포함된 Task YAML 수신. cwd=/workspace, spec.env 전달 확인 SDK 없이 curl로 자기 설정 조회 가능
존재하지 않는 Git 저장소 5회 재시도 후 13.75s에 /readyz 200. git-error.log만 남고 마커는 생략 Ready가 “클론 성공”을 뜻하지 않음. 에이전트 쪽에서 클론 결과를 따로 확인해야 함

매니페스트 검증도 v1alpha1.ValidateTask를 직접 호출해 확인했습니다. 이 함수는 API 서버가 저장 전에 부르는 검증입니다.

입력 결과 메모
metadata.name: Bad_Name REJECT RFC 1123 라벨 규칙
태그만 쓴 이미지 ghcr.io/x/agent:v1 ACCEPT AX 검증은 통과하고, 거부는 나중에 Substrate 템플릿 생성 단계에서
resources.requests.cpu REJECT “only spec.resources.limits is applied”
limits.cpu: 0 / 1000 REJECT / REJECT 0 초과, 1000코어 미만
limits."nvidia.com/gpu" 파싱 오류 (unknown field) 현재 스키마는 cpu·memory만. GPU 에이전트는 아직 범위 밖
상대 경로 워크스페이스 path: work REJECT 절대 경로 필수
spec.config 15,000B / 17,011B ACCEPT / REJECT 한도 16,384B

덧붙여, 접속할 수 없는 --server를 주고 ax apply를 돌려 보면 Bad_Name 매니페스트도 클라이언트에서 미리 걸러지지 않고 Unavailable(dial 실패)로 끝났습니다. 이 빌드에서는 검증이 서버 쪽에서 일어난다는 뜻입니다. CI에서 매니페스트를 미리 검사하고 싶다면 위처럼 pkg/apis/v1alpha1를 불러 쓰는 작은 Go 프로그램을 두는 편이 낫습니다.

9. 비슷한 도구와 비교

“에이전트 샌드박스” 시장은 붐비지만, 대부분은 격리 실행 환경 자체를 팝니다. AX의 자리는 그 위, 대량의 에이전트 작업을 선언형으로 스케줄·준비·정지·재개하는 오케스트레이션 계층입니다. 격리는 아래의 Agent Substrate(gVisor 등)에 맡깁니다.

도구 층위 상태 저장 · 정지/재개 작업 공간 준비 AX와 비교
google/ax 오케스트레이터 (K8s + Substrate 위) Postgres + Substrate 스냅샷, suspend/resume Workspace 리소스(Git·files·MCP·skills) 선언 —
Kubernetes Job/Pod 범용 워크로드 CRD/etcd, 체크포인트 개념 없음 initContainer 등 직접 구성 AX가 etcd 한계를 이유로 피해 간 지점
kubernetes-sigs/agent-sandbox (Apache-2.0) K8s 확장, 격리된 상태ful 단일 워크로드 CRD 기반 직접 구성 “샌드박스 한 개”를 K8s답게. AX는 “수백만 Task + 작업 공간 + 모델” 묶음
e2b-dev/E2B (Apache-2.0) 에이전트용 보안 실행 환경, SDK 중심 샌드박스 단위 SDK로 코드에서 준비 개발자 SDK 경험 중심. AX는 클러스터 운영자 관점의 선언형 매니페스트
daytonaio/daytona AI 생성 코드 실행용 탄력 인프라 샌드박스 단위 API·SDK 관리형 사용성 쪽. AX는 자체 클러스터 전제
NVIDIA/OpenShell (Apache-2.0) 자율 에이전트용 안전 런타임 (정책·격리) 런타임 단위 정책 중심 단일 에이전트의 “무엇을 해도 되는가”. AX는 “어디서 얼마나 많이 돌리나”. 상호보완 쪽

10. 평가 방법: 도입 전 무엇을 재 볼 것인가

평가 축 측정 방법 통과 기준 예시
Task 생성 처리량 CreateTask gRPC를 병렬로 N개 → Suspended까지 지연 분포 p95가 목표 SLA 이내, Postgres 락 경합 로그 없음
resume 지연 suspend → resume → Ready=True까지 시간 (워크스페이스 크기별) 스냅샷 복원 시간이 워크스페이스 크기에 선형인지
상태 보존 suspend 전 /workspace에 쓴 파일과 git 상태 해시 비교 파일 동일, 재클론 없음 (마커 로그 확인)
밀도 워커당 동시 Running Task 수, 유휴 Task suspend 후 회수되는 CPU·메모리 로드맵의 유휴 자동 suspend 전까지는 수동·외부 정책 필요
장애 내성 ax-server 재시작·Postgres 페일오버 중 apply/resume 락 만료 인계 (DB 기반 테스트를 실제 DB로 실행)
보안 경계 debug:false에서 게스트 서비스 차단, 이그레스 정책, 시크릿 노출 여부 ax ssh 거부 확인, spec.config에 키 없음
클론 실패 처리 잘못된 저장소로 Task 생성 Ready만 믿지 않고 /ax/git-error.log나 에이전트 자체 검사로 감지

11. 트레이드오프와 한계

  • 무거운 전제 조건. 쿠버네티스, Agent Substrate(gVisor WorkerPool, 스냅샷 스토리지, SPIFFE/PodCertificate 신뢰 번들), Postgres, 사설 레지스트리가 모두 필요합니다. 노트북에서 가볍게 써 볼 도구는 아닙니다.
  • v1alpha1과 큰 변경 예고. README가 직접 경고합니다. 이번 주에만 Reconciler 제거, Suspend/Resume 분리, 빌드 체계 교체(ko → make images), Substrate v0.6.0 업데이트가 들어갔습니다. 릴리스(v0.3.1)와 main의 차이도 큽니다.
  • 마이그레이션 없음. 테이블을 기동 시 생성할 뿐이라, 스키마가 바뀌는 업그레이드는 직접 관리해야 합니다.
  • Ready의 의미가 좁음. 스모크 테스트에서 봤듯 Git 실패도 준비 완료로 보고됩니다. 마커가 없어서 다음 부팅 때 재시도되긴 하지만, 그 사이 에이전트는 빈 디렉터리에서 시작할 수 있습니다.
  • MCP·스킬 레지스트리는 아직 얇음. 이 커밋의 기본 러너 SetupWorkspace는 files·Git·skills 경로 생성까지만 합니다. Workspace의 MCP 서버를 에이전트에 실제로 연결하는 건 claude-code-runner처럼 러너 쪽 몫입니다(정적 서버와 레지스트리 항목 아래에 나열된 서버를 --mcp-config로 변환).
  • GPU 리소스 미지원. limits는 cpu·memory뿐이라, 로컬 모델을 샌드박스 안에서 GPU로 돌리는 구성은 현재 스키마 밖입니다. Model 리소스는 외부 provider 호출을 전제로 합니다.
  • 명령 종료 코드를 컨트롤 플레인이 읽지 않음. 러너 문서에 명시돼 있습니다. 성공·실패 판정과 산출물 수거는 OnCommandExit 훅이나 외부 파이프라인이 맡아야 합니다.
  • 로드맵에 남은 핵심 기능. 워크스페이스 준비를 별도 setup actor로 분리하는 최소 권한 정책, 유휴 감지 자동 suspend, Task 분기(fork), Task별 SPIFFE ID, OpenTelemetry 궤적 수집은 아직 계획 단계입니다.
공식 저장소 자료: AX 마스코트 로고(assets/axel.svg, Apache-2.0 또는 CC-BY-4.0)와 DESIGN.md의 아키텍처 도식(ax-server → PostgreSQL / Agent Substrate)
공식 저장소 자료: AX 마스코트 로고(assets/axel.svg, Apache-2.0 또는 CC-BY-4.0)와 DESIGN.md의 아키텍처 도식(ax-server → PostgreSQL / Agent Substrate)

12. 누가 지금 써 볼 만한가

이미 쿠버네티스를 운영하면서 코딩 에이전트나 평가(eval) 작업을 수백~수천 개 단위로 병렬로 돌리고, “에이전트가 일하던 상태 그대로 멈췄다가 다시 이어 가기”가 비용 절감의 핵심인 팀이라면 AX의 설계(불변 Task, 상태 분리, 스냅샷 기반 suspend/resume, 러너 계약)는 그대로 참고할 가치가 있습니다. 반대로 에이전트 몇 개를 안전하게 돌리는 게 목표라면 SDK형 샌드박스나 단일 런타임 쪽이 훨씬 가볍습니다. 당장 도입하지 않더라도 runner 패키지와 docs/runner.md의 계약(포트 80 프로브, 루프백 메타데이터, 최초 1회 준비 마커, 10초 유예 종료)은 어떤 에이전트 플랫폼을 설계하든 체크리스트로 쓸 만합니다.