Selenium Grid on Kubernetes

[Kubernetes Meetup Tokyo #5](https://k8sjp.connpass.com/event/56945/)の登壇資料です。

---

# アジェンダ

* Why Selenium Grid?
* Why Selenium Grid on Kubernetes?
* How-tos
* Gotachas

---

# Why Selenium Grid?

* 複数ブラウザを使ったE2Eテストを並列化するため

## Selenium

ブラウザのオートメーションツール。**WebアプリケーションのE2Eテストなどに使う**

## Selenium Grid

[複数のマシンやコンテナにSeleniumのテストを分散並列実行させるためのプロキシ](https://seleniumhq.github.io/docs/grid.html#selenium_grid)。**FirefoxやChromeなど複数ブラウザを使ったE2Eテストを並列化するなどの目的で使う**

---

# Why Selenium Grid on Kubernetes?

Dev/Prod Parity & Self-hosting

---

# Selenium Grid: Pain Points

* Selenium GridのURLがローカルと非ローカル環境で異なる(Dev/Prod Parity)
* 経費的な問題でBrowserStack、SauceLabsなどの有償サービスを使いたくない→Self-hosting
* Selenium Gridの構築がめんどくさい

---

# Selenium Grid on Kubernetes: Good Points

* Selenium GridのURLがローカルと非ローカル環境で一緒(Dev/Prod Parity)
* Self-hosting
* Selenium Gridの構築がコマンド一発

---

# How-tos

* Kubernetes(K8S)クラスタ作成: ローカル & AWS
* K8SにSelenium Gridをインストール
* Chromeノードにリモートデスクトップ接続して観察・デバッグ
* Pythonのreplでテストコードを書く
* pytestでテスト実行
* K8SにConcourseをインストール
* テスト実行の様子を観察
* プロセス並列で実行

---

# K8Sクラスタ作成: ローカルマシン上に

[kubernetes/minikube](https://github.com/kubernetes/minikube)という便利なツールが

`minikube start --cpus 4 --memory 4096`

* CPUは4コア(Concourse Web/Workerでそれぞれ1コア+その他で2コア)
  * 　minikubeVMのデフォルトメモリサイズは1024M
  * 　MySQL, Redis, Selenium Grid Hub, Selenium Node 
  Firefox/Chromeを起動するとだいたい4GBくらい

---

# K8Sクラスタ作成: AWS上に

[kubernetes-incubator/kube-aws](https://github.com/kubernetes-incubator/kube-aws)という便利なツールが

```
$ mkdir my-cluster
$ cd my-cluster
$ kube-aws init --cluster-name=mycluster \
--external-dns-name=mycluster.example.com \
--region=ap-northeast-1 \
--availability-zone=ap-northeast-1a \
--key-name=mykeypair \
--kms-key-arn="arn:aws:kms:ap-northeast-1:xxxxxxxxxx:key/xxxxxxxxxxxxxxxxxxx" \
--hosted-zone-id=myhostedzoneid
$ kube-aws render credentials --generate-ca
$ kube-aws render stack
$ kube-aws up --s3-uri s3://<your-bucket>/<optional-prefix>
```

※メンテナです。宣伝乙ごめんね

---

# Selenium Gridインストール

* Kubernetesにアプリをインストールする場合は基本的にhelmというツール使用
* VNCクライアントでWebDriver Podにリモートデスクトップ接続して、ブラウザが動く様子をみたい
  * `(chrome|firefox)Debug.enabled=true`

```
helm install stable/selenium \
  --set chromeDebug.enabled=true \
  --set firefoxDebug.enabled=true
  --name selenium-grid
```

---

# Seleniumテストを書く環境作成: kubectl exec

ブラウザ=SeleniumのRemoteWebDriver

```
kubectl run selenium-python --image=google/python-hello
```

```
export PODNAME=`kubectl get pods --selector="run=selenium-python" --output=template --template="{{with index .items 0}}{{.metadata.name}}{{end}}"`

# 初回はdocker pullに時間がかかるので数分待ってから・・・
kubectl exec --stdin=true --tty=true $PODNAME bash
```

```
pip install selenium
python
```

---

# Seleniumテストを書く環境作成: telepresence

[telepresence](http://www.telepresence.io/) という、ローカルマシンからK8Sネットワークに透過的にアクセスできるようにするツール

```
$ telepresence --new-deployment telepresence \
  --method inject-tcp \
  --namespace default \
  --run-shell
```

```
Starting proxy...
@minikube|bash-3.2$
```

**macOS内のbashとそこから起動したプロセスすべてが「K8Sクラスタのネットワーク」に繋がった状態になる**

```
pip install selenium
python
```

---

# Pythonのreplでテストコードを書く

```
from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities

driver = webdriver.Remote(
  command_executor='http://selenium-grid-selenium-hub:4444/wd/hub',
  desired_capabilities=getattr(DesiredCapabilities, "CHROME")
)
driver.get("http://google.com")
assert "google" in driver.page_source
driver.close()
```

---

# pytestでテスト実行

```
pip install -U pytest
```

```python
import time

from selenium import webdriver
from selenium.webdriver.common.desired_capabilities im
port DesiredCapabilities

def test_chrome():
        driver = webdriver.Remote(
          command_executor='http://selenium-grid-selen
ium-hub:4444/wd/hub',
          desired_capabilities=getattr(DesiredCapabili
ties, "CHROME")
        )
        driver.get("http://google.com")
        time.sleep(5)
        assert "google" in driver.page_source
        driver.close()
```

```
pytest
```

しばらく待つとテストが通る。

<img src="https://qiita-image-store.s3.amazonaws.com/0/5912/027655de-12e6-e523-9a94-705e36893db5.png" width="320">

---

# だいたいおわり

---

# Gotachas

* Debugging
* Distributed Testing

---

# ChromeノードにVNCで接続

* VNC: いわゆるリモートデスクトップ
* kubectl port-forwardコマンドを使うと、Kubernetesクラスタ内のpodのポートをローカルマシン側にバインドできる(!)

```
kubectl port-forward --namespace default \
  $(kubectl get pods --namespace default \
    -l app=selenium-grid-selenium-chrome-debug \
    -o jsonpath='{ .items[0].metadata.name }') 5900
```

macOS標準装備のVNCクライアントで接続

```
open vnc://127.0.0.1:5900
```

<img src="https://qiita-image-store.s3.amazonaws.com/0/5912/ce63ced7-98b8-fba1-8a7f-36c53059c6ff.png" width="320">

---

# テスト実行の様子を観察

```
driver.get("http://google.com")
```

実際、VNCクライアントで見てみるとChromeが動いていることがわかる。

<img src="https://qiita-image-store.s3.amazonaws.com/0/5912/5ebd7ad6-c3f8-9586-1a61-4379d9d68bd2.png" width="320">

---

# 複数プロセスでテストを分散並列実行

[pytest-xdist](https://docs.pytest.org/en/3.0.1/xdist.html)を使う。

```
pip install --upgrade setuptools
pip install pytest-xdist

...

pytest -n <プロセス数>
```

※ [setuptoolsをアップグレードしないと-nオプションが認識されない。pythonあるあるなの?](https://stackoverflow.com/a/41066450)

---

# 複数マシンでテストを分散並列実行

## Why?

* 素朴にpytest -nすると、１コンテナに割り当てたCPU・メモリでさばけるプロセス数までしかスケールしない

## How?

* テスト配布元マシン 1 --同期--> N テスト実行マシン
* submitter --> ファイルストレージ --> master --> worker

## Example

* submitter(macOS) --> Minio on K8S --> master on K8S Pod --> worker on K8S Job
* kubernetes-incubator/client-python + Minio/AWS S3 + pytest-xdistで実装できる
* submitterの実装例
    * https://github.com/mumoshu/kube-distributed-test-runner/blob/master/examples/oauth2/submit.py
    * 色々やってるけど時間があれば説明

---

# 参考リンク

* [SeleniumのHelm Chart](https://github.com/kubernetes/charts/tree/master/stable/selenium)
* [SeleniumのK8S 
 Example](https://github.com/kubernetes/kubernetes/tree/master/examples/selenium)

---

# まとめ

* Dev-Prod Parity
  * ローカルマシンでも本番サーバでも全く同じテストスクリプトでSelenium Gridを使ったテストが可能
* 環境構築の容易さ
    * K8SさえあればSelenium Gridはhelm install一発
* デバッグ
    * VNCでブラウザの動く様を観察する
* 分散テスト
    * プロセス並列、マシン並列
* 登場したツール
  * Kubernetes, Selenium Grid, minikube, kube-aws, helm, VNC, `kubectl port-forward`, `open vpn://...`, telepresence, pytest-xdist

---

## 自己紹介 & おわり

twitter/github/slack.k8s.io: @mumoshu (むもしゅ)
Primary maintainer of [kubernetes-incubator/kube-aws](https://github.com/kubernetes-incubator/kube-aws)

Fin :bow: :clap: