웹을 탐색하는 AI 에이전트에는 격리된 브라우저 군단이 필요합니다. 여기 이를 위한 브로커리스 (Brokerless) 스케줄러가 있습니다.
요약
대규모 AI 에이전트를 위한 격리된 브라우저 인스턴스 관리 스케줄러인 Machineuse를 소개합니다. 이 플랫폼은 컨테이너 기반의 격리 환경을 제공하며, 브로커 없는 NNG 통신과 지능적 배치를 통해 효율적인 브라우저 군단 인프라를 구축합니다.
핵심 포인트
- systemd-nspawn 컨테이너를 통한 브라우저 인스턴스 격리
- Redis/RabbitMQ 없이 NNG를 사용하는 브로커리스 메시징 구조
- 노드 성능과 부하를 고려한 지능적 작업 배치 방식
- 스냅샷 기반의 휴면(Dormancy) 기능을 통한 리소스 최적화
- 에이전트 프레임워크가 아닌 인프라 계층에 집중한 설계
AI 에이전트에게 브라우저 하나를 주는 것은 쉽습니다. 하지만 백 개의 에이전트에게 각각의 브라우저를 준다면, 그것은 인프라 문제입니다.
대규모 브라우저 자동화 (Browser automation)는 까다로운 형태를 띱니다. 각 브라우저는 실제 리소스, 실제 파일 시스템, 그리고 실제 격리 (Isolation)를 필요로 합니다. 왜냐하면 신뢰할 수 없는 페이지를 실행하거나, 저항하는 스크래핑 대상(scraping targets)을 처리하거나, 쿠키를 공유하고 싶지 않은 에이전트 세션을 운영하기 때문입니다. 따라서 단순히 수천 개의 스레드를 생성할 수는 없습니다. 결국 컨테이너를 관리하게 되고, 그 컨테이너가 실행되는 머신을 관리하게 되며, 결국 스케줄러 (Scheduler)를 작성하게 됩니다.
Machineuse는 정확히 이러한 워크로드(workload)를 위해 구축된 스케줄러입니다. 이는 여러 워커 노드 (Worker nodes)에 걸쳐 격리된 브라우저 인스턴스를 생성, 스케줄링 및 관리하며, 로드 밸런싱 (Load balancing)과 유휴 인스턴스로부터 리소스를 회수하기 위한 스냅샷 기반의 휴면 (Dormancy) 트릭을 갖춘 Python 플랫폼입니다. 이 포스트에서는 작업이 어떻게 배치되는지, 휴면이 어떻게 작동하는지, 그리고 설계가 어떤 제약을 주는지 다룹니다.
이 프로젝트는 chromium, browser-automation, mcp 태그를 사용하고 있으므로, 에이전트-브라우저 군단(agent-browser fleet)이 바로 이 프로젝트가 염두에 둔 워크로드입니다. 다만 한 가지 정확히 짚고 넘어갈 점은, machineuse는 컨테이너를 스케줄링하고 격리한다는 것입니다. 그 내부에서 실행되는 것, 즉 브라우저와 여러분의 자동화 또는 에이전트 코드는 여러분의 몫입니다. 이것은 에이전트 프레임워크 (Agent framework)가 아니라 군단 인프라 (Fleet infrastructure)입니다. 그 분리가 핵심입니다. 여러분은 에이전트를 가져오고, 이 시스템은 머신을 가져옵니다.
핵심 아이디어
단위는 전용 리소스를 가진 systemd-nspawn 컨테이너 내부에서 실행되는 격리된 브라우저 인스턴스입니다. 플랫폼의 역할은 어떤 노드가 각 인스턴스를 실행할지 결정하고 군단의 상태를 건강하게 유지하는 것입니다.
두 가지 설계 결정이 이를 정의합니다.
첫째, 메시징은 외부 브로커 (Broker)가 없는 순수 NNG입니다. 중간에 Redis나 RabbitMQ가 없습니다. 노드들은 NNG 소켓을 통해 제어 평면 (Control plane)과 직접 통신합니다. 이는 운영해야 할 상태 저장 시스템 (Stateful system)이 하나 줄어들고, 장애가 발생할 요소가 하나 줄어든다는 것을 의미합니다.
둘째, 배치는 라운드 로빈 (Round-robin) 방식이 아닌 지능적으로 이루어집니다. 스케줄러는 노드의 성능 (Capabilities)과 현재 부하 (Load)를 기반으로 인스턴스를 배치하므로, 성능이 낮은 노드에 제대로 처리할 수 없는 작업이 할당되지 않습니다.
아키텍처가 결합되는 방식
두 가지 역할이 있습니다. 컨트롤 플레인 (Control plane)은 조정 역할을 수행하고, 워커 노드 (Worker nodes)는 컨테이너를 실행합니다. 먼저 NNG 주소에 바인딩된 컨트롤 플레인을 시작한 다음, 워커들을 해당 주소로 지정하면 됩니다.
# 컨트롤 플레인 시작
python -m machineuse.nodes.control_plane --bind tcp://*:5000
...
저장소는 역할에 따라 분리되어 있으며, 이는 합리적인 선택입니다. 각 노드는 로컬에서 SQLite를 사용합니다. 컨트롤 플레인은 공유 메타데이터를 위해 PostgreSQL을 사용할 수 있습니다. 메트릭 (Metrics) 측면의 분석에는 DuckDB가 사용됩니다. 따라서 로컬 핫 패스 (Hot path)는 임베디드 (Embedded) 방식으로 단순하게 유지되는 반면, 공유 상태 (Shared state)와 분석에는 실제로 필요한 곳에만 더 무거운 저장소를 사용합니다.
신뢰성은 자동 치유 (Auto-healing) 기능을 통해 확보됩니다. 플랫폼은 장애를 감지하고 문제가 발생한 노드에서 인스턴스를 마이그레이션 (Migrate)합니다. 실시간 메트릭은 시계열 분석 (Time-series analytics)으로 이어져, 추측하는 대신 클러스터 전체의 사용률을 확인할 수 있습니다.
스냅샷 휴면 (Snapshot dormancy) 작동 방식
이것이 바로 비용을 지불할 가치가 있는 핵심 기능입니다. 현재 활발하게 사용하지 않는 브라우저 인스턴스도 여전히 메모리와 CPU를 점유합니다. 이를 전체 플릿 (Fleet) 단위로 곱하면, 유휴 (Idle) 인스턴스가 비용의 주된 원인이 됩니다.
Machineuse는 스냅샷 휴면 (Snapshot dormancy)으로 이를 해결합니다. 인스턴스를 일시 중지하고 파일 시스템 스냅샷을 찍어 라이브 리소스를 해제한 다음, 다시 필요할 때 스냅샷으로부터 인스턴스를 복구합니다. 따라서 나중에 필요할 수도 있는 인스턴스가 계속 상주 (Resident)할 필요가 없습니다. 인스턴스는 휴면 상태로 들어가 리소스를 반환하고, 호출될 때 다시 돌아옵니다.
CLI에서는 단 두 개의 명령어로 수행할 수 있습니다.
# 인스턴스를 수면 상태로 전환하여 리소스 회수
machineuse-cli dormant <id>
...
나머지 CLI는 라이프사이클 (Lifecycle) 및 플릿 관리를 위해 예상할 수 있는 기능들로 구성되어 있습니다.
# 인스턴스 라이프사이클
machineuse-cli create --image ubuntu:22.04
machineuse-cli list --node worker-1
...
코드에서 사용하기
클러스터와 통신하는 Python 클라이언트와 8000번 포트의 REST API가 제공됩니다. 라이브러리 경로는 스케줄링 결과를 직접 반환하기 때문에 대부분의 자동화(automation) 작업에서 사용하기에 적합합니다.
from machineuse.client import ClusterManager
# 분산 클러스터에 연결
...
REST 인터페이스도 이를 반영합니다. 이미지와 설정을 포함한 POST /v2/instances는 인스턴스를 생성하고, GET /v2/instances는 인스턴스 목록을 나열하며, GET /health는 준비 상태(readiness)를 확인합니다. 배포는 Docker Compose를 사용하며, 단일 노드용 docker-compose.yml과 컨트롤 플레인(control plane), 워커(workers), PostgreSQL을 포함하는 docker-compose.distributed.yml이 제공됩니다.
적합하지 않은 경우
systemd-nspawn은 Linux, systemd, 그리고 root 권한을 의미합니다. README에 명시되어 있듯이, systemd-nspawn을 지원하는 Ubuntu 또는 Debian 시스템, Python 3.11 이상, 그리고 컨테이너 관리를 위한 root 또는 sudo 권한이 필요합니다. 이 도구는 macOS에서 실행되지 않으며, rootless(비루트) 모드로도 실행되지 않습니다. 만약 사용 중인 자동화 인프라가 직접 호스팅하는 systemd Linux가 아니라면, 이 도구는 적절하지 않습니다.
노드당 상한선이 존재합니다. MACHINEUSE_MAX_INSTANCES의 기본값은 노드당 50개의 컨테이너입니다. 이는 합리적인 기본값이며, 동시에 확장 모델(scaling model)을 시사합니다. 즉, 한 서버에 억지로 밀어넣는 것이 아니라 노드를 추가함으로써 확장(scale out)해야 합니다. 용량을 계획할 때는 '노드당 인스턴스 수 × 노드 수'를 기준으로 삼아야 하며, 각 브라우저가 실제 리소스를 소비한다는 점을 기억하십시오.
휴면 상태(Dormancy)는 공짜 용량이 아니라 트레이드오프(trade-off)입니다. 디스크로 스냅샷(Snapshotting)을 찍고 다시 살려내는 데는 시간과 디스크 공간이 소요됩니다. 인스턴스가 일시 중지 및 복구 비용을 정당화할 만큼 충분히 오랫동안 유휴(idle) 상태로 있을 때만 이 방식이 이득이 됩니다. 빠르게 순환되는 인스턴스의 경우, 스냅샷 오버헤드가 절약되는 비용보다 더 클 수 있습니다. 이 방식은 교체 주기가 매우 빠른 일회성 인스턴스(high-churn throwaway instances)보다는, 간헐적으로 사용되는 대규모 세션 풀에 적합합니다.
Brokerless NNG는 조정(coordination)의 책임을 사용자에게 부여합니다. 외부 브로커가 없다는 것은 운영 측면에서 실질적인 이점이지만, 그 대신 제어 평면(control plane)이 반드시 가용성을 유지해야 하는 조정 지점이 됩니다. 이 시스템은 공유 메타데이터를 위해 PostgreSQL을 사용하므로, 플릿(fleet)에 대한 내구성(durability) 및 고가용성(HA) 전략은 곧 해당 데이터베이스와 제어 평면 프로세스에 대한 내구성 및 고가용성 전략과 동일해집니다.
핵심 요약 (Takeaways)
Machineuse는 하나의 실제적인 문제에 대한 집중적인 해답을 제시합니다: 스케줄러를 직접 구현하거나 메시지 브로커(message broker)를 구축하지 않고도, 여러 머신에 걸쳐 많은 격리된 브라우저를 실행하는 것입니다.
벤치마킹할 만한 아이디어는 스냅샷 휴면(snapshot dormancy)입니다. 유휴 상태인 무거운 인스턴스를 파일 시스템 스냅샷으로 일시 중지(pause)했다가 필요할 때 다시 활성화(revive)하는 대상으로 취급하는 것은, "존재하는 인스턴스"와 "현재 소비 중인 리소스"를 분리하는 깔끔한 방법입니다. 이는 유휴 용량을 비용이 아닌 디스크 상의 스냅샷으로 전환합니다.
Repo: https://github.com/dotcommoners/machineuse
만약 실제 규모로 브라우저 자동화를 운영하고 있다면, 단일 노드 Compose 스택을 구축하고, 몇 개의 인스턴스를 생성한 뒤, 자체적인 휴면 상태에서 활성화까지의 시간(dormant-to-revive time)을 측정해 보십시오. 스토리지에서의 실제 활성화 지연 시간(revive latency)이 휴면 방식의 효용성을 결정하는 수치이며, 바로 이 지점이 이슈(issue)를 제기할 만한 가치가 있는 부분입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기