Tanzu Kubernetes Cluster Ingress with NSX-ALB

In this blog I will show how to use NSX-ALB (Avi) for Tanzu Kubernetes Clusters (TKC) as an Ingress and a Load Balancer in a vSphere with Tanzu environment on top of NSX-T
I am running vSphere 7U1 with NSX-T 3.1.

The main motivation for this exercise is to provide an Ingress Controller for TKCs. Today (vSphere 7.0U1) TKCs are using NSX-T for Service Type LoadBalancer, but they don’t ship with any Ingress Controller. NSX-ALB is an enterprise class Ingress Controller with GLSB and WAF capabilities and most probably it will become the default choice for ingress in vSphere with Tanzu and TKG in the future. I am using Antrea as a CNI for Pods Networking.

Here is the design i am aiming for,

The reason I am going with two-armed mode is that TKC nodes cannot be reached directly in an NSX-T Environment. When we create a TKC, NSX-T automatically create a Gateway Firewall rule that blocks direct access to the TKC nodes at the Cluster T1-GW level. Having our NSX-ALB SE directly connected the nodes segment will give us the ability to bypass the Cluster T1-GW.

To integrate with K8s, we need a component called Avi Kubernetes Operator (AKO), which will run as a pod in our TKC. AKO will be the bridge between K8s API and NSX-ALB Controller for discovery and automation.


1. ESXi and vCenter 7U1 are deployed
2. NSX-T Manager, Edge, and T0-GW are deployed
3. T0-GW is already peered with the Physical Network using BGP or Static Routes
4. WCP is enabled in vCenter with NSX-T
5. a Supervisor Cluster Namespace is created and setup correctly
4. Avi Controller is deployed as per my previous blog.

Deploy a TKC Cluster

First we need to create our Tanzu Kubernetes Cluster (TKC) in a vSphere 7 Environment. To do that we need to apply a cluster specs yaml in our Supervisor Cluster. K8s Cluster API (CAPI) will take care of deploying the cluster. Below is just an example of that and should be modified based on the cluster requirements

apiVersion: run.tanzu.vmware.com/v1alpha1      
kind: TanzuKubernetesCluster
  name: ali-tkg-cluster-1
  namespace: ali-namespace-01
    fullVersion: v1.17.8+vmware.1-tkg.1.5417466 
      count: 1
      class: guaranteed-large                  
      storageClass: gold-storage-policy         
      count: 3
      class: guaranteed-large                  
      storageClass: gold-storage-policy         
        name: antrea
        cidrBlocks: [""]     
        cidrBlocks: [""]  

This should create the cluster with Antrea CNI on vSphere as below

NSX-ALB Controller Prerequisites

Before deploying Avi Kubernetes Operator (AKO) in our TKC, there are multiple steps we need to go through in NSX-ALB Controller.

Create IPAM & DNS Profile to Automate creating DNS entries and IP address assignment when an Ingress is created.
Templates >> Profiles >> IPAM/DNS Profiles >> Create DNS Profile
This is going to be the Sub-Domain Name which will be used for our Ingress. There is no need to configure the sub-domain in our DNS Server because we will delegate it to NSX-ALB DNS at a later step.

Create IPAM Profile,
Templates >> Profiles >> IPAM/DNS Profiles >> Create IPAM Profile
We should add the network that will be used for our VIPs. (i am using an NSX-T Overlay Segment)

Now we need to point to the DNS/IPAM Profile in our Default-Cloud
Go to Infrastructure >> Clouds >> Default Cloud to add the IPAM and DNS Profile

While we are in the Default-Cloud, let’s go to the Data Center tab to Enable three things, DHCP, Prefer Static Routes vs Directly Connected Network, and Use Static Routes for Network Resolution of VIP.
By doing that, NSX-ALB will automatically create Static Routes to the internal K8s Pods subnets through the K8s nodes IP Addresses. it will work because in our design. NSX-ALB SE is directly connected to nodes L2-Segment. I will show the static routes later when AKO is deployed.
Even though I have NSX-T in my environment, and i am using NSX-T for the SE connectivity, I am using the Default-Cloud which is a vCenter Cloud. I am doing that because AKO does not support NSX-T Cloud yet when this post is being written.

