<!DOCTYPE article PUBLIC "-//NLM//DTD JATS (Z39.96) Journal Archiving and Interchange DTD v1.0 20120330//EN" "JATS-archivearticle1.dtd">
<article xmlns:xlink="http://www.w3.org/1999/xlink">
  <front>
    <journal-meta />
    <article-meta>
      <title-group>
        <article-title>Reproducible Benchmarking of Cloud-Native Applications With the Kubernetes Operator Pattern</article-title>
      </title-group>
      <contrib-group>
        <contrib contrib-type="author">
          <string-name>Sören Henning</string-name>
          <xref ref-type="aff" rid="aff0">0</xref>
        </contrib>
        <contrib contrib-type="author">
          <string-name>Benedikt Wetzel</string-name>
          <xref ref-type="aff" rid="aff0">0</xref>
        </contrib>
        <contrib contrib-type="author">
          <string-name>Wilhelm Hasselbring</string-name>
          <xref ref-type="aff" rid="aff0">0</xref>
        </contrib>
        <aff id="aff0">
          <label>0</label>
          <institution>Software Engineering Group, Kiel University</institution>
          ,
          <addr-line>24098 Kiel</addr-line>
          ,
          <country country="DE">Germany</country>
        </aff>
      </contrib-group>
      <abstract>
        <p>Reproducibility is often mentioned as a core requirement for benchmarking studies of software systems and services. “Cloud-native” is an emerging style for building large-scale software systems, which leads to an increasing amount of benchmarks for cloud-native tools and architectures. However, the complex nature of cloud-native deployments makes the execution and repetition of benchmarks tedious and error-prone. In this paper we report on our experience with developing a benchmarking tool based on established cloud-native patterns and tools. In particular, we present a benchmarking tool architecture based on the Kubernetes Operator Pattern. Accompanied with a role model and a data model for describing benchmarks and their executions, this architecture aims to simplify defining, distributing, and executing benchmarks for better reproducibility.</p>
      </abstract>
      <kwd-group>
        <kwd>eol&gt;Benchmarking</kwd>
        <kwd>Reproducibility</kwd>
        <kwd>Cloud-native</kwd>
        <kwd>Kubernetes</kwd>
      </kwd-group>
    </article-meta>
  </front>
  <body>
    <sec id="sec-1">
      <title>1. Introduction</title>
      <p>
        Along with its growing adoption in industry, “cloud-native” [
        <xref ref-type="bibr" rid="ref1">1</xref>
        ] software systems and their
performance are increasingly studied in research. For example, the performance of microservices,
an architectural pattern often classified as cloud-native, has recently been benchmarked [
        <xref ref-type="bibr" rid="ref2 ref3">2, 3</xref>
        ].
As stated by standardization organizations and research communities, reproducibility is a
core quality attribute of benchmarking studies [
        <xref ref-type="bibr" rid="ref4 ref5">4, 5</xref>
        ]. This applies even more to studies on
cloud computing, where execution environments are hardly controllable [
        <xref ref-type="bibr" rid="ref6">6</xref>
        ]. Also other often
mentioned quality attributes such as usability and verifiability support repeatability, which in
turn is a prerequisite for reproducibility. A common approach to improve reproducibility is by
providing scripts or the like, which set-up the benchmarking environment, and to share the
exact configuration used in the original research.
      </p>
      <p>However, we experienced that for cloud-native systems, this traditional way of providing
benchmarks poses significant dificulties. Cloud-native applications typically consist of several
processes, running in isolated containers, which have to be scheduled to diferent computing
nodes. They require application-level configurations passed to these containers, comprehensive
descriptions of internal and external network interconnection, and integration of diferent
storage systems. These and other aspects make the deployment and operation of cloud-native
applications a rather complex task and, hence, also complicate benchmarking.</p>
      <p>
        On the other hand, several tools emerged in the cloud-native community for enhancing the
deployment and operation. For example, Kubernetes is nowadays the de-facto standard for
declarative orchestration, Prometheus collects monitoring data of various sources, and Helm
simplifies packaging, distributing, and installing entire applications. Recently, the Kubernetes
Operator Pattern [
        <xref ref-type="bibr" rid="ref7">7</xref>
        ] has gained much attention as an approach to integrate domain knowledge
into Kubernetes. We propose to adopt established cloud-native technologies along with the
Operator Pattern for designing cloud-native benchmarking tools that particularly support
reproducibility.
      </p>
      <p>After a brief introduction of Kubernetes and the Operator Pattern, we present the necessary
data model and system architecture for a benchmarking operator. We complement this paper
by an overview of how we apply this approach in our Theodolite scalability benchmarking tool.</p>
    </sec>
    <sec id="sec-2">
      <title>2. The Kubernetes Operator Pattern</title>
      <p>Kubernetes is a declarative orchestration tool for cloud-native applications. Users describe the
desired state of an application in so-called resources and in a continuous reconciliation loop,
Kubernetes adopts the current system to reach the desired state. A typical example of a resource
is a Deployment, which describes an application component (e.g., a microservice) consisting of
container images, number of replicas and others. A common way for users to interact with
Kubernetes is by describing resources in YAML files and applying these files to Kubernetes by
using the kubectl command line tool.</p>
      <p>
        The Kubernetes Operator Pattern [
        <xref ref-type="bibr" rid="ref7">7</xref>
        ] is a recent approach to integrate domain knowledge
into Kubernetes’ orchestration process. Implementing this pattern involves two things: First,
custom resource definitions (CRDs) define new types of resources that can be managed by the
Kubernetes API. Second, a dedicated software component (the actual operator), runs inside the
Kubernetes cluster and manages the entire life cycle of what is described by the CRDs. This
so called operator interacts with the Kubernetes API and reacts to creation, modification, or
removal of custom resources (CRs, instances of CRDs).
      </p>
    </sec>
    <sec id="sec-3">
      <title>3. Custom Resource Definitions for Benchmarking</title>
      <p>As presented in Section 2, the Operator Pattern combines knowledge of operating Kubernetes
with domain knowledge. As the domain at hand is benchmarking, we propose that actors
involved in benchmarking describe their operational knowledge in CRDs. In general, we can
observe two actors involved in benchmarking:
The Benchmark Designer has knowledge about operating a system under test (SUT) as
well as how the quality to be benchmarked can be measured (metric and measurement
method). Additionally, he/she defines how metrics and results can be interpreted to
compare diferent SUTs. The Benchmark Designer bundles all of this in a benchmarking
tool or sometimes in an artifact that can be interpreted by a benchmarking tool.
The Benchmarker intents to compare and rank diferent existing SUTs, evaluates new
methods or tools against a defined standard, or repeats previous experiments. For this purpose,
publishes to
Benchmarking</p>
      <p>Operator
publishes to</p>
      <p>Benchmark</p>
      <p>Repository
Research Data</p>
      <p>Repository
Benchmark
Designer
creates</p>
      <p>Benchmark
refers to
deploys to
deploys to</p>
      <p>
        he/she executes existing benchmarks. A detailed description of this actor can be found by
Kounev et al. [
        <xref ref-type="bibr" rid="ref8">8</xref>
        ].
      </p>
      <p>Based on this distinctions of roles, we propose a data model in which benchmarks and their
executions are separated. Figure 1 illustrates this. We envisage individual CRDs for both
benchmarks and executions:
Benchmarks are defined by Benchmark Designers. They describe the SUT as well as the
load generation. Additionally, they may describe configurations of the benchmarking
method, which are interpreted by the benchmarking tool. Benchmarks can be published as
supplemental material of research papers, but ideally they are versioned and maintained
in public repositories (e.g., at GitHub). Benchmarks are stateless as they can be executed
arbitrarily often.</p>
      <p>Executions on the other hand describe a single execution of a Benchmark. They refer to
exactly one Benchmark and configure the experimental setup. Definitions of executions
can be shared, for example, as part of a research study that benchmarks the performance
of diferent SUTs. When deployed to Kubernetes, Executions have a state, which is
typically something like Pending, Running, Finished, or Failed if an error occurred (similar
to Kubernetes Jobs).</p>
    </sec>
    <sec id="sec-4">
      <title>4. Benchmarking Operator Architecture</title>
      <p>
        Standard software components involved in benchmarking are a system under test (SUT), a load
generator, an experiment controller, a measurement database, and an (optional) monitoring
component for passive observation [
        <xref ref-type="bibr" rid="ref9">9</xref>
        ]. We propose a benchmarking tool architecture consisting
of these components, which applies the Kubernetes Operator pattern and uses state-of-the-art
cloud-native technologies.1 Figure 2 provides an overview of this architecture and shows how a
1In our proposed architecture, we refer to actual technologies instead of abstract component types. The
cloudnative ecosystem evolves rapidly and new tools and their underlying concepts often go hand in hand. Nonetheless,
deploys
Benchmarks/Executions
      </p>
      <p>Kubernetes API</p>
      <p>Operator
