All Products
Search
Document Center

Container Service for Kubernetes:Expose a service with Gateway API and ALB

Last Updated:Jun 15, 2026

Configure Gateway, HTTPRoute, and TLS resources to route external traffic to ACK services through an Application Load Balancer (ALB) instance.

Resource model

The Gateway API defines three resource types, each owned by a different role:

Resource Owner Purpose
GatewayClass Infrastructure provider (Alibaba Cloud) Defines a gateway class backed by a controller — here, 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

Platform teams control infrastructure while application teams manage routing rules independently.

The ALB Ingress Controller has supported the Gateway API since version 2.17.0.

Prerequisites

Ensure the following:

  • 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

The ALB Ingress Controller automatically creates a GatewayClass resource named alb. Verify it exists before proceeding.

kubectl

Check the GatewayClass:

kubectl get gatewayclass

Example output:

NAME   CONTROLLER                         ACCEPTED   AGE
alb    gateways.alibabacloud.com/alb/v1   True       1m

ACCEPTED: True confirms the GatewayClass is ready.

Deploy a sample application

Create an httpbin.yaml file:

The Service uses ClusterIP by default. If your cluster uses the Flannel network plug-in, change spec.type to NodePort.
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

Important

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 provisions an ALB instance with an HTTP listener on port 80 for *.ingress.top. The HTTPRoute forwards 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

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

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

Use cases

Modify request headers with a filter

HTTPRoute filters let you rewrite requests or responses in transit. This example adds a custom request header.

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

The RequestHeaderModifier filter modifies requests before they reach the backend:

  • Adds my-header: foo to matching requests.

  • Uses the add action, which appends rather than replaces existing headers with the same name.

Apply the routing rule:

kubectl apply -f httproute-filter.yaml

Test with the /header endpoint, which echoes 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

My-Header: foo in the output confirms the filter works.

Split traffic by weight

Assign weights to multiple backend Services to distribute traffic proportionally, such as for canary deployments.

Create an nginx.yaml file with two NGINX deployments:

Expand to view the YAML content

apiVersion: apps/v1
kind: Deployment
metadata:
  name: old-nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      run: old-nginx
  template:
    metadata:
      labels:
        run: old-nginx
    spec:
      containers:
      - image: registry.cn-hangzhou.aliyuncs.com/acs-sample/old-nginx
        imagePullPolicy: Always
        name: old-nginx
        ports:
        - containerPort: 80
          protocol: TCP
      restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
  name: old-nginx
spec:
  type: ClusterIP
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    run: old-nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: new-nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      run: new-nginx
  template:
    metadata:
      labels:
        run: new-nginx
    spec:
      containers:
      - image: registry.cn-hangzhou.aliyuncs.com/acs-sample/new-nginx
        imagePullPolicy: Always
        name: new-nginx
        ports:
        - containerPort: 80
          protocol: TCP
      restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
  name: new-nginx
spec:
  type: ClusterIP
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    run: new-nginx

old-nginx returns old; new-nginx returns new. Both deploy 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 splits roughly 1:1 between the two backends.

Configure a TLS certificate

Add HTTPS termination to the Gateway by referencing a Kubernetes Secret with 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

In Terminate mode, ALB decrypts HTTPS traffic and forwards plain HTTP to backends. The certificateRefs field references 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

Verify the certificate:

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)

CN = ingress.top and the TLSv1.2 cipher confirm TLS termination works. The self-signed certificate error is expected — use a CA-signed certificate in production.

Test HTTPS access with the -k flag to skip self-signed certificate verification:

curl -H "Host: demo.ingress.top" -k https://${ALB_DOMAIN}/version

Example output:

version: v1
hostname: go-httpbin-547xxxxxf6-xxxxx

Next steps

  • Gateway API — Full specification, including advanced routing such as header-based matching and traffic mirroring.

  • ALB Ingress Controller — ALB-specific annotations and configuration for production workloads.