How to setup GatewayAPI on Kubernetes cluster

How to setup GatewayAPI on Kubernetes cluster

kubernetes_gatewayapi_setup

Here in this article we will try to setup GatewayAPI implementation Easegress to provide traffic routing capability to the backend kubernetes services.

Test Environment

Kubernetes cluster v1.32.0

What is Gateway API

Gateway API is an add-on containing API kinds that provide dynamic infrastructure provisioning and advanced traffic routing features. Gateway API is a set of specifications that are defined as custom resources and are supported by many implementations.

Gateway API Resources

Gateway API has three stable API kinds:

  • GatewayClass: Defines a set of gateways with common configuration and managed by a controller that implements the class.
  • Gateway: Defines an instance of traffic handling infrastructure, such as cloud load balancer.
  • HTTPRoute: Defines HTTP-specific rules for mapping traffic from a Gateway listener to a representation of backend network endpoints. These endpoints are often represented as a Service.

What is Easegress

Easegress is a Cloud Native traffic orchestration system. It can function as a sophisticated modern gateway, a robust distributed cluster, a flexible traffic orchestrator, or even an accessible service mesh. Easegress currently supports Gateway API v1beta1 version of the specification by GatewayController.

Procedure

Step1: Install Gateway API CRD’s

A Gateway API bundle represents the set of CRDs associated with a version of Gateway API. The standard release channel includes all resources that have graduated to GA or beta, including GatewayClass, Gateway, HTTPRoute, and ReferenceGrant. To install this channel, run the following kubectl command.

admin@kubemaster:~$ kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.0/standard-install.yaml
customresourcedefinition.apiextensions.k8s.io/gatewayclasses.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/gateways.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/grpcroutes.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/httproutes.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/referencegrants.gateway.networking.k8s.io created

Step2: Authorize Easegress GatewayController for using the Kubernetes API

If your cluster is configured with RBAC, first you will need to authorize Easegress GatewayController for using the Kubernetes API. Below is an example configuration. Here we are creating a service account “easegress-gateway-controller” and binding a clusterrole to the serivce account using clusterrolebinding as shown below.

Gateway controller watches a Kubernetes API for Gateway API resources and reconciles Load Balancing resources to implement the networking behavior specified by the Gateway resources.

File: easegress_rbac.yml

---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: easegress-gateway-controller
rules:
- apiGroups: [""] # "" indicates the core API group
  resources: ["services", "secrets", "endpoints", "namespaces"]
  verbs: ["get", "watch", "list"]
- apiGroups: ["gateway.networking.k8s.io"]
  resources: ["gatewayclasses", "httproutes", "gateways"]
  verbs: ["get", "watch", "list"]
- apiGroups: ["gateway.networking.k8s.io"]
  resources: ["gatewayclasses/status", "gateways/status"]
  verbs: ["update"]
# used for extensions
- apiGroups: ["easegress.megaease.com"]
  resources: ["filterspecs"]
  verbs: ["get", "watch", "list"]

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: easegress-gateway-controller
  namespace: default

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: easegress-gateway-controller
subjects:
- kind: ServiceAccount
  name: easegress-gateway-controller
  namespace: default
roleRef:
  kind: ClusterRole
  name: easegress-gateway-controller
  apiGroup: rbac.authorization.k8s.io
admin@kubemaster:~$ kubectl apply -f easegress_rbac.yml
clusterrole.rbac.authorization.k8s.io/easegress-gateway-controller created
serviceaccount/easegress-gateway-controller created
clusterrolebinding.rbac.authorization.k8s.io/easegress-gateway-controller created

Step3: Create ConfigMap to store Easegress server configuration and Easegress gateway configuration

Here we are going to create a ConfigMap to store Easegress server configuration and Easegress gateway configuration. The easegress-server.yaml creates Easegress instance named easegress-gateway-controller and controller.yaml defines GatewayController object for Easegress.

File: easegress_cm.yml

apiVersion: v1
kind: ConfigMap
metadata:
  name: easegress-cm
  namespace: default
data:
  easegress-server.yaml: |
    name: gateway-easegress
    cluster-name: easegress-gateway-controller
    cluster-role: primary
    api-addr: 0.0.0.0:2381
    data-dir: /opt/easegress/data
    log-dir: /opt/easegress/log
    debug: false
  controller.yaml: |
    kind: GatewayController
    name: gateway-controller-example
    kubeConfig:
    masterURL:
    namespaces: []
admin@kubemaster:~$ kubectl apply -f easegress_cm.yml 
configmap/easegress-cm created
admin@kubemaster:~$ kubectl get cm
NAME               DATA   AGE
easegress-cm       2      5s

Step4: Deploy Easegress GatewayController

To deploy the GatewayController, we will create a Deployment and a Service as below.

The GatewayController is created via the command line argument initial-object-config-files of easegress-server. Note that Easegress logs and data are stored to emptyDir called gateway-data-volume inside the pod as GatewayController is stateless so we can restart new pods without preserving previous state.

File: easegress_deployment.yml

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: easegress-gateway
  name: easegress
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: easegress-gateway
  template:
    metadata:
      labels:
        app: easegress-gateway
    spec:
      serviceAccountName: easegress-gateway-controller
      containers:
      - args:
        - -c
        - |-
          /opt/easegress/bin/easegress-server \
            -f /opt/eg-config/easegress-server.yaml \
            --initial-object-config-files /opt/eg-config/controller.yaml \
            --initial-cluster $(EG_NAME)=http://localhost:2380
        command:
        - /bin/sh
        env:
        - name: EG_NAME
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.name
        image: megaease/easegress:latest
        imagePullPolicy: IfNotPresent
        name: easegress-primary
        resources:
          limits:
            cpu: 1200m
            memory: 2Gi
          requests:
            cpu: 100m
            memory: 256Mi
        volumeMounts:
        - mountPath: /opt/eg-config/easegress-server.yaml
          name: easegress-cm
          subPath: easegress-server.yaml
        - mountPath: /opt/eg-config/controller.yaml
          name: easegress-cm
          subPath: controller.yaml
        - mountPath: /opt/easegress/data
          name: gateway-data-volume
        - mountPath: /opt/easegress/log
          name: gateway-data-volume
      restartPolicy: Always
      volumes:
      - emptyDir: {}
        name: gateway-data-volume
      - configMap:
          defaultMode: 420
          items:
          - key: easegress-server.yaml
            path: easegress-server.yaml
          - key: controller.yaml
            path: controller.yaml
          name: easegress-cm
        name: easegress-cm
admin@kubemaster:~$ kubectl apply -f easegress_deployment.yml 
deployment.apps/easegress created
admin@kubemaster:~$ kubectl get pods 
NAME                                  READY   STATUS    RESTARTS   AGE
easegress-576cdb7f9b-bcnzp            1/1     Running   0          77s

Step5: Deploy Backend Services

Here we are going to deploy sample application exposed as service on NodePort as shown below.

File: easegress_application.yml

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-deployment
spec:
  selector:
    matchLabels:
      app: products
      department: sales
  replicas: 2
  template:
    metadata:
      labels:
        app: products
        department: sales
    spec:
      containers:
      - name: hello-v1
        image: "us-docker.pkg.dev/google-samples/containers/gke/hello-app:1.0"
        env:
        - name: "PORT"
          value: "50001"
      - name: hello-v2
        image: "us-docker.pkg.dev/google-samples/containers/gke/hello-app:2.0"
        env:
        - name: "PORT"
          value: "50002"

---
apiVersion: v1
kind: Service
metadata:
  name: hello-service
spec:
  type: NodePort
  selector:
    app: products
    department: sales
  ports:
  - name: port-v1
    protocol: TCP
    port: 60001
    targetPort: 50001
  - name: port-v2
    protocol: TCP
    port: 60002
    targetPort: 50002
admin@kubemaster:~$ kubectl apply -f easegress_application.yml 
deployment.apps/hello-deployment created
service/hello-service created

Step6: Deploy Gateway API Objects

Gateways can be implemented by different controllers, often with different configurations. A Gateway must reference a GatewayClass that contains the name of the controller that implements the class.

A Gateway describes an instance of traffic handling infrastructure. It defines a network endpoint that can be used for processing traffic, i.e. filtering, balancing, splitting, etc. for backends such as a Service. For example, a Gateway may represent a cloud load balancer or an in-cluster proxy server that is configured to accept HTTP traffic.

The HTTPRoute kind specifies routing behaviour of HTTP requests from a Gateway listener to backend network endpoints.

File: easegress_gateway_objects.yml

apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: example-gateway-class
spec:
  controllerName: "megaease.com/gateway-controller"

---

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: example-gateway
spec:
  gatewayClassName: example-gateway-class
  listeners:
  - protocol: HTTP
    port: 8080
    name: example-listener

---

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: example-route
spec:
  parentRefs:
  - kind: Gateway
    name: example-gateway
    sectionName: example-listener
  rules:
  - matches:
    - path:
        value: /1
    backendRefs:
    - name: hello-service
      port: 60001

---

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: example-route-2
spec:
  parentRefs:
  - kind: Gateway
    name: example-gateway
    sectionName: example-listener
  hostnames:
    - megaease.com
  rules:
  - matches:
    - path:
        value: /2
    backendRefs:
    - name: hello-service
      port: 60002
admin@kubemaster:~$ kubectl apply -f easegress_gateway_objects.yml 
gatewayclass.gateway.networking.k8s.io/example-gateway-class created
gateway.gateway.networking.k8s.io/example-gateway created
httproute.gateway.networking.k8s.io/example-route created
httproute.gateway.networking.k8s.io/example-route-2 created
admin@kubemaster:~$ kubectl get gatewayclass
NAME                    CONTROLLER                        ACCEPTED   AGE
example-gateway-class   megaease.com/gateway-controller   True       54m

admin@kubemaster:~$ kubectl get gateway
NAME              CLASS                   ADDRESS   PROGRAMMED   AGE
example-gateway   example-gateway-class                          54m

admin@kubemaster:~$ kubectl get httproute
NAME              HOSTNAMES          AGE
example-route                        54m
example-route-2   ["megaease.com"]   54m

Step7: Expose The Listener Port

Create service to forward the incoming traffic to Listener in Easegress. The port web is to receive external HTTP requests from port 30080 and forward them to the HTTP server created according to the Listener configuration of the Gateway.

File: easegress_listener_service.yml

apiVersion: v1
kind: Service
metadata:
  name: easegress-public
  namespace: default
spec:
  ports:
  - name: web
    protocol: TCP
    port: 8080
    nodePort: 30080
  selector:
    app: easegress-gateway
  type: NodePort
admin@kubemaster:~$ kubectl apply -f easegress_listener_service.yml 
service/easegress-public created

Step8: Validate Application access

Here we can validate our two endpoints by access them as shown below.

admin@kubemaster:~$ curl http://127.0.0.1:30080/1
Hello, world!
Version: 2.0.0
Hostname: hello-deployment-569c6f9545-qncjr

admin@kubemaster:~$ curl http://127.0.0.1:30080/2 -i
HTTP/1.1 404 Not Found
Date: Fri, 31 Jan 2025 09:11:31 GMT
Content-Length: 0
Connection: close

admin@kubemaster:~$ curl http://127.0.0.1:30080/2 -H Host:megaease.com
Hello, world!
Version: 2.0.0
Hostname: hello-deployment-569c6f9545-qncjr

NOTE: The example-route-2 will forward the request to backend service only when it receives the request with host header as megaease.com

Hope you enjoyed reading this article. Thank you..