notifies
updates states
benchmarker will interact with such a benchmarking tool.</p>
      <p>To execute benchmarks, a Benchmarker solely interacts with the Kubernetes API, for instance,
via tools such as kubectl or the Kubernetes dashboard. The Benchmarker registers benchmarks
and initiates their executions by applying the corresponding CRs. The operator continuously
observes the Kubernetes API for changes in the list of registered executions. It takes on the
role of the experiment controller and decides based on a tool-specific policy whether it starts
an execution immediately or schedules it to a list of pending executions. A typical constraint
is, for example, that only one execution should run at a time to avoid interfering benchmark
results. Once a registered execution is started, the operator reads the CRs of that execution
and the corresponding benchmark. It creates and starts the SUT and the load generator via the
Kubernetes API as well as performs additional cluster configurations.</p>
      <p>While executing the benchmark, the operator updates the state of the Execution (e.g., to a
state Running) and registers events (e.g., once a certain phase of the execution is completed).
State and events of an Execution are managed by the Kubernetes API and are used to provide
feedback to the Benchmarker. During the entire benchmark execution, Prometheus collects
monitoring data from the SUT and the load generator. This can often be done in the same way
as the cloud-native applications is monitored in a production deployment. Once a benchmark
execution is finished, the operator stops and removes the SUT and the load generator. In addition,
it requests collected monitoring data from Prometheus and analyzes them. Also requesting and
analyzing monitoring data during an execution to adjust the SUT and the load generator is
possible, which is necessary for certain types of benchmarks.</p>
      <p>The operator stores benchmarking results persistently (e.g., in a database or as CSV files
in a Kubernetes volume), from where the Benchmarker can access them for ofline analysis,
archiving, or sharing. For the passive observation of benchmark executions, Grafana can be
integrated in the architecture to visualize the monitoring data in real time.</p>
      <p>Installing a benchmarking tool based on our proposed architecture might quickly become
a complex tasks due to the many components and dependencies involved. In particular for
reproducibility, it is often also necessary to install a specific version of a benchmarking tool. To
we expect the mentioned tools to be rather matured and that possible successors can be used similarly.
simplify installation and upgrading, we propose to package an operator-based benchmarking
tool with Helm. Helm then registers the benchmarking CRDs and installs the operator with all
its dependencies.</p>
    </sec>
    <sec id="sec-5">
      <title>5. The Theodolite Scalability Benchmarking Tool</title>
      <p>
        Theodolite [
        <xref ref-type="bibr" rid="ref10">10</xref>
        ] is our tool for benchmarking scalability of distributed stream processing engines
in microservices. We recently rebuild Theodolite according to the architecture presented in
the previous sections. Examples for Benchmark and Executions resources are provided with
Theodolite’s sources.2
      </p>
      <p>
        The Benchmark resource specifies the SUT and the load generator as sets of other resources,
which describe their deployment. Since scalability is measured according to a load type and a
resource type [
        <xref ref-type="bibr" rid="ref11">11</xref>
        ], Theodolite evaluates a SUT for diferent values of the corresponding load
and resource type. The Benchmark resource defines possible load and resource types by a name
and a set of patchers. Patchers are functions, integrated into Theodolite, which take a value
as input and modify a Kubernetes resource according to that value. For example a load that
is defined by the amount of diferent data sources can be described by a patcher that adjusts
an environment variable of the load generator, which defines the number of data sources. A
resource type defined by the amount of replicas can be defined by a patcher which adjusts the
replica field of a deployment resource. As Theodolite integrates Apache Kafka as messaging
system between load generator and SUT, the Benchmark resource also configures the Kafka
topics that are generated by the benchmarking tool.
      </p>
      <p>
        The Execution resource refers to a Benchmark resource by its name. It selects one load and
one resource type from those provided by the Benchmark and specifies a set of values to be
evaluated for each type. Theodolite performs isolated experiments for a certain load intensity
with a certain amount of resources and checks for each experiment whether SLOs are met
[
        <xref ref-type="bibr" rid="ref11">11</xref>
        ]. The set of SLOs to be evaluated can also be configured via the execution resource. In
Theodolite, SLO checks are realized in self-contained Python containers that analyze Prometheus
monitoring data. Furthermore, an Execution describes the experimental setup, for example,
the duration per experiment and the number of repetitions. Finally, an Execution allows to
make modifications to the SUT defined in the benchmark, for example, to evaluate diferent
configuration or deployment options of a SUT. This is again done with patchers, which in this
case are passed a fixed value.
      </p>
    </sec>
    <sec id="sec-6">
      <title>6. Discussion and Conclusions</title>
      <p>The Kubernetes Operator Pattern allows to manage application-level configuration as part of the
