Skip to content
Snippets Groups Projects
Commit 8856acd4 authored by Benedikt Wetzel's avatar Benedikt Wetzel
Browse files

Merge upstream master

parents 45348ebe c7f7ea59
No related branches found
No related tags found
4 merge requests!159Re-implementation of Theodolite with Kotlin/Quarkus,!157Update Graal Image in CI pipeline,!137Allow multiple repititions of an experiment,!83WIP: Re-implementation of Theodolite with Kotlin/Quarkus
Showing
with 230 additions and 133 deletions
......@@ -51,7 +51,7 @@ build-benchmarks:
- "theodolite-benchmarks/build/libs/*.jar"
- "theodolite-benchmarks/*/build/libs/*.jar"
- "theodolite-benchmarks/*/build/distributions/*.tar"
expire_in: 1 day
expire_in: 6 hours
test-benchmarks:
stage: test
......@@ -234,7 +234,7 @@ build-theodolite-jvm:
paths:
- "theodolite-quarkus/build/lib/*"
- "theodolite-quarkus/build/*-runner.jar"
expire_in: 1 day
expire_in: 6 hours
build-theodolite-native:
stage: build
......@@ -242,17 +242,18 @@ build-theodolite-native:
script:
- gu install native-image # TODO move to image
- ./gradlew --build-cache assemble -Dquarkus.package.type=native
when: manual
artifacts:
paths:
- "theodolite-quarkus/build/*-runner"
expire_in: 1 day
expire_in: 6 hours
test-theodolite:
stage: test
extends: .theodolite
needs:
- build-theodolite-jvm
- build-theodolite-native
#- build-theodolite-native
script: ./gradlew test --stacktrace
# Disabled for now
......@@ -279,12 +280,13 @@ deploy-theodolite:
- .theodolite
- .dind
needs:
- build-theodolite-native
#- build-theodolite-native
- build-theodolite-jvm
- test-theodolite
script:
- DOCKER_TAG_NAME=$(echo $CI_COMMIT_REF_SLUG- | sed 's/^master-$//')
- docker build -f src/main/docker/Dockerfile.native -t theodolite .
#- docker build -f src/main/docker/Dockerfile.jvm -t theodolite .
#- docker build -f src/main/docker/Dockerfile.native -t theodolite .
- docker build -f src/main/docker/Dockerfile.jvm -t theodolite .
- "[ ! $CI_COMMIT_TAG ] && docker tag theodolite $CR_HOST/$CR_ORG/theodolite:${DOCKER_TAG_NAME}latest"
- "[ ! $CI_COMMIT_TAG ] && docker tag theodolite $CR_HOST/$CR_ORG/theodolite:$DOCKER_TAG_NAME$CI_COMMIT_SHORT_SHA"
- "[ $CI_COMMIT_TAG ] && docker tag theodolite $CR_HOST/$CR_ORG/theodolite:$CI_COMMIT_TAG"
......@@ -309,6 +311,7 @@ deploy-slo-checker-lag-trend:
stage: deploy
extends:
- .dind
needs: []
script:
- DOCKER_TAG_NAME=$(echo $CI_COMMIT_REF_SLUG- | sed 's/^master-$//')
- docker build --pull -t theodolite-slo-checker-lag-trend slope-evaluator
......@@ -335,6 +338,7 @@ deploy-random-scheduler:
stage: deploy
extends:
- .dind
needs: []
script:
- DOCKER_TAG_NAME=$(echo $CI_COMMIT_REF_SLUG- | sed 's/^master-$//')
- docker build --pull -t theodolite-random-scheduler execution/infrastructure/random-scheduler
......
......@@ -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
......
......@@ -6,11 +6,9 @@ Install the chart via:
```sh
helm dependencies update .
helm install my-confluent .
helm install theodolite .
```
**Please note: Theodolite currently uses hard-coded URLs, to connect to Kafka and Zookeeper. For that reason, the name of this chart must be `my-confluent`.** We will change this behavior soon.
This chart installs requirements to execute benchmarks with Theodolite.
Dependencies and subcharts:
......@@ -27,7 +25,7 @@ Dependencies and subcharts:
Test the installation:
```sh
helm test <release-name>
helm test theodolite
```
Our test files are located [here](templates/../../theodolite-chart/templates/tests). Many subcharts have their own tests, these are also executed and are placed in the respective /templates folders.
......@@ -46,18 +44,18 @@ helm install theodolite . -f preconfigs/one-broker-values.yaml
## Uninstall this Chart
To uninstall/delete the `my-release` deployment:
To uninstall/delete the `theodolite` deployment:
```sh
helm delete my-release
helm delete theodolite
```
This command does not remove the CRDs which are created by this chart. Remove them manually with:
```sh
# CRDs from Theodolite
kubectl delete crd execution
kubectl delete crd benchmark
kubectl delete crd executions.theodolite.com
kubectl delete crd benchmarks.theodolite.com
# CRDs from Prometheus operator (see https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack#uninstall-chart)
kubectl delete crd alertmanagerconfigs.monitoring.coreos.com
kubectl delete crd alertmanagers.monitoring.coreos.com
......
......@@ -42,7 +42,7 @@ rules:
- delete
- list
- create
{{- if .Values.operator.enabled -}}
{{- if .Values.operator.enabled }}
- apiGroups:
- theodolite.com
resources:
......
......@@ -23,9 +23,19 @@ spec:
value: {{ .Release.Namespace }}
- name: MODE
value: operator
- name: THEODOLITE_APP_RESOURCES
value: "./benchmark-resources"
volumeMounts:
- name: benchmark-resources
mountPath: /work/benchmark-resources
- name: lag-analysis
image: ghcr.io/cau-se/theodolite-slo-checker-lag-trend:theodolite-kotlin-latest
ports:
- containerPort: 80
name: analysis
volumes:
- name: benchmark-resources
configMap:
name: benchmark-resources
optional: true
{{- end }}
......@@ -24,6 +24,9 @@ grafana:
# Administrator credentials when not using an existing secret (see below)
adminUser: admin
adminPassword: admin
grafana.ini:
users:
default_theme: light
## Sidecars that collect the configmaps with specified label and stores the included files them into the respective folders
## Requires at least Grafana 5 to work and can't be used together with parameters dashboardProviders, datasources and dashboards
sidecar:
......@@ -145,8 +148,8 @@ kafka-lag-exporter:
enabled: true
nodeSelector: {}
clusters:
- name: "my-confluent-cp-kafka"
bootstrapBrokers: "my-confluent-cp-kafka:9092"
- name: "theodolite-cp-kafka"
bootstrapBrokers: "theodolite-cp-kafka:9092"
## The interval between refreshing metrics
pollIntervalSeconds: 15
......
......@@ -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
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
......@@ -21,9 +21,9 @@ spec:
name: jmx
env:
- name: KAFKA_BOOTSTRAP_SERVERS
value: "my-confluent-cp-kafka:9092"
value: "theodolite-cp-kafka:9092"
- name: SCHEMA_REGISTRY_URL
value: "http://my-confluent-cp-schema-registry:8081"
value: "http://theodolite-cp-schema-registry:8081"
- name: JAVA_OPTS
value: "-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=5555"
- name: COMMIT_INTERVAL_MS # Set as default for the applications
......
......@@ -32,6 +32,6 @@ spec:
- name: KUBERNETES_DNS_NAME
value: "titan-ccp-load-generator.$(KUBERNETES_NAMESPACE).svc.cluster.local"
- name: KAFKA_BOOTSTRAP_SERVERS
value: "my-confluent-cp-kafka:9092"
value: "theodolite-cp-kafka:9092"
- name: SCHEMA_REGISTRY_URL
value: "http://my-confluent-cp-schema-registry:8081"
value: "http://theodolite-cp-schema-registry:8081"
......@@ -23,4 +23,37 @@ Run the Docker image:
## Configuration
You can set the `HOST` and the `PORT` (and a lot of more parameters) via environment variables. Default is `0.0.0.0:80`.
For more information see [here](https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker#advanced-usage).
For more information see the [Gunicorn/FastAPI Docker docs](https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker#advanced-usage).
## API Documentation
The running webserver provides a REST API with the following route:
* /evaluate-slope
* Method: POST
* Body:
* total_lag
* threshold
* warmup
The body of the request must be a JSON string that satisfies the following conditions:
* **total_lag**: This property is based on the [Range Vector type](https://www.prometheus.io/docs/prometheus/latest/querying/api/#range-vectors) from Prometheus and must have the following JSON structure:
```
{
"metric": {
"group": "<label_value>"
},
"values": [
[
<unix_timestamp>,
"<sample_value>"
]
]
}
```
* The `<label_value>` provided in "metric.group" must be equal to the id of the Kafka consumer group.
* The `<unix_timestamp>` provided as the first element of each element in the "values" array must be the timestamp of the measurement value in seconds (with optional decimal precision)
* The `<sample_value>` must be the measurement value as string.
* **threshold**: Must be an unsigned integer that specifies the threshold for the SLO evaluation. The SLO is considered fulfilled, if the result value is below the threshold. If the result value is equal or above the threshold, the SLO is considered not fulfilled.
* **warmup**: Specifieds the warmup time in seconds that are ignored for evaluating the SLO.
\ No newline at end of file
......@@ -18,7 +18,7 @@ if os.getenv('LOG_LEVEL') == 'INFO':
elif os.getenv('LOG_LEVEL') == 'WARNING':
logger.setLevel(logging.WARNING)
elif os.getenv('LOG_LEVEL') == 'DEBUG':
logger.setLevel((logging.DEBUG))
logger.setLevel(logging.DEBUG)
def execute(results, threshold, warmup):
d = []
......@@ -30,18 +30,18 @@ def execute(results, threshold, warmup):
df = pd.DataFrame(d)
logger.info(df)
logger.info("Calculating trend slope with warmup of %s seconds for data frame:\n %s", warmup, df)
try:
trend_slope = trend_slope_computer.compute(df, warmup)
except Exception as e:
err_msg = 'Computing trend slope failed'
err_msg = 'Computing trend slope failed.'
logger.exception(err_msg)
logger.error('Mark this subexperiment as not successful and continue benchmark')
logger.error('Mark this subexperiment as not successful and continue benchmark.')
return False
logger.info("Trend Slope: %s", trend_slope)
return trend_slope < threshold
result = trend_slope < threshold
logger.info("Computed lag trend slope is '%s'. Result is: %s", trend_slope, result)
return result
@app.post("/evaluate-slope",response_model=bool)
async def evaluate_slope(request: Request):
......
......@@ -2,13 +2,12 @@ from sklearn.linear_model import LinearRegression
import pandas as pd
import os
def compute(x, warmup_sec):
input = x
input['sec_start'] = input.loc[0:, 'timestamp'] - input.iloc[0]['timestamp']
regress = input.loc[input['sec_start'] >= warmup_sec] # Warm-Up
def compute(data, warmup_sec):
data['sec_start'] = data.loc[0:, 'timestamp'] - data.iloc[0]['timestamp']
regress = data.loc[data['sec_start'] >= warmup_sec] # Warm-Up
X = regress.iloc[:, 2].values.reshape(-1, 1) # values converts it into a numpy array
Y = regress.iloc[:, 3].values.reshape(-1, 1) # -1 means that calculate the dimension of rows, but have 1 column
X = regress.iloc[:, 1].values.reshape(-1, 1) # values converts it into a numpy array
Y = regress.iloc[:, 2].values.reshape(-1, 1) # -1 means that calculate the dimension of rows, but have 1 column
linear_regressor = LinearRegression() # create object for the class
linear_regressor.fit(X, Y) # perform linear regression
Y_pred = linear_regressor.predict(X) # make predictions
......
......@@ -6,6 +6,23 @@ plugins {
applicationDefaultJvmArgs = ["-Dlog4j.configuration=log4j.properties"]
run.classpath = sourceSets.main.runtimeClasspath
jar {
manifest {
attributes 'Built-By': System.getProperty('user.name'),
'Build-Jdk': System.getProperty('java.version')
}
}
shadowJar {
configurations = [project.configurations.compile]
zip64 true
}
tasks.distZip.enabled = false
ext {
flinkVersion = '1.12.2'
scalaBinaryVersion = '2.12'
......@@ -48,17 +65,3 @@ dependencies {
// Use JUnit test framework
testImplementation 'junit:junit:4.12'
}
run.classpath = sourceSets.main.runtimeClasspath
jar {
manifest {
attributes 'Built-By': System.getProperty('user.name'),
'Build-Jdk': System.getProperty('java.version')
}
}
shadowJar {
configurations = [project.configurations.compile]
zip64 true
}
......@@ -6,6 +6,8 @@ plugins {
id 'application'
}
tasks.distZip.enabled = false
repositories {
jcenter()
maven {
......
......@@ -6,6 +6,8 @@ plugins {
id 'application'
}
tasks.distZip.enabled = false
repositories {
jcenter()
maven {
......
......@@ -18,20 +18,20 @@ dependencies {
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
implementation 'io.quarkus:quarkus-arc'
implementation 'io.quarkus:quarkus-resteasy'
testImplementation 'io.quarkus:quarkus-junit5'
testImplementation 'io.rest-assured:rest-assured'
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'org.slf4j:slf4j-simple:1.7.29'
implementation 'io.github.microutils:kotlin-logging:1.12.0'
implementation 'io.fabric8:kubernetes-client:5.0.0-alpha-2'
implementation 'io.quarkus:quarkus-kubernetes-client'
implementation 'org.apache.kafka:kafka-clients:2.7.0'
implementation 'khttp:khttp:1.0.0'
testImplementation 'io.quarkus:quarkus-junit5'
testImplementation 'io.rest-assured:rest-assured'
}
group 'theodolite'
version '1.0.0-SNAPSHOT'
version '0.5.0-SNAPSHOT'
java {
sourceCompatibility = JavaVersion.VERSION_11
......
......@@ -14,4 +14,4 @@ spec:
targetPort: 80
protocol: TCP
- name: metrics
port: 9980
port: 5556
......@@ -19,9 +19,13 @@ loadTypes:
resource: "uc1-load-generator-deployment.yaml"
container: "workload-generator"
variableName: "NUM_SENSORS"
- type: "NumSensorsLoadGeneratorReplicaPatcher"
resource: "uc1-load-generator-deployment.yaml"
kafkaConfig:
bootstrapServer: "localhost:31290" # "my-confluent-cp-kafka:9092"
bootstrapServer: "theodolite-cp-kafka:9092"
topics:
- name: "input"
numPartitions: 40
replicationFactor: 1
- name: "theodolite-.*"
removeOnly: True
\ No newline at end of file
......@@ -2,36 +2,34 @@ name: example-execution
benchmark: "uc1-kstreams"
load:
loadType: "NumSensors"
loadValues:
- 50000
loadValues: [25000, 50000, 75000, 100000, 125000, 150000]
resources:
resourceType: "Instances"
resourceValues:
- 1
resourceValues: [1, 2, 3, 4, 5]
slos:
- sloType: "lag trend"
threshold: 1000
threshold: 2000
prometheusUrl: "http://prometheus-operated:9090"
externalSloUrl: "http://localhost:80/evaluate-slope"
offset: 0
warmup: 0
warmup: 60 # in seconds
execution:
strategy: "LinearSearch"
duration: 60
duration: 300 # in seconds
repetitions: 1
restrictions:
- "LowerBound"
configOverrides:
- patcher:
type: "NodeSelectorPatcher"
resource: "uc1-load-generator-deployment.yaml"
variableName: "env"
value: "prod"
- patcher:
type: "NodeSelectorPatcher"
resource: "uc1-kstreams-deployment.yaml"
variableName: "env"
value: "prod"
configOverrides: []
# - patcher:
# type: "NodeSelectorPatcher"
# resource: "uc1-load-generator-deployment.yaml"
# variableName: "env"
# value: "prod"
# - patcher:
# type: "NodeSelectorPatcher"
# resource: "uc1-kstreams-deployment.yaml"
# variableName: "env"
# value: "prod"
# - patcher:
# type: "ResourceLimitPatcher"
# resource: "uc1-kstreams-deployment.yaml"
......
apiVersion: theodolite.com/v1alpha1
kind: benchmark
metadata:
name: uc1-kstreams
#name: "uc1-kstreams"
appResource:
- "uc1-kstreams-deployment.yaml"
- "aggregation-service.yaml"
- "jmx-configmap.yaml"
- "uc1-service-monitor.yaml"
loadGenResource:
- "uc1-load-generator-deployment.yaml"
- "uc1-load-generator-service.yaml"
resourceTypes:
- typeName: "Instances"
patchers:
- type: "ReplicaPatcher"
resource: "uc1-kstreams-deployment.yaml"
loadTypes:
- typeName: "NumSensors"
patchers:
- type: "EnvVarPatcher"
resource: "uc1-load-generator-deployment.yaml"
container: "workload-generator"
variableName: "NUM_SENSORS"
- type: "NumSensorsLoadGeneratorReplicaPatcher"
resource: "uc1-load-generator-deployment.yaml"
kafkaConfig:
bootstrapServer: "theodolite-cp-kafka:9092"
topics:
- name: "input"
numPartitions: 40
replicationFactor: 1
- name: "theodolite-.*"
removeOnly: True
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment