- Introduction
- Existing work
- Implementation
- User guide
- Developer guide
- Conclusions
Running benchmarks on the cloud is not only useful to compare different providers but also to measure the differences when changing the operating conditions of the cluster (e.g., updating the underlying physical hardware, replacing the CNI provider, adding more nodes, running different workloads in background, etc.).
kubemarks
focuses on the latter aspect, specifically on Kubernetes: it can run a large set of benchmarks and expose their results to the Prometheus monitoring system.
Adapting existing benchmarks to run on Kubernetes may not be straight-forward, especially when dealing with distributed ones that, by definition, need to involve multiple pods. Yet there are some attempts online that try to do so. On the other hand, retrieving benchmark results from different pods requires way more work than just adapting them to Kubernetes.
This is where PerfKit Benchmarker comes into play, an open-source tool by Google Cloud Platform that contains a set of benchmarks that are ready to be run on several cloud offerings, including Kubernetes. In short, PerfKit Benchmarker:
- creates and configures as many Kubernetes pods as needed by the benchmark,
- handles their lifecycle,
- installs dependencies,
- runs benchmarks,
- retrieves results from all pods and, lastly,
- makes it easy to add additional "results writers" so that results can be exported in different ways.
This section aims to describe some system and implementation details needed to accomplish what has been described in the introduction.
There are four main categories of PerfKit Benchmarker-supported benchmarks runnable on Kubernetes:
- I/O-based (e.g., fio),
- networking-oriented (e.g., iperf and netperf),
- resource manager-oriented (e.g., measuring VM placement latency and boot time), and
- database-oriented (e.g., YCSB on Cassandra and MongoDB, memtier_benchmark on Redis),
Due to the shortage of time, benchmarks belonging to the latter group still do not run properly. However, the current bugs seem to be pretty trivial to solve. For instance, MongoDB does not start because of a missing release file:
kubemarks-pkb STDERR: E: The repository 'https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/3.0 Release' does not have a Release file.
And Redis simply exceeds Resource Quotas:
kubemarks-pkb STDERR: Error from server (Forbidden): error when creating "/tmp/tmpaIV4bj": pods "pkb-77f34c4d-9" is forbidden: exceeded quota: default-quota, requested: limits.cpu=5, used: limits.cpu=50, limited: limits.cpu=54
Non-running benchmarks are tracked in issue #5.
Please note that PerfKit Benchmarker has to be extended so that benchmarks can expose additional information to the Prometheus Pushgateway (e.g., Node IDs). Besides tracking benchmarks that still have to be extended, issue #21 also lists a few previous commits that show how to do this.
PerfKit Benchmarker fork changes
Besides minor bug fixes, the current PerfKit Benchmarker fork has been extended with an additional "results writer", i.e., endpoint to which results are exported at the end of a single benchmark execution. More specifically, it now includes a Prometheus Pushgateway exporter, which exposes results according to the OpenMetrics format. The official Prometheus Python client has been used to implement this result writer. Furthermore, the CSV writer can now write results in "append" mode, allowing it to gradually add entries to the same CSV file as soon as benchmarks finish.
While PerfKit Benchmarker does include physical node information in benchmark results (e.g., lscpu
command output), it does not include Kubernetes node IDs.
This information is essential to make a comparison between different hardware solutions.
Since PerfKit Benchmarker is in charge of creating and configuring pods, its source code had to be extended to make pods aware of the node ID they were running on.
To do this, the Kubernetes Downward API comes in handy: it makes it possible to expose pod and container fields to a running container: here is the JSON snippet which made that possible:
'env': [{
'name': 'KUBE_NODE',
'valueFrom': {
'fieldRef': {
'fieldPath': 'spec.nodeName'
}
}
}]
This way, each Kubernetes pod can retrieve the node ID of the physical machine on which it is running, and PerfKit Benchmarker can successfully include this information in the results.
Benchmarks are run periodically as a Kubernetes CronJob.
It periodically executes a shell script (scripts/pkb/start.sh
) that cycles through all the benchmarks to be executed and, for each one of them, it
- checks whether it is compatible with Kubernetes, and
- builds a proper argument list to be passed to PerfKit Benchmarker.
A Kubernetes CronJob launches periodic jobs in Docker containers.
The base CronJob file of this repository yaml/base/kubemarks-pkb-cronjob.yaml
mainly executes PerfKit Benchmarker, which in turn needs to launch benchmarks in Docker containers so that the Kubernetes scheduler can allocate those onto pods.
marcomicera/kubemarks-cronjob
and marcomicera/kubemarks-pkb
are the Docker images launched by the CronJob and PerfKit Benchmarker, respectively.
- The former launches PerfKit Benchmarker via the
scripts/pkb/start.sh
script (more info here). - The latter takes care of resolving most of the dependencies needed by benchmarks so that PerfKit Benchmarker will not waste any other time doing so.
CronJob files and their structure
Kustomize is a tool included in kubectl
that allows users to customize Kubernetes objects. In this project, it is mainly used to:
- combine multiple objects in the right order of declaration into a single YAML file, and
- enable object inheritance.
Objects combination is particularly useful when creating the base CronJob file that runs an user-defined list of benchmarks, while inheritance makes it possible to create dedicated CronJob files for single benchmarks, making it simpler for the user to launch single benchmarks.
The base CronJob file running an user-defined benchmarks list
Kustomize needs a kustomization file as a starting point.
The base kustomization.yaml
file simply lists all the Kubernetes objects needed to launch kubemarks
:
resources:
- kubemarks-conf.yaml # benchmarks list, pushgateway address
- kubemarks-role.yaml # PerfKit Benchmarker permissions
- kubemarks-role-binding.yaml # associating Role to ServiceAccount
- kubemarks-pkb-cronjob.yaml # PerfKit Benchmarker base CronJob file
- kubemarks-git-creds.yaml # Git credentials for downloading private repo
- kubemarks-sa.yaml # ServiceAccount
- kubemarks-num-pods.yaml # number of pods to be used for each benchmark
All these files can be combined together and applied with a single command:
$ kubectl kustomize yaml/base | kubectl apply -f -
Before launching this command, yaml/base/kubemarks-num-pods.yaml
, yaml/base/kubemarks-conf.yaml
, and yaml/base/kubemarks-pkb-cronjob.yaml
should be modified accordingly, as described in the Guide section.
Repo cloner InitContainer
An InitContainer takes care of cloning this repository first. If this repository is private, then it needs Git credentials mounted as a Secret: please take a look at the corresponding developer guide section to learn more.
When launching single benchmarks, Kustomize can be used to override some Kubernetes object fields of the base CronJob file.
Looking at a benchmark-specific kustomization file is enough to determine which fields are actually overridden.
The following kustomization.yaml
file depicts all changes needed to run the fio benchmark:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../../base
nameSuffix: -fio
patchesStrategicMerge:
- benchmarks-list.yaml
- schedule.yaml
This file inherits all Kubernetes objects of the base kustomization.yaml
file, and:
- adds a metadata-name suffix to all its Kubernetes objects (i.e.,
-fio
), - defines the list of benchmarks to be executed (a ConfigMap in
yaml/benchmarks/fio/benchmarks-list.yaml
containing onlyfio
in the list) and, - overrides the frequency with which this benchmark is run (based on its average completion time).
The nameSuffix
field causes all resources to have this suffix prepended to their name, hence each benchmark will run in a dedicated CronJob whose name contains the benchmark one.
It is worth noticing that since every benchmark's Kustomize folder (e.g., yaml/benchmarks/fio
) contains its own schedule.yaml
file, every benchmark will be launched at different frequencies.
Benchmarks already have pre-determined frequency values based on their average completion time (i.e., the longer they take to complete, the less frequent their corresponding CronJob will be launched), but this can be overridden by simply editing their schedule.yaml
file before launching.
Here is an example of schedule.yaml
file (for the fio benchmark):
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: kubemarks-pkb
spec:
schedule: '0 */4 * * *'
status: {}
According to the Cron format, fio will be launched every 4 hours. Modifying such files while a benchmark is running in a CronJob will not cause any change.
Upon launching a benchmark, Kustomize uses the base kustomization.yaml
file (as described here) to assemble together various Kubernetes objects.
Between these objects, there are a couple ones used for Role-based access control, such as a ServiceAccount yaml/base/kubemarks-sa.yaml
, a Role yaml/base/kubemarks-role.yaml
and a RoleBinding object yaml/base/kubemarks-role-binding.yaml
.
This section examines the How to run it section of the main README.md
file in depth.
This section describes all configuration steps to be made before launching benchmarks.
Number of Kubernetes pods
The number of Kubernetes pods to be used for every benchmark is defined in the yaml/base/kubemarks-num-pods.yaml
ConfigMap. Here is an extract:
kind: ConfigMap
metadata:
name: kubemarks-num-pods
apiVersion: v1
data:
kubemarks-num-pods.yaml: |
flags:
cloud: Kubernetes
kubernetes_anti_affinity: false
block_storage_workload:
description: >
Runs FIO in sequential, random, read and
write modes to simulate various scenarios.
vm_groups:
default:
vm_count: 1
It is worth noticing that PerfKit Benchmarker uses the term VM as a generalization of Kubernetes pod since it supports multiple cloud providers.
This ConfigMap will be then applied by Kustomize upon launching a benchmark, and it will then be mounted as a file in the container running PerfKit Benchmarker.
CronJob frequency
Next, the general CronJob frequency can be adjusted in the yaml/base/kubemarks-pkb-cronjob.yaml
file:
schedule: '*/30 * * * *'
The schedule follows the Cron format. There is no need to specify this for the one-benchmark-only CronJob files, as the frequency will be automatically set by Kustomize.
Benchmarks list and Pushgateway address
The yaml/base/kubemarks-conf.yaml
file contains two experiment options, namely
- the Prometheus Pushgateway address, and
- the list of benchmarks to run.
# `kubectl kustomize yaml/base | kubectl apply -f -` will
# automatically update this ConfigMap
apiVersion: v1
data:
benchmarks: cluster_boot fio
pushgateway: pushgateway.address.test
kind: ConfigMap
metadata:
name: kubemarks-conf
Experiments can be chosen amongst this list:
block_storage_workload
cluster_boot
fio
iperf
mesh_network
netperf
This ConfigMap will be automatically applied/updated by Kustomize.
Benchmarks can be either launched singularly with (e.g., for iperf):
$ kubectl kustomize yaml/benchmarks/iperf | kubectl apply -f -
or programmatically, after having updated the list of benchmarks to run:
$ kubectl kustomize yaml/base | kubectl apply -f -
Take a look at the CONTRIBUTING.md
file.
kubemarks
is able to periodically run various kinds of benchmarks on a Kubernetes cluster.
The current PerfKit Benchmarker fork (more details here) includes physical node identifiers into benchmark results and gradually exposes them to a Prometheus Pushgateway following the OpenMetrics format.
The tool is configurable through a few handy configuration files.