Skip to content
Snippets Groups Projects
Commit 96b94d81 authored by Sören Henning's avatar Sören Henning
Browse files

Merge branch '109-implement-kotlin-prototype' into 'theodolite-kotlin'

Resolve "Implement Quarkus/Kotlin protype"

See merge request !78
parents 51d80701 1d42d7f9
Branches beam-dataflow
No related tags found
5 merge requests!159Re-implementation of Theodolite with Kotlin/Quarkus,!157Update Graal Image in CI pipeline,!83WIP: Re-implementation of Theodolite with Kotlin/Quarkus,!82Add logger,!78Resolve "Implement Quarkus/Kotlin protype"
Pipeline #1778 skipped
Showing
with 691 additions and 242 deletions
package theodolite.k8s
import mu.KotlinLogging
import org.apache.kafka.clients.admin.AdminClient
import org.apache.kafka.clients.admin.AdminClientConfig
import org.apache.kafka.clients.admin.ListTopicsResult
import org.apache.kafka.clients.admin.NewTopic
private val logger = KotlinLogging.logger {}
/**
* Manages the topics related tasks
* @param bootstrapServers Ip of the kafka server
*/
class TopicManager(bootstrapServers: String) {
private val props = hashMapOf<String, Any>(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG to bootstrapServers)
lateinit var kafkaAdmin: AdminClient
init {
try {
kafkaAdmin = AdminClient.create(props)
} catch (e: Exception) {
logger.error { e.toString() }
}
}
/**
* Creates topics.
* @param topics Map that holds a numPartition for each topic it should create
* @param replicationFactor
*/
fun createTopics(topics: Map<String, Int>, replicationFactor: Short) {
val newTopics = mutableSetOf<NewTopic>()
for (i in topics) {
val tops = NewTopic(i.key, i.value, replicationFactor)
newTopics.add(tops)
}
kafkaAdmin.createTopics(newTopics)
logger.info { "Topics created" }
}
fun createTopics(topics: List<String>, numPartitions: Int, replicationFactor: Short) {
val newTopics = mutableSetOf<NewTopic>()
for (i in topics) {
val tops = NewTopic(i, numPartitions, replicationFactor)
newTopics.add(tops)
}
kafkaAdmin.createTopics(newTopics)
logger.info { "Creation of $topics started" }
}
/**
* Deletes topics.
* @param topics
*/
fun deleteTopics(topics: List<String>) {
val result = kafkaAdmin.deleteTopics(topics)
try {
result.all().get()
} catch (ex: Exception) {
logger.error { ex.toString() }
}
logger.info { "Topics deleted" }
}
fun getTopics(): ListTopicsResult? {
return kafkaAdmin.listTopics()
}
}
package theodolite.k8s
import io.fabric8.kubernetes.api.model.ConfigMap
import io.fabric8.kubernetes.api.model.Service
import io.fabric8.kubernetes.api.model.apps.Deployment
import io.fabric8.kubernetes.client.DefaultKubernetesClient
import io.fabric8.kubernetes.client.NamespacedKubernetesClient
import mu.KotlinLogging
import theodolite.util.AbstractBenchmark
import theodolite.util.LoadDimension
import theodolite.util.Resource
private val logger = KotlinLogging.logger {}
class UC1Benchmark(config: Config) : AbstractBenchmark(config) {
private val workloadGeneratorStateCleaner: WorkloadGeneratorStateCleaner
private val topicManager: TopicManager
// TODO("service monitor")
private val kubernetesClient: NamespacedKubernetesClient
private val yamlLoader: YamlLoader
private val deploymentManager: DeploymentManager
private val serviceManager: ServiceManager
private val configMapManager: ConfigMapManager
private var ucDeployment: Deployment
private var ucService: Service
private var wgDeployment: Deployment
private var configMap: ConfigMap
init {
this.workloadGeneratorStateCleaner =
WorkloadGeneratorStateCleaner(this.config.externalZookeeperConnectionString, path = "/workload-generation")
this.topicManager = TopicManager(this.config.externalKafkaConnectionString)
this.kubernetesClient = DefaultKubernetesClient().inNamespace("default")
this.yamlLoader = YamlLoader(this.kubernetesClient)
this.deploymentManager = DeploymentManager(this.kubernetesClient)
this.serviceManager = ServiceManager(this.kubernetesClient)
this.configMapManager = ConfigMapManager(this.kubernetesClient)
ucDeployment = this.yamlLoader.loadDeployment(this.config.ucDeploymentPath)
ucService = this.yamlLoader.loadService(this.config.ucServicePath)
wgDeployment = this.yamlLoader.loadDeployment(this.config.wgDeploymentPath)
configMap = this.yamlLoader.loadConfigmap(this.config.configMapPath)
}
override fun clearClusterEnvironment() {
this.workloadGeneratorStateCleaner.deleteAll()
this.topicManager.deleteTopics(this.config.kafkaTopics)
this.deploymentManager.delete(this.ucDeployment)
this.serviceManager.delete(this.ucService)
this.deploymentManager.delete(this.wgDeployment)
}
override fun initializeClusterEnvironment() {
this.topicManager.createTopics(
this.config.kafkaTopics,
this.config.kafkaPartition,
this.config.kafkaReplication
)
}
override fun startSUT(resources: Resource) {
this.deploymentManager.setImageName(ucDeployment, "uc-application", this.config.ucImageURL)
// set environment variables
val environmentVariables: MutableMap<String, String> = mutableMapOf()
environmentVariables.put("KAFKA_BOOTSTRAP_SERVERS", this.config.clusterKafkaConnectionString)
environmentVariables.put("SCHEMA_REGISTRY_URL", this.config.schemaRegistryConnectionString)
// setup deployment
this.deploymentManager.setReplica(ucDeployment, resources.get())
this.deploymentManager.setWorkloadEnv(ucDeployment, "uc-application", environmentVariables)
// create kubernetes resources
this.deploymentManager.deploy(ucDeployment)
this.serviceManager.deploy(ucService)
this.configMapManager.deploy(configMap)
}
override fun startWorkloadGenerator(load: LoadDimension) {
this.deploymentManager.setImageName(wgDeployment, "workload-generator", this.config.wgImageURL)
// TODO ("calculate number of required instances")
val requiredInstances = 1
val environmentVariables: MutableMap<String, String> = mutableMapOf()
environmentVariables.put("KAFKA_BOOTSTRAP_SERVERS", this.config.clusterKafkaConnectionString)
environmentVariables.put("ZK_HOST", this.config.clusterZookeeperConnectionString.split(":")[0])
environmentVariables.put("ZK_PORT", this.config.clusterZookeeperConnectionString.split(":")[1])
environmentVariables["NUM_SENSORS"] = load.get().toString()
environmentVariables["INSTANCES"] = requiredInstances.toString()
this.deploymentManager.setWorkloadEnv(this.wgDeployment, "workload-generator", environmentVariables)
this.deploymentManager.deploy(this.wgDeployment)
}
}
package theodolite.k8s
import mu.KotlinLogging
import org.apache.zookeeper.KeeperException
import org.apache.zookeeper.WatchedEvent
import org.apache.zookeeper.Watcher
import org.apache.zookeeper.ZooKeeper
import java.time.Duration
private val logger = KotlinLogging.logger {}
/**
* Resets the workloadgenerator states in zookeper (and potentially watches for Zookeper events)
*
* @param ip of zookeeper
* @param path path of the zookeeper node
*/
class WorkloadGeneratorStateCleaner(ip: String, val path: String) {
private val timeout: Duration = Duration.ofMillis(500)
private val retryAfter: Duration = Duration.ofSeconds(5)
lateinit var zookeeperClient: ZooKeeper
init {
try {
val watcher: Watcher = ZookeeperWatcher() // defined below
zookeeperClient = ZooKeeper(ip, timeout.toMillis().toInt(), watcher)
} catch (e: Exception) {
logger.error { e.toString() }
}
}
fun deleteAll() {
deleteRecusiveAll(this.path)
logger.info { "ZooKeeper reset was successful" }
}
/**
* Deletes a Zookeeper node and its children with the corresponding path.
*/
private fun deleteRecusiveAll(nodePath: String) {
while (true) {
var children: List<String>
try {
children = zookeeperClient.getChildren(nodePath, true)
} catch (e: KeeperException.NoNodeException) {
break;
}
// recursivly delete all children nodes
for (s: String in children) {
try {
deleteRecusiveAll("$nodePath/$s")
} catch (ex: Exception) {
logger.info { "$ex" }
}
}
// delete main node
try {
zookeeperClient.delete(nodePath, -1)
break;
} catch (ex: Exception) {
// no instance of node found
if (ex is KeeperException.NoNodeException) {
break;
} else {
logger.error { ex.toString() }
}
}
Thread.sleep(retryAfter.toMillis())
logger.info { "ZooKeeper reset was not successful. Retrying in 5s" }
}
}
/**
* Currently empty, could be used to watch(and react) on certain zookeeper events
*/
private class ZookeeperWatcher : Watcher {
override fun process(event: WatchedEvent) {}
}
}
package theodolite.k8s
import io.fabric8.kubernetes.api.model.ConfigMap
import io.fabric8.kubernetes.api.model.Service
import io.fabric8.kubernetes.api.model.apiextensions.v1beta1.CustomResourceDefinition
import io.fabric8.kubernetes.api.model.apps.Deployment
import io.fabric8.kubernetes.client.NamespacedKubernetesClient
import mu.KotlinLogging
private val logger = KotlinLogging.logger {}
class YamlLoader(private val client: NamespacedKubernetesClient) {
/**
* Parses a Service from a servive yaml
* @param path of the yaml file
* @return service from fabric8
*/
fun loadService(path: String): Service {
return loadGenericRessource(path) { x: String -> client.services().load(x).get() }
}
/**
* Parses a Service from a servive yaml
* @param path of the yaml file
* @return service from fabric8
*/
fun loadServiceMonitor(path: String): CustomResourceDefinition {
return loadGenericRessource(path) { x: String -> client.customResourceDefinitions().load(x).get() }
}
/**
* Parses a Deployment from a Deployment yaml
* @param path of the yaml file
* @return Deployment from fabric8
*/
fun loadDeployment(path: String): Deployment {
return loadGenericRessource(path) { x: String -> client.apps().deployments().load(x).get() }
}
/**
* Parses a ConfigMap from a ConfigMap yaml
* @param path of the yaml file
* @return ConfigMap from fabric8
*/
fun loadConfigmap(path: String): ConfigMap {
return loadGenericRessource(path) { x: String -> client.configMaps().load(x).get() }
}
/**
* Generic helper function to load a resource.
* @param path of the resource
* @param f fuction that shall be applied to the resource.
*/
private fun <T> loadGenericRessource(path: String, f: (String) -> T): T {
var resource: T? = null
try {
resource = f(path)
} catch (e: Exception) {
logger.info("You potentially misspelled the path: $path")
logger.info("$e")
}
if (resource == null) {
throw IllegalArgumentException("The Resource at path: $path could not be loaded")
}
return resource
}
}
package theodolite.strategies.restriction
import theodolite.util.Results
import theodolite.util.LoadDimension
import theodolite.util.Resource
/**
* The Lower Bound Restriction sets the lower bound of the resources to be examined to the value
* needed to successfully execute the next smaller load.
*
* @param results Result object used as a basis to restrict the resources.
*/
class LowerBoundRestriction(results: Results) : RestrictionStrategy(results) {
override fun next(load: LoadDimension, resources: List<Resource>): List<Resource> {
val maxLoad: LoadDimension? = this.results.getMaxBenchmarkedLoad(load)
var lowerBound: Resource? = this.results.getMinRequiredInstances(maxLoad)
if(lowerBound == null) {
lowerBound = resources.get(0)
}
return resources.filter{x -> x.get() >= lowerBound.get()}
}
}
\ No newline at end of file
package theodolite.strategies.restriction
import theodolite.util.Results
import theodolite.util.LoadDimension
import theodolite.util.Resource
/**
* A "Restriction Strategy" restricts a list of resources based on the current results of all previously performed benchmarks.
*/
abstract class RestrictionStrategy(val results: Results) {
/**
* Next Restrict the given resource list for the given load based on the result object.
*
* @param load Load dimension for which a subset of resources are required.
* @param resources List of resources to be restricted.
* @return Returns a list containing only elements that have not been filtered out by the restriction (possibly empty).
*/
public abstract fun next(load: LoadDimension, resources: List<Resource>): List<Resource>
}
\ No newline at end of file
package theodolite.strategies.searchstrategy
import theodolite.execution.BenchmarkExecutor
import theodolite.util.LoadDimension
import theodolite.util.Resource
import java.lang.IllegalArgumentException
/**
* Search for the smallest suitable resource with binary search.
*
* @param benchmarkExecutor Benchmark executor which runs the individual benchmarks.
*/
class BinarySearch(benchmarkExecutor: BenchmarkExecutor) : SearchStrategy(benchmarkExecutor) {
override fun findSuitableResource(load: LoadDimension, resources: List<Resource>): Resource? {
val result = binarySearch(load, resources, 0, resources.size - 1)
if( result == -1 ) {
return null;
}
return resources[result]
}
private fun binarySearch (load: LoadDimension, resources: List<Resource>, lower: Int, upper: Int): Int {
if (lower > upper) {
throw IllegalArgumentException()
}
// special case: length == 1 or 2
if (lower == upper) {
if (this.benchmarkExecutor.runExperiment(load, resources[lower])) return lower
else {
if (lower + 1 == resources.size) return - 1
return lower + 1
}
} else {
// apply binary search for a list with length > 2 and adjust upper and lower depending on the result for `resources[mid]`
val mid = (upper + lower) / 2
if (this.benchmarkExecutor.runExperiment(load, resources[mid])) {
if (mid == lower) {
return lower
}
return binarySearch(load, resources, lower, mid - 1 )
} else {
return binarySearch(load, resources, mid + 1 , upper)
}
}
}
}
\ No newline at end of file
package theodolite.strategies.searchstrategy
import theodolite.execution.BenchmarkExecutor
import theodolite.strategies.restriction.RestrictionStrategy
import theodolite.util.LoadDimension
import theodolite.util.Resource
import theodolite.util.Results
class CompositeStrategy(benchmarkExecutor: BenchmarkExecutor, private val searchStrategy: SearchStrategy, val restrictionStrategies: Set<RestrictionStrategy>) : SearchStrategy(benchmarkExecutor) {
override fun findSuitableResource(load: LoadDimension, resources: List<Resource>): Resource? {
var restrictedResources = resources.toList()
for (strategy in this.restrictionStrategies) {
restrictedResources = restrictedResources.intersect(strategy.next(load, resources)).toList()
}
return this.searchStrategy.findSuitableResource(load, restrictedResources)
}
}
\ No newline at end of file
package theodolite.strategies.searchstrategy
import theodolite.execution.BenchmarkExecutor
import theodolite.util.LoadDimension
import theodolite.util.Resource
import theodolite.util.Results
class LinearSearch(benchmarkExecutor: BenchmarkExecutor) : SearchStrategy(benchmarkExecutor) {
override fun findSuitableResource(load: LoadDimension, resources: List<Resource>): Resource? {
for (res in resources) {
if (this.benchmarkExecutor.runExperiment(load, res)) return res
}
return null;
}
}
\ No newline at end of file
package theodolite.strategies.searchstrategy
import theodolite.execution.BenchmarkExecutor
import theodolite.util.LoadDimension
import theodolite.util.Resource
import theodolite.util.Results
abstract class SearchStrategy(val benchmarkExecutor: BenchmarkExecutor) {
/**
* Find smallest suitable resource from the specified resource list for the given load.
*
* @param load Load to be tested.
* @param resources List of all possible resources.
* @return suitable resource for the specified load, or null if no suitable resource exists.
*/
abstract fun findSuitableResource(load: LoadDimension, resources: List<Resource>): Resource?;
}
\ No newline at end of file
package theodolite.util
abstract class AbstractBenchmark(val config: Config): Benchmark {
override fun start(load: LoadDimension, resources: Resource) {
this.clearClusterEnvironment()
this.initializeClusterEnvironment()
this.startSUT(resources)
this.startWorkloadGenerator(load)
}
data class Config(
val clusterZookeeperConnectionString: String,
val clusterKafkaConnectionString: String,
val externalZookeeperConnectionString: String,
val externalKafkaConnectionString: String,
val schemaRegistryConnectionString: String,
val kafkaTopics: List<String>,
val kafkaReplication: Short,
val kafkaPartition: Int,
val ucDeploymentPath: String,
val ucServicePath: String,
val configMapPath: String,
val wgDeploymentPath: String,
val ucImageURL: String,
val wgImageURL: String
) {}
}
package theodolite.util
interface Benchmark {
fun start(load: LoadDimension, resources: Resource) {
}
fun initializeClusterEnvironment();
fun clearClusterEnvironment();
fun startSUT(resources: Resource);
fun startWorkloadGenerator(load: LoadDimension)
}
\ No newline at end of file
package theodolite.util
import theodolite.strategies.restriction.RestrictionStrategy
import theodolite.strategies.searchstrategy.CompositeStrategy
import theodolite.strategies.searchstrategy.SearchStrategy
import java.time.Duration
data class Config(
val loads: List<LoadDimension>,
val resources: List<Resource>,
val compositeStrategy: CompositeStrategy,
val executionDuration: Duration
) {
}
\ No newline at end of file
package theodolite.util
data class LoadDimension(private val number: Int) {
public fun get(): Int {
return this.number;
}
}
package theodolite.util
data class Resource(private val number: Int) {
public fun get(): Int {
return this.number;
}
}
\ No newline at end of file
package theodolite.util
import theodolite.util.LoadDimension
import theodolite.util.Resource
import kotlin.math.exp
class Results {
// load, instances
private val results: MutableMap<Pair<LoadDimension, Resource>, Boolean> = mutableMapOf() // multi map guava
public fun setResult(experiment: Pair<LoadDimension, Resource>, successful: Boolean) {
this.results[experiment] = successful
}
public fun getResult (experiment: Pair<LoadDimension, Resource>): Boolean? {
return this.results[experiment]
}
public fun getMinRequiredInstances(load: LoadDimension?): Resource? {
if (this.results.isEmpty()) return Resource(Int.MIN_VALUE)
var requiredInstances: Resource? = Resource(Int.MAX_VALUE)
for(experiment in results) {
if(experiment.key.first == load && experiment.value){
if(requiredInstances == null) {
requiredInstances = experiment.key.second
}else if (experiment.key.second.get() < requiredInstances.get()) {
requiredInstances = experiment.key.second
}
}
}
return requiredInstances
}
public fun getMaxBenchmarkedLoad(load: LoadDimension): LoadDimension? {
var maxBenchmarkedLoad: LoadDimension? = null;
for(experiment in results) {
if (experiment.value) {
if(experiment.key.first.get() <= load.get()) {
if (maxBenchmarkedLoad == null) {
maxBenchmarkedLoad = experiment.key.first
} else if (maxBenchmarkedLoad.get() < experiment.key.first.get()) {
maxBenchmarkedLoad = experiment.key.first
}
}
}
}
return maxBenchmarkedLoad
}
}
\ No newline at end of file
package theodolite.util
class TestBenchmark : AbstractBenchmark(
AbstractBenchmark.Config(
clusterZookeeperConnectionString = "",
clusterKafkaConnectionString = "",
externalZookeeperConnectionString = "",
externalKafkaConnectionString = "",
schemaRegistryConnectionString = "",
kafkaTopics = emptyList(),
kafkaReplication = 0,
kafkaPartition = 0,
ucServicePath = "",
ucDeploymentPath = "",
wgDeploymentPath = "",
configMapPath = "",
ucImageURL = "",
wgImageURL = ""
)
) {
override fun initializeClusterEnvironment() {
TODO("Not yet implemented")
}
override fun clearClusterEnvironment() {
TODO("Not yet implemented")
}
override fun startSUT(resources: Resource) {
TODO("Not yet implemented")
}
override fun startWorkloadGenerator(load: LoadDimension) {
TODO("Not yet implemented")
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>theodolite-quarkus - 1.0.0-SNAPSHOT</title>
<style>
h1, h2, h3, h4, h5, h6 {
margin-bottom: 0.5rem;
font-weight: 400;
line-height: 1.5;
}
h1 {
font-size: 2.5rem;
}
h2 {
font-size: 2rem
}
h3 {
font-size: 1.75rem
}
h4 {
font-size: 1.5rem
}
h5 {
font-size: 1.25rem
}
h6 {
font-size: 1rem
}
.lead {
font-weight: 300;
font-size: 2rem;
}
.banner {
font-size: 2.7rem;
margin: 0;
padding: 2rem 1rem;
background-color: #0d1c2c;
color: white;
}
body {
margin: 0;
font-family: -apple-system, system-ui, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
}
code {
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 87.5%;
color: #e83e8c;
word-break: break-word;
}
.left-column {
padding: .75rem;
max-width: 75%;
min-width: 55%;
}
.right-column {
padding: .75rem;
max-width: 25%;
}
.container {
display: flex;
width: 100%;
}
li {
margin: 0.75rem;
}
.right-section {
margin-left: 1rem;
padding-left: 0.5rem;
}
.right-section h3 {
padding-top: 0;
font-weight: 200;
}
.right-section ul {
border-left: 0.3rem solid #71aeef;
list-style-type: none;
padding-left: 0;
}
.examples {
display: flex;
flex-wrap: wrap;
margin: 20px 0 20px -40px;
}
.example {
display: flex;
margin-left: 20px;
margin-bottom: 20px;
flex-direction: column;
width: 350px;
background-color: #205894;
color: white;
}
.example code {
color: lightgrey;
}
.example-header {
padding: 20px;
display: flex;
position: relative;
}
.example-header h4 {
margin: 0;
font-size: 1.4rem;
flex-grow: 1;
line-height: 1.5;
}
.example-description {
padding: 0 20px;
flex-grow: 1;
}
.example-paths {
display: flex;
flex-direction: column;
}
.example-paths a {
display: block;
background-color: transparent;
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
color: white;
padding: 10px;
text-decoration: none;
}
.example-paths a:before {
content: '⇨';
font-weight: bold;
font-size: 1.5rem;
margin: 20px;
}
.example-paths a:hover {
background-color: #0d1c2c;
}
.guide-link {
background-color: #71aeef;
position: absolute;
color: white;
text-decoration: none;
top: 0;
right: 0;
padding: 7px;
font-weight: bold;
}
.guide-link:hover {
background-color: #0d1c2c;
}
</style>
</head>
<body>
<div class="banner lead">
Your new Cloud-Native application is ready!
</div>
<div class="container">
<div class="left-column">
<p class="lead"> Congratulations, you have created a new Quarkus cloud application.</p>
<h2>Why do you see this?</h2>
<p>This page is served by Quarkus. The source is in
<code>src/main/resources/META-INF/resources/index.html</code>.</p>
<h2>What can I do from here?</h2>
<p>If not already done, run the application in <em>dev mode</em> using: <code>./gradlew quarkusDev</code>.
</p>
<ul>
<li>Play with your example code in <code>src/main/kotlin</code>:
<div class="examples">
<div class="example">
<div class="example-header">
<h4>RESTEasy JAX-RS</h4>
<a href="https://quarkus.io/guides/rest-json" target="_blank" class="guide-link">Guide</a>
</div>
<div class="example-description">
<p>A Hello World RESTEasy resource</p>
</div>
<div class="example-paths">
<a href="/hello-resteasy" class="path-link" target="_blank">GET /hello-resteasy</a>
</div>
</div>
</div>
</li>
<li>Your static assets are located in <code>src/main/resources/META-INF/resources</code>.</li>
<li>Configure your application in <code>src/main/resources/application.properties</code>.</li>
</ul>
<h2>Do you like Quarkus?</h2>
<p>Go give it a star on <a href="https://github.com/quarkusio/quarkus">GitHub</a>.</p>
</div>
<div class="right-column">
<div class="right-section">
<h3>Application</h3>
<ul>
<li>GroupId: theodolite</li>
<li>ArtifactId: theodolite-quarkus</li>
<li>Version: 1.0.0-SNAPSHOT</li>
<li>Quarkus Version: 1.10.3.Final</li>
</ul>
</div>
<div class="right-section">
<h3>Next steps</h3>
<ul>
<li><a href="https://quarkus.io/guides/gradle-tooling" target="_blank">Setup your IDE</a></li>
<li><a href="https://quarkus.io/guides/getting-started.html" target="_blank">Getting started</a></li>
<li><a href="https://quarkus.io" target="_blank">Quarkus Web Site</a></li>
</ul>
</div>
</div>
</div>
</body>
</html>
\ No newline at end of file
apiVersion: apps/v1
kind: Deployment
metadata:
name: titan-ccp-aggregation
spec:
selector:
matchLabels:
app: titan-ccp-aggregation
replicas: 1
template:
metadata:
labels:
app: titan-ccp-aggregation
spec:
terminationGracePeriodSeconds: 0
containers:
- name: uc-application
image: uc-app:latest
ports:
- containerPort: 5555
name: jmx
env:
- name: KAFKA_BOOTSTRAP_SERVERS
value: "my-confluent-cp-kafka:9092"
- name: SCHEMA_REGISTRY_URL
value: "http://my-confluent-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
value: "100"
resources:
limits:
memory: 4Gi
cpu: 1000m
- name: prometheus-jmx-exporter
image: "solsson/kafka-prometheus-jmx-exporter@sha256:6f82e2b0464f50da8104acd7363fb9b995001ddff77d248379f8788e78946143"
command:
- java
- -XX:+UnlockExperimentalVMOptions
- -XX:+UseCGroupMemoryLimitForHeap
- -XX:MaxRAMFraction=1
- -XshowSettings:vm
- -jar
- jmx_prometheus_httpserver.jar
- "5556"
- /etc/jmx-aggregation/jmx-kafka-prometheus.yml
ports:
- containerPort: 5556
volumeMounts:
- name: jmx-config
mountPath: /etc/jmx-aggregation
volumes:
- name: jmx-config
configMap:
name: aggregation-jmx-configmap
apiVersion: v1
kind: Service
metadata:
name: titan-ccp-aggregation
labels:
app: titan-ccp-aggregation
spec:
#type: NodePort
selector:
app: titan-ccp-aggregation
ports:
- name: http
port: 80
targetPort: 80
protocol: TCP
- name: metrics
port: 9980
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment