EC2 안 Firecracker VM으로 브라우저를 1초 미만에 시작하기

6 hours ago 3
  • Browser Use Cloud는 브라우저 세션마다 개별 Firecracker VM을 쓰면서 새 세션 시작 시간을 1초 미만으로 낮추고 비용을 브라우저 시간당 $0.06에서 $0.02로 줄임
  • 이전 Unikraft 구조는 유휴 비용에는 유리했지만, 트래픽 급증 때 사람이 용량을 조정해야 해 부하 테스트 중 프로덕션이 45분간 중단
  • 새 구조는 자체 control plane이 브라우저 플릿을 실시간으로 감시해 EC2 호스트 배치, 확장, 드레이닝을 CloudWatch보다 더 세밀하게 결정함
  • 정규 EC2 위에서 Firecracker를 실행하는 중첩 가상화를 택한 대신, 2MB 메모리 페이지, userfaultfd, vCPU 고정, real-time priority, headless Chromium 패치로 병목을 줄임
  • VM cold start는 400ms 미만이고, 10,000세션 스트레스 테스트에서 공개 API 기준 브라우저 생성 지연은 p50 825ms, p99 1.35초였으며 모든 브라우저가 성공적으로 시작됨

빠르고 격리되고 저렴한 클라우드 브라우저

  • Browser Use Cloud의 목표는 브라우저를 빠르게 시작하면서 세션 간 상태를 격리하고 비용을 낮추는 것임
  • 한 세션에는 Chromium, 파일시스템, 쿠키, 캐시, 프록시 설정, 다운로드, 때로는 로그인된 고객 세션까지 포함됨
  • 한 브라우저가 다른 브라우저의 상태를 읽을 수 있으면 보안 문제가 되므로, 각 세션을 호스트와 다른 세션에서 분리해야 함
  • 일반적인 해법은 자체 CPU, 메모리, 디스크, 네트워크 장치를 갖는 VM이지만, 계속 만들고 세션 종료 후 버리는 클라우드 브라우저 환경에는 너무 무겁고 비쌈
  • 새 구조는 모든 Browser Use Cloud 세션을 작은 Firecracker VM 하나에서 실행하고, 이 VM들을 Amazon EC2 위에서 구동함

Unikraft를 떠난 이유

  • 이전 구조는 Unikraft로 클라우드 브라우저를 실행했음
  • Unikraft는 전체 Linux를 부팅하지 않고 목적에 맞게 만든 작은 이미지인 unikernel을 로드함
  • unikernel은 빠르게 시작하고, 사용자가 없을 때 종료할 수 있어 유휴 비용이 낮음
  • 병목은 트래픽 급증 때 브라우저 용량을 빠르게 늘리는 일이었음
    • Unikraft에는 좋은 내장 autoscaling이 없었음
    • 엔지니어가 변수를 바꿔 인스턴스를 수동으로 추가해야 했음
    • 한 부하 테스트는 프로덕션을 45분간 중단시킴
  • 재구축 후에는 Firecracker를 사용함
    • Firecracker는 VM을 생성, 감시, 실행하는 계층을 제공함
    • 각 VM에 CPU, 메모리, 디스크, 네트워크 장치를 제공하고 호스트 및 다른 VM과 격리함

자체 control plane으로 브라우저 확장 자동화

  • Firecracker는 각 브라우저에 VM을 줄 수 있지만, VM 수와 배치, 확장 시점을 자동으로 결정하지는 않음
  • Browser Use는 자체 control plane을 만들어 브라우저 플릿을 감시하고 scale up/down을 결정함
  • 사용자가 브라우저를 요청하면 control plane이 여유 있는 머신을 선택함
  • 트래픽이 늘면 더 많은 머신을 시작하고, 줄어들면 제거할 머신에는 새 브라우저를 보내지 않음
  • control plane은 플릿을 실시간으로 확인함
    • AWS 모니터링 서비스인 CloudWatch는 보통 1분 단위 창으로 반응함
    • control plane은 아직 시작 중인 브라우저, 제거 중인 머신, 새 세션을 받으면 안 되는 머신처럼 일반 지표에 없는 정보를 알고 있음
  • 사용자 요청은 stateless edge router를 거쳐 control plane으로 전달되고, control plane은 여유 있는 EC2 호스트를 고름

정규 EC2 위에서 Firecracker를 실행한 이유

  • AWS에서 Firecracker를 실행하는 일반적인 방식은 .metal 인스턴스를 쓰는 것임
    • .metal은 물리 서버 전체를 빌려 Firecracker가 직접 실행되는 구조임
  • Browser Use는 정규 EC2를 선택함
    • 정규 EC2 머신은 더 빨리 확보할 수 있음
    • 유지 비용이 더 낮음
    • 호스트는 미리 만든 이미지에서 부팅하고 시작 후 약 30초 만에 브라우저를 제공함
  • 호스트를 더 빨리 추가할 수 있으면 비용을 내야 하는 유휴 용량이 줄고 고객에게 전가되는 비용도 낮아짐
  • 대가는 VM 안의 VM 구조임
    • 정규 EC2 자체가 이미 AWS의 격리 계층 안에서 실행됨
    • 그 안에서 다시 브라우저 VM을 실행함
    • 브라우저 VM이 호스트 도움을 요청할 때 두 VM 계층을 지나야 하므로 지연이 추가됨
  • Browser Use는 더 빠른 scale-up과 낮은 비용을 위해 이 절충을 선택하고, Firecracker 실행 경로를 빠르게 만드는 데 집중함

