はじめに #
E2Eテストや、定期的なスクレイピングのために、seleniumによるブラウザ動作の検証を実行したいことがあります。 今回これをGitlab CI/CDで実行する方法を紹介します。
1. dockerでselenium #
Gitlab CI/CDで実行する前に、まずdocker image内部でseleniumがうまく動くかチェックします。
ありがたいことにseleniumを実行できる各種browserのimageが用意されているのでこれを使います。docsもしっかりしているのでここでは基本的な使い方のみ紹介します。
Provides a simple way to run Selenium Grid with Chrome, Firefox, and Edge using Docker, making it easier to perform browser automation
1. selenium実行対処のブラウザーのコンテナを起動(edgeもfirefox等もあります)
$ docker run --rm -it -p 4444:4444 -p 5900:5900 -p 7900:7900 --shm-size 2g selenium/standalone-chromium:latest
2. :4444
にアクセスする。
http://localhost:4444
にアクセスすると、selenium Gridと呼ばれるブラウザの管理画面に移動することができます。
ここで任意のbrowserが起動しているか、browserのversionや現在のsession一覧を確認できます。
browserに接続できるのは1sessionのみで、既にbrowserに接続している場合新しく接続ができないので、selenium実行後にはdriverを閉じるようにしておきましょう。
atexit.register(driver.quit)
3. :7900
にアクセスする。
:7900
はVNCにアクセスすることができ、実際に稼働しているブラウザーの画面をみることできます。(passwordはsecret
)
http://localhost:7900
でアクセスするより、公式のREADMEに記載されているようなクエリ付きURLが、passwordの自動入力や画面の自動リサイズがあり便利です。
http://localhost:7900/?autoconnect=1&resize=scale&password=secret
これによってコンテナ内で動作しているブラウザをGUIで確認できるので、debugに非常に便利です。
4. seleniumでアクセスする。
seleniumの接続先はhttp://localhost:4444/wd/hub
になるので、これに向けてコードを実行します。
import atexit
import os
import time
from selenium import webdriver
REMOTE_SERVER = os.environ.get("REMOTE_SERVER", "http://localhost:4444/wd/hub")
def init_driver() -> webdriver.Remote:
options = webdriver.ChromeOptions()
# 必要に応じてメモリ削減対策を有効にする
# options.add_argument("--headless")
options.add_argument("--disable-gpu")
options.add_argument("--no-sandbox")
options.add_argument("--disable-dev-shm-usage")
driver = webdriver.Remote(
command_executor=REMOTE_SERVER,
options=options,
)
atexit.register(driver.quit)
return driver
if __name__ == "__main__":
driver = init_driver()
driver.get("https://hirohirolab.com")
print(driver.title)
time.sleep(10) # VNCでの挙動確認用
:7900
でVNCにアクセスすれば、ブラウザの状態を確認できます。
このようにコンテナ内で、seleniumのコードが問題なく動くかチェックしておきましょう。
2. Gitlab CI/CDでseleniumを利用する #
コンテナ内で動くことが確認できれば、CI/CDで同じことができるか確認しますが、割と簡単でGitlab公式でも既に解説が存在します。 https://docs.gitlab.com/ee/ci/examples/end_to_end_testing_webdriverio/
解説に従って以下のような.gitlab-ci.yml
を作ります。
selenium:
image: python
services:
- name: selenium/standalone-chrome
alias: chrome
variables:
HEALTHCHECK_TCP_PORT: "4444"
variables:
REMOTE_SERVER: "http://chrome:4444/wd/hub"
before_script:
- pip install selenium
script:
- python selenium_sample.py
services
serivcesはPipelineのジョブ実行中のコンテナに、追加で接続できるコンテナのことです。
今回ジョブのコンテナにはimage: python
を指定して、seleniumを実行し、servicesにブラウザのコンテナとしてname: selenium/standalone-chrome
を指定しています。
Gitlabではこの異なるコンテナ間の接続は、Legacy container linksと呼ばれる、dockerのlegacyな機能を使っている様です。 https://docs.gitlab.com/ee/ci/services/
Github Actionsでも同様のservicesという仕組みがあり、こちらはdocker NWのbridge機能(docker composeと同様)を使って接続を実施している様です。 https://docs.github.com/ja/actions/using-containerized-services/about-service-containers#running-jobs-in-a-container
alias
servicesのコンテナ内部のhostnameは通常、docker image名で/
があれば、__
or -
で置き換えるという形になります。
従って今回の場合、selenium__standalone-chrome
or selenium-standalone-chrome
でserviceの名前解決ができます。
https://docs.gitlab.com/ee/ci/services/#accessing-the-services
しかし分かりにくいので、alias: chrome
でコンテナ内部の名前を変更しています。
HEALTHCHECK_TCP_PORT
serivceのコンテナが起動する前に、ジョブのscript
が実行されると失敗して困る可能性があります。
例えばDBをservicesとして設定時や、今回の場合ではbrowser起動前にseleniumが走ることなどがあると思います。
この場合にはvariables
としてHEALTHCHECK_TCP_PORT
を定義しておくと、script実行待ちをしてくれるの便利です。
3. 実際に実行してみる #
5行目でselenium/standalone-chrome
が起動していることがわかります。
Starting service selenium/standalone-chrome:latest ...
8行目でselenium/standalone-chrome
が利用できるようになるまで待機します。(今回は一瞬で起動OKとなっていますが)
Waiting for services to be up and running (timeout 30 seconds)...
たまに以下のようにcrashすることがあります。
selenium.common.exceptions.WebDriverException:
Message: unknown error: session deleted because of page crash
from unknown error: cannot determine loading status
from tab crashed
この場合はmemory不足が疑われるので、以下のようなoptionでmemory削減を検討しましょう。
# options.add_argument("--headless")
options.add_argument("--disable-gpu")
options.add_argument("--no-sandbox")
options.add_argument("--disable-dev-shm-usage")
おわりに #
今回のコードやPipelineの実行結果は以下に載せています。参考にどうぞ。