diff --git a/execution/README.md b/execution/README.md index ca15111c0ad7000a200c0c50427a2c2aeb75e093..eb6ade9f2aab28b6d237e9622f22da0ea5998a50 100644 --- a/execution/README.md +++ b/execution/README.md @@ -225,7 +225,17 @@ Theodolite locally on your machine see the description below. see the [Configuration](#configuration) section below. Note, that you might uncomment the `serviceAccountName` line if RBAC is enabled on your cluster (see installation of [Theodolite RBAC](#Theodolite-RBAC)). -To start the execution of a benchmark run (with `<your-theodolite-yaml>` being your job definition): +To start the execution of a benchmark create a ConfigMap which containts all required Kubernetes resource files for the SUT and the load generator, a ConfigMap for the execution and a ConfigMap for the benchmark. + +```sh +kubectl create configmap app-resources-configmap --from-file=<folder-with-all-required-k8s-resources> +kubectl create configmap execution-configmap --from-file=<execution.yaml> +kubectl create configmap benchmark-configmap --from-file=<benchmark.yaml> +``` + +This will create three ConfigMaps. You can verify this via `kubectl get configmaps`. + +Start the Theodolite job (with `<your-theodolite-yaml>` being your job definition): ```sh kubectl create -f <your-theodolite-yaml> @@ -241,24 +251,7 @@ Kubernetes volume. ### Configuration -| Command line | Kubernetes | Description | -| -------------------- | ------------------- | ------------------------------------------------------------ | -| --uc | UC | **[Mandatory]** Stream processing use case to be benchmarked. Has to be one of `1`, `2`, `3` or `4`. | -| --loads | LOADS | **[Mandatory]** Values for the workload generator to be tested, should be sorted in ascending order. | -| --instances | INSTANCES | **[Mandatory]** Numbers of instances to be benchmarked, should be sorted in ascending order. | -| --duration | DURATION | Duration in minutes subexperiments should be executed for. *Default:* `5`. | -| --partitions | PARTITIONS | Number of partitions for Kafka topics. *Default:* `40`. | -| --cpu-limit | CPU_LIMIT | Kubernetes CPU limit for a single Pod. *Default:* `1000m`. | -| --memory-limit | MEMORY_LIMIT | Kubernetes memory limit for a single Pod. *Default:* `4Gi`. | -| --domain-restriction | DOMAIN_RESTRICTION | A flag that indiciates domain restriction should be used. *Default:* not set. For more details see Section [Domain Restriction](#domain-restriction). | -| --search-strategy | SEARCH_STRATEGY | The benchmarking search strategy. Can be set to `check-all`, `linear-search` or `binary-search`. *Default:* `check-all`. For more details see Section [Benchmarking Search Strategies](#benchmarking-search-strategies). | -| --reset | RESET | Resets the environment before each subexperiment. Useful if execution was aborted and just one experiment should be executed. | -| --reset-only | RESET_ONLY | Only resets the environment. Ignores all other parameters. Useful if execution was aborted and one want a clean state for new executions. | -| --namespace | NAMESPACE | Kubernetes namespace. *Default:* `default`. | -| --prometheus | PROMETHEUS_BASE_URL | Defines where to find the prometheus instance. *Default:* `http://localhost:9090` | -| --path | RESULT_PATH | A directory path for the results. Relative to the Execution folder. *Default:* `results` | -| --configurations | CONFIGURATIONS | Defines environment variables for the use cases and, thus, enables further configuration options. | -| --threshold | THRESHOLD | The threshold for the trend slop that the search strategies use to determine that a load could be handled. *Default:* `2000` | +Be sure, that the names of the configmap corresponds correctly to the specifications of the mounted `configmaps`, `volumes`, `mountPath`. In particular: The name of the execution file and the benchmark file must match the value of the corresponding environment variable. ### Domain Restriction diff --git a/execution/theodolite.yaml b/execution/theodolite.yaml index 06d14a0f589b2ac7a16ebaaae4d1490b840ea57b..ff8eecb312d052eab6f2e66a0bd57d8a983d38e1 100644 --- a/execution/theodolite.yaml +++ b/execution/theodolite.yaml @@ -5,47 +5,60 @@ metadata: spec: template: spec: - volumes: - - name: theodolite-pv-storage - persistentVolumeClaim: - claimName: theodolite-pv-claim + securityContext: + runAsUser: 0 # Set the permissions for write access to the volumes. containers: + - name: lag-analysis + image: ghcr.io/cau-se/theodolite-slo-checker-lag-trend:theodolite-kotlin-latest + ports: + - containerPort: 80 + name: analysis - name: theodolite - image: ghcr.io/cau-se/theodolite:latest - # imagePullPolicy: Never # Used to pull "own" local image + image: ghcr.io/cau-se/theodolite:theodolite-kotlin-latest + imagePullPolicy: Always env: - - name: UC # mandatory - value: "1" - - name: LOADS # mandatory - value: "100000, 200000" - - name: INSTANCES # mandatory - value: "1, 2, 3" - # - name: DURATION - # value: "5" - # - name: PARTITIONS - # value: "40" - # - name: DOMAIN_RESTRICTION - # value: "True" - # - name: SEARCH_STRATEGY - # value: "linear-search" - # - name: CPU_LIMIT - # value: "1000m" - # - name: MEMORY_LIMIT - # value: "4Gi" - - name: PROMETHEUS_BASE_URL - value: "http://prometheus-operated:9090" - # - name: NAMESPACE - # value: "default" - # - name: CONFIGURATIONS - # value: "COMMIT_INTERVAL_MS=100, NUM_STREAM_THREADS=1" - - name: RESULT_PATH - value: "results" - - name: PYTHONUNBUFFERED # Enable logs in Kubernetes - value: "1" + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + + # - name: MODE + # value: yaml-executor # Default is `yaml-executor` + - name: THEODOLITE_EXECUTION + value: /etc/execution/example-execution-yaml-resource.yaml # The name of this file must correspond to the filename of the execution, from which the config map is created. + - name: THEODOLITE_BENCHMARK + value: /etc/benchmark/example-benchmark-yaml-resource.yaml # The name of this file must correspond to the filename of the benchmark, from which the config map is created. + - name: THEODOLITE_APP_RESOURCES + value: /etc/app-resources + - name: RESULTS_FOLDER # Folder for saving results + value: results # Default is the pwd (/deployments) + # - name: CREATE_RESULTS_FOLDER # Specify whether the specified result folder should be created if it does not exist. + # value: "false" # Default is false. volumeMounts: - - mountPath: "/app/results" + - mountPath: "/deployments/results" # the mounted path must corresponds to the value of `RESULT_FOLDER`. name: theodolite-pv-storage + - mountPath: "/etc/app-resources" # must be corresponds to the value of `THEODOLITE_APP_RESOURCES`. + name: app-resources + - mountPath: "/etc/benchmark" # must be corresponds to the value of `THEODOLITE_BENCHMARK`. + name: benchmark + - mountPath: "/etc/execution" # must be corresponds to the value of `THEODOLITE_EXECUTION`. + name: execution restartPolicy: Never # Uncomment if RBAC is enabled and configured - # serviceAccountName: theodolite - backoffLimit: 4 + serviceAccountName: theodolite + # Multiple volumes are needed to provide the corresponding files. + # The names must correspond to the created configmaps and the volumeMounts. + volumes: + - name: theodolite-pv-storage + persistentVolumeClaim: + claimName: theodolite-pv-claim + - name: app-resources + configMap: + name: app-resources-configmap + - name: benchmark + configMap: + name: benchmark-configmap + - name: execution + configMap: + name: execution-configmap + backoffLimit: 4 \ No newline at end of file diff --git a/theodolite-quarkus/src/main/docker/Dockerfile.native b/theodolite-quarkus/src/main/docker/Dockerfile.native index 37a5a4bd8472a358194dbb14a5fce61df94804d3..29836a7148b573c3051c33341718b06008fa07e2 100644 --- a/theodolite-quarkus/src/main/docker/Dockerfile.native +++ b/theodolite-quarkus/src/main/docker/Dockerfile.native @@ -15,12 +15,12 @@ # ### FROM registry.access.redhat.com/ubi8/ubi-minimal:8.3 -WORKDIR /work/ -RUN chown 1001 /work \ - && chmod "g+rwX" /work \ - && chown 1001:root /work -COPY --chown=1001:root build/*-runner /work/application -COPY config/ /work/config/ +WORKDIR /deployments +RUN chown 1001 /deployments \ + && chmod "g+rwX" /deployments \ + && chown 1001:root /deployments +COPY --chown=1001:root build/*-runner /deployments/application +COPY config/ /deployments/config/ EXPOSE 8080 USER 1001 diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/AnalysisExecutor.kt b/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/AnalysisExecutor.kt index f15ec808ae8fdcd6ba31f500f819a08718db774b..c5d691eb8f1437726bcce22500ad995bc1b6e4da 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/AnalysisExecutor.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/AnalysisExecutor.kt @@ -44,8 +44,13 @@ class AnalysisExecutor( query = "sum by(group)(kafka_consumergroup_group_lag >= 0)" ) + var resultsFolder: String = System.getenv("RESULTS_FOLDER") + if (resultsFolder.isNotEmpty()){ + resultsFolder += "/" + } + CsvExporter().toCsv( - name = "exp${executionId}_${load.get()}_${res.get()}_${slo.sloType.toSlug()}", + name = "${resultsFolder}exp${executionId}_${load.get()}_${res.get()}_${slo.sloType.toSlug()}", prom = prometheusData ) val sloChecker = SloCheckerFactory().create( diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/Shutdown.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/Shutdown.kt index f5c0251c298dea0801dc601c1d2b790de465459e..a50a38e79b52a72fa68eb9eda70cf1072f80df74 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/Shutdown.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/Shutdown.kt @@ -5,6 +5,7 @@ import theodolite.benchmark.BenchmarkExecution import theodolite.benchmark.KubernetesBenchmark import theodolite.util.LoadDimension import theodolite.util.Resource +import java.lang.Exception private val logger = KotlinLogging.logger {} @@ -23,6 +24,7 @@ class Shutdown(private val benchmarkExecution: BenchmarkExecution, private val b */ override fun run() { // Build Configuration to teardown + try { logger.info { "Received shutdown signal -> Shutting down" } val deployment = benchmark.buildDeployment( @@ -30,8 +32,13 @@ class Shutdown(private val benchmarkExecution: BenchmarkExecution, private val b res = Resource(0, emptyList()), configurationOverrides = benchmarkExecution.configOverrides ) + deployment.teardown() + } catch (e: Exception) { + logger.warn { "Could not delete all specified resources from Kubernetes. " + + "This could be the case, if not all resources are deployed and running." } + + } logger.info { "Teardown everything deployed" } - deployment.teardown() logger.info { "Teardown completed" } } } diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteExecutor.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteExecutor.kt index c2b8cc7d2ebc60b7cbb8328dfe1d7830ec5b5aff..5fd2eedecdc42b575d69760025a31f45eb71fec0 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteExecutor.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteExecutor.kt @@ -1,6 +1,7 @@ package theodolite.execution import com.google.gson.GsonBuilder +import mu.KotlinLogging import theodolite.benchmark.BenchmarkExecution import theodolite.benchmark.KubernetesBenchmark import theodolite.patcher.PatcherDefinitionFactory @@ -10,9 +11,17 @@ import theodolite.util.Config import theodolite.util.LoadDimension import theodolite.util.Resource import theodolite.util.Results +import java.io.File import java.io.PrintWriter +import java.lang.IllegalArgumentException +import java.lang.Thread.sleep +import java.nio.file.Files +import java.nio.file.Path import java.time.Duration + +private val logger = KotlinLogging.logger {} + /** * The Theodolite executor runs all the experiments defined with the given execution and benchmark configuration. * @@ -92,13 +101,34 @@ class TheodoliteExecutor( return this.kubernetesBenchmark } + private fun getResultFolderString(): String { + var resultsFolder: String = System.getenv("RESULTS_FOLDER") ?: "" + val createResultsFolder = System.getenv("CREATE_RESULTS_FOLDER") ?: "false" + + if (resultsFolder != ""){ + logger.info { "RESULT_FOLDER: $resultsFolder" } + val directory = File(resultsFolder) + if (!directory.exists()) { + logger.error { "Folder $resultsFolder does not exist" } + if (createResultsFolder.toBoolean()) { + directory.mkdirs() + } else { + throw IllegalArgumentException("Result folder not found") + } + } + resultsFolder += "/" + } + return resultsFolder + } + /** * Run all experiments which are specified in the corresponding * execution and benchmark objects. */ fun run() { - storeAsFile(this.config, "${this.config.executionId}-execution-configuration") - storeAsFile(kubernetesBenchmark, "${this.config.executionId}-benchmark-configuration") + val resultsFolder = getResultFolderString() + storeAsFile(this.config, "$resultsFolder${this.config.executionId}-execution-configuration") + storeAsFile(kubernetesBenchmark, "$resultsFolder/${this.config.executionId}-benchmark-configuration") val config = buildConfig() // execute benchmarks for each load @@ -107,7 +137,7 @@ class TheodoliteExecutor( config.compositeStrategy.findSuitableResource(load, config.resources) } } - storeAsFile(config.compositeStrategy.benchmarkExecutor.results, "${this.config.executionId}-result") + storeAsFile(config.compositeStrategy.benchmarkExecutor.results, "$resultsFolder${this.config.executionId}-result") } private fun <T> storeAsFile(saveObject: T, filename: String) {