From b56b47bc8d16967d2749b86a3bc4ac98f43e6573 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Hus=C3=A1k?= Date: Thu, 28 May 2020 10:19:03 +0200 Subject: [PATCH] Add basis of web application benchmarks --- .gitignore | 1 + webapps/.dockerignore | 3 + webapps/apps/helloworld/benchmarkConfig.json | 3 + webapps/apps/helloworld/hello.php | 3 + webapps/apps/helloworld/install.sh | 2 + webapps/client.Dockerfile | 3 + webapps/config/peachpie/Server/Program.cs | 30 ++++++ webapps/config/peachpie/Server/Server.csproj | 15 +++ .../peachpie/Website/Website.msbuildproj | 12 +++ webapps/config/peachpie/peachpie.sln | 48 +++++++++ webapps/run.ps1 | 99 +++++++++++++++++++ ...-peachpie-0.9.981-aspnetcore3.1.Dockerfile | 17 ++++ webapps/server-php-7.4.6-apache.Dockerfile | 7 ++ 13 files changed, 243 insertions(+) create mode 100644 webapps/.dockerignore create mode 100644 webapps/apps/helloworld/benchmarkConfig.json create mode 100644 webapps/apps/helloworld/hello.php create mode 100644 webapps/apps/helloworld/install.sh create mode 100644 webapps/client.Dockerfile create mode 100644 webapps/config/peachpie/Server/Program.cs create mode 100644 webapps/config/peachpie/Server/Server.csproj create mode 100644 webapps/config/peachpie/Website/Website.msbuildproj create mode 100644 webapps/config/peachpie/peachpie.sln create mode 100644 webapps/run.ps1 create mode 100644 webapps/server-peachpie-0.9.981-aspnetcore3.1.Dockerfile create mode 100644 webapps/server-php-7.4.6-apache.Dockerfile diff --git a/.gitignore b/.gitignore index 227f58e..77980ef 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ # Measured benchmark results microbenchmarks/results.json +webapps/results.json # User-specific files *.suo diff --git a/webapps/.dockerignore b/webapps/.dockerignore new file mode 100644 index 0000000..537b44a --- /dev/null +++ b/webapps/.dockerignore @@ -0,0 +1,3 @@ +*.Dockerfile +run.ps1 +results.json diff --git a/webapps/apps/helloworld/benchmarkConfig.json b/webapps/apps/helloworld/benchmarkConfig.json new file mode 100644 index 0000000..3a1b424 --- /dev/null +++ b/webapps/apps/helloworld/benchmarkConfig.json @@ -0,0 +1,3 @@ +{ + "requestPath": "/hello.php" +} diff --git a/webapps/apps/helloworld/hello.php b/webapps/apps/helloworld/hello.php new file mode 100644 index 0000000..82966e2 --- /dev/null +++ b/webapps/apps/helloworld/hello.php @@ -0,0 +1,3 @@ +() + .UseUrls("http://*:80/") + .Build(); + + host.Run(); + } + } + + class Startup + { + public void Configure(IApplicationBuilder app, IHostEnvironment env) + { + app.UsePhp(new PhpRequestOptions(scriptAssemblyName: "peachpie")); + } + } +} \ No newline at end of file diff --git a/webapps/config/peachpie/Server/Server.csproj b/webapps/config/peachpie/Server/Server.csproj new file mode 100644 index 0000000..062becd --- /dev/null +++ b/webapps/config/peachpie/Server/Server.csproj @@ -0,0 +1,15 @@ + + + + + + Exe + netcoreapp3.1 + + + + + + + + \ No newline at end of file diff --git a/webapps/config/peachpie/Website/Website.msbuildproj b/webapps/config/peachpie/Website/Website.msbuildproj new file mode 100644 index 0000000..7405ab5 --- /dev/null +++ b/webapps/config/peachpie/Website/Website.msbuildproj @@ -0,0 +1,12 @@ + + + + Library + netstandard2.0 + peachpie + + + + + + \ No newline at end of file diff --git a/webapps/config/peachpie/peachpie.sln b/webapps/config/peachpie/peachpie.sln new file mode 100644 index 0000000..6f9cf7c --- /dev/null +++ b/webapps/config/peachpie/peachpie.sln @@ -0,0 +1,48 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Website", "Website\Website.msbuildproj", "{A271793F-72BF-429D-9EC8-83C03559CBD6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server", "Server\Server.csproj", "{AF5D53C1-32B5-473F-9229-817608068701}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A271793F-72BF-429D-9EC8-83C03559CBD6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A271793F-72BF-429D-9EC8-83C03559CBD6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A271793F-72BF-429D-9EC8-83C03559CBD6}.Debug|x64.ActiveCfg = Debug|x64 + {A271793F-72BF-429D-9EC8-83C03559CBD6}.Debug|x64.Build.0 = Debug|x64 + {A271793F-72BF-429D-9EC8-83C03559CBD6}.Debug|x86.ActiveCfg = Debug|x86 + {A271793F-72BF-429D-9EC8-83C03559CBD6}.Debug|x86.Build.0 = Debug|x86 + {A271793F-72BF-429D-9EC8-83C03559CBD6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A271793F-72BF-429D-9EC8-83C03559CBD6}.Release|Any CPU.Build.0 = Release|Any CPU + {A271793F-72BF-429D-9EC8-83C03559CBD6}.Release|x64.ActiveCfg = Release|x64 + {A271793F-72BF-429D-9EC8-83C03559CBD6}.Release|x64.Build.0 = Release|x64 + {A271793F-72BF-429D-9EC8-83C03559CBD6}.Release|x86.ActiveCfg = Release|x86 + {A271793F-72BF-429D-9EC8-83C03559CBD6}.Release|x86.Build.0 = Release|x86 + {AF5D53C1-32B5-473F-9229-817608068701}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AF5D53C1-32B5-473F-9229-817608068701}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AF5D53C1-32B5-473F-9229-817608068701}.Debug|x64.ActiveCfg = Debug|x64 + {AF5D53C1-32B5-473F-9229-817608068701}.Debug|x64.Build.0 = Debug|x64 + {AF5D53C1-32B5-473F-9229-817608068701}.Debug|x86.ActiveCfg = Debug|x86 + {AF5D53C1-32B5-473F-9229-817608068701}.Debug|x86.Build.0 = Debug|x86 + {AF5D53C1-32B5-473F-9229-817608068701}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AF5D53C1-32B5-473F-9229-817608068701}.Release|Any CPU.Build.0 = Release|Any CPU + {AF5D53C1-32B5-473F-9229-817608068701}.Release|x64.ActiveCfg = Release|x64 + {AF5D53C1-32B5-473F-9229-817608068701}.Release|x64.Build.0 = Release|x64 + {AF5D53C1-32B5-473F-9229-817608068701}.Release|x86.ActiveCfg = Release|x86 + {AF5D53C1-32B5-473F-9229-817608068701}.Release|x86.Build.0 = Release|x86 + EndGlobalSection +EndGlobal diff --git a/webapps/run.ps1 b/webapps/run.ps1 new file mode 100644 index 0000000..10305cc --- /dev/null +++ b/webapps/run.ps1 @@ -0,0 +1,99 @@ +param ( + [string] $serverDockerFilter='server-*', + [string] $appFilter='*', + [int] $seconds=30, + [int] $concurrency=20, + [string] $outputFile='results.json' +) + +# Maximum theoretical number of requests to run (in practice it's limited by time instead) +$maxRequests = 10000000 + +# Common helper variables for consistent naming of images and containers +$network = "appbenchmarks" +$clientContainer = "${network}-client" +$serverContainer = "${network}-server" +$clientTag = "${clientContainer}:latest" + +# Helper function to create a reasonably named tag for the generated image +function Get-ServerTagName { + param ($serverDockerFile, $appDir) + $serverName = $serverDockerFile.Basename.ToLower() + $appName = $appDir.Name.ToLower() + return "appbenchmarks-${serverName}-app-${appName}:latest" +} + +# Helper function for parsing text information using Regex +function Select-Regex { + param ([string]$subject, [string]$pattern) + $m = $subject | Select-String -pattern $pattern + return $m.Matches[0].Groups[1].Value +} + +# Gather *.Dockerfile files of servers according to the filter +$serverDockerFiles = Get-ChildItem . -Filter $serverDockerFilter | Where-Object {$_.Name -like "server-*.Dockerfile"} + +# Gather all the directories of all the apps to benchmark +$appDirs = Get-ChildItem ./apps -Filter $appFilter + +# Generate all the docker images, one for each pair of server and app +Foreach ($serverDockerFile in $serverDockerFiles) { + Foreach ($appDir in $appDirs) { + $tag = Get-ServerTagName $serverDockerFile $appDir + & docker build --tag $tag --file $serverDockerFile --build-arg app=$appDir . + } +} + +# Generate the docker image for the benchmark client +& docker build --tag $clientTag --file ./client.Dockerfile . + +# Create the network to enable communication between the containers +& docker network create $network + +# Run all the apps, benchmark them and gather the results +$results = @{} +Foreach ($serverDockerFile in $serverDockerFiles) { + $results[$serverDockerFile.Basename] = @{} + Foreach ($appDir in $appDirs) { + # Run the given container in the network + $tag = Get-ServerTagName $serverDockerFile $appDir + & docker run --name $serverContainer --network $network --detach --rm $tag + + # Run the client container in the network + & docker run --name $clientContainer --network $network --detach --rm --tty $clientTag + + # Compose the URL to run the benchmarks on + $appDirFullPath = $appDir.Fullname + $benchmarkConfig = Get-Content "${appDirFullPath}/benchmarkConfig.json" | ConvertFrom-Json + $requestPath = $benchmarkConfig.requestPath + $url = "http://${serverContainer}${requestPath}" + + # Wait for the server to start + Start-Sleep -s 10 + + # Warm-up server (the client is reused to prevent from cooling it down due to reset) + & docker exec $clientContainer ab -t $seconds -c $concurrency -n $maxRequests $url + + # Run the benchmarks and obtain the results + $abOutput = (& docker exec $clientContainer ab -t $seconds -c $concurrency -n $maxRequests $url) | Out-String + + # Gather the results from the Apache Bench output + $results[$serverDockerFile.Basename][$appDir.Name] = @{ + concurrency = $concurrency; + time_ms = $seconds * 1000; + requests = (Select-Regex $abOutput "Complete requests:[ ]+([0-9]+)") -as [int]; + requests_failed = (Select-Regex $abOutput "Failed requests:[ ]+([0-9]+)") -as [int]; + requests_per_second = (Select-Regex $abOutput "Requests per second:[ ]+([0-9]+(?:\.[0-9]+)?) \[#\/sec\] \(mean\)") -as [double]; + request_time_ms = (Select-Regex $abOutput "Time per request:[ ]+([0-9]+(?:\.[0-9]+)?) \[ms\] \(mean\)") -as [double]; + } + + # Stop the containers (will be removed thanks to --rm on start) + & docker stop $clientContainer $serverContainer + } +} + +# Export the results +ConvertTo-Json -Compress $results | Out-File $outputFile + +# Clean up +& docker network rm $network diff --git a/webapps/server-peachpie-0.9.981-aspnetcore3.1.Dockerfile b/webapps/server-peachpie-0.9.981-aspnetcore3.1.Dockerfile new file mode 100644 index 0000000..085f0d2 --- /dev/null +++ b/webapps/server-peachpie-0.9.981-aspnetcore3.1.Dockerfile @@ -0,0 +1,17 @@ +FROM mcr.microsoft.com/dotnet/core/sdk:3.1 + +ARG app + +COPY ./config/peachpie /peachpie + +COPY ./apps/$app /app +WORKDIR /app +RUN ["/app/install.sh", "/peachpie/Website"] + +WORKDIR /peachpie +RUN echo "{ \"msbuild-sdks\": { \"Peachpie.NET.Sdk\": \"0.9.981\" } }" > global.json +RUN dotnet restore +RUN dotnet build -c Release + +WORKDIR /peachpie/Server +ENTRYPOINT ["dotnet", "run", "--no-build", "-c", "Release"] diff --git a/webapps/server-php-7.4.6-apache.Dockerfile b/webapps/server-php-7.4.6-apache.Dockerfile new file mode 100644 index 0000000..4ada769 --- /dev/null +++ b/webapps/server-php-7.4.6-apache.Dockerfile @@ -0,0 +1,7 @@ +FROM php:7.4.6-apache + +ARG app + +COPY ./apps/$app /app +WORKDIR /app +RUN ["/app/install.sh", "/var/www/html"]