Selenium::WebDriver
を使ってHeadless Chromeを起動したときのオプション設定について調べてみました。
Selenium::WebDriverでHeadless Chromeを起動するには
通常のChromeとの違いは、オプション設定に--headless
を渡すことだけです。
require 'selenium-webdriver'
options = Selenium::WebDriver::Chrome::Options.new
options.add_argument('--headless')
@driver = Selenium::WebDriver.for :chrome, options: options
@driver.navigate.to "https://www.google.com/"
Selenium::WebDriverからHeadless Chromeを起動するとどんなプロセスが生成されているか?
上記のコードを実行すると、
- chromedriver
- google-chrome webdriver
- chrome zygote x2
- chrome gpu-process
- chrome broker
- chrome renderer
という6種類7つのプロセスが生成されます。(zygote
プロセスは2つ生成されていました)
PID値を見る限り、この順番で生成されているようです。
各プロセス生成時のオプション設定
chromedriver
chromedriver
は以下のコマンドで起動されています。
chromedriver --port=9515
オプション設定はポート指定のみです。
google-chrome webdriver
google-chrome
はchrome
のラッパーとのこと。
Difference between “/opt/google/chrome/chrome” and “/opt/google/chrome/google-chrome” on Fedora 22
https://superuser.com/questions/975050/difference-between-opt-google-chrome-chrome-and-opt-google-chrome-google-ch
以下のようにgoogle-chrome
コマンドに大量のオプションが渡されています。
/opt/google/chrome/google-chrome --disable-background-networking --disable-client-side-phishing-detection --disable-default-apps --disable-hang-monitor --disable-popup-blocking --disable-prompt-on-repost --disable-sync --disable-web-resources --enable-automation --enable-logging --force-fieldtrials=SiteIsolationExtensions/Control --headless --ignore-certificate-errors --load-extension=/tmp/.org.chromium.Chromium.YZWaub/internal --log-level=0 --metrics-recording-only --no-first-run --password-store=basic --remote-debugging-port=0 --test-type=webdriver --use-mock-keychain --user-data-dir=/tmp/.org.chromium.Chromium.tVOWvT data:,
オプション設定の内容を表にまとめました。
実に23個のオプションが設定されています。
options | 内容 |
---|---|
disable-background-networking | 複数のサブシステムの動作オフ |
disable-client-side-phishing-detection | フィッシング検知機能オフ |
disable-default-apps | デフォルトアプリ設定オフ |
disable-hang-monitor | ページの停止監視機能オフ |
disable-popup-blocking | ポップアップブロック機能オフ |
disable-prompt-on-repost | POSTリクエストの結果確認機能オフ |
disable-sync | ブラウザデータとGoogleアカウントの同期オフ |
disable-web-resources | Webリソースのためのバックエンド機能オフ |
enable-automation | 自動テスト中であることの通知機能オン |
enable-logging | コンソールログ機能オン |
force-fieldtrials=SiteIsolationExtensions/Control | fieldtrialsオプションの設定 |
headless | ヘッドレスChromeとして起動 |
ignore-certificate-errors | 認証関連エラー無視 |
load-extension=/tmp/.org.chromium.Chromium.YZWaub/internal | 拡張機能へのパス設定 |
log-level=0 | ログレベルをINFOに設定 |
metrics-recording-only | メトリックスの通知オフ、記録のみオン |
no-first-run | First runタスク オフ |
password-store=basic | パスワード用暗号化ストレージをbasicに指定 |
remote-debugging-port=0 | ポート0のリモートデバッグ オン |
test-type=webdriver | テストに使うツールをwebdriverに設定 |
use-mock-keychain | macOS用の必須設定(詳細不明) |
user-data-dir=/tmp/.org.chromium.Chromium.tVOWvT | ユーザープロフィールの保存場所指定 |
data:, | user-data-dirのオプション? |
test-type=webdriver
とあるので、このプロセスと勝手にwebdriver
と呼ぶことにしました。
主にテストを実施する際に邪魔な機能をオフする目的のオプション設定になっていると思います。
chrome zygote
webdriver
のプロセス生成に続いてChrome
の一部であるzygote
というプロセスが2つ立て続けに生成されています。
chrome
コマンドのオプションを見ると--headless
オプションが重複していました。わざわざ渡さなくても良かった、というわけではないでしょうが、理由はわかりません。
/opt/google/chrome/chrome --type=zygote --enable-logging --headless --log-level=0 --headless --enable-crash-reporter
options | 内容 |
---|---|
type=zygote | プロセスのタイプ表示のための名前設定 |
enable-logging | コンソールログ機能オン |
headless | ヘッドレスChromeとして起動 |
log-level=0 | ログレベルをINFOに設定 |
enable-crash-reporter | ヘッドレスのためのクラッシュ通知オン |
A zygote process is one that listens for spawn requests from a master process and forks itself in response. Generally they are used because forking a process after some expensive setup has been performed can save time and share extra memory pages.
zygote
プロセスは時間短縮や省メモリを実現するための仕組みとして使われているとのこと。またChrome起動中に一部のライブラリがアップデートされたときの泣き別れ対策としても動作しているようです。
Selenium
関連というより、Chrome
の動作に必要なプロセスということでしょう。
chrome gpu-process
次はgpu-process
というプロセスが生成されます。
/opt/google/chrome/chrome --type=gpu-process --enable-logging --headless --log-level=0 --headless --enable-crash-reporter --gpu-preferences=KAAAAAAAAACAAABAAQAAAAAAAAAAAGAAAAAAAAAAAAAIAAAAAAAAAAgAAAAAAAAA --use-gl=swiftshader-webgl --override-use-software-gl-for-tests --headless --enable-crash-reporter --enable-logging --log-level=0 --service-request-channel-token=10264762006126535026
options | 内容 |
---|---|
type=gpu-process | プロセスのタイプ表示のための名前設定 |
enable-logging | コンソールログ機能オン |
headless | ヘッドレスChromeとして起動 |
log-level=0 | ログレベルをINFOに設定 |
enable-crash-reporter | ヘッドレスのためのクラッシュ通知オン |
gpu-preferences=KAAAAAAAAACAAABAA... | GPU情報の設定? |
use-gl=swiftshader-webgl | GPUが使うGLをSwiftShaderに設定 |
override-use-software-gl-for-tests | ソフトウェアGLを使用するよう強制的に設定 |
service-request-channel-token=1026476200612... | 子プロセスでのメッセージパイプのためのトークン |
chrome
コマンドを見ると14個のオプションが設定されていますが、重複が多く、実際には上記表のように9種類のオプションが渡されています。
内容はgpu-process
という名前から推測される通り、GPU関連の設定を行っています。というより、GPUを使わずにソフトウェアGLを使うように強制的に設定しています。
chrome broker
broker
プロセスはタイプの指定を行っているだけで、それ以外のオプションはありません。
/opt/google/chrome/chrome --type=-broker
Chromiumのプロセス構成と Worker/SharedWorker/ServiceWorkerのうごき
https://qiita.com/amiq11/items/61cf100e5f9fac8533b6
「ブラウザ」、「GPUプロセス」という2つのプロセスに加え、おおよそそれぞれのタブが別のプロセスに割り振られていることがわかります。このタブごとのプロセスを「レンダラプロセス」といいます。
たとえばa.comを開いたとすると、ブラウザプロセスがレンダラプロセスをつくり、ネットワークリクエストがブラウザプロセスへ投げられ(1)、ブラウザプロセスがネットワークにリクエストを投げ(2)、レスポンスがブラウザプロセスに帰ってきて(3)、それをレンダラになげる(4)、という手順になります。
In Chromium, the broker is always the browser process. The broker, is in broad terms, a privileged controller/supervisor of the activities of the sandboxed processes.
broker
プロセスはブラウザプロセスとしてレンダラプロセスを作るコントローラの役目を果たしているとのこと。
chrome renderer
最後にrenderer
プロセスが生成されます。renderer
プロセスはいわゆるタブに相当するものなので、タブが3つあれば3つのrenderer
プロセスが生成されます。
chrome
コマンドは以下の通り。これも大量のオプションが渡されています。
/opt/google/chrome/chrome --type=renderer --enable-automation --enable-logging --log-level=0 --test-type=webdriver --use-gl=swiftshader-webgl --disable-gpu-compositing --service-pipe-token=4567683379984847303 --lang=en-US --headless --enable-crash-reporter --num-raster-threads=1 --service-request-channel-token=4567683379984847303 --renderer-client-id=4 --shared-files=v8_context_snapshot_data:100,v8_natives_data:101
options | 内容 |
---|---|
type=renderer | プロセスのタイプ表示のための名前設定 |
enable-automation | 自動テスト中であることの通知機能オン |
enable-logging | コンソールログ機能オン |
log-level=0 | ログレベルをINFOに設定 |
test-type=webdriver | テストに使うツールをwebdriverに設定 |
use-gl=swiftshader-webgl | GPUが使うGLをSwiftShaderに設定 |
disable-gpu-compositing | GPUの使用をオフ |
service-pipe-token=4567683379984847303 | メッセージパイプを使うためのトークン |
lang=en-US | 言語をen-USに設定 |
headless | ヘッドレスChromeとして起動 |
enable-crash-reporter | ヘッドレスのためのクラッシュ通知オン |
num-raster-threads=1 | ラスタ変換に使うスレッド数を1に設定 |
service-request-channel-token=4567683379984847303 | メッセージパイプを作るためのトークン |
renderer-client-id=4 | ??? |
shared-files=v8_context_snapshot_data:100,v8_natives_data:101 | 子プロセスへ渡すファイル記述子の設定 |
重複なく15個のオプションが設定されています。子プロセスとのやりとりに必要となるオプションがいくつか設定されていることが特徴的です。そしてこのプロセスが最も多くのメモリを使います。
まとめ
以上がSelenium::WebDriver
を使ってHeadless Chromeを起動した際に生成されるプロセスとそのプロセスに設定されるオプションです。
ブラウザの動作は奥が深いですが、なんとなく各プロセスの役割分担がオプション設定の内容を通してイメージできてきたように思います。
おまけ 各プロセス生成とSelenium::WebDriverのコードとの対応関係について
各プロセスとそれを生成しているSelenium::WebDriver
のコードの対応関係を以下のようになっていると思われます。
PID | %MEM | RSS | type | codes |
---|---|---|---|---|
046 | 1.0 | 10664 | chromedriver | @driver = Selenium::WebDriver.for :chrome, options: options |
055 | 9.2 | 93320 | webdriver | @driver = Selenium::WebDriver.for :chrome, options: options |
063 | 4.2 | 43152 | zygote | @driver = Selenium::WebDriver.for :chrome, options: options |
065 | 1.0 | 11088 | zygote | @driver = Selenium::WebDriver.for :chrome, options: options |
084 | 5.4 | 54848 | gpu-process | @driver = Selenium::WebDriver.for :chrome, options: options |
092 | 1.2 | 12712 | broker | @driver = Selenium::WebDriver.for :chrome, options: options |
101 | 13.8 | 139744 | renderer | @driver.navigate.to "https://www.google.com/" |
メモリ使用量はrenderer
が最も多く、次がwebdriver
となっていました。
余談
Headless Chromeを起動させる処理を繰り返しているうちにメモリエラーを起こしてサーバーがダウンしてしまい、メモリ容量を圧迫している原因を探っている過程で今回のオプション設定の調査を行いました。
メモリエラーの原因は@driver.quit
が抜けていたという初歩的ミスでした。
Selenium::WebDriver
を正常に終了させないまま繰り返しHeadless Chromeを起動していたため、上記のプロセスがひたすら重複して生成されてメモリを使いまくっていました。反省。