Expose a service with the Gateway API
The Gateway API is the newer, more expressive successor to Ingress. On skube
the ingress bundle ships the Gateway API CRDs, a provider-managed traefik
GatewayClass, and cert-manager wired for Gateway certificates. You write your
own Gateway and HTTPRoute; the cluster is single-tenant, so you own them
outright.
If you just need to publish one service, Ingress is simpler. Reach for the Gateway API when you want route-level control: header and method matching, traffic splitting, or many routes sharing one entry point.
We start over plain HTTP to confirm routing works, then add a domain and HTTPS by overwriting the same Gateway.
Deploy a sample app
Section titled “Deploy a sample app”cat <<'EOF' | kubectl apply -f -apiVersion: apps/v1kind: Deploymentmetadata: name: hello-gatewayspec: replicas: 1 selector: matchLabels: app: hello-gateway template: metadata: labels: app: hello-gateway spec: containers: - name: hello-gateway image: nginx ports: - containerPort: 80---apiVersion: v1kind: Servicemetadata: name: hello-gatewayspec: selector: app: hello-gateway ports: - port: 80 targetPort: 80EOFExpose over plain HTTP
Section titled “Expose over plain HTTP”Create an HTTP-only Gateway with no hostname and an HTTPRoute with no hostnames. With neither set, the listener and route match any request, so Traefik serves it to anyone hitting the node:
cat <<'EOF' | kubectl apply -f -apiVersion: gateway.networking.k8s.io/v1kind: Gatewaymetadata: name: hello-gatewayspec: gatewayClassName: traefik listeners: - name: http protocol: HTTP port: 80 allowedRoutes: namespaces: from: Same---apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata: name: hello-gatewayspec: parentRefs: - name: hello-gateway rules: - backendRefs: - name: hello-gateway port: 80EOFReach it directly at your node IP (the same IP as your API server,
https://<your-node-ip>:6443):
curl http://<your-node-ip>You should get the nginx welcome page.
Add a domain and HTTPS
Section titled “Add a domain and HTTPS”To serve real traffic over HTTPS you need a hostname that resolves to the cluster. Let’s Encrypt validates over an HTTP-01 challenge, so DNS must resolve before you apply.
In your DNS provider, create an A record pointing your hostname at your
cluster’s node IP:
gateway.example.com. A <your-node-ip>Confirm it resolves before continuing:
dig +short gateway.example.comNow overwrite the Gateway with your real hostname, the
cert-manager.io/cluster-issuer annotation, and an HTTPS listener whose
certificateRefs point at the hello-gateway-tls secret. The HTTP listener
stays for the ACME challenge and plain-HTTP traffic; cert-manager fills the secret.
cat <<'EOF' | kubectl apply -f -apiVersion: gateway.networking.k8s.io/v1kind: Gatewaymetadata: name: hello-gateway annotations: cert-manager.io/cluster-issuer: letsencrypt-prodspec: gatewayClassName: traefik listeners: - name: http protocol: HTTP port: 80 hostname: gateway.example.com allowedRoutes: namespaces: from: Same - name: https protocol: HTTPS port: 443 hostname: gateway.example.com allowedRoutes: namespaces: from: Same tls: mode: Terminate certificateRefs: - kind: Secret name: hello-gateway-tls---apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata: name: hello-gatewayspec: parentRefs: - name: hello-gateway hostnames: - gateway.example.com rules: - backendRefs: - name: hello-gateway port: 80EOFThe certificate takes a minute or two on first issuance. Watch it become ready:
kubectl get certificate hello-gateway-tls -wOnce READY is True, your app is live over HTTPS:
curl https://gateway.example.comIf issuance stalls, check the Gateway and certificate status:
kubectl describe gateway hello-gatewaykubectl describe certificate hello-gateway-tlsThe usual cause is DNS that doesn’t yet resolve to your node IP.
Clean up
Section titled “Clean up”kubectl delete httproute hello-gatewaykubectl delete gateway hello-gatewaykubectl delete service hello-gatewaykubectl delete deployment hello-gatewaykubectl delete secret hello-gateway-tls --ignore-not-found