Azure App Gateway is actually a Kubernetes Ingress Controller.
Let’s get this working. You’ve got NGINX Ingress already set up in your AKS cluster, and now you want to layer Azure Application Gateway on top of it. This isn’t about replacing NGINX; it’s about leveraging App Gateway as your external-facing load balancer that then routes traffic to your NGINX Ingress Controller, which then handles the internal routing to your services.
Here’s how you make that happen:
First, ensure you have both NGINX Ingress Controller and the Azure Application Gateway Ingress Controller (AGIC) deployed in your AKS cluster. AGIC is typically deployed via Helm.
helm upgrade --install ingress-azure ingress-azure/ingress-azure \
--namespace ingress-appgw \
--set cluster-issuer.email=<your-email> \
--set armAuth.type=msi \
--set appgw.name=<your-app-gateway-name> \
--set appgw.resourceGroup=<your-app-gateway-resource-group> \
--set appgw.subscriptionId=<your-app-gateway-subscription-id> \
--set appgw.environment=AzurePublicCloud
The key here is that AGIC needs to know which Application Gateway instance to manage. The --set appgw.name, --set appgw.resourceGroup, and --set appgw.subscriptionId parameters point AGIC to your existing Azure Application Gateway. The cluster-issuer.email is for cert-manager integration, which is highly recommended for TLS.
Next, you need to configure your NGINX Ingress Controller to listen on a specific internal IP address or hostname that App Gateway will target. This means AGIC won’t be creating public IP addresses or directly exposing your services.
You’ll modify the NGINX Ingress Controller’s deployment or Helm values to bind it to an internal IP. If you installed NGINX via Helm, you’d typically adjust values like this:
controller:
service:
type: LoadBalancer
loadBalancerIP: <internal-ip-for-nginx> # e.g., 10.240.0.5
annotations:
service.beta.kubernetes.io/azure-load-balancer-internal: "true"
service.beta.kubernetes.io/azure-load-balancer-internal-subnet: "<your-aks-subnet-name>"
The loadBalancerIP should be an IP address within your AKS cluster’s VNet, specifically in the subnet designated for your AKS nodes. The service.beta.kubernetes.io/azure-load-balancer-internal: "true" annotation is crucial. It tells Azure to create an internal load balancer for the NGINX service, meaning it won’t have a public IP and will only be reachable from within your VNet. The azure-load-balancer-internal-subnet annotation specifies which subnet the internal load balancer should reside in.
After applying these changes, NGINX Ingress Controller will expose itself via an internal LoadBalancer service. This service will have an IP address that your Azure Application Gateway can reach.
Now, you create an Ingress resource that AGIC will watch. This Ingress resource will define how App Gateway routes traffic to your NGINX Ingress Controller.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: appgw-to-nginx-ingress
namespace: default # or wherever your services are
annotations:
kubernetes.io/ingress.class: azure/application-gateway
appgw.ingress.kubernetes.io/backend-protocol: http # or https if Nginx is configured for it
spec:
rules:
- host: myapp.yourdomain.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx-internal-service # This is the name of the Kubernetes Service for NGINX
port:
number: 80 # The port Nginx is listening on internally
This Ingress resource tells AGIC to configure the Application Gateway. kubernetes.io/ingress.class: azure/application-gateway is the key annotation that directs AGIC to manage this Ingress. The backend.service.name and backend.service.port point to the Kubernetes Service that exposes your NGINX Ingress Controller internally. This internal NGINX service is the one you configured with the internal LoadBalancer IP.
AGIC will then create a backend pool in your Azure Application Gateway pointing to that internal IP of the NGINX service. It will also set up the necessary listeners and routing rules on the Application Gateway to direct external traffic (e.g., to myapp.yourdomain.com) to this backend pool.
The reason this works is that you’re creating a tiered routing architecture. Azure Application Gateway, with AGIC managing it, becomes your public-facing entry point. It handles SSL termination (if configured), WAF policies, and initial traffic distribution. It then forwards requests to an internal IP address. That internal IP address is the LoadBalancer service for your NGINX Ingress Controller. NGINX Ingress then takes over, inspecting the request (host, path, etc.) and routing it to the appropriate Kubernetes Service within your cluster.
The NGINX Ingress Controller itself should be configured to listen on the internal IP you assigned to its service. If you installed NGINX via Helm, this would be in your values.yaml under controller.service.loadBalancerIP.
The next thing you’ll likely encounter is configuring health probes on the Application Gateway to correctly monitor the NGINX Ingress Controller’s internal service.