diff --git a/pom.xml b/pom.xml
index 9f326c33..4ec0d59d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -96,6 +96,7 @@
spring-cloud-kubernetes-istio
spring-cloud-kubernetes-integration-tests
docs
+ spring-cloud-kubernetes-loadbalancer
diff --git a/spring-cloud-kubernetes-loadbalancer/pom.xml b/spring-cloud-kubernetes-loadbalancer/pom.xml
new file mode 100644
index 00000000..93514763
--- /dev/null
+++ b/spring-cloud-kubernetes-loadbalancer/pom.xml
@@ -0,0 +1,45 @@
+
+
+
+ spring-cloud-kubernetes
+ org.springframework.cloud
+ 2.0.0-SNAPSHOT
+
+ 4.0.0
+
+ spring-cloud-kubernetes-loadbalancer
+ Spring Cloud Kubernetes :: Load Balancer
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-loadbalancer
+
+
+ org.springframework.cloud
+ spring-cloud-kubernetes-discovery
+
+
+ commons-lang
+ commons-lang
+ 2.6
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+ true
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
diff --git a/spring-cloud-kubernetes-loadbalancer/src/main/java/org/springframework/cloud/kubernetes/loadbalancer/KubernetesClientConfiguration.java b/spring-cloud-kubernetes-loadbalancer/src/main/java/org/springframework/cloud/kubernetes/loadbalancer/KubernetesClientConfiguration.java
new file mode 100644
index 00000000..dddc13f4
--- /dev/null
+++ b/spring-cloud-kubernetes-loadbalancer/src/main/java/org/springframework/cloud/kubernetes/loadbalancer/KubernetesClientConfiguration.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2013-2020 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.kubernetes.loadbalancer;
+
+import io.fabric8.kubernetes.client.KubernetesClient;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.core.env.Environment;
+
+/**
+ * Kubernetes load balancer client properties.
+ *
+ * @author Piotr Minkowski
+ */
+public class KubernetesClientConfiguration {
+
+ @Bean
+ @ConditionalOnProperty(name = "spring.cloud.kubernetes.loadbalancer.mode",
+ havingValue = "SERVICE")
+ KubernetesServicesListSupplier kubernetesServicesListSupplier(Environment environment,
+ KubernetesClient kubernetesClient, KubernetesServiceInstanceMapper mapper) {
+ return new KubernetesServicesListSupplier(environment, kubernetesClient, mapper);
+ }
+
+}
diff --git a/spring-cloud-kubernetes-loadbalancer/src/main/java/org/springframework/cloud/kubernetes/loadbalancer/KubernetesLoadBalancerAutoConfiguration.java b/spring-cloud-kubernetes-loadbalancer/src/main/java/org/springframework/cloud/kubernetes/loadbalancer/KubernetesLoadBalancerAutoConfiguration.java
new file mode 100644
index 00000000..5d388db9
--- /dev/null
+++ b/spring-cloud-kubernetes-loadbalancer/src/main/java/org/springframework/cloud/kubernetes/loadbalancer/KubernetesLoadBalancerAutoConfiguration.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2013-2020 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.kubernetes.loadbalancer;
+
+import org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryProperties;
+import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author Piotr Minkowski
+ */
+@Configuration
+@LoadBalancerClients(defaultConfiguration = KubernetesClientConfiguration.class)
+public class KubernetesLoadBalancerAutoConfiguration {
+
+ @Bean
+ KubernetesLoadBalancerProperties properties() {
+ return new KubernetesLoadBalancerProperties();
+ }
+
+ @Bean
+ KubernetesServiceInstanceMapper mapper(KubernetesLoadBalancerProperties properties,
+ KubernetesDiscoveryProperties discoveryProperties) {
+ return new KubernetesServiceInstanceMapper(properties, discoveryProperties);
+ }
+
+}
diff --git a/spring-cloud-kubernetes-loadbalancer/src/main/java/org/springframework/cloud/kubernetes/loadbalancer/KubernetesLoadBalancerMode.java b/spring-cloud-kubernetes-loadbalancer/src/main/java/org/springframework/cloud/kubernetes/loadbalancer/KubernetesLoadBalancerMode.java
new file mode 100644
index 00000000..41eb66c4
--- /dev/null
+++ b/spring-cloud-kubernetes-loadbalancer/src/main/java/org/springframework/cloud/kubernetes/loadbalancer/KubernetesLoadBalancerMode.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2013-2019 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.kubernetes.loadbalancer;
+
+/**
+ * @author Piotr Minkowski
+ */
+public enum KubernetesLoadBalancerMode {
+
+ /**
+ * using pod ip and port.
+ */
+ POD,
+ /**
+ * using kubernetes service name and port.
+ */
+ SERVICE
+
+}
diff --git a/spring-cloud-kubernetes-loadbalancer/src/main/java/org/springframework/cloud/kubernetes/loadbalancer/KubernetesLoadBalancerProperties.java b/spring-cloud-kubernetes-loadbalancer/src/main/java/org/springframework/cloud/kubernetes/loadbalancer/KubernetesLoadBalancerProperties.java
new file mode 100644
index 00000000..88e27152
--- /dev/null
+++ b/spring-cloud-kubernetes-loadbalancer/src/main/java/org/springframework/cloud/kubernetes/loadbalancer/KubernetesLoadBalancerProperties.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2013-2020 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.kubernetes.loadbalancer;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * @author Piotr Minkowski
+ */
+@ConfigurationProperties(prefix = "spring.cloud.kubernetes.loadbalancer")
+public class KubernetesLoadBalancerProperties {
+
+ /**
+ * Ribbon enabled,default true.
+ */
+ private Boolean enabled = true;
+
+ /**
+ * {@link KubernetesLoadBalancerMode} setting ribbon server list with ip of pod or
+ * service name. default value is POD.
+ */
+ private KubernetesLoadBalancerMode mode = KubernetesLoadBalancerMode.POD;
+
+ /**
+ * cluster domain.
+ */
+ private String clusterDomain = "cluster.local";
+
+ /**
+ * service port name.
+ */
+ private String portName = "http";
+
+ /**
+ * Get cluster domain.
+ * @return the cluster domain
+ */
+ public String getClusterDomain() {
+ return clusterDomain;
+ }
+
+ /**
+ * Sets cluster domain.
+ * @param clusterDomain the cluster domain
+ */
+ public void setClusterDomain(String clusterDomain) {
+ this.clusterDomain = clusterDomain;
+ }
+
+ /**
+ * Gets mode.
+ * @return the mode
+ */
+ public KubernetesLoadBalancerMode getMode() {
+ return mode;
+ }
+
+ /**
+ * Sets mode.
+ * @param mode the mode
+ */
+ public void setMode(KubernetesLoadBalancerMode mode) {
+ this.mode = mode;
+ }
+
+ /**
+ * Gets enabled.
+ * @return the enabled
+ */
+ public Boolean getEnabled() {
+ return enabled;
+ }
+
+ /**
+ * Sets enabled.
+ * @param enabled the enabled
+ */
+ public void setEnabled(Boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ /**
+ * Gets portName.
+ * @return portName port name
+ */
+ public String getPortName() {
+ return portName;
+ }
+
+ /**
+ * Sets portName.
+ * @param portName port name
+ */
+ public void setPortName(String portName) {
+ this.portName = portName;
+ }
+
+}
diff --git a/spring-cloud-kubernetes-loadbalancer/src/main/java/org/springframework/cloud/kubernetes/loadbalancer/KubernetesServiceInstance.java b/spring-cloud-kubernetes-loadbalancer/src/main/java/org/springframework/cloud/kubernetes/loadbalancer/KubernetesServiceInstance.java
new file mode 100644
index 00000000..dcf71944
--- /dev/null
+++ b/spring-cloud-kubernetes-loadbalancer/src/main/java/org/springframework/cloud/kubernetes/loadbalancer/KubernetesServiceInstance.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2013-2020 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.kubernetes.loadbalancer;
+
+import java.net.URI;
+import java.util.Map;
+
+import org.springframework.cloud.client.ServiceInstance;
+
+/**
+ * @author Piotr Minkowski
+ */
+public class KubernetesServiceInstance implements ServiceInstance {
+
+ private String serviceId;
+
+ private String instanceId;
+
+ private int port;
+
+ private boolean secure;
+
+ private String host;
+
+ private URI uri;
+
+ private Map metadata;
+
+ KubernetesServiceInstance(String serviceId, String instanceId, int port,
+ boolean secure, String host, URI uri, Map metadata) {
+ this.serviceId = serviceId;
+ this.instanceId = instanceId;
+ this.port = port;
+ this.secure = secure;
+ this.host = host;
+ this.uri = uri;
+ this.metadata = metadata;
+ }
+
+ @Override
+ public String getServiceId() {
+ return serviceId;
+ }
+
+ @Override
+ public String getHost() {
+ return host;
+ }
+
+ @Override
+ public int getPort() {
+ return port;
+ }
+
+ @Override
+ public boolean isSecure() {
+ return secure;
+ }
+
+ @Override
+ public URI getUri() {
+ return uri;
+ }
+
+ @Override
+ public Map getMetadata() {
+ return metadata;
+ }
+
+}
diff --git a/spring-cloud-kubernetes-loadbalancer/src/main/java/org/springframework/cloud/kubernetes/loadbalancer/KubernetesServiceInstanceMapper.java b/spring-cloud-kubernetes-loadbalancer/src/main/java/org/springframework/cloud/kubernetes/loadbalancer/KubernetesServiceInstanceMapper.java
new file mode 100644
index 00000000..a54baf5f
--- /dev/null
+++ b/spring-cloud-kubernetes-loadbalancer/src/main/java/org/springframework/cloud/kubernetes/loadbalancer/KubernetesServiceInstanceMapper.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2013-2020 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.kubernetes.loadbalancer;
+
+import java.net.URI;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import io.fabric8.kubernetes.api.model.ObjectMeta;
+import io.fabric8.kubernetes.api.model.Service;
+import io.fabric8.kubernetes.api.model.ServicePort;
+import io.fabric8.kubernetes.client.utils.Utils;
+import org.apache.commons.lang.StringUtils;
+
+import org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryProperties;
+
+/**
+ * @author Piotr Minkowski
+ */
+public class KubernetesServiceInstanceMapper {
+
+ private static final String DSL = "//";
+
+ private static final String COLON = ":";
+
+ private final KubernetesLoadBalancerProperties properties;
+
+ private final KubernetesDiscoveryProperties discoveryProperties;
+
+ KubernetesServiceInstanceMapper(KubernetesLoadBalancerProperties properties,
+ KubernetesDiscoveryProperties discoveryProperties) {
+ this.properties = properties;
+ this.discoveryProperties = discoveryProperties;
+ }
+
+ public KubernetesServiceInstance map(Service service) {
+ final ObjectMeta meta = service.getMetadata();
+ final List ports = service.getSpec().getPorts();
+ ServicePort port = null;
+ if (ports.size() == 1) {
+ port = ports.get(0);
+ }
+ else if (ports.size() > 1
+ && Utils.isNotNullOrEmpty(this.properties.getPortName())) {
+ Optional optPort = ports.stream()
+ .filter(it -> properties.getPortName().endsWith(it.getName()))
+ .findAny();
+ if (optPort.isPresent()) {
+ port = optPort.get();
+ }
+ }
+ if (port == null) {
+ return null;
+ }
+ final String host = createHost(service);
+ final boolean secure = isSecure(service, port);
+ return new KubernetesServiceInstance(meta.getName(), meta.getUid(),
+ port.getPort(), secure, host,
+ createUri(secure ? "https" : "http", host, port.getPort()),
+ getServiceMetadata(service));
+ }
+
+ private Map getServiceMetadata(Service service) {
+ final Map serviceMetadata = new HashMap<>();
+ KubernetesDiscoveryProperties.Metadata metadataProps = this.discoveryProperties
+ .getMetadata();
+ if (metadataProps.isAddLabels()) {
+ Map labelMetadata = getMapWithPrefixedKeys(
+ service.getMetadata().getLabels(), metadataProps.getLabelsPrefix());
+ serviceMetadata.putAll(labelMetadata);
+ }
+ if (metadataProps.isAddAnnotations()) {
+ Map annotationMetadata = getMapWithPrefixedKeys(
+ service.getMetadata().getAnnotations(),
+ metadataProps.getAnnotationsPrefix());
+ serviceMetadata.putAll(annotationMetadata);
+ }
+
+ return serviceMetadata;
+ }
+
+ private Map getMapWithPrefixedKeys(Map map,
+ String prefix) {
+ if (map == null) {
+ return new HashMap<>();
+ }
+ if (!org.springframework.util.StringUtils.hasText(prefix)) {
+ return map;
+ }
+ final Map result = new HashMap<>();
+ map.forEach((k, v) -> result.put(prefix + k, v));
+ return result;
+ }
+
+ private boolean isSecure(Service service, ServicePort port) {
+ final String securedLabelValue = service.getMetadata().getLabels()
+ .getOrDefault("secured", "false");
+ if (securedLabelValue.equals("true")) {
+ return true;
+ }
+ final String securedAnnotationValue = service.getMetadata().getAnnotations()
+ .getOrDefault("secured", "false");
+ if (securedAnnotationValue.equals("true")) {
+ return true;
+ }
+ return (port.getName() != null && port.getName().endsWith("https"))
+ || port.getPort().toString().endsWith("443");
+ }
+
+ private String createHost(Service service) {
+ return String.format("%s.%s.svc.%s", service.getMetadata().getName(),
+ StringUtils.isNotBlank(service.getMetadata().getNamespace())
+ ? service.getMetadata().getNamespace() : "default",
+ properties.getClusterDomain());
+ }
+
+ private URI createUri(String scheme, String host, int port) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(scheme).append(COLON).append(DSL).append(host).append(COLON)
+ .append(port);
+ return URI.create(sb.toString());
+ }
+
+}
diff --git a/spring-cloud-kubernetes-loadbalancer/src/main/java/org/springframework/cloud/kubernetes/loadbalancer/KubernetesServicesListSupplier.java b/spring-cloud-kubernetes-loadbalancer/src/main/java/org/springframework/cloud/kubernetes/loadbalancer/KubernetesServicesListSupplier.java
new file mode 100644
index 00000000..51fe8fbc
--- /dev/null
+++ b/spring-cloud-kubernetes-loadbalancer/src/main/java/org/springframework/cloud/kubernetes/loadbalancer/KubernetesServicesListSupplier.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2013-2020 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.kubernetes.loadbalancer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import io.fabric8.kubernetes.api.model.Service;
+import io.fabric8.kubernetes.client.KubernetesClient;
+import org.apache.commons.lang.StringUtils;
+import reactor.core.publisher.Flux;
+
+import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
+import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
+import org.springframework.core.env.Environment;
+
+/**
+ * @author Piotr Minkowski
+ */
+public class KubernetesServicesListSupplier implements ServiceInstanceListSupplier {
+
+ private Environment environment;
+
+ private KubernetesClient kubernetesClient;
+
+ private KubernetesServiceInstanceMapper mapper;
+
+ KubernetesServicesListSupplier(Environment environment,
+ KubernetesClient kubernetesClient, KubernetesServiceInstanceMapper mapper) {
+ this.environment = environment;
+ this.kubernetesClient = kubernetesClient;
+ this.mapper = mapper;
+ }
+
+ @Override
+ public String getServiceId() {
+ return environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
+ }
+
+ @Override
+ public Flux> get() {
+ List result = new ArrayList<>();
+ Service service = StringUtils.isNotBlank(this.kubernetesClient.getNamespace())
+ ? this.kubernetesClient
+ .services().inNamespace(this.kubernetesClient.getNamespace())
+ .withName(this.getServiceId()).get()
+ : this.kubernetesClient.services().withName(this.getServiceId()).get();
+ if (service != null) {
+ result.add(mapper.map(service));
+ }
+ return Flux.just(result);
+ }
+
+}
diff --git a/spring-cloud-kubernetes-loadbalancer/src/main/resources/META-INF/spring.factories b/spring-cloud-kubernetes-loadbalancer/src/main/resources/META-INF/spring.factories
new file mode 100644
index 00000000..4a43c68f
--- /dev/null
+++ b/spring-cloud-kubernetes-loadbalancer/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+org.springframework.cloud.kubernetes.loadbalancer.KubernetesLoadBalancerAutoConfiguration