orchestration process. Applied to benchmarking cloud-native applications, it allows managing
benchmarks and executions directly via the Kubernetes API. Main benefits of a benchmarking
operator are:
• Benchmarking tool, benchmark, and execution are clearly separated.
2https://github.com/cau-se/theodolite/blob/master/theodolite/examples/operator/
• Benchmarks are defined according to a strict format and published as descriptive files
without including any tooling.
• All experimental setup is bundled in one file describing the execution of a benchmark.
• Benchmarks and Executions are created in a purely declarative way, saving Benchmark</p>
      <p>Designer and Benchmarker from implementing any Kubernetes-specific error handling.
• Benchmarks and Executions can be managed via default Kubernetes tools (e.g., kubectl),
providing a rich user experience without additional implementation efort.
• Since their CRDs can have an OpenAPI schema, Benchmarks and Executions can directly
be validated by the Kubernetes API.</p>
      <p>All in all, we expect building a benchmark tool with the operator pattern facilitates
repeatability and, hence, ultimately reproducibility of cloud-native benchmarking studies.</p>
    </sec>
  </body>
  <back>
    <ref-list>
      <ref id="ref1">
        <mixed-citation>
          [1]
          <string-name>
            <given-names>Cloud</given-names>
            <surname>Native Computing Foundation</surname>
          </string-name>
          ,
          <source>CNCF cloud native definition v1.0</source>
          ,
          <year>2018</year>
          . URL: https://github.com/cncf/toc/blob/main/DEFINITION.md.
        </mixed-citation>
      </ref>
      <ref id="ref2">
        <mixed-citation>
          [2]
          <string-name>
            <given-names>S.</given-names>
            <surname>Eismann</surname>
          </string-name>
          ,
          <string-name>
            <given-names>C.-P.</given-names>
            <surname>Bezemer</surname>
          </string-name>
          ,
          <string-name>
            <given-names>W.</given-names>
            <surname>Shang</surname>
          </string-name>
          ,
          <string-name>
            <given-names>D.</given-names>
            <surname>Okanović</surname>
          </string-name>
          ,
          <string-name>
            <surname>A. van Hoorn</surname>
          </string-name>
          ,
          <article-title>Microservices: A performance tester's dream or nightmare?</article-title>
          , in: International Conference on Performance Engineering,
          <year>2020</year>
          . doi:
          <volume>10</volume>
          .1145/3358960.3379124.
        </mixed-citation>
      </ref>
      <ref id="ref3">
        <mixed-citation>
          [3]
          <string-name>
            <given-names>M.</given-names>
            <surname>Grambow</surname>
          </string-name>
          ,
          <string-name>
            <given-names>E.</given-names>
            <surname>Wittern</surname>
          </string-name>
          ,
          <string-name>
            <given-names>D.</given-names>
            <surname>Bermbach</surname>
          </string-name>
          ,
          <article-title>Benchmarking the performance of microservice applications</article-title>
          ,
          <source>SIGAPP Appl. Comput. Rev</source>
          .
          <volume>20</volume>
          (
          <year>2020</year>
          ). doi:
          <volume>10</volume>
          .1145/3429204.3429206.
        </mixed-citation>
      </ref>
      <ref id="ref4">
        <mixed-citation>
          [4]
          <string-name>
            <given-names>J. v.</given-names>
            <surname>Kistowski</surname>
          </string-name>
          ,
          <string-name>
            <given-names>J. A.</given-names>
            <surname>Arnold</surname>
          </string-name>
          ,
          <string-name>
            <given-names>K.</given-names>
            <surname>Huppler</surname>
          </string-name>
          ,
          <string-name>
            <surname>K.-D. Lange</surname>
            ,
            <given-names>J. L.</given-names>
          </string-name>
          <string-name>
            <surname>Henning</surname>
            ,
            <given-names>P.</given-names>
          </string-name>
          <string-name>
            <surname>Cao</surname>
          </string-name>
          ,
          <article-title>How to build a benchmark</article-title>
          ,
          <source>in: International Conference on Performance Engineering</source>
          ,
          <year>2015</year>
          . doi:
          <volume>10</volume>
          .1145/2668930.2688819.
        </mixed-citation>
      </ref>
      <ref id="ref5">
        <mixed-citation>
          [5]
          <string-name>
            <given-names>W.</given-names>
            <surname>Hasselbring</surname>
          </string-name>
          ,
          <article-title>Benchmarking as empirical standard in software engineering research</article-title>
          , in: Evaluation and Assessment in Software Engineering,
          <year>2021</year>
          . doi:https://doi.org/10. 1145/3463274.3463361.
        </mixed-citation>
      </ref>
      <ref id="ref6">
        <mixed-citation>
          [6]
          <string-name>
            <given-names>A. V.</given-names>
            <surname>Papadopoulos</surname>
          </string-name>
          ,
          <string-name>
            <given-names>L.</given-names>
            <surname>Versluis</surname>
          </string-name>
          ,
          <string-name>
            <given-names>A.</given-names>
            <surname>Bauer</surname>
          </string-name>
          ,
          <string-name>
            <given-names>N.</given-names>
            <surname>Herbst</surname>
          </string-name>
          ,
          <string-name>
            <given-names>J. Von</given-names>
            <surname>Kistowski</surname>
          </string-name>
          ,
          <string-name>
            <surname>A</surname>
          </string-name>
          . Ali-eldin, C. Abad,
          <string-name>
            <given-names>J. N.</given-names>
            <surname>Amaral</surname>
          </string-name>
          ,
          <string-name>
            <given-names>P.</given-names>
            <surname>Tůma</surname>
          </string-name>
          ,
          <string-name>
            <given-names>A.</given-names>
            <surname>Iosup</surname>
          </string-name>
          ,
          <article-title>Methodological principles for reproducible performance evaluation in cloud computing</article-title>
          ,
          <source>IEEE Transactions on Software Engineering</source>
          <volume>47</volume>
          (
          <year>2021</year>
          ). doi:
          <volume>10</volume>
          .1109/TSE.
          <year>2019</year>
          .
          <volume>2927908</volume>
          .
        </mixed-citation>
      </ref>
      <ref id="ref7">
        <mixed-citation>
          [7]
          <string-name>
            <given-names>B.</given-names>
            <surname>Ibryam</surname>
          </string-name>
          ,
          <string-name>
            <given-names>R.</given-names>
            <surname>Huss</surname>
          </string-name>
          , Kubernetes Patterns:
          <article-title>Reusable Elements for Designing Cloud Native Applications</article-title>
          , 1st ed.,
          <source>O'Reilly</source>
          ,
          <year>2019</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref8">
        <mixed-citation>
          [8]
          <string-name>
            <given-names>S.</given-names>
            <surname>Kounev</surname>
          </string-name>
          ,
          <string-name>
            <surname>K.-D. Lange</surname>
            ,
            <given-names>J. von Kistowski</given-names>
          </string-name>
          ,
          <source>Systems Benchmarking: For Scientists and Engineers</source>
          , 1st ed., Springer,
          <year>2020</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref9">
        <mixed-citation>
          [9]
          <string-name>
            <given-names>D.</given-names>
            <surname>Bermbach</surname>
          </string-name>
          ,
          <string-name>
            <given-names>E.</given-names>
            <surname>Wittern</surname>
          </string-name>
          ,
          <string-name>
            <given-names>S.</given-names>
            <surname>Tai</surname>
          </string-name>
          , Cloud Service Benchmarking:
          <article-title>Measuring Quality of Cloud Services from a Client Perspective</article-title>
          , 1st ed., Springer,
          <year>2017</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref10">
        <mixed-citation>
          [10]
          <string-name>
            <given-names>S.</given-names>
            <surname>Henning</surname>
          </string-name>
          , W. Hasselbring, Theodolite:
          <article-title>Scalability benchmarking of distributed stream processing engines in microservice architectures</article-title>
          ,
          <source>Big Data Research</source>
          <volume>25</volume>
          (
          <year>2021</year>
          ). doi:https: //doi.org/10.1016/j.bdr.
          <year>2021</year>
          .
          <volume>100209</volume>
          .
        </mixed-citation>
      </ref>
      <ref id="ref11">
        <mixed-citation>
          [11]
          <string-name>
            <given-names>S.</given-names>
            <surname>Henning</surname>
          </string-name>
          , W. Hasselbring,
          <article-title>How to measure scalability of distributed stream processing engines?</article-title>
          , in: International Conference on Performance Engineering,
          <year>2021</year>
          . doi:https: //doi.org/10.1145/3447545.3451190.
        </mixed-citation>
      </ref>
    </ref-list>
  </back>
</article>