DeNA Testing Blog

Make Testing Fun, Smart, and Delighting End-Users

ZaleniumをKubernetes/GKEで動かす

この記事はSelenium/Appium Advent Calendar 2017の第18日目です。

SWETのGLやってます@okitanです。

少し前ですが、「日本Seleniumユーザーコミュニティ」のエキスパートが教えるSelenium最新事情という連載にDockerでSelenium Gridを構築して複数マシンのブラウザ自動テストを行うという記事を書きました。 記事中では、公式のSelenium GridのDockerイメージを使う方法に加えて、現在SWETで注目して運用しているZaleniumを利用する方法を紹介しました。

本記事では、Kubernetesを利用し、Zaleniumの以下の2つの構築方法を紹介します。

なお、本記事ではKubernetesに関する用語が出て来ます。 KubernetesのドキュメントWEB+DB PRESS Vol.99の「実践Kubernetes」がわかりやすいと思います。

脱線しますが、WEB+DB PRESS Vol.99には、SWETの@tarappoが寄稿した「UIテスト自動化」も特集2として載ってますのでおすすめです。

Selenium GridとZaleniumについてのおさらい

Zaleniumは複数のマシンで分散してブラウザ操作の自動化を実現するSelenium Gridを構築するためのソフトウェアです。 Selenium Gridを利用するとGoogle Chrome・Firefox・Microsoft Edge等多種多様なブラウザを管理でき、クロスブラウザテスティングが簡単にできるようになります。 Zaleniumは公式のSelenium Gridに追加で以下の機能をもっています。 (Selenium Gridに関する詳細やZaleniumの機能の詳細については、元記事を読んでください)

  • Selenium nodeのオートスケール機能
  • ブラウザの動作をSelenium Gridの管理コンソール上からのライブで確認できる機能(ライブプレビュー)
  • ブラウザ動作の録画機能
    • 録画された動画をドライバのログと合わせてWeb上で一覧できるダッシュボードという機能があります

ここで注目したいのは、Selenium nodeのオートスケール機能です。自動テストを開発プロセスに組み込むにはある程度短い時間で開発に対してフィードバックを行う必要があります。 その上、自動テスト数が増えていくにしたがって増加する実行時間を短くするため、並列実行数を増やしていかないといけません。 そのために、実行時間が何分以内に収まるような自動テストの並列実行計画の戦略と、それを受け入れるだけのキャパシティのあるテスト実行環境が必要になります。 Zaleniumのオートスケール機能はこの後者の問題を解決してくれます。

そしてオートスケールと相性の良いのがクラウドです。 今日ではKubernetesを用いてクラウドでコンテナ群を管理するのが一般的です。 もちろん、ZaleniumもKubernetesに対応しています。 また、最終的にクラウドで動かすにしてもローカルで動作確認できると便利なので、自分の開発環境で試してみる用にminikubeでの説明もあわせておこないます。

なお、以下の環境で動作確認してます。

minikube上にZaleniumを立てる

事前準備

minikubeのインストール方法を簡単に説明します。 公式のminikubeのチュートリアルは、 deprecatedとなっているdocker-machine-driver-xhyveを利用しています。 本記事ではdocker-machine-driver-xhyveの代替となるHyperkit driverのインストール方法もあわせて紹介しておきます。

Homebrew Caskを利用してminikubeをインストールします。

$ brew cask install minikube

Hyperkit driverをダウンロードして配置します。

$ curl -LO https://storage.googleapis.com/minikube/releases/latest/docker-machine-driver-hyperkit \
    && chmod +x docker-machine-driver-hyperkit \
    && sudo mv docker-machine-driver-hyperkit /usr/local/bin/ \
    && sudo chown root:wheel /usr/local/bin/docker-machine-driver-hyperkit \
    && sudo chmod u+s /usr/local/bin/docker-machine-driver-hyperkit

KubernetesのCLIであるkubectlをインストールします。

$ brew install kubectl

これでminikubeを起動する準備が整いました。

minikubeの起動

Hyperkit driverを利用するので、--vm-driver=hyperkitを指定して起動し、Kubernetesのcontextをminikubeに向けます。 minikubeが使えるリソースは--cpu=4--memory=4096と指定できますので、利用するホストマシーンのスペックにあわせて調整してください。

$ minikube start --vm-driver=hyperkit
$ kubectl config use-context minikube

なお、筆者の環境では何度かminikubeが起動しないこともありました。 そのような場合、rm -rf ~/.minikubeと一旦環境をリセットしてから起動するとうまくいきました。

Zaleniumの起動

Dockerだけで起動していたZaleniumでは、/var/run/docker.sockをマウントすることで、ブラウザが動くコンテナをオートスケールさせることができていました。 Kubernetesにおいては、実際にブラウザが動くPodをオートスケールさせるために、ServiceAccountが必要になります。

$ kubectl create serviceaccount zalenium

作成したServiceAccountを利用してZaleniumのDeploymentを作成します。 ZaleiniumはKubernetes利用時には、app=zaleniumというラベルを利用しているので、作成時に指定しています。

$ kubectl run zalenium \
    --image=dosel/zalenium:3.8.1c \
    --overrides='{"spec": {"template": {"spec": {"serviceAccount": "zalenium"}}}}' \
    --labels=app=zalenium,role=grid \
    --port=4444 \
    -- start --chromeContainers 1 \
             --firefoxContainers 0 \
             --seleniumImageName elgalu/selenium:3.8.1-p3 \
             --videoRecordingEnabled false \
             --sendAnonymousUsageInfo false

作成したDeploymentに対応するServiceを作成してアクセスできるようにします。

$ kubectl expose deployment zalenium --type=NodePort
service "zalenium" exposed

初回作成時にはDockerイメージを取得する時間のため少々待つ必要があります。 kubectl get podsによりPodの状況を確認します。

$ kubectl get pods
NAME                        READY     STATUS    RESTARTS   AGE
zalenium-40000-547b2        1/1       Running   0          53m
zalenium-57d6bb658f-s7vrb   1/1       Running   0          53m

上記、zalenium-40000-547b2となっているのが、ブラウザが動くSelenium Nodeです。この40000は連番で、オートスケールして台数が増えるたびに数が増えていきます。 今回は起動のオプションでGoogle Chromeのコンテナを1つだけ作ると指定しているので合計で1つ起動しています。 また、zalenium-57d6bb658f-s7vrbとなっているのがZalenium本体です。

両者ともRunningになったら、起動は完了しているので、minikubeのipアドレスおよびZalenium ServiceのnodePortからSelenium GridのURLを調べます。

$ echo http://$(minikube ip):$(kubectl get service zalenium \
         -o go-template='{{(index .spec.ports 0).nodePort}}')/wd/hub
http://192.168.64.10:31243/wd/hub

Zaleniumの動作確認

ZaleniumのURLが取得できたので動作を確認します。

require "selenium-webdriver"

caps = Selenium::WebDriver::Remote::Capabilities.chrome(
  "name" => "hello zalenium",
  "recordVideo" => true
)

driver = Selenium::WebDriver.for(:remote,
  url: "http://192.168.64.10:31243/wd/hub", # (1)
  desired_capabilities: caps
)

# あとはブラウザを好きに操作してください
driver.get("http://www.selenium.jp/")
driver.quit

上記のスクリプトをselenium_script.rbとして保存し、下記のように実行してください。 なお、スクリプト中(1)の部分は上記のSelenium GridのURLが使われてますので、実行環境に応じて読み替えてください。

$ gem install selenium-webdriver
$ ruby selenium_script.rb

スクリプトの実行に伴い、http://192.168.64.10:31243/grid/admin/liveでブラウザ動作のライブプレビューが、http://192.168.64.10:31243/dashboard/で録画されたダッシュボードが確認できます。 何度かスクリプトを実行すると、ブラウザを動かすコンテナが動的に作成されて動作することが確認できます。

GKEでZaleniumを動かす

このように、ローカルに立てたminikube上でZaleniumの動作が確認できました。 そこで、今度はGKEで試してみましょう。

まずは、Google Cloud PlatformのドキュメントをみてGCPの設定とgcloudツールの設定を行ってください。

Kubernetesクラスタの作成

まずはKubernetesを動かすためのクラスタを作成します。

$ gcloud container clusters create zalenium \
         --num-nodes 1 \
         --machine-type g1-small

こちらはGCPのコンソールから作成することも可能なので、心配な人はGUIで作成してください。 クラスタを作成したらクラスタを操作するための認証情報を取得します。

$ gcloud container clusters get-credentials zalenium

クレデンシャルを取得すると、kubectl config current-contextで取得できるcontextがGKEに自動的に変更されます。 これでZaleniumを起動する準備ができました。

Zaleniumの起動

基本はminikubeと同様に実行できるのですが、クラウド上に作成した場合は第三者にアクセスされる可能性があるので今回はZaleniumに対してBasic認証をかけておきます。 Basic認証は--gridUser--gridPasswordで指定できます。

$ kubectl create serviceaccount zalenium
$ kubectl run zalenium \
    --image=dosel/zalenium:3.8.1c \
    --overrides='{"spec": {"template": {"spec": {"serviceAccount": "zalenium"}}}}' \
    --labels=app=zalenium,role=grid \
    --port=4444 \
    -- start --chromeContainers 1 \
             --firefoxContainers 0 \
             --seleniumImageName elgalu/selenium:3.8.1-p3 \
             --videoRecordingEnabled false \
             --sendAnonymousUsageInfo false \
             --gridUser swet --gridPassword test

この作成したDeploymentは以下のようにGKEのServiceとして公開することができます。

$ kubectl expose deployment zalenium --type=LoadBalancer

LoadBalancer起動後、以下のコマンドでSelenium GridのURLが取得できます。

$ echo http://swet:test@$(kubectl get service zalenium \
         -o go-template='{{(index .status.loadBalancer.ingress 0).ip}}:{{(index .spec.ports 0).port}}')/wd/hub
http://test:swet@35.190.235.227:4444/wd/hub

ここでは、Basic認証の情報がURLについているので、--gridUser--gridPasswordを変えていた場合は適宜読み替えてください。 このBasic認証情報付きのURLはそのまま、先程のスクリプトの(1)のURLを置き換えて利用することができます。

また、Zaleniumのライブプレビュー画面とダッシュボードもBasic認証で守られていることが確認できます。

ちなみに、下のスクリーンショットはGKEを使って10並列でブラウザを動かしている様子です。

f:id:swet-blog:20171215194322p:plain
GKE上で10並列で動かしている様子

クラスタの削除

以上、GKEでZaleniumを利用可能なことがわかったのでクラスタを削除します。

$ gcloud container clusters delete zalenium

まとめ

以上のようにKubernetesを利用してクラウド上でZaleniumを利用可能となりました。 今回は導入手順を紹介するだけで実践的な内容は最小限にとどめています。 運用上注意することや工夫すること等は今後も紹介していきたいと思いますので楽しみにしていてください。

それでは楽しいクラウドでZaleniumライフを。

参考文献