요청에서 사용 가능한 브라우저까지의 흐름

  • 사용자가 브라우저를 요청하면 control plane이 여유 있는 머신을 고름
  • 해당 머신은 저장된 브라우저 VM을 복원하고, 그 안에서 Chromium을 시작함
  • Chromium이 제어 가능한 상태가 되면 연결 URL을 반환함
  • 사용자의 에이전트는 이 URL로 접속함
  • Browser Use는 WebSocket을 통해 Chrome DevTools Protocol(CDP) 로 Chromium을 제어함
    • CDP는 버튼 클릭, 텍스트 입력, 페이지 읽기, 스크린샷 촬영 같은 Chrome 원격 제어 API임
  • 지연을 만든 주요 병목은 세 가지였음
    • VM 메모리 복원
    • Chromium 시작
    • anti-bot 보안에 탐지되지 않는 stealth 유지

첫 번째 병목: 메모리 복원

  • 프로덕션 브라우저는 처음부터 부팅하지 않고 snapshot에서 재개함
  • snapshot은 이미 부팅되어 있고 Chromium 시작 직전에 멈춰 있는 저장된 VM임
  • VM 재개는 새 부팅보다 빠르지만, 초기 cold start에서는 page fault가 전체 VM exit의 72% 를 차지함
  • VM 재개부터 CDP-ready 브라우저까지 걸린 시간은 처음에 9.8초였음
  • 느린 이유는 복원된 VM이 메모리를 처음 만질 때 호스트가 해당 메모리를 다시 매핑해야 하기 때문임
    • 이 이벤트가 page fault임
    • 중첩 VM에서는 page fault가 두 VM 계층을 지날 수 있어 비쌈
  • 해결책은 메모리를 더 큰 단위로 매핑하는 것이었음
    • 이전에는 4KB 페이지로 복원함
    • 지금은 2MB 페이지를 사용함
    • 각 페이지가 512배 더 많은 메모리를 덮어 브라우저가 깨어나는 동안 page fault가 크게 줄어듦
  • Linux의 누락 메모리 페이지 처리 API인 userfaultfd에 대한 custom handler도 추가함
    • VM 실행 전에 Chromium이 먼저 접근할 가능성이 큰 메모리를 로드함
    • Chromium 시작 시 page fault 폭주를 막음
  • 이 변경으로 VM 재개부터 명령 수락 가능한 브라우저까지 걸린 시간이 9.8초에서 3.1초로 줄어듦
  • 누락 메모리 처리를 위해 브라우저 VM이 멈추고 호스트에 요청한 횟수는 재개당 약 100,000회에서 약 1,100회로 줄어 약 91배 감소함
  • 작은 최적화도 누적됨
    • 존재하지 않는 오래된 PS/2 키보드를 찾느라 쓰던 500ms 검사를 비활성화함
    • 준비 상태 확인을 HTTP polling에서 vsock 로그 읽기로 바꿈
    • 브라우저 드라이버가 로그에 ready 메시지를 쓰면 호스트가 vsock으로 읽고, ready 메시지를 1ms 미만에 확인함

두 번째 병목: Chromium 시작과 CPU 배치

  • Chromium 시작 시에는 renderer, compositor, V8 isolate를 한꺼번에 만들기 때문에 CPU 사용량이 큼
  • 시작 이후의 브라우저 자동화는 상대적으로 조용함
    • 에이전트는 클릭하고 기다리고 읽고 다시 클릭함
    • 브라우저는 대부분 페이지, 네트워크 응답, 다음 에이전트 동작을 기다림
  • 이 특성 때문에 한 호스트에 많은 브라우저를 packing할 수 있음
  • 시작 순간의 CPU burst는 두 단계로 처리함
    • 브라우저가 재개되고 Chromium이 시작되는 동안에는 vCPU를 unpinned 상태로 둠
    • Linux가 브라우저 CPU 작업을 고정 코어에 묶지 않고 호스트 전반에 분산할 수 있음
    • 브라우저가 ready 상태를 보고하면 vCPU를 안정적인 코어에 pinning함
  • 시작부터 pinning하면 여러 브라우저가 동시에 시작될 때 같은 hot core에 몰려 일부 launch가 실패함
  • hyperthread 처리도 조정함
    • 물리 CPU 코어 하나는 종종 sibling thread라는 두 논리 CPU로 보임
    • 두 브라우저 VM이 각각 sibling 하나씩 받으면 같은 물리 코어를 놓고 경쟁함
    • 중첩 환경에서는 이 경쟁이 launch 실패로 나타남
    • 현재 각 브라우저는 사용하는 물리 코어의 sibling thread 둘 다 받음
  • pinning된 각 vCPU thread에는 real-time priority를 부여함
    • Linux가 덜 중요한 작업 뒤에 VM을 멈춰 두지 않고 즉시 실행하게 함
    • 변경 전 1,000브라우저 테스트에서는 생성 직후 세션의 17%가 손실됨
    • 변경 후 같은 테스트에서는 손실이 0이었음

