Web アプリケーションの負荷テストを実行したくなったので、Azure Container Instances(ACI)を使って、Cypress を実行する仕組みを作ってみました。
Cypress とは
Cypress は、Web アプリケーションの E2E テストを実行するためのツールです。Selenium よりも、シンプルに導入でき、テスト実行も速いです。
npm でインストールでき、Test Runner は無料で使えるので、CI パイプラインにも組み込みやすいです。
SaaS で提供されている Dashboard を連携させると、テスト結果のエビデンス(スクリーンショットや動画のファイル)が自動でアップロードされます。3ユーザーで月500テストケースまでなら無料です。
www.cypress.io
Cypress でテストケースを作成する
例えば、Google で「cypress.io」を検索して結果ページのタイトルを検証するテストケースであれば、こんな感じで書きます。
const targetUrl = Cypress.env('target_url') describe('My First Test', function() { it('Visit Google', function() { cy.visit(targetUrl) cy.get("input[name='q']") .type('cypress.io') cy.contains('Google 検索') .click() cy.title().should('eq', 'cypress.io - Google 検索') }) })
Cypress.env
で、環境変数からテスト対象の URL を取得しています。初期値は、cypress.env.json
で指定しています。
{ "target_url": "https://www.google.co.jp" }
npx cypress run
コマンドでテストを実行します。
UI を使ってインタラクティブに実行させたい場合は、npx cypress open
コマンドを使います。
Cypress を実行する Docker Images を作成する
Cypress を ACI で動かしたいので、Docker Images を作ります。Cypress の公式イメージを使って、日本語フォントを追加しておきました。
FROM cypress/included:5.3.0 # 日本語フォントを追加 RUN apt-get install --no-install-recommends -y fonts-noto fonts-noto-cjk WORKDIR /app COPY ./cypress ./cypress COPY ./cypress.json ./cypress.json COPY ./cypress.env.json ./cypress.env.json RUN npx cypress run
これで Cypress を Docker で実行できるようになりました。
$ docker build -t thara0402/cypress-docker:1.0.0 . $ docker run --rm -it thara0402/cypress-docker:1.0.0
今回は、Docker Hub に Images をプッシュしておきます。
$ docker push thara0402/cypress-docker:1.0.0
Azure Container Instances で負荷テストを実行する
ACI は、ARM Template を使ってデプロイします。
{ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", "parameters": { "resourceNamePrefix": { "type": "string" }, "imageType": { "type": "string", "allowedValues": [ "Public", "Private" ] }, "imageName": { "type": "string" }, "osType": { "type": "string", "allowedValues": [ "Linux", "Windows" ] }, "numberCpuCores": { "type": "string" }, "memory": { "type": "string" }, "restartPolicy": { "type": "string", "allowedValues": [ "OnFailure", "Always", "Never" ] }, "ipAddressType": { "type": "string" }, "ports": { "type": "array" }, "environmentVariables": { "type": "array" }, "numberOfInstances": { "type": "int", "minValue": 1, "maxValue": 10 } }, "variables": { "containerInstanceName":"[concat(parameters('resourceNamePrefix'), '-aci')]" }, "resources": [ { "location": "[resourceGroup().location]", "name": "[concat(variables('containerInstanceName'), copyindex())]", "type": "Microsoft.ContainerInstance/containerGroups", "apiVersion": "2019-12-01", "properties": { "containers": [ { "name": "[concat(variables('containerInstanceName'), copyindex())]", "properties": { "image": "[parameters('imageName')]", "resources": { "requests": { "cpu": "[int(parameters('numberCpuCores'))]", "memoryInGB": "[float(parameters('memory'))]" } }, "environmentVariables": "[parameters('environmentVariables')]", "ports": "[parameters('ports')]" } } ], "restartPolicy": "[parameters('restartPolicy')]", "osType": "[parameters('osType')]", "ipAddress": { "type": "[parameters('ipAddressType')]", "ports": "[parameters('ports')]", "dnsNameLabel": "[concat(variables('containerInstanceName'), copyindex())]" } }, "tags": {}, "copy": { "name": "containerInstanceLoop", "count": "[parameters('numberOfInstances')]" } } ], "outputs": { } }
テストが失敗した場合に ACI が再起動されないように、restartPolicy
で Never
を指定しています。環境変数は environmentVariables
で指定していて、環境変数名のプレフィックスに cypress_
が必要です。
{ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", "contentVersion": "1.0.0.0", "parameters": { "resourceNamePrefix": { "value": "<ACIの名前のプレフィックスを指定してください>" }, "imageType": { "value": "Public" }, "imageName": { "value": "thara0402/cypress-docker:1.3.0" }, "osType": { "value": "Linux" }, "numberCpuCores": { "value": "1" }, "memory": { "value": "1.5" }, "restartPolicy": { "value": "Never" }, "ipAddressType": { "value": "Public" }, "ports": { "value": [ { "port": "80", "protocol": "TCP" } ] }, "environmentVariables": { "value": [ { "name": "cypress_target_url", "value": "https://www.google.co.jp" } ] }, "numberOfInstances": { "value": 1 } } }
numberOfInstances
に指定した数で、ACI を使った Cypress の負荷テストを実行できます。
$ az group create -n <ResourceGroup Name> -l japaneast $ az group deployment create -g <ResourceGroup Name> --template-file template.json --parameters parameters.json
繰り返し負荷テストを実行する場合は、ACI を開始するスクリプトを組めば OK です。
#!/usr/bin/env bash for aci in $(az container list --resource-group <ResourceGroup Name> --query "[].name" -o tsv); do az container start -g <ResourceGroup Name> -n $aci --no-wait echo $aci starting... done
負荷テスト結果を確認する
負荷テスト結果は、ACI のステータスを確認して判断することにします。
Azure CLI の az container list
で ACI のリストを取得し、az container show
でステータスを取得します。
$ az container list --resource-group <ResourceGroup Name> --query "[].id" -o tsv | xargs -I {} -P 10 az container show --query "{name:name,state:instanceView.state}" -o tsv --ids "{}" test-runner-aci0 Succeeded test-runner-aci1 Succeeded test-runner-aci2 Succeeded
まとめ
Azure Container Instances を使って Cypress で負荷テストを実行してみました。
テスト結果を残したい場合は、Cypress Dashboard と連携させたり、AzCopy で Blob にファイルを転送したりする構成を追加する必要があります。
Docker で動かせるのであれば、Cypress でなくとも使える仕組みです。このようなユースケースは、ACI が向いていると思います。
Azure サブスクリプションでリージョン毎にデプロイできる ACI はクオーターが100個までなので、もっと大規模な負荷テストを実行する場合は、Azure Kubernetes Services を使ったほうがよさそうです。
今回のスクリプト一式は、こちらで公開しています。
github.com
github.com