Our team has been hard at work building out the next iteration of our customer storefront accelerator using Next.js (the current iteration of our storefront accelerator is built using Create React App). Given that the current version of the storefront is still being used and tested, this gave us a good opportunity to exercise various deployment strategies on top of our microservices architecture. My goal was to allow our team to test out both versions simultaneously on the same Kubernetes infrastructure allowing for a canary type rollout.
Canary and A/B testing strategies are common “asks” in the real world and make a lot of sense to safely and incrementally introduce changes without having to do a “big bang” cutover. So, in this series of blog posts - I will walk you through various strategies that can help you implement similar flows in your microservices environment.
Ingress Strategy: Canary Routing Using Nginx Kubernetes Ingress
In this post, I will be describing how to implement canary routing using an Nginx Ingress. In particular, we’ll showcase canary deploying a service containing a new Next.js storefront application running alongside an existing one all while both connect to the same microservices on the backend.
With Broadleaf’s reference deployment architecture, we utilize an Nginx Ingress Proxy to front our Kubernetes cluster. The Nginx proxy is widely used and supports several interesting capabilities including the ability to do canary routing by keying on different criteria: (https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#canary).
The proxy allows you to route requests based on cookie or header and gives you the ability to define weighted requests to slowly increase the load to the canary service over time.
Deploying the Canary Service(s)
Test Example:
Here’s an example manifest that provisions 2 test “http-echo” resources (one production and another canary) that are fronted by an Nginx Ingress. Run `kubectl create -f http-echo-canary-example.yaml`
to create the deployments, services, and ingress with weighted canary enabled. This example should route about 30% of requests to the canary service and the rest to the production service.
You can now test to see this in action using the following script:
for i in $(seq 1 10); do curl -kL http://echo.example.com;
done
Broadleaf Specific Example:
In our case, the Broadleaf Microservices stack utilizes a “Backend for Frontend” pattern (https://samnewman.io/patterns/architectural/bff/) with a lightweight gateway built on Spring Cloud Gateway to facilitate the coordination of the storefront application with the core backing APIs. Because Spring Cloud Gateway is flexible and allows you to define routes via properties or environment variables I’m able to deploy a copy of the same production gateway pod that just defines a different environment variable pointing to the new canary storefront and left all the default routes to the backing production service APIs the same.
Here is an example Kubernetes manifest for reference: