Skip to content

Commit 472f431

Browse files
authored
Merge pull request #855 from containerd/add-stress-test-to-benchmark
Add stress-test to the benchmark CI
2 parents d338673 + c6de6bf commit 472f431

File tree

7 files changed

+125
-13
lines changed

7 files changed

+125
-13
lines changed

.github/workflows/benchmarks.yml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,49 @@ jobs:
115115
shell: bash
116116
run: |
117117
sudo ctr task kill -s SIGKILL wasi-http
118+
119+
benchmark-stress:
120+
runs-on: ubuntu-latest
121+
122+
steps:
123+
- uses: actions/checkout@v4
124+
- uses: actions-rust-lang/setup-rust-toolchain@v1
125+
with:
126+
rustflags: '' #Disable. By default this action sets environment variable is set to -D warnings. We manage this in the Makefile
127+
- uses: ./.github/actions/setup-env
128+
- name: Setup build profile
129+
shell: bash
130+
run: echo "OPT_PROFILE=release" >> ${GITHUB_ENV}
131+
- uses: ./.github/actions/build
132+
- name: Run Stress Tests
133+
shell: bash
134+
run: |
135+
set -euxo pipefail
136+
for RUNTIME in wasmtime wasmedge wasmer wamr; do
137+
# Run containerd stress test
138+
make test/stress-c8d-$RUNTIME \
139+
STRESS_TEST_COUNT=1000 \
140+
STRESS_TEST_TIMEOUT=10s \
141+
STRESS_TEST_JSON=stress-bench-c8d-$RUNTIME.json \
142+
STRESS_TEST_IMAGE=ghcr.io/containerd/runwasi/wasi-demo-app:latest
143+
144+
# Run non-containerd stress test
145+
make OPT_PROFILE=release test/stress-$RUNTIME \
146+
STRESS_TEST_COUNT=1000 \
147+
STRESS_TEST_TIMEOUT=10s \
148+
STRESS_TEST_JSON=stress-bench-$RUNTIME.json \
149+
STRESS_TEST_IMAGE=ghcr.io/containerd/runwasi/wasi-demo-app:latest
150+
done
151+
cat stress-bench-*.json | jq -s 'flatten(1)' > stress-bench.json
152+
- name: Store benchmark result
153+
uses: benchmark-action/[email protected]
154+
with:
155+
name: Stress Test Benchmark
156+
tool: 'customBiggerIsBetter'
157+
output-file-path: stress-bench.json
158+
github-token: ${{ secrets.GITHUB_TOKEN }}
159+
alert-threshold: '130%'
160+
fail-on-alert: ${{ github.event_name == 'schedule' }}
161+
alert-comment-cc-users: '@runwasi-committers'
162+
summary-always: true
163+
auto-push: ${{ github.event_name == 'schedule' }}

Cargo.lock

Lines changed: 3 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ homepage = "https://github.com/containerd/runwasi"
2323

2424
[workspace.dependencies]
2525
anyhow = "1.0"
26-
cap-std = "1.0"
2726
chrono = { version = "0.4", default-features = false, features = ["clock"] }
2827
containerd-shim = "0.8"
2928
containerd-shim-wasm = { path = "crates/containerd-shim-wasm", version = "0.9.0" }

Makefile

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@ KIND_CLUSTER_NAME ?= containerd-wasm
6969

7070
export
7171

72+
STRESS_TEST_COUNT ?= 100
73+
STRESS_TEST_PARALLEL ?= $$(nproc || echo 10)
74+
STRESS_TEST_TIMEOUT ?= 5s
75+
STRESS_TEST_IMAGE ?= ghcr.io/containerd/runwasi/wasi-demo-app:latest
76+
STRESS_TEST_JSON ?=
77+
STRESS_TEST_JSON_FLAG = $(if $(STRESS_TEST_JSON),--json-output $(STRESS_TEST_JSON),)
78+
7279
.PHONY: build build-common build-wasm build-%
7380
build: build-wasm $(RUNTIMES:%=build-%);
7481

@@ -142,12 +149,25 @@ test-doc:
142149
test/stress-%: dist-%
143150
# Do not use trace logging as that negatively impacts performance.
144151
# Do not use cross (always use cargo) to avoid the qemu environment.
145-
cargo run -p stress-test $(TARGET_FLAG) -- $(PWD)/dist/bin/containerd-shim-$*-v1 --count=100 --parallel=$$(nproc || echo 10) --timeout=5s
152+
cargo run -p stress-test $(TARGET_FLAG) -- \
153+
$(PWD)/dist/bin/containerd-shim-$*-v1 \
154+
--count=$(STRESS_TEST_COUNT) \
155+
--parallel=$(STRESS_TEST_PARALLEL) \
156+
--timeout=$(STRESS_TEST_TIMEOUT) \
157+
--image=$(STRESS_TEST_IMAGE) \
158+
$(STRESS_TEST_JSON_FLAG)
146159

147160
test/stress-c8d-%: dist-%
148161
# Do not use trace logging as that negatively impacts performance.
149162
# Do not use cross (always use cargo) to avoid the qemu environment.
150-
cargo run -p stress-test $(TARGET_FLAG) -- --containerd $(PWD)/dist/bin/containerd-shim-$*-v1 --count=100 --parallel=$$(nproc || echo 10) --timeout=5s
163+
cargo run -p stress-test $(TARGET_FLAG) -- \
164+
--containerd \
165+
$(PWD)/dist/bin/containerd-shim-$*-v1 \
166+
--count=$(STRESS_TEST_COUNT) \
167+
--parallel=$(STRESS_TEST_PARALLEL) \
168+
--timeout=$(STRESS_TEST_TIMEOUT) \
169+
--image=$(STRESS_TEST_IMAGE) \
170+
$(STRESS_TEST_JSON_FLAG)
151171

