RSocket Proxy for Prometheus monitoring

- Rename spring-cloud-dataflow-samples/tree/master/micrometer to spring-cloud-dataflow-samples/tree/master/micrometer-samples
 - Rename spring-cloud-dataflow-samples/tree/master/micrometer/tasks to spring-cloud-dataflow-samples/tree/master/micrometer-samples/task-apps
 - Add the apps under spring-cloud-dataflow-samples/tree/master/micrometer/stream-apps
 - Describe or cross-link the description of the new architecture in the root README
 - Add microsite SCDF monitoring links

 Resolves #122
This commit is contained in:
Christian Tzolov
2019-11-02 16:27:08 +01:00
parent 369ecce9ba
commit f8078ed08f
68 changed files with 2287 additions and 1 deletions

View File

@@ -0,0 +1,181 @@
/*
* Copyright 2015-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.dataflow.prometheus.servicediscovery;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.dataflow.rest.resource.AppStatusResource;
import org.springframework.hateoas.hal.Jackson2HalModule;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.web.client.RestTemplate;
/**
* Prometheus Service Discovery for SCSt apps deployed with the Local Deployer (?k8s?). It is build on top of the
* file_sd_config mechanism
*
* https://prometheus.io/docs/prometheus/latest/configuration/configuration/#%3Cfile_sd_config%3E
*
* @author Christian Tzolov
*/
@SpringBootApplication
@EnableScheduling
public class DataflowPrometheusServiceDiscoveryApplication {
private final Logger logger = LoggerFactory.getLogger(DataflowPrometheusServiceDiscoveryApplication.class);
private final ObjectMapper objectMapper;
private final RestTemplate restTemplate;
public enum TargetMode {local, promregator}
/**
* Url of the the SCDF Runtime REST endpoint:
* https://docs.spring.io/spring-cloud-dataflow/docs/current/reference/htmlsingle/#api-guide-resources-runtime-information-applications
*/
@Value("${metrics.prometheus.target.discovery.url:http://localhost:9393/runtime/apps}")
private String targetDiscoveryUrl;
/**
* Path where the generated targets.json file is stored.
*/
@Value("${metrics.prometheus.target.file.path:/tmp/targets.json}")
private String outputTargetFilePath;
// For k8s deployment use 'pod.ip' instead of 'url'
@Value("${metrics.prometheus.target.dataflow.runtime.attribute.name:url}")
private String attributeName;
@Value("${metrics.prometheus.target.mode:local}")
private TargetMode targetMode;
public DataflowPrometheusServiceDiscoveryApplication() {
this.objectMapper = Jackson2ObjectMapperBuilder.json().modules(new Jackson2HalModule()).build();
this.restTemplate = new RestTemplate(Arrays.asList(new MappingJackson2HttpMessageConverter(this.objectMapper)));
}
public static void main(String[] args) {
SpringApplication.run(DataflowPrometheusServiceDiscoveryApplication.class, args);
}
/**
* Use the metrics.prometheus.target.refresh.rate property (in milliseconds) to change the targets discovery rate.
*
* @throws IOException
*/
@Scheduled(fixedRateString = "${metrics.prometheus.target.refresh.rate:15000}")
public void updateTargets() throws IOException {
String targetsJson;
if (this.targetMode == TargetMode.local) {
targetsJson = this.findTargetsFromDataflowRuntimeApps();
}
else if (this.targetMode == TargetMode.promregator) {
targetsJson = this.findTargetsWithPromregator();
}
else {
throw new IllegalStateException("Unknown target mode:" + this.targetMode);
}
logger.info(this.targetMode + ": " + targetsJson);
this.updateTargetsFile(targetsJson);
}
public String findTargetsFromDataflowRuntimeApps() throws IOException {
AppStatusResource.Page page = this.restTemplate.getForObject(this.targetDiscoveryUrl, AppStatusResource.Page.class);
List<String> targetUrls = page.getContent().stream()
.map(appResource -> appResource.getInstances().getContent())
.flatMap(instances -> instances.stream().map(inst -> inst.getAttributes().get(this.attributeName)))
.map(url -> url.replace("http://", ""))
.collect(Collectors.toList());
return this.buildPrometheusTargetsJson(targetUrls);
}
private String findTargetsWithPromregator() throws IOException {
Object b = new RestTemplate().getForObject(this.targetDiscoveryUrl, Object.class);
return this.objectMapper.writeValueAsString(b);
}
/**
* Converts a list of urls into JSON list of static target configs, compliant with the file_sd_config format.
* @param targetUrls list of SCSt apps ip:ports to be used as prometheus metrics targets.
* @return json record compliant with file_sd_config
* @throws JsonProcessingException
*/
private String buildPrometheusTargetsJson(List<String> targetUrls) throws JsonProcessingException {
StaticConfig staticConfig = new StaticConfig();
staticConfig.setTargets(targetUrls);
staticConfig.setLabels(Collections.singletonMap("job", "scdf"));
return this.objectMapper.writeValueAsString(Arrays.asList(staticConfig));
}
/**
* Re-writes the updated targets json into output file used by Prometheus.
* @param targetsJson SCDF apps targets
* @throws IOException
*/
private void updateTargetsFile(String targetsJson) throws IOException {
FileWriter fw = new FileWriter(this.outputTargetFilePath);
fw.write(targetsJson);
fw.close();
}
/**
* Java model used as JSON representation of Prometheus' static target format.
* https://prometheus.io/docs/prometheus/latest/configuration/configuration/#%3Cfile_sd_config%3E
*/
public static class StaticConfig {
private List<String> targets;
private Map<String, String> labels;
public List<String> getTargets() {
return targets;
}
public void setTargets(List<String> targets) {
this.targets = targets;
}
public Map<String, String> getLabels() {
return labels;
}
public void setLabels(Map<String, String> labels) {
this.labels = labels;
}
}
}

View File

@@ -0,0 +1,16 @@
package org.springframework.cloud.dataflow.prometheus.servicediscovery;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringCloudDataflowPrometheusServiceDiscoveryApplicationTests {
@Test
public void contextLoads() {
}
}