화면 없는 stealth 브라우저

  • headless 브라우저는 보이는 창 없이 실행되고, headful 브라우저는 노트북의 브라우저처럼 창과 그래픽, 렌더링 프레임을 갖고 실행됨
  • plain headless Chromium은 anti-bot 조치를 쓰는 웹사이트에서 탐지되기 쉬움
  • Browser Use의 stealth benchmark에 따르면 plain headless Chromium은 차단을 피한 비율이 2% 였음
  • 같은 Chromium을 보이는 창이 있는 headful 상태로 실행하면 렌더링만으로 차단 회피율이 50% 가 됨
  • 많은 제공자가 headful 브라우저를 실행하는 이유는 이 때문이며, 화면을 보는 사람이 없어도 display server, GPU, compositor 비용을 냄
  • Browser Use는 브라우저 자체를 바꿔 완전한 headless 실행을 유지함
  • 첫 번째 구성요소는 Chromium fork
    • 많은 stealth 도구는 브라우저 시작 후 모든 페이지에 JavaScript를 주입해 자동화를 숨김
    • 예를 들어 navigator.webdriver 값을 덮어써 웹사이트가 true 대신 false를 보게 함
    • 웹사이트는 이런 값이 덮어써졌는지 탐지할 수 있음
    • Browser Use는 Chromium의 낮은 수준을 패치해 이런 값이 처음부터 노출되지 않게 함
  • 두 번째 구성요소는 fingerprinting
    • 브라우저 fingerprint는 웹사이트가 읽는 브라우저와 머신 정보의 조합임
    • 운영체제, 화면 크기, 폰트, 그래픽 출력, 오디오, 시간대, 언어 등 수백 가지 세부 정보가 포함됨
    • Browser Use는 macOS, Windows, Linux 전반의 실제 fingerprint 수만 개를 사용함
  • 이 브라우저들은 stealth benchmark에서 81%, Halluminate BrowserBench에서 84.8% 의 차단 회피율을 기록함
  • 화면이 없기 때문에 브라우저 실행 비용이 낮고 확장도 쉬움

올바른 브라우저에 연결하기

  • 브라우저가 준비되면 사용자는 CDP로 연결함
  • 공개 URL은 WebSocket URL
  • 브라우저 플릿 앞에는 단순한 edge router들이 있음
  • router는 WebSocket 연결을 받고 control plane에 해당 브라우저 위치를 물은 뒤, raw CDP 바이트를 올바른 VM으로 전달함
  • router는 브라우저가 어디서 실행될지 결정하지 않음
  • router 하나가 죽어도 다른 router가 새 연결을 맡을 수 있음
  • 배치는 control plane이 담당하고, router는 바이트 전달만 수행함

결과와 다음 단계

  • 현재 각 브라우저 세션은 정규 EC2 안에서 실행되는 작은 VM snapshot에서 재개되고, 그 안에서 headless Chromium이 실행되는 구조임
  • VM cold start는 400ms 미만
  • 공개 API 기준 브라우저 생성 지연은 p50 825ms, p99 1.35초
  • 이 수치는 모든 브라우저가 성공적으로 시작된 10,000세션 스트레스 테스트에서 측정됨
  • BrowserArena의 독립 leaderboard는 Browser Use를 $0.02/hr와 100% reliability로 1위에 올림
  • 이 인프라에서 가장 큰 남은 비용은 Chromium 자체임
    • resume 이후 Chromium 시작은 여전히 p50 기준 약 545ms가 걸림
  • 현재 snapshot은 Chromium 시작 직전 시점에 만들어짐
    • 모든 브라우저가 동일하고 깨끗한 지점에서 깨어난 뒤 각자 Chromium을 시작함
  • 다음 단계는 Chromium이 이미 실행된 뒤에 snapshot을 만드는 것임
    • 새 세션이 브라우저를 시작하지 않고 이미 살아 있는 브라우저와 함께 깨어날 수 있음
  • 이 작업은 복잡함
    • 실행 중인 브라우저에는 열린 장치, 타이머, 그래픽 상태, 네트워크 상태, fingerprint 상태가 있음
    • freeze 전에 이 상태들을 안전하게 만들어야 함
    • restore 후 각 브라우저는 이전 브라우저의 복제본이 아니라 자기만의 브라우저처럼 보여야 함
  • Browser Use Cloud는 cloud.browser-use.com에서 현재 사용 가능함
Read Entire Article