Headless ChromeをDockerに入れてGebで遊んでみた

昨日書いたんだけど、Kafkaを触ろうと思ってるんだよ?でも、触ろう触ろうと思ってると、違うものが目に入ってくるのであった。ということで

Headless Chromeで遊んでみた

Kafka一切関係なく、この記事を見かけたから。

Getting Started with Headless Chrome  |  Web  |  Google Developers

この辺のこともあるので、ちょっと見とこうかなって。

Phantom.jsのメンテナー、プロジェクトの将来に疑問を呈し、その座を降りる

ただ、今手元にある環境でごにょごにょするのもなんか嫌だなぁ・・・って思ったので、無駄にDockerに詰め込んでGebで遊んでみた。そして、そのせいで疲れた(ヽ´ω`)

できあがったものは

これ。

https://github.com/bufferings/sandbox-gebheadlesschrome

docker-composeで起動すると

$ docker-compose run app

Chrome(beta)とChromeDriverとGroovyがインストールされた状態で、インタラクティブモードで起動する。

root@4013e872287a:/geb# google-chrome --version
Google Chrome 59.0.3071.29 beta

root@4013e872287a:/geb# chromedriver --version
ChromeDriver 2.29.461571 (8a88bbe0775e2a23afda0ceaf2ef7ee74e822cc5)

root@4013e872287a:/geb# groovy --version
Groovy Version: 2.4.11 JVM: 1.8.0_131 Vendor: Oracle Corporation OS: Linux

Chromeを動かしてみる

これで動くかな?と思って、試しに叩いてみたらエラーになった。

# google-chrome --headless

ので、エラーメッセージからぐぐって、 dockerでGUIのアプリを使用する(chromium) | ぴあっこ作業日誌 を参考にして --no-sandbox をつけてみた。Dockerで実行してることが原因みたいね。

# google-chrome --headless --no-sandbox

一歩進んだけど、まだエラーが出るので Getting Started with Headless Chrome  |  Web  |  Google Developers をもういちど良く見てみたら、途中の例には書いてないけど、最初の方に --disable-gpu が今は必要って書いてあった

# google-chrome --headless --no-sandbox --disable-gpu

(∩´∀`)∩ワーイエラー消えた。

キャプチャとってみる

Getting Started with Headless Chrome  |  Web  |  Google Developers のサンプルに --no-sandbox --disable-gpu をつけて実行

# google-chrome --headless --no-sandbox --disable-gpu \
                --screenshot --window-size=412,732 https://www.chromestatus.com/

したらscreenshot.pngってファイルが作られてた(rootの600だったので666にした)。

あら・・・。

f:id:bufferings:20170503175152p:plain

もう一回実行したら撮れた。撮れるときと先走るときがあるみたいね。

f:id:bufferings:20170503175202p:plain

Gebで実行

Groovy入れといたのはGebで実行してみようかなと思って。なので、Geb動作確認用のファイルも置いといた。実行するとGebがHeadless Chromeを使って、 Googleのホームページを開いてキャプチャとる。そんな感じ。初回実行時は依存ライブラリーをとりにいくのでしばらく待たされる。

# groovy Hello

Gebからの画面キャプチャは、フォントが入ってないからこんなんだけど。撮れた。

f:id:bufferings:20170503164750p:plain

Chromeのキャプチャ

ヘッドレスかどうかに関係なく、Chromeのキャプチャは見えてる範囲だけみたいよね。

↓のサイトに「こんな風にして全画面キャプチャーツールとして使えるよ」ってあるけどよく分かってない。

Using headless Chrome as an automated screenshot tool

僕の作ったDockerfileの注意点

キャプチャーとかの出力ファイルがrootで作られるので、ホスト側から触るときにちょっとめんどくさい。

これ以降はたぶん全く忘れてるであろう1年後の自分への解説。

SDKMAN!

SDKMAN! the Software Development Kit Manager

を使って、JDKとGroovyを入れた。全然知らなかったんだけど簡単だったよ。

# Java & Groovy
RUN curl -s "https://get.sdkman.io" | bash \
    && echo "gvm_auto_answer=true" >> ~/.sdkman/etc/config \
    && /bin/bash -c "source /root/.sdkman/bin/sdkman-init.sh && sdk install java && sdk install groovy"

