Routes

Define HTTPRoutes for HAProxy Unified Gateway

The Gateway API defines specialized resources for routing different types of network traffic. You would use an HTTPRoute resource to route HTTP traffic.

Prerequisites checklist Jump to heading

Before continuing, ensure that you’ve met these prerequisites:

  • Define a Gateway. When defining a Gateway, set its allowedRoutes property to accept routes of kind HTTPRoute.

Implement hostname-based routing Jump to heading

Hostname-based routing is useful when you want to route traffic based on the domain. For example, you want to route traffic for api.example.com and web.example.com to different services.

To implement hostname-based routing:

  1. Update your Gateway resource so that it omits or comments out the hostname field. This ensures that the Gateway will relay traffic for all of your domains.

    gateway.yaml
    yaml
    apiVersion: gateway.networking.k8s.io/v1
    kind: Gateway
    metadata:
    name: example-haproxy-gateway
    namespace: default
    spec:
    gatewayClassName: haproxy
    listeners:
    - name: http
    protocol: HTTP
    port: 31080
    # hostname: "example.com"
    allowedRoutes:
    kinds:
    - group: gateway.networking.k8s.io
    kind: HTTPRoute
    namespaces:
    from: Same
    gateway.yaml
    yaml
    apiVersion: gateway.networking.k8s.io/v1
    kind: Gateway
    metadata:
    name: example-haproxy-gateway
    namespace: default
    spec:
    gatewayClassName: haproxy
    listeners:
    - name: http
    protocol: HTTP
    port: 31080
    # hostname: "example.com"
    allowedRoutes:
    kinds:
    - group: gateway.networking.k8s.io
    kind: HTTPRoute
    namespaces:
    from: Same
  2. Define an HTTPRoute resource for your target domain. In this example, we route traffic for the hostname web.example.com.

    • The parentRefs section references one or more Gateways to which an HTTPRoute tries to attach. This HTTPRoute will attach to Gateway resources that have listeners with allowedRoutes matching the HTTPRoute kind and a matching namespace.
    • Under rules, each entry has a backendRefs section to route to the specified service. For example, requests for web.example.com will route to web-svc. A backendRefs entry refers to the name of a Service resource and its listening port.
    hostname-based-route.yaml
    yaml
    apiVersion: gateway.networking.k8s.io/v1
    kind: HTTPRoute
    metadata:
    name: web-hostname-route
    namespace: default
    spec:
    parentRefs:
    - name: example-haproxy-gateway
    hostnames:
    - web.example.com
    rules:
    - backendRefs:
    - name: web-svc
    namespace: default
    port: 80
    hostname-based-route.yaml
    yaml
    apiVersion: gateway.networking.k8s.io/v1
    kind: HTTPRoute
    metadata:
    name: web-hostname-route
    namespace: default
    spec:
    parentRefs:
    - name: example-haproxy-gateway
    hostnames:
    - web.example.com
    rules:
    - backendRefs:
    - name: web-svc
    namespace: default
    port: 80
  3. Apply the changes with kubectl:

    nix
    kubectl apply -f hostname-based-route.yaml
    nix
    kubectl apply -f hostname-based-route.yaml
    output
    text
    httproute.gateway.networking.k8s.io/api-hostname-route created
    output
    text
    httproute.gateway.networking.k8s.io/api-hostname-route created
  4. If your HTTPRoute resource lists a backendRefs Service that’s in a namespace that’s different from the namespace where the HTTPRoute is defined, you’ll need to add a ReferenceGrant for cross-namespace service references.

Implement path-based routing Jump to heading

Migrating from Ingress API

Path-based routing with Gateway API behaves similarly to traditional Ingress rules. This makes it easy to migrate from HAProxy Kubernetes Ingress Controller or other Ingress controller implementations.

Path-based routing is useful when you have multiple applications under the same domain and want to route traffic based on the URL path. For example, you want to route traffic for example.com/web.

