Saltar al contenido

Integración de Istio con aplicaciones BWCE

Introducción

Service Mesh es una de las «nuevas grandes cosas» en nuestros entornos PaaS. No importa si estás trabajando con K8S, Docker Swarm, nube pura con EKS o AWS, has oído hablar y probablemente intentado saber cómo se puede usar esta nueva cosa que tiene tantas ventajas porque proporciona muchas opciones para manejar la comunicación entre componentes sin afectar la lógica de los componentes. Y si has oído hablar de Service Mesh, también has oído hablar de Istio, porque esta es la «opción insignia» en este momento, incluso cuando otras opciones como Linkerd o AWS App Mesh también son una gran opción, Istio es el Service Mesh más utilizado en este momento.

Probablemente hayas visto algunos ejemplos sobre cómo integrar Istio con tus desarrollos basados en código abierto, pero ¿qué pasa si tienes muchas aplicaciones BWCE o BusinessWorks… ¿puedes usar todo este poder? ¿O vas a ser excluido de este nuevo mundo?

¡No entres en pánico! Este artículo te va a mostrar lo fácil que puedes usar Istio con tu aplicación BWCE dentro de un clúster K8S. Así que, que comience el partido… ¡EMPIEZA!

Escenario

El escenario que vamos a probar es bastante simple. Es un enfoque simple de consumidor-proveedor. Vamos a usar un simple servicio web SOAP/HTTP expuesto por un backend para mostrar que esto puede funcionar no solo con API REST elegantes, sino incluso con cualquier tráfico HTTP que podamos generar a nivel de la aplicación BWCE.

Así que, vamos a invocar un servicio que va a solicitar una respuesta de su proveedor y nos dará la respuesta. Eso es bastante fácil de configurar usando BWCE puro sin nada más.

Todo el código relacionado con este ejemplo está disponible para ti en el siguiente repositorio de GitHub: ¡Ve a buscar el código!

Pasos

Paso 1 Instalar Istio dentro de tu Clúster de Kubernetes

En mi caso, estoy usando un clúster de Kubernetes dentro de mi instalación de Docker Desktop, así que puedes hacer lo mismo o usar tu clúster de Kubernetes real, eso depende de ti. El primer paso de todos modos es instalar istio. Y para hacer eso, nada mejor que seguir los pasos dados en el taller de istio que puedes encontrar aquí: https://polarsquad.github.io/istio-workshop/install-istio/ (ACTUALIZACIÓN: Ya no disponible)

Una vez que hayas terminado, vamos a obtener el siguiente escenario en nuestro clúster de kubernetes, así que por favor, verifica que el resultado sea el mismo con los siguientes comandos:

kubectl get pods -n istio-system

Deberías ver que todos los pods están en ejecución como puedes ver en la imagen a continuación:

kubectl -n istio-system get deployment -listio=sidecar-injector

Deberías ver que hay una instancia (CURRENT = 1) disponible.

kubectl get namespace -L istio-injection

Deberías ver que ISTIO-INJECTION está habilitado para el espacio de nombres predeterminado como se muestra en la imagen a continuación:

Paso 2 Construir Aplicaciones BWCE

Ahora, como tenemos toda la infraestructura necesaria a nivel de Istio, podemos comenzar a construir nuestra aplicación y para hacer eso no tenemos que hacer nada diferente en nuestra aplicación BWCE. Eventualmente, van a ser dos aplicaciones que se comunican usando HTTP como protocolo, así que nada específico.

Esto es algo importante porque cuando usualmente hablamos de Service Mesh e Istio con clientes, siempre surge la misma pregunta: ¿Es compatible Istio en BWCE? ¿Podemos usar Istio como un protocolo para comunicar nuestra aplicación BWCE? Así que, esperan que debería existir alguna paleta o algún complemento personalizado que deberían instalar para soportar Istio. Pero nada de eso es necesario a nivel de aplicación. Y eso se aplica no solo a BWCE sino también a cualquier otra tecnología como Flogo o incluso tecnologías de código abierto porque al final Istio (y Envoy es la otra parte necesaria en esta tecnología de la que usualmente evitamos hablar cuando hablamos de Istio) funciona en modo Proxy usando uno de los patrones más usuales en contenedores que es el «patrón sidecar«.

Así que, la tecnología que está exponiendo e implementando o consumiendo el servicio no sabe nada sobre toda esta «magia» que se está ejecutando en el medio de este proceso de comunicación.

Vamos a definir las siguientes propiedades como variables de entorno como lo haríamos en caso de que no estuviéramos usando Istio:

Aplicación del proveedor:

  • PROVIDER_PORT → Puerto donde el proveedor va a escuchar las solicitudes entrantes.

