Migrating from Ingress to Gateway API

It’s been a little while since I last published anything on my personal website but I have been creating quite a lot of content over on isovalent.com and posting a lot of Cilium updates on LinkedIn.

Most of the content over there is long-form and there is a little feature I discovered this morning that I thought I should share here for posterity.

I have been working on Gateway API for the past few months: previously at my last gig with HashiCorp (Consul was one of the first platforms to support it) and now at Isovalent, where Gateway API is officially released with Cilium 1.13. To help folks understanding Gateway API generally (and how it works on Cilium), I have created with an online lab which has been well-received. Check it out here.

I had been wondering what the migration path would be for Kubernetes Ingress users. Officially the line seems to be that Gateway API will not replace Ingress API but in the long-run, I imagine that it will (it doesn’t make sense for operators to run in parallel two means to route traffic into their clusters).

I was browsing the Gateway API website when I came across some details about a tool to migrate Ingress to Gateway API resources: it’s originally called Ingress to Gateway.

Written in Go, the tool checks out your Kubernetes Cluster (based on your current Kube Config), reads the Ingress API resources and outputs equivalent Gateway API resources to the terminal.

The project is still experimental as I write this blog post so obviously double-check the results and test before deploying.

The only requirement is to have Go installed and run go run . to compile and execute the tool. It worked straight-away for me.

I actually used the Isovalent Service Mesh lab to test it out as we use Ingress resources in the lab.

The Ingress Resouces deployed is the following:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: basic-ingress
  namespace: default
spec:
  ingressClassName: cilium
  rules:
  - http:
      paths:
      - backend:
          service:
            name: details
            port:
              number: 9080
        path: /details
        pathType: Prefix
      - backend:
          service:
            name: productpage
            port:
              number: 9080
        path: /
        pathType: Prefix

One of the problems with Ingress is that Ingress definitions are specified in one big YAML file and managing it can be challenging when you have multiple teams looking at applying various routing configurations.

That’s addressed by Gateway API by separating the definition of an Gateway in one resource and the actual routes in separate resources (for example, HTTPRoute).

And that’s what you get when you use Ingress2Gateway:

apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
  creationTimestamp: null
  name: cilium
  namespace: default
spec:
  gatewayClassName: cilium
  listeners:
  - name: http
    port: 80
    protocol: HTTP
status: {}
---
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
  creationTimestamp: null
  name: all-hosts
  namespace: default
spec:
  parentRefs:
  - name: cilium
  rules:
  - backendRefs:
    - name: details
      port: 9080
    matches:
    - path:
        type: PathPrefix
        value: /details
  - backendRefs:
    - name: productpage
      port: 9080
    matches:
    - path:
        type: PathPrefix
        value: /
status:
  parents: []

This is pretty good. There are some fields that are not required (like creationTimestamp) but regardless, it works. When I saved this output into a yaml file called ingress2gateway.yaml and apply it, access to the Service behind the Gateway was successful:

root@server:~# vi ingress2gateway.yaml
root@server:~# kubectl apply -f ingress2gateway.yaml 
gateway.gateway.networking.k8s.io/cilium created
httproute.gateway.networking.k8s.io/all-hosts created
root@server:~# 
root@server:~# 
root@server:~# kubectl get svc
NAME                    TYPE           CLUSTER-IP      EXTERNAL-IP      PORT(S)        AGE
cilium-gateway-cilium   LoadBalancer   10.96.251.20    172.18.255.201   80:31478/TCP   7s
details                 ClusterIP      10.96.130.54    <none>           9080/TCP       49s
kubernetes              ClusterIP      10.96.0.1       <none>           443/TCP        13m
productpage             ClusterIP      10.96.7.88      <none>           9080/TCP       49s
ratings                 ClusterIP      10.96.179.111   <none>           9080/TCP       49s
reviews                 ClusterIP      10.96.176.21    <none>           9080/TCP       49s

root@server:~# kubectl get gateway
NAME     CLASS    ADDRESS          READY   AGE
cilium   cilium   172.18.255.201   True    68s
root@server:~# GATEWAY=$(kubectl get gateway cilium -o jsonpath='{.status.addresses[0].value}')
root@server:~# echo $GATEWAY
172.18.255.201
root@server:~# 
root@server:~# curl --fail -s http://$GATEWAY/details/1 | jq
{
  "id": 1,
  "author": "William Shakespeare",
  "year": 1595,
  "type": "paperback",
  "pages": 200,
  "publisher": "PublisherA",
  "language": "English",
  "ISBN-10": "1234567890",
  "ISBN-13": "123-1234567890"
}

That’s it for today. Thanks for reading.

Leave a comment