To implement path-based routing:

  1. Define an HTTPRoute for routing HTTP traffic for the target URL paths. In this example, we route traffic for the path /web.

    • The parentRefs section references one or more Gateways to which an HTTPRoute tries to attach. This HTTPRoute will attach to Gateway resources that have listeners with allowedRoutes matching the HTTPRoute kind and a matching namespace.
    • Under rules, each entry has a matches and backendRefs section. For a path-matching rule, set the type to PathPrefix. This means that URL paths that begin with the specified value will be routed to the service specified by the backendRefs entry. For example, requests beginning with /web will route to web-svc. A backendRefs entry refers to the name of a Service resource and its listening port.
    path-based-route.yaml
    yaml
    apiVersion: gateway.networking.k8s.io/v1
    kind: HTTPRoute
    metadata:
    name: path-based-route
    namespace: default
    spec:
    parentRefs:
    - name: example-haproxy-gateway
    rules:
    - matches:
    - path:
    type: PathPrefix
    value: /web
    backendRefs:
    - name: web-svc
    namespace: default
    port: 80
    path-based-route.yaml
    yaml
    apiVersion: gateway.networking.k8s.io/v1
    kind: HTTPRoute
    metadata:
    name: path-based-route
    namespace: default
    spec:
    parentRefs:
    - name: example-haproxy-gateway
    rules:
    - matches:
    - path:
    type: PathPrefix
    value: /web
    backendRefs:
    - name: web-svc
    namespace: default
    port: 80
  2. Apply the changes with kubectl:

    nix
    kubectl apply -f path-based-route.yaml
    nix
    kubectl apply -f path-based-route.yaml
    output
    text
    httproute.gateway.networking.k8s.io/path-based-route created
    output
    text
    httproute.gateway.networking.k8s.io/path-based-route created
  3. Optional: If your HTTPRoute resource lists a backendRefs Service that’s in a namespace that’s different from the namespace where the HTTPRoute is defined, you’ll need to add a ReferenceGrant for cross-namespace service references.

  4. Optional: When using path-based routing, you may want to restrict which hostnames your Gateway will accept traffic for. See Add a Gateway for a hostname.

  5. Optional: To test connectivity with the Gateway and HTTPRoute you just defined, deploy the example application using Deployment and Service resources. The application uses the hashicorp/ettp-echo Docker container image. It’ll receive HTTP traffic on port 5678 and respond with text indicating the application’s name.

    Note that the Service is named web-svc, which is the same Service name you specified when you defined your HTTPRoute in the previous steps, and it listens on port 80. This is how the HTTPRoute knows the Service to route to.

    app.yaml
    yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: web-svc
    namespace: default
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: web-svc
    template:
    metadata:
    labels:
    app: web-svc
    spec:
    containers:
    - name: web
    image: hashicorp/http-echo:0.2.3
    args: ["-text=WEB RESPONSE"]
    ports:
    - containerPort: 5678
    ---
    apiVersion: v1
    kind: Service
    metadata:
    name: web-svc
    namespace: default
    spec:
    selector:
    app: web-svc
    ports:
    - port: 80
    targetPort: 5678
    app.yaml
    yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: web-svc
    namespace: default
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: web-svc
    template:
    metadata:
    labels:
    app: web-svc
    spec:
    containers:
    - name: web
    image: hashicorp/http-echo:0.2.3
    args: ["-text=WEB RESPONSE"]
    ports:
    - containerPort: 5678
    ---
    apiVersion: v1
    kind: Service
    metadata:
    name: web-svc
    namespace: default
    spec:
    selector:
    app: web-svc
    ports:
    - port: 80
    targetPort: 5678

    Apply the changes with kubectl:

    nix
    kubectl apply -f app.yaml
    nix
    kubectl apply -f app.yaml
    output
    text
    deployment.apps/web-svc created
    service/web-svc created
    output
    text
    deployment.apps/web-svc created
    service/web-svc created
Test the connection (click to expand)

To test the connection to the application:

  1. Use the following command to find the NodePort configured for your HAProxy Unified Gateway Service. In this example the NodePort is 31080, the NodePort associated with the TCP port 8080.

    nix
    kubectl get service haproxy-unified-gateway -n haproxy-unified-gateway
    nix
    kubectl get service haproxy-unified-gateway -n haproxy-unified-gateway
    output
    text
    NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
    haproxy-unified-gateway NodePort 10.111.136.206 <none> 8080:31080/TCP,8443:31443/TCP,31024:31689/TCP 114m
    output
    text
    NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
    haproxy-unified-gateway NodePort 10.111.136.206 <none> 8080:31080/TCP,8443:31443/TCP,31024:31689/TCP 114m
  2. From a server that has connection to your cluster, such as the server from which you run kubectl, use curl to send a request to the NodePort at the hostname you configured in your Gateway. The HAProxy Unified Gateway service will forward the request to the port you defined in your Gateway.

    If your DNS is configured to resolve example.com, use this command:

    nix
    curl http://example.com/web
    nix
    curl http://example.com/web

    Otherwise, use this command to set the Host header to example.com:

    nix
    curl http://<NODEPORT_IP>:<NODEPORT>/web -H "Host: example.com"
    nix
    curl http://<NODEPORT_IP>:<NODEPORT>/web -H "Host: example.com"
    output
    text
    WEB RESPONSE
    output
    text
    WEB RESPONSE

Implement weighted traffic splitting Jump to heading

Use weighted traffic splitting when you want different percentages of traffic to route to different services. One use case is canary deployments. A canary deployment means routing a small percentage of traffic to the new version of your application, and then gradually ramping up, allowing you to release the new version in the safest possible way.

  1. Define an HTTPRoute for routing HTTP traffic to your service.

    • The parentRefs section references the Gateways to which the HTTPRoute wants to attach, which in this case is example-haproxy-gateway.
    • In this example, we’re using hostname-based routing, so we’re including the hostnames section.
    • The backendRefs section lists two services, blue and green. The blue service will receive 90% of the traffic and the green service will receive 10% of the traffic.
    http-route.yaml
    yaml
    apiVersion: gateway.networking.k8s.io/v1
    kind: HTTPRoute
    metadata:
    name: canary-route
    namespace: default
    spec:
    parentRefs:
    - name: example-haproxy-gateway
    hostnames:
    - "example.com"
    rules:
    - backendRefs:
    - name: blue
    namespace: default
    port: 80
    weight: 9
    - name: green
    namespace: default
    port: 80
    weight: 1
    http-route.yaml
    yaml
    apiVersion: gateway.networking.k8s.io/v1
    kind: HTTPRoute
    metadata:
    name: canary-route
    namespace: default
    spec:
    parentRefs:
    - name: example-haproxy-gateway
    hostnames:
    - "example.com"
    rules:
    - backendRefs:
    - name: blue
    namespace: default
    port: 80
    weight: 9
    - name: green
    namespace: default
    port: 80
    weight: 1
  2. Apply the changes with kubectl:

    nix
    kubectl apply -f http-route.yaml
    nix
    kubectl apply -f http-route.yaml
    output
    text
    httproute.gateway.networking.k8s.io/canary-route created
    output
    text
    httproute.gateway.networking.k8s.io/canary-route created
  3. Optional: To test connectivity with the Gateway and HTTPRoute, deploy two test applications using Deployments and Services. Here, we define two example applications using the hashicorp/ettp-echo Docker container image. They will each receive HTTP traffic on port 5678 and respond with text indicating receipt of the request.

    Note that the Services are named blue and green, the same Service names you specified when you defined your HTTPRoute in the previous steps, both listening on port 80, which you defined in the HTTPRoute as well. This is how the HTTPRoute knows what Service to route to.

    apps.yaml
    yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: blue
    namespace: default
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: blue
    template:
    metadata:
    labels:
    app: blue
    spec:
    containers:
    - name: blue
    image: hashicorp/http-echo
    args:
    - "-text=Hello from blue"
    ports:
    - containerPort: 5678
    ---
    apiVersion: v1
    kind: Service
    metadata:
    name: blue
    namespace: default
    spec:
    selector:
    app: blue
    ports:
    - port: 80
    targetPort: 5678
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: green
    namespace: default
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: green
    template:
    metadata:
    labels:
    app: green
    spec:
    containers:
    - name: green
    image: hashicorp/http-echo
    args:
    - "-text=Hello from green"
    ports:
    - containerPort: 5678
    ---
    apiVersion: v1
    kind: Service
    metadata:
    name: green
    namespace: default
    spec:
    selector:
    app: green
    ports:
    - port: 80
    targetPort: 5678
    apps.yaml
    yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: blue
    namespace: default
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: blue
    template:
    metadata:
    labels:
    app: blue
    spec:
    containers:
    - name: blue
    image: hashicorp/http-echo
    args:
    - "-text=Hello from blue"
    ports:
    - containerPort: 5678
    ---
    apiVersion: v1
    kind: Service
    metadata:
    name: blue
    namespace: default
    spec:
    selector:
    app: blue
    ports:
    - port: 80
    targetPort: 5678
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: green
    namespace: default
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: green
    template:
    metadata:
    labels:
    app: green
    spec:
    containers:
    - name: green
    image: hashicorp/http-echo
    args:
    - "-text=Hello from green"
    ports:
    - containerPort: 5678
    ---
    apiVersion: v1
    kind: Service
    metadata:
    name: green
    namespace: default
    spec:
    selector:
    app: green
    ports:
    - port: 80
    targetPort: 5678
  4. Apply the changes with kubectl:

    nix
    kubectl apply -f apps.yaml
    nix
    kubectl apply -f apps.yaml
    output
    text
    deployment.apps/blue created
    service/blue created
    deployment.apps/green created
    service/green created
    output
    text
    deployment.apps/blue created
    service/blue created
    deployment.apps/green created
    service/green created