Chrome

この辺を参考にしてHeadlessに対応してるベータ版を入れた。

https://github.com/ebidel/lighthouse-ci/blob/master/builder/Dockerfile

# Chrome
RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add - \
    && sh -c 'echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list' \
    && apt-get update \
    && apt-get install -y google-chrome-beta

ChromeDriver

ここを参考にして入れた。

docker-chromedriver/Dockerfile at master · RobCherry/docker-chromedriver · GitHub

# ChromeDriver
RUN CHROMEDRIVER_VERSION=`curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE` \
    && mkdir -p /opt/chromedriver-$CHROMEDRIVER_VERSION \
    && curl -sS -o /tmp/chromedriver_linux64.zip http://chromedriver.storage.googleapis.com/$CHROMEDRIVER_VERSION/chromedriver_linux64.zip \
    && unzip -qq /tmp/chromedriver_linux64.zip -d /opt/chromedriver-$CHROMEDRIVER_VERSION \
    && rm /tmp/chromedriver_linux64.zip \
    && chmod +x /opt/chromedriver-$CHROMEDRIVER_VERSION/chromedriver \
    && ln -fs /opt/chromedriver-$CHROMEDRIVER_VERSION/chromedriver /usr/local/bin/chromedriver

Grape

Gebを試しに動かしてみるのにGrapeってGroovyの依存管理機能を初めて使ってみた。CLOVERを読みながらやってみた。

Groovyの簡易依存関係管理ツール、Grapeを使ってみる - CLOVER

@Grapes([
    @Grab("org.gebish:geb-core:1.1.1"),
    @Grab("org.seleniumhq.selenium:selenium-chrome-driver:3.4.0"),
    @Grab("org.seleniumhq.selenium:selenium-support:3.4.0")
])

import geb.Browser

Browser.drive {
    go "https://google.com/"
    report "sample"
}.quit()

Grapeがライブラリを置いておくディレクトリ(~/.groovy/grapes)は、ボリュームマウントしておくことにした。2回目以降取りに行かなくていいように。

Geb

この部分は

Browser.drive {
    go "https://google.com/"
    report "sample"
}.quit()

Googleのホームページを開いて(go)、"sample"って名前でキャプチャ(htmlとpng)を取って(report)、ブラウザを閉じる(quit)。ってことね。

GebConfig

んで、今回のメインのGebConfigだけどオプションを3つ指定して起動してるだけ。

import org.openqa.selenium.chrome.ChromeDriver
import org.openqa.selenium.remote.DesiredCapabilities
import org.openqa.selenium.chrome.ChromeOptions

reportsDir = "target"

driver = {
  ChromeOptions options = new ChromeOptions()
  options.addArguments("--headless", "--no-sandbox", "--disable-gpu")

  DesiredCapabilities capabilities = DesiredCapabilities.chrome()
  capabilities.setCapability(ChromeOptions.CAPABILITY, options)

  new ChromeDriver(capabilities)
}

docker-compose

は、マウントとか指定するのが面倒だったから書いただけ。

version: '3'
services:
  app:
    build: .
    working_dir: "/geb"
    volumes:
    - ./geb:/geb
    - ./grapes:/root/.groovy/grapes

面白かった

Dockerじゃなかったらあっさりしてたと思う。おしまい。

2017-05-04 追記

やっぱり動かんかった。

root@42dbeb2a9909:/geb# google-chrome --no-sandbox --disable-gpu --screenshot --window-size=412,732 https://www.chromestatus.com/
root@42dbeb2a9909:/geb# [0503/235453.940351:ERROR:nacl_helper_linux.cc(311)] NaCl helper process running without a sandbox!
Most likely you need to configure your SUID sandbox correctly

Gebの方はこんな感じになった。

Caused by: org.openqa.selenium.WebDriverException: unknown error: Chrome failed to start: exited abnormally
  (Driver info: chromedriver=2.29.461571 (8a88bbe0775e2a23afda0ceaf2ef7ee74e822cc5),platform=Linux 4.8.0-49-generic x86_64) (WARNING: The server did not provide any stacktrace information)