Aplicación del consumidor:

  • PROVIDER_PORT → Puerto donde el host del proveedor estará escuchando.
  • PROVIDER_HOST → Host o FQDN (también conocido como nombre del servicio K8S) donde se expondrá el servicio del proveedor.
  • CONSUMER_PORT → Puerto donde el servicio del consumidor va a escuchar las solicitudes entrantes.

Así que, como puedes ver si verificas que el código de la aplicación BWCE no necesitamos hacer nada especial para soportar Istio en nuestras aplicaciones BWCE.

NOTA: Solo hay un tema importante que no está relacionado con la integración de Istio, sino con cómo BWCE llena la propiedad BW.CLOUD.HOST que nunca se traduce a la interfaz de loopback o incluso 0.0.0.0. Así que es mejor que cambies esa variable por una personalizada o que uses localhost o 0.0.0.0 para escuchar en la interfaz de loopback porque es donde el proxy de Istio va a enviar las solicitudes.

Después de eso, vamos a crear nuestros Dockerfiles para estos servicios sin nada en particular, algo similar a lo que puedes ver aquí:

NOTA: Como requisito previo, estamos usando la imagen base de Docker de BWCE llamada bwce_base.2.4.3 que corresponde a la versión 2.4.3 de BusinessWorks Container Edition.

Y ahora construimos nuestras imágenes de docker en nuestro repositorio como puedes ver en la siguiente imagen:

Paso 3: Desplegar las Aplicaciones BWCE

Ahora, cuando todas las imágenes están siendo creadas, necesitamos generar los artefactos necesarios para desplegar estas aplicaciones en nuestro Clúster. Una vez más, en este caso, nada especial tampoco en nuestro archivo YAML, como puedes ver en la imagen a continuación, vamos a definir un servicio K8S y un despliegue K8S basado en la imagen que hemos creado en el paso anterior:

Algo similar sucede con el despliegue del consumidor, como puedes ver en la imagen a continuación:

Y podemos desplegarlos en nuestro clúster K8S con los siguientes comandos:

kubectl apply -f kube/provider.yaml

kubectl apply -f kube/consumer.yaml

Ahora, deberías ver los siguientes componentes desplegados. Solo para completar todos los componentes necesarios en nuestra estructura, vamos a crear un ingreso para hacer posible ejecutar solicitudes desde fuera del clúster a esos componentes y para hacer eso vamos a usar el siguiente archivo yaml:

kubectl apply -f kube/ingress.yaml

Y ahora, después de hacer eso, vamos a invocar el servicio dentro de nuestro proyecto SOAPUI y deberíamos obtener la siguiente respuesta:

Paso 4 — Recapitular lo que acabamos de hacer

Ok, está funcionando y piensas… mmmm puedo hacer que esto funcione sin Istio y no sé si Istio todavía está haciendo algo o no…

Ok, tienes razón, esto podría no ser tan genial como esperabas, pero, créeme, solo estamos yendo paso a paso… Veamos qué está realmente sucediendo en lugar de una simple solicitud desde fuera del clúster al servicio del consumidor y esa solicitud siendo reenviada al backend, lo que está sucediendo es un poco más complejo. Echemos un vistazo a la imagen a continuación:

La solicitud entrante desde el exterior está siendo manejada por un Controlador de Ingreso Envoy que va a ejecutar todas las reglas definidas para elegir qué servicio debe manejar la solicitud, en nuestro caso el único componente consumer-v1 lo va a hacer, y lo mismo sucede en la comunicación entre consumidor y proveedor.

Así que, estamos obteniendo algunos interceptores en el medio que PODRÍAN aplicar la lógica que nos va a ayudar a enrutar el tráfico entre nuestros componentes con el despliegue de reglas a nivel de tiempo de ejecución sin cambiar la aplicación, y esa es la MAGIA.

Paso 5 — Implementar Lanzamiento Canary

Ok, ahora apliquemos algo de esta magia a nuestro caso. Uno de los patrones más usuales que solemos aplicar cuando estamos implementando una actualización en algunos de nuestros servicios es el enfoque canary. Solo para hacer una explicación rápida de lo que es esto:

El lanzamiento canary es una técnica para reducir el riesgo de introducir una nueva versión de software en producción al implementar lentamente el cambio a un pequeño subconjunto de usuarios antes de implementarlo en toda la infraestructura y hacerlo disponible para todos.

Si quieres leer más sobre esto, puedes echar un vistazo al artículo completo en el blog de Martin Fowler.

Así que, ahora vamos a generar un pequeño cambio en nuestra aplicación del proveedor, que va a cambiar la respuesta para asegurarnos de que estamos apuntando a la versión dos, como puedes ver en la imagen a continuación:

Ahora, vamos a construir esta aplicación y generar la nueva imagen llamada provider:v2.