152172
generate-doc:
153173
RUST_LOG=trace $(CARGO) doc --workspace --all-features --no-deps --document-private-items --exclude wasi-demo-app
Binary file not shown.

crates/stress-test/Cargo.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,18 @@ tokio-async-drop = "0.1.0"
1717
humantime = "2.1.0"
1818
tempfile = { workspace = true }
1919
oci-spec = { workspace = true }
20-
clap = { version = "4", features = [ "derive"] }
20+
clap = { version = "4", features = ["derive"] }
2121
log = { workspace = true }
2222
env_logger = { workspace = true }
2323
nix = { workspace = true, features = ["process", "signal", "mount"] }
2424
trait-variant = "0.1"
2525
containerd-client = "0.6.0"
2626
tonic = "0.12"
27-
serde_json.workspace = true
27+
serde_json = { workspace = true }
28+
serde = { workspace = true }
2829
futures = "0.3"
2930

3031

3132
[package.metadata.cargo-machete]
3233
# used by the bindings generated by trapeze
33-
ignored = ["prost", "prost-types"]
34+
ignored = ["prost", "prost-types"]

crates/stress-test/src/main.rs

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ mod protos;
44
mod traits;
55
mod utils;
66

7-
use std::path::PathBuf;
7+
use std::fs::File;
8+
use std::path::{Path, PathBuf};
89
use std::pin::pin;
910
use std::sync::Arc;
1011
use std::time::Instant;
@@ -16,6 +17,7 @@ use futures::stream::FuturesUnordered;
1617
use futures::{FutureExt as _, StreamExt as _};
1718
use humantime::{format_duration, parse_duration};
1819
use nix::sys::prctl::set_child_subreaper;
20+
use serde::Serialize;
1921
use tokio::signal::ctrl_c;
2022
use tokio::sync::{Barrier, OnceCell, Semaphore};
2123
use tokio::time::Duration;
@@ -30,6 +32,15 @@ enum Step {
3032
Delete,
3133
}
3234

35+
#[derive(Serialize)]
36+
struct BenchmarkResult {
37+
name: String,
38+
unit: String,
39+
value: f64,
40+
#[serde(skip_serializing_if = "Option::is_none")]
41+
extra: Option<String>,
42+
}
43+
3344
#[derive(Parser)]
3445
#[command(version, about, long_about = None)]
3546
struct Cli {
@@ -65,6 +76,10 @@ struct Cli {
6576
/// Image to use for the test
6677
image: String,
6778

79+
#[arg(long)]
80+
/// Output the benchmark results to a JSON file
81+
json_output: Option<PathBuf>,
82+
6883
/// Path to the shim binary
6984
shim: PathBuf,
7085

@@ -95,21 +110,33 @@ async fn main_impl() -> Result<()> {
95110
}
96111
}
97112

113+
fn get_runtime(path: &Path) -> Option<&str> {
114+
let runtime = path
115+
.file_name()?
116+
.to_str()?
117+
.strip_prefix("containerd-shim-")?
118+
.split_once('-')?
119+
.0;
120+
Some(runtime)
121+
}
122+
98123
async fn run_stress_test(cli: Cli, c8d: impl Containerd) -> Result<()> {
99124
let Cli {
100-
shim,
125+
containerd,
126+
shim: shim_path,
101127
container_output,
102128
parallel,
103129
count,
104130
timeout,
105131
image,
132+
json_output,
106133
args,
107134
..
108135
} = cli;
109136

110137
println!("\x1b[1mUsing image {image:?} with arguments {args:?}\x1b[0m");
111138

112-
let shim = c8d.start_shim(shim).await?;
139+
let shim = c8d.start_shim(shim_path.clone()).await?;
113140
let shim = Arc::new(shim);
114141

115142
// create a "pause" container to keep the shim running
@@ -222,11 +249,29 @@ async fn run_stress_test(cli: Cli, c8d: impl Containerd) -> Result<()> {
222249

223250
let elapsed = start.get().unwrap().elapsed();
224251
let throuput = count as f64 / elapsed.as_secs_f64();
225-
let elapsed = format_duration(elapsed);
252+
let duration = format_duration(elapsed);
226253

227254
println!("\x1b[32m{success} tasks succeeded\x1b[0m");
228-
println!("\x1b[32m elapsed time: {elapsed}\x1b[0m");
255+
println!("\x1b[32m elapsed time: {duration}\x1b[0m");
229256
println!("\x1b[32m throuput: {throuput} tasks/s\x1b[0m");
230257

258+
let shim = get_runtime(&shim_path).unwrap_or("unknown");
259+
let containerd_shim = if containerd { "containerd" } else { "mock" };
260+
261+
if let Some(json_output) = json_output {
262+
let results = vec![BenchmarkResult {
263+
name: format!(
264+
"Stress Test Tasks Throughput with {} service - {}",
265+
containerd_shim, shim
266+
),
267+
unit: "tasks/s".to_string(),
268+
value: throuput,
269+
extra: Some(format!(
270+
"Image: {}\nTasks: {}\nParallel: {}\nDuration: {}",
271+
image, count, parallel, duration
272+
)),
273+
}];
274+
serde_json::to_writer_pretty(&mut File::create(json_output)?, &results)?;
275+
}
231276
Ok(())
232277
}

0 commit comments

Comments
 (0)