Thanks Driven Life

日々是感謝

Docker Hub の Selenium Hub/Node イメージ + fig で Selenium Grid 構築してみた

経緯

お仕事で、CI 環境用に Selenium Grid を Docker で稼動させています。 以前は Hub は別サーバに立てて、Node を Docker コンテナとして複数起動できるように構築していましたが、 いろいろあって Hub も Docker コンテナとしてしまおう、と考えました。

簡単にこんな感じです。

f:id:gongoZ:20141201112427p:plain

いざ Hub の Docker コンテナを作ろうとした際

「とりあえず java が動けばいいんだから Node のイメージそのまま流用できるよな」
「いやいやここはやはり Hub/Node はしっかり分けた方がいいだろう」
「じゃあ Hub 用と Node 用で Dockerfile 作ってこ?」
「でも Hub も Node も Java 使うんだから、まずは SeleniumBase という
 とりあえず Java と selenium.jar 入ってるイメージ作って、そいつを FROM していく?」
「つまり Dockerfile は3つか。さてディレクトリ構成どうしよう」

みたいなことを考えるわけです。 よしならば始めようと思った時にチームリーダーから

似たようなやつ Docker Hub に既にあるんじゃね?

ははまさか、って思ったらやっぱりありました。

https://registry.hub.docker.com/repos/selenium/

しかもだいたい揃ってる。こいつを使っていこう。

今回の環境

OSX Yosemite で boot2docker 使いました。

$ boot2docker version
Boot2Docker-cli version: v1.3.2
Git commit: e41a9ae

$ docker version
Client version: 1.3.2
Client API version: 1.15
Go version (client): go1.3.3
Git commit (client): 39fa2fa
OS/Arch (client): darwin/amd64
Server version: 1.3.2
Server API version: 1.15
Go version (server): go1.3.3
Git commit (server): 39fa2fa

boot2docker および fig は Homebrew でインストールしてあります。

使ってみる

今回使うのはこの2つ

README や Dockerfile、使用されているスクリプトを読むと --link オプションで Node → Hub の接続情報を補充している模様。

コンテナ起動に docker run を手で打つのはつらいので、fig を利用してみました。

fig.yml

hub:
  image: selenium/hub
  ports:
    - "4444" // (1)

node:
  image: selenium/node-firefox
  expose:
    - "5555" // (2)
  links:
    - hub
  1. 無くても問題ないのですが、Selenium Grid 画面を見たかったので Port Forwarding

    Docker ホスト外から利用する時はポート固定されてると楽なので、"4444:4444" とか

  2. selenium/node-base に EXPOSE 指定が無かったため、node.expose を追加してあります。

    Node にアクセスするのは同じ Docker ホスト上の Hub だけ想定しているので ports 指定は不要

起動

$ pwd
/tmp
$ fig up -d hub
Creating tmp_hub_1..
$ fig scale node=5
Starting tmp_node_1...
Starting tmp_node_2...
Starting tmp_node_3...
Starting tmp_node_4...
Starting tmp_node_5...

docker ps はこんな感じ

$ docker ps
CONTAINER ID        IMAGE                          COMMAND                CREATED             STATUS              PORTS                     NAMES
7077acdb0e47        selenium/node-firefox:latest   "/opt/bin/entry_poin   7 seconds ago       Up 5 seconds        5555/tcp                  tmp_node_5          
254af6c66a11        selenium/node-firefox:latest   "/opt/bin/entry_poin   7 seconds ago       Up 5 seconds        5555/tcp                  tmp_node_4          
37cf71984ad4        selenium/node-firefox:latest   "/opt/bin/entry_poin   7 seconds ago       Up 6 seconds        5555/tcp                  tmp_node_3          
7bf7fe5bc71e        selenium/node-firefox:latest   "/opt/bin/entry_poin   7 seconds ago       Up 6 seconds        5555/tcp                  tmp_node_2          
b2f3383f15dc        selenium/node-firefox:latest   "/opt/bin/entry_poin   7 seconds ago       Up 6 seconds        5555/tcp                  tmp_node_1          
2e0b30a2b22e        selenium/hub:latest            "java -jar /opt/sele   14 seconds ago      Up 13 seconds       0.0.0.0:49156->4444/tcp   tmp_hub_1

起動してるっぽい

Grid の様子をチェック

Selenium Grid のコンソールは http://{hub の IPaddr}:{hub の port}/grid/console にアクセスすると見れます。 ここでいう hub の IPaddr および hub の port

  • IPaddr

      $ boot2docker ip
    
          The VM's Host only interface IP address is: 192.168.59.103
    
  • port

      $ docker port tmp_hub_1
      4444/tcp -> 0.0.0.0:49156
    

なので http://192.168.59.103:49156/grid/console となります。

アクセスするとお馴染の画面が表示されます。

f:id:gongoZ:20141201214351p:plain

ちゃんと scale node=5 した数だけ、Node が Hub に登録されています。

※ Hub に認識されるまで時間かかる時もあります。Hub の cycle なんちょかを短かくすればい早くなるんだろうか。

Grid を使ったデモアプリ

アプリってほどでも無いですが、とりあえず一気に5つの Selenium WebDriver を走らせてみる系

# Gemfile
source 'https://rubygems.org'

gem 'selenium-webdriver'
gem 'parallel'

# main.rb
require 'selenium-webdriver'
require 'parallel'

urls = [
  'https://github.com',
  'https://hub.docker.com',
  'https://bitbucket.org',
  'https://www.heroku.com',
  'http://azure.microsoft.com/ja-jp/'
]

Parallel.each_with_index(urls, in_threads: 5) do |url, index|
  driver = Selenium::WebDriver.for :remote, url: 'http://192.168.59.103:49156/wd/hub'
  driver.navigate.to url
  driver.save_screenshot "screenshot#{index}.png"
end

こいつを

$ bundle install --path vendor/bundle
$ bundle exec ruby main.rb
$ ls
Gemfile         main.rb         screenshot1.png screenshot3.png vendor
Gemfile.lock    screenshot0.png screenshot2.png screenshot4.png

とすると、とりあえず画像が取得できる。まあこの例だと並列じゃなくてもできるんだけど。

f:id:gongoZ:20141201220638p:plain

ちなみに実行中のコンソールも無事5つ同時に使われている

f:id:gongoZ:20141201220706p:plain

イメージの中身見てみる

1. Base

github.com/SeleniumHQ/docker-selenium/Base/Dockerfile

JRE インストールして selenium jar とってきて seluser 作る。シンプル

2. Hub

github.com/SeleniumHQ/docker-selenium/Hub/Dockerfile

4444 ポートで待ち受ける Hub モード起動。

3. NodeBase

github.com/SeleniumHQ/docker-selenium/NodeBase/Dockerfile

ブラウザを動かす仮想フレームバッファとしては Xvfb を使用している。 タイムゾーンや Xvfb のパラメータをセット。

github.com/SeleniumHQ/docker-selenium/NodeBase/entry_point.sh

/opt/selenium/config.json および --link で Hub と関連付けされている前提で処理が組まれている。 後述する NodeFirefoxNodeChrome ではこれらが設定されている。

4. NodeFirefox

github.com/SeleniumHQ/docker-selenium/NodeFirefox/Dockerfile

Firefox をインストールし、それ用の Selenium Profile をセット。

まとめ

Selenium Profile やタイムゾーンだったりを変更したい場合は、 FROM selenium/node-firefox した Dockerfile で再度記述して上書きもできるし特に問題無く使えた。 Selenium 公式が出しているイメージだしどんどん使っていこう。

参考