K8s Ingress and GatewayAPI

Why do we need Ingress

Almost every time in production we need to expose multiple k8s services or api endpoints which are running as microservices to user. Like /login, /checkout,
/cart needs to communicated seperately as they are running on different k8s deployment or simply using seperete k8s service. In this case if you open each endpoints to user the loadbalancer need to be created for each of them. That is costly as well very tedious to maintain.

What is Ingress

So the solution is to create some kind of object inside the cluster which will act as a single point of contact to each external request and direct the requests to the specific k8s services. Now we need only one loadbalancer on front of it and let this object send the requests to the specific services on based of either subdomains(foo.bar.com) or endpoints(bar.com/foo). That object is Ingress.

Ingress controller

Ingress controller is the actual service that will run on the cluster as a deployment and route the trafic to the specific services. You can install multiple types of them, and they are known as ingress class. Previously K8s community was maintaing Nginx-ingress controller as a open source project now the repo is a public archive so no support and future updates. Traefik is also one more popular choice these days, and it works pretty good. You can deploy them with helm charts as well.

Ingress configuration file

To configure the Ingress controller we need a configuration file, because that is the controller which will route the requests but we need to configure it using a manifest file. In this file you specify the class-name(which controller), multiple endpoints or subdomains which route to multiple k8s services also tls decryption. This one interesting, so you can deal with the https traffic as well. Need to specify the k8s certificate object where the private key and certificate will stored and ingress controller will decrypt the traffic and forward the request to services which will eventually go to pods.

Demo

Time to open the terminal. So we will first create a Nginx deployment, expose it as a cluster ip service, then we will install the ingress controller on the server and then apply the ingress configuration manifest file and test it.

Creating deployment

kubectl create deployment nginx --image=nginx

Creating service

kubectl expose deployment nginx --name=nginx --port=8080 --target-port=80

Installing Ingress controller

  • Opensource Nginx controller
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install ingress-nginx ingress-nginx/ingress-nginx
  • Traefik Controller
helm install traefik oci://ghcr.io/traefik/helm/traefik

Ingress configuration file

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: minimal-ingress
spec:
  ingressClassName: nginx
  rules:
  - host: ingress.nginx.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: nginx
            port:
              number: 8080

Ingress configuration file with tls termination

  • create a self signed certificate
openssl req -x509 -nodes -days 365 \
-newkey rsa:2048 \
-keyout nginxtls.key \
-out nginxtls.crt \
-subj "/CN=local.nginx.com/O=nginx"
  • creating a secret for it
kubectl create secret tls nginx-tls --key=nginxtls.key --cert=nginxtls.crt
  • manifest file with tls termination
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: minimal-ingress-tls
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - tls.ingress.nginx.com
      secretName: nginx-tls
  rules:
  - host: tls.ingress.nginx.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: nginx
            port:
              number: 8080
  • Apply with
kubectl apply -f INGRESS-FILE-NAME.yaml

Note

You can create multiple configuration file which will configure multiple installed ingress controller for a same same service. It's very unusal and rare to do that but as long as you change the class-name of the ingress controller you can play around with this.

GatewayAPI

Creating a gateway-api resource with tls termination.

Installing Envoy gateway controller

helm install envoy-gateway-controller oci://docker.io/envoyproxy/gateway-helm --version v1.9.1
kubectl wait --timeout=5m   deployment/envoy-gateway   --for=condition=Available

Creating gateway class

apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: envoy-gateway-controller
spec:
  controllerName: gateway.envoyproxy.io/gatewayclass-controller

save it as

gatewayclass.yaml

Creating gateway

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: non-tls-gateway
  namespace: default
spec:
  gatewayClassName: envoy-gateway-controller

  listeners:
    - name: http
      protocol: HTTP
      port: 80
      hostname: gateway.nginx.com

      allowedRoutes:
        namespaces:
          from: Same

save it as

gateway.yaml

Creating httproute

aapiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: nginx-route-non-tls
  namespace: default

spec:
  hostnames:
    - gateway.nginx.com

  parentRefs:
    - name: non-tls-gateway

  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /

      backendRefs:
        - name: nginx
          port: 8080

save it with

httproute.yaml

With TLS termination

  • generating selfsigned certificate
openssl req -x509 -nodes -days 365 \
-newkey rsa:2048 \
-keyout nginxtls.key \
-out nginxtls.crt \
-subj "/CN=local.nginx.com/O=nginx"
  • creating a secret for it
kubectl create secret tls nginx-tls --key=nginxtls.key --cert=nginxtls.crt

Modify gateway.yaml

add tls section

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: tls-gateway
  namespace: default
spec:
  gatewayClassName: envoy-gateway-controller

  listeners:
    - name: https
      protocol: HTTPS
      port: 443
      hostname: tls.gateway.nginx.com

      tls:
        mode: Terminate
        certificateRefs:
          - kind: Secret
            name: nginx-tls

      allowedRoutes:
        namespaces:
          from: Same

save gateway.yaml file.

Modify httproute.yaml

add sectionName: https

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: nginx-route-tls
  namespace: default
spec:
  parentRefs:
    - name: tls-gateway
      sectionName: https
  
  hostnames:
    - tls.gateway.nginx.com

  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /

      backendRefs:
        - name: nginx
          port: 8080

save it as httproute.yaml

  • Apply all the files with
kubectl apply -f filenames.yaml