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 です。
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