mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-08-02 00:02:01 +00:00
Merge pull request #8082 from dborquez/enable_fio_on_ctr
Enable fio test using containerd client
This commit is contained in:
commit
265f53e594
@ -147,7 +147,7 @@ done)
|
||||
EOF
|
||||
)"
|
||||
|
||||
echo "$json" > $json_filename
|
||||
echo "${json}" | jq . > "${json_filename}"
|
||||
|
||||
# If we have a JSON URL or host/socket pair set up, post the results there as well.
|
||||
# Optionally compress into a single line.
|
||||
|
26
tests/metrics/storage/fio-dockerfile/Dockerfile
Normal file
26
tests/metrics/storage/fio-dockerfile/Dockerfile
Normal file
@ -0,0 +1,26 @@
|
||||
# Copyright (c) 2023 Intel Corporation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Set up an Ubuntu image with 'fio io tester' installed
|
||||
|
||||
FROM docker.io/library/ubuntu:22.04
|
||||
|
||||
# Version of the Dockerfile
|
||||
LABEL DOCKERFILE_VERSION="1.0"
|
||||
|
||||
# URL for the fio tester
|
||||
ENV FIO_TOOLING_URL "https://github.com/axboe/fio"
|
||||
|
||||
RUN apt-get update --quiet && \
|
||||
apt-get install --quiet --no-install-recommends -y \
|
||||
bash \
|
||||
util-linux \
|
||||
procps \
|
||||
fio && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/
|
||||
|
||||
COPY workload/fio_bench.sh /
|
||||
WORKDIR /
|
||||
CMD ["/bin/bash"]
|
129
tests/metrics/storage/fio-dockerfile/workload/fio_bench.sh
Executable file
129
tests/metrics/storage/fio-dockerfile/workload/fio_bench.sh
Executable file
@ -0,0 +1,129 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright (c) 2023 Intel Corporation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Description of the test:
|
||||
# This test runs the 'fio benchmark' on kata containers
|
||||
# https://fio.readthedocs.io/en/latest/
|
||||
|
||||
set -o pipefail
|
||||
|
||||
# FIO variable settings
|
||||
|
||||
# io-types supported:
|
||||
# read, write, randread, randwrite, randrw, readwrite
|
||||
io_type="read"
|
||||
block_size="4k"
|
||||
num_jobs="2"
|
||||
|
||||
# FIO default settings
|
||||
readonly ioengine="libaio"
|
||||
readonly rate_process="linear"
|
||||
readonly disable_buffered="1"
|
||||
readonly iodepth="2"
|
||||
readonly runtime="10s"
|
||||
# ramp time
|
||||
readonly rt="10s"
|
||||
readonly fname="test.fio"
|
||||
readonly workload_dir="/"
|
||||
readonly workload_file="${workload_dir}${fname}"
|
||||
readonly workload_size="10G"
|
||||
readonly summary_file_local="/results.json"
|
||||
|
||||
# Show help about this script
|
||||
function help() {
|
||||
cat << EOF
|
||||
Usage: $0 <count>
|
||||
Description:
|
||||
Runs FIO test using ctr to excercise IO in kata containers.
|
||||
|
||||
Params: <Operation> <io-engine>
|
||||
|
||||
Operations are:
|
||||
run-read-4k
|
||||
run-write-4k
|
||||
run-randread-4k
|
||||
run-randwrite-4k
|
||||
run-read-64k
|
||||
run-write-64k
|
||||
run-randread-64k
|
||||
run-randwrite-64k
|
||||
|
||||
<Operation>: [Mandatory]
|
||||
<io-engine> : [Optional] Any of the FIO supported ioengines, default: libaio.
|
||||
EOF
|
||||
}
|
||||
|
||||
# Run from the host
|
||||
function setup_workload() {
|
||||
# create workload file:
|
||||
if [ ! -f ${workload_file} ]; then
|
||||
pushd "${workload_dir}" > /dev/null 2>&1
|
||||
dd if=/dev/urandom of="${workload_file}" bs=64M count=160 > /dev/null 2>&1
|
||||
fi
|
||||
}
|
||||
|
||||
# Run inside container
|
||||
function launch_workload() {
|
||||
# the parameters used in the test_name are accesible globally
|
||||
local test_name="${io_type}_${block_size}_nj-${num_jobs}_${rate_process}_iodepth-${iodepth}_io-direct-${disable_buffered}"
|
||||
|
||||
setup_workload
|
||||
rm -f "${summary_file_local}" > /dev/null 2>&1
|
||||
fio \
|
||||
--name="${test_name}" \
|
||||
--output-format="json" \
|
||||
--filename="${workload_file}" \
|
||||
--size="${workload_size}" \
|
||||
--rate_process="${rate_process}" \
|
||||
--runtime="${runtime}" \
|
||||
--ioengine="${ioengine}" \
|
||||
--rw="${io_type}" \
|
||||
--direct="${disable_buffered}" \
|
||||
--numjobs="${num_jobs}" \
|
||||
--blocksize="${block_size}" \
|
||||
--ramp_time="${rt}" \
|
||||
--iodepth="${iodepth}" \
|
||||
--gtod_reduce="1" \
|
||||
--randrepeat="1" \
|
||||
| tee -a ${summary_file_local} > /dev/null 2>&1
|
||||
}
|
||||
|
||||
function print_latest_results() {
|
||||
[ ! -f "${summary_file_local}" ] && echo "Error: no results to display; you must run a test before requesting results display" && exit 1
|
||||
echo "$(cat ${summary_file_local})"
|
||||
}
|
||||
|
||||
function delete_workload() {
|
||||
rm -f "${workload_file}" > /dev/null 2>&1
|
||||
}
|
||||
|
||||
function main() {
|
||||
local action="${1:-}"
|
||||
num_jobs="${2:-1}"
|
||||
|
||||
[[ ! ${num_jobs} =~ ^[0-9]+$ ]] && die "The number of jobs must be a positive integer"
|
||||
|
||||
case "${action}" in
|
||||
run-read-4k) launch_workload ;;
|
||||
run-read-64k) block_size="64k" && launch_workload ;;
|
||||
|
||||
run-write-4k) io_type="write" && launch_workload ;;
|
||||
run-write-64k) block_size="64k" && io_type="write" && launch_workload ;;
|
||||
|
||||
run-randread-4k) io_type="randread" && launch_workload ;;
|
||||
run-randread-64k) block_size="64k" && io_type="randread" && launch_workload ;;
|
||||
|
||||
run-randwrite-4k) io_type="randwrite" && launch_workload ;;
|
||||
run-randwrite-64k) block_size="64k" && io_type="randwrite" && launch_workload ;;
|
||||
|
||||
print-latest-results) print_latest_results ;;
|
||||
delete-workload) delete_workload ;;
|
||||
|
||||
*) >&2 echo "Invalid argument" ; help ; exit 1;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
1
tests/metrics/storage/fio-k8s/.gitignore
vendored
1
tests/metrics/storage/fio-k8s/.gitignore
vendored
@ -1 +0,0 @@
|
||||
./cmd/fiotest/fio-k8s
|
@ -1,31 +0,0 @@
|
||||
#
|
||||
# Copyright (c) 2021-2022 Intel Corporation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
|
||||
MKFILE_DIR := $(dir $(MKFILE_PATH))
|
||||
|
||||
build:
|
||||
make -C $(MKFILE_DIR)/cmd/fiotest/ gomod
|
||||
make -C $(MKFILE_DIR)/cmd/fiotest/ build
|
||||
|
||||
test-report:
|
||||
$(MKFILE_DIR)/scripts/dax-compare-test/report/gen-html-fio-report.sh $(MKFILE_DIR)/cmd/fiotest/test-results/
|
||||
|
||||
test-report-interactive:
|
||||
$(MKFILE_DIR)/scripts/dax-compare-test/report/run-docker-jupyter-server.sh $(MKFILE_DIR)/cmd/fiotest/test-results/
|
||||
|
||||
test: build
|
||||
make -C $(MKFILE_DIR)/cmd/fiotest/ run
|
||||
make test-report
|
||||
|
||||
run: build
|
||||
make -C $(MKFILE_DIR)/scripts/dax-compare-test/ run
|
||||
|
||||
test-qemu: build
|
||||
make -C $(MKFILE_DIR)/cmd/fiotest/ run-qemu
|
||||
|
||||
test-clh: build
|
||||
make -C $(MKFILE_DIR)/cmd/fiotest/ run-clh
|
@ -1,30 +0,0 @@
|
||||
# FIO test in Kubernetes
|
||||
|
||||
This is an automation to run `fio` with Kubernetes.
|
||||
|
||||
## Requirements:
|
||||
|
||||
- Kubernetes cluster running.
|
||||
- Kata configured as `runtimeclass`.
|
||||
|
||||
## Test structure:
|
||||
|
||||
- [fio-test]: Program wrapper to launch `fio` in a K8s pod.
|
||||
- [pkg]: Library code that could be used for more `fio` automation.
|
||||
- [configs]: Configuration files used by [fio-test].
|
||||
- [DAX-compare-test]: Script to run [fio-test] to generate `fio` data for Kata with/without `virtio-fs DAX` and K8s bare-metal runtime(`runc`).
|
||||
- [report] Jupyter Notebook to create reports for data generated by [DAX-compare-test].
|
||||
|
||||
## Top-level Makefile targets
|
||||
|
||||
- `build`: Build `fio` metrics.
|
||||
- `test`: quick test, used to verify changes in [fio-test].
|
||||
- `run`: Run `fio` metrics and generate reports.
|
||||
- `test-report-interactive`: Run python notebook in `localhost:8888`, useful to edit the report.
|
||||
- `test-report`: Generate report from data generated by `make test`.
|
||||
|
||||
[fio-test]:cmd/fiotest
|
||||
[configs]:configs
|
||||
[pkg]:pkg
|
||||
[report]:scripts/dax-compare-test/report
|
||||
[DAX-compare-test]:scripts/dax-compare-test/README.md
|
@ -1,27 +0,0 @@
|
||||
#
|
||||
# Copyright (c) 2021-2023 Intel Corporation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
|
||||
MKFILE_DIR := $(dir $(MKFILE_PATH))
|
||||
|
||||
build:
|
||||
GO111MODULE=on go build
|
||||
|
||||
run: build
|
||||
$(MKFILE_DIR)/fio-k8s --debug --fio.size 10M --output-dir test-results --test-name kata $(MKFILE_DIR)/../../configs/example-config/
|
||||
$(MKFILE_DIR)/fio-k8s --debug --fio.size 10M --output-dir test-results --test-name runc --container-runtime runc $(MKFILE_DIR)/../../configs/example-config/
|
||||
|
||||
gomod:
|
||||
go mod edit -replace=github.com/kata-containers/kata-containers/tests/metrics/k8s=../../pkg/k8s
|
||||
go mod edit -replace=github.com/kata-containers/kata-containers/tests/metrics/exec=../../pkg/exec
|
||||
go mod edit -replace=github.com/kata-containers/kata-containers/tests/metrics/env=../../pkg/env
|
||||
go mod tidy
|
||||
|
||||
run-qemu: build
|
||||
$(MKFILE_DIR)/fio-k8s --debug --fio.size 10M --output-dir test-results --test-name kata --container-runtime kata-qemu $(MKFILE_DIR)/../../configs/example-config/
|
||||
|
||||
run-clh: build
|
||||
$(MKFILE_DIR)/fio-k8s --debug --fio.size 10M --output-dir test-results --test-name kata --container-runtime kata-clh $(MKFILE_DIR)/../../configs/example-config/
|
@ -1,24 +0,0 @@
|
||||
module github.com/kata-containers/kata-containers/tests/metrics/storage/fio-k8s
|
||||
|
||||
go 1.19
|
||||
|
||||
replace github.com/kata-containers/kata-containers/tests/metrics/exec => ../../pkg/exec
|
||||
|
||||
replace github.com/kata-containers/kata-containers/tests/metrics/k8s => ../../pkg/k8s
|
||||
|
||||
replace github.com/kata-containers/kata-containers/tests/metrics/env => ../../pkg/env
|
||||
|
||||
require (
|
||||
github.com/kata-containers/kata-containers/tests/metrics/env v0.0.0-00010101000000-000000000000
|
||||
github.com/kata-containers/kata-containers/tests/metrics/exec v0.0.0-00010101000000-000000000000
|
||||
github.com/kata-containers/kata-containers/tests/metrics/k8s v0.0.0-00010101000000-000000000000
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/urfave/cli v1.22.14
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
|
||||
)
|
@ -1,31 +0,0 @@
|
||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk=
|
||||
github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
@ -1,373 +0,0 @@
|
||||
// Copyright (c) 2021-2023 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
env "github.com/kata-containers/kata-containers/tests/metrics/env"
|
||||
exec "github.com/kata-containers/kata-containers/tests/metrics/exec"
|
||||
"github.com/kata-containers/kata-containers/tests/metrics/k8s"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var log = logrus.New()
|
||||
|
||||
var (
|
||||
optContainerRuntime = "container-runtime"
|
||||
optDebug = "debug"
|
||||
optOutputDir = "output-dir"
|
||||
optTestName = "test-name"
|
||||
// fio options
|
||||
optFioBlockSize = "fio.block-size"
|
||||
optFioDirect = "fio.direct"
|
||||
optFioIoDepth = "fio.iodepth"
|
||||
optFioSize = "fio.size"
|
||||
optFioNumJobs = "fio.numjobs"
|
||||
)
|
||||
|
||||
type RwFioOp struct {
|
||||
BandwidthKb int `json:"bw"`
|
||||
IOPS float64 `json:"iops"`
|
||||
}
|
||||
|
||||
type fioResult struct {
|
||||
GlobalOptions struct {
|
||||
IOEngine string `json:"ioengine"`
|
||||
RW string `json:"rw"`
|
||||
} `json:"global options"`
|
||||
Jobs []struct {
|
||||
JobName string `json:"jobname"`
|
||||
Read RwFioOp `json:"read"`
|
||||
Write RwFioOp `json:"write"`
|
||||
} `json:"jobs"`
|
||||
}
|
||||
|
||||
// Run fio in k8s metrics test in K8s
|
||||
func (c fioTestConfig) run() (result fioResult, err error) {
|
||||
log.Infof("Running fio config: %s", c.jobFile)
|
||||
|
||||
pod := k8s.Pod{YamlPath: c.k8sYaml}
|
||||
|
||||
log.Infof("Delete pod if already created")
|
||||
err = pod.Delete()
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
log.Infof("Create pod: %s", pod.YamlPath)
|
||||
err = pod.Run()
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
log.Info("Deleting pod")
|
||||
delErr := pod.Delete()
|
||||
if delErr != nil {
|
||||
log.Error(delErr)
|
||||
if err != nil {
|
||||
err = errors.Wrapf(err, "Could not delete pod after: %s", delErr)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
destDir := "/home/fio-jobs"
|
||||
_, err = pod.Exec("mkdir " + destDir)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
dstJobFile := path.Join(destDir, "jobFile")
|
||||
err = pod.CopyFromHost(c.jobFile, dstJobFile)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
_, err = pod.Exec("apt update")
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
_, err = pod.Exec("apt install -y fio")
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
err = env.DropCaches()
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
var directStr string
|
||||
if c.direct {
|
||||
directStr = "1"
|
||||
} else {
|
||||
directStr = "0"
|
||||
}
|
||||
|
||||
cmdFio := "fio"
|
||||
cmdFio += " --append-terse "
|
||||
cmdFio += " --blocksize=" + c.blocksize
|
||||
cmdFio += " --direct=" + directStr
|
||||
cmdFio += " --directory=" + c.directory
|
||||
cmdFio += " --iodepth=" + c.iodepth
|
||||
cmdFio += " --numjobs=" + c.numjobs
|
||||
cmdFio += " --runtime=" + c.runtime
|
||||
cmdFio += " --size=" + c.size
|
||||
cmdFio += " --output-format=json"
|
||||
cmdFio += " " + dstJobFile
|
||||
|
||||
log.Infof("Exec fio")
|
||||
output, err := pod.Exec(cmdFio, k8s.ExecOptShowStdOut())
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
err = json.Unmarshal([]byte(output), &result)
|
||||
if err != nil {
|
||||
return result, errors.Wrapf(err, "failed to unmarshall output : %s", output)
|
||||
}
|
||||
|
||||
log.Infof("ioengine:%s", result.GlobalOptions.IOEngine)
|
||||
log.Infof("rw:%s", result.GlobalOptions.RW)
|
||||
if len(result.Jobs) == 0 {
|
||||
return result, errors.New("No jobs found after parsing fio results")
|
||||
}
|
||||
|
||||
testDir := path.Join(c.outputDir, filepath.Base(c.jobFile))
|
||||
err = os.MkdirAll(testDir, 0775)
|
||||
if err != nil {
|
||||
return result, errors.Wrapf(err, "failed to create test directory for :%s", c.jobFile)
|
||||
}
|
||||
outputFile := path.Join(testDir, "output.json")
|
||||
log.Infof("Store results output in : %s", outputFile)
|
||||
|
||||
err = os.WriteFile(outputFile, []byte(output), 0644)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type fioTestConfig struct {
|
||||
//test options
|
||||
k8sYaml string
|
||||
containerRuntime string
|
||||
outputDir string
|
||||
|
||||
//fio options
|
||||
blocksize string
|
||||
directory string
|
||||
iodepth string
|
||||
numjobs string
|
||||
jobFile string
|
||||
loops string
|
||||
runtime string
|
||||
size string
|
||||
|
||||
direct bool
|
||||
}
|
||||
|
||||
func runFioJobs(testDirPath string, cfg fioTestConfig) (results []fioResult, err error) {
|
||||
fioJobsDir, err := filepath.Abs(path.Join(testDirPath, "fio-jobs"))
|
||||
if err != nil {
|
||||
return results, err
|
||||
}
|
||||
|
||||
files, err := os.ReadDir(fioJobsDir)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return results, err
|
||||
}
|
||||
|
||||
if cfg.containerRuntime == "" {
|
||||
return results, errors.New("containerRuntime is empty")
|
||||
}
|
||||
|
||||
podYAMLName := cfg.containerRuntime + ".yaml"
|
||||
cfg.k8sYaml = path.Join(testDirPath, podYAMLName)
|
||||
|
||||
if len(files) == 0 {
|
||||
return results, errors.New("No fio configs found")
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
cfg.jobFile = path.Join(fioJobsDir, file.Name())
|
||||
r, err := cfg.run()
|
||||
if err != nil {
|
||||
return results, err
|
||||
}
|
||||
results = append(results, r)
|
||||
|
||||
log.Infof("workload:%s", r.Jobs[0].JobName)
|
||||
log.Infof("bw_r:%d", r.Jobs[0].Read.BandwidthKb)
|
||||
log.Infof("IOPS_r:%f", r.Jobs[0].Read.IOPS)
|
||||
log.Infof("bw_w:%d", r.Jobs[0].Write.BandwidthKb)
|
||||
log.Infof("IOPS_w:%f", r.Jobs[0].Write.IOPS)
|
||||
|
||||
waitTime := 5
|
||||
log.Debugf("Sleep %d seconds(if not wait sometimes create another pod timesout)", waitTime)
|
||||
time.Sleep(time.Duration(waitTime) * time.Second)
|
||||
}
|
||||
return results, err
|
||||
|
||||
}
|
||||
|
||||
func generateResultsView(testName string, results []fioResult, outputDir string) error {
|
||||
outputFile := path.Join(outputDir, "results.csv")
|
||||
f, err := os.Create(outputFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
log.Infof("Creating results output in %s", outputFile)
|
||||
|
||||
w := csv.NewWriter(f)
|
||||
|
||||
headers := []string{"NAME", "WORKLOAD", "bw_r", "bw_w", "IOPS_r", "IOPS_w"}
|
||||
err = w.Write(headers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, r := range results {
|
||||
if len(r.Jobs) == 0 {
|
||||
return errors.Errorf("fio result has no jobs: %v", r)
|
||||
}
|
||||
row := []string{testName}
|
||||
row = append(row, r.Jobs[0].JobName)
|
||||
row = append(row, fmt.Sprintf("%d", r.Jobs[0].Read.BandwidthKb))
|
||||
row = append(row, fmt.Sprintf("%d", r.Jobs[0].Write.BandwidthKb))
|
||||
row = append(row, fmt.Sprintf("%f", r.Jobs[0].Read.IOPS))
|
||||
row = append(row, fmt.Sprintf("%f", r.Jobs[0].Write.IOPS))
|
||||
if err := w.Write(row); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
w.Flush()
|
||||
|
||||
return w.Error()
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
app := &cli.App{
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: optDebug,
|
||||
Usage: "Logs in debug level",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: optTestName,
|
||||
Value: "kata-fio-test",
|
||||
Usage: "Change the fio test name for reports",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: optOutputDir,
|
||||
Value: ".",
|
||||
Usage: "Use a file to store results",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: optContainerRuntime,
|
||||
Value: "kata",
|
||||
Usage: "Choose the runtime to use",
|
||||
},
|
||||
//fio options
|
||||
&cli.StringFlag{
|
||||
Name: optFioSize,
|
||||
Value: "200M",
|
||||
Usage: "File size to use for tests",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: optFioBlockSize,
|
||||
Value: "4K",
|
||||
Usage: "Block size for fio tests",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: optFioDirect,
|
||||
Usage: "Use direct io",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: optFioIoDepth,
|
||||
Value: "16",
|
||||
Usage: "Number of I/O units to keep in flight against the file",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: optFioNumJobs,
|
||||
Value: "1",
|
||||
Usage: "Number of clones (processes/threads performing the same workload) of this job",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
jobsDir := c.Args().First()
|
||||
|
||||
if jobsDir == "" {
|
||||
cli.SubcommandHelpTemplate = strings.Replace(cli.SubcommandHelpTemplate, "[arguments...]", "<test-config-dir>", -1)
|
||||
cli.ShowCommandHelp(c, "")
|
||||
return errors.New("Missing <test-config-dir>")
|
||||
}
|
||||
|
||||
if c.Bool(optDebug) {
|
||||
log.SetLevel(logrus.DebugLevel)
|
||||
k8s.Debug = true
|
||||
env.Debug = true
|
||||
}
|
||||
|
||||
exec.SetLogger(log)
|
||||
k8s.SetLogger(log)
|
||||
env.SetLogger(log)
|
||||
|
||||
testName := c.String(optTestName)
|
||||
|
||||
outputDir, err := filepath.Abs(path.Join(c.String(optOutputDir), testName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cfg := fioTestConfig{
|
||||
blocksize: c.String(optFioBlockSize),
|
||||
direct: c.Bool(optFioDirect),
|
||||
directory: ".",
|
||||
iodepth: c.String(optFioIoDepth),
|
||||
loops: "3",
|
||||
numjobs: c.String(optFioNumJobs),
|
||||
runtime: "20",
|
||||
size: c.String(optFioSize),
|
||||
containerRuntime: c.String(optContainerRuntime),
|
||||
outputDir: outputDir,
|
||||
}
|
||||
|
||||
log.Infof("Results will be created in %s", cfg.outputDir)
|
||||
|
||||
err = os.MkdirAll(cfg.outputDir, 0775)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
results, err := runFioJobs(jobsDir, cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return generateResultsView(c.String(optTestName), results, outputDir)
|
||||
},
|
||||
}
|
||||
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) 2022 Intel Corporation
|
||||
[global]
|
||||
name=io_uring
|
||||
filename=fio-file
|
||||
rw=randrw
|
||||
rwmixread=75
|
||||
ioengine=io_uring
|
||||
|
||||
[randrw-io_uring]
|
@ -1,10 +0,0 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) 2021 Intel Corporation
|
||||
[global]
|
||||
name=randrw-libaio
|
||||
filename=fio-file
|
||||
rw=randrw
|
||||
rwmixread=75
|
||||
ioengine=libaio
|
||||
|
||||
[randrw-libaio]
|
@ -1,10 +0,0 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) 2022 Intel Corporation
|
||||
[global]
|
||||
name=sync
|
||||
filename=fio-file
|
||||
rw=randrw
|
||||
rwmixread=75
|
||||
ioengine=sync
|
||||
|
||||
[randrw-sync]
|
@ -1,17 +0,0 @@
|
||||
## Copyright (c) 2021 Intel Corporation
|
||||
#
|
||||
## SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: iometrics
|
||||
spec:
|
||||
runtimeClassName: kata-clh
|
||||
containers:
|
||||
- args:
|
||||
- sleep
|
||||
- infinity
|
||||
image: ubuntu:latest
|
||||
name: iometrics
|
||||
imagePullPolicy: Always
|
@ -1,17 +0,0 @@
|
||||
## Copyright (c) 2021 Intel Corporation
|
||||
#
|
||||
## SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: iometrics
|
||||
spec:
|
||||
runtimeClassName: kata-qemu
|
||||
containers:
|
||||
- args:
|
||||
- sleep
|
||||
- infinity
|
||||
image: ubuntu:latest
|
||||
name: iometrics
|
||||
imagePullPolicy: Always
|
@ -1,16 +0,0 @@
|
||||
## Copyright (c) 2021 Intel Corporation
|
||||
#
|
||||
## SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: iometrics
|
||||
spec:
|
||||
runtimeClassName: kata
|
||||
containers:
|
||||
- name: iometrics
|
||||
image: ubuntu:latest
|
||||
# Just spin & wait forever
|
||||
command: [ "/bin/bash", "-c", "--" ]
|
||||
args: [ "sleep infinity" ]
|
@ -1,15 +0,0 @@
|
||||
## Copyright (c) 2021 Intel Corporation
|
||||
#
|
||||
## SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: iometrics
|
||||
spec:
|
||||
containers:
|
||||
- name: iometrics
|
||||
image: ubuntu:latest
|
||||
# Just spin & wait forever
|
||||
command: [ "/bin/bash", "-c", "--" ]
|
||||
args: [ "sleep infinity" ]
|
@ -1,9 +0,0 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) 2021 Intel Corporation
|
||||
[global]
|
||||
name=randread-libaio
|
||||
filename=fio-file
|
||||
rw=randread
|
||||
ioengine=libaio
|
||||
|
||||
[randread-libaio]
|
@ -1,9 +0,0 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) 2021 Intel Corporation
|
||||
[global]
|
||||
name=randread-mmap
|
||||
rw=randread
|
||||
ioengine=mmap
|
||||
|
||||
[randread-mmap]
|
||||
filename=fio-file
|
@ -1,10 +0,0 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) 2021 Intel Corporation
|
||||
[global]
|
||||
name=randrw-libaio
|
||||
filename=fio-file
|
||||
rw=randrw
|
||||
rwmixread=75
|
||||
ioengine=libaio
|
||||
|
||||
[randrw-libaio]
|
@ -1,10 +0,0 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) 2021 Intel Corporation
|
||||
[global]
|
||||
name=randrw-mmap
|
||||
rw=randrw
|
||||
rwmixread=75
|
||||
ioengine=mmap
|
||||
|
||||
[randrw-mmap]
|
||||
filename=fio-file
|
@ -1,9 +0,0 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) 2021 Intel Corporation
|
||||
[global]
|
||||
name=randwrite-libaio
|
||||
filename=fio-file
|
||||
rw=randwrite
|
||||
ioengine=libaio
|
||||
|
||||
[randwrite-libaio]
|
@ -1,9 +0,0 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) 2021 Intel Corporation
|
||||
[global]
|
||||
name=randwrite-mmap
|
||||
rw=randwrite
|
||||
ioengine=mmap
|
||||
|
||||
[randwrite-mmap]
|
||||
filename=fio-file
|
@ -1,9 +0,0 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) 2021 Intel Corporation
|
||||
[global]
|
||||
name=seqread-libaio
|
||||
filename=fio-file
|
||||
rw=read
|
||||
ioengine=libaio
|
||||
|
||||
[seqread-libaio]
|
@ -1,9 +0,0 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) 2021 Intel Corporation
|
||||
[global]
|
||||
name=seqread-mmap
|
||||
rw=read
|
||||
ioengine=mmap
|
||||
|
||||
[seqread-mmap]
|
||||
filename=fio-file
|
@ -1,8 +0,0 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) 2021 Intel Corporation
|
||||
[global]
|
||||
name=seqread-psync
|
||||
filename=fio-file
|
||||
rw=read
|
||||
|
||||
[seqread-psync]
|
@ -1,9 +0,0 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) 2021 Intel Corporation
|
||||
[global]
|
||||
name=seqwrite-libaio
|
||||
filename=fio-file
|
||||
rw=write
|
||||
ioengine=libaio
|
||||
|
||||
[seqwrite-libaio]
|
@ -1,10 +0,0 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) 2021 Intel Corporation
|
||||
[global]
|
||||
name=seqwrite-mmap
|
||||
filename=fio-file
|
||||
rw=write
|
||||
ioengine=mmap
|
||||
|
||||
[seqwrite-mmap]
|
||||
filename=fio-file
|
@ -1,16 +0,0 @@
|
||||
## Copyright (c) 2021 Intel Corporation
|
||||
#
|
||||
## SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: iometrics
|
||||
spec:
|
||||
runtimeClassName: kata
|
||||
containers:
|
||||
- name: iometrics
|
||||
image: ubuntu:latest
|
||||
# Just spin & wait forever
|
||||
command: [ "/bin/bash", "-c", "--" ]
|
||||
args: [ "sleep infinity" ]
|
@ -1,15 +0,0 @@
|
||||
## Copyright (c) 2021 Intel Corporation
|
||||
#
|
||||
## SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: iometrics
|
||||
spec:
|
||||
containers:
|
||||
- name: iometrics
|
||||
image: ubuntu:latest
|
||||
# Just spin & wait forever
|
||||
command: [ "/bin/bash", "-c", "--" ]
|
||||
args: [ "sleep infinity" ]
|
@ -1,86 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright (c) 2022-2023 Intel Corporation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
set -e
|
||||
|
||||
# General env
|
||||
SCRIPT_PATH=$(dirname "$(readlink -f "$0")")
|
||||
source "${SCRIPT_PATH}/../../lib/common.bash"
|
||||
FIO_PATH="${GOPATH}/tests/metrics/storage/fio-k8s"
|
||||
TEST_NAME="${TEST_NAME:-fio}"
|
||||
|
||||
function main() {
|
||||
cmds=("bc" "jq")
|
||||
check_cmds "${cmds[@]}"
|
||||
init_env
|
||||
check_processes
|
||||
|
||||
pushd "${FIO_PATH}"
|
||||
[ -z "${KATA_HYPERVISOR}" ] && die "Hypervisor ID is missing."
|
||||
[ "${KATA_HYPERVISOR}" != "qemu" ] && [ "${KATA_HYPERVISOR}" != "clh" ] && die "Hypervisor not recognized: ${KATA_HYPERVISOR}"
|
||||
|
||||
echo "INFO: Running K8S FIO test using ${KATA_HYPERVISOR} hypervisor"
|
||||
make "test-${KATA_HYPERVISOR}"
|
||||
popd
|
||||
|
||||
test_result_file="${FIO_PATH}/cmd/fiotest/test-results/kata/randrw-sync.job/output.json"
|
||||
|
||||
metrics_json_init
|
||||
local read_io=$(cat $test_result_file | grep io_bytes | head -1 | sed 's/[[:blank:]]//g' | cut -f2 -d ':' | cut -f1 -d ',')
|
||||
local read_bw=$(cat $test_result_file | grep bw_bytes | head -1 | sed 's/[[:blank:]]//g' | cut -f2 -d ':' | cut -f1 -d ',')
|
||||
local read_90_percentile=$(cat $test_result_file | grep 90.000000 | head -1 | sed 's/[[:blank:]]//g' | cut -f2 -d ':' | cut -f1 -d ',')
|
||||
local read_95_percentile=$(cat $test_result_file | grep 95.000000 | head -1 | sed 's/[[:blank:]]//g' | cut -f2 -d ':' | cut -f1 -d ',')
|
||||
local write_io=$(cat $test_result_file | grep io_bytes | head -2 | tail -1 | sed 's/[[:blank:]]//g' | cut -f2 -d ':' | cut -f1 -d ',')
|
||||
local write_bw=$(cat $test_result_file | grep bw_bytes | head -2 | tail -1 | sed 's/[[:blank:]]//g' | cut -f2 -d ':' | cut -f1 -d ',')
|
||||
local write_90_percentile=$(cat $test_result_file | grep 90.000000 | head -2 | tail -1 | sed 's/[[:blank:]]//g' | cut -f2 -d ':' | cut -f1 -d ',')
|
||||
local write_95_percentile=$(cat $test_result_file | grep 95.000000 | head -2 | tail -1 | sed 's/[[:blank:]]//g' | cut -f2 -d ':' | cut -f1 -d ',')
|
||||
|
||||
metrics_json_start_array
|
||||
local json="$(cat << EOF
|
||||
{
|
||||
"readio": {
|
||||
"Result" : $read_io,
|
||||
"Units" : "bytes"
|
||||
},
|
||||
"readbw": {
|
||||
"Result" : $read_bw,
|
||||
"Units" : "bytes/sec"
|
||||
},
|
||||
"read90percentile": {
|
||||
"Result" : $read_90_percentile,
|
||||
"Units" : "ns"
|
||||
},
|
||||
"read95percentile": {
|
||||
"Result" : $read_95_percentile,
|
||||
"Units" : "ns"
|
||||
},
|
||||
"writeio": {
|
||||
"Result" : $write_io,
|
||||
"Units" : "bytes"
|
||||
},
|
||||
"writebw": {
|
||||
"Result" : $write_bw,
|
||||
"Units" : "bytes/sec"
|
||||
},
|
||||
"write90percentile": {
|
||||
"Result" : $write_90_percentile,
|
||||
"Units" : "ns"
|
||||
},
|
||||
"write95percentile": {
|
||||
"Result" : $write_95_percentile,
|
||||
"Units" : "ns"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
)"
|
||||
metrics_json_add_array_element "$json"
|
||||
metrics_json_end_array "Results"
|
||||
metrics_json_save
|
||||
|
||||
check_processes
|
||||
}
|
||||
|
||||
main "$@"
|
@ -1,9 +0,0 @@
|
||||
#
|
||||
# Copyright (c) 2021-2023 Intel Corporation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
gomod:
|
||||
GO111MODULE=on go mod edit -replace=github.com/kata-containers/kata-containers/tests/metrics/exec=../exec
|
||||
GO111MODULE=on go mod tidy
|
38
tests/metrics/storage/fio-k8s/pkg/env/env.go
vendored
38
tests/metrics/storage/fio-k8s/pkg/env/env.go
vendored
@ -1,38 +0,0 @@
|
||||
// Copyright (c) 2021-2023 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package env
|
||||
|
||||
import (
|
||||
exec "github.com/kata-containers/kata-containers/tests/metrics/exec"
|
||||
)
|
||||
|
||||
// logger interface for pkg
|
||||
var log logger
|
||||
var Debug bool = false
|
||||
|
||||
type logger interface {
|
||||
Infof(string, ...interface{})
|
||||
Debugf(string, ...interface{})
|
||||
Errorf(string, ...interface{})
|
||||
}
|
||||
|
||||
func SetLogger(l logger) {
|
||||
log = l
|
||||
}
|
||||
|
||||
var sysDropCachesPath = "/proc/sys/vm/drop_caches"
|
||||
|
||||
func DropCaches() (err error) {
|
||||
log.Infof("drop caches")
|
||||
_, err = exec.ExecCmd("sync", Debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = exec.ExecCmd("echo 3 | sudo tee "+sysDropCachesPath, Debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
10
tests/metrics/storage/fio-k8s/pkg/env/go.mod
vendored
10
tests/metrics/storage/fio-k8s/pkg/env/go.mod
vendored
@ -1,10 +0,0 @@
|
||||
module github.com/kata-containers/kata-containers/tests/metrics/storage/fio-k8s/exec
|
||||
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/kata-containers/kata-containers/tests/metrics/exec v0.0.0-00010101000000-000000000000 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
)
|
||||
|
||||
replace github.com/kata-containers/kata-containers/tests/metrics/exec => ../exec
|
2
tests/metrics/storage/fio-k8s/pkg/env/go.sum
vendored
2
tests/metrics/storage/fio-k8s/pkg/env/go.sum
vendored
@ -1,2 +0,0 @@
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
@ -1,67 +0,0 @@
|
||||
// Copyright (c) 2021-2023 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package exec
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// logger interface for pkg
|
||||
var log logger
|
||||
|
||||
type logger interface {
|
||||
Infof(string, ...interface{})
|
||||
Debugf(string, ...interface{})
|
||||
Errorf(string, ...interface{})
|
||||
}
|
||||
|
||||
func SetLogger(l logger) {
|
||||
log = l
|
||||
}
|
||||
|
||||
// Exec a command
|
||||
// err != nil if command fails to execute
|
||||
// output is a string with a combined stdout and stderr
|
||||
func ExecCmd(c string, showInStdout bool) (stdout string, err error) {
|
||||
if c == "" {
|
||||
return "", errors.New("command is empty")
|
||||
}
|
||||
|
||||
log.Debugf("Exec: %s", c)
|
||||
cmd := exec.Command("bash", "-o", "pipefail", "-c", c)
|
||||
var stdBuffer bytes.Buffer
|
||||
var writers []io.Writer
|
||||
writers = append(writers, &stdBuffer)
|
||||
if showInStdout {
|
||||
writers = append(writers, os.Stdout)
|
||||
}
|
||||
mw := io.MultiWriter(writers...)
|
||||
|
||||
cmd.Stdout = mw
|
||||
cmd.Stderr = mw
|
||||
|
||||
err = cmd.Run()
|
||||
output := stdBuffer.String()
|
||||
|
||||
return stdBuffer.String(), errors.Wrap(err, output)
|
||||
}
|
||||
|
||||
// Exec a command
|
||||
// Send output to Stdout and Stderr
|
||||
func ExecStdout(c string) error {
|
||||
if c == "" {
|
||||
return errors.New("command is empty")
|
||||
}
|
||||
|
||||
log.Debugf("Exec: %s", c)
|
||||
cmd := exec.Command("bash", "-o", "pipefail", "-c", c)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd.Run()
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
module github.com/kata-containers/kata-containers/tests/metrics/storage/fio-k8s/exec
|
||||
|
||||
go 1.19
|
||||
|
||||
require github.com/pkg/errors v0.9.1
|
@ -1,2 +0,0 @@
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
@ -1,8 +0,0 @@
|
||||
#
|
||||
# Copyright (c) 2021-2023 Intel Corporation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
gomod:
|
||||
GO111MODULE=on go mod edit -replace=github.com/kata-containers/kata-containers/tests/metrics/exec=../exec
|
||||
GO111MODULE=on go mod tidy
|
@ -1,34 +0,0 @@
|
||||
// Copyright (c) 2021-2023 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package k8s
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
exec "github.com/kata-containers/kata-containers/tests/metrics/exec"
|
||||
)
|
||||
|
||||
type execOpt struct {
|
||||
showInStdOut bool
|
||||
}
|
||||
|
||||
type ExecOption func(e *execOpt)
|
||||
|
||||
func ExecOptShowStdOut() ExecOption {
|
||||
return func(e *execOpt) {
|
||||
e.showInStdOut = true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (p *Pod) Exec(cmd string, opts ...ExecOption) (output string, err error) {
|
||||
log.Debugf("Exec %q in %s", cmd, p.YamlPath)
|
||||
o := &execOpt{showInStdOut: false}
|
||||
for _, opt := range opts {
|
||||
opt(o)
|
||||
|
||||
}
|
||||
execCmd := fmt.Sprintf("kubectl exec -f %s -- /bin/bash -c %q", p.YamlPath, cmd)
|
||||
return exec.ExecCmd(execCmd, Debug || o.showInStdOut)
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
module github.com/kata-containers/kata-containers/tests/metrics/k8s
|
||||
|
||||
go 1.19
|
||||
|
||||
replace github.com/kata-containers/kata-containers/tests/metrics/exec => ../exec
|
||||
|
||||
require (
|
||||
github.com/kata-containers/kata-containers/tests/metrics/exec v0.0.0-00010101000000-000000000000 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
)
|
@ -1,2 +0,0 @@
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
@ -1,68 +0,0 @@
|
||||
// Copyright (c) 2021-2023 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package k8s
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
exec "github.com/kata-containers/kata-containers/tests/metrics/exec"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// logger interface for pkg
|
||||
var log logger
|
||||
var Debug bool = false
|
||||
|
||||
type logger interface {
|
||||
Infof(string, ...interface{})
|
||||
Debugf(string, ...interface{})
|
||||
Errorf(string, ...interface{})
|
||||
}
|
||||
|
||||
func SetLogger(l logger) {
|
||||
log = l
|
||||
}
|
||||
|
||||
type Pod struct {
|
||||
YamlPath string
|
||||
}
|
||||
|
||||
func (p *Pod) waitForReady() (err error) {
|
||||
log.Debugf("Wait for pod %s", p.YamlPath)
|
||||
_, err = exec.ExecCmd("kubectl wait --for=condition=ready -f "+p.YamlPath, Debug)
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *Pod) Run() (err error) {
|
||||
|
||||
log.Debugf("Creating K8s Pod %s", p.YamlPath)
|
||||
_, err = exec.ExecCmd("kubectl apply -f "+p.YamlPath, Debug)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "Failed to run pod %s", p.YamlPath)
|
||||
}
|
||||
|
||||
err = p.waitForReady()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "Failed to wait for pod %s", p.YamlPath)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *Pod) Delete() (err error) {
|
||||
log.Debugf("Delete pod %s", p.YamlPath)
|
||||
_, err = exec.ExecCmd("kubectl delete --ignore-not-found -f "+p.YamlPath, Debug)
|
||||
return errors.Wrapf(err, "Failed to delete pod %s", p.YamlPath)
|
||||
}
|
||||
|
||||
func (p *Pod) CopyFromHost(src, dst string) (err error) {
|
||||
podName, err := exec.ExecCmd("kubectl get -f "+p.YamlPath+" -o jsonpath={.metadata.name}", Debug)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Debugf("Copy from host %q->%q in pod %s", src, dst, p.YamlPath)
|
||||
execCmd := fmt.Sprintf("kubectl cp %s %s:%s", src, podName, dst)
|
||||
_, err = exec.ExecCmd(execCmd, Debug)
|
||||
return err
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
#
|
||||
# Copyright (c) 2021-2023 Intel Corporation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
|
||||
MKFILE_DIR := $(dir $(MKFILE_PATH))
|
||||
run:
|
||||
$(MKFILE_DIR)/compare-virtiofsd-dax.sh
|
||||
"$(MKFILE_DIR)/report/gen-html-fio-report.sh" "./results"
|
@ -1,47 +0,0 @@
|
||||
# FIO in Kubernetes
|
||||
|
||||
This test runs `fio` jobs to measure how Kata Containers work using virtio-fs DAX. The test works using Kubernetes.
|
||||
The test has to run in a single node cluster, it is needed as the test modifies Kata configuration file.
|
||||
|
||||
The `virtio-fs` options that this test will use are:
|
||||
|
||||
* `cache mode` Only `auto`, this is the most compatible mode for most of the Kata use cases. Today this is default in Kata.
|
||||
* `thread pool size` Restrict the number of worker threads per request queue, zero means no thread pool.
|
||||
* `DAX`
|
||||
```
|
||||
File contents can be mapped into a memory window on the host, allowing the guest to directly access data from the host page cache. This has several advantages: The guest page cache is bypassed, reducing the memory footprint. No communication is necessary
|
||||
to access file contents, improving I/O performance. Shared file access is coherent between virtual machines on the same host even with mmap.
|
||||
```
|
||||
|
||||
This test by default iterates over different `virtio-fs` configurations.
|
||||
|
||||
| test name | DAX | thread pool size | cache mode |
|
||||
|---------------------------|-----|------------------|------------|
|
||||
| pool_0_cache_auto_no_DAX | no | 0 | auto |
|
||||
| pool_0_cache_auto_DAX | yes | 0 | auto |
|
||||
|
||||
The `fio` options used are:
|
||||
|
||||
`ioengine`: How the IO requests are issued to the kernel.
|
||||
* `libaio`: Supports async IO for both direct and buffered IO.
|
||||
* `mmap`: File is memory mapped with mmap(2) and data copied to/from using memcpy(3).
|
||||
|
||||
`rw type`: Type of I/O pattern.
|
||||
* `randread`: Random reads.
|
||||
* `randrw`: Random mixed reads and writes.
|
||||
* `randwrite`: Random writes.
|
||||
* `read`: Sequential reads.
|
||||
* `write`: Sequential writes.
|
||||
|
||||
Additional notes: Some jobs contain a `multi` prefix. This means that the same job runs more than once at the same time using its own file.
|
||||
|
||||
### Static `fio` values:
|
||||
|
||||
Some `fio` values are not modified over all the jobs.
|
||||
|
||||
* `runtime`: Tell `fio` to terminate processing after the specified period of time(seconds).
|
||||
* `loops`: Run the specified number of iterations of this job. Used to repeat the same workload a given number of times.
|
||||
* `iodepth`: Number of I/O units to keep in flight against the file. Note that increasing `iodepth` beyond 1 will not affect synchronous `ioengine`.
|
||||
* `size`: The total size of file I/O for each thread of this job.
|
||||
* `direct`: If value is true, use non-buffered I/O. This is usually O_`DIRECT`.
|
||||
* `blocksize`: The block size in bytes used for I/O units.
|
@ -1,151 +0,0 @@
|
||||
#!/bin/bash
|
||||
#Copyright (c) 2021-2023 Intel Corporation
|
||||
#
|
||||
#SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
set -o errtrace
|
||||
|
||||
script_dir=$(dirname "$(readlink -f "$0")")
|
||||
|
||||
runtime_path="/opt/kata/bin/kata-runtime"
|
||||
kata_config_path="/opt/kata/share/defaults/kata-containers/configuration.toml"
|
||||
|
||||
results_dir="$(realpath ./)/results"
|
||||
|
||||
KATA_RUNTIME="${KATA_RUNTIME_CLASS:-kata}"
|
||||
BAREMETAL_RUNTIME="runc"
|
||||
RUNTIME_CLASS=""
|
||||
|
||||
FIO_SIZE="${FIO_SIZE:-500M}"
|
||||
FIO_BLOCKSIZE="${FIO_BLOCKSIZE:-4K}"
|
||||
VIRTIOFS_DAX_SIZE=${VIRTIOFS_DAX_SIZE:-600M}
|
||||
|
||||
# set the base case for virtiofsd
|
||||
set_base_virtiofs_config() {
|
||||
# Running kata-qemu-virtiofs
|
||||
# Defaults for virtiofs
|
||||
sudo crudini --set --existing "$kata_config_path" hypervisor.qemu virtio_fs_cache '"auto"'
|
||||
sudo crudini --set --existing "$kata_config_path" hypervisor.qemu virtio_fs_cache_size ${VIRTIOFS_DAX_SIZE}
|
||||
}
|
||||
|
||||
## helper function: get name of current bash function
|
||||
fn_name() {
|
||||
echo "${FUNCNAME[1]}"
|
||||
}
|
||||
|
||||
# directory where results are stored
|
||||
get_results_dir() {
|
||||
local test_name
|
||||
local test_result_dir
|
||||
test_name="${1}"
|
||||
test_result_dir="${results_dir}/${test_name}"
|
||||
mkdir -p "${test_result_dir}"
|
||||
echo "${test_result_dir}"
|
||||
}
|
||||
|
||||
# Collect kata env
|
||||
# save kata config toml
|
||||
# save output from kata-env
|
||||
kata_env() {
|
||||
local suffix=${1}
|
||||
local config_path
|
||||
local kata_env_bk
|
||||
local kata_config_bk
|
||||
kata_env_bk="$(get_results_dir "${suffix}")/kata-env.toml"
|
||||
kata_config_bk="$(get_results_dir "${suffix}")/kata-config.toml"
|
||||
|
||||
${runtime_path} kata-env >"${kata_env_bk}"
|
||||
config_path="$(${runtime_path} kata-env --json | jq .Runtime.Config.Path -r)"
|
||||
cp "${config_path}" "${kata_config_bk}"
|
||||
}
|
||||
|
||||
# Collect the command used by virtiofsd
|
||||
collect_qemu_virtiofs_cmd() {
|
||||
local rdir
|
||||
local test_name
|
||||
test_name="${1}"
|
||||
|
||||
rdir=$(get_results_dir "${test_name}")
|
||||
# TODO
|
||||
}
|
||||
|
||||
# Run metrics runner
|
||||
run_workload() {
|
||||
local test_name
|
||||
local test_result_file
|
||||
local test_result_dir
|
||||
|
||||
test_name="${1}"
|
||||
|
||||
test_result_dir="$(get_results_dir "${test_name}")"
|
||||
test_result_file="${test_result_dir}/test-out.txt"
|
||||
|
||||
echo "Running for kata config: ${test_name}"
|
||||
collect_qemu_virtiofs_cmd "$test_name"
|
||||
|
||||
fio_runner_dir="${script_dir}/../../cmd/fiotest/"
|
||||
fio_jobs="${script_dir}/../../configs/test-config/"
|
||||
make -C "${fio_runner_dir}" build
|
||||
pwd
|
||||
set -x
|
||||
"${fio_runner_dir}fio-k8s" \
|
||||
--debug \
|
||||
--fio.size "${FIO_SIZE}" \
|
||||
--fio.block-size "${FIO_BLOCKSIZE}" \
|
||||
--container-runtime "${RUNTIME_CLASS}" \
|
||||
--test-name "${test_name}" \
|
||||
--output-dir "$(dirname ${test_result_dir})" \
|
||||
"${fio_jobs}" |
|
||||
tee \
|
||||
"${test_result_file}"
|
||||
set +x
|
||||
}
|
||||
|
||||
pool_0_cache_auto_dax() {
|
||||
local suffix="$(fn_name)"
|
||||
|
||||
set_base_virtiofs_config
|
||||
sudo crudini --set --existing "$kata_config_path" hypervisor.qemu virtio_fs_extra_args '["--thread-pool-size=0","-o","no_posix_lock","-o","xattr"]'
|
||||
sudo crudini --set --existing "$kata_config_path" hypervisor.qemu virtio_fs_cache '"auto"'
|
||||
sudo crudini --set --existing "$kata_config_path" hypervisor.qemu virtio_fs_cache_size 1024
|
||||
kata_env "${suffix}"
|
||||
RUNTIME_CLASS="${KATA_RUNTIME}"
|
||||
run_workload "${suffix}"
|
||||
}
|
||||
|
||||
pool_0_cache_auto_no_dax() {
|
||||
local suffix="$(fn_name)"
|
||||
|
||||
set_base_virtiofs_config
|
||||
sudo crudini --set --existing "$kata_config_path" hypervisor.qemu virtio_fs_extra_args '["--thread-pool-size=0","-o","no_posix_lock","-o","xattr"]'
|
||||
sudo crudini --set --existing "$kata_config_path" hypervisor.qemu virtio_fs_cache '"auto"'
|
||||
sudo crudini --set --existing "$kata_config_path" hypervisor.qemu virtio_fs_cache_size 0
|
||||
|
||||
kata_env "${suffix}"
|
||||
|
||||
RUNTIME_CLASS="${KATA_RUNTIME}"
|
||||
run_workload "${suffix}"
|
||||
echo "done"
|
||||
}
|
||||
|
||||
k8s_baremetal() {
|
||||
local suffix="$(fn_name)"
|
||||
|
||||
RUNTIME_CLASS="${BAREMETAL_RUNTIME}"
|
||||
run_workload "${suffix}"
|
||||
}
|
||||
|
||||
main() {
|
||||
|
||||
mkdir -p "${results_dir}"
|
||||
|
||||
k8s_baremetal
|
||||
pool_0_cache_auto_dax
|
||||
pool_0_cache_auto_no_dax
|
||||
}
|
||||
|
||||
main $*
|
@ -1,51 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "tWacOPbMYPtc"
|
||||
},
|
||||
"source": [
|
||||
"# FIO comparision"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "jXtTs6yldl_y"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import fio\n",
|
||||
"fio.generate_report()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"collapsed_sections": [],
|
||||
"name": "fio.ipynb",
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.8.6"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
@ -1,102 +0,0 @@
|
||||
# Copyright (c) 2021-2023 Intel Corporation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
import pandas as pd
|
||||
import os
|
||||
import re
|
||||
import io
|
||||
import glob
|
||||
from IPython.display import display, Markdown
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
#Compare the tests results group by fio job.
|
||||
#Input:
|
||||
# df: dataset from `import_data()`
|
||||
# metric: string of metrics provided in `df`
|
||||
def compare_tests_group_by_fio_job(df, metric):
|
||||
test_names, metric_df = group_metrics_group_by_testname(df, metric)
|
||||
show_df(metric_df)
|
||||
plot_df(metric_df,test_names)
|
||||
|
||||
# Given a metric return results per test group by fio job.
|
||||
# input:
|
||||
# df: dataset from `import_data()`
|
||||
# metric: string with the name of the metric to filter.
|
||||
# output:
|
||||
# dataset with fomat:
|
||||
# 'workload' , 'name[0]' , ... , 'name[n]'
|
||||
#
|
||||
def group_metrics_group_by_testname(df, metric):
|
||||
#name of each tests from results
|
||||
names = set()
|
||||
# Rows of new data set
|
||||
rows = []
|
||||
# map:
|
||||
# keys: name of fio job
|
||||
# value: dict[k]:v where k: name of a test, v: value of test for metric`
|
||||
workload = {}
|
||||
|
||||
for k, row in df.iterrows():
|
||||
# name of a fio job
|
||||
w = row['WORKLOAD']
|
||||
# name of tests
|
||||
tname = row['NAME']
|
||||
names.add(tname)
|
||||
# given a fio job name get dict of values
|
||||
# if not previous values init empty dict
|
||||
dict_values = workload.get(w, {})
|
||||
# For a given metric, add it into as value of dict_values[testname]=val
|
||||
#e.g
|
||||
# dict_values["test-name"] = row["IOPS"]
|
||||
dict_values[tname] = row[metric]
|
||||
workload[w] = dict_values
|
||||
|
||||
names = list(names)
|
||||
cols = ['WORKLOAD'] + list(names)
|
||||
rdf = pd.DataFrame(workload,columns = cols)
|
||||
|
||||
for k in workload:
|
||||
d = workload[k]
|
||||
|
||||
if not d[names[0]] == 0:
|
||||
d["WORKLOAD"] = k;
|
||||
rdf = rdf.append(d,ignore_index=True)
|
||||
rdf = rdf.dropna()
|
||||
return names, rdf
|
||||
|
||||
def plot_df(df, names,sort_key=""):
|
||||
if sort_key != "":
|
||||
df.sort_values(sort_key, ascending=False)
|
||||
df.plot(kind='bar',x="WORKLOAD",y=names, figsize=(30, 10))
|
||||
plt.show()
|
||||
|
||||
|
||||
def import_data():
|
||||
frames = []
|
||||
for f in glob.glob('./results/*/results.csv'):
|
||||
print("reading:" + f)
|
||||
df = pd.read_csv(f)
|
||||
frames.append(df)
|
||||
return pd.concat(frames)
|
||||
|
||||
def show_df(df):
|
||||
pd.set_option('display.max_rows', df.shape[0]+1)
|
||||
print(df)
|
||||
|
||||
def print_md(s):
|
||||
display(Markdown(s))
|
||||
|
||||
#notebook entrypoint
|
||||
def generate_report():
|
||||
#Load the all test results in a single dataset
|
||||
df_results = import_data()
|
||||
print_md("Show all data from results")
|
||||
show_df(df_results)
|
||||
print_md("### Compare the tests results group by fio job. The metric used to compare is write bandwidth")
|
||||
compare_tests_group_by_fio_job(df_results, 'bw_w')
|
||||
print_md("### Compare the tests results group by fio job. The metric used to compare is read bandwidth")
|
||||
compare_tests_group_by_fio_job(df_results, 'bw_r')
|
||||
print_md("### Compare the tests results group by fio job. The metric used to compare is write IOPS(Input/Output Operations Per Second)")
|
||||
compare_tests_group_by_fio_job(df_results, 'IOPS_w')
|
||||
print_md("### Compare the tests results group by fio job. The metric used to compare is read IOPS(Input/Output Operations Per Second)")
|
||||
compare_tests_group_by_fio_job(df_results, 'IOPS_r')
|
@ -1,48 +0,0 @@
|
||||
#!/bin/bash
|
||||
#Copyright (c) 2021-2023 Intel Corporation
|
||||
#
|
||||
#SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
set -o errtrace
|
||||
|
||||
script_dir=$(dirname "$(readlink -f "$0")")
|
||||
|
||||
results_dir=${1:-}
|
||||
|
||||
usage(){
|
||||
echo "$0 <results_dir>"
|
||||
}
|
||||
|
||||
if [ "${results_dir}" == "" ];then
|
||||
echo "missing results directory"
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "${results_dir}" ];then
|
||||
echo "${results_dir} is not a directory"
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
results_dir=$(realpath "${results_dir}")
|
||||
|
||||
generate_report(){
|
||||
sudo chown "${USER}:${USER}" -R ${results_dir}
|
||||
sudo docker run --rm -e JUPYTER_ENABLE_LAB=yes \
|
||||
-v "${script_dir}:/home/jovyan" \
|
||||
-v "${results_dir}:/home/jovyan/results" \
|
||||
--user $(id -u):$(id -g) \
|
||||
jupyter/scipy-notebook:399cbb986c6b \
|
||||
bash -e -c '
|
||||
cd results;
|
||||
jupyter nbconvert --execute /home/jovyan/fio.ipynb --to html;
|
||||
cp /home/jovyan/fio.html /home/jovyan/results;
|
||||
'
|
||||
}
|
||||
|
||||
generate_report
|
@ -1,39 +0,0 @@
|
||||
#!/bin/bash
|
||||
#Copyright (c) 2021-2023 Intel Corporation
|
||||
#
|
||||
#SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
set -o errtrace
|
||||
|
||||
script_dir=$(dirname "$(readlink -f "$0")")
|
||||
NOTEBOOK_PORT="8888"
|
||||
|
||||
results_dir=${1:-}
|
||||
|
||||
usage(){
|
||||
echo "$0 <results_dir>"
|
||||
}
|
||||
|
||||
if [ "${results_dir}" == "" ];then
|
||||
echo "missing results directory"
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "${results_dir}" ];then
|
||||
echo "${results_dir} is not a directory"
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
results_dir=$(realpath "${results_dir}")
|
||||
|
||||
sudo -E docker run --rm -p "${NOTEBOOK_PORT}:${NOTEBOOK_PORT}" -e JUPYTER_ENABLE_LAB=yes \
|
||||
-v "${script_dir}:/home/jovyan" \
|
||||
-v "${results_dir}:/home/jovyan/results" \
|
||||
jupyter/scipy-notebook:399cbb986c6b \
|
||||
start.sh jupyter lab --LabApp.token=''
|
161
tests/metrics/storage/fio_test.sh
Executable file
161
tests/metrics/storage/fio_test.sh
Executable file
@ -0,0 +1,161 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright (c) 2023 Intel Corporation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Description of the test:
|
||||
# This test runs the 'fio benchmark' on kata containers
|
||||
# https://fio.readthedocs.io/en/latest/
|
||||
|
||||
set -o pipefail
|
||||
|
||||
SCRIPT_PATH=$(dirname "$(readlink -f "$0")")
|
||||
source "${SCRIPT_PATH}/../lib/common.bash"
|
||||
|
||||
CONTAINER_ID="fio_bench_${RANDOM}"
|
||||
IMAGE="docker.io/library/fio-bench:latest"
|
||||
DOCKERFILE="${SCRIPT_PATH}/fio-dockerfile/Dockerfile"
|
||||
PAYLOAD_ARGS="${PAYLOAD_ARGS:-tail -f /dev/null}"
|
||||
TEST_NAME="fio"
|
||||
|
||||
# Fio default number of jobs
|
||||
nj=4
|
||||
|
||||
function release_resources() {
|
||||
sudo -E "${CTR_EXE}" t exec --exec-id "$(random_name)" ${CONTAINER_ID} sh -c "./fio_bench.sh delete-workload"
|
||||
sleep 0.5
|
||||
clean_env_ctr
|
||||
info "fio test end"
|
||||
}
|
||||
|
||||
trap release_resources EXIT
|
||||
|
||||
function setup() {
|
||||
info "setup fio test"
|
||||
clean_env_ctr
|
||||
check_cmds "${cmds[@]}"
|
||||
check_ctr_images "$IMAGE" "$DOCKERFILE"
|
||||
init_env
|
||||
|
||||
# drop caches
|
||||
sudo sh -c 'echo 3 > /proc/sys/vm/drop_caches'
|
||||
|
||||
# launch container
|
||||
sudo -E "${CTR_EXE}" run -d --runtime "${CTR_RUNTIME}" "${IMAGE}" "${CONTAINER_ID}" sh -c "${PAYLOAD_ARGS}"
|
||||
}
|
||||
|
||||
function parse_results() {
|
||||
local data="${1}"
|
||||
local bw=0
|
||||
local bw_stddev=0
|
||||
local iops=0
|
||||
local iops_stddev=0
|
||||
|
||||
[ -z "${data}" ] && die "Data results are missing when trying to parsing them."
|
||||
|
||||
local io_type="$(echo "${data}" | jq -r '.jobs[0]."job options".rw')"
|
||||
|
||||
if [ "${io_type}" = "read" ] || [ "${io_type}" = "randread" ]; then
|
||||
# Bandwidth
|
||||
bw="$(echo "${data}" | num_jobs="$nj" jq '[.jobs[] | .read.bw] | add/(env.num_jobs|tonumber) | .*1000|round/1000')"
|
||||
bw_stddev="$(echo "${data}" | num_jobs="$nj" jq '[.jobs[] | .read.bw_dev] | add/(env.num_jobs|tonumber) | .*1000|round/1000')"
|
||||
# IOPS
|
||||
iops="$(echo "${data}" | num_jobs="$nj" jq '[.jobs[] | .read.iops] | add/(env.num_jobs|tonumber) | .*1000|round/1000')"
|
||||
iops_stddev="$(echo "${data}" | num_jobs="$nj" jq '[.jobs[] | .read.iops_stddev] | add/(env.num_jobs|tonumber) | .*1000|round/1000')"
|
||||
elif [ "${io_type}" = "write" ] || [ "${io_type}" = "randwrite" ]; then
|
||||
# Bandwidth
|
||||
bw="$(echo "${data}" | num_jobs="$nj" jq '[.jobs[] | .write.bw] | add/(env.num_jobs|tonumber) | .*1000|round/1000')"
|
||||
bw_stddev="$(echo "${data}" | num_jobs="$nj" jq '[.jobs[] | .write.bw_dev] | add/(env.num_jobs|tonumber) | .*1000|round/1000')"
|
||||
# IOPS
|
||||
iops="$(echo "${data}" | num_jobs="$nj" jq '[.jobs[] | .write.iops] | add/(env.num_jobs|tonumber) | .*1000|round/1000')"
|
||||
iops_stddev="$(echo "${data}" | num_jobs="$nj" jq '[.jobs[] | .write.iops_stddev] | add/(env.num_jobs|tonumber) | .*1000|round/1000')"
|
||||
else
|
||||
die "io type ${io_type} is not valid when parsing results"
|
||||
fi
|
||||
|
||||
convert_results_to_json "${io_type}" "${bw}" "${bw_stddev}" "${iops}" "${iops_stddev}"
|
||||
}
|
||||
|
||||
function extract_test_params() {
|
||||
local data="${1}"
|
||||
[ -z "${data}" ] && die "Missing fio parameters when trying to convert to json format."
|
||||
|
||||
local json_params="$(echo "${data}" | jq -r '.jobs[0]."job options" | del(.name) | del(.rw) | del(.filename)')"
|
||||
local json="$(cat << EOF
|
||||
{
|
||||
"Parameters" : ${json_params}
|
||||
}
|
||||
EOF
|
||||
)"
|
||||
metrics_json_add_array_element "${json}"
|
||||
}
|
||||
|
||||
function convert_results_to_json() {
|
||||
local io_type="${1}"
|
||||
local bw="${2}"
|
||||
local bw_stddev="${3}"
|
||||
local iops="${4}"
|
||||
local iops_stddev="${5}"
|
||||
|
||||
[ -z "${io_type}" ] || [ -z "${bw}" ] || [ -z "${bw_stddev}" ] || [ -z "${iops}" ] || [ -z "${iops_stddev}" ] && die "Results are missing when trying to convert to json format."
|
||||
|
||||
local json="$(cat << EOF
|
||||
{
|
||||
"${io_type}" : {
|
||||
"bw" : "${bw}",
|
||||
"bw_stddev" : "${bw_stddev}",
|
||||
"iops" : "${iops}",
|
||||
"iops_stddev" : "${iops_stddev}",
|
||||
"units" : "Kb"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
)"
|
||||
metrics_json_add_array_element "${json}"
|
||||
}
|
||||
|
||||
function store_results() {
|
||||
local data_r="${1}"
|
||||
local data_w="${2}"
|
||||
local title="${3}"
|
||||
|
||||
[ -z "${data_r}" ] || [ -z "${data_w}" ] || [ -z "${title}" ] && die "Missing data and/or title when trying storing results."
|
||||
|
||||
metrics_json_start_array
|
||||
extract_test_params "${data_r}"
|
||||
parse_results "${data_r}"
|
||||
parse_results "${data_w}"
|
||||
metrics_json_end_array "${title}"
|
||||
}
|
||||
|
||||
function main() {
|
||||
setup
|
||||
|
||||
# Collect bs=4K, num_jobs=4, io-direct, io-depth=2
|
||||
info "Processing sequential type workload"
|
||||
sudo -E "${CTR_EXE}" t exec --exec-id "${RANDOM}" ${CONTAINER_ID} sh -c "./fio_bench.sh run-read-4k ${nj}" >/dev/null 2>&1
|
||||
local results_read_4K="$(sudo -E "${CTR_EXE}" t exec -t --exec-id "${RANDOM}" ${CONTAINER_ID} sh -c "./fio_bench.sh print-latest-results")"
|
||||
|
||||
sleep 0.5
|
||||
sudo -E "${CTR_EXE}" t exec --exec-id "${RANDOM}" ${CONTAINER_ID} sh -c "./fio_bench.sh run-write-4k ${nj}" >/dev/null 2>&1
|
||||
local results_write_4K="$(sudo -E "${CTR_EXE}" t exec -t --exec-id "${RANDOM}" ${CONTAINER_ID} sh -c "./fio_bench.sh print-latest-results")"
|
||||
|
||||
# Collect bs=64K, num_jobs=4, io-direct, io-depth=2
|
||||
info "Processing random type workload"
|
||||
sleep 0.5
|
||||
sudo -E "${CTR_EXE}" t exec --exec-id "${RANDOM}" ${CONTAINER_ID} sh -c "./fio_bench.sh run-randread-64k ${nj}" >/dev/null 2>&1
|
||||
local results_rand_read_64K="$(sudo -E "${CTR_EXE}" t exec -t --exec-id "${RANDOM}" ${CONTAINER_ID} sh -c "./fio_bench.sh print-latest-results")"
|
||||
|
||||
sleep 0.5
|
||||
sudo -E "${CTR_EXE}" t exec --exec-id "${RANDOM}" ${CONTAINER_ID} sh -c "./fio_bench.sh run-randwrite-64k ${nj}" >/dev/null 2>&1
|
||||
local results_rand_write_64K="$(sudo -E "${CTR_EXE}" t exec -t --exec-id "${RANDOM}" ${CONTAINER_ID} sh -c "./fio_bench.sh print-latest-results")"
|
||||
|
||||
# parse results
|
||||
metrics_json_init
|
||||
store_results "${results_read_4K}" "${results_write_4K}" "Results sequential"
|
||||
store_results "${results_rand_read_64K}" "${results_rand_write_64K}" "Results random"
|
||||
metrics_json_save
|
||||
}
|
||||
|
||||
main "$@"
|
Loading…
Reference in New Issue
Block a user