Categories
Best Practices networking Orchestration

Kubernetes Ingress Demo

You have probably heard of Kubernetes ingress. What purpose does it serve? How to use it? Keep reading to find out.

Introduction

We have already seen how to point hostname to local WordPress. In addition, we have seen how HaProxy can serve as HTTP proxy. Now, we are going to point hostname to local WordPress using Kubernetes ingress. And we’ll use HaProxy as ingress controller. Jump straight to the demo if you prefer. Or keep reading if you want to understand the basic Kubernetes concepts we use here.

Kubernetes Services Limitations

When we want to expose some Kubernetes service (e.g. web app) to the outside world, we can configure it to be either of LoadBalancer (on cloud) or NodePort (on-prem) type. Yet, Service API has several limitations:

  • the need for manual configuration of external load balancer on-prem for NodePort services. This operational overhead might occur, for instance in the event of Kubernetes cluster node failures and their ip address changes.
  • performance overhead when traffic reaches cluster ip service and kube proxy needs to route it to a different node.
  • cloud costs for load balancers could bite if you have multiple external services.
  • Service API operates at OSI level 4 (ip, port). Frequently, we want to operate at level 7, e.g. have path based routing.

Kubernetes ingresses and their controllers is another way to expose Kubernetes services. It addresses the above limitations.

Kubernetes Ingress Controller

In addition to the above limitations, we usually access services using some hostname (e.g. local.com). And the requests directed to the service will usually through some api gateway. Api gateway may route the traffic to many backend services using provided hostnames or paths. The client of these services may not be even aware about the gateway. It just knows the path or host header it needs to provide to the (api gateway) host it accesses in order to consume the service. API gateway, in turn, needs to have routing rules defined in its configuration. Examples of such gateways are nginx, haproxy, etc… We have to deploy these third-party gateways to Kubernetes cluster. They are called Kubernetes ingress controllers. Routing rules are defined by Kubernetes ingress objects associated to ingress controllers.

Kubernetes Ingress

The routing rules of Ingress controllers are basically saying: “forward http requests sent on this path (e.g. /serviceA/doSmth) to this backend service”. Or “forward http requests sent on with these host header (e.g. serviceA.com) and this path (/serviceA/doSmth) to this backend service”. For these purposes, Kubernetes Ingress object exists. It manages routing of external HTTP or HTTPS traffic to Kubernetes services. As there could be multiple api gateways installed for different purposes, these ingress objects need to have clear association to appropriate ingress controllers. This association is implemented by ingressclassess Kubernetes objects. So each ingresclass specifies the ingress controller it’s tied to. And to associate specific ingress with the controller, ingress needs to specify ingressclass in its deployment manifest.

Kubernetes Ingress Use Cases

I’ll note 2 widely used use-cases Kubernetes Ingress supports:

  • name-based virtual hosting

This case reminds me of shared hosting services different hosting providers offer when a single ip address might be associated with multiple hostnames (websites), e.g. using CNAME DNS records. In this case, Kubernetes ingress controller will be a an internet facing service, exposed via LoadBalancer service, for example. Requests coming to the ingress will be routed to the appropriate backend services (individual websites web servers) by matching host headers to the rules present in Kubernetes ingresses associated with the controller.

  • path-based routing

Requests will be routed to backend services based on the URL paths. For example, requests to http://hostname/users will be routed to users microservice. Whereas, https://hostname/customers will be routed to customers microservice.

  • combination of the host and path based routing.

Enough talking, let’s see all of this in action.

Demo

Demo Prerequisites

I assume you have Kubernetes cluster. If you don’t, install on your machine minikubekubectl and helm.

Start Kubernetes cluster using minikube start --profile custom.

Installation of HaProxy ingress controller

First, let’s install HaProxy ingress controller using helm. We’ll just use official HaProxy docs and helm chart for that. Consider installing the chart to a dedicated namespace.

Let’s do a basic check that our ingress controller is working:

 kubectl get svc -n hap
