Alpine Linux 3.20 以降の chromium-chromedriver で E2E テストが動かない件の解消法

Alpine Linux 3.20 以降の chromium-chromedriver で E2E テストが動かない件の解消法

  • Post Author:

表題の件を解消できたので共有します。最初に断っておくと根本原因がまだ分かっていないので解決法としてはかなり限定的な物になると思われますのでご了承ください。

今回は Ruby on Rails アプリで Selenium を使っている環境での話になりますが、恐らく他の言語やフレームワークでも(同様に Alpine 3.20~ で Selenium + chromdriver を使っていれば)同じ問題が起きる可能性があります。また後述しますがこの問題は Docker コンテナ上で動かしている Linux でのみ起きている可能性があります。

経緯

今回解決に至ったプロジェクトでは Alpine Linux 3.19 系の Ruby をコンテナで動かしていました。

FROM ruby:3.3.6-alpine3.19

# 略

RUN apk add --no-cache chromium-chromedriver fontconfig

# 略

元々このプロジェクトでは Ruby のアップグレードをする際、可能なら Alpine Linux のバージョンも上げていました。いつだかの Ruby のアップグレードで Alpine Linux 3.20 が使える様になったのでいつも通りついでにアップグレードした所、Capybara (Selenium) で動かしていた E2E テストが失敗する様になってしまいました。

Net::ReadTimeout:
 Net::ReadTimeout with #<TCPSocket:(closed)>

# /usr/local/bundle/gems/net-protocol-0.2.2/lib/net/protocol.rb:229:in 'Net::BufferedIO#rbuf_fill'
# /usr/local/bundle/gems/net-protocol-0.2.2/lib/net/protocol.rb:199:in 'Net::BufferedIO#readuntil'
# /usr/local/bundle/gems/net-protocol-0.2.2/lib/net/protocol.rb:209:in 'Net::BufferedIO#readline'
# /usr/local/bundle/gems/webmock-3.24.0/lib/webmock/http_lib_adapters/net_http.rb:94:in 'block in Net::HTTP#request'
# /usr/local/bundle/gems/webmock-3.24.0/lib/webmock/http_lib_adapters/net_http.rb:99:in 'Net::HTTP#request'
# /usr/local/bundle/gems/selenium-webdriver-4.10.0/lib/selenium/webdriver/remote/http/default.rb:119:in 'Selenium::WebDriver::Remote::Http::Default#response_for'
# /usr/local/bundle/gems/selenium-webdriver-4.10.0/lib/selenium/webdriver/remote/http/default.rb:77:in 'Selenium::WebDriver::Remote::Http::Default#request'
# /usr/local/bundle/gems/selenium-webdriver-4.10.0/lib/selenium/webdriver/remote/http/common.rb:59:in 'Selenium::WebDriver::Remote::Http::Common#call'
# /usr/local/bundle/gems/selenium-webdriver-4.10.0/lib/selenium/webdriver/remote/bridge.rb:620:in 'Selenium::WebDriver::Remote::Bridge#execute'
# /usr/local/bundle/gems/selenium-webdriver-4.10.0/lib/selenium/webdriver/remote/bridge.rb:53:in 'Selenium::WebDriver::Remote::Bridge#create_session'
# /usr/local/bundle/gems/selenium-webdriver-4.10.0/lib/selenium/webdriver/common/driver.rb:317:in 'block in Selenium::WebDriver::Driver#create_bridge'
# /usr/local/bundle/gems/selenium-webdriver-4.10.0/lib/selenium/webdriver/common/driver.rb:316:in 'Selenium::WebDriver::Driver#create_bridge'
# /usr/local/bundle/gems/selenium-webdriver-4.10.0/lib/selenium/webdriver/common/driver.rb:74:in 'Selenium::WebDriver::Driver#initialize'
# /usr/local/bundle/gems/selenium-webdriver-4.10.0/lib/selenium/webdriver/chrome/driver.rb:35:in 'Selenium::WebDriver::Chrome::Driver#initialize'
# /usr/local/bundle/gems/selenium-webdriver-4.10.0/lib/selenium/webdriver/common/driver.rb:47:in 'Class#new'
# /usr/local/bundle/gems/selenium-webdriver-4.10.0/lib/selenium/webdriver/common/driver.rb:47:in 'Selenium::WebDriver::Driver.for'
# /usr/local/bundle/gems/selenium-webdriver-4.10.0/lib/selenium/webdriver.rb:88:in 'Selenium::WebDriver.for'
# /usr/local/bundle/gems/capybara-3.40.0/lib/capybara/selenium/driver.rb:75:in 'Capybara::Selenium::Driver#browser'
# /usr/local/bundle/gems/capybara-3.40.0/lib/capybara/selenium/driver.rb:95:in 'Capybara::Selenium::Driver#visit'
# /usr/local/bundle/gems/capybara-3.40.0/lib/capybara/session.rb:281:in 'Capybara::Session#visit'
# /usr/local/bundle/gems/capybara-3.40.0/lib/capybara/dsl.rb:52:in 'Method#call'
# /usr/local/bundle/gems/capybara-3.40.0/lib/capybara/dsl.rb:52:in 'Capybara::DSL#visit'
# ./spec/features/app_test.rb:19:in 'block (3 levels) in <top (required)>'

エラーの内容的におそらく Selenium が chromedriverと疎通できていない感じでした。尚、Capybara (Selenium) の設定は以下の様になっていました:

Capybara.register_driver :headless_chrome do |app|
  options = Selenium::WebDriver::Chrome::Options.new

  options.add_argument('--headless')
  options.add_argument('--no-sandbox')
  options.add_argument('--disable-dev-shm-usage')
  options.add_argument('--window-size=1400,1400')

  Capybara::Selenium::Driver.new(app, browser: :chrome, options:)
end

Capybara.javascript_driver = :headless_chrome
Capybara.default_max_wait_time = 3
Selenium::WebDriver::Chrome::Service.driver_path = '/usr/lib/chromium/chromedriver'

この設定では headless_chrome ドライバーを使ったテストでは Selenium 経由で chromedriver を動かしての検証を行なっています。

余談ですが、chromedriver が共有メモリに使っている dev/shm は Docker コンテナのデフォルトの実行設定ではサイズが 64MB と低い為落ちてしまい、テストが失敗しがちです。これを防ぐ為に --disable-dev-shm-usage を設定しています。(結構ありがちなトラブルだと思うので書いておきました)

3.19 でお茶を濁した

その時はすぐに解決できなそうだったので Alpine Linux のアップグレードは諦め、その後の Ruby のバージョンアップでも 3.19 を使い続けていました。しかし先日リリースされた Ruby 3.4 の公式イメージで Alpine Linux 3.19 が打ち切られてしまった為、本格的に対処する必要が出てきました。

解決法

いきなり紹介しますが、 options.add_argument('--disable-gpu') を追加しただけで解決しました。

恐らく GPU 関連の機能によって chromedriver が正常に動作せず、 Selenium と疎通が取れていなかったのがこのオプションによって当該機能が無効化された事により不具合が解消した物と思われます。(冒頭でも書きましたが厳密に調べていないので真相は分からずです)

一応オプションについて調べてみました

ここからは蛇足です。興味がある方だけどうぞ。

2017年の記事になりますが、 FAQ – Getting Started with Headless Chrome によると

Do I need the --disable-gpu flag?

Only on Windows. Other platforms no longer require it. The --disable-gpu flag is a temporary work around for a few bugs. You won’t need this flag in future versions of Chrome. See crbug.com/737678 for more information.

Windows に関連するいくつかの不具合に対処する為の一時的なワークアラウンドで、他のプラットフォームでは不要であるとされています。しかしこのオプションをつける事で事実として不具合は解消しています。またプラットフォームは Windows ではなく Linux です。

同じく2017年になりますが、Chromium プロジェクトで関連しそうな issue Headless: make –disable-gpu flag unnecessary がありました。内容を見てみると起票者は Linux 環境で libosmesa.so の不在によるエラーが関連していて、このオプションにより回避できたそうです。

更にやり取りを見てみると有用なコメントがありました。

`–disable-gpu` seems to be still necessary on Linux inside a Docker container:

(中略)

libGL error: MESA-LOADER: failed to retrieve device information                                      
libGL error: unable to load driver: i915_dri.so                                                      
libGL error: driver pointer missing                                                                  
libGL error: failed to load driver: i915                                                            
libGL error: failed to open drm device: No such file or directory                                    
libGL error: failed to load driver: i965
libGL error: unable to load driver: swrast_dri.so
libGL error: failed to load driver: swrast
[35:35:1214/153301.681608:ERROR:gl_context_glx.cc(227)] Couldn’t make context current with X drawable.
[35:35:1214/153301.681753:ERROR:gpu_info_collector.cc(78)] gl::GLContext::MakeCurrent() failed

やはりグラフィック関係のモジュールが不在の様です。確かに Alpine Linux 3.19 以前までは問題なく動作していたが 3.20 以降で不具合が起きた今回のケースにも関連がありそうです。たまたま 3.19 では他の依存パッケージに当該モジュールが含まれていたが 3.20 以降で含まれなくなった等が考えられます。

結局この issue は特に進展がなく Obsolete として Won't fix ステータスになりました。上のコメントは2019年の物ですが、2025年の今でも一部の環境ではまだ再現しそうです。

調査はここまでにしておきます。繰り返しになりますが今回私が遭遇した不具合の真相は分かっていません。しかし私の環境では少なくとも --disable-gpu で問題解決したので同じ様な環境で同じ問題に遭遇している方の助けになれば幸いです。

we are hiring

優秀な技術者と一緒に、好きな場所で働きませんか

株式会社もばらぶでは、優秀で意欲に溢れる方を常に求めています。働く場所は自由、働く時間も柔軟に選択可能です。

現在、以下の職種を募集中です。ご興味のある方は、リンク先をご参照下さい。

コメントを残す