EC2上のFirecracker VMでブラウザを1秒未満で起動する
(browser-use.com)- Browser Use Cloudはブラウザセッションごとに個別のFirecracker VMを使い、新規セッション開始時間を1秒未満まで短縮し、コストをブラウザ1時間あたり$0.06から**$0.02**へ削減
- 以前のUnikraft構成はアイドルコストには有利だったが、トラフィック急増時に人手で容量調整が必要で、負荷テスト中に本番環境が45分間停止した
- 新構成では独自のcontrol planeがブラウザフリートをリアルタイムで監視し、EC2ホストの配置、スケーリング、ドレインをCloudWatchより細かく判断
- 通常のEC2上でFirecrackerを動かすネストした仮想化を選ぶ代わりに、2MBメモリページ、
userfaultfd、vCPUピニング、real-time priority、headless Chromiumパッチでボトルネックを削減 - VMのコールドスタートは400ms未満で、10,000セッションのストレステストでは公開API基準のブラウザ生成レイテンシがp50 825ms、p99 1.35秒となり、すべてのブラウザが正常に起動
高速で分離され、低コストなクラウドブラウザ
- Browser Use Cloudの目標は、ブラウザを高速に起動しつつ、セッション間の状態を分離し、コストを下げること
- 1つのセッションにはChromium、ファイルシステム、Cookie、キャッシュ、プロキシ設定、ダウンロード、場合によってはログイン済みの顧客セッションまで含まれる
- あるブラウザが別のブラウザの状態を読めてしまうとセキュリティ問題になるため、各セッションをホストおよび他セッションから分離する必要がある
- 一般的な解決策は、専用のCPU、メモリ、ディスク、ネットワークデバイスを持つVMだが、継続的に生成してセッション終了後に破棄するクラウドブラウザ環境には重すぎて高価
- 新構成では、すべてのBrowser Use Cloudセッションを小さなFirecracker VM1台の中で実行し、これらの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がホストの支援を必要とすると、2つのVMレイヤーを通過するため遅延が追加される
- Browser Useはより速いscale-upと低コストのためにこのトレードオフを選び、Firecrackerの実行パス高速化に注力した
リクエストから利用可能なブラウザまでの流れ
- ユーザーがブラウザを要求すると、control planeが空きのあるマシンを選ぶ
- そのマシンが保存済みのブラウザVMを復元し、その中でChromiumを起動する
- Chromiumが制御可能な状態になると、接続URLを返す
- ユーザーのエージェントはこのURLに接続する
- Browser UseはWebSocket経由で**Chrome DevTools Protocol(CDP)**を使ってChromiumを制御する
- CDPは、ボタンのクリック、テキスト入力、ページ読み取り、スクリーンショット撮影などを行うChromeのリモート制御API
- レイテンシを生む主なボトルネックは3つあった
- VMメモリの復元
- Chromiumの起動
- anti-bot対策に検知されないstealthの維持
1つ目のボトルネック: メモリ復元
- 本番ブラウザは最初から起動せず、snapshotから再開する
- snapshotは、すでに起動済みでChromium起動直前に停止した保存済みVM
- VM再開は新規ブートより速いが、初期のコールドスタートではpage faultが全VM exitの**72%**を占めていた
- VM再開からCDP-readyなブラウザになるまでの時間は当初9.8秒だった
- 遅い理由は、復元されたVMが初めてメモリに触れる際、ホストがそのメモリを再マッピングしなければならないため
- このイベントがpage fault
- ネストしたVMではpage faultが2つのVMレイヤーを通過するため高コストになる
- 解決策は、より大きな単位でメモリをマッピングすることだった
- 以前は4KBページで復元していた
- 現在は2MBページを使用
- 各ページが512倍多くのメモリをカバーするため、ブラウザが起き上がる間のpage faultが大幅に減る
- Linuxの欠損メモリページ処理APIである
userfaultfd向けのcustom handlerも追加- VM実行前に、Chromiumが先にアクセスしそうなメモリを読み込む
- Chromium起動時のpage faultの集中を防ぐ
- この変更で、VM再開からコマンドを受け付け可能なブラウザになるまでの時間は9.8秒から3.1秒へ短縮
- 欠損メモリ処理のためにブラウザVMが停止してホストへ要求した回数は、再開1回あたり約100,000回から約1,100回へ減少し、およそ91倍の削減となった
- 小さな最適化も積み重なった
- 存在しない古いPS/2キーボードを探すために費やしていた500msのチェックを無効化
- 準備状態の確認をHTTP pollingから
vsockログ読み取りに変更 - ブラウザドライバがログにreadyメッセージを書き込むと、ホストが
vsockでそれを読み取り、readyメッセージを1ms未満で確認できる
2つ目のボトルネック: Chromium起動とCPU配置
- Chromium起動時はrenderer、compositor、V8 isolateを一斉に生成するためCPU使用量が大きい
- 起動後のブラウザ自動化は比較的静か
- エージェントはクリックし、待ち、読み、またクリックする
- ブラウザは大半の時間、ページ、ネットワーク応答、次のエージェント動作を待っている
- この特性により、1台のホストに多くのブラウザをpackingできる
- 起動瞬間のCPU burstには2段階で対応
- ブラウザが再開されChromiumが起動する間は、vCPUをunpinnedのままにする
- LinuxがブラウザのCPU処理を固定コアに縛らず、ホスト全体に分散できる
- ブラウザがready状態を報告したら、vCPUを安定したコアへpinningする
- 最初からpinningすると、複数ブラウザが同時起動した際に同じhot coreへ集中し、一部のlaunchが失敗する
- hyperthreadの扱いも調整
- 1つの物理CPUコアはしばしばsibling threadという2つの論理CPUとして見える
- 2つのブラウザVMがそれぞれ片方のsiblingを割り当てられると、同じ物理コアを奪い合う
- ネスト環境ではこの競合がlaunch失敗として現れる
- 現在は各ブラウザが使用する物理コアのsibling threadを両方とも受け取る
- pinningされた各vCPU threadにはreal-time priorityを付与
- Linuxが重要度の低い処理の後ろにVMを待たせず、すぐに実行できるようにする
- 変更前の1,000ブラウザテストでは、生成直後のセッションの17%が失われていた
- 変更後の同じテストでは損失は0だった
画面を持たないstealthブラウザ
- headlessブラウザは見えるウィンドウなしで動作し、headfulブラウザはノートPC上のブラウザのようにウィンドウ、グラフィック、レンダリングフレームを持って動作する
- plain headless Chromiumはanti-bot対策を使うWebサイトで検知されやすい
- Browser Useのstealth benchmarkによれば、plain headless Chromiumのブロック回避率は**2%**だった
- 同じChromiumを見えるウィンドウのあるheadful状態で動かすと、レンダリングだけでブロック回避率が**50%**になる
- 多くのプロバイダがheadfulブラウザを動かすのはこのためで、画面を見る人がいなくてもdisplay server、GPU、compositorのコストを負担している
- Browser Useはブラウザ自体を変更し、完全なheadless実行を維持している
- 1つ目の構成要素はChromium fork
- 多くのstealthツールはブラウザ起動後、すべてのページにJavaScriptを注入して自動化を隠す
- たとえば
navigator.webdriverの値を書き換え、Webサイトにtrueではなくfalseを見せる - Webサイトはこうした値の上書きを検知できる
- Browser UseはChromiumの低レベル部分をパッチして、こうした値が最初から露出しないようにしている
- 2つ目の構成要素はfingerprinting
- ブラウザfingerprintは、Webサイトが読み取るブラウザとマシン情報の組み合わせ
- OS、画面サイズ、フォント、グラフィック出力、オーディオ、タイムゾーン、言語など、数百の詳細が含まれる
- Browser UseはmacOS、Windows、Linux全体で実在する数万件のfingerprintを使用
- これらのブラウザはstealth benchmarkで81%、Halluminate BrowserBenchで**84.8%**のブロック回避率を記録した
- 画面がないため、ブラウザ実行コストが低く、スケールもしやすい
正しいブラウザへ接続する
- ブラウザの準備が整うと、ユーザーはCDPで接続する
- 公開URLはWebSocket URL
- ブラウザフリートの前段にはシンプルなedge routerがある
- routerはWebSocket接続を受け取り、control planeに該当ブラウザの位置を問い合わせたうえで、生のCDPバイト列を正しいVMへ転送する
- routerはブラウザをどこで動かすかを決めない
- 1台のrouterが落ちても、別のrouterが新しい接続を引き受けられる
- 配置はcontrol planeが担当し、routerはバイト転送だけを行う
結果と次のステップ
- 現在、各ブラウザセッションは通常のEC2内で動く小さなVM snapshotから再開され、その中でheadless Chromiumが実行される構成
- VMコールドスタートは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で利用できる
1件のコメント
Hacker Newsの意見
アンチボット回避をベンチマークとして掲げるのは、かなり非倫理的に見える。アンチボットの目的は望まれないボットを防ぐことなのに、この種のサービスは結局、Webを人間にとってより不親切で高コストなものにしてしまう
サイト側は自動化アクセスを引き続き阻止しようとするだろうし、コンテンツへのアクセス障壁はさらに増えるだろう。Webで本人確認の要求が強まっているのも、年齢制限や「子どもの保護」だけでなく、ボット対策や広告収益の保護まで含めた上位の効果に見える
APIがないサイトではスクレイパーも使うし、購入履歴全体をデータベースに索引して分析できるようにもしている。馬鹿げたボット検知を回避することにこれ以上時間を使いたくはないし、別の方法ではアクセスできないデータなら、喜んで費用を払うつもりもある。どうせスクレイパーが勝つしかないいたちごっこにリソースを燃やしているようなものだ
ただし、住宅用プロキシを提供するのは非倫理的である可能性が高い。そうしたプロキシの住宅回線提供者は、自分がそのようなサービスに組み込まれていることを知らない場合が多い
たとえばある公演のチケットを取るために24時間コンピュータの前に座っていられないとき、好きなバンドのチケットを買うために個人用ボットを使うのが非倫理的だとは思えない。逆に、転売目的なら非倫理的だという点には同意する。アンチボットへのアンチボットは、他人が自動化されるべきでないと考えることを可能にしようとするもので、Hacker Newsの読者のかなり多くが一度はこうしたことをしたことがある気がする。純粋に利益目的なら微妙だが、転売屋に対抗する機会を得るための用途なら問題ないように思う
複数のSaaSテナントのうちの1つにすぎず、CAPTCHAの削除を要求できるほど大口顧客でもないので、ただその制約を回避している
ここで抜けているのは、通常のEC2インスタンスでのネストされた仮想化が今年2月から可能になった点だ。それ以前は、Firecracker VMを実行するにはmetal EC2インスタンスを使う必要があった
ここまでやってもなおChromiumにこだわっているのは少し驚きだ
私たちのweb-access MCPサーバー[0]は、はるかに単純な構成でブラウザインスタンスを子プロセスとして立ち上げているが、安定性・CPU・メモリ使用量で最大の改善があったのは、ChromeからLightpanda[1]に切り替えたときだった。記事の最後にあるように、より速く起動するブラウザとは、そもそもメモリの割り当てが少ないブラウザなのかもしれない
[0]: https://github.com/EratoLab/web-access-mcp
[1]: https://lightpanda.io
LightPandaのようなブラウザにはステルス性がまったくなく、検知が非常に容易だ。不要なものを削ればChromiumももっと高速化できる余地がある。最初からエンジン全体を作り直さなくても、最優先事項であるステルス性を失わずに、Chromiumがその性能に到達できると見ている。言語が問題なのではなく、C++もZigと同じくらい高性能だが、Chromiumの肥大化が大きいことには同意する
ApiFlashというスクリーンショットAPIを運営しているが、EC2のFirecrackerの代わりに、AWS LambdaコンテナイメージにChromiumをパッケージして使っている
AWS Lambdaは隔離と自動スケーリングを無料で提供するので、スクリーンショットのような負荷が跳ねるステートレス作業に理想的だ。browser-useソリューションとほぼ同じ利点を得ながら、アーキテクチャはずっと単純だと思う。トレードオフはAWS Lambdaのコールドスタートだが、実際には連続呼び出しでウォーム状態の関数が再利用される。十分な呼び出し量があればピークは平準化され、コールドスタートもそれほど頻繁には起きない
Lambdaで経験した問題は、実行時間制限が15分であること、価格、スナップショット機構がないこと、実行ホストに対する低レベル制御が不足していることだった。私たちは最大4時間をサポートしており、必要ならさらに長く実行できる。それでも、たいていの一般的なWeb自動化のユースケースではLambdaで十分すぎるほどだ
「次は: Chromiumの起動をスキップ」と書いてあるが、すでに起動済みのブラウザの束をウォームプールとして維持し、流入するリクエストに割り当てればよいのではないかと思う
ユーザーの立場ではレイテンシはほぼゼロに近くなるだろう。トラフィックパターンに応じてウォームプールを増減させる予測ロジックは必要だろうが、最も簡単な解決策のように見える
ウォームプールは有効だが、結局はリソースを消費し、プールを常にウォームな状態に保ってバランスを取るためにブラウザを起動し続ける必要がある。今後の変更でChromiumの起動を維持しつつ、VMを50ms以内に準備できるようにして、ウォームプールを完全に上回るつもりだ。一部の顧客は特殊なパラメータや機能を必要とするため、ウォームプールの複雑さが増す。通常経路は速くても、例外経路は非常に遅くなる可能性があるが、要求されたブラウザにどんな機能が必要であっても高速性を保証したい
Firecrackerは素晴らしい技術だ。コーディング面接や個人用ワークスペース向けの分離ランタイムを実行する面接スタートアップで使っているが、非常に安定していて驚くほど軽量だ
Go SDKで統合するのもとても簡単だった
userfaultfdがもっと使われるようになってきていてうれしい。ページフォルト発生時に、メモリをどのように、どこから読み込むかを完全に制御できるので、本当に強力なAPIだ
通常のEC2もすでにVMである点はその通りだが、ハイパーバイザーアクセスを提供することでFirecrackerも可能になる特定のEC2があると理解している。間違っていたら訂正してほしい
[1] https://aws.amazon.com/about-aws/whats-new/2026/02/amazon-ec...
Firecrackerがなぜ必要なのか気になる。単にコンテナで直接実行してはだめなのか? 隔離の懸念は理解できるが、ブラウザとコンテナ脱出の組み合わせなら10億ドル級のCVEではないのか?
コンテナはVMよりはるかに広い攻撃面を提供し、業界標準として安全と見なされていないため、コンテナ脱出CVEの管理に投入されるリソースもVM脱出より少ない可能性が高い
これを
.metalではないインスタンスでやるにはカーネルパッチが必要ではないのか? PVMパッチが必要な気がする