NAME                                              TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)                                     AGE
mycontroller-kubernetes-ingress                   NodePort    10.96.227.38   <none>        80:30755/TCP,443:32615/TCP,1024:32212/TCP   22h
mycontroller-kubernetes-ingress-default-backend   ClusterIP   None           <none>        8080/TCP 

We see that mycontroller-kubernetes-ingress service is of type NodePort and it’s available at port 30755. Let’s access default backend service courteously provided to us by HaProxy chart:

$ minikube ip
192.168.58.2
$ curl http://192.168.58.2:30755/ 
default backend - 404

Ingress controller works.

Install WordPress helm chart

Next, we’ll install WordPress using helm chart to wp namespace while customizing WordPress service type to ClusterIP:

helm install wp bitnami/wordpress --set "service.type=ClusterIP" -n wp --create-namespace 

I’m skipping helm repo add and update commands assuming you are already familiar with the flow. If you are not, you can see an example from a previous post when we installed Jenkins helm chart.

Expose WordPress service using Kubernetes Ingress

Now, let’s point hostname wp.local.com to WordPress service:

$ kubectl get svc -n wp
NAME                   TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
my-release-mariadb     ClusterIP   10.111.48.139   <none>        3306/TCP         21h
my-release-wordpress   ClusterIP   10.101.58.54    <none>        80/TCP,443/TCP   21h

For that purpose, we’ll create ingress object from the manifest below by running kubectl apply on it.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-wp
  namespace: wp
spec:
  ingressClassName: haproxy
  rules:
  - host: wp.local.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: wp-wordpress
            port:
              number: 80

Pay attention to few important things:

  • ingress is namespaced Kubernetes resource and has to be in the same namespace where the service it’s exposing is located. In our case it’s wp-wordpress:
kubectl get svc -n wp
NAME           TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
wp-mariadb     ClusterIP   10.111.166.90   <none>        3306/TCP         19h
wp-wordpress   ClusterIP   10.98.171.120   <none>        80/TCP,443/TCP   19h

wp prefix is derived from wp helm release named we used during WordPress helm chart installation.

  • ingressClassName is set to haproxy. This resource was installed when we installed haproxy ingress controller. Note, that it’s not namespaced object. That’s why we can reference it in any namespace.
  • Ingress rule is path based (/). It means that any request to wp.local.com hostname in the request header (or through DNS wp.local.com name which points to Ingress controller public IP) will be routed to service named wp-wordpress on port 80.

Access WordPress through HaProxy ingress controller

As we are using minikube, we can find the minikube host using below command:

minikube ip -p custom
192.168.58.2

We see that ingress controller node port is 30755:

 kubectl get svc -n hap
NAME                                              TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)                                     AGE
mycontroller-kubernetes-ingress                   NodePort    10.96.227.38   <none>        80:30755/TCP,443:32615/TCP,1024:32212/TCP   22h

Let’s access our WordPress website using node ip and port and host header conforming to Ingress:

curl http://192.168.58.2:30755/ --header "Host: wp.local.com"

You should get a long html file in response which you can save, open in the browser and see WordPress website delivered to us thanks to Ingress we defined.

You can, of course, simulate real-life website access scenarios using its hostname. For that you may add entry 192.168.58.2 wp.local.com to /etc/hosts. Then use curl command curl http://wp.local.com:30755 which will return the same result.

Summary

That’s it about Kubernetes ingress. It’s important to note also that Ingress API also supports routing HTTPS external traffic. In addition, ingress controllers usually load balance traffic across service endpoints (pods) thus eliminating performance penalty that exists with NodePort services when the chosen load-balanced clusterIP service endpoint runs on a different node. We’ll probably see relevant demos in the future.

As always, feel free to share.

Recommended Kubernetes courses on Pluralsight:

Sign up using this link to get exclusive discounts like 50% off your first month or 15% off an annual subscription)

Recommended Kubernetes books on Amazon:

See more WordPress related posts here