The Gateway API is an official Kubernetes project and the next-generation routing API that replaces Ingress. This topic shows how to use the Gateway API with the ALB Ingress Controller to expose services in an ACK cluster through an Application Load Balancer (ALB) instance.
Resource model
The Gateway API models service networking through three resource types, each owned by a different role:
| Resource | Owner | Purpose |
|---|---|---|
| GatewayClass | Infrastructure provider (Alibaba Cloud) | Defines a class of gateways backed by a specific controller — in this case, the ALB Ingress Controller |
| Gateway | Cluster operator | Provisions an ALB instance and configures listeners (ports, protocols, hostnames) |
| HTTPRoute | Application developer | Defines routing rules that map incoming requests to backend Services |
This separation lets platform teams control infrastructure while application teams manage their own routing rules independently.
The ALB Ingress Controller has supported the Gateway API since version 2.17.0.
Prerequisites
Before you begin, ensure that you have:
-
An ACK managed cluster running Kubernetes 1.24 or later
-
The ALB Ingress Controller version 2.17.0 or later installed in the cluster
-
The Gateway API version 1.1.0 or later installed in the cluster
-
Two vSwitches that support ALB in the VPC where your cluster is deployed
Verify the environment
When all prerequisites are met, the ALB Ingress Controller automatically creates a GatewayClass resource named alb. Verify this before proceeding.
kubectl
Run the following command:
kubectl get gatewayclass
Example output:
NAME CONTROLLER ACCEPTED AGE
alb gateways.alibabacloud.com/alb/v1 True 1m
The ACCEPTED: True status confirms the GatewayClass is ready.
Deploy a sample application
Create an httpbin.yaml file with the following content.
The Service usesClusterIPby default. If your cluster uses the Flannel network plug-in, changespec.typetoNodePort.
apiVersion: apps/v1
kind: Deployment
metadata:
name: go-httpbin
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: go-httpbin
template:
metadata:
labels:
app: go-httpbin
version: v1
spec:
containers:
- image: registry.cn-hangzhou.aliyuncs.com/mse/go-httpbin
args:
- "--port=8090"
- "--version=v1"
imagePullPolicy: Always
name: go-httpbin
ports:
- containerPort: 8090
---
apiVersion: v1
kind: Service
metadata:
name: go-httpbin
namespace: default
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 8090
protocol: TCP
selector:
app: go-httpbin
Apply the manifest:
kubectl apply -f httpbin.yaml
Deploy the Gateway and routing rule
Creating a Gateway provisions an ALB instance and incurs fees. ALB instances created by a Gateway are not automatically deleted when you delete the cluster. Delete the Gateway resource before deleting the cluster to avoid unexpected charges.
Step 1: Create the Gateway and HTTPRoute
Create a gateway.yaml file:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: alb
namespace: default
spec:
gatewayClassName: alb
listeners:
- name: http
protocol: HTTP
port: 80
hostname: "*.ingress.top"
allowedRoutes:
namespaces:
from: Same
---
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: demo-route
spec:
parentRefs: # Attach this route to the Gateway above
- group: gateway.networking.k8s.io
kind: Gateway
name: alb
hostnames:
- demo.ingress.top
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs: # Forward matched traffic to go-httpbin on port 80
- kind: Service
name: go-httpbin
port: 80
The Gateway creates an ALB instance with an HTTP listener on port 80 for *.ingress.top. The HTTPRoute attaches to that Gateway and forwards all traffic for demo.ingress.top to the go-httpbin Service.
Apply the manifest:
kubectl apply -f gateway.yaml
Step 2: Wait for the Gateway to be ready
kubectl wait --for=condition=programmed gateway/alb --timeout=120s
Once the command returns, verify the Gateway address:
kubectl get gateway alb
Example output:
NAME CLASS ADDRESS PROGRAMMED AGE
alb alb alb-0mwhq4ck6xxxxxxxxx.cn-hangzhou.alb.aliyuncsslb.com True 2m12s
Step 3: Check the HTTPRoute status
kubectl describe httproute demo-route | grep Status -A 20
Example output:
Status:
Parents:
Conditions:
Last Transition Time: 2025-05-23T08:21:25Z
Message: Route is accepted.
Observed Generation: 1
Reason: Accepted
Status: True
Type: Accepted
Last Transition Time: 2025-05-23T08:21:25Z
Message: Route is resolved.
Observed Generation: 1
Reason: ResolvedRefs
Status: True
Type: ResolvedRefs
Controller Name: gateways.alibabacloud.com/alb/v1
Parent Ref:
Group: gateway.networking.k8s.io
Kind: Gateway
Name: alb
Both Accepted: True and ResolvedRefs: True confirm the route is active.
Step 4: Test access
Get the Gateway address and send a test request:
export ALB_DOMAIN=$(kubectl get gateway alb -n default -o jsonpath='{.status.addresses[?(@.type=="Hostname")].value}')
curl -H "Host: demo.ingress.top" http://${ALB_DOMAIN}/version
Example output:
version: v1
hostname: go-httpbin-547xxxxxf6-xxxxx
Scenarios
Modify request headers with a filter
HTTPRoute filters let you rewrite requests or responses in transit. This example adds a custom request header before traffic reaches the backend.
Create an httproute-filter.yaml file:
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: demo-filter
spec:
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: alb
hostnames:
- filter.ingress.top
rules:
- matches:
- path:
type: PathPrefix
value: /
filters:
- type: RequestHeaderModifier
requestHeaderModifier:
add:
- name: my-header
value: foo
backendRefs:
- kind: Service
name: go-httpbin
port: 80
This HTTPRoute uses a RequestHeaderModifier filter, which runs before the request reaches the backend. The filter:
-
Adds
my-header: footo every request that matches the route. -
Uses the
addaction, which appends the header rather than replacing any existing header with the same name.
Apply the routing rule:
kubectl apply -f httproute-filter.yaml
Send a test request to the /header endpoint, which echoes all request headers:
curl -H "Host: filter.ingress.top" http://${ALB_DOMAIN}/header
Example output:
headers: {
"Accept": [
"*/*"
],
"Connection": [
"close"
],
"Host": [
"filter.ingress.top"
],
"My-Header": [
"foo"
],
"Path": [
"/header"
],
"Protocol": [
"HTTP/1.1"
],
"Remoteip": [
"118.xx.xx.91"
],
"URL": [
"/header"
],
"User-Agent": [
"curl/8.9.1"
]
}
query param:
, hostname: go-httpbin-547xxxxxf6-xxxxx
The My-Header: foo entry in the output confirms the filter is working.
Split traffic by weight
Assign weights to multiple backend Services to distribute traffic proportionally. This is useful for canary deployments.
Create an nginx.yaml file with two NGINX deployments:
old-nginx returns the string old; new-nginx returns new. Both are deployed in the default namespace.
Create an httproute-weight.yaml file:
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: demo-weight
spec:
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: alb
hostnames:
- weight.ingress.top
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
# Weights are relative, not percentages — they do not need to add up to 100.
- kind: Service
name: old-nginx
port: 80
weight: 100
- kind: Service
name: new-nginx
port: 80
weight: 100
Both backends have equal weights (100:100), so traffic splits 1:1.
Apply the manifests:
kubectl apply -f nginx.yaml
kubectl apply -f httproute-weight.yaml
Send 10 requests to observe the distribution:
for i in {1..10}; do curl -H "Host: weight.ingress.top" http://${ALB_DOMAIN}/; done
Example output:
old
new
new
old
new
old
old
new
new
old
Traffic is distributed roughly 1:1 between the two backends.
Configure a TLS certificate
Add HTTPS termination to the Gateway by referencing a Kubernetes Secret that contains your TLS certificate.
Step 1: Create a self-signed certificate
openssl req -subj '/CN=ingress.top' -new -newkey rsa:2048 -sha256 \
-days 365 -nodes -x509 -keyout server.key -out server.crt \
-addext "subjectAltName = DNS:ingress.top" \
-addext "keyUsage = digitalSignature" \
-addext "extendedKeyUsage = serverAuth" 2> /dev/null; \
openssl x509 -in server.crt -subject -noout
Step 2: Create a TLS Secret
kubectl create secret tls ingress.top --key server.key --cert server.crt
Step 3: Update the Gateway to add an HTTPS listener
Create a gateway-tls.yaml file:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: alb
namespace: default
spec:
gatewayClassName: alb
listeners:
- name: http
protocol: HTTP
port: 80
hostname: "*.ingress.top"
allowedRoutes:
namespaces:
from: Same
- name: https
protocol: HTTPS
port: 443
hostname: "*.ingress.top"
allowedRoutes:
namespaces:
from: Same
tls:
mode: Terminate # ALB terminates TLS and forwards plain HTTP to backends
certificateRefs:
- kind: Secret
name: ingress.top # The Secret created in the previous step
The Terminate mode means ALB decrypts incoming HTTPS traffic and forwards plain HTTP to your backend Services. The certificateRefs field points to the Secret holding the certificate and private key.
Apply the update:
kubectl apply -f gateway-tls.yaml
Step 4: Verify the certificate
Wait for the Gateway PROGRAMMED status to return to True:
kubectl wait --for=condition=programmed gateway/alb --timeout=120s
Then verify the certificate is served correctly:
openssl s_client -servername ingress.top -connect ${ALB_DOMAIN}:443
Example output:
CONNECTED(00000003)
depth=0 CN = ingress.top
verify error:num=18:self-signed certificate
verify return:1
depth=0 CN = ingress.top
verify return:1
---
Certificate chain
0 s:CN = ingress.top
i:CN = ingress.top
a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256
v:NotBefore: Jun 24 10:49:39 2025 GMT; NotAfter: Jun 24 10:49:39 2026 GMT
---
Server certificate
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
subject=CN = ingress.top
issuer=CN = ingress.top
---
...
New, TLSv1.2, Cipher is ECDHE-RSA-AES128-GCM-SHA256
Server public key is 2048 bit
...
Verify return code: 18 (self-signed certificate)
The CN = ingress.top in the certificate chain and TLSv1.2 cipher confirm TLS termination is working. The self-signed certificate error is expected — production deployments should use a CA-signed certificate.
To test HTTPS access to the application, use the -k flag to skip certificate verification (required for self-signed certs):
curl -H "Host: demo.ingress.top" -k https://${ALB_DOMAIN}/version
Example output:
version: v1
hostname: go-httpbin-547xxxxxf6-xxxxx
What's next
-
Gateway API — Learn about the full Gateway API specification, including advanced routing features such as header-based matching and traffic mirroring.
-
ALB Ingress Controller — Explore ALB-specific annotations and configuration options for production workloads.