Pero antes vamos a desplegarla usando el archivo YAML llamado provider-v2.yaml, vamos a establecer una regla en nuestro Service Mesh de Istio de que todo el tráfico debe dirigirse a v1 incluso cuando otros se apliquen. Para hacer eso, vamos a desplegar el archivo llamado default.yaml que tiene el siguiente contenido:

Así que, en este caso, lo que le estamos diciendo a Istio es que incluso si hay otros componentes registrados en el servicio, siempre debe responder el v1, por lo que ahora podemos desplegar el v2 sin ningún problema porque no va a responder a ningún tráfico hasta que decidamos hacerlo. Así que, ahora podemos desplegar el v2 con el siguiente comando:

kubectl apply -f provider-v2.yaml

Y aunque ejecutemos la solicitud SOAPUI, todavía estamos obteniendo una respuesta v1 incluso si verificamos en la configuración del servicio K8S que el v2 todavía está vinculado a ese servicio.

Ok, ahora vamos a comenzar a hacer el lanzamiento y vamos a comenzar con el 10% a la nueva versión y el 90% de las solicitudes a la antigua, y para hacer eso vamos a desplegar la regla canary.yaml usando el siguiente comando:

kubectl apply -f canary.yaml

Donde canary.yaml tiene el contenido que se muestra a continuación:

Y ahora, cuando intentemos suficientes veces, vamos a obtener que la mayoría de las solicitudes (aproximadamente el 90%) van a responder v1 y solo el 10% va a responder desde mi nueva versión:

Ahora, podemos monitorear cómo está funcionando v2 sin afectar a todos los clientes y si todo va como se espera, podemos continuar aumentando ese porcentaje hasta que todos los clientes estén siendo respondidos por v2.

Etiquetas:

Integrating Istio with BWCE Applications

Introduction

Services Mesh is one the “greatest new thing” in our PaaS environments. No matter if you’re working with K8S, Docker Swarm, pure-cloud with EKS or AWS, you’ve heard and probably tried to know how can be used this new thing that has so many advantages because it provides a lot of options in handling communication between components without impacting the logic of the components. And if you’ve heard from Service Mesh, you’ve heard from Istio as well, because this is the “flagship option” at the moment, even when other options like Linkerd or AWS App Mesh are also a great option, Istio is the most used Service Mesh at the moment.

You probably have seen some examples about how to integrate Istio with your open source based developments, but what happens if you have a lot of BWCE or BusinessWorks applications… can you use all this power? Or are you going to be banned for this new world?

Do not panic! This article is going to show you how easy you can use Istio with your BWCE application inside a K8S cluster. So, let the match…. BEGIN!

Scenario

The scenario that we’re going to test is quite simple. It’s a simple consumer provider approach. We’re going to use a simple Web Service SOAP/HTTP exposed by a backend to show that this can work not only with fancy REST API but even with any HTTP traffic that we could generate at the BWCE Application level.

So, we are going to invoke a service that is going to request a response from its provider and give us the response. That’s pretty easy to set up using pure BWCE without anything else.

All code related to this example is available for you in the following GitHub repo: Go get the code!

Steps

Step 1 Install Istio inside your Kubernetes Cluster

In my case I’m using Kubernetes cluster inside my Docker Desktop installation so you can do the same or use your real Kubernetes cluster, that’s up to you. The first step anyway is to install istio. And to do that, nothing better than following the steps given at isito-workshop that you can find here: https://polarsquad.github.io/istio-workshop/install-istio/ (UPDATE: No longer available)

Once you’ve finished we’re going to get the following scenario in our kubernetes cluster, so please, check that the result is the same with the following commands:

kubectl get pods -n istio-system

You should see that all pods are Running as you can see in the picture below:

kubectl -n istio-system get deployment -listio=sidecar-injector

You should see that there is one instance (CURRENT = 1) available.

kubectl get namespace -L istio-injection

You should see that ISTIO-INJECTION is enabled for the default namespace as the image shown below:

Step 2 Build BWCE Applications

Now, as we have all the infrastructure needed at the Istio level we can start building our application and to do that we don’t have to do anything different in our BWCE application. Eventually, they’re going to be two application that talks using HTTP as protocol, so nothing specific.

This is something important because when we usually talk about Service Mesh and Istio with customers, the same question always arises: Is Istio supported in BWCE? Can we use Istio as a protocol to communicate our BWCE application? So, they expect that it should exist some palette or some custom plugin they should install to support Istio. But none of that is needed at the application level. And that applies not only to BWCE but also to any other technology like Flogo or even open source technologies because at the end Istio (and Envoy is the other part needed in this technology that we usually avoid talking about when we talk about Istio) works in a Proxy mode using one of the most usual patterns in containers that is the “sidecar pattern”.

So, the technology that is exposing and implementing or consuming the service knows nothing about all this “magic” that is being executed in the middle of this communication process.

We’re going to define the following properties as environment variables like we’ll do in case we’re not using Istio:

Provider application:

  • PROVIDER_PORT → Port where the provider is going to listen for incoming requests.

Consumer application:

  • PROVIDER_PORT → Port where the provider host will be listening to.
  • PROVIDER_HOST → Host or FQDN (aka K8S service name) where provider service will be exposed.
  • CONSUMER_PORT → Port where the consumer service is going to listen for incoming requests.

So, as you can see if you check that the code of the BWCE application we don’t need to do anything special to support Istio in our BWCE applications.

NOTE: There is only an important topic that is not related to the Istio integration but how BWCE populates the property BW.CLOUD.HOST that is never translated to loopback interface or even 0.0.0.0. So it’s better that you change that variable for a custom one or to use localhost or 0.0.0.0 to listen in the loopback interface because is where the Istio proxy is going to send the requests.

After that we’re going to create our Dockerfiles for these services without anything, in particular, something similar to what you can see here:

NOTE: As a prerequisite, we’re using BWCE Base Docker Image named as bwce_base.2.4.3 that corresponds to version 2.4.3 of BusinessWorks Container Edition.

And now we build our docker images in our repository as you can see in the following picture:

Step 3: Deploy the BWCE Applications

Now, when all the images are being created we need to generate the artifacts needed to deploy these applications in our Cluster. Once again in this case nothing special neither in our YAML file, as you can see in the picture below we’re going to define a K8S service and a K8S deployment based on the imaged we’ve created in the previous step:

A similar thing happens with consumer deployment as well as you can see in the image below:

And we can deploy them in our K8S cluster with the following commands:

kubectl apply -f kube/provider.yaml

kubectl apply -f kube/consumer.yaml

Now, you should see the following components deployed. Only to fulfill all the components needed in our structure we’re going to create an ingress to make possible to execute requests from outside the cluster to those components and to do that we’re going to use the following yaml file:

kubectl apply -f kube/ingress.yaml

And now, after doing that, we’re going to invoke the service inside our SOAPUI project and we should get the following response:

Step 4 — Recap what we’ve just done

Ok, it’s working and you think… mmmm I can get this working without Istio and I don’t know if Istio is still doing anything or not…

Ok, you’re right, this could not be so great as you were expected, but, trust me, we’re just going step by step… Let’s see what’s really happening instead of a simple request from outside the cluster to the consumer service and that request being forwarded to the backend, what’s happening is a little bit more complex. Let’s take a look at the image below:

Incoming request from the outside is being handled by an Ingress Envoy Controller that is going to execute all rules defined to choose which service should handle the request, in our case the only consumer-v1 component is going to do it, and the same thing happens in the communication between consumer and provider.

So, we’re getting some interceptors in the middle that COULD apply the logic that is going to help us to route traffic between our components with the deployment of rules at runtime level without changing the application, and that is the MAGIC.

Step 5 — Implement Canary Release

Ok, now let’s apply some of this magic to our case. One of the most usual patterns that we usually apply when we’re rolling out an update in some of our services is the canary approach. Only to do a quick explanation or what this is:

Canary release is a technique to reduce the risk of introducing a new software version in production by slowly rolling out the change to a small subset of users before rolling it out to the entire infrastructure and making it available to everybody.

If you want to read more about this you can take a look at the full article in Martin Fowler’s blog.

So, now we’re going to generate a small change in our provider application, that is going to change the response to be sure that we’re targeting version two, as you can see in the image below:

Now, we are going to build this application and generate the new image called provider:v2.

But before we’re going to deploy it using the YAML file called provider-v2.yaml we’re going to set a rule in our Istio Service Mesh that all traffic should be targeted to v1 even when others are applied. To do that we’re going to deploy the file called default.yaml that has the following content:

So, in this case, what we’re saying to Istio is that even if there are other components registered to the service, it should reply always the v1, so we can now deploy the v2 without any issue because it is not going to reply to any traffic until we decide to do so. So, now we can deploy the v2 with the following command:

kubectl apply -f provider-v2.yaml

And even when we execute the SOAPUI request we’re still getting a v1 reply even if we check in the K8S service configuration that the v2 is still bounded to that service.

Ok, now we’re going to start doing the release and we’re going to start with 10% to the new version and 90% of the requests to the old one, and to do that we’re going to deploy the rule canary.yaml using the following command:

kubectl apply -f canary.yaml

Where canary.yaml has the content shown below:

And now when we try enough times we’re going to get that most of the requests (90% approx) is going to reply v1 and only for 10% is going to reply from my new version:

Now, we can monitor how v2 is performing without affecting all customers and if everything goes as expected we can continue increasing that percentage until all customers are being replied by v2.