In the Network tap select the Management Network of the SEs. I am using a VDS dPG, but an NSX-T Overlay could be used too

For Kubernetes environment, each K8s Cluster needs its own SE Group. Lets go ahead and create one for our cluster.
Infrastructure >> Service Engine Group >> CREATE
All what needed is to give it a name. You can leave the rest to the defaults. I named it ali-tkg-cluster-1 which is the name of my Tanzu K8s Cluster.

Last thing we need to do is to make sure our VIP network and a default route are configured correctly
Infrastructure >> Networks
We can add an IP Pools for the Data Network which will be used for the VIPs (NSX-ALB-Data in my case)

Infrastructure >> Routing
Configure a default route for the VIPs

Deploy NSX-ALB SE Manually(Optional)

In this guide, I have decided to deploy the SEs manually for better control to my lab limited resources. If you are looking to deploy the SEs automatically once an ingress or a service type LoadBalancer is created, then please skip this section.

First I need to change my Default-Cloud Access-Permission from Write to Read to be able to get the SE OVA and avoid any SE automatic creation.
Infrastructure >> Clouds >> Edit Default Cloud

Now we can download the SE OVA by pressing the Download icon infront of the Default-Cloud

Now we can deploy the SE OVA in vCenter. The main thing is to get our ports assignments right for Management, Data, and Pool sides.

– Management: in my case i am using a VDS dPG for Mgmt, but it could an NSX-T Overlay-Segment.
– Data Network 1: I am using this one for the VIPs. it is an NSX-T Overlay Segment.
– Data Network 2: This is the NSX-T Overlay Segment that is used by the TKC nodes. It is very important to get this one right to have L2 connectivity to the K8s nodes. AKO will create static routes to reach the pods inside the nodes.

The other configurations we need get right is the Authentication token for Avi Controller and Controller Cluster UUID for Avi Controller. We can get those from NSX-ALB Controller.
Go to the Default-Cloud and press the key icon on the right.

Then enter those during the OVA setup.

Once the OVA is deployed, it should be seen in the Controller. Lets go ahead and move it to our cluster Service Engine Group.
Infrastructure >> Service Engine >> Edit

Edit the Service Engine by changing the SE Group. Make sure your Networks and IP addresses are assigned correctly. If not, please assign the IP addresses manually.

DNS Configuration

NSX-ALB handles Ingress external IP addresses a bit differently than other K8s Ingress Controllers. Typically in other controllers, all Ingresses are assigned a single IP address and a DNS wildcard mask is assigned to this IP address. However, in NSX-ALB Ingress, we assign each K8s Ingress a separate IP Address derived from our VIP pool to enhance the High Availability of the solution.
If you want to know more why DNS wildcard mask are not the best choice, then please watch below video,
To avoid Configuring a DNS record per Ingress, we need to delegate our sub-domain to NSX-ALB DNS Service.
First we need to configure DNS Virtual Service (VS)
Application >> Virtual Service >> CREATE VIRTUAL SERVICE
Under Settings, we need to give it a name, VIP Address (Auto-Allocate), and an Application Profile (System-DNS).

Under Advanced, our SE Group should be picked. The SE Group does not have to be the same as the one we are using for AKO if there are other SEs deployed in other SE Groups.

It should look like below (it is ok if it has lower health score when it is just deployed). Please take a note of the IP Address as it will be used for the Domain Delegation.

Now we need to go to enable the service under Administration >> Settings >> DNS Service by pointing to our DNS-VS

In our main DNS Server, we want to delegate our subdomain to NSX-ALB VS
(the subdomain should match our DNS Profile)

Point the DNS-VS IP Addess
(I already created a DNS entry ali-avi-dns-01 >>

By doing that. Any DNS request for subdomain ali-avi.vmwdxb.com will be delegated to NSX-ALB VS.

Deploy AKO

Login to our TKC using below command

$ kubectl vsphere login --server <WCP-IP-ADDRESS> -u administrator@vsphere.local --insecure-skip-tls-verify --tanzu-kubernetes-cluster-namespace ali-namespace-01 --tanzu-kubernetes-cluster-name ali-tkg-cluster-1

Cretae avi-system namespace
$ kubectl create ns avi-system

Add AKO helm repo
$ helm repo add ako https://avinetworks.github.io/avi-helm-charts/charts/stable/ako 

(optional) Search for AKO in the repo to check the version
$ helm search repo | grep ako
ako/ako 1.2.1 1.2.1 A helm chart for Avi Kubernetes Operator

Get the values.yaml base file, or simply copy it from below
$ curl -JOL https://raw.githubusercontent.com/avinetworks/avi-helm-charts/master/charts/stable/ako/values.yaml 
The file will be used to create K8s Config-Map for our AKO Pod

Edit the file as per the environment
Values that needs to be changed are in blue

replicaCount: 1

  repository: avinetworks/ako
  pullPolicy: IfNotPresent

  logLevel: "INFO" 
  fullSyncFrequency: "1800" 
  apiServerPort: 8080 
  deleteConfig: "false" 
  disableStaticRouteSync: "false" 
  clusterName: "ali-tkg-cluster-1"  #TKC Name
  cniPlugin: "" 

  - networkName: "vnet-domain-c34:f593d27f-228d-4795-af60-626f6a697dff-ali-namespace-01-al-d4350-0" #TKC Nodes Segment
    -          #TKC Nodes CIDR
  subnetIP: ""     #VIP Subnet
  subnetPrefix: "24"           
  networkName: "NSX-ALB-Data"  #VIP Network

  defaultIngController: "true"
  l7ShardingScheme: "hostname"
  serviceType: ClusterIP 
  shardVSSize: "LARGE" 
  passthroughShardSize: "SMALL" 

  defaultDomain: "" 

  serviceEngineGroupName: "ali-tkg-cluster-1" 
  controllerVersion: "20.1.2" 
  cloudName: "Default-Cloud" 
  controllerIP: ""

  key: ""
  value: ""

    cpu: 250m
    memory: 300Mi
    cpu: 100m
    memory: 75Mi

podSecurityContext: {}

  username: "admin"
  password: "CONTROLLER-PWD"

  type: ClusterIP
  port: 80

persistentVolumeClaim: ""
mountPath: "/log"
logFile: "avi.log"

Deploy AKO using helm
$ helm install ako/ako --generate-name --version 1.2.1 -f values.yaml --namespace=avi-system

Check AKO pod status. if you see some restarts, or the status is not “Running”, then there is something wrong with the values.yaml file.

$ kubectl get pods -n avi-system
ako-0 1/1 Running 0 1h

Now check that AKO created the static routes automatically for the pods under Infrastructure >> Routing

The Static Routes are created automatically because we checked below boxes in our default-cloud

Deploy Test Application with HTTP Ingress

Create a new Namespace for our Application
$ kubectl create ns yelb

Deploy yelb application
$ kubectl apply -f https://raw.githubusercontent.com/aidrees/yelb/main/yelb-no-lb.yaml -n yelb

Deploy an Ingress (you should change the “host” to match your domain name)
$ kubectl apply -f https://raw.githubusercontent.com/aidrees/yelb/main/yelb-ingress.yaml -n yelb

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
  name: yelb-ingress
  - host: "yelb.ali-avi.vmwdxb.com"
      - path: 
          serviceName: yelb-ui
          servicePort: 80
$ kubectl get ingress -n yelb
NAME           HOSTS                    ADDRESS          PORTS   AGE
yelb-ingress   yelb.ali-avi.vmwdxb.com   80     14m

Check NSX-ALB Application Dashboard,

Now we can access our application using “yelb.ali-avi.vmwdxb.com”. Please note that I did not need to configure any specific DNS record for it because we are delegating “ali-avi.vmwdxb.com” subdomain to NSX-ALB DNS Virtual Service.
One more thing to notice is NSX-ALB automatically created an HTTPS ingress. Give it a try and access the application with HTTPS

We can see in NSX-ALB Contoller how is our application performing

And we can even see the logs for a specific request

Deploy Test Application with HTTPS Ingress

Lets deploy another application
$ kubectl create ns hipster
$ kubectl apply -f https://raw.githubusercontent.com/aidrees/k8s-lab/master/hipster-no-lb.yaml -n hipster

Create HTTPS Ingress (dont forget to change the host with your domain name and cert)
$ kubectl apply -f https://raw.githubusercontent.com/aidrees/k8s-lab/master/ingress.yml -n hipster

apiVersion: v1
kind: Secret
type: kubernetes.io/tls
  name: hipster-tls

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
  name: hipstershop
    ncp/http-redirect: "true"
  - hosts:
    - hipster.ali-avi.vmwdxb.com
    secretName: hipster-tls
  - host: hipster.ali-avi.vmwdxb.com
      - path: 
          serviceName: frontend
          servicePort: 80

Check out HTTPS Ingress
$ kubectl get ingress -n hipster
hipstershop hipster.ali-avi.vmwdxb.com 80, 443

Please note the different IP address per Ingress (even if it was HTTP, NSX-ALB will always assign diffrent IP address).

Now lets access our application using HTTPS. Please note we did not need to configure anything DNS entries because we are delegating ali-avi.vmwdxb.com to NSX-ALB DNS Virtual Service.

Deploy Service Type Load Balancer

Delete previously created Ingress to avoid confusion
$ kubectl delete -f https://raw.githubusercontent.com/aidrees/k8s-lab/master/ingress.yml -n hipster

Create a Service Type LoadBalancer
$ kubectl apply -f https://raw.githubusercontent.com/aidrees/k8s-lab/master/hipster-lb-svc.yaml -n hipster

apiVersion: v1
kind: Service
  name: frontend-external
  type: LoadBalancer
    app: frontend
  - name: http
    port: 80
    targetPort: 8080

Check if the service is created and get the external IP address
$ kubectl get svc -n hipster | grep LoadBalancer
frontend-external LoadBalancer 80:31982/TCP 19s

Access the application using the IP address.

With NSX-ALB, an FQDN is assigned automatically even for Service Type LoadBalancer. it can be seen in the NSX-ALB UI

We can access the app using the external IP Address or the FQDN without configuring DNS.

Deploy a Traffic Generator (Optional)

To have some nice Diagrams, lets deploy a Traffic Generator. I am using Locust

Deploy a test app
$ kubectl apply -f https://raw.githubusercontent.com/aidrees/acme_fitness/main/secrets.yaml
$ kubectl apply -f https://github.com/aidrees/acme_fitness/blob/main/acme_fitness.yaml

Deploy Service Type Load Balancer (or Ingress)
$ kubectl apply -f https://raw.githubusercontent.com/aidrees/acme_fitness/main/acme-lb.yaml

Deploy Traffic Generator
$ git clone https://github.com/aidrees/traffic-generator.git
$ cd traffic-generator
$ pip install -r requirements.txt
$ kubectl apply -f loadgen.yaml

$ Locust --host=http://xxx.ali-avi.vmwdxb.com --port=8085
Open your browser and go to http://localhost:8085 and start warming

Explore NSX-ALB UI for some nice traffic analytics.


By that I will conclude this post. I hope you enjoyed reading it and learned something from it. In this post I showed how to make NSX-ALB works in a vSphere with Tanzu environment with NSX-T.
This is my first experience with AKO. I can honestly say that I enjoyed testing NSX-ALB (Avi) and I am very impressed with the solution. The built-in analytics capabilities are really nice, not to mention Active-Active Load Balancing, Dynamic SE creation, Auto-Scaling, ..etc. I am aware that i am only scratching the surface with the solution and hopefully I get the chance to test and write about its WAF and GSLB capabilities.

Thank you for reading!

2 thoughts on “Tanzu Kubernetes Cluster Ingress with NSX-ALB

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: