こんにちは、サイオステクノロジー技術部 武井です。今回はAzure Kubernetes Service + Seleniumでお手軽な負荷試験をできる環境を作ってみました。
Seleniumとは?
ブラウザのオートメーションツールです。つまりブラウザの動作(ボタンをクリックしたり、テキストボックスに入力したり)を自動化できるツールです。Headless Chrome / Firefoxを使えばGUIのない環境でもSeleniumが使えますので、テストの自動化に最適です。
負荷テストにSelenium?
Seleniumの用途は、E2Eテストです。Seleniumは1アクセスごとにブラウザが起動しますので、とても動作が重く、大量のリクエストを発生させる必要がある負荷テストには向きません。負荷テストといえば、スレッドでHTTPリクエストを生成するJMeterの十八番です。
ただし、JMeterはシナリオを作るのが大変です。昨今のWebアプリケーションのシーケンスは、ますます複雑化しています。アクセスのたびに動的に生成されたhiddenな値を送信したり、OpenID Connectによる認証フローとか、そういうシーケンスをJMeterで実現するためにはコーディングが必要になります。
Seleniumだったらブラウザの動きをエミュレートするだけですので、裏側でどんなに複雑なHTTPリクエストを発行していようが、クリックひとつで実現可能です。要はユーザーの動作をそのまま模倣できるのでシナリオ作りが簡単です。
ただし、世の中のWebアプリケーションって、それほどたくさんの負荷が必要なものばかりではないと思うのです。B2Cの大規模アプリならともかくとして、B2Bの小中規模なアプリなら同時10もできれば大体OKなときが多いです。でも小中規模なアプリだからといって、シーケンスは変わらず複雑だったりします。
そこでAzure Kubernetes Service
そんなコマッタを解決するためにAzure Kubernetes Serviceを使ってみました。具体的な方法は次のとおりです。
SeleniumでHeadless Chromeを利用してテストスクリプトを作成して、それをDockerコンテナ化して、Kubernetesに展開しました。ちょうど下図のようなイメージです。
一つのNodeの中にコンテナ化したSeleniumスクリプトが格納されたPodがあり、それがWebアプリケーションに対して負荷をかけるような感じです。大量の負荷を生み出したいときには、Nodeを増やします。Azure Kubernetes Serviceであれば、お財布が許す限り、Nodeをいくらでも増やせますので、高い負荷を生成することが可能です。
Seleniumスクリプトが生成する負荷試験のログの永続はfluentd + Application Insightsを使いました。Kubernetes内のPodは、そのPodのコンテナが停止すると、コンテナの中のデータは消えてしまいます。よって、ログやデータベースなどのデータの永続化はひと工夫する必要があります。
Seleniumスクリプトが格納されたPodと、fluentdが格納されたPodは、一つのVolumeを共有するします。Seleniumが吐き出したログをfluentdがtailします。Application Insightsへのログの書き出しは、Application Insights用の専用のプラグインを利用して実現します。いわゆるサイドカーとしてfluentdを配置します。この方法により、Seleniumが吐き出したログはApplication Insightsに記録されるので、ログの永続が実現できます。
Application Insightsのアラート機能により、ログに特定の文字列(例えばERRORなど)が出力された際に特定のメールアドレスに通知するようにしておきます。このようにしておけば、ログを定期的に見る必要もなくなりラクチンです。
作り方
本章では、先にご紹介した負荷テスト環境の作り方について説明致します。以下の条件を前提とします。
- Azure Kubernetes Serviceは構築済みであること(このブログをご参照下さい)
- 負荷をかけるSeleniumスクリプトは作成済みであること
- Application Insightsの環境は作成済みであること
Seleniumスクリプト実行用Dockerコンテナのイメージを作る
まずSeleniumスクリプトを実行するためのDockerコンテナのイメージを作ります。先の図で言うところのSeleniumという名前のPodに入るコンテナのことです。本システムではSeleniumで負荷をかけるアプリケーションはJavaで実装することを前提としています。
以下のDockerfileを作成して、コンテナのイメージを作成して下さい。
FROM centos:7 RUN yum -y install java-1.8.0-openjdk wget unzip # 最新のSelenium Driverが最新のChromeに対応していないので、旧バージョンのSeleniumをダウンロードして実行します。 RUN wget https://dist.control.lth.se/public/CentOS-7/x86_64/google.x86_64/Packages/google-chrome-stable-71.0.3578.98-1.x86_64.rpm -P /tmp && \ yum -y install libOSMesa google-noto-cjk-fonts && \ yum -y localinstall /tmp/google-chrome-stable-71.0.3578.98-1.x86_64.rpm # Selenium Driverをダウンロードしインストールします。 RUN wget https://chromedriver.storage.googleapis.com/73.0.3683.68/chromedriver_linux64.zip && \ unzip chromedriver_linux64.zip && \ mv chromedriver /usr/local/bin/ && \ chown root:root /usr/local/bin/chromedriver # Chromeを実行するために必要なライブラリをインストールします。 RUN yum -y install libX11 GConf2 fontconfig # 負荷テストかけるアプリケーションをstresstest.jarという実行形式のjarファイルにして、 # 所定のディレクトリに配置します。 RUN mkdir -p /opt/stress-test/bin COPY ./stresstest.jar /opt/stress-test/bin RUN mkdir -p /opt/stress-test/data COPY ./test.csv /opt/stress-test/data # 負荷テストをかけるJavaアプリケーションを実行します。 CMD ["java","-jar","/opt/stress-test/bin/stresstest.jar"]
fluentdのDockerコンテナのイメージを作る
Seleniumが吐き出したログをApplication Insightsに出力するコンテナのイメージを作成します。
まず、fluentdの設定ファイルであるfluent.confを以下のように作成します。
<source> @type tail # Seleniumが吐き出すログの出力パスです。 path /var/log/stresstest/stress-test.log tag selenium.selenium <parse> @type none </parse> </source> <match **> @type application_insights # Application InsightsのInsturmentation Keyを指定しています。 instrumentation_key <Application InsightsのInsturmentation Key> </match>
次に以下のDockerfileを作成して、コンテナのイメージを作成して下さい。
# fluentdのOffical Imageを使います。 FROM fluent/fluentd:v1.7-1 # fluentdのOfficial Imageのサイト(https://hub.docker.com/r/fluent/fluentd/)に # 書いてあるとおりにカスタマイズする。ただし13行目の部分は # Application Insights用のプラグイン(https://github.com/microsoft/fluent-plugin-application-insights)を # インストールするように変更する。 USER root RUN apk add --no-cache --update --virtual .build-deps \ sudo build-base ruby-dev \ # cutomize following instruction as you wish && sudo gem install fluent-plugin-application-insights \ && sudo gem sources --clear-all \ && apk del .build-deps \ && rm -rf /home/fluent/.gem/ruby/2.5.0/cache/*.gem COPY fluent.conf /fluentd/etc/ USER fluent
作成したコンテナのイメージをAzure Container Registryにpushする
先程作成したコンテナのイメージをAzure Container Registryにpushします。この辺りの詳細な絵オペレーションは、ブログ「Azure Kubernetes Serviceで最速k8s 」を参照して下さい。
Azure Kubernetes Serviceにデプロイするためのマニフェストファイルを作成する。
Azure Kubernetes Serviceにデプロイするために、以下のマニフェストファイルを作成して下さい。
# Seleniumを実行するアプリケーションをデプロイするためのマニフェストです。 apiVersion: apps/v1beta1 kind: Deployment metadata: name: stress-test spec: # ここで指定した下図だけSeleniumが起動します。同時に負荷をかけたい接続数を # 指定して下さい。 replicas: 10 selector: matchLabels: app: stress-test template: metadata: labels: app: stress-test spec: containers: - name: stt image: ntakeiacr.azurecr.io/stt:1.4 # fluentdとログを共有するためのVolumeをmountします。 volumeMounts: - mountPath: /var/log/stresstest name: log-volume imagePullSecrets: - name: docker-reg-credential # fluentdとログを共有するためのVolumeを作成します。 # Pod間でデータを共有したいのでhostPathとします。 volumes: - name: log-volume hostPath: path: /var/log/stresstest --- # fluentdを実行するためのマニフェストです。サイドカーとして配置するので、 # 各Nodeに一つづつ配置します。そのためリソースの種別はDamemonSetとしています。 apiVersion: apps/v1 kind: DaemonSet metadata: name: fluentd spec: selector: matchLabels: app: fluentd template: metadata: labels: app: fluentd spec: containers: - image: ntakeiacr.azurecr.io/fluentd:1.2 name: fluentd # Seleniumとログを共有するためのVolumeをmountします。 volumeMounts: - mountPath: /var/log/stresstest name: log-volume imagePullSecrets: - name: docker-reg-credential # Seleniumとログを共有するためのVolumeを作成します。 # Pod間でデータを共有したいのでhostPathとします。 volumes: - name: log-volume hostPath: path: /var/log/stresstest
では実行!!
以下のコマンドを実行して、Azure Kubernetes Serviceにデプロイすれば負荷テストが実行されます。
$ kubectl apply -f deploymeny.yml
まとめ
いかがでしたでしょうか?それほど高負荷ではなく、しかも負荷テストシナリオ作成に手間をかけたくないときは、有効な手法かと思います。これが正しいSeleniumの使い方でないような気もしますが、、、。でも、便利だと思います(´▽`)