Introduction Kubernetes Native Spring apps on Quarkus for Beginners

Introduction Kubernetes Native Spring apps on Quarkus for Beginners

Ever wondered how you could create truly micro microservices using your favorite SpringAPIs? Ever wondered how to leverage your Spring skills to build serverless apps? Have you been left wanting to leverage the groundbreaking capabilities of GraalVM-native images without sacrificing your hard-earned Spring knowledge? This session will show you how Quarkus speaks Spring by live coding a Quarkus application using some of the most popular Spring features, which will then be converted to GraalVM-native image and deployed and scaled up in milliseconds on Kubernetes and OpenShift.

Kubernetes Tutorial For Beginners with Java and Spring Boot

Kubernetes Tutorial For Beginners with Java and Spring Boot

Kubernetes Tutorial For Beginners with Java and Spring Boot. Kubernetes for Java Developers on Google Cloud. Kubernetes, Docker & Java Spring Boot Microservices on Google Kubernetes Engine

Kubernetes For Beginners with Spring Boot. Full 10 Hour Course - http://learnstartup.net/p/_Z9mq8Rh

Guide to Spring Cloud Kubernetes for Beginners

Guide to Spring Cloud Kubernetes for Beginners

Guide to Spring Cloud Kubernetes for Beginners

1. Overview

When we build a microservices solution, both Spring Cloud and Kubernetes are optimal solutions, as they provide components for resolving the most common challenges. However, if we decide to choose Kubernetes as the main container manager and deployment platform for our solution, we can still use Spring Cloud's interesting features mainly through the Spring Cloud Kubernetes project.

In this tutorial, we’ll:

  • Install Minikube on our local machine
  • Develop a microservices architecture example with two independent Spring Boot applications communicating through REST
  • Set up the application on a one-node cluster using Minikube
  • Deploy the application using YAML config files
2. Scenario

In our example, we're using the scenario of travel agents offering various deals to clients who will query the travel agents service from time to time. We'll use it to demonstrate:

  • service discovery through Spring Cloud Kubernetes
  • configuration management and injecting Kubernetes ConfigMaps and secrets to application pods using Spring Cloud Kubernetes Config
  • load balancing using Spring Cloud Kubernetes Ribbon
3. Environment Setup

First and foremost, we need to install Minikube on our local machine and preferably a VM driver such as VirtualBox. It's also recommended to look at Kubernetes and its main features before following this environment setup.

Let's start the local single-node Kubernetes cluster:

minikube start --vm-driver=virtualbox

This command creates a Virtual Machine that runs a Minikube cluster using the VirtualBox driver. The default context in kubectl will now be minikube. However, to be able to switch between contexts, we use:

kubectl config use-context minikube

After starting Minikube, we can connect to the Kubernetes dashboard to access the logs and monitor our services, pods, ConfigMaps, and Secrets easily:

minikube dashboard

3.1. Deployment

Firstly, let's get our example from GitHub.

At this point, we can either run the “deployment-travel-client.sh” script from the parent folder, or else execute each instruction one by one to get a good grasp of the procedure:

### build the repository
mvn clean install
 
### set docker env
eval $(minikube docker-env)
 
### build the docker images on minikube
cd travel-agency-service
docker build -t travel-agency-service .
cd ../client-service
docker build -t client-service .
cd ..
 
### secret and mongodb
kubectl delete -f travel-agency-service/secret.yaml
kubectl delete -f travel-agency-service/mongo-deployment.yaml
 
kubectl create -f travel-agency-service/secret.yaml
kubectl create -f travel-agency-service/mongo-deployment.yaml
 
### travel-agency-service
kubectl delete -f travel-agency-service/travel-agency-deployment.yaml
kubectl create -f travel-agency-service/travel-agency-deployment.yaml
 
### client-service
kubectl delete configmap client-service
kubectl delete -f client-service/client-service-deployment.yaml
 
kubectl create -f client-service/client-config.yaml
kubectl create -f client-service/client-service-deployment.yaml
 
# Check that the pods are running
kubectl get pods
4. Service Discovery

This project provides us with an implementation for the ServiceDiscovery interface in Kubernetes. In a microservices environment, there are usually multiple pods running the same service.** Kubernetes exposes the service as a collection of endpoints** that can be fetched and reached from within a Spring Boot Application running in a pod in the same Kubernetes cluster.

For instance, in our example, we have multiple replicas of the travel agent service, which is accessed from our client service as http://travel-agency-service:8080. However, this internally would translate into accessing different pods such as travel-agency-service-7c9cfff655-4hxnp.

Spring Cloud Kubernetes Ribbon uses this feature to load balance between the different endpoints of a service.

We can easily use Service Discovery by adding the spring-cloud-starter-kubernetes dependency on our client application:

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

Also, we should add @EnableDiscoveryClient and inject the DiscoveryClient into the ClientController by using @Autowired in our class:

@SpringBootApplication
@EnableDiscoveryClient
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
@RestController
public class ClientController {
    @Autowired
    private DiscoveryClient discoveryClient;
}
5. ConfigMaps

Typically, microservices require some kind of configuration management. For instance, in Spring Cloud applications, we would use a Spring Cloud Config Server.

However, we can achieve this by using ConfigMaps provided by Kubernetes – provided that we intend to use it for non-sensitive, unencrypted information only. Alternatively, if the information we want to share is sensitive, then we should opt to use Secrets instead.

In our example, we're using ConfigMaps on the client-service Spring Boot application. Let's create a client-config.yaml file to define the ConfigMap of the client-service:

apiVersion: v1 by d
kind: ConfigMap
metadata:
  name: client-service
data:
  application.properties: |-
    bean.message=Testing reload! Message from backend is: %s <br/> Services : %s

**It's important that the name of the ConfigMap matches the name of the application **as specified in our “application.properties” file. In this case, it's client-service. Next, we should create the ConfigMap for client-service on Kubernetes:

kubectl create -f client-config.yaml

Now, let's create a configuration class ClientConfig with the @Configuration and @ConfigurationProperties and inject into the ClientController:

@Configuration
@ConfigurationProperties(prefix = "bean")
public class ClientConfig {
 
    private String message = "Message from backend is: %s <br/> Services : %s";
 
    // getters and setters
}
@RestController
public class ClientController {
 
    @Autowired
    private ClientConfig config;
 
    @GetMapping
    public String load() {
        return String.format(config.getMessage(), "", "");
    }
}

If we don't specify a ConfigMap, then we should expect to see the default message, which is set in the class. However, when we create the ConfigMap, this default message gets overridden by that property.

Additionally, every time we decide to update the ConfigMap, the message on the page changes accordingly:

kubectl edit configmap client-service
6. Secrets

Let's look at how Secrets work by looking at the specification of MongoDB connection settings in our example. We're going to create environment variables on Kubernetes, which will then be injected into the Spring Boot application.

6.1. Create a Secret

The first step is to create a secret.yaml file, encoding the username and password to Base 64:

apiVersion: v1
kind: Secret
metadata:
  name: db-secret
data:
  username: dXNlcg==
  password: cDQ1NXcwcmQ=

Let's apply the Secret configuration on the Kubernetes cluster:

kubectl apply -f secret.yaml

6.2. Create a MongoDB Service

We should now create the MongoDB service and the deployment travel-agency-deployment.yaml file. In particular, in the deployment part, we'll use the Secret username and password that we defined previously:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: mongo
spec:
  replicas: 1
  template:
    metadata:
      labels:
        service: mongo
      name: mongodb-service
    spec:
      containers:
      - args:
        - mongod
        - --smallfiles
        image: mongo:latest
        name: mongo
        env:
          - name: MONGO_INITDB_ROOT_USERNAME
            valueFrom:
              secretKeyRef:
                name: db-secret
                key: username
          - name: MONGO_INITDB_ROOT_PASSWORD
            valueFrom:
              secretKeyRef:
                name: db-secret
                key: password

By default, the mongo:latest image will create a user with username and password on a database named admin.

6.3. Setup MongoDB on Travel Agency Service

It's important to update the application properties to add the database related information. While we can freely specify the database name admin, here we're hiding the most sensitive information such as the username and the password:

spring.cloud.kubernetes.reload.enabled=true
spring.cloud.kubernetes.secrets.name=db-secret
spring.data.mongodb.host=mongodb-service
spring.data.mongodb.port=27017
spring.data.mongodb.database=admin
spring.data.mongodb.username=${MONGO_USERNAME}
spring.data.mongodb.password=${MONGO_PASSWORD}

Now, let's take a look at our travel-agency-deployment property file to update the services and deployments with the username and password information required to connect to the mongodb-service.

Here's the relevant section of the file, with the part related to the MongoDB connection:

env:
  - name: MONGO_USERNAME
    valueFrom:
      secretKeyRef:
        name: db-secret
        key: username
  - name: MONGO_PASSWORD
    valueFrom:
      secretKeyRef:
        name: db-secret
        key: password
  1. Communication with Ribbon
    In a microservices environment, we generally need the list of pods where our service is replicated in order to perform load-balancing. This is accomplished by using a mechanism provided by Spring Cloud Kubernetes Ribbon. This mechanism can automatically discover and reach all the endpoints of a specific service, and subsequently, it populates a Ribbon ServerList with information about the endpoints.

Let's start by adding the spring-cloud-starter-kubernetes-ribbon dependency to our client-service pom.xml file:

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

The next step is to add the annotation @RibbonClient to our client-service application:

@RibbonClient(name = "travel-agency-service")

When the list of the endpoints is populated, the Kubernetes client will search the registered endpoints living in the current namespace/project matching the service name defined using the @RibbonClient annotation.

We also need to enable the ribbon client in the application properties:

ribbon.http.client.enabled=true
8. Additional Features

8.1. Hystrix

Hystrix helps in building a fault-tolerant and resilient application. Its main aims are fail fast and rapid recovery.

In particular, in our example, we're using Hystrix to implement the circuit breaker pattern on the client-server by annotating the Spring Boot application class with @EnableCircuitBreaker.

Additionally, we're using the fallback functionality by annotating the method TravelAgencyService.getDeals() with @HystrixCommand(). This means that in case of fallback the getFallBackName() will be called and “Fallback” message returned:

@HystrixCommand(fallbackMethod = "getFallbackName", commandProperties = { 
    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000") })
public String getDeals() {
    return this.restTemplate.getForObject("http://travel-agency-service:8080/deals", String.class);
}
 
private String getFallbackName() {
    return "Fallback";
}

8.2. Pod Health Indicator

We can take advantage of Spring Boot HealthIndicator and Spring Boot Actuator to expose health-related information to the user.

In particular, the Kubernetes health indicator provides:

  • pod name
  • IP address
  • namespace
  • service account
  • node name
  • a flag that indicates whether the Spring Boot application is internal or external to Kubernetes
9. Conclusion

In this article, we provide a thorough overview of the Spring Cloud Kubernetes project.

So why should we use it? If we root for Kubernetes as a microservices platform but still appreciate the features of Spring Cloud, then Spring Cloud Kubernetes gives us the best of both worlds.

The full source code of the example is available over on GitHub.

Build Microservice Architecture With Kubernetes, Spring Boot , and Docker

Build Microservice Architecture With Kubernetes, Spring Boot , and Docker

In this article we learn how to start the Spring Boot microservice project and run it fast with Kubernetes and Docker

The topics covered in this article are:

  • Using Spring Boot 2.0 in cloud-native development

  • Providing service discovery for all microservices using a Spring Cloud Kubernetes project

  • Injecting configuration settings into application pods using Kubernetes Config Maps and Secrets

  • Building application images using Docker and deploying them on Kubernetes using YAML configuration files

  • Using Spring Cloud Kubernetes together with a Zuul proxy to expose a single Swagger API documentation for all microservices

Spring Cloud and Kubernetes may be threatened as competitive solutions when you build a microservices environment. Such components like Eureka, Spring Cloud Config, or Zuul provided by Spring Cloud may be replaced by built-in Kubernetes objects like services, config maps, secrets, or ingresses. But even if you decide to use Kubernetes components instead of Spring Cloud, you can take advantage of some interesting features provided throughout the whole Spring Cloud project.

The one really interesting project that helps us in development is Spring Cloud Kubernetes. Although it is still in the incubation stage, it is definitely worth dedicating some time to it. It integrates Spring Cloud with Kubernetes. I'll show you how to use an implementation of the discovery client, inter-service communication with the Ribbon client, and Zipkin discovery using Spring Cloud Kubernetes.

Before we proceed to the source code, let's take a look at the following diagram. It illustrates the architecture of our sample system. It is quite similar to the architecture presented in the mentioned article about microservices on Spring Cloud. There are three independent applications (employee-service, department-service, organization-service), which communicate with each other through a REST API. These Spring Boot microservices use some built-in mechanisms provided by Kubernetes: config maps and secrets for distributed configuration, etcd for service discovery, and ingresses for the API gateway.

Let's proceed to the implementation. Currently, the newest stable version of Spring Cloud is Finchley.RELEASE. This version of spring-cloud-dependencies should be declared as a BOM for dependency management.

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

Spring Cloud Kubernetes is not released under Spring Cloud Release Trains, so we need to explicitly define its version. Because we use Spring Boot 2.0 we have to include the newest SNAPSHOT version of spring-cloud-kubernetes artifacts, which is 0.3.0.BUILD-SNAPSHOT.

The source code of sample applications presented in this article is available on GitHub in this repository.

Pre-Requirements

In order to be able to deploy and test our sample microservices, we need to prepare a development environment. We can realize that in the following steps:

  • You need at least a single node cluster instance of Kubernetes (Minikube) or Openshift (Minishift) running on your local machine. You should start it and expose the embedded Docker client provided by both of them. The detailed instructions for Minishift may be found in my Quick guide to deploying Java apps on OpenShift. You can also use that description to run Minikube — just replace word "minishift" with "minikube." In fact, it does not matter if you choose Kubernetes or Openshift — the next part of this tutorial will be applicable for both of them.

  • Spring Cloud Kubernetes requires access to the Kubernetes API in order to be able to retrieve a list of addresses for pods running for a single service. If you use Kubernetes, you should just execute the following command:

$ kubectl create clusterrolebinding admin --clusterrole=cluster-admin --serviceaccount=default:default

If you deploy your microservices on Minishift, you should first enable admin-user add-on, then log in as a cluster admin and grant the required permissions.

$ minishift addons enable admin-user
$ oc login -u system:admin
$ oc policy add-role-to-user cluster-reader system:serviceaccount:myproject:default
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mongodb
  labels:
    app: mongodb
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mongodb
  template:
    metadata:
      labels:
        app: mongodb
    spec:
      containers:
      - name: mongodb
        image: mongo:latest
        ports:
        - containerPort: 27017
        env:
        - name: MONGO_INITDB_DATABASE
          valueFrom:
            configMapKeyRef:
              name: mongodb
              key: database-name
        - name: MONGO_INITDB_ROOT_USERNAME
          valueFrom:
            secretKeyRef:
              name: mongodb
              key: database-user
        - name: MONGO_INITDB_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mongodb
              key: database-password
---
apiVersion: v1
kind: Service
metadata:
  name: mongodb
  labels:
    app: mongodb
spec:
  ports:
  - port: 27017
    protocol: TCP
  selector:
    app: mongodb
1. Inject the Configuration With Config Maps and Secrets

When using Spring Cloud, the most obvious choice for realizing a distributed configuration in your system is Spring Cloud Config. With Kubernetes, you can use Config Map. It holds key-value pairs of configuration data that can be consumed in pods or used to store configuration data. It is used for storing and sharing non-sensitive, unencrypted configuration information. To use sensitive information in your clusters, you must use Secrets. Use of both these Kubernetes objects can be perfectly demonstrated based on the example of MongoDB connection settings. Inside a Spring Boot application, we can easily inject it using environment variables. Here's a fragment of application.yml file with URI configuration.

spring:
  data:
    mongodb:
      uri: mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@mongodb/${MONGO_DATABASE}

While username and password are sensitive fields, a database name is not, so we can place it inside the config map.

apiVersion: v1
kind: ConfigMap
metadata:
  name: mongodb
data:
  database-name: microservices

Of course, username and password are defined as secrets.

apiVersion: v1
kind: Secret
metadata:
  name: mongodb
type: Opaque
data:
  database-password: MTIzNDU2
  database-user: cGlvdHI=

To apply the configuration to the Kubernetes cluster, we run the following commands.

$ kubectl apply -f kubernetes/mongodb-configmap.yaml
$ kubectl apply -f kubernetes/mongodb-secret.yaml

After that, we should inject the configuration properties into the application's pods. When defining the container configuration inside the Deployment YAML file, we have to include references to environment variables and secrets, as shown below.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: employee
  labels:
    app: employee
spec:
  replicas: 1
  selector:
    matchLabels:
      app: employee
  template:
    metadata:
      labels:
        app: employee
    spec:
      containers:
      - name: employee
        image: piomin/employee:1.0
        ports:
        - containerPort: 8080
        env:
        - name: MONGO_DATABASE
          valueFrom:
            configMapKeyRef:
              name: mongodb
              key: database-name
        - name: MONGO_USERNAME
          valueFrom:
            secretKeyRef:
              name: mongodb
              key: database-user
        - name: MONGO_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mongodb
              key: database-password
2. Building Service Discovery With Kubernetes

We are usually running microservices on Kubernetes using Docker containers. One or more containers are grouped by pods, which are the smallest deployable units created and managed in Kubernetes. A good practice is to run only one container inside a single pod. If you would like to scale up your microservice, you would just have to increase the number of running pods. All running pods that belong to a single microservice are logically grouped with Kubernetes Service. This service may be visible outside the cluster and is able to load balance incoming requests between all running pods. The following service definition groups all pods labeled with the field app equal to employee.

apiVersion: v1
kind: Service
metadata:
  name: employee
  labels:
    app: employee
spec:
  ports:
  - port: 8080
    protocol: TCP
  selector:
    app: employee

Service can be used to access the application outsidethe Kubernetes cluster or for inter-service communication inside a cluster. However, the communication between microservices can be implemented more comfortably with Spring Cloud Kubernetes. First, we need to include the following dependency in the project pom.xml.

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes</artifactId>
<version>0.3.0.BUILD-SNAPSHOT</version>
</dependency>

Then we should enable the discovery client for an application, the same as we have always done for discovery in Spring Cloud Netflix Eureka. This allows you to query Kubernetes endpoints (services) by name. This discovery feature is also used by Spring Cloud Kubernetes Ribbon or Zipkin projects to fetch, respectively, the list of the pods defined for a microservice to be load balanced or the Zipkin servers available to send the traces or spans.

@SpringBootApplication
@EnableDiscoveryClient
@EnableMongoRepositories
@EnableSwagger2
public class EmployeeApplication {
 public static void main(String[] args) {
  SpringApplication.run(EmployeeApplication.class, args);
 }
 // ...
}

The last important thing in this section is to guarantee that the Spring application name will be exactly the same as the Kubernetes service name for the application. For the application employee-service, it is employee.

spring:
  application:
    name: employee
3. Building Microservices Using Docker and Deploying on Kubernetes

There is nothing unusual in our sample microservices. We have included some standard Spring dependencies for building REST-based microservices, integrating with MongoDB, and generating API documentation using Swagger2.

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

In order to integrate with MongoDB, we should create an interface that extends standard Spring Data CrudRepository.

public interface EmployeeRepository extends CrudRepository {
 List findByDepartmentId(Long departmentId);
 List findByOrganizationId(Long organizationId);
}

The entity class should be annotated with Mongo @Document and a primary key field with @Id.

@Document(collection = "employee")
public class Employee {
 @Id
 private String id;
 private Long organizationId;
 private Long departmentId;
 private String name;
 private int age;
 private String position;
 // ...
}

The repository bean has been injected to the controller class. Here's the full implementation of our REST API inside employee-service.

@RestController
public class EmployeeController {
 private static final Logger LOGGER = LoggerFactory.getLogger(EmployeeController.class);
 @Autowired
 EmployeeRepository repository;
 @PostMapping("/")
 public Employee add(@RequestBody Employee employee) {
  LOGGER.info("Employee add: {}", employee);
  return repository.save(employee);
 }
 @GetMapping("/{id}")
 public Employee findById(@PathVariable("id") String id) {
  LOGGER.info("Employee find: id={}", id);
  return repository.findById(id).get();
 }
 @GetMapping("/")
 public Iterable findAll() {
  LOGGER.info("Employee find");
  return repository.findAll();
 }
 @GetMapping("/department/{departmentId}")
 public List findByDepartment(@PathVariable("departmentId") Long departmentId) {
  LOGGER.info("Employee find: departmentId={}", departmentId);
  return repository.findByDepartmentId(departmentId);
 }
 @GetMapping("/organization/{organizationId}")
 public List findByOrganization(@PathVariable("organizationId") Long organizationId) {
  LOGGER.info("Employee find: organizationId={}", organizationId);
  return repository.findByOrganizationId(organizationId);
 }
}

In order to run our microservices on Kubernetes, we should first build the whole Maven project with the mvn clean install command. Each microservice has a Dockerfile placed in the root directory. Here's the Dockerfile definition for employee-service.

FROM openjdk:8-jre-alpine
ENV APP_FILE employee-service-1.0-SNAPSHOT.jar
ENV APP_HOME /usr/apps
EXPOSE 8080
COPY target/$APP_FILE $APP_HOME/
WORKDIR $APP_HOME
ENTRYPOINT ["sh", "-c"]
CMD ["exec java -jar $APP_FILE"]

Let's build Docker images for all three sample microservices.

$ cd employee-service
$ docker build -t piomin/employee:1.0 .
$ cd department-service
$ docker build -t piomin/department:1.0 .
$ cd organization-service
$ docker build -t piomin/organization:1.0 .

The last step is to deploy Docker containers with applications on Kubernetes. To do that, just execute the commands kubectl apply on YAML configuration files. The sample deployment file for employee-service has been demonstrated in step 1. All required deployment fields are available inside the project repository in the kubernetes directory.

$ kubectl apply -f kubernetes\employee-deployment.yaml
$ kubectl apply -f kubernetes\department-deployment.yaml
$ kubectl apply -f kubernetes\organization-deployment.yaml
4. Communication Between Microservices With Spring Cloud Kubernetes Ribbon

All the microservices are deployed on Kubernetes. Now, it's worth it to discuss some aspects related to inter-service communication. The application employee-service, in contrast to other microservices, did not invoke any other microservices. Let's take a look at other microservices that call the API exposed by employee-service and communicate between each other ( organization-service calls department-service API).

First, we need to include some additional dependencies in the project. We use Spring Cloud Ribbon and OpenFeign. Alternatively, you can also use Spring@LoadBalancedRestTemplate.

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-ribbon</artifactId>
<version>0.3.0.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

Here's the main class of department-service. It enables Feign client using the @EnableFeignClients annotation. It works the same as with discovery based on Spring Cloud Netflix Eureka. OpenFeign uses Ribbon for client-side load balancing. Spring Cloud Kubernetes Ribbon provides some beans that force Ribbon to communicate with the Kubernetes API through Fabric8 KubernetesClient.

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@EnableMongoRepositories
@EnableSwagger2
public class DepartmentApplication {
 public static void main(String[] args) {
  SpringApplication.run(DepartmentApplication.class, args);
 }
 // ...
}

Here's the implementation of Feign client for calling the method exposed by employee-service.

@FeignClient(name = "employee")
public interface EmployeeClient {
 @GetMapping("/department/{departmentId}")
 List findByDepartment(@PathVariable("departmentId") String departmentId);
}

Finally, we have to inject Feign client's beans into the REST controller. Now, we may call the method defined inside EmployeeClient, which is equivalent to calling REST endpoints.

@RestController
public class DepartmentController {
 private static final Logger LOGGER = LoggerFactory.getLogger(DepartmentController.class);
 @Autowired
 DepartmentRepository repository;
 @Autowired
 EmployeeClient employeeClient;
 // ...
 @GetMapping("/organization/{organizationId}/with-employees")
 public List findByOrganizationWithEmployees(@PathVariable("organizationId") Long organizationId) {
  LOGGER.info("Department find: organizationId={}", organizationId);
  List departments = repository.findByOrganizationId(organizationId);
  departments.forEach(d -> d.setEmployees(employeeClient.findByDepartment(d.getId())));
  return departments;
 }
}
5. Building API Gateway Using Kubernetes Ingress

Ingress is a collection of rules that allow incoming requests to reach the downstream services. In our microservices architecture, ingress is playing the role of an API gateway. To create it, we should first prepare a YAML description file. The descriptor file should contain the hostname under which the gateway will be available and mapping rules to the downstream services.

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: gateway-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  backend:
    serviceName: default-http-backend
    servicePort: 80
  rules:
  - host: microservices.info
    http:
      paths:
      - path: /employee
        backend:
          serviceName: employee
          servicePort: 8080
      - path: /department
        backend:
          serviceName: department
          servicePort: 8080
      - path: /organization
        backend:
          serviceName: organization
          servicePort: 8080

You have to execute the following command to apply the configuration above to the Kubernetes cluster.

$ kubectl apply -f kubernetes\ingress.yaml

To test this solution locally, we have to insert the mapping between the IP address and hostname set in the ingress definition inside the hosts file, as shown below. After that, we can test services through ingress using defined hostname just like that: http://microservices.info/employee.

192.168.99.100 microservices.info

You can check the details of the created ingress just by executing the command kubectl describe ing gateway-ingress.

6. Enabling API Specification on the Gateway Using Swagger2

What if we would like to expose a single Swagger documentation for all microservices deployed on Kubernetes? Well, here things are getting complicated... We can run a container with Swagger UI, and map all paths exposed by the ingress manually, but it is not a good solution...

In that case, we can use Spring Cloud Kubernetes Ribbon one more time, this time together with Spring Cloud Netflix Zuul. Zuul will act as a gateway only for serving the Swagger API.
Here's the list of dependencies used in my gateway-service project.

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes</artifactId>
<version>0.3.0.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-ribbon</artifactId>
<version>0.3.0.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>

Kubernetes discovery client will detect all services exposed on the cluster. We would like to display documentation only for our three microservices. That's why I defined the following routes for Zuul.

zuul:
  routes:
    department:
      path: /department/**
    employee:
      path: /employee/**
    organization:
      path: /organization/**

Now we can use the ZuulProperties bean to get the routes' addresses from Kubernetes discovery and configure them as Swagger resources, as shown below.

@Configuration
public class GatewayApi {
 @Autowired
 ZuulProperties properties;
 @Primary
 @Bean
 public SwaggerResourcesProvider swaggerResourcesProvider() {
  return () -> {
   List resources = new ArrayList();
   properties.getRoutes().values().stream()
   .forEach(route -> resources.add(createResource(route.getId(), "2.0")));
   return resources;
  };
 }
 private SwaggerResource createResource(String location, String version) {
  SwaggerResource swaggerResource = new SwaggerResource();
  swaggerResource.setName(location);
  swaggerResource.setLocation("/" + location + "/v2/api-docs");
  swaggerResource.setSwaggerVersion(version);
  return swaggerResource;
 }
}

The application gateway-service should be deployed on the cluster the same as the other applications. You can see the list of running services by executing the command kubectl get svc. Swagger documentation is available under the address http://192.168.99.100:31237/swagger-ui.html.

Learn More

Thanks for reading !

Originally published by Piotr Mińkowski at dzone.com