Guida a Spring Cloud Kubernetes

1. Panoramica

Quando creiamo una soluzione per microservizi, sia Spring Cloud che Kubernetes sono soluzioni ottimali, poiché forniscono componenti per risolvere le sfide più comuni. Tuttavia, se decidiamo di scegliere Kubernetes come principale container manager e piattaforma di distribuzione per la nostra soluzione, possiamo comunque utilizzare le interessanti funzionalità di Spring Cloud principalmente attraverso il progetto Spring Cloud Kubernetes.

Questo progetto relativamente nuovo fornisce senza dubbio una facile integrazione con Kubernetes per le applicazioni Spring Boot. Prima di iniziare, potrebbe essere utile esaminare come distribuire un'applicazione Spring Boot su Minikube, un ambiente Kubernetes locale .

In questo tutorial, faremo:

  • Installa Minikube sulla nostra macchina locale
  • Sviluppa un esempio di architettura di microservizi con due applicazioni Spring Boot indipendenti che comunicano tramite REST
  • Configura l'applicazione su un cluster a un nodo utilizzando Minikube
  • Distribuisci l'applicazione utilizzando i file di configurazione YAML

2. Scenario

Nel nostro esempio, utilizziamo lo scenario degli agenti di viaggio che offrono varie offerte ai clienti che di volta in volta interrogheranno il servizio degli agenti di viaggio. Lo useremo per dimostrare:

  • rilevamento dei servizi tramite Spring Cloud Kubernetes
  • gestione della configurazione e inserimento di Kubernetes ConfigMaps e segreti nei pod dell'applicazione utilizzando Spring Cloud Kubernetes Config
  • bilanciamento del carico utilizzando Spring Cloud Kubernetes Ribbon

3. Configurazione dell'ambiente

Innanzitutto, dobbiamo installare Minikube sulla nostra macchina locale e preferibilmente un driver VM come VirtualBox. Si consiglia inoltre di esaminare Kubernetes e le sue caratteristiche principali prima di seguire questa configurazione dell'ambiente.

Avviamo il cluster Kubernetes a nodo singolo locale:

minikube start --vm-driver=virtualbox

Questo comando crea una macchina virtuale che esegue un cluster Minikube utilizzando il driver VirtualBox. Il contesto predefinito in kubectl sarà ora minikube . Tuttavia, per poter passare da un contesto all'altro, utilizziamo:

kubectl config use-context minikube

Dopo aver avviato Minikube, possiamo connetterci alla dashboard di Kubernetes per accedere ai log e monitorare facilmente i nostri servizi, pod, ConfigMaps e Secrets:

minikube dashboard 

3.1. Distribuzione

Innanzitutto, prendiamo il nostro esempio da GitHub.

A questo punto, possiamo eseguire lo script "deployment-travel-client.sh" dalla cartella principale, oppure eseguire ciascuna istruzione una per una per avere una buona comprensione della procedura:

### 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

Questo progetto ci fornisce un'implementazione per l' interfaccia ServiceDiscovery in Kubernetes. In un ambiente di microservizi, in genere sono presenti più pod che eseguono lo stesso servizio. Kubernetes espone il servizio come una raccolta di endpoint che possono essere recuperati e raggiunti da un'applicazione Spring Boot in esecuzione in un pod nello stesso cluster Kubernetes.

Ad esempio, nel nostro esempio, abbiamo più repliche del servizio di agenzia di viaggi, a cui si accede dal nostro servizio clienti come // travel-agency-service: 8080 . Tuttavia, questo internamente si tradurrebbe nell'accesso a diversi pod come travel-agency-service-7c9cfff655-4hxnp .

Spring Cloud Kubernetes Ribbon utilizza questa funzionalità per bilanciare il carico tra i diversi endpoint di un servizio.

Possiamo facilmente utilizzare Service Discovery aggiungendo la dipendenza spring-cloud-starter-kubernetes sulla nostra applicazione client:

 org.springframework.cloud spring-cloud-starter-kubernetes 

Inoltre, dovremmo aggiungere @EnableDiscoveryClient e iniettare DiscoveryClient in ClientController utilizzando @Autowired nella nostra classe:

@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

In genere, i microservizi richiedono un qualche tipo di gestione della configurazione . Ad esempio, nelle applicazioni Spring Cloud, useremmo un server Spring Cloud Config.

Tuttavia, possiamo ottenere questo risultato utilizzando ConfigMaps fornite da Kubernetes, a condizione che intendiamo utilizzarlo solo per informazioni non sensibili e non crittografate. In alternativa, se le informazioni che vogliamo condividere sono sensibili, allora dovremmo optare per l'uso di Segreti.

Nel nostro esempio, stiamo usando ConfigMaps sull'applicazione Spring Boot del servizio client . Creiamo un client-config. yaml per definire la ConfigMap del servizio client :

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

Services : %s

È importante che il nome di ConfigMap corrisponda al nome dell'applicazione come specificato nel nostro file "application.properties". In questo caso, è il servizio client . Successivamente, dovremmo creare la ConfigMap per il servizio client su Kubernetes:

kubectl create -f client-config.yaml

Ora, creiamo una classe di configurazione ClientConfig con @Configuration e @ConfigurationProperties e inseriamo nel ClientController :

@Configuration @ConfigurationProperties(prefix = "bean") public class ClientConfig { private String message = "Message from backend is: %s

Services : %s"; // getters and setters }

@RestController public class ClientController { @Autowired private ClientConfig config; @GetMapping public String load() { return String.format(config.getMessage(), "", ""); } }

Se non specifichiamo una ConfigMap, dovremmo aspettarci di vedere il messaggio predefinito, che è impostato nella classe. Tuttavia, quando creiamo la ConfigMap, questo messaggio predefinito viene sovrascritto da quella proprietà.

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

7. 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:

 org.springframework.cloud spring-cloud-starter-kubernetes-ribbon 

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("//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
  • un flag che indica se l'applicazione Spring Boot è interna o esterna a Kubernetes

9. Conclusione

In questo articolo, forniamo una panoramica completa del progetto Spring Cloud Kubernetes.

Allora perché dovremmo usarlo? Se facciamo il root per Kubernetes come piattaforma di microservizi ma apprezziamo comunque le funzionalità di Spring Cloud, Spring Cloud Kubernetes ci offre il meglio di entrambi i mondi.

Il codice sorgente completo dell'esempio è disponibile su GitHub.