Skip to content

Instantly share code, notes, and snippets.

@jkschneider
Last active October 10, 2019 17:53
Show Gist options
  • Save jkschneider/0d1e437e6cdd4d7f14d4ba9e5a2e3c8a to your computer and use it in GitHub Desktop.
Save jkschneider/0d1e437e6cdd4d7f14d4ba9e5a2e3c8a to your computer and use it in GitHub Desktop.
Spring Boot configuration to wire Spinnaker K8S v2 Provider annotations to Micrometer for use in Kayenta
---
apiVersion: apps/v1
kind: Deployment
...
spec:
template:
spec:
serviceAccountName: replicaset-annotations-service-account
...
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: replicaset-annotations-service-account
namespace: default
labels:
name: replicaset-annotations-service-account
app: appname
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: replicaset-annotations-role
namespace: default
labels:
name: replicaset-annotations-role
app: appname
rules:
- apiGroups: ["apps"]
resources: ["replicasets"]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: replicaset-annotations-role-binding
namespace: default
labels:
name: replicaset-annotations-role-binding
app: appname
subjects:
- kind: ServiceAccount
name: replicaset-annotations-service-account
roleRef:
kind: Role
name: replicaset-annotations-role-binding
apiGroup: rbac.authorization.k8s.io
@Configuration
public class PaneraMetricsAutoConfiguration {
private final Logger logger = LoggerFactory.getLogger(PaneraMetricsAutoConfiguration.class);
@Value("${spring.application.name:unknown}")
private String appName;
@Value("${HOSTNAME:unknown}")
private String host;
@Bean
public MeterFilter httpDistributionStatistics() {
return new MeterFilter() {
@Override
public DistributionStatisticConfig configure(Meter.Id id,
DistributionStatisticConfig config) {
if (id.getName().equals("http.server.requests")) {
return DistributionStatisticConfig.builder().percentilesHistogram(true).build()
.merge(config);
}
return config;
}
};
}
@Bean
@ConditionalOnBean(KubernetesClient.class)
public MeterFilter kubernetesMeterFilter(KubernetesClient k8sClient) {
return new MeterFilter() {
@Override
public Meter.Id map(Meter.Id id) {
try {
// Spinnaker adds annotations to the replicaSet. So we need to determine the replicaSet by looking at the pod's owner
// information.
Map<String, String> annotations = k8sClient
.pods()
.withName(host)
.get()
.getMetadata()
.getOwnerReferences()
.stream()
.findFirst()
.map(ref -> k8sClient.apps().replicaSets().withName(ref.getName()).get())
.map(replicaSet -> replicaSet.getMetadata().getAnnotations()).orElse(emptyMap());
return id.withTags(Tags.of(
"revision", annotations.getOrDefault("deployment.kubernetes.io/revision", "unknown"),
"application", annotations.getOrDefault("moniker.spinnaker.io/application", appName),
"cluster", annotations.getOrDefault("moniker.spinnaker.io/cluster", "unknown"),
"location", annotations.getOrDefault("artifact.spinnaker.io/location", "unknown")));
} catch (KubernetesClientException e) {
logger.warn("Unable to apply kubernetes telemetry tags", e);
// we tried... at least add application and host tags
return id.withTag(Tag.of("application", appName, "host", host));
}
}
};
}
@Bean
@ConditionalOnMissingBean(KubernetesClient.class)
public MeterFilter meterFilter() {
return MeterFilter.commonTags(Tags.of("application", appName, "host", host));
}
@ConditionalOnMissingBean
@Bean(destroyMethod = "pushAndClose")
PrometheusRSocketClient prometheusRSocketClient(PrometheusMeterRegistry meterRegistry) {
return new PrometheusRSocketClient(meterRegistry,
WebsocketClientTransport.create("104.198.168.96", 8081),
c -> c.retryBackoff(Long.MAX_VALUE, Duration.ofSeconds(3), Duration.ofMinutes(2)));
}
}
@jkschneider
Copy link
Author

jkschneider commented Oct 8, 2019

Add dependency implementation 'io.fabric8:kubernetes-client'.

K8S does not allow access to replicaset annotations from the default service account (unless its privileges are elevated beyond the default). You must create a role binding granting replicasets/get and associate it with a service account that is attached to the deployment/statefulset.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment