URL Rewrite for Azure Application Gateway for Containers - Ingress API

Application Gateway for Containers allows you to rewrite the URL of a client request, including the requests' hostname and/or path. When Application Gateway for Containers initiates the request to the backend target, the request contains the newly rewritten URL to initiate the request.

Usage details

URL Rewrites take advantage of Application Gateway for Containers' IngressExtension custom resource.

Background

URL rewrite enables you to translate an incoming request to a different URL when initiated to a backend target.

The following figure illustrates a request destined for contoso.com/shop being rewritten to contoso.com/ecommerce when the request is initiated to the backend target by Application Gateway for Containers:

A diagram showing the Application Gateway for Containers rewriting a URL to the backend.

Prerequisites

  1. If following the BYO deployment strategy, ensure you set up your Application Gateway for Containers resources and ALB Controller.
  2. If following the ALB managed deployment strategy, ensure you provision your ALB Controller and provision the Application Gateway for Containers resources via the ApplicationLoadBalancer custom resource.
  3. Deploy sample HTTP application:
    Apply the following deployment.yaml file on your cluster to create a sample web application to demonstrate path, query, and header based routing.
kubectl apply -f https://raw.githubusercontent.com/MicrosoftDocs/azure-docs/refs/heads/main/articles/application-gateway/for-containers/examples/traffic-split-scenario/deployment.yaml

This command creates the following on your cluster:

  • A namespace called test-infra
  • Two services called backend-v1 and backend-v2 in the test-infra namespace
  • Two deployments called backend-v1 and backend-v2 in the test-infra namespace

Deploy the required Ingress API resources

  1. Create an Ingress that catches all traffic and routes to backend-v2
kubectl apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-01
  namespace: test-infra
  annotations:
    alb.networking.azure.io/alb-name: alb-test
    alb.networking.azure.io/alb-namespace: alb-test-infra
spec:
  ingressClassName: azure-alb-external
  rules:
    - host: contoso.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: backend-v2
                port:
                  number: 8080
EOF
  1. Create an Ingress that matches the /shop prefix that routes to backend-v1
kubectl apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-02
  namespace: test-infra
  annotations:
    alb.networking.azure.io/alb-name: alb-test
    alb.networking.azure.io/alb-namespace: alb-test-infra
    alb.networking.azure.io/alb-ingress-extension: url-rewrite
spec:
  ingressClassName: azure-alb-external
  rules:
    - host: contoso.com
      http:
        paths:
          - path: /shop
            pathType: Prefix
            backend:
              service:
                name: backend-v1
                port:
                  number: 8080
EOF

Note

When the ALB Controller creates the Application Gateway for Containers resources in ARM, it'll use the following naming convention for a frontend resource: fe-<8 randomly generated characters>

If you would like to change the name of the frontend created in Azure, consider following the bring your own deployment strategy.

When each Ingress resource is created, ensure the status is valid, the listener is Programmed, and an address is assigned to the gateway.

kubectl get ingress ingress-01 -n test-infra -o yaml
kubectl get ingress ingress-02 -n test-infra -o yaml

Example output of one of the Ingress resources.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    alb.networking.azure.io/alb-frontend: FRONTEND_NAME
    alb.networking.azure.io/alb-id: /subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourcegroups/yyyyyyyy/providers/Microsoft.ServiceNetworking/trafficControllers/zzzzzz
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"networking.k8s.io/v1","kind":"Ingress","metadata":{"annotations":{"alb.networking.azure.io/alb-frontend":"FRONTEND_NAME","alb.networking.azure.io/alb-id":"/subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourcegroups/yyyyyyyy/providers/Microsoft.ServiceNetworking/trafficControllers/zzzzzz"},"name"
:"ingress-01","namespace":"test-infra"},"spec":{"ingressClassName":"azure-alb-external","rules":[{"host":"contoso.com","http":{"paths":[{"backend":{"service":{"name":"backend-v2","port":{"number":8080}}},"path":"/","pathType":"Prefix"}]}}]}}
  creationTimestamp: "2023-07-22T18:02:13Z"
  generation: 2
  name: ingress-01
  namespace: test-infra
  resourceVersion: "278238"
  uid: 17c34774-1d92-413e-85ec-c5a8da45989d
spec:
  ingressClassName: azure-alb-external
  rules:
  - host: contoso.com
    http:
      paths:
      - backend:
          service:
            name: backend-v2
            port:
              number: 8080
        path: /
        pathType: Prefix
status:
  loadBalancer:
    ingress:
    - hostname: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.fzyy.alb.azure.com
      ports:
      - port: 80
        protocol: TCP

When the Ingress is created, create an IngressExtension resource for contoso.com. This example ensures traffic sent to contoso.com/shop is initiated as contoso.com/ecommerce to the backend target.

kubectl apply -f - <<EOF
apiVersion: alb.networking.azure.io/v1
kind: IngressExtension
metadata:
  name: url-rewrite
  namespace: test-infra
spec:
  rules:
    - host: contoso.com
      rewrites:
      - type: URLRewrite
        urlRewrite:
          path:
            type: ReplacePrefixMatch
            replacePrefixMatch: /ecommerce
EOF

When the IngressExtension resource is created, ensure the IngressExtension resource shows Accepted and the Application Gateway for Containers resource is Programmed.

kubectl get IngressExtension url-rewrite -n test-infra -o yaml

Verify the Application Gateway for Containers resource is successfully updated for the IngressExtension.

Test access to the application

Now we're ready to send some traffic to our sample application, via the FQDN assigned to the frontend. Use the following command to get the FQDN.

fqdn=$(kubectl get ingress ingress-01 -n test-infra -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')

If you specify the server name indicator contoso.com/shop using the curl command, a response from the backend-v1 service is returned with the requested path to the backend target showing contoso.com/ecommerce.

fqdnIp=$(dig +short $fqdn)
curl -k --resolve contoso.com:80:$fqdnIp http://contoso.com/shop

Via the response we should see:

{
 "path": "/ecommerce",
 "host": "contoso.com",
 "method": "GET",
 "proto": "HTTP/1.1",
 "headers": {
  "Accept": [
   "*/*"
  ],
  "User-Agent": [
   "curl/7.81.0"
  ],
  "X-Forwarded-For": [
   "xxx.xxx.xxx.xxx"
  ],
  "X-Forwarded-Proto": [
   "http"
  ],
  "X-Request-Id": [
   "dcd4bcad-ea43-4fb6-948e-a906380dcd6d"
  ]
 },
 "namespace": "test-infra",
 "ingress": "",
 "service": "",
 "pod": "backend-v1-5b8fd96959-f59mm"
}

If you specify the server name indicator contoso.com using the curl command, a response is returned from the backend-v2 service as shown.

fqdnIp=$(dig +short $fqdn)
curl -k --resolve contoso.com:80:$fqdnIp http://contoso.com

The following response should be displayed:

{
 "path": "/",
 "host": "contoso.com",
 "method": "GET",
 "proto": "HTTP/1.1",
 "headers": {
  "Accept": [
   "*/*"
  ],
  "User-Agent": [
   "curl/7.81.0"
  ],
  "X-Forwarded-For": [
   "xxx.xxx.xxx.xxx"
  ],
  "X-Forwarded-Proto": [
   "http"
  ],
  "X-Request-Id": [
   "adae8cc1-8030-4d95-9e05-237dd4e3941b"
  ]
 },
 "namespace": "test-infra",
 "ingress": "",
 "service": "",
 "pod": "backend-v2-594bd59865-ppv9w"
}

Congratulations, you have installed ALB Controller, deployed a backend application and used the IngressExtension to rewrite the client requested URL, prior to traffic being set to the target on Application Gateway for Containers.