You have decided to host your APIs in Kubernetes in combination with an API management solution? You are surely not the only one! In an Azure context, one way of doing this is combining Azure API Management and Azure Kubernetes Service (AKS). This post describes one of the ways to get this done. We will use the following services:
- Virtual Network: AKS will use advanced networking and Azure CNI
- Private DNS: to host a private DNS zone (private.baeke.info) ; note that private DNS is in public preview
- AKS: deployed in a subnet of the virtual network
- Traefik: Ingress Controller deployed on AKS, configured to use an internal load balancer in a dedicated subnet of the virtual network
- Azure API Management: with virtual network integration which requires Developer or Premium; note that Premium comes at a hefty price though
Let’s take it step by step but note that this post does not contain all the detailed steps. I might do a video later with more details. Check the YouTube channel for more information.
We will setup something like this:
Consumer --> Azure API Management public IP --> ILB (in private VNET) --> Traefik (in Kubernetes) --> API (in Kubernetes - ClusterIP service in front of a deployment)
Virtual Network
Create a virtual network in a resource group. We will add a private DNS zone to this network. You should not add resources such as virtual machines to this virtual network before you add the private DNS zone.
I will call my network privdns and add a few subnets (besides default):
- aks: used by AKS
- traefik: for the internal load balancer (ILB) and the front-end IP addresses
- apim: to give API management access to the virtual network
Private DNS
Add a private DNS zone to the virtual network with Azure CLI:
az network dns zone create -g rg-ingress -n private.baeke.info --zone-type Private --resolution-vnets privdns
You can now add records to this private DNS zone:
az network dns record-set a add-record \ -g rg-ingress \ -z private.baeke.info \ -n test \ -a 1.1.1.1
To test name resolution, deploy a small Linux virtual machine and ping test.private.baeke.info:

Update for June 27th, 2019: the above commands use the old API; please see https://docs.microsoft.com/en-us/azure/dns/private-dns-getstarted-cli for the new syntax to create a zone and to link it to an existing VNET; these zones should be viewable in the portal via Private DNS Zones:

Azure Kubernetes Service
Deploy AKS and use advanced networking. Use the aks subnet when asked. Each node you deploy will get 30 IP address in the subnet:

Traefik
To expose the APIs over an internal IP we will use ingress objects, which require an Ingress Controller. Traefik is just one of the choices available. Any Ingress Controller will work.
Instead of using ingresses, you could also expose your APIs via services of type LoadBalancer and use an internal load balancer. The latter approach would require one IP per API where the ingress approach only requires one IP in total. That IP resolves to Traefik which uses the host header to route to the APIs.
We will install Traefik with Helm. Check my previous post for more info about Traefik and Helm. In this case, I will download and untar the Helm chart and modify values.yaml. To download and untar the Helm chart use the following command:
helm fetch stable/traefik --untar
You will now have a traefik folder, which contains values.yaml. Modify values.yaml as follows:

This will instruct Helm to add the above annotations to the Traefik service object. It instructs the Azure cloud integration components to use an internal load balancer. In addition, the load balancer should be created in the traefik subnet. Make sure that your AKS service principal has the RBAC role on the virtual network to perform this operation.
Now you can install Traefik on AKS. Make sure you are in the traefik folder where the Helm chart was untarred:
helm install . --name traefik --set serviceType=LoadBalancer,rbac.enabled=true,dashboard.enabled=true --namespace kube-system
When the installation is finished, there should be an internal load balancer in the resource group that is behind your AKS cluster:

The result of kubectl get svc -n kube-system should result in something like:

We can now reach Treafik on the virtual network and create an A record that resolves to this IP. The func.private.baeke.info I will use later, resolves to the above IP.
Azure API Management
Deploy API Management from the portal. API Management will need access to the virtual network which means we need a version (SKU) that has virtual network support. This is needed simply because the APIs are not exposed on the public Internet.
For testing, use the Developer SKU. In production, you should use the Premium SKU although it is very expensive. Microsoft should really make the virtual network integration part of every SKU since it is such a common scenario! Come on Microsoft, you know it’s the right thing to do! 😉

Above, API Management is configured to use the apim subnet of the virtual network. It will also be able to resolve private DNS names via this integration. Note that configuring the network integration takes quite some time.
Deploy a service and ingress
I deployed the following sample API with a simple deployment and service. Save this as func.yaml and run kubectl apply -f func.yaml. You will end up with two pods running a super simple and stupid API plus a service object of type ClusterIP, which is only reachable inside Kubernetes:
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 ports: - containerPort: 80
Next, deploy an ingress:
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: func annotations: kubernetes.io/ingress.class: traefik spec: rules: - host: func.private.baeke.info http: paths: - path: / backend: serviceName: func servicePort: 80
Notice I used func.private.baeke.info! Naturally, that name should resolve to the IP address on the ILB that routes to Traefik.
Testing the API from API Management
In API Management, I created an API that uses func.private.baeke.info as the backend. Yes, I know, the API name is bad. It’s just a sample ok? 😎

Let’s test the GET operation I created:

Conclusion
In this post, we looked at one way to expose Kubernetes-hosted APIs to the outside world via Azure API Management. The traffic flow is as follows:
Consumer --> Azure API Management public IP --> ILB (in private VNET) --> Traefik (in Kubernetes) --> API (in Kubernetes - ClusterIP service in front of a deployment)
Because we have to use host names in ingress definitions, we added a private DNS zone to the virtual network. We can create multiple A records, one for each API, and provide access to these APIs with ingress objects.
As stated above, you can also expose each API via an internal load balancer. In that case, you do not need an Ingress Controller such as Traefik. Alternatively, you could also replace Azure API Management with a solution such as Kong. I have used Kong in the past and it is quite good! The choice for one or the other will depend on several factors such as cost, features, ease of use, support, etc…
3 thoughts on “Azure API Management and Azure Kubernetes Service”