Spring Boot Microservice Communication on Kubernetes with Feign Clients

Peter Conrey
5 min readFeb 4, 2021

I was recently tasked with creating a series of Spring Boot microservices that would run on a Kubernetes cluster. For inter-service communication, I chose to use Spring feign clients. I had used them on a similar project in the past, and I really liked their simplicity and ease of configuration and use.

In this post, we’re going to create two simple RESTful web services in Spring Boot, use Spring’s @FeignClient annotation to provide a mechanism for one service to call the other, then deploy them to Kubernetes running in Docker Desktop. This article assumes that you have some understanding of creating a Spring Boot starter project. If you’re not sure how to do that, I recommend reading this first: https://spring.io/guides/gs/spring-boot/

Here’s what you’ll need to follow along:

Once you have these installed and set up, create two Spring Boot starter projects. You can do this in your IDE, or with https://start.spring.io/. Name one of your projects “hello”, and the other “goodbye”.

Goodbye Service

The first service we’ll build is the goodbye services. The Goodbye service is a simple one-endpoint microservice which returns the word “goodbye”. Here are the dependencies I have in my “goodbye” project:

Step 1:

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

Step 2:

Create the application.yaml file in src/main/resources. The only thing you’ll need in this file is:

server:
port: 9800

You could also do this in application.properties if you prefer that format.

Step 3:

Create the GoodbyeApplication.java file in src/main/java under the primary namespace of the application (If you used your IDE or start.spring.io, you should already have this file):

package com.example.goodbye;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication()
public class GoodbyeApplication {
public static void main(String[] args) {
SpringApplication.run(GoodbyeApplication.class, args);
}
}

Step 4:

Create a simple controller class in the same package:

package com.example.goodbye;import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController()
public class GoodbyeController {
@GetMapping("/saygoodbye")
public String sayGoodbye() {
return "goodbye!";
}
}

Step 5:

That’s it for goodbye. You should be able to navigate to the project directory and run the app:

> mvn clean spring-boot:run

Confirm your application works by opening a browser window and navigating to http://localhost:9800/saygoodbye. You should get a simple response:

goodbye!

Hello Service

The hello service is very similar to goodbye, but we’ll need to add a few more dependencies and a little more code.

Step 6:

Add the same dependencies to your hello project with the following additions:

<properties>
<java.version>11</java.version>
<spring.cloud>2020.0.1</spring.cloud>
<spring.cloud.kubernetes>1.1.7.RELEASE</spring.cloud.kubernetes>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-all</artifactId>
<version>${spring.cloud.kubernetes}</version>
</dependency>
</dependencies>

Step 7:

We’ll also need a little more information in our application.yaml file:

server:
port: 9900

spring:
cloud:
kubernetes:
loadbalancer:
mode: service

Step 8:

We’re going to need a new class to facilitate communications with the goodbye service. Here is where we’ll implement our feign client:

package com.example.hello;import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
@FeignClient(name = "goodbye-service")
public interface GoodbyeClient {
@GetMapping("/saygoodbye")
String sayGoodbye();
}

In Spring, a feign client is just an interface that matches the API of the goodbye service. It’s important to note that the value in the name attribute of the @FeignClient annotation must match the name of the service we’ll create shortly in Kubernetes.

Step 9:

Our HelloApplication.java and HelloController.java files are very similar to the ones we did above.

HelloApplication.java

Note the addition of two new annotations:

package com.example.hello;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients

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

HelloController.java

Here we add an @Autowired reference to our GoodbyeClient and a new endpoint, /saygoodbye, that we’ll pass through to the goodbye service using our FeignClient.

package com.example.hello;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController()
public class HelloController {
@Autowired
GoodbyeClient goodbye;
@GetMapping("/sayhello")
public String sayHello() {
return "Hello!";
}
@GetMapping("/saygoodbye")
public String sayGoodbye() {
return this.goodbye.sayGoodbye();
}

}

Step 10:

The last thing we’ll need to create is our Kubernetes deployment files for each project. In the root of each of your projects, create a folder named k8s. Inside each folder, create a file named deployment.yaml. This file will contain all of the information we’ll need to create our pods, services, and deployments on Kubernetes.

For the goodbye service, put the following in your deployment.yaml file:

apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: goodbye-service
name: goodbye-service
spec:
replicas: 1
selector:
matchLabels:
app: goodbye-service
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: goodbye-service
spec:
containers:
- image: goodbye:0.0.1-SNAPSHOT
name: goodbye-service
resources: {}
status: {}
---
apiVersion: v1
kind: Service
metadata:
creationTimestamp: null
labels:
app: goodbye-service
name: goodbye-service
spec:
ports:
- name: 9800-9800
port: 9800
protocol: TCP
targetPort: 9800
selector:
app: goodbye-service
type: LoadBalancer
status:
loadBalancer: {}

This will create a deployment of the goodbye service and expose it on port 9800 on your localhost.

Then do the same for the hello service:

apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: hello-service
name: hello-service
spec:
replicas: 1
selector:
matchLabels:
app: hello-service
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: hello-service
spec:
containers:
- image: hello:0.0.1-SNAPSHOT
name: hello-service
resources: {}
status: {}
---
apiVersion: v1
kind: Service
metadata:
creationTimestamp: null
labels:
app: hello-service
name: hello-service
spec:
ports:
- name: 9900-9900
port: 9900
protocol: TCP
targetPort: 9900
selector:
app: hello-service
type: LoadBalancer
status:
loadBalancer: {}

Putting It All Together

Now we’re ready to deploy our applications to kubernetes. In Docker Desktop, open the Dashboard, then click on the settings button at the top right (the gear icon). Select the Kubernetes menu option on the left and make sure the Enable Kubernetes option is selected. You may need to restart Docker Desktop to make it take effect.

Deploy The Services

Once you have kubernetes running, open a terminal window and navigate to your goodbye project folder. Type the following command:

> kubectl apply -f k8s/deployment.yaml

If everything is set up correctly, you should get something similar to this output:

deployment.apps/goodbye-service created
service/goodbye-service created

You can confirm that your goodbye service is running with:

> kubectl get services

And you should see (the IP address, second port, and time may differ):

goodbye-service LoadBalancer 10.1.2.3 localhost 9800:30870/TCP 1m

Finally, repeat the deployment steps above for the hello service:

> kubectl get servicesgoodbye-service LoadBalancer 10.1.2.3 localhost 9800:30870/TCP 1m
hello-service LoadBalancer 10.1.2.4 localhost 9900:30658/TCP 1m

Try It Out!

Open a browser window and navigate to the sayhello endpoint in the hello service:

http://localhost:9900/sayhello

You should get the response hello in the browser window. Finally, we’ll confirm that our FeignClient communication is working correctly:

http://localhost:9900/saygoodbye

Here we’re calling the hello service, which is passing the call on to the goodbye service. If all went correctly, you should see the response goodbye! in the browser window.

Conclusion

Congratulations! You’ve just created two microservices that can talk to each other, using a minimal amount of code and configuration, and deployed them to Kubernetes.

--

--