Test the connection (click to expand)

To test the connection to the http-echo instances:

  1. Use the following command to find the NodePort configured for your HAProxy Unified Gateway Service. In this example the NodePort is 31080, the NodePort associated with the TCP port 8080.

    nix
    kubectl get service haproxy-unified-gateway -n haproxy-unified-gateway
    nix
    kubectl get service haproxy-unified-gateway -n haproxy-unified-gateway
    output
    text
    NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
    haproxy-unified-gateway NodePort 10.111.136.206 <none> 8080:31080/TCP,8443:31443/TCP,31024:31689/TCP 114m
    output
    text
    NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
    haproxy-unified-gateway NodePort 10.111.136.206 <none> 8080:31080/TCP,8443:31443/TCP,31024:31689/TCP 114m
  2. From a server that has connection to your cluster, such as the server from which you run kubectl, use curl to send a request to the NodePort at the hostname you configured in your Gateway. The HAProxy Unified Gateway service will forward the request to the port you defined in your Gateway.

    If your DNS is configured to resolve example.com, use this command:

    nix
    for i in {1..20}; do curl http://example.com -H "Host: example.com"; done
    nix
    for i in {1..20}; do curl http://example.com -H "Host: example.com"; done

    Otherwise, use this command to set the Host header to example.com:

    nix
    for i in {1..20}; do curl http://<NODEPORT_IP>:<NODEPORT> -H "Host: example.com"; done
    nix
    for i in {1..20}; do curl http://<NODEPORT_IP>:<NODEPORT> -H "Host: example.com"; done
    output
    text
    Hello from blue
    Hello from blue
    Hello from blue
    Hello from blue
    Hello from blue
    Hello from blue
    Hello from blue
    Hello from blue
    Hello from blue
    Hello from blue
    Hello from blue
    Hello from blue
    Hello from blue
    Hello from green
    Hello from green
    Hello from blue
    Hello from blue
    Hello from blue
    Hello from blue
    Hello from blue
    output
    text
    Hello from blue
    Hello from blue
    Hello from blue
    Hello from blue
    Hello from blue
    Hello from blue
    Hello from blue
    Hello from blue
    Hello from blue
    Hello from blue
    Hello from blue
    Hello from blue
    Hello from blue
    Hello from green
    Hello from green
    Hello from blue
    Hello from blue
    Hello from blue
    Hello from blue
    Hello from blue

    Most responses should be from the blue application, with only a small percentage from the green application. The exact number of responses from each may vary from run to run.

Add a ReferenceGrant for cross-namespace service references Jump to heading

If your HTTPRoute lists a Service under backendRefs that’s in a different namespace than the HTTPRoute resource, you’ll need to define a ReferenceGrant resource to allow cross-namespace communication between the HTTPRoute and the Service.

Below, we allow an HTTPRoute in the default namespace to reference a Service in the foo namespace. Note that the to section doesn’t need a namespace because a ReferenceGrant can refer only to resources defined in its own namespace.

foo-referencegrant.yaml
yaml
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: ReferenceGrant
metadata:
name: refgrantns1
namespace: foo
spec:
from:
- group: "gateway.networking.k8s.io"
kind: "HTTPRoute"
namespace: default
to:
- group: ""
kind: "Service"
foo-referencegrant.yaml
yaml
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: ReferenceGrant
metadata:
name: refgrantns1
namespace: foo
spec:
from:
- group: "gateway.networking.k8s.io"
kind: "HTTPRoute"
namespace: default
to:
- group: ""
kind: "Service"

Apply the changes with kubectl:

nix
kubectl apply -f foo-referencegrant.yaml
nix
kubectl apply -f foo-referencegrant.yaml

Rewrite the requested URL path Jump to heading

You may want to rewrite the URL path before forwarding the request to the backend, because:

  • Your backend expects a specific URL structure.
  • You want to hide internal routing details.
  • You’re consolidating multiple backend services under a shared URL prefix.
  • You want to expose a simpler API.
  • You’re migrating legacy Ingress rules to Gateway API.

To rewrite the requested URL path:

  1. Define an HTTPRoute for routing HTTP traffic to your service.

    • The path-matching rule specifies the type as PathPrefix. This means that for URL paths that begin with the specified value will be routed to the corresponding service specified by the backendRefs in the rule. There’s also a filter of type: URLRewrite that instructs the HTTPRoute to replace /service with /.
    http-route.yaml
    yaml
    apiVersion: gateway.networking.k8s.io/v1
    kind: HTTPRoute
    metadata:
    name: rewrite-route
    namespace: default
    spec:
    parentRefs:
    - name: example-haproxy-gateway
    rules:
    - matches:
    - path:
    type: PathPrefix
    value: /service
    filters:
    - type: URLRewrite
    urlRewrite:
    path:
    type: ReplacePrefixMatch
    replacePrefixMatch: /
    backendRefs:
    - name: rewrite-backend-svc
    namespace: default
    port: 80
    http-route.yaml
    yaml
    apiVersion: gateway.networking.k8s.io/v1
    kind: HTTPRoute
    metadata:
    name: rewrite-route
    namespace: default
    spec:
    parentRefs:
    - name: example-haproxy-gateway
    rules:
    - matches:
    - path:
    type: PathPrefix
    value: /service
    filters:
    - type: URLRewrite
    urlRewrite:
    path:
    type: ReplacePrefixMatch
    replacePrefixMatch: /
    backendRefs:
    - name: rewrite-backend-svc
    namespace: default
    port: 80
  2. Apply the changes with kubectl:

    nix
    kubectl apply -f http-route.yaml
    nix
    kubectl apply -f http-route.yaml
    output
    text
    httproute.gateway.networking.k8s.io/rewrite-route created
    output
    text
    httproute.gateway.networking.k8s.io/rewrite-route created
  3. Optional: To test connectivity with the Gateway and HTTPRoute, deploy a test application using a Deployment and a Service. Here, we define an example application using the hashicorp/ettp-echo Docker container image. It will receive HTTP traffic on port 5678 (the Gateway terminates the TLS connection and then communicates with backend services over HTTP) and respond with text indicating receipt of the request.

    Note that the Service is named rewrite-backend-svc, the same Service name you specified when you defined your HTTPRoute in the previous steps, listening on port 80, which you defined in the HTTPRoute as well. This is how the HTTPRoute knows what Service to route to.

    apps.yaml
    yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: rewrite-backend
    namespace: default
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: rewrite-backend
    template:
    metadata:
    labels:
    app: rewrite-backend
    spec:
    containers:
    - name: echo
    image: hashicorp/http-echo
    args:
    - "-text=Rewrite backend reached"
    ports:
    - containerPort: 5678
    ---
    apiVersion: v1
    kind: Service
    metadata:
    name: rewrite-backend-svc
    namespace: default
    spec:
    selector:
    app: rewrite-backend
    ports:
    - port: 80
    targetPort: 5678
    apps.yaml
    yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: rewrite-backend
    namespace: default
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: rewrite-backend
    template:
    metadata:
    labels:
    app: rewrite-backend
    spec:
    containers:
    - name: echo
    image: hashicorp/http-echo
    args:
    - "-text=Rewrite backend reached"
    ports:
    - containerPort: 5678
    ---
    apiVersion: v1
    kind: Service
    metadata:
    name: rewrite-backend-svc
    namespace: default
    spec:
    selector:
    app: rewrite-backend
    ports:
    - port: 80
    targetPort: 5678
  4. Apply the changes with kubectl:

    nix
    kubectl apply -f apps.yaml
    nix
    kubectl apply -f apps.yaml
    output
    text
    deployment.apps/tls-test created
    service/tls-test-svc created
    output
    text
    deployment.apps/tls-test created
    service/tls-test-svc created
Test the connection (click to expand)

To test the connection to the http-echo instances:

  1. Use the following command to find the NodePort configured for your HAProxy Unified Gateway Service. In this example the NodePort is 31080, the NodePort associated with the TCP port 8080.

    nix
    kubectl get service haproxy-unified-gateway -n haproxy-unified-gateway
    nix
    kubectl get service haproxy-unified-gateway -n haproxy-unified-gateway
    output
    text
    NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
    haproxy-unified-gateway NodePort 10.111.136.206 <none> 8080:31080/TCP,8443:31443/TCP,31024:31689/TCP 114m
    output
    text
    NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
    haproxy-unified-gateway NodePort 10.111.136.206 <none> 8080:31080/TCP,8443:31443/TCP,31024:31689/TCP 114m
  2. From a server that has connection to your cluster, such as the server from which you run kubectl, use curl to send a request to the NodePort at the hostname you configured in your Gateway. The HAProxy Unified Gateway service will forward the request to the port you defined in your Gateway.

    If your DNS is configured to resolve example.com, use this command:

    nix
    curl http://example.com/service/foo
    nix
    curl http://example.com/service/foo

    Otherwise, use this command to set the Host header to example.com:

    nix
    curl http://<NODEPORT_IP>:<NODEPORT>/service/foo -H "Host: example.com"
    nix
    curl http://<NODEPORT_IP>:<NODEPORT>/service/foo -H "Host: example.com"
    output
    text
    Rewrite backend reached
    output
    text
    Rewrite backend reached

    This indicates that the URL path was rewritten from /service/foo to /.

See also Jump to heading

Do you have any suggestions on how we can improve the content of this page?