In the previous post, we looked at publishing and securing an API with Azure Front Door and Azure Web Application Firewall. The API ran on Kubernetes, exposed by Kong and Kong Ingress Controller. Kong was configured to require an API key to call the /users API, allowing us to identify the consumer of the API. The traffic flow was as follows:
Consumer -- HTTPS --> Azure Front Door with WAF policy -- HTTPS --> Kong (exposed with Azure Load Balancer) -- HTTP --> API Kubernetes service --> API pods
Although Kubernetes makes the API(s) highly available, you might want to take extra precautions such as deploying the API in multiple regions. In this post, we will take a look at doing so. That means we will deploy the API in both West and North Europe, in two distinct Kubernetes clusters:
- we-clu: Kubernetes cluster in West Europe
- ne-clu: Kubernetes cluster in North Europe
The flow is very similar of course:
Consumer -- HTTPS --> Azure Front Door with WAF policy -- HTTPS --> Kong (exposed with Azure Load Balancer; region to connect to depends on Front Door configuration and health probes) -- HTTP --> API Kubernetes service --> API pods
Let’s take a look at the configuration! By the way, the supporting files to deploy the Kubernetes objects are here: https://github.com/gbaeke/api-kong/tree/master. To deploy Kong, check out this post.
We deploy a Kubernetes cluster in each region, install Helm, deploy Kong, deploy our API and configure ingresses and related Kong custom resource definitions (CRDs). The result is an external IP address in each region that leads to the Kong proxy. Search for “kong” on this blog to find posts with more details about this deployment.
Note that the API deployment specifies an environment variable that will contain the string WE or NE. This environment variable will be displayed in the output when we call the API. Here is the API deployment for West Europe:
apiVersion: v1 kind: Service metadata: name: func spec: ports: - port: 80 protocol: TCP targetPort: 80 selector: app: func type: ClusterIP --- apiVersion: apps/v1 kind: Deployment metadata: name: func spec: replicas: 2 selector: matchLabels: app: func template: metadata: labels: app: func spec: containers: - name: func image: gbaeke/ingfunc env: - name: REGION value: "WE" ports: - containerPort: 80
When we call the API and Azure Front Door uses the backend in West Europe, the result will be:
The origin APIs need to be exposed on the public Internet using a DNS name. Azure Front Door requires a public backend to connect to. Naturally, the backend can be configured to only accept incoming requests from Front Door. In our case, the APIs are available on the public IP of the Kong proxy. The following names were used:
- api-o-we.baeke.info: Kong proxy in West Europe
- api-o-ne.baeke.info; Kong proxy in North Europe
Both endpoints are configured to accept TLS connections only, and use a Let’s Encrypt wildcard certificate for *.baeke.info.
Front Door Configuration
The Front Door designer looks the same as in the previous post:
However, the backend pool api-o now has two backends:
To determine the health of the backend, Front Door needs to be configured with a health probe that returns status code 200. If we were to specify the probe below, the health probe would fail:
The health probe would hit Kong’s proxy and return a 404 (Not Found). We did not create a route for /, only for /users. With Azure Front Door, when all health probes fail, all backends are considered healthy. Yes, you read that right.
Although we could create a route called /health that returns a 200, we will use the following probe just to make it work:
If you are exposing multiple APIs on each cluster, the health probe above would not make sense. Also note that the purpose of the health probe is to determine if the cluster is up or not. It will not fix one API behaving badly or being removed accidentally!
You can check the health probes from the portal:
When I connect from my home laptop in Belgium, I get the following response:
When I connect from my second home in Dublin 🤷♀️ I get:
If you enable logging to Log Analytics, you can check this in the FrontDoorAccessLog:
When I remove the /users API in West Europe (kubectl delete deploy func), my home laptop will connect to North Europe as expected:
Note that the calls will not fail from the moment you delete the /users API (the health probe here). That depends on the following setting (backends in Front Door designer):
The backend health percentage graph indicates the probe failure as well:
When you are going for a multi-region deployment of services, Azure Front Door is one of the options. Of course, there is much more to a multi-region deployment than the “front-end stuff” described in this post. What do you do with databases for instance? Can you use active-active write regions (e.g. Cosmos DB) or does your database only support active/passive with read replicas?
As in other load balancing and fail over solutions, proper health probes are crucial in the design. Think about what a good health probe can be and what it means when it is not available. One option is to just write a health probe exposed via an endpoint such as /health that merely returns a 200 status code. But your health probe could also be designed to connect to backend systems such as databases or queues to determine the health of the system.
Hopefully, this post gives you some ideas to start! Follow me on Twitter for updates.