サーバ上でchromiumを定期的にheadlessで動かすとchromedriverがエラーを吐く

忘れそうなのでメモ。

環境

snap経由でインストールしたchromiumを同じくsnap経由でインストールしたchromedriverから操作する。ライブラリはJavaのSeleniumを使う。

問題1. 特定のファイルを取得できない

snapはコンテナ化された環境で動作するので中から見えるファイルシステムはホストマシンのファイルシステムと異なる。だから /tmp/ 以下などにファイルをダウンロードし検証する…みたいなテストを書いていると失敗する。

実行ユーザーのホームディレクトリは見えるのでホームディレクトリ以下に一時ファイルを置くようにすることが無難。

問題2. ChromeDriverがChromiumと通信できない

これでハマった。エラーメッセージは

org.openqa.selenium.WebDriverException: Timed out waiting for driver server to start.

とかいうエラーがスローされたり、

cmd_run.go:1055: WARNING: cannot start document portal: dial unix /run/user/1000/bus: connect: no such file or directory

とかいうエラーメッセージがChromiumから標準エラー出力に出力されたりする。

そしてsshでChromiumの実行ユーザーとしてログインするとエラーが解消するという謎。

原因

D-Busが動いてない。D-Busとは簡単に言うとプロセス間通信のためのデーモン。Linux GUIアプリがよく使っている。KDE発祥らしい。

D-Bus のはなし

ChromiumはD-Busを使っているが、実行するユーザーがログアウトするとD-Busが止まってしまう。このD-BusはSystemdによって起動され、ユーザーレベルで動作する。現状の動作状態は

$ systemctl --user status dbus

とするとわかる。おそらくこのコマンドを実行するためにログインしているから通常は active (running) というステータスが帰ってくるはずだ。こいつはユーザーがログアウトすると終了する。

解決策

以下を実行する。

$ loginctl enable-linger <実行するユーザー名>

こうすることでユーザーがログインしていなくても指定したユーザーレベルのsystemdサービスが実行されたままになる。

解決策 2

以下の環境変数が正しくセットされている必要があるので、Chromiumが起動したときにちゃんとこの環境変数が渡るようにしておく。

export XDG_RUNTIME_DIR=/run/user/<実行ユーザーのuid>