|
| 1 | +--- |
| 2 | +title: "Working with Benk: A storage provisioning and IO performance benchmark |
| 3 | + suite for Kubernetes" |
| 4 | +date: 2024-01-12T20:03:16.376Z |
| 5 | +author: Michael Mattsson |
| 6 | +authorimage: /img/portrait-192.jpg |
| 7 | +thumbnailimage: /img/benk-some.png |
| 8 | +disable: false |
| 9 | +tags: |
| 10 | + - kubernetes |
| 11 | + - devops |
| 12 | + - hpe-alletra |
| 13 | + - hpe-nimble-storage |
| 14 | + - hpe-3par-and-primera |
| 15 | +--- |
| 16 | +Recently Hewlett Packard Enterprise (HPE) published an open source benchmark suite for storage drivers capable of dynamically provisioning persistent volumes to Kubernetes. The suite is called [Benk](https://github.com/hpe-storage/benk) and it is an acronym that plays with the word bench as in benchmark and Kubernetes. Benk is used internally at HPE for mapping performance metrics around the provisioning process itself as well as for IO performance. It’s still a bit rough around the edges and not feature complete, but it’s still a very useful tool to capture performance across large swaths of configurations with a high degree of automation and repeatability without too much user attendance. |
| 17 | + |
| 18 | +A few of the features include: |
| 19 | + |
| 20 | +* Highly customizable rendering of Kubernetes resources through kustomize templates |
| 21 | +* A simple Kubernetes batch job that manage all the provisioning, decommissioning and benchmarking |
| 22 | +* A single configuration file per job that abstracts Kubernetes constructs and IO parameters |
| 23 | +* Use of industry standard Flexible I/O tester (FIO) for filesystem benchmarking |
| 24 | +* Easy-to-build-your-own output templates using Jinja2 with the rich metrics in JSON format |
| 25 | + |
| 26 | +Let’s walk through a practical example of configuring, running and reporting a benchmark with Benk. |
| 27 | + |
| 28 | +# Prerequisites |
| 29 | + |
| 30 | +At the time of writing, parts of Benk can only be run on Mac and Linux due to a dependency on Bash. It will of course run in WSL on Windows. Besides cloning the [GitHub repository](https://github.com/hpe-storage/benk) with `git`, a recent version of `kubectl` and Python 3.x needs to be installed. |
| 31 | + |
| 32 | +Some pro-efficiency working with scripts, JSON and Kubernetes is helpful to better understand the workflows. |
| 33 | + |
| 34 | +# Hello Benk! |
| 35 | + |
| 36 | +The synopsis section of Benk on GitHub highlights the main steps to run the default job. It’s a good measure stick to understand if the lights come on and determine if the system under test is ready for a more complex job. |
| 37 | + |
| 38 | +Let’s walk through each step and explain as we go along. Descriptions are below the commands unless noted. |
| 39 | + |
| 40 | +```text |
| 41 | +git clone https://github.com/hpe-storage/benk && cd benk |
| 42 | +``` |
| 43 | + |
| 44 | +Cloning and entering the benk directory is now considered your home. Your logs will go into `logs`, your workload configurations are in `kustomize/overlays` and the Jinja2 templates are in `jinja2`. |
| 45 | + |
| 46 | +```text |
| 47 | +pip3 install -r requirements.txt |
| 48 | +``` |
| 49 | + |
| 50 | +This will install the required Python packages for the Benk `outputter.py` script. Python is not required if you don’t intend to render any output and you just want to use Benk for load generation. |
| 51 | + |
| 52 | +```text |
| 53 | +cp kustomize/base/config-dist.env kustomize/base/config.env |
| 54 | +cp kustomize/base/storageclass-dist.yaml kustomize/base/storageclass.yaml |
| 55 | +``` |
| 56 | + |
| 57 | +The two “dist” files are the base of your configuration. A `config.env` file that contains information that will be inserted into the `StorageClass` `Secret` reference. While the base configuration is heavily biased towards the HPE CSI Driver for Kubernetes, any storage driver that uses a `StorageClass` for dynamic provisioning for filesystems `Persistent Volume Claims` (PVC) can be used. Using the `storageclass.yaml` to configure your driver and pertinent details is at your discretion. |
| 58 | + |
| 59 | +```text |
| 60 | +kubectl create ns benk |
| 61 | +``` |
| 62 | + |
| 63 | +All `Namespace` resources will be provisioned into the “benk” `Namespace`. |
| 64 | + |
| 65 | +```text |
| 66 | +kubectl apply -k kustomize/overlays/default |
| 67 | +kubectl wait -n benk --for=condition=complete job/benk |
| 68 | +``` |
| 69 | + |
| 70 | +This will create the default job and wait for it to complete. It runs a 80/20 read/write random 8K workload for 30 seconds on a single replica `Deployment` using a single `PVC`. |
| 71 | + |
| 72 | +```text |
| 73 | +kubectl logs -n benk job/benk | jq |
| 74 | +``` |
| 75 | + |
| 76 | +This will render the log in pretty JSON. The log is pretty substantial and intentionally left out from the blog. An example log entry from the above job is available [on GitHub](https://github.com/hpe-storage/benk/tree/main/jinja2). |
| 77 | + |
| 78 | +There’s also tiny output template provided in the repository `jinja2/example-default.yaml.j2` that pulls some data out of the log file. |
| 79 | + |
| 80 | +```text |
| 81 | +kubectl logs -n benk job/benk | ./src/benk/outputter.py -t jinja2/example-default.yaml.j2 -l- |
| 82 | +--- |
| 83 | +report: |
| 84 | + name: Example YAML template |
| 85 | + logfile: <stdin> |
| 86 | + jobs: |
| 87 | + - threads: 1 |
| 88 | + runtime: 49s |
| 89 | + iops: 1258 |
| 90 | + bandwidth: 10MB/s |
| 91 | + bs: 8k |
| 92 | +``` |
| 93 | + |
| 94 | +Now we’ve completed the three corners of running Benk. Configured the environment and a job, ran the job successfully and distilled the results into something human readable for our demonstration. |
| 95 | + |
| 96 | +# The sequencer |
| 97 | + |
| 98 | +The core of Benk for now, is to run preconfigured jobs with kustomize. It may seem a lot of work for very little output. That’s why the repository contain two important scripts, `sequencer.sh` and `sequencer-cluster.sh`. These scripts will help you run multiple jobs and structure the output to create more meaty reports. |
| 99 | + |
| 100 | +First, copy the “default” kustomize directory into eight separate directories. |
| 101 | + |
| 102 | +```text |
| 103 | +for i in {1..8}; do cp -a kustomize/overlays/default kustomize/overlays/mytest-${i}; done |
| 104 | +``` |
| 105 | + |
| 106 | +Now, edit each `config.env` in each directory. I use `vi` to “:wn” myself through the templates. |
| 107 | + |
| 108 | +```text |
| 109 | +vi kustomize/overlays/mytest-*/config.env |
| 110 | +``` |
| 111 | + |
| 112 | +In each iteration, we’ll double `workloadThreads` so we end up with a sequence like 1, 2, 4, 8, 16, 32, 64 and 128. Would it not be useful if we could run these in sequence and report the results in the same template we used previously to understand if the system scales to when adding more threads to the workload? (The entire demo environment runs in virtual machines on decade old hardware, please don’t judge.) |
| 113 | + |
| 114 | +Run the sequence: |
| 115 | + |
| 116 | +```text |
| 117 | +./sequencer.sh mytest- |
| 118 | +``` |
| 119 | + |
| 120 | +What happens here is that the sequencer script will use globbing to find all overlays that has the prefix “mytest-”. Be mindful how you name things to avoid unexpected jobs to be executed. |
| 121 | + |
| 122 | +Next we can use the same report for all the jobs. |
| 123 | + |
| 124 | +```text |
| 125 | +./src/benk/outputter.py -l logs/run-mytest-<unique timestamp>.log -t jinja2/example-default.yaml.j2 |
| 126 | +--- |
| 127 | +report: |
| 128 | + name: Example YAML template |
| 129 | + logfile: logs/run-mytest-20240111161937.log |
| 130 | + jobs: |
| 131 | + - threads: 1 |
| 132 | + runtime: 55s |
| 133 | + iops: 1664 |
| 134 | + bandwidth: 13MB/s |
| 135 | + bs: 8k |
| 136 | + - threads: 2 |
| 137 | + runtime: 49s |
| 138 | + iops: 3291 |
| 139 | + bandwidth: 26MB/s |
| 140 | + bs: 8k |
| 141 | + - threads: 4 |
| 142 | + runtime: 48s |
| 143 | + iops: 8044 |
| 144 | + bandwidth: 63MB/s |
| 145 | + bs: 8k |
| 146 | + - threads: 8 |
| 147 | + runtime: 53s |
| 148 | + iops: 12075 |
| 149 | + bandwidth: 94MB/s |
| 150 | + bs: 8k |
| 151 | + - threads: 16 |
| 152 | + runtime: 46s |
| 153 | + iops: 16357 |
| 154 | + bandwidth: 128MB/s |
| 155 | + bs: 8k |
| 156 | + - threads: 32 |
| 157 | + runtime: 51s |
| 158 | + iops: 17284 |
| 159 | + bandwidth: 135MB/s |
| 160 | + bs: 8k |
| 161 | + - threads: 64 |
| 162 | + runtime: 48s |
| 163 | + iops: 17489 |
| 164 | + bandwidth: 137MB/s |
| 165 | + bs: 8k |
| 166 | + - threads: 128 |
| 167 | + runtime: 54s |
| 168 | + iops: 18761 |
| 169 | + bandwidth: 147MB/s |
| 170 | + bs: 8k |
| 171 | +``` |
| 172 | + |
| 173 | +We can now observe that we’re seeing diminishing returns by adding more than 16 threads to this workload. |
| 174 | + |
| 175 | +# A/B comparison |
| 176 | + |
| 177 | +A/B testing is something that has gained popularity in human interaction testing for websites. This is also a crucial testing methodology to analyze systems performance by simply changing a single parameter and rerun the exact same test to compare the outcomes. We’re in luck as Benk allows reporting on two log files at once with just a slightly different data structures being fed to the Jinja2 templates. |
| 178 | + |
| 179 | +Let’s change block size for our previous workload example. We’re going to summarize if increasing the block size will increase the bandwidth for the workload. |
| 180 | + |
| 181 | +Change the “workloadBlockSize” to “64k” with the `vi` ceremony. |
| 182 | + |
| 183 | +```text |
| 184 | +vi kustomize/overlays/mytest-*/config.env |
| 185 | +``` |
| 186 | + |
| 187 | +Re-run the sequencer. |
| 188 | + |
| 189 | +```text |
| 190 | +./sequencer.sh mytest- |
| 191 | +``` |
| 192 | + |
| 193 | +We’ll have to use a different template as Jinja2 now has to deal with managing two log files at a time. The syntax for the `outputter.py` script is also slightly different. |
| 194 | + |
| 195 | +```text |
| 196 | +./src/benk/outputter.py -a logs/run-mytest-<unique timestamp>.log -b logs/run-mytest-<unique timestamp>.log -t jinja2/example-default-ab.md.j2 |
| 197 | +``` |
| 198 | + |
| 199 | +This report template will summarize the findings in a markdown table, suitable to include in a GitHub pull request or similar to illustrate a certain discovery. |
| 200 | + |
| 201 | +```text |
| 202 | +| Threads | A (MB/s) | B (MB/s) | Diff | |
| 203 | +| ------- | -------- | -------- | ---- | |
| 204 | +| 1 | 13 | 55 | 4.2x | |
| 205 | +| 2 | 26 | 106 | 4.1x | |
| 206 | +| 4 | 63 | 181 | 2.9x | |
| 207 | +| 8 | 94 | 450 | 4.8x | |
| 208 | +| 16 | 128 | 661 | 5.2x | |
| 209 | +| 32 | 135 | 748 | 5.5x | |
| 210 | +| 64 | 137 | 840 | 6.1x | |
| 211 | +| 128 | 147 | 833 | 5.7x | |
| 212 | +``` |
| 213 | + |
| 214 | +Clearly, the performance increases nearly 5x across the board. We also have a discrepancy in the dataset. Which one? |
| 215 | + |
| 216 | +A good exercise would be to factor in latency into the report in order to understand the impact, which there will be, as performance plateaus when you add more workloads – usually as a result of higher latency. But, how high? |
| 217 | + |
| 218 | +# More sequencers and examples! |
| 219 | + |
| 220 | +There’s also a `sequencer-cluster.sh` script that allows users to orchestrate load generation across multiple clusters attached to one or many storage systems to isolate problems with high concurrency. The possibilities are quite endless. |
| 221 | + |
| 222 | +You can learn more about multi-cluster scaling in [the GitHub repository](https://github.com/hpe-storage/benk#multi-cluster-testing). |
| 223 | + |
| 224 | +You'll also find more practical examples in [the GitHub repository](https://github.com/hpe-storage/benk/tree/main/examples) stemming from real world performance testing conducted by the HPE Hybrid Cloud solutions team. |
| 225 | + |
| 226 | +# Summary |
| 227 | + |
| 228 | +Hopefully, this blog post gets you started on ideas you’d like to build and use cases you want to explore. The possibilities are endless. Bear in mind that Benk is, by all means, provided as-is. In addition, to be fully transparent, this is a very early implementation that HPE chose to open source in order to better collaborate with customers and partners to isolate performance bottlenecks. For example, not all advertised features in `config.env` have been implemented yet and the CLI is not yet completed. |
| 229 | + |
| 230 | +HPE invites collaboration and accepts pull requests to Benk on GitHub. The [first issue](https://github.com/hpe-storage/benk/issues/2) discusses a solution on how to collapse initial configuration and reporting into a single intuitive Python CLI. |
| 231 | + |
| 232 | +Let us know what you’re building or have questions. The team behind the tool is available on HPE Developer Community Slack in the [#Kubernetes](https://hpedev.slack.com/archives/C81QZ4X62) channel. Sign up [here](https://developer.hpe.com/slack-signup) and sign in at [hpedev.slack.com](https://hpedev.slack.com). |
0 commit comments