Search code examples
javaspring-bootkubernetesspring-cloudspring-cloud-feign

What is the correct way to use feign with spring cloud kubernetes?


I am using Spring Cloud Kubernetes and I am trying to make feign able to send requests based on the name of the services present in kubernetes, but I can't, when I try to make a request the following error occurs:

  "timestamp": "2019-12-06T15:37:50.285+0000",
    "status": 500,
    "error": "Internal Server Error",
    "message": "com.netflix.client.ClientException: Load balancer does not have available server for client: poc-saldo",
    "trace": "java.lang.RuntimeException: com.netflix.client.ClientException: Load balancer does not have available server for client: poc-saldo\n\tat org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient.execute....

I tried to call other services within the cluster and the problem is the same for all of them, I did a test by going into the poc-deposit pod and doing a poc-balance curl and it works normally, so the problem is not with the poc-deposit service. balance or with kubernetes's service discovery apparently.

The project has a public profile at:

https://gitlab.com/viniciusxyz/spring-kubernetes-feign

For those who want more direct information:

My main class is as follows:

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class ServiceDiscoveryApplication {

    public static void main(String[] args) {
        SpringApplication.run(ServiceDiscoveryApplication.class, args);
    }

}

My interface with feign is as follows:

@FeignClient("poc-saldo")
public interface ProxyGenerico {

    @RequestMapping(method = RequestMethod.GET)
    String getHttpResponse();

}

I can list the services available in kubernetes within the application as follows:

@RestController
public class RestTest {

    @Autowired
    private DiscoveryClient discoveryClient;

    @Autowired
    private ProxyGenerico proxyGenerico;

    @GetMapping("/services")
    public ResponseEntity<?> services()  {
        return new ResponseEntity<Object>(discoveryClient.getServices(), HttpStatus.OK);
    }

    @GetMapping("/pocsaldo")
    public ResponseEntity<?> gitlab()  {

        return new ResponseEntity<Object>(proxyGenerico.getHttpResponse(), HttpStatus.OK);
    }

}

And in this list I have several services among them the service I want to access called poc-balance, the return json looks like the following:

[

    "poc-deposito",
    "poc-saldo",
    "sonarqube",
    "sql-server-sonar",
    "zookeeper",
    "gitlab"

]

To complement the list follow my dependencies:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-kubernetes</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-kubernetes-ribbon</artifactId>
</dependency>

The discoveryClient.getInstances ("poc-saldo") command returns:

[
    {
        "instanceId": "32a4db0d-0549-11ea-8850-e0d55ef66cf8",
        "serviceId": "poc-saldo",
        "secure": false,
        "metadata": {
            "helm.sh/chart": "spring-boot-app-0.1.23",
            "port.http": "8080",
            "app.kubernetes.io/managed-by": "Tiller",
            "app.kubernetes.io/name": "poc-saldo",
            "app.kubernetes.io/instance": "banco-digital-poc-saldo",
            "app.kubernetes.io/version": "1.0"
        },
        "port": 8080,
        "host": "10.42.0.60",
        "scheme": "http://",
        "uri": "http://10.42.0.60:8080"
    }
]

Can you think of where the problem might be?


Solution

  • Long story short, Spring Cloud Feign works perfectly fine with Spring Cloud Kubernetes as of July 2021. I've moved a Spring Cloud Feign project from using Spring Cloud Netflix to Spring Cloud Kubernetes and no change in the Feign interfaces was required. I just removed the previous Service Registry (Eureka) dependency from my build.gradle file and added:

    implementation 'org.springframework.cloud:spring-cloud-starter-kubernetes-client-all'
    

    The support for Spring Cloud LoadBalancer which is now used by Spring Cloud Feign was added to Spring Cloud Kubernetes and Ribbon was removed as of Spring Cloud 2020.0. (aka Ilford), therefore there is no need to exclude it anymore.

    Beyond that, the only change required in the whole codebase was annotating the Spring Boot application class with @EnableDiscoveryClient to enable K8s-native Service Discovery:

    @EnableFeignClients
    @EnableDiscoveryClient
    @SpringBootApplication
    public class BookApplication {
    

    Spring Cloud Feign uses Spring Cloud LoadBalancer which when running on Kubernetes leverages Discovery Client for Kubernetes to check for service instances. As a result, it only chooses from instances that are up and running. The only requirement is to align the Kubernetes service name with spring.application.name property. Just like that:

    application.properties(yaml):

    spring.application.name=library-book-service
    

    combined with the following Kubernetes configuration

    kubectl get svc:

    NAME                       TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)             AGE
    library-book-service       ClusterIP   10.100.200.235   <none>        8080/TCP            5d21h