SeleniumなテストをJenkinsさんにお願いする2020

久々の記事です。NIIさんにて相変わらずプライベートクラウドのインフラ周りのお仕事をしているわけですが、 [「フルスタック地獄で完全な自動化は難しい」　国立情報学研究所はクラウド運用効率化をどう進めている？](https://www.atmarkit.co.jp/ait/articles/2008/27/news007.html) の記事にまとめていただいているように、Jupyter Notebookを取り入れながらの運用手法を追求していたりします。

この記事は6年前くらいに書いた [Java+Seleniumな自動テストプロジェクトをJenkinsさんにお願いする](https://qiita.com/yacchin1205/items/e742519e2df32c006679) に関して、Jupyter Notebookのエッセンスを加えてアップデートしたものです。

# TL;DR

Jupyter NotebookとJenkinsを組み合わせた [jupyter-with-jenkins](https://github.com/yacchin1205/jupyter-with-jenkins) で、

* Jupyter Notebookによるプロトタイピング的なSeleniumテスト作成
* Jenkins + papermillでSeleniumテストを即定期実行

が実現できる！

# 背景

ここ最近はお仕事としていただいている、プロトタイピング寄りの開発・運用と、学生としてやっている実験環境の開発・運用をメインに活動しています。1〜3年間くらいの実験的な運用を想定したプロジェクトの割合が高く、運用にどこまで力をかけるかという問題があります。

ユニットテストをそれなりに書いているとはいえ、やっぱり実際動いているものに対してSeleniumによるテストを定期的に走らせることができれば安心感が段違いでしょう。でも、真面目にSeleniumテストを書くのも大変だし、それを定期実行する環境用意するのも大変だし、うーんと思っていました。

# 作ったもの

それで作ったのが [jupyter-with-jenkins](https://github.com/yacchin1205/jupyter-with-jenkins) です。
その名の通り、Jupyter ContainerにJenkinsを同梱したものです。設計は以下のような感じ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28770/a843de1f-b93c-c3c4-277f-809aff204606.png)

NIIの[全部入りイメージ](https://github.com/NII-cloud-operation/Jupyter-LC_docker)ベースのJupyter Notebook環境にJenkinsを組み込んでいます。Jenkinsからは [papermill](https://github.com/nteract/papermill) を使ってNotebookを実行する感じ。
JenkinsコンテナをJupyter Notebookにsidecar的につなげるべきか、とか当初考えていたのですが、[MyBinder](https://mybinder.org)が急激にいい感じになってきたので、これで簡単に試せるようにしたいなと。

# とりあえず試してみる - MyBinderでの実行

MyBinderを使うとアカウント登録とか課金とかナシにこのコンテナを試せます。1時間限定ですが。以下のリンクを開くだけです。

* https://mybinder.org/v2/gh/yacchin1205/jupyter-with-jenkins/master

しばらく待つと、MyBinder配下のサーバでJupyterコンテナが起動してくれます。
Jupyter Notebookの画面に転送されますので、以下のように [New] ドロップダウンの [jenkins] をクリックすると、Jenkinsのいつもの画面が現れます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28770/52947be3-88f6-aea4-4959-9d96ef9cb87a.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28770/061874bf-d4bf-2f02-6475-7dbc5147cd50.png)

なお、設計のようにJenkinsはJupyter NotebookにReverse Proxyされて見える感じですので、Jenkins側では認証設定はしておらず、Jupyter Notebookの認証に任せています。

## Seleniumテストを書いてみる

MyBinderで起動した環境に用意されているJupyter Notebook `Using Selenium.ipynb` ([Binderで開く](https://mybinder.org/v2/gh/yacchin1205/jupyter-with-jenkins/master?filepath=Using%20Selenium.ipynb)) を試してみてください。
`driver.save_screenshot` と `IPython.display.Image` の組み合わせで、セルにDriverに表示されている画面を逐一出力しながら操作を確認できるのが良いんではないかなと。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28770/475318cf-1b13-5807-1702-20028ff31a96.png)

## Jenkinsで実行してみる

作成したNotebookをJenkinsで実行するには、papermillを使います。以下のようなシェルスクリプトで簡単に実行できます。

```
# Move notebook directory
cd /home/jovyan/

# Execute notebook
papermill "Using Selenium.ipynb" "${WORKSPACE}/${BUILD_ID}.ipynb"
```

Jenkinsで適当なジョブを作成し、設定に以下のように仕込めばOK。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28770/18b63575-3bf6-2044-da94-53d1706f09cb.png)

実行結果Notebookの出力先は `${WORKSPACE}/${BUILD_ID}.ipynb` としていますが、Workspaceに対してはJupyter Notebookの `jenkins-workspace` としてシンボリックリンクを用意していますので、ここから実行結果を確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28770/f0d0ad55-12ad-45e5-f03c-092661b7c3c5.png)

これでジョブに定期実行の設定と、通知の設定(Slack等)を行うだけで、Jupyter Notebook実行がコケたときに通知が飛んでくる仕掛けが構築できます。

# 動かしつづける

MyBinderは1時間限定なので、どこかに自分の環境を用意しなければなりません。ここでは、適当なDocker Engineがインストールされた自前環境を使うケースと、Amazon Elastic Container Serviceを使うケースについて紹介します。

## 自前環境で

適当なDocker Engineがインストールされたマシンで適当な作業ディレクトリを作り、以下を実施してください。

```
$ cd (作業ディレクトリ)
$ docker run -d -p 8888:8888 -v $(pwd):/home/jovyan --name jupyter-with-jenkins yacchin1205/jupyter-with-jenkins
```

コンテナ中の `/home/jovyan` にホストのディレクトリをマウントすることで、作成したNotebookやJenkins設定が永続化されるようにしています。
こうすると、8888ポートでjupyter-with-jenkinsが動き出します。 `http://localhost:8888` を開いてください。
Tokenの入力を求められますので、`docker logs`でコンテナのログを確認してください。

```
$ docker logs jupyter-with-jenkins
...
    Or copy and paste one of these URLs:
        http://xxxxxxxxxxxx:8888/?token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
     or http://127.0.0.1:8888/?token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

上記の `token=トークン文字列` のトークン文字列部分をブラウザに入力してください。MyBinderの時と同様にJupyter Notebookの画面が表示されます。

## Amazon Elastic Container Serviceで

監視系なので常時動かしておきたいですよね。以下のような環境を構築するNotebookを、Jupyter Notebook `Run Container on AWS ECS.ipynb` ([Binderで開く](https://mybinder.org/v2/gh/yacchin1205/jupyter-with-jenkins/master?filepath=Run%20Container%20on%20AWS%20ECS.ipynb)) にて、AWS Elastic Container Service上でjupyter-with-jenkinsコンテナを起動する手順を提供しています。
このNotebookにより作成される環境は以下のような構造になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28770/956797ce-65b5-a29e-ad34-d74221fdf976.png)


Notebookでは、 `aws` コマンドを使って CloudFormationを使ってVPC, Subnet, Security Groupを作成し、NotebookやJenkins設定を永続化するためElastic File System上にファイルシステムを作成した後、このネットワーク・ファイルシステムを使う形でECSのサービスを作成しています。

このNotebookを使う際は、コンテナ環境にAWSのAccess Key / Secret Keyを設定することになりますので、これらのキーの管理には十分注意してください。

# まとめ

こんな感じで、Jupyterのプロトタイピング環境とJenkinsの定期実行環境の合わせ技で、えいやっと自動テストを回して精神の安寧を得ることができつつあります。
もちろんきちんとしたテストの作り込みは必要なのですが、何もしない から 理想的なテスト の間のつなぎ役として使えると良いのではないかなあと。Firefox Driverを加えるとか、いろいろ考えられるので、ご興味ある方はPull Requestを是非にー

* https://github.com/yacchin1205/jupyter-with-jenkins