表題の件を解消できたので共有します。最初に断っておくと根本原因がまだ分かっていないので解決法としてはかなり限定的な物になると思われますのでご了承ください。
今回は 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
で問題解決したので同じ様な環境で同じ問題に遭遇している方の